[
  {
    "path": ".clang-format",
    "content": "---\nLanguage:        Cpp\n# BasedOnStyle:  Google\nAccessModifierOffset: -2\nAlignAfterOpenBracket: Align\nAlignConsecutiveAssignments: false\nAlignConsecutiveDeclarations: false\nAlignEscapedNewlines: Right\nAlignOperands:   true\nAlignTrailingComments: true\nAllowAllParametersOfDeclarationOnNextLine: true\nAllowShortBlocksOnASingleLine: false\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: Empty\nAllowShortIfStatementsOnASingleLine: true\nAllowShortLoopsOnASingleLine: true\nAlwaysBreakAfterDefinitionReturnType: None\nAlwaysBreakAfterReturnType: None\nAlwaysBreakBeforeMultilineStrings: false\nAlwaysBreakTemplateDeclarations: true\nBinPackArguments: false\nBinPackParameters: false\nBraceWrapping:\n  AfterClass:      false\n  AfterControlStatement: false\n  AfterEnum:       false\n  AfterFunction:   false\n  AfterNamespace:  false\n  AfterObjCDeclaration: false\n  AfterStruct:     false\n  AfterUnion:      false\n  AfterExternBlock: false\n  BeforeCatch:     false\n  BeforeElse:      false\n  IndentBraces:    false\n  SplitEmptyFunction: true\n  SplitEmptyRecord: true\n  SplitEmptyNamespace: true\nBreakBeforeBinaryOperators: None\nBreakBeforeBraces: Attach\nBreakBeforeInheritanceComma: false\nBreakBeforeTernaryOperators: true\nBreakConstructorInitializersBeforeComma: false\nBreakConstructorInitializers: BeforeColon\nBreakAfterJavaFieldAnnotations: false\nBreakStringLiterals: true\nColumnLimit:      120\nCommentPragmas:  '^ IWYU pragma:'\nCompactNamespaces: false\nConstructorInitializerAllOnOneLineOrOnePerLine: true\nConstructorInitializerIndentWidth: 4\nContinuationIndentWidth: 4\nCpp11BracedListStyle: true\nDerivePointerAlignment: false\nDisableFormat:   false\nExperimentalAutoDetectBinPacking: false\nFixNamespaceComments: true\nForEachMacros:\n  - foreach\n  - Q_FOREACH\n  - BOOST_FOREACH\nIncludeBlocks:   Preserve\nIncludeCategories:\n  - Regex:           '^<ext/.*\\.h>'\n    Priority:        2\n  - Regex:           '^<.*\\.h>'\n    Priority:        1\n  - Regex:           '^<.*'\n    Priority:        2\n  - Regex:           '.*'\n    Priority:        3\nIncludeIsMainRegex: '([-_](test|unittest))?$'\nIndentCaseLabels: false\nIndentPPDirectives: None\nIndentWidth:     4\nIndentWrappedFunctionNames: false\nJavaScriptQuotes: Leave\nJavaScriptWrapImports: true\nKeepEmptyLinesAtTheStartOfBlocks: false\nMacroBlockBegin: ''\nMacroBlockEnd:   ''\nMaxEmptyLinesToKeep: 1\nNamespaceIndentation: None\nObjCBlockIndentWidth: 2\nObjCSpaceAfterProperty: false\nObjCSpaceBeforeProtocolList: false\nPenaltyBreakAssignment: 2\nPenaltyBreakBeforeFirstCallParameter: 1\nPenaltyBreakComment: 300\nPenaltyBreakFirstLessLess: 120\nPenaltyBreakString: 1000\nPenaltyExcessCharacter: 1000000\nPenaltyReturnTypeOnItsOwnLine: 200\nPointerAlignment: Right\nReflowComments:  true\nSortIncludes:    false\nSortUsingDeclarations: true\nSpaceAfterCStyleCast: true\nSpaceAfterTemplateKeyword: true\nSpaceBeforeAssignmentOperators: true\nSpaceBeforeParens: ControlStatements\nSpaceInEmptyParentheses: false\nSpacesBeforeTrailingComments: 2\nSpacesInAngles:  false\nSpacesInContainerLiterals: true\nSpacesInCStyleCastParentheses: false\nSpacesInParentheses: false\nSpacesInSquareBrackets: false\nStandard:        Auto\nTabWidth:        8\nUseTab:          Never"
  },
  {
    "path": ".editorconfig",
    "content": "# https://editorconfig.org/\n\nroot = true\n\n[*]\ntrim_trailing_whitespace = true\ninsert_final_newline     = true\nend_of_line              = lf\ncharset                  = utf-8\nindent_style             = space\ntab_width                = 4\n\n[{*.{awk,sh},Makefile*}]\nindent_size              = 4\nindent_style             = table\nmax_line_length          = 80\n\n[*.{c,cc,cpp,h,html,inc,php,phpt}]\nindent_size              = 4\nindent_style             = space\nmax_line_length          = 120\n\n[*.md]\nindent_style             = space\nmax_line_length          = 120\n\n[COMMIT_EDITMSG]\nindent_size              = 4\nindent_style             = space\nmax_line_length          = 80\n\n"
  },
  {
    "path": ".gitattributes",
    "content": "/.github/ export-ignore\n/benchmark/ export-ignore\n/core-tests/ export-ignore\n*.h linguist-language=cpp\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE",
    "content": "Please answer these questions before submitting your issue.\n\n1. What did you do? If possible, provide a simple script for reproducing the error.\n\n\n\n2. What did you expect to see?\n\n\n\n3. What did you see instead?\n\n\n\n4. What version of Swoole are you using (show your `php --ri swoole`)?\n\n\n\n5. What is your machine environment used (show your `uname -a` & `php -v` & `gcc -v`) ?\n\n\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/alpine.Dockerfile",
    "content": "ARG PHP_VERSION\nARG ALPINE_VERSION\n\nFROM phpswoole/php:${PHP_VERSION}-alpine\n\nLABEL maintainer=\"Swoole Team <team@swoole.com>\" version=\"1.0\" license=\"Apache2\"\n\nARG PHP_VERSION\n\nCOPY . /swoole\n\nWORKDIR /swoole\n\nRUN apk add liburing-dev\n\nRUN set -ex \\\n    && phpize \\\n    && ./configure --enable-swoole-curl --enable-iouring \\\n    && make -s -j$(nproc) && make install\n\nRUN echo \"extension=swoole.so\" > \"/usr/local/etc/php/conf.d/swoole.ini\"\nRUN php -v\nRUN php -m\nRUN php --ri swoole\nRUN echo -e \"\\033[42;37m Build Completed :).\\033[0m\\n\"\n"
  },
  {
    "path": ".github/workflows/core.yml",
    "content": "name: Core Tests\n\non: [ push, pull_request ]\n\nenv:\n  CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}\n\njobs:\n  ubuntu:\n    runs-on: ubuntu-latest\n    if: \"!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[core]')\"\n    timeout-minutes: 12\n    services:\n      tinyproxy:\n        image: \"vimagick/tinyproxy\"\n        ports:\n          - 8888:8888\n      nginx:\n        image: \"nginx\"\n        ports:\n          - \"80:80\"\n        env:\n          NGINX_PORT: \"[::]:80\"\n      socks5:\n        image: \"xkuma/socks5\"\n        ports:\n          - 8080:1080\n        env:\n          PROXY_USER: user\n          PROXY_PASSWORD: password\n          PROXY_SERVER: 0.0.0.0:1080\n      socks5-no-auth:\n        image: \"xkuma/socks5\"\n        ports:\n          - 8081:1080\n        env:\n          PROXY_SERVER: 0.0.0.0:1080\n\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: install dependencies\n        run: sudo apt update -y && sudo apt install -y googletest libgtest-dev libnghttp2-dev libboost-stacktrace-dev libbrotli-dev redis-server nodejs npm nghttp2-client\n\n      - name: install liburing\n        run: sudo bash ./scripts/install-liburing.sh\n\n      - name: configure\n        run: phpize && ./configure --enable-sockets --enable-mysqlnd --enable-iouring\n\n      - name: build\n        run: |\n          cmake . -D CODE_COVERAGE=ON -D enable_thread=1 -D verbose=1 -D async_io=1 || exit 1\n          make VERBOSE=1 -j $(nproc) core-tests || exit 1\n\n      - name: run tests\n        run: |\n          ./run-core-tests.sh\n\n      - name: run coverage\n        shell: bash\n        run: sudo apt-get install lcov &&\n          sudo lcov --directory . --capture --branch-coverage --rc geninfo_unexecuted_blocks=1 --ignore-errors mismatch --output-file coverage.info &&\n          sudo lcov --remove coverage.info '/usr/*' --output-file coverage.info &&\n          sudo lcov --list coverage.info\n\n      - name: Upload coverage to Codecov\n        uses: codecov/codecov-action@v5\n        with:\n          token: ${{ secrets.CODECOV_TOKEN }}\n          files: ./coverage.info\n          fail_ci_if_error: true\n"
  },
  {
    "path": ".github/workflows/coverity.yml",
    "content": "name: coverity-scan\n\non:\n  push:\n    branches:\n      - coverity_scan\n\nenv:\n  COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}\n  COVERITY_SCAN_EMAIL: team@swoole.com\n  COVERITY_PROJECT: ${{ github.repository }}\n  COV_TOOLS_DIR: ${{ github.workspace }}/cov-analysis-linux64\n  COV_BUILD_DIR: ${{ github.workspace }}\n  COV_RESULTS_DIR: cov-int\n  COV_RESULTS_FILE: analysis-results.tgz\n\njobs:\n  coverity-scan:\n    if: \"github.repository_owner == 'swoole' && (!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[coverity]'))\"\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - name: Setup PHP\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: \"8.3\"\n\n      - name: Download and install Coverity Build Tool\n        run: |\n          wget -q https://scan.coverity.com/download/linux64 \\\n                --post-data \"token=${COVERITY_SCAN_TOKEN}&project=${COVERITY_PROJECT}\" \\\n                -O cov-analysis-linux64.tar.gz\n          mkdir ${COV_TOOLS_DIR}\n          tar xzf cov-analysis-linux64.tar.gz --strip 1 -C ${COV_TOOLS_DIR}\n          ls -la ${COV_TOOLS_DIR}/bin\n\n      - name: Run build steps\n        run: |\n          sudo phpize && ./configure --enable-mysqlnd --enable-sockets --enable-swoole-curl\n\n      - name: Run Coverity Scan Analysis Tool\n        run: |\n          export PATH=${COV_TOOLS_DIR}/bin:$PATH\n          cd ${COV_BUILD_DIR}\n          cov-build --dir ${COV_RESULTS_DIR} make -j $(nproc)\n\n      - name: Upload Coverity Scan Analysis results\n        run: |\n          cd ${COV_BUILD_DIR}\n          tar czf ${COV_RESULTS_FILE} ${COV_RESULTS_DIR}\n          curl \\\n            --form project=${COVERITY_PROJECT} \\\n            --form token=${COVERITY_SCAN_TOKEN} \\\n            --form email=${COVERITY_SCAN_EMAIL} \\\n            --form file=@${COV_RESULTS_FILE} \\\n            --form version=${GITHUB_SHA} \\\n            --form description=\"GitHub Actions\" \\\n              https://scan.coverity.com/builds?project=${COVERITY_PROJECT}\n"
  },
  {
    "path": ".github/workflows/ext.yml",
    "content": "name: Compile Tests\n\non: [ push, pull_request ]\n\nenv:\n  CPPFLAGS: \"-I/opt/homebrew/opt/pcre2/include/\"\n\njobs:\n  build-ubuntu-latest:\n    if: \"!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[ubuntu]')\"\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - name: install-deps\n        run: sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev\n      - name: phpize\n        run: phpize\n      - name: build1\n        run: ./configure &&\n          make clean && make -j$(nproc)\n      - name: build2\n        run: ./configure --enable-sockets &&\n          make clean && make -j$(nproc)\n      - name: build5\n        run: ./configure --enable-sockets --enable-mysqlnd &&\n          make clean && make -j$(nproc)\n      - name: build6\n        run: ./configure --enable-sockets --enable-mysqlnd --enable-debug-log &&\n          make clean && make -j$(nproc)\n      - name: build7\n        run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-debug-log &&\n          make clean && make -j$(nproc)\n      - name: build8\n        run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-cares --enable-debug-log &&\n          make clean && make -j$(nproc)\n      - name: build with thread context\n        run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-cares --enable-debug-log --enable-thread-context &&\n          make clean && make -j$(nproc)\n\n  build-macos-latest:\n    if: \"!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[macos]')\"\n    runs-on: macos-latest\n    steps:\n      - name: install dependencies\n        run: brew reinstall php\n      - uses: actions/checkout@v6\n      - name: phpize\n        run: phpize\n      - name: build1\n        run: ./configure CPPFLAGS=\"${CPPFLAGS}\" && make clean && make -j$(sysctl -n hw.ncpu)\n      - name: build2\n        run: ./configure CPPFLAGS=\"${CPPFLAGS}\" --enable-sockets &&\n          make clean && make -j$(sysctl -n hw.ncpu)\n      - name: build5\n        run: ./configure CPPFLAGS=\"${CPPFLAGS}\" --enable-sockets --enable-mysqlnd &&\n          make clean && make -j$(sysctl -n hw.ncpu)\n      - name: build6\n        run: ./configure CPPFLAGS=\"${CPPFLAGS}\" --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-debug-log &&\n          make clean && make -j$(sysctl -n hw.ncpu)\n      - name: build7\n        run: ./configure CPPFLAGS=\"${CPPFLAGS}\" --enable-sockets --enable-mysqlnd --enable-swoole-curl\n          --enable-debug-log --enable-cares &&\n          make clean && make -j$(sysctl -n hw.ncpu)\n\n  build-alpine-latest:\n    if: \"!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[alpine]')\"\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        php-version: [ '8.2', '8.3', '8.4', '8.5' ]\n      max-parallel: 8\n      fail-fast: false\n    steps:\n      - uses: actions/checkout@v6\n      - name: build\n        run: |\n          cp .github/workflows/alpine.Dockerfile alpine.Dockerfile\n          docker build -t swoole . -f alpine.Dockerfile --build-arg PHP_VERSION=${{ matrix.php-version }}\n"
  },
  {
    "path": ".github/workflows/framework.yml",
    "content": "name: Framework Tests\n\non:\n  push:\n  pull_request:\n\njobs:\n  linux:\n    runs-on: ubuntu-latest\n    if: \"!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[framework]')\"\n    strategy:\n      fail-fast: false\n      matrix:\n        php-version: [ '8.2', '8.3', '8.4', '8.5' ]\n        framework: [ 'Laravel Octane', 'Hyperf', 'Simps' ]\n    name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }}\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v6\n\n      - name: Setup PHP\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: ${{ matrix.php-version }}\n          extensions: dom, curl, libxml, mbstring, zip, redis, pdo, pdo_mysql, bcmath\n          tools: phpize, composer:v2\n          ini-values: extension=swoole\n          coverage: none\n\n      - name: Build Swoole\n        run: |\n          sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev libpq-dev valgrind\n          phpize\n          ./configure --enable-mysqlnd --enable-swoole-curl --enable-cares --enable-swoole-pgsql\n          make -j$(nproc)\n          sudo make install\n          php -v\n          php -m\n          php --ini\n          php --ri swoole\n\n      - name: Laravel Octane Tests\n        if: matrix.framework == 'Laravel Octane' && matrix.php-version != '8.5'\n        run: |\n          git clone https://github.com/laravel/octane.git --depth=1\n          cd octane/\n          composer update --prefer-dist --no-interaction --no-progress\n          vendor/bin/testbench package:sync-skeleton\n          vendor/bin/phpunit --display-deprecations --fail-on-deprecation\n\n      - name: Hyperf Tests\n        if: matrix.framework == 'Hyperf' && matrix.php-version != '8.5'\n        env:\n          SW_VERSION: 'master'\n          MYSQL_VERSION: '5.7'\n          PGSQL_VERSION: '14'\n        run: |\n          git clone https://github.com/hyperf/hyperf.git --depth=1\n          cd hyperf/\n          composer update -o\n          ./.travis/requirement.install.sh\n          ./.travis/setup.services.sh\n          export TRAVIS_BUILD_DIR=$(pwd) && bash ./.travis/setup.mysql.sh\n          export TRAVIS_BUILD_DIR=$(pwd) && bash ./.travis/setup.pgsql.sh\n          cp .travis/.env.example .env\n          export SWOOLE_BRANCH=${GITHUB_REF##*/}\n          if [ \"${SWOOLE_BRANCH}\" = \"valgrind\" ]; then\n            USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' bin/co-phpunit --exclude-group NonCoroutine\n            USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' vendor/bin/phpunit --group NonCoroutine\n            USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' vendor/bin/phpunit src/filesystem --group NonCoroutine\n          else\n            .travis/run.test.sh\n          fi\n\n      - name: Simps Tests\n        if: matrix.framework == 'Simps'\n        run: |\n          git clone https://github.com/simps/mqtt.git --depth=1\n          cd mqtt/\n          composer install -o\n          composer test\n\n  macos:\n    if: \"!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[framework]')\"\n    runs-on: macos-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        php-version: [ '8.2', '8.3', '8.4', '8.5' ]\n        framework: [ 'Simps' ]\n    name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} - macOS\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v6\n\n      - name: Setup PHP\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: ${{ matrix.php-version }}\n          extensions: dom, curl, libxml, mbstring, zip, redis, pdo, pdo_mysql, bcmath\n          tools: phpize, composer:v2\n          ini-values: extension=swoole\n          coverage: none\n\n      - name: Build Swoole\n        run: |\n          phpize\n          export CPPFLAGS=\"${CPPFLAGS} -I/opt/homebrew/opt/pcre2/include/\"\n          export CFLAGS=\"${CFLAGS} -I/opt/homebrew/opt/pcre2/include/\"\n          ./configure --enable-mysqlnd --enable-swoole-curl --enable-cares\n          make -j$(sysctl -n hw.ncpu)\n          sudo make install\n          php --ri swoole\n\n      - name: Simps Tests\n        if: matrix.framework == 'Simps'\n        run: |\n          git clone https://github.com/simps/mqtt.git --depth=1\n          cd mqtt/\n          composer install -o\n          composer test\n\n"
  },
  {
    "path": ".github/workflows/iouring.yml",
    "content": "name: Linux io_uring Tests\n\non: [push, pull_request]\n\njobs:\n  linux:\n    if: \"!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[iouring]')\"\n    strategy:\n      fail-fast: false\n      matrix:\n        php: [ '8.2', '8.3', '8.4', '8.5' ]\n        os: [ ubuntu-24.04, ubuntu-24.04-arm ]\n    name: ${{ matrix.php }}-${{ matrix.os }}-iouring\n    runs-on: ${{ matrix.os }}\n    steps:\n      - uses: actions/checkout@v6\n      - name: Setup PHP\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: \"${{ matrix.php }}\"\n          coverage: none\n      - name: Show machine information\n        run: |\n          date\n          env\n          uname -a\n          ulimit -a\n          php -v\n          php --ini\n          ls -al\n          pwd\n          echo \"`git log -20 --pretty --oneline`\"\n          echo \"`git log -10 --stat --pretty --oneline`\"\n      - name: Run Swoole test\n        run: |\n          export SWOOLE_CI_TYPE=IOURING\n          export SWOOLE_BRANCH=${GITHUB_REF##*/}\n          export SWOOLE_BUILD_DIR=$(realpath .)\n          export PHP_VERSION=${{ matrix.php }}\n          ${SWOOLE_BUILD_DIR}/scripts/route.sh\n\n"
  },
  {
    "path": ".github/workflows/thread.yml",
    "content": "name: Thread Support Tests\n\non: [push, pull_request]\n\njobs:\n  linux:\n    if: \"!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[thread]')\"\n    timeout-minutes: 15\n    strategy:\n      fail-fast: false\n      matrix:\n        php: ['8.2-zts', '8.3-zts', '8.4-zts', '8.5-zts']\n        os: [ ubuntu-24.04, ubuntu-24.04-arm ]\n    name: ${{ matrix.php }}-thread-${{ matrix.os }}\n    runs-on: ${{ matrix.os }}\n    steps:\n    - uses: actions/checkout@v6\n    - name: Setup PHP\n      uses: shivammathur/setup-php@v2\n      with:\n        php-version: \"${{ matrix.php }}\"\n        coverage: none\n      env:\n        phpts: ts\n    - name: Show machine information\n      run: |\n        date\n        env\n        uname -a\n        ulimit -a\n        php -v\n        php --ini\n        ls -al\n        pwd\n        echo \"`git log -20 --pretty --oneline`\"\n        echo \"`git log -10 --stat --pretty --oneline`\"\n    - name: Run tests\n      run: |\n        export SWOOLE_CI_TYPE=THREAD\n        export SWOOLE_BRANCH=${GITHUB_REF##*/}\n        export SWOOLE_BUILD_DIR=$(realpath .)\n        export PHP_VERSION=${{ matrix.php }}\n        ${SWOOLE_BUILD_DIR}/scripts/route.sh\n"
  },
  {
    "path": ".github/workflows/unit.yml",
    "content": "name: Unit Tests\n\non: [push, pull_request]\n\njobs:\n  linux:\n    if: \"!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[unit]')\"\n    timeout-minutes: 30\n    strategy:\n      fail-fast: false\n      matrix:\n        php: ['8.2', '8.3', '8.4', '8.5']\n        os: [ ubuntu-24.04, ubuntu-24.04-arm ]\n    name: ${{ matrix.php }}-${{ matrix.os }}\n    runs-on: ${{ matrix.os }}\n    steps:\n    - uses: actions/checkout@v6\n    - name: Setup PHP\n      uses: shivammathur/setup-php@v2\n      with:\n        php-version: \"${{ matrix.php }}\"\n        coverage: none\n    - name: Show machine information\n      run: |\n        date\n        env\n        uname -a\n        ulimit -a\n        php -v\n        php --ini\n        ls -al\n        pwd\n        echo \"`git log -20 --pretty --oneline`\"\n        echo \"`git log -10 --stat --pretty --oneline`\"\n    - name: Run unit tests\n      env:\n        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}\n        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}\n        AWS_REGION: ${{ secrets.AWS_REGION }}\n      run: |\n        export SWOOLE_CI_TYPE=NORMAL\n        export SWOOLE_BRANCH=${GITHUB_REF##*/}\n        export SWOOLE_BUILD_DIR=$(realpath .)\n        export PHP_VERSION=${{ matrix.php }}\n        ${{runner.workspace}}/swoole-src/scripts/route.sh\n  macos:\n    runs-on: macos-latest\n    if: \"!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[macos-unit]')\"\n    timeout-minutes: 30\n    strategy:\n      fail-fast: false\n      matrix:\n#        php-version: [ '8.2', '8.3', '8.4', '8.5' ]\n        php-version: [ '8.3' ]\n    name: ${{ matrix.php-version }} - macOS\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v6\n\n      - name: Setup PHP\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: ${{ matrix.php-version }}\n          extensions: dom, curl, libxml, mbstring, zip, redis, pdo, pdo_mysql, bcmath, sockets\n          tools: phpize, composer:v2\n          ini-values: extension=swoole\n          coverage: none\n\n      - name: Install dependencies\n        run: |\n          brew install redis tinyproxy nginx md5sha1sum\n          brew services start redis\n          brew services start nginx\n          brew services start tinyproxy\n\n      - name: Build Swoole\n        run: |\n          phpize\n          export PCRE2_INCLUDE_DIR=\"/opt/homebrew/opt/pcre2/include\"\n          export CPPFLAGS=\"${CPPFLAGS} -I${PCRE2_INCLUDE_DIR}\"\n          export CFLAGS=\"${CFLAGS} -I${PCRE2_INCLUDE_DIR}\"\n          ./configure CPPFLAGS=\"${CPPFLAGS}\" --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-cares --enable-zstd\n          make -j$(sysctl -n hw.ncpu)\n          sudo make install\n          php --ri swoole\n          uname -a\n\n      - name: Run unit tests\n        run: |\n          export SWOOLE_CI_TYPE=NORMAL\n          export SWOOLE_BRANCH=${GITHUB_REF##*/}\n          export SWOOLE_BUILD_DIR=$(realpath .)\n          export PHP_VERSION=${{ matrix.php-version }}\n          export SWOOLE_CI_IN_MACOS=1\n          cd ${{runner.workspace}}/swoole-src\n          ulimit -n 100000\n          ./scripts/run-tests.sh\n"
  },
  {
    "path": ".gitignore",
    "content": "/.cproject\n/.idea\n/.project\n*.o\n*.lo\n*~\n*.so\n*.loT\n*.pid\n*.dep\nmodules/*\n/.deps\n/.libs/\n/core\n/examples/core\n/Debug\n/Release\n/CMakeFiles\n/cmake_install.cmake\n/CMakeCache.txt\n/Makefile\n/bin\n/lib\n/library\n/install_manifest.txt\n/config.guess\n/config.h\n/config.h.in\n/config.log\n/config.nice\n/config.status\n/config.sub\n/configure\n/Makefile.fragments\n/Makefile.global\n/Makefile.objects\n/install-sh\n/libtool\n/ltmain.sh\n/missing\n/mkinstalldirs\n/swoole.la\n/acinclude.m4\n/aclocal.m4\n/run-tests.php\n/autom4te.cache\n/build\n/examples/async/data.txt\n/examples/.idea\n/examples/recv_file.jpg\n/modules\n/examples/async/test.copy\n/examples/ssl_client\n/examples/ssl/ca.crt\n/examples/ssl/client.key\n/examples/ssl/client.pfx\n/examples/ssl/client\n/examples/ssl/corpssl.crt\n/examples/ssl/corpssl.key\n/examples/ext\n/benchmark/.idea/\n/benchmark/vendor\n/benchmark/composer.lock\n/tools/.idea/\n/tmp-php.ini\n.settings/\ntests/.idea\n/tests/swoole_*/*.log\n/tests/swoole_*/*.sh\n/tests/swoole_*/*.diff\n/tests/swoole_*/*.out\n/tests/swoole_*/*.exp\n/tests/swoole_*/*.mem\n/tests/swoole_*/*.php\n/tests/swoole_library/curl/skip\n/core-tests/server/*.log\ncmake-build-debug/\n*.cbp\n/.vscode\n/.vs\n/configure.in\n/configure.ac\n\n# core-tests\n/core-tests/CMakeCache.txt\n/core-tests/CMakeFiles/\n/core-tests/Makefile\n/core-tests/bin/\n/core-tests/cmake_install.cmake\n/core-tests/samples/Makefile\n/core-tests/samples/cmake_install\\.cmake\n/core-tests/samples/CMakeCache\\.txt\n/core-tests/samples/bin/\n/core-tests/samples/CMakeFiles/\n/core-tests/.project\n/core-tests/.cproject\n/core-tests/compile_commands.json\n/core-tests/samples/.project\n/core-tests/fuzz/fuzz_results/\n/core-tests/fuzz/bin/\n\n/tools/vendor\n/tools/composer.lock\n/examples/wrapper/CMakeFiles\n/examples/wrapper/CMakeCache.txt\n/examples/wrapper/Makefile\n/examples/wrapper/server\n/examples/wrapper/cmake_install.cmake\n/out\n/gcov_result\n\n# coverage\n*.gcno\n*.gcov\n*.gcda\n*.info\n/html\n/tests/include/lib/vendor/\n/tests/include/lib/composer.lock\n/scripts/data\n/.cmake/\n/Testing/\nbuild.ninja"
  },
  {
    "path": ".php-cs-fixer.dist.php",
    "content": "<?php\n\n$header = <<<'EOF'\nThis file is part of Swoole.\n\n@link     https://www.swoole.com\n@contact  team@swoole.com\n@license  https://github.com/swoole/library/blob/master/LICENSE\nEOF;\n\nreturn (new PhpCsFixer\\Config())\n    ->setRiskyAllowed(true)\n    ->setRules([\n        '@DoctrineAnnotation'                              => true,\n        '@PhpCsFixer'                                      => true,\n        '@PSR2'                                            => true,\n        '@Symfony'                                         => true,\n        'align_multiline_comment'                          => ['comment_type' => 'all_multiline'],\n        'array_syntax'                                     => ['syntax' => 'short'],\n        'binary_operator_spaces'                           => ['operators' => ['=' => 'align', '=>' => 'align', ]],\n        'blank_line_after_namespace'                       => true,\n        'blank_line_before_statement'                      => ['statements' => ['declare']],\n        'class_attributes_separation'                      => true,\n        'concat_space'                                     => ['spacing' => 'one'],\n        'constant_case'                                    => ['case' => 'lower'],\n        'combine_consecutive_unsets'                       => true,\n        'declare_strict_types'                             => true,\n        'fully_qualified_strict_types'                     => ['phpdoc_tags' => []],\n        'general_phpdoc_annotation_remove'                 => ['annotations' => ['author']],\n        'header_comment'                                   => ['comment_type' => 'PHPDoc', 'header' => $header, 'location' => 'after_open', 'separate' => 'bottom'],\n        'increment_style'                                  => ['style' => 'post'],\n        'lambda_not_used_import'                           => false,\n        'linebreak_after_opening_tag'                      => true,\n        'list_syntax'                                      => ['syntax' => 'short'],\n        'lowercase_static_reference'                       => true,\n        'multiline_comment_opening_closing'                => true,\n        'multiline_whitespace_before_semicolons'           => ['strategy' => 'new_line_for_chained_calls'],\n        'no_superfluous_phpdoc_tags'                       => ['allow_mixed' => true, 'allow_unused_params' => true, 'remove_inheritdoc' => false],\n        'no_unused_imports'                                => true,\n        'no_useless_else'                                  => true,\n        'no_useless_return'                                => true,\n        'not_operator_with_space'                          => false,\n        'not_operator_with_successor_space'                => false,\n        'php_unit_strict'                                  => false,\n        'phpdoc_align'                                     => ['align' => 'left'],\n        'phpdoc_annotation_without_dot'                    => false,\n        'phpdoc_no_empty_return'                           => false,\n        'phpdoc_types_order'                               => ['sort_algorithm' => 'none', 'null_adjustment' => 'always_last'],\n        'phpdoc_separation'                                => false,\n        'phpdoc_summary'                                   => false,\n        'ordered_class_elements'                           => true,\n        'ordered_imports'                                  => ['imports_order' => ['class', 'function', 'const'], 'sort_algorithm' => 'alpha'],\n        'ordered_types'                                    => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'],\n        'single_line_comment_style'                        => ['comment_types' => []],\n        'single_line_comment_spacing'                      => false,\n        'single_line_empty_body'                           => false,\n        'single_quote'                                     => true,\n        'standardize_increment'                            => false,\n        'standardize_not_equals'                           => true,\n        'yoda_style'                                       => ['always_move_variable' => false, 'equal' => false, 'identical' => false],\n    ])\n    ->setFinder(\n        PhpCsFixer\\Finder::create()\n            ->exclude(['html', 'vendor'])\n            ->in(__DIR__)\n    )\n    ->setUsingCache(false);\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\nproject(libswoole)\n\nenable_language(ASM)\nset(SWOOLE_VERSION 6.2.0)\n\nset(CMAKE_CXX_STANDARD 14)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -Wall -g\")\nset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wall -g\")\n\nfile(READ ./config.h SWOOLE_CONFIG_FILE)\n\nset(CMAKE_MACOSX_RPATH 1)\nset(SWOOLE_LINK_LIBRARIES pthread dl)\n\nif (APPLE)\n    set(CMAKE_SHARED_LINKER_FLAGS \"-undefined dynamic_lookup\")\n    include_directories(BEFORE /usr/local/include)\n    link_directories(BEFORE /usr/local/lib)\nelse()\n    list(APPEND SWOOLE_LINK_LIBRARIES rt crypt)\nendif()\n\nfind_package(PkgConfig REQUIRED)\n\nif (UNIX AND NOT APPLE)\n    find_library(URING_LIBRARIES uring)\n    if (URING_LIBRARIES)\n        message(STATUS \"Found iouring\")\n        list(APPEND SWOOLE_LINK_LIBRARIES ${URING_LIBRARIES})\n    else()\n        message(WARNING \"liburing not found.\")\n    endif()\nendif()\n\nif (NOT CMAKE_BUILD_TYPE)\n    set(CMAKE_BUILD_TYPE Debug CACHE STRING \"Build type\" FORCE)\nendif ()\n\n# Code Coverage Configuration\nadd_library(coverage_config INTERFACE)\n\noption(CODE_COVERAGE \"Enable coverage reporting\" OFF)\nif(CODE_COVERAGE)\n    message(STATUS \"Open coverage\")\n    # --coverage => -fprofile-arcs -ftest-coverage\n    target_compile_options(coverage_config INTERFACE\n        -O0\n        -g\n        -fprofile-update=atomic\n        --coverage\n    )\n    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)\n        target_link_options(coverage_config INTERFACE --coverage)\n    else()\n        target_link_libraries(coverage_config INTERFACE --coverage)\n    endif()\nendif(CODE_COVERAGE)\n\nfile(GLOB_RECURSE SRC_LIST FOLLOW_SYMLINKS src/*.c src/*.cc\n            thirdparty/boost/asm/combined.S\n            thirdparty/hiredis/alloc.c\n            thirdparty/hiredis/async.c\n            thirdparty/hiredis/hiredis.c\n            thirdparty/hiredis/net.c\n            thirdparty/hiredis/read.c\n            thirdparty/hiredis/sds.c\n            thirdparty/llhttp/api.c\n            thirdparty/llhttp/http.c\n            thirdparty/llhttp/llhttp.c\n        \tthirdparty/multipart_parser.c\n)\nfile(GLOB_RECURSE HEAD_FILES FOLLOW_SYMLINKS include/*.h)\nfile(GLOB_RECURSE HEAD_WAPPER_FILES FOLLOW_SYMLINKS include/wrapper/*.hpp)\n\nset(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)\nset(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)\n\n#message(STATUS \"source=${SRC_LIST}\")\n#message(STATUS \"header=${HEAD_FILES}\")\n\nadd_definitions(-DHAVE_CONFIG_H)\n# test\n#add_definitions(-DSW_USE_THREAD_CONTEXT)\n\ninclude_directories(BEFORE ./include ./include/wrapper ext-src/ thirdparty/ thirdparty/llhttp ./)\n\n# find OpenSSL\nif (DEFINED openssl_dir)\n    include_directories(BEFORE ${openssl_dir}/include)\n    link_directories(${openssl_dir}/lib)\nelse()\n    find_package(OpenSSL)\n    if (${OPENSSL_FOUND})\n        message(STATUS \"Found OpenSSL, ${OPENSSL_LIBRARIES}\")\n        include_directories(BEFORE ${OPENSSL_INCLUDE_DIR})\n        list(APPEND SWOOLE_LINK_LIBRARIES ssl crypto)\n    else()\n        message(STATUS \"Not found OpenSSL\")\n    endif()\nendif()\n\nif (DEFINED brotli_dir)\n    include_directories(BEFORE ${brotli_dir}/include)\n    link_directories(${brotli_dir}/lib)\nendif()\n\nforeach (LINE ${SWOOLE_CONFIG_FILE})\n  if (\"${LINE}\" MATCHES \"define SW_USE_CARES 1\")\n      message(STATUS \"enable c-ares\")\n      list(APPEND SWOOLE_LINK_LIBRARIES cares)\n  endif()\nendforeach()\n\nif (DEFINED enable_trace_log)\n\tadd_definitions(-DSW_LOG_TRACE_OPEN)\nendif()\n\nif (DEFINED enable_asan)\n\tset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer\")\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer\")\n    add_definitions(-DSW_USE_ASAN)\nendif()\n\nif (DEFINED enable_thread)\n    add_definitions(-DSW_THREAD)\nendif()\n\nif (DEFINED async_io)\n    # This macro is exclusively used for kernel unit testing to\n    # verify the asynchronous operation functionality of the file thread pool.\n    add_definitions(-DSW_USE_ASYNC)\nendif()\n\nif (DEFINED verbose)\n    add_definitions(-DSW_VERBOSE)\nendif()\n\nset(php_dir \"\" CACHE STRING \"Set the root directory of PHP\")\n\nif (DEFINED php_dir)\n    set(PHP_CONFIG \"${php_dir}/bin/php-config\")\nelse ()\n    set(PHP_CONFIG \"php-config\")\nendif()\n\nexecute_process(COMMAND ${PHP_CONFIG} --includes OUTPUT_VARIABLE PHP_INCLUDES OUTPUT_STRIP_TRAILING_WHITESPACE  RESULT_VARIABLE PHP_CONFIG_RESULT)\nif (NOT PHP_CONFIG_RESULT EQUAL 0)\n    message(FATAL_ERROR \"Failed to execute php-config: ${PHP_CONFIG_RESULT}\")\nendif()\n\nexecute_process(COMMAND ${PHP_CONFIG} --extension-dir OUTPUT_VARIABLE PHP_EXTENSION_DIR OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE PHP_CONFIG_RESULT)\nif (NOT PHP_CONFIG_RESULT EQUAL 0)\n    message(FATAL_ERROR \"Failed to execute php-config: ${PHP_CONFIG_RESULT}\")\nendif()\n\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} ${PHP_INCLUDES}\")\nset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} ${PHP_INCLUDES}\")\n\nif (CMAKE_SYSTEM_NAME MATCHES \"Linux\")\n    execute_process(COMMAND ldconfig -p OUTPUT_VARIABLE LDCONFIG_LIST OUTPUT_STRIP_TRAILING_WHITESPACE)\n    #message(STATUS LDCONFIG_LIST)\n    if (LDCONFIG_LIST MATCHES brotlienc)\n         list(APPEND SWOOLE_LINK_LIBRARIES brotlienc)\n    endif()\n\n    if (LDCONFIG_LIST MATCHES brotlidec)\n        list(APPEND SWOOLE_LINK_LIBRARIES brotlidec)\n    endif()\nendif()\n\n# lib-swoole\nlink_directories(${LIBRARY_OUTPUT_PATH})\nadd_library(lib-swoole SHARED ${SRC_LIST})\nset_target_properties(lib-swoole PROPERTIES OUTPUT_NAME \"swoole\" VERSION ${SWOOLE_VERSION})\ntarget_link_libraries(lib-swoole ${SWOOLE_LINK_LIBRARIES})\n\nif (CODE_COVERAGE)\n    target_link_libraries(lib-swoole coverage_config gcov)\nendif(CODE_COVERAGE)\n\n# test_server\nset(TEST_SRC_LIST examples/cpp/test_server.cc)\nadd_executable(test_server ${TEST_SRC_LIST})\nadd_dependencies(test_server lib-swoole)\ntarget_link_libraries(test_server swoole pthread)\n\n# co\nset(TEST_SRC_LIST examples/cpp/co.cc)\nadd_executable(co ${TEST_SRC_LIST})\nadd_dependencies(co lib-swoole)\ntarget_link_libraries(co swoole)\n\n# ext-swoole\nfile(GLOB ext_cxx_files ext-src/*.cc)\nset(ext_src_list  ${ext_cxx_files}\n        thirdparty/php/curl/interface.cc\n        thirdparty/php/curl/multi.cc\n        thirdparty/php84/curl/interface.cc\n\t    thirdparty/php84/curl/multi.cc\t\n\t    thirdparty/php/sockets/multicast.cc\n        thirdparty/php/sockets/sendrecvmsg.cc\n        thirdparty/php/sockets/conversions.cc\n        thirdparty/php/sockets/sockaddr_conv.cc\n        thirdparty/php/standard/proc_open.cc\n        thirdparty/php/standard/var_decoder.cc\n\t    thirdparty/nghttp2/nghttp2_hd.c\n        thirdparty/nghttp2/nghttp2_rcbuf.c\n        thirdparty/nghttp2/nghttp2_helper.c\n        thirdparty/nghttp2/nghttp2_buf.c\n        thirdparty/nghttp2/nghttp2_mem.c\n        thirdparty/nghttp2/nghttp2_hd_huffman.c\n        thirdparty/nghttp2/nghttp2_hd_huffman_data.c\n    )\nadd_library(ext-swoole SHARED ${ext_src_list})\nset_target_properties(ext-swoole PROPERTIES PREFIX \"\")\nset_target_properties(ext-swoole PROPERTIES OUTPUT_NAME \"swoole\")\nadd_dependencies(ext-swoole lib-swoole)\n\n# core-tests\npkg_check_modules(NGHTTP2 REQUIRED libnghttp2)\nif (${NGHTTP2_FOUND})\n    message(STATUS \"Found nghttp2\")\nelse()\n    message(STATUS \"Not found nghttp2\")\nendif()\n\n# find GTest\nfind_package(GTest REQUIRED)\nif (!${GTEST_FOUND})\n    message(FATAL_ERROR \"Not found GTest\")\nendif()\nmessage(STATUS \"Found GTest\")\n\nfile(GLOB_RECURSE core_test_files core-tests/src/*.cpp thirdparty/llhttp/*.c)\nadd_executable(core-tests ${core_test_files})\nadd_dependencies(core-tests lib-swoole)\ninclude_directories(BEFORE core-tests/include thirdparty thirdparty/hiredis thirdparty/llhttp/ ${GTEST_INCLUDE_DIRS} ${NGHTTP2_INCLUDE_DIR})\ntarget_link_libraries(core-tests swoole pthread ssl crypto ${GTEST_BOTH_LIBRARIES} ${NGHTTP2_LIBRARIES})\n\n# find libpq\nif (DEFINED libpq_dir)\n    include_directories(BEFORE ${libpq_dir}/include)\n    link_directories(${libpq_dir}/lib)\nelse()\n    pkg_check_modules(LIBPQ REQUIRED libpq)\n    target_include_directories(ext-swoole PRIVATE ${LIBPQ_INCLUDE_DIRS})\nendif()\n\ntarget_link_libraries(ext-swoole swoole pq curl)\n\n# install\nINSTALL(TARGETS ext-swoole LIBRARY DESTINATION ${PHP_EXTENSION_DIR})\nINSTALL(TARGETS lib-swoole LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)\nINSTALL(FILES ${HEAD_FILES} DESTINATION include/swoole)\nINSTALL(FILES ${HEAD_WAPPER_FILES} DESTINATION include/swoole/wrapper)\nINSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/config.h DESTINATION include/swoole)\n"
  },
  {
    "path": "Dockerfile",
    "content": "# This Dockerfile is designed to create a debug version of the PHP and Swoole environment,\n # enabling ASAN (`--enable-address-sanitizer`) to facilitate debugging and analysis of runtime crashes.\nFROM ubuntu:22.04\nARG PHP_VERSION=8.2.28\nRUN apt update\nRUN apt install -y g++ cmake automake wget pkg-config git xz-utils\nRUN apt install -y libssl-dev libcurl4-openssl-dev libxml2-dev libzip-dev libsqlite3-dev libreadline-dev libonig-dev \\\n    libbz2-dev libffi-dev libxslt-dev unixodbc-dev libpq-dev libbrotli-dev libc-ares-dev\n\nRUN mkdir /work\nWORKDIR /work\n\nRUN wget https://www.php.net/distributions/php-${PHP_VERSION}.tar.xz\nRUN tar -xvf php-${PHP_VERSION}.tar.xz\n\nCOPY . /work/php-${PHP_VERSION}/ext/swoole\n\nRUN cd php-${PHP_VERSION} && ./buildconf --force && \\\n    ./configure --enable-mbstring --with-curl --with-openssl \\\n    --enable-soap --enable-intl --enable-bcmath --enable-sockets \\\n    --with-pear --with-webp --with-jpeg --with-ffi \\\n    --enable-sysvsem --enable-sysvshm --enable-sysvmsg --with-zlib --with-bz2 --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-xsl \\\n    --without-pdo-sqlite \\\n    --enable-debug --enable-address-sanitizer \\\n    --enable-swoole \\\n    --enable-swoole-curl   \\\n    --enable-swoole-pgsql  \\\n    --enable-swoole-sqlite \\\n    --enable-mysqlnd \\\n    --enable-cares \\\n    --with-swoole-odbc=unixODBC,/usr \\\n    --enable-brotli && \\\n    make clean && make -j $(nproc) && make install\n\nRUN php -v\nRUN php -m\nRUN php --ri swoole\nRUN php --ri curl\nRUN php --ri openssl\nRUN cd /work/php-${PHP_VERSION} && make clean\nRUN cd /work && rm php-${PHP_VERSION}.tar.xz && rm -rf php-${PHP_VERSION}/ext/swoole/.git\nRUN rm -rf /var/lib/apt/lists/* /usr/bin/qemu-*-static\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright Tianfeng.Han [rango@swoole.com]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "Makefile.frag",
    "content": "swoole-build-coverage:\n\tCCACHE_DISABLE=1 EXTRA_CFLAGS=\"-fprofile-arcs -ftest-coverage\" EXTRA_CXXFLAGS=\"-fprofile-arcs -ftest-coverage\" $(MAKE)\n\nswoole-test-coverage:\n\tCCACHE_DISABLE=1 EXTRA_CFLAGS=\"-fprofile-arcs -ftest-coverage\" EXTRA_CXXFLAGS=\"-fprofile-arcs -ftest-coverage\" $(MAKE) && $(MAKE) install && $(top_srcdir)/tests/start.sh $(top_srcdir)/tests\n\nswoole-test-coverage-lcov: swoole-test-coverage\n\tlcov -c --directory $(top_srcdir)/.libs --output-file $(top_srcdir)/coverage.info\n\nswoole-test-coverage-html: swoole-test-coverage-lcov\n\tgenhtml $(top_srcdir)/coverage.info --output-directory=$(top_srcdir)/html\n"
  },
  {
    "path": "README.md",
    "content": "<h2 align=center>\n<img width=\"200\" height=\"120\" alt=\"Swoole Logo\" src=\"docs/swoole-logo.svg\" /> <br />\n    Swoole is an event-driven, asynchronous, coroutine-based concurrency library with high performance for PHP.\n</h2>\n\n[![Compiler Tests](https://github.com/swoole/swoole-src/actions/workflows/ext.yml/badge.svg)](https://github.com/swoole/swoole-src/actions/workflows/ext.yml)\n[![Core Test](https://github.com/swoole/swoole-src/actions/workflows/core.yml/badge.svg)](https://github.com/swoole/swoole-src/actions/workflows/core.yml)\n[![Unit Tests](https://github.com/swoole/swoole-src/actions/workflows/unit.yml/badge.svg)](https://github.com/swoole/swoole-src/actions/workflows/unit.yml)\n[![Thread Support Tests](https://github.com/swoole/swoole-src/actions/workflows/thread.yml/badge.svg)](https://github.com/swoole/swoole-src/actions/workflows/thread.yml)\n[![Linux io_uring Tests](https://github.com/swoole/swoole-src/actions/workflows/iouring.yml/badge.svg)](https://github.com/swoole/swoole-src/actions/workflows/iouring.yml)\n[![Frameworks Tests](https://github.com/swoole/swoole-src/actions/workflows/framework.yml/badge.svg)](https://github.com/swoole/swoole-src/actions/workflows/framework.yml)\n\n[![Twitter](https://badgen.net/badge/icon/twitter?icon=twitter&label)](https://twitter.com/phpswoole)\n[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.swoole.dev)\n[![Latest Release](https://img.shields.io/github/release/swoole/swoole-src.svg)](https://github.com/swoole/swoole-src/releases/)\n[![License](https://badgen.net/github/license/swoole/swoole-src)](https://github.com/swoole/swoole-src/blob/master/LICENSE)\n[![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src)\n[![Codecov](https://codecov.io/gh/swoole/swoole-src/branch/master/graph/badge.svg)](https://codecov.io/gh/swoole/swoole-src)\n\n## ⚙️ Quick Start\n\nRun Swoole program by [Docker](https://github.com/swoole/docker-swoole)\n\n```bash\ndocker run --rm phpswoole/swoole \"php --ri swoole\"\n```\n\n> For details on how to use it, see: [How to Use This Image](https://github.com/swoole/docker-swoole#how-to-use-this-image).\n\n## Documentation\n<https://wiki.swoole.com/>\n\n### HTTP Service\n```php\n$http = new Swoole\\Http\\Server('127.0.0.1', 9501);\n$http->set(['hook_flags' => SWOOLE_HOOK_ALL]);\n\n$http->on('request', function ($request, $response) {\n    $result = [];\n    Co::join([\n        go(function () use (&$result) {\n            $result['google'] = file_get_contents(\"https://www.google.com/\");\n        }),\n        go(function () use (&$result) {\n            $result['taobao'] = file_get_contents(\"https://www.taobao.com/\");\n        })\n    ]);\n    $response->end(json_encode($result));\n});\n\n$http->start();\n```\n\n### Concurrency\n```php\nCo\\run(function() {\n    Co\\go(function() {\n        while(1) {\n            sleep(1);\n            $fp = stream_socket_client(\"tcp://127.0.0.1:8000\", $errno, $errstr, 30);\n            echo fread($fp, 8192), PHP_EOL;\n        }\n    });\n\n    Co\\go(function() {\n        $fp = stream_socket_server(\"tcp://0.0.0.0:8000\", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);\n        while(1) {\n            $conn = stream_socket_accept($fp);\n            fwrite($conn, 'The local time is ' . date('n/j/Y g:i a'));\n        }\n    });\n\n    Co\\go(function() {\n        $redis = new Redis();\n        $redis->connect('127.0.0.1', 6379);\n        while(true) {\n            $redis->subscribe(['test'], function ($instance, $channelName, $message) {\n                echo 'New redis message: '.$channelName, \"==>\", $message, PHP_EOL;\n            });\n        }\n    });\n\n    Co\\go(function() {\n        $redis = new Redis();\n        $redis->connect('127.0.0.1', 6379);\n        $count = 0;\n        while(true) {\n            sleep(2);\n            $redis->publish('test','hello, world, count='.$count++);\n        }\n    });\n});\n```\n\n## Runtime Hook\n\n**Swoole hooks the blocking io function of PHP at the `bottom layer` and `automatically` converts it to a non-blocking function, so that these functions can be called concurrently in coroutines.**\n\n### Supported extension/functions\n\n* `ext-curl` (Support `symfony` and `guzzle`)\n* `ext-redis`\n* `ext-mysqli`\n* `ext-pdo_mysql`\n* `ext-pdo_pgsql`\n* `ext-pdo_sqlite`\n* `ext-pdo_oracle`\n* `ext-pdo_odbc`\n* `stream functions` (e.g. `stream_socket_client`/`stream_socket_server`), Supports `TCP`/`UDP`/`UDG`/`Unix`/`SSL/TLS`/`FileSystem API`/`Pipe`\n* `ext-sockets`\n* `ext-soap`\n* `sleep`/`usleep`/`time_sleep_until`\n* `proc_open`\n* `gethostbyname`/`shell_exec`/`exec`\n* `fread`/`fopen`/`fsockopen`/`fwrite`/`flock`\n\n\n## 🛠 Develop & Discussion\n\n+ __IDE Helper & API__: <https://github.com/swoole/ide-helper>\n+ __Twitter__: <https://twitter.com/phpswoole>\n+ __Discord__: <https://discord.swoole.dev>\n+ __中文社区__: <https://wiki.swoole.com/#/other/discussion>\n\n## 💎 Awesome Swoole\nProject [Awesome Swoole](https://github.com/swoole/awesome-swoole) maintains a curated list of awesome things related to Swoole, including\n\n* Swoole-based frameworks and libraries.\n* Packages to integrate Swoole with popular PHP frameworks, including Laravel, Symfony, Slim, and Yii.\n* Books, videos, and other learning materials about Swoole.\n* Debugging, profiling, and testing tools for developing Swoole-based applications.\n* Coroutine-friendly packages and libraries.\n* Other Swoole related projects and resources.\n\n## ✨ Event-based\n\nThe network layer in Swoole is event-based and takes full advantage of the underlying epoll/kqueue implementation, making it really easy to serve millions of requests.\n\nSwoole 4.x uses a brand new engine kernel and now it has a full-time developer team, so we are entering an unprecedented period in PHP history which offers a unique possibility for rapid evolution in performance.\n\n## ⚡ Coroutine\n\nSwoole 4.x or later supports the built-in coroutine with high availability, and you can use fully synchronized code to implement asynchronous performance. PHP code without any additional keywords, the underlying automatic coroutine-scheduling.\n\nDevelopers can understand coroutines as ultra-lightweight threads, and you can easily create thousands of coroutines in a single process.\n\n### MySQL\n\nConcurrency 10K requests to read data from MySQL takes only 0.2s!\n\n```php\n$s = microtime(true);\nCo\\run(function() {\n    for ($c = 100; $c--;) {\n        go(function () {\n            $mysql = new Swoole\\Coroutine\\MySQL;\n            $mysql->connect([\n                'host' => '127.0.0.1',\n                'user' => 'root',\n                'password' => 'root',\n                'database' => 'test'\n            ]);\n            $statement = $mysql->prepare('SELECT * FROM `user`');\n            for ($n = 100; $n--;) {\n                $result = $statement->execute();\n                assert(count($result) > 0);\n            }\n        });\n    }\n});\necho 'use ' . (microtime(true) - $s) . ' s';\n```\n\n### Mixed server\n\nYou can create multiple services on the single event loop: TCP, HTTP, Websocket and HTTP2, and easily handle thousands of requests.\n\n```php\nfunction tcp_pack(string $data): string\n{\n    return pack('N', strlen($data)) . $data;\n}\nfunction tcp_unpack(string $data): string\n{\n    return substr($data, 4, unpack('N', substr($data, 0, 4))[1]);\n}\n$tcp_options = [\n    'open_length_check' => true,\n    'package_length_type' => 'N',\n    'package_length_offset' => 0,\n    'package_body_offset' => 4\n];\n```\n\n```php\n$server = new Swoole\\WebSocket\\Server('127.0.0.1', 9501, SWOOLE_BASE);\n$server->set(['open_http2_protocol' => true]);\n// http && http2\n$server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n    $response->end('Hello ' . $request->rawcontent());\n});\n// websocket\n$server->on('message', function (Swoole\\WebSocket\\Server $server, Swoole\\WebSocket\\Frame $frame) {\n    $server->push($frame->fd, 'Hello ' . $frame->data);\n});\n// tcp\n$tcp_server = $server->listen('127.0.0.1', 9502, SWOOLE_TCP);\n$tcp_server->set($tcp_options);\n$tcp_server->on('receive', function (Swoole\\Server $server, int $fd, int $reactor_id, string $data) {\n    $server->send($fd, tcp_pack('Hello ' . tcp_unpack($data)));\n});\n$server->start();\n```\n\n### Coroutine clients\n\nWhether you DNS query or send requests or receive responses, all of these are scheduled by coroutine automatically.\n\n```php\ngo(function () {\n    // http\n    $http_client = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', 9501);\n    assert($http_client->post('/', 'Swoole Http'));\n    var_dump($http_client->body);\n    // websocket\n    $http_client->upgrade('/');\n    $http_client->push('Swoole Websocket');\n    var_dump($http_client->recv()->data);\n});\ngo(function () {\n    // http2\n    $http2_client = new Swoole\\Coroutine\\Http2\\Client('localhost', 9501);\n    $http2_client->connect();\n    $http2_request = new Swoole\\Http2\\Request;\n    $http2_request->method = 'POST';\n    $http2_request->data = 'Swoole Http2';\n    $http2_client->send($http2_request);\n    $http2_response = $http2_client->recv();\n    var_dump($http2_response->data);\n});\ngo(function () use ($tcp_options) {\n    // tcp\n    $tcp_client = new Swoole\\Coroutine\\Client(SWOOLE_TCP);\n    $tcp_client->set($tcp_options);\n    $tcp_client->connect('127.0.0.1', 9502);\n    $tcp_client->send(tcp_pack('Swoole Tcp'));\n    var_dump(tcp_unpack($tcp_client->recv()));\n});\n```\n\n### Channel\n\nChannel is the only way for exchanging data between coroutines, the development combination of the `Coroutine + Channel` is the famous CSP programming model.\n\nIn Swoole development, Channel is usually used for implementing connection pool or scheduling coroutine concurrent.\n\n#### The simplest example of a connection pool\n\nIn the following example, we have a thousand concurrently requests to redis. Normally, this has exceeded the maximum number of Redis connections setting and will throw a connection exception, but the connection pool based on Channel can perfectly schedule requests. We don't have to worry about connection overload.\n\n```php\nclass RedisPool\n{\n    /**@var \\Swoole\\Coroutine\\Channel */\n    protected $pool;\n\n    /**\n     * RedisPool constructor.\n     * @param int $size max connections\n     */\n    public function __construct(int $size = 100)\n    {\n        $this->pool = new \\Swoole\\Coroutine\\Channel($size);\n        for ($i = 0; $i < $size; $i++) {\n            $redis = new \\Swoole\\Coroutine\\Redis();\n            $res = $redis->connect('127.0.0.1', 6379);\n            if ($res == false) {\n                throw new \\RuntimeException(\"failed to connect redis server.\");\n            } else {\n                $this->put($redis);\n            }\n        }\n    }\n\n    public function get(): \\Swoole\\Coroutine\\Redis\n    {\n        return $this->pool->pop();\n    }\n\n    public function put(\\Swoole\\Coroutine\\Redis $redis)\n    {\n        $this->pool->push($redis);\n    }\n\n    public function close(): void\n    {\n        $this->pool->close();\n        $this->pool = null;\n    }\n}\n\ngo(function () {\n    $pool = new RedisPool();\n    // max concurrency num is more than max connections\n    // but it's no problem, channel will help you with scheduling\n    for ($c = 0; $c < 1000; $c++) {\n        go(function () use ($pool, $c) {\n            for ($n = 0; $n < 100; $n++) {\n                $redis = $pool->get();\n                assert($redis->set(\"awesome-{$c}-{$n}\", 'swoole'));\n                assert($redis->get(\"awesome-{$c}-{$n}\") === 'swoole');\n                assert($redis->delete(\"awesome-{$c}-{$n}\"));\n                $pool->put($redis);\n            }\n        });\n    }\n});\n```\n\n#### Producer and consumers\n\nSome Swoole's clients implement the defer mode for concurrency, but you can still implement it flexible with a combination of coroutines and channels.\n\n```php\ngo(function () {\n    // User: I need you to bring me some information back.\n    // Channel: OK! I will be responsible for scheduling.\n    $channel = new Swoole\\Coroutine\\Channel;\n    go(function () use ($channel) {\n        // Coroutine A: Ok! I will show you the github addr info\n        $addr_info = Co::getaddrinfo('github.com');\n        $channel->push(['A', json_encode($addr_info, JSON_PRETTY_PRINT)]);\n    });\n    go(function () use ($channel) {\n        // Coroutine B: Ok! I will show you what your code look like\n        $mirror = Co::readFile(__FILE__);\n        $channel->push(['B', $mirror]);\n    });\n    go(function () use ($channel) {\n        // Coroutine C: Ok! I will show you the date\n        $channel->push(['C', date(DATE_W3C)]);\n    });\n    for ($i = 3; $i--;) {\n        list($id, $data) = $channel->pop();\n        echo \"From {$id}:\\n {$data}\\n\";\n    }\n    // User: Amazing, I got every information at earliest time!\n});\n```\n\n### Timer\n\n```php\n$id = Swoole\\Timer::tick(100, function () {\n    echo \"⚙️ Do something...\\n\";\n});\nSwoole\\Timer::after(500, function () use ($id) {\n    Swoole\\Timer::clear($id);\n    echo \"⏰ Done\\n\";\n});\nSwoole\\Timer::after(1000, function () use ($id) {\n    if (!Swoole\\Timer::exists($id)) {\n        echo \"✅ All right!\\n\";\n    }\n});\n```\n#### The way of coroutine\n\n```php\ngo(function () {\n    $i = 0;\n    while (true) {\n        Co::sleep(0.1);\n        echo \"📝 Do something...\\n\";\n        if (++$i === 5) {\n            echo \"🛎 Done\\n\";\n            break;\n        }\n    }\n    echo \"🎉 All right!\\n\";\n});\n```\n\n## 🔥 Amazing runtime hooks\n\n**As of Swoole v4.1.0, we added the ability to transform synchronous PHP network libraries into co-routine libraries using a single line of code.**\n\nSimply call the `Swoole\\Runtime::enableCoroutine()` method at the top of your script. In the sample below we connect to php-redis and concurrently read 10k requests in 0.1s:\n\n```php\nSwoole\\Runtime::enableCoroutine();\n$s = microtime(true);\nCo\\run(function() {\n    for ($c = 100; $c--;) {\n        go(function () {\n            ($redis = new Redis)->connect('127.0.0.1', 6379);\n            for ($n = 100; $n--;) {\n                assert($redis->get('awesome') === 'swoole');\n            }\n        });\n    }\n});\necho 'use ' . (microtime(true) - $s) . ' s';\n```\n\nBy calling this method, the Swoole kernel replaces ZendVM stream function pointers. If you use `php_stream` based extensions, all socket operations can be dynamically converted to be asynchronous IO scheduled by coroutine at runtime!\n\n### How many things you can do in 1s?\n\nSleep 10K times, read, write, check and delete files 10K times, use PDO and MySQLi to communicate with the database 10K times, create a TCP server and multiple clients to communicate with each other 10K times, create a UDP server and multiple clients to communicate with each other 10K times... Everything works well in one process!\n\nJust see what the Swoole brings, just imagine...\n\n```php\nSwoole\\Runtime::enableCoroutine();\n$s = microtime(true);\nCo\\run(function() {\n    // i just want to sleep...\n    for ($c = 100; $c--;) {\n        go(function () {\n            for ($n = 100; $n--;) {\n                usleep(1000);\n            }\n        });\n    }\n\n    // 10K file read and write\n    for ($c = 100; $c--;) {\n        go(function () use ($c) {\n            $tmp_filename = \"/tmp/test-{$c}.php\";\n            for ($n = 100; $n--;) {\n                $self = file_get_contents(__FILE__);\n                file_put_contents($tmp_filename, $self);\n                assert(file_get_contents($tmp_filename) === $self);\n            }\n            unlink($tmp_filename);\n        });\n    }\n\n    // 10K pdo and mysqli read\n    for ($c = 50; $c--;) {\n        go(function () {\n            $pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8', 'root', 'root');\n            $statement = $pdo->prepare('SELECT * FROM `user`');\n            for ($n = 100; $n--;) {\n                $statement->execute();\n                assert(count($statement->fetchAll()) > 0);\n            }\n        });\n    }\n    for ($c = 50; $c--;) {\n        go(function () {\n            $mysqli = new Mysqli('127.0.0.1', 'root', 'root', 'test');\n            $statement = $mysqli->prepare('SELECT `id` FROM `user`');\n            for ($n = 100; $n--;) {\n                $statement->bind_result($id);\n                $statement->execute();\n                $statement->fetch();\n                assert($id > 0);\n            }\n        });\n    }\n\n    // php_stream tcp server & client with 12.8K requests in single process\n    function tcp_pack(string $data): string\n    {\n        return pack('n', strlen($data)) . $data;\n    }\n\n    function tcp_length(string $head): int\n    {\n        return unpack('n', $head)[1];\n    }\n\n    go(function () {\n        $ctx = stream_context_create(['socket' => ['so_reuseaddr' => true, 'backlog' => 128]]);\n        $socket = stream_socket_server(\n            'tcp://0.0.0.0:9502',\n            $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctx\n        );\n        if (!$socket) {\n            echo \"$errstr ($errno)\\n\";\n        } else {\n            $i = 0;\n            while ($conn = stream_socket_accept($socket, 1)) {\n                stream_set_timeout($conn, 5);\n                for ($n = 100; $n--;) {\n                    $data = fread($conn, tcp_length(fread($conn, 2)));\n                    assert($data === \"Hello Swoole Server #{$n}!\");\n                    fwrite($conn, tcp_pack(\"Hello Swoole Client #{$n}!\"));\n                }\n                if (++$i === 128) {\n                    fclose($socket);\n                    break;\n                }\n            }\n        }\n    });\n    for ($c = 128; $c--;) {\n        go(function () {\n            $fp = stream_socket_client(\"tcp://127.0.0.1:9502\", $errno, $errstr, 1);\n            if (!$fp) {\n                echo \"$errstr ($errno)\\n\";\n            } else {\n                stream_set_timeout($fp, 5);\n                for ($n = 100; $n--;) {\n                    fwrite($fp, tcp_pack(\"Hello Swoole Server #{$n}!\"));\n                    $data = fread($fp, tcp_length(fread($fp, 2)));\n                    assert($data === \"Hello Swoole Client #{$n}!\");\n                }\n                fclose($fp);\n            }\n        });\n    }\n\n    // udp server & client with 12.8K requests in single process\n    go(function () {\n        $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_DGRAM, 0);\n        $socket->bind('127.0.0.1', 9503);\n        $client_map = [];\n        for ($c = 128; $c--;) {\n            for ($n = 0; $n < 100; $n++) {\n                $recv = $socket->recvfrom($peer);\n                $client_uid = \"{$peer['address']}:{$peer['port']}\";\n                $id = $client_map[$client_uid] = ($client_map[$client_uid] ?? -1) + 1;\n                assert($recv === \"Client: Hello #{$id}!\");\n                $socket->sendto($peer['address'], $peer['port'], \"Server: Hello #{$id}!\");\n            }\n        }\n        $socket->close();\n    });\n    for ($c = 128; $c--;) {\n        go(function () {\n            $fp = stream_socket_client(\"udp://127.0.0.1:9503\", $errno, $errstr, 1);\n            if (!$fp) {\n                echo \"$errstr ($errno)\\n\";\n            } else {\n                for ($n = 0; $n < 100; $n++) {\n                    fwrite($fp, \"Client: Hello #{$n}!\");\n                    $recv = fread($fp, 1024);\n                    list($address, $port) = explode(':', (stream_socket_get_name($fp, true)));\n                    assert($address === '127.0.0.1' && (int)$port === 9503);\n                    assert($recv === \"Server: Hello #{$n}!\");\n                }\n                fclose($fp);\n            }\n        });\n    }\n});\necho 'use ' . (microtime(true) - $s) . ' s';\n```\n\n## ⌛️ Installation\n\n> As with any open source project, Swoole always provides the most reliable stability and the most powerful features in **the latest released version**. Please ensure as much as possible that you are using the latest version.\n\n### Compiling requirements\n\n+ Linux, OS X or Cygwin, WSL\n+ PHP 8.1.0 or later (The higher the version, the better the performance.)\n+ GCC 4.8 or later\n\n### 1. Install via PECL (beginners)\n\n```shell\npecl install swoole\n```\n\n### 2. Install from source (recommended)\n\nPlease download the source packages from [Releases](https://github.com/swoole/swoole-src/releases) or clone a specific version. Don't use `master` branch as it may be in development.\n\nTo clone the source code from git specify a tag:\n```shell\ngit clone --branch v6.0.0 --single-branch https://github.com/swoole/swoole-src.git && \\\ncd swoole-src\n```\n\nCompile and install at the source folder:\n\n```shell\nphpize && \\\n./configure && \\\nmake && make install\n```\n\n#### Enable extension in PHP\n\nAfter compiling and installing to the system successfully, you have to add a new line `extension=swoole.so` to `php.ini` to enable Swoole extension.\n\n#### Extra compiler configurations\n\n> for example: `./configure --enable-openssl --enable-sockets`\n\n+ `--enable-openssl` or `--with-openssl-dir=DIR`\n+ `--enable-sockets`\n+ `--enable-mysqlnd` (need mysqlnd, it just for supporting `$mysql->escape` method)\n+ `--enable-swoole-curl`\n\n### Upgrade\n\n>  ⚠️ If you upgrade from source, don't forget to `make clean` before you upgrade your swoole\n\n1. `pecl upgrade swoole`\n2. `cd swoole-src && git pull && make clean && make && sudo make install`\n3. if you change your PHP version, please re-run `phpize clean && phpize` then try to compile\n\n### Major change since version 4.3.0\n\nAsync clients and API are moved to a separate PHP extension `swoole_async` since version 4.3.0, install `swoole_async`:\n\n```shell\ngit clone https://github.com/swoole/ext-async.git\ncd ext-async\nphpize\n./configure\nmake -j 4\nsudo make install\n```\n\nEnable it by adding a new line `extension=swoole_async.so` to `php.ini`.\n\n## 🍭 Benchmark\n\n+ On the open source [Techempower Web Framework benchmarks](https://www.techempower.com/benchmarks/#section=data-r17) Swoole used MySQL database benchmark to rank first, and all performance tests ranked in the first echelon.\n+ You can just run [Benchmark Script](https://github.com/swoole/benchmark/blob/master/benchmark.php) to quickly test the maximum QPS of Swoole-HTTP-Server on your machine.\n\n## 🔰️ Security issues\n\nSecurity issues should be reported privately, via email, to the Swoole develop team [team@swoole.com](mailto:team@swoole.com). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message.\n\n## 🖊️ Contribution\n\nYour contribution to Swoole development is very welcome!\n\nYou may contribute in the following ways:\n\n* [Report issues and feedback](https://github.com/swoole/swoole-src/issues)\n* Submit fixes, features via Pull Request\n* Write/polish documentation\n\n## ❤️ Contributors\n\nThis project exists thanks to all the people who contribute. [[Contributors](https://github.com/swoole/swoole-src/graphs/contributors)].\n<a href=\"https://github.com/swoole/swoole-src/graphs/contributors\"><img src=\"https://opencollective.com/swoole-src/contributors.svg?width=890&button=false\" /></a>\n\n## 🎙️ Official Evangelist\n\n[Demin](https://deminy.in) has been playing with PHP since 2000, focusing on building high-performance, secure web services. He is an occasional conference speaker on PHP and Swoole, and has been working for companies in the states like eBay, Visa and Glu Mobile for years. You may find Demin on [Twitter](https://twitter.com/deminy) or [GitHub](https://github.com/deminy).\n\n## 📃 License\n\nApache License Version 2.0 see http://www.apache.org/licenses/LICENSE-2.0.html\n"
  },
  {
    "path": "codecov.yml",
    "content": "ignore:\n  - \"src/core/error.cc\"\n  - \"thirdparty/hiredis/*\"\n  - \"thirdparty/llhttp/*\"\n\ncoverage:\n  range: 60..80\n  round: down\n  precision: 2\n"
  },
  {
    "path": "composer.json",
    "content": "{\n  \"name\": \"swoole/swoole\",\n  \"type\": \"php-ext\",\n  \"license\": \"Apache-2.0\",\n  \"description\": \"Swoole is an event-driven, asynchronous, coroutine-based concurrency library with high performance for PHP.\",\n  \"require\": {\n    \"php\": \">=8.2 <8.6\",\n    \"ext-pdo\": \"*\"\n  },\n  \"suggest\": {\n    \"ext-pdo_mysql\": \"PDO_MYSQL is required for Swoole to work with MySQL\",\n    \"ext-pdo_pgsql\": \"PDO_PGSQL is required for Swoole to work with PostgreSQL\",\n    \"ext-curl\": \"CURL is required for Swoole to work with HTTP\",\n    \"ext-sockets\": \"Sockets is required for Swoole to work with Socket\",\n    \"ext-openssl\": \"OpenSSL is required for Swoole to work with HTTPS\",\n    \"ext-posix\": \"*\"\n  },\n  \"php-ext\": {\n    \"extension-name\": \"swoole\",\n    \"configure-options\": [\n      {\n        \"name\": \"enable-sockets\",\n        \"description\": \"Enable sockets support\"\n      },\n      {\n        \"name\": \"with-openssl-dir\",\n        \"description\": \"Specify openssl installation directory (requires OpenSSL 1.1.0 or later)?\",\n        \"needs-value\": true\n      },\n      {\n        \"name\": \"enable-mysqlnd\",\n        \"description\": \"Enable mysqlnd support\"\n      },\n      {\n        \"name\": \"enable-swoole-curl\",\n        \"description\": \"Enable curl support\"\n      },\n      {\n        \"name\": \"enable-cares\",\n        \"description\": \"Enable cares support\"\n      },\n      {\n        \"name\": \"enable-brotli\",\n        \"description\": \"Enable brotli support\"\n      },\n      {\n        \"name\": \"with-brotli-dir\",\n        \"description\": \"Specify brotli installation directory?\",\n        \"needs-value\": true\n      },\n      {\n        \"name\": \"enable-swoole-pgsql\",\n        \"description\": \"Enable PostgreSQL database support\"\n      },\n      {\n        \"name\": \"with-swoole-odbc\",\n        \"description\": \"Enable ODBC database support\",\n        \"needs-value\": true\n      },\n      {\n        \"name\": \"with-swoole-oracle\",\n        \"description\": \"Enable Oracle database support\",\n        \"needs-value\": true\n      },\n      {\n        \"name\": \"enable-swoole-sqlite\",\n        \"description\": \"Enable Sqlite database support\"\n      },\n      {\n        \"name\": \"with-swoole-firebird\",\n        \"description\": \"Enable Firebird database support\",\n        \"needs-value\": true\n      },\n      {\n        \"name\": \"enable-swoole-thread\",\n        \"description\": \"Enable swoole thread support (need php zts support)\"\n      },\n      {\n        \"name\": \"enable-iouring\",\n        \"description\": \"Enable iouring for file async support\"\n      },\n      {\n        \"name\": \"with-liburing-dir\",\n        \"description\": \"Specify liburing installation directory (requires liburing 2.8 or later)\",\n        \"needs-value\": true\n      },\n      {\n        \"name\": \"enable-uring-socket\",\n        \"description\": \"Enable iouring for http coroutine server support\"\n      },\n      {\n        \"name\": \"with-swoole-ssh2\",\n        \"description\": \"Enable async ssh2 client support\",\n        \"needs-value\": true\n      },\n      {\n        \"name\": \"enable-swoole-ftp\",\n        \"description\": \"Enable async ssh2 client support\"\n      },\n      {\n        \"name\": \"enable-zstd\",\n        \"description\": \"Enable zstd support (requires zstd 1.4.0 or later)\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "config.m4",
    "content": "dnl $Id$\ndnl config.m4 for extension swoole\n\ndnl  +----------------------------------------------------------------------+\ndnl  | Swoole                                                               |\ndnl  +----------------------------------------------------------------------+\ndnl  | This source file is subject to version 2.0 of the Apache license,    |\ndnl  | that is bundled with this package in the file LICENSE, and is        |\ndnl  | available through the world-wide-web at the following url:           |\ndnl  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\ndnl  | If you did not receive a copy of the Apache2.0 license and are unable|\ndnl  | to obtain it through the world-wide-web, please send a note to       |\ndnl  | license@swoole.com so we can mail you a copy immediately.            |\ndnl  +----------------------------------------------------------------------+\ndnl  | Author: Tianfeng Han  <rango@swoole.com>                             |\ndnl  | Author: Twosee  <twose@qq.com>                                       |\ndnl  +----------------------------------------------------------------------+\n\nPHP_ARG_ENABLE([debug-log],\n  [enable debug log],\n  [AS_HELP_STRING([--enable-debug-log],\n    [Enable swoole debug log])], [no], [no])\n\nPHP_ARG_ENABLE([trace-log],\n  [enable trace log],\n  [AS_HELP_STRING([--enable-trace-log],\n    [Enable swoole trace log])], [no], [no])\n\nPHP_ARG_ENABLE([sockets],\n  [enable sockets support],\n  [AS_HELP_STRING([--enable-sockets],\n    [Do you have sockets extension?])], [no], [no])\n\nPHP_ARG_ENABLE([swoole],\n  [swoole support],\n  [AS_HELP_STRING([--enable-swoole],\n    [Enable swoole support])], [enable_swoole=\"yes\"])\n\nPHP_ARG_ENABLE([mysqlnd],\n  [enable mysqlnd support],\n  [AS_HELP_STRING([--enable-mysqlnd],\n    [Enable mysqlnd])], [no], [no])\n\nPHP_ARG_ENABLE([cares],\n  [enable c-ares support],\n  [AS_HELP_STRING([--enable-cares],\n    [Enable cares])], [no], [no])\n\nPHP_ARG_ENABLE([iouring],\n  [enable io-uring support],\n  [AS_HELP_STRING([--enable-iouring],\n    [Enable io-uring])], [no], [no])\n    \nPHP_ARG_WITH([liburing_dir],\n  [dir of liburing],\n  [AS_HELP_STRING([[--with-liburing-dir[=DIR]]],\n    [Include liburing support (requires liburing >= 2.13)])], [no], [no])\n    \nPHP_ARG_ENABLE([uring_socket],\n  [enable uring_socket support],\n  [AS_HELP_STRING([--enable-uring-socket],\n    [Enable iouring socket support])], [no], [no])\n\nPHP_ARG_WITH([openssl_dir],\n  [dir of openssl],\n  [AS_HELP_STRING([[--with-openssl-dir[=DIR]]],\n    [Include OpenSSL support (requires OpenSSL >= 1.1.0)])], [no], [no])\n\nPHP_ARG_ENABLE([brotli],\n  [enable brotli support],\n  [AS_HELP_STRING([[--enable-brotli]],\n    [Use brotli])], [auto], [no])\n\nPHP_ARG_WITH([brotli_dir],\n  [dir of brotli],\n  [AS_HELP_STRING([[--with-brotli-dir[=DIR]]],\n    [Include Brotli support])], [no], [no])\n\nPHP_ARG_ENABLE([zstd],\n  [enable zstd support],\n  [AS_HELP_STRING([[--enable-zstd]],\n    [Use zstd])], [no], [no])\n\nPHP_ARG_WITH([nghttp2_dir],\n  [dir of nghttp2],\n  [AS_HELP_STRING([[--with-nghttp2-dir[=DIR]]],\n    [Include nghttp2 support])], [no], [no])\n\nPHP_ARG_ENABLE([asan],\n  [enable asan],\n  [AS_HELP_STRING([--enable-asan],\n    [Enable asan])], [no], [no])\n\nPHP_ARG_ENABLE([swoole-coverage],\n  [whether to enable swoole coverage support],\n  [AS_HELP_STRING([--enable-swoole-coverage],\n    [Enable swoole coverage support])], [no], [no])\n\nPHP_ARG_ENABLE([swoole-dev],\n  [whether to enable Swoole developer build flags],\n  [AS_HELP_STRING([--enable-swoole-dev],\n    [Enable developer flags])], [no], [no])\n\nPHP_ARG_ENABLE([swoole-curl],\n  [whether to enable Swoole CURL build flags],\n  [AS_HELP_STRING([--enable-swoole-curl],\n    [Enable cURL support])], [no], [no])\n\nPHP_ARG_ENABLE([swoole-pgsql],\n  [whether to enable postgresql build flags],\n  [AS_HELP_STRING([--enable-swoole-pgsql],\n    [Enable postgresql support])], [no], [no])\n\nPHP_ARG_WITH([swoole-firebird],\n  [whether to enable firebird build flags],\n  [AS_HELP_STRING([[--with-swoole-firebird[=DIR]]],\n    [PDO: Async Firebird support. DIR is the Firebird base install directory\n    [/opt/firebird]])], [no], [no])\n\nPHP_ARG_WITH([swoole-ssh2],\n  [whether to enable ssh2 support],\n  [AS_HELP_STRING([[--with-swoole-ssh2[=DIR]]],\n    [Enable Async ssh2 support. DIR is the libssh2 base install directory\n    [/usr]])], [no], [no])\n\nPHP_ARG_ENABLE([swoole-ftp],\n  [whether to enable Async FTP support],\n  [AS_HELP_STRING([--enable-swoole-ftp],\n    [Enable Async FTP support])], [no], [no])\n\nPHP_ARG_ENABLE([thread-context],\n  [whether to enable thread context],\n  [AS_HELP_STRING([--enable-thread-context],\n    [Use thread context])], [no], [no])\n\nPHP_ARG_ENABLE([swoole-thread],\n  [whether to enable swoole thread support],\n  [AS_HELP_STRING([--enable-swoole-thread],\n    [Enable swoole thread support])], [no], [no])\n\nPHP_ARG_ENABLE([swoole-stdext],\n  [whether to enable swoole stdext support],\n  [AS_HELP_STRING([--enable-swoole-stdext],\n    [Enable swoole stdext support(\"[Experimental] This module is only used for swoole-cli.\n     If you are unsure which feature you need, keep it disabled\")])], [no], [no])\n\nPHP_ARG_ENABLE([swoole-coro-time],\n  [whether to enable coroutine execution time ],\n  [AS_HELP_STRING([--enable-swoole-coro-time],\n    [Calculating coroutine execution time])], [no], [no])\n\ndefine([PDO_ODBC_HELP_TEXT],[[\n  The include and lib dirs are looked for under 'dir'. The 'flavour' can be one\n  of: ibm-db2, iODBC, unixODBC, generic. If ',dir' part is omitted, default for\n  the flavour you have selected will be used. e.g.: --with-swoole-odbc=unixODBC\n  will check for unixODBC under /usr/local. You may attempt to use an otherwise\n  unsupported driver using the 'generic' flavour. The syntax for generic ODBC\n  support is: --with-swoole-odbc=generic,dir,libname,ldflags,cflags. When built as\n  'shared' the extension filename is always pdo_odbc.so]])\n\nPHP_ARG_WITH([swoole-odbc],\n  [\"for ODBC v3 support for PDO\"],\n  [AS_HELP_STRING([--with-swoole-odbc=flavour,dir],\n    [\"PDO: Support for 'flavour' ODBC driver.\"]PDO_ODBC_HELP_TEXT)], [no], [no])\n\nAC_DEFUN([PDO_ODBC_CHECK_HEADER],[\n  AC_MSG_CHECKING([for $1 in $PDO_ODBC_INCDIR])\n  if test -f \"$PDO_ODBC_INCDIR/$1\"; then\n    php_pdo_have_header=yes\n    AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_$1]), [1],\n      [Define to 1 if you have the <$1> header file.])\n    AC_MSG_RESULT(yes)\n  else\n    AC_MSG_RESULT(no)\n  fi\n])\n\nAC_DEFUN([SWOOLE_HAVE_PHP_EXT], [\n    extname=$1\n    haveext=$[PHP_]translit($1,a-z_-,A-Z__)\n\n    AC_MSG_CHECKING([for ext/$extname support])\n    if test -x \"$PHP_EXECUTABLE\"; then\n        grepext=`$PHP_EXECUTABLE -m | $EGREP ^$extname\\$`\n        if test \"$grepext\" = \"$extname\"; then\n            [PHP_HTTP_HAVE_EXT_]translit($1,a-z_-,A-Z__)=1\n            AC_MSG_RESULT([yes])\n            $2\n        else\n            [PHP_HTTP_HAVE_EXT_]translit($1,a-z_-,A-Z__)=\n            AC_MSG_RESULT([no])\n            $3\n        fi\n    elif test \"$haveext\" != \"no\" && test \"x$haveext\" != \"x\"; then\n        [PHP_HTTP_HAVE_EXT_]translit($1,a-z_-,A-Z__)=1\n        AC_MSG_RESULT([yes])\n        $2\n    else\n        [PHP_HTTP_HAVE_EXT_]translit($1,a-z_-,A-Z__)=\n        AC_MSG_RESULT([no])\n        $3\n    fi\n])\n\nAC_DEFUN([AC_SWOOLE_CPU_AFFINITY],\n[\n    AC_MSG_CHECKING([for cpu affinity])\n    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[\n        #ifdef __FreeBSD__\n        #include <sys/types.h>\n        #include <sys/cpuset.h>\n        typedef cpuset_t cpu_set_t;\n        #else\n        #define _GNU_SOURCE 1\n        #include <sched.h>\n        #endif\n    ]], [[\n        cpu_set_t cpu_set;\n        CPU_ZERO(&cpu_set);\n    ]])],[\n        AC_DEFINE([HAVE_CPU_AFFINITY], 1, [cpu affinity?])\n        AC_MSG_RESULT([yes])\n    ],[\n        AC_MSG_RESULT([no])\n    ])\n])\n\nAC_DEFUN([AC_SWOOLE_HAVE_REUSEPORT],\n[\n    AC_MSG_CHECKING([for socket REUSEPORT])\n    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[\n        #include <sys/socket.h>\n    ]], [[\n        int val = 1;\n        setsockopt(0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));\n    ]])],[\n        AC_DEFINE([HAVE_REUSEPORT], 1, [have SO_REUSEPORT?])\n        AC_MSG_RESULT([yes])\n    ],[\n        AC_MSG_RESULT([no])\n    ])\n])\n\nAC_DEFUN([AC_SWOOLE_HAVE_FUTEX],\n[\n    AC_MSG_CHECKING([for futex])\n    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[\n        #include <linux/futex.h>\n        #include <syscall.h>\n        #include <unistd.h>\n    ]], [[\n        int futex_addr;\n        syscall(SYS_futex, &futex_addr, FUTEX_WAIT, NULL, NULL, 0);\n    ]])],[\n        AC_DEFINE([HAVE_FUTEX], 1, [have FUTEX?])\n        AC_MSG_RESULT([yes])\n    ],[\n        AC_MSG_RESULT([no])\n    ])\n])\n\nAC_DEFUN([AC_SWOOLE_HAVE_UCONTEXT],\n[\n    AC_MSG_CHECKING([for ucontext])\n    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[\n        #define _XOPEN_SOURCE\n        #include <stdio.h>\n        #include <ucontext.h>\n        #include <unistd.h>\n    ]], [[\n        ucontext_t context;\n        getcontext(&context);\n    ]])],[\n        AC_DEFINE([HAVE_UCONTEXT], 1, [have ucontext?])\n        AC_MSG_RESULT([yes])\n    ],[\n        AC_MSG_RESULT([no])\n    ])\n])\n\nAC_DEFUN([AC_SWOOLE_HAVE_VALGRIND],\n[\n    AC_MSG_CHECKING([for valgrind])\n    AC_LANG_PUSH([C++])\n    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[\n        #include <valgrind/valgrind.h>\n    ]], [[\n\n    ]])],[\n        AC_DEFINE([HAVE_VALGRIND], 1, [have valgrind?])\n        AC_MSG_RESULT([yes])\n    ],[\n        AC_MSG_RESULT([no])\n    ])\n    AC_LANG_POP([C++])\n])\n\nAC_DEFUN([AC_SWOOLE_HAVE_BOOST_STACKTRACE],\n[\n    AC_MSG_CHECKING([for valgrind])\n    AC_LANG_PUSH([C++])\n    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[\n        #include <boost/stacktrace.hpp>\n    ]], [[\n\n    ]])],[\n        AC_DEFINE([HAVE_BOOST_STACKTRACE], 1, [have boost-stacktrace?])\n        AC_MSG_RESULT([yes])\n    ],[\n        AC_MSG_RESULT([no])\n    ])\n    AC_LANG_POP([C++])\n])\n\nAC_DEFUN([AC_SWOOLE_HAVE_IOURING_FUTEX],\n[\n    AC_MSG_CHECKING([for io_uring futex])\n    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[\n        #define _GNU_SOURCE\n        #include <liburing.h>\n    ]], [[\n        int op = IORING_OP_FUTEX_WAIT;\n    ]])],[\n        AC_DEFINE([HAVE_IOURING_FUTEX], 1, [have io_uring futex?])\n        AC_MSG_RESULT([yes])\n    ],[\n        AC_MSG_RESULT([no])\n    ])\n])\n\nAC_DEFUN([AC_SWOOLE_HAVE_IOURING_STATX],\n[\n    AC_MSG_CHECKING([for io_uring statx])\n    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[\n        #define _GNU_SOURCE\n        #include <sys/stat.h>\n        #include <string.h>\n        #include <liburing.h>\n    ]], [[\n        struct statx _statxbuf;\n        memset(&_statxbuf, 0, sizeof(_statxbuf));\n        int op = IORING_OP_STATX;\n    ]])],[\n        AC_DEFINE([HAVE_IOURING_STATX], 1, [have io_uring statx?])\n        AC_MSG_RESULT([yes])\n    ],[\n        AC_MSG_RESULT([no])\n    ])\n])\n\nAC_DEFUN([AC_SWOOLE_HAVE_IOURING_FTRUNCATE],\n[\n    AC_MSG_CHECKING([for io_uring ftruncate])\n    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[\n        #define _GNU_SOURCE\n        #include <liburing.h>\n    ]], [[\n        int op = IORING_OP_FTRUNCATE;\n    ]])],[\n        AC_DEFINE([HAVE_IOURING_FTRUNCATE], 1, [have io_uring ftruncate?])\n        AC_MSG_RESULT([yes])\n    ],[\n        AC_MSG_RESULT([no])\n    ])\n])\n\nAC_DEFUN([AC_SWOOLE_CHECK_SOCKETS], [\n    AC_CHECK_FUNCS([hstrerror socketpair if_nametoindex if_indextoname])\n    AC_CHECK_HEADERS([netdb.h netinet/tcp.h sys/un.h sys/sockio.h])\n    AC_DEFINE([HAVE_SOCKETS], 1, [ ])\n\n    dnl Check for AI_V4MAPPED flag\n    AC_CACHE_CHECK([if getaddrinfo supports AI_V4MAPPED],[ac_cv_gai_ai_v4mapped],\n    [\n        AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[\n#include <netdb.h>\n    ]], [[int flag = AI_V4MAPPED;]])],\n        [ac_cv_gai_ai_v4mapped=yes], [ac_cv_gai_ai_v4mapped=no])\n    ])\n\n    if test \"$ac_cv_gai_ai_v4mapped\" = yes; then\n        AC_DEFINE(HAVE_AI_V4MAPPED,1,[Whether you have AI_V4MAPPED])\n    fi\n\n    dnl Check for AI_ALL flag\n    AC_CACHE_CHECK([if getaddrinfo supports AI_ALL],[ac_cv_gai_ai_all],\n    [\n        AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[\n#include <netdb.h>\n    ]], [[int flag = AI_ALL;]])],\n        [ac_cv_gai_ai_all=yes], [ac_cv_gai_ai_all=no])\n    ])\n\n    if test \"$ac_cv_gai_ai_all\" = yes; then\n        AC_DEFINE(HAVE_AI_ALL,1,[Whether you have AI_ALL])\n    fi\n\n    dnl Check for AI_IDN flag\n    AC_CACHE_CHECK([if getaddrinfo supports AI_IDN],[ac_cv_gai_ai_idn],\n    [\n        AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[\n#include <netdb.h>\n    ]], [[int flag = AI_IDN;]])],\n            [ac_cv_gai_ai_idn=yes], [ac_cv_gai_ai_idn=no])\n    ])\n\n    if test \"$ac_cv_gai_ai_idn\" = yes; then\n        AC_DEFINE(HAVE_AI_IDN,1,[Whether you have AI_IDN])\n    fi\n])\n\nAC_DEFUN([AC_SWOOLE_HAVE_MSGQUEUE],\n[\n    AC_MSG_CHECKING([for sysv message queue])\n    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[\n        #include <sys/ipc.h>\n        #include <sys/msg.h>\n    ]], [[\n        msgget(0x9501, IPC_CREAT);\n    ]])],[\n        AC_DEFINE([HAVE_MSGQUEUE], 1, [have sysv message queue?])\n        AC_MSG_RESULT([yes])\n    ],[\n        AC_MSG_RESULT([no])\n    ])\n])\n\nAC_MSG_CHECKING([if compiling with clang])\nAC_COMPILE_IFELSE([\n    AC_LANG_PROGRAM([], [[\n        #ifndef __clang__\n            not clang\n        #endif\n    ]])],\n    [CLANG=yes], [CLANG=no]\n)\nAC_MSG_RESULT([$CLANG])\n\ndnl AC_PROG_CC_C99 is obsolete with autoconf >= 2.70 yet necessary for <= 2.69.\nm4_version_prereq([2.70],,[AC_PROG_CC_C99])\n\nAC_CANONICAL_HOST\n\nif test \"$PHP_SWOOLE\" != \"no\"; then\n\n    AC_CHECK_LIB(c, accept4, AC_DEFINE(HAVE_ACCEPT4, 1, [have accept4]))\n    AC_CHECK_LIB(c, signalfd, AC_DEFINE(HAVE_SIGNALFD, 1, [have signalfd]))\n    AC_CHECK_LIB(c, eventfd, AC_DEFINE(HAVE_EVENTFD, 1, [have eventfd]))\n    AC_CHECK_LIB(c, epoll_create, AC_DEFINE(HAVE_EPOLL, 1, [have epoll]))\n    AC_CHECK_LIB(c, sendfile, AC_DEFINE(HAVE_SENDFILE, 1, [have sendfile]))\n    AC_CHECK_LIB(c, kqueue, AC_DEFINE(HAVE_KQUEUE, 1, [have kqueue]))\n    AC_CHECK_LIB(c, backtrace, AC_DEFINE(HAVE_EXECINFO, 1, [have execinfo]))\n    AC_CHECK_LIB(c, daemon, AC_DEFINE(HAVE_DAEMON, 1, [have daemon]))\n    AC_CHECK_LIB(c, mkostemp, AC_DEFINE(HAVE_MKOSTEMP, 1, [have mkostemp]))\n    AC_CHECK_LIB(c, inotify_init, AC_DEFINE(HAVE_INOTIFY, 1, [have inotify]))\n    AC_CHECK_LIB(c, malloc_trim, AC_DEFINE(HAVE_MALLOC_TRIM, 1, [have malloc_trim]))\n    AC_CHECK_LIB(c, inotify_init1, AC_DEFINE(HAVE_INOTIFY_INIT1, 1, [have inotify_init1]))\n    AC_CHECK_LIB(c, gethostbyname2_r, AC_DEFINE(HAVE_GETHOSTBYNAME2_R, 1, [have gethostbyname2_r]))\n    AC_CHECK_LIB(c, ptrace, AC_DEFINE(HAVE_PTRACE, 1, [have ptrace]))\n    AC_CHECK_LIB(c, getrandom, AC_DEFINE(HAVE_GETRANDOM, 1, [have getrandom]))\n    AC_CHECK_LIB(c, arc4random, AC_DEFINE(HAVE_ARC4RANDOM, 1, [have arc4random]))\n    AC_CHECK_LIB(c, CCRandomGenerateBytes, AC_DEFINE(HAVE_CCRANDOMGENERATEBYTES, 1, [have_ccrandomgeneratebytes]))\n    AC_CHECK_LIB(pthread, pthread_rwlock_init, AC_DEFINE(HAVE_RWLOCK, 1, [have pthread_rwlock_init]))\n    AC_CHECK_LIB(pthread, pthread_spin_lock, AC_DEFINE(HAVE_SPINLOCK, 1, [have pthread_spin_lock]))\n    AC_CHECK_LIB(pthread, pthread_mutex_timedlock, AC_DEFINE(HAVE_MUTEX_TIMEDLOCK, 1, [have pthread_mutex_timedlock]))\n    AC_CHECK_LIB(pthread, pthread_rwlock_timedrdlock, AC_DEFINE(HAVE_RWLOCK_TIMEDRDLOCK, 1, [have pthread_rwlock_timedrdlock]))\n    AC_CHECK_LIB(pthread, pthread_rwlock_timedwrlock, AC_DEFINE(HAVE_RWLOCK_TIMEDWRLOCK, 1, [have pthread_rwlock_timedwrlock]))\n    AC_CHECK_LIB(pthread, pthread_barrier_init, AC_DEFINE(HAVE_PTHREAD_BARRIER, 1, [have pthread_barrier_init]))\n    AC_CHECK_LIB(pthread, pthread_mutexattr_setpshared, AC_DEFINE(HAVE_PTHREAD_MUTEXATTR_SETPSHARED, 1, [have pthread_mutexattr_setpshared]))\n    AC_CHECK_LIB(pthread, pthread_mutexattr_setrobust, AC_DEFINE(HAVE_PTHREAD_MUTEXATTR_SETROBUST, 1, [have pthread_mutexattr_setrobust]))\n    AC_CHECK_LIB(pthread, pthread_mutex_consistent, AC_DEFINE(HAVE_PTHREAD_MUTEX_CONSISTENT, 1, [have pthread_mutex_consistent]))\n\n    if test \"$PHP_SWOOLE_DEV\" = \"yes\"; then\n        AX_CHECK_COMPILE_FLAG(-Wbool-conversion,                _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wbool-conversion\")\n        AX_CHECK_COMPILE_FLAG(-Wignored-qualifiers,             _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wignored-qualifiers\")\n        AX_CHECK_COMPILE_FLAG(-Wduplicate-enum,                 _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wduplicate-enum\")\n        AX_CHECK_COMPILE_FLAG(-Wempty-body,                     _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wempty-body\")\n        AX_CHECK_COMPILE_FLAG(-Wenum-compare,                   _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wenum-compare\")\n        AX_CHECK_COMPILE_FLAG(-Wextra,                          _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wextra\")\n        AX_CHECK_COMPILE_FLAG(-Wformat-security,                _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wformat-security\")\n        AX_CHECK_COMPILE_FLAG(-Wheader-guard,                   _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wheader-guard\")\n        AX_CHECK_COMPILE_FLAG(-Wincompatible-pointer-types-discards-qualifiers, _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wincompatible-pointer-types-discards-qualifiers\")\n        AX_CHECK_COMPILE_FLAG(-Winit-self,                      _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Winit-self\")\n        AX_CHECK_COMPILE_FLAG(-Wlogical-not-parentheses,        _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wlogical-not-parentheses\")\n        AX_CHECK_COMPILE_FLAG(-Wlogical-op-parentheses,         _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wlogical-op-parentheses\")\n        AX_CHECK_COMPILE_FLAG(-Wloop-analysis,                  _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wloop-analysis\")\n        AX_CHECK_COMPILE_FLAG(-Wuninitialized,                  _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wuninitialized\")\n        AX_CHECK_COMPILE_FLAG(-Wno-date-time,                   _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wno-date-time\")\n        AX_CHECK_COMPILE_FLAG(-Wno-missing-field-initializers,  _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wno-missing-field-initializers\")\n        AX_CHECK_COMPILE_FLAG(-Wno-sign-compare,                _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wno-sign-compare\")\n        AX_CHECK_COMPILE_FLAG(-Wno-unused-const-variable,       _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wno-unused-const-variable\")\n        AX_CHECK_COMPILE_FLAG(-Wno-unused-parameter,            _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wno-unused-parameter\")\n        AX_CHECK_COMPILE_FLAG(-Wno-variadic-macros,             _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wno-variadic-macros\")\n        AX_CHECK_COMPILE_FLAG(-Wparentheses,                    _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wparentheses\")\n        AX_CHECK_COMPILE_FLAG(-Wpointer-bool-conversion,        _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wpointer-bool-conversion\")\n        AX_CHECK_COMPILE_FLAG(-Wsizeof-array-argument,          _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wsizeof-array-argument\")\n        AX_CHECK_COMPILE_FLAG(-Wwrite-strings,                  _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -Wwrite-strings\")\n        AX_CHECK_COMPILE_FLAG(-fdiagnostics-show-option,        _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -fdiagnostics-show-option\")\n        AX_CHECK_COMPILE_FLAG(-fno-omit-frame-pointer,          _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -fno-omit-frame-pointer\")\n        AX_CHECK_COMPILE_FLAG(-fno-optimize-sibling-calls,      _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -fno-optimize-sibling-calls\")\n        AX_CHECK_COMPILE_FLAG(-fsanitize-address,               _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -fsanitize-address\")\n        AX_CHECK_COMPILE_FLAG(-fstack-protector,                _MAINTAINER_CFLAGS=\"$_MAINTAINER_CFLAGS -fstack-protector\")\n\n        EXTRA_CFLAGS=\"$_MAINTAINER_CFLAGS\"\n        CFLAGS=\"-g -O0 -Wall $CFLAGS\"\n        CXXFLAGS=\"-g -O0 -Wall $CXXFLAGS\"\n    fi\n\n    if test \"$PHP_SWOOLE_CURL\" = \"yes\"; then\n        PKG_CHECK_MODULES([CURL], [libcurl >= 7.56.0])\n        PHP_EVAL_LIBLINE($CURL_LIBS, SWOOLE_SHARED_LIBADD)\n        PHP_EVAL_INCLINE($CURL_CFLAGS)\n        AC_DEFINE(SW_USE_CURL, 1, [do we enable cURL native client])\n    fi\n\n    if test \"$PHP_SWOOLE_CORO_TIME\" = \"yes\"; then\n        AC_DEFINE(SW_CORO_TIME, 1, [do we enable to calculate coroutine execution time])\n    fi\n\n    dnl pgsql begin\n\n    if test \"$PHP_SWOOLE_PGSQL\" != \"no\"; then\n        dnl TODO macros below can be reused to find curl things\n        dnl prepare pkg-config\n\n        if test -z \"$PKG_CONFIG\"; then\n            AC_PATH_PROG(PKG_CONFIG, pkg-config, no)\n        fi\n        AC_MSG_CHECKING(for libpq)\n        if test \"x${LIBPQ_LIBS+set}\" = \"xset\" || test \"x${LIBPQ_CFLAGS+set}\" = \"xset\"; then\n            AC_MSG_RESULT([using LIBPQ_CFLAGS and LIBPQ_LIBS])\n        elif test -x \"$PKG_CONFIG\" ; then\n            dnl find pkg using pkg-config cli tool\n            libpq_pkg_config_path=\"$PHP_SWOOLE_PGSQL/lib/pkgconfig\"\n            if test \"xyes\" = \"x$PHP_SWOOLE_PGSQL\" ; then\n                libpq_pkg_config_path=/lib/pkgconfig\n            fi\n            if test \"x\" != \"x$PKG_CONFIG_PATH\"; then\n                libpq_pkg_config_path=\"$libpq_pkg_config_path:$PKG_CONFIG_PATH\"\n            fi\n\n            libpq_version_full=`env PKG_CONFIG_PATH=${libpq_pkg_config_path} $PKG_CONFIG --modversion libpq`\n            AC_MSG_RESULT(${libpq_version_full})\n            LIBPQ_CFLAGS=\"`env PKG_CONFIG_PATH=${libpq_pkg_config_path} $PKG_CONFIG --cflags libpq`\"\n            LIBPQ_LIBS=\"`env PKG_CONFIG_PATH=${libpq_pkg_config_path} $PKG_CONFIG --libs libpq`\"\n        fi\n\n        _libpq_saved_cflags=\"$CFLAGS\"\n        CFLAGS=\"$CFLAGS $LIBPQ_CFLAGS\"\n        AC_CHECK_HEADER(libpq-fe.h, [], [\n            dnl this is too long, wht so chaos?\n            cat >&2 <<EOF\nlibpq headers was not found.\nset LIBPQ_CFLAGS and LIBPQ_LIBS environment or\ninstall following package to obtain them:\n    libpq-dev (for debian and its varients)\n    postgresql-devel (for rhel varients)\n    libpq-devel (for newer fedora)\n    postgresql-libs (for arch and its varients)\n    postgresql-dev (for alpine)\n    postgresql (for homebrew)\nEOF\n            AC_MSG_ERROR([postgresql support needs libpq headers to build])\n        ])\n        CFLAGS=\"$_libpq_saved_cflags\"\n\n        _libpq_saved_libs=$LIBS\n        LIBS=\"$LIBS $LIBPQ_LIBS\"\n        AC_CHECK_LIB(pq, PQlibVersion, [ ], [\n            cat >&2 <<EOF\nlibpq libraries was not found.\nset LIBPQ_CFLAGS and LIBPQ_LIBS environment or\ninstall following package to obtain them:\n    libpq-dev (for debian and its varients)\n    postgresql-devel (for rhel varients)\n    libpq-devel (for newer fedora)\n    postgresql-libs (for arch and its varients)\n    postgresql-dev (for alpine)\n    postgresql (for homebrew)\nEOF\n            AC_MSG_ERROR([postgresql support needs libpq libraries to build])\n        ])\n        LIBS=\"$_libpq_saved_libs\"\n\n        dnl FIXME: this should be SWOOLE_CFLAGS=\"$SWOOLE_CFLAGS $LIBPQ_CFLAGS\"\n        dnl or SWOOLE_PGSQL_CFLAGS=\"$SWOOLE_CFLAGS $LIBPQ_CFLAGS\" and SWOOLE_PGSQL_CFLAGS only applies to ext-src/swoole_postgresql_coro.cc\n        EXTRA_CFLAGS=\"$EXTRA_CFLAGS $LIBPQ_CFLAGS\"\n        PHP_EVAL_LIBLINE($LIBPQ_LIBS, SWOOLE_SHARED_LIBADD)\n\n        AC_DEFINE(SW_USE_PGSQL, 1, [do we enable postgresql coro support])\n    fi\n\n    dnl pgsql end\n\n    dnl odbc begin\n\n    if test \"$PHP_SWOOLE_ODBC\" != \"no\"; then\n      PHP_CHECK_PDO_INCLUDES\n\n      AC_MSG_CHECKING([for selected PDO ODBC flavour])\n\n      pdo_odbc_flavour=\"`echo $PHP_SWOOLE_ODBC | cut -d, -f1`\"\n      pdo_odbc_dir=\"`echo $PHP_SWOOLE_ODBC | cut -d, -f2`\"\n\n      if test \"$pdo_odbc_dir\" = \"$PHP_SWOOLE_ODBC\" ; then\n        pdo_odbc_dir=\n      fi\n\n      case $pdo_odbc_flavour in\n        ibm-db2)\n            pdo_odbc_def_libdir=/home/db2inst1/sqllib/lib\n            pdo_odbc_def_incdir=/home/db2inst1/sqllib/include\n            pdo_odbc_def_lib=db2\n            ;;\n\n        iODBC|iodbc)\n            pdo_odbc_def_libdir=/usr/local/$PHP_LIBDIR\n            pdo_odbc_def_incdir=/usr/local/include\n            pdo_odbc_def_lib=iodbc\n            ;;\n\n        unixODBC|unixodbc)\n            pdo_odbc_def_libdir=/usr/local/$PHP_LIBDIR\n            pdo_odbc_def_incdir=/usr/local/include\n            pdo_odbc_def_lib=odbc\n            ;;\n\n        ODBCRouter|odbcrouter)\n            pdo_odbc_def_libdir=/usr/$PHP_LIBDIR\n            pdo_odbc_def_incdir=/usr/include\n            pdo_odbc_def_lib=odbcsdk\n            ;;\n\n        generic)\n            pdo_odbc_def_lib=\"`echo $PHP_SWOOLE_ODBC | cut -d, -f3`\"\n            pdo_odbc_def_ldflags=\"`echo $PHP_SWOOLE_ODBC | cut -d, -f4`\"\n            pdo_odbc_def_cflags=\"`echo $PHP_SWOOLE_ODBC | cut -d, -f5`\"\n            pdo_odbc_flavour=\"generic-$pdo_odbc_def_lib\"\n            ;;\n\n          *)\n            AC_MSG_ERROR([Unknown ODBC flavour $pdo_odbc_flavour]PDO_ODBC_HELP_TEXT)\n            ;;\n      esac\n\n      if test -n \"$pdo_odbc_dir\"; then\n        PDO_ODBC_INCDIR=\"$pdo_odbc_dir/include\"\n        PDO_ODBC_LIBDIR=\"$pdo_odbc_dir/$PHP_LIBDIR\"\n      else\n        PDO_ODBC_INCDIR=\"$pdo_odbc_def_incdir\"\n        PDO_ODBC_LIBDIR=\"$pdo_odbc_def_libdir\"\n      fi\n\n      AC_MSG_RESULT([$pdo_odbc_flavour\n              libs       $PDO_ODBC_LIBDIR,\n              headers    $PDO_ODBC_INCDIR])\n\n      if test ! -d \"$PDO_ODBC_LIBDIR\" ; then\n        AC_MSG_WARN([library dir $PDO_ODBC_LIBDIR does not exist])\n      fi\n\n      PDO_ODBC_CHECK_HEADER(odbc.h)\n      PDO_ODBC_CHECK_HEADER(odbcsdk.h)\n      PDO_ODBC_CHECK_HEADER(iodbc.h)\n      PDO_ODBC_CHECK_HEADER(sqlunix.h)\n      PDO_ODBC_CHECK_HEADER(sqltypes.h)\n      PDO_ODBC_CHECK_HEADER(sqlucode.h)\n      PDO_ODBC_CHECK_HEADER(sql.h)\n      PDO_ODBC_CHECK_HEADER(isql.h)\n      PDO_ODBC_CHECK_HEADER(sqlext.h)\n      PDO_ODBC_CHECK_HEADER(isqlext.h)\n      PDO_ODBC_CHECK_HEADER(udbcext.h)\n      PDO_ODBC_CHECK_HEADER(sqlcli1.h)\n      PDO_ODBC_CHECK_HEADER(LibraryManager.h)\n      PDO_ODBC_CHECK_HEADER(cli0core.h)\n      PDO_ODBC_CHECK_HEADER(cli0ext.h)\n      PDO_ODBC_CHECK_HEADER(cli0cli.h)\n      PDO_ODBC_CHECK_HEADER(cli0defs.h)\n      PDO_ODBC_CHECK_HEADER(cli0env.h)\n\n      if test \"$php_pdo_have_header\" != \"yes\"; then\n        AC_MSG_ERROR([Cannot find header file(s) for pdo_odbc])\n      fi\n\n      if test -n \"$SWOOLE_ODBC_LIBS\"; then\n        ODBC_LIBS=\"$SWOOLE_ODBC_LIBS\"\n      else\n        ODBC_LIBS=\"-l$pdo_odbc_def_lib\"\n      fi\n\n      PDO_ODBC_INCLUDE=\"$pdo_odbc_def_cflags -I$PDO_ODBC_INCDIR -DPDO_ODBC_TYPE=\\\\\\\"$pdo_odbc_flavour\\\\\\\"\"\n      PDO_ODBC_LDFLAGS=\"$pdo_odbc_def_ldflags -L$PDO_ODBC_LIBDIR $ODBC_LIBS\"\n\n      PHP_EVAL_LIBLINE([$PDO_ODBC_LDFLAGS], [SWOOLE_SHARED_LIBADD])\n\n      EXTRA_CFLAGS=\"$EXTRA_CFLAGS -I$pdo_cv_inc_path $PDO_ODBC_INCLUDE\"\n\n      dnl Check first for an ODBC 1.0 function to assert that the libraries work\n      SAVE_LIBS=\"$LIBS\"\n      LIBS=\"$LIBS $PDO_ODBC_LDFLAGS\"\n\n      AC_LINK_IFELSE(\n        [AC_LANG_PROGRAM(\n           [[#include <sql.h>\n             #include <sqlext.h>]],\n           [[\n             SQLLEN ind = 0;\n             char buf[1];\n             SQLBindCol((SQLHSTMT)0, (SQLUSMALLINT)1, (SQLSMALLINT)SQL_C_CHAR,\n                        (SQLPOINTER)buf, (SQLLEN)sizeof(buf), &ind);\n             return 0;\n           ]])],\n        [\n          dnl And now check for an ODBC 3.0 function to assert that they are *good*\n          dnl libraries.\n          AC_LINK_IFELSE(\n            [AC_LANG_PROGRAM(\n               [[#include <sql.h>\n                 #include <sqlext.h>]],\n               [[\n                 SQLHANDLE out = SQL_NULL_HANDLE;\n                 SQLAllocHandle((SQLSMALLINT)SQL_HANDLE_ENV,\n                                (SQLHANDLE)SQL_NULL_HANDLE, &out);\n                 return 0;\n               ]])],\n            [],\n            [AC_MSG_ERROR([\n    Your ODBC library does not appear to be ODBC 3 compatible.\n    You should consider using iODBC or unixODBC instead, and loading your\n    libraries as a driver in that environment; it will emulate the\n    functions required for PDO support.\n    ])]\n          )\n        ],\n        [AC_MSG_ERROR([Your ODBC library does not exist or there was an error. Check config.log for more information])]\n    )\n\n      LIBS=\"$SAVE_LIBS\"\n\n      AC_DEFINE(SW_USE_ODBC, 1, [do we enable swoole-odbc coro support])\n    fi\n\n    dnl odbc end\n\n    dnl SWOOLE_ORACLE start\n\n    if test -z \"$SED\"; then\n        SWOOLE_PDO_OCI_SED=\"sed\";\n    else\n        SWOOLE_PDO_OCI_SED=\"$SED\";\n    fi\n\n    SWOOLE_PDO_OCI_TAIL1=`echo a | tail -n1 2>/dev/null`\n    if test \"$SWOOLE_PDO_OCI_TAIL1\" = \"a\"; then\n        SWOOLE_PDO_OCI_TAIL1=\"tail -n1\"\n    else\n        SWOOLE_PDO_OCI_TAIL1=\"tail -1\"\n    fi\n\n    AC_DEFUN([AC_PDO_OCI_VERSION],[\n        AC_MSG_CHECKING([Oracle version])\n        PDO_OCI_LCS_BASE=$PDO_OCI_LIB_DIR/libclntsh.$SHLIB_SUFFIX_NAME\n        dnl Oracle 10g, 11g, 12c etc\n        PDO_OCI_LCS=`ls $PDO_OCI_LCS_BASE.*.1 2> /dev/null | $SWOOLE_PDO_OCI_TAIL1`\n        if test -f \"$PDO_OCI_LCS\"; then\n            dnl Oracle 10g, 11g 12c etc. The x.2 version libraries are named x.1 for\n            dnl drop in compatibility\n            PDO_OCI_VERSION=`echo $PDO_OCI_LCS | $SWOOLE_PDO_OCI_SED -e 's/.*\\.\\(.*\\)\\.1$/\\1.1/'`\n        elif test -f $PDO_OCI_LCS_BASE.9.0; then\n            dnl There is no case for Oracle 9.2. Oracle 9.2 libraries have a 9.0 suffix\n            dnl for drop-in compatibility with Oracle 9.0\n            PDO_OCI_VERSION=9.0\n        else\n            AC_MSG_ERROR(Oracle libclntsh.$SHLIB_SUFFIX_NAME client library not found or its version is lower than 9)\n        fi\n        AC_MSG_RESULT($PDO_OCI_VERSION)\n    ])\n\n    AC_DEFUN([AC_PDO_OCI_CHECK_LIB_DIR],[\n        AC_CHECK_SIZEOF([long])\n        AC_MSG_CHECKING([if we're at 64-bit platform])\n        AS_IF([test \"$ac_cv_sizeof_long\" -eq 4],[\n            AC_MSG_RESULT([no])\n            TMP_PDO_OCI_LIB_DIR=\"$PDO_OCI_DIR/lib32\"\n        ],[\n            AC_MSG_RESULT([yes])\n            TMP_PDO_OCI_LIB_DIR=\"$PDO_OCI_DIR/lib\"\n        ])\n\n        AC_MSG_CHECKING([OCI8 libraries dir])\n        if test -d \"$PDO_OCI_DIR/lib\" && test ! -d \"$PDO_OCI_DIR/lib32\"; then\n            PDO_OCI_LIB_DIR=\"$PDO_OCI_DIR/lib\"\n        elif test ! -d \"$PDO_OCI_DIR/lib\" && test -d \"$PDO_OCI_DIR/lib32\"; then\n            PDO_OCI_LIB_DIR=\"$PDO_OCI_DIR/lib32\"\n        elif test -d \"$PDO_OCI_DIR/lib\" && test -d \"$PDO_OCI_DIR/lib32\"; then\n            PDO_OCI_LIB_DIR=$TMP_PDO_OCI_LIB_DIR\n        else\n            AC_MSG_ERROR([Oracle required OCI8 libraries not found])\n        fi\n        AC_MSG_RESULT($PDO_OCI_LIB_DIR)\n    ])\n\n    PHP_ARG_WITH([swoole-oracle],\n        [whether to enable oracle build flags],\n        [AS_HELP_STRING([[--with-swoole-oracle[=DIR]]],\n            [\"PDO: Oracle OCI support. DIR defaults to ${ORACLE_HOME}. Use\n            --with-swoole-oracle=instantclient,/path/to/instant/client/lib for an Oracle\n            Instant Client installation.\"])], [no], [no])\n\n    if test \"$PHP_SWOOLE_ORACLE\" != \"no\"; then\n        if test \"$PHP_PDO\" = \"no\" && test \"$ext_shared\" = \"no\"; then\n            AC_MSG_ERROR([PDO is not enabled! Add --enable-pdo to your configure line.])\n        fi\n\n        AC_MSG_CHECKING([Oracle Install-Dir])\n        if test \"$PHP_SWOOLE_ORACLE\" = \"yes\" || test -z \"$PHP_SWOOLE_ORACLE\"; then\n            PDO_OCI_DIR=$ORACLE_HOME\n        else\n            PDO_OCI_DIR=$PHP_SWOOLE_ORACLE\n        fi\n        AC_MSG_RESULT($PHP_SWOOLE_ORACLE)\n\n        AC_MSG_CHECKING([if that is sane])\n        if test -z \"$PDO_OCI_DIR\"; then\n            AC_MSG_ERROR([You need to tell me where to find your Oracle Instant Client SDK, or set ORACLE_HOME.])\n        else\n            AC_MSG_RESULT([yes])\n        fi\n\n        if test \"instantclient\" = \"`echo $PDO_OCI_DIR | cut -d, -f1`\" ; then\n            AC_CHECK_SIZEOF([long])\n            AC_MSG_CHECKING([if we're at 64-bit platform])\n            AS_IF([test \"$ac_cv_sizeof_long\" -eq 4],[\n                AC_MSG_RESULT([no])\n                PDO_OCI_CLIENT_DIR=\"client\"\n            ],[\n                AC_MSG_RESULT([yes])\n                PDO_OCI_CLIENT_DIR=\"client64\"\n            ])\n\n            PDO_OCI_LIB_DIR=\"`echo $PDO_OCI_DIR | cut -d, -f2`\"\n            AC_PDO_OCI_VERSION($PDO_OCI_LIB_DIR)\n\n            AC_MSG_CHECKING([for oci.h])\n            dnl Header directory for Instant Client SDK RPM install\n            OCISDKRPMINC=`echo \"$PDO_OCI_LIB_DIR\" | $SWOOLE_PDO_OCI_SED -e 's!^\\(.*\\)/lib/oracle/\\(.*\\)/\\('${PDO_OCI_CLIENT_DIR}'\\)/lib[/]*$!\\1/include/oracle/\\2/\\3!'`\n\n            dnl Header directory for manual installation\n            OCISDKMANINC=`echo \"$PDO_OCI_LIB_DIR\" | $SWOOLE_PDO_OCI_SED -e 's!^\\(.*\\)/lib[/]*$!\\1/include!'`\n\n            dnl Header directory for Instant Client SDK zip file install\n            OCISDKZIPINC=$PDO_OCI_LIB_DIR/sdk/include\n\n            if test -f \"$OCISDKRPMINC/oci.h\" ; then\n                PHP_ADD_INCLUDE($OCISDKRPMINC)\n                AC_MSG_RESULT($OCISDKRPMINC)\n            elif test -f \"$OCISDKMANINC/oci.h\" ; then\n                PHP_ADD_INCLUDE($OCISDKMANINC)\n                AC_MSG_RESULT($OCISDKMANINC)\n            elif test -f \"$OCISDKZIPINC/oci.h\" ; then\n                PHP_ADD_INCLUDE($OCISDKZIPINC)\n                AC_MSG_RESULT($OCISDKZIPINC)\n            else\n                AC_MSG_ERROR([I'm too dumb to figure out where the include dir is in your Instant Client install])\n            fi\n        else\n            AC_PDO_OCI_CHECK_LIB_DIR($PDO_OCI_DIR)\n\n            if test -d \"$PDO_OCI_DIR/rdbms/public\"; then\n                PHP_ADD_INCLUDE($PDO_OCI_DIR/rdbms/public)\n                PDO_OCI_INCLUDES=\"$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/rdbms/public\"\n            fi\n            if test -d \"$PDO_OCI_DIR/rdbms/demo\"; then\n                PHP_ADD_INCLUDE($PDO_OCI_DIR/rdbms/demo)\n                PDO_OCI_INCLUDES=\"$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/rdbms/demo\"\n            fi\n            if test -d \"$PDO_OCI_DIR/network/public\"; then\n                PHP_ADD_INCLUDE($PDO_OCI_DIR/network/public)\n                PDO_OCI_INCLUDES=\"$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/network/public\"\n            fi\n            if test -d \"$PDO_OCI_DIR/plsql/public\"; then\n                PHP_ADD_INCLUDE($PDO_OCI_DIR/plsql/public)\n                PDO_OCI_INCLUDES=\"$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/plsql/public\"\n            fi\n            if test -d \"$PDO_OCI_DIR/include\"; then\n                PHP_ADD_INCLUDE($PDO_OCI_DIR/include)\n                PDO_OCI_INCLUDES=\"$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/include\"\n            fi\n\n            if test -f \"$PDO_OCI_LIB_DIR/sysliblist\"; then\n                PHP_EVAL_LIBLINE(`cat $PDO_OCI_LIB_DIR/sysliblist`, SWOOLE_SHARED_LIBADD)\n            elif test -f \"$PDO_OCI_DIR/rdbms/lib/sysliblist\"; then\n                PHP_EVAL_LIBLINE(`cat $PDO_OCI_DIR/rdbms/lib/sysliblist`, SWOOLE_SHARED_LIBADD)\n            fi\n            AC_PDO_OCI_VERSION($PDO_OCI_LIB_DIR)\n        fi\n\n        case $PDO_OCI_VERSION in\n            7.3|8.0|8.1)\n                AC_MSG_ERROR([Oracle client libraries < 9 are not supported])\n                ;;\n        esac\n\n        PHP_ADD_LIBRARY(clntsh, 1, SWOOLE_SHARED_LIBADD)\n        PHP_ADD_LIBPATH($PDO_OCI_LIB_DIR, SWOOLE_SHARED_LIBADD)\n\n        PHP_CHECK_LIBRARY(clntsh, OCIEnvCreate,\n        [\n            AC_DEFINE(HAVE_OCIENVCREATE,1,[ ])\n        ], [], [\n            -L$PDO_OCI_LIB_DIR $SWOOLE_SHARED_LIBADD\n        ])\n\n        PHP_CHECK_LIBRARY(clntsh, OCIEnvNlsCreate,\n        [\n            AC_DEFINE(HAVE_OCIENVNLSCREATE,1,[ ])\n        ], [], [\n            -L$PDO_OCI_LIB_DIR $SWOOLE_SHARED_LIBADD\n        ])\n\n        dnl Scrollable cursors?\n        PHP_CHECK_LIBRARY(clntsh, OCIStmtFetch2,\n        [\n            AC_DEFINE(HAVE_OCISTMTFETCH2,1,[ ])\n        ], [], [\n            -L$PDO_OCI_LIB_DIR $SWOOLE_SHARED_LIBADD\n        ])\n\n        dnl Can handle bytes vs. characters?\n        PHP_CHECK_LIBRARY(clntsh, OCILobRead2,\n        [\n           AC_DEFINE(HAVE_OCILOBREAD2,1,[ ])\n        ], [], [\n           -L$PDO_OCI_LIB_DIR $SWOOLE_SHARED_LIBADD\n        ])\n\n        EXTRA_CFLAGS=\"$EXTRA_CFLAGS -I$pdo_cv_inc_path $PDO_OCI_INCLUDE\"\n        PHP_CHECK_PDO_INCLUDES\n        AC_DEFINE_UNQUOTED(SWOOLE_PDO_OCI_CLIENT_VERSION, \"$PDO_OCI_VERSION\", [ ])\n        AC_DEFINE(SW_USE_ORACLE, 1, [do we enable oracle coro support])\n    fi\n    dnl SWOOLE_ORACLE stop\n\n    dnl sqlite start\n    PHP_ARG_ENABLE([swoole-sqlite],\n        [\"for sqlite 3 support for PDO\"],\n        [AS_HELP_STRING([--enable-swoole-sqlite],\n            [PDO: sqlite 3 support.])], [no], [no])\n\n    if test \"$PHP_SWOOLE_SQLITE\" != \"no\"; then\n\n        if test \"$PHP_PDO\" = \"no\" && test \"$ext_shared\" = \"no\"; then\n            AC_MSG_ERROR([PDO is not enabled! Add --enable-pdo to your configure line.])\n        fi\n\n        PHP_CHECK_PDO_INCLUDES\n\n        PKG_CHECK_MODULES([SQLITE], [sqlite3 >= 3.7.7])\n\n        PHP_EVAL_INCLINE($SQLITE_CFLAGS)\n        PHP_EVAL_LIBLINE($SQLITE_LIBS, SWOOLE_SHARED_LIBADD)\n        AC_DEFINE(HAVE_SW_PDO_SQLITELIB, 1, [Define to 1 if you have the pdo_sqlite extension enabled.])\n\n        PHP_CHECK_LIBRARY(sqlite3, sqlite3_close_v2, [\n            AC_DEFINE(HAVE_SW_SQLITE3_CLOSE_V2, 1, [have sqlite3_close_v2])\n        ], [], [$SWOOLE_SHARED_LIBADD])\n\n        PHP_CHECK_LIBRARY(sqlite3, sqlite3_column_table_name, [\n            AC_DEFINE(HAVE_SW_SQLITE3_COLUMN_TABLE_NAME, 1, [have sqlite3_column_table_name])\n        ], [], [$SWOOLE_SHARED_LIBADD])\n\n        AC_DEFINE(SW_USE_SQLITE, 1, [do we enable sqlite coro support])\n    fi\n    dnl sqlite stop\n\n    dnl firebird start\n\n    if test \"$PHP_SWOOLE_FIREBIRD\" != \"no\"; then\n        if test \"$PHP_PDO\" = \"no\" && test \"$ext_shared\" = \"no\"; then\n            AC_MSG_ERROR([PDO is not enabled! Add --enable-pdo to your configure line.])\n        fi\n\n      AC_PATH_PROG([FB_CONFIG], [fb_config], [no])\n\n      if test -x \"$FB_CONFIG\" && test \"$PHP_PDO_FIREBIRD\" = \"yes\"; then\n        AC_MSG_CHECKING([for libfbconfig])\n        FB_CFLAGS=$($FB_CONFIG --cflags)\n        FB_LIBDIR=$($FB_CONFIG --libs)\n        FB_VERSION=$($FB_CONFIG --version)\n        AC_MSG_RESULT([version $FB_VERSION])\n        AS_VERSION_COMPARE([$FB_VERSION], [3.0],\n          [AC_MSG_ERROR([Firebird required version is at least 3.0])])\n        PHP_EVAL_LIBLINE([$FB_LIBDIR], [SWOOLE_SHARED_LIBADD])\n        PHP_EVAL_INCLINE([$FB_CFLAGS])\n      else\n        AS_VAR_IF([PHP_PDO_FIREBIRD], [yes], [\n          FIREBIRD_INCDIR=\n          FIREBIRD_LIBDIR=\n          FIREBIRD_LIBDIR_FLAG=\n        ], [\n          FIREBIRD_INCDIR=$PHP_PDO_FIREBIRD/include\n          FIREBIRD_LIBDIR=$PHP_PDO_FIREBIRD/$PHP_LIBDIR\n          FIREBIRD_LIBDIR_FLAG=-L$FIREBIRD_LIBDIR\n        ])\n\n        PHP_CHECK_LIBRARY([fbclient], [fb_get_master_interface],\n          [],\n          [AC_MSG_FAILURE([libfbclient not found.])],\n          [$FIREBIRD_LIBDIR_FLAG])\n        PHP_ADD_LIBRARY_WITH_PATH([fbclient],\n          [$FIREBIRD_LIBDIR],\n          [SWOOLE_SHARED_LIBADD])\n        PHP_ADD_INCLUDE([$FIREBIRD_INCDIR])\n      fi\n\n      PHP_CHECK_PDO_INCLUDES\n      AC_DEFINE(SW_USE_FIREBIRD, 1, [do we enable firebird coro support])\n    fi\n\n    dnl firebird stop\n\n\n    dnl ssh2 start\n\n    if test \"$PHP_SWOOLE_SSH2\" != \"no\"; then\n        SEARCH_PATH=\"/usr/local /usr\"\n        SEARCH_FOR=\"/include/libssh2.h\"\n        if test -r $PHP_SWOOLE_SSH2/$SEARCH_FOR; then # path given as parameter\n            SSH2_DIR=$PHP_SWOOLE_SSH2\n        else\n            AC_MSG_CHECKING([for ssh2 files in default path])\n            for i in $SEARCH_PATH ; do\n                if test -r $i/$SEARCH_FOR; then\n                    SSH2_DIR=$i\n                    AC_MSG_RESULT(found in $i)\n                fi\n            done\n        fi\n\n        if test -z \"$SSH2_DIR\"; then\n            AC_MSG_RESULT([not found])\n            AC_MSG_ERROR([The required libssh2 library was not found.\n                You can obtain that package from http://sourceforge.net/projects/libssh2/])\n        fi\n\n        PHP_ADD_INCLUDE($SSH2_DIR/include)\n\n        PHP_CHECK_LIBRARY(ssh2, libssh2_session_hostkey, [\n            PHP_ADD_LIBRARY_WITH_PATH(ssh2, $SSH2_DIR/lib, SWOOLE_SHARED_LIBADD)\n            AC_DEFINE(SW_HAVE_SSH2LIB, 1, [Have libssh2])\n        ],[\n            AC_MSG_ERROR([libssh2 version >= 1.2 not found])\n        ],[\n            -L$SSH2_DIR/lib -lm\n        ])\n\n        PHP_CHECK_LIBRARY(ssh2, libssh2_agent_init, [\n            AC_DEFINE(PHP_SSH2_AGENT_AUTH, 1, [Have libssh2 with ssh-agent support])\n        ],[\n            AC_MSG_WARN([libssh2 <= 1.2.3, ssh-agent subsystem support not enabled])\n        ],[\n            -L$SSH2_DIR/lib -lm\n        ])\n\n        PHP_CHECK_LIBRARY(ssh2, libssh2_session_set_timeout, [\n            AC_DEFINE(PHP_SSH2_SESSION_TIMEOUT, 1, [Have libssh2 with session timeout support])\n        ],[\n            AC_MSG_WARN([libssh2 < 1.2.9, session timeout support not enabled])\n        ],[\n            -L$SSH2_DIR/lib -lm\n        ])\n    fi\n\n    dnl ssh2 stop\n\n    AC_CHECK_LIB(z, gzgets, [\n        AC_DEFINE(SW_HAVE_ZLIB, 1, [have zlib])\n        PHP_ADD_LIBRARY(z, 1, SWOOLE_SHARED_LIBADD)\n    ])\n\n    if test \"$PHP_BROTLI_DIR\" != \"no\"; then\n        AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli])\n        PHP_ADD_INCLUDE(\"${PHP_BROTLI_DIR}/include\")\n        PHP_ADD_LIBRARY_WITH_PATH(brotlienc, \"${PHP_BROTLI_DIR}/${PHP_LIBDIR}\")\n        PHP_ADD_LIBRARY_WITH_PATH(brotlidec, \"${PHP_BROTLI_DIR}/${PHP_LIBDIR}\")\n    elif test \"$PHP_BROTLI\" = \"yes\"; then\n        PKG_CHECK_MODULES([BROTLIENC], [libbrotlienc])\n        PKG_CHECK_MODULES([BROTLIDEC], [libbrotlidec])\n        AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli])\n        PHP_EVAL_LIBLINE($BROTLIENC_LIBS, SWOOLE_SHARED_LIBADD)\n        PHP_EVAL_INCLINE($BROTLIENC_CFLAGS)\n        PHP_EVAL_LIBLINE($BROTLIDEC_LIBS, SWOOLE_SHARED_LIBADD)\n        PHP_EVAL_INCLINE($BROTLIDEC_CFLAGS)\n    elif test \"$PHP_BROTLI\" = \"auto\"; then\n        PKG_CHECK_MODULES([BROTLIENC], [libbrotlienc], [found_brotlienc=yes], [found_brotlienc=no])\n        if test \"$found_brotlienc\" = \"yes\"; then\n            AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli])\n            PHP_EVAL_LIBLINE($BROTLIENC_LIBS, SWOOLE_SHARED_LIBADD)\n            PHP_EVAL_INCLINE($BROTLIENC_CFLAGS)\n        fi\n\n        PKG_CHECK_MODULES([BROTLIDEC], [libbrotlidec], [found_brotlidec=yes], [found_brotlidec=no])\n        if test \"$found_brotlidec\" = \"yes\"; then\n            AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli])\n            PHP_EVAL_LIBLINE($BROTLIDEC_LIBS, SWOOLE_SHARED_LIBADD)\n            PHP_EVAL_INCLINE($BROTLIDEC_CFLAGS)\n        fi\n    fi\n\n    if test \"$PHP_ZSTD\" = \"yes\"; then\n        PKG_CHECK_MODULES([ZSTD], [libzstd >= 1.4.0])\n        AC_DEFINE(SW_HAVE_ZSTD, 1, [have zstd])\n        PHP_EVAL_LIBLINE($ZSTD_LIBS, SWOOLE_SHARED_LIBADD)\n        PHP_EVAL_INCLINE($ZSTD_CFLAGS)\n    fi\n\n    PHP_ADD_LIBRARY(pthread)\n    PHP_SUBST(SWOOLE_SHARED_LIBADD)\n\n    AC_ARG_ENABLE(debug,\n        [  --enable-debug          Compile with debug symbols],\n        [PHP_DEBUG=$enableval],\n        [PHP_DEBUG=0]\n    )\n\n    if test \"$PHP_DEBUG_LOG\" != \"no\"; then\n        AC_DEFINE(SW_DEBUG, 1, [do we enable swoole debug])\n        PHP_DEBUG=1\n    fi\n\n    if test \"$PHP_ASAN\" != \"no\"; then\n        PHP_DEBUG=1\n        CFLAGS=\"$CFLAGS -fsanitize=address -fno-omit-frame-pointer\"\n        CXXFLAGS=\"$CXXFLAGS -fsanitize=address -fno-omit-frame-pointer\"\n    fi\n\n    if test \"$PHP_TRACE_LOG\" != \"no\"; then\n        AC_DEFINE(SW_LOG_TRACE_OPEN, 1, [enable trace log])\n    fi\n\n    if test \"$PHP_SWOOLE_THREAD\" != \"no\"; then\n        AC_DEFINE(SW_THREAD, 1, [enable swoole thread support])\n    fi\n\n    if test \"$PHP_SWOOLE_STDEXT\" != \"no\"; then\n        AC_DEFINE(SW_STDEXT, 1, [enable swoole stdext support])\n    fi\n\n    if test \"$PHP_SOCKETS\" = \"yes\"; then\n        AC_MSG_CHECKING([for php_sockets.h])\n\n        AS_IF([test -f $abs_srcdir/ext/sockets/php_sockets.h], [AC_MSG_RESULT([ok, found in $abs_srcdir])],\n            [test -f $phpincludedir/ext/sockets/php_sockets.h], [AC_MSG_RESULT([ok, found in $phpincludedir])],\n            [AC_MSG_ERROR([cannot find php_sockets.h. Please check if sockets extension is installed.])\n        ])\n\n        AC_DEFINE(SW_SOCKETS, 1, [enable sockets support])\n\n        dnl Some systems build and package PHP socket extension separately\n        dnl and php_config.h does not have HAVE_SOCKETS defined.\n        AC_DEFINE(HAVE_SOCKETS, 1, [whether sockets extension is enabled])\n\n        PHP_ADD_EXTENSION_DEP(swoole, sockets, true)\n    fi\n\n    if test \"$PHP_THREAD\" = \"yes\"; then\n        AC_DEFINE(SW_USE_THREAD, 1, [enable thread support])\n    fi\n\n    if test \"$PHP_CARES\" = \"yes\"; then\n        PKG_CHECK_MODULES([CARES], [libcares])\n        PHP_EVAL_LIBLINE($CARES_LIBS, SWOOLE_SHARED_LIBADD)\n        PHP_EVAL_INCLINE($CARES_CFLAGS)\n        AC_DEFINE(SW_USE_CARES, 1, [do we enable c-ares support])\n        AC_DEFINE(HAVE_CARES, 1, [have c-ares])\n    fi\n\n    AC_SWOOLE_CPU_AFFINITY\n    AC_SWOOLE_HAVE_REUSEPORT\n    AC_SWOOLE_HAVE_FUTEX\n    AC_SWOOLE_HAVE_UCONTEXT\n    AC_SWOOLE_HAVE_VALGRIND\n    AC_SWOOLE_CHECK_SOCKETS\n    AC_SWOOLE_HAVE_MSGQUEUE\n    AC_SWOOLE_HAVE_BOOST_STACKTRACE\n\n    AS_CASE([$host_os],\n      [darwin*], [SW_OS=\"MAC\"],\n      [cygwin*], [SW_OS=\"CYGWIN\"],\n      [mingw*], [SW_OS=\"MINGW\"],\n      [linux*], [SW_OS=\"LINUX\"],\n      [*bsd*], [SW_OS=\"BSD\"],\n      []\n    )\n\n    CFLAGS=\"-Wall -pthread $CFLAGS\"\n    LDFLAGS=\"$LDFLAGS -lpthread\"\n\n\tHAVE_CONFIG_IOURING_X=\"no\"\n    if (test \"$PHP_IOURING\" = \"yes\" || test \"$PHP_LIBURING_DIR\" != \"no\") && test \"$SW_OS\" = \"LINUX\"; then\n        if test \"$PHP_IOURING\" != \"no\"; then\n            PKG_CHECK_MODULES([URING], [liburing >= 2.8])\n\t\t\tPHP_EVAL_LIBLINE($URING_LIBS, SWOOLE_SHARED_LIBADD)\n            PHP_EVAL_INCLINE($URING_CFLAGS)\n        elif test \"$PHP_LIBURING_DIR\" != \"no\"; then\n            PHP_ADD_INCLUDE(\"${PHP_LIBURING_DIR}/include\")\n            PHP_ADD_LIBRARY_WITH_PATH(uring, \"${PHP_LIBURING_DIR}/${PHP_LIBDIR}\")\n            PHP_ADD_LIBRARY(uring, 1, SWOOLE_SHARED_LIBADD)\n        fi\n\n        AC_SWOOLE_HAVE_IOURING_STATX\n\n        KERNEL_MAJOR=`uname -r | awk -F '.' '{print $1}'`\n        KERNEL_MINOR=`uname -r | awk -F '.' '{print $2}'`\n\n        if (test $KERNEL_MAJOR -eq 6 && test $KERNEL_MINOR -ge 9); then\n            dnl IORING_OP_FTRUNCATE is available since 6.9\n            AC_SWOOLE_HAVE_IOURING_FTRUNCATE\n        fi\n\n        if (test $KERNEL_MAJOR -eq 6 && test $KERNEL_MINOR -ge 7); then\n            dnl IORING_OP_FUTEX_WAKE/IORING_OP_FUTEX_WAIT is available since 6.7\n            AC_SWOOLE_HAVE_IOURING_FUTEX\n        fi\n\n        HAVE_CONFIG_IOURING_X=\"yes\"\n        AC_DEFINE(SW_USE_IOURING, 1, [have io_uring])\n    fi\n\n    if test \"$PHP_URING_SOCKET\" != \"no\"; then\n        if test \"$HAVE_CONFIG_IOURING_X\" != \"no\"; then\n            AC_DEFINE(SW_USE_URING_SOCKET, 1, [enable io_uring socket support])\n        else\n            AC_MSG_ERROR([--enable-uring-socket requires io_uring. Please specify either --enable-iouring or --with-liburing-dir.])\n        fi\n    fi\n\n    dnl Check should we link to librt\n\n    if test \"$SW_OS\" = \"LINUX\"; then\n        GLIBC_VERSION=$(getconf GNU_LIBC_VERSION | awk '{print $2}')\n        AC_MSG_NOTICE([glibc version: $GLIBC_VERSION])\n\n        GLIBC_MAJOR_VERSION=$(getconf GNU_LIBC_VERSION | awk '{print $2}' | cut -d '.' -f 1)\n        GLIBC_MINOR_VERSION=$(getconf GNU_LIBC_VERSION | awk '{print $2}' | cut -d '.' -f 2)\n\n        if test $GLIBC_MAJOR_VERSION -lt 2 || (test $GLIBC_MAJOR_VERSION -eq 2 && test $GLIBC_MINOR_VERSION -lt 17); then\n            OS_SHOULD_HAVE_LIBRT=1\n        else\n            AC_MSG_NOTICE([link with -lrt (only for glibc version before 2.17)])\n            OS_SHOULD_HAVE_LIBRT=0\n        fi\n    elif test \"$SW_OS\" = \"MAC\"; then\n        OS_SHOULD_HAVE_LIBRT=0\n    else\n        AS_CASE([$host_os],\n          [openbsd*], [OS_SHOULD_HAVE_LIBRT=0]\n          [OS_SHOULD_HAVE_LIBRT=1]\n        )\n    fi\n\n    if test \"x$OS_SHOULD_HAVE_LIBRT\" = \"x1\"; then\n        AC_MSG_NOTICE([Librt is required on $host_os.])\n        dnl Check for the existence of librt\n        AC_CHECK_LIB([rt], [clock_gettime], [], [\n            AC_MSG_ERROR([We have to link to librt on your os, but librt not found.])\n        ])\n        PHP_ADD_LIBRARY(rt, 1, SWOOLE_SHARED_LIBADD)\n    else\n        AC_MSG_NOTICE([$host_os doesn't have librt -- don't link to librt.])\n    fi\n\n    if test \"$SW_OS\" = \"LINUX\"; then\n        LDFLAGS=\"$LDFLAGS -z now\"\n    fi\n\n    if test \"$PHP_OPENSSL_DIR\" != \"no\"; then\n        PHP_ADD_INCLUDE(\"${PHP_OPENSSL_DIR}/include\")\n        PHP_ADD_LIBRARY_WITH_PATH(ssl, \"${PHP_OPENSSL_DIR}/${PHP_LIBDIR}\")\n        PHP_ADD_LIBRARY_WITH_PATH(crypto, \"${PHP_OPENSSL_DIR}/${PHP_LIBDIR}\")\n    else\n        PKG_CHECK_MODULES([SSL], [libssl])\n        PHP_EVAL_LIBLINE($SSL_LIBS, SWOOLE_SHARED_LIBADD)\n        PHP_EVAL_INCLINE($SSL_CFLAGS)\n        \n        PKG_CHECK_MODULES([CRYPTO], [libcrypto])\n        PHP_EVAL_LIBLINE($CRYPTO_LIBS, SWOOLE_SHARED_LIBADD)\n        PHP_EVAL_INCLINE($CRYPTO_CFLAGS)\n    fi\n    \n    if test \"$PHP_NGHTTP2_DIR\" != \"no\"; then\n        PHP_ADD_INCLUDE(\"${PHP_NGHTTP2_DIR}/include\")\n        PHP_ADD_LIBRARY_WITH_PATH(nghttp2, \"${PHP_NGHTTP2_DIR}/${PHP_LIBDIR}\")\n        PHP_ADD_LIBRARY(nghttp2, 1, SWOOLE_SHARED_LIBADD)\n    fi\n\n    PHP_ADD_LIBRARY(pthread, 1, SWOOLE_SHARED_LIBADD)\n\n    if test \"$PHP_MYSQLND\" = \"yes\"; then\n        PHP_ADD_EXTENSION_DEP(mysqli, mysqlnd)\n        AC_DEFINE(SW_USE_MYSQLND, 1, [use mysqlnd])\n    fi\n\n    AC_MSG_CHECKING([for sources])\n    if test -f \"$abs_srcdir/ext-src/php_swoole.cc\"; then\n        swoole_source_dir=$abs_srcdir\n    elif test -f \"ext-src/php_swoole.cc\"; then\n        swoole_source_dir=$(pwd)\n    else\n        swoole_source_dir=\"ext/swoole\"\n    fi\n    AC_MSG_RESULT([$swoole_source_dir])\n\n    ext_src_files=$(cd $swoole_source_dir && find ext-src/ -name *.cc)\n    lib_src_files=$(cd $swoole_source_dir && find src/ -name *.cc)\n\n    swoole_source_file=\"${ext_src_files} ${lib_src_files}\"\n\n    swoole_source_file=\"$swoole_source_file \\\n        thirdparty/php/sockets/multicast.cc \\\n        thirdparty/php/sockets/sendrecvmsg.cc \\\n        thirdparty/php/sockets/conversions.cc \\\n        thirdparty/php/sockets/sockaddr_conv.cc \\\n        thirdparty/php/standard/var_decoder.cc \\\n        thirdparty/php/standard/proc_open.cc\"\n\n    swoole_source_file=\"$swoole_source_file \\\n        thirdparty/llhttp/api.c \\\n        thirdparty/llhttp/http.c \\\n        thirdparty/llhttp/llhttp.c \\\n        thirdparty/multipart_parser.c\"\n\n    if test \"$PHP_NGHTTP2_DIR\" = \"no\"; then\n        swoole_source_file=\"$swoole_source_file \\\n            thirdparty/nghttp2/nghttp2_hd.c \\\n            thirdparty/nghttp2/nghttp2_rcbuf.c \\\n            thirdparty/nghttp2/nghttp2_helper.c \\\n            thirdparty/nghttp2/nghttp2_buf.c \\\n            thirdparty/nghttp2/nghttp2_mem.c \\\n            thirdparty/nghttp2/nghttp2_hd_huffman.c \\\n            thirdparty/nghttp2/nghttp2_hd_huffman_data.c\"\n    fi\n\n    dnl During static compilation, there is no php-config variable,\n    dnl but the php-version variable is always present and is not affected by the shell environment variables.\n    dnl During dynamic compilation, the php-config variable is always available, whereas the php-version variable is absent.\n\n    if test -z \"$PHP_CONFIG\"; then\n        if test -z \"$PHP_VERSION\"; then\n            AC_MSG_ERROR([the PHP_VERSION variable must be defined])\n        else\n            SW_PHP_VERSION=$PHP_VERSION\n        fi\n    else\n        SW_PHP_VERSION=`$PHP_CONFIG --version`\n    fi\n\n    SW_PHP_VERSION_ID=`echo \"${SW_PHP_VERSION}\" | $AWK 'BEGIN { FS = \".\"; } { printf \"%d\", ([$]1 * 10 + [$]2); }'`\n    SW_PHP_THIRDPARTY_DIR=\"thirdparty/php${SW_PHP_VERSION_ID}\"\n\n    AC_MSG_NOTICE([php version: $SW_PHP_VERSION, version_id: $SW_PHP_VERSION_ID, thirdparty_dir: $SW_PHP_THIRDPARTY_DIR])\n\n    if test \"$PHP_SWOOLE_CURL\" != \"no\"; then\n        if test \"$SW_PHP_VERSION_ID\" -ge \"84\"; then\n            swoole_source_file=\"$swoole_source_file \\\n                thirdparty/php84/curl/interface.cc \\\n                thirdparty/php84/curl/multi.cc\"\n        else\n            swoole_source_file=\"$swoole_source_file \\\n                thirdparty/php/curl/interface.cc \\\n                thirdparty/php/curl/multi.cc\"\n        fi\n    fi\n\n    if test \"$PHP_SWOOLE_PGSQL\" != \"no\"; then\n        swoole_source_file=\"$swoole_source_file \\\n            ${SW_PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_driver.c \\\n            ${SW_PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_statement.c\"\n        if test \"$SW_PHP_VERSION_ID\" -ge \"84\"; then\n            swoole_source_file=\"$swoole_source_file \\\n                ${SW_PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_sql_parser.c\"\n        fi\n    fi\n\n    if test \"$PHP_SWOOLE_ORACLE\" != \"no\"; then\n        swoole_source_file=\"$swoole_source_file \\\n            thirdparty/pdo_oci/oci_driver.c \\\n            thirdparty/pdo_oci/oci_statement.c\"\n    fi\n\n    if test \"$PHP_SWOOLE_ODBC\" != \"no\"; then\n        swoole_source_file=\"$swoole_source_file \\\n            ${SW_PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_driver.c \\\n            ${SW_PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_stmt.c\"\n    fi\n\n    if test \"$PHP_SWOOLE_SQLITE\" != \"no\"; then\n        swoole_source_file=\"$swoole_source_file \\\n            thirdparty/pdo_sqlite/sqlite_driver.c \\\n            thirdparty/pdo_sqlite/sqlite_statement.c\"\n        if test \"$SW_PHP_VERSION_ID\" -ge \"84\"; then\n            swoole_source_file=\"$swoole_source_file \\\n                thirdparty/pdo_sqlite/sqlite_sql_parser.c\"\n        fi\n    fi\n\n    if test \"$PHP_SWOOLE_FIREBIRD\" != \"no\"; then\n        if test \"$SW_PHP_VERSION_ID\" -ge \"85\"; then\n            swoole_source_file=\"$swoole_source_file \\\n                thirdparty/php85/pdo_firebird/firebird_driver.c \\\n                thirdparty/php85/pdo_firebird/firebird_statement.c \\\n                thirdparty/php85/pdo_firebird/pdo_firebird_utils.cpp\"\n        else\n            swoole_source_file=\"$swoole_source_file \\\n                thirdparty/php84/pdo_firebird/firebird_driver.c \\\n                thirdparty/php84/pdo_firebird/firebird_statement.c \\\n                thirdparty/php84/pdo_firebird/pdo_firebird_utils.cpp\"\n        fi\n    fi\n\n    if test \"$PHP_SWOOLE_SSH2\" != \"no\"; then\n        swoole_source_file=\"$swoole_source_file \\\n            thirdparty/php/ssh2/ssh2.cc \\\n            thirdparty/php/ssh2/ssh2_fopen_wrappers.cc \\\n            thirdparty/php/ssh2/ssh2_sftp.cc\"\n    fi\n\n    if test \"$PHP_SWOOLE_FTP\" != \"no\"; then\n        AC_DEFINE(SW_HAVE_FTP_SSL, 1, [have swoole-ftp with SSL])\n        swoole_source_file=\"$swoole_source_file \\\n            thirdparty/php84/ftp/ftp.c \\\n            thirdparty/php84/ftp/php_ftp.c\"\n        AC_DEFINE(SW_HAVE_FTP, 1, [have swoole-ftp])\n    fi\n\n    SW_ASM_DIR=\"thirdparty/boost/asm/\"\n    SW_USE_ASM_CONTEXT=\"yes\"\n\n    AS_CASE([$host_cpu],\n      [x86_64*], [SW_CPU=\"x86_64\"],\n      [amd64*], [SW_CPU=\"x86_64\"],\n      [x86*], [SW_CPU=\"x86\"],\n      [i?86*], [SW_CPU=\"x86\"],\n      [arm64*], [SW_CPU=\"arm64\"],\n      [aarch64*], [SW_CPU=\"arm64\"],\n      [arm*], [SW_CPU=\"arm32\"],\n      [mips64*], [SW_CPU=\"mips64\"],\n      [mips*], [SW_CPU=\"mips32\"],\n      [riscv64*], [SW_CPU=\"riscv64\"],\n      [loongarch64*], [SW_CPU=\"loongarch64\"],\n      [\n        SW_USE_ASM_CONTEXT=\"no\"\n      ]\n    )\n\n    if test \"$SW_OS\" = \"MAC\"; then\n        SW_CONTEXT_ASM_FILE=\"combined_sysv_macho_gas.S\"\n    elif test \"$SW_CPU\" = \"x86_64\"; then\n        if test \"$SW_OS\" = \"LINUX\" || test \"$SW_OS\" = \"BSD\"; then\n            SW_CONTEXT_ASM_FILE=\"x86_64_sysv_elf_gas.S\"\n        else\n            SW_USE_ASM_CONTEXT=\"no\"\n        fi\n    elif test \"$SW_CPU\" = \"x86\"; then\n        if test \"$SW_OS\" = \"LINUX\" || test \"$SW_OS\" = \"BSD\"; then\n            SW_CONTEXT_ASM_FILE=\"i386_sysv_elf_gas.S\"\n        else\n            SW_USE_ASM_CONTEXT=\"no\"\n        fi\n    elif test \"$SW_CPU\" = \"arm32\"; then\n        if test \"$SW_OS\" = \"LINUX\" || test \"$SW_OS\" = \"BSD\"; then\n            SW_CONTEXT_ASM_FILE=\"arm_aapcs_elf_gas.S\"\n        else\n            SW_USE_ASM_CONTEXT=\"no\"\n        fi\n    elif test \"$SW_CPU\" = \"arm64\"; then\n        if test \"$SW_OS\" = \"LINUX\" || test \"$SW_OS\" = \"BSD\"; then\n            SW_CONTEXT_ASM_FILE=\"arm64_aapcs_elf_gas.S\"\n        else\n            SW_USE_ASM_CONTEXT=\"no\"\n        fi\n     elif test \"$SW_CPU\" = \"ppc32\"; then\n        if test \"$SW_OS\" = \"LINUX\"; then\n            SW_CONTEXT_ASM_FILE=\"ppc32_sysv_elf_gas.S\"\n        else\n            SW_USE_ASM_CONTEXT=\"no\"\n        fi\n    elif test \"$SW_CPU\" = \"ppc64\"; then\n        if test \"$SW_OS\" = \"LINUX\" || test \"$SW_OS\" = \"BSD\"; then\n            SW_CONTEXT_ASM_FILE=\"ppc64_sysv_elf_gas.S\"\n        else\n            SW_USE_ASM_CONTEXT=\"no\"\n        fi\n    elif test \"$SW_CPU\" = \"mips64\"; then\n        if test \"$SW_OS\" = \"LINUX\"; then\n           SW_CONTEXT_ASM_FILE=\"mips64_n64_elf_gas.S\"\n        else\n            SW_USE_ASM_CONTEXT=\"no\"\n        fi\n    elif test \"$SW_CPU\" = \"mips32\"; then\n        if test \"$SW_OS\" = \"LINUX\"; then\n           SW_CONTEXT_ASM_FILE=\"mips32_o32_elf_gas.S\"\n        else\n            SW_USE_ASM_CONTEXT=\"no\"\n        fi\n    elif test \"$SW_CPU\" = \"riscv64\"; then\n        if test \"$SW_OS\" = \"LINUX\"; then\n           SW_CONTEXT_ASM_FILE=\"riscv64_sysv_elf_gas.S\"\n        else\n            SW_USE_ASM_CONTEXT=\"no\"\n        fi\n    elif test \"$SW_CPU\" = \"loongarch64\"; then\n        if test \"$SW_OS\" = \"LINUX\"; then\n           SW_CONTEXT_ASM_FILE=\"loongarch64_sysv_elf_gas.S\"\n        else\n            SW_USE_ASM_CONTEXT=\"no\"\n        fi\n    else\n        SW_USE_ASM_CONTEXT=\"no\"\n    fi\n\n    if test \"$PHP_THREAD_CONTEXT\" != \"no\"; then\n        AC_DEFINE(SW_USE_THREAD_CONTEXT, 1, [do we enable thread context])\n        SW_USE_ASM_CONTEXT=\"no\"\n    fi\n\n    if test \"$SW_USE_ASM_CONTEXT\" = \"yes\"; then\n        swoole_source_file=\"$swoole_source_file \\\n            ${SW_ASM_DIR}make_${SW_CONTEXT_ASM_FILE} \\\n            ${SW_ASM_DIR}jump_${SW_CONTEXT_ASM_FILE} \"\n        AC_DEFINE(SW_USE_ASM_CONTEXT, 1, [use boost asm context])\n    fi\n\n    EXTRA_CFLAGS=\"$EXTRA_CFLAGS -DENABLE_PHP_SWOOLE\"\n\n    PHP_NEW_EXTENSION(swoole, $swoole_source_file, $ext_shared,, \"$EXTRA_CFLAGS\", cxx)\n\n    PHP_ADD_INCLUDE([$ext_srcdir])\n    PHP_ADD_INCLUDE([$ext_srcdir/include])\n    PHP_ADD_INCLUDE([$ext_srcdir/ext-src])\n    PHP_ADD_INCLUDE([$ext_srcdir/thirdparty])\n\n    AC_MSG_CHECKING([swoole coverage])\n    if test \"$PHP_SWOOLE_COVERAGE\" != \"no\"; then\n        AC_MSG_RESULT([enabled])\n\n        PHP_ADD_MAKEFILE_FRAGMENT\n    else\n        AC_MSG_RESULT([disabled])\n    fi\n\n    PHP_INSTALL_HEADERS([ext/swoole], [ext-src/*.h config.h php_swoole.h \\\n        include/*.h \\\n        stubs/*.h \\\n        thirdparty/*.h \\\n        thirdparty/llhttp/*.h \\\n        thirdparty/nghttp2/*.h])\n\n    PHP_REQUIRE_CXX()\n\n    CXXFLAGS=\"$CXXFLAGS -Wall -Wno-date-time -Wno-unused-function -Wno-deprecated -Wno-deprecated-declarations\"\n\n    if test \"$SW_OS\" = \"CYGWIN\" || test \"$SW_OS\" = \"MINGW\"; then\n        CXXFLAGS=\"$CXXFLAGS -std=gnu++14\"\n    else\n        CXXFLAGS=\"$CXXFLAGS -std=c++14\"\n    fi\n\n    if test \"$SW_CPU\" = \"arm\"; then\n        PHP_ADD_LIBRARY(atomic, 1, SWOOLE_SHARED_LIBADD)\n    fi\n\n    PHP_ADD_BUILD_DIR($ext_builddir/ext-src)\n    PHP_ADD_BUILD_DIR($ext_builddir/src/core)\n    PHP_ADD_BUILD_DIR($ext_builddir/src/memory)\n    PHP_ADD_BUILD_DIR($ext_builddir/src/reactor)\n    PHP_ADD_BUILD_DIR($ext_builddir/src/lock)\n    PHP_ADD_BUILD_DIR($ext_builddir/src/os)\n    PHP_ADD_BUILD_DIR($ext_builddir/src/network)\n    PHP_ADD_BUILD_DIR($ext_builddir/src/server)\n    PHP_ADD_BUILD_DIR($ext_builddir/src/protocol)\n    PHP_ADD_BUILD_DIR($ext_builddir/src/coroutine)\n    PHP_ADD_BUILD_DIR($ext_builddir/src/wrapper)\n    PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/boost)\n    PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/boost/asm)\n    PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/sockets)\n    PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/standard)\n    PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/curl)\n    PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/ssh2)\n    PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/pdo_oci)\n    PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/pdo_sqlite)\n    PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/curl)\n    PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_firebird)\n    PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php85/pdo_firebird)\n    PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/llhttp)\n\n    if test \"$PHP_NGHTTP2_DIR\" = \"no\"; then\n        PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/nghttp2)\n    fi\n    if test \"$PHP_SWOOLE_PGSQL\" != \"no\"; then\n        PHP_ADD_BUILD_DIR($ext_builddir/${SW_PHP_THIRDPARTY_DIR}/pdo_pgsql)\n    fi\n    if test \"$PHP_SWOOLE_ODBC\" != \"no\"; then\n        PHP_ADD_BUILD_DIR($ext_builddir/${SW_PHP_THIRDPARTY_DIR}/pdo_odbc)\n    fi\n    if test \"$PHP_SWOOLE_FTP\" != \"no\"; then\n        PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/ftp)\n    fi\nfi\n"
  },
  {
    "path": "core-tests/.gitignore",
    "content": ".idea/\n.cmake/\nTesting/\nbuild.ninja\n.ninja_deps\n.ninja_log"
  },
  {
    "path": "core-tests/code-stats.sh",
    "content": "#!/bin/sh -e\n__CURRENT__=$(pwd)\n__DIR__=$(cd \"$(dirname \"$0\")\";pwd)\n\n# enter the dir\ncd \"${__DIR__}\"\ncloc . --exclude-dir=js,Debug,CMakeFiles,build,CMakeFiles,deps\n"
  },
  {
    "path": "core-tests/docker-compose.yml",
    "content": "version: '3.4'\nservices:\n  httpbin:\n    container_name: \"httpbin\"\n    image: \"kennethreitz/httpbin\"\n  tinyproxy:\n    container_name: \"tinyproxy\"\n    image: \"vimagick/tinyproxy\"\n    ports:\n      - 8888:8888\n  socks5:\n    container_name: \"socks5\"\n    image: \"xkuma/socks5\"\n    ports:\n      - \"8080:1080\"\n    environment:\n      - PROXY_USER=user\n      - PROXY_PASSWORD=password\n      - PROXY_SERVER=0.0.0.0:1080\n  socks5-no-auth:\n    image: \"xkuma/socks5\"\n    ports:\n      - 8081:1080\n    environment:\n      PROXY_SERVER: 0.0.0.0:1080\n"
  },
  {
    "path": "core-tests/fuzz/project.json",
    "content": "{\n    \"project\": {\n        \"name\": \"fuzz\",\n        \"type\": \"bin\"\n    },\n    \"build\": {\n        \"ldflags\": \"\",\n        \"cxxflags\": \"\",\n        \"cflags\": \"\",\n        \"c_std\": \"\",\n        \"cxx_std\": \"\"\n    },\n    \"install\": {\n        \"target\": \"\"\n    }\n}"
  },
  {
    "path": "core-tests/fuzz/src/main.cpp",
    "content": "#include \"phpx_embed.h\"\n#include <iostream>\n#include <stdio.h>\n#include <unistd.h>\n#include <fcntl.h>\n\nusing namespace php;\nusing namespace std;\n\nint main(int argc, char * argv[])\n{\n    VM vm(argc, argv);\n    cout << \"hello world\" << endl;\n\n    char buf[8192];\n    ssize_t n;\n\n    int fd = 0;\n    if (argc > 0) {\n        fd = open(argv[1], O_RDONLY);\n    }\n\n    n = read(fd, buf, 8192);\n    if (n < 0) {\n        fprintf(stderr, \"failed to read data\\n\");\n        return 1;\n    }\n\n    auto req_var = exec(\"Swoole\\\\Http\\\\Request::create\");\n\n    var_dump(req_var);\n\n    if (!req_var.isObject()) {\n        fprintf(stderr, \"cannot create object of Swoole\\\\Http\\\\Request\\n\");\n        return 2;\n    }\n\n    Variant data(buf, n);\n\n    auto req = Object(req_var);\n    auto retval = req.exec(\"parse\", data);\n\n    printf(\"retval=%ld\", retval.toInt());\n\n    var_dump(req);\n\n    return 0;\n}\n"
  },
  {
    "path": "core-tests/include/httplib_client.h",
    "content": "/**\n * httplib.h\n *\n * Copyright (c) 2020 Yuji Hirose. All rights reserved.\n * MIT License\n * GitHub: https://github.com/yhirose/cpp-httplib\n */\n\n#pragma once\n\n#include \"swoole_websocket.h\"\n\n/*\n * Configuration\n */\n\n#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND\n#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5\n#endif\n\n#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND\n#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT\n#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5\n#endif\n\n#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND\n#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300\n#endif\n\n#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND\n#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND\n#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5\n#endif\n\n#ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND\n#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND\n#define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5\n#endif\n\n#ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND\n#define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND\n#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND\n#ifdef _WIN32\n#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000\n#else\n#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0\n#endif\n#endif\n\n#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH\n#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192\n#endif\n\n#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT\n#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20\n#endif\n\n#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH\n#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())\n#endif\n\n#ifndef CPPHTTPLIB_TCP_NODELAY\n#define CPPHTTPLIB_TCP_NODELAY true\n#endif\n\n#ifndef CPPHTTPLIB_RECV_BUFSIZ\n#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u)\n#endif\n\n#ifndef CPPHTTPLIB_THREAD_POOL_COUNT\n#define CPPHTTPLIB_THREAD_POOL_COUNT                                                                                   \\\n    ((std::max)(8u, std::thread::hardware_concurrency() > 0 ? std::thread::hardware_concurrency() - 1 : 0))\n#endif\n\n// Prefer gnu::deprecated, otherwise gcc complains if we use\n// [[deprecated]] together with pedantic.\n#ifndef CPPHTTPLIB_DEPRECATED\n#if defined(__has_cpp_attribute)\n#if __has_cpp_attribute(gnu::deprecated)\n#define CPPHTTPLIB_DEPRECATED [[gnu::deprecated]]\n#else\n#if __has_cpp_attribute(deprecated)\n#define CPPHTTPLIB_DEPRECATED [[deprecated]]\n#else\n#define CPPHTTPLIB_DEPRECATED\n#endif\n#endif\n#else\n#define CPPHTTPLIB_DEPRECATED\n#endif\n#endif\n\n/*\n * Headers\n */\n\n#ifdef _WIN32\n#ifndef _CRT_SECURE_NO_WARNINGS\n#define _CRT_SECURE_NO_WARNINGS\n#endif  //_CRT_SECURE_NO_WARNINGS\n\n#ifndef _CRT_NONSTDC_NO_DEPRECATE\n#define _CRT_NONSTDC_NO_DEPRECATE\n#endif  //_CRT_NONSTDC_NO_DEPRECATE\n\n#if defined(_MSC_VER)\n#ifdef _WIN64\nusing ssize_t = __int64;\n#else\nusing ssize_t = int;\n#endif\n\n#if _MSC_VER < 1900\n#define snprintf _snprintf_s\n#endif\n#endif  // _MSC_VER\n\n#ifndef S_ISREG\n#define S_ISREG(m) (((m) &S_IFREG) == S_IFREG)\n#endif  // S_ISREG\n\n#ifndef S_ISDIR\n#define S_ISDIR(m) (((m) &S_IFDIR) == S_IFDIR)\n#endif  // S_ISDIR\n\n#ifndef NOMINMAX\n#define NOMINMAX\n#endif  // NOMINMAX\n\n#include <io.h>\n#include <winsock2.h>\n#include <ws2tcpip.h>\n\n#ifndef WSA_FLAG_NO_HANDLE_INHERIT\n#define WSA_FLAG_NO_HANDLE_INHERIT 0x80\n#endif\n\n#ifdef _MSC_VER\n#pragma comment(lib, \"ws2_32.lib\")\n#endif\n\n#ifndef strcasecmp\n#define strcasecmp _stricmp\n#endif  // strcasecmp\n\nusing socket_t = SOCKET;\n#ifdef CPPHTTPLIB_USE_POLL\n#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)\n#endif\n\n#else  // not _WIN32\n\n#include <arpa/inet.h>\n#include <cstring>\n#include <ifaddrs.h>\n#include <netdb.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n#ifdef CPPHTTPLIB_USE_POLL\n#include <poll.h>\n#endif\n#include <csignal>\n#include <pthread.h>\n#include <sys/select.h>\n#include <sys/socket.h>\n#include <unistd.h>\n\nusing socket_t = int;\n#define INVALID_SOCKET (-1)\n#endif  //_WIN32\n\n#include <array>\n#include <atomic>\n#include <cassert>\n#include <climits>\n#include <condition_variable>\n#include <errno.h>\n#include <fcntl.h>\n#include <fstream>\n#include <functional>\n#include <iostream>\n#include <list>\n#include <map>\n#include <memory>\n#include <mutex>\n#include <random>\n#include <regex>\n#include <string>\n#include <sys/stat.h>\n#include <thread>\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n#include <openssl/err.h>\n#include <openssl/md5.h>\n#include <openssl/ssl.h>\n#include <openssl/x509v3.h>\n\n#include <iomanip>\n#include <iostream>\n#include <sstream>\n\n// #if OPENSSL_VERSION_NUMBER < 0x1010100fL\n// #error Sorry, OpenSSL versions prior to 1.1.1 are not supported\n// #endif\n\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\n#include <openssl/crypto.h>\ninline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) {\n    return M_ASN1_STRING_data(asn1);\n}\n#endif\n#endif\n\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n#include <zlib.h>\n#endif\n/*\n * Declaration\n */\nnamespace httplib {\n\nconst std::string USER_AGENT = \"cpp-httplib/0.7\";\n\nnamespace detail {\n\nstruct ci {\n    bool operator()(const std::string &s1, const std::string &s2) const {\n        return std::lexicographical_compare(\n            s1.begin(), s1.end(), s2.begin(), s2.end(), [](char c1, char c2) { return ::tolower(c1) < ::tolower(c2); });\n    }\n};\n\n}  // namespace detail\n\nusing Headers = std::multimap<std::string, std::string, detail::ci>;\n\nusing Params = std::multimap<std::string, std::string>;\nusing Match = std::smatch;\n\nusing Progress = std::function<bool(uint64_t current, uint64_t total)>;\n\nstruct Response;\nstruct WebSocketFrame;\n\nusing ResponseHandler = std::function<bool(const Response &response)>;\n\nstruct MultipartFormData {\n    std::string name;\n    std::string content;\n    std::string filename;\n    std::string content_type;\n};\nusing MultipartFormDataItems = std::vector<MultipartFormData>;\nusing MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;\n\nclass DataSink {\n  public:\n    DataSink() : os(&sb_), sb_(*this) {}\n\n    DataSink(const DataSink &) = delete;\n    DataSink &operator=(const DataSink &) = delete;\n    DataSink(DataSink &&) = delete;\n    DataSink &operator=(DataSink &&) = delete;\n\n    std::function<void(const char *data, size_t data_len)> write;\n    std::function<void()> done;\n    std::function<bool()> is_writable;\n    std::ostream os;\n\n  private:\n    class data_sink_streambuf : public std::streambuf {\n      public:\n        data_sink_streambuf(DataSink &sink) : sink_(sink) {}\n\n      protected:\n        std::streamsize xsputn(const char *s, std::streamsize n) {\n            sink_.write(s, static_cast<size_t>(n));\n            return n;\n        }\n\n      private:\n        DataSink &sink_;\n    };\n\n    data_sink_streambuf sb_;\n};\n\nusing ContentProvider = std::function<bool(size_t offset, size_t length, DataSink &sink)>;\n\nusing ChunkedContentProvider = std::function<bool(size_t offset, DataSink &sink)>;\n\nusing ContentReceiver = std::function<bool(const char *data, size_t data_length)>;\n\nusing MultipartContentHeader = std::function<bool(const MultipartFormData &file)>;\n\nusing Range = std::pair<ssize_t, ssize_t>;\nusing Ranges = std::vector<Range>;\n\nstruct Request {\n    std::string method;\n    std::string path;\n    Headers headers;\n    std::string body;\n\n    std::string remote_addr;\n    int remote_port = -1;\n\n    // for server\n    std::string version;\n    std::string target;\n    Params params;\n    MultipartFormDataMap files;\n    Ranges ranges;\n    Match matches;\n\n    // for client\n    size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT;\n    ResponseHandler response_handler;\n    ContentReceiver content_receiver;\n    size_t content_length = 0;\n    ContentProvider content_provider;\n    Progress progress;\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n    const SSL *ssl;\n#endif\n\n    bool has_header(const char *key) const;\n    std::string get_header_value(const char *key, size_t id = 0) const;\n    size_t get_header_value_count(const char *key) const;\n    void set_header(const char *key, const char *val);\n    void set_header(const char *key, const std::string &val);\n\n    bool has_param(const char *key) const;\n    std::string get_param_value(const char *key, size_t id = 0) const;\n    size_t get_param_value_count(const char *key) const;\n\n    bool is_multipart_form_data() const;\n\n    bool has_file(const char *key) const;\n    MultipartFormData get_file_value(const char *key) const;\n\n    // private members...\n    size_t authorization_count_ = 0;\n};\n\nstruct Response {\n    std::string version;\n    int status = -1;\n    Headers headers;\n    std::string body;\n\n    bool has_header(const char *key) const;\n    std::string get_header_value(const char *key, size_t id = 0) const;\n    size_t get_header_value_count(const char *key) const;\n    void set_header(const char *key, const char *val);\n    void set_header(const char *key, const std::string &val);\n\n    void set_redirect(const char *url, int status = 302);\n    void set_content(const char *s, size_t n, const char *content_type);\n    void set_content(std::string s, const char *content_type);\n\n    void set_content_provider(\n        size_t length, ContentProvider provider, std::function<void()> resource_releaser = [] {});\n\n    void set_chunked_content_provider(\n        ChunkedContentProvider provider, std::function<void()> resource_releaser = [] {});\n\n    Response() = default;\n    Response(const Response &) = default;\n    Response &operator=(const Response &) = default;\n    Response(Response &&) = default;\n    Response &operator=(Response &&) = default;\n    ~Response() {\n        if (content_provider_resource_releaser_) {\n            content_provider_resource_releaser_();\n        }\n    }\n\n    // private members...\n    size_t content_length_ = 0;\n    ContentProvider content_provider_;\n    std::function<void()> content_provider_resource_releaser_;\n};\n\nstruct WebSocketFrame : public swoole::websocket::Frame {\n    WebSocketFrame() = default;\n    ~WebSocketFrame() {\n        if (payload) {\n            sw_free(payload - header_length);\n        }\n    }\n};\n\nclass Stream {\n  public:\n    virtual ~Stream() = default;\n\n    virtual bool is_readable() const = 0;\n    virtual bool is_writable() const = 0;\n\n    virtual ssize_t read(char *ptr, size_t size) = 0;\n    virtual ssize_t write(const char *ptr, size_t size) = 0;\n    virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;\n\n    template <typename... Args>\n    ssize_t write_format(const char *fmt, const Args &...args);\n    ssize_t write(const char *ptr);\n    ssize_t write(const std::string &s);\n};\n\nusing Logger = std::function<void(const Request &, const Response &)>;\n\nusing SocketOptions = std::function<void(socket_t sock)>;\n\ninline void default_socket_options(socket_t sock) {\n    int yes = 1;\n#ifdef _WIN32\n    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&yes), sizeof(yes));\n    setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, reinterpret_cast<char *>(&yes), sizeof(yes));\n#else\n#ifdef SO_REUSEPORT\n    setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<void *>(&yes), sizeof(yes));\n#else\n    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<void *>(&yes), sizeof(yes));\n#endif\n#endif\n}\n\nclass Client {\n  public:\n    explicit Client(const std::string &host);\n\n    explicit Client(const std::string &host, int port);\n\n    explicit Client(const std::string &host,\n                    int port,\n                    const std::string &client_cert_path,\n                    const std::string &client_key_path);\n\n    virtual ~Client();\n\n    virtual bool is_valid() const;\n\n    std::shared_ptr<Response> Get(const char *path);\n\n    std::shared_ptr<Response> Get(const char *path, const Headers &headers);\n\n    std::shared_ptr<Response> Get(const char *path, Progress progress);\n\n    std::shared_ptr<Response> Get(const char *path, const Headers &headers, Progress progress);\n\n    std::shared_ptr<Response> Get(const char *path, ContentReceiver content_receiver);\n\n    std::shared_ptr<Response> Get(const char *path, const Headers &headers, ContentReceiver content_receiver);\n\n    std::shared_ptr<Response> Get(const char *path, ContentReceiver content_receiver, Progress progress);\n\n    std::shared_ptr<Response> Get(const char *path,\n                                  const Headers &headers,\n                                  ContentReceiver content_receiver,\n                                  Progress progress);\n\n    std::shared_ptr<Response> Get(const char *path,\n                                  const Headers &headers,\n                                  ResponseHandler response_handler,\n                                  ContentReceiver content_receiver);\n\n    std::shared_ptr<Response> Get(const char *path,\n                                  const Headers &headers,\n                                  ResponseHandler response_handler,\n                                  ContentReceiver content_receiver,\n                                  Progress progress);\n\n    bool Upgrade(const char *path, Headers &headers);\n\n    std::shared_ptr<Response> Head(const char *path);\n\n    std::shared_ptr<Response> Head(const char *path, const Headers &headers);\n\n    std::shared_ptr<Response> Post(const char *path);\n\n    std::shared_ptr<Response> Post(const char *path, const std::string &body, const char *content_type);\n\n    std::shared_ptr<Response> Post(const char *path,\n                                   const Headers &headers,\n                                   const std::string &body,\n                                   const char *content_type);\n\n    std::shared_ptr<Response> Post(const char *path,\n                                   size_t content_length,\n                                   ContentProvider content_provider,\n                                   const char *content_type);\n\n    std::shared_ptr<Response> Post(const char *path,\n                                   const Headers &headers,\n                                   size_t content_length,\n                                   ContentProvider content_provider,\n                                   const char *content_type);\n\n    std::shared_ptr<Response> Post(const char *path, const Params &params);\n\n    std::shared_ptr<Response> Post(const char *path, const Headers &headers, const Params &params);\n\n    std::shared_ptr<Response> Post(const char *path, const MultipartFormDataItems &items);\n\n    std::shared_ptr<Response> Post(const char *path, const Headers &headers, const MultipartFormDataItems &items);\n\n    std::shared_ptr<Response> Put(const char *path);\n\n    std::shared_ptr<Response> Put(const char *path, const std::string &body, const char *content_type);\n\n    std::shared_ptr<Response> Put(const char *path,\n                                  const Headers &headers,\n                                  const std::string &body,\n                                  const char *content_type);\n\n    std::shared_ptr<Response> Put(const char *path,\n                                  size_t content_length,\n                                  ContentProvider content_provider,\n                                  const char *content_type);\n\n    std::shared_ptr<Response> Put(const char *path,\n                                  const Headers &headers,\n                                  size_t content_length,\n                                  ContentProvider content_provider,\n                                  const char *content_type);\n\n    std::shared_ptr<Response> Put(const char *path, const Params &params);\n\n    std::shared_ptr<Response> Put(const char *path, const Headers &headers, const Params &params);\n\n    // websocket\n    inline bool Push(const std::string &data, int opcode = swoole::websocket::OPCODE_TEXT) {\n        return Push(data.c_str(), data.length(), swoole::websocket::OPCODE_TEXT);\n    }\n    bool Push(const char *data, size_t length, int opcode = swoole::websocket::OPCODE_TEXT);\n    std::shared_ptr<WebSocketFrame> Recv();\n\n    std::shared_ptr<Response> Patch(const char *path, const std::string &body, const char *content_type);\n\n    std::shared_ptr<Response> Patch(const char *path,\n                                    const Headers &headers,\n                                    const std::string &body,\n                                    const char *content_type);\n\n    std::shared_ptr<Response> Patch(const char *path,\n                                    size_t content_length,\n                                    ContentProvider content_provider,\n                                    const char *content_type);\n\n    std::shared_ptr<Response> Patch(const char *path,\n                                    const Headers &headers,\n                                    size_t content_length,\n                                    ContentProvider content_provider,\n                                    const char *content_type);\n\n    std::shared_ptr<Response> Delete(const char *path);\n\n    std::shared_ptr<Response> Delete(const char *path, const std::string &body, const char *content_type);\n\n    std::shared_ptr<Response> Delete(const char *path, const Headers &headers);\n\n    std::shared_ptr<Response> Delete(const char *path,\n                                     const Headers &headers,\n                                     const std::string &body,\n                                     const char *content_type);\n\n    std::shared_ptr<Response> Options(const char *path);\n\n    std::shared_ptr<Response> Options(const char *path, const Headers &headers);\n\n    bool send(const Request &req, Response &res);\n\n    size_t is_socket_open() const;\n\n    void stop();\n\n    void set_tcp_nodelay(bool on);\n    void set_socket_options(SocketOptions socket_options);\n\n    CPPHTTPLIB_DEPRECATED void set_timeout_sec(time_t timeout_sec);\n    void set_connection_timeout(time_t sec, time_t usec = 0);\n    void set_read_timeout(time_t sec, time_t usec = 0);\n    void set_write_timeout(time_t sec, time_t usec = 0);\n\n    void set_basic_auth(const char *username, const char *password);\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n    void set_digest_auth(const char *username, const char *password);\n#endif\n\n    void set_keep_alive(bool on);\n    void set_follow_location(bool on);\n\n    void set_compress(bool on);\n\n    void set_decompress(bool on);\n\n    void set_interface(const char *intf);\n\n    void set_websocket_mask(bool on);\n\n    void set_proxy(const char *host, int port);\n    void set_proxy_basic_auth(const char *username, const char *password);\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n    void set_proxy_digest_auth(const char *username, const char *password);\n#endif\n\n    void set_logger(Logger logger);\n\n  protected:\n    struct Socket {\n        socket_t sock = INVALID_SOCKET;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n        SSL *ssl = nullptr;\n#endif\n\n        bool is_open() const {\n            return sock != INVALID_SOCKET;\n        }\n    };\n\n    virtual bool create_and_connect_socket(Socket &socket);\n    virtual void close_socket(Socket &socket, bool process_socket_ret);\n\n    bool process_request(Stream &strm, const Request &req, Response &res, bool close_connection);\n\n    // Socket endoint information\n    const std::string host_;\n    const int port_;\n    const std::string host_and_port_;\n\n    // Current open socket\n    Socket socket_;\n    mutable std::mutex socket_mutex_;\n    std::recursive_mutex request_mutex_;\n\n    // Settings\n    std::string client_cert_path_;\n    std::string client_key_path_;\n\n    time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;\n    time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;\n    time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;\n    time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;\n    time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;\n    time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND;\n\n    std::string basic_auth_username_;\n    std::string basic_auth_password_;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n    std::string digest_auth_username_;\n    std::string digest_auth_password_;\n#endif\n\n    bool keep_alive_ = false;\n    bool follow_location_ = false;\n\n    bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;\n    SocketOptions socket_options_ = nullptr;\n\n    bool compress_ = false;\n    bool decompress_ = true;\n\n    std::string interface_;\n\n    std::string proxy_host_;\n    int proxy_port_ = 80;\n\n    bool websocket_mask_ = false;\n\n    std::string proxy_basic_auth_username_;\n    std::string proxy_basic_auth_password_;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n    std::string proxy_digest_auth_username_;\n    std::string proxy_digest_auth_password_;\n#endif\n\n    Logger logger_;\n\n    void copy_settings(const Client &rhs) {\n        client_cert_path_ = rhs.client_cert_path_;\n        client_key_path_ = rhs.client_key_path_;\n        connection_timeout_sec_ = rhs.connection_timeout_sec_;\n        read_timeout_sec_ = rhs.read_timeout_sec_;\n        read_timeout_usec_ = rhs.read_timeout_usec_;\n        write_timeout_sec_ = rhs.write_timeout_sec_;\n        write_timeout_usec_ = rhs.write_timeout_usec_;\n        basic_auth_username_ = rhs.basic_auth_username_;\n        basic_auth_password_ = rhs.basic_auth_password_;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n        digest_auth_username_ = rhs.digest_auth_username_;\n        digest_auth_password_ = rhs.digest_auth_password_;\n#endif\n        keep_alive_ = rhs.keep_alive_;\n        follow_location_ = rhs.follow_location_;\n        tcp_nodelay_ = rhs.tcp_nodelay_;\n        socket_options_ = rhs.socket_options_;\n        compress_ = rhs.compress_;\n        decompress_ = rhs.decompress_;\n        interface_ = rhs.interface_;\n        proxy_host_ = rhs.proxy_host_;\n        proxy_port_ = rhs.proxy_port_;\n        proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;\n        proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n        proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;\n        proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;\n#endif\n        websocket_mask_ = rhs.websocket_mask_;\n        logger_ = rhs.logger_;\n    }\n\n  private:\n    socket_t create_client_socket() const;\n    bool read_response_line(Stream &strm, Response &res);\n    bool write_request(Stream &strm, const Request &req, bool close_connection);\n    bool redirect(const Request &req, Response &res);\n    bool handle_request(Stream &strm, const Request &req, Response &res, bool close_connection);\n\n    std::shared_ptr<Response> send_with_content_provider(const char *method,\n                                                         const char *path,\n                                                         const Headers &headers,\n                                                         const std::string &body,\n                                                         size_t content_length,\n                                                         ContentProvider content_provider,\n                                                         const char *content_type);\n\n    virtual bool process_socket(Socket &socket, std::function<bool(Stream &strm)> callback);\n    virtual bool is_ssl() const;\n};\n\ninline void Get(std::vector<Request> &requests, const char *path, const Headers &headers) {\n    Request req;\n    req.method = \"GET\";\n    req.path = path;\n    req.headers = headers;\n    requests.emplace_back(std::move(req));\n}\n\ninline void Get(std::vector<Request> &requests, const char *path) {\n    Get(requests, path, Headers());\n}\n\ninline void Post(std::vector<Request> &requests,\n                 const char *path,\n                 const Headers &headers,\n                 const std::string &body,\n                 const char *content_type) {\n    Request req;\n    req.method = \"POST\";\n    req.path = path;\n    req.headers = headers;\n    if (content_type) {\n        req.headers.emplace(\"Content-Type\", content_type);\n    }\n    req.body = body;\n    requests.emplace_back(std::move(req));\n}\n\ninline void Post(std::vector<Request> &requests, const char *path, const std::string &body, const char *content_type) {\n    Post(requests, path, Headers(), body, content_type);\n}\n\ninline void Post(std::vector<Request> &requests,\n                 const char *path,\n                 size_t content_length,\n                 ContentProvider content_provider,\n                 const char *content_type) {\n    Request req;\n    req.method = \"POST\";\n    req.headers = Headers();\n    req.path = path;\n    req.content_length = content_length;\n    req.content_provider = content_provider;\n\n    if (content_type) {\n        req.headers.emplace(\"Content-Type\", content_type);\n    }\n\n    requests.emplace_back(std::move(req));\n}\n\nclass Client2 {\n  public:\n    explicit Client2(const char *scheme_host_port) : Client2(scheme_host_port, std::string(), std::string()) {}\n\n    explicit Client2(const char *scheme_host_port,\n                     const std::string &client_cert_path,\n                     const std::string &client_key_path) {\n        const static std::regex re(R\"(^(https?)://([^:/?#]+)(?::(\\d+))?)\");\n\n        std::cmatch m;\n        if (std::regex_match(scheme_host_port, m, re)) {\n            auto scheme = m[1].str();\n            auto host = m[2].str();\n            auto port_str = m[3].str();\n\n            auto port = !port_str.empty() ? std::stoi(port_str) : (scheme == \"https\" ? 443 : 80);\n\n            if (scheme == \"https\") {\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n                is_ssl_ = true;\n                cli_ = std::make_shared<SSLClient>(host.c_str(), port, client_cert_path, client_key_path);\n#endif\n            } else {\n                cli_ = std::make_shared<Client>(host.c_str(), port, client_cert_path, client_key_path);\n            }\n        }\n    }\n\n    ~Client2() {}\n\n    bool is_valid() const {\n        return cli_ != nullptr;\n    }\n\n    std::shared_ptr<Response> Get(const char *path) {\n        return cli_->Get(path);\n    }\n\n    std::shared_ptr<Response> Get(const char *path, const Headers &headers) {\n        return cli_->Get(path, headers);\n    }\n\n    std::shared_ptr<Response> Get(const char *path, Progress progress) {\n        return cli_->Get(path, progress);\n    }\n\n    std::shared_ptr<Response> Get(const char *path, const Headers &headers, Progress progress) {\n        return cli_->Get(path, headers, progress);\n    }\n\n    std::shared_ptr<Response> Get(const char *path, ContentReceiver content_receiver) {\n        return cli_->Get(path, content_receiver);\n    }\n\n    std::shared_ptr<Response> Get(const char *path, const Headers &headers, ContentReceiver content_receiver) {\n        return cli_->Get(path, headers, content_receiver);\n    }\n\n    std::shared_ptr<Response> Get(const char *path, ContentReceiver content_receiver, Progress progress) {\n        return cli_->Get(path, content_receiver, progress);\n    }\n\n    std::shared_ptr<Response> Get(const char *path,\n                                  const Headers &headers,\n                                  ContentReceiver content_receiver,\n                                  Progress progress) {\n        return cli_->Get(path, headers, content_receiver, progress);\n    }\n\n    std::shared_ptr<Response> Get(const char *path,\n                                  const Headers &headers,\n                                  ResponseHandler response_handler,\n                                  ContentReceiver content_receiver) {\n        return cli_->Get(path, headers, response_handler, content_receiver);\n    }\n\n    std::shared_ptr<Response> Get(const char *path,\n                                  const Headers &headers,\n                                  ResponseHandler response_handler,\n                                  ContentReceiver content_receiver,\n                                  Progress progress) {\n        return cli_->Get(path, headers, response_handler, content_receiver, progress);\n    }\n\n    std::shared_ptr<Response> Head(const char *path) {\n        return cli_->Head(path);\n    }\n\n    std::shared_ptr<Response> Head(const char *path, const Headers &headers) {\n        return cli_->Head(path, headers);\n    }\n\n    std::shared_ptr<Response> Post(const char *path) {\n        return cli_->Post(path);\n    }\n\n    std::shared_ptr<Response> Post(const char *path, const std::string &body, const char *content_type) {\n        return cli_->Post(path, body, content_type);\n    }\n\n    std::shared_ptr<Response> Post(const char *path,\n                                   const Headers &headers,\n                                   const std::string &body,\n                                   const char *content_type) {\n        return cli_->Post(path, headers, body, content_type);\n    }\n\n    std::shared_ptr<Response> Post(const char *path,\n                                   size_t content_length,\n                                   ContentProvider content_provider,\n                                   const char *content_type) {\n        return cli_->Post(path, content_length, content_provider, content_type);\n    }\n\n    std::shared_ptr<Response> Post(const char *path,\n                                   const Headers &headers,\n                                   size_t content_length,\n                                   ContentProvider content_provider,\n                                   const char *content_type) {\n        return cli_->Post(path, headers, content_length, content_provider, content_type);\n    }\n\n    std::shared_ptr<Response> Post(const char *path, const Params &params) {\n        return cli_->Post(path, params);\n    }\n\n    std::shared_ptr<Response> Post(const char *path, const Headers &headers, const Params &params) {\n        return cli_->Post(path, headers, params);\n    }\n\n    std::shared_ptr<Response> Post(const char *path, const MultipartFormDataItems &items) {\n        return cli_->Post(path, items);\n    }\n\n    std::shared_ptr<Response> Post(const char *path, const Headers &headers, const MultipartFormDataItems &items) {\n        return cli_->Post(path, headers, items);\n    }\n\n    std::shared_ptr<Response> Put(const char *path) {\n        return cli_->Put(path);\n    }\n\n    std::shared_ptr<Response> Put(const char *path, const std::string &body, const char *content_type) {\n        return cli_->Put(path, body, content_type);\n    }\n\n    std::shared_ptr<Response> Put(const char *path,\n                                  const Headers &headers,\n                                  const std::string &body,\n                                  const char *content_type) {\n        return cli_->Put(path, headers, body, content_type);\n    }\n\n    std::shared_ptr<Response> Put(const char *path,\n                                  size_t content_length,\n                                  ContentProvider content_provider,\n                                  const char *content_type) {\n        return cli_->Put(path, content_length, content_provider, content_type);\n    }\n\n    std::shared_ptr<Response> Put(const char *path,\n                                  const Headers &headers,\n                                  size_t content_length,\n                                  ContentProvider content_provider,\n                                  const char *content_type) {\n        return cli_->Put(path, headers, content_length, content_provider, content_type);\n    }\n\n    std::shared_ptr<Response> Put(const char *path, const Params &params) {\n        return cli_->Put(path, params);\n    }\n\n    std::shared_ptr<Response> Put(const char *path, const Headers &headers, const Params &params) {\n        return cli_->Put(path, headers, params);\n    }\n\n    std::shared_ptr<Response> Patch(const char *path, const std::string &body, const char *content_type) {\n        return cli_->Patch(path, body, content_type);\n    }\n\n    std::shared_ptr<Response> Patch(const char *path,\n                                    const Headers &headers,\n                                    const std::string &body,\n                                    const char *content_type) {\n        return cli_->Patch(path, headers, body, content_type);\n    }\n\n    std::shared_ptr<Response> Patch(const char *path,\n                                    size_t content_length,\n                                    ContentProvider content_provider,\n                                    const char *content_type) {\n        return cli_->Patch(path, content_length, content_provider, content_type);\n    }\n\n    std::shared_ptr<Response> Patch(const char *path,\n                                    const Headers &headers,\n                                    size_t content_length,\n                                    ContentProvider content_provider,\n                                    const char *content_type) {\n        return cli_->Patch(path, headers, content_length, content_provider, content_type);\n    }\n\n    std::shared_ptr<Response> Delete(const char *path) {\n        return cli_->Delete(path);\n    }\n\n    std::shared_ptr<Response> Delete(const char *path, const std::string &body, const char *content_type) {\n        return cli_->Delete(path, body, content_type);\n    }\n\n    std::shared_ptr<Response> Delete(const char *path, const Headers &headers) {\n        return cli_->Delete(path, headers);\n    }\n\n    std::shared_ptr<Response> Delete(const char *path,\n                                     const Headers &headers,\n                                     const std::string &body,\n                                     const char *content_type) {\n        return cli_->Delete(path, headers, body, content_type);\n    }\n\n    std::shared_ptr<Response> Options(const char *path) {\n        return cli_->Options(path);\n    }\n\n    std::shared_ptr<Response> Options(const char *path, const Headers &headers) {\n        return cli_->Options(path, headers);\n    }\n\n    bool send(const Request &req, Response &res) {\n        return cli_->send(req, res);\n    }\n\n    bool is_socket_open() {\n        return cli_->is_socket_open();\n    }\n\n    void stop() {\n        cli_->stop();\n    }\n\n    void set_tcp_nodelay(bool on) {\n        cli_->set_tcp_nodelay(on);\n    }\n\n    void set_socket_options(SocketOptions socket_options) {\n        cli_->set_socket_options(socket_options);\n    }\n\n    Client2 &set_connection_timeout(time_t sec, time_t usec) {\n        cli_->set_connection_timeout(sec, usec);\n        return *this;\n    }\n\n    Client2 &set_read_timeout(time_t sec, time_t usec) {\n        cli_->set_read_timeout(sec, usec);\n        return *this;\n    }\n\n    Client2 &set_basic_auth(const char *username, const char *password) {\n        cli_->set_basic_auth(username, password);\n        return *this;\n    }\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n    Client2 &set_digest_auth(const char *username, const char *password) {\n        cli_->set_digest_auth(username, password);\n        return *this;\n    }\n#endif\n\n    Client2 &set_keep_alive(bool on) {\n        cli_->set_keep_alive(on);\n        return *this;\n    }\n\n    Client2 &set_follow_location(bool on) {\n        cli_->set_follow_location(on);\n        return *this;\n    }\n\n    Client2 &set_compress(bool on) {\n        cli_->set_compress(on);\n        return *this;\n    }\n\n    Client2 &set_decompress(bool on) {\n        cli_->set_decompress(on);\n        return *this;\n    }\n\n    Client2 &set_interface(const char *intf) {\n        cli_->set_interface(intf);\n        return *this;\n    }\n\n    Client2 &set_proxy(const char *host, int port) {\n        cli_->set_proxy(host, port);\n        return *this;\n    }\n\n    Client2 &set_proxy_basic_auth(const char *username, const char *password) {\n        cli_->set_proxy_basic_auth(username, password);\n        return *this;\n    }\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n    Client2 &set_proxy_digest_auth(const char *username, const char *password) {\n        cli_->set_proxy_digest_auth(username, password);\n        return *this;\n    }\n#endif\n\n    Client2 &set_logger(Logger logger) {\n        cli_->set_logger(logger);\n        return *this;\n    }\n\n    // SSL\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n    Client2 &set_ca_cert_path(const char *ca_cert_file_path, const char *ca_cert_dir_path = nullptr) {\n        if (is_ssl_) {\n            static_cast<SSLClient &>(*cli_).set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);\n        }\n        return *this;\n    }\n\n    Client2 &set_ca_cert_store(X509_STORE *ca_cert_store) {\n        if (is_ssl_) {\n            static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);\n        }\n        return *this;\n    }\n\n    Client2 &enable_server_certificate_verification(bool enabled) {\n        if (is_ssl_) {\n            static_cast<SSLClient &>(*cli_).enable_server_certificate_verification(enabled);\n        }\n        return *this;\n    }\n\n    long get_openssl_verify_result() const {\n        if (is_ssl_) {\n            return static_cast<SSLClient &>(*cli_).get_openssl_verify_result();\n        }\n        return -1;  // NOTE: -1 doesn't match any of X509_V_ERR_???\n    }\n\n    SSL_CTX *ssl_context() const {\n        if (is_ssl_) {\n            return static_cast<SSLClient &>(*cli_).ssl_context();\n        }\n        return nullptr;\n    }\n#endif\n\n  private:\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n    bool is_ssl_ = false;\n#endif\n    std::shared_ptr<Client> cli_;\n};\n\n// ----------------------------------------------------------------------------\n\n/*\n * Implementation\n */\n\nnamespace detail {\n\ninline bool is_hex(char c, int &v) {\n    if (0x20 <= c && isdigit(c)) {\n        v = c - '0';\n        return true;\n    } else if ('A' <= c && c <= 'F') {\n        v = c - 'A' + 10;\n        return true;\n    } else if ('a' <= c && c <= 'f') {\n        v = c - 'a' + 10;\n        return true;\n    }\n    return false;\n}\n\ninline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, int &val) {\n    if (i >= s.size()) {\n        return false;\n    }\n\n    val = 0;\n    for (; cnt; i++, cnt--) {\n        if (!s[i]) {\n            return false;\n        }\n        int v = 0;\n        if (is_hex(s[i], v)) {\n            val = val * 16 + v;\n        } else {\n            return false;\n        }\n    }\n    return true;\n}\n\ninline std::string from_i_to_hex(size_t n) {\n    const char *charset = \"0123456789abcdef\";\n    std::string ret;\n    do {\n        ret = charset[n & 15] + ret;\n        n >>= 4;\n    } while (n > 0);\n    return ret;\n}\n\ninline size_t to_utf8(int code, char *buff) {\n    if (code < 0x0080) {\n        buff[0] = (code & 0x7F);\n        return 1;\n    } else if (code < 0x0800) {\n        buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));\n        buff[1] = static_cast<char>(0x80 | (code & 0x3F));\n        return 2;\n    } else if (code < 0xD800) {\n        buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));\n        buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));\n        buff[2] = static_cast<char>(0x80 | (code & 0x3F));\n        return 3;\n    } else if (code < 0xE000) {  // D800 - DFFF is invalid...\n        return 0;\n    } else if (code < 0x10000) {\n        buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));\n        buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));\n        buff[2] = static_cast<char>(0x80 | (code & 0x3F));\n        return 3;\n    } else if (code < 0x110000) {\n        buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));\n        buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));\n        buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));\n        buff[3] = static_cast<char>(0x80 | (code & 0x3F));\n        return 4;\n    }\n\n    // NOTREACHED\n    return 0;\n}\n\n// NOTE: This code came up with the following stackoverflow post:\n// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c\ninline std::string base64_encode(const std::string &in) {\n    static const auto lookup = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\n    std::string out;\n    out.reserve(in.size());\n\n    int val = 0;\n    int valb = -6;\n\n    for (auto c : in) {\n        val = (val << 8) + static_cast<uint8_t>(c);\n        valb += 8;\n        while (valb >= 0) {\n            out.push_back(lookup[(val >> valb) & 0x3F]);\n            valb -= 6;\n        }\n    }\n\n    if (valb > -6) {\n        out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]);\n    }\n\n    while (out.size() % 4) {\n        out.push_back('=');\n    }\n\n    return out;\n}\n\ninline bool is_file(const std::string &path) {\n    struct stat st;\n    return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode);\n}\n\ninline bool is_dir(const std::string &path) {\n    struct stat st;\n    return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode);\n}\n\ninline bool is_valid_path(const std::string &path) {\n    size_t level = 0;\n    size_t i = 0;\n\n    // Skip slash\n    while (i < path.size() && path[i] == '/') {\n        i++;\n    }\n\n    while (i < path.size()) {\n        // Read component\n        auto beg = i;\n        while (i < path.size() && path[i] != '/') {\n            i++;\n        }\n\n        auto len = i - beg;\n        assert(len > 0);\n\n        if (!path.compare(beg, len, \".\")) {\n            ;\n        } else if (!path.compare(beg, len, \"..\")) {\n            if (level == 0) {\n                return false;\n            }\n            level--;\n        } else {\n            level++;\n        }\n\n        // Skip slash\n        while (i < path.size() && path[i] == '/') {\n            i++;\n        }\n    }\n\n    return true;\n}\n\ninline void read_file(const std::string &path, std::string &out) {\n    std::ifstream fs(path, std::ios_base::binary);\n    fs.seekg(0, std::ios_base::end);\n    auto size = fs.tellg();\n    fs.seekg(0);\n    out.resize(static_cast<size_t>(size));\n    fs.read(&out[0], static_cast<std::streamsize>(size));\n}\n\ninline std::string file_extension(const std::string &path) {\n    std::smatch m;\n    static auto re = std::regex(\"\\\\.([a-zA-Z0-9]+)$\");\n    if (std::regex_search(path, m, re)) {\n        return m[1].str();\n    }\n    return std::string();\n}\n\ntemplate <class Fn>\nvoid split(const char *b, const char *e, char d, Fn fn) {\n    int i = 0;\n    int beg = 0;\n\n    while (e ? (b + i != e) : (b[i] != '\\0')) {\n        if (b[i] == d) {\n            fn(&b[beg], &b[i]);\n            beg = i + 1;\n        }\n        i++;\n    }\n\n    if (i) {\n        fn(&b[beg], &b[i]);\n    }\n}\n\n// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`\n// to store data. The call can set memory on stack for performance.\nclass stream_line_reader {\n  public:\n    stream_line_reader(Stream &strm, char *fixed_buffer, size_t fixed_buffer_size)\n        : strm_(strm), fixed_buffer_(fixed_buffer), fixed_buffer_size_(fixed_buffer_size) {}\n\n    const char *ptr() const {\n        if (glowable_buffer_.empty()) {\n            return fixed_buffer_;\n        } else {\n            return glowable_buffer_.data();\n        }\n    }\n\n    size_t size() const {\n        if (glowable_buffer_.empty()) {\n            return fixed_buffer_used_size_;\n        } else {\n            return glowable_buffer_.size();\n        }\n    }\n\n    bool end_with_crlf() const {\n        auto end = ptr() + size();\n        return size() >= 2 && end[-2] == '\\r' && end[-1] == '\\n';\n    }\n\n    bool getline() {\n        fixed_buffer_used_size_ = 0;\n        glowable_buffer_.clear();\n\n        for (size_t i = 0;; i++) {\n            char byte;\n            auto n = strm_.read(&byte, 1);\n\n            if (n < 0) {\n                return false;\n            } else if (n == 0) {\n                if (i == 0) {\n                    return false;\n                } else {\n                    break;\n                }\n            }\n\n            append(byte);\n\n            if (byte == '\\n') {\n                break;\n            }\n        }\n\n        return true;\n    }\n\n  private:\n    void append(char c) {\n        if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {\n            fixed_buffer_[fixed_buffer_used_size_++] = c;\n            fixed_buffer_[fixed_buffer_used_size_] = '\\0';\n        } else {\n            if (glowable_buffer_.empty()) {\n                assert(fixed_buffer_[fixed_buffer_used_size_] == '\\0');\n                glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);\n            }\n            glowable_buffer_ += c;\n        }\n    }\n\n    Stream &strm_;\n    char *fixed_buffer_;\n    const size_t fixed_buffer_size_;\n    size_t fixed_buffer_used_size_ = 0;\n    std::string glowable_buffer_;\n};\n\ninline int close_socket(socket_t sock) {\n#ifdef _WIN32\n    return closesocket(sock);\n#else\n    return close(sock);\n#endif\n}\n\ntemplate <typename T>\ninline ssize_t handle_EINTR(T fn) {\n    ssize_t res = false;\n    while (true) {\n        res = fn();\n        if (res < 0 && errno == EINTR) {\n            continue;\n        }\n        break;\n    }\n    return res;\n}\n\ninline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {\n#ifdef CPPHTTPLIB_USE_POLL\n    struct pollfd pfd_read;\n    pfd_read.fd = sock;\n    pfd_read.events = POLLIN;\n\n    auto timeout = static_cast<int>(sec * 1000 + usec);\n\n    return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });\n#else\n    fd_set fds;\n    FD_ZERO(&fds);\n    FD_SET(sock, &fds);\n\n    timeval tv;\n    tv.tv_sec = static_cast<long>(sec);\n    tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);\n\n    return handle_EINTR([&]() { return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv); });\n#endif\n}\n\ninline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {\n#ifdef CPPHTTPLIB_USE_POLL\n    struct pollfd pfd_read;\n    pfd_read.fd = sock;\n    pfd_read.events = POLLOUT;\n\n    auto timeout = static_cast<int>(sec * 1000 + usec / 1000);\n\n    return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });\n#else\n    fd_set fds;\n    FD_ZERO(&fds);\n    FD_SET(sock, &fds);\n\n    timeval tv;\n    tv.tv_sec = static_cast<long>(sec);\n    tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);\n\n    return handle_EINTR([&]() { return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv); });\n#endif\n}\n\ninline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {\n#ifdef CPPHTTPLIB_USE_POLL\n    struct pollfd pfd_read;\n    pfd_read.fd = sock;\n    pfd_read.events = POLLIN | POLLOUT;\n\n    auto timeout = static_cast<int>(sec * 1000 + usec);\n\n    auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });\n\n    if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {\n        int error = 0;\n        socklen_t len = sizeof(error);\n        auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast<char *>(&error), &len);\n        return res >= 0 && !error;\n    }\n    return false;\n#else\n    fd_set fdsr;\n    FD_ZERO(&fdsr);\n    FD_SET(sock, &fdsr);\n\n    auto fdsw = fdsr;\n    auto fdse = fdsr;\n\n    timeval tv;\n    tv.tv_sec = static_cast<long>(sec);\n    tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);\n\n    auto ret = handle_EINTR([&]() { return select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv); });\n\n    if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {\n        int error = 0;\n        socklen_t len = sizeof(error);\n        return getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast<char *>(&error), &len) >= 0 && !error;\n    }\n    return false;\n#endif\n}\n\nclass SocketStream : public Stream {\n  public:\n    SocketStream(socket_t sock,\n                 time_t read_timeout_sec,\n                 time_t read_timeout_usec,\n                 time_t write_timeout_sec,\n                 time_t write_timeout_usec);\n    ~SocketStream() override;\n\n    bool is_readable() const override;\n    bool is_writable() const override;\n    ssize_t read(char *ptr, size_t size) override;\n    ssize_t write(const char *ptr, size_t size) override;\n    void get_remote_ip_and_port(std::string &ip, int &port) const override;\n\n  private:\n    socket_t sock_;\n    time_t read_timeout_sec_;\n    time_t read_timeout_usec_;\n    time_t write_timeout_sec_;\n    time_t write_timeout_usec_;\n};\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\nclass SSLSocketStream : public Stream {\n  public:\n    SSLSocketStream(socket_t sock,\n                    SSL *ssl,\n                    time_t read_timeout_sec,\n                    time_t read_timeout_usec,\n                    time_t write_timeout_sec,\n                    time_t write_timeout_usec);\n    ~SSLSocketStream() override;\n\n    bool is_readable() const override;\n    bool is_writable() const override;\n    ssize_t read(char *ptr, size_t size) override;\n    ssize_t write(const char *ptr, size_t size) override;\n    void get_remote_ip_and_port(std::string &ip, int &port) const override;\n\n  private:\n    socket_t sock_;\n    SSL *ssl_;\n    time_t read_timeout_sec_;\n    time_t read_timeout_usec_;\n    time_t write_timeout_sec_;\n    time_t write_timeout_usec_;\n};\n#endif\n\nclass BufferStream : public Stream {\n  public:\n    BufferStream() = default;\n    ~BufferStream() override = default;\n\n    bool is_readable() const override;\n    bool is_writable() const override;\n    ssize_t read(char *ptr, size_t size) override;\n    ssize_t write(const char *ptr, size_t size) override;\n    void get_remote_ip_and_port(std::string &ip, int &port) const override;\n\n    const std::string &get_buffer() const;\n\n  private:\n    std::string buffer;\n    size_t position = 0;\n};\n\ninline bool keep_alive(socket_t sock) {\n    using namespace std::chrono;\n    auto start = steady_clock::now();\n    while (true) {\n        auto val = select_read(sock, 0, 10000);\n        if (val < 0) {\n            return false;\n        } else if (val == 0) {\n            auto current = steady_clock::now();\n            auto duration = duration_cast<milliseconds>(current - start);\n            auto timeout = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND * 100 + CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;\n            if (duration.count() > timeout) {\n                return false;\n            }\n            std::this_thread::sleep_for(std::chrono::milliseconds(1));\n        } else {\n            return true;\n        }\n    }\n}\n\ntemplate <typename T>\ninline bool process_client_socket(socket_t sock,\n                                  time_t read_timeout_sec,\n                                  time_t read_timeout_usec,\n                                  time_t write_timeout_sec,\n                                  time_t write_timeout_usec,\n                                  T callback) {\n    SocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec);\n    return callback(strm);\n}\n\ninline int shutdown_socket(socket_t sock) {\n#ifdef _WIN32\n    return shutdown(sock, SD_BOTH);\n#else\n    return shutdown(sock, SHUT_RDWR);\n#endif\n}\n\ntemplate <typename BindOrConnect>\nsocket_t create_socket(const char *host,\n                       int port,\n                       int socket_flags,\n                       bool tcp_nodelay,\n                       SocketOptions socket_options,\n                       BindOrConnect bind_or_connect) {\n    // Get address info\n    struct addrinfo hints;\n    struct addrinfo *result;\n\n    memset(&hints, 0, sizeof(struct addrinfo));\n    hints.ai_family = AF_UNSPEC;\n    hints.ai_socktype = SOCK_STREAM;\n    hints.ai_flags = socket_flags;\n    hints.ai_protocol = 0;\n\n    auto service = std::to_string(port);\n\n    if (getaddrinfo(host, service.c_str(), &hints, &result)) {\n        return INVALID_SOCKET;\n    }\n\n    for (auto rp = result; rp; rp = rp->ai_next) {\n        // Create a socket\n#ifdef _WIN32\n        auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT);\n        /**\n         * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1\n         * and above the socket creation fails on older Windows Systems.\n         *\n         * Let's try to create a socket the old way in this case.\n         *\n         * Reference:\n         * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa\n         *\n         * WSA_FLAG_NO_HANDLE_INHERIT:\n         * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with\n         * SP1, and later\n         *\n         */\n        if (sock == INVALID_SOCKET) {\n            sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n        }\n#else\n        auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n#endif\n        if (sock == INVALID_SOCKET) {\n            continue;\n        }\n\n#ifndef _WIN32\n        if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {\n            continue;\n        }\n#endif\n\n        if (tcp_nodelay) {\n            int yes = 1;\n            setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&yes), sizeof(yes));\n        }\n\n        if (socket_options) {\n            socket_options(sock);\n        }\n\n        if (rp->ai_family == AF_INET6) {\n            int no = 0;\n            setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char *>(&no), sizeof(no));\n        }\n\n        // bind or connect\n        if (bind_or_connect(sock, *rp)) {\n            freeaddrinfo(result);\n            return sock;\n        }\n\n        close_socket(sock);\n    }\n\n    freeaddrinfo(result);\n    return INVALID_SOCKET;\n}\n\ninline void set_nonblocking(socket_t sock, bool nonblocking) {\n#ifdef _WIN32\n    auto flags = nonblocking ? 1UL : 0UL;\n    ioctlsocket(sock, FIONBIO, &flags);\n#else\n    auto flags = fcntl(sock, F_GETFL, 0);\n    fcntl(sock, F_SETFL, nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));\n#endif\n}\n\ninline bool is_connection_error() {\n#ifdef _WIN32\n    return WSAGetLastError() != WSAEWOULDBLOCK;\n#else\n    return errno != EINPROGRESS;\n#endif\n}\n\ninline bool bind_ip_address(socket_t sock, const char *host) {\n    struct addrinfo hints;\n    struct addrinfo *result;\n\n    memset(&hints, 0, sizeof(struct addrinfo));\n    hints.ai_family = AF_UNSPEC;\n    hints.ai_socktype = SOCK_STREAM;\n    hints.ai_protocol = 0;\n\n    if (getaddrinfo(host, \"0\", &hints, &result)) {\n        return false;\n    }\n\n    auto ret = false;\n    for (auto rp = result; rp; rp = rp->ai_next) {\n        const auto &ai = *rp;\n        if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {\n            ret = true;\n            break;\n        }\n    }\n\n    freeaddrinfo(result);\n    return ret;\n}\n\n#ifndef _WIN32\ninline std::string if2ip(const std::string &ifn) {\n    struct ifaddrs *ifap;\n    getifaddrs(&ifap);\n    for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {\n        if (ifa->ifa_addr && ifn == ifa->ifa_name) {\n            if (ifa->ifa_addr->sa_family == AF_INET) {\n                auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);\n                char buf[INET_ADDRSTRLEN];\n                if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {\n                    freeifaddrs(ifap);\n                    return std::string(buf, INET_ADDRSTRLEN);\n                }\n            }\n        }\n    }\n    freeifaddrs(ifap);\n    return std::string();\n}\n#endif\n\ninline socket_t create_client_socket(const char *host,\n                                     int port,\n                                     bool tcp_nodelay,\n                                     SocketOptions socket_options,\n                                     time_t timeout_sec,\n                                     time_t timeout_usec,\n                                     const std::string &intf) {\n    return create_socket(host, port, 0, tcp_nodelay, socket_options, [&](socket_t sock, struct addrinfo &ai) -> bool {\n        if (!intf.empty()) {\n#ifndef _WIN32\n            auto ip = if2ip(intf);\n            if (ip.empty()) {\n                ip = intf;\n            }\n            if (!bind_ip_address(sock, ip.c_str())) {\n                return false;\n            }\n#endif\n        }\n\n        set_nonblocking(sock, true);\n\n        auto ret = ::connect(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));\n        if (ret < 0) {\n            if (is_connection_error() || !wait_until_socket_is_ready(sock, timeout_sec, timeout_usec)) {\n                close_socket(sock);\n                return false;\n            }\n        }\n\n        set_nonblocking(sock, false);\n        return true;\n    });\n}\n\ninline void get_remote_ip_and_port(const struct sockaddr_storage &addr,\n                                   socklen_t addr_len,\n                                   std::string &ip,\n                                   int &port) {\n    if (addr.ss_family == AF_INET) {\n        port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);\n    } else if (addr.ss_family == AF_INET6) {\n        port = ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);\n    }\n\n    std::array<char, NI_MAXHOST> ipstr{};\n    if (!getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr),\n                     addr_len,\n                     ipstr.data(),\n                     static_cast<socklen_t>(ipstr.size()),\n                     nullptr,\n                     0,\n                     NI_NUMERICHOST)) {\n        ip = ipstr.data();\n    }\n}\n\ninline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {\n    struct sockaddr_storage addr;\n    socklen_t addr_len = sizeof(addr);\n\n    if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr), &addr_len)) {\n        get_remote_ip_and_port(addr, addr_len, ip, port);\n    }\n}\n\ninline const char *find_content_type(const std::string &path, const std::map<std::string, std::string> &user_data) {\n    auto ext = file_extension(path);\n\n    auto it = user_data.find(ext);\n    if (it != user_data.end()) {\n        return it->second.c_str();\n    }\n\n    if (ext == \"txt\") {\n        return \"text/plain\";\n    } else if (ext == \"html\" || ext == \"htm\") {\n        return \"text/html\";\n    } else if (ext == \"css\") {\n        return \"text/css\";\n    } else if (ext == \"jpeg\" || ext == \"jpg\") {\n        return \"image/jpg\";\n    } else if (ext == \"png\") {\n        return \"image/png\";\n    } else if (ext == \"gif\") {\n        return \"image/gif\";\n    } else if (ext == \"svg\") {\n        return \"image/svg+xml\";\n    } else if (ext == \"ico\") {\n        return \"image/x-icon\";\n    } else if (ext == \"json\") {\n        return \"application/json\";\n    } else if (ext == \"pdf\") {\n        return \"application/pdf\";\n    } else if (ext == \"js\") {\n        return \"application/javascript\";\n    } else if (ext == \"wasm\") {\n        return \"application/wasm\";\n    } else if (ext == \"xml\") {\n        return \"application/xml\";\n    } else if (ext == \"xhtml\") {\n        return \"application/xhtml+xml\";\n    }\n    return nullptr;\n}\n\ninline const char *status_message(int status) {\n    switch (status) {\n    case 100:\n        return \"Continue\";\n    case 101:\n        return \"Switching Protocol\";\n    case 102:\n        return \"Processing\";\n    case 103:\n        return \"Early Hints\";\n    case 200:\n        return \"OK\";\n    case 201:\n        return \"Created\";\n    case 202:\n        return \"Accepted\";\n    case 203:\n        return \"Non-Authoritative Information\";\n    case 204:\n        return \"No Content\";\n    case 205:\n        return \"Reset Content\";\n    case 206:\n        return \"Partial Content\";\n    case 207:\n        return \"Multi-Status\";\n    case 208:\n        return \"Already Reported\";\n    case 226:\n        return \"IM Used\";\n    case 300:\n        return \"Multiple Choice\";\n    case 301:\n        return \"Moved Permanently\";\n    case 302:\n        return \"Found\";\n    case 303:\n        return \"See Other\";\n    case 304:\n        return \"Not Modified\";\n    case 305:\n        return \"Use Proxy\";\n    case 306:\n        return \"unused\";\n    case 307:\n        return \"Temporary Redirect\";\n    case 308:\n        return \"Permanent Redirect\";\n    case 400:\n        return \"Bad Request\";\n    case 401:\n        return \"Unauthorized\";\n    case 402:\n        return \"Payment Required\";\n    case 403:\n        return \"Forbidden\";\n    case 404:\n        return \"Not Found\";\n    case 405:\n        return \"Method Not Allowed\";\n    case 406:\n        return \"Not Acceptable\";\n    case 407:\n        return \"Proxy Authentication Required\";\n    case 408:\n        return \"Request Timeout\";\n    case 409:\n        return \"Conflict\";\n    case 410:\n        return \"Gone\";\n    case 411:\n        return \"Length Required\";\n    case 412:\n        return \"Precondition Failed\";\n    case 413:\n        return \"Payload Too Large\";\n    case 414:\n        return \"URI Too Long\";\n    case 415:\n        return \"Unsupported Media Type\";\n    case 416:\n        return \"Range Not Satisfiable\";\n    case 417:\n        return \"Expectation Failed\";\n    case 418:\n        return \"I'm a teapot\";\n    case 421:\n        return \"Misdirected Request\";\n    case 422:\n        return \"Unprocessable Entity\";\n    case 423:\n        return \"Locked\";\n    case 424:\n        return \"Failed Dependency\";\n    case 425:\n        return \"Too Early\";\n    case 426:\n        return \"Upgrade Required\";\n    case 428:\n        return \"Precondition Required\";\n    case 429:\n        return \"Too Many Requests\";\n    case 431:\n        return \"Request Header Fields Too Large\";\n    case 451:\n        return \"Unavailable For Legal Reasons\";\n    case 501:\n        return \"Not Implemented\";\n    case 502:\n        return \"Bad Gateway\";\n    case 503:\n        return \"Service Unavailable\";\n    case 504:\n        return \"Gateway Timeout\";\n    case 505:\n        return \"HTTP Version Not Supported\";\n    case 506:\n        return \"Variant Also Negotiates\";\n    case 507:\n        return \"Insufficient Storage\";\n    case 508:\n        return \"Loop Detected\";\n    case 510:\n        return \"Not Extended\";\n    case 511:\n        return \"Network Authentication Required\";\n\n    default:\n    case 500:\n        return \"Internal Server Error\";\n    }\n}\n\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\ninline bool can_compress(const std::string &content_type) {\n    return !content_type.find(\"text/\") || content_type == \"image/svg+xml\" || content_type == \"application/javascript\" ||\n           content_type == \"application/json\" || content_type == \"application/xml\" ||\n           content_type == \"application/xhtml+xml\";\n}\n\ninline bool compress(std::string &content) {\n    z_stream strm;\n    strm.zalloc = Z_NULL;\n    strm.zfree = Z_NULL;\n    strm.opaque = Z_NULL;\n\n    auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);\n    if (ret != Z_OK) {\n        return false;\n    }\n\n    strm.avail_in = static_cast<decltype(strm.avail_in)>(content.size());\n    strm.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(content.data()));\n\n    std::string compressed;\n\n    std::array<char, 16384> buff{};\n    do {\n        strm.avail_out = buff.size();\n        strm.next_out = reinterpret_cast<Bytef *>(buff.data());\n        ret = deflate(&strm, Z_FINISH);\n        assert(ret != Z_STREAM_ERROR);\n        compressed.append(buff.data(), buff.size() - strm.avail_out);\n    } while (strm.avail_out == 0);\n\n    assert(ret == Z_STREAM_END);\n    assert(strm.avail_in == 0);\n\n    content.swap(compressed);\n\n    deflateEnd(&strm);\n    return true;\n}\n\nclass decompressor {\n  public:\n    decompressor() {\n        std::memset(&strm, 0, sizeof(strm));\n        strm.zalloc = Z_NULL;\n        strm.zfree = Z_NULL;\n        strm.opaque = Z_NULL;\n\n        // 15 is the value of wbits, which should be at the maximum possible value\n        // to ensure that any gzip stream can be decoded. The offset of 32 specifies\n        // that the stream type should be automatically detected either gzip or\n        // deflate.\n        is_valid_ = inflateInit2(&strm, 32 + 15) == Z_OK;\n    }\n\n    ~decompressor() {\n        inflateEnd(&strm);\n    }\n\n    bool is_valid() const {\n        return is_valid_;\n    }\n\n    template <typename T>\n    bool decompress(const char *data, size_t data_length, T callback) {\n        int ret = Z_OK;\n\n        strm.avail_in = static_cast<decltype(strm.avail_in)>(data_length);\n        strm.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));\n\n        std::array<char, 16384> buff{};\n        do {\n            strm.avail_out = buff.size();\n            strm.next_out = reinterpret_cast<Bytef *>(buff.data());\n\n            ret = inflate(&strm, Z_NO_FLUSH);\n            assert(ret != Z_STREAM_ERROR);\n            switch (ret) {\n            case Z_NEED_DICT:\n            case Z_DATA_ERROR:\n            case Z_MEM_ERROR:\n                inflateEnd(&strm);\n                return false;\n            }\n\n            if (!callback(buff.data(), buff.size() - strm.avail_out)) {\n                return false;\n            }\n        } while (strm.avail_out == 0);\n\n        return ret == Z_OK || ret == Z_STREAM_END;\n    }\n\n  private:\n    bool is_valid_;\n    z_stream strm;\n};\n#endif\n\ninline bool has_header(const Headers &headers, const char *key) {\n    return headers.find(key) != headers.end();\n}\n\ninline const char *get_header_value(const Headers &headers, const char *key, size_t id = 0, const char *def = nullptr) {\n    auto rng = headers.equal_range(key);\n    auto it = rng.first;\n    std::advance(it, static_cast<ssize_t>(id));\n    if (it != rng.second) {\n        return it->second.c_str();\n    }\n    return def;\n}\n\ninline uint64_t get_header_value_uint64(const Headers &headers, const char *key, uint64_t def = 0) {\n    auto it = headers.find(key);\n    if (it != headers.end()) {\n        return std::strtoull(it->second.data(), nullptr, 10);\n    }\n    return def;\n}\n\ninline void parse_header(const char *beg, const char *end, Headers &headers) {\n    auto p = beg;\n    while (p < end && *p != ':') {\n        p++;\n    }\n    if (p < end) {\n        auto key_end = p;\n        p++;  // skip ':'\n        while (p < end && (*p == ' ' || *p == '\\t')) {\n            p++;\n        }\n        if (p < end) {\n            auto val_begin = p;\n            while (p < end) {\n                p++;\n            }\n            headers.emplace(std::string(beg, key_end), std::string(val_begin, end));\n        }\n    }\n}\n\ninline bool read_headers(Stream &strm, Headers &headers) {\n    const auto bufsiz = 2048;\n    char buf[bufsiz];\n    stream_line_reader line_reader(strm, buf, bufsiz);\n\n    for (;;) {\n        if (!line_reader.getline()) {\n            return false;\n        }\n\n        // Check if the line ends with CRLF.\n        if (line_reader.end_with_crlf()) {\n            // Blank line indicates end of headers.\n            if (line_reader.size() == 2) {\n                break;\n            }\n        } else {\n            continue;  // Skip invalid line.\n        }\n\n        // Skip trailing spaces and tabs.\n        auto end = line_reader.ptr() + line_reader.size() - 2;\n        while (line_reader.ptr() < end && (end[-1] == ' ' || end[-1] == '\\t')) {\n            end--;\n        }\n\n        parse_header(line_reader.ptr(), end, headers);\n    }\n\n    return true;\n}\n\ninline bool read_content_with_length(Stream &strm, uint64_t len, Progress progress, ContentReceiver out) {\n    char buf[CPPHTTPLIB_RECV_BUFSIZ];\n\n    uint64_t r = 0;\n    while (r < len) {\n        auto read_len = static_cast<size_t>(len - r);\n        auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));\n        if (n <= 0) {\n            return false;\n        }\n\n        if (!out(buf, static_cast<size_t>(n))) {\n            return false;\n        }\n\n        r += static_cast<uint64_t>(n);\n\n        if (progress) {\n            if (!progress(r, len)) {\n                return false;\n            }\n        }\n    }\n\n    return true;\n}\n\ninline void skip_content_with_length(Stream &strm, uint64_t len) {\n    char buf[CPPHTTPLIB_RECV_BUFSIZ];\n    uint64_t r = 0;\n    while (r < len) {\n        auto read_len = static_cast<size_t>(len - r);\n        auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));\n        if (n <= 0) {\n            return;\n        }\n        r += static_cast<uint64_t>(n);\n    }\n}\n\ninline bool read_content_without_length(Stream &strm, ContentReceiver out) {\n    char buf[CPPHTTPLIB_RECV_BUFSIZ];\n    for (;;) {\n        auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);\n        if (n < 0) {\n            return false;\n        } else if (n == 0) {\n            return true;\n        }\n        if (!out(buf, static_cast<size_t>(n))) {\n            return false;\n        }\n    }\n\n    return true;\n}\n\ninline bool read_content_chunked(Stream &strm, ContentReceiver out) {\n    const auto bufsiz = 16;\n    char buf[bufsiz];\n\n    stream_line_reader line_reader(strm, buf, bufsiz);\n\n    if (!line_reader.getline()) {\n        return false;\n    }\n\n    unsigned long chunk_len;\n    while (true) {\n        char *end_ptr;\n\n        chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);\n\n        if (end_ptr == line_reader.ptr()) {\n            return false;\n        }\n        if (chunk_len == ULONG_MAX) {\n            return false;\n        }\n\n        if (chunk_len == 0) {\n            break;\n        }\n\n        if (!read_content_with_length(strm, chunk_len, nullptr, out)) {\n            return false;\n        }\n\n        if (!line_reader.getline()) {\n            return false;\n        }\n\n        if (strcmp(line_reader.ptr(), \"\\r\\n\")) {\n            break;\n        }\n\n        if (!line_reader.getline()) {\n            return false;\n        }\n    }\n\n    if (chunk_len == 0) {\n        // Reader terminator after chunks\n        if (!line_reader.getline() || strcmp(line_reader.ptr(), \"\\r\\n\")) return false;\n    }\n\n    return true;\n}\n\ninline bool is_chunked_transfer_encoding(const Headers &headers) {\n    return !strcasecmp(get_header_value(headers, \"Transfer-Encoding\", 0, \"\"), \"chunked\");\n}\n\ntemplate <typename T>\nbool read_content(Stream &strm,\n                  T &x,\n                  size_t payload_max_length,\n                  int &status,\n                  Progress progress,\n                  ContentReceiver receiver,\n                  bool decompress) {\n    ContentReceiver out = [&](const char *buf, size_t n) { return receiver(buf, n); };\n\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n    decompressor decompressor;\n#endif\n\n    if (decompress) {\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n        std::string content_encoding = x.get_header_value(\"Content-Encoding\");\n        if (content_encoding.find(\"gzip\") != std::string::npos ||\n            content_encoding.find(\"deflate\") != std::string::npos) {\n            if (!decompressor.is_valid()) {\n                status = 500;\n                return false;\n            }\n\n            out = [&](const char *buf, size_t n) {\n                return decompressor.decompress(buf, n, [&](const char *buf, size_t n) { return receiver(buf, n); });\n            };\n        }\n#else\n        if (x.get_header_value(\"Content-Encoding\") == \"gzip\") {\n            status = 415;\n            return false;\n        }\n#endif\n    }\n\n    auto ret = true;\n    auto exceed_payload_max_length = false;\n\n    if (is_chunked_transfer_encoding(x.headers)) {\n        ret = read_content_chunked(strm, out);\n    } else if (!has_header(x.headers, \"Content-Length\")) {\n        ret = read_content_without_length(strm, out);\n    } else {\n        auto len = get_header_value_uint64(x.headers, \"Content-Length\", 0);\n        if (len > payload_max_length) {\n            exceed_payload_max_length = true;\n            skip_content_with_length(strm, len);\n            ret = false;\n        } else if (len > 0) {\n            ret = read_content_with_length(strm, len, progress, out);\n        }\n    }\n\n    if (!ret) {\n        status = exceed_payload_max_length ? 413 : 400;\n    }\n    return ret;\n}\n\ntemplate <typename T>\ninline ssize_t write_headers(Stream &strm, const T &info, const Headers &headers) {\n    ssize_t write_len = 0;\n    for (const auto &x : info.headers) {\n        if (x.first == \"EXCEPTION_WHAT\") {\n            continue;\n        }\n        auto len = strm.write_format(\"%s: %s\\r\\n\", x.first.c_str(), x.second.c_str());\n        if (len < 0) {\n            return len;\n        }\n        write_len += len;\n    }\n    for (const auto &x : headers) {\n        auto len = strm.write_format(\"%s: %s\\r\\n\", x.first.c_str(), x.second.c_str());\n        if (len < 0) {\n            return len;\n        }\n        write_len += len;\n    }\n    auto len = strm.write(\"\\r\\n\");\n    if (len < 0) {\n        return len;\n    }\n    write_len += len;\n    return write_len;\n}\n\ninline bool write_data(Stream &strm, const char *d, size_t l) {\n    size_t offset = 0;\n    while (offset < l) {\n        auto length = strm.write(d + offset, l - offset);\n        if (length < 0) {\n            return false;\n        }\n        offset += static_cast<size_t>(length);\n    }\n    return true;\n}\n\ntemplate <typename T>\ninline ssize_t write_content(\n    Stream &strm, ContentProvider content_provider, size_t offset, size_t length, T is_shutting_down) {\n    size_t begin_offset = offset;\n    size_t end_offset = offset + length;\n\n    auto ok = true;\n\n    DataSink data_sink;\n    data_sink.write = [&](const char *d, size_t l) {\n        if (ok) {\n            offset += l;\n            if (!write_data(strm, d, l)) {\n                ok = false;\n            }\n        }\n    };\n    data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };\n\n    while (ok && offset < end_offset && !is_shutting_down()) {\n        if (!content_provider(offset, end_offset - offset, data_sink)) {\n            return -1;\n        }\n        if (!ok) {\n            return -1;\n        }\n    }\n\n    return static_cast<ssize_t>(offset - begin_offset);\n}\n\ntemplate <typename T>\ninline ssize_t write_content_chunked(Stream &strm, ContentProvider content_provider, T is_shutting_down) {\n    size_t offset = 0;\n    auto data_available = true;\n    ssize_t total_written_length = 0;\n\n    auto ok = true;\n\n    DataSink data_sink;\n    data_sink.write = [&](const char *d, size_t l) {\n        if (ok) {\n            data_available = l > 0;\n            offset += l;\n\n            // Emit chunked response header and footer for each chunk\n            auto chunk = from_i_to_hex(l) + \"\\r\\n\" + std::string(d, l) + \"\\r\\n\";\n            if (write_data(strm, chunk.data(), chunk.size())) {\n                total_written_length += chunk.size();\n            } else {\n                ok = false;\n            }\n        }\n    };\n    data_sink.done = [&](void) {\n        data_available = false;\n        if (ok) {\n            static const std::string done_marker(\"0\\r\\n\\r\\n\");\n            if (write_data(strm, done_marker.data(), done_marker.size())) {\n                total_written_length += done_marker.size();\n            } else {\n                ok = false;\n            }\n        }\n    };\n    data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };\n\n    while (data_available && !is_shutting_down()) {\n        if (!content_provider(offset, 0, data_sink)) {\n            return -1;\n        }\n        if (!ok) {\n            return -1;\n        }\n    }\n\n    return total_written_length;\n}\n\ntemplate <typename T>\ninline bool redirect(T &cli, const Request &req, Response &res, const std::string &path) {\n    Request new_req = req;\n    new_req.path = path;\n    new_req.redirect_count -= 1;\n\n    if (res.status == 303 && (req.method != \"GET\" && req.method != \"HEAD\")) {\n        new_req.method = \"GET\";\n        new_req.body.clear();\n        new_req.headers.clear();\n    }\n\n    Response new_res;\n\n    auto ret = cli.send(new_req, new_res);\n    if (ret) {\n        res = new_res;\n    }\n    return ret;\n}\n\ninline std::string encode_url(const std::string &s) {\n    std::string result;\n\n    for (size_t i = 0; s[i]; i++) {\n        switch (s[i]) {\n        case ' ':\n            result += \"%20\";\n            break;\n        case '+':\n            result += \"%2B\";\n            break;\n        case '\\r':\n            result += \"%0D\";\n            break;\n        case '\\n':\n            result += \"%0A\";\n            break;\n        case '\\'':\n            result += \"%27\";\n            break;\n        case ',':\n            result += \"%2C\";\n            break;\n        // case ':': result += \"%3A\"; break; // ok? probably...\n        case ';':\n            result += \"%3B\";\n            break;\n        default:\n            auto c = static_cast<uint8_t>(s[i]);\n            if (c >= 0x80) {\n                result += '%';\n                char hex[4];\n                auto len = snprintf(hex, sizeof(hex) - 1, \"%02X\", c);\n                assert(len == 2);\n                result.append(hex, static_cast<size_t>(len));\n            } else {\n                result += s[i];\n            }\n            break;\n        }\n    }\n\n    return result;\n}\n\ninline std::string decode_url(const std::string &s, bool convert_plus_to_space) {\n    std::string result;\n\n    for (size_t i = 0; i < s.size(); i++) {\n        if (s[i] == '%' && i + 1 < s.size()) {\n            if (s[i + 1] == 'u') {\n                int val = 0;\n                if (from_hex_to_i(s, i + 2, 4, val)) {\n                    // 4 digits Unicode codes\n                    char buff[4];\n                    size_t len = to_utf8(val, buff);\n                    if (len > 0) {\n                        result.append(buff, len);\n                    }\n                    i += 5;  // 'u0000'\n                } else {\n                    result += s[i];\n                }\n            } else {\n                int val = 0;\n                if (from_hex_to_i(s, i + 1, 2, val)) {\n                    // 2 digits hex codes\n                    result += static_cast<char>(val);\n                    i += 2;  // '00'\n                } else {\n                    result += s[i];\n                }\n            }\n        } else if (convert_plus_to_space && s[i] == '+') {\n            result += ' ';\n        } else {\n            result += s[i];\n        }\n    }\n\n    return result;\n}\n\ninline std::string params_to_query_str(const Params &params) {\n    std::string query;\n\n    for (auto it = params.begin(); it != params.end(); ++it) {\n        if (it != params.begin()) {\n            query += \"&\";\n        }\n        query += it->first;\n        query += \"=\";\n        query += detail::encode_url(it->second);\n    }\n\n    return query;\n}\n\ninline void parse_query_text(const std::string &s, Params &params) {\n    split(&s[0], &s[s.size()], '&', [&](const char *b, const char *e) {\n        std::string key;\n        std::string val;\n        split(b, e, '=', [&](const char *b2, const char *e2) {\n            if (key.empty()) {\n                key.assign(b2, e2);\n            } else {\n                val.assign(b2, e2);\n            }\n        });\n        params.emplace(decode_url(key, true), decode_url(val, true));\n    });\n}\n\ninline bool parse_multipart_boundary(const std::string &content_type, std::string &boundary) {\n    auto pos = content_type.find(\"boundary=\");\n    if (pos == std::string::npos) {\n        return false;\n    }\n    boundary = content_type.substr(pos + 9);\n    if (boundary.length() >= 2 && boundary.front() == '\"' && boundary.back() == '\"') {\n        boundary = boundary.substr(1, boundary.size() - 2);\n    }\n    return !boundary.empty();\n}\n\ninline bool parse_range_header(const std::string &s, Ranges &ranges) {\n    static auto re_first_range = std::regex(R\"(bytes=(\\d*-\\d*(?:,\\s*\\d*-\\d*)*))\");\n    std::smatch m;\n    if (std::regex_match(s, m, re_first_range)) {\n        auto pos = static_cast<size_t>(m.position(1));\n        auto len = static_cast<size_t>(m.length(1));\n        bool all_valid_ranges = true;\n        split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {\n            if (!all_valid_ranges) return;\n            static auto re_another_range = std::regex(R\"(\\s*(\\d*)-(\\d*))\");\n            std::cmatch cm;\n            if (std::regex_match(b, e, cm, re_another_range)) {\n                ssize_t first = -1;\n                if (!cm.str(1).empty()) {\n                    first = static_cast<ssize_t>(std::stoll(cm.str(1)));\n                }\n\n                ssize_t last = -1;\n                if (!cm.str(2).empty()) {\n                    last = static_cast<ssize_t>(std::stoll(cm.str(2)));\n                }\n\n                if (first != -1 && last != -1 && first > last) {\n                    all_valid_ranges = false;\n                    return;\n                }\n                ranges.emplace_back(std::make_pair(first, last));\n            }\n        });\n        return all_valid_ranges;\n    }\n    return false;\n}\n\nclass MultipartFormDataParser {\n  public:\n    MultipartFormDataParser() = default;\n\n    void set_boundary(std::string &&boundary) {\n        boundary_ = boundary;\n    }\n\n    bool is_valid() const {\n        return is_valid_;\n    }\n\n    template <typename T, typename U>\n    bool parse(const char *buf, size_t n, T content_callback, U header_callback) {\n        static const std::regex re_content_type(R\"(^Content-Type:\\s*(.*?)\\s*$)\", std::regex_constants::icase);\n\n        static const std::regex re_content_disposition(\n            \"^Content-Disposition:\\\\s*form-data;\\\\s*name=\\\"(.*?)\\\"(?:;\\\\s*filename=\"\n            \"\\\"(.*?)\\\")?\\\\s*$\",\n            std::regex_constants::icase);\n        static const std::string dash_ = \"--\";\n        static const std::string crlf_ = \"\\r\\n\";\n\n        buf_.append(buf, n);  // TODO: performance improvement\n\n        while (!buf_.empty()) {\n            switch (state_) {\n            case 0: {  // Initial boundary\n                auto pattern = dash_ + boundary_ + crlf_;\n                if (pattern.size() > buf_.size()) {\n                    return true;\n                }\n                auto pos = buf_.find(pattern);\n                if (pos != 0) {\n                    return false;\n                }\n                buf_.erase(0, pattern.size());\n                off_ += pattern.size();\n                state_ = 1;\n                break;\n            }\n            case 1: {  // New entry\n                clear_file_info();\n                state_ = 2;\n                break;\n            }\n            case 2: {  // Headers\n                auto pos = buf_.find(crlf_);\n                while (pos != std::string::npos) {\n                    // Empty line\n                    if (pos == 0) {\n                        if (!header_callback(file_)) {\n                            is_valid_ = false;\n                            return false;\n                        }\n                        buf_.erase(0, crlf_.size());\n                        off_ += crlf_.size();\n                        state_ = 3;\n                        break;\n                    }\n\n                    auto header = buf_.substr(0, pos);\n                    {\n                        std::smatch m;\n                        if (std::regex_match(header, m, re_content_type)) {\n                            file_.content_type = m[1];\n                        } else if (std::regex_match(header, m, re_content_disposition)) {\n                            file_.name = m[1];\n                            file_.filename = m[2];\n                        }\n                    }\n\n                    buf_.erase(0, pos + crlf_.size());\n                    off_ += pos + crlf_.size();\n                    pos = buf_.find(crlf_);\n                }\n                if (state_ != 3) {\n                    return true;\n                }\n                break;\n            }\n            case 3: {  // Body\n                {\n                    auto pattern = crlf_ + dash_;\n                    if (pattern.size() > buf_.size()) {\n                        return true;\n                    }\n\n                    auto pos = buf_.find(pattern);\n                    if (pos == std::string::npos) {\n                        pos = buf_.size();\n                        while (pos > 0) {\n                            auto c = buf_[pos - 1];\n                            if (c != '\\r' && c != '\\n' && c != '-') {\n                                break;\n                            }\n                            pos--;\n                        }\n                    }\n\n                    if (!content_callback(buf_.data(), pos)) {\n                        is_valid_ = false;\n                        return false;\n                    }\n\n                    off_ += pos;\n                    buf_.erase(0, pos);\n                }\n\n                {\n                    auto pattern = crlf_ + dash_ + boundary_;\n                    if (pattern.size() > buf_.size()) {\n                        return true;\n                    }\n\n                    auto pos = buf_.find(pattern);\n                    if (pos != std::string::npos) {\n                        if (!content_callback(buf_.data(), pos)) {\n                            is_valid_ = false;\n                            return false;\n                        }\n\n                        off_ += pos + pattern.size();\n                        buf_.erase(0, pos + pattern.size());\n                        state_ = 4;\n                    } else {\n                        if (!content_callback(buf_.data(), pattern.size())) {\n                            is_valid_ = false;\n                            return false;\n                        }\n\n                        off_ += pattern.size();\n                        buf_.erase(0, pattern.size());\n                    }\n                }\n                break;\n            }\n            case 4: {  // Boundary\n                if (crlf_.size() > buf_.size()) {\n                    return true;\n                }\n                if (buf_.find(crlf_) == 0) {\n                    buf_.erase(0, crlf_.size());\n                    off_ += crlf_.size();\n                    state_ = 1;\n                } else {\n                    auto pattern = dash_ + crlf_;\n                    if (pattern.size() > buf_.size()) {\n                        return true;\n                    }\n                    if (buf_.find(pattern) == 0) {\n                        buf_.erase(0, pattern.size());\n                        off_ += pattern.size();\n                        is_valid_ = true;\n                        state_ = 5;\n                    } else {\n                        return true;\n                    }\n                }\n                break;\n            }\n            case 5: {  // Done\n                is_valid_ = false;\n                return false;\n            }\n            }\n        }\n\n        return true;\n    }\n\n  private:\n    void clear_file_info() {\n        file_.name.clear();\n        file_.filename.clear();\n        file_.content_type.clear();\n    }\n\n    std::string boundary_;\n\n    std::string buf_;\n    size_t state_ = 0;\n    bool is_valid_ = false;\n    size_t off_ = 0;\n    MultipartFormData file_;\n};\n\ninline std::string to_lower(const char *beg, const char *end) {\n    std::string out;\n    auto it = beg;\n    while (it != end) {\n        out += static_cast<char>(::tolower(*it));\n        it++;\n    }\n    return out;\n}\n\ninline std::string make_multipart_data_boundary() {\n    static const char data[] = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n\n    std::random_device seed_gen;\n    std::mt19937 engine(seed_gen());\n\n    std::string result = \"--cpp-httplib-multipart-data-\";\n\n    for (auto i = 0; i < 16; i++) {\n        result += data[engine() % (sizeof(data) - 1)];\n    }\n\n    return result;\n}\n\ninline std::pair<size_t, size_t> get_range_offset_and_length(const Request &req, size_t content_length, size_t index) {\n    auto r = req.ranges[index];\n\n    if (r.first == -1 && r.second == -1) {\n        return std::make_pair(0, content_length);\n    }\n\n    auto slen = static_cast<ssize_t>(content_length);\n\n    if (r.first == -1) {\n        r.first = slen - r.second;\n        r.second = slen - 1;\n    }\n\n    if (r.second == -1) {\n        r.second = slen - 1;\n    }\n\n    return std::make_pair(r.first, r.second - r.first + 1);\n}\n\ninline std::string make_content_range_header_field(size_t offset, size_t length, size_t content_length) {\n    std::string field = \"bytes \";\n    field += std::to_string(offset);\n    field += \"-\";\n    field += std::to_string(offset + length - 1);\n    field += \"/\";\n    field += std::to_string(content_length);\n    return field;\n}\n\ntemplate <typename SToken, typename CToken, typename Content>\nbool process_multipart_ranges_data(const Request &req,\n                                   Response &res,\n                                   const std::string &boundary,\n                                   const std::string &content_type,\n                                   SToken stoken,\n                                   CToken ctoken,\n                                   Content content) {\n    for (size_t i = 0; i < req.ranges.size(); i++) {\n        ctoken(\"--\");\n        stoken(boundary);\n        ctoken(\"\\r\\n\");\n        if (!content_type.empty()) {\n            ctoken(\"Content-Type: \");\n            stoken(content_type);\n            ctoken(\"\\r\\n\");\n        }\n\n        auto offsets = get_range_offset_and_length(req, res.body.size(), i);\n        auto offset = offsets.first;\n        auto length = offsets.second;\n\n        ctoken(\"Content-Range: \");\n        stoken(make_content_range_header_field(offset, length, res.body.size()));\n        ctoken(\"\\r\\n\");\n        ctoken(\"\\r\\n\");\n        if (!content(offset, length)) {\n            return false;\n        }\n        ctoken(\"\\r\\n\");\n    }\n\n    ctoken(\"--\");\n    stoken(boundary);\n    ctoken(\"--\\r\\n\");\n\n    return true;\n}\n\ninline std::string make_multipart_ranges_data(const Request &req,\n                                              Response &res,\n                                              const std::string &boundary,\n                                              const std::string &content_type) {\n    std::string data;\n\n    process_multipart_ranges_data(\n        req,\n        res,\n        boundary,\n        content_type,\n        [&](const std::string &token) { data += token; },\n        [&](const char *token) { data += token; },\n        [&](size_t offset, size_t length) {\n            data += res.body.substr(offset, length);\n            return true;\n        });\n\n    return data;\n}\n\ninline size_t get_multipart_ranges_data_length(const Request &req,\n                                               Response &res,\n                                               const std::string &boundary,\n                                               const std::string &content_type) {\n    size_t data_length = 0;\n\n    process_multipart_ranges_data(\n        req,\n        res,\n        boundary,\n        content_type,\n        [&](const std::string &token) { data_length += token.size(); },\n        [&](const char *token) { data_length += strlen(token); },\n        [&](size_t /*offset*/, size_t length) {\n            data_length += length;\n            return true;\n        });\n\n    return data_length;\n}\n\ntemplate <typename T>\ninline bool write_multipart_ranges_data(Stream &strm,\n                                        const Request &req,\n                                        Response &res,\n                                        const std::string &boundary,\n                                        const std::string &content_type,\n                                        T is_shutting_down) {\n    return process_multipart_ranges_data(\n        req,\n        res,\n        boundary,\n        content_type,\n        [&](const std::string &token) { strm.write(token); },\n        [&](const char *token) { strm.write(token); },\n        [&](size_t offset, size_t length) {\n            return write_content(strm, res.content_provider_, offset, length, is_shutting_down) >= 0;\n        });\n}\n\ninline std::pair<size_t, size_t> get_range_offset_and_length(const Request &req, const Response &res, size_t index) {\n    auto r = req.ranges[index];\n\n    if (r.second == -1) {\n        r.second = static_cast<ssize_t>(res.content_length_) - 1;\n    }\n\n    return std::make_pair(r.first, r.second - r.first + 1);\n}\n\ninline bool expect_content(const Request &req) {\n    if (req.method == \"POST\" || req.method == \"PUT\" || req.method == \"PATCH\" || req.method == \"PRI\" ||\n        (req.method == \"DELETE\" && req.has_header(\"Content-Length\"))) {\n        return true;\n    }\n    // TODO: check if Content-Length is set\n    return false;\n}\n\ninline bool has_crlf(const char *s) {\n    auto p = s;\n    while (*p) {\n        if (*p == '\\r' || *p == '\\n') {\n            return true;\n        }\n        p++;\n    }\n    return false;\n}\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\ntemplate <typename CTX, typename Init, typename Update, typename Final>\ninline std::string message_digest(const std::string &s, Init init, Update update, Final final, size_t digest_length) {\n    using namespace std;\n\n    std::vector<unsigned char> md(digest_length, 0);\n    CTX ctx;\n    init(&ctx);\n    update(&ctx, s.data(), s.size());\n    final(md.data(), &ctx);\n\n    stringstream ss;\n    for (auto c : md) {\n        ss << setfill('0') << setw(2) << hex << (unsigned int) c;\n    }\n    return ss.str();\n}\n\ninline std::string MD5(const std::string &s) {\n    return message_digest<MD5_CTX>(s, MD5_Init, MD5_Update, MD5_Final, MD5_DIGEST_LENGTH);\n}\n\ninline std::string SHA_256(const std::string &s) {\n    return message_digest<SHA256_CTX>(s, SHA256_Init, SHA256_Update, SHA256_Final, SHA256_DIGEST_LENGTH);\n}\n\ninline std::string SHA_512(const std::string &s) {\n    return message_digest<SHA512_CTX>(s, SHA512_Init, SHA512_Update, SHA512_Final, SHA512_DIGEST_LENGTH);\n}\n#endif\n\n#ifdef _WIN32\nclass WSInit {\n  public:\n    WSInit() {\n        WSADATA wsaData;\n        WSAStartup(0x0002, &wsaData);\n    }\n\n    ~WSInit() {\n        WSACleanup();\n    }\n};\n\nstatic WSInit wsinit_;\n#endif\n\n}  // namespace detail\n\n// Header utilities\ninline std::pair<std::string, std::string> make_range_header(Ranges ranges) {\n    std::string field = \"bytes=\";\n    auto i = 0;\n    for (auto r : ranges) {\n        if (i != 0) {\n            field += \", \";\n        }\n        if (r.first != -1) {\n            field += std::to_string(r.first);\n        }\n        field += '-';\n        if (r.second != -1) {\n            field += std::to_string(r.second);\n        }\n        i++;\n    }\n    return std::make_pair(\"Range\", field);\n}\n\ninline std::pair<std::string, std::string> make_basic_authentication_header(const std::string &username,\n                                                                            const std::string &password,\n                                                                            bool is_proxy = false) {\n    auto field = \"Basic \" + detail::base64_encode(username + \":\" + password);\n    auto key = is_proxy ? \"Proxy-Authorization\" : \"Authorization\";\n    return std::make_pair(key, field);\n}\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\ninline std::pair<std::string, std::string> make_digest_authentication_header(\n    const Request &req,\n    const std::map<std::string, std::string> &auth,\n    size_t cnonce_count,\n    const std::string &cnonce,\n    const std::string &username,\n    const std::string &password,\n    bool is_proxy = false) {\n    using namespace std;\n\n    string nc;\n    {\n        stringstream ss;\n        ss << setfill('0') << setw(8) << hex << cnonce_count;\n        nc = ss.str();\n    }\n\n    auto qop = auth.at(\"qop\");\n    if (qop.find(\"auth-int\") != std::string::npos) {\n        qop = \"auth-int\";\n    } else {\n        qop = \"auth\";\n    }\n\n    std::string algo = \"MD5\";\n    if (auth.find(\"algorithm\") != auth.end()) {\n        algo = auth.at(\"algorithm\");\n    }\n\n    string response;\n    {\n        auto H = algo == \"SHA-256\" ? detail::SHA_256 : algo == \"SHA-512\" ? detail::SHA_512 : detail::MD5;\n\n        auto A1 = username + \":\" + auth.at(\"realm\") + \":\" + password;\n\n        auto A2 = req.method + \":\" + req.path;\n        if (qop == \"auth-int\") {\n            A2 += \":\" + H(req.body);\n        }\n\n        response = H(H(A1) + \":\" + auth.at(\"nonce\") + \":\" + nc + \":\" + cnonce + \":\" + qop + \":\" + H(A2));\n    }\n\n    auto field = \"Digest username=\\\"\" + username + \"\\\", realm=\\\"\" + auth.at(\"realm\") + \"\\\", nonce=\\\"\" +\n                 auth.at(\"nonce\") + \"\\\", uri=\\\"\" + req.path + \"\\\", algorithm=\" + algo + \", qop=\" + qop + \", nc=\\\"\" +\n                 nc + \"\\\", cnonce=\\\"\" + cnonce + \"\\\", response=\\\"\" + response + \"\\\"\";\n\n    auto key = is_proxy ? \"Proxy-Authorization\" : \"Authorization\";\n    return std::make_pair(key, field);\n}\n#endif\n\ninline bool parse_www_authenticate(const Response &res, std::map<std::string, std::string> &auth, bool is_proxy) {\n    auto auth_key = is_proxy ? \"Proxy-Authenticate\" : \"WWW-Authenticate\";\n    if (res.has_header(auth_key)) {\n        static auto re = std::regex(R\"~((?:(?:,\\s*)?(.+?)=(?:\"(.*?)\"|([^,]*))))~\");\n        auto s = res.get_header_value(auth_key);\n        auto pos = s.find(' ');\n        if (pos != std::string::npos) {\n            auto type = s.substr(0, pos);\n            if (type == \"Basic\") {\n                return false;\n            } else if (type == \"Digest\") {\n                s = s.substr(pos + 1);\n                auto beg = std::sregex_iterator(s.begin(), s.end(), re);\n                for (auto i = beg; i != std::sregex_iterator(); ++i) {\n                    auto m = *i;\n                    auto key = s.substr(static_cast<size_t>(m.position(1)), static_cast<size_t>(m.length(1)));\n                    auto val = m.length(2) > 0\n                                   ? s.substr(static_cast<size_t>(m.position(2)), static_cast<size_t>(m.length(2)))\n                                   : s.substr(static_cast<size_t>(m.position(3)), static_cast<size_t>(m.length(3)));\n                    auth[key] = val;\n                }\n                return true;\n            }\n        }\n    }\n    return false;\n}\n\n// https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240\ninline std::string random_string(size_t length) {\n    auto randchar = []() -> char {\n        const char charset[] = \"0123456789\"\n                               \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n                               \"abcdefghijklmnopqrstuvwxyz\";\n        const size_t max_index = (sizeof(charset) - 1);\n        return charset[static_cast<size_t>(rand()) % max_index];\n    };\n    std::string str(length, 0);\n    std::generate_n(str.begin(), length, randchar);\n    return str;\n}\n\n// Request implementation\ninline bool Request::has_header(const char *key) const {\n    return detail::has_header(headers, key);\n}\n\ninline std::string Request::get_header_value(const char *key, size_t id) const {\n    return detail::get_header_value(headers, key, id, \"\");\n}\n\ninline size_t Request::get_header_value_count(const char *key) const {\n    auto r = headers.equal_range(key);\n    return static_cast<size_t>(std::distance(r.first, r.second));\n}\n\ninline void Request::set_header(const char *key, const char *val) {\n    if (!detail::has_crlf(key) && !detail::has_crlf(val)) {\n        headers.emplace(key, val);\n    }\n}\n\ninline void Request::set_header(const char *key, const std::string &val) {\n    if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) {\n        headers.emplace(key, val);\n    }\n}\n\ninline bool Request::has_param(const char *key) const {\n    return params.find(key) != params.end();\n}\n\ninline std::string Request::get_param_value(const char *key, size_t id) const {\n    auto rng = params.equal_range(key);\n    auto it = rng.first;\n    std::advance(it, static_cast<ssize_t>(id));\n    if (it != rng.second) {\n        return it->second;\n    }\n    return std::string();\n}\n\ninline size_t Request::get_param_value_count(const char *key) const {\n    auto r = params.equal_range(key);\n    return static_cast<size_t>(std::distance(r.first, r.second));\n}\n\ninline bool Request::is_multipart_form_data() const {\n    const auto &content_type = get_header_value(\"Content-Type\");\n    return !content_type.find(\"multipart/form-data\");\n}\n\ninline bool Request::has_file(const char *key) const {\n    return files.find(key) != files.end();\n}\n\ninline MultipartFormData Request::get_file_value(const char *key) const {\n    auto it = files.find(key);\n    if (it != files.end()) {\n        return it->second;\n    }\n    return MultipartFormData();\n}\n\n// Response implementation\ninline bool Response::has_header(const char *key) const {\n    return headers.find(key) != headers.end();\n}\n\ninline std::string Response::get_header_value(const char *key, size_t id) const {\n    return detail::get_header_value(headers, key, id, \"\");\n}\n\ninline size_t Response::get_header_value_count(const char *key) const {\n    auto r = headers.equal_range(key);\n    return static_cast<size_t>(std::distance(r.first, r.second));\n}\n\ninline void Response::set_header(const char *key, const char *val) {\n    if (!detail::has_crlf(key) && !detail::has_crlf(val)) {\n        headers.emplace(key, val);\n    }\n}\n\ninline void Response::set_header(const char *key, const std::string &val) {\n    if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) {\n        headers.emplace(key, val);\n    }\n}\n\ninline void Response::set_redirect(const char *url, int stat) {\n    if (!detail::has_crlf(url)) {\n        set_header(\"Location\", url);\n        if (300 <= stat && stat < 400) {\n            this->status = stat;\n        } else {\n            this->status = 302;\n        }\n    }\n}\n\ninline void Response::set_content(const char *s, size_t n, const char *content_type) {\n    body.assign(s, n);\n    set_header(\"Content-Type\", content_type);\n}\n\ninline void Response::set_content(std::string s, const char *content_type) {\n    body = std::move(s);\n    set_header(\"Content-Type\", content_type);\n}\n\ninline void Response::set_content_provider(size_t in_length,\n                                           ContentProvider provider,\n                                           std::function<void()> resource_releaser) {\n    assert(in_length > 0);\n    content_length_ = in_length;\n    content_provider_ = [provider](size_t offset, size_t length, DataSink &sink) {\n        return provider(offset, length, sink);\n    };\n    content_provider_resource_releaser_ = resource_releaser;\n}\n\ninline void Response::set_chunked_content_provider(ChunkedContentProvider provider,\n                                                   std::function<void()> resource_releaser) {\n    content_length_ = 0;\n    content_provider_ = [provider](size_t offset, size_t, DataSink &sink) { return provider(offset, sink); };\n    content_provider_resource_releaser_ = resource_releaser;\n}\n\n// Rstream implementation\ninline ssize_t Stream::write(const char *ptr) {\n    return write(ptr, strlen(ptr));\n}\n\ninline ssize_t Stream::write(const std::string &s) {\n    return write(s.data(), s.size());\n}\n\ntemplate <typename... Args>\ninline ssize_t Stream::write_format(const char *fmt, const Args &...args) {\n    std::array<char, 2048> buf{};\n\n#if defined(_MSC_VER) && _MSC_VER < 1900\n    auto sn = _snprintf_s(buf, bufsiz, buf.size() - 1, fmt, args...);\n#else\n    auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...);\n#endif\n    if (sn <= 0) {\n        return sn;\n    }\n\n    auto n = static_cast<size_t>(sn);\n\n    if (n >= buf.size() - 1) {\n        std::vector<char> glowable_buf(buf.size());\n\n        while (n >= glowable_buf.size() - 1) {\n            glowable_buf.resize(glowable_buf.size() * 2);\n#if defined(_MSC_VER) && _MSC_VER < 1900\n            n = static_cast<size_t>(\n                _snprintf_s(&glowable_buf[0], glowable_buf.size(), glowable_buf.size() - 1, fmt, args...));\n#else\n            n = static_cast<size_t>(snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...));\n#endif\n        }\n        return write(&glowable_buf[0], n);\n    } else {\n        return write(buf.data(), n);\n    }\n}\n\nnamespace detail {\n\n// Socket stream implementation\ninline SocketStream::SocketStream(socket_t sock,\n                                  time_t read_timeout_sec,\n                                  time_t read_timeout_usec,\n                                  time_t write_timeout_sec,\n                                  time_t write_timeout_usec)\n    : sock_(sock),\n      read_timeout_sec_(read_timeout_sec),\n      read_timeout_usec_(read_timeout_usec),\n      write_timeout_sec_(write_timeout_sec),\n      write_timeout_usec_(write_timeout_usec) {}\n\ninline SocketStream::~SocketStream() {}\n\ninline bool SocketStream::is_readable() const {\n    return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;\n}\n\ninline bool SocketStream::is_writable() const {\n    return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0;\n}\n\ninline ssize_t SocketStream::read(char *ptr, size_t size) {\n    if (!is_readable()) {\n        return -1;\n    }\n\n#ifdef _WIN32\n    if (size > static_cast<size_t>((std::numeric_limits<int>::max)())) {\n        return -1;\n    }\n    return recv(sock_, ptr, static_cast<int>(size), 0);\n#else\n    return handle_EINTR([&]() { return recv(sock_, ptr, size, 0); });\n#endif\n}\n\ninline ssize_t SocketStream::write(const char *ptr, size_t size) {\n    if (!is_writable()) {\n        return -1;\n    }\n\n#ifdef _WIN32\n    if (size > static_cast<size_t>((std::numeric_limits<int>::max)())) {\n        return -1;\n    }\n    return send(sock_, ptr, static_cast<int>(size), 0);\n#else\n    return handle_EINTR([&]() { return send(sock_, ptr, size, 0); });\n#endif\n}\n\ninline void SocketStream::get_remote_ip_and_port(std::string &ip, int &port) const {\n    return detail::get_remote_ip_and_port(sock_, ip, port);\n}\n\n// Buffer stream implementation\ninline bool BufferStream::is_readable() const {\n    return true;\n}\n\ninline bool BufferStream::is_writable() const {\n    return true;\n}\n\ninline ssize_t BufferStream::read(char *ptr, size_t size) {\n#if defined(_MSC_VER) && _MSC_VER < 1900\n    auto len_read = buffer._Copy_s(ptr, size, size, position);\n#else\n    auto len_read = buffer.copy(ptr, size, position);\n#endif\n    position += static_cast<size_t>(len_read);\n    return static_cast<ssize_t>(len_read);\n}\n\ninline ssize_t BufferStream::write(const char *ptr, size_t size) {\n    buffer.append(ptr, size);\n    return static_cast<ssize_t>(size);\n}\n\ninline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/, int & /*port*/) const {}\n\ninline const std::string &BufferStream::get_buffer() const {\n    return buffer;\n}\n\n}  // namespace detail\n\n/*\n * SSL Implementation\n */\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\nnamespace detail {\n\ntemplate <typename U, typename V>\ninline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex, U SSL_connect_or_accept, V setup) {\n    SSL *ssl = nullptr;\n    {\n        std::lock_guard<std::mutex> guard(ctx_mutex);\n        ssl = SSL_new(ctx);\n    }\n\n    if (ssl) {\n        auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);\n        SSL_set_bio(ssl, bio, bio);\n\n        if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {\n            SSL_shutdown(ssl);\n            {\n                std::lock_guard<std::mutex> guard(ctx_mutex);\n                SSL_free(ssl);\n            }\n            return nullptr;\n        }\n    }\n\n    return ssl;\n}\n\ninline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, bool process_socket_ret) {\n    if (process_socket_ret) {\n        SSL_shutdown(ssl);  // shutdown only if not already closed by remote\n    }\n\n    std::lock_guard<std::mutex> guard(ctx_mutex);\n    SSL_free(ssl);\n}\n\ntemplate <typename T>\ninline bool process_client_socket_ssl(SSL *ssl,\n                                      socket_t sock,\n                                      time_t read_timeout_sec,\n                                      time_t read_timeout_usec,\n                                      time_t write_timeout_sec,\n                                      time_t write_timeout_usec,\n                                      T callback) {\n    SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec);\n    return callback(strm);\n}\n\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\nstatic std::shared_ptr<std::vector<std::mutex>> openSSL_locks_;\n\nclass SSLThreadLocks {\n  public:\n    SSLThreadLocks() {\n        openSSL_locks_ = std::make_shared<std::vector<std::mutex>>(CRYPTO_num_locks());\n        CRYPTO_set_locking_callback(locking_callback);\n    }\n\n    ~SSLThreadLocks() {\n        CRYPTO_set_locking_callback(nullptr);\n    }\n\n  private:\n    static void locking_callback(int mode, int type, const char * /*file*/, int /*line*/) {\n        auto &lk = (*openSSL_locks_)[static_cast<size_t>(type)];\n        if (mode & CRYPTO_LOCK) {\n            lk.lock();\n        } else {\n            lk.unlock();\n        }\n    }\n};\n\n#endif\n\nclass SSLInit {\n  public:\n    SSLInit() {\n#if OPENSSL_VERSION_NUMBER < 0x1010001fL\n        SSL_load_error_strings();\n        SSL_library_init();\n#else\n        OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);\n#endif\n    }\n\n    ~SSLInit() {\n#if OPENSSL_VERSION_NUMBER < 0x1010001fL\n        ERR_free_strings();\n#endif\n    }\n\n  private:\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\n    SSLThreadLocks thread_init_;\n#endif\n};\n\n// SSL socket stream implementation\ninline SSLSocketStream::SSLSocketStream(socket_t sock,\n                                        SSL *ssl,\n                                        time_t read_timeout_sec,\n                                        time_t read_timeout_usec,\n                                        time_t write_timeout_sec,\n                                        time_t write_timeout_usec)\n    : sock_(sock),\n      ssl_(ssl),\n      read_timeout_sec_(read_timeout_sec),\n      read_timeout_usec_(read_timeout_usec),\n      write_timeout_sec_(write_timeout_sec),\n      write_timeout_usec_(write_timeout_usec) {}\n\ninline SSLSocketStream::~SSLSocketStream() {}\n\ninline bool SSLSocketStream::is_readable() const {\n    return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;\n}\n\ninline bool SSLSocketStream::is_writable() const {\n    return detail::select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0;\n}\n\ninline ssize_t SSLSocketStream::read(char *ptr, size_t size) {\n    if (SSL_pending(ssl_) > 0 || is_readable()) {\n        return SSL_read(ssl_, ptr, static_cast<int>(size));\n    }\n    return -1;\n}\n\ninline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {\n    if (is_writable()) {\n        return SSL_write(ssl_, ptr, static_cast<int>(size));\n    }\n    return -1;\n}\n\ninline void SSLSocketStream::get_remote_ip_and_port(std::string &ip, int &port) const {\n    detail::get_remote_ip_and_port(sock_, ip, port);\n}\n\nstatic SSLInit sslinit_;\n\n}  // namespace detail\n\nclass SSLClient : public Client {\n  public:\n    explicit SSLClient(const std::string &host);\n\n    explicit SSLClient(const std::string &host, int port);\n\n    explicit SSLClient(const std::string &host,\n                       int port,\n                       const std::string &client_cert_path,\n                       const std::string &client_key_path);\n\n    explicit SSLClient(const std::string &host, int port, X509 *client_cert, EVP_PKEY *client_key);\n\n    ~SSLClient() override;\n\n    bool is_valid() const override;\n\n    void set_ca_cert_path(const char *ca_cert_file_path, const char *ca_cert_dir_path = nullptr);\n\n    void set_ca_cert_store(X509_STORE *ca_cert_store);\n\n    void enable_server_certificate_verification(bool enabled);\n\n    long get_openssl_verify_result() const;\n\n    SSL_CTX *ssl_context() const;\n\n  private:\n    bool create_and_connect_socket(Socket &socket) override;\n    void close_socket(Socket &socket, bool process_socket_ret) override;\n\n    bool process_socket(Socket &socket, std::function<bool(Stream &strm)> callback) override;\n    bool is_ssl() const override;\n\n    bool connect_with_proxy(Socket &sock, Response &res, bool &success);\n    bool initialize_ssl(Socket &socket);\n\n    bool verify_host(X509 *server_cert) const;\n    bool verify_host_with_subject_alt_name(X509 *server_cert) const;\n    bool verify_host_with_common_name(X509 *server_cert) const;\n    bool check_host_name(const char *pattern, size_t pattern_len) const;\n\n    SSL_CTX *ctx_;\n    std::mutex ctx_mutex_;\n    std::vector<std::string> host_components_;\n\n    std::string ca_cert_file_path_;\n    std::string ca_cert_dir_path_;\n    X509_STORE *ca_cert_store_ = nullptr;\n    bool server_certificate_verification_ = false;\n    long verify_result_ = 0;\n\n    friend class Client;\n};\n\n// SSL HTTP client implementation\ninline SSLClient::SSLClient(const std::string &host) : SSLClient(host, 443, std::string(), std::string()) {}\n\ninline SSLClient::SSLClient(const std::string &host, int port) : SSLClient(host, port, std::string(), std::string()) {}\n\ninline SSLClient::SSLClient(const std::string &host,\n                            int port,\n                            const std::string &client_cert_path,\n                            const std::string &client_key_path)\n    : Client(host, port, client_cert_path, client_key_path) {\n    ctx_ = SSL_CTX_new(SSLv23_client_method());\n\n    detail::split(&host_[0], &host_[host_.size()], '.', [&](const char *b, const char *e) {\n        host_components_.emplace_back(std::string(b, e));\n    });\n    if (!client_cert_path.empty() && !client_key_path.empty()) {\n        if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(), SSL_FILETYPE_PEM) != 1 ||\n            SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(), SSL_FILETYPE_PEM) != 1) {\n            SSL_CTX_free(ctx_);\n            ctx_ = nullptr;\n        }\n    }\n}\n\ninline SSLClient::SSLClient(const std::string &host, int port, X509 *client_cert, EVP_PKEY *client_key)\n    : Client(host, port) {\n    ctx_ = SSL_CTX_new(SSLv23_client_method());\n\n    detail::split(&host_[0], &host_[host_.size()], '.', [&](const char *b, const char *e) {\n        host_components_.emplace_back(std::string(b, e));\n    });\n    if (client_cert != nullptr && client_key != nullptr) {\n        if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 || SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {\n            SSL_CTX_free(ctx_);\n            ctx_ = nullptr;\n        }\n    }\n}\n\ninline SSLClient::~SSLClient() {\n    if (ctx_) {\n        SSL_CTX_free(ctx_);\n    }\n}\n\ninline bool SSLClient::is_valid() const {\n    return ctx_;\n}\n\ninline void SSLClient::set_ca_cert_path(const char *ca_cert_file_path, const char *ca_cert_dir_path) {\n    if (ca_cert_file_path) {\n        ca_cert_file_path_ = ca_cert_file_path;\n    }\n    if (ca_cert_dir_path) {\n        ca_cert_dir_path_ = ca_cert_dir_path;\n    }\n}\n\ninline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {\n    if (ca_cert_store) {\n        ca_cert_store_ = ca_cert_store;\n    }\n}\n\ninline void SSLClient::enable_server_certificate_verification(bool enabled) {\n    server_certificate_verification_ = enabled;\n}\n\ninline long SSLClient::get_openssl_verify_result() const {\n    return verify_result_;\n}\n\ninline SSL_CTX *SSLClient::ssl_context() const {\n    return ctx_;\n}\n\ninline bool SSLClient::create_and_connect_socket(Socket &socket) {\n    return is_valid() && Client::create_and_connect_socket(socket);\n}\n\ninline bool SSLClient::connect_with_proxy(Socket &socket, Response &res, bool &success) {\n    success = true;\n    Response res2;\n\n    if (!detail::process_client_socket(socket.sock,\n                                       read_timeout_sec_,\n                                       read_timeout_usec_,\n                                       write_timeout_sec_,\n                                       write_timeout_usec_,\n                                       [&](Stream &strm) {\n                                           Request req2;\n                                           req2.method = \"CONNECT\";\n                                           req2.path = host_and_port_;\n                                           return process_request(strm, req2, res2, false);\n                                       })) {\n        close_socket(socket, true);\n        success = false;\n        return false;\n    }\n\n    if (res2.status == 407) {\n        if (!proxy_digest_auth_username_.empty() && !proxy_digest_auth_password_.empty()) {\n            std::map<std::string, std::string> auth;\n            if (parse_www_authenticate(res2, auth, true)) {\n                Response res3;\n                if (!detail::process_client_socket(socket.sock,\n                                                   read_timeout_sec_,\n                                                   read_timeout_usec_,\n                                                   write_timeout_sec_,\n                                                   write_timeout_usec_,\n                                                   [&](Stream &strm) {\n                                                       Request req3;\n                                                       req3.method = \"CONNECT\";\n                                                       req3.path = host_and_port_;\n                                                       req3.headers.insert(make_digest_authentication_header(\n                                                           req3,\n                                                           auth,\n                                                           1,\n                                                           random_string(10),\n                                                           proxy_digest_auth_username_,\n                                                           proxy_digest_auth_password_,\n                                                           true));\n                                                       return process_request(strm, req3, res3, false);\n                                                   })) {\n                    close_socket(socket, true);\n                    success = false;\n                    return false;\n                }\n            }\n        } else {\n            res = res2;\n            return false;\n        }\n    }\n\n    return true;\n}\n\ninline bool SSLClient::initialize_ssl(Socket &socket) {\n    auto ssl = detail::ssl_new(\n        socket.sock,\n        ctx_,\n        ctx_mutex_,\n        [&](SSL *ssl) {\n            if (ca_cert_file_path_.empty() && ca_cert_store_ == nullptr) {\n                SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr);\n            } else if (!ca_cert_file_path_.empty()) {\n                if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(), nullptr)) {\n                    return false;\n                }\n                SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr);\n            } else if (ca_cert_store_ != nullptr) {\n                if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store_) {\n                    SSL_CTX_set_cert_store(ctx_, ca_cert_store_);\n                }\n                SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr);\n            }\n\n            if (SSL_connect(ssl) != 1) {\n                return false;\n            }\n\n            if (server_certificate_verification_) {\n                verify_result_ = SSL_get_verify_result(ssl);\n\n                if (verify_result_ != X509_V_OK) {\n                    return false;\n                }\n\n                auto server_cert = SSL_get_peer_certificate(ssl);\n\n                if (server_cert == nullptr) {\n                    return false;\n                }\n\n                if (!verify_host(server_cert)) {\n                    X509_free(server_cert);\n                    return false;\n                }\n                X509_free(server_cert);\n            }\n\n            return true;\n        },\n        [&](SSL *ssl) {\n            SSL_set_tlsext_host_name(ssl, host_.c_str());\n            return true;\n        });\n\n    if (ssl) {\n        socket.ssl = ssl;\n        return true;\n    }\n\n    close_socket(socket, false);\n    return false;\n}\n\ninline void SSLClient::close_socket(Socket &socket, bool process_socket_ret) {\n    detail::close_socket(socket.sock);\n    socket_.sock = INVALID_SOCKET;\n    if (socket.ssl) {\n        detail::ssl_delete(ctx_mutex_, socket.ssl, process_socket_ret);\n        socket_.ssl = nullptr;\n    }\n}\n\ninline bool SSLClient::process_socket(Socket &socket, std::function<bool(Stream &strm)> callback) {\n    assert(socket.ssl);\n    return detail::process_client_socket_ssl(socket.ssl,\n                                             socket.sock,\n                                             read_timeout_sec_,\n                                             read_timeout_usec_,\n                                             write_timeout_sec_,\n                                             write_timeout_usec_,\n                                             callback);\n}\n\ninline bool SSLClient::is_ssl() const {\n    return true;\n}\n\ninline bool SSLClient::verify_host(X509 *server_cert) const {\n    /* Quote from RFC2818 section 3.1 \"Server Identity\"\n\n       If a subjectAltName extension of type dNSName is present, that MUST\n       be used as the identity. Otherwise, the (most specific) Common Name\n       field in the Subject field of the certificate MUST be used. Although\n       the use of the Common Name is existing practice, it is deprecated and\n       Certification Authorities are encouraged to use the dNSName instead.\n\n       Matching is performed using the matching rules specified by\n       [RFC2459].  If more than one identity of a given type is present in\n       the certificate (e.g., more than one dNSName name, a match in any one\n       of the set is considered acceptable.) Names may contain the wildcard\n       character * which is considered to match any single domain name\n       component or component fragment. E.g., *.a.com matches foo.a.com but\n       not bar.foo.a.com. f*.com matches foo.com but not bar.com.\n\n       In some cases, the URI is specified as an IP address rather than a\n       hostname. In this case, the iPAddress subjectAltName must be present\n       in the certificate and must exactly match the IP in the URI.\n\n    */\n    return verify_host_with_subject_alt_name(server_cert) || verify_host_with_common_name(server_cert);\n}\n\ninline bool SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {\n    auto ret = false;\n\n    auto type = GEN_DNS;\n\n    struct in6_addr addr6;\n    struct in_addr addr;\n    size_t addr_len = 0;\n\n#ifndef __MINGW32__\n    if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {\n        type = GEN_IPADD;\n        addr_len = sizeof(struct in6_addr);\n    } else if (inet_pton(AF_INET, host_.c_str(), &addr)) {\n        type = GEN_IPADD;\n        addr_len = sizeof(struct in_addr);\n    }\n#endif\n\n    auto alt_names = static_cast<const struct stack_st_GENERAL_NAME *>(\n        X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr));\n\n    if (alt_names) {\n        auto dsn_matched = false;\n        auto ip_mached = false;\n\n        auto count = sk_GENERAL_NAME_num(alt_names);\n\n        for (decltype(count) i = 0; i < count && !dsn_matched; i++) {\n            auto val = sk_GENERAL_NAME_value(alt_names, i);\n            if (val->type == type) {\n                auto name = (const char *) ASN1_STRING_get0_data(val->d.ia5);\n                auto name_len = (size_t) ASN1_STRING_length(val->d.ia5);\n\n                if (strlen(name) == name_len) {\n                    switch (type) {\n                    case GEN_DNS:\n                        dsn_matched = check_host_name(name, name_len);\n                        break;\n\n                    case GEN_IPADD:\n                        if (!memcmp(&addr6, name, addr_len) || !memcmp(&addr, name, addr_len)) {\n                            ip_mached = true;\n                        }\n                        break;\n                    }\n                }\n            }\n        }\n\n        if (dsn_matched || ip_mached) {\n            ret = true;\n        }\n    }\n\n    GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *) alt_names);\n    return ret;\n}\n\ninline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {\n    const auto subject_name = X509_get_subject_name(server_cert);\n\n    if (subject_name != nullptr) {\n        char name[BUFSIZ];\n        auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, name, sizeof(name));\n\n        if (name_len != -1) {\n            return check_host_name(name, static_cast<size_t>(name_len));\n        }\n    }\n\n    return false;\n}\n\ninline bool SSLClient::check_host_name(const char *pattern, size_t pattern_len) const {\n    if (host_.size() == pattern_len && host_ == pattern) {\n        return true;\n    }\n\n    // Wildcard match\n    // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484\n    std::vector<std::string> pattern_components;\n    detail::split(&pattern[0], &pattern[pattern_len], '.', [&](const char *b, const char *e) {\n        pattern_components.emplace_back(std::string(b, e));\n    });\n\n    if (host_components_.size() != pattern_components.size()) {\n        return false;\n    }\n\n    auto itr = pattern_components.begin();\n    for (const auto &h : host_components_) {\n        auto &p = *itr;\n        if (p != h && p != \"*\") {\n            auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' && !p.compare(0, p.size() - 1, h));\n            if (!partial_match) {\n                return false;\n            }\n        }\n        ++itr;\n    }\n\n    return true;\n}\n#endif\n\n// ----------------------------------------------------------------------------\n\n}  // namespace httplib\n"
  },
  {
    "path": "core-tests/include/httplib_server.h",
    "content": "/**\n * httplib.h\n *\n * Copyright (c) 2020 Yuji Hirose. All rights reserved.\n * MIT License\n * GitHub: https://github.com/yhirose/cpp-httplib\n */\n\n#pragma once\n\n#include \"swoole_coroutine_system.h\"\n#include \"swoole_socket_impl.h\"\n#include \"httplib_client.h\"\n\nusing swoole::Coroutine;\nusing swoole::coroutine::System;\nusing swoole::network::Address;\n\nnamespace httplib {\n\nclass ContentReader {\n  public:\n    using Reader = std::function<bool(ContentReceiver receiver)>;\n    using MultipartReader = std::function<bool(MultipartContentHeader header, ContentReceiver receiver)>;\n\n    ContentReader(Reader reader, MultipartReader multipart_reader)\n        : reader_(reader), multipart_reader_(multipart_reader) {}\n\n    bool operator()(MultipartContentHeader header, ContentReceiver receiver) const {\n        return multipart_reader_(header, receiver);\n    }\n\n    bool operator()(ContentReceiver receiver) const {\n        return reader_(receiver);\n    }\n\n    Reader reader_;\n    MultipartReader multipart_reader_;\n};\n\nclass Server {\n  public:\n    using Handler = std::function<void(const Request &, Response &)>;\n    using HandlerWithContentReader =\n        std::function<void(const Request &, Response &, const ContentReader &content_reader)>;\n    using Expect100ContinueHandler = std::function<int(const Request &, Response &)>;\n\n    Server();\n\n    virtual ~Server();\n\n    virtual bool is_valid() const;\n\n    Server &Get(const char *pattern, Handler handler);\n    Server &Post(const char *pattern, Handler handler);\n    Server &Post(const char *pattern, HandlerWithContentReader handler);\n    Server &Put(const char *pattern, Handler handler);\n    Server &Put(const char *pattern, HandlerWithContentReader handler);\n    Server &Patch(const char *pattern, Handler handler);\n    Server &Patch(const char *pattern, HandlerWithContentReader handler);\n    Server &Delete(const char *pattern, Handler handler);\n    Server &Delete(const char *pattern, HandlerWithContentReader handler);\n    Server &Options(const char *pattern, Handler handler);\n\n    CPPHTTPLIB_DEPRECATED bool set_base_dir(const char *dir, const char *mount_point = nullptr);\n    bool set_mount_point(const char *mount_point, const char *dir);\n    bool remove_mount_point(const char *mount_point);\n    void set_file_extension_and_mimetype_mapping(const char *ext, const char *mime);\n    void set_file_request_handler(Handler handler);\n\n    void set_error_handler(Handler handler);\n    void set_expect_100_continue_handler(Expect100ContinueHandler handler);\n    void set_logger(Logger logger);\n\n    void set_tcp_nodelay(bool on);\n    void set_socket_options(SocketOptions socket_options);\n\n    void set_keep_alive_max_count(size_t count);\n    void set_read_timeout(time_t sec, time_t usec = 0);\n    void set_write_timeout(time_t sec, time_t usec = 0);\n    void set_idle_interval(time_t sec, time_t usec = 0);\n\n    void set_payload_max_length(size_t length);\n\n    bool bind_to_port(const char *host, int port, int socket_flags = 0);\n    int bind_to_any_port(const char *host, int socket_flags = 0);\n    bool listen_after_bind();\n\n    bool listen(const char *host, int port, int socket_flags = 0);\n\n    inline void BeforeListen(std::function<void(void)> fn) {\n        before_listen_callback_ = fn;\n    }\n\n    bool is_running() const;\n    void stop();\n\n  protected:\n    bool process_request(Stream &strm,\n                         bool close_connection,\n                         bool &connection_closed,\n                         const std::function<void(Request &)> &setup_request);\n\n    SocketImpl *svr_sock_;\n    size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;\n    time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;\n    time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;\n    time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;\n    time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND;\n    time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;\n    time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;\n    size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;\n\n    std::function<void(void)> before_listen_callback_ = nullptr;\n\n  private:\n    using Handlers = std::vector<std::pair<std::regex, Handler>>;\n    using HandlersForContentReader = std::vector<std::pair<std::regex, HandlerWithContentReader>>;\n\n    SocketImpl *create_server_socket(const char *host, int port, int socket_flags, SocketOptions socket_options) const;\n    int bind_internal(const char *host, int port, int socket_flags);\n    bool listen_internal();\n\n    bool routing(Request &req, Response &res, Stream &strm);\n    bool handle_file_request(Request &req, Response &res, bool head = false);\n    bool dispatch_request(Request &req, Response &res, Handlers &handlers);\n    bool dispatch_request_for_content_reader(Request &req,\n                                             Response &res,\n                                             ContentReader content_reader,\n                                             HandlersForContentReader &handlers);\n\n    bool parse_request_line(const char *s, Request &req);\n    bool write_response(Stream &strm, bool close_connection, const Request &req, Response &res);\n    bool write_content_with_provider(\n        Stream &strm, const Request &req, Response &res, const std::string &boundary, const std::string &content_type);\n    bool read_content(Stream &strm, Request &req, Response &res);\n    bool read_content_with_content_receiver(Stream &strm,\n                                            Request &req,\n                                            Response &res,\n                                            ContentReceiver receiver,\n                                            MultipartContentHeader multipart_header,\n                                            ContentReceiver multipart_receiver);\n    bool read_content_core(Stream &strm,\n                           Request &req,\n                           Response &res,\n                           ContentReceiver receiver,\n                           MultipartContentHeader mulitpart_header,\n                           ContentReceiver multipart_receiver);\n\n    virtual bool process_and_close_socket(SocketImpl *sock);\n\n    std::atomic<bool> is_running_;\n    std::vector<std::pair<std::string, std::string>> base_dirs_;\n    std::map<std::string, std::string> file_extension_and_mimetype_map_;\n    Handler file_request_handler_;\n    Handlers get_handlers_;\n    Handlers post_handlers_;\n    HandlersForContentReader post_handlers_for_content_reader_;\n    Handlers put_handlers_;\n    HandlersForContentReader put_handlers_for_content_reader_;\n    Handlers patch_handlers_;\n    HandlersForContentReader patch_handlers_for_content_reader_;\n    Handlers delete_handlers_;\n    HandlersForContentReader delete_handlers_for_content_reader_;\n    Handlers options_handlers_;\n    Handler error_handler_;\n    Logger logger_;\n    Expect100ContinueHandler expect_100_continue_handler_;\n\n    bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;\n    SocketOptions socket_options_ = default_socket_options;\n};\n\n// HTTP server implementation\ninline Server::Server() : svr_sock_(nullptr), is_running_(false) {\n#ifndef _WIN32\n    signal(SIGPIPE, SIG_IGN);\n#endif\n}\n\ninline Server::~Server() {\n    if (svr_sock_) {\n        delete svr_sock_;\n    }\n}\n\ninline Server &Server::Get(const char *pattern, Handler handler) {\n    get_handlers_.push_back(std::make_pair(std::regex(pattern), handler));\n    return *this;\n}\n\ninline Server &Server::Post(const char *pattern, Handler handler) {\n    post_handlers_.push_back(std::make_pair(std::regex(pattern), handler));\n    return *this;\n}\n\ninline Server &Server::Post(const char *pattern, HandlerWithContentReader handler) {\n    post_handlers_for_content_reader_.push_back(std::make_pair(std::regex(pattern), handler));\n    return *this;\n}\n\ninline Server &Server::Put(const char *pattern, Handler handler) {\n    put_handlers_.push_back(std::make_pair(std::regex(pattern), handler));\n    return *this;\n}\n\ninline Server &Server::Put(const char *pattern, HandlerWithContentReader handler) {\n    put_handlers_for_content_reader_.push_back(std::make_pair(std::regex(pattern), handler));\n    return *this;\n}\n\ninline Server &Server::Patch(const char *pattern, Handler handler) {\n    patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler));\n    return *this;\n}\n\ninline Server &Server::Patch(const char *pattern, HandlerWithContentReader handler) {\n    patch_handlers_for_content_reader_.push_back(std::make_pair(std::regex(pattern), handler));\n    return *this;\n}\n\ninline Server &Server::Delete(const char *pattern, Handler handler) {\n    delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler));\n    return *this;\n}\n\ninline Server &Server::Delete(const char *pattern, HandlerWithContentReader handler) {\n    delete_handlers_for_content_reader_.push_back(std::make_pair(std::regex(pattern), handler));\n    return *this;\n}\n\ninline Server &Server::Options(const char *pattern, Handler handler) {\n    options_handlers_.push_back(std::make_pair(std::regex(pattern), handler));\n    return *this;\n}\n\ninline bool Server::set_base_dir(const char *dir, const char *mount_point) {\n    return set_mount_point(mount_point, dir);\n}\n\ninline bool Server::set_mount_point(const char *mount_point, const char *dir) {\n    if (detail::is_dir(dir)) {\n        std::string mnt = mount_point ? mount_point : \"/\";\n        if (!mnt.empty() && mnt[0] == '/') {\n            base_dirs_.emplace_back(mnt, dir);\n            return true;\n        }\n    }\n    return false;\n}\n\ninline bool Server::remove_mount_point(const char *mount_point) {\n    for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {\n        if (it->first == mount_point) {\n            base_dirs_.erase(it);\n            return true;\n        }\n    }\n    return false;\n}\n\ninline void Server::set_file_extension_and_mimetype_mapping(const char *ext, const char *mime) {\n    file_extension_and_mimetype_map_[ext] = mime;\n}\n\ninline void Server::set_file_request_handler(Handler handler) {\n    file_request_handler_ = std::move(handler);\n}\n\ninline void Server::set_error_handler(Handler handler) {\n    error_handler_ = std::move(handler);\n}\n\ninline void Server::set_tcp_nodelay(bool on) {\n    tcp_nodelay_ = on;\n}\n\ninline void Server::set_socket_options(SocketOptions socket_options) {\n    socket_options_ = socket_options;\n}\n\ninline void Server::set_logger(Logger logger) {\n    logger_ = std::move(logger);\n}\n\ninline void Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {\n    expect_100_continue_handler_ = std::move(handler);\n}\n\ninline void Server::set_keep_alive_max_count(size_t count) {\n    keep_alive_max_count_ = count;\n}\n\ninline void Server::set_read_timeout(time_t sec, time_t usec) {\n    read_timeout_sec_ = sec;\n    read_timeout_usec_ = usec;\n}\n\ninline void Server::set_write_timeout(time_t sec, time_t usec) {\n    write_timeout_sec_ = sec;\n    write_timeout_usec_ = usec;\n}\n\ninline void Server::set_idle_interval(time_t sec, time_t usec) {\n    idle_interval_sec_ = sec;\n    idle_interval_usec_ = usec;\n}\n\ninline void Server::set_payload_max_length(size_t length) {\n    payload_max_length_ = length;\n}\n\ninline bool Server::bind_to_port(const char *host, int port, int socket_flags) {\n    if (bind_internal(host, port, socket_flags) < 0) return false;\n    return true;\n}\ninline int Server::bind_to_any_port(const char *host, int socket_flags) {\n    return bind_internal(host, 0, socket_flags);\n}\n\ninline bool Server::listen_after_bind() {\n    return listen_internal();\n}\n\ninline bool Server::listen(const char *host, int port, int socket_flags) {\n    return bind_to_port(host, port, socket_flags) && listen_internal();\n}\n\ninline bool Server::is_running() const {\n    return is_running_;\n}\n\ninline void Server::stop() {\n    if (is_running_) {\n        is_running_ = false;\n        svr_sock_->cancel(SW_EVENT_READ);\n    }\n}\n\ninline bool Server::parse_request_line(const char *s, Request &req) {\n    const static std::regex re(\"(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) \"\n                               \"(([^?]+)(?:\\\\?(.*?))?) (HTTP/1\\\\.[01])\\r\\n\");\n\n    std::cmatch m;\n    if (std::regex_match(s, m, re)) {\n        req.version = std::string(m[5]);\n        req.method = std::string(m[1]);\n        req.target = std::string(m[2]);\n        req.path = detail::decode_url(m[3], false);\n\n        // Parse query text\n        auto len = std::distance(m[4].first, m[4].second);\n        if (len > 0) {\n            detail::parse_query_text(m[4], req.params);\n        }\n\n        return true;\n    }\n\n    return false;\n}\n\ninline bool Server::write_response(Stream &strm, bool close_connection, const Request &req, Response &res) {\n    assert(res.status != -1);\n\n    if (400 <= res.status && error_handler_) {\n        error_handler_(req, res);\n    }\n\n    detail::BufferStream bstrm;\n\n    // Response line\n    if (!bstrm.write_format(\"HTTP/1.1 %d %s\\r\\n\", res.status, detail::status_message(res.status))) {\n        return false;\n    }\n\n    // Headers\n    if (close_connection || req.get_header_value(\"Connection\") == \"close\") {\n        res.set_header(\"Connection\", \"close\");\n    }\n\n    if (!close_connection && req.get_header_value(\"Connection\") == \"Keep-Alive\") {\n        res.set_header(\"Connection\", \"Keep-Alive\");\n    }\n\n    if (!res.has_header(\"Content-Type\") && (!res.body.empty() || res.content_length_ > 0)) {\n        res.set_header(\"Content-Type\", \"text/plain\");\n    }\n\n    if (!res.has_header(\"Accept-Ranges\") && req.method == \"HEAD\") {\n        res.set_header(\"Accept-Ranges\", \"bytes\");\n    }\n\n    std::string content_type;\n    std::string boundary;\n\n    if (req.ranges.size() > 1) {\n        boundary = detail::make_multipart_data_boundary();\n\n        auto it = res.headers.find(\"Content-Type\");\n        if (it != res.headers.end()) {\n            content_type = it->second;\n            res.headers.erase(it);\n        }\n\n        res.headers.emplace(\"Content-Type\", \"multipart/byteranges; boundary=\" + boundary);\n    }\n\n    if (res.body.empty()) {\n        if (res.content_length_ > 0) {\n            size_t length = 0;\n            if (req.ranges.empty()) {\n                length = res.content_length_;\n            } else if (req.ranges.size() == 1) {\n                auto offsets = detail::get_range_offset_and_length(req, res.content_length_, 0);\n                auto offset = offsets.first;\n                length = offsets.second;\n                auto content_range = detail::make_content_range_header_field(offset, length, res.content_length_);\n                res.set_header(\"Content-Range\", content_range);\n            } else {\n                length = detail::get_multipart_ranges_data_length(req, res, boundary, content_type);\n            }\n            res.set_header(\"Content-Length\", std::to_string(length));\n        } else {\n            if (res.content_provider_) {\n                res.set_header(\"Transfer-Encoding\", \"chunked\");\n            } else {\n                res.set_header(\"Content-Length\", \"0\");\n            }\n        }\n    } else {\n        if (req.ranges.empty()) {\n            ;\n        } else if (req.ranges.size() == 1) {\n            auto offsets = detail::get_range_offset_and_length(req, res.body.size(), 0);\n            auto offset = offsets.first;\n            auto length = offsets.second;\n            auto content_range = detail::make_content_range_header_field(offset, length, res.body.size());\n            res.set_header(\"Content-Range\", content_range);\n            res.body = res.body.substr(offset, length);\n        } else {\n            res.body = detail::make_multipart_ranges_data(req, res, boundary, content_type);\n        }\n\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n        // TODO: 'Accept-Encoding' has gzip, not gzip;q=0\n        const auto &encodings = req.get_header_value(\"Accept-Encoding\");\n        if (encodings.find(\"gzip\") != std::string::npos && detail::can_compress(res.get_header_value(\"Content-Type\"))) {\n            if (detail::compress(res.body)) {\n                res.set_header(\"Content-Encoding\", \"gzip\");\n            }\n        }\n#endif\n\n        auto length = std::to_string(res.body.size());\n        res.set_header(\"Content-Length\", length);\n    }\n\n    if (!detail::write_headers(bstrm, res, Headers())) {\n        return false;\n    }\n\n    // Flush buffer\n    auto &data = bstrm.get_buffer();\n    strm.write(data.data(), data.size());\n\n    // Body\n    if (req.method != \"HEAD\") {\n        if (!res.body.empty()) {\n            if (!strm.write(res.body)) {\n                return false;\n            }\n        } else if (res.content_provider_) {\n            if (!write_content_with_provider(strm, req, res, boundary, content_type)) {\n                return false;\n            }\n        }\n    }\n\n    // Log\n    if (logger_) {\n        logger_(req, res);\n    }\n\n    return true;\n}\n\ninline bool Server::write_content_with_provider(\n    Stream &strm, const Request &req, Response &res, const std::string &boundary, const std::string &content_type) {\n    auto is_shutting_down = [this]() { return this->svr_sock_ == nullptr; };\n\n    if (res.content_length_) {\n        if (req.ranges.empty()) {\n            if (detail::write_content(strm, res.content_provider_, 0, res.content_length_, is_shutting_down) < 0) {\n                return false;\n            }\n        } else if (req.ranges.size() == 1) {\n            auto offsets = detail::get_range_offset_and_length(req, res.content_length_, 0);\n            auto offset = offsets.first;\n            auto length = offsets.second;\n            if (detail::write_content(strm, res.content_provider_, offset, length, is_shutting_down) < 0) {\n                return false;\n            }\n        } else {\n            if (!detail::write_multipart_ranges_data(strm, req, res, boundary, content_type, is_shutting_down)) {\n                return false;\n            }\n        }\n    } else {\n        if (detail::write_content_chunked(strm, res.content_provider_, is_shutting_down) < 0) {\n            return false;\n        }\n    }\n    return true;\n}\n\ninline bool Server::read_content(Stream &strm, Request &req, Response &res) {\n    MultipartFormDataMap::iterator cur;\n    if (read_content_core(\n            strm,\n            req,\n            res,\n            // Regular\n            [&](const char *buf, size_t n) {\n                if (req.body.size() + n > req.body.max_size()) {\n                    return false;\n                }\n                req.body.append(buf, n);\n                return true;\n            },\n            // Multipart\n            [&](const MultipartFormData &file) {\n                cur = req.files.emplace(file.name, file);\n                return true;\n            },\n            [&](const char *buf, size_t n) {\n                auto &content = cur->second.content;\n                if (content.size() + n > content.max_size()) {\n                    return false;\n                }\n                content.append(buf, n);\n                return true;\n            })) {\n        const auto &content_type = req.get_header_value(\"Content-Type\");\n        if (!content_type.find(\"application/x-www-form-urlencoded\")) {\n            detail::parse_query_text(req.body, req.params);\n        }\n        return true;\n    }\n    return false;\n}\n\ninline bool Server::read_content_with_content_receiver(Stream &strm,\n                                                       Request &req,\n                                                       Response &res,\n                                                       ContentReceiver receiver,\n                                                       MultipartContentHeader multipart_header,\n                                                       ContentReceiver multipart_receiver) {\n    return read_content_core(strm, req, res, receiver, multipart_header, multipart_receiver);\n}\n\ninline bool Server::read_content_core(Stream &strm,\n                                      Request &req,\n                                      Response &res,\n                                      ContentReceiver receiver,\n                                      MultipartContentHeader mulitpart_header,\n                                      ContentReceiver multipart_receiver) {\n    detail::MultipartFormDataParser multipart_form_data_parser;\n    ContentReceiver out;\n\n    if (req.is_multipart_form_data()) {\n        const auto &content_type = req.get_header_value(\"Content-Type\");\n        std::string boundary;\n        if (!detail::parse_multipart_boundary(content_type, boundary)) {\n            res.status = 400;\n            return false;\n        }\n\n        multipart_form_data_parser.set_boundary(std::move(boundary));\n        out = [&](const char *buf, size_t n) {\n            /* For debug\n            size_t pos = 0;\n            while (pos < n) {\n              auto read_size = std::min<size_t>(1, n - pos);\n              auto ret = multipart_form_data_parser.parse(\n                  buf + pos, read_size, multipart_receiver, mulitpart_header);\n              if (!ret) { return false; }\n              pos += read_size;\n            }\n            return true;\n            */\n            return multipart_form_data_parser.parse(buf, n, multipart_receiver, mulitpart_header);\n        };\n    } else {\n        out = receiver;\n    }\n\n    if (!detail::read_content(strm, req, payload_max_length_, res.status, Progress(), out, true)) {\n        return false;\n    }\n\n    if (req.is_multipart_form_data()) {\n        if (!multipart_form_data_parser.is_valid()) {\n            res.status = 400;\n            return false;\n        }\n    }\n\n    return true;\n}\n\ninline bool Server::handle_file_request(Request &req, Response &res, bool head) {\n    for (const auto &kv : base_dirs_) {\n        const auto &mount_point = kv.first;\n        const auto &base_dir = kv.second;\n\n        // Prefix match\n        if (!req.path.find(mount_point)) {\n            std::string sub_path = \"/\" + req.path.substr(mount_point.size());\n            if (detail::is_valid_path(sub_path)) {\n                auto path = base_dir + sub_path;\n                if (path.back() == '/') {\n                    path += \"index.html\";\n                }\n\n                if (detail::is_file(path)) {\n                    detail::read_file(path, res.body);\n                    auto type = detail::find_content_type(path, file_extension_and_mimetype_map_);\n                    if (type) {\n                        res.set_header(\"Content-Type\", type);\n                    }\n                    res.status = 200;\n                    if (!head && file_request_handler_) {\n                        file_request_handler_(req, res);\n                    }\n                    return true;\n                }\n            }\n        }\n    }\n    return false;\n}\n\ninline SocketImpl *Server::create_server_socket(const char *host,\n                                                int port,\n                                                int socket_flags,\n                                                SocketOptions socket_options) const {\n    struct addrinfo hints;\n    struct addrinfo *result;\n\n    memset(&hints, 0, sizeof(struct addrinfo));\n    hints.ai_family = AF_UNSPEC;\n    hints.ai_socktype = SOCK_STREAM;\n    hints.ai_flags = socket_flags;\n    hints.ai_protocol = 0;\n\n    auto service = std::to_string(port);\n\n    if (swoole_coroutine_getaddrinfo(host, service.c_str(), &hints, &result)) {\n        return nullptr;\n    }\n\n    SocketImpl *sock = nullptr;\n    for (auto rp = result; rp; rp = rp->ai_next) {\n        sock = new SocketImpl(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n        if (sock->get_fd() == INVALID_SOCKET) {\n            delete sock;\n            continue;\n        }\n        if (tcp_nodelay_) {\n            sock->set_option(IPPROTO_TCP, TCP_NODELAY, 1);\n        }\n        if (socket_options) {\n            socket_options(sock->get_fd());\n        }\n        if (rp->ai_family == AF_INET6) {\n            sock->set_option(IPPROTO_IPV6, IPV6_V6ONLY, 0);\n        }\n        if (!sock->bind(rp->ai_addr, static_cast<socklen_t>(rp->ai_addrlen))) {\n            delete sock;\n            return nullptr;\n        }\n        if (!sock->listen(512)) {\n            delete sock;\n            return nullptr;\n        }\n        break;\n    }\n\n    freeaddrinfo(result);\n    return sock;\n}\n\ninline int Server::bind_internal(const char *host, int port, int socket_flags) {\n    if (!is_valid()) {\n        return -1;\n    }\n\n    svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);\n    if (svr_sock_ == nullptr) {\n        return -1;\n    }\n\n    if (port == 0) {\n        struct sockaddr_storage addr;\n        socklen_t addr_len = sizeof(addr);\n        if (getsockname(svr_sock_->get_fd(), reinterpret_cast<struct sockaddr *>(&addr), &addr_len) == -1) {\n            return -1;\n        }\n        if (addr.ss_family == AF_INET) {\n            return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);\n        } else if (addr.ss_family == AF_INET6) {\n            return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);\n        } else {\n            return -1;\n        }\n    } else {\n        return port;\n    }\n}\n\nstruct CoroutineArg {\n    SocketImpl *client_socket;\n    Server *this_;\n};\n\ninline bool Server::listen_internal() {\n    if (!svr_sock_) {\n        return false;\n    }\n\n    is_running_ = true;\n\n    if (before_listen_callback_) {\n        before_listen_callback_();\n    }\n\n    while (is_running_) {\n        auto client_sock = svr_sock_->accept();\n        if (client_sock) {\n            auto arg = new CoroutineArg;\n            arg->client_socket = client_sock;\n            arg->this_ = this;\n            Coroutine::create(\n                [](void *arg) {\n                    CoroutineArg *_arg = (CoroutineArg *) arg;\n                    _arg->this_->process_and_close_socket(_arg->client_socket);\n                    delete _arg;\n                },\n                arg);\n            continue;\n        }\n        if (svr_sock_->errCode == EMFILE || svr_sock_->errCode == ENFILE) {\n            System::sleep(SW_ACCEPT_RETRY_TIME);\n            continue;\n        } else if (svr_sock_->errCode == ETIMEDOUT || svr_sock_->errCode == SW_ERROR_SSL_BAD_CLIENT) {\n            continue;\n        } else if (svr_sock_->errCode == ECANCELED) {\n            break;\n        } else {\n            swoole_warning(\"accept failed, Error: %s[%d]\", svr_sock_->errMsg, svr_sock_->errCode);\n            break;\n        }\n    }\n\n    is_running_ = false;\n    return true;\n}\n\ninline bool Server::routing(Request &req, Response &res, Stream &strm) {\n    // File handler\n    bool is_head_request = req.method == \"HEAD\";\n    if ((req.method == \"GET\" || is_head_request) && handle_file_request(req, res, is_head_request)) {\n        return true;\n    }\n\n    if (detail::expect_content(req)) {\n        // Content reader handler\n        {\n            ContentReader reader(\n                [&](ContentReceiver receiver) {\n                    return read_content_with_content_receiver(strm, req, res, receiver, nullptr, nullptr);\n                },\n                [&](MultipartContentHeader header, ContentReceiver receiver) {\n                    return read_content_with_content_receiver(strm, req, res, nullptr, header, receiver);\n                });\n\n            if (req.method == \"POST\") {\n                if (dispatch_request_for_content_reader(req, res, reader, post_handlers_for_content_reader_)) {\n                    return true;\n                }\n            } else if (req.method == \"PUT\") {\n                if (dispatch_request_for_content_reader(req, res, reader, put_handlers_for_content_reader_)) {\n                    return true;\n                }\n            } else if (req.method == \"PATCH\") {\n                if (dispatch_request_for_content_reader(req, res, reader, patch_handlers_for_content_reader_)) {\n                    return true;\n                }\n            } else if (req.method == \"DELETE\") {\n                if (dispatch_request_for_content_reader(req, res, reader, delete_handlers_for_content_reader_)) {\n                    return true;\n                }\n            }\n        }\n\n        // Read content into `req.body`\n        if (!read_content(strm, req, res)) {\n            return false;\n        }\n    }\n\n    // Regular handler\n    if (req.method == \"GET\" || req.method == \"HEAD\") {\n        return dispatch_request(req, res, get_handlers_);\n    } else if (req.method == \"POST\") {\n        return dispatch_request(req, res, post_handlers_);\n    } else if (req.method == \"PUT\") {\n        return dispatch_request(req, res, put_handlers_);\n    } else if (req.method == \"DELETE\") {\n        return dispatch_request(req, res, delete_handlers_);\n    } else if (req.method == \"OPTIONS\") {\n        return dispatch_request(req, res, options_handlers_);\n    } else if (req.method == \"PATCH\") {\n        return dispatch_request(req, res, patch_handlers_);\n    }\n\n    res.status = 400;\n    return false;\n}\n\ninline bool Server::dispatch_request(Request &req, Response &res, Handlers &handlers) {\n    try {\n        for (const auto &x : handlers) {\n            const auto &pattern = x.first;\n            const auto &handler = x.second;\n\n            if (std::regex_match(req.path, req.matches, pattern)) {\n                handler(req, res);\n                return true;\n            }\n        }\n    } catch (const std::exception &ex) {\n        res.status = 500;\n        res.set_header(\"EXCEPTION_WHAT\", ex.what());\n    } catch (...) {\n        res.status = 500;\n        res.set_header(\"EXCEPTION_WHAT\", \"UNKNOWN\");\n    }\n    return false;\n}\n\ninline bool Server::dispatch_request_for_content_reader(Request &req,\n                                                        Response &res,\n                                                        ContentReader content_reader,\n                                                        HandlersForContentReader &handlers) {\n    for (const auto &x : handlers) {\n        const auto &pattern = x.first;\n        const auto &handler = x.second;\n\n        if (std::regex_match(req.path, req.matches, pattern)) {\n            handler(req, res, content_reader);\n            return true;\n        }\n    }\n    return false;\n}\n\ninline bool Server::process_request(Stream &strm,\n                                    bool close_connection,\n                                    bool &connection_closed,\n                                    const std::function<void(Request &)> &setup_request) {\n    std::array<char, 2048> buf{};\n\n    detail::stream_line_reader line_reader(strm, buf.data(), buf.size());\n\n    // Connection has been closed on client\n    if (!line_reader.getline()) {\n        return false;\n    }\n\n    Request req;\n    Response res;\n\n    res.version = \"HTTP/1.1\";\n\n    // Check if the request URI doesn't exceed the limit\n    if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {\n        Headers dummy;\n        detail::read_headers(strm, dummy);\n        res.status = 414;\n        return write_response(strm, close_connection, req, res);\n    }\n\n    // Request line and headers\n    if (!parse_request_line(line_reader.ptr(), req) || !detail::read_headers(strm, req.headers)) {\n        res.status = 400;\n        return write_response(strm, close_connection, req, res);\n    }\n\n    if (req.get_header_value(\"Connection\") == \"close\") {\n        connection_closed = true;\n    }\n\n    if (req.version == \"HTTP/1.0\" && req.get_header_value(\"Connection\") != \"Keep-Alive\") {\n        connection_closed = true;\n    }\n\n    strm.get_remote_ip_and_port(req.remote_addr, req.remote_port);\n    req.set_header(\"REMOTE_ADDR\", req.remote_addr);\n    req.set_header(\"REMOTE_PORT\", std::to_string(req.remote_port));\n\n    if (req.has_header(\"Range\")) {\n        const auto &range_header_value = req.get_header_value(\"Range\");\n        if (!detail::parse_range_header(range_header_value, req.ranges)) {\n            // TODO: error\n        }\n    }\n\n    if (setup_request) {\n        setup_request(req);\n    }\n\n    if (req.get_header_value(\"Expect\") == \"100-continue\") {\n        auto status = 100;\n        if (expect_100_continue_handler_) {\n            status = expect_100_continue_handler_(req, res);\n        }\n        switch (status) {\n        case 100:\n        case 417:\n            strm.write_format(\"HTTP/1.1 %d %s\\r\\n\\r\\n\", status, detail::status_message(status));\n            break;\n        default:\n            return write_response(strm, close_connection, req, res);\n        }\n    }\n\n    // Rounting\n    if (routing(req, res, strm)) {\n        if (res.status == -1) {\n            res.status = req.ranges.empty() ? 200 : 206;\n        }\n    } else {\n        if (res.status == -1) {\n            res.status = 404;\n        }\n    }\n\n    return write_response(strm, close_connection, req, res);\n}\n\ninline bool Server::is_valid() const {\n    return true;\n}\n\nnamespace detail {\n\ntemplate <typename T>\ninline bool process_server_socket_core(socket_t sock, size_t keep_alive_max_count, T callback) {\n    assert(keep_alive_max_count > 0);\n    auto ret = false;\n    auto count = keep_alive_max_count;\n    while (count > 0 && keep_alive(sock)) {\n        auto close_connection = count == 1;\n        auto connection_closed = false;\n        ret = callback(close_connection, connection_closed);\n        if (!ret || connection_closed) {\n            break;\n        }\n        count--;\n    }\n    return ret;\n};\n\ntemplate <typename T>\ninline bool process_server_socket(socket_t sock,\n                                  size_t keep_alive_max_count,\n                                  time_t read_timeout_sec,\n                                  time_t read_timeout_usec,\n                                  time_t write_timeout_sec,\n                                  time_t write_timeout_usec,\n                                  T callback) {\n    return process_server_socket_core(sock, keep_alive_max_count, [&](bool close_connection, bool connection_closed) {\n        SocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec);\n        return callback(strm, close_connection, connection_closed);\n    });\n}\n\n}  // namespace detail\n\nclass CoSocketStream : public detail::SocketStream {\n  public:\n    CoSocketStream(SocketImpl *sock,\n                   time_t read_timeout_sec,\n                   time_t read_timeout_usec,\n                   time_t write_timeout_sec,\n                   time_t write_timeout_usec)\n        : detail::SocketStream(\n              sock->get_fd(), read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec) {\n        sock_ = sock;\n        sock->set_timeout((double) read_timeout_sec + ((double) read_timeout_usec / 1000000), SW_TIMEOUT_READ);\n        sock->set_timeout((double) write_timeout_sec + ((double) write_timeout_usec / 1000000), SW_TIMEOUT_WRITE);\n    }\n    ~CoSocketStream() {}\n    bool is_readable() const {\n        return true;\n    }\n    bool is_writable() const {\n        return true;\n    }\n    ssize_t read(char *ptr, size_t size) {\n        return sock_->recv_with_buffer(ptr, size);\n    }\n    ssize_t write(const char *ptr, size_t size) {\n        return sock_->write(ptr, size);\n    }\n    void get_remote_ip_and_port(std::string &ip, int &port) const {\n        Address sa;\n        sock_->getpeername(&sa);\n        ip = std::string(sa.get_addr());\n        port = sa.get_port();\n    }\n\n  private:\n    SocketImpl *sock_;\n};\n\ninline bool Server::process_and_close_socket(SocketImpl *sock) {\n    size_t keep_alive_max_count = keep_alive_max_count_;\n    time_t read_timeout_sec = read_timeout_sec_;\n    time_t read_timeout_usec = read_timeout_usec_;\n    time_t write_timeout_sec = write_timeout_sec_;\n    time_t write_timeout_usec = write_timeout_usec_;\n\n    CoSocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec);\n\n    assert(keep_alive_max_count > 0);\n\n    auto ret = false;\n    auto count = keep_alive_max_count;\n\n    do {\n        auto close_connection = count == 1;\n        auto connection_closed = false;\n        ret = process_request(strm, close_connection, connection_closed, nullptr);\n        if (!ret || connection_closed) {\n            break;\n        }\n        count--;\n    } while (count > 0 && sock->check_liveness());\n\n    sock->shutdown();\n    sock->close();\n    delete sock;\n\n    return ret;\n}\n\n}  // namespace httplib\n"
  },
  {
    "path": "core-tests/include/redis_client.h",
    "content": "#pragma once\n\n#include \"swoole.h\"\n\n#include <string>\n#include <vector>\n#include <memory>\n\n#include \"hiredis.h\"\n\nnamespace swoole {\n\nclass RedisReply {\n  private:\n    redisReply *ptr_;\n\n  public:\n    RedisReply(void *ptr) : ptr_(reinterpret_cast<redisReply *>(ptr)) {}\n    RedisReply(RedisReply &&_o) {\n        ptr_ = _o.ptr_;\n        _o.ptr_ = nullptr;\n    }\n    ~RedisReply() {\n        if (ptr_) {\n            freeReplyObject(ptr_);\n        }\n    }\n    redisReply *operator->() {\n        return ptr_;\n    }\n\n    inline bool empty() {\n        return ptr_ == nullptr;\n    }\n    inline const char *str() {\n        return ptr_->str;\n    }\n    inline size_t len() {\n        return ptr_->len;\n    }\n    inline size_t type() {\n        return ptr_->type;\n    }\n};\n\nclass RedisClient {\n  private:\n    redisContext *ctx = nullptr;\n\n  public:\n    RedisClient() = default;\n\n    ~RedisClient() {\n        if (ctx) {\n            redisFree(ctx);\n        }\n    }\n\n    RedisReply Request(const std::vector<std::string> &args);\n    RedisReply Request(int argc, const char **argv, const size_t *argvlen);\n\n    bool Connect(const std::string &host = \"127.0.0.1\", int port = 6379, struct timeval timeout = {});\n    std::string Get(const std::string &key);\n    bool Set(const std::string &key, const std::string &value);\n    long Ttl(const std::string &key);\n    bool Select(int db);\n    std::string Role();\n};\n\n}  // namespace swoole\n"
  },
  {
    "path": "core-tests/include/test_core.h",
    "content": "#pragma once\n\n#include \"swoole_client.h\"\n#include \"swoole_socket_impl.h\"\n\n#include <gtest/gtest.h>\n\n#include <functional>\n#include <utility>\n#include <initializer_list>\n#include <string>\n#include <vector>\n#include <set>\n#include <thread>\n#include <mutex>\n#include <unordered_map>\n#include <fstream>\n\n#define TEST_HOST \"127.0.0.1\"\n#define TEST_HOST6 \"::1\"\n#define TEST_PORT 9501\n#define TEST_TMP_FILE \"/tmp/swoole_core_test_file\"\n#define TEST_TMP_DIR \"/tmp/swoole_core_test_dir\"\n#define TEST_JPG_FILE \"/examples/test.jpg\"\n#define TEST_JPG_MD5SUM \"64a42b4c0f3c65a14c23b60d3880a917\"\n\n#define TEST_HTTP_PROXY_HOST \"127.0.0.1\"\n#define TEST_HTTP_PROXY_PORT 8888\n#define TEST_HTTP_PROXY_USER \"user\"\n#define TEST_HTTP_PROXY_PASSWORD \"password\"\n\n#define TEST_SOCKS5_PROXY_HOST \"127.0.0.1\"\n#define TEST_SOCKS5_PROXY_PORT 8080\n#define TEST_SOCKS5_PROXY_NO_AUTH_PORT 8081\n#define TEST_SOCKS5_PROXY_USER \"user\"\n#define TEST_SOCKS5_PROXY_PASSWORD \"password\"\n\n#define TEST_DOMAIN_BAIDU \"www.baidu.com\"\n\n#define TEST_HTTP_DOMAIN \"www.gov.cn\"\n#define TEST_HTTP_EXPECT \"Location: https://www.gov.cn/\"\n#define TEST_HTTPS_EXPECT \"中国政府网\"\n\n#define TEST_STR \"hello world, hello swoole\\n\"\n#define TEST_STR2 \"I am Rango\\n\"\n\n#define TEST_LOG_FILE \"/tmp/swoole.log\"\n#define TEST_SOCK_FILE \"/tmp/swoole-core-tests.sock\"\n\n#define TEST_WRITEV_OFFSET 87\n#define TEST_READV_OFFSET 1949\n\n#define TEST_COUNTER_NUM 32\n\n#define TEST_REQUEST_BAIDU                                                                                             \\\n    \"GET / HTTP/1.1\\r\\n\"                                                                                               \\\n    \"Host: www.baidu.com\\r\\n\"                                                                                          \\\n    \"Connection: close\\r\\n\"                                                                                            \\\n    \"User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) \"                         \\\n    \"Chrome/51.0.2704.106 Safari/537.36\"                                                                               \\\n    \"\\r\\n\\r\\n\"\n\n#define CRLF \"\\r\\n\"\n#define EOF_PACKET \"hello world\" CRLF\n#define EOF_PACKET_2 \"php&swoole, java&golang\" CRLF\n#define RECV_TIMEOUT 10.0\n\n#define ASSERT_MEMEQ(x, y, n) ASSERT_EQ(memcmp((x), (y), n), 0)\n#define EXPECT_MEMEQ(x, y, n) EXPECT_EQ(memcmp((x), (y), n), 0)\n#define ASSERT_ERREQ(x) ASSERT_EQ(swoole_get_last_error(), x)\n#define EXPECT_ERREQ(x) EXPECT_EQ(swoole_get_last_error(), x)\n\n#define TEST_WRITE(fd, s) ASSERT_EQ(write(fd, s, strlen(s)), strlen(s))\n\n#define TIMER_PARAMS swoole::Timer *timer, swoole::TimerNode *tnode\n\n#ifdef SW_VERBOSE\n#define DEBUG() swoole::test::debug_output.get()\n#define debug_info printf\n#else\n#define DEBUG() swoole::test::null_stream\n#define debug_info(...)\n#endif\n\n#ifdef __ANDROID__\n#define sysv_signal signal\n#endif\n\nnamespace swoole {\nstruct HttpProxy;\nstruct Socks5Proxy;\nnamespace test {\nclass NullStream : public std::ostream {\n  public:\n    NullStream() : std::ostream(nullptr) {}\n};\n\nextern NullStream null_stream;\nextern std::reference_wrapper<std::ostream> debug_output;\nconst std::string &get_root_path();\nstd::string get_ssl_dir();\nstd::string get_jpg_file();\nbool is_github_ci();\nint exec_js_script(const std::string &file, const std::string &args);\nstd::string http_get_request(const std::string &domain, const std::string &path);\nint get_random_port();\nint has_threads();\nint has_child_processes();\nint wait_all_child_processes(bool verbose = false);\nbool is_valid_fd(int fd);\n\npid_t spawn_exec(const std::function<void(void)> &fn);\nint spawn_exec_and_wait(const std::function<void(void)> &fn);\n\nvoid counter_init();\nint *counter_ptr();\nint counter_incr(int index, int add = 1);\nint counter_get(int index);\nvoid counter_set(int index, int value);\nvoid counter_incr_and_put_log(int index, const char *msg);\n\nint dump_cert_info(const char *data, size_t len);\nint recursive_rmdir(const char *path);\n\nstd::pair<std::shared_ptr<CoSocket>, std::shared_ptr<CoSocket>> create_socket_pair();\n\nstatic inline int dump_cert_info(const String *str) {\n    return dump_cert_info(str->str, str->length);\n}\n\n}  // namespace test\n};  // namespace swoole\n"
  },
  {
    "path": "core-tests/include/test_coroutine.h",
    "content": "#pragma once\n\n#include \"test_core.h\"\n\n#include \"swoole_coroutine.h\"\n#include \"swoole_coroutine_channel.h\"\n#include \"swoole_coroutine_system.h\"\n#include \"swoole_coroutine_socket.h\"\n#include \"swoole_coroutine_api.h\"\n\nnamespace swoole {\nnamespace test {\n\nclass coroutine {\n  public:\n    coroutine(const CoroutineFunc &_fn, void *_arg, int *_complete_num)\n        : fn(_fn), arg(_arg), complete_num(_complete_num) {}\n\n    void start() {\n        fn(arg);\n        (*complete_num)++;\n    }\n\n    inline static void create(const CoroutineFunc &fn, void *arg, int *complete_num) {\n        auto test = new coroutine(fn, arg, complete_num);\n\n        long cid = swoole::Coroutine::create(\n            [](void *arg) {\n                ((coroutine *) arg)->start();\n                delete (coroutine *) arg;\n            },\n            test);\n        ASSERT_GT(cid, 0);\n    }\n\n    inline static void run(std::initializer_list<std::pair<CoroutineFunc, void *>> args) {\n        int complete_num = 0;\n        swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n        Coroutine::activate();\n        for (const auto &arg : args) {\n            create(arg.first, arg.second, &complete_num);\n        }\n        swoole_event_wait();\n        Coroutine::deactivate();\n    }\n\n    inline static void run(std::initializer_list<CoroutineFunc> fns) {\n        int complete_num = 0;\n        swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n        Coroutine::activate();\n        for (const auto &fn : fns) {\n            create(fn, nullptr, &complete_num);\n        }\n        swoole_event_wait();\n        Coroutine::deactivate();\n    }\n\n    inline static void run(const CoroutineFunc &fn, void *arg = nullptr) {\n        int complete_num = 0;\n        swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n        Coroutine::activate();\n        create(fn, arg, &complete_num);\n        swoole_event_wait();\n        Coroutine::deactivate();\n    }\n\n  private:\n    CoroutineFunc fn;\n    void *arg;\n    int *complete_num;\n};\n}  // namespace test\n}  // namespace swoole\n"
  },
  {
    "path": "core-tests/include/test_process.h",
    "content": "#pragma once\n\n#include <functional>\n\n#include \"test_core.h\"\n#include \"swoole_process_pool.h\"\n\nusing namespace std;\n\nnamespace swoole { namespace test {\n//-------------------------------------------------------------------------------\nclass Process\n{\n\nprivate:\n    std::function<void (Process*)> handler;\n\npublic:\n    Worker worker = {};\n\n    Process(std::function<void (Process*)> fn, int pipe_type = SOCK_DGRAM);\n    ~Process();\n    pid_t start();\n    ssize_t write(const void *__buf, size_t __n);\n    ssize_t read(void *__buf, size_t __nbytes);\n};\n//-------------------------------------------------------------------------------\n}}\n"
  },
  {
    "path": "core-tests/include/test_server.h",
    "content": "#pragma once\n\n#include \"test_core.h\"\n#include \"swoole_server.h\"\n\n#define SERVER_THIS ((swoole::test::Server *) serv->private_data_2)\n\n#define ON_START_PARAMS swoole::Server *serv\n#define ON_WORKER_START_PARAMS swoole::Server *serv, swoole::Worker *worker\n#define ON_PACKET_PARAMS swoole::Server *serv, swoole::RecvData *req\n#define ON_RECEIVE_PARAMS swoole::Server *serv, swoole::RecvData *req\n\nnamespace swoole {\nnamespace test {\n//--------------------------------------------------------------------------------------------------------\nclass Server {\n  private:\n    swoole::Server serv;\n    std::vector<ListenPort *> ports;\n    std::unordered_map<std::string, void *> private_data;\n    std::string host;\n    int port;\n    int mode;\n    int type;\n\n    std::string tolower(const std::string &str);\n\n  public:\n    DgramPacket *packet = nullptr;\n\n    Server(std::string _host, int _port, swoole::Server::Mode _mode, int _type);\n    ~Server();\n\n    void on(const std::string &event, const std::function<void(swServer *, Worker *)> &fn);\n    void on(const std::string &event, const std::function<void(swServer *)> &fn);\n    void on(const std::string &event, const std::function<void(swServer *, EventData *)> &fn);\n    void on(const std::string &event, const std::function<int(swServer *, EventData *)> &fn);\n    void on(const std::string &event, const std::function<int(swServer *, RecvData *)> &fn);\n    void on(const std::string &event, const std::function<void(swServer *, DataHead *)> &fn);\n\n    bool start();\n    bool listen(const std::string &host, int port, enum swSocketType type);\n    int send(int session_id, const void *data, uint32_t length);\n    ssize_t sendto(const swoole::network::Address &address, const char *__buf, size_t __n, int server_socket = -1);\n    int close(int session_id, int reset);\n\n    inline void *get_private_data(const std::string &key) {\n        auto it = private_data.find(key);\n        if (it == private_data.end()) {\n            return nullptr;\n        } else {\n            return it->second;\n        }\n    }\n\n    inline void set_private_data(const std::string &key, void *data) {\n        private_data[key] = data;\n    }\n};\n//--------------------------------------------------------------------------------------------------------\n}  // namespace test\n}  // namespace swoole\n"
  },
  {
    "path": "core-tests/js/.gitignore",
    "content": "node_modules\npackage-lock.json\n"
  },
  {
    "path": "core-tests/js/mqtt.js",
    "content": "const mqtt = require('mqtt');\nconst port = process.argv[2];\nconst pino = require('pino');\nconst logger = pino(pino.destination('/tmp/swoole.log'));\n\nconst client = mqtt.connect(`mqtt://localhost:${port}`);\n\nclient.on('connect', () => {\n    logger.info('the client is connected');\n\n    client.subscribe('test/topic', (err) => {\n        if (err) {\n            console.error('subscribe fail:', err);\n            return;\n        }\n        logger.info('subscribed: test/topic');\n\n        client.publish('test/topic', 'Hello MQTT from Node.js!');\n    });\n});\n\nclient.on('disconnect', () => {\n    logger.info('the client is disconnected');\n    client.end()\n})\n\nclient.on('message', (topic, message) => {\n    logger.info(`received message, topic: ${topic}, content: ${message.toString()}`);\n});\n\nclient.on('error', (err) => {\n    console.error('error:', err);\n});\n"
  },
  {
    "path": "core-tests/js/package.json",
    "content": "{\n  \"dependencies\": {\n    \"mqtt\": \"^4.3.8\",\n    \"pino\": \"^6.14.0\",\n    \"ws\": \"^8.18.2\"\n  }\n}\n"
  },
  {
    "path": "core-tests/js/ws_1.js",
    "content": "const WebSocket = require('ws');\nconst pino = require('pino');\nconst port = process.argv[2];\nconst logger = pino(pino.destination('/tmp/swoole.log'));\n\nconst ws_1 = new WebSocket(`ws://127.0.0.1:${port}/`);\n\nfunction delay(ms) {\n    return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nws_1.on('error', console.error);\n\nws_1.on('close', function () {\n    logger.info('the node websocket client is closed');\n})\n\nws_1.on('open', async () => {\n    ws_1.send('hello', {fin: false});\n    await delay(50);\n    ws_1.send(' ', {fin: false});\n    await delay(50);\n    ws_1.send('world', {fin: true});\n\n    await delay(200);\n    ws_1.ping(\"keep alive\")\n\n    await delay(200);\n    ws_1.close()\n});\n\nws_1.on('message', function message(data) {\n    logger.info('received: ' + data);\n});\n"
  },
  {
    "path": "core-tests/js/ws_2.js",
    "content": "const WebSocket = require('ws');\nconst pino = require('pino');\nconst port = process.argv[2];\nconst logger = pino(pino.destination('/tmp/swoole.log'));\n\nconst ws = new WebSocket(`ws://127.0.0.1:${port}/ws/close`);\n\nfunction delay(ms) {\n    return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nws.on('error', console.error);\n\nws.on('close', function (code, reason) {\n    logger.info('the node websocket client is closed, code: ' + code + ', reason: ' + reason.toString());\n})\n\nws.on('open', async () => {\n});\n\nws.on('message', function message(data) {\n});\n"
  },
  {
    "path": "core-tests/samples/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 2.8)\nproject(samples)\n#set(CMAKE_BUILD_TYPE Released)\nset(CMAKE_CXX_STANDARD 11)\nfile(GLOB_RECURSE SOURCE_FILES FOLLOW_SYMLINKS src/*.cc)\n\nadd_definitions(-DHAVE_CONFIG_H)\n\nlink_directories($ENV{SWOOLE_DIR}/lib)\ninclude_directories(. ./include $ENV{SWOOLE_DIR} $ENV{SWOOLE_DIR}/include BEFORE)\nset(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)\nadd_executable(core_samples ${SOURCE_FILES})\ntarget_link_libraries(core_samples swoole)\n"
  },
  {
    "path": "core-tests/samples/src/s1.cc",
    "content": "#include \"swoole.h\"\n#include \"swoole_client.h\"\n#include \"swoole_server.h\"\n#include \"swoole_coroutine.h\"\n#include \"swoole_socket_impl.h\"\n#include \"swoole_coroutine_system.h\"\n\n#include <iostream>\n\nusing swoole::Coroutine;\nusing swoole::coroutine::System;\n\nstruct A {\n    int x;\n    int *y;\n};\n\nstatic A G_a = {0, 0};\n\nint main(int argc, char **argv) {\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n    // coroutine 1\n    Coroutine::create([](void *arg) {\n        G_a.x = 1234;\n        int y = 5678;\n        G_a.y = &y;\n        // After the coroutine 1 is suspended here, the coroutine 2 will be executed, and the x, y values is updated in\n        // the coroutine 2. After the coroutine 2 suspends, go back to coroutine 1, where the values of x and y will be\n        // no longer as expected\n        System::sleep(1);\n        // output 100\n        std::cout << \"X=\" << G_a.x << std::endl;\n        // read invalid point\n        std::cout << \"Y=\" << *G_a.y << std::endl;\n    });\n\n    // coroutine 2\n    Coroutine::create([](void *arg) {\n        G_a.x = 100;\n        G_a.y = nullptr;\n    });\n\n    swoole_event_wait();\n\n    return 0;\n}\n"
  },
  {
    "path": "core-tests/src/_lib/http.cpp",
    "content": "#include \"test_core.h\"\n#include \"httplib_client.h\"\n#include \"swoole_http.h\"\n\nnamespace websocket = swoole::websocket;\n\nusing swoole::Protocol;\nusing swoole::String;\n\nnamespace httplib {\n\nbool Client::Upgrade(const char *_path, Headers &_headers) {\n    set_keep_alive(true);\n    _headers.emplace(\"Connection\", \"Upgrade\");\n    _headers.emplace(\"Upgrade\", \"websocket\");\n    _headers.emplace(\"Sec-Websocket-Key\", \"sN9cRrP/n9NdMgdcy2VJFQ==\");\n    _headers.emplace(\"Sec-WebSocket-Version\", \"13\");\n\n    auto resp = Get(_path, _headers);\n    if (resp == nullptr or resp->status != SW_HTTP_SWITCHING_PROTOCOLS) {\n        return false;\n    }\n\n    return true;\n}\n\nbool Client::Push(const char *data, size_t length, int opcode) {\n    if (!socket_.is_open()) {\n        return false;\n    }\n    return process_socket(socket_, [&](Stream &strm) {\n        String buffer = {};\n        char buf[32];\n        buffer.size = sizeof(buf);\n        buffer.str = buf;\n\n        auto flags = websocket::FLAG_FIN | websocket::FLAG_ENCODE_HEADER_ONLY;\n        if (websocket_mask_) {\n            flags |= websocket::FLAG_MASK;\n        }\n\n        websocket::encode(&buffer, data, length, opcode, flags);\n        if (strm.write(buffer.str, buffer.length) != (ssize_t) buffer.length) {\n            return false;\n        }\n\n        if (websocket_mask_) {\n            std::unique_ptr<char[]> marked = std::make_unique<char[]>(length + 1);\n            memcpy(marked.get(), data, length);\n            websocket::mask(marked.get(), length, buffer.str + buffer.length - SW_WEBSOCKET_MASK_LEN);\n            return strm.write(marked.get(), length) == (ssize_t) length;\n        } else {\n            return strm.write(data, length) == (ssize_t) length;\n        }\n    });\n}\n\nstd::shared_ptr<WebSocketFrame> Client::Recv() {\n    auto msg = std::make_shared<WebSocketFrame>();\n    auto retval = process_socket(socket_, [&](Stream &strm) {\n        Protocol proto = {};\n        proto.package_length_size = SW_WEBSOCKET_HEADER_LEN;\n        proto.get_package_length = websocket::get_package_length;\n        proto.package_max_length = SW_INPUT_BUFFER_SIZE;\n\n        char buf[1024];\n        ssize_t packet_len;\n\n        if (strm.read(buf, SW_WEBSOCKET_HEADER_LEN) <= 0) {\n            return false;\n        }\n        swoole::PacketLength pl{\n            buf,\n            SW_WEBSOCKET_HEADER_LEN,\n        };\n        packet_len = proto.get_package_length(&proto, nullptr, &pl);\n        if (packet_len < 0) {\n            return false;\n        }\n        if (packet_len == 0) {\n            if (strm.read(buf + SW_WEBSOCKET_HEADER_LEN, pl.header_len - SW_WEBSOCKET_HEADER_LEN) <= 0) {\n                return false;\n            }\n            pl.buf_size = pl.header_len;\n            packet_len = proto.get_package_length(&proto, nullptr, &pl);\n            if (packet_len <= 0) {\n                return false;\n            }\n        }\n\n        char *data = (char *) malloc(packet_len + 1);\n        if (data == nullptr) {\n            return false;\n        }\n        data[packet_len] = 0;\n\n        uint32_t header_len = pl.header_len > 0 ? pl.header_len : SW_WEBSOCKET_HEADER_LEN;\n        memcpy(data, buf, header_len);\n\n        ssize_t read_bytes = header_len;\n        while (read_bytes < packet_len) {\n            auto n_read = strm.read(data + read_bytes, packet_len - read_bytes);\n            if (n_read <= 0) {\n                free(data);\n                return false;\n            }\n            read_bytes += n_read;\n        }\n\n        return websocket::decode(msg.get(), data, packet_len);\n    });\n\n    return retval ? msg : nullptr;\n}\n\n// HTTP client implementation\nClient::Client(const std::string &host) : Client(host, 80, std::string(), std::string()) {}\n\nClient::Client(const std::string &host, int port) : Client(host, port, std::string(), std::string()) {}\n\nClient::Client(const std::string &host,\n               int port,\n               const std::string &client_cert_path,\n               const std::string &client_key_path)\n    : host_(host),\n      port_(port),\n      host_and_port_(host_ + \":\" + std::to_string(port_)),\n      client_cert_path_(client_cert_path),\n      client_key_path_(client_key_path) {}\n\nClient::~Client() {\n    stop();\n}\n\nbool Client::is_valid() const {\n    return true;\n}\n\nsocket_t Client::create_client_socket() const {\n    if (!proxy_host_.empty()) {\n        return detail::create_client_socket(proxy_host_.c_str(),\n                                            proxy_port_,\n                                            tcp_nodelay_,\n                                            socket_options_,\n                                            connection_timeout_sec_,\n                                            connection_timeout_usec_,\n                                            interface_);\n    }\n    return detail::create_client_socket(host_.c_str(),\n                                        port_,\n                                        tcp_nodelay_,\n                                        socket_options_,\n                                        connection_timeout_sec_,\n                                        connection_timeout_usec_,\n                                        interface_);\n}\n\nbool Client::create_and_connect_socket(Socket &socket) {\n    auto sock = create_client_socket();\n    if (sock == INVALID_SOCKET) {\n        return false;\n    }\n    socket.sock = sock;\n    return true;\n}\n\nvoid Client::close_socket(Socket &socket, bool /*process_socket_ret*/) {\n    detail::close_socket(socket.sock);\n    socket_.sock = INVALID_SOCKET;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n    socket_.ssl = nullptr;\n#endif\n}\n\nbool Client::read_response_line(Stream &strm, Response &res) {\n    std::array<char, 2048> buf{};\n\n    detail::stream_line_reader line_reader(strm, buf.data(), buf.size());\n\n    if (!line_reader.getline()) {\n        return false;\n    }\n\n    const static std::regex re(\"(HTTP/1\\\\.[01]) (\\\\d+?) .*\\r\\n\");\n\n    std::cmatch m;\n    if (std::regex_match(line_reader.ptr(), m, re)) {\n        res.version = std::string(m[1]);\n        res.status = std::stoi(std::string(m[2]));\n    }\n\n    return true;\n}\n\nbool Client::send(const Request &req, Response &res) {\n    std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);\n\n    {\n        std::lock_guard<std::mutex> guard(socket_mutex_);\n\n        auto is_alive = false;\n        if (socket_.is_open()) {\n            is_alive = detail::select_write(socket_.sock, 0, 0) > 0;\n            if (!is_alive) {\n                close_socket(socket_, false);\n            }\n        }\n\n        if (!is_alive) {\n            if (!create_and_connect_socket(socket_)) {\n                return false;\n            }\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n            // TODO: refactoring\n            if (is_ssl()) {\n                auto &scli = static_cast<SSLClient &>(*this);\n                if (!proxy_host_.empty()) {\n                    bool success = false;\n                    if (!scli.connect_with_proxy(socket_, res, success)) {\n                        return success;\n                    }\n                }\n\n                if (!scli.initialize_ssl(socket_)) {\n                    return false;\n                }\n            }\n#endif\n        }\n    }\n\n    auto close_connection = !keep_alive_;\n\n    auto ret = process_socket(socket_, [&](Stream &strm) { return handle_request(strm, req, res, close_connection); });\n\n    if (close_connection) {\n        stop();\n    }\n\n    return ret;\n}\n\nbool Client::handle_request(Stream &strm, const Request &req, Response &res, bool close_connection) {\n    if (req.path.empty()) {\n        return false;\n    }\n\n    bool ret;\n\n    if (!is_ssl() && !proxy_host_.empty()) {\n        auto req2 = req;\n        req2.path = \"http://\" + host_and_port_ + req.path;\n        ret = process_request(strm, req2, res, close_connection);\n    } else {\n        ret = process_request(strm, req, res, close_connection);\n    }\n\n    if (!ret) {\n        return false;\n    }\n\n    if (300 < res.status && res.status < 400 && follow_location_) {\n        ret = redirect(req, res);\n    }\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n    if ((res.status == 401 || res.status == 407) && req.authorization_count_ < 5) {\n        auto is_proxy = res.status == 407;\n        const auto &username = is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;\n        const auto &password = is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;\n\n        if (!username.empty() && !password.empty()) {\n            std::map<std::string, std::string> auth;\n            if (parse_www_authenticate(res, auth, is_proxy)) {\n                Request new_req = req;\n                new_req.authorization_count_ += 1;\n                auto key = is_proxy ? \"Proxy-Authorization\" : \"Authorization\";\n                new_req.headers.erase(key);\n                new_req.headers.insert(make_digest_authentication_header(\n                    req, auth, new_req.authorization_count_, random_string(10), username, password, is_proxy));\n\n                Response new_res;\n\n                ret = send(new_req, new_res);\n                if (ret) {\n                    res = new_res;\n                }\n            }\n        }\n    }\n#endif\n\n    return ret;\n}\n\nbool Client::redirect(const Request &req, Response &res) {\n    if (req.redirect_count == 0) {\n        return false;\n    }\n\n    auto location = res.get_header_value(\"location\");\n    if (location.empty()) {\n        return false;\n    }\n\n    const static std::regex re(R\"(^(?:(https?):)?(?://([^:/?#]*)(?::(\\d+))?)?([^?#]*(?:\\?[^#]*)?)(?:#.*)?)\");\n\n    std::smatch m;\n    if (!std::regex_match(location, m, re)) {\n        return false;\n    }\n\n    auto scheme = is_ssl() ? \"https\" : \"http\";\n\n    auto next_scheme = m[1].str();\n    auto next_host = m[2].str();\n    auto port_str = m[3].str();\n    auto next_path = m[4].str();\n\n    auto next_port = port_;\n    if (!port_str.empty()) {\n        next_port = std::stoi(port_str);\n    } else if (!next_scheme.empty()) {\n        next_port = next_scheme == \"https\" ? 443 : 80;\n    }\n\n    if (next_scheme.empty()) {\n        next_scheme = scheme;\n    }\n    if (next_host.empty()) {\n        next_host = host_;\n    }\n    if (next_path.empty()) {\n        next_path = \"/\";\n    }\n\n    if (next_scheme == scheme && next_host == host_ && next_port == port_) {\n        return detail::redirect(*this, req, res, next_path);\n    } else {\n        if (next_scheme == \"https\") {\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n            SSLClient cli(next_host.c_str(), next_port);\n            cli.copy_settings(*this);\n            return detail::redirect(cli, req, res, next_path);\n#else\n            return false;\n#endif\n        } else {\n            Client cli(next_host.c_str(), next_port);\n            cli.copy_settings(*this);\n            return detail::redirect(cli, req, res, next_path);\n        }\n    }\n}\n\nbool Client::write_request(Stream &strm, const Request &req, bool close_connection) {\n    detail::BufferStream bstrm;\n\n    // Request line\n    const auto &path = detail::encode_url(req.path);\n\n    bstrm.write_format(\"%s %s HTTP/1.1\\r\\n\", req.method.c_str(), path.c_str());\n\n    // Additonal headers\n    Headers headers;\n    if (close_connection) {\n        headers.emplace(\"Connection\", \"close\");\n    }\n\n    if (!req.has_header(\"Host\")) {\n        if (is_ssl()) {\n            if (port_ == 443) {\n                headers.emplace(\"Host\", host_);\n            } else {\n                headers.emplace(\"Host\", host_and_port_);\n            }\n        } else {\n            if (port_ == 80) {\n                headers.emplace(\"Host\", host_);\n            } else {\n                headers.emplace(\"Host\", host_and_port_);\n            }\n        }\n    }\n\n    if (!req.has_header(\"Accept\")) {\n        headers.emplace(\"Accept\", \"*/*\");\n    }\n\n    if (!req.has_header(\"User-Agent\")) {\n        headers.emplace(\"User-Agent\", USER_AGENT);\n    }\n\n    if (req.body.empty()) {\n        if (req.content_provider) {\n            auto length = std::to_string(req.content_length);\n            headers.emplace(\"Content-Length\", length);\n        } else {\n            headers.emplace(\"Content-Length\", \"0\");\n        }\n    } else {\n        if (!req.has_header(\"Content-Type\")) {\n            headers.emplace(\"Content-Type\", \"text/plain\");\n        }\n\n        if (!req.has_header(\"Content-Length\")) {\n            auto length = std::to_string(req.body.size());\n            headers.emplace(\"Content-Length\", length);\n        }\n    }\n\n    if (!basic_auth_username_.empty() && !basic_auth_password_.empty()) {\n        headers.insert(make_basic_authentication_header(basic_auth_username_, basic_auth_password_, false));\n    }\n\n    if (!proxy_basic_auth_username_.empty() && !proxy_basic_auth_password_.empty()) {\n        headers.insert(make_basic_authentication_header(proxy_basic_auth_username_, proxy_basic_auth_password_, true));\n    }\n\n    detail::write_headers(bstrm, req, headers);\n\n    // Flush buffer\n    auto &data = bstrm.get_buffer();\n    if (!detail::write_data(strm, data.data(), data.size())) {\n        return false;\n    }\n\n    // Body\n    if (req.body.empty()) {\n        if (req.content_provider) {\n            size_t offset = 0;\n            size_t end_offset = req.content_length;\n\n            bool ok = true;\n\n            DataSink data_sink;\n            data_sink.write = [&](const char *d, size_t l) {\n                if (ok) {\n                    if (detail::write_data(strm, d, l)) {\n                        offset += l;\n                    } else {\n                        ok = false;\n                    }\n                }\n            };\n            data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };\n\n            while (offset < end_offset) {\n                if (!req.content_provider(offset, end_offset - offset, data_sink)) {\n                    return false;\n                }\n                if (!ok) {\n                    return false;\n                }\n            }\n        }\n    } else {\n        return detail::write_data(strm, req.body.data(), req.body.size());\n    }\n\n    return true;\n}\n\nstd::shared_ptr<Response> Client::send_with_content_provider(const char *method,\n                                                             const char *path,\n                                                             const Headers &headers,\n                                                             const std::string &body,\n                                                             size_t content_length,\n                                                             ContentProvider content_provider,\n                                                             const char *content_type) {\n    Request req;\n    req.method = method;\n    req.headers = headers;\n    req.path = path;\n\n    if (content_type) {\n        req.headers.emplace(\"Content-Type\", content_type);\n    }\n\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n    if (compress_) {\n        if (content_provider) {\n            size_t offset = 0;\n\n            DataSink data_sink;\n            data_sink.write = [&](const char *data, size_t data_len) {\n                req.body.append(data, data_len);\n                offset += data_len;\n            };\n            data_sink.is_writable = [&](void) { return true; };\n\n            while (offset < content_length) {\n                if (!content_provider(offset, content_length - offset, data_sink)) {\n                    return nullptr;\n                }\n            }\n        } else {\n            req.body = body;\n        }\n\n        if (!detail::compress(req.body)) {\n            return nullptr;\n        }\n        req.headers.emplace(\"Content-Encoding\", \"gzip\");\n    } else\n#endif\n    {\n        if (content_provider) {\n            req.content_length = content_length;\n            req.content_provider = content_provider;\n        } else {\n            req.body = body;\n        }\n    }\n\n    auto res = std::make_shared<Response>();\n\n    return send(req, *res) ? res : nullptr;\n}\n\nbool Client::process_request(Stream &strm, const Request &req, Response &res, bool close_connection) {\n    // Send request\n    if (!write_request(strm, req, close_connection)) {\n        return false;\n    }\n\n    // Receive response and headers\n    if (!read_response_line(strm, res) || !detail::read_headers(strm, res.headers)) {\n        return false;\n    }\n\n    if (req.response_handler) {\n        if (!req.response_handler(res)) {\n            return false;\n        }\n    }\n\n    // Body\n    if (req.method != \"HEAD\" && req.method != \"CONNECT\") {\n        auto out =\n            req.content_receiver\n                ? static_cast<ContentReceiver>([&](const char *buf, size_t n) { return req.content_receiver(buf, n); })\n                : static_cast<ContentReceiver>([&](const char *buf, size_t n) {\n                      if (res.body.size() + n > res.body.max_size()) {\n                          return false;\n                      }\n                      res.body.append(buf, n);\n                      return true;\n                  });\n\n        int dummy_status;\n        if (!detail::read_content(\n                strm, res, (std::numeric_limits<size_t>::max)(), dummy_status, req.progress, out, decompress_)) {\n            return false;\n        }\n    }\n\n    if (res.get_header_value(\"Connection\") == \"close\" || res.version == \"HTTP/1.0\") {\n        stop();\n    }\n\n    // Log\n    if (logger_) {\n        logger_(req, res);\n    }\n\n    return true;\n}\n\nbool Client::process_socket(Socket &socket, std::function<bool(Stream &strm)> callback) {\n    return detail::process_client_socket(\n        socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, callback);\n}\n\nbool Client::is_ssl() const {\n    return false;\n}\n\nstd::shared_ptr<Response> Client::Get(const char *path) {\n    return Get(path, Headers(), Progress());\n}\n\nstd::shared_ptr<Response> Client::Get(const char *path, Progress progress) {\n    return Get(path, Headers(), std::move(progress));\n}\n\nstd::shared_ptr<Response> Client::Get(const char *path, const Headers &headers) {\n    return Get(path, headers, Progress());\n}\n\nstd::shared_ptr<Response> Client::Get(const char *path, const Headers &headers, Progress progress) {\n    Request req;\n    req.method = \"GET\";\n    req.path = path;\n    req.headers = headers;\n    req.progress = std::move(progress);\n\n    auto res = std::make_shared<Response>();\n    return send(req, *res) ? res : nullptr;\n}\n\nstd::shared_ptr<Response> Client::Get(const char *path, ContentReceiver content_receiver) {\n    return Get(path, Headers(), nullptr, std::move(content_receiver), Progress());\n}\n\nstd::shared_ptr<Response> Client::Get(const char *path, ContentReceiver content_receiver, Progress progress) {\n    return Get(path, Headers(), nullptr, std::move(content_receiver), std::move(progress));\n}\n\nstd::shared_ptr<Response> Client::Get(const char *path, const Headers &headers, ContentReceiver content_receiver) {\n    return Get(path, headers, nullptr, std::move(content_receiver), Progress());\n}\n\nstd::shared_ptr<Response> Client::Get(const char *path,\n                                      const Headers &headers,\n                                      ContentReceiver content_receiver,\n                                      Progress progress) {\n    return Get(path, headers, nullptr, std::move(content_receiver), std::move(progress));\n}\n\nstd::shared_ptr<Response> Client::Get(const char *path,\n                                      const Headers &headers,\n                                      ResponseHandler response_handler,\n                                      ContentReceiver content_receiver) {\n    return Get(path, headers, std::move(response_handler), content_receiver, Progress());\n}\n\nstd::shared_ptr<Response> Client::Get(const char *path,\n                                      const Headers &headers,\n                                      ResponseHandler response_handler,\n                                      ContentReceiver content_receiver,\n                                      Progress progress) {\n    Request req;\n    req.method = \"GET\";\n    req.path = path;\n    req.headers = headers;\n    req.response_handler = std::move(response_handler);\n    req.content_receiver = std::move(content_receiver);\n    req.progress = std::move(progress);\n\n    auto res = std::make_shared<Response>();\n    return send(req, *res) ? res : nullptr;\n}\n\nstd::shared_ptr<Response> Client::Head(const char *path) {\n    return Head(path, Headers());\n}\n\nstd::shared_ptr<Response> Client::Head(const char *path, const Headers &headers) {\n    Request req;\n    req.method = \"HEAD\";\n    req.headers = headers;\n    req.path = path;\n\n    auto res = std::make_shared<Response>();\n\n    return send(req, *res) ? res : nullptr;\n}\n\nstd::shared_ptr<Response> Client::Post(const char *path) {\n    return Post(path, std::string(), nullptr);\n}\n\nstd::shared_ptr<Response> Client::Post(const char *path, const std::string &body, const char *content_type) {\n    return Post(path, Headers(), body, content_type);\n}\n\nstd::shared_ptr<Response> Client::Post(const char *path,\n                                       const Headers &headers,\n                                       const std::string &body,\n                                       const char *content_type) {\n    return send_with_content_provider(\"POST\", path, headers, body, 0, nullptr, content_type);\n}\n\nstd::shared_ptr<Response> Client::Post(const char *path, const Params &params) {\n    return Post(path, Headers(), params);\n}\n\nstd::shared_ptr<Response> Client::Post(const char *path,\n                                       size_t content_length,\n                                       ContentProvider content_provider,\n                                       const char *content_type) {\n    return Post(path, Headers(), content_length, content_provider, content_type);\n}\n\nstd::shared_ptr<Response> Client::Post(const char *path,\n                                       const Headers &headers,\n                                       size_t content_length,\n                                       ContentProvider content_provider,\n                                       const char *content_type) {\n    return send_with_content_provider(\n        \"POST\", path, headers, std::string(), content_length, content_provider, content_type);\n}\n\nstd::shared_ptr<Response> Client::Post(const char *path, const Headers &headers, const Params &params) {\n    auto query = detail::params_to_query_str(params);\n    return Post(path, headers, query, \"application/x-www-form-urlencoded\");\n}\n\nstd::shared_ptr<Response> Client::Post(const char *path, const MultipartFormDataItems &items) {\n    return Post(path, Headers(), items);\n}\n\nstd::shared_ptr<Response> Client::Post(const char *path, const Headers &headers, const MultipartFormDataItems &items) {\n    auto boundary = detail::make_multipart_data_boundary();\n\n    std::string body;\n\n    for (const auto &item : items) {\n        body += \"--\" + boundary + \"\\r\\n\";\n        body += \"Content-Disposition: form-data; name=\\\"\" + item.name + \"\\\"\";\n        if (!item.filename.empty()) {\n            body += \"; filename=\\\"\" + item.filename + \"\\\"\";\n        }\n        body += \"\\r\\n\";\n        if (!item.content_type.empty()) {\n            body += \"Content-Type: \" + item.content_type + \"\\r\\n\";\n        }\n        body += \"\\r\\n\";\n        body += item.content + \"\\r\\n\";\n    }\n\n    body += \"--\" + boundary + \"--\\r\\n\";\n\n    std::string content_type = \"multipart/form-data; boundary=\" + boundary;\n    return Post(path, headers, body, content_type.c_str());\n}\n\nstd::shared_ptr<Response> Client::Put(const char *path) {\n    return Put(path, std::string(), nullptr);\n}\n\nstd::shared_ptr<Response> Client::Put(const char *path, const std::string &body, const char *content_type) {\n    return Put(path, Headers(), body, content_type);\n}\n\nstd::shared_ptr<Response> Client::Put(const char *path,\n                                      const Headers &headers,\n                                      const std::string &body,\n                                      const char *content_type) {\n    return send_with_content_provider(\"PUT\", path, headers, body, 0, nullptr, content_type);\n}\n\nstd::shared_ptr<Response> Client::Put(const char *path,\n                                      size_t content_length,\n                                      ContentProvider content_provider,\n                                      const char *content_type) {\n    return Put(path, Headers(), content_length, content_provider, content_type);\n}\n\nstd::shared_ptr<Response> Client::Put(const char *path,\n                                      const Headers &headers,\n                                      size_t content_length,\n                                      ContentProvider content_provider,\n                                      const char *content_type) {\n    return send_with_content_provider(\n        \"PUT\", path, headers, std::string(), content_length, content_provider, content_type);\n}\n\nstd::shared_ptr<Response> Client::Put(const char *path, const Params &params) {\n    return Put(path, Headers(), params);\n}\n\nstd::shared_ptr<Response> Client::Put(const char *path, const Headers &headers, const Params &params) {\n    auto query = detail::params_to_query_str(params);\n    return Put(path, headers, query, \"application/x-www-form-urlencoded\");\n}\n\nstd::shared_ptr<Response> Client::Patch(const char *path, const std::string &body, const char *content_type) {\n    return Patch(path, Headers(), body, content_type);\n}\n\nstd::shared_ptr<Response> Client::Patch(const char *path,\n                                        const Headers &headers,\n                                        const std::string &body,\n                                        const char *content_type) {\n    return send_with_content_provider(\"PATCH\", path, headers, body, 0, nullptr, content_type);\n}\n\nstd::shared_ptr<Response> Client::Patch(const char *path,\n                                        size_t content_length,\n                                        ContentProvider content_provider,\n                                        const char *content_type) {\n    return Patch(path, Headers(), content_length, content_provider, content_type);\n}\n\nstd::shared_ptr<Response> Client::Patch(const char *path,\n                                        const Headers &headers,\n                                        size_t content_length,\n                                        ContentProvider content_provider,\n                                        const char *content_type) {\n    return send_with_content_provider(\n        \"PATCH\", path, headers, std::string(), content_length, content_provider, content_type);\n}\n\nstd::shared_ptr<Response> Client::Delete(const char *path) {\n    return Delete(path, Headers(), std::string(), nullptr);\n}\n\nstd::shared_ptr<Response> Client::Delete(const char *path, const std::string &body, const char *content_type) {\n    return Delete(path, Headers(), body, content_type);\n}\n\nstd::shared_ptr<Response> Client::Delete(const char *path, const Headers &headers) {\n    return Delete(path, headers, std::string(), nullptr);\n}\n\nstd::shared_ptr<Response> Client::Delete(const char *path,\n                                         const Headers &headers,\n                                         const std::string &body,\n                                         const char *content_type) {\n    Request req;\n    req.method = \"DELETE\";\n    req.headers = headers;\n    req.path = path;\n\n    if (content_type) {\n        req.headers.emplace(\"Content-Type\", content_type);\n    }\n    req.body = body;\n\n    auto res = std::make_shared<Response>();\n\n    return send(req, *res) ? res : nullptr;\n}\n\nstd::shared_ptr<Response> Client::Options(const char *path) {\n    return Options(path, Headers());\n}\n\nstd::shared_ptr<Response> Client::Options(const char *path, const Headers &headers) {\n    Request req;\n    req.method = \"OPTIONS\";\n    req.path = path;\n    req.headers = headers;\n\n    auto res = std::make_shared<Response>();\n\n    return send(req, *res) ? res : nullptr;\n}\n\nsize_t Client::is_socket_open() const {\n    std::lock_guard<std::mutex> guard(socket_mutex_);\n    return socket_.is_open();\n}\n\nvoid Client::stop() {\n    std::lock_guard<std::mutex> guard(socket_mutex_);\n    if (socket_.is_open()) {\n        detail::shutdown_socket(socket_.sock);\n        std::this_thread::sleep_for(std::chrono::milliseconds(1));\n        close_socket(socket_, true);\n        std::this_thread::sleep_for(std::chrono::milliseconds(1));\n    }\n}\n\nvoid Client::set_timeout_sec(time_t timeout_sec) {\n    set_connection_timeout(timeout_sec, 0);\n}\n\nvoid Client::set_connection_timeout(time_t sec, time_t usec) {\n    connection_timeout_sec_ = sec;\n    connection_timeout_usec_ = usec;\n}\n\nvoid Client::set_read_timeout(time_t sec, time_t usec) {\n    read_timeout_sec_ = sec;\n    read_timeout_usec_ = usec;\n}\n\nvoid Client::set_write_timeout(time_t sec, time_t usec) {\n    write_timeout_sec_ = sec;\n    write_timeout_usec_ = usec;\n}\n\nvoid Client::set_basic_auth(const char *username, const char *password) {\n    basic_auth_username_ = username;\n    basic_auth_password_ = password;\n}\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\nvoid Client::set_digest_auth(const char *username, const char *password) {\n    digest_auth_username_ = username;\n    digest_auth_password_ = password;\n}\n#endif\n\nvoid Client::set_keep_alive(bool on) {\n    keep_alive_ = on;\n}\n\nvoid Client::set_follow_location(bool on) {\n    follow_location_ = on;\n}\n\nvoid Client::set_tcp_nodelay(bool on) {\n    tcp_nodelay_ = on;\n}\n\nvoid Client::set_socket_options(SocketOptions socket_options) {\n    socket_options_ = socket_options;\n}\n\nvoid Client::set_compress(bool on) {\n    compress_ = on;\n}\n\nvoid Client::set_decompress(bool on) {\n    decompress_ = on;\n}\n\nvoid Client::set_interface(const char *intf) {\n    interface_ = intf;\n}\n\nvoid Client::set_proxy(const char *host, int port) {\n    proxy_host_ = host;\n    proxy_port_ = port;\n}\n\nvoid Client::set_proxy_basic_auth(const char *username, const char *password) {\n    proxy_basic_auth_username_ = username;\n    proxy_basic_auth_password_ = password;\n}\n\nvoid Client::set_websocket_mask(bool on) {\n    websocket_mask_ = on;\n}\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\nvoid Client::set_proxy_digest_auth(const char *username, const char *password) {\n    proxy_digest_auth_username_ = username;\n    proxy_digest_auth_password_ = password;\n}\n#endif\n\nvoid Client::set_logger(Logger logger) {\n    logger_ = std::move(logger);\n}\n\n}  // namespace httplib\n"
  },
  {
    "path": "core-tests/src/_lib/process.cpp",
    "content": "#include \"test_process.h\"\n\nusing swoole::UnixSocket;\nusing swoole::test::Process;\n\nProcess::Process(std::function<void(Process *)> fn, int pipe_type) : handler(fn) {\n    if (pipe_type > 0) {\n        auto pipe = new UnixSocket(true, SOCK_DGRAM);\n\n        worker.pipe_master = pipe->get_socket(true);\n        worker.pipe_worker = pipe->get_socket(false);\n\n        worker.pipe_object = pipe;\n        worker.pipe_current = worker.pipe_master;\n    }\n}\n\nProcess::~Process() {\n    if (worker.pipe_object) {\n        delete worker.pipe_object;\n    }\n}\n\npid_t Process::start() {\n    // std::system(\"ls /proc/self/task\");\n    pid_t pid = swoole_fork(0);\n    if (pid < 0) {\n        printf(\"[Worker] Fatal Error: fork() failed\");\n        exit(1);\n    } else if (pid == 0) {\n        worker.child_process = 1;\n        worker.pipe_current = worker.pipe_worker;\n        handler(this);\n        exit(0);\n    } else {\n        worker.pid = pid;\n        worker.child_process = 0;\n        return pid;\n    }\n}\n\nssize_t Process::write(const void *__buf, size_t __n) {\n    return worker.pipe_current->write(__buf, __n);\n}\n\nssize_t Process::read(void *__buf, size_t __nbytes) {\n    return worker.pipe_current->read(__buf, __nbytes);\n}\n"
  },
  {
    "path": "core-tests/src/_lib/redis.cpp",
    "content": "#include \"redis_client.h\"\n#include \"test_core.h\"\n\n#include <memory>\n\nusing namespace std;\n\nnamespace swoole {\nbool RedisClient::Connect(const string &host, int port, struct timeval timeout) {\n    redisContext *c = redisConnectWithTimeout(host.c_str(), port, timeout);\n    if (c == NULL) {\n        printf(\"Connection error: can't allocate redis context\\n\");\n        return false;\n    }\n\n    if (c->err) {\n        printf(\"Connection error: %s\\n\", c->errstr);\n        redisFree(c);\n        return false;\n    }\n\n    ctx = c;\n    return true;\n}\n\nstring RedisClient::Get(const string &key) {\n    const char *argv[] = {\"GET\", key.c_str()};\n    size_t argvlen[] = {strlen(argv[0]), key.length()};\n\n    auto reply = Request(SW_ARRAY_SIZE(argv), argv, argvlen);\n    if (!reply.empty() && reply->str) {\n        return string(reply->str, reply->len);\n    } else {\n        return \"\";\n    }\n}\n\nlong RedisClient::Ttl(const std::string &key) {\n    const char *argv[] = {\"TTL\", key.c_str()};\n    size_t argvlen[] = {strlen(argv[0]), key.length()};\n\n    auto reply = Request(SW_ARRAY_SIZE(argv), argv, argvlen);\n    if (!reply.empty() && reply->integer) {\n        return reply->integer;\n    } else {\n        return 0;\n    }\n}\n\nbool RedisClient::Select(int db) {\n    auto _db = std::to_string(db);\n    const char *argv[] = {\"SELECT\", _db.c_str()};\n    size_t argvlen[] = {strlen(argv[0]), _db.length()};\n\n    auto reply = Request(SW_ARRAY_SIZE(argv), argv, argvlen);\n    if (!reply.empty() && reply->type == REDIS_REPLY_STATUS && strncmp(reply->str, \"OK\", 2) == 0) {\n        return true;\n    } else {\n        return false;\n    }\n}\n\nstd::string RedisClient::Role() {\n    const char *argv[] = {\"ROLE\"};\n    size_t argvlen[] = {strlen(argv[0])};\n\n    auto reply = Request(SW_ARRAY_SIZE(argv), argv, argvlen);\n    if (!reply.empty() && reply->str) {\n        return string(reply->str, reply->len);\n    } else {\n        return \"\";\n    }\n}\n\nbool RedisClient::Set(const string &key, const string &value) {\n    const char *argv[] = {\"SET\", key.c_str(), value.c_str()};\n    size_t argvlen[] = {strlen(argv[0]), key.length(), value.length()};\n\n    auto reply = Request(SW_ARRAY_SIZE(argv), argv, argvlen);\n    if (!reply.empty() && reply->type == REDIS_REPLY_STATUS && strncmp(reply->str, \"OK\", 2) == 0) {\n        return true;\n    } else {\n        return false;\n    }\n}\n\nRedisReply RedisClient::Request(int argc, const char **argv, const size_t *argvlen) {\n    return redisCommandArgv(ctx, argc, argv, argvlen);\n}\n\nRedisReply RedisClient::Request(const vector<string> &args) {\n    ctx->err = 0;\n\n    size_t n = args.size();\n    const char **argv = new const char *[n];\n    size_t *argvlen = new size_t[n];\n\n    for (size_t i = 0; i < args.size(); i++) {\n        argv[i] = args[i].c_str();\n        argvlen[i] = args[i].length();\n    }\n\n    auto reply = Request(args.size(), (const char **) argv, (const size_t *) argvlen);\n\n    delete[] argv;\n    delete[] argvlen;\n\n    return reply;\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "core-tests/src/_lib/server.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"test_server.h\"\n#include \"swoole_memory.h\"\n\n#include <algorithm>\n#include <cctype>\n\nusing namespace swoole::test;\nusing swoole::network::Address;\n\nServer::Server(std::string _host, int _port, swoole::Server::Mode _mode, int _type)\n    : serv(_mode), host(_host), port(_port), mode(_mode), type(_type) {\n    serv.worker_num = 1;\n\n    if (mode == swoole::Server::MODE_BASE) {\n        serv.reactor_num = 1;\n        serv.worker_num = 1;\n    }\n\n    serv.dispatch_mode = 2;\n    serv.private_data_2 = this;\n\n    if (!listen(host, port, (swSocketType) type)) {\n        swoole_sys_warning(\"listen(%s:%d) failed\", host.c_str(), port);\n        exit(0);\n    }\n\n    if (serv.create() < 0) {\n        swoole_sys_warning(\"create server failed\");\n        exit(0);\n    }\n}\n\nServer::~Server() {}\n\nstd::string Server::tolower(const std::string &str) {\n    std::string str_copy = str;\n    std::transform(str_copy.begin(), str_copy.end(), str_copy.begin(), [](unsigned char c) { return std::tolower(c); });\n    return str_copy;\n}\n\nvoid Server::on(const std::string &_event, const std::function<void(swServer *, swoole::Worker *)> &fn) {\n    auto event = tolower(_event);\n    if (event == \"workerstart\") {\n        serv.onWorkerStart = fn;\n    } else if (event == \"workerstop\") {\n        serv.onWorkerStop = fn;\n    }\n}\n\nvoid Server::on(const std::string &_event, const std::function<void(swServer *)> &fn) {\n    auto event = tolower(_event);\n    if (event == \"start\") {\n        serv.onStart = fn;\n    } else if (event == \"shutdown\") {\n        serv.onShutdown = fn;\n    }\n}\n\nvoid Server::on(const std::string &_event, const std::function<void(swServer *, EventData *)> &fn) {\n    auto event = tolower(_event);\n    if (event == \"pipemessage\") {\n        serv.onPipeMessage = fn;\n    }\n}\n\nvoid Server::on(const std::string &_event, const std::function<int(swServer *, EventData *)> &fn) {\n    auto event = tolower(_event);\n    if (event == \"task\") {\n        serv.onTask = fn;\n    } else if (event == \"finish\") {\n        serv.onFinish = fn;\n    }\n}\n\nvoid Server::on(const std::string &_event, const std::function<int(swServer *, RecvData *)> &fn) {\n    auto event = tolower(_event);\n    if (event == \"packet\") {\n        serv.onPacket = fn;\n    } else if (event == \"receive\") {\n        serv.onReceive = fn;\n    }\n}\n\nvoid Server::on(const std::string &_event, const std::function<void(swServer *, DataHead *)> &fn) {\n    auto event = tolower(_event);\n    if (event == \"connect\") {\n        serv.onConnect = fn;\n    } else if (event == \"close\") {\n        serv.onClose = fn;\n    }\n}\n\nbool Server::start() {\n    return serv.start() == 0;\n}\n\nbool Server::listen(const std::string &host, int port, enum swSocketType type) {\n    ListenPort *ls = serv.add_port(type, (char *) host.c_str(), port);\n    if (ls == nullptr) {\n        return false;\n    }\n\n    ports.push_back(ls);\n    return true;\n}\n\nint Server::send(int session_id, const void *data, uint32_t length) {\n    return serv.send(session_id, data, length);\n}\n\nssize_t Server::sendto(const Address &address, const char *__buf, size_t __n, int server_socket_fd) {\n    network::Socket *server_socket;\n    if (server_socket_fd < 0) {\n        server_socket = serv.udp_socket_ipv6 ? serv.udp_socket_ipv6 : serv.udp_socket_ipv4;\n    } else {\n        server_socket = serv.get_server_socket(server_socket_fd);\n    }\n    return server_socket->sendto(address, __buf, __n, 0);\n}\n\nint Server::close(int session_id, int reset) {\n    return serv.close(session_id, reset);\n}\n"
  },
  {
    "path": "core-tests/src/_lib/ssl.cpp",
    "content": "#include \"test_core.h\"\n\nnamespace swoole {\nnamespace test {\nvoid printAllSubjectEntries(X509_NAME *name) {\n    if (!name) return;\n\n    int entry_count = X509_NAME_entry_count(name);\n\n    for (int i = 0; i < entry_count; ++i) {\n        X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i);\n        ASN1_OBJECT *obj = X509_NAME_ENTRY_get_object(entry);\n        ASN1_STRING *data = X509_NAME_ENTRY_get_data(entry);\n\n        // 获取字段的短名称（如 CN、O、ST 等）\n        char obj_txt[80] = {0};\n        OBJ_obj2txt(obj_txt, sizeof(obj_txt), obj, 0);\n\n        // 获取字段的值\n        unsigned char *utf8 = nullptr;\n        int length = ASN1_STRING_to_UTF8(&utf8, data);\n        if (length >= 0 && utf8) {\n            sw_printf(\"%s: %.*s\\n\", obj_txt, length, utf8);\n            OPENSSL_free(utf8);\n        }\n    }\n}\n\nvoid printX509Info(X509 *cert) {\n    X509_NAME *subject_name = X509_get_subject_name(cert);\n    printAllSubjectEntries(subject_name);\n\n    char *subject = X509_NAME_oneline(subject_name, 0, 0);\n    if (subject) {\n        sw_printf(\"Peer certificate subject: %s\\n\", subject);\n        OPENSSL_free(subject);\n    }\n\n    X509_NAME *issuer_name = X509_get_issuer_name(cert);\n    printAllSubjectEntries(issuer_name);\n\n    // 获取证书有效期\n    ASN1_TIME *not_before = X509_get_notBefore(cert);\n    ASN1_TIME *not_after = X509_get_notAfter(cert);\n\n    BIO *bio = BIO_new(BIO_s_mem());\n    ASN1_TIME_print(bio, not_before);\n    char buf[256] = {0};\n    int len = BIO_read(bio, buf, sizeof(buf) - 1);\n    buf[len] = 0;\n    sw_printf(\"Validity Not Before:  %s\\n\", buf);\n\n    ASN1_TIME_print(bio, not_after);\n    len = BIO_read(bio, buf, sizeof(buf) - 1);\n    buf[len] = 0;\n    sw_printf(\"Validity Not After:   %s\\n\", buf);\n\n    BIO_free(bio);\n\n    // 获取公钥\n    EVP_PKEY *pubkey = X509_get_pubkey(cert);\n    if (pubkey) {\n        sw_printf(\"Public key type: %d\\n\", EVP_PKEY_id(pubkey));\n        EVP_PKEY_free(pubkey);\n    }\n}\n\nint dump_cert_info(const char *data, size_t len) {\n    BIO *bio = BIO_new_mem_buf(data, (int) len);\n    if (!bio) {\n        std::cerr << \"Failed to create BIO\" << std::endl;\n        return 1;\n    }\n\n    // 从 BIO 中读取证书\n    X509 *cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);\n    if (!cert) {\n        std::cerr << \"Failed to parse X509 certificate\" << std::endl;\n        BIO_free(bio);\n        return 1;\n    }\n\n    // 打印证书信息\n    printX509Info(cert);\n\n    // 释放资源\n    X509_free(cert);\n    BIO_free(bio);\n    EVP_cleanup();\n\n    return 0;\n}\n}  // namespace test\n}  // namespace swoole\n"
  },
  {
    "path": "core-tests/src/core/base.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"swoole_server.h\"\n#include \"swoole_file.h\"\n#include \"swoole_util.h\"\n#include \"swoole.h\"\n#include \"swoole_config.h\"\n\n#include \"swoole_api.h\"\n\n#include <sys/resource.h>\n\nusing namespace swoole;\nusing namespace std;\n\nstatic const string test_data(\"hello world\\n\");\n\nTEST(base, datahead_dump) {\n    swDataHead data = {};\n    data.fd = 123;\n    char buf[128];\n    size_t n = data.dump(buf, sizeof(buf));\n    data.print();\n\n    ASSERT_GT(std::string(buf, n).find(\"int fd = 123;\"), 1);\n}\n\nTEST(base, dec2hex) {\n    auto result = swoole_dec2hex(2684326179, 16);\n    ASSERT_STREQ(result, \"9fff9123\");\n    sw_free(result);\n}\n\nTEST(base, hex2dec) {\n    size_t n_parsed;\n    ASSERT_EQ(swoole_hex2dec(\"9fff9123\", &n_parsed), 2684326179);\n    ASSERT_EQ(n_parsed, 8);\n    ASSERT_EQ(swoole_hex2dec(\"0x9fff9123\", &n_parsed), 2684326179);\n    ASSERT_EQ(n_parsed, 10);\n    ASSERT_EQ(swoole_hex2dec(\"f\", &n_parsed), 15);\n    ASSERT_EQ(n_parsed, 1);\n}\n\nTEST(base, random_string) {\n    char buf[1024] = {};\n    swoole_random_string(buf, sizeof(buf) - 1);\n    ASSERT_EQ(strlen(buf), sizeof(buf) - 1);\n}\n\nstatic size_t test_sw_vsnprintf(char *buf, size_t size, const char *format, ...) {\n    va_list args;\n    va_start(args, format);\n    size_t result = sw_vsnprintf(buf, size, format, args);\n    va_end(args);\n    return result;\n}\n\nTEST(base, file_put_contents) {\n    char buf[65536];\n    swoole_random_string(buf, sizeof(buf) - 1);\n    ASSERT_TRUE(file_put_contents(TEST_TMP_FILE, buf, sizeof(buf)));\n    auto result = file_get_contents(TEST_TMP_FILE);\n    ASSERT_STREQ(buf, result->value());\n}\n\nTEST(base, file_get_size) {\n    File f(TEST_TMP_FILE, File::WRITE | File::CREATE);\n    char buf[65536];\n    swoole_random_string(buf, sizeof(buf) - 1);\n\n    ASSERT_TRUE(f.ready());\n    f.truncate(0);\n    f.set_offset(0);\n    f.write(buf, sizeof(buf) - 1);\n    f.close();\n\n    ASSERT_EQ(file_get_size(TEST_TMP_FILE), sizeof(buf) - 1);\n}\n\nTEST(base, version_compare) {\n    ASSERT_EQ(swoole_version_compare(\"1.2.1\", \"1.2.0\"), 1);\n    ASSERT_EQ(swoole_version_compare(\"1.2.3\", \"1.3.0\"), -1);\n    ASSERT_EQ(swoole_version_compare(\"1.2.3\", \"1.2.9\"), -1);\n    ASSERT_EQ(swoole_version_compare(\"1.2.0\", \"1.2.0\"), 0);\n}\n\nTEST(base, common_divisor) {\n    ASSERT_EQ(swoole_common_divisor(16, 12), 4);\n    ASSERT_EQ(swoole_common_divisor(6, 15), 3);\n    ASSERT_EQ(swoole_common_divisor(32, 16), 16);\n}\n\nTEST(base, common_multiple) {\n    ASSERT_EQ(swoole_common_multiple(16, 12), 48);\n    ASSERT_EQ(swoole_common_multiple(6, 15), 30);\n    ASSERT_EQ(swoole_common_multiple(32, 16), 32);\n}\n\nTEST(base, shell_exec) {\n    pid_t pid;\n    string str = \"md5sum \" + test::get_jpg_file();\n    int _pipe = swoole_shell_exec(str.c_str(), &pid, 0);\n    ASSERT_GT(_pipe, 0);\n    ASSERT_GT(pid, 0);\n    char buf[1024] = {};\n    ssize_t n = read(_pipe, buf, sizeof(buf) - 1);\n    ASSERT_GT(n, 0);\n    ASSERT_STREQ(string(buf).substr(0, sizeof(TEST_JPG_MD5SUM) - 1).c_str(), TEST_JPG_MD5SUM);\n    close(_pipe);\n\n    str = \"md5sum test.abcdef\";\n    _pipe = swoole_shell_exec(str.c_str(), &pid, 1);\n    memset(buf, 0, sizeof(buf));\n    ssize_t length = 0;\n    while (1) {\n        n = read(_pipe, buf + length, sizeof(buf) - 1 - length);\n        length += n;\n        if (n > 0) {\n            continue;\n        }\n        break;\n    }\n    ASSERT_GT(length, 0);\n\n    ASSERT_STREQ(buf, string(\"md5sum: test.abcdef: No such file or directory\\n\").c_str());\n    close(_pipe);\n}\n\nTEST(base, file_size) {\n    auto file = test::get_jpg_file();\n    ssize_t file_size = file_get_size(file);\n    ASSERT_GT(file_size, 0);\n    auto fp = fopen(file.c_str(), \"r+\");\n    ASSERT_TRUE(fp);\n    ASSERT_EQ(file_get_size(fp), file_size);\n    fclose(fp);\n}\n\nTEST(base, eventdata_pack) {\n    EventData ed1{};\n\n    ASSERT_TRUE(Server::task_pack(&ed1, test_data.c_str(), test_data.length()));\n    ASSERT_EQ(string(ed1.data, ed1.info.len), test_data);\n\n    EventData ed2{};\n    ASSERT_EQ(swoole_random_bytes(sw_tg_buffer()->str, SW_BUFFER_SIZE_BIG), SW_BUFFER_SIZE_BIG);\n    ASSERT_TRUE(Server::task_pack(&ed2, sw_tg_buffer()->str, SW_BUFFER_SIZE_BIG));\n\n    String _buffer(SW_BUFFER_SIZE_BIG);\n    PacketPtr packet;\n    ASSERT_TRUE(Server::task_unpack(&ed2, &_buffer, &packet));\n    ASSERT_EQ(memcmp(sw_tg_buffer()->str, _buffer.str, SW_BUFFER_SIZE_BIG), 0);\n}\n\nTEST(base, stack_defer_fn) {\n    int count = 0;\n\n    ON_SCOPE_EXIT {\n        count++;\n        ASSERT_EQ(count, 2);\n    };\n\n    ON_SCOPE_EXIT {\n        count++;\n        ASSERT_EQ(count, 1);\n    };\n}\n\nTEST(base, string_format) {\n    char *data = swoole_string_format(128, \"hello %d world, %s is best.\", 2020, \"swoole\");\n    ASSERT_STREQ(data, \"hello 2020 world, swoole is best.\");\n    sw_free(data);\n}\n\nTEST(base, dirname) {\n    ASSERT_EQ(dirname(\"/hello/world/index.html.abc\"), \"/hello/world\");\n    ASSERT_EQ(dirname(\"/hello/world\"), \"/hello\");\n    ASSERT_EQ(dirname(\"/root\"), \"/\");\n    ASSERT_EQ(dirname(\"/\"), \"/\");\n}\n\nTEST(base, mkdir_recursive) {\n    String dir(PATH_MAX + 2);\n    dir.append_random_bytes(PATH_MAX, true);\n    ASSERT_FALSE(swoole_mkdir_recursive(dir.to_std_string()));\n}\n\nTEST(base, set_task_tmpdir) {\n    auto ori_tmpdir = swoole_get_task_tmpdir();\n    ASSERT_FALSE(swoole_set_task_tmpdir(\"aaa\"));\n\n    size_t length = SW_TASK_TMP_PATH_SIZE + 1;\n    char too_long_dir[SW_TASK_TMP_PATH_SIZE + 2] = {};\n    swoole_random_string(too_long_dir + 1, length - 1);\n    too_long_dir[0] = '/';\n    ASSERT_FALSE(swoole_set_task_tmpdir(too_long_dir));\n\n    const char *tmpdir = \"/tmp/swoole/core_tests/base\";\n    ASSERT_TRUE(swoole_set_task_tmpdir(tmpdir));\n    File fp = make_tmpfile();\n    ASSERT_TRUE(fp.ready());\n\n    char buf[128];\n    swoole_random_string(buf, sizeof(buf) - 2);\n    buf[sizeof(buf) - 2] = '\\n';\n    buf[sizeof(buf) - 1] = '\\0';\n\n    fp.write(buf, sizeof(buf) - 1);\n    fp.close();\n\n    ASSERT_EQ(swoole::dirname(fp.get_path()), tmpdir);\n    ASSERT_STREQ(swoole::file_get_contents(fp.get_path())->str, buf);\n\n    unlink(fp.get_path().c_str());\n    rmdir(tmpdir);\n\n    char buf2[264];\n    swoole_random_string(buf2, sizeof(buf2) - 1);\n    memcpy(buf2, \"/tmp/\", 5);\n    buf2[64] = '/';\n    buf2[128] = '/';\n    buf2[192] = '/';\n    buf2[256] = '/';\n    std::string dir(buf2);\n    ASSERT_FALSE(swoole_set_task_tmpdir(dir));\n\n    test::recursive_rmdir(dir.c_str());\n\n    ASSERT_TRUE(swoole_set_task_tmpdir(ori_tmpdir));\n}\n\nTEST(base, version) {\n    ASSERT_STREQ(swoole_version(), SWOOLE_VERSION);\n    ASSERT_EQ(swoole_version_id(), SWOOLE_VERSION_ID);\n    ASSERT_EQ(swoole_api_version_id(), SWOOLE_API_VERSION_ID);\n}\n\nTEST(base, hook) {\n    int count = 0;\n    swoole_add_hook(\n        SW_GLOBAL_HOOK_END,\n        [](void *data) -> void {\n            int *_count = (int *) data;\n            *_count = 9999;\n        },\n        1);\n    ASSERT_TRUE(swoole_isset_hook(SW_GLOBAL_HOOK_END));\n    swoole_call_hook(SW_GLOBAL_HOOK_END, &count);\n    ASSERT_EQ(count, 9999);\n}\n\nTEST(base, intersection) {\n    std::vector<std::string> vec1{\"index.php\", \"index.html\", \"default.html\"};\n\n    std::set<std::string> vec2{\".\", \"..\", \"default.html\", \"index.php\", \"test.html\", \"a.json\", \"index.php\"};\n    ASSERT_EQ(\"index.php\", swoole::intersection(vec1, vec2));\n\n    std::set<std::string> vec3{\"a\", \"zh中\", \"、r\\n\"};\n    ASSERT_EQ(\"\", swoole::intersection(vec1, vec3));\n}\n\nTEST(base, itoa) {\n    char buf[128];\n    long value = 123456987;\n    int n = swoole_itoa(buf, value);\n\n    ASSERT_EQ(n, 9);\n    ASSERT_STREQ(buf, \"123456987\");\n}\n\nTEST(base, get_systemd_listen_fds) {\n    ASSERT_EQ(swoole_get_systemd_listen_fds(), -1);\n    setenv(\"LISTEN_FDS\", to_string(SW_MAX_LISTEN_PORT + 1).c_str(), 1);\n    ASSERT_EQ(swoole_get_systemd_listen_fds(), -1);\n    setenv(\"LISTEN_FDS\", to_string(SW_MAX_LISTEN_PORT - 1).c_str(), 1);\n    ASSERT_EQ(swoole_get_systemd_listen_fds(), SW_MAX_LISTEN_PORT - 1);\n}\n\nTEST(base, type_size) {\n    ASSERT_EQ(swoole_type_size('c'), 1);\n    ASSERT_EQ(swoole_type_size('s'), 2);\n    ASSERT_EQ(swoole_type_size('l'), 4);\n    ASSERT_EQ(swoole_type_size('b'), 0);\n    ASSERT_EQ(swoole_type_size('q'), 8);\n    ASSERT_EQ(swoole_type_size('P'), 8);\n}\n\nsize_t swoole_fatal_error_impl(const char *format, ...) {\n    size_t retval = 0;\n    va_list args;\n    va_start(args, format);\n\n    char buf[128];\n    retval += sw_vsnprintf(buf, 128, format, args);\n    va_end(args);\n    return retval;\n}\n\nTEST(base, vsnprintf) {\n    ASSERT_GT(swoole_fatal_error_impl(\"Hello %s\", \"World!!!\"), 0);\n\n    char buffer[10];\n    {\n        // The 9th byte will be set to \\ 0, discarding one character\n        size_t result = test_sw_vsnprintf(buffer, 9, \"Test %d\", 1234);\n        EXPECT_STREQ(buffer, \"Test 123\");\n        EXPECT_EQ(result, 8);\n    }\n\n    {\n        size_t result = test_sw_vsnprintf(buffer, sizeof(buffer), \"Test %d is too long\", 12345);\n        EXPECT_EQ(buffer[sizeof(buffer) - 1], '\\0');\n        EXPECT_EQ(result, sizeof(buffer) - 1);\n        EXPECT_STREQ(buffer, \"Test 1234\");\n    }\n}\n\nTEST(base, snprintf) {\n    char buffer[10];\n    {\n        // The 9th byte will be set to \\ 0, discarding one character\n        size_t result = sw_snprintf(buffer, 9, \"Test %d\", 1234);\n        EXPECT_STREQ(buffer, \"Test 123\");\n        EXPECT_EQ(result, 8);\n    }\n\n    {\n        size_t result = sw_snprintf(buffer, sizeof(buffer), \"Test %d is too long\", 12345);\n        EXPECT_EQ(buffer[sizeof(buffer) - 1], '\\0');\n        EXPECT_EQ(result, sizeof(buffer) - 1);\n        EXPECT_STREQ(buffer, \"Test 1234\");\n    }\n}\n\nTEST(base, log_level) {\n    int level = sw_logger()->get_level();\n    swoole_set_log_level(SW_LOG_TRACE);\n    swoole_print_backtrace();\n    EXPECT_EQ(SW_LOG_TRACE, sw_logger()->get_level());\n    swoole_set_log_level(level);\n}\n\nTEST(base, trace_flag) {\n    int flags = SwooleG.trace_flags;\n    swoole_set_trace_flags(SW_TRACE_CARES);\n    EXPECT_EQ(SW_TRACE_CARES, SwooleG.trace_flags);\n    swoole_set_trace_flags(flags);\n}\n\nTEST(base, only_dump) {\n    // just dump something\n    std::string data = \"hello world\";\n    swoole_dump_ascii(data.c_str(), data.length());\n    swoole_dump_bin((uchar *) data.c_str(), 'C', data.length());\n    swoole_dump_hex((uchar *) data.c_str(), data.length());\n    ASSERT_TRUE(true);\n}\n\nTEST(base, redirect_stdout) {\n    auto file = TEST_LOG_FILE;\n    auto out_1 = \"hello world, hello swoole!\\n\";\n    auto out_2 = \"write to /dev/null\\n\";\n    auto status = test::spawn_exec_and_wait([&]() {\n        swoole_redirect_stdout(file);\n        printf(\"%s\\n\", out_1);\n        fflush(stdout);\n\n        swoole_redirect_stdout(\"/dev/null\");\n        printf(\"%s\\n\", out_2);\n        fflush(stdout);\n\n        swoole_clear_last_error();\n        swoole_redirect_stdout(\"/tmp/not-exists/test.log\");\n        ASSERT_ERREQ(ENOTDIR);\n    });\n    ASSERT_EQ(status, 0);\n\n    auto rs = swoole::file_get_contents(file);\n    ASSERT_NE(rs, nullptr);\n    ASSERT_TRUE(rs->contains(out_1));\n    ASSERT_FALSE(rs->contains(out_2));\n    unlink(file);\n}\n\nTEST(base, fatal_error) {\n    const char *msg = \"core tests fatal error\";\n    auto status = test::spawn_exec_and_wait([msg]() {\n        swoole_set_log_file(TEST_LOG_FILE);\n        swoole_fatal_error(9999, msg);\n    });\n    ASSERT_EQ(WEXITSTATUS(status), 1);\n\n    auto rs = file_get_contents(TEST_LOG_FILE);\n    ASSERT_NE(rs, nullptr);\n    ASSERT_TRUE(rs->contains(msg));\n    ASSERT_TRUE(rs->contains(\"(ERROR 9999)\"));\n    File::remove(TEST_LOG_FILE);\n}\n\nTEST(base, spinlock) {\n    test::counter_init();\n    auto counter = test::counter_ptr();\n    int n = 4096;\n\n    auto test_fn = [counter, n]() {\n        SW_LOOP_N(n) {\n            sw_spinlock((sw_atomic_t *) &counter[0]);\n            counter[1]++;\n            if (i % 100 == 0) {\n                usleep(5);\n            }\n            sw_spinlock_release((sw_atomic_t *) &counter[0]);\n        }\n    };\n\n    std::thread t1(test_fn);\n    std::thread t2(test_fn);\n\n    t1.join();\n    t2.join();\n\n    ASSERT_EQ(counter[1], n * 2);\n}\n\nTEST(base, futex) {\n    sw_atomic_t value = 1;\n\n    std::thread t1([&value] {\n        DEBUG() << \"wait 1\\n\";\n        ASSERT_EQ(sw_atomic_futex_wait(&value, -1), SW_OK);  // no wait\n        value = 0;\n\n        DEBUG() << \"wait 2\\n\";\n\n        ASSERT_EQ(sw_atomic_futex_wait(&value, 0.05), SW_ERR);  // timed out\n        ASSERT_EQ(sw_atomic_futex_wait(&value, 0.5), SW_OK);    // success\n\n        DEBUG() << \"wait 3\\n\";\n\n        value = 0;\n        ASSERT_EQ(sw_atomic_futex_wait(&value, -1), SW_OK);  // no timeout\n    });\n\n    std::thread t2([&value] {\n        usleep(100000);\n        DEBUG() << \"wakeup 1\\n\";\n        ASSERT_EQ(sw_atomic_futex_wakeup(&value, 1), 1);\n\n        DEBUG() << \"wakeup 2\\n\";\n        usleep(100000);\n        ASSERT_EQ(sw_atomic_futex_wakeup(&value, 1), 1);\n    });\n\n    t1.join();\n    t2.join();\n}\n\nstatic int test_fork_fail(const std::function<void(void)> &after_fork_fail = nullptr) {\n    rlimit rl{};\n    rlim_t ori_nproc_max;\n    int count = 0;\n    rlim_t nproc_max = 32;\n\n    // 获取当前 NPROC 限制\n    if (getrlimit(RLIMIT_NPROC, &rl) != 0) {\n        perror(\"getrlimit failed\");\n        return 1;\n    }\n\n    printf(\"Current NPROC limit: soft=%lu, hard=%lu\\n\", rl.rlim_cur, rl.rlim_max);\n\n    ori_nproc_max = rl.rlim_max;\n    rl.rlim_cur = nproc_max;\n    if (setrlimit(RLIMIT_NPROC, &rl) != 0) {\n        perror(\"setrlimit failed\");\n        return 1;\n    }\n\n    printf(\"New NPROC limit: soft=%lu\\n\", rl.rlim_cur);\n\n    std::vector<pid_t> children;\n\n    // 循环创建子进程直到失败\n    while (true) {\n        pid_t pid = fork();\n        if (pid < 0) {\n            // fork 失败\n            printf(\"fork() failed after %d processes: %s\\n\", count, strerror(errno));\n            break;\n        } else if (pid == 0) {\n            sleep(30);\n            exit(0);\n        } else {\n            // 父进程\n            count++;\n            children.push_back(pid);\n            printf(\"Created child process #%d (PID: %d)\\n\", count, pid);\n        }\n    }\n\n    if (after_fork_fail) {\n        after_fork_fail();\n    }\n\n    printf(\"Cleaning up child processes...\\n\");\n    for (const int i : children) {\n        kill(i, SIGKILL);\n    }\n    test::wait_all_child_processes();\n\n    rl.rlim_cur = ori_nproc_max;\n    // 恢复 NPROC 限制\n    if (setrlimit(RLIMIT_NPROC, &rl) != 0) {\n        perror(\"setrlimit failed\");\n        return 1;\n    }\n\n    return 0;\n}\n\n#if 0\nTEST(base, fork_fail) {\n    auto status = test::spawn_exec_and_wait([]() {\n        if (geteuid() == 0) {\n            Server::worker_set_isolation(\"nobody\", \"nobody\", \"\");\n        }\n        ASSERT_EQ(test_fork_fail([]() {\n                      pid_t pid;\n                      auto pipe_fd = swoole_shell_exec(\"sleep 10\", &pid, 0);\n                      ASSERT_EQ(pipe_fd, -1);\n                  }),\n                  0);\n        ASSERT_ERREQ(EAGAIN);\n    });\n\n    ASSERT_EQ(status, 0);\n}\n#endif\n\nTEST(base, undefined_behavior) {\n    swoole_init();  // no effect\n    delete SwooleG.logger;\n    SwooleG.logger = nullptr;  // avoid double free in swoole_shutdown()\n    ASSERT_EQ(swoole_get_log_level(), SW_LOG_NONE);\n    SwooleG.logger = new Logger();\n}\n"
  },
  {
    "path": "core-tests/src/core/channel.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"swoole_channel.h\"\n\nusing namespace std;\nusing namespace swoole;\n\nconst int N = 10000000;\n\nTEST(channel, push) {\n    auto *c = Channel::make(128 * 1024, 8192, SW_CHAN_LOCK | SW_CHAN_NOTIFY);\n    map<int, string> m;\n\n    size_t bytes = 0;\n    int index = 0;\n\n    while (bytes < N) {\n        char buf[8000];\n        int n = swoole_random_bytes(buf, (rand() % (sizeof(buf) / 2)) + (sizeof(buf) / 2));\n        if (n <= 0) {\n            swoole_trace(\"no enough data, n=%d, errno=%d\\n\", n, errno);\n            continue;\n        }\n        m[index++] = string(buf, n);\n        bytes += n;\n    }\n\n    swoole_trace(\"size=%lu\", m.size());\n\n    thread t1([&]() {\n        auto next = m.find(0);\n        int index = 1;\n        size_t bytes = 0;\n\n        while (bytes < N) {\n            if (c->push(next->second.c_str(), next->second.length()) == SW_OK) {\n                swoole_trace(\"[PUSH] index=%d, size=%lu\", index, next->second.length());\n                bytes += next->second.length();\n                next = m.find(index++);\n                if (next == m.end()) {\n                    break;\n                }\n            } else {\n                usleep(10);\n            }\n        }\n    });\n\n    thread t2([&]() {\n        char buf[8000];\n        size_t bytes = 0;\n        int index = 0;\n        while (bytes < N) {\n            int retval = c->pop(buf, sizeof(buf));\n            if (retval > 0) {\n                swoole_trace(\"[POP] index=%d, size=%d\", index, retval);\n                string &_data = m[index++];\n                bytes += retval;\n                ASSERT_EQ(_data, string(buf, retval));\n            } else {\n                usleep(10);\n            }\n        }\n    });\n\n    t1.join();\n    t2.join();\n\n    c->destroy();\n}\n\nTEST(channel, peek) {\n    char buf[8000];\n    auto *c = Channel::make(128 * 1024, 8192, SW_CHAN_LOCK | SW_CHAN_NOTIFY);\n    ASSERT_EQ(c->peek(buf, sizeof(buf)), SW_ERR);\n\n    string value = \"test\";\n    c->push(value.c_str(), value.length());\n    ASSERT_EQ(c->peek((void *) buf, sizeof(buf)), value.length());\n    c->destroy();\n}\n\nTEST(channel, notify) {\n    auto *c = Channel::make(128 * 1024, 8192, SW_CHAN_LOCK | SW_CHAN_NOTIFY);\n    thread t1([&]() {\n        sleep(0.02);\n        string value = \"test\";\n        c->push(value.c_str(), value.length());\n        c->notify();\n    });\n\n    thread t2([&]() {\n        while (c->wait()) {\n            char buf[8000];\n            ASSERT_GT(c->pop((void *) buf, sizeof(buf)), 0);\n            break;\n        }\n    });\n\n    t1.join();\n    t2.join();\n\n    c->print();\n    c->destroy();\n}\n"
  },
  {
    "path": "core-tests/src/core/hash.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"swoole_hash.h\"\n\nstatic const int hash_count = 8192;\nstatic const int str_max_len = 1024;\nstatic const char *data = \"hello world, PHP  the best.\";\n\nTEST(hash, crc32) {\n    ASSERT_EQ(swoole_crc32(data, strlen(data)), 2962796788);\n}\n\nstatic void test_hash_func(uint64_t (*hash_fn)(const char *key, size_t len), int n) {\n    std::vector<uint64_t> hashes;\n    std::vector<std::string> data;\n    hashes.resize(n);\n    data.resize(n);\n\n    SW_LOOP_N(n) {\n        size_t len = 1 + swoole_random_int() % str_max_len;\n        char buf[str_max_len];\n        ASSERT_EQ(swoole_random_bytes(buf, len), len);\n        hashes[i] = hash_fn(buf, len);\n        data[i] = std::string(buf, len);\n    }\n\n    usleep(100);\n\n    SW_LOOP_N(n) {\n        auto &s = data.at(i);\n        ASSERT_EQ(hashes[i], hash_fn(s.c_str(), s.length()));\n    }\n}\n\nTEST(hash, php) {\n    test_hash_func(swoole_hash_php, hash_count);\n}\n\nTEST(hash, jenkins) {\n    test_hash_func(swoole_hash_jenkins, hash_count);\n}\n\nTEST(hash, austin) {\n    test_hash_func(swoole_hash_austin, hash_count);\n}\n"
  },
  {
    "path": "core-tests/src/core/heap.cpp",
    "content": "#include \"test_core.h\"\n#include \"swoole_heap.h\"\n#include <map>\n\ntypedef struct node_t {\n    int pri;\n    int val;\n} node_t;\n\n#define SIZE 100\n\nTEST(heap, random) {\n    node_t *ns;\n    node_t *n;\n    swoole::Heap pq(SIZE, swoole::Heap::MAX_HEAP);\n    std::map<int, int> _map;\n    ASSERT_EQ(pq.peek(), nullptr);\n\n    int i;\n    for (i = 0; i < SIZE * 2 - 1; i++) {\n        int pri = swoole_system_random(10000, 99999);\n        ns = (node_t *) malloc(sizeof(node_t));\n        ns->val = i;\n        ns->pri = pri;\n        pq.push(pri, ns);\n        _map[i] = pri;\n\n        if (0 == i) {\n            pq.print();  // print once\n        }\n    }\n\n    n = (node_t *) pq.peek();\n    ASSERT_EQ(_map[n->val], n->pri);\n    while ((n = (node_t *) pq.pop())) {\n        ASSERT_EQ(_map[n->val], n->pri);\n        free(n);\n    }\n}\n"
  },
  {
    "path": "core-tests/src/core/log.cpp",
    "content": "#include \"test_core.h\"\n#include \"swoole_file.h\"\n#include \"swoole_process_pool.h\"\n#include <regex>\n#include <vector>\n\nusing namespace swoole;\n\nconst char *file = \"/tmp/swoole_log_test.log\";\n\nTEST(log, level) {\n    std::vector<int> processTypes = {SW_MASTER, SW_MANAGER, SW_WORKER, SW_TASK_WORKER};\n\n    int originType = swoole_get_worker_type();\n    for (auto iter = processTypes.begin(); iter != processTypes.end(); iter++) {\n        swoole_set_worker_type(*iter);\n        sw_logger()->reset();\n\n        ASSERT_FALSE(sw_logger()->is_opened());\n\n        sw_logger()->set_level(999);\n        ASSERT_EQ(sw_logger()->get_level(), SW_LOG_NONE);\n\n        sw_logger()->set_level(SW_LOG_DEBUG - 10);\n        ASSERT_EQ(sw_logger()->get_level(), SW_LOG_DEBUG);\n\n        sw_logger()->set_level(SW_LOG_NOTICE);\n        sw_logger()->open(file);\n\n        ASSERT_TRUE(sw_logger()->is_opened());\n\n        sw_logger()->put(SW_LOG_DEBUG, SW_STRL(\"hello no debug\"));\n        sw_logger()->put(SW_LOG_TRACE, SW_STRL(\"hello no trace\"));\n        sw_logger()->put(SW_LOG_INFO, SW_STRL(\"hello info\"));\n        sw_logger()->put(SW_LOG_NOTICE, SW_STRL(\"hello notice\"));\n        sw_logger()->put(SW_LOG_WARNING, SW_STRL(\"hello warning\"));\n\n        sw_logger()->set_level(SW_LOG_DEBUG);\n        sw_logger()->put(SW_LOG_DEBUG, SW_STRL(\"hello debug\"));\n        sw_logger()->put(SW_LOG_TRACE, SW_STRL(\"hello trace\"));\n\n        auto content = file_get_contents(file);\n\n        sw_logger()->close();\n        unlink(file);\n\n        ASSERT_FALSE(content->contains(SW_STRL(\"hello no debug\")));\n        ASSERT_FALSE(content->contains(SW_STRL(\"hello no trace\")));\n        ASSERT_TRUE(content->contains(SW_STRL(\"hello debug\")));\n        ASSERT_TRUE(content->contains(SW_STRL(\"hello trace\")));\n        ASSERT_FALSE(content->contains(SW_STRL(\"hello info\")));\n        ASSERT_TRUE(content->contains(SW_STRL(\"hello notice\")));\n        ASSERT_TRUE(content->contains(SW_STRL(\"hello warning\")));\n\n        swoole_set_worker_type(originType);\n    }\n}\n\nTEST(log, date_format) {\n    sw_logger()->reset();\n    sw_logger()->set_date_format(\"day %d of %B in the year %Y. Time: %I:%S %p\");\n    sw_logger()->open(file);\n\n    sw_logger()->put(SW_LOG_WARNING, SW_STRL(\"hello world\"));\n    auto content = file_get_contents(file);\n\n    sw_logger()->close();\n    unlink(file);\n\n    int data[16];\n    char *month = nullptr;\n    char *am = nullptr;\n\n    int n = std::sscanf(content->value(),\n                        \"[day %d of %s in the year %d. Time: %d:%d %s @%d.%d]\\tWARNING\\thello world\",\n                        data,\n                        month,\n                        data + 1,\n                        data + 2,\n                        data + 3,\n                        am,\n                        data + 4,\n                        data + 5);\n\n    ASSERT_TRUE(n);\n}\n\nTEST(log, date_format_long_string) {\n    sw_logger()->reset();\n    sw_logger()->set_level(SW_LOG_ERROR);\n    std::unique_ptr<String> content(new String(256));\n    auto str = content.get();\n\n    str->repeat(\"x\", 1, 120);\n    str->append(SW_STRL(\"day %d of %B in the year %Y. Time: %I:%S %p\"));\n\n    bool retval = sw_logger()->set_date_format(str->str);\n\n    ASSERT_FALSE(retval);\n    ASSERT_EQ(swoole_get_last_error(), SW_ERROR_INVALID_PARAMS);\n}\n\nTEST(log, date_with_microseconds) {\n    sw_logger()->reset();\n    sw_logger()->set_date_with_microseconds(true);\n    sw_logger()->open(file);\n\n    sw_logger()->put(SW_LOG_WARNING, SW_STRL(\"hello world\"));\n    auto content = file_get_contents(file);\n\n    sw_logger()->close();\n    unlink(file);\n\n    std::regex e(\"\\\\[\\\\S+\\\\s\\\\d{2}:\\\\d{2}:\\\\d{2}\\\\<\\\\.(\\\\d+)\\\\>\\\\s%\\\\d+\\\\.\\\\d+\\\\]\\tWARNING\\thello world\");\n    ASSERT_TRUE(std::regex_search(content->value(), e));\n}\n\nTEST(log, rotation) {\n    std::vector<int> types = {\n        SW_LOG_ROTATION_DAILY, SW_LOG_ROTATION_EVERY_MINUTE, SW_LOG_ROTATION_HOURLY, SW_LOG_ROTATION_MONTHLY};\n    for (auto iter = types.begin(); iter != types.end(); iter++) {\n        sw_logger()->reset();\n        sw_logger()->set_rotation(*iter);\n        sw_logger()->open(file);\n\n        sw_logger()->put(SW_LOG_DEBUG, SW_STRL(\"hello world\"));\n        sw_logger()->put(SW_LOG_TRACE, SW_STRL(\"hello world\"));\n        sw_logger()->put(SW_LOG_NOTICE, SW_STRL(\"hello world\"));\n        sw_logger()->put(SW_LOG_WARNING, SW_STRL(\"hello world\"));\n        sw_logger()->put(SW_LOG_ERROR, SW_STRL(\"hello world\"));\n        sw_logger()->put(SW_LOG_INFO, SW_STRL(\"hello world\"));\n\n        ASSERT_EQ(access(sw_logger()->get_file(), R_OK), -1);\n        ASSERT_EQ(errno, ENOENT);\n        ASSERT_EQ(access(sw_logger()->get_real_file(), R_OK), 0);\n\n        sw_logger()->close();\n        unlink(sw_logger()->get_real_file());\n    }\n}\n\nTEST(log, redirect_1) {\n    auto status = test::spawn_exec_and_wait([]() {\n        sw_logger()->reset();\n        ASSERT_FALSE(sw_logger()->redirect_stdout_and_stderr(true));   // no log file opened\n        ASSERT_FALSE(sw_logger()->redirect_stdout_and_stderr(false));  // no redirected\n\n        ASSERT_TRUE(sw_logger()->open(file));\n        ASSERT_TRUE(sw_logger()->redirect_stdout_and_stderr(true));\n        ASSERT_FALSE(sw_logger()->redirect_stdout_and_stderr(true));  // has been redirected\n\n        printf(\"hello world\\n\");\n        auto content = file_get_contents(file);\n        ASSERT_NE(content.get(), nullptr);\n\n        sw_logger()->close();\n        ASSERT_TRUE(sw_logger()->redirect_stdout_and_stderr(false));\n        unlink(sw_logger()->get_real_file());\n\n        ASSERT_TRUE(content->contains(SW_STRL(\"hello world\\n\")));\n    });\n\n    ASSERT_EQ(status, 0);\n}\n\nTEST(log, redirect_2) {\n    auto status = test::spawn_exec_and_wait([]() {\n        auto file = TEST_LOG_FILE;\n        auto str = \"hello world, hello swoole\\n\";\n\n        sw_logger()->reset();\n        sw_logger()->open(file);\n        sw_logger()->redirect_stdout_and_stderr(true);\n\n        printf(\"%s\\n\", str);\n\n        File f(file, File::READ);\n        auto rs = f.read_content();\n\n        ASSERT_TRUE(rs->contains(str));\n        sw_logger()->redirect_stdout_and_stderr(false);\n        printf(\"%s\\n\", str);\n\n        sw_logger()->close();\n        unlink(sw_logger()->get_real_file());\n    });\n\n    ASSERT_EQ(status, 0);\n}\n\nnamespace TestA {\nclass TestPrettyName {\n  public:\n    static void fun(bool strip, const char *expect_str);\n};\n\nvoid TestPrettyName::fun(bool strip, const char *expect_str) {\n    ASSERT_STREQ(Logger::get_pretty_name(__PRETTY_FUNCTION__, strip).c_str(), expect_str);\n}\n\nstatic void test_pretty_name(bool strip, const char *expect_str) {\n    ASSERT_STREQ(Logger::get_pretty_name(__PRETTY_FUNCTION__, strip).c_str(), expect_str);\n}\n\nstatic void test_pretty_name_lambda(bool strip, const char *expect_str) {\n    auto fn = [](bool strip, const char *expect_str) {\n        ASSERT_STREQ(Logger::get_pretty_name(__PRETTY_FUNCTION__, strip).c_str(), expect_str);\n    };\n    fn(strip, expect_str);\n}\n\n}  // namespace TestA\n\nstatic void test_pretty_name(bool strip, const char *expect_str) {\n    ASSERT_STREQ(Logger::get_pretty_name(__PRETTY_FUNCTION__, strip).c_str(), expect_str);\n}\n\nstatic void test_pretty_name_lambda(bool strip, const char *expect_str) {\n    auto fn = [](bool strip, const char *expect_str) {\n        ASSERT_STREQ(Logger::get_pretty_name(__PRETTY_FUNCTION__, strip).c_str(), expect_str);\n    };\n    fn(strip, expect_str);\n}\n\nTEST(log, pretty_name) {\n    TestA::TestPrettyName::fun(false, \"TestA::TestPrettyName::fun\");\n    TestA::test_pretty_name(false, \"TestA::test_pretty_name\");\n    test_pretty_name(false, \"test_pretty_name\");\n\n    TestA::TestPrettyName::fun(true, \"TestPrettyName::fun\");\n    TestA::test_pretty_name(true, \"test_pretty_name\");\n    test_pretty_name(true, \"test_pretty_name\");\n}\n\nTEST(log, pretty_name_lambda) {\n    TestA::test_pretty_name_lambda(true, \"test_pretty_name_lambda\");\n    test_pretty_name_lambda(true, \"test_pretty_name_lambda\");\n\n    TestA::test_pretty_name_lambda(false, \"TestA::test_pretty_name_lambda\");\n    test_pretty_name_lambda(false, \"test_pretty_name_lambda\");\n}\n\nTEST(log, ignore_error) {\n    sw_logger()->reset();\n    sw_logger()->set_level(SW_LOG_NOTICE);\n    sw_logger()->open(file);\n\n    const int ignored_errcode = 999999;\n    const int errcode = 888888;\n\n    swoole_ignore_error(ignored_errcode);\n\n    swoole_error_log(SW_LOG_WARNING, ignored_errcode, \"error 1\");\n    swoole_error_log(SW_LOG_WARNING, errcode, \"error 2\");\n\n    auto content = file_get_contents(file);\n\n    sw_logger()->close();\n    unlink(file);\n\n    ASSERT_FALSE(content->contains(SW_STRL(\"error 1\")));\n    ASSERT_TRUE(content->contains(SW_STRL(\"error 2\")));\n}\n\nTEST(log, open_fail) {\n    sw_logger()->reset();\n    sw_logger()->set_level(SW_LOG_NOTICE);\n    sw_logger()->open(\"/tmp/not-exists/swoole.log\");\n    sw_logger()->put(SW_LOG_ERROR, SW_STRL(\"hello world\\n\"));\n}\n\nTEST(log, set_stream) {\n    sw_logger()->reset();\n    char *buffer = NULL;\n    size_t size = 0;\n    FILE *stream = open_memstream(&buffer, &size);\n\n    sw_logger()->set_stream(stream);\n    sw_logger()->put(SW_LOG_ERROR, SW_STRL(\"hello world\"));\n\n    sw_logger()->set_stream(stdout);\n    sw_logger()->put(SW_LOG_ERROR, SW_STRL(\"hello world\"));\n\n    ASSERT_NE(strstr(buffer, \"ERROR\\thello world\"), nullptr);\n    fclose(stream);\n    free(buffer);\n}\n"
  },
  {
    "path": "core-tests/src/core/string.cpp",
    "content": "#include \"test_core.h\"\n#include \"swoole_util.h\"\n\nusing namespace std;\nusing swoole::String;\n\nTEST(string, ltrim) {\n    char buf[1024];\n    char *ptr_buf;\n    strcpy(buf, \"  hello world\");\n    ptr_buf = buf;\n    swoole::ltrim(&ptr_buf, strlen(buf));\n    ASSERT_EQ(strcmp(\"hello world\", ptr_buf), 0);\n    ASSERT_NE(strcmp(\"  hello world\", ptr_buf), 0);\n\n    strcpy(buf, \"  \");\n    ptr_buf = buf;\n    swoole::ltrim(&ptr_buf, strlen(buf));\n    ASSERT_EQ(strlen(ptr_buf), 0);\n\n    memcpy(buf, \"  a\\0b\\0\", 6);\n    ptr_buf = buf;\n    swoole::ltrim(&ptr_buf, strlen(buf));\n    ASSERT_EQ(strcmp(\"a\", ptr_buf), 0);\n\n    buf[0] = '\\0';\n    ptr_buf = buf;\n    swoole::ltrim(&ptr_buf, strlen(buf));\n    ASSERT_EQ(strcmp(\"\", ptr_buf), 0);\n}\n\nTEST(string, rtrim) {\n    char buf[1024];\n    strcpy(buf, \"hello world  \");\n    swoole::rtrim(buf, strlen(buf));\n    ASSERT_EQ(strcmp(\"hello world\", buf), 0);\n    ASSERT_NE(strcmp(\"hello world  \", buf), 0);\n\n    strcpy(buf, \"  \");\n    swoole::rtrim(buf, strlen(buf));\n    ASSERT_EQ(strlen(buf), 0);\n\n    buf[0] = '\\0';\n    swoole::rtrim(buf, strlen(buf));\n    ASSERT_EQ(strcmp(\"\", buf), 0);\n}\n\nTEST(string, move_and_copy) {\n    String s1(TEST_STR);\n    ASSERT_MEMEQ(s1.str, TEST_STR, s1.length);\n\n    String s2(s1);\n    ASSERT_MEMEQ(s2.str, TEST_STR, s2.length);\n    ASSERT_NE(s1.str, nullptr);\n\n    String s3(std::move(s1));\n    ASSERT_MEMEQ(s3.str, TEST_STR, s3.length);\n    ASSERT_EQ(s1.str, nullptr);\n\n    String s4;\n    s4 = s3;\n    ASSERT_MEMEQ(s4.str, TEST_STR, s4.length);\n    ASSERT_NE(s3.str, nullptr);\n\n    String s5;\n    s5 = std::move(s3);\n    ASSERT_MEMEQ(s5.str, TEST_STR, s5.length);\n    ASSERT_EQ(s3.str, nullptr);\n\n    String s6(SW_STRL(TEST_STR));\n    ASSERT_MEMEQ(s6.str, TEST_STR, s6.length);\n}\n\nTEST(string, append) {\n    String s1(TEST_STR);\n    s1.append(12345678);\n\n    String s2(TEST_STR2);\n    s1.append(s2);\n\n    ASSERT_MEMEQ(s1.str, TEST_STR \"12345678\" TEST_STR2, s1.length);\n}\n\nTEST(string, write) {\n    String s1;\n    s1.reserve(32);\n\n    String s2(TEST_STR);\n    s1.repeat(\" \", 1, 30);\n    s1.write(30, s2);\n\n    auto s3 = s1.substr(30, s2.length);\n    ASSERT_MEMEQ(s3.str, TEST_STR, s3.length);\n}\n\nTEST(string, repeat) {\n    auto end_str = \"[end]\";\n    String s1;\n    s1.repeat(SW_STRL(\"hello\\r\\n\"), 5);\n    s1.append(end_str);\n\n    int count = 0;\n    auto offset = s1.split(SW_STRL(\"\\r\\n\"), [&](const char *data, size_t length) -> bool {\n        count++;\n        EXPECT_MEMEQ(data, \"hello\\r\\n\", 7);\n        return true;\n    });\n\n    ASSERT_EQ(offset, s1.length - strlen(end_str));\n    ASSERT_MEMEQ(s1.str + offset, end_str, strlen(end_str));\n\n    ASSERT_EQ(count, 5);\n}\n\nTEST(string, release) {\n    String s1(TEST_STR);\n    ASSERT_MEMEQ(s1.str, TEST_STR, s1.length);\n\n    auto s2 = s1.release();\n    ASSERT_EQ(s1.str, nullptr);\n    ASSERT_EQ(s1.length, 0);\n\n    ASSERT_MEMEQ(s2, TEST_STR, strlen(TEST_STR));\n    sw_free(s2);\n}\n\nTEST(string, ub) {\n    String s1(TEST_STR);\n    String s2(TEST_STR2);\n\n    s1 = s2;\n    s1 = s1;\n\n    auto rs = s1.substr(s1.length, 10);\n    ASSERT_EQ(rs.str, nullptr);\n}\n\nTEST(string, strnpos) {\n    {\n        string haystack = \"hello world\";\n        string needle = \" \";\n        int pos;\n\n        pos = swoole_strnpos(haystack.c_str(), haystack.length(), needle.c_str(), needle.length());\n        ASSERT_EQ(pos, 5);\n    }\n    {\n        string haystack = \"hello world\";\n        string needle = \"*\";\n        int pos;\n\n        pos = swoole_strnpos(haystack.c_str(), haystack.length(), needle.c_str(), needle.length());\n        ASSERT_EQ(-1, pos);\n    }\n}\n\nTEST(string, strnstr) {\n    {\n        string haystack = \"hello world\";\n        string needle = \" \";\n        const char *pos = swoole_strnstr(haystack.c_str(), haystack.length(), needle.c_str(), needle.length());\n        ASSERT_EQ(haystack.c_str() + 5, pos);\n    }\n    {\n        string haystack = \"hello world\";\n        string needle = \"*\";\n        const char *pos = swoole_strnstr(haystack.c_str(), haystack.length(), needle.c_str(), needle.length());\n        ASSERT_EQ(NULL, pos);\n    }\n    {\n        string haystack = \"hello world\\r\\n\";\n        string needle = \"\\r\\n\\r\\n\";\n        const char *pos = swoole_strnstr(haystack.c_str(), haystack.length(), needle.c_str(), needle.length());\n        ASSERT_EQ(NULL, pos);\n    }\n}\n\nTEST(string, explode) {\n    string haystack = \"hello world\";\n    string needle = \" \";\n\n    String str;\n    str.str = (char *) haystack.c_str();\n    str.length = haystack.length();\n\n    int value_1 = 0;\n\n    const char *explode_str = nullptr;\n    size_t explode_length = 0;\n\n    str.split(needle.c_str(), needle.length(), [&](const char *data, size_t length) -> int {\n        explode_str = data;\n        explode_length = length;\n        value_1 = 5;\n        return false;\n    });\n\n    ASSERT_EQ(haystack, explode_str);\n    ASSERT_EQ(6, explode_length);\n    ASSERT_EQ(5, value_1);\n}\n\nTEST(string, explode_2) {\n    string haystack = \"hello,world,swoole,php,last\";\n    string needle = \",\";\n\n    String str;\n    str.str = (char *) haystack.c_str();\n    str.length = haystack.length();\n\n    int count = 0;\n    vector<string> list;\n\n    size_t n = str.split(needle.c_str(), needle.length(), [&](const char *data, size_t length) -> int {\n        list.push_back(string(data, length - 1));\n        count++;\n        return true;\n    });\n\n    ASSERT_EQ(list[0], string(\"hello\"));\n    ASSERT_EQ(list[1], string(\"world\"));\n    ASSERT_EQ(list[2], string(\"swoole\"));\n    ASSERT_EQ(list[3], string(\"php\"));\n    ASSERT_EQ(\"last\", string(str.str + n, str.length - n));\n    ASSERT_EQ(4, count);\n    ASSERT_EQ(list.size(), count);\n}\n\nstatic const int init_size = 1024;\nstatic string test_data = \"hello,world,swoole,php,last\";\n\nTEST(string, pop_1) {\n    auto str = swoole::make_string(init_size);\n    std::unique_ptr<String> s(str);\n\n    char *str_1 = str->str;\n\n    const int len_1 = 11;\n    str->append(test_data.c_str(), test_data.length());\n    str->offset = len_1;\n    char *str_2 = str->pop(init_size);\n\n    EXPECT_EQ(str_1, str_2);\n    EXPECT_EQ(string(\"hello,world\"), string(str_2, len_1));\n    EXPECT_EQ(string(\",swoole,php,last\"), string(str->str, str->length));\n    EXPECT_EQ(init_size, str->size);\n\n    str->allocator->free(str_1);\n}\n\nTEST(string, pop_2) {\n    auto str = swoole::make_string(init_size);\n    std::unique_ptr<String> s(str);\n\n    char *str_1 = str->str;\n\n    const int len_1 = test_data.length();\n    str->append(test_data.c_str(), test_data.length());\n    str->offset = len_1;\n    char *str_2 = str->pop(init_size);\n\n    EXPECT_EQ(str_1, str_2);\n    EXPECT_EQ(test_data, string(str_2, len_1));\n    EXPECT_EQ(str->length, 0);\n    EXPECT_EQ(init_size, str->size);\n\n    str->allocator->free(str_1);\n}\n\nTEST(string, reduce_1) {\n    auto str = swoole::make_string(init_size);\n    std::unique_ptr<String> s(str);\n\n    const int len_1 = 11;\n    str->append(test_data.c_str(), test_data.length());\n    str->offset = len_1;\n\n    str->reduce(str->offset);\n\n    EXPECT_EQ(string(\",swoole,php,last\"), string(str->str, str->length));\n}\n\nTEST(string, reduce_2) {\n    auto str = swoole::make_string(init_size);\n    std::unique_ptr<String> s(str);\n\n    str->append(test_data.c_str(), test_data.length());\n    str->offset = str->length;\n\n    str->reduce(str->offset);\n\n    EXPECT_EQ(str->length, 0);\n}\n\nTEST(string, reduce_3) {\n    auto str = swoole::make_string(init_size);\n    std::unique_ptr<String> s(str);\n\n    str->append(test_data.c_str(), test_data.length());\n    str->offset = 0;\n\n    str->reduce(str->offset);\n\n    EXPECT_EQ(str->length, test_data.length());\n}\n\nconst auto FORMAT_INT = 999999999999999;\nconst auto FORMAT_STR = \"hello world\";\n\nTEST(string, format_1) {\n    String str1(1024);\n    str1.append_random_bytes(1024, true);\n\n    size_t n = str1.format(\"str=%s, value=%ld\", FORMAT_STR, FORMAT_INT);\n    std::string str2(\"str=hello world, value=999999999999999\");\n\n    ASSERT_EQ(str1.get_length(), n);\n    ASSERT_MEMEQ(str1.value(), str2.c_str(), n);\n}\n\nTEST(string, format_2) {\n    String str1(1024);\n    str1.append_random_bytes(1024, true);\n\n    std::string str2(str1.value(), str1.get_length());\n\n    size_t n = str1.format_impl(String::FORMAT_APPEND, \"str=%s, value=%ld\", FORMAT_STR, FORMAT_INT);\n    str2 += std::string(str1.value() + str2.length(), n);\n\n    EXPECT_MEMEQ(str1.value(), str2.c_str(), str1.get_length());\n}\n\nTEST(string, substr_len) {\n    const char *str1 = \"hello: swoole & world\";\n    ASSERT_EQ(swoole::substr_len(str1, strlen(str1), ':', true), 5);\n    ASSERT_EQ(swoole::substr_len(str1, strlen(str1), ':', false), 15);\n}\n\nTEST(string, starts_with) {\n    const char *str1 = \"hello world\";\n    ASSERT_TRUE(swoole::starts_with(str1, strlen(str1), SW_STRL(\"hello\")));\n    ASSERT_FALSE(swoole::starts_with(str1, strlen(str1), SW_STRL(\"php\")));\n    ASSERT_TRUE(swoole::starts_with(str1, strlen(str1), str1, strlen(str1)));\n}\n\nTEST(string, ends_with) {\n    const char *str1 = \"hello world\";\n    ASSERT_TRUE(swoole::ends_with(str1, strlen(str1), SW_STRL(\"world\")));\n    ASSERT_FALSE(swoole::ends_with(str1, strlen(str1), SW_STRL(\"php\")));\n    ASSERT_TRUE(swoole::ends_with(str1, strlen(str1), str1, strlen(str1)));\n}\n\nTEST(string, append_number) {\n    string data = \"hello\";\n    auto str = swoole::make_string(data.length() + 32);\n    str->append(data.c_str(), data.length());\n    str->append(123);\n    str->set_null_terminated();\n    EXPECT_STREQ(str->str, data.append(\"123\").c_str());\n\n    str->print(true);\n    str->print(false);\n\n    delete str;\n}\n"
  },
  {
    "path": "core-tests/src/core/time.cpp",
    "content": "#include \"test_core.h\"\n#include \"swoole_util.h\"\n\nTEST(time, get_ms) {\n    const int us = 3000;\n    long ms1 = swoole::time<std::chrono::milliseconds>();\n    usleep(us);\n    long ms2 = swoole::time<std::chrono::milliseconds>();\n    EXPECT_GE(ms2 - ms1, us / 1000);\n}\n\nTEST(time, get_ms_steady) {\n    const int us = 3000;\n    long ms1 = swoole::time<std::chrono::milliseconds>(true);\n    usleep(us);\n    long ms2 = swoole::time<std::chrono::milliseconds>(true);\n    EXPECT_GE(ms2 - ms1, us / 1000);\n}\n\nTEST(time, get_seconds) {\n    long sec1 = swoole::time<std::chrono::seconds>();\n    time_t sec2 = time(NULL);\n    ASSERT_TRUE(sec1 == sec2 or sec1 == sec2 - 1);\n}\n\nTEST(time, get_timezone) {\n    ASSERT_GE(swoole::get_timezone(), 0);\n}\n"
  },
  {
    "path": "core-tests/src/core/util.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n\n#include \"swoole_util.h\"\n\nTEST(util, bitmap) {\n    swoole::BitMap m(4096);\n\n    m.set(199);\n    m.set(1234);\n    m.set(3048);\n\n    ASSERT_EQ(m.get(199), true);\n    ASSERT_EQ(m.get(1234), true);\n    ASSERT_EQ(m.get(3048), true);\n\n    ASSERT_EQ(m.get(2048), false);\n    ASSERT_EQ(m.get(128), false);\n\n    m.unset(1234);\n    ASSERT_EQ(m.get(1234), false);\n\n    m.clear();\n}\n"
  },
  {
    "path": "core-tests/src/coroutine/accept.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_coroutine.h\"\n#include \"swoole_coroutine_socket.h\"\n#include \"swoole_coroutine_system.h\"\n\nusing namespace swoole::test;\nusing swoole::Coroutine;\n\nTEST(coroutine_hook, accept) {\n    coroutine::run([](void *arg) {\n        // Create a TCP socket using coroutine API\n        int server_sock = swoole_coroutine_socket(AF_INET, SOCK_STREAM, 0);\n        ASSERT_GT(server_sock, 0);\n\n        // Bind the socket to localhost with port 0 (auto-assign)\n        struct sockaddr_in server_addr;\n        memset(&server_addr, 0, sizeof(server_addr));\n        server_addr.sin_family = AF_INET;\n        server_addr.sin_addr.s_addr = inet_addr(\"127.0.0.1\");\n        server_addr.sin_port = 0;\n\n        int retval = ::bind(server_sock, (struct sockaddr *) &server_addr, sizeof(server_addr));\n        ASSERT_EQ(retval, 0);\n\n        // Listen on the socket\n        retval = ::listen(server_sock, 128);\n        ASSERT_EQ(retval, 0);\n\n        struct sockaddr_in client_addr;\n        socklen_t client_addr_len = sizeof(client_addr);\n\n        // Test that swoole_coroutine_accept works correctly\n        Coroutine::create([&](void *arg) {\n            // Give the server time to start listening\n            usleep(10000);\n\n            // Connect to the server using coroutine API\n            int client_sock = swoole_coroutine_socket(AF_INET, SOCK_STREAM, 0);\n            ASSERT_GT(client_sock, 0);\n\n            // Get the actual server port\n            struct sockaddr_in actual_server_addr;\n            socklen_t addr_len = sizeof(actual_server_addr);\n            ASSERT_EQ(getsockname(server_sock, (struct sockaddr *) &actual_server_addr, &addr_len), 0);\n\n            // Connect to the server\n            retval = swoole_coroutine_connect(client_sock, (struct sockaddr *) &actual_server_addr, addr_len);\n            ASSERT_EQ(retval, 0);\n\n            // Send a test message\n            const char *test_message = \"test_data\";\n            ssize_t sent_bytes = swoole_coroutine_send(client_sock, test_message, strlen(test_message), 0);\n            ASSERT_EQ(sent_bytes, (ssize_t) strlen(test_message));\n\n            // Close the client socket\n            swoole_coroutine_close(client_sock);\n        });\n\n        // Accept the connection using coroutine API\n        int client_sock = swoole_coroutine_accept(server_sock, (struct sockaddr *) &client_addr, &client_addr_len);\n        ASSERT_GT(client_sock, 0);\n\n        // Receive data from client\n        char buffer[256] = {};\n        ssize_t received_bytes = swoole_coroutine_recv(client_sock, buffer, sizeof(buffer) - 1, 0);\n        ASSERT_GT(received_bytes, 0);\n        ASSERT_STREQ(buffer, \"test_data\");\n\n        // Close the client socket\n        swoole_coroutine_close(client_sock);\n        swoole_coroutine_close(server_sock);\n    });\n}\n"
  },
  {
    "path": "core-tests/src/coroutine/async.cpp",
    "content": "#include \"test_coroutine.h\"\n#include \"swoole_async.h\"\n\n#include <iostream>\n#include <regex>\n\nusing namespace std;\nusing swoole::AsyncEvent;\nusing swoole::test::coroutine;\n\nconst int magic_code = 0x7009501;\n\nTEST(coroutine_async, usleep) {\n    coroutine::run([](void *arg) {\n        AsyncEvent ev = {};\n        bool retval = swoole::coroutine::async(\n            [](AsyncEvent *event) {\n                usleep(1000);\n                event->retval = magic_code;\n            },\n            ev);\n        ASSERT_EQ(retval, true);\n        ASSERT_EQ(ev.retval, magic_code);\n    });\n}\n\nTEST(coroutine_async, gethostbyname) {\n    coroutine::run([](void *arg) {\n        string domain(\"www.baidu.com\"), ip;\n\n        bool retval = swoole::coroutine::async([&]() {\n            char buf[128];\n            if (swoole::network::gethostbyname(AF_INET, domain.c_str(), buf) == SW_OK) {\n                char addr[128];\n                inet_ntop(AF_INET, buf, addr, sizeof(addr));\n                ip = addr;\n            } else {\n                ip = \"unknown\";\n            }\n        });\n\n        ASSERT_EQ(retval, true);\n        match_results<string::const_iterator> result;\n        try {\n            const regex pattern(\"(\\\\d{1,3}).(\\\\d{1,3}).(\\\\d{1,3}).(\\\\d{1,3})\");\n            ASSERT_EQ(regex_match(ip, result, pattern), true);\n        } catch (std::exception &ex) {\n            std::cerr << \"regex error: gcc version must be 4.9+\" << std::endl;\n        }\n    });\n}\n\nTEST(coroutine_async, error) {\n    coroutine::run([](void *arg) {\n        int retval = 0x7009501;\n        const char *test_file = \"/tmp/swoole_core_test_file_not_exists\";\n        swoole::coroutine::async([&](void) { retval = open(test_file, O_RDONLY); });\n        ASSERT_EQ(retval, -1);\n        ASSERT_EQ(errno, ENOENT);\n    });\n}\n\nTEST(coroutine_async, cancel) {\n    coroutine::run([](void *arg) {\n        AsyncEvent ev = {};\n        auto co = swoole::Coroutine::get_current();\n        swoole_timer_after(50, [co](TIMER_PARAMS) { co->cancel(); });\n\n        bool retval = swoole::coroutine::async(\n            [](AsyncEvent *event) {\n                usleep(200000);\n                event->retval = magic_code;\n            },\n            ev);\n\n        ASSERT_EQ(retval, false);\n        ASSERT_EQ(ev.error, SW_ERROR_CO_CANCELED);\n        DEBUG() << \"done\\n\";\n    });\n}\n\nTEST(coroutine_async, timeout) {\n    coroutine::run([](void *arg) {\n        AsyncEvent ev = {};\n\n        bool retval = swoole::coroutine::async(\n            [](AsyncEvent *event) {\n                usleep(200000);\n                event->retval = magic_code;\n            },\n            ev,\n            0.1);\n\n        ASSERT_EQ(retval, false);\n        ASSERT_EQ(ev.error, SW_ERROR_CO_TIMEDOUT);\n        DEBUG() << \"done\\n\";\n    });\n}\n"
  },
  {
    "path": "core-tests/src/coroutine/base.cpp",
    "content": "#include \"test_coroutine.h\"\n\nusing namespace swoole;\nusing swoole::coroutine::System;\n\nTEST(coroutine_base, create) {\n    long _cid;\n    long cid = Coroutine::create([](void *arg) { *(long *) arg = Coroutine::get_current_cid(); }, &_cid);\n\n    ASSERT_GT(cid, 0);\n    ASSERT_EQ(cid, _cid);\n}\n\nTEST(coroutine_base, get_current) {\n    long _cid;\n    long cid = Coroutine::create(\n        [](void *arg) {\n            auto co = Coroutine::get_current();\n            *(long *) arg = co->get_cid();\n        },\n        &_cid);\n\n    ASSERT_GT(cid, 0);\n    ASSERT_EQ(cid, _cid);\n}\n\nTEST(coroutine_base, get_init_msec) {\n    Coroutine::create([](void *arg) {\n        auto co = Coroutine::get_current();\n        long init_msec = co->get_init_msec();\n\n        ASSERT_GT(init_msec, 0);\n    });\n}\n\nTEST(coroutine_base, yield_resume) {\n    Coroutine::set_on_yield([](void *arg) {\n        auto task = static_cast<long *>(Coroutine::get_current_task());\n        ASSERT_NE(task, nullptr);\n        ASSERT_EQ(*task, Coroutine::get_current_cid());\n    });\n\n    Coroutine::set_on_resume([](void *arg) {\n        Coroutine *current = Coroutine::get_current();\n        ASSERT_EQ(current, nullptr);\n    });\n\n    Coroutine::set_on_close([](void *arg) {\n        auto task = static_cast<long *>(Coroutine::get_current_task());\n        ASSERT_NE(task, nullptr);\n        ASSERT_EQ(*task, Coroutine::get_current_cid());\n    });\n\n    long _cid, _cid2;\n    long cid = Coroutine::create(\n        [&_cid2](void *arg) {\n            _cid2 = Coroutine::get_current_cid();\n            Coroutine *co = Coroutine::get_by_cid(_cid2);\n            co->set_task(&_cid2);\n            co->yield();\n            *static_cast<long *>(arg) = Coroutine::get_current_cid();\n        },\n        &_cid);\n\n    ASSERT_GT(cid, 0);\n    Coroutine::get_by_cid(cid)->resume();\n    Coroutine::set_on_close(nullptr);\n    ASSERT_EQ(cid, _cid);\n}\n\nTEST(coroutine_base, get_cid) {\n    Coroutine::create([](void *arg) {\n        auto co = Coroutine::get_current();\n        long cid = co->get_cid();\n\n        ASSERT_GT(cid, 0);\n    });\n}\n\nTEST(coroutine_base, get_origin) {\n    Coroutine::create([](void *arg) {\n        auto *co = Coroutine::get_current();\n\n        Coroutine::create(\n            [](void *arg) {\n                auto current_co = Coroutine::get_current();\n                auto origin_co = current_co->get_origin();\n\n                ASSERT_EQ(arg, origin_co);\n            },\n            co);\n    });\n}\n\nTEST(coroutine_base, get_origin_cid) {\n    Coroutine::create([](void *arg) {\n        auto _cid = Coroutine::get_current_cid();\n\n        Coroutine::create(\n            [](void *arg) {\n                auto origin_cid = Coroutine::get_current()->get_origin_cid();\n\n                ASSERT_EQ(*(long *) arg, origin_cid);\n            },\n            &_cid);\n    });\n}\n\nTEST(coroutine_base, is_end) {\n    Coroutine::create([](void *_arg) {\n        auto co = Coroutine::get_current();\n        ASSERT_FALSE(co->is_end());\n    });\n}\n\nTEST(coroutine_base, set_task) {\n    Coroutine::create([](void *_arg) {\n        int task;\n        auto co = Coroutine::get_current();\n        co->set_task(&task);\n        void *actual = co->get_task();\n        ASSERT_EQ(actual, &task);\n    });\n}\n\nTEST(coroutine_base, get_current_task) {\n    Coroutine::create([](void *_arg) {\n        int task;\n        auto co = Coroutine::get_current();\n        co->set_task(&task);\n        void *actual = co->get_task();\n        ASSERT_EQ(actual, Coroutine::get_current_task());\n    });\n}\n\nTEST(coroutine_base, get_current_cid) {\n    Coroutine::create([](void *_arg) {\n        auto co = Coroutine::get_current();\n        auto actual = co->get_cid();\n        ASSERT_EQ(actual, Coroutine::get_current_cid());\n        ASSERT_EQ(actual, swoole_coroutine_get_id());\n    });\n}\n\nTEST(coroutine_base, get_by_cid) {\n    Coroutine::create([](void *_arg) {\n        auto actual = Coroutine::get_current();\n        auto cid = actual->get_cid();\n        ASSERT_EQ(actual, Coroutine::get_by_cid(cid));\n    });\n}\n\nTEST(coroutine_base, get_task_by_cid) {\n    Coroutine::create([](void *_arg) {\n        int task;\n        auto co = Coroutine::get_current();\n        co->set_task(&task);\n        auto actual = co->get_task();\n        ASSERT_EQ(actual, Coroutine::get_task_by_cid(co->get_cid()));\n    });\n}\n\nTEST(coroutine_base, get_last_cid) {\n    Coroutine::create([](void *_arg) {});\n    Coroutine::create([](void *_arg) {});\n    long cid = Coroutine::create([](void *_arg) {});\n\n    ASSERT_EQ(Coroutine::get_last_cid(), cid);\n}\n\nTEST(coroutine_base, count) {\n    Coroutine::create([](void *_arg) {\n        ASSERT_EQ(Coroutine::count(), 1);\n        Coroutine::create([](void *_arg) { ASSERT_EQ(Coroutine::count(), 2); });\n    });\n    ASSERT_EQ(Coroutine::count(), 0);\n}\n\nTEST(coroutine_base, get_peak_num) {\n    Coroutine::create(\n        [](void *_arg) { Coroutine::create([](void *_arg) { ASSERT_GE(Coroutine::get_peak_num(), 2); }); });\n}\n\nTEST(coroutine_base, get_elapsed) {\n    long elapsed_time = 0;\n    Coroutine::create(\n        [](void *arg) {\n            auto co = Coroutine::get_current();\n            usleep(2000);\n            *(long *) arg = Coroutine::get_elapsed(co->get_cid());\n        },\n        &elapsed_time);\n    ASSERT_GE(elapsed_time, 2);\n}\n\nTEST(coroutine_base, run) {\n    long cid = coroutine::run([](void *ptr) {\n\n    });\n    ASSERT_GE(cid, 1);\n}\n\nTEST(coroutine_base, cancel) {\n    coroutine::run([](void *arg) {\n        auto co = Coroutine::get_current_safe();\n        Coroutine::create([co](void *) {\n            System::sleep(0.002);\n            co->cancel();\n        });\n        ASSERT_EQ(co->yield_ex(-1), false);\n        ASSERT_EQ(co->is_canceled(), true);\n    });\n}\n\nTEST(coroutine_base, noncancelable) {\n    std::unordered_map<std::string, bool> flags;\n    coroutine::run([&flags](void *arg) {\n        auto cid = Coroutine::create([&flags](void *_arg) {\n            Coroutine *current = Coroutine::get_current();\n            flags[\"yield_1\"] = true;\n            current->yield();\n            ASSERT_FALSE(current->is_canceled());\n\n            flags[\"yield_2\"] = true;\n            current->yield_ex(-1);\n            ASSERT_TRUE(current->is_canceled());\n        });\n\n        auto co = Coroutine::get_by_cid(cid);\n\n        flags[\"cancel_1\"] = true;\n        ASSERT_FALSE(co->cancel());\n        ASSERT_EQ(swoole_get_last_error(), SW_ERROR_CO_CANNOT_CANCEL);\n        flags[\"resume_1\"] = true;\n        co->resume();\n\n        flags[\"cancel_2\"] = true;\n        ASSERT_TRUE(co->cancel());\n        flags[\"resume_2\"] = true;\n\n        flags[\"done\"] = true;\n    });\n\n    ASSERT_TRUE(flags[\"yield_1\"]);\n    ASSERT_TRUE(flags[\"yield_2\"]);\n    ASSERT_TRUE(flags[\"cancel_1\"]);\n    ASSERT_TRUE(flags[\"resume_1\"]);\n    ASSERT_TRUE(flags[\"cancel_2\"]);\n    ASSERT_TRUE(flags[\"resume_2\"]);\n    ASSERT_TRUE(flags[\"done\"]);\n}\n\nTEST(coroutine_base, timeout) {\n    coroutine::run([](void *arg) {\n        auto co = Coroutine::get_current_safe();\n        ASSERT_EQ(co->yield_ex(0.005), false);\n        ASSERT_EQ(co->is_timedout(), true);\n    });\n}\n\nTEST(coroutine_base, gdb) {\n    Coroutine::create([](void *) {\n        Coroutine *current = Coroutine::get_current();\n        long cid = current->get_cid();\n        ASSERT_EQ(swoole_coroutine_count(), 1);\n        ASSERT_EQ(swoole_coroutine_get(cid), current);\n        ASSERT_EQ(swoole_coroutine_get(999999), nullptr);\n\n        swoole_coroutine_iterator_reset();\n        ASSERT_EQ(swoole_coroutine_iterator_each(), current);\n        ASSERT_EQ(swoole_coroutine_iterator_each(), nullptr);\n\n        swoole_coroutine_iterator_reset();\n        ASSERT_EQ(swoole_coroutine_iterator_each(), current);\n        Coroutine::print_list();\n    });\n}\n\nTEST(coroutine_base, bailout) {\n    int status;\n\n    status = test::spawn_exec_and_wait([]() {\n        std::unordered_map<std::string, bool> flags;\n        coroutine::run([&flags](void *arg) {\n            Coroutine::create([&flags](void *_arg) {\n                Coroutine *current = Coroutine::get_current();\n                current->bailout([&flags]() { flags[\"exit\"] = true; });\n                flags[\"end\"] = true;\n            });\n        });\n\n        ASSERT_TRUE(flags[\"exit\"]);\n        ASSERT_FALSE(flags[\"end\"]);\n    });\n    ASSERT_EQ(status, 0);\n\n    status = test::spawn_exec_and_wait([]() {\n        std::unordered_map<std::string, bool> flags;\n        coroutine::run([&flags](void *arg) {\n            Coroutine *current = Coroutine::get_current();\n            current->bailout(nullptr);\n            flags[\"end\"] = true;\n        });\n\n        ASSERT_TRUE(flags[\"exit\"]);\n        ASSERT_FALSE(flags[\"end\"]);\n    });\n    ASSERT_EQ(WEXITSTATUS(status), 1);\n\n    status = test::spawn_exec_and_wait([]() {\n        std::unordered_map<std::string, bool> flags;\n        coroutine::run([&flags](void *arg) {\n            Coroutine *current = Coroutine::get_current();\n            swoole_event_defer(\n                [current, &flags](void *args) {\n                    flags[\"bailout\"] = true;\n                    current->bailout(nullptr);\n                    flags[\"end\"] = true;\n                },\n                nullptr);\n            flags[\"exit\"] = true;\n        });\n\n        ASSERT_TRUE(flags[\"exit\"]);\n        ASSERT_TRUE(flags[\"end\"]);\n    });\n    ASSERT_EQ(WEXITSTATUS(status), 0);\n}\n\nTEST(coroutine_base, undefined_behavior) {\n    int status;\n    status = test::spawn_exec_and_wait([]() { test::coroutine::run([](void *) { swoole_fork(0); }); });\n    ASSERT_EQ(1, WEXITSTATUS(status));\n\n    status = test::spawn_exec_and_wait([]() {\n        std::atomic<int> handle_count(0);\n        AsyncEvent event = {};\n        event.object = &handle_count;\n        event.callback = [](AsyncEvent *event) {};\n        event.handler = [](AsyncEvent *event) { ++(*static_cast<std::atomic<int> *>(event->object)); };\n\n        swoole_event_init(0);\n        auto ret = async::dispatch(&event);\n        ASSERT_NE(ret, nullptr);\n        swoole_fork(0);\n    });\n    ASSERT_EQ(1, WEXITSTATUS(status));\n\n    ASSERT_EQ(0, swoole_fork(SW_FORK_PRECHECK));\n}\n\nTEST(coroutine_base, c_api) {\n    int code = 0x9501;\n    auto c1 = swoole_coroutine_create(\n        [](void *_arg) {\n            ASSERT_EQ(*(int *) _arg, 0x9501);\n            swoole_coroutine_create([](void *_arg) { swoole_coroutine_usleep(100000); }, nullptr);\n        },\n        &code);\n\n    ASSERT_GE(c1, 1);\n}\n"
  },
  {
    "path": "core-tests/src/coroutine/channel.cpp",
    "content": "#include \"test_coroutine.h\"\n\nusing swoole::Coroutine;\nusing swoole::coroutine::Channel;\n\nusing namespace std;\nusing namespace swoole::test;\n\nTEST(coroutine_channel, push_pop) {\n    coroutine::run([](void *arg) {\n        Channel chan(1);\n        int i = 1;\n        bool ret;\n\n        ret = chan.push(&i);\n        ASSERT_TRUE(ret);\n        ASSERT_EQ(*(int *) chan.pop(), i);\n    });\n}\n\nTEST(coroutine_channel, push_yield) {\n    Channel chan(1);\n\n    coroutine::run({make_pair(\n                        [](void *arg) {\n                            auto chan = (Channel *) arg;\n                            int i = 1;\n                            bool ret;\n\n                            ret = chan->push(new int(i));\n                            ASSERT_TRUE(ret);\n                            ret = chan->push(new int(i));\n                            ASSERT_TRUE(ret);\n                        },\n                        &chan),\n\n                    make_pair(\n                        [](void *arg) {\n                            auto chan = (Channel *) arg;\n                            ASSERT_EQ(*(int *) chan->pop(), 1);\n                            ASSERT_EQ(*(int *) chan->pop(), 1);\n                        },\n                        &chan)});\n}\n\nTEST(coroutine_channel, pop_yield) {\n    Channel chan(1);\n\n    coroutine::run({make_pair(\n                        [](void *arg) {\n                            auto chan = (Channel *) arg;\n\n                            ASSERT_EQ(*(int *) chan->pop(), 1);\n                            ASSERT_EQ(*(int *) chan->pop(), 1);\n                        },\n                        &chan),\n\n                    make_pair(\n                        [](void *arg) {\n                            auto chan = (Channel *) arg;\n                            int i = 1;\n                            bool ret;\n\n                            ret = chan->push(&i);\n                            ASSERT_TRUE(ret);\n                            ret = chan->push(&i);\n                            ASSERT_TRUE(ret);\n                        },\n                        &chan)});\n}\n\nTEST(coroutine_channel, push_timeout) {\n    coroutine::run([](void *arg) {\n        Channel chan(1);\n        bool ret;\n\n        ret = chan.push(nullptr, 0.001);\n        ASSERT_TRUE(ret);\n        ret = chan.push(nullptr, 0.001);\n        ASSERT_FALSE(ret);\n    });\n}\n\nTEST(coroutine_channel, pop_timeout) {\n    coroutine::run([](void *arg) {\n        Channel chan(1);\n        void *ret;\n\n        ret = chan.pop(0.001);\n        ASSERT_EQ(ret, nullptr);\n    });\n}\n\nTEST(coroutine_channel, close) {\n    Channel chan(1);\n    coroutine::run(\n        [](void *arg) {\n            int value = 1;\n            auto chan = (Channel *) arg;\n            while (1) {\n                if (!chan->push((void *) &value)) {\n                    ASSERT_EQ(chan->get_error(), Channel::ErrorCode::ERROR_CLOSED);\n                    ASSERT_FALSE(chan->push(nullptr));\n                    break;\n                }\n            }\n        },\n        &chan);\n\n    ASSERT_TRUE(chan.close());\n    ASSERT_FALSE(chan.close());\n\n    Channel chan2(1);\n    coroutine::run(\n        [](void *arg) {\n            auto chan = (Channel *) arg;\n            while (1) {\n                if (!chan->pop(0)) {\n                    ASSERT_EQ(chan->get_error(), Channel::ErrorCode::ERROR_CLOSED);\n                    ASSERT_EQ(chan->pop(), nullptr);\n                    break;\n                }\n            }\n        },\n        &chan2);\n\n    ASSERT_TRUE(chan2.close());\n}\n\nTEST(coroutine_channel, cancel) {\n    Channel chan(1);\n\n    coroutine::run(\n        [](void *arg) {\n            auto chan = (Channel *) arg;\n            auto cid = Coroutine::create([chan](void *args) {\n                ASSERT_EQ(chan->pop(), nullptr);\n                ASSERT_EQ(chan->get_error(), Channel::ERROR_CANCELED);\n            });\n\n            auto co = Coroutine::get_by_cid(cid);\n            ASSERT_TRUE(co->cancel());\n            ASSERT_TRUE(chan->close());\n\n            ASSERT_EQ(chan->pop(), nullptr);\n            ASSERT_EQ(chan->get_error(), Channel::ERROR_CLOSED);\n        },\n        &chan);\n}\n"
  },
  {
    "path": "core-tests/src/coroutine/file.cpp",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: NathanFreeman  <mariasocute@163.com>                         |\n +----------------------------------------------------------------------+\n*/\n\n#include <sys/file.h>\n#include <sys/stat.h>\n#include \"test_coroutine.h\"\n#include \"swoole_file.h\"\n\nusing namespace std;\nusing namespace swoole;\nusing swoole::coroutine::System;\n\nTEST(coroutine_async_file, async_file) {\n    coroutine::run([](void *arg) {\n        string filename = \"/tmp/file.txt\";\n        auto file = new AsyncFile(filename, O_CREAT | O_RDWR, 0666);\n        ON_SCOPE_EXIT {\n            delete file;\n        };\n        ASSERT_EQ(file->ready(), true);\n        ASSERT_EQ(file->truncate(0), true);\n        ASSERT_EQ(file->set_offset(0), 0);\n        ASSERT_EQ(file->get_offset(), 0);\n\n        const char *data = \"Hello World!\";\n        size_t length = strlen(data);\n        ASSERT_EQ(file->write((void *) data, length), static_cast<ssize_t>(length));\n\n        ASSERT_EQ(file->sync(), true);\n        ASSERT_EQ(file->set_offset(0), 0);\n\n        char buf[1024];\n        ASSERT_EQ(file->read((void *) buf, 1024), static_cast<ssize_t>(length));\n        buf[length] = '\\0';\n        ASSERT_STREQ(data, buf);\n\n        struct stat statbuf {};\n        ASSERT_EQ(file->stat(&statbuf), true);\n        ASSERT_TRUE(statbuf.st_size > 0);\n    });\n}\n"
  },
  {
    "path": "core-tests/src/coroutine/gethostbyname.cpp",
    "content": "#include \"test_coroutine.h\"\n\nusing swoole::Coroutine;\nusing swoole::Timer;\nusing swoole::coroutine::System;\nusing swoole::test::coroutine;\n\nTEST(coroutine_gethostbyname, resolve_cache) {\n    coroutine::run([](void *arg) {\n        System::set_dns_cache_capacity(10);\n        std::string addr1 = System::gethostbyname(TEST_DOMAIN_BAIDU, AF_INET);\n        ASSERT_NE(addr1, \"\");\n        for (int i = 0; i < 100; ++i) {\n            std::string addr2 = System::gethostbyname(TEST_DOMAIN_BAIDU, AF_INET);\n            ASSERT_EQ(addr1, addr2);\n        }\n        ASSERT_GT(System::get_dns_cache_hit_ratio(), 0.99);\n\n        System::set_dns_cache_capacity(0);\n        for (int i = 0; i < 5; ++i) {\n            std::string addr2 = System::gethostbyname(TEST_DOMAIN_BAIDU, AF_INET);\n            ASSERT_NE(addr2, \"\");\n        }\n        ASSERT_LT(System::get_dns_cache_hit_ratio(), 0.01);\n    });\n}\n\nTEST(coroutine_gethostbyname, impl_async) {\n    coroutine::run([](void *arg) {\n        auto result = swoole::coroutine::gethostbyname_impl_with_async(TEST_DOMAIN_BAIDU, AF_INET);\n        ASSERT_EQ(result.empty(), false);\n    });\n}\n\nTEST(coroutine_gethostbyname, resolve_cache_inet4_and_inet6) {\n    coroutine::run([](void *arg) {\n        System::set_dns_cache_capacity(10);\n\n        std::string addr1 = System::gethostbyname(\"ipv6.sjtu.edu.cn\", AF_INET);\n        std::string addr2 = System::gethostbyname(\"ipv6.sjtu.edu.cn\", AF_INET6);\n\n        ASSERT_NE(addr1, \"\");\n        ASSERT_NE(addr2, \"\");\n        ASSERT_EQ(addr1.find(\":\"), addr1.npos);\n        ASSERT_NE(addr2.find(\":\"), addr2.npos);\n\n        int64_t start = Timer::get_absolute_msec();\n\n        for (int i = 0; i < 100; ++i) {\n            std::string addr3 = System::gethostbyname(\"ipv6.sjtu.edu.cn\", AF_INET);\n            std::string addr4 = System::gethostbyname(\"ipv6.sjtu.edu.cn\", AF_INET6);\n\n            ASSERT_EQ(addr1, addr3);\n            ASSERT_EQ(addr2, addr4);\n        }\n\n        ASSERT_LT(Timer::get_absolute_msec() - start, 5);\n    });\n}\n\nTEST(coroutine_gethostbyname, dns_expire) {\n    coroutine::run([](void *arg) {\n        System::set_dns_cache_expire(1);\n        System::clear_dns_cache();\n\n        System::gethostbyname(TEST_HTTP_DOMAIN, AF_INET);\n        System::gethostbyname(TEST_HTTP_DOMAIN, AF_INET);\n        ASSERT_GE(System::get_dns_cache_hit_ratio(), 0.5);\n\n        sleep(2);\n        System::gethostbyname(TEST_HTTP_DOMAIN, AF_INET);\n        ASSERT_LT(System::get_dns_cache_hit_ratio(), 0.35);\n\n        System::clear_dns_cache();\n        System::set_dns_cache_expire(60);\n    });\n}\n"
  },
  {
    "path": "core-tests/src/coroutine/hook.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_coroutine.h\"\n#include \"swoole_file.h\"\n#include \"swoole_util.h\"\n\nusing namespace swoole::test;\n\nusing swoole::Coroutine;\nusing swoole::File;\nusing swoole::String;\nusing swoole::coroutine::System;\nusing swoole::test::coroutine;\n\nconst char *host_1 = \"www.baidu.com\";\nconst char *host_2 = \"www.xxxxxxxxxxxxxxxxxxxxx00000xxxxxxxxx----not_found.com\";\nstatic const char *test_file = \"/tmp/swoole-core-test\";\n\nvoid static test_file_hook() {\n    char buf[8192];\n    size_t n_buf = sizeof(buf);\n    ASSERT_EQ(swoole_random_bytes(buf, n_buf), n_buf);\n\n    int fd = swoole_coroutine_open(test_file, O_WRONLY | O_TRUNC | O_CREAT, 0666);\n    ASSERT_EQ(swoole_coroutine_write(fd, buf, n_buf), n_buf);\n\n    ASSERT_EQ(swoole_coroutine_fsync(fd), 0);\n    ASSERT_EQ(swoole_coroutine_fdatasync(fd), 0);\n\n    swoole_coroutine_close(fd);\n\n    fd = swoole_coroutine_open(test_file, O_RDONLY, 0);\n    char data[8192];\n    ASSERT_EQ(swoole_coroutine_read(fd, data, n_buf), n_buf);\n    ASSERT_EQ(std::string(buf, n_buf), std::string(data, n_buf));\n    swoole_coroutine_close(fd);\n\n    struct stat statbuf;\n    swoole_coroutine_stat(test_file, &statbuf);\n\n    File f1(test_file, File::RW);\n    ASSERT_EQ(statbuf.st_size, f1.get_size());\n\n    struct stat statbuf2;\n    swoole_coroutine_lstat(test_file, &statbuf2);\n    ASSERT_EQ(statbuf2.st_size, f1.get_size());\n    f1.close();\n\n    ASSERT_EQ(swoole_coroutine_unlink(test_file), 0);\n\n    File f2(test_file, File::RW | File::CREATE);\n    swoole_coroutine_lseek(f2.get_fd(), 0, SEEK_SET);\n\n    auto fp2 = swoole_coroutine_fdopen(f2.get_fd(), \"w+\");\n    ASSERT_NE(fp2, nullptr);\n\n    swoole_coroutine_fputs(\"hello\\n\", fp2);\n    swoole_coroutine_fputs(\"world\\n\", fp2);\n    ASSERT_EQ(swoole_coroutine_fflush(fp2), 0);\n\n    swoole_coroutine_lseek(f2.get_fd(), 0, SEEK_SET);\n    auto fp3 = swoole_coroutine_fdopen(f2.get_fd(), \"r+\");\n    ASSERT_NE(fp3, nullptr);\n\n    char rbuf[2048] = {};\n    ASSERT_EQ(swoole_coroutine_fread(rbuf, sizeof(rbuf), 1, fp3), 0);\n    ASSERT_STREQ(rbuf, \"hello\\nworld\\n\");\n\n    f2.close();\n    swoole_coroutine_fclose(fp2);\n    swoole_coroutine_fclose(fp3);\n\n    ASSERT_EQ(swoole_coroutine_unlink(test_file), 0);\n}\n\nTEST(coroutine_hook, file) {\n    coroutine::run([](void *arg) { test_file_hook(); });\n\n    test_file_hook();\n}\n\nTEST(coroutine_hook, gethostbyname) {\n    coroutine::run([](void *arg) {\n        auto result1 = swoole_coroutine_gethostbyname(host_1);\n        ASSERT_NE(result1, nullptr);\n\n        auto result2 = swoole_coroutine_gethostbyname(host_2);\n        ASSERT_EQ(result2, nullptr);\n        ASSERT_EQ(h_errno, HOST_NOT_FOUND);\n    });\n}\n\nTEST(coroutine_hook, getaddrinfo) {\n    coroutine::run([](void *arg) {\n        struct addrinfo hints;\n        sw_memset_zero(&hints, sizeof(struct addrinfo));\n        hints.ai_family = AF_INET;\n        hints.ai_socktype = SOCK_STREAM;\n        hints.ai_flags = AI_PASSIVE;\n\n        struct addrinfo *result, *curr;\n        int count;\n\n        result = nullptr;\n        auto result1 = swoole_coroutine_getaddrinfo(host_1, \"http\", &hints, &result);\n        ASSERT_EQ(result1, 0);\n\n        curr = result;\n        count = 0;\n        while (curr && curr->ai_addr) {\n            curr = curr->ai_next;\n            count++;\n        }\n        ASSERT_GE(count, 1);\n        freeaddrinfo(result);\n\n        result = nullptr;\n        auto result2 = swoole_coroutine_getaddrinfo(host_2, nullptr, &hints, &result);\n        ASSERT_EQ(result2, EAI_NONAME);\n        ASSERT_EQ(result, nullptr);\n        freeaddrinfo(result);\n    });\n}\n\nstatic void test_fstat() {\n    int fd = swoole_coroutine_open(TEST_TMP_FILE, O_RDONLY, 0);\n    struct stat statbuf_1;\n    swoole_coroutine_fstat(fd, &statbuf_1);\n\n    struct stat statbuf_2;\n    fstat(fd, &statbuf_2);\n\n    ASSERT_EQ(memcmp(&statbuf_1, &statbuf_2, sizeof(statbuf_2)), 0);\n\n    swoole_coroutine_close(fd);\n}\n\nTEST(coroutine_hook, fstat) {\n    coroutine::run([](void *arg) { test_fstat(); });\n    test_fstat();\n}\n\nstatic void test_statvfs() {\n    struct statvfs statbuf_1;\n    swoole_coroutine_statvfs(\"/tmp\", &statbuf_1);\n\n    struct statvfs statbuf_2;\n    statvfs(\"/tmp\", &statbuf_2);\n\n    ASSERT_EQ(memcmp(&statbuf_1, &statbuf_2, sizeof(statbuf_2)), 0);\n}\n\nTEST(coroutine_hook, statvfs) {\n    coroutine::run([](void *arg) { test_statvfs(); });\n    test_statvfs();\n}\n\nstatic void test_hook_dir() {\n    ASSERT_EQ(swoole_coroutine_mkdir(TEST_TMP_DIR, 0666), 0);\n    ASSERT_EQ(swoole_coroutine_access(TEST_TMP_DIR, R_OK), 0);\n    ASSERT_EQ(swoole_coroutine_rmdir(TEST_TMP_DIR), 0);\n    ASSERT_EQ(access(TEST_TMP_DIR, R_OK), -1);\n}\n\nTEST(coroutine_hook, dir) {\n    coroutine::run([](void *arg) { test_hook_dir(); });\n\n    test_hook_dir();\n}\n\nstatic void test_hook_socket() {\n    int sock = swoole_coroutine_socket(AF_INET, SOCK_STREAM, 0);\n    ASSERT_GT(sock, 0);\n    swoole::network::Address sa;\n    std::string ip = System::gethostbyname(\"www.baidu.com\", AF_INET, 10);\n    sa.assign(SW_SOCK_TCP, ip, 80);\n    ASSERT_EQ(swoole_coroutine_connect(sock, &sa.addr.ss, sa.len), 0);\n    ASSERT_EQ(swoole_coroutine_socket_wait_event(sock, SW_EVENT_WRITE, 5), SW_OK);\n\n    const char req[] = \"GET / HTTP/1.1\\r\\nHost: www.baidu.com\\r\\nConnection: close\\r\\nKeepAlive: off\\r\\n\\r\\n\";\n    ASSERT_EQ(swoole_coroutine_send(sock, req, strlen(req), 0), strlen(req));\n\n    swoole::String resp(1024);\n\n    while (1) {\n        ssize_t n = swoole_coroutine_recv(sock, resp.value() + resp.length, resp.size - resp.length, 0);\n        if (n <= 0) {\n            break;\n        }\n        resp.length += n;\n        if (resp.length == resp.size) {\n            resp.reserve(resp.size * 2);\n        }\n    }\n\n    ASSERT_GT(resp.length, 100);\n    ASSERT_TRUE(resp.contains(\"baidu.com\"));\n    swoole_coroutine_close(sock);\n}\n\nTEST(coroutine_hook, socket) {\n    coroutine::run([](void *arg) { test_hook_socket(); });\n\n    test_hook_socket();\n}\n\nTEST(coroutine_hook, rename) {\n    coroutine::run([](void *arg) {\n        char buf[8192];\n        size_t n_buf = sizeof(buf);\n        ASSERT_EQ(swoole_random_bytes(buf, n_buf), n_buf);\n\n        int fd = swoole_coroutine_open(test_file, O_WRONLY | O_TRUNC | O_CREAT, 0666);\n        ASSERT_EQ(swoole_coroutine_write(fd, buf, n_buf), n_buf);\n        swoole_coroutine_close(fd);\n\n        std::string to_file_name = std::string(test_file) + \".bak\";\n        ASSERT_EQ(swoole_coroutine_rename(test_file, to_file_name.c_str()), 0);\n        ASSERT_EQ(access(TEST_TMP_DIR, F_OK), -1);\n        ASSERT_EQ(access(to_file_name.c_str(), F_OK), 0);\n\n        swoole_coroutine_unlink(to_file_name.c_str());\n    });\n}\n\nTEST(coroutine_hook, flock) {\n    long start_time = swoole::time<std::chrono::milliseconds>();\n    coroutine::run([&](void *arg) {\n        Coroutine::create([&](void *arg) {\n            int fd = swoole_coroutine_open(TEST_TMP_FILE, O_WRONLY, 0);\n\n            ASSERT_EQ(swoole_coroutine_flock(fd, 16), SW_ERR);\n            ASSERT_ERREQ(EINVAL);\n\n            ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_EX), 0);\n            System::sleep(0.1);\n            ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_UN), 0);\n\n            ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_SH), 0);\n            ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_UN), 0);\n            ASSERT_LE(swoole::time<std::chrono::milliseconds>() - start_time, 1000);\n            swoole_coroutine_close(fd);\n        });\n        Coroutine::create([&](void *arg) {\n            int fd = swoole_coroutine_open(TEST_TMP_FILE, O_WRONLY, 0);\n            ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_SH), 0);\n            System::sleep(0.5);\n            ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_UN), 0);\n            swoole_coroutine_close(fd);\n        });\n    });\n    // LOCK_NB\n    coroutine::run([](void *arg) {\n        int fd1 = swoole_coroutine_open(TEST_TMP_FILE, O_WRONLY, 0);\n        ASSERT_EQ(swoole_coroutine_flock(fd1, LOCK_EX), 0);\n        int fd2 = swoole_coroutine_open(TEST_TMP_FILE, O_WRONLY, 0);\n        ASSERT_EQ(swoole_coroutine_flock(fd2, LOCK_EX | LOCK_NB), -1);\n        ASSERT_EQ(swoole_coroutine_flock(fd1, LOCK_UN), 0);\n        swoole_coroutine_close(fd1);\n        swoole_coroutine_close(fd2);\n    });\n}\n\nTEST(coroutine_hook, read_dir) {\n    auto fp = opendir(\"/tmp\");\n    std::string dir1(readdir(fp)->d_name);\n    std::string dir2(readdir(fp)->d_name);\n    closedir(fp);\n\n    auto fn = [&]() {\n        auto fp = swoole_coroutine_opendir(\"/tmp\");\n        ASSERT_NE(fp, nullptr);\n        struct dirent *entry;\n\n        entry = swoole_coroutine_readdir(fp);\n        ASSERT_NE(entry, nullptr);\n        ASSERT_STREQ(entry->d_name, dir1.c_str());\n\n        entry = swoole_coroutine_readdir(fp);\n        ASSERT_NE(entry, nullptr);\n        ASSERT_STREQ(entry->d_name, dir2.c_str());\n\n        swoole_coroutine_closedir(fp);\n    };\n\n    coroutine::run([&](void *arg) { fn(); });\n    fn();\n}\n\nTEST(coroutine_hook, readlink) {\n    auto fn = []() {\n        char buf1[1024] = {};\n        char buf2[1024] = {};\n\n        auto retval = swoole_coroutine_readlink(\"/proc/self/cwd\", buf1, sizeof(buf1));\n        ASSERT_NE(retval, -1);\n\n        getcwd(buf2, sizeof(buf2));\n        ASSERT_STREQ(buf1, buf2);\n    };\n\n    coroutine::run([&](void *arg) { fn(); });\n    fn();\n}\n\nTEST(coroutine_hook, stdio_1) {\n    auto fn = []() {\n        FILE *fp1 = swoole_coroutine_fopen(test_file, \"w+\");\n        const char *str = \"hello world\";\n        int n = swoole_coroutine_fputs(str, fp1);\n        ASSERT_TRUE(n);\n        swoole_coroutine_fclose(fp1);\n\n        FILE *fp2 = swoole_coroutine_fopen(test_file, \"r+\");\n        char buf[1024];\n        char *str2 = swoole_coroutine_fgets(buf, sizeof(buf), fp2);\n\n        ASSERT_STREQ(str2, str);\n        swoole_coroutine_fclose(fp2);\n\n        unlink(test_file);\n    };\n\n    coroutine::run([&](void *arg) { fn(); });\n    fn();\n}\n\nTEST(coroutine_hook, stdio_2) {\n    auto fn = []() {\n        size_t size = 1024;\n\n        FILE *fp1 = swoole_coroutine_fopen(test_file, \"w+\");\n        String str(size);\n        str.append_random_bytes(size);\n        size_t n = swoole_coroutine_fwrite(str.str, 1, size, fp1);\n        ASSERT_EQ(n, size);\n        swoole_coroutine_fclose(fp1);\n\n        FILE *fp2 = swoole_coroutine_fopen(test_file, \"r+\");\n        char buf[size];\n        size_t len = swoole_coroutine_fread(buf, 1, size, fp2);\n        ASSERT_EQ(len, size);\n\n        len = swoole_coroutine_fread(buf, 1, size, fp2);\n        ASSERT_EQ(len, 0);\n\n        ASSERT_TRUE(swoole_coroutine_feof(fp2));\n\n        ASSERT_MEMEQ(buf, str.str, size);\n        swoole_coroutine_fclose(fp2);\n\n        unlink(test_file);\n    };\n\n    coroutine::run([&](void *arg) { fn(); });\n    fn();\n}\n\nTEST(coroutine_hook, sleep) {\n    coroutine::run([&](void *arg) {\n        {\n            long ms1 = swoole::time<std::chrono::milliseconds>();\n            swoole_coroutine_sleep(2);\n            long ms2 = swoole::time<std::chrono::milliseconds>();\n            ASSERT_GE(ms2 - ms1, 1900);\n        }\n        {\n            long us_1 = swoole::time<std::chrono::microseconds>();\n            swoole_coroutine_usleep(50000);\n            long us_2 = swoole::time<std::chrono::microseconds>();\n            ASSERT_GE(us_2 - us_1, 45000);\n        }\n    });\n}\n\nTEST(coroutine_hook, exists) {\n    coroutine::run([&](void *arg) {\n        const int fd = 100;  // fake fd\n        ASSERT_EQ(swoole_coroutine_socket_create(fd), 0);\n        ASSERT_TRUE(swoole_coroutine_socket_exists(fd));\n        auto sock = swoole_coroutine_get_socket_object(fd);\n        ASSERT_EQ(sock->get_fd(), fd);\n        swoole_coroutine_close(fd);\n    });\n}\n\nTEST(coroutine_hook, timeout) {\n    coroutine::run([&](void *arg) {\n        int pairs[2];\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n        std::string text = \"Hello World\";\n        size_t length = text.length();\n\n        // unregister fd\n        errno = 0;\n        ASSERT_EQ(swoole_coroutine_socket_set_timeout(pairs[0], SO_SNDTIMEO, 0.05), -1);\n        ASSERT_EQ(errno, EINVAL);\n\n        errno = 0;\n        ASSERT_EQ(swoole_coroutine_socket_set_connect_timeout(pairs[0], 0.05), -1);\n        ASSERT_EQ(errno, EINVAL);\n\n        swoole::Coroutine::create([&](void *) {\n            ASSERT_EQ(swoole_coroutine_socket_create(pairs[0]), 0);\n\n            // unknown which\n            ASSERT_EQ(swoole_coroutine_socket_set_timeout(pairs[0], 100, 0.05), -1);\n\n            swoole_coroutine_socket_set_timeout(pairs[0], SO_SNDTIMEO, 0.05);\n            size_t result = swoole_coroutine_write(pairs[0], text.c_str(), length);\n            ASSERT_EQ(swoole_coroutine_close(pairs[0]), 0);\n            ASSERT_EQ(result, length);\n        });\n\n        char data[length + 1];\n        ASSERT_EQ(swoole_coroutine_socket_create(pairs[1]), 0);\n        swoole_coroutine_socket_set_timeout(pairs[1], SO_RCVTIMEO, 0.05);\n        size_t result = swoole_coroutine_read(pairs[1], data, length);\n        data[result] = '\\0';\n        ASSERT_EQ(swoole_coroutine_close(pairs[1]), 0);\n        ASSERT_EQ(result, length);\n        ASSERT_STREQ(data, text.c_str());\n    });\n}\n\nTEST(coroutine_hook, sendmsg_and_recvmsg) {\n    coroutine::run([&](void *arg) {\n        int pairs[2];\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n\n        std::string text = \"Hello World\";\n        size_t length = text.length();\n\n        swoole::Coroutine::create([&](void *) {\n            struct msghdr msg;\n            struct iovec ivec;\n\n            msg.msg_control = nullptr;\n            msg.msg_controllen = 0;\n            msg.msg_flags = 0;\n            msg.msg_name = nullptr;\n            msg.msg_namelen = 0;\n            msg.msg_iov = &ivec;\n            msg.msg_iovlen = 1;\n\n            ivec.iov_base = (void *) text.c_str();\n            ivec.iov_len = length;\n\n            ssize_t ret = swoole_coroutine_sendmsg(pairs[0], &msg, 0);\n            ASSERT_EQ(swoole_coroutine_close(pairs[0]), 0);\n            ASSERT_EQ(ret, length);\n        });\n\n        struct msghdr msg;\n        struct iovec ivec;\n        char buf[length + 1];\n\n        msg.msg_control = nullptr;\n        msg.msg_controllen = 0;\n        msg.msg_flags = 0;\n        msg.msg_name = nullptr;\n        msg.msg_namelen = 0;\n        msg.msg_iov = &ivec;\n        msg.msg_iovlen = 1;\n\n        ivec.iov_base = buf;\n        ivec.iov_len = length;\n\n        ssize_t ret = swoole_coroutine_recvmsg(pairs[1], &msg, 0);\n        buf[ret] = '\\0';\n        ASSERT_EQ(swoole_coroutine_close(pairs[1]), 0);\n        ASSERT_STREQ(buf, text.c_str());\n    });\n}\n\nTEST(coroutine_hook, lseek) {\n    std::string file = get_jpg_file();\n    int fd = swoole_coroutine_open(file.c_str(), O_RDONLY, 'r');\n    off_t offset = swoole_coroutine_lseek(fd, 0, SEEK_SET);\n    swoole_coroutine_close(fd);\n    ASSERT_EQ(offset, 0);\n}\n\nTEST(coroutine_hook, socket_close) {\n    coroutine::run([&](void *arg) {\n        auto pair = create_socket_pair();\n\n        auto buffer = sw_tg_buffer();\n        buffer->clear();\n        buffer->append_random_bytes(256 * 1024, false);\n\n        std::map<std::string, bool> results;\n        auto _sock = pair.first;\n        auto _fd = _sock->move_fd();\n        swoole_coroutine_socket_create(_fd);\n\n        // write co\n        Coroutine::create([&](void *) {\n            SW_LOOP_N(32) {\n                ssize_t result = swoole_coroutine_write(_fd, buffer->value(), buffer->get_length());\n                if (result < 0 && errno == ECANCELED) {\n                    ASSERT_EQ(swoole_coroutine_close(_fd), -1);\n                    ASSERT_EQ(errno, SW_ERROR_CO_SOCKET_CLOSE_WAIT);\n                    results[\"write\"] = true;\n                    break;\n                }\n            }\n        });\n\n        // read co\n        Coroutine::create([&](void *) {\n            SW_LOOP_N(32) {\n                char buf[4096];\n                ssize_t result = swoole_coroutine_read(_fd, buf, sizeof(buf));\n                if (result < 0 && errno == ECANCELED) {\n                    ASSERT_EQ(swoole_coroutine_close(_fd), 0);\n                    results[\"read\"] = true;\n                    break;\n                }\n            }\n        });\n\n        System::sleep(0.1);\n        ASSERT_EQ(swoole_coroutine_close(_fd), -1);\n        ASSERT_EQ(errno, SW_ERROR_CO_SOCKET_CLOSE_WAIT);\n        ASSERT_TRUE(results[\"write\"]);\n        ASSERT_TRUE(results[\"read\"]);\n    });\n}\n\nTEST(coroutine_hook, poll) {\n    coroutine::run([&](void *arg) {\n        auto pair = create_socket_pair();\n\n        auto buffer = sw_tg_buffer();\n        buffer->clear();\n        buffer->append_random_bytes(256 * 1024, false);\n\n        std::map<std::string, bool> results;\n        auto _sock0 = pair.first;\n        auto _fd0 = _sock0->move_fd();\n        swoole_coroutine_socket_create(_fd0);\n\n        auto _sock1 = pair.second;\n        auto _fd1 = _sock1->move_fd();\n        swoole_coroutine_socket_create(_fd1);\n\n        Coroutine::create([&](void *) {\n            ssize_t result;\n            result = swoole_coroutine_write(_fd0, buffer->value(), buffer->get_length());\n            ASSERT_GT(result, 0);\n            System::sleep(0.01);\n            result = swoole_coroutine_write(_fd1, buffer->value(), 16 * 1024);\n            ASSERT_GT(result, 0);\n        });\n\n        struct pollfd fds[2];\n        char buf[4096];\n\n        bzero(fds, sizeof(pollfd));\n        fds[0].fd = _fd0;\n        fds[0].events = POLLIN;\n        fds[1].fd = _fd1;\n        fds[1].events = POLLIN;\n\n        ASSERT_EQ(swoole_coroutine_poll(fds, 2, 1000), 1);\n        ASSERT_TRUE(fds[1].revents & POLLIN);\n\n        ssize_t result = swoole_coroutine_read(_fd1, buf, sizeof(buf));\n        ASSERT_GT(result, 1024);\n\n        System::sleep(0.02);\n\n        bzero(fds, sizeof(pollfd));\n        fds[0].fd = _fd0;\n        fds[0].events = POLLIN;\n        fds[1].fd = _fd1;\n        fds[1].events = POLLIN;\n\n        ASSERT_EQ(swoole_coroutine_poll(fds, 2, 1000), 2);\n        ASSERT_TRUE(fds[0].revents & POLLIN);\n        ASSERT_TRUE(fds[1].revents & POLLIN);\n        result = swoole_coroutine_read(_fd0, buf, sizeof(buf));\n        ASSERT_GT(result, 1024);\n        result = swoole_coroutine_read(_fd1, buf, sizeof(buf));\n        ASSERT_GT(result, 1024);\n\n        System::sleep(0.02);\n\n        bzero(fds, sizeof(pollfd));\n        fds[0].fd = _fd0;\n        fds[0].events = POLLIN | POLLOUT;\n        fds[1].fd = _fd1;\n        fds[1].events = POLLIN | POLLOUT;\n\n        ASSERT_EQ(swoole_coroutine_poll(fds, 2, 1000), 2);\n        ASSERT_TRUE(fds[0].revents & POLLIN);\n        ASSERT_TRUE(fds[1].revents & POLLIN);\n        ASSERT_FALSE(fds[0].revents & POLLOUT);  // not writable\n        ASSERT_TRUE(fds[1].revents & POLLOUT);\n        result = swoole_coroutine_read(_fd0, buf, sizeof(buf));\n        ASSERT_GT(result, 1024);\n        result = swoole_coroutine_read(_fd1, buf, sizeof(buf));\n        ASSERT_GT(result, 1024);\n    });\n}\n\nTEST(coroutine_hook, poll_fake) {\n    coroutine::run([&](void *arg) {\n        auto pair = create_socket_pair();\n\n        auto buffer = sw_tg_buffer();\n        buffer->clear();\n        buffer->append_random_bytes(256 * 1024, false);\n\n        std::map<std::string, bool> results;\n        auto _sock0 = pair.first;\n        auto _fd0 = _sock0->move_fd();\n        swoole_coroutine_socket_create(_fd0);\n\n        auto _sock1 = pair.second;\n        auto _fd1 = _sock1->move_fd();\n        swoole_coroutine_socket_create(_fd1);\n\n        Coroutine::create([&](void *) {\n            ssize_t result;\n            result = swoole_coroutine_write(_fd0, buffer->value(), buffer->get_length());\n            ASSERT_GT(result, 0);\n            System::sleep(0.01);\n            result = swoole_coroutine_write(_fd1, buffer->value(), 16 * 1024);\n            ASSERT_GT(result, 0);\n        });\n\n        struct pollfd fds[2];\n        char buf[4096];\n\n        bzero(fds, sizeof(pollfd));\n        fds[0].fd = _fd1;\n        fds[0].events = POLLIN;\n\n        ASSERT_EQ(swoole_coroutine_poll_fake(fds, 1, 1000), 1);\n        ASSERT_TRUE(fds[0].revents & POLLIN);\n\n        ssize_t result = swoole_coroutine_read(_fd1, buf, sizeof(buf));\n        ASSERT_GT(result, 1024);\n\n        bzero(fds, sizeof(pollfd));\n        ASSERT_EQ(swoole_coroutine_poll_fake(fds, 2, 1000), -1);\n        ASSERT_EQ(swoole_get_last_error(), SW_ERROR_INVALID_PARAMS);\n\n        System::sleep(0.02);\n\n        bzero(fds, sizeof(pollfd));\n        fds[0].fd = _fd0;\n        fds[0].events = POLLIN | POLLOUT;\n        ASSERT_EQ(swoole_coroutine_poll_fake(fds, 1, 1000), 1);\n        ASSERT_TRUE(fds[0].revents & POLLIN);\n        ASSERT_TRUE(fds[0].revents & POLLOUT);\n    });\n}\n\nTEST(coroutine_hook, unwrap) {\n    auto pair = create_socket_pair();\n    auto _sock0 = pair.first;\n    auto _sock1 = pair.second;\n\n    ASSERT_EQ(swoole_coroutine_socket_unwrap(_sock0->get_fd()), -1);\n\n    coroutine::run([&](void *arg) {\n        ASSERT_EQ(swoole_coroutine_socket_unwrap(999999), -1);\n        ASSERT_EQ(swoole_coroutine_socket_create(_sock0->get_fd()), 0);\n        ASSERT_GT(swoole_coroutine_write(_sock0->get_fd(), SW_STRL(TEST_STR)), 0);\n\n        // blocking, wrap-socket not exists\n        char buf[128];\n        ASSERT_EQ(swoole_coroutine_read(_sock1->get_fd(), buf, sizeof(buf)), strlen(TEST_STR));\n        ASSERT_EQ(swoole_coroutine_socket_unwrap(_sock1->get_fd()), -1);\n        // unwrap\n        ASSERT_EQ(swoole_coroutine_socket_unwrap(_sock0->get_fd()), 0);\n        // fail to unwrap\n        ASSERT_EQ(swoole_coroutine_socket_unwrap(_sock0->get_fd()), -1);\n    });\n}\n\nstatic void test_freopen() {\n    auto output_file = \"/tmp/output.txt\";\n    unlink(output_file);\n    unlink(TEST_LOG_FILE);\n\n    auto fp = swoole_coroutine_fopen(TEST_LOG_FILE, \"w\");\n    if (fp == NULL) {\n        fprintf(stderr, \"Failed to open '%s': %s (errno=%d)\\n\", TEST_LOG_FILE, strerror(errno), errno);\n    }\n    ASSERT_NE(fp, nullptr);\n    swoole_coroutine_fputs(\"hello\\n\", fp);\n\n    ASSERT_NE(swoole_coroutine_freopen(output_file, \"w\", fp), nullptr);\n    swoole_coroutine_fputs(\"world\\n\", fp);\n\n    swoole_coroutine_fclose(fp);\n\n    auto rs1 = swoole::file_get_contents(output_file);\n    ASSERT_FALSE(rs1->contains(\"hello\\n\"));\n    ASSERT_TRUE(rs1->contains(\"world\\n\"));\n\n    auto rs2 = swoole::file_get_contents(TEST_LOG_FILE);\n    ASSERT_TRUE(rs2->contains(\"hello\\n\"));\n    ASSERT_FALSE(rs2->contains(\"world\\n\"));\n\n    unlink(TEST_LOG_FILE);\n    unlink(output_file);\n}\n\nTEST(coroutine_hook, freopen) {\n    coroutine::run([&](void *arg) { test_freopen(); });\n    test_freopen();\n}\n\nTEST(coroutine_hook, ftruncate) {\n    coroutine::run([&](void *arg) {\n        int fd = swoole_coroutine_open(\"/tmp/123.txt\", O_CREAT | O_RDWR, 0);\n        ASSERT_TRUE(fd > 0);\n\n        const char *data = \"aaaaaaaaaaaaaaaaaaaaaaa\";\n        size_t length = strlen(data);\n        ssize_t write_length = swoole_coroutine_write(fd, (const void *) data, length);\n        ASSERT_TRUE(write_length == static_cast<ssize_t>(length));\n        ASSERT_TRUE(swoole_coroutine_ftruncate(fd, 0) == 0);\n        swoole_coroutine_close(fd);\n    });\n}\n\nTEST(coroutine_hook, get_socket_fail) {\n    {\n        auto rs = swoole_coroutine_get_socket_object(999);\n        ASSERT_EQ(errno, ENOTSOCK);\n        ASSERT_EQ(rs, nullptr);\n    }\n\n    {\n        int fd;\n        coroutine::run([&](void *arg) {\n            fd = swoole_coroutine_socket(AF_INET, SOCK_STREAM, 0);\n            ASSERT_GT(errno, 0);\n        });\n\n        auto rs = swoole_coroutine_get_socket_object_ex(fd);\n        ASSERT_EQ(errno, EWOULDBLOCK);\n        ASSERT_EQ(rs, nullptr);\n        swoole_coroutine_close(fd);\n    }\n}\n\nTEST(coroutine_hook, create_socket_fail) {\n    coroutine::run([&](void *arg) {\n        int fd = swoole_coroutine_socket(AF_INET + 99, SOCK_CLOEXEC, 0);\n        ASSERT_EQ(fd, -1);\n        ASSERT_EQ(errno, EAFNOSUPPORT);\n    });\n}\n"
  },
  {
    "path": "core-tests/src/coroutine/http_server.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_coroutine.h\"\n#include \"httplib_server.h\"\n\nusing namespace swoole::test;\nusing namespace httplib;\nusing namespace std;\n\nTEST(coroutine_http_server, get) {\n    Server svr;\n    mutex lock;\n    int port = swoole::test::get_random_port();\n    lock.lock();\n\n    thread t1([&lock, port]() {\n        lock.lock();\n        Client cli(TEST_HOST, port);\n        auto resp1 = cli.Get(\"/hi\");\n        EXPECT_EQ(resp1->status, 200);\n        EXPECT_EQ(resp1->body, string(\"Hello World!\"));\n\n        auto resp2 = cli.Get(\"/stop\");\n        EXPECT_EQ(resp2->status, 200);\n        EXPECT_EQ(resp2->body, string(\"Stop Server!\"));\n    });\n\n    coroutine::run([&lock, &svr, port](void *arg) {\n        svr.Get(\"/hi\", [](const Request &req, Response &res) { res.set_content(\"Hello World!\", \"text/plain\"); });\n\n        svr.Get(\"/stop\", [&svr](const Request &req, Response &res) {\n            res.set_content(\"Stop Server!\", \"text/plain\");\n            svr.stop();\n        });\n\n        svr.Post(\"/post\", [](const Request &req, Response &res) { res.set_content(\"Hello World!\", \"text/plain\"); });\n\n        svr.BeforeListen([&lock]() { lock.unlock(); });\n\n        ASSERT_TRUE(svr.listen(TEST_HOST, port));\n    });\n\n    t1.join();\n}\n\nTEST(coroutine_http_server, post) {\n    Server svr;\n    mutex lock;\n    int port = swoole::test::get_random_port();\n    lock.lock();\n\n    std::thread t1([&lock, port]() {\n        lock.lock();\n\n        Client cli(TEST_HOST, port);\n\n        httplib::Params params;\n        params.emplace(\"name\", \"john\");\n        params.emplace(\"note\", \"coder\");\n\n        auto resp1 = cli.Post(\"/post\", params);\n        EXPECT_EQ(resp1->status, 200);\n        EXPECT_EQ(resp1->body, string(\"Hello World!\"));\n\n        auto resp2 = cli.Get(\"/stop\");\n        EXPECT_EQ(resp2->status, 200);\n        EXPECT_EQ(resp2->body, string(\"Stop Server!\"));\n    });\n\n    coroutine::run([&lock, &svr, port](void *arg) {\n        svr.Get(\"/stop\", [&svr](const Request &req, Response &res) {\n            res.set_content(\"Stop Server!\", \"text/plain\");\n            svr.stop();\n        });\n\n        svr.Post(\"/post\", [](const Request &req, Response &res) { res.set_content(\"Hello World!\", \"text/plain\"); });\n\n        svr.BeforeListen([&lock]() { lock.unlock(); });\n\n        svr.listen(TEST_HOST, port);\n    });\n\n    t1.join();\n}\n"
  },
  {
    "path": "core-tests/src/coroutine/iouring.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: NathanFreeman  <mariasocute@163.com>                         |\n  +----------------------------------------------------------------------+\n */\n\n#include \"test_coroutine.h\"\n#include \"swoole_iouring.h\"\n\n#include <sys/file.h>\n#include <sys/stat.h>\n\n#ifdef SW_USE_IOURING\nusing swoole::Iouring;\nusing swoole::Reactor;\nusing swoole::coroutine::System;\nusing swoole::test::coroutine;\nusing swoole::test::create_socket_pair;\n\nTEST(iouring, create) {\n    coroutine::run([](void *arg) {\n        SwooleG.iouring_entries = 4;\n        SwooleG.iouring_workers = 65536;\n        auto fd = Iouring::open(TEST_TMP_FILE, O_CREAT, 0666);\n        ASSERT_GE(fd, 0);\n        ASSERT_NE(Iouring::close(fd), -1);\n    });\n}\n\nTEST(iouring, list_all_opcode) {\n    auto list = Iouring::list_all_opcode();\n    for (auto &item : list) {\n        DEBUG() << \"opcode: \" << item.first << \", value: \" << item.second << \"\\n\";\n    }\n    ASSERT_TRUE(list.size() > 0);\n}\n\nTEST(iouring, open_and_close) {\n    coroutine::run([](void *arg) {\n        const char *test_file = \"/tmp/file_1\";\n        int fd = Iouring::open(test_file, O_CREAT, 0666);\n        ASSERT_TRUE(fd > 0);\n\n        int result = Iouring::close(fd);\n        ASSERT_TRUE(result == 0);\n\n        result = Iouring::unlink(test_file);\n        ASSERT_TRUE(result == 0);\n    });\n}\n\nTEST(iouring, mkdir_and_rmdir) {\n    coroutine::run([](void *arg) {\n        const char *directory = \"/tmp/aaaa\";\n        int rv;\n\n        rv = Iouring::mkdir(directory, 0755);\n        ASSERT_EQ(rv, 0);\n\n        rv = Iouring::rmdir(directory);\n        ASSERT_EQ(rv, 0);\n    });\n}\n\nTEST(iouring, write_and_read) {\n    coroutine::run([](void *arg) {\n        const char *test_file = \"/tmp/file_2\";\n        int fd = Iouring::open(test_file, O_CREAT | O_RDWR, 0666);\n        ASSERT_TRUE(fd > 0);\n\n        const char *data = \"aaaaaaaaaaaaaaaaaaaaaaa\";\n        size_t length = strlen(data);\n        ssize_t result = Iouring::write(fd, (const void *) data, length);\n        ASSERT_TRUE(result > 0);\n        ASSERT_TRUE(result == static_cast<ssize_t>(length));\n\n        lseek(fd, 0, SEEK_SET);\n\n        char buf[128];\n        result = Iouring::read(fd, (void *) buf, 128);\n        ASSERT_TRUE(result > 0);\n        ASSERT_TRUE(result == static_cast<ssize_t>(length));\n        buf[result] = '\\0';\n        ASSERT_STREQ(data, buf);\n\n        result = Iouring::close(fd);\n        ASSERT_TRUE(result == 0);\n\n        result = Iouring::unlink(test_file);\n        ASSERT_TRUE(result == 0);\n    });\n}\n\nTEST(iouring, rename) {\n    coroutine::run([](void *arg) {\n        const char *oldpath = \"/tmp/file_2\";\n        const char *newpath = \"/tmp/file_3\";\n        int fd = Iouring::open(oldpath, O_CREAT | O_RDWR, 0666);\n        ASSERT_TRUE(fd > 0);\n\n        int result = Iouring::close(fd);\n        ASSERT_TRUE(result == 0);\n\n        result = Iouring::rename(oldpath, newpath);\n        ASSERT_TRUE(result == 0);\n\n        result = Iouring::unlink(newpath);\n        ASSERT_TRUE(result == 0);\n    });\n}\n\n#ifdef HAVE_IOURING_STATX\nTEST(iouring, fstat_and_stat) {\n    coroutine::run([](void *arg) {\n        struct stat statbuf {};\n        int fd = Iouring::open(TEST_TMP_FILE, O_CREAT | O_RDWR, 0666);\n        ASSERT_TRUE(fd > 0);\n\n        ASSERT_EQ(Iouring::write(fd, TEST_STR, strlen(TEST_STR)), strlen(TEST_STR));\n\n        int result = Iouring::fstat(fd, &statbuf);\n        ASSERT_TRUE(result == 0);\n        ASSERT_TRUE(statbuf.st_size > 0);\n\n        result = Iouring::close(fd);\n        ASSERT_TRUE(result == 0);\n\n        statbuf = {};\n        result = Iouring::stat(TEST_TMP_FILE, &statbuf);\n        ASSERT_TRUE(result == 0);\n        ASSERT_TRUE(statbuf.st_size > 0);\n    });\n}\n#endif\n\nTEST(iouring, fsync_and_fdatasync) {\n    coroutine::run([](void *arg) {\n        const char *test_file = \"/tmp/file_2\";\n        int fd = Iouring::open(test_file, O_CREAT | O_RDWR, 0666);\n        ASSERT_TRUE(fd > 0);\n\n        const char *data = \"aaaaaaaaaaaaaaaaaaaaaaa\";\n        size_t length = strlen(data);\n        ssize_t write_length = Iouring::write(fd, (const void *) data, length);\n        ASSERT_TRUE(write_length == static_cast<ssize_t>(length));\n\n        int result = Iouring::fsync(fd);\n        ASSERT_TRUE(result == 0);\n\n        write_length = Iouring::write(fd, (const void *) data, length);\n        ASSERT_TRUE(write_length == static_cast<ssize_t>(length));\n\n        result = Iouring::fdatasync(fd);\n        ASSERT_TRUE(result == 0);\n\n        result = Iouring::close(fd);\n        ASSERT_TRUE(result == 0);\n\n        result = Iouring::unlink(test_file);\n        ASSERT_TRUE(result == 0);\n    });\n}\n\n#ifdef HAVE_IOURING_FTRUNCATE\nTEST(iouring, ftruncate) {\n    coroutine::run([&](void *arg) {\n        const char *test_file = \"/tmp/file_3\";\n        int fd = Iouring::open(test_file, O_CREAT | O_RDWR, 0666);\n        ASSERT_TRUE(fd > 0);\n\n        const char *data = \"aaaaaaaaaaaaaaaaaaaaaaa\";\n        size_t length = strlen(data);\n        ssize_t write_length = Iouring::write(fd, (const void *) data, length);\n        ASSERT_TRUE(write_length == static_cast<ssize_t>(length));\n\n        int result = Iouring::ftruncate(fd, 0);\n        ASSERT_TRUE(result == 0);\n        Iouring::close(fd);\n    });\n}\n#endif\n#endif\n\nTEST(iouring, connect) {\n    signal(SIGPIPE, SIG_IGN);\n    coroutine::run([](void *arg) {\n        int fd = Iouring::socket(AF_INET, SOCK_STREAM, 0);\n        ASSERT_NE(fd, -1);\n\n        swoole::network::Address addr{};\n        ASSERT_TRUE(addr.assign(SW_SOCK_TCP, TEST_HTTP_DOMAIN, 80, true));\n\n        int rv = Iouring::connect(fd, &addr.addr.ss, addr.len);\n\n        auto req = swoole::test::http_get_request(TEST_HTTP_DOMAIN, \"/\");\n\n        rv = Iouring::write(fd, req.c_str(), req.length());\n        ASSERT_EQ(rv, req.length());\n\n        char buf[4096];\n\n        rv = Iouring::read(fd, buf, sizeof(buf));\n        ASSERT_GT(rv, 100);\n\n        std::string s{buf};\n        ASSERT_TRUE(s.find(TEST_HTTP_EXPECT) != s.npos);\n\n        Iouring::close(fd);\n    });\n}\n\nTEST(iouring, send_recv) {\n    signal(SIGPIPE, SIG_IGN);\n    coroutine::run([](void *arg) {\n        int fd = Iouring::socket(AF_INET, SOCK_STREAM, 0);\n        ASSERT_NE(fd, -1);\n\n        swoole::network::Address addr{};\n        ASSERT_TRUE(addr.assign(SW_SOCK_TCP, TEST_HTTP_DOMAIN, 80, true));\n\n        int rv = Iouring::connect(fd, &addr.addr.ss, addr.len);\n\n        auto req = swoole::test::http_get_request(TEST_HTTP_DOMAIN, \"/\");\n        rv = Iouring::send(fd, req.c_str(), req.length(), 0);\n        ASSERT_EQ(rv, req.length());\n\n        char buf[4096];\n\n        rv = Iouring::recv(fd, buf, sizeof(buf), 0);\n        ASSERT_GT(rv, 100);\n\n        std::string s{buf};\n        ASSERT_TRUE(s.find(TEST_HTTP_EXPECT) != s.npos);\n\n        Iouring::close(fd);\n    });\n}\n\nTEST(iouring, sendfile) {\n    swoole::File file(swoole::test::get_jpg_file(), O_RDONLY);\n\n    coroutine::run([&](void *arg) {\n        int pairs[2];\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n\n        Coroutine::create([&](void *) {\n            off_t offset = 0;\n            auto rv = Iouring::sendfile(pairs[0], file.get_fd(), &offset, file.get_size());\n            ASSERT_EQ(rv, file.get_size());\n        });\n\n        char data[250000];\n        CoSocket sock(pairs[1], SW_SOCK_UNIX_STREAM);\n        ssize_t result = sock.read(data, 250000);\n        data[result] = '\\0';\n        sock.close();\n        ASSERT_EQ(result, file.get_size());\n    });\n}\n\nstatic int uring_create_server_socket(struct sockaddr_in *actual_server_addr, socklen_t *actual_server_addr_len) {\n    // Create a TCP socket using coroutine API\n    int server_sock = Iouring::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);\n    EXPECT_GT(server_sock, 0);\n\n    // Bind the socket to localhost with port 0 (auto-assign)\n    struct sockaddr_in server_addr;\n    memset(&server_addr, 0, sizeof(server_addr));\n    server_addr.sin_family = AF_INET;\n    server_addr.sin_addr.s_addr = inet_addr(\"127.0.0.1\");\n    server_addr.sin_port = 0;\n\n    int retval = Iouring::bind(server_sock, (struct sockaddr *) &server_addr, sizeof(server_addr));\n    EXPECT_EQ(retval, 0);\n\n    // Listen on the socket\n    retval = Iouring::listen(server_sock, 128);\n    EXPECT_EQ(retval, 0);\n\n    // Get the actual server port\n    EXPECT_EQ(getsockname(server_sock, (struct sockaddr *) actual_server_addr, actual_server_addr_len), 0);\n\n    return server_sock;\n}\n\nTEST(iouring, accept) {\n    coroutine::run([](void *arg) {\n        struct sockaddr_in actual_server_addr;\n        socklen_t actual_server_addr_len = sizeof(actual_server_addr);\n        auto server_sock = uring_create_server_socket(&actual_server_addr, &actual_server_addr_len);\n\n        struct sockaddr_in client_addr;\n        socklen_t client_addr_len = sizeof(client_addr);\n\n        // Test that swoole_coroutine_accept works correctly\n        Coroutine::create([&](void *arg) {\n            // Give the server time to start listening\n            System::sleep(0.01);\n\n            // Connect to the server using coroutine API\n            int client_sock = Iouring::socket(AF_INET, SOCK_STREAM, 0);\n            ASSERT_GT(client_sock, 0);\n\n            // Connect to the server\n            auto retval =\n                Iouring::connect(client_sock, (struct sockaddr *) &actual_server_addr, actual_server_addr_len);\n            ASSERT_EQ(retval, 0);\n\n            // Send a test message\n            const char *test_message = \"test_data\";\n            ssize_t sent_bytes = Iouring::send(client_sock, test_message, strlen(test_message), 0);\n            ASSERT_EQ(sent_bytes, (ssize_t) strlen(test_message));\n\n            // Close the client socket\n            Iouring::close(client_sock);\n        });\n\n        // Accept the connection using coroutine API\n        int client_sock = Iouring::accept(server_sock, (struct sockaddr *) &client_addr, &client_addr_len);\n        ASSERT_GT(client_sock, 0);\n\n        // Receive data from client\n        char buffer[256] = {};\n        ssize_t received_bytes = Iouring::recv(client_sock, buffer, sizeof(buffer) - 1, 0);\n        ASSERT_GT(received_bytes, 0);\n        ASSERT_STREQ(buffer, \"test_data\");\n\n        // Close the client socket\n        Iouring::close(client_sock);\n        Iouring::close(server_sock);\n    });\n}\n\nTEST(iouring, sleep) {\n    coroutine::run([](void *arg) {\n        {\n            auto begin = swoole::microtime();\n            ASSERT_EQ(Iouring::sleep(1, 200 * SW_NUM_MILLION), 0);\n            auto end = swoole::microtime();\n            ASSERT_GE(end - begin, 1.2);\n        }\n\n        {\n            auto begin = swoole::microtime();\n            ASSERT_EQ(Iouring::sleep(0, 300 * SW_NUM_MILLION), 0);\n            auto end = swoole::microtime();\n            ASSERT_GE(end - begin, 0.3);\n        }\n    });\n}\n\nTEST(iouring, wait_success) {\n    auto pid = swoole::test::spawn_exec([]() { sleep(1); });\n\n    coroutine::run([pid](void *arg) {\n        int status;\n        ASSERT_EQ(Iouring::wait(&status, 5), pid);\n        ASSERT_EQ(status, 0);\n    });\n}\n\nTEST(iouring, wait_timeout) {\n    auto pid = swoole::test::spawn_exec([]() { sleep(2000); });\n\n    coroutine::run([pid](void *arg) {\n        int status = 0x9501;\n        ASSERT_EQ(Iouring::wait(&status, 0.5), -1);\n        ASSERT_EQ(errno, ETIMEDOUT);\n        ASSERT_EQ(status, 0x9501);  // After the timeout, the status will not be set.\n    });\n\n    kill(pid, SIGKILL);\n}\n\nTEST(iouring, waitpid) {\n    auto pid = swoole::test::spawn_exec([]() { sleep(2000); });\n\n    coroutine::run([pid](void *arg) {\n        int status;\n        ASSERT_EQ(Iouring::waitpid(pid, &status, WNOHANG, -1), 0);\n        ASSERT_EQ(Iouring::waitpid(pid, &status, 0, 0.1), -1);\n        ASSERT_EQ(errno, ETIMEDOUT);\n\n        kill(pid, SIGKILL);\n        System::sleep(0.3);\n        ASSERT_EQ(Iouring::waitpid(pid, &status, 0, 0.1), pid);\n    });\n}\n\nTEST(iouring, poll) {\n    auto buffer = sw_tg_buffer();\n    buffer->clear();\n    buffer->append_random_bytes(256 * 1024, false);\n\n    coroutine::run([=](void *arg) {\n        int pair[2];\n        struct pollfd fds[2];\n        char buf[4096];\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pair);\n\n        std::map<std::string, bool> results;\n        auto _fd0 = pair[0];\n        auto _fd1 = pair[1];\n\n        bzero(fds, sizeof(pollfd));\n        fds[0].fd = _fd0;\n        fds[0].events = POLLIN | POLLOUT;\n        ASSERT_EQ(Iouring::poll(fds, 1, 1000), 1);\n        ASSERT_TRUE(fds[0].revents & POLLOUT);\n\n        Coroutine::create([=](void *) {\n            ssize_t result;\n            result = Iouring::write(_fd0, buffer->value(), buffer->get_length());\n            ASSERT_GT(result, 0);\n            Iouring::sleep(0.1);\n            result = Iouring::write(_fd1, buffer->value(), 16 * 1024);\n            ASSERT_GT(result, 0);\n        });\n\n        bzero(fds, sizeof(pollfd));\n        fds[0].fd = _fd1;\n        fds[0].events = POLLIN;\n\n        ASSERT_EQ(Iouring::poll(fds, 1, 1000), 1);\n        ASSERT_TRUE(fds[0].revents & POLLIN);\n\n        ssize_t result = Iouring::read(_fd1, buf, sizeof(buf));\n        ASSERT_GT(result, 1024);\n\n        bzero(fds, sizeof(pollfd));\n        ASSERT_EQ(Iouring::poll(fds, 2, 1000), -1);\n        ASSERT_EQ(swoole_get_last_error(), SW_ERROR_INVALID_PARAMS);\n\n        System::sleep(0.02);\n\n        bzero(fds, sizeof(pollfd));\n        fds[0].fd = _fd0;\n        fds[0].events = POLLIN | POLLOUT;\n        ASSERT_EQ(Iouring::poll(fds, 1, 1000), 1);\n        ASSERT_TRUE(fds[0].revents & POLLIN);\n    });\n}\n\nTEST(iouring, accept_timeout) {\n    coroutine::run([](void *arg) {\n        struct sockaddr_in actual_server_addr;\n        socklen_t actual_server_addr_len = sizeof(actual_server_addr);\n        auto server_sock = uring_create_server_socket(&actual_server_addr, &actual_server_addr_len);\n\n        struct sockaddr_in client_addr;\n        socklen_t client_addr_len = sizeof(client_addr);\n\n        int client_sock = Iouring::accept(server_sock, (struct sockaddr *) &client_addr, &client_addr_len, 0, 0.1);\n        ASSERT_EQ(client_sock, -1);\n\n        Iouring::close(server_sock);\n    });\n}\n"
  },
  {
    "path": "core-tests/src/coroutine/socket.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_process.h\"\n#include \"test_coroutine.h\"\n#include \"test_server.h\"\n\nusing swoole::Coroutine;\nusing swoole::HttpProxy;\nusing swoole::Protocol;\nusing swoole::RecvData;\nusing swoole::Socks5Proxy;\nusing swoole::String;\nusing swoole::coroutine::Socket;\nusing swoole::coroutine::System;\nusing swoole::network::Address;\nusing swoole::network::IOVector;\nusing swoole::test::coroutine;\nusing swoole::test::create_socket_pair;\nusing swoole::test::Process;\nusing swoole::test::Server;\n\nconst std::string host = \"www.baidu.com\";\n\nTEST(coroutine_socket, connect_refused) {\n    coroutine::run([](void *arg) {\n        Socket sock(SW_SOCK_TCP);\n        bool retval = sock.connect(\"127.0.0.1\", 9801);\n        ASSERT_EQ(retval, false);\n        ASSERT_EQ(sock.errCode, ECONNREFUSED);\n    });\n}\n\nTEST(coroutine_socket, connect_timeout) {\n    coroutine::run([](void *arg) {\n        Socket sock(SW_SOCK_TCP);\n\n        sock.set_timeout(0.5);\n        ASSERT_EQ(sock.get_timeout(SW_TIMEOUT_DNS), 0.5);\n        ASSERT_EQ(sock.get_timeout(SW_TIMEOUT_CONNECT), 0.5);\n        ASSERT_EQ(sock.get_timeout(SW_TIMEOUT_READ), 0.5);\n        ASSERT_EQ(sock.get_timeout(SW_TIMEOUT_WRITE), 0.5);\n\n        sock.set_timeout(1.5, SW_TIMEOUT_RDWR);\n        ASSERT_EQ(sock.get_timeout(SW_TIMEOUT_DNS), 0.5);\n        ASSERT_EQ(sock.get_timeout(SW_TIMEOUT_CONNECT), 0.5);\n        ASSERT_EQ(sock.get_timeout(SW_TIMEOUT_READ), 1.5);\n        ASSERT_EQ(sock.get_timeout(SW_TIMEOUT_WRITE), 1.5);\n\n        bool retval = sock.connect(\"192.0.0.1\", 9801);\n        ASSERT_EQ(retval, false);\n        ASSERT_EQ(sock.errCode, ETIMEDOUT);\n    });\n}\n\nTEST(coroutine_socket, timeout_controller) {\n    coroutine::run([](void *arg) {\n        const int port = __LINE__ + TEST_PORT;\n        Coroutine::create([](void *arg) {\n            Socket sock(SW_SOCK_TCP);\n            bool retval = sock.bind(\"127.0.0.1\", port);\n            ASSERT_EQ(retval, true);\n            ASSERT_EQ(sock.listen(128), true);\n\n            Socket *conn = sock.accept();\n            conn->send(TEST_STR);\n            System::sleep(1);\n            delete conn;\n        });\n\n        Socket sock(SW_SOCK_TCP);\n        Socket::TimeoutController tc(&sock, 0.5, SW_TIMEOUT_ALL);\n        ASSERT_TRUE(sock.connect(\"127.0.0.1\", port));\n\n        char buf[128];\n        off_t offset = 0;\n        sock.errCode = 0;\n        while (true) {\n            if (sw_unlikely(tc.has_timedout(SW_TIMEOUT_READ))) {\n                break;\n            }\n            auto rv = sock.recv(buf + offset, sizeof(buf) - offset);\n            if (rv <= 0) {\n                break;\n            }\n            offset += rv;\n        }\n        ASSERT_TRUE(tc.has_timedout(SW_TIMEOUT_READ));\n        ASSERT_EQ(sock.errCode, ETIMEDOUT);\n    });\n}\n\nTEST(coroutine_socket, timeout_setter) {\n    coroutine::run([](void *arg) {\n        const int port = __LINE__ + TEST_PORT;\n        Coroutine::create([](void *arg) {\n            Socket sock(SW_SOCK_TCP);\n            bool retval = sock.bind(\"127.0.0.1\", port);\n            ASSERT_EQ(retval, true);\n            ASSERT_EQ(sock.listen(128), true);\n\n            Socket *conn = sock.accept();\n            conn->send(TEST_STR);\n            System::sleep(1);\n            delete conn;\n        });\n\n        Socket sock(SW_SOCK_TCP);\n        Socket::TimeoutSetter ts(&sock, 0.5, SW_TIMEOUT_ALL);\n        ASSERT_TRUE(sock.connect(\"127.0.0.1\", port));\n\n        char buf[128];\n        off_t offset = 0;\n        sock.errCode = 0;\n        while (true) {\n            auto rv = sock.recv(buf + offset, sizeof(buf) - offset);\n            if (rv <= 0) {\n                break;\n            }\n            offset += rv;\n        }\n        ASSERT_EQ(sock.errCode, ETIMEDOUT);\n    });\n}\n\nTEST(coroutine_socket, connect_with_dns) {\n    coroutine::run([](void *arg) {\n        Socket sock(SW_SOCK_TCP);\n        bool retval = sock.connect(host, 80);\n        ASSERT_EQ(retval, true);\n        ASSERT_EQ(sock.errCode, 0);\n    });\n}\n\nTEST(coroutine_socket, tcp6) {\n    coroutine::run([](void *arg) {\n        Socket sock(SW_SOCK_TCP6);\n        bool retval = sock.connect(\"::1\", 80);\n        ASSERT_EQ(retval, true);\n        ASSERT_EQ(sock.errCode, 0);\n    });\n}\n\nTEST(coroutine_socket, unixsock_fail) {\n    coroutine::run([](void *arg) {\n        Socket sock(SW_SOCK_UNIX_STREAM);\n        bool retval = sock.connect(\"/tmp/unix.sock\");\n        ASSERT_EQ(retval, false);\n        ASSERT_EQ(sock.errCode, ENOENT);\n    });\n}\n\nTEST(coroutine_socket, recv_success) {\n    pid_t pid;\n    int port = swoole::test::get_random_port();\n\n    Process proc([port](Process *proc) {\n        Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_TCP);\n        serv.on(\"Receive\", [](ON_RECEIVE_PARAMS) {\n            SERVER_THIS->send(req->info.fd, req->data, req->info.len);\n            return 0;\n        });\n        serv.start();\n    });\n\n    pid = proc.start();\n\n    sleep(1);  // wait for the test server to start\n\n    coroutine::run([port](void *arg) {\n        Socket sock(SW_SOCK_TCP);\n        bool retval = sock.connect(TEST_HOST, port, -1);\n        ASSERT_EQ(retval, true);\n        ASSERT_EQ(sock.errCode, 0);\n        sock.send(SW_STRS(\"hello world\\n\"));\n        char buf[128];\n        int n = sock.recv(buf, sizeof(buf));\n        buf[n] = 0;\n        ASSERT_EQ(strcmp(buf, \"hello world\\n\"), 0);\n    });\n\n    kill(pid, SIGTERM);\n    int status;\n    wait(&status);\n}\n\nTEST(coroutine_socket, recv_fail) {\n    pid_t pid;\n    int port = swoole::test::get_random_port();\n\n    Process proc([port](Process *proc) {\n        Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_TCP);\n        serv.on(\"Receive\", [](ON_PACKET_PARAMS) -> int {\n            serv->close(req->info.fd, 0);\n            return 0;\n        });\n        serv.start();\n    });\n\n    pid = proc.start();\n\n    sleep(1);  // wait for the test server to start\n\n    coroutine::run([port](void *arg) {\n        Socket sock(SW_SOCK_TCP);\n        bool retval = sock.connect(TEST_HOST, port, -1);\n        ASSERT_EQ(retval, true);\n        ASSERT_EQ(sock.errCode, 0);\n        sock.send(\"close\", 6);\n        char buf[128];\n        int n = sock.recv(buf, sizeof(buf));\n        ASSERT_EQ(n, 0);\n    });\n\n    kill(pid, SIGTERM);\n    int status;\n    wait(&status);\n}\n\nTEST(coroutine_socket, bind_success) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run([port](void *arg) {\n        Socket sock(SW_SOCK_TCP);\n        bool retval = sock.bind(\"127.0.0.1\", port);\n        ASSERT_EQ(retval, true);\n\n        Socket sock_1(SW_SOCK_UNIX_DGRAM);\n        retval = sock_1.bind(\"/tmp/swoole-core-tests.sock\");\n        ASSERT_EQ(retval, true);\n    });\n}\n\nTEST(coroutine_socket, bind_fail) {\n    coroutine::run([](void *arg) {\n        Socket sock(SW_SOCK_TCP);\n        bool retval = sock.bind(\"192.111.11.1\", 9909);\n        ASSERT_EQ(retval, false);\n        ASSERT_EQ(sock.errCode, EADDRNOTAVAIL);\n\n        Socket sock_1(SW_SOCK_TCP);\n        retval = sock_1.bind(\"127.0.0.1\", 70000);\n        ASSERT_EQ(retval, false);\n    });\n}\n\nTEST(coroutine_socket, listen) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run([port](void *arg) {\n        Socket sock(SW_SOCK_TCP);\n        bool retval = sock.bind(\"127.0.0.1\", port);\n        ASSERT_EQ(retval, true);\n        ASSERT_EQ(sock.listen(128), true);\n    });\n}\n\nTEST(coroutine_socket, accept) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run({[port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.bind(\"127.0.0.1\", port);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.listen(128), true);\n\n                        Socket *conn = sock.accept();\n                        ASSERT_NE(conn, nullptr);\n                        delete conn;\n                    },\n\n                    [port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.errCode, 0);\n                        sock.close();\n                    }});\n}\n\nstatic void socket_set_eof_protocol(Socket &sock) {\n    memcpy(sock.protocol.package_eof, SW_STRL(CRLF));\n    sock.protocol.package_eof_len = 2;\n    sock.open_eof_check = true;\n}\n\nTEST(coroutine_socket, eof_1) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run({[port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.bind(\"127.0.0.1\", port);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.listen(128), true);\n\n                        Socket *conn = sock.accept();\n                        char buf[1024];\n                        ssize_t l = conn->recv(buf, sizeof(buf));\n                        EXPECT_EQ(string(buf, l), string(\"start\\r\\n\"));\n                        conn->send(EOF_PACKET);\n                    },\n\n                    [port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.errCode, 0);\n                        sock.send(\"start\\r\\n\");\n\n                        socket_set_eof_protocol(sock);\n\n                        ssize_t l = sock.recv_packet(RECV_TIMEOUT);\n                        size_t eof_packet_len = strlen(EOF_PACKET);\n                        auto buf = sock.get_read_buffer();\n\n                        ASSERT_EQ(l, eof_packet_len);\n                        ASSERT_EQ(string(buf->str, l), string(EOF_PACKET));\n                        ASSERT_EQ(buf->length, eof_packet_len);\n                        ASSERT_EQ(buf->offset, eof_packet_len);\n                    }});\n}\n\nTEST(coroutine_socket, eof_2) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run({[port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        ASSERT_TRUE(sock.bind(\"127.0.0.1\", port));\n                        ASSERT_TRUE(sock.listen(128));\n\n                        Socket *conn = sock.accept();\n                        char buf[1024];\n                        ssize_t l = conn->recv(buf, sizeof(buf));\n                        EXPECT_EQ(string(buf, l), string(\"start\\r\\n\"));\n                        conn->send(EOF_PACKET EOF_PACKET_2);\n                    },\n\n                    [port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.errCode, 0);\n                        sock.send(\"start\\r\\n\");\n\n                        socket_set_eof_protocol(sock);\n\n                        // packet 1\n                        {\n                            ssize_t l = sock.recv_packet(RECV_TIMEOUT);\n                            size_t eof_packet_len = strlen(EOF_PACKET);\n                            auto buf = sock.get_read_buffer();\n\n                            ASSERT_EQ(l, eof_packet_len);\n                            ASSERT_EQ(string(buf->str, l), string(EOF_PACKET));\n                            ASSERT_EQ(buf->length, strlen(EOF_PACKET EOF_PACKET_2));\n                            ASSERT_EQ(buf->offset, eof_packet_len);\n                        }\n                        // packet 2\n                        {\n                            ssize_t l = sock.recv_packet(RECV_TIMEOUT);\n                            size_t eof_packet_len = strlen(EOF_PACKET_2);\n                            auto buf = sock.get_read_buffer();\n\n                            ASSERT_EQ(l, eof_packet_len);\n                            ASSERT_EQ(string(buf->str, l), string(EOF_PACKET_2));\n                            ASSERT_EQ(buf->length, strlen(EOF_PACKET_2));\n                            ASSERT_EQ(buf->offset, eof_packet_len);\n                        }\n                    }});\n}\n\nTEST(coroutine_socket, eof_3) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run({[port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.bind(\"127.0.0.1\", port);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.listen(128), true);\n\n                        Socket *conn = sock.accept();\n                        char buf[1024];\n                        ssize_t l = conn->recv(buf, sizeof(buf));\n                        EXPECT_EQ(string(buf, l), string(\"start\\r\\n\"));\n                        conn->shutdown();\n                    },\n\n                    [port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.errCode, 0);\n                        sock.send(\"start\\r\\n\");\n\n                        socket_set_eof_protocol(sock);\n\n                        ssize_t l = sock.recv_packet(RECV_TIMEOUT);\n                        ASSERT_EQ(l, 0);\n                    }});\n}\n\nTEST(coroutine_socket, eof_4) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run({[port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.bind(\"127.0.0.1\", port);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.listen(128), true);\n\n                        Socket *conn = sock.accept();\n                        char buf[1024];\n                        ssize_t l = conn->recv(buf, sizeof(buf));\n                        EXPECT_EQ(string(buf, l), string(\"start\\r\\n\"));\n                        conn->send(EOF_PACKET, strlen(EOF_PACKET) - strlen(CRLF));  // no eof\n                        conn->shutdown();\n                    },\n\n                    [port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.errCode, 0);\n                        sock.send(\"start\\r\\n\");\n\n                        socket_set_eof_protocol(sock);\n\n                        ssize_t l = sock.recv_packet(RECV_TIMEOUT);\n                        ASSERT_EQ(l, 0);\n\n                        auto buf = sock.get_read_buffer();\n                        ASSERT_EQ(string(buf->str, 10), string(EOF_PACKET, 10));\n                    }});\n}\n\nTEST(coroutine_socket, eof_5) {\n    const int port = __LINE__ + TEST_PORT;\n    size_t pkt_len = 512 * 1024;\n    coroutine::run({[pkt_len, port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.bind(\"127.0.0.1\", port);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.listen(128), true);\n\n                        Socket *conn = sock.accept();\n                        char buf[1024];\n                        ssize_t l = conn->recv(buf, sizeof(buf));\n                        EXPECT_EQ(string(buf, l), string(\"start\\r\\n\"));\n\n                        String *s = swoole::make_string(pkt_len);\n                        s->repeat(\"A\", 1, pkt_len - 16);\n                        s->append(SW_STRL(CRLF));\n\n                        conn->get_socket()->set_send_buffer_size(65536);\n                        conn->send_all(s->str, s->length);\n                    },\n\n                    [pkt_len, port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.errCode, 0);\n                        sock.send(\"start\\r\\n\");\n\n                        socket_set_eof_protocol(sock);\n\n                        sock.get_socket()->set_recv_buffer_size(65536);\n                        ssize_t l = sock.recv_packet(RECV_TIMEOUT);\n                        ASSERT_EQ(l, pkt_len - 14);\n                    }});\n}\n\nTEST(coroutine_socket, eof_6) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run({[port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.bind(\"127.0.0.1\", port);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.listen(128), true);\n\n                        Socket *conn = sock.accept();\n                        char buf[1024];\n                        ssize_t l = conn->recv(buf, sizeof(buf));\n                        EXPECT_EQ(string(buf, l), string(\"start\\r\\n\"));\n\n                        String s(128 * 1024);\n                        s.repeat(\"A\", 1, 128 * 1024 - 16);\n                        s.append(SW_STRL(CRLF));\n\n                        conn->send_all(s.value(), s.get_length());\n                    },\n\n                    [port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.errCode, 0);\n                        sock.send(\"start\\r\\n\");\n\n                        socket_set_eof_protocol(sock);\n                        sock.protocol.package_max_length = 1024 * 64;\n\n                        ssize_t l = sock.recv_packet(RECV_TIMEOUT);\n                        ASSERT_EQ(l, -1);\n                        ASSERT_EQ(sock.errCode, SW_ERROR_PACKAGE_LENGTH_TOO_LARGE);\n                    }});\n}\n\nstatic void socket_set_length_protocol_1(Socket &sock) {\n    sock.protocol = {};\n\n    sock.protocol.package_length_type = 'n';\n    sock.protocol.package_length_size = swoole_type_size(sock.protocol.package_length_type);\n    sock.protocol.package_body_offset = 2;\n    sock.protocol.get_package_length = Protocol::default_length_func;\n    sock.protocol.package_max_length = 65535;\n\n    sock.open_length_check = true;\n}\n\nstatic void socket_set_length_protocol_2(Socket &sock) {\n    sock.protocol = {};\n\n    sock.protocol.package_length_type = 'N';\n    sock.protocol.package_length_size = swoole_type_size(sock.protocol.package_length_type);\n    sock.protocol.package_body_offset = 4;\n    sock.protocol.get_package_length = Protocol::default_length_func;\n    sock.protocol.package_max_length = 2 * 1024 * 1024;\n\n    sock.open_length_check = true;\n}\n\nTEST(coroutine_socket, length_1) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run({[port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.bind(\"127.0.0.1\", port);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.listen(128), true);\n\n                        Socket *conn = sock.accept();\n                        char buf[1024];\n                        ssize_t l = swoole_random_bytes(buf + 2, sizeof(buf) - 2);\n                        *(uint16_t *) buf = htons(l);\n\n                        conn->send(buf, l + 2);\n                    },\n\n                    [port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.errCode, 0);\n\n                        socket_set_length_protocol_1(sock);\n\n                        ssize_t l = sock.recv_packet(RECV_TIMEOUT);\n                        auto buf = sock.get_read_buffer();\n\n                        ASSERT_EQ(l, 1024);\n                        ASSERT_EQ(buf->length, l);\n                        ASSERT_EQ(buf->offset, l);\n                    }});\n}\n\nTEST(coroutine_socket, length_2) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run({[port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.bind(\"127.0.0.1\", port);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.listen(128), true);\n\n                        Socket *conn = sock.accept();\n                        char buf[1024];\n                        *(uint16_t *) buf = htons(0);\n\n                        conn->send(buf, 2);\n                    },\n\n                    [port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.errCode, 0);\n\n                        socket_set_length_protocol_1(sock);\n\n                        ssize_t l = sock.recv_packet(RECV_TIMEOUT);\n                        auto buf = sock.get_read_buffer();\n\n                        ASSERT_EQ(l, 2);\n                        ASSERT_EQ(buf->length, 2);\n                        ASSERT_EQ(buf->offset, 2);\n                    }});\n}\n\nTEST(coroutine_socket, length_3) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run({[](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.bind(\"127.0.0.1\", port);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.listen(128), true);\n\n                        Socket *conn = sock.accept();\n                        char buf[1024];\n                        memset(buf, 'A', sizeof(buf));\n                        *(uint16_t *) buf = htons(65530);\n\n                        conn->send(buf, sizeof(buf));\n                    },\n\n                    [](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.errCode, 0);\n\n                        socket_set_length_protocol_1(sock);\n                        sock.protocol.package_max_length = 4096;\n\n                        ssize_t l = sock.recv_packet(RECV_TIMEOUT);\n                        ASSERT_EQ(l, -1);\n                        ASSERT_EQ(sock.errCode, SW_ERROR_PACKAGE_LENGTH_TOO_LARGE);\n                    }});\n}\n\nstatic string pkt_1;\nstatic string pkt_2;\n\nstatic void length_protocol_server_func(void *arg, int port) {\n    Socket sock(SW_SOCK_TCP);\n    bool retval = sock.bind(\"127.0.0.1\", port);\n    ASSERT_EQ(retval, true);\n    ASSERT_EQ(sock.listen(128), true);\n\n    Socket *conn = sock.accept();\n    String strbuf(256 * 1024);\n\n    uint32_t pack_len;\n\n    size_t l_1 = swoole_rand(65536, 65536 * 2);\n    pack_len = htonl(l_1);\n    strbuf.append((char *) &pack_len, sizeof(pack_len));\n    strbuf.append_random_bytes(l_1);\n\n    pkt_1 = string(strbuf.str, l_1 + 4);\n\n    size_t l_2 = swoole_rand(65536, 65536 * 2);\n    pack_len = htonl(l_2);\n    strbuf.append((char *) &pack_len, sizeof(pack_len));\n    strbuf.append_random_bytes(l_2);\n\n    pkt_2 = string(strbuf.str + pkt_1.length(), l_2 + 4);\n\n    conn->send_all(strbuf.str, strbuf.length);\n}\n\nTEST(coroutine_socket, length_4) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run({[port](void *arg) { length_protocol_server_func(arg, port); },\n\n                    [port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.errCode, 0);\n\n                        socket_set_length_protocol_2(sock);\n\n                        size_t bytes = 0;\n                        for (int i = 0; i < 2; i++) {\n                            ssize_t l = sock.recv_packet(RECV_TIMEOUT);\n                            bytes += l;\n                            auto buf = sock.get_read_buffer();\n                            uint32_t unpack_len = ntohl(*(uint32_t *) buf->str);\n\n                            if (i == 0) {\n                                ASSERT_EQ(pkt_1, string(buf->str, buf->length));\n                            } else {\n                                ASSERT_EQ(pkt_2, string(buf->str, buf->length));\n                            }\n\n                            ASSERT_EQ(unpack_len, l - 4);\n                            ASSERT_EQ(buf->length, l);\n                            ASSERT_EQ(buf->offset, l);\n                        }\n                        ASSERT_GE(bytes, 65536 * 2);\n                    }});\n}\n\nTEST(coroutine_socket, length_5) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run({[port](void *arg) { length_protocol_server_func(arg, port); },\n\n                    [port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.errCode, 0);\n\n                        socket_set_length_protocol_2(sock);\n\n                        size_t bytes = 0;\n                        for (int i = 0; i < 2; i++) {\n                            ssize_t l = sock.recv_packet(RECV_TIMEOUT);\n                            bytes += l;\n                            char *data = sock.pop_packet();\n                            uint32_t unpack_len = ntohl(*(uint32_t *) data);\n                            ASSERT_EQ(unpack_len, l - 4);\n\n                            if (i == 0) {\n                                ASSERT_EQ(pkt_1, string(data, l));\n                            } else {\n                                ASSERT_EQ(pkt_2, string(data, l));\n                            }\n                        }\n                        ASSERT_GE(bytes, 65536 * 2);\n                    }});\n}\n\nTEST(coroutine_socket, length_7) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run({[port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.bind(\"127.0.0.1\", port);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.listen(128), true);\n\n                        Socket *conn = sock.accept();\n                        char buf[1024];\n                        *(uint32_t *) buf = htons(0);\n\n                        conn->send(buf, 2);\n                        System::sleep(0.01);\n                        conn->send(buf + 2, 2);\n                    },\n\n                    [port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.errCode, 0);\n\n                        socket_set_length_protocol_2(sock);\n\n                        ssize_t l = sock.recv_packet(RECV_TIMEOUT);\n                        auto buf = sock.get_read_buffer();\n\n                        ASSERT_EQ(l, 4);\n                        ASSERT_EQ(buf->length, 4);\n                        ASSERT_EQ(buf->offset, 4);\n                    }});\n}\n\nTEST(coroutine_socket, event_hup) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run({[port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.bind(\"127.0.0.1\", port);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.listen(128), true);\n\n                        Socket *conn = sock.accept();\n                        System::sleep(0.05);\n                        char buf[1024];\n                        auto ret_n = conn->recv(buf, sizeof(buf));\n                        ASSERT_EQ(ret_n, 0);\n                        delete conn;\n                    },\n\n                    [port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.errCode, 0);\n\n                        auto buf = sock.get_read_buffer();\n                        Coroutine::create([&sock](void *args) {\n                            System::sleep(0.01);\n                            sock.shutdown(SHUT_RDWR);\n                        });\n                        auto n = sock.recv_all(buf->str, buf->size);\n                        ASSERT_EQ(sock.get_socket()->event_hup, 1);\n                        ASSERT_EQ(n, 0);\n                    }});\n}\n\nTEST(coroutine_socket, recv_line) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run({[port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.bind(\"127.0.0.1\", port);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.listen(128), true);\n\n                        Socket *conn = sock.accept();\n                        conn->send(\"hello world\\n\");\n                        conn->send(\"\\r\");\n                        char buf[256];\n                        memset(buf, 'A', 128);\n                        memset(buf + 128, 'B', 125);\n                        conn->send(buf, 253);\n                        delete conn;\n                    },\n\n                    [port](void *arg) {\n                        Socket sock(SW_SOCK_TCP);\n                        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.errCode, 0);\n\n                        size_t n;\n                        auto buf = sock.get_read_buffer();\n\n                        n = sock.recv_line(buf->str, 128);\n                        ASSERT_EQ(n, 12);\n                        ASSERT_MEMEQ(buf->str, \"hello world\\n\", 12);\n\n                        n = sock.recv_line(buf->str, 128);\n                        ASSERT_EQ(n, 1);\n                        ASSERT_MEMEQ(buf->str, \"\\r\", 1);\n\n                        char buf_2[256];\n                        memset(buf_2, 'A', 128);\n                        memset(buf_2 + 128, 'B', 125);\n\n                        n = sock.recv_line(buf->str, 128);\n                        ASSERT_EQ(n, 128);\n                        ASSERT_MEMEQ(buf->str, buf_2, 128);\n\n                        n = sock.recv_line(buf->str, 128);\n                        ASSERT_EQ(n, 125);\n                        ASSERT_MEMEQ(buf->str, buf_2 + 128, 125);\n\n                        n = sock.recv_line(buf->str, 128);\n                        ASSERT_EQ(n, 0);\n                    }});\n}\n\nTEST(coroutine_socket, getsockname) {\n    coroutine::run([](void *arg) {\n        Socket sock(SW_SOCK_TCP);\n        ASSERT_TRUE(sock.connect(host, 80));\n        ASSERT_TRUE(sock.getsockname());\n        sock.close();\n    });\n}\n\nTEST(coroutine_socket, buffer) {\n    Socket sock(SW_SOCK_TCP);\n    auto rbuf = sock.get_read_buffer();\n    auto wbuf = sock.get_write_buffer();\n\n    auto rbuf_pop = sock.pop_read_buffer();\n    auto wbuf_pop = sock.pop_write_buffer();\n\n    ASSERT_EQ(rbuf, rbuf_pop);\n    ASSERT_EQ(wbuf, wbuf_pop);\n\n    auto rbuf2 = sock.get_read_buffer();\n    auto wbuf2 = sock.get_write_buffer();\n\n    ASSERT_NE(rbuf2, rbuf);\n    ASSERT_NE(wbuf2, wbuf);\n\n    delete rbuf_pop;\n    delete wbuf_pop;\n}\n\nTEST(coroutine_socket, check_liveness) {\n    coroutine::run([](void *arg) {\n        Socket sock(SW_SOCK_TCP);\n        bool retval = sock.connect(host, 80);\n        ASSERT_EQ(retval, true);\n\n        bool result = sock.check_liveness();\n        sock.close();\n        ASSERT_EQ(result, true);\n        result = sock.check_liveness();\n        ASSERT_EQ(result, false);\n    });\n}\n\nTEST(coroutine_socket, write_and_read) {\n    coroutine::run([&](void *arg) {\n        int pairs[2];\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n        std::string text = \"Hello World\";\n        size_t length = text.length();\n\n        Coroutine::create([&](void *) {\n            Socket sock(pairs[0], SW_SOCK_UNIX_STREAM);\n            ssize_t result = sock.write(text.c_str(), length);\n            sock.close();\n            ASSERT_EQ(result, length);\n        });\n\n        char data[128];\n        Socket sock(pairs[1], SW_SOCK_UNIX_STREAM);\n        ssize_t result = sock.read(data, 128);\n        sock.close();\n        ASSERT_GT(result, 0);\n        data[result] = '\\0';\n        ASSERT_STREQ(text.c_str(), data);\n    });\n}\n\nTEST(coroutine_socket, write_and_read_2) {\n    // test for Socket::Socket(int _fd, int _domain, int _type, int _protocol) construct function\n    coroutine::run([&](void *arg) {\n        int pairs[2];\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n        std::string text = \"Hello World\";\n        size_t length = text.length();\n\n        Coroutine::create([&](void *) {\n            Socket sock(pairs[0], AF_UNIX, SOCK_STREAM, 0);\n            ssize_t result = sock.write(text.c_str(), length);\n            sock.close();\n            ASSERT_EQ(result, length);\n        });\n\n        char data[128];\n        Socket sock(pairs[1], AF_UNIX, SOCK_STREAM, 0);\n        ssize_t result = sock.read(data, 128);\n        sock.close();\n        ASSERT_GT(result, 0);\n        data[result] = '\\0';\n        ASSERT_STREQ(text.c_str(), data);\n    });\n}\n\nTEST(coroutine_socket, writev_and_readv) {\n    coroutine::run([&](void *arg) {\n        int iovcnt = 3;\n        int pairs[2];\n        std::string text = \"Hello World\";\n        size_t length = text.length();\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n\n        Coroutine::create([&](void *) {\n            std::unique_ptr<iovec[]> iov(new iovec[iovcnt]);\n            for (int i = 0; i < iovcnt; i++) {\n                iov[i].iov_base = (void *) text.c_str();\n                iov[i].iov_len = length;\n            }\n            IOVector io_vector((struct iovec *) iov.get(), iovcnt);\n\n            Socket sock(pairs[0], SW_SOCK_UNIX_STREAM);\n            ssize_t result = sock.writev(&io_vector);\n            sock.close();\n            ASSERT_EQ(result, length * 3);\n        });\n\n        std::vector<std::string> results(iovcnt);\n        std::unique_ptr<iovec[]> iov(new iovec[iovcnt]);\n        for (int i = 0; i < iovcnt; i++) {\n            iov[i].iov_base = (void *) results[i].c_str();\n            iov[i].iov_len = length;\n        }\n        IOVector io_vector((struct iovec *) iov.get(), iovcnt);\n\n        Socket sock(pairs[1], SW_SOCK_UNIX_STREAM);\n        ssize_t result = sock.readv(&io_vector);\n        sock.close();\n        ASSERT_EQ(result, length * 3);\n\n        for (auto iter = results.begin(); iter != results.end(); iter++) {\n            (*iter)[length] = '\\0';\n            ASSERT_STREQ(text.c_str(), (*iter).c_str());\n        }\n    });\n}\n\nTEST(coroutine_socket, send_and_recv_all) {\n    coroutine::run([&](void *arg) {\n        int pairs[2];\n\n        String wbuf;\n        wbuf.append_random_bytes(4 * 1024 * 1024, false);\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n\n        Coroutine::create([&](void *) {\n            Socket sock(pairs[0], SW_SOCK_UNIX_STREAM);\n            sock.get_socket()->set_send_buffer_size(65536);\n\n            ASSERT_EQ(sock.send_all(wbuf.str, wbuf.length), wbuf.length);\n\n            System::sleep(0.1);\n\n            sock.close();\n        });\n\n        Socket sock(pairs[1], SW_SOCK_UNIX_STREAM);\n        sock.get_socket()->set_recv_buffer_size(65536);\n\n        String rbuf(wbuf.length);\n        ssize_t result = sock.recv_all(rbuf.str, wbuf.length);\n        ASSERT_EQ(result, wbuf.length);\n        ASSERT_MEMEQ(wbuf.str, rbuf.str, wbuf.length);\n        System::sleep(0.1);\n        sock.close();\n    });\n}\n\nTEST(coroutine_socket, writevall_and_readvall) {\n    coroutine::run([&](void *arg) {\n        int write_iovcnt = 4;\n        int pairs[2];\n\n        char buf[65536];\n        swoole_random_bytes(buf, sizeof(buf));\n\n        std::string text(buf, sizeof(buf));\n        size_t length = text.length();\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n\n        Coroutine::create([&](void *) {\n            std::unique_ptr<iovec[]> iov(new iovec[write_iovcnt]);\n            for (int i = 0; i < write_iovcnt; i++) {\n                iov[i].iov_base = (void *) text.c_str();\n                iov[i].iov_len = length;\n            }\n\n            Socket sock(pairs[0], SW_SOCK_UNIX_STREAM);\n            sock.get_socket()->set_send_buffer_size(sizeof(buf));\n\n            IOVector io_vector1((struct iovec *) iov.get(), write_iovcnt);\n            ASSERT_EQ(sock.writev_all(&io_vector1), write_iovcnt * sizeof(buf));\n\n            System::sleep(0.01);\n\n            IOVector io_vector2((struct iovec *) iov.get(), write_iovcnt);\n            ASSERT_EQ(sock.writev_all(&io_vector2), write_iovcnt * sizeof(buf));\n\n            sock.close();\n        });\n\n        int read_iovcnt = 8;\n        std::unique_ptr<iovec[]> iov(new iovec[read_iovcnt]);\n        for (int i = 0; i < read_iovcnt; i++) {\n            iov[i].iov_base = sw_malloc(length);\n            iov[i].iov_len = length;\n        }\n        IOVector io_vector((struct iovec *) iov.get(), read_iovcnt);\n\n        Socket sock(pairs[1], SW_SOCK_UNIX_STREAM);\n        sock.get_socket()->set_recv_buffer_size(sizeof(buf));\n\n        ssize_t result = sock.readv_all(&io_vector);\n        sock.close();\n        ASSERT_EQ(result, length * read_iovcnt);\n\n        for (int i = 0; i < read_iovcnt; i++) {\n            ASSERT_MEMEQ(iov[i].iov_base, buf, sizeof(buf));\n            sw_free(iov[i].iov_base);\n        }\n    });\n}\n\nTEST(coroutine_socket, sendfile) {\n    coroutine::run([&](void *arg) {\n        int pairs[2];\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n        Coroutine::create([&](void *) {\n            std::string file = swoole::test::get_jpg_file();\n            Socket sock(pairs[0], SW_SOCK_UNIX_STREAM);\n            bool result = sock.sendfile(file.c_str(), 0, 0);\n            sock.close();\n            ASSERT_TRUE(result);\n        });\n\n        char data[250000];\n        Socket sock(pairs[1], SW_SOCK_UNIX_STREAM);\n        ssize_t result = sock.read(data, 250000);\n        data[result] = '\\0';\n        sock.close();\n        ASSERT_GT(result, 0);\n    });\n}\n\nstatic void test_sendto_recvfrom(enum swSocketType sock_type) {\n    coroutine::run([&](void *arg) {\n        std::string server_text = \"hello world!!!\";\n        size_t server_length = server_text.length();\n        std::string client_text = \"hello swoole!!!\";\n        size_t client_length = client_text.length();\n\n        const char *ip = sock_type == SW_SOCK_UDP ? \"127.0.0.1\" : \"::1\";\n        const char *local = \"localhost\";\n\n        int port = swoole::test::get_random_port();\n\n        Socket sock_server(sock_type);\n        Socket sock_client(sock_type);\n        sock_server.bind(ip, port);\n        sock_client.bind(ip, port + 1);\n\n        ON_SCOPE_EXIT {\n            sock_server.close();\n            sock_client.close();\n        };\n\n        sock_server.sendto(ip, port + 1, (const void *) server_text.c_str(), server_length);\n\n        char data_from_server[128] = {};\n        struct sockaddr_in serveraddr;\n        bzero(&serveraddr, sizeof(serveraddr));\n        serveraddr.sin_family = AF_INET;\n        serveraddr.sin_addr.s_addr = inet_addr(ip);\n        serveraddr.sin_port = htons(port);\n        socklen_t addr_length = sizeof(serveraddr);\n\n        // receive data from server\n        ssize_t result =\n            sock_client.recvfrom(data_from_server, server_length, (struct sockaddr *) &serveraddr, &addr_length);\n        data_from_server[result] = '\\0';\n        ASSERT_EQ(result, server_length);\n        ASSERT_STREQ(data_from_server, server_text.c_str());\n\n        // receive data from client\n        char data_from_client[128] = {};\n        sock_client.sendto(local, port, (const void *) client_text.c_str(), client_length);\n        result = sock_server.recvfrom(data_from_client, client_length);\n        data_from_client[client_length] = '\\0';\n        ASSERT_EQ(result, client_length);\n        ASSERT_STREQ(data_from_client, client_text.c_str());\n    });\n}\n\nTEST(coroutine_socket, sendto_recvfrom_udp) {\n    test_sendto_recvfrom(SW_SOCK_UDP);\n    test_sendto_recvfrom(SW_SOCK_UDP6);\n}\n\nstatic void socket_test_request_baidu(Socket &sock) {\n    ASSERT_GT(sock.send(SW_STRL(TEST_REQUEST_BAIDU)), 0);\n\n    String buf(65536);\n    while (true) {\n        char rbuf[4096];\n        ssize_t nr = sock.recv(rbuf, sizeof(rbuf));\n        if (nr <= 0) {\n            break;\n        }\n        buf.append(rbuf, nr);\n    }\n    ASSERT_TRUE(buf.contains(\"www.baidu.com\"));\n}\n\nstatic void proxy_test(Socket &sock, bool https) {\n    if (https) {\n        sock.enable_ssl_encrypt();\n    }\n\n    bool retval = sock.connect(host, https ? 443 : 80);\n    ON_SCOPE_EXIT {\n        sock.close();\n    };\n    ASSERT_EQ(retval, true);\n\n    if (https) {\n        ASSERT_NE(sock.ssl_get_peer_cert(), \"\");\n    }\n\n    socket_test_request_baidu(sock);\n}\n\nstatic void proxy_set_socks5_proxy(Socket &socket, int port, bool auth) {\n    std::string username, password;\n    if (auth) {\n        username = std::string(TEST_SOCKS5_PROXY_USER);\n        password = std::string(TEST_SOCKS5_PROXY_PASSWORD);\n    }\n    socket.set_socks5_proxy(TEST_SOCKS5_PROXY_HOST, port, username, password);\n}\n\nTEST(coroutine_socket, https_get_with_socks5_proxy) {\n    coroutine::run([](void *arg) {\n        if (swoole::test::is_github_ci()) {\n            Socket sock(SW_SOCK_TCP);\n            proxy_set_socks5_proxy(sock, TEST_SOCKS5_PROXY_PORT, true);\n            proxy_test(sock, true);\n        }\n        // no auth\n        {\n            Socket sock(SW_SOCK_TCP);\n            proxy_set_socks5_proxy(sock, TEST_SOCKS5_PROXY_NO_AUTH_PORT, false);\n            proxy_test(sock, true);\n        }\n    });\n}\n\nTEST(coroutine_socket, http_get_with_socks5_proxy) {\n    coroutine::run([](void *arg) {\n        if (swoole::test::is_github_ci()) {\n            Socket sock(SW_SOCK_TCP);\n            proxy_set_socks5_proxy(sock, TEST_SOCKS5_PROXY_PORT, true);\n            proxy_test(sock, false);\n        }\n        // no auth\n        {\n            Socket sock(SW_SOCK_TCP);\n            proxy_set_socks5_proxy(sock, TEST_SOCKS5_PROXY_NO_AUTH_PORT, false);\n            proxy_test(sock, false);\n        }\n    });\n}\n\nstatic void proxy_set_http_proxy(Socket &socket) {\n    std::string username, password;\n    if (swoole::test::is_github_ci()) {\n        username = std::string(TEST_HTTP_PROXY_USER);\n        password = std::string(TEST_HTTP_PROXY_PASSWORD);\n    }\n    socket.set_http_proxy(TEST_HTTP_PROXY_HOST, TEST_HTTP_PROXY_PORT, username, password);\n}\n\nTEST(coroutine_socket, http_get_with_http_proxy) {\n    coroutine::run([&](void *arg) {\n        Socket sock(SW_SOCK_TCP);\n        proxy_set_http_proxy(sock);\n        proxy_test(sock, false);\n    });\n}\n\nTEST(coroutine_socket, https_get_with_http_proxy) {\n    coroutine::run([&](void *arg) {\n        Socket sock(SW_SOCK_TCP);\n        proxy_set_http_proxy(sock);\n        proxy_test(sock, true);\n    });\n}\n\nTEST(coroutine_socket, ssl) {\n    coroutine::run([&](void *arg) {\n        Socket sock(SW_SOCK_TCP);\n\n        sock.enable_ssl_encrypt();\n        sock.set_ssl_cert_file(swoole::test::get_ssl_dir() + \"/client.crt\");\n        sock.set_ssl_key_file(swoole::test::get_ssl_dir() + \"/client.key\");\n        sock.set_ssl_verify_peer(false);\n        sock.set_ssl_allow_self_signed(true);\n        sock.set_ssl_cafile(swoole::test::get_ssl_dir() + \"/ca.crt\");\n\n        proxy_test(sock, true);\n    });\n}\n\nTEST(coroutine_socket, ssl_accept) {\n    const int port = __LINE__ + TEST_PORT;\n    auto svr = [port](void *arg) {\n        Socket sock(SW_SOCK_TCP);\n        bool retval = sock.bind(\"127.0.0.1\", port);\n        ASSERT_EQ(retval, true);\n\n        sock.enable_ssl_encrypt();\n        sock.set_ssl_cert_file(swoole::test::get_ssl_dir() + \"/server.crt\");\n        sock.set_ssl_key_file(swoole::test::get_ssl_dir() + \"/server.key\");\n        sock.set_ssl_dhparam(swoole::test::get_ssl_dir() + \"/dhparams.pem\");\n        sock.set_ssl_ecdh_curve(\"secp256r1\");\n\n        ASSERT_EQ(sock.listen(128), true);\n\n        Socket *conn = sock.accept();\n        ASSERT_NE(conn, nullptr);\n        ASSERT_TRUE(conn->ssl_handshake());\n        conn->send(EOF_PACKET);\n        char rbuf[1024];\n        auto n = conn->recv(rbuf, sizeof(rbuf));\n        rbuf[n] = 0;\n\n        ASSERT_STREQ(rbuf, EOF_PACKET_2);\n        conn->close();\n        delete conn;\n    };\n\n    auto cli = [port](void *arg) {\n        Socket sock(SW_SOCK_TCP);\n        sock.enable_ssl_encrypt();\n        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n        ASSERT_EQ(retval, true);\n        ASSERT_EQ(sock.errCode, 0);\n\n        char rbuf[1024];\n        auto n = sock.recv(rbuf, sizeof(rbuf));\n        rbuf[n] = 0;\n        ASSERT_STREQ(rbuf, EOF_PACKET);\n        sock.send(EOF_PACKET_2);\n\n        sock.close();\n    };\n\n    coroutine::run({svr, cli});\n}\n\nTEST(coroutine_socket, peek) {\n    coroutine::run([&](void *arg) {\n        int pairs[2];\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n        std::string text = \"Hello World\";\n        size_t length = text.length();\n\n        Coroutine::create([&](void *) {\n            Socket sock(pairs[0], SW_SOCK_UNIX_STREAM);\n            ssize_t result = sock.write(text.c_str(), length);\n            sock.close();\n            ASSERT_EQ(result, length);\n        });\n\n        char data[128];\n        Socket sock(pairs[1], SW_SOCK_UNIX_STREAM);\n        ssize_t result = sock.peek(data, 5);\n        sock.close();\n        ASSERT_EQ(result, 5);\n        data[result] = '\\0';\n        ASSERT_STREQ(\"Hello\", data);\n    });\n}\n\nTEST(coroutine_socket, sendmsg_and_recvmsg) {\n    coroutine::run([&](void *arg) {\n        int pairs[2];\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n\n        std::string text = \"Hello World\";\n        const size_t length = text.length();\n\n        Coroutine::create([&](void *) {\n            Socket sock(pairs[0], SW_SOCK_UNIX_STREAM);\n            struct msghdr msg;\n            struct iovec ivec;\n\n            msg.msg_control = nullptr;\n            msg.msg_controllen = 0;\n            msg.msg_flags = 0;\n            msg.msg_name = nullptr;\n            msg.msg_namelen = 0;\n            msg.msg_iov = &ivec;\n            msg.msg_iovlen = 1;\n\n            ivec.iov_base = (void *) text.c_str();\n            ivec.iov_len = length;\n\n            ssize_t ret = sock.sendmsg(&msg, 0);\n            sock.close();\n            ASSERT_EQ(ret, length);\n        });\n\n        Socket sock(pairs[1], SW_SOCK_UNIX_STREAM);\n        struct msghdr msg;\n        struct iovec ivec;\n        char buf[length + 1];\n\n        msg.msg_control = nullptr;\n        msg.msg_controllen = 0;\n        msg.msg_flags = 0;\n        msg.msg_name = nullptr;\n        msg.msg_namelen = 0;\n        msg.msg_iov = &ivec;\n        msg.msg_iovlen = 1;\n\n        ivec.iov_base = buf;\n        ivec.iov_len = length;\n\n        ssize_t ret = sock.recvmsg(&msg, 0);\n        buf[ret] = '\\0';\n        sock.close();\n        ASSERT_STREQ(buf, text.c_str());\n    });\n}\n\nstd::pair<std::shared_ptr<Socket>, std::shared_ptr<Socket> > swoole::test::create_socket_pair() {\n    int pairs[2];\n    socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n\n    auto sock0 = new Socket(pairs[0], SW_SOCK_UNIX_STREAM);\n    auto sock1 = new Socket(pairs[1], SW_SOCK_UNIX_STREAM);\n\n    sock0->get_socket()->set_buffer_size(65536);\n    sock1->get_socket()->set_buffer_size(65536);\n\n    std::pair<std::shared_ptr<Socket>, std::shared_ptr<Socket> > result(sock0, sock1);\n    return result;\n}\n\nTEST(coroutine_socket, close) {\n    coroutine::run([&](void *arg) {\n        auto pair = create_socket_pair();\n\n        auto buffer = sw_tg_buffer();\n        buffer->clear();\n        buffer->append_random_bytes(256 * 1024, false);\n\n        std::map<std::string, bool> results;\n        auto _sock = pair.first;\n\n        // write co\n        Coroutine::create([&](void *) {\n            SW_LOOP_N(32) {\n                ssize_t result = _sock->write(buffer->value(), buffer->get_length());\n                if (result < 0 && _sock->errCode == ECANCELED) {\n                    ASSERT_FALSE(_sock->close());\n                    ASSERT_EQ(_sock->errCode, SW_ERROR_CO_SOCKET_CLOSE_WAIT);\n                    results[\"write\"] = true;\n                    ASSERT_EQ(_sock->write(buffer->value(), buffer->get_length()), -1);\n                    ASSERT_EQ(_sock->errCode, EBADF);\n                    break;\n                }\n            }\n        });\n\n        // read co\n        Coroutine::create([&](void *) {\n            SW_LOOP_N(32) {\n                char buf[4096];\n                ssize_t result = _sock->read(buf, sizeof(buf));\n                if (result < 0 && _sock->errCode == ECANCELED) {\n                    ASSERT_TRUE(_sock->close());\n                    results[\"read\"] = true;\n                    break;\n                }\n            }\n        });\n\n        System::sleep(0.1);\n        ASSERT_FALSE(_sock->close());\n        ASSERT_EQ(_sock->errCode, SW_ERROR_CO_SOCKET_CLOSE_WAIT);\n        ASSERT_TRUE(_sock->is_closed());\n        ASSERT_TRUE(results[\"write\"]);\n        ASSERT_TRUE(results[\"read\"]);\n        ASSERT_FALSE(_sock->close());\n        ASSERT_EQ(_sock->errCode, EBADF);\n    });\n}\n\nTEST(coroutine_socket, cancel) {\n    coroutine::run([&](void *arg) {\n        auto pair = create_socket_pair();\n\n        auto buffer = sw_tg_buffer();\n        buffer->clear();\n        buffer->append_random_bytes(256 * 1024, false);\n\n        std::map<std::string, bool> results;\n        // read co\n        Coroutine::create([&](void *) {\n            SW_LOOP_N(32) {\n                char buf[4096];\n                ssize_t result = pair.first->read(buf, sizeof(buf));\n                if (result < 0 && pair.first->errCode == ECANCELED) {\n                    results[\"read\"] = true;\n                    break;\n                }\n            }\n        });\n\n        System::sleep(0.1);\n        pair.first->cancel(SW_EVENT_READ);\n        ASSERT_TRUE(results[\"read\"]);\n    });\n}\n\nTEST(coroutine_socket, get_event_str) {\n    Socket sock;\n    ASSERT_STREQ(sock.get_event_str(SW_EVENT_READ), \"reading\");\n    ASSERT_STREQ(sock.get_event_str(SW_EVENT_WRITE), \"writing\");\n}\n\nTEST(coroutine_socket, option) {\n    Socket sock(SW_SOCK_TCP);\n    int optval;\n\n    ASSERT_TRUE(sock.get_option(SOL_SOCKET, SO_RCVBUF, &optval));\n    ASSERT_GT(optval, 65536);\n\n    optval *= 2;\n    ASSERT_TRUE(sock.set_option(SOL_SOCKET, SO_RCVBUF, optval));\n}\n\nstatic void test_ssl_verify() {\n    Socket sock(SW_SOCK_TCP);\n    sock.enable_ssl_encrypt();\n    sock.set_tls_host_name(TEST_HTTP_DOMAIN);\n    sock.set_ssl_verify_peer(true);\n    ASSERT_TRUE(sock.connect(TEST_HTTP_DOMAIN, 443));\n    ASSERT_TRUE(sock.ssl_verify(false));\n\n    auto req = swoole::test::http_get_request(TEST_HTTP_DOMAIN, \"/\");\n    ASSERT_EQ(sock.send(req.c_str(), req.length()), req.length());\n    ASSERT_TRUE(sock.check_liveness());\n\n    String buf(65536);\n    SW_LOOP {\n        auto n = sock.recv(buf.str + buf.length, buf.size - buf.length);\n        if (n <= 0) {\n            break;\n        }\n        buf.grow(n);\n    }\n\n    ASSERT_TRUE(buf.contains(TEST_HTTPS_EXPECT));\n\n    usleep(50000);\n    ASSERT_FALSE(sock.check_liveness());\n}\n\nTEST(coroutine_socket, ssl_verify) {\n    coroutine::run([](void *arg) { test_ssl_verify(); });\n}\n\nTEST(coroutine_socket, shutdown) {\n    coroutine::run([](void *arg) {\n        Socket sock(SW_SOCK_TCP);\n        ASSERT_TRUE(sock.connect(TEST_HTTP_DOMAIN, 80));\n        ASSERT_TRUE(sock.shutdown(SHUT_RD));\n        ASSERT_FALSE(sock.shutdown(SHUT_RD));\n        ASSERT_ERREQ(ENOTCONN);\n\n        ASSERT_TRUE(sock.shutdown(SHUT_WR));\n        ASSERT_FALSE(sock.shutdown(SHUT_WR));\n        ASSERT_ERREQ(ENOTCONN);\n\n        ASSERT_FALSE(sock.shutdown());\n        ASSERT_ERREQ(ENOTCONN);\n    });\n}\n\nTEST(coroutine_socket, recv_packet) {\n    coroutine::run([](void *arg) {\n        Socket sock(SW_SOCK_TCP);\n        ASSERT_TRUE(sock.connect(TEST_HTTP_DOMAIN, 80));\n        auto req = swoole::test::http_get_request(TEST_HTTP_DOMAIN, \"/\");\n        ASSERT_EQ(sock.send(req.c_str(), req.length()), req.length());\n        ASSERT_TRUE(sock.check_liveness());\n        auto n = sock.recv_packet();\n        ASSERT_GT(n, 0);\n        auto buf = sock.get_read_buffer();\n        ASSERT_TRUE(buf->contains(TEST_HTTP_EXPECT));\n    });\n}\n\nTEST(coroutine_socket, set_error) {\n    Socket sock(SW_SOCK_TCP);\n    sock.set_err(1000, std::string(TEST_STR));\n\n    ASSERT_EQ(sock.errCode, 1000);\n    ASSERT_STREQ(sock.errMsg, TEST_STR);\n}\n\nTEST(coroutine_socket, reinit) {\n    coroutine::run([](void *arg) {\n        Socket sock(SW_SOCK_TCP6);\n        ASSERT_EQ(sock.get_sock_domain(), AF_INET6);\n        proxy_set_socks5_proxy(sock, TEST_SOCKS5_PROXY_PORT, true);\n        sock.connect(\"::1\", 80);\n        ASSERT_EQ(sock.get_sock_domain(), AF_INET);\n    });\n}\n"
  },
  {
    "path": "core-tests/src/coroutine/system.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_coroutine.h\"\n#include \"swoole_pipe.h\"\n\nusing namespace swoole;\nusing namespace swoole::test;\n\nusing swoole::coroutine::Socket;\nusing swoole::coroutine::System;\n\nstatic const char *test_file = \"/tmp/swoole-core-test\";\n\nstatic constexpr int DATA_SIZE = 8 * 1024 * 1024;\nstatic constexpr int DATA_SIZE_2 = 64 * 1024;\n\nTEST(coroutine_system, file) {\n    test::coroutine::run([](void *arg) {\n        std::shared_ptr<String> buf = std::make_shared<String>(DATA_SIZE);\n        ASSERT_EQ(swoole_random_bytes(buf->str, buf->size - 1), buf->size - 1);\n        buf->str[buf->size - 1] = 0;\n        ASSERT_EQ(System::write_file(test_file, buf->str, buf->size, true, 0), buf->size);\n        auto data = System::read_file(test_file, true);\n        ASSERT_TRUE(data.get());\n        ASSERT_STREQ(buf->str, data->str);\n        unlink(test_file);\n    });\n}\n\nTEST(coroutine_system, flock) {\n    std::shared_ptr<String> buf = std::make_shared<String>(65536);\n    ASSERT_EQ(swoole_random_bytes(buf->str, buf->size - 1), buf->size - 1);\n    buf->str[buf->size - 1] = 0;\n\n    test::coroutine::run([&buf](void *) {\n        int fd = swoole_coroutine_open(test_file, File::WRITE | File::CREATE, 0666);\n        ASSERT_TRUE(fd > 0);\n        swoole_coroutine_flock(fd, LOCK_EX);\n\n        for (int i = 0; i < 4; i++) {\n            Coroutine::create([&buf](void *) {\n                int fd = swoole_coroutine_open(test_file, File::READ, 0);\n                ASSERT_TRUE(fd > 0);\n                swoole_coroutine_flock(fd, LOCK_SH);\n                String read_buf(DATA_SIZE_2);\n                auto rn = swoole_coroutine_read(fd, read_buf.str, read_buf.size - 1);\n                ASSERT_EQ(rn, read_buf.size - 1);\n                read_buf.str[read_buf.size - 1] = 0;\n                swoole_coroutine_flock(fd, LOCK_UN);\n                EXPECT_STREQ(read_buf.str, buf->str);\n                swoole_coroutine_close(fd);\n            });\n        }\n\n        auto wn = swoole_coroutine_write(fd, buf->str, buf->size - 1);\n        ASSERT_EQ(wn, buf->size - 1);\n        swoole_coroutine_flock(fd, LOCK_UN);\n        swoole_coroutine_close(fd);\n    });\n\n    unlink(test_file);\n}\n\nTEST(coroutine_system, flock_nb) {\n    swoole::coroutine::run([&](void *arg) {\n        DEBUG() << \"[thread-1] open\" << std::endl;\n        int fd = swoole_coroutine_open(test_file, File::WRITE | File::CREATE, 0666);\n        DEBUG() << \"[thread-1] LOCK_EX | LOCK_NB\" << std::endl;\n        ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_EX | LOCK_NB), 0);\n\n        std::thread t([]() {\n            int fd = open(test_file, File::WRITE | File::CREATE, 0666);\n            DEBUG() << \"[thread-2] LOCK_EX | LOCK_NB\" << std::endl;\n            ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_EX), 0);\n\n            DEBUG() << \"[thread-2] LOCK_UN\" << std::endl;\n            ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_UN), 0);\n\n            DEBUG() << \"[thread-2] close\" << std::endl;\n            swoole_coroutine_close(fd);\n            unlink(test_file);\n        });\n\n        DEBUG() << \"[thread-1] LOCK_UN\" << std::endl;\n        ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_UN), 0);\n\n        t.join();\n    });\n}\n\nTEST(coroutine_system, cancel_sleep) {\n    test::coroutine::run([](void *arg) {\n        auto co = Coroutine::get_current_safe();\n        Coroutine::create([co](void *) {\n            System::sleep(0.002);\n            co->cancel();\n        });\n        System::sleep(1000);\n    });\n}\n\nstatic void test_getaddrinfo(\n    const std::string &host, int family, int type, int protocol, const char *service, double timeout) {\n    std::vector<std::string> ip_list = System::getaddrinfo(host, family, type, protocol, service, timeout);\n    ASSERT_GT(ip_list.size(), 0);\n    for (auto &ip : ip_list) {\n        ASSERT_TRUE(swoole::network::Address::verify_ip(family, ip));\n        network::Client c(family == AF_INET ? SW_SOCK_TCP : SW_SOCK_TCP6, false);\n        if (!test::is_github_ci()) {\n            std::cout << ip.c_str() << \"\\n\";\n            ASSERT_EQ(c.connect(ip.c_str(), 443), SW_OK);\n        }\n    }\n}\n\nTEST(coroutine_system, getaddrinfo) {\n    test::coroutine::run([](void *arg) {\n        test_getaddrinfo(TEST_HTTP_DOMAIN, AF_INET, SOCK_STREAM, 0, \"http\", -1);\n        test_getaddrinfo(TEST_HTTP_DOMAIN, AF_INET6, SOCK_STREAM, 0, \"http\", -1);\n    });\n}\n\nTEST(coroutine_system, getaddrinfo_fail) {\n    test::coroutine::run([](void *arg) {\n        auto ip_list = System::getaddrinfo(\"w11.baidu.com-not-exists\", AF_INET, SOCK_STREAM, 0, \"http\", -1);\n        ASSERT_EQ(ip_list.size(), 0);\n        ASSERT_ERREQ(EAI_NONAME);\n    });\n}\n\nTEST(coroutine_system, getaddrinfo_timeout) {\n    test::coroutine::run([](void *arg) {\n        auto ip_list = System::getaddrinfo(\"w12.baidu.com-not-exists\", AF_INET, SOCK_STREAM, 0, \"http\", 0.005);\n        ASSERT_EQ(ip_list.size(), 0);\n        ASSERT_ERREQ(SW_ERROR_CO_TIMEDOUT);\n    });\n}\n\nTEST(coroutine_system, wait_signal) {\n    test::coroutine::run([](void *arg) {\n        Coroutine::create([](void *) {\n            System::sleep(0.002);\n            kill(getpid(), SIGUSR1);\n        });\n        ASSERT_EQ(System::wait_signal(SIGUSR1, 1.0), SIGUSR1);\n        ASSERT_EQ(System::wait_signal(SIGUSR2, 0.1), -1);\n    });\n}\n\nTEST(coroutine_system, wait_signal_invalid_signo) {\n    test::coroutine::run([](void *arg) {\n        ASSERT_EQ(System::wait_signal(SW_SIGNO_MAX), SW_ERR);\n        ASSERT_ERREQ(EINVAL);\n    });\n}\n\nTEST(coroutine_system, wait_signal_fail) {\n    test::coroutine::run([](void *arg) {\n        SwooleG.signal_listener_num = 1;\n        ASSERT_EQ(System::wait_signal(SIGUSR1, 1.0), SW_ERR);\n        ASSERT_ERREQ(EBUSY);\n        SwooleG.signal_listener_num = 0;\n    });\n}\n\nstatic const char *GREETING = \"hello world, hello swoole\";\n\nTEST(coroutine_system, wait_event_readable) {\n    UnixSocket p(true, SOCK_DGRAM);\n    ASSERT_TRUE(p.ready());\n\n    test::coroutine::run([&p](void *arg) {\n        Coroutine::create([&p](void *) {\n            System::sleep(0.002);\n            ASSERT_GT(p.write(GREETING, strlen(GREETING)), 0);\n        });\n\n        // bad fd\n        EXPECT_EQ(System::wait_event(9999, SW_EVENT_READ, 1), -1);\n        EXPECT_EQ(errno, EBADF);\n        EXPECT_ERREQ(EBADF);\n\n        // trigger event\n        char buffer[128];\n        auto pipe_sock = p.get_socket(false);\n        // readable\n        EXPECT_EQ(System::wait_event(pipe_sock->get_fd(), SW_EVENT_READ, 1), SW_EVENT_READ);\n        // readable + writable\n        EXPECT_EQ(System::wait_event(pipe_sock->get_fd(), SW_EVENT_READ | SW_EVENT_WRITE, 1),\n                  SW_EVENT_READ | SW_EVENT_WRITE);\n\n        ssize_t n = pipe_sock->read(buffer, sizeof(buffer));\n        buffer[n] = 0;\n        EXPECT_EQ(strlen(GREETING), n);\n        EXPECT_STREQ(GREETING, buffer);\n\n        // timeout\n        auto pipe_sock_2 = p.get_socket(true);\n        EXPECT_EQ(System::wait_event(pipe_sock_2->get_fd(), SW_EVENT_READ, 0.1), -1);\n        EXPECT_EQ(errno, SW_ERROR_CO_TIMEDOUT);\n        EXPECT_ERREQ(SW_ERROR_CO_TIMEDOUT);\n    });\n}\n\nTEST(coroutine_system, wait_event_writable) {\n    UnixSocket p(true, SOCK_STREAM);\n    ASSERT_TRUE(p.ready());\n    p.set_blocking(false);\n    p.set_buffer_size(65536);\n    sw_tg_buffer()->clear();\n\n    String str(2 * SW_NUM_MILLION);\n    str.append_random_bytes(str.size - 1, false);\n    str.append('\\0');\n\n    test::coroutine::run([&](void *arg) {\n        Coroutine::create([&](void *) {\n            System::sleep(0.002);\n            auto pipe_sock = p.get_socket(true);\n\n            char *ptr = str.value();\n            size_t len = str.get_length();\n\n            while (len > 0) {\n                ssize_t retval = pipe_sock->write(ptr, len > 8192 ? 8192 : len);\n                if (retval > 0) {\n                    ptr += retval;\n                    len -= retval;\n                } else if (retval == 0 || (retval < 0 && errno != EAGAIN)) {\n                    break;\n                }\n                System::wait_event(pipe_sock->get_fd(), SW_EVENT_WRITE, 1);\n            }\n        });\n\n        auto pipe_sock = p.get_socket(false);\n        auto tg_buf = sw_tg_buffer();\n\n        while (tg_buf->length < str.size - 1) {\n            ssize_t retval = pipe_sock->read(tg_buf->str + tg_buf->length, tg_buf->size - tg_buf->length);\n            if (retval > 0) {\n                tg_buf->grow(retval);\n                continue;\n            } else if (retval == 0 && (retval < 0 && errno != EAGAIN)) {\n                break;\n            }\n            System::wait_event(pipe_sock->get_fd(), SW_EVENT_READ, 1);\n        }\n        tg_buf->append('\\0');\n        EXPECT_STREQ(sw_tg_buffer()->value(), str.value());\n    });\n}\n\nTEST(coroutine_system, wait_event_fail) {\n    UnixSocket p(true, SOCK_DGRAM);\n    test::coroutine::run([&](void *arg) {\n        ASSERT_EQ(System::wait_event(9999, 0, 1), SW_ERR);\n        ASSERT_ERREQ(EINVAL);\n\n        ASSERT_EQ(System::wait_event(p.get_socket(true)->get_fd(), SW_EVENT_READ, 0), SW_ERR);\n        ASSERT_ERREQ(ETIMEDOUT);\n\n        ASSERT_EQ(System::wait_event(p.get_socket(false)->get_fd(), SW_EVENT_WRITE, 0), SW_EVENT_WRITE);\n\n        ASSERT_EQ(System::wait_event(9999, SW_EVENT_WRITE, 0), -1);\n        ASSERT_ERREQ(EBADF);\n\n        ASSERT_EQ(System::wait_event(9999, SW_EVENT_WRITE, 1.0), -1);\n        ASSERT_ERREQ(EBADF);\n    });\n}\n\nTEST(coroutine_system, swoole_stream_select) {\n    UnixSocket p(true, SOCK_STREAM);\n    std::unordered_map<int, swoole::coroutine::PollSocket> fds;\n    fds.emplace(std::make_pair(p.get_socket(false)->fd, swoole::coroutine::PollSocket(SW_EVENT_READ, nullptr)));\n\n    test::coroutine::run([&](void *arg) {\n        // try timeout to trigger socket_poll_timeout function\n        ASSERT_FALSE(System::socket_poll(fds, 0.5));\n    });\n\n    // start normal process\n    test::coroutine::run([&](void *arg) {\n        std::string text = \"Hello world\";\n        size_t len = text.length();\n\n        // child pipe\n        Coroutine::create([&](void *) {\n            System::sleep(0.05);\n            auto pipe_sock = p.get_socket(true);\n            const char *ptr = text.c_str();\n            ASSERT_EQ(pipe_sock->write(ptr, len), len);\n        });\n\n        // master pipe\n        bool result = System::socket_poll(fds, 0.5);\n        ASSERT_TRUE(result);\n\n        char buffer[128];\n        auto pipe_sock = p.get_socket(false);\n        ssize_t retval = pipe_sock->read(buffer, sizeof(buffer));\n        buffer[retval] = '\\0';\n\n        ASSERT_EQ(retval, len);\n        const char *ptr = text.c_str();\n        ASSERT_STREQ(ptr, buffer);\n    });\n}\n\nTEST(coroutine_system, timeout_is_zero) {\n    UnixSocket p(true, SOCK_STREAM);\n    std::unordered_map<int, swoole::coroutine::PollSocket> fds;\n    fds.emplace(std::make_pair(p.get_socket(false)->fd, swoole::coroutine::PollSocket(SW_EVENT_READ, nullptr)));\n\n    // timeout is 0\n    test::coroutine::run([&](void *arg) {\n        std::string text = \"Hello world\";\n        size_t len = text.length();\n\n        // child pipe\n        Coroutine::create([&](void *) {\n            auto pipe_sock = p.get_socket(true);\n            const char *ptr = text.c_str();\n            ASSERT_EQ(pipe_sock->write(ptr, len), len);\n        });\n\n        // master pipe\n        bool result = System::socket_poll(fds, 0);\n        ASSERT_TRUE(result);\n\n        // child pipe\n        Coroutine::create([&](void *) {\n            auto pipe_sock = p.get_socket(true);\n            const char *ptr = text.c_str();\n            ASSERT_EQ(pipe_sock->write(ptr, len), len);\n        });\n\n        // master pipe\n        auto pipe_sock = p.get_socket(false);\n        result = System::wait_event(pipe_sock->get_fd(), SW_EVENT_READ, 0);\n        ASSERT_TRUE(result);\n    });\n}\n\nTEST(coroutine_system, exec) {\n    test::coroutine::run([](void *arg) {\n        int status;\n        auto buffer = std::shared_ptr<String>(swoole::make_string(1024));\n        ASSERT_TRUE(System::exec(\"ls /\", true, buffer, &status));\n        ASSERT_TRUE(buffer->contains(SW_STRL(\"tmp\")));\n    });\n}\n\nTEST(coroutine_system, waitpid) {\n    auto pid = spawn_exec([]() { sleep(2000); });\n\n    test::coroutine::run([pid](void *arg) {\n        int status;\n        ASSERT_EQ(System::waitpid(pid, &status, WNOHANG, -1), 0);\n        ASSERT_EQ(System::waitpid(pid, &status, 0, 0.1), -1);\n        ASSERT_ERREQ(ETIMEDOUT);\n\n        kill(pid, SIGKILL);\n        System::sleep(0.3);\n        ASSERT_EQ(System::waitpid(pid, &status, 0, 0.1), pid);\n    });\n}\n\nTEST(coroutine_system, waitpid_any) {\n    auto pid = spawn_exec([]() { sleep(2000); });\n\n    test::coroutine::run([pid](void *arg) {\n        int status;\n        ASSERT_EQ(System::waitpid(pid, &status, WNOHANG, -1), 0);\n        ASSERT_EQ(System::waitpid(pid, &status, 0, 0.1), -1);\n        ASSERT_ERREQ(ETIMEDOUT);\n\n        kill(pid, SIGKILL);\n        System::sleep(0.3);\n        ASSERT_EQ(System::waitpid(-1, &status, 0, 0.1), pid);\n    });\n}\n\nTEST(coroutine_system, read_file_fail) {\n    test::coroutine::run([](void *arg) {\n        ASSERT_EQ(System::read_file(\"/tmp/not-exists\", true), nullptr);\n        ASSERT_EQ(errno, ENOENT);\n    });\n}\n\nTEST(coroutine_system, write_file_fail) {\n    test::coroutine::run([](void *arg) {\n        ASSERT_EQ(System::write_file(\"/tmp/not-exists/file.log\", SW_STRL(TEST_STR)), -1);\n        ASSERT_EQ(errno, ENOENT);\n    });\n}\n"
  },
  {
    "path": "core-tests/src/coroutine/uring_socket.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: NathanFreeman  <mariasocute@163.com>                         |\n  +----------------------------------------------------------------------+\n */\n\n#include \"test_coroutine.h\"\n#include \"swoole_uring_socket.h\"\n#include \"swoole_util.h\"\n#include \"swoole_file.h\"\n\n#include <sys/file.h>\n#include <sys/stat.h>\n\n#ifdef SW_USE_IOURING\nusing swoole::Coroutine;\nusing swoole::File;\nusing swoole::Iouring;\nusing swoole::Reactor;\nusing swoole::String;\n\nusing swoole::coroutine::System;\nusing swoole::coroutine::UringSocket;\nusing swoole::network::IOVector;\nusing swoole::test::coroutine;\nusing swoole::test::create_socket_pair;\nusing swoole::test::get_jpg_file;\n\nTEST(uring_socket, connect) {\n    coroutine::run([](void *arg) {\n        UringSocket sock(SW_SOCK_TCP);\n        bool retval = sock.connect(TEST_HTTP_DOMAIN, 80);\n        ASSERT_EQ(retval, true);\n        ASSERT_EQ(sock.errCode, 0);\n\n        ssize_t rv;\n\n        auto req = swoole::test::http_get_request(TEST_HTTP_DOMAIN, \"/\");\n\n        rv = sock.send(req.c_str(), req.length());\n        ASSERT_EQ(rv, req.length());\n\n        char buf[4096];\n\n        rv = sock.recv(buf, sizeof(buf));\n        ASSERT_GT(rv, 100);\n\n        std::string s{buf};\n        ASSERT_TRUE(s.find(TEST_HTTP_EXPECT) != s.npos);\n    });\n}\n\nTEST(uring_socket, ssl_connect) {\n    coroutine::run([](void *arg) {\n        UringSocket sock(SW_SOCK_TCP);\n        sock.enable_ssl_encrypt();\n        sock.set_tls_host_name(TEST_HTTP_DOMAIN);\n        sock.set_ssl_verify_peer(true);\n\n        auto req = swoole::test::http_get_request(TEST_HTTP_DOMAIN, \"/\");\n\n        bool retval = sock.connect(TEST_HTTP_DOMAIN, 443);\n        ASSERT_EQ(retval, true);\n        ASSERT_EQ(sock.errCode, 0);\n\n        auto rv = sock.send(req.c_str(), req.length());\n        ASSERT_EQ(rv, req.length());\n\n        ASSERT_TRUE(sock.check_liveness());\n\n        swoole::String buf(1024 * 1024);\n        while (true) {\n            char rbuf[16384];\n            ssize_t nr = sock.recv(rbuf, sizeof(rbuf));\n            if (nr <= 0) {\n                break;\n            }\n            buf.append(rbuf, nr);\n        }\n        ASSERT_TRUE(buf.contains(TEST_HTTPS_EXPECT));\n    });\n}\n\nTEST(uring_socket, accept) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run({[port](void *arg) {\n                        UringSocket sock(SW_SOCK_TCP);\n                        bool retval = sock.bind(\"127.0.0.1\", port);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.listen(128), true);\n\n                        UringSocket *conn = sock.accept();\n                        ASSERT_NE(conn, nullptr);\n                        conn->write(TEST_STR, strlen(TEST_STR));\n\n                        char buf[128];\n                        auto n = conn->recv(buf, sizeof(buf));\n                        ASSERT_EQ(n, strlen(TEST_STR2));\n                        buf[n] = '\\0';\n                        ASSERT_STREQ(buf, TEST_STR2);\n\n                        delete conn;\n                    },\n\n                    [port](void *arg) {\n                        UringSocket sock(SW_SOCK_TCP);\n                        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.errCode, 0);\n\n                        char buf[128];\n                        auto n = sock.read(buf, sizeof(buf));\n                        ASSERT_EQ(n, strlen(TEST_STR));\n                        buf[n] = '\\0';\n                        ASSERT_STREQ(buf, TEST_STR);\n\n                        ASSERT_EQ(sock.send(TEST_STR2, strlen(TEST_STR2)), strlen(TEST_STR2));\n\n                        sock.close();\n                    }});\n}\n\nTEST(uring_socket, ssl_accept) {\n    const int port = __LINE__ + TEST_PORT;\n    auto jpg_file = get_jpg_file();\n    File file(jpg_file, File::READ);\n\n    auto svr = [port, &file](void *arg) {\n        UringSocket sock(SW_SOCK_TCP);\n        bool retval = sock.bind(\"127.0.0.1\", port);\n        ASSERT_EQ(retval, true);\n\n        sock.enable_ssl_encrypt();\n        sock.set_ssl_cert_file(swoole::test::get_ssl_dir() + \"/server.crt\");\n        sock.set_ssl_key_file(swoole::test::get_ssl_dir() + \"/server.key\");\n        sock.set_ssl_dhparam(swoole::test::get_ssl_dir() + \"/dhparams.pem\");\n        sock.set_ssl_ecdh_curve(\"secp256r1\");\n\n        ASSERT_EQ(sock.listen(128), true);\n\n        UringSocket *conn = sock.accept();\n        ASSERT_NE(conn, nullptr);\n        ASSERT_TRUE(conn->ssl_handshake());\n        ASSERT_EQ(conn->send(EOF_PACKET, strlen(EOF_PACKET)), strlen(EOF_PACKET));\n        char rbuf[1024];\n\n        auto n = conn->recv(rbuf, sizeof(rbuf));\n        ASSERT_GT(n, 0);\n        rbuf[n] = 0;\n        ASSERT_STREQ(rbuf, EOF_PACKET_2);\n\n        size_t fsize = file.get_size();\n        char *jpg = new char[fsize];\n        size_t nr = 0;\n\n        while (nr < fsize) {\n            auto ret = conn->recv(jpg + nr, fsize - nr);\n            ASSERT_GT(ret, 0);\n            nr += ret;\n        }\n        auto content = file.read_content();\n        ASSERT_MEMEQ(jpg, content.get()->value(), fsize);\n\n        conn->close();\n        delete conn;\n    };\n\n    auto cli = [port, &file](void *arg) {\n        UringSocket sock(SW_SOCK_TCP);\n        sock.enable_ssl_encrypt();\n        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n        ASSERT_EQ(retval, true);\n        ASSERT_EQ(sock.errCode, 0);\n\n        char rbuf[1024];\n        auto n = sock.recv(rbuf, sizeof(rbuf));\n        ASSERT_GT(n, 0);\n        rbuf[n] = 0;\n        ASSERT_STREQ(rbuf, EOF_PACKET);\n        ASSERT_EQ(sock.send(EOF_PACKET_2, strlen(EOF_PACKET_2)), strlen(EOF_PACKET_2));\n\n        ASSERT_TRUE(sock.sendfile(file.get_path().c_str(), 0, file.get_size()));\n\n        sock.close();\n    };\n\n    coroutine::run({svr, cli});\n}\n\nstatic void socket_set_length_protocol_1(UringSocket &sock) {\n    sock.protocol = {};\n\n    sock.protocol.package_length_type = 'n';\n    sock.protocol.package_length_size = swoole_type_size(sock.protocol.package_length_type);\n    sock.protocol.package_body_offset = 2;\n    sock.protocol.get_package_length = swoole::Protocol::default_length_func;\n    sock.protocol.package_max_length = 65535;\n\n    sock.open_length_check = true;\n}\n\nTEST(uring_socket, length_3) {\n    const int port = __LINE__ + TEST_PORT;\n    coroutine::run({[](void *arg) {\n                        UringSocket sock(SW_SOCK_TCP);\n                        bool retval = sock.bind(\"127.0.0.1\", port);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.listen(128), true);\n\n                        UringSocket *conn = sock.accept();\n                        char buf[1024];\n                        memset(buf, 'A', sizeof(buf));\n                        *(uint16_t *) buf = htons(65530);\n\n                        conn->send(buf, sizeof(buf));\n                    },\n\n                    [](void *arg) {\n                        UringSocket sock(SW_SOCK_TCP);\n                        bool retval = sock.connect(\"127.0.0.1\", port, -1);\n                        ASSERT_EQ(retval, true);\n                        ASSERT_EQ(sock.errCode, 0);\n\n                        socket_set_length_protocol_1(sock);\n                        sock.protocol.package_max_length = 4096;\n\n                        ssize_t l = sock.recv_packet(RECV_TIMEOUT);\n                        ASSERT_EQ(l, -1);\n                        ASSERT_EQ(sock.errCode, SW_ERROR_PACKAGE_LENGTH_TOO_LARGE);\n                    }});\n}\n\nTEST(uring_socket, sendmsg_and_recvmsg) {\n    coroutine::run([&](void *arg) {\n        int pairs[2];\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n\n        std::string text = \"Hello World\";\n        const size_t length = text.length();\n\n        Coroutine::create([&](void *) {\n            UringSocket sock(pairs[0], SW_SOCK_UNIX_STREAM);\n            struct msghdr msg;\n            struct iovec ivec;\n\n            msg.msg_control = nullptr;\n            msg.msg_controllen = 0;\n            msg.msg_flags = 0;\n            msg.msg_name = nullptr;\n            msg.msg_namelen = 0;\n            msg.msg_iov = &ivec;\n            msg.msg_iovlen = 1;\n\n            ivec.iov_base = (void *) text.c_str();\n            ivec.iov_len = length;\n\n            ssize_t ret = sock.sendmsg(&msg, 0);\n            sock.close();\n            ASSERT_EQ(ret, length);\n        });\n\n        UringSocket sock(pairs[1], SW_SOCK_UNIX_STREAM);\n        struct msghdr msg;\n        struct iovec ivec;\n        char buf[length + 1];\n\n        msg.msg_control = nullptr;\n        msg.msg_controllen = 0;\n        msg.msg_flags = 0;\n        msg.msg_name = nullptr;\n        msg.msg_namelen = 0;\n        msg.msg_iov = &ivec;\n        msg.msg_iovlen = 1;\n\n        ivec.iov_base = buf;\n        ivec.iov_len = length;\n\n        ssize_t ret = sock.recvmsg(&msg, 0);\n        buf[ret] = '\\0';\n        sock.close();\n        ASSERT_STREQ(buf, text.c_str());\n    });\n}\n\nstatic void test_sendto_recvfrom(enum swSocketType sock_type) {\n    coroutine::run([&](void *arg) {\n        std::string server_text = \"hello world!!!\";\n        size_t server_length = server_text.length();\n        std::string client_text = \"hello swoole!!!\";\n        size_t client_length = client_text.length();\n\n        const char *ip = sock_type == SW_SOCK_UDP ? \"127.0.0.1\" : \"::1\";\n        const char *local = \"localhost\";\n\n        int port = swoole::test::get_random_port();\n\n        UringSocket sock_server(sock_type);\n        UringSocket sock_client(sock_type);\n        sock_server.bind(ip, port);\n        sock_client.bind(ip, port + 1);\n\n        ON_SCOPE_EXIT {\n            sock_server.close();\n            sock_client.close();\n        };\n\n        sock_server.sendto(ip, port + 1, (const void *) server_text.c_str(), server_length);\n\n        char data_from_server[128] = {};\n        struct sockaddr_in serveraddr;\n        bzero(&serveraddr, sizeof(serveraddr));\n        serveraddr.sin_family = AF_INET;\n        serveraddr.sin_addr.s_addr = inet_addr(ip);\n        serveraddr.sin_port = htons(port);\n        socklen_t addr_length = sizeof(serveraddr);\n\n        // receive data from server\n        ssize_t result =\n            sock_client.recvfrom(data_from_server, server_length, (struct sockaddr *) &serveraddr, &addr_length);\n        data_from_server[result] = '\\0';\n        ASSERT_EQ(result, server_length);\n        ASSERT_STREQ(data_from_server, server_text.c_str());\n\n        // receive data from client\n        char data_from_client[128] = {};\n        sock_client.sendto(local, port, (const void *) client_text.c_str(), client_length);\n        result = sock_server.recvfrom(data_from_client, client_length);\n        data_from_client[client_length] = '\\0';\n        ASSERT_EQ(result, client_length);\n        ASSERT_STREQ(data_from_client, client_text.c_str());\n    });\n}\n\nTEST(uring_socket, sendto_recvfrom_udp) {\n    test_sendto_recvfrom(SW_SOCK_UDP);\n    test_sendto_recvfrom(SW_SOCK_UDP6);\n}\n\nTEST(uring_socket, writev_and_readv) {\n    coroutine::run([&](void *arg) {\n        int iovcnt = 3;\n        int pairs[2];\n        std::string text = \"Hello World\";\n        size_t length = text.length();\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n\n        Coroutine::create([&](void *) {\n            std::unique_ptr<iovec[]> iov(new iovec[iovcnt]);\n            for (int i = 0; i < iovcnt; i++) {\n                iov[i].iov_base = (void *) text.c_str();\n                iov[i].iov_len = length;\n            }\n            IOVector io_vector((struct iovec *) iov.get(), iovcnt);\n\n            UringSocket sock(pairs[0], SW_SOCK_UNIX_STREAM);\n            ssize_t result = sock.writev(&io_vector);\n            sock.close();\n            ASSERT_EQ(result, length * 3);\n        });\n\n        std::vector<std::string> results(iovcnt);\n        std::unique_ptr<iovec[]> iov(new iovec[iovcnt]);\n        for (int i = 0; i < iovcnt; i++) {\n            iov[i].iov_base = (void *) results[i].c_str();\n            iov[i].iov_len = length;\n        }\n        IOVector io_vector((struct iovec *) iov.get(), iovcnt);\n\n        UringSocket sock(pairs[1], SW_SOCK_UNIX_STREAM);\n        ssize_t result = sock.readv(&io_vector);\n        sock.close();\n        ASSERT_EQ(result, length * 3);\n\n        for (auto iter = results.begin(); iter != results.end(); iter++) {\n            (*iter)[length] = '\\0';\n            ASSERT_STREQ(text.c_str(), (*iter).c_str());\n        }\n    });\n}\n\nTEST(uring_socket, writevall_and_readvall) {\n    coroutine::run([&](void *arg) {\n        int write_iovcnt = 4;\n        int pairs[2];\n\n        char buf[65536];\n        swoole_random_bytes(buf, sizeof(buf));\n\n        std::string text(buf, sizeof(buf));\n        size_t length = text.length();\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n\n        Coroutine::create([&](void *) {\n            std::unique_ptr<iovec[]> iov(new iovec[write_iovcnt]);\n            for (int i = 0; i < write_iovcnt; i++) {\n                iov[i].iov_base = (void *) text.c_str();\n                iov[i].iov_len = length;\n            }\n\n            UringSocket sock(pairs[0], SW_SOCK_UNIX_STREAM);\n            sock.get_socket()->set_send_buffer_size(sizeof(buf));\n\n            IOVector io_vector1((struct iovec *) iov.get(), write_iovcnt);\n            ASSERT_EQ(sock.writev_all(&io_vector1), write_iovcnt * sizeof(buf));\n\n            System::sleep(0.01);\n\n            IOVector io_vector2((struct iovec *) iov.get(), write_iovcnt);\n            ASSERT_EQ(sock.writev_all(&io_vector2), write_iovcnt * sizeof(buf));\n\n            sock.close();\n        });\n\n        int read_iovcnt = 8;\n        std::unique_ptr<iovec[]> iov(new iovec[read_iovcnt]);\n        for (int i = 0; i < read_iovcnt; i++) {\n            iov[i].iov_base = sw_malloc(length);\n            iov[i].iov_len = length;\n        }\n        IOVector io_vector((struct iovec *) iov.get(), read_iovcnt);\n\n        UringSocket sock(pairs[1], SW_SOCK_UNIX_STREAM);\n        sock.get_socket()->set_recv_buffer_size(sizeof(buf));\n\n        ssize_t result = sock.readv_all(&io_vector);\n        sock.close();\n        ASSERT_EQ(result, length * read_iovcnt);\n\n        for (int i = 0; i < read_iovcnt; i++) {\n            ASSERT_MEMEQ(iov[i].iov_base, buf, sizeof(buf));\n            sw_free(iov[i].iov_base);\n        }\n    });\n}\n\nTEST(uring_socket, sendfile) {\n    coroutine::run([&](void *arg) {\n        int pairs[2];\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n        Coroutine::create([&](void *) {\n            std::string file = swoole::test::get_jpg_file();\n            UringSocket sock(pairs[0], SW_SOCK_UNIX_STREAM);\n            bool result = sock.sendfile(file.c_str(), 0, 0);\n            std::cout << sock.errMsg << \"\\n\";\n            sock.close();\n            ASSERT_TRUE(result);\n        });\n\n        char data[250000];\n        UringSocket sock(pairs[1], SW_SOCK_UNIX_STREAM);\n        ssize_t result = sock.read(data, 250000);\n        data[result] = '\\0';\n        sock.close();\n        ASSERT_GT(result, 0);\n    });\n}\n\nTEST(uring_socket, send_and_recv_all) {\n    coroutine::run([&](void *arg) {\n        int pairs[2];\n\n        String wbuf;\n        wbuf.append_random_bytes(4 * 1024 * 1024, false);\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n\n        Coroutine::create([&](void *) {\n            UringSocket sock(pairs[0], SW_SOCK_UNIX_STREAM);\n            sock.get_socket()->set_send_buffer_size(65536);\n\n            ASSERT_EQ(sock.send_all(wbuf.str, wbuf.length), wbuf.length);\n\n            System::sleep(0.1);\n\n            sock.close();\n        });\n\n        UringSocket sock(pairs[1], SW_SOCK_UNIX_STREAM);\n        sock.get_socket()->set_recv_buffer_size(65536);\n\n        String rbuf(wbuf.length);\n        ssize_t result = sock.recv_all(rbuf.str, wbuf.length);\n        ASSERT_EQ(result, wbuf.length);\n        ASSERT_MEMEQ(wbuf.str, rbuf.str, wbuf.length);\n        System::sleep(0.1);\n        sock.close();\n    });\n}\n\nTEST(uring_socket, poll) {\n    coroutine::run([&](void *arg) {\n        int pairs[2];\n\n        String wbuf;\n        wbuf.append_random_bytes(4 * 1024 * 1024, false);\n        socketpair(AF_UNIX, SOCK_STREAM, 0, pairs);\n\n        UringSocket sock(pairs[1], SW_SOCK_UNIX_STREAM);\n        sock.get_socket()->set_recv_buffer_size(65536);\n\n        bool rs;\n\n        rs = sock.poll(SW_EVENT_READ, 0.01);\n        ASSERT_FALSE(rs);\n        ASSERT_EQ(sock.errCode, ETIMEDOUT);\n\n        TEST_WRITE(pairs[0], TEST_STR);\n        rs = sock.poll(SW_EVENT_READ, 0.01);\n        ASSERT_TRUE(rs);\n    });\n}\n\nTEST(uring_socket, ssl_readv) {\n    coroutine::run([&](void *arg) {\n        UringSocket client(SW_SOCK_TCP);\n        client.enable_ssl_encrypt();\n        client.set_tls_host_name(TEST_HTTP_DOMAIN);\n        ASSERT_TRUE(client.connect(TEST_HTTP_DOMAIN, 443));\n\n        auto req = swoole::test::http_get_request(TEST_HTTP_DOMAIN, \"/\");\n\n        constexpr off_t offset1 = TEST_WRITEV_OFFSET;\n        iovec wr_iov[2];\n        wr_iov[0].iov_base = (void *) req.c_str();\n        wr_iov[0].iov_len = offset1;\n        wr_iov[1].iov_base = (char *) req.c_str() + offset1;\n        wr_iov[1].iov_len = req.length() - offset1;\n\n        swoole::network::IOVector wr_vec(wr_iov, 2);\n        ASSERT_EQ(client.writev(&wr_vec), req.length());\n\n        sw_tg_buffer()->clear();\n        if (sw_tg_buffer()->size < 1024 * 1024) {\n            sw_tg_buffer()->extend(1024 * 1024);\n        }\n\n        constexpr off_t offset2 = TEST_READV_OFFSET;\n        iovec rd_iov[2];\n        rd_iov[0].iov_base = sw_tg_buffer()->str;\n        rd_iov[0].iov_len = offset2;\n        rd_iov[1].iov_base = sw_tg_buffer()->str + offset2;\n        rd_iov[1].iov_len = sw_tg_buffer()->size - offset2;\n\n        swoole::network::IOVector rd_vec(rd_iov, 2);\n        auto rv = client.readv(&rd_vec);\n        ASSERT_GT(rv, 1024);\n        sw_tg_buffer()->length = rv;\n        sw_tg_buffer()->set_null_terminated();\n\n        ASSERT_TRUE(sw_tg_buffer()->contains(TEST_HTTPS_EXPECT));\n    });\n}\n#endif\n"
  },
  {
    "path": "core-tests/src/lock/lock.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_coroutine.h\"\n#include \"swoole_lock.h\"\n#include \"swoole_util.h\"\n\n#include <thread>\n\nusing swoole::Lock;\nusing swoole::RWLock;\nusing swoole::SpinLock;\nusing swoole::Coroutine;\nusing swoole::CoroutineLock;\nusing swoole::Mutex;\nusing swoole::coroutine::System;\nusing swoole::test::coroutine;\n\nstatic void test_func(Lock &lock) {\n    int count = 0;\n    const int N = 100000;\n\n    auto fn = [&]() {\n        for (int i = 0; i < N; i++) {\n            ASSERT_EQ(lock.lock(), 0);\n            count++;\n            ASSERT_EQ(lock.unlock(), 0);\n        }\n    };\n\n    std::thread t1(fn);\n    std::thread t2(fn);\n\n    t1.join();\n    t2.join();\n\n    ASSERT_EQ(count, N * 2);\n}\n\nstatic void test_lock_rd_func(Lock &lock) {\n    std::thread t1([&lock]() {\n        ASSERT_EQ(lock.lock(LOCK_SH), 0);\n        usleep(2000);  // wait\n        lock.unlock();\n    });\n\n    std::thread t2([&lock]() {\n        usleep(1000);\n        ASSERT_GE(lock.lock(LOCK_SH | LOCK_NB), 0);\n    });\n\n    t1.join();\n    t2.join();\n}\n\nstatic void test_share_lock_fun(Lock &lock) {\n    lock.lock();\n    const int sleep_us = 10000;\n    int magic_num = swoole_rand(100000, 9999999);\n    int *_num = (int *) sw_mem_pool()->alloc(sizeof(int));\n    *_num = 0;\n\n    pid_t pid = fork();\n\n    if (pid == 0) {\n        lock.lock();\n        *_num = magic_num;\n        usleep(1);\n        exit(0);\n    } else {\n        usleep(sleep_us);\n        lock.unlock();\n        int status;\n        pid_t _pid = waitpid(pid, &status, 0);\n        if (_pid != pid) {\n            swoole_warning(\"error pid=%d\", _pid);\n        }\n        ASSERT_EQ(*_num, magic_num);\n    }\n}\n\nTEST(lock, mutex) {\n    Mutex lock(0);\n    test_func(reinterpret_cast<Lock &>(lock));\n}\n\nTEST(lock, lockwait) {\n    Mutex lock(0);\n\n    lock.lock();\n\n    std::thread t1([&lock]() {\n        long ms1 = swoole::time<std::chrono::milliseconds>();\n        const int TIMEOUT_1 = 2;\n        ASSERT_EQ(lock.lock(LOCK_EX, TIMEOUT_1), ETIMEDOUT);\n        long ms2 = swoole::time<std::chrono::milliseconds>();\n\n        ASSERT_GE(ms2 - ms1, TIMEOUT_1);\n\n        const int TIMEOUT_2 = 10;\n        ASSERT_EQ(lock.lock(LOCK_EX, TIMEOUT_2), 0);\n        long ms3 = swoole::time<std::chrono::milliseconds>();\n\n        ASSERT_LE(ms3 - ms2, TIMEOUT_2);\n    });\n\n    std::this_thread::sleep_for(std::chrono::milliseconds(10));\n    lock.unlock();\n\n    t1.join();\n}\n\nTEST(lock, shared) {\n    Mutex lock(true);\n    test_share_lock_fun(lock);\n}\n\nTEST(lock, try_rd) {\n    Mutex lock(0);\n    test_lock_rd_func(lock);\n}\n\nTEST(lock, coroutine_lock) {\n    auto *lock = new CoroutineLock(false);\n    ASSERT_EQ(lock->lock(), SW_ERROR_CO_OUT_OF_COROUTINE);\n    ASSERT_EQ(lock->unlock(), SW_ERROR_CO_OUT_OF_COROUTINE);\n\n    coroutine::run([lock](void *arg) {\n        Coroutine::create([lock](void *) {\n            ASSERT_EQ(lock->lock(), 0);\n            ASSERT_EQ(lock->lock(), 0);\n            System::sleep(1);\n            ASSERT_EQ(lock->unlock(), 0);\n        });\n\n        Coroutine::create([lock](void *) {\n            ASSERT_EQ(lock->lock(), 0);\n            System::sleep(1);\n            ASSERT_EQ(lock->unlock(), 0);\n            // unlock 2, no effect\n            ASSERT_EQ(lock->unlock(), 0);\n        });\n\n        Coroutine::create([lock](void *) { ASSERT_EQ(lock->lock(LOCK_NB), EBUSY); });\n    });\n\n    delete lock;\n}\n\n#ifndef HAVE_IOURING_FUTEX\nTEST(lock, coroutine_lock_cancel) {\n    CoroutineLock lock(true);\n    coroutine::run([&](void *arg) {\n        ASSERT_EQ(lock.lock(), 0);\n        Coroutine::create([&](void *) {\n            auto co = Coroutine::get_current();\n            swoole_timer_after(20, [co](TIMER_PARAMS) {\n                DEBUG() << \"cancel coroutine \" << co->get_cid() << \"\\n\";\n                co->cancel();\n            });\n            ASSERT_EQ(lock.lock(), SW_ERROR_CO_CANCELED);\n        });\n    });\n}\n#endif\n\nTEST(lock, coroutine_lock_rd) {\n    auto *lock = new CoroutineLock(false);\n    ASSERT_EQ(lock->lock(LOCK_SH), SW_ERROR_CO_OUT_OF_COROUTINE);\n\n    coroutine::run([lock](void *arg) {\n        Coroutine::create([lock](void *) {\n            ASSERT_EQ(lock->lock(LOCK_SH), 0);\n            ASSERT_EQ(lock->lock(LOCK_SH), 0);\n            System::sleep(0.3);\n            ASSERT_EQ(lock->unlock(), 0);\n        });\n\n        Coroutine::create([lock](void *) {\n            ASSERT_EQ(lock->lock(LOCK_SH), 0);\n            System::sleep(0.3);\n            ASSERT_EQ(lock->unlock(), 0);\n        });\n\n        Coroutine::create([lock](void *) { ASSERT_EQ(lock->lock(LOCK_SH | LOCK_NB), EBUSY); });\n    });\n\n    delete lock;\n}\n\n#ifdef HAVE_RWLOCK\nTEST(lock, rwlock_shared) {\n    RWLock lock(true);\n    test_share_lock_fun(lock);\n}\n\nTEST(lock, rwlock) {\n    RWLock lock(false);\n    test_func(lock);\n}\n\nTEST(lock, rwlock_try_rd) {\n    RWLock lock(false);\n    test_lock_rd_func(lock);\n}\n\nTEST(lock, rw_try_wr) {\n    RWLock lock(false);\n    std::thread t1([&lock]() {\n        ASSERT_EQ(lock.lock(), 0);\n        usleep(2000);\n        lock.unlock();\n    });\n\n    std::thread t2([&lock]() {\n        usleep(1000);\n        ASSERT_GT(lock.lock(LOCK_NB), 0);\n    });\n    t1.join();\n    t2.join();\n}\n#endif\n\n#ifdef HAVE_SPINLOCK\nTEST(lock, spinlock_shared) {\n    SpinLock lock(true);\n    test_share_lock_fun(lock);\n}\n\nTEST(lock, spinlock) {\n    SpinLock lock(false);\n    test_func(lock);\n}\n\nTEST(lock, spinlock_try_rd) {\n    SpinLock lock(false);\n    test_lock_rd_func(lock);\n}\n#endif\n"
  },
  {
    "path": "core-tests/src/main.cpp",
    "content": "#include \"test_core.h\"\n#include \"swoole_memory.h\"\n\n#include <dirent.h>\n#include <system_error>\n\nusing namespace swoole;\nusing namespace std;\n\nstatic string root_path;\nstatic int *test_counter;\n\nstatic void init_root_path(const char *);\n\nint main(int argc, char **argv) {\n    swoole_init();\n    SwooleG.max_sockets = 20000;\n    init_root_path(argv[0]);\n\n    if (getenv(\"DISPLAY_BACKTRACE\") != nullptr) {\n        sw_logger()->display_backtrace();\n    }\n\n#ifdef SW_VERBOSE\n    swoole_set_log_level(SW_LOG_TRACE);\n    swoole_set_trace_flags(SW_TRACE_ALL);\n#endif\n\n    if (getenv(\"VERBOSE\") != nullptr && std::string(getenv(\"VERBOSE\")) == \"0\") {\n        swoole_set_log_level(SW_LOG_INFO);\n        test::debug_output = test::null_stream;\n    }\n\n    test_counter = static_cast<int *>(sw_mem_pool()->alloc(sizeof(int) * TEST_COUNTER_NUM));\n\n    ::testing::InitGoogleTest(&argc, argv);\n    int retval = RUN_ALL_TESTS();\n\n    swoole_clean();\n\n    return retval;\n}\n\nstatic void init_root_path(const char *_exec_file) {\n    char buf[PATH_MAX];\n    string file;\n    if (_exec_file[0] == '/') {\n        file = _exec_file;\n    } else {\n        char *dir = getcwd(buf, sizeof(buf));\n        file = string(dir) + \"/\" + _exec_file;\n    }\n    string relative_root_path = file.substr(0, file.rfind('/')) + \"/../\";\n    char *_realpath = realpath(relative_root_path.c_str(), buf);\n    if (_realpath == nullptr) {\n        root_path = relative_root_path;\n    } else {\n        root_path = string(_realpath);\n    }\n}\n\nnamespace swoole::test {\nNullStream null_stream;\nstd::reference_wrapper<std::ostream> debug_output(std::cout);\n\nvoid counter_init() {\n    sw_memset_zero(test_counter, sizeof(int) * TEST_COUNTER_NUM);\n}\n\nint *counter_ptr() {\n    return test_counter;\n}\n\nint counter_incr(int index, int add) {\n    return sw_atomic_add_fetch(&test_counter[index], add);\n}\n\nint counter_get(int index) {\n    return test_counter[index];\n}\n\nvoid counter_set(int index, int value) {\n    test_counter[index] = value;\n}\n\nvoid counter_incr_and_put_log(int index, const char *msg) {\n    DEBUG() << \"PID: \" << getpid() << \", VALUE: \" << counter_incr(index) << \"; \" << msg << std::endl;\n}\n\n/**\n * swoole-src root path\n */\nconst string &get_root_path() {\n    return root_path;\n}\n\nstring get_ssl_dir() {\n    return get_root_path() + \"/tests/include/ssl_certs\";\n}\n\nstring get_jpg_file() {\n    return root_path + TEST_JPG_FILE;\n}\n\nstring http_get_request(const string &domain, const string &path) {\n    return \"GET \" + path +\n           \" HTTP/1.1\\r\\n\"\n           \"Host: \" +\n           domain +\n           \"\\r\\n\"\n           \"Connection: close\\r\\n\"\n           \"User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) \"\n           \"Chrome/51.0.2704.106 Safari/537.36\"\n           \"\\r\\n\\r\\n\";\n}\n\nbool is_github_ci() {\n    return getenv(\"GITHUB_ACTIONS\") != nullptr;\n}\n\nint exec_js_script(const std::string &file, const std::string &args) {\n    std::string command = \"bash -c 'node \" + get_root_path() + \"/core-tests/js/\" + file + \" \" + args + \"'\";\n    return std::system(command.c_str());\n}\n\nint get_random_port() {\n    return TEST_PORT + swoole_random_int() % 10000;\n}\n\nbool is_valid_fd(int fd) {\n\treturn fcntl(fd, F_GETFD) != -1;\n}\n\nint wait_all_child_processes(bool verbose) {\n    pid_t pid;\n    int status;\n    int count = 0;\n\n    // 循环等待所有子进程结束\n    while (true) {\n        // 使用waitpid等待任意子进程，这里会阻塞直到有子进程退出\n        pid = waitpid(-1, &status, 0);\n\n        if (pid > 0) {\n            // 成功回收一个子进程\n            count++;\n\n            // 输出子进程退出状态（如果启用详细输出）\n            if (verbose) {\n                if (WIFEXITED(status)) {\n                    std::cout << \"子进程 \" << pid << \" 正常退出，退出码: \" << WEXITSTATUS(status) << std::endl;\n                } else if (WIFSIGNALED(status)) {\n                    std::cout << \"子进程 \" << pid << \" 被信号 \" << WTERMSIG(status) << \" 终止\";\n\n                    if (WCOREDUMP(status)) {\n                        std::cout << \" (核心已转储)\";\n                    }\n\n                    std::cout << std::endl;\n                }\n            }\n        } else if (pid < 0) {\n            if (errno == ECHILD) {\n                // 没有子进程了，完成回收\n                if (verbose) {\n                    std::cout << \"所有子进程已回收，共 \" << count << \" 个\" << std::endl;\n                }\n                break;\n            } else {\n                // 其他错误\n                if (verbose) {\n                    perror(\"waitpid failed\");\n                }\n                return -1;\n            }\n        }\n    }\n\n    return count;\n}\n\n// 检测子进程\nint has_child_processes() {\n    pid_t current_pid = getpid();\n    DIR *proc_dir;\n    struct dirent *entry;\n    char stat_path[512];\n    FILE *stat_file;\n    char buffer[1024];\n    pid_t pid, ppid;\n\n    // 尝试使用waitpid快速检测\n    if (waitpid(-1, NULL, WNOHANG) == -1 && errno == ECHILD) {\n        return 0;  // 没有子进程\n    }\n\n    // 如果waitpid没有明确结果，使用/proc检测\n    proc_dir = opendir(\"/proc\");\n    if (!proc_dir) {\n        perror(\"opendir /proc failed\");\n        return -1;\n    }\n\n    while ((entry = readdir(proc_dir)) != NULL) {\n        if (entry->d_type == DT_DIR && entry->d_name[0] >= '0' && entry->d_name[0] <= '9') {\n            snprintf(stat_path, sizeof(stat_path), \"/proc/%s/stat\", entry->d_name);\n            stat_file = fopen(stat_path, \"r\");\n            if (stat_file) {\n                if (fgets(buffer, sizeof(buffer), stat_file)) {\n                    sscanf(buffer, \"%d %*s %*c %d\", &pid, &ppid);\n                    if (ppid == current_pid) {\n                        fclose(stat_file);\n                        closedir(proc_dir);\n                        return 1;  // 找到子进程\n                    }\n                }\n                fclose(stat_file);\n            }\n        }\n    }\n\n    closedir(proc_dir);\n    return 0;  // 没有子进程\n}\n\n// 检测线程\nint has_threads() {\n    FILE *status_file;\n    char path[256];\n    char line[256];\n    int thread_count = -1;\n\n    snprintf(path, sizeof(path), \"/proc/%d/status\", getpid());\n    status_file = fopen(path, \"r\");\n    if (!status_file) {\n        perror(\"fopen failed\");\n        return -1;\n    }\n\n    while (fgets(line, sizeof(line), status_file)) {\n        if (strncmp(line, \"Threads:\", 8) == 0) {\n            sscanf(line, \"Threads: %d\", &thread_count);\n            break;\n        }\n    }\n\n    fclose(status_file);\n    return thread_count;\n}\n\n/**\n * 检查目录是否为空\n * @param path 目录路径\n * @return 如果目录为空返回1，否则返回0\n */\nint is_directory_empty(const char *path) {\n    DIR *dir = opendir(path);\n    if (dir == NULL) {\n        perror(\"opendir\");\n        return 0;\n    }\n\n    int is_empty = 1;\n    struct dirent *entry;\n\n    while ((entry = readdir(dir)) != NULL) {\n        // 跳过 \".\" 和 \"..\" 目录\n        if (strcmp(entry->d_name, \".\") != 0 && strcmp(entry->d_name, \"..\") != 0) {\n            is_empty = 0;\n            break;\n        }\n    }\n\n    closedir(dir);\n    return is_empty;\n}\n\n/**\n * 检查路径是否为目录\n * @param path 路径\n * @return 如果是目录返回1，否则返回0\n */\nint is_directory(const char *path) {\n    struct stat path_stat;\n    if (stat(path, &path_stat) != 0) {\n        return 0;\n    }\n    return S_ISDIR(path_stat.st_mode);\n}\n\n/**\n * 获取父目录路径\n * @param path 当前路径\n * @param parent_path 用于存储父目录路径的缓冲区\n * @param size 缓冲区大小\n * @return 成功返回1，失败返回0\n */\nint get_parent_directory(const char *path, char *parent_path, size_t size) {\n    auto last_slash = strrchr(path, '/');\n    if (last_slash == NULL || last_slash == path) {\n        // 没有斜杠或者斜杠是第一个字符（根目录）\n        return 0;\n    }\n\n    size_t parent_length = last_slash - path;\n    if (parent_length >= size) {\n        return 0;\n    }\n\n    strncpy(parent_path, path, parent_length);\n    parent_path[parent_length] = '\\0';\n\n    // 处理路径只有一个斜杠的情况\n    if (parent_length == 0) {\n        parent_path[0] = '/';\n        parent_path[1] = '\\0';\n    }\n\n    return 1;\n}\n\n/**\n * 递归删除空目录\n * @param path 要删除的目录路径\n * @return 成功删除的目录数量\n */\nint recursive_rmdir(const char *path) {\n    // 检查路径是否存在且是目录\n    if (!is_directory(path)) {\n        return 0;\n    }\n\n    // 检查目录是否为空\n    if (!is_directory_empty(path)) {\n        return 0;\n    }\n\n    int deleted_count = 0;\n\n    // 删除当前空目录\n    if (rmdir(path) == 0) {\n        deleted_count++;\n\n        // 获取父目录\n        char parent_path[PATH_MAX];\n        if (get_parent_directory(path, parent_path, PATH_MAX)) {\n            // 如果父目录存在且不是当前目录，则尝试删除父目录\n            if (strcmp(parent_path, path) != 0) {\n                deleted_count += recursive_rmdir(parent_path);\n            }\n        }\n    }\n\n    return deleted_count;\n}\n\npid_t spawn_exec(const std::function<void(void)> &fn) {\n    pid_t child_pid = fork();\n    if (child_pid == -1) {\n        throw std::system_error{errno, std::generic_category()};\n    } else if (child_pid == 0) {\n        fn();\n        exit(0);\n    }\n    return child_pid;\n}\n\nint spawn_exec_and_wait(const std::function<void(void)> &fn) {\n    int status;\n    pid_t pid = spawn_exec(fn);\n    if (swoole_waitpid(pid, &status, 0) == pid) {\n        return status;\n    } else {\n        return -1;\n    }\n}\n}  // namespace swoole::test\n"
  },
  {
    "path": "core-tests/src/memory/buffer.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"swoole_memory.h\"\n#include \"swoole_buffer.h\"\n\nusing namespace std;\nusing namespace swoole;\n\nTEST(buffer, append_iov) {\n    Buffer buf(1024);\n    Buffer buf_for_offset(1024);\n\n    constexpr int N = 4;\n    int iovcnt = N;\n    iovec v[N];\n    size_t total_len = 0;\n\n    SW_LOOP_N(iovcnt) {\n        v[i].iov_len = swoole_rand(99, 4095);\n        total_len += v[i].iov_len;\n    }\n\n    unique_ptr<char[]> s1(new char[v[0].iov_len]);\n    unique_ptr<char[]> s2(new char[v[1].iov_len]);\n    unique_ptr<char[]> s3(new char[v[2].iov_len]);\n    unique_ptr<char[]> s4(new char[v[3].iov_len]);\n\n    v[0].iov_base = s1.get();\n    v[1].iov_base = s2.get();\n    v[2].iov_base = s3.get();\n    v[3].iov_base = s4.get();\n\n    memset(v[0].iov_base, 'A', v[0].iov_len);\n    memset(v[1].iov_base, 'B', v[1].iov_len);\n    memset(v[2].iov_base, 'C', v[2].iov_len);\n    memset(v[3].iov_base, 'D', v[3].iov_len);\n\n    buf.append(v, iovcnt, 0);\n    ASSERT_EQ(buf.length(), total_len);\n\n    size_t offset = swoole_rand(v[0].iov_len + 1, total_len - 1);\n    buf_for_offset.append(v, iovcnt, offset);\n    ASSERT_EQ(buf_for_offset.length(), total_len - offset);\n\n    String str(buf_for_offset.length());\n\n    while (!buf_for_offset.empty()) {\n        auto chunk = buf_for_offset.front();\n        str.append(chunk->value.str, chunk->length);\n        buf_for_offset.pop();\n    }\n\n    size_t indent = 0;\n    SW_LOOP_N(iovcnt) {\n        if (offset >= v[i].iov_len) {\n            offset -= v[i].iov_len;\n            continue;\n        }\n\n        ASSERT_EQ(memcmp(str.str + indent, (char *) v[i].iov_base + offset, v[i].iov_len - offset), 0);\n        indent += v[i].iov_len - offset;\n        offset = 0;\n    }\n}\n"
  },
  {
    "path": "core-tests/src/memory/fixed_pool.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"swoole_memory.h\"\n#include \"swoole_util.h\"\n\nusing namespace std;\n\nTEST(fixed_pool, alloc) {\n    auto *pool = new swoole::FixedPool(1024, 256, false);\n\n    list<void *> alloc_list;\n    ASSERT_EQ(pool->get_slice_size(), 256);\n\n    for (int i = 0; i < 1200; i++) {\n        int j = rand();\n        void *mem;\n\n        if (j % 4 < 3) {\n            mem = pool->alloc(0);\n            ASSERT_TRUE(mem);\n            alloc_list.push_back(mem);\n        } else if (!alloc_list.empty()) {\n            if (j % 2 == 1) {\n                mem = alloc_list.front();\n                alloc_list.pop_front();\n            } else {\n                mem = alloc_list.back();\n                alloc_list.pop_back();\n            }\n            pool->free(mem);\n        }\n    }\n    pool->debug(1);\n    delete pool;\n}\n\nTEST(fixed_pool, realloc) {\n    void *memory = sw_shm_malloc(1024);\n    void *new_memory = sw_shm_realloc(memory, 2048);\n    ON_SCOPE_EXIT {\n        sw_shm_free(new_memory);\n    };\n    ASSERT_NE(new_memory, nullptr);\n}\n"
  },
  {
    "path": "core-tests/src/memory/global_memory.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"swoole_memory.h\"\n\nTEST(global_memory, alloc) {\n    auto pool = new swoole::GlobalMemory(2 * 1024 * 1024, false);\n\n    char *ptr1 = (char *) pool->alloc(199);\n    pool->free(ptr1);\n    strcpy(ptr1, \"hello, world, #1\");\n\n    char *ptr2 = (char *) pool->alloc(17);\n    strcpy(ptr2, \"hello, world, #2\");\n    pool->free(ptr2);\n\n    char *ptr3 = (char *) pool->alloc(113);\n    strcpy(ptr3, \"hello, world, #3\");\n\n    ASSERT_TRUE(ptr1);\n    ASSERT_TRUE(ptr2);\n    ASSERT_TRUE(ptr3);\n\n    ASSERT_GT(pool->capacity(), 2 * 1024 * 1024 - 512);\n    ASSERT_GT(pool->get_memory_size(), 0);\n\n    ASSERT_STREQ(ptr1, \"hello, world, #1\");\n    ASSERT_STREQ(ptr2, \"hello, world, #2\");\n    ASSERT_STREQ(ptr3, \"hello, world, #3\");\n\n    delete pool;\n}\n"
  },
  {
    "path": "core-tests/src/memory/lru_cache.cpp",
    "content": "#include \"test_core.h\"\n#include \"swoole_lru_cache.h\"\n\nusing namespace swoole;\nusing namespace std;\n\nint dtor_num = 0;\nclass lru_cache_test_class {\n  public:\n    lru_cache_test_class() {}\n\n    ~lru_cache_test_class() {\n        ++dtor_num;\n    }\n};\n\nTEST(lru_cache, basic) {\n    LRUCache<string> cache(2);\n    shared_ptr<string> val = make_shared<string>(\"hello\");\n    shared_ptr<string> val1 = make_shared<string>(\"hello1\");\n\n    cache.set(\"test\", val);\n    ASSERT_EQ(cache.get(\"test\").get(), val.get());\n\n    cache.set(\"test\", val1);\n    ASSERT_EQ(cache.get(\"test\").get(), val1.get());\n\n    cache.del(\"test\");\n    ASSERT_EQ(cache.get(\"test\"), nullptr);\n\n    cache.set(\"test\", val);\n    ASSERT_EQ(cache.get(\"test\").get(), val.get());\n\n    cache.clear();\n    ASSERT_EQ(cache.get(\"test\"), nullptr);\n}\n\nTEST(lru_cache, memory_free) {\n    LRUCache<lru_cache_test_class> cache(2);\n    shared_ptr<lru_cache_test_class> val = make_shared<lru_cache_test_class>();\n    cache.set(\"test\", val);\n    ASSERT_EQ(cache.get(\"test\").get(), val.get());\n    val.reset();\n    ASSERT_EQ(dtor_num, 0);\n    cache.clear();\n    ASSERT_EQ(dtor_num, 1);\n}\n\nTEST(lru_cache, lru_kick) {\n    LRUCache<lru_cache_test_class> cache(2);\n    dtor_num = 0;\n    shared_ptr<lru_cache_test_class> val = make_shared<lru_cache_test_class>();\n    shared_ptr<lru_cache_test_class> val1 = make_shared<lru_cache_test_class>();\n    shared_ptr<lru_cache_test_class> val2 = make_shared<lru_cache_test_class>();\n    shared_ptr<lru_cache_test_class> val3 = make_shared<lru_cache_test_class>();\n\n    cache.set(\"test\", val);\n    ASSERT_EQ(cache.get(\"test\").get(), val.get());\n    val.reset();\n    ASSERT_EQ(dtor_num, 0);\n\n    cache.set(\"test1\", val1);\n    ASSERT_EQ(cache.get(\"test1\").get(), val1.get());\n    val1.reset();\n    ASSERT_EQ(dtor_num, 0);\n\n    cache.set(\"test2\", val2);\n    ASSERT_EQ(cache.get(\"test2\").get(), val2.get());\n    val2.reset();\n    ASSERT_EQ(dtor_num, 1);\n    ASSERT_EQ(cache.get(\"test\"), nullptr);\n\n    shared_ptr<lru_cache_test_class> val4 = make_shared<lru_cache_test_class>();\n    cache.set(\"test1\", val4);  // update test1 and will del test2\n    ASSERT_EQ(cache.get(\"test1\").get(), val4.get());\n    ASSERT_EQ(dtor_num, 2);\n\n    cache.set(\"test3\", val3);\n    ASSERT_EQ(cache.get(\"test3\").get(), val3.get());\n    val3.reset();\n    ASSERT_EQ(dtor_num, 3);\n    ASSERT_EQ(cache.get(\"test2\"), nullptr);\n\n    cache.clear();\n    ASSERT_EQ(dtor_num, 4);\n}\n"
  },
  {
    "path": "core-tests/src/memory/ringbuffer.cpp",
    "content": "#include \"test_core.h\"\n#include \"swoole_memory.h\"\n#include \"swoole_pipe.h\"\n\nusing namespace swoole;\n\n#include <thread>\n\n#define READ_THREAD_N 4\n#define WRITE_N 10000\n#define PACKET_LEN 90000\n//#define PRINT_SERNUM_N      10\n\nstatic MemoryPool *pool = NULL;\n\ntypedef struct {\n    uint32_t id;\n    uint32_t size;\n    uint32_t serial_num;\n    void *ptr;\n} pkg;\n\ntypedef struct {\n    std::thread *thread;\n    UnixSocket *pipe;\n} ThreadObject;\n\nstatic void thread_read(int i);\nstatic void thread_write();\nstatic ThreadObject threads[READ_THREAD_N];\n\nstatic void test_ringbuffer(bool shared) {\n    int i;\n    pool = new RingBuffer(1024 * 1024 * 4, shared);\n    ASSERT_NE(nullptr, pool);\n\n    for (i = 0; i < READ_THREAD_N; i++) {\n        threads[i].pipe = new UnixSocket(true, SOCK_DGRAM);\n        ASSERT_TRUE(threads[i].pipe->ready());\n        threads[i].thread = new std::thread(thread_read, i);\n    }\n\n    sleep(1);\n    srand((unsigned int) time(NULL));\n    thread_write();\n\n    for (i = 0; i < READ_THREAD_N; i++) {\n        threads[i].thread->join();\n        delete threads[i].pipe;\n        delete threads[i].thread;\n    }\n\n    delete pool;\n}\n\nTEST(ringbuffer, thread) {\n    test_ringbuffer(true);\n    test_ringbuffer(false);\n}\n\nstatic void thread_write() {\n    uint32_t size, yield_count = 0, yield_total_count = 0;\n    void *ptr;\n    pkg send_pkg;\n    sw_memset_zero(&send_pkg, sizeof(send_pkg));\n\n    int i;\n    for (i = 0; i < WRITE_N; i++) {\n        size = 10000 + rand() % PACKET_LEN;\n        // printf(\"[ < %d] alloc size=%d\\n\", i, size);\n\n        yield_count = 0;\n        do {\n            ptr = pool->alloc(size);\n            if (ptr) {\n                break;\n            } else {\n                yield_count++;\n                yield_total_count++;\n                usleep(10);\n            }\n        } while (yield_count < 100);\n\n        if (!ptr) {\n            swoole_warning(\"alloc failed. index=%d, break\", i);\n        }\n        ASSERT_NE(ptr, nullptr);\n\n        send_pkg.id = i;\n        send_pkg.ptr = ptr;\n        send_pkg.size = size;\n        send_pkg.serial_num = rand();\n\n        //保存长度值\n        memcpy(ptr, &size, sizeof(size));\n        //在指针末尾保存一个串号\n        memcpy((char *) ptr + size - 4, &(send_pkg.serial_num), sizeof(send_pkg.serial_num));\n\n        ASSERT_FALSE(threads[i % READ_THREAD_N].pipe->write(&send_pkg, sizeof(send_pkg)) < 0);\n    }\n\n    //    printf(\"yield_total_count=%d\\n\", yield_total_count);\n}\n\nstatic void thread_read(int i) {\n    pkg recv_pkg;\n    uint32_t tmp;\n    int ret;\n    uint32_t recv_count = 0;\n    int j = 0;\n    UnixSocket *sock = threads[i].pipe;\n    int task_n = WRITE_N / READ_THREAD_N;\n\n    for (j = 0; j < task_n; j++) {\n        ret = sock->read(&recv_pkg, sizeof(recv_pkg));\n        ASSERT_FALSE(ret < 0);\n\n        memcpy(&tmp, recv_pkg.ptr, sizeof(tmp));\n        ASSERT_EQ(tmp, recv_pkg.size);\n\n        memcpy(&tmp, (char *) recv_pkg.ptr + recv_pkg.size - 4, sizeof(tmp));\n        ASSERT_EQ(tmp, recv_pkg.serial_num);\n\n#ifdef PRINT_SERNUM_N\n        if (j % PRINT_SERNUM_N == 0) {\n            printf(\"[ > %d] recv. recv_count=%d, serial_num=%d\\n\", recv_pkg.id, recv_count, tmp);\n        }\n#endif\n\n        pool->free(recv_pkg.ptr);\n        recv_count++;\n    }\n}\n"
  },
  {
    "path": "core-tests/src/memory/table.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"swoole_table.h\"\n\nusing namespace swoole;\n\n#include <exception>\n#include <map>\n\nstruct exception_t : public std::exception {\n    int code;\n    std::string msg;\n    exception_t(std::string _msg, int _code) : std::exception() {\n        msg = _msg;\n        code = _code;\n    }\n    const char *what() const throw() {\n        return msg.c_str();\n    }\n};\n\nstruct row_t {\n    std::string name;\n    long id;\n    double score;\n};\n\nclass table_t {\n  private:\n    TableColumn *column_id;\n    TableColumn *column_name;\n    TableColumn *column_score;\n\n    Table *table;\n\n  public:\n    table_t(uint32_t rows_size, float conflict_proportion = 0.2) {\n        table = Table::make(rows_size, conflict_proportion);\n        if (!table) {\n            throw exception_t(\"alloc failed\", swoole_get_last_error());\n        }\n\n        EXPECT_TRUE(table->add_column(\"id\", TableColumn::TYPE_INT, 0));\n        EXPECT_TRUE(table->add_column(\"name\", TableColumn::TYPE_STRING, 32));\n        EXPECT_TRUE(table->add_column(\"score\", TableColumn::TYPE_FLOAT, 0));\n\n        if (!table->create()) {\n            throw exception_t(\"create failed\", swoole_get_last_error());\n        }\n        column_id = table->get_column(\"id\");\n        column_name = table->get_column(\"name\");\n        column_score = table->get_column(\"score\");\n    }\n\n    bool set(const std::string &key, const row_t &value) {\n        TableRow *_rowlock = nullptr;\n        TableRow *row = table->set(key.c_str(), key.length(), &_rowlock, nullptr);\n        if (!row) {\n            _rowlock->unlock();\n            return false;\n        }\n\n        row->set_value(column_id, (void *) &value.id, sizeof(value.id));\n        row->set_value(column_name, (void *) value.name.c_str(), value.name.length());\n        row->set_value(column_score, (void *) &value.score, sizeof(value.score));\n\n        _rowlock->unlock();\n\n        return true;\n    }\n\n    row_t get(const std::string &key) {\n        row_t result;\n        TableRow *_rowlock = nullptr;\n        TableRow *row = table->get(key.c_str(), key.length(), &_rowlock);\n        if (row) {\n            memcpy(&result.id, row->data + column_id->index, sizeof(result.id));\n            memcpy(&result.score, row->data + column_score->index, sizeof(result.score));\n\n            TableStringLength l;\n            memcpy(&l, row->data + column_name->index, sizeof(l));\n            result.name = std::string(row->data + column_name->index + sizeof(l), l);\n        }\n        _rowlock->unlock();\n\n        return result;\n    }\n\n    bool del(const std::string &key) {\n        return table->del(key.c_str(), key.length());\n    }\n\n    bool exists(const std::string &key) {\n        TableRow *_rowlock = nullptr;\n        TableRow *row = table->get(key.c_str(), key.length(), &_rowlock);\n        _rowlock->unlock();\n\n        return row != nullptr;\n    }\n\n    size_t count() {\n        return table->count();\n    }\n\n    Table *ptr() {\n        return table;\n    }\n\n    ~table_t() {\n        if (table) {\n            table->destroy();\n        }\n    }\n};\n\nTEST(table, create) {\n    table_t table(1024);\n    auto ptr = table.ptr();\n\n    ASSERT_GT(ptr->get_memory_size(), ptr->get_size() * ptr->get_column_size());\n\n    ASSERT_FALSE(ptr->create());  // create again should fail\n\n    ASSERT_TRUE(table.set(\"php\", {\"php\", 1, 1.245}));\n    ASSERT_TRUE(table.set(\"java\", {\"java\", 2, 3.1415926}));\n    ASSERT_TRUE(table.set(\"c++\", {\"c++\", 3, 4.888}));\n\n    ASSERT_EQ(table.count(), 3);\n\n    row_t r1 = table.get(\"java\");\n    ASSERT_EQ(r1.id, 2);\n    ASSERT_EQ(r1.score, 3.1415926);\n    ASSERT_EQ(r1.name, std::string(\"java\"));\n\n    ASSERT_FALSE(ptr->get_column(\"not-exists\"));\n\n    ASSERT_TRUE(table.exists(\"php\"));\n    ASSERT_TRUE(table.del(\"php\"));\n    ASSERT_FALSE(table.exists(\"php\"));\n\n    ASSERT_FALSE(table.del(\"not-exists\"));\n\n    // Test with a string that is longer than the column size\n    ASSERT_TRUE(table.set(\"golang\", {\"golang \" TEST_JPG_MD5SUM TEST_JPG_MD5SUM, 3, 4.888}));\n}\n\nvoid start_iterator(Table *_ptr) {\n    _ptr->rewind();\n    auto count = 0;\n    while (true) {\n        _ptr->forward();\n        auto row = _ptr->current();\n        if (row->key_len == 0) {\n            break;\n        }\n        ASSERT_TRUE(_ptr->exists(row->key, row->key_len));\n        count++;\n    }\n    ASSERT_EQ(count, _ptr->count());\n}\n\nTEST(table, iterator) {\n    table_t table(1024);\n\n    table.set(\"php\", {\"php\", 1, 1.245});\n    table.set(\"java\", {\"java\", 2, 3.1415926});\n    table.set(\"c++\", {\"c++\", 3, 4.888});\n\n    auto _ptr = table.ptr();\n    start_iterator(_ptr);\n}\n\nTEST(table, iterator_2) {\n    table_t table(1024);\n    auto _ptr = table.ptr();\n    _ptr->set_hash_func([](const char *key, size_t len) -> uint64_t { return 1; });\n\n    table.set(\"php\", {\"php\", 1, 1.245});\n    table.set(\"java\", {\"java\", 2, 3.1415926});\n    table.set(\"c++\", {\"c++\", 3, 4.888});\n\n    start_iterator(_ptr);\n}\n\nstatic int test_table_size = 128;\n\nstatic void create_table(table_t &table) {\n    auto ptr = table.ptr();\n    ptr->set_hash_func([](const char *key, size_t len) -> uint64_t { return 1; });\n\n    ASSERT_TRUE(table.set(\"php\", {\"php\", 1, 1.245}));\n    ASSERT_TRUE(table.set(\"java\", {\"java\", 2, 3.1415926}));\n    ASSERT_TRUE(table.set(\"c++\", {\"c++\", 3, 4.888}));\n    ASSERT_TRUE(table.set(\"js\", {\"js\", 9, 6565}));\n    ASSERT_TRUE(table.set(\"golang\", {\"golang\", 4, 9.888}));\n}\n\nTEST(table, conflict1) {\n    table_t table(test_table_size);\n    ASSERT_FALSE(table.exists(\"swift\"));\n\n    create_table(table);\n    auto ptr = table.ptr();\n\n    ASSERT_FALSE(table.exists(\"kotlin\"));\n\n    ASSERT_TRUE(table.del(\"php\"));\n    ASSERT_FALSE(table.exists(\"php\"));\n    ASSERT_TRUE(table.set(\"rust\", {\"rust\", 5, 9.888}));\n\n    ASSERT_TRUE(table.del(\"golang\"));\n    ASSERT_FALSE(table.exists(\"golang\"));\n    ASSERT_TRUE(table.set(\"erlang\", {\"erlang\", 6, 12.888}));\n\n    ASSERT_TRUE(table.del(\"java\"));\n    ASSERT_FALSE(table.exists(\"java\"));\n\n    ASSERT_EQ(ptr->get_total_slice_num() - ptr->get_available_slice_num(), table.count() - 1);\n}\n\nTEST(table, conflict2) {\n    table_t table(test_table_size);\n    create_table(table);\n    auto ptr = table.ptr();\n\n    ASSERT_TRUE(table.del(\"java\"));\n    ASSERT_FALSE(table.exists(\"java\"));\n    ASSERT_TRUE(table.set(\"rust\", {\"rust\", 5, 9.888}));\n\n    ASSERT_TRUE(table.del(\"golang\"));\n    ASSERT_FALSE(table.exists(\"golang\"));\n    ASSERT_TRUE(table.set(\"erlang\", {\"erlang\", 6, 12.888}));\n\n    ASSERT_EQ(ptr->get_total_slice_num() - ptr->get_available_slice_num(), table.count() - 1);\n}\n\nTEST(table, conflict3) {\n    table_t table(test_table_size);\n    create_table(table);\n    auto ptr = table.ptr();\n\n    ASSERT_TRUE(table.del(\"golang\"));\n    ASSERT_TRUE(table.set(\"erlang\", {\"erlang\", 6, 12.888}));\n\n    ASSERT_TRUE(table.del(\"java\"));\n\n    ASSERT_EQ(ptr->get_total_slice_num() - ptr->get_available_slice_num(), table.count() - 1);\n}\n\nTEST(table, conflict4) {\n    table_t table(test_table_size);\n    create_table(table);\n    auto ptr = table.ptr();\n\n    ASSERT_TRUE(table.del(\"c++\"));\n    ASSERT_TRUE(table.set(\"rust\", {\"rust\", 5, 9.888}));\n\n    ASSERT_TRUE(table.del(\"golang\"));\n    ASSERT_TRUE(table.set(\"erlang\", {\"erlang\", 6, 12.888}));\n\n    ASSERT_TRUE(table.del(\"java\"));\n\n    ASSERT_EQ(ptr->get_total_slice_num() - ptr->get_available_slice_num(), table.count() - 1);\n}\n\nTEST(table, get_value) {\n    table_t table(test_table_size);\n    create_table(table);\n    auto ptr = table.ptr();\n\n    std::string key(\"php\");\n    TableRow *_rowlock = nullptr;\n    TableRow *row = ptr->get(key.c_str(), key.length(), &_rowlock);\n    _rowlock->unlock();\n    TableColumn *column_id = ptr->get_column(\"id\");\n    TableColumn *column_name = ptr->get_column(\"name\");\n    TableColumn *column_score = ptr->get_column(\"score\");\n\n    char *str = nullptr;\n    TableStringLength len = 0;\n    row->get_value(column_name, &str, &len);\n    ASSERT_STREQ(str, \"php\");\n\n    double dval = 0;\n    row->get_value(column_score, &dval);\n    ASSERT_EQ(dval, 1.245);\n\n    long lval = 0;\n    row->get_value(column_id, &lval);\n    ASSERT_EQ(lval, 1);\n\n    column_id->clear(row);\n    column_name->clear(row);\n    column_score->clear(row);\n\n    row->get_value(column_name, &str, &len);\n    ASSERT_STREQ(str, \"php\");\n\n    row->get_value(column_score, &dval);\n    ASSERT_EQ(dval, 0);\n\n    row->get_value(column_id, &lval);\n    ASSERT_EQ(lval, 0);\n}\n\nTEST(table, lock) {\n    table_t table(test_table_size);\n    create_table(table);\n    auto ptr = table.ptr();\n\n    std::string key(\"php\");\n    TableRow *_rowlock = nullptr;\n\n    for (int i = 0; i <= 3; i++) {\n        std::thread t([&]() {\n            TableRow *row = ptr->get(key.c_str(), key.length(), &_rowlock);\n            TableColumn *column_name = ptr->get_column(\"name\");\n            char *str = nullptr;\n            TableStringLength len = 0;\n            row->get_value(column_name, &str, &len);\n            ASSERT_STREQ(str, \"php\");\n        });\n        t.join();\n    }\n    _rowlock->unlock();\n}\n\nTEST(table, size_limit) {\n    auto t1 = Table::make(0x90000000, 1.2);\n    ASSERT_EQ(t1->get_size(), SW_TABLE_MAX_ROW_SIZE);\n    ASSERT_EQ(t1->get_conflict_proportion(), 1.0);\n\n    EXPECT_FALSE(t1->add_column(\"bad_field\", (TableColumn::Type) 8, 0));\n\n    auto t2 = Table::make(1024, 0.1);\n    ASSERT_EQ(t2->get_size(), 1024);\n    ASSERT_EQ(t2->get_conflict_proportion(), (float) SW_TABLE_CONFLICT_PROPORTION);\n}\n\nTEST(table, lock_crash) {\n    table_t table(test_table_size);\n    create_table(table);\n    auto ptr = table.ptr();\n\n    auto child = test::spawn_exec([ptr]() {\n        TableRow *_rowlock = nullptr;\n        ptr->get(\"java\", 4, &_rowlock);\n        usleep(5);\n        exit(200);  // Simulate a crash in the child process, no release lock\n    });\n    ASSERT_GT(child, 0);\n    test::wait_all_child_processes();\n\n    TableRow *_rowlock = nullptr;\n    ASSERT_NE(ptr->get(\"java\", 4, &_rowlock), nullptr);\n    _rowlock->unlock();\n}\n\nTEST(table, lock_race) {\n    table_t table(test_table_size);\n    create_table(table);\n    auto ptr = table.ptr();\n\n    auto child = test::spawn_exec([ptr]() {\n        TableRow *_rowlock = nullptr;\n        ASSERT_NE(ptr->get(\"java\", 4, &_rowlock), nullptr);\n        usleep(5);\n        _rowlock->unlock();\n    });\n    ASSERT_GT(child, 0);\n\n    TableRow *_rowlock = nullptr;\n    ASSERT_NE(ptr->get(\"java\", 4, &_rowlock), nullptr);\n    _rowlock->unlock();\n\n    test::wait_all_child_processes();\n}"
  },
  {
    "path": "core-tests/src/network/address.cpp",
    "content": "/*\n+----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n\nusing swoole::network::Address;\n\nTEST(address, basic) {\n    Address address{};\n    ASSERT_TRUE(address.empty());\n    ASSERT_TRUE(address.assign(SW_SOCK_TCP, TEST_DOMAIN_BAIDU, 80, true));\n    address.set_port(443);\n    ASSERT_EQ(address.get_port(), 443);\n\n    ASSERT_TRUE(address.assign(SW_SOCK_TCP6, TEST_HTTP_DOMAIN, 80, true));\n    address.set_port(9501);\n    ASSERT_EQ(address.get_port(), 9501);\n}\n\nTEST(address, dns_fail) {\n    Address address{};\n    ASSERT_FALSE(address.assign(SW_SOCK_TCP, TEST_DOMAIN_BAIDU \"not-exists\", 80, true));\n    ASSERT_ERREQ(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n}\n\nTEST(address, path_to_long) {\n    Address address{};\n    swoole::String path;\n    path.repeat(\"HELLO\", 5, 128);\n    ASSERT_FALSE(address.assign(SW_SOCK_UNIX_DGRAM, path.to_std_string()));\n    ASSERT_ERREQ(SW_ERROR_NAME_TOO_LONG);\n}\n\nTEST(address, bad_type) {\n    Address address{};\n    ASSERT_FALSE(address.assign((swSocketType) (SW_SOCK_RAW6 + 9), TEST_DOMAIN_BAIDU));\n    ASSERT_ERREQ(SW_ERROR_BAD_SOCKET_TYPE);\n}\n\nTEST(address, type_str) {\n    ASSERT_STREQ(Address::type_str(SW_SOCK_TCP), \"IPv4\");\n    ASSERT_STREQ(Address::type_str(SW_SOCK_UNIX_STREAM), \"UnixSocket\");\n    ASSERT_STREQ(Address::type_str(SW_SOCK_TCP6), \"IPv6\");\n    ASSERT_STREQ(Address::type_str((swSocketType) (SW_SOCK_RAW6 + 9)), \"Unknown\");\n}\n\nTEST(address, is_loopback_addr) {\n    Address address{};\n    ASSERT_TRUE(address.assign(SW_SOCK_TCP, TEST_DOMAIN_BAIDU, 80, true));\n    ASSERT_FALSE(address.is_loopback_addr());\n\n    ASSERT_TRUE(address.assign(SW_SOCK_TCP, TEST_HOST, 80, true));\n    ASSERT_TRUE(address.is_loopback_addr());\n\n    ASSERT_TRUE(address.assign(SW_SOCK_TCP6, \"::1\", 80, true));\n    ASSERT_TRUE(address.is_loopback_addr());\n\n    ASSERT_TRUE(address.assign(SW_SOCK_TCP6, TEST_HTTP_DOMAIN, 443, true));\n    ASSERT_FALSE(address.is_loopback_addr());\n\n    ASSERT_TRUE(address.assign(SW_SOCK_UNIX_DGRAM, TEST_LOG_FILE));\n    ASSERT_FALSE(address.is_loopback_addr());\n}\n\nTEST(address, ipv4_addr) {\n    auto sock = swoole::make_socket(SW_SOCK_TCP, SW_FD_STREAM, 0);\n    Address addr;\n\n    ASSERT_TRUE(addr.assign(\"tcp://127.0.0.1:12345\"));\n    ASSERT_EQ(sock->connect(addr), SW_ERR);\n    ASSERT_EQ(errno, ECONNREFUSED);\n\n    ASSERT_TRUE(addr.assign(\"tcp://localhost:12345\"));\n    ASSERT_EQ(sock->connect(addr), SW_ERR);\n    ASSERT_EQ(errno, ECONNREFUSED);\n\n    sock->free();\n}\n\nTEST(address, ipv6_addr) {\n    auto sock = swoole::make_socket(SW_SOCK_TCP6, SW_FD_STREAM, 0);\n    Address addr;\n\n    ASSERT_TRUE(addr.assign(\"tcp://[::1]:12345\"));\n    ASSERT_EQ(sock->connect(addr), SW_ERR);\n    ASSERT_EQ(errno, ECONNREFUSED);\n\n    ASSERT_TRUE(addr.assign(\"tcp://[ip6-localhost]:12345\"));\n    ASSERT_EQ(sock->connect(addr), SW_ERR);\n    ASSERT_EQ(errno, ECONNREFUSED);\n\n    sock->free();\n}\n\nTEST(address, unix_addr) {\n    auto sock = swoole::make_socket(SW_SOCK_UNIX_STREAM, SW_FD_STREAM, 0);\n    Address addr;\n    ASSERT_TRUE(addr.assign(\"unix:///tmp/swoole-not-exists.sock\"));\n    ASSERT_EQ(sock->connect(addr), SW_ERR);\n    ASSERT_EQ(errno, ENOENT);\n    sock->free();\n}\n\nTEST(address, bad_addr) {\n    Address addr;\n    ASSERT_FALSE(addr.assign(\"test://[::1]:12345\"));\n    ASSERT_EQ(swoole_get_last_error(), SW_ERROR_BAD_HOST_ADDR);\n\n    uchar buf[16];\n    ASSERT_EQ(Address::addr_str(AF_INET6 + 9, buf), nullptr);\n    ASSERT_EQ(errno, EAFNOSUPPORT);\n}\n\nTEST(address, bad_port) {\n    Address addr;\n    ASSERT_FALSE(addr.assign(\"tcp://[::1]:92345\"));\n    ASSERT_EQ(swoole_get_last_error(), SW_ERROR_BAD_PORT);\n}\n\nTEST(address, loopback_addr) {\n    Address addr1;\n    addr1.assign(SW_SOCK_TCP, \"127.0.0.1\", 0);\n    ASSERT_TRUE(addr1.is_loopback_addr());\n\n    Address addr2;\n    addr2.assign(SW_SOCK_TCP6, \"::1\", 0);\n    ASSERT_TRUE(addr1.is_loopback_addr());\n\n    Address addr3;\n    addr3.assign(SW_SOCK_TCP, \"192.168.1.2\", 0);\n    ASSERT_FALSE(addr3.is_loopback_addr());\n\n    Address addr4;\n    addr4.assign(SW_SOCK_TCP6, \"192::66::88\", 0);\n    ASSERT_FALSE(addr4.is_loopback_addr());\n}\n"
  },
  {
    "path": "core-tests/src/network/client.cpp",
    "content": "#include \"test_core.h\"\n#include \"test_server.h\"\n#include \"test_process.h\"\n#include \"core-tests/include/test_core.h\"\n\n#include <random>\n#include <iomanip>\n#include <sstream>\n\n#define GREETER \"Hello Swoole\"\n#define GREETER_SIZE sizeof(GREETER)\n\nusing swoole::HttpProxy;\nusing swoole::Mutex;\nusing swoole::Pipe;\nusing swoole::Socks5Proxy;\nusing swoole::String;\nusing swoole::network::Address;\nusing swoole::network::AsyncClient;\nusing swoole::network::Client;\nusing swoole::network::Socket;\nusing swoole::network::SyncClient;\nusing swoole::test::Process;\nusing swoole::test::Server;\n\nTEST(client, tcp) {\n    int ret;\n    char buf[128];\n\n    pid_t pid;\n    int port = swoole::test::get_random_port();\n\n    Process proc([port](Process *proc) {\n        Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_TCP);\n        serv.on(\"Receive\", [](ON_RECEIVE_PARAMS) {\n            SERVER_THIS->send(req->info.fd, req->data, req->info.len);\n            return 0;\n        });\n        serv.start();\n    });\n\n    pid = proc.start();\n\n    usleep(300000);  // wait for the test server to start\n\n    Client cli(SW_SOCK_TCP, false);\n    ASSERT_NE(cli.socket, nullptr);\n    ret = cli.connect(TEST_HOST, port, -1, 0);\n    ASSERT_EQ(ret, 0);\n    ret = cli.send(SW_STRS(GREETER), 0);\n    ASSERT_GT(ret, 0);\n    ret = cli.recv(buf, 128, 0);\n    ASSERT_EQ(ret, GREETER_SIZE);\n    ASSERT_STREQ(GREETER, buf);\n\n    Address peer_name;\n    ASSERT_EQ(cli.get_peer_name(&peer_name), 0);\n    ASSERT_STREQ(peer_name.get_addr(), \"127.0.0.1\");\n    ASSERT_EQ(peer_name.get_port(), port);\n\n    ASSERT_EQ(cli.close(), SW_OK);\n    ASSERT_EQ(cli.close(), SW_ERR);\n\n    kill(pid, SIGTERM);\n    int status;\n    wait(&status);\n}\n\nstatic void test_sync_client_dgram(const char *host, int port, enum swSocketType type) {\n    int ret;\n    char buf[128];\n    pid_t pid;\n\n    Mutex *lock = new Mutex(true);\n    lock->lock();\n\n    Process proc([&](Process *proc) {\n        Server serv(host, port, swoole::Server::MODE_BASE, type);\n        serv.on(\"Packet\", [](ON_PACKET_PARAMS) -> int {\n            swoole::DgramPacket *packet = (swoole::DgramPacket *) req->data;\n            SERVER_THIS->sendto(packet->socket_addr, packet->data, packet->length, req->info.server_fd);\n            return 0;\n        });\n        serv.on(\"Start\", [lock](ON_START_PARAMS) { lock->unlock(); });\n        serv.start();\n    });\n\n    pid = proc.start();\n\n    lock->lock();\n\n    Client cli(type, false);\n    ASSERT_NE(cli.socket, nullptr);\n    ret = cli.connect(host, port, -1, 0);\n    ASSERT_EQ(ret, 0);\n    ret = cli.send(SW_STRS(GREETER), 0);\n    ASSERT_GT(ret, 0);\n    ret = cli.recv(buf, 128, 0);\n    ASSERT_EQ(ret, GREETER_SIZE);\n    ASSERT_STREQ(GREETER, buf);\n\n    kill(pid, SIGTERM);\n    int status;\n    wait(&status);\n}\n\nTEST(client, udp) {\n    int port = swoole::test::get_random_port();\n    test_sync_client_dgram(\"127.0.0.1\", port, SW_SOCK_UDP);\n}\n\nTEST(client, udp6) {\n    int port = swoole::test::get_random_port();\n    test_sync_client_dgram(\"::1\", port, SW_SOCK_UDP6);\n}\n\nTEST(client, udg) {\n    test_sync_client_dgram(\"/tmp/swoole_core_tests.sock\", 0, SW_SOCK_UNIX_DGRAM);\n}\n\nstatic void test_async_client_tcp(const char *host, int port, enum swSocketType type) {\n    pid_t pid;\n    Pipe p(true);\n    ASSERT_TRUE(p.ready());\n\n    Process proc([&](Process *proc) {\n        Server serv(Socket::is_inet6(type) ? TEST_HOST6 : TEST_HOST, port, swoole::Server::MODE_BASE, type);\n\n        serv.set_private_data(\"pipe\", &p);\n\n        serv.on(\"Receive\", [](ON_RECEIVE_PARAMS) {\n            SERVER_THIS->send(req->info.fd, req->data, req->info.len);\n            return 0;\n        });\n\n        serv.on(\"WorkerStart\", [](ON_WORKER_START_PARAMS) {\n            Pipe *p = (Pipe *) SERVER_THIS->get_private_data(\"pipe\");\n            int64_t value = 1;\n            p->write(&value, sizeof(value));\n        });\n\n        serv.start();\n    });\n\n    pid = proc.start();\n    int64_t value;\n    p.set_timeout(10);\n    p.read(&value, sizeof(value));\n\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    AsyncClient ac(type);\n\n    ac.on_connect([](AsyncClient *ac) { ac->send(SW_STRS(GREETER)); });\n\n    ac.on_close([](AsyncClient *ac) {});\n    ac.on_error([](AsyncClient *ac) {});\n\n    ac.on_receive([](AsyncClient *ac, const char *data, size_t len) {\n        ASSERT_EQ(len, GREETER_SIZE);\n        ASSERT_STREQ(GREETER, data);\n        ac->close();\n    });\n\n    bool retval = ac.connect(host, port, 1.0);\n    EXPECT_TRUE(retval);\n\n    swoole_event_wait();\n\n    kill(pid, SIGTERM);\n    int status;\n    wait(&status);\n}\n\nTEST(client, async_tcp) {\n    test_async_client_tcp(TEST_HOST, swoole::test::get_random_port(), SW_SOCK_TCP);\n}\n\nTEST(client, async_tcp_dns) {\n    test_async_client_tcp(\"localhost\", swoole::test::get_random_port(), SW_SOCK_TCP);\n}\n\nTEST(client, async_tcp6) {\n    test_async_client_tcp(\"::1\", swoole::test::get_random_port(), SW_SOCK_TCP6);\n}\n\nTEST(client, async_tcp6_dns) {\n    test_async_client_tcp(\"localhost\", swoole::test::get_random_port(), SW_SOCK_TCP6);\n}\n\nTEST(client, async_tcp_dns_fail) {\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    Client ac(SW_SOCK_TCP, true);\n\n    ASSERT_EQ(ac.connect(TEST_HOST, 9999), SW_ERR);\n\n    bool success = true;\n\n    ac.onConnect = [&success](Client *ac) {\n        ac->send(SW_STRS(GREETER));\n        success = true;\n    };\n\n    ac.onClose = [](Client *ac) {};\n\n    ac.onError = [&success](Client *ac) {\n        DEBUG() << \"connect failed, ERROR: \" << errno << \"\\n\";\n        ASSERT_ERREQ(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n        success = false;\n    };\n\n    ac.onReceive = [](Client *ac, const char *data, size_t len) {\n        ASSERT_EQ(len, GREETER_SIZE);\n        ASSERT_STREQ(GREETER, data);\n        ac->close();\n    };\n\n    ASSERT_EQ(ac.connect(\"www.baidu.com-not-found\", 80, 1.0), SW_OK);\n\n    swoole_event_wait();\n\n    ASSERT_FALSE(success);\n}\n\nTEST(client, async_tcp_ssl_handshake_fail) {\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    Client ac(SW_SOCK_TCP, true);\n\n    bool success = true;\n\n    ac.onConnect = [&success](Client *ac) {\n        ac->send(SW_STRS(GREETER));\n        success = true;\n    };\n\n    ac.onClose = [](Client *ac) {};\n\n    ac.onError = [&success](Client *ac) {\n        DEBUG() << \"connect failed, ERROR: \" << errno << \"\\n\";\n        ASSERT_ERREQ(SW_ERROR_SSL_HANDSHAKE_FAILED);\n        success = false;\n    };\n\n    ac.onReceive = [](Client *ac, const char *data, size_t len) {\n        ASSERT_EQ(len, GREETER_SIZE);\n        ASSERT_STREQ(GREETER, data);\n        ac->close();\n    };\n\n    ac.enable_ssl_encrypt();\n\n    ASSERT_EQ(ac.connect(\"www.baidu.com\", 80, 1.0), SW_OK);\n\n    swoole_event_wait();\n\n    ASSERT_FALSE(success);\n}\n\nTEST(client, async_tcp_http_proxy_handshake_fail) {\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    Client ac(SW_SOCK_TCP, true);\n\n    bool success = true;\n\n    ac.onConnect = [&success](Client *ac) {\n        ac->send(SW_STRS(GREETER));\n        success = true;\n    };\n\n    ac.onClose = [](Client *ac) {};\n\n    ac.onError = [&success](Client *ac) {\n        DEBUG() << \"connect failed, ERROR: \" << errno << \"\\n\";\n        ASSERT_ERREQ(SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR);\n        success = false;\n    };\n\n    ac.onReceive = [](Client *ac, const char *data, size_t len) {\n        ASSERT_EQ(len, GREETER_SIZE);\n        ASSERT_STREQ(GREETER, data);\n        ac->close();\n    };\n\n    ac.set_http_proxy(\"www.baidu.com\", 80);\n\n    ASSERT_EQ(ac.connect(\"www.baidu.com\", 80, 1.0), SW_OK);\n\n    swoole_event_wait();\n}\n\nTEST(client, async_tcp_socks5_proxy_handshake_fail) {\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    Client ac(SW_SOCK_TCP, true);\n\n    bool success = true;\n\n    ac.onConnect = [&success](Client *ac) {\n        ac->send(SW_STRS(GREETER));\n        success = true;\n    };\n\n    ac.onClose = [](Client *ac) {};\n\n    ac.onError = [&success](Client *ac) {\n        DEBUG() << \"connect failed, ERROR: \" << errno << \"\\n\";\n        ASSERT_ERREQ(ETIMEDOUT);\n        success = false;\n    };\n\n    ac.onReceive = [](Client *ac, const char *data, size_t len) {\n        ASSERT_EQ(len, GREETER_SIZE);\n        ASSERT_STREQ(GREETER, data);\n        ac->close();\n    };\n\n    ac.set_socks5_proxy(\"www.baidu.com\", 80);\n\n    ASSERT_EQ(ac.connect(\"www.baidu.com\", 80, 1.0), SW_OK);\n\n    swoole_event_wait();\n}\n\nTEST(client, sleep) {\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    String buf(65536);\n\n    auto domain = TEST_HTTP_DOMAIN;\n\n    Client client(SW_SOCK_TCP, true);\n    client.onConnect = [&domain](Client *cli) {\n        cli->sleep();\n        swoole_timer_after(200, [cli, &domain](auto _1, auto _2) {\n            auto req = swoole::test::http_get_request(domain, \"/\");\n            cli->send(req.c_str(), req.length(), 0);\n            cli->wakeup();\n        });\n    };\n\n    client.onError = [](Client *cli) {};\n    client.onClose = [](Client *cli) {};\n    client.onReceive = [&buf](Client *cli, const char *data, size_t length) { buf.append(data, length); };\n\n    ASSERT_EQ(client.connect(domain, 80, -1, 0), 0);\n\n    swoole_event_wait();\n\n    ASSERT_TRUE(buf.contains(TEST_HTTP_EXPECT));\n}\n\nTEST(client, sleep_2) {\n    auto port = __LINE__ + TEST_PORT;\n    auto server_pid = swoole::test::spawn_exec([port]() {\n        Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_TCP);\n        serv.on(\"Receive\", [](ON_RECEIVE_PARAMS) {\n            usleep(10000);\n            return SW_OK;\n        });\n        serv.on(\"workerStart\", [](ON_WORKER_START_PARAMS) { DEBUG() << \"Worker started, PID: \" << getpid() << \"\\n\"; });\n        serv.start();\n    });\n\n    ASSERT_GT(server_pid, 0);\n\n    swoole::test::counter_init();\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    String buf(65536);\n    String wbuf(8 * 1024 * 1024);\n    wbuf.append_random_bytes(wbuf.size);\n\n    Client client(SW_SOCK_TCP, true);\n\n    client.buffer_high_watermark = 1024 * 1024;\n    client.buffer_low_watermark = 32 * 1024;\n\n    client.onBufferFull = [](Client *cli) {\n        DEBUG() << \"Buffer is full, waiting for data to be sent...\\n\";\n        swoole::test::counter_incr(0);\n    };\n\n    client.onBufferEmpty = [server_pid](Client *cli) {\n        DEBUG() << \"Buffer is empty, ready to send more data...\\n\";\n        swoole::test::counter_incr(1);\n        swoole_timer_after(200, [cli, server_pid](auto _1, auto _2) {\n            cli->close();\n            DEBUG() << \"Client closed, terminating server...\\n\";\n            kill(server_pid, SIGTERM);\n        });\n    };\n\n    client.onConnect = [&wbuf, server_pid](Client *cli) {\n        DEBUG() << \"Client connected, sending data...\\n\";\n        EXPECT_EQ(cli->send(wbuf.str, wbuf.length), wbuf.length);\n        EXPECT_EQ(cli->send(wbuf.str, wbuf.length), -1);\n        ASSERT_ERREQ(SW_ERROR_OUTPUT_BUFFER_OVERFLOW);\n        swoole_timer_after(10, [cli, server_pid](auto _1, auto _2) {\n            cli->sleep();\n            DEBUG() << \"Client is sleeping...\\n\";\n            swoole_timer_after(15, [cli](auto _1, auto _2) {\n                cli->wakeup();\n                DEBUG() << \"Client woke up, closing connection...\\n\";\n            });\n        });\n    };\n\n    client.onError = [](Client *cli) {\n        DEBUG() << \"Client error occurred, ERROR: \" << swoole_get_last_error() << \"\\n\";\n    };\n    client.onClose = [](Client *cli) { DEBUG() << \"Client connection closed.\\n\"; };\n    client.onReceive = [](Client *cli, const char *data, size_t length) {\n        DEBUG() << \"Client received data, length: \" << length << \"\\n\";\n    };\n\n    ASSERT_EQ(client.connect(TEST_HOST, port, -1, 0), 0);\n\n    swoole_event_wait();\n\n    swoole::test::wait_all_child_processes();\n\n    ASSERT_GE(swoole::test::counter_get(0), 1);\n    ASSERT_GE(swoole::test::counter_get(1), 1);\n}\n\nTEST(client, connect_refuse) {\n    int ret;\n    Client cli(SW_SOCK_TCP, false);\n    ret = cli.connect(TEST_HOST, swoole::test::get_random_port(), -1, 0);\n    ASSERT_EQ(ret, -1);\n    ASSERT_EQ(swoole_get_last_error(), ECONNREFUSED);\n}\n\nTEST(client, bind) {\n    Client cli(SW_SOCK_TCP, false);\n    ASSERT_EQ(cli.bind(\"127.0.0.1\", 9999), SW_OK);\n    ASSERT_EQ(cli.bind(\"192.0.0.1\", 9999), SW_ERR);\n    ASSERT_ERREQ(EADDRNOTAVAIL);\n    ASSERT_EQ(cli.bind(\"127.0.0.1\", 80), SW_ERR);\n    if (swoole::test::is_github_ci()) {\n        ASSERT_ERREQ(EINVAL);\n    } else {\n        ASSERT_ERREQ(EACCES);\n    }\n}\n\n// DNS 报文头部结构\nstruct DNSHeader {\n    uint16_t id;       // 标识符\n    uint16_t flags;    // 各种标志\n    uint16_t qdcount;  // 问题数量\n    uint16_t ancount;  // 回答数量\n    uint16_t nscount;  // 授权记录数量\n    uint16_t arcount;  // 附加记录数量\n};\n\n// 将域名转换为 DNS 格式\nstd::vector<uint8_t> encodeDomainName(const std::string &domain) {\n    std::vector<uint8_t> result;\n    std::string label;\n\n    for (char c : domain) {\n        if (c == '.') {\n            result.push_back(static_cast<uint8_t>(label.length()));\n            for (char lc : label) {\n                result.push_back(static_cast<uint8_t>(lc));\n            }\n            label.clear();\n        } else {\n            label += c;\n        }\n    }\n\n    // 处理最后一个标签\n    if (!label.empty()) {\n        result.push_back(static_cast<uint8_t>(label.length()));\n        for (char lc : label) {\n            result.push_back(static_cast<uint8_t>(lc));\n        }\n    }\n\n    // 添加结束符\n    result.push_back(0);\n\n    return result;\n}\n\n// 构建 DNS 查询报文\nstd::vector<uint8_t> buildDNSQuery(const std::string &domain, uint16_t recordType = 1) {\n    std::vector<uint8_t> query;\n\n    // 生成随机 ID\n    std::random_device rd;\n    std::mt19937 gen(rd());\n    std::uniform_int_distribution<uint16_t> dist(0, 65535);\n    uint16_t transactionId = dist(gen);\n\n    // 构建 DNS 头部\n    DNSHeader header;\n    header.id = htons(transactionId);  // 网络字节序\n    header.flags = htons(0x0100);      // RD=1, 其余为0\n    header.qdcount = htons(1);         // 1个问题\n    header.ancount = htons(0);         // 0个回答\n    header.nscount = htons(0);         // 0个授权记录\n    header.arcount = htons(0);         // 0个附加记录\n\n    // 将头部添加到查询报文\n    uint8_t *headerPtr = reinterpret_cast<uint8_t *>(&header);\n    query.insert(query.end(), headerPtr, headerPtr + sizeof(DNSHeader));\n\n    // 添加问题部分 - 域名\n    std::vector<uint8_t> qname = encodeDomainName(domain);\n    query.insert(query.end(), qname.begin(), qname.end());\n\n    // 添加问题部分 - 查询类型和查询类\n    uint16_t qtype = htons(recordType);  // 查询类型（如A记录=1）\n    uint16_t qclass = htons(1);          // 查询类（IN=1）\n\n    uint8_t *qtypePtr = reinterpret_cast<uint8_t *>(&qtype);\n    uint8_t *qclassPtr = reinterpret_cast<uint8_t *>(&qclass);\n\n    query.insert(query.end(), qtypePtr, qtypePtr + sizeof(uint16_t));\n    query.insert(query.end(), qclassPtr, qclassPtr + sizeof(uint16_t));\n\n    return query;\n}\n\n// 将二进制数据转换为十六进制字符串\nstd::string bytesToHexString(const std::vector<uint8_t> &data) {\n    std::stringstream ss;\n\n    for (size_t i = 0; i < data.size(); ++i) {\n        ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(data[i]);\n        if (i < data.size() - 1) {\n            ss << \" \";\n        }\n    }\n\n    return ss.str();\n}\n\nTEST(client, sendto) {\n    Client cli(SW_SOCK_TCP, false);\n    ASSERT_EQ(cli.sendto(\"127.0.0.1\", 9999, SW_STRL(TEST_STR)), SW_ERR);\n    ASSERT_ERREQ(SW_ERROR_OPERATION_NOT_SUPPORT);\n\n    auto dns_server = swoole_get_dns_server();\n    Client dsock(SW_SOCK_UDP, false);\n    auto dnsQuery = buildDNSQuery(\"www.baidu.com\");\n    ASSERT_EQ(dsock.sendto(dns_server.host, dns_server.port, (const char *) dnsQuery.data(), dnsQuery.size()), SW_OK);\n    ASSERT_GT(dsock.recv(sw_tg_buffer()->str, sw_tg_buffer()->size), 0);\n\n    Address ra;\n    ASSERT_EQ(dsock.get_peer_name(&ra), SW_OK);\n    ASSERT_STREQ(ra.get_addr(), dns_server.host.c_str());\n    ASSERT_EQ(ra.get_port(), dns_server.port);\n\n    Client cli2(SW_SOCK_UDP, false);\n    ASSERT_EQ(cli2.sendto(\"www.baidu.com-not-exists\", 9999, SW_STRL(TEST_STR)), SW_ERR);\n    ASSERT_ERREQ(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n\n    Client cli3(SW_SOCK_UNIX_DGRAM, false);\n    ASSERT_EQ(cli3.sendto(\"/tmp/swoole.sock\", 0, SW_STRL(TEST_STR)), SW_ERR);\n    ASSERT_ERREQ(ENOENT);\n}\n\nTEST(client, async_unix_connect_refuse) {\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    std::unordered_map<std::string, bool> flags;\n\n    AsyncClient ac(SW_SOCK_UNIX_DGRAM);\n\n    ac.on_connect([](AsyncClient *ac) { ac->send(SW_STRS(GREETER)); });\n\n    ac.on_close([](AsyncClient *ac) {});\n\n    ac.on_error([&](AsyncClient *ac) { flags[\"onError\"] = true; });\n\n    ac.on_receive([](AsyncClient *ac, const char *data, size_t len) {\n        ASSERT_EQ(len, GREETER_SIZE);\n        ASSERT_STREQ(GREETER, data);\n        ac->close();\n    });\n\n    bool retval = ac.connect(\"/tmp/swoole-not-exists.sock\", 0);\n\n    ASSERT_EQ(retval, false);\n    ASSERT_TRUE(flags[\"onError\"]);\n    ASSERT_EQ(errno, ENOENT);\n\n    swoole_event_wait();\n}\n\nTEST(client, async_connect_timeout) {\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    std::unordered_map<std::string, bool> flags;\n\n    AsyncClient ac(SW_SOCK_TCP);\n\n    ac.on_connect([](AsyncClient *ac) { ac->send(SW_STRS(GREETER)); });\n\n    ac.on_close([](AsyncClient *ac) {});\n\n    ac.on_error([&](AsyncClient *ac) {\n        flags[\"onError\"] = true;\n        ASSERT_EQ(swoole_get_last_error(), ETIMEDOUT);\n    });\n\n    ac.on_receive([](AsyncClient *ac, const char *data, size_t len) {\n        ASSERT_EQ(len, GREETER_SIZE);\n        ASSERT_STREQ(GREETER, data);\n        ac->close();\n    });\n\n    ASSERT_TRUE(ac.connect(\"192.168.1.199\", 19999, 0.2));\n    swoole_event_wait();\n\n    ASSERT_TRUE(flags[\"onError\"]);\n}\n\nstatic void test_async_client_dgram(const char *host, int port, enum swSocketType type) {\n    pid_t pid;\n\n    Mutex *lock = new Mutex(true);\n    lock->lock();\n\n    Process proc([&](Process *proc) {\n        Server serv(host, port, swoole::Server::MODE_BASE, type);\n        serv.on(\"Packet\", [](ON_PACKET_PARAMS) -> int {\n            swoole::DgramPacket *packet = (swoole::DgramPacket *) req->data;\n            SERVER_THIS->sendto(packet->socket_addr, packet->data, packet->length, req->info.server_fd);\n            return 0;\n        });\n        serv.on(\"Start\", [lock](ON_START_PARAMS) { lock->unlock(); });\n        serv.start();\n    });\n\n    pid = proc.start();\n\n    lock->lock();\n\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    std::unordered_map<std::string, bool> flags;\n\n    AsyncClient ac(type);\n\n    ac.on_connect([&](AsyncClient *ac) {\n        flags[\"onConnect\"] = true;\n        ac->send(SW_STRS(GREETER));\n    });\n\n    ac.on_close([&](AsyncClient *ac) { flags[\"onClose\"] = true; });\n\n    ac.on_error([&](AsyncClient *ac) {\n        flags[\"onError\"] = true;\n        ASSERT_EQ(swoole_get_last_error(), ETIMEDOUT);\n    });\n\n    ac.on_receive([&](AsyncClient *ac, const char *data, size_t len) {\n        flags[\"onReceive\"] = true;\n        ASSERT_EQ(len, GREETER_SIZE);\n        ASSERT_STREQ(GREETER, data);\n        ac->close();\n    });\n\n    ASSERT_TRUE(ac.connect(host, port, 0.2));\n    swoole_event_wait();\n\n    kill(pid, SIGTERM);\n    int status;\n    wait(&status);\n\n    ASSERT_TRUE(flags[\"onConnect\"]);\n    ASSERT_TRUE(flags[\"onReceive\"]);\n    ASSERT_TRUE(flags[\"onClose\"]);\n    ASSERT_FALSE(flags[\"onError\"]);\n}\n\nTEST(client, async_udp) {\n    test_async_client_dgram(TEST_HOST, swoole::test::get_random_port(), SW_SOCK_UDP);\n}\n\nTEST(client, async_udp_dns) {\n    test_async_client_dgram(\"localhost\", swoole::test::get_random_port(), SW_SOCK_UDP);\n}\n\nTEST(client, async_udp6) {\n    test_async_client_dgram(\"::1\", swoole::test::get_random_port(), SW_SOCK_UDP6);\n}\n\nTEST(client, connect_timeout) {\n    int ret;\n    Client cli(SW_SOCK_TCP, false);\n    ret = cli.connect(\"19.168.0.99\", swoole::test::get_random_port(), 0.2, 0);\n    ASSERT_EQ(ret, -1);\n    ASSERT_EQ(swoole_get_last_error(), ETIMEDOUT);\n}\n\nTEST(client, shutdown_write) {\n    signal(SIGPIPE, SIG_IGN);\n    int ret;\n    Client cli(SW_SOCK_TCP, false);\n    ret = cli.connect(\"www.baidu.com\", 80, -1, 0);\n    ASSERT_EQ(ret, 0);\n\n    ASSERT_EQ(cli.shutdown(SHUT_WR), 0);\n    ASSERT_EQ(cli.shutdown(SHUT_WR), SW_ERR);  // already shutdown\n\n    ssize_t retval = cli.send(SW_STRL(\"hello world\"), 0);\n    ASSERT_EQ(retval, -1);\n    ASSERT_EQ(swoole_get_last_error(), EPIPE);\n}\n\nTEST(client, shutdown_read) {\n    signal(SIGPIPE, SIG_IGN);\n    int ret;\n    Client cli(SW_SOCK_TCP, false);\n    ret = cli.connect(\"www.baidu.com\", 80, -1, 0);\n    ASSERT_EQ(ret, 0);\n\n    ASSERT_EQ(cli.shutdown(SHUT_RD), SW_OK);\n    ASSERT_EQ(cli.shutdown(SHUT_RD), SW_ERR);  // already shutdown\n\n    ssize_t retval = cli.send(SW_STRL(\"hello world\\r\\n\\r\\n\"), 0);\n    ASSERT_GT(retval, 0);\n\n    char buf[1024];\n    retval = cli.recv(buf, sizeof(buf), 0);\n    ASSERT_EQ(retval, 0);\n}\n\nTEST(client, shutdown_all) {\n    signal(SIGPIPE, SIG_IGN);\n    int ret;\n    Client cli(SW_SOCK_TCP, false);\n    ret = cli.connect(\"www.baidu.com\", 80, -1, 0);\n    ASSERT_EQ(ret, 0);\n\n    ASSERT_EQ(cli.shutdown(SHUT_RDWR), SW_OK);\n    ASSERT_EQ(cli.shutdown(SHUT_RDWR + 99), SW_ERR);\n    ASSERT_ERREQ(EINVAL);\n\n    ssize_t retval = cli.send(SW_STRL(\"hello world\\r\\n\\r\\n\"), 0);\n    ASSERT_EQ(retval, -1);\n    ASSERT_EQ(swoole_get_last_error(), EPIPE);\n\n    char buf[1024];\n    retval = cli.recv(buf, sizeof(buf), 0);\n    ASSERT_EQ(retval, 0);\n}\n\nstatic void test_ssl_http_get() {\n    bool connected = false;\n    bool closed = false;\n    String buf(65536);\n\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    Client client(SW_SOCK_TCP, true);\n\n    ASSERT_EQ(client.enable_ssl_encrypt(), SW_OK);\n    ASSERT_EQ(client.enable_ssl_encrypt(), SW_ERR);  // already enabled\n\n    client.onConnect = [&connected](Client *cli) {\n        connected = true;\n        auto req = swoole::test::http_get_request(TEST_HTTP_DOMAIN, \"/\");\n        cli->send(req.c_str(), req.length(), 0);\n    };\n\n    client.onError = [](Client *cli) {};\n    client.onClose = [&closed](Client *cli) { closed = true; };\n    client.onReceive = [&buf](Client *cli, const char *data, size_t length) { buf.append(data, length); };\n\n    ASSERT_EQ(client.connect(TEST_HTTP_DOMAIN, 443, -1, 0), 0);\n\n    swoole_event_wait();\n\n    ASSERT_TRUE(connected);\n    ASSERT_TRUE(closed);\n    ASSERT_TRUE(buf.contains(TEST_HTTPS_EXPECT));\n}\n\nTEST(client, ssl_1) {\n    test_ssl_http_get();\n}\n\nTEST(client, ssl_sendfile) {\n    bool connected = false;\n    bool closed = false;\n    String buf(65536);\n\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    auto file = swoole::make_tmpfile();\n    file.write(SW_STRL(TEST_REQUEST_BAIDU));\n\n    Client client(SW_SOCK_TCP, true);\n    client.enable_ssl_encrypt();\n    client.onConnect = [&connected, &file](Client *cli) {\n        connected = true;\n        cli->sendfile(file.get_path().c_str(), 0, file.get_size());\n    };\n\n    client.onError = [](Client *cli) {};\n    client.onClose = [&closed](Client *cli) { closed = true; };\n    client.onReceive = [&buf](Client *cli, const char *data, size_t length) { buf.append(data, length); };\n\n    ASSERT_EQ(client.connect(TEST_DOMAIN_BAIDU, 443, -1, 0), 0);\n\n    swoole_event_wait();\n\n    ASSERT_TRUE(connected);\n    ASSERT_TRUE(closed);\n    ASSERT_TRUE(buf.contains(\"Baidu\"));\n}\n\nTEST(client, sync_ssl_sendfile) {\n    auto file = swoole::make_tmpfile();\n    file.write(SW_STRL(TEST_REQUEST_BAIDU));\n\n    SyncClient client(SW_SOCK_TCP);\n    ASSERT_TRUE(client.connect(TEST_DOMAIN_BAIDU, 443, -1));\n    ASSERT_TRUE(client.enable_ssl_encrypt());\n    ASSERT_TRUE(client.sendfile(file.get_path().c_str()));\n\n    String buf(65536);\n    while (true) {\n        ssize_t nr = client.recv(buf.str, buf.size - buf.length);\n        if (nr <= 0) {\n            break;\n        }\n        buf.grow(nr);\n    }\n    client.close();\n    ASSERT_TRUE(buf.contains(\"baidu.com\"));\n    unlink(file.get_path().c_str());\n}\n\nstatic void proxy_async_test(Client &client, bool https) {\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    bool connected = false;\n    bool closed = false;\n    String buf(65536);\n\n    if (https) {\n        client.enable_ssl_encrypt();\n    }\n\n    client.onConnect = [&connected](Client *cli) {\n        connected = true;\n        cli->send(SW_STRL(TEST_REQUEST_BAIDU), 0);\n    };\n\n    client.onError = [](Client *cli) {};\n    client.onClose = [&closed](Client *cli) { closed = true; };\n    client.onReceive = [&buf](Client *cli, const char *data, size_t length) { buf.append(data, length); };\n\n    ASSERT_EQ(client.connect(TEST_DOMAIN_BAIDU, https ? 443 : 80, -1, 0), 0);\n\n    swoole_event_wait();\n\n    ASSERT_TRUE(connected);\n    ASSERT_TRUE(closed);\n    ASSERT_TRUE(buf.contains(\"www.baidu.com\"));\n}\n\nstatic void proxy_sync_test(Client &client, bool https) {\n    String buf(65536);\n    if (https) {\n        client.enable_ssl_encrypt();\n    }\n\n    std::string host = TEST_DOMAIN_BAIDU;\n    if (client.socks5_proxy && !client.socks5_proxy->dns_tunnel) {\n        host = swoole::network::gethostbyname(AF_INET, host);\n        DEBUG() << \"Resolved domain \" << TEST_DOMAIN_BAIDU << \" to \" << host << \"\\n\";\n    }\n\n    ASSERT_EQ(client.connect(host.c_str(), https ? 443 : 80, -1, 0), 0);\n    ASSERT_GT(client.send(SW_STRL(TEST_REQUEST_BAIDU), 0), 0);\n\n    while (true) {\n        char rbuf[4096];\n        auto nr = client.recv(rbuf, sizeof(rbuf), 0);\n        if (nr <= 0) {\n            break;\n        }\n        buf.append(rbuf, nr);\n    }\n\n    ASSERT_TRUE(buf.contains(\"www.baidu.com\"));\n}\n\nstatic void proxy_set_socks5_proxy(Client &client) {\n    std::string username = std::string(TEST_SOCKS5_PROXY_USER);\n    std::string password = std::string(TEST_SOCKS5_PROXY_PASSWORD);\n    client.set_socks5_proxy(TEST_SOCKS5_PROXY_HOST, TEST_SOCKS5_PROXY_PORT, username, password);\n}\n\nstatic void proxy_set_http_proxy(Client &client) {\n    std::string username, password;\n    if (swoole::test::is_github_ci()) {\n        username = std::string(TEST_HTTP_PROXY_USER);\n        password = std::string(TEST_HTTP_PROXY_PASSWORD);\n    }\n    client.set_http_proxy(TEST_HTTP_PROXY_HOST, TEST_HTTP_PROXY_PORT, username, password);\n}\n\nTEST(client, https_get_async_with_http_proxy) {\n    Client client(SW_SOCK_TCP, true);\n    proxy_set_http_proxy(client);\n    proxy_async_test(client, true);\n}\n\nTEST(client, https_get_async_with_socks5_proxy) {\n    Client client(SW_SOCK_TCP, true);\n    proxy_set_socks5_proxy(client);\n    proxy_async_test(client, true);\n}\n\nTEST(client, https_get_sync_with_http_proxy) {\n    Client client(SW_SOCK_TCP, false);\n    proxy_set_http_proxy(client);\n    proxy_sync_test(client, true);\n}\n\nTEST(client, https_get_sync_with_socks5_proxy) {\n    Client client(SW_SOCK_TCP, false);\n    proxy_set_socks5_proxy(client);\n    proxy_sync_test(client, true);\n}\n\nTEST(client, http_get_sync_with_socks5_proxy_no_dns_tunnel) {\n    Client client(SW_SOCK_TCP, false);\n    proxy_set_socks5_proxy(client);\n    client.socks5_proxy->dns_tunnel = 0;\n    proxy_sync_test(client, false);\n}\n\nTEST(client, http_get_async_with_http_proxy) {\n    Client client(SW_SOCK_TCP, true);\n    proxy_set_http_proxy(client);\n    proxy_async_test(client, false);\n}\n\nTEST(client, http_get_async_with_socks5_proxy) {\n    Client client(SW_SOCK_TCP, true);\n    proxy_set_socks5_proxy(client);\n    proxy_async_test(client, false);\n}\n\nTEST(client, http_get_sync_with_http_proxy) {\n    Client client(SW_SOCK_TCP, false);\n    proxy_set_http_proxy(client);\n    proxy_sync_test(client, false);\n}\n\nTEST(client, http_get_sync_with_socks5_proxy) {\n    Client client(SW_SOCK_TCP, false);\n    proxy_set_socks5_proxy(client);\n    proxy_sync_test(client, false);\n}\n\nTEST(client, ssl) {\n    Client client(SW_SOCK_TCP, false);\n    client.enable_ssl_encrypt();\n    client.set_tls_host_name(TEST_HTTP_DOMAIN);\n    ASSERT_EQ(client.connect(TEST_HTTP_DOMAIN, 443, -1, 0), SW_OK);\n\n    auto sock = client.socket;\n    ASSERT_TRUE(sock->ssl_get_peer_certificate(sw_tg_buffer()));\n    auto ls = sock->ssl_get_peer_cert_chain(10);\n    ASSERT_FALSE(ls.empty());\n    swoole::test::dump_cert_info(sw_tg_buffer()->str, sw_tg_buffer()->length);\n    ASSERT_EQ(client.ssl_verify(false), SW_OK);\n\n    auto req = swoole::test::http_get_request(TEST_HTTP_DOMAIN, \"/\");\n\n    constexpr off_t offset1 = TEST_WRITEV_OFFSET;\n    iovec wr_iov[2];\n    wr_iov[0].iov_base = (void *) req.c_str();\n    wr_iov[0].iov_len = offset1;\n    wr_iov[1].iov_base = (char *) req.c_str() + offset1;\n    wr_iov[1].iov_len = req.length() - offset1;\n\n    swoole::network::IOVector wr_vec(wr_iov, 2);\n    ASSERT_EQ(sock->ssl_writev(&wr_vec), req.length());\n\n    sw_tg_buffer()->clear();\n    if (sw_tg_buffer()->size < 1024 * 1024) {\n        sw_tg_buffer()->extend(1024 * 1024);\n    }\n\n    constexpr off_t offset2 = TEST_READV_OFFSET;\n    iovec rd_iov[2];\n    rd_iov[0].iov_base = sw_tg_buffer()->str;\n    rd_iov[0].iov_len = offset2;\n    rd_iov[1].iov_base = sw_tg_buffer()->str + offset2;\n    rd_iov[1].iov_len = sw_tg_buffer()->size - offset2;\n\n    swoole::network::IOVector rd_vec(rd_iov, 2);\n    auto rv = sock->ssl_readv(&rd_vec);\n    ASSERT_GT(rv, 1024);\n    sw_tg_buffer()->length = rv;\n    sw_tg_buffer()->set_null_terminated();\n\n    ASSERT_TRUE(sw_tg_buffer()->contains(TEST_HTTPS_EXPECT));\n}\n\nTEST(client, fail) {\n    Client c(static_cast<swSocketType>(SW_SOCK_RAW6 + 1), false);\n    ASSERT_FALSE(c.ready());\n    ASSERT_ERREQ(ESOCKTNOSUPPORT);\n}\n\nstatic void test_recv_timeout(Client &c) {\n    std::thread t([]() {\n        SW_LOOP_N(20) {\n            usleep(50000);\n            kill(getpid(), SIGIO);\n        }\n    });\n\n    swoole_signal_set(\n        SIGIO, [](int) { swoole::test::counter_incr(0); }, 0, 1);\n\n    auto buf = sw_tg_buffer();\n    while (true) {\n        auto rv = c.recv(buf->str, buf->size);\n        DEBUG() << \"rv: \" << rv << \", error=\" << errno << \"\\n\";\n        if (c.has_timedout()) {\n            break;\n        }\n    }\n\n    t.join();\n}\n\nTEST(client, recv_timeout) {\n    Client c(SW_SOCK_TCP, false);\n    ASSERT_TRUE(c.ready());\n    ASSERT_EQ(c.connect(TEST_HTTP_DOMAIN, 80, 1.0), SW_OK);\n    test_recv_timeout(c);\n}\n\nTEST(client, ssl_recv_timeout) {\n    Client c(SW_SOCK_TCP, false);\n    ASSERT_TRUE(c.ready());\n    c.enable_ssl_encrypt();\n\n    ASSERT_EQ(c.connect(TEST_HTTP_DOMAIN, 443, 1.0), SW_OK);\n    test_recv_timeout(c);\n}\n\nTEST(client, sync_nonblock) {\n    Client cli(SW_SOCK_TCP, false);\n    ASSERT_NE(cli.socket, nullptr);\n    auto rv = cli.connect(TEST_HTTP_DOMAIN, 80, -1, 1);\n    ASSERT_EQ(rv, -1);\n    ASSERT_EQ(errno, EINPROGRESS);\n    ASSERT_TRUE(cli.async_connect);\n}\n"
  },
  {
    "path": "core-tests/src/network/dns.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_coroutine.h\"\n\n#include \"swoole_socket.h\"\n\n#include \"swoole_util.h\"\n\nusing namespace swoole;\nusing swoole::coroutine::Socket;\nusing swoole::coroutine::System;\nusing namespace swoole::test;\n\nTEST(dns, lookup1) {\n    test::coroutine::run([](void *arg) {\n        auto list = swoole::coroutine::dns_lookup(\"www.baidu.com\", AF_INET, 10);\n        ASSERT_GE(list.size(), 1);\n    });\n}\n\nTEST(dns, lookup_ipv6) {\n    test::coroutine::run([](void *arg) {\n        auto list = swoole::coroutine::dns_lookup(\"www.google.com\", AF_INET6, 2);\n        ASSERT_GE(list.size(), 1);\n    });\n}\n\nTEST(dns, domain_not_found) {\n    test::coroutine::run([](void *arg) {\n        auto list = swoole::coroutine::dns_lookup(\"www.baidu.com-not-found\", AF_INET, 2);\n        ASSERT_EQ(list.size(), 0);\n        ASSERT_EQ(swoole_get_last_error(), SW_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n    });\n}\n\nTEST(dns, bad_family) {\n    test::coroutine::run([](void *arg) {\n        auto list = swoole::coroutine::dns_lookup(\"www.google.com\", 9999, 2);\n        ASSERT_GE(list.size(), 1);\n    });\n}\n\nTEST(dns, cancel) {\n    // swoole_set_trace_flags(SW_TRACE_CARES);\n    // swoole_set_log_level(SW_LOG_TRACE);\n    test::coroutine::run([](void *arg) {\n        auto co = Coroutine::get_current_safe();\n        Coroutine::create([co](void *) {\n            System::sleep(0.001);\n            co->cancel();\n        });\n        auto list1 = swoole::coroutine::dns_lookup(\"www.baidu-not-found-for-cancel.com\", AF_INET, 2);\n        ASSERT_EQ(list1.size(), 0);\n        ASSERT_EQ(swoole_get_last_error(), SW_ERROR_CO_CANCELED);\n    });\n}\n\nTEST(dns, gethostbyname) {\n    GethostbynameRequest req1(TEST_HTTP_DOMAIN, AF_INET);\n    ASSERT_EQ(network::gethostbyname(&req1), 0);\n    ASSERT_TRUE(network::Address::verify_ip(AF_INET, req1.addr));\n\n    GethostbynameRequest req2(TEST_HTTP_DOMAIN, AF_INET6);\n    ASSERT_EQ(network::gethostbyname(&req2), 0);\n    ASSERT_TRUE(network::Address::verify_ip(AF_INET6, req2.addr));\n}\n\nTEST(dns, getaddrinfo) {\n    GetaddrinfoRequest req(\"www.baidu.com\", AF_INET, SOCK_STREAM, 0, \"\");\n    ASSERT_EQ(network::getaddrinfo(&req), 0);\n    ASSERT_GT(req.count, 0);\n\n    std::vector<std::string> ip_list;\n    req.parse_result(ip_list);\n\n    for (auto &ip : ip_list) {\n        ASSERT_TRUE(network::Address::verify_ip(AF_INET, ip));\n    }\n}\n\nTEST(dns, getaddrinfo_fail) {\n    GetaddrinfoRequest req(\"www.baidu.com-not-exists\", AF_INET, SOCK_STREAM, 0, \"\");\n    ASSERT_EQ(network::getaddrinfo(&req), -1);\n    ASSERT_EQ(req.error, EAI_NONAME);\n}\n\nTEST(dns, getaddrinfo_ipv6) {\n    GetaddrinfoRequest req(TEST_HTTP_DOMAIN, AF_INET6, SOCK_STREAM, 0, \"\");\n    ASSERT_EQ(network::getaddrinfo(&req), 0);\n    ASSERT_GT(req.count, 0);\n\n    DEBUG() << \"result count: \" << req.count << std::endl;\n\n    std::vector<std::string> ip_list;\n    req.parse_result(ip_list);\n\n    for (auto &ip : ip_list) {\n        ASSERT_TRUE(network::Address::verify_ip(AF_INET6, ip));\n    }\n}\n\nTEST(dns, load_resolv_conf) {\n    int port = get_random_port();\n\n    auto ori_dns_server = swoole_get_dns_server();\n\n    // with port\n    std::string test_server = \"127.0.0.1:\" + std::to_string(port);  // fake dns server\n    swoole_set_dns_server(test_server);\n    auto dns_server = swoole_get_dns_server();\n    ASSERT_STREQ(dns_server.host.c_str(), \"127.0.0.1\");\n    ASSERT_EQ(dns_server.port, port);\n\n    // invalid port\n    test_server = \"127.0.0.1:808088\";\n    swoole_set_dns_server(test_server);\n    dns_server = swoole_get_dns_server();\n    ASSERT_EQ(dns_server.port, SW_DNS_SERVER_PORT);\n\n    ASSERT_TRUE(swoole_load_resolv_conf());\n    dns_server = swoole_get_dns_server();\n    ASSERT_EQ(dns_server.host, ori_dns_server.host);\n    ASSERT_EQ(dns_server.port, ori_dns_server.port);\n}\n\nTEST(dns, gethosts) {\n    char hosts_file[] = \"/tmp/swoole_hosts\";\n    std::ofstream file(hosts_file);\n    if (!file.is_open()) {\n        std::cout << std::string(\"file open failed: \") + std::string(strerror(errno)) << std::endl;\n        throw strerror(errno);\n    }\n\n    ON_SCOPE_EXIT {\n        unlink(hosts_file);\n    };\n\n    file << \"\\n\";\n    file << \"127.0.0.1\\n\";\n    file << \"127.0.0.1 localhost\\n\";\n    file << \"# 127.0.0.1 aaa.com\\n\";\n    file << \"       127.0.0.1 bbb.com               ccc.com      #ddd.com\\n\";\n    file.close();\n\n    swoole_set_hosts_path(hosts_file);\n\n    std::string ip = swoole::coroutine::get_ip_by_hosts(\"localhost\");\n    ASSERT_EQ(ip, \"127.0.0.1\");\n\n    ip = swoole::coroutine::get_ip_by_hosts(\"aaa.com\");\n    ASSERT_EQ(ip, \"\");\n\n    ip = swoole::coroutine::get_ip_by_hosts(\"bbb.com\");\n    ASSERT_EQ(ip, \"127.0.0.1\");\n\n    ip = swoole::coroutine::get_ip_by_hosts(\"ccc.com\");\n    ASSERT_EQ(ip, \"127.0.0.1\");\n\n    ip = swoole::coroutine::get_ip_by_hosts(\"ddd.com\");\n    ASSERT_EQ(ip, \"\");\n\n    ip = swoole::coroutine::get_ip_by_hosts(\"non.exist.com\");\n    ASSERT_EQ(ip, \"\");\n}\n\nvoid name_resolver_test_fn_1() {\n    NameResolver::Context ctx{};\n    ctx.type = AF_INET;\n    ctx.timeout = 1;\n    ASSERT_EQ(\"127.0.0.1\", swoole_name_resolver_lookup(\"localhost\", &ctx));\n}\n\nvoid name_resolver_test_fn_2() {\n    NameResolver::Context ctx;\n    std::string domain = \"non.exist.com\";\n    NameResolver nr{[](const std::string &domain, NameResolver::Context *ctx, void *) -> std::string {\n                        if (domain == \"name1\") {\n                            return \"127.0.0.2\";\n                        } else if (domain == \"www.baidu.com\") {\n                            ctx->final_ = true;\n                            return \"\";\n                        }\n                        return \"\";\n                    },\n                    nullptr,\n                    NameResolver::TYPE_USER};\n\n    swoole_name_resolver_add(nr);\n\n    ctx = {AF_INET};\n    ASSERT_EQ(\"127.0.0.2\", swoole_name_resolver_lookup(\"name1\", &ctx));\n\n    ctx = {AF_INET};\n    ASSERT_EQ(\"\", swoole_name_resolver_lookup(\"www.baidu.com\", &ctx));\n\n    ctx = {AF_INET};\n    ASSERT_EQ(\"127.0.0.1\", swoole_name_resolver_lookup(\"localhost\", &ctx));\n\n    swoole_name_resolver_each([](const std::list<NameResolver>::iterator &iter) -> swTraverseOperation {\n        if (iter->type == NameResolver::TYPE_USER) {\n            return SW_TRAVERSE_REMOVE;\n        } else {\n            return SW_TRAVERSE_KEEP;\n        }\n    });\n\n    ctx = {AF_INET};\n    auto ip = swoole_name_resolver_lookup(\"www.baidu.com\", &ctx);\n    ASSERT_TRUE(swoole::network::Address::verify_ip(AF_INET, ip));\n}\n\nTEST(dns, name_resolve_1) {\n    name_resolver_test_fn_1();\n    test::coroutine::run([](void *arg) { name_resolver_test_fn_1(); });\n}\n\nTEST(dns, name_resolve_2) {\n    name_resolver_test_fn_2();\n    test::coroutine::run([](void *arg) { name_resolver_test_fn_2(); });\n}\n\nTEST(dns, name_resolve_fail) {\n    NameResolver::Context ctx;\n    ctx = {AF_INET};\n    auto ip = swoole_name_resolver_lookup(\"www.baidu.com-not-exists\", &ctx);\n    ASSERT_TRUE(ip.empty());\n    ASSERT_ERREQ(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n}\n"
  },
  {
    "path": "core-tests/src/network/socket.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"swoole_file.h\"\n\nusing namespace std;\nusing namespace swoole;\n\nconst char test_data[] = \"hello swoole, hello world, php is best\";\n\nTEST(socket, connect_sync) {\n    network::Address sa;\n    network::Socket *sock;\n\n    sock = make_socket(SW_SOCK_UNIX_STREAM, SW_FD_STREAM, 0);\n    ASSERT_NE(sock, nullptr);\n    sa.assign(SW_SOCK_UNIX_STREAM, \"/tmp/swole-not-exists.sock\");\n    sock->set_timeout(0.3, SW_TIMEOUT_CONNECT);\n    ASSERT_EQ(sock->connect_sync(sa), SW_ERR);\n    ASSERT_EQ(swoole_get_last_error(), ENOENT);\n    sock->free();\n\n    sock = make_socket(SW_SOCK_TCP, SW_FD_STREAM, 0);\n    ASSERT_NE(sock, nullptr);\n    sa.assign(SW_SOCK_TCP, \"192.168.199.199\", 80);\n    sock->set_timeout(0.3, SW_TIMEOUT_CONNECT);\n    ASSERT_EQ(sock->connect_sync(sa), SW_ERR);\n    ASSERT_EQ(swoole_get_last_error(), ETIMEDOUT);\n    sock->free();\n\n    sock = make_socket(SW_SOCK_TCP, SW_FD_STREAM, 0);\n    ASSERT_NE(sock, nullptr);\n    sa.assign(SW_SOCK_TCP, \"127.0.0.1\", 59999);\n    sock->set_timeout(0.3, SW_TIMEOUT_CONNECT);\n    ASSERT_EQ(sock->connect_sync(sa), SW_ERR);\n    ASSERT_EQ(swoole_get_last_error(), ECONNREFUSED);\n    sock->free();\n\n    sock = make_socket(SW_SOCK_TCP, SW_FD_STREAM, 0);\n    ASSERT_NE(sock, nullptr);\n    sa.assign(SW_SOCK_TCP, TEST_HTTP_DOMAIN, 80);\n    sock->set_timeout(0.3, SW_TIMEOUT_CONNECT);\n    ASSERT_EQ(sock->connect_sync(sa), SW_OK);\n    sock->free();\n\n    sock = make_socket(SW_SOCK_UDP, SW_FD_STREAM, 0);\n    ASSERT_NE(sock, nullptr);\n    sa.assign(SW_SOCK_UDP, \"127.0.0.1\", 9900);\n    sock->set_timeout(0.3, SW_TIMEOUT_CONNECT);\n    ASSERT_EQ(sock->connect_sync(sa), SW_OK);\n    sock->free();\n}\n\nTEST(socket, fail) {\n    auto *sock = make_socket(SW_SOCK_TCP, SW_FD_STREAM, 0);\n    ASSERT_NE(sock, nullptr);\n\n    network::Address sa;\n    sa.assign(SW_SOCK_TCP, TEST_HTTP_DOMAIN, 80);\n    sock->set_timeout(0.3, SW_TIMEOUT_CONNECT);\n    ASSERT_EQ(sock->connect_sync(sa), SW_OK);\n\n    close(sock->get_fd());\n\n    ASSERT_EQ(sock->get_name(), -1);\n    ASSERT_EQ(errno, EBADF);\n\n    network::Address peer;\n    ASSERT_EQ(sock->get_peer_name(&peer), -1);\n    ASSERT_EQ(errno, EBADF);\n\n    ASSERT_EQ(sock->set_tcp_nopush(1), -1);\n    ASSERT_EQ(sock->listen(1), -1);\n\n    ASSERT_FALSE(sock->set_buffer_size(1));\n    ASSERT_FALSE(sock->set_recv_buffer_size(1));\n    ASSERT_FALSE(sock->set_send_buffer_size(1));\n\n    ASSERT_FALSE(sock->set_tcp_nodelay());\n    ASSERT_FALSE(sock->cork());\n    ASSERT_FALSE(sock->uncork());\n\n    ASSERT_FALSE(sock->set_kernel_read_timeout(0.1));\n    ASSERT_FALSE(sock->set_kernel_write_timeout(0.1));\n\n    sock->move_fd();\n    sock->free();\n}\n\nTEST(socket, ssl_fail) {\n    sysv_signal(SIGPIPE, SIG_IGN);\n    network::Client client(SW_SOCK_TCP, false);\n    client.enable_ssl_encrypt();\n\n    ASSERT_EQ(client.connect(TEST_DOMAIN_BAIDU, 443, -1, 0), 0);\n    ASSERT_EQ(client.shutdown(SHUT_WR), 0);\n\n    ASSERT_EQ(client.get_socket()->ssl_send(SW_STRL(TEST_STR)), SW_ERR);\n    ASSERT_EQ(errno, SW_ERROR_SSL_RESET);\n\n    ASSERT_EQ(client.shutdown(SHUT_RD), 0);\n\n    char buf[1024];\n    errno = 0;\n    ASSERT_EQ(client.get_socket()->ssl_recv(SW_STRL(buf)), 0);\n    ASSERT_EQ(errno, 0);\n    ASSERT_EQ(close(client.get_socket()->get_fd()), 0);\n    client.get_socket()->move_fd();\n\n    ASSERT_EQ(client.get_socket()->ssl_recv(SW_STRL(buf)), 0);\n}\n\nTEST(socket, sendto) {\n    char sock1_path[] = \"/tmp/udp_unix1.sock\";\n    char sock2_path[] = \"/tmp/udp_unix2.sock\";\n\n    unlink(sock1_path);\n    unlink(sock2_path);\n\n    auto sock1 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0);\n    sock1->bind(sock1_path);\n\n    auto sock2 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0);\n    sock2->bind(sock2_path);\n\n    ASSERT_GT(sock1->sendto(sock2_path, 0, test_data, strlen(test_data)), 0);\n\n    char buf[1024] = {};\n    network::Address sa;\n    sa.type = SW_SOCK_UNIX_DGRAM;\n    ASSERT_GT(sock2->recvfrom(buf, sizeof(buf), 0, &sa), 0);\n    ASSERT_STREQ(test_data, buf);\n    ASSERT_STREQ(sa.get_addr(), sock1_path);\n\n    sock1->free();\n    sock2->free();\n    unlink(sock1_path);\n    unlink(sock2_path);\n}\n\nstatic void test_sendto(enum swSocketType sock_type) {\n    const char *ip = sock_type == SW_SOCK_UDP ? \"127.0.0.1\" : \"::1\";\n\n    auto sock1 = make_socket(sock_type, SW_FD_DGRAM_SERVER, 0);\n    ASSERT_EQ(sock1->bind(ip, 0), SW_OK);\n    ASSERT_EQ(sock1->get_name(), SW_OK);\n\n    auto sock2 = make_socket(sock_type, SW_FD_DGRAM_SERVER, 0);\n    ASSERT_EQ(sock2->bind(ip, 0), SW_OK);\n    ASSERT_EQ(sock2->get_name(), SW_OK);\n\n    ASSERT_GT(sock1->sendto(ip, sock2->get_port(), test_data, strlen(test_data)), 0);\n\n    char buf[1024] = {};\n    network::Address sa;\n    sa.type = sock_type;\n    ASSERT_GT(sock2->recvfrom(buf, sizeof(buf), 0, &sa), 0);\n\n    ASSERT_STREQ(test_data, buf);\n    ASSERT_EQ(sa.get_port(), sock1->get_port());\n    ASSERT_STREQ(sa.get_addr(), ip);\n\n    sock1->free();\n    sock2->free();\n}\n\nTEST(socket, sendto_ipv4) {\n    test_sendto(SW_SOCK_UDP);\n}\n\nTEST(socket, sendto_ipv6) {\n    test_sendto(SW_SOCK_UDP6);\n}\n\nTEST(socket, recv) {\n    mutex m;\n    m.lock();\n    int port = swoole::test::get_random_port();\n\n    thread t1([&m, port]() {\n        auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, port);\n        char buf[1024] = {};\n        svr->set_block();\n        m.unlock();\n\n        auto client_sock = svr->accept();\n        client_sock->recv(buf, sizeof(buf), 0);\n\n        ASSERT_STREQ(test_data, buf);\n        svr->free();\n    });\n\n    thread t2([&m, port]() {\n        m.lock();\n        auto cli = make_socket(SW_SOCK_TCP, SW_FD_STREAM_CLIENT, 0);\n        ASSERT_EQ(cli->connect(TEST_HOST, port), SW_OK);\n        std::this_thread::sleep_for(std::chrono::milliseconds(100));\n        cli->send(test_data, sizeof(test_data), 0);\n        cli->free();\n    });\n\n    t1.join();\n    t2.join();\n}\n\nTEST(socket, recvfrom_sync) {\n    mutex m;\n    m.lock();\n    int port = swoole::test::get_random_port();\n\n    thread t1([&m, port]() {\n        auto svr = make_server_socket(SW_SOCK_UDP, TEST_HOST, port);\n        network::Address addr;\n        char buf[1024] = {};\n        svr->set_nonblock();\n        m.unlock();\n        svr->recvfrom_sync(buf, sizeof(buf), 0, &addr);\n        ASSERT_STREQ(test_data, buf);\n        svr->free();\n    });\n\n    thread t2([&m, port]() {\n        m.lock();\n        auto cli = make_socket(SW_SOCK_UDP, SW_FD_STREAM_CLIENT, 0);\n        network::Address addr;\n        addr.assign(SW_SOCK_TCP, TEST_HOST, port);\n        ASSERT_EQ(cli->connect(addr), SW_OK);\n        std::this_thread::sleep_for(std::chrono::milliseconds(100));\n        cli->send(test_data, sizeof(test_data), 0);\n        cli->free();\n    });\n\n    t1.join();\n    t2.join();\n}\n\nTEST(socket, send_async_1) {\n    auto sock = make_socket(SW_SOCK_TCP, SW_FD_STREAM_CLIENT, 0);\n    ASSERT_TRUE(sock->set_block());\n    ASSERT_EQ(sock->connect(TEST_HTTP_DOMAIN, 80), SW_OK);\n\n    auto req = test::http_get_request(TEST_HTTP_DOMAIN, \"/\");\n    ASSERT_EQ(sock->send_async(req.c_str(), req.length()), req.length());\n\n    auto buf = sw_tg_buffer();\n    auto n = sock->recv_sync(buf->str, buf->size, 0);\n    ASSERT_GT(n, 0);\n    buf->length = n;\n    ASSERT_TRUE(buf->contains(SW_STRL(TEST_HTTP_EXPECT)));\n\n    sock->free();\n}\n\nTEST(socket, send_async_2) {\n    auto sock = make_socket(SW_SOCK_TCP, SW_FD_STREAM_CLIENT, 0);\n    ASSERT_TRUE(sock->set_block());\n    ASSERT_EQ(sock->connect(TEST_HTTP_DOMAIN, 80), SW_OK);\n\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    auto req = test::http_get_request(TEST_HTTP_DOMAIN, \"/\");\n    ASSERT_EQ(sock->send_async(req.c_str(), req.length()), req.length());\n\n    swoole_event_set_handler(SW_FD_STREAM_CLIENT, SW_EVENT_READ, [](Reactor *reactor, Event *event) {\n        auto buf = sw_tg_buffer();\n        auto n = event->socket->recv_sync(buf->str, buf->size, 0);\n        EXPECT_GT(n, 0);\n        buf->length = n;\n        EXPECT_TRUE(buf->contains(SW_STRL(TEST_HTTP_EXPECT)));\n\n        return 0;\n    });\n\n    swoole_event_add(sock, SW_EVENT_READ | SW_EVENT_ONCE);\n    swoole_event_wait();\n\n    sock->free();\n}\n\nTEST(socket, sendfile_sync) {\n    string file = test::get_root_path() + \"/examples/test.jpg\";\n    mutex m;\n    int port = swoole::test::get_random_port();\n    m.lock();\n\n    auto str = file_get_contents(file);\n\n    thread t1([&m, &str, port]() {\n        auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, port);\n        m.unlock();\n        auto cli = svr->accept();\n        int len;\n        cli->recv_sync(&len, sizeof(len), MSG_WAITALL);\n        int _len = ntohl(len);\n        ASSERT_EQ(_len, str->get_length());\n        ASSERT_LT(_len, 1024 * 1024);\n        std::unique_ptr<char[]> data(new char[_len]);\n        cli->recv_sync(data.get(), _len, MSG_WAITALL);\n        ASSERT_STREQ(data.get(), str->value());\n        cli->free();\n        svr->free();\n    });\n\n    thread t2([&m, &file, &str, port]() {\n        m.lock();\n        auto cli = make_socket(SW_SOCK_TCP, SW_FD_STREAM_CLIENT, 0);\n        network::Address addr;\n        addr.assign(SW_SOCK_TCP, TEST_HOST, port);\n        ASSERT_EQ(cli->connect(addr), SW_OK);\n        int len = htonl(str->get_length());\n        cli->send(&len, sizeof(len), 0);\n        ASSERT_EQ(cli->sendfile_sync(file.c_str(), 0, 0), SW_OK);\n        cli->free();\n    });\n\n    t1.join();\n    t2.join();\n}\n\nTEST(socket, sendfile) {\n    string file = \"/tmp/swoole-file-not-exists\";\n    auto cli = make_socket(SW_SOCK_TCP, SW_FD_STREAM_CLIENT, 0);\n    network::Address addr;\n    addr.assign(SW_SOCK_TCP, TEST_HTTP_DOMAIN, 80);\n    ASSERT_EQ(cli->connect(addr), SW_OK);\n\n    ASSERT_EQ(cli->sendfile_sync(file.c_str(), 0, 0), SW_ERR);\n    ASSERT_EQ(errno, ENOENT);\n\n    File fp(file, File::WRITE | File::CREATE);\n    ASSERT_TRUE(fp.ready());\n\n    ASSERT_EQ(cli->sendfile_sync(file.c_str(), 0, 0), SW_ERR);\n    ASSERT_EQ(swoole_get_last_error(), SW_ERROR_FILE_EMPTY);\n\n    fp.write(SW_STRL(TEST_STR));\n    fp.close();\n\n    ASSERT_EQ(cli->sendfile_sync(file.c_str(), 10, 100), SW_ERR);\n    ASSERT_EQ(swoole_get_last_error(), SW_ERROR_INVALID_PARAMS);\n\n    ASSERT_TRUE(fp.open(file, File::WRITE | File::APPEND));\n    auto req = test::http_get_request(TEST_HTTP_DOMAIN, \"/\");\n    fp.write(req);\n    fp.close();\n\n    ASSERT_EQ(cli->sendfile_sync(file.c_str(), strlen(TEST_STR), 0), SW_OK);\n\n    char rbuf[4096];\n    auto n = cli->recv_sync(rbuf, sizeof(rbuf), 0);\n    ASSERT_GT(n, 0);\n\n    String resp(rbuf, n);\n\n    ASSERT_TRUE(resp.contains(SW_STRL(TEST_HTTP_EXPECT)));\n\n    cli->free();\n\n    ASSERT_TRUE(File::remove(file));\n}\n\nTEST(socket, peek) {\n    char sock1_path[] = \"/tmp/udp_unix1.sock\";\n    char sock2_path[] = \"/tmp/udp_unix2.sock\";\n\n    unlink(sock1_path);\n    unlink(sock2_path);\n\n    auto sock1 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0);\n    sock1->bind(sock1_path);\n\n    auto sock2 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0);\n    sock2->bind(sock2_path);\n\n    ASSERT_GT(sock1->sendto(sock2_path, 0, test_data, strlen(test_data)), 0);\n\n    char buf[1024] = {};\n    ASSERT_GT(sock2->peek(buf, sizeof(buf), 0), 0);\n    ASSERT_STREQ(test_data, buf);\n\n    sw_memset_zero(buf, sizeof(buf));\n    ASSERT_GT(sock2->recv(buf, sizeof(buf), 0), 0);\n    ASSERT_STREQ(test_data, buf);\n\n    sock1->free();\n    sock2->free();\n    unlink(sock1_path);\n    unlink(sock2_path);\n}\n\nTEST(socket, sendto_sync) {\n    char sock1_path[] = \"/tmp/udp_unix1.sock\";\n    unlink(sock1_path);\n    auto sock1 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0);\n    sock1->bind(sock1_path);\n\n    char sock2_path[] = \"/tmp/udp_unix2.sock\";\n    unlink(sock2_path);\n    auto sock2 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0);\n    sock2->bind(sock2_path);\n\n    char sendbuf[65536] = {};\n    swoole_random_string(sendbuf, sizeof(sendbuf) - 1);\n\n    thread t1([sock2, sendbuf]() {\n        std::this_thread::sleep_for(std::chrono::milliseconds(100));\n        char recvbuf[65536] = {};\n        while (1) {\n            auto retval = sock2->recv(recvbuf, sizeof(recvbuf) - 1, 0);\n            recvbuf[retval] = 0;\n            if (retval == 3) {\n                ASSERT_STREQ(recvbuf, \"end\");\n                break;\n            } else {\n                ASSERT_STREQ(sendbuf, recvbuf);\n            }\n        }\n    });\n\n    network::Address sock2_addr;\n    ASSERT_TRUE(sock2_addr.assign(SW_SOCK_UNIX_DGRAM, sock2_path));\n\n    for (int i = 0; i < 10; i++) {\n        ASSERT_GT(sock1->sendto_sync(sock2_addr, sendbuf, strlen(sendbuf)), 0);\n    }\n    ASSERT_GT(sock1->sendto_sync(sock2_addr, \"end\", 3), 0);\n\n    t1.join();\n\n    sock1->free();\n    sock2->free();\n    unlink(sock1_path);\n    unlink(sock2_path);\n}\n\nTEST(socket, clean) {\n    char sock1_path[] = \"/tmp/udp_unix1.sock\";\n    unlink(sock1_path);\n    auto sock1 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0);\n    sock1->bind(sock1_path);\n\n    char sock2_path[] = \"/tmp/udp_unix2.sock\";\n    unlink(sock2_path);\n    auto sock2 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0);\n    sock2->bind(sock2_path);\n\n    char sendbuf[65536] = {};\n    swoole_random_string(sendbuf, sizeof(sendbuf) - 1);\n\n    network::Address sock2_addr;\n    ASSERT_TRUE(sock2_addr.assign(SW_SOCK_UNIX_DGRAM, sock2_path));\n\n    for (int i = 0; i < 3; i++) {\n        ASSERT_GT(sock1->sendto(sock2_addr, sendbuf, strlen(sendbuf)), 0);\n    }\n\n    sock2->clean();\n    char recvbuf[1024];\n    auto retval = sock2->peek(recvbuf, sizeof(recvbuf), MSG_DONTWAIT);\n    ASSERT_EQ(retval, -1);\n\n    sock1->free();\n    sock2->free();\n    unlink(sock1_path);\n    unlink(sock2_path);\n}\n\nTEST(socket, check_liveness) {\n    mutex m;\n    int svr_port = TEST_PORT + __LINE__;\n    m.lock();\n\n    thread t1([&m, svr_port]() {\n        auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, svr_port);\n        m.unlock();\n\n        auto cli = svr->accept();\n        ASSERT_TRUE(cli);\n\n        char buf[1024] = {};\n        cli->recv(buf, sizeof(buf), 0);\n        ASSERT_STREQ(test_data, buf);\n\n        ssize_t n = cli->recv(buf, sizeof(buf), 0);\n        buf[n] = 0;\n        ASSERT_STREQ(\"close\", buf);\n        cli->shutdown(SHUT_RDWR);\n        cli->free();\n\n        svr->free();\n    });\n\n    thread t2([&m, svr_port]() {\n        m.lock();\n\n        auto cli = make_socket(SW_SOCK_TCP, SW_FD_STREAM_CLIENT, 0);\n        ASSERT_EQ(cli->connect(TEST_HOST, svr_port), SW_OK);\n\n        cli->send(test_data, sizeof(test_data), 0);\n        std::this_thread::sleep_for(std::chrono::milliseconds(1));\n        ASSERT_TRUE(cli->check_liveness());\n\n        cli->send(SW_STRL(\"close\"), 0);\n        std::this_thread::sleep_for(std::chrono::milliseconds(5));\n        ASSERT_FALSE(cli->check_liveness());\n\n        cli->free();\n    });\n\n    t1.join();\n    t2.join();\n}\n\n#define CRLF \"\\r\\n\"\n\nstatic void test_socket_sync(network::Socket *sock, bool connect = true) {\n    if (connect) {\n        network::Address addr;\n        ASSERT_TRUE(addr.assign(\"tcp://\" TEST_HTTP_DOMAIN \":80\"));\n        ASSERT_EQ(sock->connect(addr), 0);\n    }\n\n    auto req = test::http_get_request(TEST_HTTP_DOMAIN, \"/get\");\n    ASSERT_EQ(sock->write_sync(req.c_str(), req.length()), req.length());\n    ASSERT_TRUE(sock->check_liveness());\n\n    string resp;\n    SW_LOOP {\n        char buf[1024];\n        auto n = sock->read_sync(buf, sizeof(buf));\n        if (n == 0) {\n            break;\n        }\n        ASSERT_GT(n, 0);\n        resp.append(buf, n);\n    }\n\n    ASSERT_TRUE(resp.find(TEST_HTTP_EXPECT) != resp.npos);\n\n    usleep(50000);\n    ASSERT_FALSE(sock->check_liveness());\n\n    sock->free();\n}\n\nTEST(socket, sync) {\n    auto sock = make_socket(SW_SOCK_TCP, SW_FD_STREAM, 0);\n    test_socket_sync(sock);\n}\n\nTEST(socket, dup) {\n    auto sock = make_socket(SW_SOCK_TCP, SW_FD_STREAM, 0);\n    network::Address addr;\n    ASSERT_TRUE(addr.assign(\"tcp://\" TEST_HTTP_DOMAIN \":80\"));\n    ASSERT_EQ(sock->connect(addr), 0);\n\n    auto sock_2 = sock->dup();\n    sock->free();\n\n    test_socket_sync(sock_2, false);\n}\n\nTEST(socket, convert_to_type) {\n    ASSERT_EQ(network::Socket::convert_to_type(AF_INET, SOCK_STREAM), SW_SOCK_TCP);\n    ASSERT_EQ(network::Socket::convert_to_type(AF_INET6, SOCK_STREAM), SW_SOCK_TCP6);\n    ASSERT_EQ(network::Socket::convert_to_type(AF_INET, SOCK_DGRAM), SW_SOCK_UDP);\n    ASSERT_EQ(network::Socket::convert_to_type(AF_INET6, SOCK_DGRAM), SW_SOCK_UDP6);\n    ASSERT_EQ(network::Socket::convert_to_type(AF_LOCAL, SOCK_STREAM), SW_SOCK_UNIX_STREAM);\n    ASSERT_EQ(network::Socket::convert_to_type(AF_LOCAL, SOCK_DGRAM), SW_SOCK_UNIX_DGRAM);\n    ASSERT_EQ(network::Socket::convert_to_type(AF_INET, SOCK_RAW), SW_SOCK_RAW);\n    ASSERT_EQ(network::Socket::convert_to_type(AF_INET6, SOCK_RAW), SW_SOCK_RAW6);\n\n    std::string s1(\"unix:///tmp/swoole.sock\");\n    ASSERT_EQ(network::Socket::convert_to_type(s1), SW_SOCK_UNIX_STREAM);\n    ASSERT_EQ(s1, \"/tmp/swoole.sock\");\n\n    std::string s2(\"127.0.0.1\");\n    ASSERT_EQ(network::Socket::convert_to_type(s2), SW_SOCK_TCP);\n\n    std::string s3(\"::1\");\n    ASSERT_EQ(network::Socket::convert_to_type(s3), SW_SOCK_TCP6);\n\n    std::string s4(\"unix:/tmp/swoole.sock\");\n    ASSERT_EQ(network::Socket::convert_to_type(s4), SW_SOCK_UNIX_STREAM);\n    ASSERT_EQ(s4, \"/tmp/swoole.sock\");\n}\n\nstatic void test_sock_type(SocketType type, int expect_sock_domain, int expect_sock_type) {\n    int sock_domain, sock_type;\n    ASSERT_EQ(network::Socket::get_domain_and_type(type, &sock_domain, &sock_type), SW_OK);\n    ASSERT_EQ(sock_domain, expect_sock_domain);\n    ASSERT_EQ(sock_type, expect_sock_type);\n}\n\nTEST(socket, get_domain_and_type) {\n    test_sock_type(SW_SOCK_TCP, AF_INET, SOCK_STREAM);\n    test_sock_type(SW_SOCK_TCP6, AF_INET6, SOCK_STREAM);\n    test_sock_type(SW_SOCK_UDP, AF_INET, SOCK_DGRAM);\n    test_sock_type(SW_SOCK_UDP6, AF_INET6, SOCK_DGRAM);\n    test_sock_type(SW_SOCK_UNIX_STREAM, AF_LOCAL, SOCK_STREAM);\n    test_sock_type(SW_SOCK_UNIX_DGRAM, AF_LOCAL, SOCK_DGRAM);\n    test_sock_type(SW_SOCK_RAW, AF_INET, SOCK_RAW);\n    test_sock_type(SW_SOCK_RAW6, AF_INET6, SOCK_RAW);\n\n    ASSERT_TRUE(network::Socket::is_dgram(SW_SOCK_UDP6));\n    ASSERT_TRUE(network::Socket::is_stream(SW_SOCK_TCP));\n\n    int sock_domain, sock_type;\n    ASSERT_EQ(\n        network::Socket::get_domain_and_type(static_cast<swSocketType>(SW_SOCK_RAW6 + 1), &sock_domain, &sock_type),\n        SW_ERR);\n}\n\nTEST(socket, make_socket) {\n    network::Socket *sock;\n\n    sock = make_socket(SW_SOCK_RAW, SW_FD_STREAM, 0);\n    ASSERT_EQ(sock, nullptr);\n    ASSERT_EQ(errno, EPROTONOSUPPORT);\n    ASSERT_EQ(swoole_get_last_error(), EPROTONOSUPPORT);\n\n    sock = make_socket(SW_SOCK_TCP, SW_FD_STREAM, AF_INET6, SOCK_RDM, 999, 0);\n    ASSERT_EQ(sock, nullptr);\n    ASSERT_EQ(errno, EINVAL);\n    ASSERT_EQ(swoole_get_last_error(), EINVAL);\n}\n\nTEST(socket, make_server_socket) {\n    network::Socket *sock;\n\n    auto bad_addr = \"199.199.0.0\";\n\n    sock = make_server_socket(SW_SOCK_RAW, bad_addr);\n    ASSERT_EQ(sock, nullptr);\n    if (geteuid() == 0) {  // root\n        ASSERT_EQ(errno, EPROTONOSUPPORT);\n        ASSERT_EQ(swoole_get_last_error(), EPROTONOSUPPORT);\n    } else {\n        ASSERT_EQ(errno, ESOCKTNOSUPPORT);\n        ASSERT_EQ(swoole_get_last_error(), ESOCKTNOSUPPORT);\n    }\n\n    sock = make_server_socket(SW_SOCK_TCP, bad_addr);\n    ASSERT_EQ(sock, nullptr);\n    ASSERT_EQ(errno, EADDRNOTAVAIL);\n\n    sock = make_server_socket(SW_SOCK_TCP, TEST_HOST, 0, -1);\n    ASSERT_NE(sock, nullptr);\n    sock->free();\n}\n\nTEST(socket, ssl_get_error_reason) {\n    swoole_ssl_init();\n    {\n        int reason = -1;\n        const char *error_str = network::Socket::ssl_get_error_reason(&reason);\n\n        EXPECT_EQ(error_str, nullptr);\n        EXPECT_EQ(reason, 0);\n    }\n    // 测试单个错误的情况\n    {\n        // 生成一个 OpenSSL 错误\n        ERR_put_error(ERR_LIB_SSL, SSL_F_SSL_SET_SESSION, SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED, __FILE__, __LINE__);\n\n        int reason = -1;\n        const char *error_str = network::Socket::ssl_get_error_reason(&reason);\n\n        // 验证错误原因代码\n        EXPECT_EQ(reason, SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED);\n\n        // 验证错误字符串\n        EXPECT_NE(error_str, nullptr);\n        EXPECT_TRUE(strstr(error_str, \"certificate expired\") != nullptr ||\n                    strstr(error_str, \"CERTIFICATE_EXPIRED\") != nullptr);\n\n        // 验证错误队列现在应该为空（因为 ERR_get_error 会移除错误）\n        EXPECT_EQ(ERR_peek_error(), 0);\n    }\n\n    // 测试多个错误的情况（只返回第一个）\n    {\n        // 生成多个 OpenSSL 错误\n        ERR_put_error(ERR_LIB_SSL, SSL_F_SSL_SET_SESSION, SSL_R_SSLV3_ALERT_BAD_CERTIFICATE, __FILE__, __LINE__);\n        ERR_put_error(ERR_LIB_SSL, SSL_F_SSL_SHUTDOWN, SSL_R_PROTOCOL_IS_SHUTDOWN, __FILE__, __LINE__);\n\n        int reason = -1;\n        const char *error_str = network::Socket::ssl_get_error_reason(&reason);\n\n        // 验证返回的是第一个错误的原因代码\n        EXPECT_EQ(reason, SSL_R_SSLV3_ALERT_BAD_CERTIFICATE);\n\n        // 验证错误字符串\n        EXPECT_NE(error_str, nullptr);\n        EXPECT_TRUE(strstr(error_str, \"bad certificate\") != nullptr || strstr(error_str, \"BAD_CERTIFICATE\") != nullptr);\n\n        // 验证错误队列中还有一个错误\n        EXPECT_NE(ERR_peek_error(), 0);\n\n        ERR_get_error();\n    }\n\n    // 测试不同库的错误\n    {\n        // 生成一个 BIO 库错误\n        ERR_put_error(ERR_LIB_BIO, BIO_F_BIO_WRITE, BIO_R_BROKEN_PIPE, __FILE__, __LINE__);\n\n        int reason = -1;\n        const char *error_str = network::Socket::ssl_get_error_reason(&reason);\n\n        // 验证错误原因代码\n        EXPECT_EQ(reason, BIO_R_BROKEN_PIPE);\n\n        // 验证错误字符串\n        EXPECT_NE(error_str, nullptr);\n        EXPECT_TRUE(strstr(error_str, \"broken pipe\") != nullptr || strstr(error_str, \"BROKEN_PIPE\") != nullptr);\n    }\n\n    // 测试 reason 参数为 nullptr 的情况（如果函数支持）\n    {\n        // 生成一个 OpenSSL 错误\n        ERR_put_error(ERR_LIB_SSL, SSL_F_SSL_READ, SSL_R_SSL_HANDSHAKE_FAILURE, __FILE__, __LINE__);\n\n        // 调用函数，传入 nullptr 作为 reason 参数\n        // 注意：如果函数不支持 nullptr 参数，这个测试会导致段错误\n        // 在这种情况下，应该跳过这个测试或修改函数以支持 nullptr\n        const char *error_str = network::Socket::ssl_get_error_reason(nullptr);\n\n        // 验证错误字符串\n        EXPECT_NE(error_str, nullptr);\n        EXPECT_TRUE(strstr(error_str, \"handshake failure\") != nullptr ||\n                    strstr(error_str, \"HANDSHAKE_FAILURE\") != nullptr);\n    }\n\n    // 测试错误队列中有错误但 ERR_reason_error_string 返回 nullptr 的情况\n    {\n        // 使用一个不常见的错误代码，可能没有对应的错误字符串\n        // 注意：这个测试可能不稳定，因为 OpenSSL 可能为所有错误代码都提供字符串\n        ERR_put_error(ERR_LIB_USER, 0, 12345, __FILE__, __LINE__);\n\n        int reason = -1;\n        const char *error_str = network::Socket::ssl_get_error_reason(&reason);\n\n        // 验证错误原因代码\n        EXPECT_EQ(reason, 12345);\n\n        // 错误字符串可能为 nullptr 或包含通用错误信息\n        // 这个验证可能需要根据实际情况调整\n        if (error_str != nullptr) {\n            EXPECT_TRUE(true);  // 如果有字符串，测试通过\n        } else {\n            EXPECT_EQ(error_str, nullptr);  // 如果没有字符串，也测试通过\n        }\n    }\n\n    // 测试函数在多次调用后的行为\n    {\n        // 生成一个 OpenSSL 错误\n        ERR_put_error(ERR_LIB_SSL, SSL_F_SSL_CTX_NEW, SSL_R_LIBRARY_HAS_NO_CIPHERS, __FILE__, __LINE__);\n\n        // 第一次调用\n        int reason1 = -1;\n        const char *error_str1 = network::Socket::ssl_get_error_reason(&reason1);\n\n        // 验证第一次调用的结果\n        EXPECT_EQ(reason1, SSL_R_LIBRARY_HAS_NO_CIPHERS);\n        EXPECT_NE(error_str1, nullptr);\n\n        // 第二次调用，应该没有错误了\n        int reason2 = -1;\n        const char *error_str2 = network::Socket::ssl_get_error_reason(&reason2);\n\n        // 验证第二次调用的结果\n        EXPECT_EQ(reason2, 0);\n        EXPECT_EQ(error_str2, nullptr);\n    }\n}\n\nTEST(socket, catch_error) {\n    network::Socket fake_sock;\n    ASSERT_EQ(fake_sock.catch_write_pipe_error(ENOBUFS), SW_REDUCE_SIZE);\n    ASSERT_EQ(fake_sock.catch_write_pipe_error(EMSGSIZE), SW_REDUCE_SIZE);\n    ASSERT_EQ(fake_sock.catch_write_pipe_error(EAGAIN), SW_WAIT);\n\n    ASSERT_EQ(fake_sock.catch_write_error(ENOBUFS), SW_WAIT);\n}\n\nTEST(socket, misc) {\n    auto sock = make_socket(SW_SOCK_TCP, SW_FD_STREAM, 0);\n    ASSERT_TRUE(sock->set_nonblock());\n    ASSERT_EQ(sock->get_out_buffer_length(), 0);\n    ASSERT_TRUE(sock->set_block());\n    ASSERT_GT(sock->get_fd(), 0);\n    ASSERT_EQ(sock->connect(TEST_HTTP_DOMAIN, 80), 0);\n    ASSERT_EQ(sock->get_name(), 0);\n    ASSERT_GT(sock->get_port(), 1024);\n\n    auto fd = sock->get_fd();\n    auto sock_fd = sock->move_fd();\n    ASSERT_EQ(fd, sock_fd);\n    ASSERT_EQ(sock->get_fd(), -1);\n\n    ASSERT_EQ(sock->catch_write_pipe_error(EMSGSIZE), SW_REDUCE_SIZE);\n    ASSERT_FALSE(sock->kernel_nobufs);\n\n    ASSERT_EQ(sock->catch_write_pipe_error(ENOBUFS), SW_REDUCE_SIZE);\n    ASSERT_TRUE(sock->kernel_nobufs);\n\n    ASSERT_EQ(sock->catch_write_error(EAGAIN), sock->catch_write_pipe_error(EAGAIN));\n\n    delete sock;\n\n    ASSERT_TRUE(network::Socket::is_dgram(SW_SOCK_UDP));\n    ASSERT_FALSE(network::Socket::is_dgram(SW_SOCK_TCP6));\n\n    ASSERT_TRUE(network::Socket::is_stream(SW_SOCK_TCP));\n    ASSERT_FALSE(network::Socket::is_stream(SW_SOCK_UDP));\n\n    ASSERT_TRUE(network::Socket::is_inet6(SW_SOCK_UDP6));\n    ASSERT_FALSE(network::Socket::is_inet6(SW_SOCK_UDP));\n\n    int fds[2];\n    ASSERT_EQ(pipe(fds), 0);\n\n    network::Socket sock1{};\n    sock1.fd = fds[1];\n\n    network::Socket sock2{};\n    sock2.fd = fds[0];\n\n    ASSERT_EQ(sock1.write(test_data, strlen(test_data)), strlen(test_data));\n\n    char rbuf[128];\n    ASSERT_EQ(sock2.read(rbuf, sizeof(rbuf)), strlen(test_data));\n    ASSERT_MEMEQ(test_data, rbuf, strlen(test_data));\n}\n"
  },
  {
    "path": "core-tests/src/network/stream.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"swoole_server.h\"\n\nusing namespace std;\nusing namespace swoole;\nusing namespace swoole::network;\n\nTEST(stream, send) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n    int svr_port = swoole::test::get_random_port();\n    int ori_log_level = sw_logger()->get_level();\n    sw_logger()->set_level(SW_LOG_ERROR);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, svr_port);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    port->open_length_check = true;\n    Stream::set_protocol(&port->protocol);\n\n    mutex lock;\n    lock.lock();\n\n    char buf[65536];\n    ASSERT_EQ(swoole_random_bytes(buf, sizeof(buf)), sizeof(buf));\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    std::thread t1([&]() {\n        swoole_signal_block_all();\n\n        lock.lock();\n\n        swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n        // bad request\n        auto stream0 = Stream::create(TEST_TMP_FILE, 0, SW_SOCK_UNIX_STREAM);\n        ASSERT_EQ(stream0, nullptr);\n\n        // bad request\n        auto stream1 = Stream::create(TEST_HOST, 39999, SW_SOCK_TCP);\n        ASSERT_TRUE(stream1);\n        stream1->response = [](Stream *stream, const char *data, uint32_t length) {\n            EXPECT_EQ(data, nullptr);\n            EXPECT_EQ(stream->errCode, ECONNREFUSED);\n        };\n        ASSERT_EQ(stream1->send(buf, sizeof(buf)), SW_OK);\n\n        // success requset\n        auto stream2 = Stream::create(TEST_HOST, svr_port, SW_SOCK_TCP);\n        ASSERT_TRUE(stream2);\n        stream2->private_data = new string(buf, sizeof(buf));\n        stream2->set_max_length(8 * 1024 * 1024);\n        stream2->response = [](Stream *stream, const char *data, uint32_t length) {\n            string *buf = (string *) stream->private_data;\n            string pkt = string(\"Server: \") + *buf;\n            EXPECT_EQ(string(data, length), pkt);\n            delete buf;\n        };\n        ASSERT_EQ(stream2->send(buf, sizeof(buf)), SW_OK);\n\n        swoole_event_wait();\n\n        kill(getpid(), SIGTERM);\n    });\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); };\n\n    serv.onReceive = [&buf](Server *serv, RecvData *req) -> int {\n        string req_body(req->data + 4, req->info.len - 4);\n\n        EXPECT_EQ(string(buf, sizeof(buf)), req_body);\n\n        string pkt = string(\"Server: \") + req_body;\n        int packed_len = htonl(pkt.length());\n\n        EXPECT_TRUE(serv->send(req->info.fd, &packed_len, sizeof(packed_len)));\n        EXPECT_TRUE(serv->send(req->info.fd, pkt.c_str(), pkt.length()));\n\n        // end stream\n        packed_len = htonl(0);\n        EXPECT_TRUE(serv->send(req->info.fd, &packed_len, sizeof(packed_len)));\n\n        return SW_OK;\n    };\n\n    serv.start();\n    t1.join();\n\n    sw_logger()->set_level(ori_log_level);\n}\n"
  },
  {
    "path": "core-tests/src/os/async.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n\n#include \"swoole_socket.h\"\n#include \"swoole_async.h\"\n\n#include <atomic>\n\nusing namespace swoole;\n\nstatic int callback_count;\n\nTEST(async, dispatch) {\n    int count = 1000;\n    callback_count = 0;\n    std::atomic<int> handle_count(0);\n    AsyncEvent event = {};\n    event.object = &handle_count;\n    event.callback = [](AsyncEvent *event) { callback_count++; };\n    event.handler = [](AsyncEvent *event) { ++(*static_cast<std::atomic<int> *>(event->object)); };\n\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    for (int i = 0; i < count; ++i) {\n        auto ret = swoole::async::dispatch(&event);\n        EXPECT_EQ(ret->object, event.object);\n    }\n\n    swoole_event_wait();\n\n    ASSERT_EQ(handle_count, count);\n    ASSERT_EQ(callback_count, count);\n}\n\nTEST(async, schedule) {\n    callback_count = 0;\n    std::atomic<int> handle_count(0);\n\n    int N = 1000;\n\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    AsyncEvent event{};\n    event.object = &handle_count;\n    event.callback = [](AsyncEvent *event) { callback_count++; };\n    event.handler = [](AsyncEvent *event) {\n        usleep(swoole_rand(50000, 100000));\n        ++(*static_cast<std::atomic<int> *>(event->object));\n    };\n\n    SwooleG.aio_core_worker_num = 4;\n    SwooleG.aio_worker_num = 128;\n    SwooleG.aio_max_wait_time = 0.05;\n    SwooleG.aio_max_idle_time = 0.5;\n\n    int count = N;\n    swoole_timer_tick(2, [&count, &event, N](Timer *, TimerNode *timer) {\n        SW_LOOP_N(swoole_rand(5, 15)) {\n            auto ret = swoole::async::dispatch(&event);\n            EXPECT_EQ(ret->object, event.object);\n            count--;\n            if (count == 0) {\n                swoole_timer_del(timer);\n                ASSERT_GT(sw_async_threads()->get_worker_num(), 16);\n                ASSERT_GT(sw_async_threads()->get_queue_size(), 100);\n                ASSERT_GT(sw_async_threads()->get_task_num(), 100);\n                break;\n            } else if (count == N - 1) {\n                ASSERT_EQ(sw_async_threads()->get_worker_num(), 4);\n                ASSERT_LE(sw_async_threads()->get_queue_size(), 1);\n                ASSERT_EQ(sw_async_threads()->get_task_num(), 1);\n            } else if (count < N / 2) {\n                ASSERT_GT(sw_async_threads()->get_worker_num(), 4);\n            }\n        }\n\n        if (count % 50 == 0) {\n            DEBUG() << \"async worker thread num=\" << sw_async_threads()->get_worker_num() << \"\\n\";\n        }\n    });\n\n    swoole_timer_tick(2000, [](TIMER_PARAMS) {\n        DEBUG() << \"async worker thread num=\" << sw_async_threads()->get_worker_num() << \"\\n\";\n        if (sw_async_threads()->get_worker_num() < 16) {\n            swoole_timer_del(tnode);\n        }\n    });\n\n    swoole_event_wait();\n\n    ASSERT_EQ(handle_count, N);\n    ASSERT_EQ(callback_count, N);\n}\n\nTEST(async, misc) {\n    callback_count = 0;\n    std::atomic<int> handle_count(0);\n    AsyncEvent event = {};\n    AsyncEvent *rv;\n    event.object = &handle_count;\n    event.callback = [](AsyncEvent *event) { callback_count++; };\n    event.handler = [](AsyncEvent *event) { ++(*static_cast<std::atomic<int> *>(event->object)); };\n\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    auto ret = swoole::async::dispatch(&event);\n    EXPECT_EQ(ret->object, event.object);\n\n    sw_async_threads()->notify_one();\n\n    AsyncEvent event2 = {};\n    event2.callback = [](AsyncEvent *event) {\n        ASSERT_EQ(event->retval, -1);\n        ASSERT_EQ(event->error, SW_ERROR_AIO_BAD_REQUEST);\n        callback_count++;\n    };\n    rv = swoole::async::dispatch(&event2);\n    EXPECT_NE(rv, nullptr);\n\n    swoole_event_wait();\n\n    ASSERT_EQ(handle_count, 1);\n    ASSERT_EQ(callback_count, 2);\n}\n"
  },
  {
    "path": "core-tests/src/os/file.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n\n#include \"swoole_file.h\"\n#include \"swoole_pipe.h\"\n\nusing namespace swoole;\n\nTEST(file, read_line) {\n    std::string filename = test::get_root_path() + \"/tests/include/bootstrap.php\";\n    File file(filename, File::READ);\n    FILE *stdc_file = fopen(filename.c_str(), \"r\");\n    ASSERT_NE(stdc_file, nullptr);\n    char buf1[1024];\n    char buf2[1024];\n\n    size_t size = file.get_size();\n    size_t total = 0;\n\n    while (true) {\n        auto retval = file.read_line(buf1, sizeof(buf1));\n        if (retval == 0) {\n            break;\n        }\n        total += retval;\n        ASSERT_NE(fgets(buf2, sizeof(buf2), stdc_file), nullptr);\n        ASSERT_STREQ(buf1, buf2);\n    }\n    ASSERT_EQ(total, size);\n}\n\nTEST(file, read_line_no_crlf) {\n    String buf(1024);\n    swoole_random_string(buf.str, buf.size - 1);\n    buf.str[buf.size - 1] = '\\0';\n\n    std::string filename = \"/tmp/swoole_file_read_line_no_crlf.txt\";\n    ASSERT_TRUE(file_put_contents(filename, buf.str, buf.size - 1));\n\n    File file(filename, File::READ);\n    char rbuf[1024];\n    ASSERT_EQ(file.read_line(rbuf, sizeof(rbuf)), sizeof(rbuf) - 1);\n    ASSERT_EQ(rbuf[sizeof(rbuf) - 1], '\\0');\n\n    remove(filename.c_str());\n}\n\nTEST(file, file_put_contents) {\n    std::string filename = \"/tmp/not-exists-dir/test.txt\";\n\n    ASSERT_FALSE(file_put_contents(filename, TEST_STR, 0));\n    ASSERT_ERREQ(SW_ERROR_FILE_EMPTY);\n\n    ASSERT_FALSE(file_put_contents(filename, TEST_STR, SwooleG.max_file_content + 1));\n    ASSERT_ERREQ(SW_ERROR_FILE_TOO_LARGE);\n\n    ASSERT_FALSE(file_put_contents(filename, SW_STRL(TEST_STR)));\n    ASSERT_ERREQ(ENOENT);\n}\n\nTEST(file, file_get_contents) {\n    std::string filename = \"/tmp/not-exists-dir/test.txt\";\n\n    ASSERT_EQ(file_get_contents(filename), nullptr);\n    ASSERT_ERREQ(ENOENT);\n\n    ASSERT_EQ(file_get_contents(\"/tmp\"), nullptr);\n    ASSERT_ERREQ(EISDIR);\n\n    auto empty_file = \"/tmp/empty-file.txt\";\n    int fd = open(empty_file, O_CREAT | O_RDWR, 0644);\n    close(fd);\n\n    ASSERT_EQ(file_get_contents(empty_file), nullptr);\n    ASSERT_ERREQ(SW_ERROR_FILE_EMPTY);\n    remove(empty_file);\n\n    auto large_file = test::get_root_path() + \"/bin/core-tests\";\n    SwooleG.max_file_content = 1024 * 1024;\n    ASSERT_EQ(file_get_contents(large_file), nullptr);\n    ASSERT_ERREQ(SW_ERROR_FILE_TOO_LARGE);\n    SwooleG.max_file_content = SW_MAX_FILE_CONTENT;\n}\n\nTEST(file, file_get_size) {\n    ASSERT_EQ(file_get_size(\"/tmp/not-exists-file.txt\"), -1);\n    ASSERT_ERREQ(ENOENT);\n\n    ASSERT_EQ(file_get_size(9999), -1);\n    ASSERT_ERREQ(EBADF);\n\n    int fd = open(\"/tmp\", O_RDONLY);\n    ASSERT_EQ(file_get_size(fd), -1);\n    ASSERT_ERREQ(EISDIR);\n}\n\nTEST(file, open_twice) {\n    auto fname = \"/tmp/swoole_file_open_twice.txt\";\n    File file1(fname, File::WRITE | File::CREATE);\n    ASSERT_TRUE(file1.ready());\n\n    file1.open(fname, File::READ);\n    ASSERT_TRUE(file1.ready());\n    ASSERT_TRUE(file1.close());\n    ASSERT_FALSE(file1.close());\n\n    remove(fname);\n}\n\nTEST(file, error) {\n    Pipe p(true);\n    auto buf = sw_tg_buffer();\n    File fp(p.get_socket(true)->get_fd());\n    ASSERT_EQ(fp.read_all(buf->str, buf->size), 0);\n    ASSERT_ERREQ(ESPIPE);\n\n    ASSERT_EQ(fp.write_all(SW_STRL(TEST_STR)), 0);\n    ASSERT_ERREQ(ESPIPE);\n\n    p.close();\n\n    FileStatus stat;\n    ASSERT_FALSE(fp.stat(&stat));\n    ASSERT_ERREQ(EBADF);\n\n    fp.release();\n}\n\nTEST(file, tmp_file) {\n    char buf[128] = \"/tmp/not-exists-dir/test.XXXXXX\";\n    ASSERT_EQ(swoole_tmpfile(buf), -1);\n    ASSERT_ERREQ(ENOENT);\n\n    auto ori_tmp_dir = swoole_get_task_tmpdir();\n    // 这里不能使用 swoole_set_task_tmpdir() ，它会递归创建目录\n    SwooleG.task_tmpfile = buf;\n    auto fp = make_tmpfile();\n    ASSERT_FALSE(fp.ready());\n    SwooleG.task_tmpfile = ori_tmp_dir;\n}\n\nTEST(file, empty_file) {\n    auto fname = \"/tmp/swoole_empty_file.txt\";\n    File fp(fname, File::WRITE | File::CREATE);\n\n    fp.open(fname, File::READ);\n    char buf[128];\n    ASSERT_EQ(fp.read_all(buf, sizeof(buf)), 0);\n    swoole_clear_last_error();\n    errno = 0;\n    ASSERT_ERREQ(0);\n    ASSERT_EQ(errno, 0);\n    fp.close();\n\n    remove(fname);\n}"
  },
  {
    "path": "core-tests/src/os/msg_queue.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"swoole_msg_queue.h\"\n\nusing swoole::MsgQueue;\nusing swoole::QueueNode;\n\nTEST(msg_queue, rbac) {\n    MsgQueue q(0x950001);\n    ASSERT_TRUE(q.ready());\n    ASSERT_GE(q.get_id(), 0);\n    QueueNode in;\n    in.mtype = 999;\n    strcpy(in.mdata, \"hello world\");\n\n    ASSERT_TRUE(q.set_capacity(8192));\n\n    // input data\n    ASSERT_TRUE(q.push(&in, strlen(in.mdata)));\n\n    size_t queue_num, queue_bytes;\n    ASSERT_TRUE(q.stat(&queue_num, &queue_bytes));\n    ASSERT_EQ(queue_num, 1);\n    ASSERT_GT(queue_bytes, 10);\n\n    // output data\n    QueueNode out{};\n    ASSERT_GT(q.pop(&out, sizeof(out.mdata)), 1);\n\n    ASSERT_TRUE(q.stat(&queue_num, &queue_bytes));\n    ASSERT_EQ(queue_num, 0);\n    ASSERT_EQ(queue_bytes, 0);\n\n    ASSERT_EQ(out.mtype, in.mtype);\n    ASSERT_STREQ(out.mdata, in.mdata);\n\n    ASSERT_TRUE(q.destroy());\n    ASSERT_FALSE(q.destroy());\n    ASSERT_ERREQ(EINVAL);\n\n    q.set_blocking(false);\n\n    ASSERT_EQ(q.pop(&out, sizeof(out.mdata)), -1);\n    ASSERT_ERREQ(EINVAL);\n\n    ASSERT_FALSE(q.push(&in, strlen(in.mdata)));\n    ASSERT_ERREQ(EINVAL);\n\n    ASSERT_FALSE(q.stat(&queue_num, &queue_bytes));\n    ASSERT_ERREQ(EINVAL);\n\n    ASSERT_FALSE(q.set_capacity(8192));\n    ASSERT_ERREQ(EINVAL);\n}\n"
  },
  {
    "path": "core-tests/src/os/os.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n\n#include \"swoole_file.h\"\n#include \"swoole_thread.h\"\n\nusing namespace swoole;\n\nTEST(os, daemon) {\n    auto sid = getsid(getpid());\n    int status;\n    swoole_waitpid(test::spawn_exec([sid]() {\n                       ASSERT_EQ(sid, getsid(getpid()));\n                       ASSERT_TRUE(isatty(STDIN_FILENO));\n\n                       ASSERT_EQ(swoole_daemon(0, 0), 0);\n                       ASSERT_NE(sid, getsid(getpid()));\n\n                       ASSERT_FALSE(isatty(STDIN_FILENO));\n                   }),\n                   &status,\n                   0);\n}\n\nTEST(os, cpu_affinity) {\n    cpu_set_t ori_affinity, affinity;\n    ASSERT_EQ(swoole_get_cpu_affinity(&affinity), 0);\n    ori_affinity = affinity;\n\n    CPU_ZERO(&affinity);\n    CPU_SET(1, &affinity);\n\n    ASSERT_EQ(swoole_set_cpu_affinity(&affinity), 0);\n    ASSERT_EQ(swoole_get_cpu_affinity(&affinity), 0);\n\n    auto cpu_n = SW_CPU_NUM;\n    SW_LOOP_N(cpu_n) {\n        if (i == 1) {\n            ASSERT_TRUE(CPU_ISSET(i, &affinity));\n        } else {\n            ASSERT_FALSE(CPU_ISSET(i, &affinity));\n        }\n    }\n\n    ASSERT_EQ(swoole_set_cpu_affinity(&ori_affinity), 0);\n}\n\nTEST(os, thread_name) {\n    std::thread t([]() {\n        char new_name[512];\n        auto thread_name = \"sw-core-tests\";\n        ASSERT_TRUE(swoole_thread_set_name(thread_name));\n        ASSERT_TRUE(swoole_thread_get_name(new_name, sizeof(new_name)));\n\n        ASSERT_STREQ(thread_name, new_name);\n\n        ASSERT_FALSE(swoole_thread_set_name(\"swoole-core-tests-max-size-is-16\"));\n        ASSERT_EQ(swoole_get_last_error(), ERANGE);\n    });\n    t.join();\n}\n\nTEST(os, thread_id) {\n    auto tid = swoole_thread_id_to_str(std::this_thread::get_id());\n    DEBUG() << \"current thread id: \" << tid << \"\\n\";\n    ASSERT_FALSE(tid.empty());\n}\n\nTEST(os, set_isolation) {\n    swoole_set_isolation(\"not-exists-group\", \"not-exists-user\", \"/tmp/not-exists-dir\");\n}\n"
  },
  {
    "path": "core-tests/src/os/pipe.cpp",
    "content": "#include \"test_core.h\"\n#include \"swoole_pipe.h\"\n\nusing namespace swoole;\n\nTEST(pipe, unixsock) {\n    UnixSocket p(true, SOCK_DGRAM);\n    ASSERT_TRUE(p.ready());\n\n    char buf[1024];\n\n    int ret = p.write((void *) SW_STRS(\"hello world1\"));\n    ASSERT_GT(ret, 0);\n    ret = p.write((void *) SW_STRS(\"hello world2\"));\n    ASSERT_GT(ret, 0);\n    ret = p.write((void *) SW_STRS(\"hello world3\"));\n    ASSERT_GT(ret, 0);\n\n    // 1\n    ret = p.read(buf, sizeof(buf));\n    if (ret < 0) {\n        swoole_sys_warning(\"read() failed.\");\n    }\n    ASSERT_GT(ret, 0);\n    ASSERT_EQ(strcmp(\"hello world1\", buf), 0);\n    // 2\n    ret = p.read(buf, sizeof(buf));\n    ASSERT_GT(ret, 0);\n    ASSERT_EQ(strcmp(\"hello world2\", buf), 0);\n    // 3\n    ret = p.read(buf, sizeof(buf));\n    ASSERT_GT(ret, 0);\n    ASSERT_EQ(strcmp(\"hello world3\", buf), 0);\n}\n\nTEST(pipe, base) {\n    int ret;\n    char data[256];\n\n    Pipe p(true);\n    ASSERT_TRUE(p.ready());\n\n    ret = p.write((void *) SW_STRL(\"hello world\\n\"));\n    ASSERT_GT(ret, 0);\n    ret = p.write((void *) SW_STRL(\"你好中国。\\n\"));\n    ASSERT_GT(ret, 0);\n\n    sw_memset_zero(data, 256);\n    ret = p.read(data, 255);\n    ASSERT_GT(ret, 0);\n    ASSERT_EQ(strcmp(\"hello world\\n你好中国。\\n\", data), 0);\n}\n"
  },
  {
    "path": "core-tests/src/os/process_pool.cpp",
    "content": "#include \"test_core.h\"\n#include \"swoole_process_pool.h\"\n\n#include <csignal>\n\n#ifdef __MACH__\n#define sysv_signal signal\n#endif\n\n#include \"swoole_signal.h\"\n#include <sys/ipc.h>\n#include <sys/msg.h>\nusing namespace swoole;\n\nconstexpr int magic_number = 99900011;\nstatic ProcessPool *current_pool = nullptr;\nstatic Worker *current_worker = nullptr;\n\nstatic void test_func(ProcessPool &pool) {\n    EventData data{};\n    size_t size = swoole_system_random(1024, 4096);\n    String rmem(size);\n    rmem.append_random_bytes(size - 1);\n    rmem.append(\"\\0\");\n\n    data.info.len = size;\n    memcpy(data.data, rmem.value(), size);\n\n    DEBUG() << \"dispatch: \" << size << \" bytes\\n\";\n\n    int worker_id = -1;\n    ASSERT_EQ(pool.dispatch_sync(&data, &worker_id), SW_OK);\n\n    pool.running = true;\n    pool.ptr = &rmem;\n    if (pool.onWorkerStart) {\n        pool.onWorkerStart(&pool, pool.get_worker(0));\n    }\n    pool.main_loop(&pool, pool.get_worker(0));\n    pool.destroy();\n}\n\nstatic void test_func_task_protocol(ProcessPool &pool) {\n    pool.set_protocol(SW_PROTOCOL_TASK);\n    pool.onTask = [](ProcessPool *pool, Worker *worker, EventData *task) -> int {\n        pool->running = false;\n        auto *_data = (String *) pool->ptr;\n        usleep(10000);\n        EXPECT_MEMEQ(_data->str, task->data, task->len());\n        return 0;\n    };\n    test_func(pool);\n}\n\nstatic void test_func_message_protocol(ProcessPool &pool) {\n    pool.set_protocol(SW_PROTOCOL_MESSAGE);\n    pool.onMessage = [](ProcessPool *pool, RecvData *rdata) {\n        pool->running = false;\n        String *_data = static_cast<String *>(pool->ptr);\n        usleep(10000);\n\n        DEBUG() << \"received: \" << rdata->info.len << \" bytes\\n\";\n        EXPECT_MEMEQ(_data->str, rdata->data, rdata->info.len);\n    };\n    test_func(pool);\n}\n\nstatic void test_func_stream_protocol(ProcessPool &pool) {\n    pool.set_protocol(SW_PROTOCOL_STREAM);\n    pool.onMessage = [](ProcessPool *pool, RecvData *rdata) {\n        pool->running = false;\n        String *_data = (String *) pool->ptr;\n        EventData *msg = (EventData *) rdata->data;\n        usleep(10000);\n\n        DEBUG() << \"received: \" << rdata->info.len << \" bytes\\n\";\n        EXPECT_MEMEQ(_data->str, msg->data, msg->len());\n    };\n    test_func(pool);\n}\n\nTEST(process_pool, tcp) {\n    ProcessPool pool{};\n    int svr_port = TEST_PORT + __LINE__;\n    ASSERT_EQ(pool.create(1, 0, SW_IPC_SOCKET), SW_OK);\n    ASSERT_EQ(pool.listen(TEST_HOST, svr_port, 128), SW_OK);\n\n    test_func_task_protocol(pool);\n}\n\nTEST(process_pool, unix_sock) {\n    ProcessPool pool{};\n    signal(SIGPIPE, SIG_IGN);\n    ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK);\n    ASSERT_EQ(pool.listen(TEST_HOST, TEST_PORT, 128), SW_ERR);\n    ASSERT_ERREQ(SW_ERROR_OPERATION_NOT_SUPPORT);\n    ASSERT_EQ(pool.listen(TEST_SOCK_FILE, 128), SW_ERR);\n    ASSERT_ERREQ(SW_ERROR_OPERATION_NOT_SUPPORT);\n\n    test_func_task_protocol(pool);\n}\n\nTEST(process_pool, tcp_raw) {\n    ProcessPool pool{};\n    constexpr int size = 2 * 1024 * 1024;\n    int svr_port = TEST_PORT + __LINE__;\n    ASSERT_EQ(pool.create(1, 0, SW_IPC_SOCKET), SW_OK);\n    ASSERT_EQ(pool.listen(TEST_HOST, svr_port, 128), SW_OK);\n    pool.set_max_packet_size(size);\n    pool.set_protocol(SW_PROTOCOL_STREAM);\n\n    String data(size);\n    data.append_random_bytes(size - 1);\n    data.append(\"\\0\");\n\n    ASSERT_EQ(pool.dispatch_sync(data.str, data.length), SW_OK);\n\n    pool.running = true;\n    pool.ptr = &data;\n    pool.onMessage = [](ProcessPool *pool, RecvData *rdata) -> void {\n        pool->running = false;\n        String *_data = (String *) pool->ptr;\n        EXPECT_MEMEQ(_data->str, rdata->data, rdata->info.len);\n    };\n    pool.main_loop(&pool, pool.get_worker(0));\n    pool.destroy();\n}\n\n#ifdef HAVE_MSGQUEUE\nTEST(process_pool, msgqueue) {\n    ProcessPool pool{};\n    ASSERT_EQ(pool.create(1, 0x9501, SW_IPC_MSGQUEUE), SW_OK);\n\n    test_func_task_protocol(pool);\n}\n\nTEST(process_pool, msgqueue_2) {\n    auto key = 0x9501 + __LINE__;\n    auto msg_id_ = msgget(key, IPC_CREAT);\n    ASSERT_GE(msg_id_, 0);\n\n    test::spawn_exec_and_wait([key]() {\n        ProcessPool pool{};\n        swoole_set_isolation(\"\", \"nobody\", \"\");\n        ASSERT_EQ(pool.create(1, key, SW_IPC_MSGQUEUE), SW_ERR);\n        ASSERT_ERREQ(EACCES);\n    });\n}\n#endif\n\nTEST(process_pool, message_protocol) {\n    ProcessPool pool{};\n    ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK);\n\n    test_func_message_protocol(pool);\n}\n\nTEST(process_pool, message_protocol_with_timer) {\n    ProcessPool pool{};\n    ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK);\n\n    pool.set_protocol(SW_PROTOCOL_MESSAGE);\n\n    swoole_signal_set(SIGTERM, [](int) {\n        DEBUG() << \"received  SIGTERM signal\\n\";\n        current_pool->running = false;\n    });\n\n    pool.onWorkerStart = [](ProcessPool *pool, Worker *worker) {\n        DEBUG() << \"onStart\\n\";\n        current_pool = pool;\n        swoole_timer_after(50, [pool](TIMER_PARAMS) {\n            DEBUG() << \"kill master\\n\";\n            kill(getpid(), SIGTERM);\n        });\n    };\n\n    pool.onMessage = [](ProcessPool *pool, RecvData *rdata) {\n        auto *_data = static_cast<String *>(pool->ptr);\n        usleep(10000);\n\n        DEBUG() << \"received: \" << rdata->info.len << \" bytes\\n\";\n        EXPECT_MEMEQ(_data->str, rdata->data, rdata->info.len);\n    };\n\n    test_func(pool);\n}\n\nTEST(process_pool, stream_protocol) {\n    ProcessPool pool{};\n    ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK);\n\n    test_func_stream_protocol(pool);\n}\n\nTEST(process_pool, stream_protocol_with_msgq) {\n    ProcessPool pool{};\n    ASSERT_EQ(pool.create(1, 0x9501, SW_IPC_MSGQUEUE), SW_OK);\n\n    test_func_stream_protocol(pool);\n}\n\nTEST(process_pool, shutdown) {\n    ProcessPool pool{};\n    int *shm_value = (int *) sw_mem_pool()->alloc(sizeof(int));\n    ASSERT_EQ(pool.create(1, 0x9501, SW_IPC_MSGQUEUE), SW_OK);\n\n    // init\n    pool.set_max_packet_size(8192);\n    pool.set_protocol(SW_PROTOCOL_TASK);\n    pool.ptr = shm_value;\n    pool.onWorkerStart = [](ProcessPool *pool, Worker *worker) {\n        int *shm_value = (int *) pool->ptr;\n        *shm_value = magic_number;\n        usleep(1);\n    };\n\n    pool.onTask = [](ProcessPool *pool, Worker *worker, EventData *task) -> int {\n        usleep(1000);\n        kill(pool->master_pid, SIGTERM);\n        return 0;\n    };\n\n    pool.onStart = [](ProcessPool *pool) {\n        EventData msg{};\n        msg.info.len = 128;\n        swoole_random_string(msg.data, msg.info.len);\n        int worker_id = -1;\n        pool->dispatch_sync(&msg, &worker_id);\n    };\n\n    current_pool = &pool;\n    sysv_signal(SIGTERM, [](int sig) { current_pool->running = false; });\n\n    // start\n    ASSERT_EQ(pool.start(), SW_OK);\n    // wait\n    ASSERT_EQ(pool.wait(), SW_OK);\n\n    pool.destroy();\n\n    ASSERT_EQ(*shm_value, magic_number);\n\n    sysv_signal(SIGTERM, SIG_DFL);\n}\n\nTEST(process_pool, reload) {\n    ProcessPool pool{};\n    test::counter_init();\n    ASSERT_EQ(pool.create(2), SW_OK);\n\n    // init\n    pool.set_max_packet_size(8192);\n    pool.max_wait_time = 1;\n\n    pool.onWorkerStart = [](ProcessPool *pool, Worker *worker) {\n        test::counter_incr(0);\n        current_pool = pool;\n\n        DEBUG() << \"onWorkerStart \" << worker->id << \"\\n\";\n\n        sysv_signal(SIGTERM, SIG_IGN);\n        sysv_signal(SIGRTMIN, [](int) { current_pool->reopen_logger(); });\n        sysv_signal(SIGWINCH, [](int) { current_pool->reopen_logger(); });\n\n        while (true) {\n            sleep(10000);\n        }\n    };\n\n    pool.onStart = [](ProcessPool *pool) {\n        pool->reopen_logger();\n        swoole_timer_after(50, [pool](TIMER_PARAMS) { kill(pool->get_worker(0)->pid, SIGRTMIN); });\n        swoole_timer_after(100, [pool](TIMER_PARAMS) { pool->reload(); });\n    };\n\n    pool.onBeforeReload = [](ProcessPool *pool) { DEBUG() << \"onBeforeReload\\n\"; };\n\n    pool.onAfterReload = [](ProcessPool *pool) {\n        DEBUG() << \"onAfterReload\\n\";\n        swoole_timer_after(100, [pool](TIMER_PARAMS) { pool->shutdown(); });\n    };\n\n    pid_t other_child_pid = test::spawn_exec([]() {\n        usleep(10000);\n        exit(123);\n    });\n    test::counter_set(20, other_child_pid);\n\n    pool.onWorkerError = [](ProcessPool *pool, Worker *worker, const ExitStatus &exit_status) {\n        DEBUG() << \"onWorkerError \" << exit_status.get_pid() << \"\\n\";\n        ASSERT_EQ(exit_status.get_signal(), SIGKILL);\n    };\n\n    pool.onWorkerMessage = [](ProcessPool *pool, EventData *msg) {\n        DEBUG() << \"onWorkerMessage: type \" << msg->info.type << \", content=\" << std::string(msg->data, msg->info.len);\n        EXPECT_EQ(msg->info.type, SW_WORKER_MESSAGE_STOP + 1);\n        EXPECT_MEMEQ(msg->data, TEST_STR, msg->info.len);\n    };\n\n    pool.onWorkerNotFound = [](ProcessPool *pool, const ExitStatus &exit_status) -> int {\n        DEBUG() << \"onWorkerNotFound \" << exit_status.get_pid() << \"\\n\";\n        EXPECT_EQ(exit_status.get_pid(), test::counter_get(20));\n        EXPECT_EQ(exit_status.get_code(), 123);\n        EXPECT_EQ(pool->push_message(SW_WORKER_MESSAGE_STOP + 1, SW_STRL(TEST_STR)), SW_OK);\n        return SW_OK;\n    };\n\n    current_pool = &pool;\n    sysv_signal(SIGTERM, [](int sig) { current_pool->running = false; });\n    sysv_signal(SIGIO, [](int sig) { current_pool->read_message = true; });\n\n    ASSERT_EQ(pool.start(), SW_OK);\n    ASSERT_EQ(pool.wait(), SW_OK);\n\n    pool.destroy();\n\n    ASSERT_EQ(test::counter_get(0), 4);\n\n    sysv_signal(SIGTERM, SIG_DFL);\n}\n\nstatic void test_async_pool() {\n    ProcessPool pool{};\n    ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK);\n\n    // init\n    pool.set_max_packet_size(8192);\n    pool.set_protocol(SW_PROTOCOL_TASK);\n    pool.async = true;\n    test::counter_init();\n\n    pool.onStart = [](ProcessPool *pool) {\n        current_pool = pool;\n        sysv_signal(SIGTERM, [](int sig) { current_pool->running = false; });\n    };\n\n    pool.onWorkerStart = [](ProcessPool *pool, Worker *worker) {\n        test::counter_set(0, magic_number);\n        current_worker = worker;\n        current_pool = pool;\n        sysv_signal(SIGTERM, [](int sig) { current_pool->running = false; });\n\n        swoole_signal_set(SIGTERM, [](int sig) {\n            DEBUG() << \"value: \" << test::counter_incr(0) << \"; \"\n                    << \"SIGTERM, stop worker\\n\";\n            current_pool->stop(current_worker);\n        });\n\n        usleep(10);\n    };\n\n    pool.onMessage = [](ProcessPool *pool, RecvData *msg) {\n        DEBUG() << \"value: \" << test::counter_incr(0) << \"; \"\n                << \"onMessage, kill\\n\";\n        kill(pool->master_pid, SIGTERM);\n    };\n\n    // start\n    ASSERT_EQ(pool.start(), SW_OK);\n\n    EventData msg{};\n    msg.info.len = 128;\n    swoole_random_string(msg.data, msg.info.len);\n    int worker_id = -1;\n    pool.dispatch_sync(&msg, &worker_id);\n\n    // wait\n    ASSERT_EQ(pool.wait(), SW_OK);\n\n    pool.destroy();\n\n    ASSERT_EQ(test::counter_get(0), magic_number + 2);\n\n    swoole_signal_clear();\n    sysv_signal(SIGTERM, SIG_DFL);\n}\n\nTEST(process_pool, async) {\n    test_async_pool();\n    // ASSERT_EQ(test::spawn_exec_and_wait([]() { test_async_pool(); }), 0);\n}\n\nstatic void test_async_pool_with_mb() {\n    ProcessPool pool{};\n    ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK);\n    ASSERT_EQ(pool.create_message_bus(), SW_OK);\n\n    if (swoole_timer_is_available()) {\n        swoole_timer_free();\n    }\n    swoole_signal_clear();\n\n    // init\n    pool.set_max_packet_size(8192);\n    pool.set_protocol(SW_PROTOCOL_TASK);\n    test::counter_init();\n    pool.async = true;\n\n    pool.onWorkerStart = [](ProcessPool *pool, Worker *worker) {\n        current_worker = worker;\n        current_pool = pool;\n\n        test::counter_incr_and_put_log(0, \"onWorkerStart\");\n\n        swoole_signal_set(SIGTERM, [](int sig) {\n            test::counter_incr_and_put_log(0, \"SIGTERM, stop worker\");\n            current_pool->stop(sw_worker());\n        });\n\n        usleep(10);\n    };\n\n    pool.onWorkerStop = [](ProcessPool *pool, Worker *worker) {\n        current_worker = worker;\n        current_pool = pool;\n\n        test::counter_incr_and_put_log(0, \"onWorkerStop\");\n    };\n\n    pool.onWorkerExit = [](ProcessPool *pool, Worker *worker) { test::counter_incr_and_put_log(0, \"onWorkerExit\"); };\n\n    pool.onStart = [](ProcessPool *pool) {\n        current_pool = pool;\n        swoole_signal_set(SIGTERM, [](int sig) { current_pool->running = false; });\n        swoole_signal_set(SIGIO, [](int sig) { current_pool->read_message = true; });\n\n        test::counter_incr_and_put_log(0, \"onStart\");\n\n        swoole_timer_after(100, [pool](TIMER_PARAMS) {\n            pool->send_message(0, SW_STRL(\"detach\"));\n\n            swoole_timer_after(100, [pool](TIMER_PARAMS) { pool->send_message(0, SW_STRL(\"shutdown\")); });\n        });\n    };\n\n    pool.onShutdown = [](ProcessPool *pool) { test::counter_incr_and_put_log(0, \"onShutdown\"); };\n\n    pool.onMessage = [](ProcessPool *pool, RecvData *msg) {\n        auto req = std::string(msg->data, msg->info.len);\n\n        if (req == \"detach\") {\n            test::counter_incr_and_put_log(0, \"onMessage, detach\");\n            ASSERT_TRUE(pool->detach());\n        } else if ((req == \"shutdown\")) {\n            test::counter_incr_and_put_log(0, \"onMessage, shutdown\");\n            pool->shutdown();\n        }\n    };\n\n    // start\n    ASSERT_EQ(pool.start(), SW_OK);\n    // wait\n    ASSERT_EQ(pool.wait(), SW_OK);\n\n    pool.destroy();\n\n    ASSERT_GE(test::counter_get(0), 8);\n\n    swoole_signal_clear();\n    sysv_signal(SIGTERM, SIG_DFL);\n    sysv_signal(SIGIO, SIG_DFL);\n}\n\nTEST(process_pool, async_mb) {\n    test_async_pool_with_mb();\n}\n\nTEST(process_pool, mb1) {\n    ProcessPool pool{};\n    ASSERT_EQ(pool.create(1, 0, SW_IPC_NONE), SW_OK);\n    ASSERT_EQ(pool.create_message_bus(), SW_ERR);\n    ASSERT_ERREQ(SW_ERROR_OPERATION_NOT_SUPPORT);\n\n    pool.destroy();\n}\n\nTEST(process_pool, mb2) {\n    ProcessPool pool{};\n    ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK);\n    ASSERT_EQ(pool.create_message_bus(), SW_OK);\n    ASSERT_EQ(pool.create_message_bus(), SW_ERR);\n    ASSERT_ERREQ(SW_ERROR_WRONG_OPERATION);\n\n    pool.destroy();\n}\n\nTEST(process_pool, socket) {\n    ProcessPool pool{};\n    ASSERT_EQ(pool.create(1, 0, SW_IPC_SOCKET), SW_OK);\n    ASSERT_EQ(pool.start(), SW_ERR);\n    ASSERT_ERREQ(SW_ERROR_WRONG_OPERATION);\n\n    pool.destroy();\n}\n\nTEST(process_pool, listen) {\n    ProcessPool pool{};\n    auto port = TEST_PORT + __LINE__;\n    ASSERT_EQ(pool.create(1, 0, SW_IPC_SOCKET), SW_OK);\n    ASSERT_EQ(pool.listen(\"127.0.0.1\", port, 128), SW_OK);\n\n    pool.set_protocol(SW_PROTOCOL_STREAM);\n\n    size_t size = 2048;\n    String rmem(size);\n    rmem.append_random_bytes(size - 1);\n    rmem.append('\\0');\n\n    String wmem(size);\n    wmem.append_random_bytes(size - 1);\n    wmem.append('\\0');\n\n    pool.ptr = &wmem;\n\n    pool.onMessage = [](ProcessPool *pool, RecvData *msg) {\n        String *wmem = (String *) pool->ptr;\n        ASSERT_EQ(pool->response(wmem->str, wmem->length), SW_OK);\n        ASSERT_EQ(pool->response(nullptr, 999), SW_ERR);\n        ASSERT_ERREQ(SW_ERROR_INVALID_PARAMS);\n        ASSERT_EQ(pool->response(wmem->str, 0), SW_ERR);\n        ASSERT_ERREQ(SW_ERROR_INVALID_PARAMS);\n    };\n\n    current_pool = &pool;\n    sysv_signal(SIGTERM, [](int sig) { current_pool->running = false; });\n\n    ASSERT_EQ(pool.start(), SW_OK);\n\n    std::thread t1([&]() {\n        swoole_signal_block_all();\n\n        network::SyncClient c(SW_SOCK_TCP);\n        c.connect(\"127.0.0.1\", port);\n\n        uint32_t pkt_len = htonl(rmem.length);\n\n        c.send((char *) &pkt_len, sizeof(pkt_len));\n        c.send(rmem.str, rmem.length);\n        char buf[4096];\n\n        EXPECT_EQ(c.recv((char *) &pkt_len, sizeof(pkt_len)), 4);\n        c.recv(buf, ntohl(pkt_len));\n\n        EXPECT_MEMEQ(buf, wmem.str, wmem.length);\n\n        ASSERT_EQ(pool.response(wmem.str, wmem.length), SW_ERR);\n        ASSERT_ERREQ(SW_ERROR_INVALID_PARAMS);\n\n        c.close();\n\n        kill(getpid(), SIGTERM);\n    });\n\n    ASSERT_EQ(pool.wait(), SW_OK);\n    pool.destroy();\n\n    sysv_signal(SIGTERM, SIG_DFL);\n\n    t1.join();\n}\n\nconst char *test_sock = \"/tmp/swoole_process_pool.sock\";\n\nTEST(process_pool, listen_unixsock) {\n    ProcessPool pool{};\n    ASSERT_EQ(pool.create(1, 0, SW_IPC_SOCKET), SW_OK);\n    ASSERT_EQ(pool.listen(test_sock, 128), SW_OK);\n\n    pool.set_protocol(SW_PROTOCOL_STREAM);\n\n    size_t size = 2048;\n    String rmem(size);\n    rmem.append_random_bytes(size - 1);\n    rmem.append('\\0');\n\n    String wmem(size);\n    wmem.append_random_bytes(size - 1);\n    wmem.append('\\0');\n\n    pool.ptr = &wmem;\n\n    pool.onMessage = [](ProcessPool *pool, RecvData *msg) {\n        String *wmem = (String *) pool->ptr;\n        pool->response(wmem->str, wmem->length);\n    };\n\n    current_pool = &pool;\n    sysv_signal(SIGTERM, [](int sig) { current_pool->running = false; });\n\n    ASSERT_EQ(pool.start(), SW_OK);\n\n    std::thread t1([&]() {\n        swoole_signal_block_all();\n\n        network::SyncClient c(SW_SOCK_UNIX_STREAM);\n        c.connect(test_sock, 0);\n\n        uint32_t pkt_len = htonl(rmem.length);\n\n        c.send((char *) &pkt_len, sizeof(pkt_len));\n        c.send(rmem.str, rmem.length);\n        char buf[4096];\n\n        EXPECT_EQ(c.recv((char *) &pkt_len, sizeof(pkt_len)), 4);\n        c.recv(buf, ntohl(pkt_len));\n\n        EXPECT_MEMEQ(buf, wmem.str, wmem.length);\n\n        c.close();\n\n        kill(getpid(), SIGTERM);\n    });\n\n    ASSERT_EQ(pool.wait(), SW_OK);\n\n    pool.destroy();\n\n    sysv_signal(SIGTERM, SIG_DFL);\n\n    t1.join();\n}\n\nTEST(process_pool, worker) {\n    Worker worker{};\n    worker.init();\n\n    ASSERT_TRUE(worker.is_running());\n    ASSERT_GT(worker.start_time, 0);\n    worker.set_max_request(1000, 200);\n\n    ASSERT_GT(SwooleWG.max_request, 1000);\n    ASSERT_LE(SwooleWG.max_request, 1200);\n\n    worker.shutdown();\n    ASSERT_TRUE(worker.is_shutdown());\n\n    swoole_set_worker_type(SW_USER_WORKER);\n    ASSERT_EQ(swoole_get_worker_symbol(), '@');\n\n    swoole_set_worker_type(SW_TASK_WORKER);\n    ASSERT_EQ(swoole_get_worker_symbol(), '^');\n\n    swoole_set_worker_type(SW_WORKER);\n    ASSERT_EQ(swoole_get_worker_symbol(), '*');\n\n    swoole_set_worker_type(SW_MASTER);\n    ASSERT_EQ(swoole_get_worker_symbol(), '#');\n\n    swoole_set_worker_type(SW_MANAGER);\n    ASSERT_EQ(swoole_get_worker_symbol(), '$');\n\n    worker.set_status_to_idle();\n    ASSERT_TRUE(worker.is_idle());\n    ASSERT_FALSE(worker.is_busy());\n\n    worker.set_status_to_busy();\n    ASSERT_FALSE(worker.is_idle());\n    ASSERT_TRUE(worker.is_busy());\n\n    worker.set_status(SW_WORKER_EXIT);\n    ASSERT_FALSE(worker.is_idle());\n    ASSERT_FALSE(worker.is_busy());\n}\n\nTEST(process_pool, add_worker) {\n    Worker worker{};\n    worker.pid = getpid();\n\n    ProcessPool pool{};\n    ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK);\n\n    pool.add_worker(&worker);\n\n    auto *worker2 = pool.get_worker_by_pid(getpid());\n    ASSERT_EQ(&worker, worker2);\n\n    ASSERT_TRUE(pool.del_worker(worker2));\n}\n"
  },
  {
    "path": "core-tests/src/os/signal.cpp",
    "content": "#include \"test_core.h\"\n#include \"swoole_process_pool.h\"\n#include \"swoole_signal.h\"\n\n#ifdef HAVE_SIGNALFD\nstatic void sig_usr1(int signo) {}\n\nTEST(os_signal, signalfd) {\n    int ret;\n    sigset_t curset;\n\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    sigemptyset(&curset);\n    sigprocmask(SIG_BLOCK, NULL, &curset);\n    ret = sigismember(&curset, SIGUSR1);\n    ASSERT_EQ(ret, 0);\n\n    swoole_signalfd_init();\n    swoole_signal_set(SIGUSR1, sig_usr1);\n\n    sigemptyset(&curset);\n    sigprocmask(SIG_BLOCK, NULL, &curset);\n    ret = sigismember(&curset, SIGUSR1);\n    ASSERT_EQ(ret, 1);\n\n    swoole_signal_set(SIGUSR1, NULL);\n    swoole_signal_clear();\n\n    sigemptyset(&curset);\n    sigprocmask(SIG_BLOCK, NULL, &curset);\n    ret = sigismember(&curset, SIGUSR1);\n    ASSERT_EQ(ret, 0);\n\n    swoole_event_wait();\n}\n#endif\n\nTEST(os_signal, block) {\n    ASSERT_EQ(swoole::test::spawn_exec_and_wait([]() {\n                  sysv_signal(SIGIO, [](int signo) { exit(255); });\n\n                  std::thread t([] {\n                      swoole_signal_block_all();\n                      pthread_kill(pthread_self(), SIGIO);\n                  });\n                  t.join();\n              }),\n              0);\n}\n\nTEST(os_signal, unblock) {\n    auto status = swoole::test::spawn_exec_and_wait([]() {\n        sysv_signal(SIGIO, [](int signo) { exit(255); });\n\n        std::thread t([] {\n            swoole_signal_block_all();\n            pthread_kill(pthread_self(), SIGIO);\n            swoole_signal_unblock_all();\n        });\n        t.join();\n    });\n\n    auto exit_status = swoole::ExitStatus(getpid(), status);\n\n    ASSERT_EQ(exit_status.get_code(), 255);\n}\n\nTEST(os_signal, signal_to_str) {\n    ASSERT_STREQ(swoole_signal_to_str(SIGTERM), \"Terminated: 15\");\n    ASSERT_STREQ(swoole_signal_to_str(SIGIO), \"I/O possible: 29\");\n    ASSERT_STREQ(swoole_signal_to_str(SIGRTMIN), \"Real-time signal 0: 34\");\n    ASSERT_STREQ(swoole_signal_to_str(99999), \"Unknown signal 99999: 99999\");\n}\n\nTEST(os_signal, set) {\n    swoole_signal_set(SIGIO, [](int signo) { exit(255); });\n    ASSERT_TRUE(swoole_signal_isset(SIGIO));\n    ASSERT_FALSE(swoole_signal_isset(SIGTERM));\n    swoole_signal_set(SIGIO, nullptr);\n    ASSERT_FALSE(swoole_signal_isset(SIGIO));\n}\n\nstatic int trigger_signal = 0;\n\nTEST(os_signal, dispatch) {\n    trigger_signal = 0;\n    swoole_signal_set(\n        SIGIO, [](int signo) { trigger_signal = signo; }, true);\n    swoole_kill(getpid(), SIGIO);\n    ASSERT_EQ(trigger_signal, 0);\n\n    ASSERT_EQ(swoole_signal_get_handler(SIGTERM), nullptr);\n    ASSERT_NE(swoole_signal_get_handler(SIGIO), nullptr);\n\n    swoole_signal_dispatch();\n    ASSERT_EQ(trigger_signal, SIGIO);\n\n    trigger_signal = 0;\n\n    swoole_signal_dispatch();\n    ASSERT_EQ(trigger_signal, 0);\n\n    ASSERT_EQ(swoole_signal_get_listener_num(), 0);\n\n    swoole_signal_clear();\n}\n\nTEST(os_signal, error) {\n    swoole_signal_set(SIGIO, nullptr, 0, 0);\n    swoole_signal_block_all();\n    swoole_signal_block_all();  // no effect\n    swoole_signal_unblock_all();\n    swoole_signal_unblock_all();  // no effect\n\n    swoole_signal_callback(SW_SIGNO_MAX);  // no effect\n\n    swoole_clear_last_error();\n    swoole_signal_callback(SIGIO);\n    ASSERT_ERREQ(SW_ERROR_UNREGISTERED_SIGNAL);\n\n    ASSERT_EQ(swoole_signal_get_handler(SW_SIGNO_MAX), nullptr);\n}"
  },
  {
    "path": "core-tests/src/os/timer.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"swoole_signal.h\"\n#include \"swoole_util.h\"\n#include \"swoole_timer.h\"\n\nusing swoole::Timer;\nusing swoole::TimerNode;\n\nTEST(timer, sys) {\n    swoole::test::counter_init();\n    auto counter = swoole::test::counter_ptr();\n\n    uint64_t ms1 = swoole::time<std::chrono::milliseconds>();\n\n    ASSERT_TRUE(swoole_timer_add(\n        20L, false, [&](TIMER_PARAMS) { counter[0]++; }, nullptr));\n\n    swoole_clear_last_error();\n    ASSERT_EQ(swoole_timer_add(-1L, false, [&](TIMER_PARAMS) {}), nullptr);\n    ASSERT_ERREQ(SW_ERROR_INVALID_PARAMS);\n\n    swoole_clear_last_error();\n    ASSERT_EQ(swoole_timer_add(0L, false, [&](TIMER_PARAMS) {}), nullptr);\n    ASSERT_ERREQ(SW_ERROR_INVALID_PARAMS);\n\n    // dtor test\n    auto timer = swoole_timer_add(20L, false, [&](TIMER_PARAMS) { counter[2]++; });\n    ASSERT_TRUE(timer);\n    timer->destructor = [&](TimerNode *tnode) { counter[3] = 9999; };\n\n    swoole_timer_add(\n        100L,\n        true,\n        [&](Timer *, TimerNode *tnode) {\n            counter[1]++;\n            if (counter[1] == 5) {\n                swoole_timer_del(tnode);\n            }\n        },\n        nullptr);\n\n    while (sw_timer()->count() > 0) {\n        sleep(10);\n        swoole_signal_dispatch();\n        if (SwooleG.signal_alarm) {\n            swoole_timer_select();\n        }\n    }\n\n    uint64_t ms2 = swoole::time<std::chrono::milliseconds>();\n\n    swoole_timer_free();\n\n    ASSERT_LE(ms2 - ms1, 510);\n    ASSERT_EQ(counter[0], 1);\n    ASSERT_EQ(counter[1], 5);\n    ASSERT_EQ(counter[2], 1);\n    ASSERT_EQ(counter[3], 9999);\n}\n\nTEST(timer, async) {\n    int timer1_count = 0;\n    int timer2_count = 0;\n\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    uint64_t ms1 = swoole::time<std::chrono::milliseconds>();\n    swoole_timer_after(\n        20, [&](Timer *, TimerNode *) { timer1_count++; }, nullptr);\n\n    swoole_timer_tick(\n        100,\n        [&](Timer *, TimerNode *tnode) {\n            timer2_count++;\n            if (timer2_count == 5) {\n                swoole_timer_del(tnode);\n            }\n        },\n        nullptr);\n\n    swoole_event_wait();\n    uint64_t ms2 = swoole::time<std::chrono::milliseconds>();\n    ASSERT_LE(ms2 - ms1, 510);\n    ASSERT_EQ(timer1_count, 1);\n    ASSERT_EQ(timer2_count, 5);\n}\n\nTEST(timer, exists) {\n    long timer_id = swoole_timer_tick(\n        100, [&](Timer *, TimerNode *tnode) {}, nullptr);\n\n    ASSERT_TRUE(swoole_timer_exists(timer_id));\n}\n\nTEST(timer, clear) {\n    long timer_id = swoole_timer_tick(\n        100, [&](Timer *, TimerNode *tnode) {}, nullptr);\n\n    swoole_timer_clear(timer_id);\n    ASSERT_FALSE(swoole_timer_exists(timer_id));\n}\n\nTEST(timer, get) {\n    long timer_id = swoole_timer_tick(\n        100, [&](Timer *, TimerNode *tnode) {}, nullptr);\n\n    TimerNode *timerNode = swoole_timer_get(timer_id);\n    ASSERT_EQ(timerNode->id, timer_id);\n    swoole_timer_free();\n}\n\nTEST(timer, delay) {\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n    uint64_t ms1 = swoole::time<std::chrono::milliseconds>();\n    uint64_t ms2 = 0;\n    long timer_id = swoole_timer_after(\n        100, [&](Timer *, TimerNode *tnode) { ms2 = swoole::time<std::chrono::milliseconds>(); }, nullptr);\n\n    TimerNode *timerNode = swoole_timer_get(timer_id);\n    swoole_timer_delay(timerNode, 100);\n    swoole_event_wait();\n    ASSERT_GE(ms2 - ms1, 100);\n    swoole_timer_del(timerNode);\n    swoole_timer_free();\n}\n\nTEST(timer, error) {\n    Timer *tmp = SwooleTG.timer;\n    SwooleTG.timer = nullptr;\n\n    swoole_timer_free();\n    swoole_timer_select();  // no effect\n    ASSERT_EQ(swoole_timer_get(1), nullptr);\n    ASSERT_FALSE(swoole_timer_clear(1));\n    ASSERT_FALSE(swoole_timer_exists(1));\n\n    long timer_id = swoole_timer_tick(\n        0, [&](Timer *, TimerNode *tnode) {}, nullptr);\n    ASSERT_EQ(timer_id, SW_ERR);\n\n    timer_id = swoole_timer_after(\n        0, [&](Timer *, TimerNode *tnode) {}, nullptr);\n    ASSERT_EQ(timer_id, SW_ERR);\n\n    swoole_timer_delay(nullptr, 100);\n    ASSERT_FALSE(swoole_timer_del(nullptr));\n    SwooleTG.timer = tmp;\n\n    swoole_timer_free();\n}\n\nTEST(timer, reinit) {\n    int timer1_count = 0;\n    int timer2_count = 0;\n\n    swoole_timer_after(\n        20,\n        [&](Timer *, TimerNode *) {\n            timer1_count++;\n            DEBUG() << \"timer2_count\" << timer2_count << \"\\n\";\n        },\n        nullptr);\n\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    sw_timer()->reinit();\n\n    uint64_t ms1 = swoole::time<std::chrono::milliseconds>();\n\n    swoole_timer_tick(\n        100,\n        [&](Timer *, TimerNode *tnode) {\n            timer2_count++;\n            DEBUG() << \"timer2_count\" << timer2_count << \"\\n\";\n            if (timer2_count == 5) {\n                swoole_timer_del(tnode);\n            }\n        },\n        nullptr);\n\n    swoole_event_wait();\n    uint64_t ms2 = swoole::time<std::chrono::milliseconds>();\n    ASSERT_LE(ms2 - ms1, 510);\n    ASSERT_EQ(timer1_count, 1);\n    ASSERT_EQ(timer2_count, 5);\n}\n\nTEST(timer, realtime_add) {\n\ttimespec ts;\n\tts.tv_sec = 1;\n\tts.tv_nsec = 900L * SW_NUM_MILLION;\n\n\tswoole::realtime_add(&ts, 1905);\n\tASSERT_EQ(ts.tv_sec, 3);\n\tASSERT_EQ(ts.tv_nsec, 805L * SW_NUM_MILLION);\n}\n"
  },
  {
    "path": "core-tests/src/os/wait.cpp",
    "content": "#include \"test_coroutine.h\"\n\nusing namespace swoole;\nusing namespace swoole::test;\nusing swoole::coroutine::System;\n\nstatic pid_t fork_child() {\n    pid_t pid = fork();\n    EXPECT_NE(pid, -1);\n\n    if (pid == 0) {\n        usleep(100000);\n        exit(0);\n    }\n    return pid;\n}\n\nstatic pid_t fork_child2() {\n    pid_t pid = fork();\n    EXPECT_NE(pid, -1);\n\n    if (pid == 0) {\n        exit(0);\n    }\n\n    usleep(100000);\n    return pid;\n}\n\nTEST(os_wait, waitpid_before_child_exit) {\n    test::coroutine::run([](void *arg) {\n        auto pid = fork_child();\n        int status = -1;\n        pid_t pid2 = swoole_coroutine_waitpid(pid, &status, 0);\n        ASSERT_EQ(status, 0);\n        ASSERT_EQ(pid, pid2);\n    });\n}\n\nTEST(os_wait, waitpid_after_child_exit) {\n    test::coroutine::run([](void *arg) {\n        pid_t pid = fork_child2();\n        int status = -1;\n        pid_t pid2 = swoole_coroutine_waitpid(pid, &status, 0);\n        ASSERT_EQ(status, 0);\n        ASSERT_EQ(pid, pid2);\n    });\n}\n\nTEST(os_wait, wait_before_child_exit) {\n    test::coroutine::run([](void *arg) {\n        pid_t pid = fork_child();\n        int status = -1;\n        pid_t pid2 = -1;\n\n        for (;;) {\n            pid2 = swoole_coroutine_wait(&status);\n            if (pid2 == pid) {\n                break;\n            }\n        }\n\n        ASSERT_EQ(WEXITSTATUS(status), 0);\n    });\n}\n\nTEST(os_wait, wait_after_child_exit) {\n    test::coroutine::run([](void *arg) {\n        pid_t pid = fork_child2();\n        int status = -1;\n        pid_t pid2 = -1;\n\n        for (;;) {\n            pid2 = swoole_coroutine_wait(&status);\n            if (pid2 == pid) {\n                break;\n            }\n        }\n\n        ASSERT_EQ(WEXITSTATUS(status), 0);\n    });\n}\n\nTEST(os_wait, waitpid_safe) {\n    test::coroutine::run([](void *arg) {\n        pid_t pid = fork_child();\n        int status = -1;\n\n        pid_t pid2 = System::waitpid_safe(pid, &status, 0);\n        ASSERT_EQ(pid2, pid);\n        ASSERT_EQ(WEXITSTATUS(status), 0);\n    });\n}\n"
  },
  {
    "path": "core-tests/src/protocol/base.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"test_coroutine.h\"\n#include \"redis_client.h\"\n#include \"swoole_redis.h\"\n\nusing namespace swoole;\nusing namespace std;\n\nconstexpr int PKG_N = 128;\nconstexpr int MAX_SIZE = 1 * 1024 * 1024 + 65536;\nconstexpr int MIN_SIZE = 512;\n\nstatic void test_protocol(Server &serv, ListenPort *port, String *pkgs) {\n    mutex lock;\n    lock.lock();\n    serv.create();\n\n    String wbuf;\n\n    for (int i = 0; i < PKG_N; i++) {\n        wbuf.append(pkgs[i]);\n    }\n\n    DEBUG() << \"data total length: \" << wbuf.length << \"\\n\";\n\n    thread t1([&]() {\n        swoole_signal_block_all();\n        lock.lock();\n\n        network::Client cli(SW_SOCK_TCP, false);\n        EXPECT_EQ(cli.connect(TEST_HOST, port->port, 1, 0), 0);\n\n        off_t offset = 0;\n        while (offset < (off_t) wbuf.length) {\n            auto n = wbuf.length - offset > 65536 ? swoole_random_int() % 65536 + 1 : wbuf.length - offset;\n            ASSERT_EQ(cli.send(wbuf.str + offset, n), n);\n            offset += n;\n        }\n\n        usleep(100000);\n    });\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); };\n\n    int recv_count = 0;\n\n    serv.onReceive = [&](Server *serv, RecvData *req) -> int {\n        EXPECT_EQ(memcmp(req->data, pkgs[recv_count].str, req->info.len), 0);\n        recv_count++;\n        if (recv_count == PKG_N) {\n            usleep(100000);\n            serv->shutdown();\n        }\n        return SW_OK;\n    };\n\n    serv.start();\n\n    t1.join();\n}\n\nTEST(protocol, length) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n\n    String pkgs[PKG_N];\n\n    for (int i = 0; i < PKG_N; i++) {\n        auto pkt_len = swoole_rand(MIN_SIZE, MAX_SIZE);\n        auto pkt_len_net = htonl(pkt_len);\n        pkgs[i].append((char *) &pkt_len_net, sizeof(pkt_len_net));\n        pkgs[i].append_random_bytes(pkt_len, false);\n    }\n\n    sw_logger()->set_level(SW_LOG_WARNING);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n    port->set_stream_protocol();\n\n    test_protocol(serv, port, pkgs);\n}\n\nTEST(protocol, length_2) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n    port->set_stream_protocol();\n\n    mutex lock;\n    lock.lock();\n    serv.create();\n\n    thread t1([&]() {\n        swoole_signal_block_all();\n        lock.lock();\n        char rbuf[32];\n        usleep(50000);\n\n        //  测试分多次发送长度\n        {\n            network::Client cli(SW_SOCK_TCP, false);\n            EXPECT_EQ(cli.connect(TEST_HOST, port->port, 1, 0), 0);\n\n            String wbuf;\n\n            auto pkt_len = swoole_rand(MIN_SIZE, MAX_SIZE);\n            auto pkt_len_net = htonl(pkt_len);\n            wbuf.append((char *) &pkt_len_net, sizeof(pkt_len_net));\n            wbuf.append_random_bytes(pkt_len, false);\n\n            ASSERT_EQ(cli.send(wbuf.str, 2), 2);\n            usleep(10);\n            ASSERT_EQ(cli.send(wbuf.str + 2, 4), 4);\n            usleep(10);\n            ASSERT_EQ(cli.send(wbuf.str + 2, wbuf.length - 6), wbuf.length - 6);\n\n            ASSERT_EQ(cli.recv(rbuf, sizeof(rbuf), 0), 3);\n            ASSERT_STREQ(rbuf, \"OK\");\n        }\n\n        //  发送 0 长度的包\n        {\n            network::Client cli(SW_SOCK_TCP, false);\n            EXPECT_EQ(cli.connect(TEST_HOST, port->port, 1, 0), 0);\n\n            auto pkt_len = 0;\n            auto pkt_len_net = htonl(pkt_len);\n\n            ASSERT_EQ(cli.send((char *) &pkt_len_net, sizeof(pkt_len)), sizeof(pkt_len));\n            ASSERT_EQ(cli.recv(rbuf, sizeof(rbuf), 0), 3);\n            ASSERT_STREQ(rbuf, \"OK\");\n        }\n\n        //  发送 INT_MAX 长度的包\n        {\n            network::Client cli(SW_SOCK_TCP, false);\n            EXPECT_EQ(cli.connect(TEST_HOST, port->port, 1, 0), 0);\n\n            auto pkt_len = INT_MAX;\n            auto pkt_len_net = htonl(pkt_len);\n\n            ASSERT_EQ(cli.send((char *) &pkt_len_net, sizeof(pkt_len)), sizeof(pkt_len));\n            ASSERT_EQ(cli.recv(rbuf, sizeof(rbuf), 0), 0);\n        }\n        usleep(50000);\n        serv.shutdown();\n    });\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); };\n\n    serv.onReceive = [&](Server *serv, RecvData *req) -> int {\n        serv->send(req->session_id(), SW_STRL(\"OK\\0\"));\n        return SW_OK;\n    };\n\n    serv.start();\n    t1.join();\n}\n\nTEST(protocol, length_3) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n    port->set_length_protocol(0, 'l', 4);\n\n    mutex lock;\n    lock.lock();\n    serv.create();\n\n    thread t1([&]() {\n        swoole_signal_block_all();\n        lock.lock();\n        char rbuf[32];\n        usleep(50000);\n\n        network::Client cli(SW_SOCK_TCP, false);\n        EXPECT_EQ(cli.connect(TEST_HOST, port->port, 1, 0), 0);\n\n        auto pkt_len = -1;\n\n        ASSERT_EQ(cli.send((char *) &pkt_len, sizeof(pkt_len)), sizeof(pkt_len));\n        ASSERT_EQ(cli.recv(rbuf, sizeof(rbuf), 0), 0);\n\n        usleep(50000);\n        serv.shutdown();\n    });\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); };\n\n    serv.onReceive = [&](Server *serv, RecvData *req) -> int {\n        serv->send(req->session_id(), SW_STRL(\"OK\\0\"));\n        return SW_OK;\n    };\n\n    serv.start();\n    t1.join();\n}\n\nTEST(protocol, eof) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n\n    String pkgs[PKG_N];\n\n    for (int i = 0; i < PKG_N; i++) {\n        pkgs[i].append_random_bytes(swoole_rand(MIN_SIZE, MAX_SIZE), true);\n        pkgs[i].append(\"\\r\\n\");\n    }\n\n    sw_logger()->set_level(SW_LOG_WARNING);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n    port->set_eof_protocol(\"\\r\\n\", false);\n\n    test_protocol(serv, port, pkgs);\n}\n\nTEST(protocol, socks5_strerror) {\n    SW_LOOP_N(16) {\n        auto error = Socks5Proxy::strerror(i);\n        if (i > 0x08) {\n            ASSERT_STREQ(\"Unknown error\", error);\n        } else {\n            ASSERT_GT(strlen(error), 5);\n        }\n    }\n}\n\nTEST(protocol, swap_byte_order) {\n    {\n        EXPECT_EQ(swoole_swap_endian16(0x1234), 0x3412);\n        EXPECT_EQ(swoole_swap_endian16(0x0001), 0x0100);\n        EXPECT_EQ(swoole_swap_endian16(0x00FF), 0xFF00);\n        EXPECT_EQ(swoole_swap_endian16(0xFF00), 0x00FF);\n        EXPECT_EQ(swoole_swap_endian16(0xFFFF), 0xFFFF);\n    }\n\n    {\n        EXPECT_EQ(swoole_swap_endian32(0x12345678), 0x78563412);\n        EXPECT_EQ(swoole_swap_endian32(0x00000001), 0x01000000);\n        EXPECT_EQ(swoole_swap_endian32(0x0000FF00), 0x00FF0000);\n        EXPECT_EQ(swoole_swap_endian32(0xFF000000), 0x000000FF);\n        EXPECT_EQ(swoole_swap_endian32(0xFFFFFFFF), 0xFFFFFFFF);\n    }\n\n    {\n        uint16_t v = 0xABCD;\n        EXPECT_EQ(swoole_swap_endian16(swoole_swap_endian16(v)), v);\n    }\n\n    {\n        uint32_t v = 0xABCDEF01;\n        EXPECT_EQ(swoole_swap_endian32(swoole_swap_endian32(v)), v);\n    }\n\n    {\n        uint64_t val = 0x1122334455667788ULL;\n        auto converted = swoole_swap_endian64(val);\n\n        auto str = (uchar *) &converted;\n        EXPECT_EQ(str[0], 0x11);\n        EXPECT_EQ(str[1], 0x22);\n        EXPECT_EQ(str[2], 0x33);\n        EXPECT_EQ(str[3], 0x44);\n        EXPECT_EQ(str[4], 0x55);\n        EXPECT_EQ(str[5], 0x66);\n        EXPECT_EQ(str[6], 0x77);\n        EXPECT_EQ(str[7], 0x88);\n    }\n}\n\n// Helper function to create binary data for testing\ntemplate <typename T>\nvoid createBinaryData(T value, char *buffer) {\n    memcpy(buffer, &value, sizeof(T));\n}\n\nTEST(protocol, unpack) {\n    // Tests for 8-bit integer formats\n    {\n        char buffer[8];\n\n        // Test signed char ('c')\n        int8_t c_val = -42;\n        createBinaryData(c_val, buffer);\n        EXPECT_EQ(swoole_unpack('c', buffer), -42);\n\n        // Test unsigned char ('C')\n        uint8_t C_val = 200;\n        createBinaryData(C_val, buffer);\n        EXPECT_EQ(swoole_unpack('C', buffer), 200);\n\n        // Test extreme values\n        createBinaryData<int8_t>(INT8_MIN, buffer);\n        EXPECT_EQ(swoole_unpack('c', buffer), INT8_MIN);\n\n        createBinaryData<int8_t>(INT8_MAX, buffer);\n        EXPECT_EQ(swoole_unpack('c', buffer), INT8_MAX);\n\n        createBinaryData<uint8_t>(UINT8_MAX, buffer);\n        EXPECT_EQ(swoole_unpack('C', buffer), UINT8_MAX);\n    }\n\n    // Tests for 16-bit integer formats\n    {\n        char buffer[8];\n\n        // Test signed short ('s')\n        int16_t s_val = -12345;\n        createBinaryData(s_val, buffer);\n        EXPECT_EQ(swoole_unpack('s', buffer), -12345);\n\n        // Test unsigned short ('S')\n        uint16_t S_val = 54321;\n        createBinaryData(S_val, buffer);\n        EXPECT_EQ(swoole_unpack('S', buffer), 54321);\n\n        // Test big-endian unsigned short ('n')\n        uint16_t n_val = 0x1234;\n        uint16_t n_be = (n_val >> 8) | (n_val << 8);  // Convert to big-endian\n        createBinaryData(n_be, buffer);\n        EXPECT_EQ(swoole_unpack('n', buffer), 0x1234);\n\n        // Test little-endian unsigned short ('v')\n        uint16_t v_val = 0x1234;\n        createBinaryData(v_val, buffer);\n        EXPECT_EQ(swoole_unpack('v', buffer), 0x1234);\n\n        // Test extreme values\n        createBinaryData<int16_t>(INT16_MIN, buffer);\n        EXPECT_EQ(swoole_unpack('s', buffer), INT16_MIN);\n\n        createBinaryData<int16_t>(INT16_MAX, buffer);\n        EXPECT_EQ(swoole_unpack('s', buffer), INT16_MAX);\n\n        createBinaryData<uint16_t>(UINT16_MAX, buffer);\n        EXPECT_EQ(swoole_unpack('S', buffer), UINT16_MAX);\n    }\n\n    // Tests for 32-bit integer formats\n    {\n        char buffer[8];\n\n        // Test signed long ('l')\n        int32_t l_val = -123456789;\n        createBinaryData(l_val, buffer);\n        EXPECT_EQ(swoole_unpack('l', buffer), -123456789);\n\n        // Test unsigned long ('L')\n        uint32_t L_val = 3000000000;\n        createBinaryData(L_val, buffer);\n        EXPECT_EQ(swoole_unpack('L', buffer), 3000000000);\n\n        // Test big-endian unsigned long ('N')\n        uint32_t N_val = 0x12345678;\n        uint32_t N_be =\n            ((N_val & 0xFF) << 24) | ((N_val & 0xFF00) << 8) | ((N_val & 0xFF0000) >> 8) | ((N_val & 0xFF000000) >> 24);\n        createBinaryData(N_be, buffer);\n        EXPECT_EQ(swoole_unpack('N', buffer), 0x12345678);\n\n        // Test little-endian unsigned long ('V')\n        uint32_t V_val = 0x12345678;\n        createBinaryData(V_val, buffer);\n        EXPECT_EQ(swoole_unpack('V', buffer), 0x12345678);\n\n        // Test extreme values\n        createBinaryData<int32_t>(INT32_MIN, buffer);\n        EXPECT_EQ(swoole_unpack('l', buffer), INT32_MIN);\n\n        createBinaryData<int32_t>(INT32_MAX, buffer);\n        EXPECT_EQ(swoole_unpack('l', buffer), INT32_MAX);\n\n        createBinaryData<uint32_t>(UINT32_MAX, buffer);\n        EXPECT_EQ(swoole_unpack('L', buffer), UINT32_MAX);\n    }\n\n    // Tests for 64-bit integer formats\n    {\n        char buffer[8];\n\n        // Test signed long long ('q')\n        int64_t q_val = -1234567890123456789LL;\n        createBinaryData(q_val, buffer);\n        EXPECT_EQ(swoole_unpack('q', buffer), -1234567890123456789LL);\n\n        // Test unsigned long long ('Q')\n        uint64_t Q_val = 10234567890123456789ULL;\n        createBinaryData(Q_val, buffer);\n        EXPECT_EQ(swoole_unpack('Q', buffer), 10234567890123456789ULL);\n\n        // Test big-endian unsigned long long ('J')\n        uint64_t J_val = 0x123456789ABCDEF0ULL;\n        uint64_t J_be = swoole_swap_endian64(J_val);  // Use our swap function for test\n        createBinaryData(J_be, buffer);\n        EXPECT_EQ(swoole_unpack('J', buffer), 0x123456789ABCDEF0ULL);\n\n        // Test little-endian unsigned long long ('P')\n        uint64_t P_val = 0x123456789ABCDEF0ULL;\n        createBinaryData(P_val, buffer);\n        EXPECT_EQ(swoole_unpack('P', buffer), 0x123456789ABCDEF0ULL);\n\n        // Test extreme values (be careful with signed min/max due to two's complement)\n        createBinaryData<int64_t>(INT64_MIN, buffer);\n        EXPECT_EQ(swoole_unpack('q', buffer), INT64_MIN);\n\n        createBinaryData<int64_t>(INT64_MAX, buffer);\n        EXPECT_EQ(swoole_unpack('q', buffer), INT64_MAX);\n\n        // For UINT64_MAX, be aware that the return type is int64_t, so this might not work as expected\n        // This test might fail due to the limitation of the return type\n        createBinaryData<uint64_t>(UINT64_MAX, buffer);\n        EXPECT_EQ(swoole_unpack('Q', buffer), (int64_t) UINT64_MAX);\n    }\n\n    // Tests for machine-dependent integer formats\n    {\n        char buffer[8];\n\n        // Test signed integer ('i')\n        int i_val = -987654321;\n        createBinaryData(i_val, buffer);\n        EXPECT_EQ(swoole_unpack('i', buffer), -987654321);\n\n        // Test unsigned integer ('I')\n        unsigned int I_val = 3000000000;\n        createBinaryData(I_val, buffer);\n        EXPECT_EQ(swoole_unpack('I', buffer), 3000000000);\n\n        // Test extreme values\n        createBinaryData<int>(INT_MIN, buffer);\n        EXPECT_EQ(swoole_unpack('i', buffer), INT_MIN);\n\n        createBinaryData<int>(INT_MAX, buffer);\n        EXPECT_EQ(swoole_unpack('i', buffer), INT_MAX);\n\n        createBinaryData<unsigned int>(UINT_MAX, buffer);\n        EXPECT_EQ(swoole_unpack('I', buffer), (int64_t) UINT_MAX);\n    }\n\n    // Test for invalid format specifier\n    {\n        char buffer[8] = {0};\n\n        // Test invalid format specifier\n        EXPECT_EQ(swoole_unpack('x', buffer), -1);\n        EXPECT_EQ(swoole_unpack('?', buffer), -1);\n        EXPECT_EQ(swoole_unpack('Z', buffer), -1);\n    }\n\n    // Test for endianness-specific behavior\n    {\n        char buffer[8];\n\n        // Test that 'n' and 'v' formats handle endianness correctly\n        buffer[0] = 0x12;\n        buffer[1] = 0x34;\n        EXPECT_EQ(swoole_unpack('n', buffer), 0x1234);\n\n        buffer[0] = 0x34;\n        buffer[1] = 0x12;\n        EXPECT_EQ(swoole_unpack('v', buffer), 0x1234);\n\n        // Test that 'N' and 'V' formats handle endianness correctly\n        buffer[0] = 0x12;\n        buffer[1] = 0x34;\n        buffer[2] = 0x56;\n        buffer[3] = 0x78;\n        EXPECT_EQ(swoole_unpack('N', buffer), 0x12345678);\n\n        buffer[0] = 0x78;\n        buffer[1] = 0x56;\n        buffer[2] = 0x34;\n        buffer[3] = 0x12;\n        EXPECT_EQ(swoole_unpack('V', buffer), 0x12345678);\n\n        // Test that 'J' and 'P' formats handle endianness correctly\n        buffer[0] = 0x12;\n        buffer[1] = 0x34;\n        buffer[2] = 0x56;\n        buffer[3] = 0x78;\n        buffer[4] = 0x9A;\n        buffer[5] = 0xBC;\n        buffer[6] = 0xDE;\n        buffer[7] = 0xF0;\n        EXPECT_EQ(swoole_unpack('J', buffer), 0x123456789ABCDEF0ULL);\n\n        buffer[0] = 0xF0;\n        buffer[1] = 0xDE;\n        buffer[2] = 0xBC;\n        buffer[3] = 0x9A;\n        buffer[4] = 0x78;\n        buffer[5] = 0x56;\n        buffer[6] = 0x34;\n        buffer[7] = 0x12;\n        EXPECT_EQ(swoole_unpack('P', buffer), 0x123456789ABCDEF0ULL);\n    }\n\n    {\n        char buffer[8];\n\n        // Test that 'n' format uses ntohs() correctly\n        uint16_t test16 = 0x1234;\n        uint16_t be16 = htons(test16);  // Convert to network byte order\n        createBinaryData(be16, buffer);\n        EXPECT_EQ(swoole_unpack('n', buffer), 0x1234);\n\n        // Test that 'N' format uses ntohl() correctly\n        uint32_t test32 = 0x12345678;\n        uint32_t be32 = htonl(test32);  // Convert to network byte order\n        createBinaryData(be32, buffer);\n        EXPECT_EQ(swoole_unpack('N', buffer), 0x12345678);\n\n        // Test that 'J' format uses swoole_ntoh64() correctly\n        uint64_t test64 = 0x123456789ABCDEF0ULL;\n        uint64_t be64 = swoole_hton64(test64);  // Convert to network byte order\n        createBinaryData(be64, buffer);\n        EXPECT_EQ(swoole_unpack('J', buffer), 0x123456789ABCDEF0ULL);\n    }\n}\n\nTEST(protocol, hton64) {\n    {\n        uint64_t val = 0x1122334455667788ULL;\n        uint64_t converted = swoole_hton64(val);\n\n        auto str = (uchar *) &converted;\n        EXPECT_EQ(str[0], 0x11);\n        EXPECT_EQ(str[1], 0x22);\n        EXPECT_EQ(str[2], 0x33);\n        EXPECT_EQ(str[3], 0x44);\n        EXPECT_EQ(str[4], 0x55);\n        EXPECT_EQ(str[5], 0x66);\n        EXPECT_EQ(str[6], 0x77);\n        EXPECT_EQ(str[7], 0x88);\n\n        uint64_t reversed = swoole_ntoh64(converted);\n        EXPECT_EQ(reversed, val);\n    }\n\n    {\n        uint64_t min_val = 0ULL;\n        uint64_t min_converted = swoole_hton64(min_val);\n\n        auto min_str = (unsigned char *) &min_converted;\n        for (int i = 0; i < 8; i++) {\n            EXPECT_EQ(min_str[i], 0x00) << \"Byte \" << i << \" should be 0x00\";\n        }\n\n        EXPECT_EQ(swoole_ntoh64(min_converted), min_val);\n\n        // 测试最大值\n        uint64_t max_val = UINT64_MAX;\n        uint64_t max_converted = swoole_hton64(max_val);\n\n        auto max_str = (unsigned char *) &max_converted;\n        for (int i = 0; i < 8; i++) {\n            EXPECT_EQ(max_str[i], 0xFF) << \"Byte \" << i << \" should be 0xFF\";\n        }\n\n        EXPECT_EQ(swoole_ntoh64(max_converted), max_val);\n    }\n\n    {\n        uint64_t alt_pattern = 0xAAAAAAAAAAAAAAAAULL;\n        uint64_t alt_converted = swoole_hton64(alt_pattern);\n        EXPECT_EQ(swoole_ntoh64(alt_converted), alt_pattern);\n\n        uint64_t alt_pattern2 = 0x5555555555555555ULL;\n        uint64_t alt_converted2 = swoole_hton64(alt_pattern2);\n        EXPECT_EQ(swoole_ntoh64(alt_converted2), alt_pattern2);\n\n        // 测试单字节模式\n        for (int i = 0; i < 8; i++) {\n            uint64_t single_byte = 0xFFULL << (i * 8);\n            uint64_t converted = swoole_hton64(single_byte);\n            EXPECT_EQ(swoole_ntoh64(converted), single_byte) << \"Failed for byte position \" << i;\n        }\n    }\n\n    {\n        for (int i = 0; i < 100; i++) {\n            uint64_t random_val = swoole_random_int();\n            uint64_t converted = swoole_hton64(random_val);\n            uint64_t reversed = swoole_ntoh64(converted);\n\n            EXPECT_EQ(reversed, random_val) << \"Failed for random value: 0x\" << std::hex << random_val;\n        }\n    }\n\n    {\n        uint64_t test_val = 0x0102030405060708ULL;\n        uint64_t converted = swoole_hton64(test_val);\n\n        auto bytes = (unsigned char *) &converted;\n\n        EXPECT_EQ(bytes[0], 0x01);\n        EXPECT_EQ(bytes[1], 0x02);\n        EXPECT_EQ(bytes[2], 0x03);\n        EXPECT_EQ(bytes[3], 0x04);\n        EXPECT_EQ(bytes[4], 0x05);\n        EXPECT_EQ(bytes[5], 0x06);\n        EXPECT_EQ(bytes[6], 0x07);\n        EXPECT_EQ(bytes[7], 0x08);\n    }\n\n    {\n        for (int i = 0; i < 100; i++) {\n            uint64_t val = swoole_random_int();\n            EXPECT_EQ(swoole_ntoh64(swoole_hton64(val)), val) << \"hton64->ntoh64 failed for 0x\" << std::hex << val;\n            EXPECT_EQ(swoole_hton64(swoole_ntoh64(val)), val) << \"ntoh64->hton64 failed for 0x\" << std::hex << val;\n        }\n    }\n}\n"
  },
  {
    "path": "core-tests/src/protocol/base64.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"swoole_base64.h\"\n\nTEST(base64, encode) {\n    char inbuf[1024];\n    char outbuf[2048];\n\n    auto n = swoole_random_bytes(inbuf, sizeof(inbuf) - 1);\n    auto n2 = swoole::base64_encode((uchar *) inbuf, n, outbuf);\n    ASSERT_GT(n2, n);\n}\n\nTEST(base64, decode) {\n    const char *inbuf = \"aGVsbG8gd29ybGQ=\";\n    char outbuf[2048];\n\n    auto n2 = swoole::base64_decode(inbuf, strlen(inbuf), outbuf);\n    ASSERT_EQ(std::string(outbuf, n2), \"hello world\");\n}\n"
  },
  {
    "path": "core-tests/src/protocol/http2.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"test_coroutine.h\"\n#include \"redis_client.h\"\n#include \"swoole_server.h\"\n#include \"swoole_http.h\"\n#include \"swoole_http2.h\"\n\n#include <nghttp2/nghttp2.h>\n#include <nghttp2/nghttp2ver.h>\n\nusing namespace swoole;\nusing namespace std;\nusing http_server::Context;\nusing network::Client;\nusing network::SyncClient;\nusing swoole::network::AsyncClient;\n\nconst std::string REDIS_TEST_KEY = \"key-swoole\";\nconst std::string REDIS_TEST_VALUE = \"value-swoole\";\n\nTEST(http2, default_settings) {\n    ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE), SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE);\n    ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH), SW_HTTP2_DEFAULT_ENABLE_PUSH);\n    ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS),\n              SW_HTTP2_DEFAULT_MAX_CONCURRENT_STREAMS);\n    ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE), SW_HTTP2_DEFAULT_INIT_WINDOW_SIZE);\n    ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE), SW_HTTP2_DEFAULT_MAX_FRAME_SIZE);\n    ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE),\n              SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE);\n\n    http2::Settings _settings = {\n        (uint32_t) swoole_rand(1, 100000),\n        (uint32_t) swoole_rand(1, 100000),\n        (uint32_t) swoole_rand(1, 100000),\n        (uint32_t) swoole_rand(1, 100000),\n        (uint32_t) swoole_rand(1, 100000),\n        (uint32_t) swoole_rand(1, 100000),\n    };\n\n    http2::put_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE, _settings.header_table_size);\n    http2::put_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH, _settings.enable_push);\n    http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, _settings.max_concurrent_streams);\n    http2::put_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE, _settings.init_window_size);\n    http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE, _settings.max_frame_size);\n    http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, _settings.max_header_list_size);\n\n    ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE), _settings.header_table_size);\n    ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH), _settings.enable_push);\n    ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS), _settings.max_concurrent_streams);\n    ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE), _settings.init_window_size);\n    ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE), _settings.max_frame_size);\n    ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE), _settings.max_header_list_size);\n}\n\nTEST(http2, pack_setting_frame) {\n    char frame[SW_HTTP2_SETTING_FRAME_SIZE + SW_HTTP2_FRAME_HEADER_SIZE];\n    http2::Settings settings_1{};\n    http2::init_settings(&settings_1);\n    size_t n = http2::pack_setting_frame(frame, settings_1, false);\n\n    ASSERT_GT(n, 16);\n\n    http2::Settings settings_2{};\n    http2::unpack_setting_data(\n        frame + SW_HTTP2_FRAME_HEADER_SIZE, n, [&settings_2](uint16_t id, uint32_t value) -> ReturnCode {\n            switch (id) {\n            case SW_HTTP2_SETTING_HEADER_TABLE_SIZE:\n                settings_2.header_table_size = value;\n                break;\n            case SW_HTTP2_SETTINGS_ENABLE_PUSH:\n                settings_2.enable_push = value;\n                break;\n            case SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:\n                settings_2.max_concurrent_streams = value;\n                break;\n            case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE:\n                settings_2.init_window_size = value;\n                break;\n            case SW_HTTP2_SETTINGS_MAX_FRAME_SIZE:\n                settings_2.max_frame_size = value;\n                break;\n            case SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:\n                settings_2.max_header_list_size = value;\n                break;\n            default:\n                return SW_ERROR;\n            }\n            return SW_SUCCESS;\n        });\n\n    ASSERT_MEMEQ(&settings_1, &settings_2, sizeof(settings_2));\n}\n\n#define HTTP2_GET_TYPE_TEST(t) ASSERT_STREQ(http2::get_type(SW_HTTP2_TYPE_##t), #t)\n\nTEST(http2, get_type) {\n    HTTP2_GET_TYPE_TEST(DATA);\n    HTTP2_GET_TYPE_TEST(HEADERS);\n    HTTP2_GET_TYPE_TEST(PRIORITY);\n    HTTP2_GET_TYPE_TEST(RST_STREAM);\n    HTTP2_GET_TYPE_TEST(SETTINGS);\n    HTTP2_GET_TYPE_TEST(PUSH_PROMISE);\n    HTTP2_GET_TYPE_TEST(PING);\n    HTTP2_GET_TYPE_TEST(GOAWAY);\n    HTTP2_GET_TYPE_TEST(WINDOW_UPDATE);\n    HTTP2_GET_TYPE_TEST(CONTINUATION);\n}\n\nTEST(http2, get_type_color) {\n    SW_LOOP_N(SW_HTTP2_TYPE_GOAWAY + 2) {\n        ASSERT_GE(http2::get_type_color(i), 0);\n    }\n}\n\nstruct Http2Session {\n    SessionId fd;\n    nghttp2_session *session;\n    Server *server;\n    std::unordered_map<int32_t, std::string> stream_paths;\n    std::unordered_map<int32_t, std::string> stream_data;\n\n    Http2Session(SessionId _fd, Server *_serv) : fd(_fd), session(nullptr), server(_serv) {}\n    ~Http2Session() {\n        if (session) {\n            nghttp2_session_del(session);\n            session = nullptr;\n        }\n    }\n};\n\n#define CHECK_NGHTTP2(expr, error_msg)                                                                                 \\\n    do {                                                                                                               \\\n        int rv = (expr);                                                                                               \\\n        if (rv != 0) {                                                                                                 \\\n            swoole_error_log(SW_LOG_ERROR, \"%s: %s\", error_msg, nghttp2_strerror(rv));                                 \\\n            return -1;                                                                                                 \\\n        }                                                                                                              \\\n    } while (0)\n\nstd::unordered_map<SessionId, std::shared_ptr<Http2Session>> sessions;\n\nstatic nghttp2_settings_entry default_settings[] = {\n    {\n        NGHTTP2_SETTINGS_HEADER_TABLE_SIZE,\n        SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE,\n    },\n    {\n        NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,\n        SW_HTTP2_DEFAULT_MAX_CONCURRENT_STREAMS,\n    },\n    {\n        NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE,\n        SW_HTTP2_DEFAULT_INIT_WINDOW_SIZE,\n    },\n    {\n        NGHTTP2_SETTINGS_MAX_FRAME_SIZE,\n        SW_HTTP2_DEFAULT_MAX_FRAME_SIZE,\n    },\n    {\n        NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE,\n        SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE,\n    },\n};\n\nstatic ssize_t send_callback(nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data) {\n    auto http2_session = static_cast<Http2Session *>(user_data);\n    Server *server = static_cast<Server *>(http2_session->server);\n\n    bool ret = server->send(http2_session->fd, reinterpret_cast<const char *>(data), length);\n    if (!ret) {\n        return NGHTTP2_ERR_CALLBACK_FAILURE;\n    }\n\n    return length;\n}\n\nstatic int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data) {\n    return 0;\n}\n\n// 处理头部回调\nstatic int on_header_callback(nghttp2_session *session,\n                              const nghttp2_frame *frame,\n                              const uint8_t *name,\n                              size_t namelen,\n                              const uint8_t *value,\n                              size_t valuelen,\n                              uint8_t flags,\n                              void *user_data) {\n    if (frame->hd.type != NGHTTP2_HEADERS || frame->headers.cat != NGHTTP2_HCAT_REQUEST) {\n        return 0;\n    }\n\n    DEBUG() << \"Header: \" << std::string(reinterpret_cast<const char *>(name), namelen) << \": \"\n            << std::string(reinterpret_cast<const char *>(value), valuelen) << std::endl;\n\n    return 0;\n}\n\n// 处理请求开始回调\nstatic int on_begin_headers_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) {\n    if (frame->hd.type != NGHTTP2_HEADERS || frame->headers.cat != NGHTTP2_HCAT_REQUEST) {\n        return 0;\n    }\n\n    DEBUG() << \"New request started on stream ID: \" << frame->hd.stream_id << std::endl;\n\n    return 0;\n}\n\nstatic void handle_request(nghttp2_session *session, int32_t stream_id, Http2Session *http2_session);\n\nstatic int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) {\n    auto http2_session = static_cast<Http2Session *>(user_data);\n\n    switch (frame->hd.type) {\n    case NGHTTP2_HEADERS:\n        if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {\n            swoole_trace_log(SW_TRACE_HTTP2, \"Received HEADERS frame for stream %d\", frame->hd.stream_id);\n\n            if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {\n                handle_request(session, frame->hd.stream_id, http2_session);\n            }\n        }\n        break;\n    case NGHTTP2_DATA:\n        swoole_trace_log(SW_TRACE_HTTP2, \"Received DATA frame for stream %d\", frame->hd.stream_id);\n\n        if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {\n            handle_request(session, frame->hd.stream_id, http2_session);\n        }\n        break;\n    }\n\n    return 0;\n}\n\nstatic int on_data_chunk_recv_callback(\n    nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) {\n    auto http2_session = static_cast<Http2Session *>(user_data);\n\n    // 将数据块添加到对应流的数据中\n    http2_session->stream_data[stream_id].append(reinterpret_cast<const char *>(data), len);\n\n    swoole_trace_log(SW_TRACE_HTTP2, \"Received %zu bytes of DATA for stream %d\", len, stream_id);\n\n    return 0;\n}\n\nstatic int on_frame_not_send_callback(nghttp2_session *session,\n                                      const nghttp2_frame *frame,\n                                      int lib_error_code,\n                                      void *user_data) {\n    // 处理帧发送失败\n    std::cerr << \"Failed to send frame type: \" << frame->hd.type << std::endl;\n    return 0;\n}\n\nstatic int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) {\n    if (frame->hd.type == NGHTTP2_WINDOW_UPDATE) {\n        DEBUG() << \"Window update sent: stream=\" << frame->hd.stream_id\n                << \", increment=\" << frame->window_update.window_size_increment << std::endl;\n    }\n    return 0;\n}\n\nstatic ssize_t string_read_callback(nghttp2_session *session,\n                                    int32_t stream_id,\n                                    uint8_t *buf,\n                                    size_t length,\n                                    uint32_t *data_flags,\n                                    nghttp2_data_source *source,\n                                    void *user_data) {\n    const char *data = static_cast<const char *>(source->ptr);\n    size_t datalen = strlen(data);\n\n    if (datalen <= length) {\n        memcpy(buf, data, datalen);\n        *data_flags |= NGHTTP2_DATA_FLAG_EOF;\n        return datalen;\n    } else {\n        memcpy(buf, data, length);\n        return length;\n    }\n}\n\nstatic void handle_request(nghttp2_session *session, int32_t stream_id, Http2Session *http2_session) {\n    // 获取路径\n    std::string path = \"/\";\n    auto path_it = http2_session->stream_paths.find(stream_id);\n    if (path_it != http2_session->stream_paths.end()) {\n        path = path_it->second;\n    }\n\n    // 获取请求体\n    std::string request_body;\n    auto body_it = http2_session->stream_data.find(stream_id);\n    if (body_it != http2_session->stream_data.end()) {\n        request_body = body_it->second;\n    }\n\n    swoole_trace_log(SW_TRACE_HTTP2,\n                     \"Request fully received on stream %d, path: %s, body length: %zu\",\n                     stream_id,\n                     path.c_str(),\n                     request_body.length());\n\n    auto header_server = \"nghttp2-server/\" NGHTTP2_VERSION;\n    // 准备响应头\n    nghttp2_nv hdrs[] = {\n        {(uint8_t *) \":status\", (uint8_t *) \"200\", 7, 3, NGHTTP2_NV_FLAG_NONE},\n        {(uint8_t *) \"content-type\", (uint8_t *) \"text/html\", 12, 9, NGHTTP2_NV_FLAG_NONE},\n        {(uint8_t *) \"server\", (uint8_t *) header_server, 6, strlen(header_server), NGHTTP2_NV_FLAG_NONE}};\n\n    if (path == \"/\" || path == \"/index.html\") {\n        const char *body = \"<html><body><h1>Welcome to HTTP/2 Server</h1>\"\n                           \"<p>This is a simple HTTP/2 server implementation.</p>\"\n                           \"</body></html>\";\n\n        nghttp2_data_provider data_prd;\n        data_prd.source.ptr = (void *) body;\n        data_prd.read_callback = string_read_callback;\n\n        // 提交响应\n        int rv = nghttp2_submit_response(session, stream_id, hdrs, sizeof(hdrs) / sizeof(hdrs[0]), &data_prd);\n        if (rv != 0) {\n            swoole_error_log(\n                SW_LOG_ERROR, SW_ERROR_HTTP2_INTERNAL_ERROR, \"Failed to submit response: %s\", nghttp2_strerror(rv));\n            return;\n        }\n    } else {\n        // 404 Not Found\n        nghttp2_nv error_hdrs[] = {{(uint8_t *) \":status\", (uint8_t *) \"404\", 7, 3, NGHTTP2_NV_FLAG_NONE},\n                                   {(uint8_t *) \"content-type\", (uint8_t *) \"text/html\", 12, 9, NGHTTP2_NV_FLAG_NONE},\n                                   {(uint8_t *) \"server\", (uint8_t *) header_server, 6, 17, NGHTTP2_NV_FLAG_NONE}};\n\n        const char *body = \"<html><body><h1>404 Not Found</h1>\"\n                           \"<p>The requested resource was not found on this server.</p>\"\n                           \"</body></html>\";\n\n        nghttp2_data_provider data_prd;\n        data_prd.source.ptr = (void *) body;\n        data_prd.read_callback = string_read_callback;\n\n        nghttp2_submit_response(session, stream_id, error_hdrs, sizeof(error_hdrs) / sizeof(error_hdrs[0]), &data_prd);\n    }\n\n    nghttp2_session_send(session);\n}\n\nstatic void http2_send_settings(Http2Session *session_data, const nghttp2_settings_entry *settings, size_t num) {\n    auto rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, settings, num);\n    if (rv != 0) {\n        swoole_error_log(\n            SW_LOG_ERROR, SW_ERROR_HTTP2_INTERNAL_ERROR, \"Failed to submit settings: %s\", nghttp2_strerror(rv));\n        return;\n    }\n    nghttp2_session_send(session_data->session);\n}\n\nstatic std::shared_ptr<Http2Session> create_http2_session(Server *serv, SessionId fd) {\n    auto session_data = std::make_shared<Http2Session>(fd, serv);\n\n    nghttp2_session_callbacks *callbacks;\n    int rv = nghttp2_session_callbacks_new(&callbacks);\n    if (rv != 0) {\n        swoole_warning(\"Failed to create nghttp2 callbacks: %s\", nghttp2_strerror(rv));\n        return nullptr;\n    }\n\n    nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);\n    nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback);\n    nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, on_stream_close_callback);\n    nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header_callback);\n    nghttp2_session_callbacks_set_on_begin_headers_callback(callbacks, on_begin_headers_callback);\n    nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, on_data_chunk_recv_callback);\n    nghttp2_session_callbacks_set_on_frame_not_send_callback(callbacks, on_frame_not_send_callback);\n    nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, on_frame_send_callback);\n    nghttp2_session_callbacks_set_on_frame_not_send_callback(callbacks, on_frame_not_send_callback);\n\n    rv = nghttp2_session_server_new(&session_data->session, callbacks, session_data.get());\n    nghttp2_session_callbacks_del(callbacks);\n\n    if (rv != 0) {\n        swoole_error_log(\n            SW_LOG_ERROR, SW_ERROR_HTTP2_INTERNAL_ERROR, \"Failed to create nghttp2 session: %s\", nghttp2_strerror(rv));\n        return nullptr;\n    }\n\n    nghttp2_session_set_user_data(session_data->session, session_data.get());\n\n    return session_data;\n}\n\nstatic void test_ssl_http2(Server::Mode mode) {\n    Server serv(mode);\n    serv.worker_num = 1;\n    swoole_set_log_level(SW_LOG_INFO);\n\n    Mutex *lock = new Mutex(true);\n    lock->lock();\n\n    const int server_port = __LINE__ + TEST_PORT;\n    ListenPort *port = serv.add_port((enum swSocketType)(SW_SOCK_TCP | SW_SOCK_SSL), TEST_HOST, server_port);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    port->open_http2_protocol = 1;\n    port->open_http_protocol = 1;\n    port->open_websocket_protocol = 1;\n    port->set_ssl_cert_file(test::get_ssl_dir() + \"/server.crt\");\n    port->set_ssl_key_file(test::get_ssl_dir() + \"/server.key\");\n    port->ssl_context->http = 1;\n    port->ssl_context->http_v2 = 1;\n    port->ssl_init();\n\n    ASSERT_EQ(serv.create(), SW_OK);\n    thread t1;\n    serv.onStart = [&lock, &t1](Server *serv) {\n        t1 = thread([=]() {\n            swoole_signal_block_all();\n            lock->lock();\n\n            auto cmd = \"nghttp -v -y https://127.0.0.1:\" + std::to_string(server_port) + \"/\";\n            pid_t pid;\n            auto _pipe = swoole_shell_exec(cmd.c_str(), &pid, 1);\n            String buf(1024);\n            while (1) {\n                auto n = read(_pipe, buf.str + buf.length, buf.size - buf.length);\n                if (n > 0) {\n                    buf.grow(n);\n                    continue;\n                }\n                break;\n            }\n\n            int status;\n            ASSERT_EQ(waitpid(pid, &status, 0), pid);\n            close(_pipe);\n\n            usleep(10000);\n\n            DEBUG() << \"NGHTTP2 VERSION: \" << NGHTTP2_VERSION << std::endl;\n            DEBUG() << buf.to_std_string();\n\n            EXPECT_TRUE(buf.contains(\"user-agent: nghttp2/\"));\n            // FIXME There is a bug in nghttp's processing of settings frames,\n            // so it can only give up detecting response content.\n            // EXPECT_TRUE(buf.contains(\"Welcome to HTTP/2 Server\"));\n\n            serv->shutdown();\n        });\n    };\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock->unlock(); };\n\n    serv.onConnect = [](Server *serv, DataHead *ev) {\n        SessionId fd = ev->fd;\n        DEBUG() << \"New connection: \" << fd << std::endl;\n\n        auto session = create_http2_session(serv, fd);\n        if (!session) {\n            serv->close(fd);\n            return;\n        }\n\n        sessions[fd] = session;\n        ssize_t consumed = nghttp2_session_mem_recv(\n            session->session, (uint8_t *) SW_HTTP2_PRI_STRING, sizeof(SW_HTTP2_PRI_STRING) - 1);\n        if (consumed < 0) {\n            swoole_error_log(SW_LOG_ERROR,\n                             SW_ERROR_HTTP2_INTERNAL_ERROR,\n                             \"nghttp2_session_mem_recv() error: %s\",\n                             nghttp2_strerror((int) consumed));\n            serv->close(fd);\n            return;\n        }\n        http2_send_settings(session.get(), default_settings, sizeof(default_settings) / sizeof(default_settings[0]));\n    };\n\n    serv.onClose = [](Server *serv, DataHead *ev) {\n        SessionId fd = ev->fd;\n        DEBUG() << \"Close connection: \" << fd << std::endl;\n        sessions.erase(fd);\n    };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        SessionId fd = req->info.fd;\n        std::shared_ptr<Http2Session> session;\n        if (sessions.find(fd) == sessions.end()) {\n            serv->close(fd);\n            return SW_ERR;\n        }\n\n        session = sessions[fd];\n        const uint8_t *data_ptr = reinterpret_cast<const uint8_t *>(req->data);\n        size_t data_len = req->info.len;\n\n        ssize_t consumed = nghttp2_session_mem_recv(session->session, data_ptr, data_len);\n        if (consumed < 0) {\n            swoole_error_log(SW_LOG_ERROR,\n                             SW_ERROR_HTTP2_INTERNAL_ERROR,\n                             \"nghttp2_session_mem_recv() error: %s\",\n                             nghttp2_strerror((int) consumed));\n            serv->close(fd);\n            return SW_ERR;\n        }\n\n        if (nghttp2_session_want_write(session->session)) {\n            nghttp2_session_send(session->session);\n        }\n\n        return SW_OK;\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n\n    t1.join();\n    delete lock;\n}\n\nTEST(http2, ssl) {\n    test_ssl_http2(Server::MODE_BASE);\n}\n"
  },
  {
    "path": "core-tests/src/protocol/mime_type.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"swoole_mime_type.h\"\n\nusing namespace swoole;\n\nTEST(mime_type, get) {\n    auto result = mime_type::get(\"test.html.json\");\n    ASSERT_EQ(result, \"application/json\");\n}\n\nTEST(mime_type, exists) {\n    ASSERT_TRUE(mime_type::exists(\"test.html.json\"));\n}\n\nTEST(mime_type, set) {\n    std::string test_mime_type(\"application/swoole-core-test\");\n    mime_type::set(\"swoole_test\", test_mime_type);\n\n    auto result = mime_type::get(\"test.swoole_test\");\n    ASSERT_EQ(result, test_mime_type);\n}\n\nTEST(mime_type, add) {\n    std::string test_mime_type(\"application/swoole-core-test2\");\n    ASSERT_TRUE(mime_type::add(\"swoole_test2\", test_mime_type));\n    ASSERT_FALSE(mime_type::add(\"swoole_test2\", test_mime_type));\n\n    auto result = mime_type::get(\"test.swoole_test2\");\n    ASSERT_EQ(result, test_mime_type);\n}\n\nTEST(mime_type, del) {\n    ASSERT_TRUE(mime_type::del(\"json\"));\n    ASSERT_FALSE(mime_type::exists(\"test.html.json\"));\n}\n"
  },
  {
    "path": "core-tests/src/protocol/redis.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"test_coroutine.h\"\n#include \"redis_client.h\"\n#include \"swoole_redis.h\"\n\nusing namespace swoole;\nusing namespace std;\n\nconst std::string REDIS_TEST_KEY = \"key-swoole\";\nconst std::string REDIS_TEST_VALUE = \"value-swoole\";\n\nTEST(redis, get) {\n    test::coroutine::run([](void *arg) {\n        RedisClient redis;\n        ASSERT_TRUE(redis.Connect(\"127.0.0.1\", 6379));\n        ASSERT_TRUE(redis.Set(REDIS_TEST_KEY, REDIS_TEST_VALUE));\n        ASSERT_EQ(redis.Get(REDIS_TEST_KEY), REDIS_TEST_VALUE);\n    });\n}\n\nTEST(redis, server) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n    serv.enable_static_handler = true;\n\n    sw_logger()->set_level(SW_LOG_WARNING);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n    port->open_redis_protocol = true;\n\n    serv.create();\n    std::unordered_map<std::string, std::string> redis_data;\n\n    serv.onWorkerStart = [&](Server *serv, Worker *worker) {\n        if (worker->id != 0) {\n            return;\n        }\n        swoole::Coroutine::create(\n            [](void *arg) {\n                Server *serv = reinterpret_cast<Server *>(arg);\n                RedisClient redis;\n                ASSERT_TRUE(redis.Connect(\"127.0.0.1\", serv->get_primary_port()->port));\n                ASSERT_TRUE(redis.Set(REDIS_TEST_KEY, REDIS_TEST_VALUE));\n                ASSERT_EQ(redis.Get(REDIS_TEST_KEY), REDIS_TEST_VALUE);\n\n                ASSERT_EQ(redis.Get(REDIS_TEST_KEY + \"-not-exists\"), \"\");\n\n                String rdata;\n                rdata.append_random_bytes(128 * 1024, true);\n                auto data = rdata.to_std_string();\n\n                ASSERT_TRUE(redis.Set(REDIS_TEST_KEY + \"-big-key\", data));\n                ASSERT_EQ(redis.Get(REDIS_TEST_KEY + \"-big-key\"), data);\n                ASSERT_EQ(redis.Ttl(REDIS_TEST_KEY), -1);\n                ASSERT_FALSE(redis.Select(1));\n                ASSERT_EQ(redis.Role(), \"master\");\n\n                kill(serv->gs->master_pid, SIGTERM);\n            },\n            serv);\n    };\n\n    serv.onReceive = [&redis_data](Server *serv, RecvData *req) -> int {\n        int session_id = req->info.fd;\n        auto list = redis::parse(req->data, req->info.len);\n\n        String *buffer = sw_tg_buffer();\n        buffer->clear();\n\n        if (strcasecmp(list[0].c_str(), \"GET\") == 0) {\n            auto result = redis_data.find(list[1]);\n            if (result == redis_data.end()) {\n                redis::format_nil(buffer);\n            } else {\n                char buf[64];\n                auto n = snprintf(buf, sizeof(buf), \"$%zu\\r\\n\", result->second.length());\n                serv->send(session_id, buf, n);\n                serv->send(session_id, result->second.c_str(), result->second.length());\n                serv->send(session_id, SW_CRLF, SW_CRLF_LEN);\n                return SW_OK;\n            }\n        } else if (strcasecmp(list[0].c_str(), \"SET\") == 0) {\n            redis::format(buffer, redis::REPLY_STATUS, \"OK\");\n            redis_data[list[1]] = list[2];\n        } else if (strcasecmp(list[0].c_str(), \"TTL\") == 0) {\n            redis::format(buffer, redis::REPLY_INT, -1);\n        } else if (strcasecmp(list[0].c_str(), \"ROLE\") == 0) {\n            redis::format(buffer, redis::REPLY_STRING, \"master\");\n        } else {\n            redis::format(buffer, redis::REPLY_ERROR, \"Not Suppport\");\n        }\n\n        serv->send(session_id, buffer->str, buffer->length);\n        return SW_OK;\n    };\n\n    serv.start();\n}\n\nTEST(redis, format) {\n    auto buf = sw_tg_buffer();\n\n    buf->clear();\n    redis::format(buf, redis::REPLY_STATUS, \"\");\n    ASSERT_MEMEQ(buf->str, \"+OK\\r\\n\", buf->length);\n\n    buf->clear();\n    redis::format(buf, redis::REPLY_ERROR, \"\");\n    ASSERT_MEMEQ(buf->str, \"-ERR\\r\\n\", buf->length);\n}\n\nTEST(redis, parse) {\n    auto buf = sw_tg_buffer();\n\n    buf->clear();\n    auto rs = redis::parse(SW_STRL(\":3\\r\\n\"));\n    ASSERT_EQ(rs[0], \"3\");\n}\n"
  },
  {
    "path": "core-tests/src/protocol/ssl.cpp",
    "content": "/*\n+----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n\n#include <openssl/sha.h>\n#include <openssl/bio.h>\n#include <openssl/evp.h>\n#include <openssl/buffer.h>\n#include <openssl/err.h>\n\nusing swoole::SSLContext;\nusing swoole::String;\n\nTEST(ssl, destroy) {\n    swoole_ssl_init();\n    swoole_ssl_destroy();\n    ASSERT_EQ(ERR_peek_error(), 0);\n}\n\nTEST(ssl, get_error) {\n    swoole_ssl_init();\n    {\n        ERR_clear_error();\n        ERR_put_error(ERR_LIB_SSL, SSL_F_SSL_SET_SESSION, SSL_R_CERTIFICATE_VERIFY_FAILED, __FILE__, __LINE__);\n        const char *error_str = swoole_ssl_get_error();\n        EXPECT_NE(error_str, nullptr);\n        String str(error_str);\n        DEBUG() << str.to_std_string() << std::endl;\n        ASSERT_TRUE(str.contains(\"certificate verify failed\"));\n    }\n    {\n        ERR_clear_error();\n\n        ERR_put_error(ERR_LIB_SSL, SSL_F_SSL_SET_SESSION, SSL_R_CERTIFICATE_VERIFY_FAILED, __FILE__, __LINE__);\n        ERR_put_error(ERR_LIB_SSL, SSL_F_SSL_SHUTDOWN, SSL_R_PROTOCOL_IS_SHUTDOWN, __FILE__, __LINE__);\n\n        const char *error_str = swoole_ssl_get_error();\n        EXPECT_NE(error_str, nullptr);\n\n        const char *error_str2 = swoole_ssl_get_error();\n        EXPECT_NE(error_str2, nullptr);\n\n        String str(error_str2);\n        DEBUG() << str.to_std_string() << std::endl;\n        ASSERT_TRUE(str.contains(\"protocol is shutdown\"));\n\n        const char *error_st3 = swoole_ssl_get_error();\n        ASSERT_STREQ(error_st3, \"\");\n    }\n}\n\nTEST(ssl, password) {\n    SSLContext ctx;\n    ctx.key_file = swoole::test::get_ssl_dir() + \"/passwd_key.pem\";\n    ctx.passphrase = \"swoole\";\n    ctx.cert_file = swoole::test::get_ssl_dir() + \"/passwd.crt\";\n    ASSERT_TRUE(ctx.create());\n}\n"
  },
  {
    "path": "core-tests/src/reactor/base.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"swoole_reactor.h\"\n#include \"swoole_pipe.h\"\n#include \"swoole_signal.h\"\n#include \"swoole_util.h\"\n#include \"swoole_api.h\"\n\nusing namespace std;\nusing namespace swoole;\n\nTEST(reactor, create) {\n    swoole_event_init(0);\n\n    Reactor *reactor = SwooleTG.reactor;\n\n    ASSERT_EQ(reactor->max_event_num, SW_REACTOR_MAXEVENTS);\n\n    ASSERT_TRUE(reactor->ready());\n    ASSERT_NE(reactor->write, nullptr);\n    ASSERT_NE(reactor->close, nullptr);\n    ASSERT_EQ(reactor->defer_tasks, nullptr);\n    ASSERT_NE(reactor->default_write_handler, nullptr);\n\n    /**\n     * coroutine socket reactor\n     */\n    ASSERT_NE(reactor->get_handler(SW_FD_CO_SOCKET, SW_EVENT_READ), nullptr);\n    ASSERT_NE(reactor->get_handler(SW_FD_CO_SOCKET, SW_EVENT_WRITE), nullptr);\n    ASSERT_NE(reactor->get_handler(SW_FD_CO_SOCKET, SW_EVENT_ERROR), nullptr);\n\n    /**\n     * system reactor\n     */\n    ASSERT_NE(reactor->get_handler(SW_FD_CO_POLL, SW_EVENT_READ), nullptr);\n    ASSERT_NE(reactor->get_handler(SW_FD_CO_POLL, SW_EVENT_WRITE), nullptr);\n    ASSERT_NE(reactor->get_handler(SW_FD_CO_POLL, SW_EVENT_ERROR), nullptr);\n\n    ASSERT_NE(reactor->get_handler(SW_FD_CO_EVENT, SW_EVENT_READ), nullptr);\n    ASSERT_NE(reactor->get_handler(SW_FD_CO_EVENT, SW_EVENT_WRITE), nullptr);\n    ASSERT_NE(reactor->get_handler(SW_FD_CO_EVENT, SW_EVENT_ERROR), nullptr);\n\n    ASSERT_NE(reactor->get_handler(SW_FD_AIO, SW_EVENT_READ), nullptr);\n\n    swoole_event_free();\n}\n\nTEST(reactor, set_handler) {\n    Reactor reactor;\n\n    reactor.set_handler(SW_FD_SESSION, SW_EVENT_READ, (ReactorHandler) 0x1);\n    ASSERT_TRUE(reactor.isset_handler(SW_FD_SESSION, SW_EVENT_READ));\n    ASSERT_EQ(reactor.get_handler(SW_FD_SESSION, SW_EVENT_READ), (ReactorHandler) 0x1);\n\n    reactor.set_handler(SW_FD_SESSION, SW_EVENT_WRITE, (ReactorHandler) 0x2);\n    ASSERT_TRUE(reactor.isset_handler(SW_FD_SESSION, SW_EVENT_WRITE));\n    ASSERT_EQ(reactor.get_handler(SW_FD_SESSION, SW_EVENT_WRITE), (ReactorHandler) 0x2);\n\n    reactor.set_handler(SW_FD_SESSION, SW_EVENT_ERROR, (ReactorHandler) 0x3);\n    ASSERT_TRUE(reactor.isset_handler(SW_FD_SESSION, SW_EVENT_ERROR));\n    ASSERT_EQ(reactor.get_handler(SW_FD_SESSION, SW_EVENT_ERROR), (ReactorHandler) 0x3);\n}\n\nTEST(reactor, wait) {\n    int ret;\n    UnixSocket p(true, SOCK_DGRAM);\n    ASSERT_TRUE(p.ready());\n\n    ret = swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n    ASSERT_EQ(ret, SW_OK);\n    ASSERT_NE(SwooleTG.reactor, nullptr);\n    ASSERT_TRUE(SwooleTG.reactor->ready());\n\n    swoole_event_set_handler(SW_FD_PIPE, SW_EVENT_READ, [](Reactor *reactor, Event *ev) -> int {\n        char buffer[16];\n\n        ssize_t n = ev->socket->read(buffer, sizeof(buffer));\n        EXPECT_EQ(sizeof(\"hello world\"), n);\n        EXPECT_STREQ(\"hello world\", buffer);\n        reactor->del(ev->socket);\n\n        return SW_OK;\n    });\n\n    ret = swoole_event_add(p.get_socket(false), SW_EVENT_READ);\n    ASSERT_EQ(swoole_event_get_socket(p.get_socket(false)->get_fd()), p.get_socket(false));\n    ASSERT_EQ(ret, SW_OK);\n\n    ret = p.write((void *) SW_STRS(\"hello world\"));\n    ASSERT_EQ(ret, sizeof(\"hello world\"));\n\n    ret = swoole_event_wait();\n    ASSERT_EQ(ret, SW_OK);\n    ASSERT_EQ(SwooleTG.reactor, nullptr);\n}\n\nTEST(reactor, write) {\n    int ret;\n    UnixSocket p(true, SOCK_DGRAM);\n    ASSERT_TRUE(p.ready());\n    p.set_blocking(false);\n    p.set_buffer_size(65536);\n\n    ret = swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n    ASSERT_EQ(ret, SW_OK);\n    ASSERT_NE(SwooleTG.reactor, nullptr);\n\n    swoole_event_set_handler(SW_FD_PIPE, SW_EVENT_READ, [](Reactor *reactor, Event *ev) -> int {\n        char buffer[16];\n\n        ssize_t n = ev->socket->read(buffer, sizeof(buffer));\n        EXPECT_EQ(sizeof(\"hello world\"), n);\n        EXPECT_STREQ(\"hello world\", buffer);\n        reactor->del(ev->socket);\n\n        return SW_OK;\n    });\n\n    ret = swoole_event_add(p.get_socket(false), SW_EVENT_READ);\n    ASSERT_EQ(ret, SW_OK);\n\n    auto sock = p.get_socket(true);\n\n    auto n = swoole_event_write(sock, (void *) SW_STRS(\"hello world\"));\n    ASSERT_EQ(n, sizeof(\"hello world\"));\n\n    ret = swoole_event_wait();\n    ASSERT_EQ(ret, SW_OK);\n    ASSERT_EQ(SwooleTG.reactor, nullptr);\n}\n\nTEST(reactor, wait_timeout) {\n    ASSERT_EQ(swoole_event_init(SW_EVENTLOOP_WAIT_EXIT), SW_OK);\n    ASSERT_NE(SwooleTG.reactor, nullptr);\n\n    sw_reactor()->set_timeout_msec(30);\n    auto started_at = swoole::microtime();\n    ASSERT_EQ(sw_reactor()->wait(), SW_OK);\n\n    auto dr = swoole::microtime() - started_at;\n    ASSERT_GE(dr, 0.03);\n    ASSERT_LT(dr, 0.05);\n\n    swoole_event_free();\n}\n\nTEST(reactor, wait_error) {\n    ASSERT_EQ(swoole_event_init(SW_EVENTLOOP_WAIT_EXIT), SW_OK);\n    ASSERT_NE(SwooleTG.reactor, nullptr);\n\n    // ERROR: EINVAL epfd is not an epoll file descriptor, or maxevents is less than or equal to zero.\n    sw_reactor()->max_event_num = 0;\n    ASSERT_EQ(sw_reactor()->wait(), SW_ERR);\n    ASSERT_EQ(errno, EINVAL);\n\n    swoole_event_free();\n}\n\nTEST(reactor, writev) {\n    network::Socket fake_sock{};\n\n    UnixSocket p(true, SOCK_DGRAM);\n    ASSERT_TRUE(p.ready());\n\n    fake_sock.fd = p.get_socket(true)->get_fd();\n\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n\n    struct iovec iov[2];\n\n    iov[0].iov_base = (void *) \"hello \";\n    iov[0].iov_len = 6;\n\n    iov[1].iov_base = (void *) \"world\\n\";\n    iov[1].iov_len = 6;\n\n    ASSERT_EQ(swoole_event_writev(&fake_sock, iov, 2), 12);\n\n    char buf[32];\n    ASSERT_EQ(p.get_socket(false)->read(buf, sizeof(buf)), 12);\n    ASSERT_MEMEQ(\"hello world\\n\", buf, 12);\n\n    fake_sock.ssl = (SSL *) -1;\n    ASSERT_EQ(swoole_event_writev(&fake_sock, iov, 2), -1);\n    ASSERT_EQ(swoole_get_last_error(), SW_ERROR_OPERATION_NOT_SUPPORT);\n\n    swoole_event_wait();\n}\n\nconstexpr int DATA_SIZE = 2 * SW_NUM_MILLION;\n\nTEST(reactor, write_2m) {\n    int ret;\n    UnixSocket p(true, SOCK_STREAM);\n    ASSERT_TRUE(p.ready());\n\n    ret = swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n    ASSERT_EQ(ret, SW_OK);\n    ASSERT_NE(SwooleTG.reactor, nullptr);\n\n    swoole_event_set_handler(SW_FD_PIPE, SW_EVENT_READ, [](Reactor *reactor, Event *ev) -> int {\n        auto tg_buf = sw_tg_buffer();\n        ssize_t n = ev->socket->read(tg_buf->str + tg_buf->length, tg_buf->size - tg_buf->length);\n        if (n <= 0) {\n            return SW_ERR;\n        }\n        tg_buf->grow(n);\n        if (tg_buf->length == DATA_SIZE) {\n            tg_buf->append('\\0');\n            reactor->del(ev->socket);\n        }\n        return SW_OK;\n    });\n\n    p.set_blocking(false);\n    p.set_buffer_size(65536);\n\n    ret = swoole_event_add(p.get_socket(false), SW_EVENT_READ);\n    ASSERT_EQ(ret, SW_OK);\n\n    String str(DATA_SIZE);\n    str.append_random_bytes(str.size - 1, false);\n    str.append('\\0');\n\n    sw_tg_buffer()->clear();\n\n    auto sock = p.get_socket(true);\n    sock->buffer_size = 2 * 1024 * 1024;\n\n    auto n = swoole_event_write(sock, str.value(), str.get_length());\n    ASSERT_EQ(n, str.get_length());\n    ASSERT_GT(sock->get_out_buffer_length(), 1024);\n\n    std::cout << sock->get_out_buffer_length() << \"\\n\";\n\n    ASSERT_EQ(swoole_event_write(sock, str.value(), 256 * 1024), SW_ERR);\n    ASSERT_EQ(swoole_get_last_error(), SW_ERROR_OUTPUT_BUFFER_OVERFLOW);\n\n    ASSERT_EQ(swoole_event_write(sock, str.value(), sock->buffer_size + 8192), SW_ERR);\n    ASSERT_EQ(swoole_get_last_error(), SW_ERROR_PACKAGE_LENGTH_TOO_LARGE);\n\n    ret = swoole_event_wait();\n    ASSERT_EQ(ret, SW_OK);\n    ASSERT_FALSE(swoole_event_is_available());\n    ASSERT_STREQ(sw_tg_buffer()->value(), str.value());\n}\n\nTEST(reactor, bad_fd) {\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n    auto sock = make_socket(999999, SW_FD_STREAM_CLIENT);\n    sock->nonblock = 1;\n    auto n = swoole_event_write(sock, SW_STRL(\"hello world\"));\n    ASSERT_EQ(n, SW_ERR);\n    ASSERT_EQ(swoole_get_last_error(), EBADF);\n    swoole_event_free();\n    sock->move_fd();\n    sock->free();\n}\n\nstatic const char *pkt = \"hello world\\r\\n\";\n\nstatic void reactor_test_func(Reactor *reactor) {\n    Pipe p(false);\n    ASSERT_TRUE(p.ready());\n\n    reactor->set_handler(SW_FD_PIPE, SW_EVENT_READ, [](Reactor *reactor, Event *event) -> int {\n        char buf[1024];\n        size_t l = strlen(pkt);\n        size_t n = event->socket->read(buf, sizeof(buf));\n        EXPECT_EQ(n, l);\n        buf[n] = 0;\n        EXPECT_EQ(std::string(buf, n), std::string(pkt));\n        reactor->del(event->socket);\n\n        return SW_OK;\n    });\n\n    reactor->set_handler(SW_FD_PIPE, SW_EVENT_WRITE, [](Reactor *reactor, Event *event) -> int {\n        size_t l = strlen(pkt);\n        EXPECT_EQ(event->socket->write(pkt, l), l);\n        reactor->del(event->socket);\n\n        return SW_OK;\n    });\n\n    ASSERT_EQ(reactor->add(p.get_socket(false), SW_EVENT_READ), SW_OK);\n    ASSERT_EQ(reactor->add(p.get_socket(true), SW_EVENT_WRITE), SW_OK);\n\n    UnixSocket unsock(false, SOCK_STREAM);\n    ASSERT_TRUE(unsock.ready());\n\n    int write_count = 0;\n    auto sock2 = unsock.get_socket(false);\n    sock2->object = &write_count;\n    sock2->fd_type = SW_FD_STREAM;\n\n    reactor->set_handler(SW_FD_STREAM, SW_EVENT_WRITE, [](Reactor *reactor, Event *event) -> int {\n        int *count = (int *) event->socket->object;\n        (*count)++;\n        return SW_OK;\n    });\n    ASSERT_EQ(reactor->add(sock2, SW_FD_STREAM | SW_EVENT_WRITE | SW_EVENT_ONCE), SW_OK);\n\n    ASSERT_EQ(write_count, 0);\n\n    ASSERT_EQ(reactor->wait(), SW_OK);\n\n    ASSERT_EQ(write_count, 1);\n}\n\nTEST(reactor, epoll) {\n    Reactor reactor(1024, Reactor::TYPE_EPOLL);\n    reactor.wait_exit = true;\n    reactor_test_func(&reactor);\n}\n\nTEST(reactor, poll) {\n    Reactor reactor(1024, Reactor::TYPE_POLL);\n    reactor.wait_exit = true;\n    reactor_test_func(&reactor);\n}\n\nTEST(reactor, poll_extra) {\n    Reactor reactor(32, Reactor::TYPE_POLL);\n\n    network::Socket fake_sock1{};\n    fake_sock1.fd = 12345;\n\n    network::Socket fake_sock2{};\n    fake_sock2.fd = 99999;\n\n    ASSERT_EQ(reactor.add(&fake_sock1, SW_EVENT_READ), SW_OK);\n    ASSERT_EQ(reactor.add(&fake_sock2, SW_EVENT_READ), SW_OK);\n\n    ASSERT_EQ(reactor.add(&fake_sock1, SW_EVENT_READ), SW_ERR);\n\n    ASSERT_EQ(reactor.get_event_num(), 2);\n\n    ASSERT_EQ(reactor.set(&fake_sock2, SW_EVENT_READ | SW_EVENT_WRITE | SW_EVENT_ERROR), SW_OK);\n\n    ASSERT_EQ(reactor.del(&fake_sock2), SW_OK);\n    ASSERT_EQ(reactor.get_event_num(), 1);\n\n    network::Socket fake_sock3{};\n    fake_sock3.fd = 88888;\n\n    ASSERT_EQ(reactor.set(&fake_sock3, SW_EVENT_READ | SW_EVENT_WRITE), SW_ERR);\n    ASSERT_EQ(swoole_get_last_error(), SW_ERROR_SOCKET_NOT_EXISTS);\n\n    ASSERT_EQ(reactor.del(&fake_sock3), SW_ERR);\n    ASSERT_EQ(swoole_get_last_error(), SW_ERROR_SOCKET_NOT_EXISTS);\n\n    network::Socket fake_socks[32];\n    SW_LOOP_N(32) {\n        fake_socks[i].fd = i + 1024;\n        if (i <= 30) {\n            ASSERT_EQ(reactor.add(&fake_socks[i], SW_EVENT_READ), SW_OK);\n        } else {\n            ASSERT_EQ(reactor.add(&fake_socks[i], SW_EVENT_READ), SW_ERR);\n        }\n    }\n\n    for (auto i = 31; i <= 0; i--) {\n        fake_socks[i].fd = i + 1024;\n        if (i <= 30) {\n            ASSERT_EQ(reactor.del(&fake_socks[i]), SW_OK);\n        } else {\n            ASSERT_EQ(reactor.del(&fake_socks[i]), SW_ERR);\n        }\n    }\n}\n\nTEST(reactor, poll_extra2) {\n    Reactor reactor(32, Reactor::TYPE_POLL);\n    reactor.once = true;\n    reactor.set_timeout_msec(10);\n    reactor.set_end_callback(Reactor::PRIORITY_DEFER_TASK, [](Reactor *reactor) {\n        DEBUG() << \"end callback\\n\";\n        ASSERT_TRUE(reactor->timed_out);\n    });\n    ASSERT_EQ(reactor.wait(), SW_OK);\n\n    swoole_signal_set(\n        SIGIO, [](int sig) { DEBUG() << \"SIGIO received\\n\"; }, 0, 0);\n\n    reactor.erase_end_callback(Reactor::PRIORITY_DEFER_TASK);\n    reactor.set_timeout_msec(1000);\n\n    std::thread t([]() {\n        swoole_signal_block_all();\n        usleep(10000);\n        kill(getpid(), SIGIO);\n    });\n    errno = 0;\n    ASSERT_EQ(reactor.wait(), SW_OK);\n    ASSERT_EQ(errno, EINTR);\n\n    t.join();\n}\n\nTEST(reactor, add_or_update) {\n    int ret;\n    UnixSocket p(true, SOCK_DGRAM);\n    ASSERT_TRUE(p.ready());\n\n    ret = swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n    ASSERT_EQ(ret, SW_OK);\n    ASSERT_NE(SwooleTG.reactor, nullptr);\n\n    ret = swoole_event_add_or_update(p.get_socket(false), SW_EVENT_READ);\n    ASSERT_EQ(ret, SW_OK);\n    ASSERT_TRUE(p.get_socket(false)->events & SW_EVENT_READ);\n\n    ret = swoole_event_add_or_update(p.get_socket(false), SW_EVENT_WRITE);\n    ASSERT_EQ(ret, SW_OK);\n    ASSERT_TRUE(p.get_socket(false)->events & SW_EVENT_READ);\n    ASSERT_TRUE(p.get_socket(false)->events & SW_EVENT_WRITE);\n\n    swoole_event_free();\n}\n\nTEST(reactor, defer_task) {\n    swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);\n    Reactor *reactor = sw_reactor();\n    ASSERT_EQ(reactor->max_event_num, SW_REACTOR_MAXEVENTS);\n\n    int count = 0;\n    reactor->defer([&count](void *) { count++; });\n    swoole_event_wait();\n    ASSERT_EQ(count, 1);\n    swoole_event_free();\n}\n\nTEST(reactor, cycle) {\n    Reactor reactor(1024, Reactor::TYPE_POLL);\n    reactor.wait_exit = true;\n\n    int event_loop_count = 0;\n    const char *test = \"hello world\";\n\n    reactor.future_task.callback = [&event_loop_count](void *data) {\n        ASSERT_STREQ((char *) data, \"hello world\");\n        event_loop_count++;\n    };\n    reactor.future_task.data = (void *) test;\n\n    reactor_test_func(&reactor);\n\n    ASSERT_GT(event_loop_count, 0);\n}\n\nstatic void event_idle_callback(void *data) {\n    ASSERT_STREQ((char *) data, \"hello world\");\n}\n\nTEST(reactor, priority_idle_task) {\n    Reactor reactor(1024, Reactor::TYPE_POLL);\n    reactor.wait_exit = true;\n\n    const char *test = \"hello world\";\n    reactor.idle_task.callback = event_idle_callback;\n    reactor.idle_task.data = (void *) test;\n    reactor_test_func(&reactor);\n}\n\nTEST(reactor, hook) {\n    Reactor *reactor = new Reactor(1024, Reactor::TYPE_POLL);\n    reactor->wait_exit = true;\n\n    swoole_add_hook(\n        SW_GLOBAL_HOOK_ON_REACTOR_CREATE,\n        [](void *data) -> void {\n            Reactor *reactor = (Reactor *) data;\n            ASSERT_EQ(Reactor::TYPE_POLL, reactor->type_);\n        },\n        1);\n\n    swoole_add_hook(\n        SW_GLOBAL_HOOK_ON_REACTOR_DESTROY,\n        [](void *data) -> void {\n            Reactor *reactor = (Reactor *) data;\n            ASSERT_EQ(Reactor::TYPE_POLL, reactor->type_);\n        },\n        1);\n\n    ON_SCOPE_EXIT {\n        SwooleG.hooks[SW_GLOBAL_HOOK_ON_REACTOR_CREATE] = nullptr;\n        SwooleG.hooks[SW_GLOBAL_HOOK_ON_REACTOR_DESTROY] = nullptr;\n    };\n\n    reactor_test_func(reactor);\n    delete reactor;\n}\n\nTEST(reactor, set_fd) {\n    UnixSocket p(true, SOCK_DGRAM);\n    Reactor *reactor = new Reactor(1024, Reactor::TYPE_EPOLL);\n    ASSERT_EQ(reactor->add(p.get_socket(false), SW_EVENT_READ), SW_OK);\n    ASSERT_EQ(reactor->set(p.get_socket(false), SW_EVENT_WRITE), SW_OK);\n    delete reactor;\n\n    reactor = new Reactor(1024, Reactor::TYPE_POLL);\n    ASSERT_EQ(reactor->add(p.get_socket(false), SW_EVENT_READ), SW_OK);\n    ASSERT_EQ(reactor->set(p.get_socket(false), SW_EVENT_WRITE), SW_OK);\n    delete reactor;\n}\n\nstatic void test_error_event(Reactor::Type type, int retval) {\n    Pipe p(true);\n    ASSERT_TRUE(p.ready());\n\n    Reactor *reactor = new Reactor(1024, type);\n    SwooleTG.reactor = reactor;\n\n    reactor->ptr = &retval;\n\n    reactor->set_handler(SW_FD_PIPE, SW_EVENT_ERROR, [](Reactor *reactor, Event *event) -> int {\n        EXPECT_EQ(reactor->del(event->socket), SW_OK);\n        reactor->running = false;\n        return *(int *) reactor->ptr;\n    });\n\n    reactor->add(p.get_socket(true), SW_EVENT_ERROR);\n    reactor->add(p.get_socket(false), SW_EVENT_ERROR);\n\n    p.close(SW_PIPE_CLOSE_WORKER);\n    ASSERT_EQ(reactor->wait(), SW_OK);\n    delete reactor;\n    SwooleTG.reactor = nullptr;\n}\n\nTEST(reactor, error_event) {\n    test_error_event(Reactor::TYPE_EPOLL, SW_OK);\n    test_error_event(Reactor::TYPE_POLL, SW_OK);\n\n    test_error_event(Reactor::TYPE_EPOLL, SW_ERR);\n    test_error_event(Reactor::TYPE_POLL, SW_ERR);\n}\n\nTEST(reactor, error) {\n    UnixSocket p(true, SOCK_DGRAM);\n\n    swoole_set_print_backtrace_on_error(true);\n\n    Reactor *reactor = new Reactor(1024, Reactor::TYPE_EPOLL);\n    ASSERT_EQ(reactor->add(p.get_socket(false), SW_EVENT_READ), SW_OK);\n    ASSERT_EQ(reactor->add(p.get_socket(false), SW_EVENT_WRITE), SW_ERR);\n    ASSERT_EQ(errno, EEXIST);\n\n    network::Socket bad_sock;\n    bad_sock.removed = 1;\n    bad_sock.fd_type = SW_FD_PIPE;\n    bad_sock.fd = dup(p.get_socket(false)->get_fd());\n    ASSERT_EQ(reactor->del(&bad_sock), SW_ERR);\n    ASSERT_EQ(swoole_get_last_error(), SW_ERROR_EVENT_REMOVE_FAILED);\n\n    ASSERT_EQ(reactor->add(&bad_sock, SW_EVENT_READ), SW_OK);\n    close(bad_sock.fd);\n\n    ASSERT_EQ(reactor->set(&bad_sock, SW_EVENT_READ | SW_EVENT_READ), SW_ERR);\n    ASSERT_EQ(errno, EBADF);\n\n    ASSERT_EQ(reactor->del(&bad_sock), SW_OK);\n\n    delete reactor;\n\n    reactor = new Reactor(1024, Reactor::TYPE_POLL);\n    ASSERT_EQ(reactor->add(p.get_socket(false), SW_EVENT_READ), SW_OK);\n    ASSERT_EQ(reactor->del(p.get_socket(false)), SW_OK);\n    ASSERT_EQ(reactor->del(p.get_socket(false)), SW_ERR);\n    ASSERT_EQ(swoole_get_last_error(), SW_ERROR_SOCKET_NOT_EXISTS);\n    delete reactor;\n}\n\nTEST(reactor, drain_write_buffer) {\n    int ret;\n    UnixSocket p(true, SOCK_STREAM);\n    ASSERT_TRUE(p.ready());\n\n    ASSERT_EQ(swoole_event_init(SW_EVENTLOOP_WAIT_EXIT), SW_OK);\n\n    p.set_blocking(false);\n    p.set_buffer_size(65536);\n\n    String str(DATA_SIZE);\n    str.append_random_bytes(str.size - 1, false);\n    str.append('\\0');\n\n    auto wsock = p.get_socket(true);\n\n    auto n = swoole_event_write(wsock, str.value(), str.get_length());\n    ASSERT_EQ(n, str.get_length());\n    ASSERT_GT(wsock->get_out_buffer_length(), 1024);\n\n    std::thread t([&]() {\n        usleep(10000);\n        auto rsock = p.get_socket(false);\n\n        String rbuf(DATA_SIZE);\n        while (true) {\n            rsock->wait_event(1000, SW_EVENT_READ);\n            auto n = rsock->read(rbuf.str + rbuf.length, rbuf.size - rbuf.length);\n            if (n > 0) {\n                rbuf.length += n;\n                if (rbuf.length == rbuf.size) {\n                    break;\n                }\n            }\n        }\n\n        ASSERT_MEMEQ(rbuf.str, str.str, DATA_SIZE);\n    });\n\n    sw_reactor()->drain_write_buffer(wsock);\n\n    ret = swoole_event_wait();\n    ASSERT_EQ(ret, SW_OK);\n    ASSERT_FALSE(swoole_event_is_available());\n    t.join();\n}\n\nTEST(reactor, handle_fail) {\n    int ret;\n    UnixSocket p(true, SOCK_DGRAM);\n    ASSERT_TRUE(p.ready());\n\n    ASSERT_EQ(swoole_event_init(SW_EVENTLOOP_WAIT_EXIT), SW_OK);\n    ASSERT_NE(SwooleTG.reactor, nullptr);\n\n    swoole_event_set_handler(SW_FD_PIPE, SW_EVENT_READ, [](Reactor *reactor, Event *ev) -> int {\n        char buffer[16];\n\n        ssize_t n = ev->socket->read(buffer, sizeof(buffer));\n        EXPECT_EQ(strlen(pkt), n);\n        EXPECT_MEMEQ(pkt, buffer, n);\n        EXPECT_EQ(reactor->del(ev->socket), 0);\n\n        EXPECT_EQ(reactor->get_event_num(), 0);\n\n        return SW_ERR;\n    });\n\n    swoole_event_set_handler(SW_FD_PIPE, SW_EVENT_WRITE, [](Reactor *reactor, Event *ev) -> int {\n        EXPECT_EQ(reactor->set(ev->socket, SW_EVENT_READ), 0);\n        UnixSocket *p = (UnixSocket *) ev->socket->object;\n        swoole_timer_after(10, [p](auto r1, auto r2) { p->get_socket(true)->write(pkt, strlen(pkt)); });\n        return SW_ERR;\n    });\n\n    auto sock = p.get_socket(false);\n    sock->object = &p;\n\n    ret = swoole_event_add(sock, SW_EVENT_READ | SW_EVENT_WRITE);\n    ASSERT_EQ(ret, SW_OK);\n\n    ret = swoole_event_wait();\n    ASSERT_EQ(ret, SW_OK);\n    ASSERT_EQ(SwooleTG.reactor, nullptr);\n}\n"
  },
  {
    "path": "core-tests/src/server/buffer.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n#include \"swoole_server.h\"\n\nusing namespace std;\nusing namespace swoole;\n\nstatic const char *packet = \"hello world\\n\";\n\nTEST(server, send_buffer) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n\n    sw_logger()->set_level(SW_LOG_WARNING);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    mutex lock;\n    lock.lock();\n\n    std::thread t1([&]() {\n        swoole_signal_block_all();\n\n        lock.lock();\n\n        swoole::network::SyncClient c(SW_SOCK_TCP);\n        c.connect(TEST_HOST, port->port);\n        c.send(packet, strlen(packet));\n        char buf[4096];\n\n        while (1) {\n            ssize_t retval = c.recv(buf, sizeof(buf));\n            if (retval <= 0) {\n                break;\n            }\n            usleep(100);\n        }\n\n        c.close();\n\n        kill(getpid(), SIGTERM);\n    });\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        EXPECT_EQ(string(req->data, req->info.len), string(packet));\n\n        String resp(1024 * 1024 * 16);\n        resp.repeat(\"A\", 1, resp.capacity());\n        EXPECT_TRUE(serv->send(req->info.fd, resp.value(), resp.get_length()));\n        EXPECT_TRUE(serv->close(req->info.fd, 0));\n\n        return SW_OK;\n    };\n\n    serv.start();\n    t1.join();\n}\n"
  },
  {
    "path": "core-tests/src/server/http_parser.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | Author    NathanFreeman  <mariasocute@163.com>                         |\n  +----------------------------------------------------------------------+\n */\n\n#include \"test_core.h\"\n#include \"swoole_util.h\"\n#include \"swoole_llhttp.h\"\n\nusing namespace std;\n\nstatic int http_request_on_url(llhttp_t *parser, const char *at, size_t length);\nstatic int http_request_on_body(llhttp_t *parser, const char *at, size_t length);\nstatic int http_request_on_header_field(llhttp_t *parser, const char *at, size_t length);\nstatic int http_request_on_header_value(llhttp_t *parser, const char *at, size_t length);\nstatic int http_request_on_headers_complete(llhttp_t *parser);\nstatic int http_request_message_complete(llhttp_t *parser);\nstatic int http_llhttp_data_cb(llhttp_t *parser, const char *at, size_t length);\nstatic int http_llhttp_cb(llhttp_t *parser);\n\n// clang-format off\nstatic const llhttp_settings_t http_parser_settings =\n{\n    http_llhttp_cb,                         // on_message_begin\n    http_llhttp_data_cb,                    // on_protocol\n    http_request_on_url,                    // on_url\n    http_llhttp_data_cb,                    // on_status\n    http_llhttp_data_cb,                    // on_method\n    http_llhttp_data_cb,                    // on_version\n    http_request_on_header_field,           // on_header_field\n    http_request_on_header_value,           // on_header_value\n    http_llhttp_data_cb,                    // on_chunk_extension_name\n    http_llhttp_data_cb,                    // on_chunk_extension_value\n    http_request_on_headers_complete,       // on_headers_complete\n    http_request_on_body,                   // on_body\n    http_request_message_complete,          // on_message_complete\n    http_llhttp_cb,                         // on_protocol_complete\n    http_llhttp_cb,                         // on_url_complete\n    http_llhttp_cb,                         // on_status_complete\n    http_llhttp_cb,                         // on_method_complete\n    http_llhttp_cb,                         // on_version_complete\n    http_llhttp_cb,                         // on_header_field_complete\n    http_llhttp_cb,                         // on_header_value_complete\n    http_llhttp_cb,                         // on_chunk_extension_name_complete\n    http_llhttp_cb,                         // on_chunk_extension_value_complete\n    http_llhttp_cb,                         // on_chunk_header\n    http_llhttp_cb,                         // on_chunk_complete\n    http_llhttp_cb,                         // on_reset\n};\n// clang-format on\n\nstruct HttpContext {\n    long fd;\n    uchar completed : 1;\n    uchar end_ : 1;\n    uchar send_header_ : 1;\n\n    uchar send_chunked : 1;\n    uchar recv_chunked : 1;\n    uchar send_trailer_ : 1;\n    uchar keepalive : 1;\n    uchar websocket : 1;\n\n    uchar upgrade : 1;\n    uchar detached : 1;\n    uchar parse_cookie : 1;\n    uchar parse_body : 1;\n    uchar parse_files : 1;\n    uchar co_socket : 1;\n    uchar http2 : 1;\n\n    llhttp_t parser;\n\n    uint16_t input_var_num;\n    char *current_header_name;\n    size_t current_header_name_len;\n    char *current_input_name;\n    size_t current_input_name_len;\n    char *current_form_data_name;\n    size_t current_form_data_name_len;\n\n    vector<string> header_fields;\n    vector<string> header_values;\n    string query_string;\n};\n\nstatic llhttp_t *swoole_http_parser_create(llhttp_type type = HTTP_REQUEST) {\n    auto *ctx = new HttpContext();\n    llhttp_t *parser = &ctx->parser;\n    swoole_llhttp_parser_init(parser, type, static_cast<void *>(ctx));\n    return parser;\n}\n\nstatic void swoole_http_destroy_context(llhttp_t *parser) {\n    delete static_cast<HttpContext *>(parser->data);\n}\n\nstatic int http_request_on_url(llhttp_t *parser, const char *at, size_t length) {\n    auto *ctx = static_cast<HttpContext *>(parser->data);\n    ctx->query_string = string(at, length);\n    return 0;\n}\n\nstatic int http_request_on_header_field(llhttp_t *parser, const char *at, size_t length) {\n    auto *ctx = static_cast<HttpContext *>(parser->data);\n    ctx->header_fields.emplace_back(at, length);\n    return 0;\n}\n\nstatic int http_request_on_header_value(llhttp_t *parser, const char *at, size_t length) {\n    auto ctx = static_cast<HttpContext *>(parser->data);\n    ctx->header_values.emplace_back(at, length);\n    return 0;\n}\n\nstatic int http_request_on_headers_complete(llhttp_t *parser) {\n    return 0;\n}\n\nstatic int http_request_on_body(llhttp_t *parser, const char *at, size_t length) {\n    return 0;\n}\n\nstatic int http_request_message_complete(llhttp_t *parser) {\n    auto ctx = static_cast<HttpContext *>(parser->data);\n    ctx->completed = 1;\n    return 0;\n}\n\nstatic int http_llhttp_data_cb(llhttp_t *parser, const char *at, size_t length) {\n    return 0;\n}\n\nstatic int http_llhttp_cb(llhttp_t *parser) {\n    return 0;\n}\n\nTEST(http_parser, get_request) {\n    llhttp_t *parser = swoole_http_parser_create();\n    ON_SCOPE_EXIT {\n        swoole_http_destroy_context(parser);\n    };\n\n    string request = \"GET /get HTTP/1.1\\r\\n\"\n                     \"Host: www.maria.com\\r\\n\"\n                     \"User-Agent: curl/7.64.1\\r\\n\"\n                     \"Accept: */*\\r\\n\"\n                     \"Connection: keep-alive\\r\\n\"\n                     \"\\r\\n\";\n    size_t length = swoole_llhttp_parser_execute(parser, &http_parser_settings, request.c_str(), request.length());\n    HttpContext *ctx = static_cast<HttpContext *>(parser->data);\n    ASSERT_TRUE(length == request.length());\n    ASSERT_TRUE(llhttp_get_errno(parser) == HPE_OK);\n    ASSERT_TRUE(ctx->completed == 1);\n    ASSERT_TRUE(llhttp_should_keep_alive(parser) == 1);\n}\n\nTEST(http_parser, version) {\n    llhttp_t *parser = swoole_http_parser_create();\n    ON_SCOPE_EXIT {\n        swoole_http_destroy_context(parser);\n    };\n\n    string http11 = \"GET /get HTTP/1.1\\r\\n\\r\\n\";\n    size_t length = swoole_llhttp_parser_execute(parser, &http_parser_settings, http11.c_str(), http11.length());\n    ASSERT_TRUE(length == http11.length());\n\n    HttpContext *ctx = static_cast<HttpContext *>(parser->data);\n    ASSERT_TRUE(llhttp_get_errno(parser) == HPE_OK);\n    ASSERT_TRUE(ctx->completed == 1);\n    ASSERT_TRUE(llhttp_get_http_major(parser) == 1);\n    ASSERT_TRUE(llhttp_get_http_minor(parser) == 1);\n}\n\nTEST(http_parser, incomplete) {\n    llhttp_t *parser = swoole_http_parser_create();\n    ON_SCOPE_EXIT {\n        swoole_http_destroy_context(parser);\n    };\n\n    string incomplete = \"GET /get HTTP/1.1\\r\\n\";\n    size_t length =\n        swoole_llhttp_parser_execute(parser, &http_parser_settings, incomplete.c_str(), incomplete.length());\n    ASSERT_TRUE(length == incomplete.length());\n    ASSERT_TRUE(llhttp_get_errno(parser) == HPE_OK);\n\n    HttpContext *ctx = static_cast<HttpContext *>(parser->data);\n    ASSERT_TRUE(ctx->completed == 0);\n}\n\nTEST(http_parser, method) {\n    llhttp_t *parser = swoole_http_parser_create();\n    ON_SCOPE_EXIT {\n        swoole_http_destroy_context(parser);\n    };\n\n    string incomplete = \"GET /get HTTP/1.1\\r\\n\\r\\n\";\n    size_t length =\n        swoole_llhttp_parser_execute(parser, &http_parser_settings, incomplete.c_str(), incomplete.length());\n    ASSERT_TRUE(length == incomplete.length());\n    ASSERT_TRUE(llhttp_get_method(parser) == HTTP_GET);\n    ASSERT_STREQ(llhttp_method_name(HTTP_GET), \"GET\");\n}\n\nTEST(http_parser, websocket) {\n    llhttp_t *parser = swoole_http_parser_create();\n    ON_SCOPE_EXIT {\n        swoole_http_destroy_context(parser);\n    };\n\n    string websocket = \"GET /chat HTTP/1.1\\r\\n\"\n                       \"Host: example.com\\r\\n\"\n                       \"Upgrade: websocket\\r\\n\"\n                       \"Connection: Upgrade\\r\\n\"\n                       \"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\\r\\n\"\n                       \"Sec-WebSocket-Version: 13\\r\\n\"\n                       \"Origin: http://example.com\\r\\n\\r\\n\";\n    size_t length = swoole_llhttp_parser_execute(parser, &http_parser_settings, websocket.c_str(), websocket.length());\n    ASSERT_TRUE(length == websocket.length());\n    ASSERT_TRUE(llhttp_get_errno(parser) == HPE_OK);\n    ASSERT_TRUE(llhttp_get_upgrade(parser) == 1);\n\n    HttpContext *ctx = static_cast<HttpContext *>(parser->data);\n    ASSERT_TRUE(ctx->completed == 1);\n}\n\nTEST(http_parser, http2) {\n    llhttp_t *parser = swoole_http_parser_create();\n    ON_SCOPE_EXIT {\n        swoole_http_destroy_context(parser);\n    };\n\n    string http2 = \"PRI * HTTP/2.0\\r\\n\\r\\nSM\\r\\n\\r\\n\";\n    size_t length = swoole_llhttp_parser_execute(parser, &http_parser_settings, http2.c_str(), http2.length());\n    ASSERT_TRUE(length == http2.length());\n    ASSERT_TRUE(llhttp_get_errno(parser) == HPE_PAUSED_H2_UPGRADE);\n    ASSERT_TRUE(llhttp_get_method(parser) == HTTP_PRI);\n}\n\nTEST(http_parser, header_field_and_value) {\n    string request = \"GET /get HTTP/1.1\\r\\n\"\n                     \"Host: www.maria.com\\r\\n\"\n                     \"User-Agent: curl/7.64.1\\r\\n\"\n                     \"Accept: */*\\r\\n\"\n                     \"Connection: keep-alive\\r\\n\"\n                     \"\\r\\n\";\n\n    llhttp_t *parser = swoole_http_parser_create();\n    ON_SCOPE_EXIT {\n        swoole_http_destroy_context(parser);\n    };\n\n    size_t length = swoole_llhttp_parser_execute(parser, &http_parser_settings, request.c_str(), request.length());\n    ASSERT_TRUE(length == request.length());\n    HttpContext *ctx = static_cast<HttpContext *>(parser->data);\n    ASSERT_TRUE(ctx->completed == 1);\n\n    ASSERT_STREQ(ctx->header_fields[0].c_str(), \"Host\");\n    ASSERT_STREQ(ctx->header_fields[1].c_str(), \"User-Agent\");\n    ASSERT_STREQ(ctx->header_fields[2].c_str(), \"Accept\");\n    ASSERT_STREQ(ctx->header_fields[3].c_str(), \"Connection\");\n\n    ASSERT_STREQ(ctx->header_values[0].c_str(), \"www.maria.com\");\n    ASSERT_STREQ(ctx->header_values[1].c_str(), \"curl/7.64.1\");\n    ASSERT_STREQ(ctx->header_values[2].c_str(), \"*/*\");\n    ASSERT_STREQ(ctx->header_values[3].c_str(), \"keep-alive\");\n}\n\nTEST(http_parser, query_string) {\n    string request = \"GET /get/swoole?a=1&b=2 HTTP/1.1\\r\\n\\r\\n\";\n    llhttp_t *parser = swoole_http_parser_create();\n    ON_SCOPE_EXIT {\n        swoole_http_destroy_context(parser);\n    };\n\n    size_t length = swoole_llhttp_parser_execute(parser, &http_parser_settings, request.c_str(), request.length());\n    ASSERT_TRUE(length == request.length());\n    ASSERT_TRUE(llhttp_get_errno(parser) == HPE_OK);\n\n    HttpContext *ctx = static_cast<HttpContext *>(parser->data);\n    ASSERT_STREQ(ctx->query_string.c_str(), \"/get/swoole?a=1&b=2\");\n}\n\nTEST(http_parser, chunk) {\n    string chunk = \"HTTP/1.1 200 OK\\r\\n\"\n                   \"Content-Type: text/plain\\r\\n\"\n                   \"Transfer-Encoding: chunked\\r\\n\\r\\n\"\n                   \"5\\r\\n\"\n                   \"Hello\\r\\n\"\n                   \"6\\r\\n\"\n                   \" World\\r\\n\"\n                   \"3\\r\\n\"\n                   \"!!!\\r\\n\"\n                   \"0\\r\\n\\r\\n\";\n\n    llhttp_t *parser = swoole_http_parser_create(HTTP_RESPONSE);\n    ON_SCOPE_EXIT {\n        swoole_http_destroy_context(parser);\n    };\n\n    size_t length = swoole_llhttp_parser_execute(parser, &http_parser_settings, chunk.c_str(), chunk.length());\n    ASSERT_EQ(length, chunk.length());\n    ASSERT_EQ(llhttp_get_errno(parser), HPE_OK);\n\n    HttpContext *ctx = static_cast<HttpContext *>(parser->data);\n    ASSERT_TRUE(ctx->completed == 1);\n}\n\nTEST(http_parser, response) {\n    string response = \"HTTP/1.1 200 OK\\r\\n\"\n                      \"Server: CLOUD ELB 1.0.0\\r\\n\"\n                      \"Date: Sat, 04 Feb 2023 08:47:14 GMT\\r\\n\"\n                      \"Content-Type: application/json\\r\\n\"\n                      \"Content-Length: 18\\r\\n\"\n                      \"Connection: close\\r\\n\"\n                      \"\\r\\n\"\n                      \"{\\\"name\\\" : \\\"laala\\\"}\";\n\n    llhttp_t *parser = swoole_http_parser_create(HTTP_RESPONSE);\n    ON_SCOPE_EXIT {\n        swoole_http_destroy_context(parser);\n    };\n\n    size_t length = swoole_llhttp_parser_execute(parser, &http_parser_settings, response.c_str(), response.length());\n    ASSERT_TRUE(length == response.length());\n    ASSERT_TRUE(llhttp_get_errno(parser) == HPE_OK);\n    ASSERT_TRUE(llhttp_get_status_code(parser) == HTTP_STATUS_OK);\n    ASSERT_TRUE(llhttp_get_http_major(parser) == 1);\n    ASSERT_TRUE(llhttp_get_http_minor(parser) == 1);\n\n    HttpContext *ctx = static_cast<HttpContext *>(parser->data);\n    ASSERT_TRUE(ctx->completed == 1);\n    ASSERT_STREQ(ctx->header_fields[0].c_str(), \"Server\");\n    ASSERT_STREQ(ctx->header_fields[1].c_str(), \"Date\");\n    ASSERT_STREQ(ctx->header_fields[2].c_str(), \"Content-Type\");\n    ASSERT_STREQ(ctx->header_fields[3].c_str(), \"Content-Length\");\n    ASSERT_STREQ(ctx->header_fields[4].c_str(), \"Connection\");\n\n    ASSERT_STREQ(ctx->header_values[0].c_str(), \"CLOUD ELB 1.0.0\");\n    ASSERT_STREQ(ctx->header_values[1].c_str(), \"Sat, 04 Feb 2023 08:47:14 GMT\");\n    ASSERT_STREQ(ctx->header_values[2].c_str(), \"application/json\");\n    ASSERT_STREQ(ctx->header_values[3].c_str(), \"18\");\n    ASSERT_STREQ(ctx->header_values[4].c_str(), \"close\");\n}\n\n// clang-format off\nconst vector<string> request_error_protocols = {\n    // request/connection\n    \"PUT /url HTTP/1.0\\r\\n\\r\\nPUT /url HTTP/1.1\\r\\n\\r\\n\",\n    \"POST / HTTP/1.1\\r\\nHost: www.example.com\\r\\nContent-Type: application/x-www-form-urlencoded\\r\\nContent-Length: 4\\r\\nConnection: close\\r\\n\\r\\nq=42\\r\\n\\r\\nGET / HTTP/1.1\\r\\n\",\n    \"PUT /url HTTP/1.1\\r\\nConnection : upgrade\\r\\nContent-Length: 4\\r\\nUpgrade: ws\\r\\n\\r\\nabcdefgh\",\n\n    // request/content-length\n    \"PUT /url HTTP/1.1\\r\\nContent-Length: 1000000000000000000000\\r\\n\\r\\n\",\n    \"PUT /url HTTP/1.1\\r\\nContent-Length: 1\\r\\nContent-Length: 2\\r\\n\\r\\n\",\n    \"PUT /url HTTP/1.1\\r\\nContent-Length: 1\\r\\nTransfer-Encoding: identity\\r\\n\\r\\n\",\n    \"PUT /url HTTP/1.1\\r\\nConnection: upgrade\\r\\nContent-Length : 4\\r\\nUpgrade: ws\\r\\n\\r\\nabcdefgh\",\n    \"POST / HTTP/1.1\\r\\nContent-Length: 4 2\\r\\n\\r\\n\",\n    \"POST / HTTP/1.1\\r\\nContent-Length: 13 37\\r\\n\\r\\n\",\n    \"POST / HTTP/1.1\\r\\nContent-Length:\\r\\n\\r\\n\",\n    \"PUT /url HTTP/1.1\\r\\nContent\\rLength: 003\\r\\n\\r\\nabc\",\n    \"PUT /url HTTP/1.1\\r\\nContent-Length: 3\\r\\n\\rabc\",\n\n    // request/method\n    \"PRI * HTTP/1.1\\r\\n\\r\\nSM\\r\\n\\r\\n\",\n\n    // request/sample\n    \"GET / HTTP/1.1\\rLine: 1\\r\\n\\r\\n\",\n    \"GET / HTTP/1.1\\r\\nLine1:   abc\\n\\tdef\\n ghi\\n\\t\\tjkl\\n  mno \\n\\t \\tqrs\\nLine2: \\t line2\\t\\nLine3:\\n line3\\nLine4: \\n \\nConnection:\\n close\\n\\n\",\n\n    // request/transfer-encoding\n    \"POST /chunked_w_unicorns_after_length HTTP/1.1\\r\\nHost: localhost\\r\\nTransfer-encoding: chunked\\r\\n\\r\\n2 erfrferferf\\r\\naa\\r\\n0 rrrr\\r\\n\\r\\n\",\n    \"POST /chunked_w_unicorns_after_length HTTP/1.1\\r\\nHost: localhost\\r\\nTransfer-encoding: chunked\\r\\n\\r\\n2;\\r\\naa\\r\\n0\\r\\n\\r\\n\",\n    \"POST /chunked_w_unicorns_after_length HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n5;ilovew3=\\\"abc\\\";somuchlove=\\\"def; ghi\\r\\nhello\\r\\n6;blahblah;blah\\r\\n world\\r\\n0\\r\\n\\r\\n\",\n    \"PUT /url HTTP/1.1\\r\\nTransfer-Encoding: pigeons\\r\\n\\r\\n\",\n    \"POST /post_identity_body_world?q=search#hey HTTP/1.1\\r\\nAccept: */*\\r\\nTransfer-Encoding: identity\\r\\nContent-Length: 5\\r\\n\\r\\nWorld\",\n    \"POST / HTTP/1.1\\r\\nHost: foo\\r\\nContent-Length: 10\\r\\nTransfer-Encoding:\\r\\nTransfer-Encoding:\\r\\nTransfer-Encoding:\\r\\n\\r\\n2\\r\\nAA\\r\\n0\\r\\n\",\n    \"POST /post_identity_body_world?q=search#hey HTTP/1.1\\r\\nAccept: */*\\r\\nTransfer-Encoding: chunked, deflate\\r\\n\\r\\nWorld\",\n    \"POST /post_identity_body_world?q=search#hey HTTP/1.1\\r\\nAccept: */*\\r\\nTransfer-Encoding: chunked\\r\\nTransfer-Encoding: deflate\\r\\n\\r\\nWorld\",\n    \"POST /post_identity_body_world?q=search#hey HTTP/1.1\\r\\nAccept: */*\\r\\nTransfer-Encoding: chunkedchunked\\r\\n\\r\\n5\\r\\nWorld\\r\\n0\\r\\n\\r\\n\",\n    \"PUT /url HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n3\\r\\nfoo\\r\\n\\r\\n\",\n    \"PUT /url HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n3 \\n  \\r\\n\\\\\\r\\nfoo\\r\\n\\r\\n\",\n    \"PUT /url HTTP/1.1\\r\\nTransfer-Encoding: chunked  abc\\r\\n\\r\\n5\\r\\nWorld\\r\\n0\\r\\n\\r\\n\",\n    \"GET / HTTP/1.1\\r\\nHost: a\\r\\nConnection: close \\r\\nTransfer-Encoding: chunked \\r\\n\\r\\n5\\r\\r;ABCD\\r\\n34\\r\\nE\\r\\n0\\r\\n\\r\\nGET / HTTP/1.1 \\r\\nHost: a\\r\\nContent-Length: 5\\r\\n\\r\\n0\\r\\n\\r\\n\",\n    \"GET / HTTP/1.1\\r\\nHost: a\\r\\nConnection: close \\r\\nTransfer-Encoding: chunked \\r\\n\\r\\n5\\r\\nABCDE0\\r\\n\\r\\n\",\n    \"PUT /url HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\na \\r\\n0123456789\\r\\n0\\r\\n\\r\\n\",\n\n    // request/invalid\n    \"GET /music/sweet/music ICE/1.0\\r\\nHost: example.com\\r\\n\\r\\n\",\n    \"GET /music/sweet/music IHTTP/1.0\\r\\nHost: example.com\\r\\n\\r\\n\",\n    \"PUT /music/sweet/music RTSP/1.0\\r\\nHost: example.com\\r\\n\\r\\n\",\n    \"ANNOUNCE /music/sweet/music HTTP/1.0\\r\\nHost: example.com\\r\\n\\r\\n\",\n    \"GET / HTTP/1.1\\r\\nFoo: 1\\rBar: 2\\r\\n\\r\\n\",\n    \"POST / HTTP/1.1\\r\\nHost: localhost:5000\\r\\nx:x\\nTransfer-Encoding: chunked\\r\\n\\r\\n1\\r\\nA\\r\\n0\\r\\n\\r\\n\",\n    \"GET / HTTP/1.1\\r\\nConnection: close\\r\\nHost: a\\r\\n\\rZGET /evil: HTTP/1.1\\r\\nHost: a\\r\\n\\r\\n\",\n    \"GET / HTTP/1.1\\r\\nConnection: close\\r\\nHost: a\\r\\n\\r\\nZGET /evil: HTTP/1.1\\r\\nHost: a\\r\\n\\r\\n\",\n    \"POST / HTTP/1.1\\r\\nConnection: Close\\r\\nHost: localhost:5000\\r\\nx:\\rTransfer-Encoding: chunked\\r\\n\\r\\n1\\r\\nA\\r\\n0\\r\\n\\r\\n\",\n    \"POST / HTTP/1.1\\r\\nHost: localhost:5000\\r\\nx:\\nTransfer-Encoding: chunked\\r\\n\\r\\n1\\r\\nA\\r\\n0\\r\\n\\r\\n\",\n    \"GET / HTTP/1.1\\r\\nFo@: Failure\\r\\n\\r\\n\",\n    \"GET / HTTP/1.1\\r\\nFoo\\01\\test: Bar\\r\\n\\r\\n\",\n    \"GET / HTTP/1.1\\r\\n: Bar\\r\\n\\r\\n\",\n    \"MKCOLA / HTTP/1.1\\r\\n\\r\\n\",\n    \"GET / HTTP/1.1\\r\\nname\\r\\n : value\\r\\n\\r\\n\",\n    \"GET / HTTP/1.1\\r\\nHost: www.example.com\\r\\nConnection\\r\\033\\065\\325eep-Alive\\r\\nAccept-Encoding: gzip\\r\\n\\r\\n\",\n    \"GET / HTTP/1.1\\r\\nHost: www.example.com\\r\\nX-Some-Header\\r\\033\\065\\325eep-Alive\\r\\nAccept-Encoding: gzip\\r\\n\\r\\n\",\n    \"GET / HTTP/1.1\\r\\nHost: localhost\\r\\nDummy: x\\nContent-Length: 23\\r\\n\\r\\nGET / HTTP/1.1\\r\\nDummy: GET /admin HTTP/1.1\\r\\nHost: localhost\\r\\n\\r\\n\",\n    \"GET / HTTP/5.6\",\n    \"GET / HTTP/1.1\\r\\n Host: foo\\r\\n\",\n    \"POST / HTTP/1.1\\nTransfer-Encoding: chunked\\nTrailer: Baz\\nFoo: abc\\nBar: def\\n\\n1\\nA\\n1;abc\\nB\\n1;def=ghi\\nC\\n1;jkl=\\\"mno\\\"\\nD\\n0\\n\\nBaz: ghi\\n\\n\",\n    \"POST /hello HTTP/1.1\\r\\nHost: localhost\\r\\nFoo: bar\\r\\n Content-Length: 38\\r\\n\\r\\nGET /bye HTTP/1.1\\r\\nHost: localhost\\r\\n\\r\\n\",\n\n    // request/uri\n    \"GET /δ¶/δt/pope?q=1#narf HTTP/1.1\\r\\nHost: github.com\\r\\n\\r\\n\",\n    \"GET /foo bar/ HTTP/1.1\\r\\n\\r\\n\",\n};\n\nconst vector<string> request_error_messages = {\n    // request/connection\n    \"Data after `Connection: close`\",\n    \"Data after `Connection: close`\",\n    \"Invalid header field char\",\n\n    // request/content-length\n    \"Content-Length overflow\",\n    \"Duplicate Content-Length\",\n    \"Transfer-Encoding can't be present with Content-Length\",\n    \"Invalid header field char\",\n    \"Invalid character in Content-Length\",\n    \"Invalid character in Content-Length\",\n    \"Empty Content-Length\",\n    \"Invalid header token\",\n    \"Expected LF after headers\",\n\n    // request/method\n    \"Pause on PRI/Upgrade\",\n\n    // request/sample\n    \"Expected CRLF after version\",\n    \"Missing expected CR after header value\",\n\n    // request/transfer-encoding\n    \"Invalid character in chunk size\",\n    \"Invalid character in chunk extensions\",\n    \"Invalid character in chunk extensions quoted value\",\n    \"Request has invalid `Transfer-Encoding`\",\n    \"Content-Length can't be present with Transfer-Encoding\",\n    \"Transfer-Encoding can't be present with Content-Length\",\n    \"Invalid `Transfer-Encoding` header value\",\n    \"Invalid `Transfer-Encoding` header value\",\n    \"Request has invalid `Transfer-Encoding`\",\n    \"Invalid character in chunk size\",\n    \"Invalid character in chunk size\",\n    \"Request has invalid `Transfer-Encoding`\",\n    \"Expected LF after chunk size\",\n    \"Expected LF after chunk data\",\n    \"Invalid character in chunk size\",\n\n    // request/invalid\n    \"Expected SOURCE method for ICE/x.x request\",\n    \"Expected HTTP/, RTSP/ or ICE/\",\n    \"Invalid method for RTSP/x.x request\",\n    \"Invalid method for HTTP/x.x request\",\n    \"Missing expected LF after header value\",\n    \"Missing expected CR after header value\",\n    \"Expected LF after headers\",\n    \"Data after `Connection: close`\",\n    \"Expected LF after CR\",\n    \"Invalid header value char\",\n    \"Invalid header token\",\n    \"Invalid header token\",\n    \"Invalid header token\",\n    \"Expected space after method\",\n    \"Invalid header token\",\n    \"Invalid header token\",\n    \"Invalid header token\",\n    \"Missing expected CR after header value\",\n    \"Invalid HTTP version\",\n    \"Unexpected space after start line\",\n    \"Expected CRLF after version\",\n    \"Unexpected whitespace after header value\",\n\n    // request/uri\n    \"Invalid char in url path\",\n    \"Expected HTTP/, RTSP/ or ICE/\",\n};\n\nconst vector<string> response_error_protocols = {\n    // response/connection\n    \"HTTP/1.1 204 No content\\r\\nConnection: close\\r\\n\\r\\nHTTP/1.1 200 OK\",\n    \"HTTP/1.1 200 No content\\r\\nContent-Length: 5\\r\\nConnection: close\\r\\n\\r\\n2ad731e3-4dcd-4f70-b871-0ad284b29ffc\",\n\n    // response/invalid\n    \"HTP/1.1 200 OK\\r\\n\\r\\n\",\n    \"HTTP/01.1 200 OK\\r\\n\\r\\n\",\n    \"HTTP/11.1 200 OK\\r\\n\\r\\n\",\n    \"HTTP/1.01 200 OK\\r\\n\\r\\n\",\n    \"HTTP/1.1\\t200 OK\\r\\n\\r\\n\",\n    \"\\rHTTP/1.1\\t200 OK\\r\\n\\r\\n\",\n    \"HTTP/1.1 200 OK\\r\\nFoo: 1\\rBar: 2\\r\\n\\r\\n\",\n    \"HTTP/5.6 200 OK\\r\\n\\r\\n\",\n    \"HTTP/1.1 200 OK\\r\\n Host: foo\\r\\n\",\n    \"HTTP/1.1  200 OK\\r\\n\\r\\n\",\n    \"HTTP/1.1 2 OK\\r\\n\\r\\n\",\n    \"HTTP/1.1 200 OK\\nContent-Length: 0\\n\\n\",\n    \"HTTP/1.1 200 OK\\nFoo: abc\\nBar: def\\n\\nBODY\\n\",\n\n    // response/sample\n    \"HTTPER/1.1 200 OK\\r\\n\\r\\n\",\n    \"HTTP/1.1 200 OK\\nContent-Type: text/html; charset=utf-8\\nConnection: close\\n\\nthese headers are from http://news.ycombinator.com/\",\n    \"HTTP/1.1 200 OK\\r\\nServer: Microsoft-IIS/6.0\\r\\nX-Powered-By: ASP.NET\\r\\nen-US Content-Type: text/xml\\r\\nContent-Type: text/xml\\r\\nContent-Length: 16\\r\\nDate: Fri, 23 Jul 2010 18:45:38 GMT\\r\\nConnection: keep-alive\\r\\n\\r\\n<xml>hello</xml>\",\n\n    // response/transfer-encoding\n    \"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n25 \\r\\nThis is the data in the first chunk\\r\\n1C\\r\\nand this is the second one\\r\\n0 \\r\\n\\r\\n\",\n    \"HTTP/1.1 200 OK\\r\\nHost: localhost\\r\\nTransfer-encoding: chunked\\r\\n\\r\\n2 erfrferferf\\r\\naa\\r\\n0 rrrr\\r\\n\\r\\n\",\n    \"HTTP/1.1 200 OK\\r\\nHost: localhost\\r\\nTransfer-encoding: chunked\\r\\n\\r\\n2;\\r\\naa\\r\\n0\\r\\n\\r\\n\",\n    \"HTTP/1.1 200 OK\\r\\nHost: localhost\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n5;ilovew3=\\\"abc\\\";somuchlove=\\\"def; ghi\\r\\nhello\\r\\n6;blahblah;blah\\r\\n world\\r\\n0\\r\\n\",\n};\n\nconst vector<string> response_error_messages = {\n    // response/connection\n    \"Data after `Connection: close`\",\n    \"Data after `Connection: close`\",\n\n    // response/invalid\n    \"Expected HTTP/, RTSP/ or ICE/\",\n    \"Expected dot\",\n    \"Expected dot\",\n    \"Expected space after version\",\n    \"Expected space after version\",\n    \"Expected space after version\",\n    \"Missing expected LF after header value\",\n    \"Invalid HTTP version\",\n    \"Unexpected space after start line\",\n    \"Invalid status code\",\n    \"Invalid status code\",\n    \"Missing expected CR after response line\",\n    \"Missing expected CR after response line\",\n\n    // response/sample\n    \"Expected HTTP/, RTSP/ or ICE/\",\n    \"Missing expected CR after response line\",\n    \"Invalid header token\",\n\n    // response/transfer-encoding\n    \"Invalid character in chunk size\",\n    \"Invalid character in chunk size\",\n    \"Invalid character in chunk extensions\",\n    \"Invalid character in chunk extensions quoted value\",\n};\n// clang-format on\n\nTEST(http_parser, request_error_case) {\n    ASSERT_TRUE(request_error_protocols.size() == request_error_messages.size());\n    llhttp_t *parser = swoole_http_parser_create(HTTP_REQUEST);\n    ON_SCOPE_EXIT {\n        swoole_http_destroy_context(parser);\n    };\n\n    for (size_t i = 0; i < request_error_protocols.size(); ++i) {\n        string error_protocol = request_error_protocols[i];\n        swoole_llhttp_parser_execute(parser, &http_parser_settings, error_protocol.c_str(), error_protocol.length());\n        ASSERT_STREQ(llhttp_get_error_reason(parser), request_error_messages[i].c_str());\n        ASSERT_NE(llhttp_get_errno(parser), HPE_OK);\n        llhttp_reset(parser);\n    }\n}\n\nTEST(http_parser, response_error_case) {\n    ASSERT_TRUE(response_error_protocols.size() == response_error_messages.size());\n    llhttp_t *parser = swoole_http_parser_create(HTTP_RESPONSE);\n    ON_SCOPE_EXIT {\n        swoole_http_destroy_context(parser);\n    };\n\n    for (size_t i = 0; i < response_error_protocols.size(); ++i) {\n        string error_protocol = response_error_protocols[i];\n        swoole_llhttp_parser_execute(parser, &http_parser_settings, error_protocol.c_str(), error_protocol.length());\n        ASSERT_STREQ(llhttp_get_error_reason(parser), response_error_messages[i].c_str());\n        ASSERT_NE(llhttp_get_errno(parser), HPE_OK);\n        llhttp_reset(parser);\n    }\n}\n\n// clang-format off\nconst vector<string> request_success_case = {\n    \"PUT /url HTTP/1.1\\r\\nConnection: keep-alive\\r\\n\\r\\n\",\n    \"PUT /url HTTP/1.1\\r\\nConnection: keep-alive\\r\\n\\r\\nPUT /url HTTP/1.1\\r\\nConnection: keep-alive\\r\\n\\r\\n\",\n    \"PUT /url HTTP/1.1\\r\\nConnection: close\\r\\n\\r\\n\",\n    \"PUT /url HTTP/1.1\\r\\nConnection: close, token, upgrade, token, keep-alive\\r\\n\\r\\n\",\n    \"GET /demo HTTP/1.1\\r\\nHost: example.com\\r\\nConnection: keep-alive, upgrade\\r\\nUpgrade: WebSocket\\r\\n\\r\\nHot diggity dogg\",\n    \"PUT /url HTTP/1.1\\r\\nConnection: upgrade\\r\\nUpgrade: ws\\r\\n\\r\\n\",\n    \"PUT /url HTTP/1.1\\r\\nConnection: upgrade\\r\\nContent-Length: 4\\r\\nUpgrade: ws\\r\\n\\r\\nabcdefgh\",\n    \"GET /demo HTTP/1.1\\r\\nHost: example.com\\r\\nConnection: Upgrade\\r\\nSec-WebSocket-Key2: 12998 5 Y3 1  .P00\\r\\nSec-WebSocket-Protocol: sample\\r\\nUpgrade: WebSocket\\r\\nSec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\\r\\nOrigin: http://example.com\\r\\n\\r\\nHot diggity dogg\",\n    \"POST /demo HTTP/1.1\\r\\nHost: example.com\\r\\nConnection: Upgrade\\r\\nUpgrade: HTTP/2.0\\r\\nContent-Length: 15\\r\\n\\r\\nsweet post body\\\\Hot diggity dogg\",\n\n    \"PUT /url HTTP/1.1\\r\\nContent-Length: 003\\r\\n\\r\\nabc\",\n    \"PUT /url HTTP/1.1\\r\\nContent-Length: 003\\r\\nOhai: world\\r\\n\\r\\nabc\",\n    \"GET /get_funky_content_length_body_hello HTTP/1.0\\r\\nconTENT-Length: 5\\r\\n\\r\\nHELLO\",\n    \"POST / HTTP/1.1\\r\\nContent-Length:  42 \\r\\n\\r\\n\",\n    \"REPORT /test HTTP/1.1\\r\\n\\r\\n\",\n    \"CONNECT 0-home0.netscape.com:443 HTTP/1.0\\r\\nUser-agent: Mozilla/1.1N\\r\\nProxy-authorization: basic aGVsbG86d29ybGQ=\\r\\n\\r\\nsome data\\nand yet even more data\",\n    \"CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\\r\\nUser-agent: Mozilla/1.1N\\r\\nProxy-authorization: basic aGVsbG86d29ybGQ=\\r\\n\\r\\n\",\n    \"CONNECT foo.bar.com:443 HTTP/1.0\\r\\nUser-agent: Mozilla/1.1N\\r\\nProxy-authorization: basic aGVsbG86d29ybGQ=\\r\\nContent-Length: 10\\r\\n\\r\\nblarfcicle\\\"\",\n    \"M-SEARCH * HTTP/1.1\\r\\nHOST: 239.255.255.250:1900\\r\\nMAN: \\\"ssdp:discover\\\"\\r\\nST: \\\"ssdp:all\\\"\\r\\n\\r\\n\",\n    \"PATCH /file.txt HTTP/1.1\\r\\nHost: www.example.com\\r\\nContent-Type: application/example\\r\\nIf-Match: \\\"e0023aa4e\\\"\\r\\nContent-Length: 10\\r\\n\\r\\ncccccccccc\",\n    \"PURGE /file.txt HTTP/1.1\\r\\nHost: www.example.com\\r\\n\\r\\n\",\n    \"SEARCH / HTTP/1.1\\r\\nHost: www.example.com\\r\\n\\r\\n\",\n    \"LINK /images/my_dog.jpg HTTP/1.1\\r\\nHost: example.com\\r\\nLink: <http://example.com/profiles/joe>; rel=\\\"tag\\\"\\r\\nLink: <http://example.com/profiles/sally>; rel=\\\"tag\\\"\\r\\n\\r\\n\",\n    \"UNLINK /images/my_dog.jpg HTTP/1.1\\r\\nHost: example.com\\r\\nLink: <http://example.com/profiles/sally>; rel=\\\"tag\\\"\\r\\n\\r\\n\",\n    \"SOURCE /music/sweet/music HTTP/1.1\\r\\nHost: example.com\\r\\n\\r\\n\",\n    \"SOURCE /music/sweet/music ICE/1.0\\r\\nHost: example.com\\r\\n\\r\\n\",\n    \"OPTIONS /music/sweet/music RTSP/1.0\\r\\nHost: example.com\\r\\n\\r\\n\",\n    \"ANNOUNCE /music/sweet/music RTSP/1.0\\r\\nHost: example.com\\r\\n\\r\\n\",\n    \"QUERY /contacts HTTP/1.1\\r\\nHost: example.org\\r\\nContent-Type: example/query\\r\\nAccept: text/csv\\r\\nContent-Length: 41\\r\\n\\r\\nselect surname, givenname, email limit 10\",\n    \"POST / HTTP/1.1\\r\\nContent-Length: 3\\r\\n\\r\\nabc\",\n    \"POST / HTTP/1.1\\r\\nContent-Length: 3\\r\\n\\r\\nabc\",\n    \"POST / HTTP/1.1\\r\\nContent-Length: 3\\r\\n\\r\\nabc\",\n    \"POST / HTTP/1.1\\r\\nContent-Length: 3\\r\\n\\r\\nabc\",\n    \"POST / HTTP/1.1\\r\\nContent-Length: 3\\r\\n\\r\\nabc\",\n    \"POST / HTTP/1.1\\r\\nContent-Length: 3\\r\\n\\r\\nabc\",\n    \"POST / HTTP/1.1\\r\\nContent-Length: 3\\r\\n\\r\\nabc\",\n    \"POST / HTTP/1.1\\r\\nContent-Length: 3\\r\\n\\r\\nabc\",\n    \"POST / HTTP/1.1\\r\\nContent-Length: 3\\r\\n\\r\\nabc\",\n    \"POST / HTTP/1.1\\r\\nContent-Length: 3\\r\\n\\r\\nabc\",\n    \"PUT / HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\na\\r\\n0123456789\\r\\n0\\r\\n\\r\\n\",\n    \"PUT / HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\na;foo=bar\\r\\n0123456789\\r\\n0\\r\\n\\r\\n\",\n    \"PUT / HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\na;foo=bar\\r\\n0123456789\\r\\n0\\r\\n\\r\\n\",\n    \"PUT / HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\na\\r\\n0123456789\\r\\n0\\r\\n\\r\\n\",\n\n    \"POST /aaa HTTP/1.1\\r\\nContent-Length: 3\\r\\n\\r\\nAAA\\r\\nPUT /bbb HTTP/1.1\\r\\nContent-Length: 4\\r\\n\\r\\nBBBB\\r\\nPATCH /ccc HTTP/1.1\\r\\nContent-Length: 5\\r\\n\\r\\nCCCC\",\n    \"OPTIONS /url HTTP/1.1\\r\\nHeader1: Value1\\r\\nHeader2:\\t Value2\\r\\n\\r\\n\",\n    \"HEAD /url HTTP/1.1\\r\\n\\r\\n\",\n    \"GET /test HTTP/1.1\\r\\nUser-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\\r\\nHost: 0.0.0.0=5000\\r\\nAccept: */*\\r\\n\\r\\n\",\n    \"GET /favicon.ico HTTP/1.1\\r\\nHost: 0.0.0.0=5000\\r\\nUser-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\\r\\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\\r\\nAccept-Language: en-us,en;q=0.5\\r\\nAccept-Encoding: gzip,deflate\\r\\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\\r\\nKeep-Alive: 300\\r\\nConnection: keep-alive\\r\\n\\r\\n\",\n    \"GET /dumbpack HTTP/1.1\\r\\naaaaaaaaaaaaa:++++++++++\\r\\n\\r\\n\",\n    \"GET /get_no_headers_no_body/world HTTP/1.1\\r\\n\\r\\n\",\n    \"GET /get_one_header_no_body HTTP/1.1\\r\\nAccept: */*\\r\\n\\r\\n\",\n    \"GET /test HTTP/1.0\\r\\nHost: 0.0.0.0:5000\\r\\nUser-Agent: ApacheBench/2.3\\r\\nAccept: */*\\r\\n\\r\\n\",\n    \"\\r\\nGET /test HTTP/1.1\\r\\n\\r\\n\",\n    \"GET /\\r\\n\\r\\n\",\n    \"\\r\\nGET /url HTTP/1.1\\r\\nHeader1: Value1\\r\\n\\r\\n\",\n    \"GET / HTTP/1.1\\r\\nTest: DÃ¼sseldorf\\r\\n\\r\\n\",\n    \"OPTIONS /url HTTP/1.1\\r\\nHeader1: Value1\\r\\nHeader2: \\xffValue2\\r\\n\\r\\n\",\n    \"GET / HTTP/1.1\\r\\nX-SSL-Nonsense:   -----BEGIN CERTIFICATE-----\\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgHTTPAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\\tRA==\\t-----END CERTIFICATE-----\\r\\n\\r\\n\",\n\n    \"PUT /url HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n\",\n    \"PUT /url HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\na\\r\\n0123456789\\r\\n0\\r\\n\\r\\n\",\n    \"PUT /url HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\nA\\r\\n0123456789\\r\\n0\\r\\n\\r\\n\",\n    \"POST /post_chunked_all_your_base HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n1e\\r\\nall your base are belong to us\\r\\n0\\r\\n\\r\\n\",\n    \"POST /two_chunks_mult_zero_end HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n5\\r\\nhello\\r\\n6\\r\\n world\\r\\n000\\r\\n\\r\\n\",\n    \"POST /chunked_w_trailing_headers HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n5\\r\\nhello\\r\\n6\\r\\n world\\r\\n0\\r\\nVary: *\\r\\nContent-Type: text/plain\\r\\n\\r\\n\",\n    \"POST /chunked_w_unicorns_after_length HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n5;ilovew3;somuchlove=aretheseparametersfor;another=withvalue\\r\\nhello\\r\\n6;blahblah;blah\\r\\n world\\r\\n0\\r\\n\\r\\n\",\n\n    \"GET /with_\\\"lovely\\\"_quotes?foo=\\\"bar\\\" HTTP/1.1\\r\\n\\r\\n\",\n    \"GET /test.cgi?foo=bar?baz HTTP/1.1\\r\\n\\r\\n\",\n    \"GET http://hypnotoad.org?hail=all HTTP/1.1\\r\\n\\r\\n\",\n    \"GET http://hypnotoad.org:1234?hail=all HTTP/1.1\\r\\n\\r\\n\",\n    \"GET /test.cgi?query=| HTTP/1.1\\r\\n\\r\\n\",\n    \"GET http://hypnotoad.org:1234 HTTP/1.1\\r\\n\\r\\n\",\n    \"GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\\r\\n\\r\\n\",\n    \"GET http://a%12:b!&*$@hypnotoad.org:1234/toto HTTP/1.1\\r\\n\\r\\n\"\n};\n// clang-format on\n\nTEST(http_parser, request_success_case) {\n    llhttp_t *parser = swoole_http_parser_create(HTTP_REQUEST);\n    ON_SCOPE_EXIT {\n        swoole_http_destroy_context(parser);\n    };\n\n    HttpContext *ctx = nullptr;\n    for (size_t i = 0; i < request_success_case.size(); ++i) {\n        string success_protocol = request_success_case[i];\n        swoole_llhttp_parser_execute(\n            parser, &http_parser_settings, success_protocol.c_str(), success_protocol.length());\n        ASSERT_EQ(llhttp_get_errno(parser), HPE_OK);\n\n        ctx = static_cast<HttpContext *>(parser->data);\n        ASSERT_EQ(ctx->completed, 1);\n        llhttp_reset(parser);\n    }\n}\n\n// clang-format off\nconst vector<string> response_success_case = {\n    \"HTTP/1.1 200 OK\\r\\nContent-Type: text/html; charset=UTF-8\\r\\nContent-Length: 11\\r\\nProxy-Connection: close\\r\\nDate: Thu, 31 Dec 2009 20:55:48 +0000\\r\\n\\r\\nhello world\",\n    \"HTTP/1.0 200 OK\\r\\nConnection: keep-alive\\r\\n\\r\\nHTTP/1.0 200 OK\",\n    \"HTTP/1.0 204 No content\\r\\nConnection: keep-alive\\r\\n\\r\\nHTTP/1.0 200 OK\",\n    \"HTTP/1.1 200 OK\\r\\n\\r\\nHTTP/1.1 200 OK\",\n    \"HTTP/1.1 204 No content\\r\\n\\r\\nHTTP/1.1 200 OK\",\n    \"HTTP/1.1 101 Switching Protocols\\r\\nConnection: upgrade\\r\\nUpgrade: h2c\\r\\nContent-Length: 4\\r\\n\\r\\nbody\\\\\\r\\nproto\",\n    \"HTTP/1.1 101 Switching Protocols\\r\\nConnection: upgrade\\r\\nUpgrade: h2c\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n2\\r\\nbo\\r\\n2\\r\\ndy\\r\\n0\\r\\n\\r\\nproto\",\n    \"HTTP/1.1 200 OK\\r\\nConnection: upgrade\\r\\nUpgrade: h2c\\r\\n\\r\\nbody\",\n    \"HTTP/1.1 200 OK\\r\\nConnection: upgrade\\r\\nUpgrade: h2c\\r\\nContent-Length: 4\\r\\n\\r\\nbody\",\n    \"HTTP/1.1 200 OK\\r\\nConnection: upgrade\\r\\nUpgrade: h2c\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n2\\r\\nbo\\r\\n2\\r\\ndy\\r\\n0\\r\\n\\r\\n\",\n    \"HTTP/1.1 304 Not Modified\\r\\nContent-Length: 10\\r\\n\\r\\nHTTP/1.1 200 OK\\r\\nContent-Length: 5\\r\\n\\r\\nhello\",\n    \"HTTP/1.1 304 Not Modified\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\nHTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n5\\r\\nhello\\r\\n0\\r\\n\\r\\n\",\n    \"HTTP/1.1 100 Continue\\r\\n\\r\\nHTTP/1.1 404 Not Found\\r\\nContent-Type: text/plain; charset=utf-8\\r\\nContent-Length: 14\\r\\nDate: Fri, 15 Sep 2023 19:47:23 GMT\\r\\nServer: Python/3.10 aiohttp/4.0.0a2.dev0\\r\\n\\r\\n404: Not Found\",\n    \"HTTP/1.1 103 Early Hints\\r\\nLink: </styles.css>; rel=preload; as=style\\r\\n\\r\\nHTTP/1.1 200 OK\\r\\nDate: Wed, 13 Sep 2023 11:09:41 GMT\\r\\nConnection: keep-alive\\r\\nKeep-Alive: timeout=5\\r\\nContent-Length: 17\\r\\n\\r\\nresponse content\",\n\n    \"HTTP/1.1 200 OK\\r\\nDate: Tue, 04 Aug 2009 07:59:32 GMT\\r\\nServer: Apache\\r\\nX-Powered-By: Servlet/2.5 JSP/2.1\\r\\nContent-Type: text/xml; charset=utf-8\\r\\nConnection: close\\r\\n\\r\\n<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n<SOAP-ENV:Envelope xmlns:SOAP-ENV=\\\"http://schemas.xmlsoap.org/soap/envelope/\\\">\\n  <SOAP-ENV:Body>\\n    <SOAP-ENV:Fault>\\n       <faultcode>SOAP-ENV:Client</faultcode>\\n       <faultstring>Client Error</faultstring>\\n    </SOAP-ENV:Fault>\\n  </SOAP-ENV:Body>\\n</SOAP-ENV:Envelope>\",\n    \"HTTP/1.1 200 OK\\r\\nContent-Length-X: 0\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n2\\r\\nOK\\r\\n0\\r\\n\\r\\n\",\n    \"HTTP/1.1 200 OK\\r\\nContent-Length: 123\\r\\n\\r\\nHTTP/1.1 200 OK\\r\\nContent-Length: 456\\r\\n\\r\\n\",\n\n    \"HTTP/1.1 200 OK\\r\\n\\r\\n\",\n\n    \"HTTP/1.1 200 OK\\r\\nContent-Length: 3\\r\\n\\r\\nabc\",\n    \"HTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\na\\r\\n0123456789\\r\\n0\\r\\n\\r\\n\",\n    \"HTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\na;foo=bar\\r\\n0123456789\\r\\n0\\r\\n\\r\\n\",\n\n    \"HTTP/1.1 200 OK\\r\\nContent-Length: 3\\r\\n\\r\\nAAA\",\n    \"HTTP/1.1 201 Created\\r\\nContent-Length: 4\\r\\n\\r\\nBBBB\",\n    \"HTTP/1.1 202 Accepted\\r\\nContent-Length: 5\\r\\n\\r\\nCCCC\",\n\n    \"HTTP/1.1 200 OK\\r\\nHeader1: Value1\\r\\nHeader2: Value2\\r\\nContent-Length: 0\\r\\n\\r\\n\",\n    \"RTSP/1.1 200 OK\\r\\n\\r\\n\",\n    \"ICE/1.1 200 OK\\r\\n\\r\\n\",\n    \"HTTP/1.1 200 OK\\r\\n\\r\\n\",\n    \"HTTP/1.1 301 Moved Permanently\\r\\nLocation: http://www.google.com/\\r\\nContent-Type: text/html; charset=UTF-8\\r\\nDate: Sun, 26 Apr 2009 11:11:49 GMT\\r\\nExpires: Tue, 26 May 2009 11:11:49 GMT\\r\\nX-$PrototypeBI-Version: 1.6.0.3\\r\\nCache-Control: public, max-age=2592000\\r\\nServer: gws\\r\\nContent-Length: 219\\r\\n\\r\\n<HTML><HEAD><meta http-equiv=content-type content=text/html;charset=utf-8>\\n<TITLE>301 Moved</TITLE></HEAD><BODY>\\n<H1>301 Moved</H1>\\nThe document has moved\\n<A HREF=\\\"http://www.google.com/\\\">here</A>.\\r\\n</BODY></HTML>\",\n    \"HTTP/1.1 301 MovedPermanently\\r\\nDate: Wed, 15 May 2013 17:06:33 GMT\\r\\nServer: Server\\r\\nx-amz-id-1: 0GPHKXSJQ826RK7GZEB2\\r\\np3p: policyref=\\\"http://www.amazon.com/w3c/p3p.xml\\\",CP=\\\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \\\"\\r\\nx-amz-id-2: STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD\\r\\nLocation: http://www.amazon.com/Dan-Brown/e/B000AP9DSU/ref=s9_pop_gw_al1?_encoding=UTF8&refinementId=618073011&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=center-2&pf_rd_r=0SHYY5BZXN3KR20BNFAY&pf_rd_t=101&pf_rd_p=1263340922&pf_rd_i=507846\\r\\nVary: Accept-Encoding,User-Agent\\r\\nContent-Type: text/html; charset=ISO-8859-1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n1\\r\\n\\n\\r\\n0\\r\\n\\r\\n\",\n    \"HTTP/1.1 404 Not Found\\r\\n\\r\\n\",\n    \"HTTP/1.1 301\\r\\n\\r\\n\",\n    \"HTTP/1.1 200 \\r\\n\\r\\n\",\n    \"HTTP/1.1 200 OK\\r\\nContent-Type: text/html; charset=utf-8\\r\\nConnection: close\\r\\n\\r\\nthese headers are from http://news.ycombinator.com/\",\n    \"HTTP/1.1 200 OK\\r\\nServer: DCLK-AdSvr\\r\\nContent-Type: text/xml\\r\\nContent-Length: 0\\r\\nDCLK_imp: v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o\\r\\n\\r\\n\",\n    \"HTTP/1.0 301 Moved Permanently\\r\\nDate: Thu, 03 Jun 2010 09:56:32 GMT\\r\\nServer: Apache/2.2.3 (Red Hat)\\r\\nCache-Control: public\\r\\nPragma: \\r\\nLocation: http://www.bonjourmadame.fr/\\r\\nVary: Accept-Encoding\\r\\nContent-Length: 0\\r\\nContent-Type: text/html; charset=UTF-8\\r\\nConnection: keep-alive\\r\\n\\r\\n\",\n    \"HTTP/1.1 200 OK\\r\\nDate: Tue, 28 Sep 2010 01:14:13 GMT\\r\\nServer: Apache\\r\\nCache-Control: no-cache, must-revalidate\\r\\nExpires: Mon, 26 Jul 1997 05:00:00 GMT\\r\\n.et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\\r\\nVary: Accept-Encoding\\r\\n_eep-Alive: timeout=45\\r\\n_onnection: Keep-Alive\\r\\nTransfer-Encoding: chunked\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\n\\r\\n0\\r\\n\\r\\n\",\n    \"HTTP/0.9 200 OK\\r\\n\\r\\n\",\n    \"HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\n\\r\\nhello world\",\n    \"HTTP/1.1 200 OK\\r\\nHeader1: Value1\\r\\nHeader2: Value2\\r\\nContent-Length: 0\\r\\n\\r\\n\",\n\n    \"HTTP/1.1 200 OK\\r\\nAccept: */*\\r\\nTransfer-Encoding: chunked, deflate\\r\\n\\r\\nWorld\",\n    \"HTTP/1.1 200 OK\\r\\nAccept: */*\\r\\nTransfer-Encoding: chunked\\r\\nTransfer-Encoding: identity\\r\\n\\r\\nWorld\",\n    \"HTTP/1.1 200 OK\\r\\nAccept: */*\\r\\nTransfer-Encoding: chunkedchunked\\r\\n\\r\\n2\\r\\nOK\\r\\n0\\r\\n\\r\\n\",\n    \"HTTP/1.1 200 OK\\r\\nHost: localhost\\r\\nTransfer-encoding: chunked\\r\\n\\r\\n5;ilovew3;somuchlove=aretheseparametersfor\\r\\nhello\\r\\n6;blahblah;blah\\r\\n world\\r\\n0\\r\\n\\r\\n\",\n    \"HTTP/1.1 200 OK\\r\\nHost: localhost\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n5;ilovew3=\\\"I love; extensions\\\";somuchlove=\\\"aretheseparametersfor\\\";blah;foo=bar\\r\\nhello\\r\\n6;blahblah;blah\\r\\n world\\r\\n0\\r\\n\",\n};\n\nTEST(http_parser, response_success_case) {\n    llhttp_t *parser = swoole_http_parser_create(HTTP_RESPONSE);\n    ON_SCOPE_EXIT {\n        swoole_http_destroy_context(parser);\n    };\n\n    HttpContext *ctx = nullptr;\n    for (size_t i = 0; i < response_success_case.size(); ++i) {\n        string success_protocol = response_success_case[i];\n        swoole_llhttp_parser_execute(parser, &http_parser_settings, success_protocol.c_str(), success_protocol.length());\n        ASSERT_EQ(llhttp_get_errno(parser), HPE_OK);\n\n        ctx = static_cast<HttpContext *>(parser->data);\n        ASSERT_EQ(ctx->completed, 1);\n        llhttp_reset(parser);\n    }\n}\n// clang-format on\n"
  },
  {
    "path": "core-tests/src/server/http_server.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n\n#include \"httplib_client.h\"\n#include \"llhttp.h\"\n#include \"swoole_server.h\"\n#include \"swoole_file.h\"\n#include \"swoole_http.h\"\n#include \"swoole_http2.h\"\n#include \"swoole_util.h\"\n\n#include <openssl/sha.h>\n#include <openssl/bio.h>\n#include <openssl/evp.h>\n#include <openssl/buffer.h>\n\nusing namespace swoole;\nusing namespace std;\nusing http_server::Context;\nusing network::Client;\nusing network::SyncClient;\nusing swoole::http_server::parse_multipart_boundary;\n\nstruct http_context {\n    unordered_map<string, string> headers;\n    unordered_map<string, string> response_headers;\n    string url;\n    string current_key;\n    Server *server;\n    int fd;\n    bool completed;\n\n    void setHeader(string key, string value) {\n        response_headers[key] = value;\n    }\n\n    void response(enum swHttpStatusCode code, string body) {\n        response_headers[\"Content-Length\"] = to_string(body.length());\n        response(code);\n        server->send(fd, body.c_str(), body.length());\n    }\n\n    void response(int code) {\n        String *buf = make_string(1024);\n        buf->length = sw_snprintf(buf->str, buf->size, \"HTTP/1.1 %s\\r\\n\", http_server::get_status_message(code));\n        for (auto &kv : response_headers) {\n            buf->append(kv.first.c_str(), kv.first.length());\n            buf->append(SW_STRL(\": \"));\n            buf->append(kv.second.c_str(), kv.second.length());\n            buf->append(SW_STRL(\"\\r\\n\"));\n        }\n        buf->append(SW_STRL(\"\\r\\n\"));\n        server->send(fd, buf->str, buf->length);\n        delete buf;\n    }\n\n    void dump_headers() {\n        for (auto kv : headers) {\n            std::cout << kv.first << \": \" << kv.second << \"\\n\";\n        }\n    }\n\n    static std::string base64Encode(const unsigned char *input, int length) {\n        BIO *bmem = nullptr;\n        BIO *b64 = nullptr;\n        BUF_MEM *bptr = nullptr;\n\n        b64 = BIO_new(BIO_f_base64());\n        BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);\n        bmem = BIO_new(BIO_s_mem());\n        b64 = BIO_push(b64, bmem);\n\n        BIO_write(b64, input, length);\n        BIO_flush(b64);\n        BIO_get_mem_ptr(b64, &bptr);\n\n        std::string encoded(bptr->data, bptr->length);\n        BIO_free_all(b64);\n\n        return encoded;\n    }\n\n    std::string createWebSocketAccept() {\n        std::string keyWithMagic = headers[\"Sec-WebSocket-Key\"] + SW_WEBSOCKET_GUID;\n\n        unsigned char sha1Result[SHA_DIGEST_LENGTH];\n        SHA1(reinterpret_cast<const unsigned char *>(keyWithMagic.c_str()), keyWithMagic.length(), sha1Result);\n\n        return base64Encode(sha1Result, SHA_DIGEST_LENGTH);\n    }\n};\n\nstatic int handle_on_message_complete(llhttp_t *parser) {\n    http_context *ctx = reinterpret_cast<http_context *>(parser->data);\n    ctx->completed = true;\n    return 0;\n}\n\nstatic int handle_on_header_field(llhttp_t *parser, const char *at, size_t length) {\n    http_context *ctx = reinterpret_cast<http_context *>(parser->data);\n    ctx->current_key = string(at, length);\n    return 0;\n}\n\nstatic int handle_on_header_value(llhttp_t *parser, const char *at, size_t length) {\n    http_context *ctx = reinterpret_cast<http_context *>(parser->data);\n    ctx->headers[ctx->current_key] = string(at, length);\n    return 0;\n}\n\nstatic int handle_on_url(llhttp_t *parser, const char *at, size_t length) {\n    http_context *ctx = reinterpret_cast<http_context *>(parser->data);\n    ctx->url = std::string(at, length);\n    return 0;\n}\n\nstatic void test_base_server(function<void(Server *)> fn) {\n    thread child_thread;\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n    serv.enable_reuse_port = true;\n    serv.heartbeat_check_interval = 1;\n    serv.private_data_2 = (void *) &fn;\n\n    test::counter_init();\n\n    serv.enable_static_handler = true;\n    ASSERT_TRUE(serv.set_document_root(test::get_root_path()));\n\n    serv.add_static_handler_location(\"/examples\");\n    serv.add_http_compression_type(\"text/html\");\n\n    sw_logger()->set_level(SW_LOG_WARNING);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n    port->open_http_protocol = true;\n    port->open_websocket_protocol = true;\n\n    serv.create();\n\n    serv.onWorkerStart = [&child_thread](Server *serv, Worker *worker) {\n        function<void(Server *)> fn = *(function<void(Server *)> *) serv->private_data_2;\n        child_thread = thread(fn, serv);\n    };\n\n    serv.onClose = [](Server *serv, DataHead *info) -> void {\n        auto session_id = info->fd;\n        auto conn = serv->get_connection_by_session_id(session_id);\n        if (conn->close_actively) {\n            EXPECT_EQ(info->reactor_id, -1);\n        } else {\n            EXPECT_GE(info->reactor_id, 0);\n        }\n    };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        auto session_id = req->info.fd;\n        auto conn = serv->get_connection_by_session_id(session_id);\n\n        test::counter_incr(0);\n\n        if (conn->websocket_status == websocket::STATUS_ACTIVE) {\n            sw_tg_buffer()->clear();\n            uchar flags = 0;\n            uchar opcode = 0;\n            websocket::parse_ext_flags(req->info.ext_flags, &opcode, &flags);\n\n            if (opcode == websocket::OPCODE_PING) {\n                websocket::encode(\n                    sw_tg_buffer(), req->data, req->info.len, websocket::OPCODE_PONG, websocket::FLAG_FIN);\n                serv->send(session_id, sw_tg_buffer()->str, sw_tg_buffer()->length);\n            } else if (opcode == websocket::OPCODE_CLOSE) {\n                // pass\n            } else {\n                std::string resp = \"Swoole: \" + string(req->data, req->info.len);\n                websocket::encode(\n                    sw_tg_buffer(), resp.c_str(), resp.length(), websocket::OPCODE_TEXT, websocket::FLAG_FIN);\n                serv->send(session_id, sw_tg_buffer()->str, sw_tg_buffer()->length);\n            }\n\n            return SW_OK;\n        }\n\n        llhttp_t parser = {};\n        llhttp_settings_t settings = {};\n        llhttp_init(&parser, HTTP_REQUEST, &settings);\n\n        http_context ctx = {};\n        parser.data = &ctx;\n        ctx.server = serv;\n        ctx.fd = session_id;\n\n        settings.on_url = handle_on_url;\n        settings.on_header_field = handle_on_header_field;\n        settings.on_header_value = handle_on_header_value;\n        settings.on_message_complete = handle_on_message_complete;\n\n        enum llhttp_errno err = llhttp_execute(&parser, req->data, req->info.len);\n\n        if (err == HPE_PAUSED_UPGRADE) {\n            ctx.setHeader(\"Connection\", \"Upgrade\");\n            ctx.setHeader(\"Sec-WebSocket-Accept\", ctx.createWebSocketAccept());\n            ctx.setHeader(\"Sec-WebSocket-Version\", \"13\");\n            ctx.setHeader(\"Upgrade\", \"websocket\");\n            ctx.setHeader(\"Content-Length\", \"0\");\n\n            ctx.response(SW_HTTP_SWITCHING_PROTOCOLS);\n\n            conn->websocket_status = websocket::STATUS_ACTIVE;\n\n            if (ctx.url == \"/ws/close\") {\n                swoole_timer_after(200, [serv, session_id](Timer *, TimerNode *) {\n                    sw_tg_buffer()->clear();\n                    websocket::pack_close_frame(\n                        sw_tg_buffer(), websocket::CLOSE_POLICY_ERROR, SW_STRL(\"swoole close\"), 0);\n                    serv->send(session_id, sw_tg_buffer()->str, sw_tg_buffer()->length);\n                });\n            }\n\n            return SW_OK;\n        }\n\n        if (err != HPE_OK) {\n            fprintf(stderr, \"Parse error: %s %s\\n\", llhttp_errno_name(err), parser.reason);\n            return SW_ERR;\n        }\n        EXPECT_EQ(err, HPE_OK);\n\n        if (ctx.url == \"/just/get/file\") {\n            std::string filename = test::get_root_path() + \"/examples/test.jpg\";\n            serv->sendfile(session_id, filename.c_str(), filename.length(), 0, 0);\n        } else {\n            ctx.response(SW_HTTP_OK, \"hello world\");\n        }\n\n        EXPECT_EQ(ctx.headers[\"User-Agent\"], httplib::USER_AGENT);\n\n        return SW_OK;\n    };\n\n    serv.start();\n    child_thread.join();\n}\n\nstatic Server *test_http_server(Server::DispatchMode dispatch_mode = Server::DISPATCH_FDMOD,\n                                bool ssl = false,\n                                int worker_num = 2,\n                                Server::Mode mode = Server::MODE_PROCESS) {\n    auto server = new Server(mode);\n    server->user_ = std::string(\"root\");\n    server->group_ = std::string(\"root\");\n    server->chroot_ = std::string(\"/\");\n    server->worker_num = worker_num;\n    server->dispatch_mode = dispatch_mode;\n    server->open_cpu_affinity = true;\n    sw_logger()->set_level(SW_LOG_WARNING);\n\n    ListenPort *port = ssl ? server->add_port((enum swSocketType)(SW_SOCK_TCP | SW_SOCK_SSL), TEST_HOST, 0)\n                           : server->add_port(SW_SOCK_TCP, TEST_HOST, 0);\n\n    port->open_http_protocol = true;\n    port->open_websocket_protocol = true;\n    port->open_tcp_keepalive = true;\n    port->tcp_fastopen = true;\n    port->tcp_defer_accept = true;\n\n    server->enable_static_handler = true;\n    server->set_document_root(test::get_root_path());\n    server->add_static_handler_location(\"/examples\");\n\n    server->create();\n\n    server->onStart = [](Server *serv) {\n        // printf(\"onStart\\n\");\n    };\n\n    server->onClose = [](Server *serv, DataHead *info) -> void {\n        auto session_id = info->fd;\n        auto conn = serv->get_connection_by_session_id(session_id);\n        if (conn->close_actively) {\n            ASSERT_EQ(info->reactor_id, -1);\n        } else {\n            ASSERT_GE(info->reactor_id, 0);\n        }\n        // printf(\"onClose\\n\");\n    };\n\n    server->onConnect = [](Server *serv, DataHead *info) -> void {\n        // printf(\"onConnect\\n\");\n    };\n\n    server->onReceive = [&](Server *serv, RecvData *req) -> int {\n        auto session_id = req->info.fd;\n        auto conn = serv->get_connection_by_session_id(session_id);\n\n        EXPECT_LE(serv->get_idle_worker_num(), serv->worker_num);\n        EXPECT_TRUE(serv->is_healthy_connection(microtime(), conn));\n\n        llhttp_t parser = {};\n        llhttp_settings_t settings = {};\n        llhttp_init(&parser, HTTP_REQUEST, &settings);\n\n        http_context ctx = {};\n        parser.data = &ctx;\n        ctx.server = serv;\n        ctx.fd = session_id;\n\n        settings.on_url = handle_on_url;\n        settings.on_header_field = handle_on_header_field;\n        settings.on_header_value = handle_on_header_value;\n        settings.on_message_complete = handle_on_message_complete;\n\n        llhttp_errno err = llhttp_execute(&parser, req->data, req->info.len);\n        if (err != HPE_OK) {\n            fprintf(stderr, \"Parse error: %s %s\\n\", llhttp_errno_name(err), parser.reason);\n            return SW_ERR;\n        }\n\n        if (ctx.url == \"/overflow\") {\n            conn->overflow = 1;\n        }\n\n        if (ctx.url == \"/pause\") {\n            serv->feedback(conn, SW_SERVER_EVENT_PAUSE_RECV);\n        }\n\n        EXPECT_EQ(err, HPE_OK);\n        ctx.response(SW_HTTP_OK, \"hello world\");\n\n        return SW_OK;\n    };\n\n    return server;\n}\n\nstatic Server *test_proxy_server() {\n    Server *server = new Server(Server::MODE_BASE);\n    server->worker_num = 1;\n\n    ListenPort *port = server->add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    port->kernel_socket_send_buffer_size = INT_MAX;\n    port->kernel_socket_recv_buffer_size = INT_MAX;\n    port->open_tcp_nodelay = true;\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    server->enable_static_handler = true;\n    server->set_document_root(test::get_root_path());\n    server->add_static_handler_location(\"/examples\");\n\n    server->get_primary_port()->set_package_max_length(64 * 1024);\n    port->open_http_protocol = 1;\n    port->open_websocket_protocol = 1;\n\n    server->create();\n\n    server->onReceive = [&](Server *server, RecvData *req) -> int {\n        auto session_id = req->info.fd;\n\n        swoole_set_worker_id(server->worker_num);\n\n        llhttp_t parser = {};\n        llhttp_settings_t settings = {};\n        llhttp_init(&parser, HTTP_REQUEST, &settings);\n\n        http_context ctx = {};\n        parser.data = &ctx;\n        ctx.server = server;\n        ctx.fd = session_id;\n\n        settings.on_url = handle_on_url;\n        settings.on_header_field = handle_on_header_field;\n        settings.on_header_value = handle_on_header_value;\n        settings.on_message_complete = handle_on_message_complete;\n\n        enum llhttp_errno err = llhttp_execute(&parser, req->data, req->info.len);\n\n        if (err != HPE_OK) {\n            fprintf(stderr, \"Parse error: %s %s\\n\", llhttp_errno_name(err), parser.reason);\n            return SW_ERR;\n        }\n\n        if (ctx.url == \"/just/get/file\") {\n            std::string filename = test::get_root_path() + \"/examples/test.jpg\";\n            server->sendfile(session_id, filename.c_str(), filename.length(), 0, 0);\n        } else {\n            ctx.response(SW_HTTP_OK, \"hello world\");\n        }\n\n        EXPECT_EQ(err, HPE_OK);\n        EXPECT_EQ(ctx.headers[\"User-Agent\"], httplib::USER_AGENT);\n        return SW_OK;\n    };\n\n    return server;\n}\n\nTEST(http_server, get) {\n    test_base_server([](Server *serv) {\n        swoole_signal_block_all();\n\n        auto port = serv->get_primary_port();\n\n        httplib::Client cli(TEST_HOST, port->port);\n        auto resp = cli.Get(\"/index.html\");\n        EXPECT_EQ(resp->status, 200);\n        EXPECT_EQ(resp->body, string(\"hello world\"));\n\n        kill(getpid(), SIGTERM);\n    });\n}\n\nTEST(http_server, reset_connection) {\n    test_base_server([](Server *serv) {\n        swoole_signal_block_all();\n\n        const auto port = serv->get_primary_port();\n        Client c(SW_SOCK_TCP, false);\n        EXPECT_EQ(c.connect(TEST_HOST, port->port), 0);\n        c.send(SW_STRL(\"GET /index.html HTTP/1.1\"));\n        usleep(10000);\n        c.close();\n\n        kill(getpid(), SIGTERM);\n    });\n\n    ASSERT_EQ(test::counter_get(0), 0);\n}\n\nTEST(http_server, heartbeat_check_interval) {\n    test_base_server([](Server *serv) {\n        swoole_signal_block_all();\n\n        auto port = serv->get_primary_port();\n\n        httplib::Client cli(TEST_HOST, port->port);\n        cli.set_keep_alive(true);\n        auto resp = cli.Get(\"/index.html\");\n        EXPECT_EQ(resp->status, 200);\n        EXPECT_EQ(resp->body, string(\"hello world\"));\n        sleep(3);\n\n        kill(getpid(), SIGTERM);\n    });\n}\n\nTEST(http_server, idle_time) {\n    test_base_server([](Server *serv) {\n        swoole_signal_block_all();\n        auto port = serv->get_primary_port();\n        port->max_idle_time = 1;\n\n        httplib::Client cli(TEST_HOST, port->port);\n        cli.set_keep_alive(true);\n        auto resp = cli.Get(\"/index.html\");\n        EXPECT_EQ(resp->status, 200);\n\n        sleep(2);\n        kill(getpid(), SIGTERM);\n    });\n}\n\nTEST(http_server, post) {\n    test_base_server([](Server *serv) {\n        swoole_signal_block_all();\n\n        auto port = serv->get_primary_port();\n\n        httplib::Client cli(TEST_HOST, port->port);\n        httplib::Params params;\n        params.emplace(\"name\", \"john\");\n        params.emplace(\"note\", \"coder\");\n        auto resp = cli.Post(\"/index.html\", params);\n        EXPECT_EQ(resp->status, 200);\n        EXPECT_EQ(resp->body, string(\"hello world\"));\n\n        kill(getpid(), SIGTERM);\n    });\n}\n\nTEST(http_server, static_get) {\n    test_base_server([](Server *serv) {\n        swoole_signal_block_all();\n\n        auto port = serv->get_primary_port();\n\n        httplib::Client cli(TEST_HOST, port->port);\n        auto resp = cli.Get(\"/examples/test.jpg\");\n        EXPECT_EQ(resp->status, 200);\n\n        string file = test::get_root_path() + \"/examples/test.jpg\";\n        File fp(file, O_RDONLY);\n        EXPECT_TRUE(fp.ready());\n\n        auto str = fp.read_content();\n\n        EXPECT_EQ(resp->body, str->to_std_string());\n\n        resp = cli.Get(\"/just/get/file\");\n        EXPECT_EQ(resp, nullptr);\n        kill(getpid(), SIGTERM);\n    });\n}\n\nTEST(http_server, static_files) {\n    test_base_server([](Server *serv) {\n        serv->http_autoindex = true;\n        serv->add_static_handler_location(\"\");\n\n        swoole_signal_block_all();\n        auto port = serv->get_primary_port();\n        httplib::Client cli(TEST_HOST, port->port);\n\n        auto resp = cli.Get(\"/\");\n        EXPECT_EQ(resp->status, 200);\n        std::string::size_type postion = resp->body.find(\"Index of\");\n        EXPECT_TRUE(postion != std::string::npos);\n\n        // directory not exists\n        resp = cli.Get(\"/test/../\");\n        EXPECT_EQ(resp->status, 404);\n\n        // must be document_root\n        resp = cli.Get(\"//tests/../\");\n        EXPECT_EQ(resp->status, 404);\n\n        resp = cli.Get(\"/tests/../README.md\");\n        EXPECT_EQ(resp->status, 200);\n\n        // file not exists\n        resp = cli.Get(\"/not-exists.jpg\");\n        EXPECT_EQ(resp->status, 404);\n\n        // try again\n        serv->add_static_handler_index_files(\"README.md\");\n        resp = cli.Get(\"/\");\n        postion = resp->body.find(\"<h2 align=center>\");\n        EXPECT_TRUE(postion != std::string::npos);\n\n        kill(getpid(), SIGTERM);\n    });\n}\n\nstatic void request_with_header(const char *date_format, httplib::Client *cli) {\n    char temp[128] = {0};\n    time_t raw_time = time(NULL) + 7 * 24 * 60 * 60;\n    tm *time_info = gmtime(&raw_time);\n\n    strftime(temp, sizeof(temp), date_format, time_info);\n    httplib::Headers headers = {{\"If-Modified-Since\", temp}};\n    auto resp = cli->Get(\"/\", headers);\n    EXPECT_EQ(resp, nullptr);\n}\n\nTEST(http_server, not_modify) {\n    test_base_server([](Server *serv) {\n        serv->http_autoindex = true;\n        serv->add_static_handler_location(\"\");\n\n        swoole_signal_block_all();\n        auto port = serv->get_primary_port();\n        httplib::Client cli(TEST_HOST, port->port);\n\n        serv->add_static_handler_index_files(\"swoole-logo.svg\");\n        auto resp = cli.Get(\"/\");\n        EXPECT_EQ(resp->status, 200);\n\n        // 304 not modified\n        cli.set_read_timeout(0, 100);\n        request_with_header(SW_HTTP_RFC1123_DATE_GMT, &cli);\n        request_with_header(SW_HTTP_RFC1123_DATE_UTC, &cli);\n        request_with_header(SW_HTTP_RFC850_DATE, &cli);\n        request_with_header(SW_HTTP_ASCTIME_DATE, &cli);\n        kill(getpid(), SIGTERM);\n    });\n}\n\nTEST(http_server, proxy_file) {\n    Server *server = test_proxy_server();\n    pid_t pid = fork();\n\n    if (pid == 0) {\n        server->start();\n        exit(0);\n    }\n\n    if (pid > 0) {\n        ON_SCOPE_EXIT {\n            kill(server->get_master_pid(), SIGTERM);\n        };\n\n        sleep(1);\n        auto port = server->get_primary_port();\n        httplib::Client cli(TEST_HOST, port->port);\n\n        auto resp = cli.Get(\"/just/get/file\");\n        ASSERT_EQ(resp, nullptr);\n    }\n}\n\n// need fix\nTEST(http_server, proxy_response) {\n    Server *server = test_proxy_server();\n    pid_t pid = fork();\n\n    if (pid == 0) {\n        server->start();\n        exit(0);\n    }\n\n    if (pid > 0) {\n        ON_SCOPE_EXIT {\n            kill(server->get_master_pid(), SIGTERM);\n        };\n        sleep(1);\n        auto port = server->get_primary_port();\n        httplib::Client cli(TEST_HOST, port->port);\n        auto resp = cli.Get(\"/\");\n        ASSERT_EQ(resp, nullptr);\n        //        ASSERT_EQ(resp->body, string(\"hello world\"));\n    }\n}\n\nstatic void websocket_test(int server_port, const char *data, size_t length, bool mask = false) {\n    httplib::Client cli(TEST_HOST, server_port);\n\n    if (mask) {\n        cli.set_websocket_mask(true);\n    }\n\n    httplib::Headers headers;\n    EXPECT_TRUE(cli.Upgrade(\"/websocket\", headers));\n    EXPECT_TRUE(cli.Push(data, length));\n\n    auto msg = cli.Recv();\n    ASSERT_NE(msg.get(), nullptr);\n    EXPECT_EQ(string(msg->payload, msg->payload_length), string(\"Swoole: \") + string(data, length));\n}\n\nTEST(http_server, websocket_small) {\n    test_base_server([](Server *serv) {\n        swoole_signal_block_all();\n        websocket_test(serv->get_primary_port()->get_port(), SW_STRL(\"hello world, swoole is best!\"));\n        kill(getpid(), SIGTERM);\n    });\n}\n\nTEST(http_server, websocket_medium) {\n    test_base_server([](Server *serv) {\n        swoole_signal_block_all();\n\n        String str(8192);\n        str.repeat(\"A\", 1, 8192);\n        websocket_test(serv->get_primary_port()->get_port(), str.value(), str.get_length());\n\n        kill(getpid(), SIGTERM);\n    });\n}\n\nTEST(http_server, websocket_big) {\n    test_base_server([](Server *serv) {\n        swoole_signal_block_all();\n\n        String str(128 * 1024);\n        str.repeat(\"A\", 1, str.capacity() - 1);\n        websocket_test(serv->get_primary_port()->get_port(), str.value(), str.get_length());\n\n        kill(getpid(), SIGTERM);\n    });\n}\n\nTEST(http_server, websocket_mask) {\n    test_base_server([](Server *serv) {\n        swoole_signal_block_all();\n\n        String str(64 * 128);\n        str.append_random_bytes(str.capacity(), true);\n\n        websocket_test(serv->get_primary_port()->get_port(), str.value(), str.get_length(), true);\n\n        kill(getpid(), SIGTERM);\n    });\n}\n\nstatic auto packet = \"hello world\\n\";\n\nTEST(http_server, websocket_encode) {\n    auto buffer = sw_tg_buffer();\n    buffer->clear();\n\n    auto log_file = TEST_LOG_FILE;\n\n    ASSERT_TRUE(websocket::encode(\n        buffer, packet, strlen(packet), websocket::OPCODE_TEXT, websocket::FLAG_FIN | websocket::FLAG_MASK));\n    websocket::Frame ws;\n\n    ASSERT_TRUE(websocket::decode(&ws, buffer->str, buffer->length));\n\n    FILE *fp = fopen(log_file, \"a+\");\n    ASSERT_NE(fp, nullptr);\n    auto ori_fp = swoole_get_stdout_stream();\n    swoole_set_stdout_stream(fp);\n    websocket::print_frame(&ws);\n    fclose(fp);\n    swoole_set_stdout_stream(ori_fp);\n\n    File f(log_file, File::READ);\n    auto rs = f.read_content();\n\n    ASSERT_TRUE(rs->contains(\"FIN: 1,\"));\n    ASSERT_TRUE(rs->contains(\"RSV1: 0,\"));\n    ASSERT_TRUE(rs->contains(\"opcode: 1,\"));\n    ASSERT_TRUE(rs->contains(\"payload: hello world\\n\"));\n\n    f.close();\n\n    unlink(log_file);\n}\n\nTEST(http_server, node_websocket_client_1) {\n    unlink(TEST_LOG_FILE);\n\n    test_base_server([](Server *serv) {\n        swoole_signal_block_all();\n\n        EXPECT_EQ(test::exec_js_script(\"ws_1.js\", std::to_string(serv->get_primary_port()->get_port())), 0);\n\n        kill(serv->get_master_pid(), SIGTERM);\n    });\n\n    File fp(TEST_LOG_FILE, O_RDONLY);\n    EXPECT_TRUE(fp.ready());\n    auto str = fp.read_content();\n    ASSERT_TRUE(str->contains(\"received: Swoole: hello world\"));\n    ASSERT_TRUE(str->contains(\"the node websocket client is closed\"));\n\n    fp.close();\n    unlink(TEST_LOG_FILE);\n}\n\nTEST(http_server, node_websocket_client_2) {\n    unlink(TEST_LOG_FILE);\n\n    test_base_server([](Server *serv) {\n        swoole_signal_block_all();\n\n        EXPECT_EQ(test::exec_js_script(\"ws_2.js\", std::to_string(serv->get_primary_port()->get_port())), 0);\n\n        kill(serv->get_master_pid(), SIGTERM);\n    });\n\n    File fp(TEST_LOG_FILE, O_RDONLY);\n    EXPECT_TRUE(fp.ready());\n    auto str = fp.read_content();\n    ASSERT_TRUE(str->contains(\"the node websocket client is closed, code: 1008, reason: swoole close\"));\n\n    fp.close();\n    unlink(TEST_LOG_FILE);\n}\n\nTEST(http_server, parser1) {\n    std::thread t;\n    string file = test::get_root_path() + \"/core-tests/fuzz/cases/req1.bin\";\n    auto server = http_server::listen(\":0\", [](Context &ctx) {\n        EXPECT_EQ(ctx.form_data.size(), 3);\n        ctx.end(\"DONE\");\n    });\n    server->worker_num = 1;\n    server->onWorkerStart = [&t, &file](Server *server, Worker *worker) {\n        t = std::thread([server, &file]() {\n            swoole_signal_block_all();\n            File fp(file, O_RDONLY);\n            EXPECT_TRUE(fp.ready());\n            auto str = fp.read_content();\n            SyncClient c(SW_SOCK_TCP);\n            c.connect(TEST_HOST, server->get_primary_port()->port);\n            c.send(str->value(), str->get_length());\n            char buf[1024];\n            auto n = c.recv(buf, sizeof(buf));\n            c.close();\n            std::string resp(buf, n);\n\n            EXPECT_TRUE(resp.find(\"200 OK\") != resp.npos);\n\n            kill(server->get_master_pid(), SIGTERM);\n        });\n    };\n    server->start();\n    t.join();\n}\n\nTEST(http_server, parser2) {\n    std::thread t;\n    auto server = http_server::listen(\":0\", [](Context &ctx) {\n        EXPECT_EQ(ctx.form_data.size(), 3);\n        ctx.end(\"DONE\");\n    });\n    server->worker_num = 1;\n    server->get_primary_port()->set_package_max_length(64 * 1024);\n    server->upload_max_filesize = 1024 * 1024;\n    server->onWorkerStart = [&t](Server *server, Worker *worker) {\n        t = std::thread([server]() {\n            swoole_signal_block_all();\n            string file = test::get_root_path() + \"/core-tests/fuzz/cases/req2.bin\";\n            File fp(file, O_RDONLY);\n            EXPECT_TRUE(fp.ready());\n            auto str = fp.read_content();\n            SyncClient c(SW_SOCK_TCP);\n            c.connect(TEST_HOST, server->get_primary_port()->port);\n            c.send(str->value(), str->get_length());\n            char buf[1024];\n            auto n = c.recv(buf, sizeof(buf));\n            c.close();\n            std::string resp(buf, n);\n\n            EXPECT_TRUE(resp.find(\"200 OK\") != resp.npos);\n\n            kill(server->get_master_pid(), SIGTERM);\n        });\n    };\n    server->start();\n    t.join();\n}\n\nTEST(http_server, upload) {\n    std::thread t;\n    auto server = http_server::listen(\":0\", [](Context &ctx) {\n        EXPECT_EQ(ctx.files.size(), 1);\n        ctx.setStatusCode(200);\n        ctx.setHeader(\"Connection\", \"close\");\n        ASSERT_EQ(ctx.request_path, \"/upload\");\n        ASSERT_EQ(ctx.query_string, \"test=curl\");\n        ctx.end(TEST_STR);\n    });\n    server->worker_num = 1;\n    server->get_primary_port()->set_package_max_length(2 * 1024 * 1024);\n    server->onWorkerStart = [&t](Server *server, Worker *worker) {\n        t = std::thread([server]() {\n            swoole_signal_block_all();\n            string jpg_path = test::get_jpg_file();\n            string str_1 = \"curl -s -S -H 'Transfer-Encoding: chunked' -F \\\"file=@\" + jpg_path + \"\\\" http://\";\n            string command =\n                str_1 + TEST_HOST + \":\" + to_string(server->get_primary_port()->port) + \"/upload?test=curl\";\n\n            pid_t pid2;\n            int pipe = swoole_shell_exec(command.c_str(), &pid2, 0);\n            usleep(200000);\n            char buf[1024] = {};\n            read(pipe, buf, sizeof(buf) - 1);\n            ASSERT_STREQ(buf, TEST_STR);\n\n            kill(server->get_master_pid(), SIGTERM);\n        });\n    };\n    server->start();\n    t.join();\n}\n\nTEST(http_server, max_request_size) {\n    std::thread t;\n    auto server = http_server::listen(\":0\", [](Context &ctx) {\n        EXPECT_EQ(ctx.files.size(), 1);\n        ctx.setStatusCode(200);\n        ctx.setHeader(\"Connection\", \"close\");\n        ASSERT_EQ(ctx.request_path, \"/upload\");\n        ASSERT_EQ(ctx.query_string, \"test=curl\");\n        ctx.end(TEST_STR);\n    });\n    server->worker_num = 1;\n    server->get_primary_port()->set_package_max_length(128 * 1024);\n    server->onWorkerStart = [&t](Server *server, Worker *worker) {\n        t = std::thread([server]() {\n            swoole_signal_block_all();\n            string jpg_path = test::get_jpg_file();\n            string str_1 = \"curl -i -s -S -F \\\"file=@\" + jpg_path + \"\\\" http://\";\n            string command =\n                str_1 + TEST_HOST + \":\" + to_string(server->get_primary_port()->port) + \"/upload?test=curl\";\n\n            pid_t pid2;\n            int pipe = swoole_shell_exec(command.c_str(), &pid2, 0);\n            usleep(200000);\n            char buf[1024] = {};\n            read(pipe, buf, sizeof(buf) - 1);\n            ASSERT_TRUE(strstr(buf, \"413 Request Entity Too Large\") != nullptr);\n\n            kill(server->get_master_pid(), SIGTERM);\n        });\n    };\n    server->start();\n    t.join();\n}\n\nTEST(http_server, heartbeat) {\n    Server *server = test_http_server();\n    server->heartbeat_check_interval = 0;\n    auto port = server->get_primary_port();\n    port->set_package_max_length(1024);\n    port->heartbeat_idle_time = 2;\n\n    pid_t pid = fork();\n\n    if (pid == 0) {\n        server->start();\n        exit(0);\n    }\n\n    if (pid > 0) {\n        ON_SCOPE_EXIT {\n            kill(server->get_master_pid(), SIGTERM);\n        };\n\n        sleep(1);\n        port = server->get_primary_port();\n        httplib::Client cli(TEST_HOST, port->port);\n        cli.set_keep_alive(true);\n        auto resp = cli.Get(\"/\");\n        ASSERT_EQ(resp->status, 200);\n        ASSERT_EQ(resp->body, string(\"hello world\"));\n        sleep(10);\n        resp = cli.Get(\"/\");\n        ASSERT_EQ(resp, nullptr);\n    }\n}\n\nTEST(http_server, overflow) {\n    Server *server = test_http_server();\n    auto port = server->get_primary_port();\n\n    pid_t pid = fork();\n\n    if (pid == 0) {\n        server->start();\n        exit(0);\n    }\n\n    if (pid > 0) {\n        ON_SCOPE_EXIT {\n            kill(server->get_master_pid(), SIGTERM);\n        };\n\n        sleep(1);\n        port = server->get_primary_port();\n        httplib::Client cli(TEST_HOST, port->port);\n        cli.set_keep_alive(true);\n        auto resp = cli.Get(\"/\");\n        ASSERT_EQ(resp->status, 200);\n        ASSERT_EQ(resp->body, string(\"hello world\"));\n        resp = cli.Get(\"/overflow\");\n        ASSERT_EQ(resp, nullptr);\n    }\n}\n\nTEST(http_server, process) {\n    Server *server = test_http_server();\n    pid_t pid = fork();\n\n    if (pid == 0) {\n        server->start();\n        exit(0);\n    }\n\n    if (pid > 0) {\n        ON_SCOPE_EXIT {\n            kill(server->get_master_pid(), SIGTERM);\n        };\n\n        sleep(1);\n        auto port = server->get_primary_port();\n        httplib::Client cli(TEST_HOST, port->port);\n        cli.set_keep_alive(true);\n        auto resp = cli.Get(\"/\");\n        ASSERT_EQ(resp->status, 200);\n        ASSERT_EQ(resp->body, string(\"hello world\"));\n\n        resp = cli.Get(\"/\");\n        ASSERT_EQ(resp->status, 200);\n        ASSERT_EQ(resp->body, string(\"hello world\"));\n    }\n}\n\nTEST(http_server, process1) {\n    Server *server = test_http_server();\n    pid_t pid = fork();\n\n    if (pid == 0) {\n        server->start();\n        exit(0);\n    }\n\n    if (pid > 0) {\n        ON_SCOPE_EXIT {\n            kill(server->get_master_pid(), SIGTERM);\n        };\n        sleep(1);\n        auto port = server->get_primary_port();\n        httplib::Client cli(TEST_HOST, port->port);\n        cli.set_keep_alive(true);\n        auto resp = cli.Get(\"/index.html\");\n        ASSERT_EQ(resp->status, 200);\n        ASSERT_EQ(resp->body, string(\"hello world\"));\n\n        sleep(1);\n        resp = cli.Get(\"/examples/test.jpg\");\n        ASSERT_EQ(resp->status, 200);\n    }\n}\n\nTEST(http_server, redundant_callback) {\n    Server *server = test_http_server(Server::DISPATCH_IDLE_WORKER);\n    server->onConnect = [](Server *serv, DataHead *info) -> int { return 0; };\n    server->onClose = [](Server *serv, DataHead *info) -> int { return 0; };\n    server->onBufferFull = [](Server *serv, DataHead *info) -> int { return 0; };\n    server->onBufferEmpty = [](Server *serv, DataHead *info) -> int { return 0; };\n\n    pid_t pid = fork();\n\n    if (pid == 0) {\n        server->start();\n        ASSERT_EQ(server->onConnect, nullptr);\n        ASSERT_EQ(server->onClose, nullptr);\n        ASSERT_EQ(server->onBufferFull, nullptr);\n        ASSERT_EQ(server->onBufferEmpty, nullptr);\n        exit(0);\n    }\n\n    if (pid > 0) {\n        sleep(2);\n        kill(server->get_master_pid(), SIGTERM);\n    }\n}\n\nTEST(http_server, pause) {\n    Server *server = test_http_server();\n    pid_t pid = fork();\n\n    if (pid == 0) {\n        server->start();\n        exit(0);\n    }\n\n    if (pid > 0) {\n        ON_SCOPE_EXIT {\n            kill(server->get_master_pid(), SIGTERM);\n        };\n\n        sleep(1);\n        auto port = server->get_primary_port();\n        httplib::Client cli(TEST_HOST, port->port);\n        cli.set_keep_alive(true);\n        auto resp = cli.Get(\"/pause\");\n        ASSERT_NE(resp, nullptr);\n        ASSERT_EQ(resp->status, 200);\n        ASSERT_EQ(resp->body, string(\"hello world\"));\n\n        resp = cli.Get(\"/\");\n        ASSERT_EQ(resp, nullptr);\n    }\n}\n\nTEST(http_server, sni) {\n    Server *server = test_http_server(Server::DISPATCH_FDMOD, true);\n    ListenPort *port = server->get_primary_port();\n    port->set_ssl_cert_file(test::get_ssl_dir() + \"/server.crt\");\n    port->set_ssl_key_file(test::get_ssl_dir() + \"/server.key\");\n    auto sni_ssl_ctx = port->dup_ssl_context();\n    sni_ssl_ctx->set_cert_file(test::get_ssl_dir() + \"/sni_server_cs_cert.pem\");\n    sni_ssl_ctx->set_key_file(test::get_ssl_dir() + \"/sni_server_cs_key.pem\");\n    port->ssl_add_sni_cert(\"localhost\", sni_ssl_ctx);\n    port->set_ssl_protocols(0);\n    port->ssl_init();\n\n    pid_t pid = fork();\n\n    if (pid == 0) {\n        server->start();\n        exit(0);\n    }\n\n    if (pid > 0) {\n        ON_SCOPE_EXIT {\n            kill(server->get_master_pid(), SIGTERM);\n        };\n\n        string port_num = to_string(server->get_primary_port()->get_port());\n\n        sleep(1);\n        pid_t pid2;\n        string command = \"curl https://localhost:\" + port_num + \" -k -vvv --stderr /tmp/wwwsnitestcom.txt\";\n        swoole_shell_exec(command.c_str(), &pid2, 0);\n        sleep(1);\n\n        stringstream buffer;\n        ifstream wwwsnitestcom;\n        wwwsnitestcom.open(\"/tmp/wwwsnitestcom.txt\");\n        ASSERT_TRUE(wwwsnitestcom.is_open());\n        buffer << wwwsnitestcom.rdbuf();\n        wwwsnitestcom.close();\n        string response(buffer.str());\n        ASSERT_TRUE(response.find(\"CN=cs.php.net\") != string::npos);\n\n        string command2 = \"curl https://127.0.0.1:\" + port_num + \" -k -vvv --stderr /tmp/wwwsnitest2com.txt\";\n        swoole_shell_exec(command2.c_str(), &pid2, 0);\n        sleep(1);\n\n        stringstream buffer2;\n        ifstream wwwsnitest2com;\n        wwwsnitest2com.open(\"/tmp/wwwsnitest2com.txt\");\n        ASSERT_TRUE(wwwsnitest2com.is_open());\n        buffer2 << wwwsnitest2com.rdbuf();\n        string response2(buffer2.str());\n        wwwsnitest2com.close();\n        ASSERT_TRUE(response2.find(\"CN=127.0.0.1\") != string::npos);\n    }\n}\n\nTEST(http_server, bad_request) {\n    Server *server = test_http_server();\n\n    pid_t pid = fork();\n\n    if (pid == 0) {\n        server->start();\n        exit(0);\n    }\n\n    if (pid > 0) {\n        ON_SCOPE_EXIT {\n            kill(server->get_master_pid(), SIGTERM);\n        };\n        sleep(1);\n\n        string str_1 = \"curl -X UNKNOWN http://\";\n        string str_2 = \":\";\n        string str_3 = \" -k -vvv --stderr /tmp/bad_request.txt\";\n        string host = TEST_HOST;\n        string port = to_string(server->get_primary_port()->port);\n        string command = str_1 + host + str_2 + port + str_3;\n\n        pid_t pid2;\n        swoole_shell_exec(command.c_str(), &pid2, 0);\n        sleep(1);\n\n        stringstream buffer;\n        ifstream bad_request;\n        bad_request.open(\"/tmp/bad_request.txt\");\n        ASSERT_TRUE(bad_request.is_open());\n        buffer << bad_request.rdbuf();\n        string response(buffer.str());\n        bad_request.close();\n        ASSERT_TRUE(response.find(\"400 Bad Request\") != string::npos);\n    }\n}\n\nTEST(http_server, chunked) {\n    Server *server = test_http_server(Server::DISPATCH_FDMOD, false, 1, Server::MODE_BASE);\n\n    std::thread t;\n    server->onStart = [&](Server *_server) {\n        t = std::thread([server]() {\n            swoole_signal_block_all();\n            usleep(300000);\n\n            string jpg_path = test::get_jpg_file();\n            string str_1 = \"curl -s -S -H 'Transfer-Encoding: chunked' -F \\\"file=@\" + jpg_path + \"\\\" http://\";\n            string str_2 = \":\";\n            string host = TEST_HOST;\n            string port = to_string(server->get_primary_port()->port);\n            string command = str_1 + host + str_2 + port;\n\n            pid_t pid2;\n            int pipe = swoole_shell_exec(command.c_str(), &pid2, 0);\n            sleep(1);\n\n            char buf[1024] = {};\n            read(pipe, buf, sizeof(buf) - 1);\n            ASSERT_STREQ(buf, \"hello world\");\n            kill(server->get_master_pid(), SIGTERM);\n        });\n    };\n\n    server->start();\n    t.join();\n    delete server;\n}\n\nTEST(http_server, max_queued_bytes) {\n    Server *server = test_http_server();\n    server->max_queued_bytes = 100;\n\n    pid_t pid = fork();\n\n    if (pid == 0) {\n        server->start();\n        exit(0);\n    }\n\n    if (pid > 0) {\n        ON_SCOPE_EXIT {\n            kill(server->get_master_pid(), SIGTERM);\n        };\n\n        sleep(1);\n\n        string jpg_path = test::get_jpg_file();\n        string str_1 = \"curl -s -S -H 'Transfer-Encoding: chunked' -F \\\"file=@\" + jpg_path + \"\\\" http://\";\n        string command = str_1 + TEST_HOST + \":\" + to_string(server->get_primary_port()->port);\n\n        pid_t pid2;\n        int pipe = swoole_shell_exec(command.c_str(), &pid2, 0);\n        sleep(1);\n\n        char buf[1024] = {};\n        read(pipe, buf, sizeof(buf) - 1);\n        ASSERT_STREQ(buf, \"hello world\");\n    }\n\n    test::wait_all_child_processes();\n}\n\nTEST(http_server, dispatch_func_return_error_worker_id) {\n    Server *server = test_http_server();\n    server->dispatch_func = [](Server *serv, Connection *conn, SendData *data) -> int {\n        return data->info.fd % 2 == 0 ? Server::DISPATCH_RESULT_DISCARD_PACKET\n                                      : Server::DISPATCH_RESULT_CLOSE_CONNECTION;\n    };\n    pid_t pid = fork();\n\n    if (pid == 0) {\n        server->start();\n        exit(0);\n    };\n\n    if (pid > 0) {\n        ON_SCOPE_EXIT {\n            kill(server->get_master_pid(), SIGTERM);\n        };\n        sleep(1);\n        auto port = server->get_primary_port();\n        httplib::Client cli(TEST_HOST, port->port);\n        cli.set_read_timeout(1, 0);\n        auto resp = cli.Get(\"/\");\n        ASSERT_EQ(resp, nullptr);\n        resp = cli.Get(\"/\");\n        ASSERT_EQ(resp, nullptr);\n    }\n}\n\nTEST(http_server, client_ca) {\n    Server *server = test_http_server(Server::DISPATCH_FDMOD, true);\n    ListenPort *port = server->get_primary_port();\n    port->set_ssl_cert_file(test::get_ssl_dir() + \"/server.crt\");\n    port->set_ssl_key_file(test::get_ssl_dir() + \"/server.key\");\n    port->set_ssl_verify_peer(true);\n    port->set_ssl_allow_self_signed(true);\n    port->set_ssl_cafile(test::get_ssl_dir() + \"/ca.crt\");\n    port->ssl_init();\n\n    pid_t pid = fork();\n\n    if (pid == 0) {\n        server->start();\n        exit(0);\n    }\n\n    if (pid > 0) {\n        string port_num = to_string(server->get_primary_port()->port);\n\n        sleep(1);\n        pid_t pid2;\n        string client_cert = \" --cert \" + test::get_ssl_dir() + \"/client.crt\";\n        string client_key = \" --key \" + test::get_ssl_dir() + \"/client.key\";\n        string command = \"curl https://127.0.0.1:\" + port_num + \" \" + client_cert + client_key +\n                         \" -k -vvv --stderr /tmp/client_ca.txt\";\n        swoole_shell_exec(command.c_str(), &pid2, 0);\n        sleep(1);\n\n        stringstream buffer;\n        ifstream client_ca;\n        client_ca.open(\"/tmp/client_ca.txt\");\n        ASSERT_TRUE(client_ca.is_open());\n        buffer << client_ca.rdbuf();\n        client_ca.close();\n        string response(buffer.str());\n        ASSERT_TRUE(response.find(\"200 OK\") != response.npos);\n\n        kill(server->get_master_pid(), SIGTERM);\n    }\n}\n\nstatic bool request_with_if_range_header(const char *date_format, std::string port) {\n    struct stat file_stat;\n    std::string file_path = test::get_root_path() + \"/docs/swoole-logo.svg\";\n    stat(file_path.c_str(), &file_stat);\n    time_t file_mtime = file_stat.st_mtim.tv_sec;\n    struct tm *time_info = gmtime(&file_mtime);\n\n    char temp[128] = {0};\n    strftime(temp, sizeof(temp), date_format, time_info);\n\n    string str_1 = \"curl http://\";\n    string host = TEST_HOST;\n    string str_2 = \":\";\n    string str_3 = \"/docs/swoole-logo.svg -k -vvv --stderr /tmp/http_range.txt \";\n    string headers = \"-H 'Range: bytes=0-500' -H 'If-Range: \";\n    string command = str_1 + host + str_2 + port + str_3 + headers + string(temp) + \"'\";\n\n    pid_t pid;\n    close(swoole_shell_exec(command.c_str(), &pid, 0));\n    sleep(2);\n\n    stringstream buffer;\n    ifstream http_range;\n    http_range.open(\"/tmp/http_range.txt\");\n    if (!http_range.is_open()) {\n        return false;\n    }\n\n    buffer << http_range.rdbuf();\n    string response(buffer.str());\n    http_range.close();\n    return response.find(\"206 Partial Content\") != string::npos && response.find(\"Content-Length: 501\") != string::npos;\n}\n\nTEST(http_server, http_range) {\n    Server *server = test_http_server();\n    server->http_autoindex = true;\n    server->add_static_handler_location(\"/docs\");\n\n    pid_t pid = fork();\n\n    if (pid == 0) {\n        server->start();\n        exit(0);\n    }\n\n    if (pid > 0) {\n        sleep(1);\n        ON_SCOPE_EXIT {\n            kill(server->get_master_pid(), SIGTERM);\n        };\n\n        string port = to_string(server->get_primary_port()->port);\n        ASSERT_TRUE(request_with_if_range_header(SW_HTTP_RFC1123_DATE_GMT, port));\n        ASSERT_TRUE(request_with_if_range_header(SW_HTTP_RFC1123_DATE_UTC, port));\n        ASSERT_TRUE(request_with_if_range_header(SW_HTTP_RFC850_DATE, port));\n        ASSERT_TRUE(request_with_if_range_header(SW_HTTP_ASCTIME_DATE, port));\n    }\n}\n\nstatic bool request_with_diff_range(std::string port, std::string range) {\n    string str_1 = \"curl -X GET http://\";\n    string host = TEST_HOST;\n    string str_2 = \":\";\n    string str_3 = \"/docs/swoole-logo.svg -k -vvv --stderr /tmp/http_range.txt \";\n    string headers = \"-H 'Range: bytes=\" + range;\n    string command = str_1 + host + str_2 + port + str_3 + headers + \"'\";\n\n    pid_t pid;\n    close(swoole_shell_exec(command.c_str(), &pid, 0));\n\n    sleep(2);\n    stringstream buffer;\n    ifstream http_range;\n    http_range.open(\"/tmp/http_range.txt\");\n    if (!http_range.is_open()) {\n        return false;\n    }\n\n    buffer << http_range.rdbuf();\n    string response(buffer.str());\n    http_range.close();\n    return response.find(\"206 Partial Content\") != string::npos;\n}\n\nTEST(http_server, http_range2) {\n    Server *server = test_http_server();\n    server->add_static_handler_location(\"/docs\");\n    server->add_static_handler_index_files(\"swoole-logo.svg\");\n\n    pid_t pid = test::spawn_exec([&]() { server->start(); });\n\n    ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), \"0-15\"));\n    ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), \"16-31\"));\n    ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), \"-16\"));\n    ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), \"128-\"));\n    ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), \"0-0,-1\"));\n\n    usleep(100000);\n    kill(pid, SIGTERM);\n\n    int status;\n    ASSERT_EQ(waitpid(pid, &status, 0), pid);\n}\n\n// it is always last test\nTEST(http_server, abort_connection) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 2;\n    auto max_sockets = SwooleG.max_sockets;\n    SwooleG.max_sockets = 2;\n    serv.set_max_connection(1);\n    sw_logger()->set_level(SW_LOG_WARNING);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_NE(port, nullptr);\n    port->open_http_protocol = true;\n\n    auto port_dgram = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_NE(port_dgram, nullptr);\n\n    serv.create();\n\n    Mutex lock(true);\n    lock.lock();\n\n    thread th([&serv, port, &lock]() {\n        swoole_signal_block_all();\n        lock.lock();\n\n        int n = 16;\n        SyncClient *clients[n];\n\n        SW_LOOP_N(n) {\n            clients[i] = new SyncClient(SW_SOCK_TCP);\n            clients[i]->connect(TEST_HOST, port->port, -1);\n        }\n\n        httplib::Client cli(TEST_HOST, port->port);\n        auto resp = cli.Get(\"/\");\n        EXPECT_EQ(resp, nullptr);\n\n        SW_LOOP_N(n) {\n            delete clients[i];\n        }\n        serv.shutdown();\n    });\n\n    serv.onStart = [&lock](Server *serv) { lock.unlock(); };\n\n    serv.onReceive = [&](Server *server, RecvData *req) -> int { return SW_OK; };\n    serv.start();\n\n    SwooleG.max_sockets = max_sockets;\n    th.join();\n}\n\nTEST(http_server, EncodeDecodeBasic) {\n    const char *input = \"Hello World!\";\n    size_t len = strlen(input);\n\n    char *encoded = swoole::http_server::url_encode(input, len);\n    EXPECT_STREQ(encoded, \"Hello%20World%21\");\n\n    size_t decoded_len = swoole::http_server::url_decode(encoded, strlen(encoded));\n\n    EXPECT_EQ(decoded_len, strlen(input));\n    EXPECT_STREQ(encoded, input);\n\n    sw_free(encoded);\n}\n\nTEST(http_server, EncodeDecodeWithSpecialChars) {\n    const char *input = \"C++ Programming & C#\";\n    size_t len = strlen(input);\n\n    char *encoded = swoole::http_server::url_encode(input, len);\n    EXPECT_STREQ(encoded, \"C%2B%2B%20Programming%20%26%20C%23\");\n\n    size_t decoded_len = swoole::http_server::url_decode(encoded, strlen(encoded));\n\n    EXPECT_EQ(decoded_len, strlen(input));\n    EXPECT_STREQ(encoded, \"C++ Programming & C#\");\n\n    sw_free(encoded);\n}\n\nTEST(http_server, get_method) {\n    ASSERT_EQ(swoole::http_server::get_method(SW_STRL(\"POST\")), SW_HTTP_POST);\n    ASSERT_EQ(swoole::http_server::get_method(SW_STRL(\"post\")), SW_HTTP_POST);\n    ASSERT_EQ(swoole::http_server::get_method(SW_STRL(\"OPTIONS\")), SW_HTTP_OPTIONS);\n}\n\nTEST(http_server, get_method_str) {\n    ASSERT_STREQ(swoole::http_server::get_method_string(SW_HTTP_POST), \"POST\");\n    ASSERT_STREQ(swoole::http_server::get_method_string(SW_HTTP_GET), \"GET\");\n    ASSERT_STREQ(swoole::http_server::get_method_string(SW_HTTP_OPTIONS), \"OPTIONS\");\n}\n\nTEST(http_server, has_expect_header) {\n    swoole::http_server::Request req{};\n    req.buffer_ = sw_tg_buffer();\n\n    req.buffer_->append(\"HTTP/1.1 200 OK\\r\\n\"\n                        \"Cache-Control: no-cache\\r\\n\"\n                        \"Connection: close\\r\\n\"\n                        \"Content-Length: 0\\r\\n\"\n                        \"Content-Type: text/html\\r\\n\"\n                        \"Pragma: no-cache\\r\\n\\r\\n\");\n    ASSERT_FALSE(req.has_expect_header());\n\n    req.buffer_->clear();\n    req.buffer_->append(\"POST /submit HTTP/1.1\\r\\n\"\n                        \"Host: www.example.com\\r\\n\"\n                        \"Content-Type: application/json\\r\\n\"\n                        \"Content-Length: 1000000\\r\\n\"\n                        \"Expect: 100-continue\\r\\n\\r\\n\");\n    ASSERT_TRUE(req.has_expect_header());\n}\n\nTEST(http_server, get_status_message) {\n    size_t n = sizeof(http_server::list_of_status_code) / sizeof(int);\n    SW_LOOP_N(n) {\n        auto code = http_server::list_of_status_code[i];\n        if (code == -1) {\n            break;\n        }\n        auto error = http_server::get_status_message(code);\n        auto str = String(error);\n        ASSERT_TRUE(str.starts_with(std::to_string(code) + \" \"));\n    }\n\n    auto error = swoole::http_server::get_status_message(999);\n    auto str = String(error);\n    ASSERT_TRUE(str.equals(std::to_string(999) + \" Unknown Status\"));\n}\n\nstatic swoole::http_server::Request req;\n\nstatic void SetRequestContent(const std::string &str) {\n    delete req.buffer_;\n    req = {};\n    req.buffer_ = new String(str);\n};\n\nstatic void SetRequestContent(String *str) {\n    delete req.buffer_;\n    req.buffer_ = str;\n};\n\nTEST(http_server, get_protocol) {\n    static auto request = &req;\n    // 测试有效的 GET 请求\n    {\n        SetRequestContent(\"GET /index.html HTTP/1.1\\r\\nHost: example.com\\r\\n\\r\\n\");\n        ASSERT_EQ(request->get_protocol(), SW_OK);\n        EXPECT_EQ(request->method, SW_HTTP_GET);\n        EXPECT_EQ(request->version, SW_HTTP_VERSION_11);\n\n        std::string url(request->buffer_->str + request->url_offset_, request->url_length_);\n        EXPECT_EQ(url, \"/index.html\");\n    }\n\n    // 超长 URL\n    {\n        auto s = new String();\n        s->append(\"GET /home/explore/?a=\");\n        s->append_random_bytes(swoole_rand(1024, 2048), 1);\n        s->append(\" HTTP/1.1\\r\\n\");\n        s->append(\"Host: 127.0.0.1\\r\\n\");\n        s->append(\"Connection: keep-alive\\r\\n\");\n        s->append(\"Cache-Control: max-age=0\\r\\n\\r\\n\");\n        SetRequestContent(s);\n        ASSERT_EQ(request->get_protocol(), SW_OK);\n        EXPECT_EQ(request->method, SW_HTTP_GET);\n        EXPECT_EQ(request->version, SW_HTTP_VERSION_11);\n\n        std::string url(request->buffer_->str + request->url_offset_, request->url_length_);\n        EXPECT_EQ(url.substr(0, url.find('?')), \"/home/explore/\");\n    }\n\n    // 测试有效的 POST 请求\n    {\n        SetRequestContent(\"POST /submit HTTP/1.0\\r\\nContent-Type: application/json\\r\\n\\r\\n{\\\"key\\\":\\\"value\\\"}\");\n\n        ASSERT_EQ(request->get_protocol(), SW_OK);\n        EXPECT_EQ(request->method, SW_HTTP_POST);\n        EXPECT_EQ(request->version, SW_HTTP_VERSION_10);\n\n        std::string url(request->buffer_->str + request->url_offset_, request->url_length_);\n        EXPECT_EQ(url, \"/submit\");\n    }\n    // 测试 PUT 方法\n    {\n        SetRequestContent(\"PUT /resource HTTP/1.1\\r\\nContent-Type: text/plain\\r\\n\\r\\nNew content\");\n\n        ASSERT_EQ(request->get_protocol(), SW_OK);\n        EXPECT_EQ(request->method, SW_HTTP_PUT);\n        EXPECT_EQ(request->version, SW_HTTP_VERSION_11);\n\n        std::string url(request->buffer_->str + request->url_offset_, request->url_length_);\n        EXPECT_EQ(url, \"/resource\");\n    }\n\n    // 测试 HTTP2 前言\n    {\n        SetRequestContent(SW_HTTP2_PRI_STRING);\n\n        ASSERT_EQ(request->get_protocol(), SW_OK);\n        EXPECT_EQ(request->method, SW_HTTP_PRI);\n    }\n\n    // 测试无效的请求 - 太短\n    {\n        SetRequestContent(\"GET /\");\n\n        ASSERT_EQ(request->get_protocol(), SW_ERR);\n        EXPECT_EQ(request->excepted, 0);  // wait more data\n    }\n\n    // 测试无效的请求 - 未知方法\n    {\n        SetRequestContent(\"UNKNOWN /path HTTP/1.1\\r\\n\");\n\n        ASSERT_EQ(request->get_protocol(), SW_ERR);\n        EXPECT_EQ(request->excepted, 1);\n    }\n\n    // 测试无效的请求 - 缺少 URL\n    {\n        SetRequestContent(\"UPDATE  HTTP/1.1\\r\\n\");\n\n        ASSERT_EQ(request->get_protocol(), SW_ERR);\n        EXPECT_EQ(request->excepted, 1);\n    }\n\n    // 测试无效的请求 - 无效的 HTTP 版本\n    {\n        SetRequestContent(\"GET /index.html HTTP/2.0\\r\\n\");\n\n        ASSERT_EQ(request->get_protocol(), SW_ERR);\n        EXPECT_EQ(request->excepted, 1);\n    }\n\n    // 测试无效的请求 - 缺少 HTTP 版本\n    {\n        SetRequestContent(\"UNSUBSCRIBE /index.html\\r\\n\");\n\n        ASSERT_EQ(request->get_protocol(), SW_ERR);\n        EXPECT_EQ(request->excepted, 1);\n    }\n\n    // 测试复杂 URL\n    {\n        SetRequestContent(\"GET /search?q=test&page=1#results HTTP/1.1\\r\\n\");\n\n        ASSERT_EQ(request->get_protocol(), SW_OK);\n        EXPECT_EQ(request->method, SW_HTTP_GET);\n\n        std::string url(request->buffer_->str + request->url_offset_, request->url_length_);\n        EXPECT_EQ(url, \"/search?q=test&page=1#results\");\n    }\n\n    // 测试多余空格\n    {\n        SetRequestContent(\"GET    /index.html    HTTP/1.1\\r\\n\");\n\n        ASSERT_EQ(request->get_protocol(), SW_OK);\n        EXPECT_EQ(request->method, SW_HTTP_GET);\n        EXPECT_EQ(request->version, SW_HTTP_VERSION_11);\n\n        std::string url(request->buffer_->str + request->url_offset_, request->url_length_);\n        EXPECT_EQ(url, \"/index.html\");\n    }\n}\n\nTEST(http_server, all_method) {\n    static auto request = &req;\n    SW_LOOP_N(SW_HTTP_PRI + 4) {\n        auto str = http_server::get_method_string(i);\n        if (i == 0 || i > SW_HTTP_PRI) {\n            ASSERT_EQ(str, nullptr);\n        } else {\n            SetRequestContent(str + std::string(\" / HTTP/1.1\\r\\nHost: example.com\\r\\n\\r\\n\"));\n            if (i == SW_HTTP_PRI) {\n                ASSERT_EQ(request->get_protocol(), SW_ERR);\n                EXPECT_EQ(request->excepted, true);\n            } else {\n                ASSERT_EQ(request->get_protocol(), SW_OK);\n                EXPECT_EQ(request->method, i);\n            }\n        }\n    }\n}\n\nTEST(http_server, parse_multipart_boundary) {\n    char *boundary_str;\n    int boundary_len;\n    {\n        const char *input = \"Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryX\";\n        size_t length = strlen(input);\n        size_t offset = 30;  // 从 \"boundary=\" 之前开始\n\n        ASSERT_TRUE(parse_multipart_boundary(input, length, offset, &boundary_str, &boundary_len));\n        EXPECT_EQ(std::string(boundary_str, boundary_len), \"----WebKitFormBoundaryX\");\n    }\n\n    // 测试带引号的 boundary\n    {\n        const char *input = \"Content-Type: multipart/form-data; boundary=\\\"----WebKitFormBoundaryX\\\"\";\n        size_t length = strlen(input);\n        size_t offset = 30;\n\n        ASSERT_TRUE(parse_multipart_boundary(input, length, offset, &boundary_str, &boundary_len));\n        EXPECT_EQ(std::string(boundary_str, boundary_len), \"----WebKitFormBoundaryX\");\n    }\n\n    // 测试带空格的格式\n    {\n        const char *input = \"Content-Type: multipart/form-data; boundary = ----WebKitFormBoundaryX\";\n        size_t length = strlen(input);\n        size_t offset = 30;\n\n        ASSERT_FALSE(parse_multipart_boundary(input, length, offset, &boundary_str, &boundary_len));\n        EXPECT_EQ(std::string(boundary_str, boundary_len), \"----WebKitFormBoundaryX\");\n    }\n\n    // 测试 boundary 后有其他参数\n    {\n        const char *input = \"Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryX; charset=UTF-8\";\n        size_t length = strlen(input);\n        size_t offset = 30;\n\n        ASSERT_TRUE(parse_multipart_boundary(input, length, offset, &boundary_str, &boundary_len));\n        EXPECT_EQ(std::string(boundary_str, boundary_len), \"----WebKitFormBoundaryX\");\n    }\n\n    // 测试 boundary 前有其他参数\n    {\n        const char *input = \"Content-Type: multipart/form-data; charset=UTF-8; boundary=----WebKitFormBoundaryX\";\n        size_t length = strlen(input);\n        size_t offset = 30;\n\n        ASSERT_TRUE(parse_multipart_boundary(input, length, offset, &boundary_str, &boundary_len));\n        EXPECT_EQ(std::string(boundary_str, boundary_len), \"----WebKitFormBoundaryX\");\n    }\n\n    // 测试空 boundary\n    {\n        const char *input = \"Content-Type: multipart/form-data; boundary=\";\n        size_t length = strlen(input);\n        size_t offset = 30;\n\n        ASSERT_FALSE(parse_multipart_boundary(input, length, offset, &boundary_str, &boundary_len));\n    }\n\n    // 测试空引号 boundary\n    {\n        const char *input = \"Content-Type: multipart/form-data; boundary=\\\"\\\"\";\n        size_t length = strlen(input);\n        size_t offset = 30;\n\n        ASSERT_FALSE(parse_multipart_boundary(input, length, offset, &boundary_str, &boundary_len));\n    }\n\n    // 测试没有 boundary 参数\n    {\n        const char *input = \"Content-Type: multipart/form-data; charset=UTF-8\";\n        size_t length = strlen(input);\n        size_t offset = 30;\n\n        ASSERT_FALSE(parse_multipart_boundary(input, length, offset, &boundary_str, &boundary_len));\n    }\n\n    // 测试大小写不敏感\n    {\n        const char *input = \"Content-Type: multipart/form-data; BOUNDARY=----WebKitFormBoundaryX\";\n        size_t length = strlen(input);\n        size_t offset = 30;\n\n        ASSERT_TRUE(parse_multipart_boundary(input, length, offset, &boundary_str, &boundary_len));\n        EXPECT_EQ(std::string(boundary_str, boundary_len), \"----WebKitFormBoundaryX\");\n    }\n\n    // 测试 offset 为 0 的情况\n    {\n        const char *input = \"boundary=----WebKitFormBoundaryX\";\n        size_t length = strlen(input);\n        size_t offset = 0;\n\n        ASSERT_TRUE(parse_multipart_boundary(input, length, offset, &boundary_str, &boundary_len));\n        EXPECT_EQ(std::string(boundary_str, boundary_len), \"----WebKitFormBoundaryX\");\n    }\n\n    // 测试超出范围的 offset\n    {\n        const char *input = \"Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryX\";\n        size_t length = strlen(input);\n        size_t offset = length + 1;\n\n        ASSERT_FALSE(parse_multipart_boundary(input, length, offset, &boundary_str, &boundary_len));\n    }\n}\n\nTEST(http_server, get_package_length) {\n    network::Socket fake_sock;\n    PacketLength pl;\n    Connection conn;\n\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_NE(port, nullptr);\n    port->open_http_protocol = true;\n    port->open_http2_protocol = true;\n    port->init_protocol();\n    ASSERT_EQ(port->protocol.package_length_size, SW_HTTP2_FRAME_HEADER_SIZE);\n\n    port->open_websocket_protocol = true;\n    port->init_protocol();\n    ASSERT_EQ(port->protocol.get_package_length_size, http_server::get_package_length_size);\n\n    fake_sock.fd = port->get_fd();\n    fake_sock.object = &conn;\n    fake_sock.socket_type = port->type;\n    fake_sock.get_name();\n\n    conn.info = fake_sock.info;\n    conn.session_id = 2025;\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    // websocket\n    sw_tg_buffer()->clear();\n    conn.websocket_status = websocket::STATUS_HANDSHAKE;\n    ASSERT_EQ(http_server::get_package_length_size(&fake_sock), SW_WEBSOCKET_FRAME_HEADER_SIZE);\n    ASSERT_TRUE(websocket::encode(sw_tg_buffer(), SW_STRL(TEST_STR), websocket::OPCODE_TEXT, websocket::FLAG_FIN));\n    pl.buf = sw_tg_buffer()->str;\n    pl.buf_size = sw_tg_buffer()->length;\n    ASSERT_EQ(http_server::get_package_length(&port->protocol, &fake_sock, &pl),\n              SW_WEBSOCKET_HEADER_LEN + strlen(TEST_STR));\n\n    // http2\n    sw_tg_buffer()->clear();\n    conn.http2_stream = 1;\n    conn.websocket_status = 0;\n    ASSERT_EQ(http_server::get_package_length_size(&fake_sock), SW_HTTP2_FRAME_HEADER_SIZE);\n    http2::Settings settings_1{};\n    http2::init_settings(&settings_1);\n    sw_tg_buffer()->length = http2::pack_setting_frame(sw_tg_buffer()->str, settings_1, false);\n    pl.buf = sw_tg_buffer()->str;\n    pl.buf_size = sw_tg_buffer()->length;\n\n    ASSERT_GT(sw_tg_buffer()->length, 16);\n    ASSERT_EQ(http_server::get_package_length(&port->protocol, &fake_sock, &pl), sw_tg_buffer()->length);\n\n    // http1.1\n    conn.websocket_status = 0;\n    conn.http2_stream = 0;\n    ASSERT_EQ(http_server::get_package_length_size(&fake_sock), 0);\n    ASSERT_EQ(http_server::get_package_length(&port->protocol, &fake_sock, &pl), SW_ERR);\n\n    String str(swoole_get_last_error_msg());\n    ASSERT_EQ(swoole_get_last_error(), SW_ERROR_PROTOCOL_ERROR);\n    ASSERT_TRUE(str.contains(\"unexpected protocol status of session\"));\n}\n\nstatic void test_ssl_http(Server::Mode mode) {\n    Server serv(mode);\n    serv.worker_num = 1;\n    swoole_set_log_level(SW_LOG_INFO);\n\n    Mutex *lock = new Mutex(true);\n    lock->lock();\n\n    const int server_port = __LINE__ + TEST_PORT;\n    ListenPort *port = serv.add_port((enum swSocketType)(SW_SOCK_TCP | SW_SOCK_SSL), TEST_HOST, server_port);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    port->open_http_protocol = 1;\n    port->set_ssl_cert_file(test::get_ssl_dir() + \"/server.crt\");\n    port->set_ssl_key_file(test::get_ssl_dir() + \"/server.key\");\n    port->ssl_context->http = 1;\n    port->ssl_init();\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    thread t1;\n\n    serv.onStart = [&lock, &t1](Server *serv) {\n        t1 = thread([=]() {\n            swoole_signal_block_all();\n            lock->lock();\n\n            auto cmd = \"curl -k https://127.0.0.1:\" + std::to_string(server_port) + \"/\";\n            pid_t pid;\n            auto _pipe = swoole_shell_exec(cmd.c_str(), &pid, 1);\n            String buf(1024);\n            while (1) {\n                auto n = read(_pipe, buf.str + buf.length, buf.size - buf.length);\n                if (n > 0) {\n                    buf.grow(n);\n                    continue;\n                }\n                break;\n            }\n\n            close(_pipe);\n            usleep(10000);\n\n            ASSERT_TRUE(buf.contains(TEST_STR));\n\n            serv->shutdown();\n        });\n    };\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock->unlock(); };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        SessionId fd = req->info.fd;\n        auto resp = \"HTTP/1.1 200 OK\\r\\nConnection: close\\r\\nContent-Length: \" + std::to_string(strlen(TEST_STR)) +\n                    \"\\r\\n\\r\\n\" TEST_STR;\n        serv->send(fd, resp.c_str(), resp.length());\n        serv->close(fd);\n        return SW_OK;\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n\n    t1.join();\n    delete lock;\n}\n\nTEST(http_server, ssl) {\n    test_ssl_http(Server::MODE_BASE);\n}\n\nTEST(http_server, fail) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n\n    std::string bad_path;\n    bad_path.resize(PATH_MAX + 4);\n    ASSERT_FALSE(serv.set_document_root(bad_path));\n    ASSERT_ERREQ(SW_ERROR_NAME_TOO_LONG);\n\n    std::string not_exists_path(\"/tmp/swoole-core-tests-not-exists\");\n    ASSERT_FALSE(serv.set_document_root(not_exists_path));\n    ASSERT_ERREQ(SW_ERROR_DIR_NOT_EXIST);\n}\n"
  },
  {
    "path": "core-tests/src/server/message_bus.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n\n#include \"swoole_server.h\"\n#include \"swoole_memory.h\"\n#include \"swoole_signal.h\"\n#include \"swoole_lock.h\"\n\nusing namespace std;\nusing namespace swoole;\n\nconstexpr int DATA_SIZE = 2 * SW_NUM_MILLION;\n\nstruct TestPacket {\n    SessionId fd;\n    std::string data;\n};\n\nstruct TestMB {\n    std::vector<TestPacket> q;\n    MessageBus mb;\n    std::function<ssize_t(network::Socket *)> read_func;\n\n    bool send_empty_packet(network::Socket *sock) {\n        SendData _data4;\n        _data4.data = \"hello world\";\n        _data4.info.fd = 4;\n        _data4.info.len = 0;\n        if (!mb.write(sock, &_data4)) {\n            return false;\n        }\n\n        SendData _data5;\n        _data5.data = nullptr;\n        _data5.info.fd = 5;\n        _data5.info.len = 10;\n        if (!mb.write(sock, &_data5)) {\n            return false;\n        }\n\n        return true;\n    }\n\n    int read(Event *ev) {\n        auto retval = read_func(ev->socket);\n        if (retval == 0) {\n            return SW_OK;\n        } else if (retval < 0) {\n            swoole_event_del(ev->socket);\n            return SW_ERR;\n        }\n\n        auto packet = mb.get_packet();\n\n        q.push_back(TestPacket{\n            mb.get_buffer()->info.fd,\n            std::string(packet.data, packet.length),\n        });\n\n        if (q.size() == 5) {\n            swoole_event_del(ev->socket);\n        }\n\n        return SW_OK;\n    }\n};\n\n#define MB_SEND(i, s)                                                                                                  \\\n    String pkt##i(s);                                                                                                  \\\n    pkt##i.append_random_bytes(pkt##i.size - 1, false);                                                                \\\n    pkt##i.append('\\0');                                                                                               \\\n                                                                                                                       \\\n    SendData _data##i{};                                                                                               \\\n    _data##i.data = pkt##i.value();                                                                                    \\\n    _data##i.info.fd = i;                                                                                              \\\n    _data##i.info.len = pkt##i.get_length();                                                                           \\\n    ASSERT_TRUE(tmb.mb.write(p.get_socket(true), &_data##i));\n\n#define MB_ASSERT(i)                                                                                                   \\\n    auto r##i = tmb.q.at(i - 1);                                                                                       \\\n    ASSERT_EQ(r##i.fd, i);                                                                                             \\\n    ASSERT_STREQ(r##i.data.c_str(), pkt##i.value());\n\nTEST(message_bus, read) {\n    UnixSocket p(true, SOCK_STREAM);\n    ASSERT_TRUE(p.ready());\n\n    ASSERT_EQ(swoole_event_init(SW_EVENTLOOP_WAIT_EXIT), SW_OK);\n    p.set_blocking(false);\n    p.set_buffer_size(65536);\n\n    uint64_t msg_id = 0;\n\n    TestMB tmb{};\n    tmb.mb.set_buffer_size(65536);\n    tmb.mb.set_id_generator([&msg_id]() { return msg_id++; });\n    tmb.mb.alloc_buffer();\n\n    tmb.read_func = [&tmb](network::Socket *sock) { return tmb.mb.read(sock); };\n\n    sw_reactor()->ptr = &tmb;\n\n    ASSERT_EQ(swoole_event_add(p.get_socket(false), SW_EVENT_READ), SW_OK);\n\n    swoole_event_set_handler(SW_FD_PIPE, SW_EVENT_READ, [](Reactor *reactor, Event *ev) -> int {\n        TestMB *tmb = (TestMB *) reactor->ptr;\n        return tmb->read(ev);\n    });\n\n    MB_SEND(1, DATA_SIZE);\n    MB_SEND(2, tmb.mb.get_buffer_size());\n    MB_SEND(3, 2341);\n\n    tmb.send_empty_packet(p.get_socket(true));\n\n    ASSERT_EQ(swoole_event_wait(), SW_OK);\n\n    MB_ASSERT(1);\n    MB_ASSERT(2);\n    MB_ASSERT(3);\n\n    auto r4 = tmb.q.at(3);\n    ASSERT_EQ(r4.fd, 4);\n    ASSERT_STREQ(r4.data.c_str(), \"\");\n\n    auto r5 = tmb.q.at(4);\n    ASSERT_EQ(r5.fd, 5);\n    ASSERT_STREQ(r5.data.c_str(), \"\");\n}\n\nTEST(message_bus, read_with_buffer) {\n    UnixSocket p(true, SOCK_DGRAM);\n    ASSERT_TRUE(p.ready());\n\n    ASSERT_EQ(swoole_event_init(SW_EVENTLOOP_WAIT_EXIT), SW_OK);\n    p.set_blocking(false);\n    p.set_buffer_size(65536);\n\n    uint64_t msg_id = 0;\n\n    TestMB tmb{};\n    tmb.mb.set_buffer_size(65536);\n    tmb.mb.set_id_generator([&msg_id]() { return msg_id++; });\n    tmb.mb.alloc_buffer();\n\n    tmb.read_func = [&tmb](network::Socket *sock) { return tmb.mb.read_with_buffer(sock); };\n\n    sw_reactor()->ptr = &tmb;\n\n    ASSERT_EQ(swoole_event_add(p.get_socket(false), SW_EVENT_READ), SW_OK);\n\n    swoole_event_set_handler(SW_FD_PIPE, SW_EVENT_READ, [](Reactor *reactor, Event *ev) -> int {\n        TestMB *tmb = (TestMB *) reactor->ptr;\n        return tmb->read(ev);\n    });\n\n    MB_SEND(1, DATA_SIZE);\n    MB_SEND(2, tmb.mb.get_buffer_size());\n    MB_SEND(3, 2341);\n\n    tmb.send_empty_packet(p.get_socket(true));\n\n    ASSERT_EQ(swoole_event_wait(), SW_OK);\n\n    MB_ASSERT(1);\n    MB_ASSERT(2);\n    MB_ASSERT(3);\n\n    ASSERT_GT(tmb.mb.get_memory_size(), 1024 * 1024 * 2);\n\n    auto r4 = tmb.q.at(3);\n    ASSERT_EQ(r4.fd, 4);\n    ASSERT_STREQ(r4.data.c_str(), \"\");\n\n    auto r5 = tmb.q.at(4);\n    ASSERT_EQ(r5.fd, 5);\n    ASSERT_STREQ(r5.data.c_str(), \"\");\n}\n"
  },
  {
    "path": "core-tests/src/server/mqtt.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n\n#include \"swoole_server.h\"\n#include \"swoole_memory.h\"\n#include \"swoole_signal.h\"\n#include \"swoole_lock.h\"\n#include \"swoole_util.h\"\n\nusing namespace std;\nusing namespace swoole;\n\nenum MqttPacketType {\n    CONNECT = 1,\n    CONNACK = 2,\n    PUBLISH = 3,\n    PUBACK = 4,\n    SUBSCRIBE = 8,\n    SUBACK = 9,\n    DISCONNECT = 14,\n};\n\nstd::string current_timestamp() {\n    using namespace std::chrono;\n    auto now = system_clock::now();\n    time_t t = system_clock::to_time_t(now);\n    char buf[64];\n    struct tm tm_now;\n    localtime_r(&t, &tm_now);\n    strftime(buf, sizeof(buf), \"%Y-%m-%d %H:%M:%S\", &tm_now);\n    return std::string(buf);\n}\n\nstruct MqttSession {\n    SessionId fd;\n    bool subscribed = false;\n    uint16_t count = 0;\n    uint16_t packet_id_subscribe = 0;\n    std::string subscribed_topic;\n    Server *server;\n\n    MqttSession(Server *_server, SessionId fd_) : fd(fd_), server(_server) {}\n\n    // 发送 CONNACK 报文，简单实现：Session Present=0, Connect Return code=0 (Success)\n    bool send_connack() {\n        uint8_t connack[] = {\n            0x20,\n            0x02,  // 固定报头：CONNACK, 剩余长度2\n            0x00,  // Session Present = 0\n            0x00   // Connect return code = 0 (成功)\n        };\n        return server->send(fd, connack, sizeof(connack));\n    }\n\n    // 发送 SUBACK 报文，确认订阅成功\n    bool send_suback(uint16_t packet_id) {\n        uint8_t suback[] = {\n            0x90,\n            0x03,  // 固定报头：SUBACK, 剩余长度3\n            uint8_t(packet_id >> 8),\n            uint8_t(packet_id & 0xFF),  // 报文标识符\n            0x00                        // 返回码：0x00 QoS 0\n        };\n        return server->send(fd, suback, sizeof(suback));\n    }\n\n    // 发送 PUBLISH 报文，QoS 0 简化，无标识符\n    bool send_publish(const std::string &topic, const std::string &message) {\n        // PUBLISH fixed header: 0x30 (QoS0), 剩余长度计算\n        // variable header: topic (2字节长度 + 字符串)\n        uint16_t topic_len = topic.size();\n        size_t var_header_len = 2 + topic_len;\n        size_t payload_len = message.size();\n        size_t remaining_length = var_header_len + payload_len;\n\n        std::vector<uint8_t> packet;\n        packet.push_back(0x30);  // PUBLISH, QoS0\n\n        // MQTT剩余长度使用可变长度编码，这里实现简单编码（长度<128假定）\n        if (remaining_length < 128) {\n            packet.push_back(uint8_t(remaining_length));\n        } else {\n            // 简单处理大于127的长度，实际可以完善\n            do {\n                uint8_t byte = remaining_length % 128;\n                remaining_length /= 128;\n                if (remaining_length > 0) byte |= 0x80;\n                packet.push_back(byte);\n            } while (remaining_length > 0);\n        }\n\n        // variable header topic\n        packet.push_back(uint8_t(topic_len >> 8));\n        packet.push_back(uint8_t(topic_len & 0xFF));\n        packet.insert(packet.end(), topic.begin(), topic.end());\n\n        // payload\n        packet.insert(packet.end(), message.begin(), message.end());\n\n        return server->send(fd, packet.data(), packet.size()) == (ssize_t) packet.size();\n    }\n\n    bool send_puback(uint16_t packet_id) {\n        uint8_t puback[] = {0x40, 0x02, uint8_t(packet_id >> 8), uint8_t(packet_id & 0xFF)};\n        return server->send(fd, puback, sizeof(puback));\n    }\n\n    bool send_disconnect() {\n        uint8_t disconnect[] = {0xE0, 0x00};\n        return server->send(fd, disconnect, sizeof(disconnect));\n    }\n\n    bool process_packet(const uint8_t *data, size_t len) {\n        uint8_t packet_type = (data[0] >> 4);\n        switch (packet_type) {\n        case CONNECT: {\n            std::cout << \"收到 CONNECT 报文\\n\";\n            // 简化：收到CONNECT直接回复CONNACK成功\n            return send_connack();\n        }\n        case SUBSCRIBE: {\n            std::cout << \"收到 SUBSCRIBE 报文\\n\";\n            // SUBSCRIBE 报文结构：固定头 + 剩余长度 + 报文标识符 (2bytes) + Payload\n            // 简化解析报文标识符和第一个订阅主题\n            if (len < 5) return false;\n            uint16_t packet_id = (data[2] << 8) | data[3];\n            packet_id_subscribe = packet_id;\n\n            size_t pos = 4;\n            if (pos + 2 > len) return false;\n            uint16_t topic_len = (data[pos] << 8) | data[pos + 1];\n            pos += 2;\n            if (pos + topic_len > len) return false;\n            subscribed_topic.assign((const char *) (data + pos), topic_len);\n            std::cout << \"订阅主题: \" << subscribed_topic << std::endl;\n\n            subscribed = true;\n            return send_suback(packet_id);\n        }\n        case PUBLISH: {\n            std::cout << \"收到 PUBLISH 报文\\n\";\n\n            uint8_t flags = data[0] & 0x0F;\n            uint8_t qos = (flags & 0x06) >> 1;\n\n            // TODO 需可变长度解析\n            size_t remaining_length = data[1];\n            EXPECT_GT(remaining_length, 2);\n\n            size_t pos = 2;\n            if (pos + 2 > len) return false;\n\n            uint16_t topic_len = (data[pos] << 8) | data[pos + 1];\n            pos += 2;\n            if (pos + topic_len > len) return false;\n\n            std::string topic((const char *) (data + pos), topic_len);\n            pos += topic_len;\n\n            uint16_t packet_id = 0;\n            if (qos > 0) {\n                if (pos + 2 > len) return false;\n                packet_id = (data[pos] << 8) | data[pos + 1];\n                pos += 2;\n            }\n\n            if (pos > len) return false;\n\n            std::string payload((const char *) (data + pos), len - pos);\n\n            std::cout << \"主题: \" << topic << \", 消息体: \" << payload << \", QoS: \" << (int) qos << std::endl;\n\n            // 根据需要处理 payload 内容\n            // 例如转发给其他客户端、存储等\n\n            // QoS1需要发送PUBACK确认\n            if (qos == 1) {\n                return send_puback(packet_id);\n            }\n\n            // QoS0直接返回成功\n            return true;\n        }\n        // 你可以增加 PINGREQ、DISCONNECT 等消息处理\n        default: {\n            std::cout << \"收到未处理的包类型: \" << (int) packet_type << std::endl;\n            return true;\n        }\n        }\n    }\n};\n\nstatic void test_mqtt_server(function<void(Server *)> fn) {\n    thread child_thread;\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n    serv.enable_reuse_port = true;\n    serv.private_data_2 = (void *) &fn;\n\n    sw_logger()->set_level(SW_LOG_WARNING);\n\n    std::unordered_map<SessionId, MqttSession *> sessions;\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 9501);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n    port->open_mqtt_protocol = 1;\n\n    serv.create();\n\n    serv.onWorkerStart = [&child_thread](Server *serv, Worker *worker) {\n        function<void(Server *)> fn = *(function<void(Server *)> *) serv->private_data_2;\n        child_thread = thread(fn, serv);\n    };\n\n    serv.onClose = [&sessions](Server *serv, DataHead *info) -> void {\n        delete sessions[info->fd];\n        sessions.erase(info->fd);\n    };\n\n    serv.onConnect = [&sessions](Server *serv, DataHead *info) -> void {\n        auto session = new MqttSession(serv, info->fd);\n        sessions[info->fd] = session;\n        swoole_timer_tick(100, [session, serv](auto r1, TimerNode *tnode) {\n            if (session->subscribed) {\n                std::string ts = current_timestamp();\n                session->send_publish(session->subscribed_topic,\n                                      \"Index: \" + std::to_string(session->count) + \", Time: \" + ts);\n                session->count++;\n                if (session->count > 10) {\n                    session->send_disconnect();\n                    serv->close(session->fd, false);\n                    swoole_timer_del(tnode);\n                }\n            }\n        });\n    };\n\n    serv.onReceive = [&sessions](Server *serv, RecvData *req) -> int {\n        auto session = sessions[req->info.fd];\n        if (!session->process_packet((uint8_t *) req->data, req->info.len)) {\n            std::cerr << \"处理数据包失败，关闭连接\\n\";\n        }\n        return SW_OK;\n    };\n\n    serv.start();\n    child_thread.join();\n}\n\nTEST(mqtt, echo) {\n    test_mqtt_server([](Server *serv) {\n        swoole_signal_block_all();\n        EXPECT_EQ(test::exec_js_script(\"mqtt.js\", std::to_string(serv->get_primary_port()->get_port())), 0);\n        kill(serv->get_master_pid(), SIGTERM);\n    });\n\n    File fp(TEST_LOG_FILE, O_RDONLY);\n    EXPECT_TRUE(fp.ready());\n    auto str = fp.read_content();\n    SW_LOOP_N(10) {\n        ASSERT_TRUE(\n            str->contains(\"received message, topic: test/topic, content: Index: \" + std::to_string(i) + \", Time: \"));\n    }\n    unlink(TEST_LOG_FILE);\n}\n"
  },
  {
    "path": "core-tests/src/server/multipart_parser.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | Author    NathanFreeman  <mariasocute@163.com>                         |\n  +----------------------------------------------------------------------+\n */\n#include \"test_core.h\"\n#include \"multipart_parser.h\"\n\nstruct MppResult {\n    std::string data;\n    std::string header_field;\n    std::string header_value;\n    bool header_complete;\n    bool body_end;\n};\n\nstatic int multipart_on_header_field(multipart_parser *p, const char *at, size_t length) {\n    swoole_trace(\"on_header_field: at=%.*s, length=%lu\", (int) length, at, length);\n    auto res = (MppResult *) p->data;\n    res->header_field = std::string(at, length);\n    return 0;\n}\n\nstatic int multipart_on_header_value(multipart_parser *p, const char *at, size_t length) {\n    swoole_trace(\"on_header_value: at=%.*s, length=%lu\", (int) length, at, length);\n    auto res = (MppResult *) p->data;\n    res->header_value = std::string(at, length);\n    return 0;\n}\n\nstatic int multipart_on_data(multipart_parser *p, const char *at, size_t length) {\n    swoole_trace(\"on_data: length=%lu\", length);\n    auto res = (MppResult *) p->data;\n    res->data.append(at, length);\n    return 0;\n}\n\nstatic int multipart_on_header_complete(multipart_parser *p) {\n    swoole_trace(\"on_header_complete\");\n    auto res = (MppResult *) p->data;\n    res->header_complete = true;\n    return 0;\n}\n\nstatic int multipart_on_data_end(multipart_parser *p) {\n    swoole_trace(\"on_data_end\");\n    return 0;\n}\n\nstatic int multipart_on_part_begin(multipart_parser *p) {\n    swoole_trace(\"on_part_begin\");\n    return 0;\n}\n\nstatic int multipart_on_body_end(multipart_parser *p) {\n    swoole_trace(\"on_body_end\");\n    auto res = (MppResult *) p->data;\n    res->body_end = true;\n    return 0;\n}\n\nstatic multipart_parser_settings _settings{\n    multipart_on_header_field,\n    multipart_on_header_value,\n    multipart_on_data,\n    multipart_on_part_begin,\n    multipart_on_header_complete,\n    multipart_on_data_end,\n    multipart_on_body_end,\n};\n\nstatic const std::string boundary = \"--WebKitFormBoundaryeGOz80A8JnaO6kuw\";\n\nstatic multipart_parser *create_parser() {\n    return multipart_parser_init(boundary.c_str(), boundary.length(), &_settings);\n}\n\nstatic void create_error(multipart_parser *parser, multipart_error error_reason, const char *error) {\n    size_t length = 1024;\n    char buf[length];\n\n    parser->error_reason = error_reason;\n    int result_len = multipart_parser_error_msg(parser, buf, length);\n    ASSERT_GT(result_len, 0);\n    buf[result_len] = '\\0';\n\n    std::string response(buf, result_len);\n    ASSERT_TRUE(response.find(error) != response.npos);\n}\n\nTEST(multipart_parser, error_message) {\n    size_t length = 1024;\n    char buf[length];\n    auto parser = create_parser();\n\n    parser->error_reason = MPPE_OK;\n    ASSERT_EQ(multipart_parser_error_msg(parser, buf, length), 0);\n\n    parser->error_expected = '\\0';\n    create_error(parser, MPPE_PAUSED, \"parser paused\");\n    create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, \"no CRLF at first boundary end: \");\n    create_error(parser, MPPE_BAD_START_BOUNDARY, \"first boundary mismatching: \");\n    create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, \"invalid char in header field: \");\n    create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, \"invalid char in header value: \");\n    create_error(parser, MPPE_BAD_PART_END, \"no next part or final hyphen: expecting CR or '-' \");\n    create_error(parser, MPPE_END_BOUNDARY_NO_DASH, \"bad final hyphen: \");\n\n    parser->error_expected = '\\r';\n    create_error(parser, MPPE_PAUSED, \"parser paused\");\n    create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, \"no CRLF at first boundary end: \");\n    create_error(parser, MPPE_BAD_START_BOUNDARY, \"first boundary mismatching: \");\n    create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, \"invalid char in header field: \");\n    create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, \"invalid char in header value: \");\n    create_error(parser, MPPE_BAD_PART_END, \"no next part or final hyphen: expecting CR or '-' \");\n    create_error(parser, MPPE_END_BOUNDARY_NO_DASH, \"bad final hyphen: \");\n\n    parser->error_expected = '\\n';\n    create_error(parser, MPPE_PAUSED, \"parser paused\");\n    create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, \"no CRLF at first boundary end: \");\n    create_error(parser, MPPE_BAD_START_BOUNDARY, \"first boundary mismatching: \");\n    create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, \"invalid char in header field: \");\n    create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, \"invalid char in header value: \");\n    create_error(parser, MPPE_BAD_PART_END, \"no next part or final hyphen: expecting CR or '-' \");\n    create_error(parser, MPPE_END_BOUNDARY_NO_DASH, \"bad final hyphen: \");\n\n    parser->error_expected = 'a';\n    create_error(parser, MPPE_PAUSED, \"parser paused\");\n    create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, \"no CRLF at first boundary end: \");\n    create_error(parser, MPPE_BAD_START_BOUNDARY, \"first boundary mismatching: \");\n    create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, \"invalid char in header field: \");\n    create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, \"invalid char in header value: \");\n    create_error(parser, MPPE_BAD_PART_END, \"no next part or final hyphen: expecting CR or '-' \");\n    create_error(parser, MPPE_END_BOUNDARY_NO_DASH, \"bad final hyphen: \");\n}\n\nTEST(multipart_parser, header_field) {\n    auto parser = create_parser();\n    ssize_t ret;\n\n    // header party\n    swoole::String header(1024);\n    header.append(\"--\");\n    header.append(boundary);\n    header.append(\"\\r\\n\");\n    header.append(\"Content-Disposition: form-data; name=\\\"test\\\"\\r\\n\\r\\n\");\n    MppResult result;\n    parser->data = &result;\n\n    ret = multipart_parser_execute(parser, header.value(), header.get_length());\n    ASSERT_EQ(ret, header.length);\n\n    ASSERT_STREQ(result.header_field.c_str(), \"Content-Disposition\");\n    ASSERT_TRUE(result.header_value.find(\"test\") != result.header_value.npos);\n\n    std::string boundary_str(parser->boundary, parser->boundary_length);\n    ASSERT_EQ(multipart_parser_execute(parser, SW_STRL(\"\\r\\n\")), 2);\n    ASSERT_EQ(multipart_parser_execute(parser, boundary_str.c_str(), boundary_str.length()), boundary_str.length());\n    ASSERT_EQ(multipart_parser_execute(parser, \"--\\r\\n\\r\\n\", 6), 6);\n}\n\nTEST(multipart_parser, header_error) {\n    auto parser = create_parser();\n    ssize_t ret;\n\n    // header party\n    swoole::String header(1024);\n    header.append(\"--\");\n    header.append(boundary);\n    header.append(\"\\r\\n\");\n    header.append(\"Content-Disposition: form-data; name=\\\"test\\\"\");\n    MppResult result;\n    parser->data = &result;\n\n    ret = multipart_parser_execute(parser, header.value(), header.get_length());\n    ASSERT_EQ(ret, -1);\n    ASSERT_EQ(parser->error_reason, MPPE_HEADER_VALUE_INCOMPLETE);\n    ASSERT_EQ(parser->error_expected, '\\r');\n}\n\nTEST(multipart_parser, data) {\n    auto parser = create_parser();\n    ssize_t ret;\n\n    // header party\n    swoole::String header(1024);\n    header.append(\"--\");\n    header.append(boundary);\n    header.append(\"\\r\\n\");\n    header.append(\"Content-Disposition: form-data; name=\\\"test\\\"\\r\\n\\r\\n\");\n    MppResult result;\n    parser->data = &result;\n    ret = multipart_parser_execute(parser, header.value(), header.get_length());\n    ASSERT_EQ(ret, header.length);\n\n    std::string boundary_str(parser->boundary, parser->boundary_length);\n\n    // data part\n    swoole::String data(128);\n    data.append_random_bytes(swoole_rand(60, 120), true);\n    data.append(\"\\r\");\n    data.append_random_bytes(swoole_rand(60, 120), true);\n    data.append(\"\\r\\n\");\n    data.append_random_bytes(swoole_rand(60, 120), true);\n    data.append(\"\\r\\n\");\n    data.append(boundary_str.substr(0, swoole_rand(1, parser->boundary_length - 2)));\n    data.append_random_bytes(swoole_rand(60, 120), true);\n    ASSERT_EQ(multipart_parser_execute(parser, data.value(), data.get_length()), data.get_length());\n\n    auto append_data = [&]() {\n        size_t offset = data.length;\n        data.append_random_bytes(swoole_rand(60, 120), true);\n        size_t len = data.length - offset;\n        ASSERT_EQ(multipart_parser_execute(parser, data.value() + offset, len), len);\n    };\n\n    append_data();\n    data.append(\"\\r\");\n    ASSERT_EQ(multipart_parser_execute(parser, SW_STRL(\"\\r\")), 1);\n\n    append_data();\n\n    data.append(\"\\r\\n\");\n    ASSERT_EQ(multipart_parser_execute(parser, SW_STRL(\"\\r\\n\")), 2);\n\n    append_data();\n\n    {\n        size_t offset = data.length;\n        data.append(boundary_str.substr(0, swoole_rand(1, parser->boundary_length - 2)));\n        size_t len = data.length - offset;\n        ASSERT_EQ(multipart_parser_execute(parser, data.value() + offset, len), len);\n    }\n\n    ASSERT_EQ(multipart_parser_execute(parser, SW_STRL(\"\\r\\n\")), 2);\n    ASSERT_EQ(multipart_parser_execute(parser, boundary_str.c_str(), boundary_str.length()), boundary_str.length());\n    ASSERT_EQ(multipart_parser_execute(parser, \"--\\r\\n\\r\\n\", 6), 6);\n\n    ASSERT_MEMEQ(data.value(), result.data.c_str(), result.data.length());\n\n    ASSERT_TRUE(result.header_complete);\n    ASSERT_TRUE(result.body_end);\n}\n"
  },
  {
    "path": "core-tests/src/server/port.cpp",
    "content": "/*\n+----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n\n#include \"swoole_server.h\"\n#include \"test_server.h\"\n\nusing swoole::ListenPort;\nusing swoole::Server;\n\nTEST(server_port, import) {\n    ListenPort port(nullptr);\n    ASSERT_FALSE(port.import(fileno(stdin)));\n    ASSERT_ERREQ(ENOTSOCK);\n\n    auto sock = swoole::make_socket(SW_SOCK_TCP, SW_FD_STREAM, 0);\n    ASSERT_FALSE(port.import(sock->fd));\n    ASSERT_ERREQ(EINVAL);\n    sock->free();\n}\n\nTEST(server_port, create) {\n    Server server(Server::MODE_BASE);\n    server.enable_reuse_port = true;\n    auto port = server.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_NE(nullptr, port);\n    ASSERT_EQ(SW_OK, port->create_socket());\n\n    port->open_eof_check = true;\n    port->protocol.package_eof_len = SW_DATA_EOF_MAXLEN + 10;\n    port->init_protocol();\n    ASSERT_STREQ(\"eof\", port->get_protocols());\n    ASSERT_EQ(port->protocol.package_eof_len, SW_DATA_EOF_MAXLEN);\n\n    ASSERT_TRUE(port->ssl_context_init());\n    ASSERT_FALSE(port->ssl_context_create(port->ssl_context.get()));\n    ASSERT_ERREQ(SW_ERROR_WRONG_OPERATION);\n}\n\nTEST(server_port, dgram) {\n    Server server(Server::MODE_BASE);\n    server.enable_reuse_port = true;\n    auto port = server.add_port(SW_SOCK_UDP, TEST_HOST, 0);\n    ASSERT_NE(nullptr, port);\n    ASSERT_STREQ(\"dgram\", port->get_protocols());\n}\n"
  },
  {
    "path": "core-tests/src/server/server.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"test_core.h\"\n\n#include \"swoole_server.h\"\n#include \"swoole_memory.h\"\n#include \"swoole_signal.h\"\n#include \"swoole_lock.h\"\n#include \"swoole_util.h\"\n\n#include <numeric>\n\nusing namespace std;\nusing namespace swoole;\nusing swoole::network::AsyncClient;\n\nint beforeReloadPid = 0;\n\nTEST(server, schedule) {\n    int ret;\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 6;\n    serv.dispatch_mode = Server::DISPATCH_IDLE_WORKER;\n    serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n\n    ret = serv.create();\n    ASSERT_EQ(SW_OK, ret);\n\n    for (uint32_t i = 0; i < serv.worker_num; i++) {\n        serv.workers[i].set_status_to_busy();\n    }\n\n    std::set<int> _worker_id_set;\n\n    for (uint32_t i = 0; i < serv.worker_num; i++) {\n        auto worker_id = serv.schedule_worker(i * 13, nullptr);\n        _worker_id_set.insert(worker_id);\n    }\n    ASSERT_EQ(_worker_id_set.size(), serv.worker_num);\n\n    for (uint32_t i = 1; i < serv.worker_num - 1; i++) {\n        serv.workers[i].set_status_to_idle();\n    }\n\n    _worker_id_set.clear();\n    for (uint32_t i = 0; i < serv.worker_num; i++) {\n        auto worker_id = serv.schedule_worker(i * 13, nullptr);\n        _worker_id_set.insert(worker_id);\n    }\n    ASSERT_EQ(_worker_id_set.size(), serv.worker_num - 2);\n\n    serv.destroy();\n}\n\nTEST(server, schedule_1) {\n    int ret;\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 8;\n    serv.dispatch_mode = Server::DISPATCH_ROUND;\n    serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n\n    ret = serv.create();\n    ASSERT_EQ(SW_OK, ret);\n\n    std::vector<size_t> counter;\n    size_t schedule_count = 1024;\n\n    counter.resize(serv.worker_num);\n    SW_LOOP_N(schedule_count) {\n        auto worker_id = serv.schedule_worker(i * 13, nullptr);\n        counter[worker_id]++;\n    }\n\n    SW_LOOP_N(serv.worker_num) {\n        ASSERT_EQ(counter[i], schedule_count / serv.worker_num);\n    }\n}\n\ndouble average_combined(const std::vector<size_t> &v1, const std::vector<size_t> &v2) {\n    size_t total_size = v1.size() + v2.size();\n    if (total_size == 0) return 0.0;\n    size_t sum1 = std::accumulate(v1.begin(), v1.end(), size_t{0});\n    size_t sum2 = std::accumulate(v2.begin(), v2.end(), size_t{0});\n    return static_cast<double>(sum1 + sum2) / total_size;\n}\n\ntemplate <typename T, typename M, M T::*member>\nstatic void test_worker_schedule(int dispatch_mode) {\n    int ret;\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 8;\n    serv.dispatch_mode = dispatch_mode;\n    auto port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n\n    ret = serv.create();\n    ASSERT_EQ(SW_OK, ret);\n\n    std::vector<size_t> counter;\n    counter.resize(serv.worker_num);\n\n    size_t schedule_count = 256 * serv.worker_num;\n\n    std::vector<size_t> init_counter;\n    init_counter.resize(serv.worker_num);\n\n    SW_LOOP_N(serv.worker_num) {\n        T &worker = serv.workers[i];\n        init_counter[i] = worker.*member = swoole_rand(32, 512);\n    }\n\n    network::Socket fake_sock{};\n    fake_sock.fd = 199;\n    serv.add_connection(port, &fake_sock, port->get_fd());\n\n    SW_LOOP_N(schedule_count) {\n        auto worker_id = serv.schedule_worker(fake_sock.fd, nullptr);\n        counter[worker_id]++;\n        T &worker = serv.workers[worker_id];\n        (worker.*member)++;\n    }\n\n    auto avg_elem = average_combined(init_counter, counter);\n    SW_LOOP_N(serv.worker_num) {\n        ASSERT_GE(counter[i] + init_counter[i], (int) avg_elem * 2 - 10);\n    }\n}\n\nTEST(server, schedule_4) {\n    int ret;\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 4;\n    serv.dispatch_mode = Server::DISPATCH_IPMOD;\n\n    auto port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_NE(port, nullptr);\n\n    auto port6 = serv.add_port(SW_SOCK_TCP6, \"::\", 0);\n    ASSERT_NE(port6, nullptr);\n\n    ret = serv.create();\n    ASSERT_EQ(SW_OK, ret);\n\n    std::vector<size_t> counter;\n    counter.resize(serv.worker_num);\n\n    size_t schedule_count = 256 * serv.worker_num;\n\n    std::vector<size_t> init_counter;\n    init_counter.resize(serv.worker_num);\n\n    network::Socket fake_sock{};\n    fake_sock.fd = 100;\n    fake_sock.info.assign(SW_SOCK_TCP, \"127.0.0.1\", 9501, false);\n    serv.add_connection(port, &fake_sock, port->get_fd());\n\n    SW_LOOP_N(schedule_count) {\n        auto worker_id = serv.schedule_worker(fake_sock.fd, nullptr);\n        counter[worker_id]++;\n    }\n\n    network::Socket fake_sock6{};\n    fake_sock6.fd = 101;\n    fake_sock6.info.assign(SW_SOCK_TCP6, \"::1\", 9502, false);\n    serv.add_connection(port6, &fake_sock6, port6->get_fd());\n\n    SW_LOOP_N(schedule_count) {\n        auto worker_id = serv.schedule_worker(fake_sock6.fd, nullptr);\n        counter[worker_id]++;\n    }\n\n    SendData sdata;\n    auto pkt = reinterpret_cast<DgramPacket *>(sw_tg_buffer()->str);\n    pkt->socket_addr.assign(SW_SOCK_UDP, \"192.168.1.103\", 29321, false);\n    sdata.data = (char *) pkt;\n    auto worker_id = serv.schedule_worker(9999, &sdata);\n    counter[worker_id]++;\n\n    ASSERT_EQ(counter[0], 0);\n    ASSERT_EQ(counter[1], schedule_count + 1);\n    ASSERT_EQ(counter[2], 0);\n    ASSERT_EQ(counter[3], schedule_count);\n}\n\nTEST(server, schedule_5) {\n    int ret;\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 4;\n    serv.dispatch_mode = Server::DISPATCH_UIDMOD;\n\n    auto port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_NE(port, nullptr);\n\n    auto port6 = serv.add_port(SW_SOCK_TCP6, \"::\", 0);\n    ASSERT_NE(port6, nullptr);\n\n    ret = serv.create();\n    ASSERT_EQ(SW_OK, ret);\n\n    std::vector<size_t> counter;\n    counter.resize(serv.worker_num);\n\n    size_t schedule_count = 256 * serv.worker_num;\n\n    std::vector<size_t> init_counter;\n    init_counter.resize(serv.worker_num);\n\n    network::Socket fake_sock{};\n    fake_sock.fd = 100;\n    fake_sock.info.assign(SW_SOCK_TCP, \"127.0.0.1\", 9501, false);\n    auto conn = serv.add_connection(port, &fake_sock, port->get_fd());\n    conn->uid = 0;\n\n    SW_LOOP_N(schedule_count) {\n        auto worker_id = serv.schedule_worker(fake_sock.fd, nullptr);\n        counter[worker_id]++;\n    }\n\n    network::Socket fake_sock6{};\n    fake_sock6.fd = 101;\n\n    fake_sock6.info.assign(SW_SOCK_TCP6, \"::1\", 9502, false);\n    auto conn6 = serv.add_connection(port6, &fake_sock6, port6->get_fd());\n    conn6->uid = 839922;\n\n    SW_LOOP_N(schedule_count) {\n        auto worker_id = serv.schedule_worker(fake_sock6.fd, nullptr);\n        counter[worker_id]++;\n    }\n\n    ASSERT_EQ(counter[0], schedule_count);\n    ASSERT_EQ(counter[1], 0);\n    ASSERT_EQ(counter[2], schedule_count);\n    ASSERT_EQ(counter[3], 0);\n}\n\nTEST(server, schedule_8) {\n    int ret;\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 4;\n    serv.dispatch_mode = Server::DISPATCH_CO_CONN_LB;\n\n    auto port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_NE(port, nullptr);\n\n    auto port6 = serv.add_port(SW_SOCK_TCP6, \"::\", 0);\n    ASSERT_NE(port6, nullptr);\n\n    ret = serv.create();\n    ASSERT_EQ(SW_OK, ret);\n\n    std::vector<size_t> counter;\n    counter.resize(serv.worker_num);\n\n    size_t schedule_count = 256 * serv.worker_num;\n\n    std::vector<size_t> init_counter;\n    init_counter.resize(serv.worker_num);\n\n    network::Socket fake_sock{};\n    fake_sock.fd = 100;\n    fake_sock.info.assign(SW_SOCK_TCP, \"127.0.0.1\", 9501, false);\n    auto conn = serv.add_connection(port, &fake_sock, port->get_fd());\n    conn->worker_id = 1;\n\n    SW_LOOP_N(schedule_count) {\n        auto worker_id = serv.schedule_worker(fake_sock.fd, nullptr);\n        counter[worker_id]++;\n    }\n\n    network::Socket fake_sock6{};\n    fake_sock6.fd = 101;\n\n    fake_sock6.info.assign(SW_SOCK_TCP6, \"::1\", 9502, false);\n    serv.add_connection(port6, &fake_sock6, port6->get_fd());\n\n    SW_LOOP_N(schedule_count) {\n        auto worker_id = serv.schedule_worker(fake_sock6.fd, nullptr);\n        counter[worker_id]++;\n    }\n\n    auto worker_id = serv.schedule_worker(9999, nullptr);\n    counter[worker_id]++;\n\n    ASSERT_EQ(counter[0], schedule_count + 1);\n    ASSERT_EQ(counter[1], schedule_count);\n    ASSERT_EQ(counter[2], 0);\n    ASSERT_EQ(counter[3], 0);\n}\n\nTEST(server, schedule_9) {\n    test_worker_schedule<Worker, size_t, &Worker::coroutine_num>(Server::DISPATCH_CO_REQ_LB);\n}\n\nTEST(server, schedule_10) {\n    test_worker_schedule<Worker, uint32_t, &Worker::concurrency>(Server::DISPATCH_CONCURRENT_LB);\n}\n\nstatic const char *packet = \"hello world\\n\";\n\nstatic void test_base() {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n    serv.pid_file = \"/tmp/swoole-core-tests.pid\";\n\n    test::counter_init();\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n\n    serv.add_hook(\n        Server::HOOK_WORKER_START,\n        [](void *ptr) {\n            void **args = (void **) ptr;\n            Server *serv = (Server *) args[0];\n            ASSERT_TRUE(serv->is_started());\n        },\n        false);\n\n    mutex lock;\n    lock.lock();\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    swoole_clear_last_error();\n    ASSERT_FALSE(serv.shutdown());\n    ASSERT_ERREQ(SW_ERROR_WRONG_OPERATION);\n\n    std::thread t1([&]() {\n        swoole_signal_block_all();\n\n        lock.lock();\n\n        network::SyncClient c(SW_SOCK_TCP);\n        c.connect(TEST_HOST, port->port);\n        c.send(packet, strlen(packet));\n        char buf[1024];\n        c.recv(buf, sizeof(buf));\n        c.close();\n\n        kill(getpid(), SIGTERM);\n    });\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        EXPECT_EQ(string(req->data, req->info.len), string(packet));\n        auto conn = serv->get_connection_by_session_id(req->session_id());\n        EXPECT_NE(strcmp(serv->get_local_addr(conn), \"unknown\"), 0);\n\n        string resp = string(\"Server: \") + string(packet);\n        serv->send(req->info.fd, resp.c_str(), resp.length());\n\n        EXPECT_FALSE(serv->finish(resp.c_str(), resp.length()));\n\n        EXPECT_EQ(serv->get_connection_num(), 1);\n        EXPECT_EQ(serv->get_primary_port()->get_connection_num(), 1);\n\n        EXPECT_EQ(serv->get_worker_message_bus()->move_packet(), nullptr);\n\n        // session not exists\n        SessionId client_fd = 9999;\n        swoole_clear_last_error();\n        EXPECT_FALSE(serv->send(client_fd, resp.c_str(), resp.length()));\n        EXPECT_ERREQ(SW_ERROR_SESSION_NOT_EXIST);\n\n        swoole_clear_last_error();\n        EXPECT_FALSE(serv->close(client_fd));\n        EXPECT_ERREQ(SW_ERROR_SESSION_NOT_EXIST);\n\n        swoole_clear_last_error();\n        SendData sd{};\n        sd.info.fd = client_fd;\n        sd.info.type = SW_SERVER_EVENT_CLOSE;\n        EXPECT_EQ(serv->send_to_connection(&sd), SW_ERR);\n        EXPECT_ERREQ(SW_ERROR_SESSION_NOT_EXIST);\n\n        return SW_OK;\n    };\n\n    serv.onStart = [](Server *serv) { ASSERT_EQ(access(serv->pid_file.c_str(), R_OK), 0); };\n\n    serv.onBeforeShutdown = [](Server *serv) {\n        beforeReloadPid = serv->gs->master_pid;\n        test::counter_incr(10);\n        DEBUG() << \"onBeforeShutdown: master_pid=\" << beforeReloadPid << \"\\n\";\n    };\n\n    serv.start();\n    t1.join();\n\n    ASSERT_EQ(access(serv.pid_file.c_str(), R_OK), -1);\n    ASSERT_EQ(test::counter_get(10), 1);  // onBeforeShutdown called\n}\n\nTEST(server, base) {\n    test_base();\n}\n\nstatic void test_process(bool single_thread = false) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 1;\n    serv.single_thread = single_thread;\n    serv.task_worker_num = 3;\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    test::counter_init();\n    auto counter = test::counter_ptr();\n\n    Mutex *lock = new Mutex(true);\n    lock->lock();\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    swoole_clear_last_error();\n    ASSERT_EQ(serv.add_port(SW_SOCK_TCP, TEST_HOST, 0), nullptr);\n    ASSERT_ERREQ(SW_ERROR_WRONG_OPERATION);\n\n    swoole_clear_last_error();\n    Worker fake_worker{};\n    ASSERT_EQ(serv.add_worker(&fake_worker), SW_ERR);\n    ASSERT_ERREQ(SW_ERROR_WRONG_OPERATION);\n\n    thread t1;\n    serv.onStart = [&lock, &t1](Server *serv) {\n        t1 = thread([=]() {\n            swoole_signal_block_all();\n\n            lock->lock();\n\n            kill(serv->get_worker(0)->pid, SIGRTMIN);\n\n            ListenPort *port = serv->get_primary_port();\n\n            network::SyncClient c(SW_SOCK_TCP);\n            c.connect(TEST_HOST, port->port);\n            c.send(packet, strlen(packet));\n            char buf[1024];\n            c.recv(buf, sizeof(buf));\n            c.close();\n\n            sleep(2);\n\n            kill(serv->gs->master_pid, SIGTERM);\n        });\n\n        // command tests\n        swoole_clear_last_error();\n        serv->call_command_callback(9999, TEST_STR);\n        ASSERT_ERREQ(SW_ERROR_SERVER_INVALID_COMMAND);\n\n        swoole_clear_last_error();\n        serv->call_command_handler_in_master(9999, TEST_STR);\n        ASSERT_ERREQ(SW_ERROR_SERVER_INVALID_COMMAND);\n    };\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) {\n        if (worker->id == 0) {\n            lock->unlock();\n        }\n        test::counter_incr(3);\n        DEBUG() << \"onWorkerStart: id=\" << worker->id << \"\\n\";\n    };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        EXPECT_EQ(string(req->data, req->info.len), string(packet));\n\n        string resp = string(\"Server: \") + string(packet);\n        serv->send(req->info.fd, resp.c_str(), resp.length());\n\n        EXPECT_EQ(serv->get_connection_num(), 1);\n        EXPECT_EQ(serv->get_primary_port()->get_connection_num(), 1);\n\n        swoole_timer_after(100, [serv](TIMER_PARAMS) { serv->kill_worker(1 + swoole_random_int() % 3); });\n\n        return SW_OK;\n    };\n\n    serv.onTask = [](Server *serv, EventData *task) -> int { return 0; };\n\n    serv.manager_alarm = 1;\n\n    serv.add_hook(\n        Server::HOOK_MANAGER_TIMER,\n        [](void *args) {\n            test::counter_incr(2);\n            DEBUG() << \"manager timer callback\\n\";\n        },\n        true);\n\n    serv.onManagerStart = [](Server *serv) {\n        DEBUG() << \"onManagerStart\\n\";\n        test::counter_incr(1);\n    };\n\n    serv.onManagerStop = [](Server *serv) {\n        DEBUG() << \"onManagerStop\\n\";\n        test::counter_incr(1);\n    };\n\n    serv.onBeforeShutdown = [](Server *serv) {\n        beforeReloadPid = serv->gs->master_pid;\n        test::counter_incr(10);\n        DEBUG() << \"onBeforeShutdown: master_pid=\" << beforeReloadPid << \"\\n\";\n    };\n\n    serv.onShutdown = [](Server *serv) {\n        DEBUG() << \"onShutdown\\n\";\n        test::counter_incr(9);\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n\n    t1.join();\n    delete lock;\n    ASSERT_EQ(counter[1], 2);             // manager callback\n    ASSERT_GE(counter[2], 2);             // manager timer\n    ASSERT_GE(counter[3], 4);             // worker start\n    ASSERT_EQ(test::counter_get(9), 1);   // onShutdown called\n    ASSERT_EQ(test::counter_get(10), 1);  // onBeforeShutdown called\n}\n\nTEST(server, process) {\n    test_process();\n    test::wait_all_child_processes();\n}\n\nTEST(server, process_single_thread) {\n    test_process(true);\n    test::wait_all_child_processes();\n}\n\nstatic void test_process_send_in_user_worker() {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 2;\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    test::counter_init();\n    auto counter = test::counter_ptr();\n\n    Mutex lock(true);\n    lock.lock();\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_NE(port, nullptr);\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onUserWorkerStart = [&lock, port](Server *serv, Worker *worker) {\n        lock.lock();\n        DEBUG() << \"onUserWorkerStart: id=\" << worker->id << \"\\n\";\n        sleep(1);\n        serv->shutdown();\n    };\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) {\n        if (worker->id == 0) {\n            lock.unlock();\n        }\n        test::counter_incr(3);\n        DEBUG() << \"onWorkerStart: id=\" << worker->id << \"\\n\";\n    };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        EXPECT_EQ(string(req->data, req->info.len), string(packet));\n\n        string resp = string(\"Server: \") + string(packet);\n        serv->send(req->info.fd, resp.c_str(), resp.length());\n\n        EXPECT_EQ(serv->get_connection_num(), 1);\n        EXPECT_EQ(serv->get_primary_port()->get_connection_num(), 1);\n\n        swoole_timer_after(100, [serv](TIMER_PARAMS) { serv->kill_worker(1 + swoole_random_int() % 3); });\n\n        return SW_OK;\n    };\n\n    serv.onShutdown = [](Server *serv) {\n        DEBUG() << \"onShutdown\\n\";\n        test::counter_incr(9);\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n\n    ASSERT_EQ(counter[1], 2);             // manager callback\n    ASSERT_GE(counter[2], 2);             // manager timer\n    ASSERT_GE(counter[3], 4);             // worker start\n    ASSERT_EQ(test::counter_get(9), 1);   // onShutdown called\n    ASSERT_EQ(test::counter_get(10), 1);  // onBeforeShutdown called\n}\n\n// TEST(server, process_send_in_user_worker) {\n//     test_process_send_in_user_worker();\n//     test::wait_all_child_processes();\n// }\n\n#ifdef SW_THREAD\nTEST(server, thread) {\n    Server serv(Server::MODE_THREAD);\n    serv.worker_num = 2;\n\n    swoole_set_trace_flags(SW_TRACE_THREAD);\n    swoole_set_log_level(SW_LOG_TRACE);\n    test::counter_init();\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n\n    mutex lock;\n    lock.lock();\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    std::thread t1([&]() {\n        swoole_signal_block_all();\n\n        lock.lock();\n\n        usleep(1000);\n\n        network::SyncClient c(SW_SOCK_TCP);\n        ASSERT_TRUE(c.connect(TEST_HOST, port->port));\n        ASSERT_EQ(c.send(packet, strlen(packet)), strlen(packet));\n        char buf[1024];\n        ASSERT_EQ(c.recv(buf, sizeof(buf)), strlen(packet) + 8);\n        string resp = string(\"Server: \") + string(packet);\n        ASSERT_MEMEQ(buf, resp.c_str(), resp.length());\n        c.close();\n\n        usleep(1000);\n\n        ASSERT_FALSE(serv.get_event_worker_pool()->read_message);\n        kill(serv.get_master_pid(), SIGIO);\n        usleep(1000);\n        ASSERT_TRUE(serv.get_event_worker_pool()->read_message);\n\n        DEBUG() << \"shutdown\\n\";\n\n        serv.shutdown();\n    });\n\n    serv.onStart = [&lock](Server *serv) {\n        DEBUG() << \"onStart\\n\";\n        lock.unlock();\n    };\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) {\n        DEBUG() << \"onWorkerStart: id=\" << worker->id << \"\\n\";\n        serv->send_pipe_message(1 - worker->id, SW_STRL(TEST_STR));\n    };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        EXPECT_EQ(string(req->data, req->info.len), string(packet));\n\n        string resp = string(\"Server: \") + string(packet);\n        serv->send(req->info.fd, resp.c_str(), resp.length());\n\n        DEBUG() << \"send\\n\";\n\n        EXPECT_EQ(serv->get_connection_num(), 1);\n        EXPECT_EQ(serv->get_primary_port()->get_connection_num(), 1);\n\n        return SW_OK;\n    };\n\n    serv.onPipeMessage = [](Server *serv, EventData *req) {\n        DEBUG() << \"onPipeMessage: \" << string(req->data, req->info.len) << \"\\n\";\n        test::counter_incr(4);\n    };\n\n    ASSERT_EQ(serv.start(), SW_OK);\n    t1.join();\n\n    test::wait_all_child_processes();\n    ASSERT_EQ(test::counter_get(4), 2);  // onPipeMessage called\n}\n\n#ifndef SW_USE_ASAN\nTEST(server, task_thread) {\n    DEBUG() << \"new server\\n\";\n    Server serv(Server::MODE_THREAD);\n    serv.worker_num = 2;\n    serv.task_worker_num = 2;\n\n    swoole_set_log_level(SW_LOG_INFO);\n\n    DEBUG() << \"add port\\n\";\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n\n    mutex lock{};\n    lock.lock();\n\n    DEBUG() << \"create server\\n\";\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    std::thread t1([&]() {\n        swoole_signal_block_all();\n\n        lock.lock();\n\n        network::SyncClient c(SW_SOCK_TCP);\n        ASSERT_TRUE(c.connect(TEST_HOST, port->port));\n        c.send(packet, strlen(packet));\n        char buf[1024];\n        c.recv(buf, sizeof(buf));\n        c.close();\n\n        usleep(100000);\n        serv.shutdown();\n    });\n\n    std::atomic<int> count(0);\n\n    serv.onStart = [&lock](Server *serv) {\n        DEBUG() << \"onStart\\n\";\n        lock.unlock();\n    };\n\n    serv.onWorkerStart = [&lock, &count](Server *serv, Worker *worker) {\n        ++count;\n        DEBUG() << \"onWorkerStart: id=\" << worker->id << \"\\n\";\n    };\n\n    serv.onFinish = [](Server *serv, EventData *task) -> int {\n        SessionId client_fd;\n        memcpy(&client_fd, task->data, sizeof(client_fd));\n        string resp = string(\"Server: \") + string(packet);\n        EXPECT_TRUE(serv->send(client_fd, resp.c_str(), resp.length()));\n        return 0;\n    };\n\n    serv.onTask = [](Server *serv, EventData *task) -> int {\n        EXPECT_TRUE(serv->finish(task->data, task->info.len, 0, task));\n        return 0;\n    };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        EXPECT_EQ(string(req->data, req->info.len), string(packet));\n\n        EventData msg;\n        SessionId client_fd = req->info.fd;\n        Server::task_pack(&msg, &client_fd, sizeof(client_fd));\n        msg.info.ext_flags |= SW_TASK_NONBLOCK;\n\n        int dst_worker_id = -1;\n        EXPECT_TRUE(serv->task(&msg, &dst_worker_id));\n\n        return SW_OK;\n    };\n\n    DEBUG() << \"start server\\n\";\n    ASSERT_EQ(serv.start(), SW_OK);\n    t1.join();\n\n    ASSERT_EQ(count.load(), serv.get_core_worker_num());\n    test::wait_all_child_processes();\n}\n\nTEST(server, reload_thread) {\n    DEBUG() << \"new server\\n\";\n    Server serv(Server::MODE_THREAD);\n    serv.worker_num = 2;\n    serv.task_worker_num = 2;\n\n    swoole_set_trace_flags(SW_TRACE_ALL);\n    swoole_set_log_level(SW_LOG_TRACE);\n\n    DEBUG() << \"add port\\n\";\n    ASSERT_NE(serv.add_port(SW_SOCK_TCP, TEST_HOST, 0), nullptr);\n\n    Worker user_worker{};\n    ASSERT_NE(serv.add_worker(&user_worker), SW_ERR);\n\n    mutex lock{};\n    lock.lock();\n\n    DEBUG() << \"create server\\n\";\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    std::thread t1([&]() {\n        swoole_thread_init();\n        lock.lock();\n        usleep(10000);\n        EXPECT_TRUE(serv.reload(true));\n        EXPECT_FALSE(serv.reload(true));  // reload again should fail\n        EXPECT_ERREQ(SW_ERROR_OPERATION_NOT_SUPPORT);\n\n        DEBUG() << \"before shutdown, sleep 1s\\n\";\n        sleep(1);\n        DEBUG() << \"shutdown\\n\";\n        EXPECT_TRUE(serv.shutdown());\n        swoole_thread_clean();\n    });\n\n    std::atomic<size_t> count(0);\n\n    serv.onUserWorkerStart = [&lock, &count](Server *serv, Worker *worker) {\n        DEBUG() << \"onUserWorkerStart: id=\" << worker->id << \"\\n\";\n        while (serv->running) {\n            usleep(100000);\n        }\n    };\n\n    serv.onStart = [&lock](Server *serv) { DEBUG() << \"onStart\\n\"; };\n\n    serv.onManagerStart = [&lock](Server *serv) {\n        DEBUG() << \"onManagerStart\\n\";\n        lock.unlock();\n    };\n\n    serv.onBeforeReload = [](Server *serv) {\n        DEBUG() << \"onBeforeReload: master_pid=\" << serv->get_manager_pid() << \"\\n\";\n    };\n\n    serv.onAfterReload = [](Server *serv) {\n        DEBUG() << \"onAfterReload: master_pid=\" << serv->get_manager_pid() << \"\\n\";\n    };\n\n    serv.onWorkerStart = [&count](Server *serv, Worker *worker) {\n        ++count;\n        DEBUG() << \"onWorkerStart: id=\" << worker->id << \"\\n\";\n    };\n\n    serv.onWorkerStop = [](Server *serv, Worker *worker) { DEBUG() << \"onWorkerStop: id=\" << worker->id << \"\\n\"; };\n\n    serv.onTask = [](Server *serv, EventData *task) -> int { return 0; };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int { return SW_OK; };\n\n    DEBUG() << \"start server\\n\";\n    ASSERT_EQ(serv.start(), SW_OK);\n    t1.join();\n    ASSERT_EQ(count.load(), serv.get_core_worker_num() * 2);\n    test::wait_all_child_processes();\n}\n\nTEST(server, reload_thread_2) {\n    Server serv(Server::MODE_THREAD);\n    serv.worker_num = 2;\n    serv.task_worker_num = 2;\n\n    test::counter_init();\n\n    std::unordered_map<std::string, bool> flags;\n    swoole_set_log_level(SW_LOG_INFO);\n\n    ASSERT_NE(serv.add_port(SW_SOCK_TCP, TEST_HOST, 0), nullptr);\n\n    Worker user_worker{};\n\n    ASSERT_EQ(serv.add_worker(&user_worker), SW_OK);\n\n    mutex lock;\n    lock.lock();\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    std::atomic<size_t> count(0);\n\n    serv.onUserWorkerStart = [](Server *serv, Worker *worker) {\n        usleep(500000);\n        test::counter_incr(4, 1);\n        DEBUG() << \"onUserWorkerStart: id=\" << worker->id << \"\\n\";\n    };\n\n    serv.onWorkerStart = [&lock, &count](Server *serv, Worker *worker) {\n        if (++count == serv->get_core_worker_num()) {\n            lock.unlock();\n        }\n    };\n\n    serv.onTask = [](Server *serv, EventData *task) -> int { return 0; };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int { return SW_OK; };\n\n    serv.onBeforeReload = [&flags](Server *serv) { flags[\"onBeforeReload\"] = true; };\n\n    serv.onAfterReload = [&flags](Server *serv) {\n        flags[\"onAfterReload\"] = true;\n        swoole_timer_after(500, [serv, &flags](auto r1, auto r2) {\n            flags[\"shutdown\"] = true;\n            serv->shutdown();\n        });\n    };\n\n    serv.onManagerStart = [&flags](Server *serv) {\n        swoole_timer_after(500, [serv, &flags](auto r1, auto r2) {\n            flags[\"reload\"] = true;\n            EXPECT_TRUE(serv->reload(true));\n        });\n    };\n\n    serv.onManagerStop = [&flags](Server *serv) { flags[\"onManagerStop\"] = true; };\n\n    ASSERT_EQ(serv.start(), SW_OK);\n\n    ASSERT_TRUE(flags[\"onBeforeReload\"]);\n    ASSERT_TRUE(flags[\"onAfterReload\"]);\n    ASSERT_TRUE(flags[\"onManagerStop\"]);\n    ASSERT_TRUE(flags[\"reload\"]);\n    ASSERT_TRUE(flags[\"shutdown\"]);\n    ASSERT_GE(test::counter_get(4), 2);\n\n    test::wait_all_child_processes();\n}\n\nTEST(server, reload_thread_3) {\n    Server serv(Server::MODE_THREAD);\n    serv.worker_num = 2;\n\n    std::unordered_map<std::string, bool> flags;\n    swoole_set_log_level(SW_LOG_INFO);\n\n    ASSERT_NE(serv.add_port(SW_SOCK_TCP, TEST_HOST, 0), nullptr);\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    std::atomic<size_t> count(0);\n\n    serv.onWorkerStart = [&count](Server *serv, Worker *worker) { ++count; };\n    serv.onReceive = [](Server *serv, RecvData *req) -> int { return SW_OK; };\n    serv.onAfterReload = [&flags](Server *serv) {\n        flags[\"onAfterReload\"] = true;\n        EXPECT_FALSE(serv->reload(false));\n        EXPECT_ERREQ(SW_ERROR_OPERATION_NOT_SUPPORT);\n        swoole_timer_after(500, [serv, &flags](auto r1, auto r2) {\n            flags[\"shutdown\"] = true;\n            serv->shutdown();\n        });\n    };\n\n    serv.onManagerStart = [&flags](Server *serv) {\n        swoole_timer_after(500, [serv, &flags](auto r1, auto r2) {\n            flags[\"reload\"] = true;\n            EXPECT_TRUE(serv->reload(true));\n        });\n    };\n\n    serv.onManagerStop = [&flags](Server *serv) { flags[\"onManagerStop\"] = true; };\n\n    ASSERT_EQ(serv.start(), SW_OK);\n\n    ASSERT_TRUE(flags[\"onAfterReload\"]);\n    ASSERT_TRUE(flags[\"onManagerStop\"]);\n    ASSERT_TRUE(flags[\"reload\"]);\n    ASSERT_TRUE(flags[\"shutdown\"]);\n\n    ASSERT_GE(count, serv.worker_num * 2);\n\n    test::wait_all_child_processes();\n}\n#endif\n#endif\n\nTEST(server, reload_all_workers) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 2;\n    serv.task_worker_num = 2;\n    serv.max_wait_time = 1;\n    serv.task_enable_coroutine = true;\n\n    test::counter_init();\n\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    serv.onTask = [](Server *serv, EventData *task) -> int { return 0; };\n    serv.onReceive = [](Server *serv, RecvData *data) -> int { return 0; };\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onBeforeReload = [](Server *serv) {\n        test::counter_incr(10);\n        DEBUG() << \"onBeforeReload: master_pid=\" << beforeReloadPid << \"\\n\";\n    };\n\n    serv.onAfterReload = [](Server *serv) {\n        DEBUG() << \"onAfterReload: master_pid=\" << beforeReloadPid << \"\\n\";\n        test::counter_incr(11);\n    };\n\n    serv.onWorkerStart = [&](Server *serv, Worker *worker) {\n        std::string filename = \"/tmp/worker_1.pid\";\n        if (worker->id == 1) {\n            if (access(filename.c_str(), R_OK) == -1) {\n                ofstream file(filename);\n                file << getpid();\n                file.close();\n                kill(serv->gs->manager_pid, SIGUSR2);\n                sleep(1);\n                kill(serv->gs->manager_pid, SIGUSR1);\n            } else {\n                char buf[10] = {0};\n                ifstream file(filename.c_str());\n                file >> buf;\n                file.close();\n\n                int oldPid = 0;\n                stringstream stringPid(buf);\n                stringPid >> oldPid;\n\n                EXPECT_TRUE(oldPid != getpid());\n\n                sleep(1);\n                remove(filename.c_str());\n                kill(serv->gs->master_pid, SIGTERM);\n            }\n        }\n\n        DEBUG() << \"onWorkerStart: id=\" << worker->id << \"\\n\";\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n    ASSERT_EQ(test::counter_get(10), 2);  // onBeforeReload called\n    ASSERT_EQ(test::counter_get(11), 2);  // onAfterReload called\n}\n\nTEST(server, reload_all_workers2) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 2;\n    serv.task_worker_num = 2;\n    serv.max_wait_time = 1;\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    test::counter_init();\n    serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    serv.onTask = [](Server *serv, EventData *task) -> int { return 0; };\n    serv.onReceive = [](Server *serv, RecvData *data) -> int { return 0; };\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onWorkerStart = [&](Server *serv, Worker *worker) {\n        std::string filename = \"/tmp/worker_2.pid\";\n        if (worker->id == 1) {\n            if (access(filename.c_str(), R_OK) == -1) {\n                ofstream file(filename);\n                file << getpid();\n                file.close();\n                kill(serv->gs->master_pid, SIGUSR2);\n                sleep(1);\n                kill(serv->gs->master_pid, SIGUSR1);\n            } else {\n                char buf[10] = {0};\n                ifstream file(filename.c_str());\n                file >> buf;\n                file.close();\n\n                int oldPid = 0;\n                stringstream stringPid(buf);\n                stringPid >> oldPid;\n\n                EXPECT_TRUE(oldPid != getpid());\n\n                sleep(1);\n                remove(filename.c_str());\n                kill(serv->gs->master_pid, SIGTERM);\n            }\n        }\n\n        DEBUG() << \"onWorkerStart: id=\" << worker->id << \"\\n\";\n    };\n\n    serv.onBeforeReload = [](Server *serv) {\n        test::counter_incr(10);\n        DEBUG() << \"onBeforeReload: master_pid=\" << beforeReloadPid << \"\\n\";\n    };\n\n    serv.onAfterReload = [](Server *serv) {\n        DEBUG() << \"onAfterReload: master_pid=\" << beforeReloadPid << \"\\n\";\n        test::counter_incr(11);\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n    ASSERT_EQ(test::counter_get(10), 2);  // onBeforeReload called\n    ASSERT_EQ(test::counter_get(11), 2);  // onAfterReload called\n}\n\nTEST(server, kill_user_workers) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n    serv.task_worker_num = 2;\n    serv.max_wait_time = 1;\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    auto *worker1 = new Worker();\n    auto *worker2 = new Worker();\n    ASSERT_EQ(serv.add_worker(worker1), worker1->id);\n    ASSERT_EQ(serv.add_worker(worker2), worker2->id);\n    ASSERT_TRUE(serv.add_port(SW_SOCK_TCP, TEST_HOST, 0));\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onUserWorkerStart = [&](Server *serv, Worker *worker) {\n        EXPECT_GT(worker->id, 0);\n        while (true) {\n            sleep(1);\n        }\n    };\n\n    serv.onTask = [](Server *serv, EventData *task) -> int {\n        while (true) {\n            sleep(1);\n        }\n        return 0;\n    };\n\n    serv.onWorkerStart = [&](Server *serv, Worker *worker) {\n        if (worker->id == 1) {\n            sleep(1);\n            kill(serv->get_manager_pid(), SIGTERM);\n        }\n    };\n\n    serv.onReceive = [](Server *serv, RecvData *data) -> int { return 0; };\n\n    ASSERT_EQ(serv.start(), 0);\n    delete worker1;\n    delete worker2;\n}\n\nTEST(server, force_kill_all_workers) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 2;\n    serv.task_worker_num = 3;\n    serv.max_wait_time = 1;\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    auto *worker1 = new Worker();\n    auto *worker2 = new Worker();\n    ASSERT_EQ(serv.add_worker(worker1), worker1->id);\n    ASSERT_EQ(serv.add_worker(worker2), worker2->id);\n    ASSERT_TRUE(serv.add_port(SW_SOCK_TCP, TEST_HOST, 0));\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onUserWorkerStart = [&](Server *serv, Worker *worker) {\n        test::counter_incr(1);\n        DEBUG() << \"onUserWorkerStart: id=\" << worker->id << \"\\n\";\n        while (true) {\n            sleep(1);\n        }\n    };\n\n    serv.onTask = [](Server *serv, EventData *task) -> int { return 0; };\n\n    serv.onWorkerStart = [&](Server *serv, Worker *worker) {\n        test::counter_incr(1);\n        DEBUG() << \"onWorkerStart: id=\" << worker->id << \"\\n\";\n        if (serv->is_task_worker()) {\n            while (true) {\n                sleep(1);\n            }\n        } else {\n            swoole_timer_tick(10000, [serv](TIMER_PARAMS) {});\n        }\n    };\n\n    serv.onReceive = [](Server *serv, RecvData *data) -> int { return 0; };\n\n    serv.onManagerStart = [](Server *serv) { swoole_timer_after(200, [serv](TIMER_PARAMS) { serv->shutdown(); }); };\n\n    ASSERT_EQ(serv.start(), 0);\n    ASSERT_EQ(test::counter_get(1), 7);\n\n    delete worker1;\n    delete worker2;\n}\n\nTEST(server, kill_user_workers1) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 1;\n    serv.task_worker_num = 2;\n    serv.max_wait_time = 1;\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    Worker *worker1 = new Worker();\n    Worker *worker2 = new Worker();\n    ASSERT_EQ(serv.add_worker(worker1), worker1->id);\n    ASSERT_EQ(serv.add_worker(worker2), worker2->id);\n\n    ASSERT_TRUE(serv.add_port(SW_SOCK_TCP, TEST_HOST, 0));\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onUserWorkerStart = [&](Server *serv, Worker *worker) { EXPECT_GT(worker->id, 0); };\n\n    serv.onTask = [](Server *serv, EventData *task) -> int {\n        while (1) {\n        }\n    };\n\n    serv.onWorkerStart = [&](Server *serv, Worker *worker) {\n        if (worker->id == 1) {\n            sleep(1);\n            kill(serv->gs->master_pid, SIGTERM);\n        }\n    };\n\n    serv.onReceive = [](Server *serv, RecvData *data) -> int { return 0; };\n\n    ASSERT_EQ(serv.start(), 0);\n}\n\nTEST(server, create_task_worker_fail) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 1;\n    serv.task_worker_num = 2;\n    serv.task_enable_coroutine = true;\n    serv.task_ipc_mode = Server::TASK_IPC_MSGQUEUE;\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    ASSERT_TRUE(serv.add_port(SW_SOCK_TCP, TEST_HOST, 0));\n    ASSERT_EQ(serv.create(), SW_ERR);\n    ASSERT_ERREQ(SW_ERROR_WRONG_OPERATION);\n}\n\nTEST(server, ssl) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 1;\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    Mutex *lock = new Mutex(true);\n    lock->lock();\n\n    ListenPort *port = serv.add_port(static_cast<enum swSocketType>(SW_SOCK_TCP | SW_SOCK_SSL), TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    port->set_ssl_cert_file(test::get_ssl_dir() + \"/server.crt\");\n    port->set_ssl_key_file(test::get_ssl_dir() + \"/server.key\");\n    port->ssl_init();\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    thread t1;\n\n    serv.onStart = [&lock, &t1](Server *serv) {\n        t1 = thread([=]() {\n            swoole_signal_block_all();\n\n            lock->lock();\n\n            ListenPort *port = serv->get_primary_port();\n\n            EXPECT_EQ(port->ssl, 1);\n\n            network::SyncClient c(SW_SOCK_TCP);\n            c.connect(TEST_HOST, port->port);\n            c.enable_ssl_encrypt();\n            c.send(packet, strlen(packet));\n            char buf[1024];\n            c.recv(buf, sizeof(buf));\n            c.close();\n\n            // bad SSL connection, send plain text packet to SSL server\n            network::SyncClient c2(SW_SOCK_TCP);\n            c2.connect(TEST_HOST, port->port);\n            c2.send(packet, strlen(packet));\n            ASSERT_EQ(c2.recv(buf, sizeof(buf)), 0);\n            c2.close();\n\n            kill(serv->gs->master_pid, SIGTERM);\n        });\n    };\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock->unlock(); };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        EXPECT_EQ(string(req->data, req->info.len), string(packet));\n\n        string resp = string(\"Server: \") + string(packet);\n        serv->send(req->info.fd, resp.c_str(), resp.length());\n\n        return SW_OK;\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n\n    t1.join();\n    delete lock;\n}\n\nTEST(server, ssl_error) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 1;\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    Mutex lock(true);\n    lock.lock();\n\n    ListenPort *port = serv.add_port(static_cast<enum swSocketType>(SW_SOCK_TCP | SW_SOCK_SSL), TEST_HOST, 0);\n    ASSERT_NE(port, nullptr);\n\n    port->set_ssl_cert_file(test::get_ssl_dir() + \"/server-not-exists.crt\");\n    port->set_ssl_key_file(test::get_ssl_dir() + \"/server-not-exists.key\");\n    ASSERT_FALSE(port->ssl_init());\n    ASSERT_ERREQ(SW_ERROR_WRONG_OPERATION);\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    thread t1;\n\n    serv.onStart = [&lock, &t1](Server *serv) {\n        t1 = thread([&lock, serv]() {\n            swoole_signal_block_all();\n\n            lock.lock();\n\n            ListenPort *port = serv->get_primary_port();\n            EXPECT_EQ(port->ssl, 1);\n\n            network::SyncClient c(SW_SOCK_TCP);\n            c.connect(TEST_HOST, port->port);\n            c.enable_ssl_encrypt();\n            c.send(packet, strlen(packet));\n            char buf[1024];\n            ASSERT_EQ(c.recv(buf, sizeof(buf)), 0);\n            c.close();\n\n            kill(serv->gs->master_pid, SIGTERM);\n        });\n    };\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int { return SW_OK; };\n\n    serv.onConnect = [](Server *serv, DataHead *req) { test::counter_incr(0); };\n\n    ASSERT_EQ(serv.start(), 0);\n\n    t1.join();\n    ASSERT_EQ(test::counter_get(0), 0);\n}\n\nTEST(server, ssl_write) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 1;\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    Mutex lock(true);\n    lock.lock();\n\n    ListenPort *port = serv.add_port(static_cast<enum swSocketType>(SW_SOCK_TCP | SW_SOCK_SSL), TEST_HOST, 0);\n    ASSERT_NE(port, nullptr);\n\n    port->set_ssl_cert_file(test::get_ssl_dir() + \"/server.crt\");\n    port->set_ssl_key_file(test::get_ssl_dir() + \"/server.key\");\n    ASSERT_TRUE(port->ssl_init());\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    String wbuf(4 * 1024 * 1024);\n    wbuf.append_random_bytes(wbuf.size);\n\n    thread t1;\n\n    serv.onStart = [&lock, &t1, &wbuf](Server *serv) {\n        t1 = thread([&lock, serv, &wbuf]() {\n            swoole_signal_block_all();\n\n            lock.lock();\n\n            ListenPort *port = serv->get_primary_port();\n            EXPECT_EQ(port->ssl, 1);\n\n            network::SyncClient c(SW_SOCK_TCP);\n            c.connect(TEST_HOST, port->port);\n            c.enable_ssl_encrypt();\n            c.send(packet, strlen(packet));\n\n            String rbuf(2 * 1024 * 1024);\n\n            while (true) {\n                size_t recv_n = rbuf.size - rbuf.length;\n                if (recv_n > 65536) {\n                    recv_n = 65536;\n                }\n                auto n = c.recv(rbuf.str + rbuf.length, rbuf.size - rbuf.length);\n                if (n <= 0) {\n                    break;\n                }\n                rbuf.length += n;\n                usleep(5000);\n            }\n\n            ASSERT_MEMEQ(rbuf.str, wbuf.str, rbuf.length);\n            c.close();\n\n            kill(serv->gs->master_pid, SIGTERM);\n        });\n    };\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); };\n\n    serv.onReceive = [&wbuf](Server *serv, RecvData *req) -> int {\n        EXPECT_TRUE(serv->send(req->session_id(), wbuf.str, wbuf.length));\n        test::counter_incr(0);\n        return SW_OK;\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n\n    t1.join();\n    ASSERT_EQ(test::counter_get(0), 1);\n}\n\nTEST(server, dtls) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    auto *lock = new Mutex(true);\n    lock->lock();\n\n    auto port = serv.add_port((enum swSocketType)(SW_SOCK_UDP | SW_SOCK_SSL), TEST_HOST, 0);\n    ASSERT_NE(port, nullptr);\n    ASSERT_TRUE(port->is_dgram());\n    ASSERT_EQ(port->object_id, 1);\n    ASSERT_TRUE(test::is_valid_fd(port->get_fd()));\n\n    auto port6 = serv.add_port((enum swSocketType)(SW_SOCK_UDP6 | SW_SOCK_SSL), TEST_HOST6, 0);\n    ASSERT_NE(port6, nullptr);\n    ASSERT_TRUE(port->is_dgram());\n    ASSERT_EQ(port6->object_id, 2);\n    ASSERT_TRUE(test::is_valid_fd(port6->get_fd()));\n\n    port->set_ssl_cert_file(test::get_ssl_dir() + \"/server.crt\");\n    port->set_ssl_key_file(test::get_ssl_dir() + \"/server.key\");\n    port->ssl_init();\n\n    port6->set_ssl_cert_file(test::get_ssl_dir() + \"/server.crt\");\n    port6->set_ssl_key_file(test::get_ssl_dir() + \"/server.key\");\n    port6->ssl_init();\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    thread t1;\n    serv.onStart = [&lock, &t1](Server *serv) {\n        t1 = thread([=]() {\n            swoole_signal_block_all();\n\n            lock->lock();\n\n            auto port = serv->ports.at(0);\n            EXPECT_EQ(port->ssl, 1);\n\n            auto cli_fn = [](network::SyncClient &c) {\n                c.enable_ssl_encrypt();\n                c.send(packet, strlen(packet));\n                char buf[1024];\n                c.recv(buf, sizeof(buf));\n                c.close();\n            };\n\n            network::SyncClient c(SW_SOCK_UDP);\n            c.connect(TEST_HOST, port->port);\n            cli_fn(c);\n\n            auto port6 = serv->ports.at(1);\n            EXPECT_EQ(port6->ssl, 1);\n\n            network::SyncClient c2(SW_SOCK_UDP6);\n            c2.connect(TEST_HOST6, port6->port);\n            cli_fn(c2);\n\n            usleep(10000);\n            serv->shutdown();\n        });\n    };\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock->unlock(); };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        EXPECT_EQ(string(req->data, req->info.len), string(packet));\n\n        string resp = string(\"Server: \") + string(packet);\n        serv->send(req->info.fd, resp.c_str(), resp.length());\n\n        return SW_OK;\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n\n    t1.join();\n    delete lock;\n}\n\nTEST(server, dtls2) {\n    Server *server = new Server(Server::MODE_PROCESS);\n    server->worker_num = 2;\n    server->single_thread = false;\n    ListenPort *port = server->add_port((enum swSocketType)(SW_SOCK_UDP | SW_SOCK_SSL), TEST_HOST, 0);\n\n    port->set_ssl_cert_file(test::get_ssl_dir() + \"/server.crt\");\n    port->set_ssl_key_file(test::get_ssl_dir() + \"/server.key\");\n    port->ssl_init();\n\n    server->create();\n    server->onReceive = [](Server *serv, RecvData *req) -> int {\n        EXPECT_EQ(string(req->data, req->info.len), string(packet));\n\n        string resp = string(\"Server: \") + string(packet);\n        serv->send(req->info.fd, resp.c_str(), resp.length());\n\n        return SW_OK;\n    };\n\n    pid_t pid = swoole_fork(0);\n\n    if (pid > 0) {\n        server->start();\n        delete server;\n    }\n\n    if (pid == 0) {\n        sleep(1);\n        auto port = server->get_primary_port();\n\n        network::SyncClient c(SW_SOCK_UDP);\n        c.connect(TEST_HOST, port->port);\n        c.enable_ssl_encrypt();\n        c.send(packet, strlen(packet));\n        char buf[1024];\n        c.recv(buf, sizeof(buf));\n        c.close();\n\n        kill(server->get_master_pid(), SIGTERM);\n        exit(0);\n    }\n}\n\nstatic void test_ssl_client_cert(Server::Mode mode) {\n    Server serv(mode);\n    serv.worker_num = 1;\n    swoole_set_log_level(SW_LOG_INFO);\n\n    Mutex *lock = new Mutex(true);\n    lock->lock();\n\n    ListenPort *port = serv.add_port((enum swSocketType)(SW_SOCK_TCP | SW_SOCK_SSL), TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    port->set_ssl_cert_file(test::get_ssl_dir() + \"/server.crt\");\n    port->set_ssl_key_file(test::get_ssl_dir() + \"/server.key\");\n    port->set_ssl_verify_peer(true);\n    port->set_ssl_allow_self_signed(true);\n    port->set_ssl_client_cert_file(test::get_ssl_dir() + \"/ca-cert.pem\");\n    port->ssl_init();\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    thread t1;\n    serv.onStart = [&lock, &t1](Server *serv) {\n        t1 = thread([=]() {\n            swoole_signal_block_all();\n\n            lock->lock();\n\n            ListenPort *port = serv->get_primary_port();\n\n            EXPECT_EQ(port->ssl, 1);\n\n            network::SyncClient c(SW_SOCK_TCP);\n            c.enable_ssl_encrypt();\n            c.get_client()->set_ssl_cert_file(test::get_ssl_dir() + \"/client-cert.pem\");\n            c.get_client()->set_ssl_key_file(test::get_ssl_dir() + \"/client-key.pem\");\n            c.connect(TEST_HOST, port->port);\n            EXPECT_EQ(c.send(packet, strlen(packet)), strlen(packet));\n\n            char buf[1024];\n            EXPECT_GT(c.recv(buf, sizeof(buf)), 0);\n            c.close();\n\n            kill(serv->gs->master_pid, SIGTERM);\n        });\n    };\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock->unlock(); };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        EXPECT_EQ(string(req->data, req->info.len), string(packet));\n\n        string resp = string(\"Server: \") + string(packet);\n        serv->send(req->info.fd, resp.c_str(), resp.length());\n\n        auto conn = serv->get_connection_by_session_id(req->session_id());\n        EXPECT_NE(conn->ssl_client_cert, nullptr);\n        EXPECT_GT(conn->ssl_client_cert->length, 16);\n\n        char *buffer = NULL;\n        size_t size = 0;\n        FILE *stream = open_memstream(&buffer, &size);\n        swoole_set_stdout_stream(stream);\n        swoole::test::dump_cert_info(conn->ssl_client_cert->str, conn->ssl_client_cert->length);\n        fflush(stream);\n        swoole_set_stdout_stream(stdout);\n\n        EXPECT_NE(strstr(buffer, \"organizationName: swoole\"), nullptr);\n\n        fclose(stream);\n        free(buffer);\n\n        return SW_OK;\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n\n    t1.join();\n    delete lock;\n}\n\nTEST(server, ssl_client_cert_1) {\n    test_ssl_client_cert(Server::MODE_BASE);\n}\n\nTEST(server, ssl_client_cert_2) {\n    test_ssl_client_cert(Server::MODE_PROCESS);\n}\n\nTEST(server, ssl_client_cert_3) {\n    test_ssl_client_cert(Server::MODE_THREAD);\n}\n\nTEST(server, task_worker) {\n    Server serv;\n    serv.worker_num = 1;\n    serv.task_worker_num = 1;\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    serv.onTask = [](Server *serv, EventData *task) -> int {\n        EXPECT_EQ(serv->get_tasking_num(), 1);\n        EXPECT_EQ(string(task->data, task->info.len), string(packet));\n        serv->get_task_worker_pool()->running = 0;\n        serv->gs->task_count++;\n        serv->gs->tasking_num--;\n        return 0;\n    };\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    thread t1([&serv]() {\n        auto pool = serv.get_task_worker_pool();\n        pool->running = true;\n        pool->main_loop(pool, &pool->workers[0]);\n        EXPECT_EQ(serv.get_tasking_num(), 0);\n        serv.gs->tasking_num--;\n        EXPECT_EQ(serv.get_tasking_num(), 0);\n        EXPECT_EQ(serv.get_idle_task_worker_num(), serv.task_worker_num);\n    });\n\n    usleep(10000);\n\n    EventData buf;\n    memset(&buf.info, 0, sizeof(buf.info));\n\n    buf.info.ext_flags = SW_TASK_NOREPLY;\n    buf.info.len = strlen(packet);\n    memcpy(buf.data, packet, strlen(packet));\n\n    int _dst_worker_id = 0;\n\n    ASSERT_TRUE(serv.task(&buf, &_dst_worker_id));\n    ASSERT_EQ(serv.gs->task_count, 1);\n\n    t1.join();\n    serv.get_task_worker_pool()->destroy();\n\n    ASSERT_EQ(serv.gs->task_count, 2);\n}\n\nTEST(server, task_worker2) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 1;\n    serv.task_worker_num = 2;\n    test::counter_init();\n\n    auto port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_NE(port, nullptr);\n\n    serv.onTask = [](Server *serv, EventData *task) -> int { return 0; };\n\n    serv.onPipeMessage = [](Server *serv, EventData *task) {\n        EXPECT_MEMEQ(task->data, TEST_STR, strlen(TEST_STR));\n        test::counter_incr(7);\n    };\n\n    serv.onWorkerStart = [](Server *serv, Worker *worker) {\n        if (worker->id == 0) {\n            swoole_timer_after(50, [serv](TIMER_PARAMS) {\n                EventData ev;\n                ev.info = {};\n                ev.info.type = SW_SERVER_EVENT_SHUTDOWN;\n                ev.info.len = 0;\n                DEBUG() << \"send SW_SERVER_EVENT_SHUTDOWN packet\\n\";\n                ASSERT_GT(serv->send_to_worker_from_worker(1, &ev, SW_PIPE_MASTER | SW_PIPE_NONBLOCK), 0);\n            });\n\n            swoole_timer_after(60,\n                               [serv](TIMER_PARAMS) { ASSERT_TRUE(serv->send_pipe_message(2, SW_STRL(TEST_STR))); });\n\n            swoole_timer_after(70, [serv](TIMER_PARAMS) {\n                EventData ev;\n                ev.info = {};\n                ev.info.type = SW_SERVER_EVENT_SHUTDOWN + 99;\n                ev.info.len = 0;\n                DEBUG() << \"send error type packet\\n\";\n                ASSERT_GT(serv->send_to_worker_from_worker(0, &ev, SW_PIPE_MASTER | SW_PIPE_NONBLOCK), 0);\n            });\n\n            swoole_timer_after(100, [serv](TIMER_PARAMS) { serv->shutdown(); });\n        }\n        test::counter_incr(1);\n        DEBUG() << \"onWorkerStart: id=\" << worker->id << \"\\n\";\n    };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int { return 0; };\n\n    ASSERT_EQ(serv.create(), SW_OK);\n    ASSERT_EQ(serv.start(), SW_OK);\n\n    ASSERT_EQ(test::counter_get(1), 4);  // onWorkerStart\n    ASSERT_EQ(test::counter_get(7), 1);  // onPipeMessage\n}\n\nTEST(server, task_worker_3) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n    serv.task_worker_num = 2;\n    test::counter_init();\n\n    auto port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_NE(port, nullptr);\n\n    serv.onTask = [](Server *serv, EventData *task) -> int { return 0; };\n\n    serv.onWorkerStart = [](Server *serv, Worker *worker) {\n        DEBUG() << \"onWorkerStart: id=\" << worker->id << \"\\n\";\n        if (test::counter_incr(1) == 5) {\n            swoole_timer_after(100, [serv](TIMER_PARAMS) { serv->shutdown(); });\n        }\n        if (worker->id == 0) {\n            swoole_timer_after(50, [serv](TIMER_PARAMS) { kill(serv->get_worker_pid(2), SIGTERM); });\n            swoole_timer_after(60, [serv](TIMER_PARAMS) { kill(serv->get_manager_pid(), SIGRTMIN); });\n        }\n        if (worker->id == 1 && test::counter_get(30) == 0) {\n            test::counter_set(30, 1);\n            swoole_timer_after(20, [serv](TIMER_PARAMS) { serv->kill_worker(-1); });\n        }\n    };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int { return 0; };\n\n    ASSERT_EQ(serv.create(), SW_OK);\n    ASSERT_EQ(serv.start(), SW_OK);\n\n    ASSERT_EQ(test::counter_get(1), 5);  // onWorkerStart\n}\n\nTEST(server, reload_single_process) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n    test::counter_init();\n\n    auto port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_NE(port, nullptr);\n\n    serv.onTask = [](Server *serv, EventData *task) -> int { return 0; };\n\n    serv.onWorkerStart = [](Server *serv, Worker *worker) {\n        if (worker->id == 0) {\n            swoole_timer_after(50, [serv](TIMER_PARAMS) {\n                ASSERT_FALSE(serv->reload(true));\n                ASSERT_ERREQ(SW_ERROR_OPERATION_NOT_SUPPORT);\n                swoole_timer_after(80, [serv](TIMER_PARAMS) { serv->shutdown(); });\n            });\n        }\n        test::counter_incr(1);\n        DEBUG() << \"onWorkerStart: id=\" << worker->id << \"\\n\";\n    };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int { return 0; };\n\n    ASSERT_EQ(serv.create(), SW_OK);\n    ASSERT_EQ(serv.start(), SW_OK);\n\n    ASSERT_EQ(test::counter_get(1), 1);  // onWorkerStart\n}\n\nTEST(server, reload_no_task_worker) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 2;\n    test::counter_init();\n\n    auto port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_NE(port, nullptr);\n\n    serv.onTask = [](Server *serv, EventData *task) -> int { return 0; };\n\n    serv.onWorkerStart = [](Server *serv, Worker *worker) {\n        if (worker->id == 0) {\n            swoole_timer_after(50, [serv](TIMER_PARAMS) {\n                ASSERT_TRUE(serv->reload(false));\n                swoole_timer_after(80, [serv](TIMER_PARAMS) { serv->shutdown(); });\n            });\n        }\n        test::counter_incr(1);\n        DEBUG() << \"onWorkerStart: id=\" << worker->id << \"\\n\";\n    };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int { return 0; };\n\n    ASSERT_EQ(serv.create(), SW_OK);\n    ASSERT_EQ(serv.start(), SW_OK);\n\n    ASSERT_EQ(test::counter_get(1), 2);  // onWorkerStart\n}\n\nstatic void test_task(Server::Mode mode, uint8_t task_ipc_mode = Server::TASK_IPC_UNIXSOCK) {\n    Server serv(mode);\n    serv.worker_num = 2;\n    serv.task_ipc_mode = task_ipc_mode;\n    serv.task_worker_num = 3;\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; };\n\n    serv.onTask = [](Server *serv, EventData *task) -> int {\n        EXPECT_EQ(string(task->data, task->info.len), string(packet));\n        EXPECT_TRUE(serv->finish(task->data, task->info.len, 0, task));\n        return 0;\n    };\n\n    serv.onFinish = [](Server *serv, EventData *task) -> int {\n        EXPECT_EQ(string(task->data, task->info.len), string(packet));\n        return 0;\n    };\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onWorkerStart = [&](Server *serv, Worker *worker) {\n        DEBUG() << \"onWorkerStart: id=\" << worker->id << \"\\n\";\n        if (worker->id == 1) {\n            int _dst_worker_id = 0;\n\n            EventData buf{};\n            memset(&buf.info, 0, sizeof(buf.info));\n            buf.info.len = strlen(packet);\n            memcpy(buf.data, packet, strlen(packet));\n            buf.info.reactor_id = worker->id;\n            buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_CALLBACK);\n            ASSERT_TRUE(serv->task(&buf, &_dst_worker_id));\n            sleep(1);\n            serv->shutdown();\n        }\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n}\n\n// PHP_METHOD(swoole_server, task)\nTEST(server, task_base) {\n    test_task(Server::MODE_BASE);\n}\n\nTEST(server, task_process) {\n    test_task(Server::MODE_PROCESS);\n}\n\nTEST(server, task_ipc_stream) {\n    test_task(Server::MODE_PROCESS, Server::TASK_IPC_STREAM);\n}\n\n// static PHP_METHOD(swoole_server, taskCo)\nTEST(server, task_worker3) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 2;\n    serv.task_worker_num = 3;\n    serv.task_enable_coroutine = 1;\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; };\n\n    serv.onTask = [](Server *serv, EventData *task) -> int {\n        EXPECT_EQ(string(task->data, task->info.len), string(packet));\n        EXPECT_TRUE(serv->finish(task->data, task->info.len, 0, task));\n        return 0;\n    };\n\n    serv.onFinish = [](Server *serv, EventData *task) -> int {\n        EXPECT_EQ(string(task->data, task->info.len), string(packet));\n        return 0;\n    };\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onWorkerStart = [&](Server *serv, Worker *worker) {\n        if (worker->id == 1) {\n            int _dst_worker_id = 0;\n\n            EventData buf{};\n            memset(&buf.info, 0, sizeof(buf.info));\n            buf.info.len = strlen(packet);\n            memcpy(buf.data, packet, strlen(packet));\n            buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE);\n            buf.info.reactor_id = worker->id;\n            serv->get_task_worker_pool()->dispatch(&buf, &_dst_worker_id);\n            sleep(1);\n            kill(serv->gs->master_pid, SIGTERM);\n        }\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n}\n\n// static PHP_METHOD(swoole_server, taskwait)\nTEST(server, task_worker4) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 2;\n    serv.task_worker_num = 3;\n    serv.task_enable_coroutine = 1;\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; };\n\n    serv.onTask = [](Server *serv, EventData *task) -> int {\n        EXPECT_EQ(string(task->data, task->info.len), string(packet));\n        EXPECT_TRUE(serv->finish(task->data, task->info.len, 0, task));\n        return 0;\n    };\n\n    serv.onFinish = [](Server *serv, EventData *task) -> int {\n        EXPECT_EQ(string(task->data, task->info.len), string(packet));\n        return 0;\n    };\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onWorkerStart = [&](Server *serv, Worker *worker) {\n        if (worker->id == 1) {\n            int _dst_worker_id = 0;\n\n            EventData buf{};\n            memset(&buf.info, 0, sizeof(buf.info));\n            buf.info.len = strlen(packet);\n            memcpy(buf.data, packet, strlen(packet));\n            buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE);\n            buf.info.reactor_id = worker->id;\n            serv->get_task_worker_pool()->dispatch(&buf, &_dst_worker_id);\n            sleep(1);\n\n            EventData *task_result = serv->get_task_result();\n            sw_memset_zero(task_result, sizeof(*task_result));\n            memset(&buf.info, 0, sizeof(buf.info));\n            buf.info.len = strlen(packet);\n            memcpy(buf.data, packet, strlen(packet));\n            buf.info.reactor_id = worker->id;\n            sw_atomic_fetch_add(&serv->gs->tasking_num, 1);\n            serv->get_task_worker_pool()->dispatch(&buf, &_dst_worker_id);\n            sw_atomic_fetch_add(&serv->gs->tasking_num, 0);\n            kill(serv->gs->master_pid, SIGTERM);\n        }\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n}\n\nTEST(server, task_sync_multi_task) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 2;\n    serv.task_worker_num = 3;\n\n    std::vector<std::string> tasks;\n    std::vector<std::string> results;\n    int n_task = 16;\n    constexpr size_t len_task = SW_IPC_MAX_SIZE * 2;\n    SW_LOOP_N(n_task) {\n        char data[len_task] = {};\n        swoole_random_string(data, len_task - 1);\n        tasks.push_back(string(data, len_task - 1));\n    }\n\n    results.resize(n_task);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; };\n\n    serv.onTask = [](Server *serv, EventData *task) -> int {\n        PacketPtr packet{};\n        String buffer(32 * 1024);\n        if (!Server::task_unpack(task, &buffer, &packet)) {\n            return -1;\n        }\n        Server::task_dump(task);\n        EXPECT_TRUE(serv->finish(packet.data, packet.length, 0, task));\n        return 0;\n    };\n\n    ASSERT_EQ(serv.create(), SW_OK);\n    SwooleG.current_task_id = 100;\n\n    serv.onWorkerStart = [&tasks, &results](Server *serv, Worker *worker) {\n        if (worker->id == 1) {\n            Server::MultiTask mt(tasks.size());\n            mt.pack = [tasks](uint16_t i, EventData *buf) -> TaskId {\n                auto &task = tasks.at(i);\n                if (!Server::task_pack(buf, task.c_str(), task.length())) {\n                    return -1;\n                } else {\n                    return buf->info.fd;\n                }\n            };\n\n            mt.unpack = [&tasks, &results](uint16_t i, EventData *result) {\n                String buffer(32 * 1024);\n                PacketPtr packet;\n                if (Server::task_unpack(result, &buffer, &packet)) {\n                    results[i] = std::string(packet.data, packet.length);\n                }\n            };\n\n            mt.fail = [&results](uint16_t i) { DEBUG() << \"task failed: \" << i << std::endl; };\n\n            EXPECT_TRUE(serv->task_sync(mt, 10));\n\n            SW_LOOP_N(tasks.size()) {\n                EXPECT_EQ(tasks[i], results[i]);\n            }\n\n            kill(serv->gs->master_pid, SIGTERM);\n        }\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n}\n\nTEST(server, task_sync) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 2;\n    serv.task_worker_num = 2;\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; };\n\n    serv.onTask = [](Server *serv, EventData *task) -> int {\n        EXPECT_EQ(string(task->data, task->info.len), string(packet));\n        Server::task_dump(task);\n        EXPECT_TRUE(serv->finish(task->data, task->info.len, 0, task));\n        return 0;\n    };\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onWorkerStart = [&](Server *serv, Worker *worker) {\n        if (worker->id == 1) {\n            int _dst_worker_id = -1;\n            EventData buf{};\n            Server::task_pack(&buf, packet, strlen(packet));\n            EXPECT_TRUE(serv->task_sync(&buf, &_dst_worker_id, 0.5));\n            auto task_result = serv->get_task_result();\n            EXPECT_EQ(string(task_result->data, task_result->info.len), string(packet));\n            kill(serv->gs->master_pid, SIGTERM);\n        }\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n}\n\nstatic void test_task_ipc(Server &serv) {\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; };\n\n    serv.onTask = [](Server *serv, EventData *task) -> int {\n        EXPECT_EQ(string(task->data, task->info.len), string(packet));\n        EXPECT_TRUE(serv->finish(task->data, task->info.len, 0, task));\n        DEBUG() << \"onTask: \" << task->info.len << \" bytes\\n\";\n        return 0;\n    };\n\n    serv.onFinish = [](Server *serv, EventData *task) -> int {\n        EXPECT_EQ(string(task->data, task->info.len), string(packet));\n        usleep(100000);\n        DEBUG() << \"onFinish: \" << task->info.len << \" bytes\\n\";\n        serv->shutdown();\n        return 0;\n    };\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onWorkerStart = [](Server *serv, Worker *worker) {\n        if (worker->id == 1) {\n            int _dst_worker_id = -1;\n            EventData buf{};\n            Server::task_pack(&buf, packet, strlen(packet));\n            buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_CALLBACK);\n            EXPECT_TRUE(serv->task(&buf, &_dst_worker_id));\n        }\n\n        DEBUG() << \"onWorkerStart: id=\" << worker->id << \"\\n\";\n    };\n\n    serv.onBeforeShutdown = [](Server *serv) { DEBUG() << \"onBeforeShutdown\\n\"; };\n\n    ASSERT_EQ(serv.start(), 0);\n}\n\nTEST(server, task_ipc_queue_1) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 2;\n    serv.task_worker_num = 2;\n    serv.task_ipc_mode = Server::TASK_IPC_MSGQUEUE;\n\n    test_task_ipc(serv);\n}\n\nTEST(server, task_ipc_queue_2) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 2;\n    serv.task_worker_num = 2;\n    serv.task_ipc_mode = Server::TASK_IPC_PREEMPTIVE;\n\n    test_task_ipc(serv);\n}\n\nTEST(server, task_ipc_queue_3) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 2;\n    serv.task_worker_num = 2;\n    serv.task_ipc_mode = Server::TASK_IPC_STREAM;\n\n    test_task_ipc(serv);\n}\n\nTEST(server, task_ipc_queue_4) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 2;\n    serv.task_worker_num = 2;\n    serv.task_ipc_mode = Server::TASK_IPC_MSGQUEUE;\n\n    test_task_ipc(serv);\n}\n\nTEST(server, task_ipc_queue_5) {\n    Server serv(Server::MODE_THREAD);\n    serv.worker_num = 2;\n    serv.task_worker_num = 2;\n    serv.task_ipc_mode = Server::TASK_IPC_MSGQUEUE;\n\n    test::wait_all_child_processes();\n\n    test_task_ipc(serv);\n}\n\nTEST(server, max_connection) {\n    Server serv;\n\n    auto ori_max_sockets = SwooleG.max_sockets;\n\n    serv.set_max_connection(0);\n    ASSERT_EQ(serv.get_max_connection(), SW_MIN(SW_MAX_CONNECTION, SwooleG.max_sockets));\n\n    serv.set_max_connection(SwooleG.max_sockets + 13);\n    ASSERT_EQ(serv.get_max_connection(), SwooleG.max_sockets);\n\n    serv.set_max_connection(SwooleG.max_sockets - 13);\n    ASSERT_EQ(serv.get_max_connection(), SwooleG.max_sockets - 13);\n\n    SwooleG.max_sockets = SW_SESSION_LIST_SIZE + 1024;\n    serv.set_max_connection(SW_SESSION_LIST_SIZE + 999);\n    ASSERT_EQ(serv.get_max_connection(), SW_SESSION_LIST_SIZE);\n    SwooleG.max_sockets = ori_max_sockets;\n\n    uint32_t last_value = serv.get_max_connection();\n\n    ASSERT_TRUE(serv.add_port(SW_SOCK_TCP, TEST_HOST, 0));\n\n    serv.create();\n\n    serv.set_max_connection(100);\n    ASSERT_EQ(serv.get_max_connection(), last_value);\n}\n\nTEST(server, min_connection) {\n    Server serv;\n\n    serv.task_worker_num = 14;\n    serv.worker_num = 5;\n\n    serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n\n    serv.set_max_connection(15);\n    serv.create();\n    ASSERT_EQ(serv.get_max_connection(), SwooleG.max_sockets);\n}\n\nTEST(server, worker_num) {\n    Server serv;\n\n    serv.worker_num = SW_CPU_NUM * SW_MAX_WORKER_NCPU + 99;\n    serv.task_worker_num = SW_CPU_NUM * SW_MAX_WORKER_NCPU + 99;\n\n    ASSERT_TRUE(serv.add_port(SW_SOCK_TCP, TEST_HOST, 0));\n\n    serv.create();\n\n    ASSERT_EQ(serv.worker_num, SW_CPU_NUM * SW_MAX_WORKER_NCPU);\n    ASSERT_EQ(serv.task_worker_num, SW_CPU_NUM * SW_MAX_WORKER_NCPU);\n}\n\nTEST(server, reactor_num_base) {\n    Server serv(Server::MODE_BASE);\n    serv.reactor_num = SW_CPU_NUM * SW_MAX_THREAD_NCPU + 99;\n    ASSERT_TRUE(serv.add_port(SW_SOCK_TCP, TEST_HOST, 0));\n    serv.create();\n\n    ASSERT_EQ(serv.reactor_num, serv.worker_num);\n}\n\nTEST(server, reactor_num_large) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = SW_CPU_NUM * SW_MAX_WORKER_NCPU;\n    serv.reactor_num = SW_CPU_NUM * SW_MAX_THREAD_NCPU + 99;\n    ASSERT_TRUE(serv.add_port(SW_SOCK_TCP, TEST_HOST, 0));\n    serv.create();\n\n    ASSERT_EQ(serv.reactor_num, SW_CPU_NUM * SW_MAX_THREAD_NCPU);\n}\n\nTEST(server, reactor_num_large2) {\n    Server serv(Server::MODE_PROCESS);\n    serv.reactor_num = SW_CPU_NUM * SW_MAX_THREAD_NCPU + 99;\n    ASSERT_TRUE(serv.add_port(SW_SOCK_TCP, TEST_HOST, 0));\n    serv.create();\n\n    ASSERT_EQ(serv.reactor_num, serv.worker_num);\n}\n\nTEST(server, reactor_num_zero) {\n    Server serv;\n    serv.reactor_num = 0;\n    ASSERT_TRUE(serv.add_port(SW_SOCK_TCP, TEST_HOST, 0));\n    serv.create();\n\n    ASSERT_EQ(serv.reactor_num, SW_CPU_NUM);\n}\n\nvoid test_command(enum Server::Mode _mode) {\n    Server serv(_mode);\n    serv.worker_num = 4;\n    serv.task_worker_num = 4;\n    serv.reactor_num = 2;\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.add_command(\"test\", Server::Command::ALL_PROCESS, [](Server *, const std::string &msg) -> std::string {\n        return std::string(\"json result, \") + msg;\n    });\n\n    serv.onStart = [](Server *serv) {\n        static Server::Command::Callback fn = [&](Server *serv, const std::string &msg) {\n            usleep(50000);\n            if (msg == \"json result, hello world [0]\") {\n                if (serv->is_base_mode()) {\n                    goto _send_to_event_worker;\n                } else {\n                    serv->command(1, Server::Command::REACTOR_THREAD, \"test\", \"hello world [1]\", fn);\n                }\n            } else if (msg == \"json result, hello world [1]\") {\n            _send_to_event_worker:\n                serv->command(1, Server::Command::EVENT_WORKER, \"test\", \"hello world [2]\", fn);\n            } else if (msg == \"json result, hello world [2]\") {\n                serv->command(1, Server::Command::TASK_WORKER, \"test\", \"hello world [3]\", fn);\n            } else if (msg == \"json result, hello world [3]\") {\n                serv->command(1, Server::Command::MANAGER, \"test\", \"hello world [4]\", fn);\n            } else if (msg == \"json result, hello world [4]\") {\n                swoole_timer_after(50, [serv](Timer *, TimerNode *) { serv->shutdown(); });\n            } else {\n                ASSERT_TRUE(0);\n            }\n        };\n        serv->command(1, Server::Command::MASTER, \"test\", \"hello world [0]\", fn);\n    };\n\n    serv.onWorkerStart = [](Server *serv, Worker *worker) {\n\n    };\n\n    serv.onTask = [](Server *, EventData *) -> int { return SW_OK; };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        EXPECT_EQ(string(req->data, req->info.len), string(packet));\n\n        string resp = string(\"Server: \") + string(packet);\n        serv->send(req->info.fd, resp.c_str(), resp.length());\n\n        return SW_OK;\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n}\n\nTEST(server, command_1) {\n    test_command(Server::MODE_PROCESS);\n}\n\nTEST(server, command_2) {\n    test_command(Server::MODE_BASE);\n}\n\nTEST(server, sendwait) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n\n    mutex lock;\n    lock.lock();\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    std::thread t1([&]() {\n        swoole_signal_block_all();\n\n        lock.lock();\n\n        network::SyncClient c(SW_SOCK_TCP);\n        c.connect(TEST_HOST, port->port);\n        c.send(packet, strlen(packet));\n        char buf[1024];\n        c.recv(buf, sizeof(buf));\n        c.close();\n\n        kill(getpid(), SIGTERM);\n    });\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        EXPECT_EQ(string(req->data, req->info.len), string(packet));\n\n        string resp = string(\"Server: \") + string(packet);\n        serv->sendwait(req->info.fd, resp.c_str(), resp.length());\n\n        return SW_OK;\n    };\n\n    serv.start();\n    t1.join();\n}\n\nTEST(server, system) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    mutex lock;\n    lock.lock();\n\n    int fd = socket(AF_INET, SOCK_STREAM, 0);\n    int svr_port = swoole::test::get_random_port();\n    struct sockaddr_in serv_addr;\n    bzero(&serv_addr, sizeof(serv_addr));\n    serv_addr.sin_addr.s_addr = inet_addr(TEST_HOST);\n    serv_addr.sin_port = htons(svr_port);\n    serv_addr.sin_family = AF_INET;\n    bind(fd, (struct sockaddr *) &serv_addr, sizeof(struct sockaddr));\n    listen(fd, 1024);\n\n    setenv(\"LISTEN_FDS_START\", to_string(fd).c_str(), 1);\n    setenv(\"LISTEN_FDS\", \"1\", 1);\n    setenv(\"LISTEN_PID\", to_string(getpid()).c_str(), 1);\n\n    EXPECT_GT(serv.add_systemd_socket(), 0);\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    std::thread t1([&]() {\n        swoole_signal_block_all();\n        lock.lock();\n\n        network::SyncClient c(SW_SOCK_TCP);\n        c.connect(TEST_HOST, svr_port);\n        c.send(packet, strlen(packet));\n        char buf[1024];\n        c.recv(buf, sizeof(buf));\n        c.close();\n\n        kill(getpid(), SIGTERM);\n    });\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        EXPECT_EQ(string(req->data, req->info.len), string(packet));\n\n        string resp = string(\"Server: \") + string(packet);\n        serv->sendwait(req->info.fd, resp.c_str(), resp.length());\n\n        return SW_OK;\n    };\n\n    serv.start();\n    t1.join();\n}\n\nTEST(server, reopen_log) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 2;\n    swoole_set_log_level(SW_LOG_WARNING);\n    string filename = TEST_LOG_FILE;\n    swoole_set_log_file(filename.c_str());\n\n    ASSERT_TRUE(serv.add_port(SW_SOCK_TCP, TEST_HOST, 0));\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onWorkerStart = [&filename](Server *serv, Worker *worker) {\n        if (worker->id != 0) {\n            return;\n        }\n        EXPECT_TRUE(access(filename.c_str(), R_OK) != -1);\n        usleep(10000);\n        unlink(filename.c_str());\n        EXPECT_TRUE(access(filename.c_str(), R_OK) == -1);\n        kill(serv->gs->master_pid, SIGRTMIN);\n        sleep(2);\n        EXPECT_TRUE(access(filename.c_str(), R_OK) != -1);\n        kill(serv->gs->master_pid, SIGTERM);\n    };\n\n    serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; };\n\n    ASSERT_EQ(serv.start(), 0);\n    remove(filename.c_str());\n}\n\nTEST(server, reopen_log2) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 2;\n    swoole_set_log_level(SW_LOG_DEBUG);\n    string filename = TEST_LOG_FILE;\n    swoole_set_log_file(filename.c_str());\n\n    ASSERT_TRUE(serv.add_port(SW_SOCK_TCP, TEST_HOST, 0));\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onStart = [](Server *serv) {\n        swoole_timer_after(50, [serv](TIMER_PARAMS) {\n            serv->signal_handler_reopen_logger();\n            swoole_timer_after(50, [serv](TIMER_PARAMS) { serv->shutdown(); });\n        });\n    };\n\n    serv.onWorkerStart = [&filename](Server *serv, Worker *worker) { test::counter_incr(0, 1); };\n\n    serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; };\n\n    ASSERT_EQ(serv.start(), 0);\n    remove(filename.c_str());\n}\n\nTEST(server, udp_packet) {\n    Server *server = new Server(Server::MODE_PROCESS);\n    server->worker_num = 2;\n    server->add_port(SW_SOCK_UDP, TEST_HOST, 0);\n\n    server->create();\n    server->onPacket = [](Server *serv, RecvData *req) {\n        DgramPacket *recv_data = (DgramPacket *) req->data;\n        EXPECT_EQ(string(recv_data->data, recv_data->length), string(packet));\n        network::Socket *server_socket = serv->get_server_socket(req->info.server_fd);\n        string resp = string(packet);\n        server_socket->sendto(recv_data->socket_addr, resp.c_str(), resp.length(), 0);\n        return SW_OK;\n    };\n\n    server->onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; };\n\n    pid_t pid = swoole_fork(0);\n\n    if (pid > 0) {\n        server->start();\n        int status;\n        waitpid(pid, &status, 0);\n    } else if (pid == 0) {\n        sleep(1);\n        auto port = server->get_primary_port();\n\n        network::Client cli(SW_SOCK_UDP, false);\n        int ret = cli.connect(TEST_HOST, port->port, -1, 0);\n        EXPECT_EQ(ret, 0);\n        ret = cli.send(packet, strlen(packet), 0);\n        EXPECT_GT(ret, 0);\n\n        char buf[1024];\n        sleep(1);\n        cli.recv(buf, 128, 0);\n        ASSERT_MEMEQ(buf, packet, strlen(packet));\n        cli.close();\n\n        kill(server->get_master_pid(), SIGTERM);\n        exit(0);\n    }\n}\n\nTEST(server, protocols) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n    swoole_set_log_level(SW_LOG_WARNING);\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n\n    port->open_eof_check = true;\n    ASSERT_STREQ(port->get_protocols(), \"eof\");\n    port->open_eof_check = false;\n\n    port->open_length_check = true;\n    ASSERT_STREQ(port->get_protocols(), \"length\");\n    port->open_length_check = false;\n\n    port->open_http_protocol = true;\n    ASSERT_STREQ(port->get_protocols(), \"http\");\n    port->open_http_protocol = false;\n\n    port->open_http_protocol = true;\n    port->open_http2_protocol = true;\n    port->open_websocket_protocol = true;\n    ASSERT_STREQ(port->get_protocols(), \"http|http2|websocket\");\n    port->open_http2_protocol = false;\n    port->open_websocket_protocol = false;\n    port->open_http_protocol = false;\n\n    port->open_http_protocol = true;\n    port->open_http2_protocol = true;\n    ASSERT_STREQ(port->get_protocols(), \"http|http2\");\n    port->open_http2_protocol = false;\n    port->open_http_protocol = false;\n\n    port->open_http_protocol = true;\n    port->open_websocket_protocol = true;\n    ASSERT_STREQ(port->get_protocols(), \"http|websocket\");\n    port->open_websocket_protocol = false;\n    port->open_http_protocol = false;\n\n    port->open_mqtt_protocol = true;\n    ASSERT_STREQ(port->get_protocols(), \"mqtt\");\n    port->open_mqtt_protocol = false;\n\n    port->open_redis_protocol = true;\n    ASSERT_STREQ(port->get_protocols(), \"redis\");\n    port->open_redis_protocol = false;\n\n    port->clear_protocol();\n    ASSERT_EQ(port->open_eof_check, 0);\n    ASSERT_EQ(port->open_length_check, 0);\n    ASSERT_EQ(port->open_http_protocol, 0);\n    ASSERT_EQ(port->open_websocket_protocol, 0);\n    ASSERT_EQ(port->open_http2_protocol, 0);\n    ASSERT_EQ(port->open_mqtt_protocol, 0);\n    ASSERT_EQ(port->open_redis_protocol, 0);\n    ASSERT_STREQ(port->get_protocols(), \"raw\");\n}\n\nTEST(server, pipe_message) {\n    Server *server = new Server(Server::MODE_PROCESS);\n    server->worker_num = 2;\n    server->add_port(SW_SOCK_TCP, TEST_HOST, 0);\n\n    server->create();\n    server->onPipeMessage = [](Server *serv, EventData *req) -> int {\n        EXPECT_EQ(string(req->data, req->info.len), string(packet));\n        return SW_OK;\n    };\n\n    server->onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; };\n\n    server->onWorkerStart = [&](Server *server, Worker *worker) {\n        if (worker->id == 1) {\n            EventData buf{};\n            string data = string(packet);\n\n            memset(&buf.info, 0, sizeof(buf.info));\n            ASSERT_TRUE(Server::task_pack(&buf, data.c_str(), data.length()));\n            ASSERT_TRUE(server->send_pipe_message(worker->id - 1, &buf));\n            sleep(1);\n\n            kill(server->get_master_pid(), SIGTERM);\n        }\n    };\n\n    server->start();\n}\n\nTEST(server, forward_message) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 2;\n\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n\n    swoole::Mutex lock(true);\n    lock.lock();\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    std::thread t1([&]() {\n        swoole_signal_block_all();\n\n        lock.lock();\n\n        network::SyncClient c(SW_SOCK_TCP);\n        c.connect(TEST_HOST, port->port);\n        c.send(packet, strlen(packet));\n        char buf[1024];\n        c.recv(buf, sizeof(buf));\n        c.close();\n\n        kill(getpid(), SIGTERM);\n    });\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); };\n\n    serv.onPipeMessage = [](Server *serv, EventData *req) -> void {\n        SessionId client_fd;\n        memcpy(&client_fd, req->data, sizeof(client_fd));\n        string resp = string(\"Server: \") + string(packet);\n        serv->send(client_fd, resp.c_str(), resp.length());\n    };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        EventData msg;\n        SessionId client_fd = req->info.fd;\n        Server::task_pack(&msg, &client_fd, sizeof(client_fd));\n        EXPECT_TRUE(serv->send_pipe_message(1 - swoole_get_worker_id(), &msg));\n        return SW_OK;\n    };\n\n    serv.start();\n    t1.join();\n}\n\nTEST(server, abnormal_pipeline_data) {\n    Server *server = new Server(Server::MODE_PROCESS);\n    server->worker_num = 2;\n    server->add_port(SW_SOCK_TCP, TEST_HOST, 0);\n\n    uint64_t msg_id = swoole_rand(1, INT_MAX);\n    string filename = TEST_LOG_FILE;\n    swoole_set_log_file(filename.c_str());\n\n    server->create();\n\n    server->onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; };\n\n    server->onWorkerStart = [&](Server *server, Worker *worker) {\n        if (worker->id == 1) {\n            auto send_fn = [server](int flags, uint64_t msg_id) {\n                auto sock = server->get_worker_pipe_master(0);\n                size_t len = swoole_rand(1000, 8000);\n                EventData ev;\n                ev.info.msg_id = msg_id;\n                ev.info.flags = flags;\n                ev.info.len = len;\n                swoole_random_bytes(ev.data, len);\n\n                sock->send_sync(&ev, sizeof(ev.info) + len);\n            };\n\n            send_fn(SW_EVENT_DATA_CHUNK | SW_EVENT_DATA_BEGIN, msg_id);\n            send_fn(SW_EVENT_DATA_CHUNK, msg_id + 9999);\n\n            usleep(100000);\n            server->shutdown();\n        }\n    };\n\n    server->start();\n\n    File fp(filename, File::READ);\n    auto cont = fp.read_content();\n    ASSERT_TRUE(cont->contains(std::string(\"abnormal pipeline data, msg_id=\") + std::to_string(msg_id + 9999)));\n\n    unlink(filename.c_str());\n}\n\nTEST(server, startup_error) {\n    Server *server = new Server(Server::MODE_PROCESS);\n    server->task_worker_num = 2;\n\n    ASSERT_NE(server->add_port(SW_SOCK_TCP, TEST_HOST, 0), nullptr);\n    ASSERT_NE(server->add_port(SW_SOCK_UDP, TEST_HOST, 0), nullptr);\n    ASSERT_EQ(server->create(), 0);\n\n    ASSERT_EQ(server->start(), -1);\n    auto startup_error = String(server->get_startup_error_message());\n    ASSERT_TRUE(startup_error.contains(\"require 'onTask' callback\"));\n    ASSERT_EQ(swoole_get_last_error(), SW_ERROR_SERVER_INVALID_CALLBACK);\n\n    server->onTask = [](Server *server, EventData *req) -> int { return SW_OK; };\n\n    ASSERT_EQ(server->start(), -1);\n    ASSERT_NE(strstr(server->get_startup_error_message(), \"require 'onReceive' callback\"), nullptr);\n\n    auto ori_log_level = swoole_get_log_level();\n    swoole_set_log_level(SW_LOG_NONE);\n\n    ASSERT_EQ(server->start(), -1);\n    auto startup_error2 = std::string(server->get_startup_error_message());\n    ASSERT_EQ(startup_error2, std::to_string(SW_ERROR_SERVER_INVALID_CALLBACK));\n    ASSERT_EQ(swoole_get_last_error(), SW_ERROR_SERVER_INVALID_CALLBACK);\n\n    swoole_set_log_level(ori_log_level);\n\n    server->onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; };\n\n    ASSERT_EQ(server->start(), -1);\n    ASSERT_NE(strstr(server->get_startup_error_message(), \"require 'onPacket' callback\"), nullptr);\n}\n\nTEST(server, abort_worker) {\n    Server *server = new Server(Server::MODE_BASE);\n    server->worker_num = 2;\n\n    auto port = server->add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_EQ(server->create(), 0);\n\n    swoole::Mutex lock(true);\n    lock.lock();\n\n    std::thread t1([&]() {\n        swoole_signal_block_all();\n\n        lock.lock();\n\n        network::SyncClient c1(SW_SOCK_TCP);\n        c1.connect(TEST_HOST, port->port);\n\n        char buf[1024];\n        auto rn = c1.recv(buf, sizeof(buf), MSG_WAITALL);\n        ASSERT_EQ(rn, 0);\n\n        c1.close();\n\n        network::SyncClient c2(SW_SOCK_TCP);\n        c2.connect(TEST_HOST, port->port);\n        c2.send(SW_STRL(\"info\"));\n        auto n = c2.recv(buf, sizeof(buf));\n        buf[n] = 0;\n        c2.close();\n\n        ASSERT_STREQ(buf, \"OK\");\n\n        server->shutdown();\n    });\n\n    server->onConnect = [](Server *server, DataHead *ev) {\n        if (ev->fd == 1) {\n            swoole_timer_after(100, [server](auto r1, auto r2) { kill(getpid(), SIGKILL); });\n        }\n    };\n\n    server->onReceive = [](Server *server, RecvData *req) -> int {\n        size_t count = 0;\n        SW_LOOP_N(SW_SESSION_LIST_SIZE) {\n            Session *session = server->get_session(i);\n            if (session->fd && session->id) {\n                count++;\n            }\n        }\n        EXPECT_EQ(count, 1);\n        if (count == 1) {\n            server->send(req->info.fd, \"OK\", 2);\n        } else {\n            server->send(req->info.fd, \"ERR\", 3);\n        }\n        return 0;\n    };\n\n    server->onWorkerStart = [&](Server *server, Worker *worker) {\n        if (worker->id == 0) {\n            lock.unlock();\n        }\n    };\n\n    ASSERT_EQ(server->start(), 0);\n    t1.join();\n}\n\nTEST(server, reactor_thread_pipe_writable) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 1;\n\n    String rdata(4 * 1024 * 1024);\n    rdata.append_random_bytes(rdata.capacity());\n\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n    port->open_length_check = true;\n    port->protocol.package_max_length = 8 * 1024 * 1024;\n    network::Stream::set_protocol(&port->protocol);\n\n    Mutex lock(true);\n    lock.lock();\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    std::thread t1([&]() {\n        swoole_signal_block_all();\n\n        lock.lock();\n\n        network::SyncClient c(SW_SOCK_TCP);\n        c.connect(TEST_HOST, port->port);\n        c.set_stream_protocol();\n        c.set_package_max_length(8 * 1024 * 1024);\n\n        uint32_t len = htonl(rdata.length);\n        c.send((char *) &len, sizeof(len));\n        c.send(rdata.str, rdata.length);\n\n        auto rbuf = new String(rdata.size + 1024);\n\n        uint32_t pkt_len;\n        ssize_t rn;\n\n        rn = c.recv((char *) &pkt_len, sizeof(pkt_len));\n        EXPECT_EQ(rn, sizeof(pkt_len));\n\n        rn = c.recv(rbuf->str, ntohl(pkt_len), MSG_WAITALL);\n        EXPECT_EQ(rn, rdata.length);\n\n        c.close();\n\n        EXPECT_MEMEQ(rbuf->str, rdata.str, rdata.length);\n        delete rbuf;\n\n        serv.shutdown();\n    });\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) {\n        lock.unlock();\n        usleep(300000);\n    };\n\n    serv.onReceive = [&](Server *serv, RecvData *req) -> int {\n        uint32_t len = htonl(rdata.length);\n        EXPECT_TRUE(req->info.flags & SW_EVENT_DATA_OBJ_PTR);\n        EXPECT_TRUE(serv->send(req->info.fd, &len, sizeof(len)));\n        EXPECT_TRUE(serv->send(req->info.fd, rdata.str, rdata.length));\n        EXPECT_MEMEQ(req->data + 4, rdata.str, rdata.length);\n\n        /**\n         * After using MessageBus::move_packet(), the data pointer will be out of the control of message_bus,\n         * and this part of the memory must be manually released; otherwise, a memory leak will occur.\n         */\n        char *data = serv->get_worker_message_bus()->move_packet();\n        EXPECT_NE(data, nullptr);\n        sw_free(data);\n\n        return SW_OK;\n    };\n\n    serv.start();\n    t1.join();\n}\n\nstatic void test_heartbeat_check(Server::Mode mode, bool single_thread = false) {\n    Server serv(mode);\n    serv.worker_num = 1;\n    serv.heartbeat_check_interval = 1;\n    serv.single_thread = single_thread;\n\n    swoole_set_print_backtrace_on_error(true);\n\n    std::unordered_map<std::string, bool> flags;\n    AsyncClient ac(SW_SOCK_TCP);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int { return SW_OK; };\n\n    serv.onStart = [port, &ac, &flags](Server *_serv) {\n        ac.on_connect([&](AsyncClient *ac) { flags[\"on_connect\"] = true; });\n\n        ac.on_close([_serv, &flags](AsyncClient *ac) {\n            flags[\"on_close\"] = true;\n            _serv->shutdown();\n        });\n\n        ac.on_error([&](AsyncClient *ac) { flags[\"on_error\"] = true; });\n\n        ac.on_receive([&](AsyncClient *ac, const char *data, size_t len) { flags[\"on_receive\"] = true; });\n\n        bool retval = ac.connect(TEST_HOST, port->get_port());\n        EXPECT_TRUE(retval);\n        flags[\"connected\"] = true;\n    };\n\n    serv.start();\n\n    ASSERT_TRUE(flags[\"connected\"]);\n    ASSERT_TRUE(flags[\"on_connect\"]);\n    ASSERT_FALSE(flags[\"on_error\"]);\n    ASSERT_FALSE(flags[\"on_receive\"]);\n    ASSERT_TRUE(flags[\"on_close\"]);\n}\n\nTEST(server, heartbeat_check_1) {\n    test_heartbeat_check(Server::MODE_BASE);\n}\n\nTEST(server, heartbeat_check_2) {\n    test_heartbeat_check(Server::MODE_PROCESS);\n}\n\nTEST(server, heartbeat_check_3) {\n    test_heartbeat_check(Server::MODE_THREAD);\n}\n\nTEST(server, heartbeat_check_4) {\n    test_heartbeat_check(Server::MODE_PROCESS);\n}\n\nstatic void test_close(Server::Mode mode, bool close_in_client, bool single_thread = false) {\n    Server serv(mode);\n    serv.worker_num = 1;\n    serv.single_thread = single_thread;\n\n    std::unordered_map<std::string, bool> flags;\n    AsyncClient ac(SW_SOCK_TCP);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onConnect = [&flags, close_in_client](Server *serv, DataHead *ev) { flags[\"server_on_connect\"] = true; };\n\n    serv.onReceive = [&flags, close_in_client](Server *serv, RecvData *req) {\n        serv->send(req->session_id(), req->data, req->length());\n        if (!close_in_client) {\n            serv->close(req->session_id());\n        }\n        flags[\"server_on_receive\"] = true;\n        return SW_OK;\n    };\n\n    serv.onClose = [&flags, close_in_client](Server *serv, DataHead *ev) {\n        if (!close_in_client) {\n            ASSERT_LT(ev->reactor_id, 0);\n        }\n        flags[\"server_on_close\"] = true;\n    };\n\n    serv.onWorkerStop = [&flags](Server *serv, Worker *worker) {\n        ASSERT_TRUE(flags[\"server_on_connect\"]);\n        ASSERT_TRUE(flags[\"server_on_receive\"]);\n        ASSERT_TRUE(flags[\"server_on_close\"]);\n    };\n\n    serv.onStart = [port, &ac, &flags, close_in_client](Server *_serv) {\n        ac.on_connect([&](AsyncClient *ac) {\n            flags[\"client_on_connect\"] = true;\n            ac->send(SW_STRL(TEST_STR));\n        });\n\n        ac.on_close([_serv, &flags](AsyncClient *ac) {\n            flags[\"client_on_close\"] = true;\n            swoole_timer_after(50, [_serv, ac](TIMER_PARAMS) { _serv->shutdown(); });\n        });\n\n        ac.on_error([&](AsyncClient *ac) { flags[\"client_on_error\"] = true; });\n\n        ac.on_receive([&](AsyncClient *ac, const char *data, size_t len) {\n            flags[\"client_on_receive\"] = true;\n            if (close_in_client) {\n                /**\n                 * When a client initiates a connection to its own port in the current process,\n                 * the epoll does not trigger a readable event upon executing close;\n                 * it is necessary to perform a shutdown first to trigger the event.\n                 */\n                ac->get_client()->shutdown(SHUT_RDWR);\n                ac->close();\n            }\n        });\n\n        bool retval = ac.connect(TEST_HOST, port->get_port());\n        EXPECT_TRUE(retval);\n        flags[\"client_connected\"] = true;\n    };\n\n    ASSERT_EQ(serv.start(), SW_OK);\n\n    ASSERT_TRUE(flags[\"client_connected\"]);\n    ASSERT_TRUE(flags[\"client_on_connect\"]);\n    ASSERT_FALSE(flags[\"client_on_error\"]);\n    ASSERT_TRUE(flags[\"client_on_receive\"]);\n    ASSERT_TRUE(flags[\"client_on_close\"]);\n}\n\nTEST(server, close_1) {\n    test_close(Server::MODE_PROCESS, false);\n}\n\nTEST(server, close_2) {\n    test_close(Server::MODE_BASE, false);\n}\n\nTEST(server, close_3) {\n    test_close(Server::MODE_THREAD, false);\n}\n\nTEST(server, close_4) {\n    test_close(Server::MODE_PROCESS, false, true);\n}\n\nTEST(server, close_5) {\n    test_close(Server::MODE_PROCESS, true);\n}\n\nTEST(server, close_6) {\n    test_close(Server::MODE_BASE, true);\n}\n\nTEST(server, close_7) {\n    test_close(Server::MODE_THREAD, true);\n}\n\nTEST(server, close_8) {\n    test_close(Server::MODE_PROCESS, true, true);\n}\n\nTEST(server, eof_check) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n    ASSERT_TRUE(port->is_stream());\n\n    port->set_eof_protocol(\"\\r\\n\", true);\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    std::unordered_map<std::string, bool> flags;\n    AsyncClient ac(SW_SOCK_TCP);\n\n    int count = 0;\n\n    serv.onWorkerStart = [&count, &flags, port, &ac](Server *serv, Worker *worker) {\n        ac.on_connect([&](AsyncClient *ac) { flags[\"on_connect\"] = true; });\n\n        ac.on_close([serv, &flags](AsyncClient *ac) {\n            flags[\"on_close\"] = true;\n            serv->shutdown();\n        });\n\n        ac.on_error([&](AsyncClient *ac) { flags[\"on_error\"] = true; });\n\n        ac.on_receive([&](AsyncClient *ac, const char *data, size_t len) {\n            flags[\"on_receive\"] = true;\n            ASSERT_MEMEQ(data, \"OK\", len);\n            count++;\n\n            if (count == 1) {\n                ac->send(\"hello world\\r\\n\");\n            } else if (count == 2) {\n                ac->send(\"hello world\\r\\nhello world\\r\\n\");\n            } else if (count == 3) {\n                ac->send(\"hello world\\r\\nhello world\\r\\nhello world\\r\\n\");\n            } else if (count == 4) {\n                ac->close();\n            }\n        });\n\n        bool retval = ac.connect(TEST_HOST, port->get_port());\n        EXPECT_TRUE(retval);\n        flags[\"connected\"] = true;\n    };\n\n    int recv_count = 0;\n\n    serv.onReceive = [&](Server *serv, RecvData *req) -> int {\n        serv->send(req->info.fd, \"OK\", 2);\n        recv_count++;\n        return SW_OK;\n    };\n\n    serv.onConnect = [&](Server *serv, DataHead *ev) { serv->send(ev->fd, \"OK\", 2); };\n\n    serv.start();\n\n    ASSERT_TRUE(flags[\"connected\"]);\n    ASSERT_TRUE(flags[\"on_connect\"]);\n    ASSERT_FALSE(flags[\"on_error\"]);\n    ASSERT_TRUE(flags[\"on_receive\"]);\n    ASSERT_TRUE(flags[\"on_close\"]);\n    ASSERT_TRUE(flags[\"on_close\"]);\n    ASSERT_EQ(recv_count, 3);\n}\n\nstatic void test_clean_worker(Server::Mode mode) {\n    Server serv(mode);\n    serv.worker_num = 2;\n\n    test::counter_init();\n\n    AsyncClient ac(SW_SOCK_TCP);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onConnect = [&ac](Server *serv, DataHead *ev) {\n        DEBUG() << \"server onConnect\\n\";\n        swoole_event_defer(\n            [serv, &ac](void *) {\n                DEBUG() << \"clean_worker_connections\\n\";\n                serv->clean_worker_connections(sw_worker());\n                DEBUG() << \"client shutdown\\n\";\n                ac.get_client()->shutdown();\n                serv->stop_async_worker(sw_worker());\n            },\n            nullptr);\n    };\n\n    serv.onReceive = [](Server *serv, RecvData *req) {\n        serv->send(req->info.fd, \"OK\", 2);\n        test::counter_incr(0, 1);\n        DEBUG() << \"server onReceive\\n\";\n        return SW_OK;\n    };\n\n    serv.onClose = [](Server *serv, DataHead *ev) { test::counter_incr(2, 1); };\n\n    serv.onWorkerStart = [](Server *serv, Worker *worker) {\n        ASSERT_EQ(serv->get_connection_num(), 0);\n        DEBUG() << \"worker#\" << worker->id << \" start\\n\";\n        if (test::counter_incr(1, 1) == 3) {\n            swoole_timer_after(100, [serv](TIMER_PARAMS) {\n                DEBUG() << \"server shutdown\\n\";\n                serv->shutdown();\n            });\n        }\n    };\n\n    serv.onWorkerStop = [](Server *serv, Worker *worker) { DEBUG() << \"worker#\" << worker->id << \" stop\\n\"; };\n\n    serv.onStart = [port, &ac](Server *_serv) {\n        DEBUG() << \"server is started\\n\";\n        swoole_timer_after(100, [port, _serv, &ac](TIMER_PARAMS) {\n            ac.on_connect([&](AsyncClient *ac) { ac->send(SW_STRL(TEST_STR)); });\n\n            ac.on_close([_serv](AsyncClient *ac) { DEBUG() << \"client onClose\\n\"; });\n\n            ac.on_error([](AsyncClient *ac) { swoole_warning(\"connect failed, error=%d\", swoole_get_last_error()); });\n\n            ac.on_receive([](AsyncClient *ac, const char *data, size_t len) {\n                DEBUG() << \"received\\n\";\n                test::counter_incr(3, 1);\n            });\n\n            bool retval = ac.connect(TEST_HOST, port->get_port());\n            EXPECT_TRUE(retval);\n            DEBUG() << \"client is connected\\n\";\n        });\n    };\n\n    ASSERT_EQ(serv.start(), SW_OK);\n    ASSERT_EQ(test::counter_get(0), 0);  // Server on_receive\n    ASSERT_EQ(test::counter_get(1), 3);  // worker start\n    ASSERT_EQ(test::counter_get(2), 0);  // Server on_close\n    ASSERT_EQ(test::counter_get(3), 0);  // Client on_receive\n}\n\nTEST(server, clean_worker_1) {\n    test_clean_worker(Server::MODE_BASE);\n}\n\nTEST(server, clean_worker_2) {\n    test_clean_worker(Server::MODE_THREAD);\n}\n\nstruct Options {\n    bool reload_async = true;\n    bool worker_exit_callback = false;\n    bool test_shutdown_event = false;\n};\n\nstatic long test_timer;\n\nstatic void test_kill_worker(Server::Mode mode, const Options &options) {\n    Server serv(mode);\n    serv.worker_num = 2;\n    serv.reload_async = options.reload_async;\n\n    test::counter_init();\n    int *counter = test::counter_ptr();\n\n    Mutex lock(true);\n    lock.lock();\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onConnect = [counter](Server *serv, DataHead *ev) {\n        counter[4] = ev->fd;\n        counter[5] = sw_worker()->id;\n    };\n\n    serv.onReceive = [counter](Server *serv, RecvData *req) {\n        serv->send(req->info.fd, \"OK\", 2);\n        sw_atomic_fetch_add(&counter[0], 1);\n\n        return SW_OK;\n    };\n\n    serv.onWorkerStop = [counter](Server *_serv, Worker *worker) {\n        _serv->close(counter[4]);\n        _serv->drain_worker_pipe();\n        DEBUG() << \"worker#\" << worker->id << \" stop \\n\";\n    };\n\n    serv.onClose = [counter](Server *serv, DataHead *ev) { sw_atomic_fetch_add(&counter[2], 1); };\n\n    serv.onWorkerStart = [counter, &options](Server *serv, Worker *worker) {\n        auto c = sw_atomic_fetch_add(&counter[1], 1);\n        DEBUG() << \"worker#\" << worker->id << \" start \\n\";\n        if (options.worker_exit_callback) {\n            test_timer = swoole_timer_tick(5000, [counter](TIMER_PARAMS) {});\n        }\n\n        if (c < 2 && options.test_shutdown_event && worker->id == 0) {\n            EventData ev;\n            ev.info = {};\n            ev.info.type = SW_SERVER_EVENT_SHUTDOWN;\n            ev.info.len = 0;\n            DEBUG() << \"send SW_SERVER_EVENT_SHUTDOWN packet\\n\";\n            ASSERT_GT(serv->send_to_worker_from_worker(1, &ev, SW_PIPE_MASTER | SW_PIPE_NONBLOCK), 0);\n        }\n    };\n\n    if (options.worker_exit_callback) {\n        serv.onWorkerExit = [counter](Server *_serv, Worker *worker) {\n            swoole_timer_clear(test_timer);\n            test::counter_incr(6, 1);\n            DEBUG() << \"worker#\" << worker->id << \" exit \\n\";\n        };\n    }\n\n    serv.onStart = [&lock, &options](Server *_serv) {\n        if (!sw_worker()) {\n            ASSERT_FALSE(_serv->kill_worker(-1));\n        }\n        lock.unlock();\n    };\n\n    std::thread t([&]() {\n        swoole_signal_block_all();\n\n        lock.lock();\n\n        usleep(50000);\n\n        network::SyncClient c(SW_SOCK_TCP);\n        EXPECT_TRUE(c.connect(TEST_HOST, port->port));\n\n        EXPECT_EQ(c.send(SW_STRL(TEST_STR)), strlen(TEST_STR));\n\n        String rbuf(1024);\n        auto rn = c.recv(rbuf.str, rbuf.size);\n        EXPECT_EQ(rn, 2);\n\n        serv.kill_worker(1 - counter[5]);\n\n        rn = c.recv(rbuf.str, rbuf.size);\n        EXPECT_EQ(rn, 0);\n\n        sw_atomic_fetch_add(&counter[3], 1);\n\n        usleep(50000);\n\n        serv.shutdown();\n    });\n\n    ASSERT_EQ(serv.start(), SW_OK);\n    t.join();\n\n    ASSERT_EQ(counter[0], 1);                                    // Client receive\n    ASSERT_EQ(counter[1], options.test_shutdown_event ? 4 : 3);  // Server onWorkerStart\n    ASSERT_EQ(counter[2], 1);                                    // Server onClose\n    ASSERT_EQ(counter[3], 1);                                    // Client close\n    // counter[4] is the client fd\n    // counter[5] is the worker id\n    // counter[6] is the worker exit count\n\n    if (options.worker_exit_callback) {\n        ASSERT_EQ(counter[6], 3);  // Worker exit\n    }\n}\n\nTEST(server, kill_worker_1) {\n    Options opt;\n    opt.reload_async = true;\n    test_kill_worker(Server::MODE_BASE, opt);\n}\n\nTEST(server, kill_worker_2) {\n    Options opt;\n    opt.reload_async = true;\n    test_kill_worker(Server::MODE_PROCESS, opt);\n}\n\nTEST(server, kill_worker_3) {\n    Options opt;\n    opt.reload_async = true;\n    test_kill_worker(Server::MODE_THREAD, opt);\n}\n\nTEST(server, kill_worker_4) {\n    Options opt;\n    opt.reload_async = false;\n    test_kill_worker(Server::MODE_BASE, opt);\n}\n\nTEST(server, kill_worker_5) {\n    Options opt;\n    opt.reload_async = false;\n    test_kill_worker(Server::MODE_PROCESS, opt);\n}\n\nTEST(server, kill_worker_6) {\n    Options opt;\n    opt.reload_async = false;\n    test_kill_worker(Server::MODE_THREAD, opt);\n}\n\nTEST(server, worker_exit) {\n    Options opt;\n    opt.worker_exit_callback = true;\n    test_kill_worker(Server::MODE_PROCESS, opt);\n}\n\nTEST(server, shutdown_event) {\n    Options opt;\n    opt.test_shutdown_event = true;\n    test_kill_worker(Server::MODE_PROCESS, opt);\n}\n\nstatic void test_kill_self(Server::Mode mode) {\n    Server serv(mode);\n    serv.worker_num = 2;\n\n    int *counter = (int *) sw_mem_pool()->alloc(sizeof(int) * 6);\n\n    swoole::Mutex lock(true);\n    lock.lock();\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    serv.onConnect = [counter](Server *serv, DataHead *ev) {\n        counter[4] = ev->fd;\n        counter[5] = sw_worker()->id;\n    };\n\n    serv.onReceive = [counter](Server *serv, RecvData *req) {\n        serv->send(req->info.fd, \"OK\", 2);\n        sw_atomic_fetch_add(&counter[0], 1);\n\n        return SW_OK;\n    };\n\n    serv.onWorkerStop = [counter](Server *_serv, Worker *worker) { _serv->close(counter[4]); };\n\n    serv.onClose = [counter](Server *serv, DataHead *ev) { sw_atomic_fetch_add(&counter[2], 1); };\n\n    serv.onWorkerStart = [counter](Server *_serv, Worker *worker) { sw_atomic_fetch_add(&counter[1], 1); };\n\n    serv.onStart = [&lock](Server *_serv) {\n        if (!sw_worker()) {\n            ASSERT_FALSE(_serv->kill_worker(-1));\n        }\n        lock.unlock();\n    };\n\n    std::thread t([&]() {\n        swoole_signal_block_all();\n\n        lock.lock();\n\n        usleep(50000);\n\n        network::SyncClient c(SW_SOCK_TCP);\n        EXPECT_TRUE(c.connect(TEST_HOST, port->port));\n\n        EXPECT_EQ(c.send(SW_STRL(TEST_STR)), strlen(TEST_STR));\n\n        String rbuf(1024);\n        auto rn = c.recv(rbuf.str, rbuf.size);\n        EXPECT_EQ(rn, 2);\n\n        serv.kill_worker(counter[5]);\n\n        rn = c.recv(rbuf.str, rbuf.size);\n        EXPECT_EQ(rn, 0);\n\n        sw_atomic_fetch_add(&counter[3], 1);\n\n        usleep(50000);\n\n        serv.shutdown();\n    });\n\n    ASSERT_EQ(serv.start(), SW_OK);\n    t.join();\n\n    ASSERT_EQ(counter[0], 1);  // Client receive\n    ASSERT_EQ(counter[1], 3);  // Server onWorkerStart\n    ASSERT_EQ(counter[2], 1);  // Server onClose\n    ASSERT_EQ(counter[3], 1);  // Client close\n}\n\nTEST(server, kill_self) {\n    test_kill_self(Server::MODE_BASE);\n}\n\nTEST(server, no_idle_worker) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 4;\n    serv.dispatch_mode = 3;\n\n    swoole_set_log_file(TEST_LOG_FILE);\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    Mutex *lock = new Mutex(true);\n    lock->lock();\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    thread t1;\n    serv.onStart = [&lock, &t1](Server *serv) {\n        t1 = thread([=]() {\n            swoole_signal_block_all();\n\n            lock->lock();\n\n            ListenPort *port = serv->get_primary_port();\n\n            network::SyncClient c(SW_SOCK_TCP);\n            c.connect(TEST_HOST, port->port);\n\n            SW_LOOP_N(1024) {\n                c.send(packet, strlen(packet));\n            }\n\n            sleep(3);\n\n            c.close();\n\n            kill(serv->gs->master_pid, SIGTERM);\n        });\n    };\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock->unlock(); };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        usleep(10000);\n        return SW_OK;\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n\n    t1.join();\n    delete lock;\n\n    auto log = swoole::file_get_contents(TEST_LOG_FILE);\n    ASSERT_TRUE(log->contains(\"No idle worker is available\"));\n\n    remove(TEST_LOG_FILE);\n}\n\nTEST(server, no_idle_task_worker) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 1;\n    serv.task_worker_num = 4;\n    serv.dispatch_mode = 3;\n\n    swoole_set_log_file(TEST_LOG_FILE);\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    Mutex *lock = new Mutex(true);\n    lock->lock();\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    thread t1;\n    serv.onStart = [&lock, &t1](Server *serv) {\n        t1 = thread([=]() {\n            swoole_signal_block_all();\n\n            lock->lock();\n\n            ListenPort *port = serv->get_primary_port();\n\n            network::SyncClient c(SW_SOCK_TCP);\n            c.connect(TEST_HOST, port->port);\n            c.send(packet, strlen(packet));\n\n            sleep(3);\n            c.close();\n\n            kill(serv->gs->master_pid, SIGTERM);\n        });\n    };\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock->unlock(); };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        SW_LOOP_N(1024) {\n            int _dst_worker_id = -1;\n            EventData buf{};\n            Server::task_pack(&buf, packet, strlen(packet));\n            buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_CALLBACK);\n            EXPECT_TRUE(serv->task(&buf, &_dst_worker_id));\n        }\n        return SW_OK;\n    };\n\n    serv.onTask = [](Server *serv, EventData *task) -> int {\n        EXPECT_EQ(string(task->data, task->info.len), string(packet));\n        usleep(10000);\n        return 0;\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n\n    t1.join();\n    delete lock;\n\n    auto log = swoole::file_get_contents(TEST_LOG_FILE);\n    ASSERT_TRUE(log->contains(\"No idle task worker is available\"));\n\n    remove(TEST_LOG_FILE);\n}\n\nstatic void test_conn_overflow(Server::Mode mode, bool send_yield) {\n    Server serv(mode);\n    serv.worker_num = 1;\n    serv.send_yield = send_yield;\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    test::counter_init();\n    auto counter = test::counter_ptr();\n\n    Mutex *lock = new Mutex(true);\n    lock->lock();\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    thread t1;\n    serv.onStart = [&lock, &t1](Server *serv) {\n        t1 = thread([=]() {\n            swoole_signal_block_all();\n\n            lock->lock();\n\n            ListenPort *port = serv->get_primary_port();\n\n            network::SyncClient c(SW_SOCK_TCP);\n            c.connect(TEST_HOST, port->port);\n            c.send(packet, strlen(packet));\n            char buf[1024];\n            c.recv(buf, sizeof(buf));\n            c.close();\n\n            kill(serv->gs->master_pid, SIGTERM);\n        });\n    };\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) {\n        if (worker->id == 0) {\n            lock->unlock();\n        }\n        test::counter_incr(3);\n        DEBUG() << \"onWorkerStart: id=\" << worker->id << \"\\n\";\n    };\n\n    serv.onReceive = [counter, send_yield](Server *serv, RecvData *req) -> int {\n        auto sid = req->session_id();\n        auto conn = serv->get_connection_by_session_id(sid);\n        conn->overflow = 1;\n\n        EXPECT_FALSE(serv->send(sid, SW_STRL(TEST_STR)));\n        EXPECT_ERREQ(send_yield ? SW_ERROR_OUTPUT_SEND_YIELD : SW_ERROR_OUTPUT_BUFFER_OVERFLOW);\n\n        counter[0] = 1;\n\n        swoole_timer_after(100, [serv, sid](TIMER_PARAMS) { serv->close(sid); });\n\n        return SW_OK;\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n\n    t1.join();\n    delete lock;\n    ASSERT_EQ(counter[0], 1);\n    ASSERT_EQ(counter[3], 1);\n}\n\nTEST(server, overflow_1) {\n    test_conn_overflow(Server::MODE_BASE, false);\n}\n\nTEST(server, overflow_2) {\n    test_conn_overflow(Server::MODE_PROCESS, false);\n}\n\nTEST(server, overflow_3) {\n    test_conn_overflow(Server::MODE_BASE, true);\n}\n\nTEST(server, overflow_4) {\n    test_conn_overflow(Server::MODE_PROCESS, true);\n}\n\nTEST(server, send_timeout) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    test::counter_init();\n    auto counter = test::counter_ptr();\n\n    Mutex *lock = new Mutex(true);\n    lock->lock();\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    port->max_idle_time = 1;\n\n    String wbuf(2 * 1024 * 1024);\n    wbuf.append_random_bytes(2 * 1024 * 1024, false);\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    thread t1;\n    serv.onStart = [&lock, &t1, &wbuf](Server *serv) {\n        t1 = thread([=]() {\n            swoole_signal_block_all();\n\n            lock->lock();\n\n            ListenPort *port = serv->get_primary_port();\n\n            network::SyncClient c(SW_SOCK_TCP);\n            c.connect(TEST_HOST, port->port);\n            c.send(packet, strlen(packet));\n\n            String rbuf(3 * 1024 * 1024);\n\n            auto rn = c.recv(rbuf.str, 1024);\n            EXPECT_EQ(rn, 1024);\n            rbuf.length += 1024;\n\n            sleep(2);\n\n            while (true) {\n                rn = c.recv(rbuf.str + rbuf.length, rbuf.size - rbuf.length);\n                if (rn <= 0) {\n                    break;\n                }\n                rbuf.length += rn;\n            }\n\n            EXPECT_MEMEQ(rbuf.str, wbuf.str, rbuf.length);\n            c.close();\n\n            kill(serv->gs->master_pid, SIGTERM);\n        });\n    };\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) {\n        if (worker->id == 0) {\n            lock->unlock();\n        }\n        test::counter_incr(3);\n        DEBUG() << \"onWorkerStart: id=\" << worker->id << \"\\n\";\n    };\n\n    serv.onReceive = [&wbuf](Server *serv, RecvData *req) -> int {\n        auto sid = req->session_id();\n        auto conn = serv->get_connection_by_session_id(sid);\n\n        swoole_timer_del(conn->socket->recv_timer);\n        conn->socket->recv_timer = nullptr;\n        conn->socket->set_buffer_size(65536);\n\n        EXPECT_TRUE(serv->send(sid, wbuf.str, wbuf.length));\n\n        test::counter_incr(0);\n\n        return SW_OK;\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n\n    t1.join();\n    delete lock;\n    ASSERT_EQ(counter[0], 1);\n    ASSERT_EQ(counter[3], 1);\n}\n\nstatic void test_max_request(Server::Mode mode) {\n    Server serv(mode);\n    serv.worker_num = 2;\n    serv.max_request = 128;\n\n    Mutex *lock = new Mutex(true);\n    lock->lock();\n\n    ASSERT_NE(serv.add_port(SW_SOCK_TCP, TEST_HOST, 0), nullptr);\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    thread t1;\n    serv.onStart = [&lock, &t1](Server *serv) {\n        t1 = thread([=]() {\n            swoole_signal_block_all();\n            lock->lock();\n            ListenPort *port = serv->get_primary_port();\n\n            auto client_fn = [&]() {\n                network::SyncClient c(SW_SOCK_TCP);\n                c.connect(TEST_HOST, port->port);\n\n                SW_LOOP_N(128) {\n                    if (c.send(packet, strlen(packet)) < 0) {\n                        break;\n                    }\n                    usleep(1000);\n                }\n                c.close();\n            };\n\n            SW_LOOP_N(8) {\n                client_fn();\n                usleep(10000);\n            }\n\n            sleep(1);\n\n            serv->shutdown();\n        });\n    };\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) {\n        lock->unlock();\n        test::counter_incr(0);\n    };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int { return SW_OK; };\n\n    ASSERT_EQ(serv.start(), 0);\n\n    t1.join();\n    delete lock;\n\n    ASSERT_GE(test::counter_get(0), 8);\n}\n\nTEST(server, max_request_1) {\n    test_max_request(Server::MODE_PROCESS);\n}\n\nTEST(server, max_request_2) {\n    test_max_request(Server::MODE_THREAD);\n}\n\nTEST(server, watermark) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 2;\n\n    Mutex *lock = new Mutex(true);\n    lock->lock();\n\n    String wbuf;\n    wbuf.append_random_bytes(2 * 1024 * 1024);\n\n    auto port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_NE(port, nullptr);\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    port->get_socket()->set_buffer_size(65536);\n    port->buffer_high_watermark = 1024 * 1024;\n    port->buffer_low_watermark = 65536;\n\n    thread t1;\n    serv.onStart = [&lock, &t1](Server *serv) {\n        t1 = thread([=]() {\n            swoole_signal_block_all();\n            lock->lock();\n            ListenPort *port = serv->get_primary_port();\n\n            network::SyncClient c(SW_SOCK_TCP);\n            c.connect(TEST_HOST, port->port);\n            c.get_client()->get_socket()->set_buffer_size(65536);\n            c.send(packet, strlen(packet));\n            usleep(1000);\n\n            String rbuf(2 * 1024 * 1024);\n            while (rbuf.length < rbuf.size) {\n                auto rn = c.recv(rbuf.str + rbuf.length, 65536);\n                usleep(10000);\n                if (rn <= 0) {\n                    break;\n                }\n                rbuf.length += rn;\n            }\n\n            sleep(1);\n            c.close();\n            serv->shutdown();\n        });\n    };\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) {\n        lock->unlock();\n        test::counter_incr(0);\n    };\n\n    serv.onReceive = [&wbuf](Server *serv, RecvData *req) -> int {\n        EXPECT_TRUE(serv->send(req->session_id(), wbuf.str, wbuf.length));\n        return SW_OK;\n    };\n\n    serv.onBufferEmpty = [](Server *serv, DataHead *ev) { test::counter_incr(1); };\n\n    serv.onBufferFull = [](Server *serv, DataHead *ev) { test::counter_incr(2); };\n\n    ASSERT_EQ(serv.start(), 0);\n\n    t1.join();\n    delete lock;\n\n    ASSERT_GE(test::counter_get(1), 1);\n    ASSERT_GE(test::counter_get(2), 1);\n}\n\nTEST(server, discard_data) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 2;\n    serv.dispatch_mode = 3;\n    serv.discard_timeout_request = true;\n    serv.disable_notify = true;\n\n    swoole_set_log_file(TEST_LOG_FILE);\n    swoole_set_log_level(SW_LOG_WARNING);\n\n    Mutex *lock = new Mutex(true);\n    lock->lock();\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    String rdata;\n    rdata.append_random_bytes(8192);\n\n    thread t1;\n    serv.onStart = [&lock, &t1, &rdata](Server *serv) {\n        t1 = thread([&lock, &rdata, serv]() {\n            swoole_signal_block_all();\n\n            lock->lock();\n\n            ListenPort *port = serv->get_primary_port();\n\n            network::SyncClient c(SW_SOCK_TCP);\n            c.connect(TEST_HOST, port->port);\n\n            SW_LOOP_N(128) {\n                c.send(rdata.str, rdata.length);\n                usleep(10);\n            }\n\n            sleep(1);\n\n            kill(serv->gs->master_pid, SIGTERM);\n        });\n    };\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock->unlock(); };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        usleep(10000);\n        serv->close(req->session_id());\n        return SW_OK;\n    };\n\n    ASSERT_EQ(serv.start(), 0);\n\n    t1.join();\n    delete lock;\n\n    auto log = file_get_contents(TEST_LOG_FILE);\n    DEBUG() << log->str << std::endl;\n    ASSERT_TRUE(log->contains(\"discard_data() (ERRNO 1007)\"));\n    remove(TEST_LOG_FILE);\n}\n\nTEST(server, pause_and_resume) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 2;\n\n    swoole_set_log_level(SW_LOG_TRACE);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n\n    Mutex lock(true);\n    lock.lock();\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    std::thread t1([&]() {\n        swoole_signal_block_all();\n        lock.lock();\n\n        usleep(1000);\n\n        network::SyncClient c(SW_SOCK_TCP);\n        ASSERT_TRUE(c.connect(TEST_HOST, port->port));\n        ASSERT_EQ(c.send(packet, strlen(packet)), strlen(packet));\n        char buf[1024];\n        auto t1 = microtime();\n        ASSERT_EQ(c.recv(buf, sizeof(buf)), strlen(packet) + 8);\n        auto t2 = microtime();\n        ASSERT_GE(t2 - t1, 0.048);  // Ensure that the pause and resume took some time\n        string resp = string(\"Server: \") + string(packet);\n        ASSERT_MEMEQ(buf, resp.c_str(), resp.length());\n        c.close();\n\n        usleep(1000);\n        DEBUG() << \"shutdown\\n\";\n        serv.shutdown();\n    });\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); };\n\n    serv.onConnect = [](Server *serv, DataHead *ev) {\n        auto session_id = ev->fd;\n        DEBUG() << \"onConnect: fd=\" << session_id << \", reactor_id=\" << ev->reactor_id << std::endl;\n        ASSERT_TRUE(serv->feedback(serv->get_connection_by_session_id(session_id), SW_SERVER_EVENT_PAUSE_RECV));\n        DEBUG() << \"pause recv ok, session_id=\" << session_id << std::endl;\n        swoole_timer_after(50, [ev, serv, session_id](TIMER_PARAMS) {\n            ASSERT_TRUE(serv->feedback(serv->get_connection_by_session_id(session_id), SW_SERVER_EVENT_RESUME_RECV));\n            DEBUG() << \"resume recv ok, session_id=\" << session_id << std::endl;\n        });\n    };\n\n    serv.onReceive = [](Server *serv, RecvData *req) -> int {\n        EXPECT_EQ(string(req->data, req->info.len), string(packet));\n        string resp = string(\"Server: \") + string(packet);\n        serv->send(req->info.fd, resp.c_str(), resp.length());\n        return SW_OK;\n    };\n\n    serv.start();\n    t1.join();\n}\n\nTEST(server, max_queued_bytes) {\n    Server serv(Server::MODE_PROCESS);\n    serv.worker_num = 2;\n    serv.max_queued_bytes = 65536;\n\n    test::counter_init();\n    swoole_set_log_level(SW_LOG_TRACE);\n\n    int buffer_size = 65536;\n    int send_count = 256;\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_TRUE(port);\n\n    Mutex lock(true);\n    lock.lock();\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    std::thread t1([&]() {\n        swoole_signal_block_all();\n        lock.lock();\n\n        usleep(1000);\n\n        String wbuf;\n        wbuf.append_random_bytes(buffer_size);\n\n        network::SyncClient c(SW_SOCK_TCP);\n        ASSERT_TRUE(c.connect(TEST_HOST, port->port));\n        SW_LOOP_N(send_count) {\n            ASSERT_EQ(c.send(wbuf.str, wbuf.length), wbuf.length);\n        }\n        char buf[1024];\n        ASSERT_EQ(c.recv(buf, sizeof(buf)), strlen(TEST_STR));\n        c.close();\n\n        usleep(1000);\n        DEBUG() << \"shutdown\\n\";\n        serv.shutdown();\n    });\n\n    serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); };\n\n    serv.onConnect = [](Server *serv, DataHead *ev) { usleep(100000); };\n\n    serv.onReceive = [=](Server *serv, RecvData *req) -> int {\n        if (test::counter_incr(0, req->info.len) == send_count * buffer_size) {\n            serv->send(req->session_id(), SW_STRL(TEST_STR));\n        }\n        return SW_OK;\n    };\n\n    serv.start();\n    t1.join();\n    ASSERT_EQ(send_count * buffer_size, test::counter_get(0));\n}\n\nTEST(server, ssl_matches_wildcard_name) {\n    // Test exact match\n    {\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"example.com\", \"example.com\"));\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"test.example.com\", \"test.example.com\"));\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"EXAMPLE.COM\", \"example.com\"));  // Case insensitive\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"example.com\", \"EXAMPLE.COM\"));  // Case insensitive\n    }\n\n    // Test no match\n    {\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"example.com\", \"example.org\"));\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"test.example.com\", \"test.example.org\"));\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"sub.example.com\", \"example.com\"));\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"example.com\", \"sub.example.com\"));\n    }\n\n    // Test wildcard in leftmost component\n    {\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"test.example.com\", \"*.example.com\"));\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"sub.example.com\", \"*.example.com\"));\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"TEST.example.com\", \"*.example.com\"));  // Case insensitive\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"test.example.com\", \"*.EXAMPLE.COM\"));  // Case insensitive\n    }\n\n    // Test wildcard with prefix\n    {\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"subtest.example.com\", \"sub*.example.com\"));\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"subthing.example.com\", \"sub*.example.com\"));\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"wrongtest.example.com\", \"sub*.example.com\"));\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"test.example.com\", \"sub*.example.com\"));\n    }\n\n    // Test wildcard in non-leftmost component (should fail)\n    {\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"test.example.com\", \"test.*.com\"));\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"test.sub.example.com\", \"test.*.example.com\"));\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"example.com\", \"example.*\"));\n    }\n\n    // Test wildcard with dot in prefix (should fail)\n    {\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"test.example.com\", \"test.*.com\"));\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"sub.test.example.com\", \"sub.*.example.com\"));\n    }\n\n    // Test multiple wildcards (only first one should be considered)\n    {\n        // EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"test.example.com\", \"*.*example.com\"));\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"test.sub.example.com\", \"*.*example.com\"));\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"test.example.com\", \"*.*\"));\n    }\n\n    // Test wildcard matching with dots between prefix and suffix\n    {\n        // These should fail because there's a dot between the prefix and suffix\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"test.sub.example.com\", \"*.example.com\"));\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"a.b.c.example.com\", \"*.example.com\"));\n\n        // This should pass because there's no dot in the wildcard portion\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"testexample.com\", \"*example.com\"));\n    }\n\n    // Test suffix length conditions\n    {\n        // Suffix longer than subject (should fail)\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"test.com\", \"*.example.com\"));\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"short\", \"*.verylongdomain.com\"));\n\n        // Suffix exactly matches subject length (edge case)\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"example.com\", \"*.example.com\"));\n    }\n\n    // Test empty strings and edge cases\n    {\n        // EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"\", \"\"));\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"example.com\", \"\"));\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"\", \"example.com\"));\n        // EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"\", \"*\"));\n        // EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"test\", \"*\"));\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"*\", \"*\"));  // Exact match\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"test\", \"*est\"));\n    }\n\n    // Test wildcard at beginning with no prefix\n    {\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"test.example.com\", \"*.example.com\"));\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"example.com\", \"*example.com\"));\n        // EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"test.example.com\", \"*test.example.com\"));\n    }\n\n    // Test wildcard at end with no suffix\n    {\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"example.com\", \"example*\"));\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"example.com\", \"example.*\"));\n    }\n\n    // Test practical examples from real-world scenarios\n    {\n        // Common wildcard cert patterns\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"www.example.com\", \"*.example.com\"));\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"api.example.com\", \"*.example.com\"));\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"login.example.com\", \"*.example.com\"));\n\n        // Subdomain matching\n        EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"sub.api.example.com\", \"*.example.com\"));\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"sub.api.example.com\", \"*.api.example.com\"));\n\n        // IP addresses (wildcards shouldn't work with IPs in practice)\n        // EXPECT_FALSE(ListenPort::ssl_matches_wildcard_name(\"192.168.1.1\", \"*.168.1.1\"));\n\n        // Partial wildcard matches\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"dev-server.example.com\", \"dev-*.example.com\"));\n        EXPECT_TRUE(ListenPort::ssl_matches_wildcard_name(\"staging-server.example.com\", \"*-server.example.com\"));\n    }\n}\n\nTEST(server, wait_other_worker) {\n    Server serv(Server::MODE_BASE);\n    serv.worker_num = 1;\n    serv.task_worker_num = 2;\n    test::counter_init();\n\n    auto port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0);\n    ASSERT_NE(port, nullptr);\n\n    serv.onWorkerStart = [](Server *serv, Worker *worker) {\n        test::counter_incr(1);\n        DEBUG() << \"onWorkerStart: id=\" << worker->id << \"\\n\";\n    };\n\n    ASSERT_EQ(serv.create(), SW_OK);\n\n    ExitStatus fake_exit(getpid(), 0);\n    auto pool = serv.get_task_worker_pool();\n    auto worker = serv.get_worker(2);\n    worker->pid = getpid();\n    pool->add_worker(worker);\n    serv.wait_other_worker(pool, fake_exit);\n\n    test::wait_all_child_processes();\n\n    ASSERT_EQ(test::counter_get(1), 1);\n}\n"
  },
  {
    "path": "docs/API.md",
    "content": "## Basic C API\nThese functions enable you to obtain the spool version and insert some hooks into the spool,\nsuch as `SW_GLOBAL_HOOK_BEFORE_SERVER_START`. The set function is called when the server starts.\n\nThe header file is `swoole_api.h`.\nThe symbol of this file will export the function name in `C` format, not `C++`.\nSimilarly, functions in the `Coroutine API` are also exported as `C` functions.\n\n- `swoole_version()` Get the current version of Swoole\n- `swoole_add_hook()` Add a hook function to the specified hook point\n\n## Coroutine C API\n\nThese functions are similar to the C function in `unistd.h`, for example, read corresponds to\n`swoole_coroutine_read()` in the coroutine environment.\n\nIn addition to the `POSIX API` like functions, some additional functions can be provided.\nFor example, the `swoole_coroutine_get_current_id()` can obtain the ID of the coroutine,\nwhich is equivalent to the `getpid()` of the process.\n\nThe function similar to creating a process or thread is `swoole_coroutine_create()`,\nwhich can be used to create a coroutine.\n\nThe header file is `swoole_coroutine_api.h`.\n\n## C MACRO HOOK\nUse `C/C++` macros to automatically replace read/write and other synchronously blocked unistd C functions as non blocking\ncoroutine functions (starts with `swoole_coroutine_`).\nThis method enables C++ network client code to be used in the swoole coroutine environment.\n\nIncluding network and file system:\n\n- `swoole_file_hook.h` Replace file system related functions, such as `open()`, `mkdir()`, etc\n- `swoole_socket_hook.h` Replace network related functions, such as `socket()`, `recv()`, `send()`, etc\n\n## C++ API\n\nIn addition to the above header files, others are only used in `C++` code.\n\n### Independent header file\n\nThese header files have no dependencies. They can only contain C standard library header files, C++ header files,\nand platform related or basic library header files.\n\n- `swoole_config.h`: Buffer size, string, constant, etc\n- `swoole_version.h`: Swoole version information\n- `swoole_atomic.h`: Atomic operations\n- `swoole_asm_context.h`: Assembly context switching\n- `swoole_util.h`: Common utility functions\n- `swoole_log.h`: Log related functions\n- `swoole_memory.h`: Memory pool related functions\n- `swoole_base64.h`: Base64 encoding and decoding\n- `swoole_error.h`: Error code related functions\n- and more ...\n\n### Core header file\n- `swoole.h`: Core class and function declarations\n- `swoole_string.h`: String class and function declarations\n- `swoole_coroutine.h`: Coroutine class and function declarations\n- `swoole_async.h`: Asynchronous IO class and function declarations\n- `swoole_process_pool.h`: Process pool class and function declarations\n- `swoole_signal.h`: Signal handling class and function declarations\n- `swoole_timer.h`: Timer class and function declarations\n- `swoole_reactor.h`: Reactor class and function declarations\n- and more ...\n\n### Facade header file\nThese header files depend on the core header files and provide a higher level of abstraction.\n\n- `swoole_server.h`: Server side class and function declarations\n- `swoole_client.h`: Client side class and function declarations\n- `swoole_coroutine_socket.h`: Coroutine socket class and function declarations\n- `swoole_coroutine_system.h`: Coroutine system API class and function declarations\n- `swoole_coroutine_channel.h`: Coroutine channel class and function declarations\n"
  },
  {
    "path": "docs/CHANGELOG.md",
    "content": "# Swoole Changelog\n\n## 2024-12-16 v6.0.0\n# ✨ New Feature:\n- Added multi-threading support, require the ZTS version of PHP. Add `--enable-swoole-thread` option to the configure command to activate it.\n- Added a new thread class `Swoole\\Thread`. @matyhtf\n- Introduced thread lock `Swoole\\Thread\\Lock`. @matyhtf\n- Added thread atomic counter `Swoole\\Thread\\Atomic`, `Swoole\\Thread\\Atomic\\Long`. @matyhtf\n- Added safe concurrent containers `Swoole\\Thread\\Map`, `Swoole\\Thread\\ArrayList`, `Swoole\\Thread\\Queue`. @matyhtf\n- The file asynchronous operation supports using `io_uring` as the underlying engine for file asynchronous operations. When liburing is installed and Swoole is compiled with the --enable-iouring option, the asynchronous operations of functions such as file_get_contents, file_put_contents, fopen, fclose, fread, fwrite, mkdir, unlink, fsync, fdatasync, rename, fstat, lstat, and filesize will be implemented by io_uring. @matyhtf @NathanFreeman\n- Upgraded `Boost Context` to version 1.84. Now, Loongson CPUs can also support coroutines. @NathanFreeman\n- Added `Swoole\\Thread\\Map::find()` method. @matyhtf\n- Added `Swoole\\Thread\\ArrayList::find()` method. @matyhtf\n- Added `Swoole\\Thread\\ArrayList::offsetUnset()` method. @matyhtf\n- Added `Swoole\\Process::getAffinity()` method. @matyhtf\n- Added `Swoole\\Thread::setName()` method. @matyhtf\n- Added `Swoole\\Thread::setAffinity()` method. @matyhtf\n- Added `Swoole\\Thread::getAffinity()` method. @matyhtf\n- Added `Swoole\\Thread::setPriority()` method. @matyhtf\n- Added `Swoole\\Thread::getPriority()` method. @matyhtf\n- Added `Swoole\\Thread::gettid()` method.\n- The file asynchronous engine `iouring` supports multi-threaded polling mode `IORING_SETUP_SQPOLL`. @NathanFreeman\n- Added `iouring_workers` to modify the number of `iouring` threads. @NathanFreeman\n- Added `iouring_flags` to support modifying the `iouring` working mode. @NathanFreeman\n- Added `Swoole\\Thread\\Barrier` for multi-thread synchronization barrier. @matyhtf\n- Added new function and class to set cookies. @matyhtf @NathanFreeman\n- Added `non-blocking, reentrant coroutine mutex lock`, which can be used between processes/threads without blocking them. @NathanFreeman\n- `Swoole\\Coroutine\\Socket::getOption()` supports the `TCP_INFO` option. @matyhtf\n- `Swoole\\Client` synchronous blocking client supports `http` proxy. @matyhtf\n- Added asynchronous non-blocking `TCP/UDP/Unix socket` client `Swoole\\Async\\Client`. @matyhtf\n- Optimized the `Swoole\\Redis\\Server::format()` method to support zero-copy memory, support `redis` nested structure. @matyhtf\n- Supports the high-performance compression tool `Zstd`. You only need to add `--enable-zstd` when compiling `Swoole`, and then `zstd` can be used to compress or decode responses between the `http` client and server. @NathanFreeman\n\n# 🐛 Bug Fixed：\n- Fixed the issue where installation via `pecl` was not possible. @remicollet\n- Fixed the bug where setting `keepalive` was not possible for `Swoole\\Coroutine\\FastCGI\\Client`. @NathanFreeman\n- Fixed the issue where exceeding the `max_input_vars` would throw an error, causing the process to restart repeatedly. @NathanFreeman\n- Fixed unknown issues caused by using `Swoole\\Event::wait()` within a coroutine. @matyhtf\n- Fixed the problem where `proc_open` does not support pty in coroutine mode. @matyhtf\n- Fixed segmentation fault issues with `pdo_sqlite` on PHP 8.3. @NathanFreeman\n- Fixed unnecessary warnings during the compilation of `Swoole`. @Appla @NathanFreeward\n- Fixed the error thrown by zend_fetch_resource2_ex when `STDOUT/STDERR` are already closed. @Appla @matyhtf\n- Fixed ineffective `set_tcp_nodelay` configuration. @matyhtf\n- Fixed the occasional unreachable branch issue during file upload. @NathanFreeman\n- Fixed the problem where setting `dispatch_func` would cause PHP's internals to throw errors. @NathanFreeman\n- Fixed the deprecation of AC_PROG_CC_C99 in autoconf >= 2.70. @petk\n- Capture exceptions when thread creation fails. @matyhtf\n- Fixed the undefined problem with `_tsrm_ls_cache`. @jingjingxyk\n- Fixed the fatal compile error with `GCC 14`. @remicollet\n- Fixed the dynamic property issue in `Swoole\\Http2\\Request`. @guandeng\n- Fixed the occasional resource unavailability issue in the `pgsql` coroutine client. @NathanFreeman\n- Fixed the issue of 503 errors due to not resetting related parameters during process restart. @matyhtf\n- Fixed the inconsistency between `$request->server['request_method']` and `$request->getMethod()` when `HTTP2` is enabled. @matyhtf\n- Fixed incorrect `content-type` when uploading files. @matyhtf\n- Fixed code errors in the `http2` coroutine client. @matyhtf\n- Fixed the missing `worker_id` property in `Swoole\\Server`. @cjavad\n- Fixed errors related to `brotli` in `config.m4`. @fundawang\n- Fixed the invalid `Swoole\\Http\\Response::create` under multi-threading. @matyhtf\n- Fixed compilation errors in the `macos` environment. @matyhtf\n- Fixed the issue of threads not being able to exit safely. @matyhtf\n- Fixed the issue where the static variable for response time returned by `Swoole\\Http\\Response` in multi-threaded mode was not generated separately for each thread. @matyhtf @NathanFreeman\n- Fixed `Fatal error` issue caused by `PHP-8.4`'s `timeout` feature in ZTS mode. @matyhtf\n- Fixed compatibility issue with the `exit()` `hook` function for `PHP-8.4`. @remicollet\n- Fixed the issue where `Swoole\\Thread::getNativeId()` did not work in `cygwin`. @matyhtf\n- Fixed the issue causing `SIGSEGV` in `Swoole\\Coroutine::getaddrinfo()` method. @matyhtf\n- Fixed the issue where the runtime TCP module did not support dynamically enabling SSL encryption. @matyhtf\n- Fixed the issue where the HTTP client had an incorrect timeout after running for a long time. @matyhtf\n- Fixed the problem where the mutex lock of `Swoole\\Table` could not be used before the process exited. @matyhtf\n- Fixed the failure of `Swoole\\Server::stop()` when using named parameters. @matyhtf\n- Fixed the crash caused by `Swoole\\Thread\\Map::toArray()` not copying the key. @matyhtf\n- Fixed the issue of being unable to delete nested numeric keys in `Swoole\\Thread\\Map`. @matyhtf\n\n# ⭐️ Kernel optimization：\n- Removed unnecessary checks for `socket structs`. @petk\n- Upgraded Swoole Library. @deminy\n- Added support for status code 451 in `Swoole\\Http\\Response`. @abnegate\n- Synchronized `file` operation code across different PHP versions. @NathanFreeman\n- Synchronized `pdo` operation code across different PHP versions. @NathanFreeman\n- Optimized the code for `Socket::ssl_recv()`. @matyhtf\n- Improved config.m4; some configurations can now set library locations via `pkg-config`. @NathanFreeman\n- Optimized the use of dynamic arrays during `request header parsing`. @NathanFreeman\n- Optimized file descriptor `fd` lifecycle issues in multi-threading mode. @matyhtf\n- Optimized some fundamental coroutine logic. @matyhtf\n- Upgraded the Oracle database version for CI testing. @gvenzl\n- Optimized the underlying logic of `sendfile`. @matyhtf\n- Replaced `PHP_DEF_HAVE` with `AC_DEFINE_UNQUOTED` in `config.m4`. @petk\n- Optimized the logic related to `heartbeat`, `shutdown`, and `stop` for the server in multi-threaded mode. @matyhtf\n- Optimized to avoid linking `librt` when `glibc` version is greater than 2.17. @matyhtf\n- Enhanced the HTTP client to accept duplicate request headers. @matyhtf\n- Optimized `Swoole\\Http\\Response::write()`. @matyhtf\n- `Swoole\\Http\\Response::write()` can now send HTTP/2 protocol. @matyhtf\n- Compatible with `PHP 8.4`. @matyhtf @NathanFreeman\n- Added the ability for asynchronous writing at the underlying socket level. @matyhtf\n- Optimized `Swoole\\Http\\Response`. @NathanFreeman\n- Improved underlying error messages. @matyhtf\n- Supported sharing PHP native sockets in multi-threaded mode. @matyhtf\n- Optimized static file service and fixed static file path error issues. @matyhtf\n- Multi-thread mode `SWOOLE_THREAD` supports restarting worker threads. @matyhtf\n- Multi-thread mode `SWOOLE_THREAD` supports starting timers in the `Manager` thread. @matyhtf\n- Compatible with the `curl` extension of `PHP-8.4`. @matyhtf @NathanFreeman\n- Rewrite the underlying `Swoole` code using `iouring`. @matyhtf @NathanFreeman\n- Optimized timers so that synchronous processes do not depend on signals. @matyhtf\n- Optimized the `Swoole\\Coroutine\\System::waitSignal()` method to allow listening to multiple signals simultaneously. @matyhtf\n\n# ❌ Deprecated：\n- No longer supports `PHP 8.0`.\n- No longer supports `Swoole\\Coroutine\\MySQL` coroutine client.\n- No longer supports `Swoole\\Coroutine\\Redis` coroutine client.\n- No longer supports `Swoole\\Coroutine\\PostgreSQL` coroutine client.\n- Removed `Swoole\\Coroutine\\System::fread()`, `Swoole\\Coroutine\\System::fwrite()`, and `Swoole\\Coroutine\\System::fgets()` methods.\n## 2024-01-24 v5.1.2\n- Added support for embed sapi @matyhtf\n- Fixed compatibility with PHP 8.3 ZEND_CHECK_STACK_LIMIT @Yurunsoft\n- Fixed no Content-Range response header when the range request returns all the contents of the file @Yurunsoft\n- Optimized HTTP server performance @NathanFreeman\n- Fixed truncated cookie @stnguyen90\n- Fixed native-curl crash on PHP 8.3 @NathanFreeman\n- Added CLOSE_SERVICE_RESTART, CLOSE_TRY_AGAIN_LATER, CLOSE_BAD_GATEWAY as valid close reasons for websocket @cjavad\n- Fixed invalid errno after Server::Manager::wait() @JacobBrownAustin\n- Fixed HTTP2 Typo @leocavalcante\n\n## 2022-07-22 v5.0.0\n\n### Added\n* Added `max_concurrency` option for `Server`\n* Added `max_retries` option for `Coroutine\\Http\\Client`\n* Added `name_resolver` global option\n* Added `upload_max_filesize` option for `Server`\n* Added `Coroutine::getExecuteTime()`\n* Added `SWOOLE_DISPATCH_CONCURRENT_LB` dispatch_mode for `Server`\n\n### Changed\n* Enhanced type system, added types for parameters and return values of all functions\n* Optimized error handling, all constructors will throw exceptions when fail\n* Adjusted the default mode of Server, the default is `SWOOLE_BASE` mode\n\n### Removed\n\n- Removed `PSR-0` style class names\n- Removed the automatic addition of `Event::wait()` in shutdown function\n- Removed `Server::tick/after/clearTimer/defer` aliases\n- Removed `--enable-http`/`--enable-swoole-json`, adjusted to be enable by default\n\n### Deprecated\n- Deprecated `Coroutine\\Redis` and `Coroutine\\MySQL`\n"
  },
  {
    "path": "docs/CODE-STYLE.md",
    "content": "Code Style\n=======\n* Google C++ Style\n* Indent adjusted to 4 characters\n* Line width adjusted to 120 characters\n\n"
  },
  {
    "path": "docs/CPPLINT.cfg",
    "content": "set noparent\nfilter=-build/include_alpha,-build/include_subdir,-build/include_what_you_use,-legal/copyright,-readability/nolint\nbraces=4\nlinelength=120\n"
  },
  {
    "path": "docs/ISSUE.md",
    "content": "# Bug reports\n\n## Instruction\n\nIf you think you have found a bug in Swoole, please report it.\nThe Swoole developers probably don't know about it,\nand unless you report it, chances are it won't be fixed.\nYou can report bugs at https://github.com/swoole/swoole-src/issues.\nPlease do not send bug reports in the mailing list or personal letters.\nThe issue page is also suitable to submit feature requests.\n\nPlease read the **How to report a bug document** before submitting any bug reports.\n\n## New issue\n\nFirst, while creating an issue, the system will give the following template:\n\n```markdown\nPlease answer these questions before submitting your issue. Thanks!\n1. What did you do? If possible, provide a simple script for reproducing the error.\n2. What did you expect to see?\n3. What did you see instead?\n4. What version of Swoole are you using (`php --ri swoole`)?\n5. What is your machine environment used (including the version of kernel & php & gcc)?\n```\nThe most important thing is to provide a simple script for reproducing the error, otherwise, you must provide as much information as possible.\n\n## Memory detection (recommended)\n\nIn addition to using `gdb` analysis, you can use the `valgrind` tool to check if the program is working properly.\n\n```shell\nUSE_ZEND_ALLOC=0 valgrind --log-file=/tmp/valgrind.log php your_file.php\n```\n\n* After the program is executed to the wrong location, `ctrl+c` is interrupted, and upload the `/tmp/valgrind.log` file.\n\n## Address Sanitizer\nIn certain cases, crash issues only occur in complex production environments, not consistently reproducible, and with a very low probability of occurrence. The use of `Valgrind` significantly impacts performance, whereas `Address Sanitizer` allows for memory detection without affecting performance.\n\nWe have created a `debug` version of `Swoole`, which can be compiled with the `--enable-debug` and `--enable-address-sanitizer` parameters to enable `Address Sanitizer`. The usage is as follows:\n\n```shell\ndocker pull phpswoole/swoole:dev-debug\ndocker run -it --rm phpswoole/swoole:dev-debug /bin/bash\nphp your_file.php\n```\n\n> To use `ASAN`, be sure to disable Address Space Layout Randomization (`ASLR`):\n\n```shell\necho 0 > /proc/sys/kernel/randomize_va_space\n```\n\nThis docker image can be used directly in a production environment, and `Address Sanitizer` will print error messages and call stack information when memory errors occur. This information can assist developers in quickly pinpointing issues.\n\n## CoreDump\n\nBesides, In a special case, you can use debugging tools to help developers locate problems\n\n```shell\nWARNING\tWorker::report_error(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=11\n```\n\nWhen a segmentation error occurs with Swoole, You can use the `gdb` tool and use `bt` command.\n> Using `gdb` to track the core file need to add the `--enable-debug` parameter when compiling `swoole`.\n\nEnable core dump\n```shell\nulimit -c unlimited\n```\n\nUse `gdb` to view the `core dump` information. The `core` file is usually in the current directory. If the operating system does the processing, put the `core dump` file in another directory, please replace it with the corresponding path.\n```\ngdb php core\ngdb php /tmp/core.4596\n```\n\nEnter bt under gdb to view the call stack information.\n```\n(gdb) bt\n```\nUse the f command in gdb to view the code segment corresponding to the ID.\n```\n(gdb)f 1\n(gdb)f 0\n```\n\nIf there is no function call stack information, it may be that the compiler has removed the debug information. Please manually modify the `Makefile` file in the swoole source directory and modify CFLAGS to\n\n```shell\nCFLAGS = -Wall -pthread -g -O0\n```\n"
  },
  {
    "path": "docs/SUPPORTED.md",
    "content": "## Supported Versions\n\n| Branch                                                     | PHP Version | Initialization | Active Support Until | Security Support Until |\n|------------------------------------------------------------|-------------|----------------|----------------------|------------------------|\n| [v6.0.x](https://github.com/swoole/swoole-src/tree/6.0)    | 8.1 - 8.4   | 2024-12-31     | 2025-12-31           | 2026-06-31             |\n| [v6.1.x](https://github.com/swoole/swoole-src/tree/master) | 8.1 - 8.4   | 2025-10-31     | 2026-10-31           | 2027-04-31             |\n\n- **Active support**： A release that is being actively supported. Reported bugs and security issues are fixed and regular point releases are made.\n- **Security fixes only**:  A release that is supported for critical security issues only. Releases are only made on an as-needed basis.\n\n## PHP Version Support\n\n1. Each branch (`MINOR version`) supports a fixed range of PHP versions. The `RELEASE VERSIONS` for that branch will not increase support for higher PHP versions.\n2. The upper limit is four PHP versions; any additional versions will not be supported. For example, version 6.0 only supports PHP 8.1 to 8.4.\n3. No support for any DEV or RC stage of PHP\n\nThe pace of PHP version updates is rapid, with each version introducing numerous underlying changes. The developers of Swoole have had to invest significant time and effort to support new releases, and we lack sufficient resources to keep up with PHP updates. Therefore, there will be a delay of one MINOR version before supporting new PHP versions.\n\n## Unsupported Branches\n\n> These releases that are no longer supported. Users of this release should upgrade as soon as possible, as they may be exposed to unpatched security vulnerabilities.\n\n \n| Branch                   | PHP Version | Duration                |\n|--------------------------|-------------|-------------------------|\n| `1.x.x`                  | 5.4 - 7.2   | 2012-07-01 ~ 2018-05-14 |\n| `2.x.x`                  | 7.0 - 7.3   | 2016-12-30 ~ 2018-05-23 |\n| `4.0.x` ~ `4.3.x`        | 7.0 - 7.4   | 2018-06-14 ~ 2019-12-31 |\n| `4.4.x`                  | 7.1 - 7.4   | 2019-04-15 ~ 2022-07-31 |\n| `4.5.x`,`4.6.x`, `4.7.x` | 7.1 - 7.4   | 2019-12-20 ~ 2021-12-31 |\n| `4.8.x`                  | 7.3 - 8.2   | 2021-10-14 ~ 2024-06-30 |\n| `5.0.x`                  | 7.4 - 8.3   | 2022-01-20 ~ 2023-07-20 |\n| `5.1.x`                  | 8.0 - 8.3   | 2023-11-29 ~ 2025-04-29 |\n"
  },
  {
    "path": "docs/TESTS.md",
    "content": "## Swoole Tests\n\n## Core Tests\nUsed to test the core code in the `src/` directory, only as a C++ library, not related to php.\nThe core tests depends on the googletest framework, and googletest needs to be installed.\nThe core test cases must be written with `C++`.\n\n`GCC/G++` version `8.0` or higher is required, with full support for `C++17`.\n\n### **Build googletest**\nSince swoole core unit testing rely on googletest, we need compile googletest at first.\n\n```shell\nwget https://github.com/google/googletest/releases/download/v1.15.2/googletest-1.15.2.tar.gz\ntar zxf googletest-1.15.2.tar.gz\ncd googletest-1.15.2\nmkdir ./build && cd ./build\ncmake ..\nmake -j\nsudo make install\n```\n\n### Build libswoole.so\n\n```shell\ncd swoole-src\ncmake .\nmake -j$(nproc) lib-swoole\n```\n### Build core-tests\n```shell\nmake -j$(nproc) core-tests\n```\n\n### Run core-tests\n```shell\n# run all dependency services\ncd core-tests\ndocker compose up -d\n# run all tests\n./bin/core-tests\n# run some test cases\n./bin/core-tests --gtest_filter=server.*\n# list all tests\n./bin/core_tests --gtest_list_tests\n```\n\n## PHP Tests\nUsed to test the php extension code in the `ext-src/` directory. The swoole php test depends on php environment.\nThe `php-dev` related components must be installed.\n\nThe php test cases must be written with `PHP`.\n\n### Build ext-swoole\n```shell\ncd swoole-src\nphpize\n./configure ${options}\nmake -j$(nproc)\nmake install\n```\nNeed to configure `php.ini`, add `extension=swoole.so` to enable `ext-swoole`.\n\n### Run tests\n```shell\n./scripts/route.sh\n```\n\nThe automated test scripts in this directory can not only run on Github Action CI. Powered by docker container technology, it can run on any systems. You only need to run the `route.sh` script to create containers of multiple PHP environments then it will run Swoole's build tests and unit tests on multiple systems automatically.\n\n### With special branch\n\n```shell\nSWOOLE_BRANCH=alpine ./scripts/route.sh\n```\n\n### Enter the container\n\n> You can cancel the unit test by `CTRL+C`\n\n```shell\ndocker exec -it -e LINES=$(tput lines) -e COLUMNS=$(tput cols) swoole /bin/bash\n```\n"
  },
  {
    "path": "docs/WORKFLOW-PARAMETERS.md",
    "content": "# Workflow Parameters\nAdding parameters in the Git commit log can control the workflow.\n\n## --filter\nThis parameter specifies which workflows to run, instead of running all of them.\nThe command format is: `--filter=[flow1][flow2][flow...]` ,\nand it supports setting multiple workflows, with names matching the filename of the yml file.\n\n## --valgrind\nSetting this parameter will cause the test program to be run with `valgrind`.\n\n```shell\ngit commit -m \"commit message --filter=[core][unit] --valgrind\"\n```\n\n## --asan\nSetting this parameter will cause the test program to be run with `ASAN`.\n"
  },
  {
    "path": "docs/google-style.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<profiles version=\"1\">\n<profile kind=\"CodeFormatterProfile\" name=\"Google C++\" version=\"1\">\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_new_line_in_empty_block\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.lineSplit\" value=\"120\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.alignment_for_member_access\" value=\"16\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_comma_in_base_types\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.keep_else_statement_on_same_line\" value=\"false\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.indent_switchstatements_compare_to_switch\" value=\"true\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.alignment_for_constructor_initializer_list\" value=\"83\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_opening_brace_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_if\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_exception_specification\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_comma_in_base_types\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.indent_body_declarations_compare_to_access_specifier\" value=\"true\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_exception_specification\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_comma_in_template_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_block\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.use_tabs_only_for_leading_indentations\" value=\"false\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_colon_in_labeled_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_colon_in_case\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.comment.min_distance_between_code_and_line_comment\" value=\"2\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_comma_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_comma_in_enum_declarations\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.alignment_for_expressions_in_array_initializer\" value=\"16\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_comma_in_declarator_list\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_bracket\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_prefix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.tabulation.size\" value=\"4\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_new_line_before_else_in_if_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.alignment_for_enumerator_list\" value=\"51\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_between_empty_parens_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_switch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.alignment_for_declarator_list\" value=\"16\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.indent_empty_lines\" value=\"false\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.indent_switchstatements_compare_to_cases\" value=\"true\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.keep_empty_array_initializer_on_one_line\" value=\"false\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_method_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.put_empty_statement_on_new_line\" value=\"true\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_switch\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_cast\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_between_empty_braces_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.brace_position_for_method_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_while\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_question_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_semicolon\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_closing_angle_bracket_in_template_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_colon_in_base_clause\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.indent_breaks_compare_to_cases\" value=\"true\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_unary_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.join_wrapped_lines\" value=\"true\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_comma_in_declarator_list\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.alignment_for_arguments_in_method_invocation\" value=\"18\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.comment.never_indent_line_comments_on_first_column\" value=\"true\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_while\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_between_empty_brackets\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_opening_bracket\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.alignment_for_parameters_in_method_declaration\" value=\"18\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.number_of_empty_lines_to_preserve\" value=\"1\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_closing_brace_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_semicolon_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_colon_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.brace_position_for_block\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.brace_position_for_type_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_assignment_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_angle_bracket_in_template_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_comma_in_expression_list\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_opening_angle_bracket_in_template_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.continuation_indentation\" value=\"2\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.alignment_for_expression_list\" value=\"0\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_comma_in_template_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_colon_in_default\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_binary_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.alignment_for_conditional_expression\" value=\"34\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_between_empty_parens_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_comma_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_if\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.format_guardian_clause_on_one_line\" value=\"false\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.indent_access_specifier_extra_spaces\" value=\"1\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_cast\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.indent_access_specifier_compare_to_type_header\" value=\"false\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.continuation_indentation_for_array_initializer\" value=\"2\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_colon_in_labeled_statement\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_semicolon_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.indent_body_declarations_compare_to_namespace_header\" value=\"false\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.alignment_for_compact_if\" value=\"0\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_assignment_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_closing_brace_in_block\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_new_line_at_end_of_file_if_missing\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.alignment_for_assignment\" value=\"16\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.alignment_for_conditional_expression_chain\" value=\"18\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_comma_in_template_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_comma_in_expression_list\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_question_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_exception_specification\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_binary_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_new_line_before_identifier_in_function_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.alignment_for_base_clause_in_type_declaration\" value=\"16\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_declaration_throws\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_between_empty_parens_in_exception_specification\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.indent_declaration_compare_to_template_header\" value=\"false\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_unary_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_switch\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.indent_statements_compare_to_body\" value=\"true\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_declaration_throws\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.alignment_for_binary_expression\" value=\"16\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.indent_statements_compare_to_block\" value=\"true\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_comma_in_template_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_new_line_before_catch_in_try_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.alignment_for_throws_clause_in_method_declaration\" value=\"16\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_closing_paren_in_cast\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_catch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_angle_bracket_in_template_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.tabulation.char\" value=\"space\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_closing_angle_bracket_in_template_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_new_line_before_colon_in_constructor_initializer_list\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_while\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.brace_position_for_block_in_case\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.compact_else_if\" value=\"true\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_postfix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_new_line_after_template_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_colon_in_base_clause\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_catch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.keep_then_statement_on_same_line\" value=\"false\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.brace_position_for_switch\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.alignment_for_overloaded_left_shift_chain\" value=\"18\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_if\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_switch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.keep_imple_if_on_one_line\" value=\"false\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.indentation.size\" value=\"4\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.brace_position_for_namespace_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_colon_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_comma_in_enum_declarations\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_prefix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_closing_angle_bracket_in_template_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.brace_position_for_array_initializer\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_colon_in_case\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_catch\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_namespace_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_postfix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_closing_bracket\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_new_line_before_while_in_do_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_closing_angle_bracket_in_template_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.cdt.core.formatter.insert_space_after_opening_angle_bracket_in_template_arguments\" value=\"do not insert\"/>\n</profile>\n</profiles>\n"
  },
  {
    "path": "docs/sponsors.md",
    "content": "<hr id=\"title1\" />\n\n# [成为 Swoole 的赞助者](#title1) \n---\n\nSwoole 是采用 Apache 许可的开源项目，是完全免费的。\n\n维护这样一个庞大的生态系统和为项目开发新功能所需的巨大努力，只有在我们的赞助者慷慨的财务支持下才得以持续。\n捐赠募集到的资金将全部用于开源项目的维护和发展。包括但不限于：\n- 向核心贡献者支付报酬\n- 服务器租赁费用\n- 网站和文档的维护\n- 举办线下活动\n- 为社区活跃者提供奖励\n\n> 您可以扫码添加微信客服或通过邮件 `service@swoole.com` 与我们联系，沟通赞助事宜\n\n<hr id=\"title2\" />\n\n## [赞助渠道](#title2) \n\n---\n来自世界各地的企业可以通过赞助来支持 `Swoole` 项目的开发。\n根据赞助商赞助金额的不同分为顶级赞助商、金牌赞助商，在获得捐赠的第一年内我们将为赞助商做以下品牌展示：\n\n> 顶级赞助商\n- 官网、文档、问答社区首屏顶部显著位置展示\n- GitHub 仓库首页顶部赞助商标识 \n- 官方微信公众号、QQ 群、微博、知乎等官方社交平台赞助商宣传内容推送\n\n> 金牌赞助商\n- 官网、文档、问答社区首屏侧边位置展示\n\n## 顶级赞助商\n\n<div class=\"sponsor\">\n    <a href=\"https://crmeb.com/\" target=\"_blank\"><img src=\"https://crmeb.com/static/images/dark_logo.png\" alt=\"CRMEB\" ></a>\n</div>\n\n\n## 金牌赞助商\n<div class=\"sponsor\">\n    <a href=\"https://www.apipost.cn/\" target=\"_blank\"><img src=\"https://img.cdn.apipost.cn/new_www/index_img/apipost-logo_or.svg\" alt=\"Apipost\" ></a>\n</div>\n<div class=\"sponsor\">\n    <a href=\"https://cloud.tencent.com/\" target=\"_blank\"><img src=\"https://cloudcache.tencent-cloud.com/qcloud/portal/kit/images/slice/logo.23996906.svg\" alt=\"腾讯云\" ></a>\n</div>\n<div class=\"sponsor\">\n    <a href=\"https://www.w7.cc/\" target=\"_blank\"><img src=\"https://wcz.netfishing.cn/web/resource/images/logo/logo-219.png\" alt=\"微擎\" ></a>\n</div>\n<div class=\"sponsor\">\n    <a href=\"https://www.zentao.net/\" target=\"_blank\"><img src=\"https://cdn.easycorp.cn/web/data/upload/zentao/202402/f_0c7d40c83be2425f404dc4bd5ee778cb.png\" alt=\"禅道\" ></a>\n</div>\n<div class=\"sponsor\">\n    <a href=\"https://zh.51talk.com/\" target=\"_blank\"><img src=\"https://cdn.51talk.com/apollo/images/c8b3d8a8127ba9e43e0f87fd46a53eca.png\" alt=\"51Talk\" ></a>\n</div>\n<div class=\"sponsor\">\n    <a href=\"https://www.easycorp.cn/\" target=\"_blank\"><img src=\"https://wenda-1252906962.file.myqcloud.com/images/sponsors/easysoft.png\" alt=\"易软天创\" ></a>\n</div>\n<div class=\"sponsor\">\n    <a href=\"https://www.liuxingedu.com/\" target=\"_blank\"><img src=\"https://www.liuxingedu.com/static/picture/logo1.png\" alt=\"六星教育\" ></a>\n</div>\n<div class=\"sponsor\">\n    <a href=\"https://guojiang.club/\" target=\"_blank\"><img src=\"https://wiki.swoole.com/static/uploads/wiki/202001/19/273190976660.jpg\" alt=\"果酱社区\" ></a>\n</div>\n\n## 银牌赞助商\n<div class=\"sponsor\">\n    <a href=\"https://shopxo.net/\" target=\"_blank\"><img src=\"https://cdn.shopxo.net/static/images/common/logo.png\" alt=\"ShopXO\" ></a>\n</div>\n<div class=\"sponsor\">\n    <a href=\"https://segmentfault.com/\" target=\"_blank\"><img src=\"https://static.segmentfault.com/main_site_next/68cae9b4/_next/static/media/logo-b.1ef53c6e.svg\" alt=\"SegmentFault\" ></a>\n</div>\n\n<div class=\"sponsor\">\n    <a href=\"https://codecasts.com/\" target=\"_blank\"><img src=\"https://user-images.githubusercontent.com/6011686/28741127-791014ae-7442-11e7-929e-114e5047d9e5.png\" alt=\"codecasts.com\" ></a>\n</div>\n\n\n\n\n"
  },
  {
    "path": "examples/atomic/long.php",
    "content": "<?php\n$l = new Swoole\\Atomic\\Long( -2 ** 36);\necho $l->get().\"\\n\";\necho $l->add(20).\"\\n\";\necho $l->sub(20).\"\\n\";\necho $l->sub(-20).\"\\n\";\necho $l->cmpset(-2 ** 36, 0).\"\\n\";\necho $l->cmpset(-2 ** 36 + 20, 0).\"\\n\";\n"
  },
  {
    "path": "examples/atomic/test.php",
    "content": "<?php\n$atomic = new Swoole\\Atomic(123);\necho $atomic->add(12).\"\\n\";\necho $atomic->sub(11).\"\\n\";\necho $atomic->cmpset(122, 999).\"\\n\";\necho $atomic->cmpset(124, 999).\"\\n\";\necho $atomic->get().\"\\n\";\n"
  },
  {
    "path": "examples/atomic/wait.php",
    "content": "<?php\n$n = new Swoole\\Atomic(0);\n\nif (pcntl_fork() > 0)\n{\n\techo \"master start\\n\";\n\t$n->wait(1.5);\n\techo \"master end\\n\";\n}\nelse\n{\n\techo \"child start\\n\";\n\tsleep(1);\n\t$n->wakeup();\n\techo \"child end\\n\";\n}\n"
  },
  {
    "path": "examples/client/c10k.php",
    "content": "<?php\n$clients = array();\nfor ($j = 0; $j < 2; $j++) {\n    $pid = pcntl_fork();\n    if ($pid > 0) {\n        continue;\n    } else {\n        for ($i = 0; $i < 9999; $i++) {\n            $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞\n            $ret = $client->connect('127.0.0.1', 9501, 0.5);\n            if (!$ret) {\n                echo \"#$i\\tConnect fail.  errno=\" . $client->errCode;\n                die(\"\\n\");\n            }\n            $clients[] = $client;\n            usleep(10);\n        }\n        echo \"Worker #\" . posix_getpid() . \" connect $i finish\\n\";\n        sleep(1000);\n        exit;\n    }\n}\nsleep(1000);\n"
  },
  {
    "path": "examples/client/get_socket.php",
    "content": "<?php\n\nfunction getClient()\n{\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n    if (!$client->connect('127.0.0.1', 9501, -1))\n    {\n        exit(\"connect failed. Error: {$client->errCode}\\n\");\n    }\n\n    $res = $client->getSocket();\n    return $client;\n}\n\n$client = getClient();\n\n$count = 0;\n//$client->set(array('open_eof_check' => true, 'package_eof' => \"\\r\\n\\r\\n\"));\n\n//$client = new Swoole\\Client(SWOOLE_SOCK_UNIX_DGRAM, SWOOLE_SOCK_SYNC); //同步阻塞\n//if (!$client->connect(dirname(__DIR__).'/server/svr.sock', 0, -1, 1))\n\n\nvar_dump($client->getsockname());\n$client->send(\"hello world\\r\\n\\r\\n\");\n\n//for($i=0; $i < 3; $i ++)\n{\n    echo $client->recv();\n    sleep(1);\n}\n\n$client->close();\n"
  },
  {
    "path": "examples/client/long_tcp.php",
    "content": "<?php\nfor($i=0; $i < 100; $i++)\n{\n\t$client = new Swoole\\Client(SWOOLE_TCP | SWOOLE_KEEP);\n\tif(!$client->connect('127.0.0.1', 9501))\n\t{\n\t\texit(\"connect failed\\n\");\n\t}\n\t$client->send(str_repeat(\"A\", 600));\n\t$data = $client->recv(7000, 0);\n\tif($data === false)\n\t{\n\t\techo \"recv fail\\n\";\n\t\tbreak;\n\t}\n\tvar_dump($data);\n\t$client->close();\n}\n"
  },
  {
    "path": "examples/client/recv_1m.php",
    "content": "<?php\n$c = new Swoole\\Client(SWOOLE_TCP);\n$f = fopen('data.log', 'w');\n$c->connect('127.0.0.1', 9509, 60);\n$c->send(\"AAAAAAAAAAAAAAAA\");\n\n$n_bytes = 0;\n\nwhile (true)\n{\n    $line = $c->recv();\n    if ($line === false)\n    {\n        echo \"recv failed.\\n\";\n        break;\n    }\n    elseif (empty($line))\n    {\n        echo \"recv $n_bytes bytes\\n\";\n        break;\n    }\n    else\n    {\n        fwrite($f, $line);\n        $n_bytes += strlen($line);\n    }\n}\n"
  },
  {
    "path": "examples/client/recv_file.php",
    "content": "<?php\nif (empty($argv[1])) {\n    $server_ip = '127.0.0.1';\n} else {\n    $server_ip = $argv[1];\n}\n$cli = new Swoole\\Client(SWOOLE_TCP);\n$start_ms = microtime(true);\n$cli->connect($server_ip, 9501, 5);\n$filesize = intval($cli->recv());\nif ($filesize == 0) {\n    die(\"get file size failed.\\n\");\n}\necho \"file_size = $filesize\\n\";\n$content = '';\n$cli->send(\"get file\");\n\n$use_waitall = false;\n\nif ($use_waitall) {\n    //waitall，需要一次性分配内存，适合小一点的文件\n    $content = $cli->recv($filesize, true);\n} else {\n    //循环接收，适合大型文件\n    while (1) {\n        //超大文件接收，这里需要改成分段写磁盘\n        $content .= $cli->recv();\n        if (strlen($content) == $filesize) {\n            break;\n        }\n    }\n}\nfile_put_contents(__DIR__ . \"/recv_file_\" . time() . \".jpg\", $content);\necho \"recv \" . strlen($content) . \" byte data\\n\";\necho \"used \" . ((microtime(true) - $start_ms) * 1000) . \"ms\\n\";\n$cli->close();\n"
  },
  {
    "path": "examples/client/select.php",
    "content": "<?php\n$clients = array();\n\nfor ($i = 0; $i < 20; $i++) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞\n    $ret = $client->connect('127.0.0.1', 9501, 0.5, 0);\n    if (!$ret) {\n        echo \"Connect Server fail.errCode=\" . $client->errCode;\n    } else {\n        $client->send(\"HELLO WORLD\\n\");\n        $clients[$client->sock] = $client;\n    }\n}\n\nwhile (!empty($clients)) {\n    $write = $error = array();\n    $read = array_values($clients);\n    $n = swoole_client_select($read, $write, $error, 0.6);\n    if ($n > 0) {\n        foreach ($read as $index => $c) {\n            echo \"Recv #{$c->sock}: \" . $c->recv() . \"\\n\";\n            unset($clients[$c->sock]);\n        }\n    }\n}\n"
  },
  {
    "path": "examples/client/simple.php",
    "content": "<?php\n$clients = array();\nfor ($i = 0; $i < 1; $i++) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞\n    $ret = $client->connect('127.0.0.1', 9501, 0.5, 0);\n    if (!$ret) {\n        echo \"Over flow. errno=\" . $client->errCode;\n        die(\"\\n\");\n    }\n    $clients[] = $client;\n}\nsleep(1);\nwhile (1) {\n    foreach ($clients as $client) {\n        $client->send(\"sss\");\n        $data = $client->recv();\n        var_dump($data);\n    }\n    sleep(1);\n}\n"
  },
  {
    "path": "examples/client/sync.php",
    "content": "<?php\n$client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n$count = 0;\n//$client->set(array('open_eof_check' => true, 'package_eof' => \"\\r\\n\\r\\n\"));\n\n//$client = new Swoole\\Client(SWOOLE_SOCK_UNIX_DGRAM, SWOOLE_SOCK_SYNC); //同步阻塞\n//if (!$client->connect(dirname(__DIR__).'/server/svr.sock', 0, -1, 1))\n\ndo_connect:\nif (!$client->connect('127.0.0.1', 9501, -1)) {\n    exit(\"connect failed. Error: {$client->errCode}\\n\");\n}\n\nvar_dump($client->getsockname());\n$client->send(\"hello world\\r\\n\\r\\n\");\n\n//for($i=0; $i < 3; $i ++)\n{\n    echo $client->recv();\n    sleep(1);\n}\n\n$client->close();\n$count++;\nif ($count < 20) {\n    goto do_connect;\n}\n"
  },
  {
    "path": "examples/client/test.txt",
    "content": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx\nXXXXXXXXXXXXXXXXXXXXXXx\nXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXX\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\nTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT\nDDDDDDDDDDDDDDDDDDDDDDDDDDD\n"
  },
  {
    "path": "examples/client/udp_sync.php",
    "content": "<?php\n$client = new Swoole\\Client(SWOOLE_SOCK_UDP, SWOOLE_SOCK_SYNC);\n$client->connect('127.0.0.1', 9502);\n\nfor ($i = 0; $i < 100; $i++)\n{\n    $client->send(\"admin\");\n    echo $client->recv().\"\\n\";\n    sleep(1);\n}\n"
  },
  {
    "path": "examples/coroutine/backtrace.php",
    "content": "<?php\nfunction test1() {\n    test2();\n}\n\nfunction test2() {\n    while(true) {\n        co::sleep(10);\n        echo __FUNCTION__.\" \\n\";\n    }\n}\n\n$cid = go(function () {\n    test1();\n});\n\ngo(function () use ($cid) {\n    while(true) {\n        echo \"BackTrace[$cid]:\\n-----------------------------------------------\\n\";\n        echo get_debug_print_backtrace(co::getBackTrace($cid)).\"\\n\";\n        co::sleep(3);\n    }\n});\n\nfunction get_debug_print_backtrace($traces){\n    $ret = array();\n    foreach($traces as $i => $call){\n        $object = '';\n        if (isset($call['class'])) {\n            $object = $call['class'].$call['type'];\n            if (is_array($call['args'])) {\n                foreach ($call['args'] as &$arg) {\n                    get_arg($arg);\n                }\n            }\n        }\n\n        $ret[] = '#'.str_pad($i - $traces_to_ignore, 3, ' ')\n        .$object.$call['function'].'('.implode(', ', $call['args'])\n        .') called at ['.$call['file'].':'.$call['line'].']';\n    }\n\n    return implode(\"\\n\",$ret);\n}\n\nfunction get_arg(&$arg) {\n    if (is_object($arg)) {\n        $arr = (array)$arg;\n        $args = array();\n        foreach($arr as $key => $value) {\n            if (strpos($key, chr(0)) !== false) {\n                $key = '';    // Private variable found\n            }\n            $args[] =  '['.$key.'] => '.get_arg($value);\n        }\n\n        $arg = get_class($arg) . ' Object ('.implode(',', $args).')';\n    }\n}\n"
  },
  {
    "path": "examples/coroutine/before_server_start.php",
    "content": "<?php\ngo(function ()\n{\n    co::sleep(1);\n\n    $http = new Swoole\\Http\\Server(\"127.0.0.1\", 9501, SWOOLE_BASE);\n\n    $http->on(\"start\", function ($server)\n    {\n        echo \"Swoole http server is started at http://127.0.0.1:9501\\n\";\n    });\n\n    $http->on(\"request\", function ($request, $response)\n    {\n        var_dump($request->header);\n        var_dump($request->server);\n\n        $response->header(\"Content-Type\", \"text/plain\");\n        $response->status(200);\n        $response->end(\"test\");\n    });\n    echo \"start\\n\";\n    $http->start();\n    echo \"end\\n\";\n});\n"
  },
  {
    "path": "examples/coroutine/behavior/do-while.php",
    "content": "<?php\nSwoole\\Coroutine::set([\n    'max_death_ms' => 2000,\n    'death_loop_threshold' => 5,\n]);\necho \"start\\n\";\ngo(function () {\n    echo \"coro start\\n\";\n    do{\n        echo \"111\\n\";\n        sleep(1);\n    } while(1);\n});\n\ngo(function () {\n    echo \"222222\\n\";\n});\necho \"end\\n\";\n"
  },
  {
    "path": "examples/coroutine/behavior/for.php",
    "content": "<?php\nco::set(['enable_preemptive_scheduler' => true]);\n$start = microtime(1);\necho \"start\\n\";\n$flag = 1;\n\ngo(function () use (&$flag) {    \n    echo \"coro 1 start to loop\\n\";\n    $i = 0;\n    for (;;) {\n//         echo \"$i\\n\";\n        if (!$flag) {\n            break;\n        }\n        $i++;\n    }\n    echo \"coro 1 can exit\\n\";\n});\n    \n$end = microtime(1);\n$msec = ($end - $start) * 1000;\necho \"use time $msec\\n\";\ngo(function () use (&$flag) {\n    echo \"coro 2 set flag = false\\n\";\n    $flag = false;\n});\necho \"end\\n\";"
  },
  {
    "path": "examples/coroutine/behavior/foreach.php",
    "content": "<?php\necho \"start\\n\";\ngo(function () {\n    echo \"coro start\\n\";\n    $arr = range(0, 20);\n    foreach($arr as $k=>$v){\n        echo $v.\"\\n\";\n    }\n});\n\ngo(function () {\n    echo \"222222\\n\";\n});\necho \"end\\n\";\n"
  },
  {
    "path": "examples/coroutine/behavior/goto.php",
    "content": "<?php\necho \"start\\n\";\ngo(function () {\n    echo \"coro start\\n\";\n    loop:\n    echo \"111\\n\";\n    sleep(1);\n    goto loop;\n});\n\ngo(function () {\n    echo \"222222\\n\";\n});\necho \"end\\n\";\n"
  },
  {
    "path": "examples/coroutine/behavior/preemptive_timer.php",
    "content": "<?php\nco::set(['enable_preemptive_scheduler' => true]);\ngo(function (){\n    $exit = false;\n    while (true){\n        $res = Swoole\\Coroutine::stats();\n        $num = $res['coroutine_num'];\n        if ($num < 10){\n            go(function () use(&$exit){\n                echo \"cid:\".Swoole\\Coroutine::getCid().\" start\\n\";\n                Swoole\\Coroutine::sleep(1);\n                echo \"cid \".Swoole\\Coroutine::getCid().\" end\\n\";\n                $exit = true;\n            });\n        }\n        if ($exit) {\n            echo \"cid \".Swoole\\Coroutine::getCid().\" break\\n\";\n            break;\n        }\n    }\n    echo \"cid \".Swoole\\Coroutine::getCid().\" exit\\n\";\n});\necho \"main end\\n\";\n\n"
  },
  {
    "path": "examples/coroutine/behavior/tick.php",
    "content": "<?php\ndeclare(ticks=10);\n\n$max_msec = 10;\nSwoole\\Coroutine::set([\n    'max_exec_msec' => $max_msec,\n]);\n\n$s = microtime(1);\necho \"start\\n\";\n$flag = 1;\ngo(function () use (&$flag, $max_msec, $s){\n    echo \"coro 1 start to loop for $max_msec msec\\n\";\n    $n = 10000000;\n    $i = 0;\n    while($n--) {\n        $i ++;\n    }\n    echo \"coro 1 can exit\\n\";\n    $t = microtime(1);\n    $u = $t-$s;\n    echo \"coro 1 use time \".(($t-$s) * 1000).\" ms\\n\";\n});\n\n$t = microtime(1);\n$u = $t-$s;\necho \"shedule use time \".($u * 1000).\" ms\\n\";\ngo(function () use (&$flag){\n    echo \"coro 2 set flag = false\\n\";\n    $flag = false;\n});\necho \"end\\n\";"
  },
  {
    "path": "examples/coroutine/behavior/while.php",
    "content": "<?php\nSwoole\\Coroutine::set([\n    'max_death_ms' => 5000,\n]);\n$s = microtime(1);\necho \"start\\n\";\ngo(function () {\n    echo \"coro 1  start\\n\";\n    $x = 5;\n    $i = 0;\n    while(!0) {\n        $i ++;\n        echo \"coro 1 $i\\n\";\n        sleep(1);\n    }\n});\n\ngo(function () {\n    echo \"coro 2  start\\n\";\n    $x = 5;\n    $i = 0;\n    while(1) {\n        $i ++;\n        echo \"coro 2 $i\\n\";\n        sleep(1);\n    }\n});\n\n$t = microtime(1);\n$u = $t-$s;\necho \"use time $u s\\n\";\ngo(function () {\n    echo \"----------------------\\n\";\n});\necho \"end\\n\";\n"
  },
  {
    "path": "examples/coroutine/behavior/while2.php",
    "content": "<?php\nSwoole\\Coroutine::set([\n    'max_death_ms' => 5000,\n]);\n$s = microtime(1);\necho \"start\\n\";\n\n$flag = 1;\ngo(function () use (&$flag){\n    echo \"coro 1 start\\n\";\n    $i = 0;\n    while($flag) {\n        $i ++;\n        echo \"$i\\n\";\n        sleep(1);\n    }\n    echo \"coro 1 can exit\\n\";\n});\n\n$t = microtime(1);\n$u = $t-$s;\necho \"use time $u s\\n\";\ngo(function () use (&$flag){\n    echo \" coro 2 set flag = false\\n\";\n    $flag = false;\n});\necho \"end\\n\";\n"
  },
  {
    "path": "examples/coroutine/cancel_throw.php",
    "content": "<?php\nCo\\run(function () {\n    $cid = Co\\go(function () {\n            while (true) {\n                sleep(1);\n                echo \"co 2 running\\n\";\n            }\n            var_dump('end');\n\n    });\n\n    sleep(3);\n    Co::cancel($cid, true);\n\n    sleep(2);\n    echo \"co 1 end\\n\";\n});\n"
  },
  {
    "path": "examples/coroutine/channel/test.php",
    "content": "<?php\nfunction BatchExecMethodByCo()\n{\n    $args = func_get_args();\n    $channel = new \\Swoole\\Coroutine\\Channel(count($args));\n    foreach ($args as $key => $func) {\n        go(function()use($channel,$func,$key){\n            $res = $func();\n            $channel->push([$key=>$res]);\n        });\n    }\n    $list = [];\n    go(function()use(&$list,$args,$channel){\n        foreach ($args as $key => $chan) {\n            $list[$key] = $channel->pop();\n        }\n    });\n    Swoole\\Event::wait();\n    return $list;\n}\nfunction test($value='')\n{\n    \\Co::sleep(1);\n    return \"test\\n\";\n}\nfunction test2($value='')\n{\n    \\Co::sleep(1);\n    return \"test2 \".rand(1,10).\"\\n\";\n}\n$r = BatchExecMethodByCo(\"test\",\"test2\",\"test\");\nvar_dump($r);\n"
  },
  {
    "path": "examples/coroutine/client_send_yield.php",
    "content": "<?php\ngo(function () {\n    $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n    $client->set(array(\n        'socket_buffer_size' => 1024 * 512,\n    ));\n    if (!$client->connect('127.0.0.1', 9501, -1))\n    {\n        exit(\"connect failed. Error: {$client->errCode}\\n\");\n    }\n    $length = 0;\n    $size = 1024 * 64;\n    while (true)\n    {\n        $ret = $client->send(str_repeat('A', $size));\n        if ($ret == false)\n        {\n            var_dump($ret);\n            break;\n        }\n        $length += $size;\n        echo \"send $length success\\n\";\n    }\n    var_dump($client->errCode);\n});\n\nSwoole\\Event::wait();\n"
  },
  {
    "path": "examples/coroutine/client_send_yield_server.php",
    "content": "<?php\n$socket = stream_socket_server(\"tcp://0.0.0.0:9501\", $errno, $errstr);\nif (!$socket) {\n    echo \"$errstr ($errno)<br />\\n\";\n} else {\n    while (true) {\n        $conn = stream_socket_accept($socket);\n        if (!$conn) {\n            continue;\n        }\n        $i = 0;\n        $length = 0;\n        while(true) {\n            $data = fread($conn, 8192);\n            if ($data == false)\n            {\n                break;\n            }\n            $length += strlen($data);\n            echo \"recv \" . $length . \" bytes\\n\";\n            usleep(100000);\n        }\n        fclose($conn);\n        echo \"closed\\n\";\n    }\n    fclose($socket);\n}\n"
  },
  {
    "path": "examples/coroutine/coro_array_map.php",
    "content": "<?php\n\nuse Swoole\\Coroutine as co;\n\nco::create(function() {\n    array_map(\"test\",array(\"func param\\n\"));\n    echo \"co flow end\\n\";\n});\n\nfunction test($p) {\n    go(function() use ($p){\n        echo $p;\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $res = $client->connect('127.0.0.1', 9501, 1);\n        echo \"co resume : connect ret = \".var_export($res,1).\"\\n\";\n        echo \"map func end \\n\";\n    });\n}\necho \"main end\\n\";\n"
  },
  {
    "path": "examples/coroutine/coro_call_user.php",
    "content": "<?php\nuse Swoole\\Coroutine as co;\nco::set(['trace_flags' => 1]);\n\nco::create(function() {\n    echo \"co func start\\n\";\n    $name = \"call_user_func\";\n    $ret = $name(\"test\",\"test\\n\");\n\techo \"co func end ret:{$ret}\\n\";\n});\n\nfunction test($params)\n{\n    echo \"func params:$params\";\n    co::sleep(1);\n    echo \"func end\\n\";\n    return \"test return\\n\";\n}\necho \"main script last\\n\";\n"
  },
  {
    "path": "examples/coroutine/coro_channel.php",
    "content": "<?php\n$http = new Swoole\\Http\\Server(\"127.0.0.1\", 9501, SWOOLE_BASE);\n$http->set(array(\n    'log_file' => '/dev/null'\n));\nuse Swoole\\Coroutine as co;\n// $http->on(\"WorkerStart\", function (\\Swoole\\Server $serv)\n// {\n//\n// });\n$http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response)\n{\n    $ch = new co\\Channel(1);\n    $out = new co\\Channel(1);\n    Swoole\\Coroutine::create(function() use ($ch, $out) {\n        $out->push(\"OK\");\n        $out->push(\"OK\");\n    });\n    $ret = $out->pop();\n    var_dump($ret);\n    $ret = $out->pop();\n    var_dump($ret);\n    $response->end(\"$ret\\n\");\n});\n$http->start();\n"
  },
  {
    "path": "examples/coroutine/coro_destruct.php",
    "content": "<?php\nuse Swoole\\Coroutine as co;\nclass T\n{\n    function __construct()\n    {\n\n    }\n\n    function test()\n    {\n        echo \"call function \\n\";\n    }\n\n    function __destruct()\n    {\n        go(function () {\n            echo \"coro start\\n\";\n            co::sleep(1.0);\n            echo \"coro exit\\n\";\n        });\n        echo \"111\\n\";\n    }\n}\n\n$t = new T();\n$t->test();\necho \"end \\n\";\n"
  },
  {
    "path": "examples/coroutine/coro_destuct.php",
    "content": "<?php\nrequire __DIR__ . \"/coro_include.php\";\n\nclass T\n{\n    function __construct()\n    {\n        echo \"call __construct \\n\";\n    }\n\n      function test()\n      {\n        echo \"call function \\n\";\n      }\n\n      function __destruct()\n      {\n        echo \"call __destruct \\n\";\n        go(function () {\n            echo \"co[1] start\\n\";\n            $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n            $res = $client->connect('127.0.0.1', 9501, 1);\n            co::sleep(1.0);\n            echo \"co[1] resume : connect ret = \".var_export($res,1).\"\\n\";\n            echo \"co[1] exit\\n\";\n        });\n      }\n}\n\n$t = new T();\n$t->test();\nunset($t);\n"
  },
  {
    "path": "examples/coroutine/coro_empty.php",
    "content": "<?php\n// co::set(['trace_flags' => 1]);\n\nco::create(function () {\n    echo \"no coro exit\\n\";\n});\necho \"exec file end\\n\";\n"
  },
  {
    "path": "examples/coroutine/coro_gethost.php",
    "content": "<?php\nrequire __DIR__ . \"/coro_include.php\";\nuse Swoole\\Coroutine as co;\n\nco::create(function () {\n    $ip = co::gethostbyname('www.baidu.com');\n    var_dump($ip);\n});\necho \"111\\n\";\n\necho \"222\\n\";\n"
  },
  {
    "path": "examples/coroutine/coro_include.php",
    "content": "<?php\nif (substr(PHP_OS, 0, 3) == 'WIN')\n{\n    exit(\"skip for Windows\");\n}\nif (!extension_loaded(\"swoole\"))\n{\n    exit(\"swoole extension is required\");\n}\n"
  },
  {
    "path": "examples/coroutine/coro_invoke.php",
    "content": "<?php\nuse Swoole\\Coroutine as co;\nco::set(['trace_flags' => 1]);\n\nco::create(function() {\n\n\n    $function = new ReflectionFunction('title');\n\n    $function->invoke();\n    echo \"invoke444\\n\";\n\n});\n\nfunction title() {\n    echo \"333invoke_________________________________\\n\";\n    $tcpclient = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n    var_dump($tcpclient->connect('127.0.0.1', 9501, 1));\n\n}\n\necho \"111\\n\";\n"
  },
  {
    "path": "examples/coroutine/coro_nested.php",
    "content": "<?php\nrequire __DIR__ . \"/coro_include.php\";\necho \"before coro\\n\";\ngo(function () {\n    echo \"co[1] start\\n\";\n\n    $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n    $res = $client->connect('127.0.0.1', 9501, 1);\n    echo \"co[1] connect ret = \".var_export($res,1).\"\\n\";\n    co::sleep(1);\n    $res = $client->send(\"hello world\\n\");\n    echo \"co[1] send ret = \".var_export($res,1).\"\\n\";\n    $res =  $client->recv();\n    echo \"co[1] recv ret = \".var_export($res,1).\"\\n\";\n    echo \"co[1] exit\\n\";\n});\necho \"out coro \\n\";\n"
  },
  {
    "path": "examples/coroutine/coro_nested_empty.php",
    "content": "<?php\nrequire __DIR__ . \"/coro_include.php\";\nfunction test()\n{\n    echo \"before coro\\n\";\n    go(function () {\n        echo \"co[1] start\\n\";\n        go(function () {\n            echo \"co[2] start\\n\";\n            echo \"co[2] exit\\n\";\n        });\n        echo \"co[1] exit\\n\";\n    });\n    echo \"func end \\n\";\n}\ntest();\n"
  },
  {
    "path": "examples/coroutine/coro_serialize.php",
    "content": "<?php\nuse Swoole\\Coroutine as co;\nclass Obj {\n  public $a;\n  protected $b;\n  private $c;\n  var $d;\n\n  function __construct($a, $b, $c, $d) {\n    $this->a = $a;\n    $this->b = $b;\n    $this->c = $c;\n    $this->d = $d;\n  }\n\n  function __sleep() {\n      // co::sleep(0.5);\n\t$client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n\t$res = $client->connect('127.0.0.1', 9501, 10);\n    var_dump($res);\n\tif ($res)\n\t{\n\t    echo(\"connect success. Error: {$client->errCode}\\n\");\n\t}\n    echo \"sleep\\n\";\n    return array('a', 'b', 'c');\n  }\n\n    // function __wakeup() {\n    //    $this->d = $this->a + $this->b + $this->c;\n    // }\n}\n$o = new Obj(1, 2, 3, 4);\nco::create(function() use($o) {\n    $serialized = serialize($o);\n    $unserialized = unserialize($serialized);\n    echo \"res:\".var_export($unserialized,1).\"\\n\";\n    echo \"call user\\n\";\n});\n"
  },
  {
    "path": "examples/coroutine/coro_set_stack_size.php",
    "content": "<?php\nuse Swoole\\Coroutine as co;\n\nco::set(['stack_size' => 1024*1024*4]);\n\nco::create(function () {\n    var_dump(co::stats());\n    echo \"no coro exit\\n\";\n});\necho \"exec file end\\n\";\n"
  },
  {
    "path": "examples/coroutine/coro_sleep.php",
    "content": "<?php\n// require  __DIR__ . \"/coro_include.php\";\nuse Swoole\\Coroutine as co;\nco::create(function () {\n    echo \"start\\n\";\n    co::sleep(0.5);\n    echo \"OK\\n\";\n});\necho \"11\\n\";\nSwoole\\Event::wait();\n"
  },
  {
    "path": "examples/coroutine/coro_stackless.php",
    "content": "<?php\nrequire __DIR__ . \"/coro_include.php\";\nuse Swoole\\Coroutine as co;\n\necho \"start\\n\";\nco::create(function () {\n    $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n    $res = $client->connect('127.0.0.1', 9501, 1);\n    var_dump($res);\n    if ($res) {\n        echo (\"connect success. Error: {$client->errCode}\\n\");\n    }\n\n    $res = $client->send(\"hello\");\n    echo \"send res:\" . var_export($res, 1) . \"\\n\";\n    $data = $client->recv();\n    echo \"recv data\" . var_export($data, 1) . \"\\n\";\n});\necho \"end\\n\";\n"
  },
  {
    "path": "examples/coroutine/coro_util.php",
    "content": "<?php\nuse Swoole\\Coroutine as co;\n\n$id = go(function(){\n    $id = co::getUid();\n    echo \"start coro $id\\n\";\n    co::suspend($id);\n    echo \"resume coro $id @1\\n\";\n    co::suspend($id);\n    echo \"resume coro $id @2\\n\";\n});\necho \"start to resume $id @1\\n\";\nco::resume($id);\necho \"start to resume $id @2\\n\";\nco::resume($id);\necho \"main\\n\";\n"
  },
  {
    "path": "examples/coroutine/csp.php",
    "content": "<?php\n$serv = new \\Swoole\\Http\\Server(\"127.0.0.1\", 9503, SWOOLE_BASE);\n\n$serv->on('request', function ($req, $resp) {\n    $chan = new chan(2);\n    go(function () use ($chan) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('www.baidu.com', 443, true);\n            $cli->set(['timeout' => 10]);\n            $cli->setHeaders([\n            'Host' => \"www.baidu.com\",\n            \"User-Agent\" => 'Chrome/49.0.2587.3',\n            'Accept' => 'text/html,application/xhtml+xml,application/xml',\n            'Accept-Encoding' => 'gzip',\n        ]);\n        $ret = $cli->get('/');\n        $chan->push(['www.baidu.com' => substr(trim(strip_tags($cli->body)), 0, 100)]);\n    });\n\n    go(function () use ($chan) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('www.taobao.com', 443, true);\n        $cli->set(['timeout' => 10]);\n        $cli->setHeaders([\n            'Host' => \"www.taobao.com\",\n            \"User-Agent\" => 'Chrome/49.0.2587.3',\n            'Accept' => 'text/html,application/xhtml+xml,application/xml',\n            'Accept-Encoding' => 'gzip',\n        ]);\n        $ret = $cli->get('/');\n        $chan->push(['www.taobao.com' => substr(trim(strip_tags($cli->body)), 0, 100)]);\n    });\n\n    $result = [];\n    for ($i = 0; $i < 2; $i++)\n    {\n        $result += $chan->pop();\n    }\n    $resp->header('Content-Type', 'text/html;charset=utf-8');\n    $resp->end(var_export($result, true));\n});\n$serv->start();\n"
  },
  {
    "path": "examples/coroutine/deadlock.php",
    "content": "<?php\n$lock = new Swoole\\Lock();\n$c = 2;\n\nwhile ($c--) {\n    go(function () use ($lock) {\n        $lock->lock();\n        Co::sleep(1);\n        $lock->unlock();\n    });\n}\n"
  },
  {
    "path": "examples/coroutine/defer.php",
    "content": "<?php\ngo(function () {\n\n    defer(function () {\n        co::sleep(1);\n        echo \"end 2\\n\";\n\n        defer(function () {\n            co::sleep(1);\n            echo \"end 3\\n\";\n        });\n\n        defer(function () {\n            co::sleep(1);\n            echo \"end 5\\n\";\n        });\n    });\n\n    defer(function () {\n        co::sleep(1);\n        echo \"end 4\\n\";\n    });\n\n    echo \"begin\\n\";\n    co::sleep(1);\n    echo \"end 1\\n\";\n});\n"
  },
  {
    "path": "examples/coroutine/defer_client.php",
    "content": "<?php\n/* new multi implement test */\n$server = new Swoole\\Http\\Server(\"127.0.0.1\", 9502, SWOOLE_BASE);\n\n$server->set([\n\t'worker_num' => 1,\n]);\n\n$server->on('Request', function ($request, $response) {\n\t$redis = new Swoole\\Coroutine\\Redis();\n\t$res = $redis->connect('127.0.0.1', 6379);\n\tif ($res == false) {\n\t\t$response->end(\"Redis connect fail!\");\n\t\treturn;\n\t}\n\t$redis->setDefer(true);\n\t$redis->get('key');\n\t$res = $redis->get('key');//get false\n\tvar_dump($res);\n\n\tvar_dump($redis->setDefer());//get true\n\tvar_dump($redis->setDefer(false));//get false\n\n\t//穿插其他client也能正常工作\n\t$redis_tmp = new Swoole\\Coroutine\\Redis();\n\t$res = $redis_tmp->connect('127.0.0.1', 6379);\n\tif ($res == false) {\n\t\t$response->end(\"Redis connect fail!\");\n\t\treturn;\n\t}\n\t$res = $redis_tmp->set('key_tmp', 'HaHa');//get true\n\tvar_dump($res);\n\n\n\t$http_client= new Swoole\\Coroutine\\Http\\Client('km.oa.com', 80);\n\t$http_client->setDefer();\n\t$http_client->get('/');\n\n\t$mysql = new Swoole\\Coroutine\\MySQL();\n\t$res = $mysql->connect(['host' => '192.168.244.128', 'user' => 'mha_manager', 'password' => 'mhapass', 'database' => 'tt']);\n\tif ($res == false) {\n\t\t$response->end(\"MySQL connect fail!\");\n\t\treturn;\n\t}\n\t$mysql->setDefer(true);\n\t$mysql->query('select sleep(1)', 2);\n\n\t$udp = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n    $res = $udp->connect(\"127.0.0.1\", 9906, 2);\n\t$udp->send('Hello World!');\n\n\t//穿插其他client也能正常工作\n\t$udp_tmp = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n    $res = $udp_tmp->connect(\"127.0.0.1\", 9909, 2);//nonexistent server\n\t$res = $udp_tmp->recv();//get false with timeout\n\tvar_dump($res);\n\n\t$udp_res = $udp->recv();\n\t$res = $mysql->query('select sleep(1)', 2);//get false\n\tvar_dump($res);\n\t$res = $mysql->setDefer(false);\n\tvar_dump($res);//get false\n\t$res = $mysql->setDefer();\n\tvar_dump($res);//get true\n\t$mysql_res = $mysql->recv();\n\t$res = $redis->get('key');//get false\n\tvar_dump($res);\n\t$redis_res = $redis->recv();\n\t$res = $http_client->get('/');\n\tvar_dump($res);//get false\n\t$res = $http_client->recv();\n\tvar_dump($res);//get true\n\n\tvar_dump($udp_res, $mysql_res, $redis_res, $http_client);\n\tvar_dump($http_client->setDefer(false));\n\tvar_dump($mysql->getDefer(), $redis->getDefer(), $http_client->getDefer());\n\t$response->end('Test End');\n});\n$server->start();\n"
  },
  {
    "path": "examples/coroutine/enable_coroutine.php",
    "content": "<?php\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$http = new Swoole\\Http\\Server('127.0.0.1', 9501);\n\n$http->set([\n    'enable_coroutine' => false, // close build-in coroutine\n]);\n\n$http->on('workerStart', function () {\n    echo \"Coroutine is \" . (Co::getuid() > 0 ? 'enable' : 'disable').\"\\n\";\n});\n\n$http->on(\"request\", function (Request $request, Response $response) {\n    $response->header(\"Content-Type\", \"text/plain\");\n    if ($request->server['request_uri'] == '/co') {\n        go(function () use ($response) {\n            $response->end(\"Hello Coroutine #\" . Co::getuid());\n        });\n    } else {\n        $response->end(\"Hello Swoole #\" . Co::getuid());\n    }\n});\n\n$http->start();\n"
  },
  {
    "path": "examples/coroutine/exception/empty.php",
    "content": "<?php\ngo(function () {\n    try {\n        echo \"before\\n\";\n        co::sleep(0.5);\n        echo \"after\\n\";\n        throw new Exception('coro Exception.');\n    } catch (Exception $e) {\n        echo 'Caught exception: ',  $e->getMessage(), \"\\n\";\n    } finally {\n        echo \"First finally.\\n\";\n    }\n});\necho \"exec file end\\n\";\n"
  },
  {
    "path": "examples/coroutine/exec.php",
    "content": "<?php\ngo(function () {\n\n\tvar_dump(co::exec(\"ls /\"));\n});\n"
  },
  {
    "path": "examples/coroutine/exit_in_coroutine.php",
    "content": "<?php\nfunction route()\n{\n    controller();\n}\n\nfunction controller()\n{\n    your_code();\n}\n\nfunction your_code()\n{\n    co::sleep(.001);\n    exit(1);\n}\n\ngo(function () {\n    try {\n        route();\n    } catch (\\Swoole\\ExitException $e) {\n        assert($e->getStatus() === 1);\n        assert($e->getFlags() === SWOOLE_EXIT_IN_COROUTINE);\n        return;\n    }\n});\n"
  },
  {
    "path": "examples/coroutine/exit_with_status.php",
    "content": "<?php\n$exit_status = 0;\ngo(function () {\n    try {\n        exit(123);\n    } catch (\\Swoole\\ExitException $e) {\n        global $exit_status;\n        $exit_status = $e->getStatus();\n    }\n});\nSwoole\\Event::wait();\nexit($exit_status);\n"
  },
  {
    "path": "examples/coroutine/fgets.php",
    "content": "<?Php\n$fp = fopen(__DIR__ . \"/defer_client.php\", \"r\");\nstream_set_chunk_size($fp, 1024);\n\ngo(function () use ($fp)\n{\n    for($i = 0; $i<100;$i++) {\n        $r =  co::fgets($fp);\n        if (empty($r) and feof($fp))\n        {\n            //echo \"EOF\\n\";\n            break;\n        }\n        //echo \"len=\".strlen($r).\"\\n\";\n        echo $r;\n        //echo \"---------------------------------------\\n\";\n        //var_dump($r);\n        //co::sleep(1);\n    }\n});\n"
  },
  {
    "path": "examples/coroutine/fread.php",
    "content": "<?php\nuse Swoole\\Coroutine as co;\n\n$fp = fopen(__DIR__ . \"/defer_client.php\", \"r\");\n\nco::create(function () use ($fp)\n{\n    fseek($fp, 256);\n    $r =  co::fread($fp);\n    var_dump($r);\n});\n"
  },
  {
    "path": "examples/coroutine/fwrite.php",
    "content": "<?php\nuse Swoole\\Coroutine as co;\n\n$fp = fopen(__DIR__ . \"/test.data\", \"a+\");\n\nco::create(function () use ($fp)\n{\n    $r =  co::fwrite($fp, \"hello world\\n\", 5);\n    var_dump($r);\n});\n"
  },
  {
    "path": "examples/coroutine/gethostbyname.php",
    "content": "<?php\nuse Swoole\\Coroutine as co;\n\nco::create(function() {\n    $ip = co::gethostbyname(\"www.baidu.com\");\n    echo \"IP: $ip\\n\";\n});\n"
  },
  {
    "path": "examples/coroutine/http/server.php",
    "content": "<?php\nCo::set([\n    'trace_flags' => SWOOLE_TRACE_HTTP2,\n    'log_level' => 0,\n]);\ngo(function () {\n\t$server = new Co\\Http\\Server(\"127.0.0.1\", 9501, false);\n\t/**\n\t * 静态文件处理器\n\t */\n\t//$server->handle('/static', $server->getStaticHandler());\n\t/**\n\t * WebSocket应用\n\t */\n\t$server->handle('/websocket', function ($request, $ws) {\n\t\t$ws->upgrade();\n\n        $frame1 = $ws->recv();\n        $frame2 = $ws->recv();\n\t\tvar_dump($frame1, $frame2);\n\n\t\t$ws->push(\"hello world\\n\");\n\n\t\twhile(true) {\n\t\t\techo \"recv begin:\\n\";\n\t\t\t$frame = $ws->recv();\n\t\t\tif ($frame == false) {\n\t\t\t    echo \"ws client is closed\\n\";\n                var_dump(\"Error: \", swoole_last_error());\n\t\t\t    break;\n            }\n\t\t\techo $frame->data .\"\\n\";\n\t\t\t$ws->push(\"hello world\");\n\t\t}\n\t});\n\t/**\n\t * Http应用\n\t */\n\t$server->handle('/', function ($request, $response) {\n// \t    var_dump($request);\n// \t\tvar_dump($request->get);\n\t\t$response->end(\"<h1>hello world</h1>\");\n\t});\n\n\t$server->handle('/test', function ($request, $response) {\n\t\tvar_dump($request->get);\n\t\t$response->end(\"<h1>Test</h1>\");\n\t});\n\n\t$server->start();\n});\n\nSwoole\\Event::wait();\n"
  },
  {
    "path": "examples/coroutine/http/write_func.php",
    "content": "<?php\nCo::set([\n    'trace_flags' => SWOOLE_TRACE_HTTP2,\n    'log_level' => 0,\n]);\nCo\\run(function () {\n\t$client = new Swoole\\Coroutine\\Http\\Client('www.jd.com', 443, true);\n    $client->set(['write_func' => function($client, $data) {\n        var_dump(strlen($data));\n    }]);\n    $client->get('/');\n    var_dump(strlen($client->getBody()));\n    return 0;\n});\n"
  },
  {
    "path": "examples/coroutine/http2_client.php",
    "content": "<?php\nuse Swoole\\Coroutine as co;\n\nconst TEST = array('get', 'post', 'pipeline');\n//const TEST = array('pipeline');\n//const TEST = array('get',);\n\nCO::set(['trace_flags' => SWOOLE_TRACE_HTTP2, \n// 'log_level' => SWOOLE_LOG_TRACE,\n\n\n\n]);\n\n\n\nco::create(function () use ($fp)\n{\n    $cli = new co\\Http2\\Client('127.0.0.1', 9518);\n\n    $cli->set([ 'timeout' => 1, 'package_max_length' => 1024*1024*8]);\n    var_dump($cli->connect());\n\n    if (in_array('get', TEST))\n    {\n        $req = new Swoole\\Http2\\Request;\n        $req->path = \"/index.html\";\n        $req->headers = [\n            'host' => \"localhost\",\n            \"user-agent\" => 'Chrome/49.0.2587.3',\n            'accept' => 'text/html,application/xhtml+xml,application/xml',\n            'accept-encoding' => 'gzip',\n        ];\n        $req->cookies = ['name' => 'rango', 'email' => '1234@qq.com'];\n        var_dump($cli->send($req));\n\n        $resp = $cli->recv();\n        var_dump($resp);\n    }\n\n    if (in_array('post', TEST))\n    {\n        $req2 = new Swoole\\Http2\\Request;\n        $req2->path = \"/index.php\";\n        $req2->headers = [\n            'host' => \"localhost\",\n            \"user-agent\" => 'Chrome/49.0.2587.3',\n            'accept' => 'text/html,application/xhtml+xml,application/xml',\n            'accept-encoding' => 'gzip',\n        ];\n        $req2->data = \"hello world\\n\";\n        var_dump($cli->send($req2));\n\n        $resp = $cli->recv();\n        var_dump($resp);\n    }\n\n    if (in_array('pipeline', TEST))\n    {\n        $req3 = new Swoole\\Http2\\Request;\n        $req3->path = \"/index.php\";\n        $req3->headers = [\n            'host' => \"localhost\",\n            \"user-agent\" => 'Chrome/49.0.2587.3',\n            'accept' => 'text/html,application/xhtml+xml,application/xml',\n            'accept-encoding' => 'gzip',\n        ];\n        $req3->pipeline = true;\n        $req3->method = \"POST\";\n        $streamId = $cli->send($req3);\n\n        $cli->write($streamId, ['int' => rand(1000, 9999)]);\n        $cli->write($streamId, ['int' => rand(1000, 9999)]);\n        //end stream\n        $cli->write($streamId, ['int' => rand(1000, 9999), 'end' => true], true);\n\n        var_dump($cli->recv());\n    }\n\n//    $cli->close();\n});\n"
  },
  {
    "path": "examples/coroutine/http_backend_serv.php",
    "content": "<?php\n/**\n * @Author: syyuanyizhi@163.com\n    connect refuse： errorCode  111\n    I/O     timeout：errorCode  110\n    http 9510\n    tcp  9511\n\n */\nclass Server\n{\n    public $server;\n\n    public function run()\n    {\n        $this->server = new Swoole\\Http\\Server(\"0.0.0.0\", 9510);\n        $this->server->set([\n            'worker_num' => 1,\n            'daemonize' => true,\n            'log_file' => '/data/markyuan/swoole.log',\n        ]);\n        $this->server->on('Request', ['Server', 'onRequest']);\n        $this->server->start();\n    }\n    public static function onRequest($request, $response)\n    {\n\n        $response->end('xxxx');\n    }\n\n\n    public static function staticFunc()\n    {\n        echo \"in static function\";\n    }\n}\n\n$server = new Server();\n\n$server->run();\n"
  },
  {
    "path": "examples/coroutine/http_client.php",
    "content": "<?php\nuse Swoole\\Coroutine as co;\nco::create(function () {\n    $cli = new co\\http\\client('127.0.0.1', 9501);\n    $cli->setHeaders(['Host' => 'localhost']);\n    $cli->set(['http_proxy_host' => HTTP_PROXY_HOST, 'http_proxy_port' => HTTP_PROXY_PORT]);\n    $result = $cli->get('/get?json=true');\n    var_dump($cli->body);\n//     assert($result);\n//     $ret = json_decode($cli->body, true);\n//     assert(is_array($ret) and $ret['json'] == 'true');\n});\n"
  },
  {
    "path": "examples/coroutine/http_download.php",
    "content": "<?php\n\nuse Swoole\\Coroutine\\Http\\Client;\n\nCo\\run(function () {\n    $host = 'www.swoole.com';\n    $cli = new Client($host, 443, true);\n    $cli->set(['timeout' => -1]);\n    $cli->setHeaders([\n        'Host' => $host,\n        \"User-Agent\" => 'Chrome/49.0.2587.3',\n        'Accept' => '*',\n        'Accept-Encoding' => 'gzip'\n    ]);\n    $cli->download('/dist/skin1/images/logo-white.png', '/tmp/logo.png');\n});\n"
  },
  {
    "path": "examples/coroutine/http_server.php",
    "content": "<?php\nini_set(\"memory_limit\",\"512M\");\nuse Swoole\\Coroutine as co;\nclass Server\n{\n    public $server;\n    public $redisPool = [];\n\n    public function run()\n    {\n        $this->server = new Swoole\\Http\\Server(\"0.0.0.0\", 9502, SWOOLE_BASE);\n        $this->server->set([\n            'worker_num' => 1,\n        ]);\n\n        // $this->server->on('Connect', [$this, 'onConnect']);\n        $this->server->on('Request', [$this, 'onRequest']);\n//         $this->server->on('Close', [$this, 'onClose']);\n        $this->server->set(['trace_flags' => 1 << 15, 'log_level' => 0]);\n        $this->server->start();\n    }\n\n    public function onRequest($request, $response)\n    {\n        $fd = $request->fd;\n        co::create(function () {\n            co::sleep(0.1);\n        });\n        $response->end(111);\n    }\n}\n\n$server = new Server();\nSwoole\\Coroutine::set(array(\n    'max_coroutine' => 1000,\n));\n$server->run();\n"
  },
  {
    "path": "examples/coroutine/httpmulti.php",
    "content": "<?php\n/**\n * @Author: syyuanyizhi@163.com\n    connect refuse： errorCode  111\n    I/O     timeout：errorCode  110\n    http 9510\n    tcp  9511\n\n */\nclass Server\n{\n    public $server;\n\n    public function run()\n    {\n        $this->server = new Swoole\\Http\\Server(\"0.0.0.0\", 9508);\n        $this->server->set([\n            'worker_num' => 1,\n            'daemonize' => true,\n            'log_file' => '/data/markyuan/swoole.log',\n        ]);\n        $this->server->on('Request', ['Server', 'onRequest']);\n        $this->server->start();\n    }\n\n    private static function https(){\n        //--enable-openssl\n        for($i=0;$i<2;$i++){\n            $cli = new Swoole\\Coroutine\\Http\\Client('0.0.0.0',443,TRUE );\n            $cli->set([ 'timeout' => 1]);\n            $cli->setHeaders([\n                'Host' => \"api.mp.qq.com\",\n                \"User-Agent\" => 'Chrome/49.0.2587.3',\n                'Accept' => 'text/html,application/xhtml+xml,application/xml',\n                'Accept-Encoding' => 'gzip',\n            ]);\n            $ret = ($cli->get('/cgi-bin/token?appid=3333&secret=222'.$i.$i.$i.$i.$i));\n            error_log(__LINE__.var_export($cli,true).PHP_EOL,3,'/tmp/markyuan');\n            $cli->close();\n        }\n    }\n\n    private static function http(){\n        error_log(__LINE__.'---------- begin --- http --------------'.PHP_EOL,3,'/tmp/markyuan');\n        for($i=0;$i<2;$i++){\n            $cli = new Swoole\\Coroutine\\Http\\Client('0.0.0.0', 9510);\n            $cli->set([ 'timeout' => 1]);\n            $cli->setHeaders([\n                'Host' => \"api.mp.qq.com\",\n                \"User-Agent\" => 'Chrome/49.0.2587.3',\n                'Accept' => 'text/html,application/xhtml+xml,application/xml',\n                'Accept-Encoding' => 'gzip',\n            ]);\n            error_log(__LINE__.var_export($cli,true).PHP_EOL,3,'/tmp/markyuan');\n            $ret = ($cli->get('/cn/token?appid=1FxxxxS9V'.$i.$i.$i.$i.$i));\n            error_log(__LINE__.var_export($ret,true).PHP_EOL,3,'/tmp/markyuan');\n            error_log(__LINE__.var_export($cli,true).PHP_EOL,3,'/tmp/markyuan');\n            $cli->close();\n        }\n        error_log(__LINE__.'---------- end --- http --------------'.PHP_EOL,3,'/tmp/markyuan');\n\n    }\n\n    private static function multihttp(){\n\n        error_log(__LINE__.'---------- begin --- multi --------------'.PHP_EOL,3,'/tmp/markyuan');\n\n        $cliAA= new Swoole\\Coroutine\\Http\\Client('0.0.0.0', 9510);\n        $cliAA->set(['timeout' => 1]);\n        $cliAA->setHeaders([\n            'Host' => \"api.mp.qq.com\",\n            \"User-Agent\" => 'Chrome/49.0.2587.3',\n        ]);\n        $cliBB= new Swoole\\Coroutine\\Http\\Client('0.0.0.0', 9510);\n        $cliBB->set([ 'timeout' => 1]);//\n        $cliBB->setHeaders([\n            'Host' => \"api.mp.qq.com\",\n            \"User-Agent\" => 'Chrome/49.0.2587.3',\n        ]);\n        error_log(__LINE__.var_export($cliAA,true).PHP_EOL,3,'/tmp/markyuan');\n        error_log(__LINE__.var_export($cliBB,true).PHP_EOL,3,'/tmp/markyuan');\n        $retAA=$cliAA->setDefer(1);\n        $retBB=$cliBB->setDefer(1);\n        error_log(__LINE__.var_export($retAA,true).PHP_EOL,3,'/tmp/markyuan');\n        error_log(__LINE__.var_export($retBB,true).PHP_EOL,3,'/tmp/markyuan');\n        error_log(__LINE__.var_export($cliAA,true).PHP_EOL,3,'/tmp/markyuan');\n        error_log(__LINE__.var_export($cliBB,true).PHP_EOL,3,'/tmp/markyuan');\n        $retAA = ($cliAA->get('/cn/token?appid=AAA'));\n        $retBB = ($cliBB->get('/cn/token?appid=BBB'));\n        error_log(__LINE__.var_export($retAA,true).PHP_EOL,3,'/tmp/markyuan');\n        error_log(__LINE__.var_export($retBB,true).PHP_EOL,3,'/tmp/markyuan');\n        error_log(__LINE__.var_export($cliAA,true).PHP_EOL,3,'/tmp/markyuan');\n        error_log(__LINE__.var_export($cliBB,true).PHP_EOL,3,'/tmp/markyuan');\n        $retAA=$cliAA->recv();\n        $retBB=$cliBB->recv();\n        error_log(__LINE__.var_export($retAA,true).PHP_EOL,3,'/tmp/markyuan');\n        error_log(__LINE__.var_export($retBB,true).PHP_EOL,3,'/tmp/markyuan');\n        error_log(__LINE__.var_export($cliAA,true).PHP_EOL,3,'/tmp/markyuan');\n        error_log(__LINE__.var_export($cliBB,true).PHP_EOL,3,'/tmp/markyuan');\n        $retAA=$cliAA->close();\n        $retBB=$cliBB->close();\n        error_log(__LINE__.'---------- end --- multi --------------'.PHP_EOL,3,'/tmp/markyuan');\n    }\n\n\n\n    private static function tcp(){\n        for($i=0;$i<2;$i++){\n            $tcp_cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n            $ret = $tcp_cli ->connect(\"0.0.0.0\", 9511);\n            $ret = $tcp_cli ->send('test for the coro');\n            $ret = $tcp_cli ->recv();\n            $ret=$tcp_cli->close();\n        }\n    }\n\n private static function coro_dns(){\n    swoole_async_set(array('use_async_resolver'=>1));\n    swoole_async_set(array('dns_cache_refresh_time'=>0));\n    $ret=swoole_async_dns_lookup_coro(\"www.baidu.com\",0.5);\n    error_log(' ip and host '.$host.print_r($ret,true),'3','/home/yuanyizhi/markyuan/markyuan.log');\n    return $ret;\n//  swoole_async_dns_lookup(\"www.baidu.com\", function($host, $ip){\n//  error_log(' ip and host '.$host.'  and  ip '.$ip,'3','/home/yuanyizhi/markyuan/markyuan.log');\n//  });\n    }\n\n\nprivate static function tcpmulti(){\n        $cliAA = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $cliBB = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $retAA = $cliAA ->connect(\"0.0.0.0\", 9511);\n        $retBB = $cliBB ->connect(\"0.0.0.0\", 9511);\n        $retAA = $cliAA ->send('test for the coro');\n        $retBB = $cliBB ->send('test for the coro');\n        $retAA = $cliAA->recv();\n        $retBB = $cliBB->recv();\n        $cliAA->close();\n        $cliBB->close();\n    }\n\n    public static function onRequest($request, $response)\n    {\n//        self::multihttp();\n//        self::http();\n        //self::https();\n//        self::tcp();\n      //  self::tcpmulti();\n        $ret=self::coro_dns();\n        $response->end(print_r($ret,true));\n    }\n\n\n    public static function staticFunc()\n    {\n        echo \"in static function\";\n    }\n}\n\n$server = new Server();\n\n$server->run();\n"
  },
  {
    "path": "examples/coroutine/join.php",
    "content": "<?php\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\n//run(function () {\n//    $cid_list = [];\n//    for ($i = 0; $i < 10; $i++) {\n//        $cid_list[] = go(function () use ($i) {\n//            System::sleep(.3);\n//            echo \"hello $i\\n\";\n//        });\n//    }\n//\n//    Coroutine::join($cid_list);\n//\n//    echo \"all done\\n\";\n//});\n\n\nrun(function () {\n    $result = [];\n    Coroutine::join([\n        go(function () use (&$result) {\n            $result['baidu'] = file_get_contents(\"https://www.baidu.com/\");\n        }),\n        go(function () use (&$result) {\n            $result['taobao'] = file_get_contents(\"https://www.taobao.com/\");\n        })\n    ]);\n\n    echo \"all done\\n\";\n    var_dump($result);\n});\n"
  },
  {
    "path": "examples/coroutine/library/base.php",
    "content": "<?php\nclass R\n{\n    public function __construct()\n    {\n        $cid = \\Swoole\\Coroutine::getCid();\n        echo \"cid:$cid \".__CLASS__ . '#' . \\spl_object_id((object)$this) . ' constructed' . PHP_EOL;\n    }\n    \n    public function __destruct()\n    {\n        $cid = \\Swoole\\Coroutine::getCid();\n        echo \"cid:$cid \".__CLASS__ . '#' . \\spl_object_id((object)$this) . ' destructed' . PHP_EOL;\n    }\n}\n\nclass Pool extends \\Swoole\\Coroutine\\ObjectPool\n{\n    public function __construct($type)\n    {\n        parent::__construct($type);\n    }\n    \n    public function create()\n    {\n        return new R;\n    }\n}\n\n$pool = new Pool(\"test\");\ngo(function() use ($pool){\n    $object = $pool->get();\n    $cid = \\Swoole\\Coroutine::getCid();\n    echo \"cid:$cid \".var_export($object,1).\"\\n\";\n});\ngo(function() use ($pool){\n    $object = $pool->get();\n    $cid = \\Swoole\\Coroutine::getCid();\n    echo \"cid:$cid \".var_export($object,1).\"\\n\";\n});\n"
  },
  {
    "path": "examples/coroutine/list_coroutines.php",
    "content": "<?php\nforeach(range(1, 100) as $i) {\n    go(function () use ($i) {\n        if ($i % 9 == 7) {\n            return;\n        }\n        while(true) {\n            co::sleep(10);\n            echo \"CORO: $i\\n\";\n        }\n    });\n}\n\ngo(function () {\n    while(true) {\n        co::sleep(3);\n        $coros = Co::listCoroutines();\n        $cids = iterator_to_array($coros );\n        var_dump($cids);\n    }\n});\n"
  },
  {
    "path": "examples/coroutine/mysql_chan.php",
    "content": "<?php\n\nuse Swoole\\Coroutine as co;\n\n$chan = new chan(4);\n\ngo(function () use ($chan) {\n\n    $db = new co\\MySQL();\n    $server = array(\n        'host' => '127.0.0.1',\n        'user' => 'root',\n        'password' => 'root',\n        'database' => 'test',\n    );\n\n    echo \"connect\\n\";\n    $ret1 = $db->connect($server);\n    var_dump($ret1);\n\n    echo \"prepare\\n\";\n    $ret2 = $db->query('SELECT * FROM userinfo WHERE id=3');\n    var_dump($ret2);\n\n    $chan->push($db);\n});\n\ngo(function () use ($chan) {\n    $db = $chan->pop();\n    $ret2 = $db->query('SELECT * FROM userinfo WHERE id=3');\n    var_dump($ret2);\n});\n"
  },
  {
    "path": "examples/coroutine/mysql_escape.php",
    "content": "<?php\n\ngo(function(){\n    $swoole_mysql = new \\Swoole\\Coroutine\\MySQL();\n    \n    $swoole_mysql->connect([\n        'host' => '127.0.0.1',\n        'port' => 3306,\n        'user' => 'root',\n        'password' => 'root',\n        'database' => 'test',\n    ]);\n    $res = $swoole_mysql->escape(\"\");\n    var_dump($res);\n});\n    \n"
  },
  {
    "path": "examples/coroutine/mysql_execute_empty.php",
    "content": "<?php\ngo(function () {\n    $db = new Swoole\\Coroutine\\Mysql;\n    $server = [\n        'host'     => '127.0.0.1',\n        'user'     => 'root',\n        'password' => 'root',\n        'database' => 'test'\n    ];\n    $db->connect($server);\n    $stmt = $db->prepare('SELECT * FROM `userinfo`');\n    $ret = $stmt->execute();\n    var_dump($ret);\n});\n"
  },
  {
    "path": "examples/coroutine/mysql_prepare.php",
    "content": "<?php\nuse Swoole\\Coroutine as co;\n\nco::create(function() {\n\n    $db = new co\\MySQL();\n    $server = array(\n        'host' => '127.0.0.1',\n        'user' => 'root',\n        'password' => 'root',\n        'database' => 'test',\n    );\n\n    echo \"connect\\n\";\n    $ret1 = $db->connect($server);\n    var_dump($ret1);\n\n    echo \"prepare [1]\\n\";\n    $stmt1 = $db->prepare('SELECT * FROM userinfo WHERE id=?');\n    var_dump($stmt1);\n    if ($stmt1 == false)\n    {\n        var_dump($db->errno, $db->error);\n    }\n\n    echo \"execute\\n\";\n    $ret3 = $stmt1->execute(array(10));\n    var_dump(count($ret3));\n\n    echo \"prepare [2]\\n\";\n    $stmt2 = $db->prepare('SELECT * FROM userinfo WHERE id > ? and level > ?');\n    var_dump($stmt2);\n    if ($stmt2 == false)\n    {\n        var_dump($db->errno, $db->error);\n    }\n\n    echo \"execute\\n\";\n    $ret4 = $stmt2->execute(array(10, 99));\n    var_dump($ret4);\n});\n"
  },
  {
    "path": "examples/coroutine/mysql_prepare_2.php",
    "content": "<?php\n\n\ngo(function() {\n\n    $db = new Co\\MySQL();\n    $server = array(\n        'host' => '127.0.0.1',\n        'user' => 'root',\n        'password' => 'root',\n        'database' => 'test',\n    );\n\n    echo \"connect\\n\";\n    $ret1 = $db->connect($server);\n    var_dump($ret1);\n\n    echo \"prepare [1]\\n\";\n    $stmt1 = $db->prepare('show tables');\n    echo \"execute\\n\";\n    $ret1 = $stmt1->execute([]);\n    var_dump($ret1);\n\n});\n"
  },
  {
    "path": "examples/coroutine/mysql_procedure_exec.php",
    "content": "<?php\n\ngo(function () {\n    $db = new Swoole\\Coroutine\\Mysql;\n    $server = [\n        'host' => '127.0.0.1',\n        'user' => 'root',\n        'password' => 'root',\n        'database' => 'test'\n    ];\n\n    $clear = <<<SQL\n    DROP PROCEDURE IF EXISTS `say`\nSQL;\n    $procedure = <<<SQL\n  CREATE DEFINER=`root`@`localhost` PROCEDURE `say`(content varchar(255))\n  BEGIN\n    SELECT concat('you said: \\\"', content, '\\\"');\n  END\nSQL;\n\n    $db->connect($server);\n    if ($db->query($clear) && $db->query($procedure)) {\n        $stmt = $db->prepare('CALL say(?)');\n        $ret = $stmt->execute(['hello mysql!']);\n        var_dump(current($ret[0])); // you said: \"hello mysql!\"\n    }\n});\n"
  },
  {
    "path": "examples/coroutine/mysql_query.php",
    "content": "<?php\nuse Swoole\\Coroutine as co;\nco::set(['trace_flags' => 1]);\n\nco::create(function() {\n\n\n    $function = new ReflectionFunction('title');\n\n    $function->invoke();\n    echo \"invoke444\\n\";\n\n});\n\nfunction title() {\n    echo \"333invoke_________________________________\\n\";\n    $tcpclient = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n    var_dump($tcpclient->connect('127.0.0.1', 9501, 1));\n\n}\n\necho \"111\\n\";\n\n\necho \"222\\n\";\nco::go();\n"
  },
  {
    "path": "examples/coroutine/mysql_unixsocket.php",
    "content": "<?php\n\ngo(function(){\n    $db = new Swoole\\Coroutine\\Mysql;\n    $server = [\n        'host'     => 'unix:/tmp/mysql.sock',\n        'user'     => 'root',\n        'password' => 'root',\n        'database' => 'test'\n    ];\n    $db->connect($server);\n    $stmt = $db->prepare('SELECT * FROM `user` WHERE id=?');\n    $ret = $stmt->execute([1]);\n    var_dump($ret);\n});\n"
  },
  {
    "path": "examples/coroutine/pdo/pdo_persistent.phpt",
    "content": "<?php\nCo\\run(function () {\n    $pdo = new PDO(\n        \"mysql:host=\" . MYSQL_SERVER_HOST . \";port=\" . MYSQL_SERVER_PORT . \";dbname=\" . MYSQL_SERVER_DB . \";charset=utf8\",\n        MYSQL_SERVER_USER, MYSQL_SERVER_PWD,\n        array(PDO::ATTR_PERSISTENT => true)\n    );\n    echo \"connected\\n\";\n    sleep(30);\n    echo \"sleep 30\\n\";\n    $pdo->exec(\"SELECT sleep(1)\");\n});\n\necho \"DONE\\n\";\n"
  },
  {
    "path": "examples/coroutine/proc_open.php",
    "content": "<?php\nSwoole\\Runtime::setHookFlags(SWOOLE_HOOK_ALL);\nSwoole\\Coroutine\\run(function () {\n\n    $descriptorspec = array(\n        0 => array(\"pipe\", \"r\"),\n        1 => array(\"pipe\", \"w\"),\n        2 => array(\"pipe\", \"w\"),\n    );\n\n    $process = proc_open('unknown', $descriptorspec, $pipes);\n\n    var_dump($pipes);\n\n    var_dump(fread($pipes[2], 8192));\n\n    $return_value = proc_close($process);\n\n    echo \"command returned $return_value\\n\";\n});"
  },
  {
    "path": "examples/coroutine/reconnect_test.php",
    "content": "<?php\n/* new multi implement test */\n$server = new Swoole\\Http\\Server(\"127.0.0.1\", 9502, SWOOLE_BASE);\n\n$server->set([\n\t'worker_num' => 1,\n]);\n\n$server->on('Request', function ($request, $response) {\n\n\t/*\n\t$mysql = new Swoole\\Coroutine\\MySQL();\n\t$res = $mysql->connect(['host' => '192.168.244.128', 'user' => 'mha_manager', 'password' => 'mhapass', 'database' => 'tt']);\n\tif ($res == false) {\n\t\t$response->end(\"MySQL connect fail!\");\n\t\treturn;\n\t}\n\t$res = $mysql->connect(['host' => '192.168.244.128', 'user' => 'mha_manager', 'password' => 'mhapass', 'database' => 'tt']);\n\tif ($res == false) {\n\t\t$response->end(\"MySQL connect fail!\");\n\t\treturn;\n\t}\n\t$mysql->close();\n\n\t$res = $mysql->connect(['host' => '192.168.244.128', 'user' => 'mha_manager', 'password' => 'mhapass', 'database' => 'tt']);\n\tif ($res == false) {\n\t\t$response->end(\"MySQL connect fail!\");\n\t\treturn;\n\t}\n\t$res = $mysql->query('select sleep(1)', 2);\n\tvar_dump($res);\n\n\t$res = $mysql->connect(['host' => '192.168.244.128', 'user' => 'mha_manager', 'password' => 'mhapass', 'database' => 'tt']);\n\tif ($res == false) {\n\t\t$response->end(\"MySQL connect fail!\");\n\t\treturn;\n\t}\n\t$res = $mysql->query('select sleep(1)', 2);\n\tvar_dump($res);\n\t*/\n\n\t$redis = new Swoole\\Coroutine\\Redis();\n\t$res = $redis->connect('127.0.0.1', 6379);\n\tif ($res == false) {\n\t\t$response->end(\"Redis connect fail!\");\n\t\treturn;\n\t}\n\t$res = $redis->connect('127.0.0.1', 6379);\n\tif ($res == false) {\n\t\t$response->end(\"Redis connect fail!\");\n\t\treturn;\n\t}\n\t$redis->close();\n\t$res = $redis->connect('127.0.0.1', 6379);\n\tif ($res == false) {\n\t\t$response->end(\"Redis connect fail!\");\n\t\treturn;\n\t}\n\t$res = $redis->get('key');\n\tvar_dump($res);\n\t$res = $redis->connect('127.0.0.1', 6379);\n\tif ($res == false) {\n\t\t$response->end(\"Redis connect fail!\");\n\t\treturn;\n\t}\n\t$res = $redis->get('key');\n\tvar_dump($res);\n\n\t$response->end('Test End');\n});\n$server->start();\n"
  },
  {
    "path": "examples/coroutine/redis/auth.php",
    "content": "<?php\ngo(function () {\n    $redis = new Swoole\\Coroutine\\Redis;\n    $redis->connect('127.0.0.1', 6379);\n    $redis->auth('root');\n    $redis->set('key', 'swoole redis work');\n    var_dump($redis->get('key'));\n});\n"
  },
  {
    "path": "examples/coroutine/redis/defer.php",
    "content": "<?php\n\nconst REDIS_SERVER_HOST = '127.0.0.1';\nconst REDIS_SERVER_PORT = 6379;\n\n\ngo(function () {\n    $redis = new Swoole\\Coroutine\\Redis();\n    $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT);\n    $redis->setDefer();\n    $redis->set('key1', 'value');\n\n    $redis2 = new Swoole\\Coroutine\\Redis();\n    $redis2->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT);\n    $redis2->setDefer();\n    $redis2->get('key1');\n\n    $result1 = $redis->recv();\n    $result2 = $redis2->recv();\n\n    var_dump($result1, $result2);\n});\n\n\n"
  },
  {
    "path": "examples/coroutine/redis/eval.php",
    "content": "<?php\ngo(function (){\n    $redis = new Co\\Redis;\n    $redis->connect('127.0.0.1', 6379);\n    $res = $redis->eval(\"return redis.call('get', 'key')\");\n    var_dump($res);\n});\n"
  },
  {
    "path": "examples/coroutine/redis/get.php",
    "content": "<?php\ngo(function (){\n\t$redis = new CO\\Redis;\n\tvar_dump($redis->connect(\"127.0.0.1\", 6379));\n\tvar_dump($redis->get(\"key\"));\n\tvar_dump($redis->set(\"key_22\", str_repeat('A', 8192*256)));\n\tvar_dump($redis->mget([\"key\", \"key_22\"]));\n});\n"
  },
  {
    "path": "examples/coroutine/redis/multi.php",
    "content": "<?php\nconst REDIS_SERVER_HOST = '127.0.0.1';\nconst REDIS_SERVER_PORT = 6379;\n\n\ngo(function () {\n    $redis = new Swoole\\Coroutine\\Redis();\n    $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT);\n    $redis->multi();\n    $redis->set('key3', 'rango');\n    $redis->get('key1');\n    $redis->get('key2');\n    $redis->get('key3');\n\n    $result = $redis->exec();\n    var_dump($result);\n});\n\n\n"
  },
  {
    "path": "examples/coroutine/redis/pipeline.php",
    "content": "<?php\nconst REDIS_SERVER_HOST = '127.0.0.1';\nconst REDIS_SERVER_PORT = 6379;\n\n\ngo(function () {\n    $redis = new Swoole\\Coroutine\\Redis();\n    $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT);\n    $redis->setDefer();\n    $redis->set('key1', 'value');\n    $redis->get('key1');\n\n    $result1 = $redis->recv();\n    $result2 = $redis->recv();\n\n    var_dump($result1, $result2);\n});\n\n\n"
  },
  {
    "path": "examples/coroutine/redis/pub.php",
    "content": "<?php\ngo(function () {\n\n$redis = new Swoole\\Coroutine\\Redis();\n$redis->connect('127.0.0.1', 6379);\nwhile (true) \n{\n\t$msg = $redis->publish('msg_1', 'hello-' . $i++);\n\tvar_dump($msg);\n\tco::sleep(1);\n}\n\n});\n"
  },
  {
    "path": "examples/coroutine/redis/request.php",
    "content": "<?php\ngo(function () {\n    $redis = new Co\\Redis;\n    $redis->connect('127.0.0.1', 6379);\n    $res = $redis->request(['object', 'encoding', 'key1']);\n    var_dump($res);\n});\n"
  },
  {
    "path": "examples/coroutine/redis/serialize.php",
    "content": "<?php\n\ngo(function () {\n    $redis = new \\Swoole\\Coroutine\\Redis;\n    $redis->connect('127.0.0.1', 6379, true); // param3 is serialize\n    $redis->set('foo', ['bar' => 'baz']);\n    $ret = $redis->get('foo');\n    var_dump($ret);\n});\n"
  },
  {
    "path": "examples/coroutine/redis/sub.php",
    "content": "<?php\ngo(function () {\n\t$redis = new Swoole\\Coroutine\\Redis();\n\t$redis->connect('127.0.0.1', 6379);\n\t$msg = $redis->subscribe(array(\"msg_1\"));\n\twhile ($msg = $redis->recv()) \n\t{\n\t\tvar_dump($msg);\n\t}\n});\n\ngo(function () {\n\t$redis = new Swoole\\Coroutine\\Redis();\n\t$redis->connect('127.0.0.1', 6379);\n\t$msg = $redis->subscribe(array(\"msg_2\"));\n\twhile ($msg = $redis->recv()) \n\t{\n\t\tvar_dump($msg);\n\t}\n});\n"
  },
  {
    "path": "examples/coroutine/redis_pool.php",
    "content": "<?php\n$count = 0;\n$pool = new SplQueue();\n$server = new Swoole\\Http\\Server('127.0.0.1', 9501, SWOOLE_BASE);\n\n$server->on('Request', function($request, $response) use(&$count, $pool) {\n    if (count($pool) == 0) {\n        $redis = new Swoole\\Coroutine\\Redis();\n        $res = $redis->connect('127.0.0.1', 6379);\n        if ($res == false) {\n            $response->end(\"redis connect fail!\");\n            return;\n        }\n        $pool->enqueue($redis);\n    }\n    $redis = $pool->dequeue();\n    $count ++;\n    $ret = $redis->set('key', 'value');\n    $response->end(\"swoole response is ok, count = $count, result=\" . var_export($ret, true));\n    $pool->enqueue($redis);\n});\n\n$server->start();\n"
  },
  {
    "path": "examples/coroutine/redis_subscribe.php",
    "content": "<?php\n\nuse Swoole\\Coroutine as co;\n\nco::create(function () {\n    $redis = new co\\Redis();\n    $redis->connect('127.0.0.1', 6379);\n    while (true)\n    {\n        $val = $redis->subscribe(['test']);\n        //订阅的channel，以第一次调用subscribe时的channel为准，后续的subscribe调用是为了收取Redis Server的回包\n        //如果需要改变订阅的channel，请close掉连接，再调用subscribe\n        var_dump($val);\n    }\n});\n"
  },
  {
    "path": "examples/coroutine/scheduler.php",
    "content": "<?php\nCo\\Run(function () {\n    Co::sleep(0.2);\n    echo \"hello world\\n\";\n});"
  },
  {
    "path": "examples/coroutine/select/1.php",
    "content": "<?php\n$c1 = new chan();\n//consumer first with select mode\n$num = 10;\ngo(function () use ($c1,$num) {\n    $read_list = [$c1];\n    $write_list = null;\n    echo \"select yield\\n\";\n    $result = chan::select($read_list, $write_list, 2);\n    echo \"select resume res: \".var_export($result,1).\"\\n\";\n    if ($read_list)\n    {\n        foreach($read_list as $ch)\n        {\n            for ($i=0;$i<$num;$i++)\n            {\n                $ret = $ch->pop();\n                echo \"pop [#$i] ret:\".var_export($ret,1).\"\\n\";\n            }\n        }\n    }\n});\n\ngo(function () use ($c1,$num) {\n    echo \"push start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        $ret = $c1->push(\"data-$i\");\n        echo \"push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n\n});\necho \"main end\\n\";\n"
  },
  {
    "path": "examples/coroutine/select/2.php",
    "content": "<?php\n$c1 = new chan();\n//consumer first without select mode\n$num = 10;\ngo(function () use ($c1, $num) {\n    echo \"pop start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        $ret = $c1->pop();\n        echo \"pop [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n});\n\ngo(function () use ($c1,$num) {\n    echo \"push start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        $ret = $c1->push(\"data-$i\");\n        echo \"push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n\n});\necho \"main end\\n\";\n"
  },
  {
    "path": "examples/coroutine/select/3.php",
    "content": "<?php\n$c1 = new chan(1);\n//product first with select mode\n$num = 10;\ngo(function () use ($c1,$num) {\n    echo \"push start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        $ret = $c1->push(\"data-$i\");\n        echo \"push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n});\n\ngo(function () use ($c1,$num) {\n    $read_list = [$c1];\n    $write_list = null;\n    echo \"select yield\\n\";\n    $result = chan::select($read_list, $write_list, 2);\n    echo \"select resume res: \".var_export($result,1).\"\\n\";\n    if ($read_list)\n    {\n        foreach($read_list as $ch)\n        {\n            for ($i=0;$i<$num;$i++)\n            {\n                $ret = $ch->pop();\n                echo \"pop [#$i] ret:\".var_export($ret,1).\"\\n\";\n            }\n        }\n    }\n});\necho \"main end\\n\";\n"
  },
  {
    "path": "examples/coroutine/select/4.php",
    "content": "<?php\n$c1 = new chan(2);\n//product first without select mode\n$num = 10;\ngo(function () use ($c1,$num) {\n    echo \"push start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        $ret = $c1->push(\"data-$i\");\n        echo \"push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n});\n\ngo(function () use ($c1, $num) {\n    echo \"pop start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        $ret = $c1->pop();\n        echo \"pop [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n});\necho \"main end\\n\";\n"
  },
  {
    "path": "examples/coroutine/select/5.php",
    "content": "<?php\n$c1 = new chan();\n$c2 = new chan();\nfunction fibonacci($c1, $c2)\n{\n    go(function () use ($c1, $c2) {\n        $a = 0;\n        $b = 1;\n        while(1) {\n            $read_list = [$c2];\n            $write_list = [$c1];\n            $result = chan::select($read_list, $write_list, 2);\n            if ($write_list) {\n                $t = $a + $b;\n                $a = $b;\n                $b = $t;\n                $write_list[0]->push($a);\n            }\n            if ($read_list) {\n                $ret = $read_list[0]->pop();\n                if ($ret === 1) {\n                    echo \"quit\\n\";\n                    return 1;\n                }\n            }\n        }\n    });\n}\n$num = 10;\ngo(function () use ($c1, $c2, $num) {\n    for ($i = 0; $i < $num; $i ++) {\n        $ret = $c1->pop();\n        echo \"fibonacci @$i $ret\\n\";\n    }\n    $c2->push(1);\n});\nfibonacci($c1, $c2);\n"
  },
  {
    "path": "examples/coroutine/select/6.php",
    "content": "<?php\n$c1 = new chan();\n\n$num = 10;\ngo(function () use ($c1,$num) {\n    $read_list = [$c1];\n    $write_list = null;\n    echo \"select yield\\n\";\n    $result = chan::select($read_list, $write_list, 2);\n    echo \"select resume res: \".var_export($result,1).\"\\n\";\n    if ($read_list)\n    {\n        foreach($read_list as $ch)\n        {\n            for ($i=0;$i<$num;$i++)\n            {\n                $ret = $ch->pop();\n                echo \"pop [#$i] ret:\".var_export($ret,1).\"\\n\";\n            }\n        }\n    }\n});\n\ngo(function () use ($c1,$num) {\n    echo \"push start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        if ($i == 2) {\n            echo \"start sleep\\n\";\n            co:sleep(1);\n            echo \"end sleep\\n\";\n        }\n        $ret = $c1->push(\"data-$i\");\n        echo \"push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n\n});\necho \"main end\\n\";\n"
  },
  {
    "path": "examples/coroutine/select/7.php",
    "content": "<?php\n//chan1 block and chan buffer\n$c1 = new chan();\n$c2 = new chan(10);\n$num = 10;\ngo(function () use ($c2,$num) {\n    for ($i=0;$i<$num;$i++)\n    {\n        $ret = $c2->push(\"chan2-$i\");\n        echo \"chan 2 push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n});\ngo(function () use ($c1,$num) {\n    $read_list = [$c1];\n    $write_list = null;\n    $result = chan::select($read_list, $write_list, 2);\n    echo \"select resume res: \".var_export($result,1).\"\\n\";\n    if ($read_list)\n    {\n        foreach($read_list as $ch)\n        {\n            for ($i=0;$i<$num;$i++)\n            {\n                $ret = $ch->pop();\n                echo \"chan1 pop [#$i] ret:\".var_export($ret,1).\"\\n\";\n            }\n        }\n    }\n});\n\ngo(function () use ($c1,$num) {\n    echo \"chan1 push start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        if ($i == 2) {\n            echo \"start sleep\\n\";\n            co:sleep(1);\n            echo \"end sleep\\n\";\n        }\n        $ret = $c1->push(\"chan1-$i\");\n        echo \"chan1 push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n\n});\n\ngo(function () use ($c2,$num) {\n    echo \"chan2 pop start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        $ret = $c2->pop();\n        echo \"chan2 pop [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n});\necho \"main end\\n\";\n"
  },
  {
    "path": "examples/coroutine/select/8.php",
    "content": "<?php\n//chan1 block and chan buffer\n$c1 = new chan();\n$c2 = new chan(10);\n$num = 10;\ngo(function () use ($c2,$num) {\n    for ($i=0;$i<$num;$i++)\n    {\n        $ret = $c2->push(\"chan2-$i\");\n        echo \"chan 2 push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n});\n\ngo(function () use ($c1,$c2,$num) {\n    $ori_list = $read_list = [$c1,$c2];\n    $write_list = null;\n    $result = chan::select($read_list, $write_list, 2);\n    echo \"select resume res: \".var_export($result,1).\"\\n\";\n\n    if ($ori_list)\n    {\n        foreach ($ori_list as $chan => $ch)\n        {\n            for ($i=0;$i<$num;$i++)\n            {\n                $ret = $ch->pop();\n                $chan_id = $chan + 1;\n                echo \"chan{$chan_id} pop [#$i] ret:\".var_export($ret,1).\"\\n\";\n            }\n        }\n    }\n});\n\ngo(function () use ($c1,$num) {\n    echo \"chan1 push start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        if ($i == 2) {\n            echo \"start sleep\\n\";\n            co:sleep(1);\n            echo \"end sleep\\n\";\n        }\n        $ret = $c1->push(\"chan1-$i\");\n        echo \"chan1 push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n\n});\necho \"main end\\n\";\n"
  },
  {
    "path": "examples/coroutine/select/9.php",
    "content": "<?php\n//chan1 block and chan buffer\n$c1 = new chan(0);\ngo(function () use ($c1) {\n    $num = 10;\n    for ($i=0;$i<$num;$i++)\n    {\n        $ret = $c1->push(\"chan1-$i\");\n        echo \"chan push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n});\n\ngo(function () use ($c1) {\n    $ret = $c1->pop();\n    echo \"chan pop ret:\".var_export($ret,1).\"\\n\";\n    co::sleep(1);\n    $ret = $c1->pop();\n    echo \"chan pop ret:\".var_export($ret,1).\"\\n\";\n});\n"
  },
  {
    "path": "examples/coroutine/select/poptimeout.php",
    "content": "<?php\n//chan1 block and chan buffer\n$c1 = new chan(1);\n\n\ngo(function () use ($c1) {\n    $start = microtime(1);\n    $ret = $c1->pop(1);\n    $end = microtime(1);\n    echo \"chan pop ret:\".var_export($ret,1).\" error:\".$c1->errCode.\"\\n\";\n    echo \"use time:\".($end-$start).\"s\\n\";\n\n});\n\ngo(function () use ($c1) {\n    co::sleep(2);\n    echo \"sleep 2\\n\";\n    $ret = $c1->push(\"chan-1\");\n    echo \"chan push ret:\".var_export($ret,1).\" error:\".$c1->errCode.\"\\n\";\n});\n"
  },
  {
    "path": "examples/coroutine/select/test.php",
    "content": "<?php\nuse Swoole\\Coroutine as co;\n\n$chan = new co\\Channel(2);\n$n = 10;\nfor ($i = 0; $i < $n; $i++) {\n    go(function () use ($i,$chan) {\n        $ret = $chan->push($i);\n        echo \"push {$i} res:\".var_export($ret, 1).\"\\n\";\n    });\n};\ngo(function ()use ($chan){\n    $bool = true;\n    while ($bool){\n        $data = $chan->pop();\n        echo \"pop res:\".var_export($data, 1).\"\\n\";\n        if($data===false){\n            $bool = false;\n        }\n        //var_dump($data);\n    }\n});\n"
  },
  {
    "path": "examples/coroutine/send_yield.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"0.0.0.0\", 9501, SWOOLE_BASE);\n$serv->set(array(\n    'worker_num' => 1,\n    'send_yield' => true,\n    'socket_buffer_size' => 512 * 1024,\n    'kernel_socket_buffer_size' => 65536,\n));\n$serv->on('connect', function ($serv, $fd) {\n    echo \"Client:Connect.\\n\";\n});\n$serv->on('receive', function ($serv, $fd, $reactor_id, $data) {\n    $length = 0;\n    $size = 1024 * 128;\n    while (true)\n    {\n        $ret = $serv->send($fd, str_repeat('A', $size));\n        if ($ret == false) {\n            break;\n        }\n        $length += $size;\n        echo \"send $length success\\n\";\n    }\n});\n$serv->on('close', function ($serv, $fd) {\n    echo \"Client: Close.\\n\";\n});\n$serv->start();\n"
  },
  {
    "path": "examples/coroutine/send_yield_client.php",
    "content": "<?php\n$client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n$client->set(array(\n    'kernel_socket_buffer_size' => 65536,\n));\n\nif (!$client->connect('127.0.0.1', 9501, -1))\n{\n    exit(\"connect failed. Error: {$client->errCode}\\n\");\n}\n\nvar_dump($client->getsockname());\n\n$client->send(\"start\\n\");\n$length = 0;\n\nwhile(true)\n{\n    $data = $client->recv(65536);\n    if ($data == false) {\n        break;\n    }\n    $length += strlen($data);\n    echo \"recv \".$length.\" bytes\\n\";\n    usleep(100000);\n}\n"
  },
  {
    "path": "examples/coroutine/server/tcp.php",
    "content": "<?php\n\nuse Swoole\\Coroutine\\Server;\nuse Swoole\\Coroutine\\Server\\Connection;\n\ngo(function () {\n    $server = new Server('0.0.0.0', 9501, false);\n\n    $server->handle(function (Connection $conn) {\n        while (true) {\n\n            $data = $conn->recv();\n            if (!$data) {\n                break;\n            }\n            $conn->send(\"hello $data\");\n        }\n        $conn->close();\n    });\n\n    $server->start();\n});\n\nSwoole\\Event::wait();\n"
  },
  {
    "path": "examples/coroutine/sleep.php",
    "content": "<?php\n$server = new Swoole\\Http\\Server(\"127.0.0.1\", 9502, SWOOLE_BASE);\n\n$server->set([\n    'worker_num' => 1,\n]);\n\n$server->on('Request', function ($request, $response) {\n    Swoole\\Coroutine::sleep(0.2);\n    $response->end('Test End');\n});\n\n$server->start();\n"
  },
  {
    "path": "examples/coroutine/socket/accept.php",
    "content": "<?php\ngo(function () {\n    $sock = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    $ret = $sock->bind('127.0.0.1', 9601);\n    var_dump($ret);\n    assert($sock->listen(512));\n    $conn = $sock->accept();\n\n    $data = $conn->recv();\n    var_dump($data);\n    $json = json_decode($data, true);\n    var_dump($json);\n    $ret = $conn->send(\"world\\n\");\n    echo \"send res {$ret} \\n\";\n    $conn->close();\n});\n"
  },
  {
    "path": "examples/coroutine/socket/sendto.php",
    "content": "<?php\n\necho \"start \\n\";\ngo(function ()  {\n    $conn = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    $ret = $conn->connect('127.0.0.1', 9601);\n    echo \"connect ret:\".var_export($ret,1).\" error:\".var_export($conn->errCode,1).\"\\n\";\n    $ret = $conn->send(json_encode(['data' => 'hello']));\n    echo \"send ret:\".var_export($ret,1).\"\\n\";\n    echo $conn->recv();\n});\necho \"end \\n\";\n"
  },
  {
    "path": "examples/coroutine/stack/1.php",
    "content": "<?php\ngo(function () {\n    $a_255 = 255;\n    $a_254 = 254;\n    $a_253 = 253;\n    $a_252 = 252;\n    $a_251 = 251;\n    $a_250 = 250;\n    $a_249 = 249;\n    $a_248 = 248;\n    $a_247 = 247;\n    $a_246 = 246;\n    $a_245 = 245;\n    $a_244 = 244;\n    $a_243 = 243;\n    $a_242 = 242;\n    $a_241 = 241;\n    $a_240 = 240;\n    $a_239 = 239;\n    $a_238 = 238;\n    $a_237 = 237;\n    $a_236 = 236;\n    $a_235 = 235;\n    $a_234 = 234;\n    $a_233 = 233;\n    $a_232 = 232;\n    $a_231 = 231;\n    $a_230 = 230;\n    $a_229 = 229;\n    $a_228 = 228;\n    $a_227 = 227;\n    $a_226 = 226;\n    $a_225 = 225;\n    $a_224 = 224;\n    $a_223 = 223;\n    $a_222 = 222;\n    $a_221 = 221;\n    $a_220 = 220;\n    $a_219 = 219;\n    $a_218 = 218;\n    $a_217 = 217;\n    $a_216 = 216;\n    $a_215 = 215;\n    $a_214 = 214;\n    $a_213 = 213;\n    $a_212 = 212;\n    $a_211 = 211;\n    $a_210 = 210;\n    $a_209 = 209;\n    $a_208 = 208;\n    $a_207 = 207;\n    $a_206 = 206;\n    $a_205 = 205;\n    $a_204 = 204;\n    $a_203 = 203;\n    $a_202 = 202;\n    $a_201 = 201;\n    $a_200 = 200;\n    $a_199 = 199;\n    $a_198 = 198;\n    $a_197 = 197;\n    $a_196 = 196;\n    $a_195 = 195;\n    $a_194 = 194;\n    $a_193 = 193;\n    $a_192 = 192;\n    $a_191 = 191;\n    $a_190 = 190;\n    $a_189 = 189;\n    $a_188 = 188;\n    $a_187 = 187;\n    $a_186 = 186;\n    $a_185 = 185;\n    $a_184 = 184;\n    $a_183 = 183;\n    $a_182 = 182;\n    $a_181 = 181;\n    $a_180 = 180;\n    $a_179 = 179;\n    $a_178 = 178;\n    $a_177 = 177;\n    $a_176 = 176;\n    $a_175 = 175;\n    $a_174 = 174;\n    $a_173 = 173;\n    $a_172 = 172;\n    $a_171 = 171;\n    $a_170 = 170;\n    $a_169 = 169;\n    $a_168 = 168;\n    $a_167 = 167;\n    $a_166 = 166;\n    $a_165 = 165;\n    $a_164 = 164;\n    $a_163 = 163;\n    $a_162 = 162;\n    $a_161 = 161;\n    $a_160 = 160;\n    $a_159 = 159;\n    $a_158 = 158;\n    $a_157 = 157;\n    $a_156 = 156;\n    $a_155 = 155;\n    $a_154 = 154;\n    $a_153 = 153;\n    $a_152 = 152;\n    $a_151 = 151;\n    $a_150 = 150;\n    $a_149 = 149;\n    $a_148 = 148;\n    $a_147 = 147;\n    $a_146 = 146;\n    $a_145 = 145;\n    $a_144 = 144;\n    $a_143 = 143;\n    $a_142 = 142;\n    $a_141 = 141;\n    $a_140 = 140;\n    $a_139 = 139;\n    $a_138 = 138;\n    $a_137 = 137;\n    $a_136 = 136;\n    $a_135 = 135;\n    $a_134 = 134;\n    $a_133 = 133;\n    $a_132 = 132;\n    $a_131 = 131;\n    $a_130 = 130;\n    $a_129 = 129;\n    $a_128 = 128;\n    $a_127 = 127;\n    $a_126 = 126;\n    $a_125 = 125;\n    $a_124 = 124;\n    $a_123 = 123;\n    $a_122 = 122;\n    $a_121 = 121;\n    $a_120 = 120;\n    $a_119 = 119;\n    $a_118 = 118;\n    $a_117 = 117;\n    $a_116 = 116;\n    $a_115 = 115;\n    $a_114 = 114;\n    $a_113 = 113;\n    $a_112 = 112;\n    $a_111 = 111;\n    $a_110 = 110;\n    $a_109 = 109;\n    $a_108 = 108;\n    $a_107 = 107;\n    $a_106 = 106;\n    $a_105 = 105;\n    $a_104 = 104;\n    $a_103 = 103;\n    $a_102 = 102;\n    $a_101 = 101;\n    $a_100 = 100;\n    $a_99 = 99;\n    $a_98 = 98;\n    $a_97 = 97;\n    $a_96 = 96;\n    $a_95 = 95;\n    $a_94 = 94;\n    $a_93 = 93;\n    $a_92 = 92;\n    $a_91 = 91;\n    $a_90 = 90;\n    $a_89 = 89;\n    $a_88 = 88;\n    $a_87 = 87;\n    $a_86 = 86;\n    $a_85 = 85;\n    $a_84 = 84;\n    $a_83 = 83;\n    $a_82 = 82;\n    $a_81 = 81;\n    $a_80 = 80;\n    $a_79 = 79;\n    $a_78 = 78;\n    $a_77 = 77;\n    $a_76 = 76;\n    $a_75 = 75;\n    $a_74 = 74;\n    $a_73 = 73;\n    $a_72 = 72;\n    $a_71 = 71;\n    $a_70 = 70;\n    $a_69 = 69;\n    $a_68 = 68;\n    $a_67 = 67;\n    $a_66 = 66;\n    $a_65 = 65;\n    $a_64 = 64;\n    $a_63 = 63;\n    $a_62 = 62;\n    $a_61 = 61;\n    $a_60 = 60;\n    $a_59 = 59;\n    $a_58 = 58;\n    $a_57 = 57;\n    $a_56 = 56;\n    $a_55 = 55;\n    $a_54 = 54;\n    $a_53 = 53;\n    $a_52 = 52;\n    $a_51 = 51;\n    $a_50 = 50;\n    $a_49 = 49;\n    $a_48 = 48;\n    $a_47 = 47;\n    $a_46 = 46;\n    $a_45 = 45;\n    $a_44 = 44;\n    $a_43 = 43;\n    $a_42 = 42;\n    $a_41 = 41;\n    $a_40 = 40;\n    $a_39 = 39;\n    $a_38 = 38;\n    $a_37 = 37;\n    $a_36 = 36;\n    $a_35 = 35;\n    $a_34 = 34;\n    $a_33 = 33;\n    $a_32 = 32;\n    $a_31 = 31;\n    $a_30 = 30;\n    $a_29 = 29;\n    $a_28 = 28;\n    $a_27 = 27;\n    $a_26 = 26;\n    $a_25 = 25;\n    $a_24 = 24;\n    $a_23 = 23;\n    $a_22 = 22;\n    $a_21 = 21;\n    $a_20 = 20;\n    $a_19 = 19;\n    $a_18 = 18;\n    $a_17 = 17;\n    $a_16 = 16;\n    $a_15 = 15;\n    $a_14 = 14;\n    $a_13 = 13;\n    $a_12 = 12;\n    $a_11 = 11;\n    $a_10 = 10;\n    $a_9 = 9;\n    $a_8 = 8;\n    $a_7 = 7;\n    $a_6 = 6;\n    $a_5 = 5;\n    $a_4 = 4;\n    $a_3 = 3;\n    $a_2 = 2;\n    $a_1 = 1;\n    $a_0 = 0;\n    \n    echo \"before\\n\";\n    co::sleep(0.5);\n    echo \"after\\n\";\n});\necho \"end\\n\";\n    "
  },
  {
    "path": "examples/coroutine/stack/2.php",
    "content": "<?php\ngo(function () {\n    echo \"before\\n\";\n    co::sleep(0.5);\n    echo \"after\\n\";\n});\necho \"end\\n\";\n    "
  },
  {
    "path": "examples/coroutine/stack.php",
    "content": "<?php\nco::set(['stack_size' => 8192*4]);\n\nfunction test($n)\n{\n    $a = 1;\n    $b = 2;\n    $c = 3;\n    $d = 4;\n    static $i;\n\n    usleep(100000);\n    echo \"index=\".($i++).\"\\n\";\n\n    return test($n + $a + $b + $c + $d);\n}\n\ngo(function () {\n    test(9);\n});\n"
  },
  {
    "path": "examples/coroutine/statvfs.php",
    "content": "<?php\ngo(function() {\n\tvar_dump(co::statvfs('/'));\n});\n"
  },
  {
    "path": "examples/coroutine/task_co.php",
    "content": "<?php\n$server = new Swoole\\Http\\Server(\"127.0.0.1\", 9502, SWOOLE_BASE);\n\n$server->set([\n    'worker_num' => 1,\n    'task_worker_num' => 2,\n]);\n\n$server->on('Task', function (Swoole\\Server $serv, $task_id, $worker_id, $data) {\n    echo \"#{$serv->worker_id}\\tonTask: worker_id={$worker_id}, task_id=$task_id\\n\";\n    if ($serv->worker_id == 1) {\n        sleep(1);\n    }\n    return $data;\n});\n\n$server->on('Finish', function (Swoole\\Server $serv, $task_id, $data) {\n    echo \"Task#$task_id finished, data_len=\".strlen($data).PHP_EOL;\n});\n\n$server->on('Request', function ($request, $response) use ($server)\n{\n    $result = $server->taskCo([\"hello world\", ['data' => 1234, 'code' => 200]], 0.5);\n    $response->end('Test End, Result: '.var_export($result, true));\n});\n\n$server->start();\n"
  },
  {
    "path": "examples/coroutine/tcp_backend_serv.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"0.0.0.0\", 9511);\n$serv->set(array(\n    'worker_num' => 1,   //工作进程数量\n    'daemonize' => true, //是否作为守护进程\n));\n$serv->on('connect', function ($serv, $fd){\n    echo \"Client:Connect.\\n\";\n});\n$serv->on('receive', function ($serv, $fd, $reactor_id, $data) {\n    $serv->send($fd, 'Swoole: '.$data);\n    $serv->close($fd);\n});\n$serv->on('close', function ($serv, $fd) {\n    echo \"Client: Close.\\n\";\n});\n$serv->start();\n"
  },
  {
    "path": "examples/coroutine/tcp_echo.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"0.0.0.0\", 9501);\n$serv->on('connect', function ($serv, $fd, $reactor_id){\n\techo \"[#\".posix_getpid().\"]\\tClient@[$fd]: Connect.\\n\";\n});\n$serv->set(array(\n    'worker_num' => 1,\n\n));\n\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n\techo \"[#\".$serv->worker_id.\"]\\tClient[$fd] receive data: $data\\n\";\n\tif ($serv->send($fd, \"{$data}\\n\") == false)\n\t{\n\t\techo \"error\\n\";\n\t}\n});\n\n$serv->on('close', function ($serv, $fd, $reactor_id) {\n\techo \"[#\".posix_getpid().\"]\\tClient@[$fd]: Close.\\n\";\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/coroutine/test.php",
    "content": "<?php\nfunction BatchExecMethodByCo($channel,$funcs)\n{\n    foreach ($funcs as $key => $func) {\n        go(function()use($channel,$func,$key){\n            $res = $func();\n            $channel->push([$key=>$res]);\n        });\n    }    \n}\nfunction test($value='')\n{\n    \\Co::sleep(1);\n    return \"test\\n\";\n}\nfunction test2($value='')\n{\n    \\Co::sleep(1);\n    return \"test2 \".rand(1,10).\"\\n\";\n}\ngo(function(){\n    $c = 2;\n    $channel = new \\Swoole\\Coroutine\\Channel(2);\n    $task = [\"test\",\"test2\",\"test\"];\n    BatchExecMethodByCo($channel,$task);\n    $list = [];\n    $num = count($task);\n    for ($i=0;$i<$num;$i++)\n    {\n        $list[$i] = $channel->pop();\n    }\n    var_dump($list);\n});\n\n"
  },
  {
    "path": "examples/coroutine/timer_test.php",
    "content": "<?\n/**\n * @Author: winterswang\n * @Date:   2016-06-26 16:34:02\n * @Last Modified by:   winterswang\n * @Last Modified time: 2016-06-26 16:41:46\n */\n\nSwoole\\Timer::after(1000, function(){\n    echo \" timer after timeout\\n\";\n});\n\nSwoole\\Timer::tick(1000, function(){\n    echo \"timer tick timeout\\n\";\n});\n?>\n"
  },
  {
    "path": "examples/coroutine/udp_client.php",
    "content": "<?php\nclass Client\n{\n    private $ip = \"127.0.0.1\";\n    const PORT = 8888;\n    private $data;\n\n    public function sendRequest()\n    {\n        $this->data = \"swoole test\";\n        $this->send();\n        $this->moreThanOneRecv();\n        return $ret;\n    }\n\n    public function send()\n    {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n        $ret = $cli->connect($this->ip, self::PORT);\n        $cli->send($this->data);\n        $ret = $cli->recv();\n        $cli->close();\n    }\n\n    public function moreThanOneRecv()\n    {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n        $ret = $cli->connect($this->ip, self::PORT);\n        $cli->send(\"sent by cli\");\n\n        $cli2 = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n        $ret = $cli2->connect($this->ip, self::PORT);\n        $cli2->send(\"sent by cli2\");\n\n        $cli3 = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n        $ret = $cli3->connect($this->ip, self::PORT);\n        $cli3->send(\"sent by cli3\");\n\n        sleep(1);\n        $ret = $cli3->recv();\n        $ret = $cli2->recv();\n        $ret = $cli->recv();\n        return;\n    }\n}\n\nclass Server\n{\n    public $server;\n\n    public function run()\n    {\n        $this->server = new Swoole\\Http\\Server(\"127.0.0.1\", 9502);\n        $this->server->set([\n            'worker_num' => 1,\n            'daemonize' => true,\n            'log_file' => '/tmp/swoole.log',\n        ]);\n        $this->server->on('Request',['Server', 'onRequest']);\n        $this->server->start();\n    }\n\n    public static function onRequest($request, $response)\n    {\n        self::staticFunc();\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n        $client = new Client();\n        $ret = $client->sendRequest();\n        $response->end($ret);\n    }\n}\n\n$server = new Server();\n$server->run();\n"
  },
  {
    "path": "examples/coroutine/udp_tcp_timeout.php",
    "content": "<?php\n/**\n * @Author: winterswang\n * @Date:   2015-06-18 16:45:09\n * @Last Modified by:   winterswang\n * @Last Modified time: 2016-09-18 17:36:18\n */\n\nclass TestHttpServer {\n\n\tpublic $http;\n\tpublic $queue;\n\tpublic $setting = array();\n\n\t/**\n\t * [__construct description]\n\t * @param array $setting [description]\n\t */\n\tpublic function __construct(){\n\n\t}\n\n\tpublic function set($setting){\n\n\t\t$this ->setting = $setting;\n\t}\n\n\t/**\n\t * [init description]\n\t * @return [type] [description]\n\t */\n\tpublic function init(){\n\n\t\tif (!isset($this ->setting['host'])) {\n\t\t\t$this ->setting['host'] = '0.0.0.0';\n\t\t}\n\t\tif (!isset($this ->setting['port'])) {\n\t\t\t$this ->setting['port'] = '9999';\n\t\t}\n\n\t\t$this ->http = new Swoole\\Http\\Server($this ->setting['host'], $this ->setting['port']);\n\t\t$this ->http ->set($this ->setting);\n\n\t\t$this ->http ->on('request', array($this, 'onRequest'));\n\t\t$this ->http ->on('close', array($this, 'onClose'));\n\t}\n\n\t/**\n\t * [onRequest description]\n\t * @param  [type] $request  [description]\n\t * @param  [type] $response [description]\n\t * @return [type]           [description]\n\t */\n\tpublic function onRequest($request, $response){\n\n\n\t\t//$udp_cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n\t\t$tcp_cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n\n\t\t// $ret = $udp_cli ->connect('10.100.65.222', 9906);\n\t\t// $ret = $udp_cli ->send('test for the coro');\n\t\t// $ret = $udp_cli ->recv(100);\n\t\t// $udp_cli->close();\n\n\t\t// if ($ret) {\n\t\t// \t//error_log(\" udp cli get rsp == \" . print_r($ret, true),3, '/data/log/udp_timeout.log');\n\t\t// }\n\t\t// else{\n\t\t// \terror_log(\" udp cli timeout \\n\",3, '/data/log/udp_timeout.log');\n\t\t// }\n\n  \t\t$ret = $tcp_cli ->connect(\"10.100.64.151\", 9805);\n\t\t$ret = $tcp_cli ->send('test for the coro');\n\t\t$ret = $tcp_cli ->recv(100);\n\t\t$tcp_cli->close();\n\n\t\tif ($ret) {\n\t\t\t//error_log(\" tcp cli get rsp == \" . print_r($ret, true) . PHP_EOL, 3, '/data/log/udp_timeout.log');\n\t\t}\n\t\telse{\n\t\t\terror_log(\" tcp cli timeout \\n\",3, '/data/log/udp_timeout.log');\n\t\t}\n\n\t\t$response ->end(\" swoole response is ok\");\n\t}\n\n\t/**\n\t * [onClose description]\n\t * @param  [type] $server  [description]\n\t * @param  [type] $fd      [description]\n\t * @param  [type] $reactor_id [description]\n\t * @return [type]          [description]\n\t */\n\tpublic function onClose($server, $fd, $reactor_id){\n\n\t\t//echo \" on close fd = $fd reactor_id = $reactor_id \\n\";\n\t}\n\n\t/**\n\t * [start description]\n\t * @return [type] [description]\n\t */\n\tpublic function start(){\n\n\t\t$this ->init();\n\t\t$this ->http ->start();\n\t}\n}\n\n$setting = array(\n\t\t'host' => '0.0.0.0',\n\t\t'port' => 10006,\n\t\t'worker_num' => 4,\n\t\t'dispatch_mode' => 3,   //固定分配请求到worker\n\t\t'reactor_num' => 4,     //亲核\n\t\t'daemonize' => 1,       //守护进程\n\t\t'backlog' => 128,\n\t\t'log_file' => '/data/log/test_http_server.log',\n);\n$th = new TestHttpServer();\n$th ->set($setting);\n$th ->start();\n"
  },
  {
    "path": "examples/coroutine/user_coroutine.php",
    "content": "<?php\nfor($i = 0; $i < 100; $i++) {\n    Swoole\\Coroutine::create(function() use ($i) {\n        $redis = new Swoole\\Coroutine\\Redis();\n        $res = $redis->connect('127.0.0.1', 6379);\n        $ret = $redis->incr('coroutine');\n        $redis->close();\n        if ($i == 50) {\n            Swoole\\Coroutine::create(function() use ($i) {\n                $redis = new Swoole\\Coroutine\\Redis();\n                $res = $redis->connect('127.0.0.1', 6379);\n                $ret = $redis->set('coroutine_i', 50);\n                $redis->close();\n            });\n        }\n    });\n}\n"
  },
  {
    "path": "examples/coroutine/util/resume001.php",
    "content": "<?php\ngo(function () {\n    $main = co::getuid();\n    echo \"start to create coro\\n\";\n    go(function () use ($main) {\n        echo \"coro 2\\n\";\n        co::sleep(0.1);\n        echo \"resume\\n\";\n        co::resume($main);\n    });\n    echo \"before suspend \\n\";\n    co::suspend();\n    echo \"after suspend \\n\";\n});\necho \"main \\n\";\n"
  },
  {
    "path": "examples/coroutine/util/resume002.php",
    "content": "<?php\ngo(function () {\n    echo \"coro 1 start\\n\";\n    co::suspend();\n    echo \"coro 1 end\\n\";\n});\necho \"main 1\\n\";\ngo(function () {\n    echo \"coro 2 start\\n\";\n    co::resume(1);\n    echo \"coro 2 end\\n\";\n});\necho \"main 2\\n\";\n"
  },
  {
    "path": "examples/coroutine/util/resume003.php",
    "content": "<?php\ngo(function () {\n    $count = 0;\n    go(function () use (&$count) {\n        echo \"task 1 start\\n\";\n        co::sleep(0.2);\n        echo \"task 1 resume count $count\\n\";\n        if (++$count === 2) {\n            co::resume(1);\n        }\n        echo \"task 1 end\\n\";\n    });\n    go(function () use (&$count) {\n        echo \"task 2 start\\n\";\n        co::sleep(0.1);\n        echo \"task 2 resume count $count\\n\";\n        if (++$count === 2) {\n            co::resume(1);\n        }\n        echo \"task 2 end\\n\";\n    });\n    co::suspend();\n});\necho \"main \\n\";\n"
  },
  {
    "path": "examples/coroutine/waitgroup.php",
    "content": "<?php\nclass WaitGroup {\n    private $count = 0;\n    private $chan;\n\n    function __construct() {\n        $this->chan = new chan;\n    }\n\n    function add() {\n        $this->count++;\n    }\n\n    function done() {\n        $this->chan->push(true);\n    }\n\n    function wait() {\n        while ($this->count--) {\n            $this->chan->pop();\n        }\n    }\n}\n\ngo(function () {\n    $wg = new WaitGroup;\n\n    for($i=0;$i<10;$i++) {\n        $wg->add();\n        go(function() use ($wg, $i) {\n            co::sleep(.3);\n            echo \"hello $i\\n\";\n            $wg->done();\n        });\n    }\n\n    $wg->wait();\n    echo \"all done\\n\";\n});\n"
  },
  {
    "path": "examples/coroutine/websocket/client.php",
    "content": "<?php\n// go(function () {\n//     $cli = new Co\\http\\Client(\"127.0.0.1\", 9501);\n//     $ret = $cli->upgrade(\"/\");\n\n//     if ($ret) {\n//         while(true) {\n//             $cli->push(\"hello\");\n//             var_dump($cli->recv());\n//             co::sleep(0.1);\n//         }\n//     }\n// });\nCo\\Run(function () {\n    $cli = new Co\\http\\Client(\"127.0.0.1\", 9501);\n    $cli->set([\n        'timeout' => 1\n    ]);\n    $ret = $cli->upgrade(\"/websocket\");\n\n    if (!$ret) {\n        echo \"ERROR\\n\";\n        return;\n    }\n\n    $cli->push(\"websocket handshake 1\\n\");\n    $cli->push(\"websocket handshake 2\\n\");\n\n    var_dump($cli->recv());\n    for ($i = 0; $i < 5; $i ++) {\n        $cli->push(\"hello @$i\");\n        var_dump($cli->recv());\n        co::sleep(0.1);\n    }\n});\n"
  },
  {
    "path": "examples/coroutine/websocket/co_server.php",
    "content": "<?php\n\nCo\\Run(function () {\n    $server = new Co\\Http\\Server(\"127.0.0.1\", 9502, false);\n    $server->handle('/websocket', function ($request, $ws) {\n        $ws->upgrade();\n        while (true) {\n            $frame = $ws->recv();\n            if ($frame === false) {\n                echo \"error : \" . swoole_last_error() . \"\\n\";\n                break;\n            } else if ($frame == '') {\n                break;\n            } else {\n                if ($frame->data == \"close\") {\n                    $ws->close();\n                    return;\n                }\n                $ws->push(\"Hello {$frame->data}!\");\n                $ws->push(\"How are you, {$frame->data}?\");\n            }\n        }\n    });\n    \n        $server->handle('/', function ($request, $response) {\n            $response->end(<<<HTML\n    <h1>Swoole WebSocket Server</h1>\n    <script>\nvar wsServer = 'ws://127.0.0.1:9502/websocket';\nvar websocket = new WebSocket(wsServer);\nwebsocket.onopen = function (evt) {\n\tconsole.log(\"Connected to WebSocket server.\");\n};\n                \nwebsocket.onclose = function (evt) {\n\tconsole.log(\"Disconnected\");\n};\n                \nwebsocket.onmessage = function (evt) {\n\tconsole.log('Retrieved data from server: ' + evt.data);\n};\n                \nwebsocket.onerror = function (evt, e) {\n\tconsole.log('Error occured: ' + evt.data);\n};\n</script>\nHTML\n                );\n    });\n    \n    $server->start();\n});"
  },
  {
    "path": "examples/coroutine/websocket/server.php",
    "content": "<?php\n$ws = new Swoole\\WebSocket\\Server(\"127.0.0.1\", 9501, SWOOLE_BASE);\n$ws->set(array(\n    'log_file' => '/dev/null'\n));\n$ws->on(\"WorkerStart\", function (\\Swoole\\Server $serv) {\n\n});\n\n$ws->on('open', function ($serv, Swoole\\Http\\Request $request) {\n    //$ip = co::gethostbyname('www.baidu.com');\n    if (1) {\n        $serv->push($request->fd, \"start\\n\");\n    }\n});\n\n$ws->on('message', function ($serv, $frame) {\n    var_dump($frame);\n    co::sleep(0.1);\n    $data = $frame->data;\n    $serv->push($frame->fd, \"hello client {$data}\\n\");\n});\n\n$ws->start();\n"
  },
  {
    "path": "examples/cpp/Makefile",
    "content": "all: co repeat test_server\n\t\nco: co.cc\n\tg++ co.cc -I../.. -I../../include -DHAVE_CONFIG_H -L../../lib -lswoole -o co -g\nrepeat: repeat.cc\n\tg++ repeat.cc -I../.. -I../../include -DHAVE_CONFIG_H -L../../lib -lswoole -o repeat -g\ntest_server: test_server.cc\n\tg++ test_server.cc -I../.. -I../../include -DHAVE_CONFIG_H -L../../lib -lswoole -o test_server -g\nclean:\n\trm -f co repeat test_server\n"
  },
  {
    "path": "examples/cpp/co.cc",
    "content": "#include \"swoole_coroutine.h\"\n#include \"swoole_coroutine_socket.h\"\n#include \"swoole_coroutine_system.h\"\n#include \"swoole_signal.h\"\n\n#include <iostream>\n#include <list>\n#include <algorithm>\n#include <vector>\n\nusing swoole::Coroutine;\nusing swoole::coroutine::Socket;\nusing swoole::coroutine::System;\nusing namespace std;\n\nlist<string> q;\nlist<Socket *> slaves;\nsize_t qs;\n\nint main(int argc, char **argv) {\n    signal(SIGPIPE, SIG_IGN);\n\n    swoole::coroutine::run([](void *arg) {\n        Coroutine::create([](void *arg) {\n            System::sleep(2.0);\n            cout << \"CO-1, sleep 2\\n\";\n        });\n\n        Coroutine::create([](void *arg) {\n            System::sleep(1);\n            cout << \"CO-2, sleep 1\\n\";\n        });\n\n        Coroutine::create([](void *arg) {\n            cout << \"CO-3, listen tcp:0.0.0.0:9001\\n\";\n            Socket s(SW_SOCK_TCP);\n            s.bind(\"0.0.0.0\", 9001);\n            s.listen();\n\n            while (1) {\n                Socket *_client = s.accept();\n                Coroutine::create(\n                    [](void *arg) {\n                        Socket *client = (Socket *) arg;\n                        while (1) {\n                            char buf[1024];\n                            auto retval = client->recv(buf, sizeof(buf));\n                            if (retval == 0) {\n                                cout << \"connection close\\n\";\n                                break;\n                            } else {\n                                if (strncasecmp(\"push\", buf, 4) == 0) {\n                                    q.push_back(string(buf + 5, retval - 5));\n                                    qs += retval - 5;\n                                    string resp(\"OK\\n\");\n                                    client->send(resp.c_str(), resp.length());\n\n                                    for (auto it = slaves.begin(); it != slaves.end();) {\n                                        auto sc = *it;\n                                        auto n = sc->send(buf, retval);\n                                        if (n <= 0) {\n                                            it = slaves.erase(it);\n                                            delete sc;\n                                        } else {\n                                            it++;\n                                        }\n                                    }\n                                } else if (strncasecmp(\"pop\", buf, 3) == 0) {\n                                    if (q.empty()) {\n                                        string resp(\"EMPTY\\n\");\n                                        client->send(resp.c_str(), resp.length());\n                                    } else {\n                                        auto data = q.front();\n                                        q.pop_front();\n                                        qs -= data.length();\n                                        client->send(data.c_str(), data.length());\n                                    }\n                                } else if (strncasecmp(\"stat\", buf, 4) == 0) {\n                                    char stat_buf[64];\n                                    int n = snprintf(stat_buf, sizeof(stat_buf), \"count=%ld,bytes=%ld\\n\", q.size(), qs);\n                                    client->send(stat_buf, n);\n                                } else {\n                                    string resp(\"ERROR\\n\");\n                                    client->send(resp.c_str(), resp.length());\n                                }\n                            }\n                        }\n                        delete client;\n                    },\n                    _client);\n            }\n        });\n\n        Coroutine::create([](void *arg) {\n            Socket s(SW_SOCK_TCP);\n            s.bind(\"0.0.0.0\", 9002);\n            s.listen();\n            while (1) {\n                Socket *_client = s.accept();\n                if (_client == nullptr) {\n                    break;\n                }\n                for (auto data : q) {\n                    _client->send(data.c_str(), data.length());\n                }\n                slaves.push_back(_client);\n            }\n        });\n    });\n\n    return 0;\n}\n"
  },
  {
    "path": "examples/cpp/repeat.cc",
    "content": "#include \"swoole_server.h\"\nusing namespace swoole;\n\nint main(int argc, char **argv) {\n    swoole_init();\n\n    enum Server::Mode factory_mode;\n    if (argc > 1) {\n        factory_mode = Server::MODE_PROCESS;\n    } else {\n        factory_mode = Server::MODE_BASE;\n    }\n\n    for (int i = 0; i < 2; i++) {\n        Server serv(factory_mode);\n\n        serv.reactor_num = 1;\n        serv.worker_num = 1;\n\n        serv.onReceive = [](Server *serv, RecvData *req) { return SW_OK; };\n\n        serv.onPacket = [](Server *serv, RecvData *req) { return SW_OK; };\n\n        serv.onWorkerStart = [](Server *serv, Worker *worker) {\n            swoole_notice(\"WorkerStart[%d]PID=%d, serv=%p,\", worker->id, getpid(), serv);\n            swoole_timer_after(\n                1000,\n                [serv](Timer *, TimerNode *tnode) {\n                    printf(\"timer=%p\\n\", tnode);\n                    if (serv->is_base_mode()) {\n                        kill(getpid(), SIGTERM);\n                    } else {\n                        kill(serv->gs->master_pid, SIGTERM);\n                    }\n                },\n                nullptr);\n        };\n\n        serv.add_port(SW_SOCK_UDP, \"0.0.0.0\", 9502);\n        serv.add_port(SW_SOCK_TCP6, \"::\", 9503);\n        serv.add_port(SW_SOCK_UDP6, \"::\", 9504);\n\n        ListenPort *port = serv.add_port(SW_SOCK_TCP, \"127.0.0.1\", 9501);\n        if (!port) {\n            swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n            exit(2);\n        }\n\n        port->open_eof_check = 0;\n        // config\n        port->backlog = 128;\n        memcpy(port->protocol.package_eof, SW_STRL(\"\\r\\n\\r\\n\"));\n\n        if (serv.create()) {\n            swoole_warning(\"create server fail[error=%d]\", swoole_get_last_error());\n            exit(1);\n        }\n\n        if (serv.start() < 0) {\n            swoole_warning(\"start server fail[error=%d]\", swoole_get_last_error());\n            exit(3);\n        }\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "examples/cpp/test_server.cc",
    "content": "/**\n * cmake .\n * make test_server\n * ./bin/test_server\n */\n#include \"swoole_server.h\"\n#include \"swoole_util.h\"\n\nusing namespace swoole;\n\nint my_onPacket(Server *serv, RecvData *req);\nint my_onReceive(Server *serv, RecvData *req);\nvoid my_onStart(Server *serv);\nvoid my_onShutdown(Server *serv);\nvoid my_onConnect(Server *serv, DataHead *info);\nvoid my_onClose(Server *serv, DataHead *info);\nvoid my_onWorkerStart(Server *serv, Worker *worker);\nvoid my_onWorkerStop(Server *serv, Worker *worker);\n\nstatic int g_receive_count = 0;\n\nint main(int argc, char **argv) {\n    swoole_init();\n\n    sw_logger()->set_date_format(\"%F %T\");\n    sw_logger()->set_date_with_microseconds(true);\n\n    Server serv(Server::MODE_BASE);\n\n    serv.reactor_num = 4;\n    serv.worker_num = 1;\n\n    serv.set_max_connection(10000);\n    // serv.open_cpu_affinity = 1;\n    // serv.open_tcp_nodelay = 1;\n    // serv.daemonize = 1;\n    // memcpy(serv.log_file, SW_STRS(\"/tmp/swoole.log\"));\n\n    serv.dispatch_mode = 2;\n    // serv.open_tcp_keepalive = 1;\n\n#ifdef HAVE_OPENSSL\n    // serv.ssl_cert_file = \"tests/ssl/ssl.crt\";\n    // serv.ssl_key_file = \"tests/ssl/ssl.key\";\n    // serv.open_ssl = 1;\n#endif\n\n    serv.onStart = my_onStart;\n    serv.onShutdown = my_onShutdown;\n    serv.onConnect = my_onConnect;\n    serv.onReceive = my_onReceive;\n    serv.onPacket = my_onPacket;\n    serv.onClose = my_onClose;\n    serv.onWorkerStart = my_onWorkerStart;\n    serv.onWorkerStop = my_onWorkerStop;\n\n    // swSignal_set(SIGINT, user_signal);\n\n    serv.add_port(SW_SOCK_UDP, \"0.0.0.0\", 9502);\n    serv.add_port(SW_SOCK_TCP6, \"::\", 9503);\n    serv.add_port(SW_SOCK_UDP6, \"::\", 9504);\n\n    ListenPort *port = serv.add_port(SW_SOCK_TCP, \"127.0.0.1\", 9501);\n    if (!port) {\n        swoole_warning(\"listen failed, [error=%d]\", swoole_get_last_error());\n        exit(2);\n    }\n\n    port->open_eof_check = 0;\n    // config\n    port->backlog = 128;\n    memcpy(port->protocol.package_eof, SW_STRL(\"\\r\\n\\r\\n\"));\n\n    if (serv.create()) {\n        swoole_warning(\"create server fail[error=%d]\", swoole_get_last_error());\n        exit(1);\n    }\n\n    if (serv.start() < 0) {\n        swoole_warning(\"start server fail[error=%d]\", swoole_get_last_error());\n        exit(3);\n    }\n    return 0;\n}\n\nvoid my_onWorkerStart(Server *serv, Worker *worker) {\n    swoole_notice(\"WorkerStart[%d]PID=%d\", worker->id, worker->pid);\n}\n\nvoid my_onWorkerStop(Server *serv, Worker *worker) {\n    swoole_notice(\"WorkerStop[%d]PID=%d\", worker->id, worker->pid);\n}\n\nint my_onReceive(Server *serv, RecvData *req) {\n    char req_data[SW_IPC_BUFFER_SIZE];\n    char resp_data[SW_IPC_BUFFER_SIZE];\n\n    g_receive_count++;\n\n    Connection *conn = serv->get_connection_by_session_id(req->info.fd);\n\n    memcpy(req_data, req->data, req->info.len);\n    swoole::rtrim(req_data, req->info.len);\n    swoole_notice(\"onReceive[%d]: ip=%s|port=%d Data=%s|Len=%d\",\n                  g_receive_count,\n                  conn->info.get_addr(),\n                  conn->info.get_port(),\n                  req_data,\n                  req->info.len);\n\n    int n = sw_snprintf(resp_data, SW_IPC_BUFFER_SIZE, \"Server: %.*s\\n\", req->info.len, req_data);\n\n    if (!serv->send(req->info.fd, resp_data, n)) {\n        swoole_notice(\"send to client fail. errno=%d\", errno);\n    } else {\n        swoole_notice(\"send %d bytes to client success. data=%s\", n, resp_data);\n    }\n    return SW_OK;\n}\n\nint my_onPacket(Server *serv, RecvData *req) {\n    char address[256];\n    int port = 0;\n    int ret = 0;\n\n    DgramPacket *packet = (DgramPacket *) req->data;\n\n    auto serv_socket = serv->get_server_socket(req->info.server_fd);\n\n    if (packet->socket_type == SW_SOCK_UDP) {\n        inet_ntop(AF_INET, &packet->socket_addr.addr.inet_v4.sin_addr, address, sizeof(address));\n        port = ntohs(packet->socket_addr.addr.inet_v4.sin_port);\n    } else if (packet->socket_type == SW_SOCK_UDP6) {\n        inet_ntop(AF_INET6, &packet->socket_addr.addr.inet_v6.sin6_addr, address, sizeof(address));\n        port = ntohs(packet->socket_addr.addr.inet_v6.sin6_port);\n    } else if (packet->socket_type == SW_SOCK_UNIX_DGRAM) {\n        strcpy(address, packet->socket_addr.addr.un.sun_path);\n    } else {\n        abort();\n    }\n\n    char *data = packet->data;\n    uint32_t length = packet->length;\n\n    swoole_notice(\"Packet[client=%s:%d, %d bytes]: data=%.*s\", address, port, length, length, data);\n\n    char resp_data[SW_IPC_BUFFER_SIZE];\n    int n = sw_snprintf(resp_data, SW_IPC_BUFFER_SIZE, \"Server: %.*s\", length, data);\n\n    ret = serv_socket->sendto(address, port, resp_data, n);\n\n    if (ret < 0) {\n        swoole_notice(\"send to client fail. errno=%d\", errno);\n    } else {\n        swoole_notice(\"send %d bytes to client success. data=%s\", n, resp_data);\n    }\n\n    return SW_OK;\n}\n\nvoid my_onStart(Server *serv) {\n    swoole_notice(\"Server is running\");\n}\n\nvoid my_onShutdown(Server *serv) {\n    swoole_notice(\"Server is shutdown\");\n}\n\nvoid my_onConnect(Server *serv, DataHead *info) {\n    swoole_notice(\"PID=%d\\tConnect fd=%ld|reactor_id=%d\", getpid(), info->fd, info->reactor_id);\n}\n\nvoid my_onClose(Server *serv, DataHead *info) {\n    swoole_notice(\"PID=%d\\tClose fd=%ld|reactor_id=%d\", getpid(), info->fd, info->reactor_id);\n}\n"
  },
  {
    "path": "examples/curl/hook.php",
    "content": "<?php\nCo::set(['hook_flags' => SWOOLE_HOOK_ALL | SWOOLE_HOOK_NATIVE_CURL, ]);\n//Co::set(['hook_flags' => SWOOLE_HOOK_ALL, ]);\n\nCo\\run(function () {\n    $n = 3;\n    while($n--) {\n        go('test');\n    }\n});\n\nfunction test() {\n    echo \"curl init\\n\";\n    $ch = curl_init();\n//    $url = 'https://www.baidu.com/';\n    $url = \"http://127.0.0.1:9801/\";\n\n    curl_setopt($ch, CURLOPT_URL, $url);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HEADER, 0);\n    curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) {\n        //var_dump($ch, $strHeader);\n        return strlen($strHeader);\n    });\n\n    $output = curl_exec($ch);\n    var_dump($output);\n    var_dump(strlen($output));\n    if ($output === false) {\n        echo \"CURL Error:\" . curl_error($ch);\n    }\n//    var_dump($output);\n    curl_close($ch);\n}\n"
  },
  {
    "path": "examples/curl/multi.php",
    "content": "<?php\n\nfunction test()\n{\n    // 创建一对cURL资源\n    $ch1 = curl_init();\n    $ch2 = curl_init();\n\n    // 设置URL和相应的选项\n    curl_setopt($ch1, CURLOPT_URL, \"http://www.baidu.com/\");\n    curl_setopt($ch1, CURLOPT_HEADER, 0);\n    curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1);\n\n    curl_setopt($ch2, CURLOPT_URL, \"http://www.gov.cn/\");\n    curl_setopt($ch2, CURLOPT_HEADER, 0);\n    curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1);\n\n    // 创建批处理cURL句柄\n    $mh = curl_multi_init();\n\n    // 增加2个句柄\n    curl_multi_add_handle($mh, $ch1);\n    curl_multi_add_handle($mh, $ch2);\n\n    echo \"add \\n\";\n\n    $active = null;\n    // 执行批处理句柄\n    do {\n        $mrc = curl_multi_exec($mh, $active);\n        echo \"exec[1], retval=$mrc\\n\";\n    } while ($mrc == CURLM_CALL_MULTI_PERFORM);\n\n    while ($active && $mrc == CURLM_OK) {\n        $n = curl_multi_select($mh);\n        echo \"select, retval=$n\\n\";\n        if ($n != -1) {\n            do {\n                $mrc = curl_multi_exec($mh, $active);\n                echo \"exec[2], retval=$mrc, active=$active\\n\";\n            } while ($mrc == CURLM_CALL_MULTI_PERFORM);\n        }\n    }\n\n\n    var_dump(strlen(curl_multi_getcontent($ch1)));\n    var_dump(strlen(curl_multi_getcontent($ch2)));\n\n    // 关闭全部句柄\n    curl_multi_remove_handle($mh, $ch1);\n    curl_multi_remove_handle($mh, $ch2);\n    curl_multi_close($mh);\n}\n\nif (empty($argv[1])) {\n    Co\\run(function () {\n        test();\n    });\n} else {\n    test();\n}\n\n"
  },
  {
    "path": "examples/curl/server.php",
    "content": "<?php\n$http = new Swoole\\Http\\Server(\"127.0.0.1\", 9801);\n\n$http->set(['worker_num' => 8, ]);\n\n$http->on(\"start\", function ($server) {\n    echo \"Swoole http server is started at http://127.0.0.1:9501\\n\";\n});\n\n$http->on(\"request\", function ($request, $response) {\n    sleep(1);\n    $response->end(\"Hello World\\n\");\n});\n\n$http->start();\n"
  },
  {
    "path": "examples/dtls/client.php",
    "content": "<?php\n\nCo\\run(\n    function () {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP | SWOOLE_SSL);\n        echo \"connect\\n\";\n        $client->connect(\"127.0.0.1\", 9905);\n        echo \"connect OK\\n\";\n        $client->send(\"hello world\");\n        echo $client->recv();\n        $client->close();\n        echo \"END\\n\";\n    }\n);"
  },
  {
    "path": "examples/dtls/server.php",
    "content": "<?php\n$server = new Swoole\\Server('0.0.0.0', 9905, SWOOLE_BASE, SWOOLE_SOCK_UDP | SWOOLE_SSL);\n\n$server->set(\n    [\n        'ssl_cert_file' => __DIR__ . '/../ssl/ssl.crt',\n        'ssl_key_file' => __DIR__ . '/../ssl/ssl.key',\n        //'ssl_method' => SWOOLE_TLSv1_2_SERVER_METHOD,\n        'worker_num' => 1,\n        //'ssl_client_cert_file' => __DIR__ . '/ca.crt',\n        //'ssl_verify_depth' => 10,\n    ]\n);\n\n$server->on('Receive', function (Swoole\\Server $serv, $fd, $tid, $data)\n{\n    var_dump($fd, $data, $serv->getClientInfo($fd));\n    $serv->send($fd, \"Swoole: $data\\n\");\n    //echo \"close dtls session\\n\";\n    //$serv->close($fd);\n});\n\n$server->start();\n"
  },
  {
    "path": "examples/eof/client.php",
    "content": "<?php\n/**\n * 分段发送数据\n *\n * @param Swoole\\Client $client\n * @param string $data\n * @param int $chunk_size\n */\nfunction send_chunk(Swoole\\Client $client, $data, $chunk_size = 1024)\n{\n    $len = strlen($data);\n    $chunk_num = intval($len / $chunk_size) + 1;\n    for ($i = 0; $i < $chunk_num; $i++) {\n        if ($len < ($i + 1) * $chunk_size) {\n            $sendn = $len - ($i * $chunk_size);\n        } else {\n            $sendn = $chunk_size;\n        }\n        $client->send(substr($data, $i * $chunk_size, $sendn));\n    }\n}\n\n$client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞\nif (!$client->connect('127.0.0.1', 9501, 0.5, 0)) {\n    echo \"Over flow. errno=\" . $client->errCode;\n    die(\"\\n\");\n}\n\n//for ($i = 0; $i < 10; $i++)\n//{\n//    $client->send(\"hello world\\r\\n\\r\\n\");\n//    echo \"send\\n\";\n//}\n//exit;\n\n$data = array(\n    'name' => __FILE__,\n    'content' => str_repeat('A', 8192 * rand(1, 3)),  //800K\n);\n\n$_serialize_data = serialize($data);\n\n$_send = $_serialize_data . \"__doit__\";\n\necho \"serialize_data length=\" . strlen($_serialize_data) . \"send length=\" . strlen($_send) . \"\\n\";\n//send_chunk($client, $_send);\n\n//\nif (!$client->send($_send)) {\n    die(\"send failed.\\n\");\n}\n\n//$client->send(\"\\r\\n\".substr($_serialize_data, 0, 8000));\n\necho $client->recv();\nexit;\n\n$client->send(substr($_serialize_data, 8000));\n\n//usleep(500000);\n\nif (!$client->send(\"\\r\\n\\r\\n\")) {\n    die(\"send failed.\\n\");\n}\n\necho $client->recv();\n\n//sleep(1);\n"
  },
  {
    "path": "examples/eof/server.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"127.0.0.1\", 9501, SWOOLE_BASE);\n$serv->set(array(\n    'package_eof' => \"\\r\\n\\r\\n\",\n    'open_eof_check' => true,\n    'open_eof_split' => true,\n//    'worker_num' => 4,\n    'dispatch_mode' => 3,\n    'package_max_length' => 1024 * 1024 * 2, //2M\n));\n//$serv->on('connect', function ($serv, $fd) {\n//    //echo \"[#\" . posix_getpid() . \"]\\tClient:Connect.\\n\";\n//});\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n    echo '#' . $serv->worker_id . \" recv: \" . strlen($data) . \"\\n\";\n    for ($i = 0; $i < 1000; $i++) {\n        $resp = str_repeat('A', rand(10000, 50000)) . \"\\r\\n\\r\\n\";\n        $serv->send($fd, $resp);\n        if ($i % 100 == 1) {\n            sleep(1);\n            echo \"send \" . strlen($resp) . \" bytes\\n\";\n        }\n    }\n});\n//$serv->on('close', function ($serv, $fd) {\n//echo \"[#\" . posix_getpid() . \"]\\tClient: Close.\\n\";\n//});\n$serv->start();\n"
  },
  {
    "path": "examples/event/cycle.php",
    "content": "<?php\n\nSwoole\\Timer::tick(2000, function ($id) {\n\tvar_dump($id);\n});\n\nSwoole\\Event::cycle(function () {\n\techo \"hello [1]\\n\";\n    Swoole\\Event::cycle(function () {\n\t    echo \"hello [2]\\n\";\n        Swoole\\Event::cycle(null);\n    });\n});\n\nSwoole\\Event::wait();\n"
  },
  {
    "path": "examples/event/inotify.php",
    "content": "<?php\n//创建一个inotify句柄\n$fd = inotify_init();\n\n//监听文件，仅监听修改操作，如果想要监听所有事件可以使用IN_ALL_EVENTS\n$watch_descriptor = inotify_add_watch($fd, __DIR__.'/inotify.data', IN_MODIFY);\n\nSwoole\\Event::add($fd, function ($fd) {\n    $events = inotify_read($fd);\n    if ($events) {\n        foreach ($events as $event) {\n            echo \"inotify Event :\" . var_export($event, 1) . \"\\n\";\n        }\n    }\n});\n\nSwoole\\Event::wait();\n"
  },
  {
    "path": "examples/event/sockets.php",
    "content": "<?php\n/**\n * require ./configure --enable-sockets\n */\n\nuse Swoole\\Event;\n\n$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die(\"Unable to create socket\\n\");\n\nsocket_set_nonblock($socket) or die(\"Unable to set nonblock on socket\\n\");\n\nfunction socket_onRead($socket)\n{\n    static $i = 0;\n\n    echo socket_read($socket, 8192) . \"\\n\";\n    $i++;\n    if ($i > 10) {\n        echo \"finish\\n\";\n        Event::del($socket);\n        socket_close($socket);\n    } else {\n        sleep(1);\n        Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE);\n    }\n}\n\nfunction socket_onWrite($socket)\n{\n    socket_write($socket, \"hi swoole\");\n    Event::set($socket, null, null, SWOOLE_EVENT_READ);\n}\n\nfunction socket_onConnect($socket)\n{\n    $err = socket_get_option($socket, SOL_SOCKET, SO_ERROR);\n    if ($err == 0) {\n        echo \"connect server success\\n\";\n        Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ);\n        socket_write($socket, \"first package\\n\");\n    } else {\n        echo \"connect server failed\\n\";\n        Event::del($socket);\n        socket_close($socket);\n    }\n}\n\nEvent::add($socket, 'socket_onRead', 'socket_onConnect', SWOOLE_EVENT_WRITE);\nsocket_connect($socket, '127.0.0.1', 9501);\nEvent::wait();\n"
  },
  {
    "path": "examples/event/stdin.php",
    "content": "<?php\nSwoole\\Event::add(STDIN, function($fp) {\n\techo \"STDIN: \".fread($fp, 8192);\n});\nSwoole\\Event::wait();\n"
  },
  {
    "path": "examples/event/stream.php",
    "content": "<?php\n$fp = stream_socket_client(\"tcp://127.0.0.1:9502\", $errno, $errstr, 30);\nif (!$fp) {\n    exit(\"$errstr ($errno)<br />\\n\");\n}\nfwrite($fp, \"HELLO world\");\n\nfunction stream_onRead($fp)\n{\n\techo fread($fp, 1024).\"\\n\";\n\tsleep(1);\n\tSwoole\\Event::write($fp, \"hello world\");\n\t//Swoole\\Event::set($fp, null, null, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE);\n\t//Swoole\\Event::del($fp);\n    //fclose($fp);\n}\n\n\nSwoole\\Event::add($fp, 'stream_onRead');\n\necho \"start\\n\";\nSwoole\\Event::wait();\n"
  },
  {
    "path": "examples/event/test.php",
    "content": "<?php\n$fp = stream_socket_client(\"tcp://127.0.0.1:9501\", $errno, $errstr, 30);\nfwrite($fp, \"HELLO world\");\n\nSwoole\\Event::add($fp, function ($fp) {\n    echo fread($fp, 1024).\"\\n\";\n    Swoole\\Event::del($fp);\n    fclose($fp);\n});\nSwoole\\Event::wait();\n"
  },
  {
    "path": "examples/http/UPPER.TXT",
    "content": "HELLO WORLD!\n"
  },
  {
    "path": "examples/http/client.php",
    "content": "<?php\n$cli = new Swoole\\Client(SWOOLE_SOCK_TCP);\n$cli->connect('127.0.0.1', 9501);\n\n//$type = 'GET';\n$type = 'POST';\n\n$cookie = \"8MLP_5753_saltkey=RSU8HYED; 8MLP_5753_lastvisit=1426120671; pgv_pvi=1454765056; CNZZDATA1000008050=684878078-1426123263-http%253A%252F%252Fcznews-team.chinaz.com%252F%7C1426485386; attentiondomain=2z.cn%2cchinaz.com%2ckuaishang.cn%2ccxpcms.com; CNZZDATA33217=cnzz_eid%3D1036784254-1426122273-http%253A%252F%252Fcznews-team.chinaz.com%252F%26ntime%3D1427414208; CNZZDATA433095=cnzz_eid%3D1613871160-1426123273-http%253A%252F%252Fcznews-team.chinaz.com%252F%26ntime%3D1427848205; CNZZDATA1254679775=309722566-1427851758-http%253A%252F%252Fcznews-team.chinaz.com%252F%7C1427851758; 8MLP_5753_security_cookiereport=c014Hgufskpv55xgM9UaB%2FZZdMrcN0QqBYdcGomTu8OlTDWzTA0z; 8MLP_5753_ulastactivity=e4a1aRIbgdzoRDd8NlT5CMIwLnWjyjr2hWyfn6T5g82RitUOdf3o; 8MLP_5753_auth=9351LJpv7Xa%2FPUylJDQgRiAONZ5HysOaj%2BqRGb6jYmpqZpRkVc2ibPXm7LAfArC%2FpIpY2Fx%2B59AHqzr843qozZWxWNZi; mytool_user=uSHVgCUFWf5Sv2Y8tKytQRUJW3wMVT3rw5xQLNGQFIsod4C6vYWeGA==; 8MLP_5753_lip=220.160.111.22%2C1428036585; pgv_si=s4245709824; PHPSESSID=t3hp9h4o8rb3956t5pajnsfab1; 8MLP_5753_st_p=1024432%7C1428040399%7Cf7599ba9053aa27e12e9e597a4c372ce; 8MLP_5753_viewid=tid_7701248; 8MLP_5753_smile=5D1; 8MLP_5753_st_t=1024432%7C1428040402%7C46d40e02d899b10b431822eb1d39f6a1; 8MLP_5753_forum_lastvisit=D_140_1427103032D_165_1427427405D_168_1427870172D_167_1427870173D_166_1428021390D_163_1428040402; 8MLP_5753_sid=k25gxK; 8MLP_5753_lastact=1428040403%09misc.php%09patch; cmstop_page-view-mode=view; cmstop_rememberusername=error; cmstop_auth=Jcn2qzVn9nsjqtodER9OphcW3PURDWNx6mO7j0Zbb9k%3D; cmstop_userid=6; cmstop_username=error; Hm_lvt_aecc9715b0f5d5f7f34fba48a3c511d6=1427967317,1428021376,1428036617,1428040224; Hm_lpvt_aecc9715b0f5d5f7f34fba48a3c511d6=1428050417; YjVmNm_timeout=0\";\n\nif ($type == 'GET')\n{\n    $header = \"GET /home/explore/ HTTP/1.1\\r\\n\";\n    $header .= \"Host: 127.0.0.1\\r\\n\";\n    $header .= \"Connection: keep-alive\\r\\n\";\n    $header .= \"Cache-Control: max-age=0\\r\\n\";\n    $header .= \"Cookie: $cookie\\r\\n\";\n    $header .= \"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\\r\\n\";\n    $header .= \"User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36\\r\\n\";\n    $header .= \"\\r\\n\";\n    $_sendStr = $header;\n}\nelse\n{\n//    $header = \"POST /home/explore/?hello=123&world=swoole#hello HTTP/1.1\\r\\n\";\n    $header = \"POST /post.php HTTP/1.1\\r\\n\";\n    $header .= \"Host: 127.0.0.1\\r\\n\";\n    $header .= \"Referer: http://group.swoole.com/\\r\\n\";\n    $header .= \"Content-Type: application/x-www-form-urlencoded\\r\\n\";\n    $header .= \"Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4,ja;q=0.2\\r\\n\";\n    $header .= \"Cookie: pgv_pvi=9559734272; efr__Session=uddfvbm87dtdtrdsro1ohlt4o6; efr_r_uname=apolov%40vip.qq.com; efr__user_login=3N_b4tHW1uXGztWW2Ojf09vssOjR5abS4abO5uWRopnm0eXb7OfT1NbIoqjWzNCvodihq9qaptqfra6imtLXpNTNpduVoque26mniKej5dvM09WMopmmpM2xxcmhveHi3uTN0aegpaiQj8Snoa2IweHP5fCL77CmxqKqmZKp5ejN1c_Q2cPZ25uro6mWqK6BmMOzy8W8k4zi2d3Nlb_G0-PaoJizz97l3deXqKyPoKacr6ynlZ2nppK71t7C4uGarKunlZ-s; pgv_si=s8426935296; Hm_lvt_4967f2faa888a2e52742bebe7fcb5f7d=1410240641,1410241802,1410243730,1410243743; Hm_lpvt_4967f2faa888a2e52742bebe7fcb5f7d=1410248408\\r\\n\";\n    $header .= \"RA-Ver: 2.5.3\\r\\n\";\n    $header .= \"RA-Sid: 2A784AF7-20140212-113827-085a9c-c4de6e\\r\\n\";\n\n    $_postData = ['body1' => 'swoole_http-server', 'message' => 'nihao'];\n    $_postBody = json_encode($_postData);\n//    $_postBody = http_build_query($_postData);\n    $header .=  \"Content-Length: \" . strlen($_postBody);\n    echo \"http header length=\".strlen($header).\"\\n\";\n    $header .=  \"Content-Length: \" . (strlen($_postBody) - 2);\n\n//    $cli->send($header);\n//    usleep(100000);\n    $_sendStr = $header . \"\\r\\n\\r\\n\" . $_postBody;\n//    $_sendStr = \"\\r\\n\\r\\n\" . $_postBody;\n    echo \"postBody length=\".strlen($_postBody).\"\\n\";\n}\n\necho \"-------------------------Request----------------------------\\n\";\necho $_sendStr;\n$cli->send($_sendStr);\necho \"send \".strlen($_sendStr).\" byte\\n\";\n\necho \"-------------------------Response----------------------------\\n\";\n$data = $cli->recv();\nvar_dump($data);\nexit;\n"
  },
  {
    "path": "examples/http/curl.php",
    "content": "<?php\n// 创建一个新cURL资源\n$ch = curl_init();\n// 设置URL和相应的选项\ncurl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:9501\");\ncurl_setopt($ch, CURLOPT_HEADER, 0);\ncurl_setopt($ch, CURLOPT_POST, 1);//设置为POST方式\ncurl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));\n\n$post_data = array('test' => str_repeat('a', 80));\n\nif (function_exists(\"curl_file_create\"))\n{\n    $cfile = curl_file_create(__DIR__ . '/../test.jpg');\n    $post_data['file'] = $cfile;\n}\nelse\n{\n    $post_data['file'] = '@' . __DIR__ . '/../test.jpg';\n}\n\ncurl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);  //POST数据\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n// 抓取URL并把它传递给浏览器\n$res =  curl_exec($ch);\nvar_dump($res);\n// 关闭cURL资源，并且释放系统资源\ncurl_close($ch);\n"
  },
  {
    "path": "examples/http/detach.php",
    "content": "<?php\n$http = new Swoole\\Http\\Server(\"0.0.0.0\", 9501);\n\n$http->set(['task_worker_num' => 1, 'worker_num' => 1]);\n\n$http->on('request', function ($req, Swoole\\Http\\Response $resp) use ($http) {\n    $resp->detach();\n    $http->task(strval($resp->fd));\n});\n\n$http->on('finish', function ()\n{\n    echo \"task finish\";\n});\n\n$http->on('task', function ($serv, $task_id, $worker_id, $data)\n{\n    var_dump($data);\n    $resp = Swoole\\Http\\Response::create($data);\n    $resp->end(\"in task\");\n    echo \"async task\\n\";\n});\n\n//$http->on('close', function(){\n//    echo \"on close\\n\";\n//});\n\n\n$http->on('workerStart', function ($serv, $id)\n{\n    //var_dump($serv);\n});\n\n$http->start();\n"
  },
  {
    "path": "examples/http/download.php",
    "content": "<?php\n$fp = fopen (__DIR__. '/test.html', 'w+');\n$ch = curl_init(\"http://127.0.0.1/index.php\");\ncurl_setopt($ch, CURLOPT_TIMEOUT, 50);\ncurl_setopt($ch, CURLOPT_ENCODING, \"gzip\");\n// write curl response to file\ncurl_setopt($ch, CURLOPT_FILE, $fp);\ncurl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);\n// get curl response\ncurl_exec($ch);\ncurl_close($ch);\nfclose($fp);\n"
  },
  {
    "path": "examples/http/empty.txt",
    "content": ""
  },
  {
    "path": "examples/http/event-stream.php",
    "content": "<?php\n$http = new Swoole\\Http\\Server(\"0.0.0.0\", 9501);\n\n$http->on('request', function ($req, Swoole\\Http\\Response $resp) use ($http) {\n    if ($req->server['request_uri'] == '/stream') {\n        $resp->header(\"Content-Type\", \"text/event-stream\");\n        $resp->header(\"Cache-Control\", \"no-cache\");\n        $resp->header(\"Connection\", \"keep-alive\");\n        $resp->header(\"X-Accel-Buffering\", \"no\");\n        $resp->header('Content-Encoding', '');\n        $resp->header(\"Content-Length\", '');\n        $resp->end();\n        go(function () use ($resp, $http) {\n            while (true) {\n                Co::sleep(1);\n                $http->send($resp->fd, 'data: ' . base64_encode(random_bytes(random_int(16, 128))). \"\\n\\n\");\n            }\n        });\n    } elseif ($req->server['request_uri'] == '/') {\n        $resp->end(<<<HTML\n<html>\n<script>\nconst source = new EventSource(\"/stream\");\nsource.onmessage = function(e){\n    console.log(e);\n};\nsource.onerror = function(e){\n    console.log(e);\n};\n</script>\n</html>\nHTML\n        );\n    } else {\n        $resp->status(404);\n        $resp->end();\n    }\n});\n\n$http->start();\n"
  },
  {
    "path": "examples/http/moc.moc",
    "content": "this is moc.moc\n"
  },
  {
    "path": "examples/http/no-compression.php",
    "content": "<?php\n\n$http = new Swoole\\Http\\Server(\"0.0.0.0\", 9501);\n\n$http->on('request', function ($req, Swoole\\Http\\Response $resp) use ($http) {\n    if ($req->server['request_uri'] == '/') {\n        $resp->header('Content-Encoding', '');\n        $resp->end(str_repeat('A', 1024));\n    } elseif ($req->server['request_uri'] == '/gzip') {\n        $resp->end(str_repeat('A', 1024));\n    } else {\n        $resp->status(404);\n        $resp->end();\n    }\n});\n\n$http->start();\n"
  },
  {
    "path": "examples/http/post.data",
    "content": "dd=wwwwww&ddd=eee&3412=3234&hello=world%2C+i+am+rango%2C+thank+you.+no+body\n"
  },
  {
    "path": "examples/http/raw.php",
    "content": "<?php\n$sock = stream_socket_client(\"tcp://127.0.0.1:9501\");\nfwrite($sock, file_get_contents('httpdata'));\nstream_set_chunk_size($sock, 2*1024*1024);\n$data = fread($sock, 8192 * 128);\nif ($argv[1] == 'save')\n{\n    file_put_contents(\"resp.html\", $data);\n}\nelse\n{\n    echo $data;\n}\n"
  },
  {
    "path": "examples/http/redirect.php",
    "content": "<?php\n$http = new Swoole\\Http\\Server(\"0.0.0.0\", 9501, SWOOLE_BASE);\n\n$http->on('request', function ($req, Swoole\\Http\\Response $resp) {\n    $resp->redirect(\"http://www.baidu.com/\", 301);\n});\n\n$http->start();\n"
  },
  {
    "path": "examples/http/server.php",
    "content": "<?php\nfunction dump($var)\n{\n    return highlight_string(\"<?php\\n\\$array = \" . var_export($var, true) . \";\", true);\n}\n\n$key_dir = dirname(dirname(__DIR__)) . '/tests/ssl';\n$http = new Swoole\\Http\\Server(\"0.0.0.0\", 9501, SWOOLE_BASE);\n//$http = new Swoole\\Http\\Server(\"::\", 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP6);\n//$http = new Swoole\\Http\\Server(\"0.0.0.0\", 9501);\n//$http = new Swoole\\Http\\Server(\"0.0.0.0\", 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n//https\n//$http = new Swoole\\Http\\Server(\"0.0.0.0\", 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n//$http->setGlobal(HTTP_GLOBAL_ALL, HTTP_GLOBAL_GET|HTTP_GLOBAL_POST|HTTP_GLOBAL_COOKIE);\n$http->set([\n//    'daemonize' => 1,\n//    'open_cpu_affinity' => 1,\n//    'task_worker_num' => 1,\n    //'open_cpu_affinity' => 1,\n    //'task_worker_num' => 100,\n    //'enable_port_reuse' => true,\n    // 'http_compression' => false,\n    'worker_num' => 1,\n    'upload_max_filesize' => 1 * 1024 * 1024 * 1024,\n    'package_max_length' => 1 * 1024 * 1024,\n    //'log_file' => __DIR__.'/swoole.log',\n//    'reactor_num' => 24,\n    //'dispatch_mode' => 3,\n    //'discard_timeout_request' => true,\n//    'open_tcp_nodelay' => true,\n//    'open_mqtt_protocol' => true,\n    //'task_worker_num' => 1,\n    //'user' => 'www-data',\n    //'group' => 'www-data',\n//'daemonize' => true,\n//    'ssl_cert_file' => $key_dir.'/ssl.crt',\n//    'ssl_key_file' => $key_dir.'/ssl.key',\n//    'enable_static_handler' => true,\n//    'document_root' => '/home/htf/workspace/php/www.swoole.com/web/'\n]);\n\n$http->listen('127.0.0.1', 9502, SWOOLE_SOCK_TCP);\n\nfunction chunk(Swoole\\Http\\Request $request, Swoole\\Http\\Response $response)\n{\n    $response->write(\"<h1>hello world1</h1>\");\n    //sleep(1);\n    $response->write(\"<h1>hello world2</h1>\");\n    //sleep(1);\n    $response->end();\n}\n\nfunction no_chunk(Swoole\\Http\\Request $request, Swoole\\Http\\Response $response)\n{\n    /**\n     * Cookie Test\n     */\n    //$response->cookie('test1', '1234', time() + 86400, '/');\n//    $response->cookie('test2', '5678', time() + 86400);\n//    var_dump($response->cookie);\n//    var_dump($request->cookie);\n//\ttry\n//\t{\n//\t\tif (rand(1, 99) % 2 == 1)\n//\t\t{\n//\t\t\tthrow new Exception(\"just for fun.\");\n//\t\t}\n//\t\t$response->end(\"<h1>Hello Swoole. #\".rand(1000, 9999).\"</h1>\");\n//\t}\n//\tcatch(Exception $e)\n//\t{\n//\t\t$response->end(\"<h1>Exceptiom</h1><div>\".$e->getMessage().\"</div>\");\n//\t}\n    //var_dump($request->server['request_uri'], substr($request->server['request_uri'], -4, 4));\n\n    if (substr($request->server['request_uri'], -8, 8) == 'test.jpg') {\n        $response->header('Content-Type', 'image/jpeg');\n        $response->sendfile(dirname(__DIR__) . '/test.jpg');\n        return;\n    } elseif ($request->server['request_uri'] == '/test.txt') {\n        $last_modified_time = filemtime(__DIR__ . '/test.txt');\n        $etag = md5_file(__DIR__ . '/test.txt');\n        // always send headers\n        $response->header(\"Last-Modified\", gmdate(\"D, d M Y H:i:s\", $last_modified_time) . \" GMT\");\n        $response->header(\"Etag\", $etag);\n        if (strtotime($request->header['if-modified-since']) == $last_modified_time or trim($request->header['if-none-match']) == $etag) {\n            $response->status(304);\n            $response->end();\n        } else {\n            $response->sendfile(__DIR__ . '/test.txt');\n        }\n        return;\n    } else if ($request->server['request_uri'] == '/favicon.ico') {\n        $response->status(404);\n        $response->end();\n        return;\n    } else if ($request->server['request_uri'] == '/big_response') {\n        var_dump($response->end(str_repeat('A', 16 * 1024 * 1024)));\n        return;\n    } else if ($request->server['request_uri'] == '/code') {\n        $response->sendfile(__FILE__);\n        return;\n    } elseif ($request->server['request_uri'] == '/save') {\n        file_put_contents(__DIR__ . '/httpdata', $request->getData());\n        $response->end('hello');\n        return;\n    } else {\n        //var_dump($request->post);\n        //var_export($request->cookie);\n//    var_dump($request->rawContent());\n//    if ($request->server['request_method'] == 'POST')\n//    {\n//        var_dump($request->post);\n//    }\n//    echo \"GET:\" . var_export($_GET, true).\"\\n\";\n//    echo \"POST:\" . var_export($_POST, true).\"\\n\";\n//    echo \"get:\" . var_export($request->get, true).\"\\n\";\n//    echo \"post:\" . var_export($request->post, true).\"\\n\";\n        //var_dump($request->server);\n        $output = '';\n        $output .= \"<h2>HEADER:</h2>\" . dump($request->header);\n        $output .= \"<h2>SERVER:</h2>\" . dump($request->server);\n        if (!empty($request->files)) {\n            $files = $request->files;\n            foreach ($files as &$f) {\n                $f['md5'] = md5_file($f['tmp_name']);\n            }\n            $output .= \"<h2>FILE:</h2>\" . dump($files);\n        }\n        if (!empty($request->cookie)) {\n            $output .= \"<h2>COOKIES:</h2>\" . dump($request->cookie);\n        }\n        if (!empty($request->get)) {\n            $output .= \"<h2>GET:</h2>\" . dump($request->get);\n        }\n        if (!empty($request->post)) {\n            $output .= \"<h2>POST:</h2>\" . dump($request->post);\n        }\n        var_dump($request->post);\n        //$response->header('X-Server', 'Swoole');\n        //unset($request, $response);\n//    Swoole\\Timer::after(2000, function() use ( $response) {\n        $response->end(\"<h1>Hello Swoole.</h1>\" . $output);\n        return;\n//    });\n    }\n    //var_dump($request);\n//    var_dump($_GET);\n    //var_dump($_POST);\n    //var_dump($_COOKIE);\n    //$response->status(301);\n    //$response->header(\"Location\", \"http://www.baidu.com/\");\n    //$response->cookie(\"hello\", \"world\", time() + 3600);\n//    $response->header(\"Content-Type\", \"text/html; charset=utf-8\");\n\n    //var_dump($request->post);\n//    var_dump($request->get);\n\n//    echo strlen(gzdeflate(\"<h1>Hello Swoole.</h1>\"));\n//    $response->end(\"<h1>Hello Swoole.</h1>\");\n    //$response->end(\"<h1>Hello Swoole. #\".str_repeat('A', rand(100, 999)).\"</h1>\");\n    //global $http;\n    //$http->task(\"hello world\");\n    $file = realpath(__DIR__ . '/../' . $request->server['request_uri']);\n    if (is_file($file)) {\n        echo \"http get file=$file\\n\";\n        if (substr($file, -4) == '.php') {\n            $response->gzip();\n        } else {\n            $response->header('Content-Type', 'image/jpeg');\n        }\n        $content = file_get_contents($file);\n        echo \"response size = \" . strlen($content) . \"\\n\";\n\n//        $response->write($content);\n//        $response->end();\n\n        $response->end($content);\n    } else {\n        $response->end(\"<h1>Hello Swoole.</h1>\");\n    }\n}\n\n$http->on('request', function ($req, $resp) {\n    $uri = $req->server['request_uri'];\n    if ($uri == '/favicon.ico') {\n        $resp->status(404);\n        $resp->end();\n    } elseif ($uri == '/chunk') {\n        chunk($req, $resp);\n    } else {\n        no_chunk($req, $resp);\n    }\n});\n\n$http->on('finish', function () {\n    echo \"task finish\";\n});\n\n$http->on('task', function () {\n    echo \"async task\\n\";\n});\n\n//$http->on('close', function(){\n//    echo \"on close\\n\";\n//});\n\n\n$http->on('workerStart', function ($serv, $id) {\n    //var_dump($serv);\n});\n\n$http->start();\n"
  },
  {
    "path": "examples/http/static_handler.php",
    "content": "<?php\n$http = new Swoole\\Http\\Server(\"0.0.0.0\", 9501, SWOOLE_BASE);\n//$http = new Swoole\\Http\\Server(\"0.0.0.0\", 9501);\n$http->set([\n    'enable_static_handler' => true,\n    'http_autoindex' => true,\n    'document_root' => realpath(__DIR__.'/../www/'),\n]);\n\n$http->on('request', function ($req, $resp) {\n    $resp->end(\"hello world\\n\");\n});\n\n$http->start();\n"
  },
  {
    "path": "examples/http/test.txt",
    "content": "hello world!\n"
  },
  {
    "path": "examples/http/url_rewrite.php",
    "content": "<?php\n/**\n * URL重写功能测试\n */\n\n$http = new Swoole\\Http\\Server(\"0.0.0.0\", 9501, SWOOLE_BASE);\n\n$http->set([\n    'enable_static_handler' => true,\n    'document_root' => realpath(__DIR__.'/../www/'),\n    'http_autoindex' => true,\n    // URL重写规则配置\n    'url_rewrite_rules' => [\n        // 普通路径重写: 将 /api 开头的请求重写到 /static/api 目录\n        '/api' => '/static/api',\n        \n        // 正则表达式重写: 将 /user/123 格式的请求重写到 /static/user.html?id=123\n        '~^/user/(\\\\d+)$~' => '/static/user.html?id=$1',\n        \n        // 正则表达式重写: 将 /article/title-slug 格式的请求重写到 /static/article.html?slug=title-slug\n        '~^/article/([\\\\w\\\\-]+)$~' => '/static/article.html?slug=$1'\n    ]\n]);\n\n$http->on('request', function ($request, $response) {\n    $response->header('Content-Type', 'text/plain; charset=utf-8');\n    $response->end(\"动态处理: \" . $request->server['request_uri']);\n});\n\n$http->on('start', function ($server) {\n    echo \"HTTP服务器已启动，监听 0.0.0.0:9501\\n\";\n    echo \"URL重写功能已启用\\n\";\n    echo \"测试示例:\\n\";\n    echo \"1. 普通重写: http://localhost:9501/api/test.txt -> /static/api/test.txt\\n\";\n    echo \"2. 正则重写: http://localhost:9501/user/123 -> /static/user.html?id=123\\n\";\n    echo \"3. 正则重写: http://localhost:9501/article/test-title -> /static/article.html?slug=test-title\\n\";\n});\n\n$http->start();"
  },
  {
    "path": "examples/http2/server.php",
    "content": "<?php\nSwoole\\Coroutine::set([\n    'trace_flags' => SWOOLE_TRACE_HTTP2,\n    'log_level' => 0,\n]);\n$key_dir = __DIR__ . '/../ssl/';\n$http = new Swoole\\Http\\Server(\"0.0.0.0\", 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n$http->set([\n    'open_http2_protocol' => 1,\n    'enable_static_handler' => TRUE,\n    'document_root' => dirname(__DIR__),\n    'package_max_length' => 1024 * 1024,\n    'ssl_cert_file' => $key_dir . '/ssl.crt',\n    'ssl_key_file' => $key_dir . '/ssl.key',\n]);\n\n$http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n\t$response->header('Test-Value', [\n        \"a\\r\\n\",\n        'd5678',\n        \"e  \\n \",\n        null,\n        5678,\n        3.1415926,\n    ]);\n    $response->end(\"<h1>Hello Swoole.</h1>\");\n});\n\n$http->start();\n"
  },
  {
    "path": "examples/http2/streaming.php",
    "content": "<?php\n$http = new Swoole\\Http\\Server(\"0.0.0.0\", 9501);\n\n$http->set([\n    'open_http2_protocol' => 1,\n]);\n\n/**\n * nghttp -v http://localhost:9501\n */\n$http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n    $n = 5;\n    while ($n--) {\n        $response->write(\"hello world, #$n <br />\\n\");\n        Co\\System::sleep(1);\n    }\n    $response->end(\"hello world\");\n});\n\n$http->start();\n\n"
  },
  {
    "path": "examples/http2/test.html",
    "content": "<h1>Test</h1>\n"
  },
  {
    "path": "examples/ipv6/tcp_client.php",
    "content": "<?php\n$client = new Swoole\\Client(SWOOLE_SOCK_TCP6);\nif (!$client->connect('::1', 9501, -1))\n{\n\texit(\"connect failed. Error: {$client->errCode}\\n\");\n}\n\nvar_dump($client->getsockname());\n\nfor($i=0; $i < 1; $i ++)\n{\n    $client->send(\"hello world\\n\");\n    echo $client->recv();\n    usleep(2000);\n}\n\n$client->close();\n"
  },
  {
    "path": "examples/ipv6/tcp_server.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"::1\", 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP6);\n$serv->set(array(\n    'worker_num' => 1,\n));\n$serv->on('connect', function ($serv, $fd, $reactor_id){\n    echo \"[#\".posix_getpid().\"]\\tClient@[$fd:$reactor_id]: Connect.\\n\";\n});\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n    echo \"[#\".posix_getpid().\"]\\tClient[$fd]: $data\\n\";\n    var_dump($serv->connection_info($fd));\n\t$serv->send($fd, json_encode(array(\"hello\" => '1213', \"bat\" => \"ab\")));\n    //$serv->close($fd);\n});\n$serv->on('close', function ($serv, $fd, $reactor_id) {\n    echo \"[#\".posix_getpid().\"]\\tClient@[$fd:$reactor_id]: Close.\\n\";\n});\n$serv->start();\n"
  },
  {
    "path": "examples/ipv6/udp_client.php",
    "content": "<?php\n$client = new Swoole\\Client(SWOOLE_SOCK_UDP6);\n$client->connect('::1', 9502);\n$client->send(\"admin\");\necho $client->recv().\"\\n\";\nvar_dump($client->getsockname());\nvar_dump($client->getpeername());\n$client->sendto('::1', 9502, \"admin2\");\necho $client->recv().\"\\n\";\nsleep(1);\n"
  },
  {
    "path": "examples/ipv6/udp_server.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"::1\", 9502, SWOOLE_PROCESS, SWOOLE_SOCK_UDP6);\n$serv->set(array(\n    'worker_num' => 1,\n));\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n    echo \"[#\".posix_getpid().\"]\\tClient[$fd]: $data\\n\";\n    var_dump($serv->connection_info($fd, $reactor_id));\n\t$serv->send($fd, json_encode(array(\"hello\" => '1213', \"bat\" => \"ab\")));\n    //$serv->close($fd);\n});\n$serv->start();\n"
  },
  {
    "path": "examples/length/client.php",
    "content": "<?php\n$client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n\n$client->set(array(\n    'open_length_check'     => true,\n    'package_length_type'   => 'N',\n    'package_length_offset' => 0,       //第N个字节是包长度的值\n    'package_body_offset'   => 4,       //第几个字节开始计算长度\n    'package_max_length'    => 2000000,  //协议最大长度\n));\n\nif (!$client->connect('127.0.0.1', 9501))\n{\n    exit(\"connect failed\\n\");\n}\n\nfor ($i = 0; $i < 10; $i++)\n{\n    $data = array(\n        'str1' => str_repeat('A', rand(10000, 20000)),\n        'str2' => str_repeat('B', rand(5000, 10000)),\n        'str3' => str_repeat('C', rand(1000, 9000)),\n    );\n\n    $data['int1'] = rand(100000, 999999);\n    $sendStr = serialize($data);\n    $sendData = pack('N', strlen($sendStr)) . $sendStr;\n    $client->send($sendData);\n    echo \"send length=\" . strlen($sendData) . \", SerId={$data['int1']}\\n\";\n\n    for ($j = 0; $j < 3; $j++)\n    {\n        $resp = $client->recv();\n        $data2 = unserialize(substr($resp, 4));\n        echo \"#$j\\trecv length=\" . strlen($resp) . \", SerId={$data2['int1']}\\n\";\n    }\n}\nsleep(2);\n"
  },
  {
    "path": "examples/length/config.php",
    "content": "<?php\n$loop1 = 1;\n$loop2 = 3;\n"
  },
  {
    "path": "examples/length/func.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"127.0.0.1\", 9501);\n\n$serv->set(array(\n    'open_length_check' => true,\n    'dispatch_mode' => 1,\n    'package_length_func' => function ($data) {\n        if (strlen($data) < 8) {\n            return 0;\n        }\n        $length = intval(trim(substr($data, 0, 8)));\n        if ($length <= 0) {\n            return -1;\n        }\n        return $length + 8;\n    },\n    'package_max_length' => 2000000,  //协议最大长度\n));\n\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data)\n{\n    var_dump($data);\n    echo \"#{$serv->worker_id}>> received length=\" . strlen($data) . \"\\n\";\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/length/server.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"127.0.0.1\", 9501, SWOOLE_BASE);\n\n$serv->set(array(\n\t\t'open_length_check'     => true,\n\t\t'dispatch_mode'         => 1,\n//\t\t'worker_num'            => 4,\n\t\t'package_length_type'   => 'N',\n\t\t'package_length_offset' => 0,       //第N个字节是包长度的值\n\t\t'package_body_offset'   => 4,       //第几个字节开始计算长度\n\t\t'package_max_length'    => 2000000,  //协议最大长度\n));\n\nfunction send(Swoole\\Server $serv, $fd, $data)\n{\n    $serv->send($fd, $data);\n    echo \"#send =\" . strlen($data) . \" bytes\\n\";\n}\n\n$serv->on('connect', function ($serv, $fd){\n\techo \"Client:Connect.\\n\";\n});\n\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n    $req = unserialize(substr($data, 4));\n    echo \"#{$serv->worker_id}>> received length=\" . strlen($data) . \", SerId: {$req['int1']}\\n\";\n    send($serv, $fd, $data);\n    send($serv, $fd, $data);\n    send($serv, $fd, $data);\n});\n\n$serv->on('close', function ($serv, $fd) {\n\techo \"Client: Close.\\n\";\n});\n$serv->start();\n"
  },
  {
    "path": "examples/lock/lock.php",
    "content": "<?php\n/**\n * SWOOLE_MUTEX 互斥锁\n * SWOOLE_FILELOCK 文件锁，需要在第二个参数传入一个锁文件\n * SWOOLE_SPINLOCK 自旋锁(请查看swoole扩展信息，检测是否支持)\n * SWOOLE_SEM 信号量\n * SWOOLE_RWLOCK 读写锁\n */\n\n$lock = new Swoole\\Lock(SWOOLE_MUTEX);\necho \"[Master]create lock\\n\";\n$lock->lock();\nif (pcntl_fork() > 0)\n{\n    sleep(1);\n    $lock->unlock();\n}\nelse\n{\n    echo \"[Child] Wait Lock\\n\";\n    $lock->lock();\n    echo \"[Child] Get Lock\\n\";\n    $lock->unlock();\n    exit(\"[Child] exit\\n\");\n}\necho \"[Master]release lock\\n\";\nunset($lock);\nsleep(1);\necho \"[Master]exit\\n\";\n"
  },
  {
    "path": "examples/misc/get_local_ip.php",
    "content": "<?php\nvar_dump(swoole_get_local_ip(AF_INET));\nvar_dump(swoole_get_local_ip(AF_INET6));\n"
  },
  {
    "path": "examples/misc/get_local_mac.php",
    "content": "<?php\nvar_dump(swoole_get_local_mac());\n"
  },
  {
    "path": "examples/misc/version.php",
    "content": "<?php\necho SWOOLE_VERSION,\"\\n\";\necho swoole_version();\n?>\n"
  },
  {
    "path": "examples/multicast/client.php",
    "content": "<?php\n$client = new Swoole\\Client(SWOOLE_SOCK_UDP, SWOOLE_SOCK_SYNC);\n$client->connect('224.10.20.30', 9905);\n$client->send(\"hello world\");\necho $client->recv() . \"\\n\";\nsleep(1);\n"
  },
  {
    "path": "examples/multicast/server.php",
    "content": "<?php\n$server = new Swoole\\Server('0.0.0.0', 9905, SWOOLE_PROCESS, SWOOLE_SOCK_UDP);\n$server->set(['worker_num' => 1]);\n$socket = $server->getSocket();\n\n$ret = socket_set_option(\n    $socket,\n    IPPROTO_IP,\n    MCAST_JOIN_GROUP,\n    array('group' => '224.10.20.30', 'interface' => 0)\n);\n\nif ($ret === false) {\n    throw new RuntimeException('Unable to join multicast group');\n}\n\n$server->on('Packet', function (Swoole\\Server $serv, $data, $addr) {\n    $serv->sendto($addr['address'], $addr['port'], \"Swoole: $data\");\n    var_dump($addr, strlen($data));\n});\n\n$server->start();\n"
  },
  {
    "path": "examples/php/buf_size.php",
    "content": "<?php\n$fds = [];\n\nsocket_create_pair(AF_UNIX, SOCK_DGRAM, 0, $fds);\n\n$socket = $fds[0];\n\nsocket_set_option($socket, SOL_SOCKET, SO_SNDBUF, 8 * 1024 * 1024);\n$send_buf_size = socket_get_option($socket, SOL_SOCKET, SO_SNDBUF);\necho \"send_buf_size=$send_buf_size\\n\";\n\n\nsocket_set_option($socket, SOL_SOCKET, SO_RCVBUF, 8 * 1024);\n$recv_buf_size = socket_get_option($socket, SOL_SOCKET, SO_RCVBUF);\necho \"recv_buf_size=$recv_buf_size\\n\";\n\n$n = $send_buf_size - 32;\n$ret_n = socket_write($socket, str_repeat('A', $n), $n);\nvar_dump($ret_n);\n\n$data = socket_read($fds[1], $n);\nvar_dump(strlen($data));\n"
  },
  {
    "path": "examples/php/debug_server.php",
    "content": "<?php\n\nclass DebugServer\n{\n    protected $alloc_point = array();\n    protected $free_point = array();\n    protected $index = 0;\n\n    function package_decode($pkg)\n    {\n        list($tag, $_vars) = explode(':', $pkg, 2);\n        $tag = trim($tag);\n        $vars = explode(',', trim($_vars));\n        $data = array();\n        foreach($vars as $str)\n        {\n            list($k, $v) = explode('=', trim($str));\n            $data[$k] = $v;\n        }\n\n        if ($tag == 'alloc')\n        {\n            file_put_contents(__DIR__.'/alloc.log', $data['ptr'].\"\\n\", FILE_APPEND);\n        }\n        elseif($tag =='memory')\n        {\n            var_dump($tag, $data);\n        }\n        elseif ($tag == 'free')\n        {\n            file_put_contents(__DIR__.'/free.log', $data['ptr'].\"\\n\", FILE_APPEND);\n        }\n        elseif($tag == 'invalid')\n        {\n            foreach($this->alloc_point as $k => $v)\n            {\n                echo \"$k => $v\\n\";\n            }\n        }\n        else\n        {\n            //var_dump($tag, $data);\n        }\n    }\n\n    function run()\n    {\n        unlink(__DIR__.'/alloc.log');\n        unlink(__DIR__.'/free.log');\n        $socket = stream_socket_server(\"udp://127.0.0.1:9999\", $errno, $errstr, STREAM_SERVER_BIND);\n        if (!$socket)\n        {\n            die(\"$errstr ($errno)\");\n        }\n        while(1)\n        {\n            $pkt = stream_socket_recvfrom($socket, 65535, 0, $peer);\n            $this->package_decode($pkt);\n            //echo \"$peer: $pkt\\n\";\n            //stream_socket_sendto($socket, date(\"D M j H:i:s Y\\r\\n\"), 0, $peer);\n        }\n    }\n}\n\n$svr = new DebugServer;\n$svr->run();\n"
  },
  {
    "path": "examples/php/error.php",
    "content": "<?php\n\n2->test();\n"
  },
  {
    "path": "examples/php/exception.php",
    "content": "<?php\r\nregister_shutdown_function(function () {\r\n    throw new Exception(\"shutdown 1\");\r\n});\r\n\r\nregister_shutdown_function(function () {\r\n    throw new Exception(\"shutdown 2\");\r\n});\r\n\r\nthrow new Exception(\"main\");"
  },
  {
    "path": "examples/php/func.php",
    "content": "<?php\ndeclare(ticks=10);\nfunction test()\n{\n    echo \"start\\n\";\n    $i = 0;\n    while ($i < 10000) {\n        $i++;\n    }\n    echo \"end\\n\";\n}"
  },
  {
    "path": "examples/php/inotify.php",
    "content": "<?php\n// Open an inotify instance\n$fd = inotify_init();\n\n// Watch __FILE__ for metadata changes (e.g. mtime)\n$watch_descriptor = inotify_add_watch($fd, __DIR__ . '/php', IN_MODIFY | IN_MOVED_FROM | IN_CREATE | IN_DELETE | IN_ISDIR);\n\nwhile (true)\n{\n    // Read events\n    $events = inotify_read($fd);\n    print_r($events);\n}\n"
  },
  {
    "path": "examples/php/proc.php",
    "content": "<?php\nSwoole\\Runtime::enableCoroutine(SWOOLE_HOOK_ALL);\n\ngo(function () {\n    $descriptorspec = array(\n        0 => array(\"pipe\", \"r\"),  // 标准输入，子进程从此管道中读取数据\n        1 => array(\"pipe\", \"w\"),  // 标准输出，子进程向此管道中写入数据\n        2 => array(\"file\", __DIR__ . \"/error-output.txt\", \"a\") // 标准错误，写入到一个文件\n    );\n\n    $cwd = '/tmp';\n    $env = array('some_option' => 'aeiou');\n    $process = proc_open('php', $descriptorspec, $pipes, $cwd, $env);\n\n//    var_dump($process, $pipes);exit;\n\n// $pipes 现在看起来是这样的：\n// 0 => 可以向子进程标准输入写入的句柄\n// 1 => 可以从子进程标准输出读取的句柄\n// 错误输出将被追加到文件 /tmp/error-output.txt\n\n    fwrite($pipes[0], '<?php sleep(1);print_r($_ENV); ?>');\n    fclose($pipes[0]);\n\n//    echo stream_get_contents($pipes[1]);\n//    fclose($pipes[1]);\n\n// 切记：在调用 proc_close 之前关闭所有的管道以避免死锁。\n    $return_value = proc_close($process);\n\n    echo \"command returned $return_value\\n\";\n});\n"
  },
  {
    "path": "examples/php/socket_client.php",
    "content": "<?php\npcntl_signal(SIGIO, function () {\n    echo \"SIGIO\";\n});\n\n\n$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\nsocket_connect($socket, '127.0.0.1', 8889);\n\n$timeout = array('sec'=>1, 'usec' => 500000);\nsocket_set_option($socket,SOL_SOCKET,SO_RCVTIMEO,$timeout);\n\n$n = socket_recv($socket, $buf, 2048, MSG_WAITALL);\n\nvar_dump($n, $buf);\n"
  },
  {
    "path": "examples/php/socket_server.php",
    "content": "<?php\nerror_reporting(E_ALL);\n$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\nif ( $socket === false ) {\n    echo \"socket_create() failed:reason:\" . socket_strerror( socket_last_error() ) . \"\\n\";\n}\n$ok = socket_bind( $socket,'127.0.0.1',11109);\nif ( $ok === false ) {\n    echo \"socket_bind() failed:reason:\" . socket_strerror( socket_last_error( $socket ) );\n}\n\n$ok = socket_listen($socket, 128);\nif ( $ok === false ) {\n    echo \"socket_bind() failed:reason:\" . socket_strerror( socket_last_error( $socket ) );\n}\n\nwhile ( true ) {\n\tsleep(1000);\n    $conn = socket_accept($socket);\n    if($conn) {\n\t\tif(socket_recv($conn, $data, 8192, null))\n\t\t{\n\t\t\techo $data,\"\\n\";\n\t\t\tsocket_send($conn, \"hello world\\n\", 11, null);\n\t\t\tsocket_close($conn);\n\t\t}\n\t} else {\n\t\techo \"error\\n\";\n\t}\n}\n"
  },
  {
    "path": "examples/php/stream_client.php",
    "content": "<?php\n$fp = stream_socket_client(\"tcp://172.16.51.114:8000\", $errno, $errstr, 30);\nif (!$fp) {\n    echo \"$errstr ($errno)<br />\\n\";\n} else {\n    fwrite($fp, \"GET / HTTP/1.0\\r\\nHost: www.example.com\\r\\nAccept: */*\\r\\n\\r\\n\");\n    sleep(1000);\n    fclose($fp);\n}\n"
  },
  {
    "path": "examples/php/stream_server.php",
    "content": "<?php\n\n$socket = stream_socket_server(\"tcp://0.0.0.0:8000\", $errno, $errstr);\nif (!$socket) {\n  echo \"$errstr ($errno)<br />\\n\";\n} else {\n  while ($conn = stream_socket_accept($socket)) {\n\t  $i = 0;\n\t  while(true) {\n\t\t    $r = fwrite($conn, str_repeat(\"A\", 8192));\n\t\t    usleep(1000);\n\t\t    if (empty($r)) {\n\t\t\t\techo \"count $i \\n\";\n\t\t\t\tvar_dump($r);\n\t\t\t\tstream_set_blocking($conn, 0);\n\t\t\t}\n\t\t    else{\n\t\t\t\t$i++;\n\t\t\t}\n\t\t\tif ($r === false) break;\n\t  }\n\t  fclose($conn);\n\n  }\n  fclose($socket);\n}\n"
  },
  {
    "path": "examples/php/tick.php",
    "content": "<?php\ndeclare(ticks=10);\n\ninclude __DIR__ . '/func.php';\n\nregister_tick_function(function () {\n    echo \"i\\n\";\n    sleep(1);\n});\ntest();"
  },
  {
    "path": "examples/postgresql/postgresql_coro.php",
    "content": "\n<?php\n\ngo(function () {\n\n    $pg = new Swoole\\Coroutine\\PostgreSql();\n    $conn  = $pg -> connect (\"host=127.0.0.1 port=5432 dbname=test user=wuzhenyu password=\");\n    $result = $pg->query($conn, 'SELECT * FROM test;');\n    $arr = $pg->fetchAll($result);// the same with affectedRows(),fetchObject(),fetchAssoc(),fetchArray(),fetchRow(),numRows()\n    var_dump($arr);\n\n});\n\ngo(function () {\n\n    $pg = new Swoole\\Coroutine\\PostgreSql();\n    $conn  = $pg -> connect (\"host=127.0.0.1 port=5432 dbname=test user=wuzhenyu password=\");\n    $metaData = $pg->metaData($conn, 'test');\n    var_dump($metaData);\n\n});\n?>\n"
  },
  {
    "path": "examples/process/alarm.php",
    "content": "<?php\nSwoole\\Process::signal(SIGALRM, function ()\n{\n    static $i = 0;\n    echo \"#{$i}\\talarm\\n\";\n    $i++;\n    if ($i > 20)\n    {\n        Swoole\\Process::alarm(-1);\n    }\n});\n\nSwoole\\Process::alarm(100 * 1000);\n"
  },
  {
    "path": "examples/process/async_master.php",
    "content": "<?php\n$workers = [];\n$worker_num = 10;\n\n//Swoole\\Process::daemon(0, 1);\n\nfunction onReceive($pipe) {\n    global $workers;\n    $worker = $workers[$pipe];\n    $data = $worker->read();\n    echo \"RECV: \" . $data;\n}\n\n//循环创建进程\nfor($i = 0; $i < $worker_num; $i++)\n{\n    $process = new Swoole\\Process(function(Swoole\\Process $process) {\n        $i = 1;\n        while($i++)\n        {\n            $process->write(\"Worker#{$process->id}: hello master\\n\");\n            if ($i > 5 and $process->id == 1) $process->exit();\n            sleep(1);\n        }\n    });\n    $process->id = $i;\n    $pid = $process->start();\n    $workers[$process->pipe] = $process;\n}\n\nSwoole\\Process::signal(SIGCHLD, function(){\n    //表示子进程已关闭，回收它\n    $status = Swoole\\Process::wait();\n    echo \"Worker#{$status['pid']} exit\\n\";\n});\n\n//将子进程的管道加入EventLoop\nforeach($workers as $process)\n{\n    Swoole\\Event::add($process->pipe, 'onReceive');\n}\n"
  },
  {
    "path": "examples/process/client.php",
    "content": "<?php\n$client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n\nif (!$client->connect('127.0.0.1', 8089, -1))\n{\n    exit(\"connect failed. Error: {$client->errCode}\\n\");\n}\n\nfunction _send(Swoole\\Client $client, $data)\n{\n    return $client->send(pack('N', strlen($data)) . $data);\n}\n\nvar_dump($client->getsockname());\n\n\n_send($client, \"hello world\");\n_send($client, \"hello world [2]\");\n\n$client->close();\n"
  },
  {
    "path": "examples/process/client3.php",
    "content": "<?php\n$fp = stream_socket_client(\"tcp://127.0.0.1:8089\", $errno, $errstr) or die(\"error: $errstr\\n\");\n$msg = json_encode(['data' => 'hello', 'uid' => 1991]);\nfwrite($fp, pack('N', strlen($msg)) . $msg);\nsleep(1);\nvar_dump(fread($fp, 8192));\nfclose($fp);\n"
  },
  {
    "path": "examples/process/close.php",
    "content": "<?php\n$process = new Swoole\\Process(function (Swoole\\Process $worker)\n{\n    echo \"Worker: start. PID=\" . $worker->pid . \"\\n\";\n    sleep(2);\n    $worker->close(Swoole\\Process::PIPE_READ);\n    $worker->write(\"hello master\\n\");\n    $worker->exit(0);\n}, false);\n\n$pid = $process->start();\n$r = array($process);\n$w = array();\n$e = array();\n$ret = swoole_select($r, $w, $e, 1.0);\nvar_dump($ret);\nvar_dump($process->read());\n"
  },
  {
    "path": "examples/process/daemon.php",
    "content": "<?php\n$fp = fopen(__DIR__.'/output.txt', 'a');\nSwoole\\Process::daemon(1, 1, [null, $fp, $fp]);\n\nsleep(1);\n\nfwrite(STDOUT, \"ERROR 1\\n\");\nfwrite(STDOUT, \"ERROR 2\\n\");\nfwrite(STDOUT, \"ERROR 3\\n\");\n\nfwrite(STDERR, \"ERROR 4\\n\");\nfwrite(STDERR, \"ERROR 5\\n\");\nfwrite(STDERR, \"ERROR 6\\n\");"
  },
  {
    "path": "examples/process/echo.py",
    "content": "import sys\n\ndef main():\n\ts = raw_input()\n\tprint \"Python:\" + s\n\nmain()\n"
  },
  {
    "path": "examples/process/exec.php",
    "content": "<?php\n$process = new Swoole\\Process('callback_function', true);\n$pid = $process->start();\n\nfunction callback_function(Swoole\\Process $worker)\n{\n    $worker->exec('/usr/local/bin/php', array(__DIR__.'/stdin_stdout.php'));\n}\n\necho \"From Worker: \".$process->read();\n$process->write(\"hello worker\\n\");\necho \"From Worker: \".$process->read();\n\n$ret = Swoole\\Process::wait();\nvar_dump($ret);\n"
  },
  {
    "path": "examples/process/func_timeout.php",
    "content": "<?php\ndeclare(ticks = 1);\nSwoole\\Async::set([\n    'enable_signalfd' => false,\n]);\n\nclass FunctionTimeoutException extends RuntimeException\n{\n\n}\n\nfunction test()\n{\n    sleep(1);\n}\n\n$serv = new Swoole\\Http\\Server(\"127.0.0.1\", 9502);\n\n$serv->set(['worker_num' => 1]);\n\n$serv->on('WorkerStart', function($serv, $workerId) {\n    pcntl_signal(SIGALRM, function () {\n        Swoole\\Process::alarm(-1);\n        throw new FunctionTimeoutException;\n    });\n});\n\n$serv->on('Request', function($request, $response) {\n    try\n    {\n        Swoole\\Process::alarm(100 * 1000);\n        test();\n        Swoole\\Process::alarm(-1);\n        $response->end(\"<h1>Finish</h1>\");\n    }\n    catch(FunctionTimeoutException $e)\n    {\n        $response->end(\"<h1>Timeout</h1>\");\n    }\n\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/process/msg_pop.php",
    "content": "<?php\nuse Swoole\\Process\\Pool;\n\n$pool = new Pool(1, SWOOLE_IPC_MSGQUEUE, 0x9501);\n$pool->on('Message', function (Pool $pool, string $data) {\n    var_dump($data);\n});\n$pool->start();\n"
  },
  {
    "path": "examples/process/msg_push.php",
    "content": "<?php\n$queue = new Swoole\\MsgQueue(0x9501);\n$msg = str_repeat('A', 8192);\n$ret = $queue->push($msg);\nvar_dump($ret);\n"
  },
  {
    "path": "examples/process/msgqueue.php",
    "content": "<?php\n$workers = [];\n$worker_num = 2;\n\nfor($i = 0; $i < $worker_num; $i++)\n{\n    $process = new Swoole\\Process('callback_function', false, false);\n    $process->useQueue();\n    $pid = $process->start();\n    $workers[$pid] = $process;\n    //echo \"Master: new worker, PID=\".$pid.\"\\n\";\n}\n\nfunction callback_function(Swoole\\Process $worker)\n{\n    //echo \"Worker: start. PID=\".$worker->pid.\"\\n\";\n    //recv data from master\n    while(true)\n    {\n        $recv = $worker->pop();\n        echo \"From Master: $recv\\n\";\n    }\n\n    sleep(2);\n    $worker->exit(0);\n}\n\nwhile(true)\n{\n    /**\n     * @var $process Swoole\\Process\n     */\n    $pid = array_rand($workers);\n    $process = $workers[$pid];\n    $process->push(\"hello worker[$pid]\\n\");\n    sleep(1);\n}\n\nfor($i = 0; $i < $worker_num; $i++)\n{\n    $ret = Swoole\\Process::wait();\n    $pid = $ret['pid'];\n    unset($workers[$pid]);\n    echo \"Worker Exit, PID=\".$pid.PHP_EOL;\n}\n"
  },
  {
    "path": "examples/process/msgqueue2.php",
    "content": "<?php\nfunction callback_function(Swoole\\Process $worker)\n{\n    //echo \"Worker: start. PID=\".$worker->pid.\"\\n\";\n    //recv data from master\n    while(true)\n    {\n        $recv = $worker->pop();\n        echo \"From Master: $recv\\n\";\n    }\n\n    sleep(2);\n    $worker->exit(0);\n}\n\n$process = new Swoole\\Process('callback_function', false, false);\n$process->useQueue(ftok(__FILE__, 1), 2 | Swoole\\Process::IPC_NOWAIT);\n\n$send_bytes = 0;\nforeach(range(1, 10) as $i)\n{\n    $data = str_repeat('A', 65535);\n//    $data = \"hello worker[$i]\\n\";\n    $send_bytes += strlen($data);\n    $process->push($data);\n}\n\n$recv_bytes = 0;\n$r_data = true;\nwhile($r_data)\n{\n    $r_data = $process->pop();\n    $recv_bytes += $r_data;\n}\necho \"send={$send_bytes}, recv=$recv_bytes\\n\";\nvar_dump($process->statQueue());\n"
  },
  {
    "path": "examples/process/msgqueue_client.php",
    "content": "<?php\n$mq = new Swoole\\MsgQueue(0x7000001);\nfor ($i = 0; $i < 1000; $i++)\n{\n    if ($i % 100 == 99)\n    {\n        var_dump($mq->stats());\n    }\n    elseif ($i % 300 == 299)\n    {\n        sleep(1);\n    }\n    $mq->push(\"hello $i\");\n}\n"
  },
  {
    "path": "examples/process/msgqueue_pool.php",
    "content": "<?php\n$pool = new Swoole\\Process\\Pool(2, SWOOLE_IPC_MSGQUEUE, 0x7000001);\n\n$pool->on(\"Message\", function ($pool, $message) {\n    echo \"Message: {$message}\\n\";\n});\n\n$pool->on(\"WorkerStart\", function ($pool, $workerId) {\n    echo \"Worker#{$workerId} is started\\n\";\n});\n\n$pool->on(\"WorkerStop\", function ($pool, $workerId) {\n    echo \"Worker#{$workerId} is stopped\\n\";\n});\n\n$pool->start();\n"
  },
  {
    "path": "examples/process/pool_socket.php",
    "content": "<?php\n$pool = new Swoole\\Process\\Pool(2, SWOOLE_IPC_SOCKET);\n\n$pool->on(\"Message\", function ($pool, $message) {\n    echo \"Message: {$message}\\n\";\n    $pool->write(\"hello \");\n    $pool->write(\"world \");\n    $pool->write(\"\\n\");\n});\n\n$pool->listen('127.0.0.1', 8089);\n\n$pool->start();\n"
  },
  {
    "path": "examples/process/python.php",
    "content": "<?php\n$process = new Swoole\\Process('pyhon_process', true);\n$pid = $process->start();\n\nfunction pyhon_process(Swoole\\Process $worker)\n{\n    $worker->exec('/usr/bin/python', array(\"echo.py\"));\n}\n\n$process->write(\"hello world\\n\");\necho $process->read();\n\n$ret = Swoole\\Process::wait();\nvar_dump($ret);\n"
  },
  {
    "path": "examples/process/select.php",
    "content": "<?php\n$process = new Swoole\\Process(function (Swoole\\Process $worker)\n{\n    echo \"Worker: start. PID=\" . $worker->pid . \"\\n\";\n    sleep(2);\n    $worker->write(\"hello master\\n\");\n    $worker->exit(0);\n}, false);\n\n$pid = $process->start();\n$r = array($process);\n$ret = swoole_select($r, null, null, 1.0);\nvar_dump($ret);\nvar_dump($process->read());\n"
  },
  {
    "path": "examples/process/set_cpu_affinity.php",
    "content": "<?php\nSwoole\\Process::setaffinity(array(0, 2));\nsleep(1000);\n"
  },
  {
    "path": "examples/process/stdin_stdout.php",
    "content": "<?php\nfwrite(STDOUT, \"Hello master.\\n\");\nfwrite(STDOUT, \"Master write: \".fgets(STDIN).\"\\n\");\n"
  },
  {
    "path": "examples/process/test.php",
    "content": "<?php\nSwoole\\Process::daemon();\n\nwhile (1)\n{\n    echo \"hello\";\n    sleep(1);\n}\n"
  },
  {
    "path": "examples/process/worker.php",
    "content": "<?php\n$redirect_stdout = false;\n$workers = [];\n$worker_num = 1;\n\n//Swoole\\Process::daemon(0, 1);\nfor($i = 0; $i < $worker_num; $i++)\n{\n    $process = new Swoole\\Process('child_async', $redirect_stdout);\n    $process->id = $i;\n    $pid = $process->start();\n    $workers[$pid] = $process;\n    //echo \"Master: new worker, PID=\".$pid.\"\\n\";\n}\n\nmaster_async($workers);\n//master_sync($workers);\n\n//异步主进程\nfunction master_async($workers)\n{\n    Swoole\\Process::signal(SIGCHLD, function ($signo) use (&$workers) {\n        while(1)\n        {\n            $ret = Swoole\\Process::wait(false);\n            if ($ret)\n            {\n                $pid = $ret['pid'];\n                $child_process = $workers[$pid];\n                //unset($workers[$pid]);\n                echo \"Worker Exit, kill_signal={$ret['signal']} PID=\" . $pid . PHP_EOL;\n                $new_pid = $child_process->start();\n                $workers[$new_pid] = $child_process;\n                unset($workers[$pid]);\n            }\n            else\n            {\n                break;\n            }\n        }\n    });\n\n    /**\n     * @var $process Swoole\\Process\n     */\n    foreach($workers as $pid => $process)\n    {\n        Swoole\\Event::add($process->pipe, function($pipe) use ($process) {\n            $recv = $process->read();\n            if ($recv) echo \"From Worker: \" . $recv;\n            $process->write(\"HELLO worker {$process->pid}\\n\");\n        });\n        $process->write(\"hello worker[$pid]\\n\");\n    }\n}\n\n//同步主进程\nfunction master_sync($workers)\n{\n    foreach($workers as $pid => $process)\n    {\n        $process->write(\"hello worker[$pid]\\n\");\n        echo \"From Worker: \".$process->read();\n    }\n}\n\nfunction child_sync(Swoole\\Process $worker)\n{\n    //echo \"Worker: start. PID=\".$worker->pid.\"\\n\";\n    //recv data from master\n    $recv = $worker->read();\n\n    echo \"From Master: $recv\\n\";\n\n    //send data to master\n    $worker->write(\"hello master\\n\");\n\n    sleep(2);\n    $worker->exit(0);\n}\n\nfunction child_async(Swoole\\Process $worker)\n{\n    //echo \"Worker: start. PID=\".$worker->pid.\"\\n\";\n    //recv data from master\n    $GLOBALS['worker'] = $worker;\n    global $argv;\n    $worker->name(\"{$argv[0]}: worker #\".$worker->id);\n\n    Swoole\\Process::signal(SIGTERM, function($signal_num) use ($worker) {\n\t\techo \"signal call = $signal_num, #{$worker->pid}\\n\";\n    });\n\n//    Swoole\\Timer::tick(2000, function () use ($worker)\n//    {\n//        if (rand(1, 3) % 2) {\n//            $worker->write(\"hello master {$worker->pid}\\n\");\n//        }\n//    });\n\n    Swoole\\Event::add($worker->pipe, function($pipe) use($worker) {\n        $recv = $worker->read();\n        echo \"From Master: $recv\\n\";\n        //$worker->write(\"hello master\\n\");\n    });\n}\n"
  },
  {
    "path": "examples/process_pool/detach.php",
    "content": "<?php\nuse Swoole\\Process;\nuse Swoole\\Atomic;\n\n$pool = new Process\\Pool(2, SWOOLE_IPC_SOCKET);\n\n$pool->on('WorkerStart', function (Process\\Pool $pool, $workerId) {\n    echo(\"[Worker #{$workerId}] WorkerStart\\n\");\n    if ($workerId == 1) {\n\n    }\n});\n\n$pool->on('WorkerStop', function (\\Swoole\\Process\\Pool $pool, $workerId) {\n    echo(\"[Worker #{$workerId}] WorkerStop\\n\");\n});\n\n$pool->on('Message', function ($pool, $msg) {\n    var_dump($msg);\n    $pool->detach();\n\n    while(1) {\n        sleep(1);\n        echo \"pid=\".posix_getpid().\"\\n\";\n    };\n});\n\n$pool->listen('127.0.0.1', 8089);\n\n$pool->start();\n"
  },
  {
    "path": "examples/process_pool/send.php",
    "content": "<?php\n$fp = stream_socket_client(\"tcp://127.0.0.1:8089\", $errno, $errstr) or die(\"error: $errstr\\n\");\n$msg = json_encode(['data' => 'hello', 'uid' => 1991]);\nfwrite($fp, pack('N', strlen($msg)) . $msg);\nsleep(1);\n"
  },
  {
    "path": "examples/runtime/coroutine.php",
    "content": "<?php\ngo(function () {\n    Swoole\\Runtime::enableCoroutine();\n\n    $redis = new redis;\n    $retval = $redis->connect(\"127.0.0.1\", 6379);\n    var_dump($retval, $redis->getLastError());\n    var_dump($redis->get(\"key\"));\n    var_dump($redis->set(\"key\", \"value2\"));\n    var_dump($redis->get(\"key\"));\n    $redis->close();\n\n\n    $db = new mysqli;\n    $db->connect('127.0.0.1', 'root', 'root', 'test');\n\n    $result = $db->query(\"show databases\");\n    var_dump($result->fetch_all());\n\n    $db = new PDO(\"mysql:host=127.0.0.1;dbname=test;charset=utf8\", \"root\" ,\"root\");\n    $query = $db->prepare(\"select * from userinfo where id=?\");\n    $rs = $query->execute(array(1));\n    var_dump($rs);\n    echo count($query->fetchAll());\n});\n"
  },
  {
    "path": "examples/runtime/curl.php",
    "content": "<?php\n\nSwoole\\Runtime::enableCoroutine();\n\ngo(function () {\n\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"https://www.gov.cn/xinwen/index.htm\");\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HEADER, 0);\n    $output = curl_exec($ch);\n    if ($output === FALSE) {\n        echo \"CURL Error:\" . curl_error($ch);\n    }\n    curl_close($ch);\n    echo strlen($output) . \"bytes\\n\";\n});\n\nSwoole\\Event::wait();\n"
  },
  {
    "path": "examples/runtime/file.php",
    "content": "<?php\nSwoole\\Runtime::enableCoroutine();\nCo\\run(function() {\n\t$fp = fopen('data.txt', 'w+');\n\techo \"open\\n\";\n\tfwrite($fp, str_repeat('A', 1024));\n\tfwrite($fp, str_repeat('B', 1024));\n\techo \"fwrite\\n\";\n\techo fread($fp, 1024);\n\techo \"fread\\n\";\n\tinclude __DIR__.\"/include.php\";\n\techo \"include\\n\";\n\tvar_dump(fstat($fp));\n\techo \"fstat\\n\";\n\tfseek($fp, 0);\n\techo \"fseek\\n\";\n\tfclose($fp);\n});\n"
  },
  {
    "path": "examples/runtime/gethostbyname.php",
    "content": "<?php\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    var_dump(gethostbyname(\"www.baidu.com\"));\n});\n\n"
  },
  {
    "path": "examples/runtime/include.php",
    "content": "<?php\nfunction test()\n{\n\techo __FILE__.\"\\n\";\n}\n"
  },
  {
    "path": "examples/runtime/mkdir.php",
    "content": "<?php\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    var_dump(mkdir(__DIR__.'/test'));\n    var_dump(rmdir(__DIR__.'/test'));\n});\n"
  },
  {
    "path": "examples/runtime/mongodb.php",
    "content": "<?php\nuse function Swoole\\Coroutine\\run;\n\nSwoole\\Runtime::setHookFlags(SWOOLE_HOOK_ALL | SWOOLE_HOOK_MONGODB);\n\nrun(function() {\n    $client = new MongoDB\\Client('mongodb://127.0.0.1:27017');\n    $list = $client->listDatabases();\n    echo \"Available databases:\\n\";\n    foreach ($list as $database) {\n        echo \"- Name: {$database->getName()}\\n\";\n        echo \"  Size: {$database->getSizeOnDisk()} bytes\\n\";\n        echo \"  Empty: \" . ($database->isEmpty() ? 'Yes' : 'No') . \"\\n\";\n        echo \"---\\n\";\n    }\n});\n"
  },
  {
    "path": "examples/runtime/oci.php",
    "content": "<?php\nfunction test()\n{\n\t$tsn = 'oci:dbname=127.0.0.1:1521/freepdb1;charset=AL32UTF8';\n\t$username = \"\";\n\t$password = \"\";\n    try {\n        $dbh = new PDO($tsn, $username, $password);\n        $dbh->exec('create table test (id int)');\n        $dbh->exec('insert into test values(1)');\n        $dbh->exec('insert into test values(2)');\n        $res = $dbh->query(\"select * from test\");\n        var_dump($res->fetchAll());\n        $dbh = null;\n    } catch (PDOException $exception) {\n        echo $exception->getMessage();\n        exit;\n    }\n}\n\nCo::set(['hook_flags' => SWOOLE_HOOK_PDO_ORACLE]);\n\nCo\\run(function () {\n    test();\n});\n"
  },
  {
    "path": "examples/runtime/odbc.php",
    "content": "<?php\n$username = \"\";\n$password = \"\";\n\nfunction test()\n{\n    try {\n        $dbh = new PDO(\"odbc:mysql-test\");\n        $res = $dbh->query(\"select sleep(1) s\");\n        var_dump($res->fetchAll());\n        $dbh = null;\n    } catch (PDOException $exception) {\n        echo $exception->getMessage();\n        exit;\n    }\n}\n\nCo::set(['trace_flags' => SWOOLE_TRACE_CO_ODBC, 'log_level' => SWOOLE_LOG_DEBUG]);\n\nCo\\run(function () {\n    test();\n});\n"
  },
  {
    "path": "examples/runtime/read.php",
    "content": "<?php\nswoole\\runtime::enableCoroutine();\n\ngo(function () {\n\t$fp = fopen(__DIR__.'/data.txt', 'r+');\n\techo \"len=\".strlen(fread($fp, 8291)).\"\\n\";\n});\n"
  },
  {
    "path": "examples/runtime/rename.phpt",
    "content": "<?php\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    var_dump(rename('data.txt', 'data2.txt'));\n});\n"
  },
  {
    "path": "examples/runtime/select.php",
    "content": "<?php\ngo(function () {\n    Swoole\\Runtime::enableCoroutine();\n\n    $fp1 = stream_socket_client(\"tcp://www.baidu.com:80\", $errno, $errstr, 30);\n    $fp2 = stream_socket_client(\"tcp://www.qq.com:80\", $errno, $errstr, 30);\n    if (!$fp1) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        fwrite($fp1, \"GET / HTTP/1.0\\r\\nHost: www.baidu.com\\r\\nUser-Agent: curl/7.58.0\\r\\nAccept: */*\\r\\n\\r\\n\");\n        $r_array = [$fp1, $fp2];\n        $w_array = $e_array = null;\n        $n = stream_select($r_array, $w_array, $e_array, 10);\n        var_dump($r_array, $n);\n        $html = '';\n        while (!feof($fp1)) {\n            $html .= fgets($fp1, 1024);\n        }\n        var_dump(strlen($html));\n        fclose($fp1);\n    }\n});\n\nSwoole\\Event::wait();\n"
  },
  {
    "path": "examples/runtime/sleep.php",
    "content": "<?php\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    sleep(1);\n});\n"
  },
  {
    "path": "examples/runtime/sqlite.php",
    "content": "<?php\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\n\nrun(function() {\n    $db = new PDO('sqlite::memory:');\n    for ($i = 0; $i < 10; $i++) {\n        go(function() use($i, $db) {\n            $db->query('select randomblob(99999999)');\n            var_dump($i);\n        });\n    }\n});\n"
  },
  {
    "path": "examples/runtime/ssl.php",
    "content": "<?php\nswoole\\runtime::enableCoroutine();\n\ngo(function () {\n    $fp = stream_socket_client(\"ssl://www.baidu.com:443\", $errno, $errstr, 30);\n    if (!$fp) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        $http = \"GET / HTTP/1.0\\r\\nAccept: */*User-Agent: Lowell-Agent\\r\\nHost: www.baidu.com\\r\\nConnection: Close\\r\\n\\r\\n\";\n        fwrite($fp, $http);\n        while (!feof($fp)) {\n            echo fgets($fp, 1024);\n        }\n        fclose($fp);\n    }\n});\n"
  },
  {
    "path": "examples/runtime/stream.php",
    "content": "<?php\ngo(function () {\n    Swoole\\Runtime::enableCoroutine();\n    $fp = stream_socket_client(\"tcp://www.baidu.com:80\", $errno, $errstr, 30);\n    if (!$fp) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        fwrite($fp, \"GET / HTTP/1.0\\r\\nHost: www.baidu.com\\r\\nAccept: */*\\r\\n\\r\\n\");\n        while (!feof($fp)) {\n            echo fgets($fp, 1024);\n        }\n        fclose($fp);\n    }\n});\n"
  },
  {
    "path": "examples/runtime/sync.php",
    "content": "<?php\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    $fp = fopen('data.txt', 'w+');\n    fwrite($fp, str_repeat('A', 8192));\n    fflush($fp);\n    fclose($fp);\n});\n"
  },
  {
    "path": "examples/runtime/time_sleep_until.php",
    "content": "<?php\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    var_dump(time_sleep_until(time() - 1));\n    var_dump(time_sleep_until(microtime(true) + 0.2));\n});\n"
  },
  {
    "path": "examples/runtime/unlink.phpt",
    "content": "<?php\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    var_dump(unlink('data.txt'));\n});\n"
  },
  {
    "path": "examples/server/db_pool.php",
    "content": "<?php\n$serv = new Swoole\\Http\\Server(\"127.0.0.1\", 9500);\n\n$serv->set(array(\n    'worker_num' => 100,\n    'task_worker_num' => 20, //database connection pool\n    'db_uri' => 'mysql:host=127.0.0.1;dbname=test',\n    'db_user' => 'root',\n    'db_passwd' => 'root',\n));\n\nfunction my_onRequest_sync($req, $resp)\n{\n    global $serv;\n    $result = $serv->taskwait(\"show tables\");\n    if ($result !== false)\n    {\n        $resp->end(var_export($result['data'], true));\n        return;\n    }\n    else\n    {\n        $resp->status(500);\n        $resp->end(\"Server Error, Timeout\\n\");\n    }\n}\n\nfunction my_onTask($serv, $task_id, $reactor_id, $sql)\n{\n    static $link = null;\n    if ($link == null)\n    {\n        $link = new PDO($serv->setting['db_uri'], $serv->setting['db_user'], $serv->setting['db_passwd']);;\n        if (!$link)\n        {\n            $link = null;\n            return array(\"data\" => '', 'error' => \"connect database failed.\");\n        }\n    }\n    $result = $link->query($sql);\n    if (!$result)\n    {\n        return array(\"data\" => '', 'error' => \"query error\");\n    }\n    $data = $result->fetchAll();\n    return array(\"data\" => $data);\n}\n\nfunction my_onFinish($serv, $data)\n{\n    echo \"AsyncTask Finish:Connect.PID=\" . posix_getpid() . PHP_EOL;\n}\n\n$serv->on('Request', 'my_onRequest_sync');\n$serv->on('Task', 'my_onTask');\n$serv->on('Finish', 'my_onFinish');\n\n$serv->start();\n"
  },
  {
    "path": "examples/server/dispatch_func.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"127.0.0.1\", 9501);\n\n$serv->set(array(\n    'dispatch_func' => function ($serv, $fd, $type, $data) {\n        var_dump($fd, $type, $data);\n        return intval($data[0]);\n    },\n));\n\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $threadId, $data)\n{\n    var_dump($data);\n    echo \"#{$serv->worker_id}>> received length=\" . strlen($data) . \"\\n\";\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/server/dispatch_stream.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"127.0.0.1\", 9501);\n\n$serv->set(array(\n    'dispatch_mode' => 7,\n    'worker_num' => 2,\n));\n\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $threadId, $data)\n{\n    var_dump($data);\n    echo \"#{$serv->worker_id}>> received length=\" . strlen($data) . \"\\n\";\n    $serv->send($fd, \"Swoole $data\\n\");\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/server/echo.php",
    "content": "<?php\n//$serv = new Swoole\\Server(\"0.0.0.0\", 9501, SWOOLE_BASE);\n// $serv = new Swoole\\Server(\"0.0.0.0\", 9501);\n$serv = new Swoole\\Server(\"0.0.0.0\", 9501, SWOOLE_THREAD);\n\nfunction getpid()\n{\n    global $serv;\n    return $serv->mode === SWOOLE_THREAD ? \\Swoole\\Thread::getId() : posix_getpid();\n}\n\n$serv->set([\n    'worker_num' => 2,\n    'task_worker_num' => 3,\n]);\n\n$serv->on('workerStart', function ($serv, $worker_id) {\n    echo \"[#\" . getpid() . \"]\\tWorker#{$worker_id} is started.\\n\";\n});\n\n$serv->on('workerStop', function ($serv, $worker_id) {\n    echo \"[#\" . getpid() . \"]\\tWorker#{$worker_id} is stopped.\\n\";\n});\n\n$serv->on('connect', function ($serv, $fd, $reactor_id) {\n    echo \"[#\" . getpid() . \"]\\tClient@[$fd:$reactor_id]: Connect.\\n\";\n});\n\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n    echo \"[#\" . $serv->worker_id . \"]\\tClient[$fd] receive data: $data\\n\";\n    if ($serv->send($fd, \"hello {$data}\\n\") == false) {\n        echo \"error\\n\";\n    }\n});\n\n$serv->on('close', function ($serv, $fd, $reactor_id) {\n    echo \"[#\" . getpid() . \"]\\tClient@[$fd:$reactor_id]: Close.\\n\";\n});\n\n$serv->on('task', function ($serv, $src_worker_id, $task) {\n    var_dump($task);\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/server/eof_client.php",
    "content": "<?php\n\n$client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n$client->set(\n    array(\n        'open_eof_split' => true,\n        'package_eof' => \"\\r\\n\",\n        'package_max_length' => 8 * 1024 * 1024,\n    )\n);\n\nif (!$client->connect('127.0.0.1', 9504)) {\n    exit(\"connect failed\\n\");\n}\n\n$func = \"send_test\" . intval(empty($argv[1]) ? 3 : $argv[1]);\n\nfor ($l = 0; $l < 1; $l++) {\n    for ($i = 0; $i < 10; $i++) {\n        $len = rand(100000, 200000);\n        $func($client, $len);\n    }\n}\n\nfunction send_test3($client, $len)\n{\n    $data = str_repeat('A', $len) . \"\\r\\n\";\n    $chunks = str_split($data, 4000);\n    foreach ($chunks as $ch) {\n        $client->send($ch);\n    }\n    echo \"send : \" . strlen($data) . \"\\n\";\n//    $data = $client->recv();\n//    echo \"recv : \" . strlen($data) . \"\\n\";\n}\n\nfunction send_test2($client, $len)\n{\n    $data = pack('N', $len + 4);\n    $data .= str_repeat('A', $len) . rand(100000, 999999);\n    $client->send($data);\n\n    $data = $client->recv();\n}\n\nfunction send_test1($client, $len)\n{\n    $client->send(pack('N', $len + 4));\n    usleep(10);\n    $client->send(str_repeat('A', $len) . rand(1000, 9999));\n    $data = $client->recv();\n}\n"
  },
  {
    "path": "examples/server/eof_server.php",
    "content": "<?php\n$serv = new SocketServer();\n$serv->run('0.0.0.0', 9504);\n\nclass SocketServer\n{\n    protected $serv; //swoole server\n\n    const MAX_PACKAGE_LEN = 8000000; //max data accept\n\n    function run($host, $port)\n    {\n        $this->serv = new Swoole\\Server($host, $port, SWOOLE_BASE);\n\n        $this->serv->set(array(\n            'enable_coroutine' => false,\n            'worker_num' => 1, //how much worker will start\n            'open_eof_split' => true,\n            'package_eof' => \"\\r\\n\",\n            'package_max_length' => 8 * 1024 * 1024,\n        ));\n\n        $this->serv->on('receive', array($this, 'onReceive'));\n        $this->serv->start();\n    }\n\n    function onReceive($serv, $fd, $tid, $data)\n    {\n        echo \"recv \" . strlen($data) . \" bytes\\n\";\n//        $packet = substr($data, 4);\n//        $result = array(\n//            \"code\" => \"0\",\n//            \"msg\" => \"ok\",\n//            \"data\" => $packet,\n//        );\n//        $resp = json_encode($result);\n//        $send_data = pack('N', strlen($resp)) . $resp;\n//        echo \"send \" . strlen($send_data) . \" bytes\\n\";\n//        $serv->send($fd, $send_data);\n    }\n}\n"
  },
  {
    "path": "examples/server/getReceivedTime.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"0.0.0.0\", 9501);\n//$serv->on('connect', function ($serv, $fd, $reactor_id){\n//\techo \"[#\".posix_getpid().\"]\\tClient@[$fd:$reactor_id]: Connect.\\n\";\n//});\n$serv->set(array(\n    'worker_num' => 1,\n));\n\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n    usleep(rand(100000, 2000000));\n    var_dump(round($serv->getReceivedTime(), 10));\n});\n\n//$serv->on('close', function ($serv, $fd, $reactor_id) {\n//\techo \"[#\".posix_getpid().\"]\\tClient@[$fd:$reactor_id]: Close.\\n\";\n//});\n\n$serv->start();\n"
  },
  {
    "path": "examples/server/host_update.php",
    "content": "<?php\n\n$serv = new Swoole\\Server(\"127.0.0.1\", 9501);\n$serv->set(array(\n    'worker_num' => 2,\n    //'open_eof_check' => true,\n    //'package_eof' => \"\\r\\n\",\n    'task_worker_num' => 2,\n    //'dispatch_mode' => 2,\n    //'daemonize' => 1,\n    //'heartbeat_idle_time' => 5,\n    //'heartbeat_check_interval' => 5,\n));\nfunction my_onStart($serv)\n{\n    echo \"MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}\\n\";\n    echo \"Server: start.Swoole version is [\" . SWOOLE_VERSION . \"]\\n\";\n}\n\nfunction my_onShutdown($serv)\n{\n    echo \"Server: onShutdown\\n\";\n}\n\nfunction my_onClose($serv, $fd, $reactor_id)\n{\n    //echo \"Client: fd=$fd is closed.\\n\";\n}\n\nfunction my_onConnect($serv, $fd, $reactor_id)\n{\n    //throw new Exception(\"hello world\");\n// \techo \"Client:Connect.\\n\";\n}\n\n\n$class = null;\nfunction my_onWorkerStart($serv, $worker_id)\n{\n    global $argv;\n    global $class;\n    opcache_reset();\n    include \"hot_update_class.php\";\n    $class = new HotUpdate();\n    if ($worker_id >= $serv->setting['worker_num']) {\n        swoole_set_process_name(\"php {$argv[0]} task worker\");\n    } else {\n        swoole_set_process_name(\"php {$argv[0]} event worker\");\n    }\n    //echo \"WorkerStart|MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}|WorkerId=$worker_id\\n\";\n}\n\nfunction my_onWorkerStop($serv, $worker_id)\n{\n    echo \"WorkerStop[$worker_id]|pid=\" . posix_getpid() . \".\\n\";\n}\n\nfunction my_onReceive(Swoole\\Server $serv, $fd, $reactor_id, $data)\n{\n    $cmd = trim($data);\n    if ($cmd == \"reload\") {\n        $serv->reload($serv);\n    } elseif ($cmd == \"task\") {\n        $task_id = $serv->task(\"hello world\", 0);\n        echo \"Dispath AsyncTask: id=$task_id\\n\";\n    } elseif ($cmd == \"info\") {\n        $info = $serv->connection_info($fd);\n        $serv->send($fd, 'Info: ' . var_export($info, true) . PHP_EOL);\n    } elseif ($cmd == \"broadcast\") {\n        $start_fd = 0;\n        while (true) {\n            $conn_list = $serv->connection_list($start_fd, 10);\n            if ($conn_list === false) {\n                break;\n            }\n            $start_fd = end($conn_list);\n            foreach ($conn_list as $conn) {\n                if ($conn === $fd) {\n                    continue;\n                }\n                $serv->send($conn, \"hello from $fd\\n\");\n            }\n        }\n    } //这里故意调用一个不存在的函数\n    elseif ($cmd == \"error\") {\n        hello_no_exists();\n    } elseif ($cmd == \"shutdown\") {\n        $serv->shutdown();\n    } else {\n        global $class;\n        $data .= $class->getData();\n        $serv->send($fd, 'Swoole: ' . $data, $reactor_id);\n        //$serv->close($fd);\n    }\n    //echo \"Client:Data. fd=$fd|reactor_id=$reactor_id|data=$data\";\n}\n\nfunction my_onTask(Swoole\\Server $serv, $task_id, $reactor_id, $data)\n{\n    echo \"AsyncTask[PID=\" . posix_getpid() . \"]: task_id=$task_id.\" . PHP_EOL;\n    $serv->finish(\"OK\");\n}\n\nfunction my_onFinish(Swoole\\Server $serv, $data)\n{\n    echo \"AsyncTask Finish:Connect.PID=\" . posix_getpid() . PHP_EOL;\n}\n\n$serv->on('Start', 'my_onStart');\n$serv->on('Connect', 'my_onConnect');\n$serv->on('Receive', 'my_onReceive');\n$serv->on('Close', 'my_onClose');\n$serv->on('Shutdown', 'my_onShutdown');\n$serv->on('WorkerStart', 'my_onWorkerStart');\n$serv->on('WorkerStop', 'my_onWorkerStop');\n$serv->on('Task', 'my_onTask');\n$serv->on('Finish', 'my_onFinish');\n$serv->on('WorkerError', function ($serv, $worker_id, $worker_pid, $exit_code) {\n    echo \"worker abnormal exit. WorkerId=$worker_id|Pid=$worker_pid|ExitCode=$exit_code\\n\";\n});\n$serv->start();\n"
  },
  {
    "path": "examples/server/hot_update_class.php",
    "content": "<?php\n\tclass HotUpdate {\n\t\tpublic function getData()\n\t\t{\n\t\t\treturn \"hello world~~\".PHP_EOL;\n\t\t}\n\t}\n"
  },
  {
    "path": "examples/server/ip_dispatch.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"0.0.0.0\", 9501);\n$serv->fdlist = [];\n$serv->workerid = 0;\n$serv->set(array(\n\t\t//'tcp_defer_accept' => 5,\n\t\t//'ipc_mode' => 2,\n\t\t'worker_num' => 4,\n\t\t//'task_worker_num' => 2,\n\t\t'dispatch_mode' => 4,   //ip dispatch\n\t\t//'max_request' => 1000,\n\t\t//'daemonize' => true,\n\t\t//'log_file' => '/tmp/swoole.log'\n));\n\n$serv->on('workerStart', function($serv, $worker_id) {\n\techo \"{$worker_id} start\".PHP_EOL;\n\t$serv->workerid = $worker_id;\n});\n\n$serv->on('connect', function ($serv, $fd, $reactor_id){\n\t//echo \"[#\".posix_getpid().\"]\\tClient@[$fd:$reactor_id]: Connect.\\n\";\n\techo \"{$fd} connect, worker:\".$serv->workerid.PHP_EOL;\n\t$conn = print_r($serv->connection_info($fd));\n\t$serv->fdlist[$fd] = 1;\n\tprint_r($serv->fdlist);\n\n});\n\n$serv->on('task', function ($serv, $task_id, $reactor_id, $data){\n\t//var_dump($task_id, $reactor_id, $data);\n\t$fd = $data;\n\t$serv->send($fd, str_repeat('B', 1024*rand(40, 60)).rand(10000, 99999).\"\\n\");\n});\n\n$serv->on('finish', function ($serv, $fd, $reactor_id){\n\n});\n\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n\n    foreach($serv->fdlist as $_fd=>$val) {\n        $serv->send($_fd, \"{$fd} say:\".$data.PHP_EOL);\n    }\n});\n\n$serv->on('close', function ($serv, $fd, $reactor_id) {\n\t//echo \"[#\".posix_getpid().\"]\\tClient@[$fd:$reactor_id]: Close.\\n\";\n\tunset($serv->fdlist[$fd]);\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/server/length_client.php",
    "content": "<?php\n$client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n$client->set(array(\n    'open_length_check' => true,\n    'package_max_length' => 8 * 1024 * 1024,\n    'package_length_type' => 'N', //see php pack()\n    'package_length_offset' => 0,\n    'package_body_offset' => 4,\n));\n\nif (!$client->connect('127.0.0.1', 9504)) {\n    exit(\"connect failed\\n\");\n}\n\n$func = \"send_test\" . intval(empty($argv[1]) ? 3 : $argv[1]);\n\nfor ($l = 0; $l < 1; $l++) {\n    $data = '';\n    for ($i = 0; $i < 10; $i++) {\n        $len = rand(100000, 200000);\n        echo \"send : \" . ($len + 4) . \"\\n\";\n        $func($client, $len);\n    }\n    sleep(1);\n}\n\nfunction send_test3($client, $len)\n{\n    $data = pack('N', $len + 4);\n    $data .= str_repeat('A', $len) . rand(1000, 9999);\n    $chunks = str_split($data, 4000);\n    foreach ($chunks as $ch) {\n        $client->send($ch);\n    }\n//    $data = $client->recv();\n//    echo \"recv : \" . strlen($data) . \"\\n\";\n}\n\nfunction send_test2($client, $len)\n{\n    $data = pack('N', $len + 4);\n    $data .= str_repeat('A', $len) . rand(100000, 999999);\n    $client->send($data);\n\n    $data = $client->recv();\n}\n\nfunction send_test1($client, $len)\n{\n    $client->send(pack('N', $len + 4));\n    usleep(10);\n    $client->send(str_repeat('A', $len) . rand(1000, 9999));\n    $data = $client->recv();\n}\n"
  },
  {
    "path": "examples/server/length_server.php",
    "content": "<?php\n$serv = new SocketServer();\n$serv->run('0.0.0.0', 9504);\n\nclass SocketServer\n{\n    protected $serv; //swoole server\n\n    const MAX_PACKAGE_LEN = 8000000; //max data accept\n\n    function run($host, $port)\n    {\n        $this->serv = new Swoole\\Server($host, $port, SWOOLE_BASE);\n\n        $this->serv->set(array(\n            'enable_coroutine' => false,\n//            'dispatch_mode' => 3, //who come first who is\n            'worker_num' => 1, //how much worker will start\n            'open_length_check' => true,\n            'package_max_length' => 8 * 1024 * 1024,\n            'package_length_type' => 'N', //see php pack()\n            'package_length_offset' => 0,\n            'package_body_offset' => 4,\n        ));\n\n        $this->serv->on('receive', array($this, 'onReceive'));\n        $this->serv->start();\n    }\n\n    function onReceive($serv, $fd, $tid, $data)\n    {\n        echo \"recv \" . strlen($data) . \" bytes\\n\";\n//        $packet = substr($data, 4);\n//        $result = array(\n//            \"code\" => \"0\",\n//            \"msg\" => \"ok\",\n//            \"data\" => $packet,\n//        );\n//        $resp = json_encode($result);\n//        $send_data = pack('N', strlen($resp)) . $resp;\n//        echo \"send \" . strlen($send_data) . \" bytes\\n\";\n//        $serv->send($fd, $send_data);\n    }\n}\n"
  },
  {
    "path": "examples/server/listen_1k_port.php",
    "content": "<?php\n$serv = new Swoole\\Server('127.0.0.1', 9001);\n\nfor($port = 9002; $port < 9999; $port++)\n{\n    $serv->listen(\"127.0.0.1\", $port, SWOOLE_SOCK_TCP);\n}\n\n$serv->on(\"receive\", function($serv, $fd, $reactor_id, $data) {\n    $info = $serv->getClientInfo($fd);\n    var_dump($info);\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/server/local_listener.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"0.0.0.0\", 9502);\n\n$serv->on('workerstart', function($server, $id) {\n     global $argv;\n     swoole_set_process_name(\"php {$argv[0]}: worker\");\n\n     $local_listener = stream_socket_server(\"127.0.0.1\", 9999);\n\n     Swoole\\Event::add($local_listener, function($server){\n\t\t  $local_client = stream_socket_accept($server);\n\n\t\t  Swoole\\Event::add($local_client, function($client){\n\t\t\t  echo fread($client, 8192);\n\t\t\t  fwrite($client, \"hello\");\n\t\t  });\n     });\n\n});\n\n$serv->on('connect', function (Swoole\\Server $serv, $fd, $reactor_id) {\n\t//echo \"connect\\n\";;\n});\n\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n\t$serv->send($fd, \"Swoole: \".$data);\n\t//$serv->close($fd);\n});\n\n$serv->on('close', function (Swoole\\Server $serv, $fd, $reactor_id) {\n\t//var_dump($serv->connection_info($fd));\n\t//echo \"onClose\\n\";\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/server/manager_timer.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"0.0.0.0\", 9501);\n\n$serv->set(array(\n    'worker_num' => 1,\n));\n\n$serv->on('managerStart', function ($erv) {\n    echo \"manager start\\n\";\n\n    // sleep(30);\n\n    $id = Swoole\\Timer::tick(3000, function () {\n        echo \"timer 1\\n\";\n    });\n\n    Swoole\\Timer::after(9000, function () use ($id) {\n        echo \"timer 2\\n\";\n        Swoole\\Timer::clear($id);\n\n        Swoole\\Timer::tick(2000, function () {\n            echo \"timer 3\\n\";\n        });\n\n        Swoole\\Timer::tick(300, function () {\n            echo \"timer 4\\n\";\n        });\n    });\n});\n\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n\techo \"[#\".$serv->worker_id.\"]\\tClient[$fd] receive data: $data\\n\";\n    if ($serv->send($fd, \"hello {$data}\\n\") == false)\n    {\n        echo \"error\\n\";\n    }\n\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/server/mixed.php",
    "content": "<?php\nuse Swoole\\Timer;\n\nclass G\n{\n    public static $index = 0;\n\n    public static $serv;\n\n    public static $config = [\n        'user'  => 'www-data',\n        'group' => 'www-data',\n        //'reactor_num'              => 16,     // 线程数. 一般设置为CPU核数的1-4倍\n        'worker_num'      => 2,    // 工作进程数量. 设置为CPU的1-4倍最合理\n        'max_request'     => 1000,     // 防止 PHP 内存溢出, 一个工作进程处理 X 次任务后自动重启 (注: 0,不自动重启)\n        'max_conn'        => 10000, // 最大连接数\n        'task_worker_num' => 1,     // 任务工作进程数量\n        //        'task_ipc_mode'            => 2,     // 设置 Task 进程与 Worker 进程之间通信的方式。\n        'task_max_request' => 0,     // 防止 PHP 内存溢出\n        //'task_tmpdir'              => '/tmp',\n        //'message_queue_key'        => ftok(SYS_ROOT . 'queue.msg', 1),\n        'dispatch_mode' => 2,\n        //'daemonize'                => 1,     // 设置守护进程模式\n        'backlog' => 128,\n        //'log_file'                 => '/data/logs/swoole.log',\n        'heartbeat_check_interval' => 2,    // 心跳检测间隔时长(秒)\n        'heartbeat_idle_time'      => 3,   // 连接最大允许空闲的时间\n        //'open_eof_check'           => 1,\n        //'open_eof_split'           => 1,\n        //'package_eof'              => \"\\r\\r\\n\",\n        //'open_cpu_affinity'        => 1,\n        'socket_buffer_size' => 1024 * 1024 * 128,\n        'output_buffer_size' => 1024 * 1024 * 2,\n        //'enable_delay_receive'       => true,\n        //'cpu_affinity_ignore' =>array(0,1)//如果你的网卡2个队列（或者没有多队列那么默认是cpu0来处理中断）,并且绑定了core 0和core 1,那么可以通过这个设置避免swoole的线程或者进程绑定到这2个core，防止cpu0，1被耗光而造成的丢包\n    ];\n}\n\nif (isset($argv[1]) and $argv[1] == 'daemon') {\n    G::$config['daemonize'] = true;\n} else {\n    G::$config['daemonize'] = false;\n}\n\n//$mode = SWOOLE_BASE;\n$mode = SWOOLE_PROCESS;\n\n$serv = new Swoole\\Server('0.0.0.0', '9501', $mode, SWOOLE_SOCK_TCP);\n$serv->listen('0.0.0.0', 9502, SWOOLE_SOCK_UDP);\n$serv->listen('::', 9503, SWOOLE_SOCK_TCP6);\n$serv->listen('::', 9504, SWOOLE_SOCK_UDP6);\n$process1 = new Swoole\\Process(function ($worker) use ($serv) {\n    global $argv;\n    swoole_set_process_name(\"php {$argv[0]}: my_process1\");\n    Timer::tick(2000, function ($interval) use ($worker, $serv) {\n        echo \"#{$worker->pid} child process timer {$interval}\\n\"; // 如果worker中没有定时器，则会输出 process timer xxx\n        foreach ($serv->connections as $conn) {\n            $serv->send($conn, \"heartbeat\\n\");\n        }\n    });\n    Timer::tick(5000, function () use ($serv) {\n        $serv->sendMessage('hello event worker', 0);\n        $serv->sendMessage('hello task worker', 4);\n    });\n}, false);\n\n//$serv->addprocess($process1);\n\n$process2 = new Swoole\\Process(function ($worker) use ($serv) {\n    global $argv;\n    swoole_set_process_name(\"php {$argv[0]}: my_process2\");\n    Timer::tick(2000, function ($interval) use ($worker, $serv) {\n        echo \"#{$worker->pid} child process timer {$interval}\\n\"; // 如果worker中没有定时器，则会输出 process timer xxx\n    });\n}, false);\n\n//$serv->addprocess($process2);\n\n$serv->set(G::$config);\n$serv->set(['reactor_num' => 4]);\n\n/*\n * 使用类的静态属性，可以直接访问\n */\nG::$serv = $serv;\n\nfunction my_onStart(Swoole\\Server $serv)\n{\n    global $argv;\n    swoole_set_process_name(\"php {$argv[0]}: master\");\n    my_log('Server: start.Swoole version is [' . SWOOLE_VERSION . ']');\n    my_log(\"MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}\");\n}\n\nfunction my_log($msg)\n{\n    global $serv;\n    if (empty($serv->worker_pid)) {\n        $serv->worker_pid = posix_getpid();\n    }\n    echo '#' . $serv->worker_pid . \"\\t[\" . date('H:i:s') . \"]\\t\" . $msg . PHP_EOL;\n}\n\nfunction forkChildInWorker()\n{\n    global $serv;\n    echo \"on worker start\\n\";\n    $process = new Swoole\\Process(function (Swoole\\Process $worker) use ($serv) {\n        // \t\t$serv = new Swoole\\Server( \"0.0.0.0\", 9503 );\n        // \t\t$serv->set(array(\n        // \t\t\t\t'worker_num' => 1\n        // \t\t));\n        // \t\t$serv->on ( 'receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n        // \t\t\t$serv->send ( $fd, \"Swoole: \" . $data );\n        // \t\t\t$serv->close ( $fd );\n        // \t\t});\n        // \t\t$serv->start ();\n        // \t\tSwoole\\Event::add ($worker->pipe, function ($pipe) use ($worker) {\n        // \t\t\techo $worker->read().\"\\n\";\n        // \t\t});\n    });\n\n    $pid = $process->start();\n    echo \"Fork child process success. pid={$pid}\\n\";\n    //保存子进程对象，这里如果不保存，那对象会被销毁，管道也会被关闭\n    $serv->childprocess = $process;\n}\n\nfunction processRename(Swoole\\Server $serv, $worker_id)\n{\n    global $argv;\n    if ($serv->taskworker) {\n        swoole_set_process_name(\"php {$argv[0]}: task\");\n    } else {\n        swoole_set_process_name(\"php {$argv[0]}: worker\");\n    }\n    //    if ($worker_id == 0)\n    //    {\n    //        var_dump($serv->setting);\n    //    }\n    my_log(\"WorkerStart: MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}|WorkerId={$serv->worker_id}|WorkerPid={$serv->worker_pid}\");\n}\n\nfunction setTimerInWorker(Swoole\\Server $serv, $worker_id)\n{\n    if ($worker_id == 0) {\n        echo 'Start: ' . microtime(true) . \"\\n\";\n        //$serv->addtimer(3000);\n        //\t\t$serv->addtimer(7000);\n        //var_dump($serv->gettimer());\n    }\n    //\t$serv->after(2000, function(){\n    //\t\techo \"Timeout: \".microtime(true).\"\\n\";\n    //\t});\n    //\t$serv->after(5000, function(){\n    //\t\techo \"Timeout: \".microtime(true).\"\\n\";\n    //\t\tglobal $serv;\n    //\t\t$serv->deltimer(3000);\n    //\t});\n}\n\nfunction my_onShutdown($serv)\n{\n    echo \"Server: onShutdown\\n\";\n}\n\nfunction my_onClose(Swoole\\Server $serv, $fd, $reactor_id)\n{\n    my_log(\"Client[{$fd}@{$reactor_id}]: fd={$fd} is closed\");\n    var_dump($serv->getClientInfo($fd));\n}\n\nfunction my_onConnect(Swoole\\Server $serv, $fd, $reactor_id)\n{\n    //throw new Exception(\"hello world\");\n    //    var_dump($serv->connection_info($fd));\n    //var_dump($serv, $fd, $reactor_id);\n    //    echo \"Worker#{$serv->worker_pid} Client[$fd@$reactor_id]: Connect.\\n\";\n    $serv->after(2000, function () use ($serv, $fd) {\n        $serv->confirm($fd);\n    });\n    my_log(\"Client: Connect --- {$fd}\");\n}\n\nfunction timer_show($id)\n{\n    my_log(\"Timer#{$id}\");\n}\n\nfunction my_onWorkerExit(Swoole\\Server $serv, $worker_id)\n{\n    global $argv;\n}\n\nfunction my_onWorkerStart(Swoole\\Server $serv, $worker_id)\n{\n    processRename($serv, $worker_id);\n\n    if (!$serv->taskworker) {\n        Swoole\\Process::signal(SIGUSR2, function ($signo) {\n            echo \"SIGNAL: {$signo}\\n\";\n        });\n        Swoole\\Event::defer(function () {\n            echo \"defer call\\n\";\n        });\n        //        $serv->tick(2000, function() use ($serv) {\n        //           echo \"Worker-{$serv->worker_id} tick-2000\\n\";\n        //        });\n    }\n    //        Swoole\\Timer::after(2000, function() {\n    //            echo \"after 2 secends.\\n\";\n    //        });\n    //        $serv->tick(1000, function ($id) use ($serv) {\n    //            if (G::$index > 10) {\n    //                $serv->after(2500, 'timer_show', 2);\n    //                G::$index = 0;\n    //            } else {\n    //                G::$index++;\n    //            }\n    //            timer_show($id);\n    //        });\n\n    //forkChildInWorker();\n    //\tsetTimerInWorker($serv, $worker_id);\n}\n\nfunction my_onWorkerStop($serv, $worker_id)\n{\n    echo \"WorkerStop[{$worker_id}]|pid=\" . $serv->worker_pid . \".\\n\";\n}\n\nfunction my_onPacket($serv, $data, $clientInfo)\n{\n    $serv->sendto($clientInfo['address'], $clientInfo['port'], 'Server ' . $data);\n    var_dump($clientInfo);\n}\n\nfunction my_onReceive(Swoole\\Server $serv, $fd, $reactor_id, $data)\n{\n    my_log(\"Worker#{$serv->worker_pid} Client[{$fd}@{$reactor_id}]: received: {$data}\");\n    $cmd = trim($data);\n    if ($cmd == 'reload') {\n        $serv->reload();\n    } elseif ($cmd == 'task') {\n        $task_id = $serv->task('task ' . $fd);\n        echo \"Dispath AsyncTask: id={$task_id}\\n\";\n    } elseif ($cmd == 'taskclose') {\n        $serv->task('close ' . $fd);\n        echo \"close the connection in taskworker\\n\";\n    } elseif ($cmd == 'tasksend') {\n        $serv->task('send ' . $fd);\n    } elseif ($cmd == 'bigtask') {\n        $serv->task(str_repeat('A', 8192 * 5));\n    } elseif ($cmd == 'taskwait') {\n        $result = $serv->taskwait('taskwait');\n        if ($result) {\n            $serv->send($fd, 'taskwaitok');\n        }\n        echo 'SyncTask: result=' . var_export($result, true) . \"\\n\";\n    } elseif ($cmd == 'taskWaitMulti') {\n        $result = $serv->taskWaitMulti([\n            str_repeat('A', 8192 * 5),\n            str_repeat('B', 8192 * 6),\n            str_repeat('C', 8192 * 8),\n        ]);\n        if ($result) {\n            $resp = \"taskWaitMulti ok\\n\";\n            foreach ($result as $k => $v) {\n                $resp .= \"result[{$k}] length=\" . strlen($v) . \"\\n\";\n            }\n            $serv->send($fd, $resp);\n        } else {\n            $serv->send($fd, \"taskWaitMulti error\\n\");\n        }\n    } elseif ($cmd == 'hellotask') {\n        $serv->task('hellotask');\n    } elseif ($cmd == 'taskcallback') {\n        $serv->task('taskcallback', -1, function (Swoole\\Server $serv, $task_id, $data) {\n            echo 'Task Callback: ';\n            var_dump($task_id, $data);\n        });\n    } elseif ($cmd == 'sendto') {\n        $serv->sendto('127.0.0.1', 9999, 'hello world');\n    } elseif ($cmd == 'close') {\n        $serv->send($fd, \"close connection\\n\");\n        $result = $serv->close($fd);\n    } elseif ($cmd == 'info') {\n        $info = $serv->connection_info(strval($fd), $reactor_id);\n        var_dump($info['remote_ip']);\n        $serv->send($fd, 'Info: ' . var_export($info, true) . PHP_EOL);\n    } elseif ($cmd == 'proxy') {\n        $serv->send(1, \"hello world\\n\");\n    } elseif ($cmd == 'sleep') {\n        sleep(10);\n    } elseif ($cmd == 'foreach') {\n        foreach ($serv->connections as $fd) {\n            echo \"conn : {$fd}\\n\";\n        }\n        return;\n    } elseif ($cmd == 'tick') {\n        $serv->tick(2000, function ($id) {\n            echo \"tick #{$id}\\n\";\n        });\n    } elseif ($cmd == 'addtimer') {\n        $serv->addtimer(3000);\n    } elseif ($cmd == 'list') {\n        $start_fd = 0;\n        echo \"broadcast\\n\";\n        while (true) {\n            $conn_list = $serv->connection_list($start_fd, 10);\n            if (empty($conn_list)) {\n                echo \"iterates finished\\n\";\n                break;\n            }\n            $start_fd = end($conn_list);\n            var_dump($conn_list);\n        }\n    } elseif ($cmd == 'list2') {\n        foreach ($serv->connections as $con) {\n            var_dump($serv->connection_info($con));\n        }\n    } elseif ($cmd == 'stats') {\n        $serv_stats = $serv->stats();\n        $serv->send($fd, 'Stats: ' . var_export($serv_stats, true) . \"\\ncount=\" . count($serv->connections) . PHP_EOL);\n    } elseif ($cmd == 'broadcast') {\n        broadcast($serv, $fd, \"hello from {$fd}\\n\");\n    } //这里故意调用一个不存在的函数\n    elseif ($cmd == 'error') {\n        hello_no_exists();\n    } elseif ($cmd == 'exit') {\n        exit(\"worker php exit.\\n\");\n    } //关闭fd\n    elseif (substr($cmd, 0, 5) == 'close') {\n        $close_fd = substr($cmd, 6);\n        $serv->close($close_fd);\n    } elseif ($cmd == 'shutdown') {\n        $serv->shutdown();\n    } elseif ($cmd == 'fatalerror') {\n        require __DIR__ . '/php/error.php';\n    } elseif ($cmd == 'defer') {\n        $serv->defer(function () use ($fd, $serv) {\n            $serv->close($fd);\n            $serv->defer(function () {\n                echo \"deferd\\n\";\n            });\n        });\n        $serv->send($fd, 'Swoole: ' . $data, $reactor_id);\n    } else {\n        $serv->send($fd, 'Swoole: ' . $data, $reactor_id);\n        //$serv->close($fd);\n    }\n    //echo \"Client:Data. fd=$fd|reactor_id=$reactor_id|data=$data\";\n    //    $serv->after(\n    //        800, function () {\n    //            echo \"hello\";\n    //        }\n    //    );\n    //Swoole\\Server_send($serv, $other_fd, \"Server: $data\", $other_reactor_id);\n}\n\nfunction my_onTask(Swoole\\Server $serv, $task_id, $reactor_id, $data)\n{\n    if ($data == 'taskwait') {\n        $fd = str_replace('task-', '', $data);\n        $serv->send($fd, 'hello world');\n        return ['task' => 'wait'];\n    }\n    if ($data == 'taskcallback') {\n        return ['task' => 'callback'];\n    }\n    $cmd = explode(' ', $data);\n    if ($cmd[0] == 'send') {\n        $serv->send($cmd[1], str_repeat('A', 10000) . \"\\n\");\n    } elseif ($cmd[0] == 'close') {\n        $serv->close($cmd[1]);\n    } else {\n        echo 'bigtask: length=' . strlen($data) . \"\\n\";\n        return $data;\n    }\n    //        $serv->sendto('127.0.0.1', 9999, \"hello world\");\n    //Swoole\\Timer::after(1000, \"test\");\n    //        var_dump($data);\n    //        $serv->send($fd, str_repeat('A', 8192 * 2));\n    //        $serv->send($fd, str_repeat('B', 8192 * 2));\n    //        $serv->send($fd, str_repeat('C', 8192 * 2));\n    //        $serv->send($fd, str_repeat('D', 8192 * 2));\n    return;\n\n    if ($data == 'hellotask') {\n        broadcast($serv, 0, 'hellotask');\n    } else {\n        echo 'AsyncTask[PID=' . $serv->worker_pid . \"]: task_id={$task_id}.\" . PHP_EOL;\n        //eg: test-18\n        return $data;\n    }\n}\n\nfunction my_onFinish(Swoole\\Server $serv, $task_id, $data)\n{\n    [$str, $fd] = explode('-', $data);\n    $serv->send($fd, 'taskok');\n    var_dump($str, $fd);\n    echo \"AsyncTask Finish: result={$data}. PID=\" . $serv->worker_pid . PHP_EOL;\n}\n\nfunction my_onWorkerError(Swoole\\Server $serv, $worker_id, $worker_pid, $exit_code, $signo)\n{\n    echo \"worker abnormal exit. WorkerId={$worker_id}|Pid={$worker_pid}|ExitCode={$exit_code}|Signal={$signo}\\n\";\n}\n\nfunction broadcast(Swoole\\Server $serv, $fd = 0, $data = 'hello')\n{\n    $start_fd = 0;\n    echo \"broadcast\\n\";\n    while (true) {\n        $conn_list = $serv->connection_list($start_fd, 10);\n        if ($conn_list === false) {\n            break;\n        }\n        $start_fd = end($conn_list);\n        foreach ($conn_list as $conn) {\n            if ($conn === $fd) {\n                continue;\n            }\n            $ret1 = $serv->send($conn, $data);\n            //var_dump($ret1);\n            //$ret2 = $serv->close($conn);\n            //var_dump($ret2);\n        }\n    }\n}\n\n$serv->on('PipeMessage', function ($serv, $src_worker_id, $msg) {\n    my_log(\"PipeMessage: Src={$src_worker_id},Msg=\" . trim($msg));\n    if ($serv->taskworker) {\n        $serv->sendMessage('hello user process',\n            $src_worker_id);\n    }\n});\n\n$serv->on('Start', 'my_onStart');\n$serv->on('Connect', 'my_onConnect');\n$serv->on('Receive', 'my_onReceive');\n$serv->on('Packet', 'my_onPacket');\n$serv->on('Close', 'my_onClose');\n$serv->on('Shutdown', 'my_onShutdown');\n$serv->on('WorkerStart', 'my_onWorkerStart');\n$serv->on('WorkerStop', 'my_onWorkerStop');\n$serv->on('Task', 'my_onTask');\n$serv->on('Finish', 'my_onFinish');\n$serv->on('WorkerError', 'my_onWorkerError');\n$serv->on('WorkerExit', 'my_onWorkerExit');\n$serv->on('ManagerStart', function ($serv) {\n    global $argv;\n    swoole_set_process_name(\"php {$argv[0]}: manager\");\n    Timer::after(5000, function () use ($serv) {\n        echo \"shutdown\\n\";\n        $serv->shutdown();\n    });\n});\n$serv->start();\n"
  },
  {
    "path": "examples/server/multi_instance.php",
    "content": "<?php\nfor ($i = 0; $i < 2; $i++)\n{\n    $p = new Swoole\\Process(function () use ($i) {\n        $port = 9501 + $i;\n        $http = new Swoole\\Http\\Server(\"127.0.0.1\", $port);\n\n        $http->on(\"start\", function ($server) use ($port) {\n            echo \"Swoole http server is started at http://127.0.0.1:{$port}\\n\";\n        });\n\n        $http->on(\"request\", function ($request, $response) {\n            $response->header(\"Content-Type\", \"text/plain\");\n            $response->end(\"Hello World\\n\");\n        });\n\n        $http->start();\n    }, false, false);\n    $p->start();\n}\n"
  },
  {
    "path": "examples/server/multi_port.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"0.0.0.0\", 9501);\n//这里监听了一个UDP端口用来做内网管理\n$port = $serv->addlistener('127.0.0.1', 9502, SWOOLE_SOCK_UDP);\n\n$port->on('packet', function($serv, $data, $client) {\n\tvar_dump($data, $client);\n\t$serv->sendto($client['address'], $client['port'], \"welcome admin\\n\");\n});\n$serv->on('connect', function ($serv, $fd) {\n    echo \"Client:Connect.\\n\";\n});\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n    $info = $serv->connection_info($fd, $reactor_id);\n    //来自9502的内网管理端口\n    if($info['server_port'] == 9502) {\n\t\t$serv->send($fd, \"welcome admin\\n\");\n\t\t$start_fd = 0;\n        while(true)\n        {\n            $conn_list = $serv->connection_list($start_fd, 10);\n            if($conn_list === false)\n            {\n                break;\n            }\n            $start_fd = end($conn_list);\n            var_dump($conn_list);\n\n            foreach($conn_list as $conn)\n            {\n                if($conn === $fd) continue;\n                $serv->send($conn, \"hello from $fd\\n\");\n            }\n        }\n\t}\n\t//来自外网\n\telse {\n\t\t$serv->send($fd, 'Swoole: '.$data);\n\t}\n});\n$serv->on('close', function ($serv, $fd) {\n    echo \"Client: Close.\\n\";\n});\n$serv->start();\n"
  },
  {
    "path": "examples/server/pipe_message.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"0.0.0.0\", 9501, SWOOLE_BASE);\n//$serv = new Swoole\\Server(\"0.0.0.0\", 9501);\n$serv->set(array(\n    'worker_num' => 2,\n    'task_worker_num' => 2,\n));\n\n$serv->on('pipeMessage', function($serv, $src_worker_id, $data) {\n\techo \"#{$serv->worker_id} message from #$src_worker_id: $data\\n\";\n});\n\n$serv->on('task', function (Swoole\\Server $serv, $task_id, $reactor_id, $data){\n    echo \"#{$serv->worker_id} NewTask: $data\\n\";\n    $serv->sendMessage($data, 0);\n\t//$serv->send($fd, str_repeat('B', 1024*rand(40, 60)).rand(10000, 99999).\"\\n\");\n});\n\n$serv->on('finish', function ($serv, $fd, $reactor_id){\n\n});\n\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n    $cmd = trim($data);\n    if($cmd == 'totask')\n    {\n        $serv->sendMessage(\"hello task process\", 2);\n    }\n    elseif($cmd == 'toworker')\n    {\n        $worker_id = 1 - $serv->worker_id;\n        $serv->sendMessage(\"hello worker\", $worker_id);\n    }\n    elseif($cmd == 'task2worker')\n    {\n        $serv->task('hello worker from task.');\n    }\n    else\n    {\n        echo \"#{$serv->worker_id} Recv: $data\\n\";\n    }\n});\n\n$serv->on('close', function ($serv, $fd, $reactor_id) {\n\t//echo \"[#\".posix_getpid().\"]\\tClient@[$fd:$reactor_id]: Close.\\n\";\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/server/proxy_sync.php",
    "content": "<?php\nclass ProxyServer\n{\n    protected $clients;\n    protected $backends;\n    protected $serv;\n\n    function run()\n    {\n        $serv = new Swoole\\Server(\"127.0.0.1\", 9509);\n        $serv->set(array(\n            'worker_num' => 32, //reactor thread num\n            'backlog' => 128, //listen backlog\n            'max_conn' => 10000,\n            'dispatch_mode' => 2,\n            //'open_tcp_keepalive' => 1,\n            //'log_file' => '/tmp/swoole.log', //swoole error log\n        ));\n        $serv->on('WorkerStart', array($this, 'onStart'));\n        $serv->on('Connect', array($this, 'onConnect'));\n        $serv->on('Receive', array($this, 'onReceive'));\n        $serv->on('Close', array($this, 'onClose'));\n        $serv->on('WorkerStop', array($this, 'onShutdown'));\n        $serv->start();\n    }\n\n    function onStart($serv)\n    {\n        $this->serv = $serv;\n        echo \"Server: start.Swoole version is [\" . SWOOLE_VERSION . \"]\\n\";\n    }\n\n    function onShutdown($serv)\n    {\n        echo \"Server: onShutdown\\n\";\n    }\n\n    function onClose($serv, $fd, $reactor_id)\n    {\n\n    }\n\n    function onConnect($serv, $fd, $reactor_id)\n    {\n\n    }\n\n    function onReceive($serv, $fd, $reactor_id, $data)\n    {\n\t\t$socket = new Swoole\\Client(SWOOLE_SOCK_TCP);\n        if($socket->connect('127.0.0.1', 80, 0.5))\n        {\n\t\t\t$socket->send($data);\n\t\t\t$serv->send($fd, $socket->recv(8192, 0));\n\t\t}\n        unset($socket);\n        $serv->close($fd);\n    }\n}\n\n$serv = new ProxyServer();\n$serv->run();\n"
  },
  {
    "path": "examples/server/redis_pool.php",
    "content": "<?php\n\n#This script is forked from db_pool.php\n#\n#u can test like this,\n#\n#/usr/local/php/bin/php bench.php -n 1000 -c 100 -s tcp://127.0.0.1:9508 -f short_tcp / long_tcp\n#\n#\n\ndefine(\"SERVER_RELOAD\", 'U0VSVkVSX1JFTE9BRAo=');\n\nif(!extension_loaded('swoole')){\n    throw new Exception(\"install swoole extension, pecl install swoole\");\n}\n\nif(!extension_loaded('redis')) {\n\tthrow new Exception(\"install redis extension, pecl install redis\");\n}\n\n$serv = new Swoole\\Server(\"0.0.0.0\", 9508);\n\n$serv->set(array(\n    'worker_num' => 4,//base on you cpu nums\n    'task_worker_num' => 4,//better equal to worker_num, anyway you can define your own\n\t'heartbeat_check_interval' => 5,\n\t'heartbeat_idle_time' => 5,\n    'open_cpu_affinity' => 1,\n\t'open_eof_check'  => 1,\n\t'package_eof'   => \"\\r\\n\\r\\n\",\n\t'package_max_length' => 1024 * 16,\n\t//'daemonize' => 1\n));\n\nfunction onStart($serv) {\n    echo \"MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}\\n\";\n    echo \"Server: start.Swoole version is [\".SWOOLE_VERSION.\"]\\n\";\n}\n\nfunction onReceive($serv, $fd, $reactor_id, $key)\n{\n\t$key = trim($key);\n    if($key === SERVER_RELOAD) { // check if this is a reload cmd\n        $ret = $serv->reload($serv);\n        ($ret === true) ? $serv->send($fd, \"reload success\\n\") : $serv->send($fd, \"reload fail\\n\");\n    }else {\n\t    $result = $serv->taskwait($key);\n\t    if ($result !== false) {\n\t        list($status, $data) = explode(':', $result, 2);\n\t        if ($status == 'OK') {\n\t            $serv->send($fd, $key . \" : \" . var_export(unserialize($data), true) . \"\\n\");\n\t        } else {\n\t            $serv->send($fd, $data);\n\t        }\n\t        return;\n\t    } else {\n\t        $serv->send($fd, \"Error. Task timeout\\n\");\n\t    }\n    }\n}\n\nfunction onTask($serv, $task_id, $reactor_id, $key)\n{\n    static $redis = null;\n    if ($redis == null) {\n        $redis = new Redis();\n        $redis->pconnect(\"127.0.0.1\", 6379);\n        if (!$redis) {\n            $redis = null;\n            $serv->finish(\"ER: Init Redis Fail.\");\n            return;\n        }\n    }\n    $data = $redis->get($key);\n    if ($data === false) {\n        $serv->finish(\"ER: Get Data Fail.\");\n        return;\n    }\n    $serv->finish(\"OK:\" . serialize($data));\n}\n\nfunction onFinish($serv, $data)\n{\n    echo \"AsyncTask Finish:Connect.PID=\" . posix_getpid() . PHP_EOL;\n}\n\n$serv->on('Start', 'onStart');\n$serv->on('Receive', 'onReceive');\n$serv->on('Task', 'onTask');\n$serv->on('Finish', 'onFinish');\n$serv->start();\n"
  },
  {
    "path": "examples/server/reload_aysnc.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"0.0.0.0\", 9501);\n\n$serv->set([\n    'worker_num' => 4,\n    'reload_async' => true,\n    'max_wait_time' => 5,\n    'task_worker_num' => 2,\n]);\n\n$serv->on('WorkerStart', function ($serv, $wid) {\n    echo \"Worker#$wid is started\\n\";\n    if ($serv->taskworker) {\n        return;\n    }\n    Swoole\\Event::add(STDIN, function () use ($wid) {\n        $data = fread(STDIN, 8192);\n        if ($data) {\n            echo \"#{$wid}: $data\";\n        }\n    });\n});\n\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n\techo \"[#\".$serv->worker_id.\"]\\tClient[$fd]: $data\\n\";\n});\n\n$serv->on('Task', function (Swoole\\Server $serv, $task_id, $reactor_id, $data) {\n    //echo \"#{$serv->worker_id}\\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=\".strlen($data).\".\".PHP_EOL;\n//    $serv->finish($data);\n    return $data;\n});\n\n$serv->on('Finish', function (Swoole\\Server $serv, $task_id, $data) {\n    echo \"Task#$task_id finished, data_len=\".strlen($data).PHP_EOL;\n});\n\n$serv->on('WorkerStop', function ($serv, $wid) {\n    //sleep($wid + 1);\n});\n\n$serv->on('WorkerExit', function ($serv, $wid) {\n    echo \"WorkerExit, PID=\".posix_getpid().\"\\t$wid\\n\";\n    Swoole\\Event::del(STDIN);\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/server/reload_force.php",
    "content": "<?php\n$flag = 0;\n$serv = new Swoole\\Server('127.0.0.1', 9501, SWOOLE_BASE);\n$serv->set([\n    \"worker_num\" => 4,\n    \"max_wait_time\" => 1\n]);\n$serv->on(\"WorkerStart\", function (\\Swoole\\Server $server, $worker_id) {\n    global $flag;\n    echo \"$worker_id [\".$server->worker_pid.\"] start \\n\";\n});\n$serv->on('receive', function ($serv, $fd, $tid, $data) {\n    echo \"$tid recv $data\\n\";\n    if ($data) {\n        sleep(100);\n    }\n});\n$serv->start();\n"
  },
  {
    "path": "examples/server/reload_force2.php",
    "content": "<?php\n$flag = 0;\n$serv = new Swoole\\Server('127.0.0.1', 9501);\n$serv->set([\n    \"worker_num\" => 4,\n    \"max_wait_time\" => 1\n]);\n$serv->on(\"WorkerStart\", function (\\Swoole\\Server $server, $worker_id) {\n    global $flag;\n    echo \"$worker_id [\".$server->worker_pid.\"] start \\n\";\n});\n$serv->on('receive', function ($serv, $fd, $tid, $data) {\n    echo \"$tid recv $data\\n\";\n    if ($data) {\n        sleep(100);\n    }\n});\n$serv->start();\n"
  },
  {
    "path": "examples/server/send_1m.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"0.0.0.0\", 9509);\n$serv->set(array('worker_num' => 1));\n$serv->on('workerStart', function($serv, $worker_id) {\n        //if($worker_id == 0) $serv->addtimer(500);\n});\n$serv->on('connect', function ($serv, $fd, $reactor_id){\n    $serv->array['fd'] = &strval($fd);\n    echo \"[#\".posix_getpid().\"]\\tClient@[$fd:$reactor_id]: Connect.\\n\";\n});\n$serv->on('receive', function ($serv, $fd, $reactor_id, $data) {\n    //echo \"[#\".posix_getpid().\"]\\tClient[$fd]: $data\\n\";\n    $array = array('A', 'B', 'C', 'D', 'E', 'F', 'G');\n    $data = '';\n    $n_bytes = 0;\n    for ($i = 0; $i < 10; $i++)\n    {\n        $_str = str_repeat($array[$i % 7], 4030) . \"\\n\";\n        //$serv->send($fd, $_str);\n        $n_bytes += strlen($_str);\n        $data .= $_str;\n    }\n    echo \"send \" . $n_bytes . \" bytes\\n\";\n    $serv->send( $serv->array['fd'], $data);\n    $serv->close($fd);\n});\n$serv->on('close', function ($serv, $fd, $reactor_id) {\n    echo \"[#\".posix_getpid().\"]\\tClient@[$fd:$reactor_id]: Close.\\n\";\n});\n$serv->start();\n"
  },
  {
    "path": "examples/server/sendfile.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"0.0.0.0\", 9501, SWOOLE_BASE);\n$serv->set(array(\n    'worker_num' => 1,\n));\n$serv->on('timer', function($serv, $interval) {\n\techo \"onTimer: $interval\\n\";\n});\n$serv->on('workerStart', function($serv, $worker_id) {\n\t//if($worker_id == 0) $serv->addtimer(300);\n});\n$serv->on('connect', function (Swoole\\Server $serv, $fd){\n\t$serv->send($fd, filesize(__DIR__.'/test.jpg'));\n    //echo \"Client:Connect.\\n\";\n});\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n    echo \"Client[$fd]: $data\\n\";\n    $serv->sendfile($fd, __DIR__.'/test.jpg');\n    //$serv->close($fd);\n});\n$serv->on('close', function ($serv, $fd) {\n    //echo \"Client: Close.\\n\";\n});\n$serv->start();\n"
  },
  {
    "path": "examples/server/single.php",
    "content": "<?php\n//single process single thread\n$server = new Swoole\\Server('127.0.0.1', 9501, SWOOLE_BASE);\n\n$server->on('Receive', function($serv, $fd, $reactor_id, $data) {\n\t$serv->send($fd, \"Swoole: $data\");\n});\n\n$server->start();\n"
  },
  {
    "path": "examples/server/tcp_client.php",
    "content": "<?php\n$client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n\nif (!$client->connect('127.0.0.1', 9504)) {\n    exit(\"connect failed\\n\");\n}\n\n$func = \"send_test\" . intval(empty($argv[1]) ? 3 : $argv[1]);\n\nfor ($l = 0; $l < 1; $l++) {\n    $data = '';\n    for ($i = 0; $i < 10; $i++) {\n        $len = rand(100000, 200000);\n        echo \"send : \" . ($len + 4) . \"\\n\";\n        send_test3($client, $len);\n    }\n}\n\nfunction send_test3($client, $len)\n{\n    $data = pack('N', $len + 4);\n    $data .= str_repeat('A', $len) . rand(1000, 9999);\n    $chunks = str_split($data, 4000);\n    foreach ($chunks as $ch) {\n        $client->send($ch);\n    }\n//    $data = $client->recv();\n//    echo \"recv : \" . strlen($data) . \"\\n\";\n}\n"
  },
  {
    "path": "examples/server/tcp_server.php",
    "content": "<?php\n$serv = new SocketServer();\n$serv->run('0.0.0.0', 9504);\n\nclass SocketServer\n{\n    protected $serv; //swoole server\n\n    const MAX_PACKAGE_LEN = 8000000; //max data accept\n\n    function run($host, $port)\n    {\n        $this->serv = new Swoole\\Server($host, $port, SWOOLE_BASE);\n\n        $this->serv->set(array(\n            'enable_coroutine' => false,\n            'worker_num' => 1, //how much worker will start\n        ));\n\n        $this->serv->on('receive', array($this, 'onReceive'));\n        $this->serv->start();\n    }\n\n    function onReceive($serv, $fd, $tid, $data)\n    {\n        echo \"recv \" . strlen($data) . \" bytes\\n\";\n//        $packet = substr($data, 4);\n//        $result = array(\n//            \"code\" => \"0\",\n//            \"msg\" => \"ok\",\n//            \"data\" => $packet,\n//        );\n//        $resp = json_encode($result);\n//        $send_data = pack('N', strlen($resp)) . $resp;\n//        echo \"send \" . strlen($send_data) . \" bytes\\n\";\n//        $serv->send($fd, $send_data);\n    }\n}\n"
  },
  {
    "path": "examples/server/trace.php",
    "content": "<?php\nfunction test()\n{\n    test_sleep();\n}\n\nfunction test_sleep()\n{\n    echo \"sleep 5\\n\";\n    sleep(5);\n}\n\n$server = new Swoole\\Server('127.0.0.1', 9501);\n\n$server->set([\n    'worker_num' => 1,\n    'task_worker_num' => 1,\n    'request_slowlog_timeout' => 1,\n    'request_slowlog_file' => '/tmp/trace.log',\n]);\n\n$server->on('Receive', function($serv, $fd, $reactor_id, $data) {\n    if (trim($data) == 'task') {\n        echo \"task\\n\";\n        $serv->task($fd);\n        return;\n    }\n    test();\n    $serv->send($fd, \"Swoole: $data\");\n});\n\n$server->on('Task', function (Swoole\\Server $serv, $task_id, $reactor_id, $data) {\n    echo \"#{$serv->worker_id}\\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=\".strlen($data).\".\".PHP_EOL;\n    test();\n    $serv->send($data, \"Swoole: task\\n\");\n});\n\n$server->on('Finish', function (Swoole\\Server $serv, $task_id, $data) {\n    echo \"Task#$task_id finished, data_len=\".strlen($data).PHP_EOL;\n});\n\n$server->start();\n"
  },
  {
    "path": "examples/server/uid_dispatch.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"0.0.0.0\", 9501);\n$serv->fdlist = [];\n$serv->set(array(\n\t\t//'tcp_defer_accept' => 5,\n\t\t//'ipc_mode' => 2,\n\t\t'worker_num' => 4,\n\t\t//'task_worker_num' => 2,\n\t\t'dispatch_mode' => 5,   //uid dispatch\n\t\t//'max_request' => 1000,\n\t\t//'daemonize' => true,\n\t\t//'log_file' => '/tmp/swoole.log'\n));\n$serv->on('timer', function($serv, $interval) {\n\techo \"onTimer: $interval\\n\";\n});\n\n$serv->on('start', function($serv) {\n\t//$serv->addtimer(1000);\n});\n\n$serv->on('workerStart', function($serv, $worker_id) {\n\techo \"{$worker_id} start\".PHP_EOL;\n\t//if($worker_id == 0) $serv->addtimer(1000);\n});\n\n$serv->on('connect', function ($serv, $fd, $reactor_id){\n\t//echo \"[#\".posix_getpid().\"]\\tClient@[$fd:$reactor_id]: Connect.\\n\";\n\techo \"{$fd} connect, worker:\".$serv->worker_id.PHP_EOL;\n});\n\n$serv->on('task', function ($serv, $task_id, $reactor_id, $data){\n});\n\n$serv->on('finish', function ($serv, $fd, $reactor_id){\n\n});\n\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n    $conn = $serv->connection_info($fd);\n    print_r($conn);\n    echo \"worker_id: \" . $serv->worker_id . PHP_EOL;\n    if (empty($conn['uid'])) {\n        $uid = $fd + 1;\n        if ($serv->bind($fd, $uid)) {\n            $serv->send($fd, \"bind {$uid} success\");\n        }\n    } else {\n        if (empty($serv->fdlist[$fd])) {\n            $serv->fdlist[$fd] = $conn['uid'];\n        }\n        print_r($serv->fdlist);\n        foreach ($serv->fdlist as $_fd => $uid) {\n            $serv->send($_fd, \"{$fd} say:\" . $data . PHP_EOL);\n        }\n    }\n});\n\n$serv->on('close', function ($serv, $fd, $reactor_id) {\n\t//echo \"[#\".posix_getpid().\"]\\tClient@[$fd:$reactor_id]: Close.\\n\";\n\tunset($serv->fdlist[$fd]);\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/server/unix_stream.php",
    "content": "<?php\n$serv = new Swoole\\Server(__DIR__.\"/svr.sock\", 0, SWOOLE_PROCESS, SWOOLE_UNIX_STREAM);\n$serv->set(array(\n    'worker_num' => 1,\n));\n$serv->on('connect', function ($serv, $fd, $reactor_id){\n    echo \"[#\".posix_getpid().\"]\\tClient@[$fd:$reactor_id]: Connect.\\n\";\n});\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n    echo \"[#\".posix_getpid().\"]\\tClient[$fd]: $data\\n\";\n    $serv->send($fd, json_encode(array(\"hello\" => '1213', \"bat\" => \"ab\")));\n    //$serv->close($fd);\n});\n$serv->on('close', function ($serv, $fd, $reactor_id) {\n    echo \"[#\".posix_getpid().\"]\\tClient@[$fd:$reactor_id]: Close.\\n\";\n});\n$serv->start();\n"
  },
  {
    "path": "examples/server/zmq.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"0.0.0.0\", 9501);\n\n$context = new ZMQContext();\n\n$sender = new ZMQSocket($context, ZMQ::SOCKET_PUSH);\n$sender->bind(\"tcp://*:5557\");\n\n$receiver = new ZMQSocket($context, ZMQ::SOCKET_PULL);\n$receiver->bind(\"tcp://*:5558\");\n\nfunction onZMQR()\n{\n\tglobal $receiver;\n\t$string = $receiver->recv();\n\techo $string, PHP_EOL;\n}\n\n$serv->set(array(\n\t//'tcp_defer_accept' => 5,\n\t'worker_num' => 1,\n\t'reactor_num' => 1,\n\t//'daemonize' => true,\n\t//'log_file' => '/tmp/swoole.log'\n));\n\n$serv->on('workerStart', function($serv, $worker_id) {\n\tglobal $sender;\n    global $receiver;\n\n    $rfd = $receiver->getsockopt(ZMQ::SOCKOPT_FD);\n    Swoole\\Event::add($rfd, 'onZMQR', NULL , SWOOLE_EVENT_READ);\n    echo \"worker start\\n\";\n});\n\n$serv->on('connect', function ($serv, $fd, $reactor_id){\n    echo \"[#\".posix_getpid().\"]\\tClient@[$fd:$reactor_id]: Connect.\\n\";\n});\n\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n\n    $cmd = trim($data);\n    echo \"[#\".posix_getpid().\"]\\tClient[$fd]: $data\\n\";\n\n    if($cmd == \"zmqtest\")\n    {\n        echo 'aaaaaaaaaaaa'. PHP_EOL;\n        $sender->send(\"msg to zmq\");\n    }\n    $serv->send($fd, 'OK'.PHP_EOL);\n    //$serv->close($fd);\n});\n\n$serv->on('close', function ($serv, $fd, $reactor_id) {\n    echo \"[#\".posix_getpid().\"]\\tClient@[$fd:$reactor_id]: Close.\\n\";\n});\n\n//$serv->start();\n"
  },
  {
    "path": "examples/socket_coro/client.php",
    "content": "<?php\n$socket = new Co\\Socket(AF_INET, SOCK_STREAM, 0);\n\ngo(function () use ($socket) {\n    $retval = $socket->connect('localhost', 9601);\n    while ($retval)\n    {\n        $n = $socket->send(\"hello\");\n        var_dump($n);\n\n        $data = $socket->recv();\n        var_dump($data);\n\n        if (empty($data)) {\n            $socket->close();\n            break;\n        }\n        co::sleep(1.0);\n    }\n    var_dump($retval, $socket->errCode);\n});\n"
  },
  {
    "path": "examples/socket_coro/server.php",
    "content": "<?php\n$socket = new Co\\Socket(AF_INET, SOCK_STREAM, 0);\n$socket->bind('127.0.0.1', 9601);\n$socket->listen(128);\n\ngo(function () use ($socket) {\n    while(true) {\n        echo \"Accept: \\n\";\n        $client = $socket->accept();\n\n        echo \"New Coroutine: \\n\";\n        go(function () use ($client) {\n            while(true) {\n                echo \"Client Recv: \\n\";\n                $data = $client->recv();\n                if (empty($data)) {\n                    $client->close();\n                    break;\n                }\n                var_dump($client->getsockname());\n                var_dump($client->getpeername());\n                echo \"Client Send: \\n\";\n                $client->send(\"Server: $data\");\n            }\n        });\n    }\n});\n"
  },
  {
    "path": "examples/socket_coro/udp.php",
    "content": "<?php\n//Server\ngo(function () {\n    $socket = new Co\\Socket(AF_INET, SOCK_DGRAM, 0);\n    $socket->bind('127.0.0.1', 9601);\n    while (true) {\n        $peer = null;\n        $data = $socket->recvfrom($peer);\n        echo \"[Server] recvfrom[{$peer['address']}:{$peer['port']}] : $data\\n\";\n        $socket->sendto($peer['address'], $peer['port'], \"Swoole: $data\");\n    }\n});\n\n//Client\ngo(function () {\n    $socket = new  Co\\Socket(AF_INET, SOCK_DGRAM, 0);\n    $i = 0;\n    while (true)\n    {\n        $socket->sendto('127.0.0.1', 9601, \"HELO-\" . $i++);\n        $peer = null;\n        $data = $socket->recvfrom($peer);\n        echo \"[Client] recvfrom[{$peer['address']}:{$peer['port']}] : $data\\n\";\n        co::sleep(1);\n    }\n});\n"
  },
  {
    "path": "examples/ssl/ca/ca-cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDKDCCAhACCQCEAJc680CvRDANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD\nTjELMAkGA1UECAwCQkoxCzAJBgNVBAcMAkJKMQ8wDQYDVQQKDAZzd29vbGUxDzAN\nBgNVBAsMBnN3b29sZTELMAkGA1UEAwwCQ0EwHhcNMTgxMjA3MDc0OTA1WhcNMjgx\nMjA0MDc0OTA1WjBWMQswCQYDVQQGEwJDTjELMAkGA1UECAwCQkoxCzAJBgNVBAcM\nAkJKMQ8wDQYDVQQKDAZzd29vbGUxDzANBgNVBAsMBnN3b29sZTELMAkGA1UEAwwC\nQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN8IQ468DyDRIIMSx4\nv2Apmrbe1vIHYNOfW7DexM+DK8TX46l3v7SsLCHCYrKmEnl2SoDyjkDBQukrZRwQ\nlZ2VaNmllK3A3HQVl2tdFWHkLz/Qdq5bLnUEz1/tI33DcjJ8fo70wpz6pAgEsHZt\n+AX1MbZVWJDbMpH8SHaJduHsOD1WQo0qiJF1Z6PdrP9SPD2Z9g56b/TKA7BU9sd+\nN7QsChqQHFWjsyC80Hl6f2c+YaYjd4/KbunzIvhFIuK/bOGB0U+Cu2I8Fq4yGHFA\nF/i/+Rthabc9jxWsbbAMFB2b/TkDhuFrjH+J7Bbqd8cZdUshuQv6l3wU4m5bv5Zm\n7s1rAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABkr7mqcRBJEBG+argNC0n+IY6Df\nkd3YRgNhAdedUfyrG6C+SwaFiAkhgZQH8NuHEzANAnLZe8DlqYEsmWJGRT7jy0Pa\nI8a8PA8PajSAg3tp6wR7ST2E4ZiQwjJsDxaFI+yDMiP5uXyHEA9WHQZ9OiWRDO/9\nX8dJw/uuEk4dXdpyqTfNleHN/5TGJP07qrPSRr0+1+HVw1YZjgKsn7ivgAoaNxEe\nfDnhoF8WncswJLP3e5D0EG6zrod/8r6H009CJWGadaGWEtcvOoGq6E97mCwRrKmI\n25Fr/izhV9gvf/Uj+nDZfvJwdx7GjxoHM+D3uc/2D8AxRkAtIPcABTyvPvI=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "examples/ssl/ca/ca-key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAzfCEOOvA8g0SCDEseL9gKZq23tbyB2DTn1uw3sTPgyvE1+Op\nd7+0rCwhwmKyphJ5dkqA8o5AwULpK2UcEJWdlWjZpZStwNx0FZdrXRVh5C8/0Hau\nWy51BM9f7SN9w3IyfH6O9MKc+qQIBLB2bfgF9TG2VViQ2zKR/Eh2iXbh7Dg9VkKN\nKoiRdWej3az/Ujw9mfYOem/0ygOwVPbHfje0LAoakBxVo7MgvNB5en9nPmGmI3eP\nym7p8yL4RSLiv2zhgdFPgrtiPBauMhhxQBf4v/kbYWm3PY8VrG2wDBQdm/05A4bh\na4x/iewW6nfHGXVLIbkL+pd8FOJuW7+WZu7NawIDAQABAoIBAG8NrW+4ikK9E6VB\nRzcSFHw7BzRh+wBJkbUUz+tBfJnCp5K4HTILOhXCcq4NZUMzITbN9H1Ygsj1GSIi\nkyDKXcEtWnj1FUG0YBEop2f52OEhLatv5JQdGFNwtMz/X5le4qTJ5rNMozRx8jT9\nJ9AjeKsBlM6vROY/fgPq26lDZ49mx9Rp8tIV4lps9cZ4YrsMu47tXTFjK2dYyVdh\nNbMNE1tgfIQd+ZH5HpLcEZas72y7djRC3UNRqlHJ6CvP1Kb98+ILa+CLj94mN+NV\nVvoqzLHLk6n7nam2FKVHMfHl0fSmQ1TQmI3dyyh62I7wpYAPi2Sk+s8UQvjRjKwI\nLCJkGZECgYEA+8DLpKnIvWKf864nknEt6XF9gV/Ft0Mld61rzkTJy5zVjD9FXqJl\nF8Cfn6RfSb1dw+wqXpoJd2R+WRTI60jCe9tfY8G7Kpmz6jV+At4jQ932QQyNRJnG\nyVYn8wz4XsPGgfz0OaFDZvIlIcQIEfkEOaow019+sU6K9S6oZsILvHMCgYEA0Wnf\nmZFlw6C9cbSb1f1Nl0YGm1NLMJuIcYudRx8OfcbNSlRFCQrVIjNcjIpnNE1ltd89\nPrRZJnkkX0lxkg9jPFqSeYcvhAksZrhO0a7ZeKHyH7F2c6E6LIqwZnoZ3tRUWzm+\nM7L5Bh6TS5G0/Y0tPB3UngRy6SWmkjTI1kCrJSkCgYEA+buHh4GRCyUxjHXe9Wrm\nY2NcX6EUrbWjDjPu9D/SefPK9oOxGa44YDaJYcCZcbmysO6uHQfqihAbMdznblbP\n7jNwTbHZK+oqyhNkPA4Fp4YgiOidnkZ5JjIcEQN1wfOtEDdQQXbSOOXNpdAjPUjr\nQxSruJJLSDGksJEnJkApijcCgYAvl24XinGkKe2j37XixfRSQrnRpvZZj59MnrwZ\no7c5YmrZT5l42pthcGbCEdouisBoutlCXN493h4kAZ7r4M9esf7D27MywJr0pUJU\naPZHchaCmWQgFy4PXV2FjI5Ak+Sv7smJGk151I0JCY4maWU0WlstpjkvPz1B0Pkr\np/q9MQKBgQDjVs5yfWb8PEwlw5oqkk8IyNVHKMz/62r7tfuT1gGr9OepWXv900fn\nZ7TavI6tr0x3zCblqTSDROUYYV6sadMoCMrHQ6yihwJNM4Htp2Qi7tJJs1onC8wE\np2gXL1fPFq0sjSaRcZhaxSrpF2KAfIPmoC17Li30A2iVe2FyLIQZvg==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "examples/ssl/ca/ca-req.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIICmzCCAYMCAQAwVjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkJKMQswCQYDVQQH\nDAJCSjEPMA0GA1UECgwGc3dvb2xlMQ8wDQYDVQQLDAZzd29vbGUxCzAJBgNVBAMM\nAkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzfCEOOvA8g0SCDEs\neL9gKZq23tbyB2DTn1uw3sTPgyvE1+Opd7+0rCwhwmKyphJ5dkqA8o5AwULpK2Uc\nEJWdlWjZpZStwNx0FZdrXRVh5C8/0HauWy51BM9f7SN9w3IyfH6O9MKc+qQIBLB2\nbfgF9TG2VViQ2zKR/Eh2iXbh7Dg9VkKNKoiRdWej3az/Ujw9mfYOem/0ygOwVPbH\nfje0LAoakBxVo7MgvNB5en9nPmGmI3ePym7p8yL4RSLiv2zhgdFPgrtiPBauMhhx\nQBf4v/kbYWm3PY8VrG2wDBQdm/05A4bha4x/iewW6nfHGXVLIbkL+pd8FOJuW7+W\nZu7NawIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAMs2wMLBDqw2TxBy2wIM2ytp\nLupk/3QVkvMpBIiYD7Jbod5YRjkV+LagLq/KE10y0jWSGuQSpbJHypb/bXsvKcU7\n18uzM/CU08xz5WWQ7LqAvITT/4GHT/WNLn38WBkIG6qHYeaaaLcGdo53KlbQiYqq\n5An3sxVin1C5dHR2ew5bKMNh+Zfq8kNXIPQbKd2KcEDKPYvt7Zb92SOijpkK/SVb\nhWgHkGsyFothmIpdKarOsQ2E2SSyTtNUKws/ZcRq1KQs7qAxNT7As4vBAfCGv0Yv\ns7gohLXDPZDWvoXmRWOPiY7nYlTzxBDKXEwtEqN0ZJcQv9OHJveQ6SSK3o75EDM=\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "examples/ssl/ca/client-cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDKDCCAhACCQCRX5mTEHplWDANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD\nTjELMAkGA1UECAwCQkoxCzAJBgNVBAcMAkJKMQ8wDQYDVQQKDAZzd29vbGUxDzAN\nBgNVBAsMBnN3b29sZTELMAkGA1UEAwwCQ0EwHhcNMTgxMjA3MDc1OTQzWhcNMjgx\nMjA0MDc1OTQzWjBWMQswCQYDVQQGEwJDTjELMAkGA1UECAwCQkoxCzAJBgNVBAcM\nAkJKMQ8wDQYDVQQKDAZzd29vbGUxDzANBgNVBAsMBnN3b29sZTELMAkGA1UEAwwC\nY24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDATA3sGW041+GHub7\nGgfAA1vcZP48Gw12F2jT15mX8SMjjQ8AutdnxG3OMt5lzgpw5+SVUgxK0nIdGg0f\nO0Rseh3VKYl7F2neUWzETfLo3qywVsu21fJiuxwtS8h+r/pM/0/drC20qbKzDEDT\nQ4hwVsCMjx0CUxZoFgiHBRhyIT6DqBdt8fylSfC0ocvLXI32Hl75QEhjrSqZqvHY\nz7cT5+YzuL/SQ5QOEtCGQarExhgqgQEtpD5ZGhbalDhrZDI/ZwRnA27I54s0VCTY\nZT6XNASf1UUH6xqV9IT6lY2GQ9rNkzKVjaG5/gJDcKPO+sBLsjzgsW4qJ8GiLPLY\ntyIZAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAICx9BbAwat+nF3Tcw3y4XyX0fpb\nJ3YXAyIESNKEh1F6IRVdPkcHTMd2YU583pGD38UJzWOVj92bBfRRXUbINnCoqM30\nQHiBfg0kQhc9UNKY+62urhSfUs4HGiyrRVwVbSDbqZpzg5MXa+msgktWMDL1KL8d\ngljnqn/Rad/hYKgjIGJ0MIk55qKl6z6tlVwIIPkdEm6FFcRhejLgkCpDC4WBwbmi\nU1hpSQfoRXICVyO96mL+/wpekZTAPFZB2LOaZOkT4/hGhlNVFZfcgws+hTh11EZb\nwfpgVYxk/T9MfgEyZGBtarUaNR/xobdnABo5eyxXrrd0RU7SjIrIRA5ORxE=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "examples/ssl/ca/client-key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAwwEwN7BltONfhh7m+xoHwANb3GT+PBsNdhdo09eZl/EjI40P\nALrXZ8RtzjLeZc4KcOfklVIMStJyHRoNHztEbHod1SmJexdp3lFsxE3y6N6ssFbL\nttXyYrscLUvIfq/6TP9P3awttKmyswxA00OIcFbAjI8dAlMWaBYIhwUYciE+g6gX\nbfH8pUnwtKHLy1yN9h5e+UBIY60qmarx2M+3E+fmM7i/0kOUDhLQhkGqxMYYKoEB\nLaQ+WRoW2pQ4a2QyP2cEZwNuyOeLNFQk2GU+lzQEn9VFB+salfSE+pWNhkPazZMy\nlY2huf4CQ3CjzvrAS7I84LFuKifBoizy2LciGQIDAQABAoIBAAUtZuZTnJ+s2cBp\n2aAZFNOYQzwawlU+MqFMBdB6DO3nzRMIj7lpPIbYxEGRQC5bxhMxVPRR9VmXNMhp\nBKgrnqlypJAgRjIqcPkiGE0t9KESKGuWIlp8W68bYKgjNvOGIJMixDASfjQpvUDb\ntmzlgh3Zb8tLkW2aXyHsU9Wh4QBGrcW7faiu4CcC1kHJk5FUg6eSxN0vhZxCQgKP\n6i4aHPOKoLu3jI36EGERTiFeDDUhjFynRLYg8gfaQc93gzuZGZEMjtlmiTUPMrKn\n9O15Y5RIDFDQSRDngNbc60ieIJ1piqzHfL6X8BQxK+Pd/J/3SSfFuXGGfISYahGS\n67SzHAECgYEA831stnQB0R6AP5UGuJDnknjgmkH9C4a0EpV3YFb8CKdLN7x3mU3p\nwLPk0bpjObW+PHmUNZWsrcnKtuw+5yVFefO+bZp8KdFptcFI2cgEsLm/PIoclZyK\ndBE0s2LlCqg/XNYImTYamsRD8RO0DYoT3yFK0/oQJUdyejBenOXjq7kCgYEAzQYL\nzKW0BP794eCo//fxmcjNTKrY5TVErWBQS8RI7uqdOsYSnlkBM/62pYnuGX3//Ol5\n1fsuRbPBTNhdKMxAF+fjdGCgx6yxEcrt7d6aEp8hIDvix/YuXvYSe3HRSULsWboE\nyLxVNW7GaeXltcLa8chJzjv5QowPhFtMFnIeGWECgYASyc3WslLlkQXyYgx3t263\nBa7HGGpvNkC04mWowG2IOWh7b4aHBNURso/ogwpfDKAWSIdgLyF484Y54TblXSHk\nTCOYj4AfNfKDPNvjOiTqghq4kCuue7rPQ/ieEvZ7gQoKntVhBSS+ZoCbnJqbJNlt\nsUL14mjWh5HUzeCvGKoduQKBgQDAndSKEId/Rh05j/rWeJfoNgoC5GPfe1spU9o8\nRI2MwKi05g/p6o7BzaTFAz/JfWH5t1P6oABKBeYuDvv+712r1/UOihWjYm/82l0M\nZFz178CMaxbVbEkGeELG7RiFrrUkWypRePbIu2j3ZsCYMhfGnbgRZLD6P2H4pFlo\noQ/8IQKBgGicAkmrhW1GLtEXpUaJDW03M59DQDkJzCJwp2ah/BCOuo0yNt3MywpM\nFPLay8ZGapGrg2vZKd713w540zrQYLNFueP8IobxHRHYvpQM+DTiNr8vHX5ISyH2\nRhc1ZZklKakTefKwTdySi212GFgdV7nCHXvurQL7hvlusWlie5ih\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "examples/ssl/ca/client-req.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIICmzCCAYMCAQAwVjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkJKMQswCQYDVQQH\nDAJCSjEPMA0GA1UECgwGc3dvb2xlMQ8wDQYDVQQLDAZzd29vbGUxCzAJBgNVBAMM\nAmNuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwwEwN7BltONfhh7m\n+xoHwANb3GT+PBsNdhdo09eZl/EjI40PALrXZ8RtzjLeZc4KcOfklVIMStJyHRoN\nHztEbHod1SmJexdp3lFsxE3y6N6ssFbLttXyYrscLUvIfq/6TP9P3awttKmyswxA\n00OIcFbAjI8dAlMWaBYIhwUYciE+g6gXbfH8pUnwtKHLy1yN9h5e+UBIY60qmarx\n2M+3E+fmM7i/0kOUDhLQhkGqxMYYKoEBLaQ+WRoW2pQ4a2QyP2cEZwNuyOeLNFQk\n2GU+lzQEn9VFB+salfSE+pWNhkPazZMylY2huf4CQ3CjzvrAS7I84LFuKifBoizy\n2LciGQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBABGKoAa56G5lDWbZEByBiKjJ\nLf7E74z8CSFLWKd4ytbS/qBJJndMz4oRsVO2etu4XlmRuZTrt1gJ1QXbQHm5b7b2\nRx8CgE2ZGTfWIMc6B111z6Rl5ahZRIt7KEduC4+hsEHeim7eoQNkWRz6XU+fBWYA\nIAIjteH22Id0LnQuEXPcxvhBycZwcxFCaLKbimltgZskXkNpe1FCYbU3ri9ZTt8H\n5MmQOPxyBr5aSWgmeJjLtrgGiH3hOkS494sYAxG+yrkKObDEa09xdP5+vS5Y8rDF\n2WODi1xjIZEypAYoDk+UrI6JY6oaSEdZitw4sLew7+kopMXBZcEbsyVuGfA9Iag=\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "examples/ssl/ca/client.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIICKTCCAZICCQC8hk8d+ohSHDANBgkqhkiG9w0BAQUFADBXMQswCQYDVQQGEwJD\nTjERMA8GA1UECBMIWmhlamlhbmcxETAPBgNVBAcTCEhhbmd6aG91MQ4wDAYDVQQK\nEwVNeSBDQTESMBAGA1UEAxMJbG9jYWxob3N0MB4XDTE3MDIwOTA3NTMxMloXDTE3\nMDMxMTA3NTMxMlowWzELMAkGA1UEBhMCQ04xETAPBgNVBAgTCFpoZWppYW5nMREw\nDwYDVQQHEwhIYW5nemhvdTESMBAGA1UEChMJTXkgQ2xpZW50MRIwEAYDVQQDEwls\nb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJEUnBzXTTiyUmDb\nyhkQoQ/yH1zTnuIk5Meg1Bp0fp1l4kwiizdPbZkk4YkTT/HXdTE6822Cqho+CwGE\nVqWZyyd2AZmj87OGb4ZRCyyFzzjfEwdCTvyqZSUBoc1gvSGdEiaA4mXE87Y0XcMB\nBasOrfmO76nuzyaXLT7xDjrB+Qw5AgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAC09q\nKOuSbAW4Zt4I3CZOh3j+bMax++6M37RWCpShCTdaapdm37y436QbtuB9K6ry7MLo\n0HcqQksDm8tLmQqEenfhqZo10FiQj0v1ckvg3lzH4OIP5IM0zXkApnlX6aKuOBbC\nXMkYSqdwK0A8QNrl051RCKE2CaYK3cnSN0z4+Vs=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "examples/ssl/ca/client.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIICWwIBAAKBgQCRFJwc1004slJg28oZEKEP8h9c057iJOTHoNQadH6dZeJMIos3\nT22ZJOGJE0/x13UxOvNtgqoaPgsBhFalmcsndgGZo/Ozhm+GUQsshc843xMHQk78\nqmUlAaHNYL0hnRImgOJlxPO2NF3DAQWrDq35ju+p7s8mly0+8Q46wfkMOQIDAQAB\nAoGAarOFvZB7st8zxxjfImAglOG2P0dE633G5Stb07kqBgkQzn35dcxtBt0hIveZ\nLH0SLAr3Tetzv6kx3wO91j2uM1QURztULIcFaDrQyrBbAYoka2WDoxJCSRoGvb9X\n7JoyuYtYvbctT8dvYF9mVttq/YdAjFfs7RHwMGSZUc9xkCECQQDA1NNdmsupDZrG\ne6sBToEcNuLd/ahB77AS19dWt9WLGSDhG+/wj2bx9l73RTnadenjIPoRaY0RZu4u\nfDNlKYErAkEAwJtQn4PSLZe3Rx/FfrGn4pxLFvL/6EgaPKxY1nNd55JY3x15L9A9\nIjODD+CH+BYbYufHfI5n27QIVRDIpNAOKwJAb/2q3BxA1+fs0gWU5WdgmLBPxjnB\ndLnt+qOcjuKphOWNMO/2xDGkyjYaJWXxGa2NrrnCQkaZBVhQUHMVrlUSjQJAeUhT\n+F5VlwgWDN9gyWqtQPESB510r5vXiaUtO7zhwNRSygwRJ56FIGg3e2PzurCRBjLV\nVwWFOL+hD4/GCKJKiQJAfQbp5pZ1Fni/YWo2gmuQi+9kMv3BKfpeHwNclPICY86c\nJSAo2+e7Xwz+GHxW9Hqpuz4J1CKjFGS0VzZAFWBi2w==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "examples/ssl/ca/server-cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDJzCCAg8CCQCRX5mTEHplVjANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD\nTjELMAkGA1UECAwCQkoxCzAJBgNVBAcMAkJKMQ8wDQYDVQQKDAZzd29vbGUxDzAN\nBgNVBAsMBnN3b29sZTELMAkGA1UEAwwCQ0EwHhcNMTgxMjA3MDc1NjUxWhcNMjgx\nMjA0MDc1NjUxWjBVMQswCQYDVQQGEwJDTjELMAkGA1UECAwCQkoxCzAJBgNVBAcM\nAkJKMQ8wDQYDVQQKDAZzd29vbGUxDzANBgNVBAsMBnN3b29sZTEKMAgGA1UEAwwB\nKjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANQSsIppKx1KEIZSyPlq\nVNA8NQJ04xsV0fUlQzehwAXTpM2Km9AMXKfLw3b4Z2dIE+O4HTc+2EpIo+oMdEeA\nTn1atAAqO5XDfrYfmnRdDCcZg7wxqdCJHh6OHNXv2K58OnmoyZBh5gPoR9LQuIUF\nC4p9TyE+qUxSwrzast2XkoX5JH+p6uKE/a7eDUMu8Dlx6E1Zs9aTNyyTBO3onK0N\niCZrguuOJDUnjSpSS4P8U74Wf4k7brl70QWS6KAXWC+12VSIKivJA8SZDIeQqgO+\nji5K1adQIxQR3g17QHOq8ZmrzwUWE+XdDCwTv7dkgcnt8yR4R3+fc9tMugqSS53/\nlDMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAyQZRigqe+J/jGj1b08jAci4Dfu6k\n4v52Hb0wvHvXi/C8ccL+g9f/9sLVKP5gdYsZVSf38wPNh9xTNBzbgKwUkXO37yHY\n8vXSt/coBkhvXAjQT4ICcKdH683mfD5Vuyq7m8QP6EbqvCo0s0of1I97j0VV3xf/\nSiSot3F++5jqaReLiDU4yjhSiACuZzyMBmqZml3jGEUg4s8+ruoj9t2PoeQv9fi8\nLHC+GnICEKCgg55SoXGKtsBdM1sewIdy5KHvMet9v00PkVBFFNDSt9wDugttjPCZ\nRbonE46/2/+qpGG+nVU3jIfUjAoHvXy30w1dBwTAemUC0yeN+xjl3OHMjA==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "examples/ssl/ca/server-key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEA1BKwimkrHUoQhlLI+WpU0Dw1AnTjGxXR9SVDN6HABdOkzYqb\n0Axcp8vDdvhnZ0gT47gdNz7YSkij6gx0R4BOfVq0ACo7lcN+th+adF0MJxmDvDGp\n0IkeHo4c1e/Yrnw6eajJkGHmA+hH0tC4hQULin1PIT6pTFLCvNqy3ZeShfkkf6nq\n4oT9rt4NQy7wOXHoTVmz1pM3LJME7eicrQ2IJmuC644kNSeNKlJLg/xTvhZ/iTtu\nuXvRBZLooBdYL7XZVIgqK8kDxJkMh5CqA76OLkrVp1AjFBHeDXtAc6rxmavPBRYT\n5d0MLBO/t2SBye3zJHhHf59z20y6CpJLnf+UMwIDAQABAoIBABsaqGWIO3Jq3OHO\nAtGdvWk/yKafucbkawkh0g1yJtCX1CBQ7skQS2dGq5yw8sZIeAJKrbE53vrffdOT\n8+iPhXiP9yFuRF9HtRIhx+PHYJ4+rI+I6WM1oxvUnerZ1J+4UODrqZdCa9tj/In9\nExgtJOR2T364O3r9uNXaPbIEhT2J9m6+3/7+hk1Cux7FHYLQmPnSSnxNmtuugwx5\naA4Wbytf3zFWG+1sDZQI4fgx80IBQCOVZPw1j8WzByQJko5Y0hYeEpVhXhU2AT/O\nb0Jo8w+vP/Z9N6UOLH3TCuLNKckeG5Bem/yh3NtldJztnt+rOPO4kL33d6GONbfl\nLAKTuoECgYEA9xlpIna96ZT9Cot2vgeLUIK/xyMU9WmPNRavx3akz50AxoU3ep4p\nFwv1TarXn3pfaeAV34qzM4vxkPGQ7QF8j/qLrvjpXWQwfwiRqrC1T3S8J8FZegGc\n11ZzTURICniOtgNn5Ai/RM3y2tR+rOC2gTUpB7UUAW1cOGxZ/xNMPksCgYEA27ZK\nHNdoDClAW22gWRLn8bJLjOWdVcWpoI8zULRgfcyvm9ffbRaguRx0R+a3TbHZvT7c\nbxppYTn4+RXuIhM5hI4PxqVtW9hWQvzta9wy44EBXUebwq0NjVEV3X1iB3T7s5UN\naXf0p+jwPJeFYRjU46nSqHxLtCu+Z4N67itZsLkCgYBGmLf05L/1kF+LN7VBLZ6b\n7f8kwd0nEHWd0VF/R2Jm/pBOJ5lmKvHKZ9w5yGm6YpgIA8G8EECKpZzqsFmbnSUd\nbZOi9fKWgB1q7ePQRJRIky1+njsWJIfO7iAbHzsF4tPDJM311Jnw6nZMFxHqCnma\nCLokPZPtpHUImcxMmBLcaQKBgCDCsZJTXCRUElCPrBUcCJG1cCZ2JlufuYdDUvd5\nrdmUJ6spKRtvAFAZugzuje570mexc9TtL0FcD4+0gWl7TFxDgn6wM/o568LZS92F\nW+EoXze1cbg5bYfgy03XYEKUYuOIGhPb2xMeXJPsU+sCdNvteoe84NzgmOd+Z1oU\nBK+hAoGAXiQPmXfdk/hbxdiUwlClm9OGNH6Wd1jXiFRUcGLN43CYi1rRe0ziX+I0\n2TwxHu8RcUy4RLiUlOnB1aDyQu1sUHJd+rnDqSUph5ozsSUuUBuJJxm4g7dC/9kZ\nAbs7BkHltPaTAGKW9RV07XydJPLEfbDJ0WGzkxeADMRzeOlHkhQ=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "examples/ssl/ca/server-req.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIICmjCCAYICAQAwVTELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkJKMQswCQYDVQQH\nDAJCSjEPMA0GA1UECgwGc3dvb2xlMQ8wDQYDVQQLDAZzd29vbGUxCjAIBgNVBAMM\nASowggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUErCKaSsdShCGUsj5\nalTQPDUCdOMbFdH1JUM3ocAF06TNipvQDFyny8N2+GdnSBPjuB03PthKSKPqDHRH\ngE59WrQAKjuVw362H5p0XQwnGYO8ManQiR4ejhzV79iufDp5qMmQYeYD6EfS0LiF\nBQuKfU8hPqlMUsK82rLdl5KF+SR/qerihP2u3g1DLvA5cehNWbPWkzcskwTt6Jyt\nDYgma4LrjiQ1J40qUkuD/FO+Fn+JO265e9EFkuigF1gvtdlUiCoryQPEmQyHkKoD\nvo4uStWnUCMUEd4Ne0BzqvGZq88FFhPl3QwsE7+3ZIHJ7fMkeEd/n3PbTLoKkkud\n/5QzAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAU1IPD//ftlyUZAoHrYBY0fkK\nROOB/3ALET2+3QYkQJKnDFSucrvYRU8nHuGVe2aE+ej1QZ5yOzicwRoOuixkD98V\ncg+6ngol96SHXoV4j2EkXYv0w9bTVlUdTUH8/SDfnNLvzzZdRNiw9fOlt15DWgfY\nMtJi5ReH555lLQ6JU+NNzyP+zAExx9uUdDd1H8DyIPjip5xexxKbXGSgPoDnGGVS\nzJ7SkzmlGcHR3XYqQglD6poqnvxhDm4O9L6cK74RIJzUr+lDOVWhVND+6zu8vrwW\nOqM2V2qN8MNu18umKfqXCgcWYFpwhc0fJktPXi8bE2knU9vbOLJHV5w0XET4AQ==\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "examples/ssl/client.c",
    "content": "/**\n * gcc -g -o client client.c -lssl -lcrypt -lcrypto\n */\n#include <stdio.h>\n#include <stdlib.h>\n#include <errno.h>\n#include <unistd.h>\n#include <malloc.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <resolv.h>\n#include <netdb.h>\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n\n#define FAIL    -1\n\nint OpenConnection(const char *hostname, int port)\n{\n    int sd;\n    struct hostent *host;\n    struct sockaddr_in addr;\n    if ((host = gethostbyname(hostname)) == NULL)\n    {\n        printf(\"Eroor: %s\\n\", hostname);\n        perror(hostname);\n        abort();\n    }\n\n    sd = socket(PF_INET, SOCK_STREAM, 0);\n    sw_memset_zero(&addr, sizeof(addr));\n    addr.sin_family = AF_INET;\n    addr.sin_port = htons(port);\n    addr.sin_addr.s_addr = *(long*) (host->h_addr);\n\n    if (connect(sd, (struct sockaddr*) &addr, sizeof(addr)) != 0)\n    {\n        close(sd);\n        perror(hostname);\n        abort();\n    }\n    return sd;\n}\n\nSSL_CTX* InitCTX(void)\n{\n    SSL_METHOD *method;\n    SSL_CTX *ctx;\n    OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */\n    SSL_load_error_strings(); /* Bring in and register error messages */\n\n//    method = SSLv3_client_method(); /* Create new client-method instance */\n    method = TLSv1_2_client_method();\n\n    ctx = SSL_CTX_new(method); /* Create new context */\n    if (ctx == NULL)\n    {\n        ERR_print_errors_fp(stderr);\n        printf(\"Eroor: %s\\n\", stderr);\n        abort();\n    }\n    return ctx;\n}\n\nvoid ShowCerts(SSL* ssl)\n{\n    X509 *cert;\n    char *line;\n\n    cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */\n    if (cert != NULL)\n    {\n        printf(\"Server certificates:\\n\");\n        line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);\n        printf(\"Subject: %s\\n\", line);\n        free(line);\n        line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);\n        printf(\"Issuer: %s\\n\", line);\n        free(line);\n        X509_free(cert);\n    }\n    else\n        printf(\"No certificates.\\n\");\n}\n\nint main(int count, char *strings[])\n{\n    SSL_CTX *ctx;\n    int server;\n    SSL *ssl;\n    char buf[1024];\n    int bytes;\n    char *hostname, *portnum;\n    if (count != 3)\n    {\n        printf(\"usage: %s <hostname> <portnum>\\n\", strings[0]);\n        exit(0);\n    }\n\n    SSL_library_init();\n    hostname = strings[1];\n    portnum = strings[2];\n    ctx = InitCTX();\n    server = OpenConnection(hostname, sw_atoi(portnum));\n\n    ssl = SSL_new(ctx); /* create new SSL connection state */\n    SSL_set_fd(ssl, server); /* attach the socket descriptor */\n    TLSv1_2_client_method();\n    if (SSL_connect(ssl) == FAIL) /* perform the connection */\n    {\n        printf(\"Eroor: %s\\n\", stderr);\n        ERR_print_errors_fp(stderr);\n    }\n    else\n    {\n        char *msg = \"HelloWorld\";\n        printf(\"Connected with %s encryption\\n\", SSL_get_cipher(ssl));\n        ShowCerts(ssl); /* get any certs */\n        SSL_write(ssl, msg, strlen(msg)); /* encrypt & send message */\n        bytes = SSL_read(ssl, buf, sizeof(buf)); /* get reply & decrypt */\n        buf[bytes] = 0;\n        printf(\"Received: \\\"%s\\\"\\n\", buf);\n        SSL_free(ssl); /* release connection state */\n    }\n    close(server); /* close socket */\n    SSL_CTX_free(ctx); /* release context */\n    return 0;\n}\n"
  },
  {
    "path": "examples/ssl/client.php",
    "content": "<?php\n$client = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL);\n\n$client->set(array(\n    'ssl_cert_file' => __DIR__.'/ca/client-cert.pem',\n    'ssl_key_file' => __DIR__.'/ca/client-key.pem',\n//     'ssl_cert_file' => __DIR__.'/ca/client.crt',\n//     'ssl_key_file' => __DIR__.'/ca/client.key',\n    'ssl_allow_self_signed' => true,\n    'ssl_verify_peer' => true,\n\n    'ssl_cafile' => __DIR__.'/ca/ca-cert.pem',\n));\nif (!$client->connect('127.0.0.1', 9501, -1))\n{\n    exit(\"connect failed. Error: {$client->errCode}\\n\");\n}\necho \"connect ok\\n\";\n$client->send(\"hello world-\" . str_repeat('A', $i) . \"\\n\");\necho $client->recv();\n"
  },
  {
    "path": "examples/ssl/co_client.php",
    "content": "<?php\ngo(function() {\n\t$c = new Co\\Http\\Client('pro-api.coinmarketcap.com', 443, true);\n\t$c->set(['ssl_host_name' => 'pro-api.coinmarketcap.com']);\n\t$c->get('/');\n\tvar_dump($c->body, $c->headers);\n});\n"
  },
  {
    "path": "examples/ssl/gen_cert.md",
    "content": "```shell\nopenssl genrsa -out ssl.key 2048\nopenssl req -new -key ssl.key -out ssl.csr\nopenssl x509 -req -days 365 -in ssl.csr -signkey ssl.key -out ssl.crt\n```"
  },
  {
    "path": "examples/ssl/http_client.php",
    "content": "<?php\n$cli = new Swoole\\Http\\Client('127.0.0.1', 9501, false);\n$cli->setHeaders(array('User-Agent' => 'swoole-http-client'));\n\n$cli->on('close', function($_cli) {\n    echo \"connection is closed\\n\";\n});\n$cli->get('/?dump.php?corpid=ding880f44069a80bca1&corpsecret=YB1cT8FNeN7VCm3eThwDAncsmSl4Ajl_1DmckaOFmOZhTFzexLbIzq5ueH3YcHrx', function ($cli) {\n    var_dump($cli);\n    var_dump($cli->headers);\n    echo $cli->body;\n    //$cli->close();\n});\n"
  },
  {
    "path": "examples/ssl/passphrase.php",
    "content": "<?php\n$client = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL);\n$client->set(array(\n    \"ssl_key_file\" => __DIR__ . '/ssl.key',\n    \"ssl_cert_file\" => __DIR__ . '/ssl.crt',\n    'ssl_passphrase' => '5524001',\n));\nif (!$client->connect('127.0.0.1', 9501, -1))\n{\n    exit(\"connect failed. Error: {$client->errCode}\\n\");\n}\necho \"connect ok\\n\";\nsleep(1);\n\nfor ($i = 0; $i < 1000; $i++)\n{\n    $client->send(\"hello world-\" . str_repeat('A', $i) . \"\\n\");\n    echo $client->recv();\n}\nsleep(1);\n"
  },
  {
    "path": "examples/ssl/server.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"0.0.0.0\", 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n// $serv = new Swoole\\Server(\"0.0.0.0\", 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n$key_dir = dirname(dirname(__DIR__)).'/tests/ssl';\n\n// $port2 = $serv->addlistener('0.0.0.0', 9502, SWOOLE_SOCK_TCP);\n// $port2->on('receive', function($serv, $fd, $reactor_id, $data){\n//     echo \"port2: \".$data.\"\\n\";\n// });\n\n$serv->set(array(\n//\t'worker_num' => 4,\n\t'ssl_cert_file' => __DIR__.'/ca/server-cert.pem',\n\t'ssl_key_file' => __DIR__.'/ca/server-key.pem',\n    'ssl_verify_peer' => true,\n    'ssl_allow_self_signed' => true,\n    'ssl_client_cert_file' => __DIR__.'/ca/ca-cert.pem',\n    'ssl_verify_depth' => 10,\n));\n\n$serv->on('connect', function (Swoole\\Server $serv, $fd, $reactor_id){\n\techo \"[#\".posix_getpid().\"]\\tClient@[$fd:$reactor_id]: Connect.\\n\";\n    $info = $serv->getClientInfo($fd);\n    var_dump($info);\n});\n\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n\techo \"[#\".posix_getpid().\"]\\tClient[$fd]: $data\\n\";\n\t$serv->send($fd, \"Swoole: $data\\n\");\n});\n\n$serv->on('close', function ($serv, $fd, $reactor_id) {\n\techo \"[#\".posix_getpid().\"]\\tClient@[$fd:$reactor_id]: Close.\\n\";\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/ssl/ssl.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDlTCCAn0CFGDgpe31nz4rUDOQKyH6U53AWXHoMA0GCSqGSIb3DQEBCwUAMIGG\nMQswCQYDVQQGEwJjbjERMA8GA1UECAwIc2hhbmdoYWkxETAPBgNVBAcMCHNoYW5n\naGFpMQ8wDQYDVQQKDAZzd29vbGUxDzANBgNVBAsMBnN3b29sZTEOMAwGA1UEAwwF\ncmFuZ28xHzAdBgkqhkiG9w0BCQEWEHJhbmdvQHN3b29sZS5jb20wHhcNMjAwNDA2\nMTMzMjAwWhcNMjEwNDA2MTMzMjAwWjCBhjELMAkGA1UEBhMCY24xETAPBgNVBAgM\nCHNoYW5naGFpMREwDwYDVQQHDAhzaGFuZ2hhaTEPMA0GA1UECgwGc3dvb2xlMQ8w\nDQYDVQQLDAZzd29vbGUxDjAMBgNVBAMMBXJhbmdvMR8wHQYJKoZIhvcNAQkBFhBy\nYW5nb0Bzd29vbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA\ns6JM9QuOV4hQ7HlOBCp5aINATTFAYot2n/fwfWnRyrC9riUrLjdIXc3PdjBMZlyr\nMq6ZameM7RDydl0yJTq4mMOMzk8QIydk4/YkWeNRtoKee7lBCS6AUVqh/PwoeuoA\nF3f4Mf6jICC1CEF3FECjHNZEBdA46jESAm/XvKjccevduXYUcVRBERRdst7Cd13A\nOmtHUE/tgRtTMWh9NxT5vOPOO+H7Ri9g3pEiGofxroOTDqxALxHRcj0k1UkH1J74\namvw+FY+twx9kBaj+f/JkaTflDjrrf/Stc11AmfjQCdZUnW62Banps9JqyqIsEdI\nBcTMYZq5EH0xBSoQDEr4cwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAsk//RTiYF\n5Fgfu5F1DTL3lNaG6JI78oKZTFx24sM7CE+wQRLUs5WEiDFkTSCInUbfA2tdRxjB\nY4ye6fKGIL7K9yIN+3Y8ChYZBZ2xHjm4QcAKZ3TFwpxueM74sXZToaDQQooJ/5xD\nE1RTcA49waQ6zTFoG0aCeK1hjZ3ZtXaOCPLIP8yqRfkmezdKoU90LBRgXRyVR5jA\nmY+0v3Q6irZTpESY+e1RYHK5Yf8TesZ4J4LGRqSjWGP6GidpNc46wko3FOF2/KeB\nlN0jjSPx+rXCdA5hrfZIjNFjOLu/CSgOHzOflpSpYh8Jgfe9YBJRFFQI1PlcILGL\n3JmkbOAiplbA\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "examples/ssl/ssl.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIICzDCCAbQCAQAwgYYxCzAJBgNVBAYTAmNuMREwDwYDVQQIDAhzaGFuZ2hhaTER\nMA8GA1UEBwwIc2hhbmdoYWkxDzANBgNVBAoMBnN3b29sZTEPMA0GA1UECwwGc3dv\nb2xlMQ4wDAYDVQQDDAVyYW5nbzEfMB0GCSqGSIb3DQEJARYQcmFuZ29Ac3dvb2xl\nLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALOiTPULjleIUOx5\nTgQqeWiDQE0xQGKLdp/38H1p0cqwva4lKy43SF3Nz3YwTGZcqzKumWpnjO0Q8nZd\nMiU6uJjDjM5PECMnZOP2JFnjUbaCnnu5QQkugFFaofz8KHrqABd3+DH+oyAgtQhB\ndxRAoxzWRAXQOOoxEgJv17yo3HHr3bl2FHFUQREUXbLewnddwDprR1BP7YEbUzFo\nfTcU+bzjzjvh+0YvYN6RIhqH8a6Dkw6sQC8R0XI9JNVJB9Se+Gpr8PhWPrcMfZAW\no/n/yZGk35Q4663/0rXNdQJn40AnWVJ1utgWp6bPSasqiLBHSAXEzGGauRB9MQUq\nEAxK+HMCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQCxXN1YuqL06eAXPEUQNbTb\nBlZ/5r7yKpGlx1bOzdZYEveDZYTT3Mmhuy7GaX29j7TD1nxM+oVdMkN7ug/AjjY+\nybwUjWt3719kWfrIBteB0tZXqk+0pr9NDz6xxDtsDdpRxP4yhfxyZTSWX/1j2HQd\na8W8YY5FRKJJWAYqM9zZY7+LIqBA9Tub0HPQS2O6tRfbiFJiKIiGkgOd7VrUXq+D\nB9y172aIRs704wSaBbq4Q4h6xzoRcqyVBp4wHNa3opiDPcJJyiu4zHCV+vV0nSVZ\n7v9c5Cu5ttio6bHfOOWhZn64Z3wlYtQVIQ/Cj/fEujaFAvDDpdDU5Br46ZUHOmOP\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "examples/ssl/ssl.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAs6JM9QuOV4hQ7HlOBCp5aINATTFAYot2n/fwfWnRyrC9riUr\nLjdIXc3PdjBMZlyrMq6ZameM7RDydl0yJTq4mMOMzk8QIydk4/YkWeNRtoKee7lB\nCS6AUVqh/PwoeuoAF3f4Mf6jICC1CEF3FECjHNZEBdA46jESAm/XvKjccevduXYU\ncVRBERRdst7Cd13AOmtHUE/tgRtTMWh9NxT5vOPOO+H7Ri9g3pEiGofxroOTDqxA\nLxHRcj0k1UkH1J74amvw+FY+twx9kBaj+f/JkaTflDjrrf/Stc11AmfjQCdZUnW6\n2Banps9JqyqIsEdIBcTMYZq5EH0xBSoQDEr4cwIDAQABAoIBAQCfwa8pm65rIJu/\nw55jzBaPoXMVc8DsI1ZLrKkgEHV5CziWYQ4HGzlr23cviILZ4n6LI4bjf9Zwm8rX\nA8luHMxBaGCYtu4GJanvp1/q5WUtqPsj070IWmfacn1wTh3+Oo/8T0DXzbvr/cqi\n3f32cAngwppahTWo6h03N5qpeZAca4ghy9VOdNEzLmDGkR6lkrgSibpJA92LVpFH\nyHsuWvnSm1kOe2GDVQv4GOKIKH5Fo5WEkTZOocXvM1C+NNQcEP+87ZIPjoyBrPwG\nVlb1EkGHhNh0tDsNTIDuYWo/WxPsKqziczS/TB4QgR7cMePsH83e8fn5VrKSRU00\njqQEt43RAoGBANhCzG0ImvmjYSF8i53bPyuVAJfqpIMQY/BVYG82O1DVNjoK+2pU\nPwIEmX0QrltLDULxx9mV3SHzRmxnA280Y+GSIsvYqYBr7doO2cTAEP3Z+MzOiXEm\nbPAANIwt+iFp8OeMCbKh12Kr0WRhkF0oKFsLEsFT8/E/7eHwrDIF7A8pAoGBANSk\nhZ2K+4GblUEQ2vQgJc/RH9+W8NUMEX+Zv+mtEpCCEoErSjU5StcGrSMIgp2K+IGT\nyB/QdH9aYUM1iiBgXPr7IvWNF0IwxwYxJys0kagDoSDH7CdY+pANiEBgWHeHPPQW\nR+EBO9eJeEqyp/3nxouwQuhM/1UQXbJhOEmpyOo7AoGAUEqcs26ae0zZZyFihL09\n+uRbUAviAHc5Y6WPJDsyUpSWiX5CUfvtWMZ0ZianVWXMCqrR1Er7yvsTjnPApq0A\n2GL1YFGUvQVVTbxtxcq5hEZFRWT5SnVFxOWOnO0FDRrUQmOPxi6oVQ4NtAM1IiEi\nuoql7/lHfK6Ll3NPu1LPjeECgYB6mD/QjIhkOObcFHOnu8ERB2hw5/0BV2vfPS0/\n8+B4IAW7ItItzVIwllDLmeR0H+MWEbWXYf9ITTs2HG/+KpBwwsls+GMobibQkxYZ\n+rzfOPh0hc7JAKu00Zh0RRe8EeQ6dX4LY0K1CTSpHEduQ2pcrspkU8ABsuXSKerh\nzVlgcQKBgQCLCN9jSnIo5dT6N7QABjIEO9sMrnlVfoEzDxn76MwYQ6ZJxf2ONwhi\nYoOXguXxMfonleOxwAt8YbfYiT8DypryUM8cuSjB4GPJILn1W1XsNtEWBjFUeiqh\nC4MNUZis1dJrUjrieWNWp8rkGV2la1JOJYEq9SSQijeuPNFemdAvNQ==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "examples/ssl/ssl_passwd/ssl.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIICazCCAdQCCQDGckgNrF7jzDANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJD\nTjERMA8GA1UECAwIU2hhbmdIYWkxEDAOBgNVBAcMB01pbmhhbmcxDzANBgNVBAoM\nBmNoZWx1bjEUMBIGA1UEAwwLaGFudGlhbmZlbmcxHzAdBgkqhkiG9w0BCQEWEHJh\nbmdvQHN3b29sZS5jb20wHhcNMTcwNjIxMDg0NDIzWhcNMTgwNjIxMDg0NDIzWjB6\nMQswCQYDVQQGEwJDTjERMA8GA1UECAwIU2hhbmdIYWkxEDAOBgNVBAcMB01pbmhh\nbmcxDzANBgNVBAoMBmNoZWx1bjEUMBIGA1UEAwwLaGFudGlhbmZlbmcxHzAdBgkq\nhkiG9w0BCQEWEHJhbmdvQHN3b29sZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0A\nMIGJAoGBAM6DB6/k7gBq335W+46qJKOF2AJ8pJcyr8rwIculc2Guc/8Y7iAnv2gP\nNuZQWGtXuWNTgEkBc2vP2G2UbsHJRwR8fVgwFphZWmWJ1vomj9RZjm+v1ID04lIw\n2d2oR4P2Nur7mw6Lxhpgx0y0DJ+4kW4/L/ObV13fJu7fNBKuprtzAgMBAAEwDQYJ\nKoZIhvcNAQELBQADgYEAPwHwRDG+PCToqybr9GZtz3oxM6ApvVGV0c24Clon8veY\n57h78tJLVmjx/b6Y4alg4HnT+DDBRZT0hJnBhdtkZivGX7eOys9sUDLZxAWsqO7a\nbHzHkP0jVawHwqQv7WFoE83pvmUYzj2QbLMULj5PJ4LMlY3bMHYE07c0SVKeJFE=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "examples/ssl/ssl_passwd/ssl.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIIBujCCASMCAQAwejELMAkGA1UEBhMCQ04xETAPBgNVBAgMCFNoYW5nSGFpMRAw\nDgYDVQQHDAdNaW5oYW5nMQ8wDQYDVQQKDAZjaGVsdW4xFDASBgNVBAMMC2hhbnRp\nYW5mZW5nMR8wHQYJKoZIhvcNAQkBFhByYW5nb0Bzd29vbGUuY29tMIGfMA0GCSqG\nSIb3DQEBAQUAA4GNADCBiQKBgQDOgwev5O4Aat9+VvuOqiSjhdgCfKSXMq/K8CHL\npXNhrnP/GO4gJ79oDzbmUFhrV7ljU4BJAXNrz9htlG7ByUcEfH1YMBaYWVplidb6\nJo/UWY5vr9SA9OJSMNndqEeD9jbq+5sOi8YaYMdMtAyfuJFuPy/zm1dd3ybu3zQS\nrqa7cwIDAQABoAAwDQYJKoZIhvcNAQELBQADgYEASQqPl/+WGbjiFmejmI7cj5Te\nbyHjUASEGc2VDRQhc1W7XRFjHFqyeKRh8xwFV7HDFbvVwsTos260s4PKGkjTD5hH\nXG2HKPevBQRzqV0dIJmdW1+gZ13QyIESnQyj9MzGkW4I6MDHXamCZmYAFel2MXtE\nVsj7idZCTW/REX/7/z8=\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "examples/ssl/ssl_passwd/ssl.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,1B8CC4C05815AA64\n\ni9kRuvTLN6Y329ctDI1Nt0oSRUXqRJo0omtGmLFPA7C8kRs8mwhvpo4G4m8Kh6pf\nl2fVpaKKVXeQxyTdfgPKPxvGsTODp3ZHODLuKswwj0ERPrjfzsDvKboEYdooC7FF\n3oYpy33I6XtA3qytkqP/gCCoF9zNaFSlHUGup7ZjEZCO4rEYZmbqtW6CPn9lMNCd\nDgTl2oNa3t6wopQYRFg3uR8NUSeTjpy0fdWIU5GTWJAw7UEaHslnxeRJlwUHdoiY\nnG37EJpDgAiDspbHfWN3sfRnRDtL0gcvFy8yzSJr/mzlIdhmuxkAvlHFQ7dtav0+\nVnaR32lNWWgJVUljnTsPPAAIpznVIaj1aXA32X/N12JWXkr2Pe64uSD0DPE8I9xM\nl/n2kaSBiiZR7MIXw1wrbLGwN55qZlc9hURlWEE6vlxEz2LFfuMX9wpN/OZtiPpA\nFdVz+0CojiLsjjBa1ayjinEW6ICPfOJFe8IhCEThF/O1bvkCuUK8edZBUd2JtChA\nRN1BuX+AwaUryvf93xrqNJ0xxK5mOICCUQi8lYCl/n9bpCJ4HCqfsUiqd4tLy8GO\nbXx22debalaoYIC3tBz26BY3HbZldDO7LQWvkxkAqK24T26NS/oLagWB8BmGHwwK\nvi6L++VHFEvfmHNmltmpkM/wjsFw6m5jg8t0ETlYffJTJdlb/1r48/R0U/1dh8Oe\nn5ymUXYs3KWGVKxZcC7nMzOwisyWI83tm3/Ifytbo1oX1Wk8UL0eQVPbcttAnKAO\n543Fjs6tfhKUGiOSkvadYLOz08p3BlnXGLbbfdeOZ5b714guzdIHTg==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "examples/ssl/stream_client.php",
    "content": "<?php\n$contextOptions = [\n    'ssl' => [\n        'verify_peer' => false,\n//        'allow_self_signed' => true,\n//        'cafile' => __DIR__.'/privkey.pem',\n        'peer_name' => 'example.com',\n    ]\n];\n$context = stream_context_create($contextOptions);\n\n$fp = stream_socket_client(\"ssl://127.0.0.1:9501\", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context);\nif (!$fp)\n{\n    die(\"Unable to connect: $errstr ($errno)\");\n}\n\nstream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_SSLv23_CLIENT);\n$ret = fwrite($fp, \"hello\\n\");\nvar_dump($ret);\n\n$recv =  fread($fp, 8192);\nvar_dump($recv);\necho \"finish\\n\";\n"
  },
  {
    "path": "examples/ssl/swoole.log",
    "content": ""
  },
  {
    "path": "examples/ssl/webserver.php",
    "content": "<?php\n//$serv = new Swoole\\Http\\Server(\"0.0.0.0\", 443, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n$serv = new Swoole\\Http\\Server(\"0.0.0.0\", 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n$serv->set([\n    'ssl_cert_file' => __DIR__ . '/ssl.crt',\n    'ssl_key_file' => __DIR__ . '/ssl.key',\n    //'ssl_method' => SWOOLE_TLSv1_2_SERVER_METHOD,\n    'worker_num' => 1,\n    'open_http2_protocol' => true,\n    //'ssl_client_cert_file' => __DIR__ . '/ca.crt',\n    //'ssl_verify_depth' => 10,\n]);\n//c158354564362fcc\n\n$serv->on('Request', function(Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n    //var_dump($request->get);\n    //var_dump($request->post);\n    //var_dump($request->cookie);\n    //var_dump($request->files);\n//    var_dump($request->header);\n//    var_dump($request->server);\n    //global $serv;\n    //$info=  $serv->getClientInfo($request->fd);\n   // var_dump($info);\n    //$response->cookie(\"User\", \"Swoole\");\n    //$response->header(\"X-Server\", \"Swoole\");\n    $response->end(\"<h1>Hello Swoole!</h1>\\n\");\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/ssl/websocket_client.html",
    "content": "<script>\nvar wsServer = 'wss://localhost:9502';\nvar websocket = new WebSocket(wsServer);\nwebsocket.onopen = function (evt) {\n\tconsole.log(\"Connected to WebSocket server.\");\n};\n\nwebsocket.onclose = function (evt) {\n\tconsole.log(\"Disconnected\");\n};\n\nwebsocket.onmessage = function (evt) {\n\tconsole.log('Retrieved data from server: ' + evt.data);\n};\n\nwebsocket.onerror = function (evt, e) {\n\tconsole.log('Error occured: ' + evt.data);\n};\n</script>\n"
  },
  {
    "path": "examples/ssl/websocket_server.php",
    "content": "<?php\n$ssl_dir = realpath('../../tests/ssl');\n$serv = new Swoole\\WebSocket\\Server(\"0.0.0.0\", 9502, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n//$serv = new Swoole\\WebSocket\\Server(\"0.0.0.0\", 9502, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n$serv->set([\n    'ssl_cert_file' => $ssl_dir . '/ssl.crt',\n    'ssl_key_file' => $ssl_dir . '/ssl.key',\n    'worker_num' => 1,\n]);\n\n$port = $serv->listen('127.0.0.1', 9501, SWOOLE_SOCK_TCP);\n$port->on('receive', function($serv, $fd, $reactor_id, $data){\n    var_dump($fd, $reactor_id, $data);\n    $serv->send($fd, \"Swoole: $data\");\n});\n\n$serv->on('connect', function ($_server, $fd) {\n    echo \"client {$fd} connect\\n\";\n});\n\n$serv->on('open', function (Swoole\\WebSocket\\Server $_server, Swoole\\Http\\Request $request) {\n    echo \"server#{$_server->worker_pid}: handshake success with fd#{$request->fd}\\n\";\n//    var_dump($request);\n});\n\n$serv->on('request', function ($req, $resp) {\n    $resp->end(file_get_contents(__DIR__.'/websocket_client.html'));\n});\n\n$serv->on('message', function (Swoole\\WebSocket\\Server $_server, $frame) {\n    var_dump($frame->data);\n    echo \"received \".strlen($frame->data).\" bytes\\n\";\n    $_send = str_repeat('B', rand(100, 800));\n    $_server->push($frame->fd, $_send);\n    // echo \"#$i\\tserver sent \" . strlen($_send) . \" byte \\n\";\n});\n\n$serv->on('close', function ($_server, $fd) {\n    echo \"client {$fd} closed\\n\";\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/stdext/array.php",
    "content": "<?php\n$array = [1, 2, 3, 4, 5];\n\n$arr = $array->slice(1, 2);\nvar_dump($arr);\n\n$array1 = ['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5];\n$array2 = [6, 7, 8, 9, 10, 11, 12];\n\nvar_dump($array2->count());\n\n// var_dump($array2->all(function ($value) {\n//     return $value > 10;\n// }));\n\n$input_array = array(\"FirSt\" => 1, \"SecOnd\" => 4);\nprint_r($input_array->changeKeyCase(CASE_UPPER));\n\n$array4 = array(0 => 'blue', 1 => 'red', 2 => 'green', 3 => 'red');\n$key = $array4->search('green');\nvar_dump($key);\n\necho \"==================================[contains]===================================\\n\";\n$os = array(\"Mac\", \"Windows\", \"Linux\");\nvar_dump($os->contains(\"Windows\"));\nvar_dump($os->contains(\"Unix\"));\n\necho \"==================================[isList]===================================\\n\";\nvar_dump($array1->isList());\nvar_dump($array2->isList());\n\nvar_dump($array1->keys());\nvar_dump($array2->values());\n\necho \"==================================[join]===================================\\n\";\nvar_dump(['a', 'b', 'c']->join(','));\n\necho \"==================================[method not exists]===================================\\n\";\ntry {\n    $array->notExists();\n} catch (throwable $e) {\n    echo \"Caught exception: \",  $e->getMessage(), \"\\n\";\n}\n\nfunction odd($var)\n{\n    // returns whether the input integer is odd\n    return $var & 1;\n}\n\nfunction even($var)\n{\n    // returns whether the input integer is even\n    return !($var & 1);\n}\n\necho \"Odd :\\n\";\nprint_r($array1->filter(\"odd\"));\n\necho \"Even:\\n\";\nprint_r($array2->filter(\"even\"));\n\n\necho \"==================================[array_map]===================================\\n\";\n\n$a = [1, 2, 3, 4, 5];\n$b = $a->map(function ($n) {\n    return ($n * $n * $n);\n});\nvar_dump($b);\n\necho \"==================================[array_key_exists]===================================\\n\";\n$searchArray = ['first' => null, 'second' => 4];\n\nvar_dump(isset($searchArray['first']));\nvar_dump($searchArray->keyExists('first'));\n"
  },
  {
    "path": "examples/stdext/foreach.php",
    "content": "<?php\n$arr = typed_array('<int>');\n\n$arr[] = 1;\n// $arr[0] += 10;\n// assert($arr[0] == 11);\n$arr[0] .= \"hello world\";"
  },
  {
    "path": "examples/stdext/ref.php",
    "content": "<?php\n\n$fruits = array(\"lemon\", \"orange\", \"banana\", \"apple\");\necho \"Before sorting:\\n\";\nforeach ($fruits as $key => $val) {\n    echo \"fruits[\" . $key . \"] = \" . $val . \"\\n\";\n}\n\n$b = &$fruits;\n$b->sort(SORT_NATURAL | SORT_FLAG_CASE);\n\necho \"After sorting:\\n\";\nforeach ($fruits as $key => $val) {\n    echo \"fruits[\" . $key . \"] = \" . $val . \"\\n\";\n}\n\n$stack = array(\"orange\", \"banana\", \"apple\", \"raspberry\");\n\n$ref = &$stack;\n$fruit = $ref->shift();\nvar_dump($stack);\n\n$ref->unshift(\"kiwi\");\nvar_dump($stack);\n"
  },
  {
    "path": "examples/stdext/string.php",
    "content": "<?php\n/**\n * This file is part of Swoole.\n *\n * @link     https://www.swoole.com\n * @contact  team@swoole.com\n * @license  https://github.com/swoole/library/blob/master/LICENSE\n */\n\ndeclare(strict_types=1);\n$str = 'hello world';\nvar_dump($str->upper());\n\nvar_dump($str->split(' ')->search('world'));\nvar_dump($str->length());\nvar_dump('test'->length());\n\nvar_dump($str->indexOf('world'));\nvar_dump($str->substr(1, 4));\n\nvar_dump($str->startsWith('hello'));\nvar_dump($str->endsWith('world'));\nvar_dump($str->endsWith('.php'));\n\nvar_dump($str->md5(), $str->sha1(), $str->crc32());\nvar_dump($str->hash('sha256'));\necho \"==============================hash=====================\\n\";\nvar_dump($str->md5() === $str->hash('md5'));\n\n$str = 'first=value&arr[]=foo+bar&arr[]=baz';\n$output = $str->parseStr();\necho $output['first'];  // value\necho $output['arr'][0]; // foo bar\necho $output['arr'][1]; // baz\n\nvar_dump($str->urlEncode());\n"
  },
  {
    "path": "examples/stdext/typed_array.php",
    "content": "<?php\n$list = typed_array('<int>');\n\n$list[] = 123;\n$list[] = 345;\n$list[] = 'hello'; // 异常\n\n"
  },
  {
    "path": "examples/stdext/typed_array_map.php",
    "content": "<?php\n$list = typed_array('<string, int>');\n\n$list[\"hello\"] = 123;\n$list[] = 345;\n$list[\"hello\"] = 'hello';\n"
  },
  {
    "path": "examples/table/deadlock.php",
    "content": "<?php\nini_set('memory_limit', '8M');\n\n$table = new Swoole\\Table(1024);\n$table->column('name', Swoole\\Table::TYPE_STRING, 1024 * 64);\n$table->create();\n\n$table->set('key1', ['name' => str_repeat('A', 1024 * 64 - 1) . \"\\n\"]);\n\nif (pcntl_fork() == 0) {\n    sleep(1);\n    $r = $table->get('key1');\n    var_dump(strlen($r['name']));\n} else {\n    $mu1 = memory_get_usage();\n    var_dump($mu1);\n    $str = str_repeat('A', 1024 * 1024 * 5);\n    $str2 = str_repeat('A', 1024 * 1024);\n    $str3 = str_repeat('A', 1024 * 64);\n    var_dump(memory_get_usage());\n    $r = $table->get('key1');\n    var_dump(strlen($r['name']));\n    echo substr($str, 0, 8);\n    pcntl_wait($status);\n}\n\n"
  },
  {
    "path": "examples/table/iterator.php",
    "content": "<?php\n$table = new Swoole\\Table(1024);\n$table->column('name', Swoole\\Table::TYPE_STRING, 64);\n$table->column('id', Swoole\\Table::TYPE_INT, 4);       //1,2,4,8\n$table->column('num', Swoole\\Table::TYPE_FLOAT);\n$table->create();\n\n$table->set('tianfenghan@qq.com', array('id' => 145, 'name' => 'rango1', 'num' => 3.1415));\n$table->set('350749960@qq.com', array('id' => 358, 'name' => \"Rango2\", 'num' => 3.1415));\n$table->set('hello@qq.com', array('id' => 189, 'name' => 'rango3', 'num' => 3.1415));\n\nvar_dump($table->get('350749960@qq.com'));\nvar_dump($table->get('350749960@qq.com', 'name'));\n\nforeach($table as $key => $value)\n{\n    var_dump($key, $value);\n}\n\necho \"======================= Total Elements: {$table->count()} ============================\\n\";\n$table->del('350749960@qq.com'); // delete a exist element\nforeach($table as $key => $value)\n{\n    var_dump($key, $value);\n}\necho \"======================= Total Elements: {$table->count()} ============================\\n\";\n$ret = $table->del('a invalid key'); // delete a invalid element\nvar_dump($ret);\nforeach($table as $key => $value)\n{\n    var_dump($key, $value);\n}\necho \"======================= Total Elements: {$table->count()} ============================\\n\";\n"
  },
  {
    "path": "examples/table/server.php",
    "content": "<?php\n$table = new Swoole\\Table(1024);\n$table->column('fd', Swoole\\Table::TYPE_INT);\n$table->column('reactor_id', Swoole\\Table::TYPE_INT);\n$table->column('data', Swoole\\Table::TYPE_STRING, 64);\n$table->create();\n\n$serv = new Swoole\\Server('127.0.0.1', 9501);\n$serv->set(['dispatch_mode' => 2]);\n$serv->table = $table;\n\n$serv->on('connect', function($serv, $fd, $reactor_id){\n\t$info = $serv->connection_info($fd);\n\t$serv->send($fd, \"INFO: fd=$fd, reactor_id=$reactor_id, addr={$info['remote_ip']}:{$info['remote_port']}\\n\");\n});\n\n$serv->on('receive', function ($serv, $fd, $reactor_id, $data) {\n\n\t$cmd = explode(\" \", trim($data));\n\n\t//get\n\tif ($cmd[0] == 'get')\n\t{\n\t\t//get self\n\t\tif (count($cmd) < 2)\n\t\t{\n\t\t\t$cmd[1] = $fd;\n\t\t}\n\t\t$get_fd = intval($cmd[1]);\n\t\t$info = $serv->table->get($get_fd);\n\t\t$serv->send($fd, var_export($info, true).\"\\n\");\n\t}\n\t//set\n\telseif ($cmd[0] == 'set')\n\t{\n\t\t$ret = $serv->table->set($fd, array('reactor_id' => $data, 'fd' => $fd, 'data' => $cmd[1]));\n\t\tif ($ret === false)\n\t\t{\n\t\t\t$serv->send($fd, \"ERROR\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$serv->send($fd, \"OK\\n\");\n\t\t}\n\t}\n\telse\n\t{\n\t\t$serv->send($fd, \"command error.\\n\");\n\t}\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/table/set.php",
    "content": "<?php\n$table = new Swoole\\Table(1024);\n$table->column('id', Swoole\\Table::TYPE_INT, 4);       //1,2,4,8\n$table->column('name', Swoole\\Table::TYPE_STRING, 64);\n$table->column('num', Swoole\\Table::TYPE_FLOAT);\n$table->create();\n\n//$worker = new Swoole\\Process('child1', false, false);\n//$worker->start();\n//\n//child\nfunction child1($worker)\n{\n    global $table;\n    $s = microtime(true);\n    $table->set('tianfenghan@qq.com', array('id' => 145, 'name' => 'rango', 'num' => 3.1415));\n    $table->set('350749960@qq.com', array('id' => 358, 'name' => \"Rango1234\", 'num' => 3.1415));\n    $table->set('hello@qq.com', array('id' => 189, 'name' => 'rango3', 'num' => 3.1415));\n    $table->set('tianfenghan@qq.com', array('id' => 145, 'name' => 'rango', 'num' => 3.1415));\n    $table->set('350749960@qq.com', array('id' => 358, 'name' => \"Rango1234\", 'num' => 3.1415));\n    echo \"set - 5 use: \".((microtime(true) - $s) * 1000).\"ms\\n\";\n}\n\n//master\nsleep(1);\n\nchild1(1245);\n$s = microtime(true);\nfor($i =0; $i < 1000; $i++)\n{\n    $arr = $table->get('350749960@qq.com');\n}\n\necho \"get -5 use: \".((microtime(true) - $s) * 1000).\"ms\\n\";\n$s = microtime(true);\n//$table->incr('tianfenghan@qq.com', 'id', 5);\n//$table->decr('hello@qq.com', 'num', 1.1);\n$ret1 = $table->get('350749960@qq.com');\n$ret2 = $table->get('tianfenghan@qq.com');\n$ret3 = $table->get('350749960@qq.com');\n$ret4 = $table->get('tianfenghan@qq.com');\n$ret5 = $table->get('hello@qq.com');\n\necho \"get -5 use: \".((microtime(true) - $s) * 1000).\"ms\\n\";\nvar_dump($ret1, $ret2, $ret3, $ret4, $ret5);\necho \"id:\".$ret1['id'].\"\\n\";\necho \"name:\".$ret1['name'].\"\\n\";\necho \"num:\".$ret1['num'].\"\\n\";\n"
  },
  {
    "path": "examples/table/simulation.php",
    "content": "<?php\n/**\n * The script is used for simulating the usage of Swoole\\Table() and guarantying its usability.\n */\n$table = new Swoole\\Table(1024);\n$table->column('name', Swoole\\Table::TYPE_STRING, 64);\n$table->column('id', Swoole\\Table::TYPE_INT, 4);       //1,2,4,8\n$table->column('num', Swoole\\Table::TYPE_FLOAT);\n$table->create();\n\nwhile (true) {\n    $i = rand(1, 1000);\n    $if = rand(0,1);\n    if ($if) {\n        $table->set($i, ['id' => $i, 'name' => $i, 'num' => $i]);\n    } else {\n        $table->del($i);\n    }\n    var_dump('count ' . $table->count());\n}\n"
  },
  {
    "path": "examples/table/usage.php",
    "content": "<?php\n$table = new Swoole\\Table(1024);\n$table->column('id', Swoole\\Table::TYPE_INT);\n$table->column('name', Swoole\\Table::TYPE_STRING, 64);\n$table->column('num', Swoole\\Table::TYPE_FLOAT);\n$table->create();\n\n$table->set('a', array('id' => 1, 'name' => 'swoole-co-uk', 'num' => 3.1415));\n$table->set('b', array('id' => 2, 'name' => \"swoole-uk\", 'num' => 3.1415));\n$table->set('hello@swoole.co.uk', array('id' => 3, 'name' => 'swoole', 'num' => 3.1415));\n\nvar_dump($table->get('a'));\nvar_dump($table->get('b', 'name'));\n"
  },
  {
    "path": "examples/task/http.php",
    "content": "<?php\n$serv = new Swoole\\Http\\Server(\"127.0.0.1\", 9501);\n$serv->set(array(\n    'worker_num' => 1,\n    'task_worker_num' => 1,\n//    'task_ipc_mode' => 3,\n//    'message_queue_key' => 0x70001001,\n    //'task_tmpdir' => '/data/task/',\n));\n\n$serv->on('Request', function ($req, $resp)\n{\n    $data = str_repeat('A', 8192 * 10);\n    global $serv;\n\n    $serv->task(array($data, 1000), -1, function ($serv, $task_id, $data) use ($resp)\n    {\n        $resp->end(\"Task#$task_id finished.\" . PHP_EOL);\n    });\n\n});\n$serv->on('Task', function (Swoole\\Server $serv, $task_id, $reactor_id, $data) {\n    //echo \"#{$serv->worker_id}\\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=\".strlen($data).\".\".PHP_EOL;\n//    $serv->finish($data);\n    return $data;\n});\n\n$serv->on('Finish', function (Swoole\\Server $serv, $task_id, $data) {\n    echo \"Task#$task_id finished, data_len=\".strlen($data).PHP_EOL;\n});\n\n$serv->on('workerStart', function($serv, $worker_id) {\n\tglobal $argv;\n    if ($serv->taskworker)\n    {\n        swoole_set_process_name(\"php {$argv[0]}: task_worker\");\n    }\n    else\n    {\n        swoole_set_process_name(\"php {$argv[0]}: worker\");\n    }\n});\n\n$serv->on('workerStop', function (Swoole\\Server $serv, $id) {\n    echo \"stop\\n\";\n    var_dump($id);\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/task/msg_push.php",
    "content": "<?php\necho \"Sending text to msg queue.\\n\";\n\nclass SwooleTask\n{\n    protected $queueId;\n    protected $workerId;\n\n    function __construct($key, $workerId)\n    {\n        $this->queueId = msg_get_queue($key);\n        if ($this->queueId === false)\n        {\n            throw new \\Swoole\\Exception(\"msg_get_queue() failed.\");\n        }\n        $this->workerId = $workerId;\n    }\n\n    function dispatch($data)\n    {\n        if (!msg_send($this->queueId, $this->workerId + 1, Swoole\\Server\\Task::pack($data), false))\n        {\n            return false;\n        }\n        else\n        {\n            return true;\n        }\n    }\n}\n\n$task = new SwooleTask(0x70001001, 0);\n//普通字符串\n$task->dispatch(\"Hello from PHP!\");\n//数组\n$task->dispatch(array('data' => str_repeat('A', 1024), 'type' => 1));\n//大包\n$task->dispatch(array('data' => str_repeat('B', 1024 * 32), 'type' => 2));\n"
  },
  {
    "path": "examples/task/shared_client.php",
    "content": "<?php\n$client = new Swoole\\Client(SWOOLE_SOCK_TCP);\nif(!$client->connect('127.0.0.1', 9501))\n{\n    exit(\"connect failed\\n\");\n}\nfunction help()\n{\n\techo \"get eg: php \".__FILE__.\" get key\".PHP_EOL;\n\techo \"set eg: php \".__FILE__.\" set key value\".PHP_EOL;\n\techo \"del eg: php \".__FILE__.\" del key\".PHP_EOL;\n\techo \"task eg: php \".__FILE__.\" task key\".PHP_EOL;\n\texit();\n}\nif($argc < 3) {\n\thelp();\n}\n$keys = array(\n\t1 => 'cmd',\n\t2 => 'key',\n\t3 => 'val'\n);\n$sends = array();\nforeach ($keys as $i => $key)\n{\n\tif (isset($argv[$i]))\n\t{\n\t\t$sends[$key] = $argv[$i];\n\t}\n}\nif (empty($sends))\n{\n\thelp();\n}\n$client->send(serialize($sends));\n$data = $client->recv();\necho $data . PHP_EOL;\n"
  },
  {
    "path": "examples/task/shared_server.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"127.0.0.1\", 9501);\n\n$serv->set(array(\n    'worker_num' => 1,\n    //'open_eof_check' => true,\n    //'package_eof' => \"\\r\\n\",\n    'task_worker_num' => 1,\n    //'dispatch_mode' => 2,\n    //'daemonize' => 1,\n    //'heartbeat_idle_time' => 5,\n    //'heartbeat_check_interval' => 5,\n));\nfunction my_onStart($serv)\n{\n    echo \"MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}\\n\";\n    echo \"Server: start.Swoole version is [\".SWOOLE_VERSION.\"]\\n\";\n    //$serv->addtimer(1000);\n}\n\nfunction my_onShutdown($serv)\n{\n    echo \"Server: onShutdown\\n\";\n}\n\nfunction my_onClose($serv, $fd, $reactor_id)\n{\n    //echo \"Client: fd=$fd is closed.\\n\";\n}\n\nfunction my_onConnect($serv, $fd, $reactor_id)\n{\n    //throw new Exception(\"hello world\");\n//  echo \"Client:Connect.\\n\";\n}\n\nfunction my_onWorkerStart($serv, $worker_id)\n{\n    global $argv;\n    if ($worker_id >= $serv->setting['worker_num']) {\n        swoole_set_process_name(\"php {$argv[0]} task worker\");\n    } else {\n        swoole_set_process_name(\"php {$argv[0]} event worker\");\n    }\n    //echo \"WorkerStart|MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}|WorkerId=$worker_id\\n\";\n    //$serv->addtimer(500); //500ms\n}\n\nfunction my_onWorkerStop($serv, $worker_id)\n{\n    echo \"WorkerStop[$worker_id]|pid=\".posix_getpid().\".\\n\";\n}\n\nfunction my_onReceive(Swoole\\Server $serv, $fd, $reactor_id, $rdata)\n{\n    $data = unserialize($rdata);\n    if (isset($data['cmd']))\n    {\n        switch ($data['cmd'])\n        {\n            case 'get':\n                $s = microtime(true);\n                $res = $serv->taskwait($data, 0.5, 0);\n                echo \"use \" . ((microtime(true) - $s) * 1000) . \"ms\\n\";\n                $serv->send($fd, PHP_EOL . \"get \" . $res['key'] . \": \" . $res['val']);\n                break;\n            case \"set\":\n                $serv->task($data, 0);\n                $serv->send($fd, \"OK\\n\");\n                break;\n            case \"del\":\n                $serv->task($data, 0);\n                break;\n            case \"reload\":\n                break;\n            default:\n                echo \"server:\" . $data . PHP_EOL;\n        }\n    }\n}\n\nfunction my_onTask(Swoole\\Server $serv, $task_id, $reactor_id, $data)\n{\n    static $datas = array();\n    if (isset($data['cmd']))\n    {\n        switch ($data['cmd']) {\n            case 'get':\n                $key = $data['key'];\n                $val = isset($datas[$key]) ? $datas[$key] : \"\";\n                $serv->finish(array('key'=>$key, 'val' => $val));\n                break;\n            case \"set\":\n                $key = $data['key'];\n                $val = $data['val'].\"_\".$reactor_id;\n                $datas[$key] = $val;\n                return;\n                break;\n            case \"del\":\n                $key = $data['key'];\n                if(isset($datas[$key])) {\n                    unset($datas[$key]);\n                }\n                break;\n            case \"task\":\n                $key = $data['key'];\n                echo \"Do task \" . $key . PHP_EOL;\n                break;\n        }\n    }\n    echo \"AsyncTask[PID=\".posix_getpid().\"]: task_id=$task_id.\".PHP_EOL;\n    // $serv->finish(\"OK\");\n}\n\nfunction my_onFinish(Swoole\\Server $serv, $task_id, $from_worker_id, $data)\n{\n    echo \"AsyncTask Finish: Connect.PID=\" . posix_getpid() . PHP_EOL;\n}\n\nfunction my_onWorkerError(Swoole\\Server $serv, $worker_id, $worker_pid, $exit_code)\n{\n    echo \"worker abnormal exit. WorkerId=$worker_id|Pid=$worker_pid|ExitCode=$exit_code\\n\";\n}\n\n$serv->on('Start', 'my_onStart');\n$serv->on('Connect', 'my_onConnect');\n$serv->on('Receive', 'my_onReceive');\n$serv->on('Close', 'my_onClose');\n$serv->on('Shutdown', 'my_onShutdown');\n$serv->on('WorkerStart', 'my_onWorkerStart');\n$serv->on('WorkerStop', 'my_onWorkerStop');\n$serv->on('Task', 'my_onTask');\n$serv->on('Finish', 'my_onFinish');\n$serv->on('WorkerError', 'my_onWorkerError');\n$serv->start();\n"
  },
  {
    "path": "examples/task/task.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"127.0.0.1\", 9501,SWOOLE_BASE);\n\n$serv->set(array(\n    //'worker_num' => 1,\n    'task_worker_num' => 1,\n//    'task_ipc_mode' => 3,\n//    'message_queue_key' => 0x70001001,\n    //'task_tmpdir' => '/data/task/',\n));\n\n$serv->on('Receive', function(Swoole\\Server $serv, $fd, $reactor_id, $data) {\n\t//AsyncTask\n    $data = trim($data);\n    //$data = str_repeat('A', 8192*100);\n//    if ($data == 'async')\n    if(false)\n//    if (true)\n    {\n        $task_id = $serv->task($data, 0);\n        $serv->send($fd, \"Dispath AsyncTask: id=$task_id\\n\");\n    }\n    //Sync Task\n\telse\n    {\n        $res = $serv->taskwait($data, 10);\n        echo \"Dispath SyncTask: result=\".$res.PHP_EOL;\n    }\n    //$serv->send($fd, \"OK\\n\");\n});\n$serv->on('Task', function (Swoole\\Server $serv, $task_id, $reactor_id, $data) {\n    echo \"#{$serv->worker_id}\\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=\".strlen($data).\".\".PHP_EOL;\n    $serv->finish($data);\n//    return $data;\n});\n\n$serv->on('Finish', function (Swoole\\Server $serv, $task_id, $data) {\n    echo \"Task#$task_id finished, data_len=\".strlen($data).PHP_EOL;\n});\n\n$serv->on('workerStart', function($serv, $worker_id) {\n\tglobal $argv;\n    if($worker_id >= $serv->setting['worker_num']) {\n        swoole_set_process_name(\"php {$argv[0]}: task_worker\");\n    } else {\n        swoole_set_process_name(\"php {$argv[0]}: worker\");\n    }\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/task/task_coro.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"127.0.0.1\", 9501);\n$serv->set(array(\n    'worker_num' => 1,\n    'task_worker_num' => 4,\n    //'task_tmpdir' => '/data/task/',\n));\n\n$serv->on('Receive', function(Swoole\\Server $serv, $fd, $reactor_id, $data) {\n    $tasks[] = mt_rand(1000, 9999);\n    $tasks[] = mt_rand(1000, 9999);\n    $tasks[] = mt_rand(1000, 9999);\n    $tasks[] = mt_rand(1000, 9999);\n    //等待所有Task结果返回，超时为10s\n    var_dump($tasks);\n    $results = $serv->taskWaitMulti($tasks, 2);\n    var_dump($results);\n});\n\n$serv->on('Task', function (Swoole\\Server $serv, $task_id, $reactor_id, $data) {\n    echo \"onTask: [ID={$serv->worker_id}]: task_id=$task_id, data=$data, data_len=\".strlen($data).\".\".PHP_EOL;\n    //测试超时\n    if ($serv->worker_id % 4 == 3)\n    {\n        sleep(3);\n    }\n    elseif ($serv->worker_id % 4 == 2)\n    {\n        usleep(1500000);\n    }\n    elseif ($serv->worker_id % 4 == 1)\n    {\n        usleep(200000);\n    }\n    return \"hello world.[{$data}]\";\n});\n\n$serv->on('Finish', function (Swoole\\Server $serv, $task_id, $data) {\n    echo \"Task#$task_id finished, data_len=\".strlen($data).PHP_EOL;\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/task/task_num.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"127.0.0.1\", 9501);\n$serv->set(array(\n    'worker_num' => 1,\n    'task_worker_num' => 2,\n    //'task_tmpdir' => '/data/task/',\n));\n\n$serv->on('Receive', function(Swoole\\Server $serv, $fd, $reactor_id, $data) {\n\t//AsyncTask\n    $data = intval($data);\n    for($i=0;$i<$data;$i++) {\n        $tid = mt_rand(0,1);\n        echo \"data:{$i} to task: {$tid} \".PHP_EOL;\n        $serv->task($i, $tid);\n    }\n\n});\n$serv->on('Task', function (Swoole\\Server $serv, $task_id, $reactor_id, $data) {\n    echo \"onTask: [PID=\".posix_getpid().\"]: task_id=$task_id, data_len=\".strlen($data).\".\".PHP_EOL;\n    sleep(10);\n    //$serv->finish($data);\n    echo 'finish'.PHP_EOL;\n    return;\n});\n\n$serv->on('Finish', function (Swoole\\Server $serv, $task_id, $data) {\n    echo \"Task#$task_id finished, data_len=\".strlen($data).PHP_EOL;\n});\n\n$serv->on('Timer', function(Swoole\\Server $serv, $time) {\n    echo \"{$time} call\".PHP_EOL;\n    print_r($serv->stats());\n});\n\n$serv->on('workerStart', function($serv, $worker_id) {\n\tglobal $argv;\n    if($worker_id >= $serv->setting['worker_num']) {\n        swoole_set_process_name(\"php {$argv[0]}: task_worker\");\n    } else {\n        $serv->addtimer(5000);\n        swoole_set_process_name(\"php {$argv[0]}: worker\");\n    }\n\n});\n$serv->start();\n"
  },
  {
    "path": "examples/task/task_queue.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"127.0.0.1\", 9501, SWOOLE_BASE);\n\n$serv->set(array(\n    //'worker_num' => 1,\n    'task_worker_num' => 4,\n    'task_ipc_mode' => 3,\n    'message_queue_key' => 0x70001001,\n    'task_tmpdir' => '/data/task/',\n));\n\n$serv->on('Receive', function(Swoole\\Server $serv, $fd, $reactor_id, $data) {\n});\n\n$serv->on('Task', function (Swoole\\Server $serv, $task_id, $reactor_id, $data) {\n    echo \"#{$serv->worker_id}\\tonTask: [PID={$serv->worker_pid}]: TASK_ID=$task_id]\\n\";\n    var_dump($data);\n});\n\n$serv->on('Finish', function (Swoole\\Server $serv, $task_id, $data) {\n    echo \"Task#$task_id finished, data_len=\".strlen($data).PHP_EOL;\n});\n\n$serv->on('workerStart', function($serv, $worker_id) {\n\tglobal $argv;\n    if($worker_id >= $serv->setting['worker_num']) {\n        swoole_set_process_name(\"php {$argv[0]}: task_worker\");\n    } else {\n        swoole_set_process_name(\"php {$argv[0]}: worker\");\n    }\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/task/task_stream.php",
    "content": "<?php\n$serv = new Swoole\\Server(\"127.0.0.1\", 9501, SWOOLE_BASE);\n\n$serv->set(array(\n    'worker_num' => 1,\n    'task_worker_num' => 1,\n    'task_ipc_mode' => 4,\n//    'message_queue_key' => 0x70001001,\n));\n\n$serv->on('Receive', function(Swoole\\Server $serv, $fd, $reactor_id, $data) {\n\t//AsyncTask\n    $data = trim($data);\n    //$data = str_repeat('A', 8192*100);\n//    if ($data == 'async')\n//    if(false)\n    if (true)\n    {\n        $task_id = $serv->task($data, 0);\n        $serv->send($fd, \"Dispath AsyncTask: id=$task_id\\n\");\n    }\n    //Sync Task\n\telse\n    {\n        $res = $serv->taskwait($data, 10);\n        echo \"Dispath SyncTask: result=\".$res.PHP_EOL;\n    }\n    //$serv->send($fd, \"OK\\n\");\n});\n$serv->on('Task', function (Swoole\\Server $serv, $task_id, $reactor_id, $data) {\n    echo \"#{$serv->worker_id}\\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=\".strlen($data).\".\".PHP_EOL;\n    $serv->finish($data);\n    return $data;\n});\n\n$serv->on('Finish', function (Swoole\\Server $serv, $task_id, $data) {\n    echo \"Task#$task_id finished, data_len=\".strlen($data).PHP_EOL;\n});\n\n$serv->on('workerStart', function($serv, $worker_id) {\n\tglobal $argv;\n    if ($serv->taskworker)\n    {\n        swoole_set_process_name(\"php {$argv[0]}: task_worker\");\n    }\n    else\n    {\n        swoole_set_process_name(\"php {$argv[0]}: worker\");\n    }\n    echo \"Worker#$worker_id, pid=\".posix_getpid().\" start\".PHP_EOL;\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/thread/aio.php",
    "content": "<?php\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Queue;\n\n\n$args = Thread::getArguments();\n$c = 4;\n$running = true;\n\nif (empty($args)) {\n    $threads = [];\n    $atomic = new Swoole\\Thread\\Atomic();\n    for ($i = 0; $i < $c; $i++) {\n        $threads[] = new Thread(__FILE__, $i, $atomic);\n    }\n    for ($i = 0; $i < $c; $i++) {\n        $threads[$i]->join();\n    }\n    var_dump($atomic->get());\n    sleep(2);\n\n    Co\\run(function () use($atomic) {\n        $n = 1024;\n        while ($n--) {\n            $atomic->add();\n            $rs = \\Swoole\\Coroutine\\System::readFile(__FILE__);\n            var_dump(strlen($rs));\n        }\n    });\n    var_dump($atomic->get());\n} else {\n    $atomic = $args[1];\n    Co\\run(function () use($atomic) {\n        $n = 1024;\n        while ($n--) {\n            $atomic->add();\n            $rs = \\Swoole\\Coroutine\\System::readFile(__FILE__);\n            var_dump(strlen($rs));\n        }\n    });\n}\n"
  },
  {
    "path": "examples/thread/argv.php",
    "content": "<?php\n\nuse Swoole\\Thread;\n\n$args = Thread::getArguments();\n\nif (empty($args)) {\n    var_dump($GLOBALS['argv']);\n    $n = 2;\n    while ($n--) {\n        $thread = new Thread(__FILE__, 'thread-' . $n, $argc, $argv);\n        $thread->join();\n    }\n} else {\n    var_dump($args[0], $args[1], $args[2]);\n    sleep(1);\n}\n"
  },
  {
    "path": "examples/thread/array.php",
    "content": "<?php\n\nini_set('memory_limit', '2G');\n$dict = [];\nconst COUNT = 10000000;\n\n$n = COUNT;\n$s = microtime(true);\nwhile ($n--) {\n    $dict['key-' . $n] = $n * 3;\n}\necho 'array set: ' . round(microtime(true) - $s, 6) . ' seconds' . PHP_EOL;\n\n$c = 0;\n$n = COUNT;\n$s = microtime(true);\nwhile ($n--) {\n    $c += $dict['key-' . $n];\n}\necho 'array get: ' . round(microtime(true) - $s, 6) . ' seconds' . PHP_EOL;\n"
  },
  {
    "path": "examples/thread/atomic.php",
    "content": "<?php\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Atomic;\nuse Swoole\\Thread\\Atomic\\Long;\n\n$args = Thread::getArguments();\n$c = 4;\n$n = 128;\n\nif (empty($args)) {\n    $threads = [];\n    $a1 = new Atomic;\n    $a2 = new Long;\n    for ($i = 0; $i < $c; $i++) {\n        $threads[] = new Thread(__FILE__, $i, $a1, $a2);\n    }\n    for ($i = 0; $i < $c; $i++) {\n        $threads[$i]->join();\n    }\n    var_dump($a1->get(), $a2->get());\n} else {\n    $a1 = $args[1];\n    $a2 = $args[2];\n\n    $a1->add(3);\n    $a2->add(7);\n}\n"
  },
  {
    "path": "examples/thread/benchmark.php",
    "content": "<?php\nini_set('memory_limit', '2G');\n$args = Swoole\\Thread::getArguments();\n\n$dict = $args[1];\nconst COUNT = 10000000;\n\n$n = COUNT;\n$s = microtime(true);\nwhile ($n--) {\n    $dict['key-' . $n] = $n * 3;\n}\necho $args[0] . \"\\t\" . 'array set: ' . round(microtime(true) - $s, 6) . ' seconds' . PHP_EOL;\n\n$c = 0;\n$n = COUNT;\n$s = microtime(true);\nwhile ($n--) {\n    $c += $dict['key-' . $n];\n}\necho $args[0] . \"\\t\" . 'array get: ' . round(microtime(true) - $s, 6) . ' seconds' . PHP_EOL;\n"
  },
  {
    "path": "examples/thread/co.php",
    "content": "<?php\n$map = new Swoole\\Thread\\Map(2);\n$map['uuid'] = uniqid();\n$map[time()] = uniqid();\n\n$list = new Swoole\\Thread\\ArrayList();\n$list[] = base64_encode(random_bytes(32));\n$list[1] = uniqid();\nvar_dump(count($list));\n\n$t1 = new Swoole\\Thread('mt.php', 'thread-1', PHP_OS, $map, $list);\n$t2 = new Swoole\\Thread('mt.php', 'thread-2', PHP_OS, $map, $list);\n\n//var_dump($t1->id);\n//var_dump($t2->id);\necho Swoole\\Thread::getId() . \"\\t\" . 'gmap[uuid]' . \"\\t\" . $map['uuid'] . \"\\n\";\n\ntry {\n    var_dump($list[999]);\n} catch (Swoole\\Exception $e) {\n    assert(str_contains($e->getMessage(), 'out of range'));\n}\n\ntry {\n    unset($list[0]);\n} catch (Swoole\\Exception $e) {\n    assert(str_contains($e->getMessage(), 'unsupported'));\n}\n\n$t1->join();\n$t2->join();\n\n\n"
  },
  {
    "path": "examples/thread/exit.php",
    "content": "<?php\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Lock;\n\n$args = Thread::getArguments();\n\nif (empty($args)) {\n    $lock = new Lock;\n    $lock->lock();\n    $thread = new Thread(__FILE__, $lock);\n    echo \"main thread\\n\";\n    $lock->unlock();\n    $thread->join();\n    var_dump($thread->getExitStatus());\n} else {\n    $lock = $args[0];\n    $lock->lock();\n    sleep(1);\n    exit(234);\n}\n"
  },
  {
    "path": "examples/thread/hook.php",
    "content": "<?php\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Lock;\n\n$args = Thread::getArguments();\n\nif (empty($args)) {\n    Swoole\\Runtime::enableCoroutine(SWOOLE_HOOK_ALL);\n    $lock = new Lock;\n    $lock->lock();\n    $thread = new Thread(__FILE__, $lock);\n    echo \"main thread\\n\";\n    $lock->unlock();\n    $thread->join();\n    var_dump($thread->getExitStatus());\n} else {\n    $lock = $args[0];\n    $lock->lock();\n    Swoole\\Runtime::enableCoroutine(SWOOLE_HOOK_ALL);\n    sleep(1);\n    Swoole\\Runtime::enableCoroutine(0);\n    exit(234);\n}\n"
  },
  {
    "path": "examples/thread/lock.php",
    "content": "<?php\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Lock;\n\n$args = Thread::getArguments();\n\nif (empty($args)) {\n    $lock = new Lock;\n    $lock->lock();\n    $thread = new Thread(__FILE__, $lock);\n    $lock->lock();\n    echo \"main thread\\n\";\n    $thread->join();\n} else {\n    $lock = $args[0];\n    sleep(1);\n    $lock->unlock();\n}\n"
  },
  {
    "path": "examples/thread/map.php",
    "content": "<?php\nuse Swoole\\Thread;\n$args = Thread::getArguments();\n\nif (empty($args)) {\n    $array = [\n        'a' => random_int(1, 999999999999999999),\n        'b' => random_bytes(128),\n        'c' => uniqid(),\n        'd' => time(),\n    ];\n\n    $map = new Thread\\Map($array);\n    $thread = new Thread(__FILE__, $map);\n} else {\n    $map = $args[0];\n    var_dump($map->toArray());\n}\n"
  },
  {
    "path": "examples/thread/mt.php",
    "content": "<?php\n//echo \"begin\\n\";\n\n$args = Swoole\\Thread::getArguments();\n\n$map = $args[2];\n$list = $args[3];\n\necho Swoole\\Thread::getId() . \"\\t\" . 'gmap[uuid]' . \"\\t\" . $map['uuid'] . \"\\n\";\n$map['hello'] = uniqid('swoole');\nvar_dump($map->keys());\n\n$list[] = uniqid('swoole');\n$list[count($list)] = uniqid('php');\n\nvar_dump($args);\n\necho Swoole\\Thread::getId() . \"\\t\" . 'glist[0]' . \"\\t\" . $list[0] . \"\\n\";\nvar_dump(count($list));\n\n//if ($args[0] == 'thread-2') {\n//    $t3 = new Swoole\\Thread('mt.php', 'thread-3', PHP_OS);\n//    $t3->join();\n//}\n\n//sleep(5);\n//echo \"end\\n\";\n"
  },
  {
    "path": "examples/thread/nested_map.php",
    "content": "<?php\n$map = new Swoole\\Thread\\Map();\n\n$map['map1'] = new Swoole\\Thread\\Map();\n$map['list1'] = new Swoole\\Thread\\ArrayList();\n\n$map['map1']['key1'] = 'value1';\n$map['list1'][0] = 'value2';\n$map['str'] = 'hello world';\n\n$map['map2'] = [\n    'a' => uniqid(),\n    'b' => random_int(1000, 9999),\n];\n\nvar_dump($map['map1']['key1']);\nvar_dump($map['list1'][0]);\n\nvar_dump($map['list1']->toArray());\n\nvar_dump($map['map2']);\n"
  },
  {
    "path": "examples/thread/pipe.php",
    "content": "<?php\n\nuse Swoole\\Thread;\n\n$args = Thread::getArguments();\n\nif (empty($args)) {\n    Co\\run(function () {\n        $sockets = swoole_coroutine_socketpair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);\n        $thread = new Thread(__FILE__, $sockets);\n        echo $sockets[0]->recv(8192), PHP_EOL;\n        $thread->join();\n    });\n} else {\n    $sockets = $args[0];\n    Co\\run(function () use ($sockets) {\n        sleep(1);\n        $sockets[1]->send(uniqid());\n    });\n}\n"
  },
  {
    "path": "examples/thread/run_test.php",
    "content": "<?php\n\nuse Swoole\\Thread\\Map;\n\n$map = new Map;\n$c = 1;\n$threads = [];\n\nfor ($i = 0; $i < $c; $i++) {\n    $threads[] = new Swoole\\Thread('benchmark.php', 'thread-' . ($i + 1), $map);\n}\n\nfor ($i = 0; $i < $c; $i++) {\n    $threads[$i]->join();\n}\n\n"
  },
  {
    "path": "examples/thread/server.php",
    "content": "<?php\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Queue;\n\n$args = Thread::getArguments();\n$c = 2;\n$running = true;\n\nif (empty($args)) {\n    $threads = [];\n    $queue = new Queue;\n    for ($i = 0; $i < $c; $i++) {\n        $threads[] = new Thread(__FILE__, $i, $queue);\n    }\n    for ($i = 0; $i < $c; $i++) {\n        $threads[$i]->join();\n    }\n} else {\n    $http = new Swoole\\Http\\Server(\"0.0.0.0\", 9503);\n    $http->on('request', function ($req, Swoole\\Http\\Response $resp) {\n        $resp->end('hello world');\n    });\n    $http->start();\n}\n"
  },
  {
    "path": "examples/thread/signal.php",
    "content": "<?php\n\nuse Swoole\\Thread;\nuse Swoole\\Coroutine\\System;\n\n$args = Thread::getArguments();\n\nif (empty($args)) {\n    Co\\run(function () {\n        echo \"main thread\\n\";\n        $sockets = swoole_coroutine_socketpair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);\n        $thread = new Thread(__FILE__, $sockets);\n        $parent_pipe = $sockets[1];\n        // 收到信号之后向子线程发送指令让子线程退出\n        if (System::waitSignal(SIGTERM)) {\n            echo \"signal term\\n\";\n            $parent_pipe->send('exit');\n        }\n        Co\\go(function () use ($parent_pipe, $thread) {\n            // 从管道中读取子线程退出的信息\n            echo $parent_pipe->recv(8192), PHP_EOL;\n            // 回收子线程\n            $thread->join();\n        });\n    });\n} else {\n    echo \"child thread\\n\";\n    $sockets = $args[0];\n    $child_pipe = $sockets[0];\n    Co\\run(function () use ($child_pipe) {\n        // 收到父线程的指令，开始退出\n        echo $child_pipe->recv(8192), PHP_EOL;\n        // 通知父线程已退出\n        $child_pipe->send('child exit');\n    });\n}\n"
  },
  {
    "path": "examples/thread/socket.php",
    "content": "<?php\n\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Lock;\nuse Swoole\\Thread\\Map;\n\n$args = Thread::getArguments();\n\nif (empty($args)) {\n    $map = new Map();\n    $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n    $map['socket'] = $sock;\n    $thread = new Thread(__FILE__, $map);\n    echo \"main thread\\n\";\n    $thread->join();\n} else {\n    $map = $args[0];\n    $sock = $map['socket'];\n    $retval = socket_connect($sock, '127.0.0.1', 80);\n}\n"
  },
  {
    "path": "examples/thread/test.php",
    "content": "<?php\n$map = new Swoole\\Thread\\Map(2);\necho \"1\\n\";\n$map['uuid'] = uniqid();\n\necho \"2\\n\";\n$map['uuid'] = uniqid();\n\n$o = new stdClass();;\n$o->uuid = uniqid();\n$map['obj'] = $o;\n\nvar_dump($map['obj']);\n\n$s = serialize($map);\nvar_dump(unserialize($s));\n\n"
  },
  {
    "path": "examples/thread/thread_pool.php",
    "content": "<?php\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Queue;\n\n$args = Thread::getArguments();\n$c = 4;\n$n = 128;\n\nif (empty($args)) {\n    $threads = [];\n    $queue = new Queue;\n    for ($i = 0; $i < $c; $i++) {\n        $threads[] = new Thread(__FILE__, $i, $queue);\n    }\n    while ($n--) {\n        $queue->push(base64_encode(random_bytes(16)), Queue::NOTIFY_ONE);\n        usleep(random_int(10000, 100000));\n    }\n    $n = 4;\n    while ($n--) {\n        $queue->push('', Queue::NOTIFY_ONE);\n    }\n    for ($i = 0; $i < $c; $i++) {\n        $threads[$i]->join();\n    }\n    var_dump($queue->count());\n} else {\n    $queue = $args[1];\n    while (1) {\n        $job = $queue->pop(-1);\n        if (!$job) {\n            break;\n        }\n        var_dump($job);\n    }\n}\n"
  },
  {
    "path": "examples/thread/thread_server.php",
    "content": "<?php\n\nuse Swoole\\Http\\Server;\n\n$http = new Server(\"0.0.0.0\", 9503, SWOOLE_THREAD);\n$http->set([\n    'worker_num' => 2,\n    'task_worker_num' => 3,\n    'enable_coroutine' => true,\n    'hook_flags' => SWOOLE_HOOK_ALL,\n//    'trace_flags' => SWOOLE_TRACE_SERVER,\n//    'log_level' => SWOOLE_LOG_TRACE,\n    'init_arguments' => function () use ($http) {\n        $map = new Swoole\\Thread\\Map;\n        return [$map];\n    }\n]);\n\n$http->on('Request', function ($req, $resp) use ($http) {\n//    $resp->end(\"tid=\" . \\Swoole\\Thread::getId() . ', fd=' . $req->fd);\n    if ($req->server['request_uri'] == '/task') {\n        $http->task(['code' => uniqid()]);\n    } elseif ($req->server['request_uri'] == '/stop') {\n        var_dump($http->getWorkerId());\n        var_dump($req->get['worker_id']);\n        $http->stop($req->get['worker_id'] ?? 0);\n    } elseif ($req->server['request_uri'] == '/msg') {\n        $dstWorkerId = random_int(0, 4);\n        if ($dstWorkerId != $http->getWorkerId()) {\n            $http->sendMessage('hello ' . base64_encode(random_bytes(16)), $dstWorkerId);\n            echo \"[worker#\" . $http->getWorkerId() . \"]\\tsend pipe message to \" . $dstWorkerId . \"\\n\";\n        }\n    }\n    $resp->end('hello world');\n});\n\n$http->on('pipeMessage', function ($http, $srcWorkerId, $msg) {\n    echo \"[worker#\" . $http->getWorkerId() . \"]\\treceived pipe message[$msg] from \" . $srcWorkerId . \"\\n\";\n});\n\n$http->addProcess(new \\Swoole\\Process(function () {\n    echo \"user process, id=\" . \\Swoole\\Thread::getId() . \"\\n\";\n    sleep(2);\n}));\n\n$http->on('Task', function ($server, $taskId, $srcWorkerId, $data) {\n    var_dump($taskId, $srcWorkerId, $data);\n    return ['result' => uniqid()];\n});\n\n$http->on('Finish', function ($server, $taskId, $data) {\n    var_dump($taskId, $data);\n});\n\n$http->on('workerStart', function ($serv, $worker_id) {\n    echo \"[#\" . Swoole\\Thread::getId() . \"]\\tWorker#{$worker_id} is started.\\n\";\n});\n\n$http->on('workerStop', function ($serv, $worker_id) {\n    echo \"[#\" . Swoole\\Thread::getId() . \"]\\tWorker#{$worker_id} is stopped.\\n\";\n});\n\n$http->on('workerExit', function (Server $serv, $worker_id) {\n    echo \"[#\" . Swoole\\Thread::getId() . \"]\\tWorker#{$worker_id} is exited, event_num=\" . Swoole\\Coroutine::stats()['event_num'] . \".\\n\";\n});\n\n$http->start();\n"
  },
  {
    "path": "examples/timer/after.php",
    "content": "<?php\nfunction timeout($tm)\n{\n    echo time() . \": Timeout #$tm\\n\";\n    if ($tm == 5)\n    {\n        Swoole\\Timer::after(3000, 'timeout', 7);\n    }\n}\n\n$timer1 = Swoole\\Timer::after(1000, function () {\n    timeout(1);\n    global $timer1, $timer3;\n    Swoole\\Timer::clear($timer1);\n    Swoole\\Timer::clear($timer3);\n});\n\n$timer2 = Swoole\\Timer::after(2000, 'timeout', 2);\n$timer3 = Swoole\\Timer::after(4000, 'timeout', 3);\n$timer4 = Swoole\\Timer::after(8000, 'timeout', 4);\n$timer5 = Swoole\\Timer::after(10000, 'timeout', 5);\n$timer6 = Swoole\\Timer::after(5000, 'timeout', 6);\nvar_dump($timer1, $timer2, $timer3, $timer4, $timer5, $timer6);\n\nSwoole\\Process::signal(SIGTERM, function() {\n\tSwoole\\Event::exit();\n});\n"
  },
  {
    "path": "examples/timer/clear.php",
    "content": "<?php\n$tm1 = Swoole\\Timer::tick(1000, function () {\n    echo \"tick 1000ms. \\n\";\n});\n\nSwoole\\Timer::tick(3000, function ($id) use ($tm1) {\n    echo \"tick , clear\\n\";\n\tSwoole\\Timer::clear($id);\n\tSwoole\\Timer::clear($tm1);\n});\n"
  },
  {
    "path": "examples/timer/enable_coroutine.php",
    "content": "<?php\nswoole_async_set([\n    'enable_coroutine' => false\n]);\nSwoole\\Timer::tick(1000, function () {\n    $uid = Co::getuid();\n    assert($uid === -1);\n    echo \"#{$uid}\\n\";\n});\n"
  },
  {
    "path": "examples/timer/tick.php",
    "content": "<?php\nfunction timeout($tm)\n{\n    echo time() . \": Timeout #$tm\\n\";\n}\n$timer1 = Swoole\\Timer::tick(1000, 'timeout', 1);\n$timer2 = Swoole\\Timer::tick(2000, 'timeout', 2);\n\nSwoole\\Timer::tick(3000, function($id) {\n    timeout($id);\n    //Swoole\\Timer::clear($id);\n    static $remove = true;\n    if ($remove) {\n        global $timer1;\n        Swoole\\Timer::clear($timer1);\n        Swoole\\Timer::tick(7000, 'timeout', 7);\n        $remove = false;\n    }\n});\n\n$timer4 = Swoole\\Timer::tick(4000, 'timeout', 4);\n$timer5 = Swoole\\Timer::tick(5000, 'timeout', 5);\n$timer6 = Swoole\\Timer::tick(6000, 'timeout', 6);\n"
  },
  {
    "path": "examples/tracer/blocking.php",
    "content": "<?php\nCo::set(['hook_flags'=> 0]);\n// php -d swoole.blocking_detection=on -d swoole.blocking_threshold=500000 blocking.php\nfunction  sleep_test()\n{\n    echo \"Start\\n\";\n    sleep(1);\n    echo \"End\\n\";\n}\n\nfunction redis_test()\n{\n    $redis = new Redis();\n    $redis->connect('127.0.0.1', 6379);\n    $result = $redis->blPop('queue_name', 1.5);\n    if ($result) {\n        list($queueName, $value) = $result;\n        echo \"获取到数据: {$value}\\n\";\n    }\n}\n\nfunction main()\n{\n    sleep_test();\n    redis_test();\n}\n\nCo\\run(function () {\n    main();\n});\n"
  },
  {
    "path": "examples/tracer/cli.php",
    "content": "<?php\n\nclass ClassA\n{\n    public $arr;\n    public $str;\n\n    public function __construct()\n    {\n        $this->arr = [];\n        $this->str = '';\n    }\n}\n\nfunction foo(ClassA $obj)\n{\n    $str = str_repeat(\"big string\", 1024);\n    $obj->arr[] = $str;\n    $obj->str .= $str;\n}\n\n$obj = new ClassA();\n$usage = memory_get_usage();\n$n = 100;\nwhile ($n--) {\n    foo($obj);\n}\n\nvar_dump(strlen($obj->str));\nvar_dump(memory_get_usage() - $usage);\nswoole_tracer_leak_detect();\n"
  },
  {
    "path": "examples/tracer/co.php",
    "content": "<?php\nswoole_tracer_prof_begin(['root_path' => __DIR__]);\n\nfunction sleep_n($time)\n{\n    Co::sleep($time);\n}\n\nCo\\run(function () {\n    Co\\go(function () {\n        sleep_n(0.1);\n    });\n\n    Co\\go(function () {\n        sleep_n(0.2);\n    });\n\n    sleep_n(0.3);\n});\n\nswoole_tracer_prof_end('./test.json');"
  },
  {
    "path": "examples/tracer/co_server.php",
    "content": "<?php\n//$arr = [];\n//$http = new Swoole\\Http\\Server(\"0.0.0.0\", 9501);\n//$http->set(array(\n//    'worker_num' => 1,\n//));\n//$http->on('request', function ($request, $response) {\n//startMemleakCheck();\n//    static $i=0;\n//    global $arr;\n//    $arr[] = $i++;\n//    print_r($arr);\n//endMemleakCheck();\n//    $response->end(\"<h1>Hello Swoole. #\".rand(1000, 9999).\"</h1>\");\n//});\n//$http->start();\n//\n//\n//\n//\n\n//class Test\nclass Test\n{\n    public $arr = [];\n\n    //问题的根源是run函数无法结束，所以run的局部变量和此函数所属的对象也应该是全局变量\n    function run()\n    {\n        $locals = '';\n        $this->run2($locals);\n    }\n\n    function run2(&$locals)\n    {\n        global $global1, $global2;\n        $http = new \\Swoole\\Http\\Server(\"0.0.0.0\", 9501, SWOOLE_BASE);\n        $http->set([\n            'worker_num' => 1\n        ]);\n        $http->on(\"start\", function ($server) {\n\n        });\n        $http->on(\"request\", function ($req, $resp) use (&$global1, &$global2, &$locals) {\n            $global2 .= \"2222222222\";\n            $locals .= \"333333333333\";\n            $global1[] = random_bytes(random_int(256, 4096));\n            $this->arr[] = \"444444444\";\n            // var_dump($global1, $global2, $run2var, $this->pro);\n            $resp->end(\"hello world\");\n\n            swoole_tracer_leak_detect(128);\n        });\n\n        $http->start();\n    }\n}\n\n(new Test())->run();\n\n\n"
  },
  {
    "path": "examples/tracer/co_socket.php",
    "content": "<?php\n\n$socket = new Co\\Socket(AF_INET, SOCK_STREAM, 0);\n$socket->bind(\"0.0.0.0\", 9501);\n$socket->listen();\n\ngo(function() use($socket) {\n    while (1) {\n        go(function() use ($socket) {\n            $client = $socket->accept(-1);\n            while (true) {\n                $data = $client->recv();\n                if (empty($data)) {\n                    $client->close();\n                    break;\n                }\n                //do business\n                $client->send(\"server\" . $data);\n            }\n        });\n    }\n});\n"
  },
  {
    "path": "examples/tracer/fn_call.php",
    "content": "<?php\n\nfunction test3($a, $b)\n{\n    usleep(random_int(100, 500) * 1000);\n    return ($a + 3) * ($b + 3);\n}\n\n\nfunction test2()\n{\n    usleep(random_int(100, 500) * 1000);\n    $res = test3(3, 5);\n    print($res . \"\\n\");\n}\n\n\nfunction main()\n{\n    var_dump(__FUNCTION__);\n    test2();\n    $o = new T;\n    $o->method_test();\n    call_user_func('test4');\n}\n\nfunction test4()\n{\n    usleep(random_int(100, 500) * 1000);\n    var_dump(time());\n}\n\nclass T\n{\n    function method_test()\n    {\n        usleep(random_int(100, 500) * 1000);\n        var_dump(__METHOD__);\n    }\n}\n\nswoole_tracer_prof_begin(['root_path' => __DIR__]);\nmain();\nvar_dump(swoole_tracer_prof_end('./test.json'));"
  },
  {
    "path": "examples/tracer/gc.php",
    "content": "<?php\n\nconst N = 200;\n\n\nclass testA\n{\n    public $pro;\n}\nfunction foo()\n{\n    var_dump(memory_get_usage());\n    for ($i = 0; $i < N; $i++) {\n        $obj = new testA();\n        $obj->pro = $obj;\n        unset($obj);\n\n        swoole_tracer_leak_detect(64);\n    }\n    var_dump(memory_get_usage());\n}\nfoo();\n"
  },
  {
    "path": "examples/udp/client.php",
    "content": "<?php\n$client = new Swoole\\Client(SWOOLE_SOCK_UDP, SWOOLE_SOCK_SYNC);\n$client->connect('127.0.0.1', 9905);\n$client->send(serialize(['hello' => str_repeat('A', 600), 'rand' => rand(1, 100)]));\necho $client->recv() . \"\\n\";\nsleep(1);\n"
  },
  {
    "path": "examples/udp/server.php",
    "content": "<?php\n$server = new Swoole\\Server('0.0.0.0', 9905, SWOOLE_PROCESS, SWOOLE_SOCK_UDP);\nfor ($i = 0; $i < 20; $i++)\n{\n    $server->listen('0.0.0.0', 9906 + $i, SWOOLE_SOCK_UDP);\n}\n$server->set(['worker_num' => 4]);\n\n$server->on('Packet', function (Swoole\\Server $serv, $data, $addr)\n{\n    $serv->sendto($addr['address'], $addr['port'], \"Swoole: $data\", $addr['server_socket']);\n});\n\n$server->start();\n"
  },
  {
    "path": "examples/unixsock/dgram_client.php",
    "content": "<?php\n$client = new Swoole\\Client(SWOOLE_SOCK_UNIX_DGRAM, SWOOLE_SOCK_SYNC);\nif (!$client->connect(__DIR__ . '/svr.sock', 0, -1))\n{\n    exit(\"connect failed. Error: {$client->errCode}\\n\");\n}\n\n$client->send(\"hello world\\n\");\necho $client->recv();\n$client->close();\n\nsleep(1);\n"
  },
  {
    "path": "examples/unixsock/dgram_server.php",
    "content": "<?php\n$serv = new Swoole\\Server(__DIR__.\"/svr.sock\", 9501, SWOOLE_PROCESS, SWOOLE_UNIX_DGRAM);\n$serv->set(array(\n    //'tcp_defer_accept' => 5,\n    'worker_num' => 1,\n    //'daemonize' => true,\n    //'log_file' => '/tmp/swoole.log'\n));\n//$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n//    echo \"[#\".posix_getpid().\"]\\tClient[$fd]: $data\\n\";\n//    $serv->send($fd, json_encode(array(\"hello\" => $data, \"from\" => $reactor_id)).PHP_EOL);\n//});\n\n$serv->on('Packet', function (Swoole\\Server $serv, $data, $addr) {\n    //echo \"[#\".posix_getpid().\"]\\tClient[{$addr['address']}]: $data\\n\";\n    var_dump($addr);\n    $serv->send($addr['address'], json_encode(array(\"hello\" => $data, \"addr\" => $addr)).PHP_EOL);\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/unixsock/stream_client.php",
    "content": "<?php\n$client = new Swoole\\Client(SWOOLE_SOCK_UNIX_STREAM, SWOOLE_SOCK_SYNC);\nif (!$client->connect(__DIR__.'/svr.sock', 0, -1))\n{\n    exit(\"connect failed. Error: {$client->errCode}\\n\");\n}\n\n$client->send(\"hello world\\n\");\necho $client->recv();\n$client->close();\n\nsleep(1);\n"
  },
  {
    "path": "examples/unixsock/stream_server.php",
    "content": "<?php\n$serv = new Swoole\\Server(__DIR__.\"/svr.sock\", 9501, SWOOLE_BASE, SWOOLE_SOCK_UNIX_STREAM);\n$serv->set(array(\n    //'tcp_defer_accept' => 5,\n    'worker_num' => 1,\n    //'daemonize' => true,\n    //'log_file' => '/tmp/swoole.log'\n));\n\n$serv->on('start', function($serv){\n   chmod($serv->host, 0777);\n});\n\n$serv->on('Connect', function($serv, $fd, $reactorId) {\n   echo \"Connect, client={$fd}\\n\";\n});\n\n$serv->on('Close', function($serv, $fd, $reactorId) {\n    echo \"Close, client={$fd}\\n\";\n});\n\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data)\n{\n    echo \"[#\" . posix_getpid() . \"]\\tClient[$fd]: $data\\n\";\n    $serv->send($fd, json_encode(array(\"hello\" => $data, \"from\" => $reactor_id)) . PHP_EOL);\n});\n\n$serv->start();\n"
  },
  {
    "path": "examples/websocket/client.html",
    "content": "<script>\nvar wsServer = 'ws://127.0.0.1:9501';\nvar websocket = new WebSocket(wsServer);\nwebsocket.onopen = function (evt) {\n\tconsole.log(\"Connected to WebSocket server.\");\n};\n\nwebsocket.onclose = function (evt) {\n\tconsole.log(\"Disconnected\");\n};\n\nwebsocket.onmessage = function (evt) {\n\tconsole.log('Retrieved data from server: ' + evt.data);\n};\n\nwebsocket.onerror = function (evt, e) {\n\tconsole.log('Error occured: ' + evt.data);\n};\n</script>\n"
  },
  {
    "path": "examples/websocket/client.php",
    "content": "<?php\nrequire __DIR__ . '/../../tests/include/api/Swoole\\WebSocket\\Server/websocket_client.php';\n$opt = getopt(\"c:n:k:\");\nprint_r($opt);\nif (empty($opt['c']) || empty($opt['n'])) {\n    echo \"examples:  php client.php -c 100 -n 10000\" . PHP_EOL;\n    return;\n}\n$clients = $opt['c'];\n$count = $opt['n'];\n$size = empty($opt['k']) ? 0 : $opt['k'];\n\n\n$host = '127.0.0.1';\n$prot = 9501;\n\n$client = new WebSocketClient($host, $prot);\n$data = $client->connect();\n//echo $data;\n$data = \"data\";\nif (!empty($size)) {\n    $data = str_repeat(\"A\", $size * 1024);\n}\nfor ($i = 0; $i < $count; $i++) {\n    $client->send(\"hello swoole, number:\" . $i . \" data:\" . $data);\n    $recvData = \"\";\n    //while(1) {\n    $tmp = $client->recv();\n    if (empty($tmp)) {\n        break;\n    }\n    $recvData .= $tmp;\n    //}\n    echo $recvData . \"size:\" . strlen($recvData) . PHP_EOL;\n}\necho PHP_EOL . \"======\" . PHP_EOL;\nsleep(1);\necho 'finish' . PHP_EOL;\n"
  },
  {
    "path": "examples/websocket/server.php",
    "content": "<?php\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Request;\nuse Swoole\\WebSocket\\Server;\n\n//$server = new Swoole\\WebSocket\\Server(\"0.0.0.0\", 9501);\n$server = new Server(\"0.0.0.0\", 9501, SWOOLE_BASE);\n//$server->addlistener('0.0.0.0', 9502, SWOOLE_SOCK_UDP);\n$server->set([\n    // 'worker_num' => 4,\n    // 'task_worker_num' => 4,\n    'websocket_compression' => true,\n]);\n\nfunction user_handshake(Request $request, Response $response)\n{\n    //自定定握手规则，没有设置则用系统内置的（只支持version:13的）\n    if (!isset($request->header['sec-websocket-key']))\n    {\n        //'Bad protocol implementation: it is not RFC6455.'\n        $response->end();\n        return false;\n    }\n    if (0 === preg_match('#^[+/0-9A-Za-z]{21}[AQgw]==$#', $request->header['sec-websocket-key'])\n        || 16 !== strlen(base64_decode($request->header['sec-websocket-key']))\n    )\n    {\n        //Header Sec-WebSocket-Key is illegal;\n        $response->end();\n        return false;\n    }\n\n    $key = base64_encode(sha1($request->header['sec-websocket-key']\n        . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',\n        true));\n    $headers = array(\n        'Upgrade'               => 'websocket',\n        'Connection'            => 'Upgrade',\n        'Sec-WebSocket-Accept'  => $key,\n        'Sec-WebSocket-Version' => '13',\n        'KeepAlive'             => 'off',\n    );\n    foreach ($headers as $key => $val)\n    {\n        $response->header($key, $val);\n    }\n    $response->status(101);\n    $response->end();\n    global $server;\n    $fd = $request->fd;\n    $server->defer(function () use ($fd, $server)\n    {\n        $server->push($fd, \"hello, welcome\\n\");\n    });\n    return true;\n}\n\n//$server->on('handshake', 'user_handshake');\n$server->on('open', function (Server $_server, Request $request) {\n    echo \"server#{$_server->worker_pid}: handshake success with fd#{$request->fd}\\n\";\n    var_dump($_server->exist($request->fd), $_server->getClientInfo($request->fd));\n    $fd = $request->fd;\n//    $_server->tick(2000, function($id) use ($fd, $_server) {\n//        $_send = str_repeat('B', rand(100, 5000));\n//        $ret = $_server->push($fd, $_send);\n//        if (!$ret)\n//        {\n//            var_dump($id);\n//            var_dump($_server->clearTimer($id));\n//        }\n//    });\n});\n\n$server->on('message', function (Server $_server, $frame) {\n    //var_dump($frame->data);\n    echo \"received \" . strlen($frame->data) . \" bytes\\n\";\n    if ($frame->data == \"close\") {\n        $_server->close($frame->fd);\n    } elseif ($frame->data == \"task\") {\n        $_server->task(['go' => 'die']);\n    } else {\n        //echo \"receive from {$frame->fd}:{$frame->data}, opcode:{$frame->opcode}, finish:{$frame->finish}\\n\";\n        // for ($i = 0; $i < 100; $i++)\n        {\n//            $_send = ''\n            $_send = base64_encode(random_bytes(rand(100, 1024)));\n//            $_send = str_repeat('B', rand(100, 800));\n            $_server->push($frame->fd, $_send, SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_FIN | SWOOLE_WEBSOCKET_FLAG_COMPRESS);\n            // echo \"#$i\\tserver sent \" . strlen($_send) . \" byte \\n\";\n        }\n    }\n});\n\n$server->on('close', function ($_server, $fd) {\n    echo \"client {$fd} closed\\n\";\n});\n\n$server->on('task', function ($_server, $worker_id, $task_id, $data)\n{\n    var_dump($worker_id, $task_id, $data);\n    return \"hello world\\n\";\n});\n\n$server->on('finish', function ($_server, $task_id, $result)\n{\n    var_dump($task_id, $result);\n});\n\n$server->on('packet', function ($_server, $data, $client) {\n    echo \"#\".posix_getpid().\"\\tPacket {$data}\\n\";\n    var_dump($client);\n});\n\n$server->on('request', function (Request $request, Response $response) {\n    $response->end(<<<HTML\n<h1>Swoole WebSocket Server</h1>\n<script>\nvar wsServer = 'ws://127.0.0.1:9501';\nvar websocket = new WebSocket(wsServer);\nwebsocket.onopen = function (evt) {\n\tconsole.log(\"Connected to WebSocket server.\");\n};\n\nwebsocket.onclose = function (evt) {\n\tconsole.log(\"Disconnected\");\n};\n\nwebsocket.onmessage = function (evt) {\n\tconsole.log('Retrieved data from server: ' + evt.data);\n};\n\nwebsocket.onerror = function (evt, e) {\n\tconsole.log('Error occured: ' + evt.data);\n};\n</script>\nHTML\n    );\n});\n\n$server->start();\n"
  },
  {
    "path": "examples/www/dir1/file1.txt",
    "content": "dir1/file1"
  },
  {
    "path": "examples/www/dir1/file2.txt",
    "content": "dir1/file2"
  },
  {
    "path": "examples/www/dir2/file1.txt",
    "content": "dir2/file1"
  },
  {
    "path": "examples/www/dir2/file2.txt",
    "content": "dir2/file2.txt"
  },
  {
    "path": "examples/www/dir2/index.txt",
    "content": "dir2/index.txt"
  },
  {
    "path": "examples/www/file1.txt",
    "content": "file1.txt"
  },
  {
    "path": "examples/www/file2.txt",
    "content": "file2"
  },
  {
    "path": "examples/www/index.html",
    "content": "<html>\n<head>\n    <meta charset='UTF-8'>\n</head>\n<body>\n    index.html\n</body>\n</html>"
  },
  {
    "path": "examples/www/index.txt",
    "content": "index.txt"
  },
  {
    "path": "ext-src/php_swoole.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n#include \"php_swoole_cxx.h\"\n#include \"php_swoole_library.h\"\n#include \"php_swoole_process.h\"\n#include \"php_swoole_thread.h\"\n#include \"swoole_iouring.h\"\n\nBEGIN_EXTERN_C()\n#include \"zend_exceptions.h\"\n#include \"zend_extensions.h\"\n\n#include \"ext/pcre/php_pcre.h\"\n#include \"ext/json/php_json.h\"\n#include \"php_open_temporary_file.h\"\n\n#include \"stubs/php_swoole_arginfo.h\"\n#include \"stubs/php_swoole_ex_arginfo.h\"\n#include \"stubs/php_swoole_tracer_arginfo.h\"\n#ifdef SW_STDEXT\n#include \"stubs/php_swoole_stdext_arginfo.h\"\n#endif\n#ifdef SW_HAVE_SSH2LIB\n#include \"stubs/php_swoole_ssh2_arginfo.h\"\n#endif\n#ifdef SW_HAVE_FTP\n#include \"stubs/php_swoole_ftp_arginfo.h\"\n#endif\nEND_EXTERN_C()\n\n#include \"swoole_coroutine.h\"\n#include \"swoole_mime_type.h\"\n#include \"swoole_server.h\"\n#include \"swoole_util.h\"\n#include \"swoole_http2.h\"\n\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <net/if.h>\n#include <ifaddrs.h>\n#include <sys/ioctl.h>\n\n#ifdef SW_USE_CURL\n#include <curl/curl.h>\n#endif\n\n#if defined(__MACH__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)\n#include <net/if_dl.h>\n#endif\n\n#ifdef SW_HAVE_ZLIB\n#include <zlib.h>\n#endif\n\n#ifdef SW_HAVE_BROTLI\n#include <brotli/encode.h>\n#include <brotli/decode.h>\n#endif\n\n#ifdef SW_HAVE_ZSTD\n#include <zstd.h>\n#endif\n\n#ifdef SW_USE_CARES\n#include <ares.h>\n#endif\n\n#ifdef SW_USE_PGSQL\nextern void swoole_libpq_version(char *buf, size_t len);\n#endif\n\nusing swoole::Coroutine;\nusing swoole::Server;\nusing swoole::network::Socket;\n#ifdef SW_USE_IOURING\nusing swoole::Iouring;\n#endif\n\nZEND_DECLARE_MODULE_GLOBALS(swoole)\n\nSW_EXTERN_C_BEGIN\nstatic PHP_FUNCTION(swoole_version);\nstatic PHP_FUNCTION(swoole_cpu_num);\nstatic PHP_FUNCTION(swoole_strerror);\nstatic PHP_FUNCTION(swoole_clear_error);\nstatic PHP_FUNCTION(swoole_errno);\nstatic PHP_FUNCTION(swoole_error_log);\nstatic PHP_FUNCTION(swoole_error_log_ex);\nstatic PHP_FUNCTION(swoole_ignore_error);\nstatic PHP_FUNCTION(swoole_get_local_ip);\nstatic PHP_FUNCTION(swoole_get_local_mac);\nstatic PHP_FUNCTION(swoole_hashcode);\nstatic PHP_FUNCTION(swoole_mime_type_add);\nstatic PHP_FUNCTION(swoole_mime_type_set);\nstatic PHP_FUNCTION(swoole_mime_type_delete);\nstatic PHP_FUNCTION(swoole_mime_type_get);\nstatic PHP_FUNCTION(swoole_mime_type_exists);\nstatic PHP_FUNCTION(swoole_mime_type_list);\nstatic PHP_FUNCTION(swoole_substr_unserialize);\nstatic PHP_FUNCTION(swoole_substr_json_decode);\nstatic PHP_FUNCTION(swoole_internal_call_user_shutdown_begin);\nstatic PHP_FUNCTION(swoole_implicit_fn);\nSW_EXTERN_C_END\n\n#ifdef SW_STDEXT\n#include \"php_swoole_stdext.h\"\n#endif\n\n#ifdef SW_HAVE_SSH2LIB\n#include \"php_swoole_ssh2_def.h\"\n#endif\n\n#ifdef SW_HAVE_FTP\n#include \"php_swoole_ftp_def.h\"\n#endif\n\n// clang-format off\nconst zend_function_entry swoole_functions[] = {\n    PHP_FE(swoole_version,    arginfo_swoole_version)\n    PHP_FE(swoole_cpu_num,    arginfo_swoole_cpu_num)\n    PHP_FE(swoole_last_error, arginfo_swoole_last_error)\n    /*------swoole_async_io------*/\n    PHP_FE(swoole_async_dns_lookup_coro, arginfo_swoole_async_dns_lookup_coro)\n    PHP_FE(swoole_async_set,             arginfo_swoole_async_set)\n    /*------swoole_coroutine------*/\n    PHP_FE(swoole_coroutine_create,      arginfo_swoole_coroutine_create)\n    PHP_FE(swoole_coroutine_defer,       arginfo_swoole_coroutine_defer)\n    PHP_FE(swoole_coroutine_socketpair,  arginfo_swoole_coroutine_socketpair)\n    PHP_FE(swoole_test_kernel_coroutine, arginfo_swoole_test_kernel_coroutine)\n    /*------other-----*/\n    PHP_FE(swoole_client_select,      arginfo_swoole_client_select)\n    PHP_FALIAS(swoole_select,         swoole_client_select, arginfo_swoole_client_select)\n    PHP_FE(swoole_set_process_name,   arginfo_swoole_set_process_name)\n    PHP_FE(swoole_get_local_ip,       arginfo_swoole_get_local_ip)\n    PHP_FE(swoole_get_local_mac,      arginfo_swoole_get_local_mac)\n    PHP_FE(swoole_strerror,           arginfo_swoole_strerror)\n    PHP_FE(swoole_errno,              arginfo_swoole_errno)\n    PHP_FE(swoole_clear_error,        arginfo_swoole_clear_error)\n    PHP_FE(swoole_error_log,          arginfo_swoole_error_log)\n    PHP_FE(swoole_error_log_ex,       arginfo_swoole_error_log_ex)\n    PHP_FE(swoole_ignore_error,       arginfo_swoole_ignore_error)\n    PHP_FE(swoole_hashcode,           arginfo_swoole_hashcode)\n    PHP_FE(swoole_mime_type_add,      arginfo_swoole_mime_type_add)\n    PHP_FE(swoole_mime_type_set,      arginfo_swoole_mime_type_set)\n    PHP_FE(swoole_mime_type_delete,   arginfo_swoole_mime_type_delete)\n    PHP_FE(swoole_mime_type_get,      arginfo_swoole_mime_type_get)\n    PHP_FALIAS(swoole_get_mime_type,  swoole_mime_type_get, arginfo_swoole_mime_type_get)\n    PHP_FE(swoole_mime_type_exists,   arginfo_swoole_mime_type_exists)\n    PHP_FE(swoole_mime_type_list,     arginfo_swoole_mime_type_list)\n    PHP_FE(swoole_clear_dns_cache,    arginfo_swoole_clear_dns_cache)\n    PHP_FE(swoole_substr_unserialize, arginfo_swoole_substr_unserialize)\n    PHP_FE(swoole_substr_json_decode, arginfo_swoole_substr_json_decode)\n    PHP_FE(swoole_internal_call_user_shutdown_begin, arginfo_swoole_internal_call_user_shutdown_begin)\n    // for test\n    PHP_FE(swoole_implicit_fn,           arginfo_swoole_implicit_fn)\n    // for admin server\n    ZEND_FE(swoole_get_objects,          arginfo_swoole_get_objects)\n    ZEND_FE(swoole_get_vm_status,        arginfo_swoole_get_vm_status)\n    ZEND_FE(swoole_get_object_by_handle, arginfo_swoole_get_object_by_handle)\n    ZEND_FE(swoole_name_resolver_lookup, arginfo_swoole_name_resolver_lookup)\n    ZEND_FE(swoole_name_resolver_add,    arginfo_swoole_name_resolver_add)\n    ZEND_FE(swoole_name_resolver_remove, arginfo_swoole_name_resolver_remove)\n    // for stdext\n#ifdef SW_STDEXT\n    ZEND_FE(swoole_call_array_method,    arginfo_swoole_call_array_method)\n    ZEND_FE(swoole_call_string_method,   arginfo_swoole_call_string_method)\n    ZEND_FE(swoole_call_stream_method,   arginfo_swoole_call_stream_method)\n    ZEND_FE(swoole_array_search,         arginfo_swoole_array_search)\n    ZEND_FE(swoole_array_contains,       arginfo_swoole_array_contains)\n    ZEND_FE(swoole_array_join,           arginfo_swoole_array_join)\n    ZEND_FE(swoole_array_key_exists,     arginfo_swoole_array_key_exists)\n    ZEND_FE(swoole_array_map,            arginfo_swoole_array_map)\n    ZEND_FE(swoole_str_split,            arginfo_swoole_str_split)\n    ZEND_FE(swoole_parse_str,            arginfo_swoole_parse_str)\n    ZEND_FE(swoole_hash,                 arginfo_swoole_hash)\n    ZEND_FE(swoole_typed_array,          arginfo_swoole_typed_array)\n    ZEND_FE(swoole_array_is_typed,       arginfo_swoole_array_is_typed)\n    ZEND_FE(swoole_str_is_empty,         arginfo_swoole_str_is_empty)\n    ZEND_FE(swoole_array_is_empty,       arginfo_swoole_array_is_empty)\n    ZEND_FE(swoole_str_match,            arginfo_swoole_str_match)\n    ZEND_FE(swoole_str_match_all,        arginfo_swoole_str_match_all)\n    ZEND_FE(swoole_str_json_decode,      arginfo_swoole_str_json_decode)\n    ZEND_FE(swoole_str_json_decode_to_object, arginfo_swoole_str_json_decode_to_object)\n    ZEND_FE(swoole_str_replace,          arginfo_swoole_str_replace)\n    ZEND_FE(swoole_str_ireplace,         arginfo_swoole_str_ireplace)\n    ZEND_FE(swoole_array_replace_str,    arginfo_swoole_array_replace_str)\n    ZEND_FE(swoole_array_ireplace_str,   arginfo_swoole_array_ireplace_str)\n#endif\n#ifdef SW_HAVE_SSH2LIB\n\tZEND_FE(ssh2_connect, arginfo_ssh2_connect)\n\tZEND_FE(ssh2_disconnect, arginfo_ssh2_disconnect)\n\tZEND_FE(ssh2_methods_negotiated, arginfo_ssh2_methods_negotiated)\n\tZEND_FE(ssh2_fingerprint, arginfo_ssh2_fingerprint)\n\tZEND_FE(ssh2_auth_none, arginfo_ssh2_auth_none)\n\tZEND_FE(ssh2_auth_password, arginfo_ssh2_auth_password)\n\tZEND_FE(ssh2_auth_pubkey_file, arginfo_ssh2_auth_pubkey_file)\n\tZEND_FE(ssh2_auth_pubkey, arginfo_ssh2_auth_pubkey)\n\tZEND_FE(ssh2_auth_hostbased_file, arginfo_ssh2_auth_hostbased_file)\n\tZEND_FE(ssh2_forward_listen, arginfo_ssh2_forward_listen)\n\tZEND_FE(ssh2_forward_accept, arginfo_ssh2_forward_accept)\n\tZEND_FE(ssh2_shell, arginfo_ssh2_shell)\n\tZEND_FE(ssh2_shell_resize, arginfo_ssh2_shell_resize)\n\tZEND_FE(ssh2_exec, arginfo_ssh2_exec)\n\tZEND_FE(ssh2_tunnel, arginfo_ssh2_tunnel)\n\tZEND_FE(ssh2_scp_recv, arginfo_ssh2_scp_recv)\n\tZEND_FE(ssh2_scp_send, arginfo_ssh2_scp_send)\n\tZEND_FE(ssh2_fetch_stream, arginfo_ssh2_fetch_stream)\n\tZEND_FE(ssh2_send_eof, arginfo_ssh2_send_eof)\n\tZEND_FE(ssh2_sftp, arginfo_ssh2_sftp)\n\tZEND_FE(ssh2_sftp_rename, arginfo_ssh2_sftp_rename)\n\tZEND_FE(ssh2_sftp_unlink, arginfo_ssh2_sftp_unlink)\n\tZEND_FE(ssh2_sftp_mkdir, arginfo_ssh2_sftp_mkdir)\n\tZEND_FE(ssh2_sftp_rmdir, arginfo_ssh2_sftp_rmdir)\n\tZEND_FE(ssh2_sftp_chmod, arginfo_ssh2_sftp_chmod)\n\tZEND_FE(ssh2_sftp_stat, arginfo_ssh2_sftp_stat)\n\tZEND_FE(ssh2_sftp_lstat, arginfo_ssh2_sftp_lstat)\n\tZEND_FE(ssh2_sftp_symlink, arginfo_ssh2_sftp_symlink)\n\tZEND_FE(ssh2_sftp_readlink, arginfo_ssh2_sftp_readlink)\n\tZEND_FE(ssh2_sftp_realpath, arginfo_ssh2_sftp_realpath)\n\tZEND_FE(ssh2_publickey_init, arginfo_ssh2_publickey_init)\n\tZEND_FE(ssh2_publickey_add, arginfo_ssh2_publickey_add)\n\tZEND_FE(ssh2_publickey_remove, arginfo_ssh2_publickey_remove)\n\tZEND_FE(ssh2_publickey_list, arginfo_ssh2_publickey_list)\n\tZEND_FE(ssh2_auth_agent, arginfo_ssh2_auth_agent)\n#endif\n#ifdef SW_HAVE_FTP\n\tZEND_FE(ftp_connect, arginfo_ftp_connect)\n#if defined(SW_HAVE_FTP_SSL)\n\tZEND_FE(ftp_ssl_connect, arginfo_ftp_ssl_connect)\n#endif\n\tZEND_FE(ftp_login, arginfo_ftp_login)\n\tZEND_FE(ftp_pwd, arginfo_ftp_pwd)\n\tZEND_FE(ftp_cdup, arginfo_ftp_cdup)\n\tZEND_FE(ftp_chdir, arginfo_ftp_chdir)\n\tZEND_FE(ftp_exec, arginfo_ftp_exec)\n\tZEND_FE(ftp_raw, arginfo_ftp_raw)\n\tZEND_FE(ftp_mkdir, arginfo_ftp_mkdir)\n\tZEND_FE(ftp_rmdir, arginfo_ftp_rmdir)\n\tZEND_FE(ftp_chmod, arginfo_ftp_chmod)\n\tZEND_FE(ftp_alloc, arginfo_ftp_alloc)\n\tZEND_FE(ftp_nlist, arginfo_ftp_nlist)\n\tZEND_FE(ftp_rawlist, arginfo_ftp_rawlist)\n\tZEND_FE(ftp_mlsd, arginfo_ftp_mlsd)\n\tZEND_FE(ftp_systype, arginfo_ftp_systype)\n\tZEND_FE(ftp_fget, arginfo_ftp_fget)\n\tZEND_FE(ftp_nb_fget, arginfo_ftp_nb_fget)\n\tZEND_FE(ftp_pasv, arginfo_ftp_pasv)\n\tZEND_FE(ftp_get, arginfo_ftp_get)\n\tZEND_FE(ftp_nb_get, arginfo_ftp_nb_get)\n\tZEND_FE(ftp_nb_continue, arginfo_ftp_nb_continue)\n\tZEND_FE(ftp_fput, arginfo_ftp_fput)\n\tZEND_FE(ftp_nb_fput, arginfo_ftp_nb_fput)\n\tZEND_FE(ftp_put, arginfo_ftp_put)\n\tZEND_FE(ftp_append, arginfo_ftp_append)\n\tZEND_FE(ftp_nb_put, arginfo_ftp_nb_put)\n\tZEND_FE(ftp_size, arginfo_ftp_size)\n\tZEND_FE(ftp_mdtm, arginfo_ftp_mdtm)\n\tZEND_FE(ftp_rename, arginfo_ftp_rename)\n\tZEND_FE(ftp_delete, arginfo_ftp_delete)\n\tZEND_FE(ftp_site, arginfo_ftp_site)\n\tZEND_FE(ftp_close, arginfo_ftp_close)\n#if PHP_VERSION_ID >= 80400\n\tZEND_RAW_FENTRY(\"ftp_quit\", zif_ftp_close, arginfo_ftp_quit, 0, NULL, NULL)\n#else\n\tZEND_RAW_FENTRY(\"ftp_quit\", zif_ftp_close, arginfo_ftp_quit, 0)\n#endif\n\tZEND_FE(ftp_set_option, arginfo_ftp_set_option)\n\tZEND_FE(ftp_get_option, arginfo_ftp_get_option)\n#endif\n    ZEND_FE(swoole_tracer_leak_detect,   arginfo_swoole_tracer_leak_detect)\n    ZEND_FE(swoole_tracer_prof_begin,    arginfo_swoole_tracer_prof_begin)\n    ZEND_FE(swoole_tracer_prof_end,      arginfo_swoole_tracer_prof_end)\n    PHP_FE_END /* Must be the last line in swoole_functions[] */\n};\n\nstatic const zend_module_dep swoole_deps[] = {\n    ZEND_MOD_REQUIRED(\"json\")\n#ifdef SW_USE_MYSQLND\n    ZEND_MOD_REQUIRED(\"mysqlnd\")\n#endif\n#ifdef SW_SOCKETS\n    ZEND_MOD_REQUIRED(\"sockets\")\n#endif\n#ifdef SW_USE_CURL\n    ZEND_MOD_REQUIRED(\"curl\")\n#endif\n#if defined(SW_USE_PGSQL) || defined(SW_USE_ORACLE) || defined(SW_USE_SQLITE) || defined(SW_USE_FIREBIRD)\n    ZEND_MOD_REQUIRED(\"pdo\")\n#endif\n#ifdef SW_HAVE_FTP\n\tZEND_MOD_CONFLICTS(\"ftp\")\n#endif\n#ifdef SW_HAVE_SSH2LIB\n\tZEND_MOD_CONFLICTS(\"ssh2\")\n#endif\n\n    ZEND_MOD_END\n};\n\nzend_module_entry swoole_module_entry = {\n    STANDARD_MODULE_HEADER_EX,\n    nullptr,\n    swoole_deps,\n    \"swoole\",\n    swoole_functions,\n    PHP_MINIT(swoole),\n    PHP_MSHUTDOWN(swoole),\n    PHP_RINIT(swoole),     //RINIT\n    PHP_RSHUTDOWN(swoole), //RSHUTDOWN\n    PHP_MINFO(swoole),\n    PHP_SWOOLE_VERSION,\n    STANDARD_MODULE_PROPERTIES\n};\n// clang-format on\n\nzend_class_entry *swoole_exception_ce;\nzend_object_handlers swoole_exception_handlers;\n\nzend_class_entry *swoole_error_ce;\nzend_object_handlers swoole_error_handlers;\n\n#ifdef COMPILE_DL_SWOOLE\n#ifdef ZTS\nZEND_TSRMLS_CACHE_DEFINE()\n#endif\nZEND_GET_MODULE(swoole)\n#endif\n\n// clang-format off\nPHP_INI_BEGIN()\nSTD_ZEND_INI_BOOLEAN(\"swoole.enable_library\", \"On\", PHP_INI_ALL, OnUpdateBool, enable_library, zend_swoole_globals, swoole_globals)\nSTD_ZEND_INI_BOOLEAN(\"swoole.enable_fiber_mock\", \"Off\", PHP_INI_ALL, OnUpdateBool, enable_fiber_mock, zend_swoole_globals, swoole_globals)\nSTD_ZEND_INI_BOOLEAN(\"swoole.enable_preemptive_scheduler\", \"Off\", PHP_INI_ALL, OnUpdateBool, enable_preemptive_scheduler, zend_swoole_globals, swoole_globals)\nSTD_ZEND_INI_BOOLEAN(\"swoole.display_errors\", \"On\", PHP_INI_ALL, OnUpdateBool, display_errors, zend_swoole_globals, swoole_globals)\nSTD_ZEND_INI_BOOLEAN(\"swoole.use_shortname\", \"On\", PHP_INI_SYSTEM, OnUpdateBool, use_shortname, zend_swoole_globals, swoole_globals)\nSTD_PHP_INI_ENTRY(\"swoole.socket_buffer_size\", ZEND_TOSTR(SW_SOCKET_BUFFER_SIZE), PHP_INI_ALL, OnUpdateLong, socket_buffer_size, zend_swoole_globals, swoole_globals)\nSTD_ZEND_INI_BOOLEAN(\"swoole.blocking_detection\", \"Off\", PHP_INI_SYSTEM, OnUpdateBool, blocking_detection, zend_swoole_globals, swoole_globals)\nSTD_PHP_INI_ENTRY(\"swoole.blocking_threshold\", \"100000\", PHP_INI_SYSTEM, OnUpdateLong, blocking_threshold, zend_swoole_globals, swoole_globals)\nSTD_ZEND_INI_BOOLEAN(\"swoole.profile\", \"Off\", PHP_INI_SYSTEM, OnUpdateBool, profile, zend_swoole_globals, swoole_globals)\nSTD_ZEND_INI_BOOLEAN(\"swoole.leak_detection\", \"Off\", PHP_INI_SYSTEM, OnUpdateBool, leak_detection, zend_swoole_globals, swoole_globals)\nPHP_INI_END()\n// clang-format on\n\nstatic void php_swoole_init_globals(zend_swoole_globals *swoole_globals) {\n    swoole_globals->enable_library = true;\n    swoole_globals->enable_fiber_mock = false;\n    swoole_globals->enable_preemptive_scheduler = false;\n    swoole_globals->socket_buffer_size = SW_SOCKET_BUFFER_SIZE;\n    swoole_globals->display_errors = true;\n    swoole_globals->use_shortname = true;\n    swoole_globals->in_autoload = nullptr;\n    swoole_globals->blocking_detection = false;\n    swoole_globals->blocking_threshold = 100000;\n    swoole_globals->profile = false;\n    swoole_globals->leak_detection = false;\n\n    if (strcmp(\"cli\", sapi_module.name) == 0 || strcmp(\"phpdbg\", sapi_module.name) == 0 ||\n        strcmp(\"embed\", sapi_module.name) == 0 || strcmp(\"micro\", sapi_module.name) == 0) {\n        swoole_globals->cli = true;\n    }\n}\n\nvoid php_swoole_register_shutdown_function(const char *function) {\n#if PHP_VERSION_ID >= 80500\n    php_shutdown_function_entry shutdown_function_entry = {\n        .fci_cache = empty_fcall_info_cache,\n        .params = NULL,\n        .param_count = 0,\n    };\n    auto fn_len = strlen(function);\n    auto fn_entry = (zend_function *) zend_hash_str_find_ptr(EG(function_table), function, fn_len);\n    assert(fn_entry);\n    shutdown_function_entry.fci_cache.function_handler = fn_entry;\n    register_user_shutdown_function(function, fn_len, &shutdown_function_entry);\n#else\n    php_shutdown_function_entry shutdown_function_entry;\n    zval function_name;\n    // In the user_shutdown_function_dtor function, the memory for the function name will be released,\n    // so must not free it manually here.\n    ZVAL_STRING(&function_name, function);\n    zend_fcall_info_init(\n        &function_name, 0, &shutdown_function_entry.fci, &shutdown_function_entry.fci_cache, nullptr, nullptr);\n    register_user_shutdown_function(Z_STRVAL(function_name), Z_STRLEN(function_name), &shutdown_function_entry);\n#endif\n}\n\nvoid php_swoole_set_global_option(HashTable *vht) {\n    zval *ztmp;\n\n#ifdef SW_DEBUG\n    if (php_swoole_array_get_value(vht, \"debug_mode\", ztmp) && zval_is_true(ztmp)) {\n        swoole_set_log_level(0);\n    }\n#endif\n    // [EventLoop]\n    // ======================================================================\n    if (php_swoole_array_get_value(vht, \"enable_signalfd\", ztmp)) {\n        SwooleG.enable_signalfd = zval_is_true(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"enable_kqueue\", ztmp)) {\n        SwooleG.enable_kqueue = zval_is_true(ztmp);\n    }\n    // [Logger]\n    // ======================================================================\n    if (php_swoole_array_get_value(vht, \"trace_flags\", ztmp)) {\n        swoole_set_trace_flags(zval_get_long(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"log_file\", ztmp)) {\n        swoole_set_log_file(zend::String(ztmp).val());\n    }\n    if (php_swoole_array_get_value(vht, \"log_level\", ztmp)) {\n        swoole_set_log_level(zval_get_long(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"log_date_format\", ztmp)) {\n        sw_logger()->set_date_format(zend::String(ztmp).val());\n    }\n    if (php_swoole_array_get_value(vht, \"log_date_with_microseconds\", ztmp)) {\n        sw_logger()->set_date_with_microseconds(zval_is_true(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"log_rotation\", ztmp)) {\n        sw_logger()->set_rotation(zval_get_long(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"display_errors\", ztmp)) {\n        SWOOLE_G(display_errors) = zval_is_true(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"print_backtrace_on_error\", ztmp)) {\n#if !defined(HAVE_BOOST_STACKTRACE) && !defined(HAVE_EXECINFO)\n        zend_throw_exception(\n            swoole_error_ce,\n            \"The `print_backtrace_on_error` option requires `boost stacktrace` or `execinfo.h` to be installed\",\n            SW_ERROR_OPERATION_NOT_SUPPORT);\n#else\n        SwooleG.print_backtrace_on_error = zval_is_true(ztmp);\n#endif\n    }\n    // [DNS]\n    // ======================================================================\n    if (php_swoole_array_get_value(vht, \"dns_server\", ztmp)) {\n        swoole_set_dns_server(zend::String(ztmp).to_std_string());\n    }\n    // [Socket]\n    // ======================================================================\n    auto timeout_format = [](zval *v) -> double {\n        double timeout = zval_get_double(v);\n        if (timeout <= 0 || timeout > INT_MAX) {\n            return INT_MAX;\n        } else {\n            return timeout;\n        }\n    };\n    if (php_swoole_array_get_value(vht, \"socket_dns_timeout\", ztmp)) {\n        Socket::default_dns_timeout = timeout_format(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"socket_connect_timeout\", ztmp)) {\n        Socket::default_connect_timeout = timeout_format(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"socket_write_timeout\", ztmp) ||\n        php_swoole_array_get_value(vht, \"socket_send_timeout\", ztmp)) {\n        Socket::default_write_timeout = timeout_format(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"socket_read_timeout\", ztmp) ||\n        php_swoole_array_get_value(vht, \"socket_recv_timeout\", ztmp)) {\n        Socket::default_read_timeout = timeout_format(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"socket_buffer_size\", ztmp)) {\n        Socket::default_buffer_size = php_swoole_parse_to_size(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"socket_timeout\", ztmp)) {\n        Socket::default_read_timeout = Socket::default_write_timeout = timeout_format(ztmp);\n    }\n    // [HTTP2]\n    // ======================================================================\n    if (php_swoole_array_get_value(vht, \"http2_header_table_size\", ztmp)) {\n        swoole::http2::put_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE, php_swoole_parse_to_size(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"http2_enable_push\", ztmp)) {\n        swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH, zval_get_long(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"http2_max_concurrent_streams\", ztmp)) {\n        swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, zval_get_long(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"http2_init_window_size\", ztmp)) {\n        swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE, php_swoole_parse_to_size(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"http2_max_frame_size\", ztmp)) {\n        swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE, php_swoole_parse_to_size(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"http2_max_header_list_size\", ztmp)) {\n        swoole::http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, php_swoole_parse_to_size(ztmp));\n    }\n}\n\nSW_API bool php_swoole_is_enable_coroutine() {\n    if (sw_server()) {\n        return sw_server()->is_enable_coroutine();\n    } else {\n        return SwooleG.enable_coroutine;\n    }\n}\n\nSW_API zend_long php_swoole_parse_to_size(zval *zv) {\n    if (ZVAL_IS_STRING(zv)) {\n        zend_string *errstr;\n        auto size = zend_ini_parse_quantity(Z_STR_P(zv), &errstr);\n        if (errstr) {\n            php_swoole_fatal_error(\n                E_ERROR, \"failed to parse '%s' to size, Error: %s\", Z_STRVAL_P(zv), ZSTR_VAL(errstr));\n            zend_string_release(errstr);\n        }\n        return size;\n    } else {\n        return zval_get_long(zv);\n    }\n}\n\nSW_API zend_string *php_swoole_serialize(zval *zdata) {\n    php_serialize_data_t var_hash;\n    smart_str serialized_data = {};\n\n    PHP_VAR_SERIALIZE_INIT(var_hash);\n    php_var_serialize(&serialized_data, zdata, &var_hash);\n    PHP_VAR_SERIALIZE_DESTROY(var_hash);\n\n    zend_string *result = nullptr;\n    if (!EG(exception)) {\n        result = zend_string_init(serialized_data.s->val, serialized_data.s->len, true);\n    }\n    smart_str_free(&serialized_data);\n    return result;\n}\n\nSW_API bool php_swoole_unserialize(const zend_string *data, zval *zv) {\n    php_unserialize_data_t var_hash;\n    const char *p = ZSTR_VAL(data);\n    size_t l = ZSTR_LEN(data);\n\n    PHP_VAR_UNSERIALIZE_INIT(var_hash);\n    zend_bool unserialized = php_var_unserialize(zv, (const uchar **) &p, (const uchar *) (p + l), &var_hash);\n    PHP_VAR_UNSERIALIZE_DESTROY(var_hash);\n    if (!unserialized) {\n        swoole_warning(\"unserialize() failed, Error at offset \" ZEND_LONG_FMT \" of %zd bytes\",\n                       (zend_long) ((char *) p - ZSTR_VAL(data)),\n                       l);\n    }\n    return unserialized;\n}\n\nstatic void fatal_error(int code, const char *format, ...) {\n    va_list args;\n    va_start(args, format);\n    zend_object *exception =\n        zend_throw_exception(swoole_error_ce, swoole::std_string::vformat(format, args).c_str(), code);\n    va_end(args);\n\n    zend::print_error(exception, E_ERROR);\n\n    if (code == SW_ERROR_CO_HAS_BEEN_BOUND) {\n        fprintf(stderr,\n                \"\\n [Coroutine-%ld] Stack trace:\"\n                \"\\n -------------------------------------------------------------------\"\n                \"\\n\",\n                Coroutine::get_socket_bound_cid());\n        sw_php_print_backtrace(Coroutine::get_socket_bound_cid());\n    }\n\n#ifdef SW_THREAD\n    if (!tsrm_is_main_thread()) {\n        php_swoole_thread_bailout();\n    }\n#endif\n    swoole_exit(255);\n}\n\nstatic void print_backtrace() {\n    fprintf(stderr, \"\\nStack trace:\\n\");\n    sw_php_print_backtrace_impl(0);\n}\n\nstatic void bug_report_message_init() {\n    SwooleG.bug_report_message += swoole::std_string::format(\"PHP_VERSION : %s\\n\", PHP_VERSION);\n}\n\nstatic int g_module_number_;\n\nint sw_module_number() {\n    return g_module_number_;\n}\n\n/* {{{ PHP_MINIT_FUNCTION\n */\nPHP_MINIT_FUNCTION(swoole) {\n    ZEND_INIT_MODULE_GLOBALS(swoole, php_swoole_init_globals, nullptr);\n    REGISTER_INI_ENTRIES();\n\n    g_module_number_ = module_number;\n\n    // clang-format off\n    // MUST be on the same line for the inspection tool to recognize correctly\n    SW_REGISTER_STRING_CONSTANT(\"SWOOLE_VERSION\", SWOOLE_VERSION);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_VERSION_ID\", SWOOLE_VERSION_ID);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_MAJOR_VERSION\", SWOOLE_MAJOR_VERSION);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_MINOR_VERSION\", SWOOLE_MINOR_VERSION);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_RELEASE_VERSION\", SWOOLE_RELEASE_VERSION);\n    SW_REGISTER_STRING_CONSTANT(\"SWOOLE_EXTRA_VERSION\", SWOOLE_EXTRA_VERSION);\n#ifndef SW_DEBUG\n    SW_REGISTER_BOOL_CONSTANT(\"SWOOLE_DEBUG\", 0);\n#else\n    SW_REGISTER_BOOL_CONSTANT(\"SWOOLE_DEBUG\", 1);\n#endif\n\n#ifdef SW_HAVE_COMPRESSION\n    SW_REGISTER_BOOL_CONSTANT(\"SWOOLE_HAVE_COMPRESSION\", 1);\n#endif\n#ifdef SW_HAVE_ZLIB\n    SW_REGISTER_BOOL_CONSTANT(\"SWOOLE_HAVE_ZLIB\", 1);\n#endif\n#ifdef SW_HAVE_BROTLI\n    SW_REGISTER_BOOL_CONSTANT(\"SWOOLE_HAVE_BROTLI\", 1);\n#endif\n    SW_REGISTER_BOOL_CONSTANT(\"SWOOLE_USE_HTTP2\", 1);\n    SW_REGISTER_BOOL_CONSTANT(\"SWOOLE_USE_SHORTNAME\", SWOOLE_G(use_shortname));\n\n    /**\n     * socket type\n     */\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SOCK_TCP\", SW_SOCK_TCP);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SOCK_TCP6\", SW_SOCK_TCP6);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SOCK_UDP\", SW_SOCK_UDP);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SOCK_UDP6\", SW_SOCK_UDP6);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SOCK_UNIX_DGRAM\", SW_SOCK_UNIX_DGRAM);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SOCK_UNIX_STREAM\", SW_SOCK_UNIX_STREAM);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SOCK_RAW\", SW_SOCK_RAW);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SOCK_RAW6\", SW_SOCK_RAW6);\n\n    /**\n     * simple socket type alias\n     */\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TCP\", SW_SOCK_TCP);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TCP6\", SW_SOCK_TCP6);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_UDP\", SW_SOCK_UDP);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_UDP6\", SW_SOCK_UDP6);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_UNIX_DGRAM\", SW_SOCK_UNIX_DGRAM);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_UNIX_STREAM\", SW_SOCK_UNIX_STREAM);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_RAW\", SW_SOCK_RAW);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_RAW6\", SW_SOCK_RAW6);\n\n    /**\n     * simple api\n     */\n    SW_REGISTER_BOOL_CONSTANT(\"SWOOLE_SOCK_SYNC\", 0);\n    SW_REGISTER_BOOL_CONSTANT(\"SWOOLE_SOCK_ASYNC\", 1);\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SSL\", SW_SOCK_SSL);\n\n    /**\n     * SSL methods\n     */\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SSLv3_METHOD\", SW_SSLv3_METHOD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SSLv3_SERVER_METHOD\", SW_SSLv3_SERVER_METHOD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SSLv3_CLIENT_METHOD\", SW_SSLv3_CLIENT_METHOD);\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TLSv1_METHOD\", SW_TLSv1_METHOD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TLSv1_SERVER_METHOD\", SW_TLSv1_SERVER_METHOD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TLSv1_CLIENT_METHOD\", SW_TLSv1_CLIENT_METHOD);\n\n#ifdef TLS1_1_VERSION\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TLSv1_1_METHOD\", SW_TLSv1_1_METHOD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TLSv1_1_SERVER_METHOD\", SW_TLSv1_1_SERVER_METHOD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TLSv1_1_CLIENT_METHOD\", SW_TLSv1_1_CLIENT_METHOD);\n#endif\n\n#ifdef TLS1_2_VERSION\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TLSv1_2_METHOD\", SW_TLSv1_2_METHOD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TLSv1_2_SERVER_METHOD\", SW_TLSv1_2_SERVER_METHOD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TLSv1_2_CLIENT_METHOD\", SW_TLSv1_2_CLIENT_METHOD);\n#endif\n\n#ifdef SW_SUPPORT_DTLS\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_DTLS_SERVER_METHOD\", SW_DTLS_SERVER_METHOD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_DTLS_CLIENT_METHOD\", SW_DTLS_CLIENT_METHOD);\n#endif\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SSLv23_METHOD\", SW_SSLv23_METHOD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SSLv23_SERVER_METHOD\", SW_SSLv23_SERVER_METHOD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SSLv23_CLIENT_METHOD\", SW_SSLv23_CLIENT_METHOD);\n    /* SSLv23_method have been renamed to TLS_method */\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TLS_METHOD\", SW_SSLv23_METHOD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TLS_SERVER_METHOD\", SW_SSLv23_SERVER_METHOD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TLS_CLIENT_METHOD\", SW_SSLv23_CLIENT_METHOD);\n\n    /**\n     * SSL protocols\n     */\n#ifdef HAVE_SSL3\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SSL_SSLv3\", SW_SSL_SSLv3);\n#endif\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SSL_TLSv1\", SW_SSL_TLSv1);\n#ifdef TLS1_1_VERSION\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SSL_TLSv1_1\", SW_SSL_TLSv1_1);\n#endif\n#ifdef TLS1_2_VERSION\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SSL_TLSv1_2\", SW_SSL_TLSv1_2);\n#endif\n#ifdef TLS1_3_VERSION\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SSL_TLSv1_3\", SW_SSL_TLSv1_3);\n#endif\n#ifdef SW_SUPPORT_DTLS\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SSL_DTLS\", SW_SSL_DTLS);\n#endif\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SSL_SSLv2\", SW_SSL_SSLv2);\n\n    /**\n     * Register event constants\n     */\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_EVENT_READ\", SW_EVENT_READ);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_EVENT_WRITE\", SW_EVENT_WRITE);\n\n    /**\n     * Register ERROR types\n     */\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_STRERROR_SYSTEM\", SW_STRERROR_SYSTEM);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_STRERROR_GAI\", SW_STRERROR_GAI);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_STRERROR_DNS\", SW_STRERROR_DNS);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_STRERROR_SWOOLE\", SW_STRERROR_SWOOLE);\n\n    /**\n     * Register ERROR constants\n     */\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_MALLOC_FAIL\", SW_ERROR_MALLOC_FAIL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SYSTEM_CALL_FAIL\", SW_ERROR_SYSTEM_CALL_FAIL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_PHP_FATAL_ERROR\", SW_ERROR_PHP_FATAL_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_NAME_TOO_LONG\", SW_ERROR_NAME_TOO_LONG);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_INVALID_PARAMS\", SW_ERROR_INVALID_PARAMS);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_QUEUE_FULL\", SW_ERROR_QUEUE_FULL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_OPERATION_NOT_SUPPORT\", SW_ERROR_OPERATION_NOT_SUPPORT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_PROTOCOL_ERROR\", SW_ERROR_PROTOCOL_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_WRONG_OPERATION\", SW_ERROR_WRONG_OPERATION);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_PHP_RUNTIME_NOTICE\", SW_ERROR_PHP_RUNTIME_NOTICE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_FOR_TEST\", SW_ERROR_FOR_TEST);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_NO_PAYLOAD\", SW_ERROR_NO_PAYLOAD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_UNDEFINED_BEHAVIOR\", SW_ERROR_UNDEFINED_BEHAVIOR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_NOT_THREAD_SAFETY\", SW_ERROR_NOT_THREAD_SAFETY);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_FILE_NOT_EXIST\", SW_ERROR_FILE_NOT_EXIST);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_FILE_TOO_LARGE\", SW_ERROR_FILE_TOO_LARGE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_FILE_EMPTY\", SW_ERROR_FILE_EMPTY);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_DIR_NOT_EXIST\", SW_ERROR_DIR_NOT_EXIST);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_DNSLOOKUP_DUPLICATE_REQUEST\", SW_ERROR_DNSLOOKUP_DUPLICATE_REQUEST);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED\", SW_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT\", SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_DNSLOOKUP_UNSUPPORTED\", SW_ERROR_DNSLOOKUP_UNSUPPORTED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_DNSLOOKUP_NO_SERVER\", SW_ERROR_DNSLOOKUP_NO_SERVER);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_BAD_IPV6_ADDRESS\", SW_ERROR_BAD_IPV6_ADDRESS);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_UNREGISTERED_SIGNAL\", SW_ERROR_UNREGISTERED_SIGNAL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_BAD_HOST_ADDR\", SW_ERROR_BAD_HOST_ADDR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_BAD_PORT\", SW_ERROR_BAD_PORT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_BAD_SOCKET_TYPE\", SW_ERROR_BAD_SOCKET_TYPE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_EVENT_REMOVE_FAILED\", SW_ERROR_EVENT_REMOVE_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_EVENT_ADD_FAILED\", SW_ERROR_EVENT_ADD_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_EVENT_UPDATE_FAILED\", SW_ERROR_EVENT_UPDATE_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_EVENT_UNKNOWN_DATA\", SW_ERROR_EVENT_UNKNOWN_DATA);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SESSION_CLOSED_BY_SERVER\", SW_ERROR_SESSION_CLOSED_BY_SERVER);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SESSION_CLOSED_BY_CLIENT\", SW_ERROR_SESSION_CLOSED_BY_CLIENT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SESSION_CLOSING\", SW_ERROR_SESSION_CLOSING);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SESSION_CLOSED\", SW_ERROR_SESSION_CLOSED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SESSION_NOT_EXIST\", SW_ERROR_SESSION_NOT_EXIST);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SESSION_INVALID_ID\", SW_ERROR_SESSION_INVALID_ID);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SESSION_DISCARD_TIMEOUT_DATA\", SW_ERROR_SESSION_DISCARD_TIMEOUT_DATA);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SESSION_DISCARD_DATA\", SW_ERROR_SESSION_DISCARD_DATA);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_OUTPUT_BUFFER_OVERFLOW\", SW_ERROR_OUTPUT_BUFFER_OVERFLOW);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_OUTPUT_SEND_YIELD\", SW_ERROR_OUTPUT_SEND_YIELD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SSL_NOT_READY\", SW_ERROR_SSL_NOT_READY);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SSL_CANNOT_USE_SENFILE\", SW_ERROR_SSL_CANNOT_USE_SENFILE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SSL_EMPTY_PEER_CERTIFICATE\", SW_ERROR_SSL_EMPTY_PEER_CERTIFICATE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SSL_VERIFY_FAILED\", SW_ERROR_SSL_VERIFY_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SSL_BAD_CLIENT\", SW_ERROR_SSL_BAD_CLIENT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SSL_BAD_PROTOCOL\", SW_ERROR_SSL_BAD_PROTOCOL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SSL_RESET\", SW_ERROR_SSL_RESET);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SSL_HANDSHAKE_FAILED\", SW_ERROR_SSL_HANDSHAKE_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SSL_CREATE_CONTEXT_FAILED\", SW_ERROR_SSL_CREATE_CONTEXT_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SSL_CREATE_SESSION_FAILED\", SW_ERROR_SSL_CREATE_SESSION_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_PACKAGE_LENGTH_TOO_LARGE\", SW_ERROR_PACKAGE_LENGTH_TOO_LARGE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_PACKAGE_LENGTH_NOT_FOUND\", SW_ERROR_PACKAGE_LENGTH_NOT_FOUND);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_DATA_LENGTH_TOO_LARGE\", SW_ERROR_DATA_LENGTH_TOO_LARGE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_PACKAGE_MALFORMED_DATA\", SW_ERROR_PACKAGE_MALFORMED_DATA);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_TASK_PACKAGE_TOO_BIG\", SW_ERROR_TASK_PACKAGE_TOO_BIG);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_TASK_DISPATCH_FAIL\", SW_ERROR_TASK_DISPATCH_FAIL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_TASK_TIMEOUT\", SW_ERROR_TASK_TIMEOUT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_HTTP2_STREAM_ID_TOO_BIG\", SW_ERROR_HTTP2_STREAM_ID_TOO_BIG);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_HTTP2_STREAM_NO_HEADER\", SW_ERROR_HTTP2_STREAM_NO_HEADER);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_HTTP2_STREAM_NOT_FOUND\", SW_ERROR_HTTP2_STREAM_NOT_FOUND);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_HTTP2_STREAM_IGNORE\", SW_ERROR_HTTP2_STREAM_IGNORE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED\", SW_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_HTTP2_INTERNAL_ERROR\", SW_ERROR_HTTP2_INTERNAL_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_AIO_BAD_REQUEST\", SW_ERROR_AIO_BAD_REQUEST);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_AIO_CANCELED\", SW_ERROR_AIO_CANCELED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_AIO_TIMEOUT\", SW_ERROR_AIO_TIMEOUT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CLIENT_NO_CONNECTION\", SW_ERROR_CLIENT_NO_CONNECTION);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SOCKET_CLOSED\", SW_ERROR_SOCKET_CLOSED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SOCKET_POLL_TIMEOUT\", SW_ERROR_SOCKET_POLL_TIMEOUT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SOCKET_NOT_EXISTS\", SW_ERROR_SOCKET_NOT_EXISTS);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SOCKS5_UNSUPPORT_VERSION\", SW_ERROR_SOCKS5_UNSUPPORT_VERSION);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SOCKS5_UNSUPPORT_METHOD\", SW_ERROR_SOCKS5_UNSUPPORT_METHOD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SOCKS5_AUTH_FAILED\", SW_ERROR_SOCKS5_AUTH_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SOCKS5_SERVER_ERROR\", SW_ERROR_SOCKS5_SERVER_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SOCKS5_HANDSHAKE_FAILED\", SW_ERROR_SOCKS5_HANDSHAKE_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SOCKS5_CONNECT_FAILED\", SW_ERROR_SOCKS5_CONNECT_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_HTTP_PROXY_HANDSHAKE_ERROR\", SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_HTTP_INVALID_PROTOCOL\", SW_ERROR_HTTP_INVALID_PROTOCOL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_HTTP_PROXY_HANDSHAKE_FAILED\", SW_ERROR_HTTP_PROXY_HANDSHAKE_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_HTTP_PROXY_BAD_RESPONSE\", SW_ERROR_HTTP_PROXY_BAD_RESPONSE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_HTTP_CONFLICT_HEADER\", SW_ERROR_HTTP_CONFLICT_HEADER);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_HTTP_CONTEXT_UNAVAILABLE\", SW_ERROR_HTTP_CONTEXT_UNAVAILABLE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_HTTP_COOKIE_UNAVAILABLE\", SW_ERROR_HTTP_COOKIE_UNAVAILABLE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_WEBSOCKET_BAD_CLIENT\", SW_ERROR_WEBSOCKET_BAD_CLIENT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_WEBSOCKET_BAD_OPCODE\", SW_ERROR_WEBSOCKET_BAD_OPCODE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_WEBSOCKET_UNCONNECTED\", SW_ERROR_WEBSOCKET_UNCONNECTED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_WEBSOCKET_HANDSHAKE_FAILED\", SW_ERROR_WEBSOCKET_HANDSHAKE_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_WEBSOCKET_PACK_FAILED\", SW_ERROR_WEBSOCKET_PACK_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_WEBSOCKET_UNPACK_FAILED\", SW_ERROR_WEBSOCKET_UNPACK_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_WEBSOCKET_INCOMPLETE_PACKET\", SW_ERROR_WEBSOCKET_INCOMPLETE_PACKET);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_MUST_CREATED_BEFORE_CLIENT\", SW_ERROR_SERVER_MUST_CREATED_BEFORE_CLIENT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_TOO_MANY_SOCKET\", SW_ERROR_SERVER_TOO_MANY_SOCKET);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_WORKER_TERMINATED\", SW_ERROR_SERVER_WORKER_TERMINATED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_INVALID_LISTEN_PORT\", SW_ERROR_SERVER_INVALID_LISTEN_PORT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_TOO_MANY_LISTEN_PORT\", SW_ERROR_SERVER_TOO_MANY_LISTEN_PORT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_PIPE_BUFFER_FULL\", SW_ERROR_SERVER_PIPE_BUFFER_FULL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_NO_IDLE_WORKER\", SW_ERROR_SERVER_NO_IDLE_WORKER);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_ONLY_START_ONE\", SW_ERROR_SERVER_ONLY_START_ONE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_SEND_IN_MASTER\", SW_ERROR_SERVER_SEND_IN_MASTER);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_INVALID_REQUEST\", SW_ERROR_SERVER_INVALID_REQUEST);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_CONNECT_FAIL\", SW_ERROR_SERVER_CONNECT_FAIL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_INVALID_COMMAND\", SW_ERROR_SERVER_INVALID_COMMAND);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_IS_NOT_REGULAR_FILE\", SW_ERROR_SERVER_IS_NOT_REGULAR_FILE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT\", SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_INVALID_CALLBACK\", SW_ERROR_SERVER_INVALID_CALLBACK);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_UNRELATED_THREAD\", SW_ERROR_SERVER_UNRELATED_THREAD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_WORKER_EXIT_TIMEOUT\", SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA\", SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_SERVER_WORKER_UNPROCESSED_DATA\", SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_OUT_OF_COROUTINE\", SW_ERROR_CO_OUT_OF_COROUTINE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_HAS_BEEN_BOUND\", SW_ERROR_CO_HAS_BEEN_BOUND);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_HAS_BEEN_DISCARDED\", SW_ERROR_CO_HAS_BEEN_DISCARDED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_MUTEX_DOUBLE_UNLOCK\", SW_ERROR_CO_MUTEX_DOUBLE_UNLOCK);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_BLOCK_OBJECT_LOCKED\", SW_ERROR_CO_BLOCK_OBJECT_LOCKED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_BLOCK_OBJECT_WAITING\", SW_ERROR_CO_BLOCK_OBJECT_WAITING);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_YIELD_FAILED\", SW_ERROR_CO_YIELD_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_GETCONTEXT_FAILED\", SW_ERROR_CO_GETCONTEXT_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_SWAPCONTEXT_FAILED\", SW_ERROR_CO_SWAPCONTEXT_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_MAKECONTEXT_FAILED\", SW_ERROR_CO_MAKECONTEXT_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_IOCPINIT_FAILED\", SW_ERROR_CO_IOCPINIT_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_PROTECT_STACK_FAILED\", SW_ERROR_CO_PROTECT_STACK_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_STD_THREAD_LINK_ERROR\", SW_ERROR_CO_STD_THREAD_LINK_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_DISABLED_MULTI_THREAD\", SW_ERROR_CO_DISABLED_MULTI_THREAD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_CANNOT_CANCEL\", SW_ERROR_CO_CANNOT_CANCEL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_NOT_EXISTS\", SW_ERROR_CO_NOT_EXISTS);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_CANCELED\", SW_ERROR_CO_CANCELED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_TIMEDOUT\", SW_ERROR_CO_TIMEDOUT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_ERROR_CO_SOCKET_CLOSE_WAIT\", SW_ERROR_CO_SOCKET_CLOSE_WAIT);\n\n    /**\n     * trace log\n     */\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_SERVER\", SW_TRACE_SERVER);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_CLIENT\", SW_TRACE_CLIENT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_BUFFER\", SW_TRACE_BUFFER);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_CONN\", SW_TRACE_CONN);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_EVENT\", SW_TRACE_EVENT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_WORKER\", SW_TRACE_WORKER);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_MEMORY\", SW_TRACE_MEMORY);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_REACTOR\", SW_TRACE_REACTOR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_PHP\", SW_TRACE_PHP);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_HTTP\", SW_TRACE_HTTP);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_HTTP2\", SW_TRACE_HTTP2);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_EOF_PROTOCOL\", SW_TRACE_EOF_PROTOCOL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_LENGTH_PROTOCOL\", SW_TRACE_LENGTH_PROTOCOL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_CLOSE\", SW_TRACE_CLOSE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_WEBSOCKET\", SW_TRACE_WEBSOCKET);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_REDIS_CLIENT\", SW_TRACE_REDIS_CLIENT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_MYSQL_CLIENT\", SW_TRACE_MYSQL_CLIENT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_HTTP_CLIENT\", SW_TRACE_HTTP_CLIENT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_AIO\", SW_TRACE_AIO);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_SSL\", SW_TRACE_SSL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_NORMAL\", SW_TRACE_NORMAL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_CHANNEL\", SW_TRACE_CHANNEL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_TIMER\", SW_TRACE_TIMER);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_SOCKET\", SW_TRACE_SOCKET);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_COROUTINE\", SW_TRACE_COROUTINE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_CONTEXT\", SW_TRACE_CONTEXT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_CO_HTTP_SERVER\", SW_TRACE_CO_HTTP_SERVER);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_TABLE\", SW_TRACE_TABLE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_CO_CURL\", SW_TRACE_CO_CURL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_CARES\", SW_TRACE_CARES);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_ZLIB\", SW_TRACE_ZLIB);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_CO_PGSQL\", SW_TRACE_CO_PGSQL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_CO_ODBC\", SW_TRACE_CO_ODBC);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_CO_ORACLE\", SW_TRACE_CO_ORACLE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_CO_SQLITE\", SW_TRACE_CO_SQLITE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_CO_FIREBIRD\", SW_TRACE_CO_FIREBIRD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_CO_SSH2\", SW_TRACE_CO_SSH2);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_THREAD\", SW_TRACE_THREAD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TRACE_ALL\", SW_TRACE_ALL);\n\n    /**\n     * log level\n     */\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_LOG_DEBUG\", SW_LOG_DEBUG);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_LOG_TRACE\", SW_LOG_TRACE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_LOG_INFO\", SW_LOG_INFO);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_LOG_NOTICE\", SW_LOG_NOTICE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_LOG_WARNING\", SW_LOG_WARNING);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_LOG_ERROR\", SW_LOG_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_LOG_NONE\", SW_LOG_NONE);\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_LOG_ROTATION_SINGLE\", SW_LOG_ROTATION_SINGLE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_LOG_ROTATION_MONTHLY\", SW_LOG_ROTATION_MONTHLY);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_LOG_ROTATION_DAILY\", SW_LOG_ROTATION_DAILY);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_LOG_ROTATION_HOURLY\", SW_LOG_ROTATION_HOURLY);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_LOG_ROTATION_EVERY_MINUTE\", SW_LOG_ROTATION_EVERY_MINUTE);\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_IPC_NONE\", SW_IPC_NONE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_IPC_UNIXSOCK\", SW_IPC_UNIXSOCK);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_IPC_SOCKET\", SW_IPC_SOCKET);\n\n    /**\n     * limit\n     */\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_IOV_MAX\", IOV_MAX);\n\n    /**\n     * iouring\n     */\n#ifdef SW_USE_IOURING\n     SW_REGISTER_LONG_CONSTANT(\"SWOOLE_IOURING_DEFAULT\", SW_IOURING_DEFAULT);\n     SW_REGISTER_LONG_CONSTANT(\"SWOOLE_IOURING_SQPOLL\", SW_IOURING_SQPOLL);\n#endif\n\n    // clang-format on\n\n    if (SWOOLE_G(use_shortname)) {\n        SW_FUNCTION_ALIAS(\n            CG(function_table), \"swoole_coroutine_create\", CG(function_table), \"go\", arginfo_swoole_coroutine_create);\n        SW_FUNCTION_ALIAS(\n            CG(function_table), \"swoole_coroutine_defer\", CG(function_table), \"defer\", arginfo_swoole_coroutine_defer);\n#ifdef SW_STDEXT\n        SW_FUNCTION_ALIAS(\n            CG(function_table), \"swoole_typed_array\", CG(function_table), \"typed_array\", arginfo_swoole_typed_array);\n#endif\n    }\n\n    swoole_init();\n\n    // init bug report message\n    bug_report_message_init();\n\n    SW_INIT_CLASS_ENTRY_EX2(\n        swoole_exception, \"Swoole\\\\Exception\", nullptr, nullptr, zend_ce_exception, zend_get_std_object_handlers());\n\n    SW_INIT_CLASS_ENTRY_EX2(\n        swoole_error, \"Swoole\\\\Error\", nullptr, nullptr, zend_ce_error, zend_get_std_object_handlers());\n\n    /** <Sort by dependency> **/\n    php_swoole_event_minit(module_number);\n    // base\n    php_swoole_atomic_minit(module_number);\n    php_swoole_lock_minit(module_number);\n    php_swoole_process_minit(module_number);\n    php_swoole_process_pool_minit(module_number);\n    php_swoole_table_minit(module_number);\n    php_swoole_timer_minit(module_number);\n    // coroutine\n    php_swoole_coroutine_minit(module_number);\n    php_swoole_coroutine_system_minit(module_number);\n    php_swoole_coroutine_scheduler_minit(module_number);\n    php_swoole_coroutine_lock_minit(module_number);\n    php_swoole_channel_coro_minit(module_number);\n    php_swoole_runtime_minit(module_number);\n    // client\n    php_swoole_socket_coro_minit(module_number);\n    php_swoole_client_minit(module_number);\n    php_swoole_client_async_minit(module_number);\n    php_swoole_client_coro_minit(module_number);\n    php_swoole_http_client_coro_minit(module_number);\n    php_swoole_http2_client_coro_minit(module_number);\n#ifdef SW_HAVE_SSH2LIB\n    php_swoole_ssh2_minit(module_number);\n#endif\n#ifdef SW_HAVE_FTP\n    PHP_MINIT(ftp)(type, module_number);\n#endif\n    // server\n    php_swoole_server_minit(module_number);\n    php_swoole_server_port_minit(module_number);\n    php_swoole_http_request_minit(module_number);\n    php_swoole_http_response_minit(module_number);\n    php_swoole_http_cookie_minit(module_number);\n    php_swoole_http_server_minit(module_number);\n    php_swoole_http_server_coro_minit(module_number);\n    php_swoole_websocket_server_minit(module_number);\n    php_swoole_redis_server_minit(module_number);\n    php_swoole_name_resolver_minit(module_number);\n#ifdef SW_USE_PGSQL\n    php_swoole_pgsql_minit(module_number);\n#endif\n#ifdef SW_USE_ODBC\n    php_swoole_odbc_minit(module_number);\n#endif\n#ifdef SW_USE_ORACLE\n    php_swoole_oracle_minit(module_number);\n#endif\n#ifdef SW_USE_SQLITE\n    php_swoole_sqlite_minit(module_number);\n#endif\n#ifdef SW_USE_FIREBIRD\n    php_swoole_firebird_minit(module_number);\n#endif\n#ifdef SW_THREAD\n    php_swoole_thread_minit(module_number);\n    php_swoole_thread_atomic_minit(module_number);\n    php_swoole_thread_lock_minit(module_number);\n    php_swoole_thread_barrier_minit(module_number);\n    php_swoole_thread_queue_minit(module_number);\n    php_swoole_thread_map_minit(module_number);\n    php_swoole_thread_arraylist_minit(module_number);\n#endif\n#ifdef SW_STDEXT\n    php_swoole_stdext_minit(module_number);\n#endif\n    php_swoole_tracer_minit(module_number);\n\n    SwooleG.fatal_error = fatal_error;\n    SwooleG.print_backtrace = print_backtrace;\n\n    Socket::default_buffer_size = SWOOLE_G(socket_buffer_size);\n    SwooleG.dns_cache_refresh_time = 60;\n\n    // enable pcre.jit and use swoole extension on macOS will lead to core dump, disable it temporarily\n#if defined(PHP_PCRE_VERSION) && defined(HAVE_PCRE_JIT_SUPPORT) && __MACH__ && !defined(SW_DEBUG)\n    PCRE_G(jit) = 0;\n#endif\n\n    zend::known_strings_init();\n\n    return SUCCESS;\n}\n/* }}} */\n\n/* {{{ PHP_MSHUTDOWN_FUNCTION\n */\nPHP_MSHUTDOWN_FUNCTION(swoole) {\n    zend::known_strings_dtor();\n\n    php_swoole_runtime_mshutdown();\n#ifdef SW_USE_PGSQL\n    php_swoole_pgsql_mshutdown();\n#endif\n#ifdef SW_USE_ORACLE\n    php_swoole_oracle_mshutdown();\n#endif\n#ifdef SW_USE_SQLITE\n    php_swoole_sqlite_mshutdown();\n#endif\n#ifdef SW_USE_FIREBIRD\n    php_swoole_firebird_mshutdown();\n#endif\n#ifdef SW_HAVE_SSH2LIB\n    php_swoole_ssh2_mshutdown();\n#endif\n\n    swoole_clean();\n\n    return SUCCESS;\n}\n/* }}} */\n\n/* {{{ PHP_MINFO_FUNCTION\n */\nPHP_MINFO_FUNCTION(swoole) {\n    char buf[64];\n    php_info_print_table_start();\n    php_info_print_table_header(2, \"Swoole\", \"enabled\");\n    php_info_print_table_row(2, \"Author\", \"Swoole Team <team@swoole.com>\");\n    php_info_print_table_row(2, \"Version\", SWOOLE_VERSION);\n    snprintf(buf, sizeof(buf), \"%s %s\", __DATE__, __TIME__);\n    php_info_print_table_row(2, \"Built\", buf);\n\n#if SW_BYTE_ORDER == SW_LITTLE_ENDIAN\n    php_info_print_table_row(2, \"host byte order\", \"little endian\");\n#else\n    php_info_print_table_row(2, \"host byte order\", \"big endian\");\n#endif\n\n#if defined(SW_USE_THREAD_CONTEXT)\n    php_info_print_table_row(2, \"coroutine\", \"enabled with thread context\");\n#elif defined(SW_USE_ASM_CONTEXT)\n    php_info_print_table_row(2, \"coroutine\", \"enabled with boost asm context\");\n#else\n    php_info_print_table_row(2, \"coroutine\", \"enabled with ucontext\");\n#endif\n#ifdef SW_DEBUG\n    php_info_print_table_row(2, \"debug\", \"enabled\");\n#endif\n#ifdef SW_LOG_TRACE_OPEN\n    php_info_print_table_row(2, \"trace_log\", \"enabled\");\n#endif\n#ifdef HAVE_EPOLL\n    php_info_print_table_row(2, \"epoll\", \"enabled\");\n#endif\n#ifdef HAVE_EVENTFD\n    php_info_print_table_row(2, \"eventfd\", \"enabled\");\n#endif\n#ifdef SW_THREAD\n    php_info_print_table_row(2, \"thread\", \"enabled\");\n#endif\n#ifdef HAVE_SIGNALFD\n    php_info_print_table_row(2, \"signalfd\", \"enabled\");\n#endif\n#ifdef SW_USE_ACCEPT4\n    php_info_print_table_row(2, \"accept4\", \"enabled\");\n#endif\n#ifdef HAVE_CPU_AFFINITY\n    php_info_print_table_row(2, \"cpu_affinity\", \"enabled\");\n#endif\n#ifdef HAVE_SPINLOCK\n    php_info_print_table_row(2, \"spinlock\", \"enabled\");\n#endif\n#ifdef HAVE_RWLOCK\n    php_info_print_table_row(2, \"rwlock\", \"enabled\");\n#endif\n#ifdef SW_SOCKETS\n    php_info_print_table_row(2, \"sockets\", \"enabled\");\n#endif\n#ifdef OPENSSL_VERSION_TEXT\n    php_info_print_table_row(2, \"openssl\", OPENSSL_VERSION_TEXT);\n#else\n    php_info_print_table_row(2, \"openssl\", \"enabled\");\n#endif\n#ifdef SW_SUPPORT_DTLS\n    php_info_print_table_row(2, \"dtls\", \"enabled\");\n#endif\n    php_info_print_table_row(2, \"http2\", \"enabled\");\n    php_info_print_table_row(2, \"json\", \"enabled\");\n#ifdef SW_USE_CURL\n    php_info_print_table_row(2, \"curl-native\", \"enabled\");\n    curl_version_info_data *d = curl_version_info(CURLVERSION_NOW);\n    php_info_print_table_row(2, \"curl-version\", d->version);\n#endif\n#ifdef SW_USE_CARES\n    php_info_print_table_row(2, \"c-ares\", ares_version(nullptr));\n#endif\n#ifdef SW_HAVE_ZLIB\n#ifdef ZLIB_VERSION\n    php_info_print_table_row(2, \"zlib\", ZLIB_VERSION);\n#else\n    php_info_print_table_row(2, \"zlib\", \"enabled\");\n#endif\n#endif\n#ifdef SW_HAVE_BROTLI\n    snprintf(buf, sizeof(buf), \"E%u/D%u\", BrotliEncoderVersion(), BrotliDecoderVersion());\n    php_info_print_table_row(2, \"brotli\", buf);\n#endif\n#ifdef SW_HAVE_ZSTD\n#ifdef ZSTD_VERSION_NUMBER\n    php_info_print_table_row(2, \"zstd\", ZSTD_VERSION_STRING);\n#else\n    php_info_print_table_row(2, \"zstd\", \"enabled\");\n#endif\n#endif\n#ifdef HAVE_MUTEX_TIMEDLOCK\n    php_info_print_table_row(2, \"mutex_timedlock\", \"enabled\");\n#endif\n#ifdef HAVE_PTHREAD_BARRIER\n    php_info_print_table_row(2, \"pthread_barrier\", \"enabled\");\n#endif\n#ifdef HAVE_FUTEX\n    php_info_print_table_row(2, \"futex\", \"enabled\");\n#endif\n#ifdef SW_USE_MYSQLND\n    php_info_print_table_row(2, \"mysqlnd\", \"enabled\");\n#endif\n#ifdef SW_USE_JEMALLOC\n    php_info_print_table_row(2, \"jemalloc\", \"enabled\");\n#endif\n#ifdef SW_USE_TCMALLOC\n    php_info_print_table_row(2, \"tcmalloc\", \"enabled\");\n#endif\n#ifdef SW_USE_PGSQL\n    char libpq_version[16];\n    swoole_libpq_version(libpq_version, sizeof(libpq_version));\n    php_info_print_table_row(2, \"postgresql(libpq) version\", libpq_version);\n    php_info_print_table_row(2, \"coroutine_pgsql\", \"enabled\");\n#endif\n#ifdef SW_USE_ODBC\n    php_info_print_table_row(2, \"coroutine_odbc\", \"enabled\");\n#endif\n#ifdef SW_USE_ORACLE\n    php_info_print_table_row(2, \"coroutine_oracle\", \"enabled\");\n#endif\n#ifdef SW_USE_SQLITE\n    php_info_print_table_row(2, \"coroutine_sqlite\", \"enabled\");\n#endif\n#ifdef SW_USE_FIREBIRD\n    php_info_print_table_row(2, \"coroutine_firebird\", \"enabled\");\n#endif\n#ifdef SW_USE_IOURING\n    php_info_print_table_row(2, \"io_uring\", \"enabled\");\n#endif\n#ifdef SW_USE_URING_SOCKET\n    php_info_print_table_row(2, \"uring_socket\", \"enabled\");\n#endif\n#ifdef HAVE_BOOST_STACKTRACE\n    php_info_print_table_row(2, \"boost stacktrace\", \"enabled\");\n#elif defined(HAVE_EXECINFO)\n    php_info_print_table_row(2, \"execinfo\", \"enabled\");\n#endif\n#ifdef SW_HAVE_SSH2LIB\n    php_swoole_ssh2_minfo();\n#endif\n#ifdef SW_HAVE_FTP\n    PHP_MINFO(ftp)(zend_module);\n#endif\n    php_info_print_table_end();\n\n    DISPLAY_INI_ENTRIES();\n}\n/* }}} */\n\nstatic void *sw_emalloc(size_t size) {\n    return emalloc(size);\n}\n\nstatic void *sw_ecalloc(size_t nmemb, size_t size) {\n    return ecalloc(nmemb, size);\n}\n\nstatic void *sw_erealloc(void *address, size_t size) {\n    return erealloc(address, size);\n}\n\nstatic void sw_efree(void *address) {\n    efree(address);\n}\n\nstatic void *sw_zend_string_malloc(size_t size) {\n    zend_string *str = zend_string_alloc(size, false);\n    if (str == nullptr) {\n        return nullptr;\n    }\n    return str->val;\n}\n\nstatic void *sw_zend_string_calloc(size_t nmemb, size_t size) {\n    void *mem = sw_zend_string_malloc(nmemb * size);\n    if (mem) {\n        sw_memset_zero(mem, size);\n    }\n    return mem;\n}\n\nstatic void *sw_zend_string_realloc(void *address, size_t size) {\n    zend_string *str = zend_string_realloc(zend::fetch_zend_string_by_val(address), size, false);\n    if (str == nullptr) {\n        return nullptr;\n    }\n    return str->val;\n}\n\nstatic void sw_zend_string_free(void *address) {\n    zend_string_release_ex(zend::fetch_zend_string_by_val(address), false);\n}\n\nstatic swoole::Allocator php_allocator{\n    sw_emalloc,\n    sw_ecalloc,\n    sw_erealloc,\n    sw_efree,\n};\n\nstatic swoole::Allocator zend_string_allocator{\n    sw_zend_string_malloc,\n    sw_zend_string_calloc,\n    sw_zend_string_realloc,\n    sw_zend_string_free,\n};\n\nconst swoole::Allocator *sw_php_allocator() {\n    return &php_allocator;\n}\n\nconst swoole::Allocator *sw_zend_string_allocator() {\n    return &zend_string_allocator;\n}\n\nvoid sw_php_exit(int status) {\n    EG(exit_status) = status;\n#ifdef SW_THREAD\n    php_swoole_thread_bailout();\n#else\n    zend_bailout();\n#endif\n}\n\nbool sw_zval_is_serializable(const zval *struc) {\nagain:\n    switch (Z_TYPE_P(struc)) {\n    case IS_OBJECT: {\n        if (Z_OBJCE_P(struc)->ce_flags & ZEND_ACC_NOT_SERIALIZABLE) {\n            return false;\n        }\n        break;\n    }\n    case IS_ARRAY: {\n        zval *elem;\n        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(struc), elem) {\n            if (!sw_zval_is_serializable(elem)) {\n                return false;\n            }\n        }\n        ZEND_HASH_FOREACH_END();\n        break;\n    }\n    case IS_REFERENCE:\n        struc = Z_REFVAL_P(struc);\n        goto again;\n    default:\n        break;\n    }\n    return true;\n}\n\nstatic void sw_after_fork(void *args) {\n#if PHP_VERSION_ID >= 80500\n    refresh_memory_manager();\n#endif\n#ifdef ZEND_MAX_EXECUTION_TIMERS\n    zend_max_execution_timer_init();\n#endif\n}\n\nPHP_RINIT_FUNCTION(swoole) {\n    if (!SWOOLE_G(cli)) {\n        return SUCCESS;\n    }\n\n    SWOOLE_G(req_status) = PHP_SWOOLE_RINIT_BEGIN;\n\n    // Use `sys_get_temp_dir` to obtain the system temporary file directory.\n    // The default temporary directory is `/tmp`, which does not exist on the Android platform.\n    // The system root path is a read-only mapping.\n    // The temporary file directory under termux is `/data/data/com.termux/files/usr/tmp`\n    swoole_set_task_tmpdir(php_get_temporary_directory());\n\n    php_swoole_register_shutdown_function(\"swoole_internal_call_user_shutdown_begin\");\n\n    if (SWOOLE_G(enable_library)\n#ifdef ZEND_COMPILE_PRELOAD\n        /* avoid execution of the code during RINIT of preloader */\n        && !(CG(compiler_options) & ZEND_COMPILE_PRELOAD)\n#endif\n    ) {\n        // https://github.com/swoole/swoole-src/issues/5182\n        /**\n         * xdebug will hook zend_execute_ex to xdebug_execute_ex.\n         * This would cause php_swoole_load_library function not to execute correctly, so it must be replaced\n         * with the execute_ex function.\n         */\n        void (*old_zend_execute_ex)(zend_execute_data * execute_data) = nullptr;\n        if (UNEXPECTED(zend_execute_ex != execute_ex)) {\n            old_zend_execute_ex = zend_execute_ex;\n            zend_execute_ex = execute_ex;\n        }\n\n        php_swoole_load_library();\n\n        if (UNEXPECTED(old_zend_execute_ex)) {\n            zend_execute_ex = old_zend_execute_ex;\n            old_zend_execute_ex = nullptr;\n        }\n    }\n\n#ifdef ZEND_SIGNALS\n    /* Disable warning even in ZEND_DEBUG because we may register our own signal handlers  */\n    SIGG(check) = false;\n#endif\n\n    swoole_add_hook(SW_GLOBAL_HOOK_AFTER_FORK, sw_after_fork, 0);\n\n    php_swoole_http_server_rinit();\n    php_swoole_coroutine_rinit();\n    php_swoole_runtime_rinit();\n#ifdef SW_USE_ORACLE\n    php_swoole_oracle_rinit();\n#endif\n#ifdef SW_THREAD\n    php_swoole_thread_rinit();\n#endif\n    php_swoole_tracer_rinit();\n\n    SWOOLE_G(req_status) = PHP_SWOOLE_RINIT_END;\n\n    return SUCCESS;\n}\n\nPHP_RSHUTDOWN_FUNCTION(swoole) {\n    if (!SWOOLE_G(cli)) {\n        return SUCCESS;\n    }\n\n    SWOOLE_G(req_status) = PHP_SWOOLE_RSHUTDOWN_BEGIN;\n\n    php_swoole_server_rshutdown();\n    php_swoole_http_server_rshutdown();\n    php_swoole_http_response_rshutdown();\n    php_swoole_async_coro_rshutdown();\n    php_swoole_redis_server_rshutdown();\n    php_swoole_coroutine_rshutdown();\n    php_swoole_coroutine_scheduler_rshutdown();\n    php_swoole_timer_rshutdown();\n    php_swoole_runtime_rshutdown();\n    php_swoole_process_rshutdown();\n#ifdef SW_THREAD\n    php_swoole_thread_rshutdown();\n#endif\n    php_swoole_tracer_rshutdown();\n\n    swoole_event_free();\n\n    SWOOLE_G(req_status) = PHP_SWOOLE_RSHUTDOWN_END;\n\n#ifdef PHP_STREAM_FLAG_NO_CLOSE\n    auto php_swoole_set_stdio_no_close = [](const char *name, size_t name_len) {\n        zval *zstream = zend_get_constant_str(name, name_len);\n        if (!zstream) {\n            return;\n        }\n        auto *stream =\n            (php_stream *) zend_fetch_resource2_ex((zstream), nullptr, php_file_le_stream(), php_file_le_pstream());\n        if (!stream) {\n            return;\n        }\n        stream->flags |= PHP_STREAM_FLAG_NO_CLOSE;\n    };\n    /* do not close the stdout and stderr */\n    php_swoole_set_stdio_no_close(ZEND_STRL(\"STDOUT\"));\n    php_swoole_set_stdio_no_close(ZEND_STRL(\"STDERR\"));\n#endif\n\n    return SUCCESS;\n}\n\nstatic PHP_FUNCTION(swoole_version) {\n    RETURN_STRING(SWOOLE_VERSION);\n}\n\nstatic uint32_t hashkit_one_at_a_time(const char *key, size_t key_length) {\n    const char *ptr = key;\n    uint32_t value = 0;\n\n    while (key_length--) {\n        auto val = (uint32_t) *ptr++;\n        value += val;\n        value += (value << 10);\n        value ^= (value >> 6);\n    }\n    value += (value << 3);\n    value ^= (value >> 11);\n    value += (value << 15);\n\n    return value;\n}\n\nstatic PHP_FUNCTION(swoole_hashcode) {\n    char *data;\n    size_t l_data;\n    zend_long type = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_STRING(data, l_data)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(type)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    switch (type) {\n    case 0:\n        RETURN_LONG(zend_hash_func(data, l_data));\n    case 1:\n        RETURN_LONG(hashkit_one_at_a_time(data, l_data));\n    default:\n        RETURN_FALSE;\n    }\n}\n\nstatic PHP_FUNCTION(swoole_clear_error) {\n    swoole_set_last_error(0);\n}\n\nPHP_FUNCTION(swoole_last_error) {\n    RETURN_LONG(swoole_get_last_error());\n}\n\nstatic PHP_FUNCTION(swoole_cpu_num) {\n    RETURN_LONG(SW_CPU_NUM);\n}\n\nstatic PHP_FUNCTION(swoole_strerror) {\n    zend_long swoole_errno;\n    zend_long error_type = SW_STRERROR_SYSTEM;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_LONG(swoole_errno)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(error_type)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (error_type == SW_STRERROR_GAI) {\n        RETURN_STRING(gai_strerror(swoole_errno));\n    } else if (error_type == SW_STRERROR_DNS) {\n        RETURN_STRING(hstrerror(swoole_errno));\n    } else if (error_type == SW_STRERROR_SWOOLE || (swoole_errno > SW_ERROR_BEGIN && swoole_errno < SW_ERROR_END)) {\n        RETURN_STRING(swoole_strerror(swoole_errno));\n    } else {\n        RETURN_STRING(strerror(swoole_errno));\n    }\n}\n\nstatic PHP_FUNCTION(swoole_error_log) {\n    char *msg;\n    size_t l_msg;\n    zend_long level;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_LONG(level)\n    Z_PARAM_STRING(msg, l_msg)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    sw_logger()->put(level, msg, l_msg);\n}\n\nstatic PHP_FUNCTION(swoole_error_log_ex) {\n    char *msg;\n    size_t l_msg;\n    zend_long level, error;\n\n    ZEND_PARSE_PARAMETERS_START(3, 3)\n    Z_PARAM_LONG(level)\n    Z_PARAM_LONG(error)\n    Z_PARAM_STRING(msg, l_msg)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_error_log(level, (int) error, \"%.*s\", (int) l_msg, msg);\n}\n\nstatic PHP_FUNCTION(swoole_ignore_error) {\n    zend_long error;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(error)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_ignore_error(error);\n}\n\nstatic PHP_FUNCTION(swoole_mime_type_add) {\n    zend_string *suffix;\n    zend_string *mime_type;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_STR(suffix)\n    Z_PARAM_STR(mime_type)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(swoole::mime_type::add(ZSTR_VAL(suffix), ZSTR_VAL(mime_type)));\n}\n\nstatic PHP_FUNCTION(swoole_mime_type_set) {\n    zend_string *suffix;\n    zend_string *mime_type;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_STR(suffix)\n    Z_PARAM_STR(mime_type)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole::mime_type::set(ZSTR_VAL(suffix), ZSTR_VAL(mime_type));\n}\n\nstatic PHP_FUNCTION(swoole_mime_type_delete) {\n    zend_string *suffix;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STR(suffix)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(swoole::mime_type::del(ZSTR_VAL(suffix)));\n}\n\nstatic PHP_FUNCTION(swoole_mime_type_get) {\n    zend_string *filename;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STR(filename)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_STRING(swoole::mime_type::get(ZSTR_VAL(filename)).c_str());\n}\n\nstatic PHP_FUNCTION(swoole_mime_type_exists) {\n    zend_string *filename;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STR(filename)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(swoole::mime_type::exists(ZSTR_VAL(filename)));\n}\n\nstatic PHP_FUNCTION(swoole_mime_type_list) {\n    array_init(return_value);\n    for (auto &i : swoole::mime_type::list()) {\n        add_next_index_string(return_value, i.second.c_str());\n    }\n}\n\nstatic PHP_FUNCTION(swoole_errno) {\n    RETURN_LONG(errno);\n}\n\nPHP_FUNCTION(swoole_set_process_name) {\n    auto *cli_set_process_title = zend::get_function(ZEND_STRL(\"cli_set_process_title\"));\n    if (!cli_set_process_title) {\n        php_swoole_fatal_error(E_WARNING, \"swoole_set_process_name only support in CLI mode\");\n        RETURN_FALSE;\n    }\n    cli_set_process_title->internal_function.handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n\nstatic PHP_FUNCTION(swoole_get_local_ip) {\n    struct ifaddrs *ipaddrs;\n\n    if (getifaddrs(&ipaddrs) != 0) {\n        php_swoole_sys_error(E_WARNING, \"getifaddrs() failed\");\n        RETURN_FALSE;\n    }\n\n    zend_long family = AF_INET;\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(family)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    array_init(return_value);\n    for (struct ifaddrs *ifa = ipaddrs; ifa != nullptr; ifa = ifa->ifa_next) {\n        if (ifa->ifa_addr == nullptr || !(ifa->ifa_flags & IFF_UP)) {\n            continue;\n        }\n\n        if (ifa->ifa_addr->sa_family != family) {\n            continue;\n        }\n\n        swoole::network::Address addr{};\n        switch (ifa->ifa_addr->sa_family) {\n        case AF_INET:\n            addr.type = SW_SOCK_TCP;\n            memcpy(&addr.addr.inet_v4, ifa->ifa_addr, sizeof(addr.addr.inet_v4));\n            break;\n        case AF_INET6:\n            addr.type = SW_SOCK_TCP6;\n            memcpy(&addr.addr.inet_v6, ifa->ifa_addr, sizeof(addr.addr.inet_v6));\n            break;\n        default:\n            continue;\n        }\n\n        if (!addr.is_loopback_addr()) {\n            add_assoc_string(return_value, ifa->ifa_name, addr.get_addr());\n        }\n    }\n    freeifaddrs(ipaddrs);\n}\n\nstatic PHP_FUNCTION(swoole_get_local_mac) {\n    auto add_assoc_address = [](zval *zv, const char *name, const unsigned char *addr) {\n        char buf[32];\n        sw_snprintf(\n            SW_STRS(buf), \"%02X:%02X:%02X:%02X:%02X:%02X\", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);\n        add_assoc_string(zv, name, buf);\n    };\n\n#ifdef SIOCGIFHWADDR\n    char buffer[4096];\n    struct ifconf ifc;\n    struct ifreq *ifr;\n    int sock;\n    int num_interfaces;\n\n    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {\n        php_swoole_sys_error(E_WARNING, \"socket creation failed\");\n        RETURN_FALSE;\n    }\n\n    ifc.ifc_len = sizeof(buffer);\n    ifc.ifc_buf = buffer;\n\n    if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {\n        php_swoole_sys_error(E_WARNING, \"ioctl SIOCGIFCONF failed\");\n        close(sock);\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n\n    ifr = ifc.ifc_req;\n    num_interfaces = ifc.ifc_len / sizeof(struct ifreq);\n\n    for (int i = 0; i < num_interfaces; i++) {\n        struct ifreq temp_ifr;\n        strncpy(temp_ifr.ifr_name, ifr[i].ifr_name, IFNAMSIZ - 1);\n        temp_ifr.ifr_name[IFNAMSIZ - 1] = '\\0';\n\n        if (ioctl(sock, SIOCGIFFLAGS, &temp_ifr) < 0) {\n            continue;\n        }\n\n        if (temp_ifr.ifr_flags & IFF_LOOPBACK) {\n            continue;\n        }\n\n        if (ioctl(sock, SIOCGIFHWADDR, &temp_ifr) == 0) {\n            add_assoc_address(return_value, temp_ifr.ifr_name, (unsigned char *) temp_ifr.ifr_hwaddr.sa_data);\n        }\n    }\n    close(sock);\n#else\n#ifdef LLADDR\n    ifaddrs *ifas, *ifa;\n\n    if (getifaddrs(&ifas) != 0) {\n        php_swoole_sys_error(E_WARNING, \"getifaddrs failed\");\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n\n    for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) {\n        if (ifa->ifa_addr && !(ifa->ifa_flags & IFF_LOOPBACK) && ifa->ifa_addr->sa_family == AF_LINK) {\n            struct sockaddr_dl *sdl = (struct sockaddr_dl *) ifa->ifa_addr;\n            if (sdl->sdl_alen == 6) {\n                add_assoc_address(return_value, ifa->ifa_name, (unsigned char *) LLADDR(sdl));\n            }\n        }\n    }\n\n    freeifaddrs(ifas);\n#else\n    php_error_docref(nullptr, E_WARNING, \"swoole_get_local_mac is not supported on this platform\");\n    RETURN_FALSE;\n#endif\n#endif\n}\n\nstatic PHP_FUNCTION(swoole_internal_call_user_shutdown_begin) {\n    if (SWOOLE_G(req_status) == PHP_SWOOLE_RINIT_END) {\n        SWOOLE_G(req_status) = PHP_SWOOLE_CALL_USER_SHUTDOWNFUNC_BEGIN;\n        RETURN_TRUE;\n    } else {\n        php_error_docref(nullptr, E_WARNING, \"can not call this function in user level\");\n        RETURN_FALSE;\n    }\n}\n\nstatic PHP_FUNCTION(swoole_substr_unserialize) {\n    zend_long offset, length = 0;\n    char *buf = nullptr;\n    size_t buf_len;\n    zval *options = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(2, 4)\n    Z_PARAM_STRING(buf, buf_len)\n    Z_PARAM_LONG(offset)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(length)\n    Z_PARAM_ARRAY(options)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if (buf_len == 0) {\n        RETURN_FALSE;\n    }\n    if (offset < 0) {\n        offset = buf_len + offset;\n        if (offset < 0) {\n            RETURN_FALSE;\n        }\n    }\n    if ((zend_long) buf_len <= offset) {\n        RETURN_FALSE;\n    }\n    if (length <= 0 || length > (zend_long) (buf_len - offset)) {\n        length = buf_len - offset;\n    }\n    zend::unserialize(return_value, buf + offset, length, options ? Z_ARRVAL_P(options) : nullptr);\n}\n\nstatic PHP_FUNCTION(swoole_substr_json_decode) {\n    zend_long offset, length = 0;\n    char *str;\n    size_t str_len;\n    zend_bool assoc = false; /* return JS objects as PHP objects by default */\n    zend_bool assoc_null = true;\n    zend_long depth = PHP_JSON_PARSER_DEFAULT_DEPTH;\n    zend_long options = 0;\n\n    ZEND_PARSE_PARAMETERS_START(2, 6)\n    Z_PARAM_STRING(str, str_len)\n    Z_PARAM_LONG(offset)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(length)\n    Z_PARAM_BOOL_EX(assoc, assoc_null, 1, 0)\n    Z_PARAM_LONG(depth)\n    Z_PARAM_LONG(options)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if (str_len == 0) {\n        php_error_docref(nullptr, E_WARNING, \"Non-empty string required\");\n        RETURN_NULL();\n    }\n    if (offset < 0) {\n        offset = str_len + offset;\n        if (offset < 0) {\n            php_error_docref(nullptr, E_WARNING, \"Offset must be not less than the negative length of the string\");\n            RETURN_NULL();\n        }\n    }\n    if ((zend_long) str_len <= offset) {\n        php_error_docref(nullptr, E_WARNING, \"Offset must be less than the length of the string\");\n        RETURN_NULL();\n    }\n    if (length <= 0 || length > (zend_long) (str_len - offset)) {\n        length = str_len - offset;\n    }\n    /* For BC reasons, the bool $assoc overrides the long $options bit for PHP_JSON_OBJECT_AS_ARRAY */\n    if (!assoc_null) {\n        if (assoc) {\n            options |= PHP_JSON_OBJECT_AS_ARRAY;\n        } else {\n            options &= ~PHP_JSON_OBJECT_AS_ARRAY;\n        }\n    }\n    zend::json_decode(return_value, str + offset, length, options, depth);\n}\n\n/**\n * The implicit functions are intended solely for internal testing and will not be documented.\n * These functions are unsafe, do not use if you are not an internal developer.\n */\nstatic PHP_FUNCTION(swoole_implicit_fn) {\n    char *fn;\n    size_t l_fn;\n    zval *zargs = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_STRING(fn, l_fn)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_ZVAL(zargs)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if (SW_STRCASEEQ(fn, l_fn, \"fatal_error\")) {\n        swoole_fatal_error(SW_ERROR_FOR_TEST, \"test\");\n        php_printf(\"never be executed here\\n\");\n    } else if (SW_STRCASEEQ(fn, l_fn, \"bailout\")) {\n        sw_php_exit(zargs ? zval_get_long(zargs) : 95);\n    } else if (SW_STRCASEEQ(fn, l_fn, \"abort\")) {\n        abort();\n    } else if (SW_STRCASEEQ(fn, l_fn, \"refcount\")) {\n        RETURN_LONG(zval_refcount_p(zargs));\n    } else if (SW_STRCASEEQ(fn, l_fn, \"func_handler\")) {\n        auto fn_str = zval_get_string(zargs);\n        auto *zf = zend::get_function(fn_str);\n        zend_string_release(fn_str);\n        if (zf == nullptr) {\n            RETURN_FALSE;\n        }\n        printf(\"zif_handler=%p\\n\", zf->internal_function.handler);\n    } else {\n        zend_throw_exception_ex(swoole_exception_ce, SW_ERROR_INVALID_PARAMS, \"unknown fn '%s'\", fn);\n    }\n}\n"
  },
  {
    "path": "ext-src/php_swoole_call_stack.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Yurun  <yurun@yurunsoft.com>                                 |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole.h\"\n\n#ifdef ZEND_CHECK_STACK_LIMIT\n#define HOOK_PHP_CALL_STACK(callback)                                                                                  \\\n    void *__stack_limit = EG(stack_limit);                                                                             \\\n    void *__stack_base = EG(stack_base);                                                                               \\\n    EG(stack_base) = (void *) 0;                                                                                       \\\n    EG(stack_limit) = (void *) 0;                                                                                      \\\n    callback EG(stack_limit) = __stack_limit;                                                                          \\\n    EG(stack_base) = __stack_base;\n#else\n#define HOOK_PHP_CALL_STACK(callback) callback\n#endif\n"
  },
  {
    "path": "ext-src/php_swoole_client.h",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2015 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#pragma once\n\n#include \"php_swoole_cxx.h\"\n#include \"swoole_client.h\"\n\nenum ClientFlag {\n    SW_FLAG_KEEP = 1u << 12,\n    SW_FLAG_ASYNC = 1u << 10,\n    SW_FLAG_SYNC = 1u << 11,\n};\n\nstruct AsyncClientObject {\n    zend::Callable *onConnect;\n    zend::Callable *onReceive;\n    zend::Callable *onClose;\n    zend::Callable *onError;\n    zend::Callable *onBufferFull;\n    zend::Callable *onBufferEmpty;\n    zend::Callable *onSSLReady;\n    zval _zobject;\n};\n\ntypedef swoole::network::Client NetClient;\n\nstruct ClientObject {\n    NetClient *cli;\n#ifdef SWOOLE_SOCKETS_SUPPORT\n    zval *zsocket;\n#endif\n    AsyncClientObject *async;\n    zend_object std;\n};\n\nstatic inline ClientObject *php_swoole_client_fetch_object(zend_object *obj) {\n    return reinterpret_cast<ClientObject *>(reinterpret_cast<char *>(obj) - swoole_client_handlers.offset);\n}\n\nstatic inline ClientObject *php_swoole_client_fetch_object(const zval *zobj) {\n    return php_swoole_client_fetch_object(Z_OBJ_P(zobj));\n}\n\nstatic inline NetClient *php_swoole_client_get_cli(const zval *zobject) {\n    return php_swoole_client_fetch_object(Z_OBJ_P(zobject))->cli;\n}\n\nstatic inline enum swSocketType php_swoole_client_get_type(long type) {\n    return (enum swSocketType)(type & (~SW_FLAG_SYNC) & (~SW_FLAG_ASYNC) & (~SW_FLAG_KEEP) & (~SW_SOCK_SSL));\n}\n\nNetClient *php_swoole_client_get_cli_safe(const zval *zobject);\nvoid php_swoole_client_free(const zval *zobject, NetClient *cli);\nvoid php_swoole_client_async_free_object(const ClientObject *client_obj);\nbool php_swoole_client_check_setting(NetClient *cli, const zval *zset);\nvoid php_swoole_client_check_ssl_setting(const NetClient *cli, const zval *zset);\nbool php_swoole_client_enable_ssl_encryption(NetClient *cli, zval *zobject);\n"
  },
  {
    "path": "ext-src/php_swoole_coroutine.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Xinyu Zhu  <xyzhu1120@gmail.com>                             |\n  |         shiguangqi <shiguangqi2008@gmail.com>                        |\n  |         Twosee  <twose@qq.com>                                       |\n  |         Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n */\n\n#pragma once\n\n#include \"swoole_coroutine.h\"\n#include \"swoole_coroutine_system.h\"\n\n#include \"zend_vm.h\"\n#include \"zend_closures.h\"\n\n#include \"zend_fibers.h\"\n#include \"zend_observer.h\"\n\n#include <stack>\n#include <thread>\n\n#define SW_DEFAULT_MAX_CORO_NUM 100000\n#define SW_DEFAULT_PHP_STACK_PAGE_SIZE 8192\n\n#define SWOG ((zend_output_globals *) &OG(handlers))\n\nSW_EXTERN_C_BEGIN\nPHP_METHOD(swoole_coroutine_scheduler, set);\nPHP_METHOD(swoole_coroutine_scheduler, getOptions);\nSW_EXTERN_C_END\n\nnamespace zend {\nstruct Function;\n}\n\nnamespace swoole {\n\nstruct PHPContext {\n    typedef std::function<void(PHPContext *)> SwapCallback;\n\n    JMP_BUF *bailout;\n    zval *vm_stack_top;\n    zval *vm_stack_end;\n    zend_vm_stack vm_stack;\n    size_t vm_stack_page_size;\n    zend_execute_data *execute_data;\n    uint32_t jit_trace_num;\n    zend_error_handling_t error_handling;\n    zend_class_entry *exception_class;\n    zend_object *exception;\n    zend_output_globals *output_ptr;\n    /*\n     * for var serialize/unserialize,\n     * coroutine switching may occur in the __sleep/__wakeup magic method of the object\n     */\n    unsigned serialize_lock;\n    struct {\n        struct php_serialize_data *data;\n        unsigned level;\n    } serialize;\n    struct {\n        struct php_unserialize_data *data;\n        unsigned level;\n    } unserialize;\n    /* for error control `@` */\n    bool in_silence;\n    bool enable_scheduler;\n    int ori_error_reporting;\n    int tmp_error_reporting;\n    Coroutine *co;\n    zend_fcall_info fci;\n    zend_fcall_info_cache fci_cache;\n    zval return_value;\n    zend_fiber_context *fiber_context;\n    bool fiber_init_notified;\n#ifdef ZEND_CHECK_STACK_LIMIT\n    void *stack_base;\n    void *stack_limit;\n#endif\n    std::stack<zend::Function *> *defer_tasks;\n    SwapCallback *on_yield;\n    SwapCallback *on_resume;\n    SwapCallback *on_close;\n    long pcid;\n    zend_object *context;\n    int64_t last_msec;\n    size_t switch_count;\n};\n\nclass PHPCoroutine {\n  public:\n    struct Args {\n        zend_fcall_info_cache *fci_cache;\n        zval *argv;\n        uint32_t argc;\n        zval *callable;\n    };\n\n    struct Config {\n        uint64_t max_num;\n        uint32_t hook_flags;\n        bool enable_preemptive_scheduler;\n        bool enable_deadlock_check;\n    };\n\n    static SW_THREAD_LOCAL zend_array *options;\n\n    enum HookType {\n        HOOK_NONE = 0,\n        HOOK_TCP = 1u << 1,\n        HOOK_UDP = 1u << 2,\n        HOOK_UNIX = 1u << 3,\n        HOOK_UDG = 1u << 4,\n        HOOK_SSL = 1u << 5,\n        HOOK_TLS = 1u << 6,\n        HOOK_STREAM_FUNCTION = 1u << 7,\n        HOOK_FILE = 1u << 8,\n        HOOK_SLEEP = 1u << 9,\n        HOOK_PROC = 1u << 10,\n        HOOK_CURL = 1u << 11,\n        HOOK_NATIVE_CURL = 1u << 12,\n        HOOK_SOCKETS = 1u << 14,\n        HOOK_STDIO = 1u << 15,\n        HOOK_PDO_PGSQL = 1u << 16,\n        HOOK_PDO_ODBC = 1u << 17,\n        HOOK_PDO_ORACLE = 1u << 18,\n        HOOK_PDO_SQLITE = 1u << 19,\n        HOOK_PDO_FIREBIRD = 1u << 20,\n        HOOK_NET_FUNCTION = 1u << 21,\n        HOOK_MONGODB = 1u << 22,\n#ifdef SW_USE_CURL\n        HOOK_ALL = 0x7fffffff & ~HOOK_CURL & ~HOOK_MONGODB,\n#else\n        HOOK_ALL = 0x7fffffff & ~HOOK_NATIVE_CURL & ~HOOK_MONGODB,\n#endif\n    };\n\n    static const uint8_t MAX_EXEC_MSEC = 10;\n    static void shutdown();\n    static long create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *callable);\n    static PHPContext *create_context(const Args *args);\n    static void defer(zend::Function *fci);\n    static void deadlock_check();\n    static bool enable_hook(uint32_t flags);\n    static bool disable_hook();\n    static void disable_unsafe_function();\n    static void enable_unsafe_function();\n    static void interrupt_thread_stop();\n\n    static inline long get_cid() {\n        return sw_likely(activated) ? Coroutine::get_current_cid() : -1;\n    }\n\n    static inline long get_pcid(long cid = 0) {\n        PHPContext *ctx = cid == 0 ? get_context() : get_context_by_cid(cid);\n        return sw_likely(ctx) ? ctx->pcid : 0;\n    }\n\n    static inline long get_elapsed(long cid = 0) {\n        return sw_likely(activated) ? Coroutine::get_elapsed(cid) : -1;\n    }\n\n    static inline PHPContext *get_context() {\n        PHPContext *ctx = (PHPContext *) Coroutine::get_current_task();\n        return ctx ? ctx : &main_context;\n    }\n\n    static inline PHPContext *get_origin_context(PHPContext *ctx) {\n        Coroutine *co = ctx->co->get_origin();\n        return co ? (PHPContext *) co->get_task() : &main_context;\n    }\n\n    static inline PHPContext *get_context_by_cid(long cid) {\n        return cid == -1 ? &main_context : (PHPContext *) Coroutine::get_task_by_cid(cid);\n    }\n\n    static inline ssize_t get_stack_usage(long cid) {\n        zend_long current_cid = PHPCoroutine::get_cid();\n        if (cid == 0) {\n            cid = current_cid;\n        }\n        PHPContext *ctx = (PHPContext *) PHPCoroutine::get_context_by_cid(cid);\n        if (UNEXPECTED(!ctx)) {\n            swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS);\n            return -1;\n        }\n        zend_vm_stack stack = cid == current_cid ? EG(vm_stack) : ctx->vm_stack;\n        size_t usage = 0;\n\n        while (stack) {\n            usage += (stack->end - stack->top) * sizeof(zval);\n            stack = stack->prev;\n        }\n        return usage;\n    }\n\n    static inline uint64_t get_max_num() {\n        return config.max_num;\n    }\n\n    static inline void set_max_num(uint64_t n) {\n        config.max_num = n;\n    }\n\n    static inline void set_deadlock_check(bool value = true) {\n        config.enable_deadlock_check = value;\n    }\n\n    static inline bool is_schedulable(PHPContext *ctx) {\n        return ctx->enable_scheduler && (Timer::get_absolute_msec() - ctx->last_msec > MAX_EXEC_MSEC);\n    }\n\n    static inline bool enable_scheduler() {\n        PHPContext *ctx = (PHPContext *) Coroutine::get_current_task();\n        if (ctx && !ctx->enable_scheduler) {\n            ctx->enable_scheduler = true;\n            return true;\n        }\n        return false;\n    }\n\n    static inline bool disable_scheduler() {\n        PHPContext *ctx = (PHPContext *) Coroutine::get_current_task();\n        if (ctx && ctx->enable_scheduler) {\n            ctx->enable_scheduler = false;\n            return true;\n        }\n        return false;\n    }\n\n    static void set_hook_flags(uint32_t flags);\n\n    static inline uint32_t get_hook_flags() {\n        return config.hook_flags;\n    }\n\n    static inline void enable_preemptive_scheduler(bool value) {\n        config.enable_preemptive_scheduler = value;\n    }\n\n    static inline bool is_activated() {\n        return activated;\n    }\n\n    static inline long get_execute_time(long cid = 0) {\n        return sw_likely(activated) ? Coroutine::get_execute_time(cid) : -1;\n    }\n\n    static inline void init_main_context() {\n        main_context.co = nullptr;\n        main_context.fiber_context = EG(main_fiber_context);\n        main_context.fiber_init_notified = true;\n        save_context(&main_context);\n    }\n\n    static inline void free_main_context() {\n        main_context = {};\n    }\n\n  protected:\n    static SW_THREAD_LOCAL bool activated;\n    static SW_THREAD_LOCAL PHPContext main_context;\n    static SW_THREAD_LOCAL Config config;\n\n    static SW_THREAD_LOCAL bool interrupt_thread_running;\n    static SW_THREAD_LOCAL std::thread interrupt_thread;\n\n    static void activate();\n    static void deactivate(void *ptr);\n\n    static void save_vm_stack(PHPContext *ctx);\n    static void restore_vm_stack(PHPContext *ctx);\n    static void save_og(PHPContext *ctx);\n    static void restore_og(PHPContext *ctx);\n    static void save_bg(PHPContext *ctx);\n    static void restore_bg(PHPContext *ctx);\n    static void save_context(PHPContext *ctx);\n    static void restore_context(PHPContext *ctx);\n    static void destroy_context(PHPContext *ctx);\n    static bool catch_exception();\n    static void bailout();\n    static void on_yield(void *arg);\n    static void on_resume(void *arg);\n    static void on_close(void *arg);\n    static void main_func(void *arg);\n    static zend_fiber_status fiber_get_status(const PHPContext *ctx);\n    static void fiber_context_init(PHPContext *ctx);\n    static void fiber_context_try_destroy(const PHPContext *ctx, PHPContext *origin_ctx);\n    static void fiber_context_switch_notify(const PHPContext *from, PHPContext *to);\n    static void fiber_context_switch_try_notify(const PHPContext *from, PHPContext *to);\n#ifdef ZEND_CHECK_STACK_LIMIT\n    static void *stack_limit(PHPContext *ctx);\n    static void *stack_base(PHPContext *ctx);\n#endif\n    static void interrupt_thread_start();\n    static void record_last_msec(PHPContext *ctx) {\n        if (interrupt_thread_running) {\n            ctx->last_msec = Timer::get_absolute_msec();\n        }\n    }\n};\n}  // namespace swoole\n\n/**\n * for gdb\n */\nzend_executor_globals *php_swoole_get_executor_globals();\n"
  },
  {
    "path": "ext-src/php_swoole_coroutine_system.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Xinyu Zhu  <xyzhu1120@gmail.com>                             |\n  |         shiguangqi <shiguangqi2008@gmail.com>                        |\n  |         Tianfeng Han  <mikan.tenny@gmail.com>                        |\n  +----------------------------------------------------------------------+\n */\n\n#pragma once\n\n#include \"php_swoole_cxx.h\"\n\n// clang-format off\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_coroutine_system_arginfo.h\"\nEND_EXTERN_C()\n// clang-format on\n\nSW_EXTERN_C_BEGIN\nPHP_METHOD(swoole_coroutine_system, exec);\nPHP_METHOD(swoole_coroutine_system, sleep);\nPHP_METHOD(swoole_coroutine_system, fread);\nPHP_METHOD(swoole_coroutine_system, fgets);\nPHP_METHOD(swoole_coroutine_system, fwrite);\nPHP_METHOD(swoole_coroutine_system, statvfs);\nPHP_METHOD(swoole_coroutine_system, getaddrinfo);\nPHP_METHOD(swoole_coroutine_system, readFile);\nPHP_METHOD(swoole_coroutine_system, writeFile);\nPHP_METHOD(swoole_coroutine_system, wait);\nPHP_METHOD(swoole_coroutine_system, waitPid);\nPHP_METHOD(swoole_coroutine_system, waitSignal);\nPHP_METHOD(swoole_coroutine_system, waitEvent);\nSW_EXTERN_C_END\n"
  },
  {
    "path": "ext-src/php_swoole_curl.h",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#pragma once\n\n#include \"php_swoole_cxx.h\"\n\n#ifdef SW_USE_CURL\n#include \"swoole_util.h\"\n\nSW_EXTERN_C_BEGIN\n#include <curl/curl.h>\n#include <curl/multi.h>\n#if PHP_VERSION_ID >= 80400\n#include \"thirdparty/php84/curl/curl_private.h\"\n#else\n#include \"thirdparty/php/curl/curl_private.h\"\n#endif\nSW_EXTERN_C_END\n\n#if LIBCURL_VERSION_NUM < 0x073800\n#error \"require cURL version 7.56.0 or later\"\n#endif\n\n#include <unordered_set>\n\nCURLcode swoole_curl_easy_perform(CURL *cp);\nphp_curl *swoole_curl_get_handle(zval *zid, bool exclusive = true, bool required = true);\nvoid swoole_curl_easy_reset(CURL *curl);\n\nnamespace swoole {\nnamespace curl {\n\nclass Multi;\n\nstruct Socket {\n    Multi *multi;\n    network::Socket *socket;\n    int bitmask;\n    int sockfd;\n    int action;\n    bool deleted;\n};\n\nstruct Handle {\n    CURL *cp;\n    Multi *multi;\n    /**\n     * This is only for the swoole_curl_easy_perform function,\n     * and it has a one-to-one relationship with the curl handle.\n     * It must be destroyed when the curl handle is released.\n     */\n    Multi *easy_multi;\n\n    explicit Handle(CURL *_cp) {\n        cp = _cp;\n        multi = nullptr;\n        easy_multi = nullptr;\n    }\n};\n\nHandle *get_handle(CURL *cp);\nHandle *create_handle(CURL *ch);\nvoid destroy_handle(CURL *ch);\n\nstruct Selector {\n    bool timer_callback = false;\n    bool executing = false;\n    std::unordered_set<Socket *> active_sockets;\n    std::unordered_set<Socket *> release_sockets;\n};\n\nclass Multi {\n    CURLM *multi_handle_;\n    TimerNode *timer = nullptr;\n    long timeout_ms_ = 0;\n    Coroutine *co = nullptr;\n    int running_handles_ = 0;\n    bool defer_callback = false;\n    Selector selector;\n    std::unordered_map<curl_socket_t, Socket *> sockets;\n\n    CURLcode read_info() const;\n\n    int set_event(void *socket_ptr, curl_socket_t sockfd, int action);\n    int del_event(void *socket_ptr, curl_socket_t sockfd);\n    void selector_finish();\n    void selector_prepare();\n\n    bool wait_event() const {\n        return timer || !sockets.empty();\n    }\n\n    void add_timer(long timeout_ms) {\n        if (timer && swoole_timer_is_available()) {\n            swoole_timer_del(timer);\n        }\n        timeout_ms_ = timeout_ms;\n        timer = swoole_timer_add(timeout_ms, false, [this](Timer *_timer, TimerNode *tnode) {\n            timer = nullptr;\n            callback(nullptr, 0);\n        });\n    }\n\n    void del_timer() {\n        if (timer && swoole_timer_is_available()) {\n            swoole_timer_del(timer);\n            timeout_ms_ = -1;\n            timer = nullptr;\n        }\n    }\n\n    void set_timer() {\n        long _timeout_ms = 0;\n        curl_multi_timeout(multi_handle_, &_timeout_ms);\n        handle_timeout(multi_handle_, _timeout_ms, this);\n    }\n\n  public:\n    Multi();\n    ~Multi();\n\n    CURLM *get_multi_handle() const {\n        return multi_handle_;\n    }\n\n    int get_running_handles() const {\n        return running_handles_;\n    }\n\n    CURLMcode add_handle(Handle *handle);\n    CURLMcode remove_handle(Handle *handle) const;\n\n    CURLMcode perform() {\n        return curl_multi_perform(multi_handle_, &running_handles_);\n    }\n\n    int get_event(int action) {\n        return action == CURL_POLL_IN ? SW_EVENT_READ : SW_EVENT_WRITE;\n    }\n\n    Coroutine *check_bound_co() {\n        if (co) {\n            Coroutine::set_socket_bound_cid(co->get_cid());\n            swoole_fatal_error(SW_ERROR_CO_HAS_BEEN_BOUND,\n                               \"This cURL handle is currently executing in coroutine#%ld, cannot be operated\",\n                               co->get_cid());\n            return nullptr;\n        }\n        return Coroutine::get_current_safe();\n    }\n\n    CURLcode exec(Handle *handle);\n    long select(php_curlm *mh, double timeout = -1);\n    void callback(Socket *curl_socket, int bitmask, int sockfd = -1);\n\n    static int cb_readable(Reactor *reactor, Event *event);\n    static int cb_writable(Reactor *reactor, Event *event);\n    static int cb_error(Reactor *reactor, Event *event);\n    static int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, void *socketp);\n    static int handle_timeout(CURLM *multi, long timeout_ms, void *userp);\n};\n};  // namespace curl\n}  // namespace swoole\n#endif\n"
  },
  {
    "path": "ext-src/php_swoole_cxx.cc",
    "content": "#include \"php_swoole_cxx.h\"\n\n//----------------------------------known string------------------------------------\n\nstatic const char *sw_known_strings[] = {\n#define _SW_ZEND_STR_DSC(id, str) str,\n    SW_ZEND_KNOWN_STRINGS(_SW_ZEND_STR_DSC)\n#undef _SW_ZEND_STR_DSC\n        nullptr};\n\nSW_API zend_string **sw_zend_known_strings = nullptr;\n\nSW_API zend_refcounted *sw_refcount_ptr;\n\nzend_refcounted *sw_get_refcount_ptr(zval *value) {\n    return (sw_refcount_ptr = value->value.counted);\n}\n\n//----------------------------------known string------------------------------------\nnamespace zend {\nvoid known_strings_init() {\n    sw_zend_known_strings = static_cast<zend_string **>(\n        pemalloc(sizeof(zend_string *) * ((sizeof(sw_known_strings) / sizeof(sw_known_strings[0]) - 1)), 1));\n    for (unsigned int i = 0; i < (sizeof(sw_known_strings) / sizeof(sw_known_strings[0])) - 1; i++) {\n        zend_string *str = zend_string_init(sw_known_strings[i], strlen(sw_known_strings[i]), true);\n        sw_zend_known_strings[i] = zend_new_interned_string(str);\n    }\n}\n\nvoid known_strings_dtor() {\n    pefree(sw_zend_known_strings, 1);\n    sw_zend_known_strings = nullptr;\n}\n\nzend_function *get_function(const zend_array *function_table, const char *name, size_t name_len) {\n    return static_cast<zend_function *>(zend_hash_str_find_ptr(function_table, name, name_len));\n}\n\nzend_function *get_function(const char *fname, size_t fname_len) {\n    return get_function(EG(function_table), fname, fname_len);\n}\n\nzend_function *get_function(const std::string &fname) {\n    return get_function(fname.c_str(), fname.length());\n}\n\nzend_function *get_function(const zend_string *fname) {\n    return get_function(ZSTR_VAL(fname), ZSTR_LEN(fname));\n}\n\nstatic zend_always_inline zval *sw_zend_symtable_str_add(\n    HashTable *ht, const char *str, size_t len, zend_ulong idx, bool numeric_key, zval *pData) {\n    if (numeric_key) {\n        return zend_hash_index_add(ht, idx, pData);\n    } else {\n        return zend_hash_str_add(ht, str, len, pData);\n    }\n}\n\nstatic zend_always_inline zval *sw_zend_symtable_str_find(\n    HashTable *ht, const char *str, size_t len, zend_ulong idx, bool numeric_key) {\n    if (numeric_key) {\n        return zend_hash_index_find(ht, idx);\n    } else {\n        return zend_hash_str_find(ht, str, len);\n    }\n}\n\nstatic zend_always_inline zval *sw_zend_symtable_str_update(\n    HashTable *ht, const char *str, size_t len, zend_ulong idx, bool numeric_key, zval *pData) {\n    if (numeric_key) {\n        return zend_hash_index_update(ht, idx, pData);\n    } else {\n        return zend_hash_str_update(ht, str, len, pData);\n    }\n}\n\nvoid array_add_or_merge(zval *zarray, const char *key, size_t key_len, zval *new_element) {\n    zend_ulong idx;\n    bool numeric_key = ZEND_HANDLE_NUMERIC_STR(key, key_len, idx);\n\n    zend_array *array = Z_ARRVAL_P(zarray);\n    zval *zresult = sw_zend_symtable_str_add(array, key, key_len, idx, numeric_key, new_element);\n    // Adding element failed, indicating that this key already exists and must be converted to an array\n    if (!zresult) {\n        zval *current_elements = sw_zend_symtable_str_find(array, key, key_len, idx, numeric_key);\n        if (ZVAL_IS_ARRAY(current_elements)) {\n            add_next_index_zval(current_elements, new_element);\n        } else {\n            zval zvalue_array;\n            array_init_size(&zvalue_array, 2);\n            Z_ADDREF_P(current_elements);\n            add_next_index_zval(&zvalue_array, current_elements);\n            add_next_index_zval(&zvalue_array, new_element);\n            sw_zend_symtable_str_update(array, key, key_len, idx, numeric_key, &zvalue_array);\n        }\n    }\n}\n\nnamespace function {\n\nbool call(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *retval, const bool enable_coroutine) {\n    bool success;\n    if (enable_coroutine) {\n        if (retval) {\n            /* the coroutine has no return value */\n            ZVAL_NULL(retval);\n        }\n        success = swoole::PHPCoroutine::create(fci_cache, argc, argv, nullptr) >= 0;\n    } else {\n        success = sw_zend_call_function_ex(nullptr, fci_cache, argc, argv, retval) == SUCCESS;\n    }\n    /* we have no chance to return to ZendVM to check the exception  */\n    if (UNEXPECTED(EG(exception))) {\n        zend_exception_error(EG(exception), E_ERROR);\n    }\n    return success;\n}\n\nVariable call(const std::string &func_name, int argc, zval *argv) {\n    zval function_name;\n    ZVAL_STRINGL(&function_name, func_name.c_str(), func_name.length());\n    Variable retval;\n    if (call_user_function(EG(function_table), nullptr, &function_name, &retval.value, argc, argv) != SUCCESS) {\n        ZVAL_NULL(&retval.value);\n    }\n    zval_dtor(&function_name);\n    /* we have no chance to return to ZendVM to check the exception  */\n    if (UNEXPECTED(EG(exception))) {\n        zend_exception_error(EG(exception), E_ERROR);\n    }\n    return retval;\n}\n\n}  // namespace function\n\nCallable::Callable(zval *_zfn) {\n    ZVAL_UNDEF(&zfn);\n    if (!zval_is_true(_zfn)) {\n        php_swoole_fatal_error(E_WARNING, \"illegal callback function\");\n        return;\n    }\n    if (!sw_zend_is_callable_ex(_zfn, nullptr, 0, &fn_name, nullptr, &fcc, nullptr)) {\n        php_swoole_fatal_error(E_WARNING, \"function '%s' is not callable\", fn_name);\n        return;\n    }\n    zfn = *_zfn;\n    zval_add_ref(&zfn);\n}\n\nCallable::~Callable() {\n    if (!ZVAL_IS_UNDEF(&zfn)) {\n        zval_ptr_dtor(&zfn);\n    }\n    if (fn_name) {\n        efree(fn_name);\n    }\n}\n\nuint32_t Callable::refcount() const {\n    return zval_refcount_p(&zfn);\n}\n}  // namespace zend\n"
  },
  {
    "path": "ext-src/php_swoole_cxx.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"php_swoole_private.h\"\n#include \"php_swoole_coroutine.h\"\n\n#include \"swoole_socket_impl.h\"\n#include \"swoole_protocol.h\"\n#include \"swoole_util.h\"\n\n#include <string>\n\n// clang-format off\n//----------------------------------Swoole known string------------------------------------\n\n#define SW_ZEND_KNOWN_STRINGS(_) \\\n    _(SW_ZEND_STR_TYPE,                     \"type\") \\\n    _(SW_ZEND_STR_HOST,                     \"host\") \\\n    _(SW_ZEND_STR_USER_AGENT,               \"user-agent\") \\\n    _(SW_ZEND_STR_ACCEPT,                   \"accept\") \\\n    _(SW_ZEND_STR_CONTENT_TYPE,             \"content-type\") \\\n    _(SW_ZEND_STR_CONTENT_LENGTH,           \"content-length\") \\\n    _(SW_ZEND_STR_AUTHORIZATION,            \"authorization\") \\\n    _(SW_ZEND_STR_CONNECTION,               \"connection\") \\\n    _(SW_ZEND_STR_ACCEPT_ENCODING,          \"accept-encoding\") \\\n    _(SW_ZEND_STR_PORT,                     \"port\") \\\n    _(SW_ZEND_STR_SETTING,                  \"setting\") \\\n    _(SW_ZEND_STR_ID,                       \"id\") \\\n    _(SW_ZEND_STR_FD,                       \"fd\") \\\n    _(SW_ZEND_STR_SOCK,                     \"sock\") \\\n    _(SW_ZEND_STR_PIPE,                     \"pipe\") \\\n    _(SW_ZEND_STR_HEADERS,                  \"headers\") \\\n    _(SW_ZEND_STR_REQUEST_METHOD,           \"requestMethod\") \\\n    _(SW_ZEND_STR_REQUEST_HEADERS,          \"requestHeaders\") \\\n    _(SW_ZEND_STR_REQUEST_BODY,             \"requestBody\") \\\n    _(SW_ZEND_STR_UPLOAD_FILES,             \"uploadFiles\") \\\n    _(SW_ZEND_STR_COOKIES,                  \"cookies\") \\\n    _(SW_ZEND_STR_DOWNLOAD_FILE,            \"downloadFile\") \\\n    _(SW_ZEND_STR_DOWNLOAD_OFFSET,          \"downloadOffset\") \\\n    _(SW_ZEND_STR_SERVER,                   \"server\") \\\n    _(SW_ZEND_STR_HEADER,                   \"header\") \\\n    _(SW_ZEND_STR_GET,                      \"get\") \\\n    _(SW_ZEND_STR_POST,                     \"post\") \\\n    _(SW_ZEND_STR_FILES,                    \"files\") \\\n    _(SW_ZEND_STR_TMPFILES,                 \"tmpfiles\") \\\n    _(SW_ZEND_STR_COOKIE,                   \"cookie\") \\\n    _(SW_ZEND_STR_METHOD,                   \"method\") \\\n    _(SW_ZEND_STR_PATH,                     \"path\") \\\n    _(SW_ZEND_STR_DATA,                     \"data\") \\\n    _(SW_ZEND_STR_PIPELINE,                 \"pipeline\") \\\n    _(SW_ZEND_STR_USE_PIPELINE_READ,        \"usePipelineRead\") \\\n    _(SW_ZEND_STR_TRAILER,                  \"trailer\") \\\n    _(SW_ZEND_STR_MASTER_PID,               \"master_pid\") \\\n    _(SW_ZEND_STR_CALLBACK,                 \"callback\") \\\n    _(SW_ZEND_STR_OPCODE,                   \"opcode\") \\\n    _(SW_ZEND_STR_CODE,                     \"code\") \\\n    _(SW_ZEND_STR_REASON,                   \"reason\") \\\n    _(SW_ZEND_STR_FLAGS,                    \"flags\") \\\n    _(SW_ZEND_STR_FINISH,                   \"finish\") \\\n    _(SW_ZEND_STR_IN_COROUTINE,             \"in_coroutine\") \\\n    _(SW_ZEND_STR_PRIVATE_DATA,             \"private_data\") \\\n    _(SW_ZEND_STR_CLASS_NAME_RESOLVER,      \"Swoole\\\\NameResolver\") \\\n    _(SW_ZEND_STR_SOCKET,                   \"socket\") \\\n    _(SW_ZEND_STR_ADDR_LOOPBACK_V4,         \"127.0.0.1\") \\\n    _(SW_ZEND_STR_ADDR_LOOPBACK_V6,         \"::1\")  \\\n    _(SW_ZEND_STR_REQUEST_METHOD2,          \"request_method\")  \\\n    _(SW_ZEND_STR_REQUEST_URI,              \"request_uri\")  \\\n    _(SW_ZEND_STR_PATH_INFO,                \"path_info\")  \\\n    _(SW_ZEND_STR_REQUEST_TIME,             \"request_time\")  \\\n    _(SW_ZEND_STR_REQUEST_TIME_FLOAT,       \"request_time_float\")  \\\n    _(SW_ZEND_STR_SERVER_PROTOCOL,          \"server_protocol\")  \\\n    _(SW_ZEND_STR_SERVER_PORT,              \"server_port\")  \\\n    _(SW_ZEND_STR_SERVER_ADDR,              \"server_addr\")  \\\n    _(SW_ZEND_STR_REMOTE_PORT,              \"remote_port\")  \\\n    _(SW_ZEND_STR_REMOTE_ADDR,              \"remote_addr\")  \\\n    _(SW_ZEND_STR_MASTER_TIME,              \"master_time\") \\\n    _(SW_ZEND_STR_QUERY_STRING,             \"query_string\") \\\n    _(SW_ZEND_STR_HTTP10,                   \"HTTP/1.0\") \\\n    _(SW_ZEND_STR_HTTP11,                   \"HTTP/1.1\") \\\n    _(SW_ZEND_STR_HTTP2,                    \"HTTP/2\") \\\n\ntypedef enum sw_zend_known_string_id {\n#define _SW_ZEND_STR_ID(id, str) id,\nSW_ZEND_KNOWN_STRINGS(_SW_ZEND_STR_ID)\n#undef _SW_ZEND_STR_ID\n    SW_ZEND_STR_LAST_KNOWN\n} sw_zend_known_string_id;\n\n// clang-format on\n\n#define SW_ZSTR_KNOWN(idx) sw_zend_known_strings[idx]\nextern zend_string **sw_zend_known_strings;\n\n//----------------------------------Swoole known string------------------------------------\n\n#define SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(module)                                                              \\\n    module##_ce->create_object = [](zend_class_entry *ce) { return sw_zend_create_object(ce, &module##_handlers); }\n\n/**\n * It is safe across coroutines,\n * add reference count, prevent the socket pointer being released\n */\n#define SW_CLIENT_GET_SOCKET_SAFE(__sock, __zsocket)                                                                   \\\n    SocketImpl *__sock = nullptr;                                                                                      \\\n    zend::Variable tmp_socket;                                                                                         \\\n    if (ZVAL_IS_OBJECT(__zsocket)) {                                                                                   \\\n        __sock = php_swoole_get_socket(__zsocket);                                                                     \\\n        tmp_socket.assign(__zsocket);                                                                                  \\\n    }\n\n#define SW_CLIENT_PRESERVE_SOCKET(__zsocket)                                                                           \\\n    zend::Variable tmp_socket;                                                                                         \\\n    if (ZVAL_IS_OBJECT(__zsocket)) {                                                                                   \\\n        tmp_socket.assign(__zsocket);                                                                                  \\\n    }\n\nSW_API bool php_swoole_is_enable_coroutine();\nSW_API zend_object *php_swoole_create_socket(enum swSocketType type);\nSW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocketType type);\nSW_API zend_object *php_swoole_create_socket_from_fd(int fd, int _domain, int _type, int _protocol);\nSW_API bool php_swoole_export_socket(zval *zobject, SocketImpl *_socket);\nSW_API zend_object *php_swoole_dup_socket(int fd, enum swSocketType type);\nSW_API void php_swoole_init_socket_object(zval *zobject, SocketImpl *socket);\nSW_API SocketImpl *php_swoole_get_socket(const zval *zobject);\nSW_API bool php_swoole_socket_is_closed(const zval *zobject);\nSW_API bool php_swoole_socket_set_ssl(SocketImpl *sock, const zval *zset);\nSW_API bool php_swoole_socket_set_protocol(SocketImpl *sock, const zval *zset);\nSW_API bool php_swoole_socket_set(SocketImpl *cli, const zval *zset);\nSW_API void php_swoole_socket_set_error_properties(const zval *zobject, int code);\nSW_API void php_swoole_socket_set_error_properties(const zval *zobject, int code, const char *msg);\nSW_API void php_swoole_socket_set_error_properties(const zval *zobject, const SocketImpl *socket);\n#define php_swoole_client_set php_swoole_socket_set\nSW_API php_stream *php_swoole_create_stream_from_socket(php_socket_t _fd,\n                                                        int domain,\n                                                        int type,\n                                                        int protocol STREAMS_DC);\nSW_API php_stream *php_swoole_create_stream_from_pipe(int fd, const char *mode, const char *persistent_id STREAMS_DC);\nSW_API php_stream_ops *php_swoole_get_ori_php_stream_stdio_ops();\nSW_API zif_handler php_swoole_get_original_handler(const char *name, size_t len);\nSW_API bool php_swoole_call_original_handler(const char *name, size_t len, INTERNAL_FUNCTION_PARAMETERS);\n\n// timer\nSW_API bool php_swoole_timer_clear(swoole::TimerNode *tnode);\nSW_API bool php_swoole_timer_clear_all();\n\nstatic inline bool php_swoole_is_fatal_error() {\n    return PG(last_error_message) && (PG(last_error_type) & E_FATAL_ERRORS);\n}\n\nssize_t php_swoole_length_func(const swoole::Protocol *, NetSocket *, swoole::PacketLength *);\nSW_API zend_long php_swoole_parse_to_size(zval *zv);\nSW_API zend_string *php_swoole_serialize(zval *zdata);\nSW_API bool php_swoole_unserialize(const zend_string *data, zval *zv);\n\n#ifdef SW_HAVE_ZLIB\nint php_swoole_zlib_decompress(z_stream *stream, swoole::String *buffer, char *body, int length);\n#endif\n\nswoole::NameResolver::Context *php_swoole_name_resolver_get_context(zval *zobject);\nstd::string php_swoole_name_resolver_lookup(const std::string &name,\n                                            swoole::NameResolver::Context *ctx,\n                                            void *_resolver);\nbool php_swoole_name_resolver_add(zval *zresolver);\n\nconst swoole::Allocator *sw_php_allocator();\nconst swoole::Allocator *sw_zend_string_allocator();\n\n#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)\n#define SOL_TCP IPPROTO_TCP\n#endif\n\n#ifdef __APPLE__\n#define TCP_INFO TCP_CONNECTION_INFO\nusing tcp_info = tcp_connection_info;\n#endif\n\n#ifdef TCP_INFO\nstd::unordered_map<std::string, uint64_t> sw_socket_parse_tcp_info(tcp_info *info);\n#endif\n\nstatic inline bool php_swoole_async(bool blocking, const std::function<void(void)> &fn) {\n    if (!blocking && swoole_coroutine_is_in()) {\n        return swoole::coroutine::async(fn);\n    } else {\n        fn();\n        return true;\n    }\n}\n\nnamespace zend {\n//-----------------------------------namespace begin--------------------------------------------\nclass String {\n  public:\n    String() {\n        str = nullptr;\n    }\n\n    String(const char *_str, size_t len) {\n        str = zend_string_init(_str, len, false);\n    }\n\n    String(const std::string &_str) {\n        str = zend_string_init(_str.c_str(), _str.length(), false);\n    }\n\n    String(zval *v) {\n        str = zval_get_string(v);\n    }\n\n    String(zend_string *v, bool copy) {\n        if (copy) {\n            str = zend_string_copy(v);\n        } else {\n            str = v;\n        }\n    }\n\n    String(const String &o) {\n        str = zend_string_copy(o.str);\n    }\n\n    String(String &&o) noexcept {\n        str = o.str;\n        o.str = nullptr;\n    }\n\n    String &operator=(zval *v) {\n        release();\n        str = zval_get_string(v);\n        return *this;\n    }\n\n    String &operator=(String &&o) noexcept {\n        release();\n        str = o.str;\n        o.str = nullptr;\n        return *this;\n    }\n\n    String &operator=(const String &o) {\n        if (&o == this) {\n            return *this;\n        }\n        release();\n        str = zend_string_copy(o.str);\n        return *this;\n    }\n\n    char *val() const {\n        return ZSTR_VAL(str);\n    }\n\n    size_t len() const {\n        return ZSTR_LEN(str);\n    }\n\n    zend_string *get() const {\n        return str;\n    }\n\n    void rtrim() const {\n        ZSTR_LEN(str) = swoole::rtrim(val(), len());\n    }\n\n    std::string to_std_string() const {\n        return {val(), len()};\n    }\n\n    char *dup() const {\n        return sw_likely(len() > 0) ? sw_strndup(val(), len()) : nullptr;\n    }\n\n    char *edup() const {\n        return sw_likely(len() > 0) ? estrndup(val(), len()) : nullptr;\n    }\n\n    void release() {\n        if (str) {\n            zend_string_release(str);\n            str = nullptr;\n        }\n    }\n\n    ~String() {\n        release();\n    }\n\n  private:\n    zend_string *str;\n};\n\nclass KeyValue {\n  public:\n    zend_ulong index;\n    zend_string *key;\n    zval zvalue;\n\n    KeyValue(zend_ulong _index, zend_string *_key, zval *_zvalue) {\n        index = _index;\n        key = _key ? zend_string_copy(_key) : nullptr;\n        ZVAL_DEREF(_zvalue);\n        zvalue = *_zvalue;\n        Z_TRY_ADDREF(zvalue);\n    }\n\n    void add_to(zval *zarray) {\n        HashTable *ht = Z_ARRVAL_P(zarray);\n        zval *dest_elem = !key ? zend_hash_index_update(ht, index, &zvalue) : zend_hash_update(ht, key, &zvalue);\n        Z_TRY_ADDREF_P(dest_elem);\n    }\n\n    ~KeyValue() {\n        if (key) {\n            zend_string_release(key);\n        }\n        zval_ptr_dtor(&zvalue);\n    }\n};\n\nclass ArrayIterator {\n  public:\n    explicit ArrayIterator(Bucket *p) {\n        _ptr = p;\n        _key = _ptr->key;\n        _val = &_ptr->val;\n        _index = _ptr->h;\n        pe = p;\n    }\n\n    ArrayIterator(Bucket *p, Bucket *_pe) {\n        _ptr = p;\n        _key = _ptr->key;\n        _val = &_ptr->val;\n        _index = _ptr->h;\n        pe = _pe;\n        skipUndefBucket();\n    }\n\n    void operator++(int i) {\n        ++_ptr;\n        skipUndefBucket();\n    }\n\n    bool operator!=(ArrayIterator b) const {\n        return b.ptr() != _ptr;\n    }\n\n    std::string key() const {\n        return {_key->val, _key->len};\n    }\n\n    zend_ulong index() const {\n        return _index;\n    }\n\n    zval *value() const {\n        return _val;\n    }\n\n    Bucket *ptr() const {\n        return _ptr;\n    }\n\n  private:\n    void skipUndefBucket() {\n        while (_ptr != pe) {\n            _val = &_ptr->val;\n            if (_val && Z_TYPE_P(_val) == IS_INDIRECT) {\n                _val = Z_INDIRECT_P(_val);\n            }\n            if (UNEXPECTED(Z_TYPE_P(_val) == IS_UNDEF)) {\n                ++_ptr;\n                continue;\n            }\n            if (_ptr->key) {\n                _key = _ptr->key;\n                _index = 0;\n            } else {\n                _index = _ptr->h;\n                _key = nullptr;\n            }\n            break;\n        }\n    }\n\n    zval *_val;\n    zend_string *_key;\n    Bucket *_ptr;\n    Bucket *pe;\n    zend_ulong _index;\n};\n\nclass Array {\n  public:\n    zval *arr;\n\n    Array(zval *_arr) {\n        assert(Z_TYPE_P(_arr) == IS_ARRAY);\n        arr = _arr;\n    }\n\n    size_t count() const {\n        return zend_hash_num_elements(Z_ARRVAL_P(arr));\n    }\n\n    bool set(zend_ulong index, zval *value) const {\n        return add_index_zval(arr, index, value) == SUCCESS;\n    }\n\n    bool append(zval *value) const {\n        return add_next_index_zval(arr, value) == SUCCESS;\n    }\n\n    bool set(zend_ulong index, zend_resource *res) const {\n        zval tmp;\n        ZVAL_RES(&tmp, res);\n        return set(index, &tmp);\n    }\n\n    ArrayIterator begin() const {\n        return {Z_ARRVAL_P(arr)->arData, Z_ARRVAL_P(arr)->arData + Z_ARRVAL_P(arr)->nNumUsed};\n    }\n\n    ArrayIterator end() const {\n        return ArrayIterator(Z_ARRVAL_P(arr)->arData + Z_ARRVAL_P(arr)->nNumUsed);\n    }\n};\n\nclass Variable {\n  public:\n    zval value;\n\n    Variable() {\n        value = {};\n    }\n\n    Variable(zval *zvalue) {\n        assign(zvalue);\n    }\n\n    Variable(const char *str, size_t l_str) {\n        ZVAL_STRINGL(&value, str, l_str);\n    }\n\n    Variable(const char *str) {\n        ZVAL_STRING(&value, str);\n    }\n\n    Variable(const std::string &str) {\n        ZVAL_STRINGL(&value, str.c_str(), str.length());\n    }\n\n    Variable(const Variable &&src) noexcept {\n        value = src.value;\n        add_ref();\n    }\n\n    Variable(Variable &&src) noexcept {\n        value = src.value;\n        src.reset();\n    }\n\n    Variable &operator=(zval *zvalue) {\n        assign(zvalue);\n        return *this;\n    }\n\n    Variable &operator=(const Variable &src) {\n        value = src.value;\n        add_ref();\n        return *this;\n    }\n\n    void assign(zval *zvalue) {\n        value = *zvalue;\n        add_ref();\n    }\n\n    zval *ptr() {\n        return &value;\n    }\n\n    void reset() {\n        ZVAL_UNDEF(&value);\n    }\n\n    void add_ref() {\n        Z_TRY_ADDREF_P(&value);\n    }\n\n    void del_ref() {\n        Z_TRY_DELREF_P(&value);\n    }\n\n    ~Variable() {\n        zval_ptr_dtor(&value);\n    }\n};\n\nclass CharPtr {\n  private:\n    char *str_;\n\n  public:\n    CharPtr() {\n        str_ = nullptr;\n    }\n\n    CharPtr(const char *str) {\n        str_ = estrndup(str, strlen(str));\n    }\n\n    CharPtr(const char *str, size_t len) {\n        str_ = estrndup(str, len);\n    }\n\n    CharPtr &operator=(const char *str) {\n        assign(str, strlen(str));\n        return *this;\n    }\n\n    void release() {\n        if (str_) {\n            efree(str_);\n            str_ = nullptr;\n        }\n    }\n\n    void assign(const char *str, size_t len) {\n        release();\n        str_ = estrndup(str, len);\n    }\n\n    void assign_tolower(const char *str, size_t len) {\n        release();\n        str_ = zend_str_tolower_dup(str, len);\n    }\n\n    ~CharPtr() {\n        release();\n    }\n\n    char *get() const {\n        return str_;\n    }\n};\n\nclass Callable {\n  private:\n    zval zfn;\n    zend_fcall_info_cache fcc;\n    char *fn_name = nullptr;\n\n    Callable() {}\n\n  public:\n    Callable(zval *_zfn);\n    ~Callable();\n    uint32_t refcount() const;\n\n    zend_refcounted *refcount_ptr() {\n        return sw_get_refcount_ptr(&zfn);\n    }\n\n    zend_fcall_info_cache *ptr() {\n        return &fcc;\n    }\n\n    bool ready() const {\n        return !ZVAL_IS_UNDEF(&zfn);\n    }\n\n    Callable *dup() const {\n        auto copy = new Callable();\n        copy->fcc = fcc;\n        copy->zfn = zfn;\n        zval_add_ref(&copy->zfn);\n        if (fn_name) {\n            copy->fn_name = estrdup(fn_name);\n        }\n        return copy;\n    }\n\n    bool call(uint32_t argc, zval *argv, zval *retval) {\n        return sw_zend_call_function_ex(&zfn, &fcc, argc, argv, retval) == SUCCESS;\n    }\n};\n\n#define SW_CONCURRENCY_HASHMAP_LOCK(code)                                                                              \\\n    if (locked_) {                                                                                                     \\\n        code;                                                                                                          \\\n    } else {                                                                                                           \\\n        lock_.lock();                                                                                                  \\\n        code;                                                                                                          \\\n        lock_.unlock();                                                                                                \\\n    }\n\ntemplate <typename KeyT, typename ValueT>\nclass ConcurrencyHashMap {\n    std::unordered_map<KeyT, ValueT> map_;\n    std::mutex lock_;\n    bool locked_;\n    ValueT default_value_;\n\n  public:\n    ConcurrencyHashMap(ValueT _default_value) : map_(), lock_() {\n        default_value_ = _default_value;\n        locked_ = false;\n    }\n\n    void set(const KeyT &key, const ValueT &value) {\n        SW_CONCURRENCY_HASHMAP_LOCK(map_[key] = value);\n    }\n\n    ValueT get(const KeyT &key) {\n        ValueT value;\n        auto fn = [&]() -> ValueT {\n            auto iter = map_.find(key);\n            if (iter == map_.end()) {\n                return default_value_;\n            }\n            return iter->second;\n        };\n        SW_CONCURRENCY_HASHMAP_LOCK(value = fn());\n        return value;\n    }\n\n    bool exists(const KeyT &key) {\n        std::unique_lock<std::mutex> _lock(lock_);\n        return map_.find(key) != map_.end();\n    }\n\n    void del(const KeyT &key) {\n        SW_CONCURRENCY_HASHMAP_LOCK(map_.erase(key));\n    }\n\n    void clear() {\n        SW_CONCURRENCY_HASHMAP_LOCK(map_.clear());\n    }\n\n    void each(const std::function<void(KeyT key, ValueT value)> &cb) {\n        std::unique_lock<std::mutex> _lock(lock_);\n        locked_ = true;\n        for (auto &iter : map_) {\n            cb(iter.first, iter.second);\n        }\n        locked_ = false;\n    }\n};\n\nnamespace function {\n/* must use this API to call event callbacks to ensure that exceptions are handled correctly */\nbool call(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *retval, const bool enable_coroutine);\nVariable call(const std::string &func_name, int argc, zval *argv);\n\nstatic inline bool call(Callable *cb, uint32_t argc, zval *argv, zval *retval, const bool enable_coroutine) {\n    return call(cb->ptr(), argc, argv, retval, enable_coroutine);\n}\n}  // namespace function\n\nstruct Function {\n    zend_fcall_info fci;\n    zend_fcall_info_cache fci_cache;\n\n    bool call(zval *retval, const bool enable_coroutine) {\n        return function::call(&fci_cache, fci.param_count, fci.params, retval, enable_coroutine);\n    }\n};\n\nvoid known_strings_init();\nvoid known_strings_dtor();\nvoid unserialize(zval *return_value, const char *buf, size_t buf_len, HashTable *options);\nvoid json_decode(zval *return_value, const char *str, size_t str_len, zend_long options, zend_long zend_long);\nzend_function *get_function(const char *fname, size_t fname_len);\nzend_function *get_function(const std::string &fname);\nzend_function *get_function(const zend_string *fname);\nzend_function *get_function(const zend_array *function_table, const char *name, size_t name_len);\n\nstatic inline zend_string *fetch_zend_string_by_val(void *val) {\n    return (zend_string *) ((char *) val - XtOffsetOf(zend_string, val));\n}\n\nstatic inline void assign_zend_string_by_val(zval *zdata, char *addr, size_t length) {\n    zend_string *zstr = fetch_zend_string_by_val(addr);\n    addr[length] = 0;\n    zstr->len = length;\n    ZVAL_STR(zdata, zstr);\n}\n\nstatic inline void array_set(zval *arg, const char *key, size_t l_key, zval *zvalue) {\n    Z_TRY_ADDREF_P(zvalue);\n    add_assoc_zval_ex(arg, key, l_key, zvalue);\n}\n\nstatic inline void array_set(zval *arg, const char *key, size_t l_key, const char *value, size_t l_value) {\n    zval ztmp;\n    ZVAL_STRINGL(&ztmp, value, l_value);\n    add_assoc_zval_ex(arg, key, l_key, &ztmp);\n}\n\nstatic inline void array_set(zval *arg, const char *key, size_t l_key, bool value) {\n    add_assoc_bool_ex(arg, key, l_key, value);\n}\n\nstatic inline void array_set(zval *arg, const char *key, size_t l_key, const char *value) {\n    add_assoc_string_ex(arg, key, l_key, value);\n}\n\nstatic inline void array_set(zval *arg, zend_ulong index, zval *zvalue) {\n    Z_TRY_ADDREF_P(zvalue);\n    zend_hash_index_add(Z_ARRVAL_P(arg), index, zvalue);\n}\n\nstatic inline void array_add(zval *arg, zval *zvalue) {\n    Z_TRY_ADDREF_P(zvalue);\n    add_next_index_zval(arg, zvalue);\n}\n\n/**\n * return reference\n */\nstatic inline zval *array_get(zval *arg, const char *key, size_t l_key) {\n    return zend_hash_str_find(Z_ARRVAL_P(arg), key, l_key);\n}\n\nstatic inline zval *array_get(zval *arg, zend_ulong index) {\n    return zend_hash_index_find(Z_ARRVAL_P(arg), index);\n}\n\nstatic inline void array_unset(zval *arg, const char *key, size_t l_key) {\n    zend_hash_str_del(Z_ARRVAL_P(arg), key, l_key);\n}\n\n/**\n * Add new element to the associative array or merge with existing elements.\n * If the key does not exist, add it to the array.\n * If the key already exists, merge all into a two-dimensional array.\n */\nvoid array_add_or_merge(zval *zarray, const char *key, size_t key_len, zval *new_element);\n\nstatic inline zend_long object_get_long(zval *obj, zend_string *key) {\n    static zval rv;\n    zval *property = zend_read_property_ex(Z_OBJCE_P(obj), Z_OBJ_P(obj), key, true, &rv);\n    return property ? zval_get_long(property) : 0;\n}\n\nstatic inline zend_long object_get_long(zval *obj, const char *key, size_t l_key) {\n    static zval rv;\n    zval *property = zend_read_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), key, l_key, true, &rv);\n    return property ? zval_get_long(property) : 0;\n}\n\nstatic inline zend_long object_get_long(zend_object *obj, const char *key, size_t l_key) {\n    static zval rv;\n    zval *property = zend_read_property(obj->ce, obj, key, l_key, true, &rv);\n    return property ? zval_get_long(property) : 0;\n}\n\nstatic inline void object_set(zval *obj, const char *name, size_t l_name, zval *zvalue) {\n    zend_update_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, zvalue);\n}\n\nstatic inline void object_set(zval *obj, const char *name, size_t l_name, const char *value) {\n    zend_update_property_string(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, value);\n}\n\nstatic inline void object_set(zval *obj, const char *name, size_t l_name, zend_long value) {\n    zend_update_property_long(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, value);\n}\n\nstatic inline void object_set(zend_object *obj, zend_string *name, zval *zvalue) {\n    zend_update_property_ex(obj->ce, obj, name, zvalue);\n}\n\nstatic inline void object_set(zend_object *obj, zend_string *name, zend_string *value) {\n    zval tmp;\n    ZVAL_STR(&tmp, value);\n    zend_update_property_ex(obj->ce, obj, name, &tmp);\n}\n\nstatic inline void object_set(zend_object *obj, zend_string *name, zend_long value) {\n    zval tmp;\n    ZVAL_LONG(&tmp, value);\n    zend_update_property_ex(obj->ce, obj, name, &tmp);\n}\n\nstatic inline zval *object_get(zval *obj, const char *name, size_t l_name) {\n    static zval rv;\n    return zend_read_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, true, &rv);\n}\n\n/**\n * print exception, The virtual machine will not be terminated.\n */\nstatic inline void print_error(zend_object *exception, int severity) {\n    zend_exception_error(exception, severity);\n}\n\nstatic inline void add_constant(const char *name, zend_long value) {\n    zend_register_long_constant(name, strlen(name), value, CONST_CS | CONST_PERSISTENT, sw_module_number());\n}\n\nstatic inline void add_constant(const char *name, const char *value) {\n    zend_register_string_constant(name, strlen(name), value, CONST_CS | CONST_PERSISTENT, sw_module_number());\n}\n\n//-----------------------------------namespace end--------------------------------------------\n}  // namespace zend\n\n/* use void* to match some C callback function pointers */\nstatic inline void sw_callable_free(void *ptr) {\n    delete (zend::Callable *) ptr;\n}\n\nstatic inline zend::Callable *sw_callable_create(zval *zfn) {\n    auto fn = new zend::Callable(zfn);\n    if (fn->ready()) {\n        return fn;\n    } else {\n        delete fn;\n        return nullptr;\n    }\n}\n\nstatic inline zend::Callable *sw_callable_create_ex(zval *zfn, const char *fname, bool allow_null = true) {\n    if (zfn == nullptr || ZVAL_IS_NULL(zfn)) {\n        if (!allow_null) {\n            zend_throw_exception_ex(\n                swoole_exception_ce, SW_ERROR_INVALID_PARAMS, \"%s must be of type callable, null given\", fname);\n        }\n        return nullptr;\n    }\n    auto cb = sw_callable_create(zfn);\n    if (!cb) {\n        zend_throw_exception_ex(swoole_exception_ce,\n                                SW_ERROR_INVALID_PARAMS,\n                                \"%s must be of type callable, %s given\",\n                                fname,\n                                zend_zval_type_name(zfn));\n        return nullptr;\n    }\n    return cb;\n}\n"
  },
  {
    "path": "ext-src/php_swoole_firebird.h",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2018 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: NathanFreeman  <mariasocute@163.com>                         |\n +----------------------------------------------------------------------+\n*/\n#ifndef SWOOLE_SRC_PHP_SWOOLE_FIREBIRD_H\n#define SWOOLE_SRC_PHP_SWOOLE_FIREBIRD_H\n\n#include \"php_swoole.h\"\n\n#ifdef SW_USE_FIREBIRD\n\nBEGIN_EXTERN_C()\n\n#include \"ext/pdo/php_pdo_driver.h\"\n\n#if PHP_VERSION_ID < 80500\n#include \"thirdparty/php84/pdo_firebird/php_pdo_firebird_int.h\"\n#else\n#include \"thirdparty/php85/pdo_firebird/php_pdo_firebird_int.h\"\n#endif\n\nextern const pdo_driver_t swoole_pdo_firebird_driver;\nvoid swoole_firebird_set_blocking(bool blocking);\n\nISC_STATUS swoole_isc_attach_database(\n    ISC_STATUS *, short, const ISC_SCHAR *, isc_db_handle *, short, const ISC_SCHAR *);\nISC_STATUS swoole_isc_detach_database(ISC_STATUS *, isc_db_handle *);\nISC_STATUS swoole_isc_dsql_execute(ISC_STATUS *, isc_tr_handle *, isc_stmt_handle *, unsigned short, const XSQLDA *);\nISC_STATUS swoole_isc_dsql_execute2(\n    ISC_STATUS *, isc_tr_handle *, isc_stmt_handle *, unsigned short, const XSQLDA *, const XSQLDA *);\nISC_STATUS swoole_isc_dsql_sql_info(ISC_STATUS *, isc_stmt_handle *, short, const ISC_SCHAR *, short, ISC_SCHAR *);\nISC_STATUS swoole_isc_dsql_free_statement(ISC_STATUS *, isc_stmt_handle *, unsigned short);\nISC_STATUS swoole_isc_start_transaction(ISC_STATUS *, isc_tr_handle *, short, isc_db_handle *, size_t, char *);\nISC_STATUS swoole_isc_commit_retaining(ISC_STATUS *, isc_tr_handle *);\nISC_STATUS swoole_isc_commit_transaction(ISC_STATUS *, isc_tr_handle *);\nISC_STATUS swoole_isc_rollback_transaction(ISC_STATUS *, isc_tr_handle *);\nISC_STATUS swoole_isc_dsql_allocate_statement(ISC_STATUS *, isc_db_handle *, isc_stmt_handle *);\nISC_STATUS swoole_isc_dsql_prepare(\n    ISC_STATUS *, isc_tr_handle *, isc_stmt_handle *, unsigned short, const ISC_SCHAR *, unsigned short, XSQLDA *);\nISC_STATUS swoole_isc_dsql_fetch(ISC_STATUS *, isc_stmt_handle *, unsigned short, const XSQLDA *);\nISC_STATUS swoole_isc_open_blob(ISC_STATUS *, isc_db_handle *, isc_tr_handle *, isc_blob_handle *, ISC_QUAD *);\nISC_STATUS swoole_isc_blob_info(ISC_STATUS *, isc_blob_handle *, short, const ISC_SCHAR *, short, ISC_SCHAR *);\nISC_STATUS swoole_isc_get_segment(ISC_STATUS *, isc_blob_handle *, unsigned short *, unsigned short, ISC_SCHAR *);\nISC_STATUS swoole_isc_put_segment(ISC_STATUS *, isc_blob_handle *, unsigned short, const ISC_SCHAR *);\nISC_STATUS swoole_isc_close_blob(ISC_STATUS *, isc_blob_handle *);\nISC_STATUS swoole_isc_create_blob(ISC_STATUS *, isc_db_handle *, isc_tr_handle *, isc_blob_handle *, ISC_QUAD *);\nISC_STATUS swoole_isc_dsql_set_cursor_name(ISC_STATUS *, isc_stmt_handle *, const ISC_SCHAR *, unsigned short);\nISC_STATUS swoole_fb_ping(ISC_STATUS *, isc_db_handle *);\nint swoole_isc_version(isc_db_handle *, ISC_VERSION_CALLBACK, void *);\n\n#ifdef SW_USE_FIREBIRD_HOOK\n#define isc_attach_database swoole_isc_attach_database\n#define isc_detach_database swoole_isc_detach_database\n#define isc_dsql_execute swoole_isc_dsql_execute\n#define isc_dsql_execute2 swoole_isc_dsql_execute2\n#define isc_dsql_sql_info swoole_isc_dsql_sql_info\n#define isc_dsql_free_statement swoole_isc_dsql_free_statement\n#define isc_start_transaction swoole_isc_start_transaction\n#define isc_commit_retaining swoole_isc_commit_retaining\n#define isc_commit_transaction swoole_isc_commit_transaction\n#define isc_rollback_transaction swoole_isc_rollback_transaction\n#define isc_dsql_allocate_statement swoole_isc_dsql_allocate_statement\n#define isc_dsql_prepare swoole_isc_dsql_prepare\n#define isc_dsql_fetch swoole_isc_dsql_fetch\n#define isc_open_blob swoole_isc_open_blob\n#define isc_blob_info swoole_isc_blob_info\n#define isc_get_segment swoole_isc_get_segment\n#define isc_put_segment swoole_isc_put_segment\n#define isc_create_blob swoole_isc_create_blob\n#define isc_close_blob swoole_isc_close_blob\n#define isc_dsql_set_cursor_name swoole_isc_dsql_set_cursor_name\n#define fb_ping swoole_fb_ping\n#define isc_version swoole_isc_version\n#endif\nEND_EXTERN_C()\n#endif\n#endif\n"
  },
  {
    "path": "ext-src/php_swoole_ftp_def.h",
    "content": "#ifndef EXT_SRC_PHP_SWOOLE_FTP_DEF_H_\n#define EXT_SRC_PHP_SWOOLE_FTP_DEF_H_\n\n#include \"php.h\"\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\nBEGIN_EXTERN_C()\nZEND_FUNCTION(ftp_connect);\n#if defined(SW_HAVE_FTP_SSL)\nZEND_FUNCTION(ftp_ssl_connect);\n#endif\nZEND_FUNCTION(ftp_login);\nZEND_FUNCTION(ftp_pwd);\nZEND_FUNCTION(ftp_cdup);\nZEND_FUNCTION(ftp_chdir);\nZEND_FUNCTION(ftp_exec);\nZEND_FUNCTION(ftp_raw);\nZEND_FUNCTION(ftp_mkdir);\nZEND_FUNCTION(ftp_rmdir);\nZEND_FUNCTION(ftp_chmod);\nZEND_FUNCTION(ftp_alloc);\nZEND_FUNCTION(ftp_nlist);\nZEND_FUNCTION(ftp_rawlist);\nZEND_FUNCTION(ftp_mlsd);\nZEND_FUNCTION(ftp_systype);\nZEND_FUNCTION(ftp_fget);\nZEND_FUNCTION(ftp_nb_fget);\nZEND_FUNCTION(ftp_pasv);\nZEND_FUNCTION(ftp_get);\nZEND_FUNCTION(ftp_nb_get);\nZEND_FUNCTION(ftp_nb_continue);\nZEND_FUNCTION(ftp_fput);\nZEND_FUNCTION(ftp_nb_fput);\nZEND_FUNCTION(ftp_put);\nZEND_FUNCTION(ftp_append);\nZEND_FUNCTION(ftp_nb_put);\nZEND_FUNCTION(ftp_size);\nZEND_FUNCTION(ftp_mdtm);\nZEND_FUNCTION(ftp_rename);\nZEND_FUNCTION(ftp_delete);\nZEND_FUNCTION(ftp_site);\nZEND_FUNCTION(ftp_close);\nZEND_FUNCTION(ftp_set_option);\nZEND_FUNCTION(ftp_get_option);\n\nPHP_MINIT_FUNCTION(ftp);\nPHP_MINFO_FUNCTION(ftp);\nEND_EXTERN_C()\n\n#endif\n"
  },
  {
    "path": "ext-src/php_swoole_http.h",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2015 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#pragma once\n\n#include \"swoole_http.h\"\n#include \"swoole_http2.h\"\n#include \"swoole_llhttp.h\"\n#include \"swoole_websocket.h\"\n\n#include \"thirdparty/multipart_parser.h\"\n\n#include <unordered_map>\n#include <unordered_set>\n\n#ifdef SW_HAVE_ZLIB\n#include <zlib.h>\n#define SW_ZLIB_ENCODING_RAW -0xf\n#define SW_ZLIB_ENCODING_GZIP 0x1f\n#define SW_ZLIB_ENCODING_DEFLATE 0x0f\n#define SW_ZLIB_ENCODING_ANY 0x2f\n#if MAX_MEM_LEVEL >= 8\n#define SW_ZLIB_DEF_MEM_LEVEL 8\n#else\n#define SW_ZLIB_DEF_MEM_LEVEL MAX_MEM_LEVEL\n#endif\n#endif\n\n#ifdef SW_HAVE_BROTLI\n#include <brotli/encode.h>\n#include <brotli/decode.h>\n#endif\n\n#ifdef SW_HAVE_ZSTD\n#include <zstd.h>\n#endif\n\n#define HTTP_API\n\n#include <nghttp2/nghttp2.h>\n\nenum swHttpHeaderFlag {\n    HTTP_HEADER_SERVER = 1u << 1,\n    HTTP_HEADER_CONNECTION = 1u << 2,\n    HTTP_HEADER_CONTENT_LENGTH = 1u << 3,\n    HTTP_HEADER_DATE = 1u << 4,\n    HTTP_HEADER_CONTENT_TYPE = 1u << 5,\n    HTTP_HEADER_TRANSFER_ENCODING = 1u << 6,\n    HTTP_HEADER_ACCEPT_ENCODING = 1u << 7,\n    HTTP_HEADER_CONTENT_ENCODING = 1u << 8,\n};\n\nenum swHttpCompressMethod {\n    HTTP_COMPRESS_NONE,\n    HTTP_COMPRESS_GZIP,\n    HTTP_COMPRESS_DEFLATE,\n    HTTP_COMPRESS_BR,\n    HTTP_COMPRESS_ZSTD,\n};\n\nenum swHttpErrorStatusCode {\n    HTTP_ESTATUS_CONNECT_FAILED = -1,\n    HTTP_ESTATUS_REQUEST_TIMEOUT = -2,\n    HTTP_ESTATUS_SERVER_RESET = -3,\n    HTTP_ESTATUS_SEND_FAILED = -4,\n};\n\nnamespace swoole {\nclass Server;\nclass Coroutine;\n\nnamespace http2 {\nclass Stream;\nclass Session;\n}  // namespace http2\n\nnamespace http {\nstruct Request {\n    int version;\n    char *path;\n    uint32_t path_len;\n    const char *ext;\n    uint32_t ext_len;\n    uint8_t post_form_urlencoded;\n\n    zval zdata;\n    const char *body_at;\n    size_t body_length;\n    String *chunked_body;\n    String *h2_data_buffer;\n\n    // Notice: Do not change the order\n    zval *zobject;\n    zval _zobject;\n    zval *zserver;\n    zval _zserver;\n    zval *zheader;\n    zval _zheader;\n    zval *zget;\n    zval _zget;\n    zval *zpost;\n    zval _zpost;\n    zval *zcookie;\n    zval _zcookie;\n    zval *zfiles;\n    zval _zfiles;\n    zval *ztmpfiles;\n    zval _ztmpfiles;\n};\n\nstruct Response {\n    enum llhttp_method method;\n    int version;\n    int status;\n    char *reason;\n\n    // Notice: Do not change the order\n    zval *zobject;\n    zval _zobject;\n    zval *zheader;\n    zval _zheader;\n    zval *zcookie;\n    zval _zcookie;\n    zval *ztrailer;\n    zval _ztrailer;\n};\n\nstruct Context {\n    SessionId fd;\n    uchar completed : 1;\n    uchar end_ : 1;\n    uchar send_header_ : 1;\n#ifdef SW_HAVE_COMPRESSION\n    uchar enable_compression : 1;\n    uchar accept_compression : 1;\n    uchar content_compressed : 1;\n#endif\n    uchar send_chunked : 1;\n    uchar recv_chunked : 1;\n    uchar send_trailer_ : 1;\n    uchar keepalive : 1;\n    uchar websocket : 1;\n    uchar websocket_compression : 1;\n    uchar upgrade : 1;\n    uchar detached : 1;\n    uchar parse_cookie : 1;\n    uchar parse_body : 1;\n    uchar parse_files : 1;\n    uchar http2 : 1;\n\n    zval zsocket;\n    uint32_t stream_id;\n    String *write_buffer;\n\n#ifdef SW_HAVE_COMPRESSION\n    int8_t compression_level;\n    int8_t compression_method;\n    uint32_t compression_min_length;\n    std::shared_ptr<std::unordered_set<std::string>> compression_types;\n    std::shared_ptr<String> zlib_buffer;\n#endif\n\n    std::shared_ptr<String> continue_frame_buffer;\n    WebSocketSettings websocket_settings;\n\n    Request request;\n    Response response;\n\n    llhttp_t parser;\n    multipart_parser *mt_parser;\n\n    uint16_t input_var_num;\n    const char *current_header_name;\n    size_t current_header_name_len;\n    char *current_input_name;\n    size_t current_input_name_len;\n    char *current_form_data_name;\n    size_t current_form_data_name_len;\n    zval *current_multipart_header;\n    const char *tmp_content_type;\n    size_t tmp_content_type_len;\n    String *form_data_buffer;\n\n    std::string upload_tmp_dir;\n\n    // The `private_data` pointer is used to store Server or CoSocket object\n    void *private_data;\n    // The `private_data_2` pointer is used to save callback function\n    void *private_data_2;\n    bool (*send)(Context *ctx, const char *data, size_t length);\n    bool (*sendfile)(Context *ctx, zend_string *file, off_t offset, size_t length);\n    bool (*close)(Context *ctx);\n    bool (*onBeforeRequest)(Context *ctx);\n    void (*onAfterResponse)(Context *ctx);\n\n    void init(Server *server);\n    void init(zval *zsock);\n    void bind(Server *server);\n    void bind(zval *zsock);\n    void copy(const Context *ctx);\n    bool init_multipart_parser(const char *boundary_str, int boundary_len);\n    bool get_multipart_boundary(\n        const char *at, size_t length, size_t offset, char **out_boundary_str, int *out_boundary_len);\n    size_t parse(const char *data, size_t length);\n    bool parse_multipart_data(const char *at, size_t length) const;\n\n    HTTP_API bool set_header(const char *, size_t, zval *, bool);\n    HTTP_API bool set_header(const char *, size_t, const char *, size_t, bool);\n    HTTP_API bool set_header(const char *, size_t, const std::string &, bool);\n    HTTP_API void end(zend_string *sdata, zval *return_value);\n    HTTP_API void write(zend_string *sdata, zval *return_value);\n    HTTP_API bool send_file(zend_string *file, off_t offset, size_t length);\n\n    String *get_write_buffer();\n\n    String *get_http2_data_buffer() {\n        String *buffer = request.h2_data_buffer;\n        if (!buffer) {\n            buffer = new String(SW_HTTP2_DATA_BUFFER_SIZE);\n            request.h2_data_buffer = buffer;\n        }\n        return buffer;\n    }\n\n    size_t get_http2_data_length() {\n        if (request.h2_data_buffer) {\n            return request.h2_data_buffer->length;\n        } else {\n            return 0;\n        }\n    }\n\n    size_t get_content_length() const {\n        return parser.content_length;\n    }\n\n    bool is_co_socket() const {\n        return !ZVAL_IS_NULL(&zsocket);\n    }\n\n    Server *get_async_server() const {\n        return static_cast<Server *>(private_data);\n    }\n\n    SocketImpl *get_co_socket() const {\n        return static_cast<SocketImpl *>(private_data);\n    }\n\n#ifdef SW_HAVE_COMPRESSION\n    void set_compression_method(const char *accept_encoding, size_t length);\n    const char *get_content_encoding() const;\n    bool compress(const char *data, size_t length);\n#endif\n\n    bool is_available() const;\n    void free();\n\n  private:\n    void build_header(String *http_buffer, const char *body, size_t length);\n    ssize_t build_trailer(String *http_buffer) const;\n    void send_trailer(zval *return_value);\n};\n\nclass Cookie {\n    bool encode_;\n    smart_str buffer_ = {};\n\n  protected:\n    zend_string *name = nullptr;\n    zend_string *value = nullptr;\n    zend_string *path = nullptr;\n    zend_string *domain = nullptr;\n    zend_string *sameSite = nullptr;\n    zend_string *priority = nullptr;\n    zend_long expires = 0;\n    zend_bool secure = false;\n    zend_bool httpOnly = false;\n    zend_bool partitioned = false;\n\n  public:\n    explicit Cookie(bool _encode = true) {\n        encode_ = _encode;\n    }\n    Cookie *withName(zend_string *);\n    Cookie *withExpires(zend_long);\n    Cookie *withSecure(zend_bool);\n    Cookie *withHttpOnly(zend_bool);\n    Cookie *withPartitioned(zend_bool);\n    Cookie *withValue(zend_string *);\n    Cookie *withPath(zend_string *);\n    Cookie *withDomain(zend_string *);\n    Cookie *withSameSite(zend_string *);\n    Cookie *withPriority(zend_string *);\n    void reset();\n    void toArray(zval *return_value) const;\n    zend_string *toString();\n    ~Cookie();\n};\n\n}  // namespace http\n\nnamespace http2 {\nclass Stream {\n  public:\n    http::Context *ctx;\n    // uint8_t priority; // useless now\n    uint32_t id;\n    // flow control\n    uint32_t remote_window_size;\n    uint32_t local_window_size;\n    Coroutine *waiting_coroutine = nullptr;\n\n    Stream(const Session *client, uint32_t _id);\n    ~Stream();\n\n    bool send_header(const String *body, bool end_stream) const;\n    bool send_body(const String *body,\n                   bool end_stream,\n                   const std::shared_ptr<Session> &session,\n                   off_t offset = 0,\n                   size_t length = 0);\n    bool send_end_stream_data_frame() const;\n    bool send_trailer() const;\n\n    void reset(uint32_t error_code) const;\n};\n\nclass Session {\n  public:\n    SessionId fd;\n    std::unordered_map<uint32_t, std::shared_ptr<Stream>> streams;\n\n    nghttp2_hd_inflater *inflater = nullptr;\n    nghttp2_hd_deflater *deflater = nullptr;\n\n    Settings local_settings = {};\n    Settings remote_settings = {};\n\n    // flow control\n    uint32_t remote_window_size;\n    uint32_t local_window_size;\n\n    uint32_t max_body_size;\n    uint32_t last_stream_id;\n    bool shutting_down;\n    bool is_coro;\n\n    http::Context *default_ctx = nullptr;\n    void *private_data = nullptr;\n\n    void (*handle)(const std::shared_ptr<Session> &, const std::shared_ptr<Stream> &) = nullptr;\n\n    explicit Session(SessionId _fd);\n    ~Session();\n    std::shared_ptr<Stream> create_stream(uint32_t stream_id);\n    std::shared_ptr<Stream> get_stream(uint32_t stream_id);\n    bool remove_stream(uint32_t stream_id);\n};\n}  // namespace http2\n}  // namespace swoole\n\nextern zend_class_entry *swoole_http_request_ce;\nextern zend_class_entry *swoole_http_response_ce;\nextern zend_class_entry *swoole_http_cookie_ce;\n\nswoole::http::Context *swoole_http_context_new(swoole::SessionId fd);\nswoole::http::Cookie *php_swoole_http_get_cooke_safety(const zval *zobject);\n\n/**\n *  These class properties cannot be modified by the user before assignment, such as Swoole\\\\Http\\\\Request.\n *  So we can use this function to init property.\n */\nstatic sw_inline zval *swoole_http_init_and_read_property(const zend_class_entry *ce,\n                                                          const zval *zobject,\n                                                          zval **zproperty_store_pp,\n                                                          zend_string *name,\n                                                          int size = HT_MIN_SIZE) {\n    if (UNEXPECTED(!*zproperty_store_pp)) {\n        zval *zv = zend_hash_find(&ce->properties_info, name);\n        auto *property_info = (zend_property_info *) Z_PTR_P(zv);\n        zval *property = OBJ_PROP(SW_Z8_OBJ_P(zobject), property_info->offset);\n        array_init_size(property, size);\n        *zproperty_store_pp = (zval *) (zproperty_store_pp + 1);\n        **zproperty_store_pp = *property;\n    }\n    return *zproperty_store_pp;\n}\n\nstatic sw_inline zval *swoole_http_init_and_read_property(\n    zend_class_entry *ce, const zval *zobject, zval **zproperty_store_pp, const char *name, size_t name_len) {\n    if (UNEXPECTED(!*zproperty_store_pp)) {\n        // Notice: swoole http server properties can not be unset anymore, so we can read it without checking\n        zval rv, *property = zend_read_property(ce, SW_Z8_OBJ_P(zobject), name, name_len, false, &rv);\n        array_init(property);\n        *zproperty_store_pp = (zval *) (zproperty_store_pp + 1);\n        **zproperty_store_pp = *property;\n    }\n    return *zproperty_store_pp;\n}\n\nstatic sw_inline bool swoole_http_has_crlf(const char *value, size_t length) {\n    /* new line/NUL character safety check */\n    for (size_t i = 0; i < length; i++) {\n        /* RFC 7230 ch. 3.2.4 deprecates folding support */\n        if (value[i] == '\\n' || value[i] == '\\r') {\n            php_swoole_error(E_WARNING, \"Header may not contain more than a single header, new line detected\");\n            return true;\n        }\n        if (value[i] == '\\0') {\n            php_swoole_error(E_WARNING, \"Header may not contain NUL bytes\");\n            return true;\n        }\n    }\n\n    return false;\n}\n\nvoid swoole_http_parse_cookie(zval *array, const char *at, size_t length);\nbool swoole_http_token_list_contains_value(const char *at, size_t length, const char *value);\n\nswoole::http::Context *php_swoole_http_request_get_context(const zval *zobject);\nvoid php_swoole_http_request_set_context(const zval *zobject, swoole::http::Context *ctx);\nswoole::http::Context *php_swoole_http_response_get_context(const zval *zobject);\nvoid php_swoole_http_response_set_context(const zval *zobject, swoole::http::Context *ctx);\nzend_string *php_swoole_http_get_date();\n\n#ifdef SW_HAVE_ZLIB\nvoidpf php_zlib_alloc(voidpf opaque, uInt items, uInt size);\nvoid php_zlib_free(voidpf opaque, voidpf address);\n#endif\n\n#ifdef SW_HAVE_BROTLI\nvoid *php_brotli_alloc(void *opaque, size_t size);\nvoid php_brotli_free(void *opaque, void *address);\n#endif\n\nstatic sw_inline nghttp2_mem *php_nghttp2_mem() {\n    static nghttp2_mem mem = {nullptr,\n                              [](size_t size, void *mem_user_data) { return emalloc(size); },\n                              [](void *ptr, void *mem_user_data) { return efree(ptr); },\n                              [](size_t nmemb, size_t size, void *mem_user_data) { return ecalloc(nmemb, size); },\n                              [](void *ptr, size_t size, void *mem_user_data) { return erealloc(ptr, size); }};\n    return &mem;\n}\n\nnamespace swoole {\nnamespace http2 {\n//-----------------------------------namespace begin--------------------------------------------\nclass HeaderSet {\n  public:\n    explicit HeaderSet(size_t size) : size(size), index(0) {\n        nvs = (nghttp2_nv *) ecalloc(size, sizeof(nghttp2_nv));\n    }\n\n    nghttp2_nv *get() const {\n        return nvs;\n    }\n\n    size_t len() const {\n        return index;\n    }\n\n    void reserve_one() {\n        index++;\n    }\n\n    void add(size_t index,\n             const char *name,\n             size_t name_len,\n             const char *value,\n             size_t value_len,\n             const uint8_t flags = NGHTTP2_NV_FLAG_NONE) const {\n        if (sw_likely(index < size || nvs[index].name == nullptr)) {\n            nghttp2_nv *nv = &nvs[index];\n            name = zend_str_tolower_dup(name, name_len);  // auto to lower\n            nv->name = (uchar *) name;\n            nv->namelen = name_len;\n            nv->value = (uchar *) emalloc(value_len);\n            memcpy(nv->value, value, value_len);\n            nv->valuelen = value_len;\n            nv->flags = flags | NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE;\n            swoole_trace_log(SW_TRACE_HTTP2,\n                             \"name=(%zu)[\" SW_ECHO_LEN_BLUE \"], value=(%zu)[\" SW_ECHO_LEN_CYAN \"]\",\n                             name_len,\n                             (int) name_len,\n                             name,\n                             value_len,\n                             (int) value_len,\n                             value);\n        } else {\n            php_swoole_fatal_error(\n                E_WARNING, \"unexpect http2 header [%.*s] (duplicated or overflow)\", (int) name_len, name);\n        }\n    }\n\n    void add(const char *name,\n             size_t name_len,\n             const char *value,\n             size_t value_len,\n             const uint8_t flags = NGHTTP2_NV_FLAG_NONE) {\n        add(index++, name, name_len, value, value_len, flags);\n    }\n\n    ~HeaderSet() {\n        for (size_t i = 0; i < size; ++i) {\n            if (sw_likely(nvs[i].name /* && nvs[i].value */)) {\n                efree((void *) nvs[i].name);\n                efree((void *) nvs[i].value);\n            }\n        }\n        efree(nvs);\n    }\n\n  private:\n    nghttp2_nv *nvs;\n    size_t size;\n    size_t index;\n};\n//-----------------------------------namespace end--------------------------------------------\n}  // namespace http2\n}  // namespace swoole\n"
  },
  {
    "path": "ext-src/php_swoole_http_server.h",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2015 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#pragma once\n\n#include \"php_swoole_server.h\"\n#include \"php_swoole_http.h\"\n\n#include \"swoole_mime_type.h\"\n\nbool swoole_http_server_onBeforeRequest(swoole::http::Context *ctx);\nvoid swoole_http_server_onAfterResponse(swoole::http::Context *ctx);\nvoid swoole_http_server_populate_ip_and_port(\n    swoole::Server *server, HashTable *ht, swoole::Connection *conn, swoole::SessionId session_id, bool keepalive);\n\nint swoole_websocket_onMessage(swoole::Server *serv, swoole::RecvData *req);\nint swoole_websocket_onHandshake(swoole::Server *serv, swoole::ListenPort *port, swoole::http::Context *ctx);\nvoid swoole_websocket_onBeforeHandshakeResponse(swoole::http::Context *ctx);\nvoid swoole_websocket_onOpen(swoole::http::Context *ctx);\nvoid swoole_websocket_onRequest(swoole::http::Context *ctx);\nbool swoole_websocket_handshake(swoole::http::Context *ctx);\n\nint swoole_http2_server_parse(const std::shared_ptr<swoole::http2::Session> &client, const char *buf);\nint swoole_http2_server_onReceive(swoole::Server *serv, swoole::Connection *conn, swoole::RecvData *req);\n\nstd::shared_ptr<swoole::http2::Session> swoole_http2_server_session_new(swoole::SessionId fd);\nvoid swoole_http2_server_session_free(swoole::SessionId fd);\n\nbool swoole_http2_server_end(swoole::http::Context *ctx, zend_string *sdata);\nbool swoole_http2_server_write(swoole::http::Context *ctx, zend_string *sdata);\nbool swoole_http2_server_send_file(swoole::http::Context *ctx, zend_string *file, off_t offset, size_t length);\nbool swoole_http2_server_ping(swoole::http::Context *ctx);\nbool swoole_http2_server_goaway(swoole::http::Context *ctx, zend_long error_code, zend_string *sdata);\n\nstatic inline void http_server_add_server_array(HashTable *ht, zend_string *key, const char *value) {\n    zval tmp;\n    ZVAL_STRING(&tmp, value);\n    zend_hash_add_new(ht, key, &tmp);\n}\n\nstatic inline void http_server_add_server_array(HashTable *ht, zend_string *key, const char *value, size_t length) {\n    zval tmp;\n    ZVAL_STRINGL(&tmp, value, length);\n    zend_hash_add_new(ht, key, &tmp);\n}\n\nstatic inline void http_server_add_server_array(HashTable *ht, zend_string *key, zend_long value) {\n    zval tmp;\n    ZVAL_LONG(&tmp, value);\n    zend_hash_add_new(ht, key, &tmp);\n}\n\nstatic inline void http_server_add_server_array(HashTable *ht, zend_string *key, double value) {\n    zval tmp;\n    ZVAL_DOUBLE(&tmp, value);\n    zend_hash_add_new(ht, key, &tmp);\n}\n\nstatic inline void http_server_add_server_array(HashTable *ht, zend_string *key, zend_string *value) {\n    zval tmp;\n    ZVAL_STR(&tmp, value);\n    zend_hash_add_new(ht, key, &tmp);\n}\n\nstatic inline void http_server_add_server_array(HashTable *ht, zend_string *key, zval *value) {\n    zend_hash_add_new(ht, key, value);\n}\n\nstatic inline void http_server_set_object_fd_property(zend_object *object, const zend_class_entry *ce, long fd) {\n    auto *zv = zend_hash_find(&ce->properties_info, SW_ZSTR_KNOWN(SW_ZEND_STR_FD));\n    auto *property_info = static_cast<zend_property_info *>(Z_PTR_P(zv));\n    auto property = OBJ_PROP(object, property_info->offset);\n    ZVAL_LONG(property, fd);\n}\n"
  },
  {
    "path": "ext-src/php_swoole_library.h",
    "content": "/**\n * -----------------------------------------------------------------------\n * Generated by build-library.php, Please DO NOT modify!\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n */\n\n/* $Id: 54ad6d15fb4330384673e6d6a8317c46d0ca78a3 */\n\n#ifndef SWOOLE_LIBRARY_H\n#define SWOOLE_LIBRARY_H\n\n#if PHP_VERSION_ID < 80000\ntypedef zval zend_source_string_t;\n#else\ntypedef zend_string zend_source_string_t;\n#endif\n\n#if PHP_VERSION_ID < 80200\n#define ZEND_COMPILE_POSITION_DC\n#define ZEND_COMPILE_POSITION_RELAY_C\n#else\n#define ZEND_COMPILE_POSITION_DC , zend_compile_position position\n#define ZEND_COMPILE_POSITION_RELAY_C , position\n#endif\n\n#if PHP_VERSION_ID < 80000\n#define ZEND_STR_CONST\n#else\n#define ZEND_STR_CONST const\n#endif\n\n\nstatic zend_op_array *(*old_compile_string)(zend_source_string_t *source_string, ZEND_STR_CONST char *filename ZEND_COMPILE_POSITION_DC);\n\nstatic inline zend_op_array *_compile_string(zend_source_string_t *source_string, ZEND_STR_CONST char *filename ZEND_COMPILE_POSITION_DC) {\n    if (UNEXPECTED(EG(exception))) {\n        zend_exception_error(EG(exception), E_ERROR);\n        return NULL;\n    }\n    zend_op_array *opa = old_compile_string(source_string, filename ZEND_COMPILE_POSITION_RELAY_C);\n    opa->type = ZEND_USER_FUNCTION;\n    return opa;\n}\n\nstatic inline zend_bool _eval(const char *code, const char *filename) {\n    if (!old_compile_string) {\n        old_compile_string = zend_compile_string;\n    }\n    // overwrite\n    zend_compile_string = _compile_string;\n    int ret = (zend_eval_stringl((char *) code, strlen(code), NULL, (char *) filename) == SUCCESS);\n    // recover\n    zend_compile_string = old_compile_string;\n    return ret;\n}\n\n#endif\n\nstatic const char* swoole_library_source_constants =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"define('SWOOLE_LIBRARY', true);\\n\"\n    \"\\n\"\n    \"!defined('CURLOPT_HEADEROPT') && define('CURLOPT_HEADEROPT', 229);\\n\"\n    \"!defined('CURLOPT_PROXYHEADER') && define('CURLOPT_PROXYHEADER', 10228);\\n\"\n    \"!defined('CURLOPT_RESOLVE') && define('CURLOPT_RESOLVE', 10203);\\n\"\n    \"!defined('CURLOPT_UNIX_SOCKET_PATH') && define('CURLOPT_UNIX_SOCKET_PATH', 10231);\\n\";\n\nstatic const char* swoole_library_source_std_exec =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Coroutine\\\\System;\\n\"\n    \"\\n\"\n    \"function swoole_exec(string $command, &$output = null, &$returnVar = null)\\n\"\n    \"{\\n\"\n    \"    $result = System::exec($command);\\n\"\n    \"    if ($result) {\\n\"\n    \"        $outputList = explode(PHP_EOL, $result['output']);\\n\"\n    \"        foreach ($outputList as &$value) {\\n\"\n    \"            $value = rtrim($value);\\n\"\n    \"        }\\n\"\n    \"        if (($endLine = end($outputList)) === '') {\\n\"\n    \"            array_pop($outputList);\\n\"\n    \"            $endLine = end($outputList);\\n\"\n    \"        }\\n\"\n    \"        if ($output) {\\n\"\n    \"            $output = array_merge($output, $outputList);\\n\"\n    \"        } else {\\n\"\n    \"            $output = $outputList;\\n\"\n    \"        }\\n\"\n    \"        $returnVar = $result['code'];\\n\"\n    \"        return $endLine;\\n\"\n    \"    }\\n\"\n    \"    return false;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_shell_exec(string $cmd)\\n\"\n    \"{\\n\"\n    \"    $result = System::exec($cmd);\\n\"\n    \"    if ($result && $result['output'] !== '') {\\n\"\n    \"        return $result['output'];\\n\"\n    \"    }\\n\"\n    \"    return null;\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_constant =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole;\\n\"\n    \"\\n\"\n    \"class Constant\\n\"\n    \"{\\n\"\n    \"    /* {{{ EVENT */\\n\"\n    \"    public const EVENT_START = 'start';\\n\"\n    \"\\n\"\n    \"    public const EVENT_BEFORE_SHUTDOWN = 'beforeShutdown';\\n\"\n    \"\\n\"\n    \"    public const EVENT_SHUTDOWN = 'shutdown';\\n\"\n    \"\\n\"\n    \"    public const EVENT_WORKER_START = 'workerStart';\\n\"\n    \"\\n\"\n    \"    public const EVENT_WORKER_STOP = 'workerStop';\\n\"\n    \"\\n\"\n    \"    public const EVENT_BEFORE_RELOAD = 'beforeReload';\\n\"\n    \"\\n\"\n    \"    public const EVENT_AFTER_RELOAD = 'afterReload';\\n\"\n    \"\\n\"\n    \"    public const EVENT_TASK = 'task';\\n\"\n    \"\\n\"\n    \"    public const EVENT_FINISH = 'finish';\\n\"\n    \"\\n\"\n    \"    public const EVENT_WORKER_EXIT = 'workerExit';\\n\"\n    \"\\n\"\n    \"    public const EVENT_WORKER_ERROR = 'workerError';\\n\"\n    \"\\n\"\n    \"    public const EVENT_MANAGER_START = 'managerStart';\\n\"\n    \"\\n\"\n    \"    public const EVENT_MANAGER_STOP = 'managerStop';\\n\"\n    \"\\n\"\n    \"    public const EVENT_PIPE_MESSAGE = 'pipeMessage';\\n\"\n    \"\\n\"\n    \"    public const EVENT_CONNECT = 'connect';\\n\"\n    \"\\n\"\n    \"    public const EVENT_RECEIVE = 'receive';\\n\"\n    \"\\n\"\n    \"    public const EVENT_CLOSE = 'close';\\n\"\n    \"\\n\"\n    \"    public const EVENT_PACKET = 'packet';\\n\"\n    \"\\n\"\n    \"    public const EVENT_BUFFER_FULL = 'bufferFull';\\n\"\n    \"\\n\"\n    \"    public const EVENT_BUFFER_EMPTY = 'bufferEmpty';\\n\"\n    \"\\n\"\n    \"    public const EVENT_REQUEST = 'request';\\n\"\n    \"\\n\"\n    \"    public const EVENT_HANDSHAKE = 'handshake';\\n\"\n    \"\\n\"\n    \"    public const EVENT_BEFORE_HANDSHAKE_RESPONSE = 'beforeHandshakeResponse';\\n\"\n    \"\\n\"\n    \"    public const EVENT_OPEN = 'open';\\n\"\n    \"\\n\"\n    \"    public const EVENT_MESSAGE = 'message';\\n\"\n    \"\\n\"\n    \"    public const EVENT_DISCONNECT = 'disconnect';\\n\"\n    \"\\n\"\n    \"    /* }}} EVENT */\\n\"\n    \"\\n\"\n    \"    public const EVENT_ERROR = 'error';\\n\"\n    \"\\n\"\n    \"    /* {{{ OPTION */\\n\"\n    \"    public const OPTION_DEBUG_MODE = 'debug_mode';\\n\"\n    \"\\n\"\n    \"    public const OPTION_TRACE_FLAGS = 'trace_flags';\\n\"\n    \"\\n\"\n    \"    public const OPTION_LOG_FILE = 'log_file';\\n\"\n    \"\\n\"\n    \"    public const OPTION_LOG_LEVEL = 'log_level';\\n\"\n    \"\\n\"\n    \"    public const OPTION_LOG_DATE_FORMAT = 'log_date_format';\\n\"\n    \"\\n\"\n    \"    public const OPTION_LOG_DATE_WITH_MICROSECONDS = 'log_date_with_microseconds';\\n\"\n    \"\\n\"\n    \"    public const OPTION_LOG_ROTATION = 'log_rotation';\\n\"\n    \"\\n\"\n    \"    public const OPTION_DISPLAY_ERRORS = 'display_errors';\\n\"\n    \"\\n\"\n    \"    public const OPTION_DNS_SERVER = 'dns_server';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Socket DNS timeout in seconds.\\n\"\n    \"     */\\n\"\n    \"    public const OPTION_SOCKET_DNS_TIMEOUT = 'socket_dns_timeout';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Default socket connect timeout in seconds.\\n\"\n    \"     */\\n\"\n    \"    public const OPTION_SOCKET_CONNECT_TIMEOUT = 'socket_connect_timeout';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Default socket write timeout in seconds.\\n\"\n    \"     *\\n\"\n    \"     * This one works the same as option \\\"socket_send_timeout\\\", but has higher priority.\\n\"\n    \"     *\\n\"\n    \"     * @see Constant::OPTION_SOCKET_SEND_TIMEOUT\\n\"\n    \"     */\\n\"\n    \"    public const OPTION_SOCKET_WRITE_TIMEOUT = 'socket_write_timeout';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Default socket write timeout in seconds.\\n\"\n    \"     *\\n\"\n    \"     * This one works the same as option \\\"socket_write_timeout\\\", but has lower priority.\\n\"\n    \"     *\\n\"\n    \"     * @see Constant::OPTION_SOCKET_WRITE_TIMEOUT\\n\"\n    \"     */\\n\"\n    \"    public const OPTION_SOCKET_SEND_TIMEOUT = 'socket_send_timeout';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Default socket read timeout in seconds.\\n\"\n    \"     *\\n\"\n    \"     * This one works the same as option \\\"socket_recv_timeout\\\", but has higher priority.\\n\"\n    \"     *\\n\"\n    \"     * @see Constant::OPTION_SOCKET_RECV_TIMEOUT\\n\"\n    \"     */\\n\"\n    \"    public const OPTION_SOCKET_READ_TIMEOUT = 'socket_read_timeout';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Default socket read timeout in seconds.\\n\"\n    \"     *\\n\"\n    \"     * This one works the same as option \\\"socket_read_timeout\\\", but has lower priority.\\n\"\n    \"     *\\n\"\n    \"     * @see Constant::OPTION_SOCKET_READ_TIMEOUT\\n\"\n    \"     */\\n\"\n    \"    public const OPTION_SOCKET_RECV_TIMEOUT = 'socket_recv_timeout';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Default socket read/write timeout in seconds.\\n\"\n    \"     *\\n\"\n    \"     * This one has the highest priority than the other read/write timeout options:\\n\"\n    \"     * - Constant::OPTION_SOCKET_WRITE_TIMEOUT\\n\"\n    \"     * - Constant::OPTION_SOCKET_SEND_TIMEOUT\\n\"\n    \"     * - Constant::OPTION_SOCKET_READ_TIMEOUT\\n\"\n    \"     * - Constant::OPTION_SOCKET_RECV_TIMEOUT\\n\"\n    \"     *\\n\"\n    \"     * @see Constant::OPTION_SOCKET_SEND_TIMEOUT\\n\"\n    \"     * @see Constant::OPTION_SOCKET_WRITE_TIMEOUT\\n\"\n    \"     * @see Constant::OPTION_SOCKET_RECV_TIMEOUT\\n\"\n    \"     * @see Constant::OPTION_SOCKET_READ_TIMEOUT\\n\"\n    \"     */\\n\"\n    \"    public const OPTION_SOCKET_TIMEOUT = 'socket_timeout';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SOCKET_BUFFER_SIZE = 'socket_buffer_size';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP2_HEADER_TABLE_SIZE = 'http2_header_table_size';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP2_ENABLE_PUSH = 'http2_enable_push';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP2_MAX_CONCURRENT_STREAMS = 'http2_max_concurrent_streams';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP2_INIT_WINDOW_SIZE = 'http2_init_window_size';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP2_MAX_FRAME_SIZE = 'http2_max_frame_size';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP2_MAX_HEADER_LIST_SIZE = 'http2_max_header_list_size';\\n\"\n    \"\\n\"\n    \"    public const OPTION_AIO_CORE_WORKER_NUM = 'aio_core_worker_num';\\n\"\n    \"\\n\"\n    \"    public const OPTION_AIO_WORKER_NUM = 'aio_worker_num';\\n\"\n    \"\\n\"\n    \"    public const OPTION_AIO_MAX_WAIT_TIME = 'aio_max_wait_time';\\n\"\n    \"\\n\"\n    \"    public const OPTION_AIO_MAX_IDLE_TIME = 'aio_max_idle_time';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @since 6.0.0-beta\\n\"\n    \"     */\\n\"\n    \"    public const OPTION_IOURING_ENTRIES = 'iouring_entries';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @since 6.0.0-rc1\\n\"\n    \"     */\\n\"\n    \"    public const OPTION_IOURING_WORKERS = 'iouring_workers';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @since 6.0.0-rc1\\n\"\n    \"     */\\n\"\n    \"    public const OPTION_IOURING_FLAG = 'iouring_flag';\\n\"\n    \"\\n\"\n    \"    public const OPTION_ENABLE_SIGNALFD = 'enable_signalfd';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @since 6.1.0\\n\"\n    \"     */\\n\"\n    \"    public const OPTION_ENABLE_KQUEUE = 'enable_kqueue';\\n\"\n    \"\\n\"\n    \"    public const OPTION_WAIT_SIGNAL = 'wait_signal';\\n\"\n    \"\\n\"\n    \"    public const OPTION_DNS_CACHE_REFRESH_TIME = 'dns_cache_refresh_time';\\n\"\n    \"\\n\"\n    \"    public const OPTION_THREAD_NUM = 'thread_num';\\n\"\n    \"\\n\"\n    \"    public const OPTION_MIN_THREAD_NUM = 'min_thread_num';\\n\"\n    \"\\n\"\n    \"    public const OPTION_MAX_THREAD_NUM = 'max_thread_num';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @removed 6.1.0\\n\"\n    \"     */\\n\"\n    \"    public const OPTION_SOCKET_DONTWAIT = 'socket_dontwait';\\n\"\n    \"\\n\"\n    \"    public const OPTION_DNS_LOOKUP_RANDOM = 'dns_lookup_random';\\n\"\n    \"\\n\"\n    \"    public const OPTION_USE_ASYNC_RESOLVER = 'use_async_resolver';\\n\"\n    \"\\n\"\n    \"    public const OPTION_ENABLE_COROUTINE = 'enable_coroutine';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_PROTOCOLS = 'ssl_protocols';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_COMPRESS = 'ssl_compress';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_CERT_FILE = 'ssl_cert_file';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_KEY_FILE = 'ssl_key_file';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_PASSPHRASE = 'ssl_passphrase';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_HOST_NAME = 'ssl_host_name';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_VERIFY_PEER = 'ssl_verify_peer';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_ALLOW_SELF_SIGNED = 'ssl_allow_self_signed';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_CAFILE = 'ssl_cafile';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_CAPATH = 'ssl_capath';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_VERIFY_DEPTH = 'ssl_verify_depth';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_CIPHERS = 'ssl_ciphers';\\n\"\n    \"\\n\"\n    \"    public const OPTION_OPEN_EOF_CHECK = 'open_eof_check';\\n\"\n    \"\\n\"\n    \"    public const OPTION_OPEN_EOF_SPLIT = 'open_eof_split';\\n\"\n    \"\\n\"\n    \"    public const OPTION_PACKAGE_EOF = 'package_eof';\\n\"\n    \"\\n\"\n    \"    public const OPTION_OPEN_MQTT_PROTOCOL = 'open_mqtt_protocol';\\n\"\n    \"\\n\"\n    \"    public const OPTION_OPEN_LENGTH_CHECK = 'open_length_check';\\n\"\n    \"\\n\"\n    \"    public const OPTION_PACKAGE_LENGTH_TYPE = 'package_length_type';\\n\"\n    \"\\n\"\n    \"    public const OPTION_PACKAGE_LENGTH_OFFSET = 'package_length_offset';\\n\"\n    \"\\n\"\n    \"    public const OPTION_PACKAGE_BODY_OFFSET = 'package_body_offset';\\n\"\n    \"\\n\"\n    \"    public const OPTION_PACKAGE_LENGTH_FUNC = 'package_length_func';\\n\"\n    \"\\n\"\n    \"    public const OPTION_PACKAGE_MAX_LENGTH = 'package_max_length';\\n\"\n    \"\\n\"\n    \"    public const OPTION_BUFFER_HIGH_WATERMARK = 'buffer_high_watermark';\\n\"\n    \"\\n\"\n    \"    public const OPTION_BUFFER_LOW_WATERMARK = 'buffer_low_watermark';\\n\"\n    \"\\n\"\n    \"    public const OPTION_BIND_PORT = 'bind_port';\\n\"\n    \"\\n\"\n    \"    public const OPTION_BIND_ADDRESS = 'bind_address';\\n\"\n    \"\\n\"\n    \"    public const OPTION_OPEN_TCP_NODELAY = 'open_tcp_nodelay';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SOCKS5_HOST = 'socks5_host';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SOCKS5_PORT = 'socks5_port';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SOCKS5_USERNAME = 'socks5_username';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SOCKS5_PASSWORD = 'socks5_password';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP_PROXY_HOST = 'http_proxy_host';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP_PROXY_PORT = 'http_proxy_port';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP_PROXY_USERNAME = 'http_proxy_username';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP_PROXY_USER = 'http_proxy_user';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP_PROXY_PASSWORD = 'http_proxy_password';\\n\"\n    \"\\n\"\n    \"    public const OPTION_MAX_CORO_NUM = 'max_coro_num';\\n\"\n    \"\\n\"\n    \"    public const OPTION_MAX_COROUTINE = 'max_coroutine';\\n\"\n    \"\\n\"\n    \"    public const OPTION_ENABLE_DEADLOCK_CHECK = 'enable_deadlock_check';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HOOK_FLAGS = 'hook_flags';\\n\"\n    \"\\n\"\n    \"    public const OPTION_ENABLE_PREEMPTIVE_SCHEDULER = 'enable_preemptive_scheduler';\\n\"\n    \"\\n\"\n    \"    public const OPTION_C_STACK_SIZE = 'c_stack_size';\\n\"\n    \"\\n\"\n    \"    public const OPTION_STACK_SIZE = 'stack_size';\\n\"\n    \"\\n\"\n    \"    public const OPTION_NAME_RESOLVER = 'name_resolver';\\n\"\n    \"\\n\"\n    \"    public const OPTION_DNS_CACHE_EXPIRE = 'dns_cache_expire';\\n\"\n    \"\\n\"\n    \"    public const OPTION_DNS_CACHE_CAPACITY = 'dns_cache_capacity';\\n\"\n    \"\\n\"\n    \"    public const OPTION_CONNECT_TIMEOUT = 'connect_timeout';\\n\"\n    \"\\n\"\n    \"    public const OPTION_TIMEOUT = 'timeout';\\n\"\n    \"\\n\"\n    \"    public const OPTION_MAX_RETRIES = 'max_retries';\\n\"\n    \"\\n\"\n    \"    public const OPTION_DEFER = 'defer';\\n\"\n    \"\\n\"\n    \"    public const OPTION_LOWERCASE_HEADER = 'lowercase_header';\\n\"\n    \"\\n\"\n    \"    public const OPTION_KEEP_ALIVE = 'keep_alive';\\n\"\n    \"\\n\"\n    \"    public const OPTION_WEBSOCKET_MASK = 'websocket_mask';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP_COMPRESSION = 'http_compression';\\n\"\n    \"\\n\"\n    \"    public const OPTION_BODY_DECOMPRESSION = 'body_decompression';\\n\"\n    \"\\n\"\n    \"    public const OPTION_WEBSOCKET_COMPRESSION = 'websocket_compression';\\n\"\n    \"\\n\"\n    \"    public const OPTION_WRITE_FUNC = 'write_func';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP_PARSE_COOKIE = 'http_parse_cookie';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP_PARSE_POST = 'http_parse_post';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP_PARSE_FILES = 'http_parse_files';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP_COMPRESSION_LEVEL = 'http_compression_level';\\n\"\n    \"\\n\"\n    \"    public const OPTION_COMPRESSION_LEVEL = 'compression_level';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP_GZIP_LEVEL = 'http_gzip_level';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP_COMPRESSION_MIN_LENGTH = 'http_compression_min_length';\\n\"\n    \"\\n\"\n    \"    public const OPTION_COMPRESSION_MIN_LENGTH = 'compression_min_length';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP_COMPRESSION_TYPES = 'http_compression_types';\\n\"\n    \"\\n\"\n    \"    public const OPTION_COMPRESSION_TYPES = 'compression_types';\\n\"\n    \"\\n\"\n    \"    public const OPTION_UPLOAD_TMP_DIR = 'upload_tmp_dir';\\n\"\n    \"\\n\"\n    \"    public const OPTION_ENABLE_MESSAGE_BUS = 'enable_message_bus';\\n\"\n    \"\\n\"\n    \"    public const OPTION_MAX_PACKAGE_SIZE = 'max_package_size';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL = 'ssl';\\n\"\n    \"\\n\"\n    \"    public const OPTION_CHROOT = 'chroot';\\n\"\n    \"\\n\"\n    \"    public const OPTION_USER = 'user';\\n\"\n    \"\\n\"\n    \"    public const OPTION_GROUP = 'group';\\n\"\n    \"\\n\"\n    \"    public const OPTION_DAEMONIZE = 'daemonize';\\n\"\n    \"\\n\"\n    \"    public const OPTION_PID_FILE = 'pid_file';\\n\"\n    \"\\n\"\n    \"    public const OPTION_REACTOR_NUM = 'reactor_num';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SINGLE_THREAD = 'single_thread';\\n\"\n    \"\\n\"\n    \"    public const OPTION_WORKER_NUM = 'worker_num';\\n\"\n    \"\\n\"\n    \"    public const OPTION_MAX_WAIT_TIME = 'max_wait_time';\\n\"\n    \"\\n\"\n    \"    public const OPTION_MAX_QUEUED_BYTES = 'max_queued_bytes';\\n\"\n    \"\\n\"\n    \"    public const OPTION_MAX_CONCURRENCY = 'max_concurrency';\\n\"\n    \"\\n\"\n    \"    public const OPTION_WORKER_MAX_CONCURRENCY = 'worker_max_concurrency';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SEND_TIMEOUT = 'send_timeout';\\n\"\n    \"\\n\"\n    \"    public const OPTION_DISPATCH_MODE = 'dispatch_mode';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SEND_YIELD = 'send_yield';\\n\"\n    \"\\n\"\n    \"    public const OPTION_DISPATCH_FUNC = 'dispatch_func';\\n\"\n    \"\\n\"\n    \"    public const OPTION_DISCARD_TIMEOUT_REQUEST = 'discard_timeout_request';\\n\"\n    \"\\n\"\n    \"    public const OPTION_ENABLE_UNSAFE_EVENT = 'enable_unsafe_event';\\n\"\n    \"\\n\"\n    \"    public const OPTION_ENABLE_DELAY_RECEIVE = 'enable_delay_receive';\\n\"\n    \"\\n\"\n    \"    public const OPTION_ENABLE_REUSE_PORT = 'enable_reuse_port';\\n\"\n    \"\\n\"\n    \"    public const OPTION_TASK_USE_OBJECT = 'task_use_object';\\n\"\n    \"\\n\"\n    \"    public const OPTION_TASK_OBJECT = 'task_object';\\n\"\n    \"\\n\"\n    \"    public const OPTION_EVENT_OBJECT = 'event_object';\\n\"\n    \"\\n\"\n    \"    public const OPTION_TASK_ENABLE_COROUTINE = 'task_enable_coroutine';\\n\"\n    \"\\n\"\n    \"    public const OPTION_TASK_WORKER_NUM = 'task_worker_num';\\n\"\n    \"\\n\"\n    \"    public const OPTION_TASK_IPC_MODE = 'task_ipc_mode';\\n\"\n    \"\\n\"\n    \"    public const OPTION_TASK_TMPDIR = 'task_tmpdir';\\n\"\n    \"\\n\"\n    \"    public const OPTION_TASK_MAX_REQUEST = 'task_max_request';\\n\"\n    \"\\n\"\n    \"    public const OPTION_TASK_MAX_REQUEST_GRACE = 'task_max_request_grace';\\n\"\n    \"\\n\"\n    \"    public const OPTION_MAX_CONNECTION = 'max_connection';\\n\"\n    \"\\n\"\n    \"    public const OPTION_MAX_CONN = 'max_conn';\\n\"\n    \"\\n\"\n    \"    public const OPTION_START_SESSION_ID = 'start_session_id';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HEARTBEAT_CHECK_INTERVAL = 'heartbeat_check_interval';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HEARTBEAT_IDLE_TIME = 'heartbeat_idle_time';\\n\"\n    \"\\n\"\n    \"    public const OPTION_MAX_REQUEST = 'max_request';\\n\"\n    \"\\n\"\n    \"    public const OPTION_MAX_REQUEST_GRACE = 'max_request_grace';\\n\"\n    \"\\n\"\n    \"    public const OPTION_RELOAD_ASYNC = 'reload_async';\\n\"\n    \"\\n\"\n    \"    public const OPTION_OPEN_CPU_AFFINITY = 'open_cpu_affinity';\\n\"\n    \"\\n\"\n    \"    public const OPTION_CPU_AFFINITY_IGNORE = 'cpu_affinity_ignore';\\n\"\n    \"\\n\"\n    \"    public const OPTION_UPLOAD_MAX_FILESIZE = 'upload_max_filesize';\\n\"\n    \"\\n\"\n    \"    public const OPTION_ENABLE_STATIC_HANDLER = 'enable_static_handler';\\n\"\n    \"\\n\"\n    \"    public const OPTION_DOCUMENT_ROOT = 'document_root';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP_AUTOINDEX = 'http_autoindex';\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP_INDEX_FILES = 'http_index_files';\\n\"\n    \"\\n\"\n    \"    public const OPTION_STATIC_HANDLER_LOCATIONS = 'static_handler_locations';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @since 6.2.0\\n\"\n    \"     */\\n\"\n    \"    public const OPTION_URL_REWRITE_RULES = 'url_rewrite_rules';\\n\"\n    \"\\n\"\n    \"    public const OPTION_INPUT_BUFFER_SIZE = 'input_buffer_size';\\n\"\n    \"\\n\"\n    \"    public const OPTION_BUFFER_INPUT_SIZE = 'buffer_input_size';\\n\"\n    \"\\n\"\n    \"    public const OPTION_OUTPUT_BUFFER_SIZE = 'output_buffer_size';\\n\"\n    \"\\n\"\n    \"    public const OPTION_BUFFER_OUTPUT_SIZE = 'buffer_output_size';\\n\"\n    \"\\n\"\n    \"    public const OPTION_MESSAGE_QUEUE_KEY = 'message_queue_key';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @since 6.0.0-beta\\n\"\n    \"     */\\n\"\n    \"    public const OPTION_BOOTSTRAP = 'bootstrap';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @since 6.0.0-beta\\n\"\n    \"     */\\n\"\n    \"    public const OPTION_INIT_ARGUMENTS = 'init_arguments';\\n\"\n    \"\\n\"\n    \"    public const OPTION_BACKLOG = 'backlog';\\n\"\n    \"\\n\"\n    \"    public const OPTION_KERNEL_SOCKET_RECV_BUFFER_SIZE = 'kernel_socket_recv_buffer_size';\\n\"\n    \"\\n\"\n    \"    public const OPTION_KERNEL_SOCKET_SEND_BUFFER_SIZE = 'kernel_socket_send_buffer_size';\\n\"\n    \"\\n\"\n    \"    public const OPTION_TCP_DEFER_ACCEPT = 'tcp_defer_accept';\\n\"\n    \"\\n\"\n    \"    public const OPTION_OPEN_TCP_KEEPALIVE = 'open_tcp_keepalive';\\n\"\n    \"\\n\"\n    \"    public const OPTION_OPEN_HTTP_PROTOCOL = 'open_http_protocol';\\n\"\n    \"\\n\"\n    \"    public const OPTION_OPEN_WEBSOCKET_PROTOCOL = 'open_websocket_protocol';\\n\"\n    \"\\n\"\n    \"    public const OPTION_WEBSOCKET_SUBPROTOCOL = 'websocket_subprotocol';\\n\"\n    \"\\n\"\n    \"    public const OPTION_OPEN_WEBSOCKET_CLOSE_FRAME = 'open_websocket_close_frame';\\n\"\n    \"\\n\"\n    \"    public const OPTION_OPEN_WEBSOCKET_PING_FRAME = 'open_websocket_ping_frame';\\n\"\n    \"\\n\"\n    \"    public const OPTION_OPEN_WEBSOCKET_PONG_FRAME = 'open_websocket_pong_frame';\\n\"\n    \"\\n\"\n    \"    public const OPTION_OPEN_HTTP2_PROTOCOL = 'open_http2_protocol';\\n\"\n    \"\\n\"\n    \"    public const OPTION_OPEN_REDIS_PROTOCOL = 'open_redis_protocol';\\n\"\n    \"\\n\"\n    \"    public const OPTION_MAX_IDLE_TIME = 'max_idle_time';\\n\"\n    \"\\n\"\n    \"    public const OPTION_TCP_KEEPIDLE = 'tcp_keepidle';\\n\"\n    \"\\n\"\n    \"    public const OPTION_TCP_KEEPINTERVAL = 'tcp_keepinterval';\\n\"\n    \"\\n\"\n    \"    public const OPTION_TCP_KEEPCOUNT = 'tcp_keepcount';\\n\"\n    \"\\n\"\n    \"    public const OPTION_TCP_USER_TIMEOUT = 'tcp_user_timeout';\\n\"\n    \"\\n\"\n    \"    public const OPTION_TCP_FASTOPEN = 'tcp_fastopen';\\n\"\n    \"\\n\"\n    \"    public const OPTION_PACKAGE_BODY_START = 'package_body_start';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_CLIENT_CERT_FILE = 'ssl_client_cert_file';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_PREFER_SERVER_CIPHERS = 'ssl_prefer_server_ciphers';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_ECDH_CURVE = 'ssl_ecdh_curve';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_DHPARAM = 'ssl_dhparam';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_SNI_CERTS = 'ssl_sni_certs';\\n\"\n    \"\\n\"\n    \"    public const OPTION_OPEN_SSL = 'open_ssl';\\n\"\n    \"\\n\"\n    \"    public const OPTION_OPEN_FASTCGI_PROTOCOL = 'open_fastcgi_protocol';\\n\"\n    \"\\n\"\n    \"    public const OPTION_READ_TIMEOUT = 'read_timeout';\\n\"\n    \"\\n\"\n    \"    public const OPTION_WRITE_TIMEOUT = 'write_timeout';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_DISABLE_COMPRESSION = 'ssl_disable_compression';\\n\"\n    \"\\n\"\n    \"    public const OPTION_SSL_GREASE = 'ssl_grease';\\n\"\n    \"\\n\"\n    \"    public const OPTION_EXIT_CONDITION = 'exit_condition';\\n\"\n    \"\\n\"\n    \"    public const OPTION_DEADLOCK_CHECK_DISABLE_TRACE = 'deadlock_check_disable_trace';\\n\"\n    \"\\n\"\n    \"    public const OPTION_DEADLOCK_CHECK_LIMIT = 'deadlock_check_limit';\\n\"\n    \"\\n\"\n    \"    public const OPTION_DEADLOCK_CHECK_DEPTH = 'deadlock_check_depth';\\n\"\n    \"\\n\"\n    \"    public const OPTION_STATS_FILE = 'stats_file';\\n\"\n    \"\\n\"\n    \"    public const OPTION_STATS_TIMER_INTERVAL = 'stats_timer_interval';\\n\"\n    \"\\n\"\n    \"    public const OPTION_ADMIN_SERVER = 'admin_server';\\n\"\n    \"\\n\"\n    \"    /* }}} OPTION */\\n\"\n    \"\\n\"\n    \"    public const OPTION_HTTP_CLIENT_DRIVER = 'http_client_driver';\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_string_object =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole;\\n\"\n    \"\\n\"\n    \"class StringObject implements \\\\Stringable\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * StringObject constructor.\\n\"\n    \"     */\\n\"\n    \"    public function __construct(protected string $string = '')\\n\"\n    \"    {\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __toString(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->string;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function from(string $string = ''): static\\n\"\n    \"    {\\n\"\n    \"        return new static($string); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function length(): int\\n\"\n    \"    {\\n\"\n    \"        return strlen($this->string);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function indexOf(string $needle, int $offset = 0): false|int\\n\"\n    \"    {\\n\"\n    \"        return strpos($this->string, $needle, $offset);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function lastIndexOf(string $needle, int $offset = 0): false|int\\n\"\n    \"    {\\n\"\n    \"        return strrpos($this->string, $needle, $offset);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function pos(string $needle, int $offset = 0): false|int\\n\"\n    \"    {\\n\"\n    \"        return strpos($this->string, $needle, $offset);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function rpos(string $needle, int $offset = 0): false|int\\n\"\n    \"    {\\n\"\n    \"        return strrpos($this->string, $needle, $offset);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function reverse(): static\\n\"\n    \"    {\\n\"\n    \"        return new static(strrev($this->string)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return false|int\\n\"\n    \"     */\\n\"\n    \"    public function ipos(string $needle)\\n\"\n    \"    {\\n\"\n    \"        return stripos($this->string, $needle);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function lower(): static\\n\"\n    \"    {\\n\"\n    \"        return new static(strtolower($this->string)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function upper(): static\\n\"\n    \"    {\\n\"\n    \"        return new static(strtoupper($this->string)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function trim(string $characters = ''): static\\n\"\n    \"    {\\n\"\n    \"        if ($characters) {\\n\"\n    \"            return new static(trim($this->string, $characters)); // @phpstan-ignore new.static\\n\"\n    \"        }\\n\"\n    \"        return new static(trim($this->string)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return static\\n\"\n    \"     */\\n\"\n    \"    public function ltrim(): self\\n\"\n    \"    {\\n\"\n    \"        return new static(ltrim($this->string)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return static\\n\"\n    \"     */\\n\"\n    \"    public function rtrim(): self\\n\"\n    \"    {\\n\"\n    \"        return new static(rtrim($this->string)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return static\\n\"\n    \"     */\\n\"\n    \"    public function substr(int $offset, ?int $length = null)\\n\"\n    \"    {\\n\"\n    \"        return new static(substr($this->string, $offset, $length)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function repeat(int $times): static\\n\"\n    \"    {\\n\"\n    \"        return new static(str_repeat($this->string, $times)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function append(mixed $str): static\\n\"\n    \"    {\\n\"\n    \"        return new static($this->string .= $str); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @param int|null $count\\n\"\n    \"     */\\n\"\n    \"    public function replace(string $search, string $replace, &$count = null): static\\n\"\n    \"    {\\n\"\n    \"        return new static(str_replace($search, $replace, $this->string, $count)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function startsWith(string $needle): bool\\n\"\n    \"    {\\n\"\n    \"        return str_starts_with($this->string, $needle);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function endsWith(string $needle): bool\\n\"\n    \"    {\\n\"\n    \"        return strrpos($this->string, $needle) === (strlen($this->string) - strlen($needle));\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function equals($str, bool $strict = false): bool\\n\"\n    \"    {\\n\"\n    \"        if ($str instanceof StringObject) {\\n\"\n    \"            $str = strval($str);\\n\"\n    \"        }\\n\"\n    \"        if ($strict) {\\n\"\n    \"            return $this->string === $str;\\n\"\n    \"        }\\n\"\n    \"        return $this->string == $str;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function contains(string $subString): bool\\n\"\n    \"    {\\n\"\n    \"        return str_contains($this->string, $subString);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function split(string $delimiter, int $limit = PHP_INT_MAX): ArrayObject\\n\"\n    \"    {\\n\"\n    \"        return static::detectArrayType(explode($delimiter, $this->string, $limit));\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function char(int $index): string\\n\"\n    \"    {\\n\"\n    \"        if ($index > strlen($this->string)) {\\n\"\n    \"            return '';\\n\"\n    \"        }\\n\"\n    \"        return $this->string[$index];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Get a new string object by splitting the string of current object into smaller chunks.\\n\"\n    \"     *\\n\"\n    \"     * @param int $length The chunk length.\\n\"\n    \"     * @param string $separator The line ending sequence.\\n\"\n    \"     * @see https://www.php.net/chunk_split\\n\"\n    \"     */\\n\"\n    \"    public function chunkSplit(int $length = 76, string $separator = \\\"\\\\r\\\\n\\\"): static\\n\"\n    \"    {\\n\"\n    \"        return new static(chunk_split($this->string, $length, $separator)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Convert a string to an array object of class \\\\Swoole\\\\ArrayObject.\\n\"\n    \"     *\\n\"\n    \"     * @param int $length Maximum length of the chunk.\\n\"\n    \"     * @see https://www.php.net/str_split\\n\"\n    \"     */\\n\"\n    \"    public function chunk(int $length = 1): ArrayObject\\n\"\n    \"    {\\n\"\n    \"        return static::detectArrayType(str_split($this->string, $length));\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function toString(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->string;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    protected static function detectArrayType(array $value): ArrayObject\\n\"\n    \"    {\\n\"\n    \"        return new ArrayObject($value);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_multibyte_string_object =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole;\\n\"\n    \"\\n\"\n    \"class MultibyteStringObject extends StringObject\\n\"\n    \"{\\n\"\n    \"    public function length(): int\\n\"\n    \"    {\\n\"\n    \"        return mb_strlen($this->string);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function indexOf(string $needle, int $offset = 0, ?string $encoding = null): false|int\\n\"\n    \"    {\\n\"\n    \"        return mb_strpos($this->string, $needle, $offset, $encoding);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function lastIndexOf(string $needle, int $offset = 0, ?string $encoding = null): false|int\\n\"\n    \"    {\\n\"\n    \"        return mb_strrpos($this->string, $needle, $offset, $encoding);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function pos(string $needle, int $offset = 0, ?string $encoding = null): false|int\\n\"\n    \"    {\\n\"\n    \"        return mb_strpos($this->string, $needle, $offset, $encoding);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function rpos(string $needle, int $offset = 0, ?string $encoding = null): false|int\\n\"\n    \"    {\\n\"\n    \"        return mb_strrpos($this->string, $needle, $offset, $encoding);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function ipos(string $needle, int $offset = 0, ?string $encoding = null): int|false\\n\"\n    \"    {\\n\"\n    \"        return mb_stripos($this->string, $needle, $offset, $encoding);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @see https://www.php.net/mb_substr\\n\"\n    \"     */\\n\"\n    \"    public function substr(int $start, ?int $length = null, ?string $encoding = null): static\\n\"\n    \"    {\\n\"\n    \"        return new static(mb_substr($this->string, $start, $length, $encoding)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * {@inheritDoc}\\n\"\n    \"     * @see https://www.php.net/mb_str_split\\n\"\n    \"     */\\n\"\n    \"    public function chunk(int $length = 1): ArrayObject\\n\"\n    \"    {\\n\"\n    \"        return static::detectArrayType(mb_str_split($this->string, $length));\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_exception_array_key_not_exists =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Exception;\\n\"\n    \"\\n\"\n    \"class ArrayKeyNotExists extends \\\\RuntimeException\\n\"\n    \"{\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_array_object =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Exception\\\\ArrayKeyNotExists;\\n\"\n    \"\\n\"\n    \"class ArrayObject implements \\\\ArrayAccess, \\\\Serializable, \\\\Countable, \\\\Iterator\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * @var array\\n\"\n    \"     */\\n\"\n    \"    protected $array;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * ArrayObject constructor.\\n\"\n    \"     */\\n\"\n    \"    public function __construct(array $array = [])\\n\"\n    \"    {\\n\"\n    \"        $this->array = $array;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __toArray(): array\\n\"\n    \"    {\\n\"\n    \"        return $this->array;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __serialize(): array\\n\"\n    \"    {\\n\"\n    \"        return $this->array;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __unserialize(array $data): void\\n\"\n    \"    {\\n\"\n    \"        $this->array = $data;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function from(array $array = []): static\\n\"\n    \"    {\\n\"\n    \"        return new static($array); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function toArray(): array\\n\"\n    \"    {\\n\"\n    \"        return $this->array;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function isEmpty(): bool\\n\"\n    \"    {\\n\"\n    \"        return empty($this->array);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function count(): int\\n\"\n    \"    {\\n\"\n    \"        return count($this->array);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return mixed\\n\"\n    \"     */\\n\"\n    \"    #[\\\\ReturnTypeWillChange]\\n\"\n    \"    public function current()\\n\"\n    \"    {\\n\"\n    \"        return current($this->array);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return mixed\\n\"\n    \"     */\\n\"\n    \"    #[\\\\ReturnTypeWillChange]\\n\"\n    \"    public function key()\\n\"\n    \"    {\\n\"\n    \"        return key($this->array);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function valid(): bool\\n\"\n    \"    {\\n\"\n    \"        return array_key_exists($this->key(), $this->array);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return mixed\\n\"\n    \"     */\\n\"\n    \"    #[\\\\ReturnTypeWillChange]\\n\"\n    \"    public function rewind()\\n\"\n    \"    {\\n\"\n    \"        return reset($this->array);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return mixed\\n\"\n    \"     */\\n\"\n    \"    #[\\\\ReturnTypeWillChange]\\n\"\n    \"    public function next()\\n\"\n    \"    {\\n\"\n    \"        return next($this->array);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return ArrayObject|StringObject\\n\"\n    \"     */\\n\"\n    \"    public function get(mixed $key)\\n\"\n    \"    {\\n\"\n    \"        if (!$this->exists($key)) {\\n\"\n    \"            throw new ArrayKeyNotExists($key);\\n\"\n    \"        }\\n\"\n    \"        return static::detectType($this->array[$key]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return ArrayObject|StringObject\\n\"\n    \"     */\\n\"\n    \"    public function getOr(mixed $key, mixed $default = null)\\n\"\n    \"    {\\n\"\n    \"        if (!$this->exists($key)) {\\n\"\n    \"            return $default;\\n\"\n    \"        }\\n\"\n    \"        return static::detectType($this->array[$key]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return mixed\\n\"\n    \"     */\\n\"\n    \"    public function last()\\n\"\n    \"    {\\n\"\n    \"        $key = array_key_last($this->array);\\n\"\n    \"        if ($key === null) {\\n\"\n    \"            return null;\\n\"\n    \"        }\\n\"\n    \"        return $this->get($key);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return int|string|null\\n\"\n    \"     */\\n\"\n    \"    public function firstKey()\\n\"\n    \"    {\\n\"\n    \"        return array_key_first($this->array);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return int|string|null\\n\"\n    \"     */\\n\"\n    \"    public function lastKey()\\n\"\n    \"    {\\n\"\n    \"        return array_key_last($this->array);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return mixed\\n\"\n    \"     */\\n\"\n    \"    public function first()\\n\"\n    \"    {\\n\"\n    \"        $key = array_key_first($this->array);\\n\"\n    \"        if ($key === null) {\\n\"\n    \"            return null;\\n\"\n    \"        }\\n\"\n    \"        return $this->get($key);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return $this\\n\"\n    \"     */\\n\"\n    \"    public function set(mixed $key, mixed $value): self\\n\"\n    \"    {\\n\"\n    \"        $this->array[$key] = $value;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return $this\\n\"\n    \"     */\\n\"\n    \"    public function delete(mixed $key): self\\n\"\n    \"    {\\n\"\n    \"        unset($this->array[$key]);\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return $this\\n\"\n    \"     */\\n\"\n    \"    public function remove(mixed $value, bool $strict = true, bool $loop = false): self\\n\"\n    \"    {\\n\"\n    \"        do {\\n\"\n    \"            $key = $this->search($value, $strict);\\n\"\n    \"            if ($key === false) {\\n\"\n    \"                break;\\n\"\n    \"            }\\n\"\n    \"            unset($this->array[$key]);\\n\"\n    \"        } while ($loop);\\n\"\n    \"\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return $this\\n\"\n    \"     */\\n\"\n    \"    public function clear(): self\\n\"\n    \"    {\\n\"\n    \"        $this->array = [];\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return mixed|null\\n\"\n    \"     */\\n\"\n    \"    #[\\\\ReturnTypeWillChange]\\n\"\n    \"    public function offsetGet(mixed $key)\\n\"\n    \"    {\\n\"\n    \"        if (!array_key_exists($key, $this->array)) {\\n\"\n    \"            return null;\\n\"\n    \"        }\\n\"\n    \"        return $this->array[$key];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function offsetSet(mixed $key, mixed $value): void\\n\"\n    \"    {\\n\"\n    \"        $this->array[$key] = $value;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function offsetUnset(mixed $key): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->array[$key]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return bool\\n\"\n    \"     */\\n\"\n    \"    #[\\\\ReturnTypeWillChange]\\n\"\n    \"    public function offsetExists(mixed $key)\\n\"\n    \"    {\\n\"\n    \"        return isset($this->array[$key]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function exists(mixed $key): bool\\n\"\n    \"    {\\n\"\n    \"        return array_key_exists($key, $this->array);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function contains(mixed $value, bool $strict = true): bool\\n\"\n    \"    {\\n\"\n    \"        return in_array($value, $this->array, $strict);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return mixed\\n\"\n    \"     */\\n\"\n    \"    public function indexOf(mixed $value, bool $strict = true)\\n\"\n    \"    {\\n\"\n    \"        return $this->search($value, $strict);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return mixed\\n\"\n    \"     */\\n\"\n    \"    public function lastIndexOf(mixed $value, bool $strict = true)\\n\"\n    \"    {\\n\"\n    \"        $array = $this->array;\\n\"\n    \"        for (end($array); ($currentKey = key($array)) !== null; prev($array)) {\\n\"\n    \"            $currentValue = current($array);\\n\"\n    \"            if ($currentValue == $value) {\\n\"\n    \"                if ($strict && $currentValue !== $value) {\\n\"\n    \"                    continue;\\n\"\n    \"                }\\n\"\n    \"                break;\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"        return $currentKey;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return mixed\\n\"\n    \"     */\\n\"\n    \"    public function search(mixed $needle, bool $strict = true)\\n\"\n    \"    {\\n\"\n    \"        return array_search($needle, $this->array, $strict);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function join(string $glue = ''): StringObject\\n\"\n    \"    {\\n\"\n    \"        return self::detectStringType(implode($glue, $this->array));\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function serialize(): string\\n\"\n    \"    {\\n\"\n    \"        return serialize($this->array);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function unserialize(string|\\\\Stringable|StringObject $string): self\\n\"\n    \"    {\\n\"\n    \"        $this->array = (array) unserialize((string) $string);\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return float|int\\n\"\n    \"     */\\n\"\n    \"    public function sum()\\n\"\n    \"    {\\n\"\n    \"        return array_sum($this->array);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return float|int\\n\"\n    \"     */\\n\"\n    \"    public function product()\\n\"\n    \"    {\\n\"\n    \"        return array_product($this->array);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return int\\n\"\n    \"     */\\n\"\n    \"    public function push(mixed $value)\\n\"\n    \"    {\\n\"\n    \"        return $this->pushBack($value);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return int\\n\"\n    \"     */\\n\"\n    \"    public function pushFront(mixed $value)\\n\"\n    \"    {\\n\"\n    \"        return array_unshift($this->array, $value);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function append(...$values): ArrayObject\\n\"\n    \"    {\\n\"\n    \"        array_push($this->array, ...$values);\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return int\\n\"\n    \"     */\\n\"\n    \"    public function pushBack(mixed $value)\\n\"\n    \"    {\\n\"\n    \"        return array_push($this->array, $value);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return $this\\n\"\n    \"     */\\n\"\n    \"    public function insert(int $offset, mixed $value): self\\n\"\n    \"    {\\n\"\n    \"        if (is_array($value) || is_object($value) || is_null($value)) {\\n\"\n    \"            $value = [$value];\\n\"\n    \"        }\\n\"\n    \"        array_splice($this->array, $offset, 0, $value);\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return mixed\\n\"\n    \"     */\\n\"\n    \"    public function pop()\\n\"\n    \"    {\\n\"\n    \"        return $this->popBack();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return mixed\\n\"\n    \"     */\\n\"\n    \"    public function popFront()\\n\"\n    \"    {\\n\"\n    \"        return array_shift($this->array);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return mixed\\n\"\n    \"     */\\n\"\n    \"    public function popBack()\\n\"\n    \"    {\\n\"\n    \"        return array_pop($this->array);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function slice(int $offset, ?int $length = null, bool $preserve_keys = false): static\\n\"\n    \"    {\\n\"\n    \"        return new static(array_slice($this->array, $offset, $length, $preserve_keys)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return ArrayObject|mixed|StringObject\\n\"\n    \"     */\\n\"\n    \"    public function randomGet()\\n\"\n    \"    {\\n\"\n    \"        return static::detectType($this->array[array_rand($this->array, 1)]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function each(callable $fn): self\\n\"\n    \"    {\\n\"\n    \"        array_walk($this->array, $fn);\\n\"\n    \"\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @param array $args\\n\"\n    \"     */\\n\"\n    \"    public function map(callable $fn, ...$args): static\\n\"\n    \"    {\\n\"\n    \"        return new static(array_map($fn, $this->array, ...$args)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @param null $initial\\n\"\n    \"     * @return mixed\\n\"\n    \"     */\\n\"\n    \"    public function reduce(callable $fn, $initial = null)\\n\"\n    \"    {\\n\"\n    \"        return array_reduce($this->array, $fn, $initial);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @param array $args\\n\"\n    \"     */\\n\"\n    \"    public function keys(...$args): static\\n\"\n    \"    {\\n\"\n    \"        return new static(array_keys($this->array, ...$args)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function values(): static\\n\"\n    \"    {\\n\"\n    \"        return new static(array_values($this->array)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function column(mixed $column_key, mixed $index = null): static\\n\"\n    \"    {\\n\"\n    \"        return new static(array_column($this->array, $column_key, $index)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function unique(int $sort_flags = SORT_STRING): static\\n\"\n    \"    {\\n\"\n    \"        return new static(array_unique($this->array, $sort_flags)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function reverse(bool $preserve_keys = false): static\\n\"\n    \"    {\\n\"\n    \"        return new static(array_reverse($this->array, $preserve_keys)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function chunk(int $size, bool $preserve_keys = false): static\\n\"\n    \"    {\\n\"\n    \"        return new static(array_chunk($this->array, $size, $preserve_keys)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Swap keys and values in an array.\\n\"\n    \"     */\\n\"\n    \"    public function flip(): static\\n\"\n    \"    {\\n\"\n    \"        return new static(array_flip($this->array)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function filter(callable $fn, int $flag = 0): static\\n\"\n    \"    {\\n\"\n    \"        return new static(array_filter($this->array, $fn, $flag)); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * | Function name     | Sorts by | Maintains key association   | Order of sort               | Related functions |\\n\"\n    \"     * | :---------------- | :------- | :-------------------------- | :-------------------------- | :---------------- |\\n\"\n    \"     * | array_multisort() | value    | associative yes, numeric no | first array or sort options | array_walk()      |\\n\"\n    \"     * | asort()           | value    | yes                         | low to high                 | arsort()          |\\n\"\n    \"     * | arsort()          | value    | yes                         | high to low                 | asort()           |\\n\"\n    \"     * | krsort()          | key      | yes                         | high to low                 | ksort()           |\\n\"\n    \"     * | ksort()           | key      | yes                         | low to high                 | asort()           |\\n\"\n    \"     * | natcasesort()     | value    | yes                         | natural, case insensitive   | natsort()         |\\n\"\n    \"     * | natsort()         | value    | yes                         | natural                     | natcasesort()     |\\n\"\n    \"     * | rsort()           | value    | no                          | high to low                 | sort()            |\\n\"\n    \"     * | shuffle()         | value    | no                          | random                      | array_rand()      |\\n\"\n    \"     * | sort()            | value    | no                          | low to high                 | rsort()           |\\n\"\n    \"     * | uasort()          | value    | yes                         | user defined                | uksort()          |\\n\"\n    \"     * | uksort()          | key      | yes                         | user defined                | uasort()          |\\n\"\n    \"     * | usort()           | value    | no                          | user defined                | uasort()          |\\n\"\n    \"     */\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return $this\\n\"\n    \"     */\\n\"\n    \"    public function asort(int $sort_flags = SORT_REGULAR): self\\n\"\n    \"    {\\n\"\n    \"        asort($this->array, $sort_flags);\\n\"\n    \"\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function arsort(int $sort_flags = SORT_REGULAR): self\\n\"\n    \"    {\\n\"\n    \"        arsort($this->array, $sort_flags);\\n\"\n    \"\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function krsort(int $sort_flags = SORT_REGULAR): self\\n\"\n    \"    {\\n\"\n    \"        krsort($this->array, $sort_flags);\\n\"\n    \"\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function ksort(int $sort_flags = SORT_REGULAR): self\\n\"\n    \"    {\\n\"\n    \"        ksort($this->array, $sort_flags);\\n\"\n    \"\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return $this\\n\"\n    \"     */\\n\"\n    \"    public function natcasesort(): self\\n\"\n    \"    {\\n\"\n    \"        if (natcasesort($this->array) !== true) { // @phpstan-ignore notIdentical.alwaysFalse\\n\"\n    \"            throw new \\\\RuntimeException('natcasesort() failed');\\n\"\n    \"        }\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return $this\\n\"\n    \"     */\\n\"\n    \"    public function natsort(): self\\n\"\n    \"    {\\n\"\n    \"        if (natsort($this->array) !== true) { // @phpstan-ignore notIdentical.alwaysFalse\\n\"\n    \"            throw new \\\\RuntimeException('natsort() failed');\\n\"\n    \"        }\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return $this\\n\"\n    \"     */\\n\"\n    \"    public function rsort(int $sort_flags = SORT_REGULAR): self\\n\"\n    \"    {\\n\"\n    \"        if (rsort($this->array, $sort_flags) !== true) { // @phpstan-ignore notIdentical.alwaysFalse\\n\"\n    \"            throw new \\\\RuntimeException('rsort() failed');\\n\"\n    \"        }\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function shuffle(): self\\n\"\n    \"    {\\n\"\n    \"        shuffle($this->array);\\n\"\n    \"\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function sort(int $sort_flags = SORT_REGULAR): self\\n\"\n    \"    {\\n\"\n    \"        sort($this->array, $sort_flags);\\n\"\n    \"\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function uasort(callable $value_compare_func): self\\n\"\n    \"    {\\n\"\n    \"        uasort($this->array, $value_compare_func);\\n\"\n    \"\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function uksort(callable $value_compare_func): self\\n\"\n    \"    {\\n\"\n    \"        uksort($this->array, $value_compare_func);\\n\"\n    \"\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function usort(callable $value_compare_func): self\\n\"\n    \"    {\\n\"\n    \"        usort($this->array, $value_compare_func);\\n\"\n    \"\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return ArrayObject|mixed|StringObject\\n\"\n    \"     */\\n\"\n    \"    protected static function detectType(mixed $value)\\n\"\n    \"    {\\n\"\n    \"        if (is_string($value)) {\\n\"\n    \"            return static::detectStringType($value);\\n\"\n    \"        }\\n\"\n    \"        if (is_array($value)) {\\n\"\n    \"            return static::detectArrayType($value);\\n\"\n    \"        }\\n\"\n    \"        return $value;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    protected static function detectStringType(string $value): StringObject\\n\"\n    \"    {\\n\"\n    \"        return new StringObject($value);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    protected static function detectArrayType(array $value): static\\n\"\n    \"    {\\n\"\n    \"        return new static($value); // @phpstan-ignore new.static\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_object_proxy =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole;\\n\"\n    \"\\n\"\n    \"class ObjectProxy\\n\"\n    \"{\\n\"\n    \"    /** @var object */\\n\"\n    \"    protected $__object;\\n\"\n    \"\\n\"\n    \"    public function __construct(object $object)\\n\"\n    \"    {\\n\"\n    \"        $this->__object = $object;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __getObject()\\n\"\n    \"    {\\n\"\n    \"        return $this->__object;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __get(string $name)\\n\"\n    \"    {\\n\"\n    \"        return $this->__object->{$name};\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __set(string $name, $value): void\\n\"\n    \"    {\\n\"\n    \"        $this->__object->{$name} = $value;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __isset($name)\\n\"\n    \"    {\\n\"\n    \"        return isset($this->__object->{$name});\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __unset(string $name): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->__object->{$name});\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __call(string $name, array $arguments)\\n\"\n    \"    {\\n\"\n    \"        return $this->__object->{$name}(...$arguments);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __invoke(...$arguments)\\n\"\n    \"    {\\n\"\n    \"        /** @var mixed $object */\\n\"\n    \"        $object = $this->__object;\\n\"\n    \"        return $object(...$arguments);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_coroutine_wait_group =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Coroutine;\\n\"\n    \"\\n\"\n    \"class WaitGroup\\n\"\n    \"{\\n\"\n    \"    protected Channel $chan;\\n\"\n    \"\\n\"\n    \"    protected int $count = 0;\\n\"\n    \"\\n\"\n    \"    protected bool $waiting = false;\\n\"\n    \"\\n\"\n    \"    public function __construct(int $delta = 0)\\n\"\n    \"    {\\n\"\n    \"        $this->chan = new Channel(1);\\n\"\n    \"        if ($delta > 0) {\\n\"\n    \"            $this->add($delta);\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function add(int $delta = 1): void\\n\"\n    \"    {\\n\"\n    \"        if ($this->waiting) {\\n\"\n    \"            throw new \\\\BadMethodCallException('WaitGroup misuse: add called concurrently with wait');\\n\"\n    \"        }\\n\"\n    \"        $count = $this->count + $delta;\\n\"\n    \"        if ($count < 0) {\\n\"\n    \"            throw new \\\\InvalidArgumentException('WaitGroup misuse: negative counter');\\n\"\n    \"        }\\n\"\n    \"        $this->count = $count;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function done(): void\\n\"\n    \"    {\\n\"\n    \"        $count = $this->count - 1;\\n\"\n    \"        if ($count < 0) {\\n\"\n    \"            throw new \\\\BadMethodCallException('WaitGroup misuse: negative counter');\\n\"\n    \"        }\\n\"\n    \"        $this->count = $count;\\n\"\n    \"        if ($count === 0 && $this->waiting) {\\n\"\n    \"            $this->chan->push(true);\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function wait(float $timeout = -1): bool\\n\"\n    \"    {\\n\"\n    \"        if ($this->waiting) {\\n\"\n    \"            throw new \\\\BadMethodCallException('WaitGroup misuse: reused before previous wait has returned');\\n\"\n    \"        }\\n\"\n    \"        if ($this->count > 0) {\\n\"\n    \"            $this->waiting = true;\\n\"\n    \"            $done          = $this->chan->pop($timeout);\\n\"\n    \"            $this->waiting = false;\\n\"\n    \"            return $done;\\n\"\n    \"        }\\n\"\n    \"        return true;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function count(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->count;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_coroutine_server =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Coroutine;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Constant;\\n\"\n    \"use Swoole\\\\Coroutine;\\n\"\n    \"use Swoole\\\\Coroutine\\\\Server\\\\Connection;\\n\"\n    \"use Swoole\\\\Exception;\\n\"\n    \"\\n\"\n    \"class Server\\n\"\n    \"{\\n\"\n    \"    /** @var string */\\n\"\n    \"    public $host = '';\\n\"\n    \"\\n\"\n    \"    /** @var int */\\n\"\n    \"    public $port = 0;\\n\"\n    \"\\n\"\n    \"    /** @var int */\\n\"\n    \"    public $type = AF_INET;\\n\"\n    \"\\n\"\n    \"    /** @var int */\\n\"\n    \"    public $fd = -1;\\n\"\n    \"\\n\"\n    \"    /** @var int */\\n\"\n    \"    public $errCode = 0;\\n\"\n    \"\\n\"\n    \"    /** @var array */\\n\"\n    \"    public $setting = [];\\n\"\n    \"\\n\"\n    \"    /** @var bool */\\n\"\n    \"    protected $running = false;\\n\"\n    \"\\n\"\n    \"    /** @var callable|null */\\n\"\n    \"    protected $fn;\\n\"\n    \"\\n\"\n    \"    /** @var Socket */\\n\"\n    \"    protected $socket;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Server constructor.\\n\"\n    \"     * @throws Exception\\n\"\n    \"     */\\n\"\n    \"    public function __construct(string $host, int $port = 0, bool $ssl = false, bool $reuse_port = false)\\n\"\n    \"    {\\n\"\n    \"        $_host = swoole_string($host);\\n\"\n    \"        if ($_host->contains('::')) {\\n\"\n    \"            $this->type = AF_INET6;\\n\"\n    \"        } elseif ($_host->startsWith('unix:/')) {\\n\"\n    \"            $host       = $_host->substr(5)->__toString();\\n\"\n    \"            $this->type = AF_UNIX;\\n\"\n    \"        } else {\\n\"\n    \"            $this->type = AF_INET;\\n\"\n    \"        }\\n\"\n    \"        $this->host = $host;\\n\"\n    \"\\n\"\n    \"        $socket = new Socket($this->type, SOCK_STREAM, 0);\\n\"\n    \"        if ($reuse_port and defined('SO_REUSEPORT')) {\\n\"\n    \"            $socket->setOption(SOL_SOCKET, SO_REUSEPORT, true);\\n\"\n    \"        }\\n\"\n    \"        if (!$socket->bind($this->host, $port)) {\\n\"\n    \"            throw new Exception(\\\"bind({$this->host}:{$port}) failed\\\", $socket->errCode);\\n\"\n    \"        }\\n\"\n    \"        if (!$socket->listen()) {\\n\"\n    \"            throw new Exception('listen() failed', $socket->errCode);\\n\"\n    \"        }\\n\"\n    \"        $this->port                = $socket->getsockname()['port'] ?? 0;\\n\"\n    \"        $this->fd                  = $socket->fd;\\n\"\n    \"        $this->socket              = $socket;\\n\"\n    \"        $this->setting['open_ssl'] = $ssl;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function set(array $setting): void\\n\"\n    \"    {\\n\"\n    \"        $this->setting = array_merge($this->setting, $setting);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function handle(callable $fn): void\\n\"\n    \"    {\\n\"\n    \"        $this->fn = $fn;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function shutdown(): bool\\n\"\n    \"    {\\n\"\n    \"        $this->running = false;\\n\"\n    \"        return $this->socket->cancel();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function start(): bool\\n\"\n    \"    {\\n\"\n    \"        $this->running = true;\\n\"\n    \"        if ($this->fn === null) {\\n\"\n    \"            $this->errCode = SOCKET_EINVAL;\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"        $socket = $this->socket;\\n\"\n    \"        if (!$socket->setProtocol($this->setting)) {\\n\"\n    \"            $this->errCode = SOCKET_EINVAL;\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        while ($this->running) { // @phpstan-ignore while.alwaysTrue\\n\"\n    \"            $conn = null;\\n\"\n    \"            /** @var Socket $conn */\\n\"\n    \"            $conn = $socket->accept();\\n\"\n    \"            if ($conn) { // @phpstan-ignore if.alwaysTrue\\n\"\n    \"                $conn->setProtocol($this->setting);\\n\"\n    \"                if (!empty($this->setting[Constant::OPTION_OPEN_SSL])) {\\n\"\n    \"                    $fn = static function ($fn, $connection) {\\n\"\n    \"                        /* @var $connection Connection */\\n\"\n    \"                        if (!$connection->exportSocket()->sslHandshake()) {\\n\"\n    \"                            return;\\n\"\n    \"                        }\\n\"\n    \"                        $fn($connection);\\n\"\n    \"                    };\\n\"\n    \"                    $arguments = [$this->fn, new Connection($conn)];\\n\"\n    \"                } else {\\n\"\n    \"                    $fn        = $this->fn;\\n\"\n    \"                    $arguments = [new Connection($conn)];\\n\"\n    \"                }\\n\"\n    \"                if (Coroutine::create($fn, ...$arguments) < 0) {\\n\"\n    \"                    goto _wait;\\n\"\n    \"                }\\n\"\n    \"            } else {\\n\"\n    \"                if ($socket->errCode == SOCKET_EMFILE or $socket->errCode == SOCKET_ENFILE) {\\n\"\n    \"                    _wait:\\n\"\n    \"                    Coroutine::sleep(1);\\n\"\n    \"                    continue;\\n\"\n    \"                }\\n\"\n    \"                if ($socket->errCode == SOCKET_ETIMEDOUT) {\\n\"\n    \"                    continue;\\n\"\n    \"                }\\n\"\n    \"                if ($socket->errCode == SOCKET_ECANCELED) {\\n\"\n    \"                    break;\\n\"\n    \"                }\\n\"\n    \"                trigger_error(\\\"accept failed, Error: {$socket->errMsg}[{$socket->errCode}]\\\", E_USER_WARNING);\\n\"\n    \"                break;\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return true; // @phpstan-ignore deadCode.unreachable\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_coroutine_server_connection =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Coroutine\\\\Server;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Coroutine\\\\Socket;\\n\"\n    \"\\n\"\n    \"class Connection\\n\"\n    \"{\\n\"\n    \"    protected $socket;\\n\"\n    \"\\n\"\n    \"    public function __construct(Socket $conn)\\n\"\n    \"    {\\n\"\n    \"        $this->socket = $conn;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function recv(float $timeout = 0)\\n\"\n    \"    {\\n\"\n    \"        return $this->socket->recvPacket($timeout);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function send(string $data)\\n\"\n    \"    {\\n\"\n    \"        return $this->socket->sendAll($data);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function close(): bool\\n\"\n    \"    {\\n\"\n    \"        return $this->socket->close();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function exportSocket(): Socket\\n\"\n    \"    {\\n\"\n    \"        return $this->socket;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_coroutine_barrier =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Coroutine;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Coroutine;\\n\"\n    \"use Swoole\\\\Exception;\\n\"\n    \"use Swoole\\\\Timer;\\n\"\n    \"\\n\"\n    \"class Barrier\\n\"\n    \"{\\n\"\n    \"    private int $cid = -1;\\n\"\n    \"\\n\"\n    \"    private $timer = -1;\\n\"\n    \"\\n\"\n    \"    private static array $cancel_list = [];\\n\"\n    \"\\n\"\n    \"    public function __destruct()\\n\"\n    \"    {\\n\"\n    \"        if ($this->timer !== -1) {\\n\"\n    \"            Timer::clear($this->timer);\\n\"\n    \"            if (isset(self::$cancel_list[$this->cid])) {\\n\"\n    \"                unset(self::$cancel_list[$this->cid]);\\n\"\n    \"                return;\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"        if ($this->cid !== -1 && $this->cid !== Coroutine::getCid()) {\\n\"\n    \"            Coroutine::resume($this->cid);\\n\"\n    \"        } else {\\n\"\n    \"            self::$cancel_list[$this->cid] = true;\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function make(): self\\n\"\n    \"    {\\n\"\n    \"        return new self();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @param-out null $barrier\\n\"\n    \"     */\\n\"\n    \"    public static function wait(Barrier &$barrier, float $timeout = -1): void\\n\"\n    \"    {\\n\"\n    \"        if ($barrier->cid !== -1) {\\n\"\n    \"            throw new Exception('The barrier is waiting, cannot wait again.');\\n\"\n    \"        }\\n\"\n    \"        $cid          = Coroutine::getCid();\\n\"\n    \"        $barrier->cid = $cid;\\n\"\n    \"        if ($timeout > 0 && ($timeout_ms = (int) ($timeout * 1000)) > 0) {\\n\"\n    \"            $barrier->timer = Timer::after($timeout_ms, function () use ($cid) {\\n\"\n    \"                self::$cancel_list[$cid] = true;\\n\"\n    \"                Coroutine::resume($cid);\\n\"\n    \"            });\\n\"\n    \"        }\\n\"\n    \"        $barrier = null;\\n\"\n    \"        if (!isset(self::$cancel_list[$cid])) {\\n\"\n    \"            Coroutine::yield();\\n\"\n    \"        } else {\\n\"\n    \"            unset(self::$cancel_list[$cid]);\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_coroutine_http_client_proxy =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Coroutine\\\\Http;\\n\"\n    \"\\n\"\n    \"class ClientProxy\\n\"\n    \"{\\n\"\n    \"    private array $headers;\\n\"\n    \"\\n\"\n    \"    private array $cookies;\\n\"\n    \"\\n\"\n    \"    public function __construct(private string $body, private int $statusCode, ?array $headers, ?array $cookies)\\n\"\n    \"    {\\n\"\n    \"        $this->headers = $headers ?? [];\\n\"\n    \"        $this->cookies = $cookies ?? [];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getBody(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->body;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getStatusCode(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->statusCode;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getHeaders(): array\\n\"\n    \"    {\\n\"\n    \"        return $this->headers;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getCookies(): array\\n\"\n    \"    {\\n\"\n    \"        return $this->cookies;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_coroutine_http_functions =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Coroutine\\\\Http;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Coroutine\\\\Http\\\\Client\\\\Exception;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * @throws Exception\\n\"\n    \" */\\n\"\n    \"function request(\\n\"\n    \"    string $url,\\n\"\n    \"    string $method,\\n\"\n    \"    mixed $data = null,\\n\"\n    \"    ?array $options = null,\\n\"\n    \"    ?array $headers = null,\\n\"\n    \"    ?array $cookies = null,\\n\"\n    \"): ClientProxy {\\n\"\n    \"    $driver = swoole_library_get_option('http_client_driver');\\n\"\n    \"    return match ($driver) {\\n\"\n    \"        'curl'   => request_with_curl($url, $method, $data, $options, $headers, $cookies),\\n\"\n    \"        'stream' => request_with_stream($url, $method, $data, $options, $headers, $cookies),\\n\"\n    \"        default  => request_with_http_client($url, $method, $data, $options, $headers, $cookies),\\n\"\n    \"    };\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * @throws Exception\\n\"\n    \" */\\n\"\n    \"function request_with_http_client(\\n\"\n    \"    string $url,\\n\"\n    \"    string $method,\\n\"\n    \"    mixed $data = null,\\n\"\n    \"    ?array $options = null,\\n\"\n    \"    ?array $headers = null,\\n\"\n    \"    ?array $cookies = null,\\n\"\n    \"): ClientProxy {\\n\"\n    \"    $info = parse_url($url);\\n\"\n    \"    if (empty($info['scheme'])) {\\n\"\n    \"        throw new Exception('The URL given is illegal [no scheme]');\\n\"\n    \"    }\\n\"\n    \"    if ($info['scheme'] == 'http') {\\n\"\n    \"        $client = new Client($info['host'], swoole_array_default_value($info, 'port', 80), false);\\n\"\n    \"    } elseif ($info['scheme'] == 'https') {\\n\"\n    \"        $client = new Client($info['host'], swoole_array_default_value($info, 'port', 443), true);\\n\"\n    \"    } else {\\n\"\n    \"        throw new Exception('unknown scheme \\\"' . $info['scheme'] . '\\\"');\\n\"\n    \"    }\\n\"\n    \"    $client->setMethod($method);\\n\"\n    \"    if ($data) {\\n\"\n    \"        $client->setData($data);\\n\"\n    \"    }\\n\"\n    \"    $client->set($options ?: []);\\n\"\n    \"    $client->setHeaders($headers ?: []);\\n\"\n    \"    $client->setCookies($cookies ?: []);\\n\"\n    \"    $request_url = swoole_array_default_value($info, 'path', '/');\\n\"\n    \"    if (!empty($info['query'])) {\\n\"\n    \"        $request_url .= '?' . $info['query'];\\n\"\n    \"    }\\n\"\n    \"    if ($client->execute($request_url)) {\\n\"\n    \"        return new ClientProxy(\\n\"\n    \"            $client->getBody(),\\n\"\n    \"            $client->getStatusCode(),\\n\"\n    \"            $client->getHeaders() ?: [],\\n\"\n    \"            $client->getCookies() ?: []\\n\"\n    \"        );\\n\"\n    \"    }\\n\"\n    \"    throw new Exception($client->errMsg, $client->errCode);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * @throws Exception\\n\"\n    \" */\\n\"\n    \"function request_with_curl(\\n\"\n    \"    string $url,\\n\"\n    \"    string $method,\\n\"\n    \"    mixed $data = null,\\n\"\n    \"    ?array $options = null,\\n\"\n    \"    ?array $headers = null,\\n\"\n    \"    ?array $cookies = null,\\n\"\n    \"): ClientProxy {\\n\"\n    \"    $ch = curl_init($url);\\n\"\n    \"    if (empty($ch)) {\\n\"\n    \"        throw new Exception('failed to curl_init');\\n\"\n    \"    }\\n\"\n    \"    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\\n\"\n    \"    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method));\\n\"\n    \"    $responseHeaders = $responseCookies = [];\\n\"\n    \"    curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $header) use (&$responseHeaders, &$responseCookies) {\\n\"\n    \"        $len    = strlen($header);\\n\"\n    \"        $header = explode(':', $header, 2);\\n\"\n    \"        if (count($header) < 2) {\\n\"\n    \"            return $len;\\n\"\n    \"        }\\n\"\n    \"        $headerKey = strtolower(trim($header[0]));\\n\"\n    \"        if ($headerKey == 'set-cookie') {\\n\"\n    \"            [$k, $v]             = explode('=', $header[1]);\\n\"\n    \"            $responseCookies[$k] = $v;\\n\"\n    \"        } else {\\n\"\n    \"            $responseHeaders[$headerKey][] = trim($header[1]);\\n\"\n    \"        }\\n\"\n    \"        return $len;\\n\"\n    \"    });\\n\"\n    \"    if ($data) {\\n\"\n    \"        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);\\n\"\n    \"    }\\n\"\n    \"    if ($headers) {\\n\"\n    \"        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);\\n\"\n    \"    }\\n\"\n    \"    if ($cookies) {\\n\"\n    \"        $cookie_str = '';\\n\"\n    \"        foreach ($cookies as $k => $v) {\\n\"\n    \"            $cookie_str .= \\\"{$k}={$v}; \\\";\\n\"\n    \"        }\\n\"\n    \"        curl_setopt($ch, CURLOPT_COOKIE, $cookie_str);\\n\"\n    \"    }\\n\"\n    \"    if (isset($options['timeout'])) {\\n\"\n    \"        if (is_float($options['timeout'])) {\\n\"\n    \"            curl_setopt($ch, CURLOPT_TIMEOUT_MS, intval($options['timeout'] * 1000));\\n\"\n    \"            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, intval($options['timeout'] * 1000));\\n\"\n    \"        } else {\\n\"\n    \"            curl_setopt($ch, CURLOPT_TIMEOUT, intval($options['timeout']));\\n\"\n    \"            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, intval($options['timeout']));\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"    if (isset($options['connect_timeout'])) {\\n\"\n    \"        if (is_float($options['connect_timeout'])) {\\n\"\n    \"            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, intval($options['connect_timeout'] * 1000));\\n\"\n    \"        } else {\\n\"\n    \"            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, intval($options['connect_timeout']));\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"    $body = curl_exec($ch);\\n\"\n    \"    if ($body !== false) {\\n\"\n    \"        return new ClientProxy($body, curl_getinfo($ch, CURLINFO_RESPONSE_CODE), $responseHeaders, $responseCookies);\\n\"\n    \"    }\\n\"\n    \"    throw new Exception(curl_error($ch), curl_errno($ch));\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * @throws Exception\\n\"\n    \" */\\n\"\n    \"function request_with_stream(\\n\"\n    \"    string $url,\\n\"\n    \"    string $method,\\n\"\n    \"    mixed $data = null,\\n\"\n    \"    ?array $options = null,\\n\"\n    \"    ?array $headers = null,\\n\"\n    \"    ?array $cookies = null,\\n\"\n    \"): ClientProxy {\\n\"\n    \"    $stream_options = [\\n\"\n    \"        'http' => [\\n\"\n    \"            'method' => $method,\\n\"\n    \"        ],\\n\"\n    \"    ];\\n\"\n    \"    $headerStr = '';\\n\"\n    \"    if ($headers) {\\n\"\n    \"        foreach ($headers as $k => $v) {\\n\"\n    \"            $headerStr .= \\\"{$k}: {$v}\\\\r\\\\n\\\";\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"    if ($cookies) {\\n\"\n    \"        foreach ($cookies as $k => $v) {\\n\"\n    \"            $headerStr .= \\\"Cookie: {$k}={$v}\\\\r\\\\n\\\";\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"    if (isset($options['timeout'])) {\\n\"\n    \"        $stream_options['http']['timeout'] = intval($options['timeout']);\\n\"\n    \"    }\\n\"\n    \"    if ($data) {\\n\"\n    \"        if (is_array($data)) {\\n\"\n    \"            $headerStr .= \\\"Content-type: application/x-www-form-urlencoded\\\\r\\\\n\\\";\\n\"\n    \"            $stream_options['http']['content'] = http_build_query($data);\\n\"\n    \"        } else {\\n\"\n    \"            $stream_options['http']['content'] = strval($data);\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"    if ($headerStr) {\\n\"\n    \"        $stream_options['http']['header'] = $headerStr;\\n\"\n    \"    }\\n\"\n    \"    $body = file_get_contents($url, false, stream_context_create($stream_options));\\n\"\n    \"    if ($body) {\\n\"\n    \"        return new ClientProxy($body, 200, [], []);\\n\"\n    \"    }\\n\"\n    \"    $error = error_get_last();\\n\"\n    \"    throw new Exception($error['message']);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * @throws Exception\\n\"\n    \" */\\n\"\n    \"function post(string $url, mixed $data, ?array $options = null, ?array $headers = null, ?array $cookies = null): ClientProxy\\n\"\n    \"{\\n\"\n    \"    return request($url, 'POST', $data, $options, $headers, $cookies);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * @throws Exception\\n\"\n    \" */\\n\"\n    \"function get(string $url, ?array $options = null, ?array $headers = null, ?array $cookies = null): ClientProxy\\n\"\n    \"{\\n\"\n    \"    return request($url, 'GET', null, $options, $headers, $cookies);\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_connection_pool =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Coroutine\\\\Channel;\\n\"\n    \"\\n\"\n    \"class ConnectionPool\\n\"\n    \"{\\n\"\n    \"    public const DEFAULT_SIZE = 64;\\n\"\n    \"\\n\"\n    \"    protected ?Channel $pool;\\n\"\n    \"\\n\"\n    \"    /** @var callable */\\n\"\n    \"    protected $constructor;\\n\"\n    \"\\n\"\n    \"    protected int $size;\\n\"\n    \"\\n\"\n    \"    protected int $num = 0;\\n\"\n    \"\\n\"\n    \"    public function __construct(callable $constructor, int $size = self::DEFAULT_SIZE, protected ?string $proxy = null)\\n\"\n    \"    {\\n\"\n    \"        $this->pool        = new Channel($this->size = $size);\\n\"\n    \"        $this->constructor = $constructor;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function fill(): void\\n\"\n    \"    {\\n\"\n    \"        while ($this->size > $this->num) {\\n\"\n    \"            $this->make();\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Get a connection from the pool.\\n\"\n    \"     *\\n\"\n    \"     * @param float $timeout > 0 means waiting for the specified number of seconds. other means no waiting.\\n\"\n    \"     * @return mixed|false Returns a connection object from the pool, or false if the pool is full and the timeout is reached.\\n\"\n    \"     */\\n\"\n    \"    public function get(float $timeout = -1)\\n\"\n    \"    {\\n\"\n    \"        if ($this->pool === null) {\\n\"\n    \"            throw new \\\\RuntimeException('Pool has been closed');\\n\"\n    \"        }\\n\"\n    \"        if ($this->pool->isEmpty() && $this->num < $this->size) {\\n\"\n    \"            $this->make();\\n\"\n    \"        }\\n\"\n    \"        return $this->pool->pop($timeout);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function put($connection): void\\n\"\n    \"    {\\n\"\n    \"        if ($this->pool === null) {\\n\"\n    \"            return;\\n\"\n    \"        }\\n\"\n    \"        if ($connection !== null) {\\n\"\n    \"            $this->pool->push($connection);\\n\"\n    \"        } else {\\n\"\n    \"            /* connection broken */\\n\"\n    \"            $this->num -= 1;\\n\"\n    \"            $this->make();\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function close(): void\\n\"\n    \"    {\\n\"\n    \"        $this->pool->close();\\n\"\n    \"        $this->pool = null;\\n\"\n    \"        $this->num  = 0;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    protected function make(): void\\n\"\n    \"    {\\n\"\n    \"        $this->num++;\\n\"\n    \"        try {\\n\"\n    \"            if ($this->proxy) {\\n\"\n    \"                $connection = new $this->proxy($this->constructor);\\n\"\n    \"            } else {\\n\"\n    \"                $constructor = $this->constructor;\\n\"\n    \"                $connection  = $constructor();\\n\"\n    \"            }\\n\"\n    \"        } catch (\\\\Throwable $throwable) {\\n\"\n    \"            $this->num--;\\n\"\n    \"            throw $throwable;\\n\"\n    \"        }\\n\"\n    \"        $this->put($connection);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_database_object_proxy =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Database;\\n\"\n    \"\\n\"\n    \"class ObjectProxy extends \\\\Swoole\\\\ObjectProxy\\n\"\n    \"{\\n\"\n    \"    final public function __clone(): void\\n\"\n    \"    {\\n\"\n    \"        throw new \\\\Error('Trying to clone an uncloneable database proxy object');\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_database_mysqli_config =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Database;\\n\"\n    \"\\n\"\n    \"class MysqliConfig\\n\"\n    \"{\\n\"\n    \"    protected string $host = '127.0.0.1';\\n\"\n    \"\\n\"\n    \"    protected int $port = 3306;\\n\"\n    \"\\n\"\n    \"    protected ?string $unixSocket;\\n\"\n    \"\\n\"\n    \"    protected string $dbname = 'test';\\n\"\n    \"\\n\"\n    \"    protected string $charset = 'utf8mb4';\\n\"\n    \"\\n\"\n    \"    protected string $username = 'root';\\n\"\n    \"\\n\"\n    \"    protected string $password = 'root';\\n\"\n    \"\\n\"\n    \"    protected array $options = [];\\n\"\n    \"\\n\"\n    \"    public function getHost(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->host;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withHost(string $host): self\\n\"\n    \"    {\\n\"\n    \"        $this->host = $host;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getPort(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->port;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getUnixSocket(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->unixSocket ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withUnixSocket(?string $unixSocket): self\\n\"\n    \"    {\\n\"\n    \"        $this->unixSocket = $unixSocket;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withPort(int $port): self\\n\"\n    \"    {\\n\"\n    \"        $this->port = $port;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getDbname(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->dbname;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withDbname(string $dbname): self\\n\"\n    \"    {\\n\"\n    \"        $this->dbname = $dbname;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getCharset(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->charset;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withCharset(string $charset): self\\n\"\n    \"    {\\n\"\n    \"        $this->charset = $charset;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getUsername(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->username;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withUsername(string $username): self\\n\"\n    \"    {\\n\"\n    \"        $this->username = $username;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getPassword(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->password;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withPassword(string $password): self\\n\"\n    \"    {\\n\"\n    \"        $this->password = $password;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getOptions(): array\\n\"\n    \"    {\\n\"\n    \"        return $this->options;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withOptions(array $options): self\\n\"\n    \"    {\\n\"\n    \"        $this->options = $options;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_database_mysqli_exception =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Database;\\n\"\n    \"\\n\"\n    \"class MysqliException extends \\\\Exception\\n\"\n    \"{\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_database_mysqli_pool =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Database;\\n\"\n    \"\\n\"\n    \"use mysqli;\\n\"\n    \"use Swoole\\\\ConnectionPool;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * @method \\\\mysqli|MysqliProxy get()\\n\"\n    \" * @method void put(mysqli|MysqliProxy $connection)\\n\"\n    \" */\\n\"\n    \"class MysqliPool extends ConnectionPool\\n\"\n    \"{\\n\"\n    \"    public function __construct(protected MysqliConfig $config, int $size = self::DEFAULT_SIZE)\\n\"\n    \"    {\\n\"\n    \"        parent::__construct(function () {\\n\"\n    \"            $mysqli = new \\\\mysqli();\\n\"\n    \"            foreach ($this->config->getOptions() as $option => $value) {\\n\"\n    \"                $mysqli->set_opt($option, $value);\\n\"\n    \"            }\\n\"\n    \"            $mysqli->real_connect(\\n\"\n    \"                $this->config->getHost(),\\n\"\n    \"                $this->config->getUsername(),\\n\"\n    \"                $this->config->getPassword(),\\n\"\n    \"                $this->config->getDbname(),\\n\"\n    \"                $this->config->getPort(),\\n\"\n    \"                $this->config->getUnixSocket()\\n\"\n    \"            );\\n\"\n    \"            if ($mysqli->connect_errno) {\\n\"\n    \"                throw new MysqliException($mysqli->connect_error, $mysqli->connect_errno);\\n\"\n    \"            }\\n\"\n    \"            $mysqli->set_charset($this->config->getCharset());\\n\"\n    \"            return $mysqli;\\n\"\n    \"        }, $size, MysqliProxy::class);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_database_mysqli_proxy =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Database;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * @method \\\\mysqli __getObject()\\n\"\n    \" */\\n\"\n    \"class MysqliProxy extends ObjectProxy\\n\"\n    \"{\\n\"\n    \"    public const IO_METHOD_REGEX = '/^autocommit|begin_transaction|change_user|close|commit|kill|multi_query|ping|prepare|query|real_connect|real_query|reap_async_query|refresh|release_savepoint|rollback|savepoint|select_db|send_query|set_charset|ssl_set$/i';\\n\"\n    \"\\n\"\n    \"    public const IO_ERRORS = [\\n\"\n    \"        2002, // MYSQLND_CR_CONNECTION_ERROR\\n\"\n    \"        2006, // MYSQLND_CR_SERVER_GONE_ERROR\\n\"\n    \"        2013, // MYSQLND_CR_SERVER_LOST\\n\"\n    \"    ];\\n\"\n    \"\\n\"\n    \"    /** @var \\\\mysqli */\\n\"\n    \"    protected $__object;\\n\"\n    \"\\n\"\n    \"    protected string $charsetContext;\\n\"\n    \"\\n\"\n    \"    protected array $setOptContext = [];\\n\"\n    \"\\n\"\n    \"    protected array $changeUserContext;\\n\"\n    \"\\n\"\n    \"    /** @var callable */\\n\"\n    \"    protected $constructor;\\n\"\n    \"\\n\"\n    \"    protected int $round = 0;\\n\"\n    \"\\n\"\n    \"    public function __construct(callable $constructor)\\n\"\n    \"    {\\n\"\n    \"        parent::__construct($constructor());\\n\"\n    \"        $this->constructor = $constructor;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __call(string $name, array $arguments)\\n\"\n    \"    {\\n\"\n    \"        for ($n = 3; $n--;) {\\n\"\n    \"            $ret = @$this->__object->{$name}(...$arguments);\\n\"\n    \"            if ($ret === false) {\\n\"\n    \"                /* non-IO method */\\n\"\n    \"                if (!preg_match(static::IO_METHOD_REGEX, $name)) {\\n\"\n    \"                    break;\\n\"\n    \"                }\\n\"\n    \"                /* no more chances or non-IO failures */\\n\"\n    \"                if (!in_array($this->__object->errno, static::IO_ERRORS, true) || ($n === 0)) {\\n\"\n    \"                    throw new MysqliException($this->__object->error, $this->__object->errno);\\n\"\n    \"                }\\n\"\n    \"                $this->reconnect();\\n\"\n    \"                continue;\\n\"\n    \"            }\\n\"\n    \"            if (strcasecmp($name, 'prepare') === 0) {\\n\"\n    \"                $ret = new MysqliStatementProxy($ret, $arguments[0], $this);\\n\"\n    \"            } elseif (strcasecmp($name, 'stmt_init') === 0) {\\n\"\n    \"                $ret = new MysqliStatementProxy($ret, null, $this);\\n\"\n    \"            }\\n\"\n    \"            break;\\n\"\n    \"        }\\n\"\n    \"        /* @noinspection PhpUndefinedVariableInspection */\\n\"\n    \"        return $ret;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getRound(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->round;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function reconnect(): void\\n\"\n    \"    {\\n\"\n    \"        $constructor = $this->constructor;\\n\"\n    \"        parent::__construct($constructor());\\n\"\n    \"        $this->round++;\\n\"\n    \"        /* restore context */\\n\"\n    \"        if (!empty($this->charsetContext)) {\\n\"\n    \"            $this->__object->set_charset($this->charsetContext);\\n\"\n    \"        }\\n\"\n    \"        foreach ($this->setOptContext as $opt => $val) {\\n\"\n    \"            $this->__object->set_opt($opt, $val);\\n\"\n    \"        }\\n\"\n    \"        if (!empty($this->changeUserContext)) {\\n\"\n    \"            $this->__object->change_user(...$this->changeUserContext);\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function options(int $option, $value): bool\\n\"\n    \"    {\\n\"\n    \"        $this->setOptContext[$option] = $value;\\n\"\n    \"        return $this->__object->options($option, $value);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function set_opt(int $option, $value): bool\\n\"\n    \"    {\\n\"\n    \"        return $this->options($option, $value);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function set_charset(string $charset): bool\\n\"\n    \"    {\\n\"\n    \"        $this->charsetContext = $charset;\\n\"\n    \"        return $this->__object->set_charset($charset);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function change_user(string $user, string $password, ?string $database): bool\\n\"\n    \"    {\\n\"\n    \"        $this->changeUserContext = [$user, $password, $database];\\n\"\n    \"        return $this->__object->change_user($user, $password, $database);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_database_mysqli_statement_proxy =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Database;\\n\"\n    \"\\n\"\n    \"class MysqliStatementProxy extends ObjectProxy\\n\"\n    \"{\\n\"\n    \"    public const IO_METHOD_REGEX = '/^close|execute|fetch|prepare$/i';\\n\"\n    \"\\n\"\n    \"    /** @var \\\\mysqli_stmt */\\n\"\n    \"    protected $__object;\\n\"\n    \"\\n\"\n    \"    protected ?string $queryString;\\n\"\n    \"\\n\"\n    \"    protected array $attrSetContext = [];\\n\"\n    \"\\n\"\n    \"    protected array $bindParamContext;\\n\"\n    \"\\n\"\n    \"    protected array $bindResultContext;\\n\"\n    \"\\n\"\n    \"    protected MysqliProxy $parent;\\n\"\n    \"\\n\"\n    \"    protected int $parentRound;\\n\"\n    \"\\n\"\n    \"    public function __construct(\\\\mysqli_stmt $object, ?string $queryString, MysqliProxy $parent)\\n\"\n    \"    {\\n\"\n    \"        parent::__construct($object);\\n\"\n    \"        $this->queryString = $queryString;\\n\"\n    \"        $this->parent      = $parent;\\n\"\n    \"        $this->parentRound = $parent->getRound();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __call(string $name, array $arguments)\\n\"\n    \"    {\\n\"\n    \"        for ($n = 3; $n--;) {\\n\"\n    \"            $ret = @$this->__object->{$name}(...$arguments);\\n\"\n    \"            if ($ret === false) {\\n\"\n    \"                /* non-IO method */\\n\"\n    \"                if (!preg_match(static::IO_METHOD_REGEX, $name)) {\\n\"\n    \"                    break;\\n\"\n    \"                }\\n\"\n    \"                /* no more chances or non-IO failures or in transaction */\\n\"\n    \"                if (!in_array($this->__object->errno, $this->parent::IO_ERRORS, true) || ($n === 0)) {\\n\"\n    \"                    throw new MysqliException($this->__object->error, $this->__object->errno);\\n\"\n    \"                }\\n\"\n    \"                if ($this->parent->getRound() === $this->parentRound) {\\n\"\n    \"                    /* if not equal, parent has reconnected */\\n\"\n    \"                    $this->parent->reconnect();\\n\"\n    \"                }\\n\"\n    \"                $parent         = $this->parent->__getObject();\\n\"\n    \"                $this->__object = $this->queryString ? @$parent->prepare($this->queryString) : @$parent->stmt_init();\\n\"\n    \"                if ($this->__object === false) {\\n\"\n    \"                    throw new MysqliException($parent->error, $parent->errno);\\n\"\n    \"                }\\n\"\n    \"                if (!empty($this->bindParamContext)) {\\n\"\n    \"                    $this->__object->bind_param($this->bindParamContext[0], ...$this->bindParamContext[1]);\\n\"\n    \"                }\\n\"\n    \"                if (!empty($this->bindResultContext)) {\\n\"\n    \"                    $this->__object->bind_result($this->bindResultContext);\\n\"\n    \"                }\\n\"\n    \"                foreach ($this->attrSetContext as $attr => $value) {\\n\"\n    \"                    $this->__object->attr_set($attr, $value);\\n\"\n    \"                }\\n\"\n    \"                continue;\\n\"\n    \"            }\\n\"\n    \"            if (strcasecmp($name, 'prepare') === 0) {\\n\"\n    \"                $this->queryString = $arguments[0];\\n\"\n    \"            }\\n\"\n    \"            break;\\n\"\n    \"        }\\n\"\n    \"        /* @noinspection PhpUndefinedVariableInspection */\\n\"\n    \"        return $ret;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function attr_set($attr, $mode): bool\\n\"\n    \"    {\\n\"\n    \"        $this->attrSetContext[$attr] = $mode;\\n\"\n    \"        return $this->__object->attr_set($attr, $mode);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function bind_param($types, &...$arguments): bool\\n\"\n    \"    {\\n\"\n    \"        $this->bindParamContext = [$types, $arguments];\\n\"\n    \"        return $this->__object->bind_param($types, ...$arguments);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function bind_result(&...$arguments): bool\\n\"\n    \"    {\\n\"\n    \"        $this->bindResultContext = $arguments;\\n\"\n    \"        return $this->__object->bind_result(...$arguments);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_database_detects_lost_connections =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Database;\\n\"\n    \"\\n\"\n    \"class DetectsLostConnections\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * @var array<string>\\n\"\n    \"     */\\n\"\n    \"    private const ERROR_MESSAGES = [\\n\"\n    \"        'server has gone away',\\n\"\n    \"        'no connection to the server',\\n\"\n    \"        'Lost connection',\\n\"\n    \"        'is dead or not enabled',\\n\"\n    \"        'Error while sending',\\n\"\n    \"        'decryption failed or bad record mac',\\n\"\n    \"        'server closed the connection unexpectedly',\\n\"\n    \"        'SSL connection has been closed unexpectedly',\\n\"\n    \"        'Error writing data to the connection',\\n\"\n    \"        'Resource deadlock avoided',\\n\"\n    \"        'Transaction() on null',\\n\"\n    \"        'child connection forced to terminate due to client_idle_limit',\\n\"\n    \"        'query_wait_timeout',\\n\"\n    \"        'reset by peer',\\n\"\n    \"        'Physical connection is not usable',\\n\"\n    \"        'TCP Provider: Error code 0x68',\\n\"\n    \"        'ORA-03113',\\n\"\n    \"        'ORA-03114',\\n\"\n    \"        'Packets out of order. Expected',\\n\"\n    \"        'Adaptive Server connection failed',\\n\"\n    \"        'Communication link failure',\\n\"\n    \"        'connection is no longer usable',\\n\"\n    \"        'Login timeout expired',\\n\"\n    \"        'SQLSTATE[HY000] [2002] Connection refused',\\n\"\n    \"        'running with the --read-only option so it cannot execute this statement',\\n\"\n    \"        'The connection is broken and recovery is not possible. The connection is marked by the client driver as unrecoverable. No attempt was made to restore the connection.',\\n\"\n    \"        'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Try again',\\n\"\n    \"        'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known',\\n\"\n    \"        'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo for',\\n\"\n    \"        'SQLSTATE[HY000]: General error: 7 SSL SYSCALL error: EOF detected',\\n\"\n    \"        'SQLSTATE[HY000]: General error: 1105 The last transaction was aborted due to Seamless Scaling. Please retry.',\\n\"\n    \"        'Temporary failure in name resolution',\\n\"\n    \"        'SQLSTATE[08S01]: Communication link failure',\\n\"\n    \"        'SQLSTATE[08006] [7] could not connect to server: Connection refused Is the server running on host',\\n\"\n    \"        'SQLSTATE[HY000]: General error: 7 SSL SYSCALL error: No route to host',\\n\"\n    \"        'The client was disconnected by the server because of inactivity. See wait_timeout and interactive_timeout for configuring this behavior.',\\n\"\n    \"        'SQLSTATE[08006] [7] could not translate host name',\\n\"\n    \"        'TCP Provider: Error code 0x274C',\\n\"\n    \"        'SQLSTATE[HY000] [2002] No such file or directory',\\n\"\n    \"        'Reason: Server is in script upgrade mode. Only administrator can connect at this time.',\\n\"\n    \"        'Unknown $curl_error_code: 77',\\n\"\n    \"        'SQLSTATE[08006] [7] SSL error: sslv3 alert unexpected message',\\n\"\n    \"        'SQLSTATE[08006] [7] unrecognized SSL error code:',\\n\"\n    \"        'SQLSTATE[HY000] [2002] No connection could be made because the target machine actively refused it',\\n\"\n    \"        'Broken pipe',\\n\"\n    \"        // PDO::prepare(): Send of 77 bytes failed with errno=110 Operation timed out\\n\"\n    \"        // SSL: Handshake timed out\\n\"\n    \"        // SSL: Operation timed out\\n\"\n    \"        // SSL: Connection timed out\\n\"\n    \"        // SQLSTATE[HY000] [2002] Connection timed out\\n\"\n    \"        'timed out',\\n\"\n    \"        'Error reading result',\\n\"\n    \"    ];\\n\"\n    \"\\n\"\n    \"    public static function causedByLostConnection(\\\\Throwable $e): bool\\n\"\n    \"    {\\n\"\n    \"        $message = $e->getMessage();\\n\"\n    \"        foreach (self::ERROR_MESSAGES as $needle) {\\n\"\n    \"            if (mb_strpos($message, $needle) !== false) {\\n\"\n    \"                return true;\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return false;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_database_pdo_config =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Database;\\n\"\n    \"\\n\"\n    \"class PDOConfig\\n\"\n    \"{\\n\"\n    \"    public const DRIVER_MYSQL = 'mysql';\\n\"\n    \"\\n\"\n    \"    protected string $driver = self::DRIVER_MYSQL;\\n\"\n    \"\\n\"\n    \"    protected string $host = '127.0.0.1';\\n\"\n    \"\\n\"\n    \"    protected int $port = 3306;\\n\"\n    \"\\n\"\n    \"    protected ?string $unixSocket;\\n\"\n    \"\\n\"\n    \"    protected string $dbname = 'test';\\n\"\n    \"\\n\"\n    \"    protected string $charset = 'utf8mb4';\\n\"\n    \"\\n\"\n    \"    protected string $username = 'root';\\n\"\n    \"\\n\"\n    \"    protected string $password = 'root';\\n\"\n    \"\\n\"\n    \"    protected array $options = [];\\n\"\n    \"\\n\"\n    \"    public function getDriver(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->driver;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withDriver(string $driver): self\\n\"\n    \"    {\\n\"\n    \"        $this->driver = $driver;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getHost(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->host;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withHost(string $host): self\\n\"\n    \"    {\\n\"\n    \"        $this->host = $host;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getPort(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->port;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function hasUnixSocket(): bool\\n\"\n    \"    {\\n\"\n    \"        return !empty($this->unixSocket);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getUnixSocket(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->unixSocket ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withUnixSocket(?string $unixSocket): self\\n\"\n    \"    {\\n\"\n    \"        $this->unixSocket = $unixSocket;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withPort(int $port): self\\n\"\n    \"    {\\n\"\n    \"        $this->port = $port;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getDbname(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->dbname;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withDbname(string $dbname): self\\n\"\n    \"    {\\n\"\n    \"        $this->dbname = $dbname;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getCharset(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->charset;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withCharset(string $charset): self\\n\"\n    \"    {\\n\"\n    \"        $this->charset = $charset;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getUsername(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->username;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withUsername(string $username): self\\n\"\n    \"    {\\n\"\n    \"        $this->username = $username;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getPassword(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->password;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withPassword(string $password): self\\n\"\n    \"    {\\n\"\n    \"        $this->password = $password;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getOptions(): array\\n\"\n    \"    {\\n\"\n    \"        return $this->options;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withOptions(array $options): self\\n\"\n    \"    {\\n\"\n    \"        $this->options = $options;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Returns the list of available drivers\\n\"\n    \"     *\\n\"\n    \"     * @return string[]\\n\"\n    \"     */\\n\"\n    \"    public static function getAvailableDrivers(): array\\n\"\n    \"    {\\n\"\n    \"        return [\\n\"\n    \"            self::DRIVER_MYSQL,\\n\"\n    \"        ];\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_database_pdo_pool =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Database;\\n\"\n    \"\\n\"\n    \"use PDO;\\n\"\n    \"use Swoole\\\\ConnectionPool;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * @method void put(PDO|PDOProxy $connection)\\n\"\n    \" */\\n\"\n    \"class PDOPool extends ConnectionPool\\n\"\n    \"{\\n\"\n    \"    public function __construct(protected PDOConfig $config, int $size = self::DEFAULT_SIZE)\\n\"\n    \"    {\\n\"\n    \"        parent::__construct(function () {\\n\"\n    \"            $driver = $this->config->getDriver();\\n\"\n    \"            if ($driver === 'sqlite') {\\n\"\n    \"                return new \\\\PDO($this->createDSN('sqlite'));\\n\"\n    \"            }\\n\"\n    \"\\n\"\n    \"            return new \\\\PDO($this->createDSN($driver), $this->config->getUsername(), $this->config->getPassword(), $this->config->getOptions());\\n\"\n    \"        }, $size, PDOProxy::class);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Get a PDO connection from the pool. The PDO connection (a PDO object) is wrapped in a PDOProxy object returned.\\n\"\n    \"     *\\n\"\n    \"     * @param float $timeout > 0 means waiting for the specified number of seconds. other means no waiting.\\n\"\n    \"     * @return PDOProxy|false Returns a PDOProxy object from the pool, or false if the pool is full and the timeout is reached.\\n\"\n    \"     *                        {@inheritDoc}\\n\"\n    \"     */\\n\"\n    \"    public function get(float $timeout = -1)\\n\"\n    \"    {\\n\"\n    \"        /* @var \\\\Swoole\\\\Database\\\\PDOProxy|false $pdo */\\n\"\n    \"        $pdo = parent::get($timeout);\\n\"\n    \"        if ($pdo === false) {\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $pdo->reset();\\n\"\n    \"\\n\"\n    \"        return $pdo;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @purpose create DSN\\n\"\n    \"     * @throws \\\\Exception\\n\"\n    \"     */\\n\"\n    \"    private function createDSN(string $driver): string\\n\"\n    \"    {\\n\"\n    \"        switch ($driver) {\\n\"\n    \"            case 'mysql':\\n\"\n    \"                if ($this->config->hasUnixSocket()) {\\n\"\n    \"                    $dsn = \\\"mysql:unix_socket={$this->config->getUnixSocket()};dbname={$this->config->getDbname()};charset={$this->config->getCharset()}\\\";\\n\"\n    \"                } else {\\n\"\n    \"                    $dsn = \\\"mysql:host={$this->config->getHost()};port={$this->config->getPort()};dbname={$this->config->getDbname()};charset={$this->config->getCharset()}\\\";\\n\"\n    \"                }\\n\"\n    \"                break;\\n\"\n    \"            case 'pgsql':\\n\"\n    \"                $dsn = 'pgsql:host=' . ($this->config->hasUnixSocket() ? $this->config->getUnixSocket() : $this->config->getHost()) . \\\";port={$this->config->getPort()};dbname={$this->config->getDbname()}\\\";\\n\"\n    \"                break;\\n\"\n    \"            case 'oci':\\n\"\n    \"                $dsn = 'oci:dbname=' . ($this->config->hasUnixSocket() ? $this->config->getUnixSocket() : $this->config->getHost()) . ':' . $this->config->getPort() . '/' . $this->config->getDbname() . ';charset=' . $this->config->getCharset();\\n\"\n    \"                break;\\n\"\n    \"            case 'sqlite':\\n\"\n    \"                // There are three types of SQLite databases: databases on disk, databases in memory, and temporary\\n\"\n    \"                // databases (which are deleted when the connections are closed). It doesn't make sense to use\\n\"\n    \"                // connection pool for the latter two types of databases, because each connection connects to a\\n\"\n    \"                //different in-memory or temporary SQLite database.\\n\"\n    \"                if ($this->config->getDbname() === '') {\\n\"\n    \"                    throw new \\\\Exception('Connection pool in Swoole does not support temporary SQLite databases.');\\n\"\n    \"                }\\n\"\n    \"                if ($this->config->getDbname() === ':memory:') {\\n\"\n    \"                    throw new \\\\Exception('Connection pool in Swoole does not support creating SQLite databases in memory.');\\n\"\n    \"                }\\n\"\n    \"                $dsn = 'sqlite:' . $this->config->getDbname();\\n\"\n    \"                break;\\n\"\n    \"            default:\\n\"\n    \"                throw new \\\\Exception('Unsupported Database Driver:' . $driver);\\n\"\n    \"        }\\n\"\n    \"        return $dsn;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_database_pdo_proxy =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Database;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * @method \\\\PDO __getObject()\\n\"\n    \" */\\n\"\n    \"class PDOProxy extends ObjectProxy\\n\"\n    \"{\\n\"\n    \"    /** @var \\\\PDO */\\n\"\n    \"    protected $__object;\\n\"\n    \"\\n\"\n    \"    protected array $setAttributeContext = [];\\n\"\n    \"\\n\"\n    \"    /** @var callable */\\n\"\n    \"    protected $constructor;\\n\"\n    \"\\n\"\n    \"    protected int $round = 0;\\n\"\n    \"\\n\"\n    \"    protected int $inTransaction = 0;\\n\"\n    \"\\n\"\n    \"    public function __construct(callable $constructor)\\n\"\n    \"    {\\n\"\n    \"        parent::__construct($constructor());\\n\"\n    \"        $this->__object->setAttribute(\\\\PDO::ATTR_ERRMODE, \\\\PDO::ERRMODE_EXCEPTION);\\n\"\n    \"        $this->constructor = $constructor;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __call(string $name, array $arguments)\\n\"\n    \"    {\\n\"\n    \"        try {\\n\"\n    \"            $ret = $this->__object->{$name}(...$arguments);\\n\"\n    \"        } catch (\\\\PDOException $e) {\\n\"\n    \"            if (!$this->__object->inTransaction() && DetectsLostConnections::causedByLostConnection($e)) {\\n\"\n    \"                $this->reconnect();\\n\"\n    \"                $ret = $this->__object->{$name}(...$arguments);\\n\"\n    \"            } else {\\n\"\n    \"                throw $e;\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if (strcasecmp($name, 'beginTransaction') === 0) {\\n\"\n    \"            $this->inTransaction++;\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if ((strcasecmp($name, 'commit') === 0 || strcasecmp($name, 'rollback') === 0) && $this->inTransaction > 0) {\\n\"\n    \"            $this->inTransaction--;\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if ((strcasecmp($name, 'prepare') === 0) || (strcasecmp($name, 'query') === 0)) {\\n\"\n    \"            $ret = new PDOStatementProxy($ret, $this);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return $ret;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getRound(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->round;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function reconnect(): void\\n\"\n    \"    {\\n\"\n    \"        $constructor = $this->constructor;\\n\"\n    \"        parent::__construct($constructor());\\n\"\n    \"        $this->__object->setAttribute(\\\\PDO::ATTR_ERRMODE, \\\\PDO::ERRMODE_EXCEPTION);\\n\"\n    \"        $this->round++;\\n\"\n    \"        /* restore context */\\n\"\n    \"        foreach ($this->setAttributeContext as $attribute => $value) {\\n\"\n    \"            $this->__object->setAttribute($attribute, $value);\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function setAttribute(int $attribute, $value): bool\\n\"\n    \"    {\\n\"\n    \"        $this->setAttributeContext[$attribute] = $value;\\n\"\n    \"        return $this->__object->setAttribute($attribute, $value);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function inTransaction(): bool\\n\"\n    \"    {\\n\"\n    \"        return $this->inTransaction > 0;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function reset(): void\\n\"\n    \"    {\\n\"\n    \"        $this->inTransaction = 0;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_database_pdo_statement_proxy =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Database;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * The proxy class for PHP class PDOStatement.\\n\"\n    \" *\\n\"\n    \" * @see https://www.php.net/PDOStatement The PDOStatement class\\n\"\n    \" */\\n\"\n    \"class PDOStatementProxy extends ObjectProxy\\n\"\n    \"{\\n\"\n    \"    /** @var \\\\PDOStatement */\\n\"\n    \"    protected $__object;\\n\"\n    \"\\n\"\n    \"    protected array $setAttributeContext = [];\\n\"\n    \"\\n\"\n    \"    protected array $setFetchModeContext;\\n\"\n    \"\\n\"\n    \"    protected array $bindParamContext = [];\\n\"\n    \"\\n\"\n    \"    protected array $bindColumnContext = [];\\n\"\n    \"\\n\"\n    \"    protected array $bindValueContext = [];\\n\"\n    \"\\n\"\n    \"    protected PDOProxy $parent;\\n\"\n    \"\\n\"\n    \"    /** @var int */\\n\"\n    \"    protected $parentRound;\\n\"\n    \"\\n\"\n    \"    public function __construct(\\\\PDOStatement $object, PDOProxy $parent)\\n\"\n    \"    {\\n\"\n    \"        parent::__construct($object);\\n\"\n    \"        $this->parent      = $parent;\\n\"\n    \"        $this->parentRound = $parent->getRound();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __call(string $name, array $arguments)\\n\"\n    \"    {\\n\"\n    \"        try {\\n\"\n    \"            $ret = $this->__object->{$name}(...$arguments);\\n\"\n    \"        } catch (\\\\PDOException $e) {\\n\"\n    \"            if (!$this->parent->inTransaction() && DetectsLostConnections::causedByLostConnection($e)) {\\n\"\n    \"                if ($this->parent->getRound() === $this->parentRound) {\\n\"\n    \"                    /* if not equal, parent has reconnected */\\n\"\n    \"                    $this->parent->reconnect();\\n\"\n    \"                }\\n\"\n    \"                $parent         = $this->parent->__getObject();\\n\"\n    \"                $this->__object = $parent->prepare($this->__object->queryString);\\n\"\n    \"\\n\"\n    \"                foreach ($this->setAttributeContext as $attribute => $value) {\\n\"\n    \"                    $this->__object->setAttribute($attribute, $value);\\n\"\n    \"                }\\n\"\n    \"                if (!empty($this->setFetchModeContext)) {\\n\"\n    \"                    $this->__object->setFetchMode(...$this->setFetchModeContext);\\n\"\n    \"                }\\n\"\n    \"                foreach ($this->bindParamContext as $param => $item) {\\n\"\n    \"                    $this->__object->bindParam($param, ...$item);\\n\"\n    \"                }\\n\"\n    \"                foreach ($this->bindColumnContext as $column => $item) {\\n\"\n    \"                    $this->__object->bindColumn($column, ...$item);\\n\"\n    \"                }\\n\"\n    \"                foreach ($this->bindValueContext as $value => $item) {\\n\"\n    \"                    $this->__object->bindParam($value, ...$item);\\n\"\n    \"                }\\n\"\n    \"                $ret = $this->__object->{$name}(...$arguments);\\n\"\n    \"            } else {\\n\"\n    \"                throw $e;\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return $ret;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function setAttribute(int $attribute, $value): bool\\n\"\n    \"    {\\n\"\n    \"        $this->setAttributeContext[$attribute] = $value;\\n\"\n    \"        return $this->__object->setAttribute($attribute, $value);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Set the default fetch mode for this statement.\\n\"\n    \"     *\\n\"\n    \"     * @see https://www.php.net/manual/en/pdostatement.setfetchmode.php\\n\"\n    \"     */\\n\"\n    \"    public function setFetchMode(int $mode, ...$params): bool\\n\"\n    \"    {\\n\"\n    \"        $this->setFetchModeContext = func_get_args();\\n\"\n    \"        return $this->__object->setFetchMode(...$this->setFetchModeContext);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function bindParam($parameter, &$variable, $data_type = \\\\PDO::PARAM_STR, $length = 0, $driver_options = null): bool\\n\"\n    \"    {\\n\"\n    \"        $this->bindParamContext[$parameter] = [$variable, $data_type, $length, $driver_options];\\n\"\n    \"        return $this->__object->bindParam($parameter, $variable, $data_type, $length, $driver_options);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function bindColumn($column, &$param, $type = null, $maxlen = null, $driverdata = null): bool\\n\"\n    \"    {\\n\"\n    \"        $this->bindColumnContext[$column] = [$param, $type, $maxlen, $driverdata];\\n\"\n    \"        return $this->__object->bindColumn($column, $param, $type, $maxlen, $driverdata);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function bindValue($parameter, $value, $data_type = \\\\PDO::PARAM_STR): bool\\n\"\n    \"    {\\n\"\n    \"        $this->bindValueContext[$parameter] = [$value, $data_type];\\n\"\n    \"        return $this->__object->bindValue($parameter, $value, $data_type);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_database_redis_config =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Database;\\n\"\n    \"\\n\"\n    \"class RedisConfig\\n\"\n    \"{\\n\"\n    \"    protected string $host = '127.0.0.1';\\n\"\n    \"\\n\"\n    \"    protected int $port = 6379;\\n\"\n    \"\\n\"\n    \"    protected float $timeout = 0.0;\\n\"\n    \"\\n\"\n    \"    protected string $reserved = '';\\n\"\n    \"\\n\"\n    \"    protected int $retry_interval = 0;\\n\"\n    \"\\n\"\n    \"    protected float $read_timeout = 0.0;\\n\"\n    \"\\n\"\n    \"    protected string $auth = '';\\n\"\n    \"\\n\"\n    \"    protected int $dbIndex = 0;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @var array<int, mixed>\\n\"\n    \"     */\\n\"\n    \"    protected array $options = [];\\n\"\n    \"\\n\"\n    \"    public function getHost(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->host;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withHost(string $host): self\\n\"\n    \"    {\\n\"\n    \"        $this->host = $host;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getPort(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->port;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withPort(int $port): self\\n\"\n    \"    {\\n\"\n    \"        $this->port = $port;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getTimeout(): float\\n\"\n    \"    {\\n\"\n    \"        return $this->timeout;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withTimeout(float $timeout): self\\n\"\n    \"    {\\n\"\n    \"        $this->timeout = $timeout;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getReserved(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->reserved;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withReserved(string $reserved): self\\n\"\n    \"    {\\n\"\n    \"        $this->reserved = $reserved;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getRetryInterval(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->retry_interval;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withRetryInterval(int $retry_interval): self\\n\"\n    \"    {\\n\"\n    \"        $this->retry_interval = $retry_interval;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getReadTimeout(): float\\n\"\n    \"    {\\n\"\n    \"        return $this->read_timeout;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withReadTimeout(float $read_timeout): self\\n\"\n    \"    {\\n\"\n    \"        $this->read_timeout = $read_timeout;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getAuth(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->auth;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withAuth(string $auth): self\\n\"\n    \"    {\\n\"\n    \"        $this->auth = $auth;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getDbIndex(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->dbIndex;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withDbIndex(int $dbIndex): self\\n\"\n    \"    {\\n\"\n    \"        $this->dbIndex = $dbIndex;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Add a configurable option.\\n\"\n    \"     */\\n\"\n    \"    public function withOption(int $option, mixed $value): self\\n\"\n    \"    {\\n\"\n    \"        $this->options[$option] = $value;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Add/override configurable options.\\n\"\n    \"     *\\n\"\n    \"     * @param array<int, mixed> $options\\n\"\n    \"     */\\n\"\n    \"    public function setOptions(array $options): self\\n\"\n    \"    {\\n\"\n    \"        $this->options = $options;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Get configurable options.\\n\"\n    \"     *\\n\"\n    \"     * @return array<int, mixed>\\n\"\n    \"     */\\n\"\n    \"    public function getOptions(): array\\n\"\n    \"    {\\n\"\n    \"        return $this->options;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_database_redis_pool =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Database;\\n\"\n    \"\\n\"\n    \"use Redis;\\n\"\n    \"use Swoole\\\\ConnectionPool;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * @method \\\\Redis get(float $timeout = -1)\\n\"\n    \" * @method void put(Redis $connection)\\n\"\n    \" */\\n\"\n    \"class RedisPool extends ConnectionPool\\n\"\n    \"{\\n\"\n    \"    public function __construct(protected RedisConfig $config, int $size = self::DEFAULT_SIZE)\\n\"\n    \"    {\\n\"\n    \"        parent::__construct(function () {\\n\"\n    \"            $redis = new \\\\Redis();\\n\"\n    \"            /* Compatible with different versions of Redis extension as much as possible */\\n\"\n    \"            $arguments = [\\n\"\n    \"                $this->config->getHost(),\\n\"\n    \"                $this->config->getPort(),\\n\"\n    \"            ];\\n\"\n    \"            if ($this->config->getTimeout() !== 0.0) {\\n\"\n    \"                $arguments[] = $this->config->getTimeout();\\n\"\n    \"            }\\n\"\n    \"            if ($this->config->getRetryInterval() !== 0) {\\n\"\n    \"                /* reserved should always be NULL */\\n\"\n    \"                $arguments[] = null;\\n\"\n    \"                $arguments[] = $this->config->getRetryInterval();\\n\"\n    \"            }\\n\"\n    \"            if ($this->config->getReadTimeout() !== 0.0) {\\n\"\n    \"                $arguments[] = $this->config->getReadTimeout();\\n\"\n    \"            }\\n\"\n    \"            $redis->connect(...$arguments);\\n\"\n    \"            if ($this->config->getAuth()) {\\n\"\n    \"                $redis->auth($this->config->getAuth());\\n\"\n    \"            }\\n\"\n    \"            if ($this->config->getDbIndex() !== 0) {\\n\"\n    \"                $redis->select($this->config->getDbIndex());\\n\"\n    \"            }\\n\"\n    \"\\n\"\n    \"            /* Set Redis options. */\\n\"\n    \"            foreach ($this->config->getOptions() as $key => $value) {\\n\"\n    \"                $redis->setOption($key, $value);\\n\"\n    \"            }\\n\"\n    \"\\n\"\n    \"            return $redis;\\n\"\n    \"        }, $size);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_http_status =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Http;\\n\"\n    \"\\n\"\n    \"abstract class Status\\n\"\n    \"{\\n\"\n    \"    public const CONTINUE = 100;\\n\"\n    \"\\n\"\n    \"    public const SWITCHING_PROTOCOLS = 101;\\n\"\n    \"\\n\"\n    \"    public const PROCESSING = 102;\\n\"\n    \"\\n\"\n    \"    public const OK = 200;\\n\"\n    \"\\n\"\n    \"    public const CREATED = 201;\\n\"\n    \"\\n\"\n    \"    public const ACCEPTED = 202;\\n\"\n    \"\\n\"\n    \"    public const NON_AUTHORITATIVE_INFORMATION = 203;\\n\"\n    \"\\n\"\n    \"    public const NO_CONTENT = 204;\\n\"\n    \"\\n\"\n    \"    public const RESET_CONTENT = 205;\\n\"\n    \"\\n\"\n    \"    public const PARTIAL_CONTENT = 206;\\n\"\n    \"\\n\"\n    \"    public const MULTI_STATUS = 207;\\n\"\n    \"\\n\"\n    \"    public const ALREADY_REPORTED = 208;\\n\"\n    \"\\n\"\n    \"    public const IM_USED = 226;\\n\"\n    \"\\n\"\n    \"    public const MULTIPLE_CHOICES = 300;\\n\"\n    \"\\n\"\n    \"    public const MOVED_PERMANENTLY = 301;\\n\"\n    \"\\n\"\n    \"    public const FOUND = 302;\\n\"\n    \"\\n\"\n    \"    public const SEE_OTHER = 303;\\n\"\n    \"\\n\"\n    \"    public const NOT_MODIFIED = 304;\\n\"\n    \"\\n\"\n    \"    public const USE_PROXY = 305;\\n\"\n    \"\\n\"\n    \"    public const SWITCH_PROXY = 306;\\n\"\n    \"\\n\"\n    \"    public const TEMPORARY_REDIRECT = 307;\\n\"\n    \"\\n\"\n    \"    public const PERMANENT_REDIRECT = 308;\\n\"\n    \"\\n\"\n    \"    public const BAD_REQUEST = 400;\\n\"\n    \"\\n\"\n    \"    public const UNAUTHORIZED = 401;\\n\"\n    \"\\n\"\n    \"    public const PAYMENT_REQUIRED = 402;\\n\"\n    \"\\n\"\n    \"    public const FORBIDDEN = 403;\\n\"\n    \"\\n\"\n    \"    public const NOT_FOUND = 404;\\n\"\n    \"\\n\"\n    \"    public const METHOD_NOT_ALLOWED = 405;\\n\"\n    \"\\n\"\n    \"    public const NOT_ACCEPTABLE = 406;\\n\"\n    \"\\n\"\n    \"    public const PROXY_AUTHENTICATION_REQUIRED = 407;\\n\"\n    \"\\n\"\n    \"    public const REQUEST_TIME_OUT = 408;\\n\"\n    \"\\n\"\n    \"    public const CONFLICT = 409;\\n\"\n    \"\\n\"\n    \"    public const GONE = 410;\\n\"\n    \"\\n\"\n    \"    public const LENGTH_REQUIRED = 411;\\n\"\n    \"\\n\"\n    \"    public const PRECONDITION_FAILED = 412;\\n\"\n    \"\\n\"\n    \"    public const REQUEST_ENTITY_TOO_LARGE = 413;\\n\"\n    \"\\n\"\n    \"    public const REQUEST_URI_TOO_LARGE = 414;\\n\"\n    \"\\n\"\n    \"    public const UNSUPPORTED_MEDIA_TYPE = 415;\\n\"\n    \"\\n\"\n    \"    public const REQUESTED_RANGE_NOT_SATISFIABLE = 416;\\n\"\n    \"\\n\"\n    \"    public const EXPECTATION_FAILED = 417;\\n\"\n    \"\\n\"\n    \"    public const MISDIRECTED_REQUEST = 421;\\n\"\n    \"\\n\"\n    \"    public const UNPROCESSABLE_ENTITY = 422;\\n\"\n    \"\\n\"\n    \"    public const LOCKED = 423;\\n\"\n    \"\\n\"\n    \"    public const FAILED_DEPENDENCY = 424;\\n\"\n    \"\\n\"\n    \"    public const UNORDERED_COLLECTION = 425;\\n\"\n    \"\\n\"\n    \"    public const UPGRADE_REQUIRED = 426;\\n\"\n    \"\\n\"\n    \"    public const PRECONDITION_REQUIRED = 428;\\n\"\n    \"\\n\"\n    \"    public const TOO_MANY_REQUESTS = 429;\\n\"\n    \"\\n\"\n    \"    public const REQUEST_HEADER_FIELDS_TOO_LARGE = 431;\\n\"\n    \"\\n\"\n    \"    public const UNAVAILABLE_FOR_LEGAL_REASONS = 451;\\n\"\n    \"\\n\"\n    \"    public const INTERNAL_SERVER_ERROR = 500;\\n\"\n    \"\\n\"\n    \"    public const NOT_IMPLEMENTED = 501;\\n\"\n    \"\\n\"\n    \"    public const BAD_GATEWAY = 502;\\n\"\n    \"\\n\"\n    \"    public const SERVICE_UNAVAILABLE = 503;\\n\"\n    \"\\n\"\n    \"    public const GATEWAY_TIME_OUT = 504;\\n\"\n    \"\\n\"\n    \"    public const HTTP_VERSION_NOT_SUPPORTED = 505;\\n\"\n    \"\\n\"\n    \"    public const VARIANT_ALSO_NEGOTIATES = 506;\\n\"\n    \"\\n\"\n    \"    public const INSUFFICIENT_STORAGE = 507;\\n\"\n    \"\\n\"\n    \"    public const LOOP_DETECTED = 508;\\n\"\n    \"\\n\"\n    \"    public const NOT_EXTENDED = 510;\\n\"\n    \"\\n\"\n    \"    public const NETWORK_AUTHENTICATION_REQUIRED = 511;\\n\"\n    \"\\n\"\n    \"    protected static $reasonPhrases = [\\n\"\n    \"        self::CONTINUE                        => 'Continue',\\n\"\n    \"        self::SWITCHING_PROTOCOLS             => 'Switching Protocols',\\n\"\n    \"        self::PROCESSING                      => 'Processing',\\n\"\n    \"        self::OK                              => 'OK',\\n\"\n    \"        self::CREATED                         => 'Created',\\n\"\n    \"        self::ACCEPTED                        => 'Accepted',\\n\"\n    \"        self::NON_AUTHORITATIVE_INFORMATION   => 'Non-Authoritative Information',\\n\"\n    \"        self::NO_CONTENT                      => 'No Content',\\n\"\n    \"        self::RESET_CONTENT                   => 'Reset Content',\\n\"\n    \"        self::PARTIAL_CONTENT                 => 'Partial Content',\\n\"\n    \"        self::MULTI_STATUS                    => 'Multi-status',\\n\"\n    \"        self::ALREADY_REPORTED                => 'Already Reported',\\n\"\n    \"        self::IM_USED                         => 'IM Used',\\n\"\n    \"        self::MULTIPLE_CHOICES                => 'Multiple Choices',\\n\"\n    \"        self::MOVED_PERMANENTLY               => 'Moved Permanently',\\n\"\n    \"        self::FOUND                           => 'Found',\\n\"\n    \"        self::SEE_OTHER                       => 'See Other',\\n\"\n    \"        self::NOT_MODIFIED                    => 'Not Modified',\\n\"\n    \"        self::USE_PROXY                       => 'Use Proxy',\\n\"\n    \"        self::SWITCH_PROXY                    => 'Switch Proxy',\\n\"\n    \"        self::TEMPORARY_REDIRECT              => 'Temporary Redirect',\\n\"\n    \"        self::PERMANENT_REDIRECT              => 'Permanent Redirect',\\n\"\n    \"        self::BAD_REQUEST                     => 'Bad Request',\\n\"\n    \"        self::UNAUTHORIZED                    => 'Unauthorized',\\n\"\n    \"        self::PAYMENT_REQUIRED                => 'Payment Required',\\n\"\n    \"        self::FORBIDDEN                       => 'Forbidden',\\n\"\n    \"        self::NOT_FOUND                       => 'Not Found',\\n\"\n    \"        self::METHOD_NOT_ALLOWED              => 'Method Not Allowed',\\n\"\n    \"        self::NOT_ACCEPTABLE                  => 'Not Acceptable',\\n\"\n    \"        self::PROXY_AUTHENTICATION_REQUIRED   => 'Proxy Authentication Required',\\n\"\n    \"        self::REQUEST_TIME_OUT                => 'Request Time-out',\\n\"\n    \"        self::CONFLICT                        => 'Conflict',\\n\"\n    \"        self::GONE                            => 'Gone',\\n\"\n    \"        self::LENGTH_REQUIRED                 => 'Length Required',\\n\"\n    \"        self::PRECONDITION_FAILED             => 'Precondition Failed',\\n\"\n    \"        self::REQUEST_ENTITY_TOO_LARGE        => 'Request Entity Too Large',\\n\"\n    \"        self::REQUEST_URI_TOO_LARGE           => 'Request-URI Too Large',\\n\"\n    \"        self::UNSUPPORTED_MEDIA_TYPE          => 'Unsupported Media Type',\\n\"\n    \"        self::REQUESTED_RANGE_NOT_SATISFIABLE => 'Requested range not satisfiable',\\n\"\n    \"        self::EXPECTATION_FAILED              => 'Expectation Failed',\\n\"\n    \"        self::MISDIRECTED_REQUEST             => 'Misdirected Request',\\n\"\n    \"        self::UNPROCESSABLE_ENTITY            => 'Unprocessable Entity',\\n\"\n    \"        self::LOCKED                          => 'Locked',\\n\"\n    \"        self::FAILED_DEPENDENCY               => 'Failed Dependency',\\n\"\n    \"        self::UNORDERED_COLLECTION            => 'Unordered Collection',\\n\"\n    \"        self::UPGRADE_REQUIRED                => 'Upgrade Required',\\n\"\n    \"        self::PRECONDITION_REQUIRED           => 'Precondition Required',\\n\"\n    \"        self::TOO_MANY_REQUESTS               => 'Too Many Requests',\\n\"\n    \"        self::REQUEST_HEADER_FIELDS_TOO_LARGE => 'Request Header Fields Too Large',\\n\"\n    \"        self::UNAVAILABLE_FOR_LEGAL_REASONS   => 'Unavailable For Legal Reasons',\\n\"\n    \"        self::INTERNAL_SERVER_ERROR           => 'Internal Server Error',\\n\"\n    \"        self::NOT_IMPLEMENTED                 => 'Not Implemented',\\n\"\n    \"        self::BAD_GATEWAY                     => 'Bad Gateway',\\n\"\n    \"        self::SERVICE_UNAVAILABLE             => 'Service Unavailable',\\n\"\n    \"        self::GATEWAY_TIME_OUT                => 'Gateway Time-out',\\n\"\n    \"        self::HTTP_VERSION_NOT_SUPPORTED      => 'HTTP Version not supported',\\n\"\n    \"        self::VARIANT_ALSO_NEGOTIATES         => 'Variant Also Negotiates',\\n\"\n    \"        self::INSUFFICIENT_STORAGE            => 'Insufficient Storage',\\n\"\n    \"        self::LOOP_DETECTED                   => 'Loop Detected',\\n\"\n    \"        self::NOT_EXTENDED                    => 'Not Extended',\\n\"\n    \"        self::NETWORK_AUTHENTICATION_REQUIRED => 'Network Authentication Required',\\n\"\n    \"    ];\\n\"\n    \"\\n\"\n    \"    public static function getReasonPhrases(): array\\n\"\n    \"    {\\n\"\n    \"        return static::$reasonPhrases;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function getReasonPhrase(int $value): string\\n\"\n    \"    {\\n\"\n    \"        return static::$reasonPhrases[$value] ?? 'Unknown';\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_curl_exception =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Curl;\\n\"\n    \"\\n\"\n    \"use Swoole;\\n\"\n    \"\\n\"\n    \"class Exception extends Swoole\\\\Exception\\n\"\n    \"{\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_curl_handler =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"/* @noinspection PhpComposerExtensionStubsInspection, PhpDuplicateSwitchCaseBodyInspection, PhpInconsistentReturnPointsInspection */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Curl;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Constant;\\n\"\n    \"use Swoole\\\\Coroutine\\\\Http\\\\Client;\\n\"\n    \"use Swoole\\\\Coroutine\\\\System;\\n\"\n    \"use Swoole\\\\Curl\\\\Exception as CurlException;\\n\"\n    \"use Swoole\\\\Http\\\\Status;\\n\"\n    \"\\n\"\n    \"final class Handler implements \\\\Stringable\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * @var Client|null\\n\"\n    \"     */\\n\"\n    \"    private $client;\\n\"\n    \"\\n\"\n    \"    private $info = [\\n\"\n    \"        'url'                     => '',\\n\"\n    \"        'content_type'            => '',\\n\"\n    \"        'http_code'               => 0,\\n\"\n    \"        'header_size'             => 0,\\n\"\n    \"        'request_size'            => 0,\\n\"\n    \"        'filetime'                => -1,\\n\"\n    \"        'ssl_verify_result'       => 0,\\n\"\n    \"        'redirect_count'          => 0,\\n\"\n    \"        'total_time'              => 5.3E-5,\\n\"\n    \"        'namelookup_time'         => 0.0,\\n\"\n    \"        'connect_time'            => 0.0,\\n\"\n    \"        'pretransfer_time'        => 0.0,\\n\"\n    \"        'size_upload'             => 0.0,\\n\"\n    \"        'size_download'           => 0.0,\\n\"\n    \"        'speed_download'          => 0.0,\\n\"\n    \"        'speed_upload'            => 0.0,\\n\"\n    \"        'download_content_length' => -1.0,\\n\"\n    \"        'upload_content_length'   => -1.0,\\n\"\n    \"        'starttransfer_time'      => 0.0,\\n\"\n    \"        'redirect_time'           => 0.0,\\n\"\n    \"        'redirect_url'            => '',\\n\"\n    \"        'primary_ip'              => '',\\n\"\n    \"        'certinfo'                => [],\\n\"\n    \"        'primary_port'            => 0,\\n\"\n    \"        'local_ip'                => '',\\n\"\n    \"        'local_port'              => 0,\\n\"\n    \"        'http_version'            => 0,\\n\"\n    \"        'protocol'                => 0,\\n\"\n    \"        'ssl_verifyresult'        => 0,\\n\"\n    \"        'scheme'                  => '',\\n\"\n    \"        'private'                 => '',\\n\"\n    \"    ];\\n\"\n    \"\\n\"\n    \"    private $withHeaderOut = false;\\n\"\n    \"\\n\"\n    \"    private $withFileTime = false;\\n\"\n    \"\\n\"\n    \"    private $urlInfo;\\n\"\n    \"\\n\"\n    \"    private $postData;\\n\"\n    \"\\n\"\n    \"    private $infile;\\n\"\n    \"\\n\"\n    \"    private $infileSize = PHP_INT_MAX;\\n\"\n    \"\\n\"\n    \"    private $outputStream;\\n\"\n    \"\\n\"\n    \"    private $proxyType;\\n\"\n    \"\\n\"\n    \"    private $proxy;\\n\"\n    \"\\n\"\n    \"    private $proxyPort = 1080;\\n\"\n    \"\\n\"\n    \"    private $proxyUsername;\\n\"\n    \"\\n\"\n    \"    private $proxyPassword;\\n\"\n    \"\\n\"\n    \"    private $clientOptions = [];\\n\"\n    \"\\n\"\n    \"    private $followLocation = false;\\n\"\n    \"\\n\"\n    \"    private $autoReferer = false;\\n\"\n    \"\\n\"\n    \"    private $maxRedirects;\\n\"\n    \"\\n\"\n    \"    private $withHeader = false;\\n\"\n    \"\\n\"\n    \"    private $nobody = false;\\n\"\n    \"\\n\"\n    \"    /** @var callable|null */\\n\"\n    \"    private $headerFunction;\\n\"\n    \"\\n\"\n    \"    /** @var callable|null */\\n\"\n    \"    private $readFunction;\\n\"\n    \"\\n\"\n    \"    /** @var callable|null */\\n\"\n    \"    private $writeFunction;\\n\"\n    \"\\n\"\n    \"    private $noProgress = true;\\n\"\n    \"\\n\"\n    \"    /** @var callable */\\n\"\n    \"    private $progressFunction;\\n\"\n    \"\\n\"\n    \"    private $returnTransfer = false;\\n\"\n    \"\\n\"\n    \"    private $method = '';\\n\"\n    \"\\n\"\n    \"    private $headers = [];\\n\"\n    \"\\n\"\n    \"    private $headerMap = [];\\n\"\n    \"\\n\"\n    \"    private $transfer;\\n\"\n    \"\\n\"\n    \"    private $errCode = 0;\\n\"\n    \"\\n\"\n    \"    private $errMsg = '';\\n\"\n    \"\\n\"\n    \"    private $failOnError = false;\\n\"\n    \"\\n\"\n    \"    private $closed = false;\\n\"\n    \"\\n\"\n    \"    private $cookieJar = '';\\n\"\n    \"\\n\"\n    \"    private $resolve = [];\\n\"\n    \"\\n\"\n    \"    private $unix_socket_path = '';\\n\"\n    \"\\n\"\n    \"    public function __construct(string $url = '')\\n\"\n    \"    {\\n\"\n    \"        if ($url) {\\n\"\n    \"            $this->setUrl($url);\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __toString(): string\\n\"\n    \"    {\\n\"\n    \"        $id = spl_object_id($this);\\n\"\n    \"        return \\\"Object({$id}) of type (curl)\\\";\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /* ====== Public APIs ====== */\\n\"\n    \"\\n\"\n    \"    public function isAvailable(): bool\\n\"\n    \"    {\\n\"\n    \"        if ($this->closed) {\\n\"\n    \"            trigger_error('supplied resource is not a valid cURL handle resource', E_USER_WARNING);\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"        return true;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function setOpt(int $opt, $value): bool\\n\"\n    \"    {\\n\"\n    \"        return $this->isAvailable() and $this->setOption($opt, $value);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function exec()\\n\"\n    \"    {\\n\"\n    \"        if (!$this->isAvailable()) {\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"        return $this->execute();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getInfo()\\n\"\n    \"    {\\n\"\n    \"        return $this->isAvailable() ? $this->info : false;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function errno(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->isAvailable() ? $this->errCode : 0;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function error(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->isAvailable() ? $this->errMsg : '';\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function reset()\\n\"\n    \"    {\\n\"\n    \"        if (!$this->isAvailable()) {\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"        foreach ((new \\\\ReflectionClass(self::class))->getDefaultProperties() as $name => $value) {\\n\"\n    \"            $this->{$name} = $value;\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getContent()\\n\"\n    \"    {\\n\"\n    \"        if (!$this->isAvailable()) {\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"        return $this->transfer;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function close(): void\\n\"\n    \"    {\\n\"\n    \"        if (!$this->isAvailable()) {\\n\"\n    \"            return;\\n\"\n    \"        }\\n\"\n    \"        foreach ($this as &$property) { // @phpstan-ignore foreach.nonIterable\\n\"\n    \"            $property = null;\\n\"\n    \"        }\\n\"\n    \"        $this->closed = true;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function create(?array $urlInfo = null): void\\n\"\n    \"    {\\n\"\n    \"        if ($urlInfo === null) {\\n\"\n    \"            $urlInfo = $this->urlInfo;\\n\"\n    \"        }\\n\"\n    \"        $host = $urlInfo['host'];\\n\"\n    \"        $port = $urlInfo['port'];\\n\"\n    \"        if (isset($this->resolve[$host])) {\\n\"\n    \"            if (!$this->hasHeader('Host')) {\\n\"\n    \"                $this->setHeader('Host', $host);\\n\"\n    \"            }\\n\"\n    \"            $this->urlInfo['host'] = $host = $this->resolve[$host][$port] ?? null ?: $host;\\n\"\n    \"        }\\n\"\n    \"        if ($this->unix_socket_path) {\\n\"\n    \"            $host = $this->unix_socket_path;\\n\"\n    \"            $port = 0;\\n\"\n    \"            if (stripos($host, 'unix:/') !== 0) {\\n\"\n    \"                $host = \\\"unix:/{$host}\\\";\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"        $this->client = new Client($host, $port, $urlInfo['scheme'] === 'https');\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function getUrl(): string\\n\"\n    \"    {\\n\"\n    \"        if (empty($this->urlInfo['path'])) {\\n\"\n    \"            $url = '/';\\n\"\n    \"        } else {\\n\"\n    \"            $url = $this->urlInfo['path'];\\n\"\n    \"        }\\n\"\n    \"        if (!empty($this->urlInfo['query'])) {\\n\"\n    \"            $url .= '?' . $this->urlInfo['query'];\\n\"\n    \"        }\\n\"\n    \"        if (!empty($this->urlInfo['fragment'])) {\\n\"\n    \"            $url .= '#' . $this->urlInfo['fragment'];\\n\"\n    \"        }\\n\"\n    \"        return $url;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function setUrl(string $url, bool $setInfo = true): bool\\n\"\n    \"    {\\n\"\n    \"        if (strlen($url) === 0) {\\n\"\n    \"            $this->setError(CURLE_URL_MALFORMAT, 'No URL set!');\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"        if (!str_contains($url, '://') && $this->unix_socket_path === '') {\\n\"\n    \"            $url = 'http://' . $url;\\n\"\n    \"        }\\n\"\n    \"        if ($setInfo) {\\n\"\n    \"            $urlInfo = parse_url($url);\\n\"\n    \"            if ($this->unix_socket_path) {\\n\"\n    \"                if (empty($urlInfo['host']) && !empty($urlInfo['path'])) {\\n\"\n    \"                    $urlInfo['host'] = explode('/', $urlInfo['path'])[1] ?? null;\\n\"\n    \"                }\\n\"\n    \"                if (!$this->hasHeader('Host') && !empty($urlInfo['host'])) {\\n\"\n    \"                    $this->setHeader('Host', $urlInfo['host']);\\n\"\n    \"                }\\n\"\n    \"            }\\n\"\n    \"            if (!is_array($urlInfo)) {\\n\"\n    \"                $this->setError(CURLE_URL_MALFORMAT, \\\"URL[{$url}] using bad/illegal format\\\");\\n\"\n    \"                return false;\\n\"\n    \"            }\\n\"\n    \"            if (!$this->setUrlInfo($urlInfo)) {\\n\"\n    \"                return false;\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"        $this->info['url'] = $url;\\n\"\n    \"        return true;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function setUrlInfo(array $urlInfo): bool\\n\"\n    \"    {\\n\"\n    \"        if (empty($urlInfo['scheme'])) {\\n\"\n    \"            $urlInfo['scheme'] = 'http';\\n\"\n    \"        }\\n\"\n    \"        $scheme = $urlInfo['scheme'];\\n\"\n    \"        if ($scheme !== 'http' and $scheme !== 'https') {\\n\"\n    \"            $this->setError(CURLE_UNSUPPORTED_PROTOCOL, \\\"Protocol \\\\\\\"{$scheme}\\\\\\\" not supported or disabled in libcurl\\\");\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"        $host = $urlInfo['host'];\\n\"\n    \"        if ($this->info['primary_port'] !== 0) {\\n\"\n    \"            /* keep same with cURL, primary_port has the highest priority */\\n\"\n    \"            $urlInfo['port'] = $this->info['primary_port'];\\n\"\n    \"        } elseif (empty($urlInfo['port'])) {\\n\"\n    \"            $urlInfo['port'] = $scheme === 'https' ? 443 : 80;\\n\"\n    \"        } else {\\n\"\n    \"            $urlInfo['port'] = intval($urlInfo['port']);\\n\"\n    \"        }\\n\"\n    \"        $port = $urlInfo['port'];\\n\"\n    \"        if (isset($this->client)) {\\n\"\n    \"            $oldUrlInfo = $this->urlInfo;\\n\"\n    \"            if (($host !== $oldUrlInfo['host']) || ($port !== $oldUrlInfo['port']) || ($scheme !== $oldUrlInfo['scheme'])) {\\n\"\n    \"                /* target changed */\\n\"\n    \"                $this->create($urlInfo);\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"        $this->urlInfo = $urlInfo;\\n\"\n    \"        return true;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function setPort(int $port): void\\n\"\n    \"    {\\n\"\n    \"        $this->info['primary_port'] = $port;\\n\"\n    \"        if (!isset($this->urlInfo['port']) || $this->urlInfo['port'] !== $port) {\\n\"\n    \"            $this->urlInfo['port'] = $port;\\n\"\n    \"            if (isset($this->client)) {\\n\"\n    \"                /* target changed */\\n\"\n    \"                $this->create();\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function setError($code, $msg = ''): void\\n\"\n    \"    {\\n\"\n    \"        $this->errCode = $code;\\n\"\n    \"        $this->errMsg  = $msg ?: curl_strerror($code);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function hasHeader(string $headerName): bool\\n\"\n    \"    {\\n\"\n    \"        return isset($this->headerMap[strtolower($headerName)]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function setHeader(string $headerName, string $value): void\\n\"\n    \"    {\\n\"\n    \"        $lowerCaseHeaderName = strtolower($headerName);\\n\"\n    \"\\n\"\n    \"        if (isset($this->headerMap[$lowerCaseHeaderName])) {\\n\"\n    \"            unset($this->headers[$this->headerMap[$lowerCaseHeaderName]]);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if ($value !== '') {\\n\"\n    \"            $this->headers[$headerName]            = $value;\\n\"\n    \"            $this->headerMap[$lowerCaseHeaderName] = $headerName;\\n\"\n    \"        } else {\\n\"\n    \"            // remove empty headers (keep same with raw cURL)\\n\"\n    \"            unset($this->headerMap[$lowerCaseHeaderName]);\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws Exception\\n\"\n    \"     */\\n\"\n    \"    private function setOption(int $opt, mixed $value): bool\\n\"\n    \"    {\\n\"\n    \"        switch ($opt) {\\n\"\n    \"            // case CURLOPT_STDERR:\\n\"\n    \"            // case CURLOPT_WRITEHEADER:\\n\"\n    \"            case CURLOPT_FILE:\\n\"\n    \"            case CURLOPT_INFILE:\\n\"\n    \"                if (!is_resource($value)) {\\n\"\n    \"                    trigger_error('swoole_curl_setopt(): supplied argument is not a valid File-Handle resource', E_USER_WARNING);\\n\"\n    \"                    return false;\\n\"\n    \"                }\\n\"\n    \"                break;\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        switch ($opt) {\\n\"\n    \"            /*\\n\"\n    \"             * Basic\\n\"\n    \"             */\\n\"\n    \"            case CURLOPT_URL:\\n\"\n    \"                return $this->setUrl((string) $value);\\n\"\n    \"            case CURLOPT_PORT:\\n\"\n    \"                $this->setPort((int) $value);\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_FORBID_REUSE:\\n\"\n    \"                $this->clientOptions[Constant::OPTION_KEEP_ALIVE] = !$value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_RETURNTRANSFER:\\n\"\n    \"                $this->returnTransfer = (bool) $value;\\n\"\n    \"                $this->transfer       = '';\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_ENCODING:\\n\"\n    \"                if (empty($value)) {\\n\"\n    \"                    if (defined('SWOOLE_HAVE_ZLIB')) {\\n\"\n    \"                        $value = 'gzip, deflate';\\n\"\n    \"                    }\\n\"\n    \"                    if (defined('SWOOLE_HAVE_BROTLI')) {\\n\"\n    \"                        if (!empty($value)) {\\n\"\n    \"                            $value = 'br, ' . $value;\\n\"\n    \"                        } else {\\n\"\n    \"                            $value = 'br';\\n\"\n    \"                        }\\n\"\n    \"                    }\\n\"\n    \"                    if (empty($value)) {\\n\"\n    \"                        break;\\n\"\n    \"                    }\\n\"\n    \"                }\\n\"\n    \"                $this->setHeader('Accept-Encoding', $value);\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_PROXYTYPE:\\n\"\n    \"                if ($value !== CURLPROXY_HTTP and $value !== CURLPROXY_SOCKS5) {\\n\"\n    \"                    throw new CurlException('swoole_curl_setopt(): Only support following CURLOPT_PROXYTYPE values: CURLPROXY_HTTP, CURLPROXY_SOCKS5');\\n\"\n    \"                }\\n\"\n    \"                $this->proxyType = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_PROXY:\\n\"\n    \"                $this->proxy = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_PROXYPORT:\\n\"\n    \"                $this->proxyPort = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_PROXYUSERNAME:\\n\"\n    \"                $this->proxyUsername = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_PROXYPASSWORD:\\n\"\n    \"                $this->proxyPassword = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_PROXYUSERPWD:\\n\"\n    \"                $usernamePassword    = explode(':', $value);\\n\"\n    \"                $this->proxyUsername = urldecode($usernamePassword[0]);\\n\"\n    \"                $this->proxyPassword = urldecode($usernamePassword[1] ?? null);\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_PROXYAUTH:\\n\"\n    \"                /* ignored temporarily */\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_UNIX_SOCKET_PATH:\\n\"\n    \"                $realpath = realpath((string) $value);\\n\"\n    \"                if ($realpath) {\\n\"\n    \"                    $this->unix_socket_path = $realpath;\\n\"\n    \"                } else {\\n\"\n    \"                    $this->setError(CURLE_COULDNT_CONNECT);\\n\"\n    \"                }\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_NOBODY:\\n\"\n    \"                $this->nobody = boolval($value);\\n\"\n    \"                $this->method = 'HEAD';\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_RESOLVE:\\n\"\n    \"                foreach ((array) $value as $resolve) {\\n\"\n    \"                    $flag = substr($resolve, 0, 1);\\n\"\n    \"                    if ($flag === '+' || $flag === '-') {\\n\"\n    \"                        // TODO: [+]HOST:PORT:ADDRESS\\n\"\n    \"                        $resolve = substr($resolve, 1);\\n\"\n    \"                    }\\n\"\n    \"                    $tmpResolve = explode(':', $resolve, 3);\\n\"\n    \"                    $host       = $tmpResolve[0];\\n\"\n    \"                    $port       = $tmpResolve[1] ?? 0;\\n\"\n    \"                    $ip         = $tmpResolve[2] ?? '';\\n\"\n    \"                    if ($flag === '-') {\\n\"\n    \"                        unset($this->resolve[$host][$port]);\\n\"\n    \"                    } else {\\n\"\n    \"                        // TODO: HOST:PORT:ADDRESS[,ADDRESS]...\\n\"\n    \"                        $this->resolve[$host][$port] = explode(',', $ip)[0];\\n\"\n    \"                    }\\n\"\n    \"                }\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_IPRESOLVE:\\n\"\n    \"                if ($value !== CURL_IPRESOLVE_WHATEVER and $value !== CURL_IPRESOLVE_V4) {\\n\"\n    \"                    throw new CurlException('swoole_curl_setopt(): Only support following CURLOPT_IPRESOLVE values: CURL_IPRESOLVE_WHATEVER, CURL_IPRESOLVE_V4');\\n\"\n    \"                }\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_TCP_NODELAY:\\n\"\n    \"                $this->clientOptions[Constant::OPTION_OPEN_TCP_NODELAY] = boolval($value);\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_PRIVATE:\\n\"\n    \"                $this->info['private'] = $value;\\n\"\n    \"                break;\\n\"\n    \"                /*\\n\"\n    \"                 * Ignore options\\n\"\n    \"                 */\\n\"\n    \"            case CURLOPT_VERBOSE:\\n\"\n    \"                // trigger_error('swoole_curl_setopt(): CURLOPT_VERBOSE is not supported', E_USER_WARNING);\\n\"\n    \"            case CURLOPT_SSLVERSION:\\n\"\n    \"            case CURLOPT_NOSIGNAL:\\n\"\n    \"            case CURLOPT_FRESH_CONNECT:\\n\"\n    \"            case CURLOPT_DNS_USE_GLOBAL_CACHE:\\n\"\n    \"            case CURLOPT_DNS_CACHE_TIMEOUT:\\n\"\n    \"            case CURLOPT_STDERR:\\n\"\n    \"            case CURLOPT_WRITEHEADER:\\n\"\n    \"            case CURLOPT_BUFFERSIZE:\\n\"\n    \"            case CURLOPT_SSLCERTTYPE:\\n\"\n    \"            case CURLOPT_SSLKEYTYPE:\\n\"\n    \"            case CURLOPT_NOPROXY:\\n\"\n    \"            case CURLOPT_CERTINFO:\\n\"\n    \"            case CURLOPT_HEADEROPT:\\n\"\n    \"            case CURLOPT_PROXYHEADER:\\n\"\n    \"            case CURLOPT_HTTPPROXYTUNNEL:\\n\"\n    \"                break;\\n\"\n    \"                /*\\n\"\n    \"                 * SSL\\n\"\n    \"                 */\\n\"\n    \"            case CURLOPT_SSL_VERIFYHOST:\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_SSL_VERIFYPEER:\\n\"\n    \"                $this->clientOptions[Constant::OPTION_SSL_VERIFY_PEER] = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_SSLCERT:\\n\"\n    \"                $this->clientOptions[Constant::OPTION_SSL_CERT_FILE] = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_SSLKEY:\\n\"\n    \"                $this->clientOptions[Constant::OPTION_SSL_KEY_FILE] = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_CAINFO:\\n\"\n    \"                $this->clientOptions[Constant::OPTION_SSL_CAFILE] = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_CAPATH:\\n\"\n    \"                $this->clientOptions[Constant::OPTION_SSL_CAPATH] = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_KEYPASSWD:\\n\"\n    \"            case CURLOPT_SSLCERTPASSWD:\\n\"\n    \"            case CURLOPT_SSLKEYPASSWD:\\n\"\n    \"                $this->clientOptions[Constant::OPTION_SSL_PASSPHRASE] = $value;\\n\"\n    \"                break;\\n\"\n    \"                /*\\n\"\n    \"                 * Http POST\\n\"\n    \"                 */\\n\"\n    \"            case CURLOPT_POST:\\n\"\n    \"                $this->method = 'POST';\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_POSTFIELDS:\\n\"\n    \"                $this->postData = $value;\\n\"\n    \"                if (!$this->method) {\\n\"\n    \"                    $this->method = 'POST';\\n\"\n    \"                }\\n\"\n    \"                break;\\n\"\n    \"                /*\\n\"\n    \"                 * Upload\\n\"\n    \"                 */\\n\"\n    \"            case CURLOPT_SAFE_UPLOAD:\\n\"\n    \"                if (!$value) {\\n\"\n    \"                    trigger_error('swoole_curl_setopt(): Disabling safe uploads is no longer supported', E_USER_WARNING);\\n\"\n    \"                    return false;\\n\"\n    \"                }\\n\"\n    \"                break;\\n\"\n    \"                /*\\n\"\n    \"                 * Http Header\\n\"\n    \"                 */\\n\"\n    \"            case CURLOPT_HTTPHEADER:\\n\"\n    \"                if (!is_array($value) and !is_iterable($value)) {\\n\"\n    \"                    trigger_error('swoole_curl_setopt(): You must pass either an object or an array with the CURLOPT_HTTPHEADER argument', E_USER_WARNING);\\n\"\n    \"                    return false;\\n\"\n    \"                }\\n\"\n    \"                foreach ($value as $header) {\\n\"\n    \"                    $header      = explode(':', $header, 2);\\n\"\n    \"                    $headerName  = $header[0];\\n\"\n    \"                    $headerValue = trim($header[1] ?? '');\\n\"\n    \"                    $this->setHeader($headerName, $headerValue);\\n\"\n    \"                }\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_REFERER:\\n\"\n    \"                $this->setHeader('Referer', $value);\\n\"\n    \"                break;\\n\"\n    \"            case CURLINFO_HEADER_OUT:\\n\"\n    \"                $this->withHeaderOut = boolval($value);\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_FILETIME:\\n\"\n    \"                $this->withFileTime = boolval($value);\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_USERAGENT:\\n\"\n    \"                $this->setHeader('User-Agent', $value);\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_CUSTOMREQUEST:\\n\"\n    \"                $this->method = (string) $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_PROTOCOLS:\\n\"\n    \"                if (($value & ~(CURLPROTO_HTTP | CURLPROTO_HTTPS)) != 0) {\\n\"\n    \"                    throw new CurlException(\\\"swoole_curl_setopt(): CURLOPT_PROTOCOLS[{$value}] is not supported\\\");\\n\"\n    \"                }\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_REDIR_PROTOCOLS:\\n\"\n    \"                if (($value & ~(CURLPROTO_HTTP | CURLPROTO_HTTPS)) != 0) {\\n\"\n    \"                    throw new CurlException(\\\"swoole_curl_setopt(): CURLOPT_REDIR_PROTOCOLS[{$value}] is not supported\\\");\\n\"\n    \"                }\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_HTTP_VERSION:\\n\"\n    \"                if ($value != CURL_HTTP_VERSION_1_1) {\\n\"\n    \"                    trigger_error(\\\"swoole_curl_setopt(): CURLOPT_HTTP_VERSION[{$value}] is not supported\\\", E_USER_WARNING);\\n\"\n    \"                    return false;\\n\"\n    \"                }\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_FAILONERROR:\\n\"\n    \"                $this->failOnError = $value;\\n\"\n    \"                break;\\n\"\n    \"                /*\\n\"\n    \"                 * Http Cookie\\n\"\n    \"                 */\\n\"\n    \"            case CURLOPT_COOKIE:\\n\"\n    \"                $this->setHeader('Cookie', $value);\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_COOKIEJAR:\\n\"\n    \"                $this->cookieJar = (string) $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_COOKIEFILE:\\n\"\n    \"                if (is_file((string) $value)) {\\n\"\n    \"                    $this->setHeader('Cookie', file_get_contents($value));\\n\"\n    \"                }\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_CONNECTTIMEOUT:\\n\"\n    \"                $this->clientOptions[Constant::OPTION_CONNECT_TIMEOUT] = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_CONNECTTIMEOUT_MS:\\n\"\n    \"                $this->clientOptions[Constant::OPTION_CONNECT_TIMEOUT] = $value / 1000;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_TIMEOUT:\\n\"\n    \"                $this->clientOptions[Constant::OPTION_TIMEOUT] = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_TIMEOUT_MS:\\n\"\n    \"                $this->clientOptions[Constant::OPTION_TIMEOUT] = $value / 1000;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_FILE:\\n\"\n    \"                $this->outputStream = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_HEADER:\\n\"\n    \"                $this->withHeader = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_HEADERFUNCTION:\\n\"\n    \"                $this->headerFunction = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_READFUNCTION:\\n\"\n    \"                $this->readFunction = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_WRITEFUNCTION:\\n\"\n    \"                if (SWOOLE_VERSION_ID >= 50100) {\\n\"\n    \"                    $this->clientOptions[Constant::OPTION_WRITE_FUNC] = function ($client, $data) use ($value) {\\n\"\n    \"                        return $value($this, $data);\\n\"\n    \"                    };\\n\"\n    \"                } else {\\n\"\n    \"                    $this->writeFunction = $value;\\n\"\n    \"                }\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_NOPROGRESS:\\n\"\n    \"                $this->noProgress = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_PROGRESSFUNCTION:\\n\"\n    \"                $this->progressFunction = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_HTTPAUTH:\\n\"\n    \"                if (!($value & CURLAUTH_BASIC)) {\\n\"\n    \"                    trigger_error(\\\"swoole_curl_setopt(): CURLOPT_HTTPAUTH[{$value}] is not supported\\\", E_USER_WARNING);\\n\"\n    \"                    return false;\\n\"\n    \"                }\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_USERPWD:\\n\"\n    \"                $this->setHeader('Authorization', 'Basic ' . base64_encode($value));\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_FOLLOWLOCATION:\\n\"\n    \"                $this->followLocation = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_AUTOREFERER:\\n\"\n    \"                $this->autoReferer = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_MAXREDIRS:\\n\"\n    \"                $this->maxRedirects = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_PUT:\\n\"\n    \"            case CURLOPT_UPLOAD:\\n\"\n    \"                /* after libcurl 7.12, CURLOPT_PUT is replaced by CURLOPT_UPLOAD */\\n\"\n    \"                $this->method = 'PUT';\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_INFILE:\\n\"\n    \"                $this->infile = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_INFILESIZE:\\n\"\n    \"                $this->infileSize = $value;\\n\"\n    \"                break;\\n\"\n    \"            case CURLOPT_HTTPGET:\\n\"\n    \"                /* Since GET is the default, this is only necessary if the request method has been changed. */\\n\"\n    \"                $this->method = 'GET';\\n\"\n    \"                break;\\n\"\n    \"            default:\\n\"\n    \"                throw new CurlException(\\\"swoole_curl_setopt(): option[{$opt}] is not supported\\\");\\n\"\n    \"        }\\n\"\n    \"        return true;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function execute()\\n\"\n    \"    {\\n\"\n    \"        $this->info['redirect_count'] = $this->info['starttransfer_time'] = 0;\\n\"\n    \"        $this->info['redirect_url']   = '';\\n\"\n    \"        $timeBegin                    = microtime(true);\\n\"\n    \"        /*\\n\"\n    \"         * Socket\\n\"\n    \"         */\\n\"\n    \"        if (!$this->urlInfo) {\\n\"\n    \"            $this->setError(CURLE_URL_MALFORMAT, 'No URL set or URL using bad/illegal format');\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"        if (!isset($this->client)) {\\n\"\n    \"            $this->create();\\n\"\n    \"        }\\n\"\n    \"        while (true) {\\n\"\n    \"            $client = $this->client;\\n\"\n    \"            /*\\n\"\n    \"             * Http Proxy\\n\"\n    \"             */\\n\"\n    \"            if ($this->proxy) {\\n\"\n    \"                $parse         = parse_url($this->proxy);\\n\"\n    \"                $proxy         = $parse['host'] ?? $parse['path'];\\n\"\n    \"                $proxyPort     = $parse['port'] ?? $this->proxyPort;\\n\"\n    \"                $proxyUsername = $parse['user'] ?? $this->proxyUsername;\\n\"\n    \"                $proxyPassword = $parse['pass'] ?? $this->proxyPassword;\\n\"\n    \"                $proxyType     = $parse['scheme'] ?? $this->proxyType;\\n\"\n    \"                if (is_string($proxyType)) {\\n\"\n    \"                    if ($proxyType === 'socks5') {\\n\"\n    \"                        $proxyType = CURLPROXY_SOCKS5;\\n\"\n    \"                    } else {\\n\"\n    \"                        $proxyType = CURLPROXY_HTTP;\\n\"\n    \"                    }\\n\"\n    \"                }\\n\"\n    \"\\n\"\n    \"                if (!filter_var($proxy, FILTER_VALIDATE_IP)) {\\n\"\n    \"                    $ip = System::gethostbyname($proxy, AF_INET, $this->clientOptions['connect_timeout'] ?? -1);\\n\"\n    \"                    if (!$ip) {\\n\"\n    \"                        $this->setError(CURLE_COULDNT_RESOLVE_PROXY, 'Could not resolve proxy: ' . $proxy);\\n\"\n    \"                        return false;\\n\"\n    \"                    }\\n\"\n    \"                    $this->proxy = $proxy = $ip;\\n\"\n    \"                }\\n\"\n    \"                $proxyOptions = match ($proxyType) {\\n\"\n    \"                    CURLPROXY_HTTP => [\\n\"\n    \"                        'http_proxy_host'     => $proxy,\\n\"\n    \"                        'http_proxy_port'     => $proxyPort,\\n\"\n    \"                        'http_proxy_username' => $proxyUsername,\\n\"\n    \"                        'http_proxy_password' => $proxyPassword,\\n\"\n    \"                    ],\\n\"\n    \"                    CURLPROXY_SOCKS5 => [\\n\"\n    \"                        'socks5_host'     => $proxy,\\n\"\n    \"                        'socks5_port'     => $proxyPort,\\n\"\n    \"                        'socks5_username' => $proxyUsername,\\n\"\n    \"                        'socks5_password' => $proxyPassword,\\n\"\n    \"                    ],\\n\"\n    \"                    default => throw new CurlException(\\\"Unexpected proxy type [{$proxyType}]\\\"),\\n\"\n    \"                };\\n\"\n    \"            }\\n\"\n    \"            /*\\n\"\n    \"             * Client Options\\n\"\n    \"             */\\n\"\n    \"            $client->set(\\n\"\n    \"                $this->clientOptions +\\n\"\n    \"                ($proxyOptions ?? [])\\n\"\n    \"            );\\n\"\n    \"            /*\\n\"\n    \"             * Method\\n\"\n    \"             */\\n\"\n    \"            if ($this->method) {\\n\"\n    \"                $client->setMethod($this->method);\\n\"\n    \"            }\\n\"\n    \"            /*\\n\"\n    \"             * Data\\n\"\n    \"             */\\n\"\n    \"            if ($this->infile) {\\n\"\n    \"                // Infile\\n\"\n    \"                // Notice: we make its priority higher than postData but raw cURL will send both of them\\n\"\n    \"                $data = '';\\n\"\n    \"                while (true) {\\n\"\n    \"                    $nLength = $this->infileSize - strlen($data);\\n\"\n    \"                    if ($nLength === 0) {\\n\"\n    \"                        break;\\n\"\n    \"                    }\\n\"\n    \"                    if (feof($this->infile)) {\\n\"\n    \"                        break;\\n\"\n    \"                    }\\n\"\n    \"                    $data .= fread($this->infile, $nLength);\\n\"\n    \"                }\\n\"\n    \"                $client->setData($data);\\n\"\n    \"                // Notice: although we reset it, raw cURL never do this\\n\"\n    \"                $this->infile     = null;\\n\"\n    \"                $this->infileSize = PHP_INT_MAX;\\n\"\n    \"            } else {\\n\"\n    \"                // POST data\\n\"\n    \"                if ($this->postData) {\\n\"\n    \"                    if (is_string($this->postData)) {\\n\"\n    \"                        if (!$this->hasHeader('content-type')) {\\n\"\n    \"                            $this->setHeader('Content-Type', 'application/x-www-form-urlencoded');\\n\"\n    \"                        }\\n\"\n    \"                    } elseif (is_array($this->postData)) {\\n\"\n    \"                        foreach ($this->postData as $k => $v) {\\n\"\n    \"                            if ($v instanceof \\\\CURLFile) {\\n\"\n    \"                                $client->addFile($v->getFilename(), $k, $v->getMimeType() ?: 'application/octet-stream', $v->getPostFilename());\\n\"\n    \"                                unset($this->postData[$k]);\\n\"\n    \"                            }\\n\"\n    \"                        }\\n\"\n    \"                    }\\n\"\n    \"                    $client->setData($this->postData);\\n\"\n    \"                }\\n\"\n    \"            }\\n\"\n    \"            /*\\n\"\n    \"             * Headers\\n\"\n    \"             */\\n\"\n    \"            // Notice: setHeaders must be placed last, because headers may be changed by other parts\\n\"\n    \"            // As much as possible to ensure that Host is the first header.\\n\"\n    \"            // See: http://tools.ietf.org/html/rfc7230#section-5.4\\n\"\n    \"            $client->setHeaders($this->headers);\\n\"\n    \"            /**\\n\"\n    \"             * Execute.\\n\"\n    \"             */\\n\"\n    \"            $executeResult = $client->execute($this->getUrl());\\n\"\n    \"            if (!$executeResult) {\\n\"\n    \"                $errCode = $client->errCode;\\n\"\n    \"                if ($errCode == SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED or $errCode == SWOOLE_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT) {\\n\"\n    \"                    $this->setError(CURLE_COULDNT_RESOLVE_HOST, 'Could not resolve host: ' . $client->host);\\n\"\n    \"                } else {\\n\"\n    \"                    $this->setError($errCode, $client->errMsg);\\n\"\n    \"                }\\n\"\n    \"                $this->info['total_time'] = microtime(true) - $timeBegin;\\n\"\n    \"                return false;\\n\"\n    \"            }\\n\"\n    \"            if ($client->statusCode >= 300 and $client->statusCode < 400 and isset($client->headers['location'])) {\\n\"\n    \"                $redirectParsedUrl = $this->getRedirectUrl($client->headers['location']);\\n\"\n    \"                $redirectUrl       = self::unparseUrl($redirectParsedUrl);\\n\"\n    \"                if ($this->followLocation and ($this->maxRedirects === null or $this->info['redirect_count'] < $this->maxRedirects)) {\\n\"\n    \"                    if ($this->info['redirect_count'] === 0) {\\n\"\n    \"                        $this->info['starttransfer_time'] = microtime(true) - $timeBegin;\\n\"\n    \"                        $redirectBeginTime                = microtime(true);\\n\"\n    \"                    }\\n\"\n    \"                    // force GET\\n\"\n    \"                    if (in_array($client->statusCode, [Status::MOVED_PERMANENTLY, Status::FOUND, Status::SEE_OTHER])) {\\n\"\n    \"                        $this->method = 'GET';\\n\"\n    \"                    }\\n\"\n    \"                    if ($this->autoReferer) {\\n\"\n    \"                        $this->setHeader('Referer', $this->info['url']);\\n\"\n    \"                    }\\n\"\n    \"                    $this->setUrl($redirectUrl, false);\\n\"\n    \"                    $this->setUrlInfo($redirectParsedUrl);\\n\"\n    \"                    $this->info['redirect_count']++;\\n\"\n    \"                } else {\\n\"\n    \"                    $this->info['redirect_url'] = $redirectUrl;\\n\"\n    \"                    break;\\n\"\n    \"                }\\n\"\n    \"            } elseif ($this->failOnError && $client->statusCode >= 400) {\\n\"\n    \"                $this->setError(CURLE_HTTP_RETURNED_ERROR, \\\"The requested URL returned error: {$client->statusCode} \\\" . Status::getReasonPhrase($client->statusCode));\\n\"\n    \"                return false;\\n\"\n    \"            } else {\\n\"\n    \"                break;\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"        $this->info['total_time']     = microtime(true) - $timeBegin;\\n\"\n    \"        $this->info['http_code']      = $client->statusCode;\\n\"\n    \"        $this->info['content_type']   = $client->headers['content-type'] ?? '';\\n\"\n    \"        $this->info['size_download']  = $this->info['download_content_length'] = strlen($client->body);\\n\"\n    \"        $this->info['speed_download'] = 1 / $this->info['total_time'] * $this->info['size_download'];\\n\"\n    \"        if (isset($redirectBeginTime)) {\\n\"\n    \"            $this->info['redirect_time'] = microtime(true) - $redirectBeginTime;\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if (filter_var($this->urlInfo['host'], FILTER_VALIDATE_IP)) {\\n\"\n    \"            $this->info['primary_ip'] = $this->urlInfo['host'];\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if ($this->unix_socket_path) {\\n\"\n    \"            $this->info['primary_ip']   = $this->unix_socket_path;\\n\"\n    \"            $this->info['primary_port'] = $this->urlInfo['port'];\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $headerContent = '';\\n\"\n    \"        if ($client->headers) {\\n\"\n    \"            $cb = $this->headerFunction;\\n\"\n    \"            if ($client->statusCode > 0) {\\n\"\n    \"                $row = \\\"HTTP/1.1 {$client->statusCode} \\\" . Status::getReasonPhrase($client->statusCode) . \\\"\\\\r\\\\n\\\";\\n\"\n    \"                if ($cb) {\\n\"\n    \"                    $cb($this, $row);\\n\"\n    \"                }\\n\"\n    \"                $headerContent .= $row;\\n\"\n    \"            }\\n\"\n    \"            foreach ($client->headers as $k => $v) {\\n\"\n    \"                $list = is_array($v) ? $v : [$v];\\n\"\n    \"                foreach ($list as $_v) {\\n\"\n    \"                    $row = \\\"{$k}: {$_v}\\\\r\\\\n\\\";\\n\"\n    \"                    if ($cb) {\\n\"\n    \"                        $cb($this, $row);\\n\"\n    \"                    }\\n\"\n    \"                    $headerContent .= $row;\\n\"\n    \"                }\\n\"\n    \"            }\\n\"\n    \"            $headerContent .= \\\"\\\\r\\\\n\\\";\\n\"\n    \"            $this->info['header_size'] = strlen($headerContent);\\n\"\n    \"            if ($cb) {\\n\"\n    \"                $cb($this, '');\\n\"\n    \"            }\\n\"\n    \"        } else {\\n\"\n    \"            $this->info['header_size'] = 0;\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if ($client->body and $this->readFunction) {\\n\"\n    \"            $cb = $this->readFunction;\\n\"\n    \"            $cb($this, $this->outputStream, strlen($client->body));\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if ($this->withHeader) {\\n\"\n    \"            $transfer = $headerContent . $client->body;\\n\"\n    \"        } else {\\n\"\n    \"            $transfer = $client->body;\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if ($this->withHeaderOut) {\\n\"\n    \"            $headerOutContent             = $client->getHeaderOut();\\n\"\n    \"            $this->info['request_header'] = $headerOutContent ? $headerOutContent . \\\"\\\\r\\\\n\\\\r\\\\n\\\" : '';\\n\"\n    \"        }\\n\"\n    \"        if ($this->withFileTime) {\\n\"\n    \"            if (isset($client->headers['last-modified'])) {\\n\"\n    \"                $this->info['filetime'] = strtotime($client->headers['last-modified']);\\n\"\n    \"            } else {\\n\"\n    \"                $this->info['filetime'] = -1;\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if (!empty($this->cookieJar)) {\\n\"\n    \"            if ($this->cookieJar === '-') {\\n\"\n    \"                foreach ((array) $client->set_cookie_headers as $cookie) {\\n\"\n    \"                    echo $cookie . PHP_EOL;\\n\"\n    \"                }\\n\"\n    \"            } else {\\n\"\n    \"                $cookies = '';\\n\"\n    \"                foreach ((array) $client->set_cookie_headers as $cookie) {\\n\"\n    \"                    $cookies .= \\\"{$cookie};\\\";\\n\"\n    \"                }\\n\"\n    \"                file_put_contents($this->cookieJar, $cookies);\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if ($this->writeFunction) {\\n\"\n    \"            if (!is_callable($this->writeFunction)) { // @phpstan-ignore booleanNot.alwaysFalse\\n\"\n    \"                trigger_error('curl_exec(): Could not call the CURLOPT_WRITEFUNCTION', E_USER_WARNING);\\n\"\n    \"                $this->setError(CURLE_WRITE_ERROR, 'Failure writing output to destination');\\n\"\n    \"                return false;\\n\"\n    \"            }\\n\"\n    \"            call_user_func($this->writeFunction, $this, $transfer);\\n\"\n    \"            return true;\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if ($this->returnTransfer) {\\n\"\n    \"            return $this->transfer = $transfer;\\n\"\n    \"        }\\n\"\n    \"        if ($this->outputStream) {\\n\"\n    \"            return fwrite($this->outputStream, $transfer) === strlen($transfer);\\n\"\n    \"        }\\n\"\n    \"        echo $transfer;\\n\"\n    \"\\n\"\n    \"        return true;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /* ====== Redirect helper ====== */\\n\"\n    \"\\n\"\n    \"    private static function unparseUrl(array $parsedUrl): string\\n\"\n    \"    {\\n\"\n    \"        $scheme   = ($parsedUrl['scheme'] ?? 'http') . '://';\\n\"\n    \"        $host     = $parsedUrl['host'] ?? '';\\n\"\n    \"        $port     = isset($parsedUrl['port']) ? ':' . $parsedUrl['port'] : '';\\n\"\n    \"        $user     = $parsedUrl['user'] ?? '';\\n\"\n    \"        $pass     = isset($parsedUrl['pass']) ? ':' . $parsedUrl['pass'] : '';\\n\"\n    \"        $pass     = ($user or $pass) ? \\\"{$pass}@\\\" : '';\\n\"\n    \"        $path     = $parsedUrl['path'] ?? '';\\n\"\n    \"        $query    = (isset($parsedUrl['query']) and $parsedUrl['query'] !== '') ? '?' . $parsedUrl['query'] : '';\\n\"\n    \"        $fragment = isset($parsedUrl['fragment']) ? '#' . $parsedUrl['fragment'] : '';\\n\"\n    \"        return $scheme . $user . $pass . $host . $port . $path . $query . $fragment;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function getRedirectUrl(string $location): array\\n\"\n    \"    {\\n\"\n    \"        $uri = parse_url($location);\\n\"\n    \"        if (isset($uri['host'])) {\\n\"\n    \"            $redirectUri = $uri;\\n\"\n    \"        } else {\\n\"\n    \"            if (!isset($location[0])) {\\n\"\n    \"                return [];\\n\"\n    \"            }\\n\"\n    \"            $redirectUri          = $this->urlInfo;\\n\"\n    \"            $redirectUri['query'] = '';\\n\"\n    \"            if ($location[0] === '/') {\\n\"\n    \"                $redirectUri['path'] = $location;\\n\"\n    \"            } else {\\n\"\n    \"                $path = dirname($redirectUri['path'] ?? '');\\n\"\n    \"                if ($path === '.') {\\n\"\n    \"                    $path = '/';\\n\"\n    \"                }\\n\"\n    \"                if (isset($location[1]) and str_starts_with($location, './')) {\\n\"\n    \"                    $location = substr($location, 2);\\n\"\n    \"                }\\n\"\n    \"                $redirectUri['path'] = $path . $location;\\n\"\n    \"            }\\n\"\n    \"            if (is_array($uri)) {\\n\"\n    \"                foreach ($uri as $k => $v) {\\n\"\n    \"                    if (!in_array($k, ['path', 'query'])) {\\n\"\n    \"                        $redirectUri[$k] = $v;\\n\"\n    \"                    }\\n\"\n    \"                }\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"        return $redirectUri;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * FastCGI constants.\\n\"\n    \" */\\n\"\n    \"class FastCGI\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * Number of bytes in a FCGI_Header.  Future versions of the protocol\\n\"\n    \"     * will not reduce this number.\\n\"\n    \"     */\\n\"\n    \"    public const HEADER_LEN = 8;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Format of FCGI_HEADER for unpacking in PHP\\n\"\n    \"     */\\n\"\n    \"    public const HEADER_FORMAT = 'Cversion/Ctype/nrequestId/ncontentLength/CpaddingLength/Creserved';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Max content length of a record\\n\"\n    \"     */\\n\"\n    \"    public const MAX_CONTENT_LENGTH = 65535;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Value for version component of FCGI_Header\\n\"\n    \"     */\\n\"\n    \"    public const VERSION_1 = 1;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Values for type component of FCGI_Header\\n\"\n    \"     */\\n\"\n    \"    public const BEGIN_REQUEST = 1;\\n\"\n    \"\\n\"\n    \"    public const ABORT_REQUEST = 2;\\n\"\n    \"\\n\"\n    \"    public const END_REQUEST = 3;\\n\"\n    \"\\n\"\n    \"    public const PARAMS = 4;\\n\"\n    \"\\n\"\n    \"    public const STDIN = 5;\\n\"\n    \"\\n\"\n    \"    public const STDOUT = 6;\\n\"\n    \"\\n\"\n    \"    public const STDERR = 7;\\n\"\n    \"\\n\"\n    \"    public const DATA = 8;\\n\"\n    \"\\n\"\n    \"    public const GET_VALUES = 9;\\n\"\n    \"\\n\"\n    \"    public const GET_VALUES_RESULT = 10;\\n\"\n    \"\\n\"\n    \"    public const UNKNOWN_TYPE = 11;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Value for requestId component of FCGI_Header\\n\"\n    \"     */\\n\"\n    \"    public const DEFAULT_REQUEST_ID = 1;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Mask for flags component of FCGI_BeginRequestBody\\n\"\n    \"     */\\n\"\n    \"    public const KEEP_CONN = 1;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Values for role component of FCGI_BeginRequestBody\\n\"\n    \"     */\\n\"\n    \"    public const RESPONDER = 1;\\n\"\n    \"\\n\"\n    \"    public const AUTHORIZER = 2;\\n\"\n    \"\\n\"\n    \"    public const FILTER = 3;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Values for protocolStatus component of FCGI_EndRequestBody\\n\"\n    \"     */\\n\"\n    \"    public const REQUEST_COMPLETE = 0;\\n\"\n    \"\\n\"\n    \"    public const CANT_MPX_CONN = 1;\\n\"\n    \"\\n\"\n    \"    public const OVERLOADED = 2;\\n\"\n    \"\\n\"\n    \"    public const UNKNOWN_ROLE = 3;\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_record =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\FastCGI;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * FastCGI record.\\n\"\n    \" */\\n\"\n    \"class Record implements \\\\Stringable\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * Identifies the FastCGI protocol version.\\n\"\n    \"     */\\n\"\n    \"    protected int $version = FastCGI::VERSION_1;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Identifies the FastCGI record type, i.e. the general function that the record performs.\\n\"\n    \"     */\\n\"\n    \"    protected int $type = FastCGI::UNKNOWN_TYPE;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Identifies the FastCGI request to which the record belongs.\\n\"\n    \"     */\\n\"\n    \"    protected int $requestId = FastCGI::DEFAULT_REQUEST_ID;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Reserved byte for future proposes\\n\"\n    \"     */\\n\"\n    \"    protected int $reserved = 0;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * The number of bytes in the contentData component of the record.\\n\"\n    \"     */\\n\"\n    \"    private int $contentLength = 0;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * The number of bytes in the paddingData component of the record.\\n\"\n    \"     */\\n\"\n    \"    private int $paddingLength = 0;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Binary data, between 0 and 65535 bytes of data, interpreted according to the record type.\\n\"\n    \"     */\\n\"\n    \"    private string $contentData = '';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Padding data, between 0 and 255 bytes of data, which are ignored.\\n\"\n    \"     */\\n\"\n    \"    private string $paddingData = '';\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Returns the binary message representation of record\\n\"\n    \"     */\\n\"\n    \"    final public function __toString(): string\\n\"\n    \"    {\\n\"\n    \"        $headerPacket = pack(\\n\"\n    \"            'CCnnCC',\\n\"\n    \"            $this->version,\\n\"\n    \"            $this->type,\\n\"\n    \"            $this->requestId,\\n\"\n    \"            $this->contentLength,\\n\"\n    \"            $this->paddingLength,\\n\"\n    \"            $this->reserved\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $payloadPacket = $this->packPayload();\\n\"\n    \"        $paddingPacket = pack(\\\"a{$this->paddingLength}\\\", $this->paddingData);\\n\"\n    \"\\n\"\n    \"        return $headerPacket . $payloadPacket . $paddingPacket;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Unpacks the message from the binary data buffer\\n\"\n    \"     */\\n\"\n    \"    final public static function unpack(string $binaryData): static\\n\"\n    \"    {\\n\"\n    \"        /** @var static $self */\\n\"\n    \"        $self = (new \\\\ReflectionClass(static::class))->newInstanceWithoutConstructor();\\n\"\n    \"\\n\"\n    \"        /** @phpstan-var false|array{version: int, type: int, requestId: int, contentLength: int, paddingLength: int, reserved: int} */\\n\"\n    \"        $packet = unpack(FastCGI::HEADER_FORMAT, $binaryData);\\n\"\n    \"        if ($packet === false) {\\n\"\n    \"            throw new \\\\RuntimeException('Can not unpack data from the binary buffer');\\n\"\n    \"        }\\n\"\n    \"        [\\n\"\n    \"            $self->version,\\n\"\n    \"            $self->type,\\n\"\n    \"            $self->requestId,\\n\"\n    \"            $self->contentLength,\\n\"\n    \"            $self->paddingLength,\\n\"\n    \"            $self->reserved,\\n\"\n    \"        ] = array_values($packet);\\n\"\n    \"\\n\"\n    \"        $payload = substr($binaryData, FastCGI::HEADER_LEN);\\n\"\n    \"        self::unpackPayload($self, $payload);\\n\"\n    \"        if (static::class !== self::class && $self->contentLength > 0) {\\n\"\n    \"            static::unpackPayload($self, $payload);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return $self;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Sets the content data and adjusts the length fields\\n\"\n    \"     *\\n\"\n    \"     * @return static\\n\"\n    \"     */\\n\"\n    \"    public function setContentData(string $data): self\\n\"\n    \"    {\\n\"\n    \"        $this->contentLength = strlen($data);\\n\"\n    \"        if ($this->contentLength > FastCGI::MAX_CONTENT_LENGTH) {\\n\"\n    \"            $this->contentLength = FastCGI::MAX_CONTENT_LENGTH;\\n\"\n    \"            $this->contentData   = substr($data, 0, FastCGI::MAX_CONTENT_LENGTH);\\n\"\n    \"        } else {\\n\"\n    \"            $this->contentData = $data;\\n\"\n    \"        }\\n\"\n    \"        $extraLength         = $this->contentLength % 8;\\n\"\n    \"        $this->paddingLength = $extraLength ? (8 - $extraLength) : 0;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Returns the context data from the record\\n\"\n    \"     */\\n\"\n    \"    public function getContentData(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->contentData;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Returns the version of record\\n\"\n    \"     */\\n\"\n    \"    public function getVersion(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->version;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Returns record type\\n\"\n    \"     */\\n\"\n    \"    public function getType(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->type;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Returns request ID\\n\"\n    \"     */\\n\"\n    \"    public function getRequestId(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->requestId;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Sets request ID\\n\"\n    \"     *\\n\"\n    \"     * There should be only one unique ID for all active requests,\\n\"\n    \"     * use random number or preferably resetting auto-increment.\\n\"\n    \"     *\\n\"\n    \"     * @return static\\n\"\n    \"     */\\n\"\n    \"    public function setRequestId(int $requestId): self\\n\"\n    \"    {\\n\"\n    \"        $this->requestId = $requestId;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Returns the size of content length\\n\"\n    \"     */\\n\"\n    \"    final public function getContentLength(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->contentLength;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Returns the size of padding length\\n\"\n    \"     */\\n\"\n    \"    final public function getPaddingLength(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->paddingLength;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Method to unpack the payload for the record.\\n\"\n    \"     *\\n\"\n    \"     * NB: Default implementation will be always called\\n\"\n    \"     */\\n\"\n    \"    protected static function unpackPayload(self $self, string $binaryData): void\\n\"\n    \"    {\\n\"\n    \"        /** @phpstan-var false|array{contentData: string, paddingData: string} */\\n\"\n    \"        $payload = unpack(\\\"a{$self->contentLength}contentData/a{$self->paddingLength}paddingData\\\", $binaryData);\\n\"\n    \"        if ($payload === false) {\\n\"\n    \"            throw new \\\\RuntimeException('Can not unpack data from the binary buffer');\\n\"\n    \"        }\\n\"\n    \"        [\\n\"\n    \"            $self->contentData,\\n\"\n    \"            $self->paddingData,\\n\"\n    \"        ] = array_values($payload);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Implementation of packing the payload\\n\"\n    \"     */\\n\"\n    \"    protected function packPayload(): string\\n\"\n    \"    {\\n\"\n    \"        return pack(\\\"a{$this->contentLength}\\\", $this->contentData);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_record_params =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\FastCGI;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * Params request record\\n\"\n    \" */\\n\"\n    \"class Params extends Record\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * List of params\\n\"\n    \"     *\\n\"\n    \"     * @var string[]\\n\"\n    \"     * @phpstan-var array<string, string>\\n\"\n    \"     */\\n\"\n    \"    protected array $values = [];\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Constructs a param request\\n\"\n    \"     *\\n\"\n    \"     * @phpstan-param array<string, string> $values\\n\"\n    \"     */\\n\"\n    \"    public function __construct(array $values)\\n\"\n    \"    {\\n\"\n    \"        $this->type   = FastCGI::PARAMS;\\n\"\n    \"        $this->values = $values;\\n\"\n    \"        $this->setContentData($this->packPayload());\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Returns an associative list of parameters\\n\"\n    \"     *\\n\"\n    \"     * @phpstan-return array<string, string>\\n\"\n    \"     */\\n\"\n    \"    public function getValues(): array\\n\"\n    \"    {\\n\"\n    \"        return $this->values;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * {@inheritdoc}\\n\"\n    \"     * @param static $self\\n\"\n    \"     */\\n\"\n    \"    protected static function unpackPayload(Record $self, string $binaryData): void\\n\"\n    \"    {\\n\"\n    \"        assert($self instanceof self); // @phpstan-ignore function.alreadyNarrowedType,instanceof.alwaysTrue\\n\"\n    \"        $currentOffset = 0;\\n\"\n    \"        do {\\n\"\n    \"            /** @phpstan-var false|array{nameLengthHigh: int} */\\n\"\n    \"            $payload = unpack('CnameLengthHigh', $binaryData);\\n\"\n    \"            if ($payload === false) {\\n\"\n    \"                throw new \\\\RuntimeException('Can not unpack data from the binary buffer');\\n\"\n    \"            }\\n\"\n    \"            [$nameLengthHigh] = array_values($payload);\\n\"\n    \"            $isLongName       = ($nameLengthHigh >> 7 == 1);\\n\"\n    \"            $valueOffset      = $isLongName ? 4 : 1;\\n\"\n    \"\\n\"\n    \"            /** @phpstan-var false|array{valueLengthHigh: int} */\\n\"\n    \"            $payload = unpack('CvalueLengthHigh', substr($binaryData, $valueOffset));\\n\"\n    \"            if ($payload === false) {\\n\"\n    \"                throw new \\\\RuntimeException('Can not unpack data from the binary buffer');\\n\"\n    \"            }\\n\"\n    \"            [$valueLengthHigh] = array_values($payload);\\n\"\n    \"            $isLongValue       = ($valueLengthHigh >> 7 == 1);\\n\"\n    \"            $dataOffset        = $valueOffset + ($isLongValue ? 4 : 1);\\n\"\n    \"\\n\"\n    \"            $formatParts = [\\n\"\n    \"                $isLongName ? 'NnameLength' : 'CnameLength',\\n\"\n    \"                $isLongValue ? 'NvalueLength' : 'CvalueLength',\\n\"\n    \"            ];\\n\"\n    \"            $format      = join('/', $formatParts);\\n\"\n    \"\\n\"\n    \"            /** @phpstan-var false|array{nameLength: int, valueLength: int} */\\n\"\n    \"            $payload = unpack($format, $binaryData);\\n\"\n    \"            if ($payload === false) {\\n\"\n    \"                throw new \\\\RuntimeException('Can not unpack data from the binary buffer');\\n\"\n    \"            }\\n\"\n    \"            [$nameLength, $valueLength] = array_values($payload);\\n\"\n    \"\\n\"\n    \"            // Clear top bit for long record\\n\"\n    \"            $nameLength &= ($isLongName ? 0x7FFFFFFF : 0x7F);\\n\"\n    \"            $valueLength &= ($isLongValue ? 0x7FFFFFFF : 0x7F);\\n\"\n    \"\\n\"\n    \"            /** @phpstan-var false|array{nameData: string, valueData: string} */\\n\"\n    \"            $payload = unpack(\\n\"\n    \"                \\\"a{$nameLength}nameData/a{$valueLength}valueData\\\",\\n\"\n    \"                substr($binaryData, $dataOffset)\\n\"\n    \"            );\\n\"\n    \"            if ($payload === false) {\\n\"\n    \"                throw new \\\\RuntimeException('Can not unpack data from the binary buffer');\\n\"\n    \"            }\\n\"\n    \"            [$nameData, $valueData] = array_values($payload);\\n\"\n    \"\\n\"\n    \"            $self->values[$nameData] = $valueData;\\n\"\n    \"\\n\"\n    \"            $keyValueLength = $dataOffset + $nameLength + $valueLength;\\n\"\n    \"            $binaryData     = substr($binaryData, $keyValueLength);\\n\"\n    \"            $currentOffset += $keyValueLength;\\n\"\n    \"        } while ($currentOffset < $self->getContentLength());\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * {@inheritdoc}\\n\"\n    \"     */\\n\"\n    \"    protected function packPayload(): string\\n\"\n    \"    {\\n\"\n    \"        $payload = '';\\n\"\n    \"        foreach ($this->values as $nameData => $valueData) {\\n\"\n    \"            if ($valueData === null) { // @phpstan-ignore identical.alwaysFalse\\n\"\n    \"                continue;\\n\"\n    \"            }\\n\"\n    \"            $nameLength  = strlen($nameData);\\n\"\n    \"            $valueLength = strlen((string) $valueData);\\n\"\n    \"            $isLongName  = $nameLength > 127;\\n\"\n    \"            $isLongValue = $valueLength > 127;\\n\"\n    \"            $formatParts = [\\n\"\n    \"                $isLongName ? 'N' : 'C',\\n\"\n    \"                $isLongValue ? 'N' : 'C',\\n\"\n    \"                \\\"a{$nameLength}\\\",\\n\"\n    \"                \\\"a{$valueLength}\\\",\\n\"\n    \"            ];\\n\"\n    \"\\n\"\n    \"            $format = join('', $formatParts);\\n\"\n    \"\\n\"\n    \"            $payload .= pack(\\n\"\n    \"                $format,\\n\"\n    \"                $isLongName ? ($nameLength | 0x80000000) : $nameLength,\\n\"\n    \"                $isLongValue ? ($valueLength | 0x80000000) : $valueLength,\\n\"\n    \"                $nameData,\\n\"\n    \"                $valueData\\n\"\n    \"            );\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return $payload;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_record_abort_request =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\FastCGI;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * The Web server sends a FCGI_ABORT_REQUEST record to abort a request\\n\"\n    \" */\\n\"\n    \"class AbortRequest extends Record\\n\"\n    \"{\\n\"\n    \"    public function __construct(int $requestId)\\n\"\n    \"    {\\n\"\n    \"        $this->type = FastCGI::ABORT_REQUEST;\\n\"\n    \"        $this->setRequestId($requestId);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_record_begin_request =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\FastCGI;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * The Web server sends a FCGI_BEGIN_REQUEST record to start a request.\\n\"\n    \" */\\n\"\n    \"class BeginRequest extends Record\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * The role component sets the role the Web server expects the application to play.\\n\"\n    \"     * The currently-defined roles are:\\n\"\n    \"     *   FCGI_RESPONDER\\n\"\n    \"     *   FCGI_AUTHORIZER\\n\"\n    \"     *   FCGI_FILTER\\n\"\n    \"     */\\n\"\n    \"    protected int $role = FastCGI::UNKNOWN_ROLE;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * The flags component contains a bit that controls connection shutdown.\\n\"\n    \"     *\\n\"\n    \"     * flags & FCGI_KEEP_CONN:\\n\"\n    \"     *   If zero, the application closes the connection after responding to this request.\\n\"\n    \"     *   If not zero, the application does not close the connection after responding to this request;\\n\"\n    \"     *   the Web server retains responsibility for the connection.\\n\"\n    \"     */\\n\"\n    \"    protected int $flags;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Reserved data, 5 bytes maximum\\n\"\n    \"     */\\n\"\n    \"    protected string $reserved1;\\n\"\n    \"\\n\"\n    \"    public function __construct(int $role = FastCGI::UNKNOWN_ROLE, int $flags = 0, string $reserved = '')\\n\"\n    \"    {\\n\"\n    \"        $this->type      = FastCGI::BEGIN_REQUEST;\\n\"\n    \"        $this->role      = $role;\\n\"\n    \"        $this->flags     = $flags;\\n\"\n    \"        $this->reserved1 = $reserved;\\n\"\n    \"        $this->setContentData($this->packPayload());\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Returns the role\\n\"\n    \"     *\\n\"\n    \"     * The role component sets the role the Web server expects the application to play.\\n\"\n    \"     * The currently-defined roles are:\\n\"\n    \"     *   FCGI_RESPONDER\\n\"\n    \"     *   FCGI_AUTHORIZER\\n\"\n    \"     *   FCGI_FILTER\\n\"\n    \"     */\\n\"\n    \"    public function getRole(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->role;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Returns the flags\\n\"\n    \"     *\\n\"\n    \"     * The flags component contains a bit that controls connection shutdown.\\n\"\n    \"     *\\n\"\n    \"     * flags & FCGI_KEEP_CONN:\\n\"\n    \"     *   If zero, the application closes the connection after responding to this request.\\n\"\n    \"     *   If not zero, the application does not close the connection after responding to this request;\\n\"\n    \"     *   the Web server retains responsibility for the connection.\\n\"\n    \"     */\\n\"\n    \"    public function getFlags(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->flags;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * {@inheritdoc}\\n\"\n    \"     * @param static $self\\n\"\n    \"     */\\n\"\n    \"    protected static function unpackPayload(Record $self, string $binaryData): void\\n\"\n    \"    {\\n\"\n    \"        assert($self instanceof self); // @phpstan-ignore function.alreadyNarrowedType,instanceof.alwaysTrue\\n\"\n    \"\\n\"\n    \"        /** @phpstan-var false|array{role: int, flags: int, reserved: string} */\\n\"\n    \"        $payload = unpack('nrole/Cflags/a5reserved', $binaryData);\\n\"\n    \"        if ($payload === false) {\\n\"\n    \"            throw new \\\\RuntimeException('Can not unpack data from the binary buffer');\\n\"\n    \"        }\\n\"\n    \"        [\\n\"\n    \"            $self->role,\\n\"\n    \"            $self->flags,\\n\"\n    \"            $self->reserved1,\\n\"\n    \"        ] = array_values($payload);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /** {@inheritdoc} */\\n\"\n    \"    protected function packPayload(): string\\n\"\n    \"    {\\n\"\n    \"        return pack(\\n\"\n    \"            'nCa5',\\n\"\n    \"            $this->role,\\n\"\n    \"            $this->flags,\\n\"\n    \"            $this->reserved1\\n\"\n    \"        );\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_record_data =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\FastCGI;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * Data binary stream\\n\"\n    \" *\\n\"\n    \" * FCGI_DATA is a second stream record type used to send additional data to the application.\\n\"\n    \" */\\n\"\n    \"class Data extends Record\\n\"\n    \"{\\n\"\n    \"    public function __construct(string $contentData)\\n\"\n    \"    {\\n\"\n    \"        $this->type = FastCGI::DATA;\\n\"\n    \"        $this->setContentData($contentData);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_record_end_request =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\FastCGI;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * The application sends a FCGI_END_REQUEST record to terminate a request, either because the application\\n\"\n    \" * has processed the request or because the application has rejected the request.\\n\"\n    \" */\\n\"\n    \"class EndRequest extends Record\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * The appStatus component is an application-level status code. Each role documents its usage of appStatus.\\n\"\n    \"     */\\n\"\n    \"    protected int $appStatus = 0;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * The protocolStatus component is a protocol-level status code.\\n\"\n    \"     *\\n\"\n    \"     * The possible protocolStatus values are:\\n\"\n    \"     *   FCGI_REQUEST_COMPLETE: normal end of request.\\n\"\n    \"     *   FCGI_CANT_MPX_CONN: rejecting a new request.\\n\"\n    \"     *      This happens when a Web server sends concurrent requests over one connection to an application that is\\n\"\n    \"     *      designed to process one request at a time per connection.\\n\"\n    \"     *   FCGI_OVERLOADED: rejecting a new request.\\n\"\n    \"     *      This happens when the application runs out of some resource, e.g. database connections.\\n\"\n    \"     *   FCGI_UNKNOWN_ROLE: rejecting a new request.\\n\"\n    \"     *      This happens when the Web server has specified a role that is unknown to the application.\\n\"\n    \"     */\\n\"\n    \"    protected int $protocolStatus = FastCGI::REQUEST_COMPLETE;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Reserved data, 3 bytes maximum\\n\"\n    \"     */\\n\"\n    \"    protected string $reserved1;\\n\"\n    \"\\n\"\n    \"    public function __construct(int $protocolStatus = FastCGI::REQUEST_COMPLETE, int $appStatus = 0, string $reserved = '')\\n\"\n    \"    {\\n\"\n    \"        $this->type           = FastCGI::END_REQUEST;\\n\"\n    \"        $this->protocolStatus = $protocolStatus;\\n\"\n    \"        $this->appStatus      = $appStatus;\\n\"\n    \"        $this->reserved1      = $reserved;\\n\"\n    \"        $this->setContentData($this->packPayload());\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Returns app status\\n\"\n    \"     *\\n\"\n    \"     * The appStatus component is an application-level status code. Each role documents its usage of appStatus.\\n\"\n    \"     */\\n\"\n    \"    public function getAppStatus(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->appStatus;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Returns the protocol status\\n\"\n    \"     *\\n\"\n    \"     * The possible protocolStatus values are:\\n\"\n    \"     *   FCGI_REQUEST_COMPLETE: normal end of request.\\n\"\n    \"     *   FCGI_CANT_MPX_CONN: rejecting a new request.\\n\"\n    \"     *      This happens when a Web server sends concurrent requests over one connection to an application that is\\n\"\n    \"     *      designed to process one request at a time per connection.\\n\"\n    \"     *   FCGI_OVERLOADED: rejecting a new request.\\n\"\n    \"     *      This happens when the application runs out of some resource, e.g. database connections.\\n\"\n    \"     *   FCGI_UNKNOWN_ROLE: rejecting a new request.\\n\"\n    \"     *      This happens when the Web server has specified a role that is unknown to the application.\\n\"\n    \"     */\\n\"\n    \"    public function getProtocolStatus(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->protocolStatus;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * {@inheritdoc}\\n\"\n    \"     * @param static $self\\n\"\n    \"     */\\n\"\n    \"    protected static function unpackPayload(Record $self, string $binaryData): void\\n\"\n    \"    {\\n\"\n    \"        assert($self instanceof self); // @phpstan-ignore function.alreadyNarrowedType,instanceof.alwaysTrue\\n\"\n    \"\\n\"\n    \"        /** @phpstan-var false|array{appStatus: int, protocolStatus: int, reserved: string} */\\n\"\n    \"        $payload = unpack('NappStatus/CprotocolStatus/a3reserved', $binaryData);\\n\"\n    \"        if ($payload === false) {\\n\"\n    \"            throw new \\\\RuntimeException('Can not unpack data from the binary buffer');\\n\"\n    \"        }\\n\"\n    \"        [\\n\"\n    \"            $self->appStatus,\\n\"\n    \"            $self->protocolStatus,\\n\"\n    \"            $self->reserved1,\\n\"\n    \"        ] = array_values($payload);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /** {@inheritdoc} */\\n\"\n    \"    protected function packPayload(): string\\n\"\n    \"    {\\n\"\n    \"        return pack(\\n\"\n    \"            'NCa3',\\n\"\n    \"            $this->appStatus,\\n\"\n    \"            $this->protocolStatus,\\n\"\n    \"            $this->reserved1\\n\"\n    \"        );\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_record_get_values =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\FastCGI;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * GetValues API\\n\"\n    \" *\\n\"\n    \" * The Web server can query specific variables within the application.\\n\"\n    \" * The server will typically perform a query on application startup in order to to automate certain aspects of\\n\"\n    \" * system configuration.\\n\"\n    \" *\\n\"\n    \" * The application responds by sending a record {FCGI_GET_VALUES_RESULT, 0, ...} with the values supplied.\\n\"\n    \" * If the application doesn't understand a variable name that was included in the query, it omits that name from\\n\"\n    \" * the response.\\n\"\n    \" *\\n\"\n    \" * FCGI_GET_VALUES is designed to allow an open-ended set of variables.\\n\"\n    \" *\\n\"\n    \" * The initial set provides information to help the server perform application and connection management:\\n\"\n    \" *   FCGI_MAX_CONNS:  The maximum number of concurrent transport connections this application will accept,\\n\"\n    \" *                    e.g. \\\"1\\\" or \\\"10\\\".\\n\"\n    \" *   FCGI_MAX_REQS:   The maximum number of concurrent requests this application will accept, e.g. \\\"1\\\" or \\\"50\\\".\\n\"\n    \" *   FCGI_MPXS_CONNS: \\\"0\\\" if this application does not multiplex connections (i.e. handle concurrent requests\\n\"\n    \" *                    over each connection), \\\"1\\\" otherwise.\\n\"\n    \" */\\n\"\n    \"class GetValues extends Params\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * Constructs a request\\n\"\n    \"     *\\n\"\n    \"     * @param array $keys List of keys to receive\\n\"\n    \"     *\\n\"\n    \"     * @phpstan-param list<string> $keys\\n\"\n    \"     */\\n\"\n    \"    public function __construct(array $keys)\\n\"\n    \"    {\\n\"\n    \"        parent::__construct(array_fill_keys($keys, ''));\\n\"\n    \"        $this->type = FastCGI::GET_VALUES;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_record_get_values_result =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\FastCGI;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * GetValues API\\n\"\n    \" *\\n\"\n    \" * The Web server can query specific variables within the application.\\n\"\n    \" * The server will typically perform a query on application startup in order to to automate certain aspects of\\n\"\n    \" * system configuration.\\n\"\n    \" *\\n\"\n    \" * The application responds by sending a record {FCGI_GET_VALUES_RESULT, 0, ...} with the values supplied.\\n\"\n    \" * If the application doesn't understand a variable name that was included in the query, it omits that name from\\n\"\n    \" * the response.\\n\"\n    \" *\\n\"\n    \" * FCGI_GET_VALUES is designed to allow an open-ended set of variables.\\n\"\n    \" *\\n\"\n    \" * The initial set provides information to help the server perform application and connection management:\\n\"\n    \" *   FCGI_MAX_CONNS:  The maximum number of concurrent transport connections this application will accept,\\n\"\n    \" *                    e.g. \\\"1\\\" or \\\"10\\\".\\n\"\n    \" *   FCGI_MAX_REQS:   The maximum number of concurrent requests this application will accept, e.g. \\\"1\\\" or \\\"50\\\".\\n\"\n    \" *   FCGI_MPXS_CONNS: \\\"0\\\" if this application does not multiplex connections (i.e. handle concurrent requests\\n\"\n    \" *                    over each connection), \\\"1\\\" otherwise.\\n\"\n    \" */\\n\"\n    \"class GetValuesResult extends Params\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * Constructs a param request\\n\"\n    \"     *\\n\"\n    \"     * @phpstan-param array<string, string> $values\\n\"\n    \"     */\\n\"\n    \"    public function __construct(array $values)\\n\"\n    \"    {\\n\"\n    \"        parent::__construct($values);\\n\"\n    \"        $this->type = FastCGI::GET_VALUES_RESULT;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_record_stdin =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\FastCGI;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * Stdin binary stream\\n\"\n    \" *\\n\"\n    \" * FCGI_STDIN is a stream record type used in sending arbitrary data from the Web server to the application\\n\"\n    \" */\\n\"\n    \"class Stdin extends Record\\n\"\n    \"{\\n\"\n    \"    public function __construct(string $contentData)\\n\"\n    \"    {\\n\"\n    \"        $this->type = FastCGI::STDIN;\\n\"\n    \"        $this->setContentData($contentData);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_record_stdout =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\FastCGI;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * Stdout binary stream\\n\"\n    \" *\\n\"\n    \" * FCGI_STDOUT is a stream record for sending arbitrary data from the application to the Web server\\n\"\n    \" */\\n\"\n    \"class Stdout extends Record\\n\"\n    \"{\\n\"\n    \"    public function __construct(string $contentData)\\n\"\n    \"    {\\n\"\n    \"        $this->type = FastCGI::STDOUT;\\n\"\n    \"        $this->setContentData($contentData);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_record_stderr =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\FastCGI;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * Stderr binary stream\\n\"\n    \" *\\n\"\n    \" * FCGI_STDERR is a stream record for sending arbitrary data from the application to the Web server\\n\"\n    \" */\\n\"\n    \"class Stderr extends Record\\n\"\n    \"{\\n\"\n    \"    public function __construct(string $contentData)\\n\"\n    \"    {\\n\"\n    \"        $this->type = FastCGI::STDERR;\\n\"\n    \"        $this->setContentData($contentData);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_record_unknown_type =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\FastCGI;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * Record for unknown queries\\n\"\n    \" *\\n\"\n    \" * The set of management record types is likely to grow in future versions of this protocol.\\n\"\n    \" * To provide for this evolution, the protocol includes the FCGI_UNKNOWN_TYPE management record.\\n\"\n    \" * When an application receives a management record whose type T it does not understand, the application responds\\n\"\n    \" * with {FCGI_UNKNOWN_TYPE, 0, {T}}.\\n\"\n    \" */\\n\"\n    \"class UnknownType extends Record\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * Type of the unrecognized management record.\\n\"\n    \"     */\\n\"\n    \"    protected int $type1;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Reserved data, 7 bytes maximum\\n\"\n    \"     */\\n\"\n    \"    protected string $reserved1;\\n\"\n    \"\\n\"\n    \"    public function __construct(int $type, string $reserved = '')\\n\"\n    \"    {\\n\"\n    \"        $this->type      = FastCGI::UNKNOWN_TYPE;\\n\"\n    \"        $this->type1     = $type;\\n\"\n    \"        $this->reserved1 = $reserved;\\n\"\n    \"        $this->setContentData($this->packPayload());\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Returns the unrecognized type\\n\"\n    \"     */\\n\"\n    \"    public function getUnrecognizedType(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->type1;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * {@inheritdoc}\\n\"\n    \"     * @param static $self\\n\"\n    \"     */\\n\"\n    \"    public static function unpackPayload(Record $self, string $binaryData): void\\n\"\n    \"    {\\n\"\n    \"        assert($self instanceof self); // @phpstan-ignore function.alreadyNarrowedType,instanceof.alwaysTrue\\n\"\n    \"\\n\"\n    \"        /** @phpstan-var false|array{type: int, reserved: string} */\\n\"\n    \"        $payload = unpack('Ctype/a7reserved', $binaryData);\\n\"\n    \"        if ($payload === false) {\\n\"\n    \"            throw new \\\\RuntimeException('Can not unpack data from the binary buffer');\\n\"\n    \"        }\\n\"\n    \"        [$self->type1, $self->reserved1] = array_values($payload);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * {@inheritdoc}\\n\"\n    \"     */\\n\"\n    \"    protected function packPayload(): string\\n\"\n    \"    {\\n\"\n    \"        return pack(\\n\"\n    \"            'Ca7',\\n\"\n    \"            $this->type1,\\n\"\n    \"            $this->reserved1\\n\"\n    \"        );\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_frame_parser =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\FastCGI;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\AbortRequest;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\BeginRequest;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\Data;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\EndRequest;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\GetValues;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\GetValuesResult;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\Params;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\Stderr;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\Stdin;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\Stdout;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\UnknownType;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * Utility class to simplify parsing of FastCGI protocol data.\\n\"\n    \" */\\n\"\n    \"class FrameParser\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * Mapping of constants to the classes\\n\"\n    \"     *\\n\"\n    \"     * @phpstan-var array<int, class-string>\\n\"\n    \"     */\\n\"\n    \"    protected static array $classMapping = [\\n\"\n    \"        FastCGI::BEGIN_REQUEST     => BeginRequest::class,\\n\"\n    \"        FastCGI::ABORT_REQUEST     => AbortRequest::class,\\n\"\n    \"        FastCGI::END_REQUEST       => EndRequest::class,\\n\"\n    \"        FastCGI::PARAMS            => Params::class,\\n\"\n    \"        FastCGI::STDIN             => Stdin::class,\\n\"\n    \"        FastCGI::STDOUT            => Stdout::class,\\n\"\n    \"        FastCGI::STDERR            => Stderr::class,\\n\"\n    \"        FastCGI::DATA              => Data::class,\\n\"\n    \"        FastCGI::GET_VALUES        => GetValues::class,\\n\"\n    \"        FastCGI::GET_VALUES_RESULT => GetValuesResult::class,\\n\"\n    \"        FastCGI::UNKNOWN_TYPE      => UnknownType::class,\\n\"\n    \"    ];\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Checks if the buffer contains a valid frame to parse\\n\"\n    \"     */\\n\"\n    \"    public static function hasFrame(string $binaryBuffer): bool\\n\"\n    \"    {\\n\"\n    \"        $bufferLength = strlen($binaryBuffer);\\n\"\n    \"        if ($bufferLength < FastCGI::HEADER_LEN) {\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        /** @phpstan-var false|array{version: int, type: int, requestId: int, contentLength: int, paddingLength: int} */\\n\"\n    \"        $fastInfo = unpack(FastCGI::HEADER_FORMAT, $binaryBuffer);\\n\"\n    \"        if ($fastInfo === false) {\\n\"\n    \"            throw new \\\\RuntimeException('Can not unpack data from the binary buffer');\\n\"\n    \"        }\\n\"\n    \"        if ($bufferLength < FastCGI::HEADER_LEN + $fastInfo['contentLength'] + $fastInfo['paddingLength']) {\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return true;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Parses a frame from the binary buffer\\n\"\n    \"     *\\n\"\n    \"     * @return Record One of the corresponding FastCGI record\\n\"\n    \"     */\\n\"\n    \"    public static function parseFrame(string &$binaryBuffer): Record\\n\"\n    \"    {\\n\"\n    \"        $bufferLength = strlen($binaryBuffer);\\n\"\n    \"        if ($bufferLength < FastCGI::HEADER_LEN) {\\n\"\n    \"            throw new \\\\RuntimeException('Not enough data in the buffer to parse');\\n\"\n    \"        }\\n\"\n    \"        /** @phpstan-var false|array{version: int, type: int, requestId: int, contentLength: int, paddingLength: int} */\\n\"\n    \"        $recordHeader = unpack(FastCGI::HEADER_FORMAT, $binaryBuffer);\\n\"\n    \"        if ($recordHeader === false) {\\n\"\n    \"            throw new \\\\RuntimeException('Can not unpack data from the binary buffer');\\n\"\n    \"        }\\n\"\n    \"        $recordType = $recordHeader['type'];\\n\"\n    \"        if (!isset(self::$classMapping[$recordType])) {\\n\"\n    \"            throw new \\\\DomainException(\\\"Invalid FastCGI record type {$recordType} received\\\");\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        /** @var Record $className */\\n\"\n    \"        $className = self::$classMapping[$recordType];\\n\"\n    \"        $record    = $className::unpack($binaryBuffer);\\n\"\n    \"\\n\"\n    \"        $offset       = FastCGI::HEADER_LEN + $record->getContentLength() + $record->getPaddingLength();\\n\"\n    \"        $binaryBuffer = substr($binaryBuffer, $offset);\\n\"\n    \"\\n\"\n    \"        return $record;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_message =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI;\\n\"\n    \"\\n\"\n    \"class Message\\n\"\n    \"{\\n\"\n    \"    protected array $params = [];\\n\"\n    \"\\n\"\n    \"    protected string $body = '';\\n\"\n    \"\\n\"\n    \"    protected string $error = '';\\n\"\n    \"\\n\"\n    \"    public function getParam(string $name): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params[$name] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withParam(string $name, string $value): static\\n\"\n    \"    {\\n\"\n    \"        $this->params[$name] = $value;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutParam(string $name): static\\n\"\n    \"    {\\n\"\n    \"        unset($this->params[$name]);\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getParams(): array\\n\"\n    \"    {\\n\"\n    \"        return $this->params;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withParams(array $params): static\\n\"\n    \"    {\\n\"\n    \"        $this->params = $params;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withAddedParams(array $params): static\\n\"\n    \"    {\\n\"\n    \"        $this->params = $params + $this->params;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getBody(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->body;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withBody(string|\\\\Stringable $body): self\\n\"\n    \"    {\\n\"\n    \"        $this->body = (string) $body;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getError(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->error;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withError(string $error): static\\n\"\n    \"    {\\n\"\n    \"        $this->error = $error;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_request =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\FastCGI;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\BeginRequest;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\Params;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\Stdin;\\n\"\n    \"\\n\"\n    \"class Request extends Message implements \\\\Stringable\\n\"\n    \"{\\n\"\n    \"    protected bool $keepConn = false;\\n\"\n    \"\\n\"\n    \"    public function __toString(): string\\n\"\n    \"    {\\n\"\n    \"        $body              = $this->getBody();\\n\"\n    \"        $beginRequestFrame = new BeginRequest(FastCGI::RESPONDER, $this->keepConn ? FastCGI::KEEP_CONN : 0);\\n\"\n    \"        $paramsFrame       = new Params($this->getParams());\\n\"\n    \"        $paramsEofFrame    = new Params([]);\\n\"\n    \"        if (empty($body)) {\\n\"\n    \"            $message = \\\"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}\\\";\\n\"\n    \"        } else {\\n\"\n    \"            $stdinList = [];\\n\"\n    \"            while (true) {\\n\"\n    \"                $stdinList[] = $stdin = new Stdin($body);\\n\"\n    \"                $stdinLength = $stdin->getContentLength();\\n\"\n    \"                if ($stdinLength === strlen($body)) {\\n\"\n    \"                    break;\\n\"\n    \"                }\\n\"\n    \"                $body = substr($body, $stdinLength);\\n\"\n    \"            }\\n\"\n    \"            $stdinList[] = new Stdin('');\\n\"\n    \"            $stdin       = implode('', $stdinList);\\n\"\n    \"            $message     = \\\"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}{$stdin}\\\";\\n\"\n    \"        }\\n\"\n    \"        return $message;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getKeepConn(): bool\\n\"\n    \"    {\\n\"\n    \"        return $this->keepConn;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withKeepConn(bool $keepConn): self\\n\"\n    \"    {\\n\"\n    \"        $this->keepConn = $keepConn;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_response =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\EndRequest;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\Stderr;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\Stdout;\\n\"\n    \"\\n\"\n    \"class Response extends Message\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * @param array<Stdout|Stderr|EndRequest> $records\\n\"\n    \"     */\\n\"\n    \"    public function __construct(array $records)\\n\"\n    \"    {\\n\"\n    \"        if (!static::verify($records)) {\\n\"\n    \"            throw new \\\\InvalidArgumentException('Bad records');\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $body = $error = '';\\n\"\n    \"        foreach ($records as $record) {\\n\"\n    \"            if ($record instanceof Stdout) {\\n\"\n    \"                if ($record->getContentLength() > 0) {\\n\"\n    \"                    $body .= $record->getContentData();\\n\"\n    \"                }\\n\"\n    \"            } elseif ($record instanceof Stderr) {\\n\"\n    \"                if ($record->getContentLength() > 0) {\\n\"\n    \"                    $error .= $record->getContentData();\\n\"\n    \"                }\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"        $this->withBody($body)->withError($error);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @param array<Stdout|Stderr|EndRequest> $records\\n\"\n    \"     */\\n\"\n    \"    protected static function verify(array $records): bool\\n\"\n    \"    {\\n\"\n    \"        return !empty($records) && $records[array_key_last($records)] instanceof EndRequest;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_http_request =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI;\\n\"\n    \"\\n\"\n    \"class HttpRequest extends Request\\n\"\n    \"{\\n\"\n    \"    protected array $params = [\\n\"\n    \"        'REQUEST_SCHEME'    => 'http',\\n\"\n    \"        'REQUEST_METHOD'    => 'GET',\\n\"\n    \"        'DOCUMENT_ROOT'     => '',\\n\"\n    \"        'SCRIPT_FILENAME'   => '',\\n\"\n    \"        'SCRIPT_NAME'       => '',\\n\"\n    \"        'DOCUMENT_URI'      => '/',\\n\"\n    \"        'REQUEST_URI'       => '/',\\n\"\n    \"        'QUERY_STRING'      => '',\\n\"\n    \"        'CONTENT_TYPE'      => 'text/plain',\\n\"\n    \"        'CONTENT_LENGTH'    => '0',\\n\"\n    \"        'GATEWAY_INTERFACE' => 'CGI/1.1',\\n\"\n    \"        'SERVER_PROTOCOL'   => 'HTTP/1.1',\\n\"\n    \"        'SERVER_SOFTWARE'   => 'swoole/' . SWOOLE_VERSION,\\n\"\n    \"        'REMOTE_ADDR'       => 'unknown',\\n\"\n    \"        'REMOTE_PORT'       => '0',\\n\"\n    \"        'SERVER_ADDR'       => 'unknown',\\n\"\n    \"        'SERVER_PORT'       => '0',\\n\"\n    \"        'SERVER_NAME'       => 'Swoole',\\n\"\n    \"        'REDIRECT_STATUS'   => '200',\\n\"\n    \"    ];\\n\"\n    \"\\n\"\n    \"    public function getScheme(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params['REQUEST_SCHEME'] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withScheme(string $scheme): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['REQUEST_SCHEME'] = $scheme;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutScheme(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['REQUEST_SCHEME']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getMethod(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params['REQUEST_METHOD'] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withMethod(string $method): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['REQUEST_METHOD'] = $method;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutMethod(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['REQUEST_METHOD']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getDocumentRoot(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params['DOCUMENT_ROOT'] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withDocumentRoot(string $documentRoot): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['DOCUMENT_ROOT'] = $documentRoot;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutDocumentRoot(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['DOCUMENT_ROOT']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getScriptFilename(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params['SCRIPT_FILENAME'] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withScriptFilename(string $scriptFilename): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['SCRIPT_FILENAME'] = $scriptFilename;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutScriptFilename(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['SCRIPT_FILENAME']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getScriptName(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params['SCRIPT_NAME'] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withScriptName(string $scriptName): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['SCRIPT_NAME'] = $scriptName;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutScriptName(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['SCRIPT_NAME']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withUri(string $uri): self\\n\"\n    \"    {\\n\"\n    \"        $info = parse_url($uri);\\n\"\n    \"        return $this->withRequestUri($uri)\\n\"\n    \"            ->withDocumentUri($info['path'] ?? '')\\n\"\n    \"            ->withQueryString($info['query'] ?? '')\\n\"\n    \"        ;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getDocumentUri(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params['DOCUMENT_URI'] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withDocumentUri(string $documentUri): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['DOCUMENT_URI'] = $documentUri;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutDocumentUri(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['DOCUMENT_URI']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getRequestUri(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params['REQUEST_URI'] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withRequestUri(string $requestUri): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['REQUEST_URI'] = $requestUri;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutRequestUri(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['REQUEST_URI']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withQuery($query): self\\n\"\n    \"    {\\n\"\n    \"        if (is_array($query)) {\\n\"\n    \"            $query = http_build_query($query);\\n\"\n    \"        }\\n\"\n    \"        return $this->withQueryString($query);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getQueryString(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params['QUERY_STRING'] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withQueryString(string $queryString): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['QUERY_STRING'] = $queryString;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutQueryString(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['QUERY_STRING']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getContentType(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params['CONTENT_TYPE'] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withContentType(string $contentType): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['CONTENT_TYPE'] = $contentType;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutContentType(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['CONTENT_TYPE']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getContentLength(): ?int\\n\"\n    \"    {\\n\"\n    \"        return isset($this->params['CONTENT_LENGTH']) ? (int) $this->params['CONTENT_LENGTH'] : null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withContentLength(int $contentLength): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['CONTENT_LENGTH'] = (string) $contentLength;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutContentLength(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['CONTENT_LENGTH']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getGatewayInterface(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params['GATEWAY_INTERFACE'] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withGatewayInterface(string $gatewayInterface): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['GATEWAY_INTERFACE'] = $gatewayInterface;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutGatewayInterface(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['GATEWAY_INTERFACE']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getServerProtocol(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params['SERVER_PROTOCOL'] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withServerProtocol(string $serverProtocol): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['SERVER_PROTOCOL'] = $serverProtocol;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutServerProtocol(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['SERVER_PROTOCOL']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withProtocolVersion(string $protocolVersion): self\\n\"\n    \"    {\\n\"\n    \"        if (!is_numeric($protocolVersion)) {\\n\"\n    \"            throw new \\\\InvalidArgumentException('Protocol version must be numeric');\\n\"\n    \"        }\\n\"\n    \"        $this->params['SERVER_PROTOCOL'] = \\\"HTTP/{$protocolVersion}\\\";\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getServerSoftware(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params['SERVER_SOFTWARE'] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withServerSoftware(string $serverSoftware): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['SERVER_SOFTWARE'] = $serverSoftware;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutServerSoftware(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['SERVER_SOFTWARE']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getRemoteAddr(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params['REMOTE_ADDR'] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withRemoteAddr(string $remoteAddr): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['REMOTE_ADDR'] = $remoteAddr;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutRemoteAddr(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['REMOTE_ADDR']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getRemotePort(): ?int\\n\"\n    \"    {\\n\"\n    \"        return isset($this->params['REMOTE_PORT']) ? (int) $this->params['REMOTE_PORT'] : null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withRemotePort(int $remotePort): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['REMOTE_PORT'] = (string) $remotePort;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutRemotePort(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['REMOTE_PORT']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getServerAddr(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params['SERVER_ADDR'] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withServerAddr(string $serverAddr): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['SERVER_ADDR'] = $serverAddr;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutServerAddr(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['SERVER_ADDR']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getServerPort(): ?int\\n\"\n    \"    {\\n\"\n    \"        return isset($this->params['SERVER_PORT']) ? (int) $this->params['SERVER_PORT'] : null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withServerPort(int $serverPort): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['SERVER_PORT'] = (string) $serverPort;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutServerPort(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['SERVER_PORT']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getServerName(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params['SERVER_NAME'] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withServerName(string $serverName): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['SERVER_NAME'] = $serverName;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutServerName(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['SERVER_NAME']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getRedirectStatus(): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params['REDIRECT_STATUS'] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withRedirectStatus(string $redirectStatus): self\\n\"\n    \"    {\\n\"\n    \"        $this->params['REDIRECT_STATUS'] = $redirectStatus;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutRedirectStatus(): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params['REDIRECT_STATUS']);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getHeader(string $name): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params[static::convertHeaderNameToParamName($name)] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withHeader(string $name, string $value): self\\n\"\n    \"    {\\n\"\n    \"        $this->params[static::convertHeaderNameToParamName($name)] = $value;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutHeader(string $name): void\\n\"\n    \"    {\\n\"\n    \"        unset($this->params[static::convertHeaderNameToParamName($name)]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getHeaders(): array\\n\"\n    \"    {\\n\"\n    \"        $headers = [];\\n\"\n    \"        foreach ($this->params as $name => $value) {\\n\"\n    \"            if (str_starts_with($name, 'HTTP_')) {\\n\"\n    \"                $headers[static::convertParamNameToHeaderName($name)] = $value;\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"        return $headers;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withHeaders(array $headers): self\\n\"\n    \"    {\\n\"\n    \"        foreach ($headers as $name => $value) {\\n\"\n    \"            $this->withHeader($name, $value);\\n\"\n    \"        }\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withBody(array|string|\\\\Stringable $body): self\\n\"\n    \"    {\\n\"\n    \"        if (is_array($body)) {\\n\"\n    \"            $body = http_build_query($body);\\n\"\n    \"            $this->withContentType('application/x-www-form-urlencoded');\\n\"\n    \"        }\\n\"\n    \"        parent::withBody($body);\\n\"\n    \"\\n\"\n    \"        return $this->withContentLength(strlen($body));\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    protected static function convertHeaderNameToParamName(string $name)\\n\"\n    \"    {\\n\"\n    \"        return 'HTTP_' . str_replace('-', '_', strtoupper($name));\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    protected static function convertParamNameToHeaderName(string $name)\\n\"\n    \"    {\\n\"\n    \"        return ucwords(str_replace('_', '-', substr($name, strlen('HTTP_'))), '-');\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_fast_cgi_http_response =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\FastCGI;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\EndRequest;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\Stderr;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\Stdout;\\n\"\n    \"use Swoole\\\\Http\\\\Status;\\n\"\n    \"\\n\"\n    \"class HttpResponse extends Response\\n\"\n    \"{\\n\"\n    \"    /** @var int */\\n\"\n    \"    protected $statusCode;\\n\"\n    \"\\n\"\n    \"    /** @var string */\\n\"\n    \"    protected $reasonPhrase;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @var array<string, string>\\n\"\n    \"     */\\n\"\n    \"    protected array $headers = [];\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @var array<string, string>\\n\"\n    \"     */\\n\"\n    \"    protected array $headersMap = [];\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @var array<string>\\n\"\n    \"     */\\n\"\n    \"    protected array $setCookieHeaderLines = [];\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @param array<Stdout|Stderr|EndRequest> $records\\n\"\n    \"     */\\n\"\n    \"    public function __construct(array $records = [])\\n\"\n    \"    {\\n\"\n    \"        parent::__construct($records);\\n\"\n    \"        $body = $this->getBody();\\n\"\n    \"        if (strlen($body) === 0) {\\n\"\n    \"            return;\\n\"\n    \"        }\\n\"\n    \"        $array = explode(\\\"\\\\r\\\\n\\\\r\\\\n\\\", $body, 2); // An array that contains the HTTP headers and the body.\\n\"\n    \"        if (count($array) != 2) {\\n\"\n    \"            $this->withStatusCode(Status::BAD_GATEWAY)->withReasonPhrase('Invalid FastCGI Response')->withError($body);\\n\"\n    \"            return;\\n\"\n    \"        }\\n\"\n    \"        $headers = explode(\\\"\\\\r\\\\n\\\", $array[0]);\\n\"\n    \"        $body    = $array[1];\\n\"\n    \"        foreach ($headers as $header) {\\n\"\n    \"            $array = explode(':', $header, 2); // An array that contains the name and the value of an HTTP header.\\n\"\n    \"            if (count($array) != 2) {\\n\"\n    \"                continue; // Invalid HTTP header? Ignore it!\\n\"\n    \"            }\\n\"\n    \"            $name  = trim($array[0]);\\n\"\n    \"            $value = trim($array[1]);\\n\"\n    \"            if (strcasecmp($name, 'Status') === 0) {\\n\"\n    \"                $array        = explode(' ', $value, 2); // An array that contains the status code (and the reason phrase).\\n\"\n    \"                $statusCode   = $array[0];\\n\"\n    \"                $reasonPhrase = $array[1] ?? null;\\n\"\n    \"            } elseif (strcasecmp($name, 'Set-Cookie') === 0) {\\n\"\n    \"                $this->withSetCookieHeaderLine($value);\\n\"\n    \"            } else {\\n\"\n    \"                $this->withHeader($name, $value);\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"        $statusCode   = (int) ($statusCode ?? Status::OK);\\n\"\n    \"        $reasonPhrase = $reasonPhrase ?? Status::getReasonPhrase($statusCode);\\n\"\n    \"        $this->withStatusCode($statusCode)->withReasonPhrase($reasonPhrase);\\n\"\n    \"        $this->withBody($body);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getStatusCode(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->statusCode;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withStatusCode(int $statusCode): self\\n\"\n    \"    {\\n\"\n    \"        $this->statusCode = $statusCode;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getReasonPhrase(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->reasonPhrase;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withReasonPhrase(string $reasonPhrase): self\\n\"\n    \"    {\\n\"\n    \"        $this->reasonPhrase = $reasonPhrase;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getHeader(string $name): ?string\\n\"\n    \"    {\\n\"\n    \"        $name = $this->headersMap[strtolower($name)] ?? null;\\n\"\n    \"        return $name ? $this->headers[$name] : null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return array<string, string>\\n\"\n    \"     */\\n\"\n    \"    public function getHeaders(): array\\n\"\n    \"    {\\n\"\n    \"        return $this->headers;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withHeader(string $name, string $value): self\\n\"\n    \"    {\\n\"\n    \"        $this->headers[$name]                = $value;\\n\"\n    \"        $this->headersMap[strtolower($name)] = $name;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @param array<string, string> $headers\\n\"\n    \"     */\\n\"\n    \"    public function withHeaders(array $headers): self\\n\"\n    \"    {\\n\"\n    \"        foreach ($headers as $name => $value) {\\n\"\n    \"            $this->withHeader($name, $value);\\n\"\n    \"        }\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return array<string>\\n\"\n    \"     */\\n\"\n    \"    public function getSetCookieHeaderLines(): array\\n\"\n    \"    {\\n\"\n    \"        return $this->setCookieHeaderLines;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withSetCookieHeaderLine(string $value): self\\n\"\n    \"    {\\n\"\n    \"        $this->setCookieHeaderLines[] = $value;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_coroutine_fast_cgi_client =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Coroutine\\\\FastCGI;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Constant;\\n\"\n    \"use Swoole\\\\Coroutine\\\\FastCGI\\\\Client\\\\Exception;\\n\"\n    \"use Swoole\\\\Coroutine\\\\Socket;\\n\"\n    \"use Swoole\\\\FastCGI\\\\FrameParser;\\n\"\n    \"use Swoole\\\\FastCGI\\\\HttpRequest;\\n\"\n    \"use Swoole\\\\FastCGI\\\\HttpResponse;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Record\\\\EndRequest;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Request;\\n\"\n    \"use Swoole\\\\FastCGI\\\\Response;\\n\"\n    \"\\n\"\n    \"class Client\\n\"\n    \"{\\n\"\n    \"    protected int $af;\\n\"\n    \"\\n\"\n    \"    protected string $host;\\n\"\n    \"\\n\"\n    \"    protected int $port;\\n\"\n    \"\\n\"\n    \"    protected bool $ssl;\\n\"\n    \"\\n\"\n    \"    protected ?Socket $socket;\\n\"\n    \"\\n\"\n    \"    public function __construct(string $host, int $port = 0, bool $ssl = false)\\n\"\n    \"    {\\n\"\n    \"        if (stripos($host, 'unix:/') === 0) {\\n\"\n    \"            $this->af = AF_UNIX;\\n\"\n    \"            $host     = '/' . ltrim(substr($host, strlen('unix:/')), '/');\\n\"\n    \"            $port     = 0;\\n\"\n    \"        } elseif (str_contains($host, ':')) {\\n\"\n    \"            $this->af = AF_INET6;\\n\"\n    \"        } else {\\n\"\n    \"            $this->af = AF_INET;\\n\"\n    \"        }\\n\"\n    \"        $this->host = $host;\\n\"\n    \"        $this->port = $port;\\n\"\n    \"        $this->ssl  = $ssl;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return ($request is HttpRequest ? HttpResponse : Response)\\n\"\n    \"     * @throws Exception\\n\"\n    \"     */\\n\"\n    \"    public function execute(Request $request, float $timeout = -1): Response\\n\"\n    \"    {\\n\"\n    \"        if (!isset($this->socket)) {\\n\"\n    \"            $this->socket = $socket = new Socket($this->af, SOCK_STREAM, IPPROTO_IP);\\n\"\n    \"            $socket->setProtocol([\\n\"\n    \"                Constant::OPTION_OPEN_SSL              => $this->ssl,\\n\"\n    \"                Constant::OPTION_OPEN_FASTCGI_PROTOCOL => true,\\n\"\n    \"            ]);\\n\"\n    \"            if (!$socket->connect($this->host, $this->port, $timeout)) {\\n\"\n    \"                $this->ioException();\\n\"\n    \"            }\\n\"\n    \"        } else {\\n\"\n    \"            $socket = $this->socket;\\n\"\n    \"        }\\n\"\n    \"        $sendData = (string) $request;\\n\"\n    \"        if ($socket->sendAll($sendData) !== strlen($sendData)) {\\n\"\n    \"            $this->ioException();\\n\"\n    \"        }\\n\"\n    \"        $records = [];\\n\"\n    \"        while (true) {\\n\"\n    \"            $recvData = $socket->recvPacket($timeout);\\n\"\n    \"            if (!$recvData) {\\n\"\n    \"                if ($recvData === '') {\\n\"\n    \"                    $this->ioException(SOCKET_ECONNRESET);\\n\"\n    \"                }\\n\"\n    \"                $this->ioException();\\n\"\n    \"            }\\n\"\n    \"            if (!FrameParser::hasFrame($recvData)) {\\n\"\n    \"                $this->ioException(SOCKET_EPROTO);\\n\"\n    \"            }\\n\"\n    \"\\n\"\n    \"            do {\\n\"\n    \"                $records[] = $record = FrameParser::parseFrame($recvData);\\n\"\n    \"            } while (strlen($recvData) !== 0);\\n\"\n    \"            if ($record instanceof EndRequest) {\\n\"\n    \"                if (!$request->getKeepConn()) {\\n\"\n    \"                    $this->socket->close();\\n\"\n    \"                    $this->socket = null;\\n\"\n    \"                }\\n\"\n    \"                // @phpstan-ignore argument.type,argument.type\\n\"\n    \"                return ($request instanceof HttpRequest) ? new HttpResponse($records) : new Response($records);\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        // Code execution should never reach here. However, we still put an exit() statement here for safe purpose.\\n\"\n    \"        exit(1); // @phpstan-ignore deadCode.unreachable\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function parseUrl(string $url): array\\n\"\n    \"    {\\n\"\n    \"        $url  = parse_url($url);\\n\"\n    \"        $host = $url['host'] ?? '';\\n\"\n    \"        $port = $url['port'] ?? 0;\\n\"\n    \"        if (empty($host)) {\\n\"\n    \"            $host = $url['path'] ?? '';\\n\"\n    \"            if (empty($host)) {\\n\"\n    \"                throw new \\\\InvalidArgumentException('Invalid url');\\n\"\n    \"            }\\n\"\n    \"            $host = \\\"unix:/{$host}\\\";\\n\"\n    \"        }\\n\"\n    \"        return [$host, $port];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function call(string $url, string $path, $data = '', float $timeout = -1): string\\n\"\n    \"    {\\n\"\n    \"        $client      = new Client(...static::parseUrl($url));\\n\"\n    \"        $pathInfo    = parse_url($path);\\n\"\n    \"        $path        = $pathInfo['path'] ?? '';\\n\"\n    \"        $root        = dirname($path);\\n\"\n    \"        $scriptName  = '/' . basename($path);\\n\"\n    \"        $documentUri = $scriptName;\\n\"\n    \"        $query       = $pathInfo['query'] ?? '';\\n\"\n    \"        $requestUri  = $query ? \\\"{$documentUri}?{$query}\\\" : $documentUri;\\n\"\n    \"        $request     = new HttpRequest();\\n\"\n    \"        $request->withDocumentRoot($root)\\n\"\n    \"            ->withScriptFilename($path)\\n\"\n    \"            ->withScriptName($documentUri)\\n\"\n    \"            ->withDocumentUri($documentUri)\\n\"\n    \"            ->withRequestUri($requestUri)\\n\"\n    \"            ->withQueryString($query)\\n\"\n    \"            ->withBody($data)\\n\"\n    \"            ->withMethod($request->getContentLength() === 0 ? 'GET' : 'POST')\\n\"\n    \"        ;\\n\"\n    \"        $response = $client->execute($request, $timeout);\\n\"\n    \"        return $response->getBody();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    protected function ioException(?int $errno = null): void\\n\"\n    \"    {\\n\"\n    \"        $socket = $this->socket;\\n\"\n    \"        if ($errno !== null) {\\n\"\n    \"            $socket->errCode = $errno;\\n\"\n    \"            $socket->errMsg  = swoole_strerror($errno);\\n\"\n    \"        }\\n\"\n    \"        $socket->close();\\n\"\n    \"        $this->socket = null;\\n\"\n    \"        throw new Exception($socket->errMsg, $socket->errCode);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_coroutine_fast_cgi_client_exception =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Coroutine\\\\FastCGI\\\\Client;\\n\"\n    \"\\n\"\n    \"class Exception extends \\\\Swoole\\\\Exception\\n\"\n    \"{\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_coroutine_fast_cgi_proxy =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Coroutine\\\\FastCGI;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\FastCGI\\\\HttpRequest;\\n\"\n    \"use Swoole\\\\FastCGI\\\\HttpResponse;\\n\"\n    \"use Swoole\\\\Http;\\n\"\n    \"use Swoole\\\\Http\\\\Request as SwooleHttpRequest;\\n\"\n    \"use Swoole\\\\Http\\\\Response as SwooleHttpResponse;\\n\"\n    \"\\n\"\n    \"class Proxy\\n\"\n    \"{\\n\"\n    \"    /* @var string */\\n\"\n    \"    protected $host;\\n\"\n    \"\\n\"\n    \"    /* @var int */\\n\"\n    \"    protected $port;\\n\"\n    \"\\n\"\n    \"    /* @var float */\\n\"\n    \"    protected $timeout = -1;\\n\"\n    \"\\n\"\n    \"    /* @var string */\\n\"\n    \"    protected $documentRoot;\\n\"\n    \"\\n\"\n    \"    /* @var bool */\\n\"\n    \"    protected $https = false;\\n\"\n    \"\\n\"\n    \"    /* @var string */\\n\"\n    \"    protected $index = 'index.php';\\n\"\n    \"\\n\"\n    \"    /* @var array */\\n\"\n    \"    protected $params = [];\\n\"\n    \"\\n\"\n    \"    /* @var null|callable */\\n\"\n    \"    protected $staticFileFilter;\\n\"\n    \"\\n\"\n    \"    public function __construct(string $url, string $documentRoot = '/')\\n\"\n    \"    {\\n\"\n    \"        [$this->host, $this->port] = Client::parseUrl($url);\\n\"\n    \"        $this->documentRoot        = $documentRoot;\\n\"\n    \"        $this->staticFileFilter    = [$this, 'staticFileFiltrate'];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withTimeout(float $timeout): self\\n\"\n    \"    {\\n\"\n    \"        $this->timeout = $timeout;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withHttps(bool $https): self\\n\"\n    \"    {\\n\"\n    \"        $this->https = $https;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withIndex(string $index): self\\n\"\n    \"    {\\n\"\n    \"        $this->index = $index;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getParam(string $name): ?string\\n\"\n    \"    {\\n\"\n    \"        return $this->params[$name] ?? null;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withParam(string $name, string $value): self\\n\"\n    \"    {\\n\"\n    \"        $this->params[$name] = $value;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withoutParam(string $name): self\\n\"\n    \"    {\\n\"\n    \"        unset($this->params[$name]);\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getParams(): array\\n\"\n    \"    {\\n\"\n    \"        return $this->params;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withParams(array $params): self\\n\"\n    \"    {\\n\"\n    \"        $this->params = $params;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withAddedParams(array $params): self\\n\"\n    \"    {\\n\"\n    \"        $this->params = $params + $this->params;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withStaticFileFilter(?callable $filter): self\\n\"\n    \"    {\\n\"\n    \"        $this->staticFileFilter = $filter;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function translateRequest(SwooleHttpRequest $userRequest): HttpRequest\\n\"\n    \"    {\\n\"\n    \"        $server   = $userRequest->server;\\n\"\n    \"        $headers  = $userRequest->header;\\n\"\n    \"        $pathInfo = $userRequest->server['path_info'];\\n\"\n    \"        $pathInfo = '/' . ltrim($pathInfo, '/');\\n\"\n    \"        if (strlen($this->index) !== 0) {\\n\"\n    \"            $extension = pathinfo($pathInfo, PATHINFO_EXTENSION);\\n\"\n    \"            if (empty($extension)) {\\n\"\n    \"                $pathInfo = rtrim($pathInfo, '/') . '/' . $this->index;\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"        $requestUri  = $scriptName = $documentUri = $server['request_uri'];\\n\"\n    \"        $queryString = $server['query_string'] ?? '';\\n\"\n    \"        if (strlen($queryString) !== 0) {\\n\"\n    \"            $requestUri .= \\\"?{$server['query_string']}\\\";\\n\"\n    \"        }\\n\"\n    \"        $request = (new HttpRequest())\\n\"\n    \"            ->withDocumentRoot($this->documentRoot)\\n\"\n    \"            ->withScriptFilename($this->documentRoot . $pathInfo)\\n\"\n    \"            ->withScriptName($scriptName)\\n\"\n    \"            ->withDocumentUri($documentUri)\\n\"\n    \"            ->withServerProtocol($server['server_protocol'])\\n\"\n    \"            ->withServerAddr('127.0.0.1')\\n\"\n    \"            ->withServerPort($server['server_port'])\\n\"\n    \"            ->withRemoteAddr($server['remote_addr'])\\n\"\n    \"            ->withRemotePort($server['remote_port'])\\n\"\n    \"            ->withMethod($server['request_method'])\\n\"\n    \"            ->withRequestUri($requestUri)\\n\"\n    \"            ->withQueryString($queryString)\\n\"\n    \"            ->withContentType($headers['content-type'] ?? '')\\n\"\n    \"            ->withContentLength((int) ($headers['content-length'] ?? 0))\\n\"\n    \"            ->withHeaders($headers)\\n\"\n    \"            ->withBody($userRequest->rawContent())\\n\"\n    \"            ->withAddedParams($this->params)\\n\"\n    \"        ;\\n\"\n    \"        if ($this->https) {\\n\"\n    \"            $request->withParam('HTTPS', '1');\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return $request;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function translateResponse(HttpResponse $response, SwooleHttpResponse $userResponse): void\\n\"\n    \"    {\\n\"\n    \"        $userResponse->status($response->getStatusCode(), $response->getReasonPhrase());\\n\"\n    \"        $userResponse->header = $response->getHeaders();\\n\"\n    \"        $userResponse->cookie = $response->getSetCookieHeaderLines();\\n\"\n    \"        $userResponse->end($response->getBody());\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function pass(SwooleHttpRequest|HttpRequest $userRequest, SwooleHttpResponse $userResponse): void\\n\"\n    \"    {\\n\"\n    \"        if (!$userRequest instanceof HttpRequest) {\\n\"\n    \"            $request = $this->translateRequest($userRequest);\\n\"\n    \"        } else {\\n\"\n    \"            $request = $userRequest;\\n\"\n    \"        }\\n\"\n    \"        unset($userRequest);\\n\"\n    \"        if ($this->staticFileFilter) {\\n\"\n    \"            $filter = $this->staticFileFilter;\\n\"\n    \"            if ($filter($request, $userResponse)) {\\n\"\n    \"                return;\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"        $response = (new Client($this->host, $this->port))->execute($request, $this->timeout);\\n\"\n    \"        $this->translateResponse($response, $userResponse);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * Send content of a static file to the client, if the file is accessible and is not a PHP file.\\n\"\n    \"     *\\n\"\n    \"     * @return bool True if the file doesn't have an extension of 'php', false otherwise. Note that the file may not be\\n\"\n    \"     *              accessible even the return value is true.\\n\"\n    \"     */\\n\"\n    \"    public function staticFileFiltrate(HttpRequest $request, SwooleHttpResponse $userResponse): bool\\n\"\n    \"    {\\n\"\n    \"        $extension = pathinfo($request->getScriptFilename(), PATHINFO_EXTENSION);\\n\"\n    \"        if ($extension !== 'php') {\\n\"\n    \"            $realPath = realpath($request->getScriptFilename());\\n\"\n    \"            if (!$realPath || !str_starts_with($realPath, $this->documentRoot) || !is_file($realPath)) {\\n\"\n    \"                $userResponse->status(Http\\\\Status::NOT_FOUND);\\n\"\n    \"            } else {\\n\"\n    \"                $userResponse->sendfile($realPath);\\n\"\n    \"            }\\n\"\n    \"            return true;\\n\"\n    \"        }\\n\"\n    \"        return false;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_process_manager =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Process;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Constant;\\n\"\n    \"\\n\"\n    \"use function Swoole\\\\Coroutine\\\\run;\\n\"\n    \"\\n\"\n    \"class Manager\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * @var Pool\\n\"\n    \"     */\\n\"\n    \"    protected $pool;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @var int\\n\"\n    \"     */\\n\"\n    \"    protected $ipcType = SWOOLE_IPC_NONE;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @var int\\n\"\n    \"     */\\n\"\n    \"    protected $msgQueueKey = 0;\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @var array\\n\"\n    \"     */\\n\"\n    \"    protected $startFuncMap = [];\\n\"\n    \"\\n\"\n    \"    public function __construct(int $ipcType = SWOOLE_IPC_NONE, int $msgQueueKey = 0)\\n\"\n    \"    {\\n\"\n    \"        $this->setIPCType($ipcType)->setMsgQueueKey($msgQueueKey);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function add(callable $func, bool $enableCoroutine = false): self\\n\"\n    \"    {\\n\"\n    \"        $this->addBatch(1, $func, $enableCoroutine);\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function addBatch(int $workerNum, callable $func, bool $enableCoroutine = false): self\\n\"\n    \"    {\\n\"\n    \"        for ($i = 0; $i < $workerNum; $i++) {\\n\"\n    \"            $this->startFuncMap[] = [$func, $enableCoroutine];\\n\"\n    \"        }\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function start(): void\\n\"\n    \"    {\\n\"\n    \"        $this->pool = new Pool(count($this->startFuncMap), $this->ipcType, $this->msgQueueKey, false);\\n\"\n    \"\\n\"\n    \"        $this->pool->on(Constant::EVENT_WORKER_START, function (Pool $pool, int $workerId) {\\n\"\n    \"            [$func, $enableCoroutine] = $this->startFuncMap[$workerId];\\n\"\n    \"            if ($enableCoroutine) {\\n\"\n    \"                run($func, $pool, $workerId);\\n\"\n    \"            } else {\\n\"\n    \"                $func($pool, $workerId);\\n\"\n    \"            }\\n\"\n    \"        });\\n\"\n    \"\\n\"\n    \"        $this->pool->start();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function setIPCType(int $ipcType): self\\n\"\n    \"    {\\n\"\n    \"        $this->ipcType = $ipcType;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getIPCType(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->ipcType;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function setMsgQueueKey(int $msgQueueKey): self\\n\"\n    \"    {\\n\"\n    \"        $this->msgQueueKey = $msgQueueKey;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getMsgQueueKey(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->msgQueueKey;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_remote_object =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\RemoteObject\\\\Client;\\n\"\n    \"use Swoole\\\\RemoteObject\\\\Exception;\\n\"\n    \"\\n\"\n    \"class RemoteObject implements \\\\ArrayAccess, \\\\Stringable, \\\\Iterator, \\\\Countable\\n\"\n    \"{\\n\"\n    \"    private int $objectId = 0;\\n\"\n    \"\\n\"\n    \"    private int $coroutineId;\\n\"\n    \"\\n\"\n    \"    private string $clientId;\\n\"\n    \"\\n\"\n    \"    private ?Client $client = null;\\n\"\n    \"\\n\"\n    \"    public function __construct($coroutineId, $clientId)\\n\"\n    \"    {\\n\"\n    \"        $this->coroutineId = $coroutineId;\\n\"\n    \"        $this->clientId    = $clientId;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __destruct()\\n\"\n    \"    {\\n\"\n    \"        // On the server side, this object will also be constructed,\\n\"\n    \"        // but it is only used for data storage and serialization.\\n\"\n    \"        // No remote calls are executed during destruction.\\n\"\n    \"        // If the objectId is 0, it indicates that the object may have been a temporary object created by a function call\\n\"\n    \"        // and does not need to be destructed.\\n\"\n    \"        if ($this->client and $this->objectId > 0) {\\n\"\n    \"            try {\\n\"\n    \"                $this->execute('/destroy', [\\n\"\n    \"                    'object' => $this->objectId,\\n\"\n    \"                ]);\\n\"\n    \"            } catch (Exception $e) {\\n\"\n    \"                error_log($e->getMessage());\\n\"\n    \"                debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws Exception\\n\"\n    \"     */\\n\"\n    \"    public function __call(string $method, array $args)\\n\"\n    \"    {\\n\"\n    \"        $rs = $this->execute('/call_method', [\\n\"\n    \"            'object' => $this->objectId,\\n\"\n    \"            'method' => $method,\\n\"\n    \"            'args'   => serialize($args),\\n\"\n    \"        ]);\\n\"\n    \"        return $rs['result'];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws Exception\\n\"\n    \"     */\\n\"\n    \"    public function __get(string $property)\\n\"\n    \"    {\\n\"\n    \"        $rs = $this->execute('/read_property', [\\n\"\n    \"            'object'   => $this->objectId,\\n\"\n    \"            'property' => $property,\\n\"\n    \"        ]);\\n\"\n    \"        return $rs['property'];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __set(string $property, mixed $value)\\n\"\n    \"    {\\n\"\n    \"        $this->execute('/write_property', [\\n\"\n    \"            'object'   => $this->objectId,\\n\"\n    \"            'property' => $property,\\n\"\n    \"            'value'    => serialize($value),\\n\"\n    \"        ]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __unserialize(array $data): void\\n\"\n    \"    {\\n\"\n    \"        $this->objectId    = $data['objectId'];\\n\"\n    \"        $this->coroutineId = $data['coroutineId'];\\n\"\n    \"        $this->clientId    = $data['clientId'];\\n\"\n    \"        $this->client      = Client::getInstance($this->clientId);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __serialize(): array\\n\"\n    \"    {\\n\"\n    \"        return [\\n\"\n    \"            'objectId'    => $this->objectId,\\n\"\n    \"            'coroutineId' => $this->coroutineId,\\n\"\n    \"            'clientId'    => $this->clientId,\\n\"\n    \"        ];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __toString(): string\\n\"\n    \"    {\\n\"\n    \"        $rs = $this->execute('/to_string', [\\n\"\n    \"            'object' => $this->objectId,\\n\"\n    \"        ]);\\n\"\n    \"        return $rs['value'];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __invoke(...$args)\\n\"\n    \"    {\\n\"\n    \"        $rs = $this->execute('/call_method', [\\n\"\n    \"            'object' => $this->objectId,\\n\"\n    \"            'method' => '__invoke',\\n\"\n    \"            'args'   => serialize($args),\\n\"\n    \"        ]);\\n\"\n    \"        return $rs['result'];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function call(Client $client, string $fn, array $args)\\n\"\n    \"    {\\n\"\n    \"        $object         = new self(Coroutine::getCid(), $client->getId());\\n\"\n    \"        $object->client = $client;\\n\"\n    \"        $rs             = $object->execute('/call_function', [\\n\"\n    \"            'function' => $fn,\\n\"\n    \"            'args'     => serialize($args),\\n\"\n    \"        ]);\\n\"\n    \"        return $rs['result'];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getObjectId(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->objectId;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws Exception\\n\"\n    \"     */\\n\"\n    \"    public static function create(Client $client, string $class, array $args): RemoteObject\\n\"\n    \"    {\\n\"\n    \"        $object         = new self(Coroutine::getCid(), $client->getId());\\n\"\n    \"        $object->client = $client;\\n\"\n    \"        $rs             = $object->execute('/new', [\\n\"\n    \"            'class' => $class,\\n\"\n    \"            'args'  => serialize($args),\\n\"\n    \"        ]);\\n\"\n    \"        $object->objectId = intval($rs['object']);\\n\"\n    \"        return $object;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * This method is only used on the server side.\\n\"\n    \"     */\\n\"\n    \"    public static function marshal(int $objectId, int $ownerCoroutineId, string $clientId): RemoteObject\\n\"\n    \"    {\\n\"\n    \"        $object             = new self($ownerCoroutineId, $clientId);\\n\"\n    \"        $object->objectId   = $objectId;\\n\"\n    \"        return $object;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function offsetGet(mixed $offset): mixed\\n\"\n    \"    {\\n\"\n    \"        $rs = $this->execute('/offset_get', [\\n\"\n    \"            'object' => $this->objectId,\\n\"\n    \"            'offset' => $offset,\\n\"\n    \"        ]);\\n\"\n    \"        return $rs['value'];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws Exception\\n\"\n    \"     */\\n\"\n    \"    public function offsetSet(mixed $offset, mixed $value): void\\n\"\n    \"    {\\n\"\n    \"        $this->execute('/offset_set', [\\n\"\n    \"            'object' => $this->objectId,\\n\"\n    \"            'offset' => $offset,\\n\"\n    \"            'value'  => serialize($value),\\n\"\n    \"        ]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws Exception\\n\"\n    \"     */\\n\"\n    \"    public function offsetUnset(mixed $offset): void\\n\"\n    \"    {\\n\"\n    \"        $this->execute('/offset_unset', [\\n\"\n    \"            'object' => $this->objectId,\\n\"\n    \"            'offset' => $offset,\\n\"\n    \"        ]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function offsetExists(mixed $offset): bool\\n\"\n    \"    {\\n\"\n    \"        $rs = $this->execute('/offset_exists', [\\n\"\n    \"            'object' => $this->objectId,\\n\"\n    \"            'offset' => $offset,\\n\"\n    \"        ]);\\n\"\n    \"        return $rs['exists'];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function current(): mixed\\n\"\n    \"    {\\n\"\n    \"        return $this->__call('current', []);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function next(): void\\n\"\n    \"    {\\n\"\n    \"        $this->__call('next', []);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function key(): mixed\\n\"\n    \"    {\\n\"\n    \"        return $this->__call('key', []);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function valid(): bool\\n\"\n    \"    {\\n\"\n    \"        return $this->__call('valid', []);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function rewind(): void\\n\"\n    \"    {\\n\"\n    \"        $this->__call('rewind', []);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function count(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->__call('count', []);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function execute(string $path, array $params = []): array\\n\"\n    \"    {\\n\"\n    \"        if (!$this->client) {\\n\"\n    \"            throw new Exception('This remote object is not bound to a client, and cannot initiate remote calls');\\n\"\n    \"        }\\n\"\n    \"        return $this->client->execute($path, $params);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_remote_object_server =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\RemoteObject;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Atomic\\\\Long;\\n\"\n    \"use Swoole\\\\Http\\\\Request;\\n\"\n    \"use Swoole\\\\Http\\\\Response;\\n\"\n    \"use Swoole\\\\Http\\\\Server as HttpServer;\\n\"\n    \"use Swoole\\\\RemoteObject;\\n\"\n    \"\\n\"\n    \"class Server\\n\"\n    \"{\\n\"\n    \"    public const DEFAULT_PORT = 9567;\\n\"\n    \"\\n\"\n    \"    private HttpServer $server;\\n\"\n    \"\\n\"\n    \"    private array $objects = [];\\n\"\n    \"\\n\"\n    \"    private array $allowedClasses = [];\\n\"\n    \"\\n\"\n    \"    private array $allowedFunctions = [];\\n\"\n    \"\\n\"\n    \"    private Long $nextObjectId;\\n\"\n    \"\\n\"\n    \"    private string $apiKey = '';\\n\"\n    \"\\n\"\n    \"    public function __construct(string $host = '127.0.0.1', int $port = self::DEFAULT_PORT, array $options = [])\\n\"\n    \"    {\\n\"\n    \"        // By default, thread mode is used, and when viewed with ps, only one process will be displayed.\\n\"\n    \"        $server_mode = $options['server_mode'] ?? SWOOLE_THREAD;\\n\"\n    \"        $socket_type = $options['socket_type'] ?? SWOOLE_SOCK_TCP;\\n\"\n    \"        $server      = new HttpServer($host, $port, $server_mode, $socket_type);\\n\"\n    \"        unset($options['server_mode'], $options['socket_type']);\\n\"\n    \"\\n\"\n    \"        if (isset($options['allowed_classes'])) {\\n\"\n    \"            if (!is_array($options['allowed_classes'])) {\\n\"\n    \"                throw new Exception('allowed_classes must be an array');\\n\"\n    \"            }\\n\"\n    \"            $this->allowedClasses = array_flip($options['allowed_classes']);\\n\"\n    \"            unset($options['allowed_classes']);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if (isset($options['allowed_functions'])) {\\n\"\n    \"            if (!is_array($options['allowed_functions'])) {\\n\"\n    \"                throw new Exception('allowed_functions must be an array');\\n\"\n    \"            }\\n\"\n    \"            $this->allowedFunctions = array_flip($options['allowed_functions']);\\n\"\n    \"            unset($options['allowed_functions']);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if (isset($options['api_key'])) {\\n\"\n    \"            $this->apiKey = $options['api_key'];\\n\"\n    \"            unset($options['api_key']);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if ($options) {\\n\"\n    \"            $server->set($options);\\n\"\n    \"        }\\n\"\n    \"        $server->on('request', [$this, 'onRequest']);\\n\"\n    \"        $server->on('start', [$this, 'onStart']);\\n\"\n    \"        $this->server       = $server;\\n\"\n    \"        $this->nextObjectId = new Long(1);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function start(): bool\\n\"\n    \"    {\\n\"\n    \"        return $this->server->start();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function onStart(): void\\n\"\n    \"    {\\n\"\n    \"        echo \\\"The remote-object server is started at http://{$this->server->host}:{$this->server->port}\\\\n\\\";\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function onRequest(Request $request, Response $response): void\\n\"\n    \"    {\\n\"\n    \"        $ctx = new Context($request, $response);\\n\"\n    \"        if ($this->apiKey and $this->apiKey !== $request->header['x-api-key']) {\\n\"\n    \"            $response->status(403);\\n\"\n    \"            $ctx->end(['code' => -3, 'msg' => 'invalid api key']);\\n\"\n    \"            return;\\n\"\n    \"        }\\n\"\n    \"        try {\\n\"\n    \"            $method = $ctx->getHandler();\\n\"\n    \"            if (method_exists($this, $method)) {\\n\"\n    \"                $this->{$method}($ctx);\\n\"\n    \"            } else {\\n\"\n    \"                $ctx->end(['code' => -1, 'msg' => 'invalid request']);\\n\"\n    \"            }\\n\"\n    \"        } catch (\\\\Throwable $e) {\\n\"\n    \"            $ctx->end(['code' => -2, 'exception' => [\\n\"\n    \"                'message' => $e->getMessage(),\\n\"\n    \"                'code'    => $e->getCode(),\\n\"\n    \"                'class'   => get_class($e),\\n\"\n    \"            ]]);\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function addObject($object): int\\n\"\n    \"    {\\n\"\n    \"        // The spl_object_id/spl_object_hash cannot be used,\\n\"\n    \"        // as the IDs they generate will be reused after the objects are destroyed.\\n\"\n    \"        $object_id                 = $this->nextObjectId->add();\\n\"\n    \"        $this->objects[$object_id] = $object;\\n\"\n    \"        return $object_id;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function marshal(Context $ctx, mixed $data): mixed\\n\"\n    \"    {\\n\"\n    \"        if (is_object($data) or is_resource($data)) {\\n\"\n    \"            $object_id = $this->addObject($data);\\n\"\n    \"            return RemoteObject::marshal($object_id, $ctx->getCoroutineId(), $ctx->getClientId());\\n\"\n    \"        }\\n\"\n    \"        if (is_array($data)) {\\n\"\n    \"            foreach ($data as $key => $value) {\\n\"\n    \"                $data[$key] = $this->marshal($ctx, $value);\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"        return $data;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function unmarshal($data): mixed\\n\"\n    \"    {\\n\"\n    \"        if (is_object($data) and $data instanceof RemoteObject) {\\n\"\n    \"            return $this->objects[$data->getObjectId()];\\n\"\n    \"        }\\n\"\n    \"        if (is_array($data)) {\\n\"\n    \"            foreach ($data as $key => $value) {\\n\"\n    \"                $data[$key] = $this->unmarshal($value);\\n\"\n    \"            }\\n\"\n    \"            return $data;\\n\"\n    \"        }\\n\"\n    \"        return $data;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws Exception\\n\"\n    \"     */\\n\"\n    \"    private function _new(Context $ctx): void\\n\"\n    \"    {\\n\"\n    \"        $class = trim($ctx->getParam('class'), '\\\\ ');\\n\"\n    \"        if (count($this->allowedClasses) > 0 and !isset($this->allowedClasses[$class])) {\\n\"\n    \"            throw new Exception(\\\"class[{$class}] not allowed\\\");\\n\"\n    \"        }\\n\"\n    \"        $class = '\\\\\\\\' . $class;\\n\"\n    \"        $args  = $ctx->getDataParam('args');\\n\"\n    \"        foreach ($args as $key => $value) {\\n\"\n    \"            $args[$key] = $this->unmarshal($value);\\n\"\n    \"        }\\n\"\n    \"        $obj       = new $class(...$args);\\n\"\n    \"        $object_id = $this->addObject($obj);\\n\"\n    \"        $ctx->end(['code' => 0, 'object' => $object_id]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function _call_function(Context $ctx): void\\n\"\n    \"    {\\n\"\n    \"        $fn = trim($ctx->getParam('function'), '\\\\ ');\\n\"\n    \"        if (count($this->allowedFunctions) > 0 and !isset($this->allowedFunctions[$fn])) {\\n\"\n    \"            throw new Exception(\\\"function[{$fn}] not allowed\\\");\\n\"\n    \"        }\\n\"\n    \"        $args = $ctx->getDataParam('args');\\n\"\n    \"        foreach ($args as $key => $value) {\\n\"\n    \"            $args[$key] = $this->unmarshal($value);\\n\"\n    \"        }\\n\"\n    \"        $fn = '\\\\\\\\' . $fn;\\n\"\n    \"        if (!function_exists($fn)) {\\n\"\n    \"            throw new Exception(\\\"function[{$fn}] not found\\\");\\n\"\n    \"        }\\n\"\n    \"        $result = $fn(...$args);\\n\"\n    \"        $ctx->end(['code' => 0, 'result' => $this->marshal($ctx, $result)]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws Exception\\n\"\n    \"     */\\n\"\n    \"    private function _call_method(Context $ctx): void\\n\"\n    \"    {\\n\"\n    \"        $object_id = $ctx->getParam('object');\\n\"\n    \"        if (!isset($this->objects[$object_id])) {\\n\"\n    \"            throw new Exception(\\\"object[#{$object_id}] not found\\\");\\n\"\n    \"        }\\n\"\n    \"        $method = $ctx->getParam('method');\\n\"\n    \"        $args   = $ctx->getDataParam('args');\\n\"\n    \"        foreach ($args as $key => $value) {\\n\"\n    \"            $args[$key] = $this->unmarshal($value);\\n\"\n    \"        }\\n\"\n    \"        $obj = $this->objects[$object_id];\\n\"\n    \"        if (!method_exists($obj, $method)) {\\n\"\n    \"            $class = get_class($obj);\\n\"\n    \"            throw new Exception(\\\"method[{$class}::{$method}] not found\\\");\\n\"\n    \"        }\\n\"\n    \"        $result = $obj->{$method}(...$args);\\n\"\n    \"        $ctx->end(['code' => 0, 'result' => $this->marshal($ctx, $result)]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws Exception\\n\"\n    \"     */\\n\"\n    \"    private function _read_property(Context $ctx): void\\n\"\n    \"    {\\n\"\n    \"        $object_id = $ctx->getParam('object');\\n\"\n    \"        $property  = $ctx->getParam('property');\\n\"\n    \"        if (!isset($this->objects[$object_id])) {\\n\"\n    \"            throw new Exception(\\\"object[#{$object_id}] not found\\\");\\n\"\n    \"        }\\n\"\n    \"        $obj    = $this->objects[$object_id];\\n\"\n    \"        $result = $obj->{$property};\\n\"\n    \"        $ctx->end(['code' => 0, 'property' => $this->marshal($ctx, $result)]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws Exception\\n\"\n    \"     */\\n\"\n    \"    private function _write_property(Context $ctx): void\\n\"\n    \"    {\\n\"\n    \"        $object_id = $ctx->getParam('object');\\n\"\n    \"        $property  = $ctx->getParam('property');\\n\"\n    \"        $value     = $ctx->getDataParam('value');\\n\"\n    \"        if (!isset($this->objects[$object_id])) {\\n\"\n    \"            throw new Exception(\\\"object[#{$object_id}] not found\\\");\\n\"\n    \"        }\\n\"\n    \"        $obj              = $this->objects[$object_id];\\n\"\n    \"        $obj->{$property} = $this->unmarshal($value);\\n\"\n    \"        $ctx->end(['code' => 0]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function _ping(Context $ctx): void\\n\"\n    \"    {\\n\"\n    \"        $ctx->end(['code' => 0]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws Exception\\n\"\n    \"     */\\n\"\n    \"    private function _destroy(Context $ctx): void\\n\"\n    \"    {\\n\"\n    \"        $object_id = $ctx->getParam('object');\\n\"\n    \"        if (!isset($this->objects[$object_id])) {\\n\"\n    \"            throw new Exception(\\\"object[#{$object_id}] not found\\\");\\n\"\n    \"        }\\n\"\n    \"        unset($this->objects[$object_id]);\\n\"\n    \"        $ctx->end(['code' => 0]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function _to_string(Context $ctx): void\\n\"\n    \"    {\\n\"\n    \"        $object_id = $ctx->getParam('object');\\n\"\n    \"        if (!isset($this->objects[$object_id])) {\\n\"\n    \"            throw new Exception(\\\"object[#{$object_id}] not found\\\");\\n\"\n    \"        }\\n\"\n    \"        $obj = $this->objects[$object_id];\\n\"\n    \"        $ctx->end(['code' => 0, 'value' => (string) $obj]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function _offset_get(Context $ctx): void\\n\"\n    \"    {\\n\"\n    \"        $object_id = $ctx->getParam('object');\\n\"\n    \"        $offset    = $ctx->getParam('offset');\\n\"\n    \"        if (!isset($this->objects[$object_id])) {\\n\"\n    \"            throw new Exception(\\\"object[#{$object_id}] not found\\\");\\n\"\n    \"        }\\n\"\n    \"        $obj    = $this->objects[$object_id];\\n\"\n    \"        $result = $obj->{$offset};\\n\"\n    \"        $ctx->end(['code' => 0, 'value' => $this->marshal($ctx, $result)]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function _offset_set(Context $ctx): void\\n\"\n    \"    {\\n\"\n    \"        $object_id = $ctx->getParam('object');\\n\"\n    \"        $offset    = $ctx->getParam('offset');\\n\"\n    \"        $value     = $ctx->getDataParam('value');\\n\"\n    \"        if (!isset($this->objects[$object_id])) {\\n\"\n    \"            throw new Exception(\\\"object[#{$object_id}] not found\\\");\\n\"\n    \"        }\\n\"\n    \"        $obj            = $this->objects[$object_id];\\n\"\n    \"        $obj->{$offset} = $this->unmarshal($value);\\n\"\n    \"        $ctx->end(['code' => 0]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function _offset_unset(Context $ctx): void\\n\"\n    \"    {\\n\"\n    \"        $object_id = $ctx->getParam('object');\\n\"\n    \"        $offset    = $ctx->getParam('offset');\\n\"\n    \"        if (!isset($this->objects[$object_id])) {\\n\"\n    \"            throw new Exception(\\\"object[#{$object_id}] not found\\\");\\n\"\n    \"        }\\n\"\n    \"        $obj = $this->objects[$object_id];\\n\"\n    \"        unset($obj->{$offset});\\n\"\n    \"        $ctx->end(['code' => 0]);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function _offset_exists(Context $ctx): void\\n\"\n    \"    {\\n\"\n    \"        $object_id = $ctx->getParam('object');\\n\"\n    \"        $offset    = $ctx->getParam('offset');\\n\"\n    \"        if (!isset($this->objects[$object_id])) {\\n\"\n    \"            throw new Exception(\\\"object[#{$object_id}] not found\\\");\\n\"\n    \"        }\\n\"\n    \"        $obj    = $this->objects[$object_id];\\n\"\n    \"        $result = isset($obj->{$offset});\\n\"\n    \"        $ctx->end(['code' => 0, 'value' => $this->marshal($ctx, $result)]);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_remote_object_context =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\RemoteObject;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Http\\\\Request;\\n\"\n    \"use Swoole\\\\Http\\\\Response;\\n\"\n    \"\\n\"\n    \"class Context\\n\"\n    \"{\\n\"\n    \"    public string $clientId;\\n\"\n    \"\\n\"\n    \"    public int $coroutineId;\\n\"\n    \"\\n\"\n    \"    public Request $request;\\n\"\n    \"\\n\"\n    \"    public Response $response;\\n\"\n    \"\\n\"\n    \"    public function __construct(Request $request, Response $response)\\n\"\n    \"    {\\n\"\n    \"        $this->clientId    = $request->header['client-id'] ?? '';\\n\"\n    \"        $this->coroutineId = intval($request->header['coroutine-id'] ?? 0);\\n\"\n    \"        $this->request     = $request;\\n\"\n    \"        $this->response    = $response;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function end(array $data): void\\n\"\n    \"    {\\n\"\n    \"        $this->response->header('Content-Type', 'application/octet-stream');\\n\"\n    \"        $this->response->end(serialize($data));\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getHandler(): string\\n\"\n    \"    {\\n\"\n    \"        $path = $this->request->server['request_uri'];\\n\"\n    \"        return str_replace('/', '_', $path);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getParam(string $name): string\\n\"\n    \"    {\\n\"\n    \"        if (!isset($this->request->post[$name])) {\\n\"\n    \"            throw new Exception(\\\"param[{$name}] is empty\\\");\\n\"\n    \"        }\\n\"\n    \"        return $this->request->post[$name];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getDataParam(string $name): mixed\\n\"\n    \"    {\\n\"\n    \"        return unserialize($this->getParam($name));\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getCoroutineId(): int\\n\"\n    \"    {\\n\"\n    \"        $coroutine_id = $this->request->header['coroutine-id'] ?? '';\\n\"\n    \"        if (!$coroutine_id) {\\n\"\n    \"            throw new Exception('coroutine-id is empty');\\n\"\n    \"        }\\n\"\n    \"        return intval($coroutine_id);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getClientId(): string\\n\"\n    \"    {\\n\"\n    \"        $client_id = $this->request->header['client-id'] ?? '';\\n\"\n    \"        if (!$client_id) {\\n\"\n    \"            throw new Exception('client-id is empty');\\n\"\n    \"        }\\n\"\n    \"        return $client_id;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_remote_object_client =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\RemoteObject;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Coroutine;\\n\"\n    \"use Swoole\\\\Coroutine\\\\Http\\\\Client as HttpClient;\\n\"\n    \"use Swoole\\\\RemoteObject;\\n\"\n    \"\\n\"\n    \"class Client\\n\"\n    \"{\\n\"\n    \"    private static array $clients = [];\\n\"\n    \"\\n\"\n    \"    private HttpClient $client;\\n\"\n    \"\\n\"\n    \"    private string $id;\\n\"\n    \"\\n\"\n    \"    private int $ownerCoroutineId;\\n\"\n    \"\\n\"\n    \"    public function __construct(string $host = '127.0.0.1', int $port = Server::DEFAULT_PORT, array $options = [])\\n\"\n    \"    {\\n\"\n    \"        $this->id               = $this->genUuid();\\n\"\n    \"        $this->client           = new HttpClient($host, $port);\\n\"\n    \"        $this->ownerCoroutineId = Coroutine::getCid();\\n\"\n    \"\\n\"\n    \"        $headers = [\\n\"\n    \"            'client-id'    => $this->id,\\n\"\n    \"            'coroutine-id' => $this->ownerCoroutineId,\\n\"\n    \"        ];\\n\"\n    \"        if (isset($options['api_key'])) {\\n\"\n    \"            $headers['x-api-key'] = $options['api_key'];\\n\"\n    \"        }\\n\"\n    \"        $this->client->setHeaders($headers);\\n\"\n    \"        self::$clients[$this->id] = $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function create(string $class, mixed ...$args): RemoteObject\\n\"\n    \"    {\\n\"\n    \"        return RemoteObject::create($this, $class, $args);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function call(string $fn, mixed ...$args): mixed\\n\"\n    \"    {\\n\"\n    \"        return RemoteObject::call($this, $fn, $args);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws Exception\\n\"\n    \"     */\\n\"\n    \"    public static function getInstance(string $clientId): ?static\\n\"\n    \"    {\\n\"\n    \"        if (empty($clientId)) {\\n\"\n    \"            throw new Exception('RemoteObject is not bound to a client');\\n\"\n    \"        }\\n\"\n    \"        if (!isset(self::$clients[$clientId])) {\\n\"\n    \"            return null;\\n\"\n    \"        }\\n\"\n    \"        return self::$clients[$clientId];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getId(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->id;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function execute(string $path, array $array)\\n\"\n    \"    {\\n\"\n    \"        $rs = $this->client->post($path, $array);\\n\"\n    \"        if (!$rs) {\\n\"\n    \"            throw new Exception($this->client->errMsg);\\n\"\n    \"        }\\n\"\n    \"        $result = unserialize($this->client->body);\\n\"\n    \"        if ($result['code'] != 0) {\\n\"\n    \"            $ex = $result['exception'];\\n\"\n    \"            throw new Exception('Server Error: ' . $ex['message'], $ex['code']);\\n\"\n    \"        }\\n\"\n    \"        return $result;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function ping(): bool\\n\"\n    \"    {\\n\"\n    \"        try {\\n\"\n    \"            $this->execute('/ping', []);\\n\"\n    \"            return true;\\n\"\n    \"        } catch (\\\\Throwable $e) {\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function genUuid(): string\\n\"\n    \"    {\\n\"\n    \"        $data    = random_bytes(16);\\n\"\n    \"        $data[6] = chr(ord($data[6]) & 0x0F | 0x40);\\n\"\n    \"        $data[8] = chr(ord($data[8]) & 0x3F | 0x80);\\n\"\n    \"        return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_remote_object_exception =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\RemoteObject;\\n\"\n    \"\\n\"\n    \"class Exception extends \\\\RuntimeException\\n\"\n    \"{\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_remote_object_proxy_trait =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\RemoteObject;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\RemoteObject;\\n\"\n    \"\\n\"\n    \"trait ProxyTrait\\n\"\n    \"{\\n\"\n    \"    public function __call(string $method, array $args)\\n\"\n    \"    {\\n\"\n    \"        return $this->getObject()->{$method}(...$args);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __get(string $property)\\n\"\n    \"    {\\n\"\n    \"        return $this->getObject()->{$property};\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __set(string $property, mixed $value)\\n\"\n    \"    {\\n\"\n    \"        $this->getObject()->{$property} = $value;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __toString(): string\\n\"\n    \"    {\\n\"\n    \"        return $this->getObject()->__toString();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function __invoke(...$args)\\n\"\n    \"    {\\n\"\n    \"        return $this->getObject()->__invoke(...$args);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function offsetGet(mixed $offset): mixed\\n\"\n    \"    {\\n\"\n    \"        return $this->getObject()->offsetGet($offset);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws Exception\\n\"\n    \"     */\\n\"\n    \"    public function offsetSet(mixed $offset, mixed $value): void\\n\"\n    \"    {\\n\"\n    \"        $this->getObject()->offsetSet($offset, $value);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws Exception\\n\"\n    \"     */\\n\"\n    \"    public function offsetUnset(mixed $offset): void\\n\"\n    \"    {\\n\"\n    \"        $this->getObject()->offsetUnset($offset);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function offsetExists(mixed $offset): bool\\n\"\n    \"    {\\n\"\n    \"        return $this->getObject()->offsetExists($offset);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function current(): mixed\\n\"\n    \"    {\\n\"\n    \"        return $this->getObject()->current();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function next(): void\\n\"\n    \"    {\\n\"\n    \"        $this->getObject()->next();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function key(): mixed\\n\"\n    \"    {\\n\"\n    \"        return $this->getObject()->key();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function valid(): bool\\n\"\n    \"    {\\n\"\n    \"        return $this->getObject()->valid();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function rewind(): void\\n\"\n    \"    {\\n\"\n    \"        $this->getObject()->rewind();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function count(): int\\n\"\n    \"    {\\n\"\n    \"        return $this->getObject()->count();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    abstract protected function getObject(): RemoteObject;\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_server_admin =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Server;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Coroutine;\\n\"\n    \"use Swoole\\\\Http\\\\Request;\\n\"\n    \"use Swoole\\\\Http\\\\Response;\\n\"\n    \"use Swoole\\\\Server;\\n\"\n    \"use Swoole\\\\StringObject;\\n\"\n    \"use Swoole\\\\Timer;\\n\"\n    \"\\n\"\n    \"class Admin\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * gdb php\\n\"\n    \"     * (gdb) p sizeof(zval)\\n\"\n    \"     * $2 = 16\\n\"\n    \"     * (gdb) p sizeof(zend_array)\\n\"\n    \"     * $1 = 56\\n\"\n    \"     * (gdb) p sizeof(zend_string)\\n\"\n    \"     * $3 = 32\\n\"\n    \"     * (gdb) p sizeof(zend_object)\\n\"\n    \"     * $4 = 56\\n\"\n    \"     */\\n\"\n    \"    public const SIZE_OF_ZVAL = 16;\\n\"\n    \"\\n\"\n    \"    public const SIZE_OF_ZEND_STRING = 32;\\n\"\n    \"\\n\"\n    \"    public const SIZE_OF_ZEND_OBJECT = 56;\\n\"\n    \"\\n\"\n    \"    public const SIZE_OF_ZEND_ARRAY = 56;\\n\"\n    \"\\n\"\n    \"    private static array $map = [\\n\"\n    \"        'reactor'        => SWOOLE_SERVER_COMMAND_REACTOR_THREAD,\\n\"\n    \"        'reactor_thread' => SWOOLE_SERVER_COMMAND_REACTOR_THREAD,\\n\"\n    \"        'worker'         => SWOOLE_SERVER_COMMAND_EVENT_WORKER,\\n\"\n    \"        'event_worker'   => SWOOLE_SERVER_COMMAND_EVENT_WORKER,\\n\"\n    \"        'task'           => SWOOLE_SERVER_COMMAND_TASK_WORKER,\\n\"\n    \"        'task_worker'    => SWOOLE_SERVER_COMMAND_TASK_WORKER,\\n\"\n    \"    ];\\n\"\n    \"\\n\"\n    \"    private static array $allList = [\\n\"\n    \"        'all',\\n\"\n    \"        'all_reactor',\\n\"\n    \"        'all_reactor_thread',\\n\"\n    \"        'all_worker',\\n\"\n    \"        'all_event_worker',\\n\"\n    \"        'all_task',\\n\"\n    \"        'all_task_worker',\\n\"\n    \"        'specific',\\n\"\n    \"    ];\\n\"\n    \"\\n\"\n    \"    private static array $postMethodList = [\\n\"\n    \"        'server_reload',\\n\"\n    \"        'server_shutdown',\\n\"\n    \"        'close_session',\\n\"\n    \"    ];\\n\"\n    \"\\n\"\n    \"    private static string $accessToken = '';\\n\"\n    \"\\n\"\n    \"    public static function init(Server $server): void\\n\"\n    \"    {\\n\"\n    \"        $accepted_process_types = SWOOLE_SERVER_COMMAND_MASTER |\\n\"\n    \"            SWOOLE_SERVER_COMMAND_MANAGER |\\n\"\n    \"            SWOOLE_SERVER_COMMAND_EVENT_WORKER |\\n\"\n    \"            SWOOLE_SERVER_COMMAND_TASK_WORKER;\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'server_reload',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            function (Server $server, string $msg) {\\n\"\n    \"                $server->reload();\\n\"\n    \"                return self::json('Operation succeeded');\\n\"\n    \"            }\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'server_shutdown',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            function (Server $server, string $msg): void {\\n\"\n    \"                $server->shutdown();\\n\"\n    \"            }\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'coroutine_stats',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            fn (Server $server, string $msg) => self::json(Coroutine::stats())\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'coroutine_list',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            fn (Server $server, string $msg) => self::json(iterator_to_array(Coroutine::list()))\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'coroutine_bt',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            function (Server $server, string $msg) {\\n\"\n    \"                $json = json_decode($msg, null, 512, JSON_THROW_ON_ERROR);\\n\"\n    \"                $cid  = empty($json->cid) ? 0 : intval($json->cid);\\n\"\n    \"                $bt   = Coroutine::getBackTrace($cid);\\n\"\n    \"                if ($bt === false) {\\n\"\n    \"                    return self::json(\\\"Coroutine#{$cid} not exists\\\", 4004);\\n\"\n    \"                }\\n\"\n    \"                return self::json($bt);\\n\"\n    \"            }\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'server_stats',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            fn (Server $server, string $msg) => self::json($server->stats())\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'server_setting',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            function (Server $server, string $msg) {\\n\"\n    \"                $setting                = $server->setting;\\n\"\n    \"                $setting['mode']        = $server->mode;\\n\"\n    \"                $setting['host']        = $server->host;\\n\"\n    \"                $setting['port']        = $server->port;\\n\"\n    \"                $setting['master_pid']  = $server->master_pid;\\n\"\n    \"                $setting['manager_pid'] = $server->manager_pid;\\n\"\n    \"                return self::json($setting);\\n\"\n    \"            }\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'get_client_info',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            function (Server $server, string $msg) {\\n\"\n    \"                $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\\n\"\n    \"                if (empty($json['session_id'])) {\\n\"\n    \"                    return self::json('require session_id', 4003);\\n\"\n    \"                }\\n\"\n    \"                return self::json($server->getClientInfo(intval($json['session_id'])));\\n\"\n    \"            }\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand('close_session', $accepted_process_types, [self::class, 'handlerCloseSession']);\\n\"\n    \"        $server->addCommand('get_version_info', $accepted_process_types, [self::class, 'handlerGetVersionInfo']);\\n\"\n    \"        $server->addCommand('get_worker_info', $accepted_process_types, [self::class, 'handlerGetWorkerInfo']);\\n\"\n    \"        $server->addCommand('get_timer_list', $accepted_process_types, [self::class, 'handlerGetTimerList']);\\n\"\n    \"        $server->addCommand('get_coroutine_list', $accepted_process_types, [self::class, 'handlerGetCoroutineList']);\\n\"\n    \"        $server->addCommand('get_objects', $accepted_process_types, [self::class, 'handlerGetObjects']);\\n\"\n    \"        $server->addCommand('get_class_info', $accepted_process_types, [self::class, 'handlerGetClassInfo']);\\n\"\n    \"        $server->addCommand('get_function_info', $accepted_process_types, [self::class, 'handlerGetFunctionInfo']);\\n\"\n    \"        $server->addCommand('get_object_by_handle', $accepted_process_types, [self::class, 'handlerGetObjectByHandle']);\\n\"\n    \"        $server->addCommand('get_server_cpu_usage', $accepted_process_types, [self::class, 'handlerGetServerCpuUsage']);\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'get_server_memory_usage',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            [self::class, 'handlerGetServerMemoryUsage']\\n\"\n    \"        );\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'get_static_property_value',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            [self::class, 'handlerGetStaticPropertyValue']\\n\"\n    \"        );\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'get_defined_functions',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            [self::class, 'handlerGetDefinedFunctions']\\n\"\n    \"        );\\n\"\n    \"        $server->addCommand('get_declared_classes', $accepted_process_types, [self::class, 'handlerGetDeclaredClasses']);\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'gc_status',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            function (Server $server, string $msg) {\\n\"\n    \"                return self::json(gc_status());\\n\"\n    \"            }\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        if (extension_loaded('opcache')) {\\n\"\n    \"            $server->addCommand(\\n\"\n    \"                'opcache_status',\\n\"\n    \"                $accepted_process_types,\\n\"\n    \"                fn (Server $server, string $msg) => self::json(opcache_get_status(true))\\n\"\n    \"            );\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'getpid',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            fn (Server $server, string $msg) => self::json(['pid' => posix_getpid()])\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'memory_usage',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            fn (Server $server, string $msg) => self::json([\\n\"\n    \"                'usage'      => memory_get_usage(),\\n\"\n    \"                'real_usage' => memory_get_usage(true),\\n\"\n    \"            ])\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'get_included_files',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            fn (Server $server, string $msg) => self::json(['files' => get_included_files()])\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand('get_resources', $accepted_process_types, [self::class, 'handlerGetResources']);\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'get_defined_constants',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            function (Server $server, string $msg) {\\n\"\n    \"                $constants = get_defined_constants();\\n\"\n    \"                foreach ($constants as $k => $c) {\\n\"\n    \"                    if (is_resource($c)) {\\n\"\n    \"                        unset($constants[$k]);\\n\"\n    \"                    }\\n\"\n    \"                }\\n\"\n    \"                unset($constants['NULL'], $constants['NAN'], $constants['INF']);\\n\"\n    \"                return self::json($constants);\\n\"\n    \"            }\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'get_loaded_extensions',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            function (Server $server, string $msg) {\\n\"\n    \"                $extensions = get_loaded_extensions();\\n\"\n    \"                $list       = [];\\n\"\n    \"                foreach ($extensions as $key => $extension) {\\n\"\n    \"                    $ext        = new \\\\ReflectionExtension($extension);\\n\"\n    \"                    $list[$key] = [\\n\"\n    \"                        'id'      => ++$key,\\n\"\n    \"                        'name'    => $extension,\\n\"\n    \"                        'version' => (string) $ext->getVersion(),\\n\"\n    \"                    ];\\n\"\n    \"                }\\n\"\n    \"                return self::json($list);\\n\"\n    \"            }\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'get_declared_interfaces',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            fn (Server $server, string $msg) => self::json(get_declared_interfaces())\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'get_declared_traits',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            fn (Server $server, string $msg) => self::json(get_declared_traits())\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'get_included_file_contents',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            function (Server $server, string $msg) {\\n\"\n    \"                $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\\n\"\n    \"                if (empty($json['filename'])) {\\n\"\n    \"                    return self::json('require filename', 4003);\\n\"\n    \"                }\\n\"\n    \"\\n\"\n    \"                if (!file_exists($json['filename'])) {\\n\"\n    \"                    return self::json(\\\"{$json['filename']} not exist\\\", 4004);\\n\"\n    \"                }\\n\"\n    \"\\n\"\n    \"                if (!in_array($json['filename'], get_included_files())) {\\n\"\n    \"                    return self::json('no permission', 4003);\\n\"\n    \"                }\\n\"\n    \"\\n\"\n    \"                return self::json(file_get_contents($json['filename']));\\n\"\n    \"            }\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'get_globals',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            function (Server $server, string $msg) {\\n\"\n    \"                $globals = [];\\n\"\n    \"                foreach ($GLOBALS as $key => $item) {\\n\"\n    \"                    if ($key === 'GLOBALS') {\\n\"\n    \"                        continue;\\n\"\n    \"                    }\\n\"\n    \"                    $type  = gettype($item);\\n\"\n    \"                    $other = [];\\n\"\n    \"                    if ($type === 'object') {\\n\"\n    \"                        $other = [\\n\"\n    \"                            'class_name'  => $item::class,\\n\"\n    \"                            'object_id'   => spl_object_id($item),\\n\"\n    \"                            'object_hash' => spl_object_hash($item),\\n\"\n    \"                        ];\\n\"\n    \"                    }\\n\"\n    \"                    if ($type == 'resource' || $type == 'resource (closed)') {\\n\"\n    \"                        $item = '';\\n\"\n    \"                    }\\n\"\n    \"                    $globals[] = [\\n\"\n    \"                        'key'   => $key,\\n\"\n    \"                        'value' => $item,\\n\"\n    \"                        'type'  => $type,\\n\"\n    \"                        'other' => $other,\\n\"\n    \"                    ];\\n\"\n    \"                }\\n\"\n    \"                return self::json($globals);\\n\"\n    \"            }\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'get_extension_info',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            function (Server $server, string $msg) {\\n\"\n    \"                $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\\n\"\n    \"\\n\"\n    \"                if (empty($json['extension_name']) || !extension_loaded($json['extension_name'])) {\\n\"\n    \"                    return self::json('require extension_name', 4004);\\n\"\n    \"                }\\n\"\n    \"\\n\"\n    \"                $ext = new \\\\ReflectionExtension($json['extension_name']);\\n\"\n    \"\\n\"\n    \"                ob_start();\\n\"\n    \"                $ext->info();\\n\"\n    \"                $info = ob_get_clean();\\n\"\n    \"\\n\"\n    \"                $constants = $ext->getConstants();\\n\"\n    \"                foreach ($constants as $k => $c) {\\n\"\n    \"                    if (is_resource($c)) {\\n\"\n    \"                        unset($constants[$k]);\\n\"\n    \"                    }\\n\"\n    \"                }\\n\"\n    \"\\n\"\n    \"                unset($constants['NULL'], $constants['NAN'], $constants['INF']);\\n\"\n    \"\\n\"\n    \"                return self::json([\\n\"\n    \"                    'classes'      => $ext->getClassNames(),\\n\"\n    \"                    'version'      => $ext->getVersion(),\\n\"\n    \"                    'constants'    => $constants,\\n\"\n    \"                    'ini_entries'  => $ext->getINIEntries(),\\n\"\n    \"                    'dependencies' => $ext->getDependencies(),\\n\"\n    \"                    'functions'    => array_keys($ext->getFunctions()),\\n\"\n    \"                    'info'         => trim($info),\\n\"\n    \"                ]);\\n\"\n    \"            }\\n\"\n    \"        );\\n\"\n    \"\\n\"\n    \"        $server->addCommand(\\n\"\n    \"            'get_composer_packages',\\n\"\n    \"            $accepted_process_types,\\n\"\n    \"            function (Server $server, string $msg) {\\n\"\n    \"                if (!class_exists(\\\\Composer\\\\InstalledVersions::class)) {\\n\"\n    \"                    return self::json('require composer 2.0', 4003);\\n\"\n    \"                }\\n\"\n    \"\\n\"\n    \"                $list = [];\\n\"\n    \"                foreach (['getAllRawData', 'getRawData'] as $method) {\\n\"\n    \"                    if (!method_exists(\\\\Composer\\\\InstalledVersions::class, $method)) {\\n\"\n    \"                        continue;\\n\"\n    \"                    }\\n\"\n    \"\\n\"\n    \"                    $raw_data[] = \\\\Composer\\\\InstalledVersions::$method();\\n\"\n    \"                    if ($method === 'getAllRawData') {\\n\"\n    \"                        $raw_data = \\\\Composer\\\\InstalledVersions::$method();\\n\"\n    \"                        array_shift($raw_data);\\n\"\n    \"                    }\\n\"\n    \"\\n\"\n    \"                    foreach ($raw_data as $key => $package) {\\n\"\n    \"                        $key_name = $package['root']['name'];\\n\"\n    \"                        if ($package['root']['name'] === '__root__' && isset($list['__root__'])) {\\n\"\n    \"                            $key_name = \\\"__root__{$key}\\\";\\n\"\n    \"                        }\\n\"\n    \"                        $package['root']['install_path'] = !empty($package['root']['install_path']) ? realpath($package['root']['install_path']) : '';\\n\"\n    \"                        $list[$key_name]                 = $package;\\n\"\n    \"                    }\\n\"\n    \"                    break;\\n\"\n    \"                }\\n\"\n    \"                return self::json($list);\\n\"\n    \"            }\\n\"\n    \"        );\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function getAccessToken(): string\\n\"\n    \"    {\\n\"\n    \"        return self::$accessToken;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function start(Server $server): void\\n\"\n    \"    {\\n\"\n    \"        $admin_server_uri = swoole_string($server->setting['admin_server']);\\n\"\n    \"        if ($admin_server_uri->startsWith('unix:/')) {\\n\"\n    \"            swoole_error_log(SWOOLE_LOG_ERROR, \\\"admin_server[{$server->setting['admin_server']}] is not supported\\\");\\n\"\n    \"            return;\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if ($admin_server_uri->contains('@')) {\\n\"\n    \"            [$access_name, $access_secret] = $admin_server_uri->split('@', 2)->get(0)->split(':', 2)->toArray();\\n\"\n    \"            self::$accessToken             = sha1($access_name . $access_secret);\\n\"\n    \"            [$host, $port]                 = $admin_server_uri->split('@', 2)->get(1)->split(':', 2)->toArray();\\n\"\n    \"        } else {\\n\"\n    \"            [$host, $port] = $admin_server_uri->split(':', 2)->toArray();\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $admin_server = new Coroutine\\\\Http\\\\Server($host, intval($port));\\n\"\n    \"\\n\"\n    \"        $admin_server->handle('/api', function (Request $req, Response $resp) use ($server) {\\n\"\n    \"            $path_array = swoole_string($req->server['request_uri'])->trim('/')->split('/');\\n\"\n    \"            if ($path_array->count() < 2 or $path_array->count() > 3) {\\n\"\n    \"                $resp->status(403);\\n\"\n    \"                $resp->end(self::json('Bad API path', 4003));\\n\"\n    \"                return;\\n\"\n    \"            }\\n\"\n    \"\\n\"\n    \"            $resp->header('Server', 'swoole-admin-server');\\n\"\n    \"            $resp->header('Access-Control-Allow-Origin', '*');\\n\"\n    \"            $resp->header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\\n\"\n    \"            $resp->header('Access-Control-Allow-Headers', 'X-ACCESS-TOKEN, X-ADMIN-SERVER-ACCESS-TOKEN');\\n\"\n    \"\\n\"\n    \"            $method = $req->getMethod();\\n\"\n    \"\\n\"\n    \"            if ($method === 'OPTIONS') {\\n\"\n    \"                $resp->end();\\n\"\n    \"                return;\\n\"\n    \"            }\\n\"\n    \"\\n\"\n    \"            $token = self::getAccessToken();\\n\"\n    \"            if (!empty($token)) {\\n\"\n    \"                $token_header = $req->header['x-admin-server-access-token'] ?? '';\\n\"\n    \"                if ($token_header !== $token) {\\n\"\n    \"                    $resp->status(403);\\n\"\n    \"                    $resp->end(self::json('Bad access token', 4003));\\n\"\n    \"                    return;\\n\"\n    \"                }\\n\"\n    \"            }\\n\"\n    \"\\n\"\n    \"            $cmd = $path_array->get(1)->toString();\\n\"\n    \"\\n\"\n    \"            if (in_array($cmd, self::$postMethodList) && $method != 'POST') {\\n\"\n    \"                $resp->status(403);\\n\"\n    \"                $resp->end(self::json('Bad request method', 4003));\\n\"\n    \"                return;\\n\"\n    \"            }\\n\"\n    \"\\n\"\n    \"            if ($method == 'GET') {\\n\"\n    \"                $data = $req->get;\\n\"\n    \"            } else {\\n\"\n    \"                $data = $req->post;\\n\"\n    \"            }\\n\"\n    \"\\n\"\n    \"            if ($cmd === 'multi') {\\n\"\n    \"                $body = json_decode($req->getContent(), true, 512, JSON_THROW_ON_ERROR);\\n\"\n    \"                if (empty($body) || !is_array($body) || $method != 'POST') {\\n\"\n    \"                    goto _bad_process;\\n\"\n    \"                }\\n\"\n    \"\\n\"\n    \"                $result = self::handlerMulti($server, $body);\\n\"\n    \"                $resp->end(json_encode($result, JSON_INVALID_UTF8_IGNORE));\\n\"\n    \"                return;\\n\"\n    \"            }\\n\"\n    \"\\n\"\n    \"            if ($path_array->count() == 2) {\\n\"\n    \"                $process = swoole_string('master');\\n\"\n    \"            } else {\\n\"\n    \"                $process = $path_array->get(2);\\n\"\n    \"            }\\n\"\n    \"\\n\"\n    \"            if ($process->startsWith('master')) {\\n\"\n    \"                $process_type = SWOOLE_SERVER_COMMAND_MASTER;\\n\"\n    \"                $process_id   = 0;\\n\"\n    \"            } elseif ($process->startsWith('manager')) {\\n\"\n    \"                $process_type = SWOOLE_SERVER_COMMAND_MANAGER;\\n\"\n    \"                $process_id   = 0;\\n\"\n    \"            } elseif ($process->startsWith('all') || $process->equals('specific')) {\\n\"\n    \"                if (!in_array($process->toString(), self::$allList)) {\\n\"\n    \"                    goto _bad_process;\\n\"\n    \"                }\\n\"\n    \"\\n\"\n    \"                $result = self::handlerGetAll($server, $process, $cmd, $data);\\n\"\n    \"\\n\"\n    \"                $resp->end(self::json($result));\\n\"\n    \"                return;\\n\"\n    \"            } else {\\n\"\n    \"                $array = $process->split('-');\\n\"\n    \"                if ($array->count() != 2) {\\n\"\n    \"                    _bad_process:\\n\"\n    \"                    $resp->status(403);\\n\"\n    \"                    $resp->end(self::json('Bad process', 4003));\\n\"\n    \"                    return;\\n\"\n    \"                }\\n\"\n    \"\\n\"\n    \"                if (!isset(self::$map[$array->get(0)->toString()])) {\\n\"\n    \"                    goto _bad_process;\\n\"\n    \"                }\\n\"\n    \"\\n\"\n    \"                $process_type = self::$map[$array->get(0)->toString()];\\n\"\n    \"                $process_id   = intval($array->get(1)->toString());\\n\"\n    \"            }\\n\"\n    \"\\n\"\n    \"            $result = $server->command($cmd, $process_id, intval($process_type), $data, false);\\n\"\n    \"            if (!$result) {\\n\"\n    \"                $resp->end(json_encode([\\n\"\n    \"                    'code' => swoole_last_error(),\\n\"\n    \"                    'data' => swoole_strerror(swoole_last_error()),\\n\"\n    \"                ], JSON_THROW_ON_ERROR));\\n\"\n    \"            } else {\\n\"\n    \"                $resp->end($result);\\n\"\n    \"            }\\n\"\n    \"        });\\n\"\n    \"        $admin_server->handle('/', function (Request $req, Response $resp): void {\\n\"\n    \"            $resp->status(404);\\n\"\n    \"        });\\n\"\n    \"        $server->admin_server = $admin_server;\\n\"\n    \"        $admin_server->start();\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return false|string\\n\"\n    \"     */\\n\"\n    \"    public static function handlerGetResources(Server $server, string $msg)\\n\"\n    \"    {\\n\"\n    \"        $resources = get_resources();\\n\"\n    \"        $list      = [];\\n\"\n    \"        foreach ($resources as $r) {\\n\"\n    \"            $info = [\\n\"\n    \"                'id'   => get_resource_id($r),\\n\"\n    \"                'type' => get_resource_type($r),\\n\"\n    \"            ];\\n\"\n    \"            if ($info['type'] == 'stream') {\\n\"\n    \"                $info['info'] = stream_get_meta_data($r);\\n\"\n    \"            }\\n\"\n    \"            $list[] = $info;\\n\"\n    \"        }\\n\"\n    \"        return self::json($list);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return false|string\\n\"\n    \"     */\\n\"\n    \"    public static function handlerGetWorkerInfo(Server $server, string $msg)\\n\"\n    \"    {\\n\"\n    \"        $info = [\\n\"\n    \"            'id'                => $server->getWorkerId(),\\n\"\n    \"            'pid'               => $server->getWorkerPid(),\\n\"\n    \"            'gc_status'         => gc_status(),\\n\"\n    \"            'memory_usage'      => memory_get_usage(),\\n\"\n    \"            'memory_real_usage' => memory_get_usage(true),\\n\"\n    \"            'process_status'    => self::getProcessStatus(),\\n\"\n    \"            'coroutine_stats'   => Coroutine::stats(),\\n\"\n    \"            'timer_stats'       => Timer::stats(),\\n\"\n    \"            'vm_status'         => swoole_get_vm_status(),\\n\"\n    \"        ];\\n\"\n    \"        return self::json($info);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return false|string\\n\"\n    \"     */\\n\"\n    \"    public static function handlerCloseSession(Server $server, string $msg)\\n\"\n    \"    {\\n\"\n    \"        $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\\n\"\n    \"        if (empty($json['session_id'])) {\\n\"\n    \"            return self::json('require session_id', 4003);\\n\"\n    \"        }\\n\"\n    \"        if ($server->close(intval($json['session_id']), !empty($json['force']))) {\\n\"\n    \"            return self::json([]);\\n\"\n    \"        }\\n\"\n    \"        return self::json(['error' => swoole_last_error()], 4004);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return false|string\\n\"\n    \"     */\\n\"\n    \"    public static function handlerGetTimerList(Server $server, string $msg)\\n\"\n    \"    {\\n\"\n    \"        $list = [];\\n\"\n    \"        foreach (Timer::list() as $timer_id) {\\n\"\n    \"            $list[] = [\\n\"\n    \"                'id'   => $timer_id,\\n\"\n    \"                'info' => Timer::info($timer_id),\\n\"\n    \"            ];\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return self::json($list);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return false|string\\n\"\n    \"     */\\n\"\n    \"    public static function handlerGetCoroutineList(Server $server, string $msg)\\n\"\n    \"    {\\n\"\n    \"        $list = [];\\n\"\n    \"        foreach (Coroutine::list() as $cid) {\\n\"\n    \"            $list[] = [\\n\"\n    \"                'id'          => $cid,\\n\"\n    \"                'elapsed'     => Coroutine::getElapsed($cid),\\n\"\n    \"                'stack_usage' => Coroutine::getStackUsage($cid),\\n\"\n    \"                'backTrace'   => Coroutine::getBackTrace($cid, DEBUG_BACKTRACE_IGNORE_ARGS, 1),\\n\"\n    \"            ];\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return self::json($list);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function handlerGetObjects(Server $server, string $msg)\\n\"\n    \"    {\\n\"\n    \"        $list    = [];\\n\"\n    \"        $objects = swoole_get_objects();\\n\"\n    \"        foreach ($objects as $o) {\\n\"\n    \"            $class_name = $o::class;\\n\"\n    \"            $class      = new \\\\ReflectionClass($class_name);\\n\"\n    \"            $filename   = $class->getFileName();\\n\"\n    \"            $line       = $class->getStartLine();\\n\"\n    \"            $list[]     = [\\n\"\n    \"                'id'          => spl_object_id($o),\\n\"\n    \"                'hash'        => spl_object_hash($o),\\n\"\n    \"                'class'       => $class_name,\\n\"\n    \"                'filename'    => $filename ?: '',\\n\"\n    \"                'line'        => $line ?: '',\\n\"\n    \"                'memory_size' => self::getObjectMemorySize($o),\\n\"\n    \"            ];\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return self::json($list);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function handlerGetClassInfo(Server $server, string $msg)\\n\"\n    \"    {\\n\"\n    \"        $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\\n\"\n    \"        if (!empty($json['class_name'])) {\\n\"\n    \"            if (!class_exists($json['class_name'], false) && !interface_exists($json['class_name'], false)) {\\n\"\n    \"                return self::json(\\\"{$json['class_name']} not exists\\\", 4003);\\n\"\n    \"            }\\n\"\n    \"            $name = $json['class_name'];\\n\"\n    \"        } elseif (!empty($json['interface_name'])) {\\n\"\n    \"            if (!interface_exists($json['interface_name'], false)) {\\n\"\n    \"                return self::json(\\\"{$json['interface_name']} not exists\\\", 4003);\\n\"\n    \"            }\\n\"\n    \"            $name = $json['interface_name'];\\n\"\n    \"        } else {\\n\"\n    \"            return self::json(['error' => 'require class_name or interface_name'], 4004);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $class = new \\\\ReflectionClass($name);\\n\"\n    \"\\n\"\n    \"        $filename = $class->getFileName();\\n\"\n    \"\\n\"\n    \"        $getTmpConstants = function ($data) {\\n\"\n    \"            $tmp = [];\\n\"\n    \"            foreach ($data as $k => $v) {\\n\"\n    \"                $tmp[] = [\\n\"\n    \"                    'name'  => $k,\\n\"\n    \"                    'value' => is_array($v) ? var_export($v, true) : $v,\\n\"\n    \"                    'type'  => is_array($v) ? 'detail' : 'default',\\n\"\n    \"                ];\\n\"\n    \"            }\\n\"\n    \"            return $tmp;\\n\"\n    \"        };\\n\"\n    \"\\n\"\n    \"        $tmpConstants = $class->getConstants();\\n\"\n    \"        $constants    = $tmpConstants ? $getTmpConstants($tmpConstants) : [];\\n\"\n    \"\\n\"\n    \"        $staticProperties = [];\\n\"\n    \"        $properties       = [];\\n\"\n    \"        $tmpProperties    = $class->getProperties();\\n\"\n    \"\\n\"\n    \"        $getTmpProperties = function ($class, $data) {\\n\"\n    \"            $static            = [];\\n\"\n    \"            $noStatic          = [];\\n\"\n    \"            $defaultProperties = $class->getDefaultProperties();\\n\"\n    \"            foreach ($data as $k => $v) {\\n\"\n    \"                $name      = $v->getName();\\n\"\n    \"                $modifiers = \\\\Reflection::getModifierNames($v->getModifiers());\\n\"\n    \"                if ($v->isStatic()) {\\n\"\n    \"                    $static[] = [\\n\"\n    \"                        'name'      => $name,\\n\"\n    \"                        'value'     => $defaultProperties[$name],\\n\"\n    \"                        'modifiers' => implode(' ', $modifiers),\\n\"\n    \"                    ];\\n\"\n    \"                } else {\\n\"\n    \"                    $noStatic[] = [\\n\"\n    \"                        'name'      => $name,\\n\"\n    \"                        'value'     => $defaultProperties[$name],\\n\"\n    \"                        'modifiers' => implode(' ', $modifiers),\\n\"\n    \"                    ];\\n\"\n    \"                }\\n\"\n    \"            }\\n\"\n    \"            return ['static' => $static, 'no_static' => $noStatic];\\n\"\n    \"        };\\n\"\n    \"\\n\"\n    \"        if ($tmpProperties) {\\n\"\n    \"            $tmpProperties    = $getTmpProperties($class, $tmpProperties);\\n\"\n    \"            $staticProperties = $tmpProperties['static'];\\n\"\n    \"            $properties       = $tmpProperties['no_static'];\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $staticMethods    = [];\\n\"\n    \"        $methods          = [];\\n\"\n    \"        $tmpStaticMethods = $class->getMethods();\\n\"\n    \"\\n\"\n    \"        $getTmpMethods = function ($data) {\\n\"\n    \"            $static   = [];\\n\"\n    \"            $noStatic = [];\\n\"\n    \"            foreach ($data as $k => $v) {\\n\"\n    \"                $name      = $v->getName();\\n\"\n    \"                $line      = $v->getStartLine();\\n\"\n    \"                $modifiers = \\\\Reflection::getModifierNames($v->getModifiers());\\n\"\n    \"                if ($v->isStatic()) {\\n\"\n    \"                    $static[] = [\\n\"\n    \"                        'name'      => $name,\\n\"\n    \"                        'line'      => $line ?: '',\\n\"\n    \"                        'modifiers' => implode(' ', $modifiers),\\n\"\n    \"                    ];\\n\"\n    \"                } else {\\n\"\n    \"                    $noStatic[] = [\\n\"\n    \"                        'name'      => $name,\\n\"\n    \"                        'line'      => $line ?: '',\\n\"\n    \"                        'modifiers' => implode(' ', $modifiers),\\n\"\n    \"                    ];\\n\"\n    \"                }\\n\"\n    \"            }\\n\"\n    \"            return ['static' => $static, 'no_static' => $noStatic];\\n\"\n    \"        };\\n\"\n    \"\\n\"\n    \"        if ($tmpStaticMethods) {\\n\"\n    \"            $tmpStaticMethods = $getTmpMethods($tmpStaticMethods);\\n\"\n    \"            $staticMethods    = $tmpStaticMethods['static'];\\n\"\n    \"            $methods          = $tmpStaticMethods['no_static'];\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $tmpParentClass = $class->getParentClass();\\n\"\n    \"        $parentClass    = $tmpParentClass ? $tmpParentClass->getName() : '';\\n\"\n    \"\\n\"\n    \"        $data = [\\n\"\n    \"            'filename'         => $filename,\\n\"\n    \"            'constants'        => $constants,\\n\"\n    \"            'staticProperties' => $staticProperties,\\n\"\n    \"            'properties'       => $properties,\\n\"\n    \"            'staticMethods'    => $staticMethods,\\n\"\n    \"            'methods'          => $methods,\\n\"\n    \"            'parentClass'      => $parentClass,\\n\"\n    \"            'interface'        => $class->getInterfaceNames(),\\n\"\n    \"        ];\\n\"\n    \"        return self::json($data);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function handlerGetFunctionInfo(Server $server, string $msg)\\n\"\n    \"    {\\n\"\n    \"        $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\\n\"\n    \"\\n\"\n    \"        $className    = $json['class_name'] ?? '';\\n\"\n    \"        $functionName = $json['function_name'] ?? '';\\n\"\n    \"\\n\"\n    \"        if (empty($json) || empty($functionName)) {\\n\"\n    \"            return self::json('require function_name', 4004);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $isStatic = false;\\n\"\n    \"        if (!empty($className)) {\\n\"\n    \"            if (!class_exists($className) && !interface_exists($className)) {\\n\"\n    \"                return self::json(\\\"{$className} not exists\\\", 4004);\\n\"\n    \"            }\\n\"\n    \"            if (!method_exists($className, $functionName)) {\\n\"\n    \"                return self::json(\\\"{$className}->{$functionName} not exists\\\", 4004);\\n\"\n    \"            }\\n\"\n    \"            $ref      = new \\\\ReflectionMethod($className, $functionName);\\n\"\n    \"            $isStatic = $ref->isStatic();\\n\"\n    \"        } else {\\n\"\n    \"            if (!function_exists($functionName)) {\\n\"\n    \"                return self::json(\\\"{$functionName} not exists\\\", 4004);\\n\"\n    \"            }\\n\"\n    \"            $ref = new \\\\ReflectionFunction($functionName);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $result = [\\n\"\n    \"            'filename'     => $ref->getFileName(),\\n\"\n    \"            'line'         => $ref->getStartLine() ?: '',\\n\"\n    \"            'num'          => $ref->getNumberOfParameters(),\\n\"\n    \"            'user_defined' => $ref->isUserDefined(),\\n\"\n    \"            'extension'    => $ref->getExtensionName(),\\n\"\n    \"            'is_static'    => $isStatic,\\n\"\n    \"        ];\\n\"\n    \"\\n\"\n    \"        $params = $ref->getParameters();\\n\"\n    \"\\n\"\n    \"        $list = [];\\n\"\n    \"        foreach ($params as $param) {\\n\"\n    \"            $type = $optional = $default = '';\\n\"\n    \"\\n\"\n    \"            $paramName = $param->getName();\\n\"\n    \"\\n\"\n    \"            if ($param->hasType()) {\\n\"\n    \"                /** @var \\\\ReflectionNamedType|\\\\ReflectionUnionType $reflection */\\n\"\n    \"                $reflection = $param->getType();\\n\"\n    \"                if ($reflection instanceof \\\\ReflectionUnionType) {\\n\"\n    \"                    $unionType = [];\\n\"\n    \"                    foreach ($reflection->getTypes() as $objType) {\\n\"\n    \"                        $unionType[] = $objType->getName();\\n\"\n    \"                    }\\n\"\n    \"                    $type = implode('|', $unionType);\\n\"\n    \"                } else {\\n\"\n    \"                    $type = $reflection->getName();\\n\"\n    \"                }\\n\"\n    \"            }\\n\"\n    \"\\n\"\n    \"            if ($param->isOptional() && !$param->isVariadic()) {\\n\"\n    \"                $optional = '?';\\n\"\n    \"                if ($param->isDefaultValueAvailable()) {\\n\"\n    \"                    $value = $param->getDefaultValue();\\n\"\n    \"                    if (in_array($value, [true, false, null, ''])) {\\n\"\n    \"                        if ($value === null) {\\n\"\n    \"                            $value = 'null';\\n\"\n    \"                        }\\n\"\n    \"                        if ($value === true) {\\n\"\n    \"                            $value = 'true';\\n\"\n    \"                        }\\n\"\n    \"                        if ($value === false) {\\n\"\n    \"                            $value = 'false';\\n\"\n    \"                        }\\n\"\n    \"                        if ($value === '') {\\n\"\n    \"                            $value = \\\"''\\\";\\n\"\n    \"                        }\\n\"\n    \"                    }\\n\"\n    \"                    $default = \\\" = {$value}\\\";\\n\"\n    \"                }\\n\"\n    \"            }\\n\"\n    \"\\n\"\n    \"            $isPassedByReference = $param->isPassedByReference() ? '&' : '';\\n\"\n    \"            $isVariadic          = $param->isVariadic() ? '...' : '';\\n\"\n    \"\\n\"\n    \"            $option = \\\"{$optional}{$type} {$isPassedByReference}{$isVariadic}\\\";\\n\"\n    \"            $param  = \\\"\\\\${$paramName}{$default}\\\";\\n\"\n    \"\\n\"\n    \"            $list[] = [\\n\"\n    \"                'optional'               => $optional,\\n\"\n    \"                'type'                   => $type,\\n\"\n    \"                'is_passed_by_reference' => $isPassedByReference,\\n\"\n    \"                'is_variadic'            => $isVariadic,\\n\"\n    \"                'name'                   => $paramName,\\n\"\n    \"                'default'                => $default,\\n\"\n    \"                'full'                   => $option !== ' ' ? \\\"{$option}{$param}\\\" : $param,\\n\"\n    \"            ];\\n\"\n    \"        }\\n\"\n    \"        $result['params'] = $list;\\n\"\n    \"\\n\"\n    \"        return self::json($result);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function handlerGetObjectByHandle(Server $server, string $msg)\\n\"\n    \"    {\\n\"\n    \"        $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\\n\"\n    \"        if (empty($json) || empty($json['object_id']) || empty($json['object_hash'])) {\\n\"\n    \"            return self::json(['error' => 'Params Error!'], 4004);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $object = swoole_get_object_by_handle((int) $json['object_id']);\\n\"\n    \"        if (!$object) {\\n\"\n    \"            return self::json(['error' => 'Object destroyed!'], 4004);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $object_hash = spl_object_hash($object); // @phpstan-ignore argument.type\\n\"\n    \"        if ($object_hash != $json['object_hash']) {\\n\"\n    \"            return self::json(['error' => 'Object destroyed!'], 4004);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return self::json(var_export($object, true));\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function handlerGetVersionInfo(Server $server, string $msg)\\n\"\n    \"    {\\n\"\n    \"        $ip_arr = swoole_get_local_ip();\\n\"\n    \"        $host   = [];\\n\"\n    \"        $local  = [];\\n\"\n    \"        foreach ($ip_arr as $k => $ip) {\\n\"\n    \"            if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {\\n\"\n    \"                $host[] = $ip;\\n\"\n    \"            } else {\\n\"\n    \"                $local[] = $ip;\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"        $data = [\\n\"\n    \"            'os'     => php_uname('s') . '-' . php_uname('r'),\\n\"\n    \"            'swoole' => swoole_version(),\\n\"\n    \"            'php'    => phpversion(),\\n\"\n    \"            'ip'     => $host ? $host[0] : $local[0],\\n\"\n    \"        ];\\n\"\n    \"        return self::json($data);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function handlerGetDefinedFunctions(Server $server, string $msg)\\n\"\n    \"    {\\n\"\n    \"        $functions = get_defined_functions();\\n\"\n    \"        $arr       = [\\n\"\n    \"            'internal' => $functions['internal'],\\n\"\n    \"        ];\\n\"\n    \"        if (!empty($functions['user'])) {\\n\"\n    \"            foreach ($functions['user'] as $function_name) {\\n\"\n    \"                $function      = new \\\\ReflectionFunction($function_name);\\n\"\n    \"                $filename      = $function->getFileName();\\n\"\n    \"                $line          = $function->getStartLine();\\n\"\n    \"                $arr['user'][] = [\\n\"\n    \"                    'function' => $function_name,\\n\"\n    \"                    'filename' => $filename,\\n\"\n    \"                    'line'     => $line,\\n\"\n    \"                ];\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"        return self::json($arr);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function handlerGetDeclaredClasses(Server $server, string $msg)\\n\"\n    \"    {\\n\"\n    \"        $classes = get_declared_classes();\\n\"\n    \"        $arr     = [];\\n\"\n    \"        if ($classes) {\\n\"\n    \"            foreach ($classes as $classes_name) {\\n\"\n    \"                $function = new \\\\ReflectionClass($classes_name);\\n\"\n    \"                $filename = $function->getFileName();\\n\"\n    \"                $line     = $function->getStartLine();\\n\"\n    \"                $arr[]    = [\\n\"\n    \"                    'class'    => $classes_name,\\n\"\n    \"                    'filename' => $filename ?: '',\\n\"\n    \"                    'line'     => $line ?: '',\\n\"\n    \"                ];\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"        return self::json($arr);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function handlerGetServerMemoryUsage(Server $server, string $msg)\\n\"\n    \"    {\\n\"\n    \"        $total = 0;\\n\"\n    \"\\n\"\n    \"        $result['master'] = $result['manager'] = 0;\\n\"\n    \"        if (self::haveMasterProcess($server)) {\\n\"\n    \"            $result['master'] = self::getProcessMemoryRealUsage($server->master_pid);\\n\"\n    \"        }\\n\"\n    \"        $total += $result['master'];\\n\"\n    \"\\n\"\n    \"        if (self::haveManagerProcess($server)) {\\n\"\n    \"            $result['manager'] = self::getProcessMemoryRealUsage($server->manager_pid);\\n\"\n    \"        }\\n\"\n    \"        $total += $result['manager'];\\n\"\n    \"\\n\"\n    \"        $n = $server->setting['worker_num'] + $server->setting['task_worker_num'];\\n\"\n    \"        /** @var int $n */\\n\"\n    \"        for ($i = 0; $i < $n; $i++) {\\n\"\n    \"            $key          = 'worker-' . $i;\\n\"\n    \"            $result[$key] = self::getProcessMemoryRealUsage($server->getWorkerPid($i));\\n\"\n    \"            $total += $result[$key];\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $result['total'] = $total;\\n\"\n    \"\\n\"\n    \"        $result['memory_size'] = 0;\\n\"\n    \"        // TODO: Support other OS\\n\"\n    \"        if (PHP_OS_FAMILY === 'Linux') {\\n\"\n    \"            preg_match('#MemTotal:\\\\s+(\\\\d+) kB#i', file_get_contents('/proc/meminfo'), $match);\\n\"\n    \"            $result['memory_size'] = intval($match[1]) * 1024;\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return self::json($result);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function handlerGetServerCpuUsage(Server $server, string $msg)\\n\"\n    \"    {\\n\"\n    \"        $total = 0;\\n\"\n    \"\\n\"\n    \"        $result['master'] = $result['manager'] = 0;\\n\"\n    \"        if (self::haveMasterProcess($server)) {\\n\"\n    \"            $result['master'] = self::getProcessCpuUsage($server->master_pid);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $total += $result['master'][1] ?? 0;\\n\"\n    \"\\n\"\n    \"        if (self::haveManagerProcess($server)) {\\n\"\n    \"            $result['manager'] = self::getProcessCpuUsage($server->manager_pid);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $total += $result['manager'][1] ?? 0;\\n\"\n    \"\\n\"\n    \"        $n = $server->setting['worker_num'] + $server->setting['task_worker_num'];\\n\"\n    \"        /** @var int $n */\\n\"\n    \"        for ($i = 0; $i < $n; $i++) {\\n\"\n    \"            $key          = 'worker-' . $i;\\n\"\n    \"            $result[$key] = self::getProcessCpuUsage($server->getWorkerPid($i))[1] ?? 0;\\n\"\n    \"            $total += $result[$key];\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $result['total']   = $total;\\n\"\n    \"        $result['cpu_num'] = swoole_cpu_num();\\n\"\n    \"\\n\"\n    \"        return self::json($result);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function handlerGetStaticPropertyValue(Server $server, string $msg)\\n\"\n    \"    {\\n\"\n    \"        $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\\n\"\n    \"        if (empty($json['class_name'])) {\\n\"\n    \"            return self::json(['error' => 'require class_name!'], 4004);\\n\"\n    \"        }\\n\"\n    \"        if (empty($json['property_name'])) {\\n\"\n    \"            return self::json(['error' => 'require property_name!'], 4004);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $className    = $json['class_name'];\\n\"\n    \"        $propertyName = $json['property_name'];\\n\"\n    \"\\n\"\n    \"        if (!class_exists($className)) {\\n\"\n    \"            return self::json(\\\"class[{$className}] not exists\\\", 4004);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $reflection = new \\\\ReflectionClass($className);\\n\"\n    \"        $value      = $reflection->getStaticPropertyValue($propertyName, []);\\n\"\n    \"\\n\"\n    \"        $result = [\\n\"\n    \"            'value' => var_export($value, true),\\n\"\n    \"        ];\\n\"\n    \"        return self::json($result);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private static function handlerMulti(Server $server, array $list)\\n\"\n    \"    {\\n\"\n    \"        $return_list = [];\\n\"\n    \"        foreach ($list as $key => $content) {\\n\"\n    \"            $path_array = swoole_string($content['path'])->trim('/')->split('/');\\n\"\n    \"            $cmd        = $path_array->get(1)->toString();\\n\"\n    \"\\n\"\n    \"            if ($path_array->count() == 2) {\\n\"\n    \"                $process = swoole_string('master');\\n\"\n    \"            } else {\\n\"\n    \"                $process = $path_array->get(2);\\n\"\n    \"            }\\n\"\n    \"\\n\"\n    \"            $data      = [];\\n\"\n    \"            $url_query = parse_url($process->toString(), PHP_URL_QUERY) ?? [];\\n\"\n    \"            if (!empty($url_query)) {\\n\"\n    \"                parse_str($url_query, $data);\\n\"\n    \"            }\\n\"\n    \"            $data = array_merge($data, $content['post'] ?? []);\\n\"\n    \"\\n\"\n    \"            if ($process->startsWith('master')) {\\n\"\n    \"                $process_type = SWOOLE_SERVER_COMMAND_MASTER;\\n\"\n    \"                $process_id   = 0;\\n\"\n    \"            } elseif ($process->startsWith('manager')) {\\n\"\n    \"                $process_type = SWOOLE_SERVER_COMMAND_MANAGER;\\n\"\n    \"                $process_id   = 0;\\n\"\n    \"            } elseif ($process->startsWith('all') || $process->startsWith('specific')) {\\n\"\n    \"                if (!in_array($process->toString(), self::$allList) && !$process->startsWith('specific')) {\\n\"\n    \"                    $return_list[$key] = json_decode('{}');\\n\"\n    \"                    continue;\\n\"\n    \"                }\\n\"\n    \"\\n\"\n    \"                $result = self::handlerGetAll($server, $process, $cmd, $data);\\n\"\n    \"\\n\"\n    \"                $return_list[$key] = ['code' => 0, 'data' => $result];\\n\"\n    \"                continue;\\n\"\n    \"            } else {\\n\"\n    \"                $array = $process->split('-');\\n\"\n    \"\\n\"\n    \"                if ($array->count() != 2 || !isset(self::$map[$array->get(0)->toString()])) {\\n\"\n    \"                    $return_list[$key] = json_decode('{}');\\n\"\n    \"                    continue;\\n\"\n    \"                }\\n\"\n    \"\\n\"\n    \"                $process_type = self::$map[$array->get(0)->toString()];\\n\"\n    \"                $process_id   = intval($array->get(1)->toString());\\n\"\n    \"            }\\n\"\n    \"\\n\"\n    \"            $return_list[$key] = $server->command($cmd, $process_id, intval($process_type), $data, true);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return $return_list;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private static function handlerGetAll(Server $server, StringObject $process, $cmd, $data, bool $json_decode = true)\\n\"\n    \"    {\\n\"\n    \"        if ($process->equals('all')) {\\n\"\n    \"            $result = self::handlerGetMaster($cmd, $data, $server, $json_decode) +\\n\"\n    \"                self::handlerGetManager($cmd, $data, $server, $json_decode) +\\n\"\n    \"                self::handlerGetAllWorker($cmd, $data, $server, $json_decode) +\\n\"\n    \"                self::handlerGetAllTaskWorker($cmd, $data, $server, $json_decode);\\n\"\n    \"        } elseif ($process->startsWith('all_reactor')) {\\n\"\n    \"            $result = self::handlerGetAllReactor($cmd, $data, $server, $json_decode);\\n\"\n    \"        } elseif ($process->equals('all_worker') || $process->equals('all_event_worker')) {\\n\"\n    \"            $result = self::handlerGetAllWorker($cmd, $data, $server, $json_decode);\\n\"\n    \"        } elseif ($process->startsWith('all_task')) {\\n\"\n    \"            $result = self::handlerGetAllTaskWorker($cmd, $data, $server, $json_decode);\\n\"\n    \"        } else {\\n\"\n    \"            // specific\\n\"\n    \"            $result = [];\\n\"\n    \"            if (!empty($data['workers']) && is_array($data['workers'])) {\\n\"\n    \"                foreach ($data['workers'] as $name) {\\n\"\n    \"                    $process = swoole_string($name);\\n\"\n    \"                    if ($process->startsWith('master')) {\\n\"\n    \"                        $result += self::handlerGetMaster($cmd, $data, $server, $json_decode);\\n\"\n    \"                    } elseif ($process->startsWith('manager')) {\\n\"\n    \"                        $result += self::handlerGetManager($cmd, $data, $server, $json_decode);\\n\"\n    \"                    } else {\\n\"\n    \"                        $array = $process->split('-');\\n\"\n    \"                        if ($array->count() != 2 || !isset(self::$map[$array->get(0)->toString()])) {\\n\"\n    \"                            $result[$name] = $json_decode ? json_decode('{}') : $json_decode;\\n\"\n    \"                        } else {\\n\"\n    \"                            $process_type  = self::$map[$array->get(0)->toString()];\\n\"\n    \"                            $process_id    = intval($array->get(1)->toString());\\n\"\n    \"                            $result[$name] = $server->command($cmd, $process_id, $process_type, $data, $json_decode);\\n\"\n    \"                        }\\n\"\n    \"                    }\\n\"\n    \"                }\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return $result;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private static function handlerGetMaster($cmd, $data, Server $server, bool $json_decode = false)\\n\"\n    \"    {\\n\"\n    \"        $list['master'] = $server->command($cmd, 0, SWOOLE_SERVER_COMMAND_MASTER, $data, $json_decode);\\n\"\n    \"        return $list;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private static function handlerGetManager($cmd, $data, Server $server, bool $json_decode = false)\\n\"\n    \"    {\\n\"\n    \"        $list['manager'] = $server->command($cmd, 0, SWOOLE_SERVER_COMMAND_MANAGER, $data, $json_decode);\\n\"\n    \"        return $list;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private static function handlerGetAllReactor($cmd, $data, Server $server, bool $json_decode = false)\\n\"\n    \"    {\\n\"\n    \"        $list = [];\\n\"\n    \"        if ($server->mode === SWOOLE_BASE) {\\n\"\n    \"            return $list;\\n\"\n    \"        }\\n\"\n    \"        $process_type = SWOOLE_SERVER_COMMAND_REACTOR_THREAD;\\n\"\n    \"        if (empty($server->setting['reactor_num'])) {\\n\"\n    \"            $reactor_num = $server->setting['worker_num'];\\n\"\n    \"        } else {\\n\"\n    \"            $reactor_num = $server->setting['reactor_num'];\\n\"\n    \"        }\\n\"\n    \"        for ($process_id = 0; $process_id < $reactor_num; $process_id++) {\\n\"\n    \"            $list[\\\"reactor-{$process_id}\\\"] = $server->command($cmd, $process_id, $process_type, $data, $json_decode);\\n\"\n    \"        }\\n\"\n    \"        return $list;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private static function handlerGetAllWorker($cmd, $data, Server $server, bool $json_decode = false)\\n\"\n    \"    {\\n\"\n    \"        $process_type = SWOOLE_SERVER_COMMAND_EVENT_WORKER;\\n\"\n    \"        $worker_num   = $server->setting['worker_num'];\\n\"\n    \"        $list         = [];\\n\"\n    \"        for ($process_id = 0; $process_id < $worker_num; $process_id++) {\\n\"\n    \"            $list[\\\"worker-{$process_id}\\\"] = $server->command($cmd, $process_id, $process_type, $data, $json_decode);\\n\"\n    \"        }\\n\"\n    \"        return $list;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private static function handlerGetAllTaskWorker($cmd, $data, Server $server, bool $json_decode = false)\\n\"\n    \"    {\\n\"\n    \"        $process_type = SWOOLE_SERVER_COMMAND_TASK_WORKER;\\n\"\n    \"        $list         = [];\\n\"\n    \"        if (empty($server->setting['task_worker_num'])) {\\n\"\n    \"            return $list;\\n\"\n    \"        }\\n\"\n    \"        $task_worker_num = $server->setting['task_worker_num'];\\n\"\n    \"        for ($process_id = 0; $process_id < $task_worker_num; $process_id++) {\\n\"\n    \"            $list[\\\"task_worker-{$process_id}\\\"] = $server->command($cmd, $process_id, $process_type, $data, $json_decode);\\n\"\n    \"        }\\n\"\n    \"        return $list;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private static function getProcessCpuUsage($pid)\\n\"\n    \"    {\\n\"\n    \"        // TODO: Support other OS\\n\"\n    \"        if (PHP_OS_FAMILY !== 'Linux' || !file_exists(\\\"/proc/{$pid}/stat\\\")) {\\n\"\n    \"            return [0];\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $statAll  = file_get_contents('/proc/stat');\\n\"\n    \"        $statProc = file_get_contents(\\\"/proc/{$pid}/stat\\\");\\n\"\n    \"\\n\"\n    \"        $dataAll = preg_split(\\\"/[ \\\\t]+/\\\", $statAll, 6);\\n\"\n    \"        assert($dataAll[0] === 'cpu', '/proc/stat malformed');\\n\"\n    \"        $dataProc = preg_split(\\\"/[ \\\\t]+/\\\", $statProc, 15);\\n\"\n    \"\\n\"\n    \"        if (isset($dataProc[13]) and isset($dataProc[14])) {\\n\"\n    \"            return [\\n\"\n    \"                (int) $dataAll[1] + (int) $dataAll[2] + (int) $dataAll[3] + (int) $dataAll[4],\\n\"\n    \"                (int) $dataProc[13] + (int) $dataProc[14],\\n\"\n    \"            ];\\n\"\n    \"        }\\n\"\n    \"        return [(int) $dataAll[1] + (int) $dataAll[2] + (int) $dataAll[3] + (int) $dataAll[4]];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private static function getProcessMemoryRealUsage($pid = 'self')\\n\"\n    \"    {\\n\"\n    \"        $status = self::getProcessStatus($pid);\\n\"\n    \"        if (!is_array($status) || !isset($status['VmRSS'])) {\\n\"\n    \"            return 0;\\n\"\n    \"        }\\n\"\n    \"        return intval($status['VmRSS']) * 1024;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private static function getProcessStatus($pid = 'self')\\n\"\n    \"    {\\n\"\n    \"        $array = [];\\n\"\n    \"        // TODO: Support other OS\\n\"\n    \"        if (PHP_OS_FAMILY !== 'Linux' || !file_exists(\\\"/proc/{$pid}/status\\\")) {\\n\"\n    \"            return $array;\\n\"\n    \"        }\\n\"\n    \"        $status = swoole_string(trim(file_get_contents(\\\"/proc/{$pid}/status\\\")));\\n\"\n    \"        $lines  = $status->split(\\\"\\\\n\\\");\\n\"\n    \"        foreach ($lines as $l) {\\n\"\n    \"            if (empty($l)) {\\n\"\n    \"                continue;\\n\"\n    \"            }\\n\"\n    \"            [$k, $v]   = swoole_string($l)->split(':');\\n\"\n    \"            $array[$k] = trim($v);\\n\"\n    \"        }\\n\"\n    \"        return $array;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private static function getArrayMemorySize(array $a): int\\n\"\n    \"    {\\n\"\n    \"        $size = self::SIZE_OF_ZVAL + self::SIZE_OF_ZEND_ARRAY;\\n\"\n    \"        foreach ($a as $k => $v) {\\n\"\n    \"            if (is_string($k)) {\\n\"\n    \"                $size += self::getStringMemorySize($k);\\n\"\n    \"            } else {\\n\"\n    \"                $size += self::SIZE_OF_ZVAL;\\n\"\n    \"            }\\n\"\n    \"            if (is_string($v)) {\\n\"\n    \"                $size += self::getStringMemorySize($v);\\n\"\n    \"            } elseif (is_array($v)) {\\n\"\n    \"                $size += self::getArrayMemorySize($v);\\n\"\n    \"            } else {\\n\"\n    \"                $size += self::SIZE_OF_ZVAL;\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"        return $size;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private static function getStringMemorySize(string $s): int\\n\"\n    \"    {\\n\"\n    \"        return self::SIZE_OF_ZVAL + self::SIZE_OF_ZEND_STRING + strlen($s);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private static function getObjectMemorySize(object $o): int\\n\"\n    \"    {\\n\"\n    \"        $vars = get_object_vars($o);\\n\"\n    \"        $size = self::SIZE_OF_ZEND_OBJECT;\\n\"\n    \"\\n\"\n    \"        foreach ($vars as $v) {\\n\"\n    \"            if (is_array($v)) {\\n\"\n    \"                $size += self::getArrayMemorySize($v);\\n\"\n    \"            } elseif (is_string($v)) {\\n\"\n    \"                $size += self::getStringMemorySize($v);\\n\"\n    \"            } else {\\n\"\n    \"                $size += self::SIZE_OF_ZVAL;\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return $size;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private static function haveMasterProcess(Server $server): bool\\n\"\n    \"    {\\n\"\n    \"        if ($server->mode === SWOOLE_BASE) {\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return true;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private static function haveManagerProcess(Server $server): bool\\n\"\n    \"    {\\n\"\n    \"        if ($server->mode === SWOOLE_BASE && $server->getManagerPid() === 0) {\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        return true;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private static function json($data, $code = 0)\\n\"\n    \"    {\\n\"\n    \"        $result = json_encode(['code' => $code, 'data' => $data], JSON_INVALID_UTF8_IGNORE);\\n\"\n    \"        if (empty($result)) {\\n\"\n    \"            return json_encode([\\n\"\n    \"                'code' => 5010,\\n\"\n    \"                'data' => ['message' => json_last_error_msg(), 'code' => json_last_error()],\\n\"\n    \"            ]);\\n\"\n    \"        }\\n\"\n    \"        return $result;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_server_helper =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Server;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Constant;\\n\"\n    \"use Swoole\\\\Coroutine;\\n\"\n    \"use Swoole\\\\Server;\\n\"\n    \"use Swoole\\\\Timer;\\n\"\n    \"\\n\"\n    \"class Helper\\n\"\n    \"{\\n\"\n    \"    public const STATS_TIMER_INTERVAL_TIME = 1000;\\n\"\n    \"\\n\"\n    \"    public const GLOBAL_OPTIONS = [\\n\"\n    \"        'debug_mode'                   => true,\\n\"\n    \"        'trace_flags'                  => true,\\n\"\n    \"        'log_file'                     => true,\\n\"\n    \"        'log_level'                    => true,\\n\"\n    \"        'log_date_format'              => true,\\n\"\n    \"        'log_date_with_microseconds'   => true,\\n\"\n    \"        'log_rotation'                 => true,\\n\"\n    \"        'display_errors'               => true,\\n\"\n    \"        'dns_server'                   => true,\\n\"\n    \"        'socket_dns_timeout'           => true,\\n\"\n    \"        'socket_connect_timeout'       => true,\\n\"\n    \"        'socket_write_timeout'         => true,\\n\"\n    \"        'socket_send_timeout'          => true,\\n\"\n    \"        'socket_read_timeout'          => true,\\n\"\n    \"        'socket_recv_timeout'          => true,\\n\"\n    \"        'socket_buffer_size'           => true,\\n\"\n    \"        'socket_timeout'               => true,\\n\"\n    \"        'http2_header_table_size'      => true,\\n\"\n    \"        'http2_enable_push'            => true,\\n\"\n    \"        'http2_max_concurrent_streams' => true,\\n\"\n    \"        'http2_init_window_size'       => true,\\n\"\n    \"        'http2_max_frame_size'         => true,\\n\"\n    \"        'http2_max_header_list_size'   => true,\\n\"\n    \"    ];\\n\"\n    \"\\n\"\n    \"    public const SERVER_OPTIONS = [\\n\"\n    \"        'chroot'                      => true,\\n\"\n    \"        'user'                        => true,\\n\"\n    \"        'group'                       => true,\\n\"\n    \"        'daemonize'                   => true,\\n\"\n    \"        'pid_file'                    => true,\\n\"\n    \"        'reactor_num'                 => true,\\n\"\n    \"        'single_thread'               => true,\\n\"\n    \"        'worker_num'                  => true,\\n\"\n    \"        'max_wait_time'               => true,\\n\"\n    \"        'max_queued_bytes'            => true,\\n\"\n    \"        'max_concurrency'             => true,\\n\"\n    \"        'worker_max_concurrency'      => true,\\n\"\n    \"        'enable_coroutine'            => true,\\n\"\n    \"        'send_timeout'                => true,\\n\"\n    \"        'dispatch_mode'               => true,\\n\"\n    \"        'send_yield'                  => true,\\n\"\n    \"        'dispatch_func'               => true,\\n\"\n    \"        'discard_timeout_request'     => true,\\n\"\n    \"        'enable_unsafe_event'         => true,\\n\"\n    \"        'enable_delay_receive'        => true,\\n\"\n    \"        'enable_reuse_port'           => true,\\n\"\n    \"        'task_use_object'             => true,\\n\"\n    \"        'task_object'                 => true,\\n\"\n    \"        'event_object'                => true,\\n\"\n    \"        'task_enable_coroutine'       => true,\\n\"\n    \"        'task_worker_num'             => true,\\n\"\n    \"        'task_ipc_mode'               => true,\\n\"\n    \"        'task_tmpdir'                 => true,\\n\"\n    \"        'task_max_request'            => true,\\n\"\n    \"        'task_max_request_grace'      => true,\\n\"\n    \"        'max_connection'              => true,\\n\"\n    \"        'max_conn'                    => true,\\n\"\n    \"        'start_session_id'            => true,\\n\"\n    \"        'heartbeat_check_interval'    => true,\\n\"\n    \"        'heartbeat_idle_time'         => true,\\n\"\n    \"        'max_request'                 => true,\\n\"\n    \"        'max_request_grace'           => true,\\n\"\n    \"        'reload_async'                => true,\\n\"\n    \"        'open_cpu_affinity'           => true,\\n\"\n    \"        'cpu_affinity_ignore'         => true,\\n\"\n    \"        'http_parse_cookie'           => true,\\n\"\n    \"        'http_parse_post'             => true,\\n\"\n    \"        'http_parse_files'            => true,\\n\"\n    \"        'http_compression'            => true,\\n\"\n    \"        'http_compression_level'      => true,\\n\"\n    \"        'compression_level'           => true,\\n\"\n    \"        'http_gzip_level'             => true,\\n\"\n    \"        'http_compression_min_length' => true,\\n\"\n    \"        'compression_min_length'      => true,\\n\"\n    \"        'websocket_compression'       => true,\\n\"\n    \"        'upload_tmp_dir'              => true,\\n\"\n    \"        'upload_max_filesize'         => true,\\n\"\n    \"        'enable_static_handler'       => true,\\n\"\n    \"        'document_root'               => true,\\n\"\n    \"        'http_autoindex'              => true,\\n\"\n    \"        'http_index_files'            => true,\\n\"\n    \"        'http_compression_types'      => true,\\n\"\n    \"        'compression_types'           => true,\\n\"\n    \"        'static_handler_locations'    => true,\\n\"\n    \"        'input_buffer_size'           => true,\\n\"\n    \"        'buffer_input_size'           => true,\\n\"\n    \"        'output_buffer_size'          => true,\\n\"\n    \"        'buffer_output_size'          => true,\\n\"\n    \"        'message_queue_key'           => true,\\n\"\n    \"        'bootstrap'                   => true,\\n\"\n    \"        'init_arguments'              => true,\\n\"\n    \"        'url_rewrite_rules'           => true,\\n\"\n    \"    ];\\n\"\n    \"\\n\"\n    \"    public const PORT_OPTIONS = [\\n\"\n    \"        'ssl_cert_file'                  => true,\\n\"\n    \"        'ssl_key_file'                   => true,\\n\"\n    \"        'backlog'                        => true,\\n\"\n    \"        'socket_buffer_size'             => true,\\n\"\n    \"        'kernel_socket_recv_buffer_size' => true,\\n\"\n    \"        'kernel_socket_send_buffer_size' => true,\\n\"\n    \"        'heartbeat_idle_time'            => true,\\n\"\n    \"        'buffer_high_watermark'          => true,\\n\"\n    \"        'buffer_low_watermark'           => true,\\n\"\n    \"        'open_tcp_nodelay'               => true,\\n\"\n    \"        'tcp_defer_accept'               => true,\\n\"\n    \"        'open_tcp_keepalive'             => true,\\n\"\n    \"        'open_eof_check'                 => true,\\n\"\n    \"        'open_eof_split'                 => true,\\n\"\n    \"        'package_eof'                    => true,\\n\"\n    \"        'open_http_protocol'             => true,\\n\"\n    \"        'open_websocket_protocol'        => true,\\n\"\n    \"        'websocket_subprotocol'          => true,\\n\"\n    \"        'open_websocket_close_frame'     => true,\\n\"\n    \"        'open_websocket_ping_frame'      => true,\\n\"\n    \"        'open_websocket_pong_frame'      => true,\\n\"\n    \"        'open_http2_protocol'            => true,\\n\"\n    \"        'open_mqtt_protocol'             => true,\\n\"\n    \"        'open_redis_protocol'            => true,\\n\"\n    \"        'max_idle_time'                  => true,\\n\"\n    \"        'tcp_keepidle'                   => true,\\n\"\n    \"        'tcp_keepinterval'               => true,\\n\"\n    \"        'tcp_keepcount'                  => true,\\n\"\n    \"        'tcp_user_timeout'               => true,\\n\"\n    \"        'tcp_fastopen'                   => true,\\n\"\n    \"        'open_length_check'              => true,\\n\"\n    \"        'package_length_type'            => true,\\n\"\n    \"        'package_length_offset'          => true,\\n\"\n    \"        'package_body_offset'            => true,\\n\"\n    \"        'package_body_start'             => true,\\n\"\n    \"        'package_length_func'            => true,\\n\"\n    \"        'package_max_length'             => true,\\n\"\n    \"        'ssl_compress'                   => true,\\n\"\n    \"        'ssl_protocols'                  => true,\\n\"\n    \"        'ssl_verify_peer'                => true,\\n\"\n    \"        'ssl_allow_self_signed'          => true,\\n\"\n    \"        'ssl_client_cert_file'           => true,\\n\"\n    \"        'ssl_cafile'                     => true,\\n\"\n    \"        'ssl_capath'                     => true,\\n\"\n    \"        'ssl_verify_depth'               => true,\\n\"\n    \"        'ssl_prefer_server_ciphers'      => true,\\n\"\n    \"        'ssl_ciphers'                    => true,\\n\"\n    \"        'ssl_ecdh_curve'                 => true,\\n\"\n    \"        'ssl_dhparam'                    => true,\\n\"\n    \"        'ssl_sni_certs'                  => true,\\n\"\n    \"    ];\\n\"\n    \"\\n\"\n    \"    public const AIO_OPTIONS = [\\n\"\n    \"        'aio_core_worker_num'    => true,\\n\"\n    \"        'aio_worker_num'         => true,\\n\"\n    \"        'aio_max_wait_time'      => true,\\n\"\n    \"        'aio_max_idle_time'      => true,\\n\"\n    \"        'iouring_entries'        => true,\\n\"\n    \"        'iouring_workers'        => true,\\n\"\n    \"        'iouring_flag'           => true,\\n\"\n    \"        'enable_signalfd'        => true,\\n\"\n    \"        'wait_signal'            => true,\\n\"\n    \"        'dns_cache_refresh_time' => true,\\n\"\n    \"        'thread_num'             => true,\\n\"\n    \"        'min_thread_num'         => true,\\n\"\n    \"        'max_thread_num'         => true,\\n\"\n    \"        'socket_dontwait'        => true,\\n\"\n    \"        'dns_lookup_random'      => true,\\n\"\n    \"        'use_async_resolver'     => true,\\n\"\n    \"        'enable_coroutine'       => true,\\n\"\n    \"    ];\\n\"\n    \"\\n\"\n    \"    public const COROUTINE_OPTIONS = [\\n\"\n    \"        'max_coro_num'                => true,\\n\"\n    \"        'max_coroutine'               => true,\\n\"\n    \"        'enable_deadlock_check'       => true,\\n\"\n    \"        'hook_flags'                  => true,\\n\"\n    \"        'enable_preemptive_scheduler' => true,\\n\"\n    \"        'c_stack_size'                => true,\\n\"\n    \"        'stack_size'                  => true,\\n\"\n    \"        'name_resolver'               => true,\\n\"\n    \"        'dns_cache_expire'            => true,\\n\"\n    \"        'dns_cache_capacity'          => true,\\n\"\n    \"    ];\\n\"\n    \"\\n\"\n    \"    public const HELPER_OPTIONS = [\\n\"\n    \"        'stats_file'           => true,\\n\"\n    \"        'stats_timer_interval' => true,\\n\"\n    \"        'admin_server'         => true,\\n\"\n    \"    ];\\n\"\n    \"\\n\"\n    \"    public static function checkOptions(array $input_options): void\\n\"\n    \"    {\\n\"\n    \"        $const_options = self::GLOBAL_OPTIONS + self::SERVER_OPTIONS + self::PORT_OPTIONS\\n\"\n    \"            + self::AIO_OPTIONS + self::COROUTINE_OPTIONS + self::HELPER_OPTIONS;\\n\"\n    \"\\n\"\n    \"        foreach ($input_options as $k => $v) {\\n\"\n    \"            if (!array_key_exists(strtolower($k), $const_options)) {\\n\"\n    \"                // TODO throw exception\\n\"\n    \"                trigger_error(\\\"unsupported option [{$k}]\\\", E_USER_WARNING);\\n\"\n    \"                debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function onBeforeStart(Server $server): void\\n\"\n    \"    {\\n\"\n    \"        if (!empty($server->setting['admin_server'])) {\\n\"\n    \"            Admin::init($server);\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function onBeforeShutdown(Server $server): void\\n\"\n    \"    {\\n\"\n    \"        if (isset($server->admin_server)) { // @phpstan-ignore isset.property\\n\"\n    \"            $server->admin_server->shutdown();\\n\"\n    \"            $server->admin_server = null; // @phpstan-ignore assign.propertyType\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function onWorkerStart(Server $server, int $workerId): void\\n\"\n    \"    {\\n\"\n    \"        if (!empty($server->setting['stats_file']) and $workerId == 0) {\\n\"\n    \"            $interval_ms = empty($server->setting['stats_timer_interval']) ? self::STATS_TIMER_INTERVAL_TIME : intval($server->setting['stats_timer_interval']);\\n\"\n    \"\\n\"\n    \"            $server->stats_timer = Timer::tick($interval_ms, function () use ($server) {\\n\"\n    \"                $stats      = $server->stats();\\n\"\n    \"                $stats_file = swoole_string($server->setting['stats_file']);\\n\"\n    \"                if ($stats_file->endsWith('.json')) {\\n\"\n    \"                    $out = json_encode($stats, JSON_THROW_ON_ERROR);\\n\"\n    \"                } elseif ($stats_file->endsWith('.php')) {\\n\"\n    \"                    $out = \\\"<?php\\\\nreturn \\\" . var_export($stats, true) . \\\";\\\\n\\\";\\n\"\n    \"                } else {\\n\"\n    \"                    $lines = [];\\n\"\n    \"                    foreach ($stats as $k => $v) {\\n\"\n    \"                        $lines[] = \\\"{$k}: {$v}\\\";\\n\"\n    \"                    }\\n\"\n    \"                    $out = implode(\\\"\\\\n\\\", $lines);\\n\"\n    \"                }\\n\"\n    \"                file_put_contents($server->setting['stats_file'], $out);\\n\"\n    \"            });\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function onWorkerExit(Server $server, int $workerId): void\\n\"\n    \"    {\\n\"\n    \"        if ($server->stats_timer) {\\n\"\n    \"            Timer::clear($server->stats_timer);\\n\"\n    \"            $server->stats_timer = null;\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function onWorkerStop(Server $server, int $workerId)\\n\"\n    \"    {\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function onStart(Server $server): void\\n\"\n    \"    {\\n\"\n    \"        if (!empty($server->setting[Constant::OPTION_ADMIN_SERVER])) {\\n\"\n    \"            Coroutine::create(function () use ($server): void {\\n\"\n    \"                Admin::start($server);\\n\"\n    \"            });\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function onShutdown(Server $server)\\n\"\n    \"    {\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function onBeforeReload(Server $server)\\n\"\n    \"    {\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function onAfterReload(Server $server)\\n\"\n    \"    {\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function onManagerStart(Server $server)\\n\"\n    \"    {\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function onManagerStop(Server $server)\\n\"\n    \"    {\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public static function onWorkerError(Server $server)\\n\"\n    \"    {\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_name_resolver =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Coroutine\\\\Http\\\\ClientProxy;\\n\"\n    \"use Swoole\\\\Http\\\\Status;\\n\"\n    \"use Swoole\\\\NameResolver\\\\Cluster;\\n\"\n    \"use Swoole\\\\NameResolver\\\\Exception;\\n\"\n    \"\\n\"\n    \"abstract class NameResolver\\n\"\n    \"{\\n\"\n    \"    protected $baseUrl;\\n\"\n    \"\\n\"\n    \"    protected $info;\\n\"\n    \"\\n\"\n    \"    private $filter_fn;\\n\"\n    \"\\n\"\n    \"    public function __construct($url, protected $prefix = 'swoole_service_')\\n\"\n    \"    {\\n\"\n    \"        $this->checkServerUrl($url);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    abstract public function join(string $name, string $ip, int $port, array $options = []): bool;\\n\"\n    \"\\n\"\n    \"    abstract public function leave(string $name, string $ip, int $port): bool;\\n\"\n    \"\\n\"\n    \"    abstract public function getCluster(string $name): ?Cluster;\\n\"\n    \"\\n\"\n    \"    public function withFilter(callable $fn): self\\n\"\n    \"    {\\n\"\n    \"        $this->filter_fn = $fn;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getFilter()\\n\"\n    \"    {\\n\"\n    \"        return $this->filter_fn;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function hasFilter(): bool\\n\"\n    \"    {\\n\"\n    \"        return !empty($this->filter_fn);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * return string: final result, non-empty string must be a valid IP address,\\n\"\n    \"     * and an empty string indicates name lookup failed, and lookup operation will not continue.\\n\"\n    \"     * return Cluster: has multiple nodes and failover is possible\\n\"\n    \"     * return false or null: try another name resolver\\n\"\n    \"     * @return Cluster|false|string|null\\n\"\n    \"     */\\n\"\n    \"    public function lookup(string $name)\\n\"\n    \"    {\\n\"\n    \"        if ($this->hasFilter() and ($this->getFilter())($name) !== true) {\\n\"\n    \"            return null;\\n\"\n    \"        }\\n\"\n    \"        $cluster = $this->getCluster($name);\\n\"\n    \"        // lookup failed, terminate execution\\n\"\n    \"        if ($cluster == null) {\\n\"\n    \"            return '';\\n\"\n    \"        }\\n\"\n    \"        // only one node, cannot retry\\n\"\n    \"        if ($cluster->count() == 1) {\\n\"\n    \"            return $cluster->pop();\\n\"\n    \"        }\\n\"\n    \"        return $cluster;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * !!! The host MUST BE IP ADDRESS\\n\"\n    \"     */\\n\"\n    \"    protected function checkServerUrl(string $url)\\n\"\n    \"    {\\n\"\n    \"        $info = parse_url($url);\\n\"\n    \"        if (empty($info['scheme']) or empty($info['host'])) {\\n\"\n    \"            throw new \\\\RuntimeException(\\\"invalid url parameter '{$url}'\\\");\\n\"\n    \"        }\\n\"\n    \"        if (!filter_var($info['host'], FILTER_VALIDATE_IP)) {\\n\"\n    \"            $info['ip'] = gethostbyname($info['host']);\\n\"\n    \"            if (!filter_var($info['ip'], FILTER_VALIDATE_IP)) {\\n\"\n    \"                throw new \\\\RuntimeException(\\\"Failed to resolve host '{$info['host']}'\\\");\\n\"\n    \"            }\\n\"\n    \"        } else {\\n\"\n    \"            $info['ip'] = $info['host'];\\n\"\n    \"        }\\n\"\n    \"        $baseUrl = $info['scheme'] . '://' . $info['ip'];\\n\"\n    \"        if (!empty($info['port'])) {\\n\"\n    \"            $baseUrl .= \\\":{$info['port']}\\\";\\n\"\n    \"        }\\n\"\n    \"        if (!empty($info['path'])) {\\n\"\n    \"            $baseUrl .= rtrim($info['path'], '/');\\n\"\n    \"        }\\n\"\n    \"        $this->baseUrl = $baseUrl;\\n\"\n    \"        $this->info    = $info;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    protected function checkResponse(ClientProxy $response): bool\\n\"\n    \"    {\\n\"\n    \"        if ($response->getStatusCode() === Status::OK) {\\n\"\n    \"            return true;\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        throw new Exception('Http Body: ' . $response->getBody(), $response->getStatusCode());\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_name_resolver_exception =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @see     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\NameResolver;\\n\"\n    \"\\n\"\n    \"class Exception extends \\\\RuntimeException\\n\"\n    \"{\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_name_resolver_cluster =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\NameResolver;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Exception;\\n\"\n    \"\\n\"\n    \"class Cluster\\n\"\n    \"{\\n\"\n    \"    private array $nodes = [];\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws Exception\\n\"\n    \"     */\\n\"\n    \"    public function add(string $host, int $port, int $weight = 100): void\\n\"\n    \"    {\\n\"\n    \"        if (!filter_var($host, FILTER_VALIDATE_IP)) {\\n\"\n    \"            throw new Exception(\\\"Bad IP Address [{$host}]\\\");\\n\"\n    \"        }\\n\"\n    \"        if ($port < 0 or $port > 65535) {\\n\"\n    \"            throw new Exception(\\\"Bad Port [{$port}]\\\");\\n\"\n    \"        }\\n\"\n    \"        if ($weight < 0 or $weight > 100) {\\n\"\n    \"            throw new Exception(\\\"Bad Weight [{$weight}]\\\");\\n\"\n    \"        }\\n\"\n    \"        $this->nodes[] = ['host' => $host, 'port' => $port, 'weight' => $weight];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @return false|string\\n\"\n    \"     */\\n\"\n    \"    public function pop()\\n\"\n    \"    {\\n\"\n    \"        if (empty($this->nodes)) {\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"        $index = array_rand($this->nodes, 1);\\n\"\n    \"        $node  = $this->nodes[$index];\\n\"\n    \"        unset($this->nodes[$index]);\\n\"\n    \"        return $node;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function count(): int\\n\"\n    \"    {\\n\"\n    \"        return count($this->nodes);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_name_resolver_redis =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @see     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\NameResolver;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\NameResolver;\\n\"\n    \"\\n\"\n    \"class Redis extends NameResolver\\n\"\n    \"{\\n\"\n    \"    private $serverHost;\\n\"\n    \"\\n\"\n    \"    private $serverPort;\\n\"\n    \"\\n\"\n    \"    public function __construct($url, $prefix = 'swoole:service:')\\n\"\n    \"    {\\n\"\n    \"        parent::__construct($url, $prefix);\\n\"\n    \"        $this->serverHost = $this->info['ip'];\\n\"\n    \"        $this->serverPort = $this->info['port'] ?? 6379;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function join(string $name, string $ip, int $port, array $options = []): bool\\n\"\n    \"    {\\n\"\n    \"        if (($redis = $this->connect()) === false) {\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"        if ($redis->sAdd($this->prefix . $name, $ip . ':' . $port) === false) {\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"        return true;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function leave(string $name, string $ip, int $port): bool\\n\"\n    \"    {\\n\"\n    \"        if (($redis = $this->connect()) === false) {\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"        if ($redis->sRem($this->prefix . $name, $ip . ':' . $port) === false) {\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"        return true;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getCluster(string $name): ?Cluster\\n\"\n    \"    {\\n\"\n    \"        if (($redis = $this->connect()) === false) {\\n\"\n    \"            return null;\\n\"\n    \"        }\\n\"\n    \"        $members = $redis->sMembers($this->prefix . $name);\\n\"\n    \"        if (empty($members)) {\\n\"\n    \"            return null;\\n\"\n    \"        }\\n\"\n    \"        $cluster = new Cluster();\\n\"\n    \"        foreach ($members as $m) {\\n\"\n    \"            [$host, $port] = explode(':', $m);\\n\"\n    \"            $cluster->add($host, intval($port));\\n\"\n    \"        }\\n\"\n    \"        return $cluster;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    protected function connect()\\n\"\n    \"    {\\n\"\n    \"        $redis = new \\\\Redis();\\n\"\n    \"        if ($redis->connect($this->serverHost, $this->serverPort) === false) {\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"        return $redis;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_name_resolver_nacos =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @see     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\NameResolver;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Coroutine;\\n\"\n    \"use Swoole\\\\NameResolver;\\n\"\n    \"\\n\"\n    \"class Nacos extends NameResolver\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * @throws Coroutine\\\\Http\\\\Client\\\\Exception|Exception\\n\"\n    \"     */\\n\"\n    \"    public function join(string $name, string $ip, int $port, array $options = []): bool\\n\"\n    \"    {\\n\"\n    \"        $params['port']        = $port;\\n\"\n    \"        $params['ip']          = $ip;\\n\"\n    \"        $params['healthy']     = 'true';\\n\"\n    \"        $params['weight']      = $options['weight'] ?? 100;\\n\"\n    \"        $params['encoding']    = $options['encoding'] ?? 'utf-8';\\n\"\n    \"        $params['namespaceId'] = $options['namespaceId'] ?? 'public';\\n\"\n    \"        $params['serviceName'] = $this->prefix . $name;\\n\"\n    \"\\n\"\n    \"        $url = $this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params);\\n\"\n    \"        $r   = Coroutine\\\\Http\\\\post($url, []);\\n\"\n    \"        return $this->checkResponse($r);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws Coroutine\\\\Http\\\\Client\\\\Exception|Exception\\n\"\n    \"     */\\n\"\n    \"    public function leave(string $name, string $ip, int $port): bool\\n\"\n    \"    {\\n\"\n    \"        $params['port']        = $port;\\n\"\n    \"        $params['ip']          = $ip;\\n\"\n    \"        $params['serviceName'] = $this->prefix . $name;\\n\"\n    \"\\n\"\n    \"        $url = $this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params);\\n\"\n    \"        $r   = Coroutine\\\\Http\\\\request($this->baseUrl . '/nacos/v1/ns/instance?' . http_build_query($params), 'DELETE');\\n\"\n    \"        return $this->checkResponse($r);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws Coroutine\\\\Http\\\\Client\\\\Exception|Exception|\\\\Swoole\\\\Exception\\n\"\n    \"     */\\n\"\n    \"    public function getCluster(string $name): ?Cluster\\n\"\n    \"    {\\n\"\n    \"        $params['serviceName'] = $this->prefix . $name;\\n\"\n    \"\\n\"\n    \"        $url = $this->baseUrl . '/nacos/v1/ns/instance/list?' . http_build_query($params);\\n\"\n    \"        $r   = Coroutine\\\\Http\\\\get($url);\\n\"\n    \"        if (!$this->checkResponse($r)) {\\n\"\n    \"            return null;\\n\"\n    \"        }\\n\"\n    \"        $result = json_decode($r->getBody(), null, 512, JSON_THROW_ON_ERROR);\\n\"\n    \"        if (empty($result)) {\\n\"\n    \"            return null;\\n\"\n    \"        }\\n\"\n    \"        $cluster = new Cluster();\\n\"\n    \"        foreach ($result->hosts as $node) {\\n\"\n    \"            $cluster->add($node->ip, $node->port, $node->weight);\\n\"\n    \"        }\\n\"\n    \"        return $cluster;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_name_resolver_consul =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @see     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\NameResolver;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\NameResolver;\\n\"\n    \"\\n\"\n    \"use function Swoole\\\\Coroutine\\\\Http\\\\get;\\n\"\n    \"use function Swoole\\\\Coroutine\\\\Http\\\\request;\\n\"\n    \"\\n\"\n    \"class Consul extends NameResolver\\n\"\n    \"{\\n\"\n    \"    public function join(string $name, string $ip, int $port, array $options = []): bool\\n\"\n    \"    {\\n\"\n    \"        $weight = $options['weight'] ?? 100;\\n\"\n    \"        $data   = [\\n\"\n    \"            'ID'                => $this->getServiceId($name, $ip, $port),\\n\"\n    \"            'Name'              => $this->prefix . $name,\\n\"\n    \"            'Address'           => $ip,\\n\"\n    \"            'Port'              => $port,\\n\"\n    \"            'EnableTagOverride' => false,\\n\"\n    \"            'Weights'           => [\\n\"\n    \"                'Passing' => $weight,\\n\"\n    \"                'Warning' => 1,\\n\"\n    \"            ],\\n\"\n    \"        ];\\n\"\n    \"        $url = $this->baseUrl . '/v1/agent/service/register';\\n\"\n    \"        $r   = request($url, 'PUT', json_encode($data, JSON_THROW_ON_ERROR));\\n\"\n    \"        return $this->checkResponse($r);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function leave(string $name, string $ip, int $port): bool\\n\"\n    \"    {\\n\"\n    \"        $url = $this->baseUrl . '/v1/agent/service/deregister/' . $this->getServiceId(\\n\"\n    \"            $name,\\n\"\n    \"            $ip,\\n\"\n    \"            $port\\n\"\n    \"        );\\n\"\n    \"        $r = request($url, 'PUT');\\n\"\n    \"        return $this->checkResponse($r);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function enableMaintenanceMode(string $name, string $ip, int $port): bool\\n\"\n    \"    {\\n\"\n    \"        $url = $this->baseUrl . '/v1/agent/service/maintenance/' . $this->getServiceId(\\n\"\n    \"            $name,\\n\"\n    \"            $ip,\\n\"\n    \"            $port\\n\"\n    \"        );\\n\"\n    \"        $r = request($url, 'PUT');\\n\"\n    \"        return $this->checkResponse($r);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function getCluster(string $name): ?Cluster\\n\"\n    \"    {\\n\"\n    \"        $url = $this->baseUrl . '/v1/catalog/service/' . $this->prefix . $name;\\n\"\n    \"        $r   = get($url);\\n\"\n    \"        if (!$this->checkResponse($r)) {\\n\"\n    \"            return null;\\n\"\n    \"        }\\n\"\n    \"        $list = json_decode($r->getBody(), null, 512, JSON_THROW_ON_ERROR);\\n\"\n    \"        if (empty($list)) {\\n\"\n    \"            return null;\\n\"\n    \"        }\\n\"\n    \"        $cluster = new Cluster();\\n\"\n    \"        foreach ($list as $li) {\\n\"\n    \"            $cluster->add($li->ServiceAddress, $li->ServicePort, $li->ServiceWeights->Passing);\\n\"\n    \"        }\\n\"\n    \"        return $cluster;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    private function getServiceId(string $name, string $ip, int $port): string\\n\"\n    \"    {\\n\"\n    \"        return $this->prefix . $name . \\\"_{$ip}:{$port}\\\";\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_thread_pool =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Thread;\\n\"\n    \"\\n\"\n    \"use PhpParser\\\\Error;\\n\"\n    \"use PhpParser\\\\ParserFactory;\\n\"\n    \"use Swoole\\\\Thread;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * @since 6.0.0-beta\\n\"\n    \" */\\n\"\n    \"class Pool\\n\"\n    \"{\\n\"\n    \"    private array $threads = [];\\n\"\n    \"\\n\"\n    \"    private string $autoloader = '';\\n\"\n    \"\\n\"\n    \"    private string $classDefinitionFile = '';\\n\"\n    \"\\n\"\n    \"    private string $runnableClass = '';\\n\"\n    \"\\n\"\n    \"    private int $threadNum = 0;\\n\"\n    \"\\n\"\n    \"    private string $proxyFile;\\n\"\n    \"\\n\"\n    \"    private array $arguments = [];\\n\"\n    \"\\n\"\n    \"    private object $running;\\n\"\n    \"\\n\"\n    \"    private object $queue;\\n\"\n    \"\\n\"\n    \"    private array $indexes = [];\\n\"\n    \"\\n\"\n    \"    public function __construct(string $runnableClass, int $threadNum)\\n\"\n    \"    {\\n\"\n    \"        if ($threadNum <= 0) {\\n\"\n    \"            throw new \\\\Exception('threadNum must be greater than 0');\\n\"\n    \"        }\\n\"\n    \"        $this->runnableClass = $runnableClass;\\n\"\n    \"        $this->threadNum     = $threadNum;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withArguments(...$arguments): static\\n\"\n    \"    {\\n\"\n    \"        $this->arguments = $arguments;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withAutoloader(string $autoloader): static\\n\"\n    \"    {\\n\"\n    \"        $this->autoloader = $autoloader;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function withClassDefinitionFile(string $classDefinitionFile): static\\n\"\n    \"    {\\n\"\n    \"        $this->classDefinitionFile = $classDefinitionFile;\\n\"\n    \"        return $this;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    /**\\n\"\n    \"     * @throws \\\\ReflectionException\\n\"\n    \"     */\\n\"\n    \"    public function start(): void\\n\"\n    \"    {\\n\"\n    \"        if (empty($this->classDefinitionFile) and class_exists($this->runnableClass, false)) {\\n\"\n    \"            $file = (new \\\\ReflectionClass($this->runnableClass))->getFileName();\\n\"\n    \"            if (!$this->isValidPhpFile($file)) {\\n\"\n    \"                throw new \\\\Exception('class definition file must not contain any expressions.');\\n\"\n    \"            }\\n\"\n    \"            $this->classDefinitionFile = $file;\\n\"\n    \"        } elseif ($this->classDefinitionFile) {\\n\"\n    \"            require_once $this->classDefinitionFile;\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if (!class_exists($this->runnableClass)) {\\n\"\n    \"            throw new \\\\Exception(\\\"class `{$this->runnableClass}` not found\\\");\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if (!is_subclass_of($this->runnableClass, Runnable::class)) {\\n\"\n    \"            throw new \\\\Exception(\\\"class `{$this->runnableClass}` must implements Thread\\\\\\\\Runnable\\\");\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if (empty($this->autoloader)) {\\n\"\n    \"            $include_files = get_included_files();\\n\"\n    \"            foreach ($include_files as $file) {\\n\"\n    \"                if (str_ends_with($file, 'vendor/autoload.php')) {\\n\"\n    \"                    $this->autoloader = $file;\\n\"\n    \"                    break;\\n\"\n    \"                }\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if ($this->autoloader) {\\n\"\n    \"            $this->proxyFile = dirname($this->autoloader) . '/thread_runner.php';\\n\"\n    \"        } else {\\n\"\n    \"            $this->proxyFile = dirname($this->classDefinitionFile) . '/thread_runner.php';\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        if (!is_file($this->proxyFile)) {\\n\"\n    \"            $script = '<?php' . PHP_EOL;\\n\"\n    \"            $script .= '$arguments = Swoole\\\\Thread::getArguments();' . PHP_EOL;\\n\"\n    \"            $script .= '$threadId = Swoole\\\\Thread::getId();' . PHP_EOL;\\n\"\n    \"            $script .= '$autoloader = $arguments[0];' . PHP_EOL;\\n\"\n    \"            $script .= '$runnableClass = $arguments[1];' . PHP_EOL;\\n\"\n    \"            $script .= '$queue = $arguments[2];' . PHP_EOL;\\n\"\n    \"            $script .= '$classDefinitionFile = $arguments[3];' . PHP_EOL;\\n\"\n    \"            $script .= '$running = $arguments[4];' . PHP_EOL;\\n\"\n    \"            $script .= '$index = $arguments[5];' . PHP_EOL;\\n\"\n    \"            $script .= '$threadArguments = array_slice($arguments, 6);' . PHP_EOL;\\n\"\n    \"            $script .= 'if ($autoloader) require_once $autoloader;' . PHP_EOL;\\n\"\n    \"            $script .= 'if ($classDefinitionFile) require_once $classDefinitionFile;' . PHP_EOL;\\n\"\n    \"            $script .= '$runnable = new $runnableClass($running, $index);' . PHP_EOL;\\n\"\n    \"            $script .= 'try { $runnable->run($threadArguments); }' . PHP_EOL;\\n\"\n    \"            $script .= 'finally { $queue->push($threadId, Swoole\\\\Thread\\\\Queue::NOTIFY_ONE); }' . PHP_EOL;\\n\"\n    \"            $script .= PHP_EOL;\\n\"\n    \"            file_put_contents($this->proxyFile, $script);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        $this->queue   = new Queue();\\n\"\n    \"        $this->running = new Atomic(1);\\n\"\n    \"\\n\"\n    \"        for ($index = 0; $index < $this->threadNum; $index++) {\\n\"\n    \"            $this->createThread($index);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        while ($this->running->get()) {\\n\"\n    \"            $threadId = $this->queue->pop(-1);\\n\"\n    \"            $thread   = $this->threads[$threadId];\\n\"\n    \"            $index    = $this->indexes[$threadId];\\n\"\n    \"            $thread->join();\\n\"\n    \"            unset($this->threads[$threadId], $this->indexes[$threadId]);\\n\"\n    \"\\n\"\n    \"            $this->createThread($index);\\n\"\n    \"        }\\n\"\n    \"\\n\"\n    \"        foreach ($this->threads as $thread) {\\n\"\n    \"            $thread->join();\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    public function shutdown(): void\\n\"\n    \"    {\\n\"\n    \"        $this->running->set(0);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    protected function isValidPhpFile($filePath): bool\\n\"\n    \"    {\\n\"\n    \"        $allowedNodeTypes = [\\n\"\n    \"            \\\\PhpParser\\\\Node\\\\Stmt\\\\Class_::class,\\n\"\n    \"            \\\\PhpParser\\\\Node\\\\Stmt\\\\Const_::class,\\n\"\n    \"            \\\\PhpParser\\\\Node\\\\Stmt\\\\Use_::class,\\n\"\n    \"            \\\\PhpParser\\\\Node\\\\Stmt\\\\Namespace_::class,\\n\"\n    \"            \\\\PhpParser\\\\Node\\\\Stmt\\\\Declare_::class,\\n\"\n    \"        ];\\n\"\n    \"\\n\"\n    \"        $parser = (new ParserFactory())->createForNewestSupportedVersion();\\n\"\n    \"        try {\\n\"\n    \"            $code     = file_get_contents($filePath);\\n\"\n    \"            $stmts    = $parser->parse($code);\\n\"\n    \"            $skipLine = -1;\\n\"\n    \"            foreach ($stmts as $stmt) {\\n\"\n    \"                $isAllowed = false;\\n\"\n    \"                foreach ($allowedNodeTypes as $allowedNodeType) {\\n\"\n    \"                    if ($stmt instanceof $allowedNodeType) {\\n\"\n    \"                        $isAllowed = true;\\n\"\n    \"                        break;\\n\"\n    \"                    }\\n\"\n    \"                }\\n\"\n    \"                if (!$isAllowed) {\\n\"\n    \"                    if ($stmt->getLine() == $skipLine) {\\n\"\n    \"                        continue;\\n\"\n    \"                    }\\n\"\n    \"                    return false;\\n\"\n    \"                }\\n\"\n    \"            }\\n\"\n    \"        } catch (Error $error) {\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"        return true;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    protected function createThread($index): void\\n\"\n    \"    {\\n\"\n    \"        $thread = new Thread($this->proxyFile,\\n\"\n    \"            $this->autoloader,\\n\"\n    \"            $this->runnableClass,\\n\"\n    \"            $this->queue,\\n\"\n    \"            $this->classDefinitionFile,\\n\"\n    \"            $this->running,\\n\"\n    \"            $index,\\n\"\n    \"            ...$this->arguments\\n\"\n    \"        );\\n\"\n    \"        $this->indexes[$thread->id] = $index;\\n\"\n    \"        $this->threads[$thread->id] = $thread;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_thread_runnable =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Thread;\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * @since 6.0.0-beta\\n\"\n    \" */\\n\"\n    \"abstract class Runnable\\n\"\n    \"{\\n\"\n    \"    protected Atomic $running;\\n\"\n    \"\\n\"\n    \"    protected int $id;\\n\"\n    \"\\n\"\n    \"    public function __construct($running, $index)\\n\"\n    \"    {\\n\"\n    \"        $this->running = $running;\\n\"\n    \"        $this->id      = $index;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    abstract public function run(array $args): void;\\n\"\n    \"\\n\"\n    \"    protected function isRunning(): bool\\n\"\n    \"    {\\n\"\n    \"        return $this->running->get() === 1;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    protected function shutdown(): void\\n\"\n    \"    {\\n\"\n    \"        $this->running->set(0);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_core_coroutine_functions =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\Coroutine;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Coroutine;\\n\"\n    \"\\n\"\n    \"function run(callable $fn, ...$args)\\n\"\n    \"{\\n\"\n    \"    $s       = new Scheduler();\\n\"\n    \"    $options = Coroutine::getOptions();\\n\"\n    \"    if (!isset($options['hook_flags'])) {\\n\"\n    \"        $s->set(['hook_flags' => SWOOLE_HOOK_ALL]);\\n\"\n    \"    }\\n\"\n    \"    $s->add($fn, ...$args);\\n\"\n    \"    return $s->start();\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function go(callable $fn, ...$args)\\n\"\n    \"{\\n\"\n    \"    return Coroutine::create($fn, ...$args);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function defer(callable $fn)\\n\"\n    \"{\\n\"\n    \"    Coroutine::defer($fn);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function batch(array $tasks, float $timeout = -1): array\\n\"\n    \"{\\n\"\n    \"    $wg = new WaitGroup(count($tasks));\\n\"\n    \"    foreach ($tasks as $id => $task) {\\n\"\n    \"        Coroutine::create(function () use ($wg, &$tasks, $id, $task) {\\n\"\n    \"            $tasks[$id] = null;\\n\"\n    \"            $tasks[$id] = $task();\\n\"\n    \"            $wg->done();\\n\"\n    \"        });\\n\"\n    \"    }\\n\"\n    \"    $wg->wait($timeout);\\n\"\n    \"    return $tasks;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function parallel(int $n, callable $fn): void\\n\"\n    \"{\\n\"\n    \"    $count = $n;\\n\"\n    \"    $wg    = new WaitGroup($n);\\n\"\n    \"    while ($count--) {\\n\"\n    \"        Coroutine::create(function () use ($fn, $wg) {\\n\"\n    \"            $fn();\\n\"\n    \"            $wg->done();\\n\"\n    \"        });\\n\"\n    \"    }\\n\"\n    \"    $wg->wait();\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * Applies the callback to the elements of the given list.\\n\"\n    \" *\\n\"\n    \" * The callback function takes on two parameters. The list parameter's value being the first, and the key/index second.\\n\"\n    \" * Each callback runs in a new coroutine, allowing the list to be processed in parallel.\\n\"\n    \" *\\n\"\n    \" * @param array $list A list of key/value paired input data.\\n\"\n    \" * @param callable $fn The callback function to apply to each item on the list. The callback takes on two parameters.\\n\"\n    \" *                     The list parameter's value being the first, and the key/index second.\\n\"\n    \" * @param float $timeout > 0 means waiting for the specified number of seconds. other means no waiting.\\n\"\n    \" * @return array Returns an array containing the results of applying the callback function to the corresponding value\\n\"\n    \" *               and key of the list (used as arguments for the callback). The returned array will preserve the keys of\\n\"\n    \" *               the list.\\n\"\n    \" */\\n\"\n    \"function map(array $list, callable $fn, float $timeout = -1): array\\n\"\n    \"{\\n\"\n    \"    $wg = new WaitGroup(count($list));\\n\"\n    \"    foreach ($list as $id => $elem) {\\n\"\n    \"        Coroutine::create(function () use ($wg, &$list, $id, $elem, $fn): void {\\n\"\n    \"            $list[$id] = null;\\n\"\n    \"            $list[$id] = $fn($elem, $id);\\n\"\n    \"            $wg->done();\\n\"\n    \"        });\\n\"\n    \"    }\\n\"\n    \"    $wg->wait($timeout);\\n\"\n    \"    return $list;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function deadlock_check()\\n\"\n    \"{\\n\"\n    \"    $all_coroutines = Coroutine::listCoroutines();\\n\"\n    \"    $count          = Coroutine::stats()['coroutine_num'];\\n\"\n    \"\\n\"\n    \"    // coroutine deadlock detected, header\\n\"\n    \"    $hr_width = 64 + strlen(strval($count));\\n\"\n    \"    $hr1      = str_repeat('=', $hr_width);\\n\"\n    \"    $hr2      = str_repeat('-', $hr_width);\\n\"\n    \"    echo '',\\n\"\n    \"    \\\"\\\\n {$hr1}\\\",\\n\"\n    \"    \\\"\\\\n  [FATAL ERROR]: all coroutines (count: {$count}) are asleep - deadlock!\\\",\\n\"\n    \"    \\\"\\\\n {$hr1}\\\",\\n\"\n    \"    \\\"\\\\n\\\";\\n\"\n    \"\\n\"\n    \"    // print all coroutine backtraces\\n\"\n    \"    $options = Coroutine::getOptions();\\n\"\n    \"    if (empty($options['deadlock_check_disable_trace'])) {\\n\"\n    \"        $index = 0;\\n\"\n    \"        $limit = empty($options['deadlock_check_limit']) ? 32 : intval($options['deadlock_check_limit']);\\n\"\n    \"        $depth = empty($options['deadlock_check_depth']) ? 32 : intval($options['deadlock_check_depth']);\\n\"\n    \"        foreach ($all_coroutines as $cid) {\\n\"\n    \"            echo \\\"\\\\n  [Coroutine-{$cid}]\\\";\\n\"\n    \"            echo \\\"\\\\n {$hr2}\\\\n\\\";\\n\"\n    \"            echo Coroutine::printBackTrace($cid, DEBUG_BACKTRACE_IGNORE_ARGS, $depth);\\n\"\n    \"            $index++;\\n\"\n    \"            // limit the number of maximum outputs\\n\"\n    \"            if ($index >= $limit) {\\n\"\n    \"                break;\\n\"\n    \"            }\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    // footer\\n\"\n    \"    echo \\\"\\\\n {$hr1}\\\\n\\\";\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_ext_curl =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"/* @noinspection PhpComposerExtensionStubsInspection */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"function swoole_curl_init(string $url = ''): Swoole\\\\Curl\\\\Handler\\n\"\n    \"{\\n\"\n    \"    return new Swoole\\\\Curl\\\\Handler($url);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_curl_setopt(Swoole\\\\Curl\\\\Handler $obj, int $opt, $value): bool\\n\"\n    \"{\\n\"\n    \"    return $obj->setOpt($opt, $value);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_curl_setopt_array(Swoole\\\\Curl\\\\Handler $obj, $array): bool\\n\"\n    \"{\\n\"\n    \"    foreach ($array as $k => $v) {\\n\"\n    \"        if ($obj->setOpt($k, $v) !== true) {\\n\"\n    \"            return false;\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"    return true;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_curl_exec(Swoole\\\\Curl\\\\Handler $obj)\\n\"\n    \"{\\n\"\n    \"    return $obj->exec();\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_curl_getinfo(Swoole\\\\Curl\\\\Handler $obj, int $opt = 0)\\n\"\n    \"{\\n\"\n    \"    $info = $obj->getInfo();\\n\"\n    \"    if (is_array($info) and $opt) {\\n\"\n    \"        return match ($opt) {\\n\"\n    \"            CURLINFO_EFFECTIVE_URL      => $info['url'],\\n\"\n    \"            CURLINFO_HTTP_CODE          => $info['http_code'],\\n\"\n    \"            CURLINFO_CONTENT_TYPE       => $info['content_type'],\\n\"\n    \"            CURLINFO_REDIRECT_COUNT     => $info['redirect_count'],\\n\"\n    \"            CURLINFO_REDIRECT_URL       => $info['redirect_url'],\\n\"\n    \"            CURLINFO_TOTAL_TIME         => $info['total_time'],\\n\"\n    \"            CURLINFO_STARTTRANSFER_TIME => $info['starttransfer_time'],\\n\"\n    \"            CURLINFO_SIZE_DOWNLOAD      => $info['size_download'],\\n\"\n    \"            CURLINFO_SPEED_DOWNLOAD     => $info['speed_download'],\\n\"\n    \"            CURLINFO_REDIRECT_TIME      => $info['redirect_time'],\\n\"\n    \"            CURLINFO_HEADER_SIZE        => $info['header_size'],\\n\"\n    \"            CURLINFO_PRIMARY_IP         => $info['primary_ip'],\\n\"\n    \"            CURLINFO_PRIVATE            => $info['private'],\\n\"\n    \"            default                     => null,\\n\"\n    \"        };\\n\"\n    \"    }\\n\"\n    \"    return $info;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_curl_errno(Swoole\\\\Curl\\\\Handler $obj): int\\n\"\n    \"{\\n\"\n    \"    return $obj->errno();\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_curl_error(Swoole\\\\Curl\\\\Handler $obj): string\\n\"\n    \"{\\n\"\n    \"    return $obj->error();\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_curl_reset(Swoole\\\\Curl\\\\Handler $obj)\\n\"\n    \"{\\n\"\n    \"    return $obj->reset();\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_curl_close(Swoole\\\\Curl\\\\Handler $obj): void\\n\"\n    \"{\\n\"\n    \"    $obj->close();\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_curl_multi_getcontent(Swoole\\\\Curl\\\\Handler $obj)\\n\"\n    \"{\\n\"\n    \"    return $obj->getContent();\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_ext_sockets =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Coroutine\\\\Socket;\\n\"\n    \"\\n\"\n    \"function swoole_socket_create(int $domain, int $type, int $protocol)\\n\"\n    \"{\\n\"\n    \"    return new Socket($domain, $type, $protocol);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_connect(Socket $socket, string $address, int $port = 0)\\n\"\n    \"{\\n\"\n    \"    return $socket->connect($address, $port);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_read(Socket $socket, int $length, int $type = PHP_BINARY_READ)\\n\"\n    \"{\\n\"\n    \"    if ($type != PHP_BINARY_READ) {\\n\"\n    \"        return $socket->recvLine($length);\\n\"\n    \"    }\\n\"\n    \"    return $socket->recv($length);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_write(Socket $socket, string $buffer, int $length = 0)\\n\"\n    \"{\\n\"\n    \"    if ($length > 0 and $length < strlen($buffer)) {\\n\"\n    \"        $buffer = substr($buffer, 0, $length);\\n\"\n    \"    }\\n\"\n    \"    return $socket->send($buffer);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_send(Socket $socket, string $buffer, int $length, int $flags)\\n\"\n    \"{\\n\"\n    \"    if ($flags != 0) {\\n\"\n    \"        throw new RuntimeException(\\\"\\\\$flags[{$flags}] is not supported\\\");\\n\"\n    \"    }\\n\"\n    \"    return swoole_socket_write($socket, $buffer, $length);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_recv(Socket $socket, &$buffer, int $length, int $flags)\\n\"\n    \"{\\n\"\n    \"    if ($flags & MSG_OOB) {\\n\"\n    \"        throw new RuntimeException('\\\\$flags[MSG_OOB] is not supported');\\n\"\n    \"    }\\n\"\n    \"    if ($flags & MSG_PEEK) {\\n\"\n    \"        $buffer = $socket->peek($length);\\n\"\n    \"    }\\n\"\n    \"    $timeout = $flags & MSG_DONTWAIT ? 0.001 : 0;\\n\"\n    \"    if ($flags & MSG_WAITALL) {\\n\"\n    \"        $buffer = $socket->recvAll($length, $timeout);\\n\"\n    \"    } else {\\n\"\n    \"        $buffer = $socket->recv($length, $timeout);\\n\"\n    \"    }\\n\"\n    \"    if ($buffer === false) {\\n\"\n    \"        return false;\\n\"\n    \"    }\\n\"\n    \"    return strlen($buffer);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_sendto(Socket $socket, string $buffer, int $length, int $flags, string $addr, int $port = 0)\\n\"\n    \"{\\n\"\n    \"    if ($flags != 0) {\\n\"\n    \"        throw new RuntimeException(\\\"\\\\$flags[{$flags}] is not supported\\\");\\n\"\n    \"    }\\n\"\n    \"    if ($socket->type != SOCK_DGRAM) {\\n\"\n    \"        throw new RuntimeException('only supports dgram type socket');\\n\"\n    \"    }\\n\"\n    \"    if ($length > 0 and $length < strlen($buffer)) {\\n\"\n    \"        $buffer = substr($buffer, 0, $length);\\n\"\n    \"    }\\n\"\n    \"    return $socket->sendto($addr, $port, $buffer);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_recvfrom(Socket $socket, &$buffer, int $length, int $flags, &$name, &$port = null)\\n\"\n    \"{\\n\"\n    \"    if ($flags != 0) {\\n\"\n    \"        throw new RuntimeException(\\\"\\\\$flags[{$flags}] is not supported\\\");\\n\"\n    \"    }\\n\"\n    \"    if ($length == 0) {\\n\"\n    \"        $socket->errCode = SOCKET_EAGAIN;\\n\"\n    \"        return false;\\n\"\n    \"    }\\n\"\n    \"    if ($socket->type != SOCK_DGRAM) {\\n\"\n    \"        throw new RuntimeException('only supports dgram type socket');\\n\"\n    \"    }\\n\"\n    \"    $data = $socket->recvfrom($peer);\\n\"\n    \"    if ($data === false) {\\n\"\n    \"        return false;\\n\"\n    \"    }\\n\"\n    \"    $name = $peer['address'];\\n\"\n    \"    if (func_num_args() == 6) {\\n\"\n    \"        $port = $peer['port'];\\n\"\n    \"    }\\n\"\n    \"    if ($length < strlen($data)) {\\n\"\n    \"        $buffer = substr($data, 0, $length);\\n\"\n    \"    } else {\\n\"\n    \"        $buffer = $data;\\n\"\n    \"    }\\n\"\n    \"    return strlen($buffer);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_bind(Socket $socket, string $address, int $port = 0): bool\\n\"\n    \"{\\n\"\n    \"    return $socket->bind($address, $port);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_listen(Socket $socket, int $backlog = 0): bool\\n\"\n    \"{\\n\"\n    \"    return $socket->listen($backlog);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_create_listen(int $port, int $backlog = 128)\\n\"\n    \"{\\n\"\n    \"    $socket = new Socket(AF_INET, SOCK_STREAM, SOL_TCP);\\n\"\n    \"    if (!$socket->bind('0.0.0.0', $port)) {\\n\"\n    \"        return false;\\n\"\n    \"    }\\n\"\n    \"    if (!$socket->listen($backlog)) {\\n\"\n    \"        return false;\\n\"\n    \"    }\\n\"\n    \"    return $socket;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_accept(Socket $socket)\\n\"\n    \"{\\n\"\n    \"    return $socket->accept();\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_getpeername(Socket $socket, &$address, &$port = null)\\n\"\n    \"{\\n\"\n    \"    $info = $socket->getpeername();\\n\"\n    \"    if (!$info) {\\n\"\n    \"        return false;\\n\"\n    \"    }\\n\"\n    \"    $address = $info['address'];\\n\"\n    \"    if (func_num_args() == 3) {\\n\"\n    \"        $port = $info['port'];\\n\"\n    \"    }\\n\"\n    \"    return true;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_getsockname(Socket $socket, &$address, &$port = null)\\n\"\n    \"{\\n\"\n    \"    $info = $socket->getsockname();\\n\"\n    \"    if (!$info) {\\n\"\n    \"        return false;\\n\"\n    \"    }\\n\"\n    \"    $address = $info['address'];\\n\"\n    \"    if (func_num_args() == 3) {\\n\"\n    \"        $port = $info['port'];\\n\"\n    \"    }\\n\"\n    \"    return true;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_set_option(Socket $socket, int $level, int $optname, $optval): bool\\n\"\n    \"{\\n\"\n    \"    return $socket->setOption($level, $optname, $optval);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_setopt(Socket $socket, int $level, int $optname, $optval): bool\\n\"\n    \"{\\n\"\n    \"    return $socket->setOption($level, $optname, $optval);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_get_option(Socket $socket, int $level, int $optname)\\n\"\n    \"{\\n\"\n    \"    return $socket->getOption($level, $optname);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_getopt(Socket $socket, int $level, int $optname)\\n\"\n    \"{\\n\"\n    \"    return $socket->getOption($level, $optname);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_shutdown(Socket $socket, int $how = 2): bool\\n\"\n    \"{\\n\"\n    \"    return $socket->shutdown($how);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_close(Socket $socket)\\n\"\n    \"{\\n\"\n    \"    $socket->close();\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_clear_error(?Socket $socket = null)\\n\"\n    \"{\\n\"\n    \"    if ($socket) {\\n\"\n    \"        $socket->errCode = 0;\\n\"\n    \"    }\\n\"\n    \"    swoole_clear_error();\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_last_error(?Socket $socket = null): int\\n\"\n    \"{\\n\"\n    \"    if ($socket) {\\n\"\n    \"        return $socket->errCode;\\n\"\n    \"    }\\n\"\n    \"    return swoole_last_error();\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_set_block(Socket $socket)\\n\"\n    \"{\\n\"\n    \"    if ($socket->isClosed()) {\\n\"\n    \"        return false;\\n\"\n    \"    }\\n\"\n    \"    if (isset($socket->__ext_sockets_nonblock) and $socket->__ext_sockets_nonblock) {\\n\"\n    \"        $socket->setOption(SOL_SOCKET, SO_RCVTIMEO, $socket->__ext_sockets_timeout); // @phpstan-ignore property.notFound\\n\"\n    \"    }\\n\"\n    \"    $socket->__ext_sockets_nonblock = false; // @phpstan-ignore property.notFound\\n\"\n    \"    return true;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_set_nonblock(Socket $socket)\\n\"\n    \"{\\n\"\n    \"    if ($socket->isClosed()) {\\n\"\n    \"        return false;\\n\"\n    \"    }\\n\"\n    \"    if (isset($socket->__ext_sockets_nonblock) and $socket->__ext_sockets_nonblock) {\\n\"\n    \"        return true;\\n\"\n    \"    }\\n\"\n    \"    $socket->__ext_sockets_nonblock = true; // @phpstan-ignore property.notFound\\n\"\n    \"    $socket->__ext_sockets_timeout  = $socket->getOption(SOL_SOCKET, SO_RCVTIMEO); // @phpstan-ignore property.notFound\\n\"\n    \"    $socket->setOption(SOL_SOCKET, SO_RCVTIMEO, ['sec' => 0, 'usec' => 1000]);\\n\"\n    \"    return true;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_socket_create_pair(\\n\"\n    \"    int $domain,\\n\"\n    \"    int $type,\\n\"\n    \"    int $protocol,\\n\"\n    \"    array &$pair,\\n\"\n    \") {\\n\"\n    \"    $_pair = swoole_coroutine_socketpair($domain, $type, $protocol);\\n\"\n    \"    if ($_pair) {\\n\"\n    \"        $pair = $_pair;\\n\"\n    \"        return true;\\n\"\n    \"    }\\n\"\n    \"    return false;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * @since 5.0.0\\n\"\n    \" */\\n\"\n    \"function swoole_socket_import_stream(mixed $stream): Socket|false\\n\"\n    \"{\\n\"\n    \"    return Socket::import($stream); // @phpstan-ignore staticMethod.notFound\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_ext_standard =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"/* @noinspection PhpComposerExtensionStubsInspection */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Coroutine\\\\System;\\n\"\n    \"\\n\"\n    \"function swoole_gethostbynamel(string $domain)\\n\"\n    \"{\\n\"\n    \"    return System::getaddrinfo($domain);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_mail(string $to, string $subject, string $message, array $headers = []): bool\\n\"\n    \"{\\n\"\n    \"    $client = swoole_get_default_remote_object_client();\\n\"\n    \"    return $client->call('mail', $to, $subject, $message, $headers);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_checkdnsrr(string $hostname, string $type = 'MX'): bool\\n\"\n    \"{\\n\"\n    \"    $client = swoole_get_default_remote_object_client();\\n\"\n    \"    return $client->call('checkdnsrr', ...func_get_args());\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_dns_check_record(string $hostname, string $type = 'MX'): bool\\n\"\n    \"{\\n\"\n    \"    return swoole_checkdnsrr($hostname, $type);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_real_getmxrr(string $hostname, ?array $hosts = null, ?array $weights = null): array\\n\"\n    \"{\\n\"\n    \"    if (func_num_args() === 2) {\\n\"\n    \"        $result['result'] = getmxrr($hostname, $hosts);\\n\"\n    \"        $result['host']   = $hosts;\\n\"\n    \"    } else {\\n\"\n    \"        $result['result'] = getmxrr($hostname, $hosts, $weights);\\n\"\n    \"        $result['host']   = $hosts;\\n\"\n    \"        $result['weight'] = $weights;\\n\"\n    \"    }\\n\"\n    \"    return $result;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_getmxrr(string $hostname, array &$hosts, ?array &$weights = null): bool\\n\"\n    \"{\\n\"\n    \"    $client   = swoole_get_default_remote_object_client();\\n\"\n    \"    $_hosts   = $hosts;\\n\"\n    \"    $_weights = $weights === null ? null : $weights;\\n\"\n    \"    $result   = $client->call('swoole_real_getmxrr', $hostname, $_hosts, $_weights);\\n\"\n    \"    $hosts    = $result['host'];\\n\"\n    \"    $weights  = $result['weight'];\\n\"\n    \"    return $result['result'];\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_dns_get_mx(string $hostname, array &$hosts, ?array &$weights = null): bool\\n\"\n    \"{\\n\"\n    \"    return swoole_getmxrr($hostname, $hosts, $weights);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_real_dns_get_record(string $hostname, int $type, ?array $authoritative_name_servers = null, ?array $additional_records = null, bool $raw = false): array\\n\"\n    \"{\\n\"\n    \"    if ($authoritative_name_servers === null && $additional_records === null) {\\n\"\n    \"        $result['result'] = dns_get_record($hostname, $type);\\n\"\n    \"    } elseif ($additional_records === null) {\\n\"\n    \"        $result['result'] = dns_get_record($hostname, $type, $authoritative_name_servers);\\n\"\n    \"    } else {\\n\"\n    \"        $result['result'] = dns_get_record($hostname, $type, $authoritative_name_servers, $additional_records);\\n\"\n    \"    }\\n\"\n    \"    $result['authoritative_name_servers'] = $authoritative_name_servers;\\n\"\n    \"    $result['additional_records']         = $additional_records;\\n\"\n    \"    return $result;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_dns_get_record(string $hostname,\\n\"\n    \"    int $type = DNS_ANY,\\n\"\n    \"    ?array &$authoritative_name_servers = null,\\n\"\n    \"    ?array &$additional_records = null,\\n\"\n    \"    bool $raw = false): array|false\\n\"\n    \"{\\n\"\n    \"    $client                     = swoole_get_default_remote_object_client();\\n\"\n    \"    $result                     = $client->call('swoole_real_dns_get_record', $hostname, $type, $authoritative_name_servers, $additional_records, $raw);\\n\"\n    \"    $authoritative_name_servers = $result['authoritative_name_servers'];\\n\"\n    \"    $additional_records         = $result['additional_records'];\\n\"\n    \"    return $result['result'];\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_gethostbyaddr(string $ip): string\\n\"\n    \"{\\n\"\n    \"    $client = swoole_get_default_remote_object_client();\\n\"\n    \"    return $client->call('gethostbyaddr', $ip);\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_ext_mongodb =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Swoole\\\\MongoDB;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\RemoteObject;\\n\"\n    \"use Swoole\\\\RemoteObject\\\\ProxyTrait;\\n\"\n    \"\\n\"\n    \"class Client\\n\"\n    \"{\\n\"\n    \"    use ProxyTrait;\\n\"\n    \"\\n\"\n    \"    public const DEFAULT_URI = 'mongodb://127.0.0.1/';\\n\"\n    \"\\n\"\n    \"    protected RemoteObject $client;\\n\"\n    \"\\n\"\n    \"    public function __construct(?string $uri = self::DEFAULT_URI, array $uriOptions = [], array $driverOptions = [])\\n\"\n    \"    {\\n\"\n    \"        $remoteObjectClient = swoole_library_get_option('mongodb_remote_object_client');\\n\"\n    \"        if ($remoteObjectClient === null) {\\n\"\n    \"            $remoteObjectClient = swoole_get_default_remote_object_client();\\n\"\n    \"        }\\n\"\n    \"        $this->client = $remoteObjectClient->create(\\\\MongoDB\\\\Client::class, $uri, $uriOptions, $driverOptions);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    protected function getObject(): RemoteObject\\n\"\n    \"    {\\n\"\n    \"        return $this->client;\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_functions =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"if (PHP_VERSION_ID < 80100) { // @phpstan-ignore smaller.alwaysFalse\\n\"\n    \"    throw new RuntimeException('require PHP version 8.1 or later');\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"if (SWOOLE_USE_SHORTNAME) { // @phpstan-ignore if.alwaysTrue\\n\"\n    \"    function _string(string $string = ''): Swoole\\\\StringObject\\n\"\n    \"    {\\n\"\n    \"        return new Swoole\\\\StringObject($string);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    function _mbstring(string $string = ''): Swoole\\\\MultibyteStringObject\\n\"\n    \"    {\\n\"\n    \"        return new Swoole\\\\MultibyteStringObject($string);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    function _array(array $array = []): Swoole\\\\ArrayObject\\n\"\n    \"    {\\n\"\n    \"        return new Swoole\\\\ArrayObject($array);\\n\"\n    \"    }\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"class SwooleLibrary\\n\"\n    \"{\\n\"\n    \"    /**\\n\"\n    \"     * @var array<string, mixed>\\n\"\n    \"     */\\n\"\n    \"    public static array $options = [];\\n\"\n    \"\\n\"\n    \"    public static bool $remote_object_server_initiated = false;\\n\"\n    \"\\n\"\n    \"    public static string $remote_object_server_socket_file = '';\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"/**\\n\"\n    \" * @param array<string, mixed> $options\\n\"\n    \" */\\n\"\n    \"function swoole_library_set_options(array $options): void\\n\"\n    \"{\\n\"\n    \"    SwooleLibrary::$options = $options;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_library_get_options(): array\\n\"\n    \"{\\n\"\n    \"    return SwooleLibrary::$options;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_library_set_option(string $key, mixed $value): void\\n\"\n    \"{\\n\"\n    \"    SwooleLibrary::$options[$key] = $value;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_library_get_option(string $key): mixed\\n\"\n    \"{\\n\"\n    \"    return SwooleLibrary::$options[$key] ?? null;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_string(string $string = ''): Swoole\\\\StringObject\\n\"\n    \"{\\n\"\n    \"    return new Swoole\\\\StringObject($string);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_mbstring(string $string = ''): Swoole\\\\MultibyteStringObject\\n\"\n    \"{\\n\"\n    \"    return new Swoole\\\\MultibyteStringObject($string);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_array(array $array = []): Swoole\\\\ArrayObject\\n\"\n    \"{\\n\"\n    \"    return new Swoole\\\\ArrayObject($array);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_table(int $size, string $fields): Swoole\\\\Table\\n\"\n    \"{\\n\"\n    \"    $_fields = swoole_string($fields)->trim()->split(',');\\n\"\n    \"\\n\"\n    \"    $table = new Swoole\\\\Table($size, 0.25);\\n\"\n    \"\\n\"\n    \"    foreach ($_fields as $f) {\\n\"\n    \"        $_f   = swoole_string($f)->trim()->split(':');\\n\"\n    \"        $name = $_f->get(0)->trim()->toString();\\n\"\n    \"        $type = $_f->get(1)->trim();\\n\"\n    \"\\n\"\n    \"        switch ($type) {\\n\"\n    \"            case 'i':\\n\"\n    \"            case 'int':\\n\"\n    \"                $table->column($name, Swoole\\\\Table::TYPE_INT);\\n\"\n    \"                break;\\n\"\n    \"            case 'f':\\n\"\n    \"            case 'float':\\n\"\n    \"                $table->column($name, Swoole\\\\Table::TYPE_FLOAT);\\n\"\n    \"                break;\\n\"\n    \"            case 's':\\n\"\n    \"            case 'string':\\n\"\n    \"                if ($_f->count() < 3) {\\n\"\n    \"                    throw new RuntimeException('need to give string length');\\n\"\n    \"                }\\n\"\n    \"                $length = (int) $_f->get(2)->trim()->toString();\\n\"\n    \"                if ($length <= 0) {\\n\"\n    \"                    throw new RuntimeException(\\\"invalid string length[{$length}]\\\");\\n\"\n    \"                }\\n\"\n    \"                $table->column($name, Swoole\\\\Table::TYPE_STRING, $length);\\n\"\n    \"                break;\\n\"\n    \"            default:\\n\"\n    \"                throw new RuntimeException(\\\"unknown field type[{$type}]\\\");\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    if (!$table->create()) {\\n\"\n    \"        throw new RuntimeException('failed to create table');\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    return $table;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_array_list(...$arrray): Swoole\\\\ArrayObject\\n\"\n    \"{\\n\"\n    \"    return new Swoole\\\\ArrayObject($arrray);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_array_default_value(array $array, $key, $default_value = null)\\n\"\n    \"{\\n\"\n    \"    return array_key_exists($key, $array) ? $array[$key] : $default_value;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_is_in_container(): bool\\n\"\n    \"{\\n\"\n    \"    $mountinfo = file_get_contents('/proc/self/mountinfo');\\n\"\n    \"    return strpos($mountinfo, 'kubepods') > 0 || strpos($mountinfo, 'docker') > 0;\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_container_cpu_num(): int\\n\"\n    \"{\\n\"\n    \"    $swoole_cpu_num = intval(getenv('SWOOLE_CPU_NUM'));\\n\"\n    \"    if ($swoole_cpu_num > 0) {\\n\"\n    \"        return $swoole_cpu_num;\\n\"\n    \"    }\\n\"\n    \"    if (!swoole_is_in_container()) {\\n\"\n    \"        return swoole_cpu_num();\\n\"\n    \"    }\\n\"\n    \"    // cgroup v2\\n\"\n    \"    $cpu_max = '/sys/fs/cgroup/cpu.max';\\n\"\n    \"    if (file_exists($cpu_max)) {\\n\"\n    \"        $cpu_max  = file_get_contents($cpu_max);\\n\"\n    \"        $fields   = explode($cpu_max, ' ');\\n\"\n    \"        $quota_us = $fields[0];\\n\"\n    \"        if ($quota_us === 'max') { // @phpstan-ignore identical.alwaysFalse\\n\"\n    \"            return swoole_cpu_num();\\n\"\n    \"        }\\n\"\n    \"        $period_us = $fields[1] ?? 100000;\\n\"\n    \"    } else {\\n\"\n    \"        $quota_us  = file_get_contents('/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us');\\n\"\n    \"        $period_us = file_get_contents('/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us');\\n\"\n    \"    }\\n\"\n    \"    $cpu_num = floatval($quota_us) / floatval($period_us);\\n\"\n    \"    if ($cpu_num < 1) {\\n\"\n    \"        return swoole_cpu_num();\\n\"\n    \"    }\\n\"\n    \"    return intval(floor($cpu_num));\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_init_default_remote_object_server(): void\\n\"\n    \"{\\n\"\n    \"    $dir = swoole_library_get_option('default_remote_object_server_dir');\\n\"\n    \"    if (empty($dir)) {\\n\"\n    \"        $home = getenv('HOME') ?: sys_get_temp_dir();\\n\"\n    \"        $dir  = $home . '/.swoole';\\n\"\n    \"        swoole_library_set_option('default_remote_object_server_dir', $dir);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    $pid_file = $dir . '/remote-object-server.pid';\\n\"\n    \"\\n\"\n    \"    if (!is_dir($dir)) {\\n\"\n    \"        mkdir($dir, 0755, true);\\n\"\n    \"    } else {\\n\"\n    \"        if (is_file($pid_file)\\n\"\n    \"            and posix_kill(intval(file_get_contents($pid_file)), 0)) {\\n\"\n    \"            return;\\n\"\n    \"        }\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    $options = swoole_library_get_option('default_remote_object_server_options');\\n\"\n    \"    if (!$options) {\\n\"\n    \"        $worker_num = swoole_library_get_option('default_remote_object_server_worker_num') ?: 128;\\n\"\n    \"        $options    = [\\n\"\n    \"            'worker_num'  => $worker_num,\\n\"\n    \"            'server_mode' => defined('SWOOLE_THREAD') ? SWOOLE_THREAD : SWOOLE_BASE,\\n\"\n    \"        ];\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    $php_file                    = $dir . '/remote-object-server.php';\\n\"\n    \"    $socket_file                 = $dir . '/remote-object-server.sock';\\n\"\n    \"    $log_file                    = $dir . '/remote-object-server.log';\\n\"\n    \"    $lock_file                   = $dir . '/remote-object-server.lock';\\n\"\n    \"\\n\"\n    \"    $wait_ready_fn = function () use ($socket_file) {\\n\"\n    \"        // wait for remote object server ready\\n\"\n    \"        while (true) {\\n\"\n    \"            if (posix_access($socket_file, POSIX_R_OK)) {\\n\"\n    \"                break;\\n\"\n    \"            }\\n\"\n    \"            usleep(500000);\\n\"\n    \"        }\\n\"\n    \"    };\\n\"\n    \"\\n\"\n    \"    $lock_handle = fopen($lock_file, 'c');\\n\"\n    \"    if (!$lock_handle) {\\n\"\n    \"        throw new RuntimeException(\\\"failed to open lock file[{$lock_file}]\\\");\\n\"\n    \"    }\\n\"\n    \"    // If the lock was not acquired, it indicates that another process is trying to start the remote object server.\\n\"\n    \"    // In this case, the service should be skipped from starting and proceed to the ready wait detection branch.\\n\"\n    \"    if (!flock($lock_handle, LOCK_EX | LOCK_NB)) {\\n\"\n    \"        fclose($lock_handle);\\n\"\n    \"        $wait_ready_fn();\\n\"\n    \"        return;\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    $options['enable_coroutine'] = false;\\n\"\n    \"    $options['bootstrap']        = $php_file;\\n\"\n    \"    $options['pid_file']         = $pid_file;\\n\"\n    \"    $options['log_file']         = $log_file;\\n\"\n    \"    $options['daemonize']        = true;\\n\"\n    \"    $options['socket_type']      = SWOOLE_SOCK_UNIX_STREAM;\\n\"\n    \"\\n\"\n    \"    $rv = file_put_contents($php_file, '<?php' .\\n\"\n    \"        \\\"\\\\nif (is_file(__DIR__ . '/vendor/autoload.php')) { require __DIR__ . '/vendor/autoload.php'; }\\\" .\\n\"\n    \"        \\\"\\\\nif (is_file(__DIR__ . '/bootstrap.php')) { require __DIR__ . '/bootstrap.php'; }\\\" .\\n\"\n    \"        \\\"\\\\n\\\" .\\n\"\n    \"        \\\"\\\\n(new Swoole\\\\\\\\RemoteObject\\\\\\\\Server(\\\"\\n\"\n    \"        . \\\"'{$socket_file}', 0, \\\"\\n\"\n    \"        . var_export($options, true) .\\n\"\n    \"        \\\"))->start();\\\\n\\\");\\n\"\n    \"    if (!$rv) {\\n\"\n    \"        throw new RuntimeException(\\\"failed to write php file[{$php_file}]\\\");\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    $php_bin = PHP_BINARY;\\n\"\n    \"    if (posix_access($socket_file, POSIX_R_OK)) {\\n\"\n    \"        unlink($socket_file);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    $hook_flags = Swoole\\\\Runtime::getHookFlags();\\n\"\n    \"    // Having enabled the MongoDB hook, you need to install the MongoDB PHP library through Composer.\\n\"\n    \"    if (defined('SWOOLE_HOOK_MONGODB') and $hook_flags & SWOOLE_HOOK_MONGODB and !is_dir($dir . '/vendor/mongodb/mongodb')) {\\n\"\n    \"        system(\\\"cd {$dir} && composer require mongodb/mongodb\\\");\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    // start server\\n\"\n    \"    $proc = proc_open(\\\"{$php_bin} {$php_file}\\\", [\\n\"\n    \"        0 => ['pipe', 'r'],\\n\"\n    \"        1 => ['pipe', 'w'],\\n\"\n    \"        2 => ['pipe', 'w'],\\n\"\n    \"    ], $pipes);\\n\"\n    \"    if ($proc === false) {\\n\"\n    \"        throw new RuntimeException('failed to start remote object server');\\n\"\n    \"    }\\n\"\n    \"    $rc = proc_close($proc);\\n\"\n    \"    if ($rc !== 0) {\\n\"\n    \"        $output = stream_get_contents($pipes[1]) . stream_get_contents($pipes[2]);\\n\"\n    \"        throw new RuntimeException(\\\"failed to start remote object server: exit code {$rc}, output: \\\" . $output);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    $wait_ready_fn();\\n\"\n    \"    flock($lock_handle, LOCK_UN);\\n\"\n    \"    fclose($lock_handle);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"function swoole_get_default_remote_object_client(): Swoole\\\\RemoteObject\\\\Client\\n\"\n    \"{\\n\"\n    \"    if (!SwooleLibrary::$remote_object_server_initiated) {\\n\"\n    \"        SwooleLibrary::$remote_object_server_initiated = true;\\n\"\n    \"        swoole_init_default_remote_object_server();\\n\"\n    \"    }\\n\"\n    \"    if (!SwooleLibrary::$remote_object_server_socket_file) {\\n\"\n    \"        $dir = swoole_library_get_option('default_remote_object_server_dir');\\n\"\n    \"        if (empty($dir)) {\\n\"\n    \"            $home = getenv('HOME') ?: sys_get_temp_dir();\\n\"\n    \"            $dir  = $home . '/.swoole';\\n\"\n    \"        }\\n\"\n    \"        SwooleLibrary::$remote_object_server_socket_file = 'unix://' . $dir . '/remote-object-server.sock';\\n\"\n    \"    }\\n\"\n    \"    return new Swoole\\\\RemoteObject\\\\Client(SwooleLibrary::$remote_object_server_socket_file);\\n\"\n    \"}\\n\";\n\nstatic const char* swoole_library_source_alias =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"if (SWOOLE_USE_SHORTNAME) { // @phpstan-ignore if.alwaysTrue\\n\"\n    \"    class_alias(Swoole\\\\Coroutine\\\\WaitGroup::class, Co\\\\WaitGroup::class, true);\\n\"\n    \"    class_alias(Swoole\\\\Coroutine\\\\Server::class, Co\\\\Server::class, true);\\n\"\n    \"    class_alias(Swoole\\\\Coroutine\\\\Server\\\\Connection::class, Co\\\\Server\\\\Connection::class, true);\\n\"\n    \"    class_alias(Swoole\\\\Coroutine\\\\FastCGI\\\\Client::class, Co\\\\FastCGI\\\\Client::class, true);\\n\"\n    \"    class_alias(Swoole\\\\Coroutine\\\\FastCGI\\\\Client\\\\Exception::class, Co\\\\FastCGI\\\\Client\\\\Exception::class, true);\\n\"\n    \"    class_alias(Swoole\\\\Coroutine\\\\FastCGI\\\\Proxy::class, Co\\\\FastCGI\\\\Proxy::class, true);\\n\"\n    \"}\\n\"\n    \"\\n\"\n    \"class_alias(Swoole\\\\Process\\\\Manager::class, Swoole\\\\Process\\\\ProcessManager::class, true);\\n\";\n\nstatic const char* swoole_library_source_alias_ns =\n    \"\\n\"\n    \"/**\\n\"\n    \" * This file is part of Swoole.\\n\"\n    \" *\\n\"\n    \" * @link     https://www.swoole.com\\n\"\n    \" * @contact  team@swoole.com\\n\"\n    \" * @license  https://github.com/swoole/library/blob/master/LICENSE\\n\"\n    \" */\\n\"\n    \"\\n\"\n    \"declare(strict_types=1);\\n\"\n    \"\\n\"\n    \"namespace Co;\\n\"\n    \"\\n\"\n    \"use Swoole\\\\Coroutine;\\n\"\n    \"\\n\"\n    \"if (SWOOLE_USE_SHORTNAME) { // @phpstan-ignore if.alwaysTrue\\n\"\n    \"    function run(callable $fn, ...$args)\\n\"\n    \"    {\\n\"\n    \"        return \\\\Swoole\\\\Coroutine\\\\run($fn, ...$args);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    function go(callable $fn, ...$args)\\n\"\n    \"    {\\n\"\n    \"        return Coroutine::create($fn, ...$args);\\n\"\n    \"    }\\n\"\n    \"\\n\"\n    \"    function defer(callable $fn)\\n\"\n    \"    {\\n\"\n    \"        Coroutine::defer($fn);\\n\"\n    \"    }\\n\"\n    \"}\\n\";\n\nvoid php_swoole_load_library()\n{\n    _eval(swoole_library_source_constants, \"@swoole/library/constants.php\");\n    _eval(swoole_library_source_std_exec, \"@swoole/library/std/exec.php\");\n    _eval(swoole_library_source_core_constant, \"@swoole/library/core/Constant.php\");\n    _eval(swoole_library_source_core_string_object, \"@swoole/library/core/StringObject.php\");\n    _eval(swoole_library_source_core_multibyte_string_object, \"@swoole/library/core/MultibyteStringObject.php\");\n    _eval(swoole_library_source_core_exception_array_key_not_exists, \"@swoole/library/core/Exception/ArrayKeyNotExists.php\");\n    _eval(swoole_library_source_core_array_object, \"@swoole/library/core/ArrayObject.php\");\n    _eval(swoole_library_source_core_object_proxy, \"@swoole/library/core/ObjectProxy.php\");\n    _eval(swoole_library_source_core_coroutine_wait_group, \"@swoole/library/core/Coroutine/WaitGroup.php\");\n    _eval(swoole_library_source_core_coroutine_server, \"@swoole/library/core/Coroutine/Server.php\");\n    _eval(swoole_library_source_core_coroutine_server_connection, \"@swoole/library/core/Coroutine/Server/Connection.php\");\n    _eval(swoole_library_source_core_coroutine_barrier, \"@swoole/library/core/Coroutine/Barrier.php\");\n    _eval(swoole_library_source_core_coroutine_http_client_proxy, \"@swoole/library/core/Coroutine/Http/ClientProxy.php\");\n    _eval(swoole_library_source_core_coroutine_http_functions, \"@swoole/library/core/Coroutine/Http/functions.php\");\n    _eval(swoole_library_source_core_connection_pool, \"@swoole/library/core/ConnectionPool.php\");\n    _eval(swoole_library_source_core_database_object_proxy, \"@swoole/library/core/Database/ObjectProxy.php\");\n    _eval(swoole_library_source_core_database_mysqli_config, \"@swoole/library/core/Database/MysqliConfig.php\");\n    _eval(swoole_library_source_core_database_mysqli_exception, \"@swoole/library/core/Database/MysqliException.php\");\n    _eval(swoole_library_source_core_database_mysqli_pool, \"@swoole/library/core/Database/MysqliPool.php\");\n    _eval(swoole_library_source_core_database_mysqli_proxy, \"@swoole/library/core/Database/MysqliProxy.php\");\n    _eval(swoole_library_source_core_database_mysqli_statement_proxy, \"@swoole/library/core/Database/MysqliStatementProxy.php\");\n    _eval(swoole_library_source_core_database_detects_lost_connections, \"@swoole/library/core/Database/DetectsLostConnections.php\");\n    _eval(swoole_library_source_core_database_pdo_config, \"@swoole/library/core/Database/PDOConfig.php\");\n    _eval(swoole_library_source_core_database_pdo_pool, \"@swoole/library/core/Database/PDOPool.php\");\n    _eval(swoole_library_source_core_database_pdo_proxy, \"@swoole/library/core/Database/PDOProxy.php\");\n    _eval(swoole_library_source_core_database_pdo_statement_proxy, \"@swoole/library/core/Database/PDOStatementProxy.php\");\n    _eval(swoole_library_source_core_database_redis_config, \"@swoole/library/core/Database/RedisConfig.php\");\n    _eval(swoole_library_source_core_database_redis_pool, \"@swoole/library/core/Database/RedisPool.php\");\n    _eval(swoole_library_source_core_http_status, \"@swoole/library/core/Http/Status.php\");\n    _eval(swoole_library_source_core_curl_exception, \"@swoole/library/core/Curl/Exception.php\");\n    _eval(swoole_library_source_core_curl_handler, \"@swoole/library/core/Curl/Handler.php\");\n    _eval(swoole_library_source_core_fast_cgi, \"@swoole/library/core/FastCGI.php\");\n    _eval(swoole_library_source_core_fast_cgi_record, \"@swoole/library/core/FastCGI/Record.php\");\n    _eval(swoole_library_source_core_fast_cgi_record_params, \"@swoole/library/core/FastCGI/Record/Params.php\");\n    _eval(swoole_library_source_core_fast_cgi_record_abort_request, \"@swoole/library/core/FastCGI/Record/AbortRequest.php\");\n    _eval(swoole_library_source_core_fast_cgi_record_begin_request, \"@swoole/library/core/FastCGI/Record/BeginRequest.php\");\n    _eval(swoole_library_source_core_fast_cgi_record_data, \"@swoole/library/core/FastCGI/Record/Data.php\");\n    _eval(swoole_library_source_core_fast_cgi_record_end_request, \"@swoole/library/core/FastCGI/Record/EndRequest.php\");\n    _eval(swoole_library_source_core_fast_cgi_record_get_values, \"@swoole/library/core/FastCGI/Record/GetValues.php\");\n    _eval(swoole_library_source_core_fast_cgi_record_get_values_result, \"@swoole/library/core/FastCGI/Record/GetValuesResult.php\");\n    _eval(swoole_library_source_core_fast_cgi_record_stdin, \"@swoole/library/core/FastCGI/Record/Stdin.php\");\n    _eval(swoole_library_source_core_fast_cgi_record_stdout, \"@swoole/library/core/FastCGI/Record/Stdout.php\");\n    _eval(swoole_library_source_core_fast_cgi_record_stderr, \"@swoole/library/core/FastCGI/Record/Stderr.php\");\n    _eval(swoole_library_source_core_fast_cgi_record_unknown_type, \"@swoole/library/core/FastCGI/Record/UnknownType.php\");\n    _eval(swoole_library_source_core_fast_cgi_frame_parser, \"@swoole/library/core/FastCGI/FrameParser.php\");\n    _eval(swoole_library_source_core_fast_cgi_message, \"@swoole/library/core/FastCGI/Message.php\");\n    _eval(swoole_library_source_core_fast_cgi_request, \"@swoole/library/core/FastCGI/Request.php\");\n    _eval(swoole_library_source_core_fast_cgi_response, \"@swoole/library/core/FastCGI/Response.php\");\n    _eval(swoole_library_source_core_fast_cgi_http_request, \"@swoole/library/core/FastCGI/HttpRequest.php\");\n    _eval(swoole_library_source_core_fast_cgi_http_response, \"@swoole/library/core/FastCGI/HttpResponse.php\");\n    _eval(swoole_library_source_core_coroutine_fast_cgi_client, \"@swoole/library/core/Coroutine/FastCGI/Client.php\");\n    _eval(swoole_library_source_core_coroutine_fast_cgi_client_exception, \"@swoole/library/core/Coroutine/FastCGI/Client/Exception.php\");\n    _eval(swoole_library_source_core_coroutine_fast_cgi_proxy, \"@swoole/library/core/Coroutine/FastCGI/Proxy.php\");\n    _eval(swoole_library_source_core_process_manager, \"@swoole/library/core/Process/Manager.php\");\n    _eval(swoole_library_source_core_remote_object, \"@swoole/library/core/RemoteObject.php\");\n    _eval(swoole_library_source_core_remote_object_server, \"@swoole/library/core/RemoteObject/Server.php\");\n    _eval(swoole_library_source_core_remote_object_context, \"@swoole/library/core/RemoteObject/Context.php\");\n    _eval(swoole_library_source_core_remote_object_client, \"@swoole/library/core/RemoteObject/Client.php\");\n    _eval(swoole_library_source_core_remote_object_exception, \"@swoole/library/core/RemoteObject/Exception.php\");\n    _eval(swoole_library_source_core_remote_object_proxy_trait, \"@swoole/library/core/RemoteObject/ProxyTrait.php\");\n    _eval(swoole_library_source_core_server_admin, \"@swoole/library/core/Server/Admin.php\");\n    _eval(swoole_library_source_core_server_helper, \"@swoole/library/core/Server/Helper.php\");\n    _eval(swoole_library_source_core_name_resolver, \"@swoole/library/core/NameResolver.php\");\n    _eval(swoole_library_source_core_name_resolver_exception, \"@swoole/library/core/NameResolver/Exception.php\");\n    _eval(swoole_library_source_core_name_resolver_cluster, \"@swoole/library/core/NameResolver/Cluster.php\");\n    _eval(swoole_library_source_core_name_resolver_redis, \"@swoole/library/core/NameResolver/Redis.php\");\n    _eval(swoole_library_source_core_name_resolver_nacos, \"@swoole/library/core/NameResolver/Nacos.php\");\n    _eval(swoole_library_source_core_name_resolver_consul, \"@swoole/library/core/NameResolver/Consul.php\");\n    _eval(swoole_library_source_core_thread_pool, \"@swoole/library/core/Thread/Pool.php\");\n    _eval(swoole_library_source_core_thread_runnable, \"@swoole/library/core/Thread/Runnable.php\");\n    _eval(swoole_library_source_core_coroutine_functions, \"@swoole/library/core/Coroutine/functions.php\");\n    _eval(swoole_library_source_ext_curl, \"@swoole/library/ext/curl.php\");\n    _eval(swoole_library_source_ext_sockets, \"@swoole/library/ext/sockets.php\");\n    _eval(swoole_library_source_ext_standard, \"@swoole/library/ext/standard.php\");\n    _eval(swoole_library_source_ext_mongodb, \"@swoole/library/ext/mongodb.php\");\n    _eval(swoole_library_source_functions, \"@swoole/library/functions.php\");\n    _eval(swoole_library_source_alias, \"@swoole/library/alias.php\");\n    _eval(swoole_library_source_alias_ns, \"@swoole/library/alias_ns.php\");\n}\n"
  },
  {
    "path": "ext-src/php_swoole_odbc.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef PHP_SWOOLE_ODBC_H\n#define PHP_SWOOLE_ODBC_H\n\n#include \"php_swoole.h\"\n\n#ifdef SW_USE_ODBC\nBEGIN_EXTERN_C()\n\n#include \"ext/pdo/php_pdo_driver.h\"\n\n#if PHP_VERSION_ID < 80300\n#include \"thirdparty/php82/pdo_odbc/php_pdo_odbc_int.h\"\n#elif PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400\n#include \"thirdparty/php83/pdo_odbc/php_pdo_odbc_int.h\"\n#elif PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500\n#include \"thirdparty/php84/pdo_odbc/php_pdo_odbc_int.h\"\n#else\n#include \"thirdparty/php85/pdo_odbc/php_pdo_odbc_int.h\"\n#endif\n\nextern const pdo_driver_t swoole_pdo_odbc_driver;\n\n#include \"php_version.h\"\n#define PHP_PDO_ODBC_VERSION PHP_VERSION\n\nRETCODE swoole_odbc_SQLConnect(SQLHDBC ConnectionHandle,\n                               SQLCHAR *ServerName,\n                               SQLSMALLINT NameLength1,\n                               SQLCHAR *UserName,\n                               SQLSMALLINT NameLength2,\n                               SQLCHAR *Authentication,\n                               SQLSMALLINT NameLength3);\n\nSQLRETURN SQL_API swoole_odbc_SQLDriverConnect(SQLHDBC hdbc,\n                                               SQLHWND hwnd,\n                                               SQLCHAR *szConnStrIn,\n                                               SQLSMALLINT cbConnStrIn,\n                                               SQLCHAR *szConnStrOut,\n                                               SQLSMALLINT cbConnStrOutMax,\n                                               SQLSMALLINT *pcbConnStrOut,\n                                               SQLUSMALLINT fDriverCompletion);\n\nSQLRETURN SQL_API swoole_odbc_SQLExecDirect(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength);\n\nSQLRETURN  SQL_API swoole_odbc_SQLGetInfo(SQLHDBC ConnectionHandle,\n                              SQLUSMALLINT InfoType, SQLPOINTER InfoValue,\n                              SQLSMALLINT BufferLength, SQLSMALLINT *StringLength);\n\nSQLRETURN  SQL_API swoole_odbc_SQLGetDiagRec(SQLSMALLINT HandleType,\n                                            SQLHANDLE Handle,\n                                            SQLSMALLINT RecNumber,\n                                            SQLCHAR *Sqlstate,\n                                            SQLINTEGER *NativeError,\n                                            SQLCHAR *MessageText,\n                                            SQLSMALLINT BufferLength,\n                                            SQLSMALLINT *TextLength);\n\nSQLRETURN SQL_API swoole_odbc_SQLPrepare(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength);\n\nSQLRETURN SQL_API swoole_odbc_SQLExecute(SQLHSTMT StatementHandle);\n\nSQLRETURN SQL_API swoole_odbc_SQLCloseCursor(SQLHSTMT StatementHandle);\n\nSQLRETURN SQL_API swoole_odbc_SQLPutData(SQLHSTMT StatementHandle, SQLPOINTER Data, SQLLEN StrLen_or_Ind);\n\nSQLRETURN  SQL_API swoole_odbc_SQLGetData(SQLHSTMT StatementHandle,\n                                  SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType,\n                                  SQLPOINTER TargetValue, SQLLEN BufferLength,\n                                  SQLLEN *StrLen_or_Ind);\n\nSQLRETURN SQL_API swoole_odbc_SQLRowCount(SQLHSTMT StatementHandle, SQLLEN *RowCount);\n\nSQLRETURN  SQL_API swoole_odbc_SQLDescribeCol(SQLHSTMT StatementHandle,\n                                  SQLUSMALLINT ColumnNumber, SQLCHAR *ColumnName,\n                                  SQLSMALLINT BufferLength, SQLSMALLINT *NameLength,\n                                  SQLSMALLINT *DataType, SQLULEN *ColumnSize,\n                                  SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable);\n\nSQLRETURN SQL_API swoole_odbc_SQLMoreResults(\n    SQLHSTMT           hstmt);\n\nSQLRETURN SQL_API swoole_odbc_SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT CompletionType);\n\nSQLRETURN SQL_API swoole_odbc_SQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle);\n\nSQLRETURN SQL_API swoole_odbc_SQLDisconnect(SQLHDBC ConnectionHandle);\n\nvoid swoole_odbc_set_blocking(bool blocking);\n\n#ifdef SW_USE_ODBC_HOOK\n\n#define SQLConnect swoole_odbc_SQLConnect\n#define SQLDriverConnect swoole_odbc_SQLDriverConnect\n#define SQLExecDirect swoole_odbc_SQLExecDirect\n#define SQLGetInfo swoole_odbc_SQLGetInfo\n#define SQLGetDiagRec swoole_odbc_SQLGetDiagRec\n#define SQLPrepare swoole_odbc_SQLPrepare\n#define SQLExecute swoole_odbc_SQLExecute\n#define SQLCloseCursor  swoole_odbc_SQLCloseCursor\n#define SQLGetData swoole_odbc_SQLGetData\n#define SQLPutData swoole_odbc_SQLPutData\n#define SQLRowCount swoole_odbc_SQLRowCount\n#define SQLDescribeCol swoole_odbc_SQLDescribeCol\n#define SQLEndTran swoole_odbc_SQLEndTran\n#define SQLFreeHandle swoole_odbc_SQLFreeHandle\n#define SQLDisconnect swoole_odbc_SQLDisconnect\n\n#endif\nEND_EXTERN_C()\n#endif\n#endif\n"
  },
  {
    "path": "ext-src/php_swoole_oracle.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | Copyright (c) 2012-2018 The Swoole Group                             |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: NathanFreeman  <mariasocute@163.com>                         |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef PHP_SWOOLE_ORACLE_H\n#define PHP_SWOOLE_ORACLE_H\n\n#include \"php_swoole.h\"\n\n#ifdef SW_USE_ORACLE\n\nBEGIN_EXTERN_C()\n\n#include \"ext/pdo/php_pdo_driver.h\"\n\n#include \"thirdparty/pdo_oci/php_pdo_oci_int.h\"\n\nextern const pdo_driver_t swoole_pdo_oci_driver;\n\nvoid swoole_oracle_set_blocking(bool blocking);\nsword swoole_oci_session_begin(OCISvcCtx *svchp, OCIError *errhp, OCISession *usrhp, ub4 credt, ub4 mode);\nsword swoole_oci_server_detach(OCIServer *srvhp, OCIError *errhp, ub4 mode);\nsword swoole_oci_stmt_prepare(\n    OCIStmt *stmtp, OCIError *errhp, const OraText *stmt, ub4 stmt_len, ub4 language, ub4 mode);\nsword swoole_oci_stmt_execute(OCISvcCtx *svchp,\n                              OCIStmt *stmtp,\n                              OCIError *errhp,\n                              ub4 iters,\n                              ub4 rowoff,\n                              const OCISnapshot *snap_in,\n                              OCISnapshot *snap_out,\n                              ub4 mode);\nsword swoole_oci_stmt_fetch(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, ub4 mode);\nsword swoole_oci_stmt_fetch2(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, sb4 scrollOffset, ub4 mode);\nsword swoole_oci_trans_commit(OCISvcCtx *svchp, OCIError *errhp, ub4 flags);\nsword swoole_oci_trans_rollback(OCISvcCtx *svchp, OCIError *errhp, ub4 flags);\nsword swoole_oci_ping(OCISvcCtx *svchp, OCIError *errhp, ub4 mode);\n\n#ifdef SW_USE_ORACLE_HOOK\n#define OCISessionBegin swoole_oci_session_begin\n#define OCIServerDetach swoole_oci_server_detach\n#define OCIStmtPrepare swoole_oci_stmt_prepare\n#define OCIStmtExecute swoole_oci_stmt_execute\n#define OCIStmtFetch swoole_oci_stmt_fetch\n#define OCIStmtFetch2 swoole_oci_stmt_fetch2\n#define OCITransCommit swoole_oci_trans_commit\n#define OCITransRollback swoole_oci_trans_rollback\n#define OCIPing swoole_oci_ping\n#endif\n\nEND_EXTERN_C()\n#endif\n#endif\n"
  },
  {
    "path": "ext-src/php_swoole_pgsql.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef PHP_SWOOLE_PGSQL_H\n#define PHP_SWOOLE_PGSQL_H\n\n#include \"php_swoole.h\"\n\n#ifdef SW_USE_PGSQL\n\nBEGIN_EXTERN_C()\n\n#include \"ext/pdo/php_pdo_driver.h\"\n\n#if PHP_VERSION_ID < 80300\n#include \"thirdparty/php82/pdo_pgsql/php_pdo_pgsql_int.h\"\n#elif PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400\n#include \"thirdparty/php83/pdo_pgsql/php_pdo_pgsql_int.h\"\n#elif PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500\n#include \"thirdparty/php84/pdo_pgsql/php_pdo_pgsql_int.h\"\n#else\n#include \"thirdparty/php85/pdo_pgsql/php_pdo_pgsql_int.h\"\n#endif\n\n\nextern const pdo_driver_t swoole_pdo_pgsql_driver;\n\n#include <libpq-fe.h>\n#include <libpq/libpq-fs.h>\n\nvoid swoole_pgsql_set_blocking(bool blocking);\n\nPGconn *swoole_pgsql_connectdb(const char *conninfo);\nPGresult *swoole_pgsql_prepare(PGconn *conn, const char *stmt_name, const char *query, int n_params, const Oid *param_types);\nPGresult *swoole_pgsql_exec_prepared(PGconn *conn, const char *stmt_name, int n_params,\n    const char *const *param_values, const int *param_lengths, const int *param_formats, int result_format);\nPGresult *swoole_pgsql_exec(PGconn *conn, const char *query);\nPGresult *swoole_pgsql_exec_params(PGconn *conn, const char *command, int n_params,\n    const Oid *param_types, const char *const *param_values, const int *param_lengths, const int *param_formats, int result_format);\n\n#ifdef SW_USE_PGSQL_HOOK\n#define PQconnectdb  swoole_pgsql_connectdb\n#define PQprepare  swoole_pgsql_prepare\n#define PQexecPrepared  swoole_pgsql_exec_prepared\n#define PQexec  swoole_pgsql_exec\n#define PQexecParams  swoole_pgsql_exec_params\n#endif\n\nEND_EXTERN_C()\n\n#endif\n#endif\n"
  },
  {
    "path": "ext-src/php_swoole_private.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef PHP_SWOOLE_PRIVATE_H\n#define PHP_SWOOLE_PRIVATE_H\n\n#include \"php_swoole.h\"\n#include \"php_swoole_api.h\"\n#include \"swoole.h\"\n#include \"swoole_api.h\"\n#include \"swoole_coroutine_api.h\"\n\n#ifdef SW_HAVE_ZLIB\n#include <zlib.h>\n#endif\n\nBEGIN_EXTERN_C()\n#include <ext/date/php_date.h>\n#include <ext/standard/url.h>\n#include <ext/standard/info.h>\n#include <ext/standard/php_array.h>\n#include <ext/standard/php_var.h>\n#include <ext/standard/basic_functions.h>\n#include <ext/standard/php_http.h>\n\n#define PHP_SWOOLE_VERSION SWOOLE_VERSION\n\n#ifdef PHP_WIN32\n#define PHP_SWOOLE_API __declspec(dllexport)\n#elif defined(__GNUC__) && __GNUC__ >= 4\n#define PHP_SWOOLE_API __attribute__((visibility(\"default\")))\n#else\n#define PHP_SWOOLE_API\n#endif\n\n#define SW_CHECK_RETURN(s)                                                                                             \\\n    if (s < 0) {                                                                                                       \\\n        RETURN_FALSE;                                                                                                  \\\n    } else {                                                                                                           \\\n        RETURN_TRUE;                                                                                                   \\\n    }\n\n#define SW_LOCK_CHECK_RETURN(s)                                                                                        \\\n    zend_long ___tmp_return_value = s;                                                                                 \\\n    if (___tmp_return_value == 0) {                                                                                    \\\n        RETURN_TRUE;                                                                                                   \\\n    } else {                                                                                                           \\\n        zend_update_property_long(NULL, SW_Z8_OBJ_P(ZEND_THIS), SW_STRL(\"errCode\"), ___tmp_return_value);              \\\n        RETURN_FALSE;                                                                                                  \\\n    }\n\n#ifdef SW_THREAD\n#define SW_MUST_BE_MAIN_THREAD_EX(op)                                                                                  \\\n    if (!tsrm_is_main_thread()) {                                                                                      \\\n        swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT);                                                         \\\n        op;                                                                                                            \\\n    }\n#define SW_MUST_BE_MAIN_THREAD() SW_MUST_BE_MAIN_THREAD_EX(RETURN_TRUE)\n#else\n#define SW_MUST_BE_MAIN_THREAD_EX(op)\n#define SW_MUST_BE_MAIN_THREAD()\n#endif\n\n#define php_swoole_fatal_error(level, fmt_str, ...)                                                                    \\\n    swoole_set_last_error(SW_ERROR_PHP_FATAL_ERROR);                                                                   \\\n    php_error_docref(NULL, level, (const char *) (fmt_str), ##__VA_ARGS__)\n\n/**\n * The error occurred at the PHP layer and no error code was set\n */\n#define php_swoole_error(level, fmt_str, ...)                                                                          \\\n    swoole_set_last_error(SW_ERROR_PHP_RUNTIME_NOTICE);                                                                \\\n    if (SWOOLE_G(display_errors) || level == E_ERROR) php_error_docref(NULL, level, fmt_str, ##__VA_ARGS__)\n\n/**\n * The error occurred in the core must have error code\n */\n#define php_swoole_core_error(level, fmt_str, ...)                                                                     \\\n    if (SWOOLE_G(display_errors) || level == E_ERROR) php_error_docref(NULL, level, fmt_str, ##__VA_ARGS__)\n\n#define php_swoole_error_ex(level, err_code, fmt_str, ...)                                                             \\\n    swoole_set_last_error(err_code);                                                                                   \\\n    if (SWOOLE_G(display_errors) || level == E_ERROR) php_error_docref(NULL, level, fmt_str, ##__VA_ARGS__)\n\n#define php_swoole_sys_error(level, fmt_str, ...)                                                                      \\\n    swoole_set_last_error(errno);                                                                                      \\\n    if (SWOOLE_G(display_errors) || level == E_ERROR)                                                                  \\\n    php_error_docref(NULL, level, fmt_str \", Error: %s[%d]\", ##__VA_ARGS__, strerror(errno), errno)\n\n#ifdef SW_USE_CARES\n#ifndef HAVE_CARES\n#error \"Enable c-ares support, require c-ares library\"\n#endif\n#endif\n\n#if defined(SW_HAVE_ZLIB) || defined(SW_HAVE_BROTLI) || defined(SW_HAVE_ZSTD)\n#define SW_HAVE_COMPRESSION\n#endif\n\n#ifdef SW_SOCKETS\n#include \"ext/sockets/php_sockets.h\"\n#define SWOOLE_SOCKETS_SUPPORT\n#endif\n\n#if PHP_VERSION_ID < 80200\n#error \"require PHP version 8.2 or later\"\n#elif PHP_VERSION_ID >= 80600\n#error \"require PHP version 8.5 or earlier\"\n#endif\n\n#if defined(ZTS) && defined(SW_USE_THREAD_CONTEXT)\n#error \"thread context cannot be used with ZTS\"\n#endif\n\n#if defined(SW_USE_IOURING) && !defined(__linux__)\n#error \"only linux support iouring\"\n#endif\n\n#if defined(SW_THREAD) && !defined(ZTS)\n#error \"swoole thread must be used with ZTS\"\n#endif\n\n//--------------------------------------------------------\n#define SW_MAX_FIND_COUNT 100  // for swoole_server::connection_list\n#define SW_PHP_CLIENT_BUFFER_SIZE 65535\n#define SW_ASYNC_FILE_PROTOCOL \"async.file\"\n//---------------------------------------------------------\nenum php_swoole_fd_type {\n    PHP_SWOOLE_FD_STREAM_CLIENT = SW_FD_STREAM_CLIENT,\n    PHP_SWOOLE_FD_DGRAM_CLIENT,\n    PHP_SWOOLE_FD_MYSQL,\n    PHP_SWOOLE_FD_REDIS,\n    PHP_SWOOLE_FD_HTTPCLIENT,\n    PHP_SWOOLE_FD_PROCESS_STREAM,\n    PHP_SWOOLE_FD_MYSQL_CORO,\n    PHP_SWOOLE_FD_REDIS_CORO,\n    PHP_SWOOLE_FD_POSTGRESQL,\n    PHP_SWOOLE_FD_SOCKET,\n    PHP_SWOOLE_FD_CO_CURL,\n};\n//---------------------------------------------------------\nenum php_swoole_req_status {\n    PHP_SWOOLE_RINIT_BEGIN,\n    PHP_SWOOLE_RINIT_END,\n    PHP_SWOOLE_CALL_USER_SHUTDOWNFUNC_BEGIN,\n    PHP_SWOOLE_RSHUTDOWN_BEGIN,\n    PHP_SWOOLE_RSHUTDOWN_END,\n};\n//---------------------------------------------------------\nenum php_swoole_hook_type {\n    PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK = SW_GLOBAL_HOOK_USER,\n    PHP_SWOOLE_HOOK_AFTER_ENABLE_HOOK,\n    PHP_SWOOLE_HOOK_BEFORE_REQUEST,\n    PHP_SWOOLE_HOOK_AFTER_RESPONSE,\n};\n//---------------------------------------------------------\n\nextern zend_class_entry *swoole_event_ce;\nextern zend_class_entry *swoole_timer_ce;\nextern zend_class_entry *swoole_socket_coro_ce;\nextern zend_class_entry *swoole_client_ce;\nextern zend_object_handlers swoole_client_handlers;\nextern zend_class_entry *swoole_server_ce;\nextern zend_object_handlers swoole_server_handlers;\nextern zend_class_entry *swoole_redis_server_ce;\nextern zend_object_handlers swoole_redis_server_handlers;\nextern zend_class_entry *swoole_connection_iterator_ce;\nextern zend_class_entry *swoole_process_ce;\nextern zend_class_entry *swoole_http_server_ce;\nextern zend_object_handlers swoole_http_server_handlers;\nextern zend_class_entry *swoole_websocket_server_ce;\nextern zend_class_entry *swoole_websocket_frame_ce;\nextern zend_class_entry *swoole_server_port_ce;\nextern zend_class_entry *swoole_exception_ce;\nextern zend_object_handlers swoole_exception_handlers;\nextern zend_class_entry *swoole_error_ce;\nextern zend_class_entry *swoole_resolve_context_ce;\nextern zend_object_handlers swoole_resolve_context_handlers;\n\nPHP_FUNCTION(swoole_clear_dns_cache);\nPHP_FUNCTION(swoole_last_error);\nPHP_FUNCTION(swoole_set_process_name);\n//---------------------------------------------------------\n//                  Coroutine API\n//---------------------------------------------------------\nPHP_FUNCTION(swoole_coroutine_create);\nPHP_FUNCTION(swoole_coroutine_exec);\nPHP_FUNCTION(swoole_coroutine_gethostbyname);\nPHP_FUNCTION(swoole_coroutine_defer);\nPHP_FUNCTION(swoole_coroutine_socketpair);\nPHP_FUNCTION(swoole_test_kernel_coroutine);  // for tests\n//---------------------------------------------------------\n//                  event\n//---------------------------------------------------------\nPHP_FUNCTION(swoole_client_select);\n//---------------------------------------------------------\n//                  async[coro]\n//---------------------------------------------------------\nPHP_FUNCTION(swoole_async_set);\nPHP_FUNCTION(swoole_async_dns_lookup_coro);\n//---------------------------------------------------------\n//                  tracer\n//---------------------------------------------------------\nPHP_FUNCTION(swoole_tracer_leak_detect);\nPHP_FUNCTION(swoole_tracer_prof_begin);\nPHP_FUNCTION(swoole_tracer_prof_end);\n//---------------------------------------------------------\n//                  error\n//---------------------------------------------------------\n#define SW_STRERROR_SYSTEM 0\n#define SW_STRERROR_GAI 1\n#define SW_STRERROR_DNS 2\n#define SW_STRERROR_SWOOLE 9\n\n/**\n * MINIT <Sort by dependency>\n * ==============================================================\n */\nvoid php_swoole_event_minit(int module_number);\n// base\nvoid php_swoole_atomic_minit(int module_number);\nvoid php_swoole_lock_minit(int module_number);\nint swoole_resolve_context_module_init(INIT_FUNC_ARGS);\nvoid php_swoole_process_minit(int module_number);\nvoid php_swoole_process_pool_minit(int module_number);\nvoid php_swoole_table_minit(int module_number);\nvoid php_swoole_timer_minit(int module_number);\n// coroutine\nvoid php_swoole_coroutine_minit(int module_number);\nvoid php_swoole_coroutine_system_minit(int module_number);\nvoid php_swoole_coroutine_scheduler_minit(int module_number);\nvoid php_swoole_coroutine_lock_minit(int module_number);\nvoid php_swoole_channel_coro_minit(int module_number);\nvoid php_swoole_runtime_minit(int module_number);\n// client\nvoid php_swoole_socket_coro_minit(int module_number);\nvoid php_swoole_client_minit(int module_number);\nvoid php_swoole_client_async_minit(int module_number);\nvoid php_swoole_client_coro_minit(int module_number);\nvoid php_swoole_http_client_coro_minit(int module_number);\nvoid php_swoole_http2_client_coro_minit(int module_number);\n#ifdef SW_USE_PGSQL\nvoid php_swoole_pgsql_minit(int module_number);\n#endif\n#ifdef SW_USE_ODBC\nint php_swoole_odbc_minit(int module_id);\n#endif\n#ifdef SW_USE_ORACLE\nvoid php_swoole_oracle_minit(int module_number);\n#endif\n#ifdef SW_USE_SQLITE\nvoid php_swoole_sqlite_minit(int module_number);\n#endif\n#ifdef SW_USE_FIREBIRD\nvoid php_swoole_firebird_minit(int module_number);\n#endif\n// server\nvoid php_swoole_server_minit(int module_number);\nvoid php_swoole_server_port_minit(int module_number);\nvoid php_swoole_http_request_minit(int module_number);\nvoid php_swoole_http_response_minit(int module_number);\nvoid php_swoole_http_cookie_minit(int module_number);\nvoid php_swoole_http_server_minit(int module_number);\nvoid php_swoole_http_server_coro_minit(int module_number);\nvoid php_swoole_websocket_server_minit(int module_number);\nvoid php_swoole_redis_server_minit(int module_number);\nvoid php_swoole_name_resolver_minit(int module_number);\n#ifdef SW_THREAD\nvoid php_swoole_thread_minit(int module_number);\nvoid php_swoole_thread_atomic_minit(int module_number);\nvoid php_swoole_thread_lock_minit(int module_number);\nvoid php_swoole_thread_barrier_minit(int module_number);\nvoid php_swoole_thread_queue_minit(int module_number);\nvoid php_swoole_thread_map_minit(int module_number);\nvoid php_swoole_thread_arraylist_minit(int module_number);\n#endif\n#ifdef SW_STDEXT\nvoid php_swoole_stdext_minit(int module_number);\n#endif\nvoid php_swoole_tracer_minit(int module_number);\n\n/**\n * RINIT\n * ==============================================================\n */\nvoid php_swoole_http_server_rinit();\nvoid php_swoole_coroutine_rinit();\nvoid php_swoole_runtime_rinit();\n#ifdef SW_USE_ORACLE\nvoid php_swoole_oracle_rinit();\n#endif\nvoid php_swoole_thread_rinit();\nvoid php_swoole_tracer_rinit();\n\n/**\n * RSHUTDOWN\n * ==============================================================\n */\nvoid php_swoole_http_server_rshutdown();\nvoid php_swoole_http_response_rshutdown();\nvoid php_swoole_async_coro_rshutdown();\nvoid php_swoole_redis_server_rshutdown();\nvoid php_swoole_coroutine_rshutdown();\nvoid php_swoole_process_rshutdown();\nvoid php_swoole_coroutine_scheduler_rshutdown();\nvoid php_swoole_runtime_rshutdown();\nvoid php_swoole_timer_rshutdown();\nvoid php_swoole_server_rshutdown();\n#ifdef SW_THREAD\nvoid php_swoole_thread_rshutdown();\n#endif\nvoid php_swoole_tracer_rshutdown();\n\nint php_swoole_reactor_init();\nvoid php_swoole_set_global_option(zend_array *vht);\nvoid php_swoole_set_coroutine_option(zend_array *vht);\nvoid php_swoole_set_aio_option(const zend_array *vht);\n\n// shutdown\nvoid php_swoole_register_shutdown_function(const char *function);\nvoid php_swoole_register_shutdown_function_prepend(const char *function);\n\n// event\nvoid php_swoole_event_init();\nvoid php_swoole_event_wait();\nvoid php_swoole_event_exit();\n\n/**\n * MSHUTDOWN\n * ==============================================================\n */\nvoid php_swoole_runtime_mshutdown();\n#ifdef SW_USE_PGSQL\nvoid php_swoole_pgsql_mshutdown();\n#endif\n#ifdef SW_USE_ORACLE\nvoid php_swoole_oracle_mshutdown();\n#endif\n#ifdef SW_USE_SQLITE\nvoid php_swoole_sqlite_mshutdown();\n#endif\n#ifdef SW_USE_FIREBIRD\nvoid php_swoole_firebird_mshutdown();\n#endif\n\nint php_swoole_convert_to_fd(zval *zsocket);\nint php_swoole_convert_to_fd_ex(zval *zsocket, int *async);\n\n#ifdef SWOOLE_SOCKETS_SUPPORT\nphp_socket *php_swoole_convert_to_socket(int sock);\n#endif\n\n#ifdef HAVE_CPU_AFFINITY\nbool php_swoole_array_to_cpu_set(const zval *array, cpu_set_t *cpu_set);\n/**\n * Converts a cpu_set_t structure to a PHP array.\n *\n * Note: On Cygwin platform, CPU_ISSET is a function that takes a non-const pointer as its second parameter,\n * which is why the cpu_set parameter cannot be declared as const.\n *\n * @param array The PHP array to store the CPU set information\n * @param cpu_set The CPU set structure to convert\n */\nvoid php_swoole_cpu_set_to_array(zval *array, cpu_set_t *cpu_set);\n#endif\n\nzend_bool php_swoole_signal_isset_handler(int signo);\n\n#define sw_zend7_object zend_object\n#define SW_Z8_OBJ_P(zobj) Z_OBJ_P(zobj)\n\ntypedef ssize_t php_stream_size_t;\ntypedef zend_string error_filename_t;\n\n//----------------------------------Zval API------------------------------------\n\n// Deprecated: do not use it anymore\n// do not use sw_copy_to_stack(return_value, foo);\n#define sw_copy_to_stack(ptr, val)                                                                                     \\\n    do {                                                                                                               \\\n        (val) = *(zval *) (ptr);                                                                                       \\\n        (ptr) = &(val);                                                                                                \\\n    } while (0)\n\n#define SW_ZVAL_SOCKET(return_value, result) ZVAL_OBJ(return_value, &result->std)\n#define SW_Z_SOCKET_P(zsocket) Z_SOCKET_P(zsocket)\n\nstatic sw_inline zend_bool ZVAL_IS_TRUE(const zval *v) {\n    return Z_TYPE_P(v) == IS_TRUE;\n}\n\nstatic sw_inline zend_bool ZVAL_IS_FALSE(const zval *v) {\n    return Z_TYPE_P(v) == IS_FALSE;\n}\n\nstatic sw_inline zend_bool ZVAL_IS_BOOL(const zval *v) {\n    return ZVAL_IS_TRUE(v) || ZVAL_IS_FALSE(v);\n}\n\nstatic sw_inline zend_bool ZVAL_IS_UNDEF(const zval *v) {\n    return Z_TYPE_P(v) == IS_UNDEF;\n}\n\nstatic sw_inline zend_bool ZVAL_IS_LONG(const zval *v) {\n    return Z_TYPE_P(v) == IS_LONG;\n}\n\nstatic sw_inline zend_bool ZVAL_IS_STRING(const zval *v) {\n    return Z_TYPE_P(v) == IS_STRING;\n}\n\nstatic sw_inline zend_bool ZVAL_IS_EMPTY_STRING(const zval *v) {\n    return Z_TYPE_P(v) == IS_STRING && Z_STRLEN_P(v) == 0;\n}\n\nstatic sw_inline zend_bool Z_BVAL_P(const zval *v) {\n    return Z_TYPE_P(v) == IS_TRUE;\n}\n\nstatic sw_inline zend_bool ZVAL_IS_ARRAY(const zval *v) {\n    return Z_TYPE_P(v) == IS_ARRAY;\n}\n\nstatic sw_inline zend_bool ZVAL_IS_REF(const zval *v) {\n    return Z_TYPE_P(v) == IS_REFERENCE;\n}\n\nstatic sw_inline zend_bool ZVAL_IS_OBJECT(const zval *v) {\n    return Z_TYPE_P(v) == IS_OBJECT;\n}\n\nstatic sw_inline zval *sw_malloc_zval() {\n    return static_cast<zval *>(emalloc(sizeof(zval)));\n}\n\nstatic sw_inline zval *sw_zval_dup(zval *val) {\n    zval *dup = sw_malloc_zval();\n    memcpy(dup, val, sizeof(zval));\n    return dup;\n}\n\nstatic sw_inline void sw_zval_free(zval *val) {\n    zval_ptr_dtor(val);\n    efree(val);\n}\n\n#ifdef SWOOLE_SOCKETS_SUPPORT\nstatic inline bool sw_zval_is_php_socket(zval *val) {\n    return instanceof_function(Z_OBJCE_P(val), socket_ce);\n}\n#endif\n\nstatic inline bool sw_zval_is_co_socket(zval *val) {\n    return instanceof_function(Z_OBJCE_P(val), swoole_socket_coro_ce);\n}\n\nstatic inline bool sw_zval_is_client(zval *val) {\n    return instanceof_function(Z_OBJCE_P(val), swoole_client_ce);\n}\n\nstatic inline bool sw_zval_is_process(zval *val) {\n    return instanceof_function(Z_OBJCE_P(val), swoole_process_ce);\n}\n\nbool sw_zval_is_serializable(const zval *struc);\n\nstatic inline bool sw_is_main_thread() {\n#ifdef SW_THREAD\n    return tsrm_is_main_thread();\n#else\n    return true;\n#endif\n}\n\nint sw_module_number();\n\n#ifdef SW_THREAD\nsize_t sw_active_thread_count(void);\n#else\nstatic inline size_t sw_active_thread_count() {\n    return 1;\n}\n#endif\n\nzend_refcounted *sw_get_refcount_ptr(zval *value);\n\nvoid sw_php_exit(int status);\nvoid sw_php_print_backtrace(zend_long cid = 0,\n                            zend_long options = 0,\n                            zend_long limit = 0,\n                            zval *return_value = nullptr);\nvoid sw_php_print_backtrace_impl(int skip_last = 1, int options = 0, int limit = 0, bool include_main = false);\n\n//----------------------------------Constant API------------------------------------\n\n#define SW_REGISTER_NULL_CONSTANT(name) REGISTER_NULL_CONSTANT(name, CONST_CS | CONST_PERSISTENT)\n#define SW_REGISTER_BOOL_CONSTANT(name, value) REGISTER_BOOL_CONSTANT(name, value, CONST_CS | CONST_PERSISTENT)\n#define SW_REGISTER_LONG_CONSTANT(name, value) REGISTER_LONG_CONSTANT(name, value, CONST_CS | CONST_PERSISTENT)\n#define SW_REGISTER_DOUBLE_CONSTANT(name, value) REGISTER_DOUBLE_CONSTANT(name, value, CONST_CS | CONST_PERSISTENT)\n#define SW_REGISTER_STRING_CONSTANT(name, value)                                                                       \\\n    REGISTER_STRING_CONSTANT(name, (char *) value, CONST_CS | CONST_PERSISTENT)\n#define SW_REGISTER_STRINGL_CONSTANT(name, value)                                                                      \\\n    REGISTER_STRINGL_CONSTANT(name, (char *) value, CONST_CS | CONST_PERSISTENT)\n\n//----------------------------------Number API-----------------------------------\n\n#define sw_php_math_round(value, places, mode) _php_math_round(value, places, mode)\n\n//----------------------------------String API-----------------------------------\n\n#define SW_PHP_OB_START(zoutput)                                                                                       \\\n    zval zoutput;                                                                                                      \\\n    do {                                                                                                               \\\n        php_output_start_user(NULL, 0, PHP_OUTPUT_HANDLER_STDFLAGS);\n#define SW_PHP_OB_END()                                                                                                \\\n    php_output_get_contents(&zoutput);                                                                                 \\\n    php_output_discard();                                                                                              \\\n    }                                                                                                                  \\\n    while (0)\n\nstatic sw_inline zend_string *sw_zend_string_recycle(zend_string *s, size_t alloc_len, size_t real_len) {\n    SW_ASSERT(!ZSTR_IS_INTERNED(s));\n    if (UNEXPECTED(alloc_len != real_len)) {\n        if (alloc_len > swoole_pagesize() && alloc_len > real_len * 2) {\n            s = zend_string_realloc(s, real_len, false);\n        } else {\n            ZSTR_LEN(s) = real_len;\n        }\n    }\n    ZSTR_VAL(s)[real_len] = '\\0';\n    return s;\n}\n\n//----------------------------------Array API------------------------------------\n\n#define php_swoole_array_length(zarray) zend_hash_num_elements(Z_ARRVAL_P(zarray))\n#define php_swoole_array_get_value(ht, str, v) ((v = zend_hash_str_find(ht, str, sizeof(str) - 1)) && !ZVAL_IS_NULL(v))\n#define php_swoole_array_get_value_ex(ht, str, v) ((v = zend_hash_str_find(ht, str, strlen(str))) && !ZVAL_IS_NULL(v))\n\nstatic sw_inline int php_swoole_array_length_safe(zval *zarray) {\n    if (zarray && ZVAL_IS_ARRAY(zarray)) {\n        return php_swoole_array_length(zarray);\n    } else {\n        return 0;\n    }\n}\n\nvoid php_swoole_sha1(const char *str, int _len, uchar *digest);\nvoid php_swoole_sha256(const char *str, int _len, uchar *digest);\n\n#define SW_HASHTABLE_FOREACH_START(ht, _val)                                                                           \\\n    ZEND_HASH_FOREACH_VAL(ht, _val);                                                                                   \\\n    {\n#define SW_HASHTABLE_FOREACH_START2(ht, k, klen, ktype, _val)                                                          \\\n    zend_string *_foreach_key;                                                                                         \\\n    ZEND_HASH_FOREACH_STR_KEY_VAL(ht, _foreach_key, _val);                                                             \\\n    if (!_foreach_key) {                                                                                               \\\n        k = NULL;                                                                                                      \\\n        klen = 0;                                                                                                      \\\n        ktype = 0;                                                                                                     \\\n    } else {                                                                                                           \\\n        k = ZSTR_VAL(_foreach_key), klen = ZSTR_LEN(_foreach_key);                                                     \\\n        ktype = 1;                                                                                                     \\\n    }                                                                                                                  \\\n    {\n#define SW_HASHTABLE_FOREACH_END()                                                                                     \\\n    }                                                                                                                  \\\n    ZEND_HASH_FOREACH_END();\n\nstatic sw_inline void add_assoc_ulong_safe_ex(zval *arg, const char *key, size_t key_len, zend_ulong value) {\n    if (sw_likely(value <= ZEND_LONG_MAX)) {\n        add_assoc_long_ex(arg, key, key_len, value);\n    } else {\n        char buf[MAX_LENGTH_OF_LONG + 1];\n        size_t len = sw_snprintf(buf, sizeof(buf), ZEND_ULONG_FMT, value);\n        add_assoc_stringl_ex(arg, key, key_len, buf, len);\n    }\n}\n\nstatic sw_inline void add_assoc_ulong_safe(zval *arg, const char *key, zend_ulong value) {\n    add_assoc_ulong_safe_ex(arg, key, strlen(key), value);\n}\n\n//----------------------------------Class API------------------------------------\n\n#define SW_Z_OBJCE_NAME_VAL_P(zobject) ZSTR_VAL(Z_OBJCE_P(zobject)->name)\n\n/* PHP 7 class declaration macros */\n\n#define SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, short_name, methods, parent_ce)                               \\\n    do {                                                                                                               \\\n        zend_class_entry _##module##_ce = {};                                                                          \\\n        INIT_CLASS_ENTRY(_##module##_ce, namespace_name, methods);                                                     \\\n        module##_ce = zend_register_internal_class_ex(&_##module##_ce, parent_ce);                                     \\\n        if (short_name) SW_CLASS_ALIAS_SHORT_NAME(short_name, module);                                                 \\\n    } while (0)\n\n#define SW_INIT_CLASS_ENTRY_STD(module, namespace_name, methods)                                                       \\\n    SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, nullptr, methods, NULL);                                          \\\n    memcpy(&module##_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers))\n\n#define SW_INIT_CLASS_ENTRY(module, namespace_name, short_name, methods)                                               \\\n    SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, short_name, methods, NULL);                                       \\\n    memcpy(&module##_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers))\n\n#define SW_INIT_CLASS_ENTRY_EX(module, namespace_name, short_name, methods, parent_module)                             \\\n    SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, short_name, methods, parent_module##_ce);                         \\\n    memcpy(&module##_handlers, &parent_module##_handlers, sizeof(zend_object_handlers))\n\n#define SW_INIT_CLASS_ENTRY_EX2(module, namespace_name, short_name, methods, parent_module_ce, parent_module_handlers) \\\n    SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, short_name, methods, parent_module_ce);                           \\\n    memcpy(&module##_handlers, parent_module_handlers, sizeof(zend_object_handlers))\n\n// Data Object: no methods, no parent\n#define SW_INIT_CLASS_ENTRY_DATA_OBJECT(module, namespace_name)                                                        \\\n    SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, NULL, NULL, NULL);                                                \\\n    memcpy(&module##_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers))\n\n#define SW_CLASS_ALIAS(name, module)                                                                                   \\\n    do {                                                                                                               \\\n        if (name) {                                                                                                    \\\n            sw_zend_register_class_alias(ZEND_STRL(name), module##_ce);                                                \\\n        }                                                                                                              \\\n    } while (0)\n\n#define SW_CLASS_ALIAS_SHORT_NAME(short_name, module)                                                                  \\\n    do {                                                                                                               \\\n        if (SWOOLE_G(use_shortname)) {                                                                                 \\\n            SW_CLASS_ALIAS(short_name, module);                                                                        \\\n        }                                                                                                              \\\n    } while (0)\n\n#define SW_SET_CLASS_NOT_SERIALIZABLE(module) module##_ce->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE;\n\n#define sw_zend_class_clone_deny NULL\n#define SW_SET_CLASS_CLONEABLE(module, _clone_obj) module##_handlers.clone_obj = _clone_obj\n\n#define SW_SET_CLASS_UNSET_PROPERTY_HANDLER(module, _unset_property) module##_handlers.unset_property = _unset_property\n\n#define SW_SET_CLASS_CREATE(module, _create_object) module##_ce->create_object = _create_object\n\n#define SW_SET_CLASS_DTOR(module, _dtor_obj) module##_handlers.dtor_obj = _dtor_obj\n\n#define SW_SET_CLASS_FREE(module, _free_obj) module##_handlers.free_obj = _free_obj\n\n#define SW_SET_CLASS_CREATE_AND_FREE(module, _create_object, _free_obj)                                                \\\n    SW_SET_CLASS_CREATE(module, _create_object);                                                                       \\\n    SW_SET_CLASS_FREE(module, _free_obj)\n\n#define SW_SET_CLASS_CUSTOM_OBJECT(module, _create_object, _free_obj, _struct, _std)                                   \\\n    SW_SET_CLASS_CREATE_AND_FREE(module, _create_object, _free_obj);                                                   \\\n    module##_handlers.offset = XtOffsetOf(_struct, _std)\n\n#define SW_PREVENT_USER_DESTRUCT()                                                                                     \\\n    do {                                                                                                               \\\n        if (sw_unlikely(!(GC_FLAGS(Z_OBJ_P(ZEND_THIS)) & IS_OBJ_DESTRUCTOR_CALLED))) {                                 \\\n            RETURN_NULL();                                                                                             \\\n        }                                                                                                              \\\n    } while (0)\n\n#define SW_FUNCTION_ALIAS(origin_function_table, origin, alias_function_table, alias, arg_info)                        \\\n    sw_zend_register_function_alias(                                                                                   \\\n        origin_function_table, ZEND_STRL(origin), alias_function_table, ZEND_STRL(alias), arg_info)\n\nstatic sw_inline int sw_zend_register_function_alias(zend_array *origin_function_table,\n                                                     const char *origin,\n                                                     size_t origin_length,\n                                                     zend_array *alias_function_table,\n                                                     const char *alias,\n                                                     size_t alias_length,\n                                                     const zend_internal_arg_info *arg_info) {\n    zend_string *lowercase_origin = zend_string_alloc(origin_length, false);\n    zend_str_tolower_copy(ZSTR_VAL(lowercase_origin), origin, origin_length);\n    auto *origin_function = static_cast<zend_function *>(zend_hash_find_ptr(origin_function_table, lowercase_origin));\n    zend_string_release(lowercase_origin);\n    if (UNEXPECTED(!origin_function)) {\n        return FAILURE;\n    }\n    SW_ASSERT(origin_function->common.type == ZEND_INTERNAL_FUNCTION);\n    char *_alias = (char *) emalloc(alias_length + 1);\n    ((char *) memcpy(_alias, alias, alias_length))[alias_length] = '\\0';\n\n    zend_function_entry zfe[] = {\n        {_alias, origin_function->internal_function.handler, arg_info, origin_function->common.num_args, 0},\n        PHP_FE_END};\n    int ret = zend_register_functions(nullptr, zfe, alias_function_table, origin_function->common.type);\n    efree(_alias);\n    return ret;\n}\n\nstatic sw_inline int sw_zend_register_class_alias(const char *name, size_t name_len, zend_class_entry *ce) {\n    zend_string *_name;\n    if (name[0] == '\\\\') {\n        _name = zend_string_init(name, name_len, true);\n        zend_str_tolower_copy(ZSTR_VAL(_name), name + 1, name_len - 1);\n    } else {\n        _name = zend_string_init(name, name_len, true);\n        zend_str_tolower_copy(ZSTR_VAL(_name), name, name_len);\n    }\n\n    zend_string *_interned_name = zend_new_interned_string(_name);\n\n    return zend_register_class_alias_ex(ZSTR_VAL(_interned_name), ZSTR_LEN(_interned_name), ce, true);\n}\n\nstatic sw_inline zend_object *sw_zend_create_object(zend_class_entry *ce, zend_object_handlers *handlers) {\n    auto *object = static_cast<zend_object *>(zend_object_alloc(sizeof(zend_object), ce));\n    zend_object_std_init(object, ce);\n    object_properties_init(object, ce);\n    object->handlers = handlers;\n    return object;\n}\n\nstatic sw_inline zend_object *sw_zend_create_object_deny(zend_class_entry *ce) {\n    zend_object *object = zend_objects_new(ce);\n    /* Initialize default properties */\n    if (EXPECTED(ce->default_properties_count != 0)) {\n        zval *p = object->properties_table;\n        zval *end = p + ce->default_properties_count;\n        do {\n            ZVAL_UNDEF(p);\n            p++;\n        } while (p != end);\n    }\n    zend_throw_error(nullptr, \"The object of %s can not be created for security reasons\", ZSTR_VAL(ce->name));\n    return object;\n}\n\nstatic sw_inline void sw_zend_class_unset_property_deny(zend_object *object, zend_string *member, void **cache_slot) {\n    zend_class_entry *ce = object->ce;\n    while (ce->parent) {\n        ce = ce->parent;\n    }\n    SW_ASSERT(ce->type == ZEND_INTERNAL_CLASS);\n    if (EXPECTED(zend_hash_find(&ce->properties_info, member))) {\n        zend_throw_error(\n            nullptr, \"Property %s of class %s cannot be unset\", ZSTR_VAL(member), ZSTR_VAL(object->ce->name));\n        return;\n    }\n    std_object_handlers.unset_property(object, member, cache_slot);\n}\n\nstatic sw_inline zval *sw_zend_read_property(zend_class_entry *ce, zval *obj, const char *s, size_t len, int silent) {\n    zval rv, *property = zend_read_property(ce, SW_Z8_OBJ_P(obj), s, len, silent, &rv);\n    if (UNEXPECTED(property == &EG(uninitialized_zval))) {\n        zend_update_property_null(ce, SW_Z8_OBJ_P(obj), s, len);\n        return zend_read_property(ce, SW_Z8_OBJ_P(obj), s, len, silent, &rv);\n    }\n    return property;\n}\n\nstatic sw_inline void sw_zend_update_property_null_ex(zend_class_entry *scope, zval *object, zend_string *s) {\n    zval tmp;\n\n    ZVAL_NULL(&tmp);\n    zend_update_property_ex(scope, SW_Z8_OBJ_P(object), s, &tmp);\n}\n\nstatic sw_inline zval *sw_zend_read_property_ex(zend_class_entry *ce, zval *zobject, zend_string *name, int silent) {\n    zval *zv = zend_hash_find(&ce->properties_info, name);\n    auto *property_info = (zend_property_info *) Z_PTR_P(zv);\n    zval *property = OBJ_PROP(SW_Z8_OBJ_P(zobject), property_info->offset);\n    if (UNEXPECTED(property == &EG(uninitialized_zval))) {\n        ZVAL_NULL(property);\n    }\n    return property;\n}\n\nstatic sw_inline zval *sw_zend_read_property_not_null(\n    zend_class_entry *ce, zval *obj, const char *s, size_t len, int silent) {\n    zval rv, *property = zend_read_property(ce, SW_Z8_OBJ_P(obj), s, len, silent, &rv);\n    zend_uchar type = Z_TYPE_P(property);\n    return (type == IS_NULL || UNEXPECTED(type == IS_UNDEF)) ? nullptr : property;\n}\n\nstatic sw_inline zval *sw_zend_read_property_not_null_ex(zend_class_entry *ce, zval *obj, zend_string *s, int silent) {\n    zval rv, *property = zend_read_property_ex(ce, SW_Z8_OBJ_P(obj), s, silent, &rv);\n    zend_uchar type = Z_TYPE_P(property);\n    return (type == IS_NULL || UNEXPECTED(type == IS_UNDEF)) ? nullptr : property;\n}\n\nstatic sw_inline zval *sw_zend_update_and_read_property_array(zend_class_entry *ce,\n                                                              zval *obj,\n                                                              const char *s,\n                                                              size_t len) {\n    zval ztmp;\n    array_init(&ztmp);\n    zend_update_property(ce, SW_Z8_OBJ_P(obj), s, len, &ztmp);\n    zval_ptr_dtor(&ztmp);\n    return zend_read_property(ce, SW_Z8_OBJ_P(obj), s, len, true, &ztmp);\n}\n\nstatic sw_inline zval *sw_zend_read_and_convert_property_array(\n    zend_class_entry *ce, zval *obj, const char *s, size_t len, int silent) {\n    zval rv, *property = zend_read_property(ce, SW_Z8_OBJ_P(obj), s, len, silent, &rv);\n    if (Z_TYPE_P(property) != IS_ARRAY) {\n        // NOTICE: if user unset the property, zend_read_property will return uninitialized_zval instead of NULL pointer\n        if (UNEXPECTED(property == &EG(uninitialized_zval))) {\n            property = sw_zend_update_and_read_property_array(ce, obj, s, len);\n        } else {\n            zval_ptr_dtor(property);\n            array_init(property);\n        }\n    }\n\n    return property;\n}\n\n#define SW_RETURN_PROPERTY(name)                                                                                       \\\n    do {                                                                                                               \\\n        RETURN_ZVAL(sw_zend_read_property(Z_OBJCE_P(ZEND_THIS), ZEND_THIS, ZEND_STRL(name), 0), 1, 0);                 \\\n    } while (0)\n\n#define RETURN_SW_STRING(buf)                                                                                          \\\n    do {                                                                                                               \\\n        RETURN_STRINGL(buf->str, buf->length);                                                                         \\\n    } while (0)\n\n//----------------------------------Function API------------------------------------\n\n/**\n * Notice (sw_zend_call_method_with_%u_params): If you don't want to check the return value, please set retval to NULL\n */\n#define sw_zend_call_method_with_0_params(zobj, obj_ce, fn_ptr_ptr, fn_name, retval)                                   \\\n    zend_call_method_with_0_params(SW_Z8_OBJ_P(zobj), obj_ce, fn_ptr_ptr, fn_name, retval)\n\n#define sw_zend_call_method_with_1_params(zobj, obj_ce, fn_ptr_ptr, fn_name, retval, v1)                               \\\n    zend_call_method_with_1_params(SW_Z8_OBJ_P(zobj), obj_ce, fn_ptr_ptr, fn_name, retval, v1)\n\n#define sw_zend_call_method_with_2_params(zobj, obj_ce, fn_ptr_ptr, fn_name, retval, v1, v2)                           \\\n    zend_call_method_with_2_params(SW_Z8_OBJ_P(zobj), obj_ce, fn_ptr_ptr, fn_name, retval, v1, v2)\n\nstatic sw_inline int sw_zend_function_max_num_args(zend_function *function) {\n    // https://github.com/php/php-src/commit/2646f7bcb98dcdd322ea21701c8bb101104ea619\n    // zend_function.common.num_args don't include the variadic argument anymore.\n    return (function->common.fn_flags & ZEND_ACC_VARIADIC) ? UINT32_MAX : function->common.num_args;\n}\n\n// TODO: remove it after remove async modules\nstatic sw_inline zend_bool sw_zend_is_callable(zval *callable, int check_flags, char **callable_name) {\n    zend_string *name;\n    zend_bool ret = zend_is_callable(callable, check_flags, &name);\n    *callable_name = estrndup(ZSTR_VAL(name), ZSTR_LEN(name));\n    zend_string_release(name);\n    return ret;\n}\n\nstatic sw_inline zend_bool sw_zend_is_callable_at_frame(zval *zcallable,\n                                                        zval *zobject,\n                                                        zend_execute_data *frame,\n                                                        uint check_flags,\n                                                        char **callable_name,\n                                                        size_t *callable_name_len,\n                                                        zend_fcall_info_cache *fci_cache,\n                                                        char **error) {\n    zend_bool ret = zend_is_callable_at_frame(\n        zcallable, zobject ? Z_OBJ_P(zobject) : nullptr, frame, check_flags, fci_cache, error);\n    zend_string *name = zend_get_callable_name_ex(zcallable, zobject ? Z_OBJ_P(zobject) : nullptr);\n    if (callable_name) {\n        *callable_name = estrndup(ZSTR_VAL(name), ZSTR_LEN(name));\n    }\n    if (callable_name_len) {\n        *callable_name_len = ZSTR_LEN(name);\n    }\n    zend_string_release(name);\n    return ret;\n}\n\nstatic sw_inline zend_bool sw_zend_is_callable_ex(zval *zcallable,\n                                                  zval *zobject,\n                                                  uint check_flags,\n                                                  char **callable_name,\n                                                  size_t *callable_name_len,\n                                                  zend_fcall_info_cache *fci_cache,\n                                                  char **error) {\n    return sw_zend_is_callable_at_frame(\n        zcallable, zobject, nullptr, check_flags, callable_name, callable_name_len, fci_cache, error);\n}\n\n/* this API can work well when retval is NULL */\nstatic sw_inline int sw_zend_call_function_ex(\n    zval *function_name, zend_fcall_info_cache *fci_cache, uint32_t param_count, zval *params, zval *retval) {\n    zend_fcall_info fci;\n    zval _retval;\n\n    fci.size = sizeof(fci);\n    fci.object = nullptr;\n    if (!fci_cache || !fci_cache->function_handler) {\n        if (!function_name) {\n            php_swoole_fatal_error(E_WARNING, \"Bad function\");\n            return FAILURE;\n        }\n        ZVAL_COPY_VALUE(&fci.function_name, function_name);\n    } else {\n        ZVAL_UNDEF(&fci.function_name);\n    }\n    fci.retval = retval ? retval : &_retval;\n    fci.param_count = param_count;\n    fci.params = params;\n    fci.named_params = nullptr;\n\n    int ret = zend_call_function(&fci, fci_cache);\n    if (!retval) {\n        zval_ptr_dtor(&_retval);\n    }\n    return ret;\n}\n\n/* we must check for exception immediately if we don't have chances to go back to ZendVM (e.g event loop) */\nstatic sw_inline int sw_zend_call_function_ex2(\n    zval *function_name, zend_fcall_info_cache *fci_cache, uint32_t param_count, zval *params, zval *retval) {\n    int ret = sw_zend_call_function_ex(function_name, fci_cache, param_count, params, retval);\n    if (UNEXPECTED(EG(exception))) {\n        zend_exception_error(EG(exception), E_ERROR);\n    }\n    return ret;\n}\n\nstatic sw_inline int sw_zend_call_function_anyway(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) {\n    zval retval;\n    if (!fci->retval) {\n        fci->retval = &retval;\n    }\n    int ret = zend_call_function(fci, fci_cache);\n    if (fci->retval == &retval) {\n        zval_ptr_dtor(&retval);\n    }\n    return ret;\n}\n\nstatic sw_inline void sw_zend_fci_params_persist(zend_fcall_info *fci) {\n    if (fci->param_count > 0) {\n        zval *params = (zval *) ecalloc(fci->param_count, sizeof(zval));\n        for (uint32_t i = 0; i < fci->param_count; i++) {\n            ZVAL_COPY(&params[i], &fci->params[i]);\n        }\n        fci->params = params;\n    }\n}\n\nstatic sw_inline void sw_zend_fci_params_discard(zend_fcall_info *fci) {\n    if (fci->param_count > 0) {\n        for (uint32_t i = 0; i < fci->param_count; i++) {\n            zval_ptr_dtor(&fci->params[i]);\n        }\n        efree(fci->params);\n    }\n}\n\nstatic sw_inline void sw_zend_fci_cache_persist(zend_fcall_info_cache *fci_cache) {\n    if (fci_cache->object) {\n        GC_ADDREF(fci_cache->object);\n    }\n    if (fci_cache->function_handler->op_array.fn_flags & ZEND_ACC_CLOSURE) {\n        GC_ADDREF(ZEND_CLOSURE_OBJECT(fci_cache->function_handler));\n    }\n}\n\nstatic sw_inline void sw_zend_fci_cache_discard(zend_fcall_info_cache *fci_cache) {\n    if (fci_cache->object) {\n        OBJ_RELEASE(fci_cache->object);\n    }\n    if (fci_cache->function_handler->op_array.fn_flags & ZEND_ACC_CLOSURE) {\n        OBJ_RELEASE(ZEND_CLOSURE_OBJECT(fci_cache->function_handler));\n    }\n}\n\n#define sw_php_spl_object_hash(o) php_spl_object_hash(Z_OBJ_P(o))\n\n//----------------------------------Misc API------------------------------------\n\nstatic sw_inline int php_swoole_check_reactor() {\n    if (SWOOLE_G(req_status) == PHP_SWOOLE_RSHUTDOWN_BEGIN) {\n        return -1;\n    }\n    if (sw_unlikely(!sw_reactor())) {\n        return php_swoole_reactor_init() == SW_OK ? 1 : -1;\n    } else {\n        return 0;\n    }\n}\n\nstatic sw_inline char *php_swoole_url_encode(const char *value, size_t value_len, size_t *exten) {\n    zend_string *str = php_url_encode(value, value_len);\n    *exten = ZSTR_LEN(str);\n    char *return_str = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));\n    zend_string_release(str);\n    return return_str;\n}\n\nstatic sw_inline char *php_swoole_http_build_query(zval *zdata, size_t *length, smart_str *formstr) {\n    if (HASH_OF(zdata)) {\n#if PHP_VERSION_ID < 80300\n        php_url_encode_hash_ex(\n            HASH_OF(zdata), formstr, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, nullptr, (int) PHP_QUERY_RFC1738);\n#else\n        php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, NULL, NULL, NULL, (int) PHP_QUERY_RFC1738);\n#endif\n    } else {\n        if (formstr->s) {\n            smart_str_free(formstr);\n        }\n        return nullptr;\n    }\n    if (!formstr->s) {\n        return nullptr;\n    }\n    smart_str_0(formstr);\n    *length = formstr->s->len;\n    return formstr->s->val;\n}\n\nstatic inline const char *php_swoole_get_last_error_message() {\n    return PG(last_error_message) ? PG(last_error_message)->val : nullptr;\n}\n\nstatic inline const char *php_swoole_get_last_error_file() {\n    return PG(last_error_file) ? PG(last_error_file)->val : \"-\";\n}\n\nEND_EXTERN_C()\n\n#endif /* PHP_SWOOLE_PRIVATE_H */\n"
  },
  {
    "path": "ext-src/php_swoole_process.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Twosee  <twose@qq.com>                                       |\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"php_swoole_cxx.h\"\n#include \"swoole_process_pool.h\"\n\nenum PipeType {\n    PIPE_TYPE_NONE = 0,\n    PIPE_TYPE_STREAM = 1,\n    PIPE_TYPE_DGRAM = 2,\n};\n\nvoid php_swoole_process_clean();\nint php_swoole_process_start(swoole::Worker *process, zval *zobject);\nswoole::Worker *php_swoole_process_get_worker(const zval *zobject);\nvoid php_swoole_process_set_worker(const zval *zobject, swoole::Worker *worker, bool enable_coroutine, int pipe_type);\n\nswoole::ProcessPool *sw_process_pool();\n"
  },
  {
    "path": "ext-src/php_swoole_server.h",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2015 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#pragma once\n\n#include \"php_swoole_cxx.h\"\n#include \"swoole_server.h\"\n\n#include <unordered_map>\n#include <list>\n#include <vector>\n\n//--------------------------------------------------------\nenum php_swoole_server_callback_type {\n    SW_SERVER_CB_onStart,           // master\n    SW_SERVER_CB_onBeforeShutdown,  // master\n    SW_SERVER_CB_onShutdown,        // master\n    SW_SERVER_CB_onWorkerStart,     // worker(event & task)\n    SW_SERVER_CB_onWorkerStop,      // worker(event & task)\n    SW_SERVER_CB_onBeforeReload,    // manager\n    SW_SERVER_CB_onAfterReload,     // manager\n    SW_SERVER_CB_onTask,            // worker(task)\n    SW_SERVER_CB_onFinish,          // worker(event & task)\n    SW_SERVER_CB_onWorkerExit,      // worker(event)\n    SW_SERVER_CB_onWorkerError,     // manager\n    SW_SERVER_CB_onManagerStart,    // manager\n    SW_SERVER_CB_onManagerStop,     // manager\n    SW_SERVER_CB_onPipeMessage,     // worker(event & task)\n};\n//--------------------------------------------------------\nenum php_swoole_server_port_callback_type {\n    SW_SERVER_CB_onConnect,                  // stream, worker(event)\n    SW_SERVER_CB_onReceive,                  // stream, worker(event)\n    SW_SERVER_CB_onClose,                    // stream, worker(event)\n    SW_SERVER_CB_onPacket,                   // dgram, worker(event)\n    SW_SERVER_CB_onRequest,                  // http, worker(event)\n    SW_SERVER_CB_onHandshake,                // websocket, worker(event)\n    SW_SERVER_CB_onBeforeHandshakeResponse,  // websocket, worker(event)\n    SW_SERVER_CB_onOpen,                     // websocket, worker(event)\n    SW_SERVER_CB_onMessage,                  // websocket, worker(event)\n    SW_SERVER_CB_onDisconnect,               // websocket (non websocket connection), worker(event)\n    SW_SERVER_CB_onBufferFull,               // worker(event)\n    SW_SERVER_CB_onBufferEmpty,              // worker(event)\n};\n\n#define PHP_SWOOLE_SERVER_CALLBACK_NUM (SW_SERVER_CB_onPipeMessage + 1)\n#define PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM (SW_SERVER_CB_onBufferEmpty + 1)\n\nnamespace swoole {\nstruct ServerPortProperty;\nstruct TaskCo;\n};  // namespace swoole\n\nzval *php_swoole_server_zval_ptr(swoole::Server *serv);\nswoole::ServerPortProperty *php_swoole_server_get_port_property(swoole::ListenPort *port);\nvoid php_swoole_server_set_port_property(swoole::ListenPort *port, swoole::ServerPortProperty *property);\n\nnamespace swoole {\n\nstruct ServerPortProperty {\n    zend::Callable *callbacks[PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM];\n    Server *serv;\n    ListenPort *port;\n    zval *zsetting;\n};\n\nstruct ServerProperty {\n    std::vector<zval *> ports;\n    std::vector<zval *> user_processes;\n    zend::Callable *callbacks[PHP_SWOOLE_SERVER_CALLBACK_NUM];\n    std::unordered_map<TaskId, zend::Callable *> task_callbacks;\n    std::unordered_map<TaskId, TaskCo *> task_coroutine_map;\n    std::unordered_map<SessionId, std::list<Coroutine *> *> send_coroutine_map;\n    std::vector<zend::Callable *> command_callbacks;\n};\n\nstruct ServerObject {\n    Server *serv;\n    ServerProperty *property;\n    zval init_arguments;\n    zend_object std;\n\n    zend_class_entry *get_ce() const {\n        return Z_OBJCE_P(php_swoole_server_zval_ptr(serv));\n    }\n\n    bool isset_callback(ListenPort *port, int event_type) const {\n        return (php_swoole_server_get_port_property(port)->callbacks[event_type] ||\n                php_swoole_server_get_port_property(serv->get_primary_port())->callbacks[event_type]);\n    }\n\n    bool isset_callback(int event_type) const {\n        return property->callbacks[event_type] != nullptr;\n    }\n\n    zend::Callable *get_callback(int event_type) const {\n        return property->callbacks[event_type];\n    }\n\n    zend_bool is_websocket_server() const {\n        return instanceof_function(get_ce(), swoole_websocket_server_ce);\n    }\n\n    zend_bool is_http_server() const {\n        return instanceof_function(get_ce(), swoole_http_server_ce);\n    }\n\n    zend_bool is_redis_server() const {\n        return instanceof_function(get_ce(), swoole_redis_server_ce);\n    }\n\n    void register_callback() const;\n    void on_before_start();\n    void copy_setting(zval *zsetting) const;\n};\n\nstruct TaskCo {\n    Coroutine *co;\n    TaskId *list;\n    uint32_t count;\n    zval *result;\n};\nvoid register_admin_server_commands(Server *serv);\n}  // namespace swoole\n\nvoid php_swoole_server_register_callbacks(swServer *serv);\nzend::Callable *php_swoole_server_get_callback(swServer *serv, int server_fd, int event_type);\nint php_swoole_create_dir(const char *path, size_t length);\nvoid php_swoole_server_before_start(swServer *serv, zval *zobject);\nbool php_swoole_server_isset_callback(swServer *serv, swoole::ListenPort *port, int event_type);\nbool php_swoole_server_send_yield(swServer *serv, swoole::SessionId session_id, zend_string *sdata);\nvoid php_swoole_get_recv_data(swServer *serv, zval *zdata, swoole::RecvData *req);\nvoid php_swoole_server_onConnect(swServer *, swoole::DataHead *);\nint php_swoole_server_onReceive(swServer *, swoole::RecvData *);\nint php_swoole_http_server_onReceive(swServer *, swoole::RecvData *);\nvoid php_swoole_http_server_onClose(swServer *serv, swoole::DataHead *info);\nvoid php_swoole_http2_server_onClose(swServer *serv, swoole::SessionId session_id);\nint php_swoole_redis_server_onReceive(swServer *serv, swoole::RecvData *req);\nint php_swoole_server_onPacket(swServer *, swoole::RecvData *);\nvoid php_swoole_server_onClose(swServer *, swoole::DataHead *);\nvoid php_swoole_server_onBufferFull(swServer *, swoole::DataHead *);\nvoid php_swoole_server_onBufferEmpty(swServer *, swoole::DataHead *);\n\nswServer *php_swoole_server_get_and_check_server(zval *zobject);\nvoid php_swoole_server_port_deref(zend_object *object);\nvoid php_swoole_server_set_websocket_option(swoole::ListenPort *port, zend_array *vht);\nswoole::ServerObject *php_swoole_server_get_zend_object(swoole::Server *serv);\n"
  },
  {
    "path": "ext-src/php_swoole_sqlite.h",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2018 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: NathanFreeman  <mariasocute@163.com>                         |\n +----------------------------------------------------------------------+\n*/\n#ifndef SWOOLE_SRC_PHP_SWOOLE_SQLITE_H\n#define SWOOLE_SRC_PHP_SWOOLE_SQLITE_H\n#include \"php_swoole.h\"\n\n#ifdef SW_USE_SQLITE\n\nBEGIN_EXTERN_C()\n\n#include \"ext/pdo/php_pdo_driver.h\"\n\n#include \"thirdparty/pdo_sqlite/php_pdo_sqlite_int.h\"\n\nextern const pdo_driver_t swoole_pdo_sqlite_driver;\nvoid swoole_sqlite_set_blocking(bool blocking);\n\nint swoole_sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs);\nint swoole_sqlite3_prepare_v2(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail);\nint swoole_sqlite3_exec(\n    sqlite3 *, const char *sql, int (*callback)(void *, int, char **, char **), void *, char **errmsg);\nint swoole_sqlite3_close(sqlite3 *db);\nint swoole_sqlite3_close_v2(sqlite3 *db);\nint swoole_sqlite3_step(sqlite3_stmt *stmt);\n\n#ifdef SW_USE_SQLITE_HOOK\n#define sqlite3_open_v2 swoole_sqlite3_open_v2\n#define sqlite3_prepare_v2 swoole_sqlite3_prepare_v2\n#define sqlite3_exec swoole_sqlite3_exec\n#define sqlite3_close swoole_sqlite3_close\n#define sqlite3_close_v2 swoole_sqlite3_close_v2\n#define sqlite3_step swoole_sqlite3_step\n#endif\n\nEND_EXTERN_C()\n#endif\n#endif\n"
  },
  {
    "path": "ext-src/php_swoole_ssh2.h",
    "content": "#pragma once\n\n#include \"php_swoole_cxx.h\"\n#include \"php_swoole_ssh2_def.h\"\n\n#include <libssh2.h>\n#include <libssh2_sftp.h>\n#include <libssh2_publickey.h>\n\ntypedef struct _php_ssh2_session_data {\n    /* Userspace callback functions */\n    zval *ignore_cb;\n    zval *debug_cb;\n    zval *macerror_cb;\n    zval *disconnect_cb;\n\n    SocketImpl *socket;\n} php_ssh2_session_data;\n\nstatic inline swoole::EventType ssh2_get_event_type(LIBSSH2_SESSION *session) {\n    int dir = libssh2_session_block_directions(session);\n    if (dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) {\n        return SW_EVENT_WRITE;\n    } else {\n        return SW_EVENT_READ;\n    }\n}\n\nstatic inline SocketImpl *ssh2_get_socket(LIBSSH2_SESSION *session) {\n    auto session_data = (php_ssh2_session_data **) libssh2_session_abstract(session);\n    return (*session_data)->socket;\n}\n\nstatic inline void ssh2_set_socket_timeout(LIBSSH2_SESSION *session, int timeout_ms) {\n    auto sock = ssh2_get_socket(session);\n    sock->set_timeout(timeout_ms / 1000, SW_TIMEOUT_ALL);\n}\n\nclass ResourceGuard {\n    zval zres_;\n\n  public:\n    ResourceGuard(zval *zres) {\n        zval_addref_p(zres);\n        zres_ = *zres;\n    }\n    ~ResourceGuard() {\n        zval_ptr_dtor(&zres_);\n    }\n};\n\nstatic inline int ssh2_async_call(LIBSSH2_SESSION *session, const std::function<int(void)> &fn) {\n    auto event = ssh2_get_event_type(session);\n    auto socket = ssh2_get_socket(session);\n\n    socket->check_bound_co(SW_EVENT_READ);\n    socket->check_bound_co(SW_EVENT_WRITE);\n\n    int rc = 0;\n    while (1) {\n        rc = fn();\n        if (rc == LIBSSH2_ERROR_EAGAIN) {\n            if (!socket->poll(event)) {\n                return LIBSSH2_ERROR_SOCKET_NONE;\n            }\n            continue;\n        }\n        break;\n    }\n    return rc;\n}\n\ntemplate <typename T>\nstatic inline T *ssh2_async_call_ex(LIBSSH2_SESSION *session, const std::function<T *(void)> &fn) {\n    auto event = ssh2_get_event_type(session);\n    auto socket = ssh2_get_socket(session);\n\n    socket->check_bound_co(SW_EVENT_READ);\n    socket->check_bound_co(SW_EVENT_WRITE);\n\n    T *handle;\n    while (1) {\n        handle = fn();\n        if (handle) {\n            return handle;\n        }\n        if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN && socket->poll(event)) {\n            continue;\n        }\n        break;\n    }\n    return nullptr;\n}\n"
  },
  {
    "path": "ext-src/php_swoole_ssh2_def.h",
    "content": "#include \"php.h\"\n\nBEGIN_EXTERN_C()\nZEND_FUNCTION(ssh2_connect);\nZEND_FUNCTION(ssh2_disconnect);\nZEND_FUNCTION(ssh2_methods_negotiated);\nZEND_FUNCTION(ssh2_fingerprint);\nZEND_FUNCTION(ssh2_auth_none);\nZEND_FUNCTION(ssh2_auth_password);\nZEND_FUNCTION(ssh2_auth_pubkey_file);\nZEND_FUNCTION(ssh2_auth_pubkey);\nZEND_FUNCTION(ssh2_auth_hostbased_file);\nZEND_FUNCTION(ssh2_forward_listen);\nZEND_FUNCTION(ssh2_forward_accept);\nZEND_FUNCTION(ssh2_shell);\nZEND_FUNCTION(ssh2_shell_resize);\nZEND_FUNCTION(ssh2_exec);\nZEND_FUNCTION(ssh2_tunnel);\nZEND_FUNCTION(ssh2_scp_recv);\nZEND_FUNCTION(ssh2_scp_send);\nZEND_FUNCTION(ssh2_fetch_stream);\nZEND_FUNCTION(ssh2_send_eof);\nZEND_FUNCTION(ssh2_sftp);\nZEND_FUNCTION(ssh2_sftp_rename);\nZEND_FUNCTION(ssh2_sftp_unlink);\nZEND_FUNCTION(ssh2_sftp_mkdir);\nZEND_FUNCTION(ssh2_sftp_rmdir);\nZEND_FUNCTION(ssh2_sftp_chmod);\nZEND_FUNCTION(ssh2_sftp_stat);\nZEND_FUNCTION(ssh2_sftp_lstat);\nZEND_FUNCTION(ssh2_sftp_symlink);\nZEND_FUNCTION(ssh2_sftp_readlink);\nZEND_FUNCTION(ssh2_sftp_realpath);\nZEND_FUNCTION(ssh2_publickey_init);\nZEND_FUNCTION(ssh2_publickey_add);\nZEND_FUNCTION(ssh2_publickey_remove);\nZEND_FUNCTION(ssh2_publickey_list);\nZEND_FUNCTION(ssh2_auth_agent);\nEND_EXTERN_C()\n\nint php_swoole_ssh2_mshutdown();\nint php_swoole_ssh2_minit(int module_number);\nvoid php_swoole_ssh2_minfo();\n"
  },
  {
    "path": "ext-src/php_swoole_ssh2_hook.h",
    "content": "#define SSH2_ASYNC_CALL(session, libssh2_func, ...)                                                                    \\\n    ssh2_async_call(session, [&](void) { return libssh2_func(__VA_ARGS__); })\n\n#define SSH2_ASYNC_CALL_EX(T, session, libssh2_func, ...)                                                              \\\n    ssh2_async_call_ex<T>(session, [&](void) -> T * { return libssh2_func(__VA_ARGS__); })\n\n#define libssh2_session_handshake(session, sockfd) SSH2_ASYNC_CALL(session, libssh2_session_handshake, session, sockfd)\n\n#undef libssh2_session_disconnect\n#define libssh2_session_disconnect(session, description)                                                               \\\n    SSH2_ASYNC_CALL(session, libssh2_session_disconnect_ex, (session), SSH_DISCONNECT_BY_APPLICATION, (description), \"\")\n\n#undef libssh2_channel_open_session\n#define libssh2_channel_open_session(session)                                                                          \\\n    SSH2_ASYNC_CALL_EX(LIBSSH2_CHANNEL,                                                                                \\\n                       session,                                                                                        \\\n                       libssh2_channel_open_ex,                                                                        \\\n                       (session),                                                                                      \\\n                       \"session\",                                                                                      \\\n                       sizeof(\"session\") - 1,                                                                          \\\n                       LIBSSH2_CHANNEL_WINDOW_DEFAULT,                                                                 \\\n                       LIBSSH2_CHANNEL_PACKET_DEFAULT,                                                                 \\\n                       NULL,                                                                                           \\\n                       0)\n\n#define libssh2_channel_setenv_ex(channel, name, name_len, value, value_len)                                           \\\n    SSH2_ASYNC_CALL(session, libssh2_channel_setenv_ex, channel, name, name_len, value, value_len)\n\n#define libssh2_channel_request_pty_ex(channel, term, term_len, modes, modes_len, width, height, width_px, height_px)  \\\n    SSH2_ASYNC_CALL(session,                                                                                           \\\n                    libssh2_channel_request_pty_ex,                                                                    \\\n                    channel,                                                                                           \\\n                    term,                                                                                              \\\n                    term_len,                                                                                          \\\n                    modes,                                                                                             \\\n                    modes_len,                                                                                         \\\n                    width,                                                                                             \\\n                    height,                                                                                            \\\n                    width_px,                                                                                          \\\n                    height_px)\n\n#undef libssh2_channel_shell\n#define libssh2_channel_shell(channel)                                                                                 \\\n    SSH2_ASYNC_CALL(session, libssh2_channel_process_startup, channel, \"shell\", sizeof(\"shell\") - 1, NULL, 0)\n\n#undef libssh2_channel_exec\n#define libssh2_channel_exec(channel, command)                                                                         \\\n    SSH2_ASYNC_CALL(session,                                                                                           \\\n                    libssh2_channel_process_startup,                                                                   \\\n                    channel,                                                                                           \\\n                    \"exec\",                                                                                            \\\n                    sizeof(\"exec\") - 1,                                                                                \\\n                    (command),                                                                                         \\\n                    (unsigned int) strlen(command))\n\n#define libssh2_channel_flush_ex(channel, streamid)                                                                    \\\n    SSH2_ASYNC_CALL(session, libssh2_channel_flush_ex, (channel), (streamid))\n\n#define libssh2_channel_read_ex(channel, streamid, buf, len)                                                           \\\n    SSH2_ASYNC_CALL(session, libssh2_channel_read_ex, channel, streamid, buf, len)\n\n#undef libssh2_channel_write_ex\n#define libssh2_channel_write_ex(channel, streamid, buf, len)                                                          \\\n    SSH2_ASYNC_CALL(session, libssh2_channel_write_ex, channel, streamid, buf, len)\n\n#undef libssh2_channel_read\n#define libssh2_channel_read(channel, buf, buflen) libssh2_channel_read_ex((channel), 0, (buf), (buflen))\n\n#undef libssh2_channel_write\n#define libssh2_channel_write(channel, buf, buflen) libssh2_channel_write_ex((channel), 0, (buf), (buflen))\n\n#undef libssh2_channel_eof\n#define libssh2_channel_eof(channel) SSH2_ASYNC_CALL(session, libssh2_channel_eof, channel)\n\n#undef libssh2_channel_close\n#define libssh2_channel_close(channel) SSH2_ASYNC_CALL(session, libssh2_channel_close, channel)\n\n#undef libssh2_channel_send_eof\n#define libssh2_channel_send_eof(channel) SSH2_ASYNC_CALL(session, libssh2_channel_send_eof, channel)\n\n#undef libssh2_channel_get_exit_status\n#define libssh2_channel_get_exit_status(channel) SSH2_ASYNC_CALL(session, libssh2_channel_get_exit_status, channel)\n\n#undef libssh2_channel_request_pty_size_ex\n#define libssh2_channel_request_pty_size_ex(channel, width, height, width_px, height_px)                               \\\n    SSH2_ASYNC_CALL(session, libssh2_channel_request_pty_size_ex, channel, width, height, width_px, height_px)\n\n#undef libssh2_channel_forward_listen_ex\n#define libssh2_channel_forward_listen_ex(session, host, port, addr, num_connections)                                  \\\n    SSH2_ASYNC_CALL_EX(                                                                                                \\\n        LIBSSH2_LISTENER, session, libssh2_channel_forward_listen_ex, session, host, port, addr, num_connections)\n\n#undef libssh2_channel_forward_accept\n#define libssh2_channel_forward_accept(listener)                                                                       \\\n    SSH2_ASYNC_CALL_EX(LIBSSH2_CHANNEL, session, libssh2_channel_forward_accept, listener)\n\n#undef libssh2_channel_forward_cancel\n#define libssh2_channel_forward_cancel(listener) SSH2_ASYNC_CALL(session, libssh2_channel_forward_cancel, listener)\n\n#undef libssh2_channel_direct_tcpip\n#define libssh2_channel_direct_tcpip(session, host, port)                                                              \\\n    SSH2_ASYNC_CALL_EX(                                                                                                \\\n        LIBSSH2_CHANNEL, session, libssh2_channel_direct_tcpip_ex, (session), (host), (port), \"127.0.0.1\", 22)\n\n#undef libssh2_sftp_fstat\n#define libssh2_sftp_fstat(handle, attrs) SSH2_ASYNC_CALL(session, libssh2_sftp_fstat_ex, handle, attrs, 0)\n\n#define libssh2_sftp_stat_ex(sftp, path, path_len, stat_type, attrs)                                                   \\\n    SSH2_ASYNC_CALL(session, libssh2_sftp_stat_ex, sftp, path, path_len, stat_type, attrs)\n\n#define libssh2_sftp_symlink_ex(sftp, path, path_len, target, target_len, link_type)                                   \\\n    SSH2_ASYNC_CALL(session, libssh2_sftp_symlink_ex, sftp, path, path_len, target, target_len, link_type)\n\n#undef libssh2_sftp_open\n#define libssh2_sftp_open(sftp, filename, flags, mode)                                                                 \\\n    SSH2_ASYNC_CALL_EX(LIBSSH2_SFTP_HANDLE,                                                                            \\\n                       session,                                                                                        \\\n                       libssh2_sftp_open_ex,                                                                           \\\n                       (sftp),                                                                                         \\\n                       (filename),                                                                                     \\\n                       strlen(filename),                                                                               \\\n                       (flags),                                                                                        \\\n                       (mode),                                                                                         \\\n                       LIBSSH2_SFTP_OPENFILE)\n\n#undef libssh2_sftp_opendir\n#define libssh2_sftp_opendir(sftp, path)                                                                               \\\n    SSH2_ASYNC_CALL_EX(                                                                                                \\\n        LIBSSH2_SFTP_HANDLE, session, libssh2_sftp_open_ex, (sftp), (path), strlen(path), 0, 0, LIBSSH2_SFTP_OPENDIR)\n\n#undef libssh2_sftp_readdir\n#define libssh2_sftp_readdir(handle, buffer, buffer_maxlen, attrs)                                                     \\\n    SSH2_ASYNC_CALL(session, libssh2_sftp_readdir_ex, (handle), (buffer), (buffer_maxlen), NULL, 0, (attrs))\n\n#undef libssh2_sftp_tell\n#define libssh2_sftp_tell(handle) SSH2_ASYNC_CALL(session, libssh2_sftp_tell, handle)\n\n#undef libssh2_sftp_read\n#define libssh2_sftp_read(handle, buffer, count)                                                                       \\\n    SSH2_ASYNC_CALL(session, libssh2_sftp_read, (handle), (buffer), (count))\n\n#undef libssh2_sftp_write\n#define libssh2_sftp_write(handle, buffer, count)                                                                      \\\n    SSH2_ASYNC_CALL(session, libssh2_sftp_write, (handle), (buffer), (count))\n\n#define libssh2_sftp_init(session) SSH2_ASYNC_CALL_EX(LIBSSH2_SFTP, session, libssh2_sftp_init, session)\n\n#undef libssh2_scp_recv\n#define libssh2_scp_recv(session, path, stat)                                                                          \\\n    SSH2_ASYNC_CALL_EX(LIBSSH2_CHANNEL, session, libssh2_scp_recv, session, path, stat)\n\n#undef libssh2_scp_send_ex\n#define libssh2_scp_send_ex(session, path, mode, size, atime, mtime)                                                   \\\n    SSH2_ASYNC_CALL_EX(LIBSSH2_CHANNEL, session, libssh2_scp_send_ex, session, path, mode, size, atime, mtime)\n\n#undef libssh2_sftp_close\n#define libssh2_sftp_close(handle) SSH2_ASYNC_CALL(session, libssh2_sftp_close_handle, handle)\n\n#define libssh2_sftp_unlink_ex(sftp, filename, filename_len)                                                           \\\n    SSH2_ASYNC_CALL(session, libssh2_sftp_unlink_ex, (sftp), (filename), filename_len)\n\n#undef libssh2_sftp_unlink\n#define libssh2_sftp_unlink(sftp, filename) libssh2_sftp_unlink_ex((sftp), (filename), strlen(filename))\n\n#define libssh2_sftp_rename_ex(sftp, source_filename, srouce_filename_len, dest_filename, dest_filename_len, flags)    \\\n    SSH2_ASYNC_CALL(session,                                                                                           \\\n                    libssh2_sftp_rename_ex,                                                                            \\\n                    sftp,                                                                                              \\\n                    source_filename,                                                                                   \\\n                    srouce_filename_len,                                                                               \\\n                    dest_filename,                                                                                     \\\n                    dest_filename_len,                                                                                 \\\n                    flags)\n\n#undef libssh2_sftp_rename\n#define libssh2_sftp_rename(sftp, sourcefile, destfile)                                                                \\\n    libssh2_sftp_rename_ex((sftp),                                                                                     \\\n                           (sourcefile),                                                                               \\\n                           strlen(sourcefile),                                                                         \\\n                           (destfile),                                                                                 \\\n                           strlen(destfile),                                                                           \\\n                           LIBSSH2_SFTP_RENAME_OVERWRITE | LIBSSH2_SFTP_RENAME_ATOMIC | LIBSSH2_SFTP_RENAME_NATIVE)\n\n#define libssh2_sftp_mkdir_ex(sftp, path, path_len, mode)                                                              \\\n    SSH2_ASYNC_CALL(session, libssh2_sftp_mkdir_ex, (sftp), (path), path_len, (mode))\n\n#undef libssh2_sftp_mkdir\n#define libssh2_sftp_mkdir(sftp, path, mode) libssh2_sftp_mkdir_ex((sftp), (path), strlen(path), (mode))\n\n#define libssh2_sftp_rmdir_ex(sftp, path, path_len)                                                                    \\\n    SSH2_ASYNC_CALL(session, libssh2_sftp_rmdir_ex, (sftp), (path), path_len)\n\n#undef libssh2_sftp_rmdir\n#define libssh2_sftp_rmdir(sftp, path) libssh2_sftp_rmdir_ex((sftp), (path), strlen(path))\n\n/* Agent related functions */\n#undef libssh2_agent_connect\n#define libssh2_agent_connect(agent) SSH2_ASYNC_CALL(session, libssh2_agent_connect, agent)\n\n#undef libssh2_agent_list_identities\n#define libssh2_agent_list_identities(agent) SSH2_ASYNC_CALL(session, libssh2_agent_list_identities, agent)\n\n#undef libssh2_agent_get_identity\n#define libssh2_agent_get_identity(agent, identity, prev_identity)                                                     \\\n    SSH2_ASYNC_CALL(session, libssh2_agent_get_identity, agent, identity, prev_identity)\n\n#undef libssh2_agent_userauth\n#define libssh2_agent_userauth(agent, username, identity)                                                              \\\n    SSH2_ASYNC_CALL(session, libssh2_agent_userauth, agent, username, identity)\n\n#undef libssh2_agent_disconnect\n#define libssh2_agent_disconnect(agent) SSH2_ASYNC_CALL(session, libssh2_agent_disconnect, agent)\n\n/* libssh2_agent_free is just memory operation, no network IO, so don't async it */\n\n/* User authentication functions */\n#undef libssh2_userauth_list\n#define libssh2_userauth_list(session, username, username_len)                                                         \\\n    SSH2_ASYNC_CALL_EX(char, session, libssh2_userauth_list, session, username, username_len)\n\n#undef libssh2_userauth_password_ex\n#define libssh2_userauth_password_ex(session, username, username_len, password, password_len, change_cb)               \\\n    SSH2_ASYNC_CALL(                                                                                                   \\\n        session, libssh2_userauth_password_ex, session, username, username_len, password, password_len, change_cb)\n\n#undef libssh2_userauth_publickey_fromfile_ex\n#define libssh2_userauth_publickey_fromfile_ex(session, username, username_len, publickey, privatekey, passphrase)     \\\n    SSH2_ASYNC_CALL(session,                                                                                           \\\n                    libssh2_userauth_publickey_fromfile_ex,                                                            \\\n                    (session),                                                                                         \\\n                    (username),                                                                                        \\\n                    (username_len),                                                                                    \\\n                    (publickey),                                                                                       \\\n                    (privatekey),                                                                                      \\\n                    (passphrase))\n\n#undef libssh2_userauth_publickey_fromfile\n#define libssh2_userauth_publickey_fromfile(session, username, publickey, privatekey, passphrase)                      \\\n    libssh2_userauth_publickey_fromfile_ex(                                                                            \\\n        (session), (username), (unsigned int) strlen(username), (publickey), (privatekey), (passphrase))\n\n#undef libssh2_userauth_publickey_frommemory\n#define libssh2_userauth_publickey_frommemory(                                                                         \\\n    session, username, username_len, pubkeydata, pubkeydata_len, privkeydata, privkeydata_len, passphrase)             \\\n    SSH2_ASYNC_CALL(session,                                                                                           \\\n                    libssh2_userauth_publickey_frommemory,                                                             \\\n                    session,                                                                                           \\\n                    username,                                                                                          \\\n                    username_len,                                                                                      \\\n                    pubkeydata,                                                                                        \\\n                    pubkeydata_len,                                                                                    \\\n                    privkeydata,                                                                                       \\\n                    privkeydata_len,                                                                                   \\\n                    passphrase)\n\n#undef libssh2_userauth_hostbased_fromfile_ex\n#define libssh2_userauth_hostbased_fromfile_ex(session,                                                                \\\n                                               username,                                                               \\\n                                               username_len,                                                           \\\n                                               pubkeyfile,                                                             \\\n                                               privkeyfile,                                                            \\\n                                               passphrase,                                                             \\\n                                               hostname,                                                               \\\n                                               hostname_len,                                                           \\\n                                               local_username,                                                         \\\n                                               local_username_len)                                                     \\\n    SSH2_ASYNC_CALL(session,                                                                                           \\\n                    libssh2_userauth_hostbased_fromfile_ex,                                                            \\\n                    session,                                                                                           \\\n                    username,                                                                                          \\\n                    username_len,                                                                                      \\\n                    pubkeyfile,                                                                                        \\\n                    privkeyfile,                                                                                       \\\n                    passphrase,                                                                                        \\\n                    hostname,                                                                                          \\\n                    hostname_len,                                                                                      \\\n                    local_username,                                                                                    \\\n                    local_username_len)\n\n#undef libssh2_userauth_keyboard_interactive\n#define libssh2_userauth_keyboard_interactive(session, username, response_callback)                                    \\\n    SSH2_ASYNC_CALL(session,                                                                                           \\\n                    libssh2_userauth_keyboard_interactive_ex,                                                          \\\n                    (session),                                                                                         \\\n                    (username),                                                                                        \\\n                    (unsigned int) strlen(username),                                                                   \\\n                    (response_callback))\n\n#undef libssh2_userauth_authenticated\n#define libssh2_userauth_authenticated(session) SSH2_ASYNC_CALL(session, libssh2_userauth_authenticated, session)"
  },
  {
    "path": "ext-src/php_swoole_stdext.h",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2015 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n */\n\n#pragma once\n\n#include \"php_swoole_cxx.h\"\n\nSW_EXTERN_C_BEGIN\nPHP_FUNCTION(swoole_call_array_method);\nPHP_FUNCTION(swoole_call_string_method);\nPHP_FUNCTION(swoole_call_stream_method);\nPHP_FUNCTION(swoole_array_search);\nPHP_FUNCTION(swoole_array_contains);\nPHP_FUNCTION(swoole_array_join);\nPHP_FUNCTION(swoole_array_key_exists);\nPHP_FUNCTION(swoole_array_map);\nPHP_FUNCTION(swoole_array_is_typed);\nPHP_FUNCTION(swoole_array_is_empty);\nPHP_FUNCTION(swoole_str_split);\nPHP_FUNCTION(swoole_str_is_empty);\nPHP_FUNCTION(swoole_str_match);\nPHP_FUNCTION(swoole_str_match_all);\nPHP_FUNCTION(swoole_str_json_decode);\nPHP_FUNCTION(swoole_str_json_decode_to_object);\nPHP_FUNCTION(swoole_parse_str);\nPHP_FUNCTION(swoole_hash);\nPHP_FUNCTION(swoole_typed_array);\nPHP_FUNCTION(swoole_str_replace);\nPHP_FUNCTION(swoole_str_ireplace);\nPHP_FUNCTION(swoole_array_replace_str);\nPHP_FUNCTION(swoole_array_ireplace_str);\nSW_EXTERN_C_END\n"
  },
  {
    "path": "ext-src/php_swoole_thread.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Twosee  <twose@qq.com>                                       |\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"php_swoole_cxx.h\"\n\n#ifdef SW_THREAD\n#include \"swoole_thread.h\"\n\ntypedef uint32_t ThreadResourceId;\nclass ThreadResource;\nclass ZendArray;\n\nextern zend_class_entry *swoole_thread_ce;\nextern zend_class_entry *swoole_thread_error_ce;\nextern zend_class_entry *swoole_thread_arraylist_ce;\nextern zend_class_entry *swoole_thread_atomic_ce;\nextern zend_class_entry *swoole_thread_atomic_long_ce;\nextern zend_class_entry *swoole_thread_barrier_ce;\nextern zend_class_entry *swoole_thread_lock_ce;\nextern zend_class_entry *swoole_thread_map_ce;\nextern zend_class_entry *swoole_thread_queue_ce;\n\nvoid php_swoole_thread_start(std::shared_ptr<swoole::Thread> thread, zend_string *file, ZendArray *argv);\nvoid php_swoole_thread_bailout();\n\nThreadResource *php_swoole_thread_arraylist_cast(const zval *zobject);\nThreadResource *php_swoole_thread_map_cast(const zval *zobject);\nThreadResource *php_swoole_thread_queue_cast(const zval *zobject);\nThreadResource *php_swoole_thread_lock_cast(const zval *zobject);\nThreadResource *php_swoole_thread_atomic_cast(const zval *zobject);\nThreadResource *php_swoole_thread_atomic_long_cast(const zval *zobject);\nThreadResource *php_swoole_thread_barrier_cast(const zval *zobject);\n\nvoid php_swoole_thread_arraylist_create(zval *return_value, ThreadResource *resource);\nvoid php_swoole_thread_map_create(zval *return_value, ThreadResource *resource);\nvoid php_swoole_thread_queue_create(zval *return_value, ThreadResource *resource);\nvoid php_swoole_thread_lock_create(zval *return_value, ThreadResource *resource);\nvoid php_swoole_thread_atomic_create(zval *return_value, ThreadResource *resource);\nvoid php_swoole_thread_atomic_long_create(zval *return_value, ThreadResource *resource);\nvoid php_swoole_thread_barrier_create(zval *return_value, ThreadResource *resource);\n\nint php_swoole_thread_stream_cast(zval *zstream);\nvoid php_swoole_thread_stream_create(zval *return_value, zend_long sockfd);\n\nint php_swoole_thread_co_socket_cast(zval *zstream, swSocketType *type);\nvoid php_swoole_thread_co_socket_create(zval *return_value, zend_long sockfd, swSocketType type);\n\n#define EMSG_NO_RESOURCE \"resource not found\"\n#define ECODE_NO_RESOURCE (-2)\n\nenum {\n    IS_ARRAYLIST = 80,\n    IS_QUEUE = 81,\n    IS_LOCK = 82,\n    IS_MAP = 83,\n    IS_BARRIER = 84,\n    IS_ATOMIC = 85,\n    IS_ATOMIC_LONG = 86,\n    IS_PHP_SOCKET = 96,\n    IS_CO_SOCKET = 97,\n    IS_STREAM_SOCKET = 98,\n    IS_SERIALIZED_OBJECT = 99,\n};\n\nclass ThreadResource {\n    sw_atomic_t ref_count;\n\n  public:\n    ThreadResource() {\n        ref_count = 1;\n    }\n\n    void add_ref() {\n        sw_atomic_add_fetch(&ref_count, 1);\n    }\n\n    void del_ref() {\n        if (sw_atomic_sub_fetch(&ref_count, 1) == 0) {\n            delete this;\n        }\n    }\n\n  protected:\n    virtual ~ThreadResource() = default;\n};\n\nstruct ArrayItem {\n    uint32_t type = IS_UNDEF;\n    zend_string *key = nullptr;\n    union {\n        zend_string *str;\n        zend_long lval;\n        double dval;\n        struct {\n            int fd;\n            swSocketType type;\n        } socket;\n        zend_string *serialized_object;\n        ThreadResource *resource;\n    } value;\n\n    explicit ArrayItem(zval *zvalue) {\n        value = {};\n        store(zvalue);\n    }\n\n    void setKey(zend::String &_key) {\n        key = zend_string_init(_key.val(), _key.len(), true);\n    }\n\n    void setKey(const zend_string *_key) {\n        key = zend_string_init(ZSTR_VAL(_key), ZSTR_LEN(_key), true);\n    }\n\n    void store(zval *zvalue);\n    void fetch(zval *return_value) const;\n    void release();\n    bool equals(const zval *zvalue) const;\n\n    static int compare(Bucket *a, Bucket *b);\n\n    ~ArrayItem() {\n        if (value.str) {\n            release();\n        }\n        if (key) {\n            zend_string_release(key);\n        }\n    }\n};\n\nclass ZendArray : public ThreadResource {\n  protected:\n    swoole::RWLock lock_;\n    zend_array ht;\n\n    static void item_dtor(zval *pDest) {\n        auto item = static_cast<ArrayItem *>(Z_PTR_P(pDest));\n        delete item;\n    }\n\n  public:\n    ZendArray() : lock_(false) {\n        zend_hash_init(&ht, 0, NULL, item_dtor, 1);\n    }\n\n    ~ZendArray() override {\n        zend_hash_destroy(&ht);\n    }\n\n    void clean() {\n        lock_.lock();\n        zend_hash_clean(&ht);\n        lock_.unlock();\n    }\n\n    void append(zval *zvalue);\n\n    void add(const zend_string *skey, zval *zvalue) {\n        auto item = new ArrayItem(zvalue);\n        item->setKey(skey);\n        zend_hash_add_ptr(&ht, item->key, item);\n    }\n\n    void add(zend::String &skey, zval *zvalue) {\n        auto item = new ArrayItem(zvalue);\n        item->setKey(skey);\n        zend_hash_add_ptr(&ht, item->key, item);\n    }\n\n    void add(zend_long index, zval *zvalue) {\n        auto item = new ArrayItem(zvalue);\n        zend_hash_index_add_ptr(&ht, index, item);\n    }\n\n    bool index_exists(zend_long index) const {\n        return index < (zend_long) zend_hash_num_elements(&ht);\n    }\n\n    bool strkey_exists(zend::String &skey) const {\n        return zend_hash_find_ptr(&ht, skey.get()) != nullptr;\n    }\n\n    bool intkey_exists(zend_long index) const {\n        return zend_hash_index_find_ptr(&ht, index) != nullptr;\n    }\n\n    void strkey_offsetGet(zval *zkey, zval *return_value) {\n        zend::String skey(zkey);\n        lock_.lock_rd();\n        auto item = static_cast<ArrayItem *>(zend_hash_find_ptr(&ht, skey.get()));\n        if (item) {\n            item->fetch(return_value);\n        }\n        lock_.unlock();\n    }\n\n    void strkey_offsetExists(zval *zkey, zval *return_value) {\n        zend::String skey(zkey);\n        lock_.lock_rd();\n        RETVAL_BOOL(strkey_exists(skey));\n        lock_.unlock();\n    }\n\n    void strkey_offsetUnset(zval *zkey) {\n        zend::String skey(zkey);\n        lock_.lock();\n        zend_hash_del(&ht, skey.get());\n        lock_.unlock();\n    }\n\n    void strkey_offsetSet(zval *zkey, zval *zvalue) {\n        zend::String skey(zkey);\n        auto item = new ArrayItem(zvalue);\n        item->setKey(skey);\n        lock_.lock();\n        zend_hash_update_ptr(&ht, item->key, item);\n        lock_.unlock();\n    }\n\n    void strkey_incr(zval *zkey, zval *zvalue, zval *return_value);\n    void intkey_incr(zend_long index, zval *zvalue, zval *return_value);\n    void strkey_decr(zval *zkey, zval *zvalue, zval *return_value);\n    void intkey_decr(zend_long index, zval *zvalue, zval *return_value);\n    bool index_incr(zval *zkey, zval *zvalue, zval *return_value);\n    bool index_decr(zval *zkey, zval *zvalue, zval *return_value);\n\n    void strkey_add(zval *zkey, zval *zvalue, zval *return_value);\n    void intkey_add(zend_long index, zval *zvalue, zval *return_value);\n    void strkey_update(zval *zkey, zval *zvalue, zval *return_value);\n    void intkey_update(zend_long index, zval *zvalue, zval *return_value);\n\n    void count(zval *return_value) {\n        lock_.lock_rd();\n        RETVAL_LONG(zend_hash_num_elements(&ht));\n        lock_.unlock();\n    }\n\n    void keys(zval *return_value);\n    void values(zval *return_value);\n    void to_array(zval *return_value);\n    void find(const zval *search, zval *return_value);\n    void sort(bool renumber);\n\n    void intkey_offsetGet(zend_long index, zval *return_value) {\n        lock_.lock_rd();\n        auto item = static_cast<ArrayItem *>(zend_hash_index_find_ptr(&ht, index));\n        if (item) {\n            item->fetch(return_value);\n        }\n        lock_.unlock();\n    }\n\n    void intkey_offsetExists(zend_long index, zval *return_value) {\n        lock_.lock_rd();\n        RETVAL_BOOL(intkey_exists(index));\n        lock_.unlock();\n    }\n\n    void intkey_offsetUnset(zend_long index) {\n        lock_.lock();\n        zend_hash_index_del(&ht, index);\n        lock_.unlock();\n    }\n\n    void intkey_offsetSet(zend_long index, zval *zvalue) {\n        auto item = new ArrayItem(zvalue);\n        lock_.lock();\n        zend_hash_index_update_ptr(&ht, index, item);\n        lock_.unlock();\n    }\n\n    bool index_offsetGet(zend_long index, zval *return_value);\n    bool index_offsetSet(zend_long index, zval *zvalue);\n    void index_offsetUnset(zend_long index);\n    void index_offsetExists(zend_long index, zval *return_value);\n\n    static void incr_update(ArrayItem *item, zval *zvalue, zval *return_value);\n    static ArrayItem *incr_create(zval *zvalue, zval *return_value);\n    static ZendArray *from(zend_array *ht);\n};\n\n#define INIT_ARRAY_INCR_PARAMS                                                                                         \\\n    zval *zkey;                                                                                                        \\\n    zval zvalue_, *zvalue = NULL;                                                                                      \\\n                                                                                                                       \\\n    ZEND_PARSE_PARAMETERS_START(1, 2)                                                                                  \\\n    Z_PARAM_ZVAL(zkey)                                                                                                 \\\n    Z_PARAM_OPTIONAL                                                                                                   \\\n    Z_PARAM_ZVAL(zvalue)                                                                                               \\\n    ZEND_PARSE_PARAMETERS_END();                                                                                       \\\n                                                                                                                       \\\n    if (!zvalue) {                                                                                                     \\\n        zvalue = &zvalue_;                                                                                             \\\n        ZVAL_LONG(zvalue, 1);                                                                                          \\\n    }\n\n#endif\n"
  },
  {
    "path": "ext-src/php_swoole_websocket.h",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2015 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#pragma once\n\n#include \"php_swoole_cxx.h\"\n\n#include \"swoole_websocket.h\"\n\n#define SW_WEBSOCKET_DEFAULT_BUFFER 4096\n\nnamespace swoole {\nnamespace websocket {\nvoid apply_setting(WebSocketSettings &settings, zend_array *vht, bool in_server);\nvoid recv_frame(const WebSocketSettings &settings,\n                std::shared_ptr<String> &frame_buffer,\n                SocketImpl *sock,\n                zval *return_value,\n                double timeout);\nssize_t send_frame(const WebSocketSettings &settings,\n                   SocketImpl *sock,\n                   uchar opcode,\n                   uchar flags,\n                   const char *payload,\n                   size_t payload_length);\nvoid construct_frame(zval *zframe, zend_long opcode, zval *zpayload, uint8_t flags);\n\n#ifdef SW_HAVE_ZLIB\nbool message_compress(String *buffer, const char *data, size_t length, int level);\nbool message_uncompress(String *buffer, const char *in, size_t in_len);\n#endif\n\nstruct FrameObject {\n    uint8_t opcode;\n    uint8_t flags;\n    uint16_t code;\n    zval *data;\n\n    explicit FrameObject(zval *data, zend_long _opcode = 0, zend_long _flags = 0, zend_long _code = 0);\n    size_t get_data_size() const {\n        return (data && ZVAL_IS_STRING(data)) ? Z_STRLEN_P(data) : 0;\n    }\n    bool pack(String *buffer);\n    static bool uncompress(zval *zpayload, const char *data, size_t length);\n};\n}  // namespace websocket\n}  // namespace swoole\n"
  },
  {
    "path": "ext-src/stubs/php_swoole.stub.php",
    "content": "<?php\n\nfunction swoole_version(): string\n{\n}\n\nfunction swoole_cpu_num(): int\n{\n}\n\nfunction swoole_last_error(): int\n{\n}\n\nfunction swoole_async_dns_lookup_coro(string $domain_name, float $timeout = 60, int $type = AF_INET): string|false\n{\n}\n\nfunction swoole_async_set(array $settings): bool\n{\n}\n\nfunction swoole_coroutine_create(callable $func, mixed ...$params): int|false\n{\n}\n\nfunction swoole_coroutine_defer(callable $callback): void\n{\n}\n\nfunction swoole_coroutine_socketpair(int $domain, int $type, int $protocol): array|false\n{\n}\n\nfunction swoole_test_kernel_coroutine(int $count = 100, float $sleep_time = 1.0): void\n{\n}\n\nfunction swoole_client_select(?array &$read, ?array &$write, ?array &$except, ?float $timeout = 0.5): false|int\n{\n}\n\nfunction swoole_set_process_name(string $process_name): bool\n{\n}\n\nfunction swoole_get_local_ip(int $family = 2): array\n{\n}\n\nfunction swoole_get_local_mac(): array\n{\n}\n\nfunction swoole_strerror(int $errno, int $error_type = SWOOLE_STRERROR_SYSTEM): string\n{\n}\n\nfunction swoole_errno(): int\n{\n}\n\nfunction swoole_clear_error(): void\n{\n}\n\nfunction swoole_error_log(int $level, string $msg): void\n{\n}\n\nfunction swoole_error_log_ex(int $level, int $error, string $msg): void\n{\n}\n\nfunction swoole_ignore_error(int $error): void\n{\n}\n\nfunction swoole_hashcode(string $data, int $type = 0): false|int\n{\n}\n\nfunction swoole_mime_type_add(string $suffix, string $mime_type): bool\n{\n}\n\nfunction swoole_mime_type_set(string $suffix, string $mime_type): void\n{\n}\n\nfunction swoole_mime_type_delete(string $suffix): bool\n{\n}\n\nfunction swoole_mime_type_get(string $filename): string\n{\n}\n\nfunction swoole_mime_type_exists(string $filename): bool\n{\n}\n\nfunction swoole_mime_type_list(): array\n{\n}\n\nfunction swoole_clear_dns_cache(): void\n{\n}\n\nfunction swoole_substr_unserialize(string $str, int $offset, int $length = 0, array $options = []): mixed\n{\n}\n\nfunction swoole_substr_json_decode(string $str, int $offset, int $length = 0, bool $associative = false, int $depth = 512, int $flags = 0): mixed\n{\n}\n\nfunction swoole_internal_call_user_shutdown_begin(): bool\n{\n}\n\n\nfunction swoole_implicit_fn(string $fn, mixed $args = null): mixed\n{\n\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: a27c7f31d40af4404abd6b7f055e7d52ace42cbc */\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_version, 0, 0, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_cpu_num, 0, 0, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_last_error arginfo_swoole_cpu_num\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_async_dns_lookup_coro, 0, 1, MAY_BE_STRING|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, domain_name, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"60\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_LONG, 0, \"AF_INET\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_async_set, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_coroutine_create, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, func, IS_CALLABLE, 0)\n\tZEND_ARG_VARIADIC_TYPE_INFO(0, params, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_coroutine_defer, 0, 1, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_coroutine_socketpair, 0, 3, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, domain, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, protocol, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_test_kernel_coroutine, 0, 0, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, \"100\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, sleep_time, IS_DOUBLE, 0, \"1.0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_client_select, 0, 3, MAY_BE_FALSE|MAY_BE_LONG)\n\tZEND_ARG_TYPE_INFO(1, read, IS_ARRAY, 1)\n\tZEND_ARG_TYPE_INFO(1, write, IS_ARRAY, 1)\n\tZEND_ARG_TYPE_INFO(1, except, IS_ARRAY, 1)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 1, \"0.5\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_set_process_name, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, process_name, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_get_local_ip, 0, 0, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, family, IS_LONG, 0, \"2\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_get_local_mac, 0, 0, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_strerror, 0, 1, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, errno, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, error_type, IS_LONG, 0, \"SWOOLE_STRERROR_SYSTEM\")\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_errno arginfo_swoole_cpu_num\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_clear_error, 0, 0, IS_VOID, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_error_log, 0, 2, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, level, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, msg, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_error_log_ex, 0, 3, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, level, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, error, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, msg, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_ignore_error, 0, 1, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, error, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_hashcode, 0, 1, MAY_BE_FALSE|MAY_BE_LONG)\n\tZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_mime_type_add, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, suffix, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, mime_type, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_mime_type_set, 0, 2, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, suffix, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, mime_type, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_mime_type_delete, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, suffix, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_mime_type_get, 0, 1, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_mime_type_exists, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_mime_type_list arginfo_swoole_get_local_mac\n\n#define arginfo_swoole_clear_dns_cache arginfo_swoole_clear_error\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_substr_unserialize, 0, 2, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, \"[]\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_substr_json_decode, 0, 2, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, associative, _IS_BOOL, 0, \"false\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, depth, IS_LONG, 0, \"512\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_internal_call_user_shutdown_begin, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_implicit_fn, 0, 1, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, fn, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, args, IS_MIXED, 0, \"null\")\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_atomic.stub.php",
    "content": "<?php\nnamespace Swoole {\n    class Atomic {\n        public function __construct(int $value = 0) {}\n        public function add(int $add_value = 1): int {}\n        public function sub(int $sub_value = 1): int {}\n        public function get(): int {}\n        public function set(int $value): void {}\n        public function cmpset(int $cmp_value, int $new_value): bool {}\n        public function wait(float $timeout = 1.0): bool {}\n        public function wakeup(int $count = 1): bool {}\n    }\n}\n\nnamespace Swoole\\Atomic {\n    class Long {\n        public function __construct(int $value = 0) {}\n        public function add(int $add_value = 1): int {}\n        public function sub(int $sub_value = 1): int {}\n        public function get(): int {}\n        public function set(int $value): void {}\n        public function cmpset(int $cmp_value, int $new_value): bool {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_atomic_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 7c83f8fbe7fd48ac2c7a2756a4e33a9edd666e42 */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Atomic___construct, 0, 0, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Atomic_add, 0, 0, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, add_value, IS_LONG, 0, \"1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Atomic_sub, 0, 0, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, sub_value, IS_LONG, 0, \"1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Atomic_get, 0, 0, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Atomic_set, 0, 1, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Atomic_cmpset, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, cmp_value, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, new_value, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Atomic_wait, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"1.0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Atomic_wakeup, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, \"1\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Atomic_Long___construct arginfo_class_Swoole_Atomic___construct\n\n#define arginfo_class_Swoole_Atomic_Long_add arginfo_class_Swoole_Atomic_add\n\n#define arginfo_class_Swoole_Atomic_Long_sub arginfo_class_Swoole_Atomic_sub\n\n#define arginfo_class_Swoole_Atomic_Long_get arginfo_class_Swoole_Atomic_get\n\n#define arginfo_class_Swoole_Atomic_Long_set arginfo_class_Swoole_Atomic_set\n\n#define arginfo_class_Swoole_Atomic_Long_cmpset arginfo_class_Swoole_Atomic_cmpset\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_channel_coro.stub.php",
    "content": "<?php\nnamespace Swoole\\Coroutine {\n\tclass Channel {\n\t\tpublic function __construct(int $size = 1) {}\n\t\tpublic function push(mixed $data, float $timeout = -1): bool {}\n\t\tpublic function pop(float $timeout = -1): mixed {}\n\t\tpublic function stats(): array {}\n\t\tpublic function close(): bool {}\n\t\tpublic function length(): int {}\n\t\tpublic function isEmpty(): bool {}\n\t\tpublic function isFull(): bool {}\n\t}\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_channel_coro_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 2ed4e36fbf2cabffca5dd794ba4be3dc1c6c086a */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Channel___construct, 0, 0, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, size, IS_LONG, 0, \"1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Channel_push, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, data, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"-1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Channel_pop, 0, 0, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"-1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Channel_stats, 0, 0, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Channel_close, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Channel_length, 0, 0, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_Channel_isEmpty arginfo_class_Swoole_Coroutine_Channel_close\n\n#define arginfo_class_Swoole_Coroutine_Channel_isFull arginfo_class_Swoole_Coroutine_Channel_close\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_client.stub.php",
    "content": "<?php\nnamespace Swoole {\n\tclass Client {\n\t\tpublic function __construct(int $type, bool $async = SWOOLE_SOCK_SYNC, string $id = '') {}\n\t\tpublic function __destruct() {}\n\t\tpublic function set(array $settings): bool {}\n\t\tpublic function connect(string $host, int $port = 0, float $timeout = 0.5, int $sock_flag = 0): bool {}\n\t\tpublic function recv(int $size = 65536, int $flag = 0): string|false {}\n\t\tpublic function send(string $data, int $flag = 0): false|int {}\n\t\tpublic function sendfile(string $filename, int $offset = 0, int $length = 0): bool {}\n\t\tpublic function sendto(string $ip, int $port, string $data): bool {}\n\t\tpublic function enableSSL(?callable $onSslReady = null): bool {}\n\t\tpublic function getPeerCert(): string|bool {}\n\t\tpublic function verifyPeerCert(): bool {}\n\t\tpublic function isConnected(): bool {}\n\t\tpublic function getsockname(): array|false {}\n\t\tpublic function getpeername(): array|false {}\n\t\tpublic function close(bool $force = false): bool {}\n\t\tpublic function shutdown(int $how): bool {}\n\t\t#ifdef SWOOLE_SOCKETS_SUPPORT\n\t\tpublic function getSocket(): \\Socket|false {}\n\t\t#endif\n\t}\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_client_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 75835dd199229a2d75969892c2fce00f4d75a362 */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Client___construct, 0, 0, 1)\n\tZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, async, _IS_BOOL, 0, \"SWOOLE_SOCK_SYNC\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id, IS_STRING, 0, \"\\'\\'\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Client___destruct, 0, 0, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Client_set, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Client_connect, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0.5\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, sock_flag, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Client_recv, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, size, IS_LONG, 0, \"65536\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flag, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Client_send, 0, 1, MAY_BE_FALSE|MAY_BE_LONG)\n\tZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flag, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Client_sendfile, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Client_sendto, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, ip, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Client_enableSSL, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, onSslReady, IS_CALLABLE, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Client_getPeerCert, 0, 0, MAY_BE_STRING|MAY_BE_BOOL)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Client_verifyPeerCert, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Client_isConnected arginfo_class_Swoole_Client_verifyPeerCert\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Client_getsockname, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Client_getpeername arginfo_class_Swoole_Client_getsockname\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Client_close, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, force, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Client_shutdown, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, how, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\n#if defined(SWOOLE_SOCKETS_SUPPORT)\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Client_getSocket, 0, 0, Socket, MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n#endif\n\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_client_async.stub.php",
    "content": "<?php\nnamespace Swoole\\Async {\n\tclass Client extends \\Swoole\\Client {\n\t\tpublic function __construct(int $type) {}\n\t\tpublic function __destruct() {}\n        public function connect(string $host, int $port = 0, float $timeout = 0.5, int $sock_flag = 0): bool {}\n        public function on(string $host, callable $callback): bool {}\n        public function enableSSL(?callable $onSslReady = null): bool {}\n\t\tpublic function isConnected(): bool {}\n        public function sleep(): bool {}\n        public function wakeup(): bool {}\n\t\tpublic function close(bool $force = false): bool {}\n\t}\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_client_async_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: fafe9bb39d602351810bfd1f51687886a9e8899f */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Async_Client___construct, 0, 0, 1)\n\tZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Async_Client___destruct, 0, 0, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Async_Client_connect, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0.5\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, sock_flag, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Async_Client_on, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Async_Client_enableSSL, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, onSslReady, IS_CALLABLE, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Async_Client_isConnected, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Async_Client_sleep arginfo_class_Swoole_Async_Client_isConnected\n\n#define arginfo_class_Swoole_Async_Client_wakeup arginfo_class_Swoole_Async_Client_isConnected\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Async_Client_close, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, force, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_client_coro.stub.php",
    "content": "<?php\nnamespace Swoole\\Coroutine {\n    class Client {\n        public function __construct(int $type) {}\n        public function __destruct() {}\n        public function set(array $settings): bool {}\n        public function connect(string $host, int $port = 0, float $timeout = 0, int $sock_flag = 0): bool {}\n        public function recv(float $timeout = 0): string|false {}\n        public function peek(int $length = 65535): string|false {}\n        public function send(string $data, float $timeout = 0): int|false {}\n        public function sendfile(string $filename, int $offset = 0, int $length = 0): bool {}\n        public function sendto(string $address, int $port, string $data): bool {}\n        public function recvfrom(int $length, mixed &$address, mixed &$port = 0): false|string {}\n        public function enableSSL(): bool {}\n        public function getPeerCert(): false|string {}\n        public function verifyPeerCert(bool $allow_self_signed = false): bool {}\n        public function exportSocket(): \\Swoole\\Coroutine\\Socket|false {}\n        public function isConnected(): bool {}\n        public function getsockname(): array|false {}\n        public function getpeername(): array|false {}\n        public function close(): bool {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_client_coro_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 21d030fcf1bd14eafc24b26c57cd56ad9a2b42bc */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Client___construct, 0, 0, 1)\n\tZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Client___destruct, 0, 0, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Client_set, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Client_connect, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, sock_flag, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Client_recv, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Client_peek, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, \"65535\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Client_send, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Client_sendfile, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Client_sendto, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, address, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Client_recvfrom, 0, 2, MAY_BE_FALSE|MAY_BE_STRING)\n\tZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(1, address, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(1, port, IS_MIXED, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Client_enableSSL, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Client_getPeerCert, 0, 0, MAY_BE_FALSE|MAY_BE_STRING)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Client_verifyPeerCert, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, allow_self_signed, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Client_exportSocket, 0, 0, Swoole\\\\Coroutine\\\\Socket, MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_Client_isConnected arginfo_class_Swoole_Coroutine_Client_enableSSL\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Client_getsockname, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_Client_getpeername arginfo_class_Swoole_Coroutine_Client_getsockname\n\n#define arginfo_class_Swoole_Coroutine_Client_close arginfo_class_Swoole_Coroutine_Client_enableSSL\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_coroutine.stub.php",
    "content": "<?php\nnamespace Swoole {\n    class Coroutine {\n        public static function create(callable $func, mixed ...$param): int|false {}\n        public static function defer(callable $callback): void {}\n        public static function set(array $options): void {}\n        public static function getOptions(): ?array {}\n        public static function exists(int $cid): bool {}\n        public static function yield(): bool {}\n        public static function cancel(int $cid, bool $throw_exception = false): bool {}\n        public static function join(array $cid_array, float $timeout = -1): bool {}\n        public static function isCanceled(): bool {}\n        public static function suspend(): bool {}\n        public static function resume(int $cid): bool {}\n        public static function stats(): array {}\n        public static function getCid(): int {}\n        public static function getuid(): int {}\n        public static function getPcid(int $cid = 0): false|int {}\n        public static function getContext(int $cid = 0): \\Swoole\\Coroutine\\Context|null {}\n        public static function getBackTrace(int $cid = 0, int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT, int $limit = 0): array|false {}\n        public static function printBackTrace(int $cid = 0, int $options = 0, int $limit = 0): void {}\n        public static function getElapsed(int $cid = 0): int {}\n        public static function getStackUsage(int $cid = 0): false|int {}\n        public static function list(): \\Swoole\\Coroutine\\Iterator {}\n        public static function listCoroutines(): \\Swoole\\Coroutine\\Iterator {}\n        public static function enableScheduler(): bool {}\n        public static function disableScheduler(): bool {}\n        public static function getExecuteTime(): int {}\n        public static function setTimeLimit(float $timeout): bool {}\n    }\n\n    class ExitException {\n        public function getFlags(): int {}\n        public function getStatus(): mixed {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_coroutine_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 55765106f02b8637d1f8d38e35d87693d6445cfc */\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_create, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, func, IS_CALLABLE, 0)\n\tZEND_ARG_VARIADIC_TYPE_INFO(0, param, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_defer, 0, 1, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_set, 0, 1, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_getOptions, 0, 0, IS_ARRAY, 1)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_exists, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, cid, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_yield, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_cancel, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, cid, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, throw_exception, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_join, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, cid_array, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"-1\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_isCanceled arginfo_class_Swoole_Coroutine_yield\n\n#define arginfo_class_Swoole_Coroutine_suspend arginfo_class_Swoole_Coroutine_yield\n\n#define arginfo_class_Swoole_Coroutine_resume arginfo_class_Swoole_Coroutine_exists\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_stats, 0, 0, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_getCid, 0, 0, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_getuid arginfo_class_Swoole_Coroutine_getCid\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_getPcid, 0, 0, MAY_BE_FALSE|MAY_BE_LONG)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, cid, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Coroutine_getContext, 0, 0, Swoole\\\\Coroutine\\\\Context, 1)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, cid, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_getBackTrace, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, cid, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, \"DEBUG_BACKTRACE_PROVIDE_OBJECT\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_printBackTrace, 0, 0, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, cid, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_getElapsed, 0, 0, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, cid, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_getStackUsage arginfo_class_Swoole_Coroutine_getPcid\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Coroutine_list, 0, 0, Swoole\\\\Coroutine\\\\Iterator, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_listCoroutines arginfo_class_Swoole_Coroutine_list\n\n#define arginfo_class_Swoole_Coroutine_enableScheduler arginfo_class_Swoole_Coroutine_yield\n\n#define arginfo_class_Swoole_Coroutine_disableScheduler arginfo_class_Swoole_Coroutine_yield\n\n#define arginfo_class_Swoole_Coroutine_getExecuteTime arginfo_class_Swoole_Coroutine_getCid\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_setTimeLimit, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, timeout, IS_DOUBLE, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_ExitException_getFlags arginfo_class_Swoole_Coroutine_getCid\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_ExitException_getStatus, 0, 0, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_coroutine_lock.stub.php",
    "content": "<?php\nnamespace Swoole\\Coroutine {\n    class Lock {\n        public function __construct(bool $shared = false) {}\n        public function lock(int $operation = LOCK_EX): bool {}\n        public function unlock(): bool {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_coroutine_lock_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 4e5606f9efd8fefdd965a1204fb289220139c10a */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Lock___construct, 0, 0, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, shared, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Lock_lock, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, operation, IS_LONG, 0, \"LOCK_EX\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Lock_unlock, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_coroutine_scheduler.stub.php",
    "content": "<?php\nnamespace Swoole\\Coroutine {\n    class Scheduler {\n        public function add(callable $func, mixed ...$param): void {}\n        public function parallel(int $n, callable $func, mixed ...$param): void {}\n        public function set(array $settings): void {}\n        public function getOptions(): ?array {}\n        public function start(): bool {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_coroutine_scheduler_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 38a1f5be85b4205ea9dfb48024e4e55b753cbdab */\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Scheduler_add, 0, 1, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, func, IS_CALLABLE, 0)\n\tZEND_ARG_VARIADIC_TYPE_INFO(0, param, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Scheduler_parallel, 0, 2, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, n, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, func, IS_CALLABLE, 0)\n\tZEND_ARG_VARIADIC_TYPE_INFO(0, param, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Scheduler_set, 0, 1, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Scheduler_getOptions, 0, 0, IS_ARRAY, 1)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Scheduler_start, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_coroutine_system.stub.php",
    "content": "<?php\nnamespace Swoole\\Coroutine {\n    class System {\n        public static function gethostbyname(string $domain_name, int $type = AF_INET, float $timeout = -1): false|string {}\n        public static function dnsLookup(string $domain_name, float $timeout = 60, int $type = AF_INET): string|false {}\n        public static function exec(string $command, bool $get_error_stream = false): array|false {}\n        public static function sleep(float $seconds): bool {}\n        public static function getaddrinfo(string $domain, int $family = AF_INET, int $socktype = SOCK_STREAM, int $protocol = STREAM_IPPROTO_TCP, ?string $service = null, float $timeout = -1): array|bool {}\n        public static function statvfs(string $path): array {}\n        public static function readFile(string $filename, int $flag = 0): false|string {}\n        public static function writeFile(string $filename, string $fileContent, int $flags = 0): false|int {}\n        public static function wait(float $timeout = -1): array|false {}\n        public static function waitPid(int $pid, float $timeout = -1): array|false {}\n        public static function waitSignal(int|array $signals, float $timeout = -1): int|false {}\n        public static function waitEvent(mixed $socket, int $events = SWOOLE_EVENT_READ, float $timeout = -1): int|false {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_coroutine_system_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 3c270ea28b44ea9ae57763943f8e0188d2fbcc03 */\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_gethostbyname, 0, 1, MAY_BE_FALSE|MAY_BE_STRING)\n\tZEND_ARG_TYPE_INFO(0, domain_name, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_LONG, 0, \"AF_INET\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"-1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_dnsLookup, 0, 1, MAY_BE_STRING|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, domain_name, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"60\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_LONG, 0, \"AF_INET\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_exec, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, get_error_stream, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_System_sleep, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, seconds, IS_DOUBLE, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_getaddrinfo, 0, 1, MAY_BE_ARRAY|MAY_BE_BOOL)\n\tZEND_ARG_TYPE_INFO(0, domain, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, family, IS_LONG, 0, \"AF_INET\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, socktype, IS_LONG, 0, \"SOCK_STREAM\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, protocol, IS_LONG, 0, \"STREAM_IPPROTO_TCP\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, service, IS_STRING, 1, \"null\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"-1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_System_statvfs, 0, 1, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_readFile, 0, 1, MAY_BE_FALSE|MAY_BE_STRING)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flag, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_writeFile, 0, 2, MAY_BE_FALSE|MAY_BE_LONG)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, fileContent, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_wait, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"-1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_waitPid, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, pid, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"-1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_waitSignal, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_MASK(0, signals, MAY_BE_LONG|MAY_BE_ARRAY, NULL)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"-1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_System_waitEvent, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, socket, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, events, IS_LONG, 0, \"SWOOLE_EVENT_READ\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"-1\")\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_event.stub.php",
    "content": "<?php\nfunction swoole_event_add(mixed $fd, ?callable $read_callback = null, ?callable $write_callback = null, int $events = SWOOLE_EVENT_READ): false|int {}\n\nfunction swoole_event_set(mixed $fd, ?callable $read_callback = null, ?callable $write_callback = null, int $events = 0): bool {}\n\nfunction swoole_event_del(mixed $fd): bool {}\n\nfunction swoole_event_write(mixed $fd, string $data): bool {}\n\nfunction swoole_event_wait(): void {}\n\nfunction swoole_event_rshutdown(): void {}\n\nfunction swoole_event_exit(): void {}\n\nfunction swoole_event_defer(callable $callback): bool {}\n\nfunction swoole_event_cycle(?callable $callback, bool $before = false): bool {}\n\nfunction swoole_event_dispatch(): bool {}\n\nfunction swoole_event_isset(mixed $fd, int $events = SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE): bool {}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_event_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 2636d6e4d82e1cf02361335a9fd1b57865f46761 */\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_event_add, 0, 1, MAY_BE_FALSE|MAY_BE_LONG)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_callback, IS_CALLABLE, 1, \"null\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, write_callback, IS_CALLABLE, 1, \"null\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, events, IS_LONG, 0, \"SWOOLE_EVENT_READ\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_event_set, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_callback, IS_CALLABLE, 1, \"null\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, write_callback, IS_CALLABLE, 1, \"null\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, events, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_event_del, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_event_write, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_event_wait, 0, 0, IS_VOID, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_event_rshutdown arginfo_swoole_event_wait\n\n#define arginfo_swoole_event_exit arginfo_swoole_event_wait\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_event_defer, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_event_cycle, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 1)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, before, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_event_dispatch, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_event_isset, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, events, IS_LONG, 0, \"SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE\")\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_ex.stub.php",
    "content": "<?php\n/**\n * @generate-function-entries\n */\nfunction swoole_get_objects(): false|array\n{\n}\n\nfunction swoole_get_vm_status(): array\n{\n}\n\nfunction swoole_get_object_by_handle(int $handle): false|object\n{\n}\n\nfunction swoole_name_resolver_lookup(string $name, Swoole\\NameResolver\\Context $ctx) : string\n{\n}\n\nfunction swoole_name_resolver_add(Swoole\\NameResolver $ns) : bool\n{\n}\n\nfunction swoole_name_resolver_remove(Swoole\\NameResolver $ns) : bool\n{\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_ex_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: b29d035d9a0fdb9b381bed781e790e6892ab209f */\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_get_objects, 0, 0, MAY_BE_FALSE|MAY_BE_ARRAY)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_get_vm_status, 0, 0, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_get_object_by_handle, 0, 1, MAY_BE_FALSE|MAY_BE_OBJECT)\n\tZEND_ARG_TYPE_INFO(0, handle, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_name_resolver_lookup, 0, 2, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)\n\tZEND_ARG_OBJ_INFO(0, ctx, Swoole\\\\NameResolver\\\\Context, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_name_resolver_add, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, ns, Swoole\\\\NameResolver, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_name_resolver_remove arginfo_swoole_name_resolver_add\n\n\nZEND_FUNCTION(swoole_get_objects);\nZEND_FUNCTION(swoole_get_vm_status);\nZEND_FUNCTION(swoole_get_object_by_handle);\nZEND_FUNCTION(swoole_name_resolver_lookup);\nZEND_FUNCTION(swoole_name_resolver_add);\nZEND_FUNCTION(swoole_name_resolver_remove);\n\n\nstatic const zend_function_entry ext_functions[] = {\n\tZEND_FE(swoole_get_objects, arginfo_swoole_get_objects)\n\tZEND_FE(swoole_get_vm_status, arginfo_swoole_get_vm_status)\n\tZEND_FE(swoole_get_object_by_handle, arginfo_swoole_get_object_by_handle)\n\tZEND_FE(swoole_name_resolver_lookup, arginfo_swoole_name_resolver_lookup)\n\tZEND_FE(swoole_name_resolver_add, arginfo_swoole_name_resolver_add)\n\tZEND_FE(swoole_name_resolver_remove, arginfo_swoole_name_resolver_remove)\n\tZEND_FE_END\n};\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_ftp.stub.php",
    "content": "<?php\n\nnamespace {\n    /**\n     * @var int\n     * @cvalue FTPTYPE_ASCII\n     */\n    const FTP_ASCII = UNKNOWN;\n    /**\n     * @var int\n     * @cvalue FTPTYPE_ASCII\n     */\n    const FTP_TEXT = UNKNOWN;\n    /**\n     * @var int\n     * @cvalue FTPTYPE_IMAGE\n     */\n    const FTP_BINARY = UNKNOWN;\n    /**\n     * @var int\n     * @cvalue FTPTYPE_IMAGE\n     */\n    const FTP_IMAGE = UNKNOWN;\n    /**\n     * @var int\n     * @cvalue PHP_FTP_AUTORESUME\n     */\n    const FTP_AUTORESUME = UNKNOWN;\n    /**\n     * @var int\n     * @cvalue PHP_FTP_OPT_TIMEOUT_SEC\n     */\n    const FTP_TIMEOUT_SEC = UNKNOWN;\n    /**\n     * @var int\n     * @cvalue PHP_FTP_OPT_AUTOSEEK\n     */\n    const FTP_AUTOSEEK = UNKNOWN;\n    /**\n     * @var int\n     * @cvalue PHP_FTP_OPT_USEPASVADDRESS\n     */\n    const FTP_USEPASVADDRESS = UNKNOWN;\n    /**\n     * @var int\n     * @cvalue PHP_FTP_FAILED\n     */\n    const FTP_FAILED = UNKNOWN;\n    /**\n     * @var int\n     * @cvalue PHP_FTP_FINISHED\n     */\n    const FTP_FINISHED = UNKNOWN;\n    /**\n     * @var int\n     * @cvalue PHP_FTP_MOREDATA\n     */\n    const FTP_MOREDATA = UNKNOWN;\n\n    function ftp_connect(string $hostname, int $port = 21, int $timeout = 90): FTP\\Connection|false {}\n\n    #ifdef SW_HAVE_FTP_SSL\n    function ftp_ssl_connect(string $hostname, int $port = 21, int $timeout = 90): FTP\\Connection|false {}\n    #endif\n\n    function ftp_login(FTP\\Connection $ftp, string $username, #[\\SensitiveParameter] string $password): bool {}\n    function ftp_pwd(FTP\\Connection $ftp): string|false {}\n    function ftp_cdup(FTP\\Connection $ftp): bool {}\n    function ftp_chdir(FTP\\Connection $ftp, string $directory): bool {}\n    function ftp_exec(FTP\\Connection $ftp, string $command): bool {}\n\n    /**\n     * @return array<int, string>|null\n     * @refcount 1\n     */\n    function ftp_raw(FTP\\Connection $ftp, string $command): ?array {}\n    function ftp_mkdir(FTP\\Connection $ftp, string $directory): string|false {}\n    function ftp_rmdir(FTP\\Connection $ftp, string $directory): bool {}\n    function ftp_chmod(FTP\\Connection $ftp, int $permissions, string $filename): int|false {}\n\n    /** @param string $response */\n    function ftp_alloc(FTP\\Connection $ftp, int $size, &$response = null): bool {}\n\n    /**\n     * @return array<int, string>|false\n     * @refcount 1\n     */\n    function ftp_nlist(FTP\\Connection $ftp, string $directory): array|false {}\n\n    /**\n     * @return array<int, string>|false\n     * @refcount 1\n     */\n    function ftp_rawlist(FTP\\Connection $ftp, string $directory, bool $recursive = false): array|false {}\n\n    /**\n     * @return array<int, array>|false\n     * @refcount 1\n     */\n    function ftp_mlsd(FTP\\Connection $ftp, string $directory): array|false {}\n\n    function ftp_systype(FTP\\Connection $ftp): string|false {}\n\n    /** @param resource $stream */\n    function ftp_fget(FTP\\Connection $ftp, $stream, string $remote_filename, int $mode = FTP_BINARY, int $offset = 0): bool {}\n\n    /** @param resource $stream */\n    function ftp_nb_fget(FTP\\Connection $ftp, $stream, string $remote_filename, int $mode = FTP_BINARY, int $offset = 0): int {}\n    function ftp_pasv(FTP\\Connection $ftp, bool $enable): bool {}\n    function ftp_get(FTP\\Connection $ftp, string $local_filename, string $remote_filename, int $mode = FTP_BINARY, int $offset = 0): bool {}\n    function ftp_nb_get(FTP\\Connection $ftp, string $local_filename, string $remote_filename, int $mode = FTP_BINARY, int $offset = 0): int|false {}\n    function ftp_nb_continue(FTP\\Connection $ftp): int {}\n\n    /** @param resource $stream */\n    function ftp_fput(FTP\\Connection $ftp, string $remote_filename, $stream, int $mode = FTP_BINARY, int $offset = 0): bool {}\n\n    /** @param resource $stream */\n    function ftp_nb_fput(FTP\\Connection $ftp, string $remote_filename, $stream, int $mode = FTP_BINARY, int $offset = 0): int {}\n    function ftp_put(FTP\\Connection $ftp, string $remote_filename, string $local_filename, int $mode = FTP_BINARY, int $offset = 0): bool {}\n    function ftp_append(FTP\\Connection $ftp, string $remote_filename, string $local_filename, int $mode = FTP_BINARY): bool {}\n    function ftp_nb_put(FTP\\Connection $ftp, string $remote_filename, string $local_filename, int $mode = FTP_BINARY, int $offset = 0): int|false {}\n    function ftp_size(FTP\\Connection $ftp, string $filename): int {}\n    function ftp_mdtm(FTP\\Connection $ftp, string $filename): int {}\n    function ftp_rename(FTP\\Connection $ftp, string $from, string $to): bool {}\n    function ftp_delete(FTP\\Connection $ftp, string $filename): bool {}\n    function ftp_site(FTP\\Connection $ftp, string $command): bool {}\n    function ftp_close(FTP\\Connection $ftp): bool {}\n\n    /** @alias ftp_close */\n    function ftp_quit(FTP\\Connection $ftp): bool {}\n\n    /** @param int|bool $value */\n    function ftp_set_option(FTP\\Connection $ftp, int $option, $value): bool {}\n    function ftp_get_option(FTP\\Connection $ftp, int $option): int|bool {}\n}\n\nnamespace FTP {\n    /**\n     * @strict-properties\n     * @not-serializable\n     */\n    final class Connection\n    {\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_ftp_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 4884776ce0fa7815e35882ffc34cbbccd0ec21ad */\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_ftp_connect, 0, 1, FTP\\\\Connection, MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, hostname, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, \"21\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_LONG, 0, \"90\")\nZEND_END_ARG_INFO()\n\n#if defined(SW_HAVE_FTP_SSL)\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_ftp_ssl_connect, 0, 1, FTP\\\\Connection, MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, hostname, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, \"21\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_LONG, 0, \"90\")\nZEND_END_ARG_INFO()\n#endif\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_login, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, username, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, password, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_ftp_pwd, 0, 1, MAY_BE_STRING|MAY_BE_FALSE)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_cdup, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_chdir, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, directory, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_exec, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_raw, 0, 2, IS_ARRAY, 1)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_ftp_mkdir, 0, 2, MAY_BE_STRING|MAY_BE_FALSE)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, directory, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_ftp_rmdir arginfo_ftp_chdir\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_ftp_chmod, 0, 3, MAY_BE_LONG|MAY_BE_FALSE)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, permissions, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_alloc, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, size, IS_LONG, 0)\n\tZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, response, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_ftp_nlist, 0, 2, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, directory, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_ftp_rawlist, 0, 2, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, directory, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, recursive, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\n#define arginfo_ftp_mlsd arginfo_ftp_nlist\n\n#define arginfo_ftp_systype arginfo_ftp_pwd\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_fget, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_INFO(0, stream)\n\tZEND_ARG_TYPE_INFO(0, remote_filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, \"FTP_BINARY\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_nb_fget, 0, 3, IS_LONG, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_INFO(0, stream)\n\tZEND_ARG_TYPE_INFO(0, remote_filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, \"FTP_BINARY\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_pasv, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, enable, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_get, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, local_filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, remote_filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, \"FTP_BINARY\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_ftp_nb_get, 0, 3, MAY_BE_LONG|MAY_BE_FALSE)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, local_filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, remote_filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, \"FTP_BINARY\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_nb_continue, 0, 1, IS_LONG, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_fput, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, remote_filename, IS_STRING, 0)\n\tZEND_ARG_INFO(0, stream)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, \"FTP_BINARY\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_nb_fput, 0, 3, IS_LONG, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, remote_filename, IS_STRING, 0)\n\tZEND_ARG_INFO(0, stream)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, \"FTP_BINARY\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_put, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, remote_filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, local_filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, \"FTP_BINARY\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_append, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, remote_filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, local_filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, \"FTP_BINARY\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_ftp_nb_put, 0, 3, MAY_BE_LONG|MAY_BE_FALSE)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, remote_filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, local_filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, \"FTP_BINARY\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_size, 0, 2, IS_LONG, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_ftp_mdtm arginfo_ftp_size\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_rename, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, from, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, to, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_delete, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_ftp_site arginfo_ftp_exec\n\n#define arginfo_ftp_close arginfo_ftp_cdup\n\n#define arginfo_ftp_quit arginfo_ftp_cdup\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftp_set_option, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)\n\tZEND_ARG_INFO(0, value)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_ftp_get_option, 0, 2, MAY_BE_LONG|MAY_BE_BOOL)\n\tZEND_ARG_OBJ_INFO(0, ftp, FTP\\\\Connection, 0)\n\tZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_http2_client_coro.stub.php",
    "content": "<?php\nnamespace Swoole\\Coroutine\\Http2 {\n    class Client {\n        public function __construct(string $host, int $port = 80, bool $open_ssl = false) {}\n        public function __destruct() {}\n        public function set(array $settings): bool {}\n        public function connect(): bool {}\n        public function stats(string $key = ''): array|int {}\n        public function isStreamExist(int $stream_id): bool {}\n        public function send(\\Swoole\\Http2\\Request $request): int|false {}\n        public function write(int $stream_id, mixed $data, bool $end_stream = false): bool {}\n        public function recv(float $timeout = 0): \\Swoole\\Http2\\Response|false {}\n        public function read(float $timeout = 0): \\Swoole\\Http2\\Response|false {}\n        public function ping(): bool {}\n        public function goaway(int $error_code = SWOOLE_HTTP2_ERROR_NO_ERROR, string $debug_data = ''): bool {}\n        public function close(): bool {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_http2_client_coro_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: df74e881c5cc6f05a6fd3288a128bdc6a69c5f4a */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Http2_Client___construct, 0, 0, 1)\n\tZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, \"80\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, open_ssl, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Http2_Client___destruct, 0, 0, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http2_Client_set, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http2_Client_connect, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Http2_Client_stats, 0, 0, MAY_BE_ARRAY|MAY_BE_LONG)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, \"\\'\\'\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http2_Client_isStreamExist, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, stream_id, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Http2_Client_send, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)\n\tZEND_ARG_OBJ_INFO(0, request, Swoole\\\\Http2\\\\Request, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http2_Client_write, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, stream_id, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, data, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, end_stream, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Http2_Client_recv, 0, 0, Swoole\\\\Http2\\\\Response, MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_Http2_Client_read arginfo_class_Swoole_Coroutine_Http2_Client_recv\n\n#define arginfo_class_Swoole_Coroutine_Http2_Client_ping arginfo_class_Swoole_Coroutine_Http2_Client_connect\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http2_Client_goaway, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, error_code, IS_LONG, 0, \"SWOOLE_HTTP2_ERROR_NO_ERROR\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, debug_data, IS_STRING, 0, \"\\'\\'\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_Http2_Client_close arginfo_class_Swoole_Coroutine_Http2_Client_connect\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_http_client_coro.stub.php",
    "content": "<?php\n/**\n * This file is part of Swoole.\n *\n * @link     https://www.swoole.com\n * @contact  team@swoole.com\n * @license  https://github.com/swoole/library/blob/master/LICENSE\n */\n\nnamespace Swoole\\Coroutine\\Http;\n\nclass Client\n{\n    public function __construct(string $host, int $port = 0, bool $ssl = false) {}\n\n    public function __destruct() {}\n\n    public function set(array $settings): bool {}\n\n    public function getDefer(): bool {}\n\n    public function setDefer(bool $defer = true): bool {}\n\n    public function setMethod(string $method): bool {}\n\n    public function setHeaders(array $headers): bool {}\n\n    public function setBasicAuth(string $username, #[\\SensitiveParameter] string $password): void {}\n\n    public function setCookies(array $cookies): bool {}\n\n    public function setData(array|string $data): bool {}\n\n    public function addFile(string $path, string $name, ?string $type = null, ?string $filename = null, int $offset = 0, int $length = 0): bool {}\n\n    public function addData(string $path, string $name, ?string $type = null, ?string $filename = null): bool {}\n\n    public function execute(string $path): bool {}\n\n    public function getsockname(): array|false {}\n\n    public function getpeername(): array|false {}\n\n    public function get(string $path): bool {}\n\n    public function post(string $path, mixed $data): bool {}\n\n    public function download(string $path, string $file, int $offset = 0): bool {}\n\n    public function getBody(): false|string {}\n\n    public function getHeaders(): null|array|false {}\n\n    public function getCookies(): null|array|false {}\n\n    public function getStatusCode(): false|int {}\n\n    public function getHeaderOut(): false|string {}\n\n    public function getPeerCert(): false|string {}\n\n    public function upgrade(string $path): bool {}\n\n    public function push(mixed $data, int $opcode = SWOOLE_WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): bool {}\n\n    public function recv(float $timeout = 0): bool|\\Swoole\\WebSocket\\Frame|string {}\n\n    public function close(): bool {}\n\n    public function ping(string $data = \"\"): bool {}\n\n    public function disconnect(int $code = SWOOLE_WEBSOCKET_CLOSE_NORMAL, string $reason = \"\"): bool {}\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_http_client_coro_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 725b2f7045c52b48fb640ff5b7d34ce2509427d7 */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client___construct, 0, 0, 1)\n\tZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, ssl, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client___destruct, 0, 0, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client_set, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client_getDefer, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client_setDefer, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, defer, _IS_BOOL, 0, \"true\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client_setMethod, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, method, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client_setHeaders, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, headers, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client_setBasicAuth, 0, 2, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, username, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, password, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client_setCookies, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, cookies, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client_setData, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_MASK(0, data, MAY_BE_ARRAY|MAY_BE_STRING, NULL)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client_addFile, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_STRING, 1, \"null\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, filename, IS_STRING, 1, \"null\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client_addData, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_STRING, 1, \"null\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, filename, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client_execute, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Http_Client_getsockname, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_Http_Client_getpeername arginfo_class_Swoole_Coroutine_Http_Client_getsockname\n\n#define arginfo_class_Swoole_Coroutine_Http_Client_get arginfo_class_Swoole_Coroutine_Http_Client_execute\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client_post, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, data, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client_download, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, file, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Http_Client_getBody, 0, 0, MAY_BE_FALSE|MAY_BE_STRING)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Http_Client_getHeaders, 0, 0, MAY_BE_NULL|MAY_BE_ARRAY|MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_Http_Client_getCookies arginfo_class_Swoole_Coroutine_Http_Client_getHeaders\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Http_Client_getStatusCode, 0, 0, MAY_BE_FALSE|MAY_BE_LONG)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_Http_Client_getHeaderOut arginfo_class_Swoole_Coroutine_Http_Client_getBody\n\n#define arginfo_class_Swoole_Coroutine_Http_Client_getPeerCert arginfo_class_Swoole_Coroutine_Http_Client_getBody\n\n#define arginfo_class_Swoole_Coroutine_Http_Client_upgrade arginfo_class_Swoole_Coroutine_Http_Client_execute\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client_push, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, data, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opcode, IS_LONG, 0, \"SWOOLE_WEBSOCKET_OPCODE_TEXT\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, \"SWOOLE_WEBSOCKET_FLAG_FIN\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Http_Client_recv, 0, 0, Swoole\\\\WebSocket\\\\Frame, MAY_BE_BOOL|MAY_BE_STRING)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_Http_Client_close arginfo_class_Swoole_Coroutine_Http_Client_getDefer\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client_ping, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, data, IS_STRING, 0, \"\\\"\\\"\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Client_disconnect, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, code, IS_LONG, 0, \"SWOOLE_WEBSOCKET_CLOSE_NORMAL\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, reason, IS_STRING, 0, \"\\\"\\\"\")\nZEND_END_ARG_INFO()\n\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_http_cookie.stub.php",
    "content": "<?php\nnamespace Swoole\\Http {\n\tclass Cookie {\n\t    public function __construct(bool $encode = true) {}\n\t    public function withName(string $name): \\Swoole\\Http\\Cookie {}\n\t    public function withValue(string $value = ''): \\Swoole\\Http\\Cookie {}\n\t    public function withExpires(int $expires = 0): \\Swoole\\Http\\Cookie {}\n\t    public function withPath(string $path = '/'): \\Swoole\\Http\\Cookie {}\n\t    public function withDomain(string $domain = ''): \\Swoole\\Http\\Cookie {}\n\t    public function withSecure(bool $secure = false): \\Swoole\\Http\\Cookie {}\n\t    public function withHttpOnly(bool $httpOnly = false): \\Swoole\\Http\\Cookie {}\n\t    public function withSameSite(string $sameSite = ''): \\Swoole\\Http\\Cookie {}\n\t    public function withPriority(string $priority = ''): \\Swoole\\Http\\Cookie {}\n\t    public function withPartitioned(bool $partitioned = false): \\Swoole\\Http\\Cookie {}\n\t    public function toArray(): array {}\n        public function toString(): string | false {}\n\t    public function reset(): void {}\n\t}\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_http_cookie_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: a11e74ea8b1a4c77a3c10fbfbd94775c504ba812 */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Http_Cookie___construct, 0, 0, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, encode, _IS_BOOL, 0, \"true\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Http_Cookie_withName, 0, 1, Swoole\\\\Http\\\\Cookie, 0)\n\tZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Http_Cookie_withValue, 0, 0, Swoole\\\\Http\\\\Cookie, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 0, \"\\'\\'\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Http_Cookie_withExpires, 0, 0, Swoole\\\\Http\\\\Cookie, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, expires, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Http_Cookie_withPath, 0, 0, Swoole\\\\Http\\\\Cookie, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, path, IS_STRING, 0, \"\\'/\\'\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Http_Cookie_withDomain, 0, 0, Swoole\\\\Http\\\\Cookie, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, domain, IS_STRING, 0, \"\\'\\'\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Http_Cookie_withSecure, 0, 0, Swoole\\\\Http\\\\Cookie, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, secure, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Http_Cookie_withHttpOnly, 0, 0, Swoole\\\\Http\\\\Cookie, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, httpOnly, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Http_Cookie_withSameSite, 0, 0, Swoole\\\\Http\\\\Cookie, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, sameSite, IS_STRING, 0, \"\\'\\'\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Http_Cookie_withPriority, 0, 0, Swoole\\\\Http\\\\Cookie, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, priority, IS_STRING, 0, \"\\'\\'\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Http_Cookie_withPartitioned, 0, 0, Swoole\\\\Http\\\\Cookie, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, partitioned, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Cookie_toArray, 0, 0, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Http_Cookie_toString, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Cookie_reset, 0, 0, IS_VOID, 0)\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_http_request.stub.php",
    "content": "<?php\nnamespace Swoole\\Http {\n\tclass Request {\n\t\tpublic function __destruct() {}\n\t\tpublic function getData(): string|false {}\n\t\tpublic static function create(array $options = []): Request {}\n\t\tpublic function parse(string $data): int|false {}\n\t\tpublic function isCompleted(): bool {}\n\t\tpublic function getMethod(): string|false {}\n\t\tpublic function getContent(): string|false {}\n\t}\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_http_request_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 826fa839b630cd233ea0b2a114cfee42003f5d19 */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Http_Request___destruct, 0, 0, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Http_Request_getData, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Http_Request_create, 0, 0, Swoole\\\\Http\\\\Request, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, \"[]\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Http_Request_parse, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Request_isCompleted, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Http_Request_getMethod arginfo_class_Swoole_Http_Request_getData\n\n#define arginfo_class_Swoole_Http_Request_getContent arginfo_class_Swoole_Http_Request_getData\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_http_response.stub.php",
    "content": "<?php\nnamespace Swoole\\Http {\n\tclass Response {\n\t\tpublic function write(string $content): bool {}\n\t\tpublic function end(?string $content = null): bool {}\n\t\tpublic function sendfile(string $filename, int $offset = 0, int $length = 0): bool {}\n\t\tpublic function redirect(string $location, int $http_code = 302): bool {}\n\t\tpublic function cookie(\\Swoole\\Http\\Cookie|string $name_or_object , string $value = '', int $expires = 0 , string $path = '/', string $domain  = '', bool $secure = false , bool $httponly = false, string $samesite = '', string $priority = '', bool $partitioned = false): bool {}\n\t\tpublic function rawcookie(string $name, string $value = '', int $expires = 0 , string $path = '/', string $domain  = '', bool $secure = false , bool $httponly = false, string $samesite = '', string $priority = '', bool $partitioned = false): bool {}\n\t\tpublic function header(string $key, string|array $value, bool $format = true): bool {}\n\t\tpublic function initHeader(): bool {}\n\t\tpublic function isWritable(): bool {}\n\t\tpublic function detach(): bool {}\n\t\tpublic static function create(object|array|int $server = -1, int $fd = -1): Response|false {}\n\t\tpublic function upgrade(): bool {}\n\t\tpublic function push(\\Swoole\\WebSocket\\Frame|string $data, int $opcode = SWOOLE_WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): bool {}\n\t\tpublic function recv(float $timeout = 0): \\Swoole\\WebSocket\\Frame|false|string {}\n\t\tpublic function close(): bool {}\n\t\tpublic function trailer(string $key, string $value): bool {}\n\t\tpublic function ping(string $data = ''): bool {}\n\t\tpublic function goaway(int $error_code = SWOOLE_HTTP2_ERROR_NO_ERROR, string $debug_data = ''): bool {}\n\t\tpublic function status(int $http_code, string $reason = ''): bool {}\n\t\tpublic function disconnect(int $code = SWOOLE_WEBSOCKET_CLOSE_NORMAL, string $reason = \"\"): bool {}\n\t\tpublic function __destruct() {}\n\t}\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_http_response_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 1a9de3282e3f5ad468a56ef68064dd824b21ad58 */\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_write, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, content, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_end, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, content, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_sendfile, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_redirect, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, location, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, http_code, IS_LONG, 0, \"302\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_cookie, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_TYPE_MASK(0, name_or_object, Swoole\\\\Http\\\\Cookie, MAY_BE_STRING, NULL)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 0, \"\\'\\'\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, expires, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, path, IS_STRING, 0, \"\\'/\\'\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, domain, IS_STRING, 0, \"\\'\\'\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, secure, _IS_BOOL, 0, \"false\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, httponly, _IS_BOOL, 0, \"false\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, samesite, IS_STRING, 0, \"\\'\\'\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, priority, IS_STRING, 0, \"\\'\\'\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, partitioned, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_rawcookie, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 0, \"\\'\\'\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, expires, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, path, IS_STRING, 0, \"\\'/\\'\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, domain, IS_STRING, 0, \"\\'\\'\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, secure, _IS_BOOL, 0, \"false\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, httponly, _IS_BOOL, 0, \"false\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, samesite, IS_STRING, 0, \"\\'\\'\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, priority, IS_STRING, 0, \"\\'\\'\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, partitioned, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_header, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)\n\tZEND_ARG_TYPE_MASK(0, value, MAY_BE_STRING|MAY_BE_ARRAY, NULL)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, format, _IS_BOOL, 0, \"true\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_initHeader, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Http_Response_isWritable arginfo_class_Swoole_Http_Response_initHeader\n\n#define arginfo_class_Swoole_Http_Response_detach arginfo_class_Swoole_Http_Response_initHeader\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Http_Response_create, 0, 0, Swoole\\\\Http\\\\Response, MAY_BE_FALSE)\n\tZEND_ARG_TYPE_MASK(0, server, MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_LONG, \"-1\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fd, IS_LONG, 0, \"-1\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Http_Response_upgrade arginfo_class_Swoole_Http_Response_initHeader\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_push, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_TYPE_MASK(0, data, Swoole\\\\WebSocket\\\\Frame, MAY_BE_STRING, NULL)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opcode, IS_LONG, 0, \"SWOOLE_WEBSOCKET_OPCODE_TEXT\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, \"SWOOLE_WEBSOCKET_FLAG_FIN\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Http_Response_recv, 0, 0, Swoole\\\\WebSocket\\\\Frame, MAY_BE_FALSE|MAY_BE_STRING)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Http_Response_close arginfo_class_Swoole_Http_Response_initHeader\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_trailer, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_ping, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, data, IS_STRING, 0, \"\\'\\'\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_goaway, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, error_code, IS_LONG, 0, \"SWOOLE_HTTP2_ERROR_NO_ERROR\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, debug_data, IS_STRING, 0, \"\\'\\'\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_status, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, http_code, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, reason, IS_STRING, 0, \"\\'\\'\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Http_Response_disconnect, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, code, IS_LONG, 0, \"SWOOLE_WEBSOCKET_CLOSE_NORMAL\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, reason, IS_STRING, 0, \"\\\"\\\"\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Http_Response___destruct, 0, 0, 0)\nZEND_END_ARG_INFO()\n\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_http_server_coro.stub.php",
    "content": "<?php\nnamespace Swoole\\Coroutine\\Http {\n\tfinal class Server {\n\t\tpublic function __construct(string $host, int $port = 0, bool $ssl = false, bool $reuse_port = false) {}\n\t\tpublic function __destruct() {}\n\t\tpublic function set(array $settings): bool {}\n\t\tpublic function handle(string $pattern, callable $callback): bool {}\n\t\tpublic function start(): bool {}\n\t\tpublic function shutdown(): void {}\n\t\tprivate function onAccept(\\Swoole\\Coroutine\\Socket $conn): void {}\n\t}\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_http_server_coro_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 6f65975475013861bdaec52a0fb3fe3b1dc75657 */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Server___construct, 0, 0, 1)\n\tZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, ssl, _IS_BOOL, 0, \"false\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, reuse_port, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Server___destruct, 0, 0, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Server_set, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Server_handle, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Server_start, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Server_shutdown, 0, 0, IS_VOID, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Http_Server_onAccept, 0, 1, IS_VOID, 0)\n\tZEND_ARG_OBJ_INFO(0, conn, Swoole\\\\Coroutine\\\\Socket, 0)\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_lock.stub.php",
    "content": "<?php\nnamespace Swoole {\n    class Lock {\n        public function __construct(int $type = SWOOLE_MUTEX) {}\n        public function lock(int $operation = LOCK_EX, float $timeout = -1): bool {}\n        public function unlock(): bool {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_lock_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 36990aba3edadc1e5e29b745b081719e57a6f7a5 */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Lock___construct, 0, 0, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_LONG, 0, \"SWOOLE_MUTEX\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Lock_lock, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, operation, IS_LONG, 0, \"LOCK_EX\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"-1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Lock_unlock, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_name_resolver.stub.php",
    "content": "<?php\n/**\n * @generate-function-entries\n */\nnamespace Swoole\\NameResolver {\n    class Context {\n        public function __construct(int $family = AF_INET, bool $withPort = false) {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_name_resolver_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 385775b8664fb0cfc035bfc26fb762885cb77aa8 */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_NameResolver_Context___construct, 0, 0, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, family, IS_LONG, 0, \"AF_INET\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, withPort, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\n\nZEND_METHOD(Swoole_NameResolver_Context, __construct);\n\n\nstatic const zend_function_entry class_Swoole_NameResolver_Context_methods[] = {\n\tZEND_ME(Swoole_NameResolver_Context, __construct, arginfo_class_Swoole_NameResolver_Context___construct, ZEND_ACC_PUBLIC)\n\tZEND_FE_END\n};\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_process.stub.php",
    "content": "<?php\nnamespace Swoole {\n    class Process {\n        public function __construct(callable $callback, bool $redirect_stdin_and_stdout = false, int $pipe_type = SOCK_DGRAM, bool $enable_coroutine = false) {}\n        public function __destruct() {}\n        public function useQueue(int $key = 0, int $mode = 2, int $capacity = -1): bool {}\n        public function statQueue(): array|false {}\n        public function freeQueue(): bool {}\n        public function pop(int $size = 65536): string|false {}\n        public function push(string $data): bool {}\n        public static function kill(int $pid, int $signal_no = SIGTERM): bool {}\n        public static function signal(int $signal_no, ?callable $callback = null): bool {}\n        public static function alarm(int $usec, int $type = 0): bool {}\n        public static function wait(bool $blocking = true): array|false {}\n        public static function daemon(bool $nochdir = true, bool $noclose = true, array $pipes = []): bool {}\n        #ifdef HAVE_CPU_AFFINITY\n        public static function setAffinity(array $cpu_settings): bool {}\n        public static function getAffinity(): array {}\n        #endif\n        public function set(array $settings): void {}\n        public function setTimeout(float $seconds): bool {}\n        public function setBlocking(bool $blocking): bool {}\n        public function setPriority(int $which, int $priority, ?int $who = null): bool {}\n        public function getPriority(int $which, ?int $who = null): int | false {}\n        public function start(): bool|int {}\n        public function write(string $data): false|int {}\n        public function read(int $size = 8192): false|string {}\n        public function close(int $which = 0): bool {}\n        public function exit(int $exit_code = 0): void {}\n        public function exec(string $exec_file, array $args): bool {}\n        public function exportSocket(): \\Swoole\\Coroutine\\Socket|false {}\n        public function name(string $process_name): bool {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_process_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: e78eaf1e64bf96d0ac0fe2220e670f866eb16473 */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Process___construct, 0, 0, 1)\n\tZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, redirect_stdin_and_stdout, _IS_BOOL, 0, \"false\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pipe_type, IS_LONG, 0, \"SOCK_DGRAM\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, enable_coroutine, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Process___destruct, 0, 0, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_useQueue, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, \"2\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, capacity, IS_LONG, 0, \"-1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Process_statQueue, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_freeQueue, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Process_pop, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, size, IS_LONG, 0, \"65536\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_push, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_kill, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, pid, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, signal_no, IS_LONG, 0, \"SIGTERM\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_signal, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, signal_no, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, callback, IS_CALLABLE, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_alarm, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, usec, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Process_wait, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, blocking, _IS_BOOL, 0, \"true\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_daemon, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nochdir, _IS_BOOL, 0, \"true\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, noclose, _IS_BOOL, 0, \"true\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pipes, IS_ARRAY, 0, \"[]\")\nZEND_END_ARG_INFO()\n\n#if defined(HAVE_CPU_AFFINITY)\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_setAffinity, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, cpu_settings, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n#endif\n\n#if defined(HAVE_CPU_AFFINITY)\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_getAffinity, 0, 0, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n#endif\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_set, 0, 1, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_setTimeout, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, seconds, IS_DOUBLE, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_setBlocking, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, blocking, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_setPriority, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, which, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, priority, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, who, IS_LONG, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Process_getPriority, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, which, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, who, IS_LONG, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Process_start, 0, 0, MAY_BE_BOOL|MAY_BE_LONG)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Process_write, 0, 1, MAY_BE_FALSE|MAY_BE_LONG)\n\tZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Process_read, 0, 0, MAY_BE_FALSE|MAY_BE_STRING)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, size, IS_LONG, 0, \"8192\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_close, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, which, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_exit, 0, 0, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, exit_code, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_exec, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, exec_file, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, args, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Process_exportSocket, 0, 0, Swoole\\\\Coroutine\\\\Socket, MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_name, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, process_name, IS_STRING, 0)\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_process_pool.stub.php",
    "content": "<?php\nnamespace Swoole\\Process {\n    class Pool {\n        public function __construct(int $worker_num, int $ipc_type = SWOOLE_IPC_NONE, int $msgqueue_key = 0, bool $enable_coroutine = false) {}\n        public function __destruct() {}\n        public function set(array $settings): void {}\n        public function on(string $name, callable $callback): bool {}\n        public function getProcess(int $work_id = -1): \\Swoole\\Process|false {}\n        public function listen(string $host, int $port = 0, int $backlog = 2048): bool {}\n        public function write(string $data): bool {}\n        public function sendMessage(string $data, int $dst_worker_id): bool {}\n        public function detach(): bool {}\n        public function start(): null|false {}\n        public function stop(): void {}\n        public function shutdown(): bool {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_process_pool_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 8d0a98f105e75dcf8b47a96a8d646fbd51f4002c */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Process_Pool___construct, 0, 0, 1)\n\tZEND_ARG_TYPE_INFO(0, worker_num, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, ipc_type, IS_LONG, 0, \"SWOOLE_IPC_NONE\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, msgqueue_key, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, enable_coroutine, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Process_Pool___destruct, 0, 0, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_Pool_set, 0, 1, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_Pool_on, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Process_Pool_getProcess, 0, 0, Swoole\\\\Process, MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, work_id, IS_LONG, 0, \"-1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_Pool_listen, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, backlog, IS_LONG, 0, \"2048\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_Pool_write, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_Pool_sendMessage, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, dst_worker_id, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_Pool_detach, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Process_Pool_start, 0, 0, MAY_BE_NULL|MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Process_Pool_stop, 0, 0, IS_VOID, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Process_Pool_shutdown arginfo_class_Swoole_Process_Pool_detach\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_redis_server.stub.php",
    "content": "<?php\nnamespace Swoole\\Redis {\n    class Server {\n        public function setHandler(string $command, callable $callback): bool {}\n        public function getHandler(string $command): \\Closure {}\n        public static function format(int $type, mixed $value = null): false|string {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_redis_server_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 748e9ecc6c1cf82a1b36dcc149d0523f9f3d977a */\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Redis_Server_setHandler, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Redis_Server_getHandler, 0, 1, Closure, 0)\n\tZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Redis_Server_format, 0, 1, MAY_BE_FALSE|MAY_BE_STRING)\n\tZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_MIXED, 0, \"null\")\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_runtime.stub.php",
    "content": "<?php\nnamespace Swoole {\n\tclass Runtime {\n\t\tpublic static function enableCoroutine(int $flags = SWOOLE_HOOK_ALL): bool {}\n\t\tpublic static function getHookFlags(): int {}\n\t\tpublic static function setHookFlags(int $flags): bool {}\n\t}\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_runtime_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: c42e4c108dd49f37a5953391a69cdad50106f17d */\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Runtime_enableCoroutine, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, \"SWOOLE_HOOK_ALL\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Runtime_getHookFlags, 0, 0, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Runtime_setHookFlags, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0)\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_server.stub.php",
    "content": "<?php\nnamespace Swoole {\n    final class Server {\n        public function __construct(string $host = '0.0.0.0', int $port = 0, int $mode = SWOOLE_BASE, int $sock_type = SWOOLE_SOCK_TCP) {}\n        public function __destruct() {}\n        public function set(array $settings): bool {}\n        public function on(string $event_name, callable $callback): bool {}\n        public function getCallback(string $event_name): \\Closure|string|null|array {}\n        public function listen(string $host, int $port, int $sock_type): false|Server\\Port {}\n        public function sendMessage(mixed $message, int $dst_worker_id): bool {}\n        public function addProcess(\\Swoole\\Process $process): int|false {}\n        public function addCommand(string $name, int $accepted_process_types, callable $callback): bool {}\n        public function start(): bool {}\n        public function stop(int $workerId = -1): bool {}\n        public function send(int|string $fd, string $send_data, int $serverSocket = -1): bool {}\n        public function sendfile(int $conn_fd, string $filename, int $offset = 0, int $length = 0): bool {}\n        public function stats(): array {}\n        public function bind(int $fd, int $uid): bool {}\n        public function sendto(string $ip, int $port, string $send_data, int $server_socket = -1): bool {}\n        public function sendwait(int $conn_fd, string $send_data): bool {}\n        public function exists(int $fd): bool {}\n        public function protect(int $fd, bool $is_protected = true): bool {}\n        public function close(int $fd, bool $reset = false): bool {}\n        public function pause(int $fd): bool {}\n        public function resume(int $fd): bool {}\n        public function task(mixed $data, int $taskWorkerIndex = -1, ?callable $finishCallback = null): int|false {}\n        public function taskwait(mixed $data, float $timeout = 0.5, int $taskWorkerIndex = -1): mixed {}\n        public function taskWaitMulti(array $tasks, float $timeout = 0.5): false|array {}\n        public function taskCo(array $tasks, float $timeout = 0.5): array|false {}\n        public function finish(mixed $data): bool {}\n        public function reload(bool $only_reload_taskworker = false): bool {}\n        public function shutdown(): bool {}\n        public function heartbeat(bool $ifCloseConnection = true): false|array {}\n        public function command(string $name, int $process_id, int $process_type, mixed $data, bool $json_decode = true): false|string|array {}\n        public function getClientList(int $start_fd = 0, int $find_count = 10): false|array {}\n        public function getClientInfo(int $fd, int $reactor_id = -1, bool $ignoreError = false): false|array {}\n        public function getWorkerId(): int|false {}\n        public function getWorkerPid(int $worker_id = -1): int|false {}\n        public function getWorkerStatus(int $worker_id = -1): int|false {}\n        public function getManagerPid(): int {}\n        public function getMasterPid(): int {}\n        #ifdef SWOOLE_SOCKETS_SUPPORT\n        public function getSocket(int $port = 0): false|\\Socket {}\n        #endif\n        public function getLastError(): int {}\n    }\n}\n\nnamespace Swoole\\Connection {\n    final class Iterator {\n        public function __construct() {}\n        public function __destruct() {}\n        public function rewind(): void {}\n        public function next(): void {}\n        public function current(): mixed {}\n        public function key(): mixed {}\n        public function valid(): bool {}\n        public function count(): int {}\n        public function offsetExists(mixed $fd): bool {}\n        public function offsetGet(mixed $fd): mixed {}\n        public function offsetSet(mixed $fd, mixed $value): void {}\n        public function offsetUnset(mixed $fd): void {}\n    }\n}\n\nnamespace Swoole\\Server {\n    final class Task {\n        public function finish(mixed $data): bool {}\n        public static function pack(mixed $data): string|false {}\n        public static function unpack(string $data): mixed {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_server_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: b6b6e8431c0b97603630fb1428d7a3c86a527db2 */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Server___construct, 0, 0, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, \"\\'0.0.0.0\\'\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, \"SWOOLE_BASE\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, sock_type, IS_LONG, 0, \"SWOOLE_SOCK_TCP\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Server___destruct, 0, 0, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_set, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_on, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, event_name, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Server_getCallback, 0, 1, Closure, MAY_BE_STRING|MAY_BE_NULL|MAY_BE_ARRAY)\n\tZEND_ARG_TYPE_INFO(0, event_name, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Server_listen, 0, 3, Swoole\\\\Server\\\\Port, MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, sock_type, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_sendMessage, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, message, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, dst_worker_id, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_addProcess, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)\n\tZEND_ARG_OBJ_INFO(0, process, Swoole\\\\Process, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_addCommand, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, accepted_process_types, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_start, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_stop, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, workerId, IS_LONG, 0, \"-1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_send, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_MASK(0, fd, MAY_BE_LONG|MAY_BE_STRING, NULL)\n\tZEND_ARG_TYPE_INFO(0, send_data, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, serverSocket, IS_LONG, 0, \"-1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_sendfile, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, conn_fd, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_stats, 0, 0, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_bind, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, uid, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_sendto, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, ip, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, send_data, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, server_socket, IS_LONG, 0, \"-1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_sendwait, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, conn_fd, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, send_data, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_exists, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_protect, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, is_protected, _IS_BOOL, 0, \"true\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_close, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, reset, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Server_pause arginfo_class_Swoole_Server_exists\n\n#define arginfo_class_Swoole_Server_resume arginfo_class_Swoole_Server_exists\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_task, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, data, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, taskWorkerIndex, IS_LONG, 0, \"-1\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, finishCallback, IS_CALLABLE, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_taskwait, 0, 1, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, data, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0.5\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, taskWorkerIndex, IS_LONG, 0, \"-1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_taskWaitMulti, 0, 1, MAY_BE_FALSE|MAY_BE_ARRAY)\n\tZEND_ARG_TYPE_INFO(0, tasks, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0.5\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_taskCo, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, tasks, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0.5\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_finish, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, data, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_reload, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, only_reload_taskworker, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Server_shutdown arginfo_class_Swoole_Server_start\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_heartbeat, 0, 0, MAY_BE_FALSE|MAY_BE_ARRAY)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, ifCloseConnection, _IS_BOOL, 0, \"true\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_command, 0, 4, MAY_BE_FALSE|MAY_BE_STRING|MAY_BE_ARRAY)\n\tZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, process_id, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, process_type, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, data, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, json_decode, _IS_BOOL, 0, \"true\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_getClientList, 0, 0, MAY_BE_FALSE|MAY_BE_ARRAY)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start_fd, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, find_count, IS_LONG, 0, \"10\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_getClientInfo, 0, 1, MAY_BE_FALSE|MAY_BE_ARRAY)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, reactor_id, IS_LONG, 0, \"-1\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, ignoreError, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_getWorkerId, 0, 0, MAY_BE_LONG|MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_getWorkerPid, 0, 0, MAY_BE_LONG|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, worker_id, IS_LONG, 0, \"-1\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Server_getWorkerStatus arginfo_class_Swoole_Server_getWorkerPid\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_getManagerPid, 0, 0, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Server_getMasterPid arginfo_class_Swoole_Server_getManagerPid\n\n#if defined(SWOOLE_SOCKETS_SUPPORT)\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Server_getSocket, 0, 0, Socket, MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n#endif\n\n#define arginfo_class_Swoole_Server_getLastError arginfo_class_Swoole_Server_getManagerPid\n\n#define arginfo_class_Swoole_Connection_Iterator___construct arginfo_class_Swoole_Server___destruct\n\n#define arginfo_class_Swoole_Connection_Iterator___destruct arginfo_class_Swoole_Server___destruct\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Connection_Iterator_rewind, 0, 0, IS_VOID, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Connection_Iterator_next arginfo_class_Swoole_Connection_Iterator_rewind\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Connection_Iterator_current, 0, 0, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Connection_Iterator_key arginfo_class_Swoole_Connection_Iterator_current\n\n#define arginfo_class_Swoole_Connection_Iterator_valid arginfo_class_Swoole_Server_start\n\n#define arginfo_class_Swoole_Connection_Iterator_count arginfo_class_Swoole_Server_getManagerPid\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Connection_Iterator_offsetExists, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Connection_Iterator_offsetGet, 0, 1, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Connection_Iterator_offsetSet, 0, 2, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Connection_Iterator_offsetUnset, 0, 1, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Server_Task_finish arginfo_class_Swoole_Server_finish\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_Task_pack, 0, 1, MAY_BE_STRING|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, data, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_Task_unpack, 0, 1, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_server_port.stub.php",
    "content": "<?php\nnamespace Swoole\\Server {\n    class Port {\n        private function __construct() {}\n        public function __destruct() {}\n        public function set(array $settings): void {}\n        public function on(string $event_name, callable $callback): bool {}\n        public function getCallback(string $event_name): \\Closure|null {}\n        #ifdef SWOOLE_SOCKETS_SUPPORT\n        public function getSocket(): \\Socket|false {}\n        #endif\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_server_port_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 8d135ba05df9cfb18ecdb5764ea5434c25efc631 */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Server_Port___construct, 0, 0, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Server_Port___destruct arginfo_class_Swoole_Server_Port___construct\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_Port_set, 0, 1, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_Port_on, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, event_name, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Server_Port_getCallback, 0, 1, Closure, 1)\n\tZEND_ARG_TYPE_INFO(0, event_name, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\n#if defined(SWOOLE_SOCKETS_SUPPORT)\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Server_Port_getSocket, 0, 0, Socket, MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n#endif\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_socket_coro.stub.php",
    "content": "<?php\nnamespace Swoole\\Coroutine {\n    class Socket {\n        public function __construct(int $domain, int $type, int $protocol = IPPROTO_IP) {}\n        public function bind(string $address,int $port = 0): bool {}\n        public function listen(int $backlog = 512): bool {}\n        public function accept(float $timeout = 0): Socket|false {}\n        public function connect(string $host, int $port = 0, float $timeout = 0): bool {}\n        public function checkLiveness(): bool {}\n        public function getBoundCid(int $event): int {}\n        public function peek(int $length = 65536): string|false {}\n        public function recv(int $length = 65536, float $timeout = 0): string|false {}\n        public function send(string $data, float $timeout = 0): int|false {}\n        public function readVector(array $io_vector, float $timeout = 0): false|array {}\n        public function readVectorAll(array $io_vector, float $timeout = 0): false|array {}\n        public function writeVector(array $io_vector, float $timeout = 0): false|int {}\n        public function writeVectorAll(array $io_vector, float $timeout = 0): false|int {}\n        public function sendFile(string $file, int $offset = 0, int $length = 0): bool {}\n        public function recvAll(int $length = 65536, float $timeout = 0): false|string {}\n        public function sendAll(string $data, float $timeout = 0): int|false {}\n        public function recvPacket(float $timeout = 0): false|string {}\n        public function recvLine(int $length = 65536, float $timeout = 0): string|false {}\n        public function recvWithBuffer(int $length = 65536, float $timeout = 0): string|false {}\n        public function recvfrom(mixed &$peername, float $timeout = 0): string|false {}\n        public function sendto(string $addr, int $port, string $data): int|false {}\n        public function getOption(int $level, int $opt_name): mixed {}\n        public function setOption(int $level, int $opt_name, mixed $opt_value): bool {}\n        public function setProtocol(array $settings): bool {}\n        public function sslHandshake(): bool {}\n        public function shutdown(int $how = 2): bool {}\n        public function close(): bool {}\n        public function cancel(int $event = SWOOLE_EVENT_READ): bool {}\n        public function getsockname(): false|array {}\n        public function getpeername(): false|array {}\n        public function isClosed(): bool {}\n        /** @param resource $stream */\n        public static function import($stream) : Socket | false {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_socket_coro_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 7d2b3ea5b4d1613340006de2fa67d2a0bf314f09 */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Socket___construct, 0, 0, 2)\n\tZEND_ARG_TYPE_INFO(0, domain, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, protocol, IS_LONG, 0, \"IPPROTO_IP\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Socket_bind, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, address, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Socket_listen, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, backlog, IS_LONG, 0, \"512\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_accept, 0, 0, Swoole\\\\Coroutine\\\\Socket, MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Socket_connect, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Socket_checkLiveness, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Socket_getBoundCid, 0, 1, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, event, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_peek, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, \"65536\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_recv, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, \"65536\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_send, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_readVector, 0, 1, MAY_BE_FALSE|MAY_BE_ARRAY)\n\tZEND_ARG_TYPE_INFO(0, io_vector, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_Socket_readVectorAll arginfo_class_Swoole_Coroutine_Socket_readVector\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_writeVector, 0, 1, MAY_BE_FALSE|MAY_BE_LONG)\n\tZEND_ARG_TYPE_INFO(0, io_vector, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_Socket_writeVectorAll arginfo_class_Swoole_Coroutine_Socket_writeVector\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Socket_sendFile, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, file, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_recvAll, 0, 0, MAY_BE_FALSE|MAY_BE_STRING)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 0, \"65536\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_Socket_sendAll arginfo_class_Swoole_Coroutine_Socket_send\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_recvPacket, 0, 0, MAY_BE_FALSE|MAY_BE_STRING)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_Socket_recvLine arginfo_class_Swoole_Coroutine_Socket_recv\n\n#define arginfo_class_Swoole_Coroutine_Socket_recvWithBuffer arginfo_class_Swoole_Coroutine_Socket_recv\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_recvfrom, 0, 1, MAY_BE_STRING|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(1, peername, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_sendto, 0, 3, MAY_BE_LONG|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, addr, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Socket_getOption, 0, 2, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, level, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, opt_name, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Socket_setOption, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, level, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, opt_name, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, opt_value, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Socket_setProtocol, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_Socket_sslHandshake arginfo_class_Swoole_Coroutine_Socket_checkLiveness\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Socket_shutdown, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, how, IS_LONG, 0, \"2\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_Socket_close arginfo_class_Swoole_Coroutine_Socket_checkLiveness\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Socket_cancel, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, event, IS_LONG, 0, \"SWOOLE_EVENT_READ\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_getsockname, 0, 0, MAY_BE_FALSE|MAY_BE_ARRAY)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Coroutine_Socket_getpeername arginfo_class_Swoole_Coroutine_Socket_getsockname\n\n#define arginfo_class_Swoole_Coroutine_Socket_isClosed arginfo_class_Swoole_Coroutine_Socket_checkLiveness\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_import, 0, 1, Swoole\\\\Coroutine\\\\Socket, MAY_BE_FALSE)\n\tZEND_ARG_INFO(0, stream)\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_ssh2.stub.php",
    "content": "<?php\n/**\n * This file is part of Swoole.\n *\n * @link     https://www.swoole.com\n * @contact  team@swoole.com\n * @license  https://github.com/swoole/library/blob/master/LICENSE\n */\n\n/**\n * Connect to an SSH server\n * @param string $host The hostname or IP address to connect to\n * @param int $port [optional] The port number to connect on\n * @param array $methods [optional] Methods to use for the connection\n * @param array $callbacks [optional] Callbacks for the connection\n * @return resource|false Returns an SSH session resource on success, or false on failure\n */\nfunction ssh2_connect(string $host, int $port = 22, ?array $methods = null, ?array $callbacks = null) {}\n\n/**\n * Disconnect from an SSH server\n * @param resource $session The SSH session to disconnect from\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_disconnect($session): bool {}\n\n/**\n * Return list of negotiated methods\n * @param resource $session The SSH session to list methods for\n * @return array Returns an associative array of negotiated methods\n */\nfunction ssh2_methods_negotiated($session): array {}\n\n/**\n * Retrieve fingerprint of remote server\n * @param resource $session The SSH session\n * @param int $flags [optional] Flags for the fingerprint\n * @return string Returns the fingerprint as a string on success, or false on failure\n */\nfunction ssh2_fingerprint($session, int $flags = 0) {}\n\n/**\n * Authenticate as \"none\"\n * @param resource $session The SSH session\n * @param string $username The username to authenticate as\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_auth_none($session, string $username): bool {}\n\n/**\n * Authenticate over SSH using a password\n * @param resource $session The SSH session\n * @param string $username The username to authenticate as\n * @param string $password The password to use for authentication\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_auth_password($session, string $username, string $password): bool {}\n\n/**\n * Authenticate using a public key\n * @param resource $session The SSH session\n * @param string $username The username to authenticate as\n * @param string $pubkeyfile The path to the public key file\n * @param string $privkeyfile The path to the private key file\n * @param string $passphrase [optional] The passphrase for the private key\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_auth_pubkey_file($session, string $username, string $pubkeyfile, string $privkeyfile, ?string $passphrase = null): bool {}\n\n/**\n * Authenticate using a public key\n * @param resource $session The SSH session\n * @param string $username The username to authenticate as\n * @param string $pubkey The public key\n * @param string $privkey The private key\n * @param string $passphrase [optional] The passphrase for the private key\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_auth_pubkey($session, string $username, string $pubkey, string $privkey, ?string $passphrase = null): bool {}\n\n/**\n * Authenticate using a public hostkey\n * @param resource $session The SSH session\n * @param string $username The username to authenticate as\n * @param string $hostname The hostname\n * @param string $pubkeyfile The path to the public key file\n * @param string $privkeyfile The path to the private key file\n * @param string $passphrase [optional] The passphrase for the private key\n * @param string $local_username [optional] The local username\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_auth_hostbased_file($session, string $username, string $hostname, string $pubkeyfile, string $privkeyfile, ?string $passphrase = null, ?string $local_username = null): bool {}\n\n/**\n * Request SSH port forwarding\n * @param resource $session The SSH session\n * @param int $port The port to listen on\n * @param string $host [optional] The host to forward to\n * @param int $max_connections [optional] The maximum number of connections\n * @return resource|false Returns a listener resource on success, or false on failure\n */\nfunction ssh2_forward_listen($session, int $port, string $host = '127.0.0.1', int $max_connections = 1): resource|false {}\n\n/**\n * Accept a connection created by ssh2_forward_listen\n */\nfunction ssh2_forward_accept(resource $listener, ?string &$host = null, ?int &$port = null): resource|false {}\n\n/**\n * Request an interactive shell\n * @param resource $session The SSH session\n * @param string $termtype [optional] The terminal type\n * @param array $env [optional] Environment variables\n * @param int $width [optional] Width of the terminal\n * @param int $height [optional] Height of the terminal\n * @param int $width_height_type [optional] Type of width/height measurement\n * @return resource|false Returns a stream resource on success, or false on failure\n */\nfunction ssh2_shell($session, string $termtype = 'vanilla', ?array $env = null, int $width = 80, int $height = 25, int $width_height_type = 0) {}\n\n/**\n * Resize an interactive shell\n * @param resource $session The SSH session\n * @param int $width The new width\n * @param int $height The new height\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_shell_resize($session, int $width, int $height): bool {}\n\n/**\n * Execute a command on a remote server\n * @param resource $session The SSH session\n * @param string $command The command to execute\n * @param bool $pty [optional] Whether to allocate a pseudo-terminal\n * @param array $env [optional] Environment variables\n * @param int $width [optional] Width of the terminal\n * @param int $height [optional] Height of the terminal\n * @param int $width_height_type [optional] Type of width/height measurement\n * @return resource|false Returns a stream resource on success, or false on failure\n */\nfunction ssh2_exec($session, string $command, bool $pty = false, ?array $env = null, int $width = 80, int $height = 25, int $width_height_type = 0) {}\n\n/**\n * Open a tunnel through a remote server\n * @param resource $session The SSH session\n * @param string $host The host to connect to\n * @param int $port The port to connect to\n * @return resource|false Returns a stream resource on success, or false on failure\n */\nfunction ssh2_tunnel($session, string $host, int $port) {}\n\n/**\n * Request a file via SCP\n * @param resource $session The SSH session\n * @param string $remote_file The remote file path\n * @param string $local_file The local file path\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_scp_recv($session, string $remote_file, string $local_file): bool {}\n\n/**\n * Send a file via SCP\n * @param resource $session The SSH session\n * @param string $local_file The local file path\n * @param string $remote_file The remote file path\n * @param int $create_mode [optional] The file creation mode\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_scp_send($session, string $local_file, string $remote_file, int $create_mode = 0644): bool {}\n\n/**\n * Fetch an extended data stream\n * @param resource $channel The channel resource\n * @param int $streamid The stream ID\n * @return resource|false Returns a stream resource on success, or false on failure\n */\nfunction ssh2_fetch_stream($channel, int $streamid) {}\n\n/**\n * Send EOF to a channel\n * @param resource $channel The channel resource\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_send_eof($channel): bool {}\n\n/**\n * Initialize SFTP subsystem\n * @param resource $session The SSH session\n * @return resource|false Returns an SSH2 SFTP resource on success, or false on failure\n */\nfunction ssh2_sftp($session) {}\n\n/**\n * Rename a remote file\n * @param resource $sftp The SSH2 SFTP resource\n * @param string $from The source file path\n * @param string $to The destination file path\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_sftp_rename($sftp, string $from, string $to): bool {}\n\n/**\n * Delete a file\n * @param resource $sftp The SSH2 SFTP resource\n * @param string $filename The file path to delete\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_sftp_unlink($sftp, string $filename): bool {}\n\n/**\n * Create a directory\n * @param resource $sftp The SSH2 SFTP resource\n * @param string $dirname The directory path to create\n * @param int $mode [optional] The directory permissions\n * @param bool $recursive [optional] Whether to create directories recursively\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_sftp_mkdir($sftp, string $dirname, int $mode = 0777, bool $recursive = false): bool {}\n\n/**\n * Remove a directory\n * @param resource $sftp The SSH2 SFTP resource\n * @param string $dirname The directory path to remove\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_sftp_rmdir($sftp, string $dirname): bool {}\n\n/**\n * Changes file mode\n * @param resource $sftp The SSH2 SFTP resource\n * @param string $filename The file path\n * @param int $mode The new file mode\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_sftp_chmod($sftp, string $filename, int $mode): bool {}\n\n/**\n * Stat a file on a remote filesystem\n * @param resource $sftp The SSH2 SFTP resource\n * @param string $path The file path to stat\n * @return array|false Returns an array of file stats on success, or false on failure\n */\nfunction ssh2_sftp_stat($sftp, string $path) {}\n\n/**\n * Stat a symbolic link on the remote filesystem\n * @param resource $sftp The SSH2 SFTP resource\n * @param string $path The link path to stat\n * @return array|false Returns an array of file stats on success, or false on failure\n */\nfunction ssh2_sftp_lstat($sftp, string $path) {}\n\n/**\n * Create a symlink\n * @param resource $sftp The SSH2 SFTP resource\n * @param string $target The target path\n * @param string $link The link path\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_sftp_symlink($sftp, string $target, string $link): bool {}\n\n/**\n * Read the target of a symlink\n * @param resource $sftp The SSH2 SFTP resource\n * @param string $link The link path\n * @return string|false Returns the target path on success, or false on failure\n */\nfunction ssh2_sftp_readlink($sftp, string $link) {}\n\n/**\n * Resolve the realpath of a filename\n * @param resource $sftp The SSH2 SFTP resource\n * @param string $filename The filename to resolve\n * @return string|false Returns the real path on success, or false on failure\n */\nfunction ssh2_sftp_realpath($sftp, string $filename) {}\n\n/**\n * Initialize Publickey subsystem\n * @param resource $session The SSH session\n * @return resource|false Returns an SSH2 Publickey resource on success, or false on failure\n */\nfunction ssh2_publickey_init($session) {}\n\n/**\n * Add an authorized publickey\n * @param resource $pkey The SSH2 Publickey resource\n * @param string $algoname The algorithm name\n * @param string $blob The public key blob\n * @param bool $overwrite [optional] Whether to overwrite existing key\n * @param array $attributes [optional] Attributes for the key\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_publickey_add($pkey, string $algoname, string $blob, bool $overwrite = false, ?array $attributes = null): bool {}\n\n/**\n * Remove an authorized publickey\n * @param resource $pkey The SSH2 Publickey resource\n * @param string $algoname The algorithm name\n * @param string $blob The public key blob\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_publickey_remove($pkey, string $algoname, string $blob): bool {}\n\n/**\n * List currently authorized publickeys\n * @param resource $pkey The SSH2 Publickey resource\n * @return array|false Returns an array of public keys on success, or false on failure\n */\nfunction ssh2_publickey_list($pkey) {}\n\n/**\n * Authenticate using ssh-agent\n * @param resource $session The SSH session\n * @param string $username The username to authenticate as\n * @return bool Returns true on success or false on failure\n */\nfunction ssh2_auth_agent($session, string $username): bool {}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_ssh2_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: dfaf05276c29a8606b576793221837fe7170806d */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_connect, 0, 0, 1)\n\tZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, \"22\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, methods, IS_ARRAY, 1, \"null\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, callbacks, IS_ARRAY, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_disconnect, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, session)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_methods_negotiated, 0, 1, IS_ARRAY, 0)\n\tZEND_ARG_INFO(0, session)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_fingerprint, 0, 0, 1)\n\tZEND_ARG_INFO(0, session)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_auth_none, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, session)\n\tZEND_ARG_TYPE_INFO(0, username, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_auth_password, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, session)\n\tZEND_ARG_TYPE_INFO(0, username, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, password, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_auth_pubkey_file, 0, 4, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, session)\n\tZEND_ARG_TYPE_INFO(0, username, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, pubkeyfile, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, privkeyfile, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, passphrase, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_auth_pubkey, 0, 4, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, session)\n\tZEND_ARG_TYPE_INFO(0, username, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, pubkey, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, privkey, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, passphrase, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_auth_hostbased_file, 0, 5, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, session)\n\tZEND_ARG_TYPE_INFO(0, username, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, hostname, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, pubkeyfile, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, privkeyfile, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, passphrase, IS_STRING, 1, \"null\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, local_username, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_ssh2_forward_listen, 0, 2, resource, MAY_BE_FALSE)\n\tZEND_ARG_INFO(0, session)\n\tZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, \"\\'127.0.0.1\\'\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, max_connections, IS_LONG, 0, \"1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_ssh2_forward_accept, 0, 1, resource, MAY_BE_FALSE)\n\tZEND_ARG_OBJ_INFO(0, listener, resource, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(1, host, IS_STRING, 1, \"null\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(1, port, IS_LONG, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_shell, 0, 0, 1)\n\tZEND_ARG_INFO(0, session)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, termtype, IS_STRING, 0, \"\\'vanilla\\'\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, env, IS_ARRAY, 1, \"null\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, width, IS_LONG, 0, \"80\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, height, IS_LONG, 0, \"25\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, width_height_type, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_shell_resize, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, session)\n\tZEND_ARG_TYPE_INFO(0, width, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, height, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_exec, 0, 0, 2)\n\tZEND_ARG_INFO(0, session)\n\tZEND_ARG_TYPE_INFO(0, command, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pty, _IS_BOOL, 0, \"false\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, env, IS_ARRAY, 1, \"null\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, width, IS_LONG, 0, \"80\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, height, IS_LONG, 0, \"25\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, width_height_type, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_tunnel, 0, 0, 3)\n\tZEND_ARG_INFO(0, session)\n\tZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_scp_recv, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, session)\n\tZEND_ARG_TYPE_INFO(0, remote_file, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, local_file, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_scp_send, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, session)\n\tZEND_ARG_TYPE_INFO(0, local_file, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, remote_file, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, create_mode, IS_LONG, 0, \"0644\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_fetch_stream, 0, 0, 2)\n\tZEND_ARG_INFO(0, channel)\n\tZEND_ARG_TYPE_INFO(0, streamid, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_send_eof, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, channel)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_sftp, 0, 0, 1)\n\tZEND_ARG_INFO(0, session)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_sftp_rename, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, sftp)\n\tZEND_ARG_TYPE_INFO(0, from, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, to, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_sftp_unlink, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, sftp)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_sftp_mkdir, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, sftp)\n\tZEND_ARG_TYPE_INFO(0, dirname, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, \"0777\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, recursive, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_sftp_rmdir, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, sftp)\n\tZEND_ARG_TYPE_INFO(0, dirname, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_sftp_chmod, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, sftp)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, mode, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_sftp_stat, 0, 0, 2)\n\tZEND_ARG_INFO(0, sftp)\n\tZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_ssh2_sftp_lstat arginfo_ssh2_sftp_stat\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_sftp_symlink, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, sftp)\n\tZEND_ARG_TYPE_INFO(0, target, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, link, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_sftp_readlink, 0, 0, 2)\n\tZEND_ARG_INFO(0, sftp)\n\tZEND_ARG_TYPE_INFO(0, link, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_sftp_realpath, 0, 0, 2)\n\tZEND_ARG_INFO(0, sftp)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_ssh2_publickey_init arginfo_ssh2_sftp\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_publickey_add, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, pkey)\n\tZEND_ARG_TYPE_INFO(0, algoname, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, blob, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, overwrite, _IS_BOOL, 0, \"false\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, attributes, IS_ARRAY, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ssh2_publickey_remove, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_INFO(0, pkey)\n\tZEND_ARG_TYPE_INFO(0, algoname, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, blob, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_publickey_list, 0, 0, 1)\n\tZEND_ARG_INFO(0, pkey)\nZEND_END_ARG_INFO()\n\n#define arginfo_ssh2_auth_agent arginfo_ssh2_auth_none\n\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_stdext.stub.php",
    "content": "<?php\n/**\n * This file is part of Swoole.\n *\n * @link     https://www.swoole.com\n * @contact  team@swoole.com\n * @license  https://github.com/swoole/library/blob/master/LICENSE\n */\n\nfunction swoole_call_array_method(mixed ...$args): mixed {}\n\nfunction swoole_call_string_method(mixed ...$args): mixed {}\n\nfunction swoole_call_stream_method(mixed ...$args): mixed {}\n\nfunction swoole_array_search(array $array, mixed $value, bool $strict = false): false|int|string {}\n\nfunction swoole_array_contains(array $array, mixed $needle, bool $strict = false): bool {}\n\nfunction swoole_array_join(array $array, string $separator): string {}\n\nfunction swoole_array_key_exists(array $array, null|bool|float|int|resource|string $key): bool {}\n\nfunction swoole_array_map(array $array, ?callable $callback, array ...$arrays): array {}\n\nfunction swoole_array_is_typed(array $array, string $typeDef = ''): bool {}\n\nfunction swoole_array_is_empty(array $array): bool {}\n\nfunction swoole_str_split(string $string, string $delimiter, int $limit = PHP_INT_MAX): array {}\n\nfunction swoole_str_is_empty(string $string): bool {}\n\nfunction swoole_str_match(string $string, string $pattern, int $flags = 0, int $offset = 0): array|false {}\n\nfunction swoole_str_match_all(string $string, string $pattern, int $flags = 0, int $offset = 0): array|false {}\n\nfunction swoole_str_json_decode(string $string, int $depth = 512, int $flags = 0): mixed {};\n\nfunction swoole_str_json_decode_to_object(string $string, int $depth = 512, int $flags = 0): mixed {};\n\nfunction swoole_parse_str(string $string): array {}\n\nfunction swoole_hash(string $data, string $algo, bool $binary = false, array $options = []): string {}\n\nfunction swoole_typed_array(string $typeDef, ?array $initArray = null): array {}\n\nfunction swoole_str_replace(string $subject, array|string $search, array|string $replace): string {}\n\nfunction swoole_str_ireplace(string $subject, array|string $search, array|string $replace): string {}\n\nfunction swoole_array_replace_str(array $subjects, array|string $search, array|string $replace): array {}\n\nfunction swoole_array_ireplace_str(array $subjects, array|string $search, array|string $replace): array {}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_stdext_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: df7f4e3ef25ccb8ecec92790de722a5da01e09b9 */\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_call_array_method, 0, 0, IS_MIXED, 0)\n\tZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_call_string_method arginfo_swoole_call_array_method\n\n#define arginfo_swoole_call_stream_method arginfo_swoole_call_array_method\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_array_search, 0, 2, MAY_BE_FALSE|MAY_BE_LONG|MAY_BE_STRING)\n\tZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, strict, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_array_contains, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO(0, needle, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, strict, _IS_BOOL, 0, \"false\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_array_join, 0, 2, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO(0, separator, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_array_key_exists, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0)\n\tZEND_ARG_OBJ_TYPE_MASK(0, key, resource, MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_DOUBLE|MAY_BE_LONG|MAY_BE_STRING, NULL)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_array_map, 0, 2, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 1)\n\tZEND_ARG_VARIADIC_TYPE_INFO(0, arrays, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_array_is_typed, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, typeDef, IS_STRING, 0, \"\\'\\'\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_array_is_empty, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_str_split, 0, 2, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, delimiter, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, \"PHP_INT_MAX\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_str_is_empty, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_str_match, 0, 2, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, \"0\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_str_match_all arginfo_swoole_str_match\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_str_json_decode, 0, 1, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, depth, IS_LONG, 0, \"512\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_str_json_decode_to_object arginfo_swoole_str_json_decode\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_parse_str, 0, 1, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_hash, 0, 2, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, algo, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, binary, _IS_BOOL, 0, \"false\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, \"[]\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_typed_array, 0, 1, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO(0, typeDef, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, initArray, IS_ARRAY, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_str_replace, 0, 3, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, subject, IS_STRING, 0)\n\tZEND_ARG_TYPE_MASK(0, search, MAY_BE_ARRAY|MAY_BE_STRING, NULL)\n\tZEND_ARG_TYPE_MASK(0, replace, MAY_BE_ARRAY|MAY_BE_STRING, NULL)\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_str_ireplace arginfo_swoole_str_replace\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_array_replace_str, 0, 3, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO(0, subjects, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_MASK(0, search, MAY_BE_ARRAY|MAY_BE_STRING, NULL)\n\tZEND_ARG_TYPE_MASK(0, replace, MAY_BE_ARRAY|MAY_BE_STRING, NULL)\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_array_ireplace_str arginfo_swoole_array_replace_str\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_table.stub.php",
    "content": "<?php\nnamespace Swoole {\n     final class Table {\n        public function __construct(int $table_size, float $conflict_proportion = 0.2) {}\n        public function column(string $name, int $type, int $size = 0): bool {}\n        public function create(): bool {}\n        public function set(string $key, array $value): bool {}\n        public function get(string $key, ?string $field = null): array|false|string|float|int {}\n        public function del(string $key): bool {}\n        public function exists(string $key): bool {}\n        public function incr(string $key, string $column, int|float $incrby = 1): float|int {}\n        public function decr(string $key, string $column, int|float $incrby = 1): float|int {}\n        public function count(): int {}\n        public function destroy(): bool {}\n        public function getSize(): int {}\n        public function getMemorySize(): int {}\n        public function stats(): false|array {}\n        public function rewind(): void {}\n        public function next(): void {}\n        public function current(): mixed {}\n        public function key(): mixed {}\n        public function valid(): bool {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_table_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 89c46050b9892b3a31a9a21fe2e7d02e2eb975bb */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Table___construct, 0, 0, 1)\n\tZEND_ARG_TYPE_INFO(0, table_size, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, conflict_proportion, IS_DOUBLE, 0, \"0.2\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Table_column, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, size, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Table_create, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Table_set, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, value, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Table_get, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE|MAY_BE_STRING|MAY_BE_DOUBLE|MAY_BE_LONG)\n\tZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, field, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Table_del, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Table_exists arginfo_class_Swoole_Table_del\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Table_incr, 0, 2, MAY_BE_DOUBLE|MAY_BE_LONG)\n\tZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, column, IS_STRING, 0)\n\tZEND_ARG_TYPE_MASK(0, incrby, MAY_BE_LONG|MAY_BE_DOUBLE, \"1\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Table_decr arginfo_class_Swoole_Table_incr\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Table_count, 0, 0, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Table_destroy arginfo_class_Swoole_Table_create\n\n#define arginfo_class_Swoole_Table_getSize arginfo_class_Swoole_Table_count\n\n#define arginfo_class_Swoole_Table_getMemorySize arginfo_class_Swoole_Table_count\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Table_stats, 0, 0, MAY_BE_FALSE|MAY_BE_ARRAY)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Table_rewind, 0, 0, IS_VOID, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Table_next arginfo_class_Swoole_Table_rewind\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Table_current, 0, 0, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Table_key arginfo_class_Swoole_Table_current\n\n#define arginfo_class_Swoole_Table_valid arginfo_class_Swoole_Table_create\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_thread.stub.php",
    "content": "<?php\nnamespace Swoole {\n    class Thread {\n        public int $id;\n        public function __construct(string $script_file, mixed ...$args) {}\n\n        public function join(): bool {}\n        public function joinable(): bool {}\n        public function getExitStatus(): int {}\n        public function isAlive(): bool {}\n        public function detach(): bool {}\n\n        public static function getArguments(): ?array {}\n        public static function getId(): int {}\n        public static function getInfo(): array {}\n        public static function activeCount(): int {}\n        public static function yield(): void {}\n\n        public static function setName(string $name): bool {}\n        #ifdef HAVE_CPU_AFFINITY\n        public static function setAffinity(array $cpu_settings): bool {}\n        public static function getAffinity(): array {}\n        #endif\n        public static function setPriority(int $priority, int $policy = 0): bool {}\n        public static function getPriority(): array {}\n        public static function getNativeId(): int {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_thread_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 46ec6a19af2d6a669e9c74651c12805626f832c8 */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread___construct, 0, 0, 1)\n\tZEND_ARG_TYPE_INFO(0, script_file, IS_STRING, 0)\n\tZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_join, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Thread_joinable arginfo_class_Swoole_Thread_join\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getExitStatus, 0, 0, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Thread_isAlive arginfo_class_Swoole_Thread_join\n\n#define arginfo_class_Swoole_Thread_detach arginfo_class_Swoole_Thread_join\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getArguments, 0, 0, IS_ARRAY, 1)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Thread_getId arginfo_class_Swoole_Thread_getExitStatus\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getInfo, 0, 0, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Thread_activeCount arginfo_class_Swoole_Thread_getExitStatus\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_yield, 0, 0, IS_VOID, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_setName, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\n#if defined(HAVE_CPU_AFFINITY)\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_setAffinity, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, cpu_settings, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n#endif\n\n#if defined(HAVE_CPU_AFFINITY)\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getAffinity, 0, 0, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n#endif\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_setPriority, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, priority, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, policy, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Thread_getPriority arginfo_class_Swoole_Thread_getInfo\n\n#define arginfo_class_Swoole_Thread_getNativeId arginfo_class_Swoole_Thread_getExitStatus\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_thread_arraylist.stub.php",
    "content": "<?php\nnamespace Swoole\\Thread {\n    class ArrayList implements ArrayAccess, Countable {\n        public function __construct(?array $array = null) {}\n        public function offsetGet(mixed $key): mixed {}\n        public function offsetExists(mixed $key): bool {}\n        public function offsetSet(mixed $key, mixed $value): void {}\n        public function offsetUnset(mixed $key): void {}\n        public function find(mixed $value): int {}\n        public function count(): int {}\n        public function incr(mixed $key, mixed $value = 1): mixed {}\n        public function decr(mixed $key, mixed $value = 1): mixed {}\n        public function clean(): void {}\n        public function toArray(): array {}\n        public function sort(): void {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_thread_arraylist_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: f9c390449be28ec68af8381a013572dde33448c8 */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread_ArrayList___construct, 0, 0, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, array, IS_ARRAY, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_ArrayList_offsetGet, 0, 1, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_ArrayList_offsetExists, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_ArrayList_offsetSet, 0, 2, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_ArrayList_offsetUnset, 0, 1, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_ArrayList_find, 0, 1, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_ArrayList_count, 0, 0, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_ArrayList_incr, 0, 1, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_MIXED, 0, \"1\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Thread_ArrayList_decr arginfo_class_Swoole_Thread_ArrayList_incr\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_ArrayList_clean, 0, 0, IS_VOID, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_ArrayList_toArray, 0, 0, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Thread_ArrayList_sort arginfo_class_Swoole_Thread_ArrayList_clean\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_thread_atomic.stub.php",
    "content": "<?php\nnamespace Swoole\\Thread {\n    class Atomic {\n        public function __construct(int $value = 0) {}\n        public function add(int $add_value = 1): int {}\n        public function sub(int $sub_value = 1): int {}\n        public function get(): int {}\n        public function set(int $value): void {}\n        public function cmpset(int $cmp_value, int $new_value): bool {}\n        public function wait(float $timeout = 1.0): bool {}\n        public function wakeup(int $count = 1): bool {}\n    }\n}\n\nnamespace Swoole\\Thread\\Atomic {\n    class Long {\n        public function __construct(int $value = 0) {}\n        public function add(int $add_value = 1): int {}\n        public function sub(int $sub_value = 1): int {}\n        public function get(): int {}\n        public function set(int $value): void {}\n        public function cmpset(int $cmp_value, int $new_value): bool {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_thread_atomic_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 4b6cbceb50641f6204da9caed8c514a526c57ee8 */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread_Atomic___construct, 0, 0, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Atomic_add, 0, 0, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, add_value, IS_LONG, 0, \"1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Atomic_sub, 0, 0, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, sub_value, IS_LONG, 0, \"1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Atomic_get, 0, 0, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Atomic_set, 0, 1, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Atomic_cmpset, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, cmp_value, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, new_value, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Atomic_wait, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"1.0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Atomic_wakeup, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, \"1\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Thread_Atomic_Long___construct arginfo_class_Swoole_Thread_Atomic___construct\n\n#define arginfo_class_Swoole_Thread_Atomic_Long_add arginfo_class_Swoole_Thread_Atomic_add\n\n#define arginfo_class_Swoole_Thread_Atomic_Long_sub arginfo_class_Swoole_Thread_Atomic_sub\n\n#define arginfo_class_Swoole_Thread_Atomic_Long_get arginfo_class_Swoole_Thread_Atomic_get\n\n#define arginfo_class_Swoole_Thread_Atomic_Long_set arginfo_class_Swoole_Thread_Atomic_set\n\n#define arginfo_class_Swoole_Thread_Atomic_Long_cmpset arginfo_class_Swoole_Thread_Atomic_cmpset\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_thread_barrier.stub.php",
    "content": "<?php\nnamespace Swoole\\Thread {\n    class Barrier {\n        public function __construct(int $count) {}\n        public function wait(): void {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_thread_barrier_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 1fe9f55b0b9487e9cd469dcd215acee893254a04 */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread_Barrier___construct, 0, 0, 1)\n\tZEND_ARG_TYPE_INFO(0, count, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Barrier_wait, 0, 0, IS_VOID, 0)\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_thread_lock.stub.php",
    "content": "<?php\nnamespace Swoole\\Thread {\n    class Lock {\n        public function __construct(int $type = SWOOLE_MUTEX) {}\n        public function lock(int $operation = LOCK_EX, float $timeout = -1): bool {}\n        public function unlock(): bool {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_thread_lock_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 3d97d9a6192d9e86fc94eda474145f2cdbb0760e */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread_Lock___construct, 0, 0, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_LONG, 0, \"SWOOLE_MUTEX\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Lock_lock, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, operation, IS_LONG, 0, \"LOCK_EX\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"-1\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Lock_unlock, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_thread_map.stub.php",
    "content": "<?php\nnamespace Swoole\\Thread {\n    class Map implements ArrayAccess, Countable {\n        public function __construct(?array $array = null) {}\n        public function offsetGet(mixed $key): mixed {}\n        public function offsetExists(mixed $key): bool {}\n        public function offsetSet(mixed $key, mixed $value): void {}\n        public function offsetUnset(mixed $key): void {}\n        public function find(mixed $value): mixed {}\n        public function count(): int {}\n        public function keys(): array {}\n        public function values(): array {}\n        public function incr(mixed $key, mixed $value = 1): mixed {}\n        public function decr(mixed $key, mixed $value = 1): mixed {}\n        public function add(mixed $key, mixed $value): bool {}\n        public function update(mixed $key, mixed $value): bool {}\n        public function clean(): void {}\n        public function toArray(): array {}\n        public function sort(): void {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_thread_map_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 4bc51546a707aba5df4b09a506e67dcc90b30d9f */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread_Map___construct, 0, 0, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, array, IS_ARRAY, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_offsetGet, 0, 1, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_offsetExists, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_offsetSet, 0, 2, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_offsetUnset, 0, 1, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_find, 0, 1, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_count, 0, 0, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_keys, 0, 0, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Thread_Map_values arginfo_class_Swoole_Thread_Map_keys\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_incr, 0, 1, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_MIXED, 0, \"1\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Thread_Map_decr arginfo_class_Swoole_Thread_Map_incr\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_add, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Thread_Map_update arginfo_class_Swoole_Thread_Map_add\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_clean, 0, 0, IS_VOID, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_Thread_Map_toArray arginfo_class_Swoole_Thread_Map_keys\n\n#define arginfo_class_Swoole_Thread_Map_sort arginfo_class_Swoole_Thread_Map_clean\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_thread_queue.stub.php",
    "content": "<?php\nnamespace Swoole\\Thread {\n    class Queue implements Countable {\n        public function __construct() {}\n        public function push(mixed $value, int $notify_which = 0): void {}\n        public function pop(float $wait = 0): mixed {}\n        public function count(): int {}\n        public function clean(): void {}\n    }\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_thread_queue_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 96a3f2b7103f36b83a3494d8b2f708a07a332b86 */\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread_Queue___construct, 0, 0, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Queue_push, 0, 1, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, notify_which, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Queue_pop, 0, 0, IS_MIXED, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, wait, IS_DOUBLE, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Queue_count, 0, 0, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Queue_clean, 0, 0, IS_VOID, 0)\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_timer.stub.php",
    "content": "<?php\n\nfunction swoole_timer_tick(int $ms, callable $callback, mixed ...$params): false|int {}\n\nfunction swoole_timer_after(int $ms, callable $callback, mixed ...$params): false|int {}\n\nfunction swoole_timer_exists(int $timer_id): bool {}\n\nfunction swoole_timer_info(int $timer_id): ?array {}\n\nfunction swoole_timer_stats(): array {}\n\nfunction swoole_timer_list(): Swoole\\Timer\\Iterator {}\n\nfunction swoole_timer_clear(int $timer_id): bool {}\n\nfunction swoole_timer_clear_all() : bool {}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_timer_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 5d7c9f61ee27a861cf41b5dd8044613118772672 */\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_timer_tick, 0, 2, MAY_BE_FALSE|MAY_BE_LONG)\n\tZEND_ARG_TYPE_INFO(0, ms, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)\n\tZEND_ARG_VARIADIC_TYPE_INFO(0, params, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_timer_after arginfo_swoole_timer_tick\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_timer_exists, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, timer_id, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_timer_info, 0, 1, IS_ARRAY, 1)\n\tZEND_ARG_TYPE_INFO(0, timer_id, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_timer_stats, 0, 0, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_swoole_timer_list, 0, 0, Swoole\\\\Timer\\\\Iterator, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_timer_clear arginfo_swoole_timer_exists\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_timer_clear_all, 0, 0, _IS_BOOL, 0)\nZEND_END_ARG_INFO()\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_tracer.stub.php",
    "content": "<?php\n\nfunction swoole_tracer_leak_detect(int $threshold = 64): void {}\nfunction swoole_tracer_prof_begin(?array $options = null): bool {}\nfunction swoole_tracer_prof_end(string $output_file): bool {}"
  },
  {
    "path": "ext-src/stubs/php_swoole_tracer_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 32c353cda2e4a56be623c82899190c3630d2550b */\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_tracer_leak_detect, 0, 0, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, threshold, IS_LONG, 0, \"64\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_tracer_prof_begin, 0, 0, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_tracer_prof_end, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, output_file, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_websocket.stub.php",
    "content": "<?php\nnamespace Swoole\\WebSocket {\n\tclass Server {\n\t\tpublic function push(int $fd, \\Swoole\\WebSocket\\Frame|string $data, int $opcode = WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): bool {}\n\t\tpublic function isEstablished(int $fd): bool {}\n\t\tpublic static function pack(\\Swoole\\WebSocket\\Frame|string $data, int $opcode = WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): string {}\n        public static function unpack(string $data): \\Swoole\\WebSocket\\Frame {}\n\t\tpublic function disconnect(int $fd, int $code = SWOOLE_WEBSOCKET_CLOSE_NORMAL, string $reason = \"\"): bool {}\n\t\tpublic function ping(int $fd, string $data = \"\"): bool {}\n\t}\n\n\tclass Frame {\n\t\tpublic function __toString(): string {}\n\t\tpublic static function pack(\\Swoole\\WebSocket\\Frame|string $data, int $opcode = WEBSOCKET_OPCODE_TEXT, int $flags = SWOOLE_WEBSOCKET_FLAG_FIN): string {}\n        public static function unpack(string $data): \\Swoole\\WebSocket\\Frame {}\n\t}\n}\n"
  },
  {
    "path": "ext-src/stubs/php_swoole_websocket_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: d7b75f2efbda42013e5b8a83aa0dab8ab761e127 */\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_WebSocket_Server_push, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_LONG, 0)\n\tZEND_ARG_OBJ_TYPE_MASK(0, data, Swoole\\\\WebSocket\\\\Frame, MAY_BE_STRING, NULL)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opcode, IS_LONG, 0, \"WEBSOCKET_OPCODE_TEXT\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, \"SWOOLE_WEBSOCKET_FLAG_FIN\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_WebSocket_Server_isEstablished, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_WebSocket_Server_pack, 0, 1, IS_STRING, 0)\n\tZEND_ARG_OBJ_TYPE_MASK(0, data, Swoole\\\\WebSocket\\\\Frame, MAY_BE_STRING, NULL)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opcode, IS_LONG, 0, \"WEBSOCKET_OPCODE_TEXT\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, \"SWOOLE_WEBSOCKET_FLAG_FIN\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_WebSocket_Server_unpack, 0, 1, Swoole\\\\WebSocket\\\\Frame, 0)\n\tZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_WebSocket_Server_disconnect, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, code, IS_LONG, 0, \"SWOOLE_WEBSOCKET_CLOSE_NORMAL\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, reason, IS_STRING, 0, \"\\\"\\\"\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_WebSocket_Server_ping, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, fd, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, data, IS_STRING, 0, \"\\\"\\\"\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_WebSocket_Frame___toString, 0, 0, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Swoole_WebSocket_Frame_pack arginfo_class_Swoole_WebSocket_Server_pack\n\n#define arginfo_class_Swoole_WebSocket_Frame_unpack arginfo_class_Swoole_WebSocket_Server_unpack\n\n"
  },
  {
    "path": "ext-src/swoole_admin_server.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_server.h\"\n#include \"swoole_process_pool.h\"\n#include \"php_swoole_http.h\"\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_ex_arginfo.h\"\nEND_EXTERN_C()\n\n#include <sstream>\n#include <thread>\n\n#include \"nlohmann/json.hpp\"\n\nusing json = nlohmann::json;\n\nnamespace swoole {\n\n#ifdef TCP_INFO\nstatic json get_socket_info(int fd);\n#endif\n\nstatic std::string handle_get_all_unix_sockets(Server *_server, const std::string &msg) {\n    auto _result = json::parse(msg);\n    if (!_result.is_object() || _result.find(\"type\") == _result.end()) {\n        json return_value{\n            {\"data\", \"require parameter type\"},\n            {\"code\", 4003},\n        };\n        return return_value.dump();\n    }\n\n    std::string _type = _result[\"type\"];\n    Worker *workers;\n    uint32_t worker_num;\n\n    if (_type == \"event\") {\n        workers = _server->get_event_worker_pool()->workers;\n        worker_num = _server->worker_num;\n    } else {\n        workers = _server->get_task_worker_pool()->workers;\n        worker_num = _server->task_worker_num;\n    }\n\n    json sockets = json::array();\n\n    SW_LOOP_N(worker_num) {\n        auto master_socket = workers[i].pipe_object->get_socket(true);\n        json master_socket_info = json::object({\n            {\"fd\", master_socket->fd},\n            {\"events\", master_socket->events},\n            {\"total_recv_bytes\", master_socket->total_recv_bytes},\n            {\"total_send_bytes\", master_socket->total_send_bytes},\n            {\"out_buffer_size\", master_socket->get_out_buffer_length()},\n        });\n        sockets.push_back(master_socket_info);\n\n        auto worker_socket = workers[i].pipe_object->get_socket(false);\n        json worker_socket_info = json::object({\n            {\"fd\", worker_socket->fd},\n            {\"events\", worker_socket->events},\n            {\"total_recv_bytes\", worker_socket->total_recv_bytes},\n            {\"total_send_bytes\", worker_socket->total_send_bytes},\n            {\"out_buffer_size\", worker_socket->get_out_buffer_length()},\n        });\n        sockets.push_back(worker_socket_info);\n    }\n\n    json return_value{\n        {\"data\", sockets},\n        {\"code\", 0},\n    };\n    return return_value.dump();\n}\n\nstatic std::string handle_get_all_sockets(Server *, const std::string &msg) {\n    if (sw_reactor() == nullptr) {\n        json return_value{\n            {\"data\", \"No event loop created\"},\n            {\"code\", 4004},\n        };\n        return return_value.dump();\n    }\n\n    json sockets = json::array();\n    sw_reactor()->foreach_socket([&sockets](int fd, network::Socket *socket) {\n        if (socket->socket_type > SW_SOCK_UNIX_DGRAM || socket->socket_type < SW_SOCK_TCP) {\n#ifdef SO_DOMAIN\n            struct stat fdstat;\n            if (fstat(fd, &fdstat) == -1) {\n                return;\n            }\n            mode_t type = fdstat.st_mode & S_IFMT;\n            if (type == S_IFSOCK) {\n                int domain;\n                if (socket->get_option(SOL_SOCKET, SO_DOMAIN, &domain) < 0) {\n                    return;\n                }\n                int _type;\n                if (socket->get_option(SOL_SOCKET, SO_TYPE, &_type) < 0) {\n                    return;\n                }\n                socket->get_name();\n            }\n#else\n            return;\n#endif\n        }\n        json info = json::object({\n            {\"fd\", socket->get_fd()},\n            {\"address\", socket->get_addr()},\n            {\"port\", socket->get_port()},\n            {\"events\", socket->events},\n            {\"socket_type\", socket->socket_type},\n            {\"fd_type\", socket->fd_type},\n            {\"total_recv_bytes\", socket->total_recv_bytes},\n            {\"total_send_bytes\", socket->total_send_bytes},\n            {\"out_buffer_size\", socket->get_out_buffer_length()},\n        });\n        sockets.push_back(info);\n    });\n\n    json return_value{\n        {\"data\", sockets},\n        {\"code\", 0},\n    };\n    return return_value.dump();\n}\n\nstatic std::string handle_get_all_commands(Server *serv, const std::string &msg) {\n    json command_list = json::array();\n    for (auto &kv : serv->commands) {\n        json info = json::object({\n            {\"id\", kv.second.id},\n            {\"name\", kv.second.name},\n            {\"accepted_process_types\", kv.second.accepted_process_types},\n        });\n        command_list.push_back(info);\n    };\n    json return_value{\n        {\"data\", command_list},\n        {\"code\", 0},\n    };\n    return return_value.dump();\n}\n\n#ifdef TCP_INFO\nstatic json get_socket_info(int fd) {\n    tcp_info info;\n    socklen_t len = sizeof(info);\n    if (getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, &len) < 0) {\n        json return_value{\n            {\"data\", \"failed to getsockopt(TCP_INFO) for socket\"},\n            {\"code\", 5001},\n        };\n        return return_value.dump();\n    }\n\n    auto info_map = sw_socket_parse_tcp_info(&info);\n    json jinfo;\n    for (const auto &iter : info_map) {\n        jinfo[iter.first] = iter.second;\n    }\n    return jinfo;\n}\n#endif\n\nstatic json get_connection_info(Server *serv, Connection *conn) {\n    auto server_socket = serv->get_port_by_server_fd(conn->server_fd)->socket;\n    json info = json::object({\n        {\"session_id\", conn->session_id},\n        {\"reactor_id\", conn->reactor_id},\n        {\"fd\", conn->fd},\n        {\"server_port\",\n         std::string(server_socket->info.get_addr()) + \":\" + std::to_string(server_socket->info.get_port())},\n        {\"address\", conn->info.get_addr()},\n        {\"port\", conn->info.get_port()},\n        {\"overflow\", conn->overflow},\n        {\"connect_time\", conn->connect_time},\n        {\"last_recv_time\", conn->last_recv_time},\n        {\"last_send_time\", conn->last_send_time},\n        {\"last_dispatch_time\", conn->last_dispatch_time},\n        {\"recv_queued_bytes\", conn->recv_queued_bytes},\n        {\"send_queued_bytes\", conn->send_queued_bytes},\n        {\"total_recv_bytes\", conn->socket->total_recv_bytes},\n        {\"total_send_bytes\", conn->socket->total_send_bytes},\n        {\"uid\", conn->uid},\n    });\n    return info;\n}\n\nstatic std::string handle_get_socket_info(Server *serv, const std::string &msg) {\n    auto _result = json::parse(msg);\n    if (!_result.is_object() || _result.find(\"fd\") == _result.end()) {\n        json return_value{\n            {\"data\", \"require parameter fd\"},\n            {\"code\", 4003},\n        };\n        return return_value.dump();\n    }\n\n#ifndef TCP_INFO\n    json return_value{\n        {\"data\", \"platform unsupported\"},\n        {\"code\", 5001},\n    };\n#else\n    std::string _fd = _result[\"fd\"];\n    int fd = sw_atoi(_fd.c_str());\n    json return_value{\n        {\"data\", get_socket_info(fd)},\n        {\"code\", 0},\n    };\n#endif\n    return return_value.dump();\n}\n\nstatic std::string handle_get_thread_info(Server *serv, const std::string &msg) {\n    ReactorThread *thread = serv->get_thread(SwooleTG.id);\n    std::stringstream ss;\n    ss << std::this_thread::get_id();\n    json jinfo{\n        {\"tid\", ss.str()},\n        {\"id\", thread->id},\n        {\"dispatch_count\", thread->dispatch_count},\n        {\"event_num\", SwooleTG.reactor->get_event_num()},\n        {\"timer_num\", SwooleTG.timer ? SwooleTG.timer->count() : 0},\n    };\n    json return_value{\n        {\"data\", jinfo},\n        {\"code\", 0},\n    };\n    return return_value.dump();\n}\n\nstatic std::string handle_get_manager_info(Server *serv, const std::string &msg) {\n    ProcessPool *pool = serv->get_event_worker_pool();\n    json jinfo{\n        {\"pid\", getpid()},\n        {\"reload_count\", pool->reload_count},\n        {\"reload_last_time\", pool->reload_last_time},\n    };\n    json return_value{\n        {\"data\", jinfo},\n        {\"code\", 0},\n    };\n    return return_value.dump();\n}\n\nstatic size_t get_socket_out_buffer_total_size() {\n    if (!sw_reactor()) {\n        return 0;\n    }\n    size_t size = 0;\n    for (auto &s : sw_reactor()->get_sockets()) {\n        size += s.second->get_out_buffer_length();\n    }\n    return size;\n}\n\nstatic std::string handle_get_memory_info(Server *serv, const std::string &msg) {\n    bool is_thread = serv->is_reactor_thread();\n\n    json jinfo{\n        {\"server\", sizeof(Server)},\n        {\"workers\", serv->get_all_worker_num() * sizeof(Worker)},\n        {\"connection_list\", serv->get_max_connection() * sizeof(Connection)},\n        {\"session_list\", SW_SESSION_LIST_SIZE * sizeof(Session)},\n        {\"global_memory\", dynamic_cast<GlobalMemory *>(sw_mem_pool())->get_memory_size()},\n        {\"thread_global_memory\", sw_tg_buffer()->size},\n        {\"message_bus\",\n         is_thread ? serv->get_thread(SwooleTG.id)->message_bus.get_memory_size()\n                   : serv->message_bus.get_memory_size()},\n        {\"socket_list\", sw_reactor() ? sw_reactor()->get_sockets().size() * sizeof(network::Socket) : 0},\n        {\"socket_out_buffer\", get_socket_out_buffer_total_size()},\n        {\"php_memory\", is_thread ? 0 : zend_memory_usage(true)},\n    };\n    json return_value{\n        {\"data\", jinfo},\n        {\"code\", 0},\n    };\n    return return_value.dump();\n}\n\nstatic std::string handle_get_connections(Server *serv, const std::string &msg) {\n    json list = json::array();\n    serv->foreach_connection([serv, &list](Connection *conn) {\n        if (serv->is_process_mode() && conn->reactor_id != SwooleTG.id) {\n            return;\n        }\n        if (serv->is_base_mode() && sw_worker() && conn->reactor_id != sw_worker()->id) {\n            return;\n        }\n        list.push_back(get_connection_info(serv, conn));\n    });\n    json return_value{\n        {\"data\", list},\n        {\"code\", 0},\n    };\n    return return_value.dump();\n}\n\nstatic std::string handle_get_connection_info(Server *serv, const std::string &msg) {\n    auto _result = json::parse(msg);\n    if (!_result.is_object() || _result.find(\"session_id\") == _result.end()) {\n        json return_value{\n            {\"data\", \"require parameter session_id\"},\n            {\"code\", 4003},\n        };\n        return return_value.dump();\n    }\n\n    std::string _session_id = _result[\"session_id\"];\n    int session_id = sw_atoi(_session_id.c_str());\n    Connection *conn = serv->get_connection_verify(session_id);\n    if (!conn) {\n        json return_value{\n            {\"data\", \"connection not exists\"},\n            {\"code\", 4004},\n        };\n        return return_value.dump();\n    }\n\n    json return_value{\n        {\"data\", get_connection_info(serv, conn)},\n        {\"code\", 0},\n    };\n    return return_value.dump();\n}\n\nstatic std::string handle_get_all_ports(Server *serv, const std::string &msg) {\n    json _list = json::array();\n    for (auto port : serv->ports) {\n        json info = json::object({\n            {\"host\", port->host},\n            {\"port\", port->port},\n            {\"backlog\", port->backlog},\n            {\"type\", port->type},\n            {\"ssl\", port->ssl},\n            {\"protocols\", port->get_protocols()},\n            {\"connection_num\", (long) port->get_connection_num()},\n        });\n        _list.push_back(info);\n    };\n    json return_value{\n        {\"data\", _list},\n        {\"code\", 0},\n    };\n    return return_value.dump();\n}\n\nvoid register_admin_server_commands(Server *serv) {\n    serv->add_command(\"get_all_sockets\", Server::Command::ALL_PROCESS, handle_get_all_sockets);\n    serv->add_command(\"get_all_commands\", Server::Command::ALL_PROCESS, handle_get_all_commands);\n    serv->add_command(\"get_socket_info\", Server::Command::ALL_PROCESS, handle_get_socket_info);\n    serv->add_command(\"get_thread_info\", Server::Command::ALL_PROCESS, handle_get_thread_info);\n    serv->add_command(\"get_manager_info\", Server::Command::MANAGER, handle_get_manager_info);\n    serv->add_command(\"get_thread_info\", Server::Command::ALL_PROCESS, handle_get_thread_info);\n    serv->add_command(\"get_memory_info\", Server::Command::ALL_PROCESS, handle_get_memory_info);\n    serv->add_command(\"get_all_unix_sockets\", Server::Command::ALL_PROCESS, handle_get_all_unix_sockets);\n    serv->add_command(\"get_all_ports\", Server::Command::MASTER, handle_get_all_ports);\n\n    int accepted_process_types;\n    if (serv->is_base_mode() || serv->single_thread) {\n        accepted_process_types = Server::Command::EVENT_WORKER | Server::Command::MASTER;\n    } else {\n        accepted_process_types = Server::Command::REACTOR_THREAD;\n    }\n    serv->add_command(\"get_connections\", accepted_process_types, handle_get_connections);\n    serv->add_command(\"get_connection_info\", accepted_process_types, handle_get_connection_info);\n}\n}  // namespace swoole\n\ntypedef std::function<void(zend_object *obj)> objects_store_iterator;\n\nstatic inline bool object_valid(zend_object *obj) {\n    return obj && IS_OBJ_VALID(obj) && obj->handlers && obj->handlers->get_class_name;\n}\n\nstatic void objects_store_foreach(const objects_store_iterator &fn) {\n    for (uint32_t i = 0; i < EG(objects_store).top; i++) {\n        zend_object *obj = EG(objects_store).object_buckets[i];\n        if (object_valid(obj)) {\n            fn(obj);\n        }\n    }\n}\n\nstatic uint32_t object_store_count() {\n    uint32_t count = 0;\n    objects_store_foreach([&count](zend_object *obj) { count++; });\n    return count;\n}\n\n#ifdef TCP_INFO\n// clang-format off\nstd::unordered_map<std::string, uint64_t> sw_socket_parse_tcp_info(tcp_info *info) {\n#if defined(__FreeBSD__) || defined(__NetBSD__)\n    return {\n        {\"state\", info->tcpi_state},\n        {\"ca_state\", info->__tcpi_ca_state},\n        {\"retransmits\", info->__tcpi_retransmits},\n        {\"probes\", info->__tcpi_probes},\n        {\"backoff\", info->__tcpi_backoff},\n        {\"options\", info->tcpi_options},\n        {\"snd_wscale\", uint8_t(info->tcpi_snd_wscale)},\n        {\"rcv_wscale\", uint8_t(info->tcpi_rcv_wscale)},\n        {\"rto\", info->tcpi_rto},\n        {\"ato\", info->__tcpi_ato},\n        {\"snd_mss\", info->tcpi_snd_mss},\n        {\"rcv_mss\", info->tcpi_rcv_mss},\n        {\"unacked\", info->__tcpi_unacked},\n        {\"sacked\", info->__tcpi_sacked},\n        {\"lost\", info->__tcpi_lost},\n        {\"retrans\", info->__tcpi_retrans},\n        {\"fackets\", info->__tcpi_fackets},\n        {\"last_data_sent\", info->__tcpi_last_data_sent},\n        {\"last_ack_sent\", info->__tcpi_last_ack_sent},\n        {\"last_data_recv\", info->tcpi_last_data_recv},\n        {\"last_ack_recv\", info->__tcpi_last_ack_recv},\n        {\"pmtu\", info->__tcpi_pmtu},\n        {\"rcv_ssthresh\", info->__tcpi_rcv_ssthresh},\n        {\"rtt\", info->tcpi_rtt},\n        {\"rttvar\", info->tcpi_rttvar},\n        {\"snd_ssthresh\", info->tcpi_snd_ssthresh},\n        {\"snd_cwnd\", info->tcpi_snd_cwnd},\n        {\"advmss\", info->__tcpi_advmss},\n        {\"reordering\", info->__tcpi_reordering},\n        {\"rcv_rtt\", info->__tcpi_rcv_rtt},\n        {\"rcv_space\", info->tcpi_rcv_space},\n        {\"snd_wnd\", info->tcpi_snd_wnd},\n        {\"snd_nxt\", info->tcpi_snd_nxt},\n        {\"rcv_nxt\", info->tcpi_rcv_nxt},\n        {\"toe_tid\", info->tcpi_toe_tid},\n        {\"total_retrans\", info->tcpi_snd_rexmitpack},\n        {\"rcv_ooopack\", info->tcpi_rcv_ooopack},\n        {\"snd_zerowin\", info->tcpi_snd_zerowin},\n    };\n#elif defined(__OpenBSD__)\n    return {\n        {\"state\", info->tcpi_state},\n        {\"ca_state\", info->__tcpi_ca_state},\n        {\"retransmits\", info->__tcpi_retransmits},\n        {\"probes\", info->__tcpi_probes},\n        {\"backoff\", info->__tcpi_backoff},\n        {\"options\", info->tcpi_options},\n        {\"snd_wscale\", uint8_t(info->tcpi_snd_wscale)},\n        {\"rcv_wscale\", uint8_t(info->tcpi_rcv_wscale)},\n        {\"rto\", info->tcpi_rto},\n        {\"ato\", info->__tcpi_ato},\n        {\"snd_mss\", info->tcpi_snd_mss},\n        {\"rcv_mss\", info->tcpi_rcv_mss},\n        {\"unacked\", info->__tcpi_unacked},\n        {\"sacked\", info->__tcpi_sacked},\n        {\"lost\", info->__tcpi_lost},\n        {\"retrans\", info->__tcpi_retrans},\n        {\"fackets\", info->__tcpi_fackets},\n        {\"last_data_sent\", info->tcpi_last_data_sent},\n        {\"last_ack_sent\", info->tcpi_last_ack_sent},\n        {\"last_data_recv\", info->tcpi_last_data_recv},\n        {\"last_ack_recv\", info->tcpi_last_ack_recv},\n        {\"pmtu\", info->__tcpi_pmtu},\n        {\"rcv_ssthresh\", info->__tcpi_rcv_ssthresh},\n        {\"rtt\", info->tcpi_rtt},\n        {\"rttvar\", info->tcpi_rttvar},\n        {\"snd_ssthresh\", info->tcpi_snd_ssthresh},\n        {\"snd_cwnd\", info->tcpi_snd_cwnd},\n        {\"advmss\", info->__tcpi_advmss},\n        {\"reordering\", info->__tcpi_reordering},\n        {\"rcv_rtt\", info->__tcpi_rcv_rtt},\n        {\"rcv_space\", info->tcpi_rcv_space},\n        {\"snd_wnd\", info->tcpi_snd_wnd},\n        {\"snd_nxt\", info->tcpi_snd_nxt},\n        {\"rcv_nxt\", info->tcpi_rcv_nxt},\n        {\"toe_tid\", info->tcpi_toe_tid},\n        {\"total_retrans\", info->tcpi_snd_rexmitpack},\n        {\"rcv_ooopack\", info->tcpi_rcv_ooopack},\n        {\"snd_zerowin\", info->tcpi_snd_zerowin},\n    };\n#elif defined(__linux__)\n    return {\n        {\"state\", info->tcpi_state},\n        {\"ca_state\", info->tcpi_ca_state},\n        {\"retransmits\", info->tcpi_retransmits},\n        {\"probes\", info->tcpi_probes},\n        {\"backoff\", info->tcpi_backoff},\n        {\"options\", info->tcpi_options},\n        {\"snd_wscale\", uint8_t(info->tcpi_snd_wscale)},\n        {\"rcv_wscale\", uint8_t(info->tcpi_rcv_wscale)},\n        {\"rto\", info->tcpi_rto},\n        {\"ato\", info->tcpi_ato},\n        {\"snd_mss\", info->tcpi_snd_mss},\n        {\"rcv_mss\", info->tcpi_rcv_mss},\n        {\"unacked\", info->tcpi_unacked},\n        {\"sacked\", info->tcpi_sacked},\n        {\"lost\", info->tcpi_lost},\n        {\"retrans\", info->tcpi_retrans},\n        {\"fackets\", info->tcpi_fackets},\n        {\"last_data_sent\", info->tcpi_last_data_sent},\n        {\"last_ack_sent\", info->tcpi_last_ack_sent},\n        {\"last_data_recv\", info->tcpi_last_data_recv},\n        {\"last_ack_recv\", info->tcpi_last_ack_recv},\n        {\"pmtu\", info->tcpi_pmtu},\n        {\"rcv_ssthresh\", info->tcpi_rcv_ssthresh},\n        {\"rtt\", info->tcpi_rtt},\n        {\"rttvar\", info->tcpi_rttvar},\n        {\"snd_ssthresh\", info->tcpi_snd_ssthresh},\n        {\"snd_cwnd\", info->tcpi_snd_cwnd},\n        {\"advmss\", info->tcpi_advmss},\n        {\"reordering\", info->tcpi_reordering},\n        {\"rcv_rtt\", info->tcpi_rcv_rtt},\n        {\"rcv_space\", info->tcpi_rcv_space},\n        {\"total_retrans\", info->tcpi_total_retrans},\n    };\n#elif defined(__APPLE__)\n    return {\n        {\"state\", (uint32_t) info->tcpi_state},\n        {\"snd_wscale\", (uint32_t) info->tcpi_snd_wscale},\n        {\"rcv_wscale\", (uint32_t) info->tcpi_rcv_wscale},\n        {\"options\", (uint32_t) info->tcpi_options},\n        {\"flags\", (uint32_t) info->tcpi_flags},\n        {\"rto\", info->tcpi_rto},\n        {\"maxseg\", info->tcpi_maxseg},\n        {\"snd_ssthresh\", info->tcpi_snd_ssthresh},\n        {\"snd_cwnd\", info->tcpi_snd_cwnd},\n        {\"snd_wnd\", info->tcpi_snd_wnd},\n        {\"snd_sbbytes\", info->tcpi_snd_sbbytes},\n        {\"rcv_wnd\", info->tcpi_rcv_wnd},\n        {\"srtt\", info->tcpi_srtt},\n        {\"rttvar\", info->tcpi_rttvar},\n    };\n#else\n    return {};\n#endif\n}\n// clang-format on\n#endif\n\nZEND_FUNCTION(swoole_get_vm_status) {\n    array_init(return_value);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"object_num\"), object_store_count());\n    add_assoc_long_ex(return_value, ZEND_STRL(\"resource_num\"), zend_array_count(&EG(regular_list)));\n}\n\nZEND_FUNCTION(swoole_get_objects) {\n    zend_objects_store *objects = &EG(objects_store);\n    if (objects->top <= 1) {\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n    objects_store_foreach([return_value](zend_object *obj) {\n        zval zobject;\n        ZVAL_OBJ(&zobject, obj);\n        zval_addref_p(&zobject);\n        add_next_index_zval(return_value, &zobject);\n    });\n}\n\nZEND_FUNCTION(swoole_get_object_by_handle) {\n    zend_long handle;\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(handle)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zend_objects_store *objects = &EG(objects_store);\n    if (objects->top <= 1 || handle >= objects->top) {\n        RETURN_FALSE;\n    }\n\n    zend_object *obj = objects->object_buckets[handle];\n    if (!object_valid(obj)) {\n        RETURN_FALSE;\n    }\n    GC_ADDREF(obj);\n    RETURN_OBJ(obj);\n}\n"
  },
  {
    "path": "ext-src/swoole_async_coro.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_cxx.h\"\n#include \"swoole_socket.h\"\n\n#include <string>\n#include <vector>\n#include <unordered_map>\n\nusing std::string;\nusing std::vector;\nusing swoole::Coroutine;\nusing swoole::PHPCoroutine;\nusing swoole::Timer;\n\nstruct DNSCacheEntity {\n    char address[INET6_ADDRSTRLEN];\n    time_t update_time;\n};\n\nstatic SW_THREAD_LOCAL std::unordered_map<std::string, DNSCacheEntity *> request_cache_map;\n\nvoid php_swoole_async_coro_rshutdown() {\n    for (auto i = request_cache_map.begin(); i != request_cache_map.end(); ++i) {\n        efree(i->second);\n    }\n}\n\nvoid php_swoole_set_aio_option(const HashTable *vht) {\n    zval *ztmp;\n    /* AIO */\n    if (php_swoole_array_get_value(vht, \"aio_core_worker_num\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        v = SW_MAX(1, SW_MIN(v, UINT32_MAX));\n        SwooleG.aio_core_worker_num = v;\n    }\n    if (php_swoole_array_get_value(vht, \"aio_worker_num\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        v = SW_MAX(1, SW_MIN(v, UINT32_MAX));\n        SwooleG.aio_worker_num = v;\n    }\n    if (php_swoole_array_get_value(vht, \"aio_max_wait_time\", ztmp)) {\n        SwooleG.aio_max_wait_time = zval_get_double(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"aio_max_idle_time\", ztmp)) {\n        SwooleG.aio_max_idle_time = zval_get_double(ztmp);\n    }\n#ifdef SW_USE_IOURING\n    if (php_swoole_array_get_value(vht, \"iouring_entries\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        SwooleG.iouring_entries = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n    }\n    if (php_swoole_array_get_value(vht, \"iouring_workers\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        SwooleG.iouring_workers = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n    }\n    if (php_swoole_array_get_value(vht, \"iouring_flag\", ztmp)) {\n        SwooleG.iouring_flag = zval_get_long(ztmp);\n    }\n#endif\n}\n\nPHP_FUNCTION(swoole_async_set) {\n    SW_MUST_BE_MAIN_THREAD();\n    if (sw_reactor()) {\n        php_swoole_fatal_error(E_ERROR, \"eventLoop has already been created. unable to change settings\");\n        swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT);\n        RETURN_FALSE;\n    }\n\n    zval *zset = nullptr;\n    zval *ztmp;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ARRAY(zset)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    HashTable *vht = Z_ARRVAL_P(zset);\n\n    php_swoole_set_global_option(vht);\n    php_swoole_set_aio_option(vht);\n\n    if (php_swoole_array_get_value(vht, \"wait_signal\", ztmp)) {\n        SwooleG.wait_signal = zval_is_true(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"dns_cache_refresh_time\", ztmp)) {\n        SwooleG.dns_cache_refresh_time = zval_get_double(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"thread_num\", ztmp) ||\n        php_swoole_array_get_value(vht, \"min_thread_num\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        v = SW_MAX(1, SW_MIN(v, UINT32_MAX));\n        SwooleG.aio_core_worker_num = v;\n    }\n    if (php_swoole_array_get_value(vht, \"max_thread_num\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        v = SW_MAX(1, SW_MIN(v, UINT32_MAX));\n        SwooleG.aio_worker_num = v;\n    }\n    if (php_swoole_array_get_value(vht, \"dns_lookup_random\", ztmp)) {\n        SwooleG.dns_lookup_random = zval_is_true(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"use_async_resolver\", ztmp)) {\n        SwooleG.use_async_resolver = zval_is_true(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"enable_coroutine\", ztmp)) {\n        SwooleG.enable_coroutine = zval_is_true(ztmp);\n    }\n    RETURN_TRUE;\n}\n\nPHP_FUNCTION(swoole_async_dns_lookup_coro) {\n    Coroutine::get_current_safe();\n\n    zval *domain;\n    zend_long type = AF_INET;\n    double timeout = swoole::network::Socket::default_dns_timeout;\n\n    ZEND_PARSE_PARAMETERS_START(1, 3)\n    Z_PARAM_ZVAL(domain)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    Z_PARAM_LONG(type)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (Z_TYPE_P(domain) != IS_STRING) {\n        php_swoole_fatal_error(E_WARNING, \"invalid domain name\");\n        RETURN_FALSE;\n    }\n\n    if (Z_STRLEN_P(domain) == 0) {\n        php_swoole_fatal_error(E_WARNING, \"domain name empty\");\n        RETURN_FALSE;\n    }\n\n    // find cache\n    std::string key(Z_STRVAL_P(domain), Z_STRLEN_P(domain));\n    DNSCacheEntity *cache;\n\n    if (request_cache_map.find(key) != request_cache_map.end()) {\n        cache = request_cache_map[key];\n        if (cache->update_time > Timer::get_absolute_msec()) {\n            RETURN_STRING(cache->address);\n        }\n    }\n\n    php_swoole_check_reactor();\n\n    vector<string> result = swoole::coroutine::dns_lookup(Z_STRVAL_P(domain), type, timeout);\n    if (result.empty()) {\n        swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n        RETURN_FALSE;\n    }\n\n    if (SwooleG.dns_lookup_random) {\n        RETVAL_STRING(result[swoole_rand() % result.size()].c_str());\n    } else {\n        RETVAL_STRING(result[0].c_str());\n    }\n\n    auto cache_iterator = request_cache_map.find(key);\n    if (cache_iterator == request_cache_map.end()) {\n        cache = (DNSCacheEntity *) emalloc(sizeof(DNSCacheEntity));\n        request_cache_map[key] = cache;\n    } else {\n        cache = cache_iterator->second;\n    }\n    memcpy(cache->address, Z_STRVAL_P(return_value), Z_STRLEN_P(return_value));\n    cache->address[Z_STRLEN_P(return_value)] = '\\0';\n    cache->update_time = Timer::get_absolute_msec() + (int64_t) (SwooleG.dns_cache_refresh_time * 1000);\n}\n"
  },
  {
    "path": "ext-src/swoole_atomic.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_private.h\"\n#include \"swoole_memory.h\"\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_atomic_arginfo.h\"\nEND_EXTERN_C()\n\nzend_class_entry *swoole_atomic_ce;\nstatic zend_object_handlers swoole_atomic_handlers;\n\nzend_class_entry *swoole_atomic_long_ce;\nstatic zend_object_handlers swoole_atomic_long_handlers;\n\nstruct AtomicObject {\n    sw_atomic_t *ptr;\n    zend_object std;\n};\n\nstatic sw_inline AtomicObject *atomic_fetch_object(zend_object *obj) {\n    return reinterpret_cast<AtomicObject *>(reinterpret_cast<char *>(obj) - swoole_atomic_handlers.offset);\n}\n\nstatic sw_atomic_t *atomic_get_ptr(const zval *zobject) {\n    return atomic_fetch_object(Z_OBJ_P(zobject))->ptr;\n}\n\nstatic void atomic_free_object(zend_object *object) {\n    sw_mem_pool()->free((void *) atomic_fetch_object(object)->ptr);\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *atomic_create_object(zend_class_entry *ce) {\n    auto *atomic = static_cast<AtomicObject *>(zend_object_alloc(sizeof(AtomicObject), ce));\n    if (atomic == nullptr) {\n        zend_throw_exception(swoole_exception_ce, \"global memory allocation failure\", SW_ERROR_MALLOC_FAIL);\n    }\n\n    zend_object_std_init(&atomic->std, ce);\n    object_properties_init(&atomic->std, ce);\n    atomic->std.handlers = &swoole_atomic_handlers;\n    atomic->ptr = (sw_atomic_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_t));\n    if (atomic->ptr == nullptr) {\n        zend_throw_exception(swoole_exception_ce, \"global memory allocation failure\", SW_ERROR_MALLOC_FAIL);\n    }\n\n    return &atomic->std;\n}\n\nstruct AtomicLongObject {\n    sw_atomic_long_t *ptr;\n    zend_object std;\n};\n\nstatic AtomicLongObject *atomic_long_fetch_object(zend_object *obj) {\n    return reinterpret_cast<AtomicLongObject *>(reinterpret_cast<char *>(obj) - swoole_atomic_long_handlers.offset);\n}\n\nstatic sw_atomic_long_t *atomic_long_get_ptr(const zval *zobject) {\n    return atomic_long_fetch_object(Z_OBJ_P(zobject))->ptr;\n}\n\nstatic void atomic_long_free_object(zend_object *object) {\n    sw_mem_pool()->free((void *) atomic_long_fetch_object(object)->ptr);\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *atomic_long_create_object(zend_class_entry *ce) {\n    auto *atomic_long = static_cast<AtomicLongObject *>(zend_object_alloc(sizeof(AtomicLongObject), ce));\n    if (atomic_long == nullptr) {\n        zend_throw_exception(swoole_exception_ce, \"global memory allocation failure\", SW_ERROR_MALLOC_FAIL);\n    }\n\n    zend_object_std_init(&atomic_long->std, ce);\n    object_properties_init(&atomic_long->std, ce);\n    atomic_long->std.handlers = &swoole_atomic_long_handlers;\n\n    atomic_long->ptr = (sw_atomic_long_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_long_t));\n    if (atomic_long->ptr == nullptr) {\n        zend_throw_exception(swoole_exception_ce, \"global memory allocation failure\", SW_ERROR_MALLOC_FAIL);\n    }\n\n    return &atomic_long->std;\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_atomic, __construct);\nstatic PHP_METHOD(swoole_atomic, add);\nstatic PHP_METHOD(swoole_atomic, sub);\nstatic PHP_METHOD(swoole_atomic, get);\nstatic PHP_METHOD(swoole_atomic, set);\nstatic PHP_METHOD(swoole_atomic, cmpset);\nstatic PHP_METHOD(swoole_atomic, wait);\nstatic PHP_METHOD(swoole_atomic, wakeup);\n\nstatic PHP_METHOD(swoole_atomic_long, __construct);\nstatic PHP_METHOD(swoole_atomic_long, add);\nstatic PHP_METHOD(swoole_atomic_long, sub);\nstatic PHP_METHOD(swoole_atomic_long, get);\nstatic PHP_METHOD(swoole_atomic_long, set);\nstatic PHP_METHOD(swoole_atomic_long, cmpset);\nSW_EXTERN_C_END\n\n// clang-format off\n\nstatic const zend_function_entry swoole_atomic_methods[] =\n{\n    PHP_ME(swoole_atomic, __construct, arginfo_class_Swoole_Atomic___construct, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_atomic, add,         arginfo_class_Swoole_Atomic_add,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_atomic, sub,         arginfo_class_Swoole_Atomic_sub,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_atomic, get,         arginfo_class_Swoole_Atomic_get,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_atomic, set,         arginfo_class_Swoole_Atomic_set,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_atomic, wait,        arginfo_class_Swoole_Atomic_wait,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_atomic, wakeup,      arginfo_class_Swoole_Atomic_wakeup,      ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_atomic, cmpset,      arginfo_class_Swoole_Atomic_cmpset,      ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n\nstatic const zend_function_entry swoole_atomic_long_methods[] =\n{\n    PHP_ME(swoole_atomic_long, __construct, arginfo_class_Swoole_Atomic_Long___construct, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_atomic_long, add,         arginfo_class_Swoole_Atomic_Long_add,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_atomic_long, sub,         arginfo_class_Swoole_Atomic_Long_sub,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_atomic_long, get,         arginfo_class_Swoole_Atomic_Long_get,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_atomic_long, set,         arginfo_class_Swoole_Atomic_Long_set,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_atomic_long, cmpset,      arginfo_class_Swoole_Atomic_Long_cmpset,      ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n\n// clang-format on\n\nvoid php_swoole_atomic_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_atomic, \"Swoole\\\\Atomic\", nullptr, swoole_atomic_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_atomic);\n    SW_SET_CLASS_CLONEABLE(swoole_atomic, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_atomic, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(swoole_atomic, atomic_create_object, atomic_free_object, AtomicObject, std);\n\n    SW_INIT_CLASS_ENTRY(swoole_atomic_long, \"Swoole\\\\Atomic\\\\Long\", nullptr, swoole_atomic_long_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_atomic_long);\n    SW_SET_CLASS_CLONEABLE(swoole_atomic_long, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_atomic_long, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(\n        swoole_atomic_long, atomic_long_create_object, atomic_long_free_object, AtomicLongObject, std);\n}\n\nPHP_METHOD(swoole_atomic, __construct) {\n    sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS);\n    zend_long value = 0;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    *atomic = (sw_atomic_t) value;\n}\n\nPHP_METHOD(swoole_atomic, add) {\n    sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS);\n    zend_long add_value = 1;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(add_value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_LONG(sw_atomic_add_fetch(atomic, (uint32_t) add_value));\n}\n\nPHP_METHOD(swoole_atomic, sub) {\n    sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS);\n    zend_long sub_value = 1;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(sub_value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_LONG(sw_atomic_sub_fetch(atomic, (uint32_t) sub_value));\n}\n\nPHP_METHOD(swoole_atomic, get) {\n    sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS);\n    RETURN_LONG(*atomic);\n}\n\nPHP_METHOD(swoole_atomic, set) {\n    sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS);\n    zend_long set_value;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(set_value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    *atomic = (uint32_t) set_value;\n}\n\nPHP_METHOD(swoole_atomic, cmpset) {\n    sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS);\n    zend_long cmp_value, set_value;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_LONG(cmp_value)\n    Z_PARAM_LONG(set_value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(sw_atomic_cmp_set(atomic, (sw_atomic_t) cmp_value, (sw_atomic_t) set_value));\n}\n\nPHP_METHOD(swoole_atomic, wait) {\n    sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS);\n    double timeout = 1.0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    SW_CHECK_RETURN(sw_atomic_futex_wait(atomic, timeout));\n}\n\nPHP_METHOD(swoole_atomic, wakeup) {\n    sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS);\n    zend_long n = 1;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(n)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    SW_CHECK_RETURN(sw_atomic_futex_wakeup(atomic, (int) n));\n}\n\nPHP_METHOD(swoole_atomic_long, __construct) {\n    sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS);\n    zend_long value = 0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    *atomic_long = (sw_atomic_long_t) value;\n    RETURN_TRUE;\n}\n\nPHP_METHOD(swoole_atomic_long, add) {\n    sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS);\n    zend_long add_value = 1;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(add_value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_LONG(sw_atomic_add_fetch(atomic_long, (sw_atomic_long_t) add_value));\n}\n\nPHP_METHOD(swoole_atomic_long, sub) {\n    sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS);\n    zend_long sub_value = 1;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(sub_value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_LONG(sw_atomic_sub_fetch(atomic_long, (sw_atomic_long_t) sub_value));\n}\n\nPHP_METHOD(swoole_atomic_long, get) {\n    sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS);\n    RETURN_LONG(*atomic_long);\n}\n\nPHP_METHOD(swoole_atomic_long, set) {\n    sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS);\n    zend_long set_value;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(set_value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    *atomic_long = (sw_atomic_long_t) set_value;\n}\n\nPHP_METHOD(swoole_atomic_long, cmpset) {\n    sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS);\n    zend_long cmp_value, set_value;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_LONG(cmp_value)\n    Z_PARAM_LONG(set_value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(sw_atomic_cmp_set(atomic_long, (sw_atomic_long_t) cmp_value, (sw_atomic_long_t) set_value));\n}\n"
  },
  {
    "path": "ext-src/swoole_channel_coro.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2018 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Xinyu Zhu  <xyzhu1120@gmail.com>                             |\n |         Tianfeng Han <rango@swoole.com>                              |\n +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_cxx.h\"\n\n#include \"swoole_coroutine_channel.h\"\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_channel_coro_arginfo.h\"\nEND_EXTERN_C()\n\nusing swoole::coroutine::Channel;\n\nstatic zend_class_entry *swoole_channel_coro_ce;\nstatic zend_object_handlers swoole_channel_coro_handlers;\n\nstruct ChannelObject {\n    Channel *chan;\n    zend_object std;\n};\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_channel_coro, __construct);\nstatic PHP_METHOD(swoole_channel_coro, push);\nstatic PHP_METHOD(swoole_channel_coro, pop);\nstatic PHP_METHOD(swoole_channel_coro, close);\nstatic PHP_METHOD(swoole_channel_coro, stats);\nstatic PHP_METHOD(swoole_channel_coro, length);\nstatic PHP_METHOD(swoole_channel_coro, isEmpty);\nstatic PHP_METHOD(swoole_channel_coro, isFull);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic const zend_function_entry swoole_channel_coro_methods[] =\n{\n    PHP_ME(swoole_channel_coro, __construct, arginfo_class_Swoole_Coroutine_Channel___construct, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_channel_coro, push,        arginfo_class_Swoole_Coroutine_Channel_push,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_channel_coro, pop,         arginfo_class_Swoole_Coroutine_Channel_pop,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_channel_coro, isEmpty,     arginfo_class_Swoole_Coroutine_Channel_isEmpty,     ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_channel_coro, isFull,      arginfo_class_Swoole_Coroutine_Channel_isFull,      ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_channel_coro, close,       arginfo_class_Swoole_Coroutine_Channel_close,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_channel_coro, stats,       arginfo_class_Swoole_Coroutine_Channel_stats,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_channel_coro, length,      arginfo_class_Swoole_Coroutine_Channel_length,      ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nstatic ChannelObject *channel_coro_fetch_object(zend_object *obj) {\n    return reinterpret_cast<ChannelObject *>(reinterpret_cast<char *>(obj) - swoole_channel_coro_handlers.offset);\n}\n\nstatic Channel *channel_coro_get_ptr(const zval *zobject) {\n    Channel *chan = channel_coro_fetch_object(Z_OBJ_P(zobject))->chan;\n    if (UNEXPECTED(!chan)) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION, \"must call constructor first\");\n    }\n    return chan;\n}\n\nstatic void channel_coro_dtor_object(zend_object *object) {\n    zend_objects_destroy_object(object);\n\n    ChannelObject *chan_object = channel_coro_fetch_object(object);\n    Channel *chan = chan_object->chan;\n    if (chan) {\n        zval *data;\n        while ((data = (zval *) chan->pop_data())) {\n            sw_zval_free(data);\n        }\n        delete chan;\n        chan_object->chan = nullptr;\n    }\n}\n\nstatic void channel_coro_free_object(zend_object *object) {\n    auto *chan_object = channel_coro_fetch_object(object);\n    delete chan_object->chan;\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *channel_coro_create_object(zend_class_entry *ce) {\n    auto *chan_object = static_cast<ChannelObject *>(zend_object_alloc(sizeof(ChannelObject), ce));\n    zend_object_std_init(&chan_object->std, ce);\n    object_properties_init(&chan_object->std, ce);\n    chan_object->std.handlers = &swoole_channel_coro_handlers;\n    return &chan_object->std;\n}\n\nvoid php_swoole_channel_coro_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_channel_coro, \"Swoole\\\\Coroutine\\\\Channel\", \"Co\\\\Channel\", swoole_channel_coro_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_channel_coro);\n    SW_SET_CLASS_CLONEABLE(swoole_channel_coro, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_channel_coro, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(\n        swoole_channel_coro, channel_coro_create_object, channel_coro_free_object, ChannelObject, std);\n    SW_SET_CLASS_DTOR(swoole_channel_coro, channel_coro_dtor_object);\n    if (SWOOLE_G(use_shortname)) {\n        SW_CLASS_ALIAS(\"Chan\", swoole_channel_coro);\n    }\n\n    zend_declare_property_long(swoole_channel_coro_ce, ZEND_STRL(\"capacity\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_channel_coro_ce, ZEND_STRL(\"errCode\"), 0, ZEND_ACC_PUBLIC);\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_CHANNEL_OK\", Channel::ERROR_OK);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_CHANNEL_TIMEOUT\", Channel::ERROR_TIMEOUT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_CHANNEL_CLOSED\", Channel::ERROR_CLOSED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_CHANNEL_CANCELED\", Channel::ERROR_CANCELED);\n}\n\nstatic PHP_METHOD(swoole_channel_coro, __construct) {\n    zend_long capacity = 1;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(capacity)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (capacity <= 0) {\n        capacity = 1;\n    }\n\n    ChannelObject *chan_t = channel_coro_fetch_object(Z_OBJ_P(ZEND_THIS));\n    chan_t->chan = new Channel(capacity);\n    zend_update_property_long(swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"capacity\"), capacity);\n}\n\nstatic PHP_METHOD(swoole_channel_coro, push) {\n    Channel *chan = channel_coro_get_ptr(ZEND_THIS);\n    zval *zdata;\n    double timeout = -1;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 2)\n    Z_PARAM_ZVAL(zdata)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    Z_TRY_ADDREF_P(zdata);\n    zdata = sw_zval_dup(zdata);\n    if (chan->push(zdata, timeout)) {\n        zend_update_property_long(\n            swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"errCode\"), Channel::ERROR_OK);\n        RETURN_TRUE;\n    } else {\n        zend_update_property_long(\n            swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"errCode\"), chan->get_error());\n        Z_TRY_DELREF_P(zdata);\n        efree(zdata);\n        RETURN_FALSE;\n    }\n}\n\nstatic PHP_METHOD(swoole_channel_coro, pop) {\n    Channel *chan = channel_coro_get_ptr(ZEND_THIS);\n    double timeout = -1;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zval *zdata = (zval *) chan->pop(timeout);\n    if (zdata) {\n        RETVAL_ZVAL(zdata, 0, 0);\n        efree(zdata);\n        zend_update_property_long(\n            swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"errCode\"), Channel::ERROR_OK);\n    } else {\n        zend_update_property_long(\n            swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"errCode\"), chan->get_error());\n        RETURN_FALSE;\n    }\n}\n\nstatic PHP_METHOD(swoole_channel_coro, close) {\n    Channel *chan = channel_coro_get_ptr(ZEND_THIS);\n    RETURN_BOOL(chan->close());\n}\n\nstatic PHP_METHOD(swoole_channel_coro, length) {\n    Channel *chan = channel_coro_get_ptr(ZEND_THIS);\n    RETURN_LONG(chan->length());\n}\n\nstatic PHP_METHOD(swoole_channel_coro, isEmpty) {\n    Channel *chan = channel_coro_get_ptr(ZEND_THIS);\n    RETURN_BOOL(chan->is_empty());\n}\n\nstatic PHP_METHOD(swoole_channel_coro, isFull) {\n    Channel *chan = channel_coro_get_ptr(ZEND_THIS);\n    RETURN_BOOL(chan->is_full());\n}\n\nstatic PHP_METHOD(swoole_channel_coro, stats) {\n    Channel *chan = channel_coro_get_ptr(ZEND_THIS);\n    array_init(return_value);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"consumer_num\"), chan->consumer_num());\n    add_assoc_long_ex(return_value, ZEND_STRL(\"producer_num\"), chan->producer_num());\n    add_assoc_long_ex(return_value, ZEND_STRL(\"queue_num\"), chan->length());\n}\n"
  },
  {
    "path": "ext-src/swoole_client.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_cxx.h\"\n#include \"php_swoole_client.h\"\n#include \"swoole_mqtt.h\"\n\n#include <string>\n#include <queue>\n#include <unordered_map>\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_client_arginfo.h\"\nEND_EXTERN_C()\n\nusing swoole::HttpProxy;\nusing swoole::PacketLength;\nusing swoole::Protocol;\nusing swoole::SocketType;\nusing swoole::Socks5Proxy;\nusing swoole::String;\nusing swoole::network::Address;\nusing swoole::network::Client;\nusing swoole::network::Socket;\n\nstatic std::unordered_map<std::string, std::queue<Client *> *> long_connections;\n\nzend_class_entry *swoole_client_ce;\nzend_object_handlers swoole_client_handlers;\n\nstatic zend_class_entry *swoole_client_exception_ce;\nstatic zend_object_handlers swoole_client_exception_handlers;\n\nstatic Client *php_swoole_client_new(zval *zobject, char *host, int host_len, int port);\n\nstatic sw_inline void php_swoole_client_set_cli(const zval *zobject, Client *cli) {\n    php_swoole_client_fetch_object(Z_OBJ_P(zobject))->cli = cli;\n}\n\n#ifdef SWOOLE_SOCKETS_SUPPORT\nstatic zval *client_get_zsocket(const zval *zobject) {\n    return php_swoole_client_fetch_object(Z_OBJ_P(zobject))->zsocket;\n}\n\nstatic void client_set_zsocket(const zval *zobject, zval *zsocket) {\n    php_swoole_client_fetch_object(Z_OBJ_P(zobject))->zsocket = zsocket;\n}\n#endif\n\nstatic void client_free_object(zend_object *object) {\n    auto client_obj = php_swoole_client_fetch_object(object);\n    if (client_obj->async) {\n        php_swoole_client_async_free_object(client_obj);\n    }\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *client_create_object(zend_class_entry *ce) {\n    auto *client = static_cast<ClientObject *>(zend_object_alloc(sizeof(ClientObject), ce));\n    zend_object_std_init(&client->std, ce);\n    object_properties_init(&client->std, ce);\n    client->std.handlers = &swoole_client_handlers;\n    client->async = nullptr;\n    return &client->std;\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_client, __construct);\nstatic PHP_METHOD(swoole_client, __destruct);\nstatic PHP_METHOD(swoole_client, set);\nstatic PHP_METHOD(swoole_client, connect);\nstatic PHP_METHOD(swoole_client, recv);\nstatic PHP_METHOD(swoole_client, send);\nstatic PHP_METHOD(swoole_client, sendfile);\nstatic PHP_METHOD(swoole_client, sendto);\nstatic PHP_METHOD(swoole_client, enableSSL);\nstatic PHP_METHOD(swoole_client, getPeerCert);\nstatic PHP_METHOD(swoole_client, verifyPeerCert);\nstatic PHP_METHOD(swoole_client, isConnected);\nstatic PHP_METHOD(swoole_client, getsockname);\nstatic PHP_METHOD(swoole_client, getpeername);\nstatic PHP_METHOD(swoole_client, close);\nstatic PHP_METHOD(swoole_client, shutdown);\n\n#ifdef SWOOLE_SOCKETS_SUPPORT\nstatic PHP_METHOD(swoole_client, getSocket);\n#endif\nSW_EXTERN_C_END\n\nstatic uint32_t client_poll_add(const zval *sock_array, uint32_t index, pollfd *fds, int maxevents, int event);\nstatic int client_poll_wait(zval *sock_array, const pollfd *fds, int maxevents, int n_event, int revent);\n\nClient *php_swoole_client_get_cli_safe(const zval *zobject) {\n    Client *cli = php_swoole_client_get_cli(zobject);\n    if (cli && cli->socket) {\n        if (cli->active) {\n            return cli;\n        }\n        if (cli->async_connect) {\n            cli->async_connect = false;\n            int error = -1;\n            if (cli->get_socket()->get_option(SOL_SOCKET, SO_ERROR, &error) == 0) {\n                if (error == 0) {\n                    cli->active = true;\n                    return cli;\n                }\n            }\n            php_swoole_client_free(zobject, cli);\n        }\n    }\n    swoole_set_last_error(SW_ERROR_CLIENT_NO_CONNECTION);\n    zend_update_property_long(swoole_client_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"errCode\"), swoole_get_last_error());\n    php_swoole_error(E_WARNING, \"client is not connected to server\");\n    return nullptr;\n}\n\n// clang-format off\nstatic const zend_function_entry swoole_client_methods[] =\n{\n    PHP_ME(swoole_client, __construct, arginfo_class_Swoole_Client___construct, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client, __destruct,  arginfo_class_Swoole_Client___destruct,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client, set,         arginfo_class_Swoole_Client_set,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client, connect,     arginfo_class_Swoole_Client_connect,     ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client, recv,        arginfo_class_Swoole_Client_recv,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client, send,        arginfo_class_Swoole_Client_send,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client, sendfile,    arginfo_class_Swoole_Client_sendfile,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client, sendto,      arginfo_class_Swoole_Client_sendto,      ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client, shutdown,    arginfo_class_Swoole_Client_shutdown,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client, enableSSL,      arginfo_class_Swoole_Client_enableSSL,      ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client, getPeerCert,    arginfo_class_Swoole_Client_getPeerCert,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client, verifyPeerCert, arginfo_class_Swoole_Client_verifyPeerCert, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client, isConnected, arginfo_class_Swoole_Client_isConnected, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client, getsockname, arginfo_class_Swoole_Client_getsockname, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client, getpeername, arginfo_class_Swoole_Client_getpeername, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client, close,       arginfo_class_Swoole_Client_close,       ZEND_ACC_PUBLIC)\n#ifdef SWOOLE_SOCKETS_SUPPORT\n    PHP_ME(swoole_client, getSocket, arginfo_class_Swoole_Client_getSocket, ZEND_ACC_PUBLIC)\n#endif\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_client_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_client, \"Swoole\\\\Client\", nullptr, swoole_client_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_client);\n    SW_SET_CLASS_CLONEABLE(swoole_client, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_client, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(swoole_client, client_create_object, client_free_object, ClientObject, std);\n\n    SW_INIT_CLASS_ENTRY_EX(swoole_client_exception, \"Swoole\\\\Client\\\\Exception\", nullptr, nullptr, swoole_exception);\n\n    zend_declare_property_long(swoole_client_ce, ZEND_STRL(\"errCode\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_client_ce, ZEND_STRL(\"sock\"), -1, ZEND_ACC_PUBLIC);\n    zend_declare_property_bool(swoole_client_ce, ZEND_STRL(\"reuse\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_client_ce, ZEND_STRL(\"reuseCount\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_client_ce, ZEND_STRL(\"type\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_client_ce, ZEND_STRL(\"id\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_client_ce, ZEND_STRL(\"setting\"), ZEND_ACC_PUBLIC);\n\n    zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL(\"MSG_OOB\"), MSG_OOB);\n    zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL(\"MSG_PEEK\"), MSG_PEEK);\n    zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL(\"MSG_DONTWAIT\"), MSG_DONTWAIT);\n    zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL(\"MSG_WAITALL\"), MSG_WAITALL);\n\n    zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL(\"SHUT_RDWR\"), SHUT_RDWR);\n    zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL(\"SHUT_RD\"), SHUT_RD);\n    zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL(\"SHUT_WR\"), SHUT_WR);\n\n    zend::add_constant(\"SWOOLE_SYNC\", SW_FLAG_SYNC);\n    zend::add_constant(\"SWOOLE_ASYNC\", SW_FLAG_ASYNC);\n    zend::add_constant(\"SWOOLE_KEEP\", SW_FLAG_KEEP);\n}\n\nvoid php_swoole_client_check_ssl_setting(const Client *cli, const zval *zset) {\n    HashTable *vht = Z_ARRVAL_P(zset);\n    zval *ztmp;\n\n    if (php_swoole_array_get_value(vht, \"ssl_protocols\", ztmp)) {\n        cli->set_ssl_protocols(zval_get_long(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_compress\", ztmp)) {\n        cli->set_ssl_disable_compress(!zval_is_true(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_cert_file\", ztmp)) {\n        zend::String str_v(ztmp);\n        if (!cli->set_ssl_cert_file(str_v.to_std_string())) {\n            php_swoole_fatal_error(E_ERROR, \"ssl cert file[%s] not found\", str_v.val());\n            return;\n        }\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_key_file\", ztmp)) {\n        zend::String str_v(ztmp);\n        if (!cli->set_ssl_key_file(str_v.to_std_string())) {\n            php_swoole_fatal_error(E_ERROR, \"ssl key file[%s] not found\", str_v.val());\n            return;\n        }\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_passphrase\", ztmp)) {\n        zend::String str_v(ztmp);\n        cli->set_ssl_passphrase(str_v.to_std_string());\n    }\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n    if (php_swoole_array_get_value(vht, \"ssl_host_name\", ztmp)) {\n        zend::String str_v(ztmp);\n        cli->set_tls_host_name(str_v.to_std_string());\n    }\n#endif\n    if (php_swoole_array_get_value(vht, \"ssl_verify_peer\", ztmp)) {\n        cli->set_ssl_verify_peer(zval_is_true(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_allow_self_signed\", ztmp)) {\n        cli->set_ssl_allow_self_signed(zval_is_true(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_cafile\", ztmp)) {\n        zend::String str_v(ztmp);\n        cli->set_ssl_cafile(str_v.to_std_string());\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_capath\", ztmp)) {\n        zend::String str_v(ztmp);\n        cli->set_ssl_capath(str_v.to_std_string());\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_verify_depth\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        cli->set_ssl_verify_depth(SW_MAX(0, SW_MIN(v, UINT8_MAX)));\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_ciphers\", ztmp)) {\n        zend::String str_v(ztmp);\n        cli->set_ssl_ciphers(str_v.to_std_string());\n    }\n    if (!cli->get_ssl_cert_file().empty() && cli->get_ssl_key_file().empty()) {\n        php_swoole_fatal_error(E_ERROR, \"ssl require key file\");\n        return;\n    }\n    if (!cli->get_ssl_key_file().empty() && cli->get_ssl_cert_file().empty()) {\n        php_swoole_fatal_error(E_ERROR, \"ssl require cert file\");\n        return;\n    }\n}\n\nbool php_swoole_client_check_setting(Client *cli, const zval *zset) {\n    zval *ztmp;\n    int value = 1;\n    HashTable *vht = Z_ARRVAL_P(zset);\n\n    // buffer: eof check\n    if (php_swoole_array_get_value(vht, \"open_eof_check\", ztmp)) {\n        cli->open_eof_check = zval_is_true(ztmp);\n    }\n    // buffer: split package with eof\n    if (php_swoole_array_get_value(vht, \"open_eof_split\", ztmp)) {\n        cli->protocol.split_by_eof = zval_is_true(ztmp);\n        if (cli->protocol.split_by_eof) {\n            cli->open_eof_check = true;\n        }\n    }\n    // package eof\n    if (php_swoole_array_get_value(vht, \"package_eof\", ztmp)) {\n        zend::String str_v(ztmp);\n        cli->protocol.package_eof_len = str_v.len();\n        if (cli->protocol.package_eof_len == 0) {\n            php_swoole_fatal_error(E_ERROR, \"package_eof cannot be an empty string\");\n            return false;\n        } else if (cli->protocol.package_eof_len > SW_DATA_EOF_MAXLEN) {\n            php_swoole_fatal_error(E_ERROR, \"package_eof max length is %d\", SW_DATA_EOF_MAXLEN);\n            return false;\n        }\n        memcpy(cli->protocol.package_eof, str_v.val(), str_v.len());\n    }\n    // open mqtt protocol\n    if (php_swoole_array_get_value(vht, \"open_mqtt_protocol\", ztmp)) {\n        cli->open_length_check = zval_is_true(ztmp);\n        if (zval_is_true(ztmp)) {\n            swoole::mqtt::set_protocol(&cli->protocol);\n        }\n    }\n    // open length check\n    if (php_swoole_array_get_value(vht, \"open_length_check\", ztmp)) {\n        cli->open_length_check = zval_is_true(ztmp);\n        cli->protocol.get_package_length = Protocol::default_length_func;\n    }\n    // package length size\n    if (php_swoole_array_get_value(vht, \"package_length_type\", ztmp)) {\n        zend::String str_v(ztmp);\n        cli->protocol.package_length_type = str_v.val()[0];\n        cli->protocol.package_length_size = swoole_type_size(cli->protocol.package_length_type);\n\n        if (cli->protocol.package_length_size == 0) {\n            php_swoole_fatal_error(E_ERROR,\n                                   \"Unknown package_length_type name '%c', see pack(). Link: https://php.net/pack\",\n                                   cli->protocol.package_length_type);\n            return false;\n        }\n    }\n    // package length offset\n    if (php_swoole_array_get_value(vht, \"package_length_offset\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        cli->protocol.package_length_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX));\n    }\n    // package body start\n    if (php_swoole_array_get_value(vht, \"package_body_offset\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        cli->protocol.package_body_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX));\n    }\n    // length function\n    if (php_swoole_array_get_value(vht, \"package_length_func\", ztmp)) {\n        auto fci_cache = sw_callable_create(ztmp);\n        if (!fci_cache) {\n            return false;\n        }\n        cli->protocol.get_package_length = php_swoole_length_func;\n        if (cli->protocol.private_data_1) {\n            sw_callable_free(cli->protocol.private_data_1);\n        }\n        cli->protocol.private_data_1 = fci_cache;\n        cli->protocol.package_length_size = 0;\n        cli->protocol.package_length_type = '\\0';\n        cli->protocol.package_length_offset = SW_IPC_BUFFER_SIZE;\n    }\n    /**\n     * package max length\n     */\n    if (php_swoole_array_get_value(vht, \"package_max_length\", ztmp)) {\n        zend_long v = php_swoole_parse_to_size(ztmp);\n        cli->protocol.package_max_length = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n    } else {\n        cli->protocol.package_max_length = SW_INPUT_BUFFER_SIZE;\n    }\n    /**\n     * socket send/recv buffer size\n     */\n    if (php_swoole_array_get_value(vht, \"socket_buffer_size\", ztmp)) {\n        zend_long v = php_swoole_parse_to_size(ztmp);\n        value = SW_MAX(1, SW_MIN(v, INT_MAX));\n        cli->socket->set_buffer_size(value);\n        cli->socket->buffer_size = value;\n    }\n    if (php_swoole_array_get_value(vht, \"buffer_high_watermark\", ztmp)) {\n        zend_long v = php_swoole_parse_to_size(ztmp);\n        value = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n        cli->buffer_high_watermark = value;\n    }\n    if (php_swoole_array_get_value(vht, \"buffer_low_watermark\", ztmp)) {\n        zend_long v = php_swoole_parse_to_size(ztmp);\n        value = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n        cli->buffer_low_watermark = value;\n    }\n    /**\n     * bind port\n     */\n    std::string bind_address;\n    int bind_port = 0;\n    if (php_swoole_array_get_value(vht, \"bind_port\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        bind_port = SW_MAX(0, SW_MIN(v, UINT16_MAX));\n    }\n    /**\n     * bind address\n     */\n    if (php_swoole_array_get_value(vht, \"bind_address\", ztmp)) {\n        bind_address = zend::String(ztmp).to_std_string();\n    }\n    if (!bind_address.empty() && cli->bind(bind_address, bind_port) < 0) {\n        return false;\n    }\n    /**\n     * client: tcp_nodelay\n     */\n    if (php_swoole_array_get_value(vht, \"open_tcp_nodelay\", ztmp)) {\n        if (zval_is_true(ztmp)) {\n            goto _open_tcp_nodelay;\n        }\n    } else {\n    _open_tcp_nodelay:\n        // The failure to set tcp_nodelay does not affect the normal operation of the client;\n        // therefore, only an error log is printed without returning false.\n        if (cli->socket->is_tcp() && !cli->socket->set_tcp_nodelay()) {\n            php_swoole_sys_error(E_WARNING, \"setsockopt(%d, TCP_NODELAY) failed\", cli->socket->fd);\n        }\n    }\n    /**\n     * socks5 proxy\n     */\n    if (php_swoole_array_get_value(vht, \"socks5_host\", ztmp)) {\n        zend::String host(ztmp);\n        if (php_swoole_array_get_value(vht, \"socks5_port\", ztmp)) {\n            auto socks5_port = zval_get_long(ztmp);\n            std::string username, password;\n            if (php_swoole_array_get_value(vht, \"socks5_username\", ztmp)) {\n                zend::String _value(ztmp);\n                username = _value.to_std_string();\n            }\n            if (php_swoole_array_get_value(vht, \"socks5_password\", ztmp)) {\n                zend::String _value(ztmp);\n                password = _value.to_std_string();\n            }\n            cli->set_socks5_proxy(host.to_std_string(), socks5_port, username, password);\n        } else {\n            php_swoole_fatal_error(E_WARNING, \"socks5_port should not be null\");\n            return false;\n        }\n    }\n    /**\n     * http proxy\n     */\n    else if (php_swoole_array_get_value(vht, \"http_proxy_host\", ztmp)) {\n        zend::String host(ztmp);\n        if (php_swoole_array_get_value(vht, \"http_proxy_port\", ztmp)) {\n            std::string username, password;\n            auto http_proxy_port = zval_get_long(ztmp);\n            if (php_swoole_array_get_value(vht, \"http_proxy_username\", ztmp) ||\n                php_swoole_array_get_value(vht, \"http_proxy_user\", ztmp)) {\n                zend::String _value(ztmp);\n                username = _value.to_std_string();\n            }\n            if (php_swoole_array_get_value(vht, \"http_proxy_password\", ztmp)) {\n                zend::String _value(ztmp);\n                password = _value.to_std_string();\n            }\n            cli->set_http_proxy(host.to_std_string(), http_proxy_port, username, password);\n        } else {\n            php_swoole_fatal_error(E_WARNING, \"http_proxy_port should not be null\");\n            return false;\n        }\n    }\n    /**\n     * ssl\n     */\n    if (cli->open_ssl) {\n        php_swoole_client_check_ssl_setting(cli, zset);\n    }\n    return true;\n}\n\nvoid php_swoole_client_free(const zval *zobject, Client *cli) {\n    if (cli->timer) {\n        swoole_timer_del(cli->timer);\n        cli->timer = nullptr;\n    }\n    if (cli->protocol.private_data_1) {\n        sw_callable_free(cli->protocol.private_data_1);\n        cli->protocol.private_data_1 = nullptr;\n    }\n    // long tcp connection, delete from connection pool\n    if (cli->keep) {\n        auto i = long_connections.find(cli->server_id);\n        if (i != long_connections.end()) {\n            std::queue<Client *> *q = i->second;\n            if (q->empty()) {\n                delete q;\n                long_connections.erase(cli->server_id);\n            }\n        }\n    }\n\n    delete cli;\n\n#ifdef SWOOLE_SOCKETS_SUPPORT\n    zval *zsocket = client_get_zsocket(zobject);\n    if (zsocket) {\n        sw_zval_free(zsocket);\n        client_set_zsocket(zobject, nullptr);\n    }\n#endif\n    // unset object\n    php_swoole_client_set_cli(zobject, nullptr);\n}\n\nssize_t php_swoole_length_func(const Protocol *protocol, Socket *_socket, PacketLength *pl) {\n    auto *cb = static_cast<zend::Callable *>(protocol->private_data_1);\n    zval zdata;\n    zval retval;\n    ssize_t ret = -1;\n\n    // TODO: reduce memory copy\n    ZVAL_STRINGL(&zdata, pl->buf, pl->buf_size);\n    if (UNEXPECTED(sw_zend_call_function_ex2(nullptr, cb->ptr(), 1, &zdata, &retval) != SUCCESS)) {\n        php_swoole_fatal_error(E_WARNING, \"length function handler error\");\n    } else {\n        ret = zval_get_long(&retval);\n        zval_ptr_dtor(&retval);\n    }\n    zval_ptr_dtor(&zdata);\n\n    return ret;\n}\n\nstatic Client *php_swoole_client_new(zval *zobject, char *host, int host_len, int port) {\n    zval *ztype = sw_zend_read_property_ex(Z_OBJCE_P(zobject), zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TYPE), 0);\n    if (ztype == nullptr || ZVAL_IS_NULL(ztype)) {\n        php_swoole_fatal_error(E_ERROR, \"failed to get swoole_client->type\");\n        return nullptr;\n    }\n\n    long type = Z_LVAL_P(ztype);\n    int socket_type = php_swoole_client_get_type(type);\n    if (Socket::is_tcp(static_cast<SocketType>(socket_type)) && !Address::verify_port(port)) {\n        php_swoole_fatal_error(E_WARNING, \"The port is invalid\");\n        swoole_set_last_error(SW_ERROR_INVALID_PARAMS);\n        return nullptr;\n    }\n\n    Client *cli;\n    std::string conn_key;\n    zval *zconnection_id =\n        sw_zend_read_property_not_null_ex(Z_OBJCE_P(zobject), zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_ID), 1);\n\n    if (zconnection_id && Z_TYPE_P(zconnection_id) == IS_STRING && Z_STRLEN_P(zconnection_id) > 0) {\n        conn_key = std::string(Z_STRVAL_P(zconnection_id), Z_STRLEN_P(zconnection_id));\n    } else {\n        size_t size = sw_snprintf(sw_tg_buffer()->str, sw_tg_buffer()->size, \"%s:%d\", host, port);\n        conn_key = std::string(sw_tg_buffer()->str, size);\n    }\n\n    // keep the tcp connection\n    if (type & SW_FLAG_KEEP) {\n        auto i = long_connections.find(conn_key);\n        if (i == long_connections.end() || i->second->empty()) {\n            goto _create_socket;\n        } else {\n            std::queue<Client *> *q = i->second;\n            cli = q->front();\n            q->pop();\n            if (!cli->socket->check_liveness()) {\n                cli->close();\n                php_swoole_client_free(zobject, cli);\n                goto _create_socket;\n            }\n            cli->reuse_count++;\n            zend_update_property_long(\n                Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL(\"reuseCount\"), cli->reuse_count);\n        }\n    } else {\n    _create_socket:\n        cli = new Client(php_swoole_client_get_type(type), false);\n        if (cli->socket == nullptr) {\n            php_swoole_sys_error(E_WARNING, \"Client_create() failed\");\n            zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL(\"errCode\"), errno);\n            delete cli;\n            return nullptr;\n        }\n\n        cli->server_id = std::string(conn_key.c_str(), conn_key.length());\n    }\n\n    zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL(\"sock\"), cli->socket->fd);\n\n    if (type & SW_FLAG_KEEP) {\n        cli->keep = true;\n    }\n\n    if (type & SW_SOCK_SSL) {\n        cli->enable_ssl_encrypt();\n    }\n\n    return cli;\n}\n\nstatic PHP_METHOD(swoole_client, __construct) {\n    zend_long type = 0;\n    zend_bool async = false;\n    char *id = nullptr;\n    size_t len = 0;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"l|bs\", &type, &async, &id, &len) == FAILURE) {\n        zend_throw_error(nullptr, \"socket type param is required\");\n        RETURN_FALSE;\n    }\n\n    if (async) {\n        zend_throw_error(nullptr, \"The $async parameter is not supported\");\n        RETURN_FALSE;\n    }\n\n    int client_type = php_swoole_client_get_type(type);\n    if (client_type < SW_SOCK_TCP || client_type > SW_SOCK_UNIX_DGRAM) {\n        const char *space, *class_name = get_active_class_name(&space);\n        zend_type_error(\"%s%s%s() expects parameter %d to be client type, unknown type \" ZEND_LONG_FMT \" given\",\n                        class_name,\n                        space,\n                        get_active_function_name(),\n                        1,\n                        type);\n        RETURN_FALSE;\n    }\n\n    zend_update_property_long(swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"type\"), type);\n    if (id) {\n        zend_update_property_stringl(swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"id\"), id, len);\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_client, __destruct) {\n    SW_PREVENT_USER_DESTRUCT();\n\n    Client *cli = php_swoole_client_get_cli(ZEND_THIS);\n    // no keep connection\n    if (cli) {\n        sw_zend_call_method_with_0_params(ZEND_THIS, swoole_client_ce, nullptr, \"close\", nullptr);\n    }\n}\n\nstatic PHP_METHOD(swoole_client, set) {\n    zval *zset;\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"z\", &zset) == FAILURE) {\n        RETURN_FALSE;\n    }\n    if (!ZVAL_IS_ARRAY(zset)) {\n        RETURN_FALSE;\n    }\n\n    zval *zsetting = sw_zend_read_and_convert_property_array(swoole_client_ce, ZEND_THIS, ZEND_STRL(\"setting\"), 0);\n    php_array_merge(Z_ARRVAL_P(zsetting), Z_ARRVAL_P(zset));\n\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_client, connect) {\n    char *host;\n    size_t host_len;\n    zend_long port = 0;\n    double timeout = SW_CLIENT_CONNECT_TIMEOUT;\n    zend_long sock_flag = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 4)\n    Z_PARAM_STRING(host, host_len)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(port)\n    Z_PARAM_DOUBLE(timeout)\n    Z_PARAM_LONG(sock_flag)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (host_len == 0) {\n        php_swoole_fatal_error(E_WARNING, \"The host is empty\");\n        RETURN_FALSE;\n    }\n\n    Client *cli = php_swoole_client_get_cli(ZEND_THIS);\n    if (cli) {\n        php_swoole_fatal_error(E_WARNING, \"connection to the server has already been established\");\n        RETURN_FALSE;\n    }\n\n    cli = php_swoole_client_new(ZEND_THIS, host, host_len, port);\n    if (cli == nullptr) {\n        RETURN_FALSE;\n    }\n    php_swoole_client_set_cli(ZEND_THIS, cli);\n\n    if (cli->keep && cli->active) {\n        zend_update_property_bool(swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"reuse\"), 1);\n        RETURN_TRUE;\n    } else if (cli->active == 1) {\n        php_swoole_fatal_error(E_WARNING, \"connection to the server has already been established\");\n        RETURN_FALSE;\n    }\n\n    zval *zset = sw_zend_read_property_ex(swoole_client_ce, ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0);\n    if (zset && ZVAL_IS_ARRAY(zset)) {\n        swoole_set_last_error(0);\n        if (!php_swoole_client_check_setting(cli, zset)) {\n            zend_update_property_long(\n                swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"errCode\"), swoole_get_last_error());\n            RETURN_FALSE;\n        }\n    }\n\n    if (cli->connect(host, port, timeout, sock_flag) < 0) {\n        zend_update_property_long(\n            swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"errCode\"), swoole_get_last_error());\n        // async connect\n        if (cli->async_connect) {\n            RETURN_TRUE;\n        }\n        php_swoole_core_error(E_WARNING,\n                              \"connect to server[%s:%d] failed. Error: %s[%d]\",\n                              host,\n                              (int) port,\n                              swoole_strerror(swoole_get_last_error()),\n                              swoole_get_last_error());\n        php_swoole_client_free(ZEND_THIS, cli);\n        RETURN_FALSE;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_client, send) {\n    char *data;\n    size_t data_len;\n    zend_long flags = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_STRING(data, data_len)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(flags)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (data_len == 0) {\n        php_swoole_fatal_error(E_WARNING, \"data to send is empty\");\n        RETURN_FALSE;\n    }\n\n    Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS);\n    if (!cli) {\n        RETURN_FALSE;\n    }\n\n    // clear errno\n    swoole_set_last_error(0);\n    ssize_t ret = cli->send(data, data_len, flags);\n    if (ret < 0) {\n        php_swoole_sys_error(E_WARNING, \"failed to send(%d) %zu bytes\", cli->socket->fd, data_len);\n        zend_update_property_long(\n            swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"errCode\"), swoole_get_last_error());\n        RETVAL_FALSE;\n    } else {\n        RETURN_LONG(ret);\n    }\n}\n\nstatic PHP_METHOD(swoole_client, sendto) {\n    char *host;\n    size_t host_len;\n    long port;\n    char *data;\n    size_t len;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"sls\", &host, &host_len, &port, &data, &len) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    if (len == 0) {\n        php_swoole_error(E_WARNING, \"data to send is empty\");\n        RETURN_FALSE;\n    }\n\n    Client *cli = php_swoole_client_get_cli(ZEND_THIS);\n    if (!cli) {\n        cli = php_swoole_client_new(ZEND_THIS, host, host_len, port);\n        if (cli == nullptr) {\n            RETURN_FALSE;\n        }\n        cli->active = true;\n        php_swoole_client_set_cli(ZEND_THIS, cli);\n    }\n\n    auto rv = cli->sendto(std::string(host, host_len), port, data, len);\n    if (rv < 0) {\n        zend::object_set(ZEND_THIS, ZEND_STRL(\"errCode\"), swoole_get_last_error());\n    }\n    SW_CHECK_RETURN(rv);\n}\n\nstatic PHP_METHOD(swoole_client, sendfile) {\n    char *file;\n    size_t file_len;\n    zend_long offset = 0;\n    zend_long length = 0;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"s|ll\", &file, &file_len, &offset, &length) == FAILURE) {\n        RETURN_FALSE;\n    }\n    if (file_len == 0) {\n        php_swoole_fatal_error(E_WARNING, \"file to send is empty\");\n        RETURN_FALSE;\n    }\n\n    Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS);\n    if (!cli) {\n        RETURN_FALSE;\n    }\n    // only stream socket can sendfile\n    if (!(cli->socket->is_stream())) {\n        php_swoole_error(E_WARNING, \"dgram socket cannot use sendfile\");\n        RETURN_FALSE;\n    }\n    // clear errno\n    swoole_set_last_error(0);\n    int ret = cli->sendfile(file, offset, length);\n    if (ret < 0) {\n        swoole_set_last_error(errno);\n        php_swoole_fatal_error(E_WARNING,\n                               \"sendfile() failed. Error: %s [%d]\",\n                               swoole_strerror(swoole_get_last_error()),\n                               swoole_get_last_error());\n        zend_update_property_long(\n            swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"errCode\"), swoole_get_last_error());\n        RETVAL_FALSE;\n    } else {\n        RETVAL_TRUE;\n    }\n}\n\nstatic PHP_METHOD(swoole_client, recv) {\n    zend_long buf_len = SW_PHP_CLIENT_BUFFER_SIZE;\n    zend_long flags = 0;\n    int ret;\n    zend_string *strbuf = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(0, 2)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(buf_len)\n    Z_PARAM_LONG(flags)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    // waitall\n    if (flags == 1) {\n        flags = MSG_WAITALL;\n    }\n\n    Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS);\n    if (!cli) {\n        RETURN_FALSE;\n    }\n\n    Protocol *protocol = &cli->protocol;\n\n    if (cli->open_eof_check) {\n        if (cli->buffer == nullptr) {\n            cli->buffer = swoole::make_string(SW_BUFFER_SIZE_BIG, sw_zend_string_allocator());\n        }\n\n        String *buffer = cli->buffer;\n        ssize_t eof = -1;\n        char *buf = nullptr;\n\n        if (buffer->length > 0) {\n            goto _find_eof;\n        }\n\n        while (true) {\n            buf = buffer->str + buffer->length;\n            buf_len = buffer->size - buffer->length;\n\n            if (buf_len > SW_BUFFER_SIZE_BIG) {\n                buf_len = SW_BUFFER_SIZE_BIG;\n            }\n\n            ret = cli->recv(buf, buf_len, 0);\n            if (ret < 0) {\n                php_swoole_sys_error(E_WARNING, \"recv() failed\");\n                zend_update_property_long(\n                    swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"errCode\"), swoole_get_last_error());\n                buffer->length = 0;\n                RETURN_FALSE;\n            } else if (ret == 0) {\n                buffer->length = 0;\n                RETURN_EMPTY_STRING();\n            }\n\n            buffer->length += ret;\n\n            if (buffer->length < protocol->package_eof_len) {\n                continue;\n            }\n\n        _find_eof:\n            eof = swoole_strnpos(buffer->str, buffer->length, protocol->package_eof, protocol->package_eof_len);\n            if (eof >= 0) {\n                eof += protocol->package_eof_len;\n\n                if ((ssize_t) buffer->length > eof) {\n                    cli->buffer = swoole::make_string(SW_BUFFER_SIZE_BIG, sw_zend_string_allocator());\n                    cli->buffer->length = buffer->length - eof;\n                    memcpy(cli->buffer->str, buffer->str + eof, cli->buffer->length);\n                } else {\n                    cli->buffer = nullptr;\n                    buffer->length = 0;\n                }\n\n                zend::assign_zend_string_by_val(return_value, buffer->str, eof);\n                buffer->str = nullptr;\n                delete buffer;\n\n                return;\n            } else {\n                if (buffer->length == protocol->package_max_length) {\n                    php_swoole_error(E_WARNING, \"no package eof\");\n                    buffer->length = 0;\n                    RETURN_FALSE;\n                } else if (buffer->length == buffer->size) {\n                    if (buffer->size < protocol->package_max_length) {\n                        uint32_t new_size = buffer->size * 2;\n                        if (new_size > protocol->package_max_length) {\n                            new_size = protocol->package_max_length;\n                        }\n                        buffer->extend(new_size);\n                    }\n                }\n            }\n        }\n        buffer->length = 0;\n        RETURN_FALSE;\n    } else if (cli->open_length_check) {\n        if (cli->buffer == nullptr) {\n            cli->buffer = new String(SW_BUFFER_SIZE_STD);\n        } else {\n            cli->buffer->clear();\n        }\n        String *buffer = cli->buffer;\n\n        uint32_t header_len = protocol->package_length_offset + protocol->package_length_size;\n\n        while (true) {\n            auto retval = cli->recv(buffer->str + buffer->length, header_len - buffer->length, 0);\n            if (retval <= 0) {\n                break;\n            }\n            buffer->length += retval;\n            PacketLength pl{\n                buffer->str,\n                (uint32_t) buffer->length,\n            };\n            buf_len = protocol->get_package_length(protocol, cli->socket, &pl);\n            if (buf_len == 0) {\n                continue;\n            } else if (buf_len < 0) {\n                break;\n            } else {\n                break;\n            }\n        }\n\n        // error package\n        if (buf_len < 0) {\n            RETURN_EMPTY_STRING();\n        }\n        // empty package\n        else if (buf_len == header_len) {\n            RETURN_STRINGL(buffer->str, header_len);\n        } else if (buf_len > protocol->package_max_length) {\n            swoole_error_log(SW_LOG_WARNING,\n                             SW_ERROR_PACKAGE_LENGTH_TOO_LARGE,\n                             \"Package is too big. package_length=%d\",\n                             (int) buf_len);\n            RETURN_EMPTY_STRING();\n        } else if (buf_len == (zend_long) buffer->length) {\n            RETURN_STRINGL(buffer->str, buffer->length);\n        } else if (buf_len < (zend_long) buffer->length) {\n            RETVAL_STRINGL(buffer->str, buf_len);\n            memmove(buffer->str, buffer->str + buf_len, buffer->length - buf_len);\n            buffer->length -= buf_len;\n            return;\n        }\n\n        strbuf = zend_string_alloc(buf_len, false);\n        memcpy(strbuf->val, buffer->str, buffer->length);\n        swoole_set_last_error(0);\n        ret = cli->recv(strbuf->val + header_len, buf_len - buffer->length, MSG_WAITALL);\n        if (ret > 0) {\n            ret += header_len;\n            if (ret != buf_len) {\n                ret = 0;\n            }\n        }\n    } else {\n        if (!(flags & MSG_WAITALL) && buf_len > SW_PHP_CLIENT_BUFFER_SIZE) {\n            buf_len = SW_PHP_CLIENT_BUFFER_SIZE;\n        }\n        strbuf = zend_string_alloc(buf_len, false);\n        swoole_set_last_error(0);\n        ret = cli->recv(strbuf->val, buf_len, flags);\n    }\n\n    if (ret < 0) {\n        php_swoole_sys_error(E_WARNING, \"recv() failed\");\n        zend_update_property_long(\n            swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"errCode\"), swoole_get_last_error());\n        if (strbuf) {\n            zend_string_free(strbuf);\n        }\n        RETURN_FALSE;\n    } else {\n        if (ret == 0) {\n            if (strbuf) {\n                zend_string_free(strbuf);\n            }\n            RETURN_EMPTY_STRING();\n        } else {\n            strbuf->len = ret;\n            strbuf->val[ret] = 0;\n            RETVAL_STR(strbuf);\n        }\n    }\n}\n\nstatic PHP_METHOD(swoole_client, isConnected) {\n    Client *cli = php_swoole_client_get_cli(ZEND_THIS);\n    if (!cli) {\n        RETURN_FALSE;\n    }\n    if (!cli->socket) {\n        RETURN_FALSE;\n    }\n    RETURN_BOOL(cli->active);\n}\n\nstatic PHP_METHOD(swoole_client, getsockname) {\n    Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS);\n    if (!cli) {\n        RETURN_FALSE;\n    }\n\n    if (cli->socket->get_name() < 0) {\n        php_swoole_sys_error(E_WARNING, \"getsockname() failed\");\n        zend::object_set(ZEND_THIS, ZEND_STRL(\"errCode\"), errno);\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n    add_assoc_long(return_value, \"port\", cli->socket->get_port());\n    add_assoc_string(return_value, \"host\", cli->socket->get_addr());\n}\n\n#ifdef SWOOLE_SOCKETS_SUPPORT\nstatic PHP_METHOD(swoole_client, getSocket) {\n    zval *zsocket = client_get_zsocket(ZEND_THIS);\n    if (zsocket) {\n        RETURN_ZVAL(zsocket, 1, 0);\n    }\n    Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS);\n    if (!cli) {\n        RETURN_FALSE;\n    }\n    if (cli->keep) {\n        php_swoole_fatal_error(E_WARNING, \"the 'getSocket' method can't be used on persistent connection\");\n        RETURN_FALSE;\n    }\n    php_socket *socket_object = php_swoole_convert_to_socket(cli->socket->fd);\n    if (!socket_object) {\n        RETURN_FALSE;\n    }\n    SW_ZVAL_SOCKET(return_value, socket_object);\n    zsocket = sw_zval_dup(return_value);\n    Z_TRY_ADDREF_P(zsocket);\n    client_set_zsocket(ZEND_THIS, zsocket);\n}\n#endif\n\nstatic PHP_METHOD(swoole_client, getpeername) {\n    Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS);\n    if (!cli) {\n        RETURN_FALSE;\n    }\n\n    Address addr;\n    if (cli->get_peer_name(&addr) < 0) {\n        php_swoole_sys_error(E_WARNING, \"getpeername() failed\");\n        zend::object_set(ZEND_THIS, ZEND_STRL(\"errCode\"), errno);\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n    add_assoc_long(return_value, \"port\", addr.get_port());\n    add_assoc_string(return_value, \"host\", addr.get_addr());\n}\n\nstatic PHP_METHOD(swoole_client, close) {\n    int ret = 1;\n    zend_bool force = false;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_BOOL(force)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    Client *cli = php_swoole_client_get_cli(ZEND_THIS);\n    if (!cli || !cli->socket) {\n        php_swoole_fatal_error(E_WARNING, \"client is not connected to the server\");\n        RETURN_FALSE;\n    }\n    if (cli->closed) {\n        php_swoole_error(E_WARNING, \"client socket is closed\");\n        RETURN_FALSE;\n    }\n    // Connection error, or short tcp connection.\n    // No keep connection\n    if (force || !cli->keep || cli->socket->catch_error(swoole_get_last_error()) == SW_CLOSE) {\n        ret = cli->close();\n        php_swoole_client_free(ZEND_THIS, cli);\n    } else {\n        if (cli->keep) {\n            std::queue<Client *> *q;\n            auto i = long_connections.find(cli->server_id);\n            if (i == long_connections.end()) {\n                q = new std::queue<Client *>;\n                long_connections[cli->server_id] = q;\n            } else {\n                q = i->second;\n            }\n            q->push(cli);\n        }\n        // unset object\n        php_swoole_client_set_cli(ZEND_THIS, nullptr);\n    }\n    SW_CHECK_RETURN(ret);\n}\n\nbool php_swoole_client_enable_ssl_encryption(Client *cli, zval *zobject) {\n    if (cli->socket->socket_type != SW_SOCK_TCP && cli->socket->socket_type != SW_SOCK_TCP6) {\n        php_swoole_fatal_error(E_WARNING, \"cannot use enableSSL\");\n        return false;\n    }\n    if (cli->socket->ssl) {\n        php_swoole_fatal_error(E_WARNING, \"SSL has been enabled\");\n        return false;\n    }\n    cli->open_ssl = true;\n    zval *zset = sw_zend_read_property_ex(swoole_client_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0);\n    if (ZVAL_IS_ARRAY(zset)) {\n        php_swoole_client_check_ssl_setting(cli, zset);\n    }\n    return cli->enable_ssl_encrypt() == SW_OK;\n}\n\nstatic PHP_METHOD(swoole_client, enableSSL) {\n    zval *zcallback = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_ZVAL(zcallback)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (zcallback) {\n        zend_throw_exception(\n            swoole_exception_ce, \"sync client does not support `onSslReady` callback\", SW_ERROR_INVALID_PARAMS);\n        RETURN_FALSE;\n    }\n\n    Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS);\n    if (!cli) {\n        RETURN_FALSE;\n    }\n    if (!php_swoole_client_enable_ssl_encryption(cli, ZEND_THIS)) {\n        RETURN_FALSE;\n    }\n    RETURN_BOOL(cli->ssl_handshake() == SW_OK);\n}\n\nstatic PHP_METHOD(swoole_client, getPeerCert) {\n    Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS);\n    if (!cli) {\n        RETURN_FALSE;\n    }\n    if (!cli->socket->ssl) {\n        php_swoole_fatal_error(E_WARNING, \"SSL is not ready\");\n        RETURN_FALSE;\n    }\n    if (!cli->socket->ssl_get_peer_certificate(sw_tg_buffer())) {\n        RETURN_FALSE;\n    }\n    RETURN_SW_STRING(sw_tg_buffer());\n}\n\nstatic PHP_METHOD(swoole_client, verifyPeerCert) {\n    Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS);\n    if (!cli) {\n        RETURN_FALSE;\n    }\n    if (!cli->socket->ssl) {\n        php_swoole_fatal_error(E_WARNING, \"SSL is not ready\");\n        RETURN_FALSE;\n    }\n    zend_bool allow_self_signed = false;\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"|b\", &allow_self_signed) == FAILURE) {\n        RETURN_FALSE;\n    }\n    SW_CHECK_RETURN(cli->ssl_verify(allow_self_signed));\n}\n\nstatic PHP_METHOD(swoole_client, shutdown) {\n    Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS);\n    if (!cli) {\n        RETURN_FALSE;\n    }\n    long _how;\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"l\", &_how) == FAILURE) {\n        RETURN_FALSE;\n    }\n    SW_CHECK_RETURN(cli->shutdown(_how));\n}\n\nPHP_FUNCTION(swoole_client_select) {\n    zval *r_array, *w_array, *e_array;\n    int retval;\n    uint32_t index = 0;\n    double timeout = SW_CLIENT_CONNECT_TIMEOUT;\n\n    ZEND_PARSE_PARAMETERS_START(3, 4)\n    Z_PARAM_ARRAY_EX2(r_array, 1, 1, 0)\n    Z_PARAM_ARRAY_EX2(w_array, 1, 1, 0)\n    Z_PARAM_ARRAY_EX2(e_array, 1, 1, 0)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END();\n\n    int maxevents = SW_MAX(SW_MAX(php_swoole_array_length_safe(r_array), php_swoole_array_length_safe(w_array)),\n                           php_swoole_array_length_safe(e_array));\n    auto *fds = static_cast<struct pollfd *>(ecalloc(maxevents, sizeof(struct pollfd)));\n\n    if (r_array != nullptr && php_swoole_array_length(r_array) > 0) {\n        index = client_poll_add(r_array, index, fds, maxevents, POLLIN);\n    }\n    if (w_array != nullptr && php_swoole_array_length(w_array) > 0) {\n        index = client_poll_add(w_array, index, fds, maxevents, POLLOUT);\n    }\n    if (e_array != nullptr && php_swoole_array_length(e_array) > 0) {\n        index = client_poll_add(e_array, index, fds, maxevents, POLLHUP);\n    }\n    if (index == 0) {\n        efree(fds);\n        php_swoole_fatal_error(E_WARNING, \"no resource arrays were passed to select\");\n        RETURN_FALSE;\n    }\n\n    do {\n        retval = poll(fds, maxevents, (int) (timeout * 1000));\n    } while (retval < 0 && errno == EINTR);\n\n    if (retval == -1) {\n        efree(fds);\n        php_swoole_sys_error(E_WARNING, \"unable to poll()\");\n        RETURN_FALSE;\n    }\n\n    if (r_array != nullptr && php_swoole_array_length(r_array) > 0) {\n        client_poll_wait(r_array, fds, maxevents, retval, POLLIN);\n    }\n    if (w_array != nullptr && php_swoole_array_length(w_array) > 0) {\n        client_poll_wait(w_array, fds, maxevents, retval, POLLOUT);\n    }\n    if (e_array != nullptr && php_swoole_array_length(e_array) > 0) {\n        client_poll_wait(e_array, fds, maxevents, retval, POLLHUP);\n    }\n    efree(fds);\n    RETURN_LONG(retval);\n}\n\nstatic inline int client_poll_get(const pollfd *fds, int maxevents, int fd) {\n    for (int i = 0; i < maxevents; i++) {\n        if (fds[i].fd == fd) {\n            return i;\n        }\n    }\n    return -1;\n}\n\nstatic int client_poll_wait(zval *sock_array, const pollfd *fds, int maxevents, int n_event, int revent) {\n    zval *element = nullptr;\n\n    ulong_t num = 0;\n    if (!ZVAL_IS_ARRAY(sock_array)) {\n        return 0;\n    }\n\n    zval new_array;\n    array_init(&new_array);\n    zend_ulong num_key;\n    zend_string *key;\n    zval *dest_element;\n\n    ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(sock_array), num_key, key, element) {\n        int sock = php_swoole_convert_to_fd(element);\n        if (sock < 0) {\n            continue;\n        }\n        int poll_key = client_poll_get(fds, maxevents, sock);\n        if (poll_key == -1) {\n            php_swoole_fatal_error(E_WARNING, \"bad fd[%d]\", sock);\n            continue;\n        }\n        if (!(fds[poll_key].revents & revent)) {\n            continue;\n        }\n        if (key) {\n            dest_element = zend_hash_add(Z_ARRVAL(new_array), key, element);\n        } else {\n            dest_element = zend_hash_index_update(Z_ARRVAL(new_array), num_key, element);\n        }\n        if (dest_element) {\n            Z_ADDREF_P(dest_element);\n        }\n        num++;\n    }\n    ZEND_HASH_FOREACH_END();\n\n    zval_ptr_dtor(sock_array);\n    ZVAL_COPY_VALUE(sock_array, &new_array);\n    return num;\n}\n\nstatic uint32_t client_poll_add(const zval *sock_array, uint32_t index, struct pollfd *fds, int maxevents, int event) {\n    zval *element = nullptr;\n    if (!ZVAL_IS_ARRAY(sock_array)) {\n        return 0;\n    }\n\n    int key = -1;\n\n    SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(sock_array), element)\n    int sock = php_swoole_convert_to_fd(element);\n    if (sock < 0) {\n        continue;\n    }\n    if (event != POLLIN) {\n        key = client_poll_get(fds, maxevents, sock);\n    }\n    if (key < 0) {\n        fds[index].fd = sock;\n        fds[index].events = event;\n        index++;\n    } else {\n        fds[key].fd = sock;\n        fds[key].events |= event;\n    }\n    SW_HASHTABLE_FOREACH_END();\n\n    return index;\n}\n"
  },
  {
    "path": "ext-src/swoole_client_async.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_client.h\"\n#include \"swoole_mqtt.h\"\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_client_async_arginfo.h\"\nEND_EXTERN_C()\n\n#include \"ext/standard/basic_functions.h\"\n\nusing swoole::SocketType;\nusing swoole::network::Address;\nusing swoole::network::Client;\nusing swoole::network::Socket;\n\nstatic PHP_METHOD(swoole_client_async, __construct);\nstatic PHP_METHOD(swoole_client_async, __destruct);\nstatic PHP_METHOD(swoole_client_async, connect);\nstatic PHP_METHOD(swoole_client_async, sleep);\nstatic PHP_METHOD(swoole_client_async, wakeup);\nstatic PHP_METHOD(swoole_client_async, enableSSL);\nstatic PHP_METHOD(swoole_client_async, isConnected);\nstatic PHP_METHOD(swoole_client_async, close);\nstatic PHP_METHOD(swoole_client_async, on);\n\nenum AsyncClientCallbackType {\n    CLIENT_CB_onConnect = 1,\n    CLIENT_CB_onReceive,\n    CLIENT_CB_onClose,\n    CLIENT_CB_onError,\n    CLIENT_CB_onBufferFull,\n    CLIENT_CB_onBufferEmpty,\n    CLIENT_CB_onSSLReady,\n};\n\nstatic void client_onConnect(Client *cli);\nstatic void client_onReceive(const Client *cli, const char *data, size_t length);\nstatic void client_onClose(Client *cli);\nstatic void client_onError(Client *cli);\nstatic void client_onBufferFull(Client *cli);\nstatic void client_onBufferEmpty(Client *cli);\n\nzend_class_entry *swoole_client_async_ce;\nstatic zend_object_handlers swoole_client_async_handlers;\n\nvoid php_swoole_client_async_free_object(const ClientObject *client_obj) {\n    if (client_obj->async->onConnect) {\n        sw_callable_free(client_obj->async->onConnect);\n    }\n    if (client_obj->async->onReceive) {\n        sw_callable_free(client_obj->async->onReceive);\n    }\n    if (client_obj->async->onClose) {\n        sw_callable_free(client_obj->async->onClose);\n    }\n    if (client_obj->async->onError) {\n        sw_callable_free(client_obj->async->onError);\n    }\n    if (client_obj->async->onBufferFull) {\n        sw_callable_free(client_obj->async->onBufferFull);\n    }\n    if (client_obj->async->onBufferEmpty) {\n        sw_callable_free(client_obj->async->onBufferEmpty);\n    }\n    if (client_obj->async->onSSLReady) {\n        sw_callable_free(client_obj->async->onSSLReady);\n    }\n    delete client_obj->async;\n}\n\nstatic sw_inline void client_execute_callback(zval *zobject, AsyncClientCallbackType type) {\n    auto client_obj = php_swoole_client_fetch_object(zobject);\n    const char *callback_name;\n    zend::Callable *cb;\n\n    switch (type) {\n    case CLIENT_CB_onConnect:\n        callback_name = \"onConnect\";\n        cb = client_obj->async->onConnect;\n        break;\n    case CLIENT_CB_onError:\n        callback_name = \"onError\";\n        cb = client_obj->async->onError;\n        break;\n    case CLIENT_CB_onClose:\n        callback_name = \"onClose\";\n        cb = client_obj->async->onClose;\n        break;\n    case CLIENT_CB_onBufferFull:\n        callback_name = \"onBufferFull\";\n        cb = client_obj->async->onBufferFull;\n        break;\n    case CLIENT_CB_onBufferEmpty:\n        callback_name = \"onBufferEmpty\";\n        cb = client_obj->async->onBufferEmpty;\n        break;\n    case CLIENT_CB_onSSLReady:\n        callback_name = \"onSSLReady\";\n        cb = client_obj->async->onSSLReady;\n        break;\n    default:\n        abort();\n        return;\n    }\n\n    if (!cb) {\n        php_swoole_fatal_error(E_WARNING, \"%s has no %s callback\", SW_Z_OBJCE_NAME_VAL_P(zobject), callback_name);\n        return;\n    }\n\n    if (UNEXPECTED(sw_zend_call_function_ex2(nullptr, cb->ptr(), 1, zobject, nullptr) != SUCCESS)) {\n        php_swoole_fatal_error(E_WARNING, \"%s->%s handler error\", SW_Z_OBJCE_NAME_VAL_P(zobject), callback_name);\n    }\n}\n\nstatic sw_inline void client_execute_callback_and_dtor(Client *cli, zval *zobject, AsyncClientCallbackType type) {\n    client_execute_callback(zobject, type);\n    php_swoole_client_free(zobject, cli);\n    zval_delref_p(zobject);\n}\n\n// clang-format off\nstatic const zend_function_entry swoole_client_async_methods[] = {\n    PHP_ME(swoole_client_async, __construct, arginfo_class_Swoole_Async_Client___construct, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_async, __destruct, arginfo_class_Swoole_Async_Client___destruct, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_async, connect, arginfo_class_Swoole_Async_Client_connect, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_async, sleep, arginfo_class_Swoole_Async_Client_sleep, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_async, wakeup, arginfo_class_Swoole_Async_Client_wakeup, ZEND_ACC_PUBLIC)\n    PHP_MALIAS(swoole_client_async, pause, sleep, arginfo_class_Swoole_Async_Client_sleep, ZEND_ACC_PUBLIC)\n    PHP_MALIAS(swoole_client_async, resume, wakeup, arginfo_class_Swoole_Async_Client_wakeup, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_async, enableSSL, arginfo_class_Swoole_Async_Client_enableSSL, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_async, isConnected, arginfo_class_Swoole_Async_Client_isConnected, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_async, close, arginfo_class_Swoole_Async_Client_close, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_async, on, arginfo_class_Swoole_Async_Client_on, ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_client_async_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY_EX(\n        swoole_client_async, \"Swoole\\\\Async\\\\Client\", nullptr, swoole_client_async_methods, swoole_client);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_client_async);\n    SW_SET_CLASS_CLONEABLE(swoole_client_async, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_client_async, sw_zend_class_unset_property_deny);\n\n    zend_declare_property_null(swoole_client_async_ce, ZEND_STRL(\"onConnect\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_client_async_ce, ZEND_STRL(\"onError\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_client_async_ce, ZEND_STRL(\"onReceive\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_client_async_ce, ZEND_STRL(\"onClose\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_client_async_ce, ZEND_STRL(\"onBufferFull\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_client_async_ce, ZEND_STRL(\"onBufferEmpty\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_client_async_ce, ZEND_STRL(\"onSSLReady\"), ZEND_ACC_PRIVATE);\n}\n\nstatic void client_onReceive(const Client *cli, const char *data, size_t length) {\n    auto zobject = static_cast<zval *>(cli->object);\n    auto client_obj = php_swoole_client_fetch_object(zobject);\n    zend_fcall_info_cache *fci_cache = client_obj->async->onReceive->ptr();\n    zval args[2];\n\n    args[0] = *zobject;\n    ZVAL_STRINGL(&args[1], data, length);\n\n    if (UNEXPECTED(sw_zend_call_function_ex2(nullptr, fci_cache, 2, args, nullptr) != SUCCESS)) {\n        php_swoole_fatal_error(E_WARNING, \"%s->onReceive handler error\", SW_Z_OBJCE_NAME_VAL_P(zobject));\n    }\n\n    zval_ptr_dtor(&args[1]);\n}\n\nstatic void client_onConnect(Client *cli) {\n    auto zobject = static_cast<zval *>(cli->object);\n    if (cli->ssl_wait_handshake) {\n        cli->ssl_wait_handshake = false;\n        client_execute_callback(zobject, CLIENT_CB_onSSLReady);\n        return;\n    }\n    client_execute_callback(zobject, CLIENT_CB_onConnect);\n}\n\nstatic void client_onClose(Client *cli) {\n    auto zobject = static_cast<zval *>(cli->object);\n    client_execute_callback_and_dtor(cli, zobject, CLIENT_CB_onClose);\n}\n\nstatic void client_onError(Client *cli) {\n    auto zobject = static_cast<zval *>(cli->object);\n    zend_update_property_long(swoole_client_async_ce, Z_OBJ_P(zobject), ZEND_STRL(\"errCode\"), swoole_get_last_error());\n    client_execute_callback_and_dtor(cli, zobject, CLIENT_CB_onError);\n}\n\nstatic void client_onBufferFull(Client *cli) {\n    auto zobject = static_cast<zval *>(cli->object);\n    client_execute_callback(zobject, CLIENT_CB_onBufferFull);\n}\n\nstatic void client_onBufferEmpty(Client *cli) {\n    auto zobject = static_cast<zval *>(cli->object);\n    client_execute_callback(zobject, CLIENT_CB_onBufferEmpty);\n}\n\nstatic PHP_METHOD(swoole_client_async, __construct) {\n    zend_long type = 0;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"l\", &type) == FAILURE) {\n        zend_throw_error(nullptr, \"socket type param is required\");\n        RETURN_FALSE;\n    }\n\n    int client_type = php_swoole_client_get_type(type);\n    if (client_type < SW_SOCK_TCP || client_type > SW_SOCK_UNIX_DGRAM) {\n        const char *space, *class_name = get_active_class_name(&space);\n        zend_type_error(\"%s%s%s() expects parameter %d to be client type, unknown type \" ZEND_LONG_FMT \" given\",\n                        class_name,\n                        space,\n                        get_active_function_name(),\n                        1,\n                        type);\n        RETURN_FALSE;\n    }\n\n    php_swoole_check_reactor();\n\n    zend_update_property_long(swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"type\"), type);\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_client_async, __destruct) {\n    SW_PREVENT_USER_DESTRUCT();\n\n    const auto cli = php_swoole_client_get_cli(ZEND_THIS);\n    if (!cli) {\n        return;\n    }\n    if (!cli->closed) {\n        cli->close();\n    }\n    php_swoole_client_free(ZEND_THIS, cli);\n}\n\nstatic Client *php_swoole_client_async_new(zval *zobject, char *host, int host_len, int port) {\n    zval *ztype = sw_zend_read_property_ex(Z_OBJCE_P(zobject), zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TYPE), 0);\n    if (ztype == nullptr || ZVAL_IS_NULL(ztype)) {\n        php_swoole_fatal_error(E_ERROR, \"failed to get client type\");\n        return nullptr;\n    }\n\n    long type = Z_LVAL_P(ztype);\n    int client_type = php_swoole_client_get_type(type);\n    if (Socket::is_tcp(static_cast<SocketType>(client_type)) && !Address::verify_port(port, true)) {\n        php_swoole_fatal_error(E_WARNING, \"The port is invalid\");\n        swoole_set_last_error(SW_ERROR_INVALID_PARAMS);\n        return nullptr;\n    }\n\n    auto *cli = new Client(php_swoole_client_get_type(type), true);\n    if (!cli->ready()) {\n        php_swoole_sys_error(E_WARNING, \"failed to create client\");\n        zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL(\"errCode\"), errno);\n        delete cli;\n        return nullptr;\n    }\n\n    zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL(\"sock\"), cli->socket->fd);\n\n    if (type & SW_SOCK_SSL) {\n        cli->enable_ssl_encrypt();\n    }\n\n    return cli;\n}\n\nstatic PHP_METHOD(swoole_client_async, connect) {\n    char *host;\n    size_t host_len;\n    zend_long port = 0;\n    double timeout = SW_CLIENT_CONNECT_TIMEOUT;\n    zend_long sock_flag = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 4)\n    Z_PARAM_STRING(host, host_len)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(port)\n    Z_PARAM_DOUBLE(timeout)\n    Z_PARAM_LONG(sock_flag)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (host_len == 0) {\n        php_swoole_fatal_error(E_WARNING, \"The host is empty\");\n        RETURN_FALSE;\n    }\n\n    auto client_obj = php_swoole_client_fetch_object(ZEND_THIS);\n    if (client_obj->cli) {\n        php_swoole_fatal_error(E_WARNING, \"connection to the server has already been established\");\n        RETURN_FALSE;\n    }\n\n    if (!client_obj->async) {\n        php_swoole_fatal_error(E_WARNING, \"async client is not initialized\");\n        RETURN_FALSE;\n    }\n\n    auto cli = php_swoole_client_async_new(ZEND_THIS, host, host_len, port);\n    if (cli == nullptr) {\n        RETURN_FALSE;\n    }\n\n    zval *zset = sw_zend_read_property(swoole_client_async_ce, ZEND_THIS, ZEND_STRL(\"setting\"), 0);\n    if (zset && ZVAL_IS_ARRAY(zset)) {\n        php_swoole_client_check_setting(cli, zset);\n    }\n    if (!client_obj->async->onReceive) {\n        php_swoole_fatal_error(E_ERROR, \"no 'onReceive' callback function\");\n        RETURN_FALSE;\n    }\n    if (cli->get_socket()->is_stream()) {\n        if (!client_obj->async->onConnect) {\n            php_swoole_fatal_error(E_ERROR, \"no 'onConnect' callback function\");\n            RETURN_FALSE;\n        }\n        if (!client_obj->async->onError) {\n            php_swoole_fatal_error(E_ERROR, \"no 'onError' callback function\");\n            RETURN_FALSE;\n        }\n        if (!client_obj->async->onClose) {\n            php_swoole_fatal_error(E_ERROR, \"no 'onClose' callback function\");\n            RETURN_FALSE;\n        }\n        cli->onConnect = client_onConnect;\n        cli->onClose = client_onClose;\n        cli->onError = client_onError;\n        cli->onReceive = client_onReceive;\n        if (client_obj->async->onBufferFull) {\n            cli->onBufferFull = client_onBufferFull;\n        }\n        if (client_obj->async->onBufferEmpty) {\n            cli->onBufferEmpty = client_onBufferEmpty;\n        }\n    } else {\n        if (client_obj->async->onConnect) {\n            cli->onConnect = client_onConnect;\n        }\n        if (client_obj->async->onClose) {\n            cli->onClose = client_onClose;\n        }\n        if (client_obj->async->onError) {\n            cli->onError = client_onError;\n        }\n        cli->onReceive = client_onReceive;\n    }\n\n    client_obj->async->_zobject = *ZEND_THIS;\n    client_obj->cli = cli;\n    cli->object = &client_obj->async->_zobject;\n    zval_addref_p(ZEND_THIS);\n\n    // nonblock async\n    if (cli->connect(host, port, timeout, sock_flag) < 0) {\n        if (errno == 0) {\n            const auto error = swoole_get_last_error();\n            if (error == SW_ERROR_DNSLOOKUP_RESOLVE_FAILED) {\n                php_swoole_error(E_WARNING,\n                                 \"connect to server[%s:%d] failed. Error: %s[%d]\",\n                                 host,\n                                 (int) port,\n                                 swoole_strerror(error),\n                                 error);\n            }\n            zend_update_property_long(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL(\"errCode\"), error);\n        } else {\n            php_swoole_sys_error(E_WARNING, \"connect to server[%s:%d] failed\", host, (int) port);\n            zend_update_property_long(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL(\"errCode\"), errno);\n        }\n        auto _cli = php_swoole_client_get_cli(ZEND_THIS);\n        if (_cli && _cli->onError == nullptr) {\n            php_swoole_client_free(ZEND_THIS, _cli);\n            zval_delref_p(ZEND_THIS);\n        }\n        RETURN_FALSE;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_client_async, isConnected) {\n    auto cli = php_swoole_client_get_cli(ZEND_THIS);\n    if (!cli) {\n        RETURN_FALSE;\n    }\n    if (!cli->socket) {\n        RETURN_FALSE;\n    }\n    RETURN_BOOL(cli->active);\n}\n\nstatic PHP_METHOD(swoole_client_async, close) {\n    auto cli = php_swoole_client_get_cli(ZEND_THIS);\n    if (!cli) {\n        php_swoole_fatal_error(E_WARNING, \"client is not connected to the server\");\n        RETURN_FALSE;\n    }\n    SW_CHECK_RETURN(cli->close());\n}\n\nstatic PHP_METHOD(swoole_client_async, on) {\n    char *cb_name;\n    size_t cb_name_len;\n    zval *zcallback;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"sz\", &cb_name, &cb_name_len, &zcallback) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    auto client_obj = php_swoole_client_fetch_object(ZEND_THIS);\n    auto cb = sw_callable_create(zcallback);\n    if (!cb) {\n        return;\n    }\n\n    if (!client_obj->async) {\n        client_obj->async = new AsyncClientObject();\n    }\n\n    if (strncasecmp(\"connect\", cb_name, cb_name_len) == 0) {\n        zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL(\"onConnect\"), zcallback);\n        if (client_obj->async->onConnect) {\n            sw_callable_free(client_obj->async->onConnect);\n        }\n        client_obj->async->onConnect = cb;\n    } else if (strncasecmp(\"receive\", cb_name, cb_name_len) == 0) {\n        zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL(\"onReceive\"), zcallback);\n        if (client_obj->async->onReceive) {\n            sw_callable_free(client_obj->async->onReceive);\n        }\n        client_obj->async->onReceive = cb;\n    } else if (strncasecmp(\"close\", cb_name, cb_name_len) == 0) {\n        zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL(\"onClose\"), zcallback);\n        if (client_obj->async->onClose) {\n            sw_callable_free(client_obj->async->onClose);\n        }\n        client_obj->async->onClose = cb;\n    } else if (strncasecmp(\"error\", cb_name, cb_name_len) == 0) {\n        zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL(\"onError\"), zcallback);\n        if (client_obj->async->onError) {\n            sw_callable_free(client_obj->async->onError);\n        }\n        client_obj->async->onError = cb;\n    } else if (strncasecmp(\"bufferFull\", cb_name, cb_name_len) == 0) {\n        zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL(\"onBufferFull\"), zcallback);\n        if (client_obj->async->onBufferFull) {\n            sw_callable_free(client_obj->async->onBufferFull);\n        }\n        client_obj->async->onBufferFull = cb;\n    } else if (strncasecmp(\"bufferEmpty\", cb_name, cb_name_len) == 0) {\n        zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL(\"onBufferEmpty\"), zcallback);\n        if (client_obj->async->onBufferEmpty) {\n            sw_callable_free(client_obj->async->onBufferEmpty);\n        }\n        client_obj->async->onBufferEmpty = cb;\n    } else {\n        php_swoole_fatal_error(E_WARNING, \"Unknown event callback type name '%s'\", cb_name);\n        sw_callable_free(cb);\n        RETURN_FALSE;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_client_async, sleep) {\n    Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS);\n    if (!cli) {\n        RETURN_FALSE;\n    }\n    SW_CHECK_RETURN(cli->sleep());\n}\n\nstatic PHP_METHOD(swoole_client_async, wakeup) {\n    Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS);\n    if (!cli) {\n        RETURN_FALSE;\n    }\n    SW_CHECK_RETURN(cli->wakeup());\n}\n\nstatic PHP_METHOD(swoole_client_async, enableSSL) {\n    zval *zcallback = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_ZVAL(zcallback)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (zcallback == nullptr) {\n        zend_throw_exception(swoole_exception_ce, \"require `onSslReady` callback\", SW_ERROR_INVALID_PARAMS);\n        RETURN_FALSE;\n    }\n\n    Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS);\n    if (!cli) {\n        RETURN_FALSE;\n    }\n    if (!php_swoole_client_enable_ssl_encryption(cli, ZEND_THIS)) {\n        RETURN_FALSE;\n    }\n\n    auto client_obj = php_swoole_client_fetch_object(ZEND_THIS);\n    if (swoole_event_set(cli->socket, SW_EVENT_WRITE) < 0) {\n        RETURN_FALSE;\n    }\n\n    if (client_obj->async->onSSLReady) {\n        sw_callable_free(client_obj->async->onSSLReady);\n    }\n\n    auto cb = sw_callable_create(zcallback);\n    if (!cb) {\n        RETURN_FALSE;\n    }\n    zend_update_property(swoole_client_async_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL(\"onSSLReady\"), zcallback);\n    client_obj->async->onSSLReady = cb;\n    cli->ssl_wait_handshake = true;\n    cli->socket->ssl_state = SW_SSL_STATE_WAIT_STREAM;\n\n    RETURN_TRUE;\n}\n"
  },
  {
    "path": "ext-src/swoole_client_coro.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_client.h\"\n\n#include \"swoole_string.h\"\n#include \"swoole_socket.h\"\n#include \"swoole_protocol.h\"\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_client_coro_arginfo.h\"\nEND_EXTERN_C()\n\nusing swoole::SSLContext;\nusing swoole::String;\nusing swoole::network::Address;\n\nstatic zend_class_entry *swoole_client_coro_ce;\nstatic zend_object_handlers swoole_client_coro_handlers;\n\nstruct ClientCoroObject {\n    SocketImpl *socket;\n    zval zsocket;\n    /* safety zval */\n    zval zobject;\n    zend_object std;\n};\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_client_coro, __construct);\nstatic PHP_METHOD(swoole_client_coro, __destruct);\nstatic PHP_METHOD(swoole_client_coro, set);\nstatic PHP_METHOD(swoole_client_coro, connect);\nstatic PHP_METHOD(swoole_client_coro, recv);\nstatic PHP_METHOD(swoole_client_coro, peek);\nstatic PHP_METHOD(swoole_client_coro, send);\nstatic PHP_METHOD(swoole_client_coro, sendfile);\nstatic PHP_METHOD(swoole_client_coro, sendto);\nstatic PHP_METHOD(swoole_client_coro, recvfrom);\nstatic PHP_METHOD(swoole_client_coro, enableSSL);\nstatic PHP_METHOD(swoole_client_coro, getPeerCert);\nstatic PHP_METHOD(swoole_client_coro, verifyPeerCert);\nstatic PHP_METHOD(swoole_client_coro, exportSocket);\nstatic PHP_METHOD(swoole_client_coro, isConnected);\nstatic PHP_METHOD(swoole_client_coro, getsockname);\nstatic PHP_METHOD(swoole_client_coro, getpeername);\nstatic PHP_METHOD(swoole_client_coro, close);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic const zend_function_entry swoole_client_coro_methods[] =\n{\n    PHP_ME(swoole_client_coro, __construct,    arginfo_class_Swoole_Coroutine_Client___construct,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_coro, __destruct,     arginfo_class_Swoole_Coroutine_Client___destruct,     ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_coro, set,            arginfo_class_Swoole_Coroutine_Client_set,            ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_coro, connect,        arginfo_class_Swoole_Coroutine_Client_connect,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_coro, recv,           arginfo_class_Swoole_Coroutine_Client_recv,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_coro, peek,           arginfo_class_Swoole_Coroutine_Client_peek,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_coro, send,           arginfo_class_Swoole_Coroutine_Client_send,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_coro, sendfile,       arginfo_class_Swoole_Coroutine_Client_sendfile,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_coro, sendto,         arginfo_class_Swoole_Coroutine_Client_sendto,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_coro, recvfrom,       arginfo_class_Swoole_Coroutine_Client_recvfrom,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_coro, enableSSL,      arginfo_class_Swoole_Coroutine_Client_enableSSL,      ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_coro, getPeerCert,    arginfo_class_Swoole_Coroutine_Client_getPeerCert,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_coro, verifyPeerCert, arginfo_class_Swoole_Coroutine_Client_verifyPeerCert, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_coro, isConnected,    arginfo_class_Swoole_Coroutine_Client_isConnected,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_coro, getsockname,    arginfo_class_Swoole_Coroutine_Client_getsockname,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_coro, getpeername,    arginfo_class_Swoole_Coroutine_Client_getpeername,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_coro, close,          arginfo_class_Swoole_Coroutine_Client_close,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_client_coro, exportSocket,   arginfo_class_Swoole_Coroutine_Client_exportSocket,   ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nstatic ClientCoroObject *client_coro_fetch_object(zend_object *obj) {\n    return reinterpret_cast<ClientCoroObject *>(reinterpret_cast<char *>(obj) - swoole_client_coro_handlers.offset);\n}\n\nstatic ClientCoroObject *client_coro_get_client(const zval *zobject) {\n    return client_coro_fetch_object(Z_OBJ_P(zobject));\n}\n\nstatic SocketImpl *client_coro_get_socket(const zval *zobject) {\n    return client_coro_get_client(zobject)->socket;\n}\n\nstatic void client_coro_free_object(zend_object *object) {\n    ClientCoroObject *client = client_coro_fetch_object(object);\n    if (client->socket) {\n        client->socket->close();\n    }\n    zend_object_std_dtor(&client->std);\n}\n\n#define CLIENT_CORO_GET_SOCKET_SAFE(__sock)                                                                            \\\n    SW_CLIENT_GET_SOCKET_SAFE(__sock, &client_coro_get_client(ZEND_THIS)->zsocket);                                    \\\n    if (!__sock) {                                                                                                     \\\n        php_swoole_socket_set_error_properties(                                                                        \\\n            ZEND_THIS, SW_ERROR_CLIENT_NO_CONNECTION, swoole_strerror(SW_ERROR_CLIENT_NO_CONNECTION));                 \\\n        RETURN_FALSE;                                                                                                  \\\n    }\n\nstatic zend_object *client_coro_create_object(zend_class_entry *ce) {\n    auto *object = static_cast<ClientCoroObject *>(zend_object_alloc(sizeof(ClientCoroObject), ce));\n    zend_object_std_init(&object->std, ce);\n    object_properties_init(&object->std, ce);\n    object->std.handlers = &swoole_client_coro_handlers;\n    ZVAL_OBJ(&object->zobject, &object->std);\n    ZVAL_NULL(&object->zsocket);\n    return &object->std;\n}\n\nstatic void client_coro_socket_dtor(ClientCoroObject *client) {\n    if (client->socket->protocol.private_data_1) {\n        sw_callable_free(client->socket->protocol.private_data_1);\n        client->socket->protocol.private_data_1 = nullptr;\n    }\n    client->socket = nullptr;\n    zend_update_property_null(Z_OBJCE_P(&client->zobject), SW_Z8_OBJ_P(&client->zobject), ZEND_STRL(\"socket\"));\n    zend_update_property_bool(Z_OBJCE_P(&client->zobject), SW_Z8_OBJ_P(&client->zobject), ZEND_STRL(\"connected\"), 0);\n    zval_ptr_dtor(&client->zsocket);\n    ZVAL_NULL(&client->zsocket);\n}\n\nstatic SocketImpl *client_coro_create_socket(zval *zobject, zend_long type) {\n    auto socket_type = php_swoole_client_get_type(type);\n    auto object = php_swoole_create_socket(socket_type);\n    if (UNEXPECTED(!object)) {\n        php_swoole_socket_set_error_properties(zobject, errno);\n        return nullptr;\n    }\n    auto client = client_coro_get_client(zobject);\n    ZVAL_OBJ(&client->zsocket, object);\n    auto *socket = php_swoole_get_socket(&client->zsocket);\n\n    socket->set_dtor([client](CoSocket *_socket) { client_coro_socket_dtor(client); });\n\n    zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL(\"fd\"), socket->get_fd());\n    zend_update_property(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL(\"socket\"), &client->zsocket);\n\n    socket->set_buffer_allocator(sw_zend_string_allocator());\n    socket->set_zero_copy(true);\n\n    if ((type & SW_SOCK_SSL) && !socket->enable_ssl_encrypt()) {\n        php_swoole_socket_set_error_properties(zobject, EISCONN);\n        client_coro_socket_dtor(client);\n        return nullptr;\n    }\n\n    return socket;\n}\n\nvoid php_swoole_client_coro_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_client_coro, \"Swoole\\\\Coroutine\\\\Client\", \"Co\\\\Client\", swoole_client_coro_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_client_coro);\n    SW_SET_CLASS_CLONEABLE(swoole_client_coro, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_client_coro, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(\n        swoole_client_coro, client_coro_create_object, client_coro_free_object, ClientCoroObject, std);\n\n    zend_declare_property_long(swoole_client_coro_ce, ZEND_STRL(\"errCode\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_string(swoole_client_coro_ce, ZEND_STRL(\"errMsg\"), \"\", ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_client_coro_ce, ZEND_STRL(\"fd\"), -1, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_client_coro_ce, ZEND_STRL(\"socket\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_client_coro_ce, ZEND_STRL(\"type\"), SW_SOCK_TCP, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_client_coro_ce, ZEND_STRL(\"setting\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_bool(swoole_client_coro_ce, ZEND_STRL(\"connected\"), 0, ZEND_ACC_PUBLIC);\n\n    zend_declare_class_constant_long(swoole_client_coro_ce, ZEND_STRL(\"MSG_OOB\"), MSG_OOB);\n    zend_declare_class_constant_long(swoole_client_coro_ce, ZEND_STRL(\"MSG_PEEK\"), MSG_PEEK);\n    zend_declare_class_constant_long(swoole_client_coro_ce, ZEND_STRL(\"MSG_DONTWAIT\"), MSG_DONTWAIT);\n    zend_declare_class_constant_long(swoole_client_coro_ce, ZEND_STRL(\"MSG_WAITALL\"), MSG_WAITALL);\n}\n\nstatic sw_inline SocketImpl *client_coro_get_socket_for_connect(zval *zobject, int port) {\n    auto client = client_coro_get_client(zobject);\n    if (client->socket) {\n        php_swoole_socket_set_error_properties(zobject, EISCONN, strerror(EISCONN));\n        return nullptr;\n    }\n\n    zval *ztype = sw_zend_read_property(swoole_client_coro_ce, zobject, ZEND_STRL(\"type\"), 1);\n    auto socket_type = php_swoole_client_get_type(zval_get_long(ztype));\n    if (NetSocket::is_tcp(socket_type) && !Address::verify_port(port, true)) {\n        php_swoole_fatal_error(E_WARNING, \"The port is invalid\");\n        return nullptr;\n    }\n\n    auto sock = client_coro_create_socket(zobject, zval_get_long(ztype));\n    if (!sock) {\n        return nullptr;\n    }\n    client->socket = sock;\n    zval *zset = sw_zend_read_property_ex(swoole_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0);\n    if (zset && ZVAL_IS_ARRAY(zset)) {\n        php_swoole_socket_set(sock, zset);\n    }\n    return sock;\n}\n\nstatic PHP_METHOD(swoole_client_coro, __construct) {\n    zend_long type = 0;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1)\n    Z_PARAM_LONG(type)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    auto socket_type = php_swoole_client_get_type(type);\n    if (socket_type < SW_SOCK_TCP || socket_type > SW_SOCK_UNIX_DGRAM) {\n        const char *space, *class_name = get_active_class_name(&space);\n        zend_type_error(\"%s%s%s() expects parameter %d to be client type, unknown type \" ZEND_LONG_FMT \" given\",\n                        class_name,\n                        space,\n                        get_active_function_name(),\n                        1,\n                        type);\n        RETURN_FALSE;\n    }\n    php_swoole_check_reactor();\n    zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"type\"), type);\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_client_coro, __destruct) {}\n\nstatic PHP_METHOD(swoole_client_coro, set) {\n    zval *zset;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ARRAY(zset)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (php_swoole_array_length(zset) == 0) {\n        RETURN_FALSE;\n    }\n\n    zval *zsetting = sw_zend_read_and_convert_property_array(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL(\"setting\"), 0);\n    php_array_merge(Z_ARRVAL_P(zsetting), Z_ARRVAL_P(zset));\n    SocketImpl *cli = client_coro_get_socket(ZEND_THIS);\n    if (cli) {\n        RETURN_BOOL(php_swoole_socket_set(cli, zset));\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_client_coro, connect) {\n    char *host;\n    size_t host_len;\n    zend_long port = 0;\n    double timeout = 0;\n    zend_long sock_flag = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 4)\n    Z_PARAM_STRING(host, host_len)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(port)\n    Z_PARAM_DOUBLE(timeout)\n    Z_PARAM_LONG(sock_flag)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (host_len == 0) {\n        php_swoole_fatal_error(E_WARNING, \"The host is empty\");\n        RETURN_FALSE;\n    }\n\n    SocketImpl *socket = client_coro_get_socket_for_connect(ZEND_THIS, port);\n    if (!socket) {\n        RETURN_FALSE;\n    }\n    socket->set_timeout(timeout, SW_TIMEOUT_CONNECT);\n    if (!socket->connect(host, port, sock_flag)) {\n        php_swoole_socket_set_error_properties(ZEND_THIS, socket);\n        socket->close();\n        RETURN_FALSE;\n    }\n    socket->set_timeout(timeout, SW_TIMEOUT_RDWR);\n    zend_update_property_bool(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"connected\"), 1);\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_client_coro, send) {\n    char *data;\n    size_t data_len;\n    double timeout = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_STRING(data, data_len)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (data_len == 0) {\n        php_swoole_fatal_error(E_WARNING, \"data to send is empty\");\n        RETURN_FALSE;\n    }\n\n    CLIENT_CORO_GET_SOCKET_SAFE(cli);\n\n    SocketImpl::TimeoutSetter ts(cli, timeout, SW_TIMEOUT_WRITE);\n    ssize_t ret = cli->send_all(data, data_len);\n    if (ret < 0) {\n        php_swoole_socket_set_error_properties(ZEND_THIS, cli);\n        RETURN_FALSE;\n    }\n\n    if ((size_t) ret < data_len && cli->errCode) {\n        php_swoole_socket_set_error_properties(ZEND_THIS, cli);\n    }\n    RETURN_LONG(ret);\n}\n\nstatic PHP_METHOD(swoole_client_coro, sendto) {\n    char *host;\n    size_t host_len;\n    zend_long port;\n    char *data;\n    size_t len;\n\n    ZEND_PARSE_PARAMETERS_START(3, 3)\n    Z_PARAM_STRING(host, host_len)\n    Z_PARAM_LONG(port)\n    Z_PARAM_STRING(data, len)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (len == 0) {\n        RETURN_FALSE;\n    }\n\n    SocketImpl *socket = nullptr;\n    const auto client = client_coro_get_client(ZEND_THIS);\n    if (client->socket == nullptr) {\n        socket = client_coro_get_socket_for_connect(ZEND_THIS, 0);\n    } else {\n        socket = client->socket;\n    }\n    if (!socket) {\n        RETURN_FALSE;\n    }\n\n    if (socket->get_socket()->is_tcp() && !Address::verify_port(port, true)) {\n        php_swoole_fatal_error(E_WARNING, \"The port is invalid\");\n        RETURN_FALSE;\n    }\n\n    auto ret = socket->sendto(std::string(host, host_len), port, data, len);\n    if (ret < 0) {\n        php_swoole_socket_set_error_properties(ZEND_THIS, socket);\n        RETURN_FALSE;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_client_coro, recvfrom) {\n    zend_long length;\n    zval *address, *port;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"lz/|z/\", &length, &address, &port) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    if (length <= 0) {\n        RETURN_FALSE;\n    }\n\n    SocketImpl *socket = nullptr;\n    auto client = client_coro_get_client(ZEND_THIS);\n    if (client->socket == nullptr) {\n        socket = client_coro_get_socket_for_connect(ZEND_THIS, 0);\n    } else {\n        socket = client->socket;\n    }\n    if (!socket) {\n        RETURN_FALSE;\n    }\n\n    zend_string *retval = zend_string_alloc(length, false);\n    ssize_t n_bytes = socket->recvfrom(ZSTR_VAL(retval), length);\n    if (n_bytes < 0) {\n        zend_string_free(retval);\n        php_swoole_socket_set_error_properties(ZEND_THIS, socket);\n        RETURN_FALSE;\n    } else {\n        zval_ptr_dtor(address);\n        ZVAL_STRING(address, socket->get_addr());\n        if (port) {\n            zval_ptr_dtor(port);\n            ZVAL_LONG(port, socket->get_port());\n        }\n\n        ZSTR_LEN(retval) = n_bytes;\n        ZSTR_VAL(retval)[ZSTR_LEN(retval)] = '\\0';\n        RETURN_STR(retval);\n    }\n}\n\nstatic PHP_METHOD(swoole_client_coro, sendfile) {\n    char *file;\n    size_t file_len;\n    zend_long offset = 0;\n    zend_long length = 0;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"s|ll\", &file, &file_len, &offset, &length) == FAILURE) {\n        RETURN_FALSE;\n    }\n    if (file_len == 0) {\n        php_swoole_fatal_error(E_WARNING, \"file to send is empty\");\n        RETURN_FALSE;\n    }\n\n    CLIENT_CORO_GET_SOCKET_SAFE(cli);\n\n    // only stream socket can sendfile\n    if (!(cli->get_type() == SW_SOCK_TCP || cli->get_type() == SW_SOCK_TCP6 ||\n          cli->get_type() == SW_SOCK_UNIX_STREAM)) {\n        zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"errCode\"), EINVAL);\n        zend_update_property_string(\n            swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"errMsg\"), \"dgram socket cannot use sendfile\");\n        RETURN_FALSE;\n    }\n    if (!cli->sendfile(file, offset, length)) {\n        php_swoole_socket_set_error_properties(ZEND_THIS, cli);\n        RETVAL_FALSE;\n    } else {\n        RETVAL_TRUE;\n    }\n}\n\nstatic PHP_METHOD(swoole_client_coro, recv) {\n    double timeout = 0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    CLIENT_CORO_GET_SOCKET_SAFE(cli);\n\n    ssize_t retval;\n    zend_string *result = nullptr;\n\n    if (cli->open_length_check || cli->open_eof_check) {\n        retval = cli->recv_packet(timeout);\n        if (retval > 0) {\n            auto strval = cli->pop_packet();\n            if (strval == nullptr) {\n                retval = -1;\n                cli->set_err(ENOMEM);\n            } else {\n                result = zend::fetch_zend_string_by_val(strval);\n            }\n        }\n    } else {\n        result = zend_string_alloc(SW_PHP_CLIENT_BUFFER_SIZE - sizeof(zend_string), false);\n        SocketImpl::TimeoutSetter ts(cli, timeout, SW_TIMEOUT_READ);\n        retval = cli->recv(ZSTR_VAL(result), SW_PHP_CLIENT_BUFFER_SIZE - sizeof(zend_string));\n        if (retval <= 0) {\n            zend_string_free(result);\n        }\n    }\n    if (retval < 0) {\n        php_swoole_socket_set_error_properties(ZEND_THIS, cli);\n        RETURN_FALSE;\n    } else if (retval == 0) {\n        RETURN_EMPTY_STRING();\n    } else {\n        ZSTR_VAL(result)[retval] = '\\0';\n        ZSTR_LEN(result) = retval;\n        RETURN_STR(result);\n    }\n}\n\nstatic PHP_METHOD(swoole_client_coro, peek) {\n    zend_long buf_len = SW_PHP_CLIENT_BUFFER_SIZE;\n    char *buf = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(buf_len)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    CLIENT_CORO_GET_SOCKET_SAFE(cli);\n\n    buf = static_cast<char *>(emalloc((size_t) buf_len + 1));\n    auto ret = cli->peek(buf, buf_len);\n    if (ret < 0) {\n        php_swoole_socket_set_error_properties(ZEND_THIS, cli);\n        efree(buf);\n        RETURN_FALSE;\n    } else {\n        buf[ret] = 0;\n        RETVAL_STRINGL(buf, ret);\n        efree(buf);\n    }\n}\n\nstatic PHP_METHOD(swoole_client_coro, isConnected) {\n    SocketImpl *cli = client_coro_get_socket(ZEND_THIS);\n    if (cli && cli->is_connected()) {\n        RETURN_TRUE;\n    } else {\n        RETURN_FALSE;\n    }\n}\n\nstatic PHP_METHOD(swoole_client_coro, getsockname) {\n    CLIENT_CORO_GET_SOCKET_SAFE(cli);\n\n    if (!cli->getsockname()) {\n        php_swoole_socket_set_error_properties(ZEND_THIS, cli);\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n    zval zaddress;\n    ZVAL_STRING(&zaddress, cli->get_addr());\n    add_assoc_zval(return_value, \"host\", &zaddress); /* backward compatibility */\n    zend::array_set(return_value, SW_STRL(\"address\"), &zaddress);\n    add_assoc_long(return_value, \"port\", cli->get_port());\n}\n\n/**\n * export Swoole\\Coroutine\\Socket object\n */\nstatic PHP_METHOD(swoole_client_coro, exportSocket) {\n    auto cli = client_coro_get_client(ZEND_THIS);\n    RETURN_ZVAL(&cli->zsocket, 1, 0);\n}\n\nstatic PHP_METHOD(swoole_client_coro, getpeername) {\n    CLIENT_CORO_GET_SOCKET_SAFE(cli);\n\n    Address sa;\n    if (!cli->getpeername(&sa)) {\n        php_swoole_socket_set_error_properties(ZEND_THIS, cli);\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n    zval zaddress;\n    ZVAL_STRING(&zaddress, sa.get_addr());\n    add_assoc_zval(return_value, \"host\", &zaddress); /* backward compatibility */\n    Z_ADDREF(zaddress);\n    add_assoc_zval(return_value, \"address\", &zaddress);\n    add_assoc_long(return_value, \"port\", sa.get_port());\n}\n\nstatic PHP_METHOD(swoole_client_coro, close) {\n    CLIENT_CORO_GET_SOCKET_SAFE(_socket);\n    zend_update_property_bool(Z_OBJCE_P(ZEND_THIS), SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"connected\"), 0);\n    if (!_socket->close()) {\n        php_swoole_socket_set_error_properties(ZEND_THIS, _socket);\n        RETURN_FALSE;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_client_coro, enableSSL) {\n    CLIENT_CORO_GET_SOCKET_SAFE(cli);\n    if (cli->get_type() != SW_SOCK_TCP && cli->get_type() != SW_SOCK_TCP6) {\n        php_swoole_socket_set_error_properties(ZEND_THIS, ESOCKTNOSUPPORT);\n        RETURN_FALSE;\n    }\n    if (cli->get_ssl()) {\n        php_swoole_socket_set_error_properties(ZEND_THIS, EISCONN);\n        RETURN_FALSE;\n    }\n    if (!cli->enable_ssl_encrypt()) {\n        php_swoole_socket_set_error_properties(ZEND_THIS, EISCONN);\n        RETURN_FALSE;\n    }\n    zval *zset = sw_zend_read_property_ex(swoole_client_coro_ce, ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0);\n    if (php_swoole_array_length_safe(zset) > 0) {\n        php_swoole_socket_set_ssl(cli, zset);\n    }\n    if (!cli->ssl_handshake()) {\n        php_swoole_socket_set_error_properties(ZEND_THIS, cli);\n        RETURN_FALSE;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_client_coro, getPeerCert) {\n    CLIENT_CORO_GET_SOCKET_SAFE(cli);\n    if (!cli->get_ssl()) {\n        php_swoole_socket_set_error_properties(ZEND_THIS, EISCONN);\n        RETURN_FALSE;\n    }\n    if (!cli->get_socket()->ssl_get_peer_certificate(sw_tg_buffer())) {\n        RETURN_FALSE;\n    }\n    RETURN_SW_STRING(sw_tg_buffer());\n}\n\nstatic PHP_METHOD(swoole_client_coro, verifyPeerCert) {\n    CLIENT_CORO_GET_SOCKET_SAFE(cli);\n    if (!cli->get_ssl()) {\n        php_swoole_socket_set_error_properties(ZEND_THIS, ENOTCONN);\n        RETURN_FALSE;\n    }\n    zend_bool allow_self_signed = false;\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"|b\", &allow_self_signed) == FAILURE) {\n        RETURN_FALSE;\n    }\n    RETURN_BOOL(cli->ssl_verify(allow_self_signed));\n}\n"
  },
  {
    "path": "ext-src/swoole_coroutine.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Xinyu Zhu  <xyzhu1120@gmail.com>                             |\n  |         shiguangqi <shiguangqi2008@gmail.com>                        |\n  |         Twosee  <twose@qq.com>                                       |\n  |         Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_cxx.h\"\n#include \"php_swoole_coroutine_system.h\"\n\n#include \"swoole_server.h\"\n#include \"swoole_signal.h\"\n#include \"swoole_async.h\"\n#include \"swoole_iouring.h\"\n\nBEGIN_EXTERN_C()\n#include \"zend_builtin_functions.h\"\n#include \"ext/standard/basic_functions.h\"\n#include \"ext/spl/spl_array.h\"\n\n#include \"stubs/php_swoole_coroutine_arginfo.h\"\nEND_EXTERN_C()\n\n#include <unordered_map>\n#include <chrono>\n\n/**\n * The coroutine canceled exception must be explicitly caught in the php code.\n * If the underlying layer implicitly catches this exception, `Co::cancel()` may be abused,\n * resulting in transactional problems and serious bugs.\n */\n#define SW_RECOVER_CANCELED_EXCEPTION 0\n\n#define INVALID_PTR -1\n\nusing std::unordered_map;\nusing swoole::Coroutine;\nusing swoole::PHPContext;\nusing swoole::PHPCoroutine;\nusing swoole::coroutine::System;\n\nenum sw_exit_flags { SW_EXIT_IN_COROUTINE = 1 << 1, SW_EXIT_IN_SERVER = 1 << 2 };\n\nSW_THREAD_LOCAL bool PHPCoroutine::activated = false;\nSW_THREAD_LOCAL zend_array *PHPCoroutine::options = nullptr;\n\nSW_THREAD_LOCAL PHPCoroutine::Config PHPCoroutine::config{\n    SW_DEFAULT_MAX_CORO_NUM,\n    0,\n    false,\n    true,\n};\n\nSW_THREAD_LOCAL PHPContext PHPCoroutine::main_context{};\nSW_THREAD_LOCAL std::thread PHPCoroutine::interrupt_thread;\nSW_THREAD_LOCAL bool PHPCoroutine::interrupt_thread_running = false;\n\nextern void php_swoole_load_library();\n\nstatic SW_THREAD_LOCAL zend_atomic_bool *zend_vm_interrupt = nullptr;\nstatic SW_THREAD_LOCAL unordered_map<long, Coroutine *> user_yield_coros;\n\n#if PHP_VERSION_ID < 80400\nstatic user_opcode_handler_t ori_exit_handler = nullptr;\n#endif\nstatic user_opcode_handler_t ori_begin_silence_handler = nullptr;\nstatic user_opcode_handler_t ori_end_silence_handler = nullptr;\n\nstatic void (*orig_interrupt_function)(zend_execute_data *execute_data) = nullptr;\n\nstatic zend_class_entry *swoole_coroutine_util_ce;\nstatic zend_class_entry *swoole_exit_exception_ce;\nstatic zend_object_handlers swoole_exit_exception_handlers;\nstatic zend_class_entry *swoole_coroutine_iterator_ce;\nstatic zend_class_entry *swoole_coroutine_context_ce;\n\nstatic zend_class_entry *swoole_coroutine_canceled_exception_ce;\nstatic zend_object_handlers swoole_coroutine_canceled_exception_handlers;\nstatic zend_class_entry *swoole_coroutine_timeout_exception_ce;\nstatic zend_object_handlers swoole_coroutine_timeout_exception_handlers;\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_coroutine, exists);\nstatic PHP_METHOD(swoole_coroutine, yield);\nstatic PHP_METHOD(swoole_coroutine, resume);\nstatic PHP_METHOD(swoole_coroutine, join);\nstatic PHP_METHOD(swoole_coroutine, cancel);\nstatic PHP_METHOD(swoole_coroutine, setTimeLimit);\nstatic PHP_METHOD(swoole_coroutine, isCanceled);\nstatic PHP_METHOD(swoole_coroutine, stats);\nstatic PHP_METHOD(swoole_coroutine, getCid);\nstatic PHP_METHOD(swoole_coroutine, getPcid);\nstatic PHP_METHOD(swoole_coroutine, getContext);\nstatic PHP_METHOD(swoole_coroutine, getBackTrace);\nstatic PHP_METHOD(swoole_coroutine, printBackTrace);\nstatic PHP_METHOD(swoole_coroutine, getElapsed);\nstatic PHP_METHOD(swoole_coroutine, getStackUsage);\nstatic PHP_METHOD(swoole_coroutine, list);\nstatic PHP_METHOD(swoole_coroutine, enableScheduler);\nstatic PHP_METHOD(swoole_coroutine, disableScheduler);\n#ifdef SW_CORO_TIME\nstatic PHP_METHOD(swoole_coroutine, getExecuteTime);\n#endif\nSW_EXTERN_C_END\n\n// clang-format off\nstatic const zend_function_entry swoole_coroutine_methods[] =\n{\n    /**\n     * Coroutine Core API\n     */\n    ZEND_FENTRY(create, ZEND_FN(swoole_coroutine_create), arginfo_class_Swoole_Coroutine_create,           ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(defer, ZEND_FN(swoole_coroutine_defer),   arginfo_class_Swoole_Coroutine_defer,            ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_scheduler, set,               arginfo_class_Swoole_Coroutine_set,              ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_scheduler, getOptions,        arginfo_class_Swoole_Coroutine_getOptions,       ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, exists,                      arginfo_class_Swoole_Coroutine_exists,           ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, yield,                       arginfo_class_Swoole_Coroutine_yield,            ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, cancel,                      arginfo_class_Swoole_Coroutine_cancel,           ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, join,                        arginfo_class_Swoole_Coroutine_join,             ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, isCanceled,                  arginfo_class_Swoole_Coroutine_isCanceled,       ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, setTimeLimit,                arginfo_class_Swoole_Coroutine_setTimeLimit,     ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_MALIAS(swoole_coroutine, suspend, yield,          arginfo_class_Swoole_Coroutine_suspend,          ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, resume,                      arginfo_class_Swoole_Coroutine_resume,           ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, stats,                       arginfo_class_Swoole_Coroutine_stats,            ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, getCid,                      arginfo_class_Swoole_Coroutine_getCid,           ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_MALIAS(swoole_coroutine, getuid, getCid,          arginfo_class_Swoole_Coroutine_getuid,           ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, getPcid,                     arginfo_class_Swoole_Coroutine_getPcid,          ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, getContext,                  arginfo_class_Swoole_Coroutine_getContext,       ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, getBackTrace,                arginfo_class_Swoole_Coroutine_getBackTrace,     ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, printBackTrace,              arginfo_class_Swoole_Coroutine_printBackTrace,   ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, getElapsed,                  arginfo_class_Swoole_Coroutine_getElapsed,       ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, getStackUsage,               arginfo_class_Swoole_Coroutine_getStackUsage,    ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, list,                        arginfo_class_Swoole_Coroutine_list,             ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_MALIAS(swoole_coroutine, listCoroutines, list,    arginfo_class_Swoole_Coroutine_listCoroutines,   ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, enableScheduler,             arginfo_class_Swoole_Coroutine_enableScheduler,  ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine, disableScheduler,            arginfo_class_Swoole_Coroutine_disableScheduler, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n#ifdef SW_CORO_TIME\n    PHP_ME(swoole_coroutine, getExecuteTime,              arginfo_class_Swoole_Coroutine_getExecuteTime,   ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n#endif\n    /**\n     * Coroutine System API\n     */\n    ZEND_FENTRY(gethostbyname,      ZEND_FN(swoole_coroutine_gethostbyname), arginfo_class_Swoole_Coroutine_System_gethostbyname, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(dnsLookup,          ZEND_FN(swoole_async_dns_lookup_coro),   arginfo_class_Swoole_Coroutine_System_dnsLookup,     ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, exec,                                    arginfo_class_Swoole_Coroutine_System_exec,          ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, sleep,                                   arginfo_class_Swoole_Coroutine_System_sleep,         ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, getaddrinfo,                             arginfo_class_Swoole_Coroutine_System_getaddrinfo,   ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, statvfs,                                 arginfo_class_Swoole_Coroutine_System_statvfs,       ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, readFile,                                arginfo_class_Swoole_Coroutine_System_readFile,      ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, writeFile,                               arginfo_class_Swoole_Coroutine_System_writeFile,     ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, wait,                                    arginfo_class_Swoole_Coroutine_System_wait,          ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, waitPid,                                 arginfo_class_Swoole_Coroutine_System_waitPid,       ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, waitSignal,                              arginfo_class_Swoole_Coroutine_System_waitSignal,    ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, waitEvent,                               arginfo_class_Swoole_Coroutine_System_waitEvent,     ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_FE_END\n};\n// clang-format on\n\n/**\n * Exit Exception\n */\nstatic PHP_METHOD(swoole_exit_exception, getFlags);\nstatic PHP_METHOD(swoole_exit_exception, getStatus);\n\n// clang-format off\nstatic constexpr zend_function_entry swoole_exit_exception_methods[] = {\n    PHP_ME(swoole_exit_exception, getFlags,  arginfo_class_Swoole_ExitException_getFlags,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_exit_exception, getStatus, arginfo_class_Swoole_ExitException_getStatus, ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\n#if PHP_VERSION_ID < 80400\nstatic int coro_exit_handler(zend_execute_data *execute_data) {\n    zval ex;\n    zend_long flags = 0;\n    if (Coroutine::get_current()) {\n        flags |= SW_EXIT_IN_COROUTINE;\n    }\n    if (sw_server() && sw_server()->is_started()) {\n        flags |= SW_EXIT_IN_SERVER;\n    }\n    if (flags) {\n        const zend_op *opline = EX(opline);\n        zval _exit_status{};\n        zval *exit_status = nullptr;\n\n        if (opline->op1_type != IS_UNUSED) {\n            if (opline->op1_type == IS_CONST) {\n                // see: https://github.com/php/php-src/commit/e70618aff6f447a298605d07648f2ce9e5a284f5\n#ifdef EX_CONSTANT\n                exit_status = EX_CONSTANT(opline->op1);\n#else\n                exit_status = RT_CONSTANT(opline, opline->op1);\n#endif\n            } else {\n                exit_status = EX_VAR(opline->op1.var);\n            }\n            if (Z_ISREF_P(exit_status)) {\n                exit_status = Z_REFVAL_P(exit_status);\n            }\n            ZVAL_DUP(&_exit_status, exit_status);\n            exit_status = &_exit_status;\n        } else {\n            exit_status = &_exit_status;\n            ZVAL_NULL(exit_status);\n        }\n        zend_object *obj = zend_throw_exception(swoole_exit_exception_ce, \"swoole exit\", 0);\n        ZVAL_OBJ(&ex, obj);\n        zend_update_property_long(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL(\"flags\"), flags);\n        Z_TRY_ADDREF_P(exit_status);\n        zend_update_property(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL(\"status\"), exit_status);\n    }\n\n    return ZEND_USER_OPCODE_DISPATCH;\n}\n#else\nSW_EXTERN_C_BEGIN\nPHP_FUNCTION(swoole_exit) {\n    zend_long flags = 0;\n    if (Coroutine::get_current()) {\n        flags |= SW_EXIT_IN_COROUTINE;\n    }\n\n    if (sw_server() && sw_server()->is_started()) {\n        flags |= SW_EXIT_IN_SERVER;\n    }\n\n    zend_string *message = NULL;\n    zend_long status = 0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_STR_OR_LONG(message, status)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if (flags) {\n        zval ex = {};\n        zend_object *obj =\n            zend_throw_exception(swoole_exit_exception_ce, (message ? ZSTR_VAL(message) : \"swoole exit\"), 0);\n        ZVAL_OBJ(&ex, obj);\n        zend_update_property_long(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL(\"flags\"), flags);\n        if (message) {\n            zend_update_property_str(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL(\"status\"), message);\n        } else {\n            zend_update_property_long(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL(\"status\"), status);\n        }\n    } else {\n        if (!php_swoole_call_original_handler(ZEND_STRL(\"exit\"), INTERNAL_FUNCTION_PARAM_PASSTHRU)) {\n            if (message) {\n                php_write(ZSTR_VAL(message), ZSTR_LEN(message));\n            }\n            sw_php_exit(status);\n        }\n    }\n}\nSW_EXTERN_C_END\n#endif\n\nstatic int coro_begin_silence_handler(zend_execute_data *execute_data) {\n    PHPContext *task = PHPCoroutine::get_context();\n    task->in_silence = true;\n    task->ori_error_reporting = EG(error_reporting);\n    return ZEND_USER_OPCODE_DISPATCH;\n}\n\nstatic int coro_end_silence_handler(zend_execute_data *execute_data) {\n    PHPContext *task = PHPCoroutine::get_context();\n    task->in_silence = false;\n    return ZEND_USER_OPCODE_DISPATCH;\n}\n\nstatic void coro_interrupt_resume(void *data) {\n    auto *co = static_cast<Coroutine *>(data);\n    if (co && !co->is_end()) {\n        swoole_trace_log(SW_TRACE_COROUTINE, \"interrupt_callback cid=%ld \", co->get_cid());\n        co->resume();\n    }\n}\n\nstatic void coro_interrupt_function(zend_execute_data *execute_data) {\n    PHPContext *task = PHPCoroutine::get_context();\n    if (task && task->co && PHPCoroutine::is_schedulable(task)) {\n        swoole_event_defer(coro_interrupt_resume, (void *) task->co);\n        task->co->yield();\n    }\n    if (orig_interrupt_function) {\n        orig_interrupt_function(execute_data);\n    }\n}\n\nPHPContext *PHPCoroutine::create_context(const Args *args) {\n    auto *ctx = static_cast<PHPContext *>(emalloc(sizeof(PHPContext)));\n    ctx->output_ptr = nullptr;\n    ctx->serialize_lock = 0;\n    ctx->serialize = {};\n    ctx->unserialize = {};\n    ctx->in_silence = false;\n\n    ctx->co = Coroutine::get_current();\n    ctx->co->set_task((void *) ctx);\n    ctx->defer_tasks = nullptr;\n    ctx->pcid = ctx->co->get_origin_cid();\n    ctx->context = nullptr;\n    ctx->on_yield = nullptr;\n    ctx->on_resume = nullptr;\n    ctx->on_close = nullptr;\n    ctx->enable_scheduler = true;\n\n    if (UNEXPECTED(SWOOLE_G(enable_fiber_mock))) {\n        fiber_context_init(ctx);\n    } else {\n        ctx->fiber_context = nullptr;\n    }\n    ctx->fiber_init_notified = false;\n\n    EG(vm_stack) = zend_vm_stack_new_page(SW_DEFAULT_PHP_STACK_PAGE_SIZE, nullptr);\n    EG(vm_stack_top) = EG(vm_stack)->top + ZEND_CALL_FRAME_SLOT;\n    EG(vm_stack_end) = EG(vm_stack)->end;\n    EG(vm_stack_page_size) = SW_DEFAULT_PHP_STACK_PAGE_SIZE;\n\n    zend_function *func = EG(current_execute_data)->func;\n    auto *call = reinterpret_cast<zend_execute_data *>((EG(vm_stack_top)));\n    EG(current_execute_data) = call;\n    memset(EG(current_execute_data), 0, sizeof(zend_execute_data));\n\n    EG(error_handling) = EH_NORMAL;\n    EG(exception_class) = nullptr;\n    EG(exception) = nullptr;\n    EG(jit_trace_num) = 0;\n\n    call->func = func;\n    EG(vm_stack_top) += ZEND_CALL_FRAME_SLOT;\n\n#ifdef ZEND_CHECK_STACK_LIMIT\n    EG(stack_base) = stack_base(ctx);\n    EG(stack_limit) = stack_limit(ctx);\n#endif\n\n    save_vm_stack(ctx);\n    record_last_msec(ctx);\n\n    ctx->fci_cache = *args->fci_cache;\n    ctx->fci.size = sizeof(ctx->fci);\n    ctx->fci.object = nullptr;\n    ctx->fci.param_count = args->argc;\n    ctx->fci.params = args->argv;\n    ctx->fci.named_params = nullptr;\n    ctx->return_value = {};\n    ctx->fci.retval = &ctx->return_value;\n\n    if (args->callable) {\n        ctx->fci.function_name = *args->callable;\n        Z_TRY_ADDREF(ctx->fci.function_name);\n    } else {\n        ZVAL_UNDEF(&ctx->fci.function_name);\n    }\n    sw_zend_fci_cache_persist(&ctx->fci_cache);\n\n    return ctx;\n}\n\nvoid PHPCoroutine::bailout() {\n    Coroutine::bailout([]() {\n        if (sw_reactor()) {\n            sw_reactor()->running = false;\n            sw_reactor()->bailout = true;\n        }\n        zend_bailout();\n    });\n}\n\nbool PHPCoroutine::catch_exception() {\n    if (UNEXPECTED(EG(exception))) {\n#if SW_RECOVER_CANCELED_EXCEPTION\n        if (EG(exception)->ce == swoole_coroutine_canceled_exception_ce) {\n            OBJ_RELEASE(EG(exception));\n            EG(exception) = nullptr;\n        } else {\n#endif\n            // the exception error messages MUST be output on the current coroutine stack\n            zend_exception_error(EG(exception), E_ERROR);\n            return true;\n#if SW_RECOVER_CANCELED_EXCEPTION\n        }\n#endif\n    }\n    return false;\n}\n\nvoid PHPCoroutine::activate() {\n    if (sw_unlikely(activated)) {\n        return;\n    }\n\n    zval *enable_library = zend_get_constant_str(ZEND_STRL(\"SWOOLE_LIBRARY\"));\n    if (enable_library == nullptr || !zval_is_true(enable_library)) {\n        php_swoole_load_library();\n    }\n\n    /* init reactor and register event wait */\n    php_swoole_check_reactor();\n\n    /* replace interrupt function */\n    orig_interrupt_function = zend_interrupt_function;\n    zend_interrupt_function = coro_interrupt_function;\n\n    if (SWOOLE_G(enable_preemptive_scheduler) || config.enable_preemptive_scheduler) {\n        /* create a thread to interrupt the coroutine that takes up too much time */\n        interrupt_thread_start();\n    }\n\n    if (sw_is_main_thread()) {\n        if (config.hook_flags) {\n            enable_hook(config.hook_flags);\n        }\n        disable_unsafe_function();\n    }\n\n    /* deactivate when reactor free */\n    sw_reactor()->add_destroy_callback(deactivate, nullptr);\n    Coroutine::activate();\n\n    Coroutine::set_on_yield(on_yield);\n    Coroutine::set_on_resume(on_resume);\n    Coroutine::set_on_close(on_close);\n\n    activated = true;\n}\n\nvoid PHPCoroutine::deactivate(void *ptr) {\n    if (sw_unlikely(!activated)) {\n        return;\n    }\n    activated = false;\n    interrupt_thread_stop();\n\n    if (sw_is_main_thread()) {\n        disable_hook();\n        enable_unsafe_function();\n    }\n\n    Coroutine::set_on_yield(nullptr);\n    Coroutine::set_on_resume(nullptr);\n    Coroutine::set_on_close(nullptr);\n\n    zend_interrupt_function = orig_interrupt_function;\n\n    if (config.enable_deadlock_check) {\n        deadlock_check();\n    }\n\n    Coroutine::deactivate();\n}\n\nvoid PHPCoroutine::shutdown() {\n    if (activated) {\n        deactivate(nullptr);\n    }\n    if (options) {\n        zend_array_destroy(options);\n        options = nullptr;\n    }\n    free_main_context();\n}\n\n// !!! [Danger Warning]\n// The deadlock_check function can only print information and cannot execute any async functions\n// This function can only execute internal code and cannot provide any callbacks, allowing the execution of user code\nvoid PHPCoroutine::deadlock_check() {\n    if (Coroutine::count() == 0) {\n        return;\n    }\n    if (php_swoole_is_fatal_error() || (sw_reactor() && sw_reactor()->bailout)) {\n        return;\n    }\n    if (SWOOLE_G(enable_library)) {\n        zend::function::call(R\"(\\Swoole\\Coroutine\\deadlock_check)\", 0, nullptr);\n    } else {\n        printf(\"\\n ===================================================================\"\n               \"\\n  [FATAL ERROR]: all coroutines (count: %lu) are asleep - deadlock! \"\n               \"\\n ===================================================================\"\n               \"\\n\",\n               Coroutine::count());\n    }\n}\n\nvoid PHPCoroutine::interrupt_thread_stop() {\n    if (!interrupt_thread_running) {\n        return;\n    }\n    interrupt_thread_running = false;\n    interrupt_thread.join();\n}\n\nvoid PHPCoroutine::interrupt_thread_start() {\n    if (interrupt_thread_running) {\n        return;\n    }\n    zend_vm_interrupt = &EG(vm_interrupt);\n    interrupt_thread_running = true;\n    interrupt_thread = std::thread([]() {\n        swoole_signal_block_all();\n        while (interrupt_thread_running) {\n            zend_atomic_bool_store(zend_vm_interrupt, 1);\n            std::this_thread::sleep_for(std::chrono::milliseconds(MAX_EXEC_MSEC / 2));\n        }\n    });\n}\n\n/**\n * The meaning of the task argument in coro switch functions\n *\n * create: origin_task\n * yield: current_task\n * resume: target_task\n * close: current_task\n *\n */\ninline void PHPCoroutine::save_vm_stack(PHPContext *ctx) {\n    ctx->bailout = EG(bailout);\n    ctx->vm_stack_top = EG(vm_stack_top);\n    ctx->vm_stack_end = EG(vm_stack_end);\n    ctx->vm_stack = EG(vm_stack);\n    ctx->vm_stack_page_size = EG(vm_stack_page_size);\n    ctx->execute_data = EG(current_execute_data);\n    ctx->jit_trace_num = EG(jit_trace_num);\n    ctx->error_handling = EG(error_handling);\n    ctx->exception_class = EG(exception_class);\n    ctx->exception = EG(exception);\n    if (UNEXPECTED(ctx->in_silence)) {\n        ctx->tmp_error_reporting = EG(error_reporting);\n        EG(error_reporting) = ctx->ori_error_reporting;\n    }\n#ifdef ZEND_CHECK_STACK_LIMIT\n    ctx->stack_base = EG(stack_base);\n    ctx->stack_limit = EG(stack_limit);\n#endif\n}\n\ninline void PHPCoroutine::restore_vm_stack(PHPContext *ctx) {\n    EG(bailout) = ctx->bailout;\n    EG(vm_stack_top) = ctx->vm_stack_top;\n    EG(vm_stack_end) = ctx->vm_stack_end;\n    EG(vm_stack) = ctx->vm_stack;\n    EG(vm_stack_page_size) = ctx->vm_stack_page_size;\n    EG(current_execute_data) = ctx->execute_data;\n    EG(jit_trace_num) = ctx->jit_trace_num;\n    EG(error_handling) = ctx->error_handling;\n    EG(exception_class) = ctx->exception_class;\n    EG(exception) = ctx->exception;\n    if (UNEXPECTED(ctx->in_silence)) {\n        EG(error_reporting) = ctx->tmp_error_reporting;\n    }\n#ifdef ZEND_CHECK_STACK_LIMIT\n    EG(stack_base) = ctx->stack_base;\n    EG(stack_limit) = ctx->stack_limit;\n#endif\n}\n\ninline void PHPCoroutine::save_og(PHPContext *ctx) {\n    if (OG(handlers).elements) {\n        ctx->output_ptr = (zend_output_globals *) emalloc(sizeof(zend_output_globals));\n        memcpy(ctx->output_ptr, SWOG, sizeof(zend_output_globals));\n        php_output_activate();\n    } else {\n        ctx->output_ptr = nullptr;\n    }\n}\n\ninline void PHPCoroutine::restore_og(PHPContext *ctx) {\n    if (ctx->output_ptr) {\n        memcpy(SWOG, ctx->output_ptr, sizeof(zend_output_globals));\n        efree(ctx->output_ptr);\n        ctx->output_ptr = nullptr;\n    }\n}\n\nvoid PHPCoroutine::save_bg(PHPContext *ctx) {\n    if (BG(serialize_lock)) {\n        ctx->serialize_lock = BG(serialize_lock);\n    }\n    if (BG(serialize).data) {\n        memcpy(&ctx->serialize, &BG(serialize), sizeof(BG(serialize)));\n    }\n    if (BG(unserialize).data) {\n        memcpy(&ctx->unserialize, &BG(unserialize), sizeof(BG(unserialize)));\n    }\n}\n\nvoid PHPCoroutine::restore_bg(PHPContext *ctx) {\n    if (ctx->serialize_lock) {\n        BG(serialize_lock) = ctx->serialize_lock;\n        ctx->serialize_lock = 0;\n    }\n    if (ctx->serialize.data) {\n        memcpy(&BG(serialize), &ctx->serialize, sizeof(BG(serialize)));\n        ctx->serialize = {};\n    }\n    if (ctx->unserialize.data) {\n        memcpy(&BG(unserialize), &ctx->unserialize, sizeof(BG(unserialize)));\n        ctx->unserialize = {};\n    }\n}\n\nvoid PHPCoroutine::set_hook_flags(uint32_t flags) {\n    zval zoptions;\n    array_init(&zoptions);\n    add_assoc_long(&zoptions, \"hook_flags\", flags);\n\n    if (options) {\n        zend_hash_merge(options, Z_ARRVAL(zoptions), nullptr, true);\n        zval_ptr_dtor(&zoptions);\n    } else {\n        options = Z_ARRVAL(zoptions);\n    }\n\n    config.hook_flags = flags;\n}\n\nvoid PHPCoroutine::save_context(PHPContext *ctx) {\n    save_vm_stack(ctx);\n    save_og(ctx);\n    save_bg(ctx);\n}\n\nvoid PHPCoroutine::restore_context(PHPContext *ctx) {\n    restore_vm_stack(ctx);\n    restore_og(ctx);\n    restore_bg(ctx);\n}\n\nvoid PHPCoroutine::on_yield(void *arg) {\n    auto *ctx = static_cast<PHPContext *>(arg);\n    auto *origin_ctx = get_origin_context(ctx);\n\n    fiber_context_switch_try_notify(ctx, origin_ctx);\n    save_context(ctx);\n    restore_context(origin_ctx);\n\n    if (ctx->on_yield) {\n        (*ctx->on_yield)(ctx);\n    }\n\n    swoole_trace_log(SW_TRACE_COROUTINE, \"from cid=%ld to cid=%ld\", ctx->co->get_cid(), ctx->co->get_origin_cid());\n}\n\nvoid PHPCoroutine::on_resume(void *arg) {\n    auto *ctx = static_cast<PHPContext *>(arg);\n    auto *current_ctx = get_context();\n\n    fiber_context_switch_try_notify(current_ctx, ctx);\n    save_context(current_ctx);\n    restore_context(ctx);\n    record_last_msec(ctx);\n\n    if (ctx->on_resume) {\n        (*ctx->on_resume)(ctx);\n    }\n\n    swoole_trace_log(SW_TRACE_COROUTINE, \"from cid=%ld to cid=%ld\", Coroutine::get_current_cid(), ctx->co->get_cid());\n}\n\nvoid PHPCoroutine::on_close(void *arg) {\n    auto *ctx = static_cast<PHPContext *>(arg);\n    if (ctx->on_close) {\n        (*ctx->on_close)(ctx);\n    }\n    efree(ctx);\n}\n\nvoid PHPCoroutine::destroy_context(PHPContext *ctx) {\n    PHPContext *origin_ctx = get_origin_context(ctx);\n#ifdef SW_LOG_TRACE_OPEN\n    // MUST be assigned here, the task memory may have been released\n    long cid = ctx->co->get_cid();\n    long origin_cid = ctx->co->get_origin_cid();\n#endif\n\n    if (swoole_isset_hook(SW_GLOBAL_HOOK_ON_CORO_STOP)) {\n        swoole_call_hook(SW_GLOBAL_HOOK_ON_CORO_STOP, ctx);\n    }\n\n    if (OG(handlers).elements) {\n        zend_bool no_headers = SG(request_info).no_headers;\n        /* Do not send headers by SAPI */\n        SG(request_info).no_headers = true;\n        if (OG(active)) {\n            php_output_end_all();\n        }\n        php_output_deactivate();\n        php_output_activate();\n        SG(request_info).no_headers = no_headers;\n    }\n\n    if (ctx->defer_tasks) {\n        while (!ctx->defer_tasks->empty()) {\n            zend::Function *defer_fci = ctx->defer_tasks->top();\n            ctx->defer_tasks->pop();\n            sw_zend_fci_cache_discard(&defer_fci->fci_cache);\n            efree(defer_fci);\n        }\n        delete ctx->defer_tasks;\n        ctx->defer_tasks = nullptr;\n    }\n\n    // Release resources\n    if (ctx->context) {\n        zend_object *context = ctx->context;\n        ctx->context = reinterpret_cast<zend_object *>(INVALID_PTR);\n        OBJ_RELEASE(context);\n    }\n\n    Z_TRY_DELREF(ctx->fci.function_name);\n    ZVAL_UNDEF(&ctx->fci.function_name);\n    sw_zend_fci_cache_discard(&ctx->fci_cache);\n\n    Z_TRY_DELREF(ctx->return_value);\n\n    fiber_context_try_destroy(ctx, origin_ctx);\n\n    swoole_trace_log(SW_TRACE_COROUTINE,\n                     \"coro close cid=%ld and resume to %ld, %zu remained. usage size: %zu. malloc size: %zu\",\n                     cid,\n                     origin_cid,\n                     (uintmax_t) Coroutine::count() - 1,\n                     (uintmax_t) zend_memory_usage(0),\n                     (uintmax_t) zend_memory_usage(1));\n\n    zend_vm_stack_destroy();\n    restore_context(origin_ctx);\n}\n\nvoid PHPCoroutine::main_func(void *_args) {\n    bool exception_caught = false;\n    const auto args = static_cast<Args *>(_args);\n    PHPContext *ctx = create_context(args);\n\n    zend_first_try {\n        swoole_trace_log(SW_TRACE_COROUTINE,\n                         \"Create coro id: %ld, origin cid: %ld, coro total count: %zu, heap size: %zu\",\n                         ctx->co->get_cid(),\n                         ctx->co->get_origin_cid(),\n                         (uintmax_t) Coroutine::count(),\n                         (uintmax_t) zend_memory_usage(0));\n\n        if (swoole_isset_hook(SW_GLOBAL_HOOK_ON_CORO_START)) {\n            swoole_call_hook(SW_GLOBAL_HOOK_ON_CORO_START, ctx);\n        }\n\n        if (UNEXPECTED(ctx->fiber_context && ctx->fci_cache.function_handler->type == ZEND_USER_FUNCTION)) {\n            zend_execute_data *tmp = EG(current_execute_data);\n            zend_execute_data call = {};\n            EG(current_execute_data) = &call;\n            EG(current_execute_data)->opline = ctx->fci_cache.function_handler->op_array.opcodes;\n            call.func = ctx->fci_cache.function_handler;\n            fiber_context_switch_try_notify(get_origin_context(ctx), ctx);\n            EG(current_execute_data) = tmp;\n        }\n\n        zend_call_function(&ctx->fci, &ctx->fci_cache);\n\n        // Catch exception in main function of the coroutine\n        exception_caught = catch_exception();\n\n        // The defer tasks still need to be executed after an exception occurs\n        if (ctx->defer_tasks) {\n            std::stack<zend::Function *> *tasks = ctx->defer_tasks;\n            while (!tasks->empty()) {\n                zend::Function *defer_fci = tasks->top();\n                tasks->pop();\n                if (Z_TYPE_P(&ctx->return_value) != IS_UNDEF) {\n                    defer_fci->fci.param_count = 1;\n                    defer_fci->fci.params = &ctx->return_value;\n                }\n                if (UNEXPECTED(sw_zend_call_function_anyway(&defer_fci->fci, &defer_fci->fci_cache) != SUCCESS)) {\n                    php_swoole_fatal_error(E_WARNING, \"defer callback handler error\");\n                }\n                if (EG(exception)) {\n                    zend_bailout();\n                }\n                sw_zend_fci_cache_discard(&defer_fci->fci_cache);\n                efree(defer_fci);\n            }\n            delete ctx->defer_tasks;\n            ctx->defer_tasks = nullptr;\n        }\n    }\n    zend_catch {\n        // zend_bailout is executed in the c function\n        catch_exception();\n        exception_caught = true;\n    }\n    zend_end_try();\n    destroy_context(ctx);\n    if (exception_caught) {\n        bailout();\n    }\n}\n\nlong PHPCoroutine::create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *callable) {\n    if (sw_unlikely(Coroutine::count() >= config.max_num)) {\n        php_swoole_fatal_error(E_WARNING, \"exceed max number of coroutine %zu\", (uintmax_t) Coroutine::count());\n        return Coroutine::ERR_LIMIT;\n    }\n    if (sw_unlikely(!fci_cache || !fci_cache->function_handler)) {\n        php_swoole_fatal_error(E_ERROR, \"invalid function call info cache\");\n        return Coroutine::ERR_INVALID;\n    }\n    zend_uchar type = fci_cache->function_handler->type;\n    if (sw_unlikely(type != ZEND_USER_FUNCTION && type != ZEND_INTERNAL_FUNCTION)) {\n        php_swoole_fatal_error(E_ERROR, \"invalid function type %u\", fci_cache->function_handler->type);\n        return Coroutine::ERR_INVALID;\n    }\n\n    if (sw_unlikely(!activated)) {\n        activate();\n    }\n\n    Args _args;\n    _args.fci_cache = fci_cache;\n    _args.argv = argv;\n    _args.argc = argc;\n    _args.callable = callable;\n    save_context(get_context());\n\n    return Coroutine::create(main_func, (void *) &_args);\n}\n\nvoid PHPCoroutine::defer(zend::Function *fci) {\n    PHPContext *ctx = get_context();\n    if (ctx->defer_tasks == nullptr) {\n        ctx->defer_tasks = new std::stack<zend::Function *>;\n    }\n    ctx->defer_tasks->push(fci);\n}\n\nvoid PHPCoroutine::fiber_context_init(PHPContext *ctx) {\n    auto *fiber_context = static_cast<zend_fiber_context *>(emalloc(sizeof(zend_fiber_context)));\n    fiber_context->handle = reinterpret_cast<void *>(INVALID_PTR);\n    fiber_context->kind = reinterpret_cast<void *>(INVALID_PTR);\n    fiber_context->function = reinterpret_cast<zend_fiber_coroutine>(INVALID_PTR);\n    fiber_context->stack = reinterpret_cast<zend_fiber_stack *>(INVALID_PTR);\n    ctx->fiber_context = fiber_context;\n\n    zend_observer_fiber_init_notify(fiber_context);\n}\n\nvoid PHPCoroutine::fiber_context_try_destroy(const PHPContext *ctx, PHPContext *origin_ctx) {\n    if (UNEXPECTED(ctx->fiber_context)) {\n        ctx->fiber_context->status = ZEND_FIBER_STATUS_DEAD;\n        fiber_context_switch_try_notify(ctx, origin_ctx);\n\n        zend_observer_fiber_destroy_notify(ctx->fiber_context);\n\n        if (ctx->fiber_context != nullptr) {\n            efree(ctx->fiber_context);\n        }\n    }\n}\n\nzend_fiber_status PHPCoroutine::fiber_get_status(const PHPContext *ctx) {\n    // main_context\n    if (ctx->fiber_context == EG(main_fiber_context)) {\n        return ZEND_FIBER_STATUS_RUNNING;\n    }\n\n    switch (ctx->co->get_state()) {\n    case Coroutine::STATE_INIT:\n        return ZEND_FIBER_STATUS_INIT;\n    case Coroutine::STATE_WAITING:\n        return ZEND_FIBER_STATUS_SUSPENDED;\n    case Coroutine::STATE_RUNNING:\n        return ZEND_FIBER_STATUS_RUNNING;\n    case Coroutine::STATE_END:\n        return ZEND_FIBER_STATUS_DEAD;\n    default:\n        php_swoole_fatal_error(E_ERROR, \"Unexpected state when get fiber status\");\n        return ZEND_FIBER_STATUS_DEAD;\n    }\n}\n\nvoid PHPCoroutine::fiber_context_switch_notify(const PHPContext *from, PHPContext *to) {\n    zend_fiber_context *from_context = from->fiber_context;\n    zend_fiber_context *to_context = to->fiber_context;\n\n    from_context->status = fiber_get_status(from);\n    to_context->status = fiber_get_status(to);\n\n    if (!to->fiber_init_notified) {\n        to_context->status = ZEND_FIBER_STATUS_INIT;\n        zend_observer_fiber_switch_notify(from_context, to_context);\n        to_context->status = fiber_get_status(to);\n        to->fiber_init_notified = true;\n    } else {\n        zend_observer_fiber_switch_notify(from_context, to_context);\n    }\n}\n\nvoid PHPCoroutine::fiber_context_switch_try_notify(const PHPContext *from, PHPContext *to) {\n    if (UNEXPECTED(from->fiber_context && to->fiber_context)) {\n        fiber_context_switch_notify(from, to);\n    }\n}\n\n#ifdef ZEND_CHECK_STACK_LIMIT\nvoid *PHPCoroutine::stack_limit(PHPContext *ctx) {\n#ifdef SW_USE_THREAD_CONTEXT\n    return nullptr;\n#else\n    zend_ulong reserve = EG(reserved_stack_size);\n\n#ifdef __APPLE__\n    /* On Apple Clang, the stack probing function ___chkstk_darwin incorrectly\n     * probes a location that is twice the entered function's stack usage away\n     * from the stack pointer, when using an alternative stack.\n     * https://openradar.appspot.com/radar?id=5497722702397440\n     */\n    reserve = reserve * 2;\n#endif\n\n    if (!ctx->co) {\n        return nullptr;\n    }\n\n    /* stack->pointer is the end of the stack */\n    return (int8_t *) ctx->co->get_ctx().get_stack() + reserve;\n#endif\n}\nvoid *PHPCoroutine::stack_base(PHPContext *ctx) {\n#ifdef SW_USE_THREAD_CONTEXT\n    return nullptr;\n#else\n    if (!ctx->co) {\n        return nullptr;\n    }\n\n    return (void *) ((uintptr_t) ctx->co->get_ctx().get_stack() + ctx->co->get_ctx().get_stack_size());\n#endif\n}\n#endif /* ZEND_CHECK_STACK_LIMIT */\n\n/* hook autoload */\n\nstatic zend_class_entry *(*original_zend_autoload)(zend_string *name, zend_string *lc_name);\n\nstruct AutoloadContext {\n    Coroutine *coroutine;\n    zend_class_entry *ce;\n};\n\nstruct AutoloadQueue {\n    Coroutine *coroutine;\n    std::queue<AutoloadContext *> *queue;\n};\n\nstatic zend_class_entry *swoole_coroutine_autoload(zend_string *name, zend_string *lc_name) {\n    auto current = Coroutine::get_current();\n    if (!current) {\n        return original_zend_autoload(name, lc_name);\n    }\n\n    ZEND_ASSERT(EG(in_autoload) != nullptr);\n    zend_hash_del(EG(in_autoload), lc_name);\n\n    if (UNEXPECTED(SWOOLE_G(in_autoload) == nullptr)) {\n        ALLOC_HASHTABLE(SWOOLE_G(in_autoload));\n        zend_hash_init(SWOOLE_G(in_autoload), 8, nullptr, nullptr, 0);\n    }\n    zval *z_queue = zend_hash_find(SWOOLE_G(in_autoload), lc_name);\n    if (z_queue != nullptr) {\n        auto queue = (AutoloadQueue *) Z_PTR_P(z_queue);\n        if (queue->coroutine == current) {\n            return nullptr;\n        }\n        AutoloadContext context;\n        context.coroutine = current;\n        context.ce = nullptr;\n        queue->queue->push(&context);\n        current->yield();\n        return context.ce;\n    }\n    AutoloadQueue queue;\n    queue.coroutine = current;\n    std::queue<AutoloadContext *> queue_object;\n    queue.queue = &queue_object;\n\n    zend_hash_add_ptr(SWOOLE_G(in_autoload), lc_name, &queue);\n    zend_class_entry *ce = original_zend_autoload(name, lc_name);\n    zend_hash_del(SWOOLE_G(in_autoload), lc_name);\n\n    AutoloadContext *pending_context = nullptr;\n    while (!queue_object.empty()) {\n        pending_context = queue_object.front();\n        queue_object.pop();\n        pending_context->ce = ce;\n        pending_context->coroutine->resume();\n    }\n    return ce;\n}\n\nvoid php_swoole_coroutine_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY_BASE(swoole_coroutine_util, \"Swoole\\\\Coroutine\", \"Co\", swoole_coroutine_methods, nullptr);\n    SW_SET_CLASS_CREATE(swoole_coroutine_util, sw_zend_create_object_deny);\n\n    SW_INIT_CLASS_ENTRY_BASE(\n        swoole_coroutine_iterator, \"Swoole\\\\Coroutine\\\\Iterator\", \"Co\\\\Iterator\", nullptr, spl_ce_ArrayIterator);\n    SW_INIT_CLASS_ENTRY_BASE(\n        swoole_coroutine_context, \"Swoole\\\\Coroutine\\\\Context\", \"Co\\\\Context\", nullptr, spl_ce_ArrayObject);\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_DEFAULT_MAX_CORO_NUM\", SW_DEFAULT_MAX_CORO_NUM);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_CORO_MAX_NUM_LIMIT\", Coroutine::MAX_NUM_LIMIT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_CORO_INIT\", Coroutine::STATE_INIT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_CORO_WAITING\", Coroutine::STATE_WAITING);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_CORO_RUNNING\", Coroutine::STATE_RUNNING);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_CORO_END\", Coroutine::STATE_END);\n\n    // prohibit exit in coroutine\n    SW_INIT_CLASS_ENTRY_EX(\n        swoole_exit_exception, \"Swoole\\\\ExitException\", nullptr, swoole_exit_exception_methods, swoole_exception);\n    zend_declare_property_long(swoole_exit_exception_ce, ZEND_STRL(\"flags\"), 0, ZEND_ACC_PRIVATE);\n    zend_declare_property_long(swoole_exit_exception_ce, ZEND_STRL(\"status\"), 0, ZEND_ACC_PRIVATE);\n\n    SW_INIT_CLASS_ENTRY_EX2(swoole_coroutine_canceled_exception,\n                            \"Swoole\\\\Coroutine\\\\CanceledException\",\n                            nullptr,\n                            nullptr,\n                            zend_ce_exception,\n                            zend_get_std_object_handlers());\n    swoole_coroutine_canceled_exception_ce->ce_flags |= ZEND_ACC_FINAL;\n\n    SW_INIT_CLASS_ENTRY_EX2(swoole_coroutine_timeout_exception,\n                            \"Swoole\\\\Coroutine\\\\TimeoutException\",\n                            nullptr,\n                            nullptr,\n                            zend_ce_exception,\n                            zend_get_std_object_handlers());\n    swoole_coroutine_timeout_exception_ce->ce_flags |= ZEND_ACC_FINAL;\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_EXIT_IN_COROUTINE\", SW_EXIT_IN_COROUTINE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_EXIT_IN_SERVER\", SW_EXIT_IN_SERVER);\n\n    if (SWOOLE_G(cli)) {\n#if PHP_VERSION_ID < 80400\n        ori_exit_handler = zend_get_user_opcode_handler(ZEND_EXIT);\n        zend_set_user_opcode_handler(ZEND_EXIT, coro_exit_handler);\n#endif\n\n        ori_begin_silence_handler = zend_get_user_opcode_handler(ZEND_BEGIN_SILENCE);\n        zend_set_user_opcode_handler(ZEND_BEGIN_SILENCE, coro_begin_silence_handler);\n\n        ori_end_silence_handler = zend_get_user_opcode_handler(ZEND_END_SILENCE);\n        zend_set_user_opcode_handler(ZEND_END_SILENCE, coro_end_silence_handler);\n    }\n\n    /* hook autoload */\n    original_zend_autoload = zend_autoload;\n    zend_autoload = swoole_coroutine_autoload;\n    SWOOLE_G(in_autoload) = nullptr;\n}\n\nvoid php_swoole_coroutine_rinit() {\n    PHPCoroutine::init_main_context();\n}\n\nvoid php_swoole_coroutine_rshutdown() {\n    if (SWOOLE_G(in_autoload)) {\n        zend_hash_destroy(SWOOLE_G(in_autoload));\n        FREE_HASHTABLE(SWOOLE_G(in_autoload));\n        SWOOLE_G(in_autoload) = nullptr;\n    }\n\n    PHPCoroutine::shutdown();\n}\n\nstatic PHP_METHOD(swoole_exit_exception, getFlags) {\n    SW_RETURN_PROPERTY(\"flags\");\n}\n\nstatic PHP_METHOD(swoole_exit_exception, getStatus) {\n    SW_RETURN_PROPERTY(\"status\");\n}\n\nPHP_FUNCTION(swoole_coroutine_create) {\n    zend_fcall_info fci;\n    zend_fcall_info_cache fci_cache;\n\n    ZEND_PARSE_PARAMETERS_START(1, -1)\n    Z_PARAM_FUNC(fci, fci_cache)\n    Z_PARAM_VARIADIC('*', fci.params, fci.param_count)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (sw_unlikely(SWOOLE_G(req_status) == PHP_SWOOLE_CALL_USER_SHUTDOWNFUNC_BEGIN)) {\n        auto *func = EG(current_execute_data)->prev_execute_data->func;\n        if (func->common.function_name &&\n            sw_unlikely(memcmp(ZSTR_VAL(func->common.function_name), ZEND_STRS(\"__destruct\")) == 0)) {\n            php_swoole_fatal_error(E_ERROR, \"can not use coroutine in __destruct after php_request_shutdown\");\n            RETURN_FALSE;\n        }\n    }\n\n    long cid = PHPCoroutine::create(&fci_cache, fci.param_count, fci.params, &fci.function_name);\n    if (sw_likely(cid > 0)) {\n        RETURN_LONG(cid);\n    } else {\n        RETURN_FALSE;\n    }\n}\n\nPHP_FUNCTION(swoole_coroutine_defer) {\n    zend_fcall_info fci;\n    zend_fcall_info_cache fci_cache;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_FUNC(fci, fci_cache)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    Coroutine::get_current_safe();\n    auto *defer_fci = static_cast<zend::Function *>(emalloc(sizeof(zend::Function)));\n    defer_fci->fci = fci;\n    defer_fci->fci_cache = fci_cache;\n    sw_zend_fci_cache_persist(&defer_fci->fci_cache);\n    PHPCoroutine::defer(defer_fci);\n}\n\nstatic PHP_METHOD(swoole_coroutine, stats) {\n    array_init(return_value);\n\n    add_assoc_long_ex(return_value, ZEND_STRL(\"event_num\"), sw_reactor() ? sw_reactor()->get_event_num() : 0);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"signal_listener_num\"), swoole_signal_get_listener_num());\n\n    if (sw_async_threads()) {\n        add_assoc_long_ex(return_value, ZEND_STRL(\"aio_task_num\"), sw_async_threads()->get_task_num());\n        add_assoc_long_ex(return_value, ZEND_STRL(\"aio_worker_num\"), sw_async_threads()->get_worker_num());\n        add_assoc_long_ex(return_value, ZEND_STRL(\"aio_queue_size\"), sw_async_threads()->get_queue_size());\n    } else {\n        add_assoc_long_ex(return_value, ZEND_STRL(\"aio_task_num\"), 0);\n        add_assoc_long_ex(return_value, ZEND_STRL(\"aio_worker_num\"), 0);\n        add_assoc_long_ex(return_value, ZEND_STRL(\"aio_queue_size\"), 0);\n    }\n\n    add_assoc_long_ex(return_value, ZEND_STRL(\"c_stack_size\"), Coroutine::get_stack_size());\n    add_assoc_long_ex(return_value, ZEND_STRL(\"coroutine_num\"), Coroutine::count());\n    add_assoc_long_ex(return_value, ZEND_STRL(\"coroutine_peak_num\"), Coroutine::get_peak_num());\n    add_assoc_long_ex(return_value, ZEND_STRL(\"coroutine_last_cid\"), Coroutine::get_last_cid());\n\n#ifdef SW_USE_IOURING\n    auto iouring = SwooleTG.iouring;\n    if (iouring) {\n        add_assoc_long_ex(return_value, ZEND_STRL(\"iouring_task_num\"), iouring->get_task_num());\n        add_assoc_long_ex(return_value, ZEND_STRL(\"iouring_sq_usage_percent\"), iouring->get_sq_usage_percent());\n        add_assoc_long_ex(return_value, ZEND_STRL(\"iouring_waiting_task_num\"), iouring->get_waiting_task_num());\n    }\n#endif\n}\n\nPHP_METHOD(swoole_coroutine, getCid) {\n    RETURN_LONG(PHPCoroutine::get_cid());\n}\n\nPHP_METHOD(swoole_coroutine, getPcid) {\n    zend_long cid = 0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(cid)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zend_long ret = PHPCoroutine::get_pcid(cid);\n    if (ret == 0) {\n        RETURN_FALSE;\n    }\n    RETURN_LONG(ret);\n}\n\nstatic PHP_METHOD(swoole_coroutine, getContext) {\n    zend_long cid = 0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(cid)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    auto *ctx = static_cast<PHPContext *>(\n        (EXPECTED(cid == 0) ? Coroutine::get_current_task() : Coroutine::get_task_by_cid(cid)));\n    if (UNEXPECTED(!ctx)) {\n        swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS);\n        RETURN_NULL();\n    }\n    if (UNEXPECTED(ctx->context == reinterpret_cast<zend_object *>(INVALID_PTR))) {\n        /* bad context (has been destroyed), see: https://github.com/swoole/swoole-src/issues/2991 */\n        php_swoole_fatal_error(E_WARNING, \"The context of coroutine has been destroyed\");\n        RETURN_NULL();\n    }\n    if (UNEXPECTED(!ctx->context)) {\n        object_init_ex(return_value, swoole_coroutine_context_ce);\n        ctx->context = Z_OBJ_P(return_value);\n    }\n    GC_ADDREF(ctx->context);\n    RETURN_OBJ(ctx->context);\n}\n\nstatic PHP_METHOD(swoole_coroutine, getElapsed) {\n    zend_long cid = 0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(cid)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_LONG(PHPCoroutine::get_elapsed(cid));\n}\n\nstatic PHP_METHOD(swoole_coroutine, getStackUsage) {\n    zend_long cid = 0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(cid)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    ssize_t usage = PHPCoroutine::get_stack_usage(cid);\n    if (usage < 0) {\n        RETURN_FALSE;\n    }\n    RETURN_LONG(usage);\n}\n\nstatic PHP_METHOD(swoole_coroutine, exists) {\n    zend_long cid;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(cid)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(Coroutine::get_by_cid(cid) != nullptr);\n}\n\nstatic PHP_METHOD(swoole_coroutine, resume) {\n    zend_long cid;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(cid)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    auto coroutine_iterator = user_yield_coros.find(cid);\n    if (coroutine_iterator == user_yield_coros.end()) {\n        php_swoole_fatal_error(E_WARNING, \"can not resume the coroutine which is in IO operation or non-existent\");\n        RETURN_FALSE;\n    }\n\n    Coroutine *co = coroutine_iterator->second;\n    user_yield_coros.erase(cid);\n    co->resume();\n\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_coroutine, yield) {\n    Coroutine *co = Coroutine::get_current_safe();\n    user_yield_coros[co->get_cid()] = co;\n\n    Coroutine::CancelFunc cancel_fn = [](Coroutine *co) {\n        user_yield_coros.erase(co->get_cid());\n        co->resume();\n        return true;\n    };\n    co->yield(&cancel_fn);\n    if (co->is_canceled()) {\n        swoole_set_last_error(SW_ERROR_CO_CANCELED);\n        RETURN_FALSE;\n    }\n\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_coroutine, join) {\n    Coroutine *co = Coroutine::get_current_safe();\n    zval *cid_array;\n    double timeout = -1;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_ARRAY(cid_array)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (php_swoole_array_length(cid_array) == 0) {\n        swoole_set_last_error(SW_ERROR_INVALID_PARAMS);\n        RETURN_FALSE;\n    }\n\n    std::set<PHPContext *> co_set;\n    std::shared_ptr<bool> canceled = std::make_shared<bool>(false);\n\n    PHPContext::SwapCallback join_fn = [&co_set, canceled, co](PHPContext *task) {\n        co_set.erase(task);\n        if (!co_set.empty()) {\n            return;\n        }\n        swoole_event_defer(\n            [co, canceled](void *) {\n                if (*canceled == false) {\n                    co->resume();\n                }\n            },\n            nullptr);\n    };\n\n    zval *zcid;\n    ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(cid_array), zcid) {\n        long cid = zval_get_long(zcid);\n        if (co->get_cid() == cid) {\n            php_swoole_error_ex(E_WARNING, SW_ERROR_WRONG_OPERATION, \"can not join self\");\n            RETURN_FALSE;\n        }\n        auto ctx = PHPCoroutine::get_context_by_cid(cid);\n        if (ctx == nullptr) {\n            continue;\n        }\n        if (ctx->on_close) {\n            swoole_set_last_error(SW_ERROR_WRONG_OPERATION);\n            RETURN_FALSE;\n        }\n        ctx->on_close = &join_fn;\n        co_set.insert(ctx);\n    }\n    ZEND_HASH_FOREACH_END();\n\n    if (co_set.empty()) {\n        swoole_set_last_error(SW_ERROR_INVALID_PARAMS);\n        RETURN_FALSE;\n    }\n\n    if (!co->yield_ex(timeout)) {\n        if (!co_set.empty()) {\n            for (auto ctx : co_set) {\n                ctx->on_close = nullptr;\n            }\n        }\n        *canceled = true;\n        RETURN_FALSE;\n    }\n\n    RETURN_TRUE;\n}\n\nstatic bool php_swoole_coroutine_throw_exception(long cid, bool throw_exception, zend_class_entry *ce) {\n    if (throw_exception) {\n        auto *task = PHPCoroutine::get_context_by_cid(cid);\n        // The coroutine does not exist or is not a PHP coroutine, and cannot be canceled or throw an exception\n        if (!task) {\n            swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS);\n            return false;\n        }\n\n        if (task->exception && task->exception_class == ce) {\n            swoole_set_last_error(SW_ERROR_CO_CANCELED);\n            return false;\n        }\n\n        zend_object *previous = task->exception;\n        task->exception_class = ce;\n        task->exception = zend_objects_new(task->exception_class);\n        object_properties_init(task->exception, task->exception_class);\n        if (previous) {\n            zend_exception_set_previous(task->exception, previous);\n        }\n\n        zend_execute_data *ex_backup = EG(current_execute_data);\n        EG(current_execute_data) = task->execute_data;\n        zend::object_set(task->exception, ZSTR_KNOWN(ZEND_STR_FILE), zend_get_executed_filename_ex());\n        zend::object_set(task->exception, ZSTR_KNOWN(ZEND_STR_LINE), zend_get_executed_lineno());\n        EG(current_execute_data) = ex_backup;\n\n        // Ignore the result of Co::cancel(). Even if the coroutine is in an irrevocable state,\n        // it will directly throw an exception and terminate the coroutine\n        task->co->cancel();\n        return true;\n    } else {\n        Coroutine *co = swoole_coroutine_get(cid);\n        if (!co) {\n            swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS);\n            return false;\n        }\n        return co->cancel();\n    }\n}\n\nstatic PHP_METHOD(swoole_coroutine, cancel) {\n    zend_long cid;\n    bool throw_exception = false;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_LONG(cid)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_BOOL(throw_exception)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(php_swoole_coroutine_throw_exception(cid, throw_exception, swoole_coroutine_canceled_exception_ce));\n}\n\nstatic PHP_METHOD(swoole_coroutine, setTimeLimit) {\n    double timeout = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    Coroutine *co = Coroutine::get_current_safe();\n    if (timeout == 0 || co == nullptr) {\n        RETURN_FALSE;\n    }\n\n    long cid = co->get_cid();\n    swoole_timer_add((long) timeout * 1000, false, [cid](swoole::Timer *, swoole::TimerNode *tnode) {\n        swoole_timer_del(tnode);\n        if (PHPCoroutine::get_context_by_cid(cid) != nullptr) {\n            php_swoole_coroutine_throw_exception(cid, true, swoole_coroutine_timeout_exception_ce);\n        }\n    });\n\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_coroutine, isCanceled) {\n    Coroutine *co = Coroutine::get_current_safe();\n    RETURN_BOOL(co->is_canceled());\n}\n\nPHP_FUNCTION(swoole_test_kernel_coroutine) {\n    if (!PHPCoroutine::is_activated()) {\n        RETURN_FALSE;\n    }\n\n    zend_long count = 100;\n    double sleep_time = 1.0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 2)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(count)\n    Z_PARAM_DOUBLE(sleep_time)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    Coroutine::create([=](void *ptr) {\n        SW_LOOP_N(count) {\n            System::sleep(sleep_time);\n        }\n    });\n}\n\nstatic PHP_METHOD(swoole_coroutine, getBackTrace) {\n    zend_long cid = 0;\n    zend_long options = DEBUG_BACKTRACE_PROVIDE_OBJECT;\n    zend_long limit = 0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 3)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(cid)\n    Z_PARAM_LONG(options)\n    Z_PARAM_LONG(limit)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (!cid || cid == PHPCoroutine::get_cid()) {\n        zend_fetch_debug_backtrace(return_value, 0, options, limit);\n    } else {\n        auto *task = PHPCoroutine::get_context_by_cid(cid);\n        if (UNEXPECTED(!task)) {\n            swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS);\n            RETURN_FALSE;\n        }\n        zend_execute_data *ex_backup = EG(current_execute_data);\n        EG(current_execute_data) = task->execute_data;\n        zend_fetch_debug_backtrace(return_value, 0, options, limit);\n        EG(current_execute_data) = ex_backup;\n    }\n}\n\nvoid sw_php_print_backtrace_impl(int skip_last, int options, int limit, bool include_main) {\n    zval backtrace;\n    zend_fetch_debug_backtrace(&backtrace, skip_last, options, limit);\n    ZEND_ASSERT(Z_TYPE(backtrace) == IS_ARRAY);\n\n    zend_string *str = zend_trace_to_string(Z_ARRVAL(backtrace), include_main);\n    ZEND_WRITE(ZSTR_VAL(str), ZSTR_LEN(str));\n    zend_string_release(str);\n    zval_ptr_dtor(&backtrace);\n}\n\nvoid sw_php_print_backtrace(zend_long cid, zend_long options, zend_long limit, zval *return_value) {\n    if (!cid || cid == PHPCoroutine::get_cid()) {\n        sw_php_print_backtrace_impl(1, options, limit);\n    } else {\n        PHPContext *ctx = PHPCoroutine::get_context_by_cid(cid);\n        if (UNEXPECTED(!ctx)) {\n            swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS);\n            if (return_value) {\n                RETVAL_FALSE;\n            }\n            return;\n        }\n        zend_execute_data *ex_backup = EG(current_execute_data);\n        EG(current_execute_data) = ctx->execute_data;\n        sw_php_print_backtrace_impl(0, options, limit);\n        EG(current_execute_data) = ex_backup;\n    }\n}\n\nstatic PHP_METHOD(swoole_coroutine, printBackTrace) {\n    zend_long cid = 0;\n    zend_long options = 0;\n    zend_long limit = 0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 3)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(cid)\n    Z_PARAM_LONG(options)\n    Z_PARAM_LONG(limit)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    sw_php_print_backtrace(cid, options, limit, return_value);\n}\n\nstatic PHP_METHOD(swoole_coroutine, list) {\n    zval zlist;\n    array_init(&zlist);\n    for (auto &co : Coroutine::coroutines) {\n        add_next_index_long(&zlist, co.second->get_cid());\n    }\n    object_init_ex(return_value, swoole_coroutine_iterator_ce);\n    sw_zend_call_method_with_1_params(return_value,\n                                      swoole_coroutine_iterator_ce,\n                                      &swoole_coroutine_iterator_ce->constructor,\n                                      \"__construct\",\n                                      nullptr,\n                                      &zlist);\n    zval_ptr_dtor(&zlist);\n}\n\n#ifdef SW_CORO_TIME\nstatic PHP_METHOD(swoole_coroutine, getExecuteTime) {\n    RETURN_LONG(PHPCoroutine::get_execute_time());\n}\n#endif\n\nPHP_METHOD(swoole_coroutine, enableScheduler) {\n    RETURN_BOOL(PHPCoroutine::enable_scheduler());\n}\n\nPHP_METHOD(swoole_coroutine, disableScheduler) {\n    RETURN_BOOL(PHPCoroutine::disable_scheduler());\n}\n\n/**\n * for gdb\n */\nzend_executor_globals *php_swoole_get_executor_globals() {\n    return (zend_executor_globals *) &EG(uninitialized_zval);\n}\n"
  },
  {
    "path": "ext-src/swoole_coroutine_lock.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_private.h\"\n#include \"swoole_memory.h\"\n#include \"swoole_lock.h\"\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_coroutine_lock_arginfo.h\"\nEND_EXTERN_C()\n\nusing swoole::CoroutineLock;\n\nstatic zend_class_entry *swoole_coroutine_lock_ce;\nstatic zend_object_handlers swoole_coroutine_lock_handlers;\n\nstruct CoLockObject {\n    CoroutineLock *lock;\n    bool shared;\n    zend_object std;\n};\n\nstatic CoLockObject *co_lock_fetch_object(zend_object *obj) {\n    return (CoLockObject *) ((char *) obj - swoole_coroutine_lock_handlers.offset);\n}\n\nstatic CoroutineLock *co_lock_get_ptr(zval *zobject) {\n    return co_lock_fetch_object(Z_OBJ_P(zobject))->lock;\n}\n\nstatic CoroutineLock *co_lock_get_and_check_ptr(zval *zobject) {\n    CoroutineLock *lock = co_lock_get_ptr(zobject);\n    if (UNEXPECTED(!lock)) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION, \"must call constructor first\");\n    }\n    return lock;\n}\n\nstatic void co_lock_set_ptr(zval *zobject, CoroutineLock *ptr) {\n    co_lock_fetch_object(Z_OBJ_P(zobject))->lock = ptr;\n}\n\nstatic void co_lock_free_object(zend_object *object) {\n    CoLockObject *o = co_lock_fetch_object(object);\n    if (o->lock && !o->shared) {\n        delete o->lock;\n    }\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *co_lock_create_object(zend_class_entry *ce) {\n    auto *lock = static_cast<CoLockObject *>(zend_object_alloc(sizeof(CoLockObject), ce));\n    zend_object_std_init(&lock->std, ce);\n    object_properties_init(&lock->std, ce);\n    lock->std.handlers = &swoole_coroutine_lock_handlers;\n    return &lock->std;\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_coroutine_lock, __construct);\nstatic PHP_METHOD(swoole_coroutine_lock, lock);\nstatic PHP_METHOD(swoole_coroutine_lock, unlock);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic constexpr zend_function_entry swoole_coroutine_lock_methods[] =\n{\n    PHP_ME(swoole_coroutine_lock, __construct,  arginfo_class_Swoole_Coroutine_Lock___construct,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_coroutine_lock, lock,         arginfo_class_Swoole_Coroutine_Lock_lock,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_coroutine_lock, unlock,       arginfo_class_Swoole_Coroutine_Lock_unlock,       ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_coroutine_lock_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_coroutine_lock, \"Swoole\\\\Coroutine\\\\Lock\", nullptr, swoole_coroutine_lock_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_coroutine_lock);\n    SW_SET_CLASS_CLONEABLE(swoole_coroutine_lock, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_coroutine_lock, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(swoole_coroutine_lock, co_lock_create_object, co_lock_free_object, CoLockObject, std);\n    zend_declare_property_long(swoole_coroutine_lock_ce, ZEND_STRL(\"errCode\"), 0, ZEND_ACC_PUBLIC);\n}\n\nstatic PHP_METHOD(swoole_coroutine_lock, __construct) {\n    CoroutineLock *lock = co_lock_get_ptr(ZEND_THIS);\n    if (lock != nullptr) {\n        zend_throw_error(nullptr, \"Constructor of %s can only be called once\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        RETURN_FALSE;\n    }\n\n    zend_bool shared = false;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_BOOL(shared)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    lock = new CoroutineLock(shared);\n    co_lock_set_ptr(ZEND_THIS, lock);\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_coroutine_lock, lock) {\n    zend_long operation = LOCK_EX;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(operation)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    CoroutineLock *lock = co_lock_get_and_check_ptr(ZEND_THIS);\n    SW_LOCK_CHECK_RETURN(lock->lock(operation));\n}\n\nstatic PHP_METHOD(swoole_coroutine_lock, unlock) {\n    CoroutineLock *lock = co_lock_get_and_check_ptr(ZEND_THIS);\n    SW_LOCK_CHECK_RETURN(lock->unlock());\n}\n"
  },
  {
    "path": "ext-src/swoole_coroutine_scheduler.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Xinyu Zhu  <xyzhu1120@gmail.com>                             |\n  |         shiguangqi <shiguangqi2008@gmail.com>                        |\n  |         Tianfeng Han  <mikan.tenny@gmail.com>                        |\n  +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_cxx.h\"\n\n#include <queue>\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_coroutine_scheduler_arginfo.h\"\nEND_EXTERN_C()\n\nusing swoole::Coroutine;\nusing swoole::NameResolver;\nusing swoole::PHPCoroutine;\nusing swoole::Reactor;\nusing swoole::coroutine::System;\n\nstruct SchedulerTask {\n    zend_long count;\n    zend_fcall_info fci;\n    zend_fcall_info_cache fci_cache;\n};\n\nstruct SchedulerObject {\n    std::queue<SchedulerTask *> *list;\n    bool started;\n    zend_object std;\n};\n\nstatic zend_class_entry *swoole_coroutine_scheduler_ce;\nstatic zend_object_handlers swoole_coroutine_scheduler_handlers;\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_coroutine_scheduler, add);\nstatic PHP_METHOD(swoole_coroutine_scheduler, parallel);\nstatic PHP_METHOD(swoole_coroutine_scheduler, start);\nSW_EXTERN_C_END\n\nstatic sw_inline SchedulerObject *scheduler_get_object(zend_object *obj) {\n    return reinterpret_cast<SchedulerObject *>(reinterpret_cast<char *>(obj) -\n                                               swoole_coroutine_scheduler_handlers.offset);\n}\n\nstatic zend_object *scheduler_create_object(zend_class_entry *ce) {\n    auto *s = static_cast<SchedulerObject *>(zend_object_alloc(sizeof(SchedulerObject), ce));\n    zend_object_std_init(&s->std, ce);\n    object_properties_init(&s->std, ce);\n    s->std.handlers = &swoole_coroutine_scheduler_handlers;\n    return &s->std;\n}\n\nstatic void scheduler_free_object(zend_object *object) {\n    SchedulerObject *s = scheduler_get_object(object);\n    if (s->list) {\n        while (!s->list->empty()) {\n            SchedulerTask *task = s->list->front();\n            s->list->pop();\n            sw_zend_fci_cache_discard(&task->fci_cache);\n            sw_zend_fci_params_discard(&task->fci);\n            efree(task);\n        }\n        delete s->list;\n        s->list = nullptr;\n    }\n    zend_object_std_dtor(&s->std);\n}\n\n// clang-format off\nstatic const zend_function_entry swoole_coroutine_scheduler_methods[] = {\n    PHP_ME(swoole_coroutine_scheduler, add,        arginfo_class_Swoole_Coroutine_Scheduler_add,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_coroutine_scheduler, parallel,   arginfo_class_Swoole_Coroutine_Scheduler_parallel,   ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_coroutine_scheduler, set,        arginfo_class_Swoole_Coroutine_Scheduler_set,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_coroutine_scheduler, getOptions, arginfo_class_Swoole_Coroutine_Scheduler_getOptions, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_coroutine_scheduler, start,      arginfo_class_Swoole_Coroutine_Scheduler_start,      ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n\n// clang-format on\n\nvoid php_swoole_coroutine_scheduler_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_coroutine_scheduler,\n                        \"Swoole\\\\Coroutine\\\\Scheduler\",\n                        \"Co\\\\Scheduler\",\n                        swoole_coroutine_scheduler_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_coroutine_scheduler);\n    SW_SET_CLASS_CLONEABLE(swoole_coroutine_scheduler, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_coroutine_scheduler, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_coroutine_scheduler);\n    SW_SET_CLASS_CUSTOM_OBJECT(\n        swoole_coroutine_scheduler, scheduler_create_object, scheduler_free_object, SchedulerObject, std);\n    swoole_coroutine_scheduler_ce->ce_flags |= ZEND_ACC_FINAL;\n}\n\nstatic zend::Callable *exit_condition_fci_cache = nullptr;\n\nstatic bool php_swoole_coroutine_reactor_can_exit(Reactor *reactor, size_t &event_num) {\n    zval retval;\n\n    SW_ASSERT(exit_condition_fci_cache);\n    ZVAL_NULL(&retval);\n    int success = sw_zend_call_function_ex(nullptr, exit_condition_fci_cache->ptr(), 0, nullptr, &retval);\n    if (UNEXPECTED(success != SUCCESS)) {\n        php_swoole_fatal_error(E_ERROR, \"Coroutine can_exit callback handler error\");\n    }\n    if (UNEXPECTED(EG(exception))) {\n        zend_exception_error(EG(exception), E_ERROR);\n    }\n    return Z_TYPE_P(&retval) != IS_FALSE;\n}\n\nvoid php_swoole_coroutine_scheduler_rshutdown() {\n    swoole_name_resolver_each([](const std::list<NameResolver>::iterator &iter) -> swTraverseOperation {\n        if (iter->type == NameResolver::TYPE_PHP) {\n            zval_dtor((zval *) iter->private_data);\n            efree(iter->private_data);\n            return SW_TRAVERSE_REMOVE;\n        } else {\n            return SW_TRAVERSE_KEEP;\n        }\n    });\n\n    if (exit_condition_fci_cache) {\n        sw_callable_free(exit_condition_fci_cache);\n        exit_condition_fci_cache = nullptr;\n    }\n}\n\nvoid php_swoole_set_coroutine_option(zend_array *vht) {\n    zval *ztmp;\n    if (php_swoole_array_get_value(vht, \"max_coro_num\", ztmp) ||\n        php_swoole_array_get_value(vht, \"max_coroutine\", ztmp)) {\n        zend_long max_num = zval_get_long(ztmp);\n        PHPCoroutine::set_max_num(max_num <= 0 ? SW_DEFAULT_MAX_CORO_NUM : max_num);\n    }\n    if (php_swoole_array_get_value(vht, \"enable_deadlock_check\", ztmp)) {\n        PHPCoroutine::set_deadlock_check(zval_is_true(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"hook_flags\", ztmp)) {\n        PHPCoroutine::set_hook_flags(zval_get_long(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"enable_preemptive_scheduler\", ztmp)) {\n        PHPCoroutine::enable_preemptive_scheduler(zval_is_true(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"c_stack_size\", ztmp) || php_swoole_array_get_value(vht, \"stack_size\", ztmp)) {\n        Coroutine::set_stack_size(php_swoole_parse_to_size(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"name_resolver\", ztmp)) {\n        if (!ZVAL_IS_ARRAY(ztmp)) {\n            php_swoole_fatal_error(E_WARNING, \"name_resolver must be an array\");\n        } else {\n            zend_hash_apply(Z_ARR_P(ztmp), [](zval *zresolver) -> int {\n                php_swoole_name_resolver_add(zresolver);\n                return ZEND_HASH_APPLY_KEEP;\n            });\n        }\n    }\n    if (PHPCoroutine::options) {\n        zend_hash_merge(PHPCoroutine::options, vht, zval_add_ref, true);\n    } else {\n        PHPCoroutine::options = zend_array_dup(vht);\n    }\n}\n\nPHP_METHOD(swoole_coroutine_scheduler, set) {\n    zval *zset = nullptr;\n    HashTable *vht = nullptr;\n    zval *ztmp;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ARRAY(zset)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    vht = Z_ARRVAL_P(zset);\n    php_swoole_set_global_option(vht);\n    php_swoole_set_coroutine_option(vht);\n\n    if (php_swoole_array_get_value(vht, \"dns_cache_expire\", ztmp)) {\n        System::set_dns_cache_expire((time_t) zval_get_long(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"dns_cache_capacity\", ztmp)) {\n        System::set_dns_cache_capacity((size_t) zval_get_long(ztmp));\n    }\n    /* Reactor can exit */\n    if ((ztmp = zend_hash_str_find(vht, ZEND_STRL(\"exit_condition\")))) {\n        if (exit_condition_fci_cache) {\n            sw_callable_free(exit_condition_fci_cache);\n        }\n\n        exit_condition_fci_cache = sw_callable_create(ztmp);\n        if (exit_condition_fci_cache) {\n            SwooleG.user_exit_condition = php_swoole_coroutine_reactor_can_exit;\n            if (sw_reactor()) {\n                sw_reactor()->set_exit_condition(Reactor::EXIT_CONDITION_USER_AFTER_DEFAULT,\n                                                 SwooleG.user_exit_condition);\n            }\n        } else {\n            if (sw_reactor()) {\n                sw_reactor()->remove_exit_condition(Reactor::EXIT_CONDITION_USER_AFTER_DEFAULT);\n                SwooleG.user_exit_condition = nullptr;\n            }\n        }\n    }\n}\n\nPHP_METHOD(swoole_coroutine_scheduler, getOptions) {\n    if (!PHPCoroutine::options) {\n        return;\n    }\n    RETURN_ARR(zend_array_dup(PHPCoroutine::options));\n}\n\nstatic void scheduler_add_task(SchedulerObject *s, SchedulerTask *task) {\n    if (!s->list) {\n        s->list = new std::queue<SchedulerTask *>;\n    }\n    sw_zend_fci_cache_persist(&task->fci_cache);\n    sw_zend_fci_params_persist(&task->fci);\n    s->list->push(task);\n}\n\nstatic PHP_METHOD(swoole_coroutine_scheduler, add) {\n    SchedulerObject *s = scheduler_get_object(Z_OBJ_P(ZEND_THIS));\n    if (s->started) {\n        php_swoole_fatal_error(\n            E_WARNING, \"scheduler is running, unable to execute %s->add\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        RETURN_FALSE;\n    }\n\n    auto *task = static_cast<SchedulerTask *>(ecalloc(1, sizeof(SchedulerTask)));\n\n    ZEND_PARSE_PARAMETERS_START(1, -1)\n    Z_PARAM_FUNC(task->fci, task->fci_cache)\n    Z_PARAM_VARIADIC('*', task->fci.params, task->fci.param_count)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    task->count = 1;\n    scheduler_add_task(s, task);\n}\n\nstatic PHP_METHOD(swoole_coroutine_scheduler, parallel) {\n    SchedulerObject *s = scheduler_get_object(Z_OBJ_P(ZEND_THIS));\n    if (s->started) {\n        php_swoole_fatal_error(\n            E_WARNING, \"scheduler is running, unable to execute %s->parallel\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        RETURN_FALSE;\n    }\n\n    auto *task = static_cast<SchedulerTask *>(ecalloc(1, sizeof(SchedulerTask)));\n    zend_long count;\n\n    ZEND_PARSE_PARAMETERS_START(2, -1)\n    Z_PARAM_LONG(count)\n    Z_PARAM_FUNC(task->fci, task->fci_cache)\n    Z_PARAM_VARIADIC('*', task->fci.params, task->fci.param_count)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    task->count = count;\n    scheduler_add_task(s, task);\n}\n\nstatic PHP_METHOD(swoole_coroutine_scheduler, start) {\n    SchedulerObject *s = scheduler_get_object(Z_OBJ_P(ZEND_THIS));\n\n    if (s->started) {\n        php_swoole_fatal_error(\n            E_WARNING, \"scheduler is started, unable to execute %s->start\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        RETURN_FALSE;\n    }\n    if (php_swoole_reactor_init() < 0) {\n        RETURN_FALSE;\n    }\n\n    s->started = true;\n\n    if (!s->list) {\n        php_swoole_fatal_error(E_WARNING, \"no coroutine task\");\n        RETURN_FALSE;\n    }\n\n    while (!s->list->empty()) {\n        SchedulerTask *task = s->list->front();\n        s->list->pop();\n        for (zend_long i = 0; i < task->count; i++) {\n            PHPCoroutine::create(&task->fci_cache, task->fci.param_count, task->fci.params, &task->fci.function_name);\n        }\n        sw_zend_fci_cache_discard(&task->fci_cache);\n        sw_zend_fci_params_discard(&task->fci);\n        efree(task);\n    }\n    php_swoole_event_wait();\n    delete s->list;\n    s->list = nullptr;\n    s->started = false;\n    RETURN_TRUE;\n}\n"
  },
  {
    "path": "ext-src/swoole_coroutine_system.cc",
    "content": "/*\n+----------------------------------------------------------------------+\n| Swoole                                                               |\n+----------------------------------------------------------------------+\n| This source file is subject to version 2.0 of the Apache license,    |\n| that is bundled with this package in the file LICENSE, and is        |\n| available through the world-wide-web at the following url:           |\n| http://www.apache.org/licenses/LICENSE-2.0.html                      |\n| If you did not receive a copy of the Apache2.0 license and are unable|\n| to obtain it through the world-wide-web, please send a note to       |\n| license@swoole.com so we can mail you a copy immediately.            |\n+----------------------------------------------------------------------+\n| Author: Tianfeng Han  <rango@swoole.com>                             |\n+----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_coroutine_system.h\"\n\n#include \"ext/standard/file.h\"\n#include <sys/file.h>\n\n#include <string>\n\nusing swoole::Coroutine;\nusing swoole::Event;\nusing swoole::PHPCoroutine;\nusing swoole::Reactor;\nusing swoole::String;\nusing swoole::TimerNode;\nusing swoole::coroutine::System;\n\nenum FileOperateFlag {\n    SW_FILE_LOCK = LOCK_EX,\n    SW_FILE_APPEND = PHP_FILE_APPEND,\n};\n\nstatic zend_class_entry *swoole_coroutine_system_ce;\n\n// clang-format off\nstatic const zend_function_entry swoole_coroutine_system_methods[] =\n{\n    ZEND_FENTRY(gethostbyname,      ZEND_FN(swoole_coroutine_gethostbyname), arginfo_class_Swoole_Coroutine_System_gethostbyname, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(dnsLookup,          ZEND_FN(swoole_async_dns_lookup_coro),   arginfo_class_Swoole_Coroutine_System_dnsLookup,     ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, exec,                                    arginfo_class_Swoole_Coroutine_System_exec,          ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, sleep,                                   arginfo_class_Swoole_Coroutine_System_sleep,         ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, getaddrinfo,                             arginfo_class_Swoole_Coroutine_System_getaddrinfo,   ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, statvfs,                                 arginfo_class_Swoole_Coroutine_System_statvfs,       ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, readFile,                                arginfo_class_Swoole_Coroutine_System_readFile,      ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, writeFile,                               arginfo_class_Swoole_Coroutine_System_writeFile,     ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, wait,                                    arginfo_class_Swoole_Coroutine_System_wait,          ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, waitPid,                                 arginfo_class_Swoole_Coroutine_System_waitPid,       ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, waitSignal,                              arginfo_class_Swoole_Coroutine_System_waitSignal,    ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_coroutine_system, waitEvent,                               arginfo_class_Swoole_Coroutine_System_waitEvent,     ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_FE_END\n};\n\n// clang-format on\n\nvoid php_swoole_coroutine_system_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY_BASE(\n        swoole_coroutine_system, \"Swoole\\\\Coroutine\\\\System\", \"Co\\\\System\", swoole_coroutine_system_methods, nullptr);\n    SW_SET_CLASS_CREATE(swoole_coroutine_system, sw_zend_create_object_deny);\n\n    zend::add_constant(\"FILE_LOCK\", SW_FILE_LOCK);\n}\n\nPHP_METHOD(swoole_coroutine_system, sleep) {\n    double seconds;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_DOUBLE(seconds)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (UNEXPECTED(seconds < SW_TIMER_MIN_SEC)) {\n        php_swoole_fatal_error(E_WARNING, \"Timer must be greater than or equal to \" ZEND_TOSTR(SW_TIMER_MIN_SEC));\n        RETURN_FALSE;\n    }\n    RETURN_BOOL(System::sleep(seconds) == 0);\n}\n\nPHP_METHOD(swoole_coroutine_system, readFile) {\n    char *filename;\n    size_t l_filename;\n    zend_long flags = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_STRING(filename, l_filename)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(flags)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    auto result = System::read_file(filename, flags & SW_FILE_LOCK);\n    if (result == nullptr) {\n        RETURN_FALSE;\n    } else {\n        RETVAL_STRINGL(result->str, result->length);\n    }\n}\n\nPHP_METHOD(swoole_coroutine_system, writeFile) {\n    char *filename;\n    size_t l_filename;\n    char *data;\n    size_t l_data;\n    zend_long flags = 0;\n\n    ZEND_PARSE_PARAMETERS_START(2, 3)\n    Z_PARAM_STRING(filename, l_filename)\n    Z_PARAM_STRING(data, l_data)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(flags)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    int _flags = 0;\n    if (flags & SW_FILE_APPEND) {\n        _flags |= O_APPEND;\n    } else {\n        _flags |= O_TRUNC;\n    }\n\n    ssize_t retval = System::write_file(filename, data, l_data, flags & SW_FILE_LOCK, _flags);\n    if (retval < 0) {\n        RETURN_FALSE;\n    } else {\n        RETURN_LONG(retval);\n    }\n}\n\nPHP_FUNCTION(swoole_coroutine_gethostbyname) {\n    Coroutine::get_current_safe();\n\n    char *domain_name;\n    size_t l_domain_name;\n    zend_long family = AF_INET;\n    double timeout = -1;\n\n    ZEND_PARSE_PARAMETERS_START(1, 3)\n    Z_PARAM_STRING(domain_name, l_domain_name)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(family)\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (l_domain_name == 0) {\n        php_swoole_fatal_error(E_WARNING, \"domain name is empty\");\n        RETURN_FALSE;\n    }\n\n    if (family != AF_INET && family != AF_INET6) {\n        php_swoole_fatal_error(E_WARNING, \"unknown protocol family, must be AF_INET or AF_INET6\");\n        RETURN_FALSE;\n    }\n\n    std::string address = System::gethostbyname(std::string(domain_name, l_domain_name), family, timeout);\n    if (address.empty()) {\n        RETURN_FALSE;\n    } else {\n        RETURN_STRINGL(address.c_str(), address.length());\n    }\n}\n\nPHP_FUNCTION(swoole_clear_dns_cache) {\n    System::clear_dns_cache();\n}\n\nPHP_METHOD(swoole_coroutine_system, getaddrinfo) {\n    char *hostname;\n    size_t l_hostname;\n    zend_long family = AF_INET;\n    zend_long socktype = SOCK_STREAM;\n    zend_long protocol = IPPROTO_TCP;\n    char *service = nullptr;\n    size_t l_service = 0;\n    double timeout = -1;\n\n    ZEND_PARSE_PARAMETERS_START(1, 6)\n    Z_PARAM_STRING(hostname, l_hostname)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(family)\n    Z_PARAM_LONG(socktype)\n    Z_PARAM_LONG(protocol)\n    Z_PARAM_STRING(service, l_service)\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (l_hostname == 0) {\n        php_swoole_fatal_error(E_WARNING, \"hostname is empty\");\n        RETURN_FALSE;\n    }\n\n    if (family != AF_INET && family != AF_INET6) {\n        php_swoole_fatal_error(E_WARNING, \"unknown protocol family, must be AF_INET or AF_INET6\");\n        RETURN_FALSE;\n    }\n\n    std::string str_service(service ? service : \"\");\n    std::vector<std::string> result = System::getaddrinfo(hostname, family, socktype, protocol, str_service, timeout);\n\n    if (result.empty()) {\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n    for (auto &i : result) {\n        add_next_index_stringl(return_value, i.c_str(), i.length());\n    }\n}\n\nPHP_METHOD(swoole_coroutine_system, statvfs) {\n    char *path;\n    size_t l_path;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STRING(path, l_path)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    struct statvfs _stat;\n    swoole_coroutine_statvfs(path, &_stat);\n\n    array_init(return_value);\n    add_assoc_long(return_value, \"bsize\", _stat.f_bsize);\n    add_assoc_long(return_value, \"frsize\", _stat.f_frsize);\n    add_assoc_long(return_value, \"blocks\", _stat.f_blocks);\n    add_assoc_long(return_value, \"bfree\", _stat.f_bfree);\n    add_assoc_long(return_value, \"bavail\", _stat.f_bavail);\n    add_assoc_long(return_value, \"files\", _stat.f_files);\n    add_assoc_long(return_value, \"ffree\", _stat.f_ffree);\n    add_assoc_long(return_value, \"favail\", _stat.f_favail);\n    add_assoc_long(return_value, \"fsid\", _stat.f_fsid);\n    add_assoc_long(return_value, \"flag\", _stat.f_flag);\n    add_assoc_long(return_value, \"namemax\", _stat.f_namemax);\n}\n\nPHP_METHOD(swoole_coroutine_system, exec) {\n    char *command;\n    size_t command_len;\n    zend_bool get_error_stream = false;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_STRING(command, command_len)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_BOOL(get_error_stream)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    int status;\n    auto buffer = std::shared_ptr<String>(swoole::make_string(1024, sw_zend_string_allocator()));\n    if (!System::exec(command, get_error_stream, buffer, &status)) {\n        RETURN_FALSE;\n    }\n\n    auto str = zend::fetch_zend_string_by_val(buffer->str);\n    buffer->set_null_terminated();\n    str->len = buffer->length;\n    buffer->release();\n\n    zval zdata;\n    ZVAL_STR(&zdata, str);\n\n    array_init(return_value);\n    add_assoc_long(return_value, \"code\", WEXITSTATUS(status));\n    add_assoc_long(return_value, \"signal\", WTERMSIG(status));\n    add_assoc_zval(return_value, \"output\", &zdata);\n}\n\nstatic void swoole_coroutine_system_wait(INTERNAL_FUNCTION_PARAMETERS, pid_t pid, double timeout) {\n    int status;\n\n    Coroutine::get_current_safe();\n\n    if (pid < 0) {\n        pid = System::wait(&status, timeout);\n    } else {\n        pid = System::waitpid(pid, &status, 0, timeout);\n    }\n    if (pid > 0) {\n        array_init(return_value);\n        add_assoc_long(return_value, \"pid\", pid);\n        add_assoc_long(return_value, \"code\", WEXITSTATUS(status));\n        add_assoc_long(return_value, \"signal\", WTERMSIG(status));\n    } else {\n        RETURN_FALSE;\n    }\n}\n\nPHP_METHOD(swoole_coroutine_system, wait) {\n    SW_MUST_BE_MAIN_THREAD();\n    double timeout = -1;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_coroutine_system_wait(INTERNAL_FUNCTION_PARAM_PASSTHRU, -1, timeout);\n}\n\nPHP_METHOD(swoole_coroutine_system, waitPid) {\n    SW_MUST_BE_MAIN_THREAD();\n    zend_long pid;\n    double timeout = -1;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_LONG(pid)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_coroutine_system_wait(INTERNAL_FUNCTION_PARAM_PASSTHRU, pid, timeout);\n}\n\nPHP_METHOD(swoole_coroutine_system, waitSignal) {\n    SW_MUST_BE_MAIN_THREAD();\n    zval *zsignals;\n    double timeout = -1;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_ZVAL(zsignals)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    std::vector<int> signals;\n\n    if (ZVAL_IS_ARRAY(zsignals)) {\n        zval *item;\n        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zsignals), item) {\n            signals.push_back(zval_get_long(item));\n        }\n        ZEND_HASH_FOREACH_END();\n    } else {\n        signals.push_back(zval_get_long(zsignals));\n    }\n\n    int signo = System::wait_signal(signals, timeout);\n    if (signo == -1) {\n        if (swoole_get_last_error() == EBUSY) {\n            php_swoole_fatal_error(E_WARNING, \"Unable to wait signal, async signal listener has been registered\");\n        } else if (swoole_get_last_error() == EINVAL) {\n            php_swoole_fatal_error(E_WARNING, \"Invalid signal in the given list\");\n        }\n        errno = swoole_get_last_error();\n        RETURN_FALSE;\n    }\n\n    RETURN_LONG(signo);\n}\n\nPHP_METHOD(swoole_coroutine_system, waitEvent) {\n    zval *zfd;\n    zend_long events = SW_EVENT_READ;\n    double timeout = -1;\n\n    ZEND_PARSE_PARAMETERS_START(1, 3)\n    Z_PARAM_ZVAL(zfd)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(events)\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    int fd = php_swoole_convert_to_fd(zfd);\n    if (fd < 0) {\n        php_swoole_fatal_error(E_WARNING, \"unknown fd type\");\n        RETURN_FALSE;\n    }\n\n    events = System::wait_event(fd, events, timeout);\n    if (events < 0) {\n        RETURN_FALSE;\n    }\n\n    RETURN_LONG(events);\n}\n"
  },
  {
    "path": "ext-src/swoole_curl.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_curl.h\"\n#include \"swoole_socket.h\"\n\n#ifdef SW_USE_CURL\n\nnamespace swoole {\nnamespace curl {\n\nHandle *get_handle(CURL *cp) {\n    Handle *handle;\n    if (curl_easy_getinfo(cp, CURLINFO_PRIVATE, (void *) &handle) == CURLE_OK) {\n        return handle;\n    } else {\n        return nullptr;\n    }\n}\n\nHandle *create_handle(CURL *cp) {\n    auto *handle = new Handle(cp);\n    curl_easy_setopt(cp, CURLOPT_PRIVATE, handle);\n    swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_MAGENTA \" handle=%p, curl=%p\", \"[CREATE]\", handle, cp);\n    return handle;\n}\n\nvoid destroy_handle(CURL *cp) {\n    auto handle = get_handle(cp);\n    if (!handle) {\n        return;\n    }\n    delete handle->easy_multi;\n    curl_easy_setopt(cp, CURLOPT_PRIVATE, nullptr);\n    swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_RED \" handle=%p, curl=%p\", \"[DESTROY]\", handle, cp);\n    delete handle;\n}\n\nstatic int execute_callback(Event *event, int bitmask) {\n    auto curl_socket = static_cast<Socket *>(event->socket->object);\n    curl_socket->bitmask |= bitmask;\n    curl_socket->multi->callback(curl_socket, bitmask, event->fd);\n    return 0;\n}\n\nMulti::Multi() {\n\tmulti_handle_ = curl_multi_init();\n\tco = nullptr;\n\tcurl_multi_setopt(multi_handle_, CURLMOPT_SOCKETFUNCTION, handle_socket);\n\tcurl_multi_setopt(multi_handle_, CURLMOPT_TIMERFUNCTION, handle_timeout);\n\tcurl_multi_setopt(multi_handle_, CURLMOPT_SOCKETDATA, this);\n\tcurl_multi_setopt(multi_handle_, CURLMOPT_TIMERDATA, this);\n}\n\nMulti::~Multi() {\n    del_timer();\n    curl_multi_cleanup(multi_handle_);\n}\n\nint Multi::cb_readable(Reactor *reactor, Event *event) {\n    return execute_callback(event, CURL_CSELECT_IN);\n}\n\nint Multi::cb_writable(Reactor *reactor, Event *event) {\n    return execute_callback(event, CURL_CSELECT_OUT);\n}\n\nint Multi::cb_error(Reactor *reactor, Event *event) {\n    return execute_callback(event, CURL_CSELECT_ERR);\n}\n\nint Multi::handle_socket(CURL *cp, curl_socket_t sockfd, int action, void *userp, void *socketp) {\n    auto *multi = static_cast<Multi *>(userp);\n    swoole_trace_log(SW_TRACE_CO_CURL,\n                     SW_ECHO_CYAN \"curl=%p, sockfd=%d, action=%d, userp=%p, socketp=%p\",\n                     \"[HANDLE_SOCKET]\",\n                     cp,\n                     sockfd,\n                     action,\n                     userp,\n                     socketp);\n    switch (action) {\n    case CURL_POLL_IN:\n    case CURL_POLL_OUT:\n    case CURL_POLL_INOUT:\n        return multi->set_event(socketp, sockfd, action);\n    case CURL_POLL_REMOVE:\n        return multi->del_event(socketp, sockfd);\n    default:\n        abort();\n    }\n    return 0;\n}\n\nint Multi::del_event(void *socket_ptr, curl_socket_t sockfd) {\n    sockets.erase(sockfd);\n    curl_multi_assign(multi_handle_, sockfd, nullptr);\n\n    if (sw_unlikely(!socket_ptr)) {\n        return SW_ERR;\n    }\n\n    auto curl_socket = static_cast<Socket *>(socket_ptr);\n    if (curl_socket->socket->events && sw_likely(swoole_event_is_available())) {\n        curl_socket->socket->silent_remove = 1;\n        swoole_event_del(curl_socket->socket);\n    }\n\n    swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_RED \" socket_ptr=%p, fd=%d\", \"[DEL_EVENT]\", socket_ptr, sockfd);\n\n    curl_socket->socket->fd = -1;\n    curl_socket->socket->free();\n\n    if (selector.executing) {\n        curl_socket->deleted = true;\n        selector.release_sockets.insert(curl_socket);\n    } else {\n        delete curl_socket;\n    }\n\n    return SW_OK;\n}\n\nint Multi::set_event(void *socket_ptr, curl_socket_t sockfd, int action) {\n    if (sw_unlikely(!swoole_event_is_available())) {\n        return -1;\n    }\n\n    if (sw_unlikely(!swoole_event_isset_handler(PHP_SWOOLE_FD_CO_CURL, SW_EVENT_READ))) {\n        swoole_event_set_handler(PHP_SWOOLE_FD_CO_CURL, SW_EVENT_READ, cb_readable);\n        swoole_event_set_handler(PHP_SWOOLE_FD_CO_CURL, SW_EVENT_WRITE, cb_writable);\n        swoole_event_set_handler(PHP_SWOOLE_FD_CO_CURL, SW_EVENT_ERROR, cb_error);\n    }\n\n    Socket *curl_socket;\n\n    if (socket_ptr) {\n        curl_socket = (Socket *) socket_ptr;\n    } else {\n        curl_socket = new Socket();\n        if (sw_unlikely(curl_multi_assign(multi_handle_, sockfd, curl_socket) != CURLM_OK)) {\n            delete curl_socket;\n            return -1;\n        }\n\n        curl_socket->socket = new network::Socket();\n        curl_socket->socket->fd = sockfd;\n        curl_socket->socket->removed = 1;\n        curl_socket->socket->fd_type = static_cast<FdType>(PHP_SWOOLE_FD_CO_CURL);\n        curl_socket->socket->object = curl_socket;\n        curl_socket->multi = this;\n\n        sockets[sockfd] = curl_socket;\n    }\n\n    curl_socket->sockfd = sockfd;\n    curl_socket->action = action;\n\n    int events = 0;\n    if (action != CURL_POLL_IN) {\n        events |= SW_EVENT_WRITE;\n    }\n    if (action != CURL_POLL_OUT) {\n        events |= SW_EVENT_READ;\n    }\n\n    swoole_trace_log(SW_TRACE_CO_CURL,\n                     SW_ECHO_GREEN \" curl_socket=%p, fd=%d, events=%d\",\n                     \"[ADD_EVENT]\",\n                     curl_socket,\n                     sockfd,\n                     events);\n\n    if (curl_socket->socket->events) {\n        return swoole_event_set(curl_socket->socket, events);\n    } else {\n        return swoole_event_add(curl_socket->socket, events);\n    }\n}\n\nCURLMcode Multi::add_handle(Handle *handle) {\n    auto retval = curl_multi_add_handle(multi_handle_, handle->cp);\n    if (retval == CURLM_OK) {\n        handle->multi = this;\n        swoole_trace_log(SW_TRACE_CO_CURL,\n                         SW_ECHO_GREEN \" handle=%p, curl=%p, multi=%p, running_handles=%d\",\n                         \"[ADD_HANDLE]\",\n                         handle,\n                         handle->cp,\n                         this,\n                         running_handles_);\n    }\n    return retval;\n}\n\nCURLMcode Multi::remove_handle(Handle *handle) const {\n    swoole_trace_log(SW_TRACE_CO_CURL,\n                     SW_ECHO_RED \" handle=%p, curl=%p, multi=%p, running_handles=%d\",\n                     \"[REMOVE_HANDLE]\",\n                     handle,\n                     handle->cp,\n                     handle->multi,\n                     handle->multi->running_handles_);\n\n    const auto rc = curl_multi_remove_handle(multi_handle_, handle->cp);\n    handle->multi = nullptr;\n    return rc;\n}\n\nvoid Multi::selector_prepare() {\n    for (auto it : sockets) {\n        Socket *curl_socket = it.second;\n        if (curl_socket->socket->removed) {\n            swoole_event_add(curl_socket->socket, get_event(curl_socket->action));\n            swoole_trace_log(SW_TRACE_CO_CURL,\n                             \"resume, curl_socket=%p, fd=%d, action=%d\",\n                             curl_socket,\n                             curl_socket->socket->get_fd(),\n                             curl_socket->action);\n        }\n    }\n}\n\nCURLcode Multi::exec(Handle *handle) {\n    if (add_handle(handle) != CURLM_OK) {\n        return CURLE_FAILED_INIT;\n    }\n\n    bool is_canceled = false;\n\n    SW_LOOP {\n        selector_prepare();\n\n        if (wait_event()) {\n            co = check_bound_co();\n            co->yield_ex(-1);\n            is_canceled = co->is_canceled();\n            co = nullptr;\n\n            if (is_canceled) {\n                swoole_set_last_error(SW_ERROR_CO_CANCELED);\n                break;\n            }\n        }\n\n        selector_finish();\n        if (running_handles_ == 0) {\n            break;\n        }\n        set_timer();\n    }\n\n    del_timer();\n\n    CURLcode retval = read_info();\n    remove_handle(handle);\n    return is_canceled ? CURLE_ABORTED_BY_CALLBACK : retval;\n}\n\nCURLcode Multi::read_info() const {\n    CURLMsg *message;\n    int pending;\n\n    while ((message = curl_multi_info_read(multi_handle_, &pending))) {\n        switch (message->msg) {\n        case CURLMSG_DONE:\n            /* Do not use message data after calling curl_multi_remove_handle() and\n             curl_easy_cleanup(). As per curl_multi_info_read() docs:\n             \"WARNING: The data the returned pointer points to will not survive\n             calling curl_multi_cleanup, curl_multi_remove_handle or\n             curl_easy_cleanup.\" */\n            return message->data.result;\n        default:\n            swoole_warning(\"CURLMSG default\");\n            break;\n        }\n    }\n    return CURLE_OK;\n}\n\nint Multi::handle_timeout(CURLM *mh, long timeout_ms, void *userp) {\n    auto *multi = static_cast<Multi *>(userp);\n    swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_BLUE \" timeout_ms=%ld\", \"[HANDLE_TIMEOUT]\", timeout_ms);\n    if (sw_unlikely(!swoole_event_is_available())) {\n        return -1;\n    }\n    if (timeout_ms < 0) {\n        if (multi->timer) {\n            multi->del_timer();\n        } else {\n            multi->add_timer(1000);\n        }\n    } else {\n        if (timeout_ms == 0) {\n            timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it in a bit */\n        }\n        multi->add_timer(timeout_ms);\n    }\n    return 0;\n}\n\nvoid Multi::selector_finish() {\n    del_timer();\n\n    selector.executing = true;\n\n    if (selector.timer_callback) {\n        selector.timer_callback = false;\n        curl_multi_socket_action(multi_handle_, CURL_SOCKET_TIMEOUT, 0, &running_handles_);\n        swoole_trace_log(SW_TRACE_CO_CURL, \"socket_action[timer], running_handles=%d\", running_handles_);\n    }\n\n    while (!selector.active_sockets.empty()) {\n        auto active_sockets = selector.active_sockets;\n        selector.active_sockets.clear();\n\n        for (auto curl_socket : active_sockets) {\n            /**\n             * In `curl_multi_socket_action`, `Handle::destroy_socket()` may be invoked,\n             * which will remove entries from the `unordered_map`.\n             * In C++, removing elements during iteration can render the iterator invalid; hence,\n             * it's necessary to copy `handle->sockets` into a new `unordered_map`.\n             */\n            swoole_trace_log(SW_TRACE_CO_CURL,\n                             \"curl_multi_socket_action(): sockfd=%d, bitmask=%d, running_handles_=%d\",\n                             curl_socket->sockfd,\n                             curl_socket->bitmask,\n                             running_handles_);\n\n            if (!curl_socket->deleted) {\n                int bitmask = curl_socket->bitmask;\n                curl_socket->bitmask = 0;\n                curl_multi_socket_action(multi_handle_, curl_socket->sockfd, bitmask, &running_handles_);\n            }\n        }\n    }\n\n    selector.executing = false;\n    for (auto curl_socket : selector.release_sockets) {\n        delete curl_socket;\n    }\n    selector.release_sockets.clear();\n}\n\nlong Multi::select(php_curlm *mh, double timeout) {\n    if (zend_llist_count(&mh->easyh) == 0) {\n        return 0;\n    }\n\n    if (curl_multi_socket_all(multi_handle_, &running_handles_) != CURLM_OK) {\n        return CURLE_FAILED_INIT;\n    }\n\n    selector_prepare();\n    set_timer();\n\n    // no events and timers, should not be suspended\n    if (!wait_event()) {\n        return 0;\n    }\n\n    co = check_bound_co();\n    co->yield_ex(timeout);\n    co = nullptr;\n\n    swoole_trace_log(SW_TRACE_CO_CURL, \"yield timeout, count=%lu\", zend_llist_count(&mh->easyh));\n\n    const auto count = selector.active_sockets.size();\n    selector_finish();\n\n    return static_cast<long>(count);\n}\n\nvoid Multi::callback(Socket *curl_socket, int bitmask, int sockfd) {\n    swoole_trace_log(\n        SW_TRACE_CO_CURL, \"curl_socket=%p, bitmask=%d, co=%p, sockfd=%d\", curl_socket, bitmask, co, sockfd);\n    if (!curl_socket) {\n        selector.timer_callback = true;\n    }\n    if (!co) {\n        if (curl_socket) {\n            swoole_event_del(curl_socket->socket);\n        } else {\n            del_timer();\n        }\n        return;\n    }\n    if (curl_socket) {\n        selector.active_sockets.insert(curl_socket);\n    }\n    if (defer_callback) {\n        return;\n    }\n    defer_callback = true;\n    swoole_event_defer(\n        [this](void *data) {\n            defer_callback = false;\n            if (co) {\n                co->resume();\n            }\n        },\n        nullptr);\n}\n}  // namespace curl\n}  // namespace swoole\n\nCURLcode swoole_curl_easy_perform(CURL *cp) {\n    auto handle = swoole::curl::get_handle(cp);\n    if (!handle->easy_multi) {\n        handle->easy_multi = new Multi();\n    }\n    return handle->easy_multi->exec(handle);\n}\n\nvoid swoole_curl_easy_reset(CURL *cp) {\n    auto handle = swoole::curl::get_handle(cp);\n    curl_easy_reset(cp);\n    curl_easy_setopt(cp, CURLOPT_PRIVATE, handle);\n}\n\nphp_curl *swoole_curl_get_handle(zval *zid, bool exclusive, bool required) {\n    php_curl *ch = Z_CURL_P(zid);\n    if (SWOOLE_G(req_status) == PHP_SWOOLE_RSHUTDOWN_END) {\n        exclusive = false;\n    }\n    if (exclusive && swoole_coroutine_is_in()) {\n        auto handle = swoole::curl::get_handle(ch->cp);\n        if (required && !handle) {\n            php_swoole_fatal_error(E_WARNING, \"The given handle is not initialized in coroutine\");\n            return nullptr;\n        }\n        if (handle && handle->multi && handle->multi->check_bound_co() == nullptr) {\n            return nullptr;\n        }\n    }\n    return ch;\n}\n\n#endif\n"
  },
  {
    "path": "ext-src/swoole_curl_interface.h",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2015 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n */\n\n#pragma once\n\n#include \"php_swoole_cxx.h\"\n\n#ifdef SW_USE_CURL\nSW_EXTERN_C_BEGIN\n\n#include <curl/curl.h>\n#include <curl/multi.h>\n\n#define fread swoole_coroutine_fread\n#define fwrite swoole_coroutine_fwrite\n#define curl_easy_reset swoole_curl_easy_reset\n\nvoid swoole_native_curl_minit(int module_number);\nvoid swoole_native_curl_mshutdown();\n\nPHP_FUNCTION(swoole_native_curl_close);\nPHP_FUNCTION(swoole_native_curl_copy_handle);\nPHP_FUNCTION(swoole_native_curl_errno);\nPHP_FUNCTION(swoole_native_curl_error);\nPHP_FUNCTION(swoole_native_curl_exec);\nPHP_FUNCTION(swoole_native_curl_getinfo);\nPHP_FUNCTION(swoole_native_curl_init);\nPHP_FUNCTION(swoole_native_curl_setopt);\nPHP_FUNCTION(swoole_native_curl_setopt_array);\nPHP_FUNCTION(swoole_native_curl_reset);\nPHP_FUNCTION(swoole_native_curl_escape);\nPHP_FUNCTION(swoole_native_curl_unescape);\nPHP_FUNCTION(swoole_native_curl_pause);\nPHP_FUNCTION(swoole_native_curl_multi_add_handle);\nPHP_FUNCTION(swoole_native_curl_multi_close);\nPHP_FUNCTION(swoole_native_curl_multi_errno);\nPHP_FUNCTION(swoole_native_curl_multi_exec);\nPHP_FUNCTION(swoole_native_curl_multi_select);\nPHP_FUNCTION(swoole_native_curl_multi_remove_handle);\nPHP_FUNCTION(swoole_native_curl_multi_setopt);\nPHP_FUNCTION(swoole_native_curl_multi_getcontent);\nPHP_FUNCTION(swoole_native_curl_multi_info_read);\nPHP_FUNCTION(swoole_native_curl_multi_init);\n#if LIBCURL_VERSION_NUM >= 0x073E00\nPHP_FUNCTION(swoole_native_curl_upkeep);\n#endif\n\nSW_EXTERN_C_END\n#endif\n"
  },
  {
    "path": "ext-src/swoole_event.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2015 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_cxx.h\"\n#include \"swoole_server.h\"\n#include \"swoole_signal.h\"\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_event_arginfo.h\"\nEND_EXTERN_C()\n\nusing namespace swoole;\nusing swoole::network::Socket;\n\nstatic std::unordered_map<int, Socket *> event_socket_map;\n\nzend_class_entry *swoole_event_ce;\nstatic zend_object_handlers swoole_event_handlers;\n\nstruct EventObject {\n    zval zsocket;\n    zend::Callable *readable_callback;\n    zend::Callable *writable_callback;\n};\n\nstatic int event_readable_callback(Reactor *reactor, Event *event);\nstatic int event_writable_callback(Reactor *reactor, Event *event);\nstatic int event_error_callback(Reactor *reactor, Event *event);\nstatic void event_defer_callback(void *data);\nstatic void event_cycle_callback(void *data);\n\nSW_EXTERN_C_BEGIN\nstatic PHP_FUNCTION(swoole_event_add);\nstatic PHP_FUNCTION(swoole_event_set);\nstatic PHP_FUNCTION(swoole_event_del);\nstatic PHP_FUNCTION(swoole_event_write);\nstatic PHP_FUNCTION(swoole_event_wait);\nstatic PHP_FUNCTION(swoole_event_rshutdown);\nstatic PHP_FUNCTION(swoole_event_exit);\nstatic PHP_FUNCTION(swoole_event_defer);\nstatic PHP_FUNCTION(swoole_event_cycle);\nstatic PHP_FUNCTION(swoole_event_dispatch);\nstatic PHP_FUNCTION(swoole_event_isset);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic const zend_function_entry swoole_event_methods[] =\n{\n    ZEND_FENTRY(add,       ZEND_FN(swoole_event_add),       arginfo_swoole_event_add,       ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(del,       ZEND_FN(swoole_event_del),       arginfo_swoole_event_del,       ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(set,       ZEND_FN(swoole_event_set),       arginfo_swoole_event_set,       ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(isset,     ZEND_FN(swoole_event_isset),     arginfo_swoole_event_isset,     ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(dispatch,  ZEND_FN(swoole_event_dispatch),  arginfo_swoole_event_dispatch,  ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(defer,     ZEND_FN(swoole_event_defer),     arginfo_swoole_event_defer,     ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(cycle,     ZEND_FN(swoole_event_cycle),     arginfo_swoole_event_cycle,     ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(write,     ZEND_FN(swoole_event_write),     arginfo_swoole_event_write,     ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(wait,      ZEND_FN(swoole_event_wait),      arginfo_swoole_event_wait,      ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(rshutdown, ZEND_FN(swoole_event_rshutdown), arginfo_swoole_event_rshutdown, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(exit,      ZEND_FN(swoole_event_exit),      arginfo_swoole_event_exit,      ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_event_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_event, \"Swoole\\\\Event\", nullptr, swoole_event_methods);\n    SW_SET_CLASS_CREATE(swoole_event, sw_zend_create_object_deny);\n\n    SW_FUNCTION_ALIAS(\n        &swoole_event_ce->function_table, \"add\", CG(function_table), \"swoole_event_add\", arginfo_swoole_event_add);\n    SW_FUNCTION_ALIAS(\n        &swoole_event_ce->function_table, \"del\", CG(function_table), \"swoole_event_del\", arginfo_swoole_event_del);\n    SW_FUNCTION_ALIAS(\n        &swoole_event_ce->function_table, \"set\", CG(function_table), \"swoole_event_set\", arginfo_swoole_event_set);\n    SW_FUNCTION_ALIAS(\n        &swoole_event_ce->function_table, \"wait\", CG(function_table), \"swoole_event_wait\", arginfo_swoole_event_wait);\n\n    SW_FUNCTION_ALIAS(&swoole_event_ce->function_table,\n                      \"isset\",\n                      CG(function_table),\n                      \"swoole_event_isset\",\n                      arginfo_swoole_event_isset);\n    SW_FUNCTION_ALIAS(&swoole_event_ce->function_table,\n                      \"dispatch\",\n                      CG(function_table),\n                      \"swoole_event_dispatch\",\n                      arginfo_swoole_event_dispatch);\n    SW_FUNCTION_ALIAS(&swoole_event_ce->function_table,\n                      \"defer\",\n                      CG(function_table),\n                      \"swoole_event_defer\",\n                      arginfo_swoole_event_defer);\n    SW_FUNCTION_ALIAS(&swoole_event_ce->function_table,\n                      \"cycle\",\n                      CG(function_table),\n                      \"swoole_event_cycle\",\n                      arginfo_swoole_event_cycle);\n    SW_FUNCTION_ALIAS(&swoole_event_ce->function_table,\n                      \"write\",\n                      CG(function_table),\n                      \"swoole_event_write\",\n                      arginfo_swoole_event_write);\n    SW_FUNCTION_ALIAS(&swoole_event_ce->function_table,\n                      \"exit\",\n                      CG(function_table),\n                      \"swoole_event_exit\",\n                      arginfo_swoole_event_exit);\n    SW_FUNCTION_ALIAS(&swoole_event_ce->function_table,\n                      \"rshutdown\",\n                      CG(function_table),\n                      \"swoole_event_rshutdown\",\n                      arginfo_swoole_event_rshutdown);\n}\n\nstatic void event_object_free(void *data) {\n    auto *peo = static_cast<EventObject *>(data);\n    delete peo->readable_callback;\n    delete peo->writable_callback;\n    zval_ptr_dtor((&peo->zsocket));\n    efree(peo);\n}\n\nstatic int event_readable_callback(Reactor *reactor, Event *event) {\n    auto *peo = static_cast<EventObject *>(event->socket->object);\n\n    zval argv[1];\n    argv[0] = peo->zsocket;\n    auto fcc = peo->readable_callback->ptr();\n\n    if (UNEXPECTED(!zend::function::call(fcc, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) {\n        php_swoole_fatal_error(E_WARNING,\n                               \"%s: readable callback handler error, fd [%d] will be removed from reactor\",\n                               ZSTR_VAL(swoole_event_ce->name),\n                               php_swoole_convert_to_fd(&peo->zsocket));\n        event->socket->object = nullptr;\n        swoole_event_defer(event_object_free, peo);\n        swoole_event_del(event->socket);\n        return SW_ERR;\n    }\n\n    return SW_OK;\n}\n\nstatic int event_writable_callback(Reactor *reactor, Event *event) {\n    auto *peo = static_cast<EventObject *>(event->socket->object);\n\n    zval argv[1];\n    argv[0] = peo->zsocket;\n    auto fcc = peo->writable_callback->ptr();\n\n    if (UNEXPECTED(!zend::function::call(fcc, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) {\n        php_swoole_fatal_error(E_WARNING,\n                               \"%s: writable callback handler error, fd [%d] will be removed from reactor\",\n                               ZSTR_VAL(swoole_event_ce->name),\n                               php_swoole_convert_to_fd(&peo->zsocket));\n        event->socket->object = nullptr;\n        swoole_event_defer(event_object_free, peo);\n        swoole_event_del(event->socket);\n        return SW_ERR;\n    }\n\n    return SW_OK;\n}\n\nstatic int event_error_callback(Reactor *reactor, Event *event) {\n    if (!(event->socket->events & SW_EVENT_ERROR)) {\n        if (event->socket->events & SW_EVENT_READ) {\n            return reactor->get_handler(event->socket->fd_type, SW_EVENT_READ)(reactor, event);\n        } else {\n            return reactor->get_handler(event->socket->fd_type, SW_EVENT_WRITE)(reactor, event);\n        }\n    }\n\n    int error;\n    if (event->socket->get_option(SOL_SOCKET, SO_ERROR, &error) < 0) {\n        php_swoole_sys_error(E_WARNING, \"swoole_event->onError[1]: getsockopt[sock=%d] failed\", event->fd);\n    }\n\n    if (error != 0) {\n        php_swoole_fatal_error(\n            E_WARNING, \"swoole_event->onError[1]: socket error. Error: %s [%d]\", strerror(error), error);\n    }\n\n    event_object_free(event->socket->object);\n    swoole_event_del(event->socket);\n\n    return SW_OK;\n}\n\nstatic void event_defer_callback(void *data) {\n    auto *cb = static_cast<zend::Callable *>(data);\n    if (UNEXPECTED(!zend::function::call(cb, 0, nullptr, nullptr, php_swoole_is_enable_coroutine()))) {\n        php_swoole_error(E_WARNING, \"%s::defer callback handler error\", ZSTR_VAL(swoole_event_ce->name));\n    }\n    delete cb;\n}\n\nstatic void event_cycle_callback(void *data) {\n    auto *cb = static_cast<zend::Callable *>(data);\n    if (UNEXPECTED(!zend::function::call(cb, 0, nullptr, nullptr, php_swoole_is_enable_coroutine()))) {\n        php_swoole_error(E_WARNING, \"%s::end callback handler error\", ZSTR_VAL(swoole_event_ce->name));\n    }\n}\n\nint php_swoole_reactor_init() {\n    if (!SWOOLE_G(cli)) {\n        php_swoole_fatal_error(E_ERROR, \"async-io must be used in PHP CLI mode\");\n        return SW_ERR;\n    }\n\n    if (sw_server()) {\n        if (sw_server()->is_task_worker() && !sw_server()->task_enable_coroutine) {\n            php_swoole_fatal_error(\n                E_ERROR, \"Unable to use async-io in task processes, please set `task_enable_coroutine` to true\");\n            return SW_ERR;\n        }\n        if (sw_server()->is_manager()) {\n            php_swoole_fatal_error(E_ERROR, \"Unable to use async-io in manager process\");\n            return SW_ERR;\n        }\n    }\n    if (!sw_reactor()) {\n        swoole_trace_log(SW_TRACE_PHP, \"init reactor\");\n\n        if (swoole_event_init(SW_EVENTLOOP_WAIT_EXIT) < 0) {\n            php_swoole_fatal_error(E_ERROR, \"Unable to create event-loop reactor\");\n            return SW_ERR;\n        }\n\n        php_swoole_register_shutdown_function(\"swoole_event_rshutdown\");\n    }\n\n    if (sw_reactor() && SwooleG.user_exit_condition &&\n        !sw_reactor()->isset_exit_condition(Reactor::EXIT_CONDITION_USER_AFTER_DEFAULT)) {\n        sw_reactor()->set_exit_condition(Reactor::EXIT_CONDITION_USER_AFTER_DEFAULT, SwooleG.user_exit_condition);\n    }\n\n    return SW_OK;\n}\n\nvoid php_swoole_event_wait() {\n    if (php_swoole_is_fatal_error() || !sw_reactor()) {\n        return;\n    }\n    if (swoole_coroutine_is_in()) {\n        php_swoole_fatal_error(E_ERROR, \"Unable to call Event::wait() in coroutine\");\n        return;\n    }\n    if (!sw_reactor()->if_exit() && !sw_reactor()->bailout) {\n        // Don't disable object slot reuse while running shutdown functions:\n        // https://github.com/php/php-src/commit/bd6eabd6591ae5a7c9ad75dfbe7cc575fa907eac\n#if defined(EG_FLAGS_IN_SHUTDOWN) && !defined(EG_FLAGS_OBJECT_STORE_NO_REUSE)\n        zend_bool in_shutdown = EG(flags) & EG_FLAGS_IN_SHUTDOWN;\n        EG(flags) &= ~EG_FLAGS_IN_SHUTDOWN;\n#endif\n        if (sw_reactor()->wait() < 0) {\n            php_swoole_sys_error(E_ERROR, \"reactor wait failed\");\n        }\n#if defined(EG_FLAGS_IN_SHUTDOWN) && !defined(EG_FLAGS_OBJECT_STORE_NO_REUSE)\n        if (in_shutdown) {\n            EG(flags) |= EG_FLAGS_IN_SHUTDOWN;\n        }\n#endif\n    }\n    swoole_event_free();\n}\n\nvoid php_swoole_event_exit() {\n    if (sw_reactor()) {\n        php_swoole_timer_clear_all();\n        sw_reactor()->running = false;\n    }\n}\n\nint php_swoole_convert_to_fd(zval *zsocket) {\n    int fd = -1;\n\n    switch (Z_TYPE_P(zsocket)) {\n    case IS_RESOURCE: {\n        php_stream *stream;\n        if ((php_stream_from_zval_no_verify(stream, zsocket))) {\n            if (php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void **) &fd, 1) ==\n                    SUCCESS &&\n                fd >= 0) {\n                return fd;\n            }\n        }\n        php_swoole_fatal_error(E_WARNING, \"fd argument must be either valid PHP stream or valid PHP socket resource\");\n        return SW_ERR;\n    }\n    case IS_LONG: {\n        fd = Z_LVAL_P(zsocket);\n        if (fd < 0) {\n            php_swoole_fatal_error(E_WARNING, \"invalid file descriptor#%d passed\", fd);\n            return SW_ERR;\n        }\n        return fd;\n    }\n    case IS_OBJECT: {\n        zval *zfd = nullptr;\n        if (sw_zval_is_co_socket(zsocket)) {\n            zfd = sw_zend_read_property_ex(Z_OBJCE_P(zsocket), zsocket, SW_ZSTR_KNOWN(SW_ZEND_STR_FD), 0);\n        } else if (sw_zval_is_client(zsocket)) {\n            zfd = sw_zend_read_property_ex(Z_OBJCE_P(zsocket), zsocket, SW_ZSTR_KNOWN(SW_ZEND_STR_SOCK), 0);\n        } else if (sw_zval_is_process(zsocket)) {\n            zfd = sw_zend_read_property_ex(Z_OBJCE_P(zsocket), zsocket, SW_ZSTR_KNOWN(SW_ZEND_STR_PIPE), 0);\n#ifdef SWOOLE_SOCKETS_SUPPORT\n        } else if (sw_zval_is_php_socket(zsocket)) {\n            php_socket *php_sock = SW_Z_SOCKET_P(zsocket);\n            if (IS_INVALID_SOCKET(php_sock)) {\n                php_swoole_fatal_error(E_WARNING, \"contains a closed socket\");\n                return SW_ERR;\n            }\n            return php_sock->bsd_socket;\n#endif\n        }\n        if (zfd == nullptr || Z_TYPE_P(zfd) != IS_LONG) {\n            return SW_ERR;\n        }\n        return Z_LVAL_P(zfd);\n    }\n    default:\n        php_swoole_fatal_error(E_WARNING, \"invalid file descriptor passed\");\n        return SW_ERR;\n    }\n}\n\nint php_swoole_convert_to_fd_ex(zval *zsocket, int *async) {\n    int fd;\n\n    *async = 0;\n    if (Z_TYPE_P(zsocket) == IS_RESOURCE) {\n        php_stream *stream;\n        if ((php_stream_from_zval_no_verify(stream, zsocket))) {\n            if (php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void **) &fd, 1) ==\n                    SUCCESS &&\n                fd >= 0) {\n                *async = (stream->wrapper && (stream->wrapper->wops == php_plain_files_wrapper.wops)) ? 0 : 1;\n                return fd;\n            }\n        }\n#ifdef SWOOLE_SOCKETS_SUPPORT\n        else {\n            php_socket *php_sock;\n            if ((php_sock = SW_Z_SOCKET_P(zsocket))) {\n                fd = php_sock->bsd_socket;\n                *async = 1;\n                return fd;\n            }\n        }\n#endif\n    }\n    php_swoole_fatal_error(E_WARNING, \"fd argument must be either valid PHP stream or valid PHP socket resource\");\n    return SW_ERR;\n}\n\n#ifdef SWOOLE_SOCKETS_SUPPORT\nphp_socket *php_swoole_convert_to_socket(int sock) {\n    php_socket *socket_object;\n    zval zsocket;\n    object_init_ex(&zsocket, socket_ce);\n    socket_object = Z_SOCKET_P(&zsocket);\n    auto new_sock = dup(sock);\n    if (new_sock < 0) {\n        return nullptr;\n    }\n    socket_import_file_descriptor(new_sock, socket_object);\n    return socket_object;\n}\n#endif\n\nstatic void event_check_reactor() {\n    php_swoole_check_reactor();\n\n    if (!swoole_event_isset_handler(SW_FD_USER, SW_EVENT_READ)) {\n        swoole_event_set_handler(SW_FD_USER, SW_EVENT_READ, event_readable_callback);\n        swoole_event_set_handler(SW_FD_USER, SW_EVENT_WRITE, event_writable_callback);\n        swoole_event_set_handler(SW_FD_USER, SW_EVENT_ERROR, event_error_callback);\n    }\n}\n\nstatic Socket *event_get_socket(int socket_fd) {\n    auto i = event_socket_map.find(socket_fd);\n    if (i == event_socket_map.end()) {\n        return nullptr;\n    }\n    return i->second;\n}\n\nstatic PHP_FUNCTION(swoole_event_add) {\n    zval *zfd;\n    zend_long events = SW_EVENT_READ;\n    zval *zreadable_callback = nullptr;\n    zval *zwritable_callback = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(1, 4)\n    Z_PARAM_ZVAL(zfd)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_ZVAL(zreadable_callback)\n    Z_PARAM_ZVAL(zwritable_callback)\n    Z_PARAM_LONG(events)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    event_check_reactor();\n\n    int socket_fd = php_swoole_convert_to_fd(zfd);\n    if (socket_fd < 0) {\n        php_swoole_fatal_error(E_WARNING, \"unknown fd type\");\n        RETURN_FALSE;\n    }\n    if (socket_fd == 0 && (events & SW_EVENT_WRITE)) {\n        php_swoole_fatal_error(E_WARNING, \"invalid socket fd [%d]\", socket_fd);\n        RETURN_FALSE;\n    }\n    if (event_socket_map.find(socket_fd) != event_socket_map.end()) {\n        php_swoole_fatal_error(E_WARNING, \"already exist\");\n        RETURN_FALSE;\n    }\n    if (!(events & (SW_EVENT_WRITE | SW_EVENT_READ))) {\n        php_swoole_fatal_error(E_WARNING, \"invalid events\");\n        RETURN_FALSE;\n    }\n\n    auto readable_callback = sw_callable_create_ex(zreadable_callback, \"readable_callback\", true);\n    if ((events & SW_EVENT_READ) && readable_callback == nullptr) {\n        php_swoole_fatal_error(\n            E_WARNING, \"%s: unable to find readable callback of fd [%d]\", ZSTR_VAL(swoole_event_ce->name), socket_fd);\n        RETURN_FALSE;\n    }\n\n    auto writable_callback = sw_callable_create_ex(zwritable_callback, \"writable_callback\", true);\n    if ((events & SW_EVENT_WRITE) && writable_callback == nullptr) {\n        php_swoole_fatal_error(\n            E_WARNING, \"%s: unable to find writable callback of fd [%d]\", ZSTR_VAL(swoole_event_ce->name), socket_fd);\n        delete readable_callback;\n        RETURN_FALSE;\n    }\n\n    auto *peo = static_cast<EventObject *>(ecalloc(1, sizeof(EventObject)));\n\n    Z_TRY_ADDREF_P(zfd);\n    peo->zsocket = *zfd;\n    peo->readable_callback = readable_callback;\n    peo->writable_callback = writable_callback;\n\n    Socket *socket = make_socket(socket_fd, SW_FD_USER);\n    socket->set_nonblock();\n    socket->object = peo;\n\n    if (swoole_event_add(socket, events) < 0) {\n        php_swoole_fatal_error(E_WARNING, \"swoole_event_add failed\");\n        socket->free();\n        event_object_free(peo);\n        RETURN_FALSE;\n    }\n\n    event_socket_map[socket_fd] = socket;\n\n    RETURN_LONG(socket_fd);\n}\n\nstatic PHP_FUNCTION(swoole_event_write) {\n    zval *zfd;\n    char *data;\n    size_t len;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_ZVAL(zfd)\n    Z_PARAM_STRING(data, len)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (len == 0) {\n        php_swoole_fatal_error(E_WARNING, \"data empty\");\n        RETURN_FALSE;\n    }\n\n    int socket_fd = php_swoole_convert_to_fd(zfd);\n    if (socket_fd < 0) {\n        php_swoole_fatal_error(E_WARNING, \"unknown type\");\n        RETURN_FALSE;\n    }\n\n    Socket *socket = event_get_socket(socket_fd);\n    if (socket == nullptr) {\n        php_swoole_fatal_error(E_WARNING, \"socket[%d] is not found in the reactor\", socket_fd);\n        RETURN_FALSE;\n    }\n\n    event_check_reactor();\n    if (swoole_event_write(socket, data, len) < 0) {\n        RETURN_FALSE;\n    } else {\n        RETURN_TRUE;\n    }\n}\n\nstatic PHP_FUNCTION(swoole_event_set) {\n    if (!sw_reactor()) {\n        php_swoole_fatal_error(E_WARNING, \"reactor is not ready, cannot call swoole_event_set\");\n        RETURN_FALSE;\n    }\n\n    zval *zfd;\n    zend_long events = 0;\n    zval *zreadable_callback = nullptr;\n    zval *zwritable_callback = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(1, 4)\n    Z_PARAM_ZVAL(zfd)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_ZVAL(zreadable_callback)\n    Z_PARAM_ZVAL(zwritable_callback)\n    Z_PARAM_LONG(events)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    int socket_fd = php_swoole_convert_to_fd(zfd);\n    if (socket_fd < 0) {\n        RETURN_FALSE;\n    }\n\n    Socket *socket = event_get_socket(socket_fd);\n    if (socket == nullptr) {\n        php_swoole_fatal_error(E_WARNING, \"socket[%d] is not found in the reactor\", socket_fd);\n        RETURN_FALSE;\n    }\n\n    auto *peo = static_cast<EventObject *>(socket->object);\n    auto readable_callback = sw_callable_create_ex(zreadable_callback, \"readable_callback\");\n    auto writable_callback = sw_callable_create_ex(zwritable_callback, \"writable_callback\");\n    if (readable_callback) {\n        if (peo->readable_callback) {\n            swoole_event_defer(sw_callable_free, peo->readable_callback);\n        }\n        peo->readable_callback = readable_callback;\n    }\n    if (writable_callback) {\n        if (peo->writable_callback) {\n            swoole_event_defer(sw_callable_free, peo->writable_callback);\n        }\n        peo->writable_callback = writable_callback;\n    }\n    if ((events & SW_EVENT_READ) && peo->readable_callback == nullptr) {\n        php_swoole_fatal_error(\n            E_WARNING, \"%s: unable to find readable callback of fd [%d]\", ZSTR_VAL(swoole_event_ce->name), socket_fd);\n        RETURN_FALSE;\n    }\n    if ((events & SW_EVENT_WRITE) && peo->writable_callback == nullptr) {\n        php_swoole_fatal_error(\n            E_WARNING, \"%s: unable to find writable callback of fd [%d]\", ZSTR_VAL(swoole_event_ce->name), socket_fd);\n        RETURN_FALSE;\n    }\n    if (swoole_event_set(socket, events) < 0) {\n        php_swoole_fatal_error(E_WARNING, \"%s::set failed\", ZSTR_VAL(swoole_event_ce->name));\n        RETURN_FALSE;\n    }\n\n    RETURN_TRUE;\n}\n\nstatic PHP_FUNCTION(swoole_event_del) {\n    zval *zfd;\n\n    if (!sw_reactor()) {\n        php_swoole_fatal_error(E_WARNING, \"reactor is not ready, cannot call swoole_event_del\");\n        RETURN_FALSE;\n    }\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ZVAL(zfd)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    int socket_fd = php_swoole_convert_to_fd(zfd);\n    if (socket_fd < 0) {\n        php_swoole_fatal_error(E_WARNING, \"unknown type\");\n        RETURN_FALSE;\n    }\n\n    Socket *socket = event_get_socket(socket_fd);\n    if (!socket) {\n        RETURN_FALSE;\n    }\n    swoole_event_defer(event_object_free, socket->object);\n    int retval = swoole_event_del(socket);\n    event_socket_map.erase(socket_fd);\n    socket->fd = -1;\n    socket->free();\n    RETURN_BOOL(retval == SW_OK);\n}\n\nstatic PHP_FUNCTION(swoole_event_defer) {\n    zval *zfn;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ZVAL(zfn)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    php_swoole_check_reactor();\n    auto fn = sw_callable_create(zfn);\n    swoole_event_defer(event_defer_callback, fn);\n\n    RETURN_TRUE;\n}\n\nstatic PHP_FUNCTION(swoole_event_cycle) {\n    zval *zcallback;\n    zend_bool before = false;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_ZVAL(zcallback)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_BOOL(before)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    event_check_reactor();\n    auto reactor = sw_reactor();\n    auto defer_task = before ? &reactor->future_task : &reactor->idle_task;\n\n    if (ZVAL_IS_NULL(zcallback)) {\n        if (defer_task->callback == nullptr) {\n            RETURN_FALSE;\n        } else {\n            swoole_event_defer(sw_callable_free, defer_task->data);\n            defer_task->callback = nullptr;\n            defer_task->data = nullptr;\n        }\n    } else {\n        if (defer_task->data != nullptr) {\n            swoole_event_defer(sw_callable_free, defer_task->data);\n        }\n        auto callback = sw_callable_create(zcallback);\n        defer_task->callback = event_cycle_callback;\n        defer_task->data = callback;\n    }\n\n    RETURN_TRUE;\n}\n\nstatic PHP_FUNCTION(swoole_event_exit) {\n    php_swoole_event_exit();\n}\n\nstatic PHP_FUNCTION(swoole_event_wait) {\n    if (!sw_reactor()) {\n        return;\n    }\n    php_swoole_event_wait();\n}\n\nstatic PHP_FUNCTION(swoole_event_rshutdown) {\n    /* prevent the program from jumping out of the rshutdown */\n    zend_try {\n        // when throw Exception, do not show the info\n        if (!php_swoole_is_fatal_error() && sw_reactor()) {\n            if (!sw_reactor()->bailout) {\n                php_swoole_fatal_error(E_DEPRECATED, \"Event::wait() in shutdown function is deprecated\");\n            }\n            php_swoole_event_wait();\n        }\n    }\n    zend_end_try();\n}\n\nstatic PHP_FUNCTION(swoole_event_dispatch) {\n    if (!sw_reactor()) {\n        RETURN_FALSE;\n    }\n    sw_reactor()->once = true;\n    if (sw_reactor()->wait() < 0) {\n        php_swoole_sys_error(E_ERROR, \"reactor wait failed\");\n    }\n    sw_reactor()->once = false;\n    RETURN_TRUE;\n}\n\nstatic PHP_FUNCTION(swoole_event_isset) {\n    if (!sw_reactor()) {\n        RETURN_FALSE;\n    }\n\n    zval *zfd;\n    zend_long events = SW_EVENT_READ | SW_EVENT_WRITE;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"z|l\", &zfd, &events) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    int socket_fd = php_swoole_convert_to_fd(zfd);\n    if (socket_fd < 0) {\n        php_swoole_fatal_error(E_WARNING, \"unknown type\");\n        RETURN_FALSE;\n    }\n\n    Socket *_socket = event_get_socket(socket_fd);\n    if (_socket == nullptr || _socket->removed) {\n        RETURN_FALSE;\n    }\n    if (_socket->events & events) {\n        RETURN_TRUE;\n    } else {\n        RETURN_FALSE;\n    }\n}\n"
  },
  {
    "path": "ext-src/swoole_firebird.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2018 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: NathanFreeman  <mariasocute@163.com>                         |\n +----------------------------------------------------------------------+\n*/\n#include \"php_swoole_private.h\"\n#include \"php_swoole_cxx.h\"\n#include \"swoole_coroutine.h\"\n\n#ifdef SW_USE_FIREBIRD\n#include \"php_swoole_firebird.h\"\n\nusing swoole::Coroutine;\n\nstatic SW_THREAD_LOCAL bool swoole_firebird_blocking = true;\n\nvoid swoole_firebird_set_blocking(bool blocking) {\n    swoole_firebird_blocking = blocking;\n}\n\nISC_STATUS swoole_isc_attach_database(\n    ISC_STATUS *_0, short _1, const ISC_SCHAR *_2, isc_db_handle *_3, short _4, const ISC_SCHAR *_5) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_attach_database\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_attach_database(_0, _1, _2, _3, _4, _5); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_detach_database(ISC_STATUS *_0, isc_db_handle *_1) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_detach_database\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_detach_database(_0, _1); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_dsql_execute(\n    ISC_STATUS *_0, isc_tr_handle *_1, isc_stmt_handle *_2, unsigned short _3, const XSQLDA *_4) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_dsql_execute\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_dsql_execute(_0, _1, _2, _3, _4); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_dsql_execute2(\n    ISC_STATUS *_0, isc_tr_handle *_1, isc_stmt_handle *_2, unsigned short _3, const XSQLDA *_4, const XSQLDA *_5) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_dsql_execute2\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_dsql_execute2(_0, _1, _2, _3, _4, _5); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_dsql_sql_info(\n    ISC_STATUS *_0, isc_stmt_handle *_1, short _2, const ISC_SCHAR *_3, short _4, ISC_SCHAR *_5) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_dsql_sql_info\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_dsql_sql_info(_0, _1, _2, _3, _4, _5); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_dsql_free_statement(ISC_STATUS *_0, isc_stmt_handle *_1, unsigned short _2) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_dsql_free_statement\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_dsql_free_statement(_0, _1, _2); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_start_transaction(\n    ISC_STATUS *_0, isc_tr_handle *_1, short _2, isc_db_handle *_3, size_t _4, char *_5) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_start_transaction\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_start_transaction(_0, _1, _2, _3, _4, _5); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_commit_retaining(ISC_STATUS *_0, isc_tr_handle *_1) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_commit_retaining\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_commit_retaining(_0, _1); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_commit_transaction(ISC_STATUS *_0, isc_tr_handle *_1) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_commit_transaction\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_commit_transaction(_0, _1); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_rollback_transaction(ISC_STATUS *_0, isc_tr_handle *_1) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_rollback_transaction\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_rollback_transaction(_0, _1); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_dsql_allocate_statement(ISC_STATUS *_0, isc_db_handle *_1, isc_stmt_handle *_2) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_dsql_allocate_statement\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_dsql_allocate_statement(_0, _1, _2); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_dsql_prepare(ISC_STATUS *_0,\n                                   isc_tr_handle *_1,\n                                   isc_stmt_handle *_2,\n                                   unsigned short _3,\n                                   const ISC_SCHAR *_4,\n                                   unsigned short _5,\n                                   XSQLDA *_6) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_dsql_prepare\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_dsql_prepare(_0, _1, _2, _3, _4, _5, _6); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_dsql_fetch(ISC_STATUS *_0, isc_stmt_handle *_1, unsigned short _2, const XSQLDA *_3) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_dsql_fetch\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_dsql_fetch(_0, _1, _2, _3); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_open_blob(\n    ISC_STATUS *_0, isc_db_handle *_1, isc_tr_handle *_2, isc_blob_handle *_3, ISC_QUAD *_4) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_open_blob\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_open_blob(_0, _1, _2, _3, _4); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_blob_info(\n    ISC_STATUS *_0, isc_blob_handle *_1, short _2, const ISC_SCHAR *_3, short _4, ISC_SCHAR *_5) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_blob_info\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_blob_info(_0, _1, _2, _3, _4, _5); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_get_segment(\n    ISC_STATUS *_0, isc_blob_handle *_1, unsigned short *_2, unsigned short _3, ISC_SCHAR *_4) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_get_segment\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_get_segment(_0, _1, _2, _3, _4); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_put_segment(ISC_STATUS *_0, isc_blob_handle *_1, unsigned short _2, const ISC_SCHAR *_3) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_put_segment\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_put_segment(_0, _1, _2, _3); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_create_blob(\n    ISC_STATUS *_0, isc_db_handle *_1, isc_tr_handle *_2, isc_blob_handle *_3, ISC_QUAD *_4) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_create_blob\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_create_blob(_0, _1, _2, _3, _4); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_close_blob(ISC_STATUS *_0, isc_blob_handle *_1) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_close_blob\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_close_blob(_0, _1); });\n\n    return result;\n}\n\nISC_STATUS swoole_isc_dsql_set_cursor_name(ISC_STATUS *_0,\n                                           isc_stmt_handle *_1,\n                                           const ISC_SCHAR *_2,\n                                           unsigned short _3) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_dsql_set_cursor_name\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_dsql_set_cursor_name(_0, _1, _2, _3); });\n\n    return result;\n}\n\nISC_STATUS swoole_fb_ping(ISC_STATUS *_0, isc_db_handle *_1) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"fb_ping\");\n\n    ISC_STATUS result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = fb_ping(_0, _1); });\n\n    return result;\n}\n\nint swoole_isc_version(isc_db_handle *_0, ISC_VERSION_CALLBACK _1, void *_2) {\n    swoole_trace_log(SW_TRACE_CO_FIREBIRD, \"isc_version\");\n\n    int result;\n    php_swoole_async(swoole_firebird_blocking, [&]() { result = isc_version(_0, _1, _2); });\n\n    return result;\n}\n\nvoid php_swoole_firebird_minit(int module_id) {\n    if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL(\"FB_ATTR_DATE_FORMAT\")) == nullptr) {\n#if PHP_VERSION_ID >= 80500\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"FB_ATTR_DATE_FORMAT\", (zend_long) PDO_FB_ATTR_DATE_FORMAT);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"FB_ATTR_TIME_FORMAT\", (zend_long) PDO_FB_ATTR_TIME_FORMAT);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"FB_ATTR_TIMESTAMP_FORMAT\", (zend_long) PDO_FB_ATTR_TIMESTAMP_FORMAT);\n#else\n        REGISTER_PDO_CLASS_CONST_LONG(\"FB_ATTR_DATE_FORMAT\", (zend_long) PDO_FB_ATTR_DATE_FORMAT);\n        REGISTER_PDO_CLASS_CONST_LONG(\"FB_ATTR_TIME_FORMAT\", (zend_long) PDO_FB_ATTR_TIME_FORMAT);\n        REGISTER_PDO_CLASS_CONST_LONG(\"FB_ATTR_TIMESTAMP_FORMAT\", (zend_long) PDO_FB_ATTR_TIMESTAMP_FORMAT);\n#endif\n    }\n\n    php_pdo_unregister_driver(&swoole_pdo_firebird_driver);\n    php_pdo_register_driver(&swoole_pdo_firebird_driver);\n}\n\nvoid php_swoole_firebird_mshutdown() {\n    php_pdo_unregister_driver(&swoole_pdo_firebird_driver);\n}\n#endif\n"
  },
  {
    "path": "ext-src/swoole_http2_client_coro.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_cxx.h\"\n#include \"php_swoole_http.h\"\n\n#include \"swoole_string.h\"\n#include \"swoole_protocol.h\"\n#include \"swoole_socket.h\"\n#include \"swoole_util.h\"\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_http2_client_coro_arginfo.h\"\nEND_EXTERN_C()\n\n#include \"swoole_http2.h\"\n\n#define HTTP2_CLIENT_HOST_HEADER_INDEX 3\n\nusing namespace swoole;\nusing swoole::http2::get_default_setting;\n\nnamespace Http2 = swoole::http2;\n\nstatic zend_class_entry *swoole_http2_client_coro_ce;\nstatic zend_object_handlers swoole_http2_client_coro_handlers;\n\nstatic zend_class_entry *swoole_http2_client_coro_exception_ce;\nstatic zend_object_handlers swoole_http2_client_coro_exception_handlers;\n\nstatic zend_class_entry *swoole_http2_request_ce;\nstatic zend_object_handlers swoole_http2_request_handlers;\n\nstatic zend_class_entry *swoole_http2_response_ce;\nstatic zend_object_handlers swoole_http2_response_handlers;\n\nnamespace swoole {\nnamespace coroutine {\nnamespace http2 {\n\nstruct Stream {\n    uint32_t stream_id;\n    uint8_t gzip;\n    uint8_t flags;\n    String *buffer;\n#ifdef SW_HAVE_ZLIB\n    z_stream gzip_stream;\n    String *gzip_buffer;\n#endif\n    zval zresponse;\n\n    // flow control\n    uint32_t remote_window_size;\n    uint32_t local_window_size;\n};\n\nclass Client {\n  public:\n    std::string host;\n    int port;\n    bool open_ssl;\n    double timeout = network::Socket::default_read_timeout;\n\n    uint32_t stream_id = 0;       // the next send stream id\n    uint32_t last_stream_id = 0;  // the last received stream id\n\n    Http2::Settings local_settings = {};\n    Http2::Settings remote_settings = {};\n\n    // flow control\n    uint32_t remote_window_size = 0;\n    uint32_t local_window_size = 0;\n\n    std::unordered_map<uint32_t, Stream *> streams;\n    std::queue<zend_string *> send_queue;\n\n    /* safety zval */\n    zval _zobject;\n    zval *zobject;\n    SocketImpl *socket_ = nullptr;\n    zval zsocket;\n\n    Client(const char *_host, size_t _host_len, int _port, bool _ssl, const zval *zobj) {\n        host = std::string(_host, _host_len);\n        port = _port;\n        open_ssl = _ssl;\n        _zobject = *zobj;\n        zobject = &_zobject;\n        Http2::init_settings(&local_settings);\n        local_window_size = local_settings.init_window_size;\n    }\n\n    Stream *get_stream(uint32_t _stream_id) {\n        auto i = streams.find(_stream_id);\n        if (i == streams.end()) {\n            return nullptr;\n        } else {\n            return i->second;\n        }\n    }\n\n    ssize_t build_header(const zval *zobj, zval *zrequest, char *buffer);\n\n    void update_error_properties(int code, const char *msg) const {\n        php_swoole_socket_set_error_properties(zobject, code, msg);\n    }\n\n    void io_error() const {\n        update_error_properties(socket_->errCode, socket_->errMsg);\n    }\n\n    void nghttp2_error(int code, const char *msg) const {\n        update_error_properties(code, std_string::format(\"%s with error: %s\", msg, nghttp2_strerror(code)).c_str());\n    }\n\n    bool is_available() const {\n        if (sw_unlikely(!socket_ || !socket_->is_connected())) {\n            php_swoole_socket_set_error_properties(zobject, SW_ERROR_CLIENT_NO_CONNECTION);\n            return false;\n        }\n        return true;\n    }\n\n    void apply_setting(const zval *zset) const {\n        if (socket_ && ZVAL_IS_ARRAY(zset)) {\n            php_swoole_socket_set(socket_, zset);\n        }\n    }\n\n    bool recv_packet(double _timeout) const {\n        if (sw_unlikely(socket_->recv_packet(_timeout) <= 0)) {\n            io_error();\n            return false;\n        }\n        return true;\n    }\n\n    bool connect();\n    Stream *create_stream(uint32_t _stream_id, uint8_t flags);\n    void destroy_stream(Stream *stream);\n\n    bool delete_stream(uint32_t _stream_id) {\n        const auto i = streams.find(_stream_id);\n        if (i == streams.end()) {\n            return false;\n        }\n\n        destroy_stream(i->second);\n        streams.erase(i);\n\n        return true;\n    }\n\n    bool send_window_update(int _stream_id, uint32_t size);\n    bool send_ping_frame();\n    bool send_data(uint32_t _stream_id, const char *p, size_t len, int flag);\n    uint32_t send_request(zval *zrequest);\n    bool write_data(uint32_t _stream_id, zval *zdata, bool end);\n    bool send_goaway_frame(zend_long error_code, const char *debug_data, size_t debug_data_len);\n    ReturnCode parse_frame(zval *return_value, bool pipeline_read = false);\n    bool close() const;\n    void socket_dtor();\n\n    ~Client() {\n        close();\n    }\n\n  private:\n    nghttp2_hd_inflater *inflater = nullptr;\n    nghttp2_hd_deflater *deflater = nullptr;\n\n    bool send_setting();\n    int parse_header(Stream *stream, int flags, char *in, size_t inlen) const;\n\n    void clean_send_queue() {\n        while (!send_queue.empty()) {\n            zend_string *frame = send_queue.front();\n            send_queue.pop();\n            zend_string_release(frame);\n        }\n    }\n\n    bool send(const char *buf, size_t len) {\n        if (socket_->has_bound(SW_EVENT_WRITE)) {\n            if (send_queue.size() > remote_settings.max_concurrent_streams) {\n                socket_->errCode = SW_ERROR_QUEUE_FULL;\n                socket_->errMsg = \"the send queue is full, try again later\";\n                io_error();\n                return false;\n            }\n            send_queue.push(zend_string_init(buf, len, false));\n            return true;\n        }\n        if (sw_unlikely(socket_->send_all(buf, len) != (ssize_t) len)) {\n            io_error();\n            return false;\n        }\n        while (!send_queue.empty()) {\n            zend_string *frame = send_queue.front();\n            if (sw_unlikely(socket_->send_all(frame->val, frame->len) != (ssize_t) frame->len)) {\n                io_error();\n                zend_throw_exception(swoole_http2_client_coro_exception_ce,\n                                     \"failed to send control frame\",\n                                     SW_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED);\n                return false;\n            }\n            send_queue.pop();\n            zend_string_release(frame);\n        }\n        return true;\n    }\n};\n\n}  // namespace http2\n}  // namespace coroutine\n}  // namespace swoole\n\nusing swoole::coroutine::http2::Client;\nusing swoole::coroutine::http2::Stream;\nusing swoole::http2::HeaderSet;\n\nstruct Http2ClientObject {\n    Client *client;\n    zend_object std;\n};\n\nstatic sw_inline Http2ClientObject *http2_client_coro_fetch_object(zend_object *obj) {\n    return reinterpret_cast<Http2ClientObject *>(reinterpret_cast<char *>(obj) -\n                                                 swoole_http2_client_coro_handlers.offset);\n}\n\nstatic sw_inline Client *http2_client_coro_get_client(const zval *zobject) {\n    return http2_client_coro_fetch_object(Z_OBJ_P(zobject))->client;\n}\n\nstatic void http2_client_coro_free_object(zend_object *object) {\n    Http2ClientObject *h2o = http2_client_coro_fetch_object(object);\n    if (h2o->client) {\n        delete h2o->client;\n        h2o->client = nullptr;\n    }\n    zend_object_std_dtor(&h2o->std);\n}\n\nstatic zend_object *http2_client_coro_create_object(zend_class_entry *ce) {\n    auto *request = static_cast<Http2ClientObject *>(zend_object_alloc(sizeof(Http2ClientObject), ce));\n    zend_object_std_init(&request->std, ce);\n    object_properties_init(&request->std, ce);\n    request->std.handlers = &swoole_http2_client_coro_handlers;\n    return &request->std;\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_http2_client_coro, __construct);\nstatic PHP_METHOD(swoole_http2_client_coro, __destruct);\nstatic PHP_METHOD(swoole_http2_client_coro, set);\nstatic PHP_METHOD(swoole_http2_client_coro, connect);\nstatic PHP_METHOD(swoole_http2_client_coro, stats);\nstatic PHP_METHOD(swoole_http2_client_coro, isStreamExist);\nstatic PHP_METHOD(swoole_http2_client_coro, send);\nstatic PHP_METHOD(swoole_http2_client_coro, write);\nstatic PHP_METHOD(swoole_http2_client_coro, recv);\nstatic PHP_METHOD(swoole_http2_client_coro, read);\nstatic PHP_METHOD(swoole_http2_client_coro, ping);\nstatic PHP_METHOD(swoole_http2_client_coro, goaway);\nstatic PHP_METHOD(swoole_http2_client_coro, close);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic const zend_function_entry swoole_http2_client_methods[] =\n{\n    PHP_ME(swoole_http2_client_coro, __construct,   arginfo_class_Swoole_Coroutine_Http2_Client___construct,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http2_client_coro, __destruct,    arginfo_class_Swoole_Coroutine_Http2_Client___destruct,     ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http2_client_coro, set,           arginfo_class_Swoole_Coroutine_Http2_Client_set,            ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http2_client_coro, connect,       arginfo_class_Swoole_Coroutine_Http2_Client_connect,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http2_client_coro, stats,         arginfo_class_Swoole_Coroutine_Http2_Client_stats,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http2_client_coro, isStreamExist, arginfo_class_Swoole_Coroutine_Http2_Client_isStreamExist,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http2_client_coro, send,          arginfo_class_Swoole_Coroutine_Http2_Client_send,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http2_client_coro, write,         arginfo_class_Swoole_Coroutine_Http2_Client_write,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http2_client_coro, recv,          arginfo_class_Swoole_Coroutine_Http2_Client_recv,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http2_client_coro, read,          arginfo_class_Swoole_Coroutine_Http2_Client_read,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http2_client_coro, goaway,        arginfo_class_Swoole_Coroutine_Http2_Client_goaway,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http2_client_coro, ping,          arginfo_class_Swoole_Coroutine_Http2_Client_ping,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http2_client_coro, close,         arginfo_class_Swoole_Coroutine_Http2_Client_close,          ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_http2_client_coro_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(\n        swoole_http2_client_coro, \"Swoole\\\\Coroutine\\\\Http2\\\\Client\", \"Co\\\\Http2\\\\Client\", swoole_http2_client_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http2_client_coro);\n    SW_SET_CLASS_CLONEABLE(swoole_http2_client_coro, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http2_client_coro, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(swoole_http2_client_coro,\n                               http2_client_coro_create_object,\n                               http2_client_coro_free_object,\n                               Http2ClientObject,\n                               std);\n\n    SW_INIT_CLASS_ENTRY_EX(swoole_http2_client_coro_exception,\n                           \"Swoole\\\\Coroutine\\\\Http2\\\\Client\\\\Exception\",\n                           \"Co\\\\Http2\\\\Client\\\\Exception\",\n                           nullptr,\n                           swoole_exception);\n\n    SW_INIT_CLASS_ENTRY(swoole_http2_request, \"Swoole\\\\Http2\\\\Request\", nullptr, nullptr);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http2_request);\n    SW_SET_CLASS_CLONEABLE(swoole_http2_request, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http2_request, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_http2_request);\n\n    SW_INIT_CLASS_ENTRY(swoole_http2_response, \"Swoole\\\\Http2\\\\Response\", nullptr, nullptr);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http2_response);\n    SW_SET_CLASS_CLONEABLE(swoole_http2_response, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http2_response, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_http2_response);\n\n    zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL(\"errCode\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_string(swoole_http2_client_coro_ce, ZEND_STRL(\"errMsg\"), \"\", ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL(\"sock\"), -1, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL(\"type\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http2_client_coro_ce, ZEND_STRL(\"setting\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http2_client_coro_ce, ZEND_STRL(\"socket\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_bool(swoole_http2_client_coro_ce, ZEND_STRL(\"connected\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http2_client_coro_ce, ZEND_STRL(\"host\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL(\"port\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_bool(swoole_http2_client_coro_ce, ZEND_STRL(\"ssl\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_http2_client_coro_ce, ZEND_STRL(\"serverLastStreamId\"), 0, ZEND_ACC_PUBLIC);\n\n    zend_declare_property_string(swoole_http2_request_ce, ZEND_STRL(\"path\"), \"/\", ZEND_ACC_PUBLIC);\n    zend_declare_property_string(swoole_http2_request_ce, ZEND_STRL(\"method\"), \"GET\", ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http2_request_ce, ZEND_STRL(\"headers\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http2_request_ce, ZEND_STRL(\"cookies\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_string(swoole_http2_request_ce, ZEND_STRL(\"data\"), \"\", ZEND_ACC_PUBLIC);\n    zend_declare_property_bool(swoole_http2_request_ce, ZEND_STRL(\"pipeline\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_bool(swoole_http2_request_ce, ZEND_STRL(\"usePipelineRead\"), 0, ZEND_ACC_PUBLIC);\n\n    zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL(\"streamId\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL(\"errCode\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL(\"statusCode\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_bool(swoole_http2_response_ce, ZEND_STRL(\"pipeline\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http2_response_ce, ZEND_STRL(\"headers\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http2_response_ce, ZEND_STRL(\"set_cookie_headers\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http2_response_ce, ZEND_STRL(\"cookies\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http2_response_ce, ZEND_STRL(\"data\"), ZEND_ACC_PUBLIC);\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_TYPE_DATA\", SW_HTTP2_TYPE_DATA);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_TYPE_HEADERS\", SW_HTTP2_TYPE_HEADERS);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_TYPE_PRIORITY\", SW_HTTP2_TYPE_PRIORITY);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_TYPE_RST_STREAM\", SW_HTTP2_TYPE_RST_STREAM);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_TYPE_SETTINGS\", SW_HTTP2_TYPE_SETTINGS);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_TYPE_PUSH_PROMISE\", SW_HTTP2_TYPE_PUSH_PROMISE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_TYPE_PING\", SW_HTTP2_TYPE_PING);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_TYPE_GOAWAY\", SW_HTTP2_TYPE_GOAWAY);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_TYPE_WINDOW_UPDATE\", SW_HTTP2_TYPE_WINDOW_UPDATE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_TYPE_CONTINUATION\", SW_HTTP2_TYPE_CONTINUATION);\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_ERROR_NO_ERROR\", SW_HTTP2_ERROR_NO_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_ERROR_PROTOCOL_ERROR\", SW_HTTP2_ERROR_PROTOCOL_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_ERROR_INTERNAL_ERROR\", SW_HTTP2_ERROR_INTERNAL_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_ERROR_FLOW_CONTROL_ERROR\", SW_HTTP2_ERROR_FLOW_CONTROL_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_ERROR_SETTINGS_TIMEOUT\", SW_HTTP2_ERROR_SETTINGS_TIMEOUT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_ERROR_STREAM_CLOSED\", SW_HTTP2_ERROR_STREAM_CLOSED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_ERROR_FRAME_SIZE_ERROR\", SW_HTTP2_ERROR_FRAME_SIZE_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_ERROR_REFUSED_STREAM\", SW_HTTP2_ERROR_REFUSED_STREAM);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_ERROR_CANCEL\", SW_HTTP2_ERROR_CANCEL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_ERROR_COMPRESSION_ERROR\", SW_HTTP2_ERROR_COMPRESSION_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_ERROR_CONNECT_ERROR\", SW_HTTP2_ERROR_CONNECT_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_ERROR_ENHANCE_YOUR_CALM\", SW_HTTP2_ERROR_ENHANCE_YOUR_CALM);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_ERROR_INADEQUATE_SECURITY\", SW_HTTP2_ERROR_INADEQUATE_SECURITY);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP2_ERROR_HTTP_1_1_REQUIRED\", SW_HTTP2_ERROR_HTTP_1_1_REQUIRED);\n}\n\nvoid Client::socket_dtor() {\n    socket_ = nullptr;\n    clean_send_queue();\n    auto i = streams.begin();\n    while (i != streams.end()) {\n        destroy_stream(i->second);\n        streams.erase(i++);\n    }\n    if (inflater) {\n        nghttp2_hd_inflate_del(inflater);\n        inflater = nullptr;\n    }\n    if (deflater) {\n        nghttp2_hd_deflate_del(deflater);\n        deflater = nullptr;\n    }\n    zend_update_property_bool(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"connected\"), 0);\n    zend_update_property_null(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"socket\"));\n    zval_ptr_dtor(&zsocket);\n    ZVAL_NULL(&zsocket);\n}\n\nbool Client::connect() {\n    if (sw_unlikely(socket_ != nullptr)) {\n        update_error_properties(EISCONN, strerror(EISCONN));\n        return false;\n    }\n\n    auto object = php_swoole_create_socket(network::Socket::convert_to_type(host));\n    if (UNEXPECTED(!object)) {\n        php_swoole_socket_set_error_properties(zobject, errno, strerror(errno));\n        return false;\n    }\n\n    ZVAL_OBJ(&zsocket, object);\n    socket_ = php_swoole_get_socket(&zsocket);\n    socket_->set_dtor([this](Socket *_socket) { socket_dtor(); });\n    socket_->set_zero_copy(true);\n    if (open_ssl && !socket_->enable_ssl_encrypt()) {\n        io_error();\n        close();\n        return false;\n    }\n    socket_->http2 = true;\n    socket_->open_length_check = true;\n    socket_->protocol.package_length_size = SW_HTTP2_FRAME_HEADER_SIZE;\n    socket_->protocol.package_length_offset = 0;\n    socket_->protocol.package_body_offset = 0;\n    socket_->protocol.get_package_length = Http2::get_frame_length;\n\n    apply_setting(\n        sw_zend_read_property_ex(swoole_http2_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0));\n\n    if (!socket_->connect(host, port)) {\n        io_error();\n        close();\n        return false;\n    }\n\n    stream_id = 1;\n    // [init]: we must set default value, server is not always send all the settings\n    Http2::init_settings(&remote_settings);\n    remote_window_size = remote_settings.init_window_size;\n\n    int ret = nghttp2_hd_inflate_new2(&inflater, php_nghttp2_mem());\n    if (ret != 0) {\n        nghttp2_error(ret, \"nghttp2_hd_inflate_new2() failed\");\n        close();\n        return false;\n    }\n    ret = nghttp2_hd_deflate_new2(&deflater, local_settings.header_table_size, php_nghttp2_mem());\n    if (ret != 0) {\n        nghttp2_error(ret, \"nghttp2_hd_deflate_new2() failed\");\n        close();\n        return false;\n    }\n\n    if (!send(ZEND_STRL(SW_HTTP2_PRI_STRING))) {\n        close();\n        return false;\n    }\n\n    if (!send_setting()) {\n        close();\n        return false;\n    }\n\n    zend_update_property(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL(\"socket\"), &zsocket);\n    zend_update_property_bool(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"connected\"), 1);\n\n    return true;\n}\n\nbool Client::close() const {\n    /*\n     * The socket_ pointer MUST be staged,\n     * when client close the member variable may be set to nullptr in socket dtor\n     */\n    SocketImpl *_socket = socket_;\n    if (_socket == nullptr) {\n        update_error_properties(EBADF, strerror(EBADF));\n        return false;\n    }\n    zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL(\"connected\"), 0);\n    if (!_socket->close()) {\n        update_error_properties(_socket->errCode, _socket->errMsg);\n        return false;\n    }\n    return true;\n}\n\nReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) {\n    char *buf = socket_->get_read_buffer()->str;\n    uint8_t type = buf[3];\n    uint8_t flags = buf[4];\n    uint32_t stream_error = 0;\n    uint32_t _stream_id = ntohl((*(int *) (buf + 5))) & 0x7fffffff;\n    ssize_t length = Http2::get_length(buf);\n    buf += SW_HTTP2_FRAME_HEADER_SIZE;\n\n    char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE];\n\n    if (_stream_id > last_stream_id) {\n        last_stream_id = _stream_id;\n    }\n\n    switch (type) {\n    case SW_HTTP2_TYPE_SETTINGS: {\n        if (flags & SW_HTTP2_FLAG_ACK) {\n            swoole_http2_frame_trace_log(\"ACK\");\n            return SW_CONTINUE;\n        }\n\n        auto rc = Http2::unpack_setting_data(buf, length, [&](uint16_t id, uint32_t value) -> ReturnCode {\n            swoole_http2_frame_trace_log(\"id=%d, value=%d\", id, value);\n            switch (id) {\n            case SW_HTTP2_SETTING_HEADER_TABLE_SIZE:\n                if (value != remote_settings.header_table_size) {\n                    remote_settings.header_table_size = value;\n                    int ret = nghttp2_hd_deflate_change_table_size(deflater, value);\n                    if (ret != 0) {\n                        nghttp2_error(ret, \"nghttp2_hd_deflate_change_table_size() failed\");\n                        return SW_ERROR;\n                    }\n                }\n                swoole_trace_log(SW_TRACE_HTTP2, \"setting: header_compression_table_max=%u\", value);\n                break;\n            case SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:\n                remote_settings.max_concurrent_streams = value;\n                swoole_trace_log(SW_TRACE_HTTP2, \"setting: max_concurrent_streams=%u\", value);\n                break;\n            case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE:\n                remote_window_size = remote_settings.init_window_size = value;\n                swoole_trace_log(SW_TRACE_HTTP2, \"setting: init_send_window=%u\", value);\n                break;\n            case SW_HTTP2_SETTINGS_MAX_FRAME_SIZE:\n                remote_settings.max_frame_size = value;\n                swoole_trace_log(SW_TRACE_HTTP2, \"setting: max_frame_size=%u\", value);\n                break;\n            case SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:\n                if (value != remote_settings.max_header_list_size) {\n                    remote_settings.max_header_list_size = value;\n#if 0\n                    int ret = nghttp2_hd_inflate_change_table_size(inflater, value);\n                    if (ret != 0) {\n                        nghttp2_error(ret, \"nghttp2_hd_inflate_change_table_size() failed\");\n                        return SW_ERROR;\n                    }\n#endif\n                }\n                swoole_trace_log(SW_TRACE_HTTP2, \"setting: max_header_list_size=%u\", value);\n                break;\n            default:\n                // disable warning and ignore it because some websites are not following http2 protocol totally\n                // swoole_warning(\"unknown option[%d]: %d\", id, value);\n                break;\n            }\n            return SW_SUCCESS;\n        });\n\n        if (rc != SW_SUCCESS) {\n            return rc;\n        }\n\n        Http2::set_frame_header(frame, SW_HTTP2_TYPE_SETTINGS, 0, SW_HTTP2_FLAG_ACK, _stream_id);\n        if (!send(frame, SW_HTTP2_FRAME_HEADER_SIZE)) {\n            return SW_ERROR;\n        }\n        return SW_CONTINUE;\n    }\n    case SW_HTTP2_TYPE_WINDOW_UPDATE: {\n        uint32_t value = ntohl(*(uint32_t *) buf);\n        swoole_trace_log(\n            SW_TRACE_HTTP2, \"[\" SW_ECHO_YELLOW \"] stream_id=%d, size=%d\", \"WINDOW_UPDATE\", stream_id, value);\n        if (_stream_id == 0) {\n            remote_window_size += value;\n        } else {\n            Stream *stream = get_stream(_stream_id);\n            if (stream) {\n                stream->remote_window_size += value;\n            }\n        }\n        return SW_CONTINUE;\n    }\n    case SW_HTTP2_TYPE_PING: {\n        swoole_http2_frame_trace_log(\"ping\");\n        if (!(flags & SW_HTTP2_FLAG_ACK)) {\n            Http2::set_frame_header(\n                frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_ACK, _stream_id);\n            memcpy(\n                frame + SW_HTTP2_FRAME_HEADER_SIZE, buf + SW_HTTP2_FRAME_HEADER_SIZE, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE);\n            if (!send(frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE)) {\n                return SW_ERROR;\n            }\n        }\n        return SW_CONTINUE;\n    }\n    case SW_HTTP2_TYPE_GOAWAY: {\n        uint32_t server_last_stream_id = ntohl(*(uint32_t *) (buf));\n        buf += 4;\n        uint32_t value = ntohl(*(uint32_t *) (buf));\n        buf += 4;\n        swoole_http2_frame_trace_log(\"last_stream_id=%d, error_code=%d, opaque_data=[%.*s]\",\n                                     server_last_stream_id,\n                                     value,\n                                     (int) (length - SW_HTTP2_GOAWAY_SIZE),\n                                     buf);\n\n        // update goaway error code and error msg\n        zend_update_property_long(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"errCode\"), value);\n        zend_update_property_stringl(\n            swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"errMsg\"), buf, length - SW_HTTP2_GOAWAY_SIZE);\n        zend_update_property_long(\n            swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"serverLastStreamId\"), server_last_stream_id);\n        close();\n        return SW_CLOSE;\n    }\n    case SW_HTTP2_TYPE_RST_STREAM: {\n        stream_error = ntohl(*(uint32_t *) (buf));\n        swoole_http2_frame_trace_log(\"error_code=%d\", stream_error);\n\n        // delete and free quietly\n        delete_stream(_stream_id);\n\n        return SW_CONTINUE;\n    }\n    /**\n     * TODO not support push_promise\n     */\n    case SW_HTTP2_TYPE_PUSH_PROMISE: {\n#ifdef SW_DEBUG\n        uint32_t promise_stream_id = ntohl(*(uint32_t *) (buf)) & 0x7fffffff;\n        swoole_http2_frame_trace_log(\"promise_stream_id=%d\", promise_stream_id);\n#endif\n        // auto promise_stream = create_stream(promise_stream_id, false);\n        // RETVAL_ZVAL(promise_stream->response_object, 0, 0);\n        // return SW_READY;\n        return SW_CONTINUE;\n    }\n    default: {\n        swoole_http2_frame_trace_log(\"\");\n    }\n    }\n\n    Stream *stream = get_stream(_stream_id);\n    // The stream is not found or has closed\n    if (stream == nullptr) {\n        swoole_notice(\"http2 stream#%d belongs to an unknown type or it never registered\", _stream_id);\n        return SW_CONTINUE;\n    }\n    if (type == SW_HTTP2_TYPE_HEADERS) {\n        parse_header(stream, flags, buf, length);\n    } else if (type == SW_HTTP2_TYPE_DATA) {\n        if (!(flags & SW_HTTP2_FLAG_END_STREAM)) {\n            stream->flags |= SW_HTTP2_STREAM_PIPELINE_RESPONSE;\n        }\n        if (length > 0) {\n            if (!stream->buffer) {\n                stream->buffer = make_string(SW_HTTP2_DATA_BUFFER_SIZE);\n            }\n#ifdef SW_HAVE_ZLIB\n            if (stream->gzip) {\n                if (php_swoole_zlib_decompress(&stream->gzip_stream, stream->gzip_buffer, buf, length) == SW_ERR) {\n                    swoole_warning(\"decompress failed\");\n                    return SW_ERROR;\n                }\n                stream->buffer->append(stream->gzip_buffer->str, stream->gzip_buffer->length);\n            } else\n#endif\n            {\n                stream->buffer->append(buf, length);\n            }\n\n            // now we control the connection flow only (not stream)\n            // our window size is unlimited, so we don't worry about subtraction overflow\n            local_window_size -= length;\n            stream->local_window_size -= length;\n            if (local_window_size < (local_settings.init_window_size / 4)) {\n                if (!send_window_update(0, local_settings.init_window_size - local_window_size)) {\n                    return SW_ERROR;\n                }\n                local_window_size = local_settings.init_window_size;\n            }\n            if (stream->local_window_size < (local_settings.init_window_size / 4)) {\n                if (!send_window_update(_stream_id, local_settings.init_window_size - stream->local_window_size)) {\n                    return SW_ERROR;\n                }\n                stream->local_window_size = local_settings.init_window_size;\n            }\n        }\n    }\n\n    bool end = (flags & SW_HTTP2_FLAG_END_STREAM) || type == SW_HTTP2_TYPE_RST_STREAM || type == SW_HTTP2_TYPE_GOAWAY;\n    pipeline_read = ((pipeline_read || (stream->flags & SW_HTTP2_STREAM_USE_PIPELINE_READ)) &&\n                     (stream->flags & SW_HTTP2_STREAM_PIPELINE_RESPONSE));\n    if (end || pipeline_read) {\n        zval *zresponse = &stream->zresponse;\n        if (type == SW_HTTP2_TYPE_RST_STREAM) {\n            zend::object_set(zresponse, ZEND_STRL(\"statusCode\"), HTTP_ESTATUS_SERVER_RESET);\n            zend::object_set(zresponse, ZEND_STRL(\"errCode\"), stream_error);\n        }\n        if (stream->buffer && stream->buffer->length > 0) {\n            zend_update_property_stringl(swoole_http2_response_ce,\n                                         SW_Z8_OBJ_P(zresponse),\n                                         ZEND_STRL(\"data\"),\n                                         stream->buffer->str,\n                                         stream->buffer->length);\n            stream->buffer->clear();\n        }\n        if (!end) {\n            zend_update_property_bool(\n                swoole_http2_response_ce, SW_Z8_OBJ_P(&stream->zresponse), ZEND_STRL(\"pipeline\"), 1);\n        }\n        RETVAL_ZVAL(zresponse, end, 0);\n        if (!end) {\n            // reinit response object for the following frames\n            object_init_ex(zresponse, swoole_http2_response_ce);\n            zend_update_property_long(\n                swoole_http2_response_ce, SW_Z8_OBJ_P(&stream->zresponse), ZEND_STRL(\"streamId\"), _stream_id);\n        } else {\n            delete_stream(_stream_id);\n        }\n\n        return SW_READY;\n    }\n\n    return SW_CONTINUE;\n}\n\n#ifdef SW_HAVE_ZLIB\nint php_swoole_zlib_decompress(z_stream *stream, String *buffer, char *body, int length) {\n    int status = 0;\n\n    stream->avail_in = length;\n    stream->next_in = (Bytef *) body;\n    stream->total_in = 0;\n    stream->total_out = 0;\n\n    swoole_trace_log(SW_TRACE_ZLIB,\n                     SW_START_LINE \"\\nstatus=%d\\tavail_in=%u,\\tavail_out=%u,\\ttotal_in=%lu,\\ttotal_out=%lu\\n\",\n                     status,\n                     stream->avail_in,\n                     stream->avail_out,\n                     stream->total_in,\n                     stream->total_out);\n\n    buffer->clear();\n\n    while (true) {\n        stream->avail_out = buffer->size - buffer->length;\n        stream->next_out = (Bytef *) (buffer->str + buffer->length);\n\n        status = inflate(stream, Z_SYNC_FLUSH);\n\n        swoole_trace_log(SW_TRACE_ZLIB,\n                         \"status=%d\\tavail_in=%d,\\tavail_out=%d,\\ttotal_in=%lu,\\ttotal_out=%lu,\\tlength=%lu\\n\",\n                         status,\n                         stream->avail_in,\n                         stream->avail_out,\n                         stream->total_in,\n                         stream->total_out,\n                         buffer->length);\n\n        if (status >= 0) {\n            buffer->length = stream->total_out;\n        }\n        if (status == Z_STREAM_END) {\n            return SW_OK;\n        } else if (status == Z_OK) {\n            if (buffer->length + 4096 >= buffer->size) {\n                buffer->extend();\n            }\n            if (stream->avail_in == 0) {\n                return SW_OK;\n            }\n        } else {\n            return SW_ERR;\n        }\n    }\n    return SW_ERR;\n}\n#endif\n\nstatic PHP_METHOD(swoole_http2_client_coro, __construct) {\n    char *host;\n    size_t host_len;\n    zend_long port = 80;\n    zend_bool ssl = false;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 3)\n    Z_PARAM_STRING(host, host_len)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(port)\n    Z_PARAM_BOOL(ssl)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (host_len == 0) {\n        zend_throw_exception(swoole_http2_client_coro_exception_ce, \"host is empty\", SW_ERROR_INVALID_PARAMS);\n        RETURN_FALSE;\n    }\n\n    auto *client = new Client(host, host_len, port, ssl, ZEND_THIS);\n    http2_client_coro_fetch_object(Z_OBJ_P(ZEND_THIS))->client = client;\n\n    zend_update_property_stringl(\n        swoole_http2_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"host\"), host, host_len);\n    zend_update_property_long(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"port\"), port);\n    zend_update_property_bool(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"ssl\"), ssl);\n}\n\nstatic PHP_METHOD(swoole_http2_client_coro, set) {\n    Client *h2c = http2_client_coro_get_client(ZEND_THIS);\n    zval *zset;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ARRAY(zset)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zval *zsetting =\n        sw_zend_read_and_convert_property_array(swoole_http2_client_coro_ce, ZEND_THIS, ZEND_STRL(\"setting\"), 0);\n    php_array_merge(Z_ARRVAL_P(zsetting), Z_ARRVAL_P(zset));\n\n    h2c->apply_setting(zset);\n\n    RETURN_TRUE;\n}\n\n/**\n * called in read channel\n */\nbool Client::send_window_update(int _stream_id, uint32_t size) {\n    char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE];\n    swoole_http2_send_trace_log(\"[\" SW_ECHO_YELLOW \"] stream_id=%d, size=%d\", \"WINDOW_UPDATE\", stream_id, size);\n    *(uint32_t *) ((char *) frame + SW_HTTP2_FRAME_HEADER_SIZE) = htonl(size);\n    Http2::set_frame_header(frame, SW_HTTP2_TYPE_WINDOW_UPDATE, SW_HTTP2_WINDOW_UPDATE_SIZE, 0, _stream_id);\n    return send(frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE);\n}\n\n/**\n * called on connect\n */\nbool Client::send_setting() {\n    char frame[SW_HTTP2_SETTING_FRAME_SIZE];\n    size_t n = Http2::pack_setting_frame(frame, local_settings, false);\n    swoole_http2_send_trace_log(\"[\" SW_ECHO_MAGENTA\n                                \"] <header_table_size=%u, enable_push=%u, max_concurrent_streams=%u, \"\n                                \"init_window_size=%u, max_frame_size=%u, max_header_list_size=%u>\",\n                                Http2::get_type(SW_HTTP2_TYPE_SETTINGS),\n                                local_settings.header_table_size,\n                                local_settings.enable_push,\n                                local_settings.max_concurrent_streams,\n                                local_settings.init_window_size,\n                                local_settings.max_frame_size,\n                                local_settings.max_header_list_size);\n    return send(frame, n);\n}\n\nvoid php_swoole_http_parse_set_cookies(const char *at, size_t length, zval *zcookies, zval *zset_cookie_headers);\n\nint Client::parse_header(Stream *stream, int flags, char *in, size_t inlen) const {\n    zval *zresponse = &stream->zresponse;\n\n    if (flags & SW_HTTP2_FLAG_PRIORITY) {\n        // int stream_deps = ntohl(*(int *) (in));\n        // uint8_t weight = in[4];\n        in += 5;\n        inlen -= 5;\n    }\n\n    zval *zheaders =\n        sw_zend_read_and_convert_property_array(swoole_http2_response_ce, zresponse, ZEND_STRL(\"headers\"), 0);\n    zval *zcookies =\n        sw_zend_read_and_convert_property_array(swoole_http2_response_ce, zresponse, ZEND_STRL(\"cookies\"), 0);\n    zval *zset_cookie_headers = sw_zend_read_and_convert_property_array(\n        swoole_http2_response_ce, zresponse, ZEND_STRL(\"set_cookie_headers\"), 0);\n\n    int inflate_flags = 0;\n\n    do {\n        nghttp2_nv nv;\n\n        ssize_t rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, (uchar *) in, inlen, 1);\n        if (rv < 0) {\n            nghttp2_error(rv, \"nghttp2_hd_inflate_hd failed\");\n            return SW_ERR;\n        }\n\n        in += (size_t) rv;\n        inlen -= (size_t) rv;\n\n        swoole_trace_log(SW_TRACE_HTTP2,\n                         \"[\" SW_ECHO_GREEN \"] %.*s[%lu]: %.*s[%lu]\",\n                         \"HEADER\",\n                         (int) nv.namelen,\n                         nv.name,\n                         nv.namelen,\n                         (int) nv.valuelen,\n                         nv.value,\n                         nv.valuelen);\n\n        if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {\n            if (nv.name[0] == ':') {\n                if (SW_STRCASEEQ((char *) nv.name + 1, nv.namelen - 1, \"status\")) {\n                    zend::object_set(zresponse, ZEND_STRL(\"statusCode\"), sw_atol((char *) nv.value));\n                }\n            } else {\n#ifdef SW_HAVE_ZLIB\n                if (SW_STRCASEEQ((char *) nv.name, nv.namelen, \"content-encoding\") &&\n                    SW_STR_ISTARTS_WITH((char *) nv.value, nv.valuelen, \"gzip\")) {\n                    /**\n                     * init zlib stream\n                     */\n                    stream->gzip = 1;\n                    memset(&stream->gzip_stream, 0, sizeof(stream->gzip_stream));\n                    stream->gzip_buffer = make_string(8192);\n                    stream->gzip_stream.zalloc = php_zlib_alloc;\n                    stream->gzip_stream.zfree = php_zlib_free;\n                    /**\n                     * zlib decode\n                     */\n                    if (Z_OK != inflateInit2(&stream->gzip_stream, MAX_WBITS + 16)) {\n                        swoole_warning(\"inflateInit2() failed\");\n                        return SW_ERR;\n                    }\n                } else\n#endif\n                    if (SW_STRCASEEQ((char *) nv.name, nv.namelen, \"set-cookie\")) {\n                    php_swoole_http_parse_set_cookies((char *) nv.value, nv.valuelen, zcookies, zset_cookie_headers);\n                }\n                add_assoc_stringl_ex(zheaders, (char *) nv.name, nv.namelen, (char *) nv.value, nv.valuelen);\n            }\n        }\n    } while ([=] {\n        if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {\n            nghttp2_hd_inflate_end_headers(inflater);\n            return false;\n        }\n        return inlen != 0;\n    }());\n\n    return SW_OK;\n}\n\nssize_t Client::build_header(const zval *zobj, zval *zrequest, char *buffer) {\n    Client *h2c = http2_client_coro_get_client(zobj);\n    zval *zmethod = sw_zend_read_property_ex(swoole_http2_request_ce, zrequest, SW_ZSTR_KNOWN(SW_ZEND_STR_METHOD), 0);\n    zval *zpath = sw_zend_read_property_ex(swoole_http2_request_ce, zrequest, SW_ZSTR_KNOWN(SW_ZEND_STR_PATH), 0);\n    zval *zheaders = sw_zend_read_property_ex(swoole_http2_request_ce, zrequest, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADERS), 0);\n    zval *zcookies = sw_zend_read_property_ex(swoole_http2_request_ce, zrequest, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIES), 0);\n    HeaderSet headers(8 + php_swoole_array_length_safe(zheaders) + php_swoole_array_length_safe(zcookies));\n    bool find_host = false;\n\n    if (Z_TYPE_P(zmethod) != IS_STRING || Z_STRLEN_P(zmethod) == 0) {\n        headers.add(ZEND_STRL(\":method\"), ZEND_STRL(\"GET\"));\n    } else {\n        headers.add(ZEND_STRL(\":method\"), Z_STRVAL_P(zmethod), Z_STRLEN_P(zmethod));\n    }\n    if (Z_TYPE_P(zpath) != IS_STRING || Z_STRLEN_P(zpath) == 0) {\n        headers.add(ZEND_STRL(\":path\"), \"/\", 1);\n    } else {\n        headers.add(ZEND_STRL(\":path\"), Z_STRVAL_P(zpath), Z_STRLEN_P(zpath));\n    }\n    if (h2c->open_ssl) {\n        headers.add(ZEND_STRL(\":scheme\"), ZEND_STRL(\"https\"));\n    } else {\n        headers.add(ZEND_STRL(\":scheme\"), ZEND_STRL(\"http\"));\n    }\n    // Host\n    headers.reserve_one();\n\n    if (ZVAL_IS_ARRAY(zheaders)) {\n        zend_string *key;\n        zval *zvalue;\n\n        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), key, zvalue) {\n            if (UNEXPECTED(!key || *ZSTR_VAL(key) == ':' || ZVAL_IS_NULL(zvalue))) {\n                continue;\n            }\n            zend::String str_value(zvalue);\n            if (SW_STRCASEEQ(ZSTR_VAL(key), ZSTR_LEN(key), \"host\")) {\n                headers.add(HTTP2_CLIENT_HOST_HEADER_INDEX, ZEND_STRL(\":authority\"), str_value.val(), str_value.len());\n                find_host = true;\n            } else {\n                headers.add(ZSTR_VAL(key), ZSTR_LEN(key), str_value.val(), str_value.len());\n            }\n        }\n        ZEND_HASH_FOREACH_END();\n    }\n    if (!find_host) {\n        const std::string *host_ptr;\n        std::string _host;\n        if (!h2c->open_ssl ? h2c->port != 80 : h2c->port != 443) {\n            _host = std_string::format(\"%s:%d\", h2c->host.c_str(), h2c->port);\n            host_ptr = &_host;\n        } else {\n            host_ptr = &h2c->host;\n        }\n        headers.add(HTTP2_CLIENT_HOST_HEADER_INDEX, ZEND_STRL(\":authority\"), host_ptr->c_str(), host_ptr->length());\n    }\n    // http cookies\n    if (ZVAL_IS_ARRAY(zcookies)) {\n        zend_string *key;\n        zval *zvalue;\n        size_t encoded_value_len;\n        String *header_buffer = sw_tg_buffer();\n\n        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zcookies), key, zvalue) {\n            if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue))) {\n                continue;\n            }\n            zend::String str_value(zvalue);\n            header_buffer->clear();\n            header_buffer->append(ZSTR_VAL(key), ZSTR_LEN(key));\n            header_buffer->append(\"=\", 1);\n            char *encoded_value = php_swoole_url_encode(str_value.val(), str_value.len(), &encoded_value_len);\n            if (encoded_value) {\n                header_buffer->append(encoded_value, encoded_value_len);\n                efree(encoded_value);\n                headers.add(ZEND_STRL(\"cookie\"), header_buffer->str, header_buffer->length);\n            }\n        }\n        ZEND_HASH_FOREACH_END();\n    }\n\n    size_t buflen = nghttp2_hd_deflate_bound(h2c->deflater, headers.get(), headers.len());\n#if 0\n    if (buflen > h2c->remote_settings.max_header_list_size) {\n        php_swoole_error(E_WARNING,\n                         \"header cannot bigger than remote max_header_list_size %u\",\n                         client->remote_settings.max_header_list_size);\n        return -1;\n    }\n#endif\n    ssize_t rv = nghttp2_hd_deflate_hd(h2c->deflater, (uchar *) buffer, buflen, headers.get(), headers.len());\n    if (rv < 0) {\n        h2c->nghttp2_error(rv, \"nghttp2_hd_deflate_hd() failed\");\n        return -1;\n    }\n    return rv;\n}\n\nvoid Client::destroy_stream(Stream *stream) {\n    delete (stream->buffer);\n#ifdef SW_HAVE_ZLIB\n    if (stream->gzip) {\n        inflateEnd(&stream->gzip_stream);\n    }\n    delete (stream->gzip_buffer);\n#endif\n    zval_ptr_dtor(&stream->zresponse);\n    efree(stream);\n}\n\nStream *Client::create_stream(uint32_t _stream_id, uint8_t flags) {\n    // malloc\n    auto *stream = static_cast<Stream *>(ecalloc(1, sizeof(Stream)));\n    // init\n    stream->stream_id = _stream_id;\n    stream->flags = flags;\n    stream->remote_window_size = remote_settings.init_window_size;\n    stream->local_window_size = local_settings.init_window_size;\n    streams.emplace(_stream_id, stream);\n    // create response object\n    object_init_ex(&stream->zresponse, swoole_http2_response_ce);\n    zend_update_property_long(\n        swoole_http2_response_ce, SW_Z8_OBJ_P(&stream->zresponse), ZEND_STRL(\"streamId\"), _stream_id);\n\n    return stream;\n}\n\n/**\n * called in write channel\n */\nbool Client::send_ping_frame() {\n    char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE];\n    Http2::set_frame_header(frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_NONE, 0);\n    swoole_http2_send_trace_log(\"[\" SW_ECHO_CYAN \"]\", \"PING\");\n    return send(frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE);\n}\n\nbool Client::send_data(uint32_t _stream_id, const char *p, size_t len, int flag) {\n    uint8_t send_flag;\n    uint32_t send_len;\n    char header[SW_HTTP2_FRAME_HEADER_SIZE];\n    while (len > 0) {\n        if (len > local_settings.max_frame_size) {\n            send_len = local_settings.max_frame_size;\n            send_flag = 0;\n        } else {\n            send_len = len;\n            send_flag = flag;\n        }\n        Http2::set_frame_header(header, SW_HTTP2_TYPE_DATA, send_len, send_flag, _stream_id);\n        if (!send(header, SW_HTTP2_FRAME_HEADER_SIZE)) {\n            return false;\n        }\n        if (!send(p, send_len)) {\n            return false;\n        }\n        len -= send_len;\n        p += send_len;\n    }\n    return true;\n}\n\n/**\n * called in write channel\n */\nuint32_t Client::send_request(zval *zrequest) {\n    zval *zheaders =\n        sw_zend_read_and_convert_property_array(swoole_http2_request_ce, zrequest, ZEND_STRL(\"headers\"), 0);\n    zval *zdata = sw_zend_read_property_ex(swoole_http2_request_ce, zrequest, SW_ZSTR_KNOWN(SW_ZEND_STR_DATA), 0);\n    zval *zpipeline =\n        sw_zend_read_property_ex(swoole_http2_request_ce, zrequest, SW_ZSTR_KNOWN(SW_ZEND_STR_PIPELINE), 0);\n    zval ztmp,\n        *zuse_pipeline_read = zend_read_property_ex(\n            Z_OBJCE_P(zrequest), SW_Z8_OBJ_P(zrequest), SW_ZSTR_KNOWN(SW_ZEND_STR_USE_PIPELINE_READ), true, &ztmp);\n    bool is_data_empty = Z_TYPE_P(zdata) == IS_STRING ? Z_STRLEN_P(zdata) == 0 : !zval_is_true(zdata);\n\n    if (ZVAL_IS_ARRAY(zdata)) {\n        add_assoc_stringl_ex(\n            zheaders, ZEND_STRL(\"content-type\"), (char *) ZEND_STRL(\"application/x-www-form-urlencoded\"));\n    }\n\n    /**\n     * send headers\n     */\n    char *buffer = sw_tg_buffer()->str;\n    ssize_t bytes = build_header(zobject, zrequest, buffer + SW_HTTP2_FRAME_HEADER_SIZE);\n\n    if (bytes <= 0) {\n        return 0;\n    }\n\n    uint8_t flags = 0;\n    if (zval_is_true(zpipeline)) {\n        flags |= SW_HTTP2_STREAM_PIPELINE_REQUEST;\n    }\n    if (zval_is_true(zuse_pipeline_read)) {\n        flags |= SW_HTTP2_STREAM_USE_PIPELINE_READ;\n    }\n\n    auto stream = create_stream(stream_id, flags);\n\n    flags = SW_HTTP2_FLAG_END_HEADERS;\n\n    if (is_data_empty && !(stream->flags & SW_HTTP2_STREAM_PIPELINE_REQUEST)) {\n        flags |= SW_HTTP2_FLAG_END_STREAM;\n    }\n\n    Http2::set_frame_header(buffer, SW_HTTP2_TYPE_HEADERS, bytes, flags, stream->stream_id);\n\n    swoole_http2_send_trace_log(\n        \"[\" SW_ECHO_GREEN \", STREAM#%d] length=%zd\", Http2::get_type(SW_HTTP2_TYPE_HEADERS), stream->stream_id, bytes);\n    if (!send(buffer, SW_HTTP2_FRAME_HEADER_SIZE + bytes)) {\n        return 0;\n    }\n\n    /**\n     * send body\n     */\n    if (!is_data_empty) {\n        char *p;\n        size_t len;\n        smart_str formstr_s = {};\n        zend::String str_zpost_data;\n\n        int flag = (stream->flags & SW_HTTP2_STREAM_PIPELINE_REQUEST) ? 0 : SW_HTTP2_FLAG_END_STREAM;\n        if (ZVAL_IS_ARRAY(zdata)) {\n            p = php_swoole_http_build_query(zdata, &len, &formstr_s);\n            if (p == nullptr) {\n                php_swoole_error(E_WARNING, \"http_build_query failed\");\n                return 0;\n            }\n        } else {\n            str_zpost_data = zdata;\n            p = str_zpost_data.val();\n            len = str_zpost_data.len();\n        }\n\n        swoole_http2_send_trace_log(\"[\" SW_ECHO_GREEN \", END, STREAM#%d] length=%zu\",\n                                    Http2::get_type(SW_HTTP2_TYPE_DATA),\n                                    stream->stream_id,\n                                    len);\n\n        if (!send_data(stream->stream_id, p, len, flag)) {\n            return 0;\n        }\n\n        if (formstr_s.s) {\n            smart_str_free(&formstr_s);\n        }\n    }\n\n    stream_id += 2;\n\n    return stream->stream_id;\n}\n\n/**\n * called in write channel\n */\nbool Client::write_data(uint32_t _stream_id, zval *zdata, bool end) {\n    char buffer[SW_HTTP2_FRAME_HEADER_SIZE];\n    Stream *stream = get_stream(_stream_id);\n    int flag = end ? SW_HTTP2_FLAG_END_STREAM : 0;\n\n    if (stream == nullptr || !(stream->flags & SW_HTTP2_STREAM_PIPELINE_REQUEST) ||\n        (stream->flags & SW_HTTP2_STREAM_REQUEST_END)) {\n        update_error_properties(EINVAL,\n                                std_string::format(\"unable to found active pipeline stream#%u\", _stream_id).c_str());\n        return false;\n    }\n\n    if (ZVAL_IS_ARRAY(zdata)) {\n        size_t len;\n        smart_str formstr_s = {};\n        char *formstr = php_swoole_http_build_query(zdata, &len, &formstr_s);\n        if (formstr == nullptr) {\n            php_swoole_error(E_WARNING, \"http_build_query failed\");\n            return false;\n        }\n        Http2::set_frame_header(buffer, SW_HTTP2_TYPE_DATA, len, flag, _stream_id);\n        swoole_trace_log(SW_TRACE_HTTP2,\n                         \"[\" SW_ECHO_GREEN \",%s STREAM#%d] length=%zu\",\n                         Http2::get_type(SW_HTTP2_TYPE_DATA),\n                         end ? \" END,\" : \"\",\n                         stream_id,\n                         len);\n        if (!send(buffer, SW_HTTP2_FRAME_HEADER_SIZE) || !send(formstr, len)) {\n            smart_str_free(&formstr_s);\n            return false;\n        }\n        smart_str_free(&formstr_s);\n    } else {\n        zend::String data(zdata);\n        Http2::set_frame_header(buffer, SW_HTTP2_TYPE_DATA, data.len(), flag, _stream_id);\n        swoole_trace_log(SW_TRACE_HTTP2,\n                         \"[\" SW_ECHO_GREEN \",%s STREAM#%d] length=%zu\",\n                         Http2::get_type(SW_HTTP2_TYPE_DATA),\n                         end ? \" END,\" : \"\",\n                         stream_id,\n                         data.len());\n        if (!send(buffer, SW_HTTP2_FRAME_HEADER_SIZE) || !send(data.val(), data.len())) {\n            return false;\n        }\n    }\n\n    if (end) {\n        stream->flags |= SW_HTTP2_STREAM_REQUEST_END;\n    }\n\n    return true;\n}\n\n/**\n * called in write channel\n */\nbool Client::send_goaway_frame(zend_long error_code, const char *debug_data, size_t debug_data_len) {\n    size_t length = SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_GOAWAY_SIZE + debug_data_len;\n    auto frame = (char *) ecalloc(1, length);\n    Http2::set_frame_header(frame, SW_HTTP2_TYPE_GOAWAY, SW_HTTP2_GOAWAY_SIZE + debug_data_len, error_code, 0);\n    *(uint32_t *) (frame + SW_HTTP2_FRAME_HEADER_SIZE) = htonl(last_stream_id);\n    *(uint32_t *) (frame + SW_HTTP2_FRAME_HEADER_SIZE + 4) = htonl(error_code);\n    if (debug_data_len > 0) {\n        memcpy(frame + SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_GOAWAY_SIZE, debug_data, debug_data_len);\n    }\n    swoole_http2_send_trace_log(\"[\" SW_ECHO_RED \"] last-sid=%u, error-code=\" ZEND_LONG_FMT,\n                                Http2::get_type(SW_HTTP2_TYPE_GOAWAY),\n                                last_stream_id,\n                                error_code);\n    bool ret = send(frame, length);\n    efree(frame);\n    return ret;\n}\n\nstatic PHP_METHOD(swoole_http2_client_coro, send) {\n    Client *h2c = http2_client_coro_get_client(ZEND_THIS);\n\n    if (!h2c->is_available()) {\n        RETURN_FALSE;\n    }\n\n    zval *zrequest;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(zrequest, swoole_http2_request_ce)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    SW_CLIENT_PRESERVE_SOCKET(&h2c->zsocket);\n\n    uint32_t stream_id = h2c->send_request(zrequest);\n    if (stream_id == 0) {\n        RETURN_FALSE;\n    } else {\n        RETURN_LONG(stream_id);\n    }\n}\n\nstatic void http2_client_coro_recv(INTERNAL_FUNCTION_PARAMETERS, bool pipeline_read) {\n    Client *h2c = http2_client_coro_get_client(ZEND_THIS);\n    double timeout = 0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    SW_CLIENT_PRESERVE_SOCKET(ZEND_THIS);\n\n    while (true) {\n        if (!h2c->is_available()) {\n            RETURN_FALSE;\n        }\n        if (!h2c->recv_packet(timeout)) {\n            RETURN_FALSE;\n        }\n        ReturnCode ret = h2c->parse_frame(return_value, pipeline_read);\n        if (ret == SW_CONTINUE) {\n            continue;\n        } else if (ret == SW_READY) {\n            break;\n        } else {\n            RETURN_FALSE;\n        }\n    }\n}\n\nstatic PHP_METHOD(swoole_http2_client_coro, recv) {\n    http2_client_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);\n}\n\nstatic PHP_METHOD(swoole_http2_client_coro, __destruct) {}\n\nstatic PHP_METHOD(swoole_http2_client_coro, close) {\n    Client *h2c = http2_client_coro_get_client(ZEND_THIS);\n    SW_CLIENT_PRESERVE_SOCKET(&h2c->zsocket);\n    RETURN_BOOL(h2c->close());\n}\n\nstatic PHP_METHOD(swoole_http2_client_coro, connect) {\n    Client *h2c = http2_client_coro_get_client(ZEND_THIS);\n    RETURN_BOOL(h2c->connect());\n}\n\nstatic sw_inline void http2_client_settings_to_array(const Http2::Settings *settings, zval *zarray) {\n    array_init(zarray);\n    add_assoc_long_ex(zarray, ZEND_STRL(\"header_table_size\"), settings->header_table_size);\n    add_assoc_long_ex(zarray, ZEND_STRL(\"init_window_size\"), settings->init_window_size);\n    add_assoc_long_ex(zarray, ZEND_STRL(\"max_concurrent_streams\"), settings->max_concurrent_streams);\n    add_assoc_long_ex(zarray, ZEND_STRL(\"max_frame_size\"), settings->max_frame_size);\n    add_assoc_long_ex(zarray, ZEND_STRL(\"max_header_list_size\"), settings->max_header_list_size);\n}\n\nstatic PHP_METHOD(swoole_http2_client_coro, stats) {\n    Client *h2c = http2_client_coro_get_client(ZEND_THIS);\n    zval _zarray, *zarray = &_zarray;\n    String key = {};\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_STRING(key.str, key.length);\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (key.length > 0) {\n        if (SW_STREQ(key.str, key.length, \"current_stream_id\")) {\n            RETURN_LONG(h2c->stream_id);\n        } else if (SW_STREQ(key.str, key.length, \"last_stream_id\")) {\n            RETURN_LONG(h2c->last_stream_id);\n        } else if (SW_STREQ(key.str, key.length, \"local_settings\")) {\n            http2_client_settings_to_array(&h2c->local_settings, zarray);\n            RETURN_ZVAL(zarray, 0, 0);\n        } else if (SW_STREQ(key.str, key.length, \"remote_settings\")) {\n            http2_client_settings_to_array(&h2c->remote_settings, zarray);\n            RETURN_ZVAL(zarray, 0, 0);\n        } else if (SW_STREQ(key.str, key.length, \"active_stream_num\")) {\n            RETURN_LONG(h2c->streams.size());\n        }\n    } else {\n        array_init(return_value);\n        add_assoc_long_ex(return_value, ZEND_STRL(\"current_stream_id\"), h2c->stream_id);\n        add_assoc_long_ex(return_value, ZEND_STRL(\"last_stream_id\"), h2c->last_stream_id);\n        http2_client_settings_to_array(&h2c->local_settings, zarray);\n        add_assoc_zval_ex(return_value, ZEND_STRL(\"local_settings\"), zarray);\n        http2_client_settings_to_array(&h2c->remote_settings, zarray);\n        add_assoc_zval_ex(return_value, ZEND_STRL(\"remote_settings\"), zarray);\n        add_assoc_long_ex(return_value, ZEND_STRL(\"active_stream_num\"), h2c->streams.size());\n    }\n}\n\nstatic PHP_METHOD(swoole_http2_client_coro, isStreamExist) {\n    zend_long stream_id = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(stream_id)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (stream_id < 0) {\n        RETURN_FALSE;\n    }\n\n    Client *h2c = http2_client_coro_get_client(ZEND_THIS);\n    if (!h2c->socket_) {\n        RETURN_FALSE;\n    } else if (stream_id == 0) {\n        RETURN_TRUE;\n    }\n    Stream *stream = h2c->get_stream(stream_id);\n    RETURN_BOOL(stream ? 1 : 0);\n}\n\nstatic PHP_METHOD(swoole_http2_client_coro, write) {\n    Client *h2c = http2_client_coro_get_client(ZEND_THIS);\n\n    if (!h2c->is_available()) {\n        RETURN_FALSE;\n    }\n\n    zend_long stream_id;\n    zval *data;\n    zend_bool end = false;\n\n    ZEND_PARSE_PARAMETERS_START(2, 3)\n    Z_PARAM_LONG(stream_id);\n    Z_PARAM_ZVAL(data);\n    Z_PARAM_OPTIONAL\n    Z_PARAM_BOOL(end);\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    SW_CLIENT_PRESERVE_SOCKET(&h2c->zsocket);\n\n    RETURN_BOOL(h2c->write_data(stream_id, data, end));\n}\n\nstatic PHP_METHOD(swoole_http2_client_coro, read) {\n    http2_client_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);\n}\n\nstatic PHP_METHOD(swoole_http2_client_coro, ping) {\n    Client *h2c = http2_client_coro_get_client(ZEND_THIS);\n\n    if (!h2c->is_available()) {\n        RETURN_FALSE;\n    }\n\n    RETURN_BOOL(h2c->send_ping_frame());\n}\n\n/**\n * +-+-------------------------------------------------------------+\n * |R|                  Last-Stream-ID (31)                        |\n * +-+-------------------------------------------------------------+\n * |                      Error Code (32)                          |\n * +---------------------------------------------------------------+\n * |                  Additional Debug Data (*)                    |\n * +---------------------------------------------------------------+\n */\nstatic PHP_METHOD(swoole_http2_client_coro, goaway) {\n    Client *h2c = http2_client_coro_get_client(ZEND_THIS);\n    zend_long error_code = SW_HTTP2_ERROR_NO_ERROR;\n    char *debug_data = nullptr;\n    size_t debug_data_len = 0;\n\n    if (!h2c->is_available()) {\n        RETURN_FALSE;\n    }\n\n    ZEND_PARSE_PARAMETERS_START(0, 2)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(error_code);\n    Z_PARAM_STRING(debug_data, debug_data_len);\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    SW_CLIENT_PRESERVE_SOCKET(&h2c->zsocket);\n    RETURN_BOOL(h2c->send_goaway_frame(error_code, debug_data, debug_data_len));\n}\n"
  },
  {
    "path": "ext-src/swoole_http2_server.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_http_server.h\"\n\n#include <memory>\n#include <sstream>\n\n#include \"swoole_static_handler.h\"\n\n#include \"main/php_variables.h\"\n\nusing namespace swoole;\nusing std::string;\nusing swoole::coroutine::System;\nusing swoole::http2::get_default_setting;\nusing swoole::http_server::StaticHandler;\n\nnamespace Http2 = swoole::http2;\n\nusing HttpContext = swoole::http::Context;\nusing Http2Stream = Http2::Stream;\nusing Http2Session = Http2::Session;\n\nstatic SW_THREAD_LOCAL std::unordered_map<SessionId, std::shared_ptr<Http2Session>> http2_sessions;\nstatic SW_THREAD_LOCAL std::unordered_map<SessionId, zend::Variable> server_ips;\nstatic SW_THREAD_LOCAL std::unordered_map<SessionId, zend::Variable> client_ips;\n\nstatic bool http2_server_respond(HttpContext *ctx, const String *body);\nstatic bool http2_server_send_range_file(HttpContext *ctx, StaticHandler *handler);\nstatic bool http2_server_send_status_code(HttpContext *ctx, int status_code);\n\nHttp2Stream::Stream(const Http2Session *client, uint32_t _id) {\n    ctx = swoole_http_context_new(client->fd);\n    ctx->copy(client->default_ctx);\n    ctx->http2 = true;\n    ctx->stream_id = _id;\n    ctx->keepalive = true;\n    id = _id;\n    local_window_size = client->local_settings.init_window_size;\n    remote_window_size = client->remote_settings.init_window_size;\n}\n\nHttp2Stream::~Stream() {\n    ctx->stream_id = 0;\n    ctx->end_ = true;\n    ctx->free();\n}\n\nvoid Http2Stream::reset(uint32_t error_code) const {\n    char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_RST_STREAM_SIZE];\n    swoole_trace_log(\n        SW_TRACE_HTTP2, \"send [\" SW_ECHO_YELLOW \"] stream_id=%u, error_code=%u\", \"RST_STREAM\", id, error_code);\n    *(uint32_t *) ((char *) frame + SW_HTTP2_FRAME_HEADER_SIZE) = htonl(error_code);\n    set_frame_header(frame, SW_HTTP2_TYPE_RST_STREAM, SW_HTTP2_RST_STREAM_SIZE, 0, id);\n    ctx->send(ctx, frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_RST_STREAM_SIZE);\n}\n\nHttp2Session::Session(SessionId _fd) {\n    fd = _fd;\n    init_settings(&local_settings);\n    // [init]: we must set default value, peer is not always send all the settings\n    init_settings(&remote_settings);\n    local_window_size = local_settings.init_window_size;\n    remote_window_size = remote_settings.init_window_size;\n    last_stream_id = 0;\n    shutting_down = false;\n    is_coro = false;\n    max_body_size = 0;\n}\n\nstd::shared_ptr<Http2Stream> Http2Session::get_stream(uint32_t stream_id) {\n    auto iter = streams.find(stream_id);\n    if (iter == streams.end()) {\n        return {};\n    } else {\n        return iter->second;\n    }\n}\n\nbool Http2Session::remove_stream(uint32_t stream_id) {\n    auto iter = streams.find(stream_id);\n    if (iter == streams.end()) {\n        return false;\n    }\n\n    auto stream = iter->second;\n    streams.erase(iter);\n    return true;\n}\n\nstd::shared_ptr<Http2Stream> Http2Session::create_stream(uint32_t stream_id) {\n    auto stream = std::make_shared<Http2Stream>(this, stream_id);\n    streams.emplace(stream_id, stream);\n    if (sw_unlikely(!stream->ctx)) {\n        swoole_error_log(\n            SW_LOG_WARNING, SW_ERROR_HTTP2_STREAM_NO_HEADER, \"http2 create stream#%d context error\", stream_id);\n        return {};\n    }\n    auto ctx = stream->ctx;\n    zend::object_set(ctx->request.zobject, ZEND_STRL(\"streamId\"), stream_id);\n    return stream;\n}\n\nHttp2Session::~Session() {\n    if (inflater) {\n        nghttp2_hd_inflate_del(inflater);\n    }\n    if (deflater) {\n        nghttp2_hd_deflate_del(deflater);\n    }\n    delete default_ctx;\n}\n\nstatic void http2_server_send_window_update(HttpContext *ctx, uint32_t stream_id, uint32_t size) {\n    char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE];\n    swoole_trace_log(\n        SW_TRACE_HTTP2, \"send [\" SW_ECHO_YELLOW \"] stream_id=%u, size=%u\", \"WINDOW_UPDATE\", stream_id, size);\n    *(uint32_t *) ((char *) frame + SW_HTTP2_FRAME_HEADER_SIZE) = htonl(size);\n    Http2::set_frame_header(frame, SW_HTTP2_TYPE_WINDOW_UPDATE, SW_HTTP2_WINDOW_UPDATE_SIZE, 0, stream_id);\n    ctx->send(ctx, frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE);\n}\n\nstatic ssize_t http2_server_build_trailer(const HttpContext *ctx, uchar *buffer) {\n    zval *ztrailer =\n        sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0);\n    uint32_t size = php_swoole_array_length_safe(ztrailer);\n\n    if (size > 0) {\n        Http2::HeaderSet trailer(size);\n        zend_string *key;\n        zval *zvalue;\n\n        ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(ztrailer), key, zvalue) {\n            if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue))) {\n                continue;\n            }\n            zend::String str_value(zvalue);\n            trailer.add(ZSTR_VAL(key), ZSTR_LEN(key), str_value.val(), str_value.len());\n        }\n        ZEND_HASH_FOREACH_END();\n\n        ssize_t rv;\n        auto client = http2_sessions[ctx->fd];\n        auto deflater = client->deflater;\n\n        if (!deflater) {\n            int ret = nghttp2_hd_deflate_new2(&deflater, client->remote_settings.header_table_size, php_nghttp2_mem());\n            if (ret != 0) {\n                swoole_warning(\"nghttp2_hd_deflate_new2() failed with error: %s\", nghttp2_strerror(ret));\n                return -1;\n            }\n            client->deflater = deflater;\n        }\n\n        size_t buflen = nghttp2_hd_deflate_bound(deflater, trailer.get(), trailer.len());\n#if 0\n        if (buflen > SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE) {\n            php_swoole_error(E_WARNING,\n                             \"header cannot bigger than remote max_header_list_size %u\",\n                             SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE);\n            return -1;\n        }\n#endif\n        rv = nghttp2_hd_deflate_hd(deflater, (uchar *) buffer, buflen, trailer.get(), trailer.len());\n        if (rv < 0) {\n            swoole_warning(\"nghttp2_hd_deflate_hd() failed with error: %s\", nghttp2_strerror((int) rv));\n            return -1;\n        }\n        return rv;\n    }\n    return 0;\n}\n\nstatic bool http2_server_is_static_file(Server *serv, HttpContext *ctx) {\n    zval *zserver = ctx->request.zserver;\n    zval *zrequest_uri = zend_hash_str_find(Z_ARR_P(zserver), ZEND_STRL(\"request_uri\"));\n    if (zrequest_uri && Z_TYPE_P(zrequest_uri) == IS_STRING) {\n        StaticHandler handler(serv, Z_STRVAL_P(zrequest_uri), Z_STRLEN_P(zrequest_uri));\n        if (!handler.try_serve()) {\n            return false;\n        }\n\n        if (handler.status_code == SW_HTTP_NOT_FOUND) {\n            String body(SW_STRL(SW_HTTP_PAGE_404));\n            ctx->response.status = SW_HTTP_NOT_FOUND;\n            http2_server_respond(ctx, &body);\n            return true;\n        }\n\n        /**\n         * if http_index_files is enabled, need to search the index file first.\n         * if the index file is found, set filename to index filename.\n         */\n        if (!handler.try_serve_index_file()) {\n            return false;\n        }\n\n        /**\n         * the index file was not found in the current directory,\n         * if http_autoindex is enabled, should show the list of files in the current directory.\n         */\n        if (!handler.has_index_file() && handler.is_enabled_auto_index() && handler.is_dir()) {\n            String body(PATH_MAX);\n            body.length = handler.make_index_page(&body);\n            http2_server_respond(ctx, &body);\n            return true;\n        }\n\n        auto date_str = handler.get_date();\n        auto date_str_last_modified = handler.get_date_last_modified();\n\n        zval *zheader = ctx->request.zheader;\n        ctx->set_header(ZEND_STRL(\"Last-Modified\"), date_str_last_modified, false);\n\n        zval *zdate_if_modified_since = zend_hash_str_find(Z_ARR_P(zheader), ZEND_STRL(\"if-modified-since\"));\n        if (zdate_if_modified_since) {\n            string date_if_modified_since(Z_STRVAL_P(zdate_if_modified_since), Z_STRLEN_P(zdate_if_modified_since));\n            if (!date_if_modified_since.empty() && handler.is_modified(date_if_modified_since)) {\n                ctx->response.status = SW_HTTP_NOT_MODIFIED;\n                return true;\n            }\n        }\n\n        zval *zrange = zend_hash_str_find(Z_ARR_P(zheader), ZEND_STRL(\"range\"));\n        zval *zif_range = zend_hash_str_find(Z_ARR_P(zheader), ZEND_STRL(\"if-range\"));\n        handler.parse_range(zrange ? Z_STRVAL_P(zrange) : nullptr, zif_range ? Z_STRVAL_P(zif_range) : nullptr);\n        ctx->response.status = handler.status_code;\n        auto tasks = handler.get_tasks();\n        if (1 == tasks.size()) {\n            if (SW_HTTP_PARTIAL_CONTENT == handler.status_code) {\n                std::stringstream content_range;\n                content_range << \"bytes \" << tasks[0].offset << \"-\" << (tasks[0].length + tasks[0].offset - 1) << \"/\"\n                              << handler.get_filesize() << \"\\r\\n\";\n                auto content_range_str = content_range.str();\n                ctx->set_header(ZEND_STRL(\"Content-Range\"), content_range_str, false);\n            } else {\n                ctx->set_header(ZEND_STRL(\"Accept-Ranges\"), SW_STRL(\"bytes\"), false);\n            }\n        }\n\n        ctx->onAfterResponse = nullptr;\n        ctx->onBeforeRequest = nullptr;\n\n        // request_method\n        zval *zrequest_method = zend_hash_str_find(Z_ARR_P(zserver), ZEND_STRL(\"request_method\"));\n        if (zrequest_method && Z_TYPE_P(zrequest_method) == IS_STRING &&\n            SW_STRCASEEQ(Z_STRVAL_P(zrequest_method), Z_STRLEN_P(zrequest_method), \"HEAD\")) {\n            String empty_body;\n            http2_server_respond(ctx, &empty_body);\n            return true;\n        } else {\n            return http2_server_send_range_file(ctx, &handler);\n        }\n    }\n\n    return false;\n}\n\nstatic void http2_server_onRequest(const std::shared_ptr<Http2Session> &client,\n                                   const std::shared_ptr<Http2Stream> &stream) {\n    HttpContext *ctx = stream->ctx;\n    auto serv = ctx->get_async_server();\n    zval args[2];\n    zend::Callable *cb = nullptr;\n\n    Connection *conn = serv->get_connection_by_session_id(ctx->fd);\n    if (!conn) {\n        goto _destroy;\n    }\n\n    ctx->request.version = SW_HTTP_VERSION_2;\n    if (serv->enable_static_handler && http2_server_is_static_file(serv, ctx)) {\n        goto _destroy;\n    }\n\n    do {\n        zval *zserver = ctx->request.zserver;\n        HashTable *ht = Z_ARR_P(zserver);\n        swoole_http_server_populate_ip_and_port(serv, ht, conn, client->fd, ctx->keepalive);\n        http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_TIME), (zend_long) time(nullptr));\n        http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_TIME_FLOAT), microtime());\n        http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_MASTER_TIME), (zend_long) conn->last_recv_time);\n        http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_PROTOCOL), SW_ZSTR_KNOWN(SW_ZEND_STR_HTTP2));\n    } while (false);\n\n    cb = php_swoole_server_get_callback(serv, conn->server_fd, SW_SERVER_CB_onRequest);\n    ctx->private_data_2 = cb;\n\n    if (ctx->onBeforeRequest && !ctx->onBeforeRequest(ctx)) {\n        return;\n    }\n\n    args[0] = *ctx->request.zobject;\n    args[1] = *ctx->response.zobject;\n    if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, serv->is_enable_coroutine()))) {\n        stream->reset(SW_HTTP2_ERROR_INTERNAL_ERROR);\n        php_swoole_error(E_WARNING, \"%s->onRequest[v2] handler error\", ZSTR_VAL(swoole_http_server_ce->name));\n    }\n\n_destroy:\n    zval_ptr_dtor(ctx->request.zobject);\n    zval_ptr_dtor(ctx->response.zobject);\n}\n\nstatic ssize_t http2_server_build_header(HttpContext *ctx, uchar *buffer, const String *body) {\n    zval *zheader =\n        sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER), 0);\n    zval *zcookie =\n        sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE), 0);\n    Http2::HeaderSet headers(32 + php_swoole_array_length_safe(zheader) + php_swoole_array_length_safe(zcookie));\n    char intbuf[2][16];\n\n    assert(ctx->send_header_ == 0);\n\n    // status code\n    if (ctx->response.status == 0) {\n        ctx->response.status = SW_HTTP_OK;\n    }\n    int ret = swoole_itoa(intbuf[0], ctx->response.status);\n    headers.add(ZEND_STRL(\":status\"), intbuf[0], ret);\n\n    uint32_t header_flags = 0x0;\n\n    // headers\n    if (ZVAL_IS_ARRAY(zheader)) {\n        const char *key;\n        uint32_t keylen;\n        zval *zvalue;\n        int type;\n\n        zend_string *content_type = nullptr;\n        auto add_header =\n            [ctx, &content_type](\n                Http2::HeaderSet &headers, const char *key, size_t l_key, zval *value, uint32_t &header_flags) {\n                if (ZVAL_IS_NULL(value)) {\n                    return;\n                }\n                zend::String str_value(value);\n                str_value.rtrim();\n                if (swoole_http_has_crlf(str_value.val(), str_value.len())) {\n                    return;\n                }\n                if (SW_STRCASEEQ(key, l_key, \"server\")) {\n                    header_flags |= HTTP_HEADER_SERVER;\n                } else if (SW_STRCASEEQ(key, l_key, \"content-length\")) {\n                    return;  // ignore\n                } else if (SW_STRCASEEQ(key, l_key, \"date\")) {\n                    header_flags |= HTTP_HEADER_DATE;\n                } else if (SW_STRCASEEQ(key, l_key, \"content-type\")) {\n                    header_flags |= HTTP_HEADER_CONTENT_TYPE;\n#ifdef SW_HAVE_COMPRESSION\n                    if (ctx->accept_compression && ctx->compression_types) {\n                        content_type = zval_get_string(value);\n                    }\n#endif\n                }\n                headers.add(key, l_key, str_value.val(), str_value.len());\n            };\n\n        SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zheader), key, keylen, type, zvalue) {\n            if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue))) {\n                continue;\n            }\n            if (ZVAL_IS_ARRAY(zvalue)) {\n                zval *zvalue_2;\n                SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(zvalue), zvalue_2) {\n                    add_header(headers, key, keylen, zvalue_2, header_flags);\n                }\n                SW_HASHTABLE_FOREACH_END();\n            } else {\n                add_header(headers, key, keylen, zvalue, header_flags);\n            }\n        }\n        SW_HASHTABLE_FOREACH_END();\n        (void) type;\n\n#ifdef SW_HAVE_COMPRESSION\n        if (ctx->accept_compression && ctx->compression_types) {\n            std::string str_content_type = content_type ? std::string(ZSTR_VAL(content_type), ZSTR_LEN(content_type))\n                                                        : std::string(ZEND_STRL(SW_HTTP_DEFAULT_CONTENT_TYPE));\n            ctx->accept_compression = ctx->compression_types->find(str_content_type) != ctx->compression_types->end();\n            if (content_type) {\n                zend_string_release(content_type);\n            }\n        }\n#endif\n    }\n\n    if (!(header_flags & HTTP_HEADER_SERVER)) {\n        headers.add(ZEND_STRL(\"server\"), ZEND_STRL(SW_HTTP_SERVER_SOFTWARE));\n    }\n    if (!(header_flags & HTTP_HEADER_DATE)) {\n        auto date_str = php_swoole_http_get_date();\n        headers.add(ZEND_STRL(\"date\"), ZSTR_VAL(date_str), ZSTR_LEN(date_str));\n    }\n    if (!(header_flags & HTTP_HEADER_CONTENT_TYPE)) {\n        headers.add(ZEND_STRL(\"content-type\"), ZEND_STRL(\"text/html\"));\n    }\n\n    // cookies\n    if (ZVAL_IS_ARRAY(zcookie)) {\n        zval *zvalue;\n        SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(zcookie), zvalue) {\n            if (Z_TYPE_P(zvalue) != IS_STRING) {\n                continue;\n            }\n            headers.add(ZEND_STRL(\"set-cookie\"), Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue));\n        }\n        SW_HASHTABLE_FOREACH_END();\n    }\n\n    if (body) {\n        size_t content_length = body->length;\n        // content length\n#ifdef SW_HAVE_COMPRESSION\n        if (ctx->compress(body->str, body->length)) {\n            content_length = ctx->zlib_buffer->length;\n            // content encoding\n            const char *content_encoding = ctx->get_content_encoding();\n            headers.add(ZEND_STRL(\"content-encoding\"), (char *) content_encoding, strlen(content_encoding));\n        }\n#endif\n        ret = swoole_itoa(intbuf[1], content_length);\n        headers.add(ZEND_STRL(\"content-length\"), intbuf[1], ret);\n    }\n\n    auto client = http2_sessions[ctx->fd];\n    auto deflater = client->deflater;\n    if (!deflater) {\n        ret = nghttp2_hd_deflate_new2(&deflater, client->remote_settings.header_table_size, php_nghttp2_mem());\n        if (ret != 0) {\n            swoole_warning(\"nghttp2_hd_deflate_new2() failed with error: %s\", nghttp2_strerror(ret));\n            return -1;\n        }\n        client->deflater = deflater;\n    }\n\n    size_t buflen = nghttp2_hd_deflate_bound(deflater, headers.get(), headers.len());\n    /*\n    if (buflen > SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE)\n    {\n        php_swoole_error(E_WARNING, \"header cannot bigger than remote max_header_list_size %u\",\n    SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE); return -1;\n    }\n    */\n    ssize_t rv = nghttp2_hd_deflate_hd(deflater, (uchar *) buffer, buflen, headers.get(), headers.len());\n    if (rv < 0) {\n        swoole_warning(\"nghttp2_hd_deflate_hd() failed with error: %s\", nghttp2_strerror((int) rv));\n        return -1;\n    }\n\n    ctx->send_header_ = 1;\n    return rv;\n}\n\nbool swoole_http2_server_ping(HttpContext *ctx) {\n    char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE];\n    Http2::set_frame_header(frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_NONE, 0);\n    return ctx->send(ctx, frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE);\n}\n\nstatic bool http2_server_send_setting_ack(HttpContext *ctx) {\n    char frame[SW_HTTP2_FRAME_HEADER_SIZE];\n    Http2::set_frame_header(frame, SW_HTTP2_TYPE_SETTINGS, 0, SW_HTTP2_FLAG_ACK, 0);\n    return ctx->send(ctx, frame, SW_HTTP2_FRAME_HEADER_SIZE);\n}\n\nstatic bool http2_server_send_rst_stream(HttpContext *ctx, int error_code) {\n    char frame[SW_HTTP2_FRAME_HEADER_SIZE + sizeof(uint32_t)];\n    Http2::set_frame_header(frame, SW_HTTP2_TYPE_RST_STREAM, 0, SW_HTTP2_FLAG_ACK, 0);\n    *(uint32_t *) (frame + SW_HTTP2_FRAME_HEADER_SIZE) = htonl(error_code);\n    return ctx->send(ctx, frame, sizeof(frame));\n}\n\nbool swoole_http2_server_goaway(HttpContext *ctx, zend_long error_code, zend_string *sdata) {\n    const char *debug_data = sdata ? ZSTR_VAL(sdata) : nullptr;\n    size_t debug_data_len = sdata ? ZSTR_LEN(sdata) : 0;\n    size_t length = SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_GOAWAY_SIZE + debug_data_len;\n    char *frame = (char *) ecalloc(1, length);\n    auto client = http2_sessions[ctx->fd];\n    uint32_t last_stream_id = client->last_stream_id;\n    Http2::set_frame_header(frame, SW_HTTP2_TYPE_GOAWAY, SW_HTTP2_GOAWAY_SIZE + debug_data_len, error_code, 0);\n    *(uint32_t *) (frame + SW_HTTP2_FRAME_HEADER_SIZE) = htonl(last_stream_id);\n    *(uint32_t *) (frame + SW_HTTP2_FRAME_HEADER_SIZE + 4) = htonl(error_code);\n    if (debug_data_len > 0) {\n        memcpy(frame + SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_GOAWAY_SIZE, debug_data, debug_data_len);\n    }\n    bool rv = ctx->send(ctx, frame, length);\n    efree(frame);\n    client->shutting_down = true;\n    return rv;\n}\n\nbool Http2Stream::send_header(const String *body, bool end_stream) const {\n    char header_buffer[SW_BUFFER_SIZE_STD];\n    ssize_t bytes = http2_server_build_header(ctx, (uchar *) header_buffer, body);\n    if (bytes < 0) {\n        return false;\n    }\n\n    String *http_buffer = ctx->get_write_buffer();\n    http_buffer->clear();\n\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    char frame_header[SW_HTTP2_FRAME_HEADER_SIZE];\n\n    if (end_stream && (!body || body->length == 0)) {\n        http2::set_frame_header(\n            frame_header, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS | SW_HTTP2_FLAG_END_STREAM, id);\n        ctx->end_ = 1;\n    } else {\n        http2::set_frame_header(frame_header, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS, id);\n    }\n\n    http_buffer->append(frame_header, SW_HTTP2_FRAME_HEADER_SIZE);\n    http_buffer->append(header_buffer, bytes);\n\n    if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) {\n        ctx->send_header_ = 0;\n        return false;\n    }\n\n    return true;\n}\n\nbool Http2Stream::send_end_stream_data_frame() const {\n    char frame_header[SW_HTTP2_FRAME_HEADER_SIZE];\n    http2::set_frame_header(frame_header, SW_HTTP2_TYPE_DATA, 0, SW_HTTP2_FLAG_END_STREAM, id);\n    return ctx->send(ctx, frame_header, SW_HTTP2_FRAME_HEADER_SIZE);\n}\n\nbool Http2Stream::send_body(\n    const String *body, bool end_stream, const std::shared_ptr<Http2Session> &session, off_t offset, size_t length) {\n    char frame_header[SW_HTTP2_FRAME_HEADER_SIZE];\n    char *p = body->str + offset;\n    size_t l = length == 0 ? body->length : length;\n\n    int flags = end_stream ? SW_HTTP2_FLAG_END_STREAM : SW_HTTP2_FLAG_NONE;\n    String *http_buffer = ctx->get_write_buffer();\n    auto max_frame_size = session->local_settings.max_frame_size;\n\n    while (l > 0) {\n        size_t send_n;\n        int _send_flags;\n        if (l > max_frame_size) {\n            send_n = max_frame_size;\n            _send_flags = 0;\n        } else {\n            send_n = l;\n            _send_flags = flags;\n        }\n\n        if (send_n > remote_window_size) {\n            // The coroutine server receives HTTP2 frames serially and cannot be suspended,\n            // therefore it does not support flow control\n            // TODO: The coroutine http server also needs to support HTTP2 flow control\n            if (ctx->is_co_socket() || !swoole_coroutine_is_in()) {\n                swoole_warning(\"The data sent exceeded remote_window_size\");\n            } else {\n                if (remote_window_size == 0) {\n                    waiting_coroutine = Coroutine::get_current();\n                    waiting_coroutine->yield();\n                    waiting_coroutine = nullptr;\n                    continue;\n                } else {\n                    send_n = remote_window_size;\n                    _send_flags = 0;\n                }\n            }\n        }\n\n        http2::set_frame_header(frame_header, SW_HTTP2_TYPE_DATA, send_n, _send_flags, id);\n\n        // send twice to reduce memory copy\n        if (send_n < swoole_pagesize()) {\n            http_buffer->clear();\n            http_buffer->append(frame_header, SW_HTTP2_FRAME_HEADER_SIZE);\n            http_buffer->append(p, send_n);\n            if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) {\n                return false;\n            }\n        } else {\n            if (!ctx->send(ctx, frame_header, SW_HTTP2_FRAME_HEADER_SIZE)) {\n                return false;\n            }\n            if (!ctx->send(ctx, p, send_n)) {\n                return false;\n            }\n        }\n\n        swoole_trace_log(\n            SW_TRACE_HTTP2, \"send [\" SW_ECHO_YELLOW \"] stream_id=%u, flags=%d, send_n=%lu\", \"DATA\", id, flags, send_n);\n\n        l -= send_n;\n        p += send_n;\n\n        remote_window_size -= send_n;\n        session->remote_window_size -= send_n;\n    }\n\n    return true;\n}\n\nbool Http2Stream::send_trailer() const {\n    char header_buffer[SW_BUFFER_SIZE_STD] = {};\n    char frame_header[SW_HTTP2_FRAME_HEADER_SIZE];\n    String *http_buffer = ctx->get_write_buffer();\n\n    http_buffer->clear();\n    ssize_t bytes = http2_server_build_trailer(ctx, (uchar *) header_buffer);\n    if (bytes > 0) {\n        http2::set_frame_header(\n            frame_header, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS | SW_HTTP2_FLAG_END_STREAM, id);\n        http_buffer->append(frame_header, SW_HTTP2_FRAME_HEADER_SIZE);\n        http_buffer->append(header_buffer, bytes);\n        if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) {\n            return false;\n        }\n    }\n\n    return true;\n}\n\nbool swoole_http2_server_end(HttpContext *ctx, zend_string *sdata) {\n    String http_body = {};\n    if (sdata) {\n        http_body.length = ZSTR_LEN(sdata);\n        http_body.str = ZSTR_VAL(sdata);\n    } else {\n        http_body.length = 0;\n        http_body.str = nullptr;\n    }\n    return http2_server_respond(ctx, &http_body);\n}\n\nbool swoole_http2_server_write(HttpContext *ctx, zend_string *sdata) {\n    String chunk = {};\n    chunk.length = ZSTR_LEN(sdata);\n    chunk.str = ZSTR_VAL(sdata);\n    if (chunk.length == 0) {\n        php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, \"the data sent must not be empty\");\n        return false;\n    }\n\n    auto client = http2_sessions[ctx->fd];\n    auto stream = client->get_stream(ctx->stream_id);\n\n    ctx->send_chunked = 1;\n\n    if (!ctx->send_header_ && !stream->send_header(nullptr, false)) {\n        return false;\n    }\n\n    if (!stream->send_body(&chunk, false, client)) {\n        return false;\n    }\n\n    return true;\n}\n\nstatic bool http2_server_respond(HttpContext *ctx, const String *body) {\n    auto client = http2_sessions[ctx->fd];\n    auto stream = client->get_stream(ctx->stream_id);\n\n    zval *ztrailer =\n        sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0);\n    if (php_swoole_array_length_safe(ztrailer) == 0) {\n        ztrailer = nullptr;\n    }\n\n    bool end_stream = (ztrailer == nullptr);\n\n    if (!ctx->send_header_ && !stream->send_header(body, end_stream)) {\n        return false;\n    }\n\n    // The headers have already been sent, retries are no longer allowed (even if send body failed)\n    ctx->end_ = 1;\n\n    bool error = true;\n\n#ifdef SW_HAVE_COMPRESSION\n    if (ctx->content_compressed) {\n        body = ctx->zlib_buffer.get();\n    }\n#endif\n\n    SW_LOOP {\n        if (ctx->send_chunked && body->length == 0 && !stream->send_end_stream_data_frame()) {\n            break;\n        } else if (!stream->send_body(body, end_stream, client)) {\n            break;\n        } else if (ztrailer && !stream->send_trailer()) {\n            break;\n        }\n        error = false;\n        break;\n    }\n\n    if (error) {\n        ctx->close(ctx);\n    } else {\n        client->remove_stream(stream->id);\n    }\n\n    if (client->shutting_down && client->streams.empty()) {\n        ctx->close(ctx);\n    }\n\n    return !error;\n}\n\nstatic bool http2_server_send_status_code(HttpContext *ctx, int status_code) {\n    auto client = http2_sessions[ctx->fd];\n    auto stream = client->get_stream(ctx->stream_id);\n    ctx->response.status = status_code;\n    return stream->send_header(nullptr, true);\n}\n\nstatic bool http2_server_send_range_file(HttpContext *ctx, StaticHandler *handler) {\n    auto client = http2_sessions[ctx->fd];\n    auto stream = client->get_stream(ctx->stream_id);\n\n#ifdef SW_HAVE_COMPRESSION\n    ctx->accept_compression = 0;\n#endif\n    bool error = false;\n    zval *ztrailer =\n        sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0);\n    if (php_swoole_array_length_safe(ztrailer) == 0) {\n        ztrailer = nullptr;\n    }\n    zval *zheader =\n        sw_zend_read_and_convert_property_array(swoole_http_response_ce, ctx->response.zobject, ZEND_STRL(\"header\"), 0);\n    if (!zend_hash_str_exists(Z_ARRVAL_P(zheader), ZEND_STRL(\"content-type\"))) {\n        ctx->set_header(ZEND_STRL(\"content-type\"), handler->get_content_type(), false);\n    }\n\n    bool end_stream = (ztrailer == nullptr);\n    auto body = std::make_shared<String>();\n    body->length = handler->get_content_length();\n    if (!stream->send_header(body.get(), end_stream)) {\n        return false;\n    }\n\n    /* headers has already been sent, retries are no longer allowed (even if send body failed) */\n    ctx->end_ = 1;\n\n    auto tasks = handler->get_tasks();\n    if (!tasks.empty()) {\n        File fp(handler->get_filename(), O_RDONLY);\n        if (!fp.ready()) {\n            return false;\n        }\n\n        char *buf;\n        if (tasks.size() > 1) {\n            for (auto i = tasks.begin(); i != tasks.end(); ++i) {\n                body = std::make_shared<String>(i->part_header, strlen(i->part_header));\n                if (!stream->send_body(body.get(), false, client)) {\n                    error = true;\n                    break;\n                }\n\n                fp.set_offset(i->offset);\n                buf = (char *) emalloc(i->length);\n                auto n_reads = fp.read(buf, i->length);\n                if (n_reads < 0) {\n                    efree(buf);\n                    return false;\n                }\n                body = std::make_shared<String>(buf, i->length);\n                efree(buf);\n                if (!stream->send_body(body.get(), false, client)) {\n                    error = true;\n                    break;\n                }\n            }\n\n            if (!error) {\n                body = std::make_shared<String>(handler->get_end_part());\n                if (!stream->send_body(body.get(), end_stream, client)) {\n                    error = true;\n                }\n            }\n        } else if (tasks[0].length > 0) {\n            auto callback = [&]() -> bool {\n                fp.set_offset(tasks[0].offset);\n                buf = (char *) emalloc(tasks[0].length);\n                auto n_reads = fp.read(buf, tasks[0].length);\n                if (n_reads < 0) {\n                    efree(buf);\n                    return false;\n                }\n                body = std::make_shared<String>(buf, n_reads);\n                efree(buf);\n                return true;\n            };\n            if (swoole_coroutine_is_in()) {\n                if (!swoole::coroutine::async(callback)) {\n                    return false;\n                }\n            } else {\n                if (!callback()) {\n                    return false;\n                }\n            }\n            if (!stream->send_body(body.get(), end_stream, client)) {\n                error = true;\n            }\n        }\n    }\n\n    if (!error && ztrailer) {\n        if (!stream->send_trailer()) {\n            error = true;\n        }\n    }\n\n    if (error) {\n        ctx->close(ctx);\n    } else {\n        client->remove_stream(ctx->stream_id);\n    }\n\n    return true;\n}\n\nbool swoole_http2_server_send_file(HttpContext *ctx, zend_string *file, off_t offset, size_t length) {\n    auto client = http2_sessions[ctx->fd];\n    auto stream = client->get_stream(ctx->stream_id);\n    std::shared_ptr<String> body;\n\n#ifdef SW_HAVE_COMPRESSION\n    ctx->accept_compression = 0;\n#endif\n    if (swoole_coroutine_is_in()) {\n        body = System::read_file(ZSTR_VAL(file), false);\n        if (!body) {\n            return false;\n        }\n    } else {\n        File fp(ZSTR_VAL(file), O_RDONLY);\n        if (!fp.ready()) {\n            return false;\n        }\n        body = fp.read_content();\n    }\n    body->length = SW_MIN(length, body->length);\n\n    zval *ztrailer =\n        sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0);\n    if (php_swoole_array_length_safe(ztrailer) == 0) {\n        ztrailer = nullptr;\n    }\n\n    zval *zheader =\n        sw_zend_read_and_convert_property_array(swoole_http_response_ce, ctx->response.zobject, ZEND_STRL(\"header\"), 0);\n    if (!zend_hash_str_exists(Z_ARRVAL_P(zheader), ZEND_STRL(\"content-type\"))) {\n        ctx->set_header(ZEND_STRL(\"content-type\"), swoole::mime_type::get({ZSTR_VAL(file), ZSTR_LEN(file)}), false);\n    }\n\n    bool end_stream = (ztrailer == nullptr);\n    if (!stream->send_header(body.get(), end_stream)) {\n        return false;\n    }\n\n    /* headers has already been sent, retries are no longer allowed (even if send body failed) */\n    ctx->end_ = 1;\n\n    bool error = false;\n\n    if (body->length > 0) {\n        if (!stream->send_body(body.get(), end_stream, client, offset, length)) {\n            error = true;\n        }\n    }\n\n    if (!error && ztrailer) {\n        if (!stream->send_trailer()) {\n            error = true;\n        }\n    }\n\n    if (error) {\n        ctx->close(ctx);\n    } else {\n        client->remove_stream(stream->id);\n    }\n\n    return true;\n}\n\nstatic bool http2_server_onBeforeRequest(HttpContext *ctx) {\n    auto serv = ctx->get_async_server();\n    if (serv->is_unavailable()) {\n        String null_body{};\n        ctx->response.status = SW_HTTP_SERVICE_UNAVAILABLE;\n        http2_server_respond(ctx, &null_body);\n        zval_ptr_dtor(ctx->request.zobject);\n        zval_ptr_dtor(ctx->response.zobject);\n        return false;\n    }\n    return swoole_http_server_onBeforeRequest(ctx);\n}\n\nstatic int http2_server_parse_header(\n    const std::shared_ptr<Http2Session> &client, HttpContext *ctx, int flags, const char *in, size_t inlen) {\n    nghttp2_hd_inflater *inflater = client->inflater;\n\n    if (!inflater) {\n        int ret = nghttp2_hd_inflate_new2(&inflater, php_nghttp2_mem());\n        if (ret != 0) {\n            swoole_warning(\"nghttp2_hd_inflate_new2() failed, Error: %s[%d]\", nghttp2_strerror(ret), ret);\n            return SW_ERR;\n        }\n        client->inflater = inflater;\n    }\n\n    if (flags & SW_HTTP2_FLAG_PRIORITY) {\n        // int stream_deps = ntohl(*(int *) (in));\n        // uint8_t weight = in[4];\n        in += 5;\n        inlen -= 5;\n    }\n\n    zval *zheader = ctx->request.zheader;\n    zval *zserver = ctx->request.zserver;\n\n    for (;;) {\n        nghttp2_nv nv;\n        int inflate_flags = 0;\n\n        ssize_t rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, (uchar *) in, inlen, 1);\n        if (rv < 0) {\n            swoole_warning(\"inflate failed, Error: %s[%zd]\", nghttp2_strerror(rv), rv);\n            return SW_ERR;\n        }\n\n        auto proclen = (size_t) rv;\n\n        in += proclen;\n        inlen -= proclen;\n\n        if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {\n            swoole_trace_log(SW_TRACE_HTTP2,\n                             \"name=(%zu)[\" SW_ECHO_BLUE \"], value=(%zu)[\" SW_ECHO_CYAN \"]\",\n                             nv.namelen,\n                             nv.name,\n                             nv.valuelen,\n                             nv.value);\n\n            if (nv.name[0] == ':') {\n                if (SW_STRCASEEQ((char *) nv.name + 1, nv.namelen - 1, \"method\")) {\n                    add_assoc_stringl_ex(zserver, ZEND_STRL(\"request_method\"), (char *) nv.value, nv.valuelen);\n                } else if (SW_STRCASEEQ((char *) nv.name + 1, nv.namelen - 1, \"path\")) {\n                    char *pathbuf = sw_tg_buffer()->str;\n                    char *v_str = strchr((char *) nv.value, '?');\n                    zend_string *zstr_path;\n                    if (v_str) {\n                        v_str++;\n                        int k_len = v_str - (char *) nv.value - 1;\n                        int v_len = nv.valuelen - k_len - 1;\n                        memcpy(pathbuf, nv.value, k_len);\n                        pathbuf[k_len] = 0;\n                        add_assoc_stringl_ex(zserver, ZEND_STRL(\"query_string\"), v_str, v_len);\n                        zstr_path = zend_string_init(pathbuf, k_len, false);\n                        // parse url params\n                        sapi_module.treat_data(\n                            PARSE_STRING,\n                            estrndup(v_str, v_len),  // it will be freed by treat_data\n                            swoole_http_init_and_read_property(\n                                swoole_http_request_ce, ctx->request.zobject, &ctx->request.zget, ZEND_STRL(\"get\")));\n                    } else {\n                        zstr_path = zend_string_init((char *) nv.value, nv.valuelen, false);\n                    }\n                    ctx->request.path = estrndup((char *) nv.value, nv.valuelen);\n                    ctx->request.path_len = nv.valuelen;\n                    add_assoc_str_ex(zserver, ZEND_STRL(\"request_uri\"), zstr_path);\n                    // path_info should be decoded\n                    zstr_path = zend_string_dup(zstr_path, false);\n                    ZSTR_LEN(zstr_path) = php_url_decode(ZSTR_VAL(zstr_path), ZSTR_LEN(zstr_path));\n                    add_assoc_str_ex(zserver, ZEND_STRL(\"path_info\"), zstr_path);\n                } else if (SW_STRCASEEQ((char *) nv.name + 1, nv.namelen - 1, \"authority\")) {\n                    add_assoc_stringl_ex(zheader, ZEND_STRL(\"host\"), (char *) nv.value, nv.valuelen);\n                }\n            } else {\n                if (SW_STRCASEEQ((char *) nv.name, nv.namelen, \"content-type\")) {\n                    if (SW_STR_ISTARTS_WITH((char *) nv.value, nv.valuelen, \"application/x-www-form-urlencoded\")) {\n                        ctx->request.post_form_urlencoded = 1;\n                    } else if (SW_STR_ISTARTS_WITH((char *) nv.value, nv.valuelen, \"multipart/form-data\")) {\n                        size_t offset = sizeof(\"multipart/form-data\") - 1;\n                        char *boundary_str;\n                        int boundary_len;\n                        if (!ctx->get_multipart_boundary(\n                                (char *) nv.value, nv.valuelen, offset, &boundary_str, &boundary_len)) {\n                            return SW_ERR;\n                        }\n                        ctx->init_multipart_parser(boundary_str, boundary_len);\n                        ctx->parser.data = ctx;\n                    }\n                } else if (SW_STRCASEEQ((char *) nv.name, nv.namelen, \"cookie\")) {\n                    swoole_http_parse_cookie(\n                        swoole_http_init_and_read_property(\n                            swoole_http_request_ce, ctx->request.zobject, &ctx->request.zcookie, ZEND_STRL(\"cookie\")),\n                        (const char *) nv.value,\n                        nv.valuelen);\n                    continue;\n                } else if (SW_STRCASEEQ((char *) nv.name, nv.namelen, \"content-length\")) {\n                    char *end;\n                    zend_long content_length = std::strtol((char *) nv.value, &end, 10);\n                    if (end != (char *) nv.value + nv.valuelen || content_length > client->max_body_size) {\n                        http2_server_send_status_code(ctx, SW_HTTP_REQUEST_ENTITY_TOO_LARGE);\n                    }\n                }\n#ifdef SW_HAVE_COMPRESSION\n                else if (ctx->enable_compression && SW_STRCASEEQ((char *) nv.name, nv.namelen, \"accept-encoding\")) {\n                    ctx->set_compression_method((char *) nv.value, nv.valuelen);\n                }\n#endif\n                add_assoc_stringl_ex(zheader, (char *) nv.name, nv.namelen, (char *) nv.value, nv.valuelen);\n            }\n        }\n\n        if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {\n            nghttp2_hd_inflate_end_headers(inflater);\n            break;\n        }\n\n        if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) {\n            break;\n        }\n    }\n\n    return SW_OK;\n}\n\nint swoole_http2_server_parse(const std::shared_ptr<Http2Session> &client, const char *buf) {\n    int type = buf[3];\n    int flags = buf[4];\n\n    uint32_t stream_id = ntohl((*(int *) (buf + 5))) & 0x7fffffff;\n\n    if (stream_id > client->last_stream_id) {\n        client->last_stream_id = stream_id;\n    }\n\n    if (client->shutting_down) {\n        swoole_error_log(\n            SW_LOG_WARNING, SW_ERROR_HTTP2_STREAM_IGNORE, \"ignore http2 stream#%d after sending goaway\", stream_id);\n        return SW_ERR;\n    }\n\n    ssize_t length = Http2::get_length(buf);\n    buf += SW_HTTP2_FRAME_HEADER_SIZE;\n\n    uint32_t value = 0;\n\n    switch (type) {\n    case SW_HTTP2_TYPE_SETTINGS: {\n        uint16_t id = 0;\n        if (flags & SW_HTTP2_FLAG_ACK) {\n            swoole_http2_frame_trace_log(\"ACK\");\n            break;\n        }\n\n        while (length > 0) {\n            id = ntohs(*(uint16_t *) (buf));\n            value = ntohl(*(uint32_t *) (buf + sizeof(uint16_t)));\n            swoole_http2_frame_trace_log(\"id=%d, value=%d\", id, value);\n            switch (id) {\n            case SW_HTTP2_SETTING_HEADER_TABLE_SIZE:\n                if (value != client->remote_settings.header_table_size) {\n                    client->remote_settings.header_table_size = value;\n                    if (client->deflater) {\n                        int ret = nghttp2_hd_deflate_change_table_size(client->deflater, value);\n                        if (ret != 0) {\n                            swoole_warning(\"nghttp2_hd_deflate_change_table_size() failed, errno=%d, errmsg=%s\",\n                                           ret,\n                                           nghttp2_strerror(ret));\n                            return SW_ERR;\n                        }\n                    }\n                }\n                swoole_trace_log(SW_TRACE_HTTP2, \"setting: header_table_size=%u\", value);\n                break;\n            case SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:\n                client->remote_settings.max_concurrent_streams = value;\n                swoole_trace_log(SW_TRACE_HTTP2, \"setting: max_concurrent_streams=%u\", value);\n                break;\n            case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE:\n                client->remote_window_size = client->remote_settings.init_window_size = value;\n                swoole_trace_log(SW_TRACE_HTTP2, \"setting: init_window_size=%u\", value);\n                break;\n            case SW_HTTP2_SETTINGS_MAX_FRAME_SIZE:\n                client->remote_settings.max_frame_size = value;\n                swoole_trace_log(SW_TRACE_HTTP2, \"setting: max_frame_size=%u\", value);\n                break;\n            case SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:\n                client->remote_settings.max_header_list_size = value;  // useless now\n                swoole_trace_log(SW_TRACE_HTTP2, \"setting: max_header_list_size=%u\", value);\n                break;\n            default:\n                // disable warning and ignore it because some websites are not following http2 protocol totally\n                // swoole_warning(\"unknown option[%d]: %d\", id, value);\n                break;\n            }\n            buf += sizeof(id) + sizeof(value);\n            length -= sizeof(id) + sizeof(value);\n        }\n        // After receiving the setting frame sent by the client, it is necessary to reply with an ACK,\n        // otherwise the client will assume that the server has not applied the setting\n        http2_server_send_setting_ack(client->default_ctx);\n        break;\n    }\n    case SW_HTTP2_TYPE_HEADERS: {\n        auto stream = client->get_stream(stream_id);\n        swoole_http2_frame_trace_log(\"%s\", (stream ? \"exist stream\" : \"new stream\"));\n        if (!stream) {\n            stream = client->create_stream(stream_id);\n            if (!stream) {\n                return SW_ERR;\n            }\n        }\n        HttpContext *ctx = stream->ctx;\n        if (http2_server_parse_header(client, ctx, flags, buf, length) < 0) {\n            return SW_ERR;\n        }\n\n        if (flags & SW_HTTP2_FLAG_END_STREAM) {\n            client->handle(client, stream);\n        } else {\n            // need continue frame\n        }\n        break;\n    }\n    case SW_HTTP2_TYPE_DATA: {\n        swoole_http2_frame_trace_log(\"data\");\n        auto stream = client->get_stream(stream_id);\n        if (!stream) {\n            swoole_error_log(SW_LOG_WARNING, SW_ERROR_HTTP2_STREAM_NOT_FOUND, \"http2 stream#%d not found\", stream_id);\n            return SW_ERR;\n        }\n\n        HttpContext *ctx = stream->ctx;\n\n        if (length > 0) {\n            auto buffer = ctx->get_http2_data_buffer();\n\n            // Exceeded the max_body_size, or if the stream has ended, sends RST_STREAM frame, stops receiving data\n            if (buffer->length + length > client->max_body_size || ctx->end_) {\n                http2_server_send_rst_stream(ctx, 0);\n                client->remove_stream(stream_id);\n                break;\n            }\n\n            buffer->append(buf, length);\n\n            // flow control\n            client->local_window_size -= length;\n            stream->local_window_size -= length;\n\n            if (client->local_window_size < (client->local_settings.init_window_size / 4)) {\n                http2_server_send_window_update(\n                    ctx, 0, client->local_settings.init_window_size - client->local_window_size);\n                client->local_window_size = client->local_settings.init_window_size;\n            }\n            if (stream->local_window_size < (client->local_settings.init_window_size / 4)) {\n                http2_server_send_window_update(\n                    ctx, stream_id, client->local_settings.init_window_size - stream->local_window_size);\n                stream->local_window_size = client->local_settings.init_window_size;\n            }\n        }\n\n        if (flags & SW_HTTP2_FLAG_END_STREAM) {\n            if (ctx->get_http2_data_length() > 0) {\n                auto buffer = ctx->get_http2_data_buffer();\n                if (ctx->parse_body && ctx->request.post_form_urlencoded) {\n                    auto post_prop = swoole_http_init_and_read_property(\n                        swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, ZEND_STRL(\"post\"));\n                    // it will be freed by sapi_module.treat_data()\n                    auto post_str = estrndup(buffer->str, buffer->length);\n                    sapi_module.treat_data(PARSE_STRING, post_str, post_prop);\n                } else if (ctx->mt_parser != nullptr) {\n                    if (!ctx->parse_multipart_data(buffer->str, buffer->length)) {\n                        return SW_ERR;\n                    }\n                }\n            }\n            client->handle(client, stream);\n        }\n        break;\n    }\n    case SW_HTTP2_TYPE_PING: {\n        swoole_http2_frame_trace_log(\"ping\");\n        if (!(flags & SW_HTTP2_FLAG_ACK)) {\n            char ping_frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE];\n            Http2::set_frame_header(\n                ping_frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_ACK, stream_id);\n            memcpy(ping_frame + SW_HTTP2_FRAME_HEADER_SIZE, buf, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE);\n            client->default_ctx->send(\n                client->default_ctx, ping_frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE);\n        }\n        break;\n    }\n    case SW_HTTP2_TYPE_WINDOW_UPDATE: {\n        value = ntohl(*(uint32_t *) buf);\n        if (stream_id == 0) {\n            client->remote_window_size += value;\n        } else {\n            auto stream = client->get_stream(stream_id);\n            if (stream) {\n                stream->remote_window_size += value;\n                if (!client->is_coro && stream->waiting_coroutine) {\n                    stream->waiting_coroutine->resume();\n                }\n            }\n        }\n        swoole_http2_frame_trace_log(\"window_size_increment=%d\", value);\n        break;\n    }\n    case SW_HTTP2_TYPE_RST_STREAM: {\n        value = ntohl(*(int *) (buf));\n        swoole_http2_frame_trace_log(\"error_code=%d\", value);\n        client->remove_stream(stream_id);\n        break;\n    }\n    case SW_HTTP2_TYPE_GOAWAY: {\n        uint32_t server_last_stream_id = ntohl(*(uint32_t *) (buf));\n        buf += 4;\n        value = ntohl(*(uint32_t *) (buf));\n        buf += 4;\n        swoole_http2_frame_trace_log(\"last_stream_id=%d, error_code=%d, opaque_data=[%.*s]\",\n                                     server_last_stream_id,\n                                     value,\n                                     (int) (length - SW_HTTP2_GOAWAY_SIZE),\n                                     buf);\n        // TODO: onRequest\n        (void) server_last_stream_id;\n\n        break;\n    }\n    default: {\n        swoole_http2_frame_trace_log(\"\");\n    }\n    }\n\n    return SW_OK;\n}\n\nint swoole_http2_server_onReceive(Server *serv, Connection *conn, RecvData *req) {\n    SessionId session_id = req->info.fd;\n    auto iter = http2_sessions.find(session_id);\n    std::shared_ptr<Http2Session> client;\n    if (iter == http2_sessions.end()) {\n        client = swoole_http2_server_session_new(session_id);\n        client->default_ctx = new HttpContext();\n        client->default_ctx->init(serv);\n        client->default_ctx->fd = session_id;\n        client->default_ctx->http2 = true;\n        client->default_ctx->keepalive = true;\n        client->default_ctx->onBeforeRequest = http2_server_onBeforeRequest;\n        client->max_body_size = serv->get_package_max_length(conn);\n        client->handle = http2_server_onRequest;\n        http2_sessions.emplace(session_id, client);\n    } else {\n        client = iter->second;\n    }\n\n    zval zdata;\n    php_swoole_get_recv_data(serv, &zdata, req);\n    int retval = swoole_http2_server_parse(client, Z_STRVAL(zdata));\n    zval_ptr_dtor(&zdata);\n\n    return retval;\n}\n\nstd::shared_ptr<Http2Session> swoole_http2_server_session_new(SessionId fd) {\n    auto session = std::make_shared<Http2Session>(fd);\n    http2_sessions.emplace(fd, session);\n    return session;\n}\n\nvoid php_swoole_http2_server_onClose(Server *serv, SessionId session_id) {\n    server_ips.erase(session_id);\n    client_ips.erase(session_id);\n    swoole_http2_server_session_free(session_id);\n}\n\nvoid swoole_http2_server_session_free(SessionId session_id) {\n    auto iter = http2_sessions.find(session_id);\n    if (iter == http2_sessions.end()) {\n        return;\n    }\n    /* default_ctx does not blong to session object */\n    iter->second->default_ctx = nullptr;\n    http2_sessions.erase(iter);\n}\n"
  },
  {
    "path": "ext-src/swoole_http_client_coro.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n | Author: Twosee  <twose@qq.com>                                       |\n | Author: Fang  <coooold@live.com>                                     |\n | Author: Yuanyi   Zhi  <syyuanyizhi@163.com>                          |\n +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_cxx.h\"\n#include \"php_swoole_http.h\"\n#include \"php_swoole_websocket.h\"\n\n#include \"swoole_protocol.h\"\n#include \"swoole_file.h\"\n#include \"swoole_util.h\"\n#include \"swoole_mime_type.h\"\n#include \"swoole_base64.h\"\n#include \"swoole_socket.h\"\n\nSW_EXTERN_C_BEGIN\n#include \"stubs/php_swoole_http_client_coro_arginfo.h\"\n#include \"ext/standard/base64.h\"\nSW_EXTERN_C_END\n\nusing swoole::AsyncFile;\nusing swoole::String;\nusing swoole::network::Address;\nusing swoole::websocket::FrameObject;\n\nnamespace WebSocket = swoole::websocket;\n\nstatic int http_parser_on_header_field(llhttp_t *parser, const char *at, size_t length);\nstatic int http_parser_on_header_value(llhttp_t *parser, const char *at, size_t length);\nstatic int http_parser_on_headers_complete(llhttp_t *parser);\nstatic int http_parser_on_body(llhttp_t *parser, const char *at, size_t length);\nstatic int http_parser_on_message_complete(llhttp_t *parser);\n\n// clang-format off\nstatic constexpr llhttp_settings_t http_parser_settings =\n{\n    nullptr,                                // on_message_begin\n    nullptr,                                // on_protocol\n    nullptr,                                // on_url\n    nullptr,                                // on_status\n    nullptr,                                // on_method\n    nullptr,                                // on_version\n    http_parser_on_header_field,            // on_header_field\n    http_parser_on_header_value,            // on_header_value\n    nullptr,                                // on_chunk_extension_name\n    nullptr,                                // on_chunk_extension_value\n    http_parser_on_headers_complete,        // on_headers_complete\n    http_parser_on_body,                    // on_body\n    http_parser_on_message_complete,        // on_message_complete\n    nullptr,                                // on_protocol_complete\n    nullptr,                                // on_url_complete\n    nullptr,                                // on_status_complete\n    nullptr,                                // on_method_complete\n    nullptr,                                // on_version_complete\n    nullptr,                                // on_header_field_complete\n    nullptr,                                // on_header_value_complete\n    nullptr,                                // on_chunk_extension_name_complete\n    nullptr,                                // on_chunk_extension_value_complete\n    nullptr,                                // on_chunk_header\n    nullptr,                                // on_chunk_complete\n    nullptr,                                // on_reset\n};\n// clang-format on\n\nnamespace swoole {\nnamespace coroutine {\nnamespace http {\nclass Client {\n  public:\n    /* request info */\n    std::string host;\n    uint16_t port;\n    uint8_t ssl;\n    double connect_timeout = 0;\n    double response_timeout = 0;\n    bool defer = false;\n    bool lowercase_header = true;\n    bool use_default_port;\n\n    int8_t method = SW_HTTP_GET;\n    std::string path;\n    std::string basic_auth;\n\n    /* for response parser */\n    const char *tmp_header_field_name = nullptr;\n    int tmp_header_field_name_len = 0;\n    String *body = nullptr;\n#ifdef SW_HAVE_COMPRESSION\n    enum swHttpCompressMethod compress_method = HTTP_COMPRESS_NONE;\n    bool compression_error = false;\n#endif\n\n    /* options */\n    uint8_t max_retries = 0;\n    bool keep_alive = true;  // enable by default\n    bool websocket = false;  // if upgrade successfully\n    bool chunked = false;    // Transfer-Encoding: chunked\n\n    bool body_decompression = true;\n    bool http_compression = true;\n\n    bool accept_websocket_compression = false;  // websocket server accepts compression\n    WebSocketSettings websocket_settings;\n\n    bool in_callback = false;\n    bool has_upload_files = false;\n\n    std::shared_ptr<AsyncFile> download_file;  // save http response to file\n    zend::String download_file_name;           // unlink the file on error\n    zend_long download_offset = 0;\n\n    /* safety zval */\n    zval _zobject;\n    zval *zobject = &_zobject;\n    zval zsocket;\n    zend::Callable *write_func = nullptr;\n    /**\n     * Retain the send buffer object of the Socket after the Socket object is destroyed,\n     * allowing access to the sent Request data even after the connection has been closed.\n     */\n    String *tmp_write_buffer = nullptr;\n    std::shared_ptr<String> continue_frame_buffer;\n    bool connection_close = false;\n    bool completed = false;\n    bool event_stream = false;\n\n    Client(const zval *zobject, const std::string &host, zend_long port = 80, zend_bool ssl = false);\n\n    bool is_available() const {\n        if (sw_unlikely(!socket || !socket->is_connected())) {\n            php_swoole_socket_set_error_properties(zobject, SW_ERROR_CLIENT_NO_CONNECTION);\n            return false;\n        }\n        return true;\n    }\n\n  private:\n#ifdef SW_HAVE_ZLIB\n    bool gzip_stream_active = false;\n    z_stream gzip_stream = {};\n#endif\n#ifdef SW_HAVE_BROTLI\n    BrotliDecoderState *brotli_decoder_state = nullptr;\n#endif\n#ifdef SW_HAVE_ZSTD\n    ZSTD_DStream *zstd_stream = nullptr;\n#endif\n    bool connect();\n    void set_error(int error, const char *msg, int status) const;\n    bool keep_liveness();\n    bool send_request();\n    void reset();\n\n    static void add_headers(String *buf, const char *key, size_t key_len, const char *data, size_t data_len) {\n        buf->append(key, key_len);\n        buf->append(ZEND_STRL(\": \"));\n        buf->append(data, data_len);\n        buf->append(ZEND_STRL(\"\\r\\n\"));\n    }\n\n    static void add_content_length(String *buf, size_t length) {\n        char content_length_str[64];\n        size_t n = sw_snprintf(SW_STRS(content_length_str), \"Content-Length: %zu\\r\\n\\r\\n\", length);\n        buf->append(content_length_str, n);\n    }\n\n    static void create_token(int length, char *buf) {\n        char characters[] = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\\\"§$%&/()=[]{}\";\n        assert(length < 1024);\n        for (int i = 0; i < length; i++) {\n            buf[i] = characters[swoole_random_int() % (sizeof(characters) - 1)];\n        }\n        buf[length] = '\\0';\n    }\n\n  public:\n#ifdef SW_HAVE_COMPRESSION\n    bool decompress_response(const char *in, size_t in_len);\n#endif\n    void apply_setting(zval *zset, bool check_all = true);\n    void set_basic_auth(const std::string &username, const std::string &password);\n    bool exec(const std::string &_path);\n    bool recv_response(double timeout = 0);\n    void recv_websocket_frame(zval *return_value, double timeout = 0);\n    void add_header(const char *key, size_t key_len, const char *str, size_t length) const;\n    bool upgrade(const std::string &_path);\n    bool push(zval *zdata,\n              zend_long opcode = websocket::OPCODE_TEXT,\n              uint8_t flags = websocket::FLAG_FIN,\n              zend_long code = websocket::CLOSE_NORMAL);\n    bool close(bool should_be_reset = true);\n    void socket_dtor();\n\n    void get_header_out(zval *return_value) const {\n        String *buffer = nullptr;\n        if (socket == nullptr) {\n            buffer = tmp_write_buffer;\n        } else {\n            buffer = socket->get_write_buffer();\n        }\n        if (buffer == nullptr) {\n            RETURN_FALSE;\n        }\n\n        off_t offset = swoole_strnpos(buffer->str, buffer->length, ZEND_STRL(\"\\r\\n\\r\\n\"));\n        if (offset <= 0) {\n            RETURN_FALSE;\n        }\n\n        RETURN_STRINGL(buffer->str, offset);\n    }\n\n    void getsockname(zval *return_value) const {\n        if (!is_available()) {\n            RETURN_FALSE;\n        }\n        if (!socket->getsockname()) {\n            php_swoole_socket_set_error_properties(zobject, socket);\n            RETURN_FALSE;\n        }\n        array_init(return_value);\n        add_assoc_string(return_value, \"address\", socket->get_addr());\n        add_assoc_long(return_value, \"port\", socket->get_port());\n    }\n\n    void getpeername(zval *return_value) const {\n        Address sa;\n        if (!is_available()) {\n            RETURN_FALSE;\n        }\n        if (!socket->getpeername(&sa)) {\n            php_swoole_socket_set_error_properties(zobject, socket);\n            RETURN_FALSE;\n        }\n        array_init(return_value);\n        add_assoc_string(return_value, \"address\", sa.get_addr());\n        add_assoc_long(return_value, \"port\", sa.get_port());\n    }\n\n    void getpeercert(zval *return_value) const {\n        if (!is_available()) {\n            RETURN_FALSE;\n        }\n        auto cert = socket->ssl_get_peer_cert();\n        if (cert.empty()) {\n            php_swoole_socket_set_error_properties(zobject, socket);\n            RETURN_FALSE;\n        } else {\n            RETURN_STRINGL(cert.c_str(), cert.length());\n        }\n    }\n\n    ~Client();\n\n  private:\n    SocketImpl *socket = nullptr;\n    NameResolver::Context resolve_context_ = {};\n    SocketType socket_type = SW_SOCK_TCP;\n    llhttp_t parser = {};\n    bool wait_response = false;\n};\n\n}  // namespace http\n}  // namespace coroutine\n}  // namespace swoole\n\nstatic zend_class_entry *swoole_http_client_coro_ce;\nstatic zend_object_handlers swoole_http_client_coro_handlers;\n\nstatic zend_class_entry *swoole_http_client_coro_exception_ce;\nstatic zend_object_handlers swoole_http_client_coro_exception_handlers;\n\nusing swoole::coroutine::http::Client;\n\nstruct HttpClientObject {\n    Client *client;\n    zend_object std;\n};\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_http_client_coro, __construct);\nstatic PHP_METHOD(swoole_http_client_coro, __destruct);\nstatic PHP_METHOD(swoole_http_client_coro, set);\nstatic PHP_METHOD(swoole_http_client_coro, getDefer);\nstatic PHP_METHOD(swoole_http_client_coro, setDefer);\nstatic PHP_METHOD(swoole_http_client_coro, setMethod);\nstatic PHP_METHOD(swoole_http_client_coro, setHeaders);\nstatic PHP_METHOD(swoole_http_client_coro, setBasicAuth);\nstatic PHP_METHOD(swoole_http_client_coro, setCookies);\nstatic PHP_METHOD(swoole_http_client_coro, setData);\nstatic PHP_METHOD(swoole_http_client_coro, addFile);\nstatic PHP_METHOD(swoole_http_client_coro, addData);\nstatic PHP_METHOD(swoole_http_client_coro, execute);\nstatic PHP_METHOD(swoole_http_client_coro, getsockname);\nstatic PHP_METHOD(swoole_http_client_coro, getpeername);\nstatic PHP_METHOD(swoole_http_client_coro, get);\nstatic PHP_METHOD(swoole_http_client_coro, post);\nstatic PHP_METHOD(swoole_http_client_coro, download);\nstatic PHP_METHOD(swoole_http_client_coro, getBody);\nstatic PHP_METHOD(swoole_http_client_coro, getHeaders);\nstatic PHP_METHOD(swoole_http_client_coro, getCookies);\nstatic PHP_METHOD(swoole_http_client_coro, getStatusCode);\nstatic PHP_METHOD(swoole_http_client_coro, getHeaderOut);\nstatic PHP_METHOD(swoole_http_client_coro, getPeerCert);\nstatic PHP_METHOD(swoole_http_client_coro, upgrade);\nstatic PHP_METHOD(swoole_http_client_coro, push);\nstatic PHP_METHOD(swoole_http_client_coro, recv);\nstatic PHP_METHOD(swoole_http_client_coro, close);\nstatic PHP_METHOD(swoole_http_client_coro, ping);\nstatic PHP_METHOD(swoole_http_client_coro, disconnect);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic const zend_function_entry swoole_http_client_coro_methods[] =\n{\n    PHP_ME(swoole_http_client_coro, __construct,   arginfo_class_Swoole_Coroutine_Http_Client___construct,   ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, __destruct,    arginfo_class_Swoole_Coroutine_Http_Client___destruct,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, set,           arginfo_class_Swoole_Coroutine_Http_Client_set,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, getDefer,      arginfo_class_Swoole_Coroutine_Http_Client_getDefer,      ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, setDefer,      arginfo_class_Swoole_Coroutine_Http_Client_setDefer,      ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, setMethod,     arginfo_class_Swoole_Coroutine_Http_Client_setMethod,     ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, setHeaders,    arginfo_class_Swoole_Coroutine_Http_Client_setHeaders,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, setBasicAuth,  arginfo_class_Swoole_Coroutine_Http_Client_setBasicAuth,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, setCookies,    arginfo_class_Swoole_Coroutine_Http_Client_setCookies,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, setData,       arginfo_class_Swoole_Coroutine_Http_Client_setData,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, addFile,       arginfo_class_Swoole_Coroutine_Http_Client_addFile,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, addData,       arginfo_class_Swoole_Coroutine_Http_Client_addData,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, execute,       arginfo_class_Swoole_Coroutine_Http_Client_execute,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, getpeername,   arginfo_class_Swoole_Coroutine_Http_Client_getpeername,   ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, getsockname,   arginfo_class_Swoole_Coroutine_Http_Client_getsockname,   ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, get,           arginfo_class_Swoole_Coroutine_Http_Client_get,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, post,          arginfo_class_Swoole_Coroutine_Http_Client_post,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, download,      arginfo_class_Swoole_Coroutine_Http_Client_download,      ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, getBody,       arginfo_class_Swoole_Coroutine_Http_Client_getBody,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, getHeaders,    arginfo_class_Swoole_Coroutine_Http_Client_getHeaders,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, getCookies,    arginfo_class_Swoole_Coroutine_Http_Client_getCookies,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, getStatusCode, arginfo_class_Swoole_Coroutine_Http_Client_getStatusCode, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, getHeaderOut,  arginfo_class_Swoole_Coroutine_Http_Client_getHeaderOut,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, getPeerCert, arginfo_class_Swoole_Coroutine_Http_Client_getPeerCert, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, upgrade, arginfo_class_Swoole_Coroutine_Http_Client_upgrade, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, push,    arginfo_class_Swoole_Coroutine_Http_Client_push,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, recv,    arginfo_class_Swoole_Coroutine_Http_Client_recv,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, close,   arginfo_class_Swoole_Coroutine_Http_Client_close,   ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, ping,       arginfo_class_Swoole_Coroutine_Http_Client_ping, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_client_coro, disconnect, arginfo_class_Swoole_Coroutine_Http_Client_disconnect, ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n\n// clang-format on\n\nvoid php_swoole_http_parse_set_cookies(const char *at, size_t length, zval *zcookies, zval *zset_cookie_headers) {\n    const char *eof = at + length;\n    size_t key_len = 0, value_len = 0;\n    zval zvalue;\n\n    // key\n    const char *p = (char *) memchr(at, '=', length);\n    if (p) {\n        key_len = p - at;\n        p++;  // point to value\n    } else {\n        p = at;  // key is empty\n    }\n    // value\n    eof = (char *) memchr(p, ';', at + length - p);\n    if (!eof) {\n        eof = at + length;\n    }\n    value_len = eof - p;\n    if (value_len != 0) {\n        ZVAL_STRINGL(&zvalue, p, value_len);\n        Z_STRLEN(zvalue) = php_url_decode(Z_STRVAL(zvalue), value_len);\n    } else {\n        ZVAL_EMPTY_STRING(&zvalue);\n    }\n    if (key_len == 0) {\n        add_next_index_zval(zcookies, &zvalue);\n    } else {\n        add_assoc_zval_ex(zcookies, at, key_len, &zvalue);\n    }\n\n    // set_cookie_headers\n    add_next_index_stringl(zset_cookie_headers, (char *) at, length);\n}\n\nstatic int http_parser_on_header_field(llhttp_t *parser, const char *at, size_t length) {\n    auto *http = static_cast<Client *>(parser->data);\n    http->tmp_header_field_name = at;\n    http->tmp_header_field_name_len = length;\n    return 0;\n}\n\nstatic int http_parser_on_header_value(llhttp_t *parser, const char *at, size_t length) {\n    auto *http = static_cast<Client *>(parser->data);\n    zval *zobject = static_cast<zval *>(http->zobject);\n\n    const char *header_name = http->tmp_header_field_name;\n    size_t header_len = http->tmp_header_field_name_len;\n    zend::CharPtr _header_name;\n\n    if (http->lowercase_header) {\n        _header_name.assign_tolower(header_name, header_len);\n        header_name = _header_name.get();\n    }\n\n    http->add_header(header_name, header_len, (char *) at, length);\n\n    if (parser->status_code == SW_HTTP_SWITCHING_PROTOCOLS && SW_STREQ(header_name, header_len, \"upgrade\")) {\n        if (swoole_http_token_list_contains_value(at, length, \"websocket\")) {\n            http->websocket = true;\n        }\n        /* TODO: protocol error? */\n    }\n#ifdef SW_HAVE_ZLIB\n    else if (http->websocket && http->websocket_settings.compression &&\n             SW_STREQ(header_name, header_len, \"sec-websocket-extensions\")) {\n        if (swoole_strncasestr(at, length, SW_STRL(\"permessage-deflate\"))) {\n            http->accept_websocket_compression = true;\n        }\n    }\n#endif\n    else if (SW_STREQ(header_name, header_len, \"set-cookie\")) {\n        zval *zcookies =\n            sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, zobject, ZEND_STRL(\"cookies\"), 0);\n        zval *zset_cookie_headers = sw_zend_read_and_convert_property_array(\n            swoole_http_client_coro_ce, zobject, ZEND_STRL(\"set_cookie_headers\"), 0);\n        php_swoole_http_parse_set_cookies(at, length, zcookies, zset_cookie_headers);\n    }\n#ifdef SW_HAVE_COMPRESSION\n    else if (SW_STREQ(header_name, header_len, \"content-encoding\")) {\n        if (false) {\n        }\n#ifdef SW_HAVE_BROTLI\n        else if (SW_STR_ISTARTS_WITH(at, length, \"br\")) {\n            http->compress_method = HTTP_COMPRESS_BR;\n        }\n#endif\n#ifdef SW_HAVE_ZLIB\n        else if (SW_STR_ISTARTS_WITH(at, length, \"gzip\")) {\n            http->compress_method = HTTP_COMPRESS_GZIP;\n        } else if (SW_STR_ISTARTS_WITH(at, length, \"deflate\")) {\n            http->compress_method = HTTP_COMPRESS_DEFLATE;\n        }\n#endif\n#ifdef SW_HAVE_ZSTD\n        else if (SW_STR_ISTARTS_WITH(at, length, \"zstd\")) {\n            http->compress_method = HTTP_COMPRESS_ZSTD;\n        }\n#endif\n    }\n#endif\n    else if (SW_STREQ(header_name, header_len, \"transfer-encoding\") && SW_STR_ISTARTS_WITH(at, length, \"chunked\")) {\n        http->chunked = true;\n    } else if (SW_STREQ(header_name, header_len, \"connection\")) {\n        http->connection_close = SW_STR_ISTARTS_WITH(at, length, \"close\");\n    } else if (SW_STREQ(header_name, header_len, \"content-type\")) {\n        http->event_stream = SW_STR_ISTARTS_WITH(at, length, \"text/event-stream\");\n    }\n\n    return 0;\n}\n\nstatic int http_parser_on_headers_complete(llhttp_t *parser) {\n    auto *http = static_cast<Client *>(parser->data);\n    if (http->method == SW_HTTP_HEAD || parser->status_code == SW_HTTP_NO_CONTENT) {\n        return 1;\n    }\n    return 0;\n}\n\nstatic int http_parser_on_body(llhttp_t *parser, const char *at, size_t length) {\n    auto *http = static_cast<Client *>(parser->data);\n    if (http->write_func) {\n        zval zargv[2];\n        zargv[0] = *http->zobject;\n        ZVAL_STRINGL(&zargv[1], at, length);\n        http->in_callback = true;\n        bool success = http->write_func->call(2, zargv, nullptr);\n        http->in_callback = false;\n        zval_ptr_dtor(&zargv[1]);\n        return success ? 0 : -1;\n    }\n#ifdef SW_HAVE_COMPRESSION\n    else if (http->body_decompression && !http->compression_error && http->compress_method != HTTP_COMPRESS_NONE) {\n        if (!http->decompress_response(at, length)) {\n            http->compression_error = true;\n            goto _append_raw;\n        }\n    }\n#endif\n    else {\n#ifdef SW_HAVE_COMPRESSION\n    _append_raw:\n#endif\n        http->body->append(at, length);\n    }\n    if (http->download_file_name.get() && http->body->length > 0) {\n        if (http->download_file == nullptr) {\n            char *download_file_name = http->download_file_name.val();\n            auto fp = std::make_shared<AsyncFile>(download_file_name, O_CREAT | O_WRONLY, 0664);\n            if (!fp->ready()) {\n                swoole_sys_warning(\"open(%s, O_CREAT | O_WRONLY) failed\", download_file_name);\n                return -1;\n            }\n            if (http->download_offset == 0) {\n                if (!fp->truncate(0)) {\n                    swoole_sys_warning(\"ftruncate(%s) failed\", download_file_name);\n                    return -1;\n                }\n            } else {\n                if (!fp->set_offset(http->download_offset)) {\n                    swoole_sys_warning(\"fseek(%s, %jd) failed\", download_file_name, (intmax_t) http->download_offset);\n                    return -1;\n                }\n            }\n            http->download_file = fp;\n        }\n        if (http->download_file->write(http->body) != (ssize_t) http->body->length) {\n            return -1;\n        }\n        http->body->clear();\n    }\n    return 0;\n}\n\nstatic int http_parser_on_message_complete(llhttp_t *parser) {\n    auto *http = static_cast<Client *>(parser->data);\n    zval *zobject = static_cast<zval *>(http->zobject);\n    http->completed = true;\n    if (parser->upgrade && !http->websocket) {\n        // not support, continue.\n        parser->upgrade = 0;\n        return HPE_PAUSED;\n    }\n\n    zend_update_property_long(\n        swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"statusCode\"), parser->status_code);\n    if (http->download_file == nullptr) {\n        zend_update_property_stringl(\n            swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"body\"), SW_STRINGL(http->body));\n    } else {\n        http->download_file_name.release();\n    }\n\n    return HPE_PAUSED;\n}\n\nClient::Client(const zval *zobject, const std::string &host, zend_long port, zend_bool ssl) {\n    this->host = host;\n    this->socket_type = network::Socket::convert_to_type(this->host);\n    this->use_default_port = port == 0;\n    if (this->use_default_port) {\n        port = ssl ? 443 : 80;\n    }\n    this->port = port;\n    this->ssl = ssl;\n    _zobject = *zobject;\n}\n\n#ifdef SW_HAVE_COMPRESSION\nbool Client::decompress_response(const char *in, size_t in_len) {\n    if (in_len == 0) {\n        return false;\n    }\n\n    size_t reserved_body_length = body->length;\n\n    switch (compress_method) {\n#ifdef SW_HAVE_ZLIB\n    case HTTP_COMPRESS_GZIP:\n    case HTTP_COMPRESS_DEFLATE: {\n        int status;\n        int encoding = compress_method == HTTP_COMPRESS_GZIP ? SW_ZLIB_ENCODING_GZIP : SW_ZLIB_ENCODING_DEFLATE;\n        bool first_decompress = !gzip_stream_active;\n\n        if (!gzip_stream_active) {\n        _retry:\n            memset(&gzip_stream, 0, sizeof(gzip_stream));\n            gzip_stream.zalloc = php_zlib_alloc;\n            gzip_stream.zfree = php_zlib_free;\n            // gzip_stream.total_out = 0;\n            status = inflateInit2(&gzip_stream, encoding);\n            if (status != Z_OK) {\n                swoole_warning(\"inflateInit2() failed by %s\", zError(status));\n                return false;\n            }\n            gzip_stream_active = true;\n        }\n\n        gzip_stream.next_in = (Bytef *) in;\n        gzip_stream.avail_in = in_len;\n        gzip_stream.total_in = 0;\n\n        while (true) {\n            const size_t total_out = gzip_stream.total_out;\n            gzip_stream.avail_out = body->size - body->length;\n            gzip_stream.next_out = (Bytef *) (body->str + body->length);\n            SW_ASSERT(body->length <= body->size);\n            status = inflate(&gzip_stream, Z_SYNC_FLUSH);\n            if (status >= 0) {\n                body->length += (gzip_stream.total_out - total_out);\n                if (body->length + (SW_BUFFER_SIZE_STD / 2) >= body->size) {\n                    body->extend();\n                }\n            }\n            if (status == Z_STREAM_END || (status == Z_OK && gzip_stream.avail_in == 0)) {\n                return true;\n            }\n            if (status != Z_OK) {\n                break;\n            }\n        }\n\n        if (status == Z_DATA_ERROR && first_decompress) {\n            first_decompress = false;\n            inflateEnd(&gzip_stream);\n            encoding = SW_ZLIB_ENCODING_RAW;\n            body->length = reserved_body_length;\n            goto _retry;\n        }\n\n        swoole_warning(\"HttpClient::decompress_response failed by %s\", zError(status));\n        body->length = reserved_body_length;\n        return false;\n    }\n#endif\n#ifdef SW_HAVE_BROTLI\n    case HTTP_COMPRESS_BR: {\n        if (!brotli_decoder_state) {\n            brotli_decoder_state = BrotliDecoderCreateInstance(php_brotli_alloc, php_brotli_free, nullptr);\n            if (!brotli_decoder_state) {\n                swoole_warning(\"BrotliDecoderCreateInstance() failed\");\n                return false;\n            }\n        }\n\n        const char *next_in = in;\n        size_t available_in = in_len;\n        while (true) {\n            size_t available_out = body->size - body->length, reserved_available_out = available_out;\n            char *next_out = body->str + body->length;\n            size_t total_out;\n            SW_ASSERT(body->length <= body->size);\n            BrotliDecoderResult result = BrotliDecoderDecompressStream(brotli_decoder_state,\n                                                                       &available_in,\n                                                                       (const uint8_t **) &next_in,\n                                                                       &available_out,\n                                                                       (uint8_t **) &next_out,\n                                                                       &total_out);\n            body->length += reserved_available_out - available_out;\n            if (result == BROTLI_DECODER_RESULT_SUCCESS || result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {\n                return true;\n            } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {\n                body->extend();\n            } else {\n                swoole_warning(\"BrotliDecoderDecompressStream() failed, %s\",\n                               BrotliDecoderErrorString(BrotliDecoderGetErrorCode(brotli_decoder_state)));\n                break;\n            }\n        }\n\n        body->length = reserved_body_length;\n        return false;\n    }\n#endif\n#ifdef SW_HAVE_ZSTD\n    case HTTP_COMPRESS_ZSTD: {\n        size_t zstd_result = 0;\n        if (zstd_stream == nullptr) {\n            zstd_stream = ZSTD_createDStream();\n            if (!zstd_stream) {\n                swoole_warning(\"ZSTD_createDStream() failed, can not create ZSTD stream\");\n                return false;\n            }\n\n            zstd_result = ZSTD_initDStream(zstd_stream);\n            if (ZSTD_isError(zstd_result)) {\n                swoole_warning(\"ZSTD_initDStream() failed, Error: [%s]\", ZSTD_getErrorName(zstd_result));\n                return false;\n            }\n        }\n\n        size_t recommended_size = ZSTD_DStreamOutSize();\n        ZSTD_inBuffer in_buffer = {in, in_len, 0};\n        ZSTD_outBuffer out_buffer = {body->str + body->length, body->size - body->length, 0};\n        while (in_buffer.pos < in_buffer.size) {\n            if (sw_unlikely(out_buffer.pos == out_buffer.size)) {\n                body->extend(recommended_size + body->size);\n                body->length += out_buffer.pos;\n                out_buffer = {body->str + body->length, body->size - body->length, 0};\n            }\n\n            zstd_result = ZSTD_decompressStream(zstd_stream, &out_buffer, &in_buffer);\n            if (ZSTD_isError(zstd_result)) {\n                swoole_warning(\"ZSTD_decompressStream() failed, Error: [%s]\", ZSTD_getErrorName(zstd_result));\n                return false;\n            }\n        }\n\n        body->length += out_buffer.pos;\n        return true;\n    }\n#endif\n    default:\n        break;\n    }\n\n    swoole_warning(\"HttpClient::decompress_response unknown compress method [%d]\", compress_method);\n    return false;\n}\n#endif\n\nvoid Client::apply_setting(zval *zset, const bool check_all) {\n    if (!ZVAL_IS_ARRAY(zset) || php_swoole_array_length(zset) == 0) {\n        return;\n    }\n    if (check_all) {\n        zval *ztmp;\n        HashTable *vht = Z_ARRVAL_P(zset);\n\n        if (php_swoole_array_get_value(vht, \"connect_timeout\", ztmp)) {\n            connect_timeout = zval_get_double(ztmp);\n        }\n        if (php_swoole_array_get_value(vht, \"timeout\", ztmp)) {\n            response_timeout = zval_get_double(ztmp);\n        }\n        if (php_swoole_array_get_value(vht, \"max_retries\", ztmp)) {\n            max_retries = (uint8_t) SW_MIN(zval_get_long(ztmp), UINT8_MAX);\n        }\n        if (php_swoole_array_get_value(vht, \"defer\", ztmp)) {\n            defer = zval_is_true(ztmp);\n        }\n        if (php_swoole_array_get_value(vht, \"lowercase_header\", ztmp)) {\n            lowercase_header = zval_is_true(ztmp);\n        }\n        if (php_swoole_array_get_value(vht, \"keep_alive\", ztmp)) {\n            keep_alive = zval_is_true(ztmp);\n        }\n        if (php_swoole_array_get_value(vht, \"http_compression\", ztmp)) {\n            http_compression = zval_is_true(ztmp);\n        }\n        if (php_swoole_array_get_value(vht, \"body_decompression\", ztmp)) {\n            body_decompression = zval_is_true(ztmp);\n        }\n        if (php_swoole_array_get_value(vht, \"write_func\", ztmp)) {\n            delete write_func;\n            write_func = sw_callable_create(ztmp);\n        }\n        WebSocket::apply_setting(websocket_settings, vht, false);\n    }\n    if (socket) {\n        php_swoole_socket_set(socket, zset);\n        if (socket->http_proxy && !socket->ssl_is_enable()) {\n            socket->http_proxy->dont_handshake = 1;\n        }\n    }\n}\n\nvoid Client::set_basic_auth(const std::string &username, const std::string &password) {\n    std::string input = username + \":\" + password;\n    size_t output_size = sizeof(\"Basic \") + BASE64_ENCODE_OUT_SIZE(input.size());\n    char *output = (char *) emalloc(output_size);\n    if (sw_likely(output)) {\n        size_t output_len = sprintf(output, \"Basic \");\n        output_len += base64_encode((const unsigned char *) input.c_str(), input.size(), output + output_len);\n        basic_auth = std::string((const char *) output, output_len);\n        efree(output);\n    }\n}\n\nvoid Client::add_header(const char *key, size_t key_len, const char *str, size_t length) const {\n    zval *zheaders =\n        sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, zobject, ZEND_STRL(\"headers\"), 0);\n\n    zval zheader_new;\n    ZVAL_STRINGL(&zheader_new, str, length);\n\n    zend::array_add_or_merge(zheaders, key, key_len, &zheader_new);\n}\n\nbool Client::connect() {\n    if (socket) {\n        return true;\n    }\n    if (!body) {\n        body = new String(SW_HTTP_RESPONSE_INIT_SIZE);\n        if (!body) {\n            set_error(ENOMEM, swoole_strerror(ENOMEM), HTTP_ESTATUS_CONNECT_FAILED);\n            return false;\n        }\n    }\n\n    php_swoole_check_reactor();\n    auto object = php_swoole_create_socket(socket_type);\n    if (UNEXPECTED(!object)) {\n        set_error(errno, swoole_strerror(errno), HTTP_ESTATUS_CONNECT_FAILED);\n        return false;\n    }\n    ZVAL_OBJ(&zsocket, object);\n    socket = php_swoole_get_socket(&zsocket);\n\n    if (ssl && !socket->enable_ssl_encrypt()) {\n        set_error(socket->errCode, socket->errMsg, HTTP_ESTATUS_CONNECT_FAILED);\n        close();\n        return false;\n    }\n\n    // apply settings\n    apply_setting(sw_zend_read_property_ex(Z_OBJCE_P(zobject), zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0), false);\n\n    // reset the properties that depend on the connection\n    websocket = false;\n#ifdef SW_HAVE_ZLIB\n    accept_websocket_compression = false;\n#endif\n\n    double _timeout = connect_timeout == 0 ? network::Socket::default_connect_timeout : connect_timeout;\n    socket->set_timeout(_timeout, SW_TIMEOUT_CONNECT);\n    socket->set_resolve_context(&resolve_context_);\n    socket->set_dtor([this](Socket *_socket) { socket_dtor(); });\n    socket->set_buffer_allocator(sw_zend_string_allocator());\n\n    if (!socket->connect(host, port)) {\n        set_error(socket->errCode, socket->errMsg, HTTP_ESTATUS_CONNECT_FAILED);\n        close();\n        return false;\n    }\n\n    zend_update_property(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"socket\"), &zsocket);\n    zend_update_property_bool(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"connected\"), 1);\n    return true;\n}\n\nvoid Client::set_error(int error, const char *msg, int status) const {\n    auto ce = swoole_http_client_coro_ce;\n    auto obj = SW_Z8_OBJ_P(zobject);\n    zend_update_property_long(ce, obj, ZEND_STRL(\"errCode\"), error);\n    zend_update_property_string(ce, obj, ZEND_STRL(\"errMsg\"), msg);\n    zend_update_property_long(ce, obj, ZEND_STRL(\"statusCode\"), status);\n}\n\nbool Client::keep_liveness() {\n    if (!socket || !socket->check_liveness()) {\n        if (socket) {\n            /* in progress */\n            socket->check_bound_co(SW_EVENT_RDWR);\n            set_error(socket->errCode, socket->errMsg, HTTP_ESTATUS_SERVER_RESET);\n            close(false);\n        }\n        SW_LOOP_N(max_retries + 1) {\n            if (connect()) {\n                return true;\n            }\n        }\n        return false;\n    }\n    return true;\n}\n\nbool Client::send_request() {\n    zval *zvalue = nullptr;\n    uint32_t header_flag = 0x0;\n    zval *zmethod, *zheaders, *zbody, *zupload_files, *zcookies, *z_download_file;\n\n    if (path.empty()) {\n        php_swoole_socket_set_error_properties(zobject, SW_ERROR_INVALID_PARAMS);\n        return false;\n    }\n\n    // when new request, clear all properties about the last response\n    zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"headers\"));\n    zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"set_cookie_headers\"));\n    zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"body\"), \"\");\n\n    if (!keep_liveness()) {\n        return false;\n    }\n\n    zend_update_property_long(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"errCode\"), 0);\n    zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"errMsg\"), \"\");\n    zend_update_property_long(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"statusCode\"), 0);\n\n    /* another coroutine is connecting */\n    socket->check_bound_co(SW_EVENT_WRITE);\n\n    // clear errno\n    swoole_set_last_error(0);\n    // alloc buffer\n    String *buffer = socket->get_write_buffer();\n    buffer->clear();\n    // clear body\n    body->clear();\n\n    zmethod = sw_zend_read_property_not_null_ex(\n        swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_METHOD), 0);\n    zheaders =\n        sw_zend_read_property_ex(swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_HEADERS), 0);\n    zbody = sw_zend_read_property_not_null_ex(\n        swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_BODY), 0);\n    zupload_files =\n        sw_zend_read_property_ex(swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_UPLOAD_FILES), 0);\n    zcookies = sw_zend_read_property_ex(swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIES), 0);\n    z_download_file = sw_zend_read_property_not_null_ex(\n        swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_DOWNLOAD_FILE), 0);\n\n    // ============   host   ============\n    zend::String str_host;\n\n    if ((ZVAL_IS_ARRAY(zheaders)) && (((zvalue = zend_hash_str_find(Z_ARRVAL_P(zheaders), ZEND_STRL(\"Host\")))) ||\n                                      ((zvalue = zend_hash_str_find(Z_ARRVAL_P(zheaders), ZEND_STRL(\"host\")))))) {\n        str_host = zvalue;\n    }\n\n    // ============ download ============\n    if (z_download_file) {\n        download_file_name = z_download_file;\n        download_offset = zval_get_long(sw_zend_read_property_ex(\n            swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_DOWNLOAD_OFFSET), 0));\n    }\n\n    // ============ method ============\n    {\n        zend::String str_method;\n        const char *_method;\n        size_t method_len;\n        if (zmethod) {\n            str_method = zmethod;\n            _method = str_method.val();\n            method_len = str_method.len();\n        } else {\n            _method = zbody ? \"POST\" : \"GET\";\n            method_len = strlen(_method);\n        }\n        this->method = http_server::get_method(_method, method_len);\n        buffer->append(_method, method_len);\n        buffer->append(ZEND_STRL(\" \"));\n    }\n\n    // ============ path & proxy ============\n    bool require_proxy_authentication = false;\n    if (socket->http_proxy && !socket->ssl_is_enable()) {\n        const static char *pre = \"http://\";\n        char *_host = (char *) host.c_str();\n        size_t _host_len = host.length();\n        if (str_host.get()) {\n            _host = str_host.val();\n            _host_len = str_host.len();\n        }\n        size_t proxy_uri_len = path.length() + _host_len + strlen(pre) + 10;\n        char *proxy_uri = (char *) emalloc(proxy_uri_len);\n        if (nullptr == memchr(_host, ':', _host_len)) {\n            proxy_uri_len = sw_snprintf(proxy_uri, proxy_uri_len, \"%s%s:%u%s\", pre, _host, port, path.c_str());\n        } else {\n            proxy_uri_len = sw_snprintf(proxy_uri, proxy_uri_len, \"%s%s%s\", pre, _host, path.c_str());\n        }\n        buffer->append(proxy_uri, proxy_uri_len);\n        if (!socket->http_proxy->password.empty()) {\n            require_proxy_authentication = true;\n        }\n        efree(proxy_uri);\n    } else {\n        buffer->append(path.c_str(), path.length());\n    }\n\n    // ============ protocol ============\n    buffer->append(ZEND_STRL(\" HTTP/1.1\\r\\n\"));\n\n    // ============ headers ============\n    char *key;\n    uint32_t keylen;\n    int keytype;\n\n    // As much as possible to ensure that Host is the first header.\n    // See: http://tools.ietf.org/html/rfc7230#section-5.4\n    if (str_host.get()) {\n        add_headers(buffer, ZEND_STRL(\"Host\"), str_host.val(), str_host.len());\n    } else {\n        // See: https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.23\n        const std::string *_host;\n        std::string real_host;\n        if (!ssl ? port != 80 : port != 443) {\n            real_host = std_string::format(\"%s:%u\", host.c_str(), port);\n            _host = &real_host;\n        } else {\n            _host = &host;\n        }\n        add_headers(buffer, ZEND_STRL(\"Host\"), _host->c_str(), _host->length());\n    }\n\n    if (ZVAL_IS_ARRAY(zheaders)) {\n        SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zheaders), key, keylen, keytype, zvalue) {\n            if (UNEXPECTED(HASH_KEY_IS_STRING != keytype || ZVAL_IS_NULL(zvalue))) {\n                continue;\n            }\n            if (SW_STRCASEEQ(key, keylen, \"Host\")) {\n                continue;\n            }\n            if (SW_STRCASEEQ(key, keylen, \"Content-Length\")) {\n                header_flag |= HTTP_HEADER_CONTENT_LENGTH;\n                // ignore custom Content-Length value\n                continue;\n            }\n\n            if (SW_STRCASEEQ(key, keylen, \"Accept-Encoding\")) {\n#ifdef SW_HAVE_COMPRESSION\n                header_flag |= HTTP_HEADER_ACCEPT_ENCODING;\n#else\n                php_swoole_error(E_WARNING, \"Missing a compression package, 'Accept-Encoding' is ignored\");\n                continue;\n#endif\n            }\n\n            zend::String str_value(zvalue);\n            add_headers(buffer, key, keylen, str_value.val(), str_value.len());\n\n            if (SW_STRCASEEQ(key, keylen, \"Connection\")) {\n                header_flag |= HTTP_HEADER_CONNECTION;\n                if (SW_STRCASEEQ(str_value.val(), str_value.len(), \"close\")) {\n                    keep_alive = false;\n                }\n            }\n        }\n        SW_HASHTABLE_FOREACH_END();\n    }\n    // http proxy authentication\n    if (require_proxy_authentication) {\n        std::string value(\"Basic \");\n        value += socket->http_proxy->get_auth_str();\n        add_headers(buffer, ZEND_STRL(\"Proxy-Authorization\"), value.c_str(), value.length());\n    }\n    if (!basic_auth.empty()) {\n        add_headers(buffer, ZEND_STRL(\"Authorization\"), basic_auth.c_str(), basic_auth.size());\n    }\n    if (!(header_flag & HTTP_HEADER_CONNECTION)) {\n        if (keep_alive) {\n            add_headers(buffer, ZEND_STRL(\"Connection\"), ZEND_STRL(\"keep-alive\"));\n        } else {\n            add_headers(buffer, ZEND_STRL(\"Connection\"), ZEND_STRL(\"closed\"));\n        }\n    }\n#ifdef SW_HAVE_COMPRESSION\n    if (http_compression && !(header_flag & HTTP_HEADER_ACCEPT_ENCODING)) {\n        add_headers(buffer,\n                    ZEND_STRL(\"Accept-Encoding\"),\n#if defined(SW_HAVE_ZLIB) && defined(SW_HAVE_BROTLI)\n                    ZEND_STRL(\"gzip, deflate, br\")\n#else\n#ifdef SW_HAVE_ZLIB\n                    ZEND_STRL(\"gzip, deflate\")\n#else\n#ifdef SW_HAVE_BROTLI\n                    ZEND_STRL(\"br\")\n#else\n#ifdef SW_HAVE_ZSTD\n                    ZEND_STRL(\"zstd\")\n#endif\n#endif\n#endif\n#endif\n        );\n    }\n#endif\n\n    // ============ cookies ============\n    if (ZVAL_IS_ARRAY(zcookies)) {\n        buffer->append(ZEND_STRL(\"Cookie: \"));\n        int n_cookie = php_swoole_array_length(zcookies);\n        int i = 0;\n        char *encoded_value;\n\n        SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zcookies), key, keylen, keytype, zvalue) {\n            i++;\n            if (HASH_KEY_IS_STRING != keytype) {\n                continue;\n            }\n            zend::String str_value(zvalue);\n            if (str_value.len() == 0) {\n                continue;\n            }\n            buffer->append(key, keylen);\n            buffer->append(\"=\", 1);\n\n            size_t encoded_value_len;\n            encoded_value = php_swoole_url_encode(str_value.val(), str_value.len(), &encoded_value_len);\n            if (encoded_value) {\n                buffer->append(encoded_value, encoded_value_len);\n                efree(encoded_value);\n            }\n            if (i < n_cookie) {\n                buffer->append(\"; \", 2);\n            }\n        }\n        SW_HASHTABLE_FOREACH_END();\n        buffer->append(ZEND_STRL(\"\\r\\n\"));\n    }\n\n    // ============ multipart/form-data ============\n    if ((has_upload_files = (php_swoole_array_length_safe(zupload_files) > 0))) {\n        char header_buf[2048];\n        char boundary_str[SW_HTTP_CLIENT_BOUNDARY_TOTAL_SIZE + 1];\n\n        // ============ content-type ============\n        memcpy(boundary_str, SW_HTTP_CLIENT_BOUNDARY_PREKEY, sizeof(SW_HTTP_CLIENT_BOUNDARY_PREKEY) - 1);\n        swoole_random_string(boundary_str + sizeof(SW_HTTP_CLIENT_BOUNDARY_PREKEY) - 1,\n                             sizeof(boundary_str) - sizeof(SW_HTTP_CLIENT_BOUNDARY_PREKEY));\n        ssize_t n = sw_snprintf(header_buf,\n                                sizeof(header_buf),\n                                \"Content-Type: multipart/form-data; boundary=%.*s\\r\\n\",\n                                (int) (sizeof(boundary_str) - 1),\n                                boundary_str);\n        buffer->append(header_buf, n);\n\n        // ============ content-length ============\n        size_t content_length = 0;\n\n        // calculate length before encode array\n        if (zbody && ZVAL_IS_ARRAY(zbody)) {\n            SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zbody), key, keylen, keytype, zvalue)\n            if (UNEXPECTED(HASH_KEY_IS_STRING != keytype || ZVAL_IS_NULL(zvalue))) {\n                continue;\n            }\n            zend::String str_value(zvalue);\n            // strlen(\"%.*s\")*2 = 8\n            // header + body + CRLF(2)\n            content_length += (sizeof(SW_HTTP_FORM_RAW_DATA_FMT) - SW_HTTP_FORM_RAW_DATA_FMT_LEN - 1) +\n                              (sizeof(boundary_str) - 1) + keylen + str_value.len() + 2;\n            SW_HASHTABLE_FOREACH_END();\n        }\n\n        zval *zname;\n        zval *ztype;\n        zval *zsize = nullptr;\n        zval *zpath = nullptr;\n        zval *zcontent = nullptr;\n        zval *zfilename;\n        zval *zoffset;\n\n        // calculate length of files\n        {\n            // upload files\n            SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zupload_files), key, keylen, keytype, zvalue) {\n                HashTable *ht = Z_ARRVAL_P(zvalue);\n                if (!((zname = zend_hash_str_find(ht, ZEND_STRL(\"name\"))))) {\n                    continue;\n                }\n                if (!((zfilename = zend_hash_str_find(ht, ZEND_STRL(\"filename\"))))) {\n                    continue;\n                }\n                if (!((zsize = zend_hash_str_find(ht, ZEND_STRL(\"size\"))))) {\n                    continue;\n                }\n                if (!((ztype = zend_hash_str_find(ht, ZEND_STRL(\"type\"))))) {\n                    continue;\n                }\n                // strlen(\"%.*s\")*4 = 16\n                // header + body + CRLF(2)\n                content_length += (sizeof(SW_HTTP_FORM_FILE_DATA_FMT) - SW_HTTP_FORM_FILE_DATA_FMT_LEN - 1) +\n                                  (sizeof(boundary_str) - 1) + Z_STRLEN_P(zname) + Z_STRLEN_P(zfilename) +\n                                  Z_STRLEN_P(ztype) + Z_LVAL_P(zsize) + 2;\n            }\n            SW_HASHTABLE_FOREACH_END();\n        }\n\n        add_content_length(buffer, content_length + sizeof(boundary_str) - 1 + 6);\n\n        // ============ form-data body ============\n        if (zbody && ZVAL_IS_ARRAY(zbody)) {\n            SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zbody), key, keylen, keytype, zvalue) {\n                if (UNEXPECTED(HASH_KEY_IS_STRING != keytype || ZVAL_IS_NULL(zvalue))) {\n                    continue;\n                }\n                zend::String str_value(zvalue);\n                n = sw_snprintf(header_buf,\n                                sizeof(header_buf),\n                                SW_HTTP_FORM_RAW_DATA_FMT,\n                                (int) (sizeof(boundary_str) - 1),\n                                boundary_str,\n                                keylen,\n                                key);\n                buffer->append(header_buf, n);\n                buffer->append(str_value.val(), str_value.len());\n                buffer->append(ZEND_STRL(\"\\r\\n\"));\n            }\n            SW_HASHTABLE_FOREACH_END();\n        }\n\n        if (socket->send_all(buffer->str, buffer->length) != (ssize_t) buffer->length) {\n            goto _send_fail;\n        }\n\n        {\n            // upload files\n            SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zupload_files), key, keylen, keytype, zvalue) {\n                if (!((zname = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL(\"name\"))))) {\n                    continue;\n                }\n                if (!((zfilename = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL(\"filename\"))))) {\n                    continue;\n                }\n                /**\n                 * from disk file\n                 */\n                if (!((zcontent = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL(\"content\"))))) {\n                    // file path\n                    if (!((zpath = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL(\"path\"))))) {\n                        continue;\n                    }\n                    // file offset\n                    if (!((zoffset = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL(\"offset\"))))) {\n                        continue;\n                    }\n                    zcontent = nullptr;\n                } else {\n                    zpath = nullptr;\n                    zoffset = nullptr;\n                }\n                if (!((zsize = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL(\"size\"))))) {\n                    continue;\n                }\n                if (!((ztype = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL(\"type\"))))) {\n                    continue;\n                }\n                /**\n                 * part header\n                 */\n                n = sw_snprintf(header_buf,\n                                sizeof(header_buf),\n                                SW_HTTP_FORM_FILE_DATA_FMT,\n                                (int) (sizeof(boundary_str) - 1),\n                                boundary_str,\n                                (int) Z_STRLEN_P(zname),\n                                Z_STRVAL_P(zname),\n                                (int) Z_STRLEN_P(zfilename),\n                                Z_STRVAL_P(zfilename),\n                                (int) Z_STRLEN_P(ztype),\n                                Z_STRVAL_P(ztype));\n                /**\n                 * from memory\n                 */\n                if (zcontent) {\n                    buffer->clear();\n                    buffer->append(header_buf, n);\n                    buffer->append(Z_STRVAL_P(zcontent), Z_STRLEN_P(zcontent));\n                    buffer->append(\"\\r\\n\", 2);\n\n                    if (socket->send_all(buffer->str, buffer->length) != (ssize_t) buffer->length) {\n                        goto _send_fail;\n                    }\n                }\n                /**\n                 * from disk file\n                 */\n                else {\n                    if (socket->send_all(header_buf, n) != n) {\n                        goto _send_fail;\n                    }\n                    if (!socket->sendfile(Z_STRVAL_P(zpath), Z_LVAL_P(zoffset), Z_LVAL_P(zsize))) {\n                        goto _send_fail;\n                    }\n                    if (socket->send_all(\"\\r\\n\", 2) != 2) {\n                        goto _send_fail;\n                    }\n                }\n            }\n            SW_HASHTABLE_FOREACH_END();\n        }\n\n        n = sw_snprintf(header_buf, sizeof(header_buf), \"--%.*s--\\r\\n\", (int) (sizeof(boundary_str) - 1), boundary_str);\n        if (socket->send_all(header_buf, n) != n) {\n            goto _send_fail;\n        }\n        wait_response = true;\n        return true;\n    }\n    // ============ x-www-form-urlencoded or raw ============\n    else if (zbody) {\n        if (ZVAL_IS_ARRAY(zbody)) {\n            size_t len;\n            add_headers(buffer, ZEND_STRL(\"Content-Type\"), ZEND_STRL(\"application/x-www-form-urlencoded\"));\n            if (php_swoole_array_length(zbody) > 0) {\n                smart_str formstr_s = {};\n                char *formstr = php_swoole_http_build_query(zbody, &len, &formstr_s);\n                if (formstr == nullptr) {\n                    php_swoole_error(E_WARNING, \"http_build_query failed\");\n                    return false;\n                }\n                add_content_length(buffer, len);\n                buffer->append(formstr, len);\n                smart_str_free(&formstr_s);\n            } else {\n                add_content_length(buffer, 0);\n            }\n        } else {\n            auto sdata = zval_get_string(zbody);\n            add_content_length(buffer, ZSTR_LEN(sdata));\n            buffer->append(ZSTR_VAL(sdata), ZSTR_LEN(sdata));\n            zend_string_release(sdata);\n        }\n    }\n    // ============ no body ============\n    else {\n        if (header_flag & HTTP_HEADER_CONTENT_LENGTH) {\n            add_content_length(buffer, 0);\n        } else {\n            buffer->append(ZEND_STRL(\"\\r\\n\"));\n        }\n    }\n\n    swoole_trace_log(SW_TRACE_HTTP_CLIENT,\n                     \"to [%s:%u%s] by fd#%d in cid#%ld with [%zu] bytes: <<EOF\\n%.*s\\nEOF\",\n                     host.c_str(),\n                     port,\n                     path.c_str(),\n                     socket->get_fd(),\n                     Coroutine::get_current_cid(),\n                     buffer->length,\n                     (int) buffer->length,\n                     buffer->str);\n\n    if (socket->send_all(buffer->str, buffer->length) != (ssize_t) buffer->length) {\n    _send_fail:\n        set_error(socket->errCode, socket->errMsg, HTTP_ESTATUS_SEND_FAILED);\n        close();\n        return false;\n    }\n    wait_response = true;\n    return true;\n}\n\nbool Client::exec(const std::string &_path) {\n    path = _path;\n    // bzero when make a new reqeust\n    resolve_context_ = {};\n    if (use_default_port) {\n        resolve_context_.with_port = true;\n    }\n    SW_LOOP_N(max_retries + 1) {\n        if (send_request() == false) {\n            return false;\n        }\n        if (defer) {\n            return true;\n        }\n        if (recv_response() == false) {\n            return false;\n        }\n        if (max_retries > 0 &&\n            (parser.status_code == SW_HTTP_BAD_GATEWAY || parser.status_code == SW_HTTP_SERVICE_UNAVAILABLE)) {\n            close(true);\n            continue;\n        }\n        return true;\n    }\n    return false;\n}\n\nbool Client::recv_response(double timeout) {\n    if (!wait_response) {\n        return false;\n    }\n    ssize_t retval = 0;\n    size_t total_bytes = 0, parsed_n = 0;\n    String *buffer = socket->get_read_buffer();\n    bool header_completed = false;\n    off_t header_crlf_offset = 0;\n\n    // re-init http response parser\n    swoole_llhttp_parser_init(&parser, HTTP_RESPONSE, (void *) this);\n\n    if (timeout == 0) {\n        timeout = response_timeout == 0 ? network::Socket::default_read_timeout : response_timeout;\n    }\n    Socket::TimeoutController tc(socket, timeout, SW_TIMEOUT_READ);\n    bool success = false;\n    while (true) {\n        if (sw_unlikely(tc.has_timedout(SW_TIMEOUT_READ))) {\n            break;\n        }\n        retval = socket->recv(buffer->str + buffer->length, buffer->size - buffer->length);\n        if (sw_unlikely(retval <= 0)) {\n            if (retval == 0) {\n                socket->set_err(ECONNRESET);\n                if (total_bytes > 0 && !llhttp_should_keep_alive(&parser)) {\n                    llhttp_finish(&parser);\n                    success = true;\n                    break;\n                }\n            }\n            break;\n        }\n\n        if (!header_completed) {\n            buffer->length += retval;\n            if (swoole_strnpos(\n                    buffer->str + header_crlf_offset, buffer->length - header_crlf_offset, ZEND_STRL(\"\\r\\n\\r\\n\")) < 0) {\n                if (buffer->length == buffer->size) {\n                    swoole_error_log(SW_LOG_TRACE, SW_ERROR_HTTP_INVALID_PROTOCOL, \"Http header too large\");\n                    socket->set_err(SW_ERROR_HTTP_INVALID_PROTOCOL);\n                    break;\n                }\n                header_crlf_offset = buffer->length > 4 ? buffer->length - 4 : 0;\n                continue;\n            } else {\n                header_completed = true;\n                header_crlf_offset = 0;\n                retval = buffer->length;\n                buffer->clear();\n            }\n        }\n\n        total_bytes += retval;\n        parsed_n = swoole_llhttp_parser_execute(&parser, &http_parser_settings, buffer->str, retval);\n        swoole_trace_log(SW_TRACE_HTTP_CLIENT,\n                         \"parsed_n=%ld, retval=%ld, total_bytes=%ld, completed=%d\",\n                         parsed_n,\n                         retval,\n                         total_bytes,\n                         completed);\n\n        if (sw_unlikely(socket->get_socket()->close_wait)) {\n            success = false;\n            break;\n        }\n\n        if (sw_likely(parser.error == HPE_OK)) {\n            if (sw_unlikely(event_stream && llhttp_message_needs_eof(&parser)) == 1) {\n                llhttp_finish(&parser);\n            }\n\n            if (completed) {\n                if (parser.upgrade && (size_t) retval > parsed_n + SW_WEBSOCKET_HEADER_LEN) {\n                    buffer->length = retval;\n                    buffer->offset = parsed_n;\n                    buffer->reduce(parsed_n);\n                }\n                success = true;\n                break;\n            }\n        } else {\n            socket->set_err(SW_ERROR_HTTP_INVALID_PROTOCOL);\n            break;\n        }\n    }\n\n    if (!success) {\n        php_swoole_socket_set_error_properties(zobject, socket);\n        zend::object_set(zobject,\n                         ZEND_STRL(\"statusCode\"),\n                         socket->errCode == ETIMEDOUT ? HTTP_ESTATUS_REQUEST_TIMEOUT : HTTP_ESTATUS_SERVER_RESET);\n        close();\n        return false;\n    }\n    /**\n     * TODO: Sec-WebSocket-Accept check\n     */\n    if (websocket) {\n        socket->open_length_check = true;\n        socket->protocol.package_length_size = SW_WEBSOCKET_HEADER_LEN;\n        socket->protocol.package_length_offset = 0;\n        socket->protocol.package_body_offset = 0;\n        socket->protocol.get_package_length = websocket::get_package_length;\n    }\n    // handler keep alive\n    if (!websocket && (!keep_alive || connection_close)) {\n        close();\n    } else {\n        reset();\n    }\n\n    return true;\n}\n\nvoid Client::recv_websocket_frame(zval *return_value, double timeout) {\n    WebSocket::recv_frame(websocket_settings, continue_frame_buffer, socket, return_value, timeout);\n    if (ZVAL_IS_EMPTY_STRING(return_value)) {\n        close();\n        return;\n    }\n    if (sw_unlikely(ZVAL_IS_FALSE(return_value))) {\n        php_swoole_socket_set_error_properties(zobject, socket);\n        zend::object_set(zobject, ZEND_STRL(\"statusCode\"), HTTP_ESTATUS_SERVER_RESET);\n        if (socket->errCode != ETIMEDOUT) {\n            close();\n        }\n        return;\n    }\n    if (sw_unlikely(ZVAL_IS_NULL(return_value))) {\n        ZVAL_FALSE(return_value);\n    }\n}\n\nbool Client::upgrade(const std::string &_path) {\n    defer = false;\n    char buf[SW_WEBSOCKET_KEY_LENGTH + 1];\n    zval *zheaders =\n        sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, zobject, ZEND_STRL(\"requestHeaders\"), 0);\n    zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"requestMethod\"), \"GET\");\n    create_token(SW_WEBSOCKET_KEY_LENGTH, buf);\n    add_assoc_string(zheaders, \"Connection\", \"Upgrade\");\n    add_assoc_string(zheaders, \"Upgrade\", \"websocket\");\n    add_assoc_string(zheaders, \"Sec-WebSocket-Version\", SW_WEBSOCKET_VERSION);\n    add_assoc_str_ex(\n        zheaders, ZEND_STRL(\"Sec-WebSocket-Key\"), php_base64_encode((const uchar *) buf, SW_WEBSOCKET_KEY_LENGTH));\n#ifdef SW_HAVE_ZLIB\n    if (websocket_settings.compression) {\n        add_assoc_string(zheaders, \"Sec-Websocket-Extensions\", SW_WEBSOCKET_EXTENSION_DEFLATE);\n    }\n#endif\n    return exec(_path);\n}\n\nbool Client::push(zval *zdata, zend_long opcode, uint8_t flags, zend_long code) {\n    if (sw_unlikely(!websocket)) {\n        swoole_set_last_error(SW_ERROR_WEBSOCKET_HANDSHAKE_FAILED);\n        php_swoole_fatal_error(E_WARNING, \"websocket handshake failed, cannot push data\");\n        zend_update_property_long(\n            swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"errCode\"), swoole_get_last_error());\n        zend_update_property_string(swoole_http_client_coro_ce,\n                                    SW_Z8_OBJ_P(zobject),\n                                    ZEND_STRL(\"errMsg\"),\n                                    \"websocket handshake failed, cannot push data\");\n        zend::object_set(zobject, ZEND_STRL(\"statusCode\"), HTTP_ESTATUS_CONNECT_FAILED);\n        return false;\n    }\n\n    String *buffer = socket->get_write_buffer();\n    FrameObject frame(zdata, opcode, flags, code);\n\n    if (websocket_settings.mask) {\n        frame.flags |= WebSocket::FLAG_MASK;\n    }\n\n    if (accept_websocket_compression) {\n        sw_set_bit(frame.flags, WebSocket::FLAG_COMPRESS);\n    }\n\n    if (sw_unlikely(!frame.pack(buffer))) {\n        swoole_set_last_error(SW_ERROR_WEBSOCKET_PACK_FAILED);\n        zend_update_property_long(\n            swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"errCode\"), swoole_get_last_error());\n        zend_update_property_string(\n            swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"errMsg\"), \"websocket frame pack failed\");\n        return false;\n    }\n\n    if (socket->send_all(buffer->str, buffer->length) != (ssize_t) buffer->length) {\n        php_swoole_socket_set_error_properties(zobject, socket);\n        zend::object_set(zobject, ZEND_STRL(\"statusCode\"), HTTP_ESTATUS_SERVER_RESET);\n        close();\n        return false;\n    } else {\n        return true;\n    }\n}\n\nvoid Client::reset() {\n    wait_response = false;\n    completed = false;\n    event_stream = false;\n#ifdef SW_HAVE_COMPRESSION\n    compress_method = HTTP_COMPRESS_NONE;\n    compression_error = false;\n#endif\n#ifdef SW_HAVE_ZLIB\n    if (gzip_stream_active) {\n        inflateEnd(&gzip_stream);\n        gzip_stream_active = false;\n    }\n#endif\n#ifdef SW_HAVE_BROTLI\n    if (brotli_decoder_state) {\n        BrotliDecoderDestroyInstance(brotli_decoder_state);\n        brotli_decoder_state = nullptr;\n    }\n#endif\n#ifdef SW_HAVE_ZSTD\n    if (zstd_stream) {\n        ZSTD_freeDStream(zstd_stream);\n        zstd_stream = nullptr;\n    }\n#endif\n    if (has_upload_files) {\n        zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"uploadFiles\"));\n    }\n    if (download_file != nullptr) {\n        download_file.reset();\n        download_file_name.release();\n        download_offset = 0;\n        zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"downloadFile\"));\n        zend_update_property_long(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"downloadOffset\"), 0);\n    }\n}\n\nvoid Client::socket_dtor() {\n    delete tmp_write_buffer;\n    tmp_write_buffer = socket->pop_write_buffer();\n    socket = nullptr;\n    zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL(\"connected\"), 0);\n    zend_update_property_null(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL(\"socket\"));\n    zval_ptr_dtor(&zsocket);\n    ZVAL_NULL(&zsocket);\n}\n\n/**\n * The socket member variables cannot be read after Socket::close(),\n * MUST return to the php layer, otherwise a memory error will occur.\n * The client, mysql client, http2 client also need to follow this coding convention.\n */\nbool Client::close(const bool should_be_reset) {\n    SocketImpl *_socket = socket;\n    if (!_socket) {\n        return false;\n    }\n    if (in_callback) {\n        _socket->get_socket()->close_wait = 1;\n        return true;\n    }\n\n    zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL(\"connected\"), 0);\n    if (!_socket->close()) {\n        php_swoole_socket_set_error_properties(zobject, _socket);\n        return false;\n    }\n    if (should_be_reset) {\n        reset();\n    }\n    return true;\n}\n\nClient::~Client() {\n    close();\n    delete body;\n    delete tmp_write_buffer;\n    delete write_func;\n}\n\nstatic sw_inline HttpClientObject *http_client_coro_fetch_object(zend_object *obj) {\n    return reinterpret_cast<HttpClientObject *>(reinterpret_cast<char *>(obj) -\n                                                swoole_http_client_coro_handlers.offset);\n}\n\nstatic sw_inline Client *http_client_coro_get_client(const zval *zobject) {\n    Client *phc = http_client_coro_fetch_object(Z_OBJ_P(zobject))->client;\n    if (UNEXPECTED(!phc)) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION, \"must call constructor first\");\n    }\n    return phc;\n}\n\nstatic void http_client_coro_free_object(zend_object *object) {\n    HttpClientObject *hcc = http_client_coro_fetch_object(object);\n    if (hcc->client) {\n        delete hcc->client;\n        hcc->client = nullptr;\n    }\n    zend_object_std_dtor(&hcc->std);\n}\n\nstatic zend_object *http_client_coro_create_object(zend_class_entry *ce) {\n    auto *hcc = (HttpClientObject *) zend_object_alloc(sizeof(HttpClientObject), ce);\n    zend_object_std_init(&hcc->std, ce);\n    object_properties_init(&hcc->std, ce);\n    hcc->std.handlers = &swoole_http_client_coro_handlers;\n    return &hcc->std;\n}\n\nvoid php_swoole_http_client_coro_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_http_client_coro,\n                        \"Swoole\\\\Coroutine\\\\Http\\\\Client\",\n                        \"Co\\\\Http\\\\Client\",\n                        swoole_http_client_coro_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_client_coro);\n    SW_SET_CLASS_CLONEABLE(swoole_http_client_coro, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_client_coro, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(\n        swoole_http_client_coro, http_client_coro_create_object, http_client_coro_free_object, HttpClientObject, std);\n    zend_add_parameter_attribute(\n        (zend_function *) zend_hash_str_find_ptr(&swoole_http_client_coro_ce->function_table, SW_STRL(\"setbasicauth\")),\n        1,\n        ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER),\n        0);\n\n    zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL(\"socket\"), ZEND_ACC_PUBLIC);\n\n    // client status\n    zend_declare_property_long(swoole_http_client_coro_ce, ZEND_STRL(\"errCode\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_string(swoole_http_client_coro_ce, ZEND_STRL(\"errMsg\"), \"\", ZEND_ACC_PUBLIC);\n    zend_declare_property_bool(swoole_http_client_coro_ce, ZEND_STRL(\"connected\"), 0, ZEND_ACC_PUBLIC);\n\n    // client info\n    zend_declare_property_string(swoole_http_client_coro_ce, ZEND_STRL(\"host\"), \"\", ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_http_client_coro_ce, ZEND_STRL(\"port\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_bool(swoole_http_client_coro_ce, ZEND_STRL(\"ssl\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL(\"setting\"), ZEND_ACC_PUBLIC);\n\n    // request properties\n    zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL(\"requestMethod\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL(\"requestHeaders\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL(\"requestBody\"), ZEND_ACC_PUBLIC);\n    // always set by API (make it private?)\n    zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL(\"uploadFiles\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL(\"downloadFile\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_http_client_coro_ce, ZEND_STRL(\"downloadOffset\"), 0, ZEND_ACC_PUBLIC);\n\n    // response properties\n    zend_declare_property_long(swoole_http_client_coro_ce, ZEND_STRL(\"statusCode\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL(\"headers\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL(\"set_cookie_headers\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL(\"cookies\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_string(swoole_http_client_coro_ce, ZEND_STRL(\"body\"), \"\", ZEND_ACC_PUBLIC);\n\n    SW_INIT_CLASS_ENTRY_EX(swoole_http_client_coro_exception,\n                           \"Swoole\\\\Coroutine\\\\Http\\\\Client\\\\Exception\",\n                           \"Co\\\\Http\\\\Client\\\\Exception\",\n                           nullptr,\n                           swoole_exception);\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED\", HTTP_ESTATUS_CONNECT_FAILED);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP_CLIENT_ESTATUS_REQUEST_TIMEOUT\", HTTP_ESTATUS_REQUEST_TIMEOUT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET\", HTTP_ESTATUS_SERVER_RESET);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HTTP_CLIENT_ESTATUS_SEND_FAILED\", HTTP_ESTATUS_SEND_FAILED);\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, __construct) {\n    HttpClientObject *hcc = http_client_coro_fetch_object(Z_OBJ_P(ZEND_THIS));\n    char *host;\n    size_t host_len;\n    zend_long port = 0;\n    zend_bool ssl = false;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 3)\n    Z_PARAM_STRING(host, host_len)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(port)\n    Z_PARAM_BOOL(ssl)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zend_update_property_stringl(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"host\"), host, host_len);\n    zend_update_property_long(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"port\"), port);\n    zend_update_property_bool(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"ssl\"), ssl);\n    // check host\n    if (host_len == 0) {\n        zend_throw_exception_ex(swoole_http_client_coro_exception_ce, EINVAL, \"host is empty\");\n        RETURN_FALSE;\n    }\n    hcc->client = new Client(ZEND_THIS, std::string(host, host_len), port, ssl);\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, __destruct) {}\n\nstatic PHP_METHOD(swoole_http_client_coro, set) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n    zval *zset;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ARRAY(zset)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (php_swoole_array_length(zset) == 0) {\n        RETURN_FALSE;\n    } else {\n        zval *zsettings =\n            sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL(\"setting\"), 0);\n        php_array_merge(Z_ARRVAL_P(zsettings), Z_ARRVAL_P(zset));\n        phc->apply_setting(zset);\n        RETURN_TRUE;\n    }\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, getDefer) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n\n    RETURN_BOOL(phc->defer);\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, setDefer) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n    zend_bool defer = true;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_BOOL(defer)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    phc->defer = defer;\n\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, setMethod) {\n    char *method;\n    size_t method_length;\n\n    // Notice: maybe string or array\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STRING(method, method_length)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zend_update_property_stringl(\n        swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"requestMethod\"), method, method_length);\n\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, setHeaders) {\n    zval *headers;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ARRAY_EX(headers, 0, 1)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zend_update_property(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"requestHeaders\"), headers);\n\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, setBasicAuth) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n    char *username, *password;\n    size_t username_len, password_len;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_STRING(username, username_len)\n    Z_PARAM_STRING(password, password_len)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    phc->set_basic_auth(std::string(username, username_len), std::string(password, password_len));\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, setCookies) {\n    zval *cookies;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ARRAY_EX(cookies, 0, 1)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zend_update_property(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"cookies\"), cookies);\n\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, setData) {\n    zval *zdata;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ZVAL(zdata)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zend_update_property(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"requestBody\"), zdata);\n\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, addFile) {\n    char *path;\n    size_t l_path;\n    char *name;\n    size_t l_name;\n    char *type = nullptr;\n    size_t l_type = 0;\n    char *filename = nullptr;\n    size_t l_filename = 0;\n    zend_long offset = 0;\n    zend_long length = 0;\n\n    ZEND_PARSE_PARAMETERS_START(2, 6)\n    Z_PARAM_STRING(path, l_path)\n    Z_PARAM_STRING(name, l_name)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_STRING(type, l_type)\n    Z_PARAM_STRING(filename, l_filename)\n    Z_PARAM_LONG(offset)\n    Z_PARAM_LONG(length)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (offset < 0) {\n        offset = 0;\n    }\n    if (length < 0) {\n        length = 0;\n    }\n    struct stat file_stat;\n    if (stat(path, &file_stat) < 0) {\n        php_swoole_sys_error(E_WARNING, \"stat(%s) failed\", path);\n        RETURN_FALSE;\n    }\n    if (file_stat.st_size == 0) {\n        php_swoole_sys_error(E_WARNING, \"cannot send empty file[%s]\", filename);\n        RETURN_FALSE;\n    }\n    if (file_stat.st_size <= offset) {\n        php_swoole_error(E_WARNING, \"parameter $offset[\" ZEND_LONG_FMT \"] exceeds the file size\", offset);\n        RETURN_FALSE;\n    }\n    if (length > file_stat.st_size - offset) {\n        php_swoole_sys_error(E_WARNING, \"parameter $length[\" ZEND_LONG_FMT \"] exceeds the file size\", length);\n        RETURN_FALSE;\n    }\n    if (length == 0) {\n        length = file_stat.st_size - offset;\n    }\n    if (l_type == 0) {\n        type = (char *) swoole::mime_type::get(path).c_str();\n        l_type = strlen(type);\n    }\n    if (l_filename == 0) {\n        char *dot = strrchr(path, '/');\n        if (dot == nullptr) {\n            filename = path;\n            l_filename = l_path;\n        } else {\n            filename = dot + 1;\n            l_filename = strlen(filename);\n        }\n    }\n\n    zval *zupload_files =\n        sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL(\"uploadFiles\"), 0);\n    zval zupload_file;\n    array_init(&zupload_file);\n    add_assoc_stringl_ex(&zupload_file, ZEND_STRL(\"path\"), path, l_path);\n    add_assoc_stringl_ex(&zupload_file, ZEND_STRL(\"name\"), name, l_name);\n    add_assoc_stringl_ex(&zupload_file, ZEND_STRL(\"filename\"), filename, l_filename);\n    add_assoc_stringl_ex(&zupload_file, ZEND_STRL(\"type\"), type, l_type);\n    add_assoc_long(&zupload_file, \"size\", length);\n    add_assoc_long(&zupload_file, \"offset\", offset);\n\n    RETURN_BOOL(add_next_index_zval(zupload_files, &zupload_file) == SUCCESS);\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, addData) {\n    char *data;\n    size_t l_data;\n    char *name;\n    size_t l_name;\n    char *type = nullptr;\n    size_t l_type = 0;\n    char *filename = nullptr;\n    size_t l_filename = 0;\n\n    ZEND_PARSE_PARAMETERS_START(2, 4)\n    Z_PARAM_STRING(data, l_data)\n    Z_PARAM_STRING(name, l_name)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_STRING(type, l_type)\n    Z_PARAM_STRING(filename, l_filename)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (l_type == 0) {\n        type = (char *) \"application/octet-stream\";\n        l_type = strlen(type);\n    }\n    if (l_filename == 0) {\n        filename = name;\n        l_filename = l_name;\n    }\n\n    zval *zupload_files =\n        sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL(\"uploadFiles\"), 0);\n    zval zupload_file;\n    array_init(&zupload_file);\n    add_assoc_stringl_ex(&zupload_file, ZEND_STRL(\"content\"), data, l_data);\n    add_assoc_stringl_ex(&zupload_file, ZEND_STRL(\"name\"), name, l_name);\n    add_assoc_stringl_ex(&zupload_file, ZEND_STRL(\"filename\"), filename, l_filename);\n    add_assoc_stringl_ex(&zupload_file, ZEND_STRL(\"type\"), type, l_type);\n    add_assoc_long(&zupload_file, \"size\", l_data);\n\n    RETURN_BOOL(add_next_index_zval(zupload_files, &zupload_file) == SUCCESS);\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, execute) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n    char *path = nullptr;\n    size_t path_len = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STRING(path, path_len)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(phc->exec(std::string(path, path_len)));\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, get) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n    char *path = nullptr;\n    size_t path_len = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STRING(path, path_len)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"requestMethod\"), \"GET\");\n\n    RETURN_BOOL(phc->exec(std::string(path, path_len)));\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, post) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n    char *path = nullptr;\n    size_t path_len = 0;\n    zval *post_data;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_STRING(path, path_len)\n    Z_PARAM_ZVAL(post_data)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"requestMethod\"), \"POST\");\n    zend_update_property(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"requestBody\"), post_data);\n\n    RETURN_BOOL(phc->exec(std::string(path, path_len)));\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, download) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n    char *path;\n    size_t path_len;\n    zval *download_file;\n    zend_long offset = 0;\n\n    ZEND_PARSE_PARAMETERS_START(2, 3)\n    Z_PARAM_STRING(path, path_len)\n    Z_PARAM_ZVAL(download_file)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(offset)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zend_update_property(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"downloadFile\"), download_file);\n    zend_update_property_long(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"downloadOffset\"), offset);\n\n    RETURN_BOOL(phc->exec(std::string(path, path_len)));\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, upgrade) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n    char *path = nullptr;\n    size_t path_len = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STRING(path, path_len)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(phc->upgrade(std::string(path, path_len)));\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, push) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n    if (!phc->is_available()) {\n        RETURN_FALSE;\n    }\n\n    zval *zdata;\n    zend_long opcode = WebSocket::OPCODE_TEXT;\n    zval *zflags = nullptr;\n    zend_long flags = WebSocket::FLAG_FIN;\n\n    ZEND_PARSE_PARAMETERS_START(1, 3)\n    Z_PARAM_ZVAL(zdata)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(opcode)\n    Z_PARAM_ZVAL_EX(zflags, 1, 0)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (zflags != nullptr) {\n        flags = zval_get_long(zflags);\n    }\n    SW_CLIENT_PRESERVE_SOCKET(&phc->zsocket);\n    RETURN_BOOL(phc->push(zdata, opcode, flags & WebSocket::FLAGS_ALL));\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, recv) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n    if (!phc->is_available()) {\n        RETURN_FALSE;\n    }\n\n    double timeout = 0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    SW_CLIENT_PRESERVE_SOCKET(&phc->zsocket);\n\n    if (phc->websocket) {\n        phc->recv_websocket_frame(return_value, timeout);\n    } else {\n        RETURN_BOOL(phc->recv_response(timeout));\n    }\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, close) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n    SW_CLIENT_PRESERVE_SOCKET(&phc->zsocket);\n    RETURN_BOOL(phc->close());\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, getBody) {\n    SW_RETURN_PROPERTY(\"body\");\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, getHeaders) {\n    SW_RETURN_PROPERTY(\"headers\");\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, getCookies) {\n    SW_RETURN_PROPERTY(\"cookies\");\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, getStatusCode) {\n    SW_RETURN_PROPERTY(\"statusCode\");\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, getHeaderOut) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n    phc->get_header_out(return_value);\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, getsockname) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n    phc->getsockname(return_value);\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, getpeername) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n    phc->getpeername(return_value);\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, getPeerCert) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n    phc->getpeercert(return_value);\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, ping) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n    if (!phc->is_available() || !phc->websocket) {\n        RETURN_FALSE;\n    }\n\n    zend_string *zdata = zend_empty_string;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_STR(zdata)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zval zpayload = {};\n    ZVAL_STR(&zpayload, zdata);\n    RETURN_BOOL(phc->push(&zpayload, WebSocket::OPCODE_PING, WebSocket::FLAG_FIN));\n}\n\nstatic PHP_METHOD(swoole_http_client_coro, disconnect) {\n    Client *phc = http_client_coro_get_client(ZEND_THIS);\n    if (!phc->is_available() || !phc->websocket) {\n        RETURN_FALSE;\n    }\n\n    zend_long code = WebSocket::CLOSE_NORMAL;\n    zend_string *reason = zend_empty_string;\n\n    ZEND_PARSE_PARAMETERS_START(0, 2)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(code)\n    Z_PARAM_STR(reason)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zval zpayload = {};\n    ZVAL_STR(&zpayload, reason);\n\n    RETVAL_BOOL(phc->push(&zpayload, WebSocket::OPCODE_CLOSE, WebSocket::FLAG_FIN, code));\n    phc->close();  // Regardless of the outcome, the connection will be closed.\n}\n"
  },
  {
    "path": "ext-src/swoole_http_cookie.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: NathanFreeman  <mariasocute@163.com>                         |\n  +----------------------------------------------------------------------+\n */\n#include \"php_swoole_http_server.h\"\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_http_cookie_arginfo.h\"\nEND_EXTERN_C()\n\nusing HttpCookie = swoole::http::Cookie;\n\n#define ILLEGAL_COOKIE_CHARACTER_PRINT \"\\\",\\\", \\\";\\\", \\\" \\\", \\\"\\\\t\\\", \\\"\\\\r\\\", \\\"\\\\n\\\", \\\"\\\\013\\\", or \\\"\\\\014\\\"\"\n#define ILLEGAL_COOKIE_CHARACTER \",; \\t\\r\\n\\013\\014\"\n\nstatic constexpr zend_long maxValidSeconds = 253402300800;\n\nzend_class_entry *swoole_http_cookie_ce;\nstatic zend_object_handlers swoole_http_cookie_handlers;\n\nstruct HttpCookieObject {\n    HttpCookie *cookie;\n    zend_object std;\n};\n\nstatic sw_inline HttpCookieObject *php_swoole_http_cookie_fetch_object(zend_object *obj) {\n    return reinterpret_cast<HttpCookieObject *>(reinterpret_cast<char *>(obj) - swoole_http_cookie_handlers.offset);\n}\n\nstatic HttpCookie *php_swoole_http_get_cookie(const zval *zobject) {\n    return php_swoole_http_cookie_fetch_object(Z_OBJ_P(zobject))->cookie;\n}\n\nHttpCookie *php_swoole_http_get_cooke_safety(const zval *zobject) {\n    HttpCookie *cookie = php_swoole_http_get_cookie(zobject);\n    if (!cookie) {\n        swoole_set_last_error(SW_ERROR_HTTP_COOKIE_UNAVAILABLE);\n        return nullptr;\n    }\n    return cookie;\n}\n\nvoid php_swoole_http_response_set_cookie(const zval *zobject, HttpCookie *cookie) {\n    php_swoole_http_cookie_fetch_object(Z_OBJ_P(zobject))->cookie = cookie;\n}\n\nstatic zend_object *php_swoole_http_cookie_create_object(zend_class_entry *ce) {\n    auto *httpCookieObject = static_cast<HttpCookieObject *>(zend_object_alloc(sizeof(HttpCookieObject), ce));\n    zend_object_std_init(&httpCookieObject->std, ce);\n    object_properties_init(&httpCookieObject->std, ce);\n    httpCookieObject->std.handlers = &swoole_http_cookie_handlers;\n    return &httpCookieObject->std;\n}\n\nstatic void php_swoole_http_cookie_free_object(zend_object *object) {\n    auto *httpCookieObject = php_swoole_http_cookie_fetch_object(object);\n    delete httpCookieObject->cookie;\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_http_cookie, __construct);\nstatic PHP_METHOD(swoole_http_cookie, withName);\nstatic PHP_METHOD(swoole_http_cookie, withValue);\nstatic PHP_METHOD(swoole_http_cookie, withExpires);\nstatic PHP_METHOD(swoole_http_cookie, withPath);\nstatic PHP_METHOD(swoole_http_cookie, withDomain);\nstatic PHP_METHOD(swoole_http_cookie, withSecure);\nstatic PHP_METHOD(swoole_http_cookie, withHttpOnly);\nstatic PHP_METHOD(swoole_http_cookie, withSameSite);\nstatic PHP_METHOD(swoole_http_cookie, withPriority);\nstatic PHP_METHOD(swoole_http_cookie, withPartitioned);\nstatic PHP_METHOD(swoole_http_cookie, toArray);\nstatic PHP_METHOD(swoole_http_cookie, toString);\nstatic PHP_METHOD(swoole_http_cookie, reset);\nSW_EXTERN_C_END\n\n// clang-format off\nconst zend_function_entry swoole_http_cookie_methods[] =\n{\n    PHP_ME(swoole_http_cookie, __construct,      arginfo_class_Swoole_Http_Cookie___construct,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_cookie, withName,         arginfo_class_Swoole_Http_Cookie_withName,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_cookie, withValue,        arginfo_class_Swoole_Http_Cookie_withValue,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_cookie, withExpires,      arginfo_class_Swoole_Http_Cookie_withExpires,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_cookie, withPath,         arginfo_class_Swoole_Http_Cookie_withPath,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_cookie, withDomain,       arginfo_class_Swoole_Http_Cookie_withDomain,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_cookie, withSecure,       arginfo_class_Swoole_Http_Cookie_withSecure,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_cookie, withHttpOnly,     arginfo_class_Swoole_Http_Cookie_withHttpOnly,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_cookie, withSameSite,     arginfo_class_Swoole_Http_Cookie_withSameSite,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_cookie, withPriority,     arginfo_class_Swoole_Http_Cookie_withPriority,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_cookie, withPartitioned,  arginfo_class_Swoole_Http_Cookie_withPartitioned,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_cookie, toString,         arginfo_class_Swoole_Http_Cookie_toString,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_cookie, toArray,          arginfo_class_Swoole_Http_Cookie_toArray,            ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_cookie, reset,            arginfo_class_Swoole_Http_Cookie_reset,              ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_http_cookie_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_http_cookie, \"Swoole\\\\Http\\\\Cookie\", nullptr, swoole_http_cookie_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_cookie);\n    SW_SET_CLASS_CLONEABLE(swoole_http_cookie, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_cookie, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(swoole_http_cookie,\n                               php_swoole_http_cookie_create_object,\n                               php_swoole_http_cookie_free_object,\n                               HttpCookieObject,\n                               std);\n}\n\n#define HTTP_COOKIE_WITH_STR(field)                                                                                    \\\n    if (field) {                                                                                                       \\\n        zend_string_release(field);                                                                                    \\\n    }                                                                                                                  \\\n    if (_##field && ZSTR_LEN(_##field) > 0) {                                                                          \\\n        zend_string_addref(_##field);                                                                                  \\\n        field = _##field;                                                                                              \\\n    } else {                                                                                                           \\\n        field = nullptr;                                                                                               \\\n    }                                                                                                                  \\\n    return this;\n\nHttpCookie *HttpCookie::withName(zend_string *_name) {\n    HTTP_COOKIE_WITH_STR(name);\n}\n\nHttpCookie *HttpCookie::withValue(zend_string *_value) {\n    HTTP_COOKIE_WITH_STR(value);\n}\n\nHttpCookie *HttpCookie::withDomain(zend_string *_domain) {\n    HTTP_COOKIE_WITH_STR(domain);\n}\n\nHttpCookie *HttpCookie::withPath(zend_string *_path) {\n    HTTP_COOKIE_WITH_STR(path);\n}\n\nHttpCookie *HttpCookie::withSameSite(zend_string *_sameSite) {\n    HTTP_COOKIE_WITH_STR(sameSite);\n}\n\nHttpCookie *HttpCookie::withPriority(zend_string *_priority) {\n    HTTP_COOKIE_WITH_STR(priority);\n}\n\nHttpCookie *HttpCookie::withExpires(zend_long _expires) {\n    expires = _expires;\n    return this;\n}\n\nHttpCookie *HttpCookie::withSecure(zend_bool _secure) {\n    secure = _secure;\n    return this;\n}\n\nHttpCookie *HttpCookie::withHttpOnly(zend_bool _httpOnly) {\n    httpOnly = _httpOnly;\n    return this;\n}\n\nHttpCookie *HttpCookie::withPartitioned(zend_bool _partitioned) {\n    partitioned = _partitioned;\n    return this;\n}\n\nzend_string *HttpCookie::toString() {\n    zend_string *date = nullptr;\n    if (name == nullptr || ZSTR_LEN(name) == 0) {\n        php_swoole_error(E_WARNING, \"The name cannot be empty\");\n        return nullptr;\n    }\n\n    if (strpbrk(ZSTR_VAL(name), \"=\" ILLEGAL_COOKIE_CHARACTER) != nullptr) {\n        php_swoole_error(E_WARNING, \"The name cannot contain \\\"=\\\", \" ILLEGAL_COOKIE_CHARACTER_PRINT);\n        return nullptr;\n    }\n\n    smart_str_append(&buffer_, name);\n\n    if (!value) {\n        smart_str_appends(&buffer_, \"=deleted; expires=\");\n\n        date = php_format_date(ZEND_STRL(\"D, d-M-Y H:i:s T\"), 1, 0);\n        smart_str_append(&buffer_, date);\n        smart_str_appends(&buffer_, \"; Max-Age=0\");\n        zend_string_free(date);\n    } else {\n        if (!encode_ && strpbrk(ZSTR_VAL(value), ILLEGAL_COOKIE_CHARACTER) != nullptr) {\n            php_swoole_error(E_WARNING, \"The value cannot contain \" ILLEGAL_COOKIE_CHARACTER_PRINT);\n            return nullptr;\n        }\n\n        smart_str_appendc(&buffer_, '=');\n\n        if (encode_) {\n            zend_string *encoded_value = php_url_encode(ZSTR_VAL(value), ZSTR_LEN(value));\n            smart_str_append(&buffer_, encoded_value);\n            zend_string_free(encoded_value);\n        } else {\n            smart_str_append(&buffer_, value);\n        }\n\n        if (expires > 0) {\n            if (expires >= maxValidSeconds) {\n                php_swoole_error(E_WARNING, \"The expires cannot have a year greater than 9999\");\n                return nullptr;\n            }\n            smart_str_appends(&buffer_, \"; expires=\");\n            date = php_format_date(ZEND_STRL(\"D, d-M-Y H:i:s T\"), expires, 0);\n            smart_str_append(&buffer_, date);\n            smart_str_appends(&buffer_, \"; Max-Age=\");\n\n            double diff = difftime(expires, php_time());\n            smart_str_append_long(&buffer_, (zend_long) (diff >= 0 ? diff : 0));\n            zend_string_free(date);\n        }\n    }\n\n    if (path && ZSTR_LEN(path) > 0) {\n        if (strpbrk(ZSTR_VAL(path), ILLEGAL_COOKIE_CHARACTER) != nullptr) {\n            php_swoole_error(E_WARNING, \"The path option cannot contain \" ILLEGAL_COOKIE_CHARACTER_PRINT);\n            return nullptr;\n        }\n        smart_str_appends(&buffer_, \"; path=\");\n        smart_str_append(&buffer_, path);\n    }\n\n    if (domain && ZSTR_LEN(domain) > 0) {\n        if (strpbrk(ZSTR_VAL(domain), ILLEGAL_COOKIE_CHARACTER) != nullptr) {\n            php_swoole_error(E_WARNING, \"The domain option cannot contain \" ILLEGAL_COOKIE_CHARACTER_PRINT);\n            return nullptr;\n        }\n        smart_str_appends(&buffer_, \"; domain=\");\n        smart_str_append(&buffer_, domain);\n    }\n\n    if (secure) {\n        smart_str_appends(&buffer_, \"; secure\");\n    }\n\n    if (httpOnly) {\n        smart_str_appends(&buffer_, \"; HttpOnly\");\n    }\n\n    if (sameSite && ZSTR_LEN(sameSite) > 0) {\n        smart_str_appends(&buffer_, \"; SameSite=\");\n        smart_str_append(&buffer_, sameSite);\n    }\n\n    if (priority && ZSTR_LEN(priority) > 0) {\n        smart_str_appends(&buffer_, \"; Priority=\");\n        smart_str_append(&buffer_, priority);\n    }\n\n    if (partitioned) {\n        smart_str_appends(&buffer_, \"; Partitioned\");\n    }\n\n    return smart_str_extract(&buffer_);\n}\n\nvoid HttpCookie::reset() {\n    expires = 0;\n    secure = false;\n    httpOnly = false;\n    partitioned = false;\n    encode_ = true;\n\n    if (name) {\n        zend_string_release(name);\n        name = nullptr;\n    }\n\n    if (value) {\n        zend_string_release(value);\n        value = nullptr;\n    }\n\n    if (path) {\n        zend_string_release(path);\n        path = nullptr;\n    }\n\n    if (domain) {\n        zend_string_release(domain);\n        domain = nullptr;\n    }\n\n    if (sameSite) {\n        zend_string_release(sameSite);\n        sameSite = nullptr;\n    }\n\n    if (priority) {\n        zend_string_release(priority);\n        priority = nullptr;\n    }\n\n    smart_str_free_ex(&buffer_, false);\n}\n\n#define HTTP_COOKIE_ADD_STR_TO_ARRAY(field)                                                                            \\\n    if (field) {                                                                                                       \\\n        add_assoc_str(return_value, #field, field);                                                                    \\\n    } else {                                                                                                           \\\n        add_assoc_string(return_value, #field, \"\");                                                                    \\\n    }\n\nvoid HttpCookie::toArray(zval *return_value) const {\n    array_init(return_value);\n\n    HTTP_COOKIE_ADD_STR_TO_ARRAY(name);\n    HTTP_COOKIE_ADD_STR_TO_ARRAY(value);\n    HTTP_COOKIE_ADD_STR_TO_ARRAY(path);\n    HTTP_COOKIE_ADD_STR_TO_ARRAY(domain);\n    HTTP_COOKIE_ADD_STR_TO_ARRAY(sameSite);\n    HTTP_COOKIE_ADD_STR_TO_ARRAY(priority);\n\n    add_assoc_bool(return_value, \"encode\", encode_);\n    add_assoc_long(return_value, \"expires\", expires);\n    add_assoc_bool(return_value, \"secure\", secure);\n    add_assoc_bool(return_value, \"httpOnly\", httpOnly);\n    add_assoc_bool(return_value, \"partitioned\", partitioned);\n}\n\nHttpCookie::~Cookie() {\n    reset();\n}\n\nstatic PHP_METHOD(swoole_http_cookie, __construct) {\n    zend_bool encode = true;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_BOOL(encode)\n    ZEND_PARSE_PARAMETERS_END();\n\n    php_swoole_http_response_set_cookie(ZEND_THIS, new HttpCookie(encode));\n}\n\n#define PHP_METHOD_HTTP_COOKIE_WITH_STR(field)                                                                         \\\n    zend_string *field;                                                                                                \\\n    HttpCookie *cookie = php_swoole_http_get_cooke_safety(ZEND_THIS);                                                  \\\n                                                                                                                       \\\n    ZEND_PARSE_PARAMETERS_START(1, 1)                                                                                  \\\n    Z_PARAM_STR(field)                                                                                                 \\\n    ZEND_PARSE_PARAMETERS_END();                                                                                       \\\n                                                                                                                       \\\n    cookie->with##field(field);                                                                                        \\\n    RETURN_ZVAL(ZEND_THIS, 1, 0);\n\n#define PHP_METHOD_HTTP_COOKIE_WITH_BOOL(field)                                                                        \\\n    zend_bool field = false;                                                                                           \\\n    HttpCookie *cookie = php_swoole_http_get_cooke_safety(ZEND_THIS);                                                  \\\n                                                                                                                       \\\n    ZEND_PARSE_PARAMETERS_START(0, 1)                                                                                  \\\n    Z_PARAM_OPTIONAL                                                                                                   \\\n    Z_PARAM_BOOL(field)                                                                                                \\\n    ZEND_PARSE_PARAMETERS_END();                                                                                       \\\n                                                                                                                       \\\n    cookie->with##field(field);                                                                                        \\\n    RETURN_ZVAL(ZEND_THIS, 1, 0);\n\nstatic PHP_METHOD(swoole_http_cookie, withName) {\n    PHP_METHOD_HTTP_COOKIE_WITH_STR(Name);\n}\n\nstatic PHP_METHOD(swoole_http_cookie, withValue) {\n    PHP_METHOD_HTTP_COOKIE_WITH_STR(Value);\n}\n\nstatic PHP_METHOD(swoole_http_cookie, withExpires) {\n    zend_long expires = 0;\n    HttpCookie *cookie = php_swoole_http_get_cooke_safety(ZEND_THIS);\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(expires)\n    ZEND_PARSE_PARAMETERS_END();\n\n    cookie->withExpires(expires);\n    RETURN_ZVAL(ZEND_THIS, 1, 0);\n}\n\nstatic PHP_METHOD(swoole_http_cookie, withPath) {\n    PHP_METHOD_HTTP_COOKIE_WITH_STR(Path);\n}\n\nstatic PHP_METHOD(swoole_http_cookie, withDomain) {\n    PHP_METHOD_HTTP_COOKIE_WITH_STR(Domain);\n}\n\nstatic PHP_METHOD(swoole_http_cookie, withSecure) {\n    PHP_METHOD_HTTP_COOKIE_WITH_BOOL(Secure);\n}\n\nstatic PHP_METHOD(swoole_http_cookie, withHttpOnly) {\n    PHP_METHOD_HTTP_COOKIE_WITH_BOOL(HttpOnly);\n}\n\nstatic PHP_METHOD(swoole_http_cookie, withSameSite) {\n    PHP_METHOD_HTTP_COOKIE_WITH_STR(SameSite);\n}\n\nstatic PHP_METHOD(swoole_http_cookie, withPriority) {\n    PHP_METHOD_HTTP_COOKIE_WITH_STR(Priority);\n}\n\nstatic PHP_METHOD(swoole_http_cookie, withPartitioned) {\n    PHP_METHOD_HTTP_COOKIE_WITH_BOOL(Partitioned);\n}\n\nstatic PHP_METHOD(swoole_http_cookie, toString) {\n    auto cookie = php_swoole_http_get_cooke_safety(ZEND_THIS);\n    auto cookie_str = cookie->toString();\n    if (!cookie_str) {\n        cookie->reset();\n        RETURN_FALSE;\n    }\n    ZVAL_STR(return_value, cookie_str);\n}\n\nstatic PHP_METHOD(swoole_http_cookie, toArray) {\n    php_swoole_http_get_cooke_safety(ZEND_THIS)->toArray(return_value);\n}\n\nstatic PHP_METHOD(swoole_http_cookie, reset) {\n    php_swoole_http_get_cooke_safety(ZEND_THIS)->reset();\n}\n"
  },
  {
    "path": "ext-src/swoole_http_request.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_http_server.h\"\n\nSW_EXTERN_C_BEGIN\n#include \"ext/standard/url.h\"\n#include \"stubs/php_swoole_http_request_arginfo.h\"\n#include \"thirdparty/php/main/SAPI.h\"\nSW_EXTERN_C_END\n\nenum http_upload_errno {\n    HTTP_UPLOAD_ERR_OK = 0,\n    HTTP_UPLOAD_ERR_INI_SIZE,\n    HTTP_UPLOAD_ERR_FORM_SIZE,\n    HTTP_UPLOAD_ERR_PARTIAL,\n    HTTP_UPLOAD_ERR_NO_FILE,\n    HTTP_UPLOAD_ERR_NO_TMP_DIR = 6,\n    HTTP_UPLOAD_ERR_CANT_WRITE,\n    HTTP_UPLOAD_ERR_FILE_READY,\n};\n\nusing HttpRequest = swoole::http::Request;\nusing HttpContext = swoole::http::Context;\nusing swoole::Connection;\nusing swoole::ListenPort;\nusing swoole::microtime;\nusing swoole::Server;\nusing swoole::http_server::ParseCookieCallback;\n\nstatic int http_request_on_url(llhttp_t *parser, const char *at, size_t length);\nstatic int http_request_on_body(llhttp_t *parser, const char *at, size_t length);\nstatic int http_request_on_header_field(llhttp_t *parser, const char *at, size_t length);\nstatic int http_request_on_header_value(llhttp_t *parser, const char *at, size_t length);\nstatic int http_request_on_headers_complete(llhttp_t *parser);\nstatic int http_request_message_complete(llhttp_t *parser);\n\nstatic int multipart_body_on_header_field(multipart_parser *p, const char *at, size_t length);\nstatic int multipart_body_on_header_value(multipart_parser *p, const char *at, size_t length);\nstatic int multipart_body_on_data(multipart_parser *p, const char *at, size_t length);\nstatic int multipart_body_on_header_complete(multipart_parser *p);\nstatic int multipart_body_on_data_end(multipart_parser *p);\n\nstatic HttpContext *http_request_get_and_check_context(const zval *zobject) {\n    auto *ctx = php_swoole_http_request_get_context(zobject);\n    if (!ctx) {\n        swoole_set_last_error(SW_ERROR_HTTP_CONTEXT_UNAVAILABLE);\n    }\n    return ctx;\n}\n\nstatic inline char *http_trim_double_quote(char *ptr, size_t *len) {\n    size_t i;\n    char *tmp = ptr;\n\n    // ltrim('\"')\n    for (i = 0; i < *len; i++) {\n        if (tmp[0] == '\"') {\n            (*len)--;\n            tmp++;\n            continue;\n        } else {\n            break;\n        }\n    }\n    // rtrim('\"')\n    for (i = (*len); i > 0; i--) {\n        if (tmp[i - 1] == '\"') {\n            tmp[i - 1] = 0;\n            (*len)--;\n            continue;\n        } else {\n            break;\n        }\n    }\n    return tmp;\n}\n\n// clang-format off\nstatic constexpr llhttp_settings_t http_parser_settings =\n{\n    nullptr,                                // on_message_begin\n    nullptr,                                // on_protocol\n    http_request_on_url,                    // on_url\n    nullptr,                                // on_status\n    nullptr,                                // on_method\n    nullptr,                                // on_version\n    http_request_on_header_field,           // on_header_field\n    http_request_on_header_value,           // on_header_value\n    nullptr,                                // on_chunk_extension_name\n    nullptr,                                // on_chunk_extension_value\n    http_request_on_headers_complete,       // on_headers_complete\n    http_request_on_body,                   // on_body\n    http_request_message_complete,          // on_message_complete\n    nullptr,                                // on_protocol_complete\n    nullptr,                                // on_url_complete\n    nullptr,                                // on_status_complete\n    nullptr,                                // on_method_complete\n    nullptr,                                // on_version_complete\n    nullptr,                                // on_header_field_complete\n    nullptr,                                // on_header_value_complete\n    nullptr,                                // on_chunk_extension_name_complete\n    nullptr,                                // on_chunk_extension_value_complete\n    nullptr,                                // on_chunk_header\n    nullptr,                                // on_chunk_complete\n    nullptr,                                // on_reset\n};\n\nstatic constexpr multipart_parser_settings mt_parser_settings =\n{\n    multipart_body_on_header_field,\n    multipart_body_on_header_value,\n    multipart_body_on_data,\n    nullptr,\n    multipart_body_on_header_complete,\n    multipart_body_on_data_end,\n    nullptr,\n};\n// clang-format on\n\nsize_t HttpContext::parse(const char *data, size_t length) {\n    return swoole_llhttp_parser_execute(&parser, &http_parser_settings, data, length);\n}\n\nbool HttpContext::parse_multipart_data(const char *at, size_t length) const {\n    ssize_t n = multipart_parser_execute(mt_parser, at, length);\n    if (n < 0) {\n        int l_error = multipart_parser_error_msg(mt_parser, sw_tg_buffer()->str, sw_tg_buffer()->size);\n        swoole_error_log(SW_LOG_NOTICE,\n                         SW_ERROR_SERVER_INVALID_REQUEST,\n                         \"parse multipart body failed, reason: %.*s\",\n                         l_error,\n                         sw_tg_buffer()->str);\n        return false;\n    } else if (n != (ssize_t) length) {\n        swoole_error_log(SW_LOG_NOTICE,\n                         SW_ERROR_SERVER_INVALID_REQUEST,\n                         \"parse multipart body failed, %lu/%zu bytes processed\",\n                         n,\n                         length);\n        return false;\n    }\n    return true;\n}\n\nzend_class_entry *swoole_http_request_ce;\nstatic zend_object_handlers swoole_http_request_handlers;\n\nstruct HttpRequestObject {\n    HttpContext *ctx;\n    zend_object std;\n};\n\nstatic sw_inline HttpRequestObject *http_request_fetch_object(zend_object *obj) {\n    return reinterpret_cast<HttpRequestObject *>(reinterpret_cast<char *>(obj) - swoole_http_request_handlers.offset);\n}\n\nHttpContext *php_swoole_http_request_get_context(const zval *zobject) {\n    return http_request_fetch_object(Z_OBJ_P(zobject))->ctx;\n}\n\nvoid php_swoole_http_request_set_context(const zval *zobject, HttpContext *ctx) {\n    http_request_fetch_object(Z_OBJ_P(zobject))->ctx = ctx;\n}\n\nstatic void http_request_free_object(zend_object *object) {\n    auto *request = http_request_fetch_object(object);\n    auto *ctx = request->ctx;\n\n    if (ctx) {\n        zval *ztmpfiles = ctx->request.ztmpfiles;\n        if (ztmpfiles && ZVAL_IS_ARRAY(ztmpfiles)) {\n            zval *z_file_path;\n            SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmpfiles), z_file_path) {\n                if (Z_TYPE_P(z_file_path) != IS_STRING) {\n                    continue;\n                }\n                unlink(Z_STRVAL_P(z_file_path));\n                if (SG(rfc1867_uploaded_files)) {\n                    zend_hash_str_del(SG(rfc1867_uploaded_files), Z_STRVAL_P(z_file_path), Z_STRLEN_P(z_file_path));\n                }\n            }\n            SW_HASHTABLE_FOREACH_END();\n        }\n        ctx->request.zobject = nullptr;\n        ctx->free();\n    }\n\n    zend_object_std_dtor(&request->std);\n}\n\nstatic zend_object *http_request_create_object(zend_class_entry *ce) {\n    auto *request = static_cast<HttpRequestObject *>(zend_object_alloc(sizeof(HttpRequestObject), ce));\n    zend_object_std_init(&request->std, ce);\n    object_properties_init(&request->std, ce);\n    request->std.handlers = &swoole_http_request_handlers;\n    return &request->std;\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_http_request, getData);\nstatic PHP_METHOD(swoole_http_request, create);\nstatic PHP_METHOD(swoole_http_request, parse);\nstatic PHP_METHOD(swoole_http_request, isCompleted);\nstatic PHP_METHOD(swoole_http_request, getMethod);\nstatic PHP_METHOD(swoole_http_request, getContent);\nSW_EXTERN_C_END\n\n// clang-format off\nconst zend_function_entry swoole_http_request_methods[] =\n{\n    PHP_ME(swoole_http_request, getContent,                 arginfo_class_Swoole_Http_Request_getContent,  ZEND_ACC_PUBLIC)\n    PHP_MALIAS(swoole_http_request, rawContent, getContent, arginfo_class_Swoole_Http_Request_getContent,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_request, getData,                    arginfo_class_Swoole_Http_Request_getData,     ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_request, create,                     arginfo_class_Swoole_Http_Request_create,      ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_http_request, parse,                      arginfo_class_Swoole_Http_Request_parse,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_request, isCompleted,                arginfo_class_Swoole_Http_Request_isCompleted, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_request, getMethod,                  arginfo_class_Swoole_Http_Request_getMethod,   ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_http_request_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_http_request, \"Swoole\\\\Http\\\\Request\", nullptr, swoole_http_request_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_request);\n    SW_SET_CLASS_CLONEABLE(swoole_http_request, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_request, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(\n        swoole_http_request, http_request_create_object, http_request_free_object, HttpRequestObject, std);\n\n    zend_declare_property_long(swoole_http_request_ce, ZEND_STRL(\"fd\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_http_request_ce, ZEND_STRL(\"streamId\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_request_ce, ZEND_STRL(\"header\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_request_ce, ZEND_STRL(\"server\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_request_ce, ZEND_STRL(\"cookie\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_request_ce, ZEND_STRL(\"get\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_request_ce, ZEND_STRL(\"files\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_request_ce, ZEND_STRL(\"post\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_request_ce, ZEND_STRL(\"tmpfiles\"), ZEND_ACC_PUBLIC);\n}\n\nstatic int http_request_on_url(llhttp_t *parser, const char *at, size_t length) {\n    const char *query_start = (const char *) memchr(at, '?', length);\n    size_t path_len = query_start ? (size_t) (query_start - at) : length;\n\n    auto *ctx = static_cast<HttpContext *>(parser->data);\n    ctx->request.path = estrndup(at, path_len);\n    ctx->request.path_len = path_len;\n\n    if (!query_start || (length - path_len) <= 1) {\n        return 0;\n    }\n\n    const char *query_str = query_start + 1;\n    size_t query_len = length - path_len - 1;\n\n    zval tmp;\n    HashTable *ht = Z_ARR_P(ctx->request.zserver);\n    ZVAL_STRINGL(&tmp, (char *) query_str, query_len);\n    http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_QUERY_STRING), &tmp);\n\n    // parse url params\n    swoole_php_treat_data(\n        PARSE_STRING,\n        estrndup((char *) query_str, query_len),  // it will be freed by treat_data\n        swoole_http_init_and_read_property(\n            swoole_http_request_ce, ctx->request.zobject, &ctx->request.zget, SW_ZSTR_KNOWN(SW_ZEND_STR_GET)));\n    return 0;\n}\n\nstatic int http_request_on_header_field(llhttp_t *parser, const char *at, size_t length) {\n    auto *ctx = static_cast<HttpContext *>(parser->data);\n    ctx->current_header_name = at;\n    ctx->current_header_name_len = length;\n    return 0;\n}\n\nbool HttpContext::init_multipart_parser(const char *boundary_str, int boundary_len) {\n    mt_parser = multipart_parser_init(boundary_str, boundary_len, &mt_parser_settings);\n    if (!mt_parser) {\n        php_swoole_fatal_error(E_WARNING, \"multipart_parser_init() failed\");\n        return false;\n    }\n    form_data_buffer = new String(SW_BUFFER_SIZE_STD);\n    mt_parser->data = this;\n    return true;\n}\n\nbool HttpContext::get_multipart_boundary(\n    const char *at, size_t length, size_t offset, char **out_boundary_str, int *out_boundary_len) {\n    if (!http_server::parse_multipart_boundary(at, length, offset, out_boundary_str, out_boundary_len)) {\n        swoole_warning(\"boundary of multipart/form-data not found, fd:%ld\", fd);\n        /* make it same with protocol error */\n        parser.error = HPE_INVALID_HEADER_TOKEN;\n        return false;\n    }\n    return true;\n}\n\nvoid swoole_http_parse_cookie(zval *zcookies, const char *at, size_t length) {\n    if (length == 0) {\n        return;\n    }\n\n    swoole_php_treat_data(PARSE_COOKIE, estrndup(at, length), zcookies);\n}\n\nstatic void http_request_add_upload_file(HttpContext *ctx, const char *file, size_t l_file) {\n    zval *zfiles = swoole_http_init_and_read_property(\n        swoole_http_request_ce, ctx->request.zobject, &ctx->request.ztmpfiles, SW_ZSTR_KNOWN(SW_ZEND_STR_TMPFILES));\n    add_next_index_stringl(zfiles, file, l_file);\n    // support is_upload_file\n    zend_hash_str_add_ptr(SG(rfc1867_uploaded_files), file, l_file, (char *) file);\n}\n\nbool swoole_http_token_list_contains_value(const char *at, size_t length, const char *value) {\n    if (0 == length) {\n        return false;\n    }\n    if (SW_STRCASEEQ(at, length, value)) {\n        return true;\n    }\n\n    char *var;\n    const char *separator = \",\\0\";\n    char *strtok_buf = nullptr;\n    size_t var_len;\n\n    char *_c = sw_tg_buffer()->str;\n    memcpy(_c, at, length);\n    _c[length] = '\\0';\n\n    var = php_strtok_r(_c, separator, &strtok_buf);\n    while (var) {\n        var_len = swoole::ltrim(&var, strlen(var));\n        var_len = swoole::rtrim(var, var_len);\n        if (swoole_strcaseeq(var, var_len, value, strlen(value))) {\n            return true;\n        }\n        var = php_strtok_r(nullptr, separator, &strtok_buf);\n    }\n    return false;\n}\n\nstatic int http_request_on_header_value(llhttp_t *parser, const char *at, size_t length) {\n    auto *ctx = static_cast<HttpContext *>(parser->data);\n    zval *zheader = ctx->request.zheader;\n    const char *header_name = ctx->current_header_name;\n    size_t header_len = ctx->current_header_name_len;\n\n    if (ctx->parse_cookie && SW_STRCASEEQ(header_name, header_len, \"cookie\")) {\n        zval *zcookie = swoole_http_init_and_read_property(\n            swoole_http_request_ce, ctx->request.zobject, &ctx->request.zcookie, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE));\n        swoole_http_parse_cookie(zcookie, at, length);\n        return 0;\n    } else if (SW_STRCASEEQ(header_name, header_len, \"upgrade\") &&\n               swoole_http_token_list_contains_value(at, length, \"websocket\")) {\n        ctx->websocket = 1;\n        if (ctx->is_co_socket()) {\n            goto _add_header;\n        }\n        auto *serv = ctx->get_async_server();\n        Connection *conn = serv->get_connection_by_session_id(ctx->fd);\n        if (!conn) {\n            swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_CLOSED, \"session[%ld] is closed\", ctx->fd);\n            return -1;\n        }\n        ListenPort *port = serv->get_port_by_server_fd(conn->server_fd);\n        if (port->open_websocket_protocol) {\n            conn->websocket_status = swoole::websocket::STATUS_CONNECTION;\n        }\n    } else if ((parser->method == HTTP_POST || parser->method == HTTP_PUT || parser->method == HTTP_DELETE ||\n                parser->method == HTTP_PATCH) &&\n               SW_STRCASEEQ(header_name, header_len, \"content-type\")) {\n        if (SW_STR_ISTARTS_WITH(at, length, \"application/x-www-form-urlencoded\")) {\n            ctx->request.post_form_urlencoded = 1;\n        } else if (SW_STR_ISTARTS_WITH(at, length, \"multipart/form-data\")) {\n            size_t offset = sizeof(\"multipart/form-data\") - 1;\n            char *boundary_str;\n            int boundary_len;\n            if (!ctx->get_multipart_boundary(at, length, offset, &boundary_str, &boundary_len)) {\n                return -1;\n            }\n            swoole_trace_log(SW_TRACE_HTTP, \"form_data, boundary_str=%s\", boundary_str);\n            ctx->init_multipart_parser(boundary_str, boundary_len);\n        }\n    }\n#ifdef SW_HAVE_COMPRESSION\n    else if (ctx->enable_compression && SW_STRCASEEQ(header_name, header_len, \"accept-encoding\")) {\n        ctx->set_compression_method(at, length);\n    }\n#endif\n    else if (SW_STRCASEEQ(header_name, header_len, \"transfer-encoding\") && SW_STR_ISTARTS_WITH(at, length, \"chunked\")) {\n        ctx->recv_chunked = 1;\n    }\n\n_add_header:\n    zval tmp;\n    ZVAL_STRINGL(&tmp, (char *) at, length);\n\n    /**\n     * some common request header key\n     */\n    if (SW_STRCASEEQ(header_name, header_len, \"host\")) {\n        zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_HOST), &tmp);\n    } else if (SW_STRCASEEQ(header_name, header_len, \"user-agent\")) {\n        zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_USER_AGENT), &tmp);\n    } else if (SW_STRCASEEQ(header_name, header_len, \"accept\")) {\n        zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_ACCEPT), &tmp);\n    } else if (SW_STRCASEEQ(header_name, header_len, \"content-type\")) {\n        zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_CONTENT_TYPE), &tmp);\n    } else if (SW_STRCASEEQ(header_name, header_len, \"content-length\")) {\n        zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_CONTENT_LENGTH), &tmp);\n    } else if (SW_STRCASEEQ(header_name, header_len, \"authorization\")) {\n        zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_AUTHORIZATION), &tmp);\n    } else if (SW_STRCASEEQ(header_name, header_len, \"connection\")) {\n        zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_CONNECTION), &tmp);\n    } else if (SW_STRCASEEQ(header_name, header_len, \"accept-encoding\")) {\n        zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_ACCEPT_ENCODING), &tmp);\n    } else {\n        char *new_header_name = estrndup(header_name, header_len);\n        zend_str_tolower_copy(new_header_name, header_name, header_len);\n        zend::array_add_or_merge(zheader, new_header_name, header_len, &tmp);\n        efree(new_header_name);\n    }\n\n    return 0;\n}\n\nstatic int http_request_on_headers_complete(llhttp_t *parser) {\n    auto *ctx = static_cast<HttpContext *>(parser->data);\n    const char *vpath = ctx->request.path, *end = vpath + ctx->request.path_len, *p = end;\n\n    ctx->request.version = parser->http_major * 100 + parser->http_minor;\n    ctx->request.ext = end;\n    ctx->request.ext_len = 0;\n\n    while (p > vpath) {\n        --p;\n        if (*p == '.') {\n            ++p;\n            ctx->request.ext = p;\n            ctx->request.ext_len = end - p;\n            break;\n        }\n    }\n\n    HashTable *ht = Z_ARR_P(ctx->request.zserver);\n    http_server_add_server_array(\n        ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_METHOD2), llhttp_method_name((enum llhttp_method) parser->method));\n    http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_URI), ctx->request.path, ctx->request.path_len);\n\n    // path_info should be decoded\n    zend_string *zstr_path = zend_string_init(ctx->request.path, ctx->request.path_len, false);\n    ZSTR_LEN(zstr_path) = php_url_decode(ZSTR_VAL(zstr_path), ZSTR_LEN(zstr_path));\n    http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_PATH_INFO), zstr_path);\n\n    http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_TIME), (zend_long) time(nullptr));\n    http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_TIME_FLOAT), microtime());\n    http_server_add_server_array(\n        ht,\n        SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_PROTOCOL),\n        (ctx->request.version == 101 ? SW_ZSTR_KNOWN(SW_ZEND_STR_HTTP11) : SW_ZSTR_KNOWN(SW_ZEND_STR_HTTP10)));\n\n    ctx->keepalive = llhttp_should_keep_alive(parser);\n    ctx->current_header_name = nullptr;\n\n    return 0;\n}\n\nstatic int multipart_body_on_header_field(multipart_parser *p, const char *at, size_t length) {\n    auto *ctx = static_cast<HttpContext *>(p->data);\n    return http_request_on_header_field(&ctx->parser, at, length);\n}\n\nstatic int multipart_body_on_header_value(multipart_parser *p, const char *at, size_t length) {\n    char value_buf[SW_HTTP_FORM_KEYLEN];\n    size_t value_len;\n    int ret = 0;\n\n    auto *ctx = static_cast<HttpContext *>(p->data);\n    /**\n     * Hash collision attack\n     */\n    if (ctx->input_var_num > PG(max_input_vars)) {\n        php_swoole_error(E_WARNING,\n                         \"Input variables exceeded \" ZEND_LONG_FMT \". \"\n                         \"To increase the limit change max_input_vars in php.ini\",\n                         PG(max_input_vars));\n        return SW_OK;\n    } else {\n        ctx->input_var_num++;\n    }\n\n    size_t header_len = ctx->current_header_name_len;\n    zend::CharPtr _header_name;\n    _header_name.assign_tolower(ctx->current_header_name, header_len);\n    char *header_name = _header_name.get();\n\n    if (SW_STRCASEEQ(header_name, header_len, \"content-disposition\")) {\n        size_t offset = 0;\n        if (swoole_strnpos(at, length, ZEND_STRL(\"form-data;\")) >= 0) {\n            offset += sizeof(\"form-data;\") - 1;\n        } else if (swoole_strnpos(at, length, ZEND_STRL(\"attachment;\")) >= 0) {\n            offset += sizeof(\"attachment;\") - 1;\n        } else {\n            swoole_warning(\"Unsupported Content-Disposition [%.*s]\", (int) length, at);\n            return ret;\n        }\n\n        zval tmp_array;\n        array_init(&tmp_array);\n        swoole_http_parse_cookie(&tmp_array, at + offset, length - offset);\n\n        zval *zform_name;\n        if (!(zform_name = zend_hash_str_find(Z_ARRVAL(tmp_array), ZEND_STRL(\"name\")))) {\n            return ret;\n        }\n\n        if (Z_STRLEN_P(zform_name) >= SW_HTTP_FORM_KEYLEN) {\n            swoole_warning(\"form_name[%s] is too large\", Z_STRVAL_P(zform_name));\n            ret = -1;\n            return ret;\n        }\n\n        swoole_strlcpy(value_buf, Z_STRVAL_P(zform_name), sizeof(value_buf));\n        value_len = Z_STRLEN_P(zform_name);\n        char *tmp = http_trim_double_quote(value_buf, &value_len);\n\n        zval *zfilename;\n        // POST form data\n        if (!(zfilename = zend_hash_str_find(Z_ARRVAL(tmp_array), ZEND_STRL(\"filename\")))) {\n            ctx->current_form_data_name = estrndup(tmp, value_len);\n            ctx->current_form_data_name_len = value_len;\n        }\n        // upload file\n        else {\n            if (Z_STRLEN_P(zfilename) >= SW_HTTP_FORM_KEYLEN) {\n                swoole_warning(\"filename[%s] is too large\", Z_STRVAL_P(zfilename));\n                ret = -1;\n                return ret;\n            }\n            ctx->current_input_name = estrndup(tmp, value_len);\n            ctx->current_input_name_len = value_len;\n\n            zval *z_multipart_header = sw_malloc_zval();\n            array_init(z_multipart_header);\n\n            if (ctx->tmp_content_type) {\n                add_assoc_stringl(z_multipart_header, \"type\", ctx->tmp_content_type, ctx->tmp_content_type_len);\n                ctx->tmp_content_type = nullptr;\n            } else {\n                add_assoc_string(z_multipart_header, \"type\", (char *) \"\");\n            }\n            add_assoc_string(z_multipart_header, \"tmp_name\", (char *) \"\");\n            add_assoc_long(z_multipart_header, \"size\", 0);\n\n            swoole_strlcpy(value_buf, Z_STRVAL_P(zfilename), sizeof(value_buf));\n            value_len = Z_STRLEN_P(zfilename);\n            tmp = http_trim_double_quote(value_buf, &value_len);\n\n            add_assoc_stringl(z_multipart_header, \"name\", tmp, value_len);\n            if (value_len == 0) {\n                add_assoc_long(z_multipart_header, \"error\", HTTP_UPLOAD_ERR_NO_FILE);\n            } else {\n                add_assoc_long(z_multipart_header, \"error\", HTTP_UPLOAD_ERR_OK);\n            }\n            ctx->current_multipart_header = z_multipart_header;\n        }\n        zval_ptr_dtor(&tmp_array);\n    } else if (SW_STRCASEEQ(header_name, header_len, \"content-type\")) {\n        if (ctx->current_multipart_header) {\n            zval *z_multipart_header = ctx->current_multipart_header;\n            zval *zerr = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL(\"error\"));\n            if (zerr && Z_TYPE_P(zerr) == IS_LONG && Z_LVAL_P(zerr) == HTTP_UPLOAD_ERR_OK) {\n                add_assoc_stringl(z_multipart_header, \"type\", (char *) at, length);\n            }\n        } else {\n            ctx->tmp_content_type = at;\n            ctx->tmp_content_type_len = length;\n        }\n    } else if (SW_STRCASEEQ(header_name, header_len, SW_HTTP_UPLOAD_FILE)) {\n        /**\n         * When the \"SW_HTTP_UPLOAD_FILE\" header appears in the request, it indicates that the uploaded file has been\n         * saved in a temporary file. The binary content in the message body will be replaced with the temporary\n         * filename. However, the Content-Length still reflects the original message size, causing llhttp to believe\n         * there is still data to be received. As a result, llhttp fails to trigger the message callback. Therefore, we\n         * need to set `ctx->completed = 1` to indicate that the message processing is complete.\n         */\n        ctx->completed = 1;\n        zval *z_multipart_header = ctx->current_multipart_header;\n        std::string tmp_file(at, length);\n        add_assoc_stringl(z_multipart_header, \"tmp_name\", at, length);\n        add_assoc_long(z_multipart_header, \"error\", HTTP_UPLOAD_ERR_FILE_READY);\n        add_assoc_long(z_multipart_header, \"size\", swoole::file_get_size(tmp_file));\n        http_request_add_upload_file(ctx, tmp_file.c_str(), tmp_file.length());\n    }\n    return ret;\n}\n\nstatic int multipart_body_on_data(multipart_parser *p, const char *at, size_t length) {\n    auto *ctx = static_cast<HttpContext *>(p->data);\n    if (ctx->current_form_data_name) {\n        ctx->form_data_buffer->append(at, length);\n        return 0;\n    }\n    if (p->fp == nullptr) {\n        return 0;\n    }\n    ssize_t n = fwrite(at, sizeof(char), length, p->fp);\n    if (n != (off_t) length) {\n        zval *z_multipart_header = ctx->current_multipart_header;\n        add_assoc_long(z_multipart_header, \"error\", HTTP_UPLOAD_ERR_CANT_WRITE);\n\n        fclose(p->fp);\n        p->fp = nullptr;\n\n        swoole_sys_warning(\"write upload file failed\");\n    }\n    return 0;\n}\n\n#if 0\nstatic void get_random_file_name(char *des, const char *src)\n{\n    unsigned char digest[16] = {};\n    char buf[19] = {};\n    int n = sprintf(buf, \"%s%d\", src, swoole_system_random(0, 9999));\n\n    PHP_MD5_CTX ctx;\n    PHP_MD5Init(&ctx);\n    PHP_MD5Update(&ctx, buf, n);\n    PHP_MD5Final(digest, &ctx);\n    make_digest_ex(des, digest, 16);\n}\n#endif\n\nstatic int multipart_body_on_header_complete(multipart_parser *p) {\n    auto *ctx = static_cast<HttpContext *>(p->data);\n    if (!ctx->current_input_name) {\n        return 0;\n    }\n\n    zval *z_multipart_header = ctx->current_multipart_header;\n    zval *zerr = nullptr;\n    if (!(zerr = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL(\"error\")))) {\n        return 0;\n    }\n    if (Z_TYPE_P(zerr) == IS_LONG && Z_LVAL_P(zerr) != HTTP_UPLOAD_ERR_OK) {\n        return 0;\n    }\n\n    char file_path[SW_HTTP_UPLOAD_TMPDIR_SIZE];\n    sw_snprintf(file_path, SW_HTTP_UPLOAD_TMPDIR_SIZE, \"%s/swoole.upfile.XXXXXX\", ctx->upload_tmp_dir.c_str());\n    int tmpfile = swoole_tmpfile(file_path);\n    if (tmpfile < 0) {\n        return 0;\n    }\n\n    FILE *fp = fdopen(tmpfile, \"wb+\");\n    if (fp == nullptr) {\n        add_assoc_long(z_multipart_header, \"error\", HTTP_UPLOAD_ERR_NO_TMP_DIR);\n        swoole_sys_warning(\"fopen(%s) failed\", file_path);\n        return 0;\n    }\n\n    p->fp = fp;\n    add_assoc_string(z_multipart_header, \"tmp_name\", file_path);\n\n    http_request_add_upload_file(ctx, file_path, strlen(file_path));\n\n    return 0;\n}\n\nstatic int multipart_body_on_data_end(multipart_parser *p) {\n    auto *ctx = static_cast<HttpContext *>(p->data);\n\n    if (ctx->current_form_data_name) {\n        php_register_variable_safe(\n            ctx->current_form_data_name,\n            ctx->form_data_buffer->str,\n            ctx->form_data_buffer->length,\n            swoole_http_init_and_read_property(\n                swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, SW_ZSTR_KNOWN(SW_ZEND_STR_POST)));\n\n        efree(ctx->current_form_data_name);\n        ctx->current_form_data_name = nullptr;\n        ctx->current_form_data_name_len = 0;\n        ctx->form_data_buffer->clear();\n        return 0;\n    }\n\n    if (!ctx->current_input_name) {\n        return 0;\n    }\n\n    zval *z_multipart_header = ctx->current_multipart_header;\n    if (p->fp != nullptr) {\n        long size = swoole::file_get_size(p->fp);\n        add_assoc_long(z_multipart_header, \"size\", size);\n\n        fclose(p->fp);\n        p->fp = nullptr;\n    }\n\n    zval *zerr;\n    if (!(zerr = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL(\"error\")))) {\n        return 0;\n    }\n    if (zval_get_long(zerr) == HTTP_UPLOAD_ERR_FILE_READY) {\n        add_assoc_long(z_multipart_header, \"error\", HTTP_UPLOAD_ERR_OK);\n    }\n\n    zval *zfiles = swoole_http_init_and_read_property(\n        swoole_http_request_ce, ctx->request.zobject, &ctx->request.zfiles, SW_ZSTR_KNOWN(SW_ZEND_STR_FILES));\n\n    int input_path_pos = swoole_strnpos(ctx->current_input_name, ctx->current_input_name_len, ZEND_STRL(\"[\"));\n    if (ctx->parse_files && input_path_pos > 0) {\n        char meta_name[SW_HTTP_FORM_KEYLEN + sizeof(\"[tmp_name]\") - 1];\n        char *input_path = ctx->current_input_name + input_path_pos;\n        char *meta_path = meta_name + input_path_pos;\n        size_t meta_path_len = sizeof(meta_name) - input_path_pos;\n\n        swoole_strlcpy(meta_name, ctx->current_input_name, sizeof(meta_name));\n\n        zval *zname = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL(\"name\"));\n        zval *ztype = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL(\"type\"));\n        zval *zfile = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL(\"tmp_name\"));\n        zval *zerror = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL(\"error\"));\n        zval *zsize = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL(\"size\"));\n\n        sw_snprintf(meta_path, meta_path_len, \"[name]%s\", input_path);\n        php_register_variable_ex(meta_name, zname, zfiles);\n\n        sw_snprintf(meta_path, meta_path_len, \"[type]%s\", input_path);\n        php_register_variable_ex(meta_name, ztype, zfiles);\n\n        sw_snprintf(meta_path, meta_path_len, \"[tmp_name]%s\", input_path);\n        php_register_variable_ex(meta_name, zfile, zfiles);\n\n        sw_snprintf(meta_path, meta_path_len, \"[error]%s\", input_path);\n        php_register_variable_ex(meta_name, zerror, zfiles);\n\n        sw_snprintf(meta_path, meta_path_len, \"[size]%s\", input_path);\n        php_register_variable_ex(meta_name, zsize, zfiles);\n    } else {\n        php_register_variable_ex(ctx->current_input_name, z_multipart_header, zfiles);\n    }\n\n    efree(ctx->current_input_name);\n    ctx->current_input_name = nullptr;\n    ctx->current_input_name_len = 0;\n    efree(ctx->current_multipart_header);\n    ctx->current_multipart_header = nullptr;\n\n    return 0;\n}\n\nstatic int http_request_on_body(llhttp_t *parser, const char *at, size_t length) {\n    if (length == 0) {\n        return 0;\n    }\n\n    auto *ctx = static_cast<HttpContext *>(parser->data);\n    bool is_beginning = (ctx->request.chunked_body ? ctx->request.chunked_body->length : ctx->request.body_length) == 0;\n\n    if (ctx->recv_chunked) {\n        if (ctx->request.chunked_body == nullptr) {\n            ctx->request.chunked_body = new swoole::String(SW_BUFFER_SIZE_STD);\n        }\n        ctx->request.chunked_body->append(at, length);\n    } else {\n        ctx->request.body_at = at - ctx->request.body_length;\n        ctx->request.body_length += length;\n    }\n\n    if (ctx->mt_parser != nullptr) {\n        if (is_beginning) {\n            /* Compatibility: some clients may send extra EOL */\n            do {\n                if (*at != '\\r' && *at != '\\n') {\n                    break;\n                }\n                at++;\n                length--;\n            } while (length != 0);\n        }\n        if (!ctx->parse_multipart_data(at, length)) {\n            return -1;\n        }\n    }\n\n    return ctx->completed ? HPE_PAUSED : 0;\n}\n\nstatic int http_request_message_complete(llhttp_t *parser) {\n    auto *ctx = static_cast<HttpContext *>(parser->data);\n    size_t content_length = ctx->request.chunked_body ? ctx->request.chunked_body->length : ctx->request.body_length;\n\n    if (ctx->request.chunked_body != nullptr && ctx->parse_body && ctx->request.post_form_urlencoded) {\n        /* parse dechunked content */\n        swoole_php_treat_data(\n            PARSE_STRING,\n            estrndup(ctx->request.chunked_body->str, content_length),  // do not free, it will be freed by treat_data\n            swoole_http_init_and_read_property(\n                swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, SW_ZSTR_KNOWN(SW_ZEND_STR_POST)));\n    } else if (!ctx->recv_chunked && ctx->parse_body && ctx->request.post_form_urlencoded && ctx->request.body_at) {\n        swoole_php_treat_data(\n            PARSE_STRING,\n            estrndup(ctx->request.body_at, ctx->request.body_length),  // do not free, it will be freed by treat_data\n            swoole_http_init_and_read_property(\n                swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, SW_ZSTR_KNOWN(SW_ZEND_STR_POST)));\n    }\n    if (ctx->mt_parser) {\n        multipart_parser_free(ctx->mt_parser);\n        ctx->mt_parser = nullptr;\n    }\n    if (ctx->form_data_buffer) {\n        delete ctx->form_data_buffer;\n        ctx->form_data_buffer = nullptr;\n    }\n\n    ctx->completed = 1;\n\n    swoole_trace_log(SW_TRACE_HTTP, \"request body length=%ld\", content_length);\n\n    /* The analysis of the http protocol has been completed, no further processing is needed. */\n    return HPE_PAUSED;\n}\n\n#ifdef SW_HAVE_COMPRESSION\nvoid HttpContext::set_compression_method(const char *accept_encoding, size_t length) {\n#ifdef SW_HAVE_BROTLI\n    if (swoole_strnpos(accept_encoding, length, ZEND_STRL(\"br\")) >= 0) {\n        accept_compression = 1;\n        compression_method = HTTP_COMPRESS_BR;\n    } else\n#endif\n#ifdef SW_HAVE_ZLIB\n        if (swoole_strnpos(accept_encoding, length, ZEND_STRL(\"gzip\")) >= 0) {\n        accept_compression = 1;\n        compression_method = HTTP_COMPRESS_GZIP;\n    } else if (swoole_strnpos(accept_encoding, length, ZEND_STRL(\"deflate\")) >= 0) {\n        accept_compression = 1;\n        compression_method = HTTP_COMPRESS_DEFLATE;\n    } else\n#endif\n#ifdef SW_HAVE_ZSTD\n        if (swoole_strnpos(accept_encoding, length, ZEND_STRL(\"zstd\")) >= 0) {\n        accept_compression = 1;\n        compression_method = HTTP_COMPRESS_ZSTD;\n    } else\n#endif\n    {\n        accept_compression = 0;\n    }\n}\n\nconst char *HttpContext::get_content_encoding() const {\n#ifdef SW_HAVE_ZLIB\n    if (compression_method == HTTP_COMPRESS_GZIP) {\n        return \"gzip\";\n    } else if (compression_method == HTTP_COMPRESS_DEFLATE) {\n        return \"deflate\";\n    } else\n#endif\n#ifdef SW_HAVE_BROTLI\n        if (compression_method == HTTP_COMPRESS_BR) {\n        return \"br\";\n    } else\n#endif\n#ifdef SW_HAVE_ZSTD\n        if (compression_method == HTTP_COMPRESS_ZSTD) {\n        return \"zstd\";\n    } else\n#endif\n    {\n        return nullptr;\n    }\n}\n#endif\n\nstatic PHP_METHOD(swoole_http_request, getContent) {\n    HttpContext *ctx = http_request_get_and_check_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        RETURN_FALSE;\n    }\n\n    HttpRequest *req = &ctx->request;\n    if (req->body_length > 0) {\n        zval *zdata = &req->zdata;\n        RETURN_STRINGL(Z_STRVAL_P(zdata) + Z_STRLEN_P(zdata) - req->body_length, req->body_length);\n    } else if (req->chunked_body && req->chunked_body->length != 0) {\n        RETURN_STRINGL(req->chunked_body->str, req->chunked_body->length);\n    } else if (ctx->get_http2_data_length() > 0) {\n        RETURN_STRINGL(req->h2_data_buffer->str, req->h2_data_buffer->length);\n    }\n\n    RETURN_EMPTY_STRING();\n}\n\nstatic PHP_METHOD(swoole_http_request, getData) {\n    HttpContext *ctx = http_request_get_and_check_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        RETURN_FALSE;\n    }\n\n    if (ctx->http2) {\n        php_swoole_fatal_error(E_WARNING, \"unable to get data from HTTP2 request\");\n        RETURN_FALSE;\n    }\n\n    if (Z_TYPE(ctx->request.zdata) == IS_STRING) {\n        RETURN_ZVAL(&ctx->request.zdata, 1, 0);\n    }\n\n    RETURN_EMPTY_STRING();\n}\n\nstatic PHP_METHOD(swoole_http_request, create) {\n    zval *zoptions = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_ARRAY(zoptions)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    auto *ctx = new HttpContext();\n    object_init_ex(return_value, swoole_http_request_ce);\n    zval *zrequest_object = &ctx->request._zobject;\n    ctx->request.zobject = zrequest_object;\n    *zrequest_object = *return_value;\n    php_swoole_http_request_set_context(zrequest_object, ctx);\n\n    ctx->parse_cookie = 1;\n    ctx->parse_body = 1;\n    ctx->parse_files = 1;\n#ifdef SW_HAVE_COMPRESSION\n    ctx->enable_compression = 1;\n    ctx->compression_level = SW_Z_BEST_SPEED;\n#endif\n    ctx->upload_tmp_dir = \"/tmp\";\n\n    if (zoptions && ZVAL_IS_ARRAY(zoptions)) {\n        char *key;\n        uint32_t keylen;\n        int keytype;\n        zval *zvalue;\n\n        SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zoptions), key, keylen, keytype, zvalue) {\n            if (SW_STRCASEEQ(key, keylen, \"parse_cookie\")) {\n                ctx->parse_cookie = zval_is_true(zvalue);\n            } else if (SW_STRCASEEQ(key, keylen, \"parse_body\")) {\n                ctx->parse_body = zval_is_true(zvalue);\n            } else if (SW_STRCASEEQ(key, keylen, \"parse_files\")) {\n                ctx->parse_files = zval_is_true(zvalue);\n            }\n#ifdef SW_HAVE_COMPRESSION\n            else if (SW_STRCASEEQ(key, keylen, \"enable_compression\")) {\n                ctx->enable_compression = zval_is_true(zvalue);\n            } else if (SW_STRCASEEQ(key, keylen, \"compression_level\")) {\n                ctx->compression_level = zval_get_long(zvalue);\n            }\n#endif\n#ifdef SW_HAVE_ZLIB\n            else if (SW_STRCASEEQ(key, keylen, \"websocket_compression\")) {\n                ctx->websocket_compression = zval_is_true(zvalue);\n            }\n#endif\n            else if (SW_STRCASEEQ(key, keylen, \"upload_tmp_dir\")) {\n                ctx->upload_tmp_dir = zend::String(zvalue).to_std_string();\n            }\n            (void) keytype;\n        }\n        SW_HASHTABLE_FOREACH_END();\n    }\n\n    llhttp_t *parser = &ctx->parser;\n    swoole_llhttp_parser_init(parser, HTTP_REQUEST, (void *) ctx);\n\n    swoole_http_init_and_read_property(\n        swoole_http_request_ce, zrequest_object, &ctx->request.zserver, ZEND_STRL(\"server\"));\n    swoole_http_init_and_read_property(\n        swoole_http_request_ce, zrequest_object, &ctx->request.zheader, ZEND_STRL(\"header\"));\n}\n\nstatic PHP_METHOD(swoole_http_request, parse) {\n    HttpContext *ctx = http_request_get_and_check_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx) || ctx->completed) {\n        RETURN_FALSE;\n    }\n\n    char *str;\n    size_t l_str;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STRING(str, l_str)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (Z_TYPE(ctx->request.zdata) != IS_STRING) {\n        ZVAL_STRINGL(&ctx->request.zdata, str, l_str);\n    } else {\n        size_t len = Z_STRLEN(ctx->request.zdata) + l_str;\n        zend_string *new_str = zend_string_alloc(len + 1, false);\n        memcpy(new_str->val, Z_STRVAL(ctx->request.zdata), Z_STRLEN(ctx->request.zdata));\n        memcpy(new_str->val + Z_STRLEN(ctx->request.zdata), str, l_str);\n        new_str->val[len] = 0;\n        new_str->len = len;\n        zval_dtor(&ctx->request.zdata);\n        ZVAL_STR(&ctx->request.zdata, new_str);\n    }\n\n    RETURN_LONG(ctx->parse(str, l_str));\n}\n\nstatic PHP_METHOD(swoole_http_request, getMethod) {\n    HttpContext *ctx = http_request_get_and_check_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        RETURN_FALSE;\n    }\n    if (ctx->http2) {\n        zval *zmethod = zend_hash_str_find(Z_ARR_P(ctx->request.zserver), ZEND_STRL(\"request_method\"));\n        RETURN_ZVAL(zmethod, 1, 0);\n    } else {\n        const char *method = llhttp_method_name((enum llhttp_method)(ctx->parser).method);\n        RETURN_STRING(method);\n    }\n}\n\nstatic PHP_METHOD(swoole_http_request, isCompleted) {\n    HttpContext *ctx = http_request_get_and_check_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        RETURN_FALSE;\n    }\n    RETURN_BOOL(ctx->completed);\n}\n"
  },
  {
    "path": "ext-src/swoole_http_response.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_http_server.h\"\n#include \"php_swoole_websocket.h\"\n#include \"swoole_util.h\"\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_http_response_arginfo.h\"\nEND_EXTERN_C()\n\nusing swoole::Connection;\nusing swoole::Server;\nusing swoole::String;\nusing swoole::substr_len;\nusing swoole::WebSocketSettings;\nusing swoole::websocket::FrameObject;\n\nusing HttpResponse = swoole::http::Response;\nusing HttpContext = swoole::http::Context;\nusing HttpCookie = swoole::http::Cookie;\n\nnamespace WebSocket = swoole::websocket;\nnamespace HttpServer = swoole::http_server;\n\nzend_class_entry *swoole_http_response_ce;\nstatic zend_object_handlers swoole_http_response_handlers;\n\nstatic SW_THREAD_LOCAL struct {\n    time_t time;\n    zend_string *date = nullptr;\n} date_cache{};\n\nstatic inline void http_response_header_key_format(char *key, int length) {\n    int state = 0;\n    for (int i = 0; i < length; i++) {\n        if (state == 0) {\n            if (key[i] >= 97 && key[i] <= 122) {\n                key[i] -= 32;\n            }\n            state = 1;\n        } else if (key[i] == '-') {\n            state = 0;\n        } else {\n            if (key[i] >= 65 && key[i] <= 90) {\n                key[i] += 32;\n            }\n        }\n    }\n}\n\nString *HttpContext::get_write_buffer() {\n    if (is_co_socket()) {\n        return get_co_socket()->get_write_buffer();\n    } else {\n        if (!write_buffer) {\n            write_buffer = new String(SW_BUFFER_SIZE_STD, sw_php_allocator());\n        }\n        return write_buffer;\n    }\n}\n\nstruct HttpResponseObject {\n    HttpContext *ctx;\n    zend_object std;\n};\n\nstatic HttpResponseObject *http_response_fetch_object(zend_object *obj) {\n    return reinterpret_cast<HttpResponseObject *>(reinterpret_cast<char *>(obj) - swoole_http_response_handlers.offset);\n}\n\nHttpContext *php_swoole_http_response_get_context(const zval *zobject) {\n    return http_response_fetch_object(Z_OBJ_P(zobject))->ctx;\n}\n\nvoid php_swoole_http_response_set_context(const zval *zobject, HttpContext *ctx) {\n    http_response_fetch_object(Z_OBJ_P(zobject))->ctx = ctx;\n}\n\nstatic HttpContext *http_response_get_and_check_context(const zval *zobject) {\n    auto *ctx = php_swoole_http_response_get_context(zobject);\n    if (!ctx || (ctx->end_ || ctx->detached)) {\n        swoole_set_last_error(SW_ERROR_HTTP_CONTEXT_UNAVAILABLE);\n        return nullptr;\n    }\n    return ctx;\n}\n\nstatic void http_response_free_object(zend_object *object) {\n    HttpResponseObject *response = http_response_fetch_object(object);\n    HttpContext *ctx = response->ctx;\n    zval ztmp; /* bool, not required to release it */\n\n    if (ctx) {\n        if (ctx->onAfterResponse) {\n            ctx->onAfterResponse(ctx);\n        }\n        if (!ctx->end_ && (ctx->send_chunked || !ctx->send_header_) && !ctx->detached && sw_reactor()) {\n            if (ctx->response.status == 0) {\n                ctx->response.status = SW_HTTP_INTERNAL_SERVER_ERROR;\n            }\n            if (ctx->is_available()) {\n                ctx->end(nullptr, &ztmp);\n            }\n        }\n        ctx->response.zobject = nullptr;\n        ctx->free();\n    }\n\n    zend_object_std_dtor(&response->std);\n}\n\nstatic zend_object *http_response_create_object(zend_class_entry *ce) {\n    auto *response = static_cast<HttpResponseObject *>(zend_object_alloc(sizeof(HttpResponseObject), ce));\n    zend_object_std_init(&response->std, ce);\n    object_properties_init(&response->std, ce);\n    response->std.handlers = &swoole_http_response_handlers;\n    return &response->std;\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_http_response, write);\nstatic PHP_METHOD(swoole_http_response, end);\nstatic PHP_METHOD(swoole_http_response, sendfile);\nstatic PHP_METHOD(swoole_http_response, redirect);\nstatic PHP_METHOD(swoole_http_response, cookie);\nstatic PHP_METHOD(swoole_http_response, rawcookie);\nstatic PHP_METHOD(swoole_http_response, header);\nstatic PHP_METHOD(swoole_http_response, initHeader);\nstatic PHP_METHOD(swoole_http_response, isWritable);\nstatic PHP_METHOD(swoole_http_response, detach);\nstatic PHP_METHOD(swoole_http_response, create);\n/**\n * for WebSocket Client\n */\nstatic PHP_METHOD(swoole_http_response, upgrade);\nstatic PHP_METHOD(swoole_http_response, push);\nstatic PHP_METHOD(swoole_http_response, recv);\nstatic PHP_METHOD(swoole_http_response, close);\nstatic PHP_METHOD(swoole_http_response, trailer);\nstatic PHP_METHOD(swoole_http_response, ping);\nstatic PHP_METHOD(swoole_http_response, goaway);\nstatic PHP_METHOD(swoole_http_response, status);\nstatic PHP_METHOD(swoole_http_response, disconnect);\nSW_EXTERN_C_END\n\n// clang-format off\nconst zend_function_entry swoole_http_response_methods[] =\n{\n    PHP_ME(swoole_http_response, initHeader,                        arginfo_class_Swoole_Http_Response_initHeader,   ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, isWritable,                        arginfo_class_Swoole_Http_Response_isWritable,   ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, cookie,                            arginfo_class_Swoole_Http_Response_cookie,       ZEND_ACC_PUBLIC)\n    PHP_MALIAS(swoole_http_response, setCookie, cookie,             arginfo_class_Swoole_Http_Response_cookie,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, rawcookie,                         arginfo_class_Swoole_Http_Response_cookie,       ZEND_ACC_PUBLIC)\n    PHP_MALIAS(swoole_http_response, setRawCookie, rawcookie,       arginfo_class_Swoole_Http_Response_cookie,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, status,                            arginfo_class_Swoole_Http_Response_status,       ZEND_ACC_PUBLIC)\n    PHP_MALIAS(swoole_http_response, setStatusCode, status,         arginfo_class_Swoole_Http_Response_status,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, header,                            arginfo_class_Swoole_Http_Response_header,       ZEND_ACC_PUBLIC)\n    PHP_MALIAS(swoole_http_response, setHeader, header,             arginfo_class_Swoole_Http_Response_header,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, trailer,                           arginfo_class_Swoole_Http_Response_trailer,      ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, ping,                              arginfo_class_Swoole_Http_Response_ping,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, goaway,                            arginfo_class_Swoole_Http_Response_goaway,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, write,                             arginfo_class_Swoole_Http_Response_write,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, end,                               arginfo_class_Swoole_Http_Response_end,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, sendfile,                          arginfo_class_Swoole_Http_Response_sendfile,     ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, redirect,                          arginfo_class_Swoole_Http_Response_redirect,     ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, detach,                            arginfo_class_Swoole_Http_Response_detach,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, create,                            arginfo_class_Swoole_Http_Response_create,       ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    /**\n     * WebSocket\n     */\n    PHP_ME(swoole_http_response, upgrade,    arginfo_class_Swoole_Http_Response_upgrade,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, push,       arginfo_class_Swoole_Http_Response_push,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, recv,       arginfo_class_Swoole_Http_Response_recv,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, close,      arginfo_class_Swoole_Http_Response_close,      ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_response, disconnect, arginfo_class_Swoole_Http_Response_disconnect, ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_http_response_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_http_response, \"Swoole\\\\Http\\\\Response\", nullptr, swoole_http_response_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_response);\n    SW_SET_CLASS_CLONEABLE(swoole_http_response, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_response, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(\n        swoole_http_response, http_response_create_object, http_response_free_object, HttpResponseObject, std);\n\n    zend_declare_property_long(swoole_http_response_ce, ZEND_STRL(\"fd\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_response_ce, ZEND_STRL(\"socket\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_response_ce, ZEND_STRL(\"header\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_response_ce, ZEND_STRL(\"cookie\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_response_ce, ZEND_STRL(\"trailer\"), ZEND_ACC_PUBLIC);\n}\n\nvoid php_swoole_http_response_rshutdown() {\n    if (date_cache.date) {\n        zend_string_release(date_cache.date);\n        date_cache = {};\n    }\n}\n\nstatic PHP_METHOD(swoole_http_response, write) {\n    zend_string *sdata;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STR(sdata)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    HttpContext *ctx = http_response_get_and_check_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        RETURN_FALSE;\n    }\n\n#ifdef SW_HAVE_COMPRESSION\n    // Why not enable compression?\n    // If both compression and chunked encoding are enabled,\n    // then the content stream is first compressed, then chunked;\n    // so the chunk encoding itself is not compressed,\n    // **and the data in each chunk is not compressed individually.**\n    // The remote endpoint then decodes the stream by concatenating the chunks and decompressing the result.\n    ctx->accept_compression = 0;\n#endif\n\n    ctx->write(sdata, return_value);\n}\n\nstatic int http_response_parse_header_name(const char *key, size_t keylen) {\n    if (SW_STRCASEEQ(key, keylen, \"Server\")) {\n        return HTTP_HEADER_SERVER;\n    } else if (SW_STRCASEEQ(key, keylen, \"Connection\")) {\n        return HTTP_HEADER_CONNECTION;\n    } else if (SW_STRCASEEQ(key, keylen, \"Date\")) {\n        return HTTP_HEADER_DATE;\n    } else if (SW_STRCASEEQ(key, keylen, \"Content-Length\")) {\n        return HTTP_HEADER_CONTENT_LENGTH;\n    } else if (SW_STRCASEEQ(key, keylen, \"Content-Type\")) {\n        return HTTP_HEADER_CONTENT_TYPE;\n    } else if (SW_STRCASEEQ(key, keylen, \"Transfer-Encoding\")) {\n        return HTTP_HEADER_TRANSFER_ENCODING;\n    } else if (SW_STRCASEEQ(key, keylen, \"Content-Encoding\")) {\n        return HTTP_HEADER_CONTENT_ENCODING;\n    }\n    return 0;\n}\n\nzend_string *php_swoole_http_get_date() {\n    time_t now = time(nullptr);\n    if (now != date_cache.time) {\n        if (date_cache.date) {\n            zend_string_release(date_cache.date);\n        }\n        date_cache.time = now;\n        date_cache.date = php_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), now, 0);\n    }\n    return date_cache.date;\n}\n\nstatic void http_response_set_date_header(String *response) {\n    auto date_str = php_swoole_http_get_date();\n    response->append(ZEND_STRL(\"Date: \"));\n    response->append(ZSTR_VAL(date_str), ZSTR_LEN(date_str));\n    response->append(ZEND_STRL(\"\\r\\n\"));\n}\n\nstatic void http_response_add_custom_header(String *http_buffer, const char *key, size_t l_key, zval *value) {\n    if (ZVAL_IS_NULL(value)) {\n        return;\n    }\n\n    zend::String str_value(value);\n    str_value.rtrim();\n    if (swoole_http_has_crlf(str_value.val(), str_value.len())) {\n        return;\n    }\n\n    http_buffer->append(key, l_key);\n    http_buffer->append(SW_STRL(\": \"));\n    http_buffer->append(str_value.val(), str_value.len());\n    http_buffer->append(SW_STRL(\"\\r\\n\"));\n}\n\nvoid HttpContext::build_header(String *http_buffer, const char *body, size_t length) {\n    assert(send_header_ == 0);\n\n    // http status line\n    http_buffer->append(ZEND_STRL(\"HTTP/1.1 \"));\n    if (response.reason) {\n        http_buffer->append(response.status);\n        http_buffer->append(ZEND_STRL(\" \"));\n        http_buffer->append(response.reason, strlen(response.reason));\n    } else {\n        const char *status = HttpServer::get_status_message(response.status);\n        http_buffer->append((char *) status, strlen(status));\n    }\n    http_buffer->append(ZEND_STRL(\"\\r\\n\"));\n\n    // http headers\n    uint32_t header_flags = 0x0;\n    zval *zheader =\n        sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER), 0);\n    if (ZVAL_IS_ARRAY(zheader)) {\n#ifdef SW_HAVE_COMPRESSION\n        zend_string *content_type = nullptr;\n#endif\n        zval *zvalue;\n        zend_string *string_key;\n        zend_ulong num_key;\n\n        ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(zheader), num_key, string_key, zvalue) {\n            if (!string_key) {\n                string_key = zend_long_to_str(num_key);\n            } else {\n                zend_string_addref(string_key);\n            }\n            zend::String key(string_key, false);\n\n            int key_header = http_response_parse_header_name(ZSTR_VAL(string_key), ZSTR_LEN(string_key));\n\n            if (key_header > 0) {\n#ifdef SW_HAVE_COMPRESSION\n                if (key_header == HTTP_HEADER_CONTENT_TYPE && accept_compression && compression_types) {\n                    content_type = zval_get_string(zvalue);\n                }\n                if (key_header == HTTP_HEADER_CONTENT_ENCODING && ZVAL_IS_STRING(zvalue)) {\n                    accept_compression = 0;\n                }\n                // https://github.com/swoole/swoole-src/issues/4857\n                // https://github.com/swoole/swoole-src/issues/5026\n                if (key_header == HTTP_HEADER_CONTENT_LENGTH && accept_compression) {\n                    swoole_error_log(SW_LOG_WARNING,\n                                     SW_ERROR_HTTP_CONFLICT_HEADER,\n                                     \"The client has set 'Accept-Encoding', 'Content-Length' will be ignored\");\n                    continue;\n                }\n#endif\n                // https://github.com/swoole/swoole-src/issues/4857\n                // https://github.com/swoole/swoole-src/issues/5026\n                if (key_header == HTTP_HEADER_CONTENT_LENGTH && send_chunked) {\n                    swoole_error_log(SW_LOG_WARNING,\n                                     SW_ERROR_HTTP_CONFLICT_HEADER,\n                                     \"You have set 'Transfer-Encoding', 'Content-Length' will be ignored\");\n                    continue;\n                }\n\n                header_flags |= key_header;\n                if (ZVAL_IS_STRING(zvalue) && Z_STRLEN_P(zvalue) == 0) {\n                    continue;\n                }\n            }\n            if (ZVAL_IS_ARRAY(zvalue)) {\n                zval *zvalue_2;\n                SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(zvalue), zvalue_2) {\n                    http_response_add_custom_header(http_buffer, ZSTR_VAL(string_key), ZSTR_LEN(string_key), zvalue_2);\n                }\n                SW_HASHTABLE_FOREACH_END();\n            } else {\n                http_response_add_custom_header(http_buffer, ZSTR_VAL(string_key), ZSTR_LEN(string_key), zvalue);\n            }\n        }\n        ZEND_HASH_FOREACH_END();\n\n#ifdef SW_HAVE_COMPRESSION\n        if (accept_compression && compression_types) {\n            std::string str_content_type = content_type ? std::string(ZSTR_VAL(content_type), ZSTR_LEN(content_type))\n                                                        : std::string(ZEND_STRL(SW_HTTP_DEFAULT_CONTENT_TYPE));\n            accept_compression = compression_types->find(str_content_type) != compression_types->end();\n            if (content_type) {\n                zend_string_release(content_type);\n            }\n        }\n#endif\n    }\n\n    // http cookies\n    zval *zcookie =\n        sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE), 0);\n    if (ZVAL_IS_ARRAY(zcookie)) {\n        zval *zvalue;\n        SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(zcookie), zvalue) {\n            if (Z_TYPE_P(zvalue) != IS_STRING || swoole_http_has_crlf(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue))) {\n                continue;\n            }\n            http_buffer->append(ZEND_STRL(\"Set-Cookie: \"));\n            http_buffer->append(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue));\n            http_buffer->append(ZEND_STRL(\"\\r\\n\"));\n        }\n        SW_HASHTABLE_FOREACH_END();\n    }\n\n    if (!(header_flags & HTTP_HEADER_SERVER)) {\n        http_buffer->append(ZEND_STRL(\"Server: \" SW_HTTP_SERVER_SOFTWARE \"\\r\\n\"));\n    }\n\n    if (!(header_flags & HTTP_HEADER_DATE)) {\n        http_response_set_date_header(http_buffer);\n    }\n\n    // websocket protocol (subsequent header info is unnecessary)\n    if (upgrade == 1) {\n        http_buffer->append(ZEND_STRL(\"\\r\\n\"));\n        send_header_ = 1;\n        return;\n    }\n    if (!(header_flags & HTTP_HEADER_CONNECTION)) {\n        if (keepalive) {\n            http_buffer->append(ZEND_STRL(\"Connection: keep-alive\\r\\n\"));\n        } else {\n            http_buffer->append(ZEND_STRL(\"Connection: close\\r\\n\"));\n        }\n    }\n    if (!(header_flags & HTTP_HEADER_CONTENT_TYPE)) {\n        http_buffer->append(ZEND_STRL(\"Content-Type: \" SW_HTTP_DEFAULT_CONTENT_TYPE \"\\r\\n\"));\n    }\n    if (send_chunked) {\n        SW_ASSERT(length == 0);\n        if (!(header_flags & HTTP_HEADER_TRANSFER_ENCODING)) {\n            http_buffer->append(ZEND_STRL(\"Transfer-Encoding: chunked\\r\\n\"));\n        }\n    }\n    // Content-Length\n    else if (length > 0 || parser.method != HTTP_HEAD) {\n#ifdef SW_HAVE_COMPRESSION\n        if (compress(body, length)) {\n            length = zlib_buffer->length;\n            const char *content_encoding = get_content_encoding();\n            http_buffer->append(ZEND_STRL(\"Content-Encoding: \"));\n            http_buffer->append((char *) content_encoding, strlen(content_encoding));\n            http_buffer->append(ZEND_STRL(\"\\r\\n\"));\n        }\n#endif\n        if (!(header_flags & HTTP_HEADER_CONTENT_LENGTH)) {\n            http_buffer->append(ZEND_STRL(\"Content-Length: \"));\n\n            char result[128];\n            int convert_result = swoole_itoa(result, length);\n            http_buffer->append(result, convert_result);\n            http_buffer->append(ZEND_STRL(\"\\r\\n\"));\n        }\n    }\n\n    http_buffer->append(ZEND_STRL(\"\\r\\n\"));\n    send_header_ = 1;\n}\n\nssize_t HttpContext::build_trailer(String *http_buffer) const {\n    char *buf = sw_tg_buffer()->str;\n    size_t l_buf = sw_tg_buffer()->size;\n    ssize_t ret = 0;\n\n    zval *ztrailer =\n        sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0);\n    uint32_t size = php_swoole_array_length_safe(ztrailer);\n\n    if (size > 0) {\n        const char *key;\n        uint32_t keylen;\n        int type;\n        zval *zvalue;\n\n        SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(ztrailer), key, keylen, type, zvalue) {\n            if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue))) {\n                continue;\n            }\n\n            if (!ZVAL_IS_NULL(zvalue)) {\n                zend::String str_value(zvalue);\n                size_t n = sw_snprintf(\n                    buf, l_buf, \"%.*s: %.*s\\r\\n\", (int) keylen, key, (int) str_value.len(), str_value.val());\n                http_buffer->append(buf, n);\n                ret += n;\n            }\n        }\n        SW_HASHTABLE_FOREACH_END();\n        (void) type;\n        http_buffer->append(ZEND_STRL(\"\\r\\n\"));\n    }\n\n    return ret;\n}\n\n#ifdef SW_HAVE_ZLIB\nvoidpf php_zlib_alloc(voidpf opaque, uInt items, uInt size) {\n    return (voidpf) safe_emalloc(items, size, 0);\n}\n\nvoid php_zlib_free(voidpf opaque, voidpf address) {\n    efree((void *) address);\n}\n#endif\n\n#ifdef SW_HAVE_BROTLI\nvoid *php_brotli_alloc(void *opaque, size_t size) {\n    return emalloc(size);\n}\n\nvoid php_brotli_free(void *opaque, void *address) {\n    efree(address);\n}\n#endif\n\n#ifdef SW_HAVE_COMPRESSION\nbool HttpContext::compress(const char *data, size_t length) {\n#ifdef SW_HAVE_ZLIB\n    int encoding;\n#endif\n\n    if (!accept_compression || length < compression_min_length) {\n        return false;\n    }\n\n    if (false) {\n        return false;\n    }\n#ifdef SW_HAVE_ZLIB\n    // gzip: 0x1f\n    else if (compression_method == HTTP_COMPRESS_GZIP) {\n        encoding = 0x1f;\n    }\n    // deflate: -0xf\n    else if (compression_method == HTTP_COMPRESS_DEFLATE) {\n        encoding = -0xf;\n    }\n#endif\n#ifdef SW_HAVE_BROTLI\n    else if (compression_method == HTTP_COMPRESS_BR) {\n        if (compression_level < BROTLI_MIN_QUALITY) {\n            compression_level = BROTLI_MIN_QUALITY;\n        } else if (compression_level > BROTLI_MAX_QUALITY) {\n            compression_level = BROTLI_MAX_QUALITY;\n        }\n\n        size_t memory_size = BrotliEncoderMaxCompressedSize(length);\n        zlib_buffer = std::make_shared<String>(memory_size);\n\n        size_t input_size = length;\n        const auto *input_buffer = reinterpret_cast<const uint8_t *>(data);\n        size_t encoded_size = zlib_buffer->size;\n        auto *encoded_buffer = reinterpret_cast<uint8_t *>(zlib_buffer->str);\n\n        if (BROTLI_TRUE != BrotliEncoderCompress(compression_level,\n                                                 BROTLI_DEFAULT_WINDOW,\n                                                 BROTLI_DEFAULT_MODE,\n                                                 input_size,\n                                                 input_buffer,\n                                                 &encoded_size,\n                                                 encoded_buffer)) {\n            swoole_warning(\"BrotliEncoderCompress() failed\");\n            return false;\n        } else {\n            zlib_buffer->length = encoded_size;\n            content_compressed = 1;\n            return true;\n        }\n    }\n#endif\n#ifdef SW_HAVE_ZSTD\n    else if (compression_method == HTTP_COMPRESS_ZSTD) {\n        int zstd_compress_level = compression_level;\n        int zstd_max_level = ZSTD_maxCLevel();\n        int zstd_min_level = ZSTD_minCLevel();\n        zstd_compress_level = (zstd_compress_level > zstd_max_level)\n                                  ? zstd_max_level\n                                  : (zstd_compress_level < zstd_min_level ? zstd_min_level : zstd_compress_level);\n\n        size_t compress_size = ZSTD_compressBound(length);\n        zlib_buffer = std::make_shared<String>(compress_size);\n        size_t zstd_compress_result =\n            ZSTD_compress((void *) zlib_buffer->str, compress_size, (void *) data, length, zstd_compress_level);\n\n        if (ZSTD_isError(zstd_compress_result)) {\n            swoole_warning(\"ZSTD_compress() failed, Error: [%s]\", ZSTD_getErrorName(zstd_compress_result));\n            return false;\n        }\n\n        zlib_buffer->length = zstd_compress_result;\n        content_compressed = 1;\n        return true;\n    }\n#endif\n    else {\n        swoole_warning(\"Unknown compression method\");\n        return false;\n    }\n#ifdef SW_HAVE_ZLIB\n    if (compression_level < Z_NO_COMPRESSION) {\n        compression_level = Z_DEFAULT_COMPRESSION;\n    } else if (compression_level == Z_NO_COMPRESSION) {\n        compression_level = Z_BEST_SPEED;\n    } else if (compression_level > Z_BEST_COMPRESSION) {\n        compression_level = Z_BEST_COMPRESSION;\n    }\n\n    size_t memory_size = ((size_t)((double) length * (double) 1.015)) + 10 + 8 + 4 + 1;\n    zlib_buffer = std::make_shared<String>(memory_size);\n\n    z_stream zstream = {};\n\n    zstream.zalloc = php_zlib_alloc;\n    zstream.zfree = php_zlib_free;\n\n    int status =\n        deflateInit2(&zstream, compression_level, Z_DEFLATED, encoding, SW_ZLIB_DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);\n    if (status != Z_OK) {\n        swoole_warning(\"deflateInit2() failed, Error: [%d]\", status);\n        return false;\n    }\n\n    zstream.next_in = (Bytef *) data;\n    zstream.avail_in = length;\n    zstream.next_out = (Bytef *) zlib_buffer->str;\n    zstream.avail_out = zlib_buffer->size;\n\n    status = deflate(&zstream, Z_FINISH);\n    deflateEnd(&zstream);\n    if (status != Z_STREAM_END) {\n        swoole_warning(\"deflate() failed, Error: [%d]\", status);\n        return false;\n    }\n\n    zlib_buffer->length = zstream.total_out;\n    zlib_buffer->offset = 0;\n    content_compressed = 1;\n    return true;\n#endif\n}\n#endif\n\nstatic PHP_METHOD(swoole_http_response, initHeader) {\n    HttpContext *ctx = http_response_get_and_check_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        RETURN_FALSE;\n    }\n    zval *zresponse_object = ctx->response.zobject;\n    swoole_http_init_and_read_property(\n        swoole_http_response_ce, zresponse_object, &ctx->response.zheader, ZEND_STRL(\"header\"));\n    swoole_http_init_and_read_property(\n        swoole_http_response_ce, zresponse_object, &ctx->response.zcookie, ZEND_STRL(\"cookie\"));\n    swoole_http_init_and_read_property(\n        swoole_http_response_ce, zresponse_object, &ctx->response.ztrailer, ZEND_STRL(\"trailer\"));\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_http_response, isWritable) {\n    HttpContext *ctx = php_swoole_http_response_get_context(ZEND_THIS);\n    if (!ctx || (ctx->end_ || ctx->detached)) {\n        RETURN_FALSE;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_http_response, end) {\n    HttpContext *ctx = http_response_get_and_check_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        RETURN_FALSE;\n    }\n\n    zend_string *sdata = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_STR_OR_NULL(sdata)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (ctx->onAfterResponse) {\n        ctx->onAfterResponse(ctx);\n    }\n\n    if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_RESPONSE)) {\n        swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_RESPONSE, ctx);\n    }\n\n    ctx->end(sdata, return_value);\n}\n\nvoid HttpContext::send_trailer(zval *return_value) {\n    String *http_buffer = get_write_buffer();\n\n    http_buffer->clear();\n    if (build_trailer(http_buffer) == 0) {\n        return;\n    }\n    if (!send(this, http_buffer->str, http_buffer->length)) {\n        end_ = 1;\n        close(this);\n        RETURN_FALSE;\n    }\n}\n\nbool HttpContext::send_file(zend_string *file, off_t offset, size_t length) {\n    if (http2) {\n        return swoole_http2_server_send_file(this, file, offset, length);\n    }\n\n    zval *zheader =\n        sw_zend_read_and_convert_property_array(swoole_http_response_ce, response.zobject, ZEND_STRL(\"header\"), 0);\n    if (!zend_hash_str_exists(Z_ARRVAL_P(zheader), ZEND_STRL(\"Content-Type\"))) {\n        auto mime_type = swoole::mime_type::get({file->val, file->len});\n        add_assoc_stringl(zheader, \"Content-Type\", mime_type.c_str(), mime_type.length());\n    }\n\n    if (!send_header_) {\n#ifdef SW_HAVE_COMPRESSION\n        accept_compression = 0;\n#endif\n        String *http_buffer = get_write_buffer();\n        http_buffer->clear();\n\n        build_header(http_buffer, nullptr, length);\n\n        if (!send(this, http_buffer->str, http_buffer->length)) {\n            send_header_ = 0;\n            return false;\n        }\n    }\n\n    if (length > 0 && !sendfile(this, file, offset, length)) {\n        close(this);\n        return false;\n    }\n\n    end_ = 1;\n\n    if (!keepalive) {\n        close(this);\n    }\n    return true;\n}\n\nvoid HttpContext::write(zend_string *sdata, zval *return_value) {\n    if (http2) {\n        RETURN_BOOL(swoole_http2_server_write(this, sdata));\n    }\n\n    String *http_buffer = get_write_buffer();\n    if (!send_header_) {\n        send_chunked = 1;\n        http_buffer->clear();\n        build_header(http_buffer, nullptr, 0);\n        if (!send(this, http_buffer->str, http_buffer->length)) {\n            send_chunked = 0;\n            send_header_ = 0;\n            RETURN_FALSE;\n        }\n    }\n\n    auto data = ZSTR_VAL(sdata);\n    size_t length = ZSTR_LEN(sdata);\n\n    if (length == 0) {\n        php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, \"the data sent must not be empty\");\n        RETURN_FALSE;\n    }\n\n    http_buffer->clear();\n    char *hex_string = swoole_dec2hex(length, 16);\n    int hex_len = strlen(hex_string);\n    //\"%.*s\\r\\n%.*s\\r\\n\", hex_len, hex_string, body.length, body.str\n    http_buffer->append(hex_string, hex_len);\n    http_buffer->append(ZEND_STRL(\"\\r\\n\"));\n    http_buffer->append(data, length);\n    http_buffer->append(ZEND_STRL(\"\\r\\n\"));\n    sw_free(hex_string);\n\n    RETURN_BOOL(send(this, http_buffer->str, http_buffer->length));\n}\n\nvoid HttpContext::end(zend_string *sdata, zval *return_value) {\n    if (http2) {\n        RETURN_BOOL(swoole_http2_server_end(this, sdata));\n    }\n\n    if (send_chunked) {\n        if (sdata && ZSTR_LEN(sdata) > 0) {\n            zval retval;\n            write(sdata, &retval);\n            if (ZVAL_IS_FALSE(&retval)) {\n                RETURN_FALSE;\n            }\n        }\n        if (send_trailer_) {\n            if (!send(this, ZEND_STRL(\"0\\r\\n\"))) {\n                RETURN_FALSE;\n            }\n            send_trailer(return_value);\n            send_trailer_ = 0;\n        } else {\n            if (!send(this, ZEND_STRL(\"0\\r\\n\\r\\n\"))) {\n                RETURN_FALSE;\n            }\n        }\n        send_chunked = 0;\n    } else {\n        const char *data = sdata ? ZSTR_VAL(sdata) : nullptr;\n        size_t length = sdata ? ZSTR_LEN(sdata) : 0;\n\n        String *http_buffer = get_write_buffer();\n        http_buffer->clear();\n\n#ifdef SW_HAVE_ZLIB\n        if (upgrade) {\n            Server *serv = nullptr;\n            Connection *conn = nullptr;\n            if (!is_co_socket()) {\n                serv = get_async_server();\n                conn = serv->get_connection_verify(fd);\n            }\n            bool enable_websocket_compression =\n                is_co_socket() ? websocket_settings.compression : serv->websocket_compression;\n            bool accept_websocket_compression = false;\n            zval *pData;\n            if (enable_websocket_compression && request.zobject &&\n                ((pData = zend_hash_str_find(Z_ARRVAL_P(request.zheader), ZEND_STRL(\"sec-websocket-extensions\")))) &&\n                Z_TYPE_P(pData) == IS_STRING) {\n                const std::string value(Z_STRVAL_P(pData), Z_STRLEN_P(pData));\n                if (value.substr(0, value.find_first_of(';')) == \"permessage-deflate\") {\n                    accept_websocket_compression = true;\n                    set_header(ZEND_STRL(\"Sec-Websocket-Extensions\"), ZEND_STRL(SW_WEBSOCKET_EXTENSION_DEFLATE), false);\n                }\n            }\n            websocket_compression = accept_websocket_compression;\n            if (conn) {\n                conn->websocket_compression = accept_websocket_compression;\n            }\n        }\n#endif\n\n        build_header(http_buffer, data, length);\n\n        if (length > 0) {\n#ifdef SW_HAVE_COMPRESSION\n            if (content_compressed) {\n                data = zlib_buffer->str;\n                length = zlib_buffer->length;\n            }\n#endif\n            // send twice to reduce memory copy\n            if (length > SW_HTTP_MAX_APPEND_DATA) {\n                if (!send(this, http_buffer->str, http_buffer->length)) {\n                    send_header_ = 0;\n                    RETURN_FALSE;\n                }\n                if (!send(this, data, length)) {\n                    end_ = 1;\n                    close(this);\n                    RETURN_FALSE;\n                }\n                goto _skip_copy;\n            } else {\n                http_buffer->append(data, length);\n            }\n        }\n\n        if (!send(this, http_buffer->str, http_buffer->length)) {\n            end_ = 1;\n            close(this);\n            RETURN_FALSE;\n        }\n    }\n\n_skip_copy:\n    if (upgrade && !is_co_socket()) {\n        auto *serv = get_async_server();\n        auto *conn = serv->get_connection_verify(fd);\n\n        if (conn && conn->websocket_status == websocket::STATUS_HANDSHAKE) {\n            if (response.status == 101) {\n                conn->websocket_status = websocket::STATUS_ACTIVE;\n            } else {\n                /* connection should be closed when handshake failed */\n                conn->websocket_status = websocket::STATUS_HANDSHAKE_FAILED;\n                keepalive = 0;\n            }\n        }\n    }\n    if (!keepalive) {\n        close(this);\n    }\n    end_ = 1;\n    RETURN_TRUE;\n}\n\nbool HttpContext::set_header(const char *k, size_t klen, const char *v, size_t vlen, bool format) {\n    zend::Variable ztmp(v, vlen);\n    return set_header(k, klen, ztmp.ptr(), format);\n}\n\nbool HttpContext::set_header(const char *k, size_t klen, const std::string &v, bool format) {\n    zend::Variable ztmp(v);\n    return set_header(k, klen, ztmp.ptr(), format);\n}\n\nbool HttpContext::set_header(const char *k, size_t klen, zval *zvalue, bool format) {\n    if (UNEXPECTED(klen > SW_HTTP_HEADER_KEY_SIZE - 1)) {\n        php_swoole_error(E_WARNING, \"header key is too long\");\n        return false;\n    }\n\n    if (swoole_http_has_crlf(k, klen)) {\n        return false;\n    }\n\n    zval *zheader = swoole_http_init_and_read_property(\n        swoole_http_response_ce, response.zobject, &response.zheader, ZEND_STRL(\"header\"));\n    if (format) {\n        swoole_strlcpy(sw_tg_buffer()->str, k, SW_HTTP_HEADER_KEY_SIZE);\n        if (http2) {\n            swoole_strtolower(sw_tg_buffer()->str, klen);\n        } else {\n            http_response_header_key_format(sw_tg_buffer()->str, klen);\n        }\n        k = sw_tg_buffer()->str;\n    }\n    zend::array_set(zheader, k, klen, zvalue);\n    return true;\n}\n\nstatic PHP_METHOD(swoole_http_response, sendfile) {\n    HttpContext *ctx = http_response_get_and_check_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        RETURN_FALSE;\n    }\n\n    if (ctx->send_chunked) {\n        php_swoole_fatal_error(E_WARNING, \"can't use sendfile when HTTP chunk is enabled\");\n        RETURN_FALSE;\n    }\n\n    zend_string *file;\n    zend_long offset = 0;\n    zend_long length = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 3)\n    Z_PARAM_STR(file)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(offset)\n    Z_PARAM_LONG(length)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (ZSTR_LEN(file) == 0) {\n        php_swoole_error(E_WARNING, \"file name is empty\");\n        RETURN_FALSE;\n    }\n\n    struct stat file_stat;\n    if (stat(file->val, &file_stat) < 0) {\n        php_swoole_sys_error(E_WARNING, \"stat(%s) failed\", file->val);\n        RETURN_FALSE;\n    }\n    if (!S_ISREG(file_stat.st_mode)) {\n        php_swoole_error(E_WARNING, \"parameter $file[%s] given is not a regular file\", file->val);\n        swoole_set_last_error(SW_ERROR_SERVER_IS_NOT_REGULAR_FILE);\n        RETURN_FALSE;\n    }\n    if (file_stat.st_size < offset) {\n        php_swoole_error(E_WARNING, \"parameter $offset[\" ZEND_LONG_FMT \"] exceeds the file size\", offset);\n        RETURN_FALSE;\n    }\n    if (length > file_stat.st_size - offset) {\n        php_swoole_error(E_WARNING, \"parameter $length[\" ZEND_LONG_FMT \"] exceeds the file size\", length);\n        RETURN_FALSE;\n    }\n    if (length == 0) {\n        length = file_stat.st_size - offset;\n    }\n\n    if (ctx->onAfterResponse) {\n        ctx->onAfterResponse(ctx);\n    }\n    if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_RESPONSE)) {\n        swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_RESPONSE, ctx);\n    }\n    RETURN_BOOL(ctx->send_file(file, offset, length));\n}\n\nstatic bool inline http_response_create_cookie(HttpCookie *cookie, zval *zobject) {\n    HttpContext *ctx = http_response_get_and_check_context(zobject);\n\n    zend_string *cookie_str = cookie->toString();\n    if (!cookie_str) {\n        cookie->reset();\n        return false;\n    }\n\n    add_next_index_str(\n        swoole_http_init_and_read_property(\n            swoole_http_response_ce, ctx->response.zobject, &ctx->response.zcookie, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE)),\n        cookie_str);\n\n    return true;\n}\n\nstatic void http_response_set_cookie(INTERNAL_FUNCTION_PARAMETERS, const bool encode) {\n    zval *name_or_object;\n    zend_string *value = nullptr, *path = nullptr, *domain = nullptr, *sameSite = nullptr, *priority = nullptr;\n    zend_long expires = 0;\n    zend_bool secure = false, httpOnly = false, partitioned = false;\n    bool result;\n\n    ZEND_PARSE_PARAMETERS_START(1, 10)\n    Z_PARAM_ZVAL(name_or_object)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_STR(value)\n    Z_PARAM_LONG(expires)\n    Z_PARAM_STR(path)\n    Z_PARAM_STR(domain)\n    Z_PARAM_BOOL(secure)\n    Z_PARAM_BOOL(httpOnly)\n    Z_PARAM_STR(sameSite)\n    Z_PARAM_STR(priority)\n    Z_PARAM_BOOL(partitioned)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (ZVAL_IS_STRING(name_or_object)) {\n        HttpCookie cookie(encode);\n        (&cookie)\n            ->withName(Z_STR_P(name_or_object))\n            ->withValue(value)\n            ->withExpires(expires)\n            ->withPath(path)\n            ->withDomain(domain)\n            ->withSecure(secure)\n            ->withHttpOnly(httpOnly)\n            ->withSameSite(sameSite)\n            ->withPriority(priority)\n            ->withPartitioned(partitioned);\n        result = http_response_create_cookie(&cookie, ZEND_THIS);\n    } else if (ZVAL_IS_OBJECT(name_or_object)) {\n        HttpCookie *cookie = php_swoole_http_get_cooke_safety(name_or_object);\n        result = http_response_create_cookie(cookie, ZEND_THIS);\n    } else {\n        php_swoole_error(E_WARNING, \"The first argument must be a string or an cookie object\");\n        result = false;\n    }\n\n    RETURN_BOOL(result);\n}\n\nstatic PHP_METHOD(swoole_http_response, cookie) {\n    http_response_set_cookie(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);\n}\n\nstatic PHP_METHOD(swoole_http_response, rawcookie) {\n    http_response_set_cookie(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);\n}\n\nstatic PHP_METHOD(swoole_http_response, status) {\n    zend_long http_status;\n    char *reason = nullptr;\n    size_t reason_len = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_LONG(http_status)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_STRING(reason, reason_len)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    HttpContext *ctx = http_response_get_and_check_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        RETURN_FALSE;\n    }\n\n    ctx->response.status = http_status;\n    ctx->response.reason = reason_len > 0 ? estrndup(reason, reason_len) : nullptr;\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_http_response, disconnect) {\n    HttpContext *ctx = php_swoole_http_response_get_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        swoole_set_last_error(SW_ERROR_SESSION_CLOSED);\n        RETURN_FALSE;\n    }\n    if (UNEXPECTED(!ctx->is_co_socket() || !ctx->upgrade)) {\n        php_swoole_fatal_error(E_WARNING, \"fd[%ld] is not a websocket conncetion\", ctx->fd);\n        RETURN_FALSE;\n    }\n\n    zend_long code = WebSocket::CLOSE_NORMAL;\n    zend_string *reason = zend_empty_string;\n    zend_long flags = WebSocket::FLAG_FIN;\n\n    ZEND_PARSE_PARAMETERS_START(0, 2)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(code)\n    Z_PARAM_STR(reason)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    String *http_buffer = ctx->get_write_buffer();\n\n    zval zdata = {};\n    ZVAL_STR(&zdata, reason);\n\n    FrameObject frame(&zdata, WebSocket::OPCODE_CLOSE, flags, code);\n\n    if (sw_unlikely(!frame.pack(http_buffer))) {\n        swoole_set_last_error(SW_ERROR_WEBSOCKET_PACK_FAILED);\n        RETURN_FALSE;\n    }\n\n    RETVAL_BOOL(ctx->send(ctx, http_buffer->str, http_buffer->length));\n    ctx->close(ctx);\n}\n\nstatic PHP_METHOD(swoole_http_response, header) {\n    char *k;\n    size_t klen;\n    zval *zvalue;\n    zend_bool format = true;\n\n    ZEND_PARSE_PARAMETERS_START(2, 3)\n    Z_PARAM_STRING(k, klen)\n    Z_PARAM_ZVAL(zvalue)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_BOOL(format)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    HttpContext *ctx = http_response_get_and_check_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        RETURN_FALSE;\n    }\n    RETURN_BOOL(ctx->set_header(k, klen, zvalue, format));\n}\n\nstatic PHP_METHOD(swoole_http_response, trailer) {\n    char *k, *v;\n    size_t klen, vlen;\n    char key_buf[SW_HTTP_HEADER_KEY_SIZE];\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_STRING(k, klen)\n    Z_PARAM_STRING_EX(v, vlen, 1, 0)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    HttpContext *ctx = http_response_get_and_check_context(ZEND_THIS);\n    if (!ctx) {\n        RETURN_FALSE;\n    }\n    if (UNEXPECTED(klen > SW_HTTP_HEADER_KEY_SIZE - 1)) {\n        php_swoole_error(E_WARNING, \"trailer key is too long\");\n        RETURN_FALSE;\n    }\n    zval *ztrailer = swoole_http_init_and_read_property(\n        swoole_http_response_ce, ctx->response.zobject, &ctx->response.ztrailer, ZEND_STRL(\"trailer\"));\n    swoole_strlcpy(key_buf, k, sizeof(key_buf));\n    swoole_strtolower(key_buf, klen);\n    if (UNEXPECTED(!v)) {\n        add_assoc_null_ex(ztrailer, key_buf, klen);\n    } else {\n        add_assoc_stringl_ex(ztrailer, key_buf, klen, v, vlen);\n    }\n    ctx->send_trailer_ = 1;\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_http_response, ping) {\n    HttpContext *ctx = php_swoole_http_response_get_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        RETURN_FALSE;\n    }\n\n    zend_string *zdata = zend_empty_string;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_STR(zdata)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (ctx->http2) {\n        RETURN_BOOL(swoole_http2_server_ping(ctx));\n    } else if (ctx->websocket) {\n        String *buffer = ctx->get_write_buffer();\n        buffer->clear();\n        WebSocket::encode(buffer, ZSTR_VAL(zdata), ZSTR_LEN(zdata), WebSocket::OPCODE_PING, WebSocket::FLAG_FIN);\n        RETURN_BOOL(ctx->send(ctx, buffer->str, buffer->length));\n    } else {\n        php_swoole_fatal_error(E_WARNING, \"only supports websocket or http2 client\");\n        RETURN_FALSE;\n    }\n}\n\nstatic PHP_METHOD(swoole_http_response, goaway) {\n    HttpContext *ctx = http_response_get_and_check_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        RETURN_FALSE;\n    }\n    if (UNEXPECTED(!ctx->http2)) {\n        php_swoole_fatal_error(E_WARNING, \"fd[%ld] is not a HTTP2 conncetion\", ctx->fd);\n        RETURN_FALSE;\n    }\n\n    zend_long error_code = SW_HTTP2_ERROR_NO_ERROR;\n    zend_string *debug_data = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(0, 2)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(error_code)\n    Z_PARAM_STR_OR_NULL(debug_data)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(swoole_http2_server_goaway(ctx, error_code, debug_data));\n}\n\nstatic PHP_METHOD(swoole_http_response, upgrade) {\n    HttpContext *ctx = http_response_get_and_check_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        RETURN_FALSE;\n    }\n    if (UNEXPECTED(!ctx->is_co_socket())) {\n        php_swoole_fatal_error(E_WARNING, \"async server dose not support protocol upgrade\");\n        RETURN_FALSE;\n    }\n    RETVAL_BOOL(swoole_websocket_handshake(ctx));\n}\n\nstatic PHP_METHOD(swoole_http_response, push) {\n    HttpContext *ctx = php_swoole_http_response_get_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        swoole_set_last_error(SW_ERROR_SESSION_CLOSED);\n        RETURN_FALSE;\n    }\n    if (UNEXPECTED(!ctx->is_co_socket() || !ctx->upgrade)) {\n        php_swoole_fatal_error(E_WARNING, \"fd[%ld] is not a websocket conncetion\", ctx->fd);\n        RETURN_FALSE;\n    }\n\n    zval *zdata;\n    zend_long opcode = WebSocket::OPCODE_TEXT;\n    zval *zflags = nullptr;\n    zend_long flags = WebSocket::FLAG_FIN;\n\n    ZEND_PARSE_PARAMETERS_START(1, 3)\n    Z_PARAM_ZVAL(zdata)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(opcode)\n    Z_PARAM_ZVAL_EX(zflags, 1, 0)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (zflags != nullptr) {\n        flags = zval_get_long(zflags);\n    }\n\n    String *http_buffer = ctx->get_write_buffer();\n\n    FrameObject frame(zdata, opcode, flags);\n    sw_unset_bit(frame.flags, WebSocket::FLAG_MASK);\n\n    if (ctx->websocket_compression) {\n        sw_set_bit(frame.flags, WebSocket::FLAG_COMPRESS);\n    }\n\n    if (sw_unlikely(!frame.pack(http_buffer))) {\n        swoole_set_last_error(SW_ERROR_WEBSOCKET_PACK_FAILED);\n        RETURN_FALSE;\n    }\n\n    RETURN_BOOL(ctx->send(ctx, http_buffer->str, http_buffer->length));\n}\n\nstatic PHP_METHOD(swoole_http_response, close) {\n    HttpContext *ctx = php_swoole_http_response_get_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        swoole_set_last_error(SW_ERROR_SESSION_CLOSED);\n        RETURN_FALSE;\n    }\n    RETURN_BOOL(ctx->close(ctx));\n}\n\nssize_t WebSocket::send_frame(const swoole::WebSocketSettings &settings,\n                              SocketImpl *sock,\n                              uchar opcode,\n                              uchar flags,\n                              const char *payload,\n                              size_t payload_length) {\n    if (settings.in_server) {\n        sw_unset_bit(flags, WebSocket::FLAG_MASK);\n    } else {\n        sw_set_bit(flags, WebSocket::FLAG_MASK);\n    }\n    auto wbuf = sock->get_write_buffer();\n    wbuf->clear();\n    WebSocket::encode(wbuf, payload, payload_length, opcode, flags);\n    return sock->send(wbuf->str, wbuf->length);\n}\n\n/**\n * return_value is object means success.\n * return_value is false means socket.read() method returning -1.\n * return_value is null means other error.\n * return_value is empry string means socket is closed.\n * the opcode is returned so the caller can decide when to release the continue_frame_buffer.\n */\nvoid WebSocket::recv_frame(const WebSocketSettings &settings,\n                           std::shared_ptr<String> &continue_frame_buffer,\n                           SocketImpl *sock,\n                           zval *return_value,\n                           double timeout) {\n    zval zpayload;\n\n    do {\n        ssize_t retval = sock->recv_packet(timeout);\n        if (retval < 0) {\n            RETURN_FALSE;\n        } else if (retval == 0) {\n            RETURN_EMPTY_STRING();\n        }\n\n        auto buffer = sock->get_read_buffer();\n        WebSocket::Frame frame{};\n\n        if (sw_unlikely(static_cast<size_t>(buffer->length) < sizeof(frame.header))) {\n            swoole_set_last_error(SW_ERROR_PROTOCOL_ERROR);\n            RETURN_NULL();\n        }\n\n        if (sw_unlikely(!WebSocket::decode(&frame, buffer))) {\n            swoole_set_last_error(SW_ERROR_PROTOCOL_ERROR);\n            RETURN_NULL();\n        }\n\n        uchar opcode = frame.header.OPCODE;\n        bool should_respond = false;\n        if (opcode == WebSocket::OPCODE_PING) {\n            if (!settings.open_ping_frame) {\n                WebSocket::send_frame(\n                    settings, sock, WebSocket::OPCODE_PONG, WebSocket::FLAG_FIN, frame.payload, frame.payload_length);\n                continue;\n            }\n            should_respond = true;\n        }\n        if (opcode == WebSocket::OPCODE_PONG) {\n            if (!settings.open_pong_frame) {\n                continue;\n            }\n            should_respond = true;\n        }\n        if (opcode == WebSocket::OPCODE_CLOSE) {\n            if (!settings.open_close_frame) {\n                WebSocket::send_frame(\n                    settings, sock, WebSocket::OPCODE_CLOSE, WebSocket::FLAG_FIN, frame.payload, frame.payload_length);\n                continue;\n            }\n            should_respond = true;\n        }\n\n        if (should_respond) {\n            ZVAL_STRINGL(&zpayload, frame.payload, frame.payload_length);\n            WebSocket::construct_frame(return_value, opcode, &zpayload, WebSocket::FLAG_FIN);\n            zval_ptr_dtor(&zpayload);\n            zend::object_set(return_value, ZEND_STRL(\"fd\"), sock->get_fd());\n            return;\n        }\n\n        if (opcode == WebSocket::OPCODE_CONTINUATION) {\n            if (sw_unlikely(!continue_frame_buffer)) {\n                swoole_warning(\"A continuation frame cannot stand alone and MUST be preceded by an initial frame whose \"\n                               \"opcode indicates either text or binary data.\");\n                RETURN_NULL();\n            }\n\n            if (sw_likely(frame.payload)) {\n                continue_frame_buffer->append(frame.payload, frame.payload_length);\n            }\n\n            if (frame.header.FIN) {\n                uchar complete_opcode = 0;\n                uchar complete_flags = 0;\n                WebSocket::parse_ext_flags(continue_frame_buffer->offset, &complete_opcode, &complete_flags);\n\n                if (complete_flags & WebSocket::FLAG_RSV1) {\n                    if (sw_unlikely(!FrameObject::uncompress(\n                            &zpayload, continue_frame_buffer->str, continue_frame_buffer->length))) {\n                        continue_frame_buffer.reset();\n                        swoole_set_last_error(SW_ERROR_PROTOCOL_ERROR);\n                        RETURN_NULL();\n                    }\n                } else {\n                    zend::assign_zend_string_by_val(\n                        &zpayload, continue_frame_buffer->str, continue_frame_buffer->length);\n                    Z_TRY_ADDREF(zpayload);\n                }\n\n                sw_set_bit(complete_flags, WebSocket::FLAG_FIN);\n                WebSocket::construct_frame(return_value, complete_opcode, &zpayload, complete_flags);\n                zend::object_set(return_value, ZEND_STRL(\"fd\"), sock->get_fd());\n                zval_ptr_dtor(&zpayload);\n                /**\n                 * The final frame of the continuous frame sequence has been received,\n                 * and the complete message has been assembled. Memory can be released immediately.\n                 */\n                continue_frame_buffer.reset();\n                return;\n            }\n        } else {\n            if (sw_unlikely(continue_frame_buffer)) {\n                swoole_warning(\"All fragments of a message, except for the initial frame, must use the continuation \"\n                               \"frame opcode(0).\");\n                RETURN_NULL();\n            }\n            if (frame.header.FIN) {\n                if (frame.compressed()) {\n                    if (sw_unlikely(!FrameObject::uncompress(&zpayload, frame.payload, frame.payload_length))) {\n                        swoole_set_last_error(SW_ERROR_PROTOCOL_ERROR);\n                        RETURN_NULL();\n                    }\n                } else {\n                    ZVAL_STRINGL(&zpayload, frame.payload, frame.payload_length);\n                }\n                WebSocket::construct_frame(return_value, frame.header.OPCODE, &zpayload, frame.get_flags());\n                zend::object_set(return_value, ZEND_STRL(\"fd\"), sock->get_fd());\n                zval_ptr_dtor(&zpayload);\n                return;\n            } else {\n                continue_frame_buffer = std::make_shared<String>(\n                    (frame.payload_length > 0 ? frame.payload_length : SW_WEBSOCKET_DEFAULT_BUFFER),\n                    sw_zend_string_allocator());\n                continue_frame_buffer->offset = WebSocket::get_ext_flags(frame.header.OPCODE, frame.get_flags());\n                if (sw_likely(frame.payload)) {\n                    continue_frame_buffer->append(frame.payload, frame.payload_length);\n                }\n            }\n        }\n    } while (true);\n}\n\nstatic PHP_METHOD(swoole_http_response, recv) {\n    HttpContext *ctx = php_swoole_http_response_get_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        swoole_set_last_error(SW_ERROR_SESSION_CLOSED);\n        RETURN_FALSE;\n    }\n    if (UNEXPECTED(!ctx->is_co_socket() || !ctx->upgrade)) {\n        php_swoole_fatal_error(E_WARNING, \"fd[%ld] is not a websocket conncetion\", ctx->fd);\n        RETURN_FALSE;\n    }\n\n    double timeout = 0;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    WebSocket::recv_frame(\n        ctx->websocket_settings, ctx->continue_frame_buffer, ctx->get_co_socket(), return_value, timeout);\n    if (ZVAL_IS_EMPTY_STRING(return_value)) {\n        ctx->close(ctx);\n        return;\n    }\n    if (sw_unlikely(ZVAL_IS_NULL(return_value))) {\n        ZVAL_FALSE(return_value);\n    }\n}\n\nstatic PHP_METHOD(swoole_http_response, detach) {\n    HttpContext *ctx = http_response_get_and_check_context(ZEND_THIS);\n    if (!ctx) {\n        RETURN_FALSE;\n    }\n    ctx->detached = 1;\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_http_response, create) {\n    zval *zobject = nullptr;\n    zval *zrequest = nullptr;\n    zval *zsocket = nullptr;\n    zend_long fd = -1;\n    Server *serv = nullptr;\n    HttpContext *ctx = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_ZVAL(zobject)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(fd)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (ZVAL_IS_OBJECT(zobject)) {\n    _type_detect:\n        if (instanceof_function(Z_OBJCE_P(zobject), swoole_server_ce)) {\n            serv = php_swoole_server_get_and_check_server(zobject);\n            if (serv->get_connection_verify(fd) == nullptr) {\n                php_swoole_fatal_error(E_WARNING, \"parameter $2 (%ld) must be valid connection session_id\", (long) fd);\n                RETURN_FALSE;\n            }\n        } else if (sw_zval_is_co_socket(zobject)) {\n            zsocket = zobject;\n            fd = php_swoole_get_socket(zobject)->get_fd();\n        } else {\n        _bad_type:\n            php_swoole_fatal_error(E_WARNING, \"parameter $1 must be instanceof Server or Coroutine\\\\Socket\");\n            RETURN_FALSE;\n        }\n    } else if (ZVAL_IS_ARRAY(zobject)) {\n        zrequest = zend_hash_index_find(Z_ARR_P(zobject), 1);\n        if (!ZVAL_IS_OBJECT(zrequest) || !instanceof_function(Z_OBJCE_P(zrequest), swoole_http_request_ce)) {\n            php_swoole_fatal_error(E_WARNING, \"parameter $1.second must be instanceof Http\\\\Request\");\n            RETURN_FALSE;\n        }\n        zobject = zend_hash_index_find(Z_ARR_P(zobject), 0);\n        if (!ZVAL_IS_OBJECT(zobject)) {\n            goto _bad_type;\n        } else {\n            ctx = php_swoole_http_request_get_context(zrequest);\n            goto _type_detect;\n        }\n    } else {\n        fd = zval_get_long(zobject);\n        serv = sw_server();\n    }\n\n    if (serv && !serv->is_started()) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    if (!ctx) {\n        ctx = new HttpContext();\n        ctx->keepalive = 1;\n\n        if (serv) {\n            ctx->init(serv);\n        } else if (zsocket) {\n            ctx->init(zsocket);\n            swoole_llhttp_parser_init(&ctx->parser, HTTP_REQUEST, (void *) ctx);\n        } else {\n            delete ctx;\n            assert(0);\n            RETURN_FALSE;\n        }\n    } else {\n        if (serv) {\n            ctx->bind(serv);\n        } else if (zsocket) {\n            ctx->bind(zsocket);\n        } else {\n            assert(0);\n            RETURN_FALSE;\n        }\n    }\n\n    object_init_ex(return_value, swoole_http_response_ce);\n    php_swoole_http_response_set_context(return_value, ctx);\n    ctx->fd = fd;\n    ctx->response.zobject = return_value;\n    sw_copy_to_stack(ctx->response.zobject, ctx->response._zobject);\n    zend_update_property_long(swoole_http_response_ce, SW_Z8_OBJ_P(return_value), ZEND_STRL(\"fd\"), fd);\n    if (ctx->is_co_socket()) {\n        zend_update_property_ex(\n            swoole_http_response_ce, SW_Z8_OBJ_P(ctx->response.zobject), SW_ZSTR_KNOWN(SW_ZEND_STR_SOCKET), zobject);\n    }\n    if (zrequest) {\n        zend_update_property_long(swoole_http_request_ce, SW_Z8_OBJ_P(ctx->request.zobject), ZEND_STRL(\"fd\"), fd);\n    }\n}\n\nstatic PHP_METHOD(swoole_http_response, redirect) {\n    zval *zurl;\n    zval *zhttp_code = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_ZVAL(zurl)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_ZVAL_EX(zhttp_code, 1, 0)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    HttpContext *ctx = http_response_get_and_check_context(ZEND_THIS);\n    if (UNEXPECTED(!ctx)) {\n        RETURN_FALSE;\n    }\n\n    // status\n    if (zhttp_code) {\n        ctx->response.status = zval_get_long(zhttp_code);\n    } else {\n        ctx->response.status = 302;\n    }\n    // header\n    ctx->set_header(ZEND_STRL(\"Location\"), zurl, false);\n    ctx->end(nullptr, return_value);\n}\n"
  },
  {
    "path": "ext-src/swoole_http_server.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_http_server.h\"\n#include \"swoole_process_pool.h\"\n\nBEGIN_EXTERN_C()\n#include \"rfc1867.h\"\nEND_EXTERN_C()\n\nusing HttpRequest = swoole::http::Request;\nusing HttpResponse = swoole::http::Response;\nusing HttpContext = swoole::http::Context;\n\nusing swoole::Connection;\nusing swoole::DataHead;\nusing swoole::RecvData;\nusing swoole::Server;\nusing swoole::SessionId;\n\nnamespace WebSocket = swoole::websocket;\n\nzend_class_entry *swoole_http_server_ce;\nzend_object_handlers swoole_http_server_handlers;\n\nstatic SW_THREAD_LOCAL std::queue<HttpContext *> queued_http_contexts;\nstatic SW_THREAD_LOCAL std::unordered_map<SessionId, zend::Variable> server_ips;\nstatic SW_THREAD_LOCAL std::unordered_map<SessionId, zend::Variable> client_ips;\n\nstatic bool http_context_send_data(HttpContext *ctx, const char *data, size_t length);\nstatic bool http_context_sendfile(HttpContext *ctx, zend_string *file, off_t offset, size_t length);\nstatic bool http_context_disconnect(HttpContext *ctx);\n\nstatic void http_server_process_request(const Server *serv, zend::Callable *cb, HttpContext *ctx) {\n    zval args[2];\n    args[0] = *ctx->request.zobject;\n    args[1] = *ctx->response.zobject;\n    if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, serv->is_enable_coroutine()))) {\n        php_swoole_error(E_WARNING, \"%s->onRequest handler error\", ZSTR_VAL(swoole_http_server_ce->name));\n#ifdef SW_HTTP_SERVICE_UNAVAILABLE_PACKET\n        ctx->send(ctx, SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET));\n#endif\n        ctx->close(ctx);\n    }\n}\n\nint php_swoole_http_server_onReceive(Server *serv, RecvData *req) {\n    auto session_id = req->info.fd;\n    auto server_fd = req->info.server_fd;\n\n    Connection *conn = serv->get_connection_verify_no_ssl(session_id);\n    if (!conn) {\n        swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, \"session[%ld] is closed\", session_id);\n        return SW_ERR;\n    }\n\n    auto *port = serv->get_port_by_server_fd(server_fd);\n    // other server port\n    if (!(port->open_http_protocol && php_swoole_server_isset_callback(serv, port, SW_SERVER_CB_onRequest)) &&\n        !(port->open_websocket_protocol && php_swoole_server_isset_callback(serv, port, SW_SERVER_CB_onMessage))) {\n        return php_swoole_server_onReceive(serv, req);\n    }\n    // websocket client\n    if (conn->websocket_status >= WebSocket::STATUS_HANDSHAKE) {\n        return swoole_websocket_onMessage(serv, req);\n    }\n\n    if (conn->http2_stream) {\n        return swoole_http2_server_onReceive(serv, conn, req);\n    }\n\n    HttpContext *ctx = swoole_http_context_new(session_id);\n    ctx->init(serv);\n    ctx->onBeforeRequest = swoole_http_server_onBeforeRequest;\n\n    zval *zdata = &ctx->request.zdata;\n    php_swoole_get_recv_data(serv, zdata, req);\n\n    swoole_trace_log(SW_TRACE_SERVER,\n                     \"http request from %ld with %d bytes: <<EOF\\n%.*s\\nEOF\",\n                     session_id,\n                     (int) Z_STRLEN_P(zdata),\n                     (int) Z_STRLEN_P(zdata),\n                     Z_STRVAL_P(zdata));\n\n    zval *zrequest_object = ctx->request.zobject;\n    zval *zresponse_object = ctx->response.zobject;\n\n    llhttp_t *parser = &ctx->parser;\n    swoole_llhttp_parser_init(parser, HTTP_REQUEST, (void *) ctx);\n\n    size_t parsed_n = ctx->parse(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata));\n    if (sw_unlikely(ctx->parser.error != HPE_OK || !ctx->completed)) {\n        ctx->send(ctx, SW_STRL(SW_HTTP_BAD_REQUEST_PACKET));\n        ctx->close(ctx);\n        if (ctx->parser.error != HPE_OK) {\n            swoole_notice(\"Invalid HTTP request discarded: %ld bytes unprocessed. Reason: %s\",\n                          Z_STRLEN_P(zdata) - parsed_n,\n                          llhttp_get_error_reason(&ctx->parser));\n        } else {\n            swoole_notice(\"Incomplete HTTP request: parsed successfully but missing required components\");\n        }\n        goto _dtor_and_return;\n    }\n\n    do {\n        zval *zserver = ctx->request.zserver;\n        HashTable *ht = Z_ARR_P(zserver);\n        swoole_http_server_populate_ip_and_port(serv, ht, conn, session_id, ctx->keepalive);\n        http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_MASTER_TIME), (zend_long) conn->last_recv_time);\n    } while (false);\n\n    if (swoole_isset_hook(static_cast<swGlobalHookType>(PHP_SWOOLE_HOOK_BEFORE_REQUEST))) {\n        swoole_call_hook(static_cast<swGlobalHookType>(PHP_SWOOLE_HOOK_BEFORE_REQUEST), ctx);\n    }\n\n    // begin to check and call registered callback\n    do {\n        zend::Callable *cb = nullptr;\n\n        if (conn->websocket_status == WebSocket::STATUS_CONNECTION) {\n            cb = php_swoole_server_get_callback(serv, server_fd, SW_SERVER_CB_onHandshake);\n            if (cb == nullptr) {\n                swoole_websocket_onHandshake(serv, port, ctx);\n                goto _dtor_and_return;\n            } else {\n                conn->websocket_status = WebSocket::STATUS_HANDSHAKE;\n                ctx->upgrade = 1;\n            }\n        } else {\n            cb = php_swoole_server_get_callback(serv, server_fd, SW_SERVER_CB_onRequest);\n            if (cb == nullptr) {\n                swoole_websocket_onRequest(ctx);\n                goto _dtor_and_return;\n            }\n        }\n        ctx->private_data_2 = cb;\n        if (ctx->onBeforeRequest && !ctx->onBeforeRequest(ctx)) {\n            return SW_OK;\n        }\n        http_server_process_request(serv, cb, ctx);\n    } while (false);\n\n_dtor_and_return:\n    zval_ptr_dtor(zrequest_object);\n    zval_ptr_dtor(zresponse_object);\n\n    return SW_OK;\n}\n\nvoid php_swoole_http_server_onClose(Server *serv, DataHead *info) {\n    server_ips.erase(info->fd);\n    client_ips.erase(info->fd);\n    php_swoole_server_onClose(serv, info);\n}\n\nvoid php_swoole_http_server_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY_EX(swoole_http_server, \"Swoole\\\\Http\\\\Server\", nullptr, nullptr, swoole_server);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_server);\n    SW_SET_CLASS_CLONEABLE(swoole_http_server, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_server, sw_zend_class_unset_property_deny);\n}\n\nvoid php_swoole_http_server_rinit() {\n    // for is_uploaded_file and move_uploaded_file\n    if (!SG(rfc1867_uploaded_files)) {\n        ALLOC_HASHTABLE(SG(rfc1867_uploaded_files));\n        zend_hash_init(SG(rfc1867_uploaded_files), 8, nullptr, nullptr, 0);\n    }\n}\n\nvoid php_swoole_http_server_rshutdown() {\n    if (SG(rfc1867_uploaded_files)) {\n        destroy_uploaded_files_hash();\n        SG(rfc1867_uploaded_files) = nullptr;\n    }\n\n    server_ips.clear();\n    client_ips.clear();\n    while (!queued_http_contexts.empty()) {\n        HttpContext *ctx = queued_http_contexts.front();\n        queued_http_contexts.pop();\n        ctx->end_ = 1;\n        ctx->onAfterResponse = nullptr;\n        zval_ptr_dtor(ctx->request.zobject);\n        zval_ptr_dtor(ctx->response.zobject);\n    }\n}\n\nHttpContext *swoole_http_context_new(SessionId fd) {\n    auto *ctx = new HttpContext();\n\n    zval *zrequest_object = &ctx->request._zobject;\n    ctx->request.zobject = zrequest_object;\n    ZVAL_OBJ(zrequest_object, swoole_http_request_ce->create_object(swoole_http_request_ce));\n    php_swoole_http_request_set_context(zrequest_object, ctx);\n\n    zval *zresponse_object = &ctx->response._zobject;\n    ctx->response.zobject = zresponse_object;\n    ZVAL_OBJ(zresponse_object, swoole_http_response_ce->create_object(swoole_http_response_ce));\n    php_swoole_http_response_set_context(zresponse_object, ctx);\n\n    http_server_set_object_fd_property(SW_Z8_OBJ_P(zrequest_object), swoole_http_request_ce, fd);\n    http_server_set_object_fd_property(SW_Z8_OBJ_P(zresponse_object), swoole_http_response_ce, fd);\n\n    swoole_http_init_and_read_property(swoole_http_request_ce,\n                                       zrequest_object,\n                                       &ctx->request.zserver,\n                                       SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER),\n                                       HT_MIN_SIZE << 1);\n    swoole_http_init_and_read_property(\n        swoole_http_request_ce, zrequest_object, &ctx->request.zheader, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER));\n\n    ctx->fd = fd;\n\n    return ctx;\n}\n\nvoid HttpContext::init(Server *serv) {\n    parse_cookie = serv->http_parse_cookie;\n    parse_body = serv->http_parse_post;\n    parse_files = serv->http_parse_files;\n#ifdef SW_HAVE_COMPRESSION\n    enable_compression = serv->http_compression;\n    compression_level = serv->http_compression_level;\n    compression_min_length = serv->compression_min_length;\n    compression_types = serv->http_compression_types;\n#endif\n    upload_tmp_dir = serv->upload_tmp_dir;\n    bind(serv);\n}\n\nvoid HttpContext::bind(Server *serv) {\n    private_data = serv;\n    send = http_context_send_data;\n    sendfile = http_context_sendfile;\n    close = http_context_disconnect;\n    ZVAL_NULL(&zsocket);\n}\n\nvoid HttpContext::copy(const HttpContext *ctx) {\n    parse_cookie = ctx->parse_cookie;\n    parse_body = ctx->parse_body;\n    parse_files = ctx->parse_files;\n#ifdef SW_HAVE_COMPRESSION\n    enable_compression = ctx->enable_compression;\n    compression_level = ctx->compression_level;\n    compression_min_length = ctx->compression_min_length;\n    compression_types = ctx->compression_types;\n#endif\n    zsocket = ctx->zsocket;\n    Z_TRY_ADDREF(zsocket);\n    private_data = ctx->private_data;\n    upload_tmp_dir = ctx->upload_tmp_dir;\n    send = ctx->send;\n    sendfile = ctx->sendfile;\n    close = ctx->close;\n    onBeforeRequest = ctx->onBeforeRequest;\n    onAfterResponse = ctx->onAfterResponse;\n}\n\nbool HttpContext::is_available() const {\n    if (!response.zobject) {\n        return false;\n    }\n    if (is_co_socket()) {\n        return !php_swoole_socket_is_closed(&zsocket);\n    } else {\n        auto *serv = get_async_server();\n        auto *conn = serv->get_connection_by_session_id(fd);\n        if (!conn || conn->closed || conn->peer_closed) {\n            return false;\n        }\n    }\n    return true;\n}\n\nvoid HttpContext::free() {\n    // http context can only be freed after request and response were freed\n    // If is an HTTP2 request, must wait for the stream to release before releasing the HTTP context\n    if (request.zobject || response.zobject || stream_id > 0) {\n        return;\n    }\n\n    HttpRequest *req = &request;\n    HttpResponse *res = &response;\n    if (req->path) {\n        efree(req->path);\n    }\n    if (Z_TYPE(req->zdata) == IS_STRING) {\n        zend_string_release(Z_STR(req->zdata));\n    }\n\n    delete req->chunked_body;\n    delete req->h2_data_buffer;\n\n    if (res->reason) {\n        efree(res->reason);\n    }\n    if (mt_parser) {\n        multipart_parser_free(mt_parser);\n        mt_parser = nullptr;\n    }\n    if (form_data_buffer) {\n        delete form_data_buffer;\n        form_data_buffer = nullptr;\n    }\n\n    if (is_co_socket()) {\n        zval_ptr_dtor(&zsocket);\n    }\n\n    delete write_buffer;\n    delete this;\n}\n\nbool http_context_send_data(HttpContext *ctx, const char *data, size_t length) {\n    auto *serv = ctx->get_async_server();\n    bool retval = serv->send(ctx->fd, data, length);\n    if (!retval && swoole_get_last_error() == SW_ERROR_OUTPUT_SEND_YIELD) {\n        auto sdata = zend_string_init(data, length, false);\n        auto rv = php_swoole_server_send_yield(serv, ctx->fd, sdata);\n        zend_string_release(sdata);\n        return rv;\n    }\n    return retval;\n}\n\nstatic bool http_context_sendfile(HttpContext *ctx, zend_string *file, off_t offset, size_t length) {\n    auto *serv = ctx->get_async_server();\n    return serv->sendfile(ctx->fd, file->val, file->len, offset, length);\n}\n\nstatic bool http_context_disconnect(HttpContext *ctx) {\n    auto *serv = ctx->get_async_server();\n    return serv->close(ctx->fd, false);\n}\n\nbool swoole_http_server_onBeforeRequest(HttpContext *ctx) {\n    ctx->onBeforeRequest = nullptr;\n    ctx->onAfterResponse = swoole_http_server_onAfterResponse;\n    if (!sw_server() || !sw_worker() || sw_worker()->is_shutdown()) {\n        return false;\n    }\n\n    auto serv = ctx->get_async_server();\n    auto worker = sw_worker();\n\n    swoole_trace(\"serv->gs->concurrency=%u, max_concurrency=%u\", serv->gs->concurrency, serv->gs->max_concurrency);\n    sw_atomic_add_fetch(&serv->gs->concurrency, 1);\n    worker->concurrency++;\n    if (worker->concurrency > serv->worker_max_concurrency) {\n        swoole_trace_log(SW_TRACE_COROUTINE,\n                         \"exceed worker_max_concurrency[%u] limit, request[%p] queued\",\n                         serv->worker_max_concurrency,\n                         ctx);\n        queued_http_contexts.push(ctx);\n        return false;\n    }\n\n    return true;\n}\n\nvoid swoole_http_server_onAfterResponse(HttpContext *ctx) {\n    ctx->onAfterResponse = nullptr;\n\n    if (sw_unlikely(!sw_server() || !sw_worker())) {\n        return;\n    }\n\n    if (sw_unlikely(sw_worker()->is_shutdown())) {\n        while (!queued_http_contexts.empty()) {\n            HttpContext *_ctx = queued_http_contexts.front();\n            queued_http_contexts.pop();\n            _ctx->send(_ctx, SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET));\n            _ctx->close(_ctx);\n        }\n        return;\n    }\n\n    auto serv = ctx->get_async_server();\n    auto worker = sw_worker();\n    swoole_trace(\"serv->gs->concurrency=%u, max_concurrency=%u\", serv->gs->concurrency, serv->gs->max_concurrency);\n    sw_atomic_sub_fetch(&serv->gs->concurrency, 1);\n    worker->concurrency--;\n\n    if (!queued_http_contexts.empty()) {\n        HttpContext *_ctx = queued_http_contexts.front();\n        swoole_trace(\"[POP 1] concurrency=%u, ctx=%p, request=%p\", worker->concurrency, _ctx, _ctx->request.zobject);\n        queued_http_contexts.pop();\n        swoole_event_defer(\n            [](void *private_data) {\n                auto *ctx = static_cast<HttpContext *>(private_data);\n                auto *serv = ctx->get_async_server();\n                auto *cb = static_cast<zend::Callable *>(ctx->private_data_2);\n                swoole_trace(\"[POP 2] ctx=%p, request=%p\", ctx, ctx->request.zobject);\n                http_server_process_request(serv, cb, ctx);\n                zval_ptr_dtor(ctx->request.zobject);\n                zval_ptr_dtor(ctx->response.zobject);\n            },\n            _ctx);\n    }\n}\n\n/**\n * When calculating the server-side IP and client-side IP, since these two calculations\n * share the same memory block, they cannot be performed simultaneously; otherwise,\n * both results would be identical. Therefore, it is necessary to first calculate the client-side IP,\n * write it to the cache, and then proceed to calculate the server-side IP.\n */\nstatic void http_server_session_track_ip(swoole::Server *server,\n                                         HashTable *ht,\n                                         swoole::Connection *conn,\n                                         swoole::SessionId session_id,\n                                         zend_string *known_string,\n                                         std::unordered_map<swoole::SessionId, zend::Variable> &ips) {\n    auto iter = ips.find(session_id);\n    if (iter == ips.end()) {\n        const char *address = nullptr;\n        if (known_string == SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_ADDR)) {\n            address = server->get_local_addr(conn);\n        } else {\n            address = conn->info.get_addr();\n        }\n\n        auto rs = ips.emplace(session_id, address);\n        iter = rs.first;\n    }\n\n    iter->second.add_ref();\n    http_server_add_server_array(ht, known_string, iter->second.ptr());\n}\n\nvoid swoole_http_server_populate_ip_and_port(\n    Server *server, HashTable *ht, Connection *conn, SessionId session_id, bool keepalive) {\n    http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_PORT), (zend_long) conn->local_port);\n    http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_PORT), (zend_long) conn->info.get_port());\n\n    if (conn->info.is_loopback_addr()) {\n        auto key = conn->info.type == SW_SOCK_TCP6 ? SW_ZEND_STR_ADDR_LOOPBACK_V6 : SW_ZEND_STR_ADDR_LOOPBACK_V4;\n        http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_ADDR), SW_ZSTR_KNOWN(key));\n        http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), SW_ZSTR_KNOWN(key));\n    } else {\n        if (keepalive && (server->is_base_mode() ||\n                          (server->is_process_mode() && server->dispatch_mode == Server::DISPATCH_FDMOD))) {\n            http_server_session_track_ip(\n                server, ht, conn, session_id, SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_ADDR), server_ips);\n            http_server_session_track_ip(\n                server, ht, conn, session_id, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), client_ips);\n        } else {\n            http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_ADDR), server->get_local_addr(conn));\n            http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), server->get_remote_addr(conn));\n        }\n    }\n}\n"
  },
  {
    "path": "ext-src/swoole_http_server_coro.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_http_server.h\"\n#include \"php_swoole_websocket.h\"\n\n#include <string>\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_http_server_coro_arginfo.h\"\nEND_EXTERN_C()\n\nusing swoole::Coroutine;\nusing swoole::microtime;\nusing swoole::PHPCoroutine;\nusing swoole::Server;\nusing swoole::String;\nusing swoole::coroutine::System;\n\nnamespace WebSocket = swoole::websocket;\n\nusing HttpRequest = swoole::http::Request;\nusing HttpResponse = swoole::http::Response;\nusing HttpContext = swoole::http::Context;\nusing Http2Stream = swoole::http2::Stream;\nusing Http2Session = swoole::http2::Session;\n\nstatic zend_class_entry *swoole_http_server_coro_ce;\nstatic zend_object_handlers swoole_http_server_coro_handlers;\n\nstatic bool http_context_send_data(HttpContext *ctx, const char *data, size_t length);\nstatic bool http_context_sendfile(HttpContext *ctx, zend_string *file, off_t offset, size_t length);\nstatic bool http_context_disconnect(HttpContext *ctx);\n\nstatic void http2_server_onRequest(const std::shared_ptr<Http2Session> &session,\n                                   const std::shared_ptr<Http2Stream> &stream);\n\nnamespace swoole {\nnamespace coroutine {\n\nclass HttpServer {\n  public:\n    SocketImpl *socket;\n    zend::Callable *default_handler;\n    std::unordered_map<std::string, zend::Callable *> handlers;\n    bool running;\n    zval zclients;\n\n    /* options */\n    bool parse_cookie;\n    bool parse_post;\n    bool parse_files;\n#ifdef SW_HAVE_COMPRESSION\n    bool compression;\n#endif\n    WebSocketSettings websocket_settings;\n\n    char *upload_tmp_dir;\n#ifdef SW_HAVE_COMPRESSION\n    uint8_t compression_level;\n    uint32_t compression_min_length;\n    std::shared_ptr<std::unordered_set<std::string>> compression_types = nullptr;\n#endif\n\n    explicit HttpServer(SocketType type) {\n        socket = new SocketImpl(type);\n        default_handler = nullptr;\n        array_init(&zclients);\n        running = true;\n\n        parse_cookie = true;\n        parse_post = true;\n        parse_files = false;\n#ifdef SW_HAVE_COMPRESSION\n        compression = true;\n        compression_level = SW_Z_BEST_SPEED;\n        compression_min_length = SW_COMPRESSION_MIN_LENGTH_DEFAULT;\n#endif\n#ifdef SW_HAVE_ZLIB\n        websocket_settings.compression = false;\n#endif\n        upload_tmp_dir = sw_strdup(\"/tmp\");\n    }\n\n    ~HttpServer() {\n        sw_free(upload_tmp_dir);\n        zval_ptr_dtor(&zclients);\n        for (const auto &handler : handlers) {\n            sw_callable_free(handler.second);\n        }\n        delete socket;\n    }\n\n    bool set_handler(const std::string &pattern, zval *zfn) {\n        auto cb = sw_callable_create(zfn);\n        if (!cb) {\n            return false;\n        }\n        if (handlers.find(pattern) != handlers.end()) {\n            sw_callable_free(handlers[pattern]);\n        }\n        handlers[pattern] = cb;\n        if (pattern == \"/\") {\n            default_handler = cb;\n        }\n        return true;\n    }\n\n    zend::Callable *get_handler(const HttpContext *ctx) const {\n        for (auto &handler : handlers) {\n            if (handler.second == default_handler) {\n                continue;\n            }\n            if (swoole_str_istarts_with(\n                    ctx->request.path, ctx->request.path_len, handler.first.c_str(), handler.first.length())) {\n                return handler.second;\n            }\n        }\n        return default_handler;\n    }\n\n    HttpContext *create_context(SocketImpl *conn, zval *zconn) const {\n        HttpContext *ctx = swoole_http_context_new(conn->get_fd());\n        ctx->parse_body = parse_post;\n        ctx->parse_cookie = parse_cookie;\n        ctx->parse_files = parse_files;\n#ifdef SW_HAVE_COMPRESSION\n        ctx->enable_compression = compression;\n        ctx->compression_level = compression_level;\n        ctx->compression_min_length = compression_min_length;\n        ctx->compression_types = compression_types;\n#endif\n        ctx->websocket_settings = websocket_settings;\n        ctx->upload_tmp_dir = upload_tmp_dir;\n\n        ctx->bind(zconn);\n\n        llhttp_t *parser = &ctx->parser;\n        swoole_llhttp_parser_init(parser, HTTP_REQUEST, (void *) ctx);\n\n        zend_update_property_ex(\n            swoole_http_response_ce, SW_Z8_OBJ_P(ctx->response.zobject), SW_ZSTR_KNOWN(SW_ZEND_STR_SOCKET), zconn);\n\n        return ctx;\n    }\n\n    void recv_http2_frame(HttpContext *ctx) {\n        auto *sock = ctx->get_co_socket();\n        http2::send_setting_frame(&sock->protocol, sock->get_socket());\n\n        sock->open_length_check = true;\n        sock->protocol.package_length_size = SW_HTTP2_FRAME_HEADER_SIZE;\n        sock->protocol.package_length_offset = 0;\n        sock->protocol.package_body_offset = 0;\n        sock->protocol.get_package_length = http2::get_frame_length;\n\n        auto session = swoole_http2_server_session_new(ctx->fd);\n        session->max_body_size = sock->protocol.package_max_length;\n        session->default_ctx = ctx;\n        session->handle = http2_server_onRequest;\n        session->private_data = this;\n        session->is_coro = true;\n\n        while (true) {\n            auto buffer = sock->get_read_buffer();\n            ssize_t retval = sock->recv_packet();\n            if (sw_unlikely(retval <= 0)) {\n                break;\n            }\n            if (swoole_http2_server_parse(session, buffer->str) == SW_ERR) {\n                break;\n            }\n        }\n\n        swoole_http2_server_session_free(ctx->fd);\n\n        ctx->detached = 1;\n        zval_dtor(ctx->request.zobject);\n        zval_dtor(ctx->response.zobject);\n    }\n};\n};  // namespace coroutine\n};  // namespace swoole\n\nusing swoole::coroutine::HttpServer;\n\nstruct HttpServerObject {\n    HttpServer *server;\n    zend_object std;\n};\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_http_server_coro, __construct);\nstatic PHP_METHOD(swoole_http_server_coro, set);\nstatic PHP_METHOD(swoole_http_server_coro, handle);\nstatic PHP_METHOD(swoole_http_server_coro, start);\nstatic PHP_METHOD(swoole_http_server_coro, shutdown);\nstatic PHP_METHOD(swoole_http_server_coro, onAccept);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic const zend_function_entry swoole_http_server_coro_methods[] =\n{\n    PHP_ME(swoole_http_server_coro, __construct, arginfo_class_Swoole_Coroutine_Http_Server___construct, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_server_coro, set,         arginfo_class_Swoole_Coroutine_Http_Server_set,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_server_coro, handle,      arginfo_class_Swoole_Coroutine_Http_Server_handle,      ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_server_coro, onAccept,    arginfo_class_Swoole_Coroutine_Http_Server_onAccept,    ZEND_ACC_PRIVATE)\n    PHP_ME(swoole_http_server_coro, start,       arginfo_class_Swoole_Coroutine_Http_Server_start,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_http_server_coro, shutdown,    arginfo_class_Swoole_Coroutine_Http_Server_shutdown,    ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nstatic zend_object *http_server_coro_create_object(zend_class_entry *ce) {\n    auto *hsc = static_cast<HttpServerObject *>(zend_object_alloc(sizeof(HttpServerObject), ce));\n    zend_object_std_init(&hsc->std, ce);\n    object_properties_init(&hsc->std, ce);\n    hsc->std.handlers = &swoole_http_server_coro_handlers;\n    return &hsc->std;\n}\n\nstatic HttpServerObject *http_server_coro_fetch_object(zend_object *obj) {\n    return reinterpret_cast<HttpServerObject *>(reinterpret_cast<char *>(obj) -\n                                                swoole_http_server_coro_handlers.offset);\n}\n\nstatic HttpServer *http_server_coro_get_object(zend_object *obj) {\n    return http_server_coro_fetch_object(obj)->server;\n}\n\nstatic void http_server_coro_set_error(const zval *zobject, const SocketImpl *sock) {\n    zend_update_property_long(swoole_http_server_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"errCode\"), sock->errCode);\n    zend_update_property_string(swoole_http_server_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"errMsg\"), sock->errMsg);\n}\n\nstatic bool http_context_send_data(HttpContext *ctx, const char *data, size_t length) {\n    return ctx->get_co_socket()->send_all(data, length) == (ssize_t) length;\n}\n\nstatic bool http_context_sendfile(HttpContext *ctx, zend_string *file, off_t offset, size_t length) {\n    return ctx->get_co_socket()->sendfile(file->val, offset, length);\n}\n\nstatic bool http_context_disconnect(HttpContext *ctx) {\n    return ctx->get_co_socket()->close();\n}\n\nstatic void http_server_coro_free_object(zend_object *object) {\n    HttpServerObject *hsc = http_server_coro_fetch_object(object);\n    if (hsc->server) {\n        HttpServer *hs = hsc->server;\n        delete hs;\n    }\n    zend_object_std_dtor(&hsc->std);\n}\n\nvoid HttpContext::init(zval *zsock) {\n    parse_cookie = 1;\n    parse_body = 1;\n    parse_files = 1;\n#ifdef SW_HAVE_COMPRESSION\n    enable_compression = 1;\n    compression_level = SW_Z_BEST_SPEED;\n#endif\n#ifdef SW_HAVE_ZLIB\n    websocket_compression = 0;\n#endif\n    upload_tmp_dir = \"/tmp\";\n    bind(zsock);\n}\n\nvoid HttpContext::bind(zval *zsock) {\n    zsocket = *zsock;\n    Z_TRY_ADDREF(zsocket);\n    private_data = php_swoole_get_socket(zsock);\n    send = http_context_send_data;\n    sendfile = http_context_sendfile;\n    close = http_context_disconnect;\n}\n\nvoid php_swoole_http_server_coro_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_http_server_coro,\n                        \"Swoole\\\\Coroutine\\\\Http\\\\Server\",\n                        \"Co\\\\Http\\\\Server\",\n                        swoole_http_server_coro_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_server_coro);\n    SW_SET_CLASS_CLONEABLE(swoole_http_server_coro, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_server_coro, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_http_server_coro);\n    SW_SET_CLASS_CUSTOM_OBJECT(\n        swoole_http_server_coro, http_server_coro_create_object, http_server_coro_free_object, HttpServerObject, std);\n    swoole_http_server_coro_ce->ce_flags |= ZEND_ACC_FINAL;\n\n    zend_declare_property_long(swoole_http_server_coro_ce, ZEND_STRL(\"fd\"), -1, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_server_coro_ce, ZEND_STRL(\"host\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_http_server_coro_ce, ZEND_STRL(\"port\"), -1, ZEND_ACC_PUBLIC);\n    zend_declare_property_bool(swoole_http_server_coro_ce, ZEND_STRL(\"ssl\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_http_server_coro_ce, ZEND_STRL(\"settings\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_http_server_coro_ce, ZEND_STRL(\"errCode\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_string(swoole_http_server_coro_ce, ZEND_STRL(\"errMsg\"), \"\", ZEND_ACC_PUBLIC);\n}\n\nstatic PHP_METHOD(swoole_http_server_coro, __construct) {\n    char *host;\n    size_t l_host;\n    zend_long port = 0;\n    zend_bool ssl = false;\n    zend_bool reuse_port = false;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 4)\n    Z_PARAM_STRING(host, l_host)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(port)\n    Z_PARAM_BOOL(ssl)\n    Z_PARAM_BOOL(reuse_port)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zend_update_property_stringl(swoole_http_server_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"host\"), host, l_host);\n    zend_update_property_bool(swoole_http_server_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"ssl\"), ssl);\n\n    // check host\n    if (l_host == 0) {\n        zend_throw_exception_ex(swoole_exception_ce, EINVAL, \"host is empty\");\n        RETURN_FALSE;\n    }\n\n    HttpServerObject *hsc = http_server_coro_fetch_object(Z_OBJ_P(ZEND_THIS));\n    std::string host_str(host, l_host);\n    hsc->server = new HttpServer(swoole::network::Socket::convert_to_type(host_str));\n    SocketImpl *sock = hsc->server->socket;\n\n    if (reuse_port) {\n        sock->get_socket()->set_reuse_port();\n    }\n\n    if (!sock->bind(host_str, port)) {\n        http_server_coro_set_error(ZEND_THIS, sock);\n        zend_throw_exception_ex(swoole_exception_ce, sock->errCode, \"bind(%s:%d) failed\", host, (int) port);\n        RETURN_FALSE;\n    }\n    // check ssl\n    if (ssl) {\n        /* we have to call ssl_check_context after user setProtocols */\n        zval *zsettings =\n            sw_zend_read_and_convert_property_array(swoole_http_server_coro_ce, ZEND_THIS, ZEND_STRL(\"settings\"), 0);\n        add_assoc_bool(zsettings, \"open_ssl\", 1);\n        sock->enable_ssl_encrypt();\n    }\n    if (!sock->listen()) {\n        http_server_coro_set_error(ZEND_THIS, sock);\n        zend_throw_exception_ex(swoole_exception_ce, sock->errCode, \"listen() failed\");\n        RETURN_FALSE;\n    }\n\n    zend_update_property_long(swoole_http_server_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"fd\"), sock->get_fd());\n    zend_update_property_long(swoole_http_server_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"port\"), sock->get_port());\n}\n\nstatic PHP_METHOD(swoole_http_server_coro, handle) {\n    char *pattern;\n    size_t pattern_len;\n    zval *zfn;\n\n    HttpServer *hs = http_server_coro_get_object(Z_OBJ_P(ZEND_THIS));\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_STRING(pattern, pattern_len)\n    Z_PARAM_ZVAL(zfn)\n    ZEND_PARSE_PARAMETERS_END();\n\n    std::string key(pattern, pattern_len);\n    RETURN_BOOL(hs->set_handler(key, zfn));\n}\n\nstatic PHP_METHOD(swoole_http_server_coro, set) {\n    zval *zset;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ARRAY(zset)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (php_swoole_array_length(zset) == 0) {\n        RETURN_FALSE;\n    } else {\n        zval *zsettings =\n            sw_zend_read_and_convert_property_array(swoole_http_server_coro_ce, ZEND_THIS, ZEND_STRL(\"settings\"), 0);\n        php_array_merge(Z_ARRVAL_P(zsettings), Z_ARRVAL_P(zset));\n        RETURN_TRUE;\n    }\n}\n\nstatic PHP_METHOD(swoole_http_server_coro, start) {\n    HttpServer *hs = http_server_coro_get_object(Z_OBJ_P(ZEND_THIS));\n    SocketImpl *sock = hs->socket;\n\n    /* get callback fci cache */\n    char *func_name = nullptr;\n    zend_fcall_info_cache fci_cache;\n    zend::Variable zcallback(\"onAccept\");\n    if (!sw_zend_is_callable_at_frame(\n            zcallback.ptr(), ZEND_THIS, execute_data, 0, &func_name, nullptr, &fci_cache, nullptr)) {\n        php_swoole_fatal_error(E_CORE_ERROR, \"function '%s' is not callable\", func_name);\n        return;\n    }\n    efree(func_name);\n\n    /* check settings */\n    zval *zsettings =\n        sw_zend_read_and_convert_property_array(swoole_http_server_coro_ce, ZEND_THIS, ZEND_STRL(\"settings\"), 0);\n    php_swoole_socket_set_protocol(hs->socket, zsettings);\n    HashTable *vht = Z_ARRVAL_P(zsettings);\n    zval *ztmp;\n    // parse cookie header\n    if (php_swoole_array_get_value(vht, \"http_parse_cookie\", ztmp)) {\n        hs->parse_cookie = zval_is_true(ztmp);\n    }\n    // parse x-www-form-urlencoded form data\n    if (php_swoole_array_get_value(vht, \"http_parse_post\", ztmp)) {\n        hs->parse_post = zval_is_true(ztmp);\n    }\n    // parse multipart/form-data file uploads\n    if (php_swoole_array_get_value(vht, \"http_parse_files\", ztmp)) {\n        hs->parse_files = zval_is_true(ztmp);\n    }\n#ifdef SW_HAVE_COMPRESSION\n    // http content compression\n    if (php_swoole_array_get_value(vht, \"http_compression\", ztmp)) {\n        hs->compression = zval_is_true(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"http_compression_level\", ztmp) ||\n        php_swoole_array_get_value(vht, \"compression_level\", ztmp) ||\n        php_swoole_array_get_value(vht, \"http_gzip_level\", ztmp)) {\n        zend_long level = zval_get_long(ztmp);\n        if (level > UINT8_MAX) {\n            level = UINT8_MAX;\n        } else if (level < 0) {\n            level = 0;\n        }\n        hs->compression_level = level;\n    }\n    if (php_swoole_array_get_value(vht, \"http_compression_min_length\", ztmp) ||\n        php_swoole_array_get_value(vht, \"compression_min_length\", ztmp)) {\n        hs->compression_min_length = zval_get_long(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"http_compression_types\", ztmp) ||\n        php_swoole_array_get_value(vht, \"compression_types\", ztmp)) {\n        hs->compression_types = std::make_shared<std::unordered_set<std::string>>();\n        if (ZVAL_IS_ARRAY(ztmp)) {\n            zval *ztype;\n            SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmp), ztype)\n            zend::String type(ztype);\n            if (type.len() > 0) {\n                hs->compression_types->emplace(type.to_std_string());\n            }\n            SW_HASHTABLE_FOREACH_END();\n        } else {\n            php_swoole_fatal_error(E_ERROR, \"http_compression_types must be array\");\n            RETURN_FALSE;\n        }\n    }\n#endif\n    WebSocket::apply_setting(hs->websocket_settings, vht, true);\n    // temporary directory for HTTP uploaded file.\n    if (php_swoole_array_get_value(vht, \"upload_tmp_dir\", ztmp)) {\n        zend::String str_v(ztmp);\n        if (php_swoole_create_dir(str_v.val(), str_v.len()) < 0) {\n            php_swoole_fatal_error(E_ERROR, \"Unable to create upload_tmp_dir[%s]\", str_v.val());\n            return;\n        }\n        if (hs->upload_tmp_dir) {\n            sw_free(hs->upload_tmp_dir);\n        }\n        hs->upload_tmp_dir = str_v.dup();\n    }\n\n    hs->running = true;\n\n    while (hs->running) {\n        auto conn = sock->accept();\n        if (conn) {\n            zval zsocket;\n            php_swoole_init_socket_object(&zsocket, conn);\n            long cid = PHPCoroutine::create(&fci_cache, 1, &zsocket, zcallback.ptr());\n            zval_dtor(&zsocket);\n            if (cid < 0) {\n                goto _wait_1s;\n            }\n        } else {\n            /*\n             * Too many connection, wait 1s\n             */\n            if (sock->errCode == EMFILE || sock->errCode == ENFILE) {\n            _wait_1s:\n                System::sleep(SW_ACCEPT_RETRY_TIME);\n            } else if (sock->errCode == ETIMEDOUT || sock->errCode == SW_ERROR_SSL_BAD_CLIENT) {\n                continue;\n            } else if (sock->errCode == ECANCELED) {\n                http_server_coro_set_error(ZEND_THIS, sock);\n                break;\n            } else {\n                http_server_coro_set_error(ZEND_THIS, sock);\n                php_swoole_fatal_error(E_WARNING, \"accept failed, Error: %s[%d]\", sock->errMsg, sock->errCode);\n                break;\n            }\n        }\n    }\n\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_http_server_coro, onAccept) {\n    HttpServer *hs = http_server_coro_get_object(Z_OBJ_P(ZEND_THIS));\n    zval *zconn;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1)\n    Z_PARAM_OBJECT(zconn)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_NULL());\n\n    Coroutine *co = Coroutine::get_current();\n    SocketImpl *sock = php_swoole_get_socket(zconn);\n    sock->set_buffer_allocator(sw_zend_string_allocator());\n    String *buffer = sock->get_read_buffer();\n    HttpContext *ctx = nullptr;\n    bool header_completed = false;\n    off_t header_crlf_offset = 0;\n    size_t total_length = 0;\n\n    if (sock->ssl_is_enable() && !sock->ssl_handshake()) {\n        RETURN_NULL();\n    }\n    zend::array_set(&hs->zclients, co->get_cid(), zconn);\n    auto remote_addr = zend::Variable(sock->get_addr());\n    int remote_port = sock->get_port();\n\n    sock->get_socket()->get_name();\n    auto server_addr = zend::Variable(sock->get_socket()->get_addr());\n    int server_port = hs->socket->get_port();\n\n    while (true) {\n    _recv_request : {\n        sock->get_socket()->recv_wait = 1;\n        ssize_t retval = sock->recv(buffer->str + buffer->length, buffer->size - buffer->length);\n        if (sw_unlikely(retval <= 0)) {\n            break;\n        }\n        buffer->length += retval;\n    }\n\n    _parse_request:\n        if (!ctx) {\n            ctx = hs->create_context(sock, zconn);\n        }\n\n        if (!header_completed) {\n            ssize_t pos = swoole_strnpos(\n                buffer->str + header_crlf_offset, buffer->length - header_crlf_offset, ZEND_STRL(\"\\r\\n\\r\\n\"));\n            if (pos < 0) {\n                if (buffer->length == buffer->size) {\n                    ctx->response.status = SW_HTTP_REQUEST_ENTITY_TOO_LARGE;\n                    break;\n                }\n                header_crlf_offset = buffer->length > 4 ? buffer->length - 4 : 0;\n                continue;\n            }\n\n            size_t header_length = header_crlf_offset + pos;\n            header_completed = true;\n            header_crlf_offset = 0;\n\n            // The HTTP header must be parsed first\n            // Header contains CRLFx2\n            header_length += 4;\n            size_t parsed_n = ctx->parse(buffer->str, header_length);\n            if (parsed_n != header_length) {\n                ctx->response.status = SW_HTTP_BAD_REQUEST;\n                break;\n            }\n            buffer->offset += header_length;\n            total_length = header_length + ctx->get_content_length();\n            if (ctx->get_content_length() > 0 && total_length > sock->protocol.package_max_length) {\n                ctx->response.status = SW_HTTP_REQUEST_ENTITY_TOO_LARGE;\n                break;\n            }\n            if (total_length > buffer->size) {\n                buffer->extend(total_length);\n            }\n        }\n\n        if (!ctx->completed) {\n            // Make sure the complete request package is received\n            if (ctx->recv_chunked && memcmp(buffer->str + buffer->length - (sizeof(SW_HTTP_CHUNK_EOF) - 1),\n                                            SW_STRL(SW_HTTP_CHUNK_EOF)) != 0) {\n                goto _recv_request;\n            }\n            if (buffer->length < total_length) {\n                goto _recv_request;\n            }\n\n            size_t parsed_n = ctx->parse(buffer->str + buffer->offset, buffer->length - buffer->offset);\n            buffer->offset += parsed_n;\n\n            swoole_trace_log(SW_TRACE_CO_HTTP_SERVER,\n                             \"parsed_n=%zu, length=%zu, offset=%jd, completed=%u\",\n                             parsed_n,\n                             buffer->length,\n                             (intmax_t) buffer->offset,\n                             ctx->completed);\n\n            if (ctx->parser.error != HPE_OK && ctx->parser.error != HPE_PAUSED_H2_UPGRADE) {\n                ctx->response.status = SW_HTTP_BAD_REQUEST;\n                break;\n            }\n        }\n\n        zval *zserver = ctx->request.zserver;\n        HashTable *ht = Z_ARRVAL_P(zserver);\n\n        http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_TIME), (zend_long) time(nullptr));\n        http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_TIME_FLOAT), microtime());\n\n        http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), remote_addr.ptr());\n        http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_PORT), (zend_long) remote_port);\n\n        http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_ADDR), server_addr.ptr());\n        http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_PORT), (zend_long) server_port);\n\n        remote_addr.add_ref();\n        server_addr.add_ref();\n\n        if (ctx->parser.error == HPE_PAUSED_H2_UPGRADE && buffer->length >= (sizeof(SW_HTTP2_PRI_STRING) - 1) &&\n            memcmp(buffer->str, SW_HTTP2_PRI_STRING, sizeof(SW_HTTP2_PRI_STRING) - 1) == 0) {\n            buffer->offset = (sizeof(SW_HTTP2_PRI_STRING) - 1);\n            hs->recv_http2_frame(ctx);\n            /* ownership of ctx has been transferred */\n            ctx = nullptr;\n            break;\n        }\n\n        http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_PROTOCOL), SW_ZSTR_KNOWN(SW_ZEND_STR_HTTP11));\n        zend::assign_zend_string_by_val(&ctx->request.zdata, buffer->pop(SW_BUFFER_SIZE_BIG), total_length);\n\n        zend::Callable *cb = hs->get_handler(ctx);\n        zval args[2] = {*ctx->request.zobject, *ctx->response.zobject};\n        bool keep_alive = !!llhttp_should_keep_alive(&ctx->parser) && !ctx->websocket;\n        sock->get_socket()->recv_wait = 0;\n\n        if (cb) {\n            if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, 0))) {\n                php_swoole_error(E_WARNING, \"handler error\");\n            }\n        } else {\n            ctx->response.status = SW_HTTP_NOT_FOUND;\n        }\n\n        zval_dtor(&args[0]);\n        zval_dtor(&args[1]);\n        ctx = nullptr;\n\n        if (!hs->running || !keep_alive || php_swoole_socket_is_closed(zconn)) {\n            break;\n        } else {\n            header_completed = false;\n            if (buffer->length > 0) {\n                goto _parse_request;\n            } else {\n                goto _recv_request;\n            }\n        }\n    }\n\n    if (ctx) {\n        zval_dtor(ctx->request.zobject);\n        zval_dtor(ctx->response.zobject);\n    }\n    zend_hash_index_del(Z_ARRVAL_P(&hs->zclients), co->get_cid());\n}\n\nstatic PHP_METHOD(swoole_http_server_coro, shutdown) {\n    HttpServer *hs = http_server_coro_get_object(Z_OBJ_P(ZEND_THIS));\n    hs->running = false;\n    hs->socket->cancel(SW_EVENT_READ);\n\n    zend_ulong index;\n    zval *zconn;\n    ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(&hs->zclients), index, zconn) {\n        SocketImpl *sock = php_swoole_get_socket(zconn);\n        if (sock->get_socket()->recv_wait) {\n            sock->cancel(SW_EVENT_READ);\n            zend_hash_index_del(Z_ARRVAL_P(&hs->zclients), index);\n        }\n    }\n    ZEND_HASH_FOREACH_END();\n}\n\nstatic void http2_server_onRequest(const std::shared_ptr<Http2Session> &session,\n                                   const std::shared_ptr<Http2Stream> &stream) {\n    HttpContext *ctx = stream->ctx;\n    zval *zserver_http2 = ctx->request.zserver;\n    zval *zserver_http1 = session->default_ctx->request.zserver;\n\n    zend_string *key;\n    zval *zvalue;\n    ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zserver_http1), key, zvalue) {\n        http_server_add_server_array(Z_ARRVAL_P(zserver_http2), key, zvalue);\n        if (ZVAL_IS_STRING(zvalue)) {\n            Z_TRY_ADDREF_P(zvalue);\n        }\n    } ZEND_HASH_FOREACH_END();\n\n    http_server_add_server_array(Z_ARRVAL_P(zserver_http2), SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_PROTOCOL), SW_ZSTR_KNOWN(SW_ZEND_STR_HTTP2));\n\n    const auto *hs = static_cast<HttpServer *>(session->private_data);\n    zend::Callable *cb = hs->get_handler(ctx);\n    zval args[2] = {*ctx->request.zobject, *ctx->response.zobject};\n\n    if (cb) {\n        if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, true))) {\n            stream->reset(SW_HTTP2_ERROR_INTERNAL_ERROR);\n            php_swoole_error(E_WARNING, \"%s->onRequest[v2] handler error\", ZSTR_VAL(swoole_http_server_ce->name));\n        }\n    } else {\n        ctx->response.status = SW_HTTP_NOT_FOUND;\n    }\n\n    zval_ptr_dtor(&args[0]);\n    zval_ptr_dtor(&args[1]);\n}\n"
  },
  {
    "path": "ext-src/swoole_lock.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_private.h\"\n#include \"swoole_memory.h\"\n#include \"swoole_lock.h\"\n#include \"swoole_util.h\"\n#include \"swoole_timer.h\"\n\n#include <sys/file.h>\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_lock_arginfo.h\"\nEND_EXTERN_C()\n\nusing swoole::Lock;\nusing swoole::Mutex;\n#ifdef HAVE_SPINLOCK\nusing swoole::SpinLock;\n#endif\n#ifdef HAVE_RWLOCK\nusing swoole::RWLock;\n#endif\n\nstatic zend_class_entry *swoole_lock_ce;\nstatic zend_object_handlers swoole_lock_handlers;\n\nstruct LockObject {\n    Lock *lock;\n    zend_object std;\n};\n\nstatic sw_inline LockObject *lock_fetch_object(zend_object *obj) {\n    return reinterpret_cast<LockObject *>(reinterpret_cast<char *>(obj) - swoole_lock_handlers.offset);\n}\n\nstatic Lock *lock_get_ptr(const zval *zobject) {\n    return lock_fetch_object(Z_OBJ_P(zobject))->lock;\n}\n\nstatic Lock *lock_get_and_check_ptr(const zval *zobject) {\n    Lock *lock = lock_get_ptr(zobject);\n    if (UNEXPECTED(!lock)) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION, \"must call constructor first\");\n    }\n    return lock;\n}\n\nstatic void lock_set_ptr(const zval *zobject, Lock *ptr) {\n    lock_fetch_object(Z_OBJ_P(zobject))->lock = ptr;\n}\n\nstatic void lock_free_object(zend_object *object) {\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *lock_create_object(zend_class_entry *ce) {\n    auto *lock = static_cast<LockObject *>(zend_object_alloc(sizeof(LockObject), ce));\n    zend_object_std_init(&lock->std, ce);\n    object_properties_init(&lock->std, ce);\n    lock->std.handlers = &swoole_lock_handlers;\n    return &lock->std;\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_lock, __construct);\nstatic PHP_METHOD(swoole_lock, lock);\nstatic PHP_METHOD(swoole_lock, unlock);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic constexpr zend_function_entry swoole_lock_methods[] =\n{\n    PHP_ME(swoole_lock, __construct,  arginfo_class_Swoole_Lock___construct,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_lock, lock,         arginfo_class_Swoole_Lock_lock,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_lock, unlock,       arginfo_class_Swoole_Lock_unlock,       ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_lock_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_lock, \"Swoole\\\\Lock\", nullptr, swoole_lock_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_lock);\n    SW_SET_CLASS_CLONEABLE(swoole_lock, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_lock, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(swoole_lock, lock_create_object, lock_free_object, LockObject, std);\n\n    zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL(\"MUTEX\"), Lock::MUTEX);\n#ifdef HAVE_RWLOCK\n    zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL(\"RWLOCK\"), Lock::RW_LOCK);\n#endif\n#ifdef HAVE_SPINLOCK\n    zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL(\"SPINLOCK\"), Lock::SPIN_LOCK);\n#endif\n    zend_declare_property_long(swoole_lock_ce, ZEND_STRL(\"errCode\"), 0, ZEND_ACC_PUBLIC);\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_MUTEX\", Lock::MUTEX);\n#ifdef HAVE_RWLOCK\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_RWLOCK\", Lock::RW_LOCK);\n#endif\n#ifdef HAVE_SPINLOCK\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SPINLOCK\", Lock::SPIN_LOCK);\n#endif\n}\n\nstatic PHP_METHOD(swoole_lock, __construct) {\n    Lock *lock = lock_get_ptr(ZEND_THIS);\n    if (lock != nullptr) {\n        zend_throw_error(nullptr, \"Constructor of %s can only be called once\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        RETURN_FALSE;\n    }\n\n    zend_long type = Lock::MUTEX;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(type)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    switch (type) {\n#ifdef HAVE_SPINLOCK\n    case Lock::SPIN_LOCK:\n        lock = new SpinLock(true);\n        break;\n#endif\n#ifdef HAVE_RWLOCK\n    case Lock::RW_LOCK:\n        lock = new RWLock(true);\n        break;\n#endif\n    case Lock::MUTEX:\n        lock = new Mutex(true);\n        break;\n    default:\n        zend_throw_exception(swoole_exception_ce, \"lock type[%d] is not support\", type);\n        RETURN_FALSE;\n        break;\n    }\n    lock_set_ptr(ZEND_THIS, lock);\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_lock, lock) {\n    zend_long operation = LOCK_EX;\n    double timeout = -1;\n\n    ZEND_PARSE_PARAMETERS_START(0, 2)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(operation)\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    Lock *lock = lock_get_and_check_ptr(ZEND_THIS);\n    SW_LOCK_CHECK_RETURN(lock->lock(operation, swoole::sec2msec(timeout)));\n}\n\nstatic PHP_METHOD(swoole_lock, unlock) {\n    Lock *lock = lock_get_and_check_ptr(ZEND_THIS);\n    SW_LOCK_CHECK_RETURN(lock->unlock());\n}\n"
  },
  {
    "path": "ext-src/swoole_name_resolver.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_cxx.h\"\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_name_resolver_arginfo.h\"\nEND_EXTERN_C()\n\nusing swoole::NameResolver;\n\nBEGIN_EXTERN_C()\n\n#include \"ext/spl/php_spl.h\"\n\nzend_class_entry *swoole_name_resolver_context_ce;\nzend_object_handlers swoole_name_resolver_context_handlers;\n\nstruct ContextObject {\n    NameResolver::Context *context;\n    zend_object std;\n};\n\nstatic zend_always_inline NameResolver::Context *swoole_name_resolver_context_get_handle(zend_object *object) {\n    return reinterpret_cast<ContextObject *>(reinterpret_cast<char *>(object) -\n                                             swoole_name_resolver_context_handlers.offset)\n        ->context;\n}\n\nstatic zend_always_inline ContextObject *swoole_name_resolver_context_get_object(zend_object *object) {\n    return reinterpret_cast<ContextObject *>(reinterpret_cast<char *>(object) -\n                                             swoole_name_resolver_context_handlers.offset);\n}\n\nstatic zend_always_inline ContextObject *swoole_name_resolver_context_get_object_safe(zend_object *object) {\n    NameResolver::Context *name_resolver_context = swoole_name_resolver_context_get_handle(object);\n    if (UNEXPECTED(!name_resolver_context)) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION, \"must call constructor first\");\n    }\n    return swoole_name_resolver_context_get_object(object);\n}\n\nstatic zend_object *swoole_name_resolver_context_create_object(zend_class_entry *ce) {\n    auto *name_resolver_context_object = static_cast<ContextObject *>(zend_object_alloc(sizeof(ContextObject), ce));\n\n    zend_object_std_init(&name_resolver_context_object->std, ce);\n    object_properties_init(&name_resolver_context_object->std, ce);\n    name_resolver_context_object->std.handlers = &swoole_name_resolver_context_handlers;\n    name_resolver_context_object->context = new NameResolver::Context();\n\n    return &name_resolver_context_object->std;\n}\n\nstatic void swoole_name_resolver_context_free_object(zend_object *object) {\n    ContextObject *name_resolver_context_object = swoole_name_resolver_context_get_object(object);\n    delete name_resolver_context_object->context;\n    zend_object_std_dtor(&name_resolver_context_object->std);\n}\n\nZEND_METHOD(Swoole_NameResolver_Context, __construct) {\n    zend_long family = AF_INET;\n    zend_bool with_port = false;\n\n    ZEND_PARSE_PARAMETERS_START(0, 2)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(family)\n    Z_PARAM_BOOL(with_port)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    ContextObject *obj = swoole_name_resolver_context_get_object_safe(Z_OBJ_P(ZEND_THIS));\n    obj->context->with_port = with_port;\n    obj->context->type = family;\n}\n\nvoid php_swoole_name_resolver_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY_STD(\n        swoole_name_resolver_context, \"Swoole\\\\NameResolver\\\\Context\", class_Swoole_NameResolver_Context_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_name_resolver_context);\n    SW_SET_CLASS_CLONEABLE(swoole_name_resolver_context, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_name_resolver_context, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(swoole_name_resolver_context,\n                               swoole_name_resolver_context_create_object,\n                               swoole_name_resolver_context_free_object,\n                               ContextObject,\n                               std);\n}\n\nPHP_FUNCTION(swoole_name_resolver_lookup) {\n    char *name;\n    size_t l_name;\n    zval *zcontext;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_STRING(name, l_name)\n    Z_PARAM_OBJECT(zcontext)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    ContextObject *obj = swoole_name_resolver_context_get_object_safe(Z_OBJ_P(zcontext));\n    auto result = swoole_name_resolver_lookup(std::string(name, l_name), obj->context);\n    RETURN_STRINGL(result.c_str(), result.length());\n}\n\nPHP_FUNCTION(swoole_name_resolver_add) {\n    zval *zresolver;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT(zresolver)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(php_swoole_name_resolver_add(zresolver));\n}\n\nPHP_FUNCTION(swoole_name_resolver_remove) {\n    zval *zresolver;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT(zresolver)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    auto hash_1 = sw_php_spl_object_hash(zresolver);\n    bool found = false;\n    swoole_name_resolver_each(\n        [&found, hash_1, zresolver](const std::list<NameResolver>::iterator &iter) -> swTraverseOperation {\n            if (found) {\n                return SW_TRAVERSE_STOP;\n            }\n            auto hash_2 = sw_php_spl_object_hash((zval *) iter->private_data);\n            bool equals = zend_string_equals(hash_2, hash_1);\n            zend_string_release(hash_2);\n            if (iter->type == NameResolver::TYPE_PHP && iter->private_data && equals) {\n                zval_dtor(zresolver);\n                efree(iter->private_data);\n                found = true;\n                return SW_TRAVERSE_REMOVE;\n            } else {\n                return SW_TRAVERSE_KEEP;\n            }\n        });\n    zend_string_release(hash_1);\n    RETURN_BOOL(found);\n}\n\nEND_EXTERN_C()\n\nbool php_swoole_name_resolver_add(zval *zresolver) {\n    auto ce = zend_lookup_class(SW_ZSTR_KNOWN(SW_ZEND_STR_CLASS_NAME_RESOLVER));\n    if (ce == nullptr) {\n        php_swoole_fatal_error(\n            E_WARNING, \"Class \\\"%s\\\" not found\", SW_ZSTR_KNOWN(SW_ZEND_STR_CLASS_NAME_RESOLVER)->val);\n        return false;\n    }\n    if (!instanceof_function(Z_OBJCE_P(zresolver), ce)) {\n        php_swoole_fatal_error(E_WARNING,\n                               \"the given object is not an instance of %s\",\n                               SW_ZSTR_KNOWN(SW_ZEND_STR_CLASS_NAME_RESOLVER)->val);\n        return false;\n    }\n    zval_add_ref(zresolver);\n    NameResolver resolver{php_swoole_name_resolver_lookup, sw_zval_dup(zresolver), NameResolver::TYPE_PHP};\n    swoole_name_resolver_add(resolver);\n    return true;\n}\n\nstd::string php_swoole_name_resolver_lookup(const std::string &name, NameResolver::Context *ctx, void *_resolver) {\n    zval *zcluster_object;\n    zval retval;\n    zval *zresolver = (zval *) _resolver;\n\n    if (!ctx->private_data) {\n    _lookup:\n        zval zname;\n        ZVAL_STRINGL(&zname, name.c_str(), name.length());\n        zend_call_method_with_1_params(SW_Z8_OBJ_P(zresolver), nullptr, nullptr, \"lookup\", &retval, &zname);\n        zval_dtor(&zname);\n        if (Z_TYPE(retval) == IS_OBJECT) {\n            ctx->private_data = zcluster_object = (zval *) ecalloc(1, sizeof(zval));\n            ctx->dtor = [](NameResolver::Context *ctx) {\n                zval *_zcluster_object = (zval *) ctx->private_data;\n                zval_dtor(_zcluster_object);\n                efree(_zcluster_object);\n            };\n            *zcluster_object = retval;\n            ctx->cluster_ = true;\n            ctx->final_ = false;\n        } else if (Z_TYPE(retval) == IS_STRING) {\n            ctx->final_ = true;\n            ctx->cluster_ = false;\n            return {Z_STRVAL(retval), Z_STRLEN(retval)};\n        } else {\n            ctx->final_ = false;\n            ctx->cluster_ = false;\n            return \"\";\n        }\n    } else {\n        zcluster_object = (zval *) ctx->private_data;\n        // no available node, resolve again\n        sw_zend_call_method_with_0_params(zcluster_object, nullptr, nullptr, \"count\", &retval);\n        if (zval_get_long(&retval) == 0) {\n            ctx->dtor(ctx);\n            ctx->private_data = nullptr;\n            goto _lookup;\n        }\n    }\n\n    sw_zend_call_method_with_0_params(zcluster_object, nullptr, nullptr, \"pop\", &retval);\n    if (!ZVAL_IS_ARRAY(&retval)) {\n        return \"\";\n    }\n    zval *zhost = zend_hash_str_find(HASH_OF(&retval), ZEND_STRL(\"host\"));\n    if (zhost == nullptr || !ZVAL_IS_STRING(zhost)) {\n        return \"\";\n    }\n    std::string result(Z_STRVAL_P(zhost), Z_STRLEN_P(zhost));\n    if (ctx->with_port) {\n        result.append(\":\");\n        zval *zport = zend_hash_str_find(HASH_OF(&retval), ZEND_STRL(\"port\"));\n        if (zport == nullptr) {\n            return \"\";\n        }\n        result.append(std::to_string(zval_get_long(zport)));\n    }\n    zval_ptr_dtor(&retval);\n    return result;\n}\n\nNameResolver::Context *php_swoole_name_resolver_get_context(zval *zobject) {\n    return swoole_name_resolver_context_get_handle(Z_OBJ_P(zobject));\n}\n"
  },
  {
    "path": "ext-src/swoole_odbc.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2018 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_odbc.h\"\n#include \"php_swoole_cxx.h\"\n#include \"php_swoole_private.h\"\n#include \"php_swoole_cxx.h\"\n\n#include \"swoole_coroutine_system.h\"\n\n#ifdef SW_USE_ODBC\n\nstatic bool swoole_odbc_blocking = true;\n\n#ifdef SQL_ATTR_CONNECTION_POOLING\nzend_ulong pdo_odbc_pool_on = SQL_CP_OFF;\nzend_ulong pdo_odbc_pool_mode = SQL_CP_ONE_PER_HENV;\n#endif\n\nvoid swoole_odbc_set_blocking(bool blocking) {\n    swoole_odbc_blocking = blocking;\n}\n\nRETCODE swoole_odbc_SQLConnect(SQLHDBC ConnectionHandle,\n                               SQLCHAR *ServerName,\n                               SQLSMALLINT NameLength1,\n                               SQLCHAR *UserName,\n                               SQLSMALLINT NameLength2,\n                               SQLCHAR *Authentication,\n                               SQLSMALLINT NameLength3) {\n    RETCODE rc;\n    swoole_trace_log(SW_TRACE_CO_ODBC, \"SQLConnect(server=%s)\", ServerName);\n    php_swoole_async(swoole_odbc_blocking, [&]() {\n        rc = SQLConnect(ConnectionHandle, ServerName, NameLength1, UserName, NameLength2, Authentication, NameLength3);\n    });\n    return rc;\n}\n\nSQLRETURN SQL_API swoole_odbc_SQLDriverConnect(SQLHDBC hdbc,\n                                               SQLHWND hwnd,\n                                               SQLCHAR *szConnStrIn,\n                                               SQLSMALLINT cbConnStrIn,\n                                               SQLCHAR *szConnStrOut,\n                                               SQLSMALLINT cbConnStrOutMax,\n                                               SQLSMALLINT *pcbConnStrOut,\n                                               SQLUSMALLINT fDriverCompletion) {\n    RETCODE rc;\n    swoole_trace_log(SW_TRACE_CO_ODBC, \"SQLDriverConnect\");\n    php_swoole_async(swoole_odbc_blocking, [&]() {\n        rc = SQLDriverConnect(\n            hdbc, hwnd, szConnStrIn, cbConnStrIn, szConnStrOut, cbConnStrOutMax, pcbConnStrOut, fDriverCompletion);\n    });\n    return rc;\n}\n\nSQLRETURN SQL_API swoole_odbc_SQLExecDirect(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength) {\n    RETCODE rc;\n    swoole_trace_log(SW_TRACE_CO_ODBC, \"SQLExecDirect\");\n    php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLExecDirect(StatementHandle, StatementText, TextLength); });\n    return rc;\n}\n\nSQLRETURN SQL_API swoole_odbc_SQLGetInfo(SQLHDBC ConnectionHandle,\n                                         SQLUSMALLINT InfoType,\n                                         SQLPOINTER InfoValue,\n                                         SQLSMALLINT BufferLength,\n                                         SQLSMALLINT *StringLength) {\n    RETCODE rc;\n    swoole_trace_log(SW_TRACE_CO_ODBC, \"SQLGetInfo\");\n    rc = SQLGetInfo(ConnectionHandle, InfoType, InfoValue, BufferLength, StringLength);\n    return rc;\n}\n\nSQLRETURN SQL_API swoole_odbc_SQLGetDiagRec(SQLSMALLINT HandleType,\n                                            SQLHANDLE Handle,\n                                            SQLSMALLINT RecNumber,\n                                            SQLCHAR *Sqlstate,\n                                            SQLINTEGER *NativeError,\n                                            SQLCHAR *MessageText,\n                                            SQLSMALLINT BufferLength,\n                                            SQLSMALLINT *TextLength) {\n    RETCODE rc;\n    swoole_trace_log(SW_TRACE_CO_ODBC, \"SQLGetInfo\");\n    rc = SQLGetDiagRec(HandleType, Handle, RecNumber, Sqlstate, NativeError, MessageText, BufferLength, TextLength);\n    return rc;\n}\n\nSQLRETURN SQL_API swoole_odbc_SQLPrepare(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength) {\n    RETCODE rc;\n    swoole_trace_log(SW_TRACE_CO_ODBC, \"SQLPrepare(StatementText=%s)\", StatementText);\n    php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLPrepare(StatementHandle, StatementText, TextLength); });\n    return rc;\n}\n\nSQLRETURN SQL_API swoole_odbc_SQLExecute(SQLHSTMT StatementHandle) {\n    RETCODE rc;\n    swoole_trace_log(SW_TRACE_CO_ODBC, \"SQLExecute\");\n    php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLExecute(StatementHandle); });\n    return rc;\n}\n\nSQLRETURN SQL_API swoole_odbc_SQLCloseCursor(SQLHSTMT StatementHandle) {\n    RETCODE rc;\n    swoole_trace_log(SW_TRACE_CO_ODBC, \"SQLCloseCursor\");\n    rc = SQLCloseCursor(StatementHandle);\n    return rc;\n}\n\nSQLRETURN SQL_API swoole_odbc_SQLPutData(SQLHSTMT StatementHandle, SQLPOINTER Data, SQLLEN StrLen_or_Ind) {\n    RETCODE rc;\n    swoole_trace_log(SW_TRACE_CO_ODBC, \"SQLPutData\");\n    php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLPutData(StatementHandle, Data, StrLen_or_Ind); });\n    return rc;\n}\n\nSQLRETURN SQL_API swoole_odbc_SQLGetData(SQLHSTMT StatementHandle,\n                                         SQLUSMALLINT ColumnNumber,\n                                         SQLSMALLINT TargetType,\n                                         SQLPOINTER TargetValue,\n                                         SQLLEN BufferLength,\n                                         SQLLEN *StrLen_or_Ind) {\n    RETCODE rc;\n    swoole_trace_log(SW_TRACE_CO_ODBC, \"SQLPutData\");\n    php_swoole_async(swoole_odbc_blocking, [&]() {\n        rc = SQLGetData(StatementHandle, ColumnNumber, TargetType, TargetValue, BufferLength, StrLen_or_Ind);\n    });\n    return rc;\n}\n\nSQLRETURN SQL_API swoole_odbc_SQLMoreResults(SQLHSTMT hstmt) {\n    RETCODE rc;\n    swoole_trace_log(SW_TRACE_CO_ODBC, \"SQLMoreResults\");\n    php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLMoreResults(hstmt); });\n    return rc;\n}\n\nSQLRETURN SQL_API swoole_odbc_SQLDescribeCol(SQLHSTMT StatementHandle,\n                                             SQLUSMALLINT ColumnNumber,\n                                             SQLCHAR *ColumnName,\n                                             SQLSMALLINT BufferLength,\n                                             SQLSMALLINT *NameLength,\n                                             SQLSMALLINT *DataType,\n                                             SQLULEN *ColumnSize,\n                                             SQLSMALLINT *DecimalDigits,\n                                             SQLSMALLINT *Nullable) {\n    RETCODE rc;\n    swoole_trace_log(SW_TRACE_CO_ODBC, \"SQLMoreResults\");\n    php_swoole_async(swoole_odbc_blocking, [&]() {\n        rc = SQLDescribeCol(StatementHandle,\n                            ColumnNumber,\n                            ColumnName,\n                            BufferLength,\n                            NameLength,\n                            DataType,\n                            ColumnSize,\n                            DecimalDigits,\n                            Nullable);\n    });\n    return rc;\n}\n\nSQLRETURN SQL_API swoole_odbc_SQLRowCount(SQLHSTMT StatementHandle, SQLLEN *RowCount) {\n    RETCODE rc;\n    swoole_trace_log(SW_TRACE_CO_ODBC, \"SQLRowCount\");\n    rc = SQLRowCount(StatementHandle, RowCount);\n    return rc;\n}\n\nSQLRETURN SQL_API swoole_odbc_SQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle) {\n    RETCODE rc;\n    swoole_trace_log(SW_TRACE_CO_ODBC, \"SQLFreeHandle\");\n    rc = SQLFreeHandle(HandleType, Handle);\n    return rc;\n}\n\nSQLRETURN SQL_API swoole_odbc_SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT CompletionType) {\n    RETCODE rc;\n    swoole_trace_log(SW_TRACE_CO_ODBC, \"SQLEndTran(CompletionType=%d)\", CompletionType);\n    php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLEndTran(HandleType, Handle, CompletionType); });\n    return rc;\n}\n\nSQLRETURN SQL_API swoole_odbc_SQLDisconnect(SQLHDBC ConnectionHandle) {\n    RETCODE rc;\n    swoole_trace_log(SW_TRACE_CO_ODBC, \"SQLDisconnect\");\n    php_swoole_async(swoole_odbc_blocking, [&]() { rc = SQLDisconnect(ConnectionHandle); });\n    return rc;\n}\n\nint php_swoole_odbc_minit(int module_id) {\n    if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL(\"ODBC_ATTR_USE_CURSOR_LIBRARY\")) ==\n        nullptr) {\n#ifdef SQL_ATTR_CONNECTION_POOLING\n        const char *pooling_val = NULL;\n#endif\n\n#ifdef SQL_ATTR_CONNECTION_POOLING\n        /* ugh, we don't really like .ini stuff in PDO, but since ODBC connection\n         * pooling is process wide, we can't set it from within the scope of a\n         * request without affecting others, which goes against our isolated request\n         * policy.  So, we use cfg_get_string here to check it this once.\n         * */\n        if (FAILURE == cfg_get_string(\"pdo_odbc.connection_pooling\", (char **) &pooling_val) || pooling_val == NULL) {\n            pooling_val = \"strict\";\n        }\n        if (strcasecmp(pooling_val, \"strict\") == 0 || strcmp(pooling_val, \"1\") == 0) {\n            pdo_odbc_pool_on = SQL_CP_ONE_PER_HENV;\n            pdo_odbc_pool_mode = SQL_CP_STRICT_MATCH;\n        } else if (strcasecmp(pooling_val, \"relaxed\") == 0) {\n            pdo_odbc_pool_on = SQL_CP_ONE_PER_HENV;\n            pdo_odbc_pool_mode = SQL_CP_RELAXED_MATCH;\n        } else if (*pooling_val == '\\0' || strcasecmp(pooling_val, \"off\") == 0) {\n            pdo_odbc_pool_on = SQL_CP_OFF;\n        } else {\n            php_error_docref(NULL,\n                             E_CORE_ERROR,\n                             \"Error in pdo_odbc.connection_pooling configuration. Value must be one of \\\"strict\\\", \"\n                             \"\\\"relaxed\\\", or \\\"off\\\"\");\n            return FAILURE;\n        }\n\n        if (pdo_odbc_pool_on != SQL_CP_OFF) {\n            SQLSetEnvAttr(SQL_NULL_HANDLE, SQL_ATTR_CONNECTION_POOLING, (void *) pdo_odbc_pool_on, 0);\n        }\n#endif\n\n#if PHP_VERSION_ID >= 80500\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"ODBC_ATTR_USE_CURSOR_LIBRARY\", PDO_ODBC_ATTR_USE_CURSOR_LIBRARY);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"ODBC_ATTR_ASSUME_UTF8\", PDO_ODBC_ATTR_ASSUME_UTF8);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"ODBC_SQL_USE_IF_NEEDED\", SQL_CUR_USE_IF_NEEDED);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"ODBC_SQL_USE_DRIVER\", SQL_CUR_USE_DRIVER);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"ODBC_SQL_USE_ODBC\", SQL_CUR_USE_ODBC);\n#else\n        REGISTER_PDO_CLASS_CONST_LONG(\"ODBC_ATTR_USE_CURSOR_LIBRARY\", PDO_ODBC_ATTR_USE_CURSOR_LIBRARY);\n        REGISTER_PDO_CLASS_CONST_LONG(\"ODBC_ATTR_ASSUME_UTF8\", PDO_ODBC_ATTR_ASSUME_UTF8);\n        REGISTER_PDO_CLASS_CONST_LONG(\"ODBC_SQL_USE_IF_NEEDED\", SQL_CUR_USE_IF_NEEDED);\n        REGISTER_PDO_CLASS_CONST_LONG(\"ODBC_SQL_USE_DRIVER\", SQL_CUR_USE_DRIVER);\n        REGISTER_PDO_CLASS_CONST_LONG(\"ODBC_SQL_USE_ODBC\", SQL_CUR_USE_ODBC);\n#endif\n    }\n\n    php_pdo_unregister_driver(&swoole_pdo_odbc_driver);\n    php_pdo_register_driver(&swoole_pdo_odbc_driver);\n\n    return SUCCESS;\n}\n\nvoid php_swoole_odbc_mshutdown(void) {\n    php_pdo_unregister_driver(&swoole_pdo_odbc_driver);\n}\n\n#endif\n"
  },
  {
    "path": "ext-src/swoole_oracle.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | Copyright (c) 2012-2018 The Swoole Group                             |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: NathanFreeman  <mariasocute@163.com>                         |\n  +----------------------------------------------------------------------+\n */\n#include \"php_swoole_private.h\"\n#include \"php_swoole_cxx.h\"\n#include \"swoole_coroutine.h\"\n#include \"php_swoole_oracle.h\"\n\n#ifdef SW_USE_ORACLE\n\nstatic bool swoole_oracle_blocking = true;\n\nvoid swoole_oracle_set_blocking(bool blocking) {\n    swoole_oracle_blocking = blocking;\n}\n\nsword swoole_oci_session_begin(OCISvcCtx *svchp, OCIError *errhp, OCISession *usrhp, ub4 credt, ub4 mode) {\n    swoole_trace_log(SW_TRACE_CO_ORACLE, \"oci_session_begin\");\n    sword result = 0;\n    php_swoole_async(swoole_oracle_blocking, [&]() { result = OCISessionBegin(svchp, errhp, usrhp, credt, mode); });\n\n    return result;\n}\n\nsword swoole_oci_server_detach(OCIServer *srvhp, OCIError *errhp, ub4 mode) {\n    swoole_trace_log(SW_TRACE_CO_ORACLE, \"oci_server_detach\");\n    sword result = 0;\n    php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIServerDetach(srvhp, errhp, mode); });\n\n    return result;\n}\n\nsword swoole_oci_stmt_prepare(\n    OCIStmt *stmtp, OCIError *errhp, const OraText *stmt, ub4 stmt_len, ub4 language, ub4 mode) {\n    swoole_trace_log(SW_TRACE_CO_ORACLE, \"oci_stmt_prepare\");\n    sword result = 0;\n    php_swoole_async(swoole_oracle_blocking,\n                     [&]() { result = OCIStmtPrepare(stmtp, errhp, stmt, stmt_len, language, mode); });\n\n    return result;\n}\n\nsword swoole_oci_stmt_execute(OCISvcCtx *svchp,\n                              OCIStmt *stmtp,\n                              OCIError *errhp,\n                              ub4 iters,\n                              ub4 rowoff,\n                              const OCISnapshot *snap_in,\n                              OCISnapshot *snap_out,\n                              ub4 mode) {\n    swoole_trace_log(SW_TRACE_CO_ORACLE, \"oci_stmt_execute\");\n    sword result = 0;\n    php_swoole_async(swoole_oracle_blocking,\n                     [&]() { result = OCIStmtExecute(svchp, stmtp, errhp, iters, rowoff, snap_in, snap_out, mode); });\n\n    return result;\n}\n\nsword swoole_oci_stmt_fetch(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, ub4 mode) {\n    swoole_trace_log(SW_TRACE_CO_ORACLE, \"oci_stmt_fetch\");\n    sword result = 0;\n    php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIStmtFetch(stmtp, errhp, nrows, orientation, mode); });\n\n    return result;\n}\n\nsword swoole_oci_stmt_fetch2(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, sb4 scrollOffset, ub4 mode) {\n    swoole_trace_log(SW_TRACE_CO_ORACLE, \"oci_stmt_fetch2\");\n    sword result = 0;\n    php_swoole_async(swoole_oracle_blocking,\n                     [&]() { result = OCIStmtFetch2(stmtp, errhp, nrows, orientation, scrollOffset, mode); });\n\n    return result;\n}\n\nsword swoole_oci_trans_commit(OCISvcCtx *svchp, OCIError *errhp, ub4 flags) {\n    swoole_trace_log(SW_TRACE_CO_ORACLE, \"oci_trans_commit\");\n    sword result = 0;\n    php_swoole_async(swoole_oracle_blocking, [&]() { result = OCITransCommit(svchp, errhp, flags); });\n\n    return result;\n}\n\nsword swoole_oci_trans_rollback(OCISvcCtx *svchp, OCIError *errhp, ub4 flags) {\n    swoole_trace_log(SW_TRACE_CO_ORACLE, \"oci_trans_rollback\");\n    sword result = 0;\n    php_swoole_async(swoole_oracle_blocking, [&]() { result = OCITransRollback(svchp, errhp, flags); });\n\n    return result;\n}\n\nsword swoole_oci_ping(OCISvcCtx *svchp, OCIError *errhp, ub4 mode) {\n    swoole_trace_log(SW_TRACE_CO_ORACLE, \"oci_ping\");\n    sword result = 0;\n    php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIPing(svchp, errhp, mode); });\n\n    return result;\n}\n\nconst ub4 SWOOLE_PDO_OCI_INIT_MODE = OCI_DEFAULT | OCI_THREADED\n#ifdef OCI_OBJECT\n                                     | OCI_OBJECT\n#endif\n    ;\n\nOCIEnv *swoole_pdo_oci_Env = NULL;\n\nvoid php_swoole_oracle_rinit() {\n    if (!swoole_pdo_oci_Env) {\n#ifdef HAVE_OCIENVCREATE\n        OCIEnvCreate(&swoole_pdo_oci_Env, SWOOLE_PDO_OCI_INIT_MODE, NULL, NULL, NULL, NULL, 0, NULL);\n#else\n        OCIInitialize(SWOOLE_PDO_OCI_INIT_MODE, NULL, NULL, NULL, NULL);\n        OCIEnvInit(&swoole_pdo_oci_Env, OCI_DEFAULT, 0, NULL);\n#endif\n    }\n}\n\nvoid php_swoole_oracle_minit(int module_id) {\n    if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL(\"OCI_ATTR_ACTION\")) == nullptr) {\n#if PHP_VERSION_ID >= 80500\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"OCI_ATTR_ACTION\", (zend_long) PDO_OCI_ATTR_ACTION);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"OCI_ATTR_CLIENT_INFO\", (zend_long) PDO_OCI_ATTR_CLIENT_INFO);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"OCI_ATTR_CLIENT_IDENTIFIER\", (zend_long) PDO_OCI_ATTR_CLIENT_IDENTIFIER);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"OCI_ATTR_MODULE\", (zend_long) PDO_OCI_ATTR_MODULE);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"OCI_ATTR_CALL_TIMEOUT\", (zend_long) PDO_OCI_ATTR_CALL_TIMEOUT);\n#else\n        REGISTER_PDO_CLASS_CONST_LONG(\"OCI_ATTR_ACTION\", (zend_long) PDO_OCI_ATTR_ACTION);\n        REGISTER_PDO_CLASS_CONST_LONG(\"OCI_ATTR_CLIENT_INFO\", (zend_long) PDO_OCI_ATTR_CLIENT_INFO);\n        REGISTER_PDO_CLASS_CONST_LONG(\"OCI_ATTR_CLIENT_IDENTIFIER\", (zend_long) PDO_OCI_ATTR_CLIENT_IDENTIFIER);\n        REGISTER_PDO_CLASS_CONST_LONG(\"OCI_ATTR_MODULE\", (zend_long) PDO_OCI_ATTR_MODULE);\n        REGISTER_PDO_CLASS_CONST_LONG(\"OCI_ATTR_CALL_TIMEOUT\", (zend_long) PDO_OCI_ATTR_CALL_TIMEOUT);\n#endif\n    }\n\n    php_pdo_unregister_driver(&swoole_pdo_oci_driver);\n    php_pdo_register_driver(&swoole_pdo_oci_driver);\n}\n\nvoid php_swoole_oracle_mshutdown(void) {\n    php_pdo_unregister_driver(&swoole_pdo_oci_driver);\n\n    if (!swoole_pdo_oci_Env) {\n        OCIHandleFree((dvoid *) swoole_pdo_oci_Env, OCI_HTYPE_ENV);\n    }\n}\n#endif\n"
  },
  {
    "path": "ext-src/swoole_pgsql.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2018 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_pgsql.h\"\n#include \"php_swoole_private.h\"\n#include \"php_swoole_coroutine.h\"\n\n#include \"swoole_socket_impl.h\"\n\n#ifdef SW_USE_PGSQL\n\nusing swoole::Coroutine;\nusing swoole::EventType;\nusing swoole::Reactor;\nusing swoole::translate_events_to_poll;\n\nstatic bool swoole_pgsql_blocking = true;\n/**\n * In macOS, under special circumstances, the poll function will not return a readable or writable signal. It is\n * necessary to set a relatively short poll timeout value to allow it to fail quickly. Then, re-execute PQconnectPoll,\n * and let the libpq underlying code determine whether the connection is ready or if an error has occurred.\n */\nstatic const double swoole_pgsql_poll_timeout = 0.1;\n\nvoid swoole_libpq_version(char *buf, size_t len)\n{\n    int version = PQlibVersion();\n    int major = version / 10000;\n    if (major >= 10) {\n        int minor = version % 10000;\n        snprintf(buf, len, \"%d.%d\", major, minor);\n    } else {\n        int minor = version / 100 % 100;\n        int revision = version % 100;\n        snprintf(buf, len, \"%d.%d.%d\", major, minor, revision);\n    }\n}\n\nstatic int swoole_pgsql_socket_poll(PGconn *conn, EventType event, bool check_nonblock = false) {\n    if (swoole_pgsql_blocking) {\n        struct pollfd fds[1];\n        fds[0].fd = PQsocket(conn);\n        fds[0].events |= translate_events_to_poll(event);\n\n        int result = 0;\n        do {\n            result = poll(fds, 1, swoole_pgsql_poll_timeout * 1000);\n        } while (result < 0 && errno == EINTR);\n\n        return result;\n    }\n\n    SocketImpl sock(PQsocket(conn), SW_SOCK_RAW);\n    sock.get_socket()->nonblock = 1;\n\n    bool retval = sock.poll(event, swoole_pgsql_poll_timeout);\n    while (check_nonblock && event == SW_EVENT_READ) {\n        if (PQconsumeInput(conn) == 0) {\n            retval = false;\n            break;\n        }\n        if (PQisBusy(conn) == 0) {\n            break;\n        }\n        retval = sock.poll(event, swoole_pgsql_poll_timeout);\n    }\n\n    sock.move_fd();\n    return retval ? 1 : sock.errCode == ETIMEDOUT ? 0 : -1;\n}\n\nstatic int swoole_pgsql_flush(PGconn *conn) {\n    int flush_ret;\n\n    do {\n        int ret = swoole_pgsql_socket_poll(conn, SW_EVENT_WRITE);\n        if (sw_unlikely(ret < 0)) {\n            return -1;\n        }\n        swoole_trace_log(SW_TRACE_CO_PGSQL, \"PQflush(conn=%p)\", conn);\n        flush_ret = PQflush(conn);\n    } while (flush_ret == 1);\n\n    return flush_ret;\n}\n\nstatic PGresult *swoole_pgsql_get_result(PGconn *conn) {\n    PGresult *result, *last_result = nullptr;\n    // PQgetResult will block the process; it is necessary to forcibly check if the data is ready.\n    int poll_ret = swoole_pgsql_socket_poll(conn, SW_EVENT_READ, true);\n    if (sw_unlikely(poll_ret < 0)) {\n        return nullptr;\n    }\n\n    swoole_trace_log(SW_TRACE_CO_PGSQL, \"PQgetResult(conn=%p)\", conn);\n    while ((result = PQgetResult(conn))) {\n        PQclear(last_result);\n        last_result = result;\n    }\n\n    return last_result;\n}\n\nPGconn *swoole_pgsql_connectdb(const char *conninfo) {\n    PGconn *conn = PQconnectStart(conninfo);\n    if (conn == nullptr) {\n        return nullptr;\n    }\n\n    int fd = PQsocket(conn);\n    if (sw_unlikely(fd < 0)) {\n        return conn;\n    }\n\n    if (!swoole_pgsql_blocking && Coroutine::get_current()) {\n        PQsetnonblocking(conn, 1);\n    } else {\n        PQsetnonblocking(conn, 0);\n    }\n\n    SW_LOOP {\n        int r = PQconnectPoll(conn);\n        if (r == PGRES_POLLING_OK || r == PGRES_POLLING_FAILED) {\n            break;\n        }\n        EventType event;\n\n        switch (r) {\n        case PGRES_POLLING_READING:\n            event = SW_EVENT_READ;\n            break;\n        case PGRES_POLLING_WRITING:\n            event = SW_EVENT_WRITE;\n            break;\n        default:\n            // should not be here including PGRES_POLLING_ACTIVE\n            abort();\n            break;\n        }\n\n        if (swoole_pgsql_socket_poll(conn, event) < 0) {\n            break;\n        }\n    }\n\n    return conn;\n}\n\nPGresult *swoole_pgsql_prepare(\n    PGconn *conn, const char *stmt_name, const char *query, int n_params, const Oid *param_types) {\n    swoole_trace_log(SW_TRACE_CO_PGSQL, \"PQsendPrepare(conn=%p, stmt_name='%s')\", conn, stmt_name);\n    int ret = PQsendPrepare(conn, stmt_name, query, n_params, param_types);\n    if (ret == 0) {\n        return nullptr;\n    }\n\n    if (swoole_pgsql_flush(conn) == -1) {\n        return nullptr;\n    }\n\n    return swoole_pgsql_get_result(conn);\n}\n\nPGresult *swoole_pgsql_exec_prepared(PGconn *conn,\n                                     const char *stmt_name,\n                                     int n_params,\n                                     const char *const *param_values,\n                                     const int *param_lengths,\n                                     const int *param_formats,\n                                     int result_format) {\n    swoole_trace_log(SW_TRACE_CO_PGSQL, \"PQsendQueryPrepared(conn=%p, stmt_name='%s')\", conn, stmt_name);\n    int ret = PQsendQueryPrepared(conn, stmt_name, n_params, param_values, param_lengths, param_formats, result_format);\n    if (ret == 0) {\n        return nullptr;\n    }\n\n    if (swoole_pgsql_flush(conn) == -1) {\n        return nullptr;\n    }\n\n    return swoole_pgsql_get_result(conn);\n}\n\nPGresult *swoole_pgsql_exec(PGconn *conn, const char *query) {\n    swoole_trace_log(SW_TRACE_CO_PGSQL, \"PQsendQuery(conn=%p, query='%s')\", conn, query);\n    int ret = PQsendQuery(conn, query);\n    if (ret == 0) {\n        return nullptr;\n    }\n\n    if (swoole_pgsql_flush(conn) == -1) {\n        return nullptr;\n    }\n\n    return swoole_pgsql_get_result(conn);\n}\n\nPGresult *swoole_pgsql_exec_params(PGconn *conn,\n                                   const char *command,\n                                   int n_params,\n                                   const Oid *param_types,\n                                   const char *const *param_values,\n                                   const int *param_lengths,\n                                   const int *param_formats,\n                                   int result_format) {\n    swoole_trace_log(SW_TRACE_CO_PGSQL, \"PQsendQueryParams(conn=%p, command='%s')\", conn, command);\n    int ret = PQsendQueryParams(\n        conn, command, n_params, param_types, param_values, param_lengths, param_formats, result_format);\n    if (ret == 0) {\n        return nullptr;\n    }\n\n    if (swoole_pgsql_flush(conn) == -1) {\n        return nullptr;\n    }\n\n    return swoole_pgsql_get_result(conn);\n}\n\nvoid swoole_pgsql_set_blocking(bool blocking) {\n    swoole_pgsql_blocking = blocking;\n}\n\nvoid php_swoole_pgsql_minit(int module_id) {\n    if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL(\"PGSQL_ATTR_DISABLE_PREPARES\")) ==\n        nullptr) {\n#if PHP_VERSION_ID >= 80500\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"PGSQL_ATTR_DISABLE_PREPARES\", PDO_PGSQL_ATTR_DISABLE_PREPARES);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"PGSQL_TRANSACTION_IDLE\", (zend_long) PGSQL_TRANSACTION_IDLE);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"PGSQL_TRANSACTION_ACTIVE\", (zend_long) PGSQL_TRANSACTION_ACTIVE);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"PGSQL_TRANSACTION_INTRANS\", (zend_long) PGSQL_TRANSACTION_INTRANS);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"PGSQL_TRANSACTION_INERROR\", (zend_long) PGSQL_TRANSACTION_INERROR);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"PGSQL_TRANSACTION_UNKNOWN\", (zend_long) PGSQL_TRANSACTION_UNKNOWN);\n#else\n        REGISTER_PDO_CLASS_CONST_LONG(\"PGSQL_ATTR_DISABLE_PREPARES\", PDO_PGSQL_ATTR_DISABLE_PREPARES);\n        REGISTER_PDO_CLASS_CONST_LONG(\"PGSQL_TRANSACTION_IDLE\", (zend_long) PGSQL_TRANSACTION_IDLE);\n        REGISTER_PDO_CLASS_CONST_LONG(\"PGSQL_TRANSACTION_ACTIVE\", (zend_long) PGSQL_TRANSACTION_ACTIVE);\n        REGISTER_PDO_CLASS_CONST_LONG(\"PGSQL_TRANSACTION_INTRANS\", (zend_long) PGSQL_TRANSACTION_INTRANS);\n        REGISTER_PDO_CLASS_CONST_LONG(\"PGSQL_TRANSACTION_INERROR\", (zend_long) PGSQL_TRANSACTION_INERROR);\n        REGISTER_PDO_CLASS_CONST_LONG(\"PGSQL_TRANSACTION_UNKNOWN\", (zend_long) PGSQL_TRANSACTION_UNKNOWN);\n#endif\n    }\n    php_pdo_unregister_driver(&swoole_pdo_pgsql_driver);\n    php_pdo_register_driver(&swoole_pdo_pgsql_driver);\n}\n\nvoid php_swoole_pgsql_mshutdown(void) {\n    php_pdo_unregister_driver(&swoole_pdo_pgsql_driver);\n}\n\n#endif\n"
  },
  {
    "path": "ext-src/swoole_process.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_cxx.h\"\n#include \"php_swoole_process.h\"\n\n#include \"swoole_server.h\"\n#include \"swoole_msg_queue.h\"\n#include \"swoole_signal.h\"\n\n#include <sys/ipc.h>\n#include <sys/resource.h>\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_process_arginfo.h\"\nEND_EXTERN_C()\n\nusing namespace swoole;\n\nzend_class_entry *swoole_process_ce;\nstatic zend_object_handlers swoole_process_handlers;\n\nstatic uint32_t round_process_id = 0;\nstatic thread_local uint32_t server_user_worker_id = 0;\nstatic zend::Callable *signal_fci_caches[SW_SIGNO_MAX] = {};\n\nstruct ProcessObject {\n    Worker *worker;\n    zend_object *zsocket;\n    PipeType pipe_type;\n    bool enable_coroutine;\n    bool blocking;\n    zend_object std;\n};\n\nstatic sw_inline ProcessObject *php_swoole_process_fetch_object(zend_object *obj) {\n    return reinterpret_cast<ProcessObject *>(reinterpret_cast<char *>(obj) - swoole_process_handlers.offset);\n}\n\nstatic sw_inline ProcessObject *php_swoole_process_fetch_object(const zval *zobj) {\n    return php_swoole_process_fetch_object(Z_OBJ_P(zobj));\n}\n\nWorker *php_swoole_process_get_worker(const zval *zobject) {\n    return php_swoole_process_fetch_object(zobject)->worker;\n}\n\nWorker *php_swoole_process_get_and_check_worker(const zval *zobject) {\n    Worker *worker = php_swoole_process_get_worker(zobject);\n    if (UNEXPECTED(!worker)) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION, \"must call constructor first\");\n    }\n    return worker;\n}\n\nvoid php_swoole_process_set_worker(const zval *zobject, Worker *worker, bool enable_coroutine, int pipe_type) {\n    auto po = php_swoole_process_fetch_object(zobject);\n    po->worker = worker;\n    po->pipe_type = static_cast<PipeType>(pipe_type);\n    po->enable_coroutine = enable_coroutine;\n    po->blocking = true;\n}\n\nstatic void php_swoole_process_free_object(zend_object *object) {\n    ProcessObject *po = php_swoole_process_fetch_object(object);\n    Worker *worker = po->worker;\n\n    if (worker) {\n        UnixSocket *_pipe = worker->pipe_object;\n        if (_pipe && !worker->shared) {\n            delete _pipe;\n        }\n        delete worker->queue;\n        delete worker;\n    }\n\n    if (po->zsocket) {\n        OBJ_RELEASE(po->zsocket);\n    }\n\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *php_swoole_process_create_object(zend_class_entry *ce) {\n    auto *process = static_cast<ProcessObject *>(zend_object_alloc(sizeof(ProcessObject), ce));\n    zend_object_std_init(&process->std, ce);\n    object_properties_init(&process->std, ce);\n    process->std.handlers = &swoole_process_handlers;\n    return &process->std;\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_process, __construct);\nstatic PHP_METHOD(swoole_process, __destruct);\nstatic PHP_METHOD(swoole_process, useQueue);\nstatic PHP_METHOD(swoole_process, statQueue);\nstatic PHP_METHOD(swoole_process, freeQueue);\nstatic PHP_METHOD(swoole_process, pop);\nstatic PHP_METHOD(swoole_process, push);\nstatic PHP_METHOD(swoole_process, kill);\nstatic PHP_METHOD(swoole_process, signal);\nstatic PHP_METHOD(swoole_process, alarm);\nstatic PHP_METHOD(swoole_process, wait);\nstatic PHP_METHOD(swoole_process, daemon);\n#ifdef HAVE_CPU_AFFINITY\nstatic PHP_METHOD(swoole_process, setAffinity);\nstatic PHP_METHOD(swoole_process, getAffinity);\n#endif\nstatic PHP_METHOD(swoole_process, set);\nstatic PHP_METHOD(swoole_process, setTimeout);\nstatic PHP_METHOD(swoole_process, setBlocking);\nstatic PHP_METHOD(swoole_process, setPriority);\nstatic PHP_METHOD(swoole_process, getPriority);\nstatic PHP_METHOD(swoole_process, start);\nstatic PHP_METHOD(swoole_process, write);\nstatic PHP_METHOD(swoole_process, read);\nstatic PHP_METHOD(swoole_process, close);\nstatic PHP_METHOD(swoole_process, exit);\nstatic PHP_METHOD(swoole_process, exec);\nstatic PHP_METHOD(swoole_process, exportSocket);\nSW_EXTERN_C_END\n\nstatic void php_swoole_onSignal(int signo);\n\n// clang-format off\n#define MSGQUEUE_NOWAIT (1 << 8)\n\nstatic const zend_function_entry swoole_process_methods[] =\n{\n    PHP_ME(swoole_process, __construct, arginfo_class_Swoole_Process___construct, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, __destruct,  arginfo_class_Swoole_Process___destruct,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, wait,        arginfo_class_Swoole_Process_wait,        ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_process, signal,      arginfo_class_Swoole_Process_signal,      ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_process, alarm,       arginfo_class_Swoole_Process_alarm,       ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_process, kill,        arginfo_class_Swoole_Process_kill,        ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_process, daemon,      arginfo_class_Swoole_Process_daemon,      ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n#ifdef HAVE_CPU_AFFINITY\n    PHP_ME(swoole_process, setAffinity, arginfo_class_Swoole_Process_setAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_process, getAffinity, arginfo_class_Swoole_Process_getAffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n#endif\n    PHP_ME(swoole_process, setPriority,       arginfo_class_Swoole_Process_setPriority,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, getPriority,       arginfo_class_Swoole_Process_getPriority,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, set,               arginfo_class_Swoole_Process_set,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, setTimeout,        arginfo_class_Swoole_Process_setTimeout,   ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, setBlocking,       arginfo_class_Swoole_Process_setBlocking,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, useQueue,          arginfo_class_Swoole_Process_useQueue,     ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, statQueue,         arginfo_class_Swoole_Process_statQueue,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, freeQueue,         arginfo_class_Swoole_Process_freeQueue,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, start,             arginfo_class_Swoole_Process_start,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, write,             arginfo_class_Swoole_Process_write,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, close,             arginfo_class_Swoole_Process_close,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, read,              arginfo_class_Swoole_Process_read,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, push,              arginfo_class_Swoole_Process_push,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, pop,               arginfo_class_Swoole_Process_pop,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, exit,              arginfo_class_Swoole_Process_exit,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, exec,              arginfo_class_Swoole_Process_exec,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process, exportSocket,      arginfo_class_Swoole_Process_exportSocket, ZEND_ACC_PUBLIC)\n    PHP_FALIAS(name, swoole_set_process_name, arginfo_class_Swoole_Process_name)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_process_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_process, \"Swoole\\\\Process\", nullptr, swoole_process_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_process);\n    SW_SET_CLASS_CLONEABLE(swoole_process, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_process, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(\n        swoole_process, php_swoole_process_create_object, php_swoole_process_free_object, ProcessObject, std);\n\n    zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL(\"IPC_NOWAIT\"), MSGQUEUE_NOWAIT);\n    zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL(\"PIPE_MASTER\"), SW_PIPE_CLOSE_MASTER);\n    zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL(\"PIPE_WORKER\"), SW_PIPE_CLOSE_WORKER);\n    zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL(\"PIPE_READ\"), SW_PIPE_CLOSE_READ);\n    zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL(\"PIPE_WRITE\"), SW_PIPE_CLOSE_WRITE);\n    zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL(\"PIPE_TYPE_NONE\"), PIPE_TYPE_NONE);\n    zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL(\"PIPE_TYPE_STREAM\"), PIPE_TYPE_STREAM);\n    zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL(\"PIPE_TYPE_DGRAM\"), PIPE_TYPE_DGRAM);\n\n    zend_declare_property_null(swoole_process_ce, ZEND_STRL(\"pipe\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_process_ce, ZEND_STRL(\"msgQueueId\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_process_ce, ZEND_STRL(\"msgQueueKey\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_process_ce, ZEND_STRL(\"pid\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_process_ce, ZEND_STRL(\"id\"), ZEND_ACC_PUBLIC);\n\n    zend_declare_property_null(swoole_process_ce, ZEND_STRL(\"callback\"), ZEND_ACC_PRIVATE);\n\n    /**\n     * 31 signal constants\n     */\n    if (!zend_hash_str_find(&module_registry, ZEND_STRL(\"pcntl\"))) {\n        REGISTER_LONG_CONSTANT(\"SIGHUP\", SIGHUP, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGINT\", SIGINT, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGQUIT\", SIGQUIT, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGILL\", SIGILL, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGTRAP\", SIGTRAP, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGABRT\", SIGABRT, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGBUS\", SIGBUS, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGFPE\", SIGFPE, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGKILL\", SIGKILL, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGUSR1\", SIGUSR1, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGSEGV\", SIGSEGV, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGUSR2\", SIGUSR2, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGPIPE\", SIGPIPE, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGALRM\", SIGALRM, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGTERM\", SIGTERM, CONST_CS | CONST_PERSISTENT);\n#ifdef SIGSTKFLT\n        REGISTER_LONG_CONSTANT(\"SIGSTKFLT\", SIGSTKFLT, CONST_CS | CONST_PERSISTENT);\n#endif\n        REGISTER_LONG_CONSTANT(\"SIGCHLD\", SIGCHLD, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGCONT\", SIGCONT, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGSTOP\", SIGSTOP, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGTSTP\", SIGTSTP, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGTTIN\", SIGTTIN, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGTTOU\", SIGTTOU, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGURG\", SIGURG, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGXCPU\", SIGXCPU, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGXFSZ\", SIGXFSZ, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGVTALRM\", SIGVTALRM, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGPROF\", SIGPROF, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGWINCH\", SIGWINCH, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"SIGIO\", SIGIO, CONST_CS | CONST_PERSISTENT);\n#ifdef SIGPWR\n        REGISTER_LONG_CONSTANT(\"SIGPWR\", SIGPWR, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef SIGSYS\n        REGISTER_LONG_CONSTANT(\"SIGSYS\", SIGSYS, CONST_CS | CONST_PERSISTENT);\n#endif\n        REGISTER_LONG_CONSTANT(\"SIG_IGN\", (zend_long) SIG_IGN, CONST_CS | CONST_PERSISTENT);\n\n        REGISTER_LONG_CONSTANT(\"PRIO_PROCESS\", PRIO_PROCESS, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"PRIO_PGRP\", PRIO_PGRP, CONST_CS | CONST_PERSISTENT);\n        REGISTER_LONG_CONSTANT(\"PRIO_USER\", PRIO_USER, CONST_CS | CONST_PERSISTENT);\n    }\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_MSGQUEUE_ORIENT\", SW_MSGQUEUE_ORIENT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_MSGQUEUE_BALANCE\", SW_MSGQUEUE_BALANCE);\n}\n\nstatic PHP_METHOD(swoole_process, __construct) {\n    auto po = php_swoole_process_fetch_object(ZEND_THIS);\n    Server *server = sw_server();\n\n    if (po->worker) {\n        zend_throw_error(nullptr, \"Constructor of %s can only be called once\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        RETURN_FALSE;\n    }\n\n    // only cli env\n    if (!SWOOLE_G(cli)) {\n        zend_throw_error(nullptr, \"%s can only be used in PHP CLI mode\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        RETURN_FALSE;\n    }\n\n    if (server && server->is_started() && server->is_master()) {\n        zend_throw_error(nullptr, \"%s can't be used in master process\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        RETURN_FALSE;\n    }\n\n    if (SwooleTG.async_threads) {\n        zend_throw_error(nullptr, \"unable to create %s with async-io threads\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        RETURN_FALSE;\n    }\n\n    zend::Function func;\n    zend_bool redirect_stdin_and_stdout = false;\n    zend_long pipe_type = PIPE_TYPE_DGRAM;\n    zend_bool enable_coroutine = false;\n\n    po->worker = new Worker();\n    Worker *process = po->worker;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 4)\n    Z_PARAM_FUNC(func.fci, func.fci_cache);\n    Z_PARAM_OPTIONAL\n    Z_PARAM_BOOL(redirect_stdin_and_stdout)\n    Z_PARAM_LONG(pipe_type)\n    Z_PARAM_BOOL(enable_coroutine)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (server && server->is_worker_thread()) {\n        Worker *shared_worker;\n        if (server->is_user_worker()) {\n            shared_worker = server->get_worker(swoole_get_worker_id());\n        } else {\n            shared_worker = server->get_worker((server_user_worker_id++) + server->get_core_worker_num());\n        }\n        *process = *shared_worker;\n        process->shared = true;\n        if (server->is_user_worker()) {\n            process->pipe_current = process->pipe_worker;\n        } else {\n            process->pipe_current = process->pipe_master;\n        }\n    } else {\n        if (redirect_stdin_and_stdout) {\n            process->redirect_stdin = true;\n            process->redirect_stdout = true;\n            process->redirect_stderr = true;\n            /**\n             * Forced to use stream pipe\n             */\n            pipe_type = PIPE_TYPE_STREAM;\n        }\n\n        uint32_t base = 1;\n        if (server && server->is_started()) {\n            base = server->get_all_worker_num();\n        }\n        if (round_process_id == 0) {\n            round_process_id = base;\n        }\n        process->id = round_process_id++;\n        process->shared = false;\n\n        if (pipe_type > 0) {\n            int socket_type = pipe_type == PIPE_TYPE_STREAM ? SOCK_STREAM : SOCK_DGRAM;\n            auto *_pipe = new UnixSocket(true, socket_type);\n            if (!_pipe->ready()) {\n                zend_throw_exception(swoole_exception_ce, \"failed to create unix soccket\", errno);\n                delete _pipe;\n                efree(process);\n                RETURN_FALSE;\n            }\n\n            process->pipe_master = _pipe->get_socket(true);\n            process->pipe_worker = _pipe->get_socket(false);\n\n            process->pipe_object = _pipe;\n            process->pipe_current = process->pipe_master;\n\n            zend_update_property_long(\n                swoole_process_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"pipe\"), process->pipe_master->fd);\n        }\n    }\n\n    zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"id\"), process->id);\n    zend_update_property(\n        swoole_process_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"callback\"), ZEND_CALL_ARG(execute_data, 1));\n    php_swoole_process_set_worker(ZEND_THIS, process, enable_coroutine, pipe_type);\n}\n\nstatic PHP_METHOD(swoole_process, __destruct) {}\n\nstatic PHP_METHOD(swoole_process, wait) {\n    zend_bool blocking = true;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"|b\", &blocking) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    auto exit_status = swoole::wait_process(-1, blocking ? 0 : WNOHANG);\n    if (exit_status.get_pid() > 0) {\n        array_init(return_value);\n        add_assoc_long(return_value, \"pid\", exit_status.get_pid());\n        add_assoc_long(return_value, \"code\", exit_status.get_code());\n        add_assoc_long(return_value, \"signal\", exit_status.get_signal());\n    } else {\n        RETURN_FALSE;\n    }\n}\n\nstatic PHP_METHOD(swoole_process, useQueue) {\n    long msgkey = 0;\n    long mode = SW_MSGQUEUE_BALANCE;\n    long capacity = -1;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"|lll\", &msgkey, &mode, &capacity) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS);\n\n    if (msgkey <= 0) {\n        msgkey = ftok(zend_get_executed_filename(), 1);\n    }\n\n    auto *queue = new MsgQueue(msgkey);\n    if (!queue->ready()) {\n        delete queue;\n        RETURN_FALSE;\n    }\n    if (mode & MSGQUEUE_NOWAIT) {\n        queue->set_blocking(false);\n        mode = mode & (~MSGQUEUE_NOWAIT);\n    }\n    if (capacity > 0) {\n        queue->set_capacity(capacity);\n    }\n    process->queue = queue;\n    process->msgqueue_mode = mode;\n    zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"msgQueueId\"), queue->get_id());\n    zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"msgQueueKey\"), msgkey);\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_process, statQueue) {\n    Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS);\n    if (!process->queue) {\n        php_swoole_fatal_error(E_WARNING, \"no queue, can't get stats of the queue\");\n        RETURN_FALSE;\n    }\n\n    size_t queue_num = -1;\n    size_t queue_bytes = -1;\n    if (process->queue->stat(&queue_num, &queue_bytes)) {\n        array_init(return_value);\n        add_assoc_long_ex(return_value, ZEND_STRL(\"queue_num\"), queue_num);\n        add_assoc_long_ex(return_value, ZEND_STRL(\"queue_bytes\"), queue_bytes);\n    } else {\n        RETURN_FALSE;\n    }\n}\n\nstatic PHP_METHOD(swoole_process, freeQueue) {\n    Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS);\n    if (process->queue && process->queue->destroy()) {\n        delete process->queue;\n        process->queue = nullptr;\n        RETURN_TRUE;\n    } else {\n        RETURN_FALSE;\n    }\n}\n\nstatic PHP_METHOD(swoole_process, kill) {\n    zend_long pid;\n    zend_long sig = SIGTERM;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"l|l\", &pid, &sig) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    int ret = swoole_kill((int) pid, (int) sig);\n    if (ret < 0) {\n        if (!(sig == 0 && errno == ESRCH)) {\n            php_swoole_sys_error(E_WARNING, \"kill(%d, %d) failed\", (int) pid, (int) sig);\n        }\n        RETURN_FALSE;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_process, signal) {\n    SW_MUST_BE_MAIN_THREAD();\n    zend_long signo = 0;\n    zval *zcallback = nullptr;\n    zend::Callable *fci_cache = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_LONG(signo)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_ZVAL_EX(zcallback, 1, 0)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (!SWOOLE_G(cli)) {\n        php_swoole_fatal_error(E_ERROR, \"%s::signal can only be used in CLI mode\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        RETURN_FALSE;\n    }\n\n    if (signo < 0 || signo >= SW_SIGNO_MAX) {\n        php_swoole_fatal_error(E_WARNING, \"invalid signal number [\" ZEND_LONG_FMT \"]\", signo);\n        RETURN_FALSE;\n    }\n\n    swSignalHandler handler = swoole_signal_get_handler(signo);\n    if (handler && handler != php_swoole_onSignal) {\n        php_swoole_fatal_error(\n            E_WARNING, \"signal [\" ZEND_LONG_FMT \"] processor has been registered by the system\", signo);\n        RETURN_FALSE;\n    }\n\n    if (zcallback == nullptr) {\n        fci_cache = signal_fci_caches[signo];\n        if (fci_cache) {\n#ifdef SW_USE_THREAD_CONTEXT\n            swoole_event_defer([signo](void *) { swoole_signal_set(signo, nullptr); }, nullptr);\n#else\n            swoole_signal_set(signo, nullptr);\n#endif\n            signal_fci_caches[signo] = nullptr;\n            swoole_event_defer(sw_callable_free, fci_cache);\n            SwooleG.signal_listener_num--;\n            RETURN_TRUE;\n        } else {\n            php_swoole_error(E_WARNING, \"unable to find the callback of signal [\" ZEND_LONG_FMT \"]\", signo);\n            RETURN_FALSE;\n        }\n    } else if (Z_TYPE_P(zcallback) == IS_LONG && Z_LVAL_P(zcallback) == (zend_long) SIG_IGN) {\n        handler = nullptr;\n    } else {\n        fci_cache = sw_callable_create(zcallback);\n        if (!fci_cache) {\n            RETURN_FALSE;\n        }\n        handler = php_swoole_onSignal;\n    }\n\n    if (sw_server() && sw_server()->is_sync_process()) {\n        if (signal_fci_caches[signo]) {\n            sw_callable_free(signal_fci_caches[signo]);\n        } else {\n            SwooleG.signal_listener_num++;\n        }\n        signal_fci_caches[signo] = fci_cache;\n#ifdef SW_USE_THREAD_CONTEXT\n        swoole_event_defer([signo, handler](void *) { swoole_signal_set(signo, handler, true); }, nullptr);\n#else\n        swoole_signal_set(signo, handler, true);\n#endif\n        RETURN_TRUE;\n    }\n\n    php_swoole_check_reactor();\n    if (!SwooleTG.reactor->isset_exit_condition(Reactor::EXIT_CONDITION_SIGNAL_LISTENER)) {\n        SwooleTG.reactor->set_exit_condition(Reactor::EXIT_CONDITION_SIGNAL_LISTENER,\n                                             [](Reactor *reactor, size_t &event_num) -> bool {\n                                                 return SwooleG.signal_listener_num == 0 or !SwooleG.wait_signal;\n                                             });\n    }\n\n    if (signal_fci_caches[signo]) {\n        // free the old fci_cache\n        swoole_event_defer(sw_callable_free, signal_fci_caches[signo]);\n    } else {\n        SwooleG.signal_listener_num++;\n    }\n    signal_fci_caches[signo] = fci_cache;\n\n#ifdef SW_USE_THREAD_CONTEXT\n    swoole_event_defer([signo, handler](void *) { swoole_signal_set(signo, handler, true); }, nullptr);\n#else\n    swoole_signal_set(signo, handler, true);\n#endif\n\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_process, alarm) {\n    zend_long usec;\n    zend_long type = ITIMER_REAL;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"l|l\", &usec, &type) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    if (!SWOOLE_G(cli)) {\n        php_swoole_fatal_error(E_ERROR, \"cannot use %s::alarm here\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        RETURN_FALSE;\n    }\n\n    if (SwooleTG.timer) {\n        php_swoole_fatal_error(E_WARNING, \"cannot use both 'timer' and 'alarm' at the same time\");\n        RETURN_FALSE;\n    }\n\n    struct itimerval timer_set = {};\n\n    if (usec > 0) {\n        long _sec = usec / 1000000;\n        long _usec = usec - (_sec * 1000000);\n\n        timer_set.it_interval.tv_sec = _sec;\n        timer_set.it_interval.tv_usec = _usec;\n\n        timer_set.it_value.tv_sec = _sec;\n        timer_set.it_value.tv_usec = _usec;\n\n        if (timer_set.it_value.tv_usec > 1e6) {\n            timer_set.it_value.tv_usec = timer_set.it_value.tv_usec - 1e6;\n            timer_set.it_value.tv_sec += 1;\n        }\n    }\n\n    if (setitimer(type, &timer_set, nullptr) < 0) {\n        php_swoole_sys_error(E_WARNING, \"setitimer() failed\");\n        RETURN_FALSE;\n    }\n\n    RETURN_TRUE;\n}\n\n/**\n * safe signal\n */\nstatic void php_swoole_onSignal(int signo) {\n    auto fci_cache = signal_fci_caches[signo];\n\n    if (fci_cache) {\n        zval argv[1];\n        ZVAL_LONG(&argv[0], signo);\n\n        if (UNEXPECTED(!zend::function::call(fci_cache->ptr(), 1, argv, nullptr, php_swoole_is_enable_coroutine()))) {\n            php_swoole_fatal_error(\n                E_WARNING, \"%s: signal [%d] handler error\", ZSTR_VAL(swoole_process_ce->name), signo);\n        }\n    }\n}\n\nzend_bool php_swoole_signal_isset_handler(int signo) {\n    if (signo < 0 || signo >= SW_SIGNO_MAX) {\n        php_swoole_fatal_error(E_WARNING, \"invalid signal number [%d]\", signo);\n        return false;\n    }\n    return signal_fci_caches[signo] != nullptr;\n}\n\nvoid php_swoole_process_clean() {\n    for (auto &signal_fci_cache : signal_fci_caches) {\n        const auto fci_cache = signal_fci_cache;\n        if (fci_cache) {\n            sw_callable_free(fci_cache);\n            signal_fci_cache = nullptr;\n        }\n    }\n#ifndef SW_THREAD\n    if (swoole_get_worker_type() != SW_USER_WORKER) {\n        swoole_set_worker_type(0);\n    }\n#endif\n}\n\nvoid php_swoole_process_rshutdown() {\n    php_swoole_process_clean();\n}\n\nint php_swoole_process_start(Worker *process, zval *zobject) {\n    zval *zcallback = sw_zend_read_property_ex(swoole_process_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_CALLBACK), 0);\n    auto fci_cache = sw_callable_create(zcallback);\n    if (!fci_cache) {\n        return SW_ERR;\n    }\n\n    process->pipe_current = process->pipe_worker;\n    process->pid = getpid();\n\n    if (process->redirect_stdin) {\n        if (dup2(process->pipe_current->fd, STDIN_FILENO) < 0) {\n            php_swoole_sys_error(E_WARNING, \"dup2() failed\");\n        }\n    }\n\n    if (process->redirect_stdout) {\n        if (dup2(process->pipe_current->fd, STDOUT_FILENO) < 0) {\n            php_swoole_sys_error(E_WARNING, \"dup2() failed\");\n        }\n    }\n\n    if (process->redirect_stderr) {\n        if (dup2(process->pipe_current->fd, STDERR_FILENO) < 0) {\n            php_swoole_sys_error(E_WARNING, \"dup2() failed\");\n        }\n    }\n\n    php_swoole_process_clean();\n    swoole_set_worker_id(process->id);\n    swoole_set_worker_pid(getpid());\n    SwooleWG.worker = process;\n\n    zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"pid\"), process->pid);\n    if (process->pipe_current) {\n        zend_update_property_long(\n            swoole_process_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"pipe\"), process->pipe_current->fd);\n    }\n    auto po = php_swoole_process_fetch_object(zobject);\n    // eventloop create\n    if (po->enable_coroutine && php_swoole_reactor_init() < 0) {\n        sw_callable_free(fci_cache);\n        return SW_ERR;\n    }\n    // main function\n    if (UNEXPECTED(!zend::function::call(fci_cache->ptr(), 1, zobject, nullptr, po->enable_coroutine))) {\n        php_swoole_error(E_WARNING, \"%s->onStart handler error\", SW_Z_OBJCE_NAME_VAL_P(zobject));\n    }\n    // eventloop start\n    if (po->enable_coroutine) {\n        php_swoole_event_wait();\n    }\n    sw_callable_free(fci_cache);\n    // equivalent to exit\n    zend_bailout();\n\n    return SW_OK;\n}\n\nstatic PHP_METHOD(swoole_process, start) {\n    Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS);\n\n    if (process->pid && swoole_kill(process->pid, 0) == 0) {\n        php_swoole_fatal_error(E_WARNING, \"process has already been started\");\n        RETURN_FALSE;\n    }\n\n    pid_t pid = swoole_fork(0);\n    if (pid < 0) {\n        php_swoole_sys_error(E_WARNING, \"fork() failed\");\n        RETURN_FALSE;\n    } else if (pid > 0) {\n        process->pid = pid;\n        process->child_process = 0;\n        zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"pid\"), process->pid);\n        RETURN_LONG(pid);\n    } else {\n        process->child_process = 1;\n        SW_CHECK_RETURN(php_swoole_process_start(process, ZEND_THIS));\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_process, read) {\n    zend_long buf_size = 8192;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(buf_size)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    const Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS);\n    if (process->pipe_current == nullptr) {\n        php_swoole_fatal_error(E_WARNING, \"no pipe, cannot read from pipe\");\n        RETURN_FALSE;\n    }\n\n    ssize_t ret;\n    zend_string *buf = zend_string_alloc(buf_size, false);\n    const auto po = php_swoole_process_fetch_object(ZEND_THIS);\n    if (po->blocking) {\n        ret = process->pipe_current->read_sync(buf->val, buf_size);\n    } else {\n        ret = process->pipe_current->read(buf->val, buf_size);\n    }\n\n    if (ret < 0) {\n        efree(buf);\n        RETURN_FALSE;\n    }\n    buf->val[ret] = 0;\n    buf->len = ret;\n    RETURN_STR(buf);\n}\n\nstatic PHP_METHOD(swoole_process, write) {\n    char *data = nullptr;\n    size_t data_len = 0;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"s\", &data, &data_len) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STRING(data, data_len)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (data_len < 1) {\n        php_swoole_fatal_error(E_WARNING, \"the data to send is empty\");\n        RETURN_FALSE;\n    }\n\n    Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS);\n    if (process->pipe_current == nullptr) {\n        php_swoole_fatal_error(E_WARNING, \"no pipe, cannot write into pipe\");\n        RETURN_FALSE;\n    }\n\n    ssize_t ret;\n    const auto po = php_swoole_process_fetch_object(ZEND_THIS);\n    if (!po->blocking && swoole_event_is_available()) {\n        ret = swoole_event_write(process->pipe_current, data, data_len);\n    } else {\n        ret = process->pipe_current->send_sync(data, data_len);\n    }\n\n    if (ret < 0) {\n        php_swoole_sys_error(E_WARNING, \"write() failed\");\n        RETURN_FALSE;\n    }\n    ZVAL_LONG(return_value, ret);\n}\n\n/**\n * export Swoole\\Coroutine\\Socket object\n */\nstatic PHP_METHOD(swoole_process, exportSocket) {\n    auto po = php_swoole_process_fetch_object(ZEND_THIS);\n    if (!po->worker || po->worker->pipe_current == nullptr) {\n        php_swoole_fatal_error(E_WARNING, \"no pipe, cannot export stream\");\n        RETURN_FALSE;\n    }\n    if (!po->zsocket) {\n        po->zsocket = php_swoole_dup_socket(\n            po->worker->pipe_current->fd, po->pipe_type == PIPE_TYPE_STREAM ? SW_SOCK_UNIX_STREAM : SW_SOCK_UNIX_DGRAM);\n        if (!po->zsocket) {\n            RETURN_FALSE;\n        }\n    }\n    GC_ADDREF(po->zsocket);\n    RETURN_OBJ(po->zsocket);\n}\n\nstatic PHP_METHOD(swoole_process, push) {\n    char *data;\n    size_t length;\n\n    struct {\n        long type;\n        char data[SW_MSGMAX];\n    } message;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STRING(data, length)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (length <= 0) {\n        php_swoole_fatal_error(E_WARNING, \"the data to push is empty\");\n        RETURN_FALSE;\n    } else if (length >= sizeof(message.data)) {\n        php_swoole_fatal_error(E_WARNING, \"the data to push is too big\");\n        RETURN_FALSE;\n    }\n\n    Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS);\n\n    if (!process->queue) {\n        php_swoole_fatal_error(E_WARNING, \"no msgqueue, cannot use push()\");\n        RETURN_FALSE;\n    }\n\n    message.type = process->id + 1;\n    memcpy(message.data, data, length);\n\n    if (!process->queue->push((QueueNode *) &message, length)) {\n        RETURN_FALSE;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_process, pop) {\n    zend_long maxsize = SW_MSGMAX;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(maxsize)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (maxsize > SW_MSGMAX || maxsize <= 0) {\n        maxsize = SW_MSGMAX;\n    }\n\n    Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS);\n    if (!process->queue) {\n        php_swoole_fatal_error(E_WARNING, \"no msgqueue, cannot use pop()\");\n        RETURN_FALSE;\n    }\n\n    struct {\n        long type;\n        char data[SW_MSGMAX];\n    } message;\n\n    if (process->msgqueue_mode == SW_MSGQUEUE_BALANCE) {\n        message.type = 0;\n    } else {\n        message.type = process->id + 1;\n    }\n\n    ssize_t n = process->queue->pop((QueueNode *) &message, maxsize);\n    if (n < 0) {\n        RETURN_FALSE;\n    }\n    RETURN_STRINGL(message.data, n);\n}\n\nstatic PHP_METHOD(swoole_process, exec) {\n    char *execfile = nullptr;\n    size_t execfile_len = 0;\n    zval *args;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"sa\", &execfile, &execfile_len, &args) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    if (execfile_len < 1) {\n        php_swoole_fatal_error(E_WARNING, \"exec file name is empty\");\n        RETURN_FALSE;\n    }\n\n    int exec_argc = php_swoole_array_length(args);\n    char **exec_args = (char **) emalloc(sizeof(char *) * (exec_argc + 2));\n\n    zval *value = nullptr;\n    exec_args[0] = sw_strdup(execfile);\n    int i = 1;\n\n    ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args), value) {\n        auto arg_str = zval_get_string(value);\n        exec_args[i] = ZSTR_VAL(arg_str);\n        i++;\n    }\n    ZEND_HASH_FOREACH_END();\n\n    exec_args[i] = nullptr;\n\n    if (execv(execfile, exec_args) < 0) {\n        php_swoole_sys_error(E_WARNING, \"execv(%s) failed\", execfile);\n        RETURN_FALSE;\n    } else {\n        RETURN_TRUE;\n    }\n}\n\nstatic PHP_METHOD(swoole_process, daemon) {\n    zend_bool nochdir = true;\n    zend_bool noclose = true;\n    zval *zpipes = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(0, 3)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_BOOL(nochdir)\n    Z_PARAM_BOOL(noclose)\n    Z_PARAM_ARRAY(zpipes)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (zpipes) {\n        int fd = 0;\n        zval *elem;\n        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zpipes), elem) {\n            if (!ZVAL_IS_NULL(elem)) {\n                int new_fd = php_swoole_convert_to_fd(elem);\n                if (new_fd >= 0) {\n                    if (dup2(new_fd, fd) < 0) {\n                        swoole_sys_warning(\"dup2(%d, %d) failed\", new_fd, fd);\n                    }\n                }\n            }\n            if (fd++ == 2) {\n                break;\n            }\n        }\n        ZEND_HASH_FOREACH_END();\n    }\n\n    RETURN_BOOL(swoole_daemon(nochdir, noclose) == 0);\n}\n\n#ifdef HAVE_CPU_AFFINITY\nbool php_swoole_array_to_cpu_set(const zval *array, cpu_set_t *cpu_set) {\n    if (php_swoole_array_length(array) == 0) {\n        return false;\n    }\n\n    if (php_swoole_array_length(array) > SW_CPU_NUM) {\n        php_swoole_fatal_error(E_WARNING, \"More than the number of CPU\");\n        return false;\n    }\n\n    zval *value = nullptr;\n    CPU_ZERO(cpu_set);\n\n    SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(array), value)\n    if (zval_get_long(value) >= SW_CPU_NUM) {\n        php_swoole_fatal_error(E_WARNING, \"invalid cpu id [%d]\", (int) Z_LVAL_P(value));\n        return false;\n    }\n    CPU_SET(Z_LVAL_P(value), cpu_set);\n    SW_HASHTABLE_FOREACH_END();\n\n    return true;\n}\n\nvoid php_swoole_cpu_set_to_array(zval *array, cpu_set_t *cpu_set) {\n    array_init(array);\n\n    int cpu_n = SW_CPU_NUM;\n    SW_LOOP_N(cpu_n) {\n        if (CPU_ISSET(i, cpu_set)) {\n            add_next_index_long(array, i);\n        }\n    }\n}\n\nstatic PHP_METHOD(swoole_process, setAffinity) {\n    zval *array;\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ARRAY(array)\n    ZEND_PARSE_PARAMETERS_END();\n\n    cpu_set_t cpu_set;\n    if (!php_swoole_array_to_cpu_set(array, &cpu_set)) {\n        RETURN_FALSE;\n    }\n\n    if (swoole_set_cpu_affinity(&cpu_set) < 0) {\n        php_swoole_sys_error(E_WARNING, \"sched_setaffinity() failed\");\n        RETURN_FALSE;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_process, getAffinity) {\n    cpu_set_t cpu_set;\n    if (swoole_get_cpu_affinity(&cpu_set) < 0) {\n        php_swoole_sys_error(E_WARNING, \"sched_getaffinity() failed\");\n        RETURN_FALSE;\n    }\n    php_swoole_cpu_set_to_array(return_value, &cpu_set);\n}\n#endif\n\nstatic PHP_METHOD(swoole_process, exit) {\n    long ret_code = 0;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"|l\", &ret_code) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS);\n\n    if (getpid() != process->pid) {\n        php_swoole_fatal_error(E_WARNING, \"not current process\");\n        RETURN_FALSE;\n    }\n\n    if (ret_code < 0 || ret_code > 255) {\n        php_swoole_fatal_error(E_WARNING, \"exit ret_code range is [>0 and <255] \");\n        ret_code = 1;\n    }\n\n    if (swoole_event_is_available()) {\n        swoole_event_free();\n    }\n\n    exit(static_cast<int>(ret_code));\n}\n\nstatic PHP_METHOD(swoole_process, close) {\n    long which = 0;\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"|l\", &which) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS);\n    if (process->pipe_current == nullptr) {\n        php_swoole_fatal_error(E_WARNING, \"no pipe, cannot close the pipe\");\n        RETURN_FALSE;\n    }\n\n    if (process->pipe_object == nullptr) {\n        php_swoole_fatal_error(E_WARNING, \"cannot close the pipe\");\n        RETURN_FALSE;\n    }\n\n    int ret;\n    if (which == SW_PIPE_CLOSE_READ) {\n        ret = process->pipe_current->shutdown(SHUT_RD);\n    } else if (which == SW_PIPE_CLOSE_WRITE) {\n        ret = process->pipe_current->shutdown(SHUT_WR);\n    } else {\n        ret = process->pipe_object->close(which);\n    }\n    if (ret < 0) {\n        php_swoole_sys_error(E_WARNING, \"close() failed\");\n        RETURN_FALSE;\n    }\n    if (which == 0) {\n        delete process->pipe_object;\n        process->pipe_object = nullptr;\n        process->pipe_current = nullptr;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_process, set) {\n    zval *zset = nullptr;\n    HashTable *vht = nullptr;\n    zval *ztmp;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ARRAY(zset)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    vht = Z_ARRVAL_P(zset);\n    auto po = php_swoole_process_fetch_object(ZEND_THIS);\n    if (UNEXPECTED(!po->worker)) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION, \"must call constructor first\");\n    }\n    if (php_swoole_array_get_value(vht, \"enable_coroutine\", ztmp)) {\n        po->enable_coroutine = zval_is_true(ztmp);\n    }\n}\n\nstatic PHP_METHOD(swoole_process, setTimeout) {\n    double seconds;\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"d\", &seconds) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS);\n    if (process->pipe_current == nullptr) {\n        php_swoole_fatal_error(E_WARNING, \"no pipe, cannot setTimeout the pipe\");\n        RETURN_FALSE;\n    }\n    process->pipe_current->set_timeout(seconds);\n    RETURN_BOOL(process->pipe_current->set_kernel_timeout(seconds));\n}\n\nstatic PHP_METHOD(swoole_process, setBlocking) {\n    zend_bool blocking;\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"b\", &blocking) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    auto po = php_swoole_process_fetch_object(ZEND_THIS);\n    if (po->worker == nullptr || po->worker->pipe_current == nullptr) {\n        php_swoole_fatal_error(E_WARNING, \"no pipe, cannot setBlocking the pipe\");\n        RETURN_FALSE;\n    }\n    po->blocking = blocking;\n    if (blocking) {\n        RETURN_BOOL(po->worker->pipe_current->set_block());\n    } else {\n        RETURN_BOOL(po->worker->pipe_current->set_nonblock());\n    }\n}\n\n#define SW_CHECK_PRIORITY_WHO()                                                                                        \\\n    if (who_is_null) {                                                                                                 \\\n        if (which == PRIO_PROCESS) {                                                                                   \\\n            Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS);                                      \\\n            who = process->pid;                                                                                        \\\n        } else {                                                                                                       \\\n            php_swoole_fatal_error(E_WARNING, \"$who parameter must not be null\");                                      \\\n            swoole_set_last_error(SW_ERROR_INVALID_PARAMS);                                                            \\\n            RETURN_FALSE;                                                                                              \\\n        }                                                                                                              \\\n    }\n\nstatic PHP_METHOD(swoole_process, setPriority) {\n    zend_long which, priority, who;\n    bool who_is_null = true;\n\n    ZEND_PARSE_PARAMETERS_START(2, 3)\n    Z_PARAM_LONG(which)\n    Z_PARAM_LONG(priority)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG_OR_NULL(who, who_is_null)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    SW_CHECK_PRIORITY_WHO();\n    if (setpriority(which, who, priority) < 0) {\n        swoole_set_last_error(errno);\n        RETURN_FALSE;\n    } else {\n        RETURN_TRUE;\n    }\n}\n\nstatic PHP_METHOD(swoole_process, getPriority) {\n    zend_long which, who;\n    bool who_is_null = true;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_LONG(which)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG_OR_NULL(who, who_is_null)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    SW_CHECK_PRIORITY_WHO();\n    errno = 0;\n    int priority = getpriority(which, who);\n    if (priority == -1 && errno != 0) {\n        swoole_set_last_error(errno);\n        RETURN_FALSE;\n    } else {\n        RETURN_LONG(priority);\n    }\n}\n"
  },
  {
    "path": "ext-src/swoole_process_pool.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_cxx.h\"\n#include \"php_swoole_process.h\"\n\n#include \"swoole_server.h\"\n#include \"swoole_signal.h\"\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_process_pool_arginfo.h\"\nEND_EXTERN_C()\n\nusing namespace swoole;\n\nstatic zend_class_entry *swoole_process_pool_ce;\nstatic zend_object_handlers swoole_process_pool_handlers;\nstatic ProcessPool *current_pool = nullptr;\nstatic Worker *current_worker = nullptr;\n\nstruct ProcessPoolObject {\n    ProcessPool *pool;\n    zend::Callable *onStart;\n    zend::Callable *onShutdown;\n    zend::Callable *onWorkerStart;\n    zend::Callable *onWorkerStop;\n    zend::Callable *onWorkerExit;\n    zend::Callable *onMessage;\n    zend_bool enable_coroutine;\n    zend_bool enable_message_bus;\n    zend_object std;\n};\n\nstatic void process_pool_signal_handler(int signo);\n\nstatic sw_inline ProcessPoolObject *process_pool_fetch_object(zend_object *obj) {\n    return reinterpret_cast<ProcessPoolObject *>(reinterpret_cast<char *>(obj) - swoole_process_pool_handlers.offset);\n}\n\nstatic sw_inline ProcessPoolObject *process_pool_fetch_object(const zval *zobject) {\n    return process_pool_fetch_object(Z_OBJ_P(zobject));\n}\n\nstatic sw_inline ProcessPool *process_pool_get_pool(const zval *zobject) {\n    return process_pool_fetch_object(Z_OBJ_P(zobject))->pool;\n}\n\nstatic sw_inline ProcessPool *process_pool_get_and_check_pool(const zval *zobject) {\n    ProcessPool *pool = process_pool_get_pool(zobject);\n    if (UNEXPECTED(!pool)) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION, \"must call constructor first\");\n    }\n    return pool;\n}\n\nstatic void process_pool_free_object(zend_object *object) {\n    ProcessPoolObject *pp = process_pool_fetch_object(object);\n\n    ProcessPool *pool = pp->pool;\n    if (pool) {\n        efree(pool->ptr);\n        pool->destroy();\n        efree(pool);\n    }\n\n    if (pp->onWorkerStart) {\n        sw_callable_free(pp->onWorkerStart);\n    }\n    if (pp->onMessage) {\n        sw_callable_free(pp->onMessage);\n    }\n    if (pp->onWorkerStop) {\n        sw_callable_free(pp->onWorkerStop);\n    }\n    if (pp->onStart) {\n        sw_callable_free(pp->onStart);\n    }\n    if (pp->onWorkerExit) {\n        sw_callable_free(pp->onWorkerExit);\n    }\n    if (pp->onShutdown) {\n        sw_callable_free(pp->onShutdown);\n    }\n\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *process_pool_create_object(zend_class_entry *ce) {\n    auto *pp = static_cast<ProcessPoolObject *>(zend_object_alloc(sizeof(ProcessPoolObject), ce));\n    zend_object_std_init(&pp->std, ce);\n    object_properties_init(&pp->std, ce);\n    pp->std.handlers = &swoole_process_pool_handlers;\n    return &pp->std;\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_process_pool, __construct);\nstatic PHP_METHOD(swoole_process_pool, __destruct);\nstatic PHP_METHOD(swoole_process_pool, set);\nstatic PHP_METHOD(swoole_process_pool, on);\nstatic PHP_METHOD(swoole_process_pool, listen);\nstatic PHP_METHOD(swoole_process_pool, write);\nstatic PHP_METHOD(swoole_process_pool, sendMessage);\nstatic PHP_METHOD(swoole_process_pool, detach);\nstatic PHP_METHOD(swoole_process_pool, getProcess);\nstatic PHP_METHOD(swoole_process_pool, start);\nstatic PHP_METHOD(swoole_process_pool, stop);\nstatic PHP_METHOD(swoole_process_pool, shutdown);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic const zend_function_entry swoole_process_pool_methods[] =\n{\n    PHP_ME(swoole_process_pool, __construct, arginfo_class_Swoole_Process_Pool___construct, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process_pool, __destruct,  arginfo_class_Swoole_Process_Pool___destruct,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process_pool, set,         arginfo_class_Swoole_Process_Pool_set,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process_pool, on,          arginfo_class_Swoole_Process_Pool_on,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process_pool, getProcess,  arginfo_class_Swoole_Process_Pool_getProcess,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process_pool, listen,      arginfo_class_Swoole_Process_Pool_listen,      ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process_pool, write,       arginfo_class_Swoole_Process_Pool_write,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process_pool, sendMessage, arginfo_class_Swoole_Process_Pool_sendMessage, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process_pool, detach,      arginfo_class_Swoole_Process_Pool_detach,      ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process_pool, start,       arginfo_class_Swoole_Process_Pool_start,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process_pool, stop,        arginfo_class_Swoole_Process_Pool_stop,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_process_pool, shutdown,    arginfo_class_Swoole_Process_Pool_shutdown,    ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_process_pool_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_process_pool, \"Swoole\\\\Process\\\\Pool\", nullptr, swoole_process_pool_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_process_pool);\n    SW_SET_CLASS_CLONEABLE(swoole_process_pool, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_process_pool, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(\n        swoole_process_pool, process_pool_create_object, process_pool_free_object, ProcessPoolObject, std);\n\n    zend_declare_property_long(swoole_process_pool_ce, ZEND_STRL(\"master_pid\"), -1, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_process_pool_ce, ZEND_STRL(\"workerPid\"), -1, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_process_pool_ce, ZEND_STRL(\"workerId\"), -1, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_process_pool_ce, ZEND_STRL(\"workers\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_bool(swoole_process_pool_ce, ZEND_STRL(\"workerRunning\"), -1, ZEND_ACC_PUBLIC);\n    zend_declare_property_bool(swoole_process_pool_ce, ZEND_STRL(\"running\"), -1, ZEND_ACC_PUBLIC);\n}\n\nstatic void process_pool_onWorkerStart(ProcessPool *pool, Worker *worker) {\n    auto zobject = static_cast<zval *>(pool->ptr);\n    auto pp = process_pool_fetch_object(zobject);\n    php_swoole_process_clean();\n\n    current_pool = pool;\n    current_worker = worker;\n\n    zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"running\"), true);\n    zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"workerRunning\"), true);\n    zend_update_property_long(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"workerPid\"), getpid());\n    zend_update_property_long(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"workerId\"), worker->id);\n\n    swoole_set_worker_type(SW_WORKER);\n    SwooleG.enable_coroutine = pp->enable_coroutine;\n\n    if (pp->onWorkerStart) {\n        zval args[2];\n        args[0] = *zobject;\n        ZVAL_LONG(&args[1], worker->id);\n        if (UNEXPECTED(!zend::function::call(pp->onWorkerStart->ptr(), 2, args, nullptr, pp->enable_coroutine))) {\n            php_swoole_error(E_WARNING, \"%s->onWorkerStart handler error\", SW_Z_OBJCE_NAME_VAL_P(zobject));\n        }\n    }\n\n    if (!swoole_signal_isset(SIGTERM) && (pp->onMessage || pp->enable_coroutine)) {\n        swoole_signal_set(SIGTERM, process_pool_signal_handler);\n    }\n    if (!swoole_signal_isset(SIGWINCH)) {\n        swoole_signal_set(SIGWINCH, process_pool_signal_handler);\n    }\n#ifdef SIGRTMIN\n    if (!swoole_signal_isset(SIGRTMIN)) {\n        swoole_signal_set(SIGRTMIN, process_pool_signal_handler);\n    }\n#endif\n}\n\nstatic void process_pool_onMessage(ProcessPool *pool, RecvData *msg) {\n    auto zobject = static_cast<zval *>(pool->ptr);\n    auto pp = process_pool_fetch_object(zobject);\n    zval args[2];\n\n    args[0] = *zobject;\n    const char *data = msg->data;\n    uint32_t length = msg->info.len;\n    if (length == 0) {\n        ZVAL_EMPTY_STRING(&args[1]);\n    } else {\n        if (msg->info.flags & SW_EVENT_DATA_OBJ_PTR) {\n            zend::assign_zend_string_by_val(&args[1], (char *) data, length);\n            pool->message_bus->move_packet();\n        } else {\n            ZVAL_STRINGL(&args[1], data, length);\n        }\n    }\n    auto *worker = sw_worker();\n    worker->set_status_to_busy();\n    if (UNEXPECTED(!zend::function::call(pp->onMessage->ptr(), 2, args, nullptr, pp->enable_coroutine))) {\n        php_swoole_error(E_WARNING, \"%s->onMessage handler error\", SW_Z_OBJCE_NAME_VAL_P(zobject));\n    }\n    worker->add_request_count();\n    worker->set_status_to_idle();\n    zval_ptr_dtor(&args[1]);\n}\n\nstatic void process_pool_onWorkerStop(ProcessPool *pool, Worker *worker) {\n    auto zobject = static_cast<zval *>(pool->ptr);\n    ProcessPoolObject *pp = process_pool_fetch_object(zobject);\n    zval args[2];\n\n    zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"running\"), false);\n    zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"workerRunning\"), false);\n\n    if (pp->onWorkerStop == nullptr) {\n        return;\n    }\n\n    args[0] = *zobject;\n    ZVAL_LONG(&args[1], worker->id);\n\n    if (UNEXPECTED(!zend::function::call(pp->onWorkerStop->ptr(), 2, args, nullptr, false))) {\n        php_swoole_error(E_WARNING, \"%s->onWorkerStop handler error\", SW_Z_OBJCE_NAME_VAL_P(zobject));\n    }\n}\n\nstatic void process_pool_onWorkerExit(ProcessPool *pool, Worker *worker) {\n    zval *zobject = (zval *) pool->ptr;\n    ProcessPoolObject *pp = process_pool_fetch_object(zobject);\n    zval args[2];\n\n    zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"running\"), false);\n    zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"workerRunning\"), false);\n\n    if (pp->onWorkerExit == nullptr) {\n        return;\n    }\n\n    args[0] = *zobject;\n    ZVAL_LONG(&args[1], worker->id);\n\n    if (UNEXPECTED(!zend::function::call(pp->onWorkerExit->ptr(), 2, args, nullptr, false))) {\n        php_swoole_error(E_WARNING, \"%s->onWorkerExit handler error\", SW_Z_OBJCE_NAME_VAL_P(zobject));\n    }\n}\n\nstatic void process_pool_onStart(ProcessPool *pool) {\n    zval *zobject = static_cast<zval *>(pool->ptr);\n    ProcessPoolObject *pp = process_pool_fetch_object(zobject);\n    zval args[1];\n\n    zend_update_property_long(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"master_pid\"), getpid());\n    zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"running\"), true);\n\n    swoole_set_worker_type(SW_MASTER);\n    SwooleG.enable_coroutine = false;\n\n    if (pp->onStart == nullptr) {\n        return;\n    }\n\n    args[0] = *zobject;\n    if (UNEXPECTED(!zend::function::call(pp->onStart->ptr(), 1, args, nullptr, false))) {\n        php_swoole_error(E_WARNING, \"%s->onStart handler error\", SW_Z_OBJCE_NAME_VAL_P(zobject));\n    }\n}\n\nstatic void process_pool_onShutdown(ProcessPool *pool) {\n    zval *zobject = static_cast<zval *>(pool->ptr);\n    ProcessPoolObject *pp = process_pool_fetch_object(zobject);\n    zval args[1];\n\n    zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"running\"), false);\n    zend_update_property_bool(swoole_process_pool_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"workerRunning\"), false);\n\n    if (pp->onShutdown == nullptr) {\n        return;\n    }\n\n    args[0] = *zobject;\n\n    if (UNEXPECTED(!zend::function::call(pp->onShutdown->ptr(), 1, args, nullptr, false))) {\n        php_swoole_error(E_WARNING, \"%s->onShutdown handler error\", SW_Z_OBJCE_NAME_VAL_P(zobject));\n    }\n}\n\nstatic void process_pool_signal_handler(int signo) {\n    if (!current_pool) {\n        return;\n    }\n    switch (signo) {\n    case SIGTERM:\n        current_pool->running = false;\n        if (current_worker) {\n            current_pool->stop(current_worker);\n        }\n        break;\n    case SIGUSR1:\n    case SIGUSR2:\n        current_pool->reload();\n        break;\n    case SIGIO:\n        current_pool->trigger_read_message_event();\n        break;\n    case SIGWINCH:\n        current_pool->reopen_logger();\n        break;\n    default:\n#ifdef SIGRTMIN\n        if (signo == SIGRTMIN) {\n            current_pool->reopen_logger();\n        }\n#endif\n        break;\n    }\n}\n\nProcessPool *sw_process_pool() {\n    return current_pool;\n}\n\nstatic PHP_METHOD(swoole_process_pool, __construct) {\n    zval *zobject = ZEND_THIS;\n    zend_long worker_num;\n    zend_long ipc_type = SW_IPC_NONE;\n    zend_long msgq_key = 0;\n    zend_bool enable_coroutine = false;\n\n    // only cli env\n    if (!SWOOLE_G(cli)) {\n        swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT);\n        zend_throw_error(nullptr, \"%s can only be used in PHP CLI mode\", SW_Z_OBJCE_NAME_VAL_P(zobject));\n        RETURN_FALSE;\n    }\n\n    if (sw_server()) {\n        swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT);\n        zend_throw_error(nullptr, \"cannot create server and process pool instances simultaneously\");\n        RETURN_FALSE;\n    }\n\n    if (sw_process_pool()) {\n        swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT);\n        zend_throw_error(nullptr, \"A process pool instance has already been created and cannot be created again\");\n        RETURN_FALSE;\n    }\n\n#ifdef SW_THREAD\n    if (!tsrm_is_main_thread()) {\n        swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT);\n        zend_throw_exception_ex(swoole_exception_ce, -1, \"This operation is only allowed in the main thread\");\n        RETURN_FALSE;\n    }\n#endif\n\n    if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), \"l|llb\", &worker_num, &ipc_type, &msgq_key, &enable_coroutine) ==\n        FAILURE) {\n        RETURN_FALSE;\n    }\n\n    if (worker_num <= 0) {\n        zend_throw_exception_ex(swoole_exception_ce, errno, \"the parameter $worker_num must be greater than 0\");\n        RETURN_FALSE;\n    }\n\n    if (enable_coroutine && ipc_type > 0 && ipc_type != SW_IPC_UNIXSOCK) {\n        ipc_type = SW_IPC_UNIXSOCK;\n        zend_throw_error(nullptr, \"the parameter $ipc_type must be SWOOLE_IPC_UNIXSOCK when enable coroutine\");\n        RETURN_FALSE;\n    }\n\n    auto *pool = static_cast<ProcessPool *>(emalloc(sizeof(ProcessPool)));\n    *pool = {};\n    if (pool->create(worker_num, (key_t) msgq_key, (swIPCMode) ipc_type) < 0) {\n        zend_throw_exception_ex(swoole_exception_ce, errno, \"failed to create process pool\");\n        efree(pool);\n        RETURN_FALSE;\n    }\n\n    pool->ptr = sw_zval_dup(zobject);\n    pool->async = enable_coroutine;\n\n    auto pp = process_pool_fetch_object(ZEND_THIS);\n    pp->enable_coroutine = enable_coroutine;\n    pp->pool = pool;\n}\n\nstatic PHP_METHOD(swoole_process_pool, set) {\n    zval *zset = nullptr;\n    HashTable *vht = nullptr;\n    zval *ztmp;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ARRAY(zset)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    vht = Z_ARRVAL_P(zset);\n\n    ProcessPoolObject *pp = process_pool_fetch_object(ZEND_THIS);\n    ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS);\n\n    php_swoole_set_global_option(vht);\n    php_swoole_set_coroutine_option(vht);\n    php_swoole_set_aio_option(vht);\n\n    if (php_swoole_array_get_value(vht, \"enable_coroutine\", ztmp)) {\n        pool->async = pp->enable_coroutine = zval_is_true(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"enable_message_bus\", ztmp)) {\n        pp->enable_message_bus = zval_is_true(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"max_package_size\", ztmp)) {\n        pool->set_max_packet_size(php_swoole_parse_to_size(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"max_wait_time\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        pool->max_wait_time = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n    }\n}\n\nstatic PHP_METHOD(swoole_process_pool, on) {\n    char *name;\n    size_t l_name;\n    zval *zfn;\n\n    ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS);\n\n    if (pool->started) {\n        php_swoole_fatal_error(E_WARNING, \"process pool is started. unable to register event callback function\");\n        RETURN_FALSE;\n    }\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 2, 2)\n    Z_PARAM_STRING(name, l_name)\n    Z_PARAM_ZVAL(zfn);\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    ProcessPoolObject *pp = process_pool_fetch_object(ZEND_THIS);\n\n    if (SW_STRCASEEQ(name, l_name, \"WorkerStart\")) {\n        if (pp->onWorkerStart) {\n            sw_callable_free(pp->onWorkerStart);\n        }\n        pp->onWorkerStart = sw_callable_create(zfn);\n    } else if (SW_STRCASEEQ(name, l_name, \"Message\")) {\n        if (pool->ipc_mode == SW_IPC_NONE) {\n            zend_throw_exception(\n                swoole_exception_ce, \"cannot set `onMessage` event with ipc_type=0\", SW_ERROR_INVALID_PARAMS);\n            RETURN_FALSE;\n        }\n        if (pp->onMessage) {\n            sw_callable_free(pp->onMessage);\n        }\n        pp->onMessage = sw_callable_create(zfn);\n    } else if (SW_STRCASEEQ(name, l_name, \"WorkerStop\")) {\n        if (pp->onWorkerStop) {\n            sw_callable_free(pp->onWorkerStop);\n        }\n        pp->onWorkerStop = sw_callable_create(zfn);\n    } else if (SW_STRCASEEQ(name, l_name, \"WorkerExit\")) {\n        if (pp->onWorkerExit) {\n            sw_callable_free(pp->onWorkerExit);\n        }\n        pp->onWorkerExit = sw_callable_create(zfn);\n    } else if (SW_STRCASEEQ(name, l_name, \"Start\")) {\n        if (pp->onStart) {\n            sw_callable_free(pp->onStart);\n        }\n        pp->onStart = sw_callable_create(zfn);\n    } else if (SW_STRCASEEQ(name, l_name, \"Shutdown\")) {\n        if (pp->onShutdown) {\n            sw_callable_free(pp->onShutdown);\n        }\n        pp->onShutdown = sw_callable_create(zfn);\n    } else {\n        php_swoole_error(E_WARNING, \"unknown event type[%s]\", name);\n        RETURN_FALSE;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_process_pool, listen) {\n    char *host;\n    size_t l_host;\n    zend_long port = 0;\n    zend_long backlog = 2048;\n\n    auto pool = process_pool_get_and_check_pool(ZEND_THIS);\n    if (pool->started) {\n        php_swoole_fatal_error(E_WARNING, \"process pool is started. unable to listen\");\n        RETURN_FALSE;\n    }\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"s|ll\", &host, &l_host, &port, &backlog) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    if (pool->ipc_mode != SW_IPC_SOCKET) {\n        php_swoole_fatal_error(E_WARNING, \"unsupported ipc type[%d]\", pool->ipc_mode);\n        RETURN_FALSE;\n    }\n\n    int ret;\n    // unix socket\n    if (SW_STR_ISTARTS_WITH(host, l_host, \"unix:/\")) {\n        ret = pool->listen(host + 5, backlog);\n    } else {\n        ret = pool->listen(host, port, backlog);\n    }\n    pool->stream_info_->socket->set_fd_option(0, 1);\n\n    SW_CHECK_RETURN(ret);\n}\n\nstatic PHP_METHOD(swoole_process_pool, write) {\n    char *data;\n    size_t length;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STRING(data, length)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS);\n    if (pool->ipc_mode != SW_IPC_SOCKET) {\n        php_swoole_fatal_error(E_WARNING, \"unsupported ipc type[%d]\", pool->ipc_mode);\n        RETURN_FALSE;\n    }\n    if (length == 0) {\n        RETURN_FALSE;\n    }\n    SW_CHECK_RETURN(pool->response(data, length));\n}\n\nstatic PHP_METHOD(swoole_process_pool, sendMessage) {\n    ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS);\n    if (!pool->started) {\n        php_swoole_fatal_error(E_WARNING, \"process pool is not started.\");\n        RETURN_FALSE;\n    }\n    if (pool->ipc_mode != SW_IPC_UNIXSOCK) {\n        php_swoole_fatal_error(E_WARNING, \"unsupported ipc type[%d]\", pool->ipc_mode);\n        RETURN_FALSE;\n    }\n\n    char *message;\n    size_t l_message;\n    zend_long worker_id;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_STRING(message, l_message)\n    Z_PARAM_LONG(worker_id)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(pool->send_message(worker_id, message, l_message));\n}\n\nstatic PHP_METHOD(swoole_process_pool, start) {\n    ProcessPool *pool = process_pool_get_and_check_pool(ZEND_THIS);\n    if (pool->started) {\n        php_swoole_fatal_error(E_WARNING, \"process pool is started\");\n        RETURN_FALSE;\n    }\n\n    ProcessPoolObject *pp = process_pool_fetch_object(ZEND_THIS);\n    std::unordered_map<int, swSignalHandler> ori_handlers;\n\n    // The reactor must be cleaned up before registering signal\n    swoole_event_free();\n    ori_handlers[SIGTERM] = swoole_signal_set(SIGTERM, process_pool_signal_handler);\n    ori_handlers[SIGUSR1] = swoole_signal_set(SIGUSR1, process_pool_signal_handler);\n    ori_handlers[SIGUSR2] = swoole_signal_set(SIGUSR2, process_pool_signal_handler);\n    ori_handlers[SIGIO] = swoole_signal_set(SIGIO, process_pool_signal_handler);\n    ori_handlers[SIGWINCH] = swoole_signal_set(SIGWINCH, process_pool_signal_handler);\n#ifdef SIGRTMIN\n    ori_handlers[SIGRTMIN] = swoole_signal_set(SIGRTMIN, process_pool_signal_handler);\n#endif\n\n    if (pp->enable_message_bus) {\n        if (pool->create_message_bus() != SW_OK) {\n            RETURN_FALSE;\n        }\n        pool->message_bus->set_allocator(sw_zend_string_allocator());\n        pool->set_protocol(SW_PROTOCOL_MESSAGE);\n    } else {\n        pool->set_protocol(SW_PROTOCOL_STREAM);\n    }\n\n    if (pp->onWorkerStart == nullptr && pp->onMessage == nullptr) {\n        if (pool->async) {\n            php_swoole_fatal_error(E_ERROR, \"require 'onWorkerStart' callback\");\n            RETURN_FALSE;\n        } else if (pool->ipc_mode != SW_IPC_NONE && pp->onMessage == nullptr) {\n            php_swoole_fatal_error(E_ERROR, \"require 'onMessage' callback\");\n            RETURN_FALSE;\n        }\n    }\n\n    if (pp->onWorkerExit && !pp->enable_coroutine) {\n        zend_throw_exception(\n            swoole_exception_ce, \"cannot set `onWorkerExit` without enable_coroutine\", SW_ERROR_INVALID_PARAMS);\n        RETURN_FALSE;\n    }\n\n    if (pp->onMessage) {\n        pool->onMessage = process_pool_onMessage;\n    } else {\n        pool->main_loop = nullptr;\n    }\n\n    current_pool = pool;\n\n    pool->onStart = process_pool_onStart;\n    pool->onShutdown = process_pool_onShutdown;\n    pool->onWorkerStart = process_pool_onWorkerStart;\n    pool->onWorkerStop = process_pool_onWorkerStop;\n\n    if (pp->enable_coroutine && pp->onWorkerExit) {\n        pool->onWorkerExit = process_pool_onWorkerExit;\n    }\n\n    if (pool->start() < 0) {\n        RETURN_FALSE;\n    }\n\n    pool->wait();\n\n    current_pool = nullptr;\n\n    for (auto &ori_handler : ori_handlers) {\n        swoole_signal_set(ori_handler.first, ori_handler.second);\n    }\n}\n\nstatic PHP_METHOD(swoole_process_pool, detach) {\n    if (current_pool == nullptr) {\n        RETURN_FALSE;\n    }\n    RETURN_BOOL(current_pool->detach());\n}\n\nstatic PHP_METHOD(swoole_process_pool, getProcess) {\n    long worker_id = -1;\n\n    if (current_pool == nullptr) {\n        RETURN_FALSE;\n    }\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"|l\", &worker_id) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    if (worker_id >= current_pool->worker_num) {\n        php_swoole_error(E_WARNING, \"invalid worker_id[%ld]\", worker_id);\n        RETURN_FALSE;\n    } else if (worker_id < 0) {\n        worker_id = swoole_get_worker_id();\n    }\n\n    zval *zworkers =\n        sw_zend_read_and_convert_property_array(swoole_process_pool_ce, ZEND_THIS, ZEND_STRL(\"workers\"), 0);\n    zval *zprocess = zend_hash_index_find(Z_ARRVAL_P(zworkers), worker_id);\n    zval zobject;\n\n    if (zprocess == nullptr || ZVAL_IS_NULL(zprocess)) {\n        zprocess = &zobject;\n        /**\n         * Separation from shared memory\n         */\n        auto *worker = static_cast<Worker *>(emalloc(sizeof(Worker)));\n        *worker = current_pool->workers[worker_id];\n\n        object_init_ex(zprocess, swoole_process_ce);\n        zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL(\"id\"), swoole_get_worker_id());\n        zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL(\"pid\"), worker->pid);\n        if (current_pool->ipc_mode == SW_IPC_UNIXSOCK) {\n            // current process\n            if (worker->id == swoole_get_worker_id()) {\n                worker->pipe_current = worker->pipe_worker;\n            } else {\n                worker->pipe_current = worker->pipe_master;\n            }\n            /**\n             * Forbidden to close pipe in the php layer\n             */\n            worker->pipe_object = nullptr;\n            zend_update_property_long(\n                swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL(\"pipe\"), worker->pipe_current->fd);\n        }\n        /**\n         * The message bus is enabled and forbid to read/write/close the pipeline in the php layer\n         */\n        if (current_pool->message_bus) {\n            worker->pipe_current = nullptr;\n            worker->pipe_object = nullptr;\n        }\n        /**\n         * The onMessage callback is not set, use getProcess()->push()/pop() to operate msgqueue\n         */\n        if (current_pool->ipc_mode == SW_IPC_MSGQUEUE && current_pool->onMessage == nullptr) {\n            worker->queue = current_pool->queue;\n            worker->msgqueue_mode = SW_MSGQUEUE_BALANCE;\n        }\n        php_swoole_process_set_worker(zprocess, worker, PIPE_TYPE_STREAM, current_pool->async);\n        (void) add_index_zval(zworkers, worker_id, zprocess);\n    } else {\n        auto _worker = php_swoole_process_get_worker(zprocess);\n        if (_worker->pid != current_pool->workers[worker_id].pid) {\n            _worker->pid = current_pool->workers[worker_id].pid;\n            zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL(\"pid\"), _worker->pid);\n        }\n    }\n\n    RETURN_ZVAL(zprocess, 1, 0);\n}\n\nstatic PHP_METHOD(swoole_process_pool, stop) {\n    if (current_pool) {\n        current_pool->running = false;\n        if (current_worker) {\n            current_pool->stop(current_worker);\n        }\n    }\n}\n\nstatic PHP_METHOD(swoole_process_pool, shutdown) {\n    if (current_pool) {\n        RETURN_BOOL(current_pool->shutdown());\n    } else {\n        zend_throw_exception(swoole_exception_ce, \"The process pool is not started\", SW_ERROR_INVALID_PARAMS);\n        RETURN_FALSE;\n    }\n}\n\nstatic PHP_METHOD(swoole_process_pool, __destruct) {}\n"
  },
  {
    "path": "ext-src/swoole_redis_server.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_server.h\"\n#include \"swoole_redis.h\"\n\n#include <unordered_map>\n#include <string>\n\nBEGIN_EXTERN_C()\n#include \"ext/standard/php_string.h\"\n#include \"stubs/php_swoole_redis_server_arginfo.h\"\nEND_EXTERN_C()\n\nusing swoole::Connection;\nusing swoole::ListenPort;\nusing swoole::RecvData;\nusing swoole::Server;\nusing swoole::String;\n\nnamespace Redis = swoole::redis;\n\nzend_class_entry *swoole_redis_server_ce;\nzend_object_handlers swoole_redis_server_handlers;\n\nstatic SW_THREAD_LOCAL std::unordered_map<std::string, zend::Callable *> redis_handlers;\n\nstatic bool redis_response_format(String *buf, zend_long type, zval *value);\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_redis_server, setHandler);\nstatic PHP_METHOD(swoole_redis_server, getHandler);\nstatic PHP_METHOD(swoole_redis_server, format);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic constexpr zend_function_entry swoole_redis_server_methods[] =\n{\n    PHP_ME(swoole_redis_server, setHandler, arginfo_class_Swoole_Redis_Server_setHandler, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_redis_server, getHandler, arginfo_class_Swoole_Redis_Server_getHandler, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_redis_server, format,     arginfo_class_Swoole_Redis_Server_format,     ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_redis_server_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY_EX(\n        swoole_redis_server, R\"(Swoole\\Redis\\Server)\", nullptr, swoole_redis_server_methods, swoole_server);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_redis_server);\n    SW_SET_CLASS_CLONEABLE(swoole_redis_server, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_redis_server, sw_zend_class_unset_property_deny);\n\n    zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL(\"NIL\"), Redis::REPLY_NIL);\n    zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL(\"ERROR\"), Redis::REPLY_ERROR);\n    zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL(\"STATUS\"), Redis::REPLY_STATUS);\n    zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL(\"INT\"), Redis::REPLY_INT);\n    zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL(\"STRING\"), Redis::REPLY_STRING);\n    zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL(\"SET\"), Redis::REPLY_SET);\n    zend_declare_class_constant_long(swoole_redis_server_ce, ZEND_STRL(\"MAP\"), Redis::REPLY_MAP);\n}\n\nvoid php_swoole_redis_server_rshutdown() {\n    for (const auto &redis_handler : redis_handlers) {\n        sw_callable_free(redis_handler.second);\n    }\n    redis_handlers.clear();\n}\n\nint php_swoole_redis_server_onReceive(Server *serv, RecvData *req) {\n    auto fd = req->info.fd;\n    Connection *conn = serv->get_connection_by_session_id(fd);\n    if (!conn) {\n        swoole_warning(\"connection[%ld] is closed\", fd);\n        return SW_ERR;\n    }\n\n    ListenPort *port = serv->get_port_by_fd(conn->fd);\n    // other server port\n    if (!port->open_redis_protocol) {\n        return php_swoole_server_onReceive(serv, req);\n    }\n\n    zval zdata;\n    php_swoole_get_recv_data(serv, &zdata, req);\n    const char *p = Z_STRVAL(zdata);\n    const char *pe = p + Z_STRLEN(zdata);\n    int ret;\n    int length = 0;\n\n    zval zparams{};\n    array_init(&zparams);\n\n    int state = Redis::STATE_RECEIVE_TOTAL_LINE;\n    int add_param = 0;\n    const char *command = nullptr;\n    int command_len = 0;\n\n    do {\n        switch (state) {\n        case Redis::STATE_RECEIVE_TOTAL_LINE:\n            if (*p == '*' && (p = Redis::get_number(p, &ret))) {\n                state = Redis::STATE_RECEIVE_LENGTH;\n                break;\n            }\n            /* no break */\n\n        case Redis::STATE_RECEIVE_LENGTH:\n            if (*p == '$' && (p = Redis::get_number(p, &ret))) {\n                if (ret == -1) {\n                    add_next_index_null(&zparams);\n                    break;\n                }\n                length = ret;\n                state = Redis::STATE_RECEIVE_STRING;\n                break;\n            }\n            // integer\n            else if (*p == ':' && (p = Redis::get_number(p, &ret))) {\n                add_next_index_long(&zparams, ret);\n                break;\n            }\n            /* no break */\n\n        case Redis::STATE_RECEIVE_STRING:\n            if (add_param == 0) {\n                command = p;\n                command_len = length;\n                add_param = 1;\n            } else {\n                add_next_index_stringl(&zparams, p, length);\n            }\n            p += length + SW_CRLF_LEN;\n            state = Redis::STATE_RECEIVE_LENGTH;\n            break;\n\n        default:\n            break;\n        }\n    } while (p < pe);\n\n    if (command_len >= SW_REDIS_MAX_COMMAND_SIZE) {\n        php_swoole_error(E_WARNING, \"command [%.8s...](length=%d) is too long\", command, command_len);\n        serv->close(fd, false);\n        return SW_OK;\n    }\n\n    char _command[SW_REDIS_MAX_COMMAND_SIZE];\n    size_t _command_len = sw_snprintf(_command, sizeof(_command), \"_handler_%.*s\", command_len, command);\n#if PHP_VERSION_ID >= 80400\n    zend_str_tolower(_command, _command_len);\n#else\n    php_strtolower(_command, _command_len);\n#endif\n\n    auto i = redis_handlers.find(std::string(_command, _command_len));\n    if (i == redis_handlers.end()) {\n        char err_msg[256];\n        length = sw_snprintf(err_msg, sizeof(err_msg), \"-ERR unknown command '%.*s'\\r\\n\", command_len, command);\n        return serv->send(fd, err_msg, length) ? SW_OK : SW_ERR;\n    }\n\n    auto fci_cache = i->second;\n    zval args[2];\n    zval retval;\n\n    ZVAL_LONG(&args[0], fd);\n    args[1] = zparams;\n\n    if (UNEXPECTED(!zend::function::call(fci_cache->ptr(), 2, args, &retval, serv->is_enable_coroutine()))) {\n        php_swoole_error(E_WARNING,\n                         \"%s->onRequest with command '%.*s' handler error\",\n                         ZSTR_VAL(swoole_redis_server_ce->name),\n                         command_len,\n                         command);\n    }\n\n    if (Z_TYPE_P(&retval) == IS_STRING) {\n        serv->send(fd, Z_STRVAL_P(&retval), Z_STRLEN_P(&retval));\n    }\n    zval_ptr_dtor(&retval);\n    zval_ptr_dtor(&zdata);\n    zval_ptr_dtor(&zparams);\n\n    return SW_OK;\n}\n\nstatic PHP_METHOD(swoole_redis_server, setHandler) {\n    char *command;\n    size_t command_len;\n    zval *zcallback;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_STRING(command, command_len)\n    Z_PARAM_ZVAL(zcallback)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (command_len == 0 || command_len >= SW_REDIS_MAX_COMMAND_SIZE) {\n        php_swoole_fatal_error(E_ERROR, \"invalid command\");\n        RETURN_FALSE;\n    }\n\n    auto fci_cache = sw_callable_create(zcallback);\n    if (!fci_cache) {\n        return;\n    }\n\n    char _command[SW_REDIS_MAX_COMMAND_SIZE];\n    size_t _command_len = sw_snprintf(_command, sizeof(_command), \"_handler_%s\", command);\n#if PHP_VERSION_ID >= 80400\n    zend_str_tolower(_command, _command_len);\n#else\n    php_strtolower(_command, _command_len);\n#endif\n\n    zend_update_property(swoole_redis_server_ce, SW_Z8_OBJ_P(ZEND_THIS), _command, _command_len, zcallback);\n\n    std::string key(_command, _command_len);\n    auto i = redis_handlers.find(key);\n    if (i != redis_handlers.end()) {\n        sw_callable_free(i->second);\n    }\n\n    redis_handlers[key] = fci_cache;\n\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_redis_server, getHandler) {\n    char *command;\n    size_t command_len;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STRING(command, command_len)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    char _command[SW_REDIS_MAX_COMMAND_SIZE];\n    size_t _command_len = sw_snprintf(_command, sizeof(_command), \"_handler_%s\", command);\n#if PHP_VERSION_ID >= 80400\n    zend_str_tolower(_command, _command_len);\n#else\n    php_strtolower(_command, _command_len);\n#endif\n\n    zval rv;\n    zval *handler = zend_read_property(swoole_redis_server_ce, SW_Z8_OBJ_P(ZEND_THIS), _command, _command_len, 1, &rv);\n    RETURN_ZVAL(handler, 1, 0);\n}\n\nstatic void redis_response_format_array_item(String *buf, zval *item) {\n    switch (Z_TYPE_P(item)) {\n    case IS_LONG:\n    case IS_FALSE:\n    case IS_TRUE:\n        redis_response_format(buf, Redis::REPLY_INT, item);\n        break;\n    case IS_ARRAY:\n        if (zend_array_is_list(Z_ARRVAL_P(item))) {\n            redis_response_format(buf, Redis::REPLY_SET, item);\n        } else {\n            redis_response_format(buf, Redis::REPLY_MAP, item);\n        }\n        break;\n    case IS_NULL:\n        redis_response_format(buf, Redis::REPLY_NIL, item);\n        break;\n    default:\n        redis_response_format(buf, Redis::REPLY_STRING, item);\n        break;\n    }\n}\n\nstatic bool redis_response_format(String *buf, zend_long type, zval *value) {\n    if (type == Redis::REPLY_NIL) {\n        buf->append(SW_STRL(SW_REDIS_RETURN_NIL));\n    } else if (type == Redis::REPLY_ERROR || type == Redis::REPLY_STATUS) {\n        char flag = type == Redis::REPLY_ERROR ? '-' : '+';\n        const char *default_message = type == Redis::REPLY_ERROR ? \"ERR\" : \"OK\";\n        if (value) {\n            zend::String str_value(value);\n            buf->append_format(\"%c%.*s\\r\\n\", flag, (int) str_value.len(), str_value.val());\n        } else {\n            buf->append_format(\"%c%s\\r\\n\", flag, default_message);\n        }\n    } else if (type == Redis::REPLY_INT) {\n        if (!value) {\n            goto _no_value;\n        }\n        buf->append_format(\":\" ZEND_LONG_FMT \"\\r\\n\", zval_get_long(value));\n    } else if (type == Redis::REPLY_STRING) {\n        if (!value) {\n        _no_value:\n            zend_throw_exception(swoole_exception_ce, \"require more parameters\", SW_ERROR_INVALID_PARAMS);\n            return false;\n        }\n        zend::String str_value(value);\n        if (sw_unlikely(str_value.len() > SW_REDIS_MAX_STRING_SIZE)) {\n            zend_throw_exception(swoole_exception_ce,\n                                 \"the length of given string exceeds the maximum allowed value\",\n                                 SW_ERROR_INVALID_PARAMS);\n            return false;\n        } else if (sw_unlikely(str_value.len() == 0)) {\n            buf->append(\"$0\\r\\n\\r\\n\");\n        } else {\n            buf->append_format(\"$%zu\\r\\n\", str_value.len());\n            buf->append(str_value.val(), str_value.len());\n            buf->append(SW_CRLF, SW_CRLF_LEN);\n        }\n    } else if (type == Redis::REPLY_SET) {\n        if (!value) {\n            goto _no_value;\n        }\n        if (!ZVAL_IS_ARRAY(value)) {\n            zend_throw_exception(\n                swoole_exception_ce, \"the second parameter should be an array\", SW_ERROR_INVALID_PARAMS);\n        }\n        buf->append_format(\"*%d\\r\\n\", zend_hash_num_elements(Z_ARRVAL_P(value)));\n\n        zval *item;\n        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(value), item) {\n            redis_response_format_array_item(buf, item);\n        }\n        ZEND_HASH_FOREACH_END();\n    } else if (type == Redis::REPLY_MAP) {\n        if (!value) {\n            goto _no_value;\n        }\n        if (!ZVAL_IS_ARRAY(value)) {\n            zend_throw_exception(\n                swoole_exception_ce, \"the second parameter should be an array\", SW_ERROR_INVALID_PARAMS);\n        }\n        buf->append_format(\"*%d\\r\\n\", 2 * zend_hash_num_elements(Z_ARRVAL_P(value)));\n\n        zend_string *key;\n        zend_ulong num_key;\n        zval *item;\n        ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(value), num_key, key, item) {\n            if (key) {\n                buf->append_format(\"$%zu\\r\\n%.*s\\r\\n\", ZSTR_LEN(key), (int) ZSTR_LEN(key), ZSTR_VAL(key));\n            } else {\n                std::string _key = std::to_string(num_key);\n                buf->append_format(\"$%zu\\r\\n%.*s\\r\\n\", _key.length(), (int) _key.length(), _key.c_str());\n            }\n            redis_response_format_array_item(buf, item);\n        }\n        ZEND_HASH_FOREACH_END();\n    } else {\n        zend_throw_exception_ex(swoole_exception_ce, SW_ERROR_INVALID_PARAMS, \"Unknown type[%d]\", (int) type);\n        return false;\n    }\n\n    return true;\n}\n\nstatic PHP_METHOD(swoole_redis_server, format) {\n    zend_long type;\n    zval *value = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_LONG(type)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_ZVAL(value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    auto buf = std::shared_ptr<String>(swoole::make_string(1024, sw_zend_string_allocator()));\n    if (!redis_response_format(buf.get(), type, value)) {\n        RETURN_FALSE;\n    }\n\n    auto str = zend::fetch_zend_string_by_val(buf->str);\n    buf->set_null_terminated();\n    str->len = buf->length;\n    buf->release();\n    RETURN_STR(str);\n}\n"
  },
  {
    "path": "ext-src/swoole_runtime.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author:   Tianfeng Han  <mikan.tenny@gmail.com>                      |\n  +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_cxx.h\"\n#include \"php_swoole_api.h\"\n#include \"php_swoole_coroutine.h\"\n\n#include \"swoole_util.h\"\n\n#include \"thirdparty/php/standard/proc_open.h\"\n\n#ifdef SW_USE_CURL\n#include \"swoole_curl_interface.h\"\n#endif\n\n#include <unordered_map>\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_runtime_arginfo.h\"\n\n#ifdef SW_USE_PGSQL\nextern void swoole_pgsql_set_blocking(bool blocking);\n#endif\n\n#ifdef SW_USE_ODBC\nextern void swoole_odbc_set_blocking(bool blocking);\n#endif\n\n#ifdef SW_USE_ORACLE\nextern void swoole_oracle_set_blocking(bool blocking);\n#endif\n\n#ifdef SW_USE_SQLITE\nextern void swoole_sqlite_set_blocking(bool blocking);\n#endif\n\n#ifdef SW_USE_FIREBIRD\nextern void swoole_firebird_set_blocking(bool blocking);\n#endif\nEND_EXTERN_C()\n\n/* openssl */\n#ifndef OPENSSL_NO_ECDH\n#define HAVE_ECDH 1\n#endif\n#ifndef OPENSSL_NO_TLSEXT\n#define HAVE_TLS_SNI 1\n#if OPENSSL_VERSION_NUMBER >= 0x10002000L\n#define HAVE_TLS_ALPN 1\n#endif\n#endif\n#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)\n#define HAVE_SEC_LEVEL 1\n#endif\n\nusing swoole::Coroutine;\nusing swoole::PHPCoroutine;\nusing swoole::coroutine::PollSocket;\nusing swoole::coroutine::System;\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_runtime, enableCoroutine);\nstatic PHP_METHOD(swoole_runtime, getHookFlags);\nstatic PHP_METHOD(swoole_runtime, setHookFlags);\nstatic PHP_FUNCTION(swoole_sleep);\nstatic PHP_FUNCTION(swoole_usleep);\nstatic PHP_FUNCTION(swoole_time_nanosleep);\nstatic PHP_FUNCTION(swoole_time_sleep_until);\nstatic PHP_FUNCTION(swoole_stream_select);\nstatic PHP_FUNCTION(swoole_stream_socket_pair);\nstatic PHP_FUNCTION(swoole_user_func_handler);\n#if defined(HAVE_PUTENV) && defined(SW_THREAD)\nstatic PHP_FUNCTION(swoole_putenv);\n#endif\n#if PHP_VERSION_ID >= 80400\nextern PHP_FUNCTION(swoole_exit);\n#endif\nSW_EXTERN_C_END\n\nstatic void inherit_class(const char *child_name, size_t child_length, const char *parent_name, size_t parent_length);\nstatic void detach_parent_class(const char *child_name);\nstatic void clear_class_entries();\nstatic int socket_set_option(php_stream *stream, int option, int value, void *ptrparam);\nstatic php_stream_size_t socket_read(php_stream *stream, char *buf, size_t count);\nstatic php_stream_size_t socket_write(php_stream *stream, const char *buf, size_t count);\nstatic int socket_flush(php_stream *stream);\nstatic int socket_close(php_stream *stream, int close_handle);\nstatic int socket_stat(php_stream *stream, php_stream_statbuf *ssb);\nstatic int socket_cast(php_stream *stream, int castas, void **ret);\nstatic bool socket_ssl_set_options(SocketImpl *sock, php_stream_context *context);\n\nstatic php_stream *socket_create(const char *proto,\n                                 size_t protolen,\n                                 const char *resourcename,\n                                 size_t resourcenamelen,\n                                 const char *persistent_id,\n                                 int options,\n                                 int flags,\n                                 struct timeval *timeout,\n                                 php_stream_context *context STREAMS_DC);\n\n// clang-format off\nstatic zend_class_entry *swoole_runtime_ce;\n\nstatic php_stream_ops socket_ops {\n    socket_write,\n    socket_read,\n    socket_close,\n    socket_flush,\n    \"socket/coroutine\",\n    nullptr, /* seek */\n    socket_cast,\n    socket_stat,\n    socket_set_option,\n};\n\nstruct NetStream {\n    php_netstream_data_t stream;\n    std::shared_ptr<SocketImpl> socket;\n    bool blocking;\n};\n\nstatic struct {\n    php_stream_transport_factory tcp;\n    php_stream_transport_factory udp;\n    php_stream_transport_factory _unix;\n    php_stream_transport_factory udg;\n    php_stream_transport_factory ssl;\n    php_stream_transport_factory tls;\n} ori_factory = {\n    nullptr,\n    nullptr,\n    nullptr,\n    nullptr,\n    nullptr,\n    nullptr,\n};\n\nstatic std::vector<std::string> unsafe_functions {\n    \"pcntl_fork\",\n    \"pcntl_rfork\",\n    \"pcntl_wait\",\n    \"pcntl_waitpid\",\n    \"pcntl_sigtimedwait\",\n    \"pcntl_sigwaitinfo\",\n};\n\n#if defined(HAVE_PUTENV) && defined(SW_THREAD)\nstatic std::unordered_map<std::string, std::string> swoole_runtime_environ;\n#endif\n\nstatic constexpr zend_function_entry swoole_runtime_methods[] = {\n    PHP_ME(swoole_runtime, enableCoroutine, arginfo_class_Swoole_Runtime_enableCoroutine, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_runtime, getHookFlags, arginfo_class_Swoole_Runtime_getHookFlags, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_runtime, setHookFlags, arginfo_class_Swoole_Runtime_setHookFlags, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_FE_END\n};\n// clang-format on\n\nstatic php_stream_wrapper ori_php_plain_files_wrapper;\nstatic php_stream_ops ori_php_stream_stdio_ops;\n\nstatic void hook_func(const char *name,\n                      size_t l_name,\n                      zif_handler handler = nullptr,\n                      zend_internal_arg_info *arg_info = nullptr);\nstatic void unhook_func(const char *name, size_t l_name);\n\nstatic bool extension_loaded(const char *name) {\n    zend_string *extension_name = zend_string_init(name, strlen(name), false);\n    zend_string *lcname = zend_string_tolower(extension_name);\n    bool rv = zend_hash_exists(&module_registry, lcname);\n    zend_string_release(lcname);\n    zend_string_release(extension_name);\n    return rv;\n}\n\nstatic bool class_exists(const char *name) {\n    zend_string *class_name = zend_string_init(name, strlen(name), false);\n    zend_string *lcname = zend_string_tolower(class_name);\n    auto *ce = (zend_class_entry *) zend_hash_find_ptr(EG(class_table), lcname);\n    zend_string_release_ex(lcname, 0);\n    zend_string_release_ex(class_name, 0);\n    return !!ce;\n}\n\nstatic zend_internal_arg_info *get_arginfo(const char *name, size_t l_name) {\n    auto *zf = zend::get_function(name, l_name);\n    if (zf == nullptr) {\n        return nullptr;\n    }\n    return zf->internal_function.arg_info;\n}\n\nstatic zend_internal_arg_info *copy_arginfo(const zend_internal_function *function, zend_internal_arg_info *_arg_info) {\n    uint32_t num_args = function->num_args + 1;\n    zend_internal_arg_info *arg_info = _arg_info - 1;\n\n    auto new_arg_info = static_cast<zend_internal_arg_info *>(pemalloc(sizeof(zend_internal_arg_info) * num_args, 1));\n    memcpy(new_arg_info, arg_info, sizeof(zend_internal_arg_info) * num_args);\n\n    if (function->fn_flags & ZEND_ACC_VARIADIC) {\n        num_args++;\n    }\n\n    for (uint32_t i = 0; i < num_args; i++) {\n        if (ZEND_TYPE_HAS_LIST(arg_info[i].type)) {\n            auto old_list = ZEND_TYPE_LIST(arg_info[i].type);\n            auto *new_list = static_cast<zend_type_list *>(pemalloc(ZEND_TYPE_LIST_SIZE(old_list->num_types), 1));\n            memcpy(new_list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types));\n            ZEND_TYPE_SET_PTR(new_arg_info[i].type, new_list);\n\n#if PHP_VERSION_ID >= 80500\n            // For PHP 8.5+, ZEND_TYPE_LIST_FOREACH gives a const pointer, which we can't modify.\n            // We must use a manual loop over the list we allocated ourselves.\n            for (uint32_t j = 0; j < new_list->num_types; j++) {\n                zend_type *list_type = &new_list->types[j];\n                if (ZEND_TYPE_HAS_NAME(*list_type)) {\n                    zend_string *name = zend_string_dup(ZEND_TYPE_NAME(*list_type), 1);\n                    ZEND_TYPE_SET_PTR(*list_type, name);\n                }\n            }\n#else\n            zend_type *list_type;\n            ZEND_TYPE_LIST_FOREACH(new_list, list_type) {\n                zend_string *name = zend_string_dup(ZEND_TYPE_NAME(*list_type), true);\n                ZEND_TYPE_SET_PTR(*list_type, name);\n            }\n            ZEND_TYPE_LIST_FOREACH_END();\n#endif\n        } else if (ZEND_TYPE_HAS_NAME(arg_info[i].type)) {\n            zend_string *name = zend_string_dup(ZEND_TYPE_NAME(arg_info[i].type), true);\n            ZEND_TYPE_SET_PTR(new_arg_info[i].type, name);\n        }\n    }\n\n    return new_arg_info + 1;\n}\n\nstatic void free_arg_info(const zend_internal_function *function, zend_internal_arg_info *arg_info_copy) {\n    zend_internal_arg_info *arg_info = arg_info_copy - 1;\n    uint32_t num_args = function->num_args + 1;\n    if (function->fn_flags & ZEND_ACC_VARIADIC) {\n        num_args++;\n    }\n    for (uint32_t i = 0; i < num_args; i++) {\n        zend_type_release(arg_info[i].type, true);\n    }\n    pefree(arg_info, 1);\n}\n\n#define SW_HOOK_FUNC(f) hook_func(ZEND_STRL(#f), PHP_FN(swoole_##f))\n#define SW_UNHOOK_FUNC(f) unhook_func(ZEND_STRL(#f))\n#define SW_HOOK_WITH_NATIVE_FUNC(f)                                                                                    \\\n    hook_func(ZEND_STRL(#f), PHP_FN(swoole_native_##f), get_arginfo(ZEND_STRL(\"swoole_native_\" #f)))\n#define SW_HOOK_WITH_PHP_FUNC(f) hook_func(ZEND_STRL(#f))\n\n#define SW_HOOK_LIBRARY_FE(name, arg_info)                                                                             \\\n    ZEND_RAW_FENTRY(\"swoole_hook_\" #name, PHP_FN(swoole_user_func_handler), arg_info, 0)\n\nstatic int runtime_hook_flags = 0;\nstatic zend_array *hook_function_table = nullptr;\nstatic std::unordered_map<std::string, zend_class_entry *> child_class_entries;\nstatic zend::ConcurrencyHashMap<std::string, zif_handler> ori_func_handlers(nullptr);\nstatic zend::ConcurrencyHashMap<std::string, zend_internal_arg_info *> ori_func_arg_infos(nullptr);\n\nSW_EXTERN_C_BEGIN\n#include \"ext/standard/file.h\"\n#include \"thirdparty/php/streams/plain_wrapper.c\"\n#undef close\nSW_EXTERN_C_END\n\nvoid php_swoole_runtime_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY_BASE(swoole_runtime, \"Swoole\\\\Runtime\", nullptr, swoole_runtime_methods, nullptr);\n    SW_SET_CLASS_CREATE(swoole_runtime, sw_zend_create_object_deny);\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_TCP\", PHPCoroutine::HOOK_TCP);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_UDP\", PHPCoroutine::HOOK_UDP);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_UNIX\", PHPCoroutine::HOOK_UNIX);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_UDG\", PHPCoroutine::HOOK_UDG);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_SSL\", PHPCoroutine::HOOK_SSL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_TLS\", PHPCoroutine::HOOK_TLS);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_STREAM_FUNCTION\", PHPCoroutine::HOOK_STREAM_FUNCTION);\n    // backward compatibility\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_STREAM_SELECT\", PHPCoroutine::HOOK_STREAM_FUNCTION);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_FILE\", PHPCoroutine::HOOK_FILE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_STDIO\", PHPCoroutine::HOOK_STDIO);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_SLEEP\", PHPCoroutine::HOOK_SLEEP);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_PROC\", PHPCoroutine::HOOK_PROC);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_CURL\", PHPCoroutine::HOOK_CURL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_NATIVE_CURL\", PHPCoroutine::HOOK_NATIVE_CURL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_SOCKETS\", PHPCoroutine::HOOK_SOCKETS);\n#ifdef SW_USE_PGSQL\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_PDO_PGSQL\", PHPCoroutine::HOOK_PDO_PGSQL);\n#endif\n#ifdef SW_USE_ODBC\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_PDO_ODBC\", PHPCoroutine::HOOK_PDO_ODBC);\n#endif\n#ifdef SW_USE_ORACLE\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_PDO_ORACLE\", PHPCoroutine::HOOK_PDO_ORACLE);\n#endif\n#ifdef SW_USE_SQLITE\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_PDO_SQLITE\", PHPCoroutine::HOOK_PDO_SQLITE);\n#endif\n#ifdef SW_USE_FIREBIRD\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_PDO_FIREBIRD\", PHPCoroutine::HOOK_PDO_FIREBIRD);\n#endif\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_NET_FUNCTION\", PHPCoroutine::HOOK_NET_FUNCTION);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_MONGODB\", PHPCoroutine::HOOK_MONGODB);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_HOOK_ALL\", PHPCoroutine::HOOK_ALL);\n#ifdef SW_USE_CURL\n    swoole_native_curl_minit(module_number);\n#endif\n    swoole_proc_open_init(module_number);\n\n    php_stream_xport_register(\"async.tcp\", socket_create);\n    php_stream_xport_register(\"async.udp\", socket_create);\n    php_stream_xport_register(\"async.unix\", socket_create);\n    php_stream_xport_register(\"async.udg\", socket_create);\n    php_stream_xport_register(\"async.ssl\", socket_create);\n    php_stream_xport_register(\"async.tls\", socket_create);\n\n    php_register_url_stream_wrapper(SW_ASYNC_FILE_PROTOCOL, &sw_php_plain_files_wrapper);\n}\n\nstruct PhpFunc {\n    zend_function *function;\n    zend_internal_arg_info *ori_arg_info;\n    zif_handler ori_handler;\n    zend_internal_arg_info *arg_info_copy;\n    uint32_t ori_fn_flags;\n    zend::Callable *fci_cache;\n    zval name;\n};\n\nvoid php_swoole_runtime_rinit() {\n    if (!sw_is_main_thread()) {\n#if PHP_VERSION_ID < 80300\n        // After creating a thread, the main thread will not modify the runtime hook,\n        // so this `hook_function_table` is read-only and safe for multi-threaded reading\n        zend_string *key = NULL;\n        void *ptr;\n        ZEND_HASH_REVERSE_FOREACH_STR_KEY_PTR(hook_function_table, key, ptr) {\n            PhpFunc *rf = (PhpFunc *) ptr;\n            // The PHP 8.3 and later removed `function_copy_ctor`. The `zend_internal_function` pointer in\n            // `EG(function_table)` and `CG(function_table)` are in shared memory across all threads, so updating the\n            // handler and arginfo once in the main thread takes effect for every thread. In PHP 8.2 and earlier, each\n            // thread called `function_copy_ctor` to copy the `zend_internal_function` memory. As a result,\n            // `EG(function_table)` and `CG(function_table)` were thread-local, and must set the `handler` and\n            // `arginfo` again in the thread `RINIT` function.\n            if (rf->function && rf->function->internal_function.handler != rf->ori_handler) {\n                auto zf = zend::get_function(key);\n                auto ifn = &rf->function->internal_function;\n                zf->internal_function.handler = ifn->handler;\n                if (ifn->arg_info != rf->ori_arg_info && ifn->arg_info) {\n                    zf->internal_function.arg_info = copy_arginfo(ifn, ifn->arg_info);\n                }\n            }\n        }\n        ZEND_HASH_FOREACH_END();\n#endif\n        return;\n    }\n\n    hook_function_table = static_cast<zend_array *>(emalloc(sizeof(zend_array)));\n    zend_hash_init(hook_function_table, 8, nullptr, nullptr, 0);\n\n#if defined(HAVE_PUTENV) && defined(SW_THREAD)\n    /**\n     * There are issues with the implementation of putenv in PHP,\n     * which can lead to memory invalid read in multi-thread environment.\n     */\n    SW_HOOK_FUNC(putenv);\n#endif\n\n#if PHP_VERSION_ID >= 80400\n    SW_HOOK_FUNC(exit);\n#endif\n\n    HashTable *xport_hash = php_stream_xport_get_hash();\n    ori_factory.tcp = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL(\"tcp\"));\n    ori_factory.udp = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL(\"udp\"));\n    ori_factory._unix = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL(\"unix\"));\n    ori_factory.udg = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL(\"udg\"));\n    ori_factory.ssl = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL(\"ssl\"));\n    ori_factory.tls = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL(\"tls\"));\n\n    memcpy(&ori_php_plain_files_wrapper, &php_plain_files_wrapper, sizeof(php_plain_files_wrapper));\n    memcpy(&ori_php_stream_stdio_ops, &php_stream_stdio_ops, sizeof(php_stream_stdio_ops));\n}\n\nvoid php_swoole_runtime_rshutdown() {\n    if (!sw_is_main_thread()) {\n        return;\n    }\n\n    PHPCoroutine::disable_hook();\n    ori_func_handlers.clear();\n    ori_func_arg_infos.clear();\n\n    void *ptr;\n    ZEND_HASH_FOREACH_PTR(hook_function_table, ptr) {\n        auto *rf = static_cast<PhpFunc *>(ptr);\n        if (rf->fci_cache) {\n            sw_callable_free(rf->fci_cache);\n        }\n        if (Z_TYPE(rf->name) == IS_STRING) {\n            zval_dtor(&rf->name);\n        }\n        if (rf->arg_info_copy) {\n            free_arg_info(&rf->function->internal_function, rf->arg_info_copy);\n            rf->arg_info_copy = nullptr;\n        }\n        rf->function->internal_function.handler = rf->ori_handler;\n        rf->function->internal_function.arg_info = rf->ori_arg_info;\n        efree(rf);\n    }\n    ZEND_HASH_FOREACH_END();\n\n    zend_hash_destroy(hook_function_table);\n    efree(hook_function_table);\n    hook_function_table = nullptr;\n\n    clear_class_entries();\n}\n\nvoid php_swoole_runtime_mshutdown() {\n#ifdef SW_USE_CURL\n    swoole_native_curl_mshutdown();\n#endif\n}\n\nstatic inline char *parse_ip_address_ex(const char *str, size_t str_len, int *portno, int get_err, zend_string **err) {\n    char *colon;\n    char *host = nullptr;\n\n    if (*(str) == '[' && str_len > 1) {\n        /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */\n        char *p = (char *) memchr(str + 1, ']', str_len - 2);\n        if (!p || *(p + 1) != ':') {\n            if (get_err) {\n                *err = strpprintf(0, \"Failed to parse IPv6 address \\\"%s\\\"\", str);\n            }\n            return nullptr;\n        }\n        *portno = sw_atoi(p + 2);\n        return estrndup(str + 1, p - str - 1);\n    }\n    if (str_len) {\n        colon = (char *) memchr(str, ':', str_len - 1);\n    } else {\n        colon = nullptr;\n    }\n    if (colon) {\n        *portno = sw_atoi(colon + 1);\n        host = estrndup(str, colon - str);\n    } else {\n        if (get_err) {\n            *err = strpprintf(0, \"Failed to parse address \\\"%s\\\"\", str);\n        }\n        return nullptr;\n    }\n\n    return host;\n}\n\nstatic php_stream_size_t socket_write(php_stream *stream, const char *buf, size_t count) {\n    ssize_t didwrite = -1;\n    std::shared_ptr<SocketImpl> sock;\n\n    auto *abstract = static_cast<NetStream *>(stream->abstract);\n    if (UNEXPECTED(!abstract || !abstract->socket)) {\n        goto _exit;\n    }\n    sock = abstract->socket;\n\n    if (abstract->blocking) {\n        didwrite = sock->send_all(buf, count);\n    } else {\n        didwrite = sock->get_socket()->send(buf, count, 0);\n        sock->set_err(errno);\n    }\n\n    if (didwrite < 0 || (size_t) didwrite != count) {\n        /* we do not expect the outer layer to continue to call the `send` syscall in a loop\n         * and `didwrite` is meaningless if it failed */\n        didwrite = -1;\n        abstract->stream.timeout_event = (sock->errCode == ETIMEDOUT);\n        php_error_docref(nullptr,\n                         E_NOTICE,\n                         \"Send of \" ZEND_LONG_FMT \" bytes failed with errno=%d %s\",\n                         (zend_long) count,\n                         sock->errCode,\n                         sock->errMsg);\n    } else {\n        php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), didwrite, 0);\n    }\n\n    if (didwrite < 0) {\n        if (sock->errCode == ETIMEDOUT || sock->get_socket()->catch_write_error(sock->errCode) == SW_WAIT) {\n            didwrite = 0;\n        } else {\n            stream->eof = 1;\n        }\n    } else if (didwrite == 0) {\n        stream->eof = 1;\n    }\n\n_exit:\n    return didwrite;\n}\n\nstatic php_stream_size_t socket_read(php_stream *stream, char *buf, size_t count) {\n    std::shared_ptr<SocketImpl> sock;\n    ssize_t nr_bytes = -1;\n\n    auto *abstract = static_cast<NetStream *>(stream->abstract);\n    if (UNEXPECTED(!abstract || !abstract->socket)) {\n        goto _exit;\n    }\n    sock = abstract->socket;\n\n    if (abstract->blocking) {\n        nr_bytes = sock->recv(buf, count);\n    } else {\n        nr_bytes = sock->get_socket()->recv(buf, count, 0);\n        sock->set_err(errno);\n    }\n\n    if (nr_bytes > 0) {\n        php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), nr_bytes, 0);\n    }\n\n    if (nr_bytes < 0) {\n        if (sock->errCode == ETIMEDOUT || sock->get_socket()->catch_read_error(sock->errCode) == SW_WAIT) {\n            nr_bytes = 0;\n        } else {\n            stream->eof = 1;\n        }\n    } else if (nr_bytes == 0) {\n        stream->eof = 1;\n    }\n\n_exit:\n    return nr_bytes;\n}\n\nstatic int socket_flush(php_stream *stream) {\n    return 0;\n}\n\nstatic int socket_close(php_stream *stream, int close_handle) {\n    const auto *abstract = static_cast<NetStream *>(stream->abstract);\n    if (UNEXPECTED(!abstract)) {\n        return FAILURE;\n    }\n    /** set it null immediately */\n    stream->abstract = nullptr;\n    /**\n     * it's always successful (even if the destructor rule is violated)\n     * every calls passes through the hook function in PHP\n     * so there is unnecessary to worry about the null pointer.\n     */\n    if (abstract->socket) {\n        abstract->socket->close();\n    }\n    delete abstract;\n    return SUCCESS;\n}\n\nenum {\n    STREAM_XPORT_OP_BIND,\n    STREAM_XPORT_OP_CONNECT,\n    STREAM_XPORT_OP_LISTEN,\n    STREAM_XPORT_OP_ACCEPT,\n    STREAM_XPORT_OP_CONNECT_ASYNC,\n    STREAM_XPORT_OP_GET_NAME,\n    STREAM_XPORT_OP_GET_PEER_NAME,\n    STREAM_XPORT_OP_RECV,\n    STREAM_XPORT_OP_SEND,\n    STREAM_XPORT_OP_SHUTDOWN,\n};\n\nenum { STREAM_XPORT_CRYPTO_OP_SETUP, STREAM_XPORT_CRYPTO_OP_ENABLE };\n\nstatic int socket_cast(php_stream *stream, int castas, void **ret) {\n    const auto *abstract = static_cast<NetStream *>(stream->abstract);\n    if (UNEXPECTED(!abstract || !abstract->socket)) {\n        return FAILURE;\n    }\n    const std::shared_ptr<SocketImpl> sock = abstract->socket;\n    switch (castas) {\n    case PHP_STREAM_AS_STDIO:\n        if (ret) {\n            *reinterpret_cast<FILE **>(ret) = fdopen(sock->get_fd(), stream->mode);\n            if (*ret) {\n                return SUCCESS;\n            }\n            return FAILURE;\n        }\n        return SUCCESS;\n    case PHP_STREAM_AS_FD_FOR_SELECT:\n    case PHP_STREAM_AS_FD:\n    case PHP_STREAM_AS_SOCKETD:\n        if (ret) *reinterpret_cast<php_socket_t *>(ret) = sock->get_fd();\n        return SUCCESS;\n    default:\n        return FAILURE;\n    }\n}\nstatic int socket_stat(php_stream *stream, php_stream_statbuf *ssb) {\n    const auto *abstract = static_cast<NetStream *>(stream->abstract);\n    if (UNEXPECTED(!abstract)) {\n        return FAILURE;\n    }\n    if (UNEXPECTED(!abstract->socket)) {\n        return FAILURE;\n    }\n    return zend_fstat(abstract->socket->get_fd(), &ssb->sb);\n}\n\nstatic inline int socket_connect(php_stream *stream, SocketImpl *sock, php_stream_xport_param *xparam) {\n    char *host = nullptr, *bindto = nullptr;\n    int portno = 0, bindport = 0;\n    int ret = 0;\n    zval *tmpzval = nullptr;\n    char *ip_address = nullptr;\n\n    if (UNEXPECTED(sock->get_fd() < 0)) {\n        return FAILURE;\n    }\n\n    if (sock->get_socket()->is_inet()) {\n        ip_address = parse_ip_address_ex(\n            xparam->inputs.name, xparam->inputs.namelen, &portno, xparam->want_errortext, &xparam->outputs.error_text);\n        host = ip_address;\n        if (sock->get_sock_type() == SOCK_STREAM) {\n            sock->get_socket()->set_tcp_nodelay();\n        }\n    } else {\n        host = xparam->inputs.name;\n    }\n    if (host == nullptr) {\n        return FAILURE;\n    }\n    ON_SCOPE_EXIT {\n        if (ip_address) {\n            efree(ip_address);\n        }\n    };\n    if (PHP_STREAM_CONTEXT(stream) &&\n        (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), \"socket\", \"bindto\")) != nullptr) {\n        if (Z_TYPE_P(tmpzval) != IS_STRING) {\n            if (xparam->want_errortext) {\n                xparam->outputs.error_text = strpprintf(0, \"local_addr context option is not a string.\");\n            }\n            return FAILURE;\n        }\n        bindto = parse_ip_address_ex(\n            Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval), &bindport, xparam->want_errortext, &xparam->outputs.error_text);\n        if (bindto == nullptr) {\n            return FAILURE;\n        }\n        if (strcmp(bindto, \"0\") == 0 || bindto[0] == '\\0') {\n            efree(bindto);\n            bindto = nullptr;\n        }\n        ON_SCOPE_EXIT {\n            if (bindto) {\n                efree(bindto);\n            }\n        };\n        if (!sock->bind(bindto ? bindto : \"0.0.0.0\", bindport)) {\n            return FAILURE;\n        }\n    }\n\n    if (xparam->inputs.timeout) {\n        sock->set_timeout(xparam->inputs.timeout, SW_TIMEOUT_CONNECT);\n    }\n    if (sock->connect(host, portno) == false) {\n        xparam->outputs.error_code = sock->errCode;\n        if (sock->errMsg) {\n            xparam->outputs.error_text = zend_string_init(sock->errMsg, strlen(sock->errMsg), false);\n        }\n        ret = -1;\n    }\n    return ret;\n}\n\nstatic inline int socket_bind(php_stream *stream, SocketImpl *sock, php_stream_xport_param *xparam STREAMS_DC) {\n    char *host = nullptr;\n    int portno = 0;\n    char *ip_address = nullptr;\n\n    if (sock->get_socket()->is_inet()) {\n        ip_address = parse_ip_address_ex(\n            xparam->inputs.name, xparam->inputs.namelen, &portno, xparam->want_errortext, &xparam->outputs.error_text);\n        host = ip_address;\n    } else {\n        host = xparam->inputs.name;\n    }\n    if (host == nullptr) {\n        sock->set_err(EINVAL);\n        return -1;\n    }\n    int ret = sock->bind(host, portno) ? 0 : -1;\n    if (ip_address) {\n        efree(ip_address);\n    }\n    return ret;\n}\n\nstatic inline int socket_accept(php_stream *stream, SocketImpl *sock, php_stream_xport_param *xparam STREAMS_DC) {\n    int tcp_nodelay = 0;\n    zval *tmpzval = nullptr;\n\n    xparam->outputs.client = nullptr;\n\n    if ((nullptr != PHP_STREAM_CONTEXT(stream)) &&\n        (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), \"socket\", \"tcp_nodelay\")) != nullptr &&\n        zval_is_true(tmpzval)) {\n        tcp_nodelay = 1;\n    }\n\n    zend_string **textaddr = xparam->want_textaddr ? &xparam->outputs.textaddr : nullptr;\n    sockaddr **addr = xparam->want_addr ? &xparam->outputs.addr : nullptr;\n    socklen_t *addrlen = xparam->want_addr ? &xparam->outputs.addrlen : nullptr;\n\n    timeval *timeout = xparam->inputs.timeout;\n    zend_string **error_string = xparam->want_errortext ? &xparam->outputs.error_text : nullptr;\n    int *error_code = &xparam->outputs.error_code;\n\n    int error = 0;\n    php_sockaddr_storage sa;\n    socklen_t sl = sizeof(sa);\n\n    if (timeout) {\n        sock->set_timeout(timeout, SW_TIMEOUT_READ);\n    }\n\n    std::shared_ptr<SocketImpl> clisock(sock->accept());\n\n    if (clisock != nullptr && clisock->ssl_is_enable()) {\n        if (!clisock->ssl_handshake()) {\n            sock->errCode = clisock->errCode;\n            clisock.reset();\n        }\n    }\n\n    if (clisock == nullptr) {\n        error = sock->errCode;\n        if (error_code) {\n            *error_code = error;\n        }\n        if (error_string) {\n            *error_string = php_socket_error_str(error);\n        }\n        return FAILURE;\n    } else {\n        php_network_populate_name_from_sockaddr((sockaddr *) &sa, sl, textaddr, addr, addrlen);\n#ifdef TCP_NODELAY\n        if (tcp_nodelay) {\n            clisock->get_socket()->set_tcp_nodelay(tcp_nodelay);\n        }\n#endif\n        auto abstract = new NetStream();\n        abstract->socket = clisock;\n        abstract->blocking = true;\n\n        xparam->outputs.client = php_stream_alloc_rel(stream->ops, abstract, nullptr, \"r+\");\n        if (xparam->outputs.client) {\n            xparam->outputs.client->ctx = stream->ctx;\n            if (stream->ctx) {\n                GC_ADDREF(stream->ctx);\n            }\n        }\n        return SUCCESS;\n    }\n}\n\nstatic inline int socket_recvfrom(\n    SocketImpl *sock, char *buf, size_t buflen, zend_string **textaddr, struct sockaddr **addr, socklen_t *addrlen) {\n    int ret;\n    int want_addr = textaddr || addr;\n\n    if (want_addr) {\n        php_sockaddr_storage sa;\n        socklen_t sl = sizeof(sa);\n        ret = sock->recvfrom(buf, buflen, (struct sockaddr *) &sa, &sl);\n        if (sl) {\n            php_network_populate_name_from_sockaddr((struct sockaddr *) &sa, sl, textaddr, addr, addrlen);\n        } else {\n            if (textaddr) {\n                *textaddr = ZSTR_EMPTY_ALLOC();\n            }\n            if (addr) {\n                *addr = nullptr;\n                *addrlen = 0;\n            }\n        }\n    } else {\n        ret = sock->recv(buf, buflen);\n    }\n\n    return ret;\n}\n\nstatic inline int socket_sendto(\n    SocketImpl *sock, const char *buf, size_t buflen, struct sockaddr *addr, socklen_t addrlen) {\n    if (addr) {\n        return sendto(sock->get_fd(), buf, buflen, 0, addr, addrlen);\n    } else {\n        return sock->send(buf, buflen);\n    }\n}\n\nstatic int socket_setup_crypto(php_stream *stream, SocketImpl *sock, php_stream_xport_crypto_param *cparam STREAMS_DC) {\n    return 0;\n}\n\nstatic int socket_xport_crypto_setup(php_stream *stream) {\n    php_stream_xport_crypto_param param = {};\n\n    param.op = (decltype(param.op)) STREAM_XPORT_CRYPTO_OP_SETUP;\n    param.inputs.method = (php_stream_xport_crypt_method_t) 0;\n    param.inputs.session = nullptr;\n\n    int ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, &param);\n\n    if (ret == PHP_STREAM_OPTION_RETURN_OK) {\n        return param.outputs.returncode;\n    }\n\n    php_error_docref(\"streams.crypto\", E_WARNING, \"this stream does not support SSL/crypto\");\n\n    return ret;\n}\n\nstatic int socket_xport_crypto_enable(php_stream *stream, int activate) {\n    php_stream_xport_crypto_param param = {};\n\n    param.op = (decltype(param.op)) STREAM_XPORT_CRYPTO_OP_ENABLE;\n    param.inputs.activate = activate;\n\n    int ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, &param);\n\n    if (ret == PHP_STREAM_OPTION_RETURN_OK) {\n        return param.outputs.returncode;\n    }\n\n    php_error_docref(\"streams.crypto\", E_WARNING, \"this stream does not support SSL/crypto\");\n\n    return ret;\n}\n\nstatic bool php_openssl_capture_peer_certs(php_stream *stream, SocketImpl *sslsock) {\n    zval *val;\n\n    std::string peer_cert = sslsock->ssl_get_peer_cert();\n    if (peer_cert.empty()) {\n        return false;\n    }\n\n    zval argv[1];\n    ZVAL_STRINGL(&argv[0], peer_cert.c_str(), peer_cert.length());\n    auto retval = zend::function::call(\"openssl_x509_read\", 1, argv);\n    php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), \"ssl\", \"peer_certificate\", &retval.value);\n    zval_dtor(&argv[0]);\n\n    if (nullptr !=\n            (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), \"ssl\", \"capture_peer_cert_chain\")) &&\n        zend_is_true(val)) {\n        zval arr;\n        auto chain = sslsock->get_socket()->ssl_get_peer_cert_chain(INT_MAX);\n\n        if (!chain.empty()) {\n            array_init(&arr);\n            for (auto &cert : chain) {\n                zval _argv[1];\n                ZVAL_STRINGL(&_argv[0], cert.c_str(), cert.length());\n                auto _retval = zend::function::call(\"openssl_x509_read\", 1, _argv);\n                zval_add_ref(&_retval.value);\n                add_next_index_zval(&arr, &_retval.value);\n                zval_dtor(&_argv[0]);\n            }\n        } else {\n            ZVAL_NULL(&arr);\n        }\n\n        php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), \"ssl\", \"peer_certificate_chain\", &arr);\n        zval_ptr_dtor(&arr);\n    }\n\n    return true;\n}\n\nstatic int socket_enable_crypto(php_stream *stream,\n                                SocketImpl *sock,\n                                php_stream_xport_crypto_param *cparam STREAMS_DC) {\n    php_stream_context *context = PHP_STREAM_CONTEXT(stream);\n    if (cparam->inputs.activate && !sock->ssl_is_available()) {\n        sock->enable_ssl_encrypt();\n        if (!socket_ssl_set_options(sock, context)) {\n            return -1;\n        }\n        if (!sock->ssl_handshake()) {\n            return -1;\n        }\n    } else if (!cparam->inputs.activate && sock->ssl_is_available() && sock->get_ssl()) {\n        sock->ssl_close();\n        return -1;\n    }\n\n    if (context && sock->ssl_is_available()) {\n        zval *val = php_stream_context_get_option(context, \"ssl\", \"capture_peer_cert\");\n        if (val && zend_is_true(val) && !php_openssl_capture_peer_certs(stream, sock)) {\n            return -1;\n        }\n    }\n\n    return 1;\n}\n\nstatic inline int socket_xport_api(php_stream *stream, SocketImpl *sock, php_stream_xport_param *xparam STREAMS_DC) {\n    static const int shutdown_how[] = {SHUT_RD, SHUT_WR, SHUT_RDWR};\n\n    switch (xparam->op) {\n    case STREAM_XPORT_OP_LISTEN: {\n        xparam->outputs.returncode = sock->listen(xparam->inputs.backlog) ? 0 : -1;\n        break;\n    }\n    case STREAM_XPORT_OP_CONNECT:\n    case STREAM_XPORT_OP_CONNECT_ASYNC:\n        xparam->outputs.returncode = socket_connect(stream, sock, xparam);\n        if (sock->ssl_is_enable() &&\n            (socket_xport_crypto_setup(stream) < 0 || socket_xport_crypto_enable(stream, 1) < 0)) {\n            xparam->outputs.returncode = -1;\n        }\n        break;\n    case STREAM_XPORT_OP_BIND: {\n        if (sock->get_sock_domain() != AF_UNIX) {\n            zval *tmpzval = nullptr;\n            php_stream_context *ctx = PHP_STREAM_CONTEXT(stream);\n            if (!ctx) {\n                break;\n            }\n#ifdef SO_REUSEADDR\n            sock->get_socket()->set_reuse_addr();\n#endif\n\n#ifdef IPV6_V6ONLY\n            if ((tmpzval = php_stream_context_get_option(ctx, \"socket\", \"ipv6_v6only\")) != nullptr &&\n                zval_is_true(tmpzval)) {\n                sock->get_socket()->set_option(IPPROTO_IPV6, IPV6_V6ONLY, 1);\n            }\n#endif\n\n#ifdef SO_REUSEPORT\n            if ((tmpzval = php_stream_context_get_option(ctx, \"socket\", \"so_reuseport\")) != nullptr &&\n                zval_is_true(tmpzval)) {\n                sock->get_socket()->set_reuse_port();\n            }\n#endif\n\n#ifdef SO_BROADCAST\n            if ((tmpzval = php_stream_context_get_option(ctx, \"socket\", \"so_broadcast\")) != nullptr &&\n                zval_is_true(tmpzval)) {\n                sock->set_option(SOL_SOCKET, SO_BROADCAST, 1);\n            }\n#endif\n        }\n        xparam->outputs.returncode = socket_bind(stream, sock, xparam STREAMS_CC);\n        break;\n    }\n    case STREAM_XPORT_OP_ACCEPT:\n        xparam->outputs.returncode = socket_accept(stream, sock, xparam STREAMS_CC);\n        break;\n    case STREAM_XPORT_OP_GET_NAME:\n        xparam->outputs.returncode =\n            php_network_get_sock_name(sock->get_fd(),\n                                      xparam->want_textaddr ? &xparam->outputs.textaddr : nullptr,\n                                      xparam->want_addr ? &xparam->outputs.addr : nullptr,\n                                      xparam->want_addr ? &xparam->outputs.addrlen : nullptr);\n        break;\n    case STREAM_XPORT_OP_GET_PEER_NAME:\n        xparam->outputs.returncode =\n            php_network_get_peer_name(sock->get_fd(),\n                                      xparam->want_textaddr ? &xparam->outputs.textaddr : nullptr,\n                                      xparam->want_addr ? &xparam->outputs.addr : nullptr,\n                                      xparam->want_addr ? &xparam->outputs.addrlen : nullptr);\n        break;\n\n    case STREAM_XPORT_OP_SEND:\n        if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {\n            php_swoole_error(E_WARNING, \"STREAM_OOB flags is not supports\");\n            xparam->outputs.returncode = -1;\n            break;\n        }\n        xparam->outputs.returncode =\n            socket_sendto(sock, xparam->inputs.buf, xparam->inputs.buflen, xparam->inputs.addr, xparam->inputs.addrlen);\n        if (xparam->outputs.returncode == -1) {\n            char *err = php_socket_strerror(php_socket_errno(), nullptr, 0);\n            php_error_docref(nullptr, E_WARNING, \"%s\\n\", err);\n            efree(err);\n        }\n        break;\n\n    case STREAM_XPORT_OP_RECV:\n        if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) {\n            php_swoole_error(E_WARNING, \"STREAM_OOB flags is not supports\");\n            xparam->outputs.returncode = -1;\n            break;\n        }\n        if ((xparam->inputs.flags & STREAM_PEEK) == STREAM_PEEK) {\n            xparam->outputs.returncode = sock->peek(xparam->inputs.buf, xparam->inputs.buflen);\n        } else {\n            xparam->outputs.returncode = socket_recvfrom(sock,\n                                                         xparam->inputs.buf,\n                                                         xparam->inputs.buflen,\n                                                         xparam->want_textaddr ? &xparam->outputs.textaddr : nullptr,\n                                                         xparam->want_addr ? &xparam->outputs.addr : nullptr,\n                                                         xparam->want_addr ? &xparam->outputs.addrlen : nullptr);\n        }\n        break;\n    case STREAM_XPORT_OP_SHUTDOWN:\n        xparam->outputs.returncode = sock->shutdown(shutdown_how[xparam->how]);\n        break;\n    default:\n#ifdef SW_DEBUG\n        php_swoole_fatal_error(E_WARNING, \"socket_xport_api: unsupported option %d\", xparam->op);\n#endif\n        break;\n    }\n    return PHP_STREAM_OPTION_RETURN_OK;\n}\n\nstatic int socket_set_option(php_stream *stream, int option, int value, void *ptrparam) {\n    auto *abstract = static_cast<NetStream *>(stream->abstract);\n    if (UNEXPECTED(!abstract || !abstract->socket)) {\n        return PHP_STREAM_OPTION_RETURN_ERR;\n    }\n    std::shared_ptr<SocketImpl> sock_wrapped = abstract->socket;\n    auto sock = sock_wrapped.get();\n    switch (option) {\n    case PHP_STREAM_OPTION_BLOCKING:\n        if (abstract->blocking == (bool) value) {\n            break;\n        }\n        abstract->blocking = (bool) value;\n        break;\n    case PHP_STREAM_OPTION_XPORT_API: {\n        return socket_xport_api(stream, sock, (php_stream_xport_param *) ptrparam STREAMS_CC);\n    }\n    case PHP_STREAM_OPTION_META_DATA_API: {\n        SSL *ssl = sock->get_socket() ? sock->get_socket()->ssl : nullptr;\n        if (ssl) {\n            zval tmp;\n            const char *proto_str;\n\n            array_init(&tmp);\n            switch (SSL_version(ssl)) {\n#ifdef TLS1_3_VERSION\n            case TLS1_3_VERSION:\n                proto_str = \"TLSv1.3\";\n                break;\n#endif\n#ifdef TLS1_2_VERSION\n            case TLS1_2_VERSION:\n                proto_str = \"TLSv1.2\";\n                break;\n#endif\n#ifdef TLS1_1_VERSION\n            case TLS1_1_VERSION:\n                proto_str = \"TLSv1.1\";\n                break;\n#endif\n            case TLS1_VERSION:\n                proto_str = \"TLSv1\";\n                break;\n#ifdef SSL3_VERSION\n            case SSL3_VERSION:\n                proto_str = \"SSLv3\";\n                break;\n#endif\n            default:\n                proto_str = \"UNKNOWN\";\n                break;\n            }\n\n            const auto cipher = SSL_get_current_cipher(ssl);\n            add_assoc_string(&tmp, \"protocol\", proto_str);\n            add_assoc_string(&tmp, \"cipher_name\", SSL_CIPHER_get_name(cipher));\n            add_assoc_long(&tmp, \"cipher_bits\", SSL_CIPHER_get_bits(cipher, nullptr));\n            add_assoc_string(&tmp, \"cipher_version\", SSL_CIPHER_get_version(cipher));\n            add_assoc_zval((zval *) ptrparam, \"crypto\", &tmp);\n        }\n        add_assoc_bool((zval *) ptrparam, \"timed_out\", sock->errCode == ETIMEDOUT);\n        add_assoc_bool((zval *) ptrparam, \"eof\", stream->eof);\n        add_assoc_bool((zval *) ptrparam, \"blocked\", true);\n        break;\n    }\n    case PHP_STREAM_OPTION_READ_TIMEOUT: {\n        abstract->socket->set_timeout(static_cast<timeval *>(ptrparam), SW_TIMEOUT_READ);\n        break;\n    }\n    case PHP_STREAM_OPTION_CRYPTO_API: {\n        auto *cparam = static_cast<php_stream_xport_crypto_param *>(ptrparam);\n        switch (cparam->op) {\n        case STREAM_XPORT_CRYPTO_OP_SETUP:\n            cparam->outputs.returncode = socket_setup_crypto(stream, sock, cparam STREAMS_CC);\n            return PHP_STREAM_OPTION_RETURN_OK;\n        case STREAM_XPORT_CRYPTO_OP_ENABLE:\n            cparam->outputs.returncode = socket_enable_crypto(stream, sock, cparam STREAMS_CC);\n            return PHP_STREAM_OPTION_RETURN_OK;\n        default:\n            /* never here */\n            SW_ASSERT(0);\n            break;\n        }\n        break;\n    }\n    case PHP_STREAM_OPTION_CHECK_LIVENESS: {\n        return sock->check_liveness() ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;\n    }\n    case PHP_STREAM_OPTION_READ_BUFFER:\n    case PHP_STREAM_OPTION_WRITE_BUFFER: {\n        // TODO: read/write buffer\n        break;\n    }\n    default:\n#ifdef SW_DEBUG\n        php_swoole_fatal_error(E_WARNING, \"socket_set_option: unsupported option %d with value %d\", option, value);\n#endif\n        break;\n    }\n    return PHP_STREAM_OPTION_RETURN_OK;\n}\n\nstatic bool socket_ssl_set_options(SocketImpl *sock, php_stream_context *context) {\n    if (context && ZVAL_IS_ARRAY(&context->options)) {\n        zval *ztmp;\n\n        if (sock->ssl_is_enable() && php_swoole_array_get_value(Z_ARRVAL_P(&context->options), \"ssl\", ztmp) &&\n            ZVAL_IS_ARRAY(ztmp)) {\n            zval zalias;\n            array_init(&zalias);\n            zend_array *options = Z_ARRVAL_P(ztmp);\n\n            auto add_alias = [&zalias, options](const char *name, const char *alias) {\n                zval *ztmp;\n                if (php_swoole_array_get_value_ex(options, name, ztmp)) {\n                    zend::array_set(&zalias, alias, strlen(alias), ztmp);\n                }\n            };\n\n            add_alias(\"peer_name\", \"ssl_host_name\");\n            add_alias(\"verify_peer\", \"ssl_verify_peer\");\n            add_alias(\"allow_self_signed\", \"ssl_allow_self_signed\");\n            add_alias(\"cafile\", \"ssl_cafile\");\n            add_alias(\"capath\", \"ssl_capath\");\n            add_alias(\"local_cert\", \"ssl_cert_file\");\n            add_alias(\"local_pk\", \"ssl_key_file\");\n            add_alias(\"passphrase\", \"ssl_passphrase\");\n            add_alias(\"verify_depth\", \"ssl_verify_depth\");\n            add_alias(\"disable_compression\", \"ssl_disable_compression\");\n\n            bool ret = php_swoole_socket_set_ssl(sock, &zalias);\n            zval_dtor(&zalias);\n            return ret;\n        }\n    }\n\n    return true;\n}\n\nstatic php_stream *socket_create_original(const char *proto,\n                                          size_t protolen,\n                                          const char *resourcename,\n                                          size_t resourcenamelen,\n                                          const char *persistent_id,\n                                          int options,\n                                          int flags,\n                                          struct timeval *timeout,\n                                          php_stream_context *context STREAMS_DC) {\n    php_stream_transport_factory factory = nullptr;\n    if (SW_STREQ(proto, protolen, \"tcp\")) {\n        factory = ori_factory.tcp;\n    } else if (SW_STREQ(proto, protolen, \"ssl\")) {\n        factory = ori_factory.ssl;\n    } else if (SW_STREQ(proto, protolen, \"tls\")) {\n        factory = ori_factory.tls;\n    } else if (SW_STREQ(proto, protolen, \"unix\")) {\n        factory = ori_factory._unix;\n    } else if (SW_STREQ(proto, protolen, \"udp\")) {\n        factory = ori_factory.udp;\n    } else if (SW_STREQ(proto, protolen, \"udg\")) {\n        factory = ori_factory.udg;\n    }\n\n    if (factory) {\n        return factory(\n            proto, protolen, resourcename, resourcenamelen, persistent_id, options, flags, timeout, context STREAMS_CC);\n    } else {\n        php_swoole_fatal_error(E_WARNING, \"unknown protocol '%s'\", proto);\n        return nullptr;\n    }\n}\n\nstatic php_stream *socket_create(const char *proto,\n                                 size_t protolen,\n                                 const char *resourcename,\n                                 size_t resourcenamelen,\n                                 const char *persistent_id,\n                                 int options,\n                                 int flags,\n                                 struct timeval *timeout,\n                                 php_stream_context *context STREAMS_DC) {\n    php_stream *stream = nullptr;\n    SocketImpl *sock = nullptr;\n\n    auto co = Coroutine::get_current();\n    if (sw_unlikely(co == nullptr)) {\n        return socket_create_original(\n            proto, protolen, resourcename, resourcenamelen, persistent_id, options, flags, timeout, context STREAMS_CC);\n    }\n\n    if (SW_STR_STARTS_WITH(proto, protolen, \"async.\")) {\n        proto += sizeof(\"async.\") - 1;\n        protolen -= sizeof(\"async.\") - 1;\n    }\n\n    if (SW_STREQ(proto, protolen, \"tcp\")) {\n        sock = new SocketImpl(resourcename[0] == '[' ? SW_SOCK_TCP6 : SW_SOCK_TCP);\n    } else if (SW_STREQ(proto, protolen, \"ssl\") || SW_STREQ(proto, protolen, \"tls\")) {\n        sock = new SocketImpl(resourcename[0] == '[' ? SW_SOCK_TCP6 : SW_SOCK_TCP);\n        sock->enable_ssl_encrypt();\n    } else if (SW_STREQ(proto, protolen, \"unix\")) {\n        sock = new SocketImpl(SW_SOCK_UNIX_STREAM);\n    } else if (SW_STREQ(proto, protolen, \"udp\")) {\n        sock = new SocketImpl(SW_SOCK_UDP);\n    } else if (SW_STREQ(proto, protolen, \"udg\")) {\n        sock = new SocketImpl(SW_SOCK_UNIX_DGRAM);\n    } else {\n        php_swoole_fatal_error(E_WARNING, \"unknown protocol '%s'\", proto);\n        return nullptr;\n    }\n\n    if (UNEXPECTED(sock->get_fd() < 0)) {\n    _failed:\n        if (!stream) {\n            delete sock;\n        } else {\n            php_stream_close(stream);\n        }\n        return nullptr;\n    }\n\n    sock->set_zero_copy(true);\n\n    auto abstract = new NetStream();\n    abstract->socket.reset(sock);\n    abstract->stream.socket = sock->get_fd();\n    abstract->blocking = true;\n\n    stream = php_stream_alloc_rel(&socket_ops, abstract, persistent_id, \"r+\");\n    if (stream == nullptr) {\n        delete abstract;\n        goto _failed;\n    }\n\n    if (!socket_ssl_set_options(sock, context)) {\n        goto _failed;\n    }\n\n    return stream;\n}\n\nstatic ZEND_FUNCTION(swoole_display_disabled_function) {\n    zend_error(E_WARNING, \"%s() has been disabled for security reasons\", get_active_function_name());\n}\n\nstatic bool disable_func(const char *name, size_t l_name) {\n    auto *rf = static_cast<PhpFunc *>(zend_hash_str_find_ptr(hook_function_table, name, l_name));\n    if (rf) {\n        rf->function->internal_function.handler = ZEND_FN(swoole_display_disabled_function);\n        return true;\n    }\n\n    auto *zf = zend::get_function(name, l_name);\n    if (zf == nullptr) {\n        return false;\n    }\n\n    rf = static_cast<PhpFunc *>(emalloc(sizeof(PhpFunc)));\n    sw_memset_zero(rf, sizeof(*rf));\n    rf->function = zf;\n    rf->ori_handler = zf->internal_function.handler;\n    rf->ori_arg_info = zf->internal_function.arg_info;\n    rf->ori_fn_flags = zf->internal_function.fn_flags;\n\n    zf->internal_function.handler = ZEND_FN(swoole_display_disabled_function);\n    zf->internal_function.fn_flags = ZEND_ACC_FAKE_CLOSURE;\n\n    zend_hash_add_ptr(hook_function_table, zf->common.function_name, rf);\n    return true;\n}\n\nstatic bool enable_func(const char *name, size_t l_name) {\n    auto *rf = static_cast<PhpFunc *>(zend_hash_str_find_ptr(hook_function_table, name, l_name));\n    if (!rf) {\n        return false;\n    }\n\n    rf->function->internal_function.handler = rf->ori_handler;\n    rf->function->internal_function.fn_flags = rf->ori_fn_flags;\n\n    return true;\n}\n\nbool php_swoole_call_original_handler(const char *name, size_t len, INTERNAL_FUNCTION_PARAMETERS) {\n    auto ori_handler = php_swoole_get_original_handler(name, len);\n    if (!ori_handler) {\n        return false;\n    }\n    ori_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n\n    return true;\n}\n\nvoid PHPCoroutine::disable_unsafe_function() {\n    for (auto &f : unsafe_functions) {\n        disable_func(f.c_str(), f.length());\n    }\n}\n\nvoid PHPCoroutine::enable_unsafe_function() {\n    for (auto &f : unsafe_functions) {\n        enable_func(f.c_str(), f.length());\n    }\n}\n\nstatic void hook_stream_throw_exception(const char *type) {\n    zend_throw_exception_ex(\n        swoole_exception_ce, SW_ERROR_PHP_FATAL_ERROR, \"failed to register `%s` stream transport factory\", type);\n}\n\nstatic void hook_stream_factory(uint32_t *flags_ptr) {\n    uint32_t flags = *flags_ptr;\n\n    if (flags & PHPCoroutine::HOOK_TCP) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_TCP)) {\n            if (php_stream_xport_register(\"tcp\", socket_create) != SUCCESS) {\n                flags ^= PHPCoroutine::HOOK_TCP;\n                hook_stream_throw_exception(\"tcp\");\n            }\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_TCP) {\n            php_stream_xport_register(\"tcp\", ori_factory.tcp);\n        }\n    }\n    if (flags & PHPCoroutine::HOOK_UDP) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_UDP)) {\n            if (php_stream_xport_register(\"udp\", socket_create) != SUCCESS) {\n                flags ^= PHPCoroutine::HOOK_UDP;\n                hook_stream_throw_exception(\"udp\");\n            }\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_UDP) {\n            php_stream_xport_register(\"udp\", ori_factory.udp);\n        }\n    }\n    if (flags & PHPCoroutine::HOOK_UNIX) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_UNIX)) {\n            if (php_stream_xport_register(\"unix\", socket_create) != SUCCESS) {\n                flags ^= PHPCoroutine::HOOK_UNIX;\n                hook_stream_throw_exception(\"unix\");\n            }\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_UNIX) {\n            php_stream_xport_register(\"unix\", ori_factory._unix);\n        }\n    }\n    if (flags & PHPCoroutine::HOOK_UDG) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_UDG)) {\n            if (php_stream_xport_register(\"udg\", socket_create) != SUCCESS) {\n                flags ^= PHPCoroutine::HOOK_UDG;\n                hook_stream_throw_exception(\"udg\");\n            }\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_UDG) {\n            php_stream_xport_register(\"udg\", ori_factory.udg);\n        }\n    }\n    if (flags & PHPCoroutine::HOOK_SSL) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_SSL)) {\n            if (php_stream_xport_register(\"ssl\", socket_create) != SUCCESS) {\n                flags ^= PHPCoroutine::HOOK_SSL;\n                hook_stream_throw_exception(\"ssl\");\n            }\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_SSL) {\n            if (ori_factory.ssl != nullptr) {\n                php_stream_xport_register(\"ssl\", ori_factory.ssl);\n            } else {\n                php_stream_xport_unregister(\"ssl\");\n            }\n        }\n    }\n    if (flags & PHPCoroutine::HOOK_TLS) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_TLS)) {\n            if (php_stream_xport_register(\"tls\", socket_create) != SUCCESS) {\n                flags ^= PHPCoroutine::HOOK_TLS;\n                hook_stream_throw_exception(\"tls\");\n            }\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_TLS) {\n            if (ori_factory.tls != nullptr) {\n                php_stream_xport_register(\"tls\", ori_factory.tls);\n            } else {\n                php_stream_xport_unregister(\"tls\");\n            }\n        }\n    }\n    *flags_ptr = flags;\n}\n\nstatic void hook_stream_ops(uint32_t flags) {\n    // file\n    if (flags & PHPCoroutine::HOOK_FILE) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_FILE)) {\n            memcpy(&php_plain_files_wrapper, &sw_php_plain_files_wrapper, sizeof(php_plain_files_wrapper));\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_FILE) {\n            memcpy(&php_plain_files_wrapper, &ori_php_plain_files_wrapper, sizeof(php_plain_files_wrapper));\n        }\n    }\n    // stdio\n    if (flags & PHPCoroutine::HOOK_STDIO) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_STDIO)) {\n            memcpy(&php_stream_stdio_ops, &sw_php_stream_stdio_ops, sizeof(php_stream_stdio_ops));\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_STDIO) {\n            memcpy(&php_stream_stdio_ops, &ori_php_stream_stdio_ops, sizeof(php_stream_stdio_ops));\n        }\n    }\n}\n\nstatic void hook_pdo_driver(uint32_t flags) {\n#ifdef SW_USE_PGSQL\n    if (flags & PHPCoroutine::HOOK_PDO_PGSQL) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_PDO_PGSQL)) {\n            swoole_pgsql_set_blocking(false);\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_PDO_PGSQL) {\n            swoole_pgsql_set_blocking(true);\n        }\n    }\n#endif\n#ifdef SW_USE_ODBC\n    if (flags & PHPCoroutine::HOOK_PDO_ODBC) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_PDO_ODBC)) {\n            swoole_odbc_set_blocking(false);\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_PDO_ODBC) {\n            swoole_odbc_set_blocking(true);\n        }\n    }\n#endif\n#ifdef SW_USE_ORACLE\n    if (flags & PHPCoroutine::HOOK_PDO_ORACLE) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_PDO_ORACLE)) {\n            swoole_oracle_set_blocking(0);\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_PDO_ORACLE) {\n            swoole_oracle_set_blocking(1);\n        }\n    }\n#endif\n#ifdef SW_USE_SQLITE\n    if (flags & PHPCoroutine::HOOK_PDO_SQLITE) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_PDO_SQLITE)) {\n            swoole_sqlite_set_blocking(false);\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_PDO_SQLITE) {\n            swoole_sqlite_set_blocking(true);\n        }\n    }\n#endif\n#ifdef SW_USE_FIREBIRD\n    if (flags & PHPCoroutine::HOOK_PDO_FIREBIRD) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_PDO_FIREBIRD)) {\n            swoole_firebird_set_blocking(false);\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_PDO_FIREBIRD) {\n            swoole_firebird_set_blocking(true);\n        }\n    }\n#endif\n}\n\nstatic void hook_all_func(uint32_t flags) {\n    // stream func\n    if (flags & PHPCoroutine::HOOK_STREAM_FUNCTION) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_STREAM_FUNCTION)) {\n            SW_HOOK_FUNC(stream_select);\n            SW_HOOK_FUNC(stream_socket_pair);\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_STREAM_FUNCTION) {\n            SW_UNHOOK_FUNC(stream_select);\n            SW_UNHOOK_FUNC(stream_socket_pair);\n        }\n    }\n    // sleep\n    if (flags & PHPCoroutine::HOOK_SLEEP) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_SLEEP)) {\n            SW_HOOK_FUNC(sleep);\n            SW_HOOK_FUNC(usleep);\n            SW_HOOK_FUNC(time_nanosleep);\n            SW_HOOK_FUNC(time_sleep_until);\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_SLEEP) {\n            SW_UNHOOK_FUNC(sleep);\n            SW_UNHOOK_FUNC(usleep);\n            SW_UNHOOK_FUNC(time_nanosleep);\n            SW_UNHOOK_FUNC(time_sleep_until);\n        }\n    }\n    // proc_open\n    if (flags & PHPCoroutine::HOOK_PROC) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_PROC)) {\n            SW_HOOK_FUNC(proc_open);\n            SW_HOOK_FUNC(proc_close);\n            SW_HOOK_FUNC(proc_get_status);\n            SW_HOOK_FUNC(proc_terminate);\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_PROC) {\n            SW_UNHOOK_FUNC(proc_open);\n            SW_UNHOOK_FUNC(proc_close);\n            SW_UNHOOK_FUNC(proc_get_status);\n            SW_UNHOOK_FUNC(proc_terminate);\n        }\n    }\n    // ext-sockets\n    if (flags & PHPCoroutine::HOOK_SOCKETS) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_SOCKETS)) {\n            SW_HOOK_WITH_PHP_FUNC(socket_create);\n            SW_HOOK_WITH_PHP_FUNC(socket_create_listen);\n            SW_HOOK_WITH_PHP_FUNC(socket_create_pair);\n            SW_HOOK_WITH_PHP_FUNC(socket_connect);\n            SW_HOOK_WITH_PHP_FUNC(socket_write);\n            SW_HOOK_WITH_PHP_FUNC(socket_read);\n            SW_HOOK_WITH_PHP_FUNC(socket_send);\n            SW_HOOK_WITH_PHP_FUNC(socket_recv);\n            SW_HOOK_WITH_PHP_FUNC(socket_sendto);\n            SW_HOOK_WITH_PHP_FUNC(socket_recvfrom);\n            SW_HOOK_WITH_PHP_FUNC(socket_bind);\n            SW_HOOK_WITH_PHP_FUNC(socket_listen);\n            SW_HOOK_WITH_PHP_FUNC(socket_accept);\n            SW_HOOK_WITH_PHP_FUNC(socket_getpeername);\n            SW_HOOK_WITH_PHP_FUNC(socket_getsockname);\n            SW_HOOK_WITH_PHP_FUNC(socket_getopt);\n            SW_HOOK_WITH_PHP_FUNC(socket_get_option);\n            SW_HOOK_WITH_PHP_FUNC(socket_setopt);\n            SW_HOOK_WITH_PHP_FUNC(socket_set_option);\n            SW_HOOK_WITH_PHP_FUNC(socket_set_block);\n            SW_HOOK_WITH_PHP_FUNC(socket_set_nonblock);\n            SW_HOOK_WITH_PHP_FUNC(socket_shutdown);\n            SW_HOOK_WITH_PHP_FUNC(socket_close);\n            SW_HOOK_WITH_PHP_FUNC(socket_clear_error);\n            SW_HOOK_WITH_PHP_FUNC(socket_last_error);\n            SW_HOOK_WITH_PHP_FUNC(socket_import_stream);\n\n            inherit_class(ZEND_STRL(\"Swoole\\\\Coroutine\\\\Socket\"), ZEND_STRL(\"Socket\"));\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_SOCKETS) {\n            SW_UNHOOK_FUNC(socket_create);\n            SW_UNHOOK_FUNC(socket_create_listen);\n            SW_UNHOOK_FUNC(socket_create_pair);\n            SW_UNHOOK_FUNC(socket_connect);\n            SW_UNHOOK_FUNC(socket_write);\n            SW_UNHOOK_FUNC(socket_read);\n            SW_UNHOOK_FUNC(socket_send);\n            SW_UNHOOK_FUNC(socket_recv);\n            SW_UNHOOK_FUNC(socket_sendto);\n            SW_UNHOOK_FUNC(socket_recvfrom);\n            SW_UNHOOK_FUNC(socket_bind);\n            SW_UNHOOK_FUNC(socket_listen);\n            SW_UNHOOK_FUNC(socket_accept);\n            SW_UNHOOK_FUNC(socket_getpeername);\n            SW_UNHOOK_FUNC(socket_getsockname);\n            SW_UNHOOK_FUNC(socket_getopt);\n            SW_UNHOOK_FUNC(socket_get_option);\n            SW_UNHOOK_FUNC(socket_setopt);\n            SW_UNHOOK_FUNC(socket_set_option);\n            SW_UNHOOK_FUNC(socket_set_block);\n            SW_UNHOOK_FUNC(socket_set_nonblock);\n            SW_UNHOOK_FUNC(socket_shutdown);\n            SW_UNHOOK_FUNC(socket_close);\n            SW_UNHOOK_FUNC(socket_clear_error);\n            SW_UNHOOK_FUNC(socket_last_error);\n            SW_UNHOOK_FUNC(socket_import_stream);\n\n            detach_parent_class(\"Swoole\\\\Coroutine\\\\Socket\");\n        }\n    }\n\n#ifdef SW_USE_CURL\n    // curl native\n    if (flags & PHPCoroutine::HOOK_NATIVE_CURL) {\n        if (flags & PHPCoroutine::HOOK_CURL) {\n            php_swoole_fatal_error(E_WARNING, \"cannot enable both hooks HOOK_NATIVE_CURL and HOOK_CURL at same time\");\n            flags ^= PHPCoroutine::HOOK_CURL;\n        }\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_NATIVE_CURL)) {\n            SW_HOOK_WITH_NATIVE_FUNC(curl_close);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_copy_handle);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_errno);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_error);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_exec);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_getinfo);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_init);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_setopt);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_setopt_array);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_reset);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_pause);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_escape);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_unescape);\n\n            SW_HOOK_WITH_NATIVE_FUNC(curl_multi_init);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_multi_add_handle);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_multi_exec);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_multi_errno);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_multi_select);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_multi_setopt);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_multi_getcontent);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_multi_info_read);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_multi_remove_handle);\n            SW_HOOK_WITH_NATIVE_FUNC(curl_multi_close);\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_NATIVE_CURL) {\n            SW_UNHOOK_FUNC(curl_close);\n            SW_UNHOOK_FUNC(curl_copy_handle);\n            SW_UNHOOK_FUNC(curl_errno);\n            SW_UNHOOK_FUNC(curl_error);\n            SW_UNHOOK_FUNC(curl_exec);\n            SW_UNHOOK_FUNC(curl_getinfo);\n            SW_UNHOOK_FUNC(curl_init);\n            SW_UNHOOK_FUNC(curl_setopt);\n            SW_UNHOOK_FUNC(curl_setopt_array);\n            SW_UNHOOK_FUNC(curl_reset);\n            SW_UNHOOK_FUNC(curl_pause);\n            SW_UNHOOK_FUNC(curl_escape);\n            SW_UNHOOK_FUNC(curl_unescape);\n\n            SW_UNHOOK_FUNC(curl_multi_init);\n            SW_UNHOOK_FUNC(curl_multi_add_handle);\n            SW_UNHOOK_FUNC(curl_multi_exec);\n            SW_UNHOOK_FUNC(curl_multi_errno);\n            SW_UNHOOK_FUNC(curl_multi_select);\n            SW_UNHOOK_FUNC(curl_multi_setopt);\n            SW_UNHOOK_FUNC(curl_multi_getcontent);\n            SW_UNHOOK_FUNC(curl_multi_info_read);\n            SW_UNHOOK_FUNC(curl_multi_remove_handle);\n            SW_UNHOOK_FUNC(curl_multi_close);\n        }\n    }\n#endif\n    // curl\n    if (flags & PHPCoroutine::HOOK_CURL) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_CURL)) {\n            SW_HOOK_WITH_PHP_FUNC(curl_init);\n            SW_HOOK_WITH_PHP_FUNC(curl_setopt);\n            SW_HOOK_WITH_PHP_FUNC(curl_setopt_array);\n            SW_HOOK_WITH_PHP_FUNC(curl_exec);\n            SW_HOOK_WITH_PHP_FUNC(curl_getinfo);\n            SW_HOOK_WITH_PHP_FUNC(curl_errno);\n            SW_HOOK_WITH_PHP_FUNC(curl_error);\n            SW_HOOK_WITH_PHP_FUNC(curl_reset);\n            SW_HOOK_WITH_PHP_FUNC(curl_close);\n            SW_HOOK_WITH_PHP_FUNC(curl_multi_getcontent);\n\n            inherit_class(ZEND_STRL(\"Swoole\\\\Curl\\\\Handler\"), ZEND_STRL(\"CurlHandle\"));\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_CURL) {\n            SW_UNHOOK_FUNC(curl_init);\n            SW_UNHOOK_FUNC(curl_setopt);\n            SW_UNHOOK_FUNC(curl_setopt_array);\n            SW_UNHOOK_FUNC(curl_exec);\n            SW_UNHOOK_FUNC(curl_getinfo);\n            SW_UNHOOK_FUNC(curl_errno);\n            SW_UNHOOK_FUNC(curl_error);\n            SW_UNHOOK_FUNC(curl_reset);\n            SW_UNHOOK_FUNC(curl_close);\n            SW_UNHOOK_FUNC(curl_multi_getcontent);\n\n            detach_parent_class(\"Swoole\\\\Curl\\\\Handler\");\n        }\n    }\n    // network functions\n    if (flags & PHPCoroutine::HOOK_NET_FUNCTION) {\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_NET_FUNCTION)) {\n            hook_func(ZEND_STRL(\"gethostbyname\"), PHP_FN(swoole_coroutine_gethostbyname));\n            SW_HOOK_WITH_PHP_FUNC(gethostbynamel);\n            SW_HOOK_WITH_PHP_FUNC(mail);\n            SW_HOOK_WITH_PHP_FUNC(dns_check_record);\n            SW_HOOK_WITH_PHP_FUNC(checkdnsrr);\n            SW_HOOK_WITH_PHP_FUNC(dns_get_mx);\n            SW_HOOK_WITH_PHP_FUNC(getmxrr);\n            SW_HOOK_WITH_PHP_FUNC(dns_get_record);\n            SW_HOOK_WITH_PHP_FUNC(gethostbyaddr);\n        }\n    } else {\n        if (runtime_hook_flags & PHPCoroutine::HOOK_NET_FUNCTION) {\n            SW_UNHOOK_FUNC(gethostbyname);\n            SW_UNHOOK_FUNC(gethostbynamel);\n            SW_UNHOOK_FUNC(mail);\n            SW_UNHOOK_FUNC(dns_check_record);\n            SW_UNHOOK_FUNC(checkdnsrr);\n            SW_UNHOOK_FUNC(dns_get_mx);\n            SW_UNHOOK_FUNC(getmxrr);\n            SW_UNHOOK_FUNC(dns_get_record);\n            SW_UNHOOK_FUNC(gethostbyaddr);\n        }\n    }\n}\n\nstatic void hook_mongodb(uint32_t flags) {\n    if (flags & PHPCoroutine::HOOK_MONGODB) {\n        auto alias_name = R\"(MongoDB\\Client)\";\n        if (!(runtime_hook_flags & PHPCoroutine::HOOK_MONGODB) && !class_exists(alias_name)) {\n            zend::String class_name(R\"(Swoole\\MongoDB\\Client)\");\n            auto ce = zend_lookup_class_ex(class_name.get(), NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD);\n            if (ce) {\n                if (zend_register_class_alias(alias_name, ce) != SUCCESS) {\n                    zend_error(E_WARNING, \"Cannot declare class %s, because the name is already in use\", alias_name);\n                }\n            }\n        }\n    }\n}\n\nbool PHPCoroutine::enable_hook(uint32_t flags) {\n    if (!sw_is_main_thread() || sw_active_thread_count() > 1) {\n        swoole_warning(\"The runtime hook can only set on the main thread and no child threads have been created\");\n        return false;\n    }\n\n    if (swoole_isset_hook((swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK)) {\n        swoole_call_hook((swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK, &flags);\n    }\n\n    if (!extension_loaded(\"sockets\")) {\n        sw_unset_bit(flags, HOOK_SOCKETS);\n    }\n\n    if (!SWOOLE_G(enable_library)) {\n        sw_unset_bit(flags, HOOK_NET_FUNCTION);\n        sw_unset_bit(flags, HOOK_MONGODB);\n    }\n\n    hook_stream_factory(&flags);\n    hook_stream_ops(flags);\n    hook_pdo_driver(flags);\n    hook_all_func(flags);\n    hook_mongodb(flags);\n\n    if (swoole_isset_hook((swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_ENABLE_HOOK)) {\n        swoole_call_hook((swGlobalHookType) PHP_SWOOLE_HOOK_AFTER_ENABLE_HOOK, &flags);\n    }\n\n    runtime_hook_flags = flags;\n\n    return true;\n}\n\nbool PHPCoroutine::disable_hook() {\n    return enable_hook(0);\n}\n\nstatic PHP_METHOD(swoole_runtime, enableCoroutine) {\n    if (!SWOOLE_G(cli)) {\n        php_swoole_fatal_error(E_ERROR, \"must be used in PHP CLI mode\");\n        RETURN_FALSE;\n    }\n    zend_long flags = PHPCoroutine::HOOK_ALL;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(flags)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    PHPCoroutine::set_hook_flags(flags);\n    RETURN_BOOL(PHPCoroutine::enable_hook(flags));\n}\n\nstatic PHP_METHOD(swoole_runtime, getHookFlags) {\n    RETURN_LONG(PHPCoroutine::get_hook_flags());\n}\n\nstatic PHP_METHOD(swoole_runtime, setHookFlags) {\n    if (!SWOOLE_G(cli)) {\n        php_swoole_fatal_error(E_ERROR, \"must be used in PHP CLI mode\");\n        RETURN_FALSE;\n    }\n    zend_long flags = PHPCoroutine::HOOK_ALL;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(flags)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    PHPCoroutine::set_hook_flags(flags);\n    RETURN_BOOL(PHPCoroutine::enable_hook(flags));\n}\n\nstatic PHP_FUNCTION(swoole_sleep) {\n    zend_long num;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(num)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if (num < 0) {\n        zend_argument_value_error(1, \"must be greater than or equal to 0\");\n        RETURN_THROWS();\n    }\n\n    if (Coroutine::get_current()) {\n        RETURN_LONG(System::sleep((double) num) < 0 ? num : 0);\n    } else {\n        RETURN_LONG(php_sleep((unsigned int) num));\n    }\n}\n\nstatic PHP_FUNCTION(swoole_usleep) {\n    zend_long num;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(num)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (num < 0) {\n        zend_argument_value_error(1, \"must be greater than or equal to 0\");\n        RETURN_THROWS();\n    }\n\n    double sec = (double) num / 1000000;\n    if (Coroutine::get_current()) {\n        System::sleep(sec);\n    } else {\n        usleep((unsigned int) num);\n    }\n}\n\nstatic PHP_FUNCTION(swoole_time_nanosleep) {\n    zend_long tv_sec, tv_nsec;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_LONG(tv_sec)\n    Z_PARAM_LONG(tv_nsec)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (tv_sec < 0) {\n        zend_argument_value_error(1, \"must be greater than or equal to 0\");\n        RETURN_THROWS();\n    }\n    if (tv_nsec < 0) {\n        zend_argument_value_error(2, \"must be greater than or equal to 0\");\n        RETURN_THROWS();\n    }\n\n    double _time = (double) tv_sec + (double) tv_nsec / 1000000000.00;\n    if (Coroutine::get_current()) {\n        System::sleep(_time);\n    } else {\n        struct timespec php_req, php_rem;\n        php_req.tv_sec = (time_t) tv_sec;\n        php_req.tv_nsec = (long) tv_nsec;\n\n        if (nanosleep(&php_req, &php_rem) == 0) {\n            RETURN_TRUE;\n        } else if (errno == EINTR) {\n            array_init(return_value);\n            add_assoc_long_ex(return_value, \"seconds\", sizeof(\"seconds\") - 1, php_rem.tv_sec);\n            add_assoc_long_ex(return_value, \"nanoseconds\", sizeof(\"nanoseconds\") - 1, php_rem.tv_nsec);\n        } else if (errno == EINVAL) {\n            php_swoole_error(E_WARNING, \"nanoseconds was not in the range 0 to 999 999 999 or seconds was negative\");\n        }\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_FUNCTION(swoole_time_sleep_until) {\n    double target_secs;\n    const uint64_t ns_per_sec = 1000000000;\n    const double top_target_sec = (double) (UINT64_MAX / ns_per_sec);\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_DOUBLE(target_secs)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (UNEXPECTED(!(target_secs >= 0 && target_secs <= top_target_sec))) {\n        zend_argument_value_error(1, \"must be between 0 and %\" PRIu64, (uint64_t) top_target_sec);\n        RETURN_THROWS();\n    }\n\n    using namespace std::chrono;\n    using dseconds = duration<double>;\n\n    const auto now = system_clock::now();\n    const auto target_duration = duration_cast<system_clock::duration>(dseconds(target_secs));\n    const auto target_time = system_clock::time_point(target_duration);\n\n    if (target_time <= now) {\n        php_error_docref(NULL, E_WARNING, \"Argument #1 ($timestamp) must be greater than or equal to the current time\");\n        RETURN_FALSE;\n    }\n\n    const auto sleep_duration = target_time - now;\n    if (Coroutine::get_current()) {\n        const double sleep_seconds = duration_cast<dseconds>(sleep_duration).count();\n        System::sleep(sleep_seconds);\n    } else {\n        std::this_thread::sleep_until(target_time);\n    }\n    RETURN_TRUE;\n}\n\nstatic void stream_array_to_fd_set(zval *stream_array, std::unordered_map<int, PollSocket> &fds, int event) {\n    zval *elem;\n    zend_ulong index;\n    zend_string *key;\n\n    if (!ZVAL_IS_ARRAY(stream_array)) {\n        return;\n    }\n\n    ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(stream_array), index, key, elem) {\n        ZVAL_DEREF(elem);\n        php_socket_t sock = php_swoole_convert_to_fd(elem);\n        if (sock < 0) {\n            continue;\n        }\n        auto i = fds.find(sock);\n        if (i == fds.end()) {\n            fds.emplace(sock, PollSocket(event, new zend::KeyValue(index, key, elem)));\n        } else {\n            i->second.events |= event;\n        }\n    }\n    ZEND_HASH_FOREACH_END();\n}\n\nstatic int stream_array_emulate_read_fd_set(zval *stream_array) {\n    zval *elem, new_array;\n    php_stream *stream;\n    int ret = 0;\n    zend_ulong num_ind;\n    zend_string *key;\n\n    if (!ZVAL_IS_ARRAY(stream_array)) {\n        return 0;\n    }\n\n    array_init_size(&new_array, zend_hash_num_elements(Z_ARRVAL_P(stream_array)));\n    HashTable *ht = Z_ARRVAL(new_array);\n\n    ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(stream_array), num_ind, key, elem) {\n        ZVAL_DEREF(elem);\n        php_stream_from_zval_no_verify(stream, elem);\n        if (stream == nullptr) {\n            continue;\n        }\n        if ((stream->writepos - stream->readpos) > 0) {\n            /* allow readable non-descriptor based streams to participate in stream_select.\n             * Non-descriptor streams will only \"work\" if they have previously buffered the\n             * data.  Not ideal, but better than nothing.\n             * This branch of code also allows blocking streams with buffered data to\n             * operate correctly in stream_select.\n             * */\n            zval *dest_elem = !key ? zend_hash_index_update(ht, num_ind, elem) : zend_hash_update(ht, key, elem);\n            zval_add_ref(dest_elem);\n            ret++;\n            continue;\n        }\n    }\n    ZEND_HASH_FOREACH_END();\n\n    if (ret > 0) {\n        /* destroy old array and add new one */\n        zend_array_destroy(Z_ARR_P(stream_array));\n        ZVAL_ARR(stream_array, ht);\n    } else {\n        zend_array_destroy(ht);\n    }\n\n    return ret;\n}\n\nstatic PHP_FUNCTION(swoole_stream_select) {\n    Coroutine::get_current_safe();\n\n    zval *r_array, *w_array, *e_array;\n    zend_long sec, usec = 0;\n    zend_bool secnull;\n    bool usecnull = true;\n    int retval = 0;\n\n    ZEND_PARSE_PARAMETERS_START(4, 5)\n    Z_PARAM_ARRAY_EX2(r_array, 1, 1, 1)\n    Z_PARAM_ARRAY_EX2(w_array, 1, 1, 1)\n    Z_PARAM_ARRAY_EX2(e_array, 1, 1, 1)\n    Z_PARAM_LONG_OR_NULL(sec, secnull)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG_OR_NULL(usec, usecnull)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (secnull && !usecnull) {\n        if (usec != 0) {\n            zend_argument_value_error(5, \"must be null when argument #4 ($seconds) is null\");\n            RETURN_THROWS();\n        }\n    }\n\n    double timeout = -1;\n    if (!secnull) {\n        if (sec < 0) {\n            php_error_docref(nullptr, E_WARNING, \"The seconds parameter must be greater than 0\");\n            RETURN_FALSE;\n        } else if (usec < 0) {\n            php_error_docref(nullptr, E_WARNING, \"The microseconds parameter must be greater than 0\");\n            RETURN_FALSE;\n        }\n        timeout = (double) sec + ((double) usec / 1000000);\n    }\n\n    std::unordered_map<int, PollSocket> fds;\n\n    if (r_array != nullptr) {\n        stream_array_to_fd_set(r_array, fds, SW_EVENT_READ);\n    }\n\n    if (w_array != nullptr) {\n        stream_array_to_fd_set(w_array, fds, SW_EVENT_WRITE);\n    }\n\n    if (e_array != nullptr) {\n        stream_array_to_fd_set(e_array, fds, SW_EVENT_ERROR);\n    }\n\n    if (fds.empty()) {\n        php_error_docref(nullptr, E_WARNING, \"No stream arrays were passed\");\n        RETURN_FALSE;\n    }\n\n    ON_SCOPE_EXIT {\n        for (auto &i : fds) {\n            const auto *kv = static_cast<zend::KeyValue *>(i.second.ptr);\n            delete kv;\n        }\n    };\n\n    /* slight hack to support buffered data; if there is data sitting in the\n     * read buffer of the streams in the read array, let's pretend\n     * that we selected, but return only the readable sockets */\n    if (r_array != nullptr) {\n        retval = stream_array_emulate_read_fd_set(r_array);\n        if (retval > 0) {\n            if (w_array != nullptr) {\n                zend_hash_clean(Z_ARRVAL_P(w_array));\n            }\n            if (e_array != nullptr) {\n                zend_hash_clean(Z_ARRVAL_P(e_array));\n            }\n            RETURN_LONG(retval);\n        }\n    }\n\n    if (r_array != nullptr) {\n        zend_hash_clean(Z_ARRVAL_P(r_array));\n    }\n    if (w_array != nullptr) {\n        zend_hash_clean(Z_ARRVAL_P(w_array));\n    }\n    if (e_array != nullptr) {\n        zend_hash_clean(Z_ARRVAL_P(e_array));\n    }\n\n    /**\n     * timeout or add failed\n     */\n    if (!System::socket_poll(fds, timeout)) {\n        RETURN_LONG(0);\n    }\n\n    for (auto &i : fds) {\n        auto *kv = static_cast<zend::KeyValue *>(i.second.ptr);\n        int revents = i.second.revents;\n        SW_ASSERT((revents & (~(SW_EVENT_READ | SW_EVENT_WRITE | SW_EVENT_ERROR))) == 0);\n        if (revents > 0) {\n            if ((revents & SW_EVENT_READ) && r_array) {\n                kv->add_to(r_array);\n            }\n            if ((revents & SW_EVENT_WRITE) && w_array) {\n                kv->add_to(w_array);\n            }\n            if ((revents & SW_EVENT_ERROR) && e_array) {\n                kv->add_to(e_array);\n            }\n            retval++;\n        }\n    }\n\n    RETURN_LONG(retval);\n}\n\nstatic void hook_func(const char *name, size_t l_name, zif_handler handler, zend_internal_arg_info *arg_info) {\n    auto *rf = static_cast<PhpFunc *>(zend_hash_str_find_ptr(hook_function_table, name, l_name));\n    bool use_php_func = false;\n    /**\n     * use php library function\n     */\n    if (handler == nullptr) {\n        handler = PHP_FN(swoole_user_func_handler);\n        use_php_func = true;\n    }\n\n    if (rf) {\n        rf->function->internal_function.handler = handler;\n        if (arg_info) {\n            rf->function->internal_function.arg_info = arg_info;\n        }\n        return;\n    }\n\n    auto *zf = zend::get_function(name, l_name);\n    if (zf == nullptr) {\n        swoole_warning(\"The function named `%s` is not found\", name);\n        return;\n    }\n\n    auto fn_str = zf->common.function_name;\n    rf = static_cast<PhpFunc *>(emalloc(sizeof(PhpFunc)));\n    sw_memset_zero(rf, sizeof(*rf));\n    rf->function = zf;\n\n    auto fn_name = std::string(fn_str->val, fn_str->len);\n\n    if (!ori_func_arg_infos.exists(fn_name)) {\n        ori_func_handlers.set(fn_name, zf->internal_function.handler);\n        ori_func_arg_infos.set(fn_name, zf->internal_function.arg_info);\n    }\n    rf->ori_handler = ori_func_handlers.get(fn_name);\n    rf->ori_arg_info = ori_func_arg_infos.get(fn_name);\n\n    zf->internal_function.handler = handler;\n    if (arg_info) {\n        zf->internal_function.arg_info = copy_arginfo(&zf->internal_function, arg_info);\n        rf->arg_info_copy = zf->internal_function.arg_info;\n    }\n\n    if (use_php_func) {\n        char func[128];\n        memcpy(func, ZEND_STRL(\"swoole_\"));\n        memcpy(func + 7, fn_str->val, fn_str->len);\n\n        ZVAL_STRINGL(&rf->name, func, fn_str->len + 7);\n        rf->fci_cache = sw_callable_create(&rf->name);\n    }\n\n    zend_hash_add_ptr(hook_function_table, fn_str, rf);\n}\n\nstatic void unhook_func(const char *name, size_t l_name) {\n    auto *rf = static_cast<PhpFunc *>(zend_hash_str_find_ptr(hook_function_table, name, l_name));\n    if (rf == nullptr) {\n        return;\n    }\n    rf->function->internal_function.handler = rf->ori_handler;\n    rf->function->internal_function.arg_info = rf->ori_arg_info;\n}\n\nphp_stream *php_swoole_create_stream_from_socket(php_socket_t _fd, int domain, int type, int protocol STREAMS_DC) {\n    auto *abstract = new NetStream();\n    abstract->socket = std::make_shared<SocketImpl>(_fd, domain, type, protocol);\n    if (FG(default_socket_timeout) > 0) {\n        abstract->socket->set_timeout((double) FG(default_socket_timeout));\n    }\n    abstract->stream.timeout.tv_sec = FG(default_socket_timeout);\n    abstract->stream.socket = abstract->socket->get_fd();\n    abstract->blocking = true;\n\n    php_stream *stream = php_stream_alloc_rel(&socket_ops, abstract, nullptr, \"r+\");\n\n    if (stream == nullptr) {\n        delete abstract;\n    } else {\n        stream->flags |= PHP_STREAM_FLAG_AVOID_BLOCKING;\n    }\n\n    return stream;\n}\n\nzend_string *php_async_socket_error_str(long err) {\n    const char *errstr = swoole_strerror(err);\n    return zend_string_init(errstr, strlen(errstr), 0);\n}\n\nint php_async_socket_connect_to_host(const char *host,\n                                     unsigned short port,\n                                     int socktype,\n                                     int asynchronous,\n                                     struct timeval *timeout,\n                                     zend_string **error_string,\n                                     int *error_code,\n                                     const char *bindto,\n                                     unsigned short bindport,\n                                     long sockopts) {\n    if (!swoole_coroutine_is_in()) {\n        php_swoole_fatal_error(E_WARNING, \"This API must be called in coroutine\");\n        return -1;\n    }\n\n    auto domain = swoole::network::Address::verify_ip(AF_INET6, host) ? AF_INET6 : AF_INET;\n    auto sock = swoole_coroutine_socket(domain, SOCK_STREAM, 0);\n    if (sock < 0) {\n        return -1;\n    }\n\n    auto sockobj = swoole_coroutine_get_socket_object(sock);\n    if (bindto) {\n        if (!sockobj->bind(bindto, bindport)) {\n            php_swoole_fatal_error(E_WARNING,\n                                   \"Failed to bind to '%s:%d', system said: %s\",\n                                   bindto,\n                                   bindport,\n                                   swoole_strerror(sockobj->errCode));\n        }\n    }\n\n#ifdef SO_BROADCAST\n    if (sockopts & STREAM_SOCKOP_SO_BROADCAST) {\n        sockobj->set_option(SOL_SOCKET, SO_BROADCAST, 1);\n    }\n#endif\n\n#ifdef TCP_NODELAY\n    if (sockopts & STREAM_SOCKOP_TCP_NODELAY) {\n        sockobj->set_option(IPPROTO_TCP, TCP_NODELAY, 1);\n    }\n#endif\n\n    if (timeout) {\n        sockobj->set_timeout(timeout, SW_TIMEOUT_ALL);\n    }\n\n    if (!sockobj->connect(host, port)) {\n        if (error_code) {\n            *error_code = sockobj->errCode;\n        }\n        if (error_string) {\n            *error_string = php_async_socket_error_str(sockobj->errCode);\n        }\n        if (sockobj->errCode == SW_ERROR_DNSLOOKUP_RESOLVE_FAILED) {\n            php_swoole_fatal_error(E_WARNING, \"getaddrinfo for '%s' failed, error: %s\", host, sockobj->get_err());\n        }\n        swoole_coroutine_close(sock);\n        return -1;\n    }\n\n    return sock;\n}\n\nint php_async_socket_poll(php_socket_t fd, int events, int timeout) {\n    auto sockobj = swoole_coroutine_get_socket_object(fd);\n    if (!sockobj) {\n        return -1;\n    }\n    return sockobj->poll(events & POLLOUT ? SW_EVENT_WRITE : SW_EVENT_READ, timeout / 1000.0);\n}\n\nphp_stream *php_swoole_create_stream_from_pipe(int fd, const char *mode, const char *persistent_id STREAMS_DC) {\n    return _sw_php_stream_fopen_from_fd(fd, mode, persistent_id, false STREAMS_CC);\n}\n\nphp_stream_ops *php_swoole_get_ori_php_stream_stdio_ops() {\n    return &ori_php_stream_stdio_ops;\n}\n\nzif_handler php_swoole_get_original_handler(const char *name, size_t len) {\n    zif_handler handler = ori_func_handlers.get(std::string(name, len));\n    if (handler) {\n        return handler;\n    }\n    auto *zf = zend::get_function(name, len);\n    if (zf && zf->type == ZEND_INTERNAL_FUNCTION && zf->internal_function.handler) {\n        return zf->internal_function.handler;\n    }\n    return nullptr;\n}\n\nstatic PHP_FUNCTION(swoole_stream_socket_pair) {\n    zend_long domain, type, protocol;\n    php_socket_t pair[2];\n\n    ZEND_PARSE_PARAMETERS_START(3, 3)\n    Z_PARAM_LONG(domain)\n    Z_PARAM_LONG(type)\n    Z_PARAM_LONG(protocol)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (0 != socketpair((int) domain, (int) type, (int) protocol, pair)) {\n        php_swoole_error(E_WARNING, \"failed to create sockets: [%d]: %s\", errno, strerror(errno));\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n\n    php_swoole_check_reactor();\n\n    php_stream *s1 = php_swoole_create_stream_from_socket(pair[0], domain, type, protocol STREAMS_CC);\n    php_stream *s2 = php_swoole_create_stream_from_socket(pair[1], domain, type, protocol STREAMS_CC);\n\n    /* set the __exposed flag.\n     * php_stream_to_zval() does, add_next_index_resource() does not */\n    php_stream_auto_cleanup(s1);\n    php_stream_auto_cleanup(s2);\n\n    add_next_index_resource(return_value, s1->res);\n    add_next_index_resource(return_value, s2->res);\n}\n\nstatic PHP_FUNCTION(swoole_user_func_handler) {\n    auto fn_str = execute_data->func->common.function_name;\n    if (!swoole_coroutine_is_in()) {\n        auto ori_handler = ori_func_handlers.get(std::string(fn_str->val, fn_str->len));\n        ori_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n        return;\n    }\n\n    auto *rf = static_cast<PhpFunc *>(zend_hash_find_ptr(hook_function_table, fn_str));\n    if (!rf || !rf->fci_cache) {\n        zend_throw_exception_ex(swoole_exception_ce, SW_ERROR_UNDEFINED_BEHAVIOR, \"%s func not exists\", fn_str->val);\n        return;\n    }\n\n    zend_fcall_info fci;\n    fci.size = sizeof(fci);\n    fci.object = nullptr;\n    fci.retval = return_value;\n    fci.param_count = ZEND_NUM_ARGS();\n    fci.params = ZEND_CALL_ARG(execute_data, 1);\n    fci.named_params = nullptr;\n    ZVAL_UNDEF(&fci.function_name);\n    zend_call_function(&fci, rf->fci_cache->ptr());\n}\n\nzend_class_entry *find_class_entry(const char *name, size_t length) {\n    zend_string *search_key = zend_string_init(name, length, false);\n    zend_class_entry *class_ce = zend_lookup_class(search_key);\n    zend_string_release(search_key);\n    return class_ce ? class_ce : nullptr;\n}\n\nstatic void inherit_class(const char *child_name, size_t child_length, const char *parent_name, size_t parent_length) {\n    zend_class_entry *temp_ce = nullptr;\n    zend_class_entry *child_ce = find_class_entry(child_name, child_length);\n    zend_class_entry *parent_ce = find_class_entry(parent_name, parent_length);\n\n    if (!child_ce || !parent_ce || instanceof_function(child_ce, parent_ce)) {\n        return;\n    }\n\n    temp_ce = child_ce;\n    while (temp_ce->parent) {\n        temp_ce = temp_ce->parent;\n    }\n    temp_ce->parent = parent_ce;\n\n    std::string key(ZSTR_VAL(child_ce->name));\n    child_class_entries.insert({key, child_ce});\n}\n\nvoid start_detach_parent_class(zend_class_entry *class_ce) {\n    zend_class_entry *p1 = nullptr;\n    zend_class_entry *p2 = nullptr;\n\n    p1 = class_ce;\n    p2 = class_ce->parent;\n    while (p2->parent) {\n        p1 = p1->parent;\n        p2 = p2->parent;\n    }\n\n    p1->parent = nullptr;\n}\n\nstatic void detach_parent_class(const char *child_name) {\n    std::string search_key(child_name);\n    auto iter = child_class_entries.find(search_key);\n    if (iter == child_class_entries.end()) {\n        return;\n    }\n    start_detach_parent_class(iter->second);\n    child_class_entries.erase(search_key);\n}\n\nstatic void clear_class_entries() {\n    for (const auto &child_class_entry : child_class_entries) {\n        start_detach_parent_class(child_class_entry.second);\n    }\n    child_class_entries.clear();\n}\n\n#if defined(HAVE_PUTENV) && defined(SW_THREAD)\n/* {{{ Set the value of an environment variable */\nstatic PHP_FUNCTION(swoole_putenv) {\n    char *setting;\n    size_t setting_len;\n    char *p;\n    bool result;\n    std::string key;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STRING(setting, setting_len)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if (setting_len == 0 || setting[0] == '=') {\n        zend_argument_value_error(1, \"must have a valid syntax\");\n        RETURN_THROWS();\n    }\n\n    if ((p = strchr(setting, '='))) {\n        key = std::string(setting, p - setting);\n    } else {\n        key = std::string(setting, setting_len);\n    }\n\n    tsrm_env_lock();\n    swoole_runtime_environ[key] = std::string(setting, setting_len);\n    auto iter = swoole_runtime_environ.find(key);\n\n#ifdef HAVE_UNSETENV\n    if (!p) { /* no '=' means we want to unset it */\n        unsetenv(iter->second.c_str());\n    }\n    if (!p || putenv((char *) iter->second.c_str()) == 0) { /* success */\n#else\n    if (putenv((char *) iter->second.c_str()) == 0) { /* success */\n#endif\n\n#ifdef HAVE_TZSET\n        if (zend_binary_strcasecmp(key.c_str(), key.length(), ZEND_STRL(\"TZ\")) == 0) {\n            tzset();\n        }\n#endif\n        result = true;\n    } else {\n        result = false;\n    }\n\n    tsrm_env_unlock();\n    RETURN_BOOL(result);\n}\n/* }}} */\n#endif\n"
  },
  {
    "path": "ext-src/swoole_server.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_server.h\"\n#include \"php_swoole_http_server.h\"\n#include \"php_swoole_process.h\"\n#include \"php_swoole_thread.h\"\n#include \"php_swoole_call_stack.h\"\n#include \"swoole_msg_queue.h\"\n#include \"swoole_coroutine_system.h\"\n\n#include \"ext/standard/php_var.h\"\n#include \"zend_smart_str.h\"\n\nBEGIN_EXTERN_C()\n#include \"ext/json/php_json.h\"\n#include \"stubs/php_swoole_server_arginfo.h\"\nEND_EXTERN_C()\n\nusing namespace swoole;\n\nstruct ConnectionIterator {\n    int current_fd;\n    SessionId session_id;\n    Server *serv;\n    ListenPort *port;\n    int index;\n};\n\nstruct ServerEvent {\n    enum php_swoole_server_callback_type type;\n    std::string name;\n    ServerEvent(enum php_swoole_server_callback_type type, std::string &&name) : type(type), name(name) {}\n};\n\n// clang-format off\nstatic std::unordered_map<std::string, ServerEvent> server_event_map({\n    { \"start\",          ServerEvent(SW_SERVER_CB_onStart,           \"Start\") },\n    { \"beforeshutdown\", ServerEvent(SW_SERVER_CB_onBeforeShutdown,  \"BeforeShutdown\") },\n    { \"shutdown\",       ServerEvent(SW_SERVER_CB_onShutdown,        \"Shutdown\") },\n    { \"workerstart\",    ServerEvent(SW_SERVER_CB_onWorkerStart,     \"WorkerStart\") },\n    { \"workerstop\",     ServerEvent(SW_SERVER_CB_onWorkerStop,      \"WorkerStop\") },\n    { \"beforereload\",   ServerEvent(SW_SERVER_CB_onBeforeReload,    \"BeforeReload\") },\n    { \"afterreload\",    ServerEvent(SW_SERVER_CB_onAfterReload,     \"AfterReload\") },\n    { \"task\",           ServerEvent(SW_SERVER_CB_onTask,            \"Task\") },\n    { \"finish\",         ServerEvent(SW_SERVER_CB_onFinish,          \"Finish\") },\n    { \"workerexit\",     ServerEvent(SW_SERVER_CB_onWorkerExit,      \"WorkerExit\") },\n    { \"workererror\",    ServerEvent(SW_SERVER_CB_onWorkerError,     \"WorkerError\") },\n    { \"managerstart\",   ServerEvent(SW_SERVER_CB_onManagerStart,    \"ManagerStart\") },\n    { \"managerstop\",    ServerEvent(SW_SERVER_CB_onManagerStop,     \"ManagerStop\") },\n    { \"pipemessage\",    ServerEvent(SW_SERVER_CB_onPipeMessage,     \"PipeMessage\") },\n});\n// clang-format on\n\n// server event callback\nstatic void php_swoole_server_onStart(Server *);\nstatic void php_swoole_server_onBeforeShutdown(Server *serv);\nstatic void php_swoole_server_onShutdown(Server *);\nstatic void php_swoole_server_onWorkerStart(Server *, Worker *worker);\nstatic void php_swoole_server_onBeforeReload(Server *serv);\nstatic void php_swoole_server_onAfterReload(Server *serv);\nstatic void php_swoole_server_onWorkerStop(Server *, Worker *worker);\nstatic void php_swoole_server_onWorkerExit(Server *serv, Worker *worker);\nstatic void php_swoole_server_onUserWorkerStart(Server *serv, Worker *worker);\nstatic int php_swoole_server_onTask(Server *, EventData *req);\nstatic int php_swoole_server_onFinish(Server *, EventData *req);\nstatic void php_swoole_server_onPipeMessage(Server *serv, EventData *req);\nstatic void php_swoole_server_onWorkerError(Server *serv, Worker *worker, const ExitStatus &exit_status);\nstatic void php_swoole_server_onManagerStart(Server *serv);\nstatic void php_swoole_server_onManagerStop(Server *serv);\n\nstatic bool php_swoole_server_task_finish(Server *serv, zval *zdata, EventData *current_task);\nstatic TaskId php_swoole_server_task_pack(zval *data, EventData *task);\nstatic bool php_swoole_server_task_unpack(zval *zresult, EventData *task_result);\nstatic int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendData *data);\nstatic zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort *port);\n\nvoid php_swoole_server_rshutdown() {\n    if (!sw_server() || !sw_worker()) {\n        return;\n    }\n\n    Server *serv = sw_server();\n    Worker *worker = sw_worker();\n    serv->drain_worker_pipe();\n\n    if (serv->is_started() && worker->is_running() && !serv->is_user_worker()) {\n        worker->shutdown();\n        if (serv->is_event_worker()) {\n            serv->clean_worker_connections(worker);\n        }\n        if (php_swoole_is_fatal_error()) {\n            swoole_error_log(SW_LOG_ERROR,\n                             SW_ERROR_PHP_FATAL_ERROR,\n                             \"Fatal error: %s in %s on line %d\",\n                             php_swoole_get_last_error_message(),\n                             php_swoole_get_last_error_file(),\n                             PG(last_error_lineno));\n        } else {\n            swoole_error_log(\n                SW_LOG_NOTICE, SW_ERROR_SERVER_WORKER_TERMINATED, \"worker process is terminated by exit()/die()\");\n        }\n    }\n}\n\nzend_class_entry *swoole_server_ce;\nzend_object_handlers swoole_server_handlers;\n\nzend_class_entry *swoole_connection_iterator_ce;\nstatic zend_object_handlers swoole_connection_iterator_handlers;\n\nstatic zend_class_entry *swoole_server_task_ce;\nstatic zend_object_handlers swoole_server_task_handlers;\n\nstatic zend_class_entry *swoole_server_event_ce;\nstatic zend_object_handlers swoole_server_event_handlers;\n\nstatic zend_class_entry *swoole_server_packet_ce;\nstatic zend_object_handlers swoole_server_packet_handlers;\n\nstatic zend_class_entry *swoole_server_pipe_message_ce;\nstatic zend_object_handlers swoole_server_pipe_message_handlers;\n\nstatic zend_class_entry *swoole_server_status_info_ce;\nstatic zend_object_handlers swoole_server_status_info_handlers;\n\nstatic zend_class_entry *swoole_server_task_result_ce;\nstatic zend_object_handlers swoole_server_task_result_handlers;\n\nstatic SW_THREAD_LOCAL zval swoole_server_instance;\n#ifdef SW_THREAD\nstatic SW_THREAD_LOCAL WorkerFn worker_thread_fn;\nstatic SW_THREAD_LOCAL std::vector<ServerPortProperty *> swoole_server_port_properties;\n#endif\n\nstatic sw_inline ServerObject *server_fetch_object(zend_object *obj) {\n    return (ServerObject *) ((char *) obj - swoole_server_handlers.offset);\n}\n\nstatic sw_inline Server *server_get_ptr(zval *zobject) {\n    return server_fetch_object(Z_OBJ_P(zobject))->serv;\n}\n\nServer *php_swoole_server_get_and_check_server(zval *zobject) {\n    Server *serv = server_get_ptr(zobject);\n    if (UNEXPECTED(!serv)) {\n        php_swoole_fatal_error(E_ERROR, \"Invalid instance of %s\", SW_Z_OBJCE_NAME_VAL_P(zobject));\n    }\n    return serv;\n}\n\nzval *php_swoole_server_zval_ptr(Server *serv) {\n    return &swoole_server_instance;\n}\n\nServerPortProperty *php_swoole_server_get_port_property(ListenPort *port) {\n#ifdef SW_THREAD\n    return swoole_server_port_properties.at(port->object_id);\n#else\n    return (ServerPortProperty *) port->ptr;\n#endif\n}\n\nvoid php_swoole_server_set_port_property(ListenPort *port, ServerPortProperty *property) {\n#ifdef SW_THREAD\n    if (swoole_server_port_properties.size() < (size_t) port->object_id + 1) {\n        swoole_server_port_properties.resize((size_t) port->object_id + 1);\n    }\n    swoole_server_port_properties[port->object_id] = property;\n#else\n    port->ptr = property;\n#endif\n}\n\nServerObject *php_swoole_server_get_zend_object(Server *serv) {\n    return server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv)));\n}\n\nbool php_swoole_server_isset_callback(Server *serv, ListenPort *port, int event_type) {\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv)));\n    return server_object->isset_callback(port, event_type);\n}\n\nstatic sw_inline void server_set_ptr(zval *zobject, Server *serv) {\n    server_fetch_object(Z_OBJ_P(zobject))->serv = serv;\n}\n\nstatic void server_free_object(zend_object *object) {\n    ServerObject *server_object = server_fetch_object(object);\n    ServerProperty *property = server_object->property;\n    Server *serv = server_object->serv;\n\n    if (serv) {\n        if (serv->private_data_3) {\n            sw_callable_free(serv->private_data_3);\n        }\n        for (auto &callback : property->callbacks) {\n            auto fci_cache = callback;\n            if (fci_cache) {\n                sw_callable_free(fci_cache);\n                callback = nullptr;\n            }\n        }\n        for (auto &user_processe : property->user_processes) {\n            sw_zval_free(user_processe);\n        }\n        for (auto zport : property->ports) {\n            php_swoole_server_port_deref(Z_OBJ_P(zport));\n            efree(zport);\n        }\n        server_object->serv = nullptr;\n    }\n\n    for (auto fci_cache : property->command_callbacks) {\n        sw_callable_free(fci_cache);\n    }\n\n    delete property;\n\n    zend_object_std_dtor(object);\n    if (serv && serv->is_master()) {\n#ifdef SW_THREAD\n        if (serv->is_thread_mode()) {\n            zend_string_release((zend_string *) serv->private_data_4);\n        }\n#endif\n        delete serv;\n    }\n}\n\nstatic zend_object *server_create_object(zend_class_entry *ce) {\n    auto *server_object = static_cast<ServerObject *>(zend_object_alloc(sizeof(ServerObject), ce));\n    zend_object_std_init(&server_object->std, ce);\n    object_properties_init(&server_object->std, ce);\n    server_object->std.handlers = &swoole_server_handlers;\n    server_object->property = new ServerProperty();\n    return &server_object->std;\n}\n\nstruct ConnectionIteratorObject {\n    ConnectionIterator iterator;\n    zend_object std;\n};\n\nstatic sw_inline ConnectionIteratorObject *php_swoole_connection_iterator_fetch_object(zend_object *obj) {\n    return (ConnectionIteratorObject *) ((char *) obj - swoole_connection_iterator_handlers.offset);\n}\n\nstatic sw_inline ConnectionIterator *php_swoole_connection_iterator_get_ptr(zval *zobject) {\n    return &php_swoole_connection_iterator_fetch_object(Z_OBJ_P(zobject))->iterator;\n}\n\nConnectionIterator *php_swoole_connection_iterator_get_and_check_ptr(zval *zobject) {\n    ConnectionIterator *iterator = php_swoole_connection_iterator_get_ptr(zobject);\n    if (UNEXPECTED(!iterator->serv)) {\n        php_swoole_fatal_error(E_ERROR, \"Invalid instance of %s\", SW_Z_OBJCE_NAME_VAL_P(zobject));\n    }\n    return iterator;\n}\n\nstatic void php_swoole_connection_iterator_free_object(zend_object *object) {\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *php_swoole_connection_iterator_create_object(zend_class_entry *ce) {\n    auto *connection = static_cast<ConnectionIteratorObject *>(zend_object_alloc(sizeof(ConnectionIteratorObject), ce));\n    zend_object_std_init(&connection->std, ce);\n    object_properties_init(&connection->std, ce);\n    connection->std.handlers = &swoole_connection_iterator_handlers;\n    return &connection->std;\n}\n\nstruct ServerTaskObject {\n    Server *serv;\n    DataHead info;\n    zend_object std;\n};\n\nstatic sw_inline ServerTaskObject *php_swoole_server_task_fetch_object(zend_object *obj) {\n    return (ServerTaskObject *) ((char *) obj - swoole_server_task_handlers.offset);\n}\n\nstatic sw_inline Server *php_swoole_server_task_get_server(zval *zobject) {\n    Server *serv = php_swoole_server_task_fetch_object(Z_OBJ_P(zobject))->serv;\n    if (!serv) {\n        php_swoole_fatal_error(E_ERROR, \"Invalid instance of %s\", SW_Z_OBJCE_NAME_VAL_P(zobject));\n    }\n    return serv;\n}\n\nstatic sw_inline void php_swoole_server_task_set_server(zval *zobject, Server *serv) {\n    php_swoole_server_task_fetch_object(Z_OBJ_P(zobject))->serv = serv;\n}\n\nstatic sw_inline DataHead *php_swoole_server_task_get_info(zval *zobject) {\n    ServerTaskObject *task = php_swoole_server_task_fetch_object(Z_OBJ_P(zobject));\n    if (!task->serv) {\n        php_swoole_fatal_error(E_ERROR, \"Invalid instance of %s\", SW_Z_OBJCE_NAME_VAL_P(zobject));\n    }\n    return &task->info;\n}\n\nstatic sw_inline void php_swoole_server_task_set_info(zval *zobject, DataHead *info) {\n    php_swoole_server_task_fetch_object(Z_OBJ_P(zobject))->info = *info;\n}\n\nstatic void php_swoole_server_task_free_object(zend_object *object) {\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *php_swoole_server_task_create_object(zend_class_entry *ce) {\n    auto *server_task = static_cast<ServerTaskObject *>(zend_object_alloc(sizeof(ServerTaskObject), ce));\n    zend_object_std_init(&server_task->std, ce);\n    object_properties_init(&server_task->std, ce);\n    server_task->std.handlers = &swoole_server_task_handlers;\n    return &server_task->std;\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_server, __construct);\nstatic PHP_METHOD(swoole_server, __destruct);\nstatic PHP_METHOD(swoole_server, set);\nstatic PHP_METHOD(swoole_server, on);\nstatic PHP_METHOD(swoole_server, getCallback);\nstatic PHP_METHOD(swoole_server, listen);\nstatic PHP_METHOD(swoole_server, sendMessage);\nstatic PHP_METHOD(swoole_server, addProcess);\nstatic PHP_METHOD(swoole_server, addCommand);\nstatic PHP_METHOD(swoole_server, start);\nstatic PHP_METHOD(swoole_server, stop);\nstatic PHP_METHOD(swoole_server, send);\nstatic PHP_METHOD(swoole_server, sendfile);\nstatic PHP_METHOD(swoole_server, stats);\nstatic PHP_METHOD(swoole_server, bind);\nstatic PHP_METHOD(swoole_server, sendto);\nstatic PHP_METHOD(swoole_server, sendwait);\nstatic PHP_METHOD(swoole_server, exists);\nstatic PHP_METHOD(swoole_server, protect);\nstatic PHP_METHOD(swoole_server, close);\nstatic PHP_METHOD(swoole_server, pause);\nstatic PHP_METHOD(swoole_server, resume);\nstatic PHP_METHOD(swoole_server, task);\nstatic PHP_METHOD(swoole_server, taskwait);\nstatic PHP_METHOD(swoole_server, taskWaitMulti);\nstatic PHP_METHOD(swoole_server, taskCo);\nstatic PHP_METHOD(swoole_server, finish);\nstatic PHP_METHOD(swoole_server, reload);\nstatic PHP_METHOD(swoole_server, shutdown);\nstatic PHP_METHOD(swoole_server, heartbeat);\nstatic PHP_METHOD(swoole_server, command);\nstatic PHP_METHOD(swoole_server, getClientList);\nstatic PHP_METHOD(swoole_server, getClientInfo);\nstatic PHP_METHOD(swoole_server, getWorkerId);\nstatic PHP_METHOD(swoole_server, getWorkerPid);\nstatic PHP_METHOD(swoole_server, getWorkerStatus);\nstatic PHP_METHOD(swoole_server, getManagerPid);\nstatic PHP_METHOD(swoole_server, getMasterPid);\n#ifdef SWOOLE_SOCKETS_SUPPORT\nstatic PHP_METHOD(swoole_server, getSocket);\n#endif\n\n/**\n * Server\\Connection\n */\nstatic PHP_METHOD(swoole_connection_iterator, count);\nstatic PHP_METHOD(swoole_connection_iterator, rewind);\nstatic PHP_METHOD(swoole_connection_iterator, next);\nstatic PHP_METHOD(swoole_connection_iterator, current);\nstatic PHP_METHOD(swoole_connection_iterator, key);\nstatic PHP_METHOD(swoole_connection_iterator, valid);\nstatic PHP_METHOD(swoole_connection_iterator, offsetExists);\nstatic PHP_METHOD(swoole_connection_iterator, offsetGet);\nstatic PHP_METHOD(swoole_connection_iterator, offsetSet);\nstatic PHP_METHOD(swoole_connection_iterator, offsetUnset);\nstatic PHP_METHOD(swoole_connection_iterator, __construct);\nstatic PHP_METHOD(swoole_connection_iterator, __destruct);\n\n/**\n * Server\\Task\n */\nstatic PHP_METHOD(swoole_server_task, finish);\nstatic PHP_METHOD(swoole_server_task, pack);\nstatic PHP_METHOD(swoole_server_task, unpack);\nSW_EXTERN_C_END\n\n// clang-format off\n\nstatic zend_function_entry swoole_server_methods[] = {\n    PHP_ME(swoole_server, __construct, arginfo_class_Swoole_Server___construct, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, __destruct, arginfo_class_Swoole_Server___destruct, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, listen, arginfo_class_Swoole_Server_listen, ZEND_ACC_PUBLIC)\n    PHP_MALIAS(swoole_server, addlistener, listen, arginfo_class_Swoole_Server_listen, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, on, arginfo_class_Swoole_Server_on, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, getCallback, arginfo_class_Swoole_Server_getCallback, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, set, arginfo_class_Swoole_Server_set, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, start, arginfo_class_Swoole_Server_start, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, send, arginfo_class_Swoole_Server_send, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, sendto, arginfo_class_Swoole_Server_sendto, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, sendwait, arginfo_class_Swoole_Server_sendwait, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, exists, arginfo_class_Swoole_Server_exists, ZEND_ACC_PUBLIC)\n    PHP_MALIAS(swoole_server, exist, exists, arginfo_class_Swoole_Server_exists, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, protect, arginfo_class_Swoole_Server_protect, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, sendfile, arginfo_class_Swoole_Server_sendfile, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, close, arginfo_class_Swoole_Server_close, ZEND_ACC_PUBLIC)\n    PHP_MALIAS(swoole_server, confirm, resume, arginfo_class_Swoole_Server_resume, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, pause, arginfo_class_Swoole_Server_pause, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, resume, arginfo_class_Swoole_Server_resume, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, task, arginfo_class_Swoole_Server_task, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, taskwait, arginfo_class_Swoole_Server_taskwait, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, taskWaitMulti, arginfo_class_Swoole_Server_taskWaitMulti, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, taskCo, arginfo_class_Swoole_Server_taskCo, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, finish, arginfo_class_Swoole_Server_finish, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, reload, arginfo_class_Swoole_Server_reload, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, shutdown, arginfo_class_Swoole_Server_shutdown, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, stop, arginfo_class_Swoole_Server_stop, ZEND_ACC_PUBLIC)\n    PHP_FALIAS(getLastError, swoole_last_error, arginfo_class_Swoole_Server_getLastError)\n    PHP_ME(swoole_server, heartbeat, arginfo_class_Swoole_Server_heartbeat, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, getClientInfo, arginfo_class_Swoole_Server_getClientInfo, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, getClientList, arginfo_class_Swoole_Server_getClientList, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, getWorkerId, arginfo_class_Swoole_Server_getWorkerId, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, getWorkerPid, arginfo_class_Swoole_Server_getWorkerPid, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, getWorkerStatus, arginfo_class_Swoole_Server_getWorkerStatus, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, getManagerPid, arginfo_class_Swoole_Server_getManagerPid, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, getMasterPid, arginfo_class_Swoole_Server_getMasterPid, ZEND_ACC_PUBLIC)\n    //psr-0 style\n    PHP_MALIAS(swoole_server, connection_info, getClientInfo, arginfo_class_Swoole_Server_getClientInfo, ZEND_ACC_PUBLIC)\n    PHP_MALIAS(swoole_server, connection_list, getClientList, arginfo_class_Swoole_Server_getClientList, ZEND_ACC_PUBLIC)\n    //process\n    PHP_ME(swoole_server, sendMessage, arginfo_class_Swoole_Server_sendMessage, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, command, arginfo_class_Swoole_Server_command, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, addCommand, arginfo_class_Swoole_Server_addCommand, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, addProcess, arginfo_class_Swoole_Server_addProcess, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server, stats, arginfo_class_Swoole_Server_stats, ZEND_ACC_PUBLIC)\n#ifdef SWOOLE_SOCKETS_SUPPORT\n    PHP_ME(swoole_server, getSocket, arginfo_class_Swoole_Server_getSocket, ZEND_ACC_PUBLIC)\n#endif\n    PHP_ME(swoole_server, bind, arginfo_class_Swoole_Server_bind, ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n\nstatic const zend_function_entry swoole_connection_iterator_methods[] =\n{\n    PHP_ME(swoole_connection_iterator, __construct,  arginfo_class_Swoole_Connection_Iterator___construct, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_connection_iterator, __destruct,  arginfo_class_Swoole_Connection_Iterator___destruct, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_connection_iterator, rewind,      arginfo_class_Swoole_Connection_Iterator_rewind, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_connection_iterator, next,        arginfo_class_Swoole_Connection_Iterator_next, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_connection_iterator, current,     arginfo_class_Swoole_Connection_Iterator_current, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_connection_iterator, key,         arginfo_class_Swoole_Connection_Iterator_key, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_connection_iterator, valid,       arginfo_class_Swoole_Connection_Iterator_valid, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_connection_iterator, count,       arginfo_class_Swoole_Connection_Iterator_count, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_connection_iterator, offsetExists,    arginfo_class_Swoole_Connection_Iterator_offsetExists, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_connection_iterator, offsetGet,       arginfo_class_Swoole_Connection_Iterator_offsetGet, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_connection_iterator, offsetSet,       arginfo_class_Swoole_Connection_Iterator_offsetSet, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_connection_iterator, offsetUnset,     arginfo_class_Swoole_Connection_Iterator_offsetUnset, ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n\nstatic constexpr zend_function_entry swoole_server_task_methods[] =\n{\n    PHP_ME(swoole_server_task, finish, arginfo_class_Swoole_Server_Task_finish, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server_task, pack, arginfo_class_Swoole_Server_Task_pack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_server_task, unpack, arginfo_class_Swoole_Server_Task_unpack, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_FE_END\n};\n// clang-format on\n\n#define SWOOLE_DISPATCH_STREAM 7  // Deprecated, kept for forward compatibility\n\nvoid php_swoole_server_minit(int module_number) {\n    // ---------------------------------------Server-------------------------------------\n    SW_INIT_CLASS_ENTRY(swoole_server, \"Swoole\\\\Server\", nullptr, swoole_server_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_server);\n    SW_SET_CLASS_CLONEABLE(swoole_server, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_server, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(swoole_server, server_create_object, server_free_object, ServerObject, std);\n\n    // ---------------------------------------Task-------------------------------------\n    SW_INIT_CLASS_ENTRY(swoole_server_task, \"Swoole\\\\Server\\\\Task\", nullptr, swoole_server_task_methods);\n    swoole_server_task_ce->ce_flags |= ZEND_ACC_FINAL;\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_server_task);\n    SW_SET_CLASS_CLONEABLE(swoole_server_task, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_server_task, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(swoole_server_task,\n                               php_swoole_server_task_create_object,\n                               php_swoole_server_task_free_object,\n                               ServerTaskObject,\n                               std);\n    zend_declare_property_null(swoole_server_task_ce, ZEND_STRL(\"data\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_double(swoole_server_task_ce, ZEND_STRL(\"dispatch_time\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_task_ce, ZEND_STRL(\"id\"), -1, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_task_ce, ZEND_STRL(\"worker_id\"), -1, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_task_ce, ZEND_STRL(\"flags\"), 0, ZEND_ACC_PUBLIC);\n    // ---------------------------------------Event-------------------------------------\n    SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_server_event, \"Swoole\\\\Server\\\\Event\");\n    zend_declare_property_long(swoole_server_event_ce, ZEND_STRL(\"reactor_id\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_event_ce, ZEND_STRL(\"fd\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_double(swoole_server_event_ce, ZEND_STRL(\"dispatch_time\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_server_event_ce, ZEND_STRL(\"data\"), ZEND_ACC_PUBLIC);\n    // ---------------------------------------Packet-------------------------------------\n    SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_server_packet, \"Swoole\\\\Server\\\\Packet\");\n    zend_declare_property_long(swoole_server_packet_ce, ZEND_STRL(\"server_socket\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_packet_ce, ZEND_STRL(\"server_port\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_double(swoole_server_packet_ce, ZEND_STRL(\"dispatch_time\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_server_packet_ce, ZEND_STRL(\"address\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_packet_ce, ZEND_STRL(\"port\"), 0, ZEND_ACC_PUBLIC);\n    // ---------------------------------------PipeMessage-------------------------------------\n    SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_server_pipe_message, \"Swoole\\\\Server\\\\PipeMessage\");\n    zend_declare_property_long(swoole_server_pipe_message_ce, ZEND_STRL(\"source_worker_id\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_pipe_message_ce, ZEND_STRL(\"worker_id\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_double(swoole_server_pipe_message_ce, ZEND_STRL(\"dispatch_time\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_server_pipe_message_ce, ZEND_STRL(\"data\"), ZEND_ACC_PUBLIC);\n    // ---------------------------------------StatusInfo-------------------------------------\n    SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_server_status_info, \"Swoole\\\\Server\\\\StatusInfo\");\n    zend_declare_property_long(swoole_server_status_info_ce, ZEND_STRL(\"worker_id\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_status_info_ce, ZEND_STRL(\"worker_pid\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_status_info_ce, ZEND_STRL(\"status\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_status_info_ce, ZEND_STRL(\"exit_code\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_status_info_ce, ZEND_STRL(\"signal\"), 0, ZEND_ACC_PUBLIC);\n    // ---------------------------------------TaskResult-------------------------------------\n    SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_server_task_result, \"Swoole\\\\Server\\\\TaskResult\");\n    zend_declare_property_long(swoole_server_task_result_ce, ZEND_STRL(\"task_id\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_task_result_ce, ZEND_STRL(\"task_worker_id\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_double(swoole_server_task_result_ce, ZEND_STRL(\"dispatch_time\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_server_task_result_ce, ZEND_STRL(\"data\"), ZEND_ACC_PUBLIC);\n    // ---------------------------------------Connection Iterator-------------------------------------\n    SW_INIT_CLASS_ENTRY(\n        swoole_connection_iterator, \"Swoole\\\\Connection\\\\Iterator\", nullptr, swoole_connection_iterator_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_connection_iterator);\n    SW_SET_CLASS_CLONEABLE(swoole_connection_iterator, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_connection_iterator, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(swoole_connection_iterator,\n                               php_swoole_connection_iterator_create_object,\n                               php_swoole_connection_iterator_free_object,\n                               ConnectionIteratorObject,\n                               std);\n    zend_class_implements(swoole_connection_iterator_ce, 2, zend_ce_iterator, zend_ce_arrayaccess);\n    zend_class_implements(swoole_connection_iterator_ce, 1, zend_ce_countable);\n    // ---------------------------------------Server Property-------------------------------------\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"onStart\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"onBeforeShutdown\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"onShutdown\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"onWorkerStart\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"onWorkerStop\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"onBeforeReload\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"onAfterReload\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"onWorkerExit\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"onWorkerError\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"onTask\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"onFinish\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"onManagerStart\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"onManagerStop\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"onPipeMessage\"), ZEND_ACC_PRIVATE);\n\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"setting\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"connections\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_string(swoole_server_ce, ZEND_STRL(\"host\"), \"\", ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_ce, ZEND_STRL(\"port\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_ce, ZEND_STRL(\"type\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_bool(swoole_server_ce, ZEND_STRL(\"ssl\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_ce, ZEND_STRL(\"mode\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"ports\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_ce, ZEND_STRL(\"master_pid\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_ce, ZEND_STRL(\"manager_pid\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_ce, ZEND_STRL(\"worker_id\"), -1, ZEND_ACC_PUBLIC);\n    zend_declare_property_bool(swoole_server_ce, ZEND_STRL(\"taskworker\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_ce, ZEND_STRL(\"worker_pid\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"stats_timer\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_server_ce, ZEND_STRL(\"admin_server\"), ZEND_ACC_PUBLIC);\n#ifdef SW_THREAD\n    zend_declare_property_string(swoole_server_ce, ZEND_STRL(\"bootstrap\"), \"\", ZEND_ACC_PUBLIC);\n#endif\n\n    /**\n     * mode type\n     */\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_BASE\", swoole::Server::MODE_BASE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_PROCESS\", swoole::Server::MODE_PROCESS);\n#ifdef SW_THREAD\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_THREAD\", swoole::Server::MODE_THREAD);\n#endif\n    /**\n     * task ipc mode\n     */\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_IPC_UNSOCK\", Server::TASK_IPC_UNIXSOCK);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_IPC_MSGQUEUE\", Server::TASK_IPC_MSGQUEUE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_IPC_PREEMPTIVE\", Server::TASK_IPC_PREEMPTIVE);\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SERVER_COMMAND_MASTER\", Server::Command::MASTER);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SERVER_COMMAND_MANAGER\", Server::Command::MANAGER);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SERVER_COMMAND_REACTOR_THREAD\", Server::Command::REACTOR_THREAD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SERVER_COMMAND_EVENT_WORKER\", Server::Command::EVENT_WORKER);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SERVER_COMMAND_WORKER\", Server::Command::EVENT_WORKER);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_SERVER_COMMAND_TASK_WORKER\", Server::Command::TASK_WORKER);\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_DISPATCH_ROUND\", Server::DISPATCH_ROUND);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_DISPATCH_FDMOD\", Server::DISPATCH_FDMOD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_DISPATCH_IDLE_WORKER\", Server::DISPATCH_IDLE_WORKER);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_DISPATCH_IPMOD\", Server::DISPATCH_IPMOD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_DISPATCH_UIDMOD\", Server::DISPATCH_UIDMOD);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_DISPATCH_USERFUNC\", Server::DISPATCH_USERFUNC);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_DISPATCH_STREAM\", SWOOLE_DISPATCH_STREAM);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_DISPATCH_CO_CONN_LB\", Server::DISPATCH_CO_CONN_LB);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_DISPATCH_CO_REQ_LB\", Server::DISPATCH_CO_REQ_LB);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_DISPATCH_CONCURRENT_LB\", Server::DISPATCH_CONCURRENT_LB);\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_DISPATCH_RESULT_DISCARD_PACKET\", Server::DISPATCH_RESULT_DISCARD_PACKET);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_DISPATCH_RESULT_CLOSE_CONNECTION\", Server::DISPATCH_RESULT_CLOSE_CONNECTION);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_DISPATCH_RESULT_USERFUNC_FALLBACK\", Server::DISPATCH_RESULT_USERFUNC_FALLBACK);\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TASK_TMPFILE\", SW_TASK_TMPFILE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TASK_SERIALIZE\", SW_TASK_SERIALIZE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TASK_NONBLOCK\", SW_TASK_NONBLOCK);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TASK_CALLBACK\", SW_TASK_CALLBACK);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TASK_WAITALL\", SW_TASK_WAITALL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TASK_COROUTINE\", SW_TASK_COROUTINE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TASK_PEEK\", SW_TASK_PEEK);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TASK_NOREPLY\", SW_TASK_NOREPLY);\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WORKER_BUSY\", SW_WORKER_BUSY);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WORKER_IDLE\", SW_WORKER_IDLE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WORKER_EXIT\", SW_WORKER_EXIT);\n}\n\nzend::Callable *php_swoole_server_get_callback(Server *serv, int server_fd, int event_type) {\n    ListenPort *port = serv->get_port_by_server_fd(server_fd);\n    ServerPortProperty *property = php_swoole_server_get_port_property(port);\n    zend::Callable *cb;\n\n    if (sw_unlikely(!port)) {\n        return nullptr;\n    }\n    if (property && ((cb = property->callbacks[event_type]))) {\n        return cb;\n    } else {\n        return php_swoole_server_get_port_property(serv->get_primary_port())->callbacks[event_type];\n    }\n}\n\nint php_swoole_create_dir(const char *path, size_t length) {\n    if (access(path, F_OK) == 0) {\n        return 0;\n    }\n    return php_stream_mkdir(path, 0777, PHP_STREAM_MKDIR_RECURSIVE | REPORT_ERRORS, nullptr) ? 0 : -1;\n}\n\nstatic TaskId php_swoole_server_task_pack(zval *zdata, EventData *task) {\n    smart_str serialized_data = {};\n    php_serialize_data_t var_hash;\n\n    bool serialized = false;\n    char *task_data_str;\n    size_t task_data_len = 0;\n    // need serialize\n    if (Z_TYPE_P(zdata) != IS_STRING) {\n        // serialize\n        serialized = true;\n\n        PHP_VAR_SERIALIZE_INIT(var_hash);\n        php_var_serialize(&serialized_data, zdata, &var_hash);\n        PHP_VAR_SERIALIZE_DESTROY(var_hash);\n\n        if (!serialized_data.s) {\n            return -1;\n        }\n        task_data_str = ZSTR_VAL(serialized_data.s);\n        task_data_len = ZSTR_LEN(serialized_data.s);\n    } else {\n        task_data_str = Z_STRVAL_P(zdata);\n        task_data_len = Z_STRLEN_P(zdata);\n    }\n\n    if (!Server::task_pack(task, task_data_str, task_data_len)) {\n        php_swoole_fatal_error(E_WARNING, \"large task pack failed\");\n        task->info.fd = SW_ERR;\n        task->info.len = 0;\n    }\n\n    if (serialized) {\n        task->info.ext_flags |= SW_TASK_SERIALIZE;\n    }\n\n    smart_str_free(&serialized_data);\n    return task->info.fd;\n}\n\nvoid php_swoole_get_recv_data(Server *serv, zval *zdata, RecvData *req) {\n    const char *data = req->data;\n    uint32_t length = req->info.len;\n    if (length == 0) {\n        ZVAL_EMPTY_STRING(zdata);\n    } else {\n        if (req->info.flags & SW_EVENT_DATA_OBJ_PTR) {\n            zend::assign_zend_string_by_val(zdata, (char *) data, length);\n            serv->get_worker_message_bus()->move_packet();\n        } else if (req->info.flags & SW_EVENT_DATA_POP_PTR) {\n            String *recv_buffer = serv->get_recv_buffer(serv->get_connection_by_session_id(req->info.fd)->socket);\n            zend::assign_zend_string_by_val(zdata, recv_buffer->pop(serv->recv_buffer_size), length);\n        } else {\n            ZVAL_STRINGL(zdata, data, length);\n        }\n    }\n}\n\nstatic inline int php_swoole_server_task_check_param(Server *serv, zend_long dst_worker_id) {\n    if (UNEXPECTED(serv->task_worker_num == 0)) {\n        php_swoole_fatal_error(E_WARNING, \"task method can't be executed without task worker\");\n        return SW_ERR;\n    }\n    if (UNEXPECTED(dst_worker_id > 0 && dst_worker_id >= serv->task_worker_num)) {\n        php_swoole_fatal_error(E_WARNING, \"worker_id must be less than task_worker_num[%u]\", serv->task_worker_num);\n        return SW_ERR;\n    }\n    if (UNEXPECTED(serv->is_task_worker())) {\n        php_swoole_fatal_error(E_WARNING, \"Server->task() cannot use in the task-worker\");\n        return SW_ERR;\n    }\n    return SW_OK;\n}\n\nstatic bool php_swoole_server_task_unpack(zval *zresult, EventData *task_result) {\n    php_unserialize_data_t var_hash;\n    PacketPtr packet;\n\n    if (!Server::task_unpack(task_result, sw_tg_buffer(), &packet)) {\n        return false;\n    }\n\n    if (task_result->info.ext_flags & SW_TASK_SERIALIZE) {\n        char *p = packet.data;\n        size_t l = packet.length;\n        PHP_VAR_UNSERIALIZE_INIT(var_hash);\n        zend_bool unserialized = php_var_unserialize(zresult, (const uchar **) &p, (const uchar *) (p + l), &var_hash);\n        PHP_VAR_UNSERIALIZE_DESTROY(var_hash);\n        if (!unserialized) {\n            swoole_warning(\"unserialize() failed, Error at offset \" ZEND_LONG_FMT \" of %zd bytes\",\n                           (zend_long) ((char *) p - packet.data),\n                           l);\n            return false;\n        }\n    } else {\n        ZVAL_STRINGL(zresult, packet.data, packet.length);\n    }\n\n    return true;\n}\n\nextern ListenPort *php_swoole_server_port_get_and_check_ptr(zval *zobject);\nextern void php_swoole_server_port_set_ptr(zval *zobject, ListenPort *port);\nextern ServerPortProperty *php_swoole_server_port_get_property(zval *zobject);\n\nstatic zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort *port) {\n    Server *serv = server_object->serv;\n\n    zval *zport = sw_malloc_zval();\n    object_init_ex(zport, swoole_server_port_ce);\n    server_object->property->ports.push_back(zport);\n\n    /* port ptr */\n    php_swoole_server_port_set_ptr(zport, port);\n\n    /* port property */\n    ServerPortProperty *property = php_swoole_server_port_get_property(zport);\n    property->serv = serv;\n    property->port = port;\n\n    /* linked */\n    php_swoole_server_set_port_property(port, property);\n\n    zend_update_property_string(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL(\"host\"), port->get_host());\n    zend_update_property_long(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL(\"port\"), port->get_port());\n    zend_update_property_long(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL(\"type\"), port->get_type());\n    zend_update_property_long(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL(\"sock\"), port->get_fd());\n    zend_update_property_bool(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL(\"ssl\"), port->ssl);\n\n    do {\n        zval *zserv = php_swoole_server_zval_ptr(serv);\n        zval *zports = sw_zend_read_and_convert_property_array(Z_OBJCE_P(zserv), zserv, ZEND_STRL(\"ports\"), 0);\n        (void) add_next_index_zval(zports, zport);\n    } while (false);\n\n    /* iterator */\n    do {\n        zval connection_iterator;\n        object_init_ex(&connection_iterator, swoole_connection_iterator_ce);\n\n        ConnectionIterator *iterator = php_swoole_connection_iterator_get_ptr(&connection_iterator);\n        iterator->serv = serv;\n        iterator->port = port;\n\n        zend_update_property(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL(\"connections\"), &connection_iterator);\n        zval_ptr_dtor(&connection_iterator);\n    } while (false);\n\n    return zport;\n}\n\nvoid ServerObject::copy_setting(zval *zsetting) const {\n    zend_array *new_array = zend_array_dup(Z_ARRVAL_P(zsetting));\n    zend_hash_apply(new_array, [](zval *el) -> int {\n        return sw_zval_is_serializable(el) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE;\n    });\n    zval znew_array;\n    ZVAL_ARR(&znew_array, new_array);\n    serv->private_data_4 = php_swoole_serialize(&znew_array);\n    zval_ptr_dtor(&znew_array);\n}\n\nvoid ServerObject::on_before_start() {\n    /**\n     * create swoole server\n     */\n    if (serv->create() < 0) {\n        php_swoole_fatal_error(E_ERROR, \"failed to create the server. Error: %s\", sw_error);\n        return;\n    }\n\n    zval *zobject = php_swoole_server_zval_ptr(serv);\n    auto primary_port = serv->get_primary_port();\n\n#ifdef SW_LOG_TRACE_OPEN\n    swoole_trace_log(SW_TRACE_SERVER,\n                     \"Create Server: host=%s, port=%d, mode=%d, type=%d\",\n                     primary_port->host.c_str(),\n                     (int) primary_port->port,\n                     serv->is_base_mode() ? Server::MODE_BASE : Server::MODE_PROCESS,\n                     (int) primary_port->type);\n#endif\n\n    if (serv->enable_coroutine) {\n        serv->reload_async = true;\n    }\n\n    if (serv->send_yield) {\n        if (serv->onClose == nullptr && serv->is_support_unsafe_events()) {\n            serv->onClose = php_swoole_server_onClose;\n        }\n    }\n\n    serv->message_bus.set_allocator(sw_zend_string_allocator());\n\n    if (serv->is_base_mode() || serv->is_thread_mode()) {\n        serv->recv_buffer_allocator = sw_zend_string_allocator();\n    }\n\n    /**\n     * Master Process ID\n     */\n    zend_update_property_long(get_ce(), SW_Z8_OBJ_P(zobject), ZEND_STRL(\"master_pid\"), getpid());\n\n    zval *zsetting = sw_zend_read_and_convert_property_array(get_ce(), zobject, ZEND_STRL(\"setting\"), 0);\n\n    if (!zend_hash_str_exists(Z_ARRVAL_P(zsetting), ZEND_STRL(\"worker_num\"))) {\n        add_assoc_long(zsetting, \"worker_num\", serv->worker_num);\n    }\n    if (!zend_hash_str_exists(Z_ARRVAL_P(zsetting), ZEND_STRL(\"task_worker_num\"))) {\n        add_assoc_long(zsetting, \"task_worker_num\", serv->task_worker_num);\n    }\n    if (!zend_hash_str_exists(Z_ARRVAL_P(zsetting), ZEND_STRL(\"output_buffer_size\"))) {\n        add_assoc_long(zsetting, \"output_buffer_size\", serv->output_buffer_size);\n    }\n    if (!zend_hash_str_exists(Z_ARRVAL_P(zsetting), ZEND_STRL(\"max_connection\"))) {\n        add_assoc_long(zsetting, \"max_connection\", serv->get_max_connection());\n    }\n    // for admin_server\n    if (zend_hash_str_exists(Z_ARRVAL_P(zsetting), ZEND_STRL(\"admin_server\"))) {\n        swoole::register_admin_server_commands(serv);\n    }\n\n    bool find_http_port = false;\n    if (is_redis_server()) {\n        add_assoc_bool(zsetting, \"open_redis_protocol\", 1);\n        add_assoc_bool(zsetting, \"open_http_protocol\", 0);\n        add_assoc_bool(zsetting, \"open_mqtt_protocol\", 0);\n        add_assoc_bool(zsetting, \"open_eof_check\", 0);\n        add_assoc_bool(zsetting, \"open_length_check\", 0);\n        primary_port->clear_protocol();\n        primary_port->open_redis_protocol = true;\n        serv->onReceive = php_swoole_redis_server_onReceive;\n    } else if (is_http_server()) {\n        if (is_websocket_server()) {\n            if (!isset_callback(primary_port, SW_SERVER_CB_onMessage)) {\n                php_swoole_fatal_error(E_ERROR, \"require onMessage callback\");\n                return;\n            }\n        } else if (!isset_callback(primary_port, SW_SERVER_CB_onRequest)) {\n            php_swoole_fatal_error(E_ERROR, \"require onRequest callback\");\n            return;\n        }\n\n        add_assoc_bool(zsetting, \"open_http_protocol\", 1);\n        add_assoc_bool(zsetting, \"open_mqtt_protocol\", 0);\n        add_assoc_bool(zsetting, \"open_eof_check\", 0);\n        add_assoc_bool(zsetting, \"open_length_check\", 0);\n\n        enum protocol_flags { SW_HTTP2_PROTOCOL = 1u << 1, SW_WEBSOCKET_PROTOCOL = 1u << 2 };\n        uint8_t protocol_flag = 0;\n        if (primary_port->open_http2_protocol) {\n            add_assoc_bool(zsetting, \"open_http2_protocol\", 1);\n            protocol_flag |= SW_HTTP2_PROTOCOL;\n        }\n        if (primary_port->open_websocket_protocol || is_websocket_server()) {\n            add_assoc_bool(zsetting, \"open_websocket_protocol\", 1);\n            protocol_flag |= SW_WEBSOCKET_PROTOCOL;\n        }\n        primary_port->clear_protocol();\n        primary_port->open_http_protocol = true;\n        primary_port->open_http2_protocol = !!(protocol_flag & SW_HTTP2_PROTOCOL);\n        primary_port->open_websocket_protocol = !!(protocol_flag & SW_WEBSOCKET_PROTOCOL);\n        find_http_port = true;\n        serv->onReceive = php_swoole_http_server_onReceive;\n    } else {\n        if (serv->if_require_packet_callback(primary_port, isset_callback(primary_port, SW_SERVER_CB_onPacket))) {\n            php_swoole_fatal_error(E_ERROR, \"require onPacket callback\");\n            return;\n        }\n        if (serv->if_require_receive_callback(primary_port, isset_callback(primary_port, SW_SERVER_CB_onReceive))) {\n            php_swoole_fatal_error(E_ERROR, \"require onReceive callback\");\n            return;\n        }\n        serv->onReceive = php_swoole_server_onReceive;\n    }\n\n    for (size_t i = 1; i < property->ports.size(); i++) {\n        zval *zport = property->ports.at(i);\n        zval *zport_setting =\n            sw_zend_read_property_ex(swoole_server_port_ce, zport, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0);\n        // use swoole_server->setting\n        if (zport_setting == nullptr || ZVAL_IS_NULL(zport_setting)) {\n            Z_TRY_ADDREF_P(zport);\n            sw_zend_call_method_with_1_params(zport, swoole_server_port_ce, nullptr, \"set\", nullptr, zsetting);\n        }\n    }\n\n    for (auto zport : property->ports) {\n        ListenPort *port = php_swoole_server_port_get_and_check_ptr(zport);\n\n        if (serv->if_require_packet_callback(port, isset_callback(port, SW_SERVER_CB_onPacket))) {\n            php_swoole_fatal_error(E_ERROR, \"require onPacket callback\");\n            return;\n        }\n\n        if (port->ssl_is_enable() && port->get_ssl_verify_peer() && port->get_ssl_client_cert_file().empty() &&\n            port->get_ssl_cafile().empty() && port->get_ssl_capath().empty()) {\n            php_swoole_fatal_error(\n                E_ERROR,\n                \"server open verify peer require `ssl_client_cert_file` or `ssl_capath` or `ssl_cafile` config\");\n            return;\n        }\n\n        if (port->open_http2_protocol && !serv->is_hash_dispatch_mode()) {\n            php_swoole_fatal_error(\n                E_ERROR,\n                \"server dispatch mode should be FDMOD(%d) or IPMOD(%d) if open_http2_protocol is true\",\n                Server::DISPATCH_FDMOD,\n                Server::DISPATCH_IPMOD);\n            return;\n        }\n\n        if (!port->open_http_protocol) {\n            port->open_http_protocol = port->open_websocket_protocol || port->open_http2_protocol;\n        }\n        if (port->open_http_protocol) {\n            find_http_port = true;\n            if (port->open_websocket_protocol) {\n                if (!isset_callback(port, SW_SERVER_CB_onMessage) && !isset_callback(port, SW_SERVER_CB_onReceive)) {\n                    php_swoole_fatal_error(E_ERROR, \"require onMessage callback\");\n                    return;\n                }\n            } else if (port->open_http_protocol && !isset_callback(port, SW_SERVER_CB_onRequest) &&\n                       !isset_callback(port, SW_SERVER_CB_onReceive)) {\n                php_swoole_fatal_error(E_ERROR, \"require onRequest callback\");\n                return;\n            }\n            if (!is_http_server() && isset_callback(port, SW_SERVER_CB_onRequest)) {\n                php_swoole_error(\n                    E_WARNING,\n                    \"use %s class and open http related protocols may lead to some errors (inconsistent class type)\",\n                    SW_Z_OBJCE_NAME_VAL_P(zobject));\n            }\n        } else if (!port->open_redis_protocol) {\n            // redis server does not require to set receive callback\n            if (port->is_stream() && !isset_callback(port, SW_SERVER_CB_onReceive)) {\n                php_swoole_fatal_error(E_ERROR, \"require onReceive callback\");\n                return;\n            }\n        }\n    }\n\n    if (find_http_port) {\n        serv->onReceive = php_swoole_http_server_onReceive;\n        if (serv->is_base_mode()) {\n            serv->onClose = php_swoole_http_server_onClose;\n        }\n    }\n\n#ifdef SW_THREAD\n    if (serv->is_thread_mode()) {\n        copy_setting(zsetting);\n    }\n#endif\n\n    if (SWOOLE_G(enable_library)) {\n        zend::function::call(R\"(\\Swoole\\Server\\Helper::onBeforeStart)\", 1, zobject);\n    }\n}\n\nvoid ServerObject::register_callback() const {\n    // control plane\n    serv->onStart = php_swoole_server_onStart;\n    serv->onBeforeShutdown = php_swoole_server_onBeforeShutdown;\n    serv->onShutdown = php_swoole_server_onShutdown;\n    serv->onWorkerStart = php_swoole_server_onWorkerStart;\n    serv->onWorkerStop = php_swoole_server_onWorkerStop;\n    serv->onWorkerExit = php_swoole_server_onWorkerExit;\n    serv->onBeforeReload = php_swoole_server_onBeforeReload;\n    serv->onAfterReload = php_swoole_server_onAfterReload;\n    serv->onManagerStart = php_swoole_server_onManagerStart;\n    serv->onManagerStop = php_swoole_server_onManagerStop;\n    serv->onWorkerError = php_swoole_server_onWorkerError;\n\n    // data plane\n    if (property->callbacks[SW_SERVER_CB_onTask] != nullptr) {\n        serv->onTask = php_swoole_server_onTask;\n        serv->onFinish = php_swoole_server_onFinish;\n    }\n    if (property->callbacks[SW_SERVER_CB_onPipeMessage] != nullptr) {\n        serv->onPipeMessage = php_swoole_server_onPipeMessage;\n    }\n    if (serv->send_yield && serv->is_support_unsafe_events()) {\n        serv->onBufferEmpty = php_swoole_server_onBufferEmpty;\n    }\n}\n\nstatic bool php_swoole_server_task_finish(Server *serv, zval *zdata, EventData *current_task) {\n    int flags = 0;\n    smart_str serialized_data = {};\n    php_serialize_data_t var_hash;\n    char *data_str;\n    size_t data_len = 0;\n\n    // need serialize\n    if (Z_TYPE_P(zdata) != IS_STRING) {\n        // serialize\n        flags |= SW_TASK_SERIALIZE;\n\n        PHP_VAR_SERIALIZE_INIT(var_hash);\n        php_var_serialize(&serialized_data, zdata, &var_hash);\n        PHP_VAR_SERIALIZE_DESTROY(var_hash);\n        data_str = ZSTR_VAL(serialized_data.s);\n        data_len = ZSTR_LEN(serialized_data.s);\n\n    } else {\n        data_str = Z_STRVAL_P(zdata);\n        data_len = Z_STRLEN_P(zdata);\n    }\n\n    bool success = serv->finish(data_str, data_len, flags, current_task);\n    smart_str_free(&serialized_data);\n    return success;\n}\n\nstatic void php_swoole_server_onPipeMessage(Server *serv, EventData *req) {\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv)));\n    auto cb = server_object->get_callback(SW_SERVER_CB_onPipeMessage);\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n\n    zend::Variable zresult;\n    if (UNEXPECTED(!php_swoole_server_task_unpack(zresult.ptr(), req))) {\n        return;\n    }\n\n    swoole_trace_log(SW_TRACE_SERVER,\n                     \"PipeMessage: fd=%ld|len=%d|src_worker_id=%d|data=%.*s\\n\",\n                     req->info.fd,\n                     req->info.len,\n                     req->info.reactor_id,\n                     req->info.len,\n                     req->data);\n\n    zval args[3];\n    int argc;\n    args[0] = *zserv;\n\n    if (serv->event_object) {\n        zval *object = &args[1];\n        object_init_ex(object, swoole_server_pipe_message_ce);\n        zend_update_property_long(swoole_server_pipe_message_ce,\n                                  SW_Z8_OBJ_P(object),\n                                  ZEND_STRL(\"worker_id\"),\n                                  (zend_long) serv->get_task_src_worker_id(req));\n        zend_update_property_long(swoole_server_pipe_message_ce,\n                                  SW_Z8_OBJ_P(object),\n                                  ZEND_STRL(\"source_worker_id\"),\n                                  (zend_long) serv->get_task_src_worker_id(req));\n        zend_update_property_double(\n            swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"dispatch_time\"), req->info.time);\n        zend_update_property(swoole_server_pipe_message_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"data\"), zresult.ptr());\n        argc = 2;\n    } else {\n        ZVAL_LONG(&args[1], (zend_long) serv->get_task_src_worker_id(req));\n        args[2] = zresult.value;\n        argc = 3;\n    }\n\n    if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->is_enable_coroutine()))) {\n        php_swoole_error(E_WARNING, \"%s->onPipeMessage handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n    }\n\n    if (serv->event_object) {\n        zval_ptr_dtor(&args[1]);\n    }\n}\n\nint php_swoole_server_onReceive(Server *serv, RecvData *req) {\n    auto cb = php_swoole_server_get_callback(serv, req->info.server_fd, SW_SERVER_CB_onReceive);\n\n    if (cb) {\n        zval *zserv = php_swoole_server_zval_ptr(serv);\n        zval args[4];\n        int argc;\n\n        args[0] = *zserv;\n\n        if (serv->event_object) {\n            zval *object = &args[1];\n            zval data;\n            object_init_ex(object, swoole_server_event_ce);\n            zend_update_property_long(\n                swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"fd\"), (zend_long) req->info.fd);\n            zend_update_property_long(\n                swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"reactor_id\"), (zend_long) req->info.reactor_id);\n            zend_update_property_double(\n                swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"dispatch_time\"), req->info.time);\n            php_swoole_get_recv_data(serv, &data, req);\n            zend_update_property(swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"data\"), &data);\n            zval_ptr_dtor(&data);\n            argc = 2;\n        } else {\n            ZVAL_LONG(&args[1], (zend_long) req->info.fd);\n            ZVAL_LONG(&args[2], (zend_long) req->info.reactor_id);\n            php_swoole_get_recv_data(serv, &args[3], req);\n            argc = 4;\n        }\n\n        if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->enable_coroutine))) {\n            php_swoole_error(E_WARNING, \"%s->onReceive handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n            serv->close(req->info.fd, false);\n        }\n        if (serv->event_object) {\n            zval_ptr_dtor(&args[1]);\n        } else {\n            zval_ptr_dtor(&args[3]);\n        }\n    }\n\n    return SW_OK;\n}\n\nint php_swoole_server_onPacket(Server *serv, RecvData *req) {\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    zval args[3];\n    int argc;\n\n    args[0] = *zserv;\n\n    auto *packet = (DgramPacket *) req->data;\n\n    if (serv->event_object) {\n        zval zobject;\n        object_init_ex(&zobject, swoole_server_packet_ce);\n        zend_update_property_long(\n            swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL(\"server_socket\"), req->info.server_fd);\n        zend_update_property_double(\n            swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL(\"dispatch_time\"), req->info.time);\n\n        Connection *server_sock = serv->get_connection(req->info.server_fd);\n        if (server_sock) {\n            zend_update_property_long(\n                swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL(\"server_port\"), server_sock->info.get_port());\n        }\n\n        char address[INET6_ADDRSTRLEN];\n        if (packet->socket_type == SW_SOCK_UDP) {\n            inet_ntop(AF_INET, &packet->socket_addr.addr.inet_v4.sin_addr, address, sizeof(address));\n            zend_update_property_string(swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL(\"address\"), address);\n            zend_update_property_long(swoole_server_packet_ce,\n                                      SW_Z8_OBJ_P(&zobject),\n                                      ZEND_STRL(\"port\"),\n                                      ntohs(packet->socket_addr.addr.inet_v4.sin_port));\n        } else if (packet->socket_type == SW_SOCK_UDP6) {\n            inet_ntop(AF_INET6, &packet->socket_addr.addr.inet_v6.sin6_addr, address, sizeof(address));\n            zend_update_property_string(swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL(\"address\"), address);\n            zend_update_property_long(swoole_server_packet_ce,\n                                      SW_Z8_OBJ_P(&zobject),\n                                      ZEND_STRL(\"port\"),\n                                      ntohs(packet->socket_addr.addr.inet_v6.sin6_port));\n        } else if (packet->socket_type == SW_SOCK_UNIX_DGRAM) {\n            zend_update_property_string(swoole_server_packet_ce,\n                                        SW_Z8_OBJ_P(&zobject),\n                                        ZEND_STRL(\"address\"),\n                                        packet->socket_addr.addr.un.sun_path);\n        }\n        zend_update_property_stringl(\n            swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL(\"data\"), packet->data, packet->length);\n        args[1] = zobject;\n        argc = 2;\n    } else {\n        zval zaddr;\n        array_init(&zaddr);\n        add_assoc_long(&zaddr, \"server_socket\", req->info.server_fd);\n        add_assoc_double(&zaddr, \"dispatch_time\", req->info.time);\n        Connection *server_sock = serv->get_connection(req->info.server_fd);\n        if (server_sock) {\n            add_assoc_long(&zaddr, \"server_port\", server_sock->info.get_port());\n        }\n\n        char address[INET6_ADDRSTRLEN];\n        if (packet->socket_type == SW_SOCK_UDP) {\n            inet_ntop(AF_INET, &packet->socket_addr.addr.inet_v4.sin_addr, address, sizeof(address));\n            add_assoc_string(&zaddr, \"address\", address);\n            add_assoc_long(&zaddr, \"port\", ntohs(packet->socket_addr.addr.inet_v4.sin_port));\n        } else if (packet->socket_type == SW_SOCK_UDP6) {\n            inet_ntop(AF_INET6, &packet->socket_addr.addr.inet_v6.sin6_addr, address, sizeof(address));\n            add_assoc_string(&zaddr, \"address\", address);\n            add_assoc_long(&zaddr, \"port\", ntohs(packet->socket_addr.addr.inet_v6.sin6_port));\n        } else if (packet->socket_type == SW_SOCK_UNIX_DGRAM) {\n            add_assoc_string(&zaddr, \"address\", packet->socket_addr.addr.un.sun_path);\n        }\n\n        ZVAL_STRINGL(&args[1], packet->data, packet->length);\n        args[2] = zaddr;\n        argc = 3;\n    }\n\n    auto cb = php_swoole_server_get_callback(serv, req->info.server_fd, SW_SERVER_CB_onPacket);\n    if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->enable_coroutine))) {\n        php_swoole_error(E_WARNING, \"%s->onPipeMessage handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n    }\n\n    zval_ptr_dtor(&args[1]);\n    if (!serv->event_object) {\n        zval_ptr_dtor(&args[2]);\n    }\n\n    return SW_OK;\n}\n\nstatic sw_inline void php_swoole_create_task_object(zval *ztask, Server *serv, EventData *req, zval *zdata) {\n    object_init_ex(ztask, swoole_server_task_ce);\n    php_swoole_server_task_set_server(ztask, serv);\n    php_swoole_server_task_set_info(ztask, &req->info);\n\n    zend_update_property_long(swoole_server_task_ce,\n                              SW_Z8_OBJ_P(ztask),\n                              ZEND_STRL(\"worker_id\"),\n                              (zend_long) serv->get_task_src_worker_id(req));\n    zend_update_property_long(\n        swoole_server_task_ce, SW_Z8_OBJ_P(ztask), ZEND_STRL(\"id\"), (zend_long) serv->get_task_id(req));\n    zend_update_property(swoole_server_task_ce, SW_Z8_OBJ_P(ztask), ZEND_STRL(\"data\"), zdata);\n    zend_update_property_double(swoole_server_task_ce, SW_Z8_OBJ_P(ztask), ZEND_STRL(\"dispatch_time\"), req->info.time);\n    zend_update_property_long(\n        swoole_server_task_ce, SW_Z8_OBJ_P(ztask), ZEND_STRL(\"flags\"), (zend_long) req->info.ext_flags);\n}\n\nstatic int php_swoole_server_onTask(Server *serv, EventData *req) {\n    sw_atomic_fetch_sub(&serv->gs->tasking_num, 1);\n\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));\n\n    zend::Variable zresult;\n    if (!php_swoole_server_task_unpack(zresult.ptr(), req)) {\n        return SW_ERR;\n    }\n\n    zval retval;\n    uint32_t argc;\n    zval argv[4];\n\n    if (serv->task_enable_coroutine || serv->task_object) {\n        argc = 2;\n        argv[0] = *zserv;\n        php_swoole_create_task_object(&argv[1], serv, req, zresult.ptr());\n    } else {\n        argc = 4;\n        argv[0] = *zserv;\n        ZVAL_LONG(&argv[1], (zend_long) serv->get_task_id(req));\n        ZVAL_LONG(&argv[2], (zend_long) serv->get_task_src_worker_id(req));\n        argv[3] = zresult.value;\n    }\n\n    if (UNEXPECTED(!zend::function::call(server_object->get_callback(SW_SERVER_CB_onTask)->ptr(),\n                                         argc,\n                                         argv,\n                                         &retval,\n                                         serv->task_enable_coroutine))) {\n        php_swoole_error(E_WARNING, \"%s->onTask handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n    }\n\n    if (argc == 2) {\n        zval_ptr_dtor(&argv[1]);\n    }\n\n    if (!ZVAL_IS_NULL(&retval)) {\n        php_swoole_server_task_finish(serv, &retval, req);\n        zval_ptr_dtor(&retval);\n    }\n\n    return SW_OK;\n}\n\nstatic int php_swoole_server_onFinish(Server *serv, EventData *req) {\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));\n    TaskId task_id = serv->get_task_id(req);\n\n    zend::Variable zresult;\n    if (!php_swoole_server_task_unpack(zresult.ptr(), req)) {\n        return SW_ERR;\n    }\n\n    if (req->info.ext_flags & SW_TASK_COROUTINE) {\n        auto task_co_iterator = server_object->property->task_coroutine_map.find(task_id);\n        if (task_co_iterator == server_object->property->task_coroutine_map.end()) {\n            swoole_error_log(SW_LOG_WARNING, SW_ERROR_TASK_TIMEOUT, \"task[%ld] has expired\", task_id);\n            return SW_OK;\n        }\n        TaskCo *task_co = task_co_iterator->second;\n        // Server->taskwait\n        if (task_co->list == nullptr) {\n            ZVAL_COPY_VALUE(task_co->result, zresult.ptr());\n            zresult.reset();\n            task_co->co->resume();\n            return SW_OK;\n        }\n        // Server->taskCo\n        int task_index = -1;\n        SW_LOOP_N(task_co->count) {\n            if (task_co->list[i] == task_id) {\n                task_index = i;\n                break;\n            }\n        }\n        if (task_index < 0) {\n            php_swoole_fatal_error(E_WARNING, \"task[%ld] is invalid\", task_id);\n            return SW_OK;\n        }\n        (void) add_index_zval(task_co->result, task_index, zresult.ptr());\n        zresult.reset();\n        server_object->property->task_coroutine_map.erase(task_id);\n\n        if (php_swoole_array_length(task_co->result) == task_co->count) {\n            task_co->co->resume();\n        }\n        return SW_OK;\n    }\n\n    zend::Callable *cb = nullptr;\n    if (req->info.ext_flags & SW_TASK_CALLBACK) {\n        auto callback_iterator = server_object->property->task_callbacks.find(task_id);\n        if (callback_iterator == server_object->property->task_callbacks.end()) {\n            req->info.ext_flags = req->info.ext_flags & (~SW_TASK_CALLBACK);\n        } else {\n            cb = callback_iterator->second;\n        }\n    } else {\n        cb = server_object->get_callback(SW_SERVER_CB_onFinish);\n    }\n\n    if (UNEXPECTED(cb == nullptr)) {\n        php_swoole_fatal_error(E_WARNING, \"require 'onFinish' callback\");\n        return SW_ERR;\n    }\n\n    zval args[3];\n    int argc;\n    args[0] = *zserv;\n\n    if (serv->event_object) {\n        zval *object = &args[1];\n        object_init_ex(object, swoole_server_task_result_ce);\n        zend_update_property_long(\n            swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"task_id\"), (zend_long) task_id);\n        zend_update_property_long(swoole_server_task_result_ce,\n                                  SW_Z8_OBJ_P(object),\n                                  ZEND_STRL(\"task_worker_id\"),\n                                  (zend_long) serv->get_task_src_worker_id(req));\n        zend_update_property_double(\n            swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"dispatch_time\"), req->info.time);\n        zend_update_property(swoole_server_task_result_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"data\"), zresult.ptr());\n        argc = 2;\n    } else {\n        ZVAL_LONG(&args[1], (zend_long) task_id);\n        args[2] = zresult.value;\n        argc = 3;\n    }\n\n    if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->enable_coroutine))) {\n        php_swoole_error(E_WARNING, \"%s->onFinish handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n    }\n    if (req->info.ext_flags & SW_TASK_CALLBACK) {\n        sw_callable_free(server_object->property->task_callbacks[task_id]);\n        server_object->property->task_callbacks.erase(task_id);\n    }\n    if (serv->event_object) {\n        zval_ptr_dtor(&args[1]);\n    }\n\n    return SW_OK;\n}\n\nstatic void php_swoole_server_onStart(Server *serv) {\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));\n    auto cb = server_object->get_callback(SW_SERVER_CB_onStart);\n\n    zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL(\"master_pid\"), serv->gs->master_pid);\n    zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL(\"manager_pid\"), serv->gs->manager_pid);\n\n    if (SWOOLE_G(enable_library)) {\n        zend::function::call(R\"(\\Swoole\\Server\\Helper::onStart)\", 1, zserv);\n    }\n\n    if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, serv->is_enable_coroutine()))) {\n        php_swoole_error(E_WARNING, \"%s->onStart handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n    }\n}\n\nstatic void php_swoole_server_onManagerStart(Server *serv) {\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));\n    auto cb = server_object->get_callback(SW_SERVER_CB_onManagerStart);\n\n    zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL(\"master_pid\"), serv->gs->master_pid);\n    zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL(\"manager_pid\"), serv->gs->manager_pid);\n\n    if (SWOOLE_G(enable_library)) {\n        zend::function::call(R\"(\\Swoole\\Server\\Helper::onManagerStart)\", 1, zserv);\n    }\n\n    if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) {\n        php_swoole_error(E_WARNING, \"%s->onManagerStart handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n    }\n}\n\nstatic void php_swoole_server_onManagerStop(Server *serv) {\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));\n    auto cb = server_object->get_callback(SW_SERVER_CB_onManagerStop);\n\n    if (SWOOLE_G(enable_library)) {\n        zend::function::call(R\"(\\Swoole\\Server\\Helper::onManagerStop)\", 1, zserv);\n    }\n\n    if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) {\n        php_swoole_error(E_WARNING, \"%s->onManagerStop handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n    }\n}\n\nstatic void php_swoole_server_onBeforeShutdown(Server *serv) {\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));\n    auto cb = server_object->get_callback(SW_SERVER_CB_onBeforeShutdown);\n\n    if (SWOOLE_G(enable_library)) {\n        zend::function::call(R\"(\\Swoole\\Server\\Helper::onBeforeShutdown)\", 1, zserv);\n    }\n\n    if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, serv->is_enable_coroutine()))) {\n        php_swoole_error(E_WARNING, \"%s->onBeforeShutdown handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n    }\n}\n\nstatic void php_swoole_server_onShutdown(Server *serv) {\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));\n    auto cb = server_object->get_callback(SW_SERVER_CB_onShutdown);\n\n    if (SWOOLE_G(enable_library)) {\n        zend::function::call(R\"(\\Swoole\\Server\\Helper::onShutdown)\", 1, zserv);\n    }\n\n    if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) {\n        php_swoole_error(E_WARNING, \"%s->onShutdown handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n    }\n}\n\nstatic void php_swoole_server_onWorkerStart(Server *serv, Worker *worker) {\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));\n    auto cb = server_object->get_callback(SW_SERVER_CB_onWorkerStart);\n\n    zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL(\"master_pid\"), serv->gs->master_pid);\n    zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL(\"manager_pid\"), serv->gs->manager_pid);\n    zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL(\"worker_id\"), worker->id);\n    zend_update_property_bool(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL(\"taskworker\"), serv->is_task_worker());\n    zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL(\"worker_pid\"), getpid());\n\n    if (serv->is_task_worker() && !serv->task_enable_coroutine && !serv->is_thread_mode()) {\n        PHPCoroutine::disable_hook();\n    }\n    serv->get_worker_message_bus()->set_allocator(sw_zend_string_allocator());\n\n    zval args[2];\n    args[0] = *zserv;\n    ZVAL_LONG(&args[1], worker->id);\n\n    if (SWOOLE_G(enable_library)) {\n        zend::function::call(R\"(\\Swoole\\Server\\Helper::onWorkerStart)\", 2, args);\n    }\n\n    if (cb && UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, serv->is_enable_coroutine()))) {\n        php_swoole_error(E_WARNING, \"%s->onWorkerStart handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n    }\n}\n\nstatic void php_swoole_server_onBeforeReload(Server *serv) {\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));\n    auto cb = server_object->get_callback(SW_SERVER_CB_onBeforeReload);\n\n    if (SWOOLE_G(enable_library)) {\n        zend::function::call(R\"(\\Swoole\\Server\\Helper::onBeforeReload)\", 1, zserv);\n    }\n\n    if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) {\n        php_swoole_error(E_WARNING, \"%s->onBeforeReload handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n    }\n}\n\nstatic void php_swoole_server_onAfterReload(Server *serv) {\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));\n    auto cb = server_object->get_callback(SW_SERVER_CB_onAfterReload);\n\n    if (SWOOLE_G(enable_library)) {\n        zend::function::call(R\"(\\Swoole\\Server\\Helper::onAfterReload)\", 1, zserv);\n    }\n\n    if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) {\n        php_swoole_error(E_WARNING, \"%s->onAfterReload handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n    }\n}\n\nstatic void php_swoole_server_onWorkerStop(Server *serv, Worker *worker) {\n    if (!worker->is_running()) {\n        return;\n    }\n\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));\n    auto cb = server_object->get_callback(SW_SERVER_CB_onWorkerStop);\n\n    zval args[2];\n    args[0] = *zserv;\n    ZVAL_LONG(&args[1], worker->id);\n\n    if (SWOOLE_G(enable_library)) {\n        zend::function::call(R\"(\\Swoole\\Server\\Helper::onWorkerStop)\", 2, args);\n    }\n\n    if (cb && UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, false))) {\n        php_swoole_error(E_WARNING, \"%s->onWorkerStop handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n    }\n}\n\nstatic void php_swoole_server_onWorkerExit(Server *serv, Worker *worker) {\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));\n    auto fci_cache = server_object->get_callback(SW_SERVER_CB_onWorkerExit);\n\n    zval args[2];\n    args[0] = *zserv;\n    ZVAL_LONG(&args[1], worker->id);\n\n    if (SWOOLE_G(enable_library)) {\n        zend::function::call(R\"(\\Swoole\\Server\\Helper::onWorkerExit)\", 2, args);\n    }\n\n    if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, false))) {\n        php_swoole_error(E_WARNING, \"%s->onWorkerExit handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n    }\n}\n\nstatic void php_swoole_server_onUserWorkerStart(Server *serv, Worker *worker) {\n    zval *object;\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n\n    if (serv->is_thread_mode()) {\n        ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));\n        int index = worker->id - serv->worker_num - serv->task_worker_num;\n        object = server_object->property->user_processes[index];\n        serv->get_worker_message_bus()->set_allocator(sw_zend_string_allocator());\n    } else {\n        object = (zval *) worker->ptr;\n    }\n\n    zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"id\"), worker->id);\n    zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL(\"master_pid\"), serv->gs->master_pid);\n    zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL(\"manager_pid\"), serv->gs->manager_pid);\n\n    php_swoole_process_start(worker, object);\n}\n\nstatic void php_swoole_server_onWorkerError(Server *serv, Worker *worker, const ExitStatus &exit_status) {\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));\n    auto fci_cache = server_object->get_callback(SW_SERVER_CB_onWorkerError);\n\n    zval args[5];\n    int argc;\n    args[0] = *zserv;\n\n    if (serv->event_object) {\n        zval *object = &args[1];\n        object_init_ex(object, swoole_server_status_info_ce);\n        zend_update_property_long(\n            swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"worker_id\"), worker->id);\n        zend_update_property_long(\n            swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"worker_pid\"), exit_status.get_pid());\n        zend_update_property_long(\n            swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"status\"), exit_status.get_status());\n        zend_update_property_long(\n            swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"exit_code\"), exit_status.get_code());\n        zend_update_property_long(\n            swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"signal\"), exit_status.get_signal());\n        argc = 2;\n    } else {\n        ZVAL_LONG(&args[1], worker->id);\n        ZVAL_LONG(&args[2], exit_status.get_pid());\n        ZVAL_LONG(&args[3], exit_status.get_code());\n        ZVAL_LONG(&args[4], exit_status.get_signal());\n        argc = 5;\n    }\n\n    if (SWOOLE_G(enable_library)) {\n        zend::function::call(R\"(\\Swoole\\Server\\Helper::onWorkerError)\", 1, zserv);\n    }\n\n    if (fci_cache && UNEXPECTED(!zend::function::call(fci_cache, argc, args, nullptr, false))) {\n        php_swoole_error(E_WARNING, \"%s->onWorkerError handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n    }\n\n    if (serv->event_object) {\n        zval_ptr_dtor(&args[1]);\n    }\n}\n\nvoid php_swoole_server_onConnect(Server *serv, DataHead *info) {\n    auto cb = php_swoole_server_get_callback(serv, info->server_fd, SW_SERVER_CB_onConnect);\n    if (!cb) {\n        return;\n    }\n\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    zval args[3];\n    int argc;\n    args[0] = *zserv;\n\n    if (serv->event_object) {\n        zval *object = &args[1];\n        object_init_ex(object, swoole_server_event_ce);\n        zend_update_property_long(swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"fd\"), (zend_long) info->fd);\n        zend_update_property_long(\n            swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"reactor_id\"), (zend_long) info->reactor_id);\n        zend_update_property_double(\n            swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"dispatch_time\"), info->time);\n        argc = 2;\n    } else {\n        ZVAL_LONG(&args[1], info->fd);\n        ZVAL_LONG(&args[2], info->reactor_id);\n        argc = 3;\n    }\n\n    if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->enable_coroutine))) {\n        php_swoole_error(E_WARNING, \"%s->onConnect handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n    }\n\n    if (serv->event_object) {\n        zval_ptr_dtor(&args[1]);\n    }\n}\n\nvoid php_swoole_server_onClose(Server *serv, DataHead *info) {\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));\n    SessionId session_id = info->fd;\n\n    if (serv->enable_coroutine && serv->send_yield) {\n        auto _i_co_list = server_object->property->send_coroutine_map.find(session_id);\n        if (_i_co_list != server_object->property->send_coroutine_map.end()) {\n            auto co_list = _i_co_list->second;\n            server_object->property->send_coroutine_map.erase(session_id);\n            while (!co_list->empty()) {\n                Coroutine *co = co_list->front();\n                co_list->pop_front();\n                swoole_set_last_error(ECONNRESET);\n                co->resume();\n            }\n            delete co_list;\n        }\n    }\n\n    auto *cb = php_swoole_server_get_callback(serv, info->server_fd, SW_SERVER_CB_onClose);\n    Connection *conn = serv->get_connection_by_session_id(session_id);\n    if (!conn) {\n        return;\n    }\n    if (conn->websocket_status != swoole::websocket::STATUS_ACTIVE) {\n        ListenPort *port = serv->get_port_by_server_fd(info->server_fd);\n        if (port && port->open_websocket_protocol &&\n            php_swoole_server_isset_callback(serv, port, SW_SERVER_CB_onDisconnect)) {\n            cb = php_swoole_server_get_callback(serv, info->server_fd, SW_SERVER_CB_onDisconnect);\n        }\n    }\n    if (cb) {\n        zval *zserv = php_swoole_server_zval_ptr(serv);\n        zval args[3];\n        int argc;\n        args[0] = *zserv;\n\n        if (serv->event_object) {\n            zval *object = &args[1];\n            object_init_ex(object, swoole_server_event_ce);\n            zend_update_property_long(\n                swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"fd\"), (zend_long) session_id);\n            zend_update_property_long(\n                swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"reactor_id\"), (zend_long) info->reactor_id);\n            zend_update_property_double(\n                swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL(\"dispatch_time\"), info->time);\n            argc = 2;\n        } else {\n            ZVAL_LONG(&args[1], session_id);\n            ZVAL_LONG(&args[2], info->reactor_id);\n            argc = 3;\n        }\n\n        if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->enable_coroutine))) {\n            php_swoole_error(E_WARNING, \"%s->onClose handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n        }\n\n        if (serv->event_object) {\n            zval_ptr_dtor(&args[1]);\n        }\n    }\n    if (conn->http2_stream) {\n        php_swoole_http2_server_onClose(serv, conn->session_id);\n    }\n}\n\nvoid php_swoole_server_onBufferFull(Server *serv, DataHead *info) {\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    auto cb = php_swoole_server_get_callback(serv, info->server_fd, SW_SERVER_CB_onBufferFull);\n\n    if (cb) {\n        zval args[2];\n\n        args[0] = *zserv;\n        ZVAL_LONG(&args[1], info->fd);\n\n        if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, false))) {\n            php_swoole_error(E_WARNING, \"%s->onBufferFull handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n        }\n    }\n}\n\nvoid php_swoole_server_check_kernel_nobufs(Server *serv, SessionId session_id) {\n    if (swoole_coroutine_is_in() && serv->has_kernel_nobufs_error(session_id)) {\n        swoole::coroutine::System::sleep(0.01);\n    }\n}\n\nbool php_swoole_server_send_yield(Server *serv, SessionId session_id, zend_string *sdata) {\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv)));\n    Coroutine *co = Coroutine::get_current_safe();\n    size_t length = ZSTR_LEN(sdata);\n\n    if (length == 0) {\n        return false;\n    }\n\n    SW_LOOP {\n        auto coroutine_iterator = server_object->property->send_coroutine_map.find(session_id);\n        std::list<Coroutine *> *co_list;\n        if (coroutine_iterator == server_object->property->send_coroutine_map.end()) {\n            co_list = new std::list<Coroutine *>;\n            server_object->property->send_coroutine_map[session_id] = co_list;\n        } else {\n            co_list = coroutine_iterator->second;\n        }\n        co_list->push_back(co);\n        auto iter = std::prev(co_list->end());\n        if (!co->yield_ex(serv->send_timeout)) {\n            co_list->erase(iter);\n            return false;\n        }\n        bool rv = serv->send(session_id, ZSTR_VAL(sdata), length);\n        if (!rv && swoole_get_last_error() == SW_ERROR_OUTPUT_SEND_YIELD && serv->send_yield) {\n            continue;\n        }\n        return rv;\n    }\n\n    return false;\n}\n\nstatic int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendData *data) {\n    auto cb = (zend::Callable *) serv->private_data_3;\n    zval args[4];\n    zval *zserv = &args[0], *zfd = &args[1], *ztype = &args[2], *zdata = nullptr;\n    zval retval;\n    zend_long worker_id = -1;\n\n    *zserv = *(php_swoole_server_zval_ptr(serv));\n    ZVAL_LONG(zfd, conn ? conn->session_id : data->info.fd);\n    ZVAL_LONG(ztype, (zend_long) (data ? data->info.type : (int) SW_SERVER_EVENT_CLOSE));\n    if (data && sw_zend_function_max_num_args(cb->ptr()->function_handler) > 3) {\n        // TODO: reduce memory copy\n        zdata = &args[3];\n        ZVAL_STRINGL(zdata, data->data, data->info.len > SW_IPC_BUFFER_SIZE ? SW_IPC_BUFFER_SIZE : data->info.len);\n    }\n    HOOK_PHP_CALL_STACK(auto call_result = sw_zend_call_function_ex(nullptr, cb->ptr(), zdata ? 4 : 3, args, &retval););\n    if (UNEXPECTED(call_result != SUCCESS)) {\n        php_swoole_error(E_WARNING, \"%s->onDispatch handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n    } else if (!ZVAL_IS_NULL(&retval)) {\n        worker_id = zval_get_long(&retval);\n        if (worker_id >= (zend_long) serv->worker_num) {\n            php_swoole_fatal_error(E_WARNING, \"invalid target worker-id[\" ZEND_LONG_FMT \"]\", worker_id);\n            worker_id = -1;\n        }\n        zval_ptr_dtor(&retval);\n    }\n    if (zdata) {\n        zval_ptr_dtor(zdata);\n    }\n\n    /* the exception should only be thrown after unlocked */\n    if (UNEXPECTED(EG(exception))) {\n        zend_exception_error(EG(exception), E_ERROR);\n    }\n\n    return worker_id;\n}\n\nvoid php_swoole_server_onBufferEmpty(Server *serv, DataHead *info) {\n    zval *zserv = php_swoole_server_zval_ptr(serv);\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));\n\n    if (serv->send_yield) {\n        auto _i_co_list = server_object->property->send_coroutine_map.find(info->fd);\n        if (_i_co_list != server_object->property->send_coroutine_map.end()) {\n            auto co_list = _i_co_list->second;\n            server_object->property->send_coroutine_map.erase(info->fd);\n            while (!co_list->empty()) {\n                Coroutine *co = co_list->front();\n                co_list->pop_front();\n                co->resume();\n            }\n            delete co_list;\n        }\n    }\n\n    auto cb = php_swoole_server_get_callback(serv, info->server_fd, SW_SERVER_CB_onBufferEmpty);\n    if (cb) {\n        zval args[2];\n\n        args[0] = *zserv;\n        ZVAL_LONG(&args[1], info->fd);\n\n        if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, false))) {\n            php_swoole_error(E_WARNING, \"%s->onBufferEmpty handler error\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n        }\n    }\n}\n\nstatic void server_ctor(zval *zserv, Server *serv) {\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv));\n    *php_swoole_server_zval_ptr(serv) = *zserv;\n    server_set_ptr(zserv, serv);\n\n    /* primary port */\n    for (auto ls : serv->ports) {\n        php_swoole_server_add_port(server_object, ls);\n    }\n\n    /* iterator */\n    do {\n        zval connection_iterator;\n        object_init_ex(&connection_iterator, swoole_connection_iterator_ce);\n\n        ConnectionIterator *iterator = php_swoole_connection_iterator_get_ptr(&connection_iterator);\n        iterator->serv = serv;\n\n        zend_update_property(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL(\"connections\"), &connection_iterator);\n        zval_ptr_dtor(&connection_iterator);\n    } while (false);\n\n    /* info */\n    auto port = serv->get_primary_port();\n    zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL(\"mode\"), serv->get_mode());\n    zend_update_property_stringl(\n        swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL(\"host\"), port->host.c_str(), port->host.length());\n    zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL(\"port\"), port->get_port());\n    zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL(\"type\"), port->get_type());\n    zend_update_property_bool(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL(\"ssl\"), port->ssl);\n}\n\nstatic PHP_METHOD(swoole_server, __construct) {\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS));\n    Server *serv = server_object->serv;\n    if (serv) {\n        zend_throw_error(nullptr, \"Constructor of %s can only be called once\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        RETURN_FALSE;\n    }\n\n    zval *zserv = ZEND_THIS;\n    char *host;\n    size_t host_len = 0;\n    zend_long sock_type = SW_SOCK_TCP;\n    zend_long serv_port = 0;\n    zend_long serv_mode = Server::MODE_BASE;\n\n    if (!SWOOLE_G(cli)) {\n        zend_throw_exception_ex(\n            swoole_exception_ce, -1, \"%s can only be used in CLI mode\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n        RETURN_FALSE;\n    }\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 4)\n    Z_PARAM_STRING(host, host_len)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(serv_port)\n    Z_PARAM_LONG(serv_mode)\n    Z_PARAM_LONG(sock_type)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (serv_mode != Server::MODE_BASE && serv_mode != Server::MODE_PROCESS\n#ifdef SW_THREAD\n        && serv_mode != Server::MODE_THREAD\n#endif\n    ) {\n        swoole_set_last_error(SW_ERROR_INVALID_PARAMS);\n        zend_throw_error(nullptr, \"invalid $mode parameters %d\", (int) serv_mode);\n        RETURN_FALSE;\n    }\n\n#ifdef SW_THREAD\n    if (sw_server() && sw_server()->is_worker_thread()) {\n        server_ctor(ZEND_THIS, sw_server());\n        return;\n    }\n    if (!tsrm_is_main_thread()) {\n        swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT);\n        zend_throw_exception_ex(swoole_exception_ce, -2, \"This operation is only allowed in the main thread\");\n        RETURN_FALSE;\n    }\n#else\n    if (sw_server() != nullptr) {\n        swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT);\n        zend_throw_exception_ex(\n            swoole_exception_ce, -3, \"server is running. unable to create %s\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n        RETURN_FALSE;\n    }\n#endif\n\n    serv = new Server((enum Server::Mode) serv_mode);\n\n    if (serv_mode == Server::MODE_BASE) {\n        serv->reactor_num = 1;\n        serv->worker_num = 1;\n    }\n\n    if (serv_port == 0 && strcasecmp(host, \"SYSTEMD\") == 0) {\n        if (serv->add_systemd_socket() <= 0) {\n            zend_throw_error(nullptr, \"failed to add systemd socket\");\n            RETURN_FALSE;\n        }\n    } else {\n        auto *port = serv->add_port((SocketType) sock_type, host, serv_port);\n        if (!port) {\n            zend_throw_exception_ex(swoole_exception_ce,\n                                    swoole_get_last_error(),\n                                    \"failed to listen server port[%s:\" ZEND_LONG_FMT \"], Error: %s[%d]\",\n                                    host,\n                                    serv_port,\n                                    swoole_strerror(swoole_get_last_error()),\n                                    swoole_get_last_error());\n            RETURN_FALSE;\n        }\n    }\n\n    server_ctor(zserv, serv);\n}\n\nstatic PHP_METHOD(swoole_server, __destruct) {}\n\nstatic PHP_METHOD(swoole_server, set) {\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS));\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (serv->is_worker_thread()) {\n        swoole_set_last_error(SW_ERROR_SERVER_UNRELATED_THREAD);\n        RETURN_FALSE;\n    }\n    if (serv->is_started()) {\n        php_swoole_fatal_error(\n            E_WARNING, \"server is running, unable to execute %s->set\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        RETURN_FALSE;\n    }\n\n    zval *zset = nullptr, *ztmp;\n    HashTable *vht;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ARRAY(zset)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    vht = Z_ARRVAL_P(zset);\n\n    php_swoole_set_global_option(vht);\n    php_swoole_set_coroutine_option(vht);\n    php_swoole_set_aio_option(vht);\n\n    if (php_swoole_array_get_value(vht, \"chroot\", ztmp)) {\n        serv->chroot_ = zend::String(ztmp).to_std_string();\n    }\n    if (php_swoole_array_get_value(vht, \"user\", ztmp)) {\n        serv->user_ = zend::String(ztmp).to_std_string();\n    }\n    if (php_swoole_array_get_value(vht, \"group\", ztmp)) {\n        serv->group_ = zend::String(ztmp).to_std_string();\n    }\n    if (php_swoole_array_get_value(vht, \"daemonize\", ztmp)) {\n        serv->daemonize = zval_is_true(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"pid_file\", ztmp)) {\n        serv->pid_file = zend::String(ztmp).to_std_string();\n    }\n    if (php_swoole_array_get_value(vht, \"reactor_num\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        serv->reactor_num = SW_MAX(0, SW_MIN(v, UINT16_MAX));\n        if (serv->reactor_num == 0) {\n            serv->reactor_num = SW_CPU_NUM;\n        }\n    }\n    if (php_swoole_array_get_value(vht, \"single_thread\", ztmp)) {\n        serv->single_thread = zval_is_true(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"worker_num\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        serv->worker_num = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n        if (serv->worker_num == 0) {\n            serv->worker_num = SW_CPU_NUM;\n        }\n    }\n    if (php_swoole_array_get_value(vht, \"max_wait_time\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        serv->max_wait_time = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n    }\n    if (php_swoole_array_get_value(vht, \"max_queued_bytes\", ztmp)) {\n        zend_long v = php_swoole_parse_to_size(ztmp);\n        serv->max_queued_bytes = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n    }\n    if (php_swoole_array_get_value(vht, \"max_concurrency\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        serv->set_max_concurrency(SW_MAX(1, SW_MIN(v, UINT32_MAX)));\n        if (serv->get_worker_max_concurrency() == UINT_MAX) {\n            serv->set_worker_max_concurrency(serv->get_max_concurrency());\n        }\n    }\n    if (php_swoole_array_get_value(vht, \"worker_max_concurrency\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        serv->set_worker_max_concurrency(SW_MAX(1, SW_MIN(v, UINT32_MAX)));\n    }\n    if (php_swoole_array_get_value(vht, \"enable_coroutine\", ztmp)) {\n        serv->enable_coroutine = zval_is_true(ztmp);\n    } else {\n        serv->enable_coroutine = SwooleG.enable_coroutine;\n    }\n    if (php_swoole_array_get_value(vht, \"send_timeout\", ztmp)) {\n        serv->send_timeout = zval_get_double(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"dispatch_mode\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        serv->dispatch_mode = SW_MAX(0, SW_MIN(v, UINT8_MAX));\n    }\n    if (php_swoole_array_get_value(vht, \"send_yield\", ztmp)) {\n        serv->send_yield = zval_is_true(ztmp);\n        if (serv->send_yield &&\n            !(serv->dispatch_mode == Server::DISPATCH_FDMOD || serv->dispatch_mode == Server::DISPATCH_IPMOD)) {\n            php_swoole_error(E_WARNING, \"'send_yield' option can only be set when using dispatch_mode=2/4\");\n            serv->send_yield = false;\n        }\n    } else {\n        serv->send_yield = serv->enable_coroutine;\n    }\n    if (php_swoole_array_get_value(vht, \"dispatch_func\", ztmp)) {\n        auto fci_cache = sw_callable_create(ztmp);\n        if (fci_cache) {\n            if (serv->private_data_3) {\n                sw_callable_free(serv->private_data_3);\n            }\n            serv->private_data_3 = fci_cache;\n            serv->dispatch_func = php_swoole_server_dispatch_func;\n            serv->single_thread = true;\n        }\n    }\n    /**\n     * for dispatch_mode = 1/3\n     */\n    if (php_swoole_array_get_value(vht, \"discard_timeout_request\", ztmp)) {\n        serv->discard_timeout_request = zval_is_true(ztmp);\n    }\n    // onConnect/onClose event\n    if (php_swoole_array_get_value(vht, \"enable_unsafe_event\", ztmp)) {\n        serv->enable_unsafe_event = zval_is_true(ztmp);\n    }\n    // delay receive\n    if (php_swoole_array_get_value(vht, \"enable_delay_receive\", ztmp)) {\n        serv->enable_delay_receive = zval_is_true(ztmp);\n    }\n#if defined(__linux__) and defined(HAVE_REUSEPORT)\n    if (php_swoole_array_get_value(vht, \"enable_reuse_port\", ztmp)) {\n        serv->enable_reuse_port = zval_is_true(ztmp);\n    }\n#endif\n    // task use object\n    if (php_swoole_array_get_value(vht, \"task_use_object\", ztmp) ||\n        php_swoole_array_get_value(vht, \"task_object\", ztmp)) {\n        serv->task_object = zval_is_true(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"event_object\", ztmp)) {\n        serv->event_object = zval_is_true(ztmp);\n        if (serv->event_object) {\n            serv->task_object = true;\n        }\n    }\n    // task coroutine\n    if (php_swoole_array_get_value(vht, \"task_enable_coroutine\", ztmp)) {\n        serv->task_enable_coroutine = zval_is_true(ztmp);\n    }\n    // task_worker_num\n    if (php_swoole_array_get_value(vht, \"task_worker_num\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        serv->task_worker_num = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n    }\n    // task ipc mode, 1,2,3\n    if (php_swoole_array_get_value(vht, \"task_ipc_mode\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        serv->task_ipc_mode = SW_MAX(0, SW_MIN(v, UINT8_MAX));\n    }\n    /**\n     * Temporary file directory for task_worker\n     */\n    if (php_swoole_array_get_value(vht, \"task_tmpdir\", ztmp)) {\n        zend::String str_v(ztmp);\n        swoole_set_task_tmpdir(str_v.to_std_string());\n    }\n    // task_max_request\n    if (php_swoole_array_get_value(vht, \"task_max_request\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        serv->task_max_request = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n        // task_max_request_grace\n        if (php_swoole_array_get_value(vht, \"task_max_request_grace\", ztmp)) {\n            zend_long v2 = zval_get_long(ztmp);\n            serv->task_max_request_grace = SW_MAX(0, SW_MIN(v2, UINT32_MAX));\n        } else if (serv->task_max_request > SW_WORKER_MIN_REQUEST) {\n            serv->task_max_request_grace = serv->task_max_request / 2;\n        }\n    }\n    // max_connection\n    if (php_swoole_array_get_value(vht, \"max_connection\", ztmp) || php_swoole_array_get_value(vht, \"max_conn\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        serv->set_max_connection(SW_MAX(0, SW_MIN(v, UINT32_MAX)));\n    }\n    if (php_swoole_array_get_value(vht, \"start_session_id\", ztmp)) {\n        serv->set_start_session_id(zval_get_long(ztmp));\n    }\n    // heartbeat_check_interval\n    if (php_swoole_array_get_value(vht, \"heartbeat_check_interval\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        serv->heartbeat_check_interval = SW_MAX(0, SW_MIN(v, UINT16_MAX));\n    } else if (php_swoole_array_get_value(vht, \"heartbeat_idle_time\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        serv->heartbeat_check_interval = v > 2 ? v / 2 : 1;\n    }\n    // max_request\n    if (php_swoole_array_get_value(vht, \"max_request\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        serv->max_request = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n        // max_request_grace\n        if (php_swoole_array_get_value(vht, \"max_request_grace\", ztmp)) {\n            zend_long v2 = zval_get_long(ztmp);\n            serv->max_request_grace = SW_MAX(0, SW_MIN(v2, UINT32_MAX));\n        } else if (serv->max_request > SW_WORKER_MIN_REQUEST) {\n            serv->max_request_grace = serv->max_request / 2;\n        }\n    }\n    // reload async\n    if (php_swoole_array_get_value(vht, \"reload_async\", ztmp)) {\n        serv->reload_async = zval_is_true(ztmp);\n    }\n    // cpu affinity\n    if (php_swoole_array_get_value(vht, \"open_cpu_affinity\", ztmp)) {\n        serv->open_cpu_affinity = zval_is_true(ztmp);\n    }\n    // cpu affinity set\n    if (php_swoole_array_get_value(vht, \"cpu_affinity_ignore\", ztmp)) {\n        int ignore_num = zend_hash_num_elements(Z_ARRVAL_P(ztmp));\n        if (ignore_num >= SW_CPU_NUM) {\n            php_swoole_fatal_error(E_ERROR, \"cpu_affinity_ignore num must be less than cpu num (%d)\", SW_CPU_NUM);\n            RETURN_FALSE;\n        }\n        int available_num = SW_CPU_NUM - ignore_num;\n        int *available_cpu = (int *) sw_malloc(sizeof(int) * available_num);\n        if (!available_cpu) {\n            php_swoole_fatal_error(E_WARNING, \"malloc() failed\");\n            RETURN_FALSE;\n        }\n        int flag, i, available_i = 0;\n\n        zval *zval_core = nullptr;\n        for (i = 0; i < SW_CPU_NUM; i++) {\n            flag = 1;\n            SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmp), zval_core)\n            if (i == zval_get_long(zval_core)) {\n                flag = 0;\n                break;\n            }\n            SW_HASHTABLE_FOREACH_END();\n            if (flag) {\n                available_cpu[available_i] = i;\n                available_i++;\n            }\n        }\n        serv->cpu_affinity_available_num = available_num;\n        if (serv->cpu_affinity_available) {\n            sw_free(serv->cpu_affinity_available);\n        }\n        serv->cpu_affinity_available = available_cpu;\n    }\n    // parse cookie header\n    if (php_swoole_array_get_value(vht, \"http_parse_cookie\", ztmp)) {\n        serv->http_parse_cookie = zval_is_true(ztmp);\n    }\n    // parse x-www-form-urlencoded form data\n    if (php_swoole_array_get_value(vht, \"http_parse_post\", ztmp)) {\n        serv->http_parse_post = zval_is_true(ztmp);\n    }\n    // parse multipart/form-data file uploads\n    if (php_swoole_array_get_value(vht, \"http_parse_files\", ztmp)) {\n        serv->http_parse_files = zval_is_true(ztmp);\n    }\n#ifdef SW_HAVE_COMPRESSION\n    // http content compression\n    if (php_swoole_array_get_value(vht, \"http_compression\", ztmp)) {\n        serv->http_compression = zval_is_true(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"http_compression_level\", ztmp) ||\n        php_swoole_array_get_value(vht, \"compression_level\", ztmp) ||\n        php_swoole_array_get_value(vht, \"http_gzip_level\", ztmp)) {\n        zend_long level = zval_get_long(ztmp);\n        if (level > UINT8_MAX) {\n            level = UINT8_MAX;\n        } else if (level < 0) {\n            level = 0;\n        }\n        serv->http_compression_level = level;\n    }\n    if (php_swoole_array_get_value(vht, \"http_compression_min_length\", ztmp) ||\n        php_swoole_array_get_value(vht, \"compression_min_length\", ztmp)) {\n        serv->compression_min_length = php_swoole_parse_to_size(ztmp);\n    }\n#endif\n\n#ifdef SW_HAVE_ZLIB\n    if (php_swoole_array_get_value(vht, \"websocket_compression\", ztmp)) {\n        serv->websocket_compression = zval_is_true(ztmp);\n    }\n#endif\n\n    // temporary directory for HTTP uploaded file.\n    if (php_swoole_array_get_value(vht, \"upload_tmp_dir\", ztmp)) {\n        zend::String str_v(ztmp);\n        if (php_swoole_create_dir(str_v.val(), str_v.len()) < 0) {\n            php_swoole_fatal_error(E_ERROR, \"Unable to create upload_tmp_dir[%s]\", str_v.val());\n            return;\n        }\n        serv->upload_tmp_dir = str_v.to_std_string();\n    }\n    if (php_swoole_array_get_value(vht, \"upload_max_filesize\", ztmp)) {\n        serv->upload_max_filesize = php_swoole_parse_to_size(ztmp);\n    }\n    /**\n     * http static file handler\n     */\n    if (php_swoole_array_get_value(vht, \"enable_static_handler\", ztmp)) {\n        serv->enable_static_handler = zval_is_true(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"document_root\", ztmp)) {\n        zend::String str_v(ztmp);\n        if (str_v.len() >= PATH_MAX) {\n            php_swoole_fatal_error(E_ERROR, \"The length of document_root must be less than %d\", PATH_MAX);\n            return;\n        }\n        serv->set_document_root(std::string(str_v.val(), str_v.len()));\n    }\n    if (php_swoole_array_get_value(vht, \"http_autoindex\", ztmp)) {\n        serv->http_autoindex = zval_is_true(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"http_index_files\", ztmp)) {\n        if (ZVAL_IS_ARRAY(ztmp)) {\n            zval *_http_index_files;\n            SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmp), _http_index_files)\n            zend::String obj_http_index_files(_http_index_files);\n            if (obj_http_index_files.len() > 0) {\n                serv->add_static_handler_index_files(obj_http_index_files.to_std_string());\n            }\n            SW_HASHTABLE_FOREACH_END();\n        } else {\n            php_swoole_fatal_error(E_ERROR, \"http_index_files must be array\");\n            RETURN_FALSE;\n        }\n    }\n    if (php_swoole_array_get_value(vht, \"http_compression_types\", ztmp) ||\n        php_swoole_array_get_value(vht, \"compression_types\", ztmp)) {\n        if (ZVAL_IS_ARRAY(ztmp)) {\n            zval *ztype;\n            SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmp), ztype)\n            zend::String type(ztype);\n            if (type.len() > 0) {\n                serv->add_http_compression_type(type.to_std_string());\n            }\n            SW_HASHTABLE_FOREACH_END();\n        } else {\n            php_swoole_fatal_error(E_ERROR, \"http_compression_types must be array\");\n            RETURN_FALSE;\n        }\n    }\n    /**\n     * [static_handler] locations\n     */\n    if (php_swoole_array_get_value(vht, \"static_handler_locations\", ztmp)) {\n        if (ZVAL_IS_ARRAY(ztmp)) {\n            zval *_location;\n            SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmp), _location)\n            zend::String obj_location(_location);\n            if (obj_location.len() > 0 && obj_location.val()[0] == '/') {\n                serv->add_static_handler_location(obj_location.to_std_string());\n            }\n            SW_HASHTABLE_FOREACH_END();\n        } else {\n            php_swoole_fatal_error(E_ERROR, \"static_handler_locations num must be array\");\n            RETURN_FALSE;\n        }\n    }\n    /**\n     * [url_rewrite] rules\n     */\n    if (php_swoole_array_get_value(vht, \"url_rewrite_rules\", ztmp)) {\n        if (ZVAL_IS_ARRAY(ztmp)) {\n            zval *replacement;\n            zend_string *pattern;\n            ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(ztmp), pattern, replacement) {\n                ZVAL_DEREF(replacement);\n                if (Z_TYPE_P(replacement) == IS_STRING) {\n                    serv->add_rewrite_rule(std::string(ZSTR_VAL(pattern), ZSTR_LEN(pattern)),\n                                           std::string(Z_STRVAL_P(replacement), Z_STRLEN_P(replacement)));\n                } else {\n                    php_swoole_fatal_error(E_ERROR, \"The `replacement` must be string\");\n                    RETURN_FALSE;\n                }\n            }\n            ZEND_HASH_FOREACH_END();\n        } else {\n            php_swoole_fatal_error(E_ERROR, \"The `url_rewrite_rules` must be array\");\n            RETURN_FALSE;\n        }\n    }\n    /**\n     * buffer input size\n     */\n    if (php_swoole_array_get_value(vht, \"input_buffer_size\", ztmp) ||\n        php_swoole_array_get_value(vht, \"buffer_input_size\", ztmp)) {\n        zend_long v = php_swoole_parse_to_size(ztmp);\n        serv->input_buffer_size = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n    }\n    /**\n     * buffer output size\n     */\n    if (php_swoole_array_get_value(vht, \"output_buffer_size\", ztmp) ||\n        php_swoole_array_get_value(vht, \"buffer_output_size\", ztmp)) {\n        zend_long v = php_swoole_parse_to_size(ztmp);\n        serv->output_buffer_size = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n    }\n    // message queue key\n    if (php_swoole_array_get_value(vht, \"message_queue_key\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        serv->message_queue_key = SW_MAX(0, SW_MIN(v, INT64_MAX));\n    }\n#ifdef SW_THREAD\n    // bootstrap\n    if (php_swoole_array_get_value(vht, \"bootstrap\", ztmp)) {\n        zend::object_set(ZEND_THIS, ZEND_STRL(\"bootstrap\"), ztmp);\n    } else {\n        zend::object_set(ZEND_THIS, ZEND_STRL(\"bootstrap\"), SG(request_info).path_translated);\n    }\n    // thread arguments\n    if (php_swoole_array_get_value(vht, \"init_arguments\", ztmp)) {\n        server_object->init_arguments = *ztmp;\n    } else {\n        ZVAL_NULL(&server_object->init_arguments);\n    }\n#endif\n\n#ifndef HAVE_MSGQUEUE\n    if (serv->task_ipc_mode == Server::TASK_IPC_MSGQUEUE || serv->task_ipc_mode == Server::TASK_IPC_PREEMPTIVE) {\n        php_swoole_fatal_error(E_ERROR, \"not support `sysvmsg`\");\n        RETURN_FALSE;\n    }\n#endif\n\n    if (serv->task_enable_coroutine &&\n        (serv->task_ipc_mode == Server::TASK_IPC_MSGQUEUE || serv->task_ipc_mode == Server::TASK_IPC_PREEMPTIVE)) {\n        php_swoole_fatal_error(E_ERROR, \"cannot use msgqueue when `task_enable_coroutine` is enable\");\n        RETURN_FALSE;\n    }\n\n    sw_zend_call_method_with_1_params(\n        server_object->property->ports.at(0), swoole_server_port_ce, nullptr, \"set\", nullptr, zset);\n\n    zval *zsetting = sw_zend_read_and_convert_property_array(swoole_server_ce, ZEND_THIS, ZEND_STRL(\"setting\"), 0);\n    php_array_merge(Z_ARRVAL_P(zsetting), Z_ARRVAL_P(zset));\n\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_server, on) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (!serv->is_worker_thread() && serv->is_started()) {\n        php_swoole_fatal_error(E_WARNING, \"server is running, unable to register event callback function\");\n        RETURN_FALSE;\n    }\n\n    zval *name;\n    zval *cb;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_ZVAL(name)\n    Z_PARAM_ZVAL(cb)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zend::String _event_name_ori(name);\n    zend::String _event_name_tolower(zend_string_tolower(_event_name_ori.get()), false);\n\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS));\n\n    auto i = server_event_map.find(_event_name_tolower.to_std_string());\n    if (i == server_event_map.end()) {\n        zval *port_object = server_object->property->ports.at(0);\n        zval retval;\n        sw_zend_call_method_with_2_params(port_object, swoole_server_port_ce, nullptr, \"on\", &retval, name, cb);\n        RETURN_BOOL(Z_BVAL_P(&retval));\n    } else {\n        int event_type = i->second.type;\n        std::string property_name = \"on\" + i->second.name;\n\n        zend_update_property(\n            swoole_server_ce, SW_Z8_OBJ_P(ZEND_THIS), property_name.c_str(), property_name.length(), cb);\n\n        if (server_object->property->callbacks[event_type]) {\n            sw_callable_free(server_object->property->callbacks[event_type]);\n        }\n\n        auto fci_cache = sw_callable_create(cb);\n        if (!fci_cache) {\n            RETURN_FALSE;\n        }\n\n        server_object->property->callbacks[event_type] = fci_cache;\n\n        RETURN_TRUE;\n    }\n}\n\nstatic PHP_METHOD(swoole_server, getCallback) {\n    zval *name;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ZVAL(name)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zend::String _event_name_ori(name);\n    zend::String _event_name_tolower(zend_string_tolower(_event_name_ori.get()), false);\n    auto i = server_event_map.find(_event_name_tolower.to_std_string());\n    if (i != server_event_map.end()) {\n        std::string property_name = \"on\" + i->second.name;\n        // Notice: we should use Z_OBJCE_P instead of swoole_server_ce, because we need to consider the subclasses.\n        zval rv,\n            *property = zend_read_property(\n                Z_OBJCE_P(ZEND_THIS), SW_Z8_OBJ_P(ZEND_THIS), property_name.c_str(), property_name.length(), true, &rv);\n        if (!ZVAL_IS_NULL(property)) {\n            RETURN_ZVAL(property, 1, 0);\n        }\n    }\n\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS));\n    sw_zend_call_method_with_1_params(\n        server_object->property->ports.at(0), swoole_server_port_ce, nullptr, \"getcallback\", return_value, name);\n}\n\nstatic PHP_METHOD(swoole_server, listen) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (!serv->is_worker_thread() && serv->is_started()) {\n        php_swoole_fatal_error(E_WARNING, \"server is running, cannot add listener\");\n        RETURN_FALSE;\n    }\n\n    char *host;\n    size_t host_len;\n    zend_long sock_type;\n    zend_long port;\n\n    ZEND_PARSE_PARAMETERS_START(3, 3)\n    Z_PARAM_STRING(host, host_len)\n    Z_PARAM_LONG(port)\n    Z_PARAM_LONG(sock_type)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    ListenPort *ls;\n    if (serv->is_worker_thread()) {\n        ls = serv->get_port((enum swSocketType) sock_type, host, (int) port);\n    } else {\n        ls = serv->add_port((enum swSocketType) sock_type, host, (int) port);\n    }\n    if (!ls) {\n        RETURN_FALSE;\n    }\n\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS));\n    zval *port_object = php_swoole_server_add_port(server_object, ls);\n    RETURN_ZVAL(port_object, 1, 0);\n}\n\nextern Worker *php_swoole_process_get_and_check_worker(const zval *zobject);\n\nstatic PHP_METHOD(swoole_server, addProcess) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (!serv->is_worker_thread() && serv->is_started()) {\n        php_swoole_fatal_error(E_WARNING, \"server is running, cannot add process\");\n        RETURN_FALSE;\n    }\n\n    int worker_id;\n    Worker *worker;\n    zval *process;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ZVAL(process)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (!ZVAL_IS_OBJECT(process) || !instanceof_function(Z_OBJCE_P(process), swoole_process_ce)) {\n        php_swoole_fatal_error(E_ERROR, \"object is not instanceof swoole_process\");\n        RETURN_FALSE;\n    }\n\n    if (serv->onUserWorkerStart == nullptr) {\n        serv->onUserWorkerStart = php_swoole_server_onUserWorkerStart;\n    }\n\n    zval *tmp_process = (zval *) emalloc(sizeof(zval));\n    memcpy(tmp_process, process, sizeof(zval));\n    process = tmp_process;\n    Z_TRY_ADDREF_P(process);\n\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS));\n    server_object->property->user_processes.push_back(process);\n\n    if (serv->is_worker_thread()) {\n        if (!serv->is_user_worker()) {\n            swoole_set_last_error(SW_ERROR_SERVER_UNRELATED_THREAD);\n            RETURN_FALSE;\n        }\n        worker_id = swoole_get_worker_id();\n        worker = serv->get_worker(worker_id);\n        worker->redirect_stdin = worker->redirect_stdout = worker->redirect_stderr = false;\n        worker_id -= serv->get_core_worker_num();\n    } else {\n        worker = php_swoole_process_get_and_check_worker(process);\n        worker_id = serv->add_worker(worker);\n        if (worker_id < 0) {\n            php_swoole_fatal_error(E_WARNING, \"failed to add worker\");\n            RETURN_FALSE;\n        }\n        worker->ptr = process;\n    }\n    zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(process), ZEND_STRL(\"id\"), worker_id);\n    RETURN_LONG(worker_id);\n}\n\nstatic PHP_METHOD(swoole_server, addCommand) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (serv->is_started()) {\n        php_swoole_fatal_error(E_WARNING, \"server is running, can't add command\");\n        RETURN_FALSE;\n    }\n\n    char *name;\n    size_t l_name;\n    zend_long accepted_process_types;\n    zval *zfn;\n\n    ZEND_PARSE_PARAMETERS_START(3, 3)\n    Z_PARAM_STRING(name, l_name)\n    Z_PARAM_LONG(accepted_process_types)\n    Z_PARAM_ZVAL(zfn)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (accepted_process_types & Server::Command::REACTOR_THREAD) {\n        php_swoole_fatal_error(E_WARNING, \"only support worker/task_worker process\");\n        RETURN_FALSE;\n    }\n\n    auto cb = sw_callable_create(zfn);\n    if (!cb) {\n        RETURN_FALSE;\n    }\n\n    Server::Command::Handler fn = [cb](Server *serv, const std::string &msg) {\n        zval *zserv = php_swoole_server_zval_ptr(serv);\n        zval argv[2];\n        argv[0] = *zserv;\n        ZVAL_STRINGL(&argv[1], msg.c_str(), msg.length());\n        zval return_value;\n\n        if (UNEXPECTED(!zend::function::call(cb, 2, argv, &return_value, false))) {\n            php_swoole_fatal_error(E_WARNING, \"%s: command handler error\", ZSTR_VAL(swoole_server_ce->name));\n            return std::string(R\"({\"data\": \"failed to call function\", \"code\": -1})\");\n        }\n\n        if (!ZVAL_IS_STRING(&return_value)) {\n            return std::string(R\"({\"data\": \"wrong return type\", \"code\": -2})\");\n        }\n\n        return std::string(Z_STRVAL(return_value), Z_STRLEN(return_value));\n    };\n\n    if (!serv->add_command(std::string(name, l_name), accepted_process_types, fn)) {\n        sw_callable_free(cb);\n        RETURN_FALSE;\n    }\n\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS));\n    server_object->property->command_callbacks.push_back(cb);\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_server, start) {\n    zval *zserv = ZEND_THIS;\n    Server *serv = php_swoole_server_get_and_check_server(zserv);\n\n#ifdef SW_THREAD\n    if (serv->is_worker_thread()) {\n        zval *zsetting = sw_zend_read_and_convert_property_array(Z_OBJCE_P(ZEND_THIS), zserv, ZEND_STRL(\"setting\"), 0);\n        php_swoole_unserialize((zend_string *) serv->private_data_4, zsetting);\n\n        auto ht = Z_ARRVAL_P(zsetting);\n        /**\n         * The coroutine configurations are thread-local variables,\n         * and each worker thread must reset them once.\n         */\n        php_swoole_set_coroutine_option(ht);\n\n        worker_thread_fn();\n        RETURN_TRUE;\n    }\n#endif\n\n    if (serv->is_started()) {\n        php_swoole_fatal_error(\n            E_WARNING, \"The server is running, unable to execute %s->start()\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n        RETURN_FALSE;\n    }\n    if (serv->is_shutdown()) {\n        php_swoole_fatal_error(\n            E_WARNING, \"The server have been shutdown, unable to execute %s->start()\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n        RETURN_FALSE;\n    }\n\n    if (sw_reactor()) {\n        php_swoole_fatal_error(\n            E_WARNING, \"The event-loop has already been created, unable to start %s\", SW_Z_OBJCE_NAME_VAL_P(zserv));\n        RETURN_FALSE;\n    }\n\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv)));\n\n#ifdef SW_THREAD\n    zend_string *bootstrap = nullptr;\n    ZendArray *thread_argv = nullptr;\n\n    if (serv->is_thread_mode()) {\n        zval *_bootstrap = zend::object_get(ZEND_THIS, ZEND_STRL(\"bootstrap\"));\n        bootstrap = zend_string_dup(Z_STR_P(_bootstrap), true);\n\n        if (!ZVAL_IS_NULL(&server_object->init_arguments)) {\n            zval _thread_argv;\n            call_user_function(NULL, nullptr, &server_object->init_arguments, &_thread_argv, 0, nullptr);\n            if (ZVAL_IS_ARRAY(&_thread_argv)) {\n                thread_argv = ZendArray::from(Z_ARRVAL(_thread_argv));\n            }\n            zval_ptr_dtor(&_thread_argv);\n        }\n\n        serv->worker_thread_start = [bootstrap, thread_argv](std::shared_ptr<Thread> thread, const WorkerFn &fn) {\n            worker_thread_fn = fn;\n            zend_string *bootstrap_copy = zend_string_dup(bootstrap, true);\n            if (thread_argv) {\n                thread_argv->add_ref();\n            }\n            php_swoole_thread_start(thread, bootstrap_copy, thread_argv);\n        };\n\n        // The runtime hook must be enabled before creating child threads.\n        if (PHPCoroutine::get_hook_flags() > 0) {\n            PHPCoroutine::enable_hook(PHPCoroutine::get_hook_flags());\n        }\n    }\n#endif\n\n    server_object->register_callback();\n    server_object->on_before_start();\n\n    if (serv->start() < 0) {\n        php_swoole_fatal_error(E_ERROR, \"failed to start server. Error: %s\", serv->get_startup_error_message());\n    }\n\n#ifdef SW_THREAD\n    if (bootstrap) {\n        zend_string_release(bootstrap);\n    }\n    if (thread_argv) {\n        thread_argv->del_ref();\n    }\n#endif\n\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_server, send) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zval *zfd;\n    zend_string *sdata;\n    zend_long server_socket = -1;\n\n    ZEND_PARSE_PARAMETERS_START(2, 3)\n    Z_PARAM_ZVAL(zfd)\n    Z_PARAM_STR(sdata)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(server_socket)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (UNEXPECTED(ZVAL_IS_NULL(zfd))) {\n        php_swoole_fatal_error(E_WARNING, \"fd can not be null\");\n        RETURN_FALSE;\n    }\n\n    auto data = ZSTR_VAL(sdata);\n    size_t length = ZSTR_LEN(sdata);\n\n    if (length == 0) {\n        php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, \"the data sent must not be empty\");\n        RETURN_FALSE;\n    }\n\n    // UNIX DGRAM SOCKET\n    if (serv->have_dgram_sock && Z_TYPE_P(zfd) == IS_STRING && Z_STRVAL_P(zfd)[0] == '/') {\n        network::Socket *sock = server_socket == -1 ? serv->dgram_socket : serv->get_server_socket(server_socket);\n        if (sock == nullptr) {\n            RETURN_FALSE;\n        }\n        RETURN_BOOL(sock->sendto(Z_STRVAL_P(zfd), 0, data, length) > 0);\n    }\n\n    zend_long fd = zval_get_long(zfd);\n    if (UNEXPECTED(fd <= 0)) {\n        php_swoole_fatal_error(E_WARNING, \"invalid fd[\" ZEND_LONG_FMT \"]\", fd);\n        RETURN_FALSE;\n    }\n    bool rv = serv->send(fd, data, length);\n    if (!rv && swoole_get_last_error() == SW_ERROR_OUTPUT_SEND_YIELD) {\n        rv = php_swoole_server_send_yield(serv, fd, sdata);\n    } else {\n        php_swoole_server_check_kernel_nobufs(serv, fd);\n    }\n    RETURN_BOOL(rv);\n}\n\nstatic PHP_METHOD(swoole_server, sendto) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    char *addr;\n    size_t addr_len;\n    zend_long port;\n    char *data;\n    size_t len;\n    zend_long server_socket_fd = -1;\n    enum swSocketType type;\n\n    ZEND_PARSE_PARAMETERS_START(3, 4)\n    Z_PARAM_STRING(addr, addr_len)\n    Z_PARAM_LONG(port)\n    Z_PARAM_STRING(data, len)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(server_socket_fd)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (len == 0) {\n        php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, \"the data sent must not be empty\");\n        RETURN_FALSE;\n    }\n\n    if (addr[0] == '/') {\n        type = SW_SOCK_UNIX_DGRAM;\n    } else if (strchr(addr, ':')) {\n        type = SW_SOCK_UDP6;\n    } else {\n        type = SW_SOCK_UDP;\n    }\n\n    network::Socket *server_socket = nullptr;\n\n    switch (type) {\n    case SW_SOCK_UDP:\n        if (!serv->udp_socket_ipv4) {\n            php_swoole_fatal_error(E_WARNING, \"UDP listener has to be added before executing sendto\");\n            RETURN_FALSE;\n        } else {\n            server_socket = server_socket_fd < 0 ? serv->udp_socket_ipv4 : serv->get_server_socket(server_socket_fd);\n        }\n        break;\n    case SW_SOCK_UDP6:\n        if (!serv->udp_socket_ipv6) {\n            php_swoole_fatal_error(E_WARNING, \"UDP6 listener has to be added before executing sendto\");\n            RETURN_FALSE;\n        } else {\n            server_socket = server_socket_fd < 0 ? serv->udp_socket_ipv6 : serv->get_server_socket(server_socket_fd);\n        }\n        break;\n    case SW_SOCK_UNIX_DGRAM:\n        if (!serv->dgram_socket) {\n            php_swoole_fatal_error(E_WARNING, \"UnixDgram listener has to be added before executing sendto\");\n            RETURN_FALSE;\n        } else {\n            server_socket = server_socket_fd < 0 ? serv->dgram_socket : serv->get_server_socket(server_socket_fd);\n        }\n        break;\n    default:\n        abort();\n        break;\n    }\n    SW_CHECK_RETURN(server_socket->sendto(addr, port, data, len));\n}\n\nstatic PHP_METHOD(swoole_server, sendfile) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_long fd;\n    char *filename;\n    size_t len;\n    zend_long offset = 0;\n    zend_long length = 0;\n\n    ZEND_PARSE_PARAMETERS_START(2, 4)\n    Z_PARAM_LONG(fd)\n    Z_PARAM_STRING(filename, len)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(offset)\n    Z_PARAM_LONG(length)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (serv->is_master()) {\n        php_swoole_fatal_error(E_WARNING, \"can't sendfile[%s] to the connections in master process\", filename);\n        RETURN_FALSE;\n    }\n\n    RETURN_BOOL(serv->sendfile(fd, filename, len, offset, length));\n}\n\nstatic PHP_METHOD(swoole_server, close) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_long fd;\n    zend_bool reset = false;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_LONG(fd)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_BOOL(reset)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(serv->close(fd, reset));\n}\n\nstatic PHP_METHOD(swoole_server, pause) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_long fd;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(fd)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    Connection *conn = serv->get_connection_verify(fd);\n    if (!conn) {\n        swoole_set_last_error(SW_ERROR_SESSION_NOT_EXIST);\n        RETURN_FALSE;\n    }\n    RETURN_BOOL(serv->feedback(conn, SW_SERVER_EVENT_PAUSE_RECV));\n}\n\nstatic PHP_METHOD(swoole_server, resume) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_long fd;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(fd)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    Connection *conn = serv->get_connection_verify(fd);\n    if (!conn) {\n        swoole_set_last_error(SW_ERROR_SESSION_NOT_EXIST);\n        RETURN_FALSE;\n    }\n    RETURN_BOOL(serv->feedback(conn, SW_SERVER_EVENT_RESUME_RECV));\n}\n\nstatic PHP_METHOD(swoole_server, stats) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"start_time\"), serv->gs->start_time);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"connection_num\"), serv->get_connection_num());\n    add_assoc_long_ex(return_value, ZEND_STRL(\"abort_count\"), serv->gs->abort_count);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"accept_count\"), serv->gs->accept_count);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"close_count\"), serv->gs->close_count);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"worker_num\"), serv->worker_num);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"task_worker_num\"), serv->task_worker_num);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"user_worker_num\"), serv->get_user_worker_num());\n    add_assoc_long_ex(return_value, ZEND_STRL(\"idle_worker_num\"), serv->get_idle_worker_num());\n    add_assoc_long_ex(return_value, ZEND_STRL(\"dispatch_count\"), serv->gs->dispatch_count);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"request_count\"), serv->gs->request_count);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"response_count\"), serv->gs->response_count);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"total_recv_bytes\"), serv->gs->total_recv_bytes);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"total_send_bytes\"), serv->gs->total_send_bytes);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"pipe_packet_msg_id\"), serv->gs->pipe_packet_msg_id);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"concurrency\"), serv->get_concurrency());\n    add_assoc_long_ex(return_value, ZEND_STRL(\"session_round\"), serv->gs->session_round);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"min_fd\"), serv->gs->min_fd);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"max_fd\"), serv->gs->max_fd);\n\n    if (sw_worker()) {\n        add_assoc_long_ex(return_value, ZEND_STRL(\"worker_request_count\"), sw_worker()->request_count);\n        add_assoc_long_ex(return_value, ZEND_STRL(\"worker_response_count\"), sw_worker()->response_count);\n        add_assoc_long_ex(return_value, ZEND_STRL(\"worker_dispatch_count\"), sw_worker()->dispatch_count);\n        add_assoc_long_ex(return_value, ZEND_STRL(\"worker_concurrency\"), sw_worker()->concurrency);\n    }\n\n    if (serv->task_ipc_mode > Server::TASK_IPC_UNIXSOCK && serv->get_task_worker_pool()->queue) {\n        size_t queue_num = -1;\n        size_t queue_bytes = -1;\n        if (serv->get_task_worker_pool()->queue->stat(&queue_num, &queue_bytes)) {\n            add_assoc_long_ex(return_value, ZEND_STRL(\"task_queue_num\"), queue_num);\n            add_assoc_long_ex(return_value, ZEND_STRL(\"task_queue_bytes\"), queue_bytes);\n        }\n    }\n\n    if (serv->task_worker_num > 0) {\n        add_assoc_long_ex(return_value, ZEND_STRL(\"task_idle_worker_num\"), serv->get_idle_task_worker_num());\n        add_assoc_long_ex(return_value, ZEND_STRL(\"tasking_num\"), serv->get_tasking_num());\n        add_assoc_long_ex(return_value, ZEND_STRL(\"task_count\"), serv->gs->task_count);\n    }\n\n    add_assoc_long_ex(return_value, ZEND_STRL(\"coroutine_num\"), Coroutine::count());\n    add_assoc_long_ex(return_value, ZEND_STRL(\"coroutine_peek_num\"), Coroutine::get_peak_num());\n}\n\nstatic PHP_METHOD(swoole_server, reload) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_bool only_reload_task_workers = false;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_BOOL(only_reload_task_workers)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(serv->reload(!only_reload_task_workers));\n}\n\nstatic PHP_METHOD(swoole_server, heartbeat) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_bool close_connection = false;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_BOOL(close_connection)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (serv->heartbeat_check_interval < 1) {\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n    double now = microtime();\n\n    serv->foreach_connection([serv, now, close_connection, return_value](Connection *conn) {\n        SessionId session_id = conn->session_id;\n        if (session_id <= 0) {\n            return;\n        }\n        swoole_trace(\"heartbeat check fd=%d\", conn->fd);\n        if (serv->is_healthy_connection(now, conn)) {\n            return;\n        }\n        if (close_connection) {\n            conn->close_force = 1;\n            serv->close(session_id, false);\n        }\n        add_next_index_long(return_value, session_id);\n    });\n}\n\nstatic PHP_METHOD(swoole_server, taskwait) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    if (!serv->is_worker()) {\n        php_swoole_fatal_error(E_WARNING, \"taskwait method can only be used in the worker process\");\n        RETURN_FALSE;\n    }\n\n    zval *zdata;\n    double timeout = SW_TASKWAIT_TIMEOUT;\n    zend_long dst_worker_id = -1;\n\n    ZEND_PARSE_PARAMETERS_START(1, 3)\n    Z_PARAM_ZVAL(zdata)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    Z_PARAM_LONG(dst_worker_id)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (php_swoole_server_task_check_param(serv, dst_worker_id) < 0) {\n        RETURN_FALSE;\n    }\n\n    EventData buf;\n    if (php_swoole_server_task_pack(zdata, &buf) < 0) {\n        RETURN_FALSE;\n    }\n\n    TaskId task_id = serv->get_task_id(&buf);\n\n    // coroutine\n    if (swoole_coroutine_is_in()) {\n        ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv)));\n        buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE);\n\n        TaskCo task_co{};\n        task_co.co = Coroutine::get_current_safe();\n        task_co.count = 1;\n        task_co.result = return_value;\n\n        if (!serv->task(&buf, (int *) &dst_worker_id)) {\n            RETURN_FALSE;\n        }\n\n        server_object->property->task_coroutine_map[task_id] = &task_co;\n        auto retval = task_co.co->yield_ex(timeout);\n        server_object->property->task_coroutine_map.erase(task_id);\n\n        if (!retval) {\n            RETURN_FALSE;\n        }\n    } else {\n        auto retval = serv->task_sync(&buf, (int *) &dst_worker_id, timeout);\n        if (!retval) {\n            RETURN_FALSE;\n        }\n        zval zresult;\n        auto task_result = serv->get_task_result();\n        if (!php_swoole_server_task_unpack(&zresult, task_result)) {\n            RETURN_FALSE;\n        } else {\n            RETURN_ZVAL(&zresult, 0, 0);\n        }\n    }\n}\n\nstatic PHP_METHOD(swoole_server, taskWaitMulti) {\n    if (swoole_coroutine_is_in()) {\n        return ZEND_MN(swoole_server_taskCo)(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n    }\n\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n    if (!serv->is_worker()) {\n        php_swoole_fatal_error(E_WARNING, \"taskWaitMulti method can only be used in the worker process\");\n        RETURN_FALSE;\n    }\n\n    zval *ztasks;\n    double timeout = SW_TASKWAIT_TIMEOUT;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_ZVAL(ztasks)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (php_swoole_server_task_check_param(serv, -1) < 0) {\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n\n    int n_task = php_swoole_array_length(ztasks);\n    if (n_task >= SW_MAX_CONCURRENT_TASK) {\n        php_swoole_fatal_error(E_WARNING, \"too many concurrent tasks\");\n        RETURN_FALSE;\n    }\n\n    Server::MultiTask mt(n_task);\n    mt.pack = [ztasks](uint16_t i, EventData *buf) {\n        auto *ztask = zend::array_get(ztasks, (zend_ulong) i);\n        return php_swoole_server_task_pack(ztask, buf);\n    };\n\n    mt.unpack = [return_value](uint16_t i, EventData *result) {\n        zval zresult;\n        if (php_swoole_server_task_unpack(&zresult, result)) {\n            add_index_zval(return_value, i, &zresult);\n        }\n    };\n\n    mt.fail = [return_value](uint16_t i) { add_index_bool(return_value, i, false); };\n\n    if (!serv->task_sync(mt, timeout)) {\n        zval_ptr_dtor(return_value);\n        RETURN_FALSE;\n    }\n}\n\nstatic PHP_METHOD(swoole_server, taskCo) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n    if (!serv->is_worker()) {\n        php_swoole_fatal_error(E_WARNING, \"taskCo method can only be used in the worker process\");\n        RETURN_FALSE;\n    }\n\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS));\n\n    zval *ztasks;\n    double timeout = SW_TASKWAIT_TIMEOUT;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_ZVAL(ztasks)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    int dst_worker_id = -1;\n    TaskId task_id;\n    int i = 0;\n    uint32_t n_task = php_swoole_array_length(ztasks);\n\n    if (n_task >= SW_MAX_CONCURRENT_TASK) {\n        php_swoole_fatal_error(E_WARNING, \"too many concurrent tasks\");\n        RETURN_FALSE;\n    }\n\n    if (php_swoole_server_task_check_param(serv, dst_worker_id) < 0) {\n        RETURN_FALSE;\n    }\n\n    auto *list = static_cast<TaskId *>(ecalloc(n_task, sizeof(TaskId)));\n    if (list == nullptr) {\n        RETURN_FALSE;\n    }\n\n    TaskCo task_co;\n    task_co.co = Coroutine::get_current_safe();\n\n    array_init_size(return_value, n_task);\n\n    zval *ztask;\n    SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztasks), ztask) {\n        EventData buf;\n        task_id = php_swoole_server_task_pack(ztask, &buf);\n        if (task_id < 0) {\n            php_swoole_fatal_error(E_WARNING, \"failed to pack task\");\n            goto _fail;\n        }\n        buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE);\n        dst_worker_id = -1;\n        if (!serv->task(&buf, &dst_worker_id)) {\n            task_id = -1;\n        _fail:\n            add_index_bool(return_value, i, false);\n            n_task--;\n        } else {\n            server_object->property->task_coroutine_map[task_id] = &task_co;\n        }\n        list[i] = task_id;\n        i++;\n    }\n    SW_HASHTABLE_FOREACH_END();\n\n    if (n_task == 0) {\n        swoole_set_last_error(SW_ERROR_TASK_DISPATCH_FAIL);\n        RETURN_FALSE;\n    }\n\n    task_co.result = return_value;\n    task_co.list = list;\n    task_co.count = n_task;\n\n    if (!task_co.co->yield_ex(timeout)) {\n        bool is_called_in_taskCo = strcasecmp(EX(func)->internal_function.function_name->val, \"taskCo\") == 0;\n        for (uint32_t j = 0; j < n_task; j++) {\n            if (!zend_hash_index_exists(Z_ARRVAL_P(return_value), j)) {\n                if (is_called_in_taskCo) {\n                    add_index_bool(return_value, j, false);\n                }\n                server_object->property->task_coroutine_map.erase(list[j]);\n            }\n        }\n    }\n}\n\nstatic PHP_METHOD(swoole_server, task) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n    ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS));\n\n    zval *zdata;\n    zend_long dst_worker_id = -1;\n    zval *zfn = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(1, 3)\n    Z_PARAM_ZVAL(zdata)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(dst_worker_id)\n    Z_PARAM_ZVAL(zfn)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (php_swoole_server_task_check_param(serv, dst_worker_id) < 0) {\n        RETURN_FALSE;\n    }\n\n    EventData buf;\n    TaskId task_id = php_swoole_server_task_pack(zdata, &buf);\n    if (task_id < 0) {\n        RETURN_FALSE;\n    }\n\n    if (!serv->is_worker()) {\n        buf.info.ext_flags |= SW_TASK_NOREPLY;\n    } else if (zfn && zval_is_true(zfn)) {\n        buf.info.ext_flags |= SW_TASK_CALLBACK;\n        auto cb = sw_callable_create(zfn);\n        if (!cb) {\n            RETURN_FALSE;\n        }\n        server_object->property->task_callbacks[task_id] = cb;\n    }\n\n    buf.info.ext_flags |= SW_TASK_NONBLOCK;\n\n    if (serv->task(&buf, (int *) &dst_worker_id)) {\n        RETURN_LONG(task_id);\n    } else {\n        RETURN_FALSE;\n    }\n}\n\nstatic PHP_METHOD(swoole_server, command) {\n    char *name;\n    size_t l_name;\n    zend_long process_id, process_type;\n    zval *zdata;\n    zend_bool json_decode = true;\n\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    ZEND_PARSE_PARAMETERS_START(4, 5)\n    Z_PARAM_STRING(name, l_name)\n    Z_PARAM_LONG(process_id)\n    Z_PARAM_LONG(process_type)\n    Z_PARAM_ZVAL(zdata)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_BOOL(json_decode)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    smart_str buf = {};\n    if (php_json_encode(&buf, zdata, 0) == FAILURE || !buf.s) {\n        RETURN_FALSE;\n    }\n\n    auto co = Coroutine::get_current_safe();\n    bool donot_yield = false;\n    Server::Command::Callback fn = [co, return_value, json_decode, &donot_yield](Server *serv, const std::string &msg) {\n        if (json_decode) {\n            if (php_json_decode(return_value, msg.c_str(), (int) msg.length(), true, 0) == FAILURE) {\n                RETVAL_FALSE;\n            }\n        } else {\n            ZVAL_STRINGL(return_value, msg.c_str(), msg.length());\n        }\n\n        if (co->is_suspending()) {\n            co->resume();\n        } else {\n            donot_yield = true;\n        }\n    };\n\n    if (!serv->command((uint16_t) process_id,\n                       (Server::Command::ProcessType) process_type,\n                       std::string(name, l_name),\n                       std::string(ZSTR_VAL(buf.s), ZSTR_LEN(buf.s)),\n                       fn)) {\n        smart_str_free(&buf);\n        RETURN_FALSE;\n    }\n    smart_str_free(&buf);\n    if (!donot_yield) {\n        co->yield();\n    }\n}\n\nstatic PHP_METHOD(swoole_server, sendMessage) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n    if (!serv->onPipeMessage) {\n        php_swoole_fatal_error(E_WARNING, \"onPipeMessage is null, can't use sendMessage\");\n        RETURN_FALSE;\n    }\n\n    zval *zmessage;\n    zend_long worker_id = -1;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_ZVAL(zmessage)\n    Z_PARAM_LONG(worker_id)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if ((serv->is_worker() || serv->is_task_worker()) && worker_id == swoole_get_worker_id()) {\n        php_swoole_fatal_error(E_WARNING, \"can't send messages to self\");\n        RETURN_FALSE;\n    }\n    if (worker_id < 0 || worker_id >= (long) serv->get_core_worker_num()) {\n        php_swoole_fatal_error(E_WARNING, \"worker_id[%d] is invalid\", (int) worker_id);\n        RETURN_FALSE;\n    }\n\n    EventData buf;\n    if (php_swoole_server_task_pack(zmessage, &buf) < 0) {\n        RETURN_FALSE;\n    }\n\n    RETURN_BOOL(serv->send_pipe_message(worker_id, &buf));\n}\n\nstatic PHP_METHOD(swoole_server, finish) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n    if (sw_unlikely(serv->task_enable_coroutine)) {\n        php_swoole_fatal_error(E_ERROR,\n                               \"please use %s->finish instead when task_enable_coroutine is enable\",\n                               ZSTR_VAL(swoole_server_task_ce->name));\n        RETURN_FALSE;\n    }\n\n    zval *zdata;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ZVAL(zdata)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(php_swoole_server_task_finish(serv, zdata, nullptr));\n}\n\nstatic PHP_METHOD(swoole_server_task, finish) {\n    Server *serv = php_swoole_server_task_get_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zval *zdata;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ZVAL(zdata)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    DataHead *info = php_swoole_server_task_get_info(ZEND_THIS);\n    RETURN_BOOL(php_swoole_server_task_finish(serv, zdata, (EventData *) info));\n}\n\nstatic PHP_METHOD(swoole_server_task, pack) {\n    zval *zdata;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ZVAL(zdata)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    EventData buf;\n    if (php_swoole_server_task_pack(zdata, &buf) < 0) {\n        RETURN_FALSE;\n    }\n    buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_NOREPLY);\n\n    RETURN_STRINGL((char *) &buf, buf.size());\n}\n\nstatic PHP_METHOD(swoole_server_task, unpack) {\n    zval *zdata;\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ZVAL(zdata)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    auto *buf = reinterpret_cast<EventData *>(Z_STRVAL_P(zdata));\n    if (!php_swoole_server_task_unpack(return_value, buf)) {\n        RETURN_FALSE;\n    }\n}\n\nstatic PHP_METHOD(swoole_server, bind) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_long fd = 0;\n    zend_long uid = 0;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_LONG(fd)\n    Z_PARAM_LONG(uid)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (uid > UINT32_MAX || uid < INT32_MIN) {\n        php_swoole_fatal_error(E_WARNING, \"uid can not be greater than %u or less than %d\", UINT32_MAX, INT32_MIN);\n        RETURN_FALSE;\n    }\n\n    Connection *conn = serv->get_connection_verify(fd);\n    if (conn == nullptr) {\n        RETURN_FALSE;\n    }\n\n    sw_spinlock(&conn->lock);\n    if (conn->uid != 0) {\n        RETVAL_FALSE;\n    } else {\n        conn->uid = (uint32_t) uid;\n        RETVAL_TRUE;\n    }\n    sw_spinlock_release(&conn->lock);\n}\n\n#ifdef SWOOLE_SOCKETS_SUPPORT\nstatic PHP_METHOD(swoole_server, getSocket) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n\n    zend_long port = 0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(port)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    const ListenPort *lp = serv->get_port(port);\n    php_socket *socket_object = php_swoole_convert_to_socket(lp->get_fd());\n\n    if (!socket_object) {\n        RETURN_FALSE;\n    }\n    SW_ZVAL_SOCKET(return_value, socket_object);\n    zval *zsocket = sw_zval_dup(return_value);\n    Z_TRY_ADDREF_P(zsocket);\n}\n#endif\n\nstatic PHP_METHOD(swoole_server, getClientInfo) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_long fd;\n    zend_long reactor_id = -1;\n    zend_bool dont_check_connection = false;\n\n    ZEND_PARSE_PARAMETERS_START(1, 3)\n    Z_PARAM_LONG(fd)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(reactor_id)\n    Z_PARAM_BOOL(dont_check_connection)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    Connection *conn = serv->get_connection_verify(fd);\n    if (!conn) {\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n\n    if (conn->uid > 0 || serv->dispatch_mode == Server::DISPATCH_UIDMOD) {\n        add_assoc_long(return_value, \"uid\", conn->uid);\n    }\n    if (conn->worker_id > 0 || serv->dispatch_mode == Server::DISPATCH_CO_CONN_LB) {\n        add_assoc_long(return_value, \"worker_id\", conn->worker_id);\n    }\n\n    ListenPort *port = serv->get_port_by_fd(conn->fd);\n    if (port && port->open_websocket_protocol) {\n        add_assoc_long(return_value, \"websocket_status\", conn->websocket_status);\n    }\n\n    if (conn->ssl_client_cert && conn->ssl_client_cert_pid == swoole_get_worker_pid()) {\n        add_assoc_stringl(return_value, \"ssl_client_cert\", conn->ssl_client_cert->str, conn->ssl_client_cert->length);\n    }\n    // server socket\n    Connection *server_socket = serv->get_connection(conn->server_fd);\n    if (server_socket) {\n        add_assoc_long(return_value, \"server_port\", server_socket->info.get_port());\n    }\n    add_assoc_long(return_value, \"server_fd\", conn->server_fd);\n    add_assoc_long(return_value, \"socket_fd\", conn->fd);\n    add_assoc_long(return_value, \"socket_type\", conn->socket_type);\n    add_assoc_long(return_value, \"remote_port\", conn->info.get_port());\n    add_assoc_string(return_value, \"remote_ip\", (char *) conn->info.get_addr());\n    add_assoc_long(return_value, \"reactor_id\", conn->reactor_id);\n    add_assoc_long(return_value, \"connect_time\", conn->connect_time);\n    add_assoc_long(return_value, \"last_time\", (int) conn->last_recv_time);\n    add_assoc_double(return_value, \"last_recv_time\", conn->last_recv_time);\n    add_assoc_double(return_value, \"last_send_time\", conn->last_send_time);\n    add_assoc_double(return_value, \"last_dispatch_time\", conn->last_dispatch_time);\n    add_assoc_long(return_value, \"close_errno\", conn->close_errno);\n    add_assoc_long(return_value, \"recv_queued_bytes\", conn->recv_queued_bytes);\n    add_assoc_long(return_value, \"send_queued_bytes\", conn->send_queued_bytes);\n}\n\nstatic PHP_METHOD(swoole_server, getClientList) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_long start_session_id = 0;\n    zend_long find_count = 10;\n\n    ZEND_PARSE_PARAMETERS_START(0, 2)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(start_session_id)\n    Z_PARAM_LONG(find_count)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    // exceeded the maximum number of searches\n    if (find_count > SW_MAX_FIND_COUNT) {\n        php_swoole_fatal_error(E_WARNING, \"swoole connection list max_find_count=%d\", SW_MAX_FIND_COUNT);\n        RETURN_FALSE;\n    }\n\n    // copy it out to avoid being overwritten by other processes\n    int serv_max_fd = serv->get_maxfd();\n    int start_fd;\n\n    if (start_session_id == 0) {\n        start_fd = serv->get_minfd();\n    } else {\n        Connection *conn = serv->get_connection_verify(start_session_id);\n        if (!conn) {\n            RETURN_FALSE;\n        }\n        start_fd = conn->fd;\n    }\n\n    if ((int) start_fd >= serv_max_fd) {\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n    int fd = start_fd + 1;\n\n    for (; fd <= serv_max_fd; fd++) {\n        swoole_trace(\"maxfd=%d, fd=%d, find_count=\" ZEND_LONG_FMT \", start_fd=\" ZEND_LONG_FMT,\n                     serv_max_fd,\n                     fd,\n                     find_count,\n                     start_session_id);\n        Connection *conn = serv->get_connection_for_iterator(fd);\n        if (conn) {\n            SessionId session_id = conn->session_id;\n            if (session_id <= 0) {\n                continue;\n            }\n            add_next_index_long(return_value, session_id);\n            find_count--;\n        }\n        // finish fetch\n        if (find_count <= 0) {\n            break;\n        }\n    }\n}\n\nstatic PHP_METHOD(swoole_server, sendwait) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_long fd;\n    char *data;\n    size_t length;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_LONG(fd)\n    Z_PARAM_STRING(data, length)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (length == 0) {\n        php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, \"the data sent must not be empty\");\n        RETURN_FALSE;\n    }\n\n    if (serv->is_process_mode() || serv->is_task_worker()) {\n        php_swoole_fatal_error(E_WARNING, \"can only be used with base mode and must be within worker process\");\n        RETURN_FALSE;\n    }\n\n    RETURN_BOOL(serv->sendwait(fd, data, length));\n}\n\nstatic PHP_METHOD(swoole_server, exists) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_long session_id;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(session_id)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    Connection *conn = serv->get_connection_verify(session_id);\n    if (!conn || conn->closed || conn->closing) {\n        RETURN_FALSE;\n    } else {\n        RETURN_TRUE;\n    }\n}\n\nstatic PHP_METHOD(swoole_server, protect) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_long session_id;\n    zend_bool value = true;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_LONG(session_id)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_BOOL(value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    Connection *conn = serv->get_connection_verify(session_id);\n    if (!conn || conn->closed) {\n        RETURN_FALSE;\n    } else {\n        conn->protect = value;\n        RETURN_TRUE;\n    }\n}\n\nstatic PHP_METHOD(swoole_server, getWorkerId) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (!serv->is_worker() && !serv->is_task_worker()) {\n        RETURN_FALSE;\n    } else {\n        RETURN_LONG(swoole_get_worker_id());\n    }\n}\n\nstatic PHP_METHOD(swoole_server, getWorkerStatus) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_long worker_id = -1;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(worker_id)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    worker_id = worker_id < 0 ? swoole_get_worker_id() : worker_id;\n    Worker *worker = serv->get_worker(worker_id);\n    if (!worker) {\n        RETURN_FALSE;\n    } else {\n        RETURN_LONG(worker->status);\n    }\n}\n\nstatic PHP_METHOD(swoole_server, getWorkerPid) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    zend_long worker_id = -1;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(worker_id)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    worker_id = worker_id < 0 ? swoole_get_worker_id() : worker_id;\n    Worker *worker = serv->get_worker(worker_id);\n    if (!worker) {\n        RETURN_FALSE;\n    }\n    RETURN_LONG(worker->pid);\n}\n\nstatic PHP_METHOD(swoole_server, getManagerPid) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    RETURN_LONG(serv->get_manager_pid());\n}\n\nstatic PHP_METHOD(swoole_server, getMasterPid) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    RETURN_LONG(serv->get_master_pid());\n}\n\nstatic PHP_METHOD(swoole_server, shutdown) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    RETURN_BOOL(serv->shutdown());\n}\n\nstatic PHP_METHOD(swoole_server, stop) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_long worker_id = -1;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(worker_id)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(serv->kill_worker(worker_id));\n}\n\n// swoole_connection_iterator\n\nstatic PHP_METHOD(swoole_connection_iterator, __construct) {\n    zend_throw_error(nullptr, \"please use the Swoole\\\\Server->connections\");\n}\n\nstatic PHP_METHOD(swoole_connection_iterator, rewind) {\n    ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS);\n    iterator->index = 0;\n    iterator->current_fd = iterator->serv->get_minfd();\n}\n\nstatic PHP_METHOD(swoole_connection_iterator, valid) {\n    ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS);\n    int fd = iterator->current_fd;\n    int max_fd = iterator->serv->get_maxfd();\n\n    for (; fd <= max_fd; fd++) {\n        Connection *conn = iterator->serv->get_connection_for_iterator(fd);\n        if (!conn) {\n            continue;\n        }\n        SessionId session_id = conn->session_id;\n        if (session_id <= 0 ||\n            (iterator->port && (iterator->port->get_fd() < 0 || conn->server_fd != iterator->port->get_fd()))) {\n            continue;\n        }\n        iterator->session_id = session_id;\n        iterator->current_fd = fd;\n        iterator->index++;\n        RETURN_TRUE;\n    }\n\n    RETURN_FALSE;\n}\n\nstatic PHP_METHOD(swoole_connection_iterator, current) {\n    ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS);\n    RETURN_LONG(iterator->session_id);\n}\n\nstatic PHP_METHOD(swoole_connection_iterator, next) {\n    ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS);\n    iterator->current_fd++;\n}\n\nstatic PHP_METHOD(swoole_connection_iterator, key) {\n    ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS);\n    RETURN_LONG(iterator->index);\n}\n\nstatic PHP_METHOD(swoole_connection_iterator, count) {\n    ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS);\n    if (iterator->port) {\n        RETURN_LONG(iterator->port->get_connection_num());\n    } else {\n        RETURN_LONG(iterator->serv->get_connection_num());\n    }\n}\n\nstatic PHP_METHOD(swoole_connection_iterator, offsetExists) {\n    ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS);\n    zval *zserv = php_swoole_server_zval_ptr(iterator->serv);\n    zval *zfd;\n    zval retval;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ZVAL(zfd)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    sw_zend_call_method_with_1_params(zserv, swoole_server_ce, nullptr, \"exists\", &retval, zfd);\n    RETVAL_BOOL(Z_BVAL_P(&retval));\n}\n\nstatic PHP_METHOD(swoole_connection_iterator, offsetGet) {\n    ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS);\n    zval *zserv = php_swoole_server_zval_ptr(iterator->serv);\n    zval *zfd;\n    zval retval;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ZVAL(zfd)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    sw_zend_call_method_with_1_params(zserv, swoole_server_ce, nullptr, \"getClientInfo\", &retval, zfd);\n    RETVAL_ZVAL(&retval, 0, 0);\n}\n\nstatic PHP_METHOD(swoole_connection_iterator, offsetSet) {}\nstatic PHP_METHOD(swoole_connection_iterator, offsetUnset) {}\nstatic PHP_METHOD(swoole_connection_iterator, __destruct) {}\n"
  },
  {
    "path": "ext-src/swoole_server_port.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_server.h\"\n#include \"php_swoole_call_stack.h\"\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_server_port_arginfo.h\"\nEND_EXTERN_C()\n\nusing namespace swoole;\n\nstruct ServerPortEvent {\n    enum php_swoole_server_port_callback_type type;\n    std::string name;\n    ServerPortEvent(enum php_swoole_server_port_callback_type type, std::string &&name) : type(type), name(name) {}\n};\n\n// clang-format off\nstatic std::unordered_map<std::string, ServerPortEvent> server_port_event_map({\n    { \"connect\",     ServerPortEvent(SW_SERVER_CB_onConnect,     \"Connect\") },\n    { \"receive\",     ServerPortEvent(SW_SERVER_CB_onReceive,     \"Receive\") },\n    { \"close\",       ServerPortEvent(SW_SERVER_CB_onClose,       \"Close\") },\n    { \"packet\",      ServerPortEvent(SW_SERVER_CB_onPacket,      \"Packet\") },\n    { \"bufferfull\",  ServerPortEvent(SW_SERVER_CB_onBufferFull,  \"BufferFull\") },\n    { \"bufferempty\", ServerPortEvent(SW_SERVER_CB_onBufferEmpty, \"BufferEmpty\") },\n    { \"request\",     ServerPortEvent(SW_SERVER_CB_onRequest,     \"Request\") },\n    { \"handshake\",   ServerPortEvent(SW_SERVER_CB_onHandshake,   \"Handshake\") },\n    { \"beforehandshakeresponse\", ServerPortEvent(SW_SERVER_CB_onBeforeHandshakeResponse, \"BeforeHandshakeResponse\") },\n    { \"open\",        ServerPortEvent(SW_SERVER_CB_onOpen,        \"Open\") },\n    { \"message\",     ServerPortEvent(SW_SERVER_CB_onMessage,     \"Message\") },\n    { \"disconnect\",  ServerPortEvent(SW_SERVER_CB_onDisconnect,  \"Disconnect\") },\n});\n// clang-format on\n\nzend_class_entry *swoole_server_port_ce;\nstatic zend_object_handlers swoole_server_port_handlers;\n\nstruct ServerPortObject {\n    ListenPort *port;\n    ServerPortProperty property;\n    zend_object std;\n};\n\nstatic sw_inline ServerPortObject *php_swoole_server_port_fetch_object(zend_object *obj) {\n    return (ServerPortObject *) ((char *) obj - swoole_server_port_handlers.offset);\n}\n\nstatic sw_inline ListenPort *php_swoole_server_port_get_ptr(zval *zobject) {\n    return php_swoole_server_port_fetch_object(Z_OBJ_P(zobject))->port;\n}\n\nListenPort *php_swoole_server_port_get_and_check_ptr(zval *zobject) {\n    ListenPort *port = php_swoole_server_port_get_ptr(zobject);\n    if (UNEXPECTED(!port)) {\n        php_swoole_fatal_error(E_ERROR, \"Invalid instance of %s\", SW_Z_OBJCE_NAME_VAL_P(zobject));\n    }\n    return port;\n}\n\nvoid php_swoole_server_port_set_ptr(zval *zobject, ListenPort *port) {\n    php_swoole_server_port_fetch_object(Z_OBJ_P(zobject))->port = port;\n}\n\nServerPortProperty *php_swoole_server_port_get_property(zval *zobject) {\n    return &php_swoole_server_port_fetch_object(Z_OBJ_P(zobject))->property;\n}\n\nstatic ServerPortProperty *php_swoole_server_port_get_and_check_property(zval *zobject) {\n    ServerPortProperty *property = php_swoole_server_port_get_property(zobject);\n    if (UNEXPECTED(!property->serv)) {\n        php_swoole_fatal_error(E_ERROR, \"Invalid instance of %s\", SW_Z_OBJCE_NAME_VAL_P(zobject));\n    }\n    return property;\n}\n\n// Dereference from server object\nvoid php_swoole_server_port_deref(zend_object *object) {\n    ServerPortObject *server_port = php_swoole_server_port_fetch_object(object);\n    ServerPortProperty *property = &server_port->property;\n    if (property->serv) {\n        for (auto &callback : property->callbacks) {\n            if (callback) {\n                sw_callable_free(callback);\n                callback = nullptr;\n            }\n        }\n        property->serv = nullptr;\n    }\n\n    ListenPort *port = server_port->port;\n    if (port) {\n        if (port->protocol.private_data_1) {\n            sw_callable_free(port->protocol.private_data_1);\n            port->protocol.private_data_1 = nullptr;\n        }\n        server_port->port = nullptr;\n    }\n}\n\nstatic void php_swoole_server_port_free_object(zend_object *object) {\n    php_swoole_server_port_deref(object);\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *php_swoole_server_port_create_object(zend_class_entry *ce) {\n    auto *server_port = static_cast<ServerPortObject *>(zend_object_alloc(sizeof(ServerPortObject), ce));\n    zend_object_std_init(&server_port->std, ce);\n    object_properties_init(&server_port->std, ce);\n    server_port->std.handlers = &swoole_server_port_handlers;\n    return &server_port->std;\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_server_port, __construct);\nstatic PHP_METHOD(swoole_server_port, __destruct);\nstatic PHP_METHOD(swoole_server_port, on);\nstatic PHP_METHOD(swoole_server_port, set);\nstatic PHP_METHOD(swoole_server_port, getCallback);\n\n#ifdef SWOOLE_SOCKETS_SUPPORT\nstatic PHP_METHOD(swoole_server_port, getSocket);\n#endif\nSW_EXTERN_C_END\n\n// clang-format off\nconst zend_function_entry swoole_server_port_methods[] =\n{\n    PHP_ME(swoole_server_port, __construct, arginfo_class_Swoole_Server_Port___construct, ZEND_ACC_PRIVATE)\n    PHP_ME(swoole_server_port, __destruct,  arginfo_class_Swoole_Server_Port___destruct,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server_port, set,         arginfo_class_Swoole_Server_Port_set,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server_port, on,          arginfo_class_Swoole_Server_Port_on,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_server_port, getCallback, arginfo_class_Swoole_Server_Port_getCallback, ZEND_ACC_PUBLIC)\n#ifdef SWOOLE_SOCKETS_SUPPORT\n    PHP_ME(swoole_server_port, getSocket,   arginfo_class_Swoole_Server_Port_getSocket,   ZEND_ACC_PUBLIC)\n#endif\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_server_port_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_server_port, \"Swoole\\\\Server\\\\Port\", nullptr, swoole_server_port_methods);\n#ifndef SW_THREAD\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_server_port);\n#endif\n    SW_SET_CLASS_CLONEABLE(swoole_server_port, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_server_port, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(swoole_server_port,\n                               php_swoole_server_port_create_object,\n                               php_swoole_server_port_free_object,\n                               ServerPortObject,\n                               std);\n\n    zend_declare_property_null(swoole_server_port_ce, ZEND_STRL(\"onConnect\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_port_ce, ZEND_STRL(\"onReceive\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_port_ce, ZEND_STRL(\"onClose\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_port_ce, ZEND_STRL(\"onPacket\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_port_ce, ZEND_STRL(\"onBufferFull\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_port_ce, ZEND_STRL(\"onBufferEmpty\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_port_ce, ZEND_STRL(\"onRequest\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_port_ce, ZEND_STRL(\"onHandshake\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_port_ce, ZEND_STRL(\"onOpen\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_port_ce, ZEND_STRL(\"onMessage\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_port_ce, ZEND_STRL(\"onDisconnect\"), ZEND_ACC_PRIVATE);\n    zend_declare_property_null(swoole_server_port_ce, ZEND_STRL(\"onBeforeHandshakeResponse\"), ZEND_ACC_PRIVATE);\n\n    zend_declare_property_null(swoole_server_port_ce, ZEND_STRL(\"host\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_port_ce, ZEND_STRL(\"port\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_port_ce, ZEND_STRL(\"type\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_server_port_ce, ZEND_STRL(\"sock\"), -1, ZEND_ACC_PUBLIC);\n    zend_declare_property_bool(swoole_server_port_ce, ZEND_STRL(\"ssl\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_server_port_ce, ZEND_STRL(\"setting\"), ZEND_ACC_PUBLIC);\n\n    zend_declare_property_null(swoole_server_port_ce, ZEND_STRL(\"connections\"), ZEND_ACC_PUBLIC);\n}\n\n/**\n * [Master/Worker]\n */\nstatic ssize_t php_swoole_server_length_func(const Protocol *protocol, network::Socket *conn, PacketLength *pl) {\n    auto *cb = static_cast<zend::Callable *>(protocol->private_data_1);\n    zval zdata;\n    zval retval;\n    ssize_t ret = -1;\n\n    ZVAL_STRINGL(&zdata, pl->buf, pl->buf_size);\n    HOOK_PHP_CALL_STACK(auto call_result = sw_zend_call_function_ex(nullptr, cb->ptr(), 1, &zdata, &retval););\n    if (UNEXPECTED(call_result) != SUCCESS) {\n        php_swoole_fatal_error(E_WARNING, \"length function handler error\");\n    } else {\n        ret = zval_get_long(&retval);\n        zval_ptr_dtor(&retval);\n    }\n    zval_ptr_dtor(&zdata);\n\n    /* the exception should only be thrown after unlocked */\n    if (UNEXPECTED(EG(exception))) {\n        zend_exception_error(EG(exception), E_ERROR);\n    }\n\n    return ret;\n}\n\nstatic PHP_METHOD(swoole_server_port, __construct) {\n    zend_throw_error(nullptr, \"please use the Swoole\\\\Server->listen method\");\n    RETURN_FALSE;\n}\n\nstatic PHP_METHOD(swoole_server_port, __destruct) {}\n\nstatic bool php_swoole_server_set_ssl_option(zend_array *vht, SSLContext *ctx) {\n    zval *ztmp;\n    if (php_swoole_array_get_value(vht, \"ssl_cert_file\", ztmp)) {\n        zend::String str_v(ztmp);\n        if (!ctx->set_cert_file(str_v.to_std_string())) {\n            php_swoole_fatal_error(E_ERROR, \"ssl cert file[%s] not found\", str_v.val());\n            return false;\n        }\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_key_file\", ztmp)) {\n        zend::String str_v(ztmp);\n        if (!ctx->set_key_file(str_v.to_std_string())) {\n            php_swoole_fatal_error(E_ERROR, \"ssl key file[%s] not found\", str_v.val());\n            return false;\n        }\n    }\n    return true;\n}\n\nstatic PHP_METHOD(swoole_server_port, set) {\n    zval *zset = nullptr;\n    HashTable *vht;\n    zval *ztmp;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ARRAY(zset)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    vht = Z_ARRVAL_P(zset);\n\n    ListenPort *port = php_swoole_server_port_get_and_check_ptr(ZEND_THIS);\n    ServerPortProperty *property = php_swoole_server_port_get_and_check_property(ZEND_THIS);\n\n    if (port == nullptr || property == nullptr) {\n        php_swoole_fatal_error(E_ERROR, \"please use the swoole_server->listen method\");\n        return;\n    }\n\n    // backlog\n    if (php_swoole_array_get_value(vht, \"backlog\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        port->backlog = SW_MAX(0, SW_MIN(v, UINT16_MAX));\n    }\n    if (php_swoole_array_get_value(vht, \"socket_buffer_size\", ztmp)) {\n        zend_long v = php_swoole_parse_to_size(ztmp);\n        port->socket_buffer_size = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX));\n        if (port->socket_buffer_size <= 0) {\n            port->socket_buffer_size = INT_MAX;\n        }\n    }\n    /**\n     * !!! Don't set this option, for tests only.\n     */\n    if (php_swoole_array_get_value(vht, \"kernel_socket_recv_buffer_size\", ztmp)) {\n        zend_long v = php_swoole_parse_to_size(ztmp);\n        port->kernel_socket_recv_buffer_size = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX));\n        if (port->kernel_socket_recv_buffer_size <= 0) {\n            port->kernel_socket_recv_buffer_size = INT_MAX;\n        }\n    }\n    /**\n     * !!! Don't set this option, for tests only.\n     */\n    if (php_swoole_array_get_value(vht, \"kernel_socket_send_buffer_size\", ztmp)) {\n        zend_long v = php_swoole_parse_to_size(ztmp);\n        port->kernel_socket_send_buffer_size = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX));\n        if (port->kernel_socket_send_buffer_size <= 0) {\n            port->kernel_socket_send_buffer_size = INT_MAX;\n        }\n    }\n    // heartbeat idle time\n    if (php_swoole_array_get_value(vht, \"heartbeat_idle_time\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        port->heartbeat_idle_time = SW_MAX(0, SW_MIN(v, UINT16_MAX));\n    }\n    if (php_swoole_array_get_value(vht, \"buffer_high_watermark\", ztmp)) {\n        zend_long v = php_swoole_parse_to_size(ztmp);\n        port->buffer_high_watermark = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n    }\n    if (php_swoole_array_get_value(vht, \"buffer_low_watermark\", ztmp)) {\n        zend_long v = php_swoole_parse_to_size(ztmp);\n        port->buffer_low_watermark = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n    }\n    // server: tcp_nodelay\n    if (php_swoole_array_get_value(vht, \"open_tcp_nodelay\", ztmp)) {\n        port->open_tcp_nodelay = zval_is_true(ztmp);\n    } else {\n        port->open_tcp_nodelay = true;\n    }\n    // tcp_defer_accept\n    if (php_swoole_array_get_value(vht, \"tcp_defer_accept\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        port->tcp_defer_accept = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX));\n    }\n    // tcp_keepalive\n    if (php_swoole_array_get_value(vht, \"open_tcp_keepalive\", ztmp)) {\n        port->open_tcp_keepalive = zval_is_true(ztmp);\n    }\n    // buffer: eof check\n    if (php_swoole_array_get_value(vht, \"open_eof_check\", ztmp)) {\n        port->open_eof_check = zval_is_true(ztmp);\n    }\n    // buffer: split package with eof\n    if (php_swoole_array_get_value(vht, \"open_eof_split\", ztmp)) {\n        port->protocol.split_by_eof = zval_is_true(ztmp);\n        if (port->protocol.split_by_eof) {\n            port->open_eof_check = true;\n        }\n    }\n    // package eof\n    if (php_swoole_array_get_value(vht, \"package_eof\", ztmp)) {\n        zend::String str_v(ztmp);\n        port->protocol.package_eof_len = str_v.len();\n        if (port->protocol.package_eof_len == 0) {\n            php_swoole_fatal_error(E_ERROR, \"package_eof cannot be an empty string\");\n            RETURN_FALSE;\n        } else if (port->protocol.package_eof_len > SW_DATA_EOF_MAXLEN) {\n            php_swoole_fatal_error(E_ERROR, \"package_eof max length is %d\", SW_DATA_EOF_MAXLEN);\n            RETURN_FALSE;\n        }\n        memcpy(port->protocol.package_eof, str_v.val(), str_v.len());\n    }\n    // http_protocol\n    if (php_swoole_array_get_value(vht, \"open_http_protocol\", ztmp)) {\n        port->open_http_protocol = zval_is_true(ztmp);\n    }\n    // websocket protocol\n    if (php_swoole_array_get_value(vht, \"open_websocket_protocol\", ztmp)) {\n        port->open_websocket_protocol = zval_is_true(ztmp);\n        if (port->open_websocket_protocol) {\n            port->open_http_protocol = true;\n        }\n    }\n    // websocket settings\n    php_swoole_server_set_websocket_option(port, vht);\n    // http2 protocol\n    if (php_swoole_array_get_value(vht, \"open_http2_protocol\", ztmp)) {\n        port->open_http2_protocol = zval_is_true(ztmp);\n        if (port->open_http2_protocol) {\n            port->open_http_protocol = true;\n        }\n    }\n    // buffer: mqtt protocol\n    if (php_swoole_array_get_value(vht, \"open_mqtt_protocol\", ztmp)) {\n        port->open_mqtt_protocol = zval_is_true(ztmp);\n    }\n    // redis protocol\n    if (php_swoole_array_get_value(vht, \"open_redis_protocol\", ztmp)) {\n        port->open_redis_protocol = zval_get_long(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"max_idle_time\", ztmp)) {\n        double v = zval_get_double(ztmp);\n        port->max_idle_time = SW_MAX(v, SW_TIMER_MIN_SEC);\n    }\n    // tcp_keepidle\n    if (php_swoole_array_get_value(vht, \"tcp_keepidle\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        port->tcp_keepidle = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX));\n    }\n    // tcp_keepinterval\n    if (php_swoole_array_get_value(vht, \"tcp_keepinterval\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        port->tcp_keepinterval = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX));\n    }\n    // tcp_keepcount\n    if (php_swoole_array_get_value(vht, \"tcp_keepcount\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        port->tcp_keepcount = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX));\n    }\n    // tcp_user_timeout\n    if (php_swoole_array_get_value(vht, \"tcp_user_timeout\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        port->tcp_user_timeout = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX));\n    }\n    // tcp_fastopen\n    if (php_swoole_array_get_value(vht, \"tcp_fastopen\", ztmp)) {\n        port->tcp_fastopen = zval_is_true(ztmp);\n    }\n    // open length check\n    if (php_swoole_array_get_value(vht, \"open_length_check\", ztmp)) {\n        port->open_length_check = zval_is_true(ztmp);\n    }\n    // package length size\n    if (php_swoole_array_get_value(vht, \"package_length_type\", ztmp)) {\n        zend::String str_v(ztmp);\n        port->protocol.package_length_type = str_v.val()[0];\n        port->protocol.package_length_size = swoole_type_size(port->protocol.package_length_type);\n        if (port->protocol.package_length_size == 0) {\n            php_swoole_fatal_error(E_ERROR, \"unknown package_length_type, see pack(). Link: https://php.net/pack\");\n            RETURN_FALSE;\n        }\n    }\n    // package length offset\n    if (php_swoole_array_get_value(vht, \"package_length_offset\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        port->protocol.package_length_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX));\n        if (port->protocol.package_length_offset > SW_IPC_BUFFER_SIZE) {\n            php_swoole_fatal_error(E_ERROR, \"'package_length_offset' value is too large\");\n        }\n    }\n    // package body start\n    if (php_swoole_array_get_value(vht, \"package_body_offset\", ztmp) ||\n        php_swoole_array_get_value(vht, \"package_body_start\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        port->protocol.package_body_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX));\n        if (port->protocol.package_body_offset > SW_IPC_BUFFER_SIZE) {\n            php_swoole_fatal_error(E_ERROR, \"'package_body_offset' value is too large\");\n        }\n    }\n    // length function\n    if (php_swoole_array_get_value(vht, \"package_length_func\", ztmp)) {\n        auto cb = sw_callable_create(ztmp);\n        if (cb) {\n            port->protocol.get_package_length = php_swoole_server_length_func;\n            if (port->protocol.private_data_1) {\n                sw_callable_free(port->protocol.private_data_1);\n            }\n            port->protocol.private_data_1 = cb;\n            port->protocol.package_length_size = 0;\n            port->protocol.package_length_type = '\\0';\n            port->protocol.package_length_offset = SW_IPC_BUFFER_SIZE;\n            property->serv->single_thread = true;\n        }\n    }\n    /**\n     * package max length\n     */\n    if (php_swoole_array_get_value(vht, \"package_max_length\", ztmp)) {\n        zend_long v = php_swoole_parse_to_size(ztmp);\n        port->protocol.package_max_length = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n    }\n\n    if (port->ssl) {\n        if (!php_swoole_server_set_ssl_option(vht, port->get_ssl_context())) {\n            RETURN_FALSE;\n        }\n        if (php_swoole_array_get_value(vht, \"ssl_compress\", ztmp)) {\n            port->set_ssl_disable_compress(!zval_is_true(ztmp));\n        }\n        if (php_swoole_array_get_value(vht, \"ssl_protocols\", ztmp)) {\n            port->set_ssl_protocols(zval_get_long(ztmp));\n        }\n        if (php_swoole_array_get_value(vht, \"ssl_verify_peer\", ztmp)) {\n            port->set_ssl_verify_peer(zval_is_true(ztmp));\n        }\n        if (php_swoole_array_get_value(vht, \"ssl_allow_self_signed\", ztmp)) {\n            port->set_ssl_allow_self_signed(zval_is_true(ztmp));\n        }\n        if (php_swoole_array_get_value(vht, \"ssl_client_cert_file\", ztmp)) {\n            zend::String str_v(ztmp);\n            if (!port->set_ssl_client_cert_file(str_v.to_std_string())) {\n                php_swoole_fatal_error(E_ERROR, \"ssl_client_cert_file[%s] not found\", str_v.val());\n                return;\n            }\n        }\n        if (php_swoole_array_get_value(vht, \"ssl_cafile\", ztmp)) {\n            zend::String str_v(ztmp);\n            port->set_ssl_cafile(str_v.to_std_string());\n        }\n        if (php_swoole_array_get_value(vht, \"ssl_capath\", ztmp)) {\n            zend::String str_v(ztmp);\n            port->set_ssl_capath(str_v.to_std_string());\n        }\n        if (php_swoole_array_get_value(vht, \"ssl_verify_depth\", ztmp)) {\n            zend_long v = zval_get_long(ztmp);\n            port->set_ssl_verify_depth(SW_MAX(0, SW_MIN(v, UINT8_MAX)));\n        }\n        if (php_swoole_array_get_value(vht, \"ssl_prefer_server_ciphers\", ztmp)) {\n            port->set_ssl_prefer_server_ciphers(zval_is_true(ztmp));\n        }\n        if (php_swoole_array_get_value(vht, \"ssl_ciphers\", ztmp)) {\n            port->set_ssl_ciphers(zend::String(ztmp).to_std_string());\n        }\n        if (php_swoole_array_get_value(vht, \"ssl_ecdh_curve\", ztmp)) {\n            port->set_ssl_ecdh_curve(zend::String(ztmp).to_std_string());\n        }\n        if (php_swoole_array_get_value(vht, \"ssl_dhparam\", ztmp)) {\n            port->set_ssl_dhparam(zend::String(ztmp).to_std_string());\n        }\n        if (php_swoole_array_get_value(vht, \"ssl_sni_certs\", ztmp)) {\n            if (Z_TYPE_P(ztmp) != IS_ARRAY) {\n                php_swoole_fatal_error(E_WARNING, \"ssl_sni_certs requires an array mapping host names to cert paths\");\n                RETURN_FALSE;\n            }\n\n            zval *current;\n            zend_string *key;\n            zend_ulong key_index;\n\n            ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(ztmp), key_index, key, current) {\n                (void) key_index;\n                if (!key) {\n                    php_swoole_fatal_error(E_WARNING, \"ssl_sni_certs array requires string host name keys\");\n                    RETURN_FALSE;\n                }\n                if (Z_TYPE_P(current) != IS_ARRAY) {\n                    php_swoole_fatal_error(E_WARNING, \"invalid SNI_cert setting\");\n                    RETURN_FALSE;\n                }\n                auto context = port->dup_ssl_context();\n                if (!php_swoole_server_set_ssl_option(Z_ARRVAL_P(current), context.get())) {\n                    RETURN_FALSE;\n                }\n                if (!port->ssl_add_sni_cert(std::string(key->val, key->len), context)) {\n                    php_swoole_fatal_error(E_ERROR, \"ssl_add_sni_cert() failed\");\n                    RETURN_FALSE;\n                }\n            }\n            ZEND_HASH_FOREACH_END();\n        }\n\n        if (!port->get_ssl_cert_file().empty() || !port->has_sni_contexts()) {\n            if (!port->ssl_init()) {\n                php_swoole_fatal_error(E_ERROR, \"ssl_init() failed\");\n                RETURN_FALSE;\n            }\n        }\n    }\n\n    if (SWOOLE_G(enable_library)) {\n        zval params[1] = {\n            *zset,\n        };\n        zend::function::call(R\"(\\Swoole\\Server\\Helper::checkOptions)\", 1, params);\n    }\n\n    zval *zsetting = sw_zend_read_and_convert_property_array(swoole_server_port_ce, ZEND_THIS, ZEND_STRL(\"setting\"), 0);\n    php_array_merge(Z_ARRVAL_P(zsetting), Z_ARRVAL_P(zset));\n    property->zsetting = zsetting;\n}\n\nstatic PHP_METHOD(swoole_server_port, on) {\n    char *name = nullptr;\n    size_t len;\n    zval *cb;\n\n    ServerPortProperty *property = php_swoole_server_port_get_and_check_property(ZEND_THIS);\n    Server *serv = property->serv;\n    if (!serv->is_worker_thread() && serv->is_started()) {\n        php_swoole_fatal_error(E_WARNING, \"can't register event callback function after server started\");\n        RETURN_FALSE;\n    }\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_STRING(name, len)\n    Z_PARAM_ZVAL(cb)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    bool found = false;\n    for (auto &i : server_port_event_map) {\n        if (!swoole_strcaseeq(name, len, i.first.c_str(), i.first.length())) {\n            continue;\n        }\n\n        found = true;\n        int index = i.second.type;\n        std::string property_name = std::string(\"on\") + i.second.name;\n        zend_update_property(\n            swoole_server_port_ce, SW_Z8_OBJ_P(ZEND_THIS), property_name.c_str(), property_name.length(), cb);\n\n        if (property->callbacks[index]) {\n            sw_callable_free(property->callbacks[index]);\n        }\n\n        auto fci_cache = sw_callable_create(cb);\n        if (!fci_cache) {\n            RETURN_FALSE;\n        }\n        property->callbacks[index] = fci_cache;\n\n        if (index == SW_SERVER_CB_onConnect && !serv->onConnect) {\n            serv->onConnect = php_swoole_server_onConnect;\n        } else if (index == SW_SERVER_CB_onPacket && !serv->onPacket) {\n            serv->onPacket = php_swoole_server_onPacket;\n        } else if (index == SW_SERVER_CB_onClose && !serv->onClose) {\n            serv->onClose = php_swoole_server_onClose;\n        } else if (index == SW_SERVER_CB_onBufferFull && !serv->onBufferFull) {\n            serv->onBufferFull = php_swoole_server_onBufferFull;\n        } else if (index == SW_SERVER_CB_onBufferEmpty && !serv->onBufferEmpty) {\n            serv->onBufferEmpty = php_swoole_server_onBufferEmpty;\n        }\n        break;\n    }\n\n    if (!found) {\n        php_swoole_error(E_WARNING, \"unknown event types[%s]\", name);\n        RETURN_FALSE;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_server_port, getCallback) {\n    zval *name;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ZVAL(name)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zend::String _event_name_ori(name);\n    zend::String _event_name_tolower(zend_string_tolower(_event_name_ori.get()), false);\n    auto i = server_port_event_map.find(_event_name_tolower.to_std_string());\n    if (i != server_port_event_map.end()) {\n        std::string property_name = \"on\" + i->second.name;\n        zval rv, *property = zend_read_property(swoole_server_port_ce,\n                                                SW_Z8_OBJ_P(ZEND_THIS),\n                                                property_name.c_str(),\n                                                property_name.length(),\n                                                true,\n                                                &rv);\n        if (!ZVAL_IS_NULL(property)) {\n            RETURN_ZVAL(property, 1, 0);\n        }\n    }\n    RETURN_NULL();\n}\n\n#ifdef SWOOLE_SOCKETS_SUPPORT\nstatic PHP_METHOD(swoole_server_port, getSocket) {\n    ListenPort *port = php_swoole_server_port_get_and_check_ptr(ZEND_THIS);\n    php_socket *socket_object = php_swoole_convert_to_socket(port->get_fd());\n    if (!socket_object) {\n        RETURN_FALSE;\n    }\n    SW_ZVAL_SOCKET(return_value, socket_object);\n    zval *zsocket = sw_zval_dup(return_value);\n    Z_TRY_ADDREF_P(zsocket);\n}\n#endif\n"
  },
  {
    "path": "ext-src/swoole_socket_coro.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2018 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_cxx.h\"\n#include \"php_swoole_thread.h\"\n\n#include \"swoole_string.h\"\n#include \"swoole_socket.h\"\n#include \"swoole_util.h\"\n#include \"swoole_protocol.h\"\n#include \"swoole_mqtt.h\"\n\n#include \"thirdparty/php/sockets/php_sockets_cxx.h\"\n\n#include <string>\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_socket_coro_arginfo.h\"\nEND_EXTERN_C()\n\nusing swoole::HttpProxy;\nusing swoole::PacketLength;\nusing swoole::Protocol;\nusing swoole::Socks5Proxy;\nusing swoole::network::Address;\n\nzend_class_entry *swoole_socket_coro_ce;\nstatic zend_object_handlers swoole_socket_coro_handlers;\n\nstatic zend_class_entry *swoole_socket_coro_exception_ce;\nstatic zend_object_handlers swoole_socket_coro_exception_handlers;\n\nstruct SocketObject {\n    SocketImpl *socket;\n    zval zstream;\n    bool reference;\n    zend_object std;\n};\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_socket_coro, __construct);\nstatic PHP_METHOD(swoole_socket_coro, bind);\nstatic PHP_METHOD(swoole_socket_coro, listen);\nstatic PHP_METHOD(swoole_socket_coro, accept);\nstatic PHP_METHOD(swoole_socket_coro, connect);\nstatic PHP_METHOD(swoole_socket_coro, checkLiveness);\nstatic PHP_METHOD(swoole_socket_coro, getBoundCid);\nstatic PHP_METHOD(swoole_socket_coro, peek);\nstatic PHP_METHOD(swoole_socket_coro, recv);\nstatic PHP_METHOD(swoole_socket_coro, send);\nstatic PHP_METHOD(swoole_socket_coro, readVector);\nstatic PHP_METHOD(swoole_socket_coro, readVectorAll);\nstatic PHP_METHOD(swoole_socket_coro, writeVector);\nstatic PHP_METHOD(swoole_socket_coro, writeVectorAll);\nstatic PHP_METHOD(swoole_socket_coro, sendFile);\nstatic PHP_METHOD(swoole_socket_coro, recvAll);\nstatic PHP_METHOD(swoole_socket_coro, sendAll);\nstatic PHP_METHOD(swoole_socket_coro, recvPacket);\nstatic PHP_METHOD(swoole_socket_coro, recvLine);\nstatic PHP_METHOD(swoole_socket_coro, recvWithBuffer);\nstatic PHP_METHOD(swoole_socket_coro, recvfrom);\nstatic PHP_METHOD(swoole_socket_coro, sendto);\nstatic PHP_METHOD(swoole_socket_coro, getOption);\nstatic PHP_METHOD(swoole_socket_coro, setOption);\nstatic PHP_METHOD(swoole_socket_coro, setProtocol);\nstatic PHP_METHOD(swoole_socket_coro, sslHandshake);\nstatic PHP_METHOD(swoole_socket_coro, shutdown);\nstatic PHP_METHOD(swoole_socket_coro, close);\nstatic PHP_METHOD(swoole_socket_coro, cancel);\nstatic PHP_METHOD(swoole_socket_coro, getsockname);\nstatic PHP_METHOD(swoole_socket_coro, getpeername);\nstatic PHP_METHOD(swoole_socket_coro, isClosed);\nstatic PHP_METHOD(swoole_socket_coro, import);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic const zend_function_entry swoole_socket_coro_methods[] =\n{\n    PHP_ME(swoole_socket_coro, __construct,    arginfo_class_Swoole_Coroutine_Socket___construct,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, bind,           arginfo_class_Swoole_Coroutine_Socket_bind,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, listen,         arginfo_class_Swoole_Coroutine_Socket_listen,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, accept,         arginfo_class_Swoole_Coroutine_Socket_accept,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, connect,        arginfo_class_Swoole_Coroutine_Socket_connect,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, checkLiveness,  arginfo_class_Swoole_Coroutine_Socket_checkLiveness,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, getBoundCid,    arginfo_class_Swoole_Coroutine_Socket_getBoundCid,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, peek,           arginfo_class_Swoole_Coroutine_Socket_peek,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, recv,           arginfo_class_Swoole_Coroutine_Socket_recv,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, recvAll,        arginfo_class_Swoole_Coroutine_Socket_recvAll,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, recvLine,       arginfo_class_Swoole_Coroutine_Socket_recvLine,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, recvWithBuffer, arginfo_class_Swoole_Coroutine_Socket_recvWithBuffer, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, recvPacket,     arginfo_class_Swoole_Coroutine_Socket_recvPacket,     ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, send,           arginfo_class_Swoole_Coroutine_Socket_send,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, readVector,     arginfo_class_Swoole_Coroutine_Socket_readVector,     ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, readVectorAll,  arginfo_class_Swoole_Coroutine_Socket_readVectorAll,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, writeVector,    arginfo_class_Swoole_Coroutine_Socket_writeVector,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, writeVectorAll, arginfo_class_Swoole_Coroutine_Socket_writeVectorAll, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, sendFile,       arginfo_class_Swoole_Coroutine_Socket_sendFile,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, sendAll,        arginfo_class_Swoole_Coroutine_Socket_sendAll,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, recvfrom,       arginfo_class_Swoole_Coroutine_Socket_recvfrom,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, sendto,         arginfo_class_Swoole_Coroutine_Socket_sendto,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, getOption,      arginfo_class_Swoole_Coroutine_Socket_getOption,      ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, setProtocol,    arginfo_class_Swoole_Coroutine_Socket_setProtocol,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, setOption,      arginfo_class_Swoole_Coroutine_Socket_setOption,      ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, sslHandshake,  arginfo_class_Swoole_Coroutine_Socket_sslHandshake,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, shutdown,      arginfo_class_Swoole_Coroutine_Socket_shutdown,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, cancel,        arginfo_class_Swoole_Coroutine_Socket_cancel,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, close,         arginfo_class_Swoole_Coroutine_Socket_close,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, getpeername,   arginfo_class_Swoole_Coroutine_Socket_getpeername,     ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, getsockname,   arginfo_class_Swoole_Coroutine_Socket_getsockname,     ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, isClosed,      arginfo_class_Swoole_Coroutine_Socket_isClosed,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_socket_coro, import,        arginfo_class_Swoole_Coroutine_Socket_import,          ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_FE_END\n};\n// clang-format on\n\n#define swoole_get_socket_coro(_sock, _zobject)                                                                        \\\n    SocketObject *_sock = socket_coro_fetch_object(Z_OBJ_P(_zobject));                                                 \\\n    if (UNEXPECTED(!sock->socket)) {                                                                                   \\\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION, \"must call constructor first\");                                   \\\n    }                                                                                                                  \\\n    if (UNEXPECTED(_sock->socket->is_closed())) {                                                                      \\\n        zend_update_property_long(swoole_socket_coro_ce, SW_Z8_OBJ_P(_zobject), ZEND_STRL(\"errCode\"), EBADF);          \\\n        zend_update_property_string(                                                                                   \\\n            swoole_socket_coro_ce, SW_Z8_OBJ_P(_zobject), ZEND_STRL(\"errMsg\"), strerror(EBADF));                       \\\n        RETURN_FALSE;                                                                                                  \\\n    }\n\nstatic sw_inline SocketObject *socket_coro_fetch_object(zend_object *obj) {\n    return reinterpret_cast<SocketObject *>(reinterpret_cast<char *>(obj) - swoole_socket_coro_handlers.offset);\n}\n\n/**\n * cannot execute close in the destructor, it may be shutting down,\n * executing close will try to resume other coroutines.\n */\nstatic void socket_coro_free_object(zend_object *object) {\n    auto *sock = socket_coro_fetch_object(object);\n    if (!sock->reference && sock->socket) {\n        if (!Z_ISUNDEF(sock->zstream)) {\n            sock->socket->move_fd();\n            zval_ptr_dtor(&sock->zstream);\n        }\n        delete sock->socket;\n    }\n    zend_object_std_dtor(&sock->std);\n}\n\nstatic zend_object *socket_coro_create_object(zend_class_entry *ce) {\n    auto *sock = static_cast<SocketObject *>(zend_object_alloc(sizeof(SocketObject), ce));\n    zend_object_std_init(&sock->std, ce);\n    /* Even if you don't use properties yourself you should still call object_properties_init(),\n     * because extending classes may use properties. (Generally a lot of the stuff you will do is\n     * for the sake of not breaking extending classes). */\n    object_properties_init(&sock->std, ce);\n    sock->std.handlers = &swoole_socket_coro_handlers;\n    return &sock->std;\n}\n\nstatic void socket_coro_register_constants(int module_number) {\n    REGISTER_LONG_CONSTANT(\"AF_UNIX\", AF_UNIX, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"AF_INET\", AF_INET, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"AF_INET6\", AF_INET6, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SOCK_STREAM\", SOCK_STREAM, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SOCK_DGRAM\", SOCK_DGRAM, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SOCK_RAW\", SOCK_RAW, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SOCK_SEQPACKET\", SOCK_SEQPACKET, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SOCK_RDM\", SOCK_RDM, CONST_CS | CONST_PERSISTENT);\n\n    REGISTER_LONG_CONSTANT(\"MSG_OOB\", MSG_OOB, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"MSG_WAITALL\", MSG_WAITALL, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"MSG_CTRUNC\", MSG_CTRUNC, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"MSG_TRUNC\", MSG_TRUNC, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"MSG_PEEK\", MSG_PEEK, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"MSG_DONTROUTE\", MSG_DONTROUTE, CONST_CS | CONST_PERSISTENT);\n#ifdef MSG_EOR\n    REGISTER_LONG_CONSTANT(\"MSG_EOR\", MSG_EOR, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef MSG_EOF\n    REGISTER_LONG_CONSTANT(\"MSG_EOF\", MSG_EOF, CONST_CS | CONST_PERSISTENT);\n#endif\n\n#ifdef MSG_CONFIRM\n    REGISTER_LONG_CONSTANT(\"MSG_CONFIRM\", MSG_CONFIRM, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef MSG_ERRQUEUE\n    REGISTER_LONG_CONSTANT(\"MSG_ERRQUEUE\", MSG_ERRQUEUE, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef MSG_NOSIGNAL\n    REGISTER_LONG_CONSTANT(\"MSG_NOSIGNAL\", MSG_NOSIGNAL, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef MSG_DONTWAIT\n    REGISTER_LONG_CONSTANT(\"MSG_DONTWAIT\", MSG_DONTWAIT, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef MSG_MORE\n    REGISTER_LONG_CONSTANT(\"MSG_MORE\", MSG_MORE, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef MSG_WAITFORONE\n    REGISTER_LONG_CONSTANT(\"MSG_WAITFORONE\", MSG_WAITFORONE, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef MSG_CMSG_CLOEXEC\n    REGISTER_LONG_CONSTANT(\"MSG_CMSG_CLOEXEC\", MSG_CMSG_CLOEXEC, CONST_CS | CONST_PERSISTENT);\n#endif\n\n    REGISTER_LONG_CONSTANT(\"SO_DEBUG\", SO_DEBUG, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SO_REUSEADDR\", SO_REUSEADDR, CONST_CS | CONST_PERSISTENT);\n#ifdef SO_REUSEPORT\n    REGISTER_LONG_CONSTANT(\"SO_REUSEPORT\", SO_REUSEPORT, CONST_CS | CONST_PERSISTENT);\n#endif\n    REGISTER_LONG_CONSTANT(\"SO_KEEPALIVE\", SO_KEEPALIVE, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SO_DONTROUTE\", SO_DONTROUTE, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SO_LINGER\", SO_LINGER, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SO_BROADCAST\", SO_BROADCAST, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SO_OOBINLINE\", SO_OOBINLINE, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SO_SNDBUF\", SO_SNDBUF, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SO_RCVBUF\", SO_RCVBUF, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SO_SNDLOWAT\", SO_SNDLOWAT, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SO_RCVLOWAT\", SO_RCVLOWAT, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SO_SNDTIMEO\", SO_SNDTIMEO, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SO_RCVTIMEO\", SO_RCVTIMEO, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SO_TYPE\", SO_TYPE, CONST_CS | CONST_PERSISTENT);\n#ifdef SO_FAMILY\n    REGISTER_LONG_CONSTANT(\"SO_FAMILY\", SO_FAMILY, CONST_CS | CONST_PERSISTENT);\n#endif\n    REGISTER_LONG_CONSTANT(\"SO_ERROR\", SO_ERROR, CONST_CS | CONST_PERSISTENT);\n#ifdef SO_BINDTODEVICE\n    REGISTER_LONG_CONSTANT(\"SO_BINDTODEVICE\", SO_BINDTODEVICE, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef SO_USER_COOKIE\n    REGISTER_LONG_CONSTANT(\"SO_LABEL\", SO_LABEL, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SO_PEERLABEL\", SO_PEERLABEL, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SO_LISTENQLIMIT\", SO_LISTENQLIMIT, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SO_LISTENQLEN\", SO_LISTENQLEN, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SO_USER_COOKIE\", SO_USER_COOKIE, CONST_CS | CONST_PERSISTENT);\n#endif\n    REGISTER_LONG_CONSTANT(\"SOL_SOCKET\", SOL_SOCKET, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SOMAXCONN\", SOMAXCONN, CONST_CS | CONST_PERSISTENT);\n#ifdef TCP_NODELAY\n    REGISTER_LONG_CONSTANT(\"TCP_NODELAY\", TCP_NODELAY, CONST_CS | CONST_PERSISTENT);\n#endif\n\n    REGISTER_LONG_CONSTANT(\"MCAST_JOIN_GROUP\", PHP_MCAST_JOIN_GROUP, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"MCAST_LEAVE_GROUP\", PHP_MCAST_LEAVE_GROUP, CONST_CS | CONST_PERSISTENT);\n#ifdef HAS_MCAST_EXT\n    REGISTER_LONG_CONSTANT(\"MCAST_BLOCK_SOURCE\", PHP_MCAST_BLOCK_SOURCE, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"MCAST_UNBLOCK_SOURCE\", PHP_MCAST_UNBLOCK_SOURCE, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"MCAST_JOIN_SOURCE_GROUP\", PHP_MCAST_JOIN_SOURCE_GROUP, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"MCAST_LEAVE_SOURCE_GROUP\", PHP_MCAST_LEAVE_SOURCE_GROUP, CONST_CS | CONST_PERSISTENT);\n#endif\n\n    REGISTER_LONG_CONSTANT(\"IP_MULTICAST_IF\", IP_MULTICAST_IF, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"IP_MULTICAST_TTL\", IP_MULTICAST_TTL, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"IP_MULTICAST_LOOP\", IP_MULTICAST_LOOP, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"IPV6_MULTICAST_IF\", IPV6_MULTICAST_IF, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"IPV6_MULTICAST_HOPS\", IPV6_MULTICAST_HOPS, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"IPV6_MULTICAST_LOOP\", IPV6_MULTICAST_LOOP, CONST_CS | CONST_PERSISTENT);\n\n#ifdef IPV6_V6ONLY\n    REGISTER_LONG_CONSTANT(\"IPV6_V6ONLY\", IPV6_V6ONLY, CONST_CS | CONST_PERSISTENT);\n#endif\n\n#ifdef EPERM\n    /* Operation not permitted */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EPERM\", EPERM, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOENT\n    /* No such file or directory */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOENT\", ENOENT, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EINTR\n    /* Interrupted system call */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EINTR\", EINTR, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EIO\n    /* I/O error */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EIO\", EIO, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENXIO\n    /* No such device or address */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENXIO\", ENXIO, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef E2BIG\n    /* Arg list too long */\n    REGISTER_LONG_CONSTANT(\"SOCKET_E2BIG\", E2BIG, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EBADF\n    /* Bad file number */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EBADF\", EBADF, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EAGAIN\n    /* Try again */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EAGAIN\", EAGAIN, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOMEM\n    /* Out of memory */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOMEM\", ENOMEM, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EACCES\n    /* Permission denied */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EACCES\", EACCES, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EFAULT\n    /* Bad address */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EFAULT\", EFAULT, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOTBLK\n    /* Block device required */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOTBLK\", ENOTBLK, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EBUSY\n    /* Device or resource busy */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EBUSY\", EBUSY, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EEXIST\n    /* File exists */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EEXIST\", EEXIST, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EXDEV\n    /* Cross-device link */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EXDEV\", EXDEV, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENODEV\n    /* No such device */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENODEV\", ENODEV, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOTDIR\n    /* Not a directory */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOTDIR\", ENOTDIR, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EISDIR\n    /* Is a directory */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EISDIR\", EISDIR, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EINVAL\n    /* Invalid argument */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EINVAL\", EINVAL, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENFILE\n    /* File table overflow */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENFILE\", ENFILE, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EMFILE\n    /* Too many open files */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EMFILE\", EMFILE, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOTTY\n    /* Not a typewriter */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOTTY\", ENOTTY, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOSPC\n    /* No space left on device */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOSPC\", ENOSPC, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ESPIPE\n    /* Illegal seek */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ESPIPE\", ESPIPE, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EROFS\n    /* Read-only file system */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EROFS\", EROFS, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EMLINK\n    /* Too many links */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EMLINK\", EMLINK, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EPIPE\n    /* Broken pipe */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EPIPE\", EPIPE, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENAMETOOLONG\n    /* File name too long */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENAMETOOLONG\", ENAMETOOLONG, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOLCK\n    /* No record locks available */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOLCK\", ENOLCK, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOSYS\n    /* Function not implemented */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOSYS\", ENOSYS, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOTEMPTY\n    /* Directory not empty */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOTEMPTY\", ENOTEMPTY, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ELOOP\n    /* Too many symbolic links encountered */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ELOOP\", ELOOP, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EWOULDBLOCK\n    /* Operation would block */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EWOULDBLOCK\", EWOULDBLOCK, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOMSG\n    /* No message of desired type */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOMSG\", ENOMSG, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EIDRM\n    /* Identifier removed */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EIDRM\", EIDRM, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ECHRNG\n    /* Channel number out of range */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ECHRNG\", ECHRNG, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EL2NSYNC\n    /* Level 2 not synchronized */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EL2NSYNC\", EL2NSYNC, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EL3HLT\n    /* Level 3 halted */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EL3HLT\", EL3HLT, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EL3RST\n    /* Level 3 reset */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EL3RST\", EL3RST, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ELNRNG\n    /* Link number out of range */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ELNRNG\", ELNRNG, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EUNATCH\n    /* Protocol driver not attached */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EUNATCH\", EUNATCH, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOCSI\n    /* No CSI structure available */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOCSI\", ENOCSI, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EL2HLT\n    /* Level 2 halted */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EL2HLT\", EL2HLT, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EBADE\n    /* Invalid exchange */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EBADE\", EBADE, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EBADR\n    /* Invalid request descriptor */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EBADR\", EBADR, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EXFULL\n    /* Exchange full */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EXFULL\", EXFULL, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOANO\n    /* No anode */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOANO\", ENOANO, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EBADRQC\n    /* Invalid request code */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EBADRQC\", EBADRQC, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EBADSLT\n    /* Invalid slot */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EBADSLT\", EBADSLT, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOSTR\n    /* Device not a stream */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOSTR\", ENOSTR, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENODATA\n    /* No data available */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENODATA\", ENODATA, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ETIME\n    /* Timer expired */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ETIME\", ETIME, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOSR\n    /* Out of streams resources */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOSR\", ENOSR, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENONET\n    /* Machine is not on the network */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENONET\", ENONET, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EREMOTE\n    /* Object is remote */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EREMOTE\", EREMOTE, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOLINK\n    /* Link has been severed */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOLINK\", ENOLINK, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EADV\n    /* Advertise error */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EADV\", EADV, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ESRMNT\n    /* Srmount error */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ESRMNT\", ESRMNT, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ECOMM\n    /* Communication error on send */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ECOMM\", ECOMM, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EPROTO\n    /* Protocol error */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EPROTO\", EPROTO, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EMULTIHOP\n    /* Multihop attempted */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EMULTIHOP\", EMULTIHOP, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EBADMSG\n    /* Not a data message */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EBADMSG\", EBADMSG, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOTUNIQ\n    /* Name not unique on network */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOTUNIQ\", ENOTUNIQ, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EBADFD\n    /* File descriptor in bad state */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EBADFD\", EBADFD, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EREMCHG\n    /* Remote address changed */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EREMCHG\", EREMCHG, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ERESTART\n    /* Interrupted system call should be restarted */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ERESTART\", ERESTART, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ESTRPIPE\n    /* Streams pipe error */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ESTRPIPE\", ESTRPIPE, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EUSERS\n    /* Too many users */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EUSERS\", EUSERS, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOTSOCK\n    /* Socket operation on non-socket */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOTSOCK\", ENOTSOCK, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EDESTADDRREQ\n    /* Destination address required */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EDESTADDRREQ\", EDESTADDRREQ, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EMSGSIZE\n    /* Message too long */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EMSGSIZE\", EMSGSIZE, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EPROTOTYPE\n    /* Protocol wrong type for socket */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EPROTOTYPE\", EPROTOTYPE, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOPROTOOPT\n    /* Protocol not available */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOPROTOOPT\", ENOPROTOOPT, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EPROTONOSUPPORT\n    /* Protocol not supported */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EPROTONOSUPPORT\", EPROTONOSUPPORT, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ESOCKTNOSUPPORT\n    /* Socket type not supported */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ESOCKTNOSUPPORT\", ESOCKTNOSUPPORT, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EOPNOTSUPP\n    /* Operation not supported on transport endpoint */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EOPNOTSUPP\", EOPNOTSUPP, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EPFNOSUPPORT\n    /* Protocol family not supported */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EPFNOSUPPORT\", EPFNOSUPPORT, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EAFNOSUPPORT\n    /* Address family not supported by protocol */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EAFNOSUPPORT\", EAFNOSUPPORT, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EADDRINUSE\n    /* Address already in use */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EADDRINUSE\", EADDRINUSE, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EADDRNOTAVAIL\n    /* Cannot assign requested address */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EADDRNOTAVAIL\", EADDRNOTAVAIL, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENETDOWN\n    /* Network is down */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENETDOWN\", ENETDOWN, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENETUNREACH\n    /* Network is unreachable */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENETUNREACH\", ENETUNREACH, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENETRESET\n    /* Network dropped connection because of reset */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENETRESET\", ENETRESET, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ECONNABORTED\n    /* Software caused connection abort */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ECONNABORTED\", ECONNABORTED, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ECONNRESET\n    /* Connection reset by peer */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ECONNRESET\", ECONNRESET, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOBUFS\n    /* No buffer space available */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOBUFS\", ENOBUFS, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EISCONN\n    /* Transport endpoint is already connected */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EISCONN\", EISCONN, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOTCONN\n    /* Transport endpoint is not connected */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOTCONN\", ENOTCONN, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ESHUTDOWN\n    /* Cannot send after transport endpoint shutdown */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ESHUTDOWN\", ESHUTDOWN, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ETOOMANYREFS\n    /* Too many references: cannot splice */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ETOOMANYREFS\", ETOOMANYREFS, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ETIMEDOUT\n    /* Connection timed out */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ETIMEDOUT\", ETIMEDOUT, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ECONNREFUSED\n    /* Connection refused */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ECONNREFUSED\", ECONNREFUSED, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EHOSTDOWN\n    /* Host is down */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EHOSTDOWN\", EHOSTDOWN, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EHOSTUNREACH\n    /* No route to host */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EHOSTUNREACH\", EHOSTUNREACH, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EALREADY\n    /* Operation already in progress */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EALREADY\", EALREADY, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EINPROGRESS\n    /* Operation now in progress */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EINPROGRESS\", EINPROGRESS, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EISNAM\n    /* Is a named type file */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EISNAM\", EISNAM, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EREMOTEIO\n    /* Remote I/O error */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EREMOTEIO\", EREMOTEIO, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EDQUOT\n    /* Quota exceeded */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EDQUOT\", EDQUOT, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef ENOMEDIUM\n    /* No medium found */\n    REGISTER_LONG_CONSTANT(\"SOCKET_ENOMEDIUM\", ENOMEDIUM, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef EMEDIUMTYPE\n    /* Wrong medium type */\n    REGISTER_LONG_CONSTANT(\"SOCKET_EMEDIUMTYPE\", EMEDIUMTYPE, CONST_CS | CONST_PERSISTENT);\n#endif\n\n    REGISTER_LONG_CONSTANT(\"IPPROTO_IP\", IPPROTO_IP, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"IPPROTO_IPV6\", IPPROTO_IPV6, CONST_CS | CONST_PERSISTENT);\n\n    REGISTER_LONG_CONSTANT(\"SOL_TCP\", IPPROTO_TCP, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SOL_UDP\", IPPROTO_UDP, CONST_CS | CONST_PERSISTENT);\n\n    REGISTER_LONG_CONSTANT(\"IPV6_UNICAST_HOPS\", IPV6_UNICAST_HOPS, CONST_CS | CONST_PERSISTENT);\n\n    REGISTER_LONG_CONSTANT(\"AI_PASSIVE\", AI_PASSIVE, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"AI_CANONNAME\", AI_CANONNAME, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"AI_NUMERICHOST\", AI_NUMERICHOST, CONST_CS | CONST_PERSISTENT);\n#if HAVE_AI_V4MAPPED\n    REGISTER_LONG_CONSTANT(\"AI_V4MAPPED\", AI_V4MAPPED, CONST_CS | CONST_PERSISTENT);\n#endif\n#if HAVE_AI_ALL\n    REGISTER_LONG_CONSTANT(\"AI_ALL\", AI_ALL, CONST_CS | CONST_PERSISTENT);\n#endif\n    REGISTER_LONG_CONSTANT(\"AI_ADDRCONFIG\", AI_ADDRCONFIG, CONST_CS | CONST_PERSISTENT);\n#if HAVE_AI_IDN\n    REGISTER_LONG_CONSTANT(\"AI_IDN\", AI_IDN, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"AI_CANONIDN\", AI_CANONIDN, CONST_CS | CONST_PERSISTENT);\n    // REGISTER_LONG_CONSTANT(\"AI_IDN_ALLOW_UNASSIGNED\", AI_IDN_ALLOW_UNASSIGNED, CONST_CS | CONST_PERSISTENT);\n    // REGISTER_LONG_CONSTANT(\"AI_IDN_USE_STD3_ASCII_RULES\", AI_IDN_USE_STD3_ASCII_RULES, CONST_CS | CONST_PERSISTENT);\n#endif\n#ifdef AI_NUMERICSERV\n    REGISTER_LONG_CONSTANT(\"AI_NUMERICSERV\", AI_NUMERICSERV, CONST_CS | CONST_PERSISTENT);\n#endif\n}\n\nvoid php_swoole_socket_coro_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_socket_coro, \"Swoole\\\\Coroutine\\\\Socket\", \"Co\\\\Socket\", swoole_socket_coro_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_socket_coro);\n    SW_SET_CLASS_CLONEABLE(swoole_socket_coro, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_socket_coro, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(\n        swoole_socket_coro, socket_coro_create_object, socket_coro_free_object, SocketObject, std);\n\n    zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL(\"fd\"), -1, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY);\n    zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL(\"domain\"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY);\n    zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL(\"type\"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY);\n    zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL(\"protocol\"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY);\n    zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL(\"errCode\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_string(swoole_socket_coro_ce, ZEND_STRL(\"errMsg\"), \"\", ZEND_ACC_PUBLIC);\n\n#ifdef SWOOLE_SOCKETS_SUPPORT\n    zend_declare_property_bool(swoole_socket_coro_ce, ZEND_STRL(\"__ext_sockets_nonblock\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL(\"__ext_sockets_timeout\"), 0, ZEND_ACC_PUBLIC);\n#endif\n\n    SW_INIT_CLASS_ENTRY_EX(swoole_socket_coro_exception,\n                           \"Swoole\\\\Coroutine\\\\Socket\\\\Exception\",\n                           \"Co\\\\Socket\\\\Exception\",\n                           nullptr,\n                           swoole_exception);\n\n    if (!zend_hash_str_find_ptr(&module_registry, ZEND_STRL(\"sockets\"))) {\n        socket_coro_register_constants(module_number);\n    }\n#ifdef ECANCELED\n    SW_REGISTER_LONG_CONSTANT(\"SOCKET_ECANCELED\", ECANCELED);\n#endif\n#ifdef TCP_INFO\n    SW_REGISTER_LONG_CONSTANT(\"TCP_INFO\", TCP_INFO);\n#endif\n}\n\nstatic sw_inline void socket_coro_sync_properties(const zval *zobject, const SocketObject *sock) {\n    zend_update_property_long(swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"errCode\"), sock->socket->errCode);\n    zend_update_property_string(swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"errMsg\"), sock->socket->errMsg);\n}\n\nstatic void sw_inline socket_coro_init(const zval *zobject, const SocketObject *sock) {\n    sock->socket->set_zero_copy(true);\n    sock->socket->set_buffer_allocator(sw_zend_string_allocator());\n    zend_update_property_long(swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"fd\"), sock->socket->get_fd());\n    zend_update_property_long(\n        swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"domain\"), sock->socket->get_sock_domain());\n    zend_update_property_long(\n        swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"type\"), sock->socket->get_sock_type());\n    zend_update_property_long(\n        swoole_socket_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL(\"protocol\"), sock->socket->get_sock_protocol());\n}\n\nSW_API bool php_swoole_export_socket(zval *zobject, SocketImpl *_socket) {\n    zend_object *object = socket_coro_create_object(swoole_socket_coro_ce);\n    if (!object) {\n        return false;\n    }\n\n    auto *sock = socket_coro_fetch_object(object);\n    sock->reference = true;\n    sock->socket = _socket;\n\n    ZVAL_OBJ(zobject, object);\n    socket_coro_init(zobject, sock);\n    return true;\n}\n\nSW_API zend_object *php_swoole_dup_socket(int fd, swSocketType type) {\n    php_swoole_check_reactor();\n    int new_fd = dup(fd);\n    if (new_fd < 0) {\n        php_swoole_sys_error(E_WARNING, \"dup(%d) failed\", fd);\n        return nullptr;\n    }\n    return php_swoole_create_socket_from_fd(new_fd, type);\n}\n\nSW_API zend_object *php_swoole_create_socket(swSocketType type) {\n    zval zobject;\n    zend_object *object = socket_coro_create_object(swoole_socket_coro_ce);\n    auto *sock = socket_coro_fetch_object(object);\n\n    sock->socket = new SocketImpl(type);\n    if (UNEXPECTED(sock->socket->get_fd() < 0)) {\n        php_swoole_sys_error(E_WARNING, \"new Socket() failed\");\n        delete sock->socket;\n        sock->socket = nullptr;\n        OBJ_RELEASE(object);\n        return nullptr;\n    }\n\n    ZVAL_OBJ(&zobject, object);\n    socket_coro_init(&zobject, sock);\n    return object;\n}\n\nSW_API void php_swoole_socket_set_error_properties(const zval *zobject, int code, const char *msg) {\n    swoole_set_last_error(code);\n    zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL(\"errCode\"), code);\n    zend_update_property_string(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL(\"errMsg\"), msg);\n}\n\nSW_API void php_swoole_socket_set_error_properties(const zval *zobject, int code) {\n    php_swoole_socket_set_error_properties(zobject, code, swoole_strerror(code));\n}\n\nSW_API void php_swoole_socket_set_error_properties(const zval *zobject, const SocketImpl *socket) {\n    php_swoole_socket_set_error_properties(zobject, socket->errCode, socket->errMsg);\n}\n\nstatic zend_object *socket_coro_create_object(SocketImpl *socket) {\n    zval zobject;\n    auto *object = socket_coro_create_object(swoole_socket_coro_ce);\n    auto *sock = socket_coro_fetch_object(object);\n\n    sock->socket = socket;\n    if (UNEXPECTED(sock->socket->get_fd() < 0)) {\n        php_swoole_sys_error(E_WARNING, \"new Socket() failed\");\n        delete sock->socket;\n        sock->socket = nullptr;\n        OBJ_RELEASE(object);\n        return nullptr;\n    }\n\n    ZVAL_OBJ(&zobject, object);\n    socket_coro_init(&zobject, sock);\n    return object;\n}\n\nSW_API zend_object *php_swoole_create_socket_from_fd(int fd, swSocketType type) {\n    return socket_coro_create_object(new SocketImpl(fd, type));\n}\n\nSW_API zend_object *php_swoole_create_socket_from_fd(int fd, int _domain, int _type, int _protocol) {\n    return socket_coro_create_object(new SocketImpl(fd, _domain, _type, _protocol));\n}\n\nSW_API SocketImpl *php_swoole_get_socket(const zval *zobject) {\n    SW_ASSERT(Z_OBJCE_P(zobject) == swoole_socket_coro_ce);\n    auto *sock = socket_coro_fetch_object(Z_OBJ_P(zobject));\n    return sock->socket;\n}\n\nSW_API bool php_swoole_socket_is_closed(const zval *zobject) {\n    auto *_sock = socket_coro_fetch_object(Z_OBJ_P(zobject));\n    return _sock->socket == nullptr || _sock->socket->is_closed();\n}\n\nSW_API void php_swoole_init_socket_object(zval *zobject, SocketImpl *socket) {\n    auto *object = socket_coro_create_object(swoole_socket_coro_ce);\n    auto *sock = socket_coro_fetch_object(object);\n    sock->socket = socket;\n    ZVAL_OBJ(zobject, object);\n    socket_coro_init(zobject, sock);\n}\n\nSW_API bool php_swoole_socket_set_protocol(SocketImpl *sock, const zval *zset) {\n    HashTable *vht = Z_ARRVAL_P(zset);\n    zval *ztmp;\n    bool ret = true;\n\n    /**\n     * ssl\n     */\n    if (php_swoole_array_get_value(vht, \"open_ssl\", ztmp)) {\n        if (zval_is_true(ztmp)) {\n            sock->enable_ssl_encrypt();\n        }\n    }\n    if (php_swoole_array_get_value(vht, \"open_http2_protocol\", ztmp)) {\n        sock->http2 = zval_is_true(ztmp);\n    }\n    if (sock->ssl_is_enable()) {\n        if (!php_swoole_socket_set_ssl(sock, zset)) {\n            ret = false;\n        }\n    }\n    /**\n     * protocol\n     */\n    // buffer: eof check\n    if (php_swoole_array_get_value(vht, \"open_eof_check\", ztmp)) {\n        sock->open_eof_check = zval_is_true(ztmp);\n    }\n    // buffer: split package with eof\n    if (php_swoole_array_get_value(vht, \"open_eof_split\", ztmp)) {\n        sock->protocol.split_by_eof = zval_is_true(ztmp);\n        if (sock->protocol.split_by_eof) {\n            sock->open_eof_check = true;\n        }\n    }\n    // package eof\n    if (php_swoole_array_get_value(vht, \"package_eof\", ztmp)) {\n        zend::String str_v(ztmp);\n        if (str_v.len() == 0) {\n            php_swoole_fatal_error(E_ERROR, \"package_eof cannot be an empty string\");\n            ret = false;\n        } else if (str_v.len() > SW_DATA_EOF_MAXLEN) {\n            php_swoole_fatal_error(E_ERROR, \"package_eof max length is %d\", SW_DATA_EOF_MAXLEN);\n            ret = false;\n        } else {\n            sock->protocol.package_eof_len = str_v.len();\n            memcpy(sock->protocol.package_eof, str_v.val(), str_v.len());\n        }\n    }\n    if (php_swoole_array_get_value(vht, \"open_fastcgi_protocol\", ztmp)) {\n#define FCGI_HEADER_LEN 8\n#define FCGI_MAX_LENGTH 0xffff\n        sock->open_length_check = zval_is_true(ztmp);\n        sock->protocol.package_length_size = FCGI_HEADER_LEN;\n        sock->protocol.package_length_offset = 0;\n        sock->protocol.package_body_offset = 0;\n        sock->protocol.get_package_length =\n            [](const Protocol *protocol, swoole::network::Socket *conn, PacketLength *pl) {\n                const auto *p = reinterpret_cast<const uint8_t *>(pl->buf);\n                ssize_t length = 0;\n                if (pl->buf_size >= FCGI_HEADER_LEN) {\n                    length = ((p[4] << 8) | p[5]) + p[6];\n                    if (length > FCGI_MAX_LENGTH) {\n                        length = -1;\n                    } else {\n                        length += FCGI_HEADER_LEN;\n                    }\n                }\n                return length;\n            };\n    }\n    // open mqtt protocol\n    if (php_swoole_array_get_value(vht, \"open_mqtt_protocol\", ztmp)) {\n        sock->open_length_check = zval_is_true(ztmp);\n        if (zval_is_true(ztmp)) {\n            swoole::mqtt::set_protocol(&sock->protocol);\n        }\n    }\n    // open length check\n    if (php_swoole_array_get_value(vht, \"open_length_check\", ztmp)) {\n        sock->open_length_check = zval_is_true(ztmp);\n        sock->protocol.get_package_length = Protocol::default_length_func;\n    }\n    // package length size\n    if (php_swoole_array_get_value(vht, \"package_length_type\", ztmp)) {\n        zend::String str_v(ztmp);\n        sock->protocol.package_length_type = str_v.val()[0];\n        sock->protocol.package_length_size = swoole_type_size(sock->protocol.package_length_type);\n        if (sock->protocol.package_length_size == 0) {\n            php_swoole_fatal_error(E_WARNING,\n                                   \"Unknown package_length_type name '%c', see pack(). Link: https://php.net/pack\",\n                                   sock->protocol.package_length_type);\n            ret = false;\n        }\n    }\n    // package length offset\n    if (php_swoole_array_get_value(vht, \"package_length_offset\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        sock->protocol.package_length_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX));\n    }\n    // package body start\n    if (php_swoole_array_get_value(vht, \"package_body_offset\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        sock->protocol.package_body_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX));\n    }\n    // length function\n    if (php_swoole_array_get_value(vht, \"package_length_func\", ztmp)) {\n        auto cb = sw_callable_create(ztmp);\n        if (cb) {\n            sock->protocol.get_package_length = php_swoole_length_func;\n            if (sock->protocol.private_data_1) {\n                sw_callable_free(sock->protocol.private_data_1);\n            }\n            sock->protocol.private_data_1 = cb;\n            sock->protocol.package_length_size = 0;\n            sock->protocol.package_length_type = '\\0';\n            sock->protocol.package_length_offset = SW_IPC_BUFFER_SIZE;\n        }\n    }\n    /**\n     * package max length\n     */\n    if (php_swoole_array_get_value(vht, \"package_max_length\", ztmp)) {\n        zend_long v = php_swoole_parse_to_size(ztmp);\n        sock->protocol.package_max_length = SW_MAX(0, SW_MIN(v, UINT32_MAX));\n    } else {\n        sock->protocol.package_max_length = SW_INPUT_BUFFER_SIZE;\n    }\n\n    return ret;\n}\n\nSW_API bool php_swoole_socket_set(SocketImpl *cli, const zval *zset) {\n    HashTable *vht = Z_ARRVAL_P(zset);\n    zval *ztmp;\n    bool ret = true;\n\n    /**\n     * timeout\n     */\n    if (php_swoole_array_get_value(vht, \"timeout\", ztmp)) {\n        cli->set_timeout(zval_get_double(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"connect_timeout\", ztmp)) {\n        cli->set_timeout(zval_get_double(ztmp), SW_TIMEOUT_CONNECT);\n    }\n    if (php_swoole_array_get_value(vht, \"read_timeout\", ztmp)) {\n        cli->set_timeout(zval_get_double(ztmp), SW_TIMEOUT_READ);\n    }\n    if (php_swoole_array_get_value(vht, \"write_timeout\", ztmp)) {\n        cli->set_timeout(zval_get_double(ztmp), SW_TIMEOUT_WRITE);\n    }\n    std::string _bind_address;\n    int _bind_port = 0;\n    if (php_swoole_array_get_value(vht, \"bind_port\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        _bind_port = SW_MAX(0, SW_MIN(v, UINT16_MAX));\n    }\n    if (php_swoole_array_get_value(vht, \"bind_address\", ztmp)) {\n        zend::String tmp = ztmp;\n        _bind_address = tmp.to_std_string();\n    }\n    if (!_bind_address.empty() && !cli->bind(_bind_address, _bind_port)) {\n        ret = false;\n    }\n    /**\n     * socket send/recv buffer size\n     */\n    if (php_swoole_array_get_value(vht, \"socket_buffer_size\", ztmp)) {\n        zend_long size = php_swoole_parse_to_size(ztmp);\n        if (size <= 0) {\n            php_swoole_fatal_error(E_WARNING, \"socket buffer size must be greater than 0, got \" ZEND_LONG_FMT, size);\n            ret = false;\n        } else {\n            cli->set_option(SOL_SOCKET, SO_RCVBUF, size) && cli->set_option(SOL_SOCKET, SO_SNDBUF, size);\n        }\n    }\n    /**\n     * client: tcp_nodelay\n     */\n    if (php_swoole_array_get_value(vht, \"open_tcp_nodelay\", ztmp)) {\n        if (cli->get_type() == SW_SOCK_TCP || cli->get_type() != SW_SOCK_TCP6) {\n            cli->get_socket()->set_tcp_nodelay(zval_is_true(ztmp));\n        }\n    }\n    /**\n     * openssl and protocol options\n     */\n    if (!php_swoole_socket_set_protocol(cli, zset)) {\n        ret = false;\n    }\n    /**\n     * socks5 proxy\n     */\n    if (php_swoole_array_get_value(vht, \"socks5_host\", ztmp)) {\n        if (!cli->get_socket()->is_inet()) {\n            zend_throw_exception_ex(\n                swoole_exception_ce, SW_ERROR_OPERATION_NOT_SUPPORT, \"Only tcp socket supports socks5 proxy settings\");\n            return false;\n        }\n\n        zend::String host(ztmp);\n        if (php_swoole_array_get_value(vht, \"socks5_port\", ztmp)) {\n            std::string user, pwd;\n            auto socks5_port = zval_get_long(ztmp);\n            if (php_swoole_array_get_value(vht, \"socks5_username\", ztmp)) {\n                user = zend::String(ztmp).to_std_string();\n                if (!user.empty() && php_swoole_array_get_value(vht, \"socks5_password\", ztmp)) {\n                    pwd = zend::String(ztmp).to_std_string();\n                } else {\n                    php_swoole_fatal_error(E_WARNING, \"socks5_password should not be null\");\n                    ret = false;\n                }\n            }\n            cli->set_socks5_proxy(host.to_std_string(), socks5_port, user, pwd);\n        } else {\n            php_swoole_fatal_error(E_WARNING, \"socks5_port should not be null\");\n            ret = false;\n        }\n    }\n    /**\n     * http proxy\n     */\n    else if (php_swoole_array_get_value(vht, \"http_proxy_host\", ztmp)) {\n        if (!cli->get_socket()->is_inet()) {\n            zend_throw_exception_ex(\n                swoole_exception_ce, SW_ERROR_OPERATION_NOT_SUPPORT, \"Only tcp socket supports http proxy settings\");\n            return false;\n        }\n        zend::String host(ztmp);\n        if (php_swoole_array_get_value(vht, \"http_proxy_port\", ztmp)) {\n            std::string user, pwd;\n            auto http_proxy_port = zval_get_long(ztmp);\n            if (php_swoole_array_get_value(vht, \"http_proxy_username\", ztmp) ||\n                php_swoole_array_get_value(vht, \"http_proxy_user\", ztmp)) {\n                user = zend::String(ztmp).to_std_string();\n                if (!user.empty() && php_swoole_array_get_value(vht, \"http_proxy_password\", ztmp)) {\n                    pwd = zend::String(ztmp).to_std_string();\n                } else {\n                    php_swoole_fatal_error(E_WARNING, \"socks5_password should not be null\");\n                    ret = false;\n                }\n            }\n            cli->set_http_proxy(host.to_std_string(), http_proxy_port, user, pwd);\n        } else {\n            php_swoole_fatal_error(E_WARNING, \"http_proxy_port should not be null\");\n            ret = false;\n        }\n    }\n\n    return ret;\n}\n\nSW_API bool php_swoole_socket_set_ssl(SocketImpl *sock, const zval *zset) {\n    HashTable *vht = Z_ARRVAL_P(zset);\n    zval *ztmp;\n\n    if (php_swoole_array_get_value(vht, \"ssl_protocols\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        sock->set_ssl_protocols(v);\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_compress\", ztmp)) {\n        sock->set_ssl_disable_compress(!zval_is_true(ztmp));\n    } else if (php_swoole_array_get_value(vht, \"ssl_disable_compression\", ztmp)) {\n        sock->set_ssl_disable_compress(!zval_is_true(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_cert_file\", ztmp)) {\n        zend::String str_v(ztmp);\n        if (!sock->set_ssl_cert_file(str_v.to_std_string())) {\n            php_swoole_fatal_error(E_WARNING, \"ssl cert file[%s] not found\", str_v.val());\n            return false;\n        }\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_key_file\", ztmp)) {\n        zend::String str_v(ztmp);\n        if (!sock->set_ssl_key_file(str_v.to_std_string())) {\n            php_swoole_fatal_error(E_WARNING, \"ssl key file[%s] not found\", str_v.val());\n            return false;\n        }\n    }\n    if (!sock->get_ssl_cert_file().empty() && sock->get_ssl_key_file().empty()) {\n        php_swoole_fatal_error(E_WARNING, \"ssl require key file\");\n    }\n    if (!sock->get_ssl_key_file().empty() && sock->get_ssl_cert_file().empty()) {\n        php_swoole_fatal_error(E_WARNING, \"ssl require cert file\");\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_passphrase\", ztmp)) {\n        sock->set_ssl_passphrase(zend::String(ztmp).to_std_string());\n    }\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n    if (php_swoole_array_get_value(vht, \"ssl_host_name\", ztmp)) {\n        sock->set_tls_host_name(zend::String(ztmp).to_std_string());\n    }\n#endif\n    if (php_swoole_array_get_value(vht, \"ssl_verify_peer\", ztmp)) {\n        sock->set_ssl_verify_peer(zval_is_true(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_allow_self_signed\", ztmp)) {\n        sock->set_ssl_allow_self_signed(zval_is_true(ztmp));\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_cafile\", ztmp)) {\n        sock->set_ssl_cafile(zend::String(ztmp).to_std_string());\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_capath\", ztmp)) {\n        sock->set_ssl_capath(zend::String(ztmp).to_std_string());\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_verify_depth\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        sock->set_ssl_verify_depth(SW_MAX(0, SW_MIN(v, UINT8_MAX)));\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_ciphers\", ztmp)) {\n        sock->set_ssl_ciphers(zend::String(ztmp).to_std_string());\n    }\n    if (php_swoole_array_get_value(vht, \"ssl_ecdh_curve\", ztmp)) {\n        sock->set_ssl_ecdh_curve(zend::String(ztmp).to_std_string());\n    }\n#ifdef OPENSSL_IS_BORINGSSL\n    if (php_swoole_array_get_value(vht, \"ssl_grease\", ztmp)) {\n        zend_long v = zval_get_long(ztmp);\n        sock->set_ssl_grease(SW_MAX(0, SW_MIN(v, UINT8_MAX)));\n    }\n#endif\n    return true;\n}\n\nPHP_FUNCTION(swoole_coroutine_socketpair) {\n    zend_long domain, type, protocol;\n    php_socket_t pair[2];\n\n    ZEND_PARSE_PARAMETERS_START(3, 3)\n    Z_PARAM_LONG(domain)\n    Z_PARAM_LONG(type)\n    Z_PARAM_LONG(protocol)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (0 != socketpair((int) domain, (int) type, (int) protocol, pair)) {\n        php_swoole_sys_error(E_WARNING, \"failed to create socket\");\n        RETURN_FALSE;\n    }\n\n    php_swoole_check_reactor();\n\n    auto sock_type = swoole::network::Socket::convert_to_type(domain, type);\n\n    zend_object *s1 = php_swoole_create_socket_from_fd(pair[0], sock_type);\n    if (s1 == nullptr) {\n        RETURN_FALSE;\n    }\n\n    zend_object *s2 = php_swoole_create_socket_from_fd(pair[1], sock_type);\n    if (s2 == nullptr) {\n        OBJ_RELEASE(s1);\n        RETURN_FALSE;\n    }\n\n    zval zobject1, zobject2;\n    ZVAL_OBJ(&zobject1, s1);\n    ZVAL_OBJ(&zobject2, s2);\n\n    array_init(return_value);\n    add_next_index_zval(return_value, &zobject1);\n    add_next_index_zval(return_value, &zobject2);\n}\n\nstatic PHP_METHOD(swoole_socket_coro, __construct) {\n    zend_long domain, type, protocol = IPPROTO_IP;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 2, 3)\n    Z_PARAM_LONG(domain)\n    Z_PARAM_LONG(type)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(protocol)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    auto *sock = socket_coro_fetch_object(Z_OBJ_P(ZEND_THIS));\n    if (sock->socket) {\n        zend_throw_error(nullptr, \"Constructor of %s can only be called once\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        RETURN_FALSE;\n    }\n\n    php_swoole_check_reactor();\n    sock->socket = new SocketImpl((int) domain, (int) type, (int) protocol);\n    if (UNEXPECTED(sock->socket->get_fd() < 0)) {\n        zend_throw_exception_ex(\n            swoole_socket_coro_exception_ce, errno, \"new Socket() failed. Error: %s [%d]\", strerror(errno), errno);\n        delete sock->socket;\n        sock->socket = nullptr;\n        RETURN_FALSE;\n    }\n    socket_coro_init(ZEND_THIS, sock);\n}\n\nstatic PHP_METHOD(swoole_socket_coro, bind) {\n    char *address;\n    size_t l_address;\n    zend_long port = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_STRING(address, l_address)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(port)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_get_socket_coro(sock, ZEND_THIS);\n\n    if (!sock->socket->bind(std::string(address, l_address), port)) {\n        socket_coro_sync_properties(ZEND_THIS, sock);\n        RETURN_FALSE;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_socket_coro, listen) {\n    zend_long backlog = SW_BACKLOG;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(backlog)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_get_socket_coro(sock, ZEND_THIS);\n\n    if (!sock->socket->listen(backlog)) {\n        socket_coro_sync_properties(ZEND_THIS, sock);\n        RETURN_FALSE;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_socket_coro, accept) {\n    double timeout = 0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_get_socket_coro(sock, ZEND_THIS);\n\n    SocketImpl *conn = sock->socket->accept(timeout);\n    if (conn) {\n        zend_object *client = socket_coro_create_object(swoole_socket_coro_ce);\n        auto *client_sock = socket_coro_fetch_object(client);\n        client_sock->socket = conn;\n        ZVAL_OBJ(return_value, &client_sock->std);\n        socket_coro_init(return_value, client_sock);\n        // It must be copied once to avoid destroying the function when the connection closes.\n        if (sock->socket->protocol.private_data_1) {\n            auto *cb = static_cast<zend::Callable *>(sock->socket->protocol.private_data_1);\n            conn->protocol.private_data_1 = cb->dup();\n        }\n    } else {\n        socket_coro_sync_properties(ZEND_THIS, sock);\n        RETURN_FALSE;\n    }\n}\n\nstatic PHP_METHOD(swoole_socket_coro, connect) {\n    char *host;\n    size_t l_host;\n    zend_long port = 0;\n    double timeout = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 3)\n    Z_PARAM_STRING(host, l_host)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(port)\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_get_socket_coro(sock, ZEND_THIS);\n\n    if (sock->socket->is_port_required()) {\n        if (ZEND_NUM_ARGS() == 1) {\n            php_swoole_error(E_WARNING, \"Socket of type AF_INET/AF_INET6 requires port argument\");\n            RETURN_FALSE;\n        } else if (port == 0 || port >= 65536) {\n            php_swoole_error(E_WARNING, \"Invalid port argument[\" ZEND_LONG_FMT \"]\", port);\n            RETURN_FALSE;\n        }\n    }\n    SocketImpl::TimeoutSetter ts(sock->socket, timeout, SW_TIMEOUT_CONNECT);\n    if (!sock->socket->connect(std::string(host, l_host), port)) {\n        socket_coro_sync_properties(ZEND_THIS, sock);\n        RETURN_FALSE;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_socket_coro, checkLiveness) {\n    swoole_get_socket_coro(sock, ZEND_THIS);\n\n    bool liveness = sock->socket->check_liveness();\n    socket_coro_sync_properties(ZEND_THIS, sock);\n    RETURN_BOOL(liveness);\n}\n\nstatic PHP_METHOD(swoole_socket_coro, getBoundCid) {\n    zend_long event;\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(event)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_get_socket_coro(sock, ZEND_THIS);\n    RETURN_LONG(sock->socket->get_bound_cid((swEventType) event));\n}\n\nstatic PHP_METHOD(swoole_socket_coro, peek) {\n    zend_long length = SW_BUFFER_SIZE_BIG;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(length)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (UNEXPECTED(length <= 0)) {\n        length = SW_BUFFER_SIZE_BIG;\n    }\n\n    swoole_get_socket_coro(sock, ZEND_THIS);\n\n    zend_string *buf = zend_string_alloc(length, false);\n    ssize_t bytes = sock->socket->peek(ZSTR_VAL(buf), length);\n    socket_coro_sync_properties(ZEND_THIS, sock);\n    if (UNEXPECTED(bytes < 0)) {\n        zend_string_free(buf);\n        RETURN_FALSE;\n    } else if (UNEXPECTED(bytes == 0)) {\n        zend_string_free(buf);\n        RETURN_EMPTY_STRING();\n    } else {\n        RETURN_STR(sw_zend_string_recycle(buf, length, bytes));\n    }\n}\n\nenum RecvMode {\n    SOCKET_RECV,\n    SOCKET_RECV_ALL,\n    SOCKET_RECV_LINE,\n    SOCKET_RECV_WITH_BUFFER,\n};\n\nstatic inline void socket_coro_recv(INTERNAL_FUNCTION_PARAMETERS, RecvMode type) {\n    zend_long length = SW_BUFFER_SIZE_BIG;\n    double timeout = 0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 2)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(length)\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (UNEXPECTED(length <= 0)) {\n        length = SW_BUFFER_SIZE_BIG;\n    }\n\n    swoole_get_socket_coro(sock, ZEND_THIS);\n\n    zend_string *buf = zend_string_alloc(length, false);\n    SocketImpl::TimeoutSetter ts(sock->socket, timeout, SW_TIMEOUT_READ);\n    ssize_t bytes = -1;\n    switch (type) {\n    case SOCKET_RECV:\n        bytes = sock->socket->recv(ZSTR_VAL(buf), length);\n        break;\n    case SOCKET_RECV_ALL:\n        bytes = sock->socket->recv_all(ZSTR_VAL(buf), length);\n        break;\n    case SOCKET_RECV_LINE:\n        bytes = sock->socket->recv_line(ZSTR_VAL(buf), length);\n        break;\n    case SOCKET_RECV_WITH_BUFFER:\n        bytes = sock->socket->recv_with_buffer(ZSTR_VAL(buf), length);\n        break;\n    default:\n        assert(0);\n        break;\n    }\n    socket_coro_sync_properties(ZEND_THIS, sock);\n    if (UNEXPECTED(bytes < 0)) {\n        zend_string_free(buf);\n        RETURN_FALSE;\n    } else if (UNEXPECTED(bytes == 0)) {\n        zend_string_free(buf);\n        RETURN_EMPTY_STRING();\n    } else {\n        RETURN_STR(sw_zend_string_recycle(buf, length, bytes));\n    }\n}\n\nstatic PHP_METHOD(swoole_socket_coro, recv) {\n    socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV);\n}\n\nstatic PHP_METHOD(swoole_socket_coro, recvAll) {\n    socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_ALL);\n}\n\nstatic PHP_METHOD(swoole_socket_coro, recvLine) {\n    socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_LINE);\n}\n\nstatic PHP_METHOD(swoole_socket_coro, recvWithBuffer) {\n    socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_WITH_BUFFER);\n}\n\nstatic PHP_METHOD(swoole_socket_coro, recvPacket) {\n    double timeout = 0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_get_socket_coro(sock, ZEND_THIS);\n    ssize_t retval = sock->socket->recv_packet(timeout);\n    socket_coro_sync_properties(ZEND_THIS, sock);\n    if (retval < 0) {\n        RETURN_FALSE;\n    } else if (retval == 0) {\n        sock->socket->get_read_buffer()->clear();\n        RETURN_EMPTY_STRING();\n    } else {\n        auto strval = sock->socket->pop_packet();\n        if (strval == nullptr) {\n            sock->socket->set_err(ENOMEM);\n            RETURN_FALSE;\n        } else {\n            zend::assign_zend_string_by_val(return_value, strval, retval);\n        }\n    }\n}\n\nstatic sw_inline void socket_coro_send(INTERNAL_FUNCTION_PARAMETERS, const bool all) {\n    char *data;\n    size_t length;\n    double timeout = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_STRING(data, length)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_get_socket_coro(sock, ZEND_THIS);\n\n    SocketImpl::TimeoutSetter ts(sock->socket, timeout, SW_TIMEOUT_WRITE);\n    ssize_t retval = all ? sock->socket->send_all(data, length) : sock->socket->send(data, length);\n    socket_coro_sync_properties(ZEND_THIS, sock);\n    if (UNEXPECTED(retval < 0)) {\n        RETURN_FALSE;\n    } else {\n        RETURN_LONG(retval);\n    }\n}\n\nstatic PHP_METHOD(swoole_socket_coro, send) {\n    socket_coro_send(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);\n}\n\nstatic void socket_coro_write_vector(INTERNAL_FUNCTION_PARAMETERS, const bool all) {\n    zval *ziov = nullptr;\n    zval *zelement = nullptr;\n    double timeout = 0;\n    int iovcnt = 0;\n    int iov_index = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_ARRAY(ziov)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_get_socket_coro(sock, ZEND_THIS);\n\n    ON_SCOPE_EXIT {\n        socket_coro_sync_properties(ZEND_THIS, sock);\n    };\n\n    HashTable *vht = Z_ARRVAL_P(ziov);\n    iovcnt = zend_array_count(vht);\n\n    if (iovcnt > IOV_MAX) {\n        sw_tg_buffer()->length = sw_snprintf(sw_tg_buffer()->str, sw_tg_buffer()->size, SW_IOV_MAX_ERROR_MSG, IOV_MAX);\n        sock->socket->set_err(EINVAL, sw_tg_buffer()->to_std_string());\n        RETURN_FALSE;\n    }\n\n    std::unique_ptr<iovec[]> iov(new iovec[iovcnt]);\n\n    SW_HASHTABLE_FOREACH_START(vht, zelement)\n    if (!ZVAL_IS_STRING(zelement)) {\n        zend_throw_exception_ex(swoole_socket_coro_exception_ce,\n                                EINVAL,\n                                \"Item #[%d] must be of type string, %s given\",\n                                iov_index,\n                                zend_get_type_by_const(Z_TYPE_P(zelement)));\n        RETURN_FALSE;\n    }\n    if (Z_STRLEN_P(zelement) == 0) {\n        zend_throw_exception_ex(\n            swoole_socket_coro_exception_ce, EINVAL, \"Item #[%d] cannot be empty string\", iov_index);\n        RETURN_FALSE;\n    }\n    iov[iov_index].iov_base = Z_STRVAL_P(zelement);\n    iov[iov_index].iov_len = Z_STRLEN_P(zelement);\n    iov_index++;\n    SW_HASHTABLE_FOREACH_END();\n\n    swoole::network::IOVector io_vector((struct iovec *) iov.get(), iovcnt);\n\n    SocketImpl::TimeoutSetter ts(sock->socket, timeout, SW_TIMEOUT_WRITE);\n    ssize_t retval = all ? sock->socket->writev_all(&io_vector) : sock->socket->writev(&io_vector);\n    if (UNEXPECTED(retval < 0)) {\n        RETURN_FALSE;\n    } else {\n        RETURN_LONG(retval);\n    }\n}\n\nstatic PHP_METHOD(swoole_socket_coro, writeVector) {\n    socket_coro_write_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);\n}\n\nstatic PHP_METHOD(swoole_socket_coro, writeVectorAll) {\n    socket_coro_write_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);\n}\n\nstatic void socket_coro_read_vector(INTERNAL_FUNCTION_PARAMETERS, const bool all) {\n    zval *ziov = nullptr;\n    zval *zelement = nullptr;\n    double timeout = 0;\n    int iovcnt = 0;\n    int iov_index = 0;\n    ssize_t total_length = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_ARRAY(ziov)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_get_socket_coro(sock, ZEND_THIS);\n\n    ON_SCOPE_EXIT {\n        socket_coro_sync_properties(ZEND_THIS, sock);\n    };\n\n    HashTable *vht = Z_ARRVAL_P(ziov);\n    iovcnt = zend_array_count(vht);\n\n    if (iovcnt > IOV_MAX) {\n        sw_tg_buffer()->length = sw_snprintf(sw_tg_buffer()->str, sw_tg_buffer()->size, SW_IOV_MAX_ERROR_MSG, IOV_MAX);\n        sock->socket->set_err(EINVAL, sw_tg_buffer()->to_std_string());\n        RETURN_FALSE;\n    }\n\n    std::unique_ptr<iovec[]> iov(new iovec[iovcnt]);\n\n    SW_HASHTABLE_FOREACH_START(vht, zelement) {\n        if (!ZVAL_IS_LONG(zelement)) {\n            zend_throw_exception_ex(swoole_socket_coro_exception_ce,\n                                    EINVAL,\n                                    \"Item #[%d] must be of type int, %s given\",\n                                    iov_index,\n                                    zend_get_type_by_const(Z_TYPE_P(zelement)));\n            RETURN_FALSE;\n        }\n        if (Z_LVAL_P(zelement) < 0) {\n            zend_throw_exception_ex(\n                swoole_socket_coro_exception_ce, EINVAL, \"Item #[%d] must be greater than 0\", iov_index);\n            RETURN_FALSE;\n        }\n        size_t iov_len = Z_LVAL_P(zelement);\n\n        iov[iov_index].iov_base = zend_string_alloc(iov_len, false)->val;\n        iov[iov_index].iov_len = iov_len;\n        iov_index++;\n        total_length += iov_len;\n    }\n    SW_HASHTABLE_FOREACH_END();\n\n    swoole::network::IOVector io_vector((struct iovec *) iov.get(), iovcnt);\n\n    SocketImpl::TimeoutSetter ts(sock->socket, timeout, SW_TIMEOUT_READ);\n    ssize_t retval = all ? sock->socket->readv_all(&io_vector) : sock->socket->readv(&io_vector);\n\n    auto free_func = [](const iovec *iov, int iovcnt, int iov_index) {\n        for (; iov_index < iovcnt; iov_index++) {\n            zend_string_free(zend::fetch_zend_string_by_val((char *) iov[iov_index].iov_base));\n        }\n    };\n\n    if (UNEXPECTED(retval < 0)) {\n        free_func(iov.get(), iovcnt, 0);\n        RETURN_FALSE;\n    } else if (UNEXPECTED(retval == 0)) {\n        free_func(iov.get(), iovcnt, 0);\n        RETURN_EMPTY_ARRAY();\n    } else {\n        array_init(return_value);\n        size_t real_count;\n\n        if (retval < total_length) {\n            /**\n             * Free the extra memory.\n             * For example iov is [5, 5, 5], but we get ['hello', 'world'], we should free the last iov.\n             */\n            iov_index = io_vector.get_index();\n            size_t offset_bytes = io_vector.get_offset_bytes();\n\n            real_count = iov_index + 1;\n            zend_string *str = zend::fetch_zend_string_by_val((char *) iov[iov_index].iov_base);\n            iov[iov_index].iov_base = sw_zend_string_recycle(str, iov[iov_index].iov_len, offset_bytes)->val;\n            iov[iov_index].iov_len = offset_bytes;\n            free_func(iov.get(), iovcnt, real_count);\n        } else {\n            real_count = iovcnt;\n        }\n\n        SW_LOOP_N(real_count) {\n            ((char *) iov[i].iov_base)[iov[i].iov_len] = '\\0';\n            add_next_index_str(return_value, zend::fetch_zend_string_by_val((char *) iov[i].iov_base));\n        }\n    }\n}\n\nstatic PHP_METHOD(swoole_socket_coro, readVector) {\n    socket_coro_read_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);\n}\n\nstatic PHP_METHOD(swoole_socket_coro, readVectorAll) {\n    socket_coro_read_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);\n}\n\nstatic PHP_METHOD(swoole_socket_coro, sendFile) {\n    char *file;\n    size_t file_len;\n    zend_long offset = 0;\n    zend_long length = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 3)\n    Z_PARAM_STRING(file, file_len)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(offset)\n    Z_PARAM_LONG(length)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (file_len == 0) {\n        php_swoole_fatal_error(E_WARNING, \"file to send is empty\");\n        RETURN_FALSE;\n    }\n\n    swoole_get_socket_coro(sock, ZEND_THIS);\n    if (!sock->socket->sendfile(file, offset, length)) {\n        socket_coro_sync_properties(ZEND_THIS, sock);\n        RETVAL_FALSE;\n    } else {\n        RETVAL_TRUE;\n    }\n}\n\nstatic PHP_METHOD(swoole_socket_coro, sendAll) {\n    socket_coro_send(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);\n}\n\nstatic PHP_METHOD(swoole_socket_coro, recvfrom) {\n    zval *peername;\n    double timeout = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_ZVAL_EX(peername, 0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_get_socket_coro(sock, ZEND_THIS);\n\n    zend_string *buf = zend_string_alloc(SW_BUFFER_SIZE_BIG, false);\n    SocketImpl::TimeoutSetter ts(sock->socket, timeout, SW_TIMEOUT_READ);\n    ssize_t bytes = sock->socket->recvfrom(ZSTR_VAL(buf), SW_BUFFER_SIZE_BIG);\n    socket_coro_sync_properties(ZEND_THIS, sock);\n    if (bytes < 0) {\n        zend_string_free(buf);\n        RETURN_FALSE;\n    } else if (bytes == 0) {\n        zend_string_free(buf);\n        RETURN_EMPTY_STRING();\n    } else {\n        zval_dtor(peername);\n        array_init(peername);\n        add_assoc_string(peername, \"address\", (char *) sock->socket->get_addr());\n        add_assoc_long(peername, \"port\", sock->socket->get_port());\n\n        ZSTR_LEN(buf) = bytes;\n        ZSTR_VAL(buf)[bytes] = 0;\n        RETURN_STR(buf);\n    }\n}\n\nstatic PHP_METHOD(swoole_socket_coro, sendto) {\n    char *data;\n    size_t l_data;\n    char *addr;\n    size_t l_addr;\n    zend_long port = 0;\n\n    ZEND_PARSE_PARAMETERS_START(3, 3)\n    Z_PARAM_STRING(addr, l_addr)\n    Z_PARAM_LONG(port)\n    Z_PARAM_STRING(data, l_data)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_get_socket_coro(sock, ZEND_THIS);\n\n    ssize_t retval = sock->socket->sendto(std::string(addr, l_addr), port, data, l_data);\n    socket_coro_sync_properties(ZEND_THIS, sock);\n    if (retval < 0) {\n        RETURN_FALSE;\n    } else {\n        RETURN_LONG(retval);\n    }\n}\n\nstatic PHP_METHOD(swoole_socket_coro, shutdown) {\n    zend_long how = SHUT_RDWR;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(how)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_get_socket_coro(sock, ZEND_THIS);\n\n    if (!sock->socket->shutdown(how)) {\n        socket_coro_sync_properties(ZEND_THIS, sock);\n        RETURN_FALSE;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_socket_coro, close) {\n    swoole_get_socket_coro(sock, ZEND_THIS);\n    if (sock->reference) {\n        php_swoole_error(E_WARNING, \"cannot close the referenced resource\");\n        RETURN_FALSE;\n    }\n    if (sock->socket->protocol.private_data_1) {\n        sw_callable_free(sock->socket->protocol.private_data_1);\n        sock->socket->protocol.private_data_1 = nullptr;\n    }\n    if (!Z_ISUNDEF(sock->zstream)) {\n        php_stream *stream = nullptr;\n        php_stream_from_zval_no_verify(stream, &sock->zstream);\n        if (stream != nullptr) {\n            /* close & destroy stream, incl. removing it from the rsrc list;\n             * resource stored in php_sock->zstream will become invalid */\n            php_stream_free(stream,\n                            PHP_STREAM_FREE_KEEP_RSRC | PHP_STREAM_FREE_CLOSE |\n                                (stream->is_persistent ? PHP_STREAM_FREE_CLOSE_PERSISTENT : 0));\n        }\n        ZVAL_UNDEF(&sock->zstream);\n        sock->socket->move_fd();\n    } else {\n        sock->socket->close();\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_socket_coro, getsockname) {\n    swoole_get_socket_coro(sock, ZEND_THIS);\n\n    if (!sock->socket->getsockname()) {\n        socket_coro_sync_properties(ZEND_THIS, sock);\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n    add_assoc_string(return_value, \"address\", sock->socket->get_addr());\n    add_assoc_long(return_value, \"port\", sock->socket->get_port());\n}\n\nstatic PHP_METHOD(swoole_socket_coro, getpeername) {\n    swoole_get_socket_coro(sock, ZEND_THIS);\n\n    Address sa;\n    if (!sock->socket->getpeername(&sa)) {\n        socket_coro_sync_properties(ZEND_THIS, sock);\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n    add_assoc_string(return_value, \"address\", sa.get_addr());\n    add_assoc_long(return_value, \"port\", sa.get_port());\n}\n\nstatic PHP_METHOD(swoole_socket_coro, getOption) {\n    struct linger linger_val;\n    socklen_t optlen;\n    int other_val;\n    zend_long level, optname;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_LONG(level)\n    Z_PARAM_LONG(optname)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_get_socket_coro(sock, ZEND_THIS);\n    auto _socket = sock->socket->get_socket();\n\n    if (level == IPPROTO_IP) {\n        switch (optname) {\n        case IP_MULTICAST_IF: {\n            struct in_addr if_addr;\n            unsigned int if_index;\n            optlen = sizeof(if_addr);\n            if (_socket->get_option(level, optname, &if_addr, &optlen) != 0) {\n                php_swoole_sys_error(E_WARNING,\n                                     \"getsockopt(%d, \" ZEND_LONG_FMT \", \" ZEND_LONG_FMT \")\",\n                                     sock->socket->get_fd(),\n                                     level,\n                                     optname);\n                RETURN_FALSE;\n            }\n            if (php_add4_to_if_index(&if_addr, sock->socket, &if_index) == SUCCESS) {\n                RETURN_LONG((zend_long) if_index);\n            } else {\n                RETURN_FALSE;\n            }\n        }\n        default:\n            break;\n        }\n    } else if (level == IPPROTO_IPV6) {\n        int ret = php_do_getsockopt_ipv6_rfc3542(sock->socket, level, optname, return_value);\n        if (ret == SUCCESS) {\n            return;\n        } else if (ret == FAILURE) {\n            RETURN_FALSE;\n        } /* else continue */\n    }\n\n    /* sol_socket options and general case */\n    switch (optname) {\n    case SO_LINGER: {\n        optlen = sizeof(linger_val);\n\n        if (_socket->get_option(level, optname, (char *) &linger_val, &optlen) != 0) {\n            php_swoole_sys_error(E_WARNING,\n                                 \"getsockopt(%d, \" ZEND_LONG_FMT \", \" ZEND_LONG_FMT \")\",\n                                 sock->socket->get_fd(),\n                                 level,\n                                 optname);\n            RETURN_FALSE;\n        }\n\n        array_init(return_value);\n        add_assoc_long(return_value, \"l_onoff\", linger_val.l_onoff);\n        add_assoc_long(return_value, \"l_linger\", linger_val.l_linger);\n        break;\n    }\n    case SO_RCVTIMEO:\n    case SO_SNDTIMEO: {\n        double timeout = sock->socket->get_timeout(optname == SO_RCVTIMEO ? SW_TIMEOUT_READ : SW_TIMEOUT_WRITE);\n        array_init(return_value);\n        int sec = (int) timeout;\n        add_assoc_long(return_value, \"sec\", (int) timeout);\n        add_assoc_long(return_value, \"usec\", (timeout - (double) sec) * 1000000);\n        break;\n    }\n#ifdef TCP_INFO\n    case TCP_INFO: {\n        tcp_info info;\n        socklen_t len = sizeof(info);\n        if (_socket->get_option(SOL_TCP, TCP_INFO, &info, &len) < 0) {\n            php_swoole_sys_error(E_WARNING, \"getsockopt(%d, SOL_TCP, TCP_INFO)\", sock->socket->get_fd());\n        } else {\n            array_init(return_value);\n            auto info_map = sw_socket_parse_tcp_info(&info);\n            for (const auto &iter : info_map) {\n                add_assoc_long_ex(return_value, iter.first.c_str(), iter.first.length(), (zend_long) iter.second);\n            }\n        }\n        break;\n    }\n#endif\n    default: {\n        optlen = sizeof(other_val);\n\n        if (_socket->get_option(level, optname, (char *) &other_val, &optlen) != 0) {\n            php_swoole_sys_error(E_WARNING,\n                                 \"getsockopt(%d, \" ZEND_LONG_FMT \", \" ZEND_LONG_FMT \")\",\n                                 sock->socket->get_fd(),\n                                 level,\n                                 optname);\n            RETURN_FALSE;\n        }\n        if (optlen == 1) {\n            char _val;\n            memcpy(&_val, &other_val, optlen);\n            RETURN_LONG(_val);\n        } else {\n            RETURN_LONG(other_val);\n        }\n        break;\n    }\n    }\n}\n\nstatic PHP_METHOD(swoole_socket_coro, setOption) {\n    zval *optval;\n    struct linger lv;\n    int ov, optlen, retval;\n    struct timeval tv;\n    zend_long level, optname;\n    char *opt_ptr;\n    HashTable *opt_ht;\n\n    ZEND_PARSE_PARAMETERS_START(3, 3)\n    Z_PARAM_LONG(level)\n    Z_PARAM_LONG(optname)\n    Z_PARAM_ZVAL(optval)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_get_socket_coro(sock, ZEND_THIS);\n\n    uint32_t optval_arg_index = 3;\n\n#define HANDLE_SUBCALL(res)                                                                                            \\\n    do {                                                                                                               \\\n        if (res == 1) {                                                                                                \\\n            goto default_case;                                                                                         \\\n        } else if (res == SUCCESS) {                                                                                   \\\n            RETURN_TRUE;                                                                                               \\\n        } else {                                                                                                       \\\n            RETURN_FALSE;                                                                                              \\\n        }                                                                                                              \\\n    } while (0)\n\n    if (level == IPPROTO_IP) {\n        int res = php_do_setsockopt_ip_mcast(sock->socket, level, optname, optval);\n        HANDLE_SUBCALL(res);\n    } else if (level == IPPROTO_IPV6) {\n        int res = php_do_setsockopt_ipv6_mcast(sock->socket, level, optname, optval);\n        if (res == 1) {\n            res = php_do_setsockopt_ipv6_rfc3542(sock->socket, level, optname, optval);\n        }\n        HANDLE_SUBCALL(res);\n    }\n\n    switch (optname) {\n    case SO_LINGER: {\n        const char l_onoff_key[] = \"l_onoff\";\n        const char l_linger_key[] = \"l_linger\";\n\n        if (Z_TYPE_P(optval) != IS_ARRAY) {\n            if (UNEXPECTED(Z_TYPE_P(optval) != IS_OBJECT)) {\n                zend_argument_type_error(optval_arg_index,\n                                         \"must be of type array when argument $option is SO_LINGER, %s given\",\n                                         zend_zval_value_name(optval));\n                RETURN_THROWS();\n            } else {\n                opt_ht = Z_OBJPROP_P(optval);\n            }\n        } else {\n            opt_ht = Z_ARRVAL_P(optval);\n        }\n\n        zval *l_onoff, *l_linger;\n        if ((l_onoff = zend_hash_str_find(opt_ht, l_onoff_key, sizeof(l_onoff_key) - 1)) == nullptr) {\n            php_error_docref(nullptr, E_WARNING, \"no key \\\"%s\\\" passed in optval\", l_onoff_key);\n            RETURN_FALSE;\n        }\n        if ((l_linger = zend_hash_str_find(opt_ht, l_linger_key, sizeof(l_linger_key) - 1)) == nullptr) {\n            php_error_docref(nullptr, E_WARNING, \"no key \\\"%s\\\" passed in optval\", l_linger_key);\n            RETURN_FALSE;\n        }\n\n        zend_long val_lonoff = zval_get_long(l_onoff);\n        zend_long val_linger = zval_get_long(l_linger);\n\n        if (val_lonoff < 0 || val_lonoff > USHRT_MAX) {\n            zend_argument_value_error(optval_arg_index, \"\\\"%s\\\" must be between 0 and %u\", l_onoff_key, USHRT_MAX);\n            RETURN_THROWS();\n        }\n\n        if (val_linger < 0 || val_linger > USHRT_MAX) {\n            zend_argument_value_error(optval_arg_index, \"\\\"%s\\\" must be between 0 and %d\", l_linger, USHRT_MAX);\n            RETURN_THROWS();\n        }\n\n        lv.l_onoff = (unsigned short) val_lonoff;\n        lv.l_linger = (unsigned short) val_linger;\n\n        optlen = sizeof(lv);\n        opt_ptr = (char *) &lv;\n        break;\n    }\n\n    case SO_RCVTIMEO:\n    case SO_SNDTIMEO: {\n        constexpr char sec_key[] = \"sec\";\n        constexpr char usec_key[] = \"usec\";\n        zval *sec, *usec;\n\n        if (Z_TYPE_P(optval) != IS_ARRAY) {\n            if (UNEXPECTED(Z_TYPE_P(optval) != IS_OBJECT)) {\n                zend_argument_type_error(optval_arg_index,\n                                         \"must be of type array when argument $option is %s, %s given\",\n                                         optname == SO_RCVTIMEO ? \"SO_RCVTIMEO\" : \"SO_SNDTIMEO\",\n                                         zend_zval_value_name(optval));\n                RETURN_THROWS();\n            } else {\n                opt_ht = Z_OBJPROP_P(optval);\n            }\n        } else {\n            opt_ht = Z_ARRVAL_P(optval);\n        }\n\n        opt_ht = Z_ARRVAL_P(optval);\n\n        if ((sec = zend_hash_str_find(opt_ht, sec_key, sizeof(sec_key) - 1)) == nullptr) {\n            php_error_docref(nullptr, E_WARNING, \"no key \\\"%s\\\" passed in optval\", sec_key);\n            RETURN_FALSE;\n        }\n        if ((usec = zend_hash_str_find(opt_ht, usec_key, sizeof(usec_key) - 1)) == nullptr) {\n            php_error_docref(nullptr, E_WARNING, \"no key \\\"%s\\\" passed in optval\", usec_key);\n            RETURN_FALSE;\n        }\n\n        zend_long valsec = zval_get_long(sec);\n        zend_long valusec = zval_get_long(usec);\n\n        tv.tv_sec = valsec;\n        tv.tv_usec = valusec;\n        sock->socket->set_timeout(&tv,\n                                  optname == SO_RCVTIMEO ? SW_TIMEOUT_READ : SW_TIMEOUT_CONNECT | SW_TIMEOUT_WRITE);\n        RETURN_TRUE;\n        break;\n    }\n#ifdef SO_BINDTODEVICE\n    case SO_BINDTODEVICE: {\n        if (Z_TYPE_P(optval) == IS_STRING) {\n            opt_ptr = Z_STRVAL_P(optval);\n            optlen = Z_STRLEN_P(optval);\n        } else {\n            opt_ptr = (char *) \"\";\n            optlen = 0;\n        }\n        break;\n    }\n#endif\n\n    default: {\n    default_case:\n        ov = zval_get_long(optval);\n\n        optlen = sizeof(ov);\n        opt_ptr = (char *) &ov;\n        break;\n    }\n    }\n\n    retval = sock->socket->get_socket()->set_option(level, optname, opt_ptr, optlen);\n    if (retval != 0) {\n        php_swoole_sys_error(E_WARNING, \"setsockopt(%d) failed\", sock->socket->get_fd());\n        RETURN_FALSE;\n    }\n\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_socket_coro, cancel) {\n    swoole_get_socket_coro(sock, ZEND_THIS);\n    zend_long event = SW_EVENT_READ;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(event)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(sock->socket->cancel(event == SW_EVENT_READ ? SW_EVENT_READ : SW_EVENT_WRITE));\n}\n\nstatic PHP_METHOD(swoole_socket_coro, setProtocol) {\n    swoole_get_socket_coro(sock, ZEND_THIS);\n    zval *zset;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ARRAY(zset)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (php_swoole_array_length(zset) == 0) {\n        RETURN_FALSE;\n    } else {\n        RETURN_BOOL(php_swoole_socket_set_protocol(sock->socket, zset));\n    }\n}\n\nstatic PHP_METHOD(swoole_socket_coro, sslHandshake) {\n    swoole_get_socket_coro(sock, ZEND_THIS);\n\n    RETURN_BOOL(sock->socket->ssl_handshake());\n}\n\nstatic PHP_METHOD(swoole_socket_coro, isClosed) {\n    RETURN_BOOL(php_swoole_socket_is_closed(ZEND_THIS));\n}\n\nstatic PHP_METHOD(swoole_socket_coro, import) {\n    zval *zstream;\n    php_stream *stream;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_RESOURCE(zstream)\n    ZEND_PARSE_PARAMETERS_END();\n\n    php_stream_from_zval(stream, zstream);\n\n    swSocketType type = SW_SOCK_TCP;\n    int socket_fd;\n\n    if (php_stream_cast(stream, PHP_STREAM_AS_SOCKETD, (void **) &socket_fd, 1)) {\n        /* error supposedly already shown */\n        RETURN_FALSE;\n    }\n\n    int sock_domain = AF_INET, sock_type = SOCK_STREAM;\n    php_sockaddr_storage addr;\n    socklen_t addr_len = sizeof(addr);\n\n#ifdef SO_DOMAIN\n    socklen_t sock_domain_len = sizeof(sock_domain);\n    if (getsockopt(socket_fd, SOL_SOCKET, SO_DOMAIN, &sock_domain, &sock_domain_len) == 0) {\n    } else\n#endif\n        if (getsockname(socket_fd, (struct sockaddr *) &addr, &addr_len) == 0) {\n        sock_domain = addr.ss_family;\n    } else {\n        php_swoole_sys_error(E_WARNING, \"getsockname() failed\");\n        RETURN_FALSE;\n    }\n\n#ifdef SO_TYPE\n    socklen_t sock_type_len = sizeof(sock_type);\n    if (getsockopt(socket_fd, SOL_SOCKET, SO_TYPE, &sock_type, &sock_type_len) < 0) {\n        php_swoole_sys_error(E_WARNING, \"getsockopt(SOL_SOCKET, SO_TYPE) failed\");\n        RETURN_FALSE;\n    }\n#endif\n\n    type = swoole::network::Socket::convert_to_type(sock_domain, sock_type);\n\n    /* determine blocking mode */\n    int t = fcntl(socket_fd, F_GETFL);\n    if (t < 0) {\n        php_swoole_sys_error(E_WARNING, \"fcntl(F_GETFL) failed\");\n        RETURN_FALSE;\n    }\n    zend_object *object = php_swoole_create_socket_from_fd(socket_fd, type);\n    SocketObject *sock = socket_coro_fetch_object(object);\n\n    ZVAL_COPY(&sock->zstream, zstream);\n    php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL);\n    sock->socket->get_socket()->nonblock = (t & O_NONBLOCK);\n\n    RETURN_OBJ(object);\n}\n"
  },
  {
    "path": "ext-src/swoole_sqlite.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2018 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: NathanFreeman  <mariasocute@163.com>                         |\n +----------------------------------------------------------------------+\n*/\n#include \"php_swoole_private.h\"\n#include \"php_swoole_cxx.h\"\n#include \"swoole_coroutine.h\"\n#include \"php_swoole_sqlite.h\"\n\n#ifdef SW_USE_SQLITE\nusing swoole::Coroutine;\n\nstatic bool swoole_sqlite_blocking = true;\n\nvoid swoole_sqlite_set_blocking(bool blocking) {\n    if (blocking) {\n        swoole_sqlite_blocking = blocking;\n        return;\n    }\n\n    int thread_safe_mode = sqlite3_threadsafe();\n    if (!thread_safe_mode) {\n        swoole_warning(\"hook sqlite coroutine failed because thread safe mode is single-thread.\");\n        return;\n    }\n    swoole_sqlite_blocking = blocking;\n}\n\nint swoole_sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs) {\n    swoole_trace_log(SW_TRACE_CO_SQLITE, \"sqlite3_open_v2\");\n\n    if (!swoole_sqlite_blocking && Coroutine::get_current()) {\n        flags |= SQLITE_OPEN_FULLMUTEX;\n    }\n\n    int result = 0;\n    php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_open_v2(filename, ppDb, flags, zVfs); });\n\n    return result;\n}\n\nint swoole_sqlite3_prepare_v2(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail) {\n    swoole_trace_log(SW_TRACE_CO_SQLITE, \"sqlite3_prepare_v2\");\n    int result = 0;\n    php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_prepare_v2(db, zSql, nByte, ppStmt, pzTail); });\n\n    return result;\n}\n\nint swoole_sqlite3_exec(\n    sqlite3 *db, const char *sql, int (*callback)(void *, int, char **, char **), void *argument, char **errmsg) {\n    swoole_trace_log(SW_TRACE_CO_SQLITE, \"sqlite3_exec\");\n    int result = 0;\n    php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_exec(db, sql, callback, argument, errmsg); });\n\n    return result;\n}\n\nint swoole_sqlite3_close(sqlite3 *db) {\n    swoole_trace_log(SW_TRACE_CO_SQLITE, \"sqlite3_close\");\n    int result = 0;\n    php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_close(db); });\n\n    return result;\n}\n\nint swoole_sqlite3_close_v2(sqlite3 *db) {\n    swoole_trace_log(SW_TRACE_CO_SQLITE, \"sqlite3_close_v2\");\n    int result = 0;\n    php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_close_v2(db); });\n\n    return result;\n}\n\nint swoole_sqlite3_step(sqlite3_stmt *stmt) {\n    swoole_trace_log(SW_TRACE_CO_SQLITE, \"sqlite3_step\");\n    int result = 0;\n    php_swoole_async(swoole_sqlite_blocking, [&]() { result = sqlite3_step(stmt); });\n\n    return result;\n}\n\nvoid php_swoole_sqlite_minit(int module_id) {\n    if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL(\"SQLITE_ATTR_OPEN_FLAGS\")) == nullptr) {\n#ifdef SQLITE_DETERMINISTIC\n#if PHP_VERSION_ID >= 80500\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"SQLITE_DETERMINISTIC\", (zend_long) SQLITE_DETERMINISTIC);\n#else\n        REGISTER_PDO_CLASS_CONST_LONG(\"SQLITE_DETERMINISTIC\", (zend_long) SQLITE_DETERMINISTIC);\n#endif\n#endif\n\n#if PHP_VERSION_ID >= 80500\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"SQLITE_ATTR_OPEN_FLAGS\", (zend_long) PDO_SQLITE_ATTR_OPEN_FLAGS);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"SQLITE_OPEN_READONLY\", (zend_long) SQLITE_OPEN_READONLY);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"SQLITE_OPEN_READWRITE\", (zend_long) SQLITE_OPEN_READWRITE);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"SQLITE_OPEN_CREATE\", (zend_long) SQLITE_OPEN_CREATE);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"SQLITE_ATTR_READONLY_STATEMENT\", (zend_long) PDO_SQLITE_ATTR_READONLY_STATEMENT);\n        REGISTER_PDO_CLASS_CONST_LONG_DEPRECATED_85(\"SQLITE_ATTR_EXTENDED_RESULT_CODES\",\n                                      (zend_long) PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES);\n#else\n        REGISTER_PDO_CLASS_CONST_LONG(\"SQLITE_ATTR_OPEN_FLAGS\", (zend_long) PDO_SQLITE_ATTR_OPEN_FLAGS);\n        REGISTER_PDO_CLASS_CONST_LONG(\"SQLITE_OPEN_READONLY\", (zend_long) SQLITE_OPEN_READONLY);\n        REGISTER_PDO_CLASS_CONST_LONG(\"SQLITE_OPEN_READWRITE\", (zend_long) SQLITE_OPEN_READWRITE);\n        REGISTER_PDO_CLASS_CONST_LONG(\"SQLITE_OPEN_CREATE\", (zend_long) SQLITE_OPEN_CREATE);\n        REGISTER_PDO_CLASS_CONST_LONG(\"SQLITE_ATTR_READONLY_STATEMENT\", (zend_long) PDO_SQLITE_ATTR_READONLY_STATEMENT);\n        REGISTER_PDO_CLASS_CONST_LONG(\"SQLITE_ATTR_EXTENDED_RESULT_CODES\",\n                                      (zend_long) PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES);\n#endif\n    }\n\n    php_pdo_unregister_driver(&swoole_pdo_sqlite_driver);\n    php_pdo_register_driver(&swoole_pdo_sqlite_driver);\n}\n\nvoid php_swoole_sqlite_mshutdown() {\n    php_pdo_unregister_driver(&swoole_pdo_sqlite_driver);\n}\n#endif\n"
  },
  {
    "path": "ext-src/swoole_stdext.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n */\n#include \"php_swoole_private.h\"\n\n#ifdef SW_STDEXT\n#include \"php_swoole_stdext.h\"\n#include \"php_swoole_cxx.h\"\n#include \"php_variables.h\"\n\nSW_EXTERN_C_BEGIN\n#include \"ext/pcre/php_pcre.h\"\n#include \"ext/json/php_json.h\"\n#include \"thirdparty/php/zend/zend_execute.c\"\nSW_EXTERN_C_END\n\nenum HashFlag {\n    HASH_FLAG_TYPED_ARRAY = (1 << 12),\n};\n\n/**\n * This module aims to enhance the PHP standard library without modifying the php-src core code.\n * It seeks to introduce strongly-typed arrays and enable the use of built-in methods directly on arrays and strings,\n * instead of relying on array_* or str_* functions.\n */\n\nstruct CallInfo {\n    zval func;\n    zval this_;\n    uint8_t op1_type;\n};\n\nstruct ArrayTypeValue {\n    uint8_t type_of_value;\n    uint8_t type_of_key;\n    uint16_t offset_of_value_type_str;\n    uint16_t len_of_value_type_str;\n\n    bool parse(const char *type_str, size_t len_of_type_str);\n};\n\nstruct ArrayTypeInfo {\n    ArrayTypeValue self;\n    ArrayTypeValue element;\n    zend_class_entry *value_ce;\n    uint16_t len_of_type_str;\n    char type_str[0];\n\n    bool parse(zend_string *type_def);\n    bool equals(const ArrayTypeInfo *other) const {\n        return self.type_of_key == other->self.type_of_key && self.type_of_value == other->self.type_of_value &&\n               len_of_type_str == other->len_of_type_str && memcmp(type_str, other->type_str, len_of_type_str) == 0;\n    }\n    bool element_type_equals(const ArrayTypeInfo *element_array_type_info) const {\n        return element_array_type_info->get_type_of_key() == element.type_of_key &&\n               element_array_type_info->get_type_of_value() == element.type_of_value &&\n               element_array_type_info->len_of_type_str == get_len_of_value_type_str() &&\n               memcmp(element_array_type_info->type_str, get_value_type_str(), get_len_of_value_type_str()) == 0;\n    }\n    const char *get_value_type_str() const {\n        return type_str + self.offset_of_value_type_str;\n    }\n    uint16_t get_len_of_type_str() const {\n        return len_of_type_str;\n    }\n    uint16_t get_len_of_value_type_str() const {\n        return self.len_of_value_type_str;\n    }\n    uint8_t get_type_of_key() const {\n        return self.type_of_key;\n    }\n    uint8_t get_type_of_value() const {\n        return self.type_of_value;\n    }\n    bool is_list() const {\n        return self.type_of_key == 0;\n    }\n    bool value_is_bool() const {\n        return self.type_of_value == IS_TRUE || self.type_of_value == IS_FALSE;\n    }\n    bool value_is_object() const {\n        return self.type_of_value == IS_OBJECT;\n    }\n    bool value_is_array() const {\n        return self.type_of_value == IS_ARRAY;\n    }\n    bool value_is_string() const {\n        return self.type_of_value == IS_STRING;\n    }\n    bool value_is_numeric() const {\n        return self.type_of_value == IS_LONG || self.type_of_value == IS_DOUBLE;\n    }\n    bool instance_of(const zval *value) const {\n        return instanceof_function(Z_OBJCE_P(value), value_ce);\n    }\n    bool check(const zend_array *ht, const zval *key, const zval *value) const;\n    ArrayTypeInfo *dup() const {\n        const auto copy = static_cast<ArrayTypeInfo *>(emalloc(sizeof(ArrayTypeInfo) + get_len_of_type_str() + 1));\n        memcpy(copy, this, sizeof(ArrayTypeInfo) + get_len_of_type_str() + 1);\n        return copy;\n    }\n};\n\nstatic zend_function *fn_swoole_call_array_method = nullptr;\nstatic zend_function *fn_swoole_call_string_method = nullptr;\nstatic zend_function *fn_swoole_call_stream_method = nullptr;\nstatic zend_function *fn_array_push = nullptr;\nstatic zend_function *fn_array_unshift = nullptr;\nstatic zend_function *fn_array_splice = nullptr;\nstatic zif_handler ori_handler_array_push;\nstatic zif_handler ori_handler_array_unshift;\nstatic zif_handler ori_handler_array_splice;\n\nstatic int opcode_handler_array_assign(zend_execute_data *execute_data);\nstatic int opcode_handler_array_assign_op(zend_execute_data *execute_data);\nstatic int opcode_handler_array_unset(zend_execute_data *execute_data);\nstatic int opcode_handler_foreach_begin(zend_execute_data *execute_data);\nstatic int opcode_handler_method_call(zend_execute_data *execute_data);\nstatic ArrayTypeInfo *get_type_info(zend_array *array);\n\nstatic PHP_FUNCTION(swoole_array_push);\nstatic PHP_FUNCTION(swoole_array_unshift);\nstatic PHP_FUNCTION(swoole_array_splice);\n\nstatic bool is_typed_array(const zval *zval) {\n    return HT_FLAGS(Z_ARRVAL_P(zval)) & HASH_FLAG_TYPED_ARRAY;\n}\n\nstatic std::unordered_map<std::string, std::string> array_methods = {\n    {\"all\", \"array_all\"},\n    {\"any\", \"array_any\"},\n    {\"changeKeyCase\", \"array_change_key_case\"},\n    {\"chunk\", \"array_chunk\"},\n    {\"column\", \"array_column\"},\n    {\"countValues\", \"array_count_values\"},\n    {\"diff\", \"array_diff\"},\n    {\"diffAssoc\", \"array_diff_assoc\"},\n    {\"diffKey\", \"array_diff_key\"},\n    {\"filter\", \"array_filter\"},\n    {\"find\", \"array_find\"},\n    {\"flip\", \"array_flip\"},\n    {\"intersect\", \"array_intersect\"},\n    {\"intersectAssoc\", \"array_intersect_assoc\"},\n    {\"isList\", \"array_is_list\"},\n    {\"keyExists\", \"swoole_array_key_exists\"},\n    {\"keyFirst\", \"array_key_first\"},\n    {\"keyLast\", \"array_key_last\"},\n    {\"keys\", \"array_keys\"},\n    {\"map\", \"swoole_array_map\"},\n    {\"pad\", \"array_pad\"},\n    {\"product\", \"array_product\"},\n    {\"rand\", \"array_rand\"},\n    {\"reduce\", \"array_reduce\"},\n    {\"replace\", \"array_replace\"},\n    {\"reverse\", \"array_reverse\"},\n    {\"search\", \"swoole_array_search\"},\n    {\"slice\", \"array_slice\"},\n    {\"sum\", \"array_sum\"},\n    {\"unique\", \"array_unique\"},\n    {\"values\", \"array_values\"},\n    {\"count\", \"count\"},\n    {\"merge\", \"array_merge\"},\n    {\"contains\", \"swoole_array_contains\"},\n    {\"join\", \"swoole_array_join\"},\n    {\"isTyped\", \"swoole_array_is_typed\"},\n    {\"isEmpty\", \"swoole_array_is_empty\"},\n    // pass by ref\n    {\"sort\", \"sort\"},\n    {\"pop\", \"array_pop\"},\n    {\"push\", \"array_push\"},\n    {\"shift\", \"array_shift\"},\n    {\"unshift\", \"array_unshift\"},\n    {\"splice\", \"array_splice\"},\n    {\"walk\", \"array_walk\"},\n    {\"replaceStr\", \"swoole_array_replace_str\"},\n    {\"iReplaceStr\", \"swoole_array_ireplace_str\"},\n    // serialize\n    {\"serialize\", \"serialize\"},\n    {\"marshal\", \"serialize\"},\n    {\"jsonEncode\", \"json_encode\"},\n};\n\n/**\n * i=ignore case, l=left, r=right\n */\nstatic std::unordered_map<std::string, std::string> string_methods = {\n    {\"length\", \"strlen\"},\n    {\"isEmpty\", \"swoole_str_is_empty\"},\n    {\"lower\", \"strtolower\"},\n    {\"upper\", \"strtoupper\"},\n    {\"lowerFirst\", \"lcfirst\"},\n    {\"upperFirst\", \"ucfirst\"},\n    {\"upperWords\", \"ucwords\"},\n    {\"addCSlashes\", \"addcslashes\"},\n    {\"addSlashes\", \"addslashes\"},\n    {\"chunkSplit\", \"chunk_split\"},\n    {\"countChars\", \"count_chars\"},\n    {\"htmlEntityDecode\", \"html_entity_decode\"},\n    {\"htmlEntityEncode\", \"htmlentities\"},\n    {\"htmlSpecialCharsEncode\", \"htmlspecialchars\"},\n    {\"htmlSpecialCharsDecode\", \"htmlspecialchars_decode\"},\n    {\"trim\", \"trim\"},\n    {\"lTrim\", \"ltrim\"},\n    {\"rTrim\", \"rtrim\"},\n    {\"parseStr\", \"swoole_parse_str\"},\n    {\"parseUrl\", \"parse_url\"},\n    {\"contains\", \"str_contains\"},\n    {\"incr\", \"str_increment\"},\n    {\"decr\", \"str_decrement\"},\n    {\"pad\", \"str_pad\"},\n    {\"repeat\", \"str_repeat\"},\n    {\"replace\", \"swoole_str_replace\"},\n    {\"iReplace\", \"swoole_str_ireplace\"},\n    {\"shuffle\", \"str_shuffle\"},\n    {\"split\", \"swoole_str_split\"},  // explode\n    {\"startsWith\", \"str_starts_with\"},\n    {\"endsWith\", \"str_ends_with\"},\n    {\"wordCount\", \"str_word_count\"},\n    {\"iCompare\", \"strcasecmp\"},\n    {\"compare\", \"strcmp\"},\n    {\"find\", \"strstr\"},\n    {\"iFind\", \"stristr\"},\n    {\"stripTags\", \"strip_tags\"},\n    {\"stripCSlashes\", \"stripcslashes\"},\n    {\"stripSlashes\", \"stripslashes\"},\n    {\"iIndexOf\", \"stripos\"},\n    {\"indexOf\", \"strpos\"},\n    {\"lastIndexOf\", \"strrpos\"},\n    {\"iLastIndexOf\", \"strripos\"},\n    {\"lastCharIndexOf\", \"strrchr\"},\n    {\"substr\", \"substr\"},\n    {\"substrCompare\", \"substr_compare\"},\n    {\"substrCount\", \"substr_count\"},\n    {\"substrReplace\", \"substr_replace\"},\n    {\"reverse\", \"strrev\"},\n    {\"md5\", \"md5\"},\n    {\"sha1\", \"sha1\"},\n    {\"crc32\", \"crc32\"},\n    {\"hash\", \"swoole_hash\"},\n    {\"hashCode\", \"swoole_hashcode\"},\n    {\"base64Decode\", \"base64_decode\"},\n    {\"base64Encode\", \"base64_encode\"},\n    {\"urlDecode\", \"urldecode\"},\n    {\"urlEncode\", \"urlencode\"},\n    {\"rawUrlEncode\", \"rawurlencode\"},\n    {\"rawUrlDecode\", \"rawurldecode\"},\n    {\"match\", \"swoole_str_match\"},\n    {\"matchAll\", \"swoole_str_match_all\"},\n    {\"isNumeric\", \"is_numeric\"},\n    // mbstring\n    {\"mbUpperFirst\", \"mb_ucfirst\"},\n    {\"mbLowerFirst\", \"mb_lcfirst\"},\n    {\"mbTrim\", \"mb_trim\"},\n    {\"mbSubstrCount\", \"mb_substr_count\"},\n    {\"mbSubstr\", \"mb_substr\"},\n    {\"mbUpper\", \"mb_strtoupper\"},\n    {\"mbLower\", \"mb_strtolower\"},\n    {\"mbFind\", \"mb_strstr\"},\n    {\"mbIndexOf\", \"mb_strpos\"},\n    {\"mbLastIndexOf\", \"mb_strrpos\"},\n    {\"mbILastIndexOf\", \"mb_strripos\"},\n    {\"mbLastCharIndexOf\", \"mb_strrchr\"},\n    {\"mbILastCharIndex\", \"mb_strrichr\"},\n    {\"mbLength\", \"mb_strlen\"},\n    {\"mbIFind\", \"mb_stristr\"},\n    {\"mbIIndexOf\", \"mb_stripos\"},\n    {\"mbCut\", \"mb_strcut\"},\n    {\"mbRTrim\", \"mb_rtrim\"},\n    {\"mbLTrim\", \"mb_ltrim\"},\n    {\"mbDetectEncoding\", \"mb_detect_encoding\"},\n    {\"mbConvertEncoding\", \"mb_convert_encoding\"},\n    {\"mbConvertCase\", \"mb_convert_case\"},\n    // serialize\n    {\"unserialize\", \"unserialize\"},\n    {\"unmarshal\", \"unserialize\"},\n    {\"jsonDecode\", \"swoole_str_json_decode\"},\n    {\"jsonDecodeToObject\", \"swoole_str_json_decode_to_object\"},\n};\n\nstatic std::unordered_map<std::string, std::string> stream_methods = {\n    {\"write\", \"fwrite\"},\n    {\"read\", \"fread\"},\n    {\"close\", \"fclose\"},\n    {\"dataSync\", \"fdatasync\"},\n    {\"sync\", \"fsync\"},\n    {\"truncate\", \"ftruncate\"},\n    {\"stat\", \"fstat\"},\n    {\"seek\", \"fseek\"},\n    {\"tell\", \"ftell\"},\n    {\"lock\", \"flock\"},\n    {\"eof\", \"feof\"},\n    {\"getChar\", \"fgetc\"},\n    {\"getLine\", \"fgets\"},\n};\n\nstatic void move_first_element(const zval src[], zval dst[], int size, int position) {\n    zval first = src[0];\n    for (int i = 0; i < position; i++) {\n        dst[i] = src[i + 1];\n    }\n    dst[position] = first;\n    for (int i = position + 1; i < size; i++) {\n        dst[i] = src[i];\n    }\n}\n\nstatic void call_func_move_first_arg(zend_function *fn, zend_execute_data *execute_data, zval *retval, int position) {\n    const zval *arg_ptr = ZEND_CALL_ARG(execute_data, 1);\n    const int arg_count = ZEND_CALL_NUM_ARGS(execute_data);\n    const auto argv = static_cast<zval *>(ecalloc(arg_count, sizeof(zval)));\n    move_first_element(arg_ptr, argv, arg_count, position);\n    zend_call_known_function(fn, nullptr, nullptr, retval, arg_count, argv, nullptr);\n    efree(argv);\n}\n\nstatic void call_method(const std::unordered_map<std::string, std::string> &method_map,\n                        zend_execute_data *execute_data,\n                        zval *retval) {\n    const auto call_info = reinterpret_cast<CallInfo *>(execute_data->run_time_cache);\n    const auto name = std::string(Z_STRVAL(call_info->func), Z_STRLEN(call_info->func));\n    const auto iter = method_map.find(name);\n\n    ON_SCOPE_EXIT {\n        efree(call_info);\n        execute_data->run_time_cache = nullptr;\n    };\n\n    if (iter == method_map.end()) {\n        zend_throw_error(\n            nullptr, \"The method `%s` is undefined on %s\", name.c_str(), zend_zval_type_name(&call_info->this_));\n        return;\n    }\n    const auto real_fn = iter->second;\n    const auto fn = zend::get_function(real_fn.c_str(), real_fn.length());\n    if (!fn) {\n        zend_throw_error(nullptr, \"The function `%s` is undefined\", real_fn.c_str());\n        return;\n    }\n\n    const zval *arg_ptr = ZEND_CALL_ARG(execute_data, 1);\n    const int arg_count = ZEND_CALL_NUM_ARGS(execute_data);\n    const auto argv = static_cast<zval *>(ecalloc(arg_count + 1, sizeof(zval)));\n\n    argv[0] = call_info->this_;\n    for (int i = 0; i < arg_count; i++) {\n        argv[i + 1] = arg_ptr[i];\n    }\n\n    zend_call_known_function(fn, nullptr, nullptr, retval, arg_count + 1, argv, nullptr);\n    if (call_info->op1_type == IS_VAR) {\n        zval_ptr_dtor(&call_info->this_);\n    }\n    efree(argv);\n}\n\nstatic int opcode_handler_method_call(zend_execute_data *execute_data) {\n    const zend_op *opline = EX(opline);\n    zval *object;\n    if (opline->op1_type == IS_CONST) {\n        object = RT_CONSTANT(opline, opline->op1);\n    } else if (UNEXPECTED(opline->op1_type == IS_UNUSED)) {\n        return ZEND_USER_OPCODE_DISPATCH;\n    } else {\n        object = EX_VAR(opline->op1.var);\n    }\n\n    auto type = Z_TYPE_P(object);\n    if (type == IS_REFERENCE) {\n        type = Z_TYPE_P(Z_REFVAL_P(object));\n    }\n\n    if (type == IS_ARRAY || type == IS_STRING || type == IS_RESOURCE) {\n        auto call_info = static_cast<CallInfo *>(emalloc(sizeof(CallInfo)));\n        call_info->func = *RT_CONSTANT(opline, opline->op2);\n        call_info->this_ = *object;\n        call_info->op1_type = opline->op1_type;\n\n        zend_function *fbc = nullptr;\n        switch (type) {\n        case IS_ARRAY:\n            fbc = fn_swoole_call_array_method;\n            break;\n        case IS_STRING:\n            fbc = fn_swoole_call_string_method;\n            break;\n        default:\n            fbc = fn_swoole_call_stream_method;\n        }\n\n        zend_execute_data *call =\n            zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION, fbc, opline->extended_value, object);\n\n        call->run_time_cache = reinterpret_cast<void **>(call_info);\n        call->prev_execute_data = EX(call);\n        EX(call) = call;\n        EX(opline)++;\n\n        return ZEND_USER_OPCODE_CONTINUE;\n    }\n\n    return ZEND_USER_OPCODE_DISPATCH;\n}\n\nvoid php_swoole_stdext_minit(int module_number) {\n    zend_set_user_opcode_handler(ZEND_INIT_METHOD_CALL, opcode_handler_method_call);\n    zend_set_user_opcode_handler(ZEND_ASSIGN_DIM, opcode_handler_array_assign);\n    zend_set_user_opcode_handler(ZEND_ASSIGN_DIM_OP, opcode_handler_array_assign_op);\n    zend_set_user_opcode_handler(ZEND_UNSET_DIM, opcode_handler_array_unset);\n    zend_set_user_opcode_handler(ZEND_FE_RESET_RW, opcode_handler_foreach_begin);\n\n    fn_swoole_call_array_method = zend::get_function(CG(function_table), ZEND_STRL(\"swoole_call_array_method\"));\n    fn_swoole_call_string_method = zend::get_function(CG(function_table), ZEND_STRL(\"swoole_call_string_method\"));\n    fn_swoole_call_stream_method = zend::get_function(CG(function_table), ZEND_STRL(\"swoole_call_stream_method\"));\n\n    fn_array_push = zend::get_function(CG(function_table), ZEND_STRL(\"array_push\"));\n    fn_array_unshift = zend::get_function(CG(function_table), ZEND_STRL(\"array_unshift\"));\n    fn_array_splice = zend::get_function(CG(function_table), ZEND_STRL(\"array_splice\"));\n\n    ori_handler_array_push = fn_array_push->internal_function.handler;\n    fn_array_push->internal_function.handler = ZEND_FN(swoole_array_push);\n    ori_handler_array_unshift = fn_array_unshift->internal_function.handler;\n    fn_array_unshift->internal_function.handler = ZEND_FN(swoole_array_unshift);\n    ori_handler_array_splice = fn_array_splice->internal_function.handler;\n    fn_array_splice->internal_function.handler = ZEND_FN(swoole_array_splice);\n}\n\n#define SW_CREATE_PHP_FUNCTION_WRAPPER(php_func_name, swoole_func_name, position)                                      \\\n    PHP_FUNCTION(swoole_func_name) {                                                                                   \\\n        static zend_function *fn_##swoole_func_name = nullptr;                                                         \\\n        if (!fn_##swoole_func_name) {                                                                                  \\\n            fn_##swoole_func_name = zend::get_function(CG(function_table), ZEND_STRL(#php_func_name));                 \\\n        }                                                                                                              \\\n        call_func_move_first_arg(fn_##swoole_func_name, execute_data, return_value, position);                         \\\n    }\n\n// array\nSW_CREATE_PHP_FUNCTION_WRAPPER(array_search, swoole_array_search, 1);\nSW_CREATE_PHP_FUNCTION_WRAPPER(in_array, swoole_array_contains, 1);\nSW_CREATE_PHP_FUNCTION_WRAPPER(implode, swoole_array_join, 1);\nSW_CREATE_PHP_FUNCTION_WRAPPER(array_key_exists, swoole_array_key_exists, 1);\nSW_CREATE_PHP_FUNCTION_WRAPPER(array_map, swoole_array_map, 1);\nSW_CREATE_PHP_FUNCTION_WRAPPER(str_replace, swoole_array_replace_str, 2);\nSW_CREATE_PHP_FUNCTION_WRAPPER(str_ireplace, swoole_array_ireplace_str, 2);\n\n// string\nSW_CREATE_PHP_FUNCTION_WRAPPER(explode, swoole_str_split, 1);\nSW_CREATE_PHP_FUNCTION_WRAPPER(hash, swoole_hash, 1);\nSW_CREATE_PHP_FUNCTION_WRAPPER(str_replace, swoole_str_replace, 2);\nSW_CREATE_PHP_FUNCTION_WRAPPER(str_ireplace, swoole_str_ireplace, 2);\n\nPHP_FUNCTION(swoole_parse_str) {\n    char *arg;\n    size_t arglen;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STRING(arg, arglen)\n    ZEND_PARSE_PARAMETERS_END();\n\n    array_init(return_value);\n    auto res = estrndup(arg, arglen);\n    sapi_module.treat_data(PARSE_STRING, res, return_value);\n}\n\nPHP_FUNCTION(swoole_str_is_empty) {\n    zend_string *str;\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STR(str)\n    ZEND_PARSE_PARAMETERS_END();\n    RETURN_BOOL(str->len == 0);\n}\n\nPHP_FUNCTION(swoole_array_is_empty) {\n    zval *array;\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ARRAY(array)\n    ZEND_PARSE_PARAMETERS_END();\n    RETURN_BOOL(zend_array_count(Z_ARRVAL_P(array)) == 0);\n}\n\nPHP_FUNCTION(swoole_call_array_method) {\n    call_method(array_methods, execute_data, return_value);\n}\n\nPHP_FUNCTION(swoole_call_string_method) {\n    call_method(string_methods, execute_data, return_value);\n}\n\nPHP_FUNCTION(swoole_call_stream_method) {\n    call_method(stream_methods, execute_data, return_value);\n}\n\nstatic HashTable *make_typed_array(const uint32_t nSize, const uint32_t nTypeStr) {\n    const auto ht = static_cast<zend_array *>(emalloc(sizeof(HashTable) + sizeof(ArrayTypeInfo) + nTypeStr + 1));\n    _zend_hash_init(ht, nSize, ZVAL_PTR_DTOR, false);\n    HT_FLAGS(ht) |= HASH_FLAG_TYPED_ARRAY;\n    return ht;\n}\n\nvoid copy_array_type_info(zval *container, zend_array *src) {\n    auto src_type_info = get_type_info(src);\n    zend_array *ht = Z_ARRVAL_P(container);\n    auto extra_size = sizeof(ArrayTypeInfo) + src_type_info->get_len_of_type_str() + 1;\n    const auto tmp = static_cast<zend_array *>(emalloc(sizeof(HashTable) + extra_size));\n    memcpy(tmp, ht, sizeof(HashTable));\n    memcpy(reinterpret_cast<char *>(tmp) + sizeof(HashTable), src_type_info, extra_size);\n    HT_FLAGS(tmp) |= HASH_FLAG_TYPED_ARRAY;\n    Z_ARRVAL_P(container) = tmp;\n    efree(ht);\n}\n\nstatic ArrayTypeInfo *get_type_info(zend_array *array) {\n    return reinterpret_cast<ArrayTypeInfo *>(reinterpret_cast<char *>(array) + sizeof(HashTable));\n}\n\nstatic zend_string *get_array_type_def(const ArrayTypeInfo *info) {\n    zend_string *result = zend_string_alloc(info->get_len_of_value_type_str() + 16, false);\n    char *p = result->val;\n    *p = '<';\n    if (info->get_type_of_key() == IS_STRING) {\n        p++;\n        strcpy(p, \"string,\");\n        p += 7;\n    } else if (info->get_type_of_key() == IS_LONG) {\n        p++;\n        strcpy(p, \"int,\");\n        p += 4;\n    }\n\n    memcpy(p, info->get_value_type_str(), info->get_len_of_value_type_str());\n    p += info->get_len_of_value_type_str();\n    *p = '>';\n    p++;\n    *p = '\\0';\n    result->len = p - result->val;\n\n    return result;\n}\n\nbool ArrayTypeInfo::check(const zend_array *ht, const zval *key, const zval *value) const {\n    if (get_type_of_key() > 0) {\n        if (Z_TYPE_P(key) != get_type_of_key()) {\n            zend_type_error(\"Array key type mismatch, expected `%s`, got `%s`\",\n                            zend_get_type_by_const(get_type_of_key()),\n                            zend_get_type_by_const(Z_TYPE_P(key)));\n            return false;\n        }\n    } else {\n        if (Z_TYPE_P(key) == IS_LONG) {\n            if (Z_LVAL_P(key) > zend_hash_num_elements(ht)) {\n                zend_throw_error(\n                    nullptr, \"Incorrect array key `%ld`, out of the permitted range\", (long) Z_LVAL_P(key));\n                return false;\n            }\n        } else if (!(Z_TYPE_P(key) == IS_UNDEF || Z_TYPE_P(key) == IS_NULL)) {\n            zend_throw_error(nullptr, \"Incorrect array key, must be undef or int\");\n            return false;\n        }\n    }\n    ZVAL_DEREF(value);\n    if (value_is_bool() && ZVAL_IS_BOOL(value)) {\n        return true;\n    }\n    if (Z_TYPE_P(value) != get_type_of_value()) {\n        zend_type_error(\"Array value type mismatch, expected `%s`, got `%s`\",\n                        zend_get_type_by_const(get_type_of_value()),\n                        zend_get_type_by_const(Z_TYPE_P(value)));\n        return false;\n    }\n    if (value_is_object() && !instance_of(value)) {\n        zend_type_error(\n            \"Array value type mismatch, expected `%s`, got `%s`\", value_ce->name->val, Z_OBJCE_P(value)->name->val);\n        return false;\n    }\n    if (value_is_array()) {\n        const auto element_array_type_info = get_type_info(Z_ARRVAL_P(value));\n        const auto element_ht = Z_ARRVAL_P(value);\n        if (!(HT_FLAGS(element_ht) & HASH_FLAG_TYPED_ARRAY)) {\n            zend_type_error(\"Array value type mismatch, expected `%.*s`, got `array`\",\n                            get_len_of_value_type_str(),\n                            get_value_type_str());\n            return false;\n        }\n        if (!element_type_equals(element_array_type_info)) {\n            const auto element_type_str = get_array_type_def(element_array_type_info);\n            zend_type_error(\"Array value type mismatch, expected `%.*s`, got `%.*s`\",\n                            get_len_of_value_type_str(),\n                            get_value_type_str(),\n                            (int) ZSTR_LEN(element_type_str),\n                            ZSTR_VAL(element_type_str));\n            zend_string_release(element_type_str);\n            return false;\n        }\n    }\n    return true;\n}\n\nstatic zval *get_array_on_opline(const zend_op *opline EXECUTE_DATA_DC) {\n    auto array = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC);\n    if (ZVAL_IS_REF(array)) {\n        array = Z_REFVAL_P(array);\n    }\n    if (!ZVAL_IS_ARRAY(array)) {\n        return nullptr;\n    }\n    return array;\n}\n\n#ifdef DEBUG\nstatic void debug_val(const char *tag, int op_type, zval *value) {\n    printf(\"[%s] refcount=%d, op1_type=%d, type=%s, refcounted=%d\\n\",\n           tag,\n           Z_REFCOUNTED_P(value) ? Z_REFCOUNT_P(value) : 0,\n           op_type,\n           zend_get_type_by_const(Z_TYPE_P(value)),\n           Z_REFCOUNTED_P(value));\n}\n#else\n#define debug_val(tag, op_type, value)\n#endif\n\n// In a release version, this function suddenly changes from static to ZEND_API.\n// We don't know which version it is. In principle, the ZEND_API should not be changed in the release version,\n// but PHP still does so, which is against the R&D specification. We have to copy the code of this function once.\n#define zend_cannot_add_element sw_zend_cannot_add_element\nstatic zend_never_inline ZEND_COLD void ZEND_FASTCALL sw_zend_cannot_add_element(void) {\n    zend_throw_error(NULL, \"Cannot add element to the array as the next element is already occupied\");\n}\n\nstatic void array_add_or_update(const zend_op *opline, zval *container, const zval *key, zval *value EXECUTE_DATA_DC) {\n    zval *var_ptr;\n    HashTable *source = Z_ARRVAL_P(container);\n    SEPARATE_ARRAY(container);\n    if (source != Z_ARRVAL_P(container)) {\n        copy_array_type_info(container, source);\n    }\n    HashTable *ht = Z_ARRVAL_P(container);\n    const zend_op *op_data = opline + 1;\n\n    if (ZVAL_IS_NULL(key)) {\n        var_ptr = zend_hash_next_index_insert(ht, value);\n        if (UNEXPECTED(!var_ptr)) {\n            zend_cannot_add_element();\n            goto assign_dim_op_ret_null;\n        }\n    } else {\n        zval *variable_ptr;\n        if (opline->op2_type == IS_CONST) {\n            variable_ptr = zend_fetch_dimension_address_inner_W_CONST(Z_ARRVAL_P(container), key EXECUTE_DATA_CC);\n        } else {\n            variable_ptr = zend_fetch_dimension_address_inner_W(Z_ARRVAL_P(container), key EXECUTE_DATA_CC);\n        }\n        if (UNEXPECTED(variable_ptr == nullptr)) {\n            goto assign_dim_op_ret_null;\n        }\n        debug_val(\"1\", op_data->op1_type, value);\n        var_ptr = zend_assign_to_variable(variable_ptr, value, op_data->op1_type, EX_USES_STRICT_TYPES());\n        debug_val(\"2\", op_data->op1_type, value);\n        if (UNEXPECTED(!var_ptr)) {\n        assign_dim_op_ret_null:\n            FREE_OP(op_data->op1_type, op_data->op1.var);\n            if (UNEXPECTED(RETURN_VALUE_USED(opline))) {\n                ZVAL_NULL(EX_VAR(opline->result.var));\n            }\n            return;\n        }\n    }\n    debug_val(\"3\", op_data->op1_type, value);\n    if (UNEXPECTED(RETURN_VALUE_USED(opline))) {\n        ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);\n    }\n    if (op_data->op1_type == IS_VAR) {\n        Z_TRY_ADDREF_P(value);\n    }\n    FREE_OP(op_data->op1_type, op_data->op1.var);\n    debug_val(\"4\", op_data->op1_type, value);\n}\n\nstatic void array_op(const zend_op *opline, zval *container, const zval *key, zval *value EXECUTE_DATA_DC) {\n    HashTable *source = Z_ARRVAL_P(container);\n    SEPARATE_ARRAY(container);\n    if (source != Z_ARRVAL_P(container)) {\n        copy_array_type_info(container, source);\n    }\n    const auto type_info = get_type_info(Z_ARRVAL_P(container));\n\n    zval *variable_ptr;\n    if ((opline + 1)->op1_type == IS_CONST) {\n        variable_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), key EXECUTE_DATA_CC);\n    } else {\n        variable_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), key EXECUTE_DATA_CC);\n    }\n    if (UNEXPECTED(variable_ptr == nullptr)) {\n    assign_dim_op_ret_null:\n        FREE_OP((opline + 1)->op1_type, (opline + 1)->op1.var);\n        if (UNEXPECTED(RETURN_VALUE_USED(opline))) {\n            ZVAL_NULL(EX_VAR(opline->result.var));\n        }\n        return;\n    }\n    do {\n        if (UNEXPECTED(Z_ISREF_P(variable_ptr))) {\n            zend_reference *ref = Z_REF_P(variable_ptr);\n            variable_ptr = Z_REFVAL_P(variable_ptr);\n            if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) {\n                zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC);\n                break;\n            }\n        }\n        const auto opcode = opline->extended_value;\n        if (opcode == ZEND_CONCAT) {\n            if (!type_info->value_is_string()) {\n                zend_type_error(\"Only string support concat operation\");\n                goto assign_dim_op_ret_null;\n            }\n        } else {\n            if (!type_info->value_is_numeric()) {\n                zend_type_error(\"Only int or float support arithmetic operation\");\n                goto assign_dim_op_ret_null;\n            }\n        }\n        zend_binary_op(variable_ptr, variable_ptr, value OPLINE_CC);\n    } while (false);\n\n    if (UNEXPECTED(RETURN_VALUE_USED(opline))) {\n        ZVAL_COPY(EX_VAR(opline->result.var), variable_ptr);\n    }\n    FREE_OP((opline + 1)->op1_type, (opline + 1)->op1.var);\n}\n\ntypedef std::function<void(const zend_op *, zval *, const zval *, zval *EXECUTE_DATA_DC)> ArrayFn;\n\nstatic int opcode_handler_array(zend_execute_data *execute_data, const ArrayFn &fn) {\n    const zend_op *opline = EX(opline);\n    const zend_op *op_data = opline + 1;\n    zval *array = get_array_on_opline(opline EXECUTE_DATA_CC);\n    if (UNEXPECTED(!array)) {\n        return ZEND_USER_OPCODE_DISPATCH;\n    }\n    zend_array *ht = Z_ARRVAL_P(array);\n    if (!(HT_FLAGS(ht) & HASH_FLAG_TYPED_ARRAY)) {\n        return ZEND_USER_OPCODE_DISPATCH;\n    }\n    const auto value = get_op_data_zval_ptr_r(op_data->op1_type, op_data->op1);\n    zval *key;\n    if (opline->op2_type == IS_CONST) {\n        key = RT_CONSTANT(opline, opline->op2);\n    } else if (UNEXPECTED(opline->op2_type == IS_UNUSED)) {\n        key = &EG(uninitialized_zval);\n    } else {\n        key = EX_VAR(opline->op2.var);\n    }\n    const auto type_info = get_type_info(ht);\n    if (!type_info->check(ht, key, value)) {\n        FREE_OP(op_data->op1_type, op_data->op1.var);\n        return ZEND_USER_OPCODE_CONTINUE;\n    }\n    fn(opline, array, key, value EXECUTE_DATA_CC);\n    EX(opline) += 2;\n    return ZEND_USER_OPCODE_CONTINUE;\n}\n\nstatic int opcode_handler_array_assign(zend_execute_data *execute_data) {\n    return opcode_handler_array(execute_data, array_add_or_update);\n}\n\nstatic int opcode_handler_array_assign_op(zend_execute_data *execute_data) {\n    return opcode_handler_array(execute_data, array_op);\n}\n\nstatic int opcode_handler_foreach_begin(zend_execute_data *execute_data) {\n    const zend_op *opline = EX(opline);\n    zval *array;\n    if (opline->op1_type == IS_VAR || opline->op1_type == IS_CV) {\n        array = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC);\n    } else if (opline->op1_type == IS_CONST) {\n        array = RT_CONSTANT(opline, opline->op1);\n    } else {\n        array = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);\n    }\n    ZVAL_DEREF(array);\n    if (UNEXPECTED(!array || !ZVAL_IS_ARRAY(array))) {\n        return ZEND_USER_OPCODE_DISPATCH;\n    }\n    const zend_array *ht = Z_ARRVAL_P(array);\n    if (HT_FLAGS(ht) & HASH_FLAG_TYPED_ARRAY) {\n        zend_throw_error(nullptr, \"The type array do not support using references for element value during iteration\");\n        ZVAL_UNDEF(EX_VAR(opline->result.var));\n        Z_FE_ITER_P(EX_VAR(opline->result.var)) = static_cast<uint32_t>(-1);\n\n        return ZEND_USER_OPCODE_CONTINUE;\n    }\n    return ZEND_USER_OPCODE_DISPATCH;\n}\n\nstatic int opcode_handler_array_unset(zend_execute_data *execute_data) {\n    const zend_op *opline = EX(opline);\n    const zval *array = get_array_on_opline(opline EXECUTE_DATA_CC);\n    if (!array) {\n        return ZEND_USER_OPCODE_DISPATCH;\n    }\n    zend_array *ht = Z_ARRVAL_P(array);\n    if (!(HT_FLAGS(ht) & HASH_FLAG_TYPED_ARRAY)) {\n        return ZEND_USER_OPCODE_DISPATCH;\n    }\n    const auto type_info = get_type_info(ht);\n    if (type_info->is_list()) {\n        zend_throw_error(nullptr, \"The typed array list do not support random deletion of elements\");\n        return ZEND_USER_OPCODE_CONTINUE;\n    }\n    return ZEND_USER_OPCODE_DISPATCH;\n}\n\nstatic void remove_all_spaces(char **val, uint16_t *len) {\n    if (!*val || *len == 0) {\n        return;\n    }\n\n    const char *src = *val;\n    char *dst = *val;\n    size_t new_len = 0;\n\n    for (size_t i = 0; i < *len; i++) {\n        if (!isspace((uchar) *src)) {\n            *dst = *src;\n            dst++;\n            new_len++;\n        }\n        src++;\n    }\n\n    *len = new_len;\n}\n\nstatic int8_t get_type(const char *val, size_t len) {\n    if (SW_STRCASEEQ(val, len, \"int\")) {\n        return IS_LONG;\n    } else if (SW_STRCASEEQ(val, len, \"float\")) {\n        return IS_DOUBLE;\n    } else if (SW_STRCASEEQ(val, len, \"string\")) {\n        return IS_STRING;\n    } else if (SW_STRCASEEQ(val, len, \"bool\")) {\n        return IS_TRUE;  // IS_TRUE or IS_FALSE\n    } else if (val[0] == '<' && val[len - 1] == '>') {\n        return IS_ARRAY;\n    } else if (SW_STRCASEEQ(val, len, \"resource\")) {\n        return IS_RESOURCE;\n    } else if (SW_STRCASEEQ(val, len, \"null\")) {\n        return IS_NULL;\n    } else {\n        return IS_OBJECT;\n    }\n}\n\nbool ArrayTypeValue::parse(const char *type_str, const size_t len_of_type_str) {\n    auto pos = strchr(type_str, ',');\n    if (pos == nullptr) {\n        type_of_key = 0;\n        offset_of_value_type_str = 1;\n    } else {\n        type_of_key = get_type(type_str + 1, pos - type_str - 1);\n        if (type_of_key != IS_STRING && type_of_key != IS_LONG) {\n            zend_throw_error(nullptr, \"The key type of array must be string or int, but got %s\", pos + 1);\n            return false;\n        }\n        offset_of_value_type_str = pos - type_str + 1;\n    }\n    len_of_value_type_str = len_of_type_str - offset_of_value_type_str - 1;\n    type_of_value = get_type(type_str + offset_of_value_type_str, len_of_value_type_str);\n    return true;\n}\n\nbool ArrayTypeInfo::parse(zend_string *type_def) {\n    if (type_def->len >= 65535) {\n        zend_throw_error(nullptr, \"The type definition string is too long (must be less than 65535 characters)\");\n        return false;\n    }\n    zend_string *lc_type_def = zend_string_tolower(type_def);\n    memcpy(type_str, lc_type_def->val, lc_type_def->len + 1);\n    len_of_type_str = lc_type_def->len;\n    zend_string_release(lc_type_def);\n\n    char *tmp_type_str = type_str;\n    remove_all_spaces(&tmp_type_str, &len_of_type_str);\n    tmp_type_str[len_of_type_str] = '\\0';\n    if (tmp_type_str != type_str) {\n        memmove(type_str, tmp_type_str, len_of_type_str + 1);\n    }\n\n    if (type_str[0] != '<' || type_str[len_of_type_str - 1] != '>') {\n        zend_throw_error(nullptr, \"The type definition of typed array must start with '<' and end with '>'\");\n        return false;\n    }\n    if (!self.parse(type_str, len_of_type_str)) {\n        return false;\n    }\n\n    if (self.type_of_value == IS_OBJECT) {\n        zend::String type_str_of_value(type_str + self.offset_of_value_type_str, self.len_of_value_type_str);\n        value_ce = zend_lookup_class(type_str_of_value.get());\n        if (!value_ce) {\n            zend_throw_error(nullptr, \"Class '%s' not found\", type_str_of_value.val());\n            return false;\n        }\n    }\n\n    return true;\n}\n\nPHP_FUNCTION(swoole_typed_array) {\n    zend_string *type_def;\n    zval *init_values = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_STR(type_def)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_ARRAY(init_values)\n    ZEND_PARSE_PARAMETERS_END();\n\n    auto tmp_info = static_cast<ArrayTypeInfo *>(emalloc(sizeof(ArrayTypeInfo) + ZSTR_LEN(type_def) + 1));\n    if (!tmp_info->parse(type_def)) {\n        efree(tmp_info);\n        RETURN_NULL();\n    }\n\n    if (init_values && is_typed_array(init_values)) {\n        auto type_info = get_type_info(Z_ARRVAL_P(init_values));\n        if (tmp_info->equals(type_info)) {\n            ZVAL_COPY(return_value, init_values);\n        } else {\n            zend_throw_error(nullptr, \"The type definition of the typed array does not match the initial values\");\n        }\n        efree(tmp_info);\n        return;\n    }\n\n    auto n = init_values ? zend_array_count(Z_ARRVAL_P(init_values)) : 0;\n    auto array = make_typed_array(n, tmp_info->len_of_type_str);\n    ZVAL_ARR(return_value, array);\n    auto info = get_type_info(array);\n    memcpy(info, tmp_info, sizeof(ArrayTypeInfo) + tmp_info->len_of_type_str + 1);\n    efree(tmp_info);\n\n    if (info->self.type_of_value == IS_ARRAY) {\n        if (!info->element.parse(info->get_value_type_str(), info->get_len_of_value_type_str())) {\n            zval_ptr_dtor(return_value);\n            RETURN_NULL();\n        }\n    }\n\n    if (init_values) {\n        zend_string *str_key;\n        zend_ulong num_key;\n        zval *zv;\n        zval zk;\n        HashTable *ht = Z_ARRVAL_P(init_values);\n\n        ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, str_key, zv) {\n            if (str_key) {\n                ZVAL_STR(&zk, str_key);\n            } else {\n                ZVAL_LONG(&zk, num_key);\n            }\n            if (!info->check(array, &zk, zv)) {\n                zval_ptr_dtor(return_value);\n                RETURN_NULL();\n            }\n            Z_TRY_ADDREF_P(zv);\n            if (str_key) {\n                zend_hash_add(array, str_key, zv);\n            } else {\n                zend_hash_index_add(array, num_key, zv);\n            }\n        }\n        ZEND_HASH_FOREACH_END();\n    }\n}\n\nPHP_FUNCTION(swoole_array_is_typed) {\n    zend_string *type_def = nullptr;\n    zval *array;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_ARRAY(array)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_STR(type_def)\n    ZEND_PARSE_PARAMETERS_END();\n\n    HashTable *ht = Z_ARRVAL_P(array);\n    if (!(HT_FLAGS(ht) & HASH_FLAG_TYPED_ARRAY)) {\n        RETURN_FALSE;\n    }\n    if (!type_def) {\n        RETURN_TRUE;\n    }\n\n    const auto tmp_info = static_cast<ArrayTypeInfo *>(emalloc(sizeof(ArrayTypeInfo) + ZSTR_LEN(type_def) + 1));\n    if (!tmp_info->parse(type_def)) {\n        efree(tmp_info);\n        RETURN_FALSE;\n    }\n\n    const auto info = get_type_info(ht);\n    RETVAL_BOOL(info->equals(tmp_info));\n    efree(tmp_info);\n}\n\nstatic PHP_FUNCTION(swoole_array_push) {\n    zval *arg_ptr = ZEND_CALL_ARG(execute_data, 1);\n    const int arg_count = ZEND_CALL_NUM_ARGS(execute_data);\n    zval *array = &arg_ptr[0];\n    ZVAL_DEREF(array);\n\n    if (Z_TYPE_P(array) == IS_ARRAY && is_typed_array(array)) {\n        auto source = Z_ARRVAL_P(array);\n        auto type_info = get_type_info(source);\n        for (int i = 1; i < arg_count; i++) {\n            if (!type_info->check(source, &EG(uninitialized_zval), &arg_ptr[i])) {\n                return;\n            }\n        }\n    }\n    ori_handler_array_push(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n\nstatic PHP_FUNCTION(swoole_array_unshift) {\n    zval *arg_ptr = ZEND_CALL_ARG(execute_data, 1);\n    const int arg_count = ZEND_CALL_NUM_ARGS(execute_data);\n    zval *array = &arg_ptr[0];\n    ZVAL_DEREF(array);\n\n    if (Z_TYPE_P(array) == IS_ARRAY && is_typed_array(array)) {\n        auto source = Z_ARRVAL_P(array);\n        auto type_info = get_type_info(source);\n        for (int i = 1; i < arg_count; i++) {\n            if (!type_info->check(source, &EG(uninitialized_zval), &arg_ptr[i])) {\n                return;\n            }\n        }\n        ori_handler_array_unshift(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n        HT_FLAGS(source) |= HASH_FLAG_TYPED_ARRAY;\n    } else {\n        ori_handler_array_unshift(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n    }\n}\n\nstatic PHP_FUNCTION(swoole_array_splice) {\n    const zval *arg_ptr = ZEND_CALL_ARG(execute_data, 1);\n    const int arg_count = ZEND_CALL_NUM_ARGS(execute_data);\n    const zval *array = &arg_ptr[0];\n    ZVAL_DEREF(array);\n    if (Z_TYPE_P(array) == IS_ARRAY && is_typed_array(array)) {\n        if (arg_count > 3) {\n            auto type_info = get_type_info(Z_ARRVAL_P(array));\n            auto values = &arg_ptr[3];\n            ZVAL_DEREF(values);\n            if (Z_TYPE_P(values) == IS_ARRAY) {\n                zval *zv;\n                HashTable *ht = Z_ARRVAL_P(values);\n                ZEND_HASH_FOREACH_VAL(ht, zv) {\n                    if (!type_info->check(Z_ARRVAL_P(array), &EG(uninitialized_zval), zv)) {\n                        return;\n                    }\n                }\n                ZEND_HASH_FOREACH_END();\n            } else {\n                if (!type_info->check(Z_ARRVAL_P(array), &EG(uninitialized_zval), values)) {\n                    return;\n                }\n            }\n        }\n        const auto source = Z_ARRVAL_P(array);\n        ori_handler_array_splice(execute_data, return_value);\n        HT_FLAGS(source) |= HASH_FLAG_TYPED_ARRAY;\n    } else {\n        ori_handler_array_splice(execute_data, return_value);\n    }\n}\n\nstatic void php_do_pcre_match(INTERNAL_FUNCTION_PARAMETERS, int global) /* {{{ */\n{\n    /* parameters */\n    zend_string *regex;         /* Regular expression */\n    zend_string *subject;       /* String to match against */\n    pcre_cache_entry *pce;      /* Compiled regular expression */\n    zend_long flags = 0;        /* Match control flags */\n    zend_long start_offset = 0; /* Where the new search starts */\n\n    ZEND_PARSE_PARAMETERS_START(2, 4)\n    Z_PARAM_STR(subject)\n    Z_PARAM_STR(regex)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(flags)\n    Z_PARAM_LONG(start_offset)\n    ZEND_PARSE_PARAMETERS_END();\n\n    /* Compile regex or get it from cache. */\n    if ((pce = pcre_get_compiled_regex_cache(regex)) == nullptr) {\n        RETURN_FALSE;\n    }\n\n    zval count = {};\n    php_pcre_pce_incref(pce);\n#if PHP_VERSION_ID >= 80400\n    php_pcre_match_impl(pce, subject, &count, return_value, global == 1, flags, start_offset);\n#else\n    php_pcre_match_impl(pce, subject, &count, return_value, global, ZEND_NUM_ARGS() >= 3, flags, start_offset);\n#endif\n    php_pcre_pce_decref(pce);\n}\n/* }}} */\n\nPHP_FUNCTION(swoole_str_match) {\n    php_do_pcre_match(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);\n}\n\nPHP_FUNCTION(swoole_str_match_all) {\n    php_do_pcre_match(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);\n}\n\nstatic void php_do_json_decode(INTERNAL_FUNCTION_PARAMETERS, bool assoc) /* {{{ */\n{\n    char *str;\n    size_t str_len;\n    zend_long depth = PHP_JSON_PARSER_DEFAULT_DEPTH;\n    zend_long options = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 3)\n    Z_PARAM_STRING(str, str_len)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(depth)\n    Z_PARAM_LONG(options)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if (assoc) {\n        options |= PHP_JSON_OBJECT_AS_ARRAY;\n    } else {\n        options &= ~PHP_JSON_OBJECT_AS_ARRAY;\n    }\n\n    zend::json_decode(return_value, str, str_len, options, depth);\n}\n/* }}} */\n\nPHP_FUNCTION(swoole_str_json_decode) {\n    php_do_json_decode(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);\n}\n\nPHP_FUNCTION(swoole_str_json_decode_to_object) {\n    php_do_json_decode(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);\n}\n#endif\n"
  },
  {
    "path": "ext-src/swoole_table.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_cxx.h\"\n\n#include \"swoole_table.h\"\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_table_arginfo.h\"\nEND_EXTERN_C()\n\nusing namespace swoole;\n\nstatic inline void table_row2array(const Table *table, TableRow *row, zval *return_value) {\n    array_init(return_value);\n\n    for (const auto col : *table->column_list) {\n        if (col->type == TableColumn::TYPE_STRING) {\n            TableStringLength len = 0;\n            char *str = nullptr;\n            row->get_value(col, &str, &len);\n            add_assoc_stringl_ex(return_value, col->name.c_str(), col->name.length(), str, len);\n        } else if (col->type == TableColumn::TYPE_FLOAT) {\n            double dval = 0;\n            row->get_value(col, &dval);\n            add_assoc_double_ex(return_value, col->name.c_str(), col->name.length(), dval);\n        } else if (col->type == TableColumn::TYPE_INT) {\n            long lval = 0;\n            row->get_value(col, &lval);\n            add_assoc_long_ex(return_value, col->name.c_str(), col->name.length(), lval);\n        } else {\n            abort();\n        }\n    }\n}\n\nstatic inline void table_get_field_value(Table *table, TableRow *row, zval *return_value, const zend_string *field) {\n    TableColumn *col = table->get_column(std::string(ZSTR_VAL(field), ZSTR_LEN(field)));\n    if (!col) {\n        ZVAL_FALSE(return_value);\n        return;\n    }\n    if (col->type == TableColumn::TYPE_STRING) {\n        TableStringLength len = 0;\n        char *str = nullptr;\n        row->get_value(col, &str, &len);\n        ZVAL_STRINGL(return_value, str, len);\n    } else if (col->type == TableColumn::TYPE_FLOAT) {\n        double dval = 0;\n        row->get_value(col, &dval);\n        ZVAL_DOUBLE(return_value, dval);\n    } else if (col->type == TableColumn::TYPE_INT) {\n        long lval = 0;\n        row->get_value(col, &lval);\n        ZVAL_LONG(return_value, lval);\n    } else {\n        abort();\n    }\n}\n\nstatic zend_class_entry *swoole_table_ce;\nstatic zend_object_handlers swoole_table_handlers;\n\nstruct TableObject {\n    Table *ptr;\n    zend_object std;\n};\n\nstatic TableObject *table_fetch_object(zend_object *obj) {\n    return reinterpret_cast<TableObject *>(reinterpret_cast<char *>(obj) - swoole_table_handlers.offset);\n}\n\nstatic Table *table_get_ptr(const zval *zobject) {\n    return table_fetch_object(Z_OBJ_P(zobject))->ptr;\n}\n\nstatic Table *table_get_and_check_ptr(const zval *zobject) {\n    Table *table = table_get_ptr(zobject);\n    if (UNEXPECTED(!table)) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION, \"must call constructor first\");\n    }\n    return table;\n}\n\nstatic Table *table_get_and_check_ptr2(const zval *zobject) {\n    Table *table = table_get_and_check_ptr(zobject);\n    if (!table->ready()) {\n        php_swoole_fatal_error(E_ERROR, \"table is not created or has been destroyed\");\n    }\n    return table;\n}\n\nstatic void table_set_ptr(const zval *zobject, Table *ptr) {\n    table_fetch_object(Z_OBJ_P(zobject))->ptr = ptr;\n}\n\nstatic void table_free_object(zend_object *object) {\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *table_create_object(zend_class_entry *ce) {\n    auto *table = static_cast<TableObject *>(zend_object_alloc(sizeof(TableObject), ce));\n    zend_object_std_init(&table->std, ce);\n    object_properties_init(&table->std, ce);\n    table->std.handlers = &swoole_table_handlers;\n    return &table->std;\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_table, __construct);\nstatic PHP_METHOD(swoole_table, column);\nstatic PHP_METHOD(swoole_table, create);\nstatic PHP_METHOD(swoole_table, set);\nstatic PHP_METHOD(swoole_table, get);\nstatic PHP_METHOD(swoole_table, del);\nstatic PHP_METHOD(swoole_table, exists);\nstatic PHP_METHOD(swoole_table, incr);\nstatic PHP_METHOD(swoole_table, decr);\nstatic PHP_METHOD(swoole_table, count);\nstatic PHP_METHOD(swoole_table, destroy);\nstatic PHP_METHOD(swoole_table, getSize);\nstatic PHP_METHOD(swoole_table, getMemorySize);\nstatic PHP_METHOD(swoole_table, stats);\n\nstatic PHP_METHOD(swoole_table, rewind);\nstatic PHP_METHOD(swoole_table, next);\nstatic PHP_METHOD(swoole_table, current);\nstatic PHP_METHOD(swoole_table, key);\nstatic PHP_METHOD(swoole_table, valid);\n\nSW_EXTERN_C_END\n\n// clang-format off\nstatic const zend_function_entry swoole_table_methods[] =\n{\n    PHP_ME(swoole_table, __construct,       arginfo_class_Swoole_Table___construct,   ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_table, column,            arginfo_class_Swoole_Table_column,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_table, create,            arginfo_class_Swoole_Table_create,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_table, destroy,           arginfo_class_Swoole_Table_destroy,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_table, set,               arginfo_class_Swoole_Table_set,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_table, get,               arginfo_class_Swoole_Table_get,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_table, count,             arginfo_class_Swoole_Table_count,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_table, del,               arginfo_class_Swoole_Table_del,           ZEND_ACC_PUBLIC)\n    PHP_MALIAS(swoole_table, delete, del,   arginfo_class_Swoole_Table_del,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_table, exists,            arginfo_class_Swoole_Table_exists,        ZEND_ACC_PUBLIC)\n    PHP_MALIAS(swoole_table, exist, exists, arginfo_class_Swoole_Table_exists,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_table, incr,              arginfo_class_Swoole_Table_incr,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_table, decr,              arginfo_class_Swoole_Table_decr,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_table, getSize,           arginfo_class_Swoole_Table_getSize,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_table, getMemorySize,     arginfo_class_Swoole_Table_getMemorySize, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_table, stats,             arginfo_class_Swoole_Table_stats,         ZEND_ACC_PUBLIC)\n    // implement Iterator\n    PHP_ME(swoole_table, rewind,            arginfo_class_Swoole_Table_rewind,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_table, valid,             arginfo_class_Swoole_Table_valid,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_table, next,              arginfo_class_Swoole_Table_next,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_table, current,           arginfo_class_Swoole_Table_current,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_table, key,               arginfo_class_Swoole_Table_key,           ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_table_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_table, \"Swoole\\\\Table\", nullptr, swoole_table_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_table);\n    SW_SET_CLASS_CLONEABLE(swoole_table, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_table, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(swoole_table, table_create_object, table_free_object, TableObject, std);\n    zend_class_implements(swoole_table_ce, 1, zend_ce_iterator);\n    zend_class_implements(swoole_table_ce, 1, zend_ce_countable);\n\n    zend_declare_property_null(swoole_table_ce, ZEND_STRL(\"size\"), ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_table_ce, ZEND_STRL(\"memorySize\"), ZEND_ACC_PUBLIC);\n\n    zend_declare_class_constant_long(swoole_table_ce, ZEND_STRL(\"TYPE_INT\"), TableColumn::TYPE_INT);\n    zend_declare_class_constant_long(swoole_table_ce, ZEND_STRL(\"TYPE_STRING\"), TableColumn::TYPE_STRING);\n    zend_declare_class_constant_long(swoole_table_ce, ZEND_STRL(\"TYPE_FLOAT\"), TableColumn::TYPE_FLOAT);\n}\n\nPHP_METHOD(swoole_table, __construct) {\n    Table *table = table_get_ptr(ZEND_THIS);\n    if (table) {\n        zend_throw_error(nullptr, \"Constructor of %s can only be called once\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        RETURN_FALSE;\n    }\n\n    zend_long table_size;\n    double conflict_proportion = SW_TABLE_CONFLICT_PROPORTION;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 2)\n    Z_PARAM_LONG(table_size)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(conflict_proportion)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    table = Table::make(table_size, static_cast<float>(conflict_proportion));\n    if (table == nullptr) {\n        zend_throw_exception(swoole_exception_ce, \"global memory allocation failure\", SW_ERROR_MALLOC_FAIL);\n        RETURN_FALSE;\n    }\n    table->set_hash_func([](const char *key, size_t len) -> uint64_t {\n        return zend_string_hash_val(zend::fetch_zend_string_by_val((void *) key));\n    });\n    table_set_ptr(ZEND_THIS, table);\n}\n\nPHP_METHOD(swoole_table, column) {\n    Table *table = table_get_and_check_ptr(ZEND_THIS);\n    char *name;\n    size_t len;\n    long type;\n    long size = 0;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"sl|l\", &name, &len, &type, &size) == FAILURE) {\n        RETURN_FALSE;\n    }\n    if (type == TableColumn::TYPE_STRING) {\n        if (size < 1) {\n            php_swoole_fatal_error(E_WARNING, \"the length of string type values has to be more than zero\");\n            RETURN_FALSE;\n        }\n        size = SW_MEM_ALIGNED_SIZE(size);\n    }\n    if (table->ready()) {\n        php_swoole_fatal_error(E_WARNING, \"unable to add column after table has been created\");\n        RETURN_FALSE;\n    }\n    RETURN_BOOL(table->add_column(std::string(name, len), static_cast<TableColumn::Type>(type), size));\n}\n\nstatic PHP_METHOD(swoole_table, create) {\n    Table *table = table_get_and_check_ptr(ZEND_THIS);\n\n    if (!table->create()) {\n        php_swoole_fatal_error(E_ERROR, \"unable to allocate memory\");\n        RETURN_FALSE;\n    }\n    zend_update_property_long(\n        swoole_table_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"size\"), (zend_long) table->get_size());\n    zend_update_property_long(\n        swoole_table_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL(\"memorySize\"), (zend_long) table->get_memory_size());\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_table, destroy) {\n    Table *table = table_get_and_check_ptr2(ZEND_THIS);\n\n    table->destroy();\n    table_set_ptr(ZEND_THIS, nullptr);\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_table, set) {\n    Table *table = table_get_and_check_ptr2(ZEND_THIS);\n    zval *array;\n    char *key;\n    size_t keylen;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 2, 2)\n    Z_PARAM_STRING(key, keylen)\n    Z_PARAM_ARRAY(array)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (!table->ready()) {\n        php_swoole_fatal_error(E_ERROR, \"the table object does not exist\");\n        RETURN_FALSE;\n    }\n\n    if (keylen >= SW_TABLE_KEY_SIZE) {\n        php_swoole_fatal_error(E_WARNING, \"key[%s] is too long\", key);\n    }\n\n    int out_flags;\n    TableRow *_rowlock = nullptr;\n    TableRow *row = table->set(key, keylen, &_rowlock, &out_flags);\n    if (!row) {\n        _rowlock->unlock();\n        php_swoole_error(E_WARNING, \"failed to set('%*s'), unable to allocate memory\", (int) keylen, key);\n        RETURN_FALSE;\n    }\n\n    HashTable *ht = Z_ARRVAL_P(array);\n\n    if (out_flags & SW_TABLE_FLAG_NEW_ROW) {\n        for (const auto col : *table->column_list) {\n            zval *zv = zend_hash_str_find(ht, col->name.c_str(), col->name.length());\n            if (zv == nullptr || ZVAL_IS_NULL(zv)) {\n                col->clear(row);\n            } else {\n                if (col->type == TableColumn::TYPE_STRING) {\n                    zend_string *str = zval_get_string(zv);\n                    row->set_value(col, ZSTR_VAL(str), ZSTR_LEN(str));\n                    zend_string_release(str);\n                } else if (col->type == TableColumn::TYPE_FLOAT) {\n                    double _value = zval_get_double(zv);\n                    row->set_value(col, &_value, 0);\n                } else {\n                    long _value = zval_get_long(zv);\n                    row->set_value(col, &_value, 0);\n                }\n            }\n        }\n    } else {\n        const char *k;\n        uint32_t klen;\n        int ktype;\n        zval *zv;\n        SW_HASHTABLE_FOREACH_START2(ht, k, klen, ktype, zv) {\n            if (k == nullptr) {\n                continue;\n            }\n            TableColumn *col = table->get_column(std::string(k, klen));\n            if (col == nullptr) {\n                continue;\n            } else if (col->type == TableColumn::TYPE_STRING) {\n                zend_string *str = zval_get_string(zv);\n                row->set_value(col, ZSTR_VAL(str), ZSTR_LEN(str));\n                zend_string_release(str);\n            } else if (col->type == TableColumn::TYPE_FLOAT) {\n                double _value = zval_get_double(zv);\n                row->set_value(col, &_value, 0);\n            } else {\n                long _value = zval_get_long(zv);\n                row->set_value(col, &_value, 0);\n            }\n        }\n        (void) ktype;\n        SW_HASHTABLE_FOREACH_END();\n    }\n    _rowlock->unlock();\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_table, incr) {\n    Table *table = table_get_and_check_ptr2(ZEND_THIS);\n    char *key;\n    size_t key_len;\n    char *col;\n    size_t col_len;\n    zval *incrby = nullptr;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"ss|z\", &key, &key_len, &col, &col_len, &incrby) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    int out_flags;\n    TableRow *_rowlock = nullptr;\n    TableRow *row = table->set(key, key_len, &_rowlock, &out_flags);\n    if (!row) {\n        _rowlock->unlock();\n        php_swoole_fatal_error(E_WARNING, \"unable to allocate memory\");\n        RETURN_FALSE;\n    }\n\n    TableColumn *column = table->get_column(std::string(col, col_len));\n    if (column == nullptr) {\n        _rowlock->unlock();\n        php_swoole_fatal_error(E_WARNING, \"column[%s] does not exist\", col);\n        RETURN_FALSE;\n    }\n\n    if (out_flags & SW_TABLE_FLAG_NEW_ROW) {\n        table->clear_row(row);\n    }\n\n    if (column->type == TableColumn::TYPE_STRING) {\n        _rowlock->unlock();\n        php_swoole_fatal_error(E_WARNING, \"can't execute 'incr' on a string type column\");\n        RETURN_FALSE;\n    } else if (column->type == TableColumn::TYPE_FLOAT) {\n        double set_value = 0;\n        memcpy(&set_value, row->data + column->index, sizeof(set_value));\n        if (incrby) {\n            set_value += zval_get_double(incrby);\n        } else {\n            set_value += 1;\n        }\n        row->set_value(column, &set_value, 0);\n        RETVAL_DOUBLE(set_value);\n    } else {\n        long set_value = 0;\n        memcpy(&set_value, row->data + column->index, sizeof(set_value));\n        if (incrby) {\n            set_value += zval_get_long(incrby);\n        } else {\n            set_value += 1;\n        }\n        row->set_value(column, &set_value, 0);\n        RETVAL_LONG(set_value);\n    }\n    _rowlock->unlock();\n}\n\nstatic PHP_METHOD(swoole_table, decr) {\n    Table *table = table_get_and_check_ptr2(ZEND_THIS);\n    char *key;\n    size_t key_len;\n    char *col;\n    size_t col_len;\n    zval *decrby = nullptr;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"ss|z\", &key, &key_len, &col, &col_len, &decrby) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    int out_flags;\n    TableRow *_rowlock = nullptr;\n    TableRow *row = table->set(key, key_len, &_rowlock, &out_flags);\n    if (!row) {\n        _rowlock->unlock();\n        php_swoole_fatal_error(E_WARNING, \"unable to allocate memory\");\n        RETURN_FALSE;\n    }\n\n    TableColumn *column = table->get_column(std::string(col, col_len));\n    if (column == nullptr) {\n        _rowlock->unlock();\n        php_swoole_fatal_error(E_WARNING, \"column[%s] does not exist\", col);\n        RETURN_FALSE;\n    }\n\n    if (out_flags & SW_TABLE_FLAG_NEW_ROW) {\n        table->clear_row(row);\n    }\n\n    if (column->type == TableColumn::TYPE_STRING) {\n        _rowlock->unlock();\n        php_swoole_fatal_error(E_WARNING, \"can't execute 'decr' on a string type column\");\n        RETURN_FALSE;\n    } else if (column->type == TableColumn::TYPE_FLOAT) {\n        double set_value = 0;\n        memcpy(&set_value, row->data + column->index, sizeof(set_value));\n        if (decrby) {\n            set_value -= zval_get_double(decrby);\n        } else {\n            set_value -= 1;\n        }\n        row->set_value(column, &set_value, 0);\n        RETVAL_DOUBLE(set_value);\n    } else {\n        long set_value = 0;\n        memcpy(&set_value, row->data + column->index, sizeof(set_value));\n        if (decrby) {\n            set_value -= zval_get_long(decrby);\n        } else {\n            set_value -= 1;\n        }\n        row->set_value(column, &set_value, 0);\n        RETVAL_LONG(set_value);\n    }\n    _rowlock->unlock();\n}\n\nstatic PHP_METHOD(swoole_table, get) {\n    Table *table = table_get_and_check_ptr2(ZEND_THIS);\n    char *key;\n    size_t keylen;\n    zend_string *field = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 2)\n    Z_PARAM_STRING(key, keylen)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_STR_OR_NULL(field)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    TableRow *_rowlock = nullptr;\n    TableRow *row = table->get(key, keylen, &_rowlock);\n    if (!row) {\n        RETVAL_FALSE;\n    } else if (field) {\n        table_get_field_value(table, row, return_value, field);\n    } else {\n        table_row2array(table, row, return_value);\n    }\n    _rowlock->unlock();\n}\n\nstatic PHP_METHOD(swoole_table, exists) {\n    Table *table = table_get_and_check_ptr2(ZEND_THIS);\n    char *key;\n    size_t keylen;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"s\", &key, &keylen) == FAILURE) {\n        RETURN_FALSE;\n    }\n    RETURN_BOOL(table->exists(key, keylen));\n}\n\nstatic PHP_METHOD(swoole_table, del) {\n    Table *table = table_get_and_check_ptr2(ZEND_THIS);\n    char *key;\n    size_t keylen;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1)\n    Z_PARAM_STRING(key, keylen)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(table->del(key, keylen));\n}\n\nstatic PHP_METHOD(swoole_table, count) {\n#define COUNT_NORMAL 0\n#define COUNT_RECURSIVE 1\n    Table *table = table_get_ptr(ZEND_THIS);\n    if (!table) {\n        RETURN_LONG(0);\n    }\n\n    zend_long mode = COUNT_NORMAL;\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"|l\", &mode) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    if (mode == COUNT_NORMAL) {\n        RETURN_LONG(table->count());\n    } else {\n        RETURN_LONG(table->count() * table->column_list->size());\n    }\n}\n\nstatic PHP_METHOD(swoole_table, getMemorySize) {\n    Table *table = table_get_ptr(ZEND_THIS);\n    if (!table) {\n        RETURN_LONG(0);\n    } else {\n        RETURN_LONG(table->get_memory_size());\n    }\n}\n\nstatic PHP_METHOD(swoole_table, getSize) {\n    Table *table = table_get_ptr(ZEND_THIS);\n    if (!table) {\n        RETURN_LONG(0);\n    } else {\n        RETURN_LONG(table->get_size());\n    }\n}\n\nstatic PHP_METHOD(swoole_table, stats) {\n    Table *table = table_get_ptr(ZEND_THIS);\n    if (!table) {\n        RETURN_FALSE;\n    }\n    array_init(return_value);\n    add_assoc_long(return_value, \"num\", table->count());\n    add_assoc_long(return_value, \"conflict_count\", table->conflict_count);\n    add_assoc_long(return_value, \"conflict_max_level\", table->conflict_max_level);\n    add_assoc_long(return_value, \"insert_count\", (zend_long) table->insert_count);\n    add_assoc_long(return_value, \"update_count\", (zend_long) table->update_count);\n    add_assoc_long(return_value, \"delete_count\", (zend_long) table->delete_count);\n    add_assoc_long(return_value, \"available_slice_num\", table->get_available_slice_num());\n    add_assoc_long(return_value, \"total_slice_num\", table->get_total_slice_num());\n}\n\nstatic PHP_METHOD(swoole_table, rewind) {\n    Table *table = table_get_and_check_ptr2(ZEND_THIS);\n    table->rewind();\n    table->forward();\n}\n\nstatic PHP_METHOD(swoole_table, valid) {\n    Table *table = table_get_and_check_ptr2(ZEND_THIS);\n    auto key = table->current();\n    RETURN_BOOL(key->key_len != 0);\n}\n\nstatic PHP_METHOD(swoole_table, current) {\n    Table *table = table_get_and_check_ptr2(ZEND_THIS);\n    auto row = table->current();\n    if (row->key_len == 0) {\n        RETURN_NULL();\n    }\n    table_row2array(table, row, return_value);\n}\n\nstatic PHP_METHOD(swoole_table, key) {\n    Table *table = table_get_and_check_ptr2(ZEND_THIS);\n    auto row = table->current();\n    if (row->key_len == 0) {\n        RETURN_NULL();\n    }\n    RETVAL_STRINGL(row->key, row->key_len);\n}\n\nstatic PHP_METHOD(swoole_table, next) {\n    Table *table = table_get_and_check_ptr2(ZEND_THIS);\n    table->forward();\n}\n"
  },
  {
    "path": "ext-src/swoole_thread.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_cxx.h\"\n#include \"php_swoole_thread.h\"\n\n#ifdef SW_THREAD\n\n#include <sys/ipc.h>\n#include <sys/resource.h>\n\n#include <atomic>\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_thread_arginfo.h\"\nEND_EXTERN_C()\n\nzend_class_entry *swoole_thread_ce;\nstatic zend_object_handlers swoole_thread_handlers;\n\nzend_class_entry *swoole_thread_error_ce;\nstatic zend_object_handlers swoole_thread_error_handlers;\n\nstatic struct {\n    char *path_translated;\n    zend_string *argv_serialized;\n    int argc;\n} request_info;\n\nTSRMLS_CACHE_EXTERN();\n\nusing swoole::Thread;\n\nstruct PhpThread {\n    std::shared_ptr<Thread> thread;\n\n    PhpThread() : thread(std::make_shared<Thread>()) {}\n\n    bool join() const {\n        if (!thread->joinable()) {\n            return false;\n        }\n        thread->join();\n        return true;\n    }\n};\n\nstruct ThreadObject {\n    PhpThread *pt;\n    zend_object std;\n};\n\nstatic void thread_register_stdio_file_handles(bool no_close);\n\nstatic thread_local zval thread_argv = {};\nstatic thread_local JMP_BUF *thread_bailout = nullptr;\nstatic std::atomic<size_t> thread_num(1);\n\nstatic sw_inline ThreadObject *thread_fetch_object(zend_object *obj) {\n    return reinterpret_cast<ThreadObject *>(reinterpret_cast<char *>(obj) - swoole_thread_handlers.offset);\n}\n\nstatic sw_inline ThreadObject *thread_fetch_object(const zval *zobj) {\n    return thread_fetch_object(Z_OBJ_P(zobj));\n}\n\nstatic sw_inline PhpThread *thread_get_php_thread(zend_object *obj) {\n    return thread_fetch_object(obj)->pt;\n}\n\nstatic sw_inline PhpThread *thread_get_php_thread(const zval *zobj) {\n    return thread_fetch_object(zobj)->pt;\n}\n\nstatic void thread_free_object(zend_object *object) {\n    auto pt = thread_get_php_thread(object);\n    pt->join();\n    delete pt;\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *thread_create_object(zend_class_entry *ce) {\n    auto to = static_cast<ThreadObject *>(zend_object_alloc(sizeof(ThreadObject), ce));\n    zend_object_std_init(&to->std, ce);\n    object_properties_init(&to->std, ce);\n    to->pt = new PhpThread();\n    to->std.handlers = &swoole_thread_handlers;\n    return &to->std;\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_thread, __construct);\nstatic PHP_METHOD(swoole_thread, isAlive);\nstatic PHP_METHOD(swoole_thread, join);\nstatic PHP_METHOD(swoole_thread, joinable);\nstatic PHP_METHOD(swoole_thread, getExitStatus);\nstatic PHP_METHOD(swoole_thread, detach);\nstatic PHP_METHOD(swoole_thread, getArguments);\nstatic PHP_METHOD(swoole_thread, getId);\nstatic PHP_METHOD(swoole_thread, getInfo);\nstatic PHP_METHOD(swoole_thread, activeCount);\nstatic PHP_METHOD(swoole_thread, yield);\nstatic PHP_METHOD(swoole_thread, setName);\n#ifdef HAVE_CPU_AFFINITY\nstatic PHP_METHOD(swoole_thread, setAffinity);\nstatic PHP_METHOD(swoole_thread, getAffinity);\n#endif\nstatic PHP_METHOD(swoole_thread, setPriority);\nstatic PHP_METHOD(swoole_thread, getPriority);\nstatic PHP_METHOD(swoole_thread, getNativeId);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic const zend_function_entry swoole_thread_methods[] = {\n    PHP_ME(swoole_thread, __construct,   arginfo_class_Swoole_Thread___construct,   ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread, isAlive,       arginfo_class_Swoole_Thread_isAlive,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread, join,          arginfo_class_Swoole_Thread_join,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread, joinable,      arginfo_class_Swoole_Thread_joinable,      ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread, getExitStatus, arginfo_class_Swoole_Thread_getExitStatus, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread, detach,        arginfo_class_Swoole_Thread_detach,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread, getArguments,  arginfo_class_Swoole_Thread_getArguments,  ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_thread, getId,         arginfo_class_Swoole_Thread_getId,         ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_thread, getInfo,       arginfo_class_Swoole_Thread_getInfo,       ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_thread, activeCount,   arginfo_class_Swoole_Thread_activeCount,   ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_thread, yield,         arginfo_class_Swoole_Thread_yield,         ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_thread, setName,       arginfo_class_Swoole_Thread_setName,       ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n#ifdef HAVE_CPU_AFFINITY\n    PHP_ME(swoole_thread, setAffinity,   arginfo_class_Swoole_Thread_setAffinity,   ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_thread, getAffinity,   arginfo_class_Swoole_Thread_getAffinity,   ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n#endif\n    PHP_ME(swoole_thread, setPriority,   arginfo_class_Swoole_Thread_setPriority,   ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_thread, getPriority,   arginfo_class_Swoole_Thread_getPriority,   ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_thread, getNativeId,   arginfo_class_Swoole_Thread_getNativeId,   ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_thread_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_thread, \"Swoole\\\\Thread\", nullptr, swoole_thread_methods);\n    swoole_thread_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE;\n    SW_SET_CLASS_CLONEABLE(swoole_thread, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread, thread_create_object, thread_free_object, ThreadObject, std);\n\n    zend_declare_property_long(swoole_thread_ce, ZEND_STRL(\"id\"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY);\n    zend_declare_class_constant_long(\n        swoole_thread_ce, ZEND_STRL(\"HARDWARE_CONCURRENCY\"), std::thread::hardware_concurrency());\n    zend_declare_class_constant_string(swoole_thread_ce, ZEND_STRL(\"API_NAME\"), tsrm_api_name());\n\n    zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL(\"SCHED_OTHER\"), SCHED_OTHER);\n    zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL(\"SCHED_FIFO\"), SCHED_FIFO);\n    zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL(\"SCHED_RR\"), SCHED_RR);\n#ifdef SCHED_BATCH\n    zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL(\"SCHED_BATCH\"), SCHED_BATCH);\n#endif\n#ifdef SCHED_ISO\n    zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL(\"SCHED_ISO\"), SCHED_ISO);\n#endif\n#ifdef SCHED_IDLE\n    zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL(\"SCHED_IDLE\"), SCHED_IDLE);\n#endif\n#ifdef SCHED_DEADLINE\n    zend_declare_class_constant_long(swoole_thread_ce, ZEND_STRL(\"SCHED_DEADLINE\"), SCHED_DEADLINE);\n#endif\n\n    SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_thread_error, \"Swoole\\\\Thread\\\\Error\");\n    zend_declare_property_long(swoole_thread_error_ce, ZEND_STRL(\"code\"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY);\n}\n\nstatic PHP_METHOD(swoole_thread, __construct) {\n    char *script_file;\n    size_t l_script_file;\n    zval *args;\n    int argc = 0;\n    ZendArray *argv = nullptr;\n\n    ZEND_PARSE_PARAMETERS_START(1, -1)\n    Z_PARAM_STRING(script_file, l_script_file)\n    Z_PARAM_VARIADIC('+', args, argc)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if (l_script_file < 1) {\n        zend_throw_exception(swoole_exception_ce, \"exec file name is empty\", SW_ERROR_INVALID_PARAMS);\n        return;\n    }\n\n    auto pt = thread_get_php_thread(ZEND_THIS);\n    zend_string *file = zend_string_init(script_file, l_script_file, true);\n\n    if (argc > 0) {\n        argv = new ZendArray();\n        for (int i = 0; i < argc; i++) {\n            argv->append(&args[i]);\n        }\n    }\n\n    try {\n        pt->thread->start([file, argv, pt]() { php_swoole_thread_start(pt->thread, file, argv); });\n    } catch (const std::exception &e) {\n        zend_throw_exception(swoole_exception_ce, e.what(), SW_ERROR_SYSTEM_CALL_FAIL);\n        return;\n    }\n\n    zend::object_set(ZEND_THIS, ZEND_STRL(\"id\"), (zend_long) pt->thread->get_id());\n}\n\nstatic PHP_METHOD(swoole_thread, isAlive) {\n    auto pt = thread_get_php_thread(ZEND_THIS);\n    RETURN_BOOL(pt->thread->is_alive());\n}\n\nstatic PHP_METHOD(swoole_thread, join) {\n    auto pt = thread_get_php_thread(ZEND_THIS);\n    RETURN_BOOL(pt->join());\n}\n\nstatic PHP_METHOD(swoole_thread, joinable) {\n    auto pt = thread_get_php_thread(ZEND_THIS);\n    RETURN_BOOL(pt->thread->joinable());\n}\n\nstatic PHP_METHOD(swoole_thread, detach) {\n    auto pt = thread_get_php_thread(ZEND_THIS);\n    if (!pt->thread->joinable()) {\n        RETURN_FALSE;\n    }\n    pt->thread->detach();\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_thread, getArguments) {\n    if (Z_TYPE(thread_argv) == IS_ARRAY) {\n        RETURN_ZVAL(&thread_argv, 1, 0);\n    }\n}\n\nstatic PHP_METHOD(swoole_thread, getId) {\n    RETURN_LONG((zend_long) pthread_self());\n}\n\nstatic PHP_METHOD(swoole_thread, getExitStatus) {\n    auto pt = thread_get_php_thread(ZEND_THIS);\n    RETURN_LONG(pt->thread->get_exit_status());\n}\n\nstatic PHP_METHOD(swoole_thread, setName) {\n    char *name;\n    size_t l_name;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STRING(name, l_name)\n    ZEND_PARSE_PARAMETERS_END();\n\n    RETURN_BOOL(swoole_thread_set_name(name));\n}\n\n#ifdef HAVE_CPU_AFFINITY\nstatic PHP_METHOD(swoole_thread, setAffinity) {\n    zval *array;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ARRAY(array)\n    ZEND_PARSE_PARAMETERS_END();\n\n    cpu_set_t cpu_set;\n    if (!php_swoole_array_to_cpu_set(array, &cpu_set)) {\n        RETURN_FALSE;\n    }\n\n    if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set) < 0) {\n        php_swoole_error(E_WARNING, \"pthread_setaffinity_np() failed\");\n        RETURN_FALSE;\n    }\n    RETURN_TRUE;\n}\n\nstatic PHP_METHOD(swoole_thread, getAffinity) {\n    cpu_set_t cpu_set;\n    if (pthread_getaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set) < 0) {\n        php_swoole_error(E_WARNING, \"pthread_getaffinity_np() failed\");\n        RETURN_FALSE;\n    }\n    php_swoole_cpu_set_to_array(return_value, &cpu_set);\n}\n#endif\n\nstatic PHP_METHOD(swoole_thread, setPriority) {\n    zend_long priority, policy = -1;\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_LONG(priority)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(policy)\n    ZEND_PARSE_PARAMETERS_END();\n\n    struct sched_param param;\n    if (policy == -1) {\n        pthread_setschedparam(pthread_self(), policy, &param);\n    }\n\n    param.sched_priority = priority;\n    int retval = pthread_setschedparam(pthread_self(), policy, &param);\n    if (retval == 0) {\n        RETURN_TRUE;\n    } else {\n        php_swoole_sys_error(E_WARNING, \"pthread_setschedparam() failed\");\n        RETURN_FALSE;\n    }\n}\n\nstatic PHP_METHOD(swoole_thread, getPriority) {\n    struct sched_param param;\n    int policy;\n    if (pthread_getschedparam(pthread_self(), &policy, &param) != 0) {\n        php_swoole_error(E_WARNING, \"pthread_getschedparam() failed\");\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"policy\"), policy);\n    add_assoc_long_ex(return_value, ZEND_STRL(\"priority\"), param.sched_priority);\n}\n\nstatic PHP_METHOD(swoole_thread, getNativeId) {\n    RETURN_LONG((zend_long) swoole_thread_get_native_id());\n}\n\nvoid php_swoole_thread_rinit() {\n    if (tsrm_is_main_thread()) {\n        if (SG(request_info).path_translated) {\n            request_info.path_translated = strdup(SG(request_info).path_translated);\n        }\n        // Return reference\n        zval *global_argv = zend_hash_find_ind(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGV));\n        if (global_argv) {\n            request_info.argv_serialized = php_swoole_serialize(global_argv);\n            request_info.argc = SG(request_info).argc;\n        }\n    }\n}\n\nvoid php_swoole_thread_rshutdown() {\n    zval_dtor(&thread_argv);\n    if (!tsrm_is_main_thread()) {\n        return;\n    }\n    if (sw_active_thread_count() > 1) {\n        swoole_warning(\"Fatal Error: %zu active threads are running, cannot exit safely.\", sw_active_thread_count());\n        exit(200);\n    }\n    if (request_info.path_translated) {\n        free((void *) request_info.path_translated);\n        request_info.path_translated = nullptr;\n    }\n    if (request_info.argv_serialized) {\n        zend_string_release(request_info.argv_serialized);\n        request_info.argv_serialized = nullptr;\n    }\n}\n\nstatic void thread_register_stdio_file_handles(bool no_close) {\n    php_stream *s_in, *s_out, *s_err;\n    php_stream_context *sc_in = nullptr, *sc_out = nullptr, *sc_err = nullptr;\n    zend_constant ic, oc, ec;\n\n    s_in = php_stream_open_wrapper_ex(\"php://stdin\", \"rb\", 0, NULL, sc_in);\n    s_out = php_stream_open_wrapper_ex(\"php://stdout\", \"wb\", 0, NULL, sc_out);\n    s_err = php_stream_open_wrapper_ex(\"php://stderr\", \"wb\", 0, NULL, sc_err);\n\n    if (s_in == nullptr || s_out == nullptr || s_err == nullptr) {\n        if (s_in) php_stream_close(s_in);\n        if (s_out) php_stream_close(s_out);\n        if (s_err) php_stream_close(s_err);\n        return;\n    }\n\n    if (no_close) {\n        s_in->flags |= PHP_STREAM_FLAG_NO_CLOSE;\n        s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;\n        s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE;\n    }\n\n    php_stream_to_zval(s_in, &ic.value);\n    php_stream_to_zval(s_out, &oc.value);\n    php_stream_to_zval(s_err, &ec.value);\n\n    ZEND_CONSTANT_SET_FLAGS(&ic, CONST_CS, 0);\n    ic.name = zend_string_init_interned(\"STDIN\", sizeof(\"STDIN\") - 1, false);\n    zend_register_constant(&ic);\n\n    ZEND_CONSTANT_SET_FLAGS(&oc, CONST_CS, 0);\n    oc.name = zend_string_init_interned(\"STDOUT\", sizeof(\"STDOUT\") - 1, false);\n    zend_register_constant(&oc);\n\n    ZEND_CONSTANT_SET_FLAGS(&ec, CONST_CS, 0);\n    ec.name = zend_string_init_interned(\"STDERR\", sizeof(\"STDERR\") - 1, false);\n    zend_register_constant(&ec);\n}\n\nvoid php_swoole_thread_start(std::shared_ptr<Thread> thread, zend_string *file, ZendArray *argv) {\n    thread_num.fetch_add(1);\n    thread->enter();\n    ts_resource(0);\n#if defined(COMPILE_DL_SWOOLE) && defined(ZTS)\n    ZEND_TSRMLS_CACHE_UPDATE();\n#endif\n    zend_file_handle file_handle{};\n    zval global_argc, global_argv;\n\n    PG(expose_php) = false;\n    PG(auto_globals_jit) = true;\n    PG(enable_dl) = false;\n\n    swoole_thread_init(false);\n\n    if (php_request_startup() != SUCCESS) {\n        EG(exit_status) = 1;\n        goto _startup_error;\n    }\n\n    PG(during_request_startup) = false;\n    SG(sapi_started) = false;\n    SG(headers_sent) = 1;\n    SG(request_info).no_headers = true;\n    SG(request_info).path_translated = request_info.path_translated;\n    SG(request_info).argc = request_info.argc;\n\n#if PHP_VERSION_ID >= 80500\n    refresh_memory_manager();\n#endif\n\n    zend_stream_init_filename(&file_handle, ZSTR_VAL(file));\n    file_handle.primary_script = true;\n\n    zend_first_try {\n        thread_bailout = EG(bailout);\n        if (request_info.argv_serialized) {\n            php_swoole_unserialize(request_info.argv_serialized, &global_argv);\n            ZVAL_LONG(&global_argc, request_info.argc);\n            zend_hash_update(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGV), &global_argv);\n            zend_hash_update(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGC), &global_argc);\n        }\n        if (argv) {\n            argv->to_array(&thread_argv);\n            argv->del_ref();\n        }\n        thread_register_stdio_file_handles(true);\n        php_execute_script(&file_handle);\n    }\n    zend_end_try();\n\n    zend_destroy_file_handle(&file_handle);\n\n    php_request_shutdown(nullptr);\n    file_handle.filename = nullptr;\n\n_startup_error:\n    zend_string_release(file);\n    thread->exit(EG(exit_status));\n    ts_free_thread();\n    swoole_thread_clean(false);\n    thread_num.fetch_sub(1);\n}\n\nsize_t sw_active_thread_count() {\n    return thread_num.load();\n}\n\nvoid php_swoole_thread_bailout() {\n    if (thread_bailout) {\n        EG(bailout) = thread_bailout;\n    }\n    zend_bailout();\n}\n\nint php_swoole_thread_stream_cast(zval *zstream) {\n    php_stream *stream;\n    int sockfd;\n    int cast_flags = PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL;\n    if ((php_stream_from_zval_no_verify(stream, zstream))) {\n        if (php_stream_cast(stream, cast_flags, (void **) &sockfd, 1) == SUCCESS && sockfd >= 0) {\n            return dup(sockfd);\n        }\n    }\n    return -1;\n}\n\nint php_swoole_thread_co_socket_cast(zval *zvalue, swSocketType *type) {\n    SocketImpl *socket = php_swoole_get_socket(zvalue);\n    if (!socket) {\n        return -1;\n    }\n    int sockfd = socket->get_fd();\n    if (sockfd < 0) {\n        return -1;\n    }\n    int newfd = dup(sockfd);\n    if (newfd < 0) {\n        return -1;\n    }\n    *type = socket->get_type();\n    return newfd;\n}\n\nvoid php_swoole_thread_stream_create(zval *return_value, zend_long sockfd) {\n    std::string path = \"php://fd/\" + std::to_string(sockfd);\n    // The file descriptor will be duplicated once here\n    php_stream *stream = php_stream_open_wrapper_ex(path.c_str(), \"\", 0, NULL, NULL);\n    if (stream) {\n        php_stream_to_zval(stream, return_value);\n    } else {\n        object_init_ex(return_value, swoole_thread_error_ce);\n        zend::object_set(return_value, ZEND_STRL(\"code\"), errno);\n    }\n}\n\nvoid php_swoole_thread_co_socket_create(zval *return_value, zend_long sockfd, swSocketType type) {\n    int newfd = dup(sockfd);\n    if (newfd < 0) {\n    _error:\n        object_init_ex(return_value, swoole_thread_error_ce);\n        zend::object_set(return_value, ZEND_STRL(\"code\"), errno);\n        return;\n    }\n    zend_object *sockobj = php_swoole_create_socket_from_fd(newfd, type);\n    if (sockobj) {\n        ZVAL_OBJ(return_value, sockobj);\n    } else {\n        goto _error;\n    }\n}\n\n#ifdef SWOOLE_SOCKETS_SUPPORT\nvoid php_swoole_thread_php_socket_create(zval *return_value, zend_long sockfd) {\n    int newfd = dup(sockfd);\n    if (newfd < 0) {\n    _error:\n        object_init_ex(return_value, swoole_thread_error_ce);\n        zend::object_set(return_value, ZEND_STRL(\"code\"), errno);\n        return;\n    }\n    object_init_ex(return_value, socket_ce);\n    auto retsock = Z_SOCKET_P(return_value);\n    if (!socket_import_file_descriptor(newfd, retsock)) {\n        goto _error;\n    }\n}\n#endif\n\nstatic PHP_METHOD(swoole_thread, getInfo) {\n    array_init(return_value);\n    add_assoc_bool(return_value, \"is_main_thread\", tsrm_is_main_thread());\n    add_assoc_bool(return_value, \"is_shutdown\", tsrm_is_shutdown());\n    add_assoc_long(return_value, \"thread_num\", thread_num.load());\n}\n\nstatic PHP_METHOD(swoole_thread, activeCount) {\n    RETURN_LONG(thread_num.load());\n}\n\nstatic PHP_METHOD(swoole_thread, yield) {\n    std::this_thread::yield();\n}\n\n#define CAST_OBJ_TO_RESOURCE(_name, _type)                                                                             \\\n    else if (instanceof_function(Z_OBJCE_P(zvalue), swoole_thread_##_name##_ce)) {                                     \\\n        value.resource = php_swoole_thread_##_name##_cast(zvalue);                                                     \\\n        value.resource->add_ref();                                                                                     \\\n        type = _type;                                                                                                  \\\n        break;                                                                                                         \\\n    }\n\nvoid ArrayItem::store(zval *zvalue) {\n    type = Z_TYPE_P(zvalue);\n    switch (type) {\n    case IS_LONG:\n        value.lval = zval_get_long(zvalue);\n        break;\n    case IS_DOUBLE:\n        value.dval = zval_get_double(zvalue);\n        break;\n    case IS_STRING: {\n        value.str = zend_string_init(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue), true);\n        break;\n    }\n    case IS_TRUE:\n    case IS_FALSE:\n    case IS_NULL:\n        break;\n    case IS_RESOURCE: {\n        value.socket.fd = php_swoole_thread_stream_cast(zvalue);\n        type = IS_STREAM_SOCKET;\n        if (value.socket.fd == -1) {\n            zend_throw_exception(swoole_exception_ce, \"failed to convert to socket fd\", errno);\n        }\n        break;\n    }\n    case IS_ARRAY: {\n        type = zend_array_is_list(Z_ARRVAL_P(zvalue)) ? IS_ARRAYLIST : IS_MAP;\n        value.resource = ZendArray::from(Z_ARRVAL_P(zvalue));\n        break;\n    }\n    case IS_OBJECT: {\n        if (sw_zval_is_co_socket(zvalue)) {\n            value.socket.fd = php_swoole_thread_co_socket_cast(zvalue, &value.socket.type);\n            type = IS_CO_SOCKET;\n            if (value.socket.fd == -1) {\n                zend_throw_exception(swoole_exception_ce, \"failed to convert to socket fd\", errno);\n            }\n            break;\n        }\n#ifdef SWOOLE_SOCKETS_SUPPORT\n        else if (sw_zval_is_php_socket(zvalue)) {\n            php_socket *php_sock = SW_Z_SOCKET_P(zvalue);\n            if (php_sock->bsd_socket == -1) {\n                zend_throw_exception(swoole_exception_ce, \"invalid socket fd\", EBADF);\n                break;\n            }\n            value.socket.fd = dup(php_sock->bsd_socket);\n            if (value.socket.fd == -1) {\n                zend_throw_exception(swoole_exception_ce, \"failed to dup socket fd\", errno);\n            }\n            type = IS_PHP_SOCKET;\n            break;\n        }\n#endif\n        CAST_OBJ_TO_RESOURCE(arraylist, IS_ARRAYLIST)\n        CAST_OBJ_TO_RESOURCE(map, IS_MAP)\n        CAST_OBJ_TO_RESOURCE(queue, IS_QUEUE)\n        CAST_OBJ_TO_RESOURCE(lock, IS_LOCK)\n        CAST_OBJ_TO_RESOURCE(atomic, IS_ATOMIC)\n        CAST_OBJ_TO_RESOURCE(atomic_long, IS_ATOMIC_LONG)\n        CAST_OBJ_TO_RESOURCE(barrier, IS_BARRIER)\n    }\n    /* no break */\n    default: {\n        auto _serialized_object = php_swoole_serialize(zvalue);\n        if (!_serialized_object) {\n            type = IS_UNDEF;\n            break;\n        } else {\n            type = IS_SERIALIZED_OBJECT;\n            value.serialized_object = _serialized_object;\n        }\n        break;\n    }\n    }\n}\n\nbool ArrayItem::equals(const zval *zvalue) const {\n    if (Z_TYPE_P(zvalue) != type) {\n        return false;\n    }\n    switch (type) {\n    case IS_LONG:\n        return Z_LVAL_P(zvalue) == value.lval;\n    case IS_DOUBLE:\n        return Z_DVAL_P(zvalue) == value.dval;\n    case IS_TRUE:\n    case IS_FALSE:\n    case IS_NULL:\n        return true;\n    case IS_STRING:\n        return zend_string_equals(value.str, Z_STR_P(zvalue));\n    default:\n        return false;\n    }\n}\n\n#define TYPE_PAIR(t1, t2) (((t1) << 4) | (t2))\n#define ITEM_TYPE(item) (item->type)\n#define ITEM_LVAL(item) (item->value.lval)\n#define ITEM_DVAL(item) (item->value.dval)\n#define ITEM_STR(item) (item->value.str)\n\nstatic int compare_long_to_string(zend_long lval, const zend_string *str) /* {{{ */\n{\n    zend_long str_lval;\n    double str_dval;\n    zend_uchar type = is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &str_lval, &str_dval, false);\n\n    if (type == IS_LONG) {\n        return lval > str_lval ? 1 : lval < str_lval ? -1 : 0;\n    }\n\n    if (type == IS_DOUBLE) {\n        double diff = (double) lval - str_dval;\n        return ZEND_NORMALIZE_BOOL(diff);\n    }\n\n    zend_string *lval_as_str = zend_long_to_str(lval);\n    int cmp_result = zend_binary_strcmp(ZSTR_VAL(lval_as_str), ZSTR_LEN(lval_as_str), ZSTR_VAL(str), ZSTR_LEN(str));\n    zend_string_release(lval_as_str);\n    return ZEND_NORMALIZE_BOOL(cmp_result);\n}\n/* }}} */\n\nstatic int compare_double_to_string(double dval, const zend_string *str) /* {{{ */\n{\n    zend_long str_lval;\n    double str_dval;\n    zend_uchar type = is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &str_lval, &str_dval, false);\n\n    if (type == IS_LONG) {\n        double diff = dval - (double) str_lval;\n        return ZEND_NORMALIZE_BOOL(diff);\n    }\n\n    if (type == IS_DOUBLE) {\n        if (dval == str_dval) {\n            return 0;\n        }\n        return ZEND_NORMALIZE_BOOL(dval - str_dval);\n    }\n\n    zend_string *dval_as_str = zend_double_to_str(dval);\n    int cmp_result = zend_binary_strcmp(ZSTR_VAL(dval_as_str), ZSTR_LEN(dval_as_str), ZSTR_VAL(str), ZSTR_LEN(str));\n    zend_string_release(dval_as_str);\n    return ZEND_NORMALIZE_BOOL(cmp_result);\n}\n/* }}} */\n\nint ArrayItem::compare(Bucket *a, Bucket *b) {\n    const ArrayItem *op1 = static_cast<ArrayItem *>(Z_PTR(a->val));\n    const ArrayItem *op2 = static_cast<ArrayItem *>(Z_PTR(b->val));\n\n    switch (TYPE_PAIR(ITEM_TYPE(op1), ITEM_TYPE(op2))) {\n    case TYPE_PAIR(IS_LONG, IS_LONG):\n        return ITEM_LVAL(op1) > ITEM_LVAL(op2) ? 1 : (ITEM_LVAL(op1) < ITEM_LVAL(op2) ? -1 : 0);\n\n    case TYPE_PAIR(IS_DOUBLE, IS_LONG):\n        return ZEND_NORMALIZE_BOOL(ITEM_DVAL(op1) - (double) ITEM_LVAL(op2));\n\n    case TYPE_PAIR(IS_LONG, IS_DOUBLE):\n        return ZEND_NORMALIZE_BOOL((double) ITEM_LVAL(op1) - ITEM_DVAL(op2));\n\n    case TYPE_PAIR(IS_DOUBLE, IS_DOUBLE):\n        if (ITEM_DVAL(op1) == ITEM_DVAL(op2)) {\n            return 0;\n        } else {\n            return ZEND_NORMALIZE_BOOL(ITEM_DVAL(op1) - ITEM_DVAL(op2));\n        }\n\n    case TYPE_PAIR(IS_NULL, IS_NULL):\n    case TYPE_PAIR(IS_NULL, IS_FALSE):\n    case TYPE_PAIR(IS_FALSE, IS_NULL):\n    case TYPE_PAIR(IS_FALSE, IS_FALSE):\n    case TYPE_PAIR(IS_TRUE, IS_TRUE):\n        return 0;\n\n    case TYPE_PAIR(IS_NULL, IS_TRUE):\n        return -1;\n\n    case TYPE_PAIR(IS_TRUE, IS_NULL):\n        return 1;\n\n    case TYPE_PAIR(IS_STRING, IS_STRING):\n        if (ITEM_STR(op1) == ITEM_STR(op2)) {\n            return 0;\n        }\n        return zendi_smart_strcmp(ITEM_STR(op1), ITEM_STR(op2));\n\n    case TYPE_PAIR(IS_NULL, IS_STRING):\n        return Z_STRLEN_P(op2) == 0 ? 0 : -1;\n\n    case TYPE_PAIR(IS_STRING, IS_NULL):\n        return Z_STRLEN_P(op1) == 0 ? 0 : 1;\n\n    case TYPE_PAIR(IS_LONG, IS_STRING):\n        return compare_long_to_string(ITEM_LVAL(op1), ITEM_STR(op2));\n\n    case TYPE_PAIR(IS_STRING, IS_LONG):\n        return -compare_long_to_string(ITEM_LVAL(op2), ITEM_STR(op1));\n\n    case TYPE_PAIR(IS_DOUBLE, IS_STRING):\n        if (zend_isnan(ITEM_DVAL(op1))) {\n            return 1;\n        }\n        return compare_double_to_string(ITEM_DVAL(op1), ITEM_STR(op2));\n\n    case TYPE_PAIR(IS_STRING, IS_DOUBLE):\n        if (zend_isnan(ITEM_DVAL(op2))) {\n            return 1;\n        }\n        return -compare_double_to_string(ITEM_DVAL(op2), ITEM_STR(op1));\n\n    case TYPE_PAIR(IS_OBJECT, IS_NULL):\n        return 1;\n\n    case TYPE_PAIR(IS_NULL, IS_OBJECT):\n        return -1;\n\n    default:\n        zend_throw_error(nullptr, \"Unsupported operand types\");\n        return 1;\n    }\n}\n\nvoid ArrayItem::fetch(zval *return_value) const {\n    switch (type) {\n    case IS_LONG:\n        RETVAL_LONG(value.lval);\n        break;\n    case IS_DOUBLE:\n        RETVAL_DOUBLE(value.dval);\n        break;\n    case IS_TRUE:\n        RETVAL_TRUE;\n        break;\n    case IS_FALSE:\n        RETVAL_FALSE;\n        break;\n    case IS_STRING:\n        RETVAL_NEW_STR(zend_string_init(ZSTR_VAL(value.str), ZSTR_LEN(value.str), 0));\n        break;\n    case IS_ARRAYLIST:\n        value.resource->add_ref();\n        php_swoole_thread_arraylist_create(return_value, value.resource);\n        break;\n    case IS_QUEUE:\n        value.resource->add_ref();\n        php_swoole_thread_queue_create(return_value, value.resource);\n        break;\n    case IS_LOCK:\n        value.resource->add_ref();\n        php_swoole_thread_lock_create(return_value, value.resource);\n        break;\n    case IS_MAP:\n        value.resource->add_ref();\n        php_swoole_thread_map_create(return_value, value.resource);\n        break;\n    case IS_BARRIER:\n        value.resource->add_ref();\n        php_swoole_thread_barrier_create(return_value, value.resource);\n        break;\n    case IS_ATOMIC:\n        value.resource->add_ref();\n        php_swoole_thread_atomic_create(return_value, value.resource);\n        break;\n    case IS_ATOMIC_LONG:\n        value.resource->add_ref();\n        php_swoole_thread_atomic_long_create(return_value, value.resource);\n        break;\n    case IS_STREAM_SOCKET:\n        php_swoole_thread_stream_create(return_value, value.socket.fd);\n        break;\n    case IS_CO_SOCKET:\n        php_swoole_thread_co_socket_create(return_value, value.socket.fd, value.socket.type);\n        break;\n#ifdef SWOOLE_SOCKETS_SUPPORT\n    case IS_PHP_SOCKET:\n        php_swoole_thread_php_socket_create(return_value, value.socket.fd);\n        break;\n#endif\n    case IS_SERIALIZED_OBJECT:\n        php_swoole_unserialize(value.serialized_object, return_value);\n        break;\n    default:\n        break;\n    }\n}\n\nvoid ArrayItem::release() {\n    if (type == IS_STRING) {\n        zend_string_release(value.str);\n        value.str = nullptr;\n    } else if (type == IS_STREAM_SOCKET || type == IS_CO_SOCKET || type == IS_PHP_SOCKET) {\n        close(value.socket.fd);\n        value.socket.fd = -1;\n    } else if (type == IS_SERIALIZED_OBJECT) {\n        zend_string_release(value.serialized_object);\n        value.serialized_object = nullptr;\n    } else if (type >= IS_ARRAYLIST && type <= IS_ATOMIC_LONG) {\n        value.resource->del_ref();\n        value.resource = nullptr;\n    }\n}\n\n#define INIT_DECR_VALUE(v)                                                                                             \\\n    zval rvalue = *v;                                                                                                  \\\n    if (Z_TYPE_P(v) == IS_DOUBLE) {                                                                                    \\\n        rvalue.value.dval = -rvalue.value.dval;                                                                        \\\n    } else {                                                                                                           \\\n        ZVAL_LONG(&rvalue, -zval_get_long(v));                                                                         \\\n    }\n\nvoid ZendArray::incr_update(ArrayItem *item, zval *zvalue, zval *return_value) {\n    if (item->type == IS_DOUBLE) {\n        item->value.dval += zval_get_double(zvalue);\n        RETVAL_DOUBLE(item->value.dval);\n    } else {\n        item->value.lval += zval_get_long(zvalue);\n        RETVAL_LONG(item->value.lval);\n    }\n}\n\nArrayItem *ZendArray::incr_create(zval *zvalue, zval *return_value) {\n    zval rvalue = *zvalue;\n    if (Z_TYPE_P(zvalue) == IS_DOUBLE) {\n        RETVAL_DOUBLE(rvalue.value.dval);\n    } else {\n        ZVAL_LONG(&rvalue, zval_get_long(zvalue));\n        RETVAL_LONG(rvalue.value.lval);\n    }\n    return new ArrayItem(&rvalue);\n}\n\nvoid ZendArray::strkey_incr(zval *zkey, zval *zvalue, zval *return_value) {\n    zend::String skey(zkey);\n\n    lock_.lock();\n    auto *item = static_cast<ArrayItem *>(zend_hash_find_ptr(&ht, skey.get()));\n    if (item) {\n        incr_update(item, zvalue, return_value);\n    } else {\n        item = incr_create(zvalue, return_value);\n        item->setKey(skey);\n        zend_hash_update_ptr(&ht, item->key, item);\n    }\n    lock_.unlock();\n}\n\nvoid ZendArray::intkey_incr(zend_long index, zval *zvalue, zval *return_value) {\n    lock_.lock();\n    auto item = static_cast<ArrayItem *>(zend_hash_index_find_ptr(&ht, index));\n    if (item) {\n        incr_update(item, zvalue, return_value);\n    } else {\n        item = incr_create(zvalue, return_value);\n        item = new ArrayItem(zvalue);\n        zend_hash_index_update_ptr(&ht, index, item);\n    }\n    lock_.unlock();\n}\n\nvoid ZendArray::strkey_decr(zval *zkey, zval *zvalue, zval *return_value) {\n    INIT_DECR_VALUE(zvalue);\n    strkey_incr(zkey, &rvalue, return_value);\n}\n\nvoid ZendArray::intkey_decr(zend_long index, zval *zvalue, zval *return_value) {\n    INIT_DECR_VALUE(zvalue);\n    intkey_incr(index, &rvalue, return_value);\n}\n\nvoid ZendArray::strkey_add(zval *zkey, zval *zvalue, zval *return_value) {\n    zend::String skey(zkey);\n    lock_.lock();\n    if (strkey_exists(skey)) {\n        RETVAL_FALSE;\n    } else {\n        add(skey, zvalue);\n        RETVAL_TRUE;\n    }\n    lock_.unlock();\n}\n\nvoid ZendArray::intkey_add(zend_long index, zval *zvalue, zval *return_value) {\n    lock_.lock();\n    if (intkey_exists(index)) {\n        RETVAL_FALSE;\n    } else {\n        add(index, zvalue);\n        RETVAL_TRUE;\n    }\n    lock_.unlock();\n}\n\nvoid ZendArray::strkey_update(zval *zkey, zval *zvalue, zval *return_value) {\n    zend::String skey(zkey);\n    lock_.lock();\n    if (!strkey_exists(skey)) {\n        RETVAL_FALSE;\n    } else {\n        auto item = new ArrayItem(zvalue);\n        item->setKey(skey);\n        zend_hash_update_ptr(&ht, item->key, item);\n        RETVAL_TRUE;\n    }\n    lock_.unlock();\n}\n\nvoid ZendArray::intkey_update(zend_long index, zval *zvalue, zval *return_value) {\n    lock_.lock();\n    if (!intkey_exists(index)) {\n        RETVAL_FALSE;\n    } else {\n        auto item = new ArrayItem(zvalue);\n        zend_hash_index_update_ptr(&ht, index, item);\n        RETVAL_TRUE;\n    }\n    lock_.unlock();\n}\n\nbool ZendArray::index_offsetGet(zend_long index, zval *return_value) {\n    bool out_of_range = true;\n    lock_.lock_rd();\n    if (index_exists(index)) {\n        out_of_range = false;\n        auto item = static_cast<ArrayItem *>(zend_hash_index_find_ptr(&ht, index));\n        if (item) {\n            item->fetch(return_value);\n        }\n    }\n    lock_.unlock();\n    return !out_of_range;\n}\n\nbool ZendArray::index_offsetSet(zend_long index, zval *zvalue) {\n    auto item = new ArrayItem(zvalue);\n    bool success = true;\n    lock_.lock();\n    if (index > zend_hash_num_elements(&ht)) {\n        success = false;\n        delete item;\n    } else if (index == -1 || index == zend_hash_num_elements(&ht)) {\n        zend_hash_next_index_insert_ptr(&ht, item);\n    } else {\n        zend_hash_index_update_ptr(&ht, index, item);\n    }\n    lock_.unlock();\n    return success;\n}\n\nvoid ZendArray::append(zval *zvalue) {\n    zend_hash_next_index_insert_ptr(&ht, new ArrayItem(zvalue));\n}\n\nbool ZendArray::index_incr(zval *zkey, zval *zvalue, zval *return_value) {\n    zend_long index = ZVAL_IS_NULL(zkey) ? -1 : zval_get_long(zkey);\n\n    bool success = true;\n    lock_.lock();\n    if (index > zend_hash_num_elements(&ht)) {\n        success = false;\n    } else if (index == -1 || index == zend_hash_num_elements(&ht)) {\n        auto item = incr_create(zvalue, return_value);\n        zend_hash_next_index_insert_ptr(&ht, item);\n    } else {\n        auto item = static_cast<ArrayItem *>(zend_hash_index_find_ptr(&ht, index));\n        incr_update(item, zvalue, return_value);\n    }\n    lock_.unlock();\n    return success;\n}\n\nvoid ZendArray::index_offsetExists(zend_long index, zval *return_value) {\n    lock_.lock_rd();\n    RETVAL_BOOL(index_exists(index));\n    lock_.unlock();\n}\n\nvoid ZendArray::index_offsetUnset(zend_long index) {\n    lock_.lock();\n    zend_long i = index;\n    zend_long n = zend_hash_num_elements(&ht);\n    HT_FLAGS(&ht) |= HASH_FLAG_PACKED | HASH_FLAG_STATIC_KEYS;\n    auto item = static_cast<ArrayItem *>(zend_hash_index_find_ptr(&ht, index));\n    delete item;\n    while (i < n - 1) {\n        Z_PTR(ht.arPacked[i]) = Z_PTR(ht.arPacked[i + 1]);\n        i++;\n    }\n    ht.nNumUsed--;\n    ht.nNumOfElements--;\n    ht.nNextFreeElement--;\n    lock_.unlock();\n}\n\nbool ZendArray::index_decr(zval *zkey, zval *zvalue, zval *return_value) {\n    INIT_DECR_VALUE(zvalue);\n    return index_incr(zkey, &rvalue, return_value);\n}\n\nvoid ZendArray::keys(zval *return_value) {\n    lock_.lock_rd();\n    zend_ulong elem_count = zend_hash_num_elements(&ht);\n    array_init_size(return_value, elem_count);\n    zend_hash_real_init_packed(Z_ARRVAL_P(return_value));\n    zend_ulong num_idx;\n    zend_string *str_idx;\n    zval *entry;\n    ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {\n        if (HT_IS_PACKED(&ht) && HT_IS_WITHOUT_HOLES(&ht)) {\n            /* Optimistic case: range(0..n-1) for vector-like packed array */\n            zend_ulong lval = 0;\n\n            for (; lval < elem_count; ++lval) {\n                ZEND_HASH_FILL_SET_LONG(lval);\n                ZEND_HASH_FILL_NEXT();\n            }\n        } else {\n            /* Go through input array and add keys to the return array */\n            ZEND_HASH_FOREACH_KEY_VAL(&ht, num_idx, str_idx, entry) {\n                if (str_idx) {\n                    ZEND_HASH_FILL_SET_STR(zend_string_init(str_idx->val, str_idx->len, 0));\n                } else {\n                    ZEND_HASH_FILL_SET_LONG(num_idx);\n                }\n                ZEND_HASH_FILL_NEXT();\n            }\n            ZEND_HASH_FOREACH_END();\n        }\n        (void) entry;\n    }\n    ZEND_HASH_FILL_END();\n    lock_.unlock();\n}\n\nvoid ZendArray::values(zval *return_value) {\n    lock_.lock_rd();\n    zend_ulong elem_count = zend_hash_num_elements(&ht);\n    array_init_size(return_value, elem_count);\n    void *tmp;\n    ZEND_HASH_FOREACH_PTR(&ht, tmp) {\n        zval value;\n        auto item = static_cast<ArrayItem *>(tmp);\n        item->fetch(&value);\n        zend_hash_next_index_insert_new(Z_ARR_P(return_value), &value);\n    }\n    ZEND_HASH_FOREACH_END();\n    lock_.unlock();\n}\n\nvoid ZendArray::to_array(zval *return_value) {\n    lock_.lock_rd();\n    zend_ulong elem_count = zend_hash_num_elements(&ht);\n    array_init_size(return_value, elem_count);\n    zend_string *key;\n    zend_ulong index;\n    void *tmp;\n    ZEND_HASH_FOREACH_KEY_PTR(&ht, index, key, tmp) {\n        zval value;\n        const auto item = static_cast<ArrayItem *>(tmp);\n        item->fetch(&value);\n        if (key) {\n            zend_hash_str_add(Z_ARR_P(return_value), ZSTR_VAL(key), ZSTR_LEN(key), &value);\n        } else {\n            zend_hash_index_add(Z_ARR_P(return_value), index, &value);\n        }\n    }\n    ZEND_HASH_FOREACH_END();\n    lock_.unlock();\n}\n\nvoid ZendArray::find(const zval *search, zval *return_value) {\n    lock_.lock_rd();\n    zend_string *key;\n    zend_ulong index;\n    void *tmp;\n    ZEND_HASH_FOREACH_KEY_PTR(&ht, index, key, tmp) {\n        const auto item = static_cast<ArrayItem *>(tmp);\n        if (item->equals(search)) {\n            if (key) {\n                RETVAL_STRINGL(ZSTR_VAL(key), ZSTR_LEN(key));\n            } else {\n                RETVAL_LONG(index);\n            }\n            break;\n        }\n    }\n    ZEND_HASH_FOREACH_END();\n    lock_.unlock();\n}\n\nvoid ZendArray::sort(bool renumber) {\n    lock_.lock();\n    zend_hash_sort(&ht, ArrayItem::compare, renumber);\n    lock_.unlock();\n}\n\nZendArray *ZendArray::from(zend_array *src) {\n    zend_string *key;\n    zend_ulong index;\n    zval *tmp;\n    auto *result = new ZendArray();\n    ZEND_HASH_FOREACH_KEY_VAL(src, index, key, tmp) {\n        ZVAL_DEREF(tmp);\n        if (key) {\n            result->add(key, tmp);\n        } else {\n            result->add(index, tmp);\n        }\n    }\n    ZEND_HASH_FOREACH_END();\n    return result;\n}\n\n#endif\n"
  },
  {
    "path": "ext-src/swoole_thread_arraylist.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_cxx.h\"\n\n#ifdef SW_THREAD\n#include \"php_swoole_thread.h\"\n\nSW_EXTERN_C_BEGIN\n#include \"stubs/php_swoole_thread_arraylist_arginfo.h\"\nSW_EXTERN_C_END\n\nzend_class_entry *swoole_thread_arraylist_ce;\nstatic zend_object_handlers swoole_thread_arraylist_handlers;\n\nstruct ThreadArrayListObject {\n    ZendArray *list;\n    zend_object std;\n};\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_thread_arraylist, __construct);\nstatic PHP_METHOD(swoole_thread_arraylist, offsetGet);\nstatic PHP_METHOD(swoole_thread_arraylist, offsetExists);\nstatic PHP_METHOD(swoole_thread_arraylist, offsetSet);\nstatic PHP_METHOD(swoole_thread_arraylist, offsetUnset);\nstatic PHP_METHOD(swoole_thread_arraylist, find);\nstatic PHP_METHOD(swoole_thread_arraylist, count);\nstatic PHP_METHOD(swoole_thread_arraylist, incr);\nstatic PHP_METHOD(swoole_thread_arraylist, decr);\nstatic PHP_METHOD(swoole_thread_arraylist, clean);\nstatic PHP_METHOD(swoole_thread_arraylist, toArray);\nstatic PHP_METHOD(swoole_thread_arraylist, sort);\nSW_EXTERN_C_END\n\nstatic sw_inline ThreadArrayListObject *arraylist_fetch_object(zend_object *obj) {\n    return reinterpret_cast<ThreadArrayListObject *>(reinterpret_cast<char *>(obj) -\n                                                     swoole_thread_arraylist_handlers.offset);\n}\n\nstatic void arraylist_free_object(zend_object *object) {\n    auto ao = arraylist_fetch_object(object);\n    if (ao->list) {\n        ao->list->del_ref();\n        ao->list = nullptr;\n    }\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *arraylist_create_object(zend_class_entry *ce) {\n    auto ao = static_cast<ThreadArrayListObject *>(zend_object_alloc(sizeof(ThreadArrayListObject), ce));\n    zend_object_std_init(&ao->std, ce);\n    object_properties_init(&ao->std, ce);\n    ao->std.handlers = &swoole_thread_arraylist_handlers;\n    return &ao->std;\n}\n\nstatic ThreadArrayListObject *arraylist_fetch_object_check(const zval *zobject) {\n    auto ao = arraylist_fetch_object(Z_OBJ_P(zobject));\n    if (!ao->list) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION, \"must call constructor first\");\n    }\n    return ao;\n}\n\nThreadResource *php_swoole_thread_arraylist_cast(const zval *zobject) {\n    return arraylist_fetch_object_check(zobject)->list;\n}\n\nvoid php_swoole_thread_arraylist_create(zval *return_value, ThreadResource *resource) {\n    auto obj = arraylist_create_object(swoole_thread_arraylist_ce);\n    auto ao = arraylist_fetch_object(obj);\n    ao->list = dynamic_cast<ZendArray *>(resource);\n    ZVAL_OBJ(return_value, obj);\n}\n\n// clang-format off\nstatic const zend_function_entry swoole_thread_arraylist_methods[] = {\n    PHP_ME(swoole_thread_arraylist, __construct,  arginfo_class_Swoole_Thread_ArrayList___construct,   ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_arraylist, offsetGet,    arginfo_class_Swoole_Thread_ArrayList_offsetGet,     ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_arraylist, offsetExists, arginfo_class_Swoole_Thread_ArrayList_offsetExists,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_arraylist, offsetSet,    arginfo_class_Swoole_Thread_ArrayList_offsetSet,     ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_arraylist, offsetUnset,  arginfo_class_Swoole_Thread_ArrayList_offsetUnset,   ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_arraylist, find,         arginfo_class_Swoole_Thread_ArrayList_find,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_arraylist, incr,         arginfo_class_Swoole_Thread_ArrayList_incr,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_arraylist, decr,         arginfo_class_Swoole_Thread_ArrayList_decr,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_arraylist, clean,        arginfo_class_Swoole_Thread_ArrayList_clean,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_arraylist, count,        arginfo_class_Swoole_Thread_ArrayList_count,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_arraylist, toArray,      arginfo_class_Swoole_Thread_ArrayList_toArray,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_arraylist, sort,         arginfo_class_Swoole_Thread_ArrayList_sort,          ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_thread_arraylist_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_thread_arraylist, \"Swoole\\\\Thread\\\\ArrayList\", nullptr, swoole_thread_arraylist_methods);\n    swoole_thread_arraylist_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE;\n    SW_SET_CLASS_CLONEABLE(swoole_thread_arraylist, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_arraylist, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(\n        swoole_thread_arraylist, arraylist_create_object, arraylist_free_object, ThreadArrayListObject, std);\n\n    zend_class_implements(swoole_thread_arraylist_ce, 2, zend_ce_arrayaccess, zend_ce_countable);\n    zend_declare_property_long(swoole_thread_arraylist_ce, ZEND_STRL(\"id\"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY);\n}\n\nstatic PHP_METHOD(swoole_thread_arraylist, __construct) {\n    zend_array *array = nullptr;\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_ARRAY_HT_OR_NULL(array)\n    ZEND_PARSE_PARAMETERS_END();\n\n    auto ao = arraylist_fetch_object(Z_OBJ_P(ZEND_THIS));\n    if (ao->list != nullptr) {\n        zend_throw_error(nullptr, \"Constructor of %s can only be called once\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        return;\n    }\n\n    if (array) {\n        if (!zend_array_is_list(array)) {\n            zend_throw_error(nullptr, \"the parameter $array must be an array of type list\");\n            return;\n        }\n        ao->list = ZendArray::from(array);\n    } else {\n        ao->list = new ZendArray();\n    }\n}\n\nstatic PHP_METHOD(swoole_thread_arraylist, offsetGet) {\n    zend_long index;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(index)\n    ZEND_PARSE_PARAMETERS_END();\n\n    auto ao = arraylist_fetch_object_check(ZEND_THIS);\n    if (!ao->list->index_offsetGet(index, return_value)) {\n        zend_throw_exception(swoole_exception_ce, \"out of range\", -1);\n    }\n}\n\nstatic PHP_METHOD(swoole_thread_arraylist, offsetExists) {\n    zend_long index;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(index)\n    ZEND_PARSE_PARAMETERS_END();\n\n    auto ao = arraylist_fetch_object_check(ZEND_THIS);\n    ao->list->index_offsetExists(index, return_value);\n}\n\nstatic PHP_METHOD(swoole_thread_arraylist, offsetSet) {\n    zval *zkey;\n    zval *zvalue;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_ZVAL(zkey)\n    Z_PARAM_ZVAL(zvalue)\n    ZEND_PARSE_PARAMETERS_END();\n\n    auto ao = arraylist_fetch_object_check(ZEND_THIS);\n    zend_long index = ZVAL_IS_NULL(zkey) ? -1 : zval_get_long(zkey);\n    if (!ao->list->index_offsetSet(index, zvalue)) {\n        zend_throw_exception(swoole_exception_ce, \"out of range\", -1);\n    }\n}\n\nstatic PHP_METHOD(swoole_thread_arraylist, incr) {\n    INIT_ARRAY_INCR_PARAMS\n    auto ao = arraylist_fetch_object_check(ZEND_THIS);\n    if (!ao->list->index_incr(zkey, zvalue, return_value)) {\n        zend_throw_exception(swoole_exception_ce, \"out of range\", -1);\n    }\n}\n\nstatic PHP_METHOD(swoole_thread_arraylist, decr) {\n    INIT_ARRAY_INCR_PARAMS\n    auto ao = arraylist_fetch_object_check(ZEND_THIS);\n    if (!ao->list->index_decr(zkey, zvalue, return_value)) {\n        zend_throw_exception(swoole_exception_ce, \"out of range\", -1);\n    }\n}\n\nstatic PHP_METHOD(swoole_thread_arraylist, offsetUnset) {\n    zend_long index;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(index)\n    ZEND_PARSE_PARAMETERS_END();\n\n    auto ao = arraylist_fetch_object_check(ZEND_THIS);\n    ao->list->index_offsetUnset(index);\n}\n\nstatic PHP_METHOD(swoole_thread_arraylist, find) {\n    zval *zvalue;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ZVAL(zvalue)\n    ZEND_PARSE_PARAMETERS_END();\n\n    auto ao = arraylist_fetch_object_check(ZEND_THIS);\n    ao->list->find(zvalue, return_value);\n}\n\nstatic PHP_METHOD(swoole_thread_arraylist, count) {\n    auto ao = arraylist_fetch_object_check(ZEND_THIS);\n    ao->list->count(return_value);\n}\n\nstatic PHP_METHOD(swoole_thread_arraylist, clean) {\n    auto ao = arraylist_fetch_object_check(ZEND_THIS);\n    ao->list->clean();\n}\n\nstatic PHP_METHOD(swoole_thread_arraylist, toArray) {\n    auto ao = arraylist_fetch_object_check(ZEND_THIS);\n    ao->list->to_array(return_value);\n}\n\nstatic PHP_METHOD(swoole_thread_arraylist, sort) {\n    auto ao = arraylist_fetch_object_check(ZEND_THIS);\n    ao->list->sort(true);\n}\n#endif\n"
  },
  {
    "path": "ext-src/swoole_thread_atomic.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_cxx.h\"\n#include \"php_swoole_thread.h\"\n\n#ifdef SW_THREAD\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_thread_atomic_arginfo.h\"\nEND_EXTERN_C()\n\nzend_class_entry *swoole_thread_atomic_ce;\nstatic zend_object_handlers swoole_thread_atomic_handlers;\n\nzend_class_entry *swoole_thread_atomic_long_ce;\nstatic zend_object_handlers swoole_thread_atomic_long_handlers;\n\nstruct AtomicResource : ThreadResource {\n    sw_atomic_t value;\n\n    explicit AtomicResource(zend_long _value) {\n        value = _value;\n    }\n\n    ~AtomicResource() override = default;\n};\n\nstruct AtomicObject {\n    AtomicResource *atomic;\n    zend_object std;\n};\n\nstatic sw_inline AtomicObject *atomic_fetch_object(zend_object *obj) {\n    return reinterpret_cast<AtomicObject *>(reinterpret_cast<char *>(obj) - swoole_thread_atomic_handlers.offset);\n}\n\nstatic sw_atomic_t *atomic_get_ptr(const zval *zobject) {\n    return &atomic_fetch_object(Z_OBJ_P(zobject))->atomic->value;\n}\n\nstatic void atomic_free_object(zend_object *object) {\n    AtomicObject *o = atomic_fetch_object(object);\n    if (o->atomic) {\n        o->atomic->del_ref();\n        o->atomic = nullptr;\n    }\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *atomic_create_object(zend_class_entry *ce) {\n    const auto atomic = static_cast<AtomicObject *>(zend_object_alloc(sizeof(AtomicObject), ce));\n    zend_object_std_init(&atomic->std, ce);\n    object_properties_init(&atomic->std, ce);\n    atomic->std.handlers = &swoole_thread_atomic_handlers;\n\n    return &atomic->std;\n}\n\nstruct AtomicLongResource : ThreadResource {\n    sw_atomic_long_t value;\n\n    explicit AtomicLongResource(zend_long _value) {\n        value = _value;\n    }\n\n    ~AtomicLongResource() override = default;\n};\n\nstruct AtomicLongObject {\n    AtomicLongResource *atomic;\n    zend_object std;\n};\n\nstatic sw_inline AtomicLongObject *atomic_long_fetch_object(zend_object *obj) {\n    return reinterpret_cast<AtomicLongObject *>(reinterpret_cast<char *>(obj) -\n                                                swoole_thread_atomic_long_handlers.offset);\n}\n\nstatic sw_atomic_long_t *atomic_long_get_ptr(const zval *zobject) {\n    return &atomic_long_fetch_object(Z_OBJ_P(zobject))->atomic->value;\n}\n\nstatic void atomic_long_free_object(zend_object *object) {\n    AtomicLongObject *o = atomic_long_fetch_object(object);\n    if (o->atomic) {\n        o->atomic->del_ref();\n        o->atomic = nullptr;\n    }\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *atomic_long_create_object(zend_class_entry *ce) {\n    auto atomic_long = static_cast<AtomicLongObject *>(zend_object_alloc(sizeof(AtomicLongObject), ce));\n    zend_object_std_init(&atomic_long->std, ce);\n    object_properties_init(&atomic_long->std, ce);\n    atomic_long->std.handlers = &swoole_thread_atomic_long_handlers;\n    return &atomic_long->std;\n}\n\nThreadResource *php_swoole_thread_atomic_cast(const zval *zobject) {\n    return atomic_fetch_object(Z_OBJ_P(zobject))->atomic;\n}\n\nThreadResource *php_swoole_thread_atomic_long_cast(const zval *zobject) {\n    return atomic_long_fetch_object(Z_OBJ_P(zobject))->atomic;\n}\n\nvoid php_swoole_thread_atomic_create(zval *return_value, ThreadResource *resource) {\n    auto obj = atomic_create_object(swoole_thread_atomic_ce);\n    auto ao = atomic_fetch_object(obj);\n    ao->atomic = dynamic_cast<AtomicResource *>(resource);\n    ZVAL_OBJ(return_value, obj);\n}\n\nvoid php_swoole_thread_atomic_long_create(zval *return_value, ThreadResource *resource) {\n    auto obj = atomic_long_create_object(swoole_thread_atomic_long_ce);\n    auto ao = atomic_long_fetch_object(obj);\n    ao->atomic = dynamic_cast<AtomicLongResource *>(resource);\n    ZVAL_OBJ(return_value, obj);\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_thread_atomic, __construct);\nstatic PHP_METHOD(swoole_thread_atomic, add);\nstatic PHP_METHOD(swoole_thread_atomic, sub);\nstatic PHP_METHOD(swoole_thread_atomic, get);\nstatic PHP_METHOD(swoole_thread_atomic, set);\nstatic PHP_METHOD(swoole_thread_atomic, cmpset);\nstatic PHP_METHOD(swoole_thread_atomic, wait);\nstatic PHP_METHOD(swoole_thread_atomic, wakeup);\n\nstatic PHP_METHOD(swoole_thread_atomic_long, __construct);\nstatic PHP_METHOD(swoole_thread_atomic_long, add);\nstatic PHP_METHOD(swoole_thread_atomic_long, sub);\nstatic PHP_METHOD(swoole_thread_atomic_long, get);\nstatic PHP_METHOD(swoole_thread_atomic_long, set);\nstatic PHP_METHOD(swoole_thread_atomic_long, cmpset);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic const zend_function_entry swoole_thread_atomic_methods[] =\n{\n    PHP_ME(swoole_thread_atomic, __construct, arginfo_class_Swoole_Thread_Atomic___construct, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_atomic, add,         arginfo_class_Swoole_Thread_Atomic_add,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_atomic, sub,         arginfo_class_Swoole_Thread_Atomic_sub,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_atomic, get,         arginfo_class_Swoole_Thread_Atomic_get,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_atomic, set,         arginfo_class_Swoole_Thread_Atomic_set,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_atomic, wait,        arginfo_class_Swoole_Thread_Atomic_wait,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_atomic, wakeup,      arginfo_class_Swoole_Thread_Atomic_wakeup,      ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_atomic, cmpset,      arginfo_class_Swoole_Thread_Atomic_cmpset,      ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n\nstatic const zend_function_entry swoole_thread_atomic_long_methods[] =\n{\n    PHP_ME(swoole_thread_atomic_long, __construct, arginfo_class_Swoole_Thread_Atomic_Long___construct, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_atomic_long, add,         arginfo_class_Swoole_Thread_Atomic_Long_add,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_atomic_long, sub,         arginfo_class_Swoole_Thread_Atomic_Long_sub,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_atomic_long, get,         arginfo_class_Swoole_Thread_Atomic_Long_get,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_atomic_long, set,         arginfo_class_Swoole_Thread_Atomic_Long_set,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_atomic_long, cmpset,      arginfo_class_Swoole_Thread_Atomic_Long_cmpset,      ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_thread_atomic_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_thread_atomic, \"Swoole\\\\Thread\\\\Atomic\", nullptr, swoole_thread_atomic_methods);\n    swoole_thread_atomic_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE;\n    SW_SET_CLASS_CLONEABLE(swoole_thread_atomic, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_atomic, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_atomic, atomic_create_object, atomic_free_object, AtomicObject, std);\n\n    SW_INIT_CLASS_ENTRY(\n        swoole_thread_atomic_long, \"Swoole\\\\Thread\\\\Atomic\\\\Long\", nullptr, swoole_thread_atomic_long_methods);\n    swoole_thread_atomic_long_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE;\n    SW_SET_CLASS_CLONEABLE(swoole_thread_atomic_long, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_atomic_long, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(\n        swoole_thread_atomic_long, atomic_long_create_object, atomic_long_free_object, AtomicLongObject, std);\n}\n\nPHP_METHOD(swoole_thread_atomic, __construct) {\n    auto o = atomic_fetch_object(Z_OBJ_P(ZEND_THIS));\n    zend_long value = 0;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (o->atomic) {\n        zend_throw_error(nullptr, \"Constructor of %s can only be called once\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        return;\n    }\n    o->atomic = new AtomicResource(value);\n}\n\nPHP_METHOD(swoole_thread_atomic, add) {\n    sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS);\n    zend_long add_value = 1;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(add_value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_LONG(sw_atomic_add_fetch(atomic, (uint32_t) add_value));\n}\n\nPHP_METHOD(swoole_thread_atomic, sub) {\n    sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS);\n    zend_long sub_value = 1;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(sub_value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_LONG(sw_atomic_sub_fetch(atomic, (uint32_t) sub_value));\n}\n\nPHP_METHOD(swoole_thread_atomic, get) {\n    sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS);\n    RETURN_LONG(*atomic);\n}\n\nPHP_METHOD(swoole_thread_atomic, set) {\n    sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS);\n    zend_long set_value;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(set_value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    *atomic = (uint32_t) set_value;\n}\n\nPHP_METHOD(swoole_thread_atomic, cmpset) {\n    sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS);\n    zend_long cmp_value, set_value;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_LONG(cmp_value)\n    Z_PARAM_LONG(set_value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(sw_atomic_cmp_set(atomic, (sw_atomic_t) cmp_value, (sw_atomic_t) set_value));\n}\n\nPHP_METHOD(swoole_thread_atomic, wait) {\n    sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS);\n    double timeout = 1.0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    SW_CHECK_RETURN(sw_atomic_futex_wait(atomic, timeout));\n}\n\nPHP_METHOD(swoole_thread_atomic, wakeup) {\n    sw_atomic_t *atomic = atomic_get_ptr(ZEND_THIS);\n    zend_long n = 1;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(n)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    SW_CHECK_RETURN(sw_atomic_futex_wakeup(atomic, (int) n));\n}\n\nPHP_METHOD(swoole_thread_atomic_long, __construct) {\n    auto o = atomic_long_fetch_object(Z_OBJ_P(ZEND_THIS));\n    zend_long value = 0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (o->atomic) {\n        zend_throw_error(nullptr, \"Constructor of %s can only be called once\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        RETURN_FALSE;\n    }\n    o->atomic = new AtomicLongResource(value);\n}\n\nPHP_METHOD(swoole_thread_atomic_long, add) {\n    sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS);\n    zend_long add_value = 1;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(add_value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_LONG(sw_atomic_add_fetch(atomic_long, (sw_atomic_long_t) add_value));\n}\n\nPHP_METHOD(swoole_thread_atomic_long, sub) {\n    sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS);\n    zend_long sub_value = 1;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(sub_value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_LONG(sw_atomic_sub_fetch(atomic_long, (sw_atomic_long_t) sub_value));\n}\n\nPHP_METHOD(swoole_thread_atomic_long, get) {\n    sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS);\n    RETURN_LONG(*atomic_long);\n}\n\nPHP_METHOD(swoole_thread_atomic_long, set) {\n    sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS);\n    zend_long set_value;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(set_value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    *atomic_long = (sw_atomic_long_t) set_value;\n}\n\nPHP_METHOD(swoole_thread_atomic_long, cmpset) {\n    sw_atomic_long_t *atomic_long = atomic_long_get_ptr(ZEND_THIS);\n    zend_long cmp_value, set_value;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_LONG(cmp_value)\n    Z_PARAM_LONG(set_value)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    RETURN_BOOL(sw_atomic_cmp_set(atomic_long, (sw_atomic_long_t) cmp_value, (sw_atomic_long_t) set_value));\n}\n\n#endif\n"
  },
  {
    "path": "ext-src/swoole_thread_barrier.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_private.h\"\n#include \"php_swoole_thread.h\"\n#include \"swoole_lock.h\"\n\n#ifdef SW_THREAD\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_thread_barrier_arginfo.h\"\nEND_EXTERN_C()\n\nusing swoole::Barrier;\n\nzend_class_entry *swoole_thread_barrier_ce;\nstatic zend_object_handlers swoole_thread_barrier_handlers;\n\nstruct BarrierResource : ThreadResource {\n    Barrier barrier_;\n    explicit BarrierResource(int count) {\n        barrier_.init(false, count);\n    }\n    void wait() {\n        barrier_.wait();\n    }\n    ~BarrierResource() override {\n        barrier_.destroy();\n    }\n};\n\nstruct BarrierObject {\n    BarrierResource *barrier;\n    zend_object std;\n};\n\nstatic sw_inline BarrierObject *barrier_fetch_object(zend_object *obj) {\n    return reinterpret_cast<BarrierObject *>(reinterpret_cast<char *>(obj) - swoole_thread_barrier_handlers.offset);\n}\n\nstatic BarrierResource *barrier_get_ptr(const zval *zobject) {\n    return barrier_fetch_object(Z_OBJ_P(zobject))->barrier;\n}\n\nstatic BarrierResource *barrier_get_and_check_ptr(const zval *zobject) {\n    BarrierResource *barrier = barrier_get_ptr(zobject);\n    if (UNEXPECTED(!barrier)) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION, \"must call constructor first\");\n    }\n    return barrier;\n}\n\nstatic void barrier_free_object(zend_object *object) {\n    auto bo = barrier_fetch_object(object);\n    if (bo->barrier) {\n        bo->barrier->del_ref();\n        bo->barrier = nullptr;\n    }\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *barrier_create_object(zend_class_entry *ce) {\n    auto bo = static_cast<BarrierObject *>(zend_object_alloc(sizeof(BarrierObject), ce));\n    zend_object_std_init(&bo->std, ce);\n    object_properties_init(&bo->std, ce);\n    bo->std.handlers = &swoole_thread_barrier_handlers;\n    return &bo->std;\n}\n\nThreadResource *php_swoole_thread_barrier_cast(const zval *zobject) {\n    return barrier_fetch_object(Z_OBJ_P(zobject))->barrier;\n}\n\nvoid php_swoole_thread_barrier_create(zval *return_value, ThreadResource *resource) {\n    auto obj = barrier_create_object(swoole_thread_barrier_ce);\n    auto bo = barrier_fetch_object(obj);\n    bo->barrier = dynamic_cast<BarrierResource *>(resource);\n    ZVAL_OBJ(return_value, obj);\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_thread_barrier, __construct);\nstatic PHP_METHOD(swoole_thread_barrier, wait);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic constexpr zend_function_entry swoole_thread_barrier_methods[] =\n{\n    PHP_ME(swoole_thread_barrier, __construct,  arginfo_class_Swoole_Thread_Barrier___construct,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_barrier, wait,         arginfo_class_Swoole_Thread_Barrier_wait,         ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_thread_barrier_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_thread_barrier, \"Swoole\\\\Thread\\\\Barrier\", nullptr, swoole_thread_barrier_methods);\n    swoole_thread_barrier_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE;\n    SW_SET_CLASS_CLONEABLE(swoole_thread_barrier, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_barrier, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_barrier, barrier_create_object, barrier_free_object, BarrierObject, std);\n}\n\nstatic PHP_METHOD(swoole_thread_barrier, __construct) {\n    auto bo = barrier_fetch_object(Z_OBJ_P(ZEND_THIS));\n    if (bo->barrier != nullptr) {\n        zend_throw_error(nullptr, \"Constructor of %s can only be called once\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        return;\n    }\n\n    zend_long count;\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(count)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if (count < 2) {\n        zend_throw_exception(\n            swoole_exception_ce, \"The parameter $count must be greater than 1\", SW_ERROR_INVALID_PARAMS);\n        return;\n    }\n\n    bo->barrier = new BarrierResource(count);\n}\n\nstatic PHP_METHOD(swoole_thread_barrier, wait) {\n    BarrierResource *barrier = barrier_get_and_check_ptr(ZEND_THIS);\n    if (barrier) {\n        barrier->wait();\n    }\n}\n\n#endif\n"
  },
  {
    "path": "ext-src/swoole_thread_lock.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_private.h\"\n#include \"php_swoole_thread.h\"\n#include \"swoole_lock.h\"\n#include \"swoole_timer.h\"\n\n#ifdef SW_THREAD\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_thread_lock_arginfo.h\"\nEND_EXTERN_C()\n\nusing swoole::Lock;\nusing swoole::Mutex;\n#ifdef HAVE_SPINLOCK\nusing swoole::SpinLock;\n#endif\n#ifdef HAVE_RWLOCK\nusing swoole::RWLock;\n#endif\n\nzend_class_entry *swoole_thread_lock_ce;\nstatic zend_object_handlers swoole_thread_lock_handlers;\n\nstruct LockResource : public ThreadResource {\n    Lock *lock_;\n    LockResource(int type) : ThreadResource() {\n        switch (type) {\n#ifdef HAVE_SPINLOCK\n        case Lock::SPIN_LOCK:\n            lock_ = new SpinLock(false);\n            break;\n#endif\n#ifdef HAVE_RWLOCK\n        case Lock::RW_LOCK:\n            lock_ = new RWLock(false);\n            break;\n#endif\n        case Lock::MUTEX:\n        default:\n            lock_ = new Mutex(false);\n            break;\n        }\n    }\n    ~LockResource() override {\n        delete lock_;\n    }\n};\n\nstruct ThreadLockObject {\n    LockResource *lock;\n    zend_object std;\n};\n\nstatic sw_inline ThreadLockObject *thread_lock_fetch_object(zend_object *obj) {\n    return reinterpret_cast<ThreadLockObject *>(reinterpret_cast<char *>(obj) - swoole_thread_lock_handlers.offset);\n}\n\nstatic Lock *thread_lock_get_ptr(const zval *zobject) {\n    return thread_lock_fetch_object(Z_OBJ_P(zobject))->lock->lock_;\n}\n\nstatic Lock *thread_lock_get_and_check_ptr(const zval *zobject) {\n    Lock *lock = thread_lock_get_ptr(zobject);\n    if (!lock) {\n        php_swoole_fatal_error(E_ERROR, \"must call constructor first\");\n    }\n    return lock;\n}\n\nstatic void thread_lock_free_object(zend_object *object) {\n    ThreadLockObject *o = thread_lock_fetch_object(object);\n    if (o->lock) {\n        o->lock->del_ref();\n        o->lock = nullptr;\n    }\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *thread_lock_create_object(zend_class_entry *ce) {\n    auto lock = static_cast<ThreadLockObject *>(zend_object_alloc(sizeof(ThreadLockObject), ce));\n    zend_object_std_init(&lock->std, ce);\n    object_properties_init(&lock->std, ce);\n    lock->std.handlers = &swoole_thread_lock_handlers;\n    return &lock->std;\n}\n\nThreadResource *php_swoole_thread_lock_cast(const zval *zobject) {\n    return thread_lock_fetch_object(Z_OBJ_P(zobject))->lock;\n}\n\nvoid php_swoole_thread_lock_create(zval *return_value, ThreadResource *resource) {\n    auto obj = thread_lock_create_object(swoole_thread_lock_ce);\n    auto lo = thread_lock_fetch_object(obj);\n    lo->lock = dynamic_cast<LockResource *>(resource);\n    ZVAL_OBJ(return_value, obj);\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_thread_lock, __construct);\nstatic PHP_METHOD(swoole_thread_lock, lock);\nstatic PHP_METHOD(swoole_thread_lock, unlock);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic constexpr zend_function_entry swoole_thread_lock_methods[] =\n{\n    PHP_ME(swoole_thread_lock, __construct,  arginfo_class_Swoole_Thread_Lock___construct,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_lock, lock,         arginfo_class_Swoole_Thread_Lock_lock,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_lock, unlock,       arginfo_class_Swoole_Thread_Lock_unlock,       ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_thread_lock_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_thread_lock, \"Swoole\\\\Thread\\\\Lock\", nullptr, swoole_thread_lock_methods);\n    swoole_thread_lock_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE;\n    SW_SET_CLASS_CLONEABLE(swoole_thread_lock, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_lock, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(\n        swoole_thread_lock, thread_lock_create_object, thread_lock_free_object, ThreadLockObject, std);\n\n    zend_declare_class_constant_long(swoole_thread_lock_ce, ZEND_STRL(\"MUTEX\"), Lock::MUTEX);\n#ifdef HAVE_RWLOCK\n    zend_declare_class_constant_long(swoole_thread_lock_ce, ZEND_STRL(\"RWLOCK\"), Lock::RW_LOCK);\n#endif\n#ifdef HAVE_SPINLOCK\n    zend_declare_class_constant_long(swoole_thread_lock_ce, ZEND_STRL(\"SPINLOCK\"), Lock::SPIN_LOCK);\n#endif\n    zend_declare_property_long(swoole_thread_lock_ce, ZEND_STRL(\"errCode\"), 0, ZEND_ACC_PUBLIC);\n}\n\nstatic PHP_METHOD(swoole_thread_lock, __construct) {\n    auto o = thread_lock_fetch_object(Z_OBJ_P(ZEND_THIS));\n    if (o->lock != nullptr) {\n        zend_throw_error(nullptr, \"Constructor of %s can only be called once\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        RETURN_FALSE;\n    }\n\n    zend_long type = Lock::MUTEX;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(type)\n    ZEND_PARSE_PARAMETERS_END();\n\n    o->lock = new LockResource(type);\n}\n\nstatic PHP_METHOD(swoole_thread_lock, lock) {\n    zend_long operation = LOCK_EX;\n    double timeout = -1;\n\n    ZEND_PARSE_PARAMETERS_START(0, 2)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(operation)\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    Lock *lock = thread_lock_get_and_check_ptr(ZEND_THIS);\n    SW_LOCK_CHECK_RETURN(lock->lock(operation, swoole::sec2msec(timeout)));\n}\n\nstatic PHP_METHOD(swoole_thread_lock, unlock) {\n    Lock *lock = thread_lock_get_and_check_ptr(ZEND_THIS);\n    SW_LOCK_CHECK_RETURN(lock->unlock());\n}\n\n#endif\n"
  },
  {
    "path": "ext-src/swoole_thread_map.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_cxx.h\"\n\n#ifdef SW_THREAD\n#include \"php_swoole_thread.h\"\n\nSW_EXTERN_C_BEGIN\n#include \"stubs/php_swoole_thread_map_arginfo.h\"\nSW_EXTERN_C_END\n\nzend_class_entry *swoole_thread_map_ce;\nstatic zend_object_handlers swoole_thread_map_handlers;\n\nstruct ThreadMapObject {\n    ZendArray *map;\n    zend_object std;\n};\n\nstatic sw_inline ThreadMapObject *map_fetch_object(zend_object *obj) {\n    return reinterpret_cast<ThreadMapObject *>(reinterpret_cast<char *>(obj) - swoole_thread_map_handlers.offset);\n}\n\nstatic void map_free_object(zend_object *object) {\n    auto mo = map_fetch_object(object);\n    if (mo->map) {\n        mo->map->del_ref();\n        mo->map = nullptr;\n    }\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *map_create_object(zend_class_entry *ce) {\n    auto mo = static_cast<ThreadMapObject *>(zend_object_alloc(sizeof(ThreadMapObject), ce));\n    zend_object_std_init(&mo->std, ce);\n    object_properties_init(&mo->std, ce);\n    mo->std.handlers = &swoole_thread_map_handlers;\n    return &mo->std;\n}\n\nstatic ThreadMapObject *map_fetch_object_check(const zval *zobject) {\n    ThreadMapObject *map = map_fetch_object(Z_OBJ_P(zobject));\n    if (!map->map) {\n        php_swoole_fatal_error(E_ERROR, \"must call constructor first\");\n    }\n    return map;\n}\n\nThreadResource *php_swoole_thread_map_cast(const zval *zobject) {\n    return map_fetch_object(Z_OBJ_P(zobject))->map;\n}\n\nvoid php_swoole_thread_map_create(zval *return_value, ThreadResource *resource) {\n    auto obj = map_create_object(swoole_thread_map_ce);\n    auto mo = map_fetch_object(obj);\n    mo->map = dynamic_cast<ZendArray *>(resource);\n    ZVAL_OBJ(return_value, obj);\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_thread_map, __construct);\nstatic PHP_METHOD(swoole_thread_map, offsetGet);\nstatic PHP_METHOD(swoole_thread_map, offsetExists);\nstatic PHP_METHOD(swoole_thread_map, offsetSet);\nstatic PHP_METHOD(swoole_thread_map, offsetUnset);\nstatic PHP_METHOD(swoole_thread_map, find);\nstatic PHP_METHOD(swoole_thread_map, count);\nstatic PHP_METHOD(swoole_thread_map, keys);\nstatic PHP_METHOD(swoole_thread_map, values);\nstatic PHP_METHOD(swoole_thread_map, incr);\nstatic PHP_METHOD(swoole_thread_map, decr);\nstatic PHP_METHOD(swoole_thread_map, add);\nstatic PHP_METHOD(swoole_thread_map, update);\nstatic PHP_METHOD(swoole_thread_map, clean);\nstatic PHP_METHOD(swoole_thread_map, toArray);\nstatic PHP_METHOD(swoole_thread_map, sort);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic const zend_function_entry swoole_thread_map_methods[] = {\n    PHP_ME(swoole_thread_map, __construct,     arginfo_class_Swoole_Thread_Map___construct,   ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_map, offsetGet,       arginfo_class_Swoole_Thread_Map_offsetGet,     ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_map, offsetExists,    arginfo_class_Swoole_Thread_Map_offsetExists,  ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_map, offsetSet,       arginfo_class_Swoole_Thread_Map_offsetSet,     ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_map, offsetUnset,     arginfo_class_Swoole_Thread_Map_offsetUnset,   ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_map, find,            arginfo_class_Swoole_Thread_Map_find,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_map, count,           arginfo_class_Swoole_Thread_Map_count,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_map, incr,            arginfo_class_Swoole_Thread_Map_incr,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_map, decr,            arginfo_class_Swoole_Thread_Map_decr,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_map, add,             arginfo_class_Swoole_Thread_Map_add,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_map, update,          arginfo_class_Swoole_Thread_Map_update,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_map, clean,           arginfo_class_Swoole_Thread_Map_clean,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_map, keys,            arginfo_class_Swoole_Thread_Map_keys,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_map, values,          arginfo_class_Swoole_Thread_Map_values,        ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_map, toArray,         arginfo_class_Swoole_Thread_Map_toArray,       ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_map, sort,            arginfo_class_Swoole_Thread_Map_sort,          ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_thread_map_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_thread_map, \"Swoole\\\\Thread\\\\Map\", nullptr, swoole_thread_map_methods);\n    swoole_thread_map_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE;\n    SW_SET_CLASS_CLONEABLE(swoole_thread_map, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_map, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_map, map_create_object, map_free_object, ThreadMapObject, std);\n\n    zend_class_implements(swoole_thread_map_ce, 2, zend_ce_arrayaccess, zend_ce_countable);\n}\n\nstatic PHP_METHOD(swoole_thread_map, __construct) {\n    zend_array *array = nullptr;\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_ARRAY_HT_OR_NULL(array)\n    ZEND_PARSE_PARAMETERS_END();\n\n    auto mo = map_fetch_object(Z_OBJ_P(ZEND_THIS));\n    if (mo->map != nullptr) {\n        zend_throw_error(nullptr, \"Constructor of %s can only be called once\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        return;\n    }\n\n    if (array) {\n        mo->map = ZendArray::from(array);\n    } else {\n        mo->map = new ZendArray();\n    }\n}\n\nstatic int handle_array_key(const zval *key, zend_ulong *idx) {\n    switch (Z_TYPE_P(key)) {\n    case IS_STRING:\n        return _zend_handle_numeric_str(Z_STRVAL_P(key), Z_STRLEN_P(key), idx) ? IS_LONG : IS_STRING;\n    case IS_LONG:\n        *idx = Z_LVAL_P(key);\n        return IS_LONG;\n    case IS_NULL:\n        return IS_NULL;\n    case IS_DOUBLE:\n        *idx = zend_dval_to_lval_safe(Z_DVAL_P(key));\n        return IS_LONG;\n    case IS_FALSE:\n        *idx = 0;\n        return IS_LONG;\n    case IS_TRUE:\n        *idx = 1;\n        return IS_LONG;\n    case IS_RESOURCE:\n        zend_use_resource_as_offset(key);\n        *idx = Z_RES_HANDLE_P(key);\n        return IS_LONG;\n    default:\n        zend_argument_type_error(1, \"Illegal offset type\");\n        return IS_UNDEF;\n    }\n}\n\n#define ZEND_ARRAY_CALL_METHOD(array, method, zkey, ...)                                                               \\\n    zend_ulong idx;                                                                                                    \\\n    int type_of_key = handle_array_key(zkey, &idx);                                                                    \\\n    if (type_of_key == IS_LONG) {                                                                                      \\\n        array->intkey_##method(idx, ##__VA_ARGS__);                                                                    \\\n    } else if (type_of_key == IS_STRING) {                                                                             \\\n        array->strkey_##method(zkey, ##__VA_ARGS__);                                                                   \\\n    } else if (type_of_key == IS_NULL) {                                                                               \\\n        zval empty_str;                                                                                                \\\n        ZVAL_EMPTY_STRING(&empty_str);                                                                                 \\\n        array->strkey_##method(&empty_str, ##__VA_ARGS__);                                                             \\\n    } else {                                                                                                           \\\n        zend_type_error(\"Illegal offset type\");                                                                        \\\n    }\n\nstatic PHP_METHOD(swoole_thread_map, offsetGet) {\n    zval *zkey;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ZVAL(zkey)\n    ZEND_PARSE_PARAMETERS_END();\n\n    auto mo = map_fetch_object_check(ZEND_THIS);\n    ZEND_ARRAY_CALL_METHOD(mo->map, offsetGet, zkey, return_value);\n}\n\nstatic PHP_METHOD(swoole_thread_map, offsetExists) {\n    zval *zkey;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ZVAL(zkey)\n    ZEND_PARSE_PARAMETERS_END();\n\n    auto mo = map_fetch_object_check(ZEND_THIS);\n    ZEND_ARRAY_CALL_METHOD(mo->map, offsetExists, zkey, return_value);\n}\n\nstatic PHP_METHOD(swoole_thread_map, offsetSet) {\n    zval *zkey;\n    zval *zvalue;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_ZVAL(zkey)\n    Z_PARAM_ZVAL(zvalue)\n    ZEND_PARSE_PARAMETERS_END();\n\n    auto mo = map_fetch_object_check(ZEND_THIS);\n    ZEND_ARRAY_CALL_METHOD(mo->map, offsetSet, zkey, zvalue);\n}\n\nstatic PHP_METHOD(swoole_thread_map, incr) {\n    INIT_ARRAY_INCR_PARAMS\n    auto mo = map_fetch_object_check(ZEND_THIS);\n    ZEND_ARRAY_CALL_METHOD(mo->map, incr, zkey, zvalue, return_value);\n}\n\nstatic PHP_METHOD(swoole_thread_map, decr) {\n    INIT_ARRAY_INCR_PARAMS\n    auto mo = map_fetch_object_check(ZEND_THIS);\n    ZEND_ARRAY_CALL_METHOD(mo->map, decr, zkey, zvalue, return_value);\n}\n\nstatic PHP_METHOD(swoole_thread_map, add) {\n    zval *zkey;\n    zval *zvalue;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_ZVAL(zkey)\n    Z_PARAM_ZVAL(zvalue)\n    ZEND_PARSE_PARAMETERS_END();\n\n    auto mo = map_fetch_object_check(ZEND_THIS);\n    ZEND_ARRAY_CALL_METHOD(mo->map, add, zkey, zvalue, return_value);\n}\n\nstatic PHP_METHOD(swoole_thread_map, update) {\n    zval *zkey;\n    zval *zvalue;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_ZVAL(zkey)\n    Z_PARAM_ZVAL(zvalue)\n    ZEND_PARSE_PARAMETERS_END();\n\n    auto mo = map_fetch_object_check(ZEND_THIS);\n    ZEND_ARRAY_CALL_METHOD(mo->map, update, zkey, zvalue, return_value);\n}\n\nstatic PHP_METHOD(swoole_thread_map, offsetUnset) {\n    zval *zkey;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ZVAL(zkey)\n    ZEND_PARSE_PARAMETERS_END();\n\n    auto mo = map_fetch_object_check(ZEND_THIS);\n    ZEND_ARRAY_CALL_METHOD(mo->map, offsetUnset, zkey);\n}\n\nstatic PHP_METHOD(swoole_thread_map, find) {\n    zval *zvalue;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_ZVAL(zvalue)\n    ZEND_PARSE_PARAMETERS_END();\n\n    auto mo = map_fetch_object_check(ZEND_THIS);\n    mo->map->find(zvalue, return_value);\n}\n\nstatic PHP_METHOD(swoole_thread_map, count) {\n    auto mo = map_fetch_object_check(ZEND_THIS);\n    mo->map->count(return_value);\n}\n\nstatic PHP_METHOD(swoole_thread_map, keys) {\n    auto mo = map_fetch_object_check(ZEND_THIS);\n    mo->map->keys(return_value);\n}\n\nstatic PHP_METHOD(swoole_thread_map, values) {\n    auto mo = map_fetch_object_check(ZEND_THIS);\n    mo->map->values(return_value);\n}\n\nstatic PHP_METHOD(swoole_thread_map, toArray) {\n    auto mo = map_fetch_object_check(ZEND_THIS);\n    mo->map->to_array(return_value);\n}\n\nstatic PHP_METHOD(swoole_thread_map, clean) {\n    auto mo = map_fetch_object_check(ZEND_THIS);\n    mo->map->clean();\n}\n\nstatic PHP_METHOD(swoole_thread_map, sort) {\n    auto mo = map_fetch_object_check(ZEND_THIS);\n    mo->map->sort(false);\n}\n#endif\n"
  },
  {
    "path": "ext-src/swoole_thread_queue.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_cxx.h\"\n\n#ifdef SW_THREAD\n#include \"php_swoole_thread.h\"\n#include \"stubs/php_swoole_thread_queue_arginfo.h\"\n\n#include <queue>\n#include <condition_variable>\n\nzend_class_entry *swoole_thread_queue_ce;\nstatic zend_object_handlers swoole_thread_queue_handlers;\n\nstruct Queue : ThreadResource {\n    std::queue<ArrayItem *> queue;\n    std::mutex lock_;\n    std::condition_variable cv_;\n\n    enum {\n        NOTIFY_NONE = 0,\n        NOTIFY_ONE = 1,\n        NOTIFY_ALL = 2,\n    };\n\n    Queue() = default;\n\n    ~Queue() override {\n        clean();\n    }\n\n    void push(zval *zvalue) {\n        auto item = new ArrayItem(zvalue);\n        lock_.lock();\n        queue.push(item);\n        lock_.unlock();\n    }\n\n    void pop(zval *return_value) {\n        ArrayItem *item = nullptr;\n        lock_.lock();\n        if (!queue.empty()) {\n            item = queue.front();\n            queue.pop();\n        }\n        lock_.unlock();\n        if (item) {\n            item->fetch(return_value);\n            delete item;\n        }\n    }\n\n    void push_notify(zval *zvalue, bool notify_all) {\n        push(zvalue);\n        if (notify_all) {\n            cv_.notify_all();\n        } else {\n            cv_.notify_one();\n        }\n    }\n\n    void pop_wait(zval *return_value, double timeout) {\n        ArrayItem *item = nullptr;\n        std::unique_lock<std::mutex> _lock(lock_);\n\n        if (timeout > 0) {\n            cv_.wait_for(_lock, std::chrono::duration<double>(timeout), [this] { return !queue.empty(); });\n        } else {\n            cv_.wait(_lock, [this] { return !queue.empty(); });\n        }\n\n        if (!queue.empty()) {\n            item = queue.front();\n            queue.pop();\n        } else {\n            // All threads have been awakened,\n            // but the data has already been acquired by other thread, returning NULL.\n            RETVAL_NULL();\n            swoole_set_last_error(SW_ERROR_NO_PAYLOAD);\n        }\n\n        if (item) {\n            item->fetch(return_value);\n            delete item;\n        }\n    }\n\n    void count(zval *return_value) {\n        lock_.lock();\n        RETVAL_LONG(queue.size());\n        lock_.unlock();\n    }\n\n    void clean() {\n        lock_.lock();\n        while (!queue.empty()) {\n            const ArrayItem *item = queue.front();\n            delete item;\n            queue.pop();\n        }\n        lock_.unlock();\n    }\n};\n\nstruct ThreadQueueObject {\n    Queue *queue;\n    zend_object std;\n};\n\nstatic sw_inline ThreadQueueObject *queue_fetch_object(zend_object *obj) {\n    return reinterpret_cast<ThreadQueueObject *>(reinterpret_cast<char *>(obj) - swoole_thread_queue_handlers.offset);\n}\n\nstatic void queue_free_object(zend_object *object) {\n    ThreadQueueObject *qo = queue_fetch_object(object);\n    if (qo->queue) {\n        qo->queue->del_ref();\n        qo->queue = nullptr;\n    }\n    zend_object_std_dtor(object);\n}\n\nstatic zend_object *queue_create_object(zend_class_entry *ce) {\n    const auto qo = static_cast<ThreadQueueObject *>(zend_object_alloc(sizeof(ThreadQueueObject), ce));\n    zend_object_std_init(&qo->std, ce);\n    object_properties_init(&qo->std, ce);\n    qo->std.handlers = &swoole_thread_queue_handlers;\n    return &qo->std;\n}\n\nThreadQueueObject *queue_fetch_object_check(const zval *zobject) {\n    ThreadQueueObject *qo = queue_fetch_object(Z_OBJ_P(zobject));\n    if (!qo->queue) {\n        php_swoole_fatal_error(E_ERROR, \"must call constructor first\");\n    }\n    return qo;\n}\n\nThreadResource *php_swoole_thread_queue_cast(const zval *zobject) {\n    return queue_fetch_object(Z_OBJ_P(zobject))->queue;\n}\n\nvoid php_swoole_thread_queue_create(zval *return_value, ThreadResource *resource) {\n    auto obj = queue_create_object(swoole_thread_queue_ce);\n    auto qo = queue_fetch_object(obj);\n    qo->queue = dynamic_cast<Queue *>(resource);\n    ZVAL_OBJ(return_value, obj);\n}\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_thread_queue, __construct);\nstatic PHP_METHOD(swoole_thread_queue, push);\nstatic PHP_METHOD(swoole_thread_queue, pop);\nstatic PHP_METHOD(swoole_thread_queue, count);\nstatic PHP_METHOD(swoole_thread_queue, clean);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic const zend_function_entry swoole_thread_queue_methods[] = {\n    PHP_ME(swoole_thread_queue, __construct,  arginfo_class_Swoole_Thread_Queue___construct,   ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_queue, push,         arginfo_class_Swoole_Thread_Queue_push,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_queue, pop,          arginfo_class_Swoole_Thread_Queue_pop,           ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_queue, clean,        arginfo_class_Swoole_Thread_Queue_clean,         ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_thread_queue, count,        arginfo_class_Swoole_Thread_Queue_count,         ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_thread_queue_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_thread_queue, \"Swoole\\\\Thread\\\\Queue\", nullptr, swoole_thread_queue_methods);\n    swoole_thread_queue_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NOT_SERIALIZABLE;\n    SW_SET_CLASS_CLONEABLE(swoole_thread_queue, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_queue, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_queue, queue_create_object, queue_free_object, ThreadQueueObject, std);\n\n    zend_class_implements(swoole_thread_queue_ce, 1, zend_ce_countable);\n\n    zend_declare_class_constant_long(swoole_thread_queue_ce, ZEND_STRL(\"NOTIFY_ONE\"), Queue::NOTIFY_ONE);\n    zend_declare_class_constant_long(swoole_thread_queue_ce, ZEND_STRL(\"NOTIFY_ALL\"), Queue::NOTIFY_ALL);\n}\n\nstatic PHP_METHOD(swoole_thread_queue, __construct) {\n    auto qo = queue_fetch_object(Z_OBJ_P(ZEND_THIS));\n    if (qo->queue != nullptr) {\n        zend_throw_error(nullptr, \"Constructor of %s can only be called once\", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS));\n        return;\n    }\n    qo->queue = new Queue();\n}\n\nstatic PHP_METHOD(swoole_thread_queue, push) {\n    zval *zvalue;\n    zend_long notify_which = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_ZVAL(zvalue)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(notify_which)\n    ZEND_PARSE_PARAMETERS_END();\n\n    auto qo = queue_fetch_object_check(ZEND_THIS);\n    if (notify_which > 0) {\n        qo->queue->push_notify(zvalue, notify_which == Queue::NOTIFY_ALL);\n    } else {\n        qo->queue->push(zvalue);\n    }\n}\n\nstatic PHP_METHOD(swoole_thread_queue, pop) {\n    double timeout = 0;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END();\n\n    auto qo = queue_fetch_object_check(ZEND_THIS);\n    if (timeout == 0) {\n        qo->queue->pop(return_value);\n    } else {\n        qo->queue->pop_wait(return_value, timeout);\n    }\n}\n\nstatic PHP_METHOD(swoole_thread_queue, count) {\n    auto qo = queue_fetch_object_check(ZEND_THIS);\n    qo->queue->count(return_value);\n}\n\nstatic PHP_METHOD(swoole_thread_queue, clean) {\n    auto qo = queue_fetch_object_check(ZEND_THIS);\n    qo->queue->clean();\n}\n\n#endif\n"
  },
  {
    "path": "ext-src/swoole_timer.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2015 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_cxx.h\"\n#include \"php_swoole_process.h\"\n\n#include \"swoole_server.h\"\n\n#include \"ext/spl/spl_array.h\"\n\nBEGIN_EXTERN_C()\n#include \"stubs/php_swoole_timer_arginfo.h\"\nEND_EXTERN_C()\n\nusing swoole::Timer;\nusing swoole::TimerNode;\nusing zend::Function;\n\nzend_class_entry *swoole_timer_ce;\nstatic zend_object_handlers swoole_timer_handlers;\n\nstatic zend_class_entry *swoole_timer_iterator_ce;\n\nSW_EXTERN_C_BEGIN\nstatic PHP_FUNCTION(swoole_timer_after);\nstatic PHP_FUNCTION(swoole_timer_tick);\nstatic PHP_FUNCTION(swoole_timer_exists);\nstatic PHP_FUNCTION(swoole_timer_info);\nstatic PHP_FUNCTION(swoole_timer_stats);\nstatic PHP_FUNCTION(swoole_timer_list);\nstatic PHP_FUNCTION(swoole_timer_clear);\nstatic PHP_FUNCTION(swoole_timer_clear_all);\nSW_EXTERN_C_END\n\n// clang-format off\nstatic const zend_function_entry swoole_timer_methods[] =\n{\n    ZEND_FENTRY(tick,     ZEND_FN(swoole_timer_tick),      arginfo_swoole_timer_tick,      ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(after,    ZEND_FN(swoole_timer_after),     arginfo_swoole_timer_after,     ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(exists,   ZEND_FN(swoole_timer_exists),    arginfo_swoole_timer_exists,    ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(info,     ZEND_FN(swoole_timer_info),      arginfo_swoole_timer_info,      ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(stats,    ZEND_FN(swoole_timer_stats),     arginfo_swoole_timer_stats,     ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(list,     ZEND_FN(swoole_timer_list),      arginfo_swoole_timer_list,      ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(clear,    ZEND_FN(swoole_timer_clear),     arginfo_swoole_timer_clear,     ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    ZEND_FENTRY(clearAll, ZEND_FN(swoole_timer_clear_all), arginfo_swoole_timer_clear_all, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid php_swoole_timer_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY(swoole_timer, \"Swoole\\\\Timer\", nullptr, swoole_timer_methods);\n    SW_SET_CLASS_CREATE(swoole_timer, sw_zend_create_object_deny);\n\n    SW_INIT_CLASS_ENTRY_BASE(swoole_timer_iterator, \"Swoole\\\\Timer\\\\Iterator\", nullptr, nullptr, spl_ce_ArrayIterator);\n\n    SW_FUNCTION_ALIAS(\n        &swoole_timer_ce->function_table, \"after\", CG(function_table), \"swoole_timer_after\", arginfo_swoole_timer_tick);\n    SW_FUNCTION_ALIAS(\n        &swoole_timer_ce->function_table, \"tick\", CG(function_table), \"swoole_timer_tick\", arginfo_swoole_timer_after);\n    SW_FUNCTION_ALIAS(\n        &swoole_timer_ce->function_table, \"info\", CG(function_table), \"swoole_timer_info\", arginfo_swoole_timer_info);\n    SW_FUNCTION_ALIAS(\n        &swoole_timer_ce->function_table, \"list\", CG(function_table), \"swoole_timer_list\", arginfo_swoole_timer_list);\n\n    SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table,\n                      \"exists\",\n                      CG(function_table),\n                      \"swoole_timer_exists\",\n                      arginfo_swoole_timer_exists);\n    SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table,\n                      \"stats\",\n                      CG(function_table),\n                      \"swoole_timer_stats\",\n                      arginfo_swoole_timer_stats);\n    SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table,\n                      \"clear\",\n                      CG(function_table),\n                      \"swoole_timer_clear\",\n                      arginfo_swoole_timer_clear);\n    SW_FUNCTION_ALIAS(&swoole_timer_ce->function_table,\n                      \"clearAll\",\n                      CG(function_table),\n                      \"swoole_timer_clear_all\",\n                      arginfo_swoole_timer_clear_all);\n\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TIMER_MIN_MS\", SW_TIMER_MIN_MS);\n    SW_REGISTER_DOUBLE_CONSTANT(\"SWOOLE_TIMER_MIN_SEC\", SW_TIMER_MIN_SEC);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_TIMER_MAX_MS\", SW_TIMER_MAX_MS);\n    SW_REGISTER_DOUBLE_CONSTANT(\"SWOOLE_TIMER_MAX_SEC\", SW_TIMER_MAX_SEC);\n}\n\nvoid php_swoole_timer_rshutdown() {\n    php_swoole_timer_clear_all();\n}\n\nstatic void timer_dtor(TimerNode *tnode) {\n    auto *fci = static_cast<Function *>(tnode->data);\n    sw_zend_fci_params_discard(&fci->fci);\n    sw_zend_fci_cache_discard(&fci->fci_cache);\n    efree(fci);\n}\n\nbool php_swoole_timer_clear(TimerNode *tnode) {\n    return swoole_timer_del(tnode);\n}\n\nbool php_swoole_timer_clear_all() {\n    if (UNEXPECTED(!swoole_timer_is_available())) {\n        return false;\n    }\n\n    size_t num = sw_timer()->count(), index = 0;\n    auto **list = static_cast<TimerNode **>(emalloc(num * sizeof(TimerNode *)));\n    for (auto &kv : sw_timer()->get_map()) {\n        TimerNode *tnode = kv.second;\n        if (tnode->type == TimerNode::TYPE_PHP) {\n            list[index++] = tnode;\n        }\n    }\n\n    while (index--) {\n        swoole_timer_del(list[index]);\n    }\n\n    efree(list);\n\n    return true;\n}\n\nstatic void timer_callback(Timer *timer, TimerNode *tnode) {\n    auto *fci = static_cast<Function *>(tnode->data);\n\n    if (UNEXPECTED(!fci->call(nullptr, php_swoole_is_enable_coroutine()))) {\n        php_swoole_error(E_WARNING, \"%s->onTimeout handler error\", ZSTR_VAL(swoole_timer_ce->name));\n    }\n}\n\nstatic bool timer_if_use_reactor() {\n    auto server = sw_server();\n    if (server) {\n        return server->is_user_worker() || (server->is_task_worker() && server->task_enable_coroutine);\n    }\n    auto process_pool = sw_process_pool();\n    if (process_pool) {\n        return !process_pool->is_master();\n    }\n    return true;\n}\n\nstatic void timer_add(INTERNAL_FUNCTION_PARAMETERS, bool persistent) {\n    zend_long ms;\n    auto *fci = static_cast<Function *>(ecalloc(1, sizeof(Function)));\n    TimerNode *tnode;\n\n    ZEND_PARSE_PARAMETERS_START(2, -1)\n    Z_PARAM_LONG(ms)\n    Z_PARAM_FUNC(fci->fci, fci->fci_cache)\n    Z_PARAM_VARIADIC('*', fci->fci.params, fci->fci.param_count)\n    ZEND_PARSE_PARAMETERS_END_EX(goto _failed);\n\n    if (UNEXPECTED(ms < SW_TIMER_MIN_MS)) {\n        php_swoole_fatal_error(E_WARNING, \"Timer must be greater than or equal to \" ZEND_TOSTR(SW_TIMER_MIN_MS));\n    _failed:\n        efree(fci);\n        RETURN_FALSE;\n    }\n\n    if (UNEXPECTED(!sw_reactor() && timer_if_use_reactor())) {\n        php_swoole_check_reactor();\n    }\n\n    /**\n     * In certain systems, such as macOS, zend_long is the long long type,\n     * and it must be explicitly converted to long.\n     */\n    tnode = swoole_timer_add((long) ms, persistent, timer_callback, fci);\n    if (UNEXPECTED(!tnode)) {\n        php_swoole_fatal_error(E_WARNING, \"add timer failed\");\n        goto _failed;\n    }\n    tnode->type = TimerNode::TYPE_PHP;\n    tnode->destructor = timer_dtor;\n    if (persistent) {\n        if (fci->fci.param_count > 0) {\n            auto params = static_cast<zval *>(ecalloc(fci->fci.param_count + 1, sizeof(zval)));\n            for (uint32_t i = 0; i < fci->fci.param_count; i++) {\n                ZVAL_COPY(&params[i + 1], &fci->fci.params[i]);\n            }\n            fci->fci.params = params;\n        } else {\n            fci->fci.params = static_cast<zval *>(emalloc(sizeof(zval)));\n        }\n        fci->fci.param_count += 1;\n        ZVAL_LONG(fci->fci.params, tnode->id);\n    } else {\n        sw_zend_fci_params_persist(&fci->fci);\n    }\n    sw_zend_fci_cache_persist(&fci->fci_cache);\n    RETURN_LONG(tnode->id);\n}\n\nstatic PHP_FUNCTION(swoole_timer_after) {\n    timer_add(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);\n}\n\nstatic PHP_FUNCTION(swoole_timer_tick) {\n    timer_add(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);\n}\n\nstatic PHP_FUNCTION(swoole_timer_exists) {\n    if (UNEXPECTED(!swoole_timer_is_available())) {\n        RETURN_FALSE;\n    }\n    zend_long id;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(id)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    auto *tnode = swoole_timer_get(id);\n    RETURN_BOOL(tnode && !tnode->removed);\n}\n\nstatic PHP_FUNCTION(swoole_timer_info) {\n    if (UNEXPECTED(!swoole_timer_is_available())) {\n        RETURN_FALSE;\n    }\n\n    zend_long id;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(id)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    auto *tnode = swoole_timer_get(id);\n    if (UNEXPECTED(!tnode)) {\n        RETURN_NULL();\n    }\n    array_init(return_value);\n    add_assoc_long(return_value, \"exec_msec\", tnode->exec_msec);\n    add_assoc_long(return_value, \"exec_count\", tnode->exec_count);\n    add_assoc_long(return_value, \"interval\", tnode->interval);\n    add_assoc_long(return_value, \"round\", tnode->round);\n    add_assoc_bool(return_value, \"removed\", tnode->removed);\n}\n\nstatic PHP_FUNCTION(swoole_timer_stats) {\n    array_init(return_value);\n    if (swoole_timer_is_available()) {\n        add_assoc_bool(return_value, \"initialized\", 1);\n        add_assoc_long(return_value, \"num\", SwooleTG.timer->count());\n        add_assoc_long(return_value, \"round\", SwooleTG.timer->get_round());\n    } else {\n        add_assoc_bool(return_value, \"initialized\", 0);\n        add_assoc_long(return_value, \"num\", 0);\n        add_assoc_long(return_value, \"round\", 0);\n    }\n}\n\nstatic PHP_FUNCTION(swoole_timer_list) {\n    zval zlist;\n    array_init(&zlist);\n    if (EXPECTED(SwooleTG.timer)) {\n        for (auto &kv : SwooleTG.timer->get_map()) {\n            TimerNode *tnode = kv.second;\n            if (tnode->type == TimerNode::TYPE_PHP) {\n                add_next_index_long(&zlist, tnode->id);\n            }\n        }\n    }\n    object_init_ex(return_value, swoole_timer_iterator_ce);\n    sw_zend_call_method_with_1_params(\n        return_value, swoole_timer_iterator_ce, &swoole_timer_iterator_ce->constructor, \"__construct\", nullptr, &zlist);\n    zval_ptr_dtor(&zlist);\n}\n\nstatic PHP_FUNCTION(swoole_timer_clear) {\n    if (UNEXPECTED(!SwooleTG.timer)) {\n        RETURN_FALSE;\n    } else {\n        zend_long id;\n\n        ZEND_PARSE_PARAMETERS_START(1, 1)\n        Z_PARAM_LONG(id)\n        ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n        TimerNode *tnode = swoole_timer_get(id);\n        if (!tnode || tnode->type != TimerNode::TYPE_PHP) {\n            RETURN_FALSE;\n        }\n        RETURN_BOOL(swoole_timer_del(tnode));\n    }\n}\n\nstatic PHP_FUNCTION(swoole_timer_clear_all) {\n    RETURN_BOOL(php_swoole_timer_clear_all());\n}\n"
  },
  {
    "path": "ext-src/swoole_tracer.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author:   Tianfeng Han  <mikan.tenny@gmail.com>                      |\n  +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_cxx.h\"\n#include \"php_swoole_api.h\"\n#include \"php_swoole_coroutine.h\"\n\n#include \"swoole_thread.h\"\n\n#include <unordered_map>\n#include <unordered_set>\n#include <sstream>\n#include <iomanip>\n#include <iostream>\n#include <fstream>\n#include <array>\n\nusing swoole::Coroutine;\nusing swoole::PHPContext;\nusing swoole::PHPCoroutine;\n\nBEGIN_EXTERN_C()\n#include \"zend_builtin_functions.h\"\n#include \"zend_observer.h\"\n#include \"ext/standard/php_math.h\"\n#include \"ext/json/php_json.h\"\nEND_EXTERN_C()\n\nstruct Prof {\n    std::string fname;\n    std::string file_pos;\n    long began_at;\n    long cid;\n};\n\nstruct ProfContext {\n    std::array<Prof, 128> profs;\n    uint8_t call_stack_level;\n};\n\nstruct AllocPoint {\n    size_t size;\n    size_t count;\n    std::string at;\n};\n\nstruct AllocStat {\n    size_t total_bytes;\n    size_t count;\n};\n\nstruct BlockingDetectionSpan {\n    zend_long began_at;\n    size_t switch_count;\n    PHPContext::SwapCallback swap_callback;\n};\n\nstatic SW_THREAD_LOCAL struct {\n    std::unordered_map<void *, AllocPoint> points;\n    std::unordered_set<void *> debug_points;\n    std::unordered_map<std::string, zval> backtraces;\n    std::unordered_map<std::string, uint32_t> counters;\n    size_t loop;\n    zend_long threshold;\n    bool profiling;\n    std::unordered_map<long, ProfContext> co_prof;\n    ProfContext main_co_prof;\n    std::string prof_root_path;\n    zval prof_events;\n    size_t fcall_count;\n    size_t return_count;\n    pid_t pid;\n} TracerG;\n\n#define DEBUG 0\n#define DEBUG_LINE 16\n\nconstexpr int blocking_detection_func_reserve_index = 4;\n\n#if PHP_VERSION_ID < 80400\n#define MM_LINE_DC\n#define MM_LINE_ORIG_DC\n#define MM_LINE_CC\n#define MM_LINE_ORIG_RELAY_CC\n#else\n#define MM_LINE_DC ZEND_FILE_LINE_DC\n#define MM_LINE_ORIG_DC ZEND_FILE_LINE_ORIG_DC\n#define MM_LINE_CC ZEND_FILE_LINE_RELAY_CC\n#define MM_LINE_ORIG_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC\n#endif\n\nstatic void *(*ori_malloc)(size_t MM_LINE_DC MM_LINE_ORIG_DC);\nstatic void (*ori_free)(void *MM_LINE_DC MM_LINE_ORIG_DC);\nstatic void *(*ori_realloc)(void *, size_t MM_LINE_DC MM_LINE_ORIG_DC);\n\nstatic void *tracer_malloc(size_t size MM_LINE_DC MM_LINE_ORIG_DC);\nstatic void tracer_free(void *ptr MM_LINE_DC MM_LINE_ORIG_DC);\nstatic void *tracer_realloc(void *ptr, size_t size MM_LINE_DC MM_LINE_ORIG_DC);\n\nstatic void hook_emalloc();\nstatic void unhook_emalloc();\n\nstatic long tracer_get_time_us() {\n    return swoole::time<std::chrono::microseconds>(true);\n}\n\nstatic std::string zstr_to_std_string(zend_string *zs) {\n    return std::string(ZSTR_VAL(zs), ZSTR_LEN(zs));\n}\n\nstatic bool str_starts_with(const std::string &str, const std::string &prefix) {\n    return str.size() >= prefix.size() && str.compare(0, prefix.size(), prefix) == 0;\n}\n\nstatic bool str_ends_with(const std::string &str, const std::string &suffix) {\n    return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;\n}\n\nstatic void debug(const char *label, void *ptr, size_t size, uint32_t lineno) {\n#if DEBUG\n    auto iter = TrackerG.points.find(ptr);\n    if (iter != TrackerG.points.end() && str_ends_with(iter->second.at, \":\" + std::to_string(lineno))) {\n        printf(\"[%s]\\tptr=%p, size=%lu, count=%lu, lineno=%u, at=%s\\n\",\n               label,\n               ptr,\n               size,\n               iter->second.count,\n               lineno,\n               iter->second.at.c_str());\n        if (strcmp(label, \"update\")) {\n            TrackerG.debug_points.insert(ptr);\n        }\n    }\n\n    if (strcmp(label, \"free\") && TrackerG.debug_points.find(ptr) != TrackerG.debug_points.end()) {\n        TrackerG.debug_points.erase(ptr);\n        printf(\"free ptr=%p\\n\", ptr);\n    }\n#endif\n}\n\nstatic uint32_t tracer_get_executed_lineno(void) {\n    zend_execute_data *ex = EG(current_execute_data);\n    while (ex && (!ex->func || !ZEND_USER_CODE(ex->func->type))) {\n        ex = ex->prev_execute_data;\n    }\n\n    if (ex && ex->opline) {\n        if (EG(exception) && ex->opline->opcode == ZEND_HANDLE_EXCEPTION && ex->opline->lineno == 0 &&\n            EG(opline_before_exception)) {\n            return EG(opline_before_exception)->lineno;\n        }\n        return ex->opline->lineno;\n    } else {\n        return 0;\n    }\n}\n\nstatic zend_string *tracer_get_file_and_line() {\n    const char *file = zend_get_executed_filename();\n    uint32_t lineno = tracer_get_executed_lineno();\n    char file_line_buf[1024];\n    if (lineno == 0 && strcmp(file, \"[no active file]\") == 0) {\n        return nullptr;\n    }\n    size_t file_len = strlen(file);\n    if (UNEXPECTED(file_len + 100 > sizeof(file_line_buf))) {\n        char *tmp_buf = (char *) malloc(file_len + 100);\n        if (!tmp_buf) {\n            php_printf(\"tracker out of memory\\n\");\n            zend_bailout();\n        }\n        sprintf(tmp_buf, \"%s:%u\", file, lineno);\n        zend_string *ret = zend_string_init(tmp_buf, strlen(tmp_buf), 1);\n        free(tmp_buf);\n        return ret;\n    } else {\n        sprintf(file_line_buf, \"%s:%u\", file, lineno);\n        return zend_string_init(file_line_buf, strlen(file_line_buf), 1);\n    }\n}\n\nstatic zend_string *tracer_get_backtrace() {\n    zval backtrace;\n    zend_fetch_debug_backtrace(&backtrace, 0, 0, 0);\n    auto backtrace_str = zend_trace_to_string(Z_ARRVAL(backtrace), false);\n    zval_ptr_dtor(&backtrace);\n    return backtrace_str;\n}\n\nstatic long tracer_get_pid() {\n    return swoole_thread_get_native_id();\n}\n\nstatic long tracer_get_tid() {\n    return swoole_coroutine_get_id();\n}\n\nstatic ProfContext &tracer_get_ctx(long tid) {\n    if (tid == TracerG.pid) {\n        return TracerG.main_co_prof;\n    } else {\n        if (TracerG.co_prof.find(tid) == TracerG.co_prof.end()) {\n            TracerG.co_prof[tid] = {};\n        }\n        return TracerG.co_prof[tid];\n    }\n}\n\nstatic zend_string *tracer_format_number(long num) {\n#if PHP_VERSION_ID >= 80300\n    return _php_math_number_format_long(num, 0, \".\", 1, \",\", 1);\n#else\n    return _php_math_number_format((double) num, 0, '.', ',');\n#endif\n}\n\nstatic std::string format_bytes(uint64_t size) {\n    const char *units[] = {\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"};\n    int unitIndex = 0;\n    double adjustedSize = static_cast<double>(size);\n\n    while (adjustedSize >= 1024.0 && unitIndex < 8) {\n        adjustedSize /= 1024.0;\n        unitIndex++;\n    }\n\n    std::ostringstream oss;\n    oss << std::fixed << std::setprecision(2) << adjustedSize << \" \" << units[unitIndex];\n    return oss.str();\n}\n\nstatic void tracer_leak_clear_stat(const std::string &at) {\n    for (auto iter = TracerG.points.begin(); iter != TracerG.points.end(); iter++) {\n        if (iter->second.at == at) {\n            iter = TracerG.points.erase(iter);\n            if (iter == TracerG.points.end()) {\n                break;\n            }\n        }\n    }\n}\n\nstatic void save_backtrace(const std::string &alloc_at) {\n    if (TracerG.backtraces.find(alloc_at) == TracerG.backtraces.end()) {\n        zval bt;\n        zend_fetch_debug_backtrace(&bt, 0, DEBUG_BACKTRACE_IGNORE_ARGS, 0);\n        TracerG.backtraces[alloc_at] = bt;\n    }\n}\n\nstatic void add_point(void *ptr, size_t size, zend_string *current_file_lineno) {\n    std::string alloc_at = zstr_to_std_string(current_file_lineno);\n    TracerG.points[ptr] = {\n        size,\n        1,\n        alloc_at,\n    };\n\n    auto iter = TracerG.counters.find(alloc_at);\n    if (iter == TracerG.counters.end()) {\n        TracerG.counters[alloc_at] = 1;\n    } else {\n        iter->second++;\n        if (iter->second >= TracerG.threshold - 1) {\n            save_backtrace(alloc_at);\n        }\n    }\n\n    debug(\"new\", ptr, size, DEBUG_LINE);\n}\n\nstatic void update_point(AllocPoint &point, void *new_ptr, size_t new_size) {\n    TracerG.points[new_ptr] = {\n        new_size,\n        point.count + 1,\n        point.at,\n    };\n\n    auto counter_iter = TracerG.counters.find(point.at);\n    if (counter_iter != TracerG.counters.end()) {\n        counter_iter->second++;\n        if (counter_iter->second >= TracerG.threshold - 1) {\n            save_backtrace(point.at);\n        }\n    }\n\n    debug(\"update\", new_ptr, new_size, DEBUG_LINE);\n}\n\nstatic void del_point(void *ptr, decltype(TracerG.points)::iterator &iter) {\n    debug(\"free\", ptr, 0, DEBUG_LINE);\n    const auto &alloc_at = iter->second.at;\n    auto counter_iter = TracerG.counters.find(alloc_at);\n    if (counter_iter != TracerG.counters.end()) {\n        counter_iter->second--;\n    }\n    TracerG.points.erase(iter);\n}\n\nstatic void *tracer_malloc(size_t size MM_LINE_DC MM_LINE_ORIG_DC) {\n    void *ptr;\n    if (ori_malloc) {\n        ptr = ori_malloc(size MM_LINE_CC MM_LINE_ORIG_RELAY_CC);\n    } else {\n        zend_mm_heap *heap = zend_mm_get_heap();\n        ptr = zend_mm_alloc(heap, size);\n    }\n\n    unhook_emalloc();\n\n    zend_string *current_file_lineno = tracer_get_file_and_line();\n    if (current_file_lineno) {\n        add_point(ptr, size, current_file_lineno);\n        zend_string_release(current_file_lineno);\n    }\n\n    hook_emalloc();\n\n    return ptr;\n}\n\nstatic void *tracer_realloc(void *ptr, size_t size MM_LINE_DC MM_LINE_ORIG_DC) {\n    void *new_ptr;\n\n    if (ori_realloc) {\n        new_ptr = ori_realloc(ptr, size MM_LINE_CC MM_LINE_ORIG_RELAY_CC);\n    } else {\n        zend_mm_heap *heap = zend_mm_get_heap();\n        new_ptr = zend_mm_realloc(heap, ptr, size);\n    }\n\n    unhook_emalloc();\n\n    zend_string *current_file_lineno = tracer_get_file_and_line();\n    if (current_file_lineno) {\n        auto iter = TracerG.points.find(ptr);\n        if (iter != TracerG.points.end()) {\n            update_point(iter->second, new_ptr, size);\n            if (new_ptr != ptr) {\n                TracerG.points.erase(iter);\n            }\n        } else {\n            add_point(new_ptr, size, current_file_lineno);\n        }\n        zend_string_release(current_file_lineno);\n    }\n\n    hook_emalloc();\n\n    return new_ptr;\n}\n\nstatic void tracer_free(void *ptr MM_LINE_DC MM_LINE_ORIG_DC) {\n    if (ori_free) {\n        ori_free(ptr MM_LINE_CC MM_LINE_ORIG_RELAY_CC);\n    } else {\n        zend_mm_heap *heap = zend_mm_get_heap();\n        zend_mm_free(heap, ptr);\n    }\n\n    auto iter = TracerG.points.find(ptr);\n    if (iter != TracerG.points.end()) {\n        del_point(ptr, iter);\n    }\n}\n\nstatic void hook_emalloc() {\n    zend_mm_heap *heap = zend_mm_get_heap();\n    zend_mm_get_custom_handlers(heap, &ori_malloc, &ori_free, &ori_realloc);\n    zend_mm_set_custom_handlers(heap, &tracer_malloc, &tracer_free, &tracer_realloc);\n}\n\nstatic void unhook_emalloc() {\n    zend_mm_heap *heap = zend_mm_get_heap();\n    if (ori_malloc || ori_free || ori_realloc) {\n        zend_mm_set_custom_handlers(heap, ori_malloc, ori_free, ori_realloc);\n        ori_malloc = NULL;\n        ori_free = NULL;\n        ori_realloc = NULL;\n    } else {\n        *((int *) heap) = 0;\n    }\n}\n\nstatic void profiling_begin(zend_string *root_symbol, zend_execute_data *execute_data) {\n    zend_function *fbc = execute_data->func;\n    auto type = fbc->type;\n\n    if (!TracerG.profiling || type == ZEND_INTERNAL_FUNCTION) {\n        return;\n    }\n\n    auto ts = tracer_get_time_us();\n    zend_string *fn_name = fbc->common.function_name;\n    std::string fn;\n    std::string file_pos;\n\n    if (fbc->common.scope) {\n        zend_string *class_name = fbc->common.scope->name;\n        fn = std::string(ZSTR_VAL(class_name)) + \"::\" + std::string(ZSTR_VAL(fn_name));\n    } else {\n        fn = std::string(ZSTR_VAL(fn_name));\n    }\n\n#if DEBUG\n    printf(\"fn=%s, level=%d \\n\", fn.c_str(), TracerG.call_stack_level);\n#endif\n\n    zend_string *current_file_lineno = tracer_get_file_and_line();\n    if (current_file_lineno) {\n        file_pos = zstr_to_std_string(current_file_lineno);\n        if (str_starts_with(file_pos, TracerG.prof_root_path)) {\n            file_pos =\n                file_pos.substr(TracerG.prof_root_path.length(), file_pos.length() - TracerG.prof_root_path.length());\n        }\n        zend_string_release(current_file_lineno);\n    }\n\n    auto tid = tracer_get_tid();\n\n    Prof prof{\n        fn,\n        file_pos,\n        ts,\n        tid,\n    };\n\n    auto &ctx = tracer_get_ctx(tid);\n    ctx.profs[ctx.call_stack_level++] = prof;\n}\n\nstatic void profiling_clear() {\n    zval_ptr_dtor(&TracerG.prof_events);\n    ZVAL_NULL(&TracerG.prof_events);\n    TracerG.main_co_prof = {};\n    TracerG.co_prof.clear();\n    TracerG.profiling = false;\n}\n\nstatic void profiling_end() {\n    auto tid = tracer_get_tid();\n    auto &ctx = tracer_get_ctx(tid);\n\n    assert(ctx.call_stack_level > 0);\n\n    ctx.call_stack_level--;\n\n    auto &prof = ctx.profs[ctx.call_stack_level];\n\n#if DEBUG\n    printf(\"return level=%d, count=%zu, func=%p\\n\",\n           TracerG.call_stack_level,\n           array_count(&TracerG.prof_events),\n           prof.fname.c_str());\n#endif\n\n    auto te = tracer_get_time_us();\n\n    zval event;\n    array_init(&event);\n\n    std::string name;\n    if (prof.file_pos.length() > 0) {\n        name = prof.fname + \" (\" + prof.file_pos + \")\";\n    } else {\n        name = prof.fname;\n    }\n\n    add_assoc_stringl_ex(&event, ZEND_STRL(\"name\"), name.c_str(), name.length());\n    add_assoc_string_ex(&event, ZEND_STRL(\"ph\"), \"X\");\n    add_assoc_string_ex(&event, ZEND_STRL(\"cat\"), \"FEE\");\n    add_assoc_double_ex(&event, ZEND_STRL(\"ts\"), prof.began_at);\n    add_assoc_double_ex(&event, ZEND_STRL(\"dur\"), te - prof.began_at);\n    add_assoc_long_ex(&event, ZEND_STRL(\"pid\"), tracer_get_pid());\n    add_assoc_long_ex(&event, ZEND_STRL(\"tid\"), tid);\n\n    add_next_index_zval(&TracerG.prof_events, &event);\n\n#if DEBUG\n    printf(\"fn=%s, begun_at=%f, dr=%f, at=%s\\n\",\n           prof.fname.c_str(),\n           prof.began_at,\n           te - prof.began_at,\n           prof.file_pos.c_str());\n#endif\n}\n\nstatic void blocking_detection_begin(zend_execute_data *execute_data) {\n    auto ctx = (PHPContext *) swoole::Coroutine::get_current_task();\n    if (!ctx) {\n        return;\n    }\n\n    auto span = new BlockingDetectionSpan;\n    span->began_at = tracer_get_time_us();\n    span->switch_count = ctx->switch_count = 0;\n    span->swap_callback = [](PHPContext *ctx) { ctx->switch_count++; };\n    ctx->on_resume = &span->swap_callback;\n    ctx->on_yield = &span->swap_callback;\n    execute_data->func->internal_function.reserved[blocking_detection_func_reserve_index] = span;\n}\n\nstatic void blocking_detection_end(zend_execute_data *execute_data) {\n    PHPContext *ctx = PHPCoroutine::get_context();\n    if (!ctx) {\n        return;\n    }\n\n    ctx->on_resume = nullptr;\n    ctx->on_yield = nullptr;\n\n    auto fn = &execute_data->func->internal_function;\n    auto span = (BlockingDetectionSpan *) fn->reserved[blocking_detection_func_reserve_index];\n    if (!span) {\n        return;\n    }\n    fn->reserved[blocking_detection_func_reserve_index] = nullptr;\n\n    auto now = tracer_get_time_us();\n    auto duration = now - span->began_at;\n    if (span->switch_count == ctx->switch_count && duration > SWOOLE_G(blocking_threshold)) {\n        auto duration_str = tracer_format_number(duration);\n        auto backtrace_str = tracer_get_backtrace();\n\n        const char *scope = nullptr;\n        if (execute_data->func->common.scope) {\n            scope = ZSTR_VAL(execute_data->func->common.scope->name);\n        }\n\n        sw_printf(\" >>> [Detected blocking I/O in Coroutine#%ld, internal function `%s%s%s()` blocked for %s us]\\n%s\",\n                  PHPCoroutine::get_cid(),\n                  scope ? scope : \"\",\n                  scope ? \"::\" : \"\",\n                  fn->function_name->val,\n                  duration_str->val,\n                  backtrace_str->val);\n\n        zend_string_release(duration_str);\n        zend_string_release(backtrace_str);\n    }\n\n    swoole_event_defer(\n        [](void *ptr) {\n            auto span = (BlockingDetectionSpan *) ptr;\n            delete span;\n        },\n        span);\n}\n\nstatic bool tracer_observer_if_enable_profile(zend_execute_data *execute_data) {\n    return SWOOLE_G(profile) && TracerG.profiling && execute_data->func->type != ZEND_INTERNAL_FUNCTION;\n}\n\nstatic bool tracer_observer_if_enable_blocking_detection(zend_execute_data *execute_data) {\n    return SWOOLE_G(blocking_detection) && execute_data->func->type == ZEND_INTERNAL_FUNCTION;\n}\n\nstatic void tracer_observer_begin(zend_execute_data *execute_data) {\n    if (tracer_observer_if_enable_profile(execute_data)) {\n        profiling_begin(NULL, execute_data);\n    }\n\n    if (tracer_observer_if_enable_blocking_detection(execute_data)) {\n        blocking_detection_begin(execute_data);\n    }\n}\n\nstatic void tracer_observer_end(zend_execute_data *execute_data, zval *return_value) {\n    if (tracer_observer_if_enable_profile(execute_data)) {\n        profiling_end();\n    }\n\n    if (tracer_observer_if_enable_blocking_detection(execute_data)) {\n        blocking_detection_end(execute_data);\n    }\n}\n\nstatic zend_observer_fcall_handlers tracer_observer(zend_execute_data *execute_data) {\n    zend_observer_fcall_handlers empty_handlers = {nullptr, nullptr};\n\n    if (!execute_data->func || !execute_data->func->common.function_name) {\n        return empty_handlers;\n    }\n\n    if (!tracer_observer_if_enable_profile(execute_data) &&\n        !tracer_observer_if_enable_blocking_detection(execute_data)) {\n        return empty_handlers;\n    }\n\n    return {tracer_observer_begin, tracer_observer_end};\n}\n\nPHP_FUNCTION(swoole_tracer_leak_detect) {\n    if (!SWOOLE_G(leak_detection)) {\n        return;\n    }\n\n    zend_long threshold = 64;\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(threshold)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    std::unordered_map<std::string, AllocStat> stats;\n    unhook_emalloc();\n    for (auto &point : TracerG.points) {\n        auto iter = stats.find(point.second.at);\n        if (iter == stats.end()) {\n            stats[point.second.at] = {\n                point.second.size,\n                point.second.count,\n            };\n        } else {\n            iter->second.count += point.second.count;\n            iter->second.total_bytes += point.second.size;\n        }\n    }\n\n    for (auto &stat : stats) {\n        if (stat.second.count >= (size_t) threshold) {\n            php_printf(\"[Round#%lu] leak %s bytes, alloc %lu times at %s\\n\",\n                       TracerG.loop,\n                       format_bytes(stat.second.total_bytes).c_str(),\n                       stat.second.count,\n                       stat.first.c_str());\n\n            auto bt_iter = TracerG.backtraces.find(stat.first);\n            if (bt_iter != TracerG.backtraces.end()) {\n                zend_string *str = zend_trace_to_string(Z_ARR(bt_iter->second), false);\n                ZEND_WRITE(ZSTR_VAL(str), ZSTR_LEN(str));\n                zend_string_release(str);\n            }\n            tracer_leak_clear_stat(stat.first);\n            php_printf(\"\\n\");\n        }\n    }\n\n    TracerG.threshold = threshold;\n    TracerG.loop++;\n    hook_emalloc();\n}\n\nPHP_FUNCTION(swoole_tracer_prof_begin) {\n    if (!SWOOLE_G(profile) || TracerG.profiling) {\n        RETURN_FALSE;\n    }\n\n    array_init(&TracerG.prof_events);\n    TracerG.profiling = true;\n\n    zval *options = NULL; /* optional array arg: for future use */\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_ARRAY(options)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (options) {\n        zval *pzval = zend_hash_str_find(Z_ARRVAL_P(options), ZEND_STRL(\"root_path\"));\n        if (pzval) {\n            auto tmp = zval_get_string(pzval);\n            TracerG.prof_root_path = zstr_to_std_string(tmp);\n            if (TracerG.prof_root_path.at(TracerG.prof_root_path.length() - 1) != '/') {\n                TracerG.prof_root_path.append(\"/\");\n            }\n            zend_string_release(tmp);\n        }\n    }\n\n    TracerG.pid = getpid();\n\n    RETURN_TRUE;\n}\n\nPHP_FUNCTION(swoole_tracer_prof_end) {\n    if (!SWOOLE_G(profile) || !TracerG.profiling) {\n        RETURN_FALSE;\n    }\n\n    zend_string *file;\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1)\n    Z_PARAM_STR(file)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zval json;\n    array_init(&json);\n\n    zend::array_set(&json, ZEND_STRL(\"traceEvents\"), &TracerG.prof_events);\n\n    zval metadata;\n    array_init(&metadata);\n\n    zend::array_set(&metadata, ZEND_STRL(\"version\"), \"0.17.1\");\n    zend::array_set(&metadata, ZEND_STRL(\"overflow\"), false);\n    zend::array_set(&json, ZEND_STRL(\"viztracer_metadata\"), &metadata);\n\n    smart_str buf = {};\n    if (php_json_encode(&buf, &json, 0) == FAILURE) {\n    _fail:\n        zval_ptr_dtor(&TracerG.prof_events);\n        RETURN_FALSE;\n    }\n\n    std::ofstream outputFile(ZSTR_VAL(file), std::ios::binary);\n    if (!outputFile.is_open()) {\n        goto _fail;\n    }\n\n    outputFile.write(buf.s->val, buf.s->len);\n    outputFile.close();\n\n    zval_ptr_dtor(&metadata);\n    zval_ptr_dtor(&json);\n\n    smart_str_free(&buf);\n    profiling_clear();\n\n    RETURN_TRUE;\n}\n\nvoid php_swoole_tracer_minit(int module_number) {\n    if (SWOOLE_G(blocking_detection) || SWOOLE_G(profile)) {\n        zend_observer_fcall_register(tracer_observer);\n        SWOOLE_G(enable_fiber_mock) = true;\n    }\n}\n\nvoid php_swoole_tracer_rinit() {\n    if (SWOOLE_G(leak_detection)) {\n        hook_emalloc();\n    }\n}\n\nvoid php_swoole_tracer_rshutdown() {\n    if (SWOOLE_G(leak_detection)) {\n        unhook_emalloc();\n    }\n\n    for (auto &iter : TracerG.backtraces) {\n        zval_ptr_dtor(&iter.second);\n    }\n\n    profiling_clear();\n}\n"
  },
  {
    "path": "ext-src/swoole_websocket_server.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_http_server.h\"\n#include \"php_swoole_websocket.h\"\n\nSW_EXTERN_C_BEGIN\n#include \"ext/standard/sha1.h\"\n#include \"stubs/php_swoole_websocket_arginfo.h\"\nSW_EXTERN_C_END\n\n#include \"swoole_base64.h\"\n\nusing swoole::Connection;\nusing swoole::ListenPort;\nusing swoole::make_string;\nusing swoole::RecvData;\nusing swoole::Server;\nusing swoole::SessionId;\nusing swoole::String;\nusing swoole::WebSocketSettings;\nusing swoole::websocket::Frame;\nusing swoole::websocket::FrameObject;\n\nusing HttpContext = swoole::http::Context;\n\nnamespace WebSocket = swoole::websocket;\n\nzend_class_entry *swoole_websocket_server_ce;\nstatic zend_object_handlers swoole_websocket_server_handlers;\n\nzend_class_entry *swoole_websocket_frame_ce;\nstatic zend_object_handlers swoole_websocket_frame_handlers;\n\nstatic zend_class_entry *swoole_websocket_closeframe_ce;\nstatic zend_object_handlers swoole_websocket_closeframe_handlers;\n\nSW_EXTERN_C_BEGIN\nstatic PHP_METHOD(swoole_websocket_server, push);\nstatic PHP_METHOD(swoole_websocket_server, isEstablished);\nstatic PHP_METHOD(swoole_websocket_server, pack);\nstatic PHP_METHOD(swoole_websocket_server, unpack);\nstatic PHP_METHOD(swoole_websocket_server, disconnect);\nstatic PHP_METHOD(swoole_websocket_server, ping);\n\nstatic PHP_METHOD(swoole_websocket_frame, __toString);\nSW_EXTERN_C_END\n\n// clang-format off\nconst zend_function_entry swoole_websocket_server_methods[] =\n{\n    PHP_ME(swoole_websocket_server, push,          arginfo_class_Swoole_WebSocket_Server_push,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_websocket_server, disconnect,    arginfo_class_Swoole_WebSocket_Server_disconnect,    ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_websocket_server, ping,          arginfo_class_Swoole_WebSocket_Server_ping,          ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_websocket_server, isEstablished, arginfo_class_Swoole_WebSocket_Server_isEstablished, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_websocket_server, pack,          arginfo_class_Swoole_WebSocket_Server_pack,          ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_websocket_server, unpack,        arginfo_class_Swoole_WebSocket_Server_unpack,        ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_FE_END\n};\n\nstatic constexpr zend_function_entry swoole_websocket_frame_methods[] =\n{\n    PHP_ME(swoole_websocket_frame, __toString, arginfo_class_Swoole_WebSocket_Frame___toString, ZEND_ACC_PUBLIC)\n    PHP_ME(swoole_websocket_server, pack,      arginfo_class_Swoole_WebSocket_Frame_pack,       ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_ME(swoole_websocket_server, unpack,    arginfo_class_Swoole_WebSocket_Frame_unpack,     ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)\n    PHP_FE_END\n};\n// clang-format on\n\nvoid WebSocket::construct_frame(zval *zframe, zend_long opcode, zval *zpayload, uint8_t flags) {\n    if (opcode == OPCODE_CLOSE) {\n        const char *payload = Z_STRVAL_P(zpayload);\n        size_t payload_length = Z_STRLEN_P(zpayload);\n        object_init_ex(zframe, swoole_websocket_closeframe_ce);\n        if (payload_length >= SW_WEBSOCKET_CLOSE_CODE_LEN) {\n            // WebSocket Close code\n            zend_update_property_long(swoole_websocket_closeframe_ce,\n                                      SW_Z8_OBJ_P(zframe),\n                                      ZEND_STRL(\"code\"),\n                                      (payload[0] << 8) ^ (payload[1] & 0xFF));\n            if (payload_length > SW_WEBSOCKET_CLOSE_CODE_LEN) {\n                // WebSocket Close reason message\n                zend_update_property_stringl(swoole_websocket_closeframe_ce,\n                                             SW_Z8_OBJ_P(zframe),\n                                             ZEND_STRL(\"reason\"),\n                                             payload + SW_WEBSOCKET_CLOSE_CODE_LEN,\n                                             payload_length - SW_WEBSOCKET_CLOSE_CODE_LEN);\n            }\n        }\n    } else {\n        object_init_ex(zframe, swoole_websocket_frame_ce);\n        zend_update_property(swoole_websocket_frame_ce, SW_Z8_OBJ_P(zframe), ZEND_STRL(\"data\"), zpayload);\n    }\n    if (flags & FLAG_RSV1) {\n        flags |= FLAG_COMPRESS;\n    }\n    zend_update_property_long(swoole_websocket_frame_ce, SW_Z8_OBJ_P(zframe), ZEND_STRL(\"opcode\"), opcode);\n    zend_update_property_long(swoole_websocket_frame_ce, SW_Z8_OBJ_P(zframe), ZEND_STRL(\"flags\"), flags);\n    zend_update_property_bool(swoole_websocket_frame_ce, SW_Z8_OBJ_P(zframe), ZEND_STRL(\"finish\"), flags & FLAG_FIN);\n}\n\nbool FrameObject::uncompress(zval *zpayload, const char *data, size_t length) {\n#ifndef SW_HAVE_ZLIB\n    swoole_warning(\"The compressed websocket data frame is received, the `zlib` supports is required\");\n    return false;\n#else\n    String zlib_buffer(length + SW_WEBSOCKET_DEFAULT_PAYLOAD_SIZE, sw_zend_string_allocator());\n    if (sw_likely(WebSocket::message_uncompress(&zlib_buffer, data, length))) {\n        zend::assign_zend_string_by_val(zpayload, zlib_buffer.str, zlib_buffer.length);\n        zlib_buffer.release();\n        return true;\n    } else {\n        return false;\n    }\n#endif\n}\n\nbool FrameObject::pack(String *buffer) {\n    const char *ptr = nullptr;\n    size_t len = 0;\n\n    if (sw_unlikely(opcode > SW_WEBSOCKET_OPCODE_MAX)) {\n        php_swoole_fatal_error(E_WARNING, \"the maximum value of opcode is %d\", SW_WEBSOCKET_OPCODE_MAX);\n        return false;\n    }\n\n    zend::String str_zdata;\n    if (data && !ZVAL_IS_NULL(data)) {\n        str_zdata = data;\n        ptr = str_zdata.val();\n        len = str_zdata.len();\n    }\n\n#ifndef SW_HAVE_ZLIB\n    swoole_warning(\"Unable to compress websocket data frame, the `zlib` supports is required\");\n    return false;\n#else\n    bool need_compress = ((flags & WebSocket::FLAG_COMPRESS) && len > 0);\n    if (opcode == WebSocket::OPCODE_CLOSE || opcode == WebSocket::OPCODE_PING || opcode == WebSocket::OPCODE_PONG) {\n        sw_unset_bit(flags, WebSocket::FLAG_COMPRESS | WebSocket::FLAG_RSV1);\n        sw_set_bit(flags, WebSocket::FLAG_FIN);\n        need_compress = false;\n    } else if (opcode == WebSocket::OPCODE_CONTINUATION || !(flags & WebSocket::FLAG_FIN)) {\n        // Continuous frames and WebSocket message frames without the FLAG_FIN flag do not require compression.\n        need_compress = false;\n    }\n\n    if (need_compress) {\n        String *zlib_buffer = sw_tg_buffer();\n        zlib_buffer->clear();\n        if (WebSocket::message_compress(zlib_buffer, ptr, len, Z_DEFAULT_COMPRESSION)) {\n            ptr = zlib_buffer->str;\n            len = zlib_buffer->length;\n            sw_set_bit(flags, WebSocket::FLAG_RSV1);\n        } else {\n            sw_unset_bit(flags, WebSocket::FLAG_RSV1);\n        }\n    }\n#endif\n\n    buffer->clear();\n    if (UNEXPECTED(opcode == WebSocket::OPCODE_CLOSE)) {\n        return WebSocket::pack_close_frame(buffer, code, ptr, len, flags);\n    } else {\n        return WebSocket::encode(buffer, ptr, len, opcode, flags);\n    }\n}\n\nFrameObject::FrameObject(zval *zdata, zend_long _opcode, zend_long _flags, zend_long _code) {\n    if (Z_TYPE_P(zdata) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zdata), swoole_websocket_frame_ce)) {\n        zval *ztmp = nullptr;\n        if ((ztmp = sw_zend_read_property_ex(swoole_websocket_frame_ce, zdata, SW_ZSTR_KNOWN(SW_ZEND_STR_OPCODE), 1))) {\n            opcode = zval_get_long(ztmp);\n        } else {\n            opcode = OPCODE_TEXT;\n        }\n        if (opcode == OPCODE_CLOSE) {\n            if ((ztmp = sw_zend_read_property_not_null_ex(\n                     swoole_websocket_frame_ce, zdata, SW_ZSTR_KNOWN(SW_ZEND_STR_CODE), 1))) {\n                code = zval_get_long(ztmp);\n            } else {\n                code = CLOSE_NORMAL;\n            }\n            data = sw_zend_read_property_not_null_ex(\n                swoole_websocket_frame_ce, zdata, SW_ZSTR_KNOWN(SW_ZEND_STR_REASON), 1);\n        } else {\n            data =\n                sw_zend_read_property_not_null_ex(swoole_websocket_frame_ce, zdata, SW_ZSTR_KNOWN(SW_ZEND_STR_DATA), 1);\n        }\n        if ((ztmp = sw_zend_read_property_ex(swoole_websocket_frame_ce, zdata, SW_ZSTR_KNOWN(SW_ZEND_STR_FLAGS), 1))) {\n            flags = zval_get_long(ztmp) & FLAGS_ALL;\n        } else {\n            flags = 0;\n        }\n        if ((ztmp = sw_zend_read_property_not_null_ex(\n                 swoole_websocket_frame_ce, zdata, SW_ZSTR_KNOWN(SW_ZEND_STR_FINISH), 1))) {\n            if (zval_is_true(ztmp)) {\n                sw_set_bit(flags, WebSocket::FLAG_FIN);\n            } else {\n                sw_unset_bit(flags, WebSocket::FLAG_FIN);\n            }\n        }\n    } else {\n        opcode = _opcode;\n        flags = _flags & FLAGS_ALL;\n        code = _code;\n        data = zdata;\n    }\n}\n\nvoid swoole_websocket_onBeforeHandshakeResponse(Server *serv, int server_fd, HttpContext *ctx) {\n    auto cb = php_swoole_server_get_callback(serv, server_fd, SW_SERVER_CB_onBeforeHandshakeResponse);\n    if (cb) {\n        zval args[3];\n        args[0] = *php_swoole_server_zval_ptr(serv);\n        args[1] = *ctx->request.zobject;\n        args[2] = *ctx->response.zobject;\n        if (UNEXPECTED(!zend::function::call(cb, 3, args, nullptr, serv->is_enable_coroutine()))) {\n            php_swoole_error(\n                E_WARNING, \"%s->onBeforeHandshakeResponse handler error\", ZSTR_VAL(swoole_websocket_server_ce->name));\n            serv->close(ctx->fd, false);\n        }\n    }\n}\n\nvoid swoole_websocket_onOpen(Server *serv, const HttpContext *ctx) {\n    Connection *conn = serv->get_connection_by_session_id(ctx->fd);\n    if (!conn) {\n        swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, \"session[%ld] is closed\", ctx->fd);\n        return;\n    }\n    auto cb = php_swoole_server_get_callback(serv, conn->server_fd, SW_SERVER_CB_onOpen);\n    if (cb) {\n        zval args[2];\n        args[0] = *php_swoole_server_zval_ptr(serv);\n        args[1] = *ctx->request.zobject;\n        if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, serv->is_enable_coroutine()))) {\n            php_swoole_error(E_WARNING, \"%s->onOpen handler error\", ZSTR_VAL(swoole_websocket_server_ce->name));\n            serv->close(ctx->fd, false);\n        }\n    }\n}\n\n/**\n * default onRequest callback\n */\nvoid swoole_websocket_onRequest(HttpContext *ctx) {\n    const char *bad_request = \"HTTP/1.1 400 Bad Request\\r\\n\"\n                              \"Connection: close\\r\\n\"\n                              \"Content-Type: text/html; charset=UTF-8\\r\\n\"\n                              \"Cache-Control: must-revalidate,no-cache,no-store\\r\\n\"\n                              \"Content-Length: 83\\r\\n\"\n                              \"Server: \" SW_HTTP_SERVER_SOFTWARE \"\\r\\n\\r\\n\"\n                              \"<html><body><h2>HTTP 400 Bad Request</h2><hr><i>Powered by Swoole</i></body></html>\";\n\n    ctx->send(ctx, bad_request, strlen(bad_request));\n    ctx->end_ = 1;\n    ctx->close(ctx);\n}\n\nvoid php_swoole_sha1(const char *str, int _len, unsigned char *digest) {\n    PHP_SHA1_CTX context;\n    PHP_SHA1Init(&context);\n    PHP_SHA1Update(&context, (unsigned char *) str, _len);\n    PHP_SHA1Final(digest, &context);\n}\n\nbool swoole_websocket_handshake(HttpContext *ctx) {\n    char sec_buf[128];\n    zval *header = ctx->request.zheader;\n    HashTable *ht = Z_ARRVAL_P(header);\n    zval *pData;\n    zval retval;\n\n    if (!((pData = zend_hash_str_find(ht, ZEND_STRL(\"sec-websocket-key\"))))) {\n    _bad_request:\n        ctx->response.status = SW_HTTP_BAD_REQUEST;\n        ctx->end(nullptr, &retval);\n        return false;\n    }\n\n    zend::String str_pData(pData);\n\n    if (str_pData.len() != BASE64_ENCODE_OUT_SIZE(SW_WEBSOCKET_SEC_KEY_LEN)) {\n        goto _bad_request;\n    }\n\n    char sha1_str[20];\n    // sec_websocket_accept\n    memcpy(sec_buf, str_pData.val(), str_pData.len());\n    memcpy(sec_buf + str_pData.len(), SW_WEBSOCKET_GUID, sizeof(SW_WEBSOCKET_GUID) - 1);\n    // sha1 sec_websocket_accept\n    php_swoole_sha1(sec_buf, str_pData.len() + sizeof(SW_WEBSOCKET_GUID) - 1, (unsigned char *) sha1_str);\n    // base64 encode\n    int sec_len = swoole::base64_encode((unsigned char *) sha1_str, sizeof(sha1_str), sec_buf);\n\n    ctx->set_header(ZEND_STRL(\"Upgrade\"), ZEND_STRL(\"websocket\"), false);\n    ctx->set_header(ZEND_STRL(\"Connection\"), ZEND_STRL(\"Upgrade\"), false);\n    ctx->set_header(ZEND_STRL(\"Sec-WebSocket-Accept\"), sec_buf, sec_len, false);\n    ctx->set_header(ZEND_STRL(\"Sec-WebSocket-Version\"), ZEND_STRL(SW_WEBSOCKET_VERSION), false);\n\n    Server *serv = nullptr;\n    Connection *conn = nullptr;\n\n    if (!ctx->is_co_socket()) {\n        serv = ctx->get_async_server();\n        conn = serv->get_connection_by_session_id(ctx->fd);\n        if (!conn) {\n            swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, \"session[%ld] is closed\", ctx->fd);\n            return false;\n        }\n    }\n\n    if (conn) {\n        conn->websocket_status = WebSocket::STATUS_ACTIVE;\n        ListenPort *port = serv->get_port_by_server_fd(conn->server_fd);\n        if (port && !port->websocket_settings.protocol.empty()) {\n            ctx->set_header(ZEND_STRL(\"Sec-WebSocket-Protocol\"), port->websocket_settings.protocol, false);\n        }\n        swoole_websocket_onBeforeHandshakeResponse(serv, conn->server_fd, ctx);\n    } else {\n        auto sock = ctx->get_co_socket();\n        sock->open_length_check = true;\n        sock->protocol.package_length_size = SW_WEBSOCKET_HEADER_LEN;\n        sock->protocol.package_length_offset = 0;\n        sock->protocol.package_body_offset = 0;\n        sock->protocol.get_package_length = WebSocket::get_package_length;\n    }\n\n    ctx->response.status = SW_HTTP_SWITCHING_PROTOCOLS;\n    ctx->upgrade = 1;\n\n    ctx->end(nullptr, &retval);\n    return Z_TYPE(retval) == IS_TRUE;\n}\n\n#ifdef SW_HAVE_ZLIB\nbool WebSocket::message_uncompress(String *buffer, const char *in, size_t in_len) {\n    z_stream zstream;\n    int status;\n    bool ret = false;\n\n    memset(&zstream, 0, sizeof(zstream));\n    zstream.zalloc = php_zlib_alloc;\n    zstream.zfree = php_zlib_free;\n    // gzip_stream.total_out = 0;\n    status = inflateInit2(&zstream, SW_ZLIB_ENCODING_RAW);\n    if (status != Z_OK) {\n        swoole_warning(\"inflateInit2() failed by %s\", zError(status));\n        return false;\n    }\n\n    zstream.next_in = (Bytef *) in;\n    zstream.avail_in = in_len;\n    zstream.total_in = 0;\n\n    while (true) {\n        zstream.avail_out = buffer->size - buffer->length;\n        zstream.next_out = (Bytef *) (buffer->str + buffer->length);\n        status = inflate(&zstream, Z_SYNC_FLUSH);\n        if (status >= 0) {\n            buffer->length = zstream.total_out;\n        }\n        if (status == Z_STREAM_END || (status == Z_OK && zstream.avail_in == 0)) {\n            ret = true;\n            break;\n        }\n        if (status != Z_OK) {\n            break;\n        }\n        if (buffer->length + (SW_BUFFER_SIZE_STD / 2) >= buffer->size) {\n            buffer->extend();\n        }\n    }\n    inflateEnd(&zstream);\n\n    if (!ret) {\n        php_swoole_fatal_error(E_WARNING, \"inflate() failed, Error: %s[%d]\", zError(status), status);\n        return false;\n    }\n    return true;\n}\n\nbool WebSocket::message_compress(String *buffer, const char *data, size_t length, int level) {\n    // ==== ZLIB ====\n    if (level == Z_NO_COMPRESSION) {\n        level = Z_DEFAULT_COMPRESSION;\n    } else if (level > Z_BEST_COMPRESSION) {\n        level = Z_BEST_COMPRESSION;\n    }\n\n    z_stream zstream = {};\n    int status;\n\n    zstream.zalloc = php_zlib_alloc;\n    zstream.zfree = php_zlib_free;\n\n    status = deflateInit2(&zstream, level, Z_DEFLATED, SW_ZLIB_ENCODING_RAW, SW_ZLIB_DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);\n    if (status != Z_OK) {\n        php_swoole_fatal_error(E_WARNING, \"deflateInit2() failed, Error: [%d]\", status);\n        return false;\n    }\n\n    zstream.next_in = (Bytef *) data;\n    zstream.avail_in = length;\n    zstream.next_out = (Bytef *) buffer->str;\n\n    size_t max_length = deflateBound(&zstream, length);\n    if (max_length > buffer->size) {\n        buffer->extend(max_length);\n    }\n\n    size_t bytes_written = 0;\n    uchar in_sync_flush;\n    int result;\n\n    do {\n        size_t write_remaining;\n\n        if (zstream.avail_out == 0) {\n            size_t write_position;\n\n            zstream.avail_out = max_length;\n            write_position = buffer->length;\n            buffer->length = max_length;\n            zstream.next_out = (Bytef *) buffer->str + write_position;\n\n            /* Use a fixed value for buffer increments */\n            max_length = 4096;\n        }\n\n        write_remaining = buffer->length - bytes_written;\n        in_sync_flush = zstream.avail_in == 0;\n        result = deflate(&zstream, in_sync_flush ? Z_SYNC_FLUSH : Z_NO_FLUSH);\n        bytes_written += write_remaining - zstream.avail_out;\n    } while (result == Z_OK);\n\n    deflateEnd(&zstream);\n\n    if (result != Z_BUF_ERROR || bytes_written < 4) {\n        php_swoole_fatal_error(E_WARNING, \"Failed to compress outgoing frame\");\n        return false;\n    }\n\n    if (status != Z_OK) {\n        php_swoole_fatal_error(E_WARNING, \"deflate() failed, Error: [%d]\", status);\n        return false;\n    }\n\n    buffer->length = bytes_written - 4;\n\n    return true;\n}\n#endif\n\nint swoole_websocket_onMessage(Server *serv, RecvData *req) {\n    SessionId fd = req->info.fd;\n    uchar flags = 0;\n    uchar opcode = 0;\n    auto port = serv->get_port_by_session_id(fd);\n    if (!port) {\n        return SW_ERR;\n    }\n\n    zval zdata;\n    php_swoole_get_recv_data(serv, &zdata, req);\n\n    WebSocket::parse_ext_flags(req->info.ext_flags, &opcode, &flags);\n\n    if ((opcode == WebSocket::OPCODE_CLOSE && !port->websocket_settings.open_close_frame) ||\n        (opcode == WebSocket::OPCODE_PING && !port->websocket_settings.open_ping_frame) ||\n        (opcode == WebSocket::OPCODE_PONG && !port->websocket_settings.open_pong_frame)) {\n        if (opcode == WebSocket::OPCODE_PING) {\n            String frame(SW_WEBSOCKET_FRAME_HEADER_SIZE + req->info.len, sw_php_allocator());\n            WebSocket::encode(&frame, req->data, req->info.len, WebSocket::OPCODE_PONG, WebSocket::FLAG_FIN);\n            serv->send(fd, frame.str, frame.length);\n        }\n        zval_ptr_dtor(&zdata);\n        return SW_OK;\n    }\n\n    // RFC 7692: uncompress websocket data\n    // See https://datatracker.ietf.org/doc/html/rfc7692\n    if (flags & WebSocket::FLAG_RSV1) {\n        zval zpayload;\n        auto rs = FrameObject::uncompress(&zpayload, Z_STRVAL(zdata), Z_STRLEN(zdata));\n        zval_ptr_dtor(&zdata);\n        if (!rs) {\n            return SW_OK;\n        }\n        zdata = zpayload;\n    }\n\n    auto cb = php_swoole_server_get_callback(serv, req->info.server_fd, SW_SERVER_CB_onMessage);\n    zval args[2];\n\n    args[0] = *php_swoole_server_zval_ptr(serv);\n    WebSocket::construct_frame(&args[1], opcode, &zdata, flags);\n    zend_update_property_long(swoole_websocket_frame_ce, SW_Z8_OBJ_P(&args[1]), ZEND_STRL(\"fd\"), fd);\n\n    if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, serv->is_enable_coroutine()))) {\n        php_swoole_error(E_WARNING, \"%s->onMessage handler error\", ZSTR_VAL(swoole_websocket_server_ce->name));\n        serv->close(fd, false);\n    }\n\n    zval_ptr_dtor(&zdata);\n    zval_ptr_dtor(&args[1]);\n\n    return SW_OK;\n}\n\nint swoole_websocket_onHandshake(Server *serv, ListenPort *port, HttpContext *ctx) {\n    SessionId fd = ctx->fd;\n    bool success = swoole_websocket_handshake(ctx);\n    if (success) {\n        swoole_websocket_onOpen(serv, ctx);\n    } else {\n        serv->close(fd, true);\n    }\n    return SW_OK;\n}\n\nvoid php_swoole_websocket_server_minit(int module_number) {\n    SW_INIT_CLASS_ENTRY_EX(swoole_websocket_server,\n                           \"Swoole\\\\WebSocket\\\\Server\",\n                           nullptr,\n                           swoole_websocket_server_methods,\n                           swoole_http_server);\n#ifndef SW_THREAD\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_websocket_server);\n#endif\n    SW_SET_CLASS_CLONEABLE(swoole_websocket_server, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_websocket_server, sw_zend_class_unset_property_deny);\n\n    SW_INIT_CLASS_ENTRY(swoole_websocket_frame, R\"(Swoole\\WebSocket\\Frame)\", nullptr, swoole_websocket_frame_methods);\n    zend_class_implements(swoole_websocket_frame_ce, 1, zend_ce_stringable);\n    zend_declare_property_long(swoole_websocket_frame_ce, ZEND_STRL(\"fd\"), 0, ZEND_ACC_PUBLIC);\n    zend_declare_property_string(swoole_websocket_frame_ce, ZEND_STRL(\"data\"), \"\", ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_websocket_frame_ce, ZEND_STRL(\"opcode\"), WebSocket::OPCODE_TEXT, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(swoole_websocket_frame_ce, ZEND_STRL(\"flags\"), WebSocket::FLAG_FIN, ZEND_ACC_PUBLIC);\n    zend_declare_property_null(swoole_websocket_frame_ce, ZEND_STRL(\"finish\"), ZEND_ACC_PUBLIC);\n\n    SW_INIT_CLASS_ENTRY_EX(\n        swoole_websocket_closeframe, \"Swoole\\\\WebSocket\\\\CloseFrame\", nullptr, nullptr, swoole_websocket_frame);\n    zend_declare_property_long(\n        swoole_websocket_closeframe_ce, ZEND_STRL(\"opcode\"), WebSocket::OPCODE_CLOSE, ZEND_ACC_PUBLIC);\n    zend_declare_property_long(\n        swoole_websocket_closeframe_ce, ZEND_STRL(\"code\"), WebSocket::CLOSE_NORMAL, ZEND_ACC_PUBLIC);\n    zend_declare_property_string(swoole_websocket_closeframe_ce, ZEND_STRL(\"reason\"), \"\", ZEND_ACC_PUBLIC);\n\n    /* {{{ swoole namespace */\n    // status\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_STATUS_CONNECTION\", WebSocket::STATUS_CONNECTION);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_STATUS_HANDSHAKE\", WebSocket::STATUS_HANDSHAKE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_STATUS_ACTIVE\", WebSocket::STATUS_ACTIVE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_STATUS_CLOSING\", WebSocket::STATUS_CLOSING);\n    // all opcodes\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_OPCODE_CONTINUATION\", WebSocket::OPCODE_CONTINUATION);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_OPCODE_TEXT\", WebSocket::OPCODE_TEXT);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_OPCODE_BINARY\", WebSocket::OPCODE_BINARY);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_OPCODE_CLOSE\", WebSocket::OPCODE_CLOSE);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_OPCODE_PING\", WebSocket::OPCODE_PING);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_OPCODE_PONG\", WebSocket::OPCODE_PONG);\n    // flags\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_FLAG_FIN\", WebSocket::FLAG_FIN);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_FLAG_RSV1\", WebSocket::FLAG_RSV1);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_FLAG_RSV2\", WebSocket::FLAG_RSV2);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_FLAG_RSV3\", WebSocket::FLAG_RSV3);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_FLAG_MASK\", WebSocket::FLAG_MASK);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_FLAG_COMPRESS\", WebSocket::FLAG_COMPRESS);\n    // close error\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_CLOSE_NORMAL\", WebSocket::CLOSE_NORMAL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_CLOSE_GOING_AWAY\", WebSocket::CLOSE_GOING_AWAY);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_CLOSE_PROTOCOL_ERROR\", WebSocket::CLOSE_PROTOCOL_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_CLOSE_DATA_ERROR\", WebSocket::CLOSE_DATA_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_CLOSE_STATUS_ERROR\", WebSocket::CLOSE_STATUS_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_CLOSE_ABNORMAL\", WebSocket::CLOSE_ABNORMAL);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_CLOSE_MESSAGE_ERROR\", WebSocket::CLOSE_MESSAGE_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_CLOSE_POLICY_ERROR\", WebSocket::CLOSE_POLICY_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_CLOSE_MESSAGE_TOO_BIG\", WebSocket::CLOSE_MESSAGE_TOO_BIG);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_CLOSE_EXTENSION_MISSING\", WebSocket::CLOSE_EXTENSION_MISSING);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_CLOSE_SERVER_ERROR\", WebSocket::CLOSE_SERVER_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_CLOSE_CLOSE_SERVICE_RESTART\", WebSocket::CLOSE_SERVICE_RESTART);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_CLOSE_TRY_AGAIN_LATER\", WebSocket::CLOSE_TRY_AGAIN_LATER);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_CLOSE_BAD_GATEWAY\", WebSocket::CLOSE_BAD_GATEWAY);\n    SW_REGISTER_LONG_CONSTANT(\"SWOOLE_WEBSOCKET_CLOSE_TLS\", WebSocket::CLOSE_TLS);\n    /* swoole namespace }}} */\n\n    /* BC */\n    // status\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_STATUS_CONNECTION\", WebSocket::STATUS_CONNECTION);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_STATUS_HANDSHAKE\", WebSocket::STATUS_HANDSHAKE);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_STATUS_FRAME\", WebSocket::STATUS_ACTIVE);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_STATUS_ACTIVE\", WebSocket::STATUS_ACTIVE);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_STATUS_CLOSING\", WebSocket::STATUS_CLOSING);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_STATUS_HANDSHAKE_FAILED\", WebSocket::STATUS_HANDSHAKE_FAILED);\n    // all opcodes\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_OPCODE_CONTINUATION\", WebSocket::OPCODE_CONTINUATION);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_OPCODE_TEXT\", WebSocket::OPCODE_TEXT);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_OPCODE_BINARY\", WebSocket::OPCODE_BINARY);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_OPCODE_CLOSE\", WebSocket::OPCODE_CLOSE);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_OPCODE_PING\", WebSocket::OPCODE_PING);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_OPCODE_PONG\", WebSocket::OPCODE_PONG);\n    // close error\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_CLOSE_NORMAL\", WebSocket::CLOSE_NORMAL);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_CLOSE_GOING_AWAY\", WebSocket::CLOSE_GOING_AWAY);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_CLOSE_PROTOCOL_ERROR\", WebSocket::CLOSE_PROTOCOL_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_CLOSE_DATA_ERROR\", WebSocket::CLOSE_DATA_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_CLOSE_STATUS_ERROR\", WebSocket::CLOSE_STATUS_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_CLOSE_ABNORMAL\", WebSocket::CLOSE_ABNORMAL);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_CLOSE_MESSAGE_ERROR\", WebSocket::CLOSE_MESSAGE_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_CLOSE_POLICY_ERROR\", WebSocket::CLOSE_POLICY_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_CLOSE_MESSAGE_TOO_BIG\", WebSocket::CLOSE_MESSAGE_TOO_BIG);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_CLOSE_EXTENSION_MISSING\", WebSocket::CLOSE_EXTENSION_MISSING);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_CLOSE_SERVER_ERROR\", WebSocket::CLOSE_SERVER_ERROR);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_CLOSE_CLOSE_SERVICE_RESTART\", WebSocket::CLOSE_SERVICE_RESTART);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_CLOSE_TRY_AGAIN_LATER\", WebSocket::CLOSE_TRY_AGAIN_LATER);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_CLOSE_BAD_GATEWAY\", WebSocket::CLOSE_BAD_GATEWAY);\n    SW_REGISTER_LONG_CONSTANT(\"WEBSOCKET_CLOSE_TLS\", WebSocket::CLOSE_TLS);\n}\n\nvoid php_swoole_server_set_websocket_option(ListenPort *port, zend_array *vht) {\n    WebSocket::apply_setting(port->websocket_settings, vht, true);\n}\n\nvoid WebSocket::apply_setting(WebSocketSettings &settings, zend_array *vht, bool in_server) {\n    zval *ztmp;\n    if (php_swoole_array_get_value(vht, \"websocket_subprotocol\", ztmp)) {\n        settings.protocol = zend::String(ztmp).to_std_string();\n    }\n    if (php_swoole_array_get_value(vht, \"websocket_mask\", ztmp)) {\n        settings.mask = zval_is_true(ztmp);\n    } else {\n        settings.mask = !in_server;\n    }\n#ifdef SW_HAVE_ZLIB\n    if (php_swoole_array_get_value(vht, \"websocket_compression\", ztmp)) {\n        settings.compression = zval_is_true(ztmp);\n    }\n#endif\n    if (php_swoole_array_get_value(vht, \"open_websocket_close_frame\", ztmp)) {\n        settings.open_close_frame = zval_is_true(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"open_websocket_ping_frame\", ztmp)) {\n        settings.open_ping_frame = zval_is_true(ztmp);\n    }\n    if (php_swoole_array_get_value(vht, \"open_websocket_pong_frame\", ztmp)) {\n        settings.open_pong_frame = zval_is_true(ztmp);\n    }\n    settings.in_server = in_server;\n}\n\nstatic sw_inline bool swoole_websocket_server_push(Server *serv, SessionId fd, String *buffer) {\n    if (sw_unlikely(fd <= 0)) {\n        php_swoole_fatal_error(E_WARNING, \"fd[%ld] is invalid\", fd);\n        return false;\n    }\n\n    Connection *conn = serv->get_connection_by_session_id(fd);\n    if (!conn || conn->websocket_status < WebSocket::STATUS_HANDSHAKE) {\n        swoole_set_last_error(SW_ERROR_WEBSOCKET_UNCONNECTED);\n        php_swoole_fatal_error(\n            E_WARNING, \"the connected client of connection[%ld] is not a websocket client or closed\", fd);\n        return false;\n    }\n\n    bool rv = serv->send(fd, buffer->str, buffer->length);\n    if (!rv && swoole_get_last_error() == SW_ERROR_OUTPUT_SEND_YIELD) {\n        auto sdata = zend_string_init(buffer->str, buffer->length, false);\n        rv = php_swoole_server_send_yield(serv, fd, sdata);\n        zend_string_release(sdata);\n    }\n    return rv;\n}\n\nstatic sw_inline bool swoole_websocket_server_close(Server *serv, SessionId fd, String *buffer, bool real_close) {\n    bool ret = swoole_websocket_server_push(serv, fd, buffer);\n    if (!ret || !real_close) {\n        return ret;\n    }\n    Connection *conn = serv->get_connection_by_session_id(fd);\n    if (conn) {\n        // Change status immediately to avoid double close\n        conn->websocket_status = WebSocket::STATUS_CLOSING;\n        // Server close connection immediately\n        return serv->close(fd, false);\n    } else {\n        return false;\n    }\n}\n\nstatic inline void swoole_websocket_server_pack(zval *zdata, zend_long opcode, zend_long flags, zval *return_value) {\n    FrameObject frame{zdata, opcode, flags};\n    String buffer(SW_WEBSOCKET_FRAME_HEADER_SIZE + frame.get_data_size(), sw_zend_string_allocator());\n\n    if (sw_unlikely(!frame.pack(&buffer))) {\n        RETURN_EMPTY_STRING();\n    }\n\n    auto packed_str = zend::fetch_zend_string_by_val(buffer.str);\n    ZSTR_VAL(packed_str)[buffer.length] = '\\0';\n    ZSTR_LEN(packed_str) = buffer.length;\n    buffer.release();\n    RETURN_STR(packed_str);\n}\n\nstatic PHP_METHOD(swoole_websocket_server, disconnect) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_long fd = 0;\n    zend_long code = WebSocket::CLOSE_NORMAL;\n    char *data = nullptr;\n    size_t length = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 3)\n    Z_PARAM_LONG(fd)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(code)\n    Z_PARAM_STRING(data, length)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    String buffer(SW_WEBSOCKET_FRAME_HEADER_SIZE + length + 2, sw_zend_string_allocator());\n    if (!WebSocket::pack_close_frame(&buffer, code, data, length, 0)) {\n        RETURN_FALSE;\n    }\n    RETURN_BOOL(swoole_websocket_server_close(serv, fd, &buffer, 1));\n}\n\nstatic PHP_METHOD(swoole_websocket_server, ping) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_long fd = 0;\n    zend_string *zpayload = zend_empty_string;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_LONG(fd)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_STR(zpayload)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    zval zdata = {};\n    ZVAL_STR(&zdata, zpayload);\n    FrameObject frame{&zdata, WebSocket::OPCODE_PING, WebSocket::FLAG_FIN};\n\n    String buffer(SW_WEBSOCKET_FRAME_HEADER_SIZE + frame.get_data_size(), sw_zend_string_allocator());\n    if (sw_unlikely(!frame.pack(&buffer))) {\n        swoole_set_last_error(SW_ERROR_WEBSOCKET_PACK_FAILED);\n        RETURN_FALSE;\n    }\n\n    RETURN_BOOL(swoole_websocket_server_push(serv, fd, &buffer));\n}\n\nstatic PHP_METHOD(swoole_websocket_server, push) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_long fd = 0;\n    zval *zdata = nullptr;\n    zend_long opcode = WebSocket::OPCODE_TEXT;\n    zval *zflags = nullptr;\n    zend_long flags = WebSocket::FLAG_FIN;\n\n    ZEND_PARSE_PARAMETERS_START(2, 4)\n    Z_PARAM_LONG(fd)\n    Z_PARAM_ZVAL(zdata)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(opcode)\n    Z_PARAM_ZVAL_EX(zflags, 1, 0)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    if (zflags != nullptr) {\n        flags = zval_get_long(zflags);\n    }\n\n    Connection *conn = serv->get_connection_verify(fd);\n    if (sw_unlikely(!conn)) {\n        swoole_set_last_error(SW_ERROR_SESSION_NOT_EXIST);\n        php_swoole_fatal_error(E_WARNING, \"session#\" ZEND_LONG_FMT \" does not exists\", fd);\n        RETURN_FALSE;\n    }\n\n    FrameObject frame{zdata, opcode, flags};\n\n    if (conn->websocket_compression) {\n        sw_set_bit(frame.flags, WebSocket::FLAG_COMPRESS);\n    }\n\n    // WebSocket server must not set data mask\n    sw_unset_bit(frame.flags, WebSocket::FLAG_MASK);\n\n    String buffer(SW_WEBSOCKET_FRAME_HEADER_SIZE + frame.get_data_size(), sw_zend_string_allocator());\n\n    if (sw_unlikely(!frame.pack(&buffer))) {\n        swoole_set_last_error(SW_ERROR_WEBSOCKET_PACK_FAILED);\n        RETURN_FALSE;\n    }\n\n    RETURN_BOOL(swoole_websocket_server_push(serv, fd, &buffer));\n}\n\nstatic PHP_METHOD(swoole_websocket_server, pack) {\n    zval *zdata;\n    zend_long opcode = WebSocket::OPCODE_TEXT;\n    zend_long flags = WebSocket::FLAG_FIN;\n\n    ZEND_PARSE_PARAMETERS_START(1, 3)\n    Z_PARAM_ZVAL(zdata)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(opcode)\n    Z_PARAM_LONG(flags)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    swoole_websocket_server_pack(zdata, opcode, flags, return_value);\n}\n\nstatic PHP_METHOD(swoole_websocket_frame, __toString) {\n    swoole_websocket_server_pack(ZEND_THIS, 0, 0, return_value);\n}\n\nstatic PHP_METHOD(swoole_websocket_server, unpack) {\n    char *data;\n    size_t length;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_STRING(data, length)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    WebSocket::Frame frame;\n\n    if (length < sizeof(frame.header)) {\n        swoole_set_last_error(SW_ERROR_PROTOCOL_ERROR);\n        RETURN_FALSE;\n    }\n\n    if (!WebSocket::decode(&frame, data, length)) {\n        swoole_set_last_error(SW_ERROR_PROTOCOL_ERROR);\n        RETURN_FALSE;\n    }\n\n    zval zpayload{};\n    uint8_t flags = frame.get_flags();\n\n    if (frame.compressed()) {\n        if (sw_unlikely(!FrameObject::uncompress(&zpayload, frame.payload, frame.payload_length))) {\n            swoole_set_last_error(SW_ERROR_PROTOCOL_ERROR);\n            RETURN_FALSE;\n        }\n    } else {\n        ZVAL_STRINGL(&zpayload, frame.payload, frame.payload_length);\n    }\n\n    WebSocket::construct_frame(return_value, frame.header.OPCODE, &zpayload, flags);\n    zval_ptr_dtor(&zpayload);\n}\n\nstatic PHP_METHOD(swoole_websocket_server, isEstablished) {\n    Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS);\n    if (sw_unlikely(!serv->is_started())) {\n        php_swoole_fatal_error(E_WARNING, \"server is not running\");\n        RETURN_FALSE;\n    }\n\n    zend_long session_id;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(session_id)\n    ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);\n\n    Connection *conn = serv->get_connection_verify(session_id);\n    // not isEstablished\n    if (!conn || conn->closed || conn->websocket_status < WebSocket::STATUS_ACTIVE) {\n        RETURN_FALSE;\n    } else {\n        RETURN_TRUE;\n    }\n}\n"
  },
  {
    "path": "gdbinit",
    "content": "define timer_list\n    if SwooleTG.timer\n        printf \"current timer number: %d, round: %d\\n\", SwooleTG.timer.num,SwooleTG.timer->round\n        set $running = 1\n        set $i = 1\n        while $running\n            if $i < SwooleTG.timer->heap->num\n                set $tmp = SwooleTG.timer->heap->nodes[$i]\n                set $node = (swTimer_node *)$tmp->data\n                if $node\n                   printf \"\\t timer[%d] exec_msec:%ld round:%ld\\n\", $node->id, $node->exec_msec, $node->round\n                end\n            else\n                set $running = 0\n            end\n            set $i = $i + 1\n        end\n    else\n        printf \"no timer\\n\"\n    end\nend\n\ndefine reactor_info\n    if SwooleTG.reactor\n        printf \"\\t reactor id: %d\\n\",SwooleTG.reactor->id\n        printf \"\\t running: %d\\n\", SwooleTG.reactor->running\n        printf \"\\t event_num: %d\\n\", SwooleTG.reactor->event_num\n        printf \"\\t aio_task_num: %d\\n\", SwooleTG.aio_task_num\n        printf \"\\t max_event_num: %d\\n\", SwooleTG.reactor->max_event_num\n        printf \"\\t check_timer: %d\\n\", SwooleTG.reactor->check_timer\n        printf \"\\t timeout_msec: %d\\n\", SwooleTG.reactor->timeout_msec\n    end\nend\n\ndefine sw_hash_map_list\n    set $hmap = $arg0\n    if $hmap\n        if $hmap->root->hh.tbl->num_items == 0\n            echo \"no content\\n\"\n        else\n            set $running = 1\n            set $it = $hmap->iterator\n            if $it == 0\n               set $it = $hmap->root\n            end\n            while $running\n                \n                set $tmp = (swHashMap_node *)$it->hh.next\n                if $tmp\n                    printf \"key_int[%d] key_str:%s data:%p\\n\", $tmp->key_int, $tmp->key_str, $tmp->data\n                    set $it = $tmp\n                else\n                    set $running = 0\n                end\n            end \n        end\n    end\nend\n\ndefine co_list\n    call swoole_coroutine_iterator_reset()\n    set $running = 1\n    while $running\n        set $co = swoole_coroutine_iterator_each()\n        if $co\n            printf \"coroutine %ld \", $co->cid\n            if $co->state == 0\n                printlnc $GREEN \"SW_CORO_INIT\"\n            end\n            if $co->state == 1\n                printlnc $YELLOW \"SW_CORO_WAITING\"\n            end\n            if $co->state == 2\n                printlnc $GREEN \"SW_CORO_RUNNING\"\n            end\n            if $co->state == 3\n                printlnc $CYAN \"SW_CORO_END\"\n            end\n        else\n            set $running = 0\n        end\n    end\nend\n\ndefine co_bt\n    if swoole_coroutine_count() == 0\n        printf \"no coroutine is running\\n\"\n    end\n    ____sw_executor_globals\n    if $argc > 0\n        set $cid = (int) $arg0\n    else\n        if 'swoole::Coroutine::current'\n            set $cid = (int) 'swoole::Coroutine::current'->cid\n        else\n            set $cid = -1\n        end\n    end\n\n    printf \"coroutine cid: [%d]\\n\", $cid\n    if $argc > 0\n        __co_bt $cid\n    else\n        sw_dump_bt php_swoole_get_executor_globals()->current_execute_data\n    end\nend\ndocument co_bt\n    dump current coroutine or the cid backtrace.\n    useage: co_bt [cid]\nend\n\ndefine __co_bt\n    set $cid = (int)$arg0\n    set $co = swoole_coroutine_get($cid)\n    if $co\n        set $task = ('swoole::PHPContext' *) $co->get_task()\n        if $task\n            sw_dump_bt $task->execute_data\n        end\n    else\n        printf \"coroutines %d not found\\n\", $cid\n    end\nend\n\ndefine co_status\n    printf \"\\t c_stack_size: %d\\n\",  'swoole::Coroutine::stack_size'\n    printf \"\\t active: %d\\n\",  'swoole::PHPCoroutine::active'\n    printf \"\\t coro_num: %d\\n\",  swoole_coroutine_count()\n    printf \"\\t peak_coro_num: %d\\n\",  'swoole::Coroutine::peak_num'\n    printf \"\\t config: \"\n    print 'swoole::PHPCoroutine::config'\nend\n\ndefine ____sw_executor_globals\n    set $eg = php_swoole_get_executor_globals()\nend\n\ndefine ____sw_print_str\n    set $tmp = 0\n    set $str = $arg0\n    if $argc > 2\n        set $maxlen = $arg2\n    else\n        set $maxlen = 256\n    end\n\n    printf \"\\\"\"\n    while $tmp < $arg1 && $tmp < $maxlen\n        if $str[$tmp] > 31 && $str[$tmp] < 127\n            printf \"%c\", $str[$tmp]\n        else\n            printf \"\\\\%o\", $str[$tmp]\n        end\n        set $tmp = $tmp + 1\n    end\n    if $tmp != $arg1\n        printf \"...\"\n    end\n    printf \"\\\"\"\nend\n\ndefine sw_dump_bt\n    set $ex = $arg0\n    while $ex\n        printf \"[%p] \", $ex\n        set $func = $ex->func\n        if $func\n            if $ex->This->value.obj\n                if $func->common.scope\n                    printf \"%s->\", $func->common.scope->name->val\n                else\n                    printf \"%s->\", $ex->This->value.obj->ce.name->val\n                end\n            else\n                if $func->common.scope\n                    printf \"%s::\", $func->common.scope->name->val\n                end\n            end\n\n            if $func->common.function_name\n                printf \"%s(\", $func->common.function_name->val\n            else\n                printf \"(main\"\n            end\n\n            set $callFrameSize = (sizeof(zend_execute_data) + sizeof(zval) - 1) / sizeof(zval)\n\n            set $count = $ex->This.u2.num_args\n            set $arg = 0\n            while $arg < $count\n                if $arg > 0\n                    printf \", \"\n                end\n                set $zvalue = (zval *) $ex + $callFrameSize + $arg\n                set $type = $zvalue->u1.v.type\n                if $type == 1\n                    printf \"NULL\"\n                end\n                if $type == 2\n                    printf \"false\"\n                end\n                if $type == 3\n                    printf \"true\"\n                end\n                if $type == 4\n                    printf \"%ld\", $zvalue->value.lval\n                end\n                if $type == 5\n                    printf \"%f\", $zvalue->value.dval\n                end\n                if $type == 6\n                    ____sw_print_str $zvalue->value.str->val $zvalue->value.str->len\n                end\n                if $type == 7\n                    printf \"array(%d)[%p]\", $zvalue->value.arr->nNumOfElements, $zvalue\n                end\n                if $type == 8\n                    printf \"object[%p]\", $zvalue\n                end\n                if $type == 9\n                    printf \"resource(#%d)\", $zvalue->value.lval\n                end\n                if $type == 10\n                    printf \"reference\"\n                end\n                if $type > 10\n                    printf \"unknown type %d\", $type\n                end\n                set $arg = $arg + 1\n            end\n\n            printf \") \"\n        else\n            printf \"??? \"\n        end\n        if $func != 0\n            if $func->type == 2\n                printf \"%s:%d \", $func->op_array.filename->val, $ex->opline->lineno\n            else\n                printf \"[internal function]\"\n            end\n        end\n        set $ex = $ex->prev_execute_data\n        printf \"\\n\"\n    end\nend\n\n# ======== color ========\nset $BLACK   = 0\nset $RED     = 1\nset $GREEN   = 2\nset $YELLOW  = 3\nset $BLUE    = 4\nset $MAGENTA = 5\nset $CYAN    = 6\nset $WHITE   = 7\n\ndefine color\n    if $argc == 0\n        set $arg = 0\n    else\n        set $arg = $arg0 + 30\n    end\n    printf \"%c[%dm\", 27, $arg\nend\n\n# ======== print ========\n\ndefine printlnc\n    color $arg0\n    printf \"%s\\n\", $arg1\n    color\nend\n"
  },
  {
    "path": "include/helper/kqueue.h",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2015 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#ifndef KQUEUE_IDE_HELPER_H_\n#define KQUEUE_IDE_HELPER_H_\n#ifdef USE_KQUEUE_IDE_HELPER\n\n#include <stdint.h>\n\n#define EVFILT_READ             (-1)\n#define EVFILT_WRITE            (-2)\n#define EVFILT_SIGNAL           (-6)\n\n/* kevent system call flags */\n#define KEVENT_FLAG_NONE                         0x000000       /* no flag value */\n#define KEVENT_FLAG_IMMEDIATE                    0x000001       /* immediate timeout */\n#define KEVENT_FLAG_ERROR_EVENTS                 0x000002       /* output events only include change errors */\n\n\n/* actions */\n#define EV_ADD              0x0001      /* add event to kq (implies enable) */\n#define EV_DELETE           0x0002      /* delete event from kq */\n#define EV_ENABLE           0x0004      /* enable event */\n#define EV_DISABLE          0x0008      /* disable event (not reported) */\n\n/* flags */\n#define EV_ONESHOT          0x0010      /* only report one occurrence */\n#define EV_CLEAR            0x0020      /* clear event state after reporting */\n#define EV_RECEIPT          0x0040      /* force immediate event output */\n                                        /* ... with or without EV_ERROR */\n                                        /* ... use KEVENT_FLAG_ERROR_EVENTS */\n                                        /*     on syscalls supporting flags */\n\n#define EV_DISPATCH         0x0080      /* disable event after reporting */\n#define EV_UDATA_SPECIFIC   0x0100      /* unique kevent per udata value */\n\n#define EV_DISPATCH2        (EV_DISPATCH | EV_UDATA_SPECIFIC)\n/* ... in combination with EV_DELETE */\n/* will defer delete until udata-specific */\n/* event enabled. EINPROGRESS will be */\n/* returned to indicate the deferral */\n\n#define EV_VANISHED         0x0200      /* report that source has vanished  */\n                                        /* ... only valid with EV_DISPATCH2 */\n\n#define EV_SYSFLAGS         0xF000      /* reserved by system */\n#define EV_FLAG0            0x1000      /* filter-specific flag */\n#define EV_FLAG1            0x2000      /* filter-specific flag */\n\n/* returned values */\n#define EV_EOF              0x8000      /* EOF detected */\n#define EV_ERROR            0x4000      /* error, data contains errno */\n\n/*\n * Filter specific flags for EVFILT_READ\n *\n * The default behavior for EVFILT_READ is to make the \"read\" determination\n * relative to the current file descriptor read pointer.\n *\n * The EV_POLL flag indicates the determination should be made via poll(2)\n * semantics. These semantics dictate always returning true for regular files,\n * regardless of the amount of unread data in the file.\n *\n * On input, EV_OOBAND specifies that filter should actively return in the\n * presence of OOB on the descriptor. It implies that filter will return\n * if there is OOB data available to read OR when any other condition\n * for the read are met (for example number of bytes regular data becomes >=\n * low-watermark).\n * If EV_OOBAND is not set on input, it implies that the filter should not actively\n * return for out of band data on the descriptor. The filter will then only return\n * when some other condition for read is met (ex: when number of regular data bytes\n * >=low-watermark OR when socket can't receive more data (SS_CANTRCVMORE)).\n *\n * On output, EV_OOBAND indicates the presence of OOB data on the descriptor.\n * If it was not specified as an input parameter, then the data count is the\n * number of bytes before the current OOB marker, else data count is the number\n * of bytes beyond OOB marker.\n */\n#define EV_POLL         EV_FLAG0\n#define EV_OOBAND       EV_FLAG1\n\nstruct kevent {\n    uintptr_t       ident;          /* identifier for this event */\n    int16_t         filter;         /* filter for event */\n    uint16_t        flags;          /* general flags */\n    uint32_t        fflags;         /* filter-specific flags */\n    intptr_t        data;           /* filter-specific data */\n    void            *udata;         /* opaque user data identifier */\n};\n\nint kqueue(void);\nint kevent(\n    int kq,\n    const struct kevent *changelist, int nchanges,\n    struct kevent *eventlist, int nevents,\n    const struct timespec *timeout\n);\n\n#define EV_SET(kevp, a, b, c, d, e, f) do {     \\\n    struct kevent *__kevp__ = (kevp);       \\\n    __kevp__->ident = (a);                  \\\n    __kevp__->filter = (b);                 \\\n    __kevp__->flags = (c);                  \\\n    __kevp__->fflags = (d);                 \\\n    __kevp__->data = (e);                   \\\n    __kevp__->udata = (f);                  \\\n} while(0)\n\n#endif /* USE_KQUEUE_IDE_HELPER */\n#endif /* KQUEUE_IDE_HELPER_H_ */\n"
  },
  {
    "path": "include/swoole.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#elif defined(ENABLE_PHP_SWOOLE)\n#include \"php_config.h\"\n#endif\n\n#ifdef __cplusplus\n#define SW_EXTERN_C_BEGIN extern \"C\" {\n#define SW_EXTERN_C_END }\n#else\n#define SW_EXTERN_C_BEGIN\n#define SW_EXTERN_C_END\n#endif\n\n#ifndef _GNU_SOURCE\n#define _GNU_SOURCE\n#endif\n\n#ifndef _PTHREAD_PSHARED\n#define _PTHREAD_PSHARED\n#endif\n\n/*--- C standard library ---*/\n#include <cassert>\n#include <cctype>\n#include <cstdarg>\n#include <cstddef>\n#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n#include <climits>\n#include <unistd.h>\n#include <pthread.h>\n#include <inttypes.h>\n\n#include <sys/uio.h>\n#include <sys/utsname.h>\n\n#include <string>\n#include <memory>\n#include <list>\n#include <functional>\n#include <mutex>\n\ntypedef unsigned long ulong_t;\n\n#if defined(__GNUC__)\n#if __GNUC__ >= 3\n#define sw_inline inline __attribute__((always_inline))\n#else\n#define sw_inline inline\n#endif\n#elif defined(_MSC_VER)\n#define sw_inline __forceinline\n#else\n#define sw_inline inline\n#endif\n\n#if defined(__GNUC__) && __GNUC__ >= 4\n#define SW_API __attribute__((visibility(\"default\")))\n#else\n#define SW_API\n#endif\n\n#if !defined(__GNUC__) || __GNUC__ < 3\n#define __builtin_expect(x, expected_value) (x)\n#endif\n\n#define sw_likely(x) __builtin_expect(!!(x), 1)\n#define sw_unlikely(x) __builtin_expect(!!(x), 0)\n\n#define SW_START_LINE \"-------------------------START----------------------------\"\n#define SW_END_LINE \"--------------------------END-----------------------------\"\n#define SW_ECHO_RED \"\\e[31m%s\\e[0m\"\n#define SW_ECHO_GREEN \"\\e[32m%s\\e[0m\"\n#define SW_ECHO_YELLOW \"\\e[33m%s\\e[0m\"\n#define SW_ECHO_BLUE \"\\e[34m%s\\e[0m\"\n#define SW_ECHO_MAGENTA \"\\e[35m%s\\e[0m\"\n#define SW_ECHO_CYAN \"\\e[36m%s\\e[0m\"\n#define SW_ECHO_WHITE \"\\e[37m%s\\e[0m\"\n\n#define SW_ECHO_LEN_RED \"\\e[31m%.*s\\e[0m\"\n#define SW_ECHO_LEN_GREEN \"\\e[32m%.*s\\e[0m\"\n#define SW_ECHO_LEN_YELLOW \"\\e[33m%.*s\\e[0m\"\n#define SW_ECHO_LEN_BLUE \"\\e[34m%.*s\\e[0m\"\n#define SW_ECHO_LEN_MAGENTA \"\\e[35m%.*s\\e[0m\"\n#define SW_ECHO_LEN_CYAN \"\\e[36m%.*s\\e[0m\"\n#define SW_ECHO_LEN_WHITE \"\\e[37m%.*s\\e[0m\"\n\n#define SW_ECHO_RED_BG \"\\e[41m%s\\e[0m\"\n#define SW_ECHO_GREEN_BG \"\\e[42m%s\\e[0m\"\n\n#define SW_COLOR_RED 1\n#define SW_COLOR_GREEN 2\n#define SW_COLOR_YELLOW 3\n#define SW_COLOR_BLUE 4\n#define SW_COLOR_MAGENTA 5\n#define SW_COLOR_CYAN 6\n#define SW_COLOR_WHITE 7\n\n#define SW_SPACE ' '\n#define SW_CRLF \"\\r\\n\"\n#define SW_CRLF_LEN 2\n#define SW_ASCII_CODE_0 64\n#define SW_ASCII_CODE_Z 106\n/*----------------------------------------------------------------------------*/\n\n#include \"swoole_config.h\"\n#include \"swoole_version.h\"\n#include \"swoole_log.h\"\n#include \"swoole_atomic.h\"\n#include \"swoole_error.h\"\n\n#define SW_MAX(A, B) ((A) > (B) ? (A) : (B))\n#define SW_MIN(A, B) ((A) < (B) ? (A) : (B))\n#define SW_LOOP_N(n) for (decltype(n) i = 0; i < n; i++)\n#define SW_LOOP for (;;)\n\n#ifndef MAYBE_UNUSED\n#ifdef __GNUC__\n#define MAYBE_UNUSED __attribute__((used))\n#else\n#define MAYBE_UNUSED\n#endif\n#endif\n\n#ifndef MAX\n#define MAX(A, B) SW_MAX(A, B)\n#endif\n#ifndef MIN\n#define MIN(A, B) SW_MIN(A, B)\n#endif\n\n#define SW_NUM_BILLION (1000 * 1000 * 1000)\n#define SW_NUM_MILLION (1000 * 1000)\n\n#ifdef SW_DEBUG\n#define SW_ASSERT(e) assert(e)\n#define SW_ASSERT_1BYTE(v)                                                                                             \\\n    do {                                                                                                               \\\n        size_t i = 0, n = 0;                                                                                           \\\n        for (; i < sizeof(v); i++) {                                                                                   \\\n            n += ((v >> i) & 1) ? 1 : 0;                                                                               \\\n        }                                                                                                              \\\n        assert(n == 1);                                                                                                \\\n    } while (0)\n#else\n#define SW_ASSERT(e)\n#define SW_ASSERT_1BYTE(v)\n#endif\n#define SW_START_SLEEP usleep(100000)  // sleep 0.1s, wait fork and pthread_create\n\n#ifdef SW_THREAD\n#define SW_THREAD_LOCAL thread_local\nextern std::mutex sw_thread_lock;\n#else\n#define SW_THREAD_LOCAL\n#endif\n\n/**\n * API naming rules\n * -----------------------------------\n * - starts with swoole_, means it is ready or has been used as an external API\n * - starts with sw_, internal use only\n */\n\n/*-----------------------------------Memory------------------------------------*/\nvoid *sw_malloc(size_t size);\nvoid sw_free(void *ptr);\nvoid *sw_calloc(size_t nmemb, size_t size);\nvoid *sw_realloc(void *ptr, size_t size);\n\n// Evaluates to the number of elements in 'array'\n#define SW_ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))\n\n#define SW_DEFAULT_ALIGNMENT sizeof(unsigned long)\n#define SW_MEM_ALIGNED_SIZE(size) SW_MEM_ALIGNED_SIZE_EX(size, SW_DEFAULT_ALIGNMENT)\n#define SW_MEM_ALIGNED_SIZE_EX(size, alignment) (((size) + ((alignment) -1LL)) & ~((alignment) -1LL))\n\n/*-------------------------------Declare Struct--------------------------------*/\nnamespace swoole {\nclass MemoryPool;\nclass Reactor;\nclass String;\nclass Timer;\nstruct TimerNode;\nstruct Event;\nclass Pipe;\nclass MessageBus;\nclass Server;\nnamespace network {\nstruct Socket;\nstruct Address;\n}  // namespace network\nclass AsyncThreads;\n#ifdef SW_USE_IOURING\nclass Iouring;\n#endif\nnamespace async {\nclass ThreadPool;\n}\nstruct Protocol;\nstruct EventData;\nstruct DataHead;\ntypedef int (*ReactorHandler)(Reactor *reactor, Event *event);\ntypedef std::function<void(void *)> Callback;\ntypedef std::function<void(Timer *, TimerNode *)> TimerCallback;\n}  // namespace swoole\n\ntypedef swoole::DataHead swDataHead;\n\n/*----------------------------------String-------------------------------------*/\n\n#define SW_STRS(s) s, sizeof(s)\n#define SW_STRL(s) s, sizeof(s) - 1\n\n#define SW_STREQ(str, len, const_str) swoole_streq(str, len, SW_STRL(const_str))\n#define SW_STRCASEEQ(str, len, const_str) swoole_strcaseeq(str, len, SW_STRL(const_str))\n\n#define SW_STR_STARTS_WITH(str, len, const_sub_str) swoole_str_starts_with(str, len, SW_STRL(const_sub_str))\n#define SW_STR_ISTARTS_WITH(str, len, const_sub_str) swoole_str_istarts_with(str, len, SW_STRL(const_sub_str))\n\n#if defined(SW_USE_JEMALLOC) || defined(SW_USE_TCMALLOC)\n#define sw_strdup swoole_strdup\n#define sw_strndup swoole_strndup\n#else\n#define sw_strdup strdup\n#define sw_strndup strndup\n#endif\n\n/** always return less than size, zero termination  */\nsize_t sw_snprintf(char *buf, size_t size, const char *format, ...) __attribute__((format(printf, 3, 4)));\nsize_t sw_vsnprintf(char *buf, size_t size, const char *format, va_list args);\nint sw_printf(const char *format, ...);\nbool sw_wait_for(const std::function<bool()> &fn, int timeout_ms);\n\nstatic inline long sw_atol(const char *str) {\n    return std::strtol(str, nullptr, 10);\n}\n\nstatic inline int sw_atoi(const char *str) {\n    return static_cast<int>(sw_atol(str));\n}\n\nstatic inline void sw_memset_zero(void *s, size_t n) {\n    memset(s, '\\0', n);\n}\n\n#define sw_unset_bit(val, bit) val &= ~bit\n#define sw_set_bit(val, bit) val |= bit\n\nstatic inline int sw_mem_equal(const void *v1, size_t s1, const void *v2, size_t s2) {\n    return s1 == s2 && memcmp(v1, v2, s2) == 0;\n}\n\nstatic inline size_t swoole_strlcpy(char *dest, const char *src, size_t size) {\n    const size_t len = strlen(src);\n    if (size != 0) {\n        const size_t n = std::min(len, size - 1);\n        memcpy(dest, src, n);\n        dest[n] = '\\0';\n    }\n    return len;\n}\n\nstatic inline char *swoole_strdup(const char *s) {\n    size_t l = strlen(s) + 1;\n    char *p = static_cast<char *>(sw_malloc(l));\n    if (sw_likely(p)) {\n        memcpy(p, s, l);\n    }\n    return p;\n}\n\nstatic inline char *swoole_strndup(const char *s, const size_t n) {\n    char *p = static_cast<char *>(sw_malloc(n + 1));\n    if (sw_likely(p)) {\n        strncpy(p, s, n)[n] = '\\0';\n    }\n    return p;\n}\n\n/* string equal */\nstatic inline unsigned int swoole_streq(const char *str1, size_t len1, const char *str2, size_t len2) {\n    return (len1 == len2) && (strncmp(str1, str2, len1) == 0);\n}\n\nstatic inline unsigned int swoole_strcaseeq(const char *str1, size_t len1, const char *str2, size_t len2) {\n    return (len1 == len2) && (strncasecmp(str1, str2, len1) == 0);\n}\n\nstatic inline unsigned int swoole_str_starts_with(const char *pstr, size_t plen, const char *sstr, size_t slen) {\n    return (plen >= slen) && (strncmp(pstr, sstr, slen) == 0);\n}\n\nstatic inline unsigned int swoole_str_istarts_with(const char *pstr, size_t plen, const char *sstr, size_t slen) {\n    return (plen >= slen) && (strncasecmp(pstr, sstr, slen) == 0);\n}\n\nstatic inline const char *swoole_strnstr(const char *haystack,\n                                         uint32_t haystack_length,\n                                         const char *needle,\n                                         uint32_t needle_length) {\n    assert(needle_length > 0);\n\n    if (sw_likely(needle_length <= haystack_length)) {\n        for (uint32_t i = 0; i < haystack_length - needle_length + 1; i++) {\n            if ((haystack[0] == needle[0]) && (0 == memcmp(haystack, needle, needle_length))) {\n                return haystack;\n            }\n            haystack++;\n        }\n    }\n\n    return nullptr;\n}\n\nstatic inline const char *swoole_strncasestr(const char *haystack,\n                                             uint32_t haystack_length,\n                                             const char *needle,\n                                             uint32_t needle_length) {\n    assert(needle_length > 0);\n\n    if (sw_likely(needle_length <= haystack_length)) {\n        for (uint32_t i = 0; i < haystack_length - needle_length + 1; i++) {\n            if ((haystack[0] == needle[0]) && (0 == strncasecmp(haystack, needle, needle_length))) {\n                return haystack;\n            }\n            haystack++;\n        }\n    }\n\n    return nullptr;\n}\n\nstatic inline ssize_t swoole_strnpos(const char *haystack,\n                                     uint32_t haystack_length,\n                                     const char *needle,\n                                     uint32_t needle_length) {\n    assert(needle_length > 0);\n\n    const char *pos = swoole_strnstr(haystack, haystack_length, needle, needle_length);\n    return pos == nullptr ? -1 : pos - haystack;\n}\n\nstatic inline ssize_t swoole_strrnpos(const char *haystack, const char *needle, uint32_t length) {\n    uint32_t needle_length = strlen(needle);\n    assert(needle_length > 0);\n    haystack += (length - needle_length);\n\n    for (uint32_t i = length - needle_length; i > 0; i--) {\n        if ((haystack[0] == needle[0]) && (0 == memcmp(haystack, needle, needle_length))) {\n            return i;\n        }\n        haystack--;\n    }\n\n    return -1;\n}\n\nstatic inline void swoole_strtolower(char *str, const int length) {\n    char *c = str;\n    const char *e = c + length;\n\n    while (c < e) {\n        *c = static_cast<char>(tolower(*c));\n        c++;\n    }\n}\n\n/*--------------------------------Constants------------------------------------*/\nenum swResultCode {\n    SW_OK = 0,\n    SW_ERR = -1,\n};\n\nenum swReturnCode {\n    SW_SUCCESS = 0,\n    SW_CONTINUE = 1,\n    SW_WAIT = 2,\n    SW_CLOSE = 3,\n    SW_ERROR = 4,\n    SW_READY = 5,\n    SW_INVALID = 6,\n    SW_REDUCE_SIZE = 7,\n};\n\nenum swFdType {\n    SW_FD_SESSION,        // server stream session\n    SW_FD_STREAM_SERVER,  // server stream port\n    SW_FD_DGRAM_SERVER,   // server dgram port\n    SW_FD_PIPE,\n    SW_FD_STREAM,\n    SW_FD_AIO,\n    /**\n     * Coroutine Socket\n     */\n    SW_FD_CO_SOCKET,\n    /**\n     * socket poll fd [coroutine::socket_poll]\n     */\n    SW_FD_CO_POLL,\n    /**\n     * event waiter\n     */\n    SW_FD_CO_EVENT,\n    /**\n     * signalfd\n     */\n    SW_FD_SIGNAL,\n    SW_FD_DNS_RESOLVER,\n    SW_FD_CARES,\n    /**\n     * io_uring\n     */\n    SW_FD_IOURING,\n    /**\n     * SW_FD_USER or SW_FD_USER+n: for custom event\n     */\n    SW_FD_USER = 16,\n    SW_FD_STREAM_CLIENT,\n    SW_FD_DGRAM_CLIENT,\n};\n\nenum swSocketFlag {\n    SW_SOCK_NONBLOCK = 1 << 2,\n    SW_SOCK_CLOEXEC = 1 << 3,\n    SW_SOCK_SSL = (1u << 9),\n};\n\nenum swSocketType {\n    SW_SOCK_TCP = 1,\n    SW_SOCK_UDP = 2,\n    SW_SOCK_TCP6 = 3,\n    SW_SOCK_UDP6 = 4,\n    SW_SOCK_UNIX_STREAM = 5,  // unix sock stream\n    SW_SOCK_UNIX_DGRAM = 6,   // unix sock dgram\n    SW_SOCK_RAW = 7,\n    SW_SOCK_RAW6 = 8,\n};\n\nenum swTimeoutType {\n    SW_TIMEOUT_DNS = 1 << 0,\n    SW_TIMEOUT_CONNECT = 1 << 1,\n    SW_TIMEOUT_READ = 1 << 2,\n    SW_TIMEOUT_WRITE = 1 << 3,\n    SW_TIMEOUT_RDWR = SW_TIMEOUT_READ | SW_TIMEOUT_WRITE,\n    SW_TIMEOUT_ALL = SW_TIMEOUT_DNS | SW_TIMEOUT_CONNECT | SW_TIMEOUT_RDWR,\n};\n\nenum swEventType {\n    SW_EVENT_NULL = 0,\n    SW_EVENT_DEAULT = 1u << 8,\n    SW_EVENT_READ = 1u << 9,\n    SW_EVENT_WRITE = 1u << 10,\n    SW_EVENT_RDWR = SW_EVENT_READ | SW_EVENT_WRITE,\n    SW_EVENT_ERROR = 1u << 11,\n    SW_EVENT_ONCE = 1u << 12,\n};\n\nenum swForkType {\n    SW_FORK_SPAWN = 0,\n    SW_FORK_EXEC = 1 << 1,\n    SW_FORK_DAEMON = 1 << 2,\n    SW_FORK_PRECHECK = 1 << 3,\n};\n\nenum swTraverseOperation {\n    SW_TRAVERSE_KEEP = 0,\n    SW_TRAVERSE_REMOVE = 1,\n    SW_TRAVERSE_STOP = 2,\n};\n\n//------------------------------Base--------------------------------\n#ifndef uchar\ntypedef unsigned char uchar;\n#endif\n\n#define swoole_tolower(c) (uchar)((c >= 'A' && c <= 'Z') ? (c | 0x20) : c)\n#define swoole_toupper(c) (uchar)((c >= 'a' && c <= 'z') ? (c & ~0x20) : c)\n\n/**\n * This function appends a '\\0' at the end of the string,\n * so the allocated memory buffer must be len + 1.\n */\nvoid swoole_random_string(char *buf, size_t len);\nvoid swoole_random_string(std::string &str, size_t len);\nuint64_t swoole_random_int();\nsize_t swoole_random_bytes(char *buf, size_t size);\n\nstatic inline char *swoole_strlchr(char *p, const char *last, char c) {\n    while (p < last) {\n        if (*p == c) {\n            return p;\n        }\n        p++;\n    }\n    return nullptr;\n}\n\nstatic inline size_t swoole_size_align(size_t size, int pagesize) {\n    return size + (pagesize - (size % pagesize));\n}\n\n//------------------------------Base--------------------------------\nenum swEventDataFlag {\n    SW_EVENT_DATA_NORMAL,\n    SW_EVENT_DATA_PTR = 1u << 1,\n    SW_EVENT_DATA_CHUNK = 1u << 2,\n    SW_EVENT_DATA_BEGIN = 1u << 3,\n    SW_EVENT_DATA_END = 1u << 4,\n    SW_EVENT_DATA_OBJ_PTR = 1u << 5,\n    SW_EVENT_DATA_POP_PTR = 1u << 6,\n};\n\nenum swTaskFlag {\n    SW_TASK_TMPFILE = 1,\n    SW_TASK_SERIALIZE = 1u << 1,\n    SW_TASK_NONBLOCK = 1u << 2,\n    SW_TASK_CALLBACK = 1u << 3,\n    SW_TASK_WAITALL = 1u << 4,\n    SW_TASK_COROUTINE = 1u << 5,\n    SW_TASK_PEEK = 1u << 6,\n    SW_TASK_NOREPLY = 1u << 7,\n};\n\nenum swDNSLookupFlag {\n    SW_DNS_LOOKUP_RANDOM = (1u << 11),\n};\n\nextern thread_local char sw_error[SW_ERROR_MSG_SIZE];\n\nenum swPipeType {\n    SW_PIPE_WORKER = 0,\n    SW_PIPE_MASTER = 1,\n    SW_PIPE_READ = 0,\n    SW_PIPE_WRITE = 1,\n    SW_PIPE_NONBLOCK = 2,\n};\n\n//----------------------Tool Function---------------------\nuint32_t swoole_common_multiple(uint32_t u, uint32_t v);\nuint32_t swoole_common_divisor(uint32_t u, uint32_t v);\n\nint swoole_itoa(char *buf, long value);\nbool swoole_mkdir_recursive(const std::string &dir);\n\nint swoole_rand();\nint swoole_rand(int min, int max);\nint swoole_system_random(int min, int max);\n\nint swoole_version_compare(const char *version1, const char *version2);\nvoid swoole_print_backtrace();\nvoid swoole_print_backtrace_on_error();\nchar *swoole_string_format(size_t n, const char *format, ...);\nbool swoole_get_env(const char *name, int *value);\nint swoole_get_systemd_listen_fds();\n\nvoid swoole_init();\nvoid swoole_clean();\nvoid swoole_exit(int _status);\npid_t swoole_fork(int flags);\npid_t swoole_fork_exec(const std::function<void()> &child_fn);\npid_t swoole_waitpid(pid_t _pid, int *_stat_loc, int _options);\nvoid swoole_thread_init(bool main_thread = false);\nvoid swoole_thread_clean(bool main_thread = false);\nvoid swoole_redirect_stdout(int new_fd);\nvoid swoole_redirect_stdout(const char *file);\nint swoole_shell_exec(const char *command, pid_t *pid, bool get_error_stream);\nint swoole_daemon(int nochdir, int noclose);\nbool swoole_is_root_user();\nvoid swoole_set_isolation(const std::string &group_, const std::string &user_, const std::string &chroot_);\nbool swoole_set_task_tmpdir(const std::string &dir);\nvoid swoole_set_process_death_signal(int signal);\nconst std::string &swoole_get_task_tmpdir();\nint swoole_tmpfile(char *filename);\n\n#ifdef HAVE_CPU_AFFINITY\n#ifdef __FreeBSD__\n#include <sys/types.h>\n#include <sys/cpuset.h>\n#include <pthread_np.h>\ntypedef cpuset_t cpu_set_t;\n#endif\nint swoole_set_cpu_affinity(cpu_set_t *set);\nint swoole_get_cpu_affinity(cpu_set_t *set);\n#endif\n\nnamespace swoole {\ntypedef long SessionId;\ntypedef long TaskId;\ntypedef uint8_t ReactorId;\ntypedef uint32_t WorkerId;\ntypedef swEventType EventType;\ntypedef swSocketType SocketType;\ntypedef swTimeoutType TimeoutType;\ntypedef swFdType FdType;\ntypedef swReturnCode ReturnCode;\ntypedef swResultCode ResultCode;\n\nstruct Event {\n    int fd;\n    int16_t reactor_id;\n    FdType type;\n    network::Socket *socket;\n};\n\nstruct DataHead {\n    SessionId fd;\n    uint64_t msg_id;\n    uint32_t len;\n    int16_t reactor_id;\n    uint8_t type;\n    uint8_t flags;\n    uint16_t server_fd;\n    uint16_t ext_flags;\n    uint32_t reserved;\n    double time;\n    size_t dump(char *buf, size_t len);\n    void print();\n};\n\nstruct EventData {\n    DataHead info;\n    char data[SW_IPC_BUFFER_SIZE];\n\n    uint32_t size() const {\n        return sizeof(info) + len();\n    }\n\n    uint32_t len() const {\n        return info.len;\n    }\n};\n\nstruct ThreadGlobal {\n    uint16_t id;\n    uint8_t type;\n    bool main_thread;\n    int32_t error;\n    String *buffer_stack;\n    Reactor *reactor;\n    Timer *timer;\n    MessageBus *message_bus;\n    AsyncThreads *async_threads;\n#ifdef SW_USE_IOURING\n    Iouring *iouring;\n#endif\n    bool signal_blocking_all;\n};\n\nstruct Allocator {\n    void *(*malloc)(size_t size);\n    void *(*calloc)(size_t nmemb, size_t size);\n    void *(*realloc)(void *ptr, size_t size);\n    void (*free)(void *ptr);\n};\n\nstruct NameResolver {\n    enum Type {\n        TYPE_KERNEL,\n        TYPE_PHP,\n        TYPE_USER,\n    };\n    struct Context {\n        int type;\n        double timeout;\n        void *private_data;\n        bool with_port;\n        bool cluster_;\n        bool final_;\n        std::function<void(Context *ctx)> dtor;\n\n        ~Context() {\n            if (private_data && dtor) {\n                dtor(this);\n            }\n        }\n    };\n    std::function<std::string(const std::string &, Context *, void *)> resolve;\n    void *private_data;\n    Type type;\n};\n\nstruct DnsServer {\n    std::string host;\n    int port;\n};\n\nstruct Global {\n    uchar init : 1;\n    uchar running : 1;\n    uchar wait_signal : 1;\n    uchar enable_signalfd : 1;\n    /**\n     * Under macOS or FreeBSD, kqueue does not support listening for writable events on pipes. When a large amount of\n     * data is written to a pipe in process A, and the buffer becomes full, listening for writable events will not work.\n     * In process B, even after consuming the data from the pipe, the writable event in process A cannot be triggered.\n     * As a result, the functionality of Task and Process Server cannot be supported, making all scenarios relying on\n     * pipes for inter-process communication unable to function properly.\n     */\n    uchar enable_kqueue : 1;\n    uchar dns_lookup_random : 1;\n    uchar use_async_resolver : 1;\n    uchar use_name_resolver : 1;\n    uchar enable_coroutine : 1;\n    uchar print_backtrace_on_error : 1;\n\n    TaskId current_task_id;\n\n    int signal_fd;\n    bool signal_alarm;\n    bool signal_dispatch;\n    uint32_t signal_listener_num;\n    uint32_t signal_async_listener_num;\n\n    long trace_flags;\n\n    void (*fatal_error)(int code, const char *str, ...);\n    void (*print_backtrace)();\n\n    //-----------------------[System]--------------------------\n    uint16_t cpu_num;\n    uint32_t pagesize;\n    struct utsname uname;\n    uint32_t max_sockets;\n    uint32_t max_file_content;\n    //-----------------------[Memory]--------------------------\n    MemoryPool *memory_pool;\n    Allocator std_allocator;\n    std::string task_tmpfile;\n    //------------------[Single Instance]----------------------\n    Logger *logger;\n    Server *server;\n    FILE *stdout_;\n    //-----------------------[DNS]-----------------------------\n    DnsServer dns_server;\n    double dns_cache_refresh_time;\n    int dns_tries;\n    std::string dns_resolvconf_path;\n    std::string dns_hosts_path;\n    std::list<NameResolver> name_resolvers;\n    //-----------------------[AIO]----------------------------\n    uint32_t aio_core_worker_num;\n    uint32_t aio_worker_num;\n#ifdef SW_USE_IOURING\n    uint32_t iouring_entries = 0;\n    uint32_t iouring_workers = 0;\n    uint32_t iouring_flag = 0;\n#endif\n    double aio_max_wait_time;\n    double aio_max_idle_time;\n    network::Socket *aio_default_socket;\n    //-----------------------[Hook]--------------------------\n    void *hooks[SW_MAX_HOOK_TYPE];\n    std::function<bool(Reactor *reactor, size_t &event_num)> user_exit_condition;\n    // bug report message\n    std::string bug_report_message;\n};\n\nstd::string dirname(const std::string &file);\nvoid hook_add(void **hooks, int type, const Callback &func, int push_back);\nvoid hook_call(void **hooks, int type, void *arg);\ndouble microtime();\nvoid realtime_get(timespec *time);\nvoid realtime_add(timespec *time, int64_t add_msec);\n}  // namespace swoole\n\nextern swoole::Global SwooleG;                      // Local Global Variable\nextern thread_local swoole::ThreadGlobal SwooleTG;  // Thread Global Variable\n\n#define SW_CPU_NUM (SwooleG.cpu_num)\n\nstatic inline void swoole_set_last_error(int error) {\n    SwooleTG.error = error;\n}\n\nstatic inline int swoole_get_last_error() {\n    return SwooleTG.error;\n}\n\nstatic inline void swoole_clear_last_error() {\n    SwooleTG.error = 0;\n}\n\nvoid swoole_clear_last_error_msg();\nconst char *swoole_get_last_error_msg();\n\nstatic inline int swoole_get_thread_id() {\n    return SwooleTG.id;\n}\n\nstatic inline int swoole_get_thread_type() {\n    return SwooleTG.type;\n}\n\nstatic inline void swoole_set_thread_id(uint16_t id) {\n    SwooleTG.id = id;\n}\n\nstatic inline void swoole_set_thread_type(uint8_t type) {\n    SwooleTG.type = type;\n}\n\nstatic inline uint32_t swoole_pagesize() {\n    return SwooleG.pagesize;\n}\n\nSW_API const char *swoole_strerror(int code);\nSW_API void swoole_throw_error(int code);\nSW_API void swoole_ignore_error(int code);\nSW_API bool swoole_is_ignored_error(int code);\nSW_API bool swoole_is_main_thread();\nSW_API void swoole_set_log_level(int level);\nSW_API void swoole_set_log_file(const char *file);\nSW_API void swoole_set_trace_flags(long flags);\nSW_API void swoole_set_print_backtrace_on_error(bool enable = true);\nSW_API void swoole_set_stdout_stream(FILE *fp);\nSW_API void swoole_set_dns_server(const std::string &server);\nSW_API void swoole_set_hosts_path(const std::string &hosts_file);\nSW_API swoole::DnsServer swoole_get_dns_server();\nSW_API bool swoole_load_resolv_conf();\nSW_API void swoole_name_resolver_add(const swoole::NameResolver &resolver, bool append = true);\nSW_API void swoole_name_resolver_each(\n    const std::function<enum swTraverseOperation(const std::list<swoole::NameResolver>::iterator &iter)> &fn);\nSW_API std::string swoole_name_resolver_lookup(const std::string &host_name, swoole::NameResolver::Context *ctx);\nSW_API int swoole_get_log_level();\nSW_API FILE *swoole_get_stdout_stream();\n\nenum swEventInitFlag {\n    SW_EVENTLOOP_WAIT_EXIT = 1,\n};\n\n/**\n * manually_trigger:\n * Once enabled, the timer will no longer be triggered by event polling or the operating system's timer;\n * instead, it will be managed directly at the user space.\n */\nSW_API swoole::Timer *swoole_timer_create(bool manually_trigger = false);\nSW_API long swoole_timer_after(long ms, const swoole::TimerCallback &callback, void *private_data = nullptr);\nSW_API long swoole_timer_tick(long ms, const swoole::TimerCallback &callback, void *private_data = nullptr);\nSW_API swoole::TimerNode *swoole_timer_add(double ms,\n                                           bool persistent,\n                                           const swoole::TimerCallback &callback,\n                                           void *private_data = nullptr);\nSW_API swoole::TimerNode *swoole_timer_add(long ms,\n                                           bool persistent,\n                                           const swoole::TimerCallback &callback,\n                                           void *private_data = nullptr);\nSW_API bool swoole_timer_del(swoole::TimerNode *tnode);\nSW_API bool swoole_timer_exists(long timer_id);\nSW_API void swoole_timer_delay(swoole::TimerNode *tnode, long delay_ms);\nSW_API swoole::TimerNode *swoole_timer_get(long timer_id);\nSW_API bool swoole_timer_clear(long timer_id);\nSW_API void swoole_timer_free();\nSW_API void swoole_timer_select();\nSW_API int64_t swoole_timer_get_next_msec();\nSW_API bool swoole_timer_is_available();\n\nSW_API int swoole_event_init(int flags);\nSW_API int swoole_event_add(swoole::network::Socket *socket, int events);\nSW_API int swoole_event_set(swoole::network::Socket *socket, int events);\nSW_API int swoole_event_add_or_update(swoole::network::Socket *socket, int event);\nSW_API int swoole_event_del(swoole::network::Socket *socket);\nSW_API void swoole_event_defer(const swoole::Callback &cb, void *private_data);\nSW_API ssize_t swoole_event_write(swoole::network::Socket *socket, const void *data, size_t len);\nSW_API ssize_t swoole_event_writev(swoole::network::Socket *socket, const iovec *iov, size_t iovcnt);\nSW_API swoole::network::Socket *swoole_event_get_socket(int fd);\nSW_API int swoole_event_wait();\nSW_API int swoole_event_free();\nSW_API void swoole_event_set_handler(int fd_type, int event, swoole::ReactorHandler handler);\nSW_API bool swoole_event_isset_handler(int fd_type, int event);\nSW_API bool swoole_event_is_available();\nSW_API bool swoole_event_is_running();\n\nstatic sw_inline swoole::String *sw_tg_buffer() {\n    return SwooleTG.buffer_stack;\n}\n\nstatic sw_inline swoole::MemoryPool *sw_mem_pool() {\n    return SwooleG.memory_pool;\n}\n\nstatic sw_inline const swoole::Allocator *sw_std_allocator() {\n    return &SwooleG.std_allocator;\n}\n\nstatic sw_inline swoole::Reactor *sw_reactor() {\n    return SwooleTG.reactor;\n}\n\nstatic sw_inline swoole::Timer *sw_timer() {\n    return SwooleTG.timer;\n}\n"
  },
  {
    "path": "include/swoole_api.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef SW_C_API_H_\n#define SW_C_API_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"swoole_config.h\"\n\nenum swGlobalHookType {\n    SW_GLOBAL_HOOK_BEFORE_SERVER_START,\n    SW_GLOBAL_HOOK_BEFORE_CLIENT_START,\n    SW_GLOBAL_HOOK_BEFORE_WORKER_START,\n    SW_GLOBAL_HOOK_ON_CORO_START,\n    SW_GLOBAL_HOOK_ON_CORO_STOP,\n    SW_GLOBAL_HOOK_ON_REACTOR_CREATE,\n    SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN,\n    SW_GLOBAL_HOOK_AFTER_SERVER_SHUTDOWN,\n    SW_GLOBAL_HOOK_BEFORE_WORKER_STOP,\n    SW_GLOBAL_HOOK_ON_REACTOR_DESTROY,\n    SW_GLOBAL_HOOK_BEFORE_SERVER_CREATE,\n    SW_GLOBAL_HOOK_AFTER_SERVER_CREATE,\n    SW_GLOBAL_HOOK_AFTER_FORK,\n    SW_GLOBAL_HOOK_USER = 24,\n    SW_GLOBAL_HOOK_END = SW_MAX_HOOK_TYPE - 1,\n};\n\ntypedef void (*swHookFunc)(void *data);\n\nvoid swoole_add_hook(swGlobalHookType type, swHookFunc cb, int push_back);\nvoid swoole_call_hook(swGlobalHookType type, void *arg);\nbool swoole_isset_hook(swGlobalHookType type);\n\nconst char *swoole_version(void);\nint swoole_version_id(void);\nint swoole_api_version_id(void);\n\n#ifdef __cplusplus\n} /* end extern \"C\" */\n#endif\n#endif\n"
  },
  {
    "path": "include/swoole_asm_context.h",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2018 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#ifndef _SW_ASM_CONTEXT_H_\n#define _SW_ASM_CONTEXT_H_\n\n#ifdef SW_USE_ASM_CONTEXT\n\nSW_EXTERN_C_BEGIN\n\n#include <stdint.h>\n#include <stdio.h>\n#include <stddef.h>\n\ntypedef void *fcontext_t;\n\nstruct transfer_t {\n    fcontext_t fctx;\n    void *data;\n};\n\n#ifdef __GNUC__\n#define SW_GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__)\n#else\n#define SW_GCC_VERSION 0\n#endif\n\n#if defined(__GNUC__) && SW_GCC_VERSION >= 9000\n#define SW_INDIRECT_RETURN __attribute__((__indirect_return__))\n#else\n#define SW_INDIRECT_RETURN\n#endif\n\n#undef SWOOLE_CONTEXT_CALLDECL\n#if (defined(i386) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i586__) ||                \\\n     defined(__i686__) || defined(__X86__) || defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) ||          \\\n     defined(__INTEL__) || defined(__IA32__) || defined(_M_IX86) || defined(_I86_)) &&                                 \\\n    defined(BOOST_WINDOWS)\n#define SWOOLE_CONTEXT_CALLDECL __cdecl\n#else\n#define SWOOLE_CONTEXT_CALLDECL\n#endif\n\ntransfer_t SWOOLE_CONTEXT_CALLDECL swoole_jump_fcontext(fcontext_t to, void *vp);\nfcontext_t SWOOLE_CONTEXT_CALLDECL swoole_make_fcontext(void *stack, size_t stack_size, void (*fn)(transfer_t));\n\nSW_EXTERN_C_END\n\n#endif\n#endif\n"
  },
  {
    "path": "include/swoole_async.h",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2018 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#pragma once\n\n#include \"swoole_socket.h\"\n\n#include <vector>\n\n#ifndef O_DIRECT\n#define O_DIRECT 040000\n#endif\n\nnamespace swoole {\n\nenum AsyncFlag {\n    SW_AIO_WRITE_FSYNC = 1u << 1,\n    SW_AIO_EOF = 1u << 2,\n};\n\nstruct AsyncRequest {\n    virtual ~AsyncRequest() = default;\n};\n\nstruct AsyncEvent {\n    size_t task_id;\n    uint8_t canceled;\n    int error;\n    /**\n     * input & output\n     */\n    std::shared_ptr<AsyncRequest> data;\n    /**\n     * output\n     */\n    ssize_t retval;\n    /**\n     * internal use only\n     */\n    network::Socket *pipe_socket;\n    double timestamp;\n    void *object;\n    void (*handler)(AsyncEvent *event);\n    void (*callback)(AsyncEvent *event);\n\n    bool catch_error() const {\n        return (error == SW_ERROR_AIO_TIMEOUT || error == SW_ERROR_AIO_CANCELED);\n    }\n};\n\nstruct GethostbynameRequest : AsyncRequest {\n    std::string name;\n    int family;\n    std::string addr;\n\n    GethostbynameRequest(std::string _name, int _family);\n    ~GethostbynameRequest() override = default;\n};\n\nstruct GetaddrinfoRequest : public AsyncRequest {\n    std::string hostname;\n    std::string service;\n    int family;\n    int socktype;\n    int protocol;\n    int error;\n    std::vector<sockaddr_in6> results;\n    int count;\n\n    void parse_result(std::vector<std::string> &retval) const;\n\n    GetaddrinfoRequest(std::string _hostname, int _family, int _socktype, int _protocol, std::string _service);\n    ~GetaddrinfoRequest() override = default;\n};\n\nclass AsyncThreads {\n  public:\n    size_t task_num = 0;\n    Pipe *pipe = nullptr;\n    std::shared_ptr<async::ThreadPool> pool;\n    network::Socket *read_socket = nullptr;\n    network::Socket *write_socket = nullptr;\n\n    AsyncThreads();\n    ~AsyncThreads();\n\n    size_t get_task_num() const {\n        return task_num;\n    }\n\n    size_t get_queue_size() const;\n    size_t get_worker_num() const;\n    void notify_one() const;\n\n    static int callback(Reactor *reactor, Event *event);\n};\n\nnamespace async {\n\ntypedef void (*Handler)(AsyncEvent *event);\n\nAsyncEvent *dispatch(const AsyncEvent *request);\n\nvoid handler_gethostbyname(AsyncEvent *event);\nvoid handler_getaddrinfo(AsyncEvent *event);\n\n}  // namespace async\n};  // namespace swoole\n\nswoole::AsyncThreads *sw_async_threads();\n"
  },
  {
    "path": "include/swoole_atomic.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\ntypedef volatile int32_t sw_atomic_int32_t;\ntypedef volatile uint32_t sw_atomic_uint32_t;\ntypedef volatile int64_t sw_atomic_int64_t;\ntypedef volatile uint64_t sw_atomic_uint64_t;\n\ntypedef sw_atomic_int64_t sw_atomic_long_t;\ntypedef sw_atomic_uint64_t sw_atomic_ulong_t;\ntypedef sw_atomic_uint32_t sw_atomic_t;\n\n#define sw_atomic_cmp_set(lock, old, set) __sync_bool_compare_and_swap(lock, old, set)\n#define sw_atomic_value_cmp_set(value, expected, set) __sync_val_compare_and_swap(value, expected, set)\n#define sw_atomic_fetch_add(value, add) __sync_fetch_and_add(value, add)\n#define sw_atomic_fetch_sub(value, sub) __sync_fetch_and_sub(value, sub)\n#define sw_atomic_memory_barrier() __sync_synchronize()\n#define sw_atomic_add_fetch(value, add) __sync_add_and_fetch(value, add)\n#define sw_atomic_sub_fetch(value, sub) __sync_sub_and_fetch(value, sub)\n\n#if defined(__x86_64__)\n#define sw_atomic_cpu_pause() __asm__ __volatile__(\"pause\")\n#elif defined(__aarch64__)\n#define sw_atomic_cpu_pause() __asm__ __volatile__(\"yield\")\n#else\n#define sw_atomic_cpu_pause()\n#endif\n\nvoid sw_spinlock(sw_atomic_t *lock);\n#define sw_spinlock_release(lock) __sync_lock_release(lock)\nint sw_atomic_futex_wait(sw_atomic_t *atomic, double timeout);\nint sw_atomic_futex_wakeup(sw_atomic_t *atomic, int n);\n"
  },
  {
    "path": "include/swoole_base64.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include <cstdint>\n#include <cstdio>\n#include <cstring>\n\n#define BASE64_ENCODE_OUT_SIZE(s) (((s) + 2) / 3 * 4)\n#define BASE64_DECODE_OUT_SIZE(s) (((s)) / 4 * 3)\n\nnamespace swoole {\nsize_t base64_encode(const unsigned char *in, size_t inlen, char *out);\nsize_t base64_decode(const char *in, size_t inlen, char *out);\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_buffer.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include <queue>\n#include <sys/uio.h>\n\nnamespace swoole {\n\nstruct BufferChunk {\n    enum Type {\n        TYPE_DATA,\n        TYPE_SENDFILE,\n        TYPE_CLOSE,\n    };\n\n    Type type;\n    uint32_t length = 0;\n    uint32_t offset = 0;\n    union {\n        char *str;\n        void *ptr;\n        uint32_t u32;\n        uint64_t u64;\n    } value{};\n    uint32_t size = 0;\n\n    BufferChunk(Type type, uint32_t size);\n    ~BufferChunk();\n\n    void (*destroy)(BufferChunk *chunk) = nullptr;\n};\n\nclass Buffer {\n    // 0: don't use chunk\n    uint32_t chunk_size;\n    uint32_t total_length = 0;\n    std::queue<BufferChunk *> queue_;\n\n  public:\n    explicit Buffer(uint32_t _chunk_size);\n    ~Buffer();\n\n    BufferChunk *alloc(BufferChunk::Type type, uint32_t size);\n\n    BufferChunk *front() const {\n        return queue_.front();\n    }\n\n    void pop();\n    void append(const char *data, uint32_t size);\n    void append(const struct iovec *iov, size_t iovcnt, off_t offset);\n\n    uint32_t length() const {\n        return total_length;\n    }\n\n    size_t count() const {\n        return queue_.size();\n    }\n\n    bool empty() const {\n        return queue_.empty();\n    }\n\n    static bool empty(const Buffer *buffer) {\n        return buffer == nullptr || buffer->queue_.empty();\n    }\n};\n\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_channel.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Xinyu Zhu  <xyzhu1120@gmail.com>                             |\n  |         shiguangqi <shiguangqi2008@gmail.com>                        |\n  |         Twosee  <twose@qq.com>                                       |\n  |         Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n */\n\n#pragma once\n\n#include \"swoole_lock.h\"\n\nnamespace swoole {\n\nenum ChannelFlag {\n    SW_CHAN_LOCK = 1u << 1,\n    SW_CHAN_NOTIFY = 1u << 2,\n    SW_CHAN_SHM = 1u << 3,\n};\n\nstruct Channel {\n    off_t head;\n    off_t tail;\n    size_t size;\n    char head_tag;\n    char tail_tag;\n    int num;\n    int max_num;\n    /**\n     * Data length, excluding structure\n     */\n    size_t bytes;\n    int flags;\n    int maxlen;\n    /**\n     * memory point\n     */\n    void *mem;\n    Lock *lock;\n    Pipe *notify_pipe;\n\n    bool empty() const {\n        return num == 0;\n    }\n    bool full() const {\n        return ((head == tail && tail_tag != head_tag) || (bytes + sizeof(int) * num == size));\n    }\n    int pop(void *out_buf, int buffer_length);\n    int push(const void *in_data, int data_length);\n    int out(void *out_buf, int buffer_length);\n    int in(const void *in_data, int data_length);\n    int peek(void *out, int buffer_length) const;\n    int wait() const;\n    int notify() const;\n    void destroy();\n    void print() const;\n    int count() const {\n        return num;\n    }\n    size_t get_bytes() const {\n        return bytes;\n    }\n    static Channel *make(size_t size, size_t maxlen, int flags);\n};\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_client.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_string.h\"\n#include \"swoole_socket.h\"\n#include \"swoole_reactor.h\"\n#include \"swoole_protocol.h\"\n#include \"swoole_proxy.h\"\n\nnamespace swoole {\nnamespace network {\n\nclass Client {\n    int (*connect_)(Client *cli, const char *host, int port, double timeout, int sock_flag) = nullptr;\n    ssize_t (*send_)(Client *cli, const char *data, size_t length, int flags) = nullptr;\n    int (*sendfile_)(Client *cli, const char *filename, off_t offset, size_t length) = nullptr;\n    ssize_t (*recv_)(Client *cli, char *data, size_t length, int flags) = nullptr;\n\n  public:\n    int id = 0;\n    long timeout_id = 0;  // timeout node id\n    int sock_type_ = 0;\n    int sock_domain_ = 0;\n    int sock_flags_ = 0;\n    int protocol_ = 0;\n    FdType fd_type;\n    bool active = false;\n    bool async = false;\n    bool keep = false;\n    bool http2 = false;\n    bool sleep_ = false;\n    bool wait_dns = false;\n    bool dns_completed = false;\n    bool host_preseted = false;\n    bool shutdown_rw = false;\n    bool shutdown_read = false;\n    bool shutdown_write = false;\n    bool remove_delay = false;\n    bool closed = false;\n    bool high_watermark = false;\n    bool async_connect = false;\n    bool onerror_called = false;\n\n    /**\n     * one package: length check\n     */\n    bool open_length_check = false;\n    bool open_eof_check = false;\n\n    Protocol protocol = {};\n    std::unique_ptr<Socks5Proxy> socks5_proxy = nullptr;\n    std::unique_ptr<HttpProxy> http_proxy = nullptr;\n\n    uint32_t reuse_count = 0;\n\n    std::string server_id;\n    std::string server_host;\n    int server_port = 0;\n    void *ptr = nullptr;\n    void *params = nullptr;\n\n    TimerNode *timer = nullptr;\n\n    /**\n     * for connect()/sendto()\n     */\n    Address server_addr = {};\n    /**\n     * for recvfrom()\n     */\n    Address remote_addr = {};\n\n    Socket *socket;\n\n    void *object = nullptr;\n\n    String *buffer = nullptr;\n    uint32_t wait_length = 0;\n    uint32_t input_buffer_size = 0;\n\n    uint32_t buffer_high_watermark = 0;\n    uint32_t buffer_low_watermark = 0;\n\n    bool open_ssl = false;\n    bool ssl_wait_handshake = false;\n    std::shared_ptr<SSLContext> ssl_context = nullptr;\n\n    std::function<void(Client *cli)> onConnect = nullptr;\n    std::function<void(Client *cli)> onError = nullptr;\n    std::function<void(Client *cli, const char *, size_t)> onReceive = nullptr;\n    std::function<void(Client *cli)> onClose = nullptr;\n    std::function<void(Client *cli)> onBufferFull = nullptr;\n    std::function<void(Client *cli)> onBufferEmpty = nullptr;\n\n    static void init_reactor(Reactor *reactor);\n    Client(SocketType _type, bool async);\n    ~Client();\n\n    Socket *get_socket() const {\n        return socket;\n    }\n\n    bool ready() const {\n        return socket != nullptr;\n    }\n\n    SocketType get_socket_type() const {\n        return socket->socket_type;\n    }\n\n    const std::string &get_http_proxy_host_name() const {\n        if (ssl_context && !ssl_context->tls_host_name.empty()) {\n            return ssl_context->tls_host_name;\n        }\n        return http_proxy->target_host;\n    }\n\n    int connect(const char *_host, int _port, double _timeout = -1, int _sock_flag = 0) {\n        return connect_(this, _host, _port, _timeout, _sock_flag);\n    }\n\n    ssize_t send(const char *_data, size_t _length, int _flags = 0) {\n        return send_(this, _data, _length, _flags);\n    }\n\n    int sendfile(const char *_filename, off_t _offset = 0, size_t _length = 0) {\n        return sendfile_(this, _filename, _offset, _length);\n    }\n\n    ssize_t recv(char *_data, size_t _length, int _flags = 0) {\n        return recv_(this, _data, _length, _flags);\n    }\n\n    int bind(const std::string &addr, int port) const;\n    int sleep();\n    int wakeup();\n    int sendto(const std::string &host, int port, const char *data, size_t len) const;\n    int get_peer_name(Address *addr) const;\n    int shutdown(int _how = SHUT_RDWR);\n    int close();\n    bool socks5_handshake(const char *recv_data, size_t length);\n    void set_timeout(double timeout, TimeoutType type = SW_TIMEOUT_ALL) const;\n    bool has_timedout() const;\n    void set_socks5_proxy(const std::string &host, int port, const std::string &user = \"\", const std::string &pwd = \"\");\n    void set_http_proxy(const std::string &host, int port, const std::string &user = \"\", const std::string &pwd = \"\");\n\n    int enable_ssl_encrypt();\n#ifdef SW_SUPPORT_DTLS\n    void enable_dtls();\n#endif\n    int ssl_handshake() const;\n    int ssl_verify(int allow_self_signed) const;\n\n    bool set_ssl_key_file(const std::string &file) const {\n        return ssl_context->set_key_file(file);\n    }\n\n    bool set_ssl_cert_file(const std::string &file) const {\n        return ssl_context->set_cert_file(file);\n    }\n\n    void set_ssl_cafile(const std::string &file) const {\n        ssl_context->cafile = file;\n    }\n\n    void set_ssl_capath(const std::string &path) const {\n        ssl_context->capath = path;\n    }\n\n    void set_ssl_passphrase(const std::string &str) const {\n        ssl_context->passphrase = str;\n    }\n\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n    void set_tls_host_name(const std::string &str) const {\n        ssl_context->tls_host_name = str;\n        // if user set empty ssl_host_name, disable it, otherwise the underlying may set it automatically\n        ssl_context->disable_tls_host_name = ssl_context->tls_host_name.empty();\n    }\n#endif\n\n    void set_ssl_dhparam(const std::string &file) const {\n        ssl_context->dhparam = file;\n    }\n\n    void set_ssl_ecdh_curve(const std::string &str) const {\n        ssl_context->ecdh_curve = str;\n    }\n\n    void set_ssl_protocols(long protocols) const {\n        ssl_context->protocols = protocols;\n    }\n\n    void set_ssl_disable_compress(bool value) const {\n        ssl_context->disable_compress = value;\n    }\n\n    void set_ssl_verify_peer(bool value) const {\n        ssl_context->verify_peer = value;\n    }\n\n    void set_ssl_allow_self_signed(bool value) const {\n        ssl_context->allow_self_signed = value;\n    }\n\n    void set_ssl_verify_depth(uint8_t value) const {\n        ssl_context->verify_depth = value;\n    }\n\n    void set_ssl_ciphers(const std::string &str) const {\n        ssl_context->ciphers = str;\n    }\n\n#ifdef OPENSSL_IS_BORINGSSL\n    void set_ssl_grease(uint8_t value) const {\n        ssl_context->grease = value;\n    }\n#endif\n\n    const std::string &get_ssl_cert_file() const {\n        return ssl_context->cert_file;\n    }\n\n    const std::string &get_ssl_key_file() const {\n        return ssl_context->key_file;\n    }\n};\n\n//----------------------------------------Stream---------------------------------------\nclass Stream {\n  public:\n    String *buffer = nullptr;\n    Client client;\n    bool connected = false;\n    bool cancel = false;\n    int errCode = 0;\n    void *private_data = nullptr;\n    void *private_data_2 = nullptr;\n    long private_data_fd = -1;\n\n    std::function<void(Stream *stream, const char *data, uint32_t length)> response = nullptr;\n\n    int send(const char *data, size_t length);\n    void set_max_length(uint32_t max_length);\n    ~Stream();\n\n    static Stream *create(const char *dst_host, int dst_port, SocketType type);\n    static ssize_t recv_sync(Socket *sock, void *_buf, size_t _len);\n    static void set_protocol(Protocol *protocol);\n\n  private:\n    Stream(const char *dst_host, int dst_port, SocketType type);\n};\n//----------------------------------------Stream End------------------------------------\n\nclass SyncClient {\n  protected:\n    Client client;\n    bool connected = false;\n    bool created;\n    bool async = false;\n    SocketType type;\n\n  public:\n    explicit SyncClient(SocketType _type, bool _async = false) : client(_type, _async), async(_async), type(_type) {\n        created = client.socket != nullptr;\n    }\n\n    virtual bool connect(const char *host, int port, double timeout = -1) {\n        if (connected || !created) {\n            return false;\n        }\n        if (client.connect(host, port, timeout, client.socket->is_dgram()) < 0) {\n            return false;\n        }\n        connected = true;\n        return true;\n    }\n\n    void set_stream_protocol() {\n        client.open_length_check = true;\n        Stream::set_protocol(&client.protocol);\n    }\n\n    void set_package_max_length(uint32_t max_length) {\n        client.protocol.package_max_length = max_length;\n    }\n\n    bool enable_ssl_encrypt() {\n        if (client.enable_ssl_encrypt() < 0) {\n            return false;\n        }\n        if (connected) {\n            return client.ssl_handshake() == SW_OK;\n        } else {\n            return true;\n        }\n    }\n\n    ssize_t send(const std::string &data) {\n        return client.send(data.c_str(), data.length(), 0);\n    }\n\n    ssize_t send(const char *buf, size_t len) {\n        return client.send(buf, len, 0);\n    }\n\n    bool sendfile(const char *filename, off_t offset = 0, size_t length = 0) {\n        return client.sendfile(filename, offset, length) == SW_OK;\n    }\n\n    ssize_t recv(char *buf, size_t len, int flags = 0) {\n        return client.recv(buf, len, flags);\n    }\n\n    bool close() {\n        if (!created || client.closed) {\n            return false;\n        }\n        client.close();\n        created = false;\n        return true;\n    }\n\n    Client *get_client() {\n        return &client;\n    }\n\n    virtual ~SyncClient() {\n        if (created) {\n            close();\n        }\n    }\n};\n\nclass AsyncClient : public SyncClient {\n  protected:\n    std::function<void(AsyncClient *)> _onConnect = nullptr;\n    std::function<void(AsyncClient *)> _onError = nullptr;\n    std::function<void(AsyncClient *)> _onClose = nullptr;\n    std::function<void(AsyncClient *, const char *data, size_t length)> _onReceive = nullptr;\n\n  public:\n    explicit AsyncClient(SocketType _type) : SyncClient(_type, true) {}\n\n    bool connect(const char *host, int port, double timeout = -1) override {\n        client.object = this;\n        client.onConnect = [](Client *cli) {\n            auto *ac = (AsyncClient *) cli->object;\n            ac->_onConnect(ac);\n        };\n        client.onError = [](Client *cli) {\n            auto *ac = (AsyncClient *) cli->object;\n            ac->_onError(ac);\n        };\n        client.onClose = [](Client *cli) {\n            auto *ac = (AsyncClient *) cli->object;\n            ac->_onClose(ac);\n        };\n        client.onReceive = [](Client *cli, const char *data, size_t length) {\n            auto *ac = (AsyncClient *) cli->object;\n            ac->_onReceive(ac, data, length);\n        };\n        return SyncClient::connect(host, port, timeout);\n    }\n\n    void on_connect(const std::function<void(AsyncClient *)> &fn) {\n        _onConnect = fn;\n    }\n\n    void on_error(const std::function<void(AsyncClient *)> &fn) {\n        _onError = fn;\n    }\n\n    void on_close(const std::function<void(AsyncClient *)> &fn) {\n        _onClose = fn;\n    }\n\n    void on_receive(const std::function<void(AsyncClient *, const char *data, size_t length)> &fn) {\n        _onReceive = fn;\n    }\n};\n\n}  // namespace network\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_config.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n#ifndef SWOOLE_CONFIG_H_\n#define SWOOLE_CONFIG_H_\n\n#ifndef __clang__\n// gcc version check\n#if defined(__GNUC__) && (__GNUC__ < 3 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8))\n#error \"GCC 4.8 or later required\"\n#endif\n#endif\n\n#define SW_MAX_FDTYPE 32  // 32 kinds of event\n#define SW_MAX_HOOK_TYPE 32\n#define SW_ERROR_MSG_SIZE 16384\n#define SW_MAX_FILE_CONTENT (64 * 1024 * 1024)  // for swoole_file_get_contents\n#define SW_MAX_LISTEN_PORT 60000\n#define SW_MAX_CONNECTION 100000\n#define SW_MAX_CONCURRENT_TASK 1024\n#define SW_STACK_BUFFER_SIZE 65536\n\n#ifdef HAVE_MALLOC_TRIM\n#define SW_USE_MALLOC_TRIM 1\n#endif\n#define SW_MALLOC_TRIM_INTERVAL 60\n#define SW_MALLOC_TRIM_PAD 0\n\n#define SW_MAX_SOCKETS_DEFAULT 1024\n\n#define SW_SOCKET_BUFFER_SIZE 8388608\n#define SW_SOCKET_RETRY_COUNT 10\n\n#define SW_SOCKET_DEFAULT_DNS_TIMEOUT 60\n#define SW_SOCKET_DEFAULT_CONNECT_TIMEOUT 10\n#define SW_SOCKET_DEFAULT_READ_TIMEOUT 60\n#define SW_SOCKET_DEFAULT_WRITE_TIMEOUT 60\n#define SW_SOCKET_CORK_MIN_SIZE 65536\n\n#define SW_SYSTEMD_FDS_START 3\n\n#define SW_GLOBAL_MEMORY_PAGESIZE (2 * 1024 * 1024)  // global memory page\n\n#define SW_MAX_THREAD_NCPU 4     // n * cpu_num\n#define SW_MAX_WORKER_NCPU 1000  // n * cpu_num\n\n#define SW_HOST_MAXSIZE                                                                                                \\\n    sizeof(((struct sockaddr_un *) NULL)->sun_path)  // Linux has 108 UNIX_PATH_MAX, but BSD/macOS limit is only 104\n\n#define SW_CLIENT_BUFFER_SIZE 65536\n#define SW_CLIENT_CONNECT_TIMEOUT 0.5\n\n// !!!Don't modify.----------------------------------------------------------\n#ifdef __MACH__\n#define SW_IPC_MAX_SIZE 2048  // MacOS\n#else\n#define SW_IPC_MAX_SIZE 8192  // for IPC, dgram and message-queue max size\n#endif\n#define SW_IPC_BUFFER_MAX_SIZE (64 * 1024)\n#define SW_IPC_BUFFER_SIZE (SW_IPC_MAX_SIZE - sizeof(swoole::DataHead))\n#define SW_IPC_MSG_MIN (2048 - sizeof(swoole::DataHead))\n// !!!End.-------------------------------------------------------------------\n\n#define SW_BUFFER_SIZE_STD 8192\n#define SW_BUFFER_SIZE_BIG 65536\n#define SW_BUFFER_SIZE_UDP 65536\n\n#define SW_SENDFILE_CHUNK_SIZE 65536\n\n#define SW_DATA_EOF \"\\r\\n\\r\\n\"\n#define SW_DATA_EOF_MAXLEN 8\n\n#define SW_TASKWAIT_TIMEOUT 0.5\n\n#define SW_AIO_THREAD_NUM_MULTIPLE 8\n#define SW_AIO_THREAD_MAX_IDLE_TIME 1.0\n#define SW_AIO_TASK_MAX_WAIT_TIME 0.001\n#define SW_AIO_EVENT_NUM 128\n\n#define SW_WORKER_MAX_WAIT_TIME 3\n#define SW_WORKER_MIN_REQUEST 10\n#define SW_WORKER_MAX_RECV_CHUNK_COUNT 32\n\n#define SW_REACTOR_MAXEVENTS 4096\n\n#define SW_SESSION_LIST_SIZE (1 * 1024 * 1024)\n\n#define SW_MSGMAX 65536\n#define SW_MESSAGE_BOX_SIZE 65536\n\n/**\n * The maximum number of Reactor threads\n * the number of the CPU cores threads will be started by default\n * number 8 is the maximum\n */\n#define SW_REACTOR_MAX_THREAD 8\n\n#define SW_INPUT_BUFFER_SIZE (2 * 1024 * 1024)\n#define SW_BUFFER_MIN_SIZE 65536\n#define SW_SEND_BUFFER_SIZE 65536\n\n#define SW_BACKLOG 512\n\n/**\n * max accept times for single time\n */\n#define SW_ACCEPT_MAX_COUNT 64\n#define SW_ACCEPT_RETRY_TIME 1.0\n\n#define SW_TCP_KEEPCOUNT 5\n#define SW_TCP_KEEPIDLE 3600  // 1 hour\n#define SW_TCP_KEEPINTERVAL 60\n\n#define SW_TASK_TMP_PATH_SIZE 256\n#define SW_TASK_TMP_DIR \"/tmp\"\n#define SW_TASK_TMP_FILE \"swoole.task.XXXXXX\"\n\n#define SW_FILE_CHUNK_SIZE 65536\n\n#define SW_TABLE_CONFLICT_PROPORTION 0.2  // 20%\n#define SW_TABLE_KEY_SIZE 64\n\n#define SW_SSL_BUFFER_SIZE 16384\n#define SW_SSL_CIPHER_LIST \"EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH\"\n#define SW_SSL_ECDH_CURVE \"auto\"\n\n#define SW_SPINLOCK_LOOP_N 1024\n#define SW_FILE_LOCK_DEFAULT_SECOND 0.001\n#define SW_FILE_LOCK_MAX_SECOND 0.1\n\n#define SW_SIGNO_MAX 128\n#define SW_UNREGISTERED_SIGNAL_FMT \"Unable to find callback function for signal %s\"\n\n#define SW_DNS_HOST_BUFFER_SIZE 16\n#define SW_DNS_SERVER_PORT 53\n#define SW_DNS_RESOLV_CONF \"/etc/resolv.conf\"\n\n#define SW_Z_BEST_SPEED 1\n#define SW_COMPRESSION_MIN_LENGTH_DEFAULT 20\n\n#ifndef IOV_MAX\n#define IOV_MAX 16\n#endif\n\n#define SW_IOV_MAX_ERROR_MSG \"The maximum of iov count is %d\"\n\n#define SW_IOURING_QUEUE_SIZE 8192\n#define SW_IOURING_SQE_BATCH_SIZE 16\n\n/**\n * HTTP Protocol\n */\n#define SW_HTTP_SERVER_SOFTWARE \"swoole-http-server\"\n#define SW_HTTP_SERVER_BOUNDARY_PREKEY \"SwooleBoundary\"\n#define SW_HTTP_SERVER_BOUNDARY_TOTAL_SIZE 39\n#define SW_HTTP_SERVER_PART_HEADER 256\n#define SW_HTTP_PARAM_MAX_NUM 128\n#define SW_HTTP_FORM_KEYLEN 512\n#define SW_HTTP_RESPONSE_INIT_SIZE 65536\n#define SW_HTTP_HEADER_MAX_SIZE 65536\n#define SW_HTTP_HEADER_KEY_SIZE 128\n#define SW_HTTP_UPLOAD_TMPDIR_SIZE SW_TASK_TMP_PATH_SIZE\n#define SW_HTTP_DATE_FORMAT \"D, d M Y H:i:s T\"\n#define SW_HTTP_RFC1123_DATE_GMT \"%a, %d %b %Y %T GMT\"\n#define SW_HTTP_RFC1123_DATE_UTC \"%a, %d %b %Y %T UTC\"\n#define SW_HTTP_RFC850_DATE \"%A, %d-%b-%y %T GMT\"\n#define SW_HTTP_ASCTIME_DATE \"%a %b %e %T %Y\"\n#define SW_HTTP_UPLOAD_FILE \"Swoole-Upload-File\"\n#define SW_HTTP_CHUNK_EOF \"0\\r\\n\\r\\n\"\n#define SW_HTTP_DEFAULT_CONTENT_TYPE \"text/html\"\n#define SW_HTTP_MAX_APPEND_DATA 16384\n\n#define SW_HTTP_100_CONTINUE_PACKET \"HTTP/1.1 100 Continue\\r\\n\\r\\n\"\n#define SW_HTTP_BAD_REQUEST_PACKET \"HTTP/1.1 400 Bad Request\\r\\n\\r\\n\"\n#define SW_HTTP_REQUEST_ENTITY_TOO_LARGE_PACKET \"HTTP/1.1 413 Request Entity Too Large\\r\\n\\r\\n\"\n#define SW_HTTP_SERVICE_UNAVAILABLE_PACKET \"HTTP/1.1 503 Service Unavailable\\r\\n\\r\\n\"\n\n#define SW_HTTP_PAGE_CSS                                                                                               \\\n    \"<style> \\\nbody { padding: 0.5em; line-height: 2; } \\\nh1 { font-size: 1.5em; padding-bottom: 0.3em; border-bottom: 1px solid #ccc; } \\\nul { list-style-type: disc; } \\\nfooter { border-top: 1px solid #ccc; } \\\na { color: #0969da; } \\\n</style>\"\n\n#define SW_HTTP_POWER_BY \"<footer><i>Powered by Swoole</i></footer>\"\n\n#define SW_HTTP_PAGE_400                                                                                               \\\n    \"<html><body>\" SW_HTTP_PAGE_CSS \"<h1>HTTP 400 Bad Request</h1>\" SW_HTTP_POWER_BY \"</body></html>\"\n#define SW_HTTP_PAGE_404 \"<html><body>\" SW_HTTP_PAGE_CSS \"<h1>HTTP 404 Not Found</h1>\" SW_HTTP_POWER_BY \"</body></html>\"\n#define SW_HTTP_PAGE_500                                                                                               \\\n    \"<html><body>\" SW_HTTP_PAGE_CSS \"<h1>HTTP 500 Internal Server Error</h1>\" SW_HTTP_POWER_BY \"</body></html>\"\n\n/**\n * HTTP2 Protocol\n */\n#define SW_HTTP2_DATA_BUFFER_SIZE 8192\n#define SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE (1 << 12)\n#define SW_HTTP2_DEFAULT_MAX_CONCURRENT_STREAMS UINT_MAX\n#define SW_HTTP2_DEFAULT_ENABLE_PUSH 0\n#define SW_HTTP2_DEFAULT_MAX_FRAME_SIZE (1u << 14)\n#define SW_HTTP2_DEFAULT_INIT_WINDOW_SIZE ((1 << 16) - 1)\n#define SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE UINT_MAX\n\n#define SW_HTTP_CLIENT_USERAGENT \"swoole-http-client\"\n#define SW_HTTP_CLIENT_BOUNDARY_PREKEY \"----SwooleBoundary\"\n#define SW_HTTP_CLIENT_BOUNDARY_TOTAL_SIZE 39\n#define SW_HTTP_FORM_RAW_DATA_FMT \"--%.*s\\r\\nContent-Disposition: form-data; name=\\\"%.*s\\\"\\r\\n\\r\\n\"\n#define SW_HTTP_FORM_RAW_DATA_FMT_LEN 8\n#define SW_HTTP_FORM_FILE_DATA_FMT                                                                                     \\\n    \"--%.*s\\r\\nContent-Disposition: form-data; name=\\\"%.*s\\\"; filename=\\\"%.*s\\\"\\r\\nContent-Type: %.*s\\r\\n\\r\\n\"\n#define SW_HTTP_FORM_FILE_DATA_FMT_LEN 16\n\n#define SW_WEBSOCKET_VERSION \"13\"\n#define SW_WEBSOCKET_KEY_LENGTH 16\n#define SW_WEBSOCKET_QUEUE_SIZE 16\n#define SW_WEBSOCKET_EXTENSION_DEFLATE \"permessage-deflate; client_no_context_takeover; server_no_context_takeover\"\n\n/**\n * MySQL Client\n */\n#define SW_MYSQL_DEFAULT_HOST \"127.0.0.1\"\n#define SW_MYSQL_DEFAULT_PORT 3306\n#define SW_MYSQL_DEFAULT_CHARSET 33  // 0x21, utf8_general_ci\n\n/**\n * PGSQL Client\n */\n#define SW_PGSQL_CONNECT_TIMEOUT 3.0\n\n/**\n * Coroutine\n */\n#define SW_DEFAULT_C_STACK_SIZE (2 * 1024 * 1024)\n#define SW_CORO_BAILOUT_EXIT_CODE 1\n#if 0\n#define SW_CONTEXT_PROTECT_STACK_PAGE 1\n#define SW_CONTEXT_DETECT_STACK_USAGE 1\n#endif\n\n#ifdef SW_DEBUG\n#ifndef SW_LOG_TRACE_OPEN\n#define SW_LOG_TRACE_OPEN 1\n#endif\n#endif\n\n#endif /* SWOOLE_CONFIG_H_ */\n"
  },
  {
    "path": "include/swoole_coroutine.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_string.h\"\n#include \"swoole_socket.h\"\n#include \"swoole_reactor.h\"\n#include \"swoole_timer.h\"\n#include \"swoole_async.h\"\n#include \"swoole_util.h\"\n\n#include \"swoole_coroutine_context.h\"\n\n#include <climits>\n\n#include <functional>\n#include <string>\n#include <unordered_map>\n#ifdef SW_USE_THREAD_CONTEXT\n#include <system_error>\n#endif\n\ntypedef std::chrono::microseconds seconds_type;\n\n#ifdef SW_CORO_TIME\n#define CALC_EXECUTE_USEC(yield_coroutine, resume_coroutine) calc_execute_usec(yield_coroutine, resume_coroutine)\n#else\n#define CALC_EXECUTE_USEC(yield_coroutine, resume_coroutine)\n#endif\n\nnamespace swoole {\nclass Coroutine {\n  public:\n    constexpr static int STACK_ALIGNED_SIZE = (4 * 1024);\n    constexpr static int MIN_STACK_SIZE = (64 * 1024);\n    constexpr static int MAX_STACK_SIZE = (16 * 1024 * 1024);\n    constexpr static long MAX_NUM_LIMIT = LONG_MAX;\n\n    enum State {\n        STATE_INIT = 0,\n        STATE_WAITING,\n        STATE_RUNNING,\n        STATE_END,\n    };\n\n    enum Error {\n        ERR_END = 0,\n        ERR_LIMIT = -1,\n        ERR_INVALID = -2,\n    };\n\n    enum ResumeCode {\n        RC_OK = 0,\n        RC_TIMEDOUT = -1,\n        RC_CANCELED = -2,\n    };\n\n    typedef void (*SwapCallback)(void *);\n    typedef std::function<void(void)> BailoutCallback;\n    typedef std::function<bool(swoole::Coroutine *)> CancelFunc;\n\n    void resume();\n    void yield();\n    void yield(CancelFunc *cancel_fn);\n    bool cancel();\n\n    bool yield_ex(double timeout = -1);\n\n    enum State get_state() const {\n        return state;\n    }\n\n    long get_init_msec() const {\n        return init_msec;\n    }\n\n    long get_cid() const {\n        return cid;\n    }\n\n    Coroutine *get_origin() const {\n        return origin;\n    }\n\n    long get_origin_cid() const {\n        return sw_likely(origin) ? origin->get_cid() : -1;\n    }\n\n    void *get_task() const {\n        return task;\n    }\n\n    bool is_end() const {\n        return ctx.is_end();\n    }\n\n    bool is_canceled() const {\n        return resume_code_ == RC_CANCELED;\n    }\n\n    bool is_timedout() const {\n        return resume_code_ == RC_TIMEDOUT;\n    }\n\n    bool is_suspending() const {\n        return state == STATE_WAITING;\n    }\n\n    void set_task(void *_task) {\n        task = _task;\n    }\n\n    void set_cancel_fn(CancelFunc *cancel_fn) {\n        cancel_fn_ = cancel_fn;\n    }\n\n    long get_execute_usec() const {\n        return time<seconds_type>(true) - switch_usec + execute_usec;\n    }\n\n    coroutine::Context &get_ctx() {\n        return ctx;\n    }\n\n    static SW_THREAD_LOCAL std::unordered_map<long, Coroutine *> coroutines;\n\n    static void set_on_yield(SwapCallback func);\n    static void set_on_resume(SwapCallback func);\n    static void set_on_close(SwapCallback func);\n    static void bailout(const BailoutCallback &func);\n\n    static long create(const CoroutineFunc &fn, void *args = nullptr) {\n#ifdef SW_USE_THREAD_CONTEXT\n        try {\n            return (new Coroutine(fn, args))->run();\n        } catch (const std::system_error &e) {\n            swoole_set_last_error(e.code().value());\n            swoole_warning(\"failed to create coroutine, Error: %s[%d]\", e.what(), swoole_get_last_error());\n            return -1;\n        }\n#else\n        return (new Coroutine(fn, args))->run();\n#endif\n    }\n\n    static void activate();\n    static void deactivate();\n\n    static Coroutine *get_current() {\n        return current;\n    }\n\n    static Coroutine *get_current_safe() {\n        if (sw_unlikely(!current)) {\n            swoole_fatal_error(SW_ERROR_CO_OUT_OF_COROUTINE, \"API must be called in the coroutine\");\n        }\n        return current;\n    }\n\n    static void *get_current_task() {\n        return sw_likely(current) ? current->get_task() : nullptr;\n    }\n\n    static long get_current_cid() {\n        return sw_likely(current) ? current->get_cid() : -1;\n    }\n\n    static Coroutine *get_by_cid(long cid) {\n        auto i = coroutines.find(cid);\n        return sw_likely(i != coroutines.end()) ? i->second : nullptr;\n    }\n\n    static void *get_task_by_cid(long cid) {\n        Coroutine *co = get_by_cid(cid);\n        return sw_likely(co) ? co->get_task() : nullptr;\n    }\n\n    static size_t get_stack_size() {\n        return stack_size;\n    }\n\n    static void set_stack_size(size_t size) {\n        stack_size = SW_MEM_ALIGNED_SIZE_EX(SW_MAX(MIN_STACK_SIZE, SW_MIN(size, MAX_STACK_SIZE)), STACK_ALIGNED_SIZE);\n    }\n\n    static void set_socket_bound_cid(long cid) {\n        socket_bound_cid = cid;\n    }\n\n    static long get_last_cid() {\n        return last_cid;\n    }\n\n    static long get_socket_bound_cid() {\n        return socket_bound_cid;\n    }\n\n    static size_t count() {\n        return coroutines.size();\n    }\n\n    static uint64_t get_peak_num() {\n        return peak_num;\n    }\n\n    static long get_elapsed(long cid) {\n        Coroutine *co = cid == 0 ? get_current() : get_by_cid(cid);\n        return sw_likely(co) ? Timer::get_absolute_msec() - co->get_init_msec() : -1;\n    }\n\n    static long get_execute_time(long cid) {\n        Coroutine *co = cid == 0 ? get_current() : get_by_cid(cid);\n        return sw_likely(co) ? co->get_execute_usec() : -1;\n    }\n\n#ifdef SW_CORO_TIME\n    static void calc_execute_usec(Coroutine *yield_coroutine, Coroutine *resume_coroutine);\n#endif\n    static void print_list();\n    static void print_socket_bound_error(int sock_fd, const char *event_str, long bound_cid);\n\n  protected:\n    static SW_THREAD_LOCAL Coroutine *current;\n    static SW_THREAD_LOCAL long last_cid;\n    static SW_THREAD_LOCAL long socket_bound_cid;\n    static SW_THREAD_LOCAL uint64_t peak_num;\n    static SW_THREAD_LOCAL size_t stack_size;\n    static SW_THREAD_LOCAL SwapCallback on_yield;      /* before yield */\n    static SW_THREAD_LOCAL SwapCallback on_resume;     /* before resume */\n    static SW_THREAD_LOCAL SwapCallback on_close;      /* before close */\n    static SW_THREAD_LOCAL BailoutCallback on_bailout; /* when bailout */\n    static SW_THREAD_LOCAL bool activated;\n\n    enum State state = STATE_INIT;\n    enum ResumeCode resume_code_ = RC_OK;\n    long cid;\n    long init_msec = Timer::get_absolute_msec();\n    long switch_usec = time<seconds_type>(true);\n    long execute_usec = 0;\n    void *task = nullptr;\n    coroutine::Context ctx;\n    Coroutine *origin = nullptr;\n    CancelFunc *cancel_fn_ = nullptr;\n\n    Coroutine(const CoroutineFunc &fn, void *private_data);\n    long run();\n    void check_end();\n    void close();\n};\n//-------------------------------------------------------------------------------\nnamespace coroutine {\n/**\n * Support for timeouts and cancellations requires the caller to store the memory pointers of\n * the input and output parameter objects in the `data` pointer of the `AsyncEvent` object.\n * This field is a `shared_ptr`, which increments the reference count when dispatched to the AIO thread,\n * collectively managing the `data` pointer.\n * When the async task is completed, the caller receives the results or cancels or timeouts,\n * the reference count will reach zero, and the memory will be released.\n */\nbool async(async::Handler handler, AsyncEvent &event, double timeout = -1);\n/**\n * This function should be used for asynchronous operations that do not support cancellation and timeouts.\n * For example, in write/read operations,\n * asynchronous tasks cannot transfer the memory ownership of wbuf/rbuf to the AIO thread.\n * In the event of a timeout or cancellation, the memory of wbuf/rbuf will be released by the caller,\n * which may lead the AIO thread to read from an erroneous memory pointer and consequently crash.\n */\nbool async(const std::function<void()> &fn);\nbool run(const CoroutineFunc &fn, void *arg = nullptr);\nbool wait_for(const std::function<bool()> &fn);\n}  // namespace coroutine\n//-------------------------------------------------------------------------------\n}  // namespace swoole\n\n/**\n * for gdb\n */\nswoole::Coroutine *swoole_coroutine_iterator_each();\nvoid swoole_coroutine_iterator_reset();\nswoole::Coroutine *swoole_coroutine_get(long cid);\nsize_t swoole_coroutine_count();\n"
  },
  {
    "path": "include/swoole_coroutine_api.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef SW_COROUTINE_API_H_\n#define SW_COROUTINE_API_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <sys/stat.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <sys/statvfs.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <netdb.h>\n#include <poll.h>\n#include <dirent.h>\n\n/**\n * basic API\n */\nlong swoole_coroutine_create(void (*routine)(void *), void *arg);\nuint8_t swoole_coroutine_is_in(void);\nlong swoole_coroutine_get_id(void);\nvoid swoole_coroutine_sleep(int sec);\nvoid swoole_coroutine_usleep(int usec);\n\n/**\n * file\n */\nint swoole_coroutine_access(const char *pathname, int mode);\nint swoole_coroutine_open(const char *pathname, int flags, mode_t mode);\nssize_t swoole_coroutine_read(int fd, void *buf, size_t count);\nssize_t swoole_coroutine_write(int fd, const void *buf, size_t count);\noff_t swoole_coroutine_lseek(int fd, off_t offset, int whence);\nint swoole_coroutine_fstat(int fd, struct stat *statbuf);\nint swoole_coroutine_stat(const char *path, struct stat *statbuf);\nint swoole_coroutine_lstat(const char *path, struct stat *statbuf);\nssize_t swoole_coroutine_readlink(const char *pathname, char *buf, size_t len);\nint swoole_coroutine_unlink(const char *pathname);\nint swoole_coroutine_mkdir(const char *pathname, mode_t mode);\nint swoole_coroutine_rmdir(const char *pathname);\nint swoole_coroutine_rename(const char *oldpath, const char *newpath);\nint swoole_coroutine_flock(int fd, int operation);\nint swoole_coroutine_statvfs(const char *path, struct statvfs *buf);\nint swoole_coroutine_close(int fd);\nint swoole_coroutine_fsync(int fd);\nint swoole_coroutine_fdatasync(int fd);\nint swoole_coroutine_ftruncate(int fd, off_t length);\n\n/**\n * stdio\n */\nFILE *swoole_coroutine_fopen(const char *pathname, const char *mode);\nFILE *swoole_coroutine_fdopen(int fd, const char *mode);\nFILE *swoole_coroutine_freopen(const char *pathname, const char *mode, FILE *stream);\nsize_t swoole_coroutine_fread(void *ptr, size_t size, size_t nmemb, FILE *stream);\nsize_t swoole_coroutine_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);\nchar *swoole_coroutine_fgets(char *s, int size, FILE *stream);\nint swoole_coroutine_fputs(const char *s, FILE *stream);\nint swoole_coroutine_fflush(FILE *stream);\nint swoole_coroutine_feof(FILE *stream);\nint swoole_coroutine_fclose(FILE *stream);\n\n/**\n * dir\n */\nDIR *swoole_coroutine_opendir(const char *name);\nstruct dirent *swoole_coroutine_readdir(DIR *dirp);\nint swoole_coroutine_closedir(DIR *dirp);\n\n/**\n * socket API\n * The `bind()` and `listen()` functions do not block IO waiting,\n * so the original functions can be used directly without the need for hooks.\n */\nint swoole_coroutine_socket(int domain, int type, int protocol);\nint swoole_coroutine_socket_create(int fd);\nint swoole_coroutine_socket_unwrap(int fd);\nuint8_t swoole_coroutine_socket_exists(int fd);\nssize_t swoole_coroutine_send(int sockfd, const void *buf, size_t len, int flags);\nssize_t swoole_coroutine_sendmsg(int sockfd, const struct msghdr *msg, int flags);\nssize_t swoole_coroutine_recv(int sockfd, void *buf, size_t len, int flags);\nssize_t swoole_coroutine_recvmsg(int sockfd, struct msghdr *msg, int flags);\nint swoole_coroutine_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);\nint swoole_coroutine_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);\nint swoole_coroutine_poll(struct pollfd *fds, nfds_t nfds, int timeout);\nint swoole_coroutine_poll_fake(struct pollfd *fds, nfds_t nfds, int timeout);\nint swoole_coroutine_socket_set_timeout(int fd, int which, double timeout);\nint swoole_coroutine_socket_set_connect_timeout(int fd, double timeout);\nint swoole_coroutine_socket_wait_event(int fd, int event, double timeout);\nint swoole_coroutine_getaddrinfo(const char *name,\n                                 const char *service,\n                                 const struct addrinfo *req,\n                                 struct addrinfo **pai);\nstruct hostent *swoole_coroutine_gethostbyname(const char *name);\n\n/**\n * wait\n */\nsize_t swoole_coroutine_wait_count(void);\npid_t swoole_coroutine_waitpid(pid_t _pid, int *_stat_loc, int _options);\npid_t swoole_coroutine_wait(int *_stat_loc);\n\n#ifdef __cplusplus\n} /* end extern \"C\" */\n#endif\n#endif\n"
  },
  {
    "path": "include/swoole_coroutine_channel.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_coroutine.h\"\n\n#include <iostream>\n#include <string>\n#include <list>\n#include <queue>\n\nnamespace swoole {\nnamespace coroutine {\n//-------------------------------------------------------------------------------\nclass Channel {\n  public:\n    enum Opcode {\n        PRODUCER = 1,\n        CONSUMER = 2,\n    };\n\n    enum ErrorCode {\n        ERROR_OK = 0,\n        ERROR_TIMEOUT = -1,\n        ERROR_CLOSED = -2,\n        ERROR_CANCELED = -3,\n    };\n\n    struct TimeoutMessage {\n        Channel *chan;\n        Opcode type;\n        Coroutine *co;\n        bool error;\n        TimerNode *timer;\n    };\n\n    void *pop(double timeout = -1);\n    bool push(void *data, double timeout = -1);\n    bool close();\n\n    explicit Channel(size_t _capacity = 1) : capacity(_capacity) {}\n\n    ~Channel() {\n        if (!producer_queue.empty()) {\n            swoole_error_log(SW_LOG_WARNING,\n                             SW_ERROR_CO_HAS_BEEN_DISCARDED,\n                             \"channel is destroyed, %zu producers will be discarded\",\n                             producer_queue.size());\n        }\n        if (!consumer_queue.empty()) {\n            swoole_error_log(SW_LOG_WARNING,\n                             SW_ERROR_CO_HAS_BEEN_DISCARDED,\n                             \"channel is destroyed, %zu consumers will be discarded\",\n                             consumer_queue.size());\n        }\n    }\n\n    bool is_closed() const {\n        return closed;\n    }\n\n    bool is_empty() const {\n        return data_queue.empty();\n    }\n\n    bool is_full() const {\n        return data_queue.size() == capacity;\n    }\n\n    size_t length() const {\n        return data_queue.size();\n    }\n\n    size_t consumer_num() const {\n        return consumer_queue.size();\n    }\n\n    size_t producer_num() const {\n        return producer_queue.size();\n    }\n\n    void *pop_data() {\n        if (data_queue.empty()) {\n            return nullptr;\n        }\n        void *data = data_queue.front();\n        data_queue.pop();\n        return data;\n    }\n\n    int get_error() const {\n        return error_;\n    }\n\n  protected:\n    size_t capacity = 1;\n    bool closed = false;\n    int error_ = 0;\n    std::list<Coroutine *> producer_queue;\n    std::list<Coroutine *> consumer_queue;\n    std::queue<void *> data_queue;\n\n    static void timer_callback(Timer *timer, TimerNode *tnode);\n\n    void yield(Opcode type);\n\n    void consumer_remove(Coroutine *co) {\n        consumer_queue.remove(co);\n    }\n\n    void producer_remove(Coroutine *co) {\n        producer_queue.remove(co);\n    }\n\n    Coroutine *pop_coroutine(Opcode type) {\n        Coroutine *co;\n        if (type == PRODUCER) {\n            co = producer_queue.front();\n            producer_queue.pop_front();\n            swoole_trace_log(SW_TRACE_CHANNEL, \"resume producer cid=%ld\", co->get_cid());\n        } else  // if (type == CONSUMER)\n        {\n            co = consumer_queue.front();\n            consumer_queue.pop_front();\n            swoole_trace_log(SW_TRACE_CHANNEL, \"resume consumer cid=%ld\", co->get_cid());\n        }\n        return co;\n    }\n};\n//-------------------------------------------------------------------------------\n}  // namespace coroutine\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_coroutine_context.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole.h\"\n\n#ifdef SW_USE_THREAD_CONTEXT\n#include <thread>\n#include <mutex>\n#elif !defined(SW_USE_ASM_CONTEXT)\n#define USE_UCONTEXT 1\n#ifndef _XOPEN_SOURCE\n#define _XOPEN_SOURCE\n#endif\n#include <ucontext.h>\n#else\n#define USE_ASM_CONTEXT 1\n#include \"swoole_asm_context.h\"\n#endif\n\n#if defined(HAVE_VALGRIND) && !defined(HAVE_KQUEUE)\n#define USE_VALGRIND 1\n#include <valgrind/valgrind.h>\n#endif\n\n#ifdef USE_UCONTEXT\ntypedef ucontext_t coroutine_context_t;\n#elif defined(USE_ASM_CONTEXT)\ntypedef fcontext_t coroutine_context_t;\ntypedef transfer_t coroutine_transfer_t;\n#endif\n\n#if defined(USE_UCONTEXT) || defined(SW_USE_THREAD_CONTEXT)\ntypedef void *coroutine_transfer_t;\n#endif\n\ntypedef std::function<void(void *)> CoroutineFunc;\n\nnamespace swoole {\nnamespace coroutine {\n\nclass Context {\n  public:\n    Context(size_t stack_size, CoroutineFunc fn, void *private_data);\n    ~Context();\n    bool swap_in();\n    bool swap_out();\n#if !defined(SW_USE_THREAD_CONTEXT) && defined(SW_CONTEXT_DETECT_STACK_USAGE)\n    ssize_t get_stack_usage();\n#endif\n#ifndef SW_USE_THREAD_CONTEXT\n    char *get_stack() const {\n        return stack_;\n    }\n\n    size_t get_stack_size() const {\n        return stack_size_;\n    }\n#endif\n    bool is_end() const {\n        return end_;\n    }\n\n  protected:\n    CoroutineFunc fn_;\n#ifdef SW_USE_THREAD_CONTEXT\n    std::thread thread_;\n    std::mutex lock_;\n    std::mutex *swap_lock_;\n#else\n    coroutine_context_t ctx_;\n    coroutine_context_t swap_ctx_;\n    char *stack_;\n    uint32_t stack_size_;\n#endif\n#ifdef USE_VALGRIND\n    uint32_t valgrind_stack_id;\n#endif\n    void *private_data_;\n    bool end_;\n\n    static void context_func(coroutine_transfer_t arg);\n};\n\n}  // namespace coroutine\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_coroutine_socket.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_socket.h\"\n#include \"swoole_coroutine.h\"\n#include \"swoole_protocol.h\"\n#include \"swoole_proxy.h\"\n\n#include <vector>\n\nnamespace swoole {\nnamespace coroutine {\n//-------------------------------------------------------------------------------\n/**\n * @return true: continue to wait for events\n * @return false: stop event waiting and resume coroutine\n */\nusing EventBarrier = std::function<bool()>;\n\nclass Socket {\n  public:\n    int errCode = 0;\n    const char *errMsg = \"\";\n    std::string errString;\n\n    bool open_length_check = false;\n    bool open_eof_check = false;\n    bool http2 = false;\n\n    Protocol protocol = {};\n    std::unique_ptr<Socks5Proxy> socks5_proxy = nullptr;\n    std::unique_ptr<HttpProxy> http_proxy = nullptr;\n\n    static TimeoutType timeout_type_list[4];\n\n    Socket(int domain, int type, int protocol);\n    Socket(int _fd, int _domain, int _type, int _protocol);\n    explicit Socket(SocketType type = SW_SOCK_TCP);\n    Socket(int _fd, SocketType _type);\n    virtual ~Socket();\n    /**\n     * If SSL is enabled, an SSL handshake will automatically take place during the connect() method.\n     * When connect() returns true, it indicates that the TCP connection has been successfully\n     * established and the SSL handshake has also succeeded.\n     */\n    bool connect(const std::string &host, int port = 0, int flags = 0);\n    virtual bool connect(const sockaddr *addr, socklen_t addrlen);\n    bool shutdown(int how = SHUT_RDWR);\n    bool cancel(EventType event);\n    bool close();\n\n    bool is_connected() const {\n        return connected && !is_closed();\n    }\n\n    bool is_closed() const {\n        return sock_fd == SW_BAD_SOCKET;\n    }\n\n    bool is_port_required() const {\n        return type <= SW_SOCK_UDP6;\n    }\n\n    bool check_liveness();\n    ssize_t peek(void *_buf, size_t _n);\n    virtual ssize_t recv(void *_buf, size_t _n);\n    virtual ssize_t send(const void *_buf, size_t _n);\n\n    ssize_t send(const std::string &buf) {\n        return send(buf.c_str(), buf.length());\n    }\n\n    /**\n     * The read()/write()/recvmsg()/sendmsg() functions currently does not support SSL\n     */\n    virtual ssize_t read(void *_buf, size_t _n);\n    virtual ssize_t write(const void *_buf, size_t _n);\n    virtual ssize_t recvmsg(msghdr *msg, int flags);\n    virtual ssize_t sendmsg(const msghdr *msg, int flags);\n\n    virtual ssize_t readv(network::IOVector *io_vector);\n    virtual ssize_t readv_all(network::IOVector *io_vector);\n    virtual ssize_t writev(network::IOVector *io_vector);\n    virtual ssize_t writev_all(network::IOVector *io_vector);\n    virtual ssize_t recv_all(void *_buf, size_t _n);\n    virtual ssize_t send_all(const void *_buf, size_t _n);\n    ssize_t recv_packet(double timeout = 0);\n    ssize_t recv_line(void *_buf, size_t maxlen);\n    ssize_t recv_with_buffer(void *_buf, size_t _n);\n\n    char *pop_packet() const {\n        if (read_buffer->offset == 0) {\n            return nullptr;\n        } else {\n            return read_buffer->pop(buffer_init_size);\n        }\n    }\n\n    virtual bool poll(EventType _type, double timeout = 0);\n    /**\n     * If the server has SSL enabled, you must explicitly call `ssl_handshake()`,\n     * as it will not be automatically executed within the `accept()` function.\n     * This behavior is inconsistent with `connect()`, which internally executes `ssl_handshake()` automatically,\n     * thus not requiring an explicit call at the application level.\n     * The reason for this design is that `ssl_handshake()` can typically be performed concurrently within a separate\n     * client coroutine. If `ssl_handshake()` were to be automatically executed inside the `accept()` function,\n     * it would block the server's listening coroutine,\n     * causing the `ssl_handshake()` processes to execute sequentially rather than in parallel.\n     */\n    Socket *accept(double timeout = 0);\n    bool bind(const std::string &address, int port = 0);\n    bool bind(const sockaddr *sa, socklen_t len);\n    bool listen(int backlog = 0);\n    virtual bool sendfile(const char *filename, off_t offset, size_t length);\n    virtual ssize_t sendto(const std::string &host, int port, const void *_buf, size_t _n);\n    ssize_t recvfrom(void *_buf, size_t _n);\n    virtual ssize_t recvfrom(void *_buf, size_t _n, sockaddr *_addr, socklen_t *_socklen);\n\n    /**\n     * Operation sequence:\n     * 1. enable_ssl_encrypt()\n     * 2. Set SSL parameters, such as certificate file, key file\n     * 3. ssl_handshake(), to be executed after connect or accept\n     */\n    bool enable_ssl_encrypt() {\n        if (ssl_context.get()) {\n            return false;\n        }\n        ssl_context = std::make_shared<SSLContext>();\n        return true;\n    }\n\n    bool ssl_is_enable() const {\n        return get_ssl_context() != nullptr;\n    }\n\n    SSLContext *get_ssl_context() const {\n        return ssl_context.get();\n    }\n\n    virtual bool ssl_handshake();\n    bool ssl_verify(bool allow_self_signed);\n    std::string ssl_get_peer_cert();\n\n    bool set_ssl_key_file(const std::string &file) const {\n        return ssl_context->set_key_file(file);\n    }\n\n    bool set_ssl_cert_file(const std::string &file) const {\n        return ssl_context->set_cert_file(file);\n    }\n\n    void set_ssl_cafile(const std::string &file) const {\n        ssl_context->cafile = file;\n    }\n\n    void set_ssl_capath(const std::string &path) const {\n        ssl_context->capath = path;\n    }\n\n    void set_ssl_passphrase(const std::string &str) const {\n        ssl_context->passphrase = str;\n    }\n\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n    void set_tls_host_name(const std::string &str) const {\n        ssl_context->tls_host_name = str;\n        // if user set empty ssl_host_name, disable it, otherwise the underlying may set it automatically\n        ssl_context->disable_tls_host_name = ssl_context->tls_host_name.empty();\n    }\n#endif\n\n    void set_ssl_dhparam(const std::string &file) const {\n        ssl_context->dhparam = file;\n    }\n\n    void set_ssl_ecdh_curve(const std::string &str) const {\n        ssl_context->ecdh_curve = str;\n    }\n\n    void set_ssl_protocols(long protocols) const {\n        ssl_context->protocols = protocols;\n    }\n\n    void set_ssl_disable_compress(bool value) const {\n        ssl_context->disable_compress = value;\n    }\n\n    void set_ssl_verify_peer(bool value) const {\n        ssl_context->verify_peer = value;\n    }\n\n    void set_ssl_allow_self_signed(bool value) const {\n        ssl_context->allow_self_signed = value;\n    }\n\n    void set_ssl_verify_depth(uint8_t value) const {\n        ssl_context->verify_depth = value;\n    }\n\n    void set_ssl_ciphers(const std::string &str) const {\n        ssl_context->ciphers = str;\n    }\n\n#ifdef OPENSSL_IS_BORINGSSL\n    void set_ssl_grease(uint8_t value) {\n        ssl_context->grease = value;\n    }\n#endif\n\n    const std::string &get_ssl_cert_file() const {\n        return ssl_context->cert_file;\n    }\n\n    const std::string &get_ssl_key_file() const {\n        return ssl_context->key_file;\n    }\n\n    static inline void init_reactor(Reactor *reactor) {\n        reactor->set_handler(SW_FD_CO_SOCKET, SW_EVENT_READ, readable_event_callback);\n        reactor->set_handler(SW_FD_CO_SOCKET, SW_EVENT_WRITE, writable_event_callback);\n        reactor->set_handler(SW_FD_CO_SOCKET, SW_EVENT_ERROR, error_event_callback);\n    }\n\n    SocketType get_type() const {\n        return type;\n    }\n\n    FdType get_fd_type() const {\n        return socket->fd_type;\n    }\n\n    int get_sock_domain() const {\n        return sock_domain;\n    }\n\n    int get_sock_type() const {\n        return sock_type;\n    }\n\n    int get_sock_protocol() const {\n        return sock_protocol;\n    }\n\n    int get_fd() const {\n        return sock_fd;\n    }\n\n    network::Socket *get_socket() const {\n        return socket;\n    }\n\n    bool getsockname() const;\n    bool getpeername(network::Address *sa);\n\n    const char *get_addr() const {\n        return socket->get_addr();\n    }\n\n    int get_port() const {\n        return socket->get_port();\n    }\n\n    bool has_bound(const EventType event = SW_EVENT_RDWR) const {\n        return get_bound_co(event) != nullptr;\n    }\n\n    Coroutine *get_bound_co(const EventType event) const {\n        if (event & SW_EVENT_READ) {\n            if (read_co) {\n                return read_co;\n            }\n        }\n        if (event & SW_EVENT_WRITE) {\n            if (write_co) {\n                return write_co;\n            }\n        }\n        return nullptr;\n    }\n\n    long get_bound_cid(const EventType event = SW_EVENT_RDWR) const {\n        Coroutine *co = get_bound_co(event);\n        return co ? co->get_cid() : 0;\n    }\n\n    const char *get_event_str(EventType event) const;\n\n    void check_bound_co(const EventType event) const {\n        auto bound_cid = get_bound_cid(event);\n        if (sw_unlikely(bound_cid)) {\n            Coroutine::print_socket_bound_error(sock_fd, get_event_str(event), bound_cid);\n        }\n    }\n\n    void set_err(const int e) {\n        errCode = errno = e;\n        swoole_set_last_error(errCode);\n        errMsg = e ? swoole_strerror(e) : \"\";\n    }\n\n    void set_err() {\n        errCode = swoole_get_last_error() ? swoole_get_last_error() : errno;\n        errMsg = swoole_strerror(errCode);\n    }\n\n    void set_err(const int e, const char *s) {\n        errCode = errno = e;\n        swoole_set_last_error(errCode);\n        errMsg = s;\n    }\n\n    void set_err(const int e, const std::string &s) {\n        errCode = errno = e;\n        swoole_set_last_error(errCode);\n        errString = s;\n        errMsg = errString.c_str();\n    }\n\n    const char *get_err() {\n        return swoole_strerror(errCode);\n    }\n\n    /* set connect read write timeout */\n    void set_timeout(double timeout, int _type = SW_TIMEOUT_ALL) const;\n\n    void set_timeout(const timeval *timeout, int _type = SW_TIMEOUT_ALL) const {\n        set_timeout((double) timeout->tv_sec + ((double) timeout->tv_usec / 1000 / 1000), _type);\n    }\n\n    double get_timeout(TimeoutType _type) const;\n    bool get_option(int level, int optname, void *optval, socklen_t *optlen) const;\n    bool get_option(int level, int optname, int *optval) const;\n    bool set_option(int level, int optname, const void *optval, socklen_t optlen) const;\n    bool set_option(int level, int optname, int optval) const;\n    void set_socks5_proxy(const std::string &host, int port, const std::string &user = \"\", const std::string &pwd = \"\");\n    void set_http_proxy(const std::string &host, int port, const std::string &user = \"\", const std::string &pwd = \"\");\n    String *get_read_buffer();\n    String *get_write_buffer();\n    String *pop_read_buffer();\n    String *pop_write_buffer();\n\n    void set_resolve_context(NameResolver::Context *ctx) {\n        resolve_context_ = ctx;\n    }\n\n    void set_dtor(const std::function<void(Socket *)> &dtor) {\n        dtor_ = dtor;\n    }\n\n    void set_zero_copy(bool enable) {\n        zero_copy = enable;\n    }\n\n    void set_buffer_allocator(const Allocator *allocator) {\n        buffer_allocator = allocator;\n    }\n\n    void set_buffer_init_size(size_t size) {\n        if (size == 0) {\n            return;\n        }\n        buffer_init_size = size;\n    }\n\n    int move_fd() {\n        sock_fd = SW_BAD_SOCKET;\n        return socket->move_fd();\n    }\n\n    network::Socket *move_socket() {\n        network::Socket *_socket = socket;\n        socket = nullptr;\n        return _socket;\n    }\n\n    bool ssl_is_available() const {\n        return socket && ssl_handshaked;\n    }\n\n    SSL *get_ssl() const {\n        return socket->ssl;\n    }\n\n    void ssl_close() const;\n\n  protected:\n    SocketType type;\n    network::Socket *socket = nullptr;\n    int sock_domain = 0;\n    int sock_type = 0;\n    int sock_protocol = 0;\n    int sock_fd = -1;\n\n    Coroutine *read_co = nullptr;\n    Coroutine *write_co = nullptr;\n\n    EventType want_event = SW_EVENT_NULL;\n\n    std::string connect_host;\n    int connect_port = 0;\n    int backlog = 0;\n\n    TimerNode *read_timer = nullptr;\n    TimerNode *write_timer = nullptr;\n\n    const Allocator *buffer_allocator = nullptr;\n    size_t buffer_init_size = SW_BUFFER_SIZE_BIG;\n    String *read_buffer = nullptr;\n    String *write_buffer = nullptr;\n\n    EventBarrier *recv_barrier = nullptr;\n    EventBarrier *send_barrier = nullptr;\n\n    bool ssl_is_server = false;\n    bool ssl_handshaked = false;\n    std::shared_ptr<SSLContext> ssl_context = nullptr;\n    std::string ssl_host_name;\n    bool ssl_context_create();\n    bool ssl_create(SSLContext *ssl_context);\n\n    bool connected = false;\n    bool shutdown_read = false;\n    bool shutdown_write = false;\n\n    bool zero_copy = false;\n\n    NameResolver::Context *resolve_context_ = nullptr;\n    std::function<void(Socket *)> dtor_;\n\n    Socket(network::Socket *sock, const Socket *server_sock);\n\n    static void timer_callback(Timer *timer, TimerNode *tnode);\n    static int readable_event_callback(Reactor *reactor, Event *event);\n    static int writable_event_callback(Reactor *reactor, Event *event);\n    static int error_event_callback(Reactor *reactor, Event *event);\n\n    void init_sock_type(SocketType _type);\n    bool init_sock();\n    bool reinit_sock(SocketType _type);\n    bool init_reactor_socket(int fd);\n\n    void check_return_value(ssize_t retval) {\n        if (retval >= 0) {\n            set_err(0);\n        } else if (errCode == 0) {\n            set_err(errno);\n        }\n    }\n\n    void init_options() {\n        if (socket->is_tcp()) {\n            set_option(IPPROTO_TCP, TCP_NODELAY, 1);\n        }\n        if (socket->is_udp()) {\n            socket->set_buffer_size(network::Socket::default_buffer_size);\n        }\n        protocol.package_length_type = 'N';\n        protocol.package_length_size = 4;\n        protocol.package_length_offset = 0;\n        protocol.package_body_offset = 0;\n        protocol.package_max_length = SW_INPUT_BUFFER_SIZE;\n    }\n\n    bool add_event(EventType event);\n    bool wait_event(EventType event, const void **_buf = nullptr, size_t _n = 0);\n\n    ssize_t recv_packet_with_length_protocol();\n    ssize_t recv_packet_with_eof_protocol();\n\n    bool is_available(const EventType event) {\n        if (event != SW_EVENT_NULL) {\n            check_bound_co(event);\n        }\n        if (sw_unlikely(is_closed())) {\n            set_err(EBADF);\n            return false;\n        }\n        if (sw_unlikely(socket->close_wait)) {\n            set_err(SW_ERROR_CO_SOCKET_CLOSE_WAIT);\n            return false;\n        }\n        return true;\n    }\n\n    bool socks5_handshake();\n\n    const std::string &get_http_proxy_host_name() const {\n        if (ssl_context && !ssl_context->tls_host_name.empty()) {\n            return ssl_context->tls_host_name;\n        }\n        return http_proxy->target_host;\n    }\n\n    bool http_proxy_handshake();\n\n    class TimerController {\n      public:\n        TimerController(TimerNode **_timer_pp, double _timeout, Socket *_socket, TimerCallback _callback)\n            : timer_pp(_timer_pp), timeout(_timeout), socket_(_socket), callback(std::move(_callback)) {}\n        bool start();\n        ~TimerController();\n\n      private:\n        bool enabled = false;\n        TimerNode **timer_pp;\n        double timeout;\n        Socket *socket_;\n        TimerCallback callback;\n    };\n\n  public:\n    class TimeoutSetter {\n      public:\n        TimeoutSetter(Socket *socket, double _timeout, TimeoutType _type);\n        ~TimeoutSetter();\n\n      protected:\n        Socket *socket_;\n        double timeout;\n        TimeoutType type;\n        double original_timeout[sizeof(timeout_type_list)] = {};\n    };\n\n    class TimeoutController : public TimeoutSetter {\n      public:\n        TimeoutController(Socket *_socket, double _timeout, const TimeoutType _type)\n            : TimeoutSetter(_socket, _timeout, _type) {}\n        bool has_timedout(TimeoutType _type);\n\n      protected:\n        double startup_time = 0;\n    };\n};\n\nclass ProtocolSwitch {\n    bool ori_open_eof_check;\n    bool ori_open_length_check;\n    Protocol ori_protocol;\n    Socket *socket_;\n\n  public:\n    explicit ProtocolSwitch(Socket *socket) {\n        ori_open_eof_check = socket->open_eof_check;\n        ori_open_length_check = socket->open_length_check;\n        ori_protocol = socket->protocol;\n        socket_ = socket;\n    }\n\n    ~ProtocolSwitch() {\n        /* revert protocol settings */\n        socket_->open_eof_check = ori_open_eof_check;\n        socket_->open_length_check = ori_open_length_check;\n        socket_->protocol = ori_protocol;\n    }\n};\n\nstd::vector<std::string> dns_lookup(const char *domain, int family = AF_INET, double timeout = 2.0);\nstd::vector<std::string> dns_lookup_impl_with_socket(const char *domain, int family, double timeout);\n#ifdef SW_USE_CARES\nstd::vector<std::string> dns_lookup_impl_with_cares(const char *domain, int family, double timeout);\n#endif\nstd::string get_ip_by_hosts(const std::string &domain);\n//-------------------------------------------------------------------------------\n}  // namespace coroutine\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_coroutine_system.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_coroutine.h\"\n\n#include <vector>\n\nnamespace swoole {\nnamespace coroutine {\n//-------------------------------------------------------------------------------\nstruct PollSocket {\n    int events;\n    int16_t revents;\n    void *ptr;\n    network::Socket *socket;\n\n    PollSocket(int _event, void *_ptr) {\n        events = _event;\n        ptr = _ptr;\n        revents = 0;\n        socket = nullptr;\n    }\n};\n\nclass System {\n  public:\n    static void init_reactor(Reactor *reactor);\n    /* sleep */\n    static int sleep(double sec);\n    /* file */\n    static std::shared_ptr<String> read_file(const char *file, bool lock = false);\n    static ssize_t write_file(const char *file, const char *buf, size_t length, bool lock = false, int flags = 0);\n    /* dns */\n    static std::string gethostbyname(const std::string &hostname, int domain, double timeout = -1);\n    static std::vector<std::string> getaddrinfo(const std::string &hostname,\n                                                int family = AF_INET,\n                                                int socktype = SOCK_STREAM,\n                                                int protocol = IPPROTO_TCP,\n                                                const std::string &service = \"\",\n                                                double timeout = -1);\n    static void set_dns_cache_expire(time_t expire);\n    static void set_dns_cache_capacity(size_t capacity);\n    static void clear_dns_cache();\n    static float get_dns_cache_hit_ratio();\n    /* multiplexing */\n    static bool socket_poll(std::unordered_map<int, PollSocket> &fds, double timeout);\n    /* wait */\n    static pid_t wait(int *_stat_loc, double timeout = -1);\n    static pid_t waitpid(pid_t _pid, int *_stat_loc, int _options, double timeout = -1);\n    /**\n     * waitpid_safe() does not deps on the signal\n     * and can be safely used in a multithreaded environment.\n     */\n    static pid_t waitpid_safe(pid_t _pid, int *_stat_loc, int _options);\n    /* signal */\n    static int wait_signal(int signal, double timeout = -1);\n    static int wait_signal(const std::vector<int> &signals, double timeout = -1);\n    /* event */\n\n    /**\n     * On failure, it returns -1, and you can use swoole_get_last_error() or errno to determine the cause of the\n     * failure. If successful, it returns the event set, such as SW_EVENT_READ | SW_EVENT_WRITE.\n     */\n    static int wait_event(int fd, int events, double timeout);\n    static bool exec(const char *command, bool get_error_stream, std::shared_ptr<String> buffer, int *status);\n};\nstd::string gethostbyname_impl_with_async(const std::string &hostname, int domain, double timeout = -1);\n//-------------------------------------------------------------------------------\n}  // namespace coroutine\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_dtls.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_ssl.h\"\n\n#ifdef SW_SUPPORT_DTLS\n#include <deque>\n\nnamespace swoole {\nnamespace dtls {\n//-------------------------------------------------------------------------------\nusing Socket = network::Socket;\n\nint BIO_write(BIO *b, const char *data, int dlen);\nint BIO_read(BIO *b, char *data, int dlen);\nlong BIO_ctrl(BIO *b, int cmd, long larg, void *pargs);\nint BIO_create(BIO *b);\nint BIO_destroy(BIO *b);\nBIO_METHOD *BIO_get_methods();\nvoid BIO_meth_free();\n\nstruct Buffer {\n    uint16_t length;\n    uchar data[0];\n};\n\nstruct Session {\n    std::shared_ptr<SSLContext> ctx;\n    bool listened = false;\n    Socket *socket;\n    std::deque<Buffer *> rxqueue;\n    bool peek_mode = false;\n\n    Session(Socket *_sock, const std::shared_ptr<SSLContext> &_ctx) {\n        socket = _sock;\n        ctx = _ctx;\n    }\n\n    ~Session() {\n        while (!rxqueue.empty()) {\n            Buffer *buffer = rxqueue.front();\n            rxqueue.pop_front();\n            sw_free(buffer);\n        }\n    }\n\n    bool init();\n    bool listen();\n\n    void append(const char *data, ssize_t len);\n\n    void append(Buffer *buffer) {\n        rxqueue.push_back(buffer);\n    }\n\n    size_t get_buffer_length() const {\n        size_t total_length = 0;\n        for (const auto i : rxqueue) {\n            total_length += i->length;\n        }\n        return total_length;\n    }\n};\n//-------------------------------------------------------------------------------\n}  // namespace dtls\n}  // namespace swoole\n#endif\n"
  },
  {
    "path": "include/swoole_error.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\nenum swErrorCode {\n    /**\n     * Prevent repetition with errno [syscall error]\n     */\n    SW_ERROR_BEGIN = 500,\n\n    /**\n     * common error\n     */\n    SW_ERROR_MALLOC_FAIL = 501,\n    SW_ERROR_SYSTEM_CALL_FAIL,\n    SW_ERROR_PHP_FATAL_ERROR,\n    SW_ERROR_NAME_TOO_LONG,\n    SW_ERROR_INVALID_PARAMS,\n    SW_ERROR_QUEUE_FULL,\n    SW_ERROR_OPERATION_NOT_SUPPORT,\n    SW_ERROR_PROTOCOL_ERROR,\n    SW_ERROR_WRONG_OPERATION,\n    SW_ERROR_PHP_RUNTIME_NOTICE,  // Non-fatal errors, just runtime warnings\n    SW_ERROR_FOR_TEST,\n\n    SW_ERROR_NO_PAYLOAD = 550,\n\n    SW_ERROR_UNDEFINED_BEHAVIOR = 600,\n    SW_ERROR_NOT_THREAD_SAFETY,\n\n    SW_ERROR_FILE_NOT_EXIST = 700,\n    SW_ERROR_FILE_TOO_LARGE,\n    SW_ERROR_FILE_EMPTY,\n    SW_ERROR_DIR_NOT_EXIST,\n\n    SW_ERROR_DNSLOOKUP_DUPLICATE_REQUEST = 710,\n    SW_ERROR_DNSLOOKUP_RESOLVE_FAILED,\n    SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT,\n    SW_ERROR_DNSLOOKUP_UNSUPPORTED,\n    SW_ERROR_DNSLOOKUP_NO_SERVER,\n\n    SW_ERROR_BAD_IPV6_ADDRESS = 720,\n    SW_ERROR_UNREGISTERED_SIGNAL,\n    SW_ERROR_BAD_HOST_ADDR,\n    SW_ERROR_BAD_PORT,\n    SW_ERROR_BAD_SOCKET_TYPE,\n\n    // EventLoop\n    SW_ERROR_EVENT_REMOVE_FAILED = 800,\n    SW_ERROR_EVENT_ADD_FAILED,\n    SW_ERROR_EVENT_UPDATE_FAILED,\n    SW_ERROR_EVENT_UNKNOWN_DATA,\n\n    /**\n     * connection error\n     */\n    SW_ERROR_SESSION_CLOSED_BY_SERVER = 1001,\n    SW_ERROR_SESSION_CLOSED_BY_CLIENT,\n    SW_ERROR_SESSION_CLOSING,\n    SW_ERROR_SESSION_CLOSED,\n    SW_ERROR_SESSION_NOT_EXIST,\n    SW_ERROR_SESSION_INVALID_ID,\n    SW_ERROR_SESSION_DISCARD_TIMEOUT_DATA,\n    SW_ERROR_SESSION_DISCARD_DATA,\n    SW_ERROR_OUTPUT_BUFFER_OVERFLOW,\n    SW_ERROR_OUTPUT_SEND_YIELD,\n    SW_ERROR_SSL_NOT_READY,\n    SW_ERROR_SSL_CANNOT_USE_SENFILE,\n    SW_ERROR_SSL_EMPTY_PEER_CERTIFICATE,\n    SW_ERROR_SSL_VERIFY_FAILED,\n    SW_ERROR_SSL_BAD_CLIENT,\n    SW_ERROR_SSL_BAD_PROTOCOL,\n    SW_ERROR_SSL_RESET,\n    SW_ERROR_SSL_HANDSHAKE_FAILED,\n    SW_ERROR_SSL_CREATE_CONTEXT_FAILED,\n    SW_ERROR_SSL_CREATE_SESSION_FAILED,\n\n    SW_ERROR_PACKAGE_LENGTH_TOO_LARGE = 1201,\n    SW_ERROR_PACKAGE_LENGTH_NOT_FOUND,\n    SW_ERROR_DATA_LENGTH_TOO_LARGE,\n    SW_ERROR_PACKAGE_MALFORMED_DATA,\n\n    /**\n     * task error\n     */\n    SW_ERROR_TASK_PACKAGE_TOO_BIG = 2001,\n    SW_ERROR_TASK_DISPATCH_FAIL,\n    SW_ERROR_TASK_TIMEOUT,\n\n    /**\n     * http2 protocol error\n     */\n    SW_ERROR_HTTP2_STREAM_ID_TOO_BIG = 3001,\n    SW_ERROR_HTTP2_STREAM_NO_HEADER,\n    SW_ERROR_HTTP2_STREAM_NOT_FOUND,\n    SW_ERROR_HTTP2_STREAM_IGNORE,\n    SW_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED,\n    SW_ERROR_HTTP2_INTERNAL_ERROR,\n\n    /**\n     * AIO\n     */\n    SW_ERROR_AIO_BAD_REQUEST = 4001,\n    SW_ERROR_AIO_CANCELED,\n    SW_ERROR_AIO_TIMEOUT,\n\n    /**\n     * Client\n     */\n    SW_ERROR_CLIENT_NO_CONNECTION = 5001,\n\n    /**\n     * Socket\n     */\n    SW_ERROR_SOCKET_CLOSED = 6001,\n    SW_ERROR_SOCKET_POLL_TIMEOUT,\n    SW_ERROR_SOCKET_NOT_EXISTS,\n\n    /**\n     * Proxy\n     */\n    SW_ERROR_SOCKS5_UNSUPPORT_VERSION = 7001,\n    SW_ERROR_SOCKS5_UNSUPPORT_METHOD,\n    SW_ERROR_SOCKS5_AUTH_FAILED,\n    SW_ERROR_SOCKS5_SERVER_ERROR,\n    SW_ERROR_SOCKS5_HANDSHAKE_FAILED,\n    SW_ERROR_SOCKS5_CONNECT_FAILED,\n\n    SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR = 7101,\n    SW_ERROR_HTTP_INVALID_PROTOCOL,\n    SW_ERROR_HTTP_PROXY_HANDSHAKE_FAILED,\n    SW_ERROR_HTTP_PROXY_BAD_RESPONSE,\n    SW_ERROR_HTTP_CONFLICT_HEADER,\n    SW_ERROR_HTTP_CONTEXT_UNAVAILABLE,\n    SW_ERROR_HTTP_COOKIE_UNAVAILABLE,\n\n    SW_ERROR_WEBSOCKET_BAD_CLIENT = 8501,\n    SW_ERROR_WEBSOCKET_BAD_OPCODE,\n    SW_ERROR_WEBSOCKET_UNCONNECTED,\n    SW_ERROR_WEBSOCKET_HANDSHAKE_FAILED,\n    SW_ERROR_WEBSOCKET_PACK_FAILED,\n    SW_ERROR_WEBSOCKET_UNPACK_FAILED,\n    SW_ERROR_WEBSOCKET_INCOMPLETE_PACKET,\n\n    /**\n     * server global error\n     */\n    SW_ERROR_SERVER_MUST_CREATED_BEFORE_CLIENT = 9001,\n    SW_ERROR_SERVER_TOO_MANY_SOCKET,\n    SW_ERROR_SERVER_WORKER_TERMINATED,\n    SW_ERROR_SERVER_INVALID_LISTEN_PORT,\n    SW_ERROR_SERVER_TOO_MANY_LISTEN_PORT,\n    SW_ERROR_SERVER_PIPE_BUFFER_FULL,\n    SW_ERROR_SERVER_NO_IDLE_WORKER,\n    SW_ERROR_SERVER_ONLY_START_ONE,\n    SW_ERROR_SERVER_SEND_IN_MASTER,\n    SW_ERROR_SERVER_INVALID_REQUEST,\n    SW_ERROR_SERVER_CONNECT_FAIL,\n    SW_ERROR_SERVER_INVALID_COMMAND,\n    SW_ERROR_SERVER_IS_NOT_REGULAR_FILE,\n    SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT,\n    SW_ERROR_SERVER_INVALID_CALLBACK,\n    SW_ERROR_SERVER_UNRELATED_THREAD,\n\n    /**\n     * Process exit timeout, forced to end.\n     */\n    SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT = 9101,\n    SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA,\n    SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA,\n\n    /**\n     * Coroutine\n     */\n    SW_ERROR_CO_OUT_OF_COROUTINE = 10001,\n    SW_ERROR_CO_HAS_BEEN_BOUND,\n    SW_ERROR_CO_HAS_BEEN_DISCARDED,\n\n    SW_ERROR_CO_MUTEX_DOUBLE_UNLOCK,\n    SW_ERROR_CO_BLOCK_OBJECT_LOCKED,\n    SW_ERROR_CO_BLOCK_OBJECT_WAITING,\n    SW_ERROR_CO_YIELD_FAILED,\n    SW_ERROR_CO_GETCONTEXT_FAILED,\n    SW_ERROR_CO_SWAPCONTEXT_FAILED,\n    SW_ERROR_CO_MAKECONTEXT_FAILED,\n\n    SW_ERROR_CO_IOCPINIT_FAILED,\n    SW_ERROR_CO_PROTECT_STACK_FAILED,\n    SW_ERROR_CO_STD_THREAD_LINK_ERROR,\n    SW_ERROR_CO_DISABLED_MULTI_THREAD,\n\n    SW_ERROR_CO_CANNOT_CANCEL,\n    SW_ERROR_CO_NOT_EXISTS,\n    SW_ERROR_CO_CANCELED,\n    SW_ERROR_CO_TIMEDOUT,\n\n    // close failed, there are currently other coroutines holding this socket,\n    // need to wait for the bound coroutine to return from the socket wait_event operation\n    SW_ERROR_CO_SOCKET_CLOSE_WAIT,\n\n    SW_ERROR_END\n};\n\nnamespace swoole {\nclass Exception final : std::exception {\n  public:\n    int code;\n    const char *msg;\n\n    explicit Exception(int code) noexcept;\n};\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_file.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_string.h\"\n\n#include <sys/file.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n\nnamespace swoole {\n\nssize_t file_get_size(FILE *fp);\nssize_t file_get_size(int fd);\nssize_t file_get_size(const std::string &filename);\nstd::shared_ptr<String> file_get_contents(const std::string &filename);\nbool file_put_contents(const std::string &filename, const char *content, size_t length);\nbool file_exists(const std::string &filename);\n\ntypedef struct stat FileStatus;\n\nclass File {\n    int fd_;\n    int flags_;\n    std::string path_;\n\n  public:\n    enum Flag {\n        READ = O_RDONLY,\n        WRITE = O_WRONLY,\n        RW = O_RDWR,\n        CREATE = O_CREAT,\n        EXCL = O_EXCL,\n        APPEND = O_APPEND,\n    };\n\n    explicit File(const int fd) {\n        fd_ = fd;\n        flags_ = 0;\n    }\n\n    File(int fd, const std::string &path) {\n        fd_ = fd;\n        path_ = path;\n        flags_ = 0;\n    }\n\n    File(const std::string &path, int oflags);\n    File(const std::string &path, int oflags, int mode);\n    ~File();\n\n    bool open(const std::string &path, int oflags, int mode = 0);\n    bool close();\n    bool stat(FileStatus *_stat) const;\n\n    bool ready() const {\n        return fd_ != -1;\n    }\n\n    ssize_t write(const void *_buf, size_t _n) const {\n        return ::write(fd_, _buf, _n);\n    }\n\n    ssize_t write(const std::string &str) const {\n        return ::write(fd_, str.c_str(), str.length());\n    }\n\n    ssize_t read(void *_buf, const size_t _n) const {\n        return ::read(fd_, _buf, _n);\n    }\n\n    ssize_t pwrite(const void *_buf, size_t _n, off_t _offset) const {\n        return ::pwrite(fd_, _buf, _n, _offset);\n    }\n\n    ssize_t pread(void *_buf, const size_t _n, off_t _offset) const {\n        return ::pread(fd_, _buf, _n, _offset);\n    }\n\n    size_t write_all(const void *data, size_t len) const;\n    size_t read_all(void *buf, size_t len) const;\n    /**\n     * Read one line of file, reading ends when __n - 1 bytes have been read,\n     * or a newline (which is included in the return value),\n     * or an EOF (read bytes less than __n)\n     * Returns length of line on success, -1 otherwise.\n     * NOTE: `buf` must be ended with zero.\n     */\n    ssize_t read_line(void *_buf, size_t _n) const;\n\n    std::shared_ptr<String> read_content() const;\n\n    bool sync() const {\n        return ::fsync(fd_) == 0;\n    }\n\n    bool truncate(off_t length) const {\n        return ::ftruncate(fd_, length) == 0;\n    }\n\n    off_t set_offset(off_t offset) const {\n        return lseek(fd_, offset, SEEK_SET);\n    }\n\n    off_t get_offset() const {\n        return lseek(fd_, 0, SEEK_CUR);\n    }\n\n    bool lock(int operation) const {\n        return ::flock(fd_, operation) == 0;\n    }\n\n    bool unlock() const {\n        return ::flock(fd_, LOCK_UN) == 0;\n    }\n\n    ssize_t get_size() const {\n        return file_get_size(fd_);\n    }\n\n    void release() {\n        fd_ = -1;\n    }\n\n    int get_fd() const {\n        return fd_;\n    }\n\n    const std::string &get_path() const {\n        return path_;\n    }\n\n    static bool exists(const std::string &file) {\n        return ::access(file.c_str(), R_OK) == 0;\n    }\n\n    static bool remove(const std::string &file) {\n        return ::remove(file.c_str()) == 0;\n    }\n};\n\nFile make_tmpfile();\n\nclass AsyncFile {\n  private:\n    int fd = -1;\n    int flags_ = 0;\n    mode_t mode_ = 0;\n    std::string path_;\n\n  public:\n    AsyncFile(const std::string &path, int flags, int mode);\n    ~AsyncFile();\n\n    bool open(const std::string &path, int flags, mode_t mode);\n    bool close() const;\n\n    ssize_t read(void *buf, size_t count) const;\n    ssize_t write(const void *buf, size_t count) const;\n    ssize_t write(const String *buf) const {\n        return write(SW_STRINGL(buf));\n    }\n\n    bool sync() const;\n    bool truncate(off_t length) const;\n    bool stat(FileStatus *statbuf) const;\n\n    off_t get_offset() const;\n    off_t set_offset(off_t offset) const;\n\n    bool ready() const {\n        return fd != -1;\n    }\n};\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_file_hook.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef SW_FILE_HOOK_H_\n#define SW_FILE_HOOK_H_\n\n#include \"swoole_coroutine_api.h\"\n\n#define open(pathname, flags, mode) swoole_coroutine_open(pathname, flags, mode)\n#define close(fd) swoole_coroutine_close(fd)\n#define read(fd, buf, count) swoole_coroutine_read(fd, buf, count)\n#define write(fd, buf, count) swoole_coroutine_write(fd, buf, count)\n#define lseek(fd, offset, whence) swoole_coroutine_lseek(fd, offset, whence)\n#define readlink(fd, buf, size) swoole_coroutine_readlink(fd, buf, size)\n#define unlink(pathname) swoole_coroutine_unlink(pathname)\n#define mkdir(pathname, mode) swoole_coroutine_mkdir(pathname, mode)\n#define rmdir(pathname) swoole_coroutine_rmdir(pathname)\n#define rename(oldpath, newpath) swoole_coroutine_rename(oldpath, newpath)\n#define fsync(fd) swoole_coroutine_fsync(fd)\n#define fdatasync(fd) swoole_coroutine_fdatasync(fd)\n#define ftruncate(fd, length) swoole_coroutine_ftruncate(fd, length)\n\n#define access(pathname, mode) swoole_coroutine_access(pathname, mode)\n#define fopen(pathname, mode) swoole_coroutine_fopen(pathname, mode)\n#define fdopen(fd, mode) swoole_coroutine_fdopen(fd, mode)\n#define freopen(pathname, mode, stream) swoole_coroutine_freopen(pathname, mode, stream)\n#define fread(ptr, size, nmemb, stream) swoole_coroutine_fread(ptr, size, nmemb, stream)\n#define fwrite(ptr, size, nmemb, stream) swoole_coroutine_fwrite(ptr, size, nmemb, stream)\n#define fgets(s, size, stream) swoole_coroutine_fgets(s, size, stream)\n#define fputs(s, stream) swoole_coroutine_fputs(s, stream)\n#define feof(stream) swoole_coroutine_feof(stream)\n#define fflush(stream) swoole_coroutine_fflush(stream)\n#define fclose(stream) swoole_coroutine_fclose(stream)\n\n#define opendir(name) swoole_coroutine_opendir(name)\n#define readdir(dir) swoole_coroutine_readdir(dir)\n#define closedir(dir) swoole_coroutine_closedir(dir)\n\n#endif\n"
  },
  {
    "path": "include/swoole_hash.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n\nuint64_t swoole_hash_jenkins(const char *key, size_t keylen);\nuint64_t swoole_hash_php(const char *key, size_t len);\nuint64_t swoole_hash_austin(const char *key, size_t keylen);\nuint32_t swoole_crc32(const char *data, size_t size);\n"
  },
  {
    "path": "include/swoole_heap.h",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@php.net so we can mail you a copy immediately.               |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n\nnamespace swoole {\n\nstruct HeapNode {\n    uint64_t priority;\n    uint32_t position;\n    void *data;\n};\n\nclass Heap {\n  public:\n    enum Type {\n        MIN_HEAP,\n        MAX_HEAP,\n    };\n\n    Heap(size_t _n, Type _type);\n    ~Heap();\n\n    size_t count() const {\n        return num - 1;\n    }\n\n    HeapNode *push(uint64_t priority, void *data);\n    void *pop();\n    void change_priority(uint64_t new_priority, HeapNode *ptr) const;\n    void remove(HeapNode *node);\n    void *peek() const;\n    void print() const;\n    int compare(uint64_t a, uint64_t b) const;\n\n    HeapNode *top() const {\n        if (num == 1) {\n            return nullptr;\n        }\n        return nodes[1];\n    }\n\n  private:\n    uint32_t num;\n    uint32_t size;\n    enum Type type;\n    HeapNode **nodes;\n\n    void bubble_up(uint32_t i) const;\n    uint32_t maxchild(uint32_t i) const;\n    void percolate_down(uint32_t i) const;\n};\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_http.h",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@php.net so we can mail you a copy immediately.               |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n#pragma once\n\n#include \"swoole_protocol.h\"\n\n#include <unordered_map>\n\nenum swHttpVersion {\n    SW_HTTP_VERSION_10 = 1,\n    SW_HTTP_VERSION_11,\n    SW_HTTP_VERSION_2,\n    SW_HTTP_VERSION_3,\n};\n\nenum swHttpMethod {\n    SW_HTTP_DELETE = 1,\n    SW_HTTP_GET,\n    SW_HTTP_HEAD,\n    SW_HTTP_POST,\n    SW_HTTP_PUT,\n    SW_HTTP_PATCH,\n    /* pathological */\n    SW_HTTP_CONNECT,\n    SW_HTTP_OPTIONS,\n    SW_HTTP_TRACE,\n    /* webdav */\n    SW_HTTP_COPY,\n    SW_HTTP_LOCK,\n    SW_HTTP_MKCOL,\n    SW_HTTP_MOVE,\n    SW_HTTP_PROPFIND,\n    SW_HTTP_PROPPATCH,\n    SW_HTTP_UNLOCK,\n    /* subversion */\n    SW_HTTP_REPORT,\n    SW_HTTP_MKACTIVITY,\n    SW_HTTP_CHECKOUT,\n    SW_HTTP_MERGE,\n    /* upnp */\n    SW_HTTP_MSEARCH,\n    SW_HTTP_NOTIFY,\n    SW_HTTP_SUBSCRIBE,\n    SW_HTTP_UNSUBSCRIBE,\n    /* proxy */\n    SW_HTTP_PURGE,\n    /* Http2 */\n    SW_HTTP_PRI,\n};\n\nenum swHttpStatusCode {\n    SW_HTTP_CONTINUE = 100,\n    SW_HTTP_SWITCHING_PROTOCOLS = 101,\n    SW_HTTP_PROCESSING = 102,\n\n    SW_HTTP_OK = 200,\n    SW_HTTP_CREATED = 201,\n    SW_HTTP_ACCEPTED = 202,\n    SW_HTTP_NO_CONTENT = 204,\n    SW_HTTP_PARTIAL_CONTENT = 206,\n\n    SW_HTTP_SPECIAL_RESPONSE = 300,\n    SW_HTTP_MOVED_PERMANENTLY = 301,\n    SW_HTTP_MOVED_TEMPORARILY = 302,\n    SW_HTTP_SEE_OTHER = 303,\n    SW_HTTP_NOT_MODIFIED = 304,\n    SW_HTTP_TEMPORARY_REDIRECT = 307,\n    SW_HTTP_PERMANENT_REDIRECT = 308,\n\n    SW_HTTP_BAD_REQUEST = 400,\n    SW_HTTP_UNAUTHORIZED = 401,\n    SW_HTTP_FORBIDDEN = 403,\n    SW_HTTP_NOT_FOUND = 404,\n    SW_HTTP_NOT_ALLOWED = 405,\n    SW_HTTP_REQUEST_TIME_OUT = 408,\n    SW_HTTP_CONFLICT = 409,\n    SW_HTTP_LENGTH_REQUIRED = 411,\n    SW_HTTP_PRECONDITION_FAILED = 412,\n    SW_HTTP_REQUEST_ENTITY_TOO_LARGE = 413,\n    SW_HTTP_REQUEST_URI_TOO_LARGE = 414,\n    SW_HTTP_UNSUPPORTED_MEDIA_TYPE = 415,\n    SW_HTTP_RANGE_NOT_SATISFIABLE = 416,\n    SW_HTTP_MISDIRECTED_REQUEST = 421,\n    SW_HTTP_TOO_MANY_REQUESTS = 429,\n    SW_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451,\n\n    SW_HTTP_INTERNAL_SERVER_ERROR = 500,\n    SW_HTTP_NOT_IMPLEMENTED = 501,\n    SW_HTTP_BAD_GATEWAY = 502,\n    SW_HTTP_SERVICE_UNAVAILABLE = 503,\n    SW_HTTP_GATEWAY_TIME_OUT = 504,\n    SW_HTTP_VERSION_NOT_SUPPORTED = 505,\n    SW_HTTP_INSUFFICIENT_STORAGE = 507\n};\n\nstruct multipart_parser;\n\nnamespace swoole {\nclass Server;\nnamespace http_server {\n//-----------------------------------------------------------------\nstruct FormData {\n    const char *multipart_boundary_buf;\n    uint32_t multipart_boundary_len;\n    multipart_parser *multipart_parser_;\n    String *multipart_buffer_;\n    String *upload_tmpfile;\n    std::string upload_tmpfile_fmt_;\n    const char *current_header_name;\n    size_t current_header_name_len;\n    size_t upload_filesize;\n    size_t upload_max_filesize;\n};\n\nstruct Request {\n    uint8_t method;\n    uint8_t version;\n    uchar excepted : 1;\n    uchar too_large : 1;\n    uchar unavailable : 1;\n\n    uchar header_parsed : 1;\n    uchar tried_to_dispatch : 1;\n    uchar multipart_header_parsed : 1;\n\n    uchar known_length : 1;\n    uchar keep_alive : 1;\n    uchar chunked : 1;\n    uchar nobody_chunked : 1;\n\n    uint32_t url_offset_;\n    uint32_t url_length_;\n\n    uint32_t max_length_;\n    uint32_t request_line_length_; /* without \\r\\n  */\n    uint32_t header_length_;       /* include request_line_length + \\r\\n */\n    uint64_t content_length_;\n\n    FormData *form_data_;\n    String *buffer_;\n\n    Request() {\n        clean();\n        form_data_ = nullptr;\n        buffer_ = nullptr;\n    }\n    ~Request();\n    void clean() {\n        memset(&method, 0, offsetof(Request, form_data_));\n    }\n    int get_protocol();\n    int get_header_length();\n    int get_chunked_body_length();\n    void parse_header_info();\n    bool parse_multipart_data(String *buffer);\n    bool init_multipart_parser(const Server *server);\n    void destroy_multipart_parser();\n    std::string get_header(const char *name) const;\n    bool has_expect_header() const;\n};\n\ntypedef std::function<bool(char *key, size_t key_len, char *value, size_t value_len)> ParseCookieCallback;\n\nint get_method(const char *method_str, size_t method_len);\nconst char *get_method_string(int method);\nconst char *get_status_message(int code);\nextern int list_of_status_code[128];\n/**\n * This function will directly modify the data in the input buffer.\n */\nsize_t url_decode(char *str, size_t len);\n/**\n * Returns a new memory address, and the pointer at this address needs to be manually released in the calling layer.\n */\nchar *url_encode(char const *str, size_t len);\nint dispatch_request(Server *serv, const Protocol *proto, network::Socket *socket, const RecvData *rdata);\nbool parse_multipart_boundary(\n    const char *at, size_t length, size_t offset, char **out_boundary_str, int *out_boundary_len);\nvoid parse_cookie(const char *at, size_t length, const ParseCookieCallback &cb);\n\nssize_t get_package_length(const Protocol *protocol, network::Socket *conn, PacketLength *pl);\nuint8_t get_package_length_size(network::Socket *conn);\nint dispatch_frame(const Protocol *protocol, network::Socket *conn, const RecvData *rdata);\n\nstruct ContextImpl;\n\nclass Context {\n  public:\n    Context(Server *server, SessionId session_id, ContextImpl *_impl) {\n        server_ = server;\n        session_id_ = session_id;\n        impl = _impl;\n    }\n    ~Context();\n    bool end(const std::string &data) {\n        return end(data.c_str(), data.length());\n    }\n    bool end(const char *data, size_t length);\n    void setHeader(const std::string &key, const std::string &value) {\n        response.headers[key] = value;\n    }\n    void setStatusCode(int code) {\n        response.code = code;\n    }\n    // Request\n    int version = 0;\n    bool keepalive = false;\n    bool post_form_urlencoded = false;\n    std::string request_path;\n    std::string query_string;\n    std::string server_protocol;\n    std::unordered_map<std::string, std::string> headers;\n    std::unordered_map<std::string, std::string> files;\n    std::unordered_map<std::string, std::string> form_data;\n    std::string body;\n    // Response\n    struct {\n        int code = 200;\n        std::unordered_map<std::string, std::string> headers;\n    } response;\n    // Impl\n    Server *server_;\n    SessionId session_id_;\n    ContextImpl *impl;\n};\n\nstd::shared_ptr<Server> listen(const std::string &addr, const std::function<void(Context &ctx)> &cb, int mode = 1);\n//-----------------------------------------------------------------\n}  // namespace http_server\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_http2.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_protocol.h\"\n\n#define SW_HTTP2_PRI_STRING \"PRI * HTTP/2.0\\r\\n\\r\\nSM\\r\\n\\r\\n\"\n\nenum swHttp2ErrorCode {\n    SW_HTTP2_ERROR_NO_ERROR = 0x0,\n    SW_HTTP2_ERROR_PROTOCOL_ERROR = 0x1,\n    SW_HTTP2_ERROR_INTERNAL_ERROR = 0x2,\n    SW_HTTP2_ERROR_FLOW_CONTROL_ERROR = 0x3,\n    SW_HTTP2_ERROR_SETTINGS_TIMEOUT = 0x4,\n    SW_HTTP2_ERROR_STREAM_CLOSED = 0x5,\n    SW_HTTP2_ERROR_FRAME_SIZE_ERROR = 0x6,\n    SW_HTTP2_ERROR_REFUSED_STREAM = 0x7,\n    SW_HTTP2_ERROR_CANCEL = 0x8,\n    SW_HTTP2_ERROR_COMPRESSION_ERROR = 0x9,\n    SW_HTTP2_ERROR_CONNECT_ERROR = 0xa,\n    SW_HTTP2_ERROR_ENHANCE_YOUR_CALM = 0xb,\n    SW_HTTP2_ERROR_INADEQUATE_SECURITY = 0xc,\n    SW_HTTP2_ERROR_HTTP_1_1_REQUIRED = 0xd,\n};\n\nenum swHttp2FrameType {\n    SW_HTTP2_TYPE_DATA = 0,\n    SW_HTTP2_TYPE_HEADERS = 1,\n    SW_HTTP2_TYPE_PRIORITY = 2,\n    SW_HTTP2_TYPE_RST_STREAM = 3,\n    SW_HTTP2_TYPE_SETTINGS = 4,\n    SW_HTTP2_TYPE_PUSH_PROMISE = 5,\n    SW_HTTP2_TYPE_PING = 6,\n    SW_HTTP2_TYPE_GOAWAY = 7,\n    SW_HTTP2_TYPE_WINDOW_UPDATE = 8,\n    SW_HTTP2_TYPE_CONTINUATION = 9,\n};\n\nenum swHttp2FrameFlag {\n    SW_HTTP2_FLAG_NONE = 0x00,\n    SW_HTTP2_FLAG_ACK = 0x01,\n    SW_HTTP2_FLAG_END_STREAM = 0x01,\n    SW_HTTP2_FLAG_END_HEADERS = 0x04,\n    SW_HTTP2_FLAG_PADDED = 0x08,\n    SW_HTTP2_FLAG_PRIORITY = 0x20,\n};\n\nenum swHttp2SettingId {\n    SW_HTTP2_SETTING_HEADER_TABLE_SIZE = 0x1,\n    SW_HTTP2_SETTINGS_ENABLE_PUSH = 0x2,\n    SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x3,\n    SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE = 0x4,\n    SW_HTTP2_SETTINGS_MAX_FRAME_SIZE = 0x5,\n    SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x6,\n};\n\nenum swHttp2StreamFlag {\n    SW_HTTP2_STREAM_NORMAL = 0,\n    SW_HTTP2_STREAM_REQUEST_END = 1 << 0,\n    SW_HTTP2_STREAM_PIPELINE_REQUEST = 1 << 1,\n    SW_HTTP2_STREAM_PIPELINE_RESPONSE = 1 << 2,\n    SW_HTTP2_STREAM_USE_PIPELINE_READ = 1 << 3,\n};\n\n#define SW_HTTP2_FRAME_HEADER_SIZE 9\n#define SW_HTTP2_SETTING_OPTION_SIZE 6\n#define SW_HTTP2_SETTING_FRAME_SIZE (SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_SETTING_OPTION_SIZE * 6)\n#define SW_HTTP2_FRAME_PING_PAYLOAD_SIZE 8\n\n#define SW_HTTP2_RST_STREAM_SIZE 4\n#define SW_HTTP2_PRIORITY_SIZE 5\n#define SW_HTTP2_PING_SIZE 8\n#define SW_HTTP2_RST_STREAM_SIZE 4\n#define SW_HTTP2_GOAWAY_SIZE 8\n#define SW_HTTP2_WINDOW_UPDATE_SIZE 4\n#define SW_HTTP2_STREAM_ID_SIZE 4\n#define SW_HTTP2_SETTINGS_PARAM_SIZE 6\n\n#define swoole_http2_frame_trace_log(_trace_str, ...)                                                                  \\\n    swoole_trace_log(SW_TRACE_HTTP2,                                                                                   \\\n                     SW_ECHO_RED_BG \" [\" SW_ECHO_GREEN \"] \"                                                            \\\n                                    \"<length=%jd, flags=(%s), stream_id=%d> \" _trace_str,                              \\\n                     \" RECV \",                                                                                         \\\n                     swoole::http2::get_type(type),                                                                    \\\n                     length,                                                                                           \\\n                     swoole::http2::get_flag_string(flags).c_str(),                                                    \\\n                     stream_id,                                                                                        \\\n                     ##__VA_ARGS__)\n\n#define swoole_http2_send_trace_log(_trace_str, ...)                                                                   \\\n    swoole_trace_log(SW_TRACE_HTTP2, SW_ECHO_GREEN_BG \" \" _trace_str, \" SEND \", ##__VA_ARGS__)\n\n#define swoole_http2_recv_trace_log(_trace_str, ...)                                                                   \\\n    swoole_trace_log(SW_TRACE_HTTP2, SW_ECHO_RED_BG \" \" _trace_str, \" RECV \", ##__VA_ARGS__)\n\nnamespace swoole {\nnamespace http2 {\n\nstruct Settings {\n    uint32_t header_table_size;\n    uint32_t enable_push;\n    uint32_t max_concurrent_streams;\n    uint32_t init_window_size;\n    uint32_t max_frame_size;\n    uint32_t max_header_list_size;\n};\n\n/**\n +-----------------------------------------------+\n |                 Length (24)                   |\n +---------------+---------------+---------------+\n |   Type (8)    |   Flags (8)   |\n +-+-------------+---------------+-------------------------------+\n |R|                 Stream Identifier (31)                      |\n +=+=============================================================+\n |                   Frame Payload (0...)                      ...\n +---------------------------------------------------------------+\n */\nstruct Frame {\n    uint32_t length : 24;\n    uint32_t type : 8;\n    uint32_t flags : 8;\n    uint32_t rsv1 : 1;\n    uint32_t identifier : 31;\n    char data[0];\n};\n\nstatic sw_inline ssize_t get_length(const char *buf) {\n    return (((uint8_t) buf[0]) << 16) + (((uint8_t) buf[1]) << 8) + (uint8_t) buf[2];\n}\n\nvoid put_default_setting(enum swHttp2SettingId id, uint32_t value);\nuint32_t get_default_setting(enum swHttp2SettingId id);\nsize_t pack_setting_frame(char *buf, const Settings &settings, bool server_side);\nReturnCode unpack_setting_data(const char *buf,\n                               ssize_t length,\n                               const std::function<ReturnCode(uint16_t, uint32_t)> &cb);\nssize_t get_frame_length(const Protocol *protocol, network::Socket *conn, PacketLength *pl);\nint send_setting_frame(Protocol *protocol, network::Socket *conn);\nconst char *get_type(int type);\nint get_type_color(int type);\n\nstatic sw_inline void init_settings(Settings *settings) {\n    settings->header_table_size = get_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE);\n    settings->enable_push = get_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH);\n    settings->max_concurrent_streams = get_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);\n    settings->init_window_size = get_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE);\n    settings->max_frame_size = get_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE);\n    settings->max_header_list_size = get_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE);\n}\n\nstatic inline std::string get_flag_string(int _flags) {\n    std::string str;\n    if (_flags & SW_HTTP2_FLAG_ACK) {\n        str.append(\"ACK|\");\n    }\n    if (_flags & SW_HTTP2_FLAG_END_STREAM) {\n        str.append(\"END_STREAM|\");\n    }\n    if (_flags & SW_HTTP2_FLAG_END_HEADERS) {\n        str.append(\"END_HEADERS|\");\n    }\n    if (_flags & SW_HTTP2_FLAG_PADDED) {\n        str.append(\"PADDED|\");\n    }\n    if (_flags & SW_HTTP2_FLAG_PRIORITY) {\n        str.append(\"PRIORITY|\");\n    }\n    if (str.back() == '|') {\n        return str.substr(0, str.length() - 1);\n    } else {\n        return {\"none\"};\n    }\n}\n\n/**\n +-----------------------------------------------+\n |                 Length (24)                   |\n +---------------+---------------+---------------+\n |   Type (8)    |   Flags (8)   |\n +-+-------------+---------------+-------------------------------+\n |R|                 Stream Identifier (31)                      |\n +=+=============================================================+\n |                   Frame Payload (0...)                      ...\n +---------------------------------------------------------------+\n */\nstatic sw_inline void set_frame_header(char *buffer, uint8_t type, uint32_t length, uint8_t flags, uint32_t stream_id) {\n    buffer[0] = length >> 16;\n    buffer[1] = length >> 8;\n    buffer[2] = length;\n    buffer[3] = type;\n    buffer[4] = flags;\n    *(uint32_t *) (buffer + 5) = htonl(stream_id);\n}\n\n}  // namespace http2\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_iouring.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: NathanFreeman  <mariasocute@163.com>                         |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_coroutine.h\"\n\n#ifdef SW_USE_IOURING\n#include <liburing.h>\n\nusing swoole::Coroutine;\n\nenum swIouringFlag {\n    SW_IOURING_DEFAULT = 0,\n    SW_IOURING_SQPOLL = IORING_SETUP_SQPOLL,\n};\n\nnamespace swoole {\n\nstruct IouringEvent;\n\nstruct IouringTimeout {\n    int64_t tv_sec;\n    int64_t tv_nsec;\n};\n\nclass Iouring {\n    uint32_t task_num = 0;\n    uint32_t entries = SW_IOURING_QUEUE_SIZE;\n    io_uring ring;\n    std::queue<IouringEvent *> waiting_tasks;\n    network::Socket *ring_socket = nullptr;\n    Reactor *reactor = nullptr;\n\n    IouringEvent *ready_events[SW_IOURING_QUEUE_SIZE];\n    io_uring_cqe *cqes[SW_IOURING_QUEUE_SIZE];\n\n    explicit Iouring(Reactor *reactor_);\n    bool ready() const;\n    void yield(IouringEvent *event);\n    void resume(IouringEvent *event);\n    bool cancel(IouringEvent *prev_event);\n    void dispatch(IouringEvent *event);\n    void submit(bool immediately);\n    bool wakeup();\n\n    io_uring_sqe *alloc_sqe() {\n        return io_uring_get_sqe(&ring);\n    }\n\n    static Iouring *get_instance();\n    static ssize_t execute(IouringEvent *event);\n    static const char *get_opcode_name(enum io_uring_op opcode);\n\n  public:\n    ~Iouring();\n\n    bool is_empty_waiting_tasks() const {\n        return waiting_tasks.empty();\n    }\n\n    uint64_t get_task_num() const {\n        return task_num;\n    }\n\n    uint32_t get_sq_space_left() const {\n        return io_uring_sq_space_left(&ring);\n    }\n\n    uint32_t get_sq_capacity() const {\n        return ring.sq.ring_entries;\n    }\n\n    uint32_t get_sq_used() const {\n        return get_sq_capacity() - get_sq_space_left();\n    }\n\n    size_t get_waiting_task_num() const {\n        return waiting_tasks.size();\n    }\n\n    float get_sq_usage_percent() const {\n        return (float) get_sq_used() / get_sq_capacity() * 100.0f;\n    }\n\n    static int socket(int domain, int type, int protocol = 0, int flags = 0);\n    static int open(const char *pathname, int flags, mode_t mode);\n    static int connect(int fd, const struct sockaddr *addr, socklen_t len, double timeout = -1);\n    static int accept(int fd, struct sockaddr *addr, socklen_t *len, int flags = 0, double timeout = -1);\n    static int bind(int fd, const struct sockaddr *addr, socklen_t len);\n    static int listen(int fd, int backlog);\n    static int sleep(int tv_sec, int tv_nsec, int flags = 0);\n    static int sleep(double seconds);\n    static ssize_t recv(int fd, void *buf, size_t len, int flags, double timeout = -1);\n    static ssize_t send(int fd, const void *buf, size_t len, int flags, double timeout = -1);\n    static ssize_t recvmsg(int fd, struct msghdr *message, int flags, double timeout = -1);\n    static ssize_t sendmsg(int fd, const struct msghdr *message, int flags, double timeout = -1);\n    static ssize_t sendto(\n        int fd, const void *buf, size_t n, int flags, const struct sockaddr *addr, socklen_t len, double timeout = -1);\n    static ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t size, double timeout = -1);\n    static ssize_t recvfrom(int fd, void *_buf, size_t _n, sockaddr *_addr, socklen_t *_socklen, double timeout = -1);\n    static ssize_t readv(int fd, const struct iovec *iovec, int count, double timeout = -1);\n    static ssize_t writev(int fd, const struct iovec *iovec, int count, double timeout = -1);\n    static int shutdown(int fd, int how);\n    static int close(int fd);\n    static ssize_t read(int fd, void *buf, size_t size, double timeout = -1);\n    static ssize_t write(int fd, const void *buf, size_t size, double timeout = -1);\n    static int rename(const char *oldpath, const char *newpath);\n    static int mkdir(const char *pathname, mode_t mode);\n    static int unlink(const char *pathname);\n#ifdef HAVE_IOURING_STATX\n    static int fstat(int fd, struct stat *statbuf);\n    static int stat(const char *path, struct stat *statbuf);\n#endif\n    static int rmdir(const char *pathname);\n    static int fsync(int fd);\n    static int fdatasync(int fd);\n    static pid_t wait(int *stat_loc, double timeout = -1);\n    static pid_t waitpid(pid_t pid, int *stat_loc, int options, double timeout = -1);\n    /**\n     * Only supports listening to the readable and writable events of a single fd; nfds must be 1.\n     */\n    static int poll(struct pollfd *fds, nfds_t nfds, int timeout);\n#ifdef HAVE_IOURING_FUTEX\n    static int futex_wait(uint32_t *futex);\n    static int futex_wakeup(uint32_t *futex);\n#endif\n#ifdef HAVE_IOURING_FTRUNCATE\n    static int ftruncate(int fd, off_t length);\n#endif\n\n    static std::unordered_map<std::string, int> list_all_opcode();\n    static int callback(Reactor *reactor, Event *event);\n};\n};  // namespace swoole\n#endif\n"
  },
  {
    "path": "include/swoole_llhttp.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: NathanFreeman  <mariasocute@163.com>                         |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef SWOOLE_LLHTTP_H\n#define SWOOLE_LLHTTP_H\n\n#include \"swoole.h\"\n#include \"thirdparty/llhttp/llhttp.h\"\n\nstatic sw_inline void swoole_llhttp_parser_init(llhttp_t *parser, llhttp_type_t type, void *ctx) {\n    llhttp_init(parser, type, nullptr);\n    parser->data = ctx;\n}\n\nstatic sw_inline size_t swoole_llhttp_parser_execute(llhttp_t *parser,\n                                                     const llhttp_settings_t *settings,\n                                                     const char *data,\n                                                     size_t length) {\n    parser->settings = (void *) settings;\n    const llhttp_errno_t result = llhttp_execute(parser, data, length);\n\n    if (result == HPE_OK) {\n        return length;\n    }\n\n    const size_t parsed_length = llhttp_get_error_pos(parser) - data;\n    switch (result) {\n    case HPE_PAUSED:\n        llhttp_resume(parser);\n        break;\n    case HPE_PAUSED_UPGRADE:\n        llhttp_resume_after_upgrade(parser);\n        break;\n    default:\n        break;\n    }\n\n    return parsed_length;\n}\n\n#endif  // SWOOLE_LLHTTP_H\n"
  },
  {
    "path": "include/swoole_lock.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_memory.h\"\n\n#include <sys/file.h>\n#include <system_error>\n\nnamespace swoole {\n\nclass Lock {\n  public:\n    enum Type {\n        RW_LOCK = 1,\n        MUTEX = 3,\n        SPIN_LOCK = 5,\n        COROUTINE_LOCK = 6,\n    };\n    Type get_type() const {\n        return type_;\n    }\n    virtual ~Lock() = default;\n    virtual int lock(int operation = LOCK_EX, int timeout_msec = -1) = 0;\n    virtual int unlock() = 0;\n\n  protected:\n    Lock(Type type, bool shared) {\n        type_ = type;\n        shared_ = shared;\n    }\n    Type type_;\n    bool shared_;\n};\n\nstruct MutexImpl;\n\nclass Mutex final : public Lock {\n    MutexImpl *impl;\n\n  public:\n    explicit Mutex(bool shared);\n    ~Mutex() override;\n    int lock(int operation = LOCK_EX, int timeout_msec = -1) override;\n    int unlock() override;\n};\n\n#ifdef HAVE_RWLOCK\nstruct RWLockImpl;\n\nclass RWLock final : public Lock {\n    RWLockImpl *impl;\n\n  public:\n    explicit RWLock(bool shared);\n    ~RWLock() override;\n    int lock(int operation = LOCK_EX, int timeout_msec = -1) override;\n    int unlock() override;\n    int lock_rd() {\n    \treturn lock(LOCK_SH);\n    }\n    int lock_wr() {\n    \treturn lock(LOCK_EX);\n    }\n};\n#endif\n\n#ifdef HAVE_SPINLOCK\nclass SpinLock final : public Lock {\n    pthread_spinlock_t *impl;\n\n  public:\n    explicit SpinLock(bool shared);\n    ~SpinLock() override;\n    int lock(int operation = LOCK_EX, int timeout_msec = -1) override;\n    int unlock() override;\n};\n#endif\n\nclass CoroutineLock final : public Lock {\n    long cid = 0;\n    sw_atomic_t *value = nullptr;\n    void *coroutine = nullptr;\n\n    int lock_impl(bool blocking = true);\n\n  public:\n    explicit CoroutineLock(bool shared);\n    ~CoroutineLock() override;\n    int lock(int operation = LOCK_EX, int timeout_msec = -1) override;\n    int unlock() override;\n};\n\n#if defined(HAVE_PTHREAD_BARRIER) && !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__))\n#define SW_USE_PTHREAD_BARRIER\n#endif\n\nstruct Barrier {\n#ifdef SW_USE_PTHREAD_BARRIER\n    pthread_barrier_t barrier_;\n    pthread_barrierattr_t barrier_attr_;\n    bool shared_;\n#else\n    sw_atomic_t count_;\n    sw_atomic_t barrier_;\n#endif\n    void init(bool shared, int count);\n    void wait();\n    void destroy();\n};\n\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_log.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <string>\n#include <unistd.h>\n\n#define SW_LOG_BUFFER_SIZE (SW_ERROR_MSG_SIZE + 256)\n#define SW_LOG_DATE_STRLEN 128\n#define SW_LOG_DEFAULT_DATE_FORMAT \"%F %T\"\n\nenum swLogLevel {\n    SW_LOG_DEBUG = 0,\n    SW_LOG_TRACE,\n    SW_LOG_INFO,\n    SW_LOG_NOTICE,\n    SW_LOG_WARNING,\n    SW_LOG_ERROR,\n    SW_LOG_NONE,\n};\n\nenum swLogRotationType {\n    SW_LOG_ROTATION_SINGLE = 0,\n    SW_LOG_ROTATION_MONTHLY,\n    SW_LOG_ROTATION_DAILY,\n    SW_LOG_ROTATION_HOURLY,\n    SW_LOG_ROTATION_EVERY_MINUTE,\n};\n\nnamespace swoole {\nclass Logger {\n  private:\n    bool opened = false;\n    // Redirect stdin and stdout to log_fd\n    bool redirected = false;\n    bool display_backtrace_ = false;\n    int stdout_fd = -1;\n    int stderr_fd = -1;\n    FILE *log_fp = stdout;\n    int log_level = SW_LOG_INFO;\n    bool date_with_microseconds = false;\n    std::string date_format = SW_LOG_DEFAULT_DATE_FORMAT;\n    std::string log_file;\n    std::string log_real_file;\n    std::mutex lock;\n    int log_rotation = SW_LOG_ROTATION_SINGLE;\n\n    void reopen_without_lock();\n\n  public:\n    bool open(const char *logfile);\n    void reopen();\n    void set_stream(FILE *stream);\n    /**\n     * Only the `put` and `reopen` functions are thread-safe,\n     * other functions must be used in a single-threaded environment.\n     */\n    void put(int level, const char *content, size_t length);\n    void close();\n    void reset();\n    void set_level(int lv);\n    int get_level() const;\n    bool set_date_format(const char *format);\n    void set_rotation(int rotation);\n    const char *get_real_file();\n    const char *get_file() const;\n    bool is_opened() const;\n    bool redirect_stdout_and_stderr(bool enable);\n    void set_date_with_microseconds(bool enable);\n    std::string gen_real_file(const std::string &file) const;\n    static std::string get_pretty_name(const std::string &prettyFunction, bool strip = true);\n\n    void display_backtrace() {\n        display_backtrace_ = true;\n    }\n};\n}  // namespace swoole\n\nswoole::Logger *sw_logger();\n#define __SW_FUNC__ (swoole::Logger::get_pretty_name(__PRETTY_FUNCTION__).c_str())\n\n#define swoole_info(str, ...)                                                                                          \\\n    if (SW_LOG_INFO >= swoole_get_log_level()) {                                                                       \\\n        size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, str, ##__VA_ARGS__);                           \\\n        sw_logger()->put(SW_LOG_INFO, sw_error, _sw_error_len);                                                        \\\n    }\n\n#define swoole_notice(str, ...)                                                                                        \\\n    if (SW_LOG_NOTICE >= swoole_get_log_level()) {                                                                     \\\n        size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, str, ##__VA_ARGS__);                           \\\n        sw_logger()->put(SW_LOG_NOTICE, sw_error, _sw_error_len);                                                      \\\n    }\n\n#define swoole_sys_notice(str, ...)                                                                                    \\\n    do {                                                                                                               \\\n        swoole_set_last_error(errno);                                                                                  \\\n        if (SW_LOG_ERROR >= swoole_get_log_level()) {                                                                  \\\n            size_t _sw_error_len = sw_snprintf(sw_error,                                                               \\\n                                               SW_ERROR_MSG_SIZE,                                                      \\\n                                               \"%s(:%d): \" str \", Error: %s[%d]\",                                      \\\n                                               __SW_FUNC__,                                                            \\\n                                               __LINE__,                                                               \\\n                                               ##__VA_ARGS__,                                                          \\\n                                               swoole_strerror(errno),                                                 \\\n                                               errno);                                                                 \\\n            sw_logger()->put(SW_LOG_NOTICE, sw_error, _sw_error_len);                                                  \\\n        }                                                                                                              \\\n    } while (0)\n\n#define swoole_warning(str, ...)                                                                                       \\\n    do {                                                                                                               \\\n        if (SW_LOG_WARNING >= swoole_get_log_level()) {                                                                \\\n            size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, \"%s(): \" str, __SW_FUNC__, ##__VA_ARGS__); \\\n            sw_logger()->put(SW_LOG_WARNING, sw_error, _sw_error_len);                                                 \\\n        }                                                                                                              \\\n    } while (0)\n\n#define swoole_sys_warning(str, ...)                                                                                   \\\n    do {                                                                                                               \\\n        swoole_set_last_error(errno);                                                                                  \\\n        if (SW_LOG_ERROR >= swoole_get_log_level()) {                                                                  \\\n            size_t _sw_error_len = sw_snprintf(sw_error,                                                               \\\n                                               SW_ERROR_MSG_SIZE,                                                      \\\n                                               \"%s(): \" str \", Error: %s[%d]\",                                         \\\n                                               __SW_FUNC__,                                                            \\\n                                               ##__VA_ARGS__,                                                          \\\n                                               swoole_strerror(errno),                                                 \\\n                                               errno);                                                                 \\\n            sw_logger()->put(SW_LOG_WARNING, sw_error, _sw_error_len);                                                 \\\n        }                                                                                                              \\\n    } while (0)\n\n#define swoole_error(str, ...)                                                                                         \\\n    do {                                                                                                               \\\n        size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, \"%s(): \" str, __SW_FUNC__, ##__VA_ARGS__);     \\\n        sw_logger()->put(SW_LOG_ERROR, sw_error, _sw_error_len);                                                       \\\n        swoole_exit(1);                                                                                                \\\n    } while (0)\n\n#define swoole_sys_error(str, ...)                                                                                     \\\n    do {                                                                                                               \\\n        size_t _sw_error_len = sw_snprintf(sw_error,                                                                   \\\n                                           SW_ERROR_MSG_SIZE,                                                          \\\n                                           \"%s(): \" str \", Error: %s[%d]\",                                             \\\n                                           __SW_FUNC__,                                                                \\\n                                           ##__VA_ARGS__,                                                              \\\n                                           swoole_strerror(errno),                                                     \\\n                                           errno);                                                                     \\\n        sw_logger()->put(SW_LOG_ERROR, sw_error, _sw_error_len);                                                       \\\n        swoole_exit(1);                                                                                                \\\n    } while (0)\n\n#define swoole_fatal_error(code, str, ...) SwooleG.fatal_error(code, str, ##__VA_ARGS__)\n\n#define swoole_error_log(level, error, str, ...)                                                                       \\\n    do {                                                                                                               \\\n        swoole_set_last_error(error);                                                                                  \\\n        if (level >= swoole_get_log_level() && !swoole_is_ignored_error(error)) {                                      \\\n            size_t _sw_error_len =                                                                                     \\\n                sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, \"%s() (ERRNO %d): \" str, __SW_FUNC__, error, ##__VA_ARGS__);  \\\n            sw_logger()->put(level, sw_error, _sw_error_len);                                                          \\\n        }                                                                                                              \\\n    } while (0)\n\n#ifdef SW_DEBUG\n#define swoole_debug(str, ...)                                                                                         \\\n    if (SW_LOG_DEBUG >= swoole_get_log_level()) {                                                                      \\\n        size_t _sw_error_len =                                                                                         \\\n            sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, \"%s(:%d): \" str, __SW_FUNC__, __LINE__, ##__VA_ARGS__);           \\\n        sw_logger()->put(SW_LOG_DEBUG, sw_error, _sw_error_len);                                                       \\\n    }\n\n#define swoole_hex_dump(data, length)                                                                                  \\\n    do {                                                                                                               \\\n        const char *__data = (data);                                                                                   \\\n        size_t __length = (length);                                                                                    \\\n        swoole_debug(\"+----------+------------+-----------+-----------+------------+------------------+\");             \\\n        for (size_t of = 0; of < __length; of += 16) {                                                                 \\\n            char hex[16 * 3 + 1];                                                                                      \\\n            char str[16 + 1];                                                                                          \\\n            size_t i, hof = 0, sof = 0;                                                                                \\\n            for (i = of; i < of + 16 && i < __length; i++) {                                                           \\\n                hof += sprintf(hex + hof, \"%02x \", (__data)[i] & 0xff);                                                \\\n                sof += sprintf(str + sof, \"%c\", isprint((int) (__data)[i]) ? (__data)[i] : '.');                       \\\n            }                                                                                                          \\\n            swoole_debug(\"| %08zx | %-48s| %-16s |\", of, hex, str);                                                    \\\n        }                                                                                                              \\\n        swoole_debug(\"+----------+------------+-----------+-----------+------------+------------------+\");             \\\n    } while (0)\n#else\n#define swoole_debug(str, ...)\n#define swoole_hex_dump(data, length)\n#endif\n\nenum swTraceWhat : long {\n    /**\n     * Server\n     */\n    SW_TRACE_SERVER = 1u << 1,\n    SW_TRACE_CLIENT = 1u << 2,\n    SW_TRACE_BUFFER = 1u << 3,\n    SW_TRACE_CONN = 1u << 4,\n    SW_TRACE_EVENT = 1u << 5,\n    SW_TRACE_WORKER = 1u << 6,\n    SW_TRACE_MEMORY = 1u << 7,\n    SW_TRACE_REACTOR = 1u << 8,\n    SW_TRACE_PHP = 1u << 9,\n    SW_TRACE_HTTP = 1u << 10,\n    SW_TRACE_HTTP2 = 1u << 11,\n    SW_TRACE_EOF_PROTOCOL = 1u << 12,\n    SW_TRACE_LENGTH_PROTOCOL = 1u << 13,\n    SW_TRACE_CLOSE = 1u << 14,\n    SW_TRACE_WEBSOCKET = 1u << 15,\n    /**\n     * Client\n     */\n    SW_TRACE_REDIS_CLIENT = 1u << 16,\n    SW_TRACE_MYSQL_CLIENT = 1u << 17,\n    SW_TRACE_HTTP_CLIENT = 1u << 18,\n    SW_TRACE_AIO = 1u << 19,\n    SW_TRACE_SSL = 1u << 20,\n    SW_TRACE_NORMAL = 1u << 21,\n    /**\n     * Coroutine\n     */\n    SW_TRACE_CHANNEL = 1u << 22,\n    SW_TRACE_TIMER = 1u << 23,\n    SW_TRACE_SOCKET = 1u << 24,\n    SW_TRACE_COROUTINE = 1u << 25,\n    SW_TRACE_CONTEXT = 1u << 26,\n    SW_TRACE_CO_HTTP_SERVER = 1u << 27,\n    SW_TRACE_TABLE = 1u << 28,\n    SW_TRACE_CO_CURL = 1u << 29,\n    SW_TRACE_CARES = 1u << 30,\n\n    SW_TRACE_ZLIB = 1u << 31,\n    SW_TRACE_CO_PGSQL = 1ul << 32,\n    SW_TRACE_CO_ODBC = 1ul << 33,\n    SW_TRACE_CO_ORACLE = 1ul << 34,\n    SW_TRACE_CO_SQLITE = 1ul << 35,\n    SW_TRACE_CO_FIREBIRD = 1ul << 36,\n    SW_TRACE_CO_SSH2 = 1ul << 37,\n    /**\n     * Thread\n     */\n    SW_TRACE_THREAD = 1ul << 40,\n\n    SW_TRACE_ALL = 0x7fffffffffffffff\n};\n\n#ifdef SW_LOG_TRACE_OPEN\n#define swoole_trace_log(what, str, ...)                                                                               \\\n    if (SW_LOG_TRACE >= swoole_get_log_level() && (what & SwooleG.trace_flags)) {                                      \\\n        size_t _sw_error_len =                                                                                         \\\n            sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, \"%s(:%d): \" str, __SW_FUNC__, __LINE__, ##__VA_ARGS__);           \\\n        sw_logger()->put(SW_LOG_TRACE, sw_error, _sw_error_len);                                                       \\\n    }\n#else\n#define swoole_trace_log(what, str, ...)\n#endif\n\n#define swoole_trace(str, ...) swoole_trace_log(SW_TRACE_NORMAL, str, ##__VA_ARGS__)\n"
  },
  {
    "path": "include/swoole_lru_cache.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include <unordered_map>\n#include <list>\n#include <utility>\n#include <memory>\n#include <ctime>\n\nnamespace swoole {\n/**\n * This cache isn't thread safe\n */\ntemplate <typename T>\nclass LRUCache {\n    typedef std::pair<time_t, std::shared_ptr<T>> cache_node_t;\n    typedef std::list<std::pair<std::string, cache_node_t>> cache_list_t;\n\n    std::unordered_map<std::string, typename cache_list_t::iterator> cache_map;\n    cache_list_t cache_list;\n    size_t cache_capacity;\n\n  public:\n    explicit LRUCache(size_t capacity) {\n        cache_capacity = capacity;\n    }\n\n    std::shared_ptr<T> get(const std::string &key) {\n        auto iter = cache_map.find(key);\n        if (iter == cache_map.end()) {\n            return nullptr;\n        }\n\n        if (iter->second->second.first < ::time(nullptr) && iter->second->second.first > 0) {\n            return nullptr;\n        }\n\n        cache_list.splice(cache_list.begin(), cache_list, iter->second);\n        return iter->second->second.second;  // iter -> list::iter -> cache_node_t -> value\n    }\n\n    void set(const std::string &key, const std::shared_ptr<T> &val, time_t expire = 0) {\n        time_t expire_time;\n\n        if (expire <= 0) {\n            expire_time = 0;\n        } else {\n            expire_time = ::time(nullptr) + expire;\n        }\n\n        auto iter = cache_map.find(key);\n        if (iter != cache_map.end()) {\n            iter->second->second.first = expire_time;\n            iter->second->second.second = val;\n            cache_list.splice(cache_list.begin(), cache_list, iter->second);\n            return;\n        }\n\n        size_t size = cache_list.size();\n        if (size == cache_capacity && size > 0) {\n            auto del = cache_list.back();\n            cache_map.erase(del.first);\n            cache_list.pop_back();\n        }\n\n        cache_list.emplace_front(key, cache_node_t{expire_time, val});\n        cache_map[key] = cache_list.begin();\n    }\n\n    void del(const std::string &key) {\n        auto iter = cache_map.find(key);\n        if (iter == cache_map.end()) {\n            return;\n        }\n\n        cache_list.erase(iter->second);\n        cache_map.erase(iter);\n    }\n\n    void clear() {\n        cache_list.clear();\n        cache_map.clear();\n    }\n};\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_memory.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole.h\"\n\n//-------------------memory manager-------------------------\nnamespace swoole {\n\nclass MemoryPool {\n  public:\n    virtual ~MemoryPool() = default;\n    virtual void *alloc(uint32_t size) = 0;\n    virtual void free(void *ptr) = 0;\n\n  protected:\n    MemoryPool(){};\n};\n\nstruct FixedPoolImpl;\n\nclass FixedPool final : public MemoryPool {\n    FixedPoolImpl *impl;\n\n  public:\n    FixedPool(uint32_t slice_num, uint32_t slice_size, bool shared);\n    FixedPool(uint32_t slice_size, void *memory, size_t size, bool shared);\n    ~FixedPool() override;\n    void *alloc(uint32_t size) override;\n    void free(void *ptr) override;\n    void debug(int max_lines = 100) const;\n    uint32_t get_number_of_spare_slice() const;\n    uint32_t get_number_of_total_slice() const;\n    uint32_t get_slice_size() const;\n    static size_t sizeof_struct_slice();\n    static size_t sizeof_struct_impl();\n};\n\nstruct RingBufferImpl;\n\n// RingBuffer, In order for malloc / free\nclass RingBuffer : public MemoryPool {\n    RingBufferImpl *impl;\n\n  public:\n    RingBuffer(uint32_t size, bool shared);\n    ~RingBuffer() override;\n    void *alloc(uint32_t size) override;\n    void free(void *ptr) override;\n};\n\nstruct GlobalMemoryImpl;\n\n// Global memory, the program life cycle only malloc / free one time\nclass GlobalMemory : public MemoryPool {\n  private:\n    GlobalMemoryImpl *impl;\n\n  public:\n    GlobalMemory(uint32_t page_size, bool shared);\n    ~GlobalMemory() override;\n    void *alloc(uint32_t size) override;\n    void free(void *ptr) override;\n    size_t capacity() const;\n    size_t get_memory_size() const;\n};\n}  // namespace swoole\n\nvoid *sw_shm_malloc(size_t size);\nvoid sw_shm_free(void *ptr);\nvoid *sw_shm_calloc(size_t num, size_t _size);\nint sw_shm_protect(void *ptr, int flags);\nvoid *sw_shm_realloc(void *ptr, size_t new_size);\n"
  },
  {
    "path": "include/swoole_message_bus.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_string.h\"\n#include \"swoole_socket.h\"\n#include \"swoole_protocol.h\"\n\n#include <unordered_map>\n\nnamespace swoole {\nstruct PipeBuffer {\n    DataHead info;\n    char data[0];\n\n    bool is_begin() const {\n        return info.flags & SW_EVENT_DATA_BEGIN;\n    }\n\n    bool is_chunked() const {\n        return info.flags & SW_EVENT_DATA_CHUNK;\n    }\n\n    bool is_end() const {\n        return info.flags & SW_EVENT_DATA_END;\n    }\n};\n\nstruct PacketPtr {\n    size_t length;\n    char *data;\n};\n\nstruct DgramPacket {\n    SocketType socket_type;\n    network::Address socket_addr;\n    uint32_t length;\n    char data[0];\n};\n\nstruct PacketTask {\n    size_t length;\n    char tmpfile[SW_TASK_TMP_PATH_SIZE];\n};\n\nclass MessageBus {\n  private:\n    const Allocator *allocator_;\n    std::unordered_map<uint64_t, std::shared_ptr<String>> packet_pool_;\n    std::vector<network::Socket *> pipe_sockets_;\n    std::function<uint64_t(void)> id_generator_;\n    size_t buffer_size_;\n    PipeBuffer *buffer_ = nullptr;\n    bool always_chunked_transfer_ = false;\n\n    String *get_packet_buffer();\n    ReturnCode prepare_packet(uint16_t &recv_chunk_count, String *packet_buffer);\n\n  public:\n    MessageBus() {\n        allocator_ = sw_std_allocator();\n        buffer_size_ = SW_BUFFER_SIZE_STD;\n    }\n\n    ~MessageBus();\n\n    bool empty() const {\n        return packet_pool_.empty();\n    }\n\n    size_t count() const {\n        return packet_pool_.size();\n    }\n\n    void clear() {\n        packet_pool_.clear();\n    }\n\n    void set_allocator(const Allocator *allocator) {\n        allocator_ = allocator;\n    }\n\n    void set_id_generator(const std::function<uint64_t(void)> &id_generator) {\n        id_generator_ = id_generator;\n    }\n\n    void set_buffer_size(size_t buffer_size) {\n        buffer_size_ = buffer_size;\n    }\n\n    void set_always_chunked_transfer() {\n        always_chunked_transfer_ = true;\n    }\n\n    size_t get_buffer_size() const {\n        return buffer_size_;\n    }\n\n    size_t get_memory_size() const;\n    bool alloc_buffer();\n\n    /**\n     * If use the zend_string_allocator, must manually call this function to release the memory,\n     * otherwise coredump will occur when php shutdown, because zend_string has been released\n     */\n    void free_buffer() {\n        allocator_->free(buffer_);\n        buffer_ = nullptr;\n    }\n\n    void pass(const SendData *task) const;\n\n    /**\n     * Send data to socket. If the data sent is larger than Server::ipc_max_size, then it is sent in chunks.\n     * Otherwise, send it directly.\n     * When sending data in multi-thread environment, must use get_pipe_socket() to separate socket memory.\n     * @return: send success returns true, send failure returns false.\n     */\n    bool write(network::Socket *sock, SendData *packet) const;\n    /**\n     * Receive data from socket, if only one chunk is received, packet will be saved in packet_pool.\n     * Then continue to listen to readable events, waiting for more chunks.\n     * @return: >0: receive a complete packet, 0: continue to wait for data, -1: an error occurred\n     */\n    ssize_t read(network::Socket *sock);\n    /**\n     * Receive data from pipeline, and store data to buffer\n     * @return: >0: receive a complete packet, 0: continue to wait for data, -1: an error occurred\n     */\n    ssize_t read_with_buffer(network::Socket *sock);\n    /**\n     * The last chunk of data has been received, return address and length, start processing this packet.\n     */\n    PacketPtr get_packet() const;\n    PipeBuffer *get_buffer() const {\n        return buffer_;\n    }\n    /**\n     * Pop the data memory address to the outer layer, no longer managed by MessageBus\n     */\n    char *move_packet();\n    /**\n     * The processing of this data packet has been completed, and the relevant memory has been released\n     */\n    void pop() {\n        if (buffer_->is_end()) {\n            packet_pool_.erase(buffer_->info.msg_id);\n        }\n    }\n    /**\n     * It is possible to operate the same pipe in multiple threads.\n     * Each thread must have a unique buffer and the socket memory must be separated.\n     */\n    network::Socket *get_pipe_socket(const network::Socket *sock) const {\n        return pipe_sockets_[sock->get_fd()];\n    }\n    void init_pipe_socket(const network::Socket *sock);\n};\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_mime_type.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include <unordered_map>\n#include <string>\n\nnamespace swoole {\nnamespace mime_type {\nconst std::unordered_map<std::string, std::string> &list();\nbool add(const std::string &suffix, const std::string &mime_type);\nvoid set(const std::string &suffix, const std::string &mime_type);\nbool del(const std::string &suffix);\nconst std::string &get(const std::string &filename);\nbool exists(const std::string &filename);\n}  // namespace mime_type\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_mqtt.h",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2015 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n#pragma once\n\n#include \"swoole_protocol.h\"\n\n#define SW_MQTT_MIN_LENGTH_SIZE 1\n#define SW_MQTT_MAX_LENGTH_SIZE 4\n#define SW_MQTT_MAX_PAYLOAD_SIZE 268435455\n\n#define SW_MQTT_SETRETAIN(HDR, R) (HDR | (R))\n#define SW_MQTT_SETQOS(HDR, Q) (HDR | ((Q) << 1))\n#define SW_MQTT_SETDUP(HDR, D) (HDR | ((D) << 3))\n\nnamespace swoole {\nnamespace mqtt {\n\nenum Opcode {\n    SW_MQTT_CONNECT = 0x10,\n    SW_MQTT_CONNACK = 0x20,\n    SW_MQTT_PUBLISH = 0x30,\n    SW_MQTT_PUBACK = 0x40,\n    SW_MQTT_PUBREC = 0x50,\n    SW_MQTT_PUBREL = 0x60,\n    SW_MQTT_PUBCOMP = 0x70,\n    SW_MQTT_SUBSCRIBE = 0x80,\n    SW_MQTT_SUBACK = 0x90,\n    SW_MQTT_UNSUBSCRIBE = 0xA0,\n    SW_MQTT_UNSUBACK = 0xB0,\n    SW_MQTT_PINGREQ = 0xC0,\n    SW_MQTT_PINGRESP = 0xD0,\n    SW_MQTT_DISCONNECT = 0xE0,\n};\n\nstruct Packet {\n    uint8_t type : 4;\n    uint8_t dup : 1;\n    uint8_t qos : 2;\n    uint8_t retain : 1;\n    uint32_t length;\n    char protocol_name[8];\n};\n\nssize_t get_package_length(const Protocol *protocol, network::Socket *conn, PacketLength *pl);\nvoid set_protocol(Protocol *protocol);\n}  // namespace mqtt\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_msg_queue.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole.h\"\n\n#include <sys/types.h>\n\nnamespace swoole {\n\nenum {\n    SW_MSGQUEUE_ORIENT = 1,\n    SW_MSGQUEUE_BALANCE = 2,\n};\n\nstruct QueueNode {\n    long mtype;                    /* type of received/sent message */\n    char mdata[sizeof(EventData)]; /* text of the message */\n};\n\nclass MsgQueue {\n  private:\n    bool blocking_;\n    int msg_id_;\n    key_t msg_key_;\n    int flags_;\n    int perms_;\n\n  public:\n    explicit MsgQueue(key_t msg_key, bool blocking = true, int perms = 0);\n    ~MsgQueue();\n\n    bool ready() const {\n        return msg_id_ >= 0;\n    }\n\n    int get_id() const {\n        return msg_id_;\n    }\n\n    void set_blocking(bool blocking);\n    bool set_capacity(size_t queue_bytes) const;\n    bool push(const QueueNode *in, size_t mdata_length) const;\n    ssize_t pop(QueueNode *out, size_t mdata_size) const;\n    bool stat(size_t *queue_num, size_t *queue_bytes) const;\n    bool destroy();\n};\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_pipe.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_socket.h\"\n\nenum swPipe_close_which {\n    SW_PIPE_CLOSE_MASTER = 1,\n    SW_PIPE_CLOSE_WORKER = 2,\n    SW_PIPE_CLOSE_READ = 3,\n    SW_PIPE_CLOSE_WRITE = 4,\n    SW_PIPE_CLOSE_BOTH = 0,\n};\n\nnamespace swoole {\nclass SocketPair {\n  protected:\n    bool blocking;\n    double timeout;\n\n    /**\n     * master : socks[1], for write operation\n     * worker : socks[0], for read operation\n     */\n    int socks[2]{};\n\n    network::Socket *master_socket = nullptr;\n    network::Socket *worker_socket = nullptr;\n\n    void init_socket(int master_fd, int worker_fd);\n\n  public:\n    explicit SocketPair(bool _blocking) {\n        blocking = _blocking;\n        timeout = network::Socket::default_read_timeout;\n    }\n    ~SocketPair();\n\n    ssize_t read(void *_buf, size_t length) const;\n    ssize_t write(const void *_buf, size_t length) const;\n    void clean() const;\n    bool close(int which = 0);\n\n    network::Socket *get_socket(bool _master) const {\n        return _master ? master_socket : worker_socket;\n    }\n\n    bool ready() const {\n        return master_socket != nullptr && worker_socket != nullptr;\n    }\n\n    void set_timeout(double _timeout) {\n        timeout = _timeout;\n        master_socket->set_timeout(timeout);\n        worker_socket->set_timeout(timeout);\n    }\n\n    void set_blocking(bool _blocking);\n};\n\nclass Pipe : public SocketPair {\n  public:\n    explicit Pipe(bool blocking);\n};\n\nclass UnixSocket : public SocketPair {\n    int protocol_;\n\n  public:\n    UnixSocket(bool blocking, int _protocol);\n    bool set_buffer_size(size_t _size) const;\n};\n\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_process_pool.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_signal.h\"\n#include \"swoole_lock.h\"\n#include \"swoole_pipe.h\"\n#include \"swoole_channel.h\"\n#include \"swoole_msg_queue.h\"\n#include \"swoole_message_bus.h\"\n\n#include <sys/wait.h>\n\n#include <csignal>\n#include <unordered_map>\n#include <unordered_set>\n#include <queue>\n\nenum swWorkerStatus {\n    SW_WORKER_BUSY = 1,\n    SW_WORKER_IDLE = 2,\n    SW_WORKER_EXIT = 3,\n};\n\nenum swWorkerType {\n    SW_MASTER = 1,\n    SW_WORKER = 2,\n    SW_MANAGER = 3,\n    SW_EVENT_WORKER = 2,\n    SW_TASK_WORKER = 4,\n    SW_USER_WORKER = 5,\n};\n\nenum swIPCMode {\n    SW_IPC_NONE = 0,\n    SW_IPC_UNIXSOCK = 1,\n    SW_IPC_MSGQUEUE = 2,\n    SW_IPC_SOCKET = 3,\n};\n\nSW_API swoole::WorkerId swoole_get_worker_id();\nSW_API pid_t swoole_get_worker_pid();\nSW_API int swoole_get_worker_type();\nSW_API void swoole_set_worker_pid(pid_t pid);\nSW_API void swoole_set_worker_id(swoole::WorkerId worker_id);\nSW_API void swoole_set_worker_type(int type);\nSW_API char swoole_get_worker_symbol();\n\nnamespace swoole {\nenum WorkerMessageType {\n    SW_WORKER_MESSAGE_STOP = 1,\n};\n\nenum ProtocolType {\n    SW_PROTOCOL_TASK = 1,\n    SW_PROTOCOL_STREAM,\n    SW_PROTOCOL_MESSAGE,\n};\n\nstruct WorkerStopMessage {\n    pid_t pid;\n    uint16_t worker_id;\n};\n\nclass ExitStatus {\n    pid_t pid_;\n    int status_;\n\n  public:\n    ExitStatus(pid_t _pid, int _status) : pid_(_pid), status_(_status) {}\n\n    pid_t get_pid() const {\n        return pid_;\n    }\n\n    int get_status() const {\n        return status_;\n    }\n\n    int get_code() const {\n        return WEXITSTATUS(status_);\n    }\n\n    int get_signal() const {\n        return WTERMSIG(status_);\n    }\n\n    bool is_normal_exit() const {\n        return WIFEXITED(status_);\n    }\n};\n\nstatic inline ExitStatus wait_process() {\n    int status = 0;\n    pid_t pid = ::wait(&status);\n    return {pid, status};\n}\n\nstatic inline ExitStatus wait_process(pid_t _pid, int options) {\n    int status = 0;\n    pid_t pid = ::waitpid(_pid, &status, options);\n    return {pid, status};\n}\n\nstruct ProcessPool;\nstruct Worker;\n\nstruct WorkerGlobal {\n    WorkerId id;\n    uint8_t type;\n    pid_t pid;\n    bool shutdown;\n    bool running;\n    uint32_t max_request;\n    /**\n     * worker is shared memory, visible in other work processes.\n     * When a worker process restarts, it may be held by both the old and new processes simultaneously,\n     * necessitating careful handling of the state.\n     */\n    Worker *worker;\n    /**\n     *  worker_copy is a copy of worker,\n     *  but it must be local memory and only used within the current process or thread.\n     *  It is not visible to other worker processes.\n     */\n    Worker *worker_copy;\n    time_t exit_time;\n};\n\nstruct Worker {\n    pid_t pid;\n    WorkerId id;\n    ProcessPool *pool;\n    MsgQueue *queue;\n    bool shared;\n\n    bool redirect_stdout;\n    bool redirect_stdin;\n    bool redirect_stderr;\n\n    /**\n     * worker status, IDLE or BUSY\n     */\n    uint8_t status;\n    uint8_t type;\n    uint8_t msgqueue_mode;\n    uint8_t child_process;\n\n    uint32_t concurrency;\n    time_t start_time;\n\n    sw_atomic_long_t dispatch_count;\n    sw_atomic_long_t request_count;\n    sw_atomic_long_t response_count;\n    size_t coroutine_num;\n\n    Mutex *lock;\n    UnixSocket *pipe_object;\n\n    network::Socket *pipe_master;\n    network::Socket *pipe_worker;\n    network::Socket *pipe_current;\n\n    void *ptr;\n\n    ssize_t send_pipe_message(const void *buf, size_t n, int flags) const;\n    bool has_exceeded_max_request() const;\n    void set_max_request(uint32_t max_request, uint32_t max_request_grace);\n    void report_error(const ExitStatus &exit_status) const;\n    /**\n     * Init global state for worker process.\n     * Must be called after the process is spawned and before the main loop is executed.\n     */\n    void init();\n    void shutdown();\n    bool is_shutdown();\n    static bool is_running();\n\n    void set_status(swWorkerStatus _status) {\n        status = _status;\n    }\n\n    void set_status_to_idle() {\n        set_status(SW_WORKER_IDLE);\n    }\n\n    void set_status_to_busy() {\n        set_status(SW_WORKER_BUSY);\n    }\n\n    void add_request_count() {\n        request_count++;\n    }\n\n    bool is_busy() const {\n        return status == SW_WORKER_BUSY;\n    }\n\n    bool is_idle() const {\n        return status == SW_WORKER_IDLE;\n    }\n};\n\nstruct StreamInfo {\n    network::Socket *socket;\n    network::Socket *last_connection;\n    char *socket_file;\n    int socket_port;\n    String *response_buffer;\n};\n\nstruct ReloadTask {\n    std::unordered_map<pid_t, Worker *> workers;\n    std::queue<pid_t> kill_queue;\n    TimerNode *timer = nullptr;\n\n    size_t count() const {\n        return workers.size();\n    }\n\n    bool is_completed() const {\n        return workers.empty();\n    }\n\n    bool exists(pid_t pid) {\n        return workers.find(pid) != workers.end();\n    }\n\n    ReloadTask() = default;\n    ~ReloadTask();\n    void kill_one(int signal_number = SIGTERM);\n    void kill_all(int signal_number = SIGKILL);\n    void add_workers(Worker *list, size_t n);\n    void add_timeout_killer(int timeout);\n    bool remove(pid_t pid);\n    void clear_queue();\n};\n\nstruct ProcessPool {\n    bool running;\n    bool reload_init;\n    bool read_message;\n    bool started;\n    bool schedule_by_sysvmsg;\n    bool async;\n\n    uint8_t ipc_mode;\n    ProtocolType protocol_type_;\n    pid_t master_pid;\n    uint32_t max_wait_time;\n    uint64_t reload_count;\n    time_t reload_last_time;\n\n    /**\n     * process type\n     */\n    uint8_t type;\n\n    /**\n     * worker->id = start_id + i\n     */\n    uint16_t start_id;\n\n    /**\n     * use message queue IPC\n     */\n    uint8_t use_msgqueue;\n\n    /**\n     * use stream socket IPC\n     */\n    uint8_t use_socket;\n\n    char *packet_buffer;\n    uint32_t max_packet_size_;\n\n    /**\n     * message queue key\n     */\n    key_t msgqueue_key;\n\n    uint32_t worker_num;\n    uint32_t max_request;\n    uint32_t max_request_grace;\n\n    /**\n     * No idle task work process is available.\n     */\n    uint8_t scheduler_warning;\n    time_t warning_time;\n\n    void (*onStart)(ProcessPool *pool);\n    void (*onShutdown)(ProcessPool *pool);\n    void (*onBeforeReload)(ProcessPool *pool);\n    void (*onAfterReload)(ProcessPool *pool);\n    int (*onTask)(ProcessPool *pool, Worker *worker, EventData *task);\n    void (*onWorkerStart)(ProcessPool *pool, Worker *worker);\n    void (*onMessage)(ProcessPool *pool, RecvData *msg);\n    void (*onWorkerExit)(ProcessPool *pool, Worker *worker);\n    void (*onWorkerStop)(ProcessPool *pool, Worker *worker);\n    void (*onWorkerError)(ProcessPool *pool, Worker *worker, const ExitStatus &exit_status);\n    void (*onWorkerMessage)(ProcessPool *pool, EventData *msg);\n    int (*onWorkerNotFound)(ProcessPool *pool, const ExitStatus &exit_status);\n    int (*main_loop)(ProcessPool *pool, Worker *worker);\n\n    sw_atomic_t round_id;\n\n    Worker *workers;\n    std::vector<std::shared_ptr<UnixSocket>> *pipes;\n    std::unordered_map<pid_t, Worker *> *map_;\n    MsgQueue *queue;\n    StreamInfo *stream_info_;\n    Channel *message_box = nullptr;\n    MessageBus *message_bus = nullptr;\n    ReloadTask *reload_task = nullptr;\n\n    void *ptr;\n\n    Worker *get_worker(WorkerId worker_id) const {\n        return &(workers[worker_id - start_id]);\n    }\n\n    static TaskId get_task_id(const EventData *task) {\n        return task->info.fd;\n    }\n\n    static WorkerId get_task_src_worker_id(const EventData *task) {\n        return task->info.reactor_id;\n    }\n\n    void set_max_packet_size(uint32_t _max_packet_size) {\n        max_packet_size_ = _max_packet_size;\n    }\n\n    bool is_master() const {\n        return swoole_get_worker_type() == SW_MASTER;\n    }\n\n    bool is_worker() const {\n        return swoole_get_worker_type() == SW_WORKER;\n    }\n\n    /**\n     * SW_PROTOCOL_TASK\n     * ==================================================================\n     * The `EventData` structure must be sent as a single message and cannot be split into multiple transmissions.\n     * If the length of the message content exceeds the size limit of the data field in EventData,\n     * it should be written to a temporary file.\n     * In this case, set the SW_TASK_TMPFILE flag in info.ext_flags.\n     * Only the path to the temporary file will be transmitted,\n     * and the receiving end should retrieve the actual message content from this temporary file.\n     * Reference: Server::task_pack()\n     *\n     * SW_PROTOCOL_MESSAGE\n     * ==================================================================\n     * When sending the `EventData` structure, the message can be split into multiple transmissions.\n     * When sending data in multiple parts, you must set a unique `info.msg_id`.\n     * For the first slice, set the `info.flags` with the SW_EVENT_DATA_CHUNK | SW_EVENT_DATA_BEGIN flag,\n     * and for the last slice, set the `info.flags` with the SW_EVENT_DATA_CHUNK | SW_EVENT_DATA_END flag.\n     * The receiving end will place the data into a memory cache table, merge the data,\n     * and only execute the onMessage callback once the complete message has been received.\n     *\n     * Reference: MessageBus::write() and MessageBus::read()\n     *\n     * SW_PROTOCOL_STREAM\n     * ==================================================================\n     *  +-------------------------------+-------------------------------+\n     *  | Payload Length     ( 4 byte, network byte order)              |\n     *  | Payload Data ...   ( Payload Length byte )                    |\n     *  +-------------------------------- - - - - - - - - - - - - - - - +\n     *\n     *  The packet consists of a 4 byte length header followed by the data payload.\n     *  The receiving end should first use `socket.recv(&payload_len, 4)` to obtain the length of the data payload.\n     *  Then, execute `socket.recv(payload, payload_len)` to receive the complete data.\n     *  Please note that sufficient memory space must be allocated for the payload,\n     *  for example, `payload = malloc(payload_len)`.\n     */\n    void set_protocol(ProtocolType _protocol_type);\n    void set_type(int _type);\n    void set_start_id(int _start_id);\n    void set_max_request(uint32_t _max_request, uint32_t _max_request_grace);\n    bool detach();\n    int wait();\n    int start_check();\n    int start();\n    bool shutdown();\n    bool reload();\n    void reopen_logger();\n\n    void trigger_read_message_event() {\n        read_message = true;\n    }\n\n    pid_t spawn(Worker *worker);\n    void stop(Worker *worker);\n    void kill_all_workers(int signo = SIGKILL);\n    swResultCode dispatch(EventData *data, int *worker_id);\n    int response(const char *data, uint32_t length) const;\n    swResultCode dispatch_sync(EventData *data, int *dst_worker_id);\n    swResultCode dispatch_sync(const char *data, uint32_t len) const;\n    void add_worker(Worker *worker) const;\n    bool del_worker(const Worker *worker) const;\n    Worker *get_worker_by_pid(pid_t pid) const;\n    void destroy();\n    int create(uint32_t worker_num, key_t msgqueue_key = 0, swIPCMode ipc_mode = SW_IPC_NONE);\n    int create_message_box(size_t memory_size);\n    int create_message_bus();\n    int push_message(uint8_t _type, const void *data, size_t length) const;\n    int push_message(const EventData *msg) const;\n    bool send_message(WorkerId worker_id, const char *message, size_t l_message) const;\n    int pop_message(void *data, size_t size) const;\n    int listen(const char *socket_file, int backlog) const;\n    int listen(const char *host, int port, int backlog) const;\n    int schedule();\n    bool is_worker_running(Worker *worker) const;\n\n  private:\n    static int recv_packet(Reactor *reactor, Event *event);\n    static int recv_message(Reactor *reactor, Event *event);\n    static int run_with_task_protocol(ProcessPool *pool, Worker *worker);\n    static int run_with_stream_protocol(ProcessPool *pool, Worker *worker);\n    static int run_with_message_protocol(ProcessPool *pool, Worker *worker);\n    static int run_async(ProcessPool *pool, Worker *worker);\n    void at_worker_enter(Worker *worker) const;\n    void at_worker_exit(Worker *worker);\n\n    bool wait_detached_worker(std::unordered_set<pid_t> &detached_workers, pid_t pid);\n};\n};  // namespace swoole\n\nstatic sw_inline int swoole_kill(pid_t _pid, int _sig) {\n    return kill(_pid, _sig);\n}\n\ntypedef swoole::ProtocolType swProtocolType;\n\nextern SW_THREAD_LOCAL swoole::WorkerGlobal SwooleWG;\n\nstatic inline swoole::Worker *sw_worker() {\n    return SwooleWG.worker;\n}\n"
  },
  {
    "path": "include/swoole_protocol.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_socket.h\"\n\n#include <netdb.h>\n\nnamespace swoole {\nstruct SendData {\n    DataHead info;\n    const char *data;\n};\n\nstruct RecvData {\n    DataHead info;\n    const char *data;\n\n    SessionId session_id() const {\n        return info.fd;\n    }\n\n    uint32_t length() const {\n        return info.len;\n    }\n};\n\nstruct PacketLength {\n    const char *buf;\n    uint32_t buf_size;\n    uint32_t header_len;\n};\n\nstruct WebSocketSettings {\n    std::string protocol;           // with `Sec-WebSocket-Protocol` HTTP Header\n    bool in_server;                 // server or client\n    bool mask = true;               // enable websocket mask\n    bool open_ping_frame = false;   // handle websocket ping frame by user\n    bool open_pong_frame = false;   // handle websocket pong frame by user\n    bool open_close_frame = false;  // handle websocket close frame by user\n    /**\n     * The default value is false, which means that websocket frame data compression is not enabled.\n     * If supported by `zlib` or other compression libraries, the client can accept compressed data\n     * (depending on whether the `Sec-Websocket-Extensions` header contains `permessage-deflate`)\n     */\n    bool compression = false;\n};\n\nstruct Protocol {\n    typedef ssize_t (*LengthFunc)(const Protocol *, network::Socket *, PacketLength *pl);\n    /* one package: eof check */\n    bool split_by_eof;\n\n    char package_eof[SW_DATA_EOF_MAXLEN];\n    uint8_t package_eof_len;\n\n    char package_length_type;\n    uint8_t package_length_size;\n    uint16_t package_length_offset;\n    uint16_t package_body_offset;\n    uint32_t package_max_length;\n\n    void *private_data_1;\n    void *private_data_2;\n\n    /**\n     * callback this function when a complete data packet is received\n     */\n    int (*onPackage)(const Protocol *, network::Socket *, const RecvData *);\n    /**\n     * parse the length value in the received data\n     * @return 0: more data needs to be received\n     * @return -1: abnormal value, connection should be closed\n     * @return >0: the length of the data packet\n     */\n    LengthFunc get_package_length;\n    uint8_t (*get_package_length_size)(network::Socket *);\n\n    int recv_with_eof_protocol(network::Socket *socket, String *buffer) const;\n    int recv_with_length_protocol(network::Socket *socket, String *buffer) const;\n    int recv_split_by_eof(network::Socket *socket, String *buffer) const;\n\n    static ssize_t default_length_func(const Protocol *protocol, network::Socket *socket, PacketLength *pl);\n};\n}  // namespace swoole\n\n#ifdef __BYTE_ORDER\n#define SW_BYTE_ORDER __BYTE_ORDER\n#elif defined(_BYTE_ORDER)\n#define SW_BYTE_ORDER _BYTE_ORDER\n#elif defined(BYTE_ORDER)\n#define SW_BYTE_ORDER BYTE_ORDER\n#else\n#error \"Unable to determine machine byte order\"\n#endif\n\n#ifdef __LITTLE_ENDIAN\n#define SW_LITTLE_ENDIAN __LITTLE_ENDIAN\n#elif defined(_LITTLE_ENDIAN)\n#define SW_LITTLE_ENDIAN _LITTLE_ENDIAN\n#elif defined(LITTLE_ENDIAN)\n#define SW_LITTLE_ENDIAN LITTLE_ENDIAN\n#else\n#error \"No LITTLE_ENDIAN macro\"\n#endif\n\nstatic sw_inline uint16_t swoole_swap_endian16(uint16_t x) {\n    return (((x & 0xff) << 8) | ((x & 0xff00) >> 8));\n}\n\nstatic sw_inline uint32_t swoole_swap_endian32(uint32_t x) {\n    return (((x & 0xff) << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | ((x & 0xff000000) >> 24));\n}\n\nstatic sw_inline uint64_t swoole_swap_endian64(uint64_t x) {\n    return (((x & 0xff) << 56) | ((x & 0xff00) << 40) | ((x & 0xff0000) << 24) | ((x & 0xff000000) << 8) |\n            ((x & 0xff00000000) >> 8) | ((x & 0xff0000000000) >> 24) | ((x & 0xff000000000000) >> 40) |\n            ((x & 0xff00000000000000) >> 56));\n}\n\nint64_t swoole_unpack(char type, const void *data);\nuint64_t swoole_hton64(uint64_t value);\nuint64_t swoole_ntoh64(uint64_t value);\n\nvoid swoole_dump_ascii(const char *data, size_t size);\nvoid swoole_dump_bin(const uchar *data, char type, size_t size);\nvoid swoole_dump_hex(const uchar *data, size_t outlen);\n\nchar *swoole_dec2hex(ulong_t value, int base);\nulong_t swoole_hex2dec(const char *hex, size_t *parsed_bytes);\nint swoole_type_size(char type);\n"
  },
  {
    "path": "include/swoole_proxy.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include <string>\n#include <cstdint>\n#include <functional>\n\n#define SW_SOCKS5_VERSION_CODE 0x05\n#define SW_HTTP_PROXY_CHECK_MESSAGE 0\n#define SW_HTTP_PROXY_HANDSHAKE_RESPONSE \"HTTP/1.1 200 Connection established\\r\\n\"\n\n#define SW_HTTP_PROXY_FMT                                                                                              \\\n    \"CONNECT %.*s:%d HTTP/1.1\\r\\n\"                                                                                     \\\n    \"Host: %.*s:%d\\r\\n\"                                                                                                \\\n    \"User-Agent: Swoole/\" SWOOLE_VERSION \"\\r\\n\"                                                                        \\\n    \"Proxy-Connection: Keep-Alive\\r\\n\"\n\nenum swHttpProxyState {\n    SW_HTTP_PROXY_STATE_WAIT = 0,\n    SW_HTTP_PROXY_STATE_HANDSHAKE,\n    SW_HTTP_PROXY_STATE_READY,\n};\n\nenum swSocks5State {\n    SW_SOCKS5_STATE_WAIT = 0,\n    SW_SOCKS5_STATE_HANDSHAKE,\n    SW_SOCKS5_STATE_AUTH,\n    SW_SOCKS5_STATE_CONNECT,\n    SW_SOCKS5_STATE_READY,\n};\n\nenum swSocks5Method {\n    SW_SOCKS5_METHOD_NO_AUTH = 0x00,\n    SW_SOCKS5_METHOD_AUTH = 0x02,\n};\n\nnamespace swoole {\nclass String;\n\nstruct HttpProxy {\n    uint8_t state;\n    uint8_t dont_handshake;\n    int port;\n    std::string host;\n    std::string username;\n    std::string password;\n    std::string target_host;\n    int target_port;\n\n    std::string get_auth_str() const;\n    size_t pack(const String *send_buffer, const std::string &host_name) const;\n    static bool handshake(const String *recv_buffer);\n\n    static HttpProxy *create(const std::string &host, int port, const std::string &user, const std::string &pwd);\n};\n\nstruct Socks5Proxy {\n    std::string host;\n    int port;\n    uint8_t state;\n    uint8_t version;\n    uint8_t method;\n    uint8_t dns_tunnel;\n    std::string username;\n    std::string password;\n    std::string target_host;\n    int target_port;\n    int socket_type;\n    char buf[512];\n\n    ssize_t pack_negotiate_request();\n    ssize_t pack_auth_request();\n    ssize_t pack_connect_request();\n    bool handshake(const char *rbuf, size_t rlen, const std::function<ssize_t(const char *buf, size_t len)> &send_fn);\n\n    static const char *strerror(int code);\n    static Socks5Proxy *create(\n        int socket_type, const std::string &host, int port, const std::string &user, const std::string &pwd);\n};\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_reactor.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_socket.h\"\n\n#include <list>\n#include <map>\n#include <unordered_map>\n\nnamespace swoole {\n\nstruct DeferCallback {\n    Callback callback;\n    void *data;\n};\n\nclass Reactor;\n\nclass ReactorImpl {\n  protected:\n    Reactor *reactor_;\n\n  public:\n    explicit ReactorImpl(Reactor *_reactor) {\n        reactor_ = _reactor;\n    }\n    void after_removal_failure(const network::Socket *_socket) const;\n    virtual ~ReactorImpl() = default;\n    virtual bool ready() = 0;\n    virtual int add(network::Socket *socket, int events) = 0;\n    virtual int set(network::Socket *socket, int events) = 0;\n    virtual int del(network::Socket *socket) = 0;\n    virtual int wait() = 0;\n};\n\nclass CallbackManager {\n  public:\n    typedef std::list<std::pair<Callback, void *>> TaskList;\n    void append(const Callback &fn, void *private_data) {\n        list_.emplace_back(fn, private_data);\n    }\n    void prepend(const Callback &fn, void *private_data) {\n        list_.emplace_front(fn, private_data);\n        auto t = list_.back();\n    }\n    void remove(const TaskList::iterator iter) {\n        list_.erase(iter);\n    }\n    void execute() {\n        while (!list_.empty()) {\n            std::pair<Callback, void *> task = list_.front();\n            list_.pop_front();\n            task.first(task.second);\n        }\n    }\n\n  protected:\n    TaskList list_;\n};\n\nclass Reactor {\n  public:\n    enum Type {\n        TYPE_AUTO,\n        TYPE_EPOLL,\n        TYPE_KQUEUE,\n        TYPE_POLL,\n    };\n\n    enum EndCallback {\n        PRIORITY_TIMER = 0,\n        PRIORITY_DEFER_TASK,\n        PRIORITY_IDLE_TASK,\n        PRIORITY_SIGNAL_CALLBACK,\n        PRIORITY_TRY_EXIT,\n        PRIORITY_MALLOC_TRIM,\n        PRIORITY_WORKER_CALLBACK,\n        /**\n         * PRIORITY_IOURING_SUBMIT must be the last one, as other callback functions might allocate new SQEs.\n         * It is essential to ensure that the SQE is submitted before the next event loop iteration and before the\n         * epoll_wait() call.\n         */\n        PRIORITY_IOURING_SUBMIT,\n    };\n\n    enum ExitCondition {\n        EXIT_CONDITION_TIMER = 0,\n        EXIT_CONDITION_DEFER_TASK,\n        EXIT_CONDITION_WAIT_PID,\n        EXIT_CONDITION_CO_SIGNAL_LISTENER,\n        EXIT_CONDITION_SIGNAL_LISTENER,\n        EXIT_CONDITION_AIO_TASK,\n        EXIT_CONDITION_SIGNALFD,\n        EXIT_CONDITION_USER_BEFORE_DEFAULT,\n        EXIT_CONDITION_FORCED_TERMINATION,\n        EXIT_CONDITION_IOURING,\n        EXIT_CONDITION_DEFAULT = 999,\n        EXIT_CONDITION_USER_AFTER_DEFAULT,\n    };\n\n    Type type_;\n    void *ptr = nullptr;\n    int native_handle = -1;\n    uint32_t max_event_num = 0;\n\n    bool ready_ = false;\n    bool running = false;\n    bool start = false;\n    bool once = false;\n    bool wait_exit = false;\n    bool destroyed = false;\n    bool bailout = false;\n    bool timed_out = false;\n\n    /**\n     * reactor->wait timeout (millisecond) or -1\n     */\n    int32_t timeout_msec = 0;\n\n    uint16_t id = 0;\n\n    uint32_t max_socket = 0;\n\n#ifdef SW_USE_MALLOC_TRIM\n    time_t last_malloc_trim_time = 0;\n#endif\n\n    ReactorHandler read_handler[SW_MAX_FDTYPE] = {};\n    ReactorHandler write_handler[SW_MAX_FDTYPE] = {};\n    ReactorHandler error_handler[SW_MAX_FDTYPE] = {};\n\n    ReactorHandler default_write_handler = nullptr;\n    ReactorHandler default_error_handler = nullptr;\n\n    int add(network::Socket *socket, int events) const {\n        return impl->add(socket, events);\n    }\n\n    int set(network::Socket *socket, int events) const {\n        return impl->set(socket, events);\n    }\n\n    int del(network::Socket *socket) const {\n        return impl->del(socket);\n    }\n\n    int wait() const {\n        return impl->wait();\n    }\n\n    CallbackManager *defer_tasks = nullptr;\n    CallbackManager destroy_callbacks;\n\n    DeferCallback idle_task;\n    DeferCallback future_task;\n\n#ifdef SW_USE_IOURING\n    std::function<void(Reactor *)> iouring_interrupt_handler;\n#endif\n\n    ssize_t (*write)(Reactor *reactor, network::Socket *socket, const void *buf, size_t n) = nullptr;\n    ssize_t (*writev)(Reactor *reactor, network::Socket *socket, const iovec *iov, size_t iovcnt) = nullptr;\n    int (*close)(Reactor *reactor, network::Socket *socket) = nullptr;\n\n  private:\n    ReactorImpl *impl;\n    std::map<int, std::function<void(Reactor *)>> end_callbacks;\n    std::map<int, std::function<bool(Reactor *, size_t &)>> exit_conditions;\n    std::unordered_map<int, network::Socket *> sockets_;\n\n  public:\n    explicit Reactor(int max_event = SW_REACTOR_MAXEVENTS, Type _type = TYPE_AUTO);\n    ~Reactor();\n    bool if_exit();\n    void defer(const Callback &cb, void *data = nullptr);\n    void set_end_callback(EndCallback _id, const std::function<void(Reactor *)> &fn);\n    void erase_end_callback(EndCallback _id);\n    void set_exit_condition(ExitCondition _id, const std::function<bool(Reactor *, size_t &)> &fn);\n    void set_handler(int fd_type, int event, ReactorHandler handler);\n    bool isset_handler(int fd_type, int event) const;\n    void add_destroy_callback(const Callback &cb, void *data = nullptr);\n    void execute_begin_callback() const;\n    void execute_end_callbacks(bool _timed_out = false);\n    void drain_write_buffer(network::Socket *socket);\n\n    bool ready() const {\n        return ready_;\n    }\n\n    bool is_running() const {\n        return running;\n    }\n\n    size_t remove_exit_condition(const ExitCondition _id) {\n        return exit_conditions.erase(_id);\n    }\n\n    bool isset_exit_condition(const ExitCondition _id) {\n        return exit_conditions.find(_id) != exit_conditions.end();\n    }\n\n    int add_event(network::Socket *_socket, EventType event_type) const {\n        if (!(_socket->events & event_type)) {\n            return set(_socket, _socket->events | event_type);\n        }\n        return SW_OK;\n    }\n\n    int del_event(network::Socket *_socket, EventType event_type) const {\n        if (_socket->events & event_type) {\n            return set(_socket, _socket->events & (~event_type));\n        }\n        return SW_OK;\n    }\n\n    int remove_read_event(network::Socket *_socket) const {\n        if (_socket->events & SW_EVENT_WRITE) {\n            _socket->events &= (~SW_EVENT_READ);\n            return set(_socket, _socket->events);\n        } else {\n            return del(_socket);\n        }\n    }\n\n    int remove_write_event(network::Socket *_socket) const {\n        if (_socket->events & SW_EVENT_READ) {\n            _socket->events &= (~SW_EVENT_WRITE);\n            return set(_socket, _socket->events);\n        } else {\n            return del(_socket);\n        }\n    }\n\n    int add_read_event(network::Socket *_socket) const {\n        if (_socket->events & SW_EVENT_WRITE) {\n            _socket->events |= SW_EVENT_READ;\n            return set(_socket, _socket->events);\n        } else {\n            return add(_socket, SW_EVENT_READ);\n        }\n    }\n\n    int add_write_event(network::Socket *_socket) const {\n        if (_socket->events & SW_EVENT_READ) {\n            _socket->events |= SW_EVENT_WRITE;\n            return set(_socket, _socket->events);\n        } else {\n            return add(_socket, SW_EVENT_WRITE);\n        }\n    }\n\n    bool exists(const network::Socket *_socket) const {\n        return !_socket->removed && _socket->events;\n    }\n\n    bool exists(const int fd) const {\n        return sockets_.find(fd) != sockets_.end();\n    }\n\n    int get_timeout_msec() const {\n        return defer_tasks == nullptr ? timeout_msec : 0;\n    }\n\n    void set_timeout_msec(int mesc) {\n        timeout_msec = mesc;\n    }\n\n    size_t get_event_num() const {\n        return sockets_.size();\n    }\n\n    const std::unordered_map<int, network::Socket *> &get_sockets() {\n        return sockets_;\n    }\n\n    network::Socket *get_socket(const int fd) {\n        return sockets_[fd];\n    }\n\n    void foreach_socket(const std::function<void(int, network::Socket *)> &callback) const {\n        for (auto &kv : sockets_) {\n            callback(kv.first, kv.second);\n        }\n    }\n\n    ReactorHandler get_handler(const FdType fd_type, const EventType event) const {\n        switch (event) {\n        case SW_EVENT_READ:\n            return read_handler[fd_type];\n        case SW_EVENT_WRITE:\n            return write_handler[fd_type] ? write_handler[fd_type] : default_write_handler;\n        case SW_EVENT_ERROR:\n            return error_handler[fd_type] ? error_handler[fd_type] : default_error_handler;\n        default:\n            abort();\n        }\n    }\n\n    ReactorHandler get_error_handler(const FdType fd_type) const {\n        ReactorHandler handler = get_handler(fd_type, SW_EVENT_ERROR);\n        // error callback is not set, try to use readable or writable callback\n        if (handler == nullptr) {\n            handler = get_handler(fd_type, SW_EVENT_READ);\n            if (handler == nullptr) {\n                handler = get_handler(fd_type, SW_EVENT_WRITE);\n            }\n        }\n        return handler;\n    }\n\n    void before_wait() {\n        start = running = true;\n        if (timeout_msec == 0) {\n            timeout_msec = -1;\n        }\n    }\n\n    int trigger_close_event(Event *event) {\n        return default_error_handler(this, event);\n    }\n\n    void set_wait_exit(const bool enable) {\n        wait_exit = enable;\n    }\n\n    void _add(network::Socket *_socket, const int events) {\n        _socket->events = events;\n        _socket->removed = 0;\n        sockets_[_socket->fd] = _socket;\n    }\n\n    void _set(network::Socket *_socket, const int events) {\n        _socket->events = events;\n    }\n\n    bool _exists(const network::Socket *_socket) {\n        return sockets_.find(_socket->fd) != sockets_.end();\n    }\n\n    void _del(network::Socket *_socket) {\n        _socket->events = 0;\n        _socket->removed = 1;\n        sockets_.erase(_socket->fd);\n    }\n\n    bool catch_error() const {\n        switch (errno) {\n        case EINTR:\n            return true;\n        default:\n            break;\n        }\n        return false;\n    }\n\n    static ssize_t _write(Reactor *reactor, network::Socket *socket, const void *buf, size_t n);\n    static ssize_t _writev(Reactor *reactor, network::Socket *socket, const iovec *iov, size_t iovcnt);\n    static int _close(Reactor *reactor, network::Socket *socket);\n    static int _writable_callback(Reactor *reactor, Event *ev);\n    static ssize_t write_func(const Reactor *reactor,\n                              network::Socket *socket,\n                              size_t _len,\n                              const std::function<ssize_t()> &send_fn,\n                              const std::function<void(Buffer *buffer)> &append_fn);\n\n    static bool isset_read_event(const int events) {\n        return (events < SW_EVENT_DEAULT) || (events & SW_EVENT_READ);\n    }\n\n    static bool isset_write_event(const int events) {\n        return events & SW_EVENT_WRITE;\n    }\n\n    static bool isset_error_event(const int events) {\n        return events & SW_EVENT_ERROR;\n    }\n};\n\nint16_t translate_events_to_poll(int events);\nint translate_events_from_poll(int16_t events);\n}  // namespace swoole\n\n#define SW_REACTOR_CONTINUE                                                                                            \\\n    if (reactor_->once) {                                                                                              \\\n        break;                                                                                                         \\\n    } else {                                                                                                           \\\n        continue;                                                                                                      \\\n    }\n"
  },
  {
    "path": "include/swoole_redis.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_server.h\"\n\n#include <vector>\n#include <string>\n\n#define SW_REDIS_RETURN_NIL \"$-1\\r\\n\"\n\n#define SW_REDIS_MAX_COMMAND_SIZE 64\n#define SW_REDIS_MAX_LINES 128\n#define SW_REDIS_MAX_STRING_SIZE 536870912  // 512M\n\nnamespace swoole {\nnamespace redis {\n\nenum State {\n    STATE_RECEIVE_TOTAL_LINE,\n    STATE_RECEIVE_LENGTH,\n    STATE_RECEIVE_STRING,\n};\n\nenum ReplyType {\n    REPLY_ERROR,\n    REPLY_NIL,\n    REPLY_STATUS,\n    REPLY_INT,\n    REPLY_STRING,\n    REPLY_SET,\n    REPLY_MAP,\n};\n\nconst char *get_number(const char *p, int *_ret);\nint recv_packet(Protocol *protocol, Connection *conn, String *buffer);\nstd::vector<std::string> parse(const char *data, size_t len);\nvoid format_nil(String *buf);\nvoid format(String *buf, ReplyType type, const std::string &value);\nvoid format(String *buf, ReplyType type, long value);\n\n}  // namespace redis\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_server.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_string.h\"\n#include \"swoole_socket.h\"\n#include \"swoole_timer.h\"\n#include \"swoole_reactor.h\"\n#include \"swoole_signal.h\"\n#include \"swoole_protocol.h\"\n#include \"swoole_process_pool.h\"\n#include \"swoole_pipe.h\"\n#include \"swoole_channel.h\"\n#include \"swoole_message_bus.h\"\n\n#ifdef SW_SUPPORT_DTLS\n#include \"swoole_dtls.h\"\n#endif\n\n#ifdef __MACH__\n#include <sys/syslimits.h>\n#endif\n\n#include <string>\n#include <queue>\n#include <thread>\n#include <mutex>\n#include <atomic>\n#include <unordered_map>\n#include <unordered_set>\n#include <condition_variable>\n\n//------------------------------------Server-------------------------------------------\nnamespace swoole {\n\nnamespace http_server {\nstruct Request;\nstruct RewriteRule;\nclass StaticHandler;\n}  // namespace http_server\n\nclass Server;\nstruct Manager;\nclass Thread;\n\ntypedef std::function<void(void)> WorkerFn;\n\nstruct Session {\n    SessionId id;\n    int fd;\n    uint32_t reactor_id : 8;\n    uint32_t reserve_ : 24;\n};\n\nstruct Connection {\n    /**\n     * It must be in the header. When set to 0, it means that connection does not exist.\n     * One-write and multiple-read operation is thread-safe\n     * system fd must be 0. en: signalfd, listen socket\n     */\n    uint8_t active;\n    SocketType socket_type;\n    int fd;\n    int worker_id;\n    SessionId session_id;\n    //--------------------------------------------------------------\n    uint8_t ssl;\n    uint8_t ssl_ready;\n    uint8_t overflow;\n    uint8_t high_watermark;\n    uint8_t http_upgrade;\n    uint8_t http2_stream;\n    uint8_t websocket_compression;\n    // If it is equal to 1, it means server actively closed the connection\n    uint8_t close_actively;\n    uint8_t closed;\n    uint8_t close_queued;\n    uint8_t closing;\n    uint8_t close_reset;\n    uint8_t peer_closed;\n    // protected connection, do not close connection when receiving/sending timeout\n    uint8_t protect;\n    uint8_t close_notify;\n    uint8_t close_force;\n    ReactorId reactor_id;\n    uint16_t close_errno;\n    int server_fd;\n    sw_atomic_t recv_queued_bytes;\n    uint32_t send_queued_bytes;\n    uint16_t waiting_time;\n    uint16_t local_port;\n    uint16_t local_addr_index;\n    TimerNode *timer;\n    /**\n     * socket address\n     */\n    network::Address info;\n    /**\n     * link anything, for kernel, do not use with application.\n     */\n    void *object;\n    /**\n     * socket, only operated in the main process\n     */\n    network::Socket *socket;\n    /**\n     * connect/recv/send/close time\n     */\n    double connect_time;\n    double last_recv_time;\n    double last_send_time;\n    double last_dispatch_time;\n    /**\n     * bind uid\n     */\n    uint32_t uid;\n    /**\n     * upgrade websocket\n     */\n    uint8_t websocket_status;\n    /**\n     * unfinished data frame\n     */\n    String *websocket_buffer;\n\n    String *ssl_client_cert;\n    pid_t ssl_client_cert_pid;\n    sw_atomic_t lock;\n};\n\n//------------------------------------ReactorThread-------------------------------------------\nstruct ReactorThread {\n    int id;\n    std::thread thread;\n    network::Socket *notify_pipe = nullptr;\n    uint64_t dispatch_count = 0;\n    network::Socket *pipe_command = nullptr;\n    TimerNode *heartbeat_timer = nullptr;\n    MessageBus message_bus;\n\n    int init(Server *serv, Reactor *reactor, uint16_t reactor_id);\n    void shutdown(Reactor *reactor);\n    int close_connection(Reactor *reactor, SessionId session_id);\n    void clean();\n};\n\nstruct ServerPortGS {\n    sw_atomic_t connection_num;\n    sw_atomic_t *connection_nums = nullptr;\n    sw_atomic_long_t abort_count;\n    sw_atomic_long_t accept_count;\n    sw_atomic_long_t close_count;\n    sw_atomic_long_t dispatch_count;\n    sw_atomic_long_t request_count;\n    sw_atomic_long_t response_count;\n    sw_atomic_long_t total_recv_bytes;\n    sw_atomic_long_t total_send_bytes;\n};\n\nstruct ListenPort {\n    uint16_t object_id;\n    /**\n     * tcp socket listen backlog\n     */\n    uint16_t backlog = SW_BACKLOG;\n    bool listening = false;\n    /**\n     * open tcp_defer_accept option\n     */\n    int tcp_defer_accept = 0;\n    /**\n     * TCP_FASTOPEN\n     */\n    int tcp_fastopen = 0;\n    /**\n     * TCP KeepAlive\n     */\n    int tcp_keepidle = SW_TCP_KEEPIDLE;\n    int tcp_keepinterval = SW_TCP_KEEPINTERVAL;\n    int tcp_keepcount = SW_TCP_KEEPCOUNT;\n\n    int tcp_user_timeout = 0;\n\n    double max_idle_time = 0;\n\n    int socket_buffer_size = network::Socket::default_buffer_size;\n    uint32_t buffer_high_watermark = 0;\n    uint32_t buffer_low_watermark = 0;\n\n    SocketType type = SW_SOCK_TCP;\n    uint8_t ssl = 0;\n    std::string host;\n    int port = 0;\n    network::Socket *socket = nullptr;\n    pthread_t thread_id = 0;\n\n    uint16_t heartbeat_idle_time = 0;\n\n    /**\n     * check data eof\n     */\n    bool open_eof_check = false;\n    /**\n     * built-in http protocol\n     */\n    bool open_http_protocol = false;\n    /**\n     * built-in http2.0 protocol\n     */\n    bool open_http2_protocol = false;\n    /**\n     * built-in websocket protocol\n     */\n    bool open_websocket_protocol = false;\n    /**\n     * Relevant settings of websocket server\n     */\n    WebSocketSettings websocket_settings = {};\n    /**\n     *  one package: length check\n     */\n    bool open_length_check = false;\n    /**\n     * for mqtt protocol\n     */\n    bool open_mqtt_protocol = false;\n    /**\n     *  redis protocol\n     */\n    bool open_redis_protocol = false;\n    /**\n     * open tcp nodelay option\n     */\n    bool open_tcp_nodelay = false;\n    /**\n     * open tcp nopush option(for sendfile)\n     */\n    bool open_tcp_nopush = true;\n    /**\n     * open tcp keepalive\n     */\n    bool open_tcp_keepalive = false;\n    /**\n     * set socket option\n     */\n    int kernel_socket_recv_buffer_size = 0;\n    int kernel_socket_send_buffer_size = 0;\n\n    std::shared_ptr<SSLContext> ssl_context = nullptr;\n    std::unordered_map<std::string, std::shared_ptr<SSLContext>> sni_contexts;\n\n#ifdef SW_SUPPORT_DTLS\n    std::unordered_map<int, dtls::Session *> *dtls_sessions = nullptr;\n    dtls::Session *create_dtls_session(network::Socket *sock) const;\n#endif\n\n    bool ssl_is_enable() const {\n        return get_ssl_context() != nullptr;\n    }\n\n    SSLContext *get_ssl_context() const {\n        return ssl_context.get();\n    }\n\n    std::shared_ptr<SSLContext> dup_ssl_context() const {\n        auto new_ctx = std::make_shared<SSLContext>();\n        *new_ctx = *ssl_context;\n        return new_ctx;\n    }\n\n    ServerPortGS *gs = nullptr;\n\n    Protocol protocol = {};\n    void *ptr = nullptr;\n\n    int (*onRead)(Reactor *reactor, ListenPort *port, Event *event) = nullptr;\n\n    bool is_dgram() const {\n        return network::Socket::is_dgram(type);\n    }\n\n    bool is_dtls() const {\n#ifdef SW_SUPPORT_DTLS\n        return ssl_context && (ssl_context->protocols & SW_SSL_DTLS);\n#else\n        return false;\n#endif\n    }\n\n    bool is_stream() const {\n        return network::Socket::is_stream(type);\n    }\n\n    void set_eof_protocol(const std::string &eof, bool find_from_right = false);\n    void set_length_protocol(uint32_t length_offset, char length_type, uint32_t body_offset);\n    void set_stream_protocol();\n\n    void set_package_max_length(uint32_t max_length) {\n        protocol.package_max_length = max_length;\n    }\n\n    explicit ListenPort(Server *server);\n    ~ListenPort() = default;\n    int listen();\n    void close();\n    bool import(int sock);\n    void init_protocol();\n    const char *get_protocols() const;\n    int create_socket();\n    void close_socket();\n    void destroy_http_request(Connection *conn);\n\n    static int readable_callback_raw(Reactor *reactor, ListenPort *lp, Event *event);\n    static int readable_callback_length(Reactor *reactor, ListenPort *lp, Event *event);\n    static int readable_callback_eof(Reactor *reactor, ListenPort *lp, Event *event);\n    static int readable_callback_http(Reactor *reactor, ListenPort *lp, Event *event);\n    static int readable_callback_redis(Reactor *reactor, ListenPort *lp, Event *event);\n\n    bool ssl_context_init();\n    bool ssl_context_create(SSLContext *context) const;\n    bool ssl_create(network::Socket *sock);\n    bool ssl_add_sni_cert(const std::string &name, const std::shared_ptr<SSLContext> &ctx);\n    static bool ssl_matches_wildcard_name(const char *subject_name, const char *cert_name);\n    bool ssl_init() const;\n\n    bool set_ssl_key_file(const std::string &file) const {\n        return ssl_context->set_key_file(file);\n    }\n\n    bool set_ssl_cert_file(const std::string &file) const {\n        return ssl_context->set_cert_file(file);\n    }\n\n    void set_ssl_cafile(const std::string &file) const {\n        ssl_context->cafile = file;\n    }\n\n    bool set_ssl_client_cert_file(const std::string &file) const {\n        return ssl_context->set_client_cert_file(file);\n    }\n\n    void set_ssl_capath(const std::string &path) const {\n        ssl_context->capath = path;\n    }\n\n    void set_ssl_passphrase(const std::string &str) const {\n        ssl_context->passphrase = str;\n    }\n\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n    void set_tls_host_name(const std::string &str) const {\n        ssl_context->tls_host_name = str;\n        // if user set empty ssl_host_name, disable it, otherwise the underlying may set it automatically\n        ssl_context->disable_tls_host_name = ssl_context->tls_host_name.empty();\n    }\n#endif\n\n    void set_ssl_dhparam(const std::string &file) const {\n        ssl_context->dhparam = file;\n    }\n\n    void set_ssl_ecdh_curve(const std::string &str) const {\n        ssl_context->ecdh_curve = str;\n    }\n\n    void set_ssl_protocols(long protocols) const {\n        if (protocols & SW_SSL_DTLS) {\n#ifndef SW_SUPPORT_DTLS\n            protocols ^= SW_SSL_DTLS;\n#else\n            if (is_dgram()) {\n                protocols ^= SW_SSL_DTLS;\n            }\n#endif\n        }\n        ssl_context->protocols = protocols;\n    }\n\n    void set_ssl_disable_compress(bool value) const {\n        ssl_context->disable_compress = value;\n    }\n\n    void set_ssl_verify_peer(bool value) const {\n        ssl_context->verify_peer = value;\n    }\n\n    void set_ssl_allow_self_signed(bool value) const {\n        ssl_context->allow_self_signed = value;\n    }\n\n    void set_ssl_verify_depth(uint8_t value) const {\n        ssl_context->verify_depth = value;\n    }\n\n    void set_ssl_ciphers(const std::string &str) const {\n        ssl_context->ciphers = str;\n    }\n\n    void set_ssl_prefer_server_ciphers(bool value) const {\n        ssl_context->prefer_server_ciphers = value;\n    }\n\n#ifdef OPENSSL_IS_BORINGSSL\n    void set_ssl_grease(uint8_t value) {\n        ssl_context->grease = value;\n    }\n#endif\n\n    const std::string &get_ssl_cert_file() const {\n        return ssl_context->cert_file;\n    }\n\n    const std::string &get_ssl_key_file() const {\n        return ssl_context->key_file;\n    }\n\n    const std::string &get_ssl_client_cert_file() const {\n        return ssl_context->client_cert_file;\n    }\n\n    const std::string &get_ssl_capath() const {\n        return ssl_context->capath;\n    }\n\n    const std::string &get_ssl_cafile() const {\n        return ssl_context->cafile;\n    }\n\n    bool get_ssl_verify_peer() const {\n        return ssl_context->verify_peer;\n    }\n\n    bool get_ssl_allow_self_signed() const {\n        return ssl_context->allow_self_signed;\n    }\n\n    uint32_t get_ssl_protocols() const {\n        return ssl_context->protocols;\n    }\n\n    bool has_sni_contexts() const {\n        return !sni_contexts.empty();\n    }\n\n    static int ssl_server_sni_callback(SSL *ssl, int *al, void *arg);\n    void clear_protocol();\n\n    network::Socket *get_socket() const {\n        return socket;\n    }\n\n    int get_port() const {\n        return port;\n    }\n\n    const char *get_host() const {\n        return host.c_str();\n    }\n\n    SocketType get_type() const {\n        return type;\n    }\n\n    int get_fd() const {\n        return socket ? socket->fd : -1;\n    }\n\n    size_t get_connection_num() const;\n};\n\nstruct ServerGS {\n    pid_t master_pid;\n    pid_t manager_pid;\n\n    SessionId session_round;\n    sw_atomic_t start;\n    sw_atomic_t shutdown;\n\n    int max_fd;\n    int min_fd;\n\n    bool onstart_called;\n    time_t start_time;\n    sw_atomic_t connection_num;\n    sw_atomic_t *connection_nums = nullptr;\n    sw_atomic_t tasking_num;\n    uint32_t max_concurrency;\n    sw_atomic_t concurrency;\n    sw_atomic_long_t abort_count;\n    sw_atomic_long_t accept_count;\n    sw_atomic_long_t close_count;\n    sw_atomic_long_t dispatch_count;\n    sw_atomic_long_t request_count;\n    sw_atomic_long_t response_count;\n    sw_atomic_long_t total_recv_bytes;\n    sw_atomic_long_t total_send_bytes;\n    sw_atomic_long_t pipe_packet_msg_id;\n    sw_atomic_long_t task_count;\n\n    sw_atomic_t spinlock;\n\n    Barrier manager_barrier;\n\n    ProcessPool task_workers;\n    ProcessPool event_workers;\n};\n\nclass Factory {\n  protected:\n    Server *server_;\n\n  public:\n    explicit Factory(Server *_server) {\n        server_ = _server;\n    }\n    pid_t spawn_event_worker(Worker *worker) const;\n    pid_t spawn_user_worker(Worker *worker) const;\n    pid_t spawn_task_worker(Worker *worker) const;\n    void kill_user_workers() const;\n    void kill_event_workers() const;\n    void kill_task_workers() const;\n    void check_worker_exit_status(Worker *worker, const ExitStatus &exit_status) const;\n    virtual ~Factory() = default;\n    virtual bool start() = 0;\n    virtual bool shutdown() = 0;\n    virtual bool dispatch(SendData *) = 0;\n    virtual bool finish(SendData *) = 0;\n    virtual bool notify(DataHead *) = 0;\n    virtual bool end(SessionId session_id, int flags) = 0;\n};\n\nclass BaseFactory : public Factory {\n  public:\n    explicit BaseFactory(Server *server);\n    ~BaseFactory() override;\n    bool start() override;\n    bool shutdown() override;\n    bool dispatch(SendData *) override;\n    bool finish(SendData *) override;\n    bool notify(DataHead *) override;\n    bool end(SessionId session_id, int flags) override;\n    bool forward_message(const Session *session, SendData *data) const;\n};\n\nclass ProcessFactory : public Factory {\n  public:\n    explicit ProcessFactory(Server *server);\n    ~ProcessFactory() override;\n    bool start() override;\n    bool shutdown() override;\n    bool dispatch(SendData *) override;\n    bool finish(SendData *) override;\n    bool notify(DataHead *) override;\n    bool end(SessionId session_id, int flags) override;\n};\n\nstruct ThreadReloadTask {\n    Server *server_;\n    uint16_t worker_num;\n    uint16_t reloaded_num;\n\n    bool is_completed() const {\n        return reloaded_num == worker_num;\n    }\n\n    ThreadReloadTask(Server *_server, bool _reload_all_workers);\n    ~ThreadReloadTask() = default;\n};\n\nclass ThreadFactory : public BaseFactory {\n    std::vector<std::shared_ptr<Thread>> threads_;\n    std::mutex lock_;\n    std::condition_variable cv_;\n    std::queue<Worker *> queue_;\n    bool reload_all_workers = false;\n    sw_atomic_t reloading = 0;\n    std::shared_ptr<ThreadReloadTask> reload_task;\n    void at_thread_enter(WorkerId id, int process_type);\n    void at_thread_exit(Worker *worker);\n    void create_message_bus() const;\n    void destroy_message_bus();\n    void do_reload();\n    void push_to_wait_queue(Worker *worker);\n\n  public:\n    explicit ThreadFactory(Server *server);\n    ~ThreadFactory() override;\n    WorkerId get_manager_thread_id() const;\n    WorkerId get_master_thread_id() const;\n    void spawn_event_worker(WorkerId i);\n    void spawn_task_worker(WorkerId i);\n    void spawn_user_worker(WorkerId i);\n    void spawn_manager_thread(WorkerId i);\n    void terminate_manager_thread();\n    void wait();\n    bool reload(bool reload_all_workers);\n    bool start() override;\n    bool shutdown() override;\n};\n\nenum ServerEventType {\n    // recv data payload\n    SW_SERVER_EVENT_RECV_DATA,\n    SW_SERVER_EVENT_RECV_DGRAM,\n    // send data\n    SW_SERVER_EVENT_SEND_DATA,\n    SW_SERVER_EVENT_SEND_FILE,\n    // connection event\n    SW_SERVER_EVENT_CLOSE,\n    SW_SERVER_EVENT_CONNECT,\n    SW_SERVER_EVENT_CLOSE_FORCE,\n    SW_SERVER_EVENT_CLOSE_FORWARD,\n    // task\n    SW_SERVER_EVENT_TASK,\n    SW_SERVER_EVENT_FINISH,\n    // pipe\n    SW_SERVER_EVENT_PIPE_MESSAGE,\n    // event operate\n    SW_SERVER_EVENT_PAUSE_RECV,\n    SW_SERVER_EVENT_RESUME_RECV,\n    // buffer event\n    SW_SERVER_EVENT_BUFFER_FULL,\n    SW_SERVER_EVENT_BUFFER_EMPTY,\n    // process message\n    SW_SERVER_EVENT_INCOMING,\n    SW_SERVER_EVENT_SHUTDOWN,\n    SW_SERVER_EVENT_COMMAND_REQUEST,\n    SW_SERVER_EVENT_COMMAND_RESPONSE,\n    SW_SERVER_EVENT_SHUTDOWN_SIGNAL,\n};\n\nclass Server {\n  public:\n    typedef int (*DispatchFunction)(Server *, Connection *, SendData *);\n\n    struct Command {\n        typedef std::function<void(Server *, const std::string &msg)> Callback;\n        typedef std::function<std::string(Server *, const std::string &msg)> Handler;\n        enum ProcessType {\n            MASTER = 1u << 1,\n            REACTOR_THREAD = 1u << 2,\n            EVENT_WORKER = 1u << 3,\n            TASK_WORKER = 1u << 4,\n            MANAGER = 1u << 5,\n            ALL_PROCESS = MASTER | REACTOR_THREAD | EVENT_WORKER | TASK_WORKER | MANAGER,\n        };\n        int id;\n        int accepted_process_types;\n        std::string name;\n    };\n\n    struct MultiTask {\n        uint16_t count;\n        std::unordered_map<TaskId, uint16_t> map;\n\n        std::function<TaskId(uint16_t index, EventData *buf)> pack;\n        std::function<void(uint16_t index, EventData *result)> unpack;\n        std::function<void(uint16_t index)> fail;\n\n        explicit MultiTask(uint16_t n) {\n            count = n;\n        }\n\n        int find(TaskId task_id);\n    };\n\n    enum Mode {\n        MODE_BASE = 1,\n        MODE_PROCESS = 2,\n        MODE_THREAD = 3,\n    };\n\n    enum TaskIpcMode {\n        TASK_IPC_UNIXSOCK = 1,\n        TASK_IPC_MSGQUEUE = 2,\n        TASK_IPC_PREEMPTIVE = 3,\n        TASK_IPC_STREAM = 4,\n    };\n\n    enum ThreadType {\n        THREAD_NORMAL = 0,\n        THREAD_MASTER = 1,\n        THREAD_REACTOR = 2,\n        THREAD_HEARTBEAT = 3,\n        THREAD_WORKER = 4,\n    };\n\n    enum DispatchMode {\n        DISPATCH_ROUND = 1,\n        DISPATCH_FDMOD = 2,\n        DISPATCH_IDLE_WORKER = 3,\n        DISPATCH_IPMOD = 4,\n        DISPATCH_UIDMOD = 5,\n        DISPATCH_USERFUNC = 6,\n        DISPATCH_CO_CONN_LB = 8,\n        DISPATCH_CO_REQ_LB = 9,\n        DISPATCH_CONCURRENT_LB = 10,\n    };\n\n    enum FactoryDispatchResult {\n        DISPATCH_RESULT_DISCARD_PACKET = -1,\n        DISPATCH_RESULT_CLOSE_CONNECTION = -2,\n        DISPATCH_RESULT_USERFUNC_FALLBACK = -3,\n    };\n\n    // deprecated, will be removed in the next minor version\n    enum HookType {\n        HOOK_MASTER_START,\n        HOOK_MASTER_TIMER,\n        HOOK_REACTOR_START,\n        HOOK_WORKER_START,\n        HOOK_TASK_WORKER_START,\n        HOOK_MASTER_CONNECT,\n        HOOK_REACTOR_CONNECT,\n        HOOK_WORKER_CONNECT,\n        HOOK_REACTOR_RECEIVE,\n        HOOK_WORKER_RECEIVE,\n        HOOK_REACTOR_CLOSE,\n        HOOK_WORKER_CLOSE,\n        HOOK_MANAGER_START,\n        HOOK_MANAGER_TIMER,\n        HOOK_PROCESS_TIMER,\n        HOOK_END = SW_MAX_HOOK_TYPE - 1,\n    };\n\n    enum CloseFlag {\n        CLOSE_RESET = 1u << 1,\n        CLOSE_ACTIVELY = 1u << 2,\n    };\n\n    /**\n     * reactor thread/process num\n     */\n    uint16_t reactor_num = 0;\n    /**\n     * worker process num\n     */\n    uint32_t worker_num = 0;\n\n    uint8_t dgram_port_num = 0;\n\n    /**\n     * package dispatch mode\n     */\n    uint8_t dispatch_mode = DISPATCH_FDMOD;\n\n    /**\n     * No idle work process is available.\n     */\n    bool scheduler_warning = false;\n\n    int worker_uid = 0;\n    int worker_groupid = 0;\n\n    /**\n     * worker process max request\n     */\n    uint32_t max_request = 0;\n    uint32_t max_request_grace = 0;\n\n    network::Socket *udp_socket_ipv4 = nullptr;\n    network::Socket *udp_socket_ipv6 = nullptr;\n    network::Socket *dgram_socket = nullptr;\n\n    uint32_t max_wait_time = SW_WORKER_MAX_WAIT_TIME;\n    uint32_t worker_max_concurrency = UINT_MAX;\n\n    /*----------------------------Reactor schedule--------------------------------*/\n    sw_atomic_t worker_round_id = 0;\n\n    /**\n     * worker(worker and task_worker) process chroot / user / group\n     */\n    std::string chroot_;\n    std::string user_;\n    std::string group_;\n\n    /**\n     * run as a daemon process\n     */\n    bool daemonize = false;\n    /**\n     * have dgram socket\n     */\n    bool have_dgram_sock = false;\n    /**\n     * have stream socket\n     */\n    bool have_stream_sock = false;\n    /**\n     * open cpu affinity setting\n     */\n    bool open_cpu_affinity = false;\n    /**\n     * disable notice when use SW_DISPATCH_ROUND and SW_DISPATCH_QUEUE\n     */\n    bool disable_notify = false;\n    /**\n     * discard the timeout request\n     */\n    bool discard_timeout_request = false;\n    /**\n     * parse cookie header\n     */\n    bool http_parse_cookie = true;\n    /**\n     * parse x-www-form-urlencoded data\n     */\n    bool http_parse_post = true;\n    /**\n     * parse multipart/form-data files to match $_FILES\n     */\n    bool http_parse_files = false;\n    /**\n     * http content compression\n     */\n    bool http_compression = false;\n    /**\n     * RFC-7692\n     */\n    bool websocket_compression = false;\n    /**\n     * handle static files\n     */\n    bool enable_static_handler = false;\n    /**\n     * show file list in the current directory\n     */\n    bool http_autoindex = false;\n    /**\n     * enable onConnect/onClose event when use dispatch_mode=1/3\n     */\n    bool enable_unsafe_event = false;\n    /**\n     * waiting for worker onConnect callback function to return\n     */\n    bool enable_delay_receive = false;\n    /**\n     * reuse port\n     */\n    bool enable_reuse_port = false;\n    /**\n     * asynchronous reloading\n     */\n    bool reload_async = true;\n    /**\n     * use event object\n     */\n    bool event_object = false;\n    /**\n     * use task object\n     */\n    bool task_object = false;\n    /**\n     * enable coroutine in task worker\n     */\n    bool task_enable_coroutine = false;\n    /**\n     * yield coroutine when the output buffer is full\n     */\n    bool send_yield = true;\n    /**\n     * enable coroutine\n     */\n    bool enable_coroutine = true;\n    /**\n     * disable multi-threads\n     */\n    bool single_thread = false;\n    /**\n     * server status\n     */\n    bool running = true;\n\n    int *cpu_affinity_available = nullptr;\n    int cpu_affinity_available_num = 0;\n\n    UnixSocket *pipe_command = nullptr;\n    MessageBus message_bus;\n\n    double send_timeout = 0;\n\n    uint16_t heartbeat_check_interval = 0;\n\n    time_t reload_time = 0;\n    time_t warning_time = 0;\n    long timezone_ = 0;\n    TimerNode *master_timer = nullptr;\n    TimerNode *heartbeat_timer = nullptr;\n\n    /* buffer output/input setting*/\n    uint32_t output_buffer_size = UINT_MAX;\n    uint32_t input_buffer_size = SW_INPUT_BUFFER_SIZE;\n    uint32_t max_queued_bytes = 0;\n\n    /**\n     * the master process and worker process communicate using unix socket dgram.\n     * ipc_max_size represents the maximum size of each datagram,\n     * which is obtained from the kernel send buffer of unix socket in swServer_set_ipc_max_size function.\n     */\n    uint32_t ipc_max_size = SW_IPC_MAX_SIZE;\n\n    void *private_data_1 = nullptr;\n    void *private_data_2 = nullptr;\n    void *private_data_3 = nullptr;\n    void *private_data_4 = nullptr;\n\n    Factory *factory_ = nullptr;\n    Manager *manager_ = nullptr;\n\n    std::vector<ListenPort *> ports;\n    std::vector<std::shared_ptr<UnixSocket>> worker_pipes;\n\n    ListenPort *get_primary_port() const {\n        return ports.front();\n    }\n\n    Mode get_mode() const {\n        return mode_;\n    };\n\n    /**\n     * This method can only be used for INET ports and cannot obtain Unix socket ports.\n     */\n    ListenPort *get_port(int _port) const {\n        for (auto port : ports) {\n            if (port->port == _port || _port == 0) {\n                return port;\n            }\n        }\n        return nullptr;\n    }\n\n    ListenPort *get_port(SocketType type, const char *host, int _port) const {\n        for (auto port : ports) {\n            if (port->port == _port && port->type == type && strcmp(host, port->host.c_str()) == 0) {\n                return port;\n            }\n        }\n        return nullptr;\n    }\n\n    ListenPort *get_port_by_server_fd(const int server_fd) const {\n        return static_cast<ListenPort *>(connection_list[server_fd].object);\n    }\n\n    ListenPort *get_port_by_fd(int fd) const {\n        return get_port_by_server_fd(connection_list[fd].server_fd);\n    }\n\n    ListenPort *get_port_by_session_id(SessionId session_id) const {\n        const Connection *conn = get_connection_by_session_id(session_id);\n        if (!conn) {\n            return nullptr;\n        }\n        return get_port_by_fd(conn->fd);\n    }\n\n    uint32_t get_package_max_length(Connection *conn) {\n        return get_port_by_fd(conn->fd)->protocol.package_max_length;\n    }\n\n    network::Socket *get_server_socket(int fd) const {\n        return connection_list[fd].socket;\n    }\n\n    network::Socket *get_command_reply_socket() const {\n        return is_base_mode() ? get_worker(0)->pipe_master : pipe_command->get_socket(false);\n    }\n\n    network::Socket *get_worker_pipe_master(WorkerId id) const {\n        return get_worker(id)->pipe_master;\n    }\n\n    network::Socket *get_worker_pipe_worker(WorkerId id) const {\n        return get_worker(id)->pipe_worker;\n    }\n\n    network::Socket *get_pipe_socket_in_message_bus(network::Socket *_socket) {\n        if (is_thread_mode()) {\n            return get_worker_message_bus()->get_pipe_socket(_socket);\n        } else {\n            return _socket;\n        }\n    }\n\n    network::Socket *get_worker_pipe_master_in_message_bus(const Worker *worker) {\n        return get_pipe_socket_in_message_bus(worker->pipe_master);\n    }\n\n    network::Socket *get_worker_pipe_worker_in_message_bus(const Worker *worker) {\n        return get_pipe_socket_in_message_bus(worker->pipe_worker);\n    }\n\n    network::Socket *get_worker_pipe_master_in_message_bus(WorkerId id) {\n        return get_worker_pipe_master_in_message_bus(get_worker(id));\n    }\n\n    network::Socket *get_worker_pipe_worker_in_message_bus(WorkerId id) {\n        return get_worker_pipe_worker_in_message_bus(get_worker(id));\n    }\n\n    /**\n     * [Worker|Master]\n     */\n    network::Socket *get_reactor_pipe_socket(const SessionId session_id, int reactor_id) const {\n        const int pipe_index = session_id % reactor_pipe_num;\n        /**\n         * pipe_worker_id: The pipe in which worker.\n         */\n        int pipe_worker_id = reactor_id + (pipe_index * reactor_num);\n        Worker *worker = get_worker(pipe_worker_id);\n        return worker->pipe_worker;\n    }\n\n    /**\n     *  task process\n     */\n    uint32_t task_worker_num = 0;\n    uint8_t task_ipc_mode = TASK_IPC_UNIXSOCK;\n    uint32_t task_max_request = 0;\n    uint32_t task_max_request_grace = 0;\n    std::vector<std::shared_ptr<Pipe>> task_notify_pipes;\n    EventData *task_results = nullptr;\n\n    /**\n     * Used for process management, saving the mapping relationship between PID and worker pointers\n     */\n    std::unordered_map<pid_t, Worker *> user_worker_map;\n    /**\n     * Shared memory, sharing state between processes\n     */\n    Worker *user_workers = nullptr;\n\n    std::unordered_map<std::string, Command> commands;\n    std::unordered_map<int, Command::Handler> command_handlers;\n    std::unordered_map<int64_t, Command::Callback> command_callbacks;\n    int command_current_id = 1;\n    int64_t command_current_request_id = 1;\n\n    Worker *workers = nullptr;\n    ServerGS *gs = nullptr;\n\n    std::shared_ptr<std::unordered_set<std::string>> locations = nullptr;\n    std::shared_ptr<std::vector<std::string>> http_index_files = nullptr;\n    std::shared_ptr<std::unordered_set<std::string>> http_compression_types = nullptr;\n\n    Barrier reactor_thread_barrier = {};\n\n    /**\n     * temporary directory for HTTP uploaded file.\n     */\n    std::string upload_tmp_dir = \"/tmp\";\n    /**\n     * Write the uploaded file in form-data to disk file\n     */\n    size_t upload_max_filesize = 0;\n    /**\n     * http compression level for gzip/br\n     */\n    uint8_t http_compression_level = 0;\n    uint32_t compression_min_length;\n    /**\n     * master process pid\n     */\n    std::string pid_file;\n\n    std::queue<String *> *buffer_pool = nullptr;\n\n    const Allocator *recv_buffer_allocator = &SwooleG.std_allocator;\n    size_t recv_buffer_size = SW_BUFFER_SIZE_BIG;\n\n    int manager_alarm = 0;\n\n    /**\n     * message queue key\n     */\n    uint64_t message_queue_key = 0;\n\n    void *hooks[SW_MAX_HOOK_TYPE] = {};\n\n    /*----------------------------Event Callback--------------------------------*/\n    /**\n     * Master Process\n     */\n    std::function<void(Server *)> onStart;\n    std::function<void(Server *)> onBeforeShutdown;\n    std::function<void(Server *)> onShutdown;\n    /**\n     * Manager Process\n     */\n    std::function<void(Server *)> onManagerStart;\n    std::function<void(Server *)> onManagerStop;\n    std::function<void(Server *, Worker *, const ExitStatus &)> onWorkerError;\n    std::function<void(Server *)> onBeforeReload;\n    std::function<void(Server *)> onAfterReload;\n    /**\n     * Worker Process\n     */\n    std::function<void(Server *, EventData *)> onPipeMessage;\n    std::function<void(Server *, Worker *)> onWorkerStart;\n    std::function<void(Server *, Worker *)> onWorkerStop;\n    std::function<void(Server *, Worker *)> onWorkerExit;\n    std::function<void(Server *, Worker *)> onUserWorkerStart;\n    /**\n     * Connection\n     */\n    std::function<int(Server *, RecvData *)> onReceive;\n    std::function<int(Server *, RecvData *)> onPacket;\n    std::function<void(Server *, DataHead *)> onClose;\n    std::function<void(Server *, DataHead *)> onConnect;\n    std::function<void(Server *, DataHead *)> onBufferFull;\n    std::function<void(Server *, DataHead *)> onBufferEmpty;\n    /**\n     * Task Worker\n     */\n    std::function<int(Server *, EventData *)> onTask;\n    std::function<int(Server *, EventData *)> onFinish;\n    /**\n     * for MessageBus\n     */\n    std::function<uint64_t(void)> msg_id_generator;\n    /**\n     * Hook\n     */\n    int (*dispatch_func)(Server *, Connection *, SendData *) = nullptr;\n\n    explicit Server(Mode _mode = MODE_BASE);\n    ~Server();\n\n    bool set_document_root(const std::string &path);\n    void add_static_handler_location(const std::string &);\n    void add_static_handler_index_files(const std::string &);\n    bool select_static_handler(const http_server::Request *request, const Connection *conn);\n    bool apply_rewrite_rules(http_server::StaticHandler *handler);\n    void add_http_compression_type(const std::string &type);\n    void add_rewrite_rule(const std::string &pattern, const std::string &replacement);\n\n    int create();\n    bool create_worker_pipes();\n\n    int start();\n    void destroy();\n    bool reload(bool reload_all_workers) const;\n    bool shutdown();\n\n    int add_worker(Worker *worker);\n    ListenPort *add_port(SocketType type, const char *host, int port);\n    int add_systemd_socket();\n    void add_hook(enum HookType type, const Callback &func, int push_back);\n    bool add_command(const std::string &command, int accepted_process_types, const Command::Handler &func);\n    Connection *add_connection(const ListenPort *ls, network::Socket *_socket, int server_fd);\n    const char *get_local_addr(Connection *conn);\n    const char *get_remote_addr(Connection *conn);\n    void abort_connection(Reactor *reactor, const ListenPort *ls, network::Socket *_socket) const;\n    void abort_worker(Worker *worker) const;\n    void reset_worker_counter(Worker *worker) const;\n    int connection_incoming(Reactor *reactor, Connection *conn) const;\n\n    uint32_t get_idle_worker_num() const;\n    int get_idle_task_worker_num() const;\n    int get_tasking_num() const;\n\n    TaskId get_task_id(const EventData *task) const {\n        return get_task_worker_pool()->get_task_id(task);\n    }\n\n    static uint16_t get_command_id(const EventData *cmd) {\n        return cmd->info.server_fd;\n    }\n\n    EventData *get_task_result() const {\n        return &(task_results[swoole_get_worker_id()]);\n    }\n\n    WorkerId get_task_src_worker_id(const EventData *task) const {\n        return get_task_worker_pool()->get_task_src_worker_id(task);\n    }\n\n    int get_minfd() const {\n        return gs->min_fd;\n    }\n\n    int get_maxfd() const {\n        return gs->max_fd;\n    }\n\n    void set_maxfd(int maxfd) const {\n        gs->max_fd = maxfd;\n    }\n\n    void set_minfd(int minfd) const {\n        gs->min_fd = minfd;\n    }\n\n    pid_t get_master_pid() const {\n        return gs->master_pid;\n    }\n\n    pid_t get_manager_pid() const {\n        return gs->manager_pid;\n    }\n\n    pid_t get_worker_pid(WorkerId worker_id) const {\n        return get_worker(worker_id)->pid;\n    }\n\n    const std::string &get_document_root() {\n        return document_root;\n    }\n\n    String *get_recv_buffer(network::Socket *_socket) const {\n        String *buffer = _socket->recv_buffer;\n        if (buffer == nullptr) {\n            buffer = new String(SW_BUFFER_SIZE_BIG, recv_buffer_allocator);\n            _socket->recv_buffer = buffer;\n        }\n\n        return buffer;\n    }\n\n    MessageBus *get_worker_message_bus() {\n#ifdef SW_THREAD\n        return sw_likely(is_thread_mode()) ? SwooleTG.message_bus : &message_bus;\n#else\n        return &message_bus;\n#endif\n    }\n\n    uint32_t get_worker_buffer_num() const {\n        return is_base_mode() ? 1 : reactor_num + dgram_port_num;\n    }\n\n    ProcessPool *get_task_worker_pool() const {\n        return &gs->task_workers;\n    }\n\n    ProcessPool *get_event_worker_pool() const {\n        return &gs->event_workers;\n    }\n\n    bool is_support_unsafe_events() const {\n        if (is_hash_dispatch_mode()) {\n            return true;\n        } else {\n            return enable_unsafe_event;\n        }\n    }\n\n    bool is_process_mode() const {\n        return mode_ == MODE_PROCESS;\n    }\n\n    bool is_base_mode() const {\n        return mode_ == MODE_BASE;\n    }\n\n    bool is_thread_mode() const {\n        return mode_ == MODE_THREAD;\n    }\n\n    bool is_enable_coroutine() const {\n        if (is_task_worker()) {\n            return task_enable_coroutine;\n        } else if (is_manager()) {\n            return false;\n        } else {\n            return enable_coroutine;\n        }\n    }\n\n    bool is_master_thread() const {\n        return swoole_get_thread_type() == THREAD_MASTER;\n    }\n\n    bool is_hash_dispatch_mode() const {\n        return dispatch_mode == DISPATCH_FDMOD || dispatch_mode == DISPATCH_IPMOD ||\n               dispatch_mode == DISPATCH_CO_CONN_LB;\n    }\n\n    bool is_support_send_yield() const {\n        return is_hash_dispatch_mode();\n    }\n\n    bool if_require_packet_callback(const ListenPort *port, bool isset) const {\n        return (port->is_dgram() && !port->ssl && !isset);\n    }\n\n    bool if_require_receive_callback(const ListenPort *port, bool isset) const {\n        return (((port->is_dgram() && port->ssl) || port->is_stream()) && !isset);\n    }\n\n    bool if_forward_message(const Session *session) const {\n        return session->reactor_id != swoole_get_worker_id();\n    }\n\n    Worker *get_worker(uint16_t worker_id) const;\n    bool kill_worker(int worker_id);\n    void stop_async_worker(Worker *worker);\n\n    Pipe *get_pipe_object(int pipe_fd) const {\n        return static_cast<Pipe *>(connection_list[pipe_fd].object);\n    }\n\n    size_t get_all_worker_num() const {\n        return get_core_worker_num() + get_user_worker_num();\n    }\n\n    size_t get_user_worker_num() const {\n        return user_worker_list.size();\n    }\n\n    size_t get_core_worker_num() const {\n        return worker_num + task_worker_num;\n    }\n\n    ReactorThread *get_thread(int reactor_id) const {\n        return &reactor_threads[reactor_id];\n    }\n\n    bool is_started() const {\n        return gs->start;\n    }\n\n    bool is_created() const {\n        return factory_ != nullptr;\n    }\n\n    bool is_running() const {\n        return running;\n    }\n\n    bool is_master() const {\n        return swoole_get_worker_type() == SW_MASTER;\n    }\n\n    bool is_worker() const {\n        return swoole_get_worker_type() == SW_EVENT_WORKER;\n    }\n\n    bool is_event_worker() const {\n        return is_worker();\n    }\n\n    bool is_task_worker() const {\n        return swoole_get_worker_type() == SW_TASK_WORKER;\n    }\n\n    bool is_manager() const {\n        return swoole_get_worker_type() == SW_MANAGER;\n    }\n\n    bool is_user_worker() const {\n        return swoole_get_worker_type() == SW_USER_WORKER;\n    }\n\n    bool is_worker_thread() const {\n        return is_thread_mode() && swoole_get_thread_type() == THREAD_WORKER;\n    }\n\n    bool is_worker_process() const {\n        return !is_thread_mode() && (is_worker() || is_task_worker());\n    }\n\n    bool is_reactor_thread() {\n        return swoole_get_thread_type() == THREAD_REACTOR;\n    }\n\n    bool is_single_worker() const {\n        return (worker_num == 1 && task_worker_num == 0 && max_request == 0 && get_user_worker_num() == 0);\n    }\n\n    bool isset_hook(HookType type) const {\n        assert(type <= HOOK_END);\n        return hooks[type];\n    }\n\n    bool is_sync_process() const {\n        if (is_manager()) {\n            return true;\n        }\n        if (is_task_worker() && !task_enable_coroutine) {\n            return true;\n        }\n        return false;\n    }\n\n    bool is_shutdown() const {\n        return gs->shutdown;\n    }\n\n    // can only be used in the main process\n    static bool is_valid_connection(const Connection *conn) {\n        return (conn && conn->socket && conn->active && conn->socket->fd_type == SW_FD_SESSION);\n    }\n\n    bool is_healthy_connection(double now, const Connection *conn) const;\n\n    static bool is_dgram_event(uint8_t type) {\n        return type == SW_SERVER_EVENT_RECV_DGRAM;\n    }\n\n    static bool is_stream_event(uint8_t type) {\n        switch (type) {\n        case SW_SERVER_EVENT_RECV_DATA:\n        case SW_SERVER_EVENT_SEND_DATA:\n        case SW_SERVER_EVENT_SEND_FILE:\n        case SW_SERVER_EVENT_CONNECT:\n        case SW_SERVER_EVENT_CLOSE:\n        case SW_SERVER_EVENT_PAUSE_RECV:\n        case SW_SERVER_EVENT_RESUME_RECV:\n        case SW_SERVER_EVENT_BUFFER_FULL:\n        case SW_SERVER_EVENT_BUFFER_EMPTY:\n            return true;\n        default:\n            return false;\n        }\n    }\n\n    int get_connection_fd(SessionId session_id) const {\n        return session_list[session_id % SW_SESSION_LIST_SIZE].fd;\n    }\n\n    Connection *get_connection_verify_no_ssl(SessionId session_id) const {\n        Session *session = get_session(session_id);\n        int fd = session->fd;\n        Connection *conn = get_connection(fd);\n        if (!conn || conn->active == 0) {\n            return nullptr;\n        }\n        if (session->id != session_id || conn->session_id != session_id) {\n            return nullptr;\n        }\n        return conn;\n    }\n\n    Connection *get_connection_verify(SessionId session_id) const {\n        Connection *conn = get_connection_verify_no_ssl(session_id);\n        if (conn && conn->ssl && !conn->ssl_ready) {\n            return nullptr;\n        }\n        return conn;\n    }\n\n    Connection *get_connection(const int fd) const {\n        if (static_cast<uint32_t>(fd) > max_connection) {\n            return nullptr;\n        }\n        return &connection_list[fd];\n    }\n\n    Connection *get_connection_for_iterator(int fd) const {\n        Connection *conn = get_connection(fd);\n        if (conn && conn->active && !conn->closed) {\n            if (conn->ssl && !conn->ssl_ready) {\n                return nullptr;\n            }\n            return conn;\n        }\n        return nullptr;\n    }\n\n    Connection *get_connection_by_session_id(SessionId session_id) const {\n        return get_connection(get_connection_fd(session_id));\n    }\n\n    Session *get_session(SessionId session_id) const {\n        return &session_list[session_id % SW_SESSION_LIST_SIZE];\n    }\n\n    void clear_timer();\n    static void timer_callback(Timer *timer, TimerNode *tnode);\n\n    bool create_event_workers();\n    bool create_task_workers();\n    bool create_user_workers();\n    int start_manager_process();\n\n    void call_hook(enum HookType type, void *arg);\n    void call_worker_start_callback(Worker *worker);\n    void call_worker_stop_callback(Worker *worker);\n    void call_worker_error_callback(Worker *worker, const ExitStatus &status);\n    void call_command_handler(MessageBus &mb, uint16_t worker_id, network::Socket *sock);\n    std::string call_command_handler_in_master(int command_id, const std::string &msg);\n    void call_command_callback(int64_t request_id, const std::string &result);\n    void foreach_connection(const std::function<void(Connection *)> &callback) const;\n    static int accept_connection(Reactor *reactor, Event *event);\n#ifdef SW_SUPPORT_DTLS\n    dtls::Session *accept_dtls_connection(const ListenPort *ls, const network::Address *sa);\n#endif\n    static int accept_command_result(Reactor *reactor, Event *event);\n    static int close_connection(Reactor *reactor, network::Socket *_socket);\n    static int dispatch_task(const Protocol *proto, network::Socket *_socket, const RecvData *rdata);\n\n    int send_to_connection(const SendData *) const;\n    ssize_t send_to_worker_from_worker(const Worker *dst_worker, const void *buf, size_t len, int flags);\n    bool has_kernel_nobufs_error(SessionId session_id) const;\n\n    ssize_t send_to_worker_from_worker(WorkerId id, const EventData *data, int flags) {\n        return send_to_worker_from_worker(get_worker(id), data, data->size(), flags);\n    }\n\n    ssize_t send_to_reactor_thread(const EventData *ev_data, size_t sendn, SessionId session_id) const;\n\n    /**\n     * Send data to session.\n     * This function is used for sending data to the client in the server.\n     * @return true on success, false on failure.\n     */\n    bool send(SessionId session_id, const void *data, uint32_t length) const;\n    /**\n     * Send file to session.\n     * This function is used for sending files in the HTTP server.\n     * It will read the file from disk and send it to the client.\n     */\n    bool sendfile(SessionId session_id, const char *file, uint32_t l_file, off_t offset, size_t length) const;\n    bool sendwait(SessionId session_id, const void *data, uint32_t length) const;\n    bool close(SessionId session_id, bool reset = false) const;\n\n    bool notify(Connection *conn, ServerEventType event) const;\n    bool feedback(Connection *conn, ServerEventType event);\n    bool command(WorkerId process_id,\n                 Command::ProcessType process_type,\n                 const std::string &name,\n                 const std::string &msg,\n                 const Command::Callback &fn);\n\n    bool task(EventData *_task, int *dst_worker_id, bool blocking = false);\n    bool finish(const char *data, size_t data_len, int flags = 0, const EventData *current_task = nullptr);\n    bool task_sync(EventData *task, int *dst_worker_id, double timeout = -1);\n    bool task_sync(MultiTask &mtask, double timeout = -1);\n    bool send_pipe_message(WorkerId worker_id, EventData *msg);\n    bool send_pipe_message(WorkerId worker_id, const char *data, size_t len);\n\n    void init_reactor(Reactor *reactor);\n    void init_event_worker(Worker *worker) const;\n    bool init_task_workers();\n    void init_signal_handler() const;\n    void init_ipc_max_size();\n    void init_pipe_sockets(MessageBus *mb) const;\n\n    void set_max_connection(uint32_t _max_connection);\n\n    void set_max_concurrency(uint32_t _max_concurrency) const {\n        if (_max_concurrency == 0) {\n            _max_concurrency = UINT_MAX;\n        }\n        gs->max_concurrency = _max_concurrency;\n    }\n\n    void set_worker_max_concurrency(uint32_t _max_concurrency) {\n        if (_max_concurrency == 0) {\n            _max_concurrency = UINT_MAX;\n        }\n        worker_max_concurrency = _max_concurrency;\n    }\n\n    uint32_t get_max_connection() const {\n        return max_connection;\n    }\n\n    uint32_t get_max_concurrency() const {\n        return gs->max_concurrency;\n    }\n\n    uint32_t get_concurrency() const {\n        return gs->concurrency;\n    }\n\n    bool is_unavailable() const {\n        return get_concurrency() >= get_max_concurrency();\n    }\n\n    uint32_t get_worker_max_concurrency() const {\n        return worker_max_concurrency;\n    }\n\n    void set_start_session_id(SessionId value) const {\n        if (value > UINT_MAX) {\n            value = UINT_MAX;\n        }\n        gs->session_round = value;\n    }\n\n    void disable_accept();\n    int schedule_worker(int fd, SendData *data);\n\n    size_t get_connection_num() const {\n        if (gs->connection_nums) {\n            size_t num = 0;\n            for (uint32_t i = 0; i < worker_num; i++) {\n                num += gs->connection_nums[i];\n            }\n            return num;\n        } else {\n            return gs->connection_num;\n        }\n    }\n\n    static int wait_other_worker(ProcessPool *pool, const ExitStatus &exit_status);\n    static void read_worker_message(ProcessPool *pool, EventData *msg);\n\n    void drain_worker_pipe() const;\n    void clean_worker_connections(Worker *worker);\n\n    /**\n     * [Worker]\n     */\n    void worker_start_callback(Worker *worker);\n    void worker_stop_callback(Worker *worker);\n    void worker_accept_event(DataHead *info);\n    void worker_signal_init() const;\n\n    std::function<void(std::shared_ptr<Thread>, const WorkerFn &fn)> worker_thread_start;\n\n    /**\n     * [Master]\n     */\n    bool signal_handler_shutdown();\n    bool signal_handler_child_exit() const;\n    bool signal_handler_reload(bool reload_all_workers) const;\n    bool signal_handler_read_message() const;\n    bool signal_handler_reopen_logger() const;\n\n    static void worker_signal_handler(int signo);\n    static int reactor_process_main_loop(ProcessPool *pool, Worker *worker);\n    static void reactor_thread_main_loop(Server *serv, int reactor_id);\n    static bool task_pack(EventData *task, const void *data, size_t data_len);\n    static void task_dump(EventData *task);\n    static bool task_unpack(EventData *task, String *buffer, PacketPtr *packet);\n    static void master_signal_handler(int signo);\n    static void heartbeat_check(Timer *timer, TimerNode *tnode);\n\n    int start_event_worker(Worker *worker);\n\n    const char *get_startup_error_message() const;\n\n  private:\n    enum Mode mode_;\n    Connection *connection_list = nullptr;\n    Session *session_list = nullptr;\n    ServerPortGS *port_gs_list = nullptr;\n    /**\n     * http static file directory\n     */\n    std::string document_root;\n\n    std::shared_ptr<std::vector<http_server::RewriteRule>> rewrite_rules;\n    std::mutex lock_;\n    uint32_t max_connection = 0;\n    TimerNode *enable_accept_timer = nullptr;\n    std::thread heartbeat_thread;\n    /**\n     * The number of pipe per reactor maintenance\n     */\n    uint16_t reactor_pipe_num = 0;\n    ReactorThread *reactor_threads = nullptr;\n    /**\n     * Only used for temporarily saving pointers in add_worker()\n     */\n    std::vector<Worker *> user_worker_list;\n    std::unordered_map<uint16_t, network::Address> local_addr_v4_map;\n    std::unordered_map<uint16_t, network::Address> local_addr_v6_map;\n\n    int create_pipe_buffers();\n    void release_pipe_buffers();\n    void create_worker(Worker *worker);\n    Factory *create_base_factory();\n    Factory *create_thread_factory();\n    Factory *create_process_factory();\n    int start_check();\n    void check_port_type(const ListenPort *ls);\n    void store_listen_socket();\n    void store_pipe_fd(UnixSocket *p);\n    void destroy_base_factory() const;\n    void destroy_thread_factory() const;\n    void destroy_process_factory();\n    void destroy_worker(Worker *worker);\n    void destroy_task_workers() const;\n    int start_reactor_threads();\n    int start_reactor_processes();\n    int start_worker_threads();\n    int start_master_thread(Reactor *reactor);\n    void start_heartbeat_thread();\n    void stop_worker_threads();\n    bool reload_worker_threads(bool reload_all_workers) const;\n    void join_reactor_thread();\n    void stop_master_thread();\n    void join_heartbeat_thread();\n    TimerCallback get_timeout_callback(ListenPort *port, Reactor *reactor, Connection *conn) const;\n    bool init_network_interface_addr_map();\n    uint16_t get_local_addr_index(network::Address *addr);\n\n    int get_lowest_load_worker_id() const {\n        uint32_t lowest_load_worker_id = 0;\n        size_t min_coroutine = workers[0].coroutine_num;\n        for (uint32_t i = 1; i < worker_num; i++) {\n            if (workers[i].coroutine_num < min_coroutine) {\n                min_coroutine = workers[i].coroutine_num;\n                lowest_load_worker_id = i;\n            }\n        }\n        return lowest_load_worker_id;\n    }\n\n    int get_lowest_concurrent_worker_id() const {\n        uint32_t lowest_concurrent_worker_id = 0;\n        size_t min_concurrency = workers[0].concurrency;\n        for (uint32_t i = 1; i < worker_num; i++) {\n            if (workers[i].concurrency < min_concurrency) {\n                min_concurrency = workers[i].concurrency;\n                lowest_concurrent_worker_id = i;\n            }\n        }\n        return lowest_concurrent_worker_id;\n    }\n\n    int get_idle_worker_id() {\n        bool found = false;\n        uint32_t key = 0;\n        SW_LOOP_N(worker_num + 1) {\n            key = sw_atomic_fetch_add(&worker_round_id, 1) % worker_num;\n            if (workers[key].is_idle()) {\n                found = true;\n                break;\n            }\n        }\n        if (sw_unlikely(!found)) {\n            scheduler_warning = true;\n        }\n        swoole_trace_log(SW_TRACE_SERVER, \"schedule=%d, round=%d\", key, worker_round_id);\n        return key;\n    }\n\n    void lock() {\n        lock_.lock();\n    }\n\n    void unlock() {\n        lock_.unlock();\n    }\n};\n\n}  // namespace swoole\n\ntypedef swoole::Server swServer;\n\nstatic inline swoole::Server *sw_server() {\n    return SwooleG.server;\n}\n"
  },
  {
    "path": "include/swoole_signal.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole.h\"\n\n#include <csignal>\n\nnamespace swoole {\ntypedef void (*SignalHandler)(int);\n\nstruct Signal {\n    SignalHandler handler;\n    uint16_t signo;\n    bool activated;\n};\n}  // namespace swoole\n\ntypedef swoole::SignalHandler swSignalHandler;\n\n#ifdef HAVE_SIGNALFD\nvoid swoole_signalfd_init();\n#endif\n\n/**\n * The synchronous blocking IO mode is unsafe for executing PHP code within signal callback functions,\n * such as in the Server's Task worker process or the Manager process.\n * If a new signal is triggered during the execution of a signal function,\n * the recursive execution of the signal function can lead to a crash of the ZendVM.\n * When using `Swoole\\Process::signal()` to register a PHP function as a signal handler,\n * it is crucial to set the third parameter to true;\n * this way, the underlying layer will not execute directly but will call\n * `swoole_signal_dispatch()` in a safe manner to execute the PHP signal callback function.\n */\nSW_API swSignalHandler swoole_signal_set(int signo, swSignalHandler func, bool safety = false);\nSW_API bool swoole_signal_isset(int signo);\nSW_API swSignalHandler swoole_signal_set(int signo, swSignalHandler func, int restart, int mask);\nSW_API swSignalHandler swoole_signal_get_handler(int signo);\nSW_API uint32_t swoole_signal_get_listener_num();\n\nSW_API void swoole_signal_clear();\nSW_API void swoole_signal_block_all();\nSW_API void swoole_signal_unblock_all();\nSW_API char *swoole_signal_to_str(int sig);\nSW_API void swoole_signal_callback(int signo);\n\n/**\n * Only for synchronously blocked processes.\n * Due to the unreliability of signals,\n * executing complex logic directly within the signal handler function may pose security risks.\n * Therefore, the lower layer only marks memory in the signal handler\n * without directly invoking the application's set signal callback function.\n * Executing `swoole_signal_dispatch` in a safe context will actually call the signal callback function,\n * allowing for the execution of complex code within the callback.\n */\nSW_API void swoole_signal_dispatch();\n"
  },
  {
    "path": "include/swoole_socket.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole.h\"\n#include \"swoole_ssl.h\"\n#include \"swoole_buffer.h\"\n#include \"swoole_file.h\"\n\n#include <poll.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <sys/uio.h>\n\n#include <netinet/in.h>\n#include <netinet/ip6.h>\n#include <netinet/tcp.h>\n#include <netinet/udp.h>\n#include <arpa/inet.h>\n\n#include <string>\n#include <vector>\n\n#ifndef SOCK_NONBLOCK\n#define SOCK_NONBLOCK O_NONBLOCK\n#endif\n\n#ifdef __sun\n#define s6_addr8 _S6_un._S6_u8\n#define s6_addr16 _S6_un._S6_u16\n#define s6_addr32 _S6_un._S6_u32\n#endif\n\nssize_t swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size);\n\nenum {\n    SW_BAD_SOCKET = -1,\n};\n\nnamespace swoole {\nstruct GethostbynameRequest;\nstruct GetaddrinfoRequest;\n\nnamespace network {\nstruct SendfileTask {\n    off_t offset;\n    size_t length;\n    char filename[0];\n};\n\nstruct SendfileRequest {\n    File file;\n    int8_t corked;\n    off_t begin;\n    off_t end;\n\n  public:\n    SendfileRequest(const char *filename, off_t _offset) : file(filename, O_RDONLY) {\n        begin = _offset;\n        end = 0;\n        corked = 0;\n    }\n\n    const char *get_filename() const {\n        return file.get_path().c_str();\n    }\n};\n\nstruct Address {\n    union {\n        sockaddr ss;\n        sockaddr_in inet_v4;\n        sockaddr_in6 inet_v6;\n        sockaddr_un un;\n    } addr;\n    socklen_t len;\n    SocketType type;\n\n    /**\n     * Assign an address based on the socket type and host/port.\n     * For IPv4, the host can be an IP address like \"192.168.1.100\"\n     * or a domain name like \"www.example.com\".\n     * For IPv6, the host can be an IP address like \"2001:0db8:85a3:0000:0000:8a2e:0370:7334\"\n     * or a domain name like \"ipv6.example.com\".\n     * For UNIX socket, the host is the path to the socket file.\n     * If _port is 0, it will not set the port.\n     * If _resolve_name is false, it will not resolve the domain name.\n     *\n     * Returns true on success, false on failure.\n     */\n    bool assign(SocketType _type, const std::string &_host, int _port = 0, bool _resolve_name = true);\n    /**\n     * Assign an address based on a URL string.\n     * The format of the URL can be:\n     * - tcp://hostname:port\n     * - udp://hostname:port\n     * - tcp://[IPv6_address]:port\n     * - udp://[IPv6_address]:port\n     * - unix:///path/to/socket\n     * - udg:///path/to/socket\n     *\n     * Returns true on success, false on failure.\n     */\n    bool assign(const std::string &url);\n\n    int get_port() const;\n    void set_port(int _port);\n    const char *get_addr() const;\n    bool is_loopback_addr() const;\n    bool empty() const;\n\n    in_addr *addr_v4() {\n        return &addr.inet_v4.sin_addr;\n    }\n\n    in6_addr *addr_v6() {\n        return &addr.inet_v6.sin6_addr;\n    }\n\n    /**\n     * Get the string representation of the address\n     */\n    static const char *type_str(SocketType type);\n    /**\n     * Convert the address to a string representation.\n     * For IPv4, it will be in the format \"192.168.1.100\"\n     * For IPv6, it will be in the format \"2001:0db8:85a3:0000:0000:8a2e:0370:7334\"\n     * For UNIX socket, it will be the path of the socket file.\n     * The returned pointer is a static buffer, so it should not be freed.\n     */\n    static const char *addr_str(int family, const void *addr);\n    /**\n     * Verify if the input string is an IP address,\n     * where AF_INET indicates an IPv4 address, such as 192.168.1.100,\n     * and AF_INET6 indicates an IPv6 address, for example, 2001:0000:130F:0000:0000:09C0:876A:130B.\n     */\n    static bool verify_ip(int family, const std::string &str);\n    static bool verify_port(int port, bool for_connect = false);\n};\n\nstruct IOVector {\n    // we should modify iov_iterator instead of iov, iov is readonly\n    iovec *iov = nullptr;\n    iovec *iov_iterator = nullptr;\n    int count = 0;\n    int remain_count = 0;\n    int index = 0;\n    size_t offset_bytes = 0;\n\n    IOVector(const iovec *_iov, int _iovcnt);\n    ~IOVector();\n\n    void update_iterator(ssize_t _n);\n\n    iovec *get_iterator() const {\n        return iov_iterator;\n    }\n\n    size_t length() {\n        size_t len = 0;\n        SW_LOOP_N(count) {\n            len += iov[i].iov_len;\n        }\n        return len;\n    }\n\n    int get_remain_count() const {\n        return remain_count;\n    }\n\n    int get_index() const {\n        return index;\n    }\n\n    size_t get_offset_bytes() const {\n        return offset_bytes;\n    }\n};\n\nstruct Socket {\n    static double default_dns_timeout;\n    static double default_connect_timeout;\n    static double default_read_timeout;\n    static double default_write_timeout;\n    static uint32_t default_buffer_size;\n\n    int fd;\n    FdType fd_type;\n    SocketType socket_type;\n    int events;\n    bool enable_tcp_nodelay;\n    bool kernel_nobufs;\n\n    uchar removed : 1;\n    uchar silent_remove : 1;\n    uchar nonblock : 1;\n    uchar cloexec : 1;\n    uchar direct_send : 1;\n    uchar bound : 1;\n    uchar listened : 1;\n    uchar ssl_send_ : 1;\n    uchar ssl_want_read : 1;\n    uchar ssl_want_write : 1;\n    uchar ssl_renegotiation : 1;\n    uchar ssl_handshake_buffer_set : 1;\n    uchar ssl_quiet_shutdown : 1;\n    uchar ssl_closed_ : 1;\n#ifdef SW_SUPPORT_DTLS\n    uchar dtls : 1;\n#endif\n    uchar close_wait : 1;\n    uchar send_wait : 1;\n    uchar tcp_nopush : 1;\n    uchar tcp_nodelay : 1;\n    uchar skip_recv : 1;\n    uchar recv_wait : 1;\n    uchar event_hup : 1;\n    /**\n     * The default setting is false, meaning that system calls interrupted by signals will be automatically retried. If\n     * set to true, the call will not be retried but will immediately return -1, setting errno to EINTR. In this case,\n     * the caller must explicitly handle this error.\n     */\n    uchar dont_restart : 1;\n\n    // memory buffer size [user space]\n    uint32_t buffer_size;\n    uint32_t chunk_size;\n\n    void *object;\n\n    SSL *ssl;\n    uint32_t ssl_state;\n\n    /**\n     * Only used for getsockname, written by the OS, not user. This is the exact actual address.\n     */\n    Address info;\n    double dns_timeout = default_dns_timeout;\n    double connect_timeout = default_connect_timeout;\n    double read_timeout = default_read_timeout;\n    double write_timeout = default_write_timeout;\n\n    double last_received_time;\n    double last_sent_time;\n\n    Buffer *out_buffer;\n    Buffer *in_buffer;\n    String *recv_buffer;\n\n    TimerNode *recv_timer;\n    TimerNode *send_timer;\n\n    size_t total_recv_bytes;\n    size_t total_send_bytes;\n\n    // for reactor\n    int handle_send();\n    int handle_sendfile();\n    // user space memory buffer\n    void set_memory_buffer_size(uint32_t _buffer_size) {\n        buffer_size = _buffer_size;\n    }\n    // socket option [kernel space]\n    bool set_buffer_size(uint32_t _buffer_size) const;\n    bool set_recv_buffer_size(uint32_t _buffer_size) const;\n    bool set_send_buffer_size(uint32_t _buffer_size) const;\n    bool set_kernel_read_timeout(double timeout);\n    bool set_kernel_write_timeout(double timeout);\n\n    bool set_kernel_timeout(double timeout) {\n        return set_kernel_read_timeout(timeout) && set_kernel_write_timeout(timeout);\n    }\n\n    // socket option [user space]\n    void set_timeout(double timeout, int type = SW_TIMEOUT_ALL);\n    double get_timeout(TimeoutType type) const;\n    bool has_timedout() const;\n    bool has_kernel_nobufs();\n\n    bool set_nonblock() {\n        return set_fd_option(1, -1);\n    }\n\n    bool set_block() {\n        return set_fd_option(0, -1);\n    }\n\n    bool set_fd_option(int _nonblock, int _cloexec);\n\n    int set_option(int level, int optname, int optval) const {\n        return setsockopt(fd, level, optname, &optval, sizeof(optval));\n    }\n\n    int set_option(int level, int optname, const void *optval, socklen_t optlen) const {\n        return setsockopt(fd, level, optname, optval, optlen);\n    }\n\n    int get_option(int level, int optname, void *optval, socklen_t *optlen) const {\n        return getsockopt(fd, level, optname, optval, optlen);\n    }\n\n    int get_option(int level, int optname, int *optval) const {\n        socklen_t optlen = sizeof(*optval);\n        return get_option(level, optname, optval, &optlen);\n    }\n\n    int get_fd() const {\n        return fd;\n    }\n\n    const char *get_addr() const {\n        return info.get_addr();\n    }\n\n    int get_port() const {\n        return info.get_port();\n    }\n\n    uint32_t get_out_buffer_length() const {\n        return out_buffer ? out_buffer->length() : 0;\n    }\n\n    int move_fd() {\n        int sock_fd = fd;\n        fd = SW_BAD_SOCKET;\n        return sock_fd;\n    }\n\n    int get_name();\n    int get_peer_name(Address *sa) const;\n    int set_tcp_nopush(int nopush);\n\n    int set_reuse_addr(int enable = 1) const {\n        return set_option(SOL_SOCKET, SO_REUSEADDR, enable);\n    }\n\n    int set_reuse_port(int enable = 1) const {\n#ifdef SO_REUSEPORT\n        return set_option(SOL_SOCKET, SO_REUSEPORT, enable);\n#endif\n        return -1;\n    }\n\n    bool set_tcp_nodelay(int nodelay = 1);\n    bool check_liveness();\n\n    int sendfile_async(const char *filename, off_t offset, size_t length);\n    int sendfile_sync(const char *filename, off_t offset, size_t length);\n    ssize_t sendfile(const File &fp, off_t *offset, size_t length);\n\n    ssize_t recv(void *_buf, size_t _n, int _flags);\n    ssize_t send(const void *_buf, size_t _n, int _flags);\n    ssize_t peek(void *_buf, size_t _n, int _flags) const;\n    Socket *accept();\n    Socket *dup() const;\n\n    ssize_t readv(IOVector *io_vector);\n    ssize_t writev(IOVector *io_vector);\n\n    ssize_t writev(const iovec *iov, size_t iovcnt) const {\n        return ::writev(fd, iov, iovcnt);\n    }\n\n    /**\n     * If the port is 0, the system will automatically allocate an available port.\n     */\n    int bind(const std::string &_host, int port = 0);\n\n    int bind(const Address &addr) {\n        return bind(&addr.addr.ss, addr.len);\n    }\n\n    int bind(const struct sockaddr *sa, socklen_t len);\n    int listen(int backlog = 0);\n\n    void clean() const;\n    ssize_t send_sync(const void *_data, size_t _len, int flags = 0);\n    ssize_t send_async(const void *_data, size_t _len);\n    ssize_t recv_sync(void *_data, size_t _len, int flags = 0);\n    ssize_t writev_sync(const iovec *iov, size_t iovcnt);\n\n    int connect(const Address &sa) const {\n        return ::connect(fd, &sa.addr.ss, sa.len);\n    }\n\n    int connect(const Address *sa) const {\n        return ::connect(fd, &sa->addr.ss, sa->len);\n    }\n\n    int connect(const std::string &host, int port) const {\n        Address addr;\n        addr.assign(socket_type, host, port);\n        return connect(addr);\n    }\n\n    int connect_sync(const Address &sa);\n    ReturnCode connect_async(const Address &sa);\n\n    void ssl_clear_error() {\n        ERR_clear_error();\n        ssl_want_read = 0;\n        ssl_want_write = 0;\n    }\n    /**\n     * This function does not set the last error; to obtain internal SSL error information, you should call\n     * ERR_get_error().\n     */\n    int ssl_create(SSLContext *_ssl_context, int _flags);\n    int ssl_connect();\n    ReturnCode ssl_accept();\n    ssize_t ssl_recv(void *_buf, size_t _n);\n    ssize_t ssl_send(const void *_buf, size_t _n);\n    ssize_t ssl_readv(IOVector *io_vector);\n    ssize_t ssl_writev(IOVector *io_vector);\n    ssize_t ssl_sendfile(const File &fp, off_t *offset, size_t size);\n    STACK_OF(X509) * ssl_get_peer_cert_chain() const;\n    std::vector<std::string> ssl_get_peer_cert_chain(int limit) const;\n    X509 *ssl_get_peer_certificate() const;\n    int ssl_get_peer_certificate(char *buf, size_t n) const;\n    bool ssl_get_peer_certificate(String *buf) const;\n    bool ssl_verify(bool allow_self_signed) const;\n    bool ssl_check_host(const char *tls_host_name) const;\n    void ssl_catch_error() const;\n    bool ssl_shutdown();\n    void ssl_close();\n    static const char *ssl_get_error_reason(int *reason);\n\n    ssize_t recvfrom(char *_buf, size_t _len, int flags, Address *sa) const {\n        sa->len = sizeof(sa->addr);\n        return recvfrom(_buf, _len, flags, &sa->addr.ss, &sa->len);\n    }\n\n    ssize_t recvfrom(char *buf, size_t len, int flags, sockaddr *addr, socklen_t *addr_len) const;\n    ssize_t recvfrom_sync(char *_buf, size_t _len, int flags, Address *sa);\n    ssize_t recvfrom_sync(char *_buf, size_t _len, int flags, sockaddr *addr, socklen_t *addr_len);\n\n    bool cork();\n    bool uncork();\n\n    bool isset_readable_event() const {\n        return events & SW_EVENT_READ;\n    }\n\n    bool isset_writable_event() const {\n        return events & SW_EVENT_WRITE;\n    }\n\n    int wait_event(int timeout_ms, int _events) const;\n    bool wait_for(const std::function<ReturnCode()> &fn, int event, int timeout_msec = -1);\n    int what_event_want(int default_event) const;\n    void free();\n\n    static inline bool is_dgram(SocketType type) {\n        return type == SW_SOCK_UDP || type == SW_SOCK_UDP6 || type == SW_SOCK_UNIX_DGRAM;\n    }\n\n    static inline bool is_stream(SocketType type) {\n        return type == SW_SOCK_TCP || type == SW_SOCK_TCP6 || type == SW_SOCK_UNIX_STREAM;\n    }\n\n    static inline bool is_inet4(SocketType type) {\n        return type == SW_SOCK_TCP || type == SW_SOCK_UDP || type == SW_SOCK_RAW;\n    }\n\n    static inline bool is_inet6(SocketType type) {\n        return type == SW_SOCK_TCP6 || type == SW_SOCK_UDP6 || type == SW_SOCK_RAW6;\n    }\n\n    static inline bool is_tcp(SocketType type) {\n        return type == SW_SOCK_TCP || type == SW_SOCK_TCP6;\n    }\n\n    static inline bool is_udp(SocketType type) {\n        return type == SW_SOCK_UDP || type == SW_SOCK_UDP6;\n    }\n\n    static inline bool is_local(SocketType type) {\n        return type == SW_SOCK_UNIX_STREAM || type == SW_SOCK_UNIX_DGRAM;\n    }\n\n    static inline bool is_raw(SocketType type) {\n        return type == SW_SOCK_RAW || type == SW_SOCK_RAW6;\n    }\n\n    bool is_stream() const {\n        return is_stream(socket_type);\n    }\n\n    bool is_tcp() const {\n        return is_tcp(socket_type);\n    }\n\n    bool is_udp() const {\n        return is_udp(socket_type);\n    }\n\n    bool is_dgram() const {\n        return is_dgram(socket_type);\n    }\n\n    bool is_inet4() const {\n        return is_inet4(socket_type);\n    }\n\n    bool is_inet6() const {\n        return is_inet6(socket_type);\n    }\n\n    bool is_inet() const {\n        return is_inet4() || is_inet6();\n    }\n\n    bool is_local() const {\n        return is_local(socket_type);\n    }\n\n    bool is_raw() const {\n        return is_raw(socket_type);\n    }\n\n    ssize_t write(const void *_buf, size_t _len) const {\n        return ::write(fd, _buf, _len);\n    }\n\n    ssize_t read(void *_buf, size_t _len) const {\n        return ::read(fd, _buf, _len);\n    }\n\n    /**\n     * Read data from the socket synchronously without setting non-blocking or blocking IO,\n     * and allow interruptions by signals.\n     */\n    ssize_t read_sync(void *_buf, size_t _len);\n\n    /**\n     * Write data to the socket synchronously without setting non-blocking or blocking IO,\n     * and allow interruptions by signals.\n     */\n    ssize_t write_sync(const void *_buf, size_t _len);\n\n    int shutdown(int _how) const {\n        return ::shutdown(fd, _how);\n    }\n\n    ssize_t sendto_sync(const Address &dst_addr, const void *_buf, size_t _n, int flags = 0);\n\n    ssize_t sendto(const char *dst_host, int dst_port, const void *data, size_t len, int flags = 0) const {\n        Address addr;\n        if (!addr.assign(socket_type, dst_host, dst_port)) {\n            return SW_ERR;\n        }\n        return sendto(addr, data, len, flags);\n    }\n\n    ssize_t sendto(const Address &dst_addr, const void *data, size_t len, int flags = 0) const {\n        return ::sendto(fd, data, len, flags, &dst_addr.addr.ss, dst_addr.len);\n    }\n\n    int catch_error(int err);\n\n    int catch_write_error(const int err) {\n        return catch_error(err);\n    }\n\n    int catch_write_pipe_error(const int err) {\n        switch (err) {\n        case ENOBUFS:\n#ifdef __linux__\n            kernel_nobufs = true;\n            return SW_REDUCE_SIZE;\n#else\n            return catch_error(err);\n#endif\n        case EMSGSIZE:\n            return SW_REDUCE_SIZE;\n        default:\n            return catch_error(err);\n        }\n    }\n\n    int catch_read_error(const int err) {\n        return catch_error(err);\n    }\n\n    static SocketType convert_to_type(int domain, int type);\n    static SocketType convert_to_type(std::string &host);\n    static int get_domain_and_type(SocketType type, int *sock_domain, int *sock_type);\n};\n\nstd::string gethostbyname(int type, const std::string &name);\nint gethostbyname(int type, const char *name, char *addr);\nint gethostbyname(GethostbynameRequest *req);\nint getaddrinfo(GetaddrinfoRequest *req);\n\n}  // namespace network\n\n/**\n * This function will never return NULL; if memory allocation fails, a C++ exception will be thrown.\n * Must use the `socket->free()` function to release the object pointer instead of the `delete` operator.\n * When the socket is released, it will close the file descriptor (fd).\n * If you do not want the fd to be closed, use `socket->move_fd()` to relinquish ownership of the fd.\n */\nnetwork::Socket *make_socket(int fd, FdType fd_type);\n/**\n * The following three functions will return a null pointer if the socket creation fails.\n * It is essential to check the return value;\n * if it is nullptr, you should inspect errno to determine the cause of the error.\n */\nnetwork::Socket *make_socket(SocketType socket_type, FdType fd_type, int flags);\nnetwork::Socket *make_socket(\n    SocketType type, FdType fd_type, int sock_domain, int sock_type, int socket_protocol, int flags);\nint socket(int sock_domain, int sock_type, int socket_protocol, int flags);\nnetwork::Socket *make_server_socket(SocketType socket_type,\n                                    const char *address,\n                                    int port = 0,\n                                    int backlog = SW_BACKLOG);\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_socket_hook.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef SW_SOCKET_HOOK_H_\n#define SW_SOCKET_HOOK_H_\n\n#include <stdlib.h>\n#include <unistd.h>\n#include <sys/socket.h>\n#include <poll.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"swoole_coroutine_api.h\"\n\n#define socket(domain, type, protocol) swoole_coroutine_socket(domain, type, protocol)\n#define send(sockfd, buf, len, flags) swoole_coroutine_send(sockfd, buf, len, flags)\n#define read(sockfd, buf, len) swoole_coroutine_read(sockfd, buf, len)\n#define write(sockfd, buf, len) swoole_coroutine_write(sockfd, buf, len)\n#define recv(sockfd, buf, len, flags) swoole_coroutine_recv(sockfd, buf, len, flags)\n#define close(fd) swoole_coroutine_close(fd)\n#define connect(sockfd, addr, addrlen) swoole_coroutine_connect(sockfd, addr, addrlen)\n#define accept(sockfd, addr, addrlen) swoole_coroutine_accept(sockfd, addr, addrlen)\n#ifdef SW_HOOK_POLL_FAKE\n#define poll(fds, nfds, timeout) swoole_coroutine_poll_fake(fds, nfds, timeout)\n#else\n#define poll(fds, nfds, timeout) swoole_coroutine_poll(fds, nfds, timeout)\n#endif\n#define sendmsg(sockfd, msg, flags) swoole_coroutine_sendmsg(sockfd, msg, flags)\n#define recvmsg(sockfd, msg, flags) swoole_coroutine_recvmsg(sockfd, msg, flags)\n#define getaddrinfo(name, service, req, pai) swoole_coroutine_getaddrinfo(name, service, req, pai)\n#define gethostbyname(name) swoole_coroutine_gethostbyname(name)\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/swoole_socket_impl.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_coroutine_socket.h\"\n\n#include <memory>\n\nusing CoSocket = swoole::coroutine::Socket;\nusing NetSocket = swoole::network::Socket;\n\n#ifdef SW_USE_URING_SOCKET\n#include \"swoole_uring_socket.h\"\nusing swoole::coroutine::UringSocket;\nusing SocketImpl = UringSocket;\n#else\nusing SocketImpl = CoSocket;\n#endif\n\nstd::shared_ptr<SocketImpl> swoole_coroutine_get_socket_object(int sockfd);\nstd::shared_ptr<SocketImpl> swoole_coroutine_get_socket_object_ex(int sockfd);\n"
  },
  {
    "path": "include/swoole_ssl.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole.h\"\n\n#include <unordered_map>\n#include <string>\n\n#include <openssl/ssl.h>\n#include <openssl/bio.h>\n#include <openssl/err.h>\n#include <openssl/crypto.h>\n#include <openssl/x509.h>\n#include <openssl/x509v3.h>\n#include <openssl/rand.h>\n#include <openssl/conf.h>\n#include <openssl/opensslv.h>\n\n#if OPENSSL_VERSION_NUMBER >= 0x10100000L\n#define SW_SUPPORT_DTLS\n#endif\n\n#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3000000fL\n#undef SW_SUPPORT_DTLS\n#endif\n\n#ifdef OPENSSL_IS_BORINGSSL\n#define BIO_CTRL_DGRAM_SET_CONNECTED 32\n#define BIO_CTRL_DGRAM_SET_PEER 44\n#define BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT 45\n#define BIO_dgram_get_peer(b, peer) (int) BIO_ctrl(b, BIO_CTRL_DGRAM_GET_PEER, 0, (char *) (peer))\n#define OPENSSL_assert(x) assert(x)\n#endif\n\nenum swSSLCreateFlag {\n    SW_SSL_SERVER = 1,\n    SW_SSL_CLIENT = 2,\n};\n\nenum swSSLState {\n    SW_SSL_STATE_HANDSHAKE = 0,\n    SW_SSL_STATE_READY = 1,\n    SW_SSL_STATE_WAIT_STREAM = 2,\n};\n\nenum swSSLVersion {\n    SW_SSL_SSLv2 = 1u << 1,\n    SW_SSL_SSLv3 = 1u << 2,\n    SW_SSL_TLSv1 = 1u << 3,\n    SW_SSL_TLSv1_1 = 1u << 4,\n    SW_SSL_TLSv1_2 = 1u << 5,\n    SW_SSL_TLSv1_3 = 1u << 6,\n    SW_SSL_DTLS = 1u << 7,\n};\n\n#define SW_SSL_ALL (SW_SSL_SSLv2 | SW_SSL_SSLv3 | SW_SSL_TLSv1 | SW_SSL_TLSv1_1 | SW_SSL_TLSv1_2 | SW_SSL_TLSv1_3)\n\nenum swSSLMethod {\n    SW_SSLv23_METHOD = 0,\n    SW_SSLv3_METHOD,\n    SW_SSLv3_SERVER_METHOD,\n    SW_SSLv3_CLIENT_METHOD,\n    SW_SSLv23_SERVER_METHOD,\n    SW_SSLv23_CLIENT_METHOD,\n    SW_TLSv1_METHOD,\n    SW_TLSv1_SERVER_METHOD,\n    SW_TLSv1_CLIENT_METHOD,\n#ifdef TLS1_1_VERSION\n    SW_TLSv1_1_METHOD,\n    SW_TLSv1_1_SERVER_METHOD,\n    SW_TLSv1_1_CLIENT_METHOD,\n#endif\n#ifdef TLS1_2_VERSION\n    SW_TLSv1_2_METHOD,\n    SW_TLSv1_2_SERVER_METHOD,\n    SW_TLSv1_2_CLIENT_METHOD,\n#endif\n#ifdef SW_SUPPORT_DTLS\n    SW_DTLS_CLIENT_METHOD,\n    SW_DTLS_SERVER_METHOD,\n#endif\n};\n\nnamespace swoole {\n\nstruct SSLContext {\n    uchar http : 1;\n    uchar http_v2 : 1;\n    uchar prefer_server_ciphers : 1;\n    uchar session_tickets : 1;\n    uchar stapling : 1;\n    uchar stapling_verify : 1;\n    std::string ciphers;\n    std::string ecdh_curve;\n    std::string session_cache;\n    std::string dhparam;\n    std::string cert_file;\n    std::string key_file;\n    std::string passphrase;\n    std::string client_cert_file;\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n    uchar disable_tls_host_name : 1;\n    std::string tls_host_name;\n#endif\n\n#ifdef OPENSSL_IS_BORINGSSL\n    uint8_t grease;\n#endif\n\n    std::string cafile;\n    std::string capath;\n    uint8_t verify_depth;\n    uchar disable_compress : 1;\n    uchar verify_peer : 1;\n    uchar allow_self_signed : 1;\n    uint32_t protocols;\n    uint8_t create_flag;\n    SSL_CTX *context;\n\n    SSL_CTX *get_context() const {\n        return context;\n    }\n\n    bool ready() const {\n        return context != nullptr;\n    }\n\n    void set_protocols(uint32_t _protocols) {\n        protocols = _protocols;\n    }\n\n    bool set_cert_file(const std::string &_cert_file) {\n        if (access(_cert_file.c_str(), R_OK) < 0) {\n            swoole_warning(\"ssl cert file[%s] not found\", _cert_file.c_str());\n            return false;\n        }\n        cert_file = _cert_file;\n        return true;\n    }\n\n    bool set_key_file(const std::string &_key_file) {\n        if (access(_key_file.c_str(), R_OK) < 0) {\n            swoole_warning(\"ssl key file[%s] not found\", _key_file.c_str());\n            return false;\n        }\n        key_file = _key_file;\n        return true;\n    }\n\n    bool set_client_cert_file(const std::string &file) {\n        if (access(file.c_str(), R_OK) < 0) {\n            swoole_warning(\"ssl client cert file[%s] not found\", file.c_str());\n            return false;\n        }\n        client_cert_file = file;\n        return true;\n    }\n\n    bool create();\n    bool set_capath() const;\n    bool set_ciphers() const;\n    bool set_client_certificate() const;\n    bool set_ecdh_curve() const;\n    bool set_dhparam() const;\n    ~SSLContext();\n};\n}  // namespace swoole\n\nvoid swoole_ssl_init();\nvoid swoole_ssl_destroy();\nvoid swoole_ssl_lock_callback(int mode, int type, const char *file, int line);\nvoid swoole_ssl_server_http_advise(swoole::SSLContext &);\nconst char *swoole_ssl_get_error();\nint swoole_ssl_get_ex_connection_index();\nint swoole_ssl_get_ex_port_index();\nstd::string swoole_ssl_get_version_message();\n"
  },
  {
    "path": "include/swoole_static_handler.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_server.h\"\n#include \"swoole_http.h\"\n#include \"swoole_mime_type.h\"\n\n#include <string>\n#include <set>\n\nnamespace swoole {\nnamespace http_server {\n\nstruct RewriteRule {\n    std::string pattern;\n    std::string replacement;\n    bool is_regex;\n};\n\nclass StaticHandler {\n  private:\n    Server *serv;\n    std::string request_url;\n    std::string original_url;\n    std::string dir_path;\n    std::set<std::string> dir_files;\n    std::string index_file;\n    typedef struct {\n        off_t offset;\n        size_t length;\n        char part_header[SW_HTTP_SERVER_PART_HEADER];\n    } task_t;\n    std::vector<task_t> tasks;\n\n    size_t l_filename = 0;\n    char filename[PATH_MAX];\n    struct stat file_stat;\n    bool last = false;\n    std::string content_type;\n    std::string boundary;\n    std::string end_part;\n    size_t content_length = 0;\n\n  public:\n    int status_code = SW_HTTP_OK;\n    StaticHandler(Server *_server, const char *url, size_t url_length)\n        : request_url(url, url_length), original_url(url, url_length) {\n        serv = _server;\n    }\n\n    /**\n     * @return true: continue to execute backwards\n     * @return false: break static handler\n     */\n    bool try_serve();\n    bool try_serve_index_file();\n\n    bool is_modified(const std::string &date_if_modified_since) const;\n    bool is_modified_range(const std::string &date_range) const;\n    size_t make_index_page(String *buffer);\n    bool get_dir_files();\n    bool set_filename(const std::string &filename);\n\n    bool catch_error() {\n        if (last) {\n            status_code = SW_HTTP_NOT_FOUND;\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    bool has_index_file() const {\n        return !index_file.empty();\n    }\n\n    bool is_enabled_auto_index() const {\n        return serv->http_autoindex;\n    }\n\n    static std::string get_date();\n\n    time_t get_file_mtime() const {\n#ifdef __MACH__\n        return file_stat.st_mtimespec.tv_sec;\n#else\n        return file_stat.st_mtim.tv_sec;\n#endif\n    }\n\n    std::string get_date_last_modified() const;\n\n    const char *get_filename() const {\n        return filename;\n    }\n\n    const std::string &get_request_url() const {\n        return request_url;\n    }\n\n    void set_request_url(const std::string &rewritten_url) {\n        request_url = rewritten_url;\n    }\n\n    const std::string &get_boundary() {\n        if (boundary.empty()) {\n            boundary = std::string(SW_HTTP_SERVER_BOUNDARY_PREKEY);\n            swoole_random_string(boundary, SW_HTTP_SERVER_BOUNDARY_TOTAL_SIZE - sizeof(SW_HTTP_SERVER_BOUNDARY_PREKEY));\n        }\n        return boundary;\n    }\n\n    const std::string &get_content_type() {\n        if (tasks.size() > 1) {\n            content_type = std::string(\"multipart/byteranges; boundary=\") + get_boundary();\n            return content_type;\n        } else {\n            return get_mimetype();\n        }\n    }\n\n    const std::string &get_mimetype() const {\n        return mime_type::get(get_filename());\n    }\n\n    std::string get_filename_std_string() {\n        return {filename, l_filename};\n    }\n\n    bool get_absolute_path();\n\n    const std::string &get_original_url() const {\n        return original_url;\n    }\n\n    size_t get_filesize() const {\n        return file_stat.st_size;\n    }\n\n    const std::vector<task_t> &get_tasks() {\n        return tasks;\n    }\n\n    bool is_dir() const {\n        return S_ISDIR(file_stat.st_mode);\n    }\n\n    bool is_link() const {\n        return S_ISLNK(file_stat.st_mode);\n    }\n\n    bool is_file() const {\n        return S_ISREG(file_stat.st_mode);\n    }\n\n    bool is_absolute_path() {\n        return swoole_strnpos(filename, l_filename, SW_STRL(\"..\")) == -1;\n    }\n\n    bool is_located_in_document_root() {\n        const std::string &document_root = serv->get_document_root();\n        const size_t l_document_root = document_root.length();\n\n        return l_filename > l_document_root && filename[l_document_root] == '/' &&\n               swoole_str_starts_with(filename, l_filename, document_root.c_str(), l_document_root);\n    }\n\n    size_t get_content_length() const {\n        return content_length;\n    }\n\n    const std::string &get_end_part() {\n        return end_part;\n    }\n\n    void parse_range(const char *range, const char *if_range);\n};\n\n};  // namespace http_server\n};  // namespace swoole\n"
  },
  {
    "path": "include/swoole_string.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole.h\"\n\n#include <string>\n\n#define SW_STRINGL(s) s->str, s->length\n#define SW_STRINGS(s) s->str, s->size\n// copy value\n#define SW_STRINGCVL(s) s->str + s->offset, s->length - s->offset\n// append value\n#define SW_STRINGAVL(s) s->str + s->length, s->size - s->length\n\nnamespace swoole {\ntypedef std::function<bool(const char *, size_t)> StringExplodeHandler;\n\nclass String {\n  private:\n    void alloc(size_t _size, const Allocator *_allocator);\n    void move(String &&src);\n    void copy(const String &src);\n\n  public:\n    size_t length;\n    size_t size;\n    off_t offset;\n    char *str;\n    const Allocator *allocator;\n\n    String() {\n        length = size = offset = 0;\n        str = nullptr;\n        allocator = nullptr;\n    }\n\n    explicit String(size_t _size, const Allocator *_allocator = nullptr) {\n        alloc(_size, _allocator);\n    }\n\n    String(const char *_str, size_t _length) {\n        alloc(_length + 1, nullptr);\n        memcpy(str, _str, _length);\n        str[_length] = '\\0';\n        length = _length;\n    }\n\n    explicit String(const std::string &_str) : String(_str.c_str(), _str.length()) {}\n\n    String(const String &src) {\n        copy(src);\n    }\n\n    String(String &&src) noexcept {\n        move(std::move(src));\n    }\n\n    String &operator=(const String &src) noexcept;\n    String &operator=(String &&src) noexcept;\n\n    ~String() {\n        if (allocator && str) {\n            allocator->free(str);\n        }\n    }\n\n    char *value() const {\n        return str;\n    }\n\n    size_t get_length() const {\n        return length;\n    }\n\n    size_t capacity() const {\n        return size;\n    }\n\n    std::string to_std_string() const {\n        return {str, length};\n    }\n\n    bool contains(const char *needle, size_t l_needle) const {\n        return swoole_strnstr(str, length, needle, l_needle) != nullptr;\n    }\n\n    bool contains(const std::string &needle) const {\n        return contains(needle.c_str(), needle.size());\n    }\n\n    bool starts_with(const char *needle, size_t l_needle) const {\n        if (length < l_needle) {\n            return false;\n        }\n        return memcmp(str, needle, l_needle) == 0;\n    }\n\n    bool starts_with(const std::string &needle) const {\n        return starts_with(needle.c_str(), needle.length());\n    }\n\n    bool ends_with(const char *needle, size_t l_needle) const {\n        if (length < l_needle) {\n            return false;\n        }\n        return memcmp(str + length - l_needle, needle, l_needle) == 0;\n    }\n\n    bool ends_with(const std::string &needle) const {\n        return ends_with(needle.c_str(), needle.length());\n    }\n\n    bool equals(const char *data, size_t len) const {\n        if (length != len) {\n            return false;\n        }\n        return memcmp(str, data, len) == 0;\n    }\n\n    bool equals(const std::string &data) const {\n        if (length != data.size()) {\n            return false;\n        }\n        return memcmp(str, data.c_str(), length) == 0;\n    }\n\n    void grow(size_t incr_value);\n    String substr(size_t offset, size_t len) const;\n\n    bool empty() const {\n        return str == nullptr || length == 0;\n    }\n\n    void clear() {\n        length = 0;\n        offset = 0;\n    }\n\n    void extend() {\n        extend(size * 2);\n    }\n\n    void extend(size_t new_size) {\n        assert(new_size > size);\n        reserve(new_size);\n    }\n\n    void extend_align(size_t _new_size) {\n        size_t align_size = SW_MEM_ALIGNED_SIZE(size * 2);\n        while (align_size < _new_size) {\n            align_size *= 2;\n        }\n        reserve(align_size);\n    }\n\n    void reserve(size_t new_size);\n    /**\n     * Transfer ownership of the string content pointer to the caller, who will capture this memory.\n     * The caller must manage and free this memory; it will not free when the string is destructed.\n     */\n    char *release();\n    void repeat(const char *data, size_t len, size_t n);\n    void append(const char *append_str, size_t length);\n\n    void append(const std::string &append_str) {\n        append(append_str.c_str(), append_str.length());\n    }\n\n    void append(const char c) {\n        append(&c, sizeof(c));\n    }\n\n    void append(int value);\n    void append(const String &append_str);\n    bool append_random_bytes(size_t length, bool base64 = false);\n\n    void write(off_t _offset, const String &write_str);\n    void write(off_t _offset, const char *write_str, size_t _length);\n\n    void set_null_terminated() {\n        if (length == size) {\n            extend(length + 1);\n        }\n        str[length] = '\\0';\n    }\n\n    ssize_t split(const char *delimiter, size_t delimiter_length, const StringExplodeHandler &handler);\n    void print(bool print_value = true) const;\n\n    enum FormatFlag {\n        FORMAT_APPEND = 1 << 0,\n        FORMAT_GROW = 1 << 1,\n    };\n\n    template <typename... Args>\n    size_t format_impl(int flags, const char *format, Args... args) {\n        size_t _size = sw_snprintf(nullptr, 0, format, args...);\n        if (_size == 0) {\n            return 0;\n        }\n        // store \\0 terminator\n        ++_size;\n\n        size_t new_size = (flags & FORMAT_APPEND) ? length + _size : _size;\n        if (flags & FORMAT_GROW) {\n            size_t align_size = SW_MEM_ALIGNED_SIZE(size * 2);\n            while (align_size < new_size) {\n                align_size *= 2;\n            }\n            new_size = align_size;\n        }\n\n        size_t n;\n        if (flags & FORMAT_APPEND) {\n            if (_size > size - length) {\n                reserve(new_size);\n            }\n            n = sw_snprintf(str + length, size - length, format, args...);\n            length += n;\n        } else {\n            if (_size > size) {\n                reserve(new_size);\n            }\n            n = sw_snprintf(str, size, format, args...);\n            length = n;\n        }\n\n        return n;\n    }\n\n    // This function replaces the entire string instead of appending content.\n    template <typename... Args>\n    size_t format(const char *format, Args... args) {\n        return format_impl(0, format, args...);\n    }\n\n    template <typename... Args>\n    size_t append_format(const char *format, Args... args) {\n        return format_impl(FORMAT_APPEND, format, args...);\n    }\n\n    char *pop(size_t init_size);\n    void reduce(off_t offset);\n};\n\ninline String *make_string(size_t size, const Allocator *allocator = nullptr) {\n    return new String(size, allocator);\n}\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_table.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_memory.h\"\n#include \"swoole_lock.h\"\n\n#include <csignal>\n\n#include <vector>\n#include <unordered_map>\n\n//#define SW_TABLE_DEBUG   0\n#define SW_TABLE_FORCE_UNLOCK_TIME 2000  // milliseconds\n#define SW_TABLE_USE_PHP_HASH\n#define SW_TABLE_MAX_ROW_SIZE 0x80000000\n\nnamespace swoole {\ntypedef uint32_t TableStringLength;\ntypedef uint64_t (*HashFunc)(const char *key, size_t len);\n\nstruct TableColumn;\n\nstruct TableRow {\n    sw_atomic_t lock_;\n    pid_t lock_pid;\n    /**\n     * 1:used, 0:empty\n     */\n    uint8_t active;\n    uint8_t key_len;\n    /**\n     * next slot\n     */\n    TableRow *next;\n    /**\n     * Hash Key\n     */\n    char key[SW_TABLE_KEY_SIZE];\n    char data[0];\n\n    void lock();\n\n    void unlock() {\n        sw_spinlock_release(&lock_);\n    }\n\n    void clear() {\n        sw_memset_zero((char *) &lock_pid, sizeof(TableRow) - offsetof(TableRow, lock_pid));\n    }\n\n    void set_value(const TableColumn *col, const void *value, size_t vlen);\n    void get_value(const TableColumn *col, double *dval) const;\n    void get_value(const TableColumn *col, long *lval) const;\n    void get_value(const TableColumn *col, char **strval, TableStringLength *strlen);\n};\n\nstruct TableIterator {\n    size_t row_memory_size_;\n    uint32_t absolute_index = 0;\n    uint32_t collision_index = 0;\n    TableRow *current_;\n    Mutex *mutex_;\n\n    explicit TableIterator(size_t row_size);\n    ~TableIterator();\n\n    void reset();\n\n    void lock() const {\n        mutex_->lock();\n    }\n\n    void unlock() const {\n        mutex_->unlock();\n    }\n};\n\nenum TableFlag {\n    SW_TABLE_FLAG_NEW_ROW = 1,\n    SW_TABLE_FLAG_CONFLICT = 1u << 1,\n};\n\nstruct TableColumn {\n    enum Type {\n        TYPE_INT = 1,\n        TYPE_FLOAT,\n        TYPE_STRING,\n    };\n\n    Type type;\n    uint32_t size;\n    std::string name;\n    size_t index;\n\n    TableColumn(const std::string &_name, Type _type, size_t _size);\n\n    void clear(TableRow *row) const;\n};\n\nclass Table {\n    std::unordered_map<std::string, TableColumn *> *column_map;\n    Mutex *mutex;\n    size_t size;\n    size_t mask;\n    size_t item_size;\n    size_t memory_size;\n    float conflict_proportion;\n\n    /**\n     * total rows that in active state(shm)\n     */\n    sw_atomic_t row_num;\n\n    TableRow **rows;\n    FixedPool *pool;\n\n    TableIterator *iterator;\n    HashFunc hash_func;\n    bool created;\n\n    void *memory;\n\n  public:\n    std::vector<TableColumn *> *column_list;\n\n    size_t conflict_count;\n    sw_atomic_long_t insert_count;\n    sw_atomic_long_t delete_count;\n    sw_atomic_long_t update_count;\n    uint32_t conflict_max_level;\n\n    Table() = delete;\n    ~Table() = delete;\n\n    static Table *make(uint32_t rows_size, float conflict_proportion);\n    size_t calc_memory_size() const;\n    size_t get_memory_size() const;\n    uint32_t get_available_slice_num() const;\n    uint32_t get_total_slice_num() const;\n    bool create();\n    bool add_column(const std::string &name, enum TableColumn::Type type, size_t size);\n    TableColumn *get_column(const std::string &key) const;\n    TableRow *set(const char *key, uint16_t keylen, TableRow **rowlock, int *out_flags);\n    TableRow *get(const char *key, uint16_t keylen, TableRow **rowlock) const;\n    bool exists(const char *key, uint16_t keylen) const;\n    bool del(const char *key, uint16_t keylen);\n    void forward() const;\n    // release shared memory\n    void destroy();\n\n    bool is_created() const {\n        return created;\n    }\n\n    bool ready() const {\n        return memory != nullptr;\n    }\n\n    void set_hash_func(HashFunc _fn) {\n        hash_func = _fn;\n    }\n\n    size_t get_size() const {\n        return size;\n    }\n\n    float get_conflict_proportion() const {\n        return conflict_proportion;\n    }\n\n    size_t get_column_size() const {\n        return column_map->size();\n    }\n\n    TableRow *get_by_index(uint32_t index) const {\n        TableRow *row = rows[index];\n        return row->active ? row : nullptr;\n    }\n\n    size_t count() const {\n        return row_num;\n    }\n\n    bool exists(const std::string &key) const {\n        return exists(key.c_str(), key.length());\n    }\n\n    TableRow *current() const {\n        return iterator->current_;\n    }\n\n    void rewind() const {\n        iterator->lock();\n        iterator->reset();\n        iterator->unlock();\n    }\n\n    void clear_row(TableRow *row) const {\n        for (auto &i : *column_list) {\n            i->clear(row);\n        }\n    }\n\n  private:\n    TableRow *hash(const char *key, int keylen) const {\n        uint64_t hashv = hash_func(key, keylen);\n        uint64_t index = hashv & mask;\n        assert(index < size);\n        return rows[index];\n    }\n\n    TableRow *alloc_row() const {\n        lock();\n        const auto new_row = static_cast<TableRow *>(pool->alloc(0));\n        unlock();\n        return new_row;\n    }\n\n    void free_row(TableRow *tmp) const {\n        lock();\n        tmp->clear();\n        pool->free(tmp);\n        unlock();\n    }\n\n    static void check_key_length(uint16_t *keylen) {\n        if (*keylen >= SW_TABLE_KEY_SIZE) {\n            *keylen = SW_TABLE_KEY_SIZE - 1;\n        }\n    }\n\n    void init_row(TableRow *new_row, const char *key, int keylen) {\n        sw_memset_zero(reinterpret_cast<char *>(new_row) + offsetof(TableRow, active),\n                       sizeof(TableRow) - offsetof(TableRow, active));\n        memcpy(new_row->key, key, keylen);\n        new_row->key[keylen] = '\\0';\n        new_row->key_len = keylen;\n        new_row->active = 1;\n        sw_atomic_fetch_add(&(row_num), 1);\n    }\n\n    int lock() const {\n        return mutex->lock();\n    }\n\n    int unlock() const {\n        return mutex->unlock();\n    }\n};\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_thread.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_lock.h\"\n\n#include <thread>\n#include <string>\n\nlong swoole_thread_get_native_id();\nbool swoole_thread_set_name(const char *name);\nbool swoole_thread_get_name(char *buf, size_t len);\nstd::string swoole_thread_id_to_str(std::thread::id id);\n\nnamespace swoole {\nclass Thread {\n    int exit_status = 0;\n    bool living = false;\n    std::thread thread;\n\n  public:\n    bool is_alive() const {\n        return living;\n    }\n\n    bool joinable() const {\n        return thread.joinable();\n    }\n\n    void join() {\n        thread.join();\n    }\n\n    void detach() {\n        thread.detach();\n    }\n\n    int get_exit_status() const {\n        return exit_status;\n    }\n\n    pthread_t get_id() {\n        return thread.native_handle();\n    }\n\n    template <typename Callable>\n    void start(Callable fn) {\n        thread = std::thread(fn);\n    }\n\n    void enter() {\n        exit_status = 0;\n        living = true;\n    }\n\n    void exit(const int status) {\n        exit_status = status;\n        living = false;\n    }\n};\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_timer.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_heap.h\"\n#include \"swoole_reactor.h\"\n#include \"swoole_util.h\"\n\n#include <unordered_map>\n\n#define SW_TIMER_MIN_MS 1\n#define SW_TIMER_MIN_SEC 0.001\n#define SW_TIMER_MAX_MS LONG_MAX\n#define SW_TIMER_MAX_SEC ((double) LONG_MAX / 1000)\n\nnamespace swoole {\ntypedef std::function<void(TimerNode *)> TimerDestructor;\n\nstruct TimerNode {\n    enum Type {\n        TYPE_KERNEL,\n        TYPE_PHP,\n    };\n    long id;\n    Type type;\n    int64_t exec_msec;\n    int64_t interval;\n    uint64_t exec_count;\n    uint64_t round;\n    bool removed;\n    HeapNode *heap_node;\n    TimerCallback callback;\n    void *data;\n    TimerDestructor destructor;\n};\n\nclass Timer {\n    /*--------------signal timer--------------*/\n    Reactor *reactor_ = nullptr;\n    Heap heap;\n    std::unordered_map<long, TimerNode *> map;\n    uint64_t round;\n    long _next_id;\n    long _current_id;\n    /*---------------event timer--------------*/\n    int64_t base_time;\n    /**\n     * The time when the next timer will trigger, in milliseconds.\n     * This event will serve as the timeout for the event loop's epoll/poll/kqueue,\n     * or be set as the trigger time for the system clock.\n     */\n    int64_t next_msec_;\n    /*----------------------------------------*/\n    std::function<int(Timer *timer, long exec_msec)> set;\n    std::function<void(Timer *timer)> close;\n\n    void init(bool manually_trigger);\n    void init_with_reactor(Reactor *reactor);\n    void init_with_system_timer();\n    void release_node(TimerNode *tnode);\n\n  public:\n    explicit Timer(bool manually_trigger);\n    ~Timer();\n\n    int64_t get_relative_msec() const {\n        return get_absolute_msec() - base_time;\n    }\n\n    int64_t get_next_msec() const {\n        return next_msec_;\n    }\n\n    static int64_t get_absolute_msec() {\n        return time<std::chrono::milliseconds>(true);\n    }\n\n    Reactor *get_reactor() const {\n        return reactor_;\n    }\n\n    TimerNode *add(long _msec, bool persistent, void *data, const TimerCallback &callback);\n    bool remove(TimerNode *tnode);\n    void update(const TimerNode *tnode) const {\n        heap.change_priority(tnode->exec_msec, tnode->heap_node);\n    }\n    void delay(TimerNode *tnode, long delay_ms) const {\n        long now_ms = get_relative_msec();\n        tnode->exec_msec = (now_ms < 0 ? tnode->exec_msec : now_ms) + delay_ms;\n        update(tnode);\n    }\n    void reinit(bool manually_trigger = false);\n    void select();\n\n    TimerNode *get(long id) {\n        const auto it = map.find(id);\n        if (it == map.end()) {\n            return nullptr;\n        }\n        return it->second;\n    }\n\n    TimerNode *get(long id, const TimerNode::Type type) {\n        TimerNode *tnode = get(id);\n        return (tnode && tnode->type == type) ? tnode : nullptr;\n    }\n\n    size_t count() const {\n        return map.size();\n    }\n\n    uint64_t get_round() const {\n        return round;\n    }\n\n    bool remove(long id) {\n        return remove(get(id));\n    }\n\n    const std::unordered_map<long, TimerNode *> &get_map() {\n        return map;\n    }\n};\n\nstatic inline long sec2msec(const long sec) {\n    return sec * 1000;\n}\n\nstatic inline long sec2msec(const int sec) {\n    return sec * 1000;\n}\n\nstatic inline long sec2msec(const double sec) {\n    return static_cast<long>(sec * 1000);\n}\n\nstatic inline double msec2sec(const int msec) {\n    return static_cast<double>(msec) / 1000.0;\n}\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_uring_socket.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_coroutine_socket.h\"\n\nnamespace swoole {\nnamespace coroutine {\nclass UringSocket : public Socket {\n    BIO *rbio = nullptr;\n    BIO *wbio = nullptr;\n\n    bool ssl_bio_write();\n    bool ssl_bio_read();\n    bool ssl_bio_prepare();\n    bool ssl_bio_perform(int rc, const char *fn);\n    ssize_t ssl_recv(void *_buf, size_t _n);\n    ssize_t ssl_send(const void *_buf, size_t _n);\n    ssize_t ssl_readv(network::IOVector *io_vector);\n    ssize_t ssl_writev(network::IOVector *io_vector);\n    ssize_t ssl_sendfile(const File &file, off_t *offset, size_t size);\n\n    ssize_t uring_send(const void *_buf, size_t _n);\n    ssize_t uring_recv(void *_buf, size_t _n);\n    ssize_t uring_readv(const struct iovec *iovec, int count);\n    ssize_t uring_writev(const struct iovec *iovec, int count);\n    ssize_t uring_sendfile(const File &file, off_t *offset, size_t size);\n    network::Socket *uring_accept(double timeout);\n\n    bool is_ssl() {\n        return !!socket->ssl;\n    }\n\n  public:\n    UringSocket(SocketType sock_type) : Socket(sock_type) {}\n    UringSocket(int domain, int type, int protocol) : Socket(domain, type, protocol) {}\n    UringSocket(int _fd, int _domain, int _type, int _protocol) : Socket(_fd, _domain, _type, _protocol) {}\n    UringSocket(int _fd, SocketType _type) : Socket(_fd, _type) {}\n    UringSocket(network::Socket *sock, const UringSocket *server_sock) : Socket(sock, server_sock) {}\n\n    bool connect(const std::string &_host, int _port = 0, int flags = 0) {\n        return Socket::connect(_host, _port, flags);\n    }\n\n    ssize_t recvfrom(void *_buf, size_t _n) {\n        return Socket::recvfrom(_buf, _n);\n    }\n\n    bool connect(const sockaddr *addr, socklen_t addrlen) override;\n    UringSocket *accept(double timeout = 0);\n\n    ssize_t read(void *_buf, size_t _n) override;\n    ssize_t write(const void *_buf, size_t _n) override;\n    ssize_t recvmsg(msghdr *msg, int flags) override;\n    ssize_t sendmsg(const msghdr *msg, int flags) override;\n    ssize_t recvfrom(void *_buf, size_t _n, sockaddr *_addr, socklen_t *_socklen) override;\n    ssize_t sendto(const std::string &host, int port, const void *_buf, size_t _n) override;\n    ssize_t recv(void *_buf, size_t _n) override;\n    ssize_t send(const void *_buf, size_t _n) override;\n    ssize_t recv_all(void *_buf, size_t _n) override;\n    ssize_t send_all(const void *_buf, size_t _n) override;\n    bool sendfile(const char *filename, off_t offset, size_t length) override;\n    ssize_t readv(network::IOVector *io_vector) override;\n    ssize_t readv_all(network::IOVector *io_vector) override;\n    ssize_t writev(network::IOVector *io_vector) override;\n    ssize_t writev_all(network::IOVector *io_vector) override;\n    bool poll(EventType _type, double timeout = 0) override;\n    bool ssl_handshake() override;\n};\n}  // namespace coroutine\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_util.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include <cstdio>\n#include <cstdarg>\n#include <cassert>\n#include <ctime>\n\n#include <string>\n#include <memory>\n#include <chrono>\n#include <set>\n#include <vector>\n#include <stack>\n#include <thread>\n#include <type_traits>\n#include <algorithm>\n\n#define SW_STRUCT_MEMBER_SIZE(_s, _m) sizeof(std::declval<struct _s>()._m)\n\nnamespace swoole {\ntemplate <typename T>\nbool in_range(T value, std::initializer_list<T> allowed_values) {\n    return std::find(allowed_values.begin(), allowed_values.end(), value) != allowed_values.end();\n}\n\nnamespace std_string {\ntemplate <typename... Args>\ninline std::string format(const char *format, Args... args) {\n    size_t size = snprintf(nullptr, 0, format, args...) + 1;  // Extra space for '\\0'\n    std::unique_ptr<char[]> buf(new char[size]);\n    snprintf(buf.get(), size, format, args...);\n    return {buf.get(), buf.get() + size - 1};  // We don't want the '\\0' inside\n}\n\ninline std::string vformat(const char *format, va_list args) {\n    va_list _args;\n    va_copy(_args, args);\n    size_t size = vsnprintf(nullptr, 0, format, _args) + 1;  // Extra space for '\\0'\n    va_end(_args);\n    std::unique_ptr<char[]> buf(new char[size]);\n    vsnprintf(buf.get(), size, format, args);\n    return {buf.get(), buf.get() + size - 1};  // We don't want the '\\0' inside\n}\n}  // namespace std_string\n\n// Keep parameter 'steady' as false for backward compatibility.\ntemplate <typename T>\nstatic inline long time(bool steady = false) {\n    if (sw_likely(steady)) {\n        auto now = std::chrono::steady_clock::now();\n        return std::chrono::duration_cast<T>(now.time_since_epoch()).count();\n    } else {\n        auto now = std::chrono::system_clock::now();\n        return std::chrono::duration_cast<T>(now.time_since_epoch()).count();\n    }\n}\n\nstatic inline long get_timezone() {\n    using namespace std::chrono;\n    auto now_time_t = system_clock::to_time_t(system_clock::now());\n    std::tm local_tm{};\n    localtime_r(&now_time_t, &local_tm);\n    return local_tm.tm_gmtoff;\n}\n\nclass DeferTask {\n    std::stack<Callback> list_;\n\n  public:\n    void add(const Callback &fn) {\n        list_.push(fn);\n    }\n\n    ~DeferTask() {\n        while (!list_.empty()) {\n            auto fn = list_.top();\n            fn(nullptr);\n            list_.pop();\n        }\n    }\n};\n\ntemplate <typename Fun>\nclass ScopeGuard {\n  public:\n    explicit ScopeGuard(Fun &&f) : _fun(std::forward<Fun>(f)), _active(true) {}\n\n    ~ScopeGuard() {\n        if (_active) {\n            _fun();\n        }\n    }\n\n    void dismiss() {\n        _active = false;\n    }\n\n    ScopeGuard() = delete;\n    ScopeGuard(const ScopeGuard &) = delete;\n    ScopeGuard &operator=(const ScopeGuard &) = delete;\n\n    ScopeGuard(ScopeGuard &&rhs) noexcept : _fun(std::move(rhs._fun)), _active(rhs._active) {\n        rhs.dismiss();\n    }\n\n  private:\n    Fun _fun;\n    bool _active;\n};\n\nclass BitMap {\n    uint64_t *array_;\n    size_t n_bits_;\n\n    static size_t get_array_size(size_t n_bits) {\n        return (((n_bits) + 63) / 64 * 8);\n    }\n\n    size_t get_offset(size_t i) const {\n        assert(i < n_bits_);\n        /* (i / 64) */\n        return i >> 6;\n    }\n\n    static uint64_t to_int(const size_t i, const size_t offset) {\n        return static_cast<uint64_t>(1) << (i - (offset << 6));\n    }\n\n  public:\n    explicit BitMap(const size_t n_bits) {\n        assert(n_bits > 0);\n        array_ = new uint64_t[get_array_size(n_bits)];\n        n_bits_ = n_bits;\n    }\n\n    ~BitMap() {\n        delete[] array_;\n    }\n\n    void clear() const {\n        memset(array_, 0, sizeof(uint64_t) * get_array_size(n_bits_));\n    }\n\n    void set(const size_t i) const {\n        const size_t off = get_offset(i);\n        array_[off] |= to_int(i, off);\n    }\n\n    void unset(const size_t i) const {\n        const size_t off = get_offset(i);\n        array_[off] &= ~to_int(i, off);\n    }\n\n    bool get(const size_t i) const {\n        const size_t off = get_offset(i);\n        return array_[off] & to_int(i, off);\n    }\n};\n\nnamespace detail {\nenum class ScopeGuardOnExit {};\n\ntemplate <typename Fun>\ninline ScopeGuard<Fun> operator+(ScopeGuardOnExit, Fun &&fn) {\n    return ScopeGuard<Fun>(std::forward<Fun>(fn));\n}\n}  // namespace detail\n\n#define __SCOPE_GUARD_CONCATENATE_IMPL(s1, s2) s1##s2\n#define __SCOPE_GUARD_CONCATENATE(s1, s2) __SCOPE_GUARD_CONCATENATE_IMPL(s1, s2)\n\n/**\n * Call the specified function when exiting the scope, similar to Golang's defer function.\n * After using this helper macro,\n * it is not necessary to manually release resources before the return statement of the failed branch.\n */\n#define ON_SCOPE_EXIT                                                                                                  \\\n    auto __SCOPE_GUARD_CONCATENATE(ext_exitBlock_, __LINE__) = swoole::detail::ScopeGuardOnExit() + [&]()\n\nstd::string intersection(const std::vector<std::string> &vec1, std::set<std::string> &vec2);\n\nstatic inline size_t ltrim(char **str, size_t len) {\n    size_t i;\n    for (i = 0; i < len; ++i) {\n        if ('\\0' != **str && isspace(**str)) {\n            ++*str;\n        } else {\n            break;\n        }\n    }\n    return len - i;\n}\n\nstatic inline size_t rtrim(char *str, size_t len) {\n    for (size_t i = len; i > 0;) {\n        if (isspace(str[--i])) {\n            str[i] = 0;\n            len--;\n        } else {\n            break;\n        }\n    }\n    return len;\n}\n\nstatic inline size_t rtrim(const char *str, size_t len) {\n    for (size_t i = len; i > 0;) {\n        if (isspace(str[--i])) {\n            len--;\n        } else {\n            break;\n        }\n    }\n    return len;\n}\n\nstatic inline ssize_t substr_len(const char *str, size_t len, char separator, bool before = false) {\n    const auto substr = static_cast<const char *>(memchr(str, separator, len));\n    if (substr == nullptr) {\n        return -1;\n    }\n    return before ? substr - str : str + len - substr - 1;\n}\n\nstatic inline bool starts_with(const char *haystack, size_t l_haystack, const char *needle, size_t l_needle) {\n    if (l_needle > l_haystack) {\n        return false;\n    }\n    return memcmp(haystack, needle, l_needle) == 0;\n}\n\nstatic inline bool starts_with(const std::string &str, const std::string &prefix) {\n    if (prefix.size() > str.size()) {\n        return false;\n    }\n    return std::equal(prefix.begin(), prefix.end(), str.begin());\n}\n\nstatic inline bool ends_with(const char *haystack, size_t l_haystack, const char *needle, size_t l_needle) {\n    if (l_needle > l_haystack) {\n        return false;\n    }\n    return memcmp(haystack + l_haystack - l_needle, needle, l_needle) == 0;\n}\n}  // namespace swoole\n"
  },
  {
    "path": "include/swoole_version.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef SWOOLE_VERSION_H_\n#define SWOOLE_VERSION_H_\n\n#define SWOOLE_MAJOR_VERSION 6\n#define SWOOLE_MINOR_VERSION 2\n#define SWOOLE_RELEASE_VERSION 0\n#define SWOOLE_EXTRA_VERSION \"\"\n#define SWOOLE_VERSION \"6.2.0\"\n#define SWOOLE_VERSION_ID 60200\n#define SWOOLE_API_VERSION_ID 0x202208a\n\n#define SWOOLE_BUG_REPORT                                                                                              \\\n    \"A process crash occurred in Swoole-v\" SWOOLE_VERSION \". Please report this issue.\\n\"                              \\\n    \"You can refer to the documentation below, submit an issue to us on GitHub.\\n\"                                     \\\n    \">> https://github.com/swoole/swoole-src/blob/master/docs/ISSUE.md\\n\"\n#endif\n"
  },
  {
    "path": "include/swoole_websocket.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#pragma once\n\n#include \"swoole_http.h\"\n\n#define SW_WEBSOCKET_SEC_KEY_LEN 16\n#define SW_WEBSOCKET_GUID \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\"\n#define SW_WEBSOCKET_HEADER_LEN 2\n#define SW_WEBSOCKET_MASK_LEN 4\n#define SW_WEBSOCKET_MASK_DATA \"258E\"\n#define SW_WEBSOCKET_EXT16_MAX_LEN 0xFFFF\n#define SW_WEBSOCKET_EXT16_LENGTH 0x7E\n#define SW_WEBSOCKET_EXT64_LENGTH 0x7F\n#define SW_WEBSOCKET_CLOSE_CODE_LEN 2\n#define SW_WEBSOCKET_CLOSE_REASON_MAX_LEN 125\n#define SW_WEBSOCKET_OPCODE_MAX swoole::websocket::OPCODE_PONG\n#define SW_WEBSOCKET_FRAME_HEADER_SIZE (SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_MASK_LEN + sizeof(uint64_t))\n#define SW_WEBSOCKET_DEFAULT_PAYLOAD_SIZE 1024\n\nnamespace swoole {\nnamespace websocket {\n\nenum Status {\n    STATUS_NONE = 0,\n    STATUS_CONNECTION = 1,\n    STATUS_HANDSHAKE = 2,\n    STATUS_ACTIVE = 3,\n    STATUS_CLOSING = 4,\n    STATUS_HANDSHAKE_FAILED = 5,\n};\n\nenum Flag {\n    FLAG_FIN = 1 << 0, /* BC: must be 1 */\n    FLAG_COMPRESS = 1 << 1,\n    // readonly for user\n    FLAG_RSV1 = 1 << 2,\n    FLAG_RSV2 = 1 << 3,\n    FLAG_RSV3 = 1 << 4,\n    FLAG_MASK = 1 << 5,\n    // for encoder/decoder\n    FLAG_ENCODE_HEADER_ONLY = 1 << 6,\n    FLAGS_ALL = /* used to prevent overflow  */\n    FLAG_FIN | FLAG_RSV1 | FLAG_RSV2 | FLAG_RSV3 | FLAG_MASK | FLAG_COMPRESS\n};\n\nstruct Header {\n    /**\n     * fin:1 rsv1:1 rsv2:1 rsv3:1 opcode:4\n     */\n    uchar OPCODE : 4;\n    uchar RSV3 : 1;\n    uchar RSV2 : 1;\n    uchar RSV1 : 1;\n    uchar FIN : 1;\n    uchar LENGTH : 7;\n    uchar MASK : 1;\n};\n\nstruct Frame {\n    Header header;\n    char mask_key[SW_WEBSOCKET_MASK_LEN];\n    uint16_t header_length;\n    size_t payload_length;\n    char *payload;\n\n    uchar get_flags() const {\n        uchar flags = 0;\n        if (header.FIN) {\n            flags |= FLAG_FIN;\n        }\n        if (header.RSV1) {\n            flags |= FLAG_RSV1;\n        }\n        if (header.RSV2) {\n            flags |= FLAG_RSV2;\n        }\n        if (header.RSV3) {\n            flags |= FLAG_RSV3;\n        }\n        if (header.MASK) {\n            flags |= FLAG_MASK;\n        }\n        return flags;\n    }\n\n    bool compressed() const {\n        return header.RSV1;\n    }\n};\n\n#define WEBSOCKET_VERSION 13\n\nenum Opcode {\n    OPCODE_CONTINUATION = 0x0,\n    OPCODE_TEXT = 0x1,\n    OPCODE_BINARY = 0x2,\n    OPCODE_CLOSE = 0x8,\n    OPCODE_PING = 0x9,\n    OPCODE_PONG = 0xa,\n};\n\nenum CloseReason {\n    CLOSE_NORMAL = 1000,\n    CLOSE_GOING_AWAY = 1001,\n    CLOSE_PROTOCOL_ERROR = 1002,\n    CLOSE_DATA_ERROR = 1003,\n    CLOSE_STATUS_ERROR = 1005,\n    CLOSE_ABNORMAL = 1006,\n    CLOSE_MESSAGE_ERROR = 1007,\n    CLOSE_POLICY_ERROR = 1008,\n    CLOSE_MESSAGE_TOO_BIG = 1009,\n    CLOSE_EXTENSION_MISSING = 1010,\n    CLOSE_SERVER_ERROR = 1011,\n    CLOSE_SERVICE_RESTART = 1012,\n    CLOSE_TRY_AGAIN_LATER = 1013,\n    CLOSE_BAD_GATEWAY = 1014,\n    CLOSE_TLS = 1015,\n};\n\nstatic inline uint16_t get_ext_flags(uchar opcode, uchar flags) {\n    uint16_t ext_flags = opcode;\n    ext_flags = ext_flags << 8;\n    ext_flags += flags;\n    return ext_flags;\n}\n\nstatic inline uchar set_flags(uchar fin, uchar mask, uchar rsv1, uchar rsv2, uchar rsv3) {\n    uchar flags = 0;\n    if (fin) {\n        flags |= FLAG_FIN;\n    }\n    if (mask) {\n        flags |= FLAG_MASK;\n    }\n    if (rsv1) {\n        flags |= FLAG_RSV1;\n    }\n    if (rsv2) {\n        flags |= FLAG_RSV2;\n    }\n    if (rsv3) {\n        flags |= FLAG_RSV3;\n    }\n    return flags;\n}\n\nbool encode(String *buffer, const char *data, size_t length, uint8_t opcode, uint8_t flags);\nbool decode(Frame *frame, char *data, size_t length);\nvoid mask(char *data, size_t len, const char *mask_key);\nbool pack_close_frame(String *buffer, int code, const char *reason, size_t length, uint8_t flags);\nvoid print_frame(const Frame *frame);\n\nstatic inline bool decode(Frame *frame, const String *str) {\n    return decode(frame, str->str, str->length);\n}\n\nstatic inline void parse_ext_flags(uint16_t ext_flags, uchar *opcode, uchar *flags) {\n    *opcode = (uchar) (ext_flags >> 8);\n    *flags = (uchar) (ext_flags & 0xFF);\n}\n\nssize_t get_package_length(const Protocol *protocol, network::Socket *conn, PacketLength *pl);\nint dispatch_frame(const Protocol *protocol, network::Socket *conn, const RecvData *rdata);\n\n}  // namespace websocket\n}  // namespace swoole\n"
  },
  {
    "path": "package.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<package xmlns=\"http://pear.php.net/dtd/package-2.0\" xmlns:tasks=\"http://pear.php.net/dtd/tasks-1.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" packagerversion=\"1.9.4\" version=\"2.0\" xsi:schemaLocation=\"http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd\">\n    <name>swoole</name>\n    <channel>pecl.php.net</channel>\n    <summary>Event-driven asynchronous and concurrent networking engine with high performance for PHP.</summary>\n    <description>\n        Event-driven asynchronous and concurrent networking engine with high performance for PHP.\n        - event-driven\n        - coroutine\n        - asynchronous non-blocking\n        - multi-thread reactor\n        - multi-process worker\n        - multi-protocol\n        - millisecond timer\n        - built-in tcp/http/websocket/http2 server\n        - coroutine tcp/http/websocket client\n        - coroutine read/write file system\n        - coroutine dns lookup\n        - support IPv4/IPv6/UnixSocket/TCP/UDP\n        - support SSL/TLS encrypted transmission\n    </description>\n    <lead>\n        <name>Tianfeng Han</name>\n        <user>tianfenghan</user>\n        <email>rango@swoole.com</email>\n        <active>yes</active>\n    </lead>\n    <developer>\n        <name>Twosee</name>\n        <user>twosee</user>\n        <email>twosee@php.net</email>\n        <active>yes</active>\n    </developer>\n    <developer>\n        <name>Shen Zhe</name>\n        <user>shenzhe</user>\n        <email>shenzhe163@gmail.com</email>\n        <active>no</active>\n    </developer>\n    <developer>\n        <name>Lu Fei</name>\n        <user>lufei</user>\n        <email>lufei@php.net</email>\n        <active>yes</active>\n    </developer>\n    <developer>\n        <name>Bruce Dou</name>\n        <user>doubaokun</user>\n        <email>doubaokun@php.net</email>\n        <active>yes</active>\n    </developer>\n    <date>2026-03-10</date>\n    <time>22:00:00</time>\n    <version>\n        <release>6.2.0</release>\n        <api>6.0</api>\n    </version>\n    <stability>\n        <release>stable</release>\n        <api>stable</api>\n    </stability>\n    <license uri=\"http://www.apache.org/licenses/LICENSE-2.0.html\">Apache2.0</license>\n    <notes>\n        - Added coroutine-based `FTP` client. By including the `--enable-swoole-ftp` option during compilation, coroutine support for `FTP` operations can be enabled to avoid network blocking.\n        - Added coroutine-based `SSH` client. By including the `--enable-swoole-ssh` option during compilation, coroutine support for `SSH` operations can be enabled to improve concurrency efficiency.\n        - Added support for `io_uring` in the `HTTP` coroutine server. The `HTTP` coroutine server can now utilize the high-performance `io_uring` event mechanism. Enable it by adding the `--enable-uring_socket` option during compilation for better I/O performance.\n        - Added the `Swoole\\RemoteObject\\Server` module, providing transparent coroutine operation support for `MongoDB`.\n        - Added the `Swoole\\Coroutine::setTimeLimit()` function to control coroutine execution timeout, preventing coroutines from occupying resources for extended periods.\n        - Added `URL` rewriting support for the `HTTP` static file server.\n        - Added coroutine support for `pdo_firebird`.\n        - Added support for `PHP 8.5`.\n        - Added coroutine support for the `gethostbyname` function.\n        - Swoole\\Coroutine::cancel now supports canceling iouring operations.\n        - Optimized the implementation of `Server::shutdown()`, replacing signal communication with pipe communication in `Process` mode.\n        - In the callback functions of Swoole's HTTP servers (including Swoole\\Http\\Server, Swoole\\Http2\\Server, and Swoole\\Coroutine\\Http\\Server), the `server` property of the `Swoole\\Http\\Request` object now includes a new `server_addr` field, which identifies the server's IP address.\n        - `SSH` and `FTP` coroutine clients cannot coexist with PHP's `ext-ssh` and `ext-ftp`. To enable these features, the PHP `ssh` and `ftp` extensions must be disabled.\n        - Fixed an issue where the HTTP2 server session was released multiple times in a multi-threaded environment.\n        - Fixed an issue in version `8.5` where `refresh_memory_manager()` must be executed after forking a child process or creating a child thread; otherwise, the program would crash.\n        - Fixed an issue where the `swoole_get_local_mac` function did not work correctly on `macOS` systems.\n        - Fixed a potential crash issue that could occur when users manually suspended coroutines in a multi-threaded environment.\n        - Fixed compilation errors on Alpine systems.\n        - Fixed a memory leak issue when function hooking failed.\n        - Fixed thread safety issues during the coroutinization process of `pdo_sqlite` and `pdo_oci`.\n        - Fixed the `sw_php_print_backtrace` function to ensure its output format matches the backtrace information format of PHP's built-in functions.\n        - Fixed compilation failures caused by enabling --enable-uring-socket, --enable-trace-log, and --enable-error-log.\n        - Fixed the issue where `pdo_firebird` could not be compiled due to undefined macros.\n        - Executing `php --ri swoole` now outputs version information for `libpq`.\n        - Optimized logic related to `io_uring` in file coroutine operations.\n        - Added PHP call stack information output when event addition fails.\n        - Optimized the implementation code for the `swoole_get_local_ip` and `swoole_get_local_mac` functions.\n        - Optimized the coroutine waiting time for file locks to prevent exponentially increasing sleep times from making locks increasingly difficult to acquire.\n        - Improved compatibility with the `Android` platform.\n        - Removed the `--enable-openssl` compilation option; support for `OpenSSL` is now included by default.\n        - `--enable-uring-socket` requires explicitly specifying either `--enable-iouring` or `--with-liburing-dir`.\n        - `PHP 8.1` is no longer supported.\n    </notes>\n    <contents>\n        <dir name=\"/\">\n            <file role=\"src\" name=\"CMakeLists.txt\" />\n            <file role=\"doc\" name=\"Dockerfile\" />\n            <file role=\"doc\" name=\"LICENSE\" />\n            <file role=\"src\" name=\"Makefile.frag\" />\n            <file role=\"doc\" name=\"README.md\" />\n            <file role=\"src\" name=\"codecov.yml\" />\n            <file role=\"src\" name=\"composer.json\" />\n            <file role=\"src\" name=\"config.m4\" />\n            <file role=\"src\" name=\"core-tests/.gitignore\" />\n            <file role=\"src\" name=\"core-tests/code-stats.sh\" />\n            <file role=\"src\" name=\"core-tests/docker-compose.yml\" />\n            <file role=\"src\" name=\"core-tests/fuzz/cases/req1.bin\" />\n            <file role=\"src\" name=\"core-tests/fuzz/cases/req2.bin\" />\n            <file role=\"src\" name=\"core-tests/fuzz/project.json\" />\n            <file role=\"src\" name=\"core-tests/fuzz/src/main.cpp\" />\n            <file role=\"src\" name=\"core-tests/include/httplib_client.h\" />\n            <file role=\"src\" name=\"core-tests/include/httplib_server.h\" />\n            <file role=\"src\" name=\"core-tests/include/redis_client.h\" />\n            <file role=\"src\" name=\"core-tests/include/test_core.h\" />\n            <file role=\"src\" name=\"core-tests/include/test_coroutine.h\" />\n            <file role=\"src\" name=\"core-tests/include/test_process.h\" />\n            <file role=\"src\" name=\"core-tests/include/test_server.h\" />\n            <file role=\"src\" name=\"core-tests/js/.gitignore\" />\n            <file role=\"src\" name=\"core-tests/js/mqtt.js\" />\n            <file role=\"src\" name=\"core-tests/js/package.json\" />\n            <file role=\"src\" name=\"core-tests/js/ws_1.js\" />\n            <file role=\"src\" name=\"core-tests/js/ws_2.js\" />\n            <file role=\"src\" name=\"core-tests/samples/CMakeLists.txt\" />\n            <file role=\"src\" name=\"core-tests/samples/src/s1.cc\" />\n            <file role=\"src\" name=\"core-tests/src/_lib/http.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/_lib/process.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/_lib/redis.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/_lib/server.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/_lib/ssl.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/core/base.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/core/channel.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/core/hash.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/core/heap.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/core/log.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/core/string.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/core/time.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/core/util.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/coroutine/accept.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/coroutine/async.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/coroutine/base.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/coroutine/channel.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/coroutine/file.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/coroutine/gethostbyname.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/coroutine/hook.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/coroutine/http_server.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/coroutine/iouring.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/coroutine/socket.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/coroutine/system.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/coroutine/uring_socket.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/lock/lock.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/main.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/memory/buffer.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/memory/fixed_pool.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/memory/global_memory.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/memory/lru_cache.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/memory/ringbuffer.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/memory/table.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/network/address.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/network/client.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/network/dns.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/network/socket.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/network/stream.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/os/async.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/os/file.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/os/msg_queue.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/os/os.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/os/pipe.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/os/process_pool.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/os/signal.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/os/timer.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/os/wait.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/protocol/base.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/protocol/base64.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/protocol/http2.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/protocol/mime_type.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/protocol/redis.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/protocol/ssl.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/reactor/base.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/server/buffer.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/server/http_parser.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/server/http_server.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/server/message_bus.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/server/mqtt.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/server/multipart_parser.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/server/port.cpp\" />\n            <file role=\"src\" name=\"core-tests/src/server/server.cpp\" />\n            <file role=\"doc\" name=\"docs/API.md\" />\n            <file role=\"doc\" name=\"docs/CHANGELOG.md\" />\n            <file role=\"doc\" name=\"docs/CODE-STYLE.md\" />\n            <file role=\"src\" name=\"docs/CPPLINT.cfg\" />\n            <file role=\"doc\" name=\"docs/ISSUE.md\" />\n            <file role=\"doc\" name=\"docs/SUPPORTED.md\" />\n            <file role=\"doc\" name=\"docs/TESTS.md\" />\n            <file role=\"doc\" name=\"docs/WORKFLOW-PARAMETERS.md\" />\n            <file role=\"src\" name=\"docs/google-style.xml\" />\n            <file role=\"doc\" name=\"docs/sponsors.md\" />\n            <file role=\"src\" name=\"docs/swoole-logo.svg\" />\n            <file role=\"doc\" name=\"examples/atomic/long.php\" />\n            <file role=\"doc\" name=\"examples/atomic/test.php\" />\n            <file role=\"doc\" name=\"examples/atomic/wait.php\" />\n            <file role=\"doc\" name=\"examples/client/c10k.php\" />\n            <file role=\"doc\" name=\"examples/client/get_socket.php\" />\n            <file role=\"doc\" name=\"examples/client/long_tcp.php\" />\n            <file role=\"doc\" name=\"examples/client/recv_1m.php\" />\n            <file role=\"doc\" name=\"examples/client/recv_file.php\" />\n            <file role=\"doc\" name=\"examples/client/select.php\" />\n            <file role=\"doc\" name=\"examples/client/simple.php\" />\n            <file role=\"doc\" name=\"examples/client/sync.php\" />\n            <file role=\"doc\" name=\"examples/client/test.txt\" />\n            <file role=\"doc\" name=\"examples/client/udp_sync.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/backtrace.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/before_server_start.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/behavior/do-while.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/behavior/for.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/behavior/foreach.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/behavior/goto.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/behavior/preemptive_timer.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/behavior/tick.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/behavior/while.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/behavior/while2.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/cancel_throw.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/channel/test.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/client_send_yield.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/client_send_yield_server.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/coro_array_map.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/coro_call_user.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/coro_channel.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/coro_destruct.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/coro_destuct.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/coro_empty.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/coro_gethost.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/coro_include.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/coro_invoke.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/coro_nested.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/coro_nested_empty.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/coro_serialize.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/coro_set_stack_size.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/coro_sleep.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/coro_stackless.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/coro_util.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/csp.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/deadlock.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/defer.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/defer_client.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/enable_coroutine.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/exception/empty.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/exec.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/exit_in_coroutine.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/exit_with_status.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/fgets.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/fread.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/fwrite.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/gethostbyname.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/http/server.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/http/write_func.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/http2_client.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/http_backend_serv.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/http_client.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/http_download.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/http_server.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/httpmulti.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/join.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/library/base.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/list_coroutines.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/mysql_chan.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/mysql_escape.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/mysql_execute_empty.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/mysql_prepare.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/mysql_prepare_2.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/mysql_procedure_exec.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/mysql_query.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/mysql_unixsocket.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/pdo/pdo_persistent.phpt\" />\n            <file role=\"doc\" name=\"examples/coroutine/proc_open.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/reconnect_test.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/redis/auth.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/redis/defer.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/redis/eval.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/redis/get.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/redis/multi.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/redis/pipeline.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/redis/pub.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/redis/request.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/redis/serialize.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/redis/sub.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/redis_pool.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/redis_subscribe.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/scheduler.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/select/1.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/select/2.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/select/3.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/select/4.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/select/5.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/select/6.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/select/7.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/select/8.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/select/9.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/select/poptimeout.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/select/test.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/send_yield.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/send_yield_client.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/server/tcp.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/sleep.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/socket/accept.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/socket/sendto.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/stack.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/stack/1.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/stack/2.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/statvfs.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/task_co.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/tcp_backend_serv.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/tcp_echo.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/test.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/timer_test.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/udp_client.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/udp_tcp_timeout.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/user_coroutine.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/util/resume001.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/util/resume002.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/util/resume003.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/waitgroup.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/websocket/client.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/websocket/co_server.php\" />\n            <file role=\"doc\" name=\"examples/coroutine/websocket/server.php\" />\n            <file role=\"doc\" name=\"examples/cpp/Makefile\" />\n            <file role=\"doc\" name=\"examples/cpp/co.cc\" />\n            <file role=\"doc\" name=\"examples/cpp/repeat.cc\" />\n            <file role=\"doc\" name=\"examples/cpp/test_server.cc\" />\n            <file role=\"doc\" name=\"examples/curl/hook.php\" />\n            <file role=\"doc\" name=\"examples/curl/multi.php\" />\n            <file role=\"doc\" name=\"examples/curl/server.php\" />\n            <file role=\"doc\" name=\"examples/dtls/client.php\" />\n            <file role=\"doc\" name=\"examples/dtls/server.php\" />\n            <file role=\"doc\" name=\"examples/eof/client.php\" />\n            <file role=\"doc\" name=\"examples/eof/server.php\" />\n            <file role=\"doc\" name=\"examples/event/cycle.php\" />\n            <file role=\"doc\" name=\"examples/event/inotify.php\" />\n            <file role=\"doc\" name=\"examples/event/sockets.php\" />\n            <file role=\"doc\" name=\"examples/event/stdin.php\" />\n            <file role=\"doc\" name=\"examples/event/stream.php\" />\n            <file role=\"doc\" name=\"examples/event/test.php\" />\n            <file role=\"doc\" name=\"examples/http/UPPER.TXT\" />\n            <file role=\"doc\" name=\"examples/http/client.php\" />\n            <file role=\"doc\" name=\"examples/http/curl.php\" />\n            <file role=\"doc\" name=\"examples/http/detach.php\" />\n            <file role=\"doc\" name=\"examples/http/download.php\" />\n            <file role=\"doc\" name=\"examples/http/empty.txt\" />\n            <file role=\"doc\" name=\"examples/http/event-stream.php\" />\n            <file role=\"doc\" name=\"examples/http/moc.moc\" />\n            <file role=\"doc\" name=\"examples/http/no-compression.php\" />\n            <file role=\"doc\" name=\"examples/http/post.data\" />\n            <file role=\"doc\" name=\"examples/http/raw.php\" />\n            <file role=\"doc\" name=\"examples/http/redirect.php\" />\n            <file role=\"doc\" name=\"examples/http/server.php\" />\n            <file role=\"doc\" name=\"examples/http/static_handler.php\" />\n            <file role=\"doc\" name=\"examples/http/test.txt\" />\n            <file role=\"doc\" name=\"examples/http/url_rewrite.php\" />\n            <file role=\"doc\" name=\"examples/http2/server.php\" />\n            <file role=\"doc\" name=\"examples/http2/streaming.php\" />\n            <file role=\"doc\" name=\"examples/http2/test.html\" />\n            <file role=\"doc\" name=\"examples/ipv6/tcp_client.php\" />\n            <file role=\"doc\" name=\"examples/ipv6/tcp_server.php\" />\n            <file role=\"doc\" name=\"examples/ipv6/udp_client.php\" />\n            <file role=\"doc\" name=\"examples/ipv6/udp_server.php\" />\n            <file role=\"doc\" name=\"examples/length/client.php\" />\n            <file role=\"doc\" name=\"examples/length/config.php\" />\n            <file role=\"doc\" name=\"examples/length/func.php\" />\n            <file role=\"doc\" name=\"examples/length/server.php\" />\n            <file role=\"doc\" name=\"examples/lock/lock.php\" />\n            <file role=\"doc\" name=\"examples/misc/get_local_ip.php\" />\n            <file role=\"doc\" name=\"examples/misc/get_local_mac.php\" />\n            <file role=\"doc\" name=\"examples/misc/version.php\" />\n            <file role=\"doc\" name=\"examples/multicast/client.php\" />\n            <file role=\"doc\" name=\"examples/multicast/server.php\" />\n            <file role=\"doc\" name=\"examples/php/buf_size.php\" />\n            <file role=\"doc\" name=\"examples/php/debug_server.php\" />\n            <file role=\"doc\" name=\"examples/php/error.php\" />\n            <file role=\"doc\" name=\"examples/php/exception.php\" />\n            <file role=\"doc\" name=\"examples/php/func.php\" />\n            <file role=\"doc\" name=\"examples/php/inotify.php\" />\n            <file role=\"doc\" name=\"examples/php/proc.php\" />\n            <file role=\"doc\" name=\"examples/php/socket_client.php\" />\n            <file role=\"doc\" name=\"examples/php/socket_server.php\" />\n            <file role=\"doc\" name=\"examples/php/stream_client.php\" />\n            <file role=\"doc\" name=\"examples/php/stream_server.php\" />\n            <file role=\"doc\" name=\"examples/php/tick.php\" />\n            <file role=\"doc\" name=\"examples/postgresql/postgresql_coro.php\" />\n            <file role=\"doc\" name=\"examples/process/alarm.php\" />\n            <file role=\"doc\" name=\"examples/process/async_master.php\" />\n            <file role=\"doc\" name=\"examples/process/client.php\" />\n            <file role=\"doc\" name=\"examples/process/client3.php\" />\n            <file role=\"doc\" name=\"examples/process/close.php\" />\n            <file role=\"doc\" name=\"examples/process/daemon.php\" />\n            <file role=\"doc\" name=\"examples/process/echo.py\" />\n            <file role=\"doc\" name=\"examples/process/exec.php\" />\n            <file role=\"doc\" name=\"examples/process/func_timeout.php\" />\n            <file role=\"doc\" name=\"examples/process/msg_pop.php\" />\n            <file role=\"doc\" name=\"examples/process/msg_push.php\" />\n            <file role=\"doc\" name=\"examples/process/msgqueue.php\" />\n            <file role=\"doc\" name=\"examples/process/msgqueue2.php\" />\n            <file role=\"doc\" name=\"examples/process/msgqueue_client.php\" />\n            <file role=\"doc\" name=\"examples/process/msgqueue_pool.php\" />\n            <file role=\"doc\" name=\"examples/process/pool_socket.php\" />\n            <file role=\"doc\" name=\"examples/process/python.php\" />\n            <file role=\"doc\" name=\"examples/process/select.php\" />\n            <file role=\"doc\" name=\"examples/process/set_cpu_affinity.php\" />\n            <file role=\"doc\" name=\"examples/process/stdin_stdout.php\" />\n            <file role=\"doc\" name=\"examples/process/test.php\" />\n            <file role=\"doc\" name=\"examples/process/worker.php\" />\n            <file role=\"doc\" name=\"examples/process_pool/detach.php\" />\n            <file role=\"doc\" name=\"examples/process_pool/send.php\" />\n            <file role=\"doc\" name=\"examples/runtime/coroutine.php\" />\n            <file role=\"doc\" name=\"examples/runtime/curl.php\" />\n            <file role=\"doc\" name=\"examples/runtime/file.php\" />\n            <file role=\"doc\" name=\"examples/runtime/gethostbyname.php\" />\n            <file role=\"doc\" name=\"examples/runtime/include.php\" />\n            <file role=\"doc\" name=\"examples/runtime/mkdir.php\" />\n            <file role=\"doc\" name=\"examples/runtime/mongodb.php\" />\n            <file role=\"doc\" name=\"examples/runtime/oci.php\" />\n            <file role=\"doc\" name=\"examples/runtime/odbc.php\" />\n            <file role=\"doc\" name=\"examples/runtime/read.php\" />\n            <file role=\"doc\" name=\"examples/runtime/rename.phpt\" />\n            <file role=\"doc\" name=\"examples/runtime/select.php\" />\n            <file role=\"doc\" name=\"examples/runtime/sleep.php\" />\n            <file role=\"doc\" name=\"examples/runtime/sqlite.php\" />\n            <file role=\"doc\" name=\"examples/runtime/ssl.php\" />\n            <file role=\"doc\" name=\"examples/runtime/stream.php\" />\n            <file role=\"doc\" name=\"examples/runtime/sync.php\" />\n            <file role=\"doc\" name=\"examples/runtime/time_sleep_until.php\" />\n            <file role=\"doc\" name=\"examples/runtime/unlink.phpt\" />\n            <file role=\"doc\" name=\"examples/server/db_pool.php\" />\n            <file role=\"doc\" name=\"examples/server/dispatch_func.php\" />\n            <file role=\"doc\" name=\"examples/server/dispatch_stream.php\" />\n            <file role=\"doc\" name=\"examples/server/echo.php\" />\n            <file role=\"doc\" name=\"examples/server/eof_client.php\" />\n            <file role=\"doc\" name=\"examples/server/eof_server.php\" />\n            <file role=\"doc\" name=\"examples/server/getReceivedTime.php\" />\n            <file role=\"doc\" name=\"examples/server/host_update.php\" />\n            <file role=\"doc\" name=\"examples/server/hot_update_class.php\" />\n            <file role=\"doc\" name=\"examples/server/ip_dispatch.php\" />\n            <file role=\"doc\" name=\"examples/server/length_client.php\" />\n            <file role=\"doc\" name=\"examples/server/length_server.php\" />\n            <file role=\"doc\" name=\"examples/server/listen_1k_port.php\" />\n            <file role=\"doc\" name=\"examples/server/local_listener.php\" />\n            <file role=\"doc\" name=\"examples/server/manager_timer.php\" />\n            <file role=\"doc\" name=\"examples/server/mixed.php\" />\n            <file role=\"doc\" name=\"examples/server/multi_instance.php\" />\n            <file role=\"doc\" name=\"examples/server/multi_port.php\" />\n            <file role=\"doc\" name=\"examples/server/pipe_message.php\" />\n            <file role=\"doc\" name=\"examples/server/proxy_sync.php\" />\n            <file role=\"doc\" name=\"examples/server/redis_pool.php\" />\n            <file role=\"doc\" name=\"examples/server/reload_aysnc.php\" />\n            <file role=\"doc\" name=\"examples/server/reload_force.php\" />\n            <file role=\"doc\" name=\"examples/server/reload_force2.php\" />\n            <file role=\"doc\" name=\"examples/server/send_1m.php\" />\n            <file role=\"doc\" name=\"examples/server/sendfile.php\" />\n            <file role=\"doc\" name=\"examples/server/single.php\" />\n            <file role=\"doc\" name=\"examples/server/tcp_client.php\" />\n            <file role=\"doc\" name=\"examples/server/tcp_server.php\" />\n            <file role=\"doc\" name=\"examples/server/trace.php\" />\n            <file role=\"doc\" name=\"examples/server/uid_dispatch.php\" />\n            <file role=\"doc\" name=\"examples/server/unix_stream.php\" />\n            <file role=\"doc\" name=\"examples/server/zmq.php\" />\n            <file role=\"doc\" name=\"examples/socket_coro/client.php\" />\n            <file role=\"doc\" name=\"examples/socket_coro/server.php\" />\n            <file role=\"doc\" name=\"examples/socket_coro/udp.php\" />\n            <file role=\"doc\" name=\"examples/ssl/ca/ca-cert.pem\" />\n            <file role=\"doc\" name=\"examples/ssl/ca/ca-key.pem\" />\n            <file role=\"doc\" name=\"examples/ssl/ca/ca-req.csr\" />\n            <file role=\"doc\" name=\"examples/ssl/ca/client-cert.pem\" />\n            <file role=\"doc\" name=\"examples/ssl/ca/client-key.pem\" />\n            <file role=\"doc\" name=\"examples/ssl/ca/client-req.csr\" />\n            <file role=\"doc\" name=\"examples/ssl/ca/client.crt\" />\n            <file role=\"doc\" name=\"examples/ssl/ca/client.key\" />\n            <file role=\"doc\" name=\"examples/ssl/ca/server-cert.pem\" />\n            <file role=\"doc\" name=\"examples/ssl/ca/server-key.pem\" />\n            <file role=\"doc\" name=\"examples/ssl/ca/server-req.csr\" />\n            <file role=\"doc\" name=\"examples/ssl/client.c\" />\n            <file role=\"doc\" name=\"examples/ssl/client.php\" />\n            <file role=\"doc\" name=\"examples/ssl/co_client.php\" />\n            <file role=\"doc\" name=\"examples/ssl/gen_cert.md\" />\n            <file role=\"doc\" name=\"examples/ssl/http_client.php\" />\n            <file role=\"doc\" name=\"examples/ssl/passphrase.php\" />\n            <file role=\"doc\" name=\"examples/ssl/server.php\" />\n            <file role=\"doc\" name=\"examples/ssl/ssl.crt\" />\n            <file role=\"doc\" name=\"examples/ssl/ssl.csr\" />\n            <file role=\"doc\" name=\"examples/ssl/ssl.key\" />\n            <file role=\"doc\" name=\"examples/ssl/ssl_passwd/ssl.crt\" />\n            <file role=\"doc\" name=\"examples/ssl/ssl_passwd/ssl.csr\" />\n            <file role=\"doc\" name=\"examples/ssl/ssl_passwd/ssl.key\" />\n            <file role=\"doc\" name=\"examples/ssl/stream_client.php\" />\n            <file role=\"doc\" name=\"examples/ssl/swoole.log\" />\n            <file role=\"doc\" name=\"examples/ssl/webserver.php\" />\n            <file role=\"doc\" name=\"examples/ssl/websocket_client.html\" />\n            <file role=\"doc\" name=\"examples/ssl/websocket_server.php\" />\n            <file role=\"doc\" name=\"examples/stdext/array.php\" />\n            <file role=\"doc\" name=\"examples/stdext/foreach.php\" />\n            <file role=\"doc\" name=\"examples/stdext/ref.php\" />\n            <file role=\"doc\" name=\"examples/stdext/string.php\" />\n            <file role=\"doc\" name=\"examples/stdext/typed_array.php\" />\n            <file role=\"doc\" name=\"examples/stdext/typed_array_map.php\" />\n            <file role=\"doc\" name=\"examples/table/deadlock.php\" />\n            <file role=\"doc\" name=\"examples/table/iterator.php\" />\n            <file role=\"doc\" name=\"examples/table/server.php\" />\n            <file role=\"doc\" name=\"examples/table/set.php\" />\n            <file role=\"doc\" name=\"examples/table/simulation.php\" />\n            <file role=\"doc\" name=\"examples/table/usage.php\" />\n            <file role=\"doc\" name=\"examples/task/http.php\" />\n            <file role=\"doc\" name=\"examples/task/msg_push.php\" />\n            <file role=\"doc\" name=\"examples/task/shared_client.php\" />\n            <file role=\"doc\" name=\"examples/task/shared_server.php\" />\n            <file role=\"doc\" name=\"examples/task/task.php\" />\n            <file role=\"doc\" name=\"examples/task/task_coro.php\" />\n            <file role=\"doc\" name=\"examples/task/task_num.php\" />\n            <file role=\"doc\" name=\"examples/task/task_queue.php\" />\n            <file role=\"doc\" name=\"examples/task/task_stream.php\" />\n            <file role=\"doc\" name=\"examples/test.jpg\" />\n            <file role=\"doc\" name=\"examples/thread/aio.php\" />\n            <file role=\"doc\" name=\"examples/thread/argv.php\" />\n            <file role=\"doc\" name=\"examples/thread/array.php\" />\n            <file role=\"doc\" name=\"examples/thread/atomic.php\" />\n            <file role=\"doc\" name=\"examples/thread/benchmark.php\" />\n            <file role=\"doc\" name=\"examples/thread/co.php\" />\n            <file role=\"doc\" name=\"examples/thread/exit.php\" />\n            <file role=\"doc\" name=\"examples/thread/hook.php\" />\n            <file role=\"doc\" name=\"examples/thread/lock.php\" />\n            <file role=\"doc\" name=\"examples/thread/map.php\" />\n            <file role=\"doc\" name=\"examples/thread/mt.php\" />\n            <file role=\"doc\" name=\"examples/thread/nested_map.php\" />\n            <file role=\"doc\" name=\"examples/thread/pipe.php\" />\n            <file role=\"doc\" name=\"examples/thread/run_test.php\" />\n            <file role=\"doc\" name=\"examples/thread/server.php\" />\n            <file role=\"doc\" name=\"examples/thread/signal.php\" />\n            <file role=\"doc\" name=\"examples/thread/socket.php\" />\n            <file role=\"doc\" name=\"examples/thread/test.php\" />\n            <file role=\"doc\" name=\"examples/thread/thread_pool.php\" />\n            <file role=\"doc\" name=\"examples/thread/thread_server.php\" />\n            <file role=\"doc\" name=\"examples/timer/after.php\" />\n            <file role=\"doc\" name=\"examples/timer/clear.php\" />\n            <file role=\"doc\" name=\"examples/timer/enable_coroutine.php\" />\n            <file role=\"doc\" name=\"examples/timer/tick.php\" />\n            <file role=\"doc\" name=\"examples/tracer/blocking.php\" />\n            <file role=\"doc\" name=\"examples/tracer/cli.php\" />\n            <file role=\"doc\" name=\"examples/tracer/co.php\" />\n            <file role=\"doc\" name=\"examples/tracer/co_server.php\" />\n            <file role=\"doc\" name=\"examples/tracer/co_socket.php\" />\n            <file role=\"doc\" name=\"examples/tracer/fn_call.php\" />\n            <file role=\"doc\" name=\"examples/tracer/gc.php\" />\n            <file role=\"doc\" name=\"examples/udp/client.php\" />\n            <file role=\"doc\" name=\"examples/udp/server.php\" />\n            <file role=\"doc\" name=\"examples/unixsock/dgram_client.php\" />\n            <file role=\"doc\" name=\"examples/unixsock/dgram_server.php\" />\n            <file role=\"doc\" name=\"examples/unixsock/stream_client.php\" />\n            <file role=\"doc\" name=\"examples/unixsock/stream_server.php\" />\n            <file role=\"doc\" name=\"examples/websocket/client.html\" />\n            <file role=\"doc\" name=\"examples/websocket/client.php\" />\n            <file role=\"doc\" name=\"examples/websocket/server.php\" />\n            <file role=\"doc\" name=\"examples/www/dir1/file1.txt\" />\n            <file role=\"doc\" name=\"examples/www/dir1/file2.txt\" />\n            <file role=\"doc\" name=\"examples/www/dir2/file1.txt\" />\n            <file role=\"doc\" name=\"examples/www/dir2/file2.txt\" />\n            <file role=\"doc\" name=\"examples/www/dir2/index.txt\" />\n            <file role=\"doc\" name=\"examples/www/file1.txt\" />\n            <file role=\"doc\" name=\"examples/www/file2.txt\" />\n            <file role=\"doc\" name=\"examples/www/index.html\" />\n            <file role=\"doc\" name=\"examples/www/index.txt\" />\n            <file role=\"src\" name=\"ext-src/php_swoole.cc\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_call_stack.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_client.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_coroutine.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_coroutine_system.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_curl.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_cxx.cc\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_cxx.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_firebird.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_ftp_def.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_http.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_http_server.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_library.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_odbc.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_oracle.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_pgsql.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_private.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_process.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_server.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_sqlite.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_ssh2.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_ssh2_def.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_ssh2_hook.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_stdext.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_thread.h\" />\n            <file role=\"src\" name=\"ext-src/php_swoole_websocket.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_atomic.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_atomic_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_channel_coro.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_channel_coro_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_client.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_client_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_client_async.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_client_async_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_client_coro.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_client_coro_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_coroutine.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_coroutine_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_coroutine_lock.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_coroutine_lock_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_coroutine_scheduler.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_coroutine_scheduler_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_coroutine_system.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_coroutine_system_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_event.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_event_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_ex.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_ex_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_ftp.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_ftp_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_http2_client_coro.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_http2_client_coro_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_http_client_coro.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_http_client_coro_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_http_cookie.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_http_cookie_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_http_request.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_http_request_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_http_response.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_http_response_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_http_server_coro.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_http_server_coro_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_lock.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_lock_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_name_resolver.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_name_resolver_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_process.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_process_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_process_pool.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_process_pool_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_redis_server.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_redis_server_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_runtime.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_runtime_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_server.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_server_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_server_port.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_server_port_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_socket_coro.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_socket_coro_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_ssh2.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_ssh2_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_stdext.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_stdext_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_table.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_table_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_thread.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_thread_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_thread_arraylist.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_thread_arraylist_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_thread_atomic.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_thread_atomic_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_thread_barrier.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_thread_barrier_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_thread_lock.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_thread_lock_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_thread_map.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_thread_map_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_thread_queue.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_thread_queue_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_timer.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_timer_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_tracer.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_tracer_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_websocket.stub.php\" />\n            <file role=\"src\" name=\"ext-src/stubs/php_swoole_websocket_arginfo.h\" />\n            <file role=\"src\" name=\"ext-src/swoole_admin_server.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_async_coro.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_atomic.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_channel_coro.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_client.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_client_async.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_client_coro.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_coroutine.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_coroutine_lock.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_coroutine_scheduler.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_coroutine_system.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_curl.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_curl_interface.h\" />\n            <file role=\"src\" name=\"ext-src/swoole_event.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_firebird.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_http2_client_coro.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_http2_server.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_http_client_coro.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_http_cookie.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_http_request.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_http_response.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_http_server.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_http_server_coro.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_lock.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_name_resolver.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_odbc.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_oracle.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_pgsql.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_process.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_process_pool.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_redis_server.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_runtime.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_server.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_server_port.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_socket_coro.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_sqlite.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_stdext.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_table.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_thread.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_thread_arraylist.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_thread_atomic.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_thread_barrier.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_thread_lock.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_thread_map.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_thread_queue.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_timer.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_tracer.cc\" />\n            <file role=\"src\" name=\"ext-src/swoole_websocket_server.cc\" />\n            <file role=\"doc\" name=\"gdbinit\" />\n            <file role=\"src\" name=\"include/helper/kqueue.h\" />\n            <file role=\"src\" name=\"include/swoole.h\" />\n            <file role=\"src\" name=\"include/swoole_api.h\" />\n            <file role=\"src\" name=\"include/swoole_asm_context.h\" />\n            <file role=\"src\" name=\"include/swoole_async.h\" />\n            <file role=\"src\" name=\"include/swoole_atomic.h\" />\n            <file role=\"src\" name=\"include/swoole_base64.h\" />\n            <file role=\"src\" name=\"include/swoole_buffer.h\" />\n            <file role=\"src\" name=\"include/swoole_channel.h\" />\n            <file role=\"src\" name=\"include/swoole_client.h\" />\n            <file role=\"src\" name=\"include/swoole_config.h\" />\n            <file role=\"src\" name=\"include/swoole_coroutine.h\" />\n            <file role=\"src\" name=\"include/swoole_coroutine_api.h\" />\n            <file role=\"src\" name=\"include/swoole_coroutine_channel.h\" />\n            <file role=\"src\" name=\"include/swoole_coroutine_context.h\" />\n            <file role=\"src\" name=\"include/swoole_coroutine_socket.h\" />\n            <file role=\"src\" name=\"include/swoole_coroutine_system.h\" />\n            <file role=\"src\" name=\"include/swoole_dtls.h\" />\n            <file role=\"src\" name=\"include/swoole_error.h\" />\n            <file role=\"src\" name=\"include/swoole_file.h\" />\n            <file role=\"src\" name=\"include/swoole_file_hook.h\" />\n            <file role=\"src\" name=\"include/swoole_hash.h\" />\n            <file role=\"src\" name=\"include/swoole_heap.h\" />\n            <file role=\"src\" name=\"include/swoole_http.h\" />\n            <file role=\"src\" name=\"include/swoole_http2.h\" />\n            <file role=\"src\" name=\"include/swoole_iouring.h\" />\n            <file role=\"src\" name=\"include/swoole_llhttp.h\" />\n            <file role=\"src\" name=\"include/swoole_lock.h\" />\n            <file role=\"src\" name=\"include/swoole_log.h\" />\n            <file role=\"src\" name=\"include/swoole_lru_cache.h\" />\n            <file role=\"src\" name=\"include/swoole_memory.h\" />\n            <file role=\"src\" name=\"include/swoole_message_bus.h\" />\n            <file role=\"src\" name=\"include/swoole_mime_type.h\" />\n            <file role=\"src\" name=\"include/swoole_mqtt.h\" />\n            <file role=\"src\" name=\"include/swoole_msg_queue.h\" />\n            <file role=\"src\" name=\"include/swoole_pipe.h\" />\n            <file role=\"src\" name=\"include/swoole_process_pool.h\" />\n            <file role=\"src\" name=\"include/swoole_protocol.h\" />\n            <file role=\"src\" name=\"include/swoole_proxy.h\" />\n            <file role=\"src\" name=\"include/swoole_reactor.h\" />\n            <file role=\"src\" name=\"include/swoole_redis.h\" />\n            <file role=\"src\" name=\"include/swoole_server.h\" />\n            <file role=\"src\" name=\"include/swoole_signal.h\" />\n            <file role=\"src\" name=\"include/swoole_socket.h\" />\n            <file role=\"src\" name=\"include/swoole_socket_hook.h\" />\n            <file role=\"src\" name=\"include/swoole_socket_impl.h\" />\n            <file role=\"src\" name=\"include/swoole_ssl.h\" />\n            <file role=\"src\" name=\"include/swoole_static_handler.h\" />\n            <file role=\"src\" name=\"include/swoole_string.h\" />\n            <file role=\"src\" name=\"include/swoole_table.h\" />\n            <file role=\"src\" name=\"include/swoole_thread.h\" />\n            <file role=\"src\" name=\"include/swoole_timer.h\" />\n            <file role=\"src\" name=\"include/swoole_uring_socket.h\" />\n            <file role=\"src\" name=\"include/swoole_util.h\" />\n            <file role=\"src\" name=\"include/swoole_version.h\" />\n            <file role=\"src\" name=\"include/swoole_websocket.h\" />\n            <file role=\"doc\" name=\"php-cs-fix\" />\n            <file role=\"src\" name=\"php_swoole.h\" />\n            <file role=\"src\" name=\"php_swoole_api.h\" />\n            <file role=\"src\" name=\"run-core-tests.sh\" />\n            <file role=\"src\" name=\"scripts/.gitignore\" />\n            <file role=\"src\" name=\"scripts/clear.sh\" />\n            <file role=\"src\" name=\"scripts/code-format.sh\" />\n            <file role=\"src\" name=\"scripts/code-stats.sh\" />\n            <file role=\"src\" name=\"scripts/debug/swoole_info.php\" />\n            <file role=\"src\" name=\"scripts/debug/swoole_table_implements.php\" />\n            <file role=\"src\" name=\"scripts/docker-compile-with-iouring.sh\" />\n            <file role=\"src\" name=\"scripts/docker-compile-with-thread.sh\" />\n            <file role=\"src\" name=\"scripts/docker-compile.sh\" />\n            <file role=\"src\" name=\"scripts/docker-compose.yml\" />\n            <file role=\"src\" name=\"scripts/docker-iouring-route.sh\" />\n            <file role=\"src\" name=\"scripts/docker-route.sh\" />\n            <file role=\"src\" name=\"scripts/docker-thread-route.sh\" />\n            <file role=\"src\" name=\"scripts/format-changed-files.sh\" />\n            <file role=\"src\" name=\"scripts/install-deps-on-ubuntu.sh\" />\n            <file role=\"src\" name=\"scripts/install-liburing.sh\" />\n            <file role=\"src\" name=\"scripts/library.sh\" />\n            <file role=\"src\" name=\"scripts/make.sh\" />\n            <file role=\"src\" name=\"scripts/pecl-install.sh\" />\n            <file role=\"src\" name=\"scripts/rename.php\" />\n            <file role=\"src\" name=\"scripts/route.sh\" />\n            <file role=\"src\" name=\"scripts/run-tests.sh\" />\n            <file role=\"src\" name=\"scripts/simple-compile-on-github.sh\" />\n            <file role=\"src\" name=\"scripts/simple-compile.sh\" />\n            <file role=\"src\" name=\"src/core/base.cc\" />\n            <file role=\"src\" name=\"src/core/base64.cc\" />\n            <file role=\"src\" name=\"src/core/buffer.cc\" />\n            <file role=\"src\" name=\"src/core/channel.cc\" />\n            <file role=\"src\" name=\"src/core/crc32.cc\" />\n            <file role=\"src\" name=\"src/core/error.cc\" />\n            <file role=\"src\" name=\"src/core/heap.cc\" />\n            <file role=\"src\" name=\"src/core/log.cc\" />\n            <file role=\"src\" name=\"src/core/misc.cc\" />\n            <file role=\"src\" name=\"src/core/string.cc\" />\n            <file role=\"src\" name=\"src/core/timer.cc\" />\n            <file role=\"src\" name=\"src/coroutine/base.cc\" />\n            <file role=\"src\" name=\"src/coroutine/channel.cc\" />\n            <file role=\"src\" name=\"src/coroutine/context.cc\" />\n            <file role=\"src\" name=\"src/coroutine/file.cc\" />\n            <file role=\"src\" name=\"src/coroutine/file_lock.cc\" />\n            <file role=\"src\" name=\"src/coroutine/hook.cc\" />\n            <file role=\"src\" name=\"src/coroutine/iouring.cc\" />\n            <file role=\"src\" name=\"src/coroutine/socket.cc\" />\n            <file role=\"src\" name=\"src/coroutine/system.cc\" />\n            <file role=\"src\" name=\"src/coroutine/thread_context.cc\" />\n            <file role=\"src\" name=\"src/coroutine/uring_socket.cc\" />\n            <file role=\"src\" name=\"src/lock/barrier.cc\" />\n            <file role=\"src\" name=\"src/lock/coroutine_lock.cc\" />\n            <file role=\"src\" name=\"src/lock/mutex.cc\" />\n            <file role=\"src\" name=\"src/lock/rw_lock.cc\" />\n            <file role=\"src\" name=\"src/lock/spin_lock.cc\" />\n            <file role=\"src\" name=\"src/memory/fixed_pool.cc\" />\n            <file role=\"src\" name=\"src/memory/global_memory.cc\" />\n            <file role=\"src\" name=\"src/memory/ring_buffer.cc\" />\n            <file role=\"src\" name=\"src/memory/shared_memory.cc\" />\n            <file role=\"src\" name=\"src/memory/table.cc\" />\n            <file role=\"src\" name=\"src/network/address.cc\" />\n            <file role=\"src\" name=\"src/network/client.cc\" />\n            <file role=\"src\" name=\"src/network/dns.cc\" />\n            <file role=\"src\" name=\"src/network/socket.cc\" />\n            <file role=\"src\" name=\"src/network/stream.cc\" />\n            <file role=\"src\" name=\"src/os/async_thread.cc\" />\n            <file role=\"src\" name=\"src/os/base.cc\" />\n            <file role=\"src\" name=\"src/os/file.cc\" />\n            <file role=\"src\" name=\"src/os/msg_queue.cc\" />\n            <file role=\"src\" name=\"src/os/pipe.cc\" />\n            <file role=\"src\" name=\"src/os/process_pool.cc\" />\n            <file role=\"src\" name=\"src/os/sendfile.cc\" />\n            <file role=\"src\" name=\"src/os/signal.cc\" />\n            <file role=\"src\" name=\"src/os/timer.cc\" />\n            <file role=\"src\" name=\"src/os/unix_socket.cc\" />\n            <file role=\"src\" name=\"src/os/wait.cc\" />\n            <file role=\"src\" name=\"src/protocol/base.cc\" />\n            <file role=\"src\" name=\"src/protocol/dtls.cc\" />\n            <file role=\"src\" name=\"src/protocol/http.cc\" />\n            <file role=\"src\" name=\"src/protocol/http2.cc\" />\n            <file role=\"src\" name=\"src/protocol/message_bus.cc\" />\n            <file role=\"src\" name=\"src/protocol/mime_type.cc\" />\n            <file role=\"src\" name=\"src/protocol/mqtt.cc\" />\n            <file role=\"src\" name=\"src/protocol/redis.cc\" />\n            <file role=\"src\" name=\"src/protocol/socks5.cc\" />\n            <file role=\"src\" name=\"src/protocol/ssl.cc\" />\n            <file role=\"src\" name=\"src/protocol/websocket.cc\" />\n            <file role=\"src\" name=\"src/reactor/base.cc\" />\n            <file role=\"src\" name=\"src/reactor/epoll.cc\" />\n            <file role=\"src\" name=\"src/reactor/kqueue.cc\" />\n            <file role=\"src\" name=\"src/reactor/poll.cc\" />\n            <file role=\"src\" name=\"src/server/base.cc\" />\n            <file role=\"src\" name=\"src/server/manager.cc\" />\n            <file role=\"src\" name=\"src/server/master.cc\" />\n            <file role=\"src\" name=\"src/server/port.cc\" />\n            <file role=\"src\" name=\"src/server/process.cc\" />\n            <file role=\"src\" name=\"src/server/reactor_process.cc\" />\n            <file role=\"src\" name=\"src/server/reactor_thread.cc\" />\n            <file role=\"src\" name=\"src/server/static_handler.cc\" />\n            <file role=\"src\" name=\"src/server/task_worker.cc\" />\n            <file role=\"src\" name=\"src/server/thread.cc\" />\n            <file role=\"src\" name=\"src/server/worker.cc\" />\n            <file role=\"src\" name=\"src/wrapper/event.cc\" />\n            <file role=\"src\" name=\"src/wrapper/http.cc\" />\n            <file role=\"src\" name=\"src/wrapper/timer.cc\" />\n            <file role=\"test\" name=\"tests/CONTRIBUTION\" />\n            <file role=\"test\" name=\"tests/README.md\" />\n            <file role=\"test\" name=\"tests/clean\" />\n            <file role=\"test\" name=\"tests/include/api/curl_multi.php\" />\n            <file role=\"test\" name=\"tests/include/api/exit.php\" />\n            <file role=\"test\" name=\"tests/include/api/http_server.php\" />\n            <file role=\"test\" name=\"tests/include/api/http_test_cases.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_callback/swoole_cannot_destroy_active_lambda_function.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_client/connect_timeout.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_client/connect_twice.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_client/http_get.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_client/opcode_client.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_client/simple_client.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_client/socket_free.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_http_server/http_server.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_http_server/http_server_without_response.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_http_server/simple_http_server.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_http_server/simple_https_server.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_server/TestServer.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_server/multi_protocol_server.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_server/opcode_server.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_server/reconnect_fail/tcp_client.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_server/reconnect_fail/tcp_serv.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_server/server_manager_process_exit.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_server/server_send_fast_recv_slow.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_server/simple_server.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_server/simple_tcp_server.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_server/simple_udp_server.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_server/tcp_serv.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_server/tcp_task_server.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_server/testsendfile.txt\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_thread/putenv.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_thread/sleep.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_timer/accurate_test.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_timer/fixRate_vs_fixDelay.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_timer/invalid_args.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_timer/multi_timer.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_timer/register_shutdown_priority.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_websocket_server/send_large_request_data.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_websocket_server/send_small_request_data.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_websocket_server/swoole_websocket_server.php\" />\n            <file role=\"test\" name=\"tests/include/api/swoole_websocket_server/websocket_client.php\" />\n            <file role=\"test\" name=\"tests/include/api/syntax_error.txt\" />\n            <file role=\"test\" name=\"tests/include/api/tcp_server.php\" />\n            <file role=\"test\" name=\"tests/include/api/test_classes/A.php\" />\n            <file role=\"test\" name=\"tests/include/api/test_classes/A2.php\" />\n            <file role=\"test\" name=\"tests/include/api/test_classes/B.php\" />\n            <file role=\"test\" name=\"tests/include/bootstrap.php\" />\n            <file role=\"test\" name=\"tests/include/config.php\" />\n            <file role=\"test\" name=\"tests/include/functions.php\" />\n            <file role=\"test\" name=\"tests/include/lib/composer.json\" />\n            <file role=\"test\" name=\"tests/include/lib/src/Assert.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/ChildProcess.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/CoServer.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/CurlManager.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/DbWrapper.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/LengthServer.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/MQTT/Helper.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/MysqlPool.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/ProcessManager.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/RandStr.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/Redis/DBConnectException.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/Redis/Lock.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/Redis/Redis.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/Redis/SQLPool.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/Samtleben/WebsocketClient.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/ServerManager.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/TcpStat.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/ThreadManager.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/WaitRef.php\" />\n            <file role=\"test\" name=\"tests/include/lib/src/responder/get.php\" />\n            <file role=\"test\" name=\"tests/include/macos/phpstorm.py\" />\n            <file role=\"test\" name=\"tests/include/skipif.inc\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/ca-cert.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/ca-key.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/ca-req.csr\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/ca.crt\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/ca.csr\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/ca.key\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/ca.srl\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/client-cert.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/client-expired.crt\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/client-expired.csr\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/client-expired.key\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/client-key.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/client-req.csr\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/client.crt\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/client.csr\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/client.key\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/client.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/dhparams.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/mosquitto.org.crt\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/passwd.crt\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/passwd_key.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/server-cert.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/server-key.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/server-req.csr\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/server.crt\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/server.csr\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/server.key\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/server.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/sni_server_ca.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/sni_server_cs.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/sni_server_cs_cert.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/sni_server_cs_key.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/sni_server_uk.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/sni_server_uk_cert.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/sni_server_uk_key.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/sni_server_us.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/sni_server_us_cert.pem\" />\n            <file role=\"test\" name=\"tests/include/ssl_certs/sni_server_us_key.pem\" />\n            <file role=\"test\" name=\"tests/init\" />\n            <file role=\"test\" name=\"tests/new\" />\n            <file role=\"test\" name=\"tests/pgsql.sql\" />\n            <file role=\"test\" name=\"tests/run-tests\" />\n            <file role=\"test\" name=\"tests/start.sh\" />\n            <file role=\"test\" name=\"tests/swoole_atomic/atomic.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_atomic/dtor_in_child.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_atomic/multi_wakeup.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_atomic/wait.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_atomic/wait_and_wakeup.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_atomic/wait_ex.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/10.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/5.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/6.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/7.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/8.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/9.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/basic.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/benchmark.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/blocking_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/bug_1947.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/bug_clear_timer.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/chan_select_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/chan_stats.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/close.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/coro_wait.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/discard.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/fibonacci.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/http2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/hybird_chan.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/hybird_chan2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/hybird_chan3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/lock.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/no_ctor.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/pool.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/pop_after_close.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/pop_close1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/pop_timeout1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/pop_timeout2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/pop_timeout3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/pop_timeout4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/pop_timeout5.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/pop_timeout6.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/pop_timeout7.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/pop_timeout8.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/push_close1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/push_timeout1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/push_timeout2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/push_timeout3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/push_timeout4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_channel_coro/type.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/big_package_memory_leak.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/buffer_full.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/connect_dns.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/connect_refuse.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/connect_refuse_udg.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/connect_refuse_unix.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/connect_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/connect_twice.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/enableSSL.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/enableSSL_bad_callback.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/enableSSL_before_connect.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/eof.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/eof_close.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/getSocket_bug.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/getpeername.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/getsockname.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/length_protocol.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/length_protocol_func.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/port_invalid.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/sendfile.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_async/sleep_wake.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/bug_2346.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/close.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/close_in_other_co.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/close_resume.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/close_socket_property.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/close_twice.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/connect_dns_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/connect_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/connect_with_dns.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/dtls.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/enableSSL.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/eof.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/eof_02.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/eof_03.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/eof_04.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/export_socket.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/fixed_package.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/getpeername.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/getsockname.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/isConnected.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/length_01.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/length_02.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/length_03.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/length_04.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/length_protocol_func.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/length_types.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/read_and_write.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/reconnect.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/reconnect_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/reconnect_3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/recv_after_close.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/recv_bad_packet.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/recv_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/recv_timeout2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/recvfrom.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/recvfrom_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/recvfrom_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/recvfrom_timeout2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/send_big.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/sendfile.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/sendto.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/ssl.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/ssl_verify.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/tcp_client.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/tcp_nodelay.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/udp_client.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/udp_recv_failed.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/unsock_dgram.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_coro/unsock_stream.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/bind_address.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/connect_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/connect_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/connect_3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/enableSSL.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/enableSSL_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/eof.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/eof_close.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/eof_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/http_proxy.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/keep1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/keep2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/keep3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/keep4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/keep5.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/keep6.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/length_protocol.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/length_protocol_02.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/length_protocol_func.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/recv_in_task.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/recv_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/recv_with_open_eof_check.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/select.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/select_null.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/send_recv.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/sendfile.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/sendto.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/socks5_proxy.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/ssl_recv_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/sync_send_recv.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/udg_send_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_client_sync/udp_client_sendto.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/after_start_server_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/all_asleep.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/array_walk.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/array_walk_deep.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/async_callback/event_cycle.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/async_callback/signal.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/async_callback/timer.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/autoload.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/autoload_not_found.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/autoload_not_found_not_in_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/autoload_not_in_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/bailout/co_redis_in_shutdown_function.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/bailout/error.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/bailout/error_in.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/bailout/error_internal.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/bailout/error_internal2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/bailout/error_out.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/bailout/exit.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/before_create_server_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/before_create_server_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/before_create_server_3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/bug_2387.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/bug_5699.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/c_stack_size.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/call_not_exists_func.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/call_user_func_array.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/call_user_func_array2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/call_with_args.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/callback.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/cancel/channel_pop.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/cancel/channel_push.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/cancel/error.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/cancel/gethostbyname.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/cancel/kill.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/cancel/sleep.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/cancel/socket.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/cancel/suspend.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/cancel/throw_exception.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/cancel/wait.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/cancel/wait_event.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/cancel/wait_signal.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/check.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/cid.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/create_after_rshutdown.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/current.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/dead_lock.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/defer/defer.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/defer/defer_close.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/defer/defer_exception.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/defer/defer_in_try.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/destruct/destruct1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/destruct/destruct2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/destruct/destruct3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/dnslookup_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/dnslookup_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/dnslookup_3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/dnslookup_4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/dnslookup_5.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/dnslookup_ipv6.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/dnslookup_query_hosts.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/empty.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/eval.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exception.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exception/core_error.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exception/defer1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exception/defer2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exception/dtor.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exception/empty.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exception/error.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exception/error1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exception/error2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exception/error3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exception/fatal_error.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exception/nested.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exception/yield.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exception/yield1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/execute_time.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exists.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exit.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exit_84.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exit_exception_backtrace.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/exit_exception_backtrace_84.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/fatal_error.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/forbidden_case/array_map.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/forbidden_case/call_user.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/forbidden_case/invoke.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/getContext.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/getElasped.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/getPcid.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/getPcid_by_random_cid.phpt.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/gethostbyname.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/iterator.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/join/1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/join/2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/join/3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/join/4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/join/5.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/join/6.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/join/7.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/join/8.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/kernel_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/list_and_backtrace.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/max_num.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/max_num_limit.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/nested1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/nested2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/nested3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/nested_empty.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/nested_uid.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/new_process.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/new_server.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/no_inline_func.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/output/concurrency.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/output/create.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/output/in_nested_co.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/output/ob_main.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/output/output_control.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/parallel1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/parallel2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/parallel3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/pdo_error_handing.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/private_access.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/resume_loop.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/scheduler.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/signal_listener.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/stats.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/use_process.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/user_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine/user_coroutine_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_lock/lock.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_lock/trylock.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_lock/trylock2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/getOptions.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/hook_flags.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/hook_flags_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/parallel.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/preemptive/disable.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/preemptive/disable2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/preemptive/do-while.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/preemptive/do-while2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/preemptive/do-while3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/preemptive/for.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/preemptive/for2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/preemptive/goto.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/preemptive/goto2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/preemptive/timer.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/preemptive/while.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/preemptive/while2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/preemptive/while3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/repeat.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/resume1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/resume2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/resume3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/resume4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/resume5.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/resume6.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/start.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/start_in_server_shutdown.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_scheduler/start_in_server_worker_stop.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_system/aio_thread_num.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_system/getaddrinfo.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_system/getaddrinfo_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_system/gethostbyname.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_system/gethostbyname_ipv6.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_system/gethostbyname_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_system/readfile.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_system/sleep.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_system/wait.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_system/waitEvent.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_system/waitPid.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_system/waitSignal.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_system/waitSignal_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_system/writefile.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_system/writefile_append.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_util/dns_lookup.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_util/exec.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_util/exec_sleep.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_util/fgets.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_util/fread.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_util/fwrite.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_util/list_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_util/task_worker.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_wait_group/base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_wait_group/defer.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_wait_group/empty.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_coroutine_wait_group/logic.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/abnormal_response/1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/abnormal_response/2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/10.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/11.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/12.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/13.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/14.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/15.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/19.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/20.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/21.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/22.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/23.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/24.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/25.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/5.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/7.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/8.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/basic/9.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/cancel.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/close_before_resume.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/concurrent.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/error.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/event_exit.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/exec_twice.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/fatal_error_in_callback.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/ftp.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/guzzle.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/guzzle/cancel.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/guzzle/cannot_cancel_finished.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/guzzle/cookie.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/guzzle/promise.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/guzzle/request_async.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/guzzle/request_on_stats.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/guzzle/send_async.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/https.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/keepalive.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/5.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/6.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/add_after_easy_exec.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/bug4393.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/bug48203_multi.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/bug67643.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/bug71523.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/bug76675.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/bug77535.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/bug77946.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/close.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/curl_basic_018.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/curl_copy_handle_variation4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/curl_int_cast.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/curl_multi_close_basic.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/curl_multi_close_basic001.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/curl_multi_close_reference.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/curl_multi_errno_strerror_001.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/curl_multi_getcontent_basic3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/curl_multi_info_read.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/curl_multi_init_basic.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/curl_multi_segfault.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/curl_multi_select_basic1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/curl_multi_setopt_basic001.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/dtor.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/multiple_binding.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/no_hook.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/select_cancel.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/select_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/multi/select_twice.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/non_exclusive.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/setopt/1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/setopt/3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/setopt/4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/setopt/5_skip.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/setopt/filetime_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/setopt/filetime_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/setopt/header_out.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/setopt/infile.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/setopt/nobody.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/share/1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/share/5.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/sleep.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/ssl/version.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/symfony-noco.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/symfony.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/template\" />\n            <file role=\"test\" name=\"tests/swoole_curl/timer_coredump.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/undefined_behavior/0.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/undefined_behavior/1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/undefined_behavior/2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/undefined_behavior/3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/undefined_behavior/4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/undefined_behavior/5.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/undefined_behavior/6.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/undefined_behavior/7.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/undefined_behavior/8.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/upload/1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/upload/2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/upload/3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/upload/curl_testdata1.txt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/upload/curl_testdata2.txt\" />\n            <file role=\"test\" name=\"tests/swoole_curl/yield_in_callback.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/add_after_server_start.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/cycle.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/defer.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/defer_with_sleep.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/defer_without_io.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/del.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/del_after_close.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/deprecated_event_wait.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/dispatch.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/function_alias.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/isset.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/kqueue.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/rshutdown.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/set.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/simple.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/sockets.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/sync_client_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/sync_client_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/wait.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_event/write.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_feature/cross_close/client.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_feature/cross_close/client_by_server.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_feature/cross_close/full_duplex.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_feature/cross_close/full_duplex_by_server.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_feature/cross_close/http.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_feature/cross_close/http_by_server.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_feature/cross_close/php_stream_full_duplex.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_feature/cross_close/php_stream_full_duplex_by_server.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_feature/cross_close/redis.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_feature/cross_close/redis_by_server.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_feature/cross_close/stream.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_feature/cross_close/stream_by_server.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_feature/full_duplex/client.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_feature/full_duplex/socket.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_feature/full_duplex/socket_ssl.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_feature/full_duplex/websocket.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/001.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/002.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/003.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/004.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/005.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/007.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/bug27809.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/bug37799.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/bug39458-2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/bug39458.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/bug39583-2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/bug39583.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/bug7216-2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/bug7216.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/bug79100.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/bug80901.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/cert.pem\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/dead-resource.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/filesize_large.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_alloc_basic1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_alloc_basic2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_append.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_chmod_basic.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_connect_001.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_constructor.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_delete.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_exec_basic.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_fget_basic.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_fget_basic1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_fget_basic2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_fget_basic3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_fput.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_fput_ascii_over_4_kib.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_get_basic.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_get_option.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_mdtm_basic.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_mlsd.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_mlsd_empty_directory.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_mlsd_missing_directory.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_nb_continue.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_nb_fget_basic1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_nb_fget_basic2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_nb_fget_basic3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_nb_fput.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_nb_get_large.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_nb_put.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_pasv.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_rawlist_basic1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_rawlist_basic2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_rename_basic1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_rmdir_basic.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_set_option.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_set_option_errors.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_site_basic.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/ftp_ssl_connect_error.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/gh10521.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/gh10562.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ftp/server.inc\" />\n            <file role=\"test\" name=\"tests/swoole_function/substr_json_decode.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_function/substr_unserialize.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_function/swoole_clear_dns_cache.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_function/swoole_cpu_num.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_function/swoole_error_log.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_function/swoole_get_local_ip.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_function/swoole_get_local_mac.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_function/swoole_set_process_name.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_function/swoole_strerror.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_function/swoole_version.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_global/channel_construct_check.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_global/closed_stdout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_global/create_deny.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_global/function_alias.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_global/serialize_deny.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_global/socket_construct_check.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_global/too_many_objects.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_global/unset_deny.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_global/unset_property_01.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_global/unset_property_02.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_global/unset_property_03.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/bug_5127.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/connect_twice.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/cookies.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/error.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/goaway.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/headers.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/host.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/http_proxy.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/huge_headers.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/issues_2374.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/max-frame-size.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/multi.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/no-gzip.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/number.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/ping.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/pipeline.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/post.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/send-cookies.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/send_only_bug.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/set-cookies.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/sock_type_unix.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_client_coro/wrong_headers.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/413.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/big_data.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/compression.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/compression_types.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/getMethod.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/goaway.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/http2_headers.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/issue_4365.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/max_concurrency.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/nghttp2_big_data.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/no_compression.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/ping.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/sendfile.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/sendfile_content_type.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/sendfile_set_content_type.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/server_addr.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/static_handler.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/streaming.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/streaming_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/trailer.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server/worker_max_concurrency.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http2_server_coro/cookies.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/204.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/addData.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/alias.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/another_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/auto_reconnect.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/bind_address.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/bug_2661.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/bug_3118.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/compression_with_big_data.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/connect_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/connection_close.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/construct_failed.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/cookies_set_bug.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/defer.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/defer_02.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/disable_keep_alive.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/download.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/download_302.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/download_failed.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/download_filename_bug.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/duplicate_header.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/error_handler.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/get.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/get_header_out_after_close.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/get_twice.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/get_twice_keepalive.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/get_without_content_length.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/h2c_upgrade.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/head_method.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/host.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/http_chunk.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/http_proxy.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/http_proxy_443.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/http_proxy_with_host_port.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/http_upload_big.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/https.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/https_upload_big.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/issue_2664.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/keep_alive.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/long_domain.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/lowercase_header.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/multi.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/multi_and_reuse.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/parser.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/post_array.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/reconnect_but_failed.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/recv_slow_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/recv_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/set_basic_auth.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/slow_server.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/socks5_proxy.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/socks5_proxy_ipv6.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/ssl.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/ssl_host_name.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/ssl_verify_peer_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/ssl_verify_peer_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/timeout_before_connect.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/timeout_when_recv.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/unixsocket.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/upload.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/upload_huge.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/upload_with_null_args.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/auto_pong.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/bug_01.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/bug_02.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/close_socket.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/continue_frame_finish_flag.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/continue_frame_finish_flag2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/continue_frames.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/continue_frames2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/continue_frames3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/continue_frames4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/control_frame_compress.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/control_frame_fragmented.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/disconnect.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/open_websocket_frame.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/ping.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/priority.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/priority1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/send_more_continue_frame.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/send_more_continue_frame2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/server_push_first.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/ssl_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/ssl_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/websocket/upgrade_after_get.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/write_func_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_client_coro/write_func_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/0.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/100-continue.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/If_Modified_Since.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/accept_encoding.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/buffer_output_size.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/bug_2368.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/bug_2444.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/bug_2608.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/bug_2751.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/bug_2786.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/bug_2947.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/bug_2988.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/bug_4261.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/bug_4857.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/bug_5107.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/bug_5114.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/bug_5146.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/bug_5186.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/bug_6007.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/bug_compression_level.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/bug_get_request_data_after_end.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/callback_new_obj_method.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/callback_new_static_method.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/callback_string.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/callback_with_internal_function.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/callback_with_private.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/callback_with_protected.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/chunk.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/chunk_with_end_data.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/chunked_pipeline_request.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/client_ca.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/client_compress.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/co_switching.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/compression.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/compression_min_length.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/compression_types.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/cookieAlias.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/cookie_delete.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/cookie_samesite.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/cookie_vs_rawcookie.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/cookies.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/cookies_parse.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/create_request.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/data_parse.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/disable_compression.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/disable_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/dispatch_mode_7.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/duplicate_header.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/enable_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/error_1203.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/error_413.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/event_stream.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/event_stream2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/form_data_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/form_data_with_charset.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/head_method.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/headers_sent.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/headers_sent_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/http_autoindex.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/http_index_files.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/http_index_files_autoindex.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/https.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/issue_2360.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/json_encode.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/json_encode2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/large_url.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/max-age.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/max_concurrency.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/max_coro_num.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/max_execution_time.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/max_input_vars.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/mixed_server.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/new_cookie.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/no_compression.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/numeric_header_name.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/objectCookie.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/objectCookieMemory.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/octane_bug_651.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/pipeline.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/post.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/purge_method.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/range.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/range2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/rawContent.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/rawCookie.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/redirect.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/reset_concurrency_with_base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/reset_concurrency_with_process.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/response_create.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/send_500_to_client.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/send_empty_file.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/send_yield.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/sendfile.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/sendfile_client_reset.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/sendfile_dir.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/sendfile_link.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/sendfile_no_keepalive.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/sendfile_with_dispatch_mode_7.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/sendfile_with_ssl.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/server_addr.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/server_addr2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/set_content_length.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/shutdown_in_event_worker.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/shutdown_in_task_worker.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/slow_client.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/slow_large_post.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/sni/server.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/static_handler.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/static_handler/locations.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/static_handler/mimetype_not_exists.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/static_handler/read_link_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/static_handler/read_link_file.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/static_handler/relative_path.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/static_handler/relative_path_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/static_handler/relative_path_3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/static_handler/urldecode.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/task/enable_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/task/enable_coroutine_with_wrong_usage.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/task/use_object.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/tmp-content-type.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/too_many_special_chars_in_cookie.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/trailer.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/unixsocket.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/unixsocket2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/unset_response_header.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/upload.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/upload4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/uploadFile.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/upload_02.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/upload_03.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/upload_big_file.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/upload_file_array_default.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/upload_file_array_parsed.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/upload_file_empty.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/upload_max_filesize.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/url_rewrite.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/worker_max_concurrency.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server/zstd.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/bad_request.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/buffer_clear.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/bug_2682.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/bug_3025.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/bug_4519.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/bug_no_handle.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/check_cookie_crlf.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/check_http_header_crlf.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/chunked_pipeline_request.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/close_socket.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/compress_continue_frames.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/compression_min_length.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/compression_types.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/continue_frames.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/continue_frames2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/continue_frames3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/continue_frames4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/control_frame_compress.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/control_frame_fragmented.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/crash-bad-return-type.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/create_response.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/create_response_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/disconnect.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/error_404.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/error_413.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/form_data_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/form_data_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/graceful_shutdown.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/handle.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/http2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/http2_ssl.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/https.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/ipv6.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/keepalive.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/open_frame.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/ping.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/pipeline.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/post_array.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/random_port.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/rawContent_get_big_data.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/remote_addr.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/restart.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/reuse_port.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/sendfile.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/server_addr.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/slow_client.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/slow_large_post.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/ssl_bad_client.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/tcp_nodelay.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/upload.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/websocket.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/websocket_close.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/websocket_compression.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/websocket_mixed.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/websocket_ping.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_http_server_coro/websocket_ping_pong.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_iouring/mix.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_iouring/setting.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_library/array_object/base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_library/database/mysqli.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_library/database/pdo.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_library/database/redis.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_library/exec/exec/1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_library/exec/exec/2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_library/exec/shell_exec/1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_library/multibyte_string_object/base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_library/name_resolver/1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_library/name_resolver/lookup.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_library/string_object/base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_library/wait_group/normal.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_library/wait_group/timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_lock/bug_5984.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_lock/lock_nb.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_lock/lock_read.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_lock/lock_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_lock/mutex.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_lock/spinlock_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_firebird/base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_firebird/pdo_firebird.inc\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_firebird/query.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_firebird/sleep.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_firebird/transaction.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_odbc/base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_odbc/blocking.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_odbc/query.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_odbc/race.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_odbc/server_info.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_odbc/sleep.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_odbc/transaction.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/bug41996.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/bug44301.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/bug46274.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/bug46274_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/bug54379.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/bug57702.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/bug60994.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/bug_33707.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/checkliveness.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/coroutint.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/oci_success_with_info.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_attr_action.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_attr_call_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_attr_case.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_attr_client.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_attr_client_identifier.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_attr_client_info.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_attr_drivername.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_attr_module.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_attr_nulls_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_attr_server.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_class_constants.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_debugdumpparams.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_fread_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_phpinfo.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_quote1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_stmt_getcolumnmeta.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_stream_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_stream_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oci_templob_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pdo_oracle.inc\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pecl_bug_11345.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/pecl_bug_6364.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/transcation.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_oracle/transcation2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_pgsql/base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_pgsql/blocking.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_pgsql/bug_5635.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_pgsql/libpq_version.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_pgsql/pdo_pgsql.inc\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_pgsql/query.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_pgsql/race.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_pgsql/sleep.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_pgsql/transaction.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug33841.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug35336.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug38334.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug43831.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug44327_2_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug44327_2_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug44327_3_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug44327_3_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug46139.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug46542.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug48773.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug50728.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug52487.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug66033.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug70862.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug70862_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug78192_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug78192_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug79664_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug79664_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug81740.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug_42589.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug_44159_sqlite_version_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug_44159_sqlite_version_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug_47769.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug_63916-2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug_63916_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug_63916_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/bug_64705.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/coroutine2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/debugdumpparams_001.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/gh9032.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/open_basedir.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/pdo_035.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/pdo_fetch_func_001.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/pdo_fetch_func_001_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/pdo_sqlite.inc\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/pdo_sqlite_extendederror_attr.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/pdo_sqlite_filename_uri.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/pdo_sqlite_get_attribute.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/pdo_sqlite_lastinsertid.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/pdo_sqlite_open_flags.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/pdo_sqlite_statement_getattribute.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/pdo_sqlite_tostring_exception.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_pdo_sqlite/pdo_sqlite_transaction.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/alarm.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/close.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/coro/ipc.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/coro/set_protocol.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/coro/signal.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/coro/start.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/ctor.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/daemon.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/deamon.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/echo.py\" />\n            <file role=\"test\" name=\"tests/swoole_process/enable_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/exception.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/exec.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/exit.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/freeQueue.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/getaffinity.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/ignore_sigpipe.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/ignore_sigpipe_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/kill.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/msgq_capacity.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/name.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/null_callback.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/pop.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/priority.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/priority_error.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/process_exec.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/process_id.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/process_msgqueue.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/process_push.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/process_select.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/push.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/read.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/redirect.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/setaffinity.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/signal.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/signal_in_manager.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/signal_in_manager_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/signal_in_manager_3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/signal_in_task_worker.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/signal_twice.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/start.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/useQueue.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/wait.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/wait_signal.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/write.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process/write_in_worker.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/bug_2652.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/create_websocket_server.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/detach.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/enable_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/enable_coroutine2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/export_socket.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/getprocess_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/getprocess_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/getprocess_3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/getprocess_4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/getprocess_5.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/master_callback.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/master_pid.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/max_wait_time.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/message.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/message_async.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/message_bus.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/message_bus_sync.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/msgqueue.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/msgqueue_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/reload.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/reuse_port.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/shutdown.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/socket_coro.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/start_pool_twice_in_same_process.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/start_twice.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_process_pool/worker_exit_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_redis_server/big_packet.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_redis_server/empty.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_redis_server/format.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_redis_server/getHandler.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_redis_server/large_data_map.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_redis_server/nested_map.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/accept.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/accept_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/async_protocol.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/bindto.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/bindto2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/block.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/bug_4657.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/bug_5104.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/cancel_sleep.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/destruct.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/enable_crypto.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/exec/exec.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/a.inc\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/async_file.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/b.inc\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/bug_4327.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/bug_ftell_2g.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/bug_scandir.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/fgets.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/flock.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/fread.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/include.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/include_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/lock_ex.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/lock_nb_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/lock_nb_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/lock_sh.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/open_basedir.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/read.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_hook/readdir.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_lock/async_file.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_lock/lock_ex.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_lock/lock_nb.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/file_lock/lock_sh_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/ftp_fopen_wrapper.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/get_hook_flags.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/gethostbyname.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/hook_default.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/hook_enable_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/hook_set_flags.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/library.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/nonblock.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/out_of_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/pdo.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/persistent.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/proc/1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/proc/2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/proc/3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/proc/4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/proc/5.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/proc/close_after_wait.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/proc/proc_open_pipes.inc\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/redis_connect.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/redis_pconnect.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/remote_object/dns.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sento.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/set_hook_flags.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sleep.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sleep_yield.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/bug46360.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/bug49341.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/bug63000.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/bug76839.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/ipv4loop.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/ipv6_skipif.inc\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/ipv6loop.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/mcast_helpers.php.inc\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/mcast_ipv4_recv.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/mcast_ipv4_send.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/mcast_ipv4_send_error.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/mcast_ipv6_recv.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/mcast_ipv6_recv_limited.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/mcast_ipv6_send.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_accept_failure.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_bind.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_clear_error.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_connect_params.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_create_listen-nobind.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_create_listen.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_create_listen_used.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_create_pair.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_getopt.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_getpeername.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_getpeername_ipv4loop.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_getpeername_ipv6loop.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_getsockname.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_listen-wrongparams.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_read_params.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_select-wrongparams-2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_select.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_select_error.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv4_udp.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv6_udp.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_unix.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_set_block-retval.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_set_nonblock-retval.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_set_nonblock.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_set_option_bindtodevice.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_set_option_error_socket_option.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_set_option_in6_pktinfo.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_set_option_rcvtimeo.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_set_option_seolinger.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_set_option_sndtimeo.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_setopt_basic.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_shutdown.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/socket_strerror.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/basic/unixloop.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/error.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/get_name.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/import.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/nonblock.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/socketpair.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/tcp_client.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/tcp_server.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/sockets/udp.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/ssl/capture_peer_cert.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/ssl/client.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/ssl/enable_crypto.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/ssl/local_cert.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/ssl/server.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/ssl/without_key.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stdin.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_context.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_context_pass_null.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_copy_to_stream_socket.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_get_meta_data.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_select/base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_select/blocked.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_select/bug46024.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_select/bug60120.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_select/bug60602.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_select/bug64438.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_select/bug64770.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_select/bug69521.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_select/bug72075.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_select/conflict.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_select/never_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_select/preserve_keys.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_select/return_val.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_select/rw_events.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_select/stream_set_blocking.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_select/timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/stream_socket_pair.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/tcp-c10k.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/tcp.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/udg.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/udp-c10k.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/udp.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/unix.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_runtime/unsafe/pcntl_fork.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/accept_zero.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/addListener.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/addProcess.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/addProcess_base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/addProcess_with_error.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/addProcess_with_event_wait.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/addProcess_with_tick.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/base/reload_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/base/reload_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/base/shutdown.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/base/shutdown_single.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/bigPipeMessage.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/big_session_id.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/big_udp_packet.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/bind.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/bug_11000_01.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/bug_1864.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/bug_2308.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/bug_2313.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/bug_2639.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/bug_2736.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/bug_aio.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/check_callback.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/check_chunk_total_size.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/client_close_in_writable_event.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/close_force.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/close_in_connect_callback.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/close_in_manager.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/close_in_master.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/close_in_non_current_worker.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/close_in_other_worker_with_base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/close_in_task_worker.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/close_max_fd.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/close_queued.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/close_reset.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/command.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/connections.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/discard_timeout_packet.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/dispatch_fallback.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/dispatch_func.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/dispatch_func_discard.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/dispatch_func_memory_leak.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/dispatch_mode_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/dispatch_mode_10.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/dispatch_mode_3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/dispatch_mode_7.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/dispatch_mode_8.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/dispatch_mode_9.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/duplicate_registered.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/enable_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/enable_delay_receive.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/enable_reuse_port.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/eof_protocol.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/eof_server.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/event/before_shutdown.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/event/manager_start.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/event/manager_stop.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/event/shutdown.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/event/start.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/event/worker_exit.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/exist.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/force_reload.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/force_reload2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/force_reload3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/force_reload4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/getCallback.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/getClientInfo.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/getClientInfo_in_callback_of_close.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/getClientInfo_in_callback_of_close_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/getClientList.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/getLastError.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/getSocket.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/getWorkerStatus.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/heartbeat.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/heartbeat_true.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/heartbeat_with_base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/http_protocol.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/idle_worekr_num.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/invalid_fd.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/invalid_option.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/kill_user_process_01.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/kill_user_process_02.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/kill_worker_01.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/kill_worker_02.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/last_time.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/length/00.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/length/01.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/length/02.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/length/03.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/length/length_func.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/listen_fail.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/max_idle_time_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/max_idle_time_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/max_queued_bytes.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/max_request.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/max_request_grace_disabled.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/max_request_grace_enabled.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/max_request_threshold.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/memory_leak/length.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/memory_leak/pipe_message.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/memory_leak/task.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/memory_leak/tcp.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/mqtt/length_offset.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/mqtt/recv_fail.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/mqtt/send_big_pack.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/new_twice.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/object/event.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/object/getManagerPid.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/object/getMasterPid.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/object/getWorkerId.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/object/getWorkerPid.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/object/packet.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/object/pipe_message.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/object/status_info.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/object/task_result.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/onReload.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/parse_option_to_size.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/pid_file.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/protect.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/protect_false.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/reload.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/reload_async.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/reload_process.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/sendMessage_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/sendMessage_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/sendMessage_3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/sendMessage_4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/sendMessage_in_manager.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/send_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/send_2m_in_task_worker.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/send_2m_in_user_process.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/send_3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/send_big_packet.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/send_in_master.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/send_in_other_worker_with_base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/sendfile.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/sendfile_02.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/sendfile_ssl.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/sendto_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/shutdown.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/shutdown_in_master.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/shutdown_in_process.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/shutdown_with_base_mode.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/sigint_with_base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/sigint_with_process.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/single_thread/heartbeat.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/single_thread/large_packet.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/single_thread/user_setting.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/sleep.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/slow_client.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/slow_master.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/slow_worker.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/ssl/00.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/ssl/bad_client.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/ssl/code/client.go\" />\n            <file role=\"test\" name=\"tests/swoole_server/ssl/code/client.js\" />\n            <file role=\"test\" name=\"tests/swoole_server/ssl/dtls.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/ssl/dtls_big_packet.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/ssl/dtls_with_length_protocol.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/ssl/golang.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/ssl/heartbeat_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/ssl/heartbeat_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/ssl/nodejs.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/ssl/ssl_send_wait.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/ssl/verify_01.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/ssl/verify_02.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/ssl/verify_03.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/start_twice.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/stats.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/stats_file.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/stats_file_json.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/stats_file_php.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/stop.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/stop_in_workerStart.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/systemd_fds.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/bug_2585.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/callback_is_null.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/enable_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/finish_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/huge_data.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/idle_worekr_num.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/invalid_packet.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/kill_01.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/kill_02.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/kill_task_worker_02.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/scheduler_warning.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/task_callback.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/task_co.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/task_enable_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/task_enable_coroutine_return.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/task_in_manager.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/task_in_master.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/task_in_task_worker.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/task_in_user_process.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/task_ipc_mode_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/task_ipc_mode_3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/task_max_request.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/task_pack.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/task_queue.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/task_wait.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/timer.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/unpack.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/task/without_onfinish.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/taskWaitMulti.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/taskwait_01.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/taskwait_02.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/unregistered_signal.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/unsock_dgram.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/unsock_stream.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/user_process.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/user_process_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/user_process_force_exit.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/user_process_pid.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/wrong_eof_setting.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server/z_conn_10k.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server_coro/length_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server_coro/random_port.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server_coro/reuse_port.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server_coro/ssl.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server_coro/tcp.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server_port/connections.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server_port/duplicate_registered.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server_port/heartbeat_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server_port/heartbeat_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server_port/heartbeat_3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server_port/http.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server_port/multi_port.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server_port/sub_handshake.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_server_port/tcp_eof.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/accept.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/all.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/bound_error.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/cancel.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/check_writev_readv_param_type.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/closed.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/complete_test.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/concurrency.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/construct_parse_args_failed.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/fd.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/getBoundCid.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/getopt/get.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/getopt/tcpinfo.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/getpeername.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/getsockname.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/icmp.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/import_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/import_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/import_3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/import_4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/import_5.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/iov_max.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/peek_and_checkLiveness.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/protocol/bug_3586.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/protocol/package_length_func.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/readVectorAll.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/readVectorAll_ssl.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/readVector_ssl.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/readVector_ssl_eagain.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/readv.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/readv_eagain.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/recvAll.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/recvAll_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/recv_bad_packet.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/recv_line.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/recv_timeout.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/recv_with_buffer.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/reuse.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/reuse_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/sendfile.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/sendto.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/sendto_big.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/sendto_large_packet.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/server_accept.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/setopt/bindtodevice.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/setopt/ipv6_pktinfo.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/setopt/multicast.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/setopt/recvtimeo.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/setopt/reuse.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/shutdown.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/ssl.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/ssl_bad_server.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/tcp-c10k.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/ulimit.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/unix_dgram.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/unix_stream.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/writeVectorAll.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/writeVectorAll_ssl.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/writeVector_ssl.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/writeVector_ssl_eagain.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/writev.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_socket_coro/writev_eagain.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/bug63480.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/bug79631.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/ssh2_auth.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/ssh2_auth_pubkey.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/ssh2_auth_pubkey_file.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/ssh2_connect.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/ssh2_exec.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/ssh2_scp_recv.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/ssh2_scp_send.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/ssh2_send_eof.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/ssh2_sftp_001.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/ssh2_sftp_002.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/ssh2_shell.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/ssh2_skip.inc\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/ssh2_stream_select.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/ssh2_test.inc\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/testkey_ed25519\" />\n            <file role=\"test\" name=\"tests/swoole_ssh2/testkey_ed25519.pub\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/array_method/0.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/array_method/1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/array_method/2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/array_method/method.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/stream_method/0.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/string_method/0.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/string_method/1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/string_method/json.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/string_method/marshal.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/string_method/match.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/string_method/mbstring.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/string_method/method.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/0.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/10.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/11.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/5.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/6.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/7.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/8.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/9.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/array_pop.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/array_push.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/array_shift.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/array_splice.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/array_unshift.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/resource.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_stdext/typed_array/sort.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/big_size.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/bug_2263.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/bug_2290.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/create_10k_object.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/del.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/force_unlock.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/foreach.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/getMemorySize_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/get_after_destroy.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/get_before_create.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/gh-5789.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/incr_after_del.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/int.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/key_value.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/negative.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/random_bytes.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/set_after_del.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/stats.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_table/type_conv.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/add_update.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/affinity.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/arraylist.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/async-io.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/atomic_ctor.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/barrier.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/co-stream.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/co-user-yield.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/empty_args.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/exit.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/fatal_error_1.inc\" />\n            <file role=\"test\" name=\"tests/swoole_thread/fatal_error_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/fatal_error_2.inc\" />\n            <file role=\"test\" name=\"tests/swoole_thread/fatal_error_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/fatal_error_3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/incr.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/info.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/lock.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/map.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/map2array.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/name.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/numeric_strkey.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/php_socket.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/pipe.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/priority.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/putenv.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/queue.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/queue_notify_all.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/bug_5662.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/bug_5694.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/create_response.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/exit.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/fatal_error.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/functions.inc\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/heartbeat.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/hook_flags.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/listen.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/manager_timer.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/reload.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/reload_task_workers.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/reload_task_workers_async.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/reset_concurrency.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/send_in_user_process.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/send_large_packet.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/setting.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/stop_worker.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/taskCo.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/taskWaitMulti.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/udp_port.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/server/websocket.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/shell_exec.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/signal.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/sort.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/stdio.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/stream.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/stream_arg.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/thread_status.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_thread/yield.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/after.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/after_fork.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/bug_2342.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/bug_4794.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/bug_4794_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/bug_4794_3.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/bug_4794_4.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/bug_4794_5.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/bug_4794_6.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/bug_4794_7.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/bug_4794_8.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/call_private.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/callback_bug_with_array.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/clearAll.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/enable_coroutine.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/enable_coroutine2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/function_alias.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/greater_than_0.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/info.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/list.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/manager.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/master.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/master_base.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/memory.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/next_round.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/not_exist.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/reinit_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/reinit_2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/stats.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/swoole_timer_list_alias.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/task_worker.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/task_worker_tick_1k.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_timer/verify.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/bug_1.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/close_frame_flag.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/close_frame_full.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/compression.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/disconnect.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/disconnect_with_code.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/dynamic_property.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/empty_message.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/exists_and_isEstablished.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/fin.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/fin2.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/get_large_requests.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/get_small_requests.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/greeter.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/header_token.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/listener.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/malformed_data.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/memory.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/message_size.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/onDisconnct.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/pack.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/ping.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/pingloop.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/pingloop_open_ping_pong_frame.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/pingpong.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/pingpong_open_ping_pong_frame.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/query.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/recv_decode.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/send_close_frame.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/send_encode.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/send_encode_async.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/set_cookie_on_before_handshake_response.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/set_cookie_on_handshake.phpt\" />\n            <file role=\"test\" name=\"tests/swoole_websocket_server/websocket_compress_on_handshake.phpt\" />\n            <file role=\"test\" name=\"tests/template\" />\n            <file role=\"test\" name=\"tests/test.sql\" />\n            <file role=\"doc\" name=\"thirdparty/boost/asm/LICENSE\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/combined.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/jump_combined_sysv_macho_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/jump_loongarch64_sysv_elf_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/jump_mips64_n64_elf_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/jump_sparc64_sysv_elf_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/make_combined_sysv_macho_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/make_loongarch64_sysv_elf_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/make_mips64_n64_elf_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/make_riscv64_sysv_elf_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/make_sparc64_sysv_elf_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S\" />\n            <file role=\"src\" name=\"thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S\" />\n            <file role=\"doc\" name=\"thirdparty/hiredis/CHANGELOG.md\" />\n            <file role=\"doc\" name=\"thirdparty/hiredis/COPYING\" />\n            <file role=\"doc\" name=\"thirdparty/hiredis/README.md\" />\n            <file role=\"src\" name=\"thirdparty/hiredis/alloc.c\" />\n            <file role=\"src\" name=\"thirdparty/hiredis/alloc.h\" />\n            <file role=\"src\" name=\"thirdparty/hiredis/fmacros.h\" />\n            <file role=\"src\" name=\"thirdparty/hiredis/hiredis.c\" />\n            <file role=\"src\" name=\"thirdparty/hiredis/hiredis.h\" />\n            <file role=\"src\" name=\"thirdparty/hiredis/net.c\" />\n            <file role=\"src\" name=\"thirdparty/hiredis/net.h\" />\n            <file role=\"src\" name=\"thirdparty/hiredis/read.c\" />\n            <file role=\"src\" name=\"thirdparty/hiredis/read.h\" />\n            <file role=\"src\" name=\"thirdparty/hiredis/sds.c\" />\n            <file role=\"src\" name=\"thirdparty/hiredis/sds.h\" />\n            <file role=\"src\" name=\"thirdparty/hiredis/sdsalloc.h\" />\n            <file role=\"doc\" name=\"thirdparty/llhttp/LICENSE\" />\n            <file role=\"doc\" name=\"thirdparty/llhttp/LICENSE-MIT\" />\n            <file role=\"src\" name=\"thirdparty/llhttp/api.c\" />\n            <file role=\"src\" name=\"thirdparty/llhttp/http.c\" />\n            <file role=\"src\" name=\"thirdparty/llhttp/llhttp.c\" />\n            <file role=\"src\" name=\"thirdparty/llhttp/llhttp.h\" />\n            <file role=\"src\" name=\"thirdparty/multipart_parser.c\" />\n            <file role=\"src\" name=\"thirdparty/multipart_parser.h\" />\n            <file role=\"doc\" name=\"thirdparty/nghttp2/COPYING\" />\n            <file role=\"doc\" name=\"thirdparty/nghttp2/LICENSE\" />\n            <file role=\"src\" name=\"thirdparty/nghttp2/nghttp2.h\" />\n            <file role=\"src\" name=\"thirdparty/nghttp2/nghttp2_buf.c\" />\n            <file role=\"src\" name=\"thirdparty/nghttp2/nghttp2_buf.h\" />\n            <file role=\"src\" name=\"thirdparty/nghttp2/nghttp2_hd.c\" />\n            <file role=\"src\" name=\"thirdparty/nghttp2/nghttp2_hd.h\" />\n            <file role=\"src\" name=\"thirdparty/nghttp2/nghttp2_hd_huffman.c\" />\n            <file role=\"src\" name=\"thirdparty/nghttp2/nghttp2_hd_huffman.h\" />\n            <file role=\"src\" name=\"thirdparty/nghttp2/nghttp2_hd_huffman_data.c\" />\n            <file role=\"src\" name=\"thirdparty/nghttp2/nghttp2_helper.c\" />\n            <file role=\"src\" name=\"thirdparty/nghttp2/nghttp2_mem.c\" />\n            <file role=\"src\" name=\"thirdparty/nghttp2/nghttp2_mem.h\" />\n            <file role=\"src\" name=\"thirdparty/nghttp2/nghttp2_rcbuf.c\" />\n            <file role=\"src\" name=\"thirdparty/nghttp2/nghttp2_rcbuf.h\" />\n            <file role=\"src\" name=\"thirdparty/nghttp2/nghttp2ver.h\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/LICENSE.MIT\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/adl_serializer.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/conversions/from_json.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/conversions/to_chars.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/conversions/to_json.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/exceptions.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/input/binary_reader.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/input/input_adapters.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/input/json_sax.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/input/lexer.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/input/parser.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/input/position_t.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/iterators/internal_iterator.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/iterators/iter_impl.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/iterators/iteration_proxy.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/iterators/iterator_traits.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/iterators/json_reverse_iterator.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/iterators/primitive_iterator.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/json_pointer.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/json_ref.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/macro_scope.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/macro_unscope.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/meta/cpp_future.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/meta/detected.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/meta/is_sax.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/meta/type_traits.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/meta/void_t.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/output/binary_writer.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/output/output_adapters.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/output/serializer.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/detail/value_t.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/json.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/json_fwd.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/thirdparty/hedley/hedley.hpp\" />\n            <file role=\"src\" name=\"thirdparty/nlohmann/thirdparty/hedley/hedley_undef.hpp\" />\n            <file role=\"doc\" name=\"thirdparty/pdo_oci/CREDITS\" />\n            <file role=\"doc\" name=\"thirdparty/pdo_oci/README.md\" />\n            <file role=\"src\" name=\"thirdparty/pdo_oci/oci_driver.c\" />\n            <file role=\"src\" name=\"thirdparty/pdo_oci/oci_statement.c\" />\n            <file role=\"src\" name=\"thirdparty/pdo_oci/pdo_oci.c\" />\n            <file role=\"src\" name=\"thirdparty/pdo_oci/php_pdo_oci_int.h\" />\n            <file role=\"src\" name=\"thirdparty/pdo_sqlite/php_pdo_sqlite.h\" />\n            <file role=\"src\" name=\"thirdparty/pdo_sqlite/php_pdo_sqlite_int.h\" />\n            <file role=\"src\" name=\"thirdparty/pdo_sqlite/sqlite_driver.c\" />\n            <file role=\"src\" name=\"thirdparty/pdo_sqlite/sqlite_sql_parser.c\" />\n            <file role=\"src\" name=\"thirdparty/pdo_sqlite/sqlite_statement.c\" />\n            <file role=\"doc\" name=\"thirdparty/php/LICENSE\" />\n            <file role=\"src\" name=\"thirdparty/php/curl/curl_arginfo.h\" />\n            <file role=\"src\" name=\"thirdparty/php/curl/curl_private.h\" />\n            <file role=\"src\" name=\"thirdparty/php/curl/interface.cc\" />\n            <file role=\"src\" name=\"thirdparty/php/curl/multi.cc\" />\n            <file role=\"src\" name=\"thirdparty/php/curl/php_curl.h\" />\n            <file role=\"src\" name=\"thirdparty/php/main/SAPI.h\" />\n            <file role=\"src\" name=\"thirdparty/php/sockets/conversions.cc\" />\n            <file role=\"src\" name=\"thirdparty/php/sockets/conversions.h\" />\n            <file role=\"src\" name=\"thirdparty/php/sockets/multicast.cc\" />\n            <file role=\"src\" name=\"thirdparty/php/sockets/multicast.h\" />\n            <file role=\"src\" name=\"thirdparty/php/sockets/php_sockets_cxx.h\" />\n            <file role=\"src\" name=\"thirdparty/php/sockets/sendrecvmsg.cc\" />\n            <file role=\"src\" name=\"thirdparty/php/sockets/sockaddr_conv.cc\" />\n            <file role=\"doc\" name=\"thirdparty/php/ssh2/LICENSE\" />\n            <file role=\"src\" name=\"thirdparty/php/ssh2/php_ssh2.h\" />\n            <file role=\"src\" name=\"thirdparty/php/ssh2/ssh2.cc\" />\n            <file role=\"src\" name=\"thirdparty/php/ssh2/ssh2_fopen_wrappers.cc\" />\n            <file role=\"src\" name=\"thirdparty/php/ssh2/ssh2_sftp.cc\" />\n            <file role=\"src\" name=\"thirdparty/php/standard/proc_open.cc\" />\n            <file role=\"src\" name=\"thirdparty/php/standard/proc_open.h\" />\n            <file role=\"src\" name=\"thirdparty/php/standard/var_decoder.cc\" />\n            <file role=\"src\" name=\"thirdparty/php/streams/php_streams_int.h\" />\n            <file role=\"src\" name=\"thirdparty/php/streams/plain_wrapper.c\" />\n            <file role=\"src\" name=\"thirdparty/php/zend/zend_execute.c\" />\n            <file role=\"src\" name=\"thirdparty/php82/pdo_odbc/odbc_driver.c\" />\n            <file role=\"src\" name=\"thirdparty/php82/pdo_odbc/odbc_stmt.c\" />\n            <file role=\"src\" name=\"thirdparty/php82/pdo_odbc/php_pdo_odbc_int.h\" />\n            <file role=\"src\" name=\"thirdparty/php82/pdo_pgsql/pgsql_driver.c\" />\n            <file role=\"src\" name=\"thirdparty/php82/pdo_pgsql/pgsql_driver_arginfo.h\" />\n            <file role=\"src\" name=\"thirdparty/php82/pdo_pgsql/pgsql_statement.c\" />\n            <file role=\"src\" name=\"thirdparty/php82/pdo_pgsql/php_pdo_pgsql_int.h\" />\n            <file role=\"src\" name=\"thirdparty/php83/pdo_odbc/odbc_driver.c\" />\n            <file role=\"src\" name=\"thirdparty/php83/pdo_odbc/odbc_stmt.c\" />\n            <file role=\"src\" name=\"thirdparty/php83/pdo_odbc/php_pdo_odbc_int.h\" />\n            <file role=\"src\" name=\"thirdparty/php83/pdo_pgsql/pgsql_driver.c\" />\n            <file role=\"src\" name=\"thirdparty/php83/pdo_pgsql/pgsql_driver_arginfo.h\" />\n            <file role=\"src\" name=\"thirdparty/php83/pdo_pgsql/pgsql_statement.c\" />\n            <file role=\"src\" name=\"thirdparty/php83/pdo_pgsql/php_pdo_pgsql_int.h\" />\n            <file role=\"src\" name=\"thirdparty/php84/curl/curl_arginfo.h\" />\n            <file role=\"src\" name=\"thirdparty/php84/curl/curl_private.h\" />\n            <file role=\"src\" name=\"thirdparty/php84/curl/interface.cc\" />\n            <file role=\"src\" name=\"thirdparty/php84/curl/multi.cc\" />\n            <file role=\"src\" name=\"thirdparty/php84/curl/php_curl.h\" />\n            <file role=\"doc\" name=\"thirdparty/php84/ftp/CREDITS\" />\n            <file role=\"src\" name=\"thirdparty/php84/ftp/ftp.c\" />\n            <file role=\"src\" name=\"thirdparty/php84/ftp/ftp.h\" />\n            <file role=\"src\" name=\"thirdparty/php84/ftp/php_ftp.c\" />\n            <file role=\"src\" name=\"thirdparty/php84/ftp/php_ftp.h\" />\n            <file role=\"doc\" name=\"thirdparty/php84/pdo_firebird/CREDITS\" />\n            <file role=\"src\" name=\"thirdparty/php84/pdo_firebird/firebird_driver.c\" />\n            <file role=\"src\" name=\"thirdparty/php84/pdo_firebird/firebird_statement.c\" />\n            <file role=\"src\" name=\"thirdparty/php84/pdo_firebird/pdo_firebird_utils.cpp\" />\n            <file role=\"src\" name=\"thirdparty/php84/pdo_firebird/pdo_firebird_utils.h\" />\n            <file role=\"src\" name=\"thirdparty/php84/pdo_firebird/php_pdo_firebird_int.h\" />\n            <file role=\"src\" name=\"thirdparty/php84/pdo_odbc/odbc_driver.c\" />\n            <file role=\"src\" name=\"thirdparty/php84/pdo_odbc/odbc_stmt.c\" />\n            <file role=\"src\" name=\"thirdparty/php84/pdo_odbc/php_pdo_odbc_int.h\" />\n            <file role=\"src\" name=\"thirdparty/php84/pdo_pgsql/pgsql_driver.c\" />\n            <file role=\"src\" name=\"thirdparty/php84/pdo_pgsql/pgsql_driver_arginfo.h\" />\n            <file role=\"src\" name=\"thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c\" />\n            <file role=\"src\" name=\"thirdparty/php84/pdo_pgsql/pgsql_statement.c\" />\n            <file role=\"src\" name=\"thirdparty/php84/pdo_pgsql/php_pdo_pgsql_int.h\" />\n            <file role=\"doc\" name=\"thirdparty/php85/pdo_firebird/CREDITS\" />\n            <file role=\"src\" name=\"thirdparty/php85/pdo_firebird/firebird_driver.c\" />\n            <file role=\"src\" name=\"thirdparty/php85/pdo_firebird/firebird_statement.c\" />\n            <file role=\"src\" name=\"thirdparty/php85/pdo_firebird/pdo_firebird_utils.cpp\" />\n            <file role=\"src\" name=\"thirdparty/php85/pdo_firebird/pdo_firebird_utils.h\" />\n            <file role=\"src\" name=\"thirdparty/php85/pdo_firebird/php_pdo_firebird_int.h\" />\n            <file role=\"src\" name=\"thirdparty/php85/pdo_odbc/odbc_driver.c\" />\n            <file role=\"src\" name=\"thirdparty/php85/pdo_odbc/odbc_stmt.c\" />\n            <file role=\"src\" name=\"thirdparty/php85/pdo_odbc/php_pdo_odbc.h\" />\n            <file role=\"src\" name=\"thirdparty/php85/pdo_odbc/php_pdo_odbc_int.h\" />\n            <file role=\"src\" name=\"thirdparty/php85/pdo_pgsql/pdo_pgsql_arginfo.h\" />\n            <file role=\"src\" name=\"thirdparty/php85/pdo_pgsql/pgsql_driver.c\" />\n            <file role=\"src\" name=\"thirdparty/php85/pdo_pgsql/pgsql_driver_arginfo.h\" />\n            <file role=\"src\" name=\"thirdparty/php85/pdo_pgsql/pgsql_sql_parser.c\" />\n            <file role=\"src\" name=\"thirdparty/php85/pdo_pgsql/pgsql_statement.c\" />\n            <file role=\"src\" name=\"thirdparty/php85/pdo_pgsql/php_pdo_pgsql.h\" />\n            <file role=\"src\" name=\"thirdparty/php85/pdo_pgsql/php_pdo_pgsql_int.h\" />\n            <file role=\"src\" name=\"tools/analysis.php\" />\n            <file role=\"src\" name=\"tools/arginfo-check.php\" />\n            <file role=\"src\" name=\"tools/bootstrap.php\" />\n            <file role=\"src\" name=\"tools/build-library.php\" />\n            <file role=\"src\" name=\"tools/code-generator.php\" />\n            <file role=\"src\" name=\"tools/composer.json\" />\n            <file role=\"src\" name=\"tools/constant-generator.php\" />\n            <file role=\"src\" name=\"tools/export.php\" />\n            <file role=\"src\" name=\"tools/gen-data.php\" />\n            <file role=\"src\" name=\"tools/gen-ext-class.php\" />\n            <file role=\"src\" name=\"tools/get-ip-info.php\" />\n            <file role=\"src\" name=\"tools/next-version.php\" />\n            <file role=\"src\" name=\"tools/option-generator.php\" />\n            <file role=\"src\" name=\"tools/pecl-package.php\" />\n            <file role=\"src\" name=\"tools/phpt-fixer.php\" />\n            <file role=\"src\" name=\"tools/rename.php\" />\n            <file role=\"src\" name=\"tools/send-http-data.php\" />\n            <file role=\"src\" name=\"tools/show-big-files.php\" />\n            <file role=\"src\" name=\"tools/templates/class.c\" />\n            <file role=\"src\" name=\"tools/templates/version.tpl.h\" />\n        </dir>\n    </contents>\n    <dependencies>\n        <required>\n            <php>\n                <min>8.2.0</min>\n                <max>8.5.99</max>\n            </php>\n            <pearinstaller>\n                <min>1.4.0</min>\n            </pearinstaller>\n        </required>\n    </dependencies>\n    <providesextension>swoole</providesextension>\n    <extsrcrelease>\n        <configureoption default=\"no\" name=\"enable-sockets\" prompt=\"enable sockets support?\"/>\n        <configureoption default=\"no\" name=\"with-openssl-dir\" prompt=\"specify openssl installation directory (requires openssl 1.1.0 or later)?\"/>\n        <configureoption default=\"no\" name=\"enable-mysqlnd\" prompt=\"enable mysqlnd support?\"/>\n        <configureoption default=\"no\" name=\"enable-swoole-curl\" prompt=\"enable curl support?\"/>\n        <configureoption default=\"no\" name=\"enable-cares\" prompt=\"enable cares support?\"/>\n        <configureoption default=\"yes\" name=\"enable-brotli\" prompt=\"enable brotli support?\"/>\n        <configureoption default=\"no\" name=\"with-brotli-dir\" prompt=\"specify brotli installation directory?\"/>\n        <configureoption default=\"no\" name=\"enable-zstd\" prompt=\"enable zstd support (requires zstd 1.4.0 or later)?\"/>\n        <configureoption default=\"no\" name=\"enable-swoole-pgsql\" prompt=\"enable PostgreSQL database support?\"/>\n        <configureoption default=\"no\" name=\"with-swoole-odbc\" prompt=\"enable ODBC database support?\"/>\n        <configureoption default=\"no\" name=\"with-swoole-oracle\" prompt=\"enable Oracle database support?\"/>\n        <configureoption default=\"no\" name=\"enable-swoole-sqlite\" prompt=\"enable Sqlite database support?\"/>\n        <configureoption default=\"no\" name=\"with-swoole-firebird\" prompt=\"enable Firebird database support?\"/>\n        <configureoption default=\"no\" name=\"enable-swoole-thread\" prompt=\"enable swoole thread support (need php zts support)?\"/>\n        <configureoption default=\"no\" name=\"enable-iouring\" prompt=\"enable iouring for file async support?\"/>\n        <configureoption default=\"no\" name=\"with-liburing-dir\" prompt=\"specify liburing installation directory (requires liburing 2.8 or later)?\"/>\n        <configureoption default=\"no\" name=\"enable-uring-socket\" prompt=\"enable iouring for http coroutine server support?\"/>\n        <configureoption default=\"no\" name=\"with-swoole-ssh2\" prompt=\"enable async ssh2 client support?\"/>\n        <configureoption default=\"no\" name=\"enable-swoole-ftp\" prompt=\"enable async ftp client support?\"/>\n    </extsrcrelease>\n</package>\n"
  },
  {
    "path": "php-cs-fix",
    "content": "#/bin/bash\n./tests/include/lib/vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --using-cache=no --verbose $1\n"
  },
  {
    "path": "php_swoole.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef PHP_SWOOLE_H\n#define PHP_SWOOLE_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"php_globals.h\"\n#include \"php_main.h\"\n\n#include \"php_streams.h\"\n#include \"php_network.h\"\n\n#include \"zend_variables.h\"\n#include \"zend_interfaces.h\"\n#include \"zend_closures.h\"\n#include \"zend_exceptions.h\"\n#include \"zend_attributes.h\"\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\nextern zend_module_entry swoole_module_entry;\n#define phpext_swoole_ptr &swoole_module_entry\n\nPHP_MINIT_FUNCTION(swoole);\nPHP_MSHUTDOWN_FUNCTION(swoole);\nPHP_RINIT_FUNCTION(swoole);\nPHP_RSHUTDOWN_FUNCTION(swoole);\nPHP_MINFO_FUNCTION(swoole);\n\n// clang-format off\nZEND_BEGIN_MODULE_GLOBALS(swoole)\n    zend_bool display_errors;\n    zend_bool cli;\n    zend_bool use_shortname;\n    zend_bool enable_preemptive_scheduler;\n    zend_bool enable_library;\n    zend_bool enable_fiber_mock;\n    zend_bool blocking_detection;\n    zend_long blocking_threshold;\n    zend_bool profile;\n    zend_bool leak_detection;\n    zend_long socket_buffer_size;\n    int req_status;\n    HashTable *in_autoload;\nZEND_END_MODULE_GLOBALS(swoole)\n// clang-format on\n\nextern ZEND_DECLARE_MODULE_GLOBALS(swoole);\n\n#ifdef ZTS\n#define SWOOLE_G(v) TSRMG(swoole_globals_id, zend_swoole_globals *, v)\n#else\n#define SWOOLE_G(v) (swoole_globals.v)\n#endif\n\n#endif /* PHP_SWOOLE_H */\n"
  },
  {
    "path": "php_swoole_api.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef PHP_SWOOLE_API_H\n#define PHP_SWOOLE_API_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"php.h\"\n#include \"php_network.h\"\n\nint php_async_socket_connect_to_host(const char *host,\n                                     unsigned short port,\n                                     int socktype,\n                                     int asynchronous,\n                                     struct timeval *timeout,\n                                     zend_string **error_string,\n                                     int *error_code,\n                                     const char *bindto,\n                                     unsigned short bindport,\n                                     long sockopts);\n\nint php_async_socket_poll(php_socket_t fd, int events, int timeout);\n\n#if PHP_VERSION_ID < 80300\nstatic inline const char *zend_zval_value_name(const zval *arg) {\n\tZVAL_DEREF(arg);\n\n\tif (Z_ISUNDEF_P(arg)) {\n\t\treturn \"null\";\n\t}\n\n\tif (Z_TYPE_P(arg) == IS_OBJECT) {\n\t\treturn ZSTR_VAL(Z_OBJCE_P(arg)->name);\n\t} else if (Z_TYPE_P(arg) == IS_FALSE) {\n\t\treturn \"false\";\n\t} else if  (Z_TYPE_P(arg) == IS_TRUE) {\n\t\treturn \"true\";\n\t}\n\n\treturn zend_get_type_by_const(Z_TYPE_P(arg));\n}\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* PHP_SWOOLE_API_H */\n"
  },
  {
    "path": "run-core-tests.sh",
    "content": "#!/bin/bash\n__DIR__=$(cd \"$(dirname \"$0\")\" || exit;pwd)\n\nexport ASAN_OPTIONS=detect_leaks=0\nsudo sysctl -w kernel.randomize_va_space=0\n\nipcs -q\n\ncd \"${__DIR__}\"/core-tests/js || exit 1\nnpm install\ncd \"${__DIR__}\" || exit 1\n\ntasks=$(./bin/core-tests --gtest_list_tests | awk '/\\./') || exit 255\nfor task in $tasks; do\n    execute_command=\"./bin/core-tests\"\n\n    if [ \"$task\" = \"log.\" ]; then\n        $execute_command --gtest_filter=\"$task\"*\n    else\n        sudo -E \"$execute_command\" --gtest_filter=\"$task\"*\n    fi\n\n    if [ $? -ne 0 ]; then\n        exit 255\n    fi\ndone\n"
  },
  {
    "path": "scripts/.gitignore",
    "content": "data\n"
  },
  {
    "path": "scripts/clear.sh",
    "content": "#!/bin/sh\n__DIR__=$(cd \"$(dirname \"$0\")\";pwd)\n__SRC_DIR__=$(cd \"$(dirname \"${__DIR__}\")\";pwd)\n\nset -e\ncd \"${__SRC_DIR__}\"\nset +e\nfind . \\( -name \\*.gcno -o -name \\*.gcda \\) -print0 | xargs -0 rm -f\nfind . \\( -name \\*.lo -o -name \\*.o \\) -print0 | xargs -0 rm -f\nfind . \\( -name \\*.la -o -name \\*.a \\) -print0 | xargs -0 rm -f\nfind . \\( -name \\*.dep \\) -print0 | xargs -0 rm -f\nfind . -name \\*.so -print0 | xargs -0 rm -f\nfind . -name .libs -a -type d -print0 | xargs -0 rm -rf\nrm -f libphp.la modules/* libs/*\n"
  },
  {
    "path": "scripts/code-format.sh",
    "content": "#!/bin/sh\n__DIR__=$(cd \"$(dirname \"$0\")\";pwd)\n__SRC_DIR__=$(cd \"$(dirname \"${__DIR__}\")\";pwd)\n\ncd $__SRC_DIR__\n\n## core source file\nclang-format -i src/core/*.cc\nclang-format -i src/coroutine/*.cc\nclang-format -i src/lock/*.cc\nclang-format -i src/memory/*.cc\nclang-format -i src/network/*.cc\nclang-format -i src/os/*.cc\nclang-format -i src/pipe/*.cc\nclang-format -i src/protocol/*.cc\nclang-format -i src/reactor/*.cc\nclang-format -i src/server/*.cc\nclang-format -i src/wrapper/*.cc\n## core header file\nclang-format -i include/*.h\n\n## ext source file\nclang-format -i *.cc\nclang-format -i *.h\n\n## examples\nclang-format -i examples/cpp/*.cc\n\n## core-tests source file\nclang-format -i core-tests/src/_lib/*.cpp\nclang-format -i core-tests/src/client/*.cpp\nclang-format -i core-tests/src/core/*.cpp\nclang-format -i core-tests/src/coroutine/*.cpp\nclang-format -i core-tests/src/lock/*.cpp\nclang-format -i core-tests/src/memory/*.cpp\nclang-format -i core-tests/src/network/*.cpp\nclang-format -i core-tests/src/os/*.cpp\nclang-format -i core-tests/src/process/*.cpp\nclang-format -i core-tests/src/protocol/*.cpp\nclang-format -i core-tests/src/reactor/*.cpp\nclang-format -i core-tests/src/server/*.cpp\nclang-format -i core-tests/src/main.cpp\n"
  },
  {
    "path": "scripts/code-stats.sh",
    "content": "#!/bin/sh -e\n__CURRENT__=$(pwd)\n__DIR__=$(cd \"$(dirname \"$0\")\";pwd)\n\n# enter the dir\ncd \"${__DIR__}/..\"\ncloc . --exclude-dir=thirdparty,Debug,CMakeFiles,build,.git \\\n  --fullpath \\\n  --not-match-d='tools/vendor' \\\n  --not-match-d='tests/include/lib/vendor' \\\n  --not-match-f='ext-src/php_swoole_library\\.h$'\n"
  },
  {
    "path": "scripts/debug/swoole_info.php",
    "content": "<?php\nvar_dump([\n    'version' => swoole_version(),\n    'cpu_num' => swoole_cpu_num(),\n    'local_mac' => swoole_get_local_mac(),\n    'local_ip' => swoole_get_local_ip()\n]);\n"
  },
  {
    "path": "scripts/debug/swoole_table_implements.php",
    "content": "<?php\n$table = new Swoole\\Table(65536);\nvar_dump($table);\nvar_dump(class_implements($table));\n"
  },
  {
    "path": "scripts/docker-compile-with-iouring.sh",
    "content": "#!/bin/sh -e\n__CURRENT__=$(pwd)\n__DIR__=$(cd \"$(dirname \"$0\")\";pwd)\n\nif [ ! -f \"/.dockerenv\" ]; then\n    echo \"\" && echo \"❌ This script is just for Docker!\"\n    exit\nfi\n\nsh library.sh\n\ncd \"${__DIR__}\" && cd ..\n\n./scripts/clear.sh && phpize\nif [ -n \"$(php -v | grep \"ZTS\")\" ]; then\n   echo \"\" && echo \"🚀 php zts + swoole thread mode + iouring!\"\n  ./configure --enable-iouring --enable-swoole-thread\nelse\n  echo \"\" && echo \"🚀 php nts + swoole + iouring!\"\n  ./configure --enable-iouring\nfi\n\nmake -j$(cat /proc/cpuinfo | grep processor | wc -l)\nmake install\ndocker-php-ext-enable swoole\nphp -v\nphp -m\nphp --ri curl\nphp --ri swoole\n\n"
  },
  {
    "path": "scripts/docker-compile-with-thread.sh",
    "content": "#!/bin/sh -e\n__CURRENT__=$(pwd)\n__DIR__=$(cd \"$(dirname \"$0\")\";pwd)\n\nsh library.sh\n\nif [ ! -f \"/.dockerenv\" ]; then\n    echo \"\" && echo \"❌ This script is just for Docker!\"\n    exit\nfi\n\ncd \"${__DIR__}\" && cd ..\n./scripts/clear.sh\nphpize\n./configure \\\n--enable-brotli \\\n--enable-zstd \\\n--enable-sockets \\\n--enable-mysqlnd \\\n--enable-swoole-curl \\\n--enable-cares \\\n--enable-swoole-stdext \\\n--enable-swoole-pgsql \\\n--enable-swoole-thread \\\n--with-swoole-odbc=unixODBC,/usr \\\n--with-swoole-oracle=instantclient,/usr/local/instantclient \\\n--enable-swoole-sqlite\n\nmake -j$(cat /proc/cpuinfo | grep processor | wc -l)\nmake install\ndocker-php-ext-enable swoole\nphp -v\nphp -m\nphp --ri curl\nphp --ri swoole\n\n"
  },
  {
    "path": "scripts/docker-compile.sh",
    "content": "#!/bin/sh -e\n__CURRENT__=$(pwd)\n__DIR__=$(cd \"$(dirname \"$0\")\";pwd)\n\nif [ ! -f \"/.dockerenv\" ]; then\n    echo \"\" && echo \"❌ This script is just for Docker!\"\n    exit\nfi\n\nsh library.sh\ncd \"${__DIR__}/..\"\n./scripts/clear.sh\nphpize\n\noption=\"--enable-brotli \\\n       --enable-zstd \\\n       --enable-sockets \\\n       --enable-mysqlnd \\\n       --enable-swoole-curl \\\n       --enable-swoole-stdext \\\n       --enable-cares \\\n       --enable-swoole-pgsql \\\n       --with-swoole-firebird \\\n       --with-swoole-odbc=unixODBC,/usr \\\n       --with-swoole-oracle=instantclient,/usr/local/instantclient \\\n       --with-swoole-ssh2 \\\n       --enable-swoole-ftp \\\n       --enable-swoole-sqlite\"\n\nif [ \"$SWOOLE_THREAD\" = 1 ]; then\n  ./configure $option --enable-swoole-thread\nelif [ \"$SWOOLE_IOURING\" = 1 ]; then\n  if [ -n \"$(php -v | grep \"ZTS\")\" ]; then\n     echo \"\" && echo \"🚀 php zts + swoole thread mode + iouring!\"\n    ./configure --enable-iouring --enable-swoole-thread\n  else\n    echo \"\" && echo \"🚀 php nts + swoole + iouring!\"\n    ./configure --enable-iouring\n  fi\nelse\n  ./configure $option\nfi\n\nmake -j$(cat /proc/cpuinfo | grep processor | wc -l)\nmake install\ndocker-php-ext-enable swoole\nphp -v\nphp -m\nphp --ri swoole\n\n"
  },
  {
    "path": "scripts/docker-compose.yml",
    "content": "version: '3.4'\nservices:\n  swoole:\n    container_name: \"swoole\"\n    image: \"phpswoole/php:${PHP_VERSION}\"\n    volumes:\n      - \"${SWOOLE_BUILD_DIR}:/swoole-src:rw\"\n    working_dir: /swoole-src\n    ulimits:\n      core: -1\n    privileged: true\n    depends_on:\n      - mysql\n      - redis\n      - pgsql\n      - oracle\n    dns:\n      - 8.8.8.8\n      - 1.1.1.1\n    environment:\n      SWOOLE_BRANCH: \"${SWOOLE_BRANCH}\"\n    command: tail -f /etc/group\n  mysql:\n    container_name: \"mysql\"\n    image: \"mysql:latest\"\n    volumes:\n      - ./data/mysql:/var/lib/mysql:rw\n      - ./data/run/mysqld:/var/run/mysqld:rw\n    environment:\n      MYSQL_ROOT_PASSWORD: root\n      MYSQL_DATABASE: test\n      MYSQL_USER: swoole\n      MYSQL_PASSWORD: swoole\n  pgsql:\n    image: postgres:14\n    container_name: \"pgsql\"\n    environment:\n      POSTGRES_USER: root\n      POSTGRES_DB: test\n      POSTGRES_PASSWORD: root\n  oracle:\n    image: gvenzl/oracle-free:slim\n    container_name: \"oracle\"\n    environment:\n      ORACLE_PASSWORD: oracle\n    ports:\n      - \"1521:1521\"\n  redis:\n    container_name: \"redis\"\n    image: \"redis:latest\"\n    volumes:\n      - ./data/redis:/var/lib/redis:rw\n      - ./data/run/redis:/var/run/redis:rw\n    sysctls:\n        net.core.somaxconn: 65535\n  httpbin:\n    container_name: \"httpbin\"\n    image: \"arnaudlacour/httpbin\"\n  tinyproxy:\n    container_name: \"tinyproxy\"\n    image: \"kalaksi/tinyproxy\"\n  golang-h2demo:\n    container_name: \"golang-h2demo\"\n    image: \"phpswoole/golang-h2demo\"\n  socks5:\n    container_name: \"socks5\"\n    image: \"serjs/go-socks5-proxy\"\n    ports:\n      - \"1080:1080\"\n  ftp:\n    container_name: \"ftp\"\n    image: \"teezily/ftpd\"\n    environment:\n      FTP_USER: admin\n      FTP_PASSWORD: admin\n\n  firebirdsql:\n    container_name: \"firebirdsql\"\n    image: \"firebirdsql/firebird\"\n    environment:\n      - FIREBIRD_ROOT_PASSWORD=root\n      - FIREBIRD_USER=test\n      - FIREBIRD_PASSWORD=test\n      - FIREBIRD_DATABASE=test.fdb\n      - FIREBIRD_DATABASE_DEFAULT_CHARSET=UTF8\n    volumes:\n      - ./data:/var/lib/firebird/data\n    ports:\n      - \"3050:3050\"\n\n"
  },
  {
    "path": "scripts/docker-iouring-route.sh",
    "content": "#!/bin/sh -e\n__CURRENT__=$(pwd)\n__DIR__=$(cd \"$(dirname \"$0\")\";pwd)\nexport SWOOLE_USE_IOURING=1\n\n# enter the dir\ncd \"${__DIR__}\"\n\n# show system info\ndate && echo \"\"\nuname -a && echo \"\"\n\n# show php info\nphp -v && echo \"\"\n\n# compile in docker\necho \"\" && echo \"📦 Compile ext-swoole[iouring] in docker...\" && echo \"\"\n./docker-compile-with-iouring.sh\n\n# run unit tests\necho \"\" && echo \"📋 Run php tests[iouring] in docker...\" && echo \"\"\n./run-tests.sh\n\n"
  },
  {
    "path": "scripts/docker-route.sh",
    "content": "#!/bin/sh -e\n__CURRENT__=$(pwd)\n__DIR__=$(cd \"$(dirname \"$0\")\";pwd)\n\n# show system info and php info\ndate && echo \"\"\nuname -a && echo \"\"\nphp -v && echo \"\"\n\n# enter the dir\ncd \"${__DIR__}\"\n\nif [ \"$1\" = \"THREAD\" ]; then\n  export SWOOLE_THREAD=1\nelif [ \"$1\" = \"IOURING\" ]; then\n  export SWOOLE_IOURING=1\nfi\n\n# compile in docker\necho \"\" && echo \"📦 Compile test in docker...\" && echo \"\"\n./docker-compile.sh\n\n# run unit tests\necho \"\" && echo \"📋 PHP unit tests in docker...\" && echo \"\"\n./run-tests.sh\n"
  },
  {
    "path": "scripts/docker-thread-route.sh",
    "content": "#!/bin/sh -e\n__CURRENT__=$(pwd)\n__DIR__=$(cd \"$(dirname \"$0\")\";pwd)\nexport SWOOLE_THREAD=1\n\n# enter the dir\ncd \"${__DIR__}\"\n\n# show system info\ndate && echo \"\"\nuname -a && echo \"\"\n\n# show php info\nphp -v && echo \"\"\n\n# compile in docker\necho \"\" && echo \"📦 Compile ext-swoole[thread] in docker...\" && echo \"\"\n./docker-compile-with-thread.sh\n\n# run unit tests\necho \"\" && echo \"📋 Run phpt tests[thread] in docker...\" && echo \"\"\n./run-tests.sh\n"
  },
  {
    "path": "scripts/format-changed-files.sh",
    "content": "#!/bin/bash\n\n__CURRENT__=`pwd`\n__DIR__=$(cd \"$(dirname \"$0\")\" || exit;pwd)\n\ncpp_files=$(git status --porcelain | grep '^[ M]' | grep '\\.\\(cc\\|cpp\\|h\\)$' | awk '{print $2}')\nphp_files=$(git status --porcelain | grep '^[ M]' | grep '\\.\\(php\\|phpt\\)$' | awk '{print $2}')\narginfo_files=$(git status --porcelain | grep '^[ M]' | grep '_arginfo\\.h$' | awk '{print $2}')\n\nif [ -z \"$cpp_files\" ] && [ -z \"$php_files\" ]; then\n    echo \"No files to format.\"\n    exit 0\nfi\n\n# 格式化 C/C++ 文件\nif [ ! -z \"$cpp_files\" ]; then\n    echo \"Formatting C/C++ files...\"\n    for file in $cpp_files; do\n        # 额外检查确保不处理 _arginfo.h 文件\n        if [[ \"$file\" != *_arginfo.h && \"$file\" != \"ext-src/php_swoole_library.h\" ]]; then\n            echo \"  - $file\"\n            clang-format -i \"$file\"\n        fi\n    done\nfi\n\n# 格式化 PHP 和 PHPT 文件\nif [ -n \"$php_files\" ]; then\n    echo \"Formatting PHP files...\"\n\n    # 过滤掉 .stub.php 文件\n    filtered_files=$(echo \"$php_files\" | grep -v '\\.stub\\.php$')\n\n    if [ -n \"$filtered_files\" ]; then\n        echo \"$filtered_files\" | xargs -I {} bash -c '\n            file=\"{}\"\n            if [ -f \"$file\" ]; then\n                echo \"  ✓ Formatting: $file\"\n                \"'\"$__DIR__\"'/../tests/include/lib/vendor/bin/php-cs-fixer\" fix \"$file\"\n            fi\n        '\n    else\n        echo \"All files are stub files, skipping.\"\n    fi\nfi\n\n# 显示跳过的 _arginfo.h 文件\nif [ ! -z \"$arginfo_files\" ]; then\n    echo \"Skipped auto-generated files:\"\n    for file in $arginfo_files; do\n        echo \"  - $file (auto-generated)\"\n    done\nfi\n\n\necho \"✅ Formatting completed successfully!\"\n"
  },
  {
    "path": "scripts/install-deps-on-ubuntu.sh",
    "content": "apt install -y cmake make gcc g++ \\\n    iputils-ping \\\n    libc-ares-dev \\\n    libssl-dev \\\n    libcurl4-openssl-dev \\\n    libmariadb-dev \\\n    libaio-dev \\\n    zlib1g-dev \\\n    sqlite3 libsqlite3-dev \\\n    libbrotli-dev \\\n    libpq-dev \\\n    unixodbc-dev \\\n    firebird-dev \\\n    libzstd-dev \\\n    libssh2-1-dev \\\n    iproute2\n\n# The built-in liburing version of Ubuntu is 0.7, which is too low. We must install liburing through the source code\n# liburing-dev\n"
  },
  {
    "path": "scripts/install-liburing.sh",
    "content": "LIBURING_VERSION=2.13\nwget https://github.com/axboe/liburing/archive/refs/tags/liburing-${LIBURING_VERSION}.tar.gz\ntar zxf liburing-${LIBURING_VERSION}.tar.gz\ncd liburing-liburing-${LIBURING_VERSION} || exit\n./configure\nmake -j$(cat /proc/cpuinfo | grep processor | wc -l) install"
  },
  {
    "path": "scripts/library.sh",
    "content": "#!/bin/sh -e\n__CURRENT__=$(pwd)\n__DIR__=$(cd \"$(dirname \"$0\")\";pwd)\n\nif [ \"$(uname -m)\" = \"aarch64\" ]; then\n  arch=\"arm64\"\nelse\n  arch=\"x64\"\nfi\n\ncd \"${__DIR__}/\"\n\napt update\nbash ./install-deps-on-ubuntu.sh\n\n# sshd\napt install -y openssh-server\nservice ssh start\n\n# MariaDB ODBC Connector\nMARIADB_CONNECTOR_VERSION=3.1.22\nwget https://github.com/mariadb-corporation/mariadb-connector-odbc/archive/refs/tags/${MARIADB_CONNECTOR_VERSION}.tar.gz\ntar zxf ${MARIADB_CONNECTOR_VERSION}.tar.gz\nmkdir build\ncd build\ncmake ../mariadb-connector-odbc-${MARIADB_CONNECTOR_VERSION}/ -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCONC_WITH_UNIT_TESTS=Off -DCMAKE_INSTALL_PREFIX=/usr/local -DWITH_SSL=OPENSSL\ncmake --build . --config RelWithDebInfo\nmake install\necho '/usr/local/lib/mariadb/' > /etc/ld.so.conf.d/odbc-mariadb.conf\nldconfig\n\nwget -nv -O instantclient-basiclite-linux${arch}.zip https://download.oracle.com/otn_software/linux/instantclient/1930000/instantclient-basiclite-linux.${arch}-19.30.0.0.0dbru.zip\nunzip instantclient-basiclite-linux${arch}.zip && rm instantclient-basiclite-linux${arch}.zip\nwget -nv -O instantclient-sdk-linux${arch}.zip https://download.oracle.com/otn_software/linux/instantclient/1930000/instantclient-sdk-linux.${arch}-19.30.0.0.0dbru.zip\nunzip instantclient-sdk-linux${arch}.zip && rm instantclient-sdk-linux${arch}.zip\nmv instantclient_*_* ./instantclient\nrm ./instantclient/sdk/include/ldap.h\n# fix debug build warning: zend_signal: handler was replaced for signal (2) after startup\necho DISABLE_INTERRUPT=on > ./instantclient/network/admin/sqlnet.ora\nmv ./instantclient /usr/local/\necho '/usr/local/instantclient' > /etc/ld.so.conf.d/oracle-instantclient.conf\nldconfig\n\ncd \"${__DIR__}/\"\nbash ./install-liburing.sh\n\ncd -\n"
  },
  {
    "path": "scripts/make.sh",
    "content": "#!/bin/sh\n__CURRENT_DIR__=$(cd \"$(dirname \"$0\")\";pwd)\n__DIR__=$(cd \"$(dirname \"${__CURRENT_DIR__}\")\";pwd)\n__HAVE_ZTS__=$(php -v|grep ZTS)\n\nCOMPILE_PARAMS=\" \\\n--enable-sockets \\\n--enable-mysqlnd \\\n--enable-swoole-curl \\\n--enable-cares \\\n--enable-zstd \\\n--enable-swoole-pgsql \\\n--enable-swoole-stdext \\\n--with-swoole-firebird \\\n--enable-uring-socket \\\n--with-swoole-ssh2 \\\n--enable-swoole-ftp \\\n--with-swoole-odbc=unixODBC,/usr \\\n--enable-swoole-sqlite\"\n\nTEMP=$(getopt -o ad --long asan,debug:,oci -n \"$0\" -- \"$@\")\n\nif [ $? != 0 ]; then\n  echo \"Parameter parsing failed!\" >&2\n  exit 1\nfi\n\neval set -- \"$TEMP\"\n\nwhile true; do\n  case \"$1\" in\n    -a|--asan)\n      ASAN=true\n      shift\n      ;;\n    -d|--debug)\n      DEBUG=true\n      shift\n      ;;\n    --oci)\n      OCI=true\n      shift\n      ;;\n    --)\n      shift\n      break\n      ;;\n    *)\n      echo \"Unsupported parameters!\"\n      exit 1\n      ;;\n  esac\ndone\n\nif [ \"$ASAN\" = true ]; then\n      COMPILE_PARAMS=\"$COMPILE_PARAMS --enable-asan\"\nfi\n\nif [ \"$DEBUG\" = true ]; then\n      COMPILE_PARAMS=\"$COMPILE_PARAMS --enable-debug\"\nfi\n\nif [ \"$OCI\" = true ]; then\n      COMPILE_PARAMS=\"$COMPILE_PARAMS --with-swoole-oracle=instantclient,/usr/local/instantclient\"\nfi\n\nif [ -n \"$__HAVE_ZTS__\" ]; then\n    COMPILE_PARAMS=\"$COMPILE_PARAMS --enable-swoole-thread\"\nfi\n\nif [ \"$(uname)\" = \"Linux\" ]; then\n    COMPILE_PARAMS=\"$COMPILE_PARAMS --enable-iouring\"\nfi\n\nif [ \"$(uname | grep -i darwin)\"x != \"\"x ]; then\n  CPU_COUNT=\"$(sysctl -n machdep.cpu.core_count)\"\nelse\n  CPU_COUNT=\"$(/usr/bin/nproc)\"\nfi\nif [ -z ${CPU_COUNT} ]; then\n  CPU_COUNT=4\nfi\n\ncd \"${__DIR__}\"\n\nif [ \"$1\" = \"cmake\" ] ;then\n  phpize\n  ./configure ${COMPILE_PARAMS}\n  cmake .\n  make -j ${CPU_COUNT}\n  make install\n  exit 0\nfi\n\nif [ \"$1\" = \"clean\" ] ;then\n  make clean\n  phpize --clean\n  exit 0\nfi\n\nif [ \"$1\" = \"install-module\" ] ;then\n  make ext-swoole\n  __EXT_DIR__=$(php-config --extension-dir)\n  cp lib/swoole.so \"${__EXT_DIR__}\"\n  echo \"cp lib/swoole.so ${__EXT_DIR__}\"\n  exit 0\nfi\n\nif [ \"$1\" = \"library\" ] ;then\n  set -e\n  cd ${__DIR__}\n  set +e\n  echo \"rm ext-src/php_swoole.lo\"\n  rm -f ext-src/php_swoole.lo\n  echo \"rm ext-src/php_swoole_library.h\"\n  rm -f ext-src/php_swoole_library.h\n  set -e\n\n  if [ \"$2\" = \"dev\" ] ;then\n    /usr/bin/env php tools/build-library.php dev\n  else\n    /usr/bin/env php tools/build-library.php\n  fi\n\n  echo \"remake...\"\n  make\n  echo \"done\"\n  exit 0\nfi\n\nif [ \"$1\" = \"help\" ] ;then\n  echo \"./make.sh cmake\"\n  echo \"./make.sh install-module\"\n  echo \"./make.sh clean\"\n  echo \"./make.sh debug\"\n  echo \"./make.sh trace\"\n  echo \"./make.sh library [dev]\"\n  echo \"./make.sh\"\n  exit 0\nfi\n\nphpize\n\nif [ \"$1\" = \"debug\" ] ;then\n  ./configure ${COMPILE_PARAMS} --enable-debug-log\nelif [ \"$1\" = \"trace\" ] ;then\n  ./configure ${COMPILE_PARAMS} --enable-trace-log\nelif [ \"$1\" = \"config\" ] ;then\n  ./configure ${COMPILE_PARAMS}\n  exit 0\nelse\n  ./configure ${COMPILE_PARAMS}\nfi\n\nmake clean\nmake -j ${CPU_COUNT}\nmake install\n"
  },
  {
    "path": "scripts/pecl-install.sh",
    "content": "#!/bin/sh -e\n__CURRENT__=`pwd`\n__DIR__=$(cd \"$(dirname \"$0\")\";pwd)\n\ncd ${__DIR__} && cd ../ && \\\npecl config-show && \\\nphp tools/pecl-package.php && package_file=\"`ls | grep swoole-*tgz`\" && \\\necho \"\\n\" | pecl install -f ${package_file} | tee pecl.log && \\\ncat pecl.log | grep \"successfully\" && \\\nphp -d extension=swoole --ri swoole && \\\npecl uninstall swoole && \\\nrm -f pecl.log\n"
  },
  {
    "path": "scripts/rename.php",
    "content": "<?php\n\n$match = $argv[1];\n$files = glob($match);\nvar_dump($files);\n\n$regx = '/' . str_replace('*', '(.+)', $match) . '/';\n\n$ext = pathinfo($regx, PATHINFO_EXTENSION);\n\nforeach ($files as $file) {\n    if (is_file($file)) {\n        preg_match($regx, $file, $matches);\n        $newName = $matches[1] . '.' . $ext;\n        `git mv $file $newName`;\n    }\n}\n"
  },
  {
    "path": "scripts/route.sh",
    "content": "#!/bin/sh\n__CURRENT__=`pwd`\n__DIR__=$(cd \"$(dirname \"$0\")\";pwd)\n\nexport DOCKER_COMPOSE_VERSION=\"v2.33.1\"\nif [ \"${SWOOLE_BRANCH}\" = \"alpine\" ]; then\n    export PHP_VERSION=\"${PHP_VERSION}-alpine\"\nfi\n\necho \"\\n🗻 With PHP version ${PHP_VERSION} on ${SWOOLE_BRANCH} branch\"\n\ncheck_docker_dependency(){\n    if [ \"`docker -v 2>&1 | grep \"version\"`\"x = \"\"x ]; then\n        echo \"\\n❌ Docker not found!\"\n        exit 1\n    elif [ \"`docker ps 2>&1 | grep Cannot`\"x != \"\"x ]; then\n        echo \"\\n❌ Docker is not running!\"\n        exit 1\n    else\n        which \"docker-compose\" > /dev/null\n        if [ $? -ne 0 ]; then\n            echo \"\\n🤔 Can not found docker-compose, try to install it now...\\n\"\n            curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose && \\\n            chmod +x docker-compose && \\\n            sudo mv docker-compose /usr/local/bin\n\n            which \"docker-compose\" > /dev/null\n            if [ $? -ne 0 ]; then\n                echo \"\\n❌ Install docker-compose failed!\"\n                exit 1\n            fi\n\n            docker -v &&  docker-compose -v\n        fi\n    fi\n}\n\ncreate_docker_images(){\n  arch=`uname -m`\n  if [ \"$arch\" = \"aarch64\" ]; then\n      echo \"\\n 📢 create golang-h2demo aarch64 docker image\"\n      git clone https://github.com/swoole/golang-h2demo.git\n      apt install -y golang\n      cd ./golang-h2demo && GOOS=linux GOARCH=arm64 go build -o h2demo . && docker build . -t phpswoole/golang-h2demo && cd -\n\n      echo \"\\n 📢 create ${PHP_VERSION} aarch64 docker image\"\n      git clone https://github.com/swoole/php-docker.git\n      cd php-docker\n      cd ${PHP_VERSION} && sed -i '/odbc-mariadb \\\\/d' Dockerfile && docker build . -t phpswoole/php:${PHP_VERSION} && cd -\n      cd ../\n  fi\n}\n\nprepare_data_files(){\n    cd ${__DIR__} && \\\n    remove_data_files && \\\n    mkdir -p \\\n    data \\\n    data/run \\\n    data/mysql data/run/mysqld \\\n    data/redis data/run/redis && \\\n    chmod -R 777 data\n    if [ $? -ne 0 ]; then\n        echo \"\\n❌ Prepare data files failed!\"\n        exit 1\n    fi\n}\n\nremove_data_files(){\n    cd ${__DIR__} && \\\n    rm -rf scripts/data\n}\n\nstart_docker_containers(){\n    remove_docker_containers\n    cd ${__DIR__} && \\\n    docker-compose up -d && \\\n    docker ps -a\n    if [ $? -ne 0 ]; then\n        echo \"\\n❌ Create containers failed!\"\n        exit 1\n    fi\n}\n\nremove_docker_containers(){\n    cd ${__DIR__} && \\\n    docker-compose kill > /dev/null 2>&1 && \\\n    docker-compose rm -f > /dev/null 2>&1\n}\n\nrun_tests_in_docker(){\n    docker exec swoole touch /.cienv && \\\n    docker exec swoole /swoole-src/scripts/docker-route.sh $SWOOLE_CI_TYPE\n    code=$?\n    if [ $code -ne 0 ]; then\n        echo \"\\n❌ Run tests failed! ExitCode: $code\"\n        exit 1\n    fi\n}\n\nremove_tests_resources(){\n    remove_docker_containers\n    remove_data_files\n}\n\ncheck_docker_dependency\ncreate_docker_images\necho \"\\n📖 Prepare for files...\\n\"\nprepare_data_files\n\necho \"📦 Start docker containers...\\n\"\nstart_docker_containers # && trap \"remove_tests_resources\"\n\necho \"\\n⏳ Run tests in docker...\\n\"\nrun_tests_in_docker\necho \"\\n🚀🚀🚀Completed successfully🚀🚀🚀\\n\"\n"
  },
  {
    "path": "scripts/run-tests.sh",
    "content": "#!/bin/sh -e\n__CURRENT__=`pwd`\n__DIR__=$(cd \"$(dirname \"$0\")\";pwd)\n\n[ -z \"${SWOOLE_BRANCH}\" ] && export SWOOLE_BRANCH=\"master\"\n\n#-------------PHPT-------------\ncd ${__DIR__} && cd ../tests/\n\n# initialization\necho \"\" && echo \"⭐️ Initialization for tests...\" && echo \"\"\n\nif [ \"$SWOOLE_CI_IN_MACOS\" = 1 ]; then\n  echo \"run in macOS, skip init database\"\nelse\n  php ./init\nfi\n\ncd ./include/lib\necho \"composer update\"\ncomposer update\ncd -\necho \"\"\n\n# debug\nfor debug_file in ${__DIR__}/debug/*.php\ndo\n    if test -f \"${debug_file}\";then\n        debug_file_basename=\"`basename ${debug_file}`\"\n        echo \"\" && echo \"====== RUN ${debug_file_basename} ======\" && echo \"\"\n        php \"${debug_file}\"\n        echo \"\" && echo \"========================================\" && echo \"\"\n    fi\ndone\n\n# run tests @params($1=list_file, $2=options)\nrun_tests(){\n    ./start.sh \\\n    \"`tr '\\n' ' ' < ${1} | xargs`\" \\\n    -w ${1} \\\n    ${2}\n}\n\nhas_failures(){\n    cat tests.list\n}\n\nshould_exit_with_error(){\n    if [ \"${SWOOLE_BRANCH}\" = \"valgrind\" ]; then\n        set +e\n        find ./ -type f -name \"*.mem\"\n        set -e\n    else\n        has_failures\n    fi\n}\n\ntouch tests.list\ntrap \"rm -f tests.list; echo ''; echo '⌛ Done on '`date \"+%Y-%m-%d %H:%M:%S\"`;\" EXIT\n\ncpu_num=\"$(/usr/bin/env php -r \"echo swoole_cpu_num() * 2;\")\"\n\n#if [ \"$SWOOLE_CI_IN_MACOS\" = 1 ]; then\n#    options=\"\"\n#else\n#    options=\"-j${cpu_num}\"\n#fi\n\noptions=\"-j${cpu_num}\"\n\necho \"\" && echo \"🌵️️ Current branch is ${SWOOLE_BRANCH}\" && echo \"\"\nif [ \"${SWOOLE_BRANCH}\" = \"valgrind\" ]; then\n    dir=\"base\"\n    options=\"${options} -m\"\nelif [ \"$SWOOLE_THREAD\" = 1 ]; then\n    dir=\"swoole_thread\"\nelif [ \"$SWOOLE_IOURING\" = 1 ]; then\n    dir=\"swoole_runtime/file_hook swoole_iouring swoole_http_client_coro/download_filename_bug.phpt\"\nelif [ \"$SWOOLE_CI_IN_MACOS\" = 1 ]; then\n    dir=\"swoole_atomic swoole_coroutine swoole_coroutine_wait_group swoole_global swoole_http_server swoole_process_pool  swoole_server_port \\\n        swoole_websocket_server swoole_channel_coro swoole_coroutine_lock swoole_curl swoole_http2_client_coro swoole_http_server_coro  \\\n        swoole_redis_server swoole_socket_coro swoole_client_async swoole_coroutine_scheduler swoole_event swoole_http2_server \\\n        swoole_runtime swoole_table swoole_client_coro swoole_coroutine_system  swoole_feature swoole_http2_server_coro swoole_library swoole_server \\\n        swoole_client_sync swoole_coroutine_util swoole_function swoole_http_client_coro swoole_lock swoole_process swoole_server_coro swoole_timer\"\nelse\n    dir=\"swoole_*\"\nfi\n\necho \"${dir}\"\necho \"${dir}\" > tests.list\nfor i in 1 2 3 4 5\ndo\n    if [ \"`has_failures`\" ]; then\n        if [ ${i} -gt \"1\" ]; then\n            sleep ${i}\n            echo \"\" && echo \"😮 Retry failed tests#${i}:\" && echo \"\"\n        fi\n        cat tests.list\n        timeout=`echo | expr ${i} \\* 15 + 15`\n        options=\"${options} --set-timeout ${timeout}\"\n        run_tests tests.list \"${options}\"\n    else\n        break\n    fi\ndone\n\nif [ \"`should_exit_with_error`\" ]; then\n    exit 255\nfi\n"
  },
  {
    "path": "scripts/simple-compile-on-github.sh",
    "content": "#!/bin/sh -e\n# shellcheck disable=SC2034\n# shellcheck disable=SC2006\n__CURRENT__=`pwd`\n__DIR__=$(cd \"$(dirname \"$0\")\";pwd)\n\nif [ \"${GITHUB_ACTIONS}\" = true ]; then\n  # shellcheck disable=SC2028\n  echo \"\\n❌ This script is just for Github!\"\n  exit 255\nfi\n\nsudo apt-get update -y\nsudo apt-get install -y libcurl4-openssl-dev libc-ares-dev\n\ncd \"${__DIR__}\" && cd ../ && \\\n./clear.sh > /dev/null && \\\nphpize --clean > /dev/null && \\\nphpize > /dev/null && \\\n./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-cares > /dev/null && \\\nmake -j8 > /dev/null | tee /tmp/compile.log && \\\n(test \"`cat /tmp/compile.log`\"x = \"\"x || exit 255) && \\\nmake install && \\\nphp --ri curl && \\\nphp -d extension=swoole.so --ri swoole\n"
  },
  {
    "path": "scripts/simple-compile.sh",
    "content": "#!/bin/sh -e\n__CURRENT__=`pwd`\n__DIR__=$(cd \"$(dirname \"$0\")\";pwd)\n\ncd ${__DIR__} && cd ../ && \\\n./clear.sh > /dev/null && \\\nphpize --clean > /dev/null && \\\nphpize > /dev/null && \\\n./configure > /dev/null && \\\nmake -j8 > /dev/null | tee /tmp/compile.log && \\\n(test \"`cat /tmp/compile.log`\"x = \"\"x || exit 255) && \\\nmake install && \\\necho \"\\n[swoole]\\nextension=swoole.so\" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini\n"
  },
  {
    "path": "src/core/base.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole.h\"\n\n#include <cstdarg>\n#include <cassert>\n#include <fcntl.h>\n\n#include <sys/stat.h>\n#include <sys/resource.h>\n\n#ifdef __MACH__\n#include <sys/syslimits.h>\n#endif\n\n#include <list>\n#include <set>\n#include <chrono>\n#include <random>\n\n#include \"swoole_string.h\"\n#include \"swoole_signal.h\"\n#include \"swoole_memory.h\"\n#include \"swoole_protocol.h\"\n#include \"swoole_util.h\"\n#include \"swoole_async.h\"\n#include \"swoole_coroutine_system.h\"\n#include \"swoole_ssl.h\"\n\n#include \"swoole_api.h\"\n#include \"swoole_coroutine_api.h\"\n\nusing swoole::Logger;\nusing swoole::NameResolver;\nusing swoole::String;\nusing swoole::coroutine::System;\n\nswoole::Global SwooleG = {};\nthread_local swoole::ThreadGlobal SwooleTG = {};\nthread_local char sw_error[SW_ERROR_MSG_SIZE];\nstd::mutex sw_thread_lock;\n\nstatic void swoole_fatal_error_impl(int code, const char *format, ...);\n\nswoole::Logger *sw_logger() {\n    return SwooleG.logger;\n}\n\nvoid *sw_malloc(size_t size) {\n    return SwooleG.std_allocator.malloc(size);\n}\n\nvoid sw_free(void *ptr) {\n    return SwooleG.std_allocator.free(ptr);\n}\n\nvoid *sw_calloc(size_t nmemb, size_t size) {\n    return SwooleG.std_allocator.calloc(nmemb, size);\n}\n\nvoid *sw_realloc(void *ptr, size_t size) {\n    return SwooleG.std_allocator.realloc(ptr, size);\n}\n\nstatic void bug_report_message_init() {\n    SwooleG.bug_report_message += \"\\n\" + std::string(SWOOLE_BUG_REPORT) + \"\\n\";\n\n    utsname u;\n    if (uname(&u) != -1) {\n        SwooleG.bug_report_message +=\n            swoole::std_string::format(\"OS: %s %s %s %s\\n\", u.sysname, u.release, u.version, u.machine);\n    }\n\n#ifdef __VERSION__\n    SwooleG.bug_report_message += swoole::std_string::format(\"GCC_VERSION: %s\\n\", __VERSION__);\n#endif\n\n    SwooleG.bug_report_message += swoole_ssl_get_version_message();\n}\n\nvoid swoole_init() {\n    if (SwooleG.init) {\n        return;\n    }\n\n    SwooleG = {};\n    sw_memset_zero(sw_error, SW_ERROR_MSG_SIZE);\n\n    SwooleG.running = 1;\n    SwooleG.init = 1;\n    SwooleG.enable_coroutine = 1;\n    SwooleG.std_allocator = {malloc, calloc, realloc, free};\n    SwooleG.stdout_ = stdout;\n    SwooleG.fatal_error = swoole_fatal_error_impl;\n    SwooleG.cpu_num = SW_MAX(1, sysconf(_SC_NPROCESSORS_ONLN));\n    SwooleG.pagesize = getpagesize();\n    SwooleG.max_file_content = SW_MAX_FILE_CONTENT;\n\n    // DNS options\n    SwooleG.dns_tries = 1;\n    SwooleG.dns_resolvconf_path = SW_DNS_RESOLV_CONF;\n\n    // get system uname\n    uname(&SwooleG.uname);\n    // random seed\n    srandom(time(nullptr));\n\n    if (!SwooleG.logger) {\n        SwooleG.logger = new Logger();\n    }\n\n    swoole_thread_init(true);\n\n#ifdef SW_DEBUG\n    sw_logger()->set_level(0);\n    SwooleG.trace_flags = 0x7fffffff;\n#else\n    sw_logger()->set_level(SW_LOG_INFO);\n#endif\n\n    // init global shared memory\n    SwooleG.memory_pool = new swoole::GlobalMemory(SW_GLOBAL_MEMORY_PAGESIZE, true);\n    SwooleG.max_sockets = SW_MAX_SOCKETS_DEFAULT;\n    rlimit rlmt;\n    if (getrlimit(RLIMIT_NOFILE, &rlmt) < 0) {\n        swoole_sys_warning(\"getrlimit() failed\");\n    } else {\n        SwooleG.max_sockets = SW_MAX((uint32_t) rlmt.rlim_cur, SW_MAX_SOCKETS_DEFAULT);\n        SwooleG.max_sockets = SW_MIN((uint32_t) rlmt.rlim_cur, SW_SESSION_LIST_SIZE);\n    }\n\n    SwooleG.task_tmpfile = SW_TASK_TMP_DIR \"/\" SW_TASK_TMP_FILE;\n\n    // init signalfd\n#ifdef HAVE_SIGNALFD\n    swoole_signalfd_init();\n    SwooleG.enable_signalfd = 1;\n#endif\n\n    // init bug report message\n    bug_report_message_init();\n}\n\nSW_EXTERN_C_BEGIN\n\nSW_API void swoole_add_hook(enum swGlobalHookType type, swHookFunc func, int push_back) {\n    assert(type <= SW_GLOBAL_HOOK_END);\n    swoole::hook_add(SwooleG.hooks, type, func, push_back);\n}\n\nSW_API void swoole_call_hook(enum swGlobalHookType type, void *arg) {\n    assert(type <= SW_GLOBAL_HOOK_END);\n    swoole::hook_call(SwooleG.hooks, type, arg);\n}\n\nSW_API bool swoole_isset_hook(enum swGlobalHookType type) {\n    assert(type <= SW_GLOBAL_HOOK_END);\n    return SwooleG.hooks[type] != nullptr;\n}\n\nSW_API const char *swoole_version(void) {\n    return SWOOLE_VERSION;\n}\n\nSW_API int swoole_version_id(void) {\n    return SWOOLE_VERSION_ID;\n}\n\nSW_API int swoole_api_version_id(void) {\n    return SWOOLE_API_VERSION_ID;\n}\n\nSW_EXTERN_C_END\n\nvoid swoole_clean() {\n    SW_LOOP_N(SW_MAX_HOOK_TYPE) {\n        if (SwooleG.hooks[i]) {\n            auto hooks = static_cast<std::list<swoole::Callback> *>(SwooleG.hooks[i]);\n            delete hooks;\n        }\n    }\n\n    swoole_signal_clear();\n    swoole_thread_clean(true);\n\n    if (SwooleG.logger) {\n        SwooleG.logger->close();\n    }\n    delete SwooleG.logger;\n    delete SwooleG.memory_pool;\n    SwooleG = {};\n}\n\nSW_API void swoole_set_log_level(int level) {\n    if (sw_logger()) {\n        sw_logger()->set_level(level);\n    }\n}\n\nSW_API void swoole_set_stdout_stream(FILE *fp) {\n    SwooleG.stdout_ = fp;\n}\n\nSW_API FILE *swoole_get_stdout_stream() {\n    return SwooleG.stdout_;\n}\n\nSW_API int swoole_get_log_level() {\n    if (sw_logger()) {\n        return sw_logger()->get_level();\n    } else {\n        return SW_LOG_NONE;\n    }\n}\n\nSW_API void swoole_set_log_file(const char *file) {\n    if (sw_logger()) {\n        sw_logger()->open(file);\n    }\n}\n\nSW_API void swoole_set_trace_flags(long flags) {\n    SwooleG.trace_flags = flags;\n}\n\nSW_API void swoole_set_print_backtrace_on_error(bool enable) {\n    SwooleG.print_backtrace_on_error = enable;\n}\n\nbool swoole_set_task_tmpdir(const std::string &dir) {\n#ifdef SW_THREAD\n    std::unique_lock<std::mutex> _lock(sw_thread_lock);\n#endif\n\n    if (dir.at(0) != '/') {\n        swoole_warning(\"wrong absolute path '%s'\", dir.c_str());\n        return false;\n    }\n\n    if (access(dir.c_str(), R_OK) < 0 && !swoole_mkdir_recursive(dir)) {\n        swoole_warning(\"create task tmp dir('%s') failed\", dir.c_str());\n        return false;\n    }\n\n    sw_tg_buffer()->format(\"%s/\" SW_TASK_TMP_FILE, dir.c_str());\n    SwooleG.task_tmpfile = sw_tg_buffer()->to_std_string();\n\n    if (SwooleG.task_tmpfile.length() >= SW_TASK_TMP_PATH_SIZE) {\n        swoole_warning(\"task tmp_dir is too large, the max size is '%d'\", SW_TASK_TMP_PATH_SIZE - 1);\n        return false;\n    }\n\n    return true;\n}\n\nconst std::string &swoole_get_task_tmpdir() {\n    return SwooleG.task_tmpfile;\n}\n\npid_t swoole_fork_exec(const std::function<void(void)> &fn) {\n    pid_t pid = fork();\n    switch (pid) {\n    case -1:\n        return false;\n    case 0:\n        fn();\n        exit(0);\n    default:\n        break;\n    }\n    return pid;\n}\n\npid_t swoole_fork(int flags) {\n    if (!(flags & SW_FORK_EXEC)) {\n        if (swoole_coroutine_is_in()) {\n            swoole_fatal_error(SW_ERROR_OPERATION_NOT_SUPPORT, \"must be forked outside the coroutine\");\n        }\n        if (SwooleTG.async_threads) {\n            swoole_trace(\"aio_task_num=%lu, reactor=%p\", SwooleTG.async_threads->task_num, sw_reactor());\n            swoole_fatal_error(SW_ERROR_OPERATION_NOT_SUPPORT, \"can not fork after using async-threads\");\n        }\n    }\n    if (flags & SW_FORK_PRECHECK) {\n        return 0;\n    }\n\n    pid_t pid = fork();\n    if (pid == 0) {\n        if (flags & SW_FORK_DAEMON) {\n            return pid;\n        }\n        /**\n         * [!!!] All timers and event loops must be cleaned up after fork\n         */\n        if (swoole_timer_is_available()) {\n            swoole_timer_free();\n        }\n        if (!(flags & SW_FORK_EXEC)) {\n            /**\n             * Do not release the allocated memory pages.\n             * The global memory will be returned to the OS upon process termination.\n             */\n            SwooleG.memory_pool = new swoole::GlobalMemory(SW_GLOBAL_MEMORY_PAGESIZE, true);\n            // reopen log file\n            sw_logger()->reopen();\n            // reset eventLoop\n            if (swoole_event_is_available()) {\n                swoole_event_free();\n                swoole_trace_log(SW_TRACE_REACTOR, \"reactor has been destroyed\");\n            }\n        } else {\n            sw_logger()->close();\n        }\n        // reset signal handler\n        swoole_signal_clear();\n\n        if (swoole_isset_hook(SW_GLOBAL_HOOK_AFTER_FORK)) {\n            swoole_call_hook(SW_GLOBAL_HOOK_AFTER_FORK, nullptr);\n        }\n    }\n\n    return pid;\n}\n\nbool swoole_is_main_thread() {\n    return SwooleTG.main_thread;\n}\n\nvoid swoole_thread_init(bool main_thread) {\n    if (!SwooleTG.buffer_stack) {\n        SwooleTG.buffer_stack = new String(SW_STACK_BUFFER_SIZE);\n    }\n    if (!main_thread) {\n        swoole_signal_block_all();\n    }\n    SwooleTG.main_thread = main_thread;\n}\n\nvoid swoole_thread_clean(bool main_thread) {\n    if (SwooleTG.timer) {\n        swoole_timer_free();\n    }\n    if (SwooleTG.reactor) {\n        swoole_event_free();\n    }\n    if (SwooleTG.buffer_stack) {\n        delete SwooleTG.buffer_stack;\n        SwooleTG.buffer_stack = nullptr;\n    }\n}\n\nvoid swoole_dump_ascii(const char *data, size_t size) {\n    for (size_t i = 0; i < size; i++) {\n        printf(\"%u \", (unsigned) data[i]);\n    }\n    printf(\"\\n\");\n}\n\nvoid swoole_dump_bin(const uchar *data, char type, size_t size) {\n    int type_size = swoole_type_size(type);\n    if (type_size <= 0) {\n        return;\n    }\n    int n = size / type_size;\n    for (int i = 0; i < n; i++) {\n        printf(\"%ld,\", (long) swoole_unpack(type, data + type_size * i));\n    }\n    printf(\"\\n\");\n}\n\nvoid swoole_dump_hex(const uchar *data, size_t outlen) {\n    for (size_t i = 0; i < outlen; ++i) {\n        if ((i & 0x0fu) == 0) {\n            printf(\"%08zX: \", i);\n        }\n        printf(\"%02X \", data[i]);\n        if (((i + 1) & 0x0fu) == 0) {\n            printf(\"\\n\");\n        }\n    }\n    printf(\"\\n\");\n}\n\n/**\n * Recursive directory creation\n */\nbool swoole_mkdir_recursive(const std::string &dir) {\n    char tmp[PATH_MAX];\n    size_t len = dir.length();\n\n    // PATH_MAX limit includes string trailing null character\n    if (len + 1 > PATH_MAX) {\n        swoole_error_log(SW_LOG_WARNING,\n                         SW_ERROR_NAME_TOO_LONG,\n                         \"mkdir() failed. Path exceeds the limit of %d characters\",\n                         PATH_MAX - 1);\n        return false;\n    }\n    swoole_strlcpy(tmp, dir.c_str(), PATH_MAX);\n\n    if (dir[len - 1] != '/') {\n        strcat(tmp, \"/\");\n    }\n\n    len = strlen(tmp);\n    for (size_t i = 1; i < len; i++) {\n        if (tmp[i] == '/') {\n            tmp[i] = 0;\n            if (access(tmp, R_OK) != 0) {\n                if (mkdir(tmp, 0755) == -1) {\n                    swoole_sys_warning(\"mkdir('%s') failed\", tmp);\n                    return false;\n                }\n            }\n            tmp[i] = '/';\n        }\n    }\n\n    return true;\n}\n\nint swoole_type_size(char type) {\n    switch (type) {\n    case 'c':\n    case 'C':\n        return 1;\n    case 's':\n    case 'S':\n    case 'n':\n    case 'v':\n        return 2;\n    case 'l':\n    case 'L':\n    case 'N':\n    case 'V':\n        return 4;\n    case 'q':\n    case 'Q':\n    case 'J':\n    case 'P':\n        return 8;\n    default:\n        return 0;\n    }\n}\n\nchar *swoole_dec2hex(ulong_t value, int base) {\n    assert(base > 1 && base < 37);\n\n    static char digits[] = \"0123456789abcdefghijklmnopqrstuvwxyz\";\n    char buf[(sizeof(ulong_t) << 3) + 1];\n    char *ptr;\n\n    char *end = ptr = buf + sizeof(buf) - 1;\n    *ptr = '\\0';\n\n    do {\n        *--ptr = digits[value % base];\n        value /= base;\n    } while (ptr > buf && value);\n\n    return sw_strndup(ptr, end - ptr);\n}\n\nulong_t swoole_hex2dec(const char *hex, size_t *parsed_bytes) {\n    size_t value = 0;\n    *parsed_bytes = 0;\n    const char *p = hex;\n\n    if (strncasecmp(hex, \"0x\", 2) == 0) {\n        p += 2;\n    }\n\n    while (true) {\n        char c = *p;\n        if ((c >= '0') && (c <= '9')) {\n            value = value * 16 + (c - '0');\n        } else {\n            c = toupper(c);\n            if ((c >= 'A') && (c <= 'Z')) {\n                value = value * 16 + (c - 'A') + 10;\n            } else {\n                break;\n            }\n        }\n        p++;\n    }\n    *parsed_bytes = p - hex;\n    return value;\n}\n\n#ifndef RAND_MAX\n#define RAND_MAX 2147483647\n#endif\n\nint swoole_system_random(int min, int max) {\n    static int dev_random_fd = -1;\n    unsigned random_value;\n\n    assert(max > min);\n\n    if (dev_random_fd == -1) {\n        dev_random_fd = open(\"/dev/urandom\", O_RDONLY);\n        if (dev_random_fd < 0) {\n            return swoole_rand(min, max);\n        }\n    }\n\n    auto next_random_byte = (char *) &random_value;\n    constexpr int bytes_to_read = sizeof(random_value);\n\n    if (read(dev_random_fd, next_random_byte, bytes_to_read) < bytes_to_read) {\n        swoole_sys_warning(\"read() from /dev/urandom failed\");\n        return SW_ERR;\n    }\n    return min + (random_value % (max - min + 1));\n}\n\nvoid swoole_redirect_stdout(int new_fd) {\n    if (dup2(new_fd, STDOUT_FILENO) < 0) {\n        swoole_sys_warning(\"dup2(STDOUT_FILENO) failed\");\n    }\n    if (dup2(new_fd, STDERR_FILENO) < 0) {\n        swoole_sys_warning(\"dup2(STDERR_FILENO) failed\");\n    }\n}\n\nvoid swoole_redirect_stdout(const char *file) {\n    auto fd = open(file, O_WRONLY | O_APPEND | O_CREAT, 0644);\n    if (fd >= 0) {\n        swoole_redirect_stdout(fd);\n        close(fd);\n    } else {\n        swoole_sys_warning(\"open('%s') failed\", file);\n    }\n}\n\nint swoole_version_compare(const char *version1, const char *version2) {\n    int result = 0;\n\n    while (result == 0) {\n        char *tail1;\n        char *tail2;\n\n        unsigned long ver1 = strtoul(version1, &tail1, 10);\n        unsigned long ver2 = strtoul(version2, &tail2, 10);\n\n        if (ver1 < ver2) {\n            result = -1;\n        } else if (ver1 > ver2) {\n            result = +1;\n        } else {\n            version1 = tail1;\n            version2 = tail2;\n            if (*version1 == '\\0' && *version2 == '\\0') {\n                break;\n            } else if (*version1 == '\\0') {\n                result = -1;\n            } else if (*version2 == '\\0') {\n                result = +1;\n            } else {\n                version1++;\n                version2++;\n            }\n        }\n    }\n    return result;\n}\n\n/**\n * Maximum common divisor\n */\nuint32_t swoole_common_divisor(uint32_t u, uint32_t v) {\n    assert(u > 0);\n    assert(v > 0);\n    while (u > 0) {\n        if (u < v) {\n            uint32_t t = u;\n            u = v;\n            v = t;\n        }\n        u = u - v;\n    }\n    return v;\n}\n\n/**\n * The least common multiple\n */\nuint32_t swoole_common_multiple(uint32_t u, uint32_t v) {\n    assert(u > 0);\n    assert(v > 0);\n\n    uint32_t m_cup = u;\n    uint32_t n_cup = v;\n    int res = m_cup % n_cup;\n\n    while (res != 0) {\n        m_cup = n_cup;\n        n_cup = res;\n        res = m_cup % n_cup;\n    }\n    return u * v / n_cup;\n}\n\nsize_t sw_snprintf(char *buf, size_t size, const char *format, ...) {\n    va_list args;\n    va_start(args, format);\n    int retval = vsnprintf(buf, size, format, args);\n    va_end(args);\n\n    if (size == 0) {\n        return retval;\n    } else if (sw_unlikely(retval < 0)) {\n        retval = 0;\n        buf[0] = '\\0';\n    } else if (sw_unlikely(retval >= (int) size)) {\n        retval = size - 1;\n        buf[retval] = '\\0';\n    }\n    return retval;\n}\n\nsize_t sw_vsnprintf(char *buf, size_t size, const char *format, va_list args) {\n    int retval = vsnprintf(buf, size, format, args);\n    if (sw_unlikely(retval < 0)) {\n        retval = 0;\n        buf[0] = '\\0';\n    } else if (sw_unlikely(retval >= (int) size)) {\n        retval = size - 1;\n        buf[retval] = '\\0';\n    }\n    return retval;\n}\n\nint sw_printf(const char *format, ...) {\n    va_list args;\n    va_start(args, format);\n    int retval = vfprintf(SwooleG.stdout_, format, args);\n    va_end(args);\n    return retval;\n}\n\nbool sw_wait_for(const std::function<bool(void)> &fn, int timeout_ms) {\n    int sleep_msec = 1;\n    while (timeout_ms >= 0) {\n        if (fn()) {\n            return true;\n        }\n        usleep(sleep_msec * 1000);\n        sleep_msec *= 2;\n        // Align the time so that the timeout is consistent with the user settings\n        if (timeout_ms > 0 && timeout_ms - sleep_msec < 0) {\n            sleep_msec = timeout_ms;\n            timeout_ms = 0;\n        } else {\n            timeout_ms -= sleep_msec;\n        }\n    }\n    return false;\n}\n\nint swoole_itoa(char *buf, long value) {\n    long i = 0, j;\n\n    long sign_mask = value >> (sizeof(long) * 8 - 1);\n    unsigned long nn = (value + sign_mask) ^ sign_mask;\n    do {\n        buf[i++] = nn % 10 + '0';\n    } while (nn /= 10);\n\n    buf[i] = '-';\n    i += sign_mask & 1;\n    buf[i] = '\\0';\n\n    int s_len = i;\n\n    for (i = 0, j = s_len - 1; i < j; ++i, --j) {\n        char swap = buf[i];\n        buf[i] = buf[j];\n        buf[j] = swap;\n    }\n    buf[s_len] = 0;\n    return s_len;\n}\n\nint swoole_shell_exec(const char *command, pid_t *pid, bool get_error_stream) {\n    pid_t child_pid;\n    int fds[2];\n    if (pipe(fds) < 0) {\n        return SW_ERR;\n    }\n\n    if ((child_pid = fork()) == -1) {\n        swoole_sys_warning(\"fork() failed\");\n        close(fds[0]);\n        close(fds[1]);\n        return SW_ERR;\n    }\n\n    if (child_pid == 0) {\n        close(fds[SW_PIPE_READ]);\n\n        if (get_error_stream) {\n            if (fds[SW_PIPE_WRITE] == fileno(stdout)) {\n                dup2(fds[SW_PIPE_WRITE], fileno(stderr));\n            } else if (fds[SW_PIPE_WRITE] == fileno(stderr)) {\n                dup2(fds[SW_PIPE_WRITE], fileno(stdout));\n            } else {\n                dup2(fds[SW_PIPE_WRITE], fileno(stdout));\n                dup2(fds[SW_PIPE_WRITE], fileno(stderr));\n                close(fds[SW_PIPE_WRITE]);\n            }\n        } else {\n            if (fds[SW_PIPE_WRITE] != fileno(stdout)) {\n                dup2(fds[SW_PIPE_WRITE], fileno(stdout));\n                close(fds[SW_PIPE_WRITE]);\n            }\n        }\n\n        execl(\"/bin/sh\", \"sh\", \"-c\", command, nullptr);\n        exit(127);\n    } else {\n        *pid = child_pid;\n        close(fds[SW_PIPE_WRITE]);\n    }\n    return fds[SW_PIPE_READ];\n}\n\nchar *swoole_string_format(size_t n, const char *format, ...) {\n    char *buf = (char *) sw_malloc(n);\n    if (!buf) {\n        return nullptr;\n    }\n\n    va_list va_list;\n    va_start(va_list, format);\n    int ret = vsnprintf(buf, n, format, va_list);\n    va_end(va_list);\n    if (ret >= 0) {\n        return buf;\n    }\n    sw_free(buf);\n    return nullptr;\n}\n\nstatic constexpr char characters[] = {\n    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',\n    'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',\n    'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',\n};\n\nvoid swoole_random_string(char *buf, size_t len) {\n    size_t i = 0;\n    for (; i < len; i++) {\n        buf[i] = characters[swoole_rand(0, sizeof(characters) - 1)];\n    }\n    buf[i] = '\\0';\n}\n\nvoid swoole_random_string(std::string &str, size_t len) {\n    size_t i = 0;\n    for (; i < len; i++) {\n        str.append(1, characters[swoole_rand(0, sizeof(characters) - 1)]);\n    }\n}\n\nuint64_t swoole_random_int() {\n    static thread_local std::random_device rd;\n    static thread_local std::mt19937_64 gen(rd());\n    static thread_local std::uniform_int_distribution<uint64_t> dis;\n    std::uniform_int_distribution<uint64_t>::param_type params(0, UINT64_MAX);\n    return dis(gen, params);\n}\n\nint swoole_rand(int min, int max) {\n    static thread_local std::random_device rd;\n    static thread_local std::mt19937 gen(rd());\n    static thread_local std::uniform_int_distribution<int> dis;\n    std::uniform_int_distribution<int>::param_type params(min, max);\n    return dis(gen, params);\n}\n\nint swoole_rand() {\n    return swoole_rand(0, INT_MAX);\n}\n\nbool swoole_get_env(const char *name, int *value) {\n    const char *e = getenv(name);\n    if (!e) {\n        return false;\n    }\n    *value = std::stoi(e);\n    return true;\n}\n\nint swoole_get_systemd_listen_fds() {\n    int ret;\n    if (!swoole_get_env(\"LISTEN_FDS\", &ret)) {\n        swoole_warning(\"invalid LISTEN_FDS\");\n        return -1;\n    } else if (ret >= SW_MAX_LISTEN_PORT) {\n        swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_TOO_MANY_LISTEN_PORT, \"LISTEN_FDS is too big\");\n        return -1;\n    }\n    return ret;\n}\n\n#ifdef HAVE_BOOST_STACKTRACE\n#include <boost/stacktrace.hpp>\n#include <iostream>\nvoid swoole_print_backtrace() {\n    std::cout << boost::stacktrace::stacktrace();\n}\n#elif defined(HAVE_EXECINFO) && !defined(__ANDROID__)\n#include <execinfo.h>\nvoid swoole_print_backtrace() {\n    int size = 16;\n    void *array[16];\n    int stack_num = backtrace(array, size);\n    char **stacktrace = backtrace_symbols(array, stack_num);\n    int i;\n\n    for (i = 0; i < stack_num; ++i) {\n        printf(\"%s\\n\", stacktrace[i]);\n    }\n    free(stacktrace);\n}\n#else\nvoid swoole_print_backtrace() {}\n#endif\n\nvoid swoole_print_backtrace_on_error() {\n    if (SwooleG.print_backtrace_on_error) {\n        swoole_print_backtrace();\n        if (SwooleG.print_backtrace) {\n            SwooleG.print_backtrace();\n        }\n    }\n}\n\nstatic void swoole_fatal_error_impl(int code, const char *format, ...) {\n    size_t retval = 0;\n    va_list args;\n\n    retval += sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, \"(ERROR %d): \", code);\n    va_start(args, format);\n    retval += sw_vsnprintf(sw_error + retval, SW_ERROR_MSG_SIZE - retval, format, args);\n    va_end(args);\n    sw_logger()->put(SW_LOG_ERROR, sw_error, retval);\n    swoole_exit(1);\n}\n\nvoid swoole_exit(int _status) {\n#ifdef SW_THREAD\n    /**\n     * If multiple threads call exit simultaneously, it can result in a crash.\n     * Implementing locking mechanisms can prevent concurrent calls to exit.\n     */\n    std::unique_lock<std::mutex> _lock(sw_thread_lock);\n#endif\n    exit(_status);\n}\n\nnamespace swoole {\n//-------------------------------------------------------------------------------\nsize_t DataHead::dump(char *_buf, size_t _len) {\n    return sw_snprintf(_buf,\n                       _len,\n                       \"DataHead[%p]\\n\"\n                       \"{\\n\"\n                       \"    long fd = %ld;\\n\"\n                       \"    uint64_t msg_id = %\" PRIu64 \";\\n\"\n                       \"    uint32_t len = %d;\\n\"\n                       \"    int16_t reactor_id = %d;\\n\"\n                       \"    uint8_t type = %d;\\n\"\n                       \"    uint8_t flags = %d;\\n\"\n                       \"    uint16_t server_fd = %d;\\n\"\n                       \"    uint16_t ext_flags = %d;\\n\"\n                       \"    double time = %f;\\n\"\n                       \"}\\n\",\n                       this,\n                       fd,\n                       msg_id,\n                       len,\n                       reactor_id,\n                       type,\n                       flags,\n                       server_fd,\n                       ext_flags,\n                       time);\n}\n\nvoid DataHead::print() {\n    sw_tg_buffer()->length = dump(sw_tg_buffer()->str, sw_tg_buffer()->size);\n    printf(\"%.*s\", (int) sw_tg_buffer()->length, sw_tg_buffer()->str);\n}\n\nstd::string dirname(const std::string &file) {\n    size_t index = file.find_last_of('/');\n    if (index == std::string::npos) {\n        return {};\n    } else if (index == 0) {\n        return \"/\";\n    }\n    return file.substr(0, index);\n}\n\nvoid hook_add(void **hooks, int type, const Callback &func, int push_back) {\n    if (hooks[type] == nullptr) {\n        hooks[type] = new std::list<Callback>;\n    }\n\n    auto *l = static_cast<std::list<Callback> *>(hooks[type]);\n    if (push_back) {\n        l->push_back(func);\n    } else {\n        l->push_front(func);\n    }\n}\n\nvoid hook_call(void **hooks, int type, void *arg) {\n    if (hooks[type] == nullptr) {\n        return;\n    }\n    const auto *l = static_cast<std::list<Callback> *>(hooks[type]);\n    for (auto &i : *l) {\n        i(arg);\n    }\n}\n\n/**\n * return the first file of the intersection, in order of vec1\n */\nstd::string intersection(const std::vector<std::string> &vec1, std::set<std::string> &vec2) {\n    for (const auto &vec1_item : vec1) {\n        if (vec2.find(vec1_item) != vec2.end()) {\n            return vec1_item;\n        }\n    }\n\n    return \"\";\n}\n\ndouble microtime() {\n    using namespace std::chrono;\n    return duration_cast<duration<double>>(system_clock::now().time_since_epoch()).count();\n}\n};  // namespace swoole\n"
  },
  {
    "path": "src/core/base64.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2017 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_base64.h\"\n\nnamespace swoole {\n\n// clang-format off\n\n/* BASE 64 encode table */\nstatic char base64en[] =\n{\n    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',\n    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',\n    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',\n};\n\n#define BASE64_PAD        '='\n#define BASE64DE_FIRST    '+'\n#define BASE64DE_LAST     'z'\n\n/* ASCII order for BASE 64 decode, -1 in unused character */\nstatic signed char base64de[] = {\n/* '+', ',', '-', '.', '/', '0', '1', '2', */\n        62, -1, -1, -1, 63, 52, 53, 54,\n/* '3', '4', '5', '6', '7', '8', '9', ':', */\n        55, 56, 57, 58, 59, 60, 61, -1,\n/* ';', '<', '=', '>', '?', '@', 'A', 'B', */\n        -1, -1, -1, -1, -1, -1, 0, 1,\n/* 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', */\n        2, 3, 4, 5, 6, 7, 8, 9,\n/* 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', */\n        10, 11, 12, 13, 14, 15, 16, 17,\n/* 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', */\n        18, 19, 20, 21, 22, 23, 24, 25,\n/* '[', '\\', ']', '^', '_', '`', 'a', 'b', */\n        -1, -1, -1, -1, -1, -1, 26, 27,\n/* 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', */\n        28, 29, 30, 31, 32, 33, 34, 35,\n/* 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', */\n        36, 37, 38, 39, 40, 41, 42, 43,\n/* 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', */\n        44, 45, 46, 47, 48, 49, 50, 51,\n};\n\n// clang-format on\n\nsize_t base64_encode(const unsigned char *in, size_t inlen, char *out) {\n    size_t i, j;\n\n    for (i = j = 0; i < inlen; i++) {\n        int s = i % 3; /* from 6/gcd(6, 8) */\n        switch (s) {\n        case 0:\n            out[j++] = base64en[(in[i] >> 2) & 0x3F];\n            continue;\n        case 1:\n            out[j++] = base64en[((in[i - 1] & 0x3) << 4) + ((in[i] >> 4) & 0xF)];\n            continue;\n        case 2:\n            out[j++] = base64en[((in[i - 1] & 0xF) << 2) + ((in[i] >> 6) & 0x3)];\n            out[j++] = base64en[in[i] & 0x3F];\n        }\n    }\n    /* move back */\n    i -= 1;\n    /* check the last and add padding */\n    if ((i % 3) == 0) {\n        out[j++] = base64en[(in[i] & 0x3) << 4];\n        out[j++] = BASE64_PAD;\n        out[j++] = BASE64_PAD;\n    } else if ((i % 3) == 1) {\n        out[j++] = base64en[(in[i] & 0xF) << 2];\n        out[j++] = BASE64_PAD;\n    }\n    out[j] = '\\0';\n\n    return j;\n}\n\nsize_t base64_decode(const char *in, size_t inlen, char *out) {\n    size_t j;\n\n    for (size_t i = j = 0; i < inlen; i++) {\n        int c;\n        int s = i % 4; /* from 8/gcd(6, 8) */\n\n        if (in[i] == '=') {\n            break;\n        }\n\n        if (in[i] < BASE64DE_FIRST || in[i] > BASE64DE_LAST || (c = base64de[in[i] - BASE64DE_FIRST]) == -1) {\n            return 0;\n        }\n\n        switch (s) {\n        case 0:\n            out[j] = c << 2;\n            continue;\n        case 1:\n            out[j++] += (c >> 4) & 0x3;\n            /* if not last char with padding */\n            if (i < (inlen - 3) || in[inlen - 2] != '=') out[j] = (c & 0xF) << 4;\n            continue;\n        case 2:\n            out[j++] += (c >> 2) & 0xF;\n            /* if not last char with padding */\n            if (i < (inlen - 2) || in[inlen - 1] != '=') out[j] = (c & 0x3) << 6;\n            continue;\n        case 3:\n            out[j++] += c;\n        }\n    }\n    out[j] = '\\0';\n\n    return j;\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "src/core/buffer.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole.h\"\n#include \"swoole_buffer.h\"\n\nnamespace swoole {\n\nBufferChunk::BufferChunk(Type type, uint32_t size) : type(type), size(size) {\n    if (type == TYPE_DATA && size > 0) {\n        value.str = new char[size];\n    }\n}\n\nBufferChunk::~BufferChunk() {\n    if (type == TYPE_DATA) {\n        delete[] value.str;\n    }\n    if (destroy) {\n        destroy(this);\n    }\n}\n\nBuffer::Buffer(uint32_t _chunk_size) {\n    chunk_size = _chunk_size == 0 ? INT_MAX : _chunk_size;\n}\n\nBufferChunk *Buffer::alloc(BufferChunk::Type type, uint32_t size) {\n    auto *chunk = new BufferChunk(type, size);\n    queue_.push(chunk);\n    return chunk;\n}\n\nvoid Buffer::pop() {\n    BufferChunk *chunk = queue_.front();\n    total_length -= chunk->size;\n    delete chunk;\n    queue_.pop();\n}\n\nBuffer::~Buffer() {\n    while (!queue_.empty()) {\n        pop();\n    }\n}\n\nvoid Buffer::append(const char *data, uint32_t size) {\n    uint32_t _length = size;\n    auto _pos = data;\n\n    assert(size > 0);\n\n    // buffer enQueue\n    while (_length > 0) {\n        uint32_t _n = _length >= chunk_size ? chunk_size : _length;\n\n        BufferChunk *chunk = alloc(BufferChunk::TYPE_DATA, _n);\n\n        total_length += _n;\n\n        memcpy(chunk->value.str, _pos, _n);\n        chunk->length = _n;\n\n        swoole_trace_log(\n            SW_TRACE_BUFFER, \"chunk_n=%lu|size=%u|chunk_len=%u|chunk=%p\", count(), _n, chunk->length, chunk);\n\n        _pos += _n;\n        _length -= _n;\n    }\n}\n\nvoid Buffer::append(const iovec *iov, size_t iovcnt, off_t offset) {\n    size_t _length = 0;\n\n    SW_LOOP_N(iovcnt) {\n        assert(iov[i].iov_len > 0);\n        assert(iov[i].iov_base != nullptr);\n        _length += iov[i].iov_len;\n    }\n\n    auto pos = static_cast<char *>(iov[0].iov_base);\n    BufferChunk *chunk = nullptr;\n    size_t iov_remain_len = iov[0].iov_len, chunk_remain_len;\n    size_t i = 0;\n\n    while (true) {\n        if (chunk) {\n            if (chunk->size == chunk->length) {\n                chunk = nullptr;\n                continue;\n            } else {\n                chunk_remain_len = chunk->size - chunk->length;\n            }\n        } else {\n            if (offset > 0) {\n                if (offset >= (off_t) iov[i].iov_len) {\n                    offset -= iov[i].iov_len;\n                    i++;\n                    continue;\n                } else {\n                    pos = static_cast<char *>(iov[i].iov_base) + offset;\n                    iov_remain_len = iov[i].iov_len - offset;\n                    offset = 0;\n                }\n            }\n            chunk_remain_len = _length >= chunk_size ? chunk_size : _length;\n            chunk = alloc(BufferChunk::TYPE_DATA, chunk_remain_len);\n        }\n\n        size_t _n = std::min(iov_remain_len, chunk_remain_len);\n        memcpy(chunk->value.str + chunk->length, pos, _n);\n        total_length += _n;\n        _length -= _n;\n\n        swoole_trace_log(\n            SW_TRACE_BUFFER, \"chunk_n=%lu|size=%lu|chunk_len=%u|chunk=%p\", count(), _n, chunk->length, chunk);\n\n        chunk->length += _n;\n        iov_remain_len -= _n;\n\n        if (iov_remain_len == 0) {\n            i++;\n            if (i == iovcnt) {\n                break;\n            }\n            iov_remain_len = iov[i].iov_len;\n            pos = (char *) iov[i].iov_base;\n        } else {\n            pos += _n;\n        }\n    }\n}\n}  // namespace swoole\n"
  },
  {
    "path": "src/core/channel.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_memory.h\"\n#include \"swoole_channel.h\"\n#include \"swoole_lock.h\"\n#include \"swoole_pipe.h\"\n\nnamespace swoole {\n\n#define SW_CHANNEL_MIN_MEM (1024 * 64)\n\nstruct ChannelSlice {\n    int length;\n    char data[0];\n};\n\nChannel *Channel::make(size_t size, size_t maxlen, int flags) {\n    assert(size >= maxlen);\n    void *mem;\n\n    // use shared memory\n    if (flags & SW_CHAN_SHM) {\n        /**\n         * overflow space\n         */\n        mem = sw_shm_malloc(size + sizeof(Channel) + maxlen + sizeof(ChannelSlice));\n    } else {\n        mem = sw_malloc(size + sizeof(Channel) + maxlen + sizeof(ChannelSlice));\n    }\n\n    if (mem == nullptr) {\n        swoole_warning(\"alloc(%ld) failed\", size);\n        return nullptr;\n    }\n\n    auto *object = static_cast<Channel *>(mem);\n    mem = static_cast<char *>(mem) + sizeof(Channel);\n\n    *object = {};\n\n    // overflow space\n    object->size = size;\n    object->mem = mem;\n    object->maxlen = maxlen;\n    object->flags = flags;\n\n    if (flags & SW_CHAN_LOCK) {\n        object->lock = new Mutex(true);\n    }\n\n    if (flags & SW_CHAN_NOTIFY) {\n        object->notify_pipe = new Pipe(true);\n        if (!object->notify_pipe->ready()) {\n            swoole_warning(\"notify_fd init failed\");\n            delete object->notify_pipe;\n            return nullptr;\n        }\n    }\n\n    return object;\n}\n\n/**\n * push data(no lock)\n */\nint Channel::in(const void *in_data, int data_length) {\n    assert(data_length <= maxlen);\n    if (full()) {\n        return SW_ERR;\n    }\n    ChannelSlice *item;\n    int msize = sizeof(item->length) + data_length;\n\n    if (tail < head) {\n        // no enough memory space\n        if ((head - tail) < msize) {\n            return SW_ERR;\n        }\n        item = reinterpret_cast<ChannelSlice *>(static_cast<char *>(mem) + tail);\n        tail += msize;\n    } else {\n        item = reinterpret_cast<ChannelSlice *>(static_cast<char *>(mem) + tail);\n        tail += msize;\n        if (tail >= (off_t) size) {\n            tail = 0;\n            tail_tag = 1 - tail_tag;\n        }\n    }\n    num++;\n    bytes += data_length;\n    item->length = data_length;\n    memcpy(item->data, in_data, data_length);\n    return SW_OK;\n}\n\n/**\n * pop data(no lock)\n */\nint Channel::out(void *out_buf, int buffer_length) {\n    if (empty()) {\n        return SW_ERR;\n    }\n\n    auto *item = reinterpret_cast<ChannelSlice *>(static_cast<char *>(mem) + head);\n    assert(buffer_length >= item->length);\n    memcpy(out_buf, item->data, item->length);\n    head += (item->length + sizeof(item->length));\n    if (head >= (off_t) size) {\n        head = 0;\n        head_tag = 1 - head_tag;\n    }\n    num--;\n    bytes -= item->length;\n    return item->length;\n}\n\n/**\n * peek data\n */\nint Channel::peek(void *out, int buffer_length) const {\n    if (empty()) {\n        return SW_ERR;\n    }\n\n    lock->lock();\n    auto *item = reinterpret_cast<ChannelSlice *>(static_cast<char *>(mem) + head);\n    assert(buffer_length >= item->length);\n    memcpy(out, item->data, item->length);\n    int length = item->length;\n    lock->unlock();\n\n    return length;\n}\n\n/**\n * wait notify\n */\nint Channel::wait() const {\n    assert(flags & SW_CHAN_NOTIFY);\n    uint64_t value;\n    return notify_pipe->read(&value, sizeof(value)) > 0 ? SW_OK : SW_ERR;\n}\n\n/**\n * new data coming, notify to customer\n */\nint Channel::notify() const {\n    assert(flags & SW_CHAN_NOTIFY);\n    uint64_t value = 1;\n    return notify_pipe->write(&value, sizeof(value)) == sizeof(value) ? SW_OK : SW_ERR;\n}\n\n/**\n * push data (lock)\n */\nint Channel::push(const void *in_data, int data_length) {\n    assert(flags & SW_CHAN_LOCK);\n    lock->lock();\n    int ret = in(in_data, data_length);\n    lock->unlock();\n    return ret;\n}\n\n/**\n * free channel\n */\nvoid Channel::destroy() {\n    if (flags & SW_CHAN_LOCK) {\n        delete lock;\n    }\n    if (flags & SW_CHAN_NOTIFY) {\n        notify_pipe->close();\n        delete notify_pipe;\n    }\n    if (flags & SW_CHAN_SHM) {\n        sw_shm_free(this);\n    } else {\n        sw_free(this);\n    }\n}\n\n/**\n * pop data (lock)\n */\nint Channel::pop(void *out_buf, int buffer_length) {\n    assert(flags & SW_CHAN_LOCK);\n    lock->lock();\n    int n = out(out_buf, buffer_length);\n    lock->unlock();\n    return n;\n}\n\nvoid Channel::print() const {\n    printf(\"Channel\\n{\\n\"\n           \"    off_t head = %ld;\\n\"\n           \"    off_t tail = %ld;\\n\"\n           \"    size_t size = %ld;\\n\"\n           \"    char head_tag = %d;\\n\"\n           \"    char tail_tag = %d;\\n\"\n           \"    int num = %d;\\n\"\n           \"    size_t bytes = %ld;\\n\"\n           \"    int flag = %d;\\n\"\n           \"    int maxlen = %d;\\n\"\n           \"\\n}\\n\",\n           (long) head,\n           (long) tail,\n           size,\n           tail_tag,\n           head_tag,\n           num,\n           bytes,\n           flags,\n           maxlen);\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "src/core/crc32.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole.h\"\n#include \"swoole_hash.h\"\n\nstatic constexpr int CRC32_TABLE_SIZE = 256;\nstatic uint32_t crc32_table[CRC32_TABLE_SIZE];\nstatic bool generated = false;\n\nstatic void generate_table(uint32_t (&table)[CRC32_TABLE_SIZE]) {\n    uint32_t polynomial = 0xEDB88320;\n    for (uint32_t i = 0; i < CRC32_TABLE_SIZE; i++) {\n        uint32_t c = i;\n        for (size_t j = 0; j < 8; j++) {\n            if (c & 1) {\n                c = polynomial ^ (c >> 1);\n            } else {\n                c >>= 1;\n            }\n        }\n        table[i] = c;\n    }\n}\n\nuint32_t swoole_crc32(const char *data, size_t size) {\n    if (sw_unlikely(!generated)) {\n        generate_table(crc32_table);\n    }\n\n    uint32_t crcinit = 0;\n    uint32_t crc = crcinit ^ 0xffffffff;\n    for (; size--; ++data) {\n        crc = ((crc >> 8) & 0x00ffffff) ^ crc32_table[(crc ^ (*data)) & 0xff];\n    }\n\n    return (crc ^ 0xffffffff);\n}\n"
  },
  {
    "path": "src/core/error.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole.h\"\n\n#include <unordered_set>\n\nstatic std::unordered_set<int> ignored_errors;\n\nnamespace swoole {\nException::Exception(const int code) noexcept : code(code) {\n    msg = swoole_strerror(code);\n}\n}  // namespace swoole\n\nconst char *swoole_strerror(int code) {\n    if (code < SW_ERROR_BEGIN) {\n        return strerror(code);\n    }\n    /* swstrerror {{{*/\n    switch (code) {\n    case SW_ERROR_MALLOC_FAIL:\n        return \"Malloc fail\";\n    case SW_ERROR_SYSTEM_CALL_FAIL:\n        return \"System call fail\";\n    case SW_ERROR_PHP_FATAL_ERROR:\n        return \"PHP fatal error\";\n    case SW_ERROR_NAME_TOO_LONG:\n        return \"Name too long\";\n    case SW_ERROR_INVALID_PARAMS:\n        return \"Invalid params\";\n    case SW_ERROR_QUEUE_FULL:\n        return \"Queue full\";\n    case SW_ERROR_OPERATION_NOT_SUPPORT:\n        return \"Operation not support\";\n    case SW_ERROR_PROTOCOL_ERROR:\n        return \"Protocol error\";\n    case SW_ERROR_WRONG_OPERATION:\n        return \"Wrong operation\";\n    case SW_ERROR_PHP_RUNTIME_NOTICE:\n        return \"PHP runtime notice\";\n    case SW_ERROR_FOR_TEST:\n        return \"For test\";\n    case SW_ERROR_NO_PAYLOAD:\n        return \"No payload\";\n    case SW_ERROR_UNDEFINED_BEHAVIOR:\n        return \"Undefined behavior\";\n    case SW_ERROR_NOT_THREAD_SAFETY:\n        return \"Not thread safety\";\n    case SW_ERROR_FILE_NOT_EXIST:\n        return \"File not exist\";\n    case SW_ERROR_FILE_TOO_LARGE:\n        return \"File too large\";\n    case SW_ERROR_FILE_EMPTY:\n        return \"File empty\";\n    case SW_ERROR_DIR_NOT_EXIST:\n        return \"Dir not exist\";\n    case SW_ERROR_DNSLOOKUP_DUPLICATE_REQUEST:\n        return \"DNS Lookup duplicate request\";\n    case SW_ERROR_DNSLOOKUP_RESOLVE_FAILED:\n        return \"DNS Lookup resolve failed\";\n    case SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT:\n        return \"DNS Lookup resolve timeout\";\n    case SW_ERROR_DNSLOOKUP_UNSUPPORTED:\n        return \"DNS Lookup unsupported\";\n    case SW_ERROR_DNSLOOKUP_NO_SERVER:\n        return \"DNS Lookup no server\";\n    case SW_ERROR_BAD_IPV6_ADDRESS:\n        return \"Bad ipv6 address\";\n    case SW_ERROR_UNREGISTERED_SIGNAL:\n        return \"Unregistered signal\";\n    case SW_ERROR_BAD_HOST_ADDR:\n        return \"Bad host addr\";\n    case SW_ERROR_BAD_PORT:\n        return \"Bad port\";\n    case SW_ERROR_BAD_SOCKET_TYPE:\n        return \"Bad socket type\";\n    case SW_ERROR_EVENT_REMOVE_FAILED:\n        return \"Event remove failed\";\n    case SW_ERROR_EVENT_ADD_FAILED:\n        return \"Event add failed\";\n    case SW_ERROR_EVENT_UPDATE_FAILED:\n        return \"Event update failed\";\n    case SW_ERROR_EVENT_UNKNOWN_DATA:\n        return \"Event unknown data\";\n    case SW_ERROR_SESSION_CLOSED_BY_SERVER:\n        return \"Session closed by server\";\n    case SW_ERROR_SESSION_CLOSED_BY_CLIENT:\n        return \"Session closed by client\";\n    case SW_ERROR_SESSION_CLOSING:\n        return \"Session closing\";\n    case SW_ERROR_SESSION_CLOSED:\n        return \"Session closed\";\n    case SW_ERROR_SESSION_NOT_EXIST:\n        return \"Session not exist\";\n    case SW_ERROR_SESSION_INVALID_ID:\n        return \"Session invalid id\";\n    case SW_ERROR_SESSION_DISCARD_TIMEOUT_DATA:\n        return \"Session discard timeout data\";\n    case SW_ERROR_SESSION_DISCARD_DATA:\n        return \"Session discard data\";\n    case SW_ERROR_OUTPUT_BUFFER_OVERFLOW:\n        return \"Output buffer overflow\";\n    case SW_ERROR_OUTPUT_SEND_YIELD:\n        return \"Output send yield\";\n    case SW_ERROR_SSL_NOT_READY:\n        return \"SSL not ready\";\n    case SW_ERROR_SSL_CANNOT_USE_SENFILE:\n        return \"SSL cannot use senfile\";\n    case SW_ERROR_SSL_EMPTY_PEER_CERTIFICATE:\n        return \"SSL empty peer certificate\";\n    case SW_ERROR_SSL_VERIFY_FAILED:\n        return \"SSL verify failed\";\n    case SW_ERROR_SSL_BAD_CLIENT:\n        return \"SSL bad client\";\n    case SW_ERROR_SSL_BAD_PROTOCOL:\n        return \"SSL bad protocol\";\n    case SW_ERROR_SSL_RESET:\n        return \"SSL reset\";\n    case SW_ERROR_SSL_HANDSHAKE_FAILED:\n        return \"SSL handshake failed\";\n    case SW_ERROR_SSL_CREATE_CONTEXT_FAILED:\n        return \"SSL create context failed\";\n    case SW_ERROR_SSL_CREATE_SESSION_FAILED:\n        return \"SSL create session failed\";\n    case SW_ERROR_PACKAGE_LENGTH_TOO_LARGE:\n        return \"Package length too large\";\n    case SW_ERROR_PACKAGE_LENGTH_NOT_FOUND:\n        return \"Package length not found\";\n    case SW_ERROR_DATA_LENGTH_TOO_LARGE:\n        return \"Data length too large\";\n    case SW_ERROR_PACKAGE_MALFORMED_DATA:\n        return \"Package malformed data\";\n    case SW_ERROR_TASK_PACKAGE_TOO_BIG:\n        return \"Task package too big\";\n    case SW_ERROR_TASK_DISPATCH_FAIL:\n        return \"Task dispatch fail\";\n    case SW_ERROR_TASK_TIMEOUT:\n        return \"Task timeout\";\n    case SW_ERROR_HTTP2_STREAM_ID_TOO_BIG:\n        return \"Http2 stream id too big\";\n    case SW_ERROR_HTTP2_STREAM_NO_HEADER:\n        return \"Http2 stream no header\";\n    case SW_ERROR_HTTP2_STREAM_NOT_FOUND:\n        return \"Http2 stream not found\";\n    case SW_ERROR_HTTP2_STREAM_IGNORE:\n        return \"Http2 stream ignore\";\n    case SW_ERROR_HTTP2_SEND_CONTROL_FRAME_FAILED:\n        return \"Http2 send control frame failed\";\n    case SW_ERROR_HTTP2_INTERNAL_ERROR:\n        return \"Http2 internal error\";\n    case SW_ERROR_AIO_BAD_REQUEST:\n        return \"Aio bad request\";\n    case SW_ERROR_AIO_CANCELED:\n        return \"Aio canceled\";\n    case SW_ERROR_AIO_TIMEOUT:\n        return \"Aio timeout\";\n    case SW_ERROR_CLIENT_NO_CONNECTION:\n        return \"Client no connection\";\n    case SW_ERROR_SOCKET_CLOSED:\n        return \"Socket closed\";\n    case SW_ERROR_SOCKET_POLL_TIMEOUT:\n        return \"Socket poll timeout\";\n    case SW_ERROR_SOCKET_NOT_EXISTS:\n        return \"Socket not exists\";\n    case SW_ERROR_SOCKS5_UNSUPPORT_VERSION:\n        return \"Socks5 unsupport version\";\n    case SW_ERROR_SOCKS5_UNSUPPORT_METHOD:\n        return \"Socks5 unsupport method\";\n    case SW_ERROR_SOCKS5_AUTH_FAILED:\n        return \"Socks5 auth failed\";\n    case SW_ERROR_SOCKS5_SERVER_ERROR:\n        return \"Socks5 server error\";\n    case SW_ERROR_SOCKS5_HANDSHAKE_FAILED:\n        return \"Socks5 handshake failed\";\n    case SW_ERROR_SOCKS5_CONNECT_FAILED:\n        return \"Socks5 connect failed\";\n    case SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR:\n        return \"Http proxy handshake error\";\n    case SW_ERROR_HTTP_INVALID_PROTOCOL:\n        return \"Http invalid protocol\";\n    case SW_ERROR_HTTP_PROXY_HANDSHAKE_FAILED:\n        return \"Http proxy handshake failed\";\n    case SW_ERROR_HTTP_PROXY_BAD_RESPONSE:\n        return \"Http proxy bad response\";\n    case SW_ERROR_HTTP_CONFLICT_HEADER:\n        return \"Http conflict header\";\n    case SW_ERROR_HTTP_CONTEXT_UNAVAILABLE:\n        return \"Http context unavailable\";\n    case SW_ERROR_HTTP_COOKIE_UNAVAILABLE:\n        return \"Http cookie unavailable\";\n    case SW_ERROR_WEBSOCKET_BAD_CLIENT:\n        return \"Websocket bad client\";\n    case SW_ERROR_WEBSOCKET_BAD_OPCODE:\n        return \"Websocket bad opcode\";\n    case SW_ERROR_WEBSOCKET_UNCONNECTED:\n        return \"Websocket unconnected\";\n    case SW_ERROR_WEBSOCKET_HANDSHAKE_FAILED:\n        return \"Websocket handshake failed\";\n    case SW_ERROR_WEBSOCKET_PACK_FAILED:\n        return \"Websocket pack failed\";\n    case SW_ERROR_WEBSOCKET_UNPACK_FAILED:\n        return \"Websocket unpack failed\";\n    case SW_ERROR_WEBSOCKET_INCOMPLETE_PACKET:\n        return \"Websocket incomplete packet\";\n    case SW_ERROR_SERVER_MUST_CREATED_BEFORE_CLIENT:\n        return \"Server must created before client\";\n    case SW_ERROR_SERVER_TOO_MANY_SOCKET:\n        return \"Server too many socket\";\n    case SW_ERROR_SERVER_WORKER_TERMINATED:\n        return \"Server worker terminated\";\n    case SW_ERROR_SERVER_INVALID_LISTEN_PORT:\n        return \"Server invalid listen port\";\n    case SW_ERROR_SERVER_TOO_MANY_LISTEN_PORT:\n        return \"Server too many listen port\";\n    case SW_ERROR_SERVER_PIPE_BUFFER_FULL:\n        return \"Server pipe buffer full\";\n    case SW_ERROR_SERVER_NO_IDLE_WORKER:\n        return \"Server no idle worker\";\n    case SW_ERROR_SERVER_ONLY_START_ONE:\n        return \"Server only start one\";\n    case SW_ERROR_SERVER_SEND_IN_MASTER:\n        return \"Server send in master\";\n    case SW_ERROR_SERVER_INVALID_REQUEST:\n        return \"Server invalid request\";\n    case SW_ERROR_SERVER_CONNECT_FAIL:\n        return \"Server connect fail\";\n    case SW_ERROR_SERVER_INVALID_COMMAND:\n        return \"Server invalid command\";\n    case SW_ERROR_SERVER_IS_NOT_REGULAR_FILE:\n        return \"Server is not regular file\";\n    case SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT:\n        return \"Server send to woker timeout\";\n    case SW_ERROR_SERVER_INVALID_CALLBACK:\n        return \"Server invalid callback\";\n    case SW_ERROR_SERVER_UNRELATED_THREAD:\n        return \"Server unrelated thread\";\n    case SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT:\n        return \"Server worker exit timeout\";\n    case SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA:\n        return \"Server worker abnormal pipe data\";\n    case SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA:\n        return \"Server worker unprocessed data\";\n    case SW_ERROR_CO_OUT_OF_COROUTINE:\n        return \"Coroutine out of coroutine\";\n    case SW_ERROR_CO_HAS_BEEN_BOUND:\n        return \"Coroutine has been bound\";\n    case SW_ERROR_CO_HAS_BEEN_DISCARDED:\n        return \"Coroutine has been discarded\";\n    case SW_ERROR_CO_MUTEX_DOUBLE_UNLOCK:\n        return \"Coroutine mutex double unlock\";\n    case SW_ERROR_CO_BLOCK_OBJECT_LOCKED:\n        return \"Coroutine block object locked\";\n    case SW_ERROR_CO_BLOCK_OBJECT_WAITING:\n        return \"Coroutine block object waiting\";\n    case SW_ERROR_CO_YIELD_FAILED:\n        return \"Coroutine yield failed\";\n    case SW_ERROR_CO_GETCONTEXT_FAILED:\n        return \"Coroutine getcontext failed\";\n    case SW_ERROR_CO_SWAPCONTEXT_FAILED:\n        return \"Coroutine swapcontext failed\";\n    case SW_ERROR_CO_MAKECONTEXT_FAILED:\n        return \"Coroutine makecontext failed\";\n    case SW_ERROR_CO_IOCPINIT_FAILED:\n        return \"Coroutine iocpinit failed\";\n    case SW_ERROR_CO_PROTECT_STACK_FAILED:\n        return \"Coroutine protect stack failed\";\n    case SW_ERROR_CO_STD_THREAD_LINK_ERROR:\n        return \"Coroutine std thread link error\";\n    case SW_ERROR_CO_DISABLED_MULTI_THREAD:\n        return \"Coroutine disabled multi thread\";\n    case SW_ERROR_CO_CANNOT_CANCEL:\n        return \"Coroutine cannot cancel\";\n    case SW_ERROR_CO_NOT_EXISTS:\n        return \"Coroutine not exists\";\n    case SW_ERROR_CO_CANCELED:\n        return \"Coroutine canceled\";\n    case SW_ERROR_CO_TIMEDOUT:\n        return \"Coroutine timedout\";\n    case SW_ERROR_CO_SOCKET_CLOSE_WAIT:\n        return \"Coroutine socket close wait\";\n    default:\n        static char buffer[32];\n#ifndef __MACH__\n        snprintf(buffer, sizeof(buffer), \"Unknown error %d\", code);\n#else\n        snprintf(buffer, sizeof(buffer), \"Unknown error: %d\", code);\n#endif\n        return buffer;\n    }\n    /*}}}*/\n}\n\nvoid swoole_throw_error(int code) {\n    throw swoole::Exception(code);\n}\n\nvoid swoole_ignore_error(int code) {\n    ignored_errors.insert(code);\n}\n\nbool swoole_is_ignored_error(const int code) {\n    return ignored_errors.find(code) != ignored_errors.end();\n}\n\nvoid swoole_clear_last_error_msg() {\n    sw_error[0] = '\\0';\n}\n\nconst char *swoole_get_last_error_msg() {\n    return sw_error;\n}\n"
  },
  {
    "path": "src/core/heap.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole.h\"\n#include \"swoole_heap.h\"\n\n#define left(i) ((i) << 1)\n#define right(i) (((i) << 1) + 1)\n#define parent(i) ((i) >> 1)\n\nnamespace swoole {\n\nHeap::Heap(size_t _n, Type _type) {\n    if (!((nodes = static_cast<HeapNode **>(sw_malloc((_n + 1) * sizeof(void *)))))) {\n        throw std::bad_alloc();\n    }\n    num = 1;\n    size = (_n + 1);\n    type = _type;\n}\n\nHeap::~Heap() {\n    for (uint32_t i = 1; i < num; i++) {\n        if (nodes[i]) {\n            delete nodes[i];\n        }\n    }\n    sw_free(nodes);\n}\n\nint Heap::compare(uint64_t a, uint64_t b) const {\n    if (type == MIN_HEAP) {\n        return a > b;\n    } else {\n        return a < b;\n    }\n}\n\nuint32_t Heap::maxchild(uint32_t i) const {\n    uint32_t child_i = left(i);\n    if (child_i >= num) {\n        return 0;\n    }\n    HeapNode *child_node = nodes[child_i];\n    if ((child_i + 1) < num && compare(child_node->priority, nodes[child_i + 1]->priority)) {\n        child_i++;\n    }\n    return child_i;\n}\n\nvoid Heap::bubble_up(uint32_t i) const {\n    HeapNode *moving_node = nodes[i];\n    uint32_t parent_i;\n\n    for (parent_i = parent(i); (i > 1) && compare(nodes[parent_i]->priority, moving_node->priority);\n         i = parent_i, parent_i = parent(i)) {\n        nodes[i] = nodes[parent_i];\n        nodes[i]->position = i;\n    }\n\n    nodes[i] = moving_node;\n    moving_node->position = i;\n}\n\nvoid Heap::percolate_down(uint32_t i) const {\n    uint32_t child_i;\n    HeapNode *moving_node = nodes[i];\n\n    while (((child_i = maxchild(i))) && compare(moving_node->priority, nodes[child_i]->priority)) {\n        nodes[i] = nodes[child_i];\n        nodes[i]->position = i;\n        i = child_i;\n    }\n\n    nodes[i] = moving_node;\n    moving_node->position = i;\n}\n\nHeapNode *Heap::push(uint64_t priority, void *data) {\n    HeapNode **tmp;\n    uint32_t i;\n\n    if (num >= size) {\n        uint32_t newsize = size * 2;\n        if (!((tmp = static_cast<HeapNode **>(sw_realloc(nodes, sizeof(HeapNode *) * newsize))))) {\n            return nullptr;\n        }\n        nodes = tmp;\n        size = newsize;\n    }\n\n    auto *node = new HeapNode;\n    node->priority = priority;\n    node->data = data;\n    i = num++;\n    nodes[i] = node;\n    bubble_up(i);\n    return node;\n}\n\nvoid Heap::change_priority(uint64_t new_priority, HeapNode *node) const {\n    uint32_t pos = node->position;\n    uint64_t old_pri = node->priority;\n\n    node->priority = new_priority;\n    if (compare(old_pri, new_priority)) {\n        bubble_up(pos);\n    } else {\n        percolate_down(pos);\n    }\n}\n\nvoid Heap::remove(HeapNode *node) {\n    uint32_t pos = node->position;\n    nodes[pos] = nodes[--num];\n\n    if (compare(node->priority, nodes[pos]->priority)) {\n        bubble_up(pos);\n    } else {\n        percolate_down(pos);\n    }\n    delete node;\n}\n\nvoid *Heap::pop() {\n    if (count() == 0) {\n        return nullptr;\n    }\n\n    HeapNode *head = nodes[1];\n    nodes[1] = nodes[--num];\n    percolate_down(1);\n\n    void *data = head->data;\n    delete head;\n    return data;\n}\n\nvoid *Heap::peek() const {\n    if (num == 1) {\n        return nullptr;\n    }\n    HeapNode *node = nodes[1];\n    if (!node) {\n        return nullptr;\n    }\n    return node->data;\n}\n\nvoid Heap::print() const {\n    for (uint32_t i = 1; i < num; i++) {\n        printf(\"#%u\\tpriority=%ld, data=%p\\n\", i, (long) nodes[i]->priority, nodes[i]->data);\n    }\n}\n}  // namespace swoole\n"
  },
  {
    "path": "src/core/log.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_process_pool.h\"\n\n#include <cstring>\n#include <fcntl.h>\n\n#include <string>\n#include <chrono>  // NOLINT [build/c++11]\n\nnamespace swoole {\n\nstd::string Logger::get_pretty_name(const std::string &pretty_function, bool strip) {\n    const size_t brackets = pretty_function.find_first_of('(');\n    if (brackets == std::string::npos) {\n        return \"\";\n    }\n\n    const size_t begin = pretty_function.substr(0, brackets).rfind(' ') + 1;\n    const size_t end = brackets - begin;\n    if (!strip) {\n        return pretty_function.substr(begin, end);\n    }\n\n    auto method_name = pretty_function.substr(begin, end);\n    size_t count = 0, index = method_name.length();\n    while (true) {\n        index = method_name.rfind(\"::\", index);\n        if (index == std::basic_string<char>::npos) {\n            if (count == 1) {\n                return method_name.substr(method_name.rfind(\"::\") + 2);\n            }\n            break;\n        }\n        count++;\n        if (count == 2) {\n            return method_name.substr(index + 2);\n        }\n        index -= 2;\n    }\n\n    return method_name;\n}\n\nbool Logger::open(const char *_log_file) {\n    if (opened) {\n        close();\n    }\n\n    log_file = _log_file;\n\n    if (log_rotation) {\n        log_real_file = gen_real_file(log_file);\n    } else {\n        log_real_file = log_file;\n    }\n\n    auto log_fd = ::open(log_real_file.c_str(), O_APPEND | O_RDWR | O_CREAT, 0666);\n    if (log_fd < 0) {\n        swoole_error_log(SW_LOG_WARNING,\n                         SW_ERROR_SYSTEM_CALL_FAIL,\n                         \"open('%s') failed. Error: %s[%d]\",\n                         log_real_file.c_str(),\n                         strerror(errno),\n                         errno);\n        opened = false;\n        log_file = \"\";\n        log_real_file = \"\";\n\n        return false;\n    } else {\n        opened = true;\n        log_fp = fdopen(log_fd, \"a\");\n\n        return true;\n    }\n}\n\nvoid Logger::set_stream(FILE *stream) {\n    if (opened) {\n        close();\n    }\n    log_fp = stream;\n}\n\nvoid Logger::close() {\n    if (opened) {\n        fclose(log_fp);\n        log_fp = stdout;\n        log_file = \"\";\n        opened = false;\n    }\n}\n\nint Logger::get_level() const {\n    return log_level;\n}\n\nvoid Logger::set_level(int level) {\n    if (level < SW_LOG_DEBUG) {\n        level = SW_LOG_DEBUG;\n    }\n    if (level > SW_LOG_NONE) {\n        level = SW_LOG_NONE;\n    }\n    log_level = level;\n}\n\nvoid Logger::set_rotation(int _rotation) {\n    log_rotation = _rotation;\n}\n\nbool Logger::redirect_stdout_and_stderr(bool enable) {\n    if (enable) {\n        if (!opened) {\n            swoole_warning(\"no log file opened\");\n            return false;\n        }\n        if (redirected) {\n            swoole_warning(\"has been redirected\");\n            return false;\n        }\n        if ((stdout_fd = dup(STDOUT_FILENO)) < 0) {\n            swoole_sys_warning(\"dup(STDOUT_FILENO) failed\");\n            return false;\n        }\n        if ((stderr_fd = dup(STDERR_FILENO)) < 0) {\n            swoole_sys_warning(\"dup(STDERR_FILENO) failed\");\n            return false;\n        }\n        swoole_redirect_stdout(fileno(log_fp));\n        redirected = true;\n    } else {\n        if (!redirected) {\n            swoole_warning(\"no redirected\");\n            return false;\n        }\n        if (dup2(stdout_fd, STDOUT_FILENO) < 0) {\n            swoole_sys_warning(\"dup2(STDOUT_FILENO) failed\");\n        }\n        if (dup2(stderr_fd, STDERR_FILENO) < 0) {\n            swoole_sys_warning(\"dup2(STDERR_FILENO) failed\");\n        }\n        ::close(stdout_fd);\n        ::close(stderr_fd);\n        stdout_fd = -1;\n        stderr_fd = -1;\n        redirected = false;\n    }\n\n    return true;\n}\n\nvoid Logger::reset() {\n    date_format = SW_LOG_DEFAULT_DATE_FORMAT;\n    date_with_microseconds = false;\n    log_rotation = SW_LOG_ROTATION_SINGLE;\n    log_level = SW_LOG_INFO;\n}\n\nbool Logger::set_date_format(const char *format) {\n    char date_str[SW_LOG_DATE_STRLEN];\n    time_t now_sec;\n\n    now_sec = ::time(nullptr);\n    size_t l_data_str = std::strftime(date_str, sizeof(date_str), format, std::localtime(&now_sec));\n\n    if (l_data_str == 0) {\n        swoole_set_last_error(SW_ERROR_INVALID_PARAMS);\n        swoole_error_log(\n            SW_LOG_WARNING, SW_ERROR_INVALID_PARAMS, \"The date format string[length=%ld] is too long\", strlen(format));\n\n        return false;\n    } else {\n        date_format = format;\n\n        return true;\n    }\n}\n\nvoid Logger::set_date_with_microseconds(bool enable) {\n    date_with_microseconds = enable;\n}\n\nvoid Logger::reopen_without_lock() {\n    if (!opened) {\n        return;\n    }\n\n    std::string new_log_file(log_file);\n    close();\n    open(new_log_file.c_str());\n    if (redirected) {\n        swoole_redirect_stdout(fileno(log_fp));\n    }\n}\n\nvoid Logger::reopen() {\n    std::unique_lock<std::mutex> _lock(lock);\n    reopen_without_lock();\n}\n\nconst char *Logger::get_real_file() {\n    return log_real_file.c_str();\n}\n\nconst char *Logger::get_file() const {\n    return log_file.c_str();\n}\n\nstd::string Logger::gen_real_file(const std::string &file) const {\n    char date_str[16];\n    auto now_sec = ::time(nullptr);\n    const char *fmt;\n\n    switch (log_rotation) {\n    case SW_LOG_ROTATION_MONTHLY:\n        fmt = \"%Y%m\";\n        break;\n    case SW_LOG_ROTATION_HOURLY:\n        fmt = \"%Y%m%d%H\";\n        break;\n    case SW_LOG_ROTATION_EVERY_MINUTE:\n        fmt = \"%Y%m%d%H%M\";\n        break;\n    case SW_LOG_ROTATION_DAILY:\n    default:\n        fmt = \"%Y%m%d\";\n        break;\n    }\n\n    size_t l_data_str = std::strftime(date_str, sizeof(date_str), fmt, std::localtime(&now_sec));\n    std::string real_file = file + \".\" + std::string(date_str, l_data_str);\n\n    return real_file;\n}\n\nbool Logger::is_opened() const {\n    return opened;\n}\n\nvoid Logger::put(int level, const char *content, size_t length) {\n    const char *level_str;\n    char date_str[SW_LOG_DATE_STRLEN];\n    char log_str[SW_LOG_BUFFER_SIZE];\n\n    if (level < log_level) {\n        return;\n    }\n\n    switch (level) {\n    case SW_LOG_DEBUG:\n        level_str = \"DEBUG\";\n        break;\n    case SW_LOG_TRACE:\n        level_str = \"TRACE\";\n        break;\n    case SW_LOG_NOTICE:\n        level_str = \"NOTICE\";\n        break;\n    case SW_LOG_WARNING:\n        level_str = \"WARNING\";\n        break;\n    case SW_LOG_ERROR:\n        level_str = \"ERROR\";\n        break;\n    case SW_LOG_INFO:\n    default:\n        level_str = \"INFO\";\n        break;\n    }\n\n    auto now = std::chrono::system_clock::now();\n    auto now_sec = std::chrono::system_clock::to_time_t(now);\n    size_t l_data_str = std::strftime(date_str, sizeof(date_str), date_format.c_str(), std::localtime(&now_sec));\n\n    if (log_rotation) {\n        std::string tmp = gen_real_file(log_file);\n        /**\n         * If the current thread fails to acquire the lock, it will forgo executing the log rotation.\n         */\n        if (tmp != log_real_file && lock.try_lock()) {\n            reopen_without_lock();\n            lock.unlock();\n        }\n    }\n\n    if (date_with_microseconds) {\n        auto now_us = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count();\n        l_data_str += sw_snprintf(\n            date_str + l_data_str, SW_LOG_DATE_STRLEN - l_data_str, \"<.%lld>\", (long long) now_us - now_sec * 1000000);\n    }\n\n    int worker_id = swoole_get_worker_id();\n    pid_t worker_pid = swoole_get_worker_pid();\n    if (worker_pid == 0) {\n        worker_pid = getpid();\n    }\n    char worker_symbol = swoole_get_worker_symbol();\n\n    size_t n = sw_snprintf(log_str,\n                           SW_LOG_BUFFER_SIZE,\n                           \"[%.*s %c%d.%d]\\t%s\\t%.*s\\n\",\n                           static_cast<int>(l_data_str),\n                           date_str,\n                           worker_symbol,\n                           worker_pid,\n                           worker_id,\n                           level_str,\n                           static_cast<int>(length),\n                           content);\n\n    lock.lock();\n    if (opened) {\n        flockfile(log_fp);\n    }\n    fwrite(log_str, n, 1, log_fp);\n    fflush(log_fp);\n    if (opened) {\n        funlockfile(log_fp);\n    }\n    lock.unlock();\n\n    if (display_backtrace_) {\n        swoole_print_backtrace();\n    }\n}\n}  // namespace swoole\n"
  },
  {
    "path": "src/core/misc.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole.h\"\n\n#include <thread>\n\nvoid sw_spinlock(sw_atomic_t *lock) {\n    uint32_t i, n;\n    while (true) {\n        if (*lock == 0 && sw_atomic_cmp_set(lock, 0, 1)) {\n            return;\n        }\n        if (SW_CPU_NUM > 1) {\n            for (n = 1; n < SW_SPINLOCK_LOOP_N; n <<= 1) {\n                for (i = 0; i < n; i++) {\n                    sw_atomic_cpu_pause();\n                }\n\n                if (*lock == 0 && sw_atomic_cmp_set(lock, 0, 1)) {\n                    return;\n                }\n            }\n        }\n        std::this_thread::yield();\n    }\n}\n\n#ifdef HAVE_FUTEX\n#include <linux/futex.h>\n#include <sys/syscall.h>\n\nint sw_atomic_futex_wait(sw_atomic_t *atomic, double timeout) {\n    if (sw_atomic_cmp_set(atomic, 1, 0)) {\n        return 0;\n    }\n\n    int ret;\n    timespec _timeout;\n\n    if (timeout > 0) {\n        _timeout.tv_sec = static_cast<long>(timeout);\n        _timeout.tv_nsec = (timeout - _timeout.tv_sec) * 1000 * 1000 * 1000;\n        ret = syscall(SYS_futex, atomic, FUTEX_WAIT, 0, &_timeout, NULL, 0);\n    } else {\n        ret = syscall(SYS_futex, atomic, FUTEX_WAIT, 0, NULL, NULL, 0);\n    }\n    if (ret == 0 && sw_atomic_cmp_set(atomic, 1, 0)) {\n        return 0;\n    } else {\n        return -1;\n    }\n}\n\nint sw_atomic_futex_wakeup(sw_atomic_t *atomic, int n) {\n    if (sw_atomic_cmp_set(atomic, 0, 1)) {\n        return syscall(SYS_futex, atomic, FUTEX_WAKE, n, NULL, NULL, 0);\n    } else {\n        return 0;\n    }\n}\n#else\nint sw_atomic_futex_wait(sw_atomic_t *atomic, double timeout) {\n    if (sw_atomic_cmp_set(atomic, (sw_atomic_t) 1, (sw_atomic_t) 0)) {\n        return 0;\n    }\n    timeout = timeout <= 0 ? INT_MAX : timeout;\n    int32_t i = (int32_t) sw_atomic_sub_fetch(atomic, 1);\n    while (timeout > 0) {\n        if ((int32_t) *atomic > i) {\n            return 0;\n        } else {\n            usleep(1000);\n            timeout -= 0.001;\n        }\n    }\n    sw_atomic_fetch_add(atomic, 1);\n    return -1;\n}\n\nint sw_atomic_futex_wakeup(sw_atomic_t *atomic, int n) {\n    if (1 == (int32_t) *atomic) {\n        return 0;\n    }\n    sw_atomic_fetch_add(atomic, n);\n    return 0;\n}\n#endif\n\n/* {{{ DJBX33A (Daniel J. Bernstein, Times 33 with Addition)\n *\n * This is Daniel J. Bernstein's popular `times 33' hash function as\n * posted by him years ago on comp->lang.c. It basically uses a function\n * like ``hash(i) = hash(i-1) * 33 + str[i]''. This is one of the best\n * known hash functions for strings. Because it is both computed very\n * fast and distributes very well.\n *\n * The magic of number 33, i.e. why it works better than many other\n * constants, prime or not, has never been adequately explained by\n * anyone. So I try an explanation: if one experimentally tests all\n * multipliers between 1 and 256 (as RSE did now) one detects that even\n * numbers are not useable at all. The remaining 128 odd numbers\n * (except for the number 1) work more or less all equally well. They\n * all distribute in an acceptable way and this way fill a hash table\n * with an average percent of approx. 86%.\n *\n * If one compares the Chi^2 values of the variants, the number 33 not\n * even has the best value. But the number 33 and a few other equally\n * good numbers like 17, 31, 63, 127 and 129 have nevertheless a great\n * advantage to the remaining numbers in the large set of possible\n * multipliers: their multiply operation can be replaced by a faster\n * operation based on just one shift plus either a single addition\n * or subtraction operation. And because a hash function has to both\n * distribute good _and_ has to be very fast to compute, those few\n * numbers should be preferred and seems to be the reason why Daniel J.\n * Bernstein also preferred it.\n *\n *                  -- Ralf S. Engelschall <rse@engelschall.com>\n */\nuint64_t swoole_hash_php(const char *key, size_t len) {\n    ulong_t hash = 5381;\n    /* variant with the hash unrolled eight times */\n    for (; len >= 8; len -= 8) {\n        hash = ((hash << 5) + hash) + *key++;\n        hash = ((hash << 5) + hash) + *key++;\n        hash = ((hash << 5) + hash) + *key++;\n        hash = ((hash << 5) + hash) + *key++;\n        hash = ((hash << 5) + hash) + *key++;\n        hash = ((hash << 5) + hash) + *key++;\n        hash = ((hash << 5) + hash) + *key++;\n        hash = ((hash << 5) + hash) + *key++;\n    }\n\n    switch (len) {\n    case 7:\n        hash = ((hash << 5) + hash) + *key++; /* fallthrough... */\n    /* no break */\n    case 6:\n        hash = ((hash << 5) + hash) + *key++; /* fallthrough... */\n    /* no break */\n    case 5:\n        hash = ((hash << 5) + hash) + *key++; /* fallthrough... */\n    /* no break */\n    case 4:\n        hash = ((hash << 5) + hash) + *key++; /* fallthrough... */\n    /* no break */\n    case 3:\n        hash = ((hash << 5) + hash) + *key++; /* fallthrough... */\n    /* no break */\n    case 2:\n        hash = ((hash << 5) + hash) + *key++; /* fallthrough... */\n    /* no break */\n    case 1:\n        hash = ((hash << 5) + hash) + *key++;\n        break;\n    case 0:\n        break;\n    default:\n        break;\n    }\n    return hash;\n}\n\n#define HASH_JEN_MIX(a, b, c)                                                                                          \\\n    do {                                                                                                               \\\n        a -= b;                                                                                                        \\\n        a -= c;                                                                                                        \\\n        a ^= (c >> 13);                                                                                                \\\n        b -= c;                                                                                                        \\\n        b -= a;                                                                                                        \\\n        b ^= (a << 8);                                                                                                 \\\n        c -= a;                                                                                                        \\\n        c -= b;                                                                                                        \\\n        c ^= (b >> 13);                                                                                                \\\n        a -= b;                                                                                                        \\\n        a -= c;                                                                                                        \\\n        a ^= (c >> 12);                                                                                                \\\n        b -= c;                                                                                                        \\\n        b -= a;                                                                                                        \\\n        b ^= (a << 16);                                                                                                \\\n        c -= a;                                                                                                        \\\n        c -= b;                                                                                                        \\\n        c ^= (b >> 5);                                                                                                 \\\n        a -= b;                                                                                                        \\\n        a -= c;                                                                                                        \\\n        a ^= (c >> 3);                                                                                                 \\\n        b -= c;                                                                                                        \\\n        b -= a;                                                                                                        \\\n        b ^= (a << 10);                                                                                                \\\n        c -= a;                                                                                                        \\\n        c -= b;                                                                                                        \\\n        c ^= (b >> 15);                                                                                                \\\n    } while (0)\n\n/**\n * MurmurHash2(Austin Appleby)\n */\nuint64_t swoole_hash_jenkins(const char *key, size_t keylen) {\n    unsigned j;\n    uint64_t hashv = 0xfeedbeef;\n    unsigned i = j = 0x9e3779b9;\n    auto k = (unsigned) (keylen);\n\n    while (k >= 12) {\n        i += (key[0] + ((unsigned) key[1] << 8) + ((unsigned) key[2] << 16) + ((unsigned) key[3] << 24));\n        j += (key[4] + ((unsigned) key[5] << 8) + ((unsigned) key[6] << 16) + ((unsigned) key[7] << 24));\n        hashv += (key[8] + ((unsigned) key[9] << 8) + ((unsigned) key[10] << 16) + ((unsigned) key[11] << 24));\n\n        HASH_JEN_MIX(i, j, hashv);\n\n        key += 12;\n        k -= 12;\n    }\n    hashv += keylen;\n    switch (k) {\n    case 11:\n        hashv += ((unsigned) key[10] << 24);\n        /* no break */\n    case 10:\n        hashv += ((unsigned) key[9] << 16);\n        /* no break */\n    case 9:\n        hashv += ((unsigned) key[8] << 8);\n        /* no break */\n    case 8:\n        j += ((unsigned) key[7] << 24);\n        /* no break */\n    case 7:\n        j += ((unsigned) key[6] << 16);\n        /* no break */\n    case 6:\n        j += ((unsigned) key[5] << 8);\n        /* no break */\n    case 5:\n        j += key[4];\n        /* no break */\n    case 4:\n        i += ((unsigned) key[3] << 24);\n        /* no break */\n    case 3:\n        i += ((unsigned) key[2] << 16);\n        /* no break */\n    case 2:\n        i += ((unsigned) key[1] << 8);\n        /* no break */\n    case 1:\n        i += key[0];\n    }\n    HASH_JEN_MIX(i, j, hashv);\n    return hashv;\n}\n\n/**\n * MurmurHash2(Austin Appleby)\n */\nuint64_t swoole_hash_austin(const char *key, size_t keylen) {\n    uint64_t h = 0 ^ keylen;\n\n    while (keylen >= 4) {\n        uint64_t k = key[0];\n        k |= key[1] << 8;\n        k |= key[2] << 16;\n        k |= key[3] << 24;\n\n        k *= 0x5bd1e995;\n        k ^= k >> 24;\n        k *= 0x5bd1e995;\n\n        h *= 0x5bd1e995;\n        h ^= k;\n\n        key += 4;\n        keylen -= 4;\n    }\n\n    switch (keylen) {\n    case 3:\n        h ^= key[2] << 16;\n        /* no break */\n    case 2:\n        h ^= key[1] << 8;\n        /* no break */\n    case 1:\n        h ^= key[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/string.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_string.h\"\n#include \"swoole_base64.h\"\n\n#include <memory>\n\nnamespace swoole {\n\nvoid String::alloc(size_t _size, const Allocator *_allocator) {\n    if (_allocator == nullptr) {\n        _allocator = sw_std_allocator();\n    }\n\n    _size = SW_MEM_ALIGNED_SIZE(_size);\n    length = 0;\n    size = _size;\n    offset = 0;\n    str = (char *) _allocator->malloc(_size);\n    allocator = _allocator;\n\n    if (str == nullptr) {\n        throw std::bad_alloc();\n    }\n}\n\nvoid String::move(String &&src) {\n    str = src.str;\n    length = src.length;\n    offset = src.offset;\n    size = src.size;\n    allocator = src.allocator;\n\n    src.str = nullptr;\n    src.length = 0;\n    src.size = 0;\n    src.offset = 0;\n}\n\nString &String::operator=(const String &src) noexcept {\n    if (&src == this) {\n        return *this;\n    }\n    if (allocator && str) {\n        allocator->free(str);\n    }\n    copy(src);\n    return *this;\n}\n\nvoid String::copy(const String &src) {\n    alloc(src.size, src.allocator);\n    memcpy(str, src.str, src.length);\n    length = src.length;\n    offset = src.offset;\n}\n\nString &String::operator=(String &&src) noexcept {\n    if (&src == this) {\n        return *this;\n    }\n    if (allocator && str) {\n        allocator->free(str);\n    }\n    move(std::move(src));\n    return *this;\n}\n\nvoid String::append(const String &append_str) {\n    size_t new_size = length + append_str.length;\n    if (new_size > size) {\n        reserve(new_size);\n    }\n\n    memcpy(str + length, append_str.str, append_str.length);\n    length += append_str.length;\n}\n\nvoid String::write(off_t _offset, const String &write_str) {\n    write(_offset, write_str.str, write_str.length);\n}\n\nvoid String::write(off_t _offset, const char *write_str, size_t _length) {\n    size_t new_length = _offset + _length;\n    if (new_length > size) {\n        reserve(swoole_size_align(new_length * 2, swoole_pagesize()));\n    }\n\n    memcpy(str + _offset, write_str, _length);\n    if (new_length > length) {\n        length = new_length;\n    }\n}\n\nvoid String::grow(size_t incr_value) {\n    length += incr_value;\n    if (length == size) {\n        reserve(size * 2);\n    }\n}\n\nString String::substr(size_t _offset, size_t len) const {\n    if (_offset + len > length) {\n        return {};\n    }\n    String _substr(len);\n    _substr.append(str + _offset, len);\n    return _substr;\n}\n\nchar *String::pop(size_t init_size) {\n    assert(length >= (size_t) offset);\n\n    char *val = str;\n    size_t _length = length - offset;\n    size_t alloc_size = SW_MEM_ALIGNED_SIZE(_length == 0 ? init_size : SW_MAX(_length, init_size));\n\n    auto new_val = static_cast<char *>(allocator->malloc(alloc_size));\n    if (new_val == nullptr) {\n        return nullptr;\n    }\n\n    str = new_val;\n    size = alloc_size;\n    length = _length;\n    if (length > 0) {\n        memcpy(new_val, val + offset, length);\n    }\n    offset = 0;\n\n    return val;\n}\n\n/**\n * migrate data to head, [offset, length - offset] -> [0, length - offset]\n */\nvoid String::reduce(off_t _offset) {\n    assert(_offset >= 0 && (size_t) _offset <= length);\n    if (sw_unlikely(_offset == 0)) {\n        return;\n    }\n    length -= _offset;\n    offset = 0;\n    if (length == 0) {\n        return;\n    }\n    memmove(str, str + _offset, length);\n}\n\nvoid String::print(bool print_value) const {\n    if (print_value) {\n        printf(\"String[length=%zu,size=%zu,offset=%jd]=%.*s\\n\", length, size, (intmax_t) offset, (int) length, str);\n    } else {\n        printf(\"String[length=%zu,size=%zu,offset=%jd]=%p\\n\", length, size, (intmax_t) offset, str);\n    }\n}\n\nvoid String::append(int value) {\n    char buf[16];\n    int s_len = swoole_itoa(buf, value);\n\n    size_t new_size = length + s_len;\n    if (new_size > size) {\n        reserve(new_size);\n    }\n\n    memcpy(str + length, buf, s_len);\n    length += s_len;\n}\n\nvoid String::append(const char *append_str, size_t _length) {\n    size_t new_size = length + _length;\n    if (new_size > size) {\n        reserve(new_size);\n    }\n\n    memcpy(str + length, append_str, _length);\n    length += _length;\n}\n\nbool String::append_random_bytes(size_t _length, bool base64) {\n    size_t new_size = length + _length;\n    size_t base_encode_size;\n\n    if (base64) {\n        base_encode_size = BASE64_ENCODE_OUT_SIZE(_length) + 1;\n        new_size += base_encode_size;\n    }\n\n    if (new_size > size) {\n        reserve(swoole_size_align(new_size * 2, swoole_pagesize()));\n    }\n\n    size_t n = swoole_random_bytes(str + length, _length);\n    if (n != _length) {\n        return false;\n    }\n\n    if (base64) {\n        std::unique_ptr<char[]> out(new char[base_encode_size]);\n        n = base64_encode((uchar *) str + length, _length, out.get());\n        memcpy(str + length, out.get(), n);\n    }\n\n    length += n;\n\n    return true;\n}\n\nvoid String::reserve(size_t new_size) {\n    if (size == 0) {\n        alloc(new_size, nullptr);\n        return;\n    }\n\n    new_size = SW_MEM_ALIGNED_SIZE(new_size);\n    auto new_str = static_cast<char *>(allocator->realloc(str, new_size));\n    if (new_str == nullptr) {\n        throw std::bad_alloc();\n    }\n\n    str = new_str;\n    size = new_size;\n}\n\nchar *String::release() {\n    char *tmp = str;\n    str = nullptr;\n    size = 0;\n    length = 0;\n    offset = 0;\n    return tmp;\n}\n\nvoid String::repeat(const char *data, size_t len, size_t n) {\n    assert(n > 0);\n    if (len == 1) {\n        if ((size < length + n)) {\n            reserve(length + n);\n        }\n        memset(str + length, data[0], n);\n        length += n;\n    } else {\n        for (size_t i = 0; i < n; i++) {\n            append(data, len);\n        }\n    }\n}\n\n/**\n * @return retval\n * 1. less than zero, the execution of the string_split function was terminated prematurely\n * 2. equal to zero, eof was not found in the target string\n * 3. greater than zero, 0 to retval has eof in the target string, and the position of retval is eof\n */\nssize_t String::split(const char *delimiter, size_t delimiter_length, const StringExplodeHandler &handler) {\n#ifdef SW_LOG_TRACE_OPEN\n    static int count;\n    count++;\n#endif\n    const char *start_addr = str + offset;\n    const char *delimiter_addr = swoole_strnstr(start_addr, length - offset, delimiter, delimiter_length);\n    off_t _offset = offset;\n\n    swoole_trace_log(SW_TRACE_EOF_PROTOCOL,\n                     \"#[0] count=%d, length=%ld, size=%ld, offset=%jd\",\n                     count,\n                     length,\n                     size,\n                     (intmax_t) offset);\n\n    while (delimiter_addr) {\n        size_t _length = delimiter_addr - start_addr + delimiter_length;\n        swoole_trace_log(SW_TRACE_EOF_PROTOCOL, \"#[4] count=%d, length=%zu\", count, (size_t) (_length + offset));\n        if (handler((char *) start_addr - _offset, _length + _offset) == false) {\n            return -1;\n        }\n        offset += _length;\n        start_addr = str + offset;\n        delimiter_addr = swoole_strnstr(start_addr, length - offset, delimiter, delimiter_length);\n        _offset = 0;\n    }\n\n    /**\n     * not found eof in str\n     */\n    if (_offset == offset) {\n        /**\n         * why is offset not equal to length,\n         * because the length may contain part of eof and the other part in the next recv\n         */\n        offset = length - delimiter_length;\n    }\n\n    size_t ret = start_addr - str - _offset;\n    if (ret > 0 && ret < length) {\n        swoole_trace_log(\n            SW_TRACE_EOF_PROTOCOL, \"#[5] count=%d, remaining_length=%zu\", count, (size_t) (length - offset));\n    } else if (ret >= length) {\n        swoole_trace_log(\n            SW_TRACE_EOF_PROTOCOL, \"#[3] length=%ld, size=%zu, offset=%jd\", length, size, (intmax_t) offset);\n    }\n\n    return ret;\n}\n}  // namespace swoole\n"
  },
  {
    "path": "src/core/timer.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_reactor.h\"\n#include \"swoole_timer.h\"\n\nnamespace swoole {\nTimer::Timer(bool manually_trigger) : heap(1024, Heap::MIN_HEAP) {\n    _current_id = -1;\n    next_msec_ = -1;\n    _next_id = 1;\n    round = 0;\n    base_time = get_absolute_msec();\n    init(manually_trigger);\n}\n\nvoid Timer::init(bool manually_trigger) {\n    if (manually_trigger) {\n        set = [](Timer *, long) -> int { return SW_OK; };\n        close = [](Timer *) {};\n        return;\n    }\n    if (SwooleTG.reactor) {\n        init_with_reactor(SwooleTG.reactor);\n    } else {\n        init_with_system_timer();\n    }\n}\n\nvoid Timer::release_node(TimerNode *tnode) {\n    if (tnode->destructor) {\n        tnode->destructor(tnode);\n    }\n    delete tnode;\n}\n\nvoid Timer::init_with_reactor(Reactor *reactor) {\n    reactor_ = reactor;\n    set = [](Timer *timer, long exec_msec) -> int {\n        timer->reactor_->timeout_msec = exec_msec;\n        return SW_OK;\n    };\n    close = [](Timer *timer) { timer->set(timer, -1); };\n\n    reactor->set_end_callback(Reactor::PRIORITY_TIMER, [this](Reactor *) { select(); });\n\n    reactor->set_exit_condition(Reactor::EXIT_CONDITION_TIMER,\n                                [this](Reactor *reactor, size_t &event_num) -> bool { return count() == 0; });\n\n    reactor->add_destroy_callback([](void *) {\n        if (swoole_timer_is_available()) {\n            swoole_timer_free();\n        }\n    });\n}\n\nvoid Timer::reinit(bool manually_trigger) {\n    close(this);\n    init(manually_trigger);\n    set(this, next_msec_);\n}\n\nTimer::~Timer() {\n    if (close) {\n        close(this);\n    }\n    for (const auto &iter : map) {\n        release_node(iter.second);\n    }\n}\n\nTimerNode *Timer::add(long _msec, bool persistent, void *data, const TimerCallback &callback) {\n    if (sw_unlikely(_msec <= 0)) {\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_INVALID_PARAMS, \"msec value[%ld] is invalid\", _msec);\n        return nullptr;\n    }\n\n    auto *tnode = new TimerNode();\n    tnode->id = _next_id++;\n    tnode->data = data;\n    tnode->type = TimerNode::TYPE_KERNEL;\n    tnode->exec_msec = get_relative_msec() + _msec;\n    tnode->interval = persistent ? _msec : 0;\n    tnode->removed = false;\n    tnode->callback = callback;\n    tnode->round = round;\n    tnode->destructor = nullptr;\n\n    if (next_msec_ < 0 || next_msec_ > _msec) {\n        set(this, _msec);\n        next_msec_ = _msec;\n    }\n\n    tnode->heap_node = heap.push(tnode->exec_msec, tnode);\n    if (sw_unlikely(tnode->heap_node == nullptr)) {\n        release_node(tnode);\n        return nullptr;\n    }\n    map.emplace(tnode->id, tnode);\n    swoole_trace_log(SW_TRACE_TIMER,\n                     \"id=%ld, exec_msec=%\" PRId64 \", msec=%ld, round=%\" PRIu64 \", exist=%lu\",\n                     tnode->id,\n                     tnode->exec_msec,\n                     _msec,\n                     tnode->round,\n                     count());\n    return tnode;\n}\n\nbool Timer::remove(TimerNode *tnode) {\n    if (sw_unlikely(!tnode || tnode->removed)) {\n        return false;\n    }\n    if (sw_unlikely(_current_id > 0 && tnode->id == _current_id)) {\n        tnode->removed = true;\n        swoole_trace_log(SW_TRACE_TIMER,\n                         \"set-remove: id=%ld, exec_msec=%\" PRId64 \", round=%\" PRIu64 \", exist=%lu\",\n                         tnode->id,\n                         tnode->exec_msec,\n                         tnode->round,\n                         count());\n        return true;\n    }\n    if (sw_unlikely(!map.erase(tnode->id))) {\n        return false;\n    }\n    if (tnode->heap_node) {\n        heap.remove(tnode->heap_node);\n    }\n    swoole_trace_log(SW_TRACE_TIMER,\n                     \"id=%ld, exec_msec=%\" PRId64 \", round=%\" PRIu64 \", exist=%lu\",\n                     tnode->id,\n                     tnode->exec_msec,\n                     tnode->round,\n                     count());\n    release_node(tnode);\n    return true;\n}\n\nvoid Timer::select() {\n    const int64_t now_msec = get_relative_msec();\n    TimerNode *tnode = nullptr;\n    HeapNode *tmp;\n\n    swoole_trace_log(SW_TRACE_TIMER, \"select begin: now_msec=%\" PRId64 \", round=%\" PRId64, now_msec, round);\n\n    while ((tmp = heap.top())) {\n        tnode = static_cast<TimerNode *>(tmp->data);\n        if (tnode->exec_msec > now_msec || tnode->round == round) {\n            break;\n        }\n\n        _current_id = tnode->id;\n        if (!tnode->removed) {\n            swoole_trace_log(SW_TRACE_TIMER,\n                             \"execute callback [id=%ld, exec_msec=%\" PRId64 \", round=%\" PRIu64 \", exist=%lu]\",\n                             tnode->id,\n                             tnode->exec_msec,\n                             tnode->round,\n                             count() - 1);\n            tnode->callback(this, tnode);\n        }\n        _current_id = -1;\n\n        // persistent timer\n        if (tnode->interval > 0 && !tnode->removed) {\n            while (tnode->exec_msec <= now_msec) {\n                tnode->exec_msec += tnode->interval;\n            }\n            tnode->exec_count++;\n            heap.change_priority(tnode->exec_msec, tmp);\n            continue;\n        }\n\n        heap.pop();\n        map.erase(tnode->id);\n        release_node(tnode);\n        tnode = nullptr;\n    }\n\n    if (!tnode || !tmp) {\n        next_msec_ = -1;\n        set(this, -1);\n    } else {\n        next_msec_ = tnode->exec_msec - now_msec;\n        if (next_msec_ <= 0) {\n            next_msec_ = 1;\n        }\n        set(this, next_msec_);\n    }\n    round++;\n}\n};  // namespace swoole\n"
  },
  {
    "path": "src/coroutine/base.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_coroutine.h\"\n#include \"swoole_coroutine_api.h\"\n\nnamespace swoole {\n\nSW_THREAD_LOCAL Coroutine *Coroutine::current = nullptr;\nSW_THREAD_LOCAL long Coroutine::last_cid = 0;\nSW_THREAD_LOCAL long Coroutine::socket_bound_cid = 0;\nSW_THREAD_LOCAL std::unordered_map<long, Coroutine *> Coroutine::coroutines;\nSW_THREAD_LOCAL uint64_t Coroutine::peak_num = 0;\nSW_THREAD_LOCAL bool Coroutine::activated = false;\n\nSW_THREAD_LOCAL size_t Coroutine::stack_size = SW_DEFAULT_C_STACK_SIZE;\nSW_THREAD_LOCAL Coroutine::SwapCallback Coroutine::on_yield = nullptr;\nSW_THREAD_LOCAL Coroutine::SwapCallback Coroutine::on_resume = nullptr;\nSW_THREAD_LOCAL Coroutine::SwapCallback Coroutine::on_close = nullptr;\nSW_THREAD_LOCAL Coroutine::BailoutCallback Coroutine::on_bailout = nullptr;\n\n#ifdef SW_USE_THREAD_CONTEXT\nnamespace coroutine {\nvoid thread_context_init();\nvoid thread_context_clean();\n}  // namespace coroutine\n#endif\n\nvoid Coroutine::activate() {\n#ifdef SW_USE_THREAD_CONTEXT\n    coroutine::thread_context_init();\n#endif\n    activated = true;\n    on_bailout = nullptr;\n}\n\nvoid Coroutine::deactivate() {\n#ifdef SW_USE_THREAD_CONTEXT\n    coroutine::thread_context_clean();\n#endif\n    activated = false;\n    on_bailout = []() {\n        // The coroutine scheduler has been destroyed,\n        // Can not resume any coroutine\n        // Expect that never here\n        swoole_error(\"have been bailout, can not resume any coroutine\");\n    };\n}\n\n#ifdef SW_CORO_TIME\nvoid Coroutine::calc_execute_usec(Coroutine *yield_coroutine, Coroutine *resume_coroutine) {\n    long current_usec = time<seconds_type>(true);\n    if (yield_coroutine) {\n        yield_coroutine->execute_usec += current_usec - yield_coroutine->switch_usec;\n    }\n\n    if (resume_coroutine) {\n        resume_coroutine->switch_usec = current_usec;\n    }\n}\n#endif\n\nCoroutine::Coroutine(const CoroutineFunc &fn, void *private_data) : ctx(stack_size, fn, private_data) {\n    cid = ++last_cid;\n    coroutines[cid] = this;\n    if (sw_unlikely(count() > peak_num)) {\n        peak_num = count();\n    }\n    if (!activated) {\n        activate();\n    }\n}\n\nvoid Coroutine::check_end() {\n    if (ctx.is_end()) {\n        close();\n    } else if (sw_unlikely(on_bailout)) {\n        SW_ASSERT(current == nullptr);\n        on_bailout();\n    }\n}\n\nlong Coroutine::run() {\n    const long _cid = cid;\n    origin = current;\n    current = this;\n    CALC_EXECUTE_USEC(origin, nullptr);\n    state = STATE_RUNNING;\n    ctx.swap_in();\n    check_end();\n    return _cid;\n}\n\nvoid Coroutine::yield() {\n    SW_ASSERT(current == this || on_bailout != nullptr);\n    state = STATE_WAITING;\n    resume_code_ = RC_OK;\n    if (sw_likely(on_yield && task)) {\n        on_yield(task);\n    }\n    current = origin;\n\n    CALC_EXECUTE_USEC(this, current);\n    ctx.swap_out();\n}\n\nvoid Coroutine::yield(CancelFunc *cancel_fn) {\n    set_cancel_fn(cancel_fn);\n    yield();\n    set_cancel_fn(nullptr);\n}\n\nbool Coroutine::yield_ex(double timeout) {\n    TimerNode *timer = nullptr;\n    TimerCallback timer_callback = [this](Timer *timer, TimerNode *tnode) {\n        resume_code_ = RC_TIMEDOUT;\n        resume();\n    };\n\n    if (timeout > 0) {\n        timer = swoole_timer_add(timeout, false, timer_callback, nullptr);\n    }\n\n    CancelFunc cancel_fn = [](Coroutine *co) {\n        co->resume();\n        return true;\n    };\n\n    yield(&cancel_fn);\n\n    if (is_timedout()) {\n        swoole_set_last_error(SW_ERROR_CO_TIMEDOUT);\n        return false;\n    }\n    if (timer) {\n        swoole_timer_del(timer);\n    }\n    if (is_canceled()) {\n        swoole_set_last_error(SW_ERROR_CO_CANCELED);\n        return false;\n    }\n    return true;\n}\n\nvoid Coroutine::resume() {\n    SW_ASSERT(current != this);\n    if (sw_unlikely(on_bailout)) {\n        return;\n    }\n    state = STATE_RUNNING;\n    if (sw_likely(on_resume && task)) {\n        on_resume(task);\n    }\n    origin = current;\n    current = this;\n\n    CALC_EXECUTE_USEC(origin, this);\n    ctx.swap_in();\n    check_end();\n}\n\nbool Coroutine::cancel() {\n    if (!cancel_fn_) {\n        swoole_set_last_error(SW_ERROR_CO_CANNOT_CANCEL);\n        return false;\n    }\n    auto fn = *cancel_fn_;\n    set_cancel_fn(nullptr);\n    resume_code_ = RC_CANCELED;\n    return fn(this);\n}\n\nvoid Coroutine::close() {\n    SW_ASSERT(current == this);\n    state = STATE_END;\n    if (on_close && task) {\n        on_close(task);\n    }\n#if !defined(SW_USE_THREAD_CONTEXT) && defined(SW_CONTEXT_DETECT_STACK_USAGE)\n    swoole_trace_log(\n        SW_TRACE_CONTEXT, \"coroutine#%ld stack memory use less than %ld bytes\", get_cid(), ctx.get_stack_usage());\n#endif\n    current = origin;\n    coroutines.erase(cid);\n    delete this;\n}\n\nvoid Coroutine::print_list() {\n    for (auto &coroutine : coroutines) {\n        const char *state;\n        switch (coroutine.second->state) {\n        case STATE_INIT:\n            state = \"[INIT]\";\n            break;\n        case STATE_WAITING:\n            state = \"[WAITING]\";\n            break;\n        case STATE_RUNNING:\n            state = \"[RUNNING]\";\n            break;\n        case STATE_END:\n            state = \"[END]\";\n            break;\n        default:\n            abort();\n            return;\n        }\n        sw_printf(\"Coroutine\\t%ld\\t%s\\n\", coroutine.first, state);\n    }\n}\n\nvoid Coroutine::print_socket_bound_error(int sock_fd, const char *event_str, long bound_cid) {\n    socket_bound_cid = bound_cid;\n    swoole_fatal_error(SW_ERROR_CO_HAS_BEEN_BOUND,\n                       \"Socket#%d has already been bound to another coroutine#%ld, \"\n                       \"%s of the same socket in coroutine#%ld at the same time is not allowed\",\n                       sock_fd,\n                       socket_bound_cid,\n                       event_str,\n                       get_current_cid());\n}\n\nvoid Coroutine::set_on_yield(const SwapCallback func) {\n    on_yield = func;\n}\n\nvoid Coroutine::set_on_resume(const SwapCallback func) {\n    on_resume = func;\n}\n\nvoid Coroutine::set_on_close(const SwapCallback func) {\n    on_close = func;\n}\n\nvoid Coroutine::bailout(const BailoutCallback &func) {\n    Coroutine *co = current;\n    if (!co) {\n        // marks that it can no longer resume any coroutine\n        static BailoutCallback fn = []() {\n            // expect that never here\n            swoole_error(\"have been bailout, can not resume any coroutine\");\n        };\n        on_bailout = fn;\n        return;\n    }\n    if (!func) {\n        swoole_error(\"bailout without callback function\");\n    }\n    on_bailout = func;\n    // find the coroutine which is closest to the main\n    while (co->origin) {\n        co = co->origin;\n    }\n    // it will jump to main context directly (it also breaks contexts)\n    co->yield();\n    // expect that never here\n    exit(SW_CORO_BAILOUT_EXIT_CODE);\n}\n\nnamespace coroutine {\nbool run(const CoroutineFunc &fn, void *arg) {\n    if (swoole_event_init(SW_EVENTLOOP_WAIT_EXIT) < 0) {\n        return false;\n    }\n    Coroutine::activate();\n    long cid = Coroutine::create(fn, arg);\n    swoole_event_wait();\n    Coroutine::deactivate();\n    return cid > 0;\n}\n}  // namespace coroutine\n}  // namespace swoole\n\nuint8_t swoole_coroutine_is_in() {\n    return !!swoole::Coroutine::get_current();\n}\n\nlong swoole_coroutine_create(void (*routine)(void *), void *arg) {\n    if (sw_likely(swoole_event_is_available())) {\n        return swoole::Coroutine::create(routine, arg);\n    } else {\n        if (swoole_event_init(SW_EVENTLOOP_WAIT_EXIT) < 0) {\n            return -1;\n        }\n        swoole::Coroutine::activate();\n        long cid = swoole::Coroutine::create(routine, arg);\n        swoole_event_wait();\n        swoole::Coroutine::deactivate();\n        return cid;\n    }\n}\n\nlong swoole_coroutine_get_id() {\n    return swoole::Coroutine::get_current_cid();\n}\n\nswoole::Coroutine *swoole_coroutine_get(long cid) {\n    auto i = swoole::Coroutine::coroutines.find(cid);\n    if (i == swoole::Coroutine::coroutines.end()) {\n        return nullptr;\n    } else {\n        return i->second;\n    }\n}\n\nsize_t swoole_coroutine_count() {\n    return swoole::Coroutine::coroutines.size();\n}\n\n/**\n * for gdb\n */\nstatic std::unordered_map<long, swoole::Coroutine *>::iterator gdb_iterator_;\n\nvoid swoole_coroutine_iterator_reset() {\n    gdb_iterator_ = swoole::Coroutine::coroutines.begin();\n}\n\nswoole::Coroutine *swoole_coroutine_iterator_each() {\n    if (gdb_iterator_ == swoole::Coroutine::coroutines.end()) {\n        return nullptr;\n    }\n    swoole::Coroutine *co = gdb_iterator_->second;\n    ++gdb_iterator_;\n    return co;\n}\n"
  },
  {
    "path": "src/coroutine/channel.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_coroutine_channel.h\"\n\nnamespace swoole {\nnamespace coroutine {\n\nvoid Channel::timer_callback(Timer *timer, TimerNode *tnode) {\n    auto *msg = static_cast<TimeoutMessage *>(tnode->data);\n    msg->error = true;\n    msg->timer = nullptr;\n    if (msg->type == CONSUMER) {\n        msg->chan->consumer_remove(msg->co);\n    } else {\n        msg->chan->producer_remove(msg->co);\n    }\n    msg->co->resume();\n}\n\nvoid Channel::yield(Opcode type) {\n    Coroutine *co = Coroutine::get_current_safe();\n    if (type == PRODUCER) {\n        producer_queue.push_back(co);\n        swoole_trace_log(SW_TRACE_CHANNEL, \"producer cid=%ld\", co->get_cid());\n    } else {\n        consumer_queue.push_back(co);\n        swoole_trace_log(SW_TRACE_CHANNEL, \"consumer cid=%ld\", co->get_cid());\n    }\n    Coroutine::CancelFunc cancel_fn = [this, type](Coroutine *co) {\n        if (type == CONSUMER) {\n            consumer_remove(co);\n        } else {\n            producer_remove(co);\n        }\n        co->resume();\n        return true;\n    };\n    co->yield(&cancel_fn);\n}\n\nvoid *Channel::pop(double timeout) {\n    Coroutine *current_co = Coroutine::get_current_safe();\n    if (closed && is_empty()) {\n        error_ = ERROR_CLOSED;\n        return nullptr;\n    }\n    if (is_empty() || !consumer_queue.empty()) {\n        TimeoutMessage msg;\n        msg.error = false;\n        msg.timer = nullptr;\n        if (timeout > 0) {\n            msg.chan = this;\n            msg.type = CONSUMER;\n            msg.co = current_co;\n            msg.timer = swoole_timer_add(timeout, false, timer_callback, &msg);\n        }\n\n        yield(CONSUMER);\n\n        if (msg.timer) {\n            swoole_timer_del(msg.timer);\n        }\n        if (current_co->is_canceled()) {\n            error_ = ERROR_CANCELED;\n            return nullptr;\n        }\n        if (msg.error) {\n            error_ = ERROR_TIMEOUT;\n            return nullptr;\n        }\n        if (closed && is_empty()) {\n            error_ = ERROR_CLOSED;\n            return nullptr;\n        }\n    }\n    /**\n     * pop data\n     */\n    void *data = data_queue.front();\n    data_queue.pop();\n    /**\n     * notify producer\n     */\n    if (!producer_queue.empty()) {\n        Coroutine *co = pop_coroutine(PRODUCER);\n        co->resume();\n    }\n    return data;\n}\n\nbool Channel::push(void *data, double timeout) {\n    Coroutine *current_co = Coroutine::get_current_safe();\n    if (closed) {\n        error_ = ERROR_CLOSED;\n        return false;\n    }\n    if (is_full() || !producer_queue.empty()) {\n        TimeoutMessage msg;\n        msg.error = false;\n        msg.timer = nullptr;\n        if (timeout > 0) {\n            msg.chan = this;\n            msg.type = PRODUCER;\n            msg.co = current_co;\n            msg.timer = swoole_timer_add(timeout, false, timer_callback, &msg);\n        }\n\n        yield(PRODUCER);\n\n        if (msg.timer) {\n            swoole_timer_del(msg.timer);\n        }\n        if (current_co->is_canceled()) {\n            error_ = ERROR_CANCELED;\n            return false;\n        }\n        if (msg.error) {\n            error_ = ERROR_TIMEOUT;\n            return false;\n        }\n        if (closed) {\n            error_ = ERROR_CLOSED;\n            return false;\n        }\n    }\n    /**\n     * push data\n     */\n    data_queue.push(data);\n    swoole_trace_log(SW_TRACE_CHANNEL, \"push data to channel, count=%ld\", length());\n    /**\n     * notify consumer\n     */\n    if (!consumer_queue.empty()) {\n        Coroutine *co = pop_coroutine(CONSUMER);\n        co->resume();\n    }\n    return true;\n}\n\nbool Channel::close() {\n    if (closed) {\n        return false;\n    }\n    swoole_trace_log(SW_TRACE_CHANNEL, \"channel closed\");\n    closed = true;\n    while (!producer_queue.empty()) {\n        Coroutine *co = pop_coroutine(PRODUCER);\n        co->resume();\n    }\n    while (!consumer_queue.empty()) {\n        Coroutine *co = pop_coroutine(CONSUMER);\n        co->resume();\n    }\n    return true;\n}\n\n}  // namespace coroutine\n}  // namespace swoole\n"
  },
  {
    "path": "src/coroutine/context.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_coroutine_context.h\"\n\n#ifdef SW_CONTEXT_PROTECT_STACK_PAGE\n#include <sys/mman.h>\n#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)\n#define MAP_ANONYMOUS MAP_ANON\n#endif\n#endif\n\n#ifndef SW_USE_THREAD_CONTEXT\n\n#define MAGIC_STRING \"swoole_coroutine#5652a7fb2b38be\"\n#define START_OFFSET (64 * 1024)\n\nnamespace swoole {\nnamespace coroutine {\n\nContext::Context(size_t stack_size, CoroutineFunc fn, void *private_data)\n    : fn_(std::move(fn)), stack_size_(stack_size), private_data_(private_data) {\n    end_ = false;\n\n#ifdef SW_CONTEXT_PROTECT_STACK_PAGE\n    int mapflags = MAP_PRIVATE | MAP_ANONYMOUS;\n#ifdef __OpenBSD__\n    // no-op for Linux and NetBSD, not to enable on FreeBSD as the semantic differs.\n    // However, necessary on OpenBSD.\n    mapflags |= MAP_STACK;\n#endif\n    stack_ = (char *) ::mmap(0, stack_size_, PROT_READ | PROT_WRITE, mapflags, -1, 0);\n#else\n    stack_ = (char *) sw_malloc(stack_size_);\n#endif\n    if (!stack_) {\n        swoole_fatal_error(SW_ERROR_MALLOC_FAIL, \"failed to malloc stack memory.\");\n        exit(254);\n    }\n    swoole_trace_log(SW_TRACE_COROUTINE, \"alloc stack: size=%u, ptr=%p\", stack_size_, stack_);\n\n    void *sp = (void *) ((char *) stack_ + stack_size_);\n#ifdef USE_VALGRIND\n    valgrind_stack_id = VALGRIND_STACK_REGISTER(sp, stack_);\n#endif\n\n#ifdef USE_UCONTEXT\n    if (-1 == getcontext(&ctx_)) {\n        swoole_throw_error(SW_ERROR_CO_GETCONTEXT_FAILED);\n        sw_free(stack_);\n        return;\n    }\n    ctx_.uc_stack.ss_sp = stack_;\n    ctx_.uc_stack.ss_size = stack_size;\n    ctx_.uc_link = nullptr;\n    makecontext(&ctx_, (void (*)(void)) & context_func, 1, this);\n#else\n    ctx_ = swoole_make_fcontext(sp, stack_size_, (void (*)(transfer_t)) & context_func);\n    swap_ctx_ = nullptr;\n#endif\n\n#ifdef SW_CONTEXT_DETECT_STACK_USAGE\n    size_t offset = START_OFFSET;\n    while (offset <= stack_size) {\n        memcpy((char *) sp - offset + (sizeof(MAGIC_STRING) - 1), SW_STRL(MAGIC_STRING));\n        offset *= 2;\n    }\n#endif\n\n#ifdef SW_CONTEXT_PROTECT_STACK_PAGE\n    mprotect(stack_, SwooleG.pagesize, PROT_NONE);\n#endif\n}\n\nContext::~Context() {\n    if (stack_) {\n        swoole_trace_log(SW_TRACE_COROUTINE, \"free stack: ptr=%p\", stack_);\n#ifdef USE_VALGRIND\n        VALGRIND_STACK_DEREGISTER(valgrind_stack_id);\n#endif\n\n#ifdef SW_CONTEXT_PROTECT_STACK_PAGE\n        ::munmap(stack_, stack_size_);\n#else\n        sw_free(stack_);\n#endif\n        stack_ = nullptr;\n    }\n}\n\n#ifdef SW_CONTEXT_DETECT_STACK_USAGE\nssize_t Context::get_stack_usage() {\n    size_t offset = START_OFFSET;\n    size_t retval = START_OFFSET;\n\n    void *sp = (void *) ((char *) stack_ + stack_size_);\n\n    while (offset < stack_size_) {\n        if (memcmp((char *) sp - offset + (sizeof(MAGIC_STRING) - 1), SW_STRL(MAGIC_STRING)) != 0) {\n            retval = offset * 2;\n        }\n        offset *= 2;\n    }\n\n    return retval;\n}\n#endif\n\nbool Context::swap_in() {\n#ifdef USE_UCONTEXT\n    return 0 == swapcontext(&swap_ctx_, &ctx_);\n#else\n    coroutine_transfer_t transfer_data = swoole_jump_fcontext(ctx_, (void *) this);\n    ctx_ = transfer_data.fctx;\n    return true;\n#endif\n}\n\nbool Context::swap_out() {\n#ifdef USE_UCONTEXT\n    return 0 == swapcontext(&ctx_, &swap_ctx_);\n#else\n    coroutine_transfer_t transfer_data = swoole_jump_fcontext(swap_ctx_, (void *) this);\n    swap_ctx_ = transfer_data.fctx;\n    return true;\n#endif\n}\n\nvoid Context::context_func(coroutine_transfer_t arg) {\n#if defined(USE_UCONTEXT) || defined(SW_USE_THREAD_CONTEXT)\n    auto *_this = (Context *) arg;\n#else\n    auto *_this = (Context *) arg.data;\n    _this->swap_ctx_ = arg.fctx;\n#endif\n    _this->fn_(_this->private_data_);\n    _this->end_ = true;\n    _this->swap_out();\n}\n}  // namespace coroutine\n}  // namespace swoole\n#endif\n"
  },
  {
    "path": "src/coroutine/file.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: NathanFreeman  <mariasocute@163.com>                         |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_file.h\"\n#include \"swoole_coroutine_api.h\"\n\nnamespace swoole {\nAsyncFile::AsyncFile(const std::string &path, int flags, int mode) {\n    open(path, flags, mode);\n}\n\nAsyncFile::~AsyncFile() {\n    close();\n}\n\nbool AsyncFile::open(const std::string &path, int flags, mode_t mode) {\n    close();\n\n    flags_ = flags;\n    mode_ = mode;\n    path_ = path;\n    fd = swoole_coroutine_open(path.c_str(), flags, mode);\n    return fd > 0;\n}\n\nbool AsyncFile::close() const {\n    if (sw_unlikely(fd == -1)) {\n        return false;\n    }\n\n    return swoole_coroutine_close(fd) == 0;\n}\n\nssize_t AsyncFile::read(void *buf, size_t count) const {\n    return swoole_coroutine_read(fd, buf, count);\n}\n\nssize_t AsyncFile::write(const void *buf, size_t count) const {\n    return swoole_coroutine_write(fd, buf, count);\n}\n\nbool AsyncFile::sync() const {\n    return swoole_coroutine_fsync(fd) == 0;\n}\n\nbool AsyncFile::truncate(off_t length) const {\n    return swoole_coroutine_ftruncate(fd, length) == 0;\n}\n\nbool AsyncFile::stat(FileStatus *statbuf) const {\n    return swoole_coroutine_fstat(fd, statbuf) == 0;\n}\n\noff_t AsyncFile::get_offset() const {\n    return swoole_coroutine_lseek(fd, 0, SEEK_CUR);\n}\n\noff_t AsyncFile::set_offset(off_t offset) const {\n    return swoole_coroutine_lseek(fd, offset, SEEK_SET);\n}\n}  // namespace swoole\n"
  },
  {
    "path": "src/coroutine/file_lock.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include <sys/file.h>\n\n#include \"swoole_coroutine.h\"\n#include \"swoole_coroutine_api.h\"\n\nusing swoole::Coroutine;\nusing swoole::coroutine::wait_for;\n\nstatic inline int do_lock(int fd, int operation) {\n    int retval = 0;\n    auto success = wait_for([&retval, operation, fd]() {\n        auto rv = flock(fd, operation | LOCK_NB);\n        if (rv == 0) {\n            retval = 0;\n        } else if (rv == -1 && errno == EWOULDBLOCK) {\n            return false;\n        } else {\n            retval = -1;\n        }\n        return true;\n    });\n    return success ? retval : -1;\n}\n\nstatic inline int lock_ex(int fd) {\n    return do_lock(fd, LOCK_EX);\n}\n\nstatic inline int lock_sh(int fd) {\n    return do_lock(fd, LOCK_SH);\n}\n\nstatic inline int lock_release(int fd) {\n    return flock(fd, LOCK_UN);\n}\n\nint swoole_coroutine_flock(int fd, int operation) {\n    Coroutine *co = Coroutine::get_current();\n    if (sw_unlikely(SwooleTG.reactor == nullptr || !co)) {\n        return ::flock(fd, operation);\n    }\n    if (operation & LOCK_NB) {\n        return ::flock(fd, operation);\n    }\n    switch (operation) {\n    case LOCK_EX:\n        return lock_ex(fd);\n    case LOCK_SH:\n        return lock_sh(fd);\n    case LOCK_UN:\n        return lock_release(fd);\n    default:\n        break;\n    }\n    errno = EINVAL;\n    swoole_set_last_error(EINVAL);\n    return -1;\n}\n"
  },
  {
    "path": "src/coroutine/hook.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include <sys/file.h>\n#include <sys/stat.h>\n#include <sys/statvfs.h>\n#include <netdb.h>\n#include <poll.h>\n#include <dirent.h>\n\n#include <mutex>\n#include <unordered_map>\n\n#include \"swoole_coroutine_system.h\"\n#include \"swoole_socket_impl.h\"\n#include \"swoole_iouring.h\"\n\nusing swoole::AsyncEvent;\nusing swoole::Coroutine;\nusing swoole::EventType;\nusing swoole::translate_events_from_poll;\nusing swoole::translate_events_to_poll;\nusing swoole::async::dispatch;\nusing swoole::coroutine::async;\nusing swoole::coroutine::PollSocket;\nusing swoole::coroutine::System;\n\n#ifdef SW_USE_IOURING\nusing swoole::Iouring;\n#else\n#define SW_USE_ASYNC 1\n#endif\n\nstatic std::unordered_map<int, std::shared_ptr<SocketImpl>> socket_map;\nstatic std::mutex socket_map_lock;\n\n#if defined(__APPLE__) || defined(__MACH__)\nstatic int fdatasync(int fd) {\n    return fcntl(fd, F_FULLFSYNC);\n}\n#endif\n\nstatic sw_inline bool is_no_coro() {\n    return SwooleTG.reactor == nullptr || !Coroutine::get_current();\n}\n\nstatic sw_inline std::shared_ptr<SocketImpl> get_socket(int sockfd) {\n    std::unique_lock<std::mutex> _lock(socket_map_lock);\n    auto socket_iterator = socket_map.find(sockfd);\n    if (socket_iterator == socket_map.end()) {\n        errno = ENOTSOCK;\n        return nullptr;\n    }\n    return socket_iterator->second;\n}\n\nstatic sw_inline std::shared_ptr<SocketImpl> get_socket_ex(int sockfd) {\n    if (sw_unlikely(is_no_coro())) {\n        errno = EWOULDBLOCK;\n        return nullptr;\n    }\n    return get_socket(sockfd);\n}\n\nstd::shared_ptr<SocketImpl> swoole_coroutine_get_socket_object(int sockfd) {\n    return get_socket(sockfd);\n}\n\nstd::shared_ptr<SocketImpl> swoole_coroutine_get_socket_object_ex(int sockfd) {\n    return get_socket_ex(sockfd);\n}\n\nSW_EXTERN_C_BEGIN\nint swoole_coroutine_socket(int domain, int type, int protocol) {\n    if (sw_unlikely(is_no_coro())) {\n        return ::socket(domain, type, protocol);\n    }\n    auto socket = std::make_shared<SocketImpl>(domain, type, protocol);\n    int fd = socket->get_fd();\n    if (sw_unlikely(fd < 0)) {\n        return -1;\n    } else {\n        std::unique_lock<std::mutex> _lock(socket_map_lock);\n        socket_map[fd] = socket;\n    }\n    return fd;\n}\n\nssize_t swoole_coroutine_send(int sockfd, const void *buf, size_t len, int flags) {\n    auto socket = get_socket_ex(sockfd);\n    if (sw_unlikely(socket == nullptr)) {\n        return ::send(sockfd, buf, len, flags);\n    }\n    return socket->send(buf, len);\n}\n\nssize_t swoole_coroutine_sendmsg(int sockfd, const struct msghdr *msg, int flags) {\n    auto socket = get_socket_ex(sockfd);\n    if (sw_unlikely(socket == nullptr)) {\n        return ::sendmsg(sockfd, msg, flags);\n    }\n    return socket->sendmsg(msg, flags);\n}\n\nssize_t swoole_coroutine_recvmsg(int sockfd, struct msghdr *msg, int flags) {\n    auto socket = get_socket_ex(sockfd);\n    if (sw_unlikely(socket == nullptr)) {\n        return ::recvmsg(sockfd, msg, flags);\n    }\n    return socket->recvmsg(msg, flags);\n}\n\nssize_t swoole_coroutine_recv(int sockfd, void *buf, size_t len, int flags) {\n    auto socket = get_socket_ex(sockfd);\n    if (sw_unlikely(socket == nullptr)) {\n        return ::recv(sockfd, buf, len, flags);\n    }\n    if (flags & MSG_PEEK) {\n        return socket->peek(buf, len);\n    } else {\n        return socket->recv(buf, len);\n    }\n}\n\nint swoole_coroutine_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {\n    auto socket = get_socket_ex(sockfd);\n    if (sw_unlikely(socket == nullptr)) {\n        return ::connect(sockfd, addr, addrlen);\n    }\n    return socket->connect(addr, addrlen) ? 0 : -1;\n}\n\nint swoole_coroutine_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {\n    auto socket = get_socket_ex(sockfd);\n    if (sw_unlikely(socket == nullptr)) {\n        return ::accept(sockfd, addr, addrlen);\n    }\n\n    auto conn = socket->accept(0);\n    if (!conn) {\n        return -1;\n    }\n\n    *addrlen = conn->get_socket()->info.len;\n    memcpy(addr, &conn->get_socket()->info.addr.ss, *addrlen);\n\n    std::unique_lock<std::mutex> _lock(socket_map_lock);\n    socket_map.emplace(conn->get_fd(), std::shared_ptr<SocketImpl>(conn));\n    return conn->get_fd();\n}\n\nint swoole_coroutine_poll_fake(struct pollfd *fds, nfds_t nfds, int timeout) {\n    if (nfds != 1) {\n        swoole_set_last_error(SW_ERROR_INVALID_PARAMS);\n        swoole_warning(\"fake poll() implementation, only supports one fd\");\n        return -1;\n    }\n    auto socket = get_socket_ex(fds[0].fd);\n    if (sw_unlikely(timeout == 0 || socket == nullptr)) {\n        return poll(fds, nfds, timeout);\n    }\n    socket->set_timeout((double) timeout / 1000);\n    if (fds[0].events & POLLIN) {\n        fds[0].revents |= POLLIN;\n    }\n    if (fds[0].events & POLLOUT) {\n        fds[0].revents |= POLLOUT;\n    }\n    return 1;\n}\n\nint swoole_coroutine_poll(struct pollfd *fds, nfds_t nfds, int timeout) {\n    if (sw_unlikely(is_no_coro() || timeout == 0)) {\n        return poll(fds, nfds, timeout);\n    }\n\n#ifdef SW_USE_IOURING\n    if (nfds == 1) {\n        return Iouring::poll(fds, nfds, timeout);\n    }\n#endif\n\n    std::unordered_map<int, PollSocket> _fds;\n    for (nfds_t i = 0; i < nfds; i++) {\n        _fds.emplace(fds[i].fd, PollSocket(translate_events_from_poll(fds[i].events), &fds[i]));\n    }\n\n    if (!System::socket_poll(_fds, (double) timeout / 1000)) {\n        return -1;\n    }\n\n    int retval = 0;\n    for (auto &i : _fds) {\n        int revents = i.second.revents;\n        auto *_fd = static_cast<struct pollfd *>(i.second.ptr);\n        _fd->revents = translate_events_to_poll(revents);\n        if (revents > 0) {\n            retval++;\n        }\n    }\n\n    return retval;\n}\n\nint swoole_coroutine_socket_create(int fd) {\n    if (sw_unlikely(is_no_coro())) {\n        return -1;\n    }\n    auto socket = std::make_shared<SocketImpl>(fd, SW_SOCK_RAW);\n    int _fd = socket->get_fd();\n    if (sw_unlikely(_fd < 0)) {\n        return -1;\n    }\n    socket->get_socket()->set_nonblock();\n    std::unique_lock<std::mutex> _lock(socket_map_lock);\n    socket_map[fd] = socket;\n    return 0;\n}\n\nint swoole_coroutine_socket_unwrap(int fd) {\n    if (sw_unlikely(is_no_coro())) {\n        return -1;\n    }\n    auto socket = get_socket(fd);\n    if (socket == nullptr) {\n        return -1;\n    }\n    std::unique_lock<std::mutex> _lock(socket_map_lock);\n    socket->move_fd();\n    socket_map.erase(fd);\n    return 0;\n}\n\nuint8_t swoole_coroutine_socket_exists(int fd) {\n    return socket_map.find(fd) != socket_map.end();\n}\n\nFILE *swoole_coroutine_fopen(const char *pathname, const char *mode) {\n    if (sw_unlikely(is_no_coro())) {\n        return fopen(pathname, mode);\n    }\n\n    FILE *retval = nullptr;\n    async([&]() { retval = fopen(pathname, mode); });\n    return retval;\n}\n\nFILE *swoole_coroutine_fdopen(int fd, const char *mode) {\n    if (sw_unlikely(is_no_coro())) {\n        return fdopen(fd, mode);\n    }\n\n    FILE *retval = nullptr;\n    async([&]() { retval = fdopen(fd, mode); });\n    return retval;\n}\n\nFILE *swoole_coroutine_freopen(const char *pathname, const char *mode, FILE *stream) {\n    if (sw_unlikely(is_no_coro())) {\n        return freopen(pathname, mode, stream);\n    }\n\n    FILE *retval = nullptr;\n    async([&]() { retval = freopen(pathname, mode, stream); });\n    return retval;\n}\n\nsize_t swoole_coroutine_fread(void *ptr, size_t size, size_t nmemb, FILE *stream) {\n    if (sw_unlikely(is_no_coro())) {\n        return fread(ptr, size, nmemb, stream);\n    }\n\n    size_t retval = 0;\n    async([&]() { retval = fread(ptr, size, nmemb, stream); });\n    return retval;\n}\n\nsize_t swoole_coroutine_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) {\n    if (sw_unlikely(is_no_coro())) {\n        return fwrite(ptr, size, nmemb, stream);\n    }\n\n    size_t retval = 0;\n    async([&]() { retval = fwrite(ptr, size, nmemb, stream); });\n    return retval;\n}\n\nchar *swoole_coroutine_fgets(char *s, int size, FILE *stream) {\n    if (sw_unlikely(is_no_coro())) {\n        return fgets(s, size, stream);\n    }\n\n    char *retval = nullptr;\n    async([&]() { retval = fgets(s, size, stream); });\n    return retval;\n}\n\nint swoole_coroutine_fputs(const char *s, FILE *stream) {\n    if (sw_unlikely(is_no_coro())) {\n        return fputs(s, stream);\n    }\n\n    int retval = -1;\n    async([&]() { retval = fputs(s, stream); });\n    return retval;\n}\n\nint swoole_coroutine_feof(FILE *stream) {\n    if (sw_unlikely(is_no_coro())) {\n        return feof(stream);\n    }\n\n    int retval = -1;\n    async([&]() { retval = feof(stream); });\n    return retval;\n}\n\nint swoole_coroutine_fflush(FILE *stream) {\n    if (sw_unlikely(is_no_coro())) {\n        return fflush(stream);\n    }\n\n    int retval = -1;\n    async([&]() { retval = fflush(stream); });\n    return retval;\n}\n\nint swoole_coroutine_fclose(FILE *stream) {\n    if (sw_unlikely(is_no_coro())) {\n        return fclose(stream);\n    }\n\n    int retval = -1;\n    async([&]() { retval = fclose(stream); });\n    return retval;\n}\n\nDIR *swoole_coroutine_opendir(const char *name) {\n    if (sw_unlikely(is_no_coro())) {\n        return opendir(name);\n    }\n\n    DIR *retval = nullptr;\n    async([&]() { retval = opendir(name); });\n    return retval;\n}\n\nstruct dirent *swoole_coroutine_readdir(DIR *dirp) {\n    if (sw_unlikely(is_no_coro())) {\n        return readdir(dirp);\n    }\n\n    struct dirent *retval;\n    async([&retval, dirp]() { retval = readdir(dirp); });\n    return retval;\n}\n\nint swoole_coroutine_closedir(DIR *dirp) {\n    if (sw_unlikely(is_no_coro())) {\n        return closedir(dirp);\n    }\n\n    int retval = -1;\n    async([&]() { retval = closedir(dirp); });\n    return retval;\n}\n\nvoid swoole_coroutine_sleep(int sec) {\n    System::sleep((double) sec);\n}\n\nvoid swoole_coroutine_usleep(int usec) {\n    System::sleep((double) usec / 1024 / 1024);\n}\n\nint swoole_coroutine_socket_set_timeout(int sockfd, int which, double timeout) {\n    auto socket = get_socket_ex(sockfd);\n    if (sw_unlikely(socket == nullptr)) {\n        errno = EINVAL;\n        return -1;\n    }\n    if (which == SO_RCVTIMEO) {\n        socket->set_timeout(timeout, SW_TIMEOUT_READ);\n        return 0;\n    } else if (which == SO_SNDTIMEO) {\n        socket->set_timeout(timeout, SW_TIMEOUT_WRITE);\n        return 0;\n    } else {\n        errno = EINVAL;\n        return -1;\n    }\n}\n\nint swoole_coroutine_socket_set_connect_timeout(int sockfd, double timeout) {\n    auto socket = get_socket_ex(sockfd);\n    if (sw_unlikely(socket == nullptr)) {\n        errno = EINVAL;\n        return -1;\n    }\n    socket->set_timeout(timeout, SW_TIMEOUT_DNS | SW_TIMEOUT_CONNECT);\n    return 0;\n}\n\nint swoole_coroutine_socket_wait_event(int sockfd, int event, double timeout) {\n    auto socket = get_socket_ex(sockfd);\n    if (sw_unlikely(socket == nullptr)) {\n        pollfd poll_ev{};\n        poll_ev.fd = sockfd;\n        poll_ev.events = translate_events_to_poll(event);\n        return poll(&poll_ev, 1, (int) (timeout * 1000)) == 1 ? SW_OK : SW_ERR;\n    }\n    double ori_timeout = socket->get_timeout(event == SW_EVENT_READ ? SW_TIMEOUT_READ : SW_TIMEOUT_WRITE);\n    socket->set_timeout(timeout);\n    bool retval = socket->poll(static_cast<EventType>(event));\n    socket->set_timeout(ori_timeout);\n    return retval ? SW_OK : SW_ERR;\n}\n\nint swoole_coroutine_getaddrinfo(const char *name, const char *service, const addrinfo *req, addrinfo **pai) {\n    int retval = -1;\n    async([&]() { retval = getaddrinfo(name, service, req, pai); });\n    return retval;\n}\n\nhostent *swoole_coroutine_gethostbyname(const char *name) {\n    hostent *retval = nullptr;\n    int _tmp_h_errno = 0;\n    async([&]() {\n        retval = gethostbyname(name);\n        _tmp_h_errno = h_errno;\n    });\n    h_errno = _tmp_h_errno;\n    return retval;\n}\n\nint swoole_coroutine_open(const char *pathname, int flags, mode_t mode) {\n    if (sw_unlikely(is_no_coro())) {\n        return open(pathname, flags, mode);\n    }\n\n#ifdef SW_USE_ASYNC\n    int ret = -1;\n    async([&]() { ret = open(pathname, flags, mode); });\n    return ret;\n#else\n    return Iouring::open(pathname, flags, mode);\n#endif\n}\n\nint swoole_coroutine_close(int sockfd) {\n    if (sw_unlikely(is_no_coro())) {\n        return close(sockfd);\n    }\n\n    auto socket = get_socket(sockfd);\n    if (socket != nullptr) {\n        if (socket->close()) {\n            std::unique_lock<std::mutex> _lock(socket_map_lock);\n            socket_map.erase(sockfd);\n            return 0;\n        }\n        return -1;\n    }\n\n#ifdef SW_USE_ASYNC\n    int ret = -1;\n    async([&]() { ret = close(sockfd); });\n    return ret;\n#else\n    return Iouring::close(sockfd);\n#endif\n}\n\nssize_t swoole_coroutine_read(int sockfd, void *buf, size_t count) {\n    if (sw_unlikely(is_no_coro())) {\n        return read(sockfd, buf, count);\n    }\n\n    auto socket = get_socket(sockfd);\n    if (socket != nullptr) {\n        return socket->read(buf, count);\n    }\n\n#ifdef SW_USE_ASYNC\n    ssize_t ret = -1;\n    NetSocket sock = {};\n    sock.fd = sockfd;\n    sock.nonblock = 1;\n    sock.read_timeout = -1;\n    async([&]() { ret = sock.read_sync(buf, count); });\n    return ret;\n#else\n    return Iouring::read(sockfd, buf, count);\n#endif\n}\n\nssize_t swoole_coroutine_write(int sockfd, const void *buf, size_t count) {\n    if (sw_unlikely(is_no_coro())) {\n        return write(sockfd, buf, count);\n    }\n\n    auto socket = get_socket(sockfd);\n    if (socket != nullptr) {\n        return socket->write(buf, count);\n    }\n\n#ifdef SW_USE_ASYNC\n    ssize_t ret = -1;\n    NetSocket sock = {};\n    sock.fd = sockfd;\n    sock.nonblock = 1;\n    sock.write_timeout = -1;\n    async([&]() { ret = sock.write_sync(buf, count); });\n    return ret;\n#else\n    return Iouring::write(sockfd, buf, count);\n#endif\n}\n\nint swoole_coroutine_fstat(int fd, struct stat *statbuf) {\n    if (sw_unlikely(is_no_coro())) {\n        return fstat(fd, statbuf);\n    }\n\n#if defined(SW_USE_ASYNC) || !defined(HAVE_IOURING_STATX)\n    int ret = -1;\n    async([&]() { ret = fstat(fd, statbuf); });\n    return ret;\n#else\n    return Iouring::fstat(fd, statbuf);\n#endif\n}\n\nint swoole_coroutine_stat(const char *path, struct stat *statbuf) {\n    if (sw_unlikely(is_no_coro())) {\n        return stat(path, statbuf);\n    }\n\n#if defined(SW_USE_ASYNC) || !defined(HAVE_IOURING_STATX)\n    int ret = -1;\n    async([&]() { ret = stat(path, statbuf); });\n    return ret;\n#else\n    return Iouring::stat(path, statbuf);\n#endif\n}\n\nint swoole_coroutine_lstat(const char *path, struct stat *statbuf) {\n    if (sw_unlikely(is_no_coro())) {\n        return lstat(path, statbuf);\n    }\n\n#if defined(SW_USE_ASYNC) || !defined(HAVE_IOURING_STATX)\n    int ret = -1;\n    async([&]() { ret = lstat(path, statbuf); });\n    return ret;\n#else\n    return Iouring::stat(path, statbuf);\n#endif\n}\n\nint swoole_coroutine_unlink(const char *pathname) {\n    if (sw_unlikely(is_no_coro())) {\n        return unlink(pathname);\n    }\n\n#ifdef SW_USE_ASYNC\n    int ret = -1;\n    async([&]() { ret = unlink(pathname); });\n    return ret;\n#else\n    return Iouring::unlink(pathname);\n#endif\n}\n\nint swoole_coroutine_mkdir(const char *pathname, mode_t mode) {\n    if (sw_unlikely(is_no_coro())) {\n        return mkdir(pathname, mode);\n    }\n\n#ifdef SW_USE_ASYNC\n    int ret = -1;\n    async([&]() { ret = mkdir(pathname, mode); });\n    return ret;\n#else\n    return Iouring::mkdir(pathname, mode);\n#endif\n}\n\nint swoole_coroutine_rmdir(const char *pathname) {\n    if (sw_unlikely(is_no_coro())) {\n        return rmdir(pathname);\n    }\n\n#ifdef SW_USE_ASYNC\n    int ret = -1;\n    async([&]() { ret = rmdir(pathname); });\n    return ret;\n#else\n    return Iouring::rmdir(pathname);\n#endif\n}\n\nint swoole_coroutine_rename(const char *oldpath, const char *newpath) {\n    if (sw_unlikely(is_no_coro())) {\n        return rename(oldpath, newpath);\n    }\n\n#ifdef SW_USE_ASYNC\n    int ret = -1;\n    async([&]() { ret = rename(oldpath, newpath); });\n    return ret;\n#else\n    return Iouring::rename(oldpath, newpath);\n#endif\n}\n\nint swoole_coroutine_fsync(int fd) {\n    if (sw_unlikely(is_no_coro())) {\n        return fsync(fd);\n    }\n\n#ifdef SW_USE_ASYNC\n    int ret = -1;\n    async([&]() { ret = fsync(fd); });\n    return ret;\n#else\n    return Iouring::fsync(fd);\n#endif\n}\n\nint swoole_coroutine_fdatasync(int fd) {\n    if (sw_unlikely(is_no_coro())) {\n#ifdef HAVE_FDATASYNC\n        return fdatasync(fd);\n#else\n        return fsync(fd);\n#endif\n    }\n\n#ifdef SW_USE_ASYNC\n    int ret = -1;\n#ifdef HAVE_FDATASYNC\n    async([&]() { ret = fdatasync(fd); });\n#else\n    async([&]() { ret = fsync(fd); });\n#endif\n    return ret;\n#else\n    return Iouring::fdatasync(fd);\n#endif\n}\n\nint swoole_coroutine_ftruncate(int fd, off_t length) {\n    if (sw_unlikely(is_no_coro())) {\n        return ftruncate(fd, length);\n    }\n\n#if defined(SW_USE_ASYNC) || !defined(HAVE_IOURING_FTRUNCATE)\n    int ret = -1;\n    async([&]() { ret = ftruncate(fd, length); });\n    return ret;\n#else\n    return Iouring::ftruncate(fd, length);\n#endif\n}\n\noff_t swoole_coroutine_lseek(int fd, off_t offset, int whence) {\n    if (sw_unlikely(is_no_coro())) {\n        return lseek(fd, offset, whence);\n    }\n\n    off_t ret = -1;\n    async([&]() { ret = lseek(fd, offset, whence); });\n    return ret;\n}\n\nssize_t swoole_coroutine_readlink(const char *pathname, char *buf, size_t len) {\n    if (sw_unlikely(is_no_coro())) {\n        return readlink(pathname, buf, len);\n    }\n\n    ssize_t ret = -1;\n    async([&]() { ret = readlink(pathname, buf, len); });\n    return ret;\n}\n\nint swoole_coroutine_statvfs(const char *path, struct statvfs *buf) {\n    if (sw_unlikely(is_no_coro())) {\n        return statvfs(path, buf);\n    }\n\n    int ret = -1;\n    async([&]() { ret = statvfs(path, buf); });\n    return ret;\n}\n\nint swoole_coroutine_access(const char *pathname, int mode) {\n    if (sw_unlikely(is_no_coro())) {\n        return access(pathname, mode);\n    }\n\n    int ret = -1;\n    async([&]() { ret = access(pathname, mode); });\n    return ret;\n}\nSW_EXTERN_C_END\n"
  },
  {
    "path": "src/coroutine/iouring.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   NathanFreeman  <mariasocute@163.com>                       |\n  |           Tianfeng Han   <rango@swoole.com>                          |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_iouring.h\"\n#include \"swoole_coroutine_system.h\"\n\n#ifdef SW_USE_IOURING\n#ifdef HAVE_IOURING_FUTEX\n#ifndef FUTEX2_SIZE_U32\n#define FUTEX2_SIZE_U32 0x02\n#endif\n#include <linux/futex.h>\n#endif\n\n#if IO_URING_VERSION_MAJOR < 2 || (IO_URING_VERSION_MAJOR == 2 && IO_URING_VERSION_MINOR < 8)\n#error \"The version of liburing required must be greater than or equal to 2.8.\"\n#endif\n\n#include <cmath>\n\n#define DOUBLE_TO_TIMESPEC(seconds, ts)                                                                                \\\n    do {                                                                                                               \\\n        double __int_part;                                                                                             \\\n        double __frac_part = modf((seconds), &__int_part);                                                             \\\n        (ts)->tv_sec = (__kernel_time64_t) __int_part;                                                                 \\\n        (ts)->tv_nsec = (long long) (__frac_part * 1000000000.0);                                                      \\\n        if ((ts)->tv_nsec >= 1000000000) {                                                                             \\\n            (ts)->tv_sec += 1;                                                                                         \\\n            (ts)->tv_nsec = 0;                                                                                         \\\n        }                                                                                                              \\\n    } while (0)\n\n#define TIMEOUT_EVENT (-1)\n\nusing swoole::Coroutine;\nusing swoole::coroutine::System;\n\nnamespace swoole {\n//-------------------------------------------------------------------------------\nenum IouringEventFlag {\n    SW_URING_TIMEOUT = 1U << 0,\n};\n\nstruct IouringEvent {\n    Coroutine *coroutine;\n    io_uring_sqe data;\n    ssize_t result;\n    IouringTimeout timeout;\n    int flags;\n\n    void set_timeout(double seconds) {\n        if (seconds > 0) {\n            flags |= SW_URING_TIMEOUT;\n            DOUBLE_TO_TIMESPEC(seconds, &timeout);\n        }\n    }\n};\n\nstatic void parse_kernel_version(const char *release, int *major, int *minor) {\n    char copy[SW_STRUCT_MEMBER_SIZE(utsname, release)];\n    strcpy(copy, release);\n\n    char *token = strtok(copy, \".-\");\n    *major = token ? sw_atoi(token) : 0;\n\n    token = strtok(nullptr, \".-\");\n    *minor = token ? sw_atoi(token) : 0;\n}\n\nIouring::Iouring(Reactor *_reactor) {\n    reactor = _reactor;\n    if (SwooleG.iouring_entries > 0) {\n        uint32_t i = 6;\n        while ((1U << i) < SwooleG.iouring_entries) {\n            i++;\n        }\n        entries = 1 << i;\n    }\n\n    int ret =\n        io_uring_queue_init(entries, &ring, (SwooleG.iouring_flag == IORING_SETUP_SQPOLL ? IORING_SETUP_SQPOLL : 0));\n    if (ret < 0) {\n        errno = -ret;\n        swoole_sys_error(\"Failed to initialize io_uring instance\");\n        return;\n    }\n\n    if (SwooleG.iouring_workers > 0) {\n        uint32_t workers[2] = {SwooleG.iouring_workers, SwooleG.iouring_workers};\n        ret = io_uring_register_iowq_max_workers(&ring, workers);\n        if (ret < 0) {\n            errno = -ret;\n            swoole_sys_error(\"Failed to set the maximum of io_uring async workers\");\n            return;\n        }\n    }\n\n    int major, minor;\n    parse_kernel_version(SwooleG.uname.release, &major, &minor);\n\n#ifdef HAVE_IOURING_FUTEX\n    if (!(major >= 6 && minor >= 7)) {\n        swoole_error(\"The Iouring::futex_wait()/Iouring::futex_wakeup() requires `6.7` or higher Linux kernel\");\n    }\n#endif\n\n#ifdef HAVE_IOURING_FTRUNCATE\n    if (!(major >= 6 && minor >= 9)) {\n        swoole_error(\"The Iouring::ftruncate() requires `6.9` or higher Linux kernel\");\n    }\n#endif\n\n    ring_socket = make_socket(ring.ring_fd, SW_FD_IOURING);\n    ring_socket->object = this;\n\n    reactor->set_exit_condition(Reactor::EXIT_CONDITION_IOURING, [](Reactor *reactor, size_t &event_num) -> bool {\n        if (SwooleTG.iouring && SwooleTG.iouring->get_task_num() == 0 && SwooleTG.iouring->is_empty_waiting_tasks()) {\n            event_num--;\n        }\n        return true;\n    });\n\n    reactor->add_destroy_callback([](void *data) {\n        if (!SwooleTG.iouring) {\n            return;\n        }\n        delete SwooleTG.iouring;\n        SwooleTG.iouring = nullptr;\n    });\n\n    reactor->set_end_callback(Reactor::PRIORITY_IOURING_SUBMIT, [](Reactor *reactor) {\n        if (!SwooleTG.iouring) {\n            return;\n        }\n        SwooleTG.iouring->submit(true);\n    });\n\n    reactor->iouring_interrupt_handler = [this](Reactor *reactor) { wakeup(); };\n\n    if (reactor->add(ring_socket, SW_EVENT_READ) == SW_ERR) {\n        swoole_sys_error(\"Failed to add io_uring ring fd to the event loop\");\n    }\n}\n\nIouring::~Iouring() {\n    if (!ring_socket) {\n        return;\n    }\n\n    if (!ring_socket->removed) {\n        reactor->del(ring_socket);\n    }\n    ring_socket->move_fd();\n    ring_socket->free();\n    ring_socket = nullptr;\n\n    io_uring_queue_exit(&ring);\n}\n\nbool Iouring::ready() const {\n    return ring_socket && reactor->exists(ring_socket);\n}\n\nvoid Iouring::yield(IouringEvent *event) {\n    ++task_num;\n    Coroutine::CancelFunc cancel_fn = [event, this](Coroutine *) { return cancel(event); };\n    event->coroutine->yield(&cancel_fn);\n}\n\nvoid Iouring::resume(IouringEvent *event) {\n    --task_num;\n    event->coroutine->resume();\n}\n\nbool Iouring::wakeup() {\n    IouringEvent *waiting_task = nullptr;\n\n    while (true) {\n        auto count = io_uring_peek_batch_cqe(&ring, cqes, SW_IOURING_QUEUE_SIZE);\n        if (count == 0) {\n            break;\n        }\n\n        uint32_t ready_count = 0;\n        SW_LOOP_N(count) {\n            auto cqe = cqes[i];\n            auto event = static_cast<IouringEvent *>(io_uring_cqe_get_data(cqe));\n            // The user data for the timeout request is -1, this event should be ignored.\n            if (event == reinterpret_cast<void *>(TIMEOUT_EVENT)) {\n                swoole_trace(\n                    \"timeout, cqe.flags=%d, ceq.res=%d, error=`%s`\", cqe->flags, cqe->res, strerror(-cqe->res));\n                continue;\n            }\n\n            event->result = cqe->res;\n            ready_events[ready_count++] = event;\n\n            swoole_trace(\"opcode=%s, cqe.flags=%d, ceq.res=%d, error=`%s`\",\n                         get_opcode_name((io_uring_op) event->data.opcode),\n                         cqe->flags,\n                         cqe->res,\n                         strerror(cqe->res < 0 ? errno : 0));\n        }\n        io_uring_cq_advance(&ring, count);\n\n        /**\n         * After the harvest is completed, the operating system's unprocessed task queue is reduced.\n         * Before resuming the coroutine ready for IO, it should extract the queued SQE to the SQEs queue for processing\n         * by the operating system. This can achieve a relatively good balance. If the submission is made after resuming\n         * the coroutine, the kernel may become idle.\n         */\n        SW_LOOP_N(ready_count) {\n            if (get_sq_space_left() == 0 || is_empty_waiting_tasks()) {\n                break;\n            }\n            waiting_task = waiting_tasks.front();\n            waiting_tasks.pop();\n            dispatch(waiting_task);\n        }\n\n        SW_LOOP_N(ready_count) {\n            auto event = ready_events[i];\n            if (event->result < 0) {\n                errno = -(event->result);\n                /**\n                 * After a timeout, iouring will set errno to `ECANCELED`, but in the async implementation,\n                 * the errno after a timeout is `ETIMEDOUT`.\n                 * To maintain compatibility, numerical conversion is necessary.\n                 */\n                errno = errno == ECANCELED ? ETIMEDOUT : errno;\n                event->result = -1;\n            }\n            resume(ready_events[i]);\n        }\n    }\n\n    return true;\n}\n\nconst char *Iouring::get_opcode_name(io_uring_op opcode) {\n    switch (opcode) {\n    case IORING_OP_SOCKET:\n        return \"SOCKET\";\n    case IORING_OP_OPENAT:\n        return \"OPENAT\";\n    case IORING_OP_ACCEPT:\n        return \"ACCEPT\";\n    case IORING_OP_CONNECT:\n        return \"CONNECT\";\n    case IORING_OP_BIND:\n        return \"BIND\";\n    case IORING_OP_LISTEN:\n        return \"LISTEN\";\n    case IORING_OP_SEND:\n        return \"SEND\";\n    case IORING_OP_RECV:\n        return \"RECV\";\n    case IORING_OP_READV:\n        return \"READV\";\n    case IORING_OP_WRITEV:\n        return \"WRITEV\";\n    case IORING_OP_SENDMSG:\n        return \"SENDMSG\";\n    case IORING_OP_RECVMSG:\n        return \"RECVMSG\";\n    case IORING_OP_SHUTDOWN:\n        return \"SHUTDOWN\";\n    case IORING_OP_CLOSE:\n        return \"CLOSE\";\n    case IORING_OP_STATX:\n        return \"STATX\";\n    case IORING_OP_READ:\n        return \"READ\";\n    case IORING_OP_WRITE:\n        return \"WRITE\";\n    case IORING_OP_RENAMEAT:\n        return \"RENAMEAT\";\n    case IORING_OP_MKDIRAT:\n        return \"MKDIRAT\";\n    case IORING_OP_UNLINKAT:\n        return \"UNLINKAT\";\n    case IORING_OP_FSYNC:\n        return \"FSYNC\";\n#ifdef HAVE_IOURING_FUTEX\n    case IORING_OP_FUTEX_WAIT:\n        return \"FUTEX_WAIT\";\n    case IORING_OP_FUTEX_WAKE:\n        return \"FUTEX_WAKE\";\n#endif\n#ifdef HAVE_IOURING_FTRUNCATE\n    case IORING_OP_FTRUNCATE:\n        return \"FTRUNCATE\";\n#endif\n    case IORING_OP_POLL_ADD:\n        return \"POLL_ADD\";\n    case IORING_OP_POLL_REMOVE:\n        return \"POLL_REMOVE\";\n    case IORING_OP_ASYNC_CANCEL:\n        return \"CANCEL\";\n    default:\n        return \"unknown\";\n    }\n}\n\nstd::unordered_map<std::string, int> Iouring::list_all_opcode() {\n    std::unordered_map<std::string, int> opcodes;\n    for (int i = IORING_OP_NOP; i < IORING_OP_LAST; i++) {\n        auto name = get_opcode_name((io_uring_op) i);\n        if (strcmp(name, \"unknown\") == 0) {\n            continue;\n        }\n        opcodes[name] = i;\n    }\n    return opcodes;\n}\n\nvoid Iouring::submit(bool immediately) {\n    /**\n     * Submit SQEs in batches to reduce the number of system calls.\n     * If the number of SQEs is still less than SW_IOURING_SQE_BATCH_SIZE at the end of this event loop,\n     * it will be automatically submitted at the end of the event loop.\n     */\n    if (!immediately && swoole_event_is_running() && get_sq_used() < SW_IOURING_SQE_BATCH_SIZE) {\n        return;\n    }\n\n    do {\n        /**\n         * If the value returned by io_uring_submit() is less than the current length of the SQE queue,\n         * it indicates that only a part of the SQEs has been submitted and needs to be retried after the next event\n         * loop.\n         */\n        int ret = io_uring_submit(&ring);\n        if (ret < 0) {\n            /**\n             * Returned EAGAIN error, ignoring this error, will retry on the next submit.\n             */\n            if (-ret == EAGAIN) {\n                return;\n            } else if (-ret == EBUSY) {\n                usleep(10000);\n                continue;\n            } else if (-ret == EINTR) {\n                continue;\n            } else {\n                swoole_sys_error(\"io_uring_submit() failed\");\n                return;\n            }\n        }\n        break;\n    } while (true);\n}\n\nIouring *Iouring::get_instance() {\n    if (sw_unlikely(!SwooleTG.iouring)) {\n        if (!swoole_event_is_available()) {\n            swoole_error(\"The event loop is unavailable, unable to create io_uring instance.\");\n        }\n        SwooleTG.iouring = new Iouring(sw_reactor());\n    }\n    return SwooleTG.iouring;\n}\n\nssize_t Iouring::execute(IouringEvent *event) {\n    auto iouring = get_instance();\n    iouring->dispatch(event);\n    iouring->yield(event);\n    return event->result;\n}\n\nvoid Iouring::dispatch(IouringEvent *event) {\n    io_uring_sqe *sqe = alloc_sqe();\n    if (sw_unlikely(!sqe)) {\n        waiting_tasks.push(event);\n        return;\n    }\n\n    swoole_trace(\"opcode=%s, timeout[tv_sec=%ld, tv_nsec=%ld]\",\n                 get_opcode_name((io_uring_op) event->data.opcode),\n                 event->timeout.tv_sec,\n                 event->timeout.tv_nsec);\n\n    memcpy(sqe, &event->data, sizeof(event->data));\n    io_uring_sqe_set_data(sqe, (void *) event);\n\n    if (event->flags & SW_URING_TIMEOUT) {\n        auto timeout_sqe = alloc_sqe();\n        if (sw_likely(timeout_sqe)) {\n            memset(timeout_sqe, 0, sizeof(*timeout_sqe));\n            io_uring_prep_link_timeout(timeout_sqe, reinterpret_cast<__kernel_timespec *>(&event->timeout), 0);\n            io_uring_sqe_set_data(timeout_sqe, reinterpret_cast<void *>(TIMEOUT_EVENT));\n            sqe->flags |= IOSQE_IO_LINK;\n        } else {\n            swoole_warning(\"timeout setting failed, the iouring queue[%d] is full\", ring.ring_fd);\n        }\n    }\n\n    submit(false);\n}\n\n#define INIT_EVENT(op)                                                                                                 \\\n    IouringEvent event{};                                                                                              \\\n    event.coroutine = Coroutine::get_current_safe();\n\nint Iouring::open(const char *pathname, int flags, mode_t mode) {\n    INIT_EVENT(IORING_OP_OPENAT);\n    io_uring_prep_open(&event.data, pathname, flags | O_CLOEXEC, mode);\n    return static_cast<int>(execute(&event));\n}\n\nbool Iouring::cancel(IouringEvent *prev_event) {\n    INIT_EVENT(IORING_OP_ASYNC_CANCEL);\n    io_uring_prep_cancel(&event.data, (void *) prev_event, 0);\n    return static_cast<int>(execute(&event)) == 0;\n}\n\nint Iouring::socket(int domain, int type, int protocol, int flags) {\n    INIT_EVENT(IORING_OP_SOCKET);\n    io_uring_prep_socket(&event.data, domain, type, protocol, flags);\n    return static_cast<int>(execute(&event));\n}\n\nint Iouring::connect(int fd, const struct sockaddr *addr, socklen_t len, double timeout) {\n    INIT_EVENT(IORING_OP_CONNECT);\n    io_uring_prep_connect(&event.data, fd, addr, len);\n    event.set_timeout(timeout);\n    return static_cast<int>(execute(&event));\n}\n\nint Iouring::bind(int fd, const struct sockaddr *addr, socklen_t len) {\n#if 1\n    return ::bind(fd, addr, len);\n#else\n    INIT_EVENT(IORING_OP_BIND);\n    io_uring_prep_bind(&event.data, fd, (struct sockaddr *) addr, len);\n    return static_cast<int>(execute(&event));\n#endif\n}\n\nint Iouring::listen(int fd, int backlog) {\n#if 1\n    return ::listen(fd, backlog);\n#else\n    io_uring_prep_listen(sqe, fd, backlog);\n#endif\n}\n\nint Iouring::sleep(double seconds) {\n    IouringTimeout ts;\n    DOUBLE_TO_TIMESPEC(seconds, &ts);\n    return sleep(ts.tv_sec, ts.tv_nsec, 0);\n}\n\nint Iouring::sleep(int tv_sec, int tv_nsec, int flags) {\n    struct __kernel_timespec ts {\n        tv_sec, tv_nsec,\n    };\n\n    INIT_EVENT(IORING_OP_TIMEOUT);\n    io_uring_prep_timeout(&event.data, &ts, 0, flags);\n    execute(&event);\n    return errno == ETIME ? SW_OK : SW_ERR;\n}\n\nint Iouring::accept(int fd, struct sockaddr *addr, socklen_t *len, int flags, double timeout) {\n    INIT_EVENT(IORING_OP_ACCEPT);\n    io_uring_prep_accept(&event.data, fd, addr, len, flags);\n    event.set_timeout(timeout);\n    return static_cast<int>(execute(&event));\n}\n\nssize_t Iouring::recv(int fd, void *buf, size_t len, int flags, double timeout) {\n    INIT_EVENT(IORING_OP_RECV);\n    io_uring_prep_recv(&event.data, fd, buf, len, flags);\n    event.set_timeout(timeout);\n    return execute(&event);\n}\n\nssize_t Iouring::send(int fd, const void *buf, size_t len, int flags, double timeout) {\n    INIT_EVENT(IORING_OP_SEND);\n    io_uring_prep_send(&event.data, fd, buf, len, flags);\n    event.set_timeout(timeout);\n    return execute(&event);\n}\n\nssize_t Iouring::recvmsg(int fd, struct msghdr *message, int flags, double timeout) {\n    INIT_EVENT(IORING_OP_RECVMSG);\n    io_uring_prep_recvmsg(&event.data, fd, message, flags);\n    event.set_timeout(timeout);\n    return execute(&event);\n}\n\nssize_t Iouring::sendmsg(int fd, const struct msghdr *message, int flags, double timeout) {\n    INIT_EVENT(IORING_OP_SENDMSG);\n    io_uring_prep_sendmsg(&event.data, fd, message, flags);\n    event.set_timeout(timeout);\n    return execute(&event);\n}\n\nssize_t Iouring::sendto(\n    int fd, const void *buf, size_t n, int flags, const struct sockaddr *addr, socklen_t len, double timeout) {\n    INIT_EVENT(IORING_OP_SENDTO);\n    io_uring_prep_sendto(&event.data, fd, buf, n, flags, addr, len);\n    event.set_timeout(timeout);\n    return execute(&event);\n}\n\nssize_t Iouring::recvfrom(int fd, void *_buf, size_t _n, sockaddr *_addr, socklen_t *_socklen, double timeout) {\n    auto rv = recv(fd, _buf, _n, MSG_PEEK, timeout);\n    if (rv > 0) {\n        return ::recvfrom(fd, _buf, _n, MSG_DONTWAIT, _addr, _socklen);\n    } else {\n        return rv;\n    }\n}\n\nssize_t Iouring::readv(int fd, const struct iovec *iovec, int count, double timeout) {\n    INIT_EVENT(IORING_OP_READV);\n    io_uring_prep_readv(&event.data, fd, iovec, count, -1);\n    event.set_timeout(timeout);\n    return execute(&event);\n}\n\nssize_t Iouring::writev(int fd, const struct iovec *iovec, int count, double timeout) {\n    INIT_EVENT(IORING_OP_WRITEV);\n    io_uring_prep_writev(&event.data, fd, iovec, count, -1);\n    event.set_timeout(timeout);\n    return execute(&event);\n}\n\nssize_t Iouring::sendfile(int out_fd, int in_fd, off_t *offset, size_t size, double timeout) {\n    if (size == 0) {\n        return 0;\n    }\n\n    INIT_EVENT(IORING_OP_SPLICE);\n    event.set_timeout(timeout);\n\n#ifndef MSG_SPLICE_PAGES\n    int pipe_fds[2];\n    if (pipe(pipe_fds) < 0) {\n        return -1;\n    }\n\n    fcntl(pipe_fds[1], F_SETPIPE_SZ, 1024 * 1024);\n    int pipe_size = fcntl(pipe_fds[0], F_GETPIPE_SZ);\n    if (pipe_size < 0) {\n        pipe_size = 65536;\n    }\n\n    size_t total_sent = 0;\n    off_t current_offset = offset ? *offset : 0;\n\n    while (total_sent < size) {\n        size_t remaining = size - total_sent;\n        size_t to_send = std::min(remaining, (size_t) pipe_size);\n\n        event.data = {};\n        io_uring_prep_splice(\n            &event.data, in_fd, offset ? (current_offset + total_sent) : -1, pipe_fds[1], -1, to_send, 0);\n\n        ssize_t ret1 = execute(&event);\n        if (ret1 <= 0) {\n            if (total_sent > 0) {\n                break;\n            }\n            int saved_errno = errno;\n            close(pipe_fds[0]);\n            close(pipe_fds[1]);\n            errno = saved_errno;\n            return -1;\n        }\n\n        event.data = {};\n        io_uring_prep_splice(&event.data, pipe_fds[0], -1, out_fd, -1, ret1, 0);\n\n        ssize_t ret2 = execute(&event);\n        if (ret2 <= 0) {\n            if (total_sent > 0) {\n                break;\n            }\n            int saved_errno = errno;\n            close(pipe_fds[0]);\n            close(pipe_fds[1]);\n            errno = saved_errno;\n            return -1;\n        }\n\n        total_sent += ret2;\n\n        if (ret2 < ret1) {\n            break;\n        }\n    }\n\n    close(pipe_fds[0]);\n    close(pipe_fds[1]);\n\n    if (total_sent > 0 && offset) {\n        *offset += total_sent;\n    }\n\n    return total_sent;\n\n#else\n    struct msghdr msg = {0};\n    struct iovec iov = {0};\n\n    iov.iov_base = NULL;\n    iov.iov_len = size;\n\n    msg.msg_iov = &iov;\n    msg.msg_iovlen = 1;\n\n    io_uring_prep_sendmsg(&event.data, out_fd, &msg, MSG_SPLICE_PAGES);\n\n    event.data.len = size;\n    event.data.splice_fd_in = in_fd;\n    event.data.addr2 = offset ? (__u64) *offset : (__u64) -1;\n\n    ssize_t ret = execute(&event);\n\n    if (ret > 0 && offset) {\n        *offset += ret;\n    }\n\n    return ret;\n#endif\n}\n\nint Iouring::shutdown(int fd, int how) {\n    INIT_EVENT(IORING_OP_SHUTDOWN);\n    io_uring_prep_shutdown(&event.data, fd, how);\n    return static_cast<int>(execute(&event));\n}\n\nint Iouring::close(int fd) {\n    INIT_EVENT(IORING_OP_CLOSE);\n    io_uring_prep_close(&event.data, fd);\n    return static_cast<int>(execute(&event));\n}\n\nssize_t Iouring::read(int fd, void *buf, size_t size, double timeout) {\n    INIT_EVENT(IORING_OP_READ);\n    io_uring_prep_read(&event.data, fd, buf, size, -1);\n    event.set_timeout(timeout);\n    return execute(&event);\n}\n\nssize_t Iouring::write(int fd, const void *buf, size_t size, double timeout) {\n    INIT_EVENT(IORING_OP_WRITE);\n    io_uring_prep_write(&event.data, fd, buf, size, -1);\n    event.set_timeout(timeout);\n    return execute(&event);\n}\n\nint Iouring::rename(const char *oldpath, const char *newpath) {\n    INIT_EVENT(IORING_OP_RENAMEAT);\n    io_uring_prep_rename(&event.data, oldpath, newpath);\n    return static_cast<int>(execute(&event));\n}\n\nint Iouring::mkdir(const char *pathname, mode_t mode) {\n    INIT_EVENT(IORING_OP_MKDIRAT);\n    io_uring_prep_mkdir(&event.data, pathname, mode);\n    return static_cast<int>(execute(&event));\n}\n\nint Iouring::unlink(const char *pathname) {\n    INIT_EVENT(IORING_OP_UNLINK_FILE);\n    io_uring_prep_unlink(&event.data, pathname, 0);\n    return static_cast<int>(execute(&event));\n}\n\nint Iouring::rmdir(const char *pathname) {\n    INIT_EVENT(IORING_OP_UNLINK_DIR);\n    io_uring_prep_unlink(&event.data, pathname, AT_REMOVEDIR);\n    return static_cast<int>(execute(&event));\n}\n\nint Iouring::fsync(int fd) {\n    INIT_EVENT(IORING_OP_FSYNC);\n    io_uring_prep_fsync(&event.data, fd, 0);\n    return static_cast<int>(execute(&event));\n}\n\nint Iouring::fdatasync(int fd) {\n    INIT_EVENT(IORING_OP_FDATASYNC);\n    io_uring_prep_fsync(&event.data, fd, IORING_FSYNC_DATASYNC);\n    return static_cast<int>(execute(&event));\n}\n\n#ifdef HAVE_IOURING_FTRUNCATE\nint Iouring::ftruncate(int fd, off_t length) {\n    INIT_EVENT(IORING_OP_FTRUNCATE);\n    io_uring_prep_ftruncate(&event.data, fd, length);\n    return static_cast<int>(execute(&event));\n}\n#endif\n\nstatic inline int siginfo_to_status(const siginfo_t *info) {\n    int status = 0;\n\n    switch (info->si_code) {\n    case CLD_EXITED:\n        status = (info->si_status & 0xFF) << 8;\n        break;\n    case CLD_KILLED:\n        status = info->si_status & 0x7F;\n        break;\n    case CLD_DUMPED:\n        status = (info->si_status & 0x7F) | 0x80;\n        break;\n    case CLD_STOPPED:\n        status = ((info->si_status & 0xFF) << 8) | 0x7F;\n        break;\n    case CLD_CONTINUED:\n        status = 0xFFFF;\n        break;\n    }\n\n    return status;\n}\n\n#ifdef HAVE_IOURING_STATX\nstatic void swoole_statx_to_stat(const struct statx *statxbuf, struct stat *statbuf) {\n    statbuf->st_dev = (((unsigned int) statxbuf->stx_dev_major) << 8) | (unsigned int) statxbuf->stx_dev_minor;\n    statbuf->st_mode = statxbuf->stx_mode;\n    statbuf->st_nlink = statxbuf->stx_nlink;\n    statbuf->st_uid = statxbuf->stx_uid;\n    statbuf->st_gid = statxbuf->stx_gid;\n    statbuf->st_rdev = (((unsigned int) statxbuf->stx_rdev_major) << 8) | (unsigned int) statxbuf->stx_rdev_minor;\n    statbuf->st_ino = statxbuf->stx_ino;\n    statbuf->st_size = statxbuf->stx_size;\n    statbuf->st_blksize = statxbuf->stx_blksize;\n    statbuf->st_blocks = statxbuf->stx_blocks;\n    statbuf->st_atim.tv_sec = statxbuf->stx_atime.tv_sec;\n    statbuf->st_atim.tv_nsec = statxbuf->stx_atime.tv_nsec;\n    statbuf->st_mtim.tv_sec = statxbuf->stx_mtime.tv_sec;\n    statbuf->st_mtim.tv_nsec = statxbuf->stx_mtime.tv_nsec;\n    statbuf->st_ctim.tv_sec = statxbuf->stx_ctime.tv_sec;\n    statbuf->st_ctim.tv_nsec = statxbuf->stx_ctime.tv_nsec;\n}\n\nint Iouring::fstat(int fd, struct stat *statbuf) {\n    struct statx statxbuf;\n    INIT_EVENT(IORING_OP_FSTAT);\n    io_uring_prep_statx(&event.data, fd, \"\", AT_EMPTY_PATH, STATX_BASIC_STATS | STATX_BTIME, &statxbuf);\n\n    auto retval = execute(&event);\n    if (retval == 0) {\n        swoole_statx_to_stat(&statxbuf, statbuf);\n    }\n    return retval;\n}\n\nint Iouring::stat(const char *path, struct stat *statbuf) {\n    struct statx statxbuf;\n    INIT_EVENT(IORING_OP_FSTAT);\n    io_uring_prep_statx(\n        &event.data, AT_FDCWD, path, AT_SYMLINK_NOFOLLOW, STATX_BASIC_STATS | STATX_BTIME, &statxbuf);\n\n    auto retval = execute(&event);\n    if (retval == 0) {\n        swoole_statx_to_stat(&statxbuf, statbuf);\n    }\n    return retval;\n}\n#endif\n\n#ifdef HAVE_IOURING_FUTEX\nint Iouring::futex_wait(uint32_t *futex) {\n    INIT_EVENT(IORING_OP_FUTEX_WAIT);\n    io_uring_prep_futex_wait(&event.data, futex, 1, FUTEX_BITSET_MATCH_ANY, FUTEX2_SIZE_U32, 0);\n    return static_cast<int>(execute(&event));\n}\n\nint Iouring::futex_wakeup(uint32_t *futex) {\n    INIT_EVENT(IORING_OP_FUTEX_WAKE);\n    io_uring_prep_futex_wake(&event.data, futex, 1, FUTEX_BITSET_MATCH_ANY, FUTEX2_SIZE_U32, 0);\n    return static_cast<int>(execute(&event));\n}\n#endif\n\npid_t Iouring::wait(int *stat_loc, double timeout) {\n    return waitpid(-1, stat_loc, 0, timeout);\n}\n\npid_t Iouring::waitpid(pid_t _pid, int *stat_loc, int options, double timeout) {\n    if (options & WNOHANG) {\n        return ::waitpid(_pid, stat_loc, options);\n    }\n\n    INIT_EVENT(IORING_OP_WAITID);\n    siginfo_t info{};\n    idtype_t idtype = _pid > 0 ? P_PID : P_ALL;\n    id_t id = _pid > 0 ? _pid : 0;\n    options = options == 0 ? WEXITED : options;\n    io_uring_prep_waitid(&event.data, idtype, id, &info, options, 0);\n\n    event.set_timeout(timeout);\n    int rc = static_cast<int>(execute(&event));\n    if (rc != -1) {\n        *stat_loc = siginfo_to_status(&info);\n        return info.si_pid;\n    }\n    return rc;\n}\n\nint Iouring::poll(struct pollfd *fds, nfds_t nfds, int timeout) {\n    if (nfds != 1) {\n        errno = EINVAL;\n        swoole_error_log(\n            SW_LOG_WARNING, SW_ERROR_INVALID_PARAMS, \"incomplete poll() implementation, only supports one fd\");\n        return -1;\n    }\n\n    if (timeout == 0) {\n        return ::poll(fds, nfds, timeout);\n    }\n\n    INIT_EVENT(IORING_OP_POLL);\n    io_uring_prep_poll_add(&event.data, fds[0].fd, fds[0].events);\n    event.set_timeout((double) timeout / 1000);\n\n    int rc = static_cast<int>(execute(&event));\n    if (rc > 0) {\n        fds[0].revents = rc;\n        return 1;\n    }\n    return rc;\n}\n\nint Iouring::callback(Reactor *reactor, Event *event) {\n    auto *iouring = static_cast<Iouring *>(event->socket->object);\n    return iouring->wakeup() ? SW_OK : SW_ERR;\n}\n}  // namespace swoole\n#endif\n"
  },
  {
    "path": "src/coroutine/socket.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_string.h\"\n#include \"swoole_util.h\"\n#include \"swoole_reactor.h\"\n#include \"swoole_base64.h\"\n\n#include \"swoole_coroutine_socket.h\"\n#include \"swoole_coroutine_system.h\"\n\nnamespace swoole {\nnamespace coroutine {\nTimeoutType Socket::timeout_type_list[4] = {SW_TIMEOUT_DNS, SW_TIMEOUT_CONNECT, SW_TIMEOUT_READ, SW_TIMEOUT_WRITE};\n\nvoid Socket::timer_callback(Timer *timer, TimerNode *tnode) {\n    auto *socket = static_cast<Socket *>(tnode->data);\n    socket->set_err(ETIMEDOUT);\n    if (sw_likely(tnode == socket->read_timer)) {\n        socket->read_timer = nullptr;\n        socket->read_co->resume();\n    } else if (tnode == socket->write_timer) {\n        socket->write_timer = nullptr;\n        socket->write_co->resume();\n    } else {\n        abort();\n    }\n}\n\nint Socket::readable_event_callback(Reactor *reactor, Event *event) {\n    auto *socket = static_cast<Socket *>(event->socket->object);\n    socket->set_err(0);\n    if (sw_unlikely(socket->want_event != SW_EVENT_NULL)) {\n        if (socket->want_event == SW_EVENT_READ) {\n            socket->write_co->resume();\n        }\n    } else {\n        if (socket->recv_barrier && (*socket->recv_barrier)() && !event->socket->event_hup) {\n            return SW_OK;\n        }\n        socket->read_co->resume();\n    }\n\n    return SW_OK;\n}\n\nint Socket::writable_event_callback(Reactor *reactor, Event *event) {\n    auto *socket = static_cast<Socket *>(event->socket->object);\n    socket->set_err(0);\n    if (sw_unlikely(socket->want_event != SW_EVENT_NULL)) {\n        if (socket->want_event == SW_EVENT_WRITE) {\n            socket->read_co->resume();\n        }\n    } else {\n        if (socket->send_barrier && (*socket->send_barrier)() && !event->socket->event_hup) {\n            return SW_OK;\n        }\n        socket->write_co->resume();\n    }\n\n    return SW_OK;\n}\n\nint Socket::error_event_callback(Reactor *reactor, Event *event) {\n    auto *socket = static_cast<Socket *>(event->socket->object);\n    if (socket->write_co) {\n        socket->set_err(0);\n        socket->write_co->resume();\n    }\n    // Notice: socket maybe deleted in write coroutine\n    if (event->socket->object == socket && !event->socket->removed && socket->read_co) {\n        socket->set_err(0);\n        socket->read_co->resume();\n    }\n    return SW_OK;\n}\n\nbool Socket::add_event(const EventType event) {\n    bool ret = true;\n    if (sw_likely(!(socket->events & event))) {\n        if (socket->removed) {\n            ret = swoole_event_add(socket, event) == SW_OK;\n        } else {\n            ret = swoole_event_set(socket, socket->events | event) == SW_OK;\n        }\n    }\n    set_err(ret ? 0 : errno);\n    return ret;\n}\n\n#ifdef SW_LOG_TRACE_OPEN\nstatic const char *get_trigger_event_name(Socket *socket, EventType added_event) {\n    if (socket->is_closed()) {\n        return \"CLOSE\";\n    }\n    if (socket->errCode) {\n        return socket->errCode == ETIMEDOUT ? \"TIMEOUT\" : \"ERROR\";\n    }\n    return added_event == SW_EVENT_READ ? \"READ\" : \"WRITE\";\n}\n\nstatic const char *get_wait_event_name(Socket *socket, EventType event) {\n    if (socket->get_socket()->ssl_want_read) {\n        return \"SSL READ\";\n    } else if (socket->get_socket()->ssl_want_write) {\n        return \"SSL WRITE\";\n    } else {\n        return event == SW_EVENT_READ ? \"READ\" : \"WRITE\";\n    }\n}\n#endif\n\n/**\n * If an exception occurs while waiting for an event, false is returned.\n * For example, when waiting for a read event, timeout, connection closed, are exceptions to the interrupt event.\n * And these exceptions will actively set the errCode, We don't need to set the exception's errCode ourselves.\n * We only need to set the errCode for the socket operation when wait_event returns true,\n * which means that the exception's error code priority is greater than the current event error priority.\n */\nbool Socket::wait_event(const EventType event, const void **_buf, size_t _n) {\n    EventType added_event = event;\n    Coroutine *co = Coroutine::get_current_safe();\n    if (!co) {\n        return false;\n    }\n    if (sw_unlikely(socket->close_wait)) {\n        set_err(SW_ERROR_CO_SOCKET_CLOSE_WAIT);\n        return false;\n    }\n\n    if (socket->has_kernel_nobufs()) {\n        if (sw_likely(event == SW_EVENT_READ)) {\n            read_co = co;\n            System::sleep(0.01);\n            read_co = nullptr;\n        } else {\n            write_co = co;\n            System::sleep(0.01);\n            write_co = nullptr;\n        }\n        return !is_closed();\n    }\n\n    // clear the last errCode\n    set_err(0);\n    if (sw_unlikely(socket->ssl && ((event == SW_EVENT_READ && socket->ssl_want_write) ||\n                                    (event == SW_EVENT_WRITE && socket->ssl_want_read)))) {\n        if (sw_likely(socket->ssl_want_write && add_event(SW_EVENT_WRITE))) {\n            want_event = SW_EVENT_WRITE;\n        } else if (socket->ssl_want_read && add_event(SW_EVENT_READ)) {\n            want_event = SW_EVENT_READ;\n        } else {\n            return false;\n        }\n        added_event = want_event;\n    } else if (sw_unlikely(!add_event(event))) {\n        return false;\n    }\n    swoole_trace_log(SW_TRACE_SOCKET,\n                     \"socket#%d blongs to cid#%ld is waiting for %s event\",\n                     sock_fd,\n                     co->get_cid(),\n                     get_wait_event_name(this, event));\n\n    Coroutine::CancelFunc cancel_fn = [this, event](Coroutine *co) { return cancel(event); };\n\n    if (sw_likely(event == SW_EVENT_READ)) {\n        read_co = co;\n        read_co->yield(&cancel_fn);\n        read_co = nullptr;\n    } else if (event == SW_EVENT_WRITE) {\n        if (sw_unlikely(!zero_copy && _n > 0 && *_buf != get_write_buffer()->str)) {\n            write_buffer->clear();\n            write_buffer->append(static_cast<const char *>(*_buf), _n);\n            *_buf = write_buffer->str;\n        }\n        write_co = co;\n        write_co->yield(&cancel_fn);\n        write_co = nullptr;\n    } else {\n        assert(0);\n        return false;\n    }\n    // maybe read_co and write_co are all waiting for the same event when we use SSL\n    if (sw_likely(want_event == SW_EVENT_NULL || !has_bound())) {\n        Reactor *reactor = SwooleTG.reactor;\n        if (sw_likely(added_event == SW_EVENT_READ)) {\n            reactor->remove_read_event(socket);\n        } else {\n            reactor->remove_write_event(socket);\n        }\n    }\n    want_event = SW_EVENT_NULL;\n    swoole_trace_log(SW_TRACE_SOCKET,\n                     \"socket#%d blongs to cid#%ld trigger %s event\",\n                     sock_fd,\n                     co->get_cid(),\n                     get_trigger_event_name(this, added_event));\n    return !is_closed() && !errCode;\n}\n\nbool Socket::socks5_handshake() {\n    Socks5Proxy *ctx = socks5_proxy.get();\n    const auto len = ctx->pack_negotiate_request();\n    if (send(ctx->buf, len) < 0) {\n        return false;\n    }\n\n    auto send_fn = [this](const char *buf, size_t len) { return send(buf, len); };\n    char recv_buf[512];\n    ctx->state = SW_SOCKS5_STATE_HANDSHAKE;\n    while (true) {\n        const ssize_t n = recv(recv_buf, sizeof(recv_buf));\n        if (n > 0 && ctx->handshake(recv_buf, n, send_fn)) {\n            if (ctx->state == SW_SOCKS5_STATE_READY) {\n                return true;\n            }\n            continue;\n        }\n        break;\n    }\n    return false;\n}\n\nbool Socket::http_proxy_handshake() {\n    auto target_host = get_http_proxy_host_name();\n\n    String *send_buffer = get_write_buffer();\n    ON_SCOPE_EXIT {\n        send_buffer->clear();\n    };\n\n    size_t n = http_proxy->pack(send_buffer, target_host);\n    send_buffer->length = n;\n    swoole_trace_log(SW_TRACE_HTTP_CLIENT, \"proxy request: <<EOF\\n%.*sEOF\", (int) n, send_buffer->str);\n\n    if (send(send_buffer->str, n) != (ssize_t) n) {\n        return false;\n    }\n\n    String *recv_buffer = get_read_buffer();\n    ON_SCOPE_EXIT {\n        recv_buffer->clear();\n    };\n\n    ProtocolSwitch ps(this);\n    open_eof_check = true;\n    open_length_check = false;\n    protocol.package_eof_len = sizeof(\"\\r\\n\\r\\n\") - 1;\n    memcpy(protocol.package_eof, SW_STRS(\"\\r\\n\\r\\n\"));\n\n    if (recv_packet() <= 0) {\n        return false;\n    }\n\n    swoole_trace_log(SW_TRACE_HTTP_CLIENT, \"proxy response: <<EOF\\n%.*sEOF\", (int) n, recv_buffer->str);\n\n    if (!http_proxy->handshake(recv_buffer)) {\n        set_err(SW_ERROR_HTTP_PROXY_BAD_RESPONSE,\n                std::string(\"wrong http_proxy response received, \\n[Request]: \") + send_buffer->to_std_string() +\n                    \"\\n[Response]: \" + send_buffer->to_std_string());\n        return false;\n    }\n\n    return true;\n}\n\nvoid Socket::init_sock_type(SocketType _type) {\n    type = _type;\n    network::Socket::get_domain_and_type(_type, &sock_domain, &sock_type);\n}\n\nbool Socket::init_sock() {\n    socket =\n        make_socket(type, SW_FD_CO_SOCKET, sock_domain, sock_type, sock_protocol, SW_SOCK_NONBLOCK | SW_SOCK_CLOEXEC);\n    if (socket == nullptr) {\n        return false;\n    }\n    sock_fd = socket->fd;\n    socket->object = this;\n    socket->info.type = type;\n    return true;\n}\n\nbool Socket::reinit_sock(SocketType _type) {\n    int _sock_domain;\n    int _sock_type;\n    network::Socket::get_domain_and_type(_type, &_sock_domain, &_sock_type);\n\n    auto new_sock = make_socket(\n        _type, SW_FD_CO_SOCKET, _sock_domain, _sock_type, sock_protocol, SW_SOCK_NONBLOCK | SW_SOCK_CLOEXEC);\n    if (socket == nullptr) {\n        return false;\n    }\n\n    socket->free();\n    socket = new_sock;\n    socket->object = this;\n    socket->info.type = type;\n    sock_domain = _sock_domain;\n    sock_type = _sock_type;\n    type = _type;\n    sock_fd = socket->fd;\n    return true;\n}\n\nbool Socket::init_reactor_socket(int _fd) {\n    socket = make_socket(_fd, SW_FD_CO_SOCKET);\n    sock_fd = _fd;\n    socket->object = this;\n    socket->socket_type = type;\n    socket->nonblock = 1;\n    socket->cloexec = 1;\n    socket->info.type = type;\n    return true;\n}\n\nSocket::Socket(int _domain, int _type, int _protocol)\n    : sock_domain(_domain), sock_type(_type), sock_protocol(_protocol) {\n    type = network::Socket::convert_to_type(_domain, _type);\n    if (sw_unlikely(!init_sock())) {\n        return;\n    }\n    init_options();\n}\n\nSocket::Socket(SocketType _type) {\n    init_sock_type(_type);\n    if (sw_unlikely(!init_sock())) {\n        return;\n    }\n    init_options();\n}\n\nSocket::Socket(int _fd, SocketType _type) {\n    init_sock_type(_type);\n    if (sw_unlikely(!init_reactor_socket(_fd))) {\n        return;\n    }\n    if (_type == SW_SOCK_RAW) {\n        return;\n    }\n    socket->set_nonblock();\n    init_options();\n}\n\nSocket::Socket(int _fd, int _domain, int _type, int _protocol)\n    : sock_domain(_domain), sock_type(_type), sock_protocol(_protocol) {\n    type = network::Socket::convert_to_type(_domain, _type);\n    if (sw_unlikely(!init_reactor_socket(_fd))) {\n        return;\n    }\n    socket->set_nonblock();\n    init_options();\n}\n\n/**\n * Only used as accept member method\n */\nSocket::Socket(network::Socket *sock, const Socket *server_sock) {\n    type = server_sock->type;\n    sock_domain = server_sock->sock_domain;\n    sock_type = server_sock->sock_type;\n    sock_protocol = server_sock->sock_protocol;\n    sock_fd = sock->fd;\n    socket = sock;\n    socket->object = this;\n    socket->socket_type = type;\n    socket->fd_type = SW_FD_CO_SOCKET;\n    init_options();\n    /* inherits server socket options */\n    socket->dns_timeout = server_sock->get_socket()->dns_timeout;\n    socket->connect_timeout = server_sock->get_socket()->connect_timeout;\n    socket->read_timeout = server_sock->get_socket()->read_timeout;\n    socket->write_timeout = server_sock->get_socket()->write_timeout;\n    open_length_check = server_sock->open_length_check;\n    open_eof_check = server_sock->open_eof_check;\n    http2 = server_sock->http2;\n    protocol = server_sock->protocol;\n    connected = true;\n    ssl_context = server_sock->ssl_context;\n    ssl_is_server = server_sock->ssl_is_server;\n    if (server_sock->ssl_is_enable() && !ssl_create(server_sock->get_ssl_context())) {\n        close();\n    }\n}\n\nbool Socket::getsockname() const {\n    return socket->get_name() == SW_OK;\n}\n\nbool Socket::getpeername(network::Address *sa) {\n    sa->len = sizeof(sa->addr);\n    if (::getpeername(sock_fd, reinterpret_cast<sockaddr *>(&sa->addr), &sa->len) != 0) {\n        set_err(errno);\n        return false;\n    }\n    sa->type = type;\n    return true;\n}\n\ndouble Socket::get_timeout(TimeoutType _type) const {\n    return socket->get_timeout(_type);\n}\n\nString *Socket::get_read_buffer() {\n    if (sw_unlikely(!read_buffer)) {\n        read_buffer = make_string(SW_BUFFER_SIZE_BIG, buffer_allocator);\n    }\n    return read_buffer;\n}\n\nString *Socket::get_write_buffer() {\n    if (sw_unlikely(!write_buffer)) {\n        write_buffer = make_string(SW_BUFFER_SIZE_BIG, buffer_allocator);\n    }\n    return write_buffer;\n}\n\nString *Socket::pop_read_buffer() {\n    if (sw_unlikely(!read_buffer)) {\n        return nullptr;\n    }\n    auto tmp = read_buffer;\n    read_buffer = nullptr;\n    return tmp;\n}\n\nString *Socket::pop_write_buffer() {\n    if (sw_unlikely(!write_buffer)) {\n        return nullptr;\n    }\n    auto tmp = write_buffer;\n    write_buffer = nullptr;\n    return tmp;\n}\n\nvoid Socket::set_timeout(double timeout, int _type) const {\n    socket->set_timeout(timeout, _type);\n}\n\nconst char *Socket::get_event_str(const EventType event) const {\n    if (event == SW_EVENT_READ) {\n        return \"reading\";\n    } else if (event == SW_EVENT_WRITE) {\n        return \"writing\";\n    } else {\n        return read_co && write_co ? \"reading or writing\" : (read_co ? \"reading\" : \"writing\");\n    }\n}\n\nbool Socket::set_option(int level, int optname, int optval) const {\n    return set_option(level, optname, &optval, sizeof(optval));\n}\n\nbool Socket::get_option(int level, int optname, int *optval) const {\n    socklen_t optval_size = sizeof(*optval);\n    return get_option(level, optname, optval, &optval_size);\n}\n\nbool Socket::set_option(int level, int optname, const void *optval, socklen_t optlen) const {\n    if (socket->set_option(level, optname, optval, optlen) < 0) {\n        swoole_sys_warning(\"setsockopt(%d, %d, %d, %u) failed\", sock_fd, level, optname, optlen);\n        return false;\n    }\n    return true;\n}\n\nbool Socket::get_option(int level, int optname, void *optval, socklen_t *optlen) const {\n    if (socket->get_option(level, optname, optval, optlen) < 0) {\n        swoole_sys_warning(\"getsockopt(%d, %d, %d) failed\", sock_fd, level, optname);\n        return false;\n    }\n    return true;\n}\n\nvoid Socket::set_socks5_proxy(const std::string &host, int port, const std::string &user, const std::string &pwd) {\n    socks5_proxy.reset(Socks5Proxy::create(type, host, port, user, pwd));\n}\n\nvoid Socket::set_http_proxy(const std::string &host, int port, const std::string &user, const std::string &pwd) {\n    http_proxy.reset(HttpProxy::create(host, port, user, pwd));\n}\n\nbool Socket::connect(const sockaddr *addr, socklen_t addrlen) {\n    if (sw_unlikely(!is_available(SW_EVENT_RDWR))) {\n        return false;\n    }\n    int retval;\n    do {\n        retval = ::connect(sock_fd, addr, addrlen);\n    } while (retval < 0 && errno == EINTR);\n    if (retval < 0) {\n        if (errno != EINPROGRESS) {\n            set_err(errno);\n            return false;\n        } else {\n            TimerController timer(&write_timer, socket->connect_timeout, this, timer_callback);\n            if (!timer.start() || !wait_event(SW_EVENT_WRITE)) {\n                if (is_closed()) {\n                    set_err(ECONNABORTED);\n                }\n                return false;\n            } else {\n                if (socket->get_option(SOL_SOCKET, SO_ERROR, &errCode) < 0 || errCode != 0) {\n                    set_err(errCode);\n                    return false;\n                }\n            }\n        }\n    }\n    connected = true;\n    socket->get_name();\n    set_err(0);\n    return true;\n}\n\nbool Socket::connect(const std::string &_host, int _port, int flags) {\n    if (sw_unlikely(!is_available(SW_EVENT_RDWR))) {\n        return false;\n    }\n\n    if (ssl_context && (socks5_proxy || http_proxy)) {\n        /* If the proxy is enabled, the host will be replaced with the proxy ip,\n         * so we have to handle the host first,\n         * if the host is not an ip, assign it to ssl_host_name\n         */\n        if (!network::Address::verify_ip(sock_domain, _host)) {\n            ssl_host_name = _host;\n        }\n    }\n    if (socks5_proxy) {\n        socks5_proxy->target_host = _host;\n        socks5_proxy->target_port = _port;\n\n        connect_host = socks5_proxy->host;\n        connect_port = socks5_proxy->port;\n    } else if (http_proxy) {\n        http_proxy->target_host = _host;\n        http_proxy->target_port = _port;\n\n        connect_host = http_proxy->host;\n        connect_port = http_proxy->port;\n    } else {\n        connect_host = _host;\n        connect_port = _port;\n    }\n\n    if (socks5_proxy || http_proxy) {\n        if (socket->is_inet6()) {\n            if (network::Address::verify_ip(AF_INET, connect_host) && !reinit_sock(SW_SOCK_TCP)) {\n                return false;\n            }\n        } else if (socket->is_inet4()) {\n            if (network::Address::verify_ip(AF_INET6, connect_host) && !reinit_sock(SW_SOCK_TCP6)) {\n                return false;\n            }\n        }\n    }\n\n    NameResolver::Context *ctx = resolve_context_;\n\n    NameResolver::Context _ctx{};\n    if (ctx == nullptr) {\n        ctx = &_ctx;\n    }\n    ctx->timeout = socket->dns_timeout;\n\n    std::once_flag oc;\n    auto name_resolve_fn = [ctx, &oc, this](int _type) -> bool {\n        ctx->type = _type;\n        std::call_once(oc, [this]() {\n            if (ssl_context && !(socks5_proxy || http_proxy)) {\n                ssl_host_name = connect_host;\n            }\n        });\n        /* locked like wait_event */\n        read_co = write_co = Coroutine::get_current_safe();\n        ON_SCOPE_EXIT {\n            read_co = write_co = nullptr;\n        };\n        std::string addr = swoole_name_resolver_lookup(connect_host, ctx);\n        if (addr.empty()) {\n            set_err(swoole_get_last_error());\n            return false;\n        }\n        if (ctx->with_port) {\n            char delimiter = _type == AF_INET6 ? '@' : ':';\n            auto port_pos = addr.find_first_of(delimiter);\n            if (port_pos != std::string::npos) {\n                connect_port = std::stoi(addr.substr(port_pos + 1));\n                connect_host = addr.substr(0, port_pos);\n                return true;\n            }\n        }\n        connect_host = addr;\n        return true;\n    };\n\n    network::Address server_addr;\n\n    for (int i = 0; i < 2; i++) {\n        if (!server_addr.assign(type, connect_host, connect_port, false)) {\n            if (swoole_get_last_error() != SW_ERROR_BAD_HOST_ADDR) {\n                set_err(swoole_get_last_error(), swoole_strerror(swoole_get_last_error()));\n                return false;\n            }\n            if (!name_resolve_fn(sock_domain)) {\n                set_err(swoole_get_last_error(), swoole_strerror(swoole_get_last_error()));\n                return false;\n            }\n            continue;\n        }\n        break;\n    }\n\n    if (!connect(&server_addr.addr.ss, server_addr.len)) {\n        return false;\n    }\n\n    // socks5 proxy\n    if (socks5_proxy && !socks5_handshake()) {\n        set_err(SW_ERROR_SOCKS5_HANDSHAKE_FAILED);\n        return false;\n    }\n    // http proxy\n    if (http_proxy && !http_proxy->dont_handshake && !http_proxy_handshake()) {\n        set_err(SW_ERROR_HTTP_PROXY_HANDSHAKE_FAILED);\n        return false;\n    }\n    ssl_is_server = false;\n    if (ssl_context && !ssl_handshake()) {\n        if (swoole_get_last_error() == 0) {\n            set_err(SW_ERROR_SSL_HANDSHAKE_FAILED);\n        }\n        return false;\n    }\n    return true;\n}\n\nbool Socket::check_liveness() {\n    if (is_closed()) {\n        set_err(EBADF);\n        return false;\n    }\n    if (!socket->check_liveness()) {\n        set_err(errno ? errno : ECONNRESET);\n        return false;\n    }\n    set_err(0);\n    return true;\n}\n\nssize_t Socket::peek(void *_buf, size_t _n) {\n    ssize_t retval = socket->peek(_buf, _n, 0);\n    check_return_value(retval);\n    return retval;\n}\n\nbool Socket::poll(EventType _type, double timeout) {\n    if (sw_unlikely(!is_available(_type))) {\n        return false;\n    }\n    TimerNode **timer_pp = _type == SW_EVENT_READ ? &read_timer : &write_timer;\n    if (timeout == 0) {\n        timeout = _type == SW_EVENT_READ ? socket->read_timeout : socket->write_timeout;\n    }\n    TimerController timer(timer_pp, timeout, this, timer_callback);\n    if (timer.start() && wait_event(_type)) {\n        return true;\n    } else {\n        return false;\n    }\n}\n\nssize_t Socket::recv(void *_buf, size_t _n) {\n    if (sw_unlikely(!is_available(SW_EVENT_READ))) {\n        return -1;\n    }\n    ssize_t retval;\n    TimerController timer(&read_timer, socket->read_timeout, this, timer_callback);\n    do {\n        retval = socket->recv(_buf, _n, 0);\n    } while (retval < 0 && socket->catch_read_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ));\n    check_return_value(retval);\n    return retval;\n}\n\nssize_t Socket::send(const void *_buf, size_t _n) {\n    if (sw_unlikely(!is_available(SW_EVENT_WRITE))) {\n        return -1;\n    }\n    ssize_t retval;\n    TimerController timer(&write_timer, socket->write_timeout, this, timer_callback);\n    do {\n        retval = socket->send(_buf, _n, 0);\n    } while (retval < 0 && socket->catch_write_error(errno) == SW_WAIT && timer.start() &&\n             wait_event(SW_EVENT_WRITE, &_buf, _n));\n    check_return_value(retval);\n    return retval;\n}\n\nssize_t Socket::read(void *_buf, size_t _n) {\n    if (sw_unlikely(!is_available(SW_EVENT_READ))) {\n        return -1;\n    }\n    ssize_t retval;\n    TimerController timer(&read_timer, socket->read_timeout, this, timer_callback);\n    do {\n        retval = socket->read(_buf, _n);\n    } while (retval < 0 && socket->catch_read_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ));\n    check_return_value(retval);\n    return retval;\n}\n\nssize_t Socket::recv_line(void *_buf, size_t maxlen) {\n    size_t n = 0;\n    ssize_t m = 0;\n    auto t = static_cast<char *>(_buf);\n\n    *t = '\\0';\n    while (*t != '\\n' && *t != '\\r' && n < maxlen) {\n        if (m > 0) {\n            t++;\n            n++;\n        }\n        if (n < maxlen) {\n            m = recv_with_buffer((void *) t, 1);\n            if (m < 0) {\n                return -1;\n            } else if (m == 0) {\n                return n > 0 ? n : 0;\n            }\n        }\n    }\n    if (n < maxlen) {\n        n++;\n    }\n    return n;\n}\n\nssize_t Socket::recv_with_buffer(void *_buf, size_t _n) {\n    if (sw_unlikely(!is_available(SW_EVENT_READ))) {\n        return -1;\n    }\n\n    String *buffer = get_read_buffer();\n    size_t buffer_bytes = buffer->length - buffer->offset;\n\n    if (_n <= buffer_bytes) {\n        memcpy(_buf, buffer->str + buffer->offset, _n);\n        buffer->offset += _n;\n        return _n;\n    }\n\n    if (buffer_bytes > 0) {\n        memcpy(_buf, buffer->str + buffer->offset, buffer_bytes);\n        buffer->offset += buffer_bytes;\n    }\n\n    if ((size_t) buffer->offset >= buffer->size / 2) {\n        buffer->reduce(buffer->offset);\n    }\n\n    ssize_t retval = recv(buffer->str + buffer->length, buffer->size - buffer->length);\n    if (retval <= 0) {\n        return buffer_bytes > 0 ? buffer_bytes : retval;\n    }\n\n    buffer->length += retval;\n    size_t copy_bytes = SW_MIN(_n - buffer_bytes, buffer->length - buffer->offset);\n    memcpy((char *) _buf + buffer_bytes, buffer->str + buffer->offset, copy_bytes);\n    buffer->offset += copy_bytes;\n\n    return buffer_bytes + copy_bytes;\n}\n\nssize_t Socket::write(const void *_buf, size_t _n) {\n    if (sw_unlikely(!is_available(SW_EVENT_WRITE))) {\n        return -1;\n    }\n    ssize_t retval;\n    TimerController timer(&write_timer, socket->write_timeout, this, timer_callback);\n    do {\n        retval = socket->write((void *) _buf, _n);\n    } while (retval < 0 && socket->catch_write_error(errno) == SW_WAIT && timer.start() &&\n             wait_event(SW_EVENT_WRITE, &_buf, _n));\n    check_return_value(retval);\n    return retval;\n}\n\nssize_t Socket::readv(network::IOVector *io_vector) {\n    if (sw_unlikely(!is_available(SW_EVENT_READ))) {\n        return -1;\n    }\n    ssize_t retval;\n    TimerController timer(&read_timer, socket->read_timeout, this, timer_callback);\n    do {\n        retval = socket->readv(io_vector);\n    } while (retval < 0 && socket->catch_read_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ));\n    check_return_value(retval);\n\n    return retval;\n}\n\nssize_t Socket::readv_all(network::IOVector *io_vector) {\n    if (sw_unlikely(!is_available(SW_EVENT_READ))) {\n        return -1;\n    }\n    ssize_t total_bytes = 0;\n    TimerController timer(&read_timer, socket->read_timeout, this, timer_callback);\n\n    ssize_t retval = socket->readv(io_vector);\n    swoole_trace_log(SW_TRACE_SOCKET, \"readv %ld bytes, errno=%d\", retval, errno);\n\n    if (retval < 0 && socket->catch_read_error(errno) != SW_WAIT) {\n        set_err(errno);\n        return retval;\n    }\n\n    if (retval == 0) {\n        return retval;\n    }\n\n    total_bytes += retval > 0 ? retval : 0;\n    if (io_vector->get_remain_count() == 0) {\n        // iov should not be modified, prevent valgrind from checking for invalid read\n        return retval;\n    }\n\n    EventBarrier barrier = [&io_vector, &total_bytes, &retval, this]() -> bool {\n        do {\n            retval = socket->readv(io_vector);\n\n            if (retval <= 0) {\n                break;\n            }\n\n            total_bytes += retval;\n        } while (retval > 0 && io_vector->get_remain_count() > 0);\n\n        return retval < 0 && socket->catch_read_error(errno) == SW_WAIT;\n    };\n\n    recv_barrier = &barrier;\n    if (timer.start() && wait_event(SW_EVENT_READ)) {\n        check_return_value(retval);\n    }\n    recv_barrier = nullptr;\n\n    return total_bytes;\n}\n\nssize_t Socket::writev(network::IOVector *io_vector) {\n    if (sw_unlikely(!is_available(SW_EVENT_WRITE))) {\n        return -1;\n    }\n    ssize_t retval;\n    TimerController timer(&write_timer, socket->write_timeout, this, timer_callback);\n    do {\n        retval = socket->writev(io_vector);\n    } while (retval < 0 && socket->catch_write_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE));\n    check_return_value(retval);\n\n    return retval;\n}\n\nssize_t Socket::writev_all(network::IOVector *io_vector) {\n    if (sw_unlikely(!is_available(SW_EVENT_WRITE))) {\n        return -1;\n    }\n    ssize_t total_bytes = 0;\n    TimerController timer(&write_timer, socket->write_timeout, this, timer_callback);\n\n    ssize_t retval = socket->writev(io_vector);\n    swoole_trace_log(SW_TRACE_SOCKET, \"writev %ld bytes, errno=%d\", retval, errno);\n\n    if (retval < 0 && socket->catch_write_error(errno) != SW_WAIT) {\n        set_err(errno);\n        return retval;\n    }\n\n    if (retval == 0) {\n        return retval;\n    }\n\n    total_bytes += retval > 0 ? retval : 0;\n    if (io_vector->get_remain_count() == 0) {\n        // iov should not be modified, prevent valgrind from checking for invalid read\n        return retval;\n    }\n\n    EventBarrier barrier = [&io_vector, &total_bytes, &retval, this]() -> bool {\n        do {\n            retval = socket->writev(io_vector);\n\n            if (retval <= 0) {\n                break;\n            }\n\n            total_bytes += retval;\n        } while (retval > 0 && io_vector->get_remain_count() > 0);\n\n        return retval < 0 && socket->catch_write_error(errno) == SW_WAIT;\n    };\n\n    send_barrier = &barrier;\n    if (timer.start() && wait_event(SW_EVENT_WRITE)) {\n        check_return_value(retval);\n    }\n    send_barrier = nullptr;\n\n    return total_bytes;\n}\n\nssize_t Socket::recv_all(void *_buf, size_t _n) {\n    if (sw_unlikely(!is_available(SW_EVENT_READ))) {\n        return -1;\n    }\n    ssize_t retval = 0;\n    size_t total_bytes = 0;\n    TimerController timer(&read_timer, socket->read_timeout, this, timer_callback);\n\n    retval = socket->recv(_buf, _n, 0);\n\n    if (retval == 0 || retval == (ssize_t) _n) {\n        return retval;\n    }\n    if (retval < 0 && socket->catch_read_error(errno) != SW_WAIT) {\n        set_err(errno);\n        return retval;\n    }\n    total_bytes = retval > 0 ? retval : 0;\n\n    retval = -1;\n\n    EventBarrier barrier = [&_n, &total_bytes, &retval, &_buf, this]() -> bool {\n        retval = socket->recv((char *) _buf + total_bytes, _n - total_bytes, 0);\n        return (retval < 0 && socket->catch_read_error(errno) == SW_WAIT) ||\n               (retval > 0 && (total_bytes += retval) < _n);\n    };\n\n    recv_barrier = &barrier;\n    if (timer.start() && wait_event(SW_EVENT_READ)) {\n        check_return_value(retval);\n    }\n    recv_barrier = nullptr;\n\n    return retval < 0 && total_bytes == 0 ? -1 : total_bytes;\n}\n\nssize_t Socket::send_all(const void *_buf, size_t _n) {\n    if (sw_unlikely(!is_available(SW_EVENT_WRITE))) {\n        return -1;\n    }\n    ssize_t retval = 0;\n    size_t total_bytes = 0;\n    TimerController timer(&write_timer, socket->write_timeout, this, timer_callback);\n\n    retval = socket->send(_buf, _n, 0);\n\n    if (retval == 0 || retval == (ssize_t) _n) {\n        return retval;\n    }\n    if (retval < 0 && socket->catch_write_error(errno) != SW_WAIT) {\n        set_err(errno);\n        return retval;\n    }\n    total_bytes = retval > 0 ? retval : 0;\n\n    retval = -1;\n\n    EventBarrier barrier = [&_n, &total_bytes, &retval, &_buf, this]() -> bool {\n        retval = socket->send((char *) _buf + total_bytes, _n - total_bytes, 0);\n        return (retval < 0 && socket->catch_write_error(errno) == SW_WAIT) ||\n               (retval > 0 && (total_bytes += retval) < _n);\n    };\n\n    send_barrier = &barrier;\n    if (timer.start() && wait_event(SW_EVENT_WRITE)) {\n        check_return_value(retval);\n    }\n    send_barrier = nullptr;\n\n    return retval < 0 && total_bytes == 0 ? -1 : total_bytes;\n}\n\nssize_t Socket::recvmsg(struct msghdr *msg, int flags) {\n    if (sw_unlikely(!is_available(SW_EVENT_READ))) {\n        return -1;\n    }\n    ssize_t retval;\n    TimerController timer(&read_timer, socket->read_timeout, this, timer_callback);\n    do {\n        retval = ::recvmsg(sock_fd, msg, flags);\n    } while (retval < 0 && socket->catch_read_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ));\n    check_return_value(retval);\n    return retval;\n}\n\n/**\n * Notice: you must use non-global buffer here (or else it may be changed after yield)\n */\nssize_t Socket::sendmsg(const struct msghdr *msg, int flags) {\n    if (sw_unlikely(!is_available(SW_EVENT_WRITE))) {\n        return -1;\n    }\n    ssize_t retval;\n    TimerController timer(&write_timer, socket->write_timeout, this, timer_callback);\n    do {\n        retval = ::sendmsg(sock_fd, msg, flags);\n    } while (retval < 0 && socket->catch_write_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE));\n    check_return_value(retval);\n    return retval;\n}\n\nbool Socket::bind(const sockaddr *sa, socklen_t len) {\n    if (socket->bind(sa, len) < 0) {\n        set_err();\n        return false;\n    }\n    return true;\n}\n\nbool Socket::bind(const std::string &address, const int port) {\n    if (sw_unlikely(!is_available(SW_EVENT_NULL))) {\n        return false;\n    }\n\n    swoole_clear_last_error();\n\n    if (socket->set_reuse_addr() < 0) {\n        swoole_sys_warning(\"setsockopt(%d, SO_REUSEADDR) failed\", get_fd());\n    }\n\n    if (socket->bind(address, port) < 0) {\n        set_err();\n        return false;\n    }\n\n    if (socket->get_name() < 0) {\n        set_err();\n        return false;\n    }\n\n    return true;\n}\n\nbool Socket::listen(int backlog) {\n    if (sw_unlikely(!is_available(SW_EVENT_NULL))) {\n        return false;\n    }\n    this->backlog = backlog <= 0 ? SW_BACKLOG : backlog;\n    if (socket->listen(this->backlog) < 0) {\n        set_err(errno);\n        return false;\n    }\n    ssl_is_server = true;\n    return true;\n}\n\nSocket *Socket::accept(double timeout) {\n    if (sw_unlikely(!is_available(SW_EVENT_READ))) {\n        return nullptr;\n    }\n    if (ssl_is_enable() && sw_unlikely(ssl_context->context == nullptr) && !ssl_context_create()) {\n        return nullptr;\n    }\n    network::Socket *conn = socket->accept();\n    if (conn == nullptr && errno == EAGAIN) {\n        TimerController timer(&read_timer, timeout == 0 ? socket->read_timeout : timeout, this, timer_callback);\n        if (!timer.start() || !wait_event(SW_EVENT_READ)) {\n            return nullptr;\n        }\n        conn = socket->accept();\n    }\n    if (conn == nullptr) {\n        set_err(errno);\n        return nullptr;\n    }\n\n    auto *client_sock = new Socket(conn, this);\n    if (sw_unlikely(client_sock->get_fd() < 0)) {\n        swoole_sys_warning(\"new Socket() failed\");\n        set_err(errno);\n        delete client_sock;\n        return nullptr;\n    }\n\n    return client_sock;\n}\n\nbool Socket::ssl_context_create() {\n    if (socket->is_dgram()) {\n#ifdef SW_SUPPORT_DTLS\n        socket->dtls = 1;\n        ssl_context->protocols = SW_SSL_DTLS;\n        socket->chunk_size = SW_SSL_BUFFER_SIZE;\n#else\n        swoole_warning(\"DTLS support require openssl-1.1 or later\");\n        return false;\n#endif\n    }\n    ssl_context->http_v2 = http2;\n    if (!ssl_context->create()) {\n        set_err();\n        return false;\n    }\n    socket->ssl_send_ = 1;\n    return true;\n}\n\nbool Socket::ssl_create(SSLContext *ssl_context) {\n    if (socket->ssl) {\n        return true;\n    }\n    if (socket->ssl_create(ssl_context, 0) < 0) {\n        set_err(SW_ERROR_SSL_CREATE_SESSION_FAILED);\n        return false;\n    }\n#ifdef SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER\n    SSL_set_mode(socket->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);\n#endif\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n    if (!ssl_context->tls_host_name.empty()) {\n        SSL_set_tlsext_host_name(socket->ssl, ssl_context->tls_host_name.c_str());\n    } else if (!ssl_context->disable_tls_host_name && !ssl_host_name.empty()) {\n        SSL_set_tlsext_host_name(socket->ssl, ssl_host_name.c_str());\n    }\n#endif\n    return true;\n}\n\nbool Socket::ssl_handshake() {\n    if (ssl_handshaked) {\n        set_err(SW_ERROR_WRONG_OPERATION);\n        return false;\n    }\n    if (sw_unlikely(!is_available(SW_EVENT_RDWR))) {\n        return false;\n    }\n    /**\n     * If the ssl_context is empty, it indicates that this socket was not a connection\n     * returned by a server socket accept, and a new ssl_context needs to be created.\n     */\n    if (ssl_context->context == nullptr && !ssl_context_create()) {\n        return false;\n    }\n    if (!ssl_create(get_ssl_context())) {\n        return false;\n    }\n    /**\n     * The server will use ssl_accept to complete the SSL handshake,\n     * while the client will use ssl_connect.\n     */\n    if (!ssl_is_server) {\n        while (true) {\n            if (socket->ssl_connect() < 0) {\n                set_err();\n                return false;\n            }\n            if (socket->ssl_state == SW_SSL_STATE_WAIT_STREAM) {\n                TimerController timer(&read_timer, socket->read_timeout, this, timer_callback);\n                if (!timer.start() || !wait_event(SW_EVENT_READ)) {\n                    return false;\n                }\n            } else if (socket->ssl_state == SW_SSL_STATE_READY) {\n                break;\n            }\n        }\n    } else {\n        ReturnCode retval;\n        TimerController timer(&read_timer, socket->read_timeout, this, timer_callback);\n\n        do {\n            retval = socket->ssl_accept();\n        } while (retval == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ));\n\n        if (retval != SW_READY) {\n            set_err(SW_ERROR_SSL_BAD_CLIENT);\n            return false;\n        }\n    }\n    if (ssl_context->verify_peer) {\n        if (!ssl_verify(ssl_context->allow_self_signed)) {\n            return false;\n        }\n    }\n    ssl_handshaked = true;\n\n    return true;\n}\n\nbool Socket::ssl_verify(bool allow_self_signed) {\n    if (!socket->ssl_verify(allow_self_signed)) {\n        set_err(SW_ERROR_SSL_VERIFY_FAILED);\n        return false;\n    }\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n    if (!ssl_context->tls_host_name.empty() && !socket->ssl_check_host(ssl_context->tls_host_name.c_str())) {\n        set_err(SW_ERROR_SSL_VERIFY_FAILED);\n        return false;\n    }\n#endif\n    return true;\n}\n\nstd::string Socket::ssl_get_peer_cert() {\n    if (!socket->ssl_get_peer_certificate(sw_tg_buffer())) {\n        set_err(SW_ERROR_SSL_EMPTY_PEER_CERTIFICATE);\n        return \"\";\n    } else {\n        return sw_tg_buffer()->to_std_string();\n    }\n}\n\nbool Socket::sendfile(const char *filename, off_t offset, size_t length) {\n    if (sw_unlikely(!is_available(SW_EVENT_WRITE))) {\n        return false;\n    }\n\n    File file(filename, O_RDONLY);\n    if (!file.ready()) {\n        set_err(errno, std_string::format(\"open(%s) failed, %s\", filename, strerror(errno)));\n        return false;\n    }\n\n    if (length == 0) {\n        FileStatus file_stat;\n        if (!file.stat(&file_stat)) {\n            set_err(errno, std_string::format(\"fstat(%s) failed, %s\", filename, strerror(errno)));\n            return false;\n        }\n        length = file_stat.st_size;\n    } else {\n        // total length of the file\n        length = offset + length;\n    }\n\n    TimerController timer(&write_timer, socket->write_timeout, this, timer_callback);\n    while ((size_t) offset < length) {\n        ssize_t sent_bytes = (length - offset > SW_SENDFILE_CHUNK_SIZE) ? SW_SENDFILE_CHUNK_SIZE : length - offset;\n        ssize_t n = socket->sendfile(file, &offset, sent_bytes);\n        if (n > 0) {\n            continue;\n        } else if (n == 0) {\n            set_err(SW_ERROR_SYSTEM_CALL_FAIL, \"sendfile return zero\");\n            return false;\n        } else if (errno != EAGAIN) {\n            set_err(errno, std_string::format(\"sendfile(%d, %s) failed, %s\", sock_fd, filename, strerror(errno)));\n            return false;\n        }\n        if (!timer.start() || !wait_event(SW_EVENT_WRITE)) {\n            return false;\n        }\n    }\n    return true;\n}\n\nssize_t Socket::sendto(const std::string &host, int port, const void *_buf, size_t _n) {\n    if (sw_unlikely(!is_available(SW_EVENT_WRITE))) {\n        return -1;\n    }\n\n    ssize_t retval = 0;\n    network::Address addr;\n\n    if (!socket->is_dgram()) {\n        set_err(EPROTONOSUPPORT);\n        return -1;\n    }\n\n    auto ip_addr = host;\n\n    SW_LOOP_N(2) {\n        if (!addr.assign(type, ip_addr, port, false)) {\n            if (swoole_get_last_error() == SW_ERROR_BAD_HOST_ADDR) {\n                ip_addr = System::gethostbyname(host, sock_domain, socket->dns_timeout);\n                if (!ip_addr.empty()) {\n                    continue;\n                }\n            }\n            set_err();\n            return -1;\n        }\n        break;\n    }\n\n    TimerController timer(&write_timer, socket->write_timeout, this, timer_callback);\n    do {\n        retval = socket->sendto(addr, _buf, _n, 0);\n        swoole_trace_log(SW_TRACE_SOCKET, \"sendto %ld/%ld bytes, errno=%d\", retval, _n, errno);\n    } while (retval < 0 && (errno == EINTR || (socket->catch_write_error(errno) == SW_WAIT && timer.start() &&\n                                               wait_event(SW_EVENT_WRITE, &_buf, _n))));\n    check_return_value(retval);\n\n    return retval;\n}\n\nssize_t Socket::recvfrom(void *_buf, size_t _n) {\n    if (sw_unlikely(!is_available(SW_EVENT_READ))) {\n        return -1;\n    }\n    socket->info.len = sizeof(socket->info.addr);\n    return recvfrom(_buf, _n, reinterpret_cast<sockaddr *>(&socket->info.addr), &socket->info.len);\n}\n\nssize_t Socket::recvfrom(void *_buf, size_t _n, sockaddr *_addr, socklen_t *_socklen) {\n    if (sw_unlikely(!is_available(SW_EVENT_READ))) {\n        return -1;\n    }\n    ssize_t retval;\n    TimerController timer(&read_timer, socket->read_timeout, this, timer_callback);\n    do {\n        retval = ::recvfrom(sock_fd, _buf, _n, 0, _addr, _socklen);\n        swoole_trace_log(SW_TRACE_SOCKET, \"recvfrom %ld/%ld bytes, errno=%d\", retval, _n, errno);\n    } while (retval < 0 && ((errno == EINTR) || (socket->catch_read_error(errno) == SW_WAIT && timer.start() &&\n                                                 wait_event(SW_EVENT_READ))));\n    check_return_value(retval);\n    return retval;\n}\n\nssize_t Socket::recv_packet_with_length_protocol() {\n    ssize_t packet_len = SW_BUFFER_SIZE_STD;\n    ssize_t retval;\n    PacketLength pl;\n    uint32_t header_len = protocol.package_length_offset + protocol.package_length_size;\n\n    if (read_buffer->length > 0) {\n        if (read_buffer->length >= header_len ||\n            (protocol.package_length_size == 0 && protocol.package_length_type == '\\0')  // custom package_length_func\n        ) {\n            goto _get_length;\n        } else {\n            goto _recv_header;\n        }\n    }\n\n_recv_header:\n    retval = recv(read_buffer->str + read_buffer->length, header_len - read_buffer->length);\n    if (retval <= 0) {\n        return retval;\n    } else {\n        read_buffer->length += retval;\n    }\n\n_get_length:\n    pl.header_len = 0;\n    pl.buf = read_buffer->str;\n    pl.buf_size = (uint32_t) read_buffer->length;\n    packet_len = protocol.get_package_length(&protocol, socket, &pl);\n    swoole_trace_log(SW_TRACE_SOCKET, \"packet_len=%ld, length=%ld\", packet_len, read_buffer->length);\n    if (packet_len < 0) {\n        set_err(SW_ERROR_PACKAGE_LENGTH_NOT_FOUND, \"get package length failed\");\n        return 0;\n    } else if (packet_len == 0) {\n        if (pl.header_len != 0) {\n            header_len = pl.header_len;\n        }\n        goto _recv_header;\n    } else if (packet_len > protocol.package_max_length) {\n        read_buffer->clear();\n        swoole_error_log(SW_LOG_WARNING,\n                         SW_ERROR_PACKAGE_LENGTH_TOO_LARGE,\n                         \"packet length is too big, remote_addr=%s:%d, length=%zu\",\n                         socket->get_addr(),\n                         socket->get_port(),\n                         packet_len);\n        set_err(SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, sw_error);\n        return -1;\n    }\n\n    read_buffer->offset = packet_len;\n\n    if ((size_t) packet_len <= read_buffer->length) {\n        return packet_len;\n    }\n\n    if ((size_t) packet_len > read_buffer->size) {\n        read_buffer->extend(packet_len);\n    }\n\n    retval = recv_all(read_buffer->str + read_buffer->length, packet_len - read_buffer->length);\n    if (retval > 0) {\n        read_buffer->length += retval;\n        if (read_buffer->length != (size_t) packet_len) {\n            retval = 0;\n        } else {\n            return packet_len;\n        }\n    }\n\n    return retval;\n}\n\nssize_t Socket::recv_packet_with_eof_protocol() {\n    ssize_t retval, eof = -1;\n    char *buf = nullptr;\n    size_t l_buf = 0;\n\n    if (read_buffer->length > 0) {\n        goto _find_eof;\n    }\n\n    while (true) {\n        buf = read_buffer->str + read_buffer->length;\n        l_buf = read_buffer->size - read_buffer->length;\n\n        if (l_buf > SW_BUFFER_SIZE_BIG) {\n            l_buf = SW_BUFFER_SIZE_BIG;\n        }\n\n        retval = recv(buf, l_buf);\n        if (retval <= 0) {\n            read_buffer->clear();\n            return retval;\n        }\n\n        read_buffer->length += retval;\n\n        if (read_buffer->length < protocol.package_eof_len) {\n            continue;\n        }\n\n    _find_eof:\n        eof = swoole_strnpos(read_buffer->str, read_buffer->length, protocol.package_eof, protocol.package_eof_len);\n        if (eof >= 0) {\n            return (read_buffer->offset = eof + protocol.package_eof_len);\n        }\n        if (read_buffer->length == protocol.package_max_length) {\n            read_buffer->clear();\n            set_err(SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, \"no package eof, package_max_length exceeded\");\n            return -1;\n        }\n        if (read_buffer->length == read_buffer->size && read_buffer->size < protocol.package_max_length) {\n            size_t new_size = read_buffer->size * 2;\n            if (new_size > protocol.package_max_length) {\n                new_size = protocol.package_max_length;\n            }\n            read_buffer->extend(new_size);\n        }\n    }\n    assert(0);\n    return -1;\n}\n\n/**\n * Recv packet with protocol\n * Returns the length of the packet, [return value == read_buffer->offset]\n * ---------------------------------------Usage---------------------------------------------\n * ssize_t l = sock.recv_packet();\n * String *pkt = sock.get_read_buffer();\n * a) memcpy(result_buf, pkt->str, l); //copy data to new buffer\n * b) result_buf = sock.pop_packet();  //pop packet data, create a new buffer memory\n * ---------------------------------------read_buffer---------------------------------------\n * [read_buffer->length > read_buffer->offset] : may be unprocessed data in the buffer\n * [read_buffer->length == read_buffer->offset] : no data in the buffer\n */\nssize_t Socket::recv_packet(double timeout) {\n    if (sw_unlikely(!is_available(SW_EVENT_READ))) {\n        return -1;\n    }\n\n    TimerController timer(&read_timer, timeout == 0 ? socket->read_timeout : timeout, this, timer_callback);\n    if (sw_unlikely(!timer.start())) {\n        return 0;\n    }\n\n    get_read_buffer();\n\n    // unprocessed data\n    if (read_buffer->offset > 0) {\n        read_buffer->reduce(read_buffer->offset);\n    }\n\n    ssize_t recv_bytes;\n\n    if (open_length_check) {\n        recv_bytes = recv_packet_with_length_protocol();\n    } else if (open_eof_check) {\n        recv_bytes = recv_packet_with_eof_protocol();\n    } else {\n        recv_bytes = recv(read_buffer->str, read_buffer->size);\n        if (recv_bytes > 0) {\n            read_buffer->length = read_buffer->offset = recv_bytes;\n        }\n    }\n    if (recv_bytes <= 0) {\n        read_buffer->clear();\n    }\n    return recv_bytes;\n}\n\nbool Socket::shutdown(int _how) {\n    set_err(0);\n    if (!is_connected() || (_how == SHUT_RD && shutdown_read) || (_how == SHUT_WR && shutdown_write)) {\n        errno = ENOTCONN;\n    } else {\n        if (socket->ssl) {\n            socket->ssl_shutdown();\n        }\n        if (::shutdown(sock_fd, _how) == 0 || errno == ENOTCONN) {\n            if (errno == ENOTCONN) {\n                // connection reset by server side\n                _how = SHUT_RDWR;\n            }\n            switch (_how) {\n            case SHUT_RD:\n                shutdown_read = true;\n                break;\n            case SHUT_WR:\n                shutdown_write = true;\n                break;\n            default:\n                shutdown_read = shutdown_write = true;\n                break;\n            }\n            if (shutdown_read && shutdown_write) {\n                connected = false;\n            }\n            return true;\n        }\n    }\n    set_err(errno);\n    return false;\n}\n\nvoid Socket::ssl_close() const {\n    socket->ssl_close();\n}\n\nbool Socket::cancel(const EventType event) {\n    if (!has_bound(event)) {\n        return false;\n    }\n    if (event == SW_EVENT_READ) {\n        set_err(ECANCELED);\n        read_co->resume();\n        return true;\n    } else if (event == SW_EVENT_WRITE) {\n        set_err(ECANCELED);\n        write_co->resume();\n        return true;\n    } else {\n        set_err(EINVAL);\n        return false;\n    }\n}\n\n/**\n * @return bool\n * If true is returned, the related resources of this socket can be released\n * If false is returned, it means that other coroutines are still referencing this socket,\n * and need to wait for the coroutine bound to readable or writable event to execute close,\n * and release when all references are 0\n */\nbool Socket::close() {\n    if (is_closed()) {\n        set_err(EBADF);\n        return false;\n    }\n    if (connected) {\n        shutdown();\n    }\n    if (sw_unlikely(has_bound())) {\n        socket->close_wait = 1;\n        cancel(SW_EVENT_WRITE);\n        cancel(SW_EVENT_READ);\n        set_err(SW_ERROR_CO_SOCKET_CLOSE_WAIT);\n        return false;\n    } else {\n        sock_fd = SW_BAD_SOCKET;\n        if (dtor_ != nullptr) {\n            auto dtor = dtor_;\n            dtor_ = nullptr;\n            dtor(this);\n        }\n        return true;\n    }\n}\n\n/**\n * Warn:\n * the destructor should only be called in following two cases:\n * 1. construct failed\n * 2. called close() and it returns true\n * 3. called close() and it returns false, but it will not be accessed anywhere else\n */\nSocket::~Socket() {\n#ifdef SW_DEBUG\n    if (SwooleG.running) {\n        SW_ASSERT(!has_bound() && socket->removed);\n    }\n#endif\n    if (dtor_ != nullptr) {\n        dtor_(this);\n    }\n    delete read_buffer;\n    delete write_buffer;\n    if (socket == nullptr) {\n        return;\n    }\n    /* {{{ release socket resources */\n    if (socket->ssl) {\n        ssl_close();\n    }\n    socket->free();\n}\n\nbool Socket::TimerController::start() {\n    if (timeout != 0 && !*timer_pp) {\n        enabled = true;\n        if (timeout > 0) {\n            *timer_pp = swoole_timer_add(timeout, false, callback, socket_);\n            return *timer_pp != nullptr;\n        }\n        *timer_pp = reinterpret_cast<TimerNode *>(-1);\n    }\n    return true;\n}\n\nSocket::TimerController::~TimerController() {\n    if (enabled && *timer_pp) {\n        if (*timer_pp != reinterpret_cast<TimerNode *>(-1)) {\n            swoole_timer_del(*timer_pp);\n        }\n        *timer_pp = nullptr;\n    }\n}\n\nSocket::TimeoutSetter::TimeoutSetter(Socket *socket, double _timeout, TimeoutType _type)\n    : socket_(socket), timeout(_timeout), type(_type) {\n    if (_timeout == 0) {\n        return;\n    }\n    for (uint8_t i = 0; i < SW_ARRAY_SIZE(timeout_type_list); i++) {\n        if (_type & timeout_type_list[i]) {\n            original_timeout[i] = socket->get_timeout(timeout_type_list[i]);\n            if (_timeout != original_timeout[i]) {\n                socket->set_timeout(_timeout, timeout_type_list[i]);\n            }\n        }\n    }\n}\n\nSocket::TimeoutSetter::~TimeoutSetter() {\n    if (timeout == 0) {\n        return;\n    }\n    for (uint8_t i = 0; i < SW_ARRAY_SIZE(timeout_type_list); i++) {\n        if (type & timeout_type_list[i]) {\n            if (timeout != original_timeout[i]) {\n                socket_->set_timeout(original_timeout[i], timeout_type_list[i]);\n            }\n        }\n    }\n}\n\nbool Socket::TimeoutController::has_timedout(TimeoutType _type) {\n    SW_ASSERT_1BYTE(_type);\n    if (timeout > 0) {\n        if (sw_unlikely(startup_time == 0)) {\n            startup_time = microtime();\n        } else {\n            double used_time = microtime() - startup_time;\n            if (sw_unlikely(timeout - used_time < SW_TIMER_MIN_SEC)) {\n                socket_->set_err(ETIMEDOUT);\n                return true;\n            }\n            socket_->set_timeout(timeout - used_time, _type);\n        }\n    }\n    return false;\n}\n}  // namespace coroutine\n}  // namespace swoole\n"
  },
  {
    "path": "src/coroutine/system.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_coroutine_system.h\"\n#include \"swoole_lru_cache.h\"\n#include \"swoole_signal.h\"\n#include \"swoole_iouring.h\"\n#include \"swoole_socket_impl.h\"\n\nnamespace swoole {\nnamespace coroutine {\n\nstatic struct {\n    size_t capacity;\n    time_t expire;\n    LRUCache<std::string> *data;\n    size_t miss_count;\n    size_t hit_count;\n} dns_cache = {\n    1000,\n    60,\n    nullptr,\n    0,\n    0,\n};\n\nvoid System::set_dns_cache_expire(time_t expire) {\n    dns_cache.expire = expire;\n}\n\nvoid System::set_dns_cache_capacity(size_t capacity) {\n    dns_cache.capacity = capacity;\n    clear_dns_cache();\n    delete dns_cache.data;\n    dns_cache.data = nullptr;\n}\n\nvoid System::clear_dns_cache() {\n    if (dns_cache.data) {\n        dns_cache.data->clear();\n    }\n    dns_cache.miss_count = 0;\n    dns_cache.hit_count = 0;\n}\n\nfloat System::get_dns_cache_hit_ratio() {\n    auto total = dns_cache.hit_count + dns_cache.miss_count;\n    if (total == 0) {\n        return 0;\n    }\n    return (float) dns_cache.hit_count / (float) total;\n}\n\nint System::sleep(double sec) {\n#if SW_USE_IOURING\n    return Iouring::sleep(sec);\n#endif\n    Coroutine *co = Coroutine::get_current_safe();\n    if (sec < SW_TIMER_MIN_SEC) {\n        sec = SW_TIMER_MIN_SEC;\n    }\n    co->yield_ex(sec);\n    return co->is_canceled() ? SW_ERR : SW_OK;\n}\n\nstd::shared_ptr<String> System::read_file(const char *file, bool lock) {\n    std::shared_ptr<String> result;\n    async([&result, file, lock]() {\n        File fp(file, O_RDONLY);\n        if (!fp.ready()) {\n            swoole_sys_warning(\"open(%s, O_RDONLY) failed\", file);\n            return;\n        }\n        if (lock && !fp.lock(LOCK_SH)) {\n            swoole_sys_warning(\"flock(%s, LOCK_SH) failed\", file);\n            return;\n        }\n        ssize_t filesize = fp.get_size();\n        if (filesize > 0) {\n            auto content = make_string(filesize + 1);\n            content->length = fp.read_all(content->str, filesize);\n            content->str[content->length] = 0;\n            result = std::shared_ptr<String>(content);\n        } else {\n            result = fp.read_content();\n        }\n        if (lock && !fp.unlock()) {\n            swoole_sys_warning(\"flock(%s, LOCK_UN) failed\", file);\n        }\n    });\n    return result;\n}\n\nssize_t System::write_file(const char *file, const char *buf, size_t length, bool lock, int flags) {\n    ssize_t retval = -1;\n    int file_flags = flags | O_CREAT | O_WRONLY;\n    async([&]() {\n        File _file(file, file_flags, 0644);\n        if (!_file.ready()) {\n            swoole_sys_warning(\"open(%s, %d) failed\", file, file_flags);\n            return;\n        }\n        if (lock && !_file.lock(LOCK_EX)) {\n            swoole_sys_warning(\"flock(%s, LOCK_EX) failed\", file);\n            return;\n        }\n        size_t bytes = _file.write_all(buf, length);\n        if ((file_flags & SW_AIO_WRITE_FSYNC) && !_file.sync()) {\n            swoole_sys_warning(\"fsync(%s) failed\", file);\n        }\n        if (lock && !_file.unlock()) {\n            swoole_sys_warning(\"flock(%s, LOCK_UN) failed\", file);\n        }\n        retval = bytes;\n    });\n    return retval;\n}\n\nstd::string gethostbyname_impl_with_async(const std::string &hostname, int domain, double timeout) {\n    AsyncEvent ev{};\n    auto req = new GethostbynameRequest(hostname, domain);\n    ev.data = std::shared_ptr<AsyncRequest>(req);\n    ev.retval = 1;\n\n    coroutine::async(async::handler_gethostbyname, ev, timeout);\n\n    if (ev.retval == -1) {\n        if (ev.error == SW_ERROR_AIO_TIMEOUT) {\n            ev.error = SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT;\n        }\n        swoole_set_last_error(ev.error);\n        return \"\";\n    } else {\n        return req->addr;\n    }\n}\n\nstd::string System::gethostbyname(const std::string &hostname, int domain, double timeout) {\n    if (dns_cache.data == nullptr && dns_cache.capacity != 0) {\n        dns_cache.data = new LRUCache<std::string>(dns_cache.capacity);\n    }\n\n    std::string cache_key;\n    std::string result;\n\n    if (dns_cache.data) {\n        /**\n         * The cache key must end with a prefix that uses a dot.\n         * The domain name cannot contain the `.` symbol, and other characters are considered unsafe.\n         */\n        cache_key.append(domain == AF_INET ? \"IPv4.\" : \"IPv6.\");\n        cache_key.append(hostname);\n        auto cache = dns_cache.data->get(cache_key);\n\n        if (cache) {\n            dns_cache.hit_count++;\n            return *(std::string *) cache.get();\n        } else {\n            dns_cache.miss_count++;\n        }\n    }\n\n#ifdef SW_USE_CARES\n    auto result_list = dns_lookup_impl_with_cares(hostname.c_str(), domain, timeout);\n    if (!result_list.empty()) {\n        if (SwooleG.dns_lookup_random) {\n            result = result_list[swoole_rand() % result_list.size()];\n        } else {\n            result = result_list[0];\n        }\n    }\n#else\n    result = gethostbyname_impl_with_async(hostname, domain, timeout);\n#endif\n\n    if (dns_cache.data && !result.empty()) {\n        dns_cache.data->set(cache_key, std::make_shared<std::string>(result), dns_cache.expire);\n    }\n\n    return result;\n}\n\nstd::vector<std::string> System::getaddrinfo(\n    const std::string &hostname, int family, int socktype, int protocol, const std::string &service, double timeout) {\n    assert(!hostname.empty());\n    assert(family == AF_INET || family == AF_INET6);\n\n    AsyncEvent ev{};\n    auto req = new GetaddrinfoRequest(hostname, family, socktype, protocol, service);\n    ev.data = std::shared_ptr<AsyncRequest>(req);\n\n    coroutine::async(async::handler_getaddrinfo, ev, timeout);\n\n    std::vector<std::string> retval;\n\n    if (ev.retval == -1 || req->error != 0) {\n        if (ev.error == SW_ERROR_AIO_TIMEOUT) {\n            ev.error = SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT;\n        }\n        swoole_set_last_error(ev.error);\n    } else {\n        req->parse_result(retval);\n    }\n\n    return retval;\n}\n\nstruct SignalListener {\n    Coroutine *co;\n    int signo;\n};\n\n/**\n * Only the main thread should listen for signals,\n * without modifying it to a thread-local variable.\n */\nstatic SignalListener *listeners[SW_SIGNO_MAX];\n\nint System::wait_signal(int signal, double timeout) {\n    std::vector<int> signals = {signal};\n    return wait_signal(signals, timeout);\n}\n\n/**\n * @error: swoole_get_last_error()\n */\nint System::wait_signal(const std::vector<int> &signals, double timeout) {\n    SignalListener listener = {\n        Coroutine::get_current_safe(),\n        -1,\n    };\n\n    if (SwooleG.signal_listener_num > 0) {\n        swoole_set_last_error(EBUSY);\n        return -1;\n    }\n\n    auto callback_fn = [](int signo) {\n        auto listener = listeners[signo];\n        if (listener) {\n            listeners[signo] = nullptr;\n            listener->signo = signo;\n            listener->co->resume();\n        }\n    };\n\n    for (auto &signo : signals) {\n        if (signo < 0 || signo >= SW_SIGNO_MAX || signo == SIGCHLD) {\n            swoole_set_last_error(EINVAL);\n            return -1;\n        }\n\n        /* resgiter signal */\n        listeners[signo] = &listener;\n\n#ifdef SW_USE_THREAD_CONTEXT\n        swoole_event_defer([signo, &callback_fn](void *) { swoole_signal_set(signo, callback_fn); }, nullptr);\n#else\n        swoole_signal_set(signo, callback_fn);\n#endif\n    }\n\n    // exit condition\n    if (!sw_reactor()->isset_exit_condition(Reactor::EXIT_CONDITION_CO_SIGNAL_LISTENER)) {\n        sw_reactor()->set_exit_condition(\n            Reactor::EXIT_CONDITION_CO_SIGNAL_LISTENER,\n            [](Reactor *reactor, size_t &event_num) -> bool { return SwooleG.signal_async_listener_num == 0; });\n    }\n\n    SwooleG.signal_async_listener_num++;\n\n    bool retval = listener.co->yield_ex(timeout);\n\n    for (auto &signo : signals) {\n#ifdef SW_USE_THREAD_CONTEXT\n        swoole_event_defer([signo](void *) { swoole_signal_set(signo, nullptr); }, nullptr);\n#else\n        swoole_signal_set(signo, nullptr);\n#endif\n        listeners[signo] = nullptr;\n    }\n\n    SwooleG.signal_async_listener_num--;\n\n    return retval ? listener.signo : -1;\n}\n\nstruct CoroPollTask {\n    std::unordered_map<int, PollSocket> *fds = nullptr;\n    Coroutine *co = nullptr;\n    TimerNode *timer = nullptr;\n    bool success = false;\n    bool wait = true;\n};\n\nstatic inline void socket_poll_clean(const CoroPollTask *task) {\n    for (auto &fd : *task->fds) {\n        network::Socket *socket = fd.second.socket;\n        if (!socket) {\n            continue;\n        }\n        int retval = swoole_event_del(fd.second.socket);\n        socket->move_fd();\n        socket->free();\n        fd.second.socket = nullptr;\n        if (retval < 0) {\n            continue;\n        }\n    }\n}\n\nstatic void socket_poll_timeout(Timer *timer, TimerNode *tnode) {\n    auto *task = static_cast<CoroPollTask *>(tnode->data);\n    task->timer = nullptr;\n    task->success = false;\n    task->wait = false;\n    socket_poll_clean(task);\n    task->co->resume();\n}\n\nstatic void socket_poll_completed(void *data) {\n    auto *task = static_cast<CoroPollTask *>(data);\n    socket_poll_clean(task);\n    task->co->resume();\n}\n\nstatic inline void socket_poll_trigger_event(Reactor *reactor, CoroPollTask *task, int fd, EventType event) {\n    auto i = task->fds->find(fd);\n    if (event == SW_EVENT_ERROR && !(i->second.events & SW_EVENT_ERROR)) {\n        if (i->second.events & SW_EVENT_READ) {\n            i->second.revents |= SW_EVENT_READ;\n        }\n        if (i->second.events & SW_EVENT_WRITE) {\n            i->second.revents |= SW_EVENT_WRITE;\n        }\n    } else {\n        i->second.revents |= event;\n    }\n    if (task->wait) {\n        task->wait = false;\n        task->success = true;\n        if (task->timer) {\n            swoole_timer_del(task->timer);\n            task->timer = nullptr;\n        }\n        reactor->defer(socket_poll_completed, task);\n    }\n}\n\nstatic int socket_poll_read_callback(Reactor *reactor, Event *event) {\n    socket_poll_trigger_event(reactor, (CoroPollTask *) event->socket->object, event->fd, SW_EVENT_READ);\n    return SW_OK;\n}\n\nstatic int socket_poll_write_callback(Reactor *reactor, Event *event) {\n    socket_poll_trigger_event(reactor, (CoroPollTask *) event->socket->object, event->fd, SW_EVENT_WRITE);\n    return SW_OK;\n}\n\nstatic int socket_poll_error_callback(Reactor *reactor, Event *event) {\n    socket_poll_trigger_event(reactor, (CoroPollTask *) event->socket->object, event->fd, SW_EVENT_ERROR);\n    return SW_OK;\n}\n\nbool System::socket_poll(std::unordered_map<int, PollSocket> &fds, double timeout) {\n    if (timeout == 0) {\n        auto *event_list = static_cast<struct pollfd *>(sw_calloc(fds.size(), sizeof(struct pollfd)));\n        if (!event_list) {\n            swoole_warning(\"calloc() failed\");\n            return false;\n        }\n        int n = 0;\n        for (auto i = fds.begin(); i != fds.end(); ++i, n++) {\n            event_list[n].fd = i->first;\n            event_list[n].events = translate_events_to_poll(i->second.events);\n            event_list[n].revents = 0;\n        }\n        int retval = ::poll(event_list, n, 0);\n        if (retval > 0) {\n            int _n = 0;\n            for (auto i = fds.begin(); i != fds.end(); ++i, _n++) {\n                i->second.revents = translate_events_from_poll(event_list[_n].revents);\n            }\n        }\n        sw_free(event_list);\n        return retval > 0;\n    }\n\n    size_t tasked_num = 0;\n    CoroPollTask task;\n    task.fds = &fds;\n    task.co = Coroutine::get_current_safe();\n\n    for (auto &fd : fds) {\n        fd.second.socket = make_socket(fd.first, SW_FD_CO_POLL);\n        if (swoole_event_add(fd.second.socket, fd.second.events) < 0) {\n            // socket_poll() is not the owner of the socket,\n            // so the socket should not be closed upon failure or successful return;\n            // it is necessary to release control over the fd.\n            fd.second.socket->move_fd();\n            fd.second.socket->free();\n            continue;\n        }\n        fd.second.socket->object = &task;\n        tasked_num++;\n    }\n\n    if (sw_unlikely(tasked_num == 0)) {\n        return false;\n    }\n\n    if (timeout > 0) {\n        task.timer = swoole_timer_add(timeout, false, socket_poll_timeout, &task);\n    }\n\n    task.co->yield();\n\n    return task.success;\n}\n\nstruct EventWaiter {\n    network::Socket *socket;\n    Coroutine *co;\n    int revents;\n    int error_;\n\n    EventWaiter(int fd, int events, double timeout) {\n        error_ = revents = 0;\n        socket = swoole::make_socket(fd, SW_FD_CO_EVENT);\n        socket->object = this;\n        co = Coroutine::get_current_safe();\n\n        if (swoole_event_add(socket, events) < 0) {\n            error_ = swoole_get_last_error();\n            goto _done;\n        }\n\n        if (!co->yield_ex(timeout)) {\n            error_ = swoole_get_last_error();\n        }\n\n        swoole_event_del(socket);\n    _done:\n        socket->fd = -1; /* skip close */\n        socket->free();\n    }\n};\n\nstatic inline void event_waiter_callback(Reactor *reactor, EventWaiter *waiter, EventType event) {\n    if (waiter->revents == 0) {\n        reactor->defer([waiter](void *data) { waiter->co->resume(); });\n    }\n    waiter->revents |= event;\n}\n\nstatic int event_waiter_read_callback(Reactor *reactor, Event *event) {\n    event_waiter_callback(reactor, (EventWaiter *) event->socket->object, SW_EVENT_READ);\n    return SW_OK;\n}\n\nstatic int event_waiter_write_callback(Reactor *reactor, Event *event) {\n    event_waiter_callback(reactor, (EventWaiter *) event->socket->object, SW_EVENT_WRITE);\n    return SW_OK;\n}\n\nstatic int event_waiter_error_callback(Reactor *reactor, Event *event) {\n    event_waiter_callback(reactor, (EventWaiter *) event->socket->object, SW_EVENT_ERROR);\n    return SW_OK;\n}\n\n/**\n * @errror: errno & swoole_get_last_error()\n */\nint System::wait_event(int fd, int events, double timeout) {\n    events &= SW_EVENT_READ | SW_EVENT_WRITE;\n    if (events == 0) {\n        swoole_set_last_error(EINVAL);\n        return -1;\n    }\n\n    if (timeout == 0) {\n        pollfd pfd;\n        pfd.fd = fd;\n        pfd.events = translate_events_to_poll(events);\n        pfd.revents = 0;\n\n        int retval = ::poll(&pfd, 1, 0);\n        if (retval == 1) {\n            if (pfd.revents & POLLNVAL) {\n                swoole_set_last_error(EBADF);\n                return -1;\n            }\n            return translate_events_from_poll(pfd.revents);\n        }\n        swoole_set_last_error(retval < 0 ? errno : ETIMEDOUT);\n        return -1;\n    }\n\n    EventWaiter waiter(fd, events, timeout);\n    if (waiter.error_) {\n        errno = waiter.error_;\n        return SW_ERR;\n    }\n\n    int revents = waiter.revents;\n    if (revents & SW_EVENT_ERROR) {\n        revents ^= SW_EVENT_ERROR;\n        if (events & SW_EVENT_READ) {\n            revents |= SW_EVENT_READ;\n        }\n        if (events & SW_EVENT_WRITE) {\n            revents |= SW_EVENT_WRITE;\n        }\n    }\n\n    return revents;\n}\n\nbool System::exec(const char *command, bool get_error_stream, std::shared_ptr<String> buffer, int *status) {\n    Coroutine::get_current_safe();\n\n    pid_t pid;\n    int fd = swoole_shell_exec(command, &pid, get_error_stream);\n    if (fd < 0) {\n        swoole_sys_warning(\"Unable to execute '%s'\", command);\n        return false;\n    }\n\n    SocketImpl socket(fd, SW_SOCK_UNIX_STREAM);\n    while (true) {\n        ssize_t retval = socket.read(buffer->str + buffer->length, buffer->size - buffer->length);\n        if (retval > 0) {\n            buffer->length += retval;\n            if (buffer->length == buffer->size) {\n                buffer->extend();\n            }\n        } else {\n            break;\n        }\n    }\n    socket.close();\n\n    return waitpid_safe(pid, status, 0) == pid;\n}\n\nvoid System::init_reactor(Reactor *reactor) {\n    reactor->set_handler(SW_FD_CO_POLL, SW_EVENT_READ, socket_poll_read_callback);\n    reactor->set_handler(SW_FD_CO_POLL, SW_EVENT_WRITE, socket_poll_write_callback);\n    reactor->set_handler(SW_FD_CO_POLL, SW_EVENT_ERROR, socket_poll_error_callback);\n\n    reactor->set_handler(SW_FD_CO_EVENT, SW_EVENT_READ, event_waiter_read_callback);\n    reactor->set_handler(SW_FD_CO_EVENT, SW_EVENT_WRITE, event_waiter_write_callback);\n    reactor->set_handler(SW_FD_CO_EVENT, SW_EVENT_ERROR, event_waiter_error_callback);\n\n    reactor->set_handler(SW_FD_AIO, SW_EVENT_READ, AsyncThreads::callback);\n#ifdef SW_USE_IOURING\n    reactor->set_handler(SW_FD_IOURING, SW_EVENT_READ, Iouring::callback);\n#endif\n}\n\nstatic void async_task_completed(AsyncEvent *event) {\n    if (event->canceled) {\n        return;\n    }\n    auto *co = static_cast<Coroutine *>(event->object);\n    co->resume();\n}\n\n/**\n * @error: swoole_get_last_error()\n */\nbool async(async::Handler handler, AsyncEvent &event, double timeout) {\n    Coroutine *co = Coroutine::get_current_safe();\n\n    event.object = co;\n    event.handler = handler;\n    event.callback = async_task_completed;\n\n    AsyncEvent *_ev = async::dispatch(&event);\n    if (_ev == nullptr) {\n        return false;\n    }\n\n    if (!co->yield_ex(timeout)) {\n        event.canceled = _ev->canceled = true;\n        event.retval = -1;\n        event.error = errno = swoole_get_last_error();\n        return false;\n    } else {\n        event.canceled = _ev->canceled;\n        event.error = errno = _ev->error;\n        event.retval = _ev->retval;\n        return true;\n    }\n}\n\nstruct AsyncLambdaTask {\n    Coroutine *co;\n    std::function<void()> fn;\n};\n\nstatic void async_lambda_handler(AsyncEvent *event) {\n    auto *task = static_cast<AsyncLambdaTask *>(event->object);\n    task->fn();\n    event->error = errno;\n    event->retval = 0;\n}\n\nstatic void async_lambda_callback(AsyncEvent *event) {\n    auto *task = static_cast<AsyncLambdaTask *>(event->object);\n    task->co->resume();\n}\n\nbool async(const std::function<void()> &fn) {\n    AsyncEvent event{};\n    AsyncLambdaTask task{Coroutine::get_current_safe(), fn};\n\n    event.object = &task;\n    event.handler = async_lambda_handler;\n    event.callback = async_lambda_callback;\n\n    AsyncEvent *_ev = async::dispatch(&event);\n    if (_ev == nullptr) {\n        return false;\n    }\n\n    task.co->yield();\n    errno = _ev->error;\n    return true;\n}\n\nbool wait_for(const std::function<bool(void)> &fn) {\n    double second = SW_FILE_LOCK_DEFAULT_SECOND;\n    while (true) {\n        if (fn()) {\n            break;\n        }\n        if (System::sleep(second) != SW_OK) {\n            return false;\n        }\n        // Limit the maximum waiting time to 0.1 second; otherwise, this exponential time waiting\n        // will make it increasingly difficult to acquire the lock.\n        second = (second >= SW_FILE_LOCK_MAX_SECOND) ? SW_FILE_LOCK_DEFAULT_SECOND\n                                                     : SW_MIN(second * 2, SW_FILE_LOCK_MAX_SECOND);\n    }\n    return true;\n}\n\n}  // namespace coroutine\n}  // namespace swoole\n"
  },
  {
    "path": "src/coroutine/thread_context.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_signal.h\"\n#include \"swoole_async.h\"\n#include \"swoole_coroutine_context.h\"\n\n#ifdef SW_USE_THREAD_CONTEXT\n\nnamespace swoole {\nnamespace coroutine {\n\nstatic std::mutex g_lock;\nstatic Reactor *g_reactor = nullptr;\nstatic Timer *g_timer = nullptr;\nstatic String *g_buffer = nullptr;\nstatic AsyncThreads *g_async_threads = nullptr;\nstatic std::mutex *current_lock = nullptr;\n\nvoid thread_context_init() {\n    if (!swoole_timer_is_available()) {\n        swoole_timer_add(\n            1L,\n            false,\n            [](Timer *timer, TimerNode *tnode) {\n                // do nothing\n            },\n            nullptr);\n    }\n    if (SwooleTG.async_threads == nullptr) {\n        SwooleTG.async_threads = new AsyncThreads();\n    }\n    g_reactor = SwooleTG.reactor;\n    g_buffer = SwooleTG.buffer_stack;\n    g_timer = SwooleTG.timer;\n    g_async_threads = SwooleTG.async_threads;\n    current_lock = &g_lock;\n    g_lock.lock();\n}\n\nvoid thread_context_clean() {\n    g_reactor = nullptr;\n    g_buffer = nullptr;\n    g_timer = nullptr;\n    g_async_threads = nullptr;\n    current_lock = nullptr;\n    g_lock.unlock();\n}\n\nContext::Context(size_t stack_size, CoroutineFunc fn, void *private_data)\n    : fn_(std::move(fn)), private_data_(private_data) {\n    end_ = false;\n    lock_.lock();\n    swap_lock_ = nullptr;\n    thread_ = std::thread(Context::context_func, this);\n}\n\nContext::~Context() {\n    thread_.join();\n}\n\nbool Context::swap_in() {\n    swap_lock_ = current_lock;\n    current_lock = &lock_;\n    lock_.unlock();\n    swap_lock_->lock();\n    return true;\n}\n\nbool Context::swap_out() {\n    current_lock = swap_lock_;\n    swap_lock_->unlock();\n    lock_.lock();\n    return true;\n}\n\nvoid Context::context_func(coroutine_transfer_t arg) {\n    swoole_signal_block_all();\n    Context *_this = (Context *) arg;\n    SwooleTG.reactor = g_reactor;\n    SwooleTG.timer = g_timer;\n    SwooleTG.buffer_stack = g_buffer;\n    SwooleTG.async_threads = g_async_threads;\n    _this->lock_.lock();\n    _this->fn_(_this->private_data_);\n    _this->end_ = true;\n    current_lock = _this->swap_lock_;\n    _this->swap_lock_->unlock();\n}\n}  // namespace coroutine\n}  // namespace swoole\n#endif\n"
  },
  {
    "path": "src/coroutine/uring_socket.cc",
    "content": "\n\n/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | @link     https://www.swoole.com/                                    |\n  | @contact  team@swoole.com                                            |\n  | @license  https://github.com/swoole/swoole-src/blob/master/LICENSE   |\n  | @Author   Tianfeng Han  <rango@swoole.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_uring_socket.h\"\n#include \"swoole_coroutine_socket.h\"\n#include \"swoole_coroutine_system.h\"\n#include \"swoole_util.h\"\n#include \"swoole_iouring.h\"\n\ntypedef swoole::network::Socket NetSocket;\n\n#ifdef SW_USE_IOURING\nnamespace swoole {\nnamespace coroutine {\nbool UringSocket::connect(const sockaddr *addr, socklen_t addrlen) {\n    if (sw_unlikely(!is_available(SW_EVENT_RDWR))) {\n        return false;\n    }\n\n    write_co = read_co = Coroutine::get_current_safe();\n    int retval = Iouring::connect(socket->get_fd(), addr, addrlen, socket->connect_timeout);\n    write_co = read_co = nullptr;\n    if (retval < 0) {\n        return false;\n    }\n\n    connected = true;\n    socket->get_name();\n    set_err(0);\n    return true;\n}\n\nUringSocket *UringSocket::accept(double timeout) {\n    if (sw_unlikely(!is_available(SW_EVENT_READ))) {\n        return nullptr;\n    }\n    if (ssl_is_enable() && sw_unlikely(ssl_context->context == nullptr) && !ssl_context_create()) {\n        return nullptr;\n    }\n\n    read_co = Coroutine::get_current_safe();\n    network::Socket *conn = uring_accept(timeout == 0 ? socket->read_timeout : timeout);\n    read_co = nullptr;\n\n    if (conn == nullptr) {\n        set_err(errno);\n        return nullptr;\n    }\n\n    auto *client_sock = new UringSocket(conn, this);\n    if (sw_unlikely(client_sock->get_fd() < 0)) {\n        swoole_sys_warning(\"new Socket() failed\");\n        set_err(errno);\n        delete client_sock;\n        return nullptr;\n    }\n\n    return client_sock;\n}\n\nNetSocket *UringSocket::uring_accept(double timeout) {\n    auto *client_socket = new NetSocket();\n    int fd = Iouring::accept(socket->get_fd(),\n                             reinterpret_cast<sockaddr *>(&client_socket->info.addr),\n                             &client_socket->info.len,\n                             SOCK_CLOEXEC | SOCK_NONBLOCK,\n                             socket->read_timeout);\n    if (fd < 0) {\n        delete client_socket;\n        return nullptr;\n    }\n\n    client_socket->fd = fd;\n    client_socket->removed = 1;\n    client_socket->info.type = socket->socket_type;\n    client_socket->nonblock = 1;\n    client_socket->cloexec = 1;\n\n    return client_socket;\n}\n\nssize_t UringSocket::uring_send(const void *_buf, size_t _n) {\n    return Iouring::send(socket->get_fd(), _buf, _n, 0, socket->write_timeout);\n}\n\nssize_t UringSocket::uring_recv(void *_buf, size_t _n) {\n    return Iouring::recv(socket->get_fd(), _buf, _n, 0, socket->read_timeout);\n}\n\nssize_t UringSocket::uring_readv(const struct iovec *iovec, int count) {\n    return Iouring::readv(socket->get_fd(), iovec, count, socket->read_timeout);\n}\n\nssize_t UringSocket::uring_writev(const struct iovec *iovec, int count) {\n    return Iouring::writev(socket->get_fd(), iovec, count, socket->write_timeout);\n}\n\nssize_t UringSocket::uring_sendfile(const File &file, off_t *offset, size_t size) {\n    return Iouring::sendfile(socket->get_fd(), file.get_fd(), offset, size, socket->write_timeout);\n}\n\nssize_t UringSocket::read(void *_buf, size_t _n) {\n    if (sw_unlikely(!is_available(SW_EVENT_READ))) {\n        return -1;\n    }\n    read_co = Coroutine::get_current_safe();\n    ssize_t retval = Iouring::read(socket->get_fd(), _buf, _n, socket->read_timeout);\n    read_co = nullptr;\n    check_return_value(retval);\n    return retval;\n}\n\nssize_t UringSocket::write(const void *_buf, size_t _n) {\n    if (sw_unlikely(!is_available(SW_EVENT_WRITE))) {\n        return -1;\n    }\n    write_co = Coroutine::get_current_safe();\n    ssize_t retval = Iouring::write(socket->get_fd(), _buf, _n, socket->write_timeout);\n    write_co = nullptr;\n    check_return_value(retval);\n    return retval;\n}\n\nssize_t UringSocket::recvmsg(msghdr *msg, int flags) {\n    if (sw_unlikely(!is_available(SW_EVENT_READ))) {\n        return -1;\n    }\n    read_co = Coroutine::get_current_safe();\n    ssize_t retval = Iouring::recvmsg(socket->get_fd(), msg, flags, socket->read_timeout);\n    read_co = nullptr;\n    check_return_value(retval);\n    return retval;\n}\n\nssize_t UringSocket::sendmsg(const msghdr *msg, int flags) {\n    if (sw_unlikely(!is_available(SW_EVENT_WRITE))) {\n        return -1;\n    }\n    write_co = Coroutine::get_current_safe();\n    ssize_t retval = Iouring::sendmsg(socket->get_fd(), msg, flags, socket->write_timeout);\n    write_co = nullptr;\n    check_return_value(retval);\n    return retval;\n}\n\nssize_t UringSocket::recvfrom(void *_buf, size_t _n, sockaddr *_addr, socklen_t *_socklen) {\n    if (sw_unlikely(!is_available(SW_EVENT_READ))) {\n        return -1;\n    }\n    read_co = Coroutine::get_current_safe();\n    ssize_t retval = Iouring::recvfrom(socket->get_fd(), _buf, _n, _addr, _socklen, socket->read_timeout);\n    read_co = nullptr;\n    check_return_value(retval);\n    return retval;\n}\n\nssize_t UringSocket::sendto(const std::string &host, int port, const void *_buf, size_t _n) {\n    if (sw_unlikely(!is_available(SW_EVENT_WRITE))) {\n        return -1;\n    }\n\n    ssize_t retval = 0;\n    network::Address addr;\n\n    if (!socket->is_dgram()) {\n        set_err(EPROTONOSUPPORT);\n        return -1;\n    }\n\n    auto ip_addr = host;\n\n    SW_LOOP_N(2) {\n        if (!addr.assign(type, ip_addr, port, false)) {\n            if (swoole_get_last_error() == SW_ERROR_BAD_HOST_ADDR) {\n                ip_addr = System::gethostbyname(host, sock_domain, socket->dns_timeout);\n                if (!ip_addr.empty()) {\n                    continue;\n                }\n            }\n            set_err();\n            return -1;\n        }\n        break;\n    }\n\n    write_co = Coroutine::get_current_safe();\n    retval = Iouring::sendto(socket->get_fd(), _buf, _n, 0, &addr.addr.ss, addr.len, socket->write_timeout);\n    write_co = nullptr;\n\n    swoole_trace_log(SW_TRACE_SOCKET, \"sendto %ld/%ld bytes, errno=%d\", retval, _n, errno);\n\n    check_return_value(retval);\n    return retval;\n}\n\nssize_t UringSocket::recv(void *_buf, size_t _n) {\n    if (sw_unlikely(!is_available(SW_EVENT_READ))) {\n        return -1;\n    }\n\n    ssize_t retval;\n    read_co = Coroutine::get_current_safe();\n    if (is_ssl()) {\n        retval = ssl_recv(_buf, _n);\n    } else {\n        retval = uring_recv(_buf, _n);\n    }\n    read_co = nullptr;\n\n    check_return_value(retval);\n    return retval;\n}\n\nssize_t UringSocket::send(const void *_buf, size_t _n) {\n    if (sw_unlikely(!is_available(SW_EVENT_WRITE))) {\n        return -1;\n    }\n\n    ssize_t retval;\n    write_co = Coroutine::get_current_safe();\n    if (is_ssl()) {\n        retval = ssl_send(_buf, _n);\n    } else {\n        retval = uring_send(_buf, _n);\n    }\n    write_co = nullptr;\n\n    check_return_value(retval);\n    return retval;\n}\n\nssize_t UringSocket::recv_all(void *_buf, size_t _n) {\n    ssize_t retval = 0;\n    size_t total_bytes = 0;\n\n    do {\n        retval = recv((char *) _buf + total_bytes, _n - total_bytes);\n        if (retval <= 0) {\n            break;\n        }\n        total_bytes += retval;\n    } while (total_bytes < _n);\n\n    check_return_value(retval);\n    return retval < 0 && total_bytes == 0 ? -1 : total_bytes;\n}\n\nssize_t UringSocket::send_all(const void *_buf, size_t _n) {\n    ssize_t retval = 0;\n    size_t total_bytes = 0;\n\n    do {\n        retval = send((char *) _buf + total_bytes, _n - total_bytes);\n        if (retval <= 0) {\n            break;\n        }\n        total_bytes += retval;\n    } while (total_bytes < _n);\n\n    check_return_value(retval);\n    return retval < 0 && total_bytes == 0 ? -1 : total_bytes;\n}\n\nbool UringSocket::poll(EventType _type, double timeout) {\n    if (sw_unlikely(!is_available(_type))) {\n        return false;\n    }\n\n    struct pollfd fds[1];\n    fds[0].events = translate_events_to_poll(_type);\n    fds[0].fd = socket->get_fd();\n    fds[0].revents = 0;\n\n    auto rc = Iouring::poll(fds, 1, timeout > 0 ? timeout * 1000 : timeout) == 1;\n    if (rc != 1) {\n        set_err(rc == 0 ? ETIMEDOUT : errno);\n        return false;\n    }\n    return true;\n}\n\nbool UringSocket::sendfile(const char *filename, off_t offset, size_t length) {\n    if (sw_unlikely(!is_available(SW_EVENT_WRITE))) {\n        return false;\n    }\n\n    File file(filename, O_RDONLY);\n    if (!file.ready()) {\n        set_err(errno, std_string::format(\"open(%s) failed, %s\", filename, strerror(errno)));\n        return false;\n    }\n\n    if (length == 0) {\n        FileStatus file_stat;\n        if (!file.stat(&file_stat)) {\n            set_err(errno, std_string::format(\"fstat(%s) failed, %s\", filename, strerror(errno)));\n            return false;\n        }\n        length = file_stat.st_size;\n    } else {\n        // total length of the file\n        length = offset + length;\n    }\n\n    ssize_t retval;\n\n    write_co = Coroutine::get_current_safe();\n    if (is_ssl()) {\n        retval = ssl_sendfile(file, &offset, length);\n    } else {\n        retval = uring_sendfile(file, &offset, length);\n    }\n    write_co = nullptr;\n\n    return retval == (ssize_t) length;\n}\n\nssize_t UringSocket::readv(network::IOVector *io_vector) {\n    ssize_t retval;\n    if (sw_unlikely(!is_available(SW_EVENT_READ))) {\n        return -1;\n    }\n\n    read_co = Coroutine::get_current_safe();\n    do {\n        if (is_ssl()) {\n            retval = ssl_readv(io_vector);\n        } else {\n            retval = uring_readv(io_vector->get_iterator(), io_vector->get_remain_count());\n        }\n        io_vector->update_iterator(retval);\n    } while (retval < 0 && errno == EINTR);\n    read_co = nullptr;\n\n    check_return_value(retval);\n    return retval;\n}\n\nssize_t UringSocket::writev(network::IOVector *io_vector) {\n    ssize_t retval;\n    if (sw_unlikely(!is_available(SW_EVENT_WRITE))) {\n        return -1;\n    }\n\n    write_co = Coroutine::get_current_safe();\n    do {\n        if (is_ssl()) {\n            retval = ssl_writev(io_vector);\n        } else {\n            retval = uring_writev(io_vector->get_iterator(), io_vector->get_remain_count());\n        }\n        io_vector->update_iterator(retval);\n    } while (retval < 0 && errno == EINTR);\n    write_co = nullptr;\n\n    check_return_value(retval);\n    return retval;\n}\n\nssize_t UringSocket::readv_all(network::IOVector *io_vector) {\n    ssize_t retval = 0;\n    size_t total_bytes = 0;\n\n    do {\n        retval = readv(io_vector);\n        if (retval <= 0) {\n            break;\n        }\n        total_bytes += retval;\n    } while (retval > 0 && io_vector->get_remain_count() > 0);\n\n    check_return_value(retval);\n    return retval < 0 && total_bytes == 0 ? -1 : total_bytes;\n}\n\nssize_t UringSocket::writev_all(network::IOVector *io_vector) {\n    ssize_t retval = 0;\n    size_t total_bytes = 0;\n\n    do {\n        retval = writev(io_vector);\n        if (retval <= 0) {\n            break;\n        }\n        total_bytes += retval;\n    } while (retval > 0 && io_vector->get_remain_count() > 0);\n\n    check_return_value(retval);\n    return retval < 0 && total_bytes == 0 ? -1 : total_bytes;\n}\n\nbool UringSocket::ssl_bio_write() {\n    auto buf = get_write_buffer();\n\n    while (true) {\n        size_t pending = BIO_ctrl_pending(wbio);\n        if (pending == 0) {\n            break;\n        }\n\n        int nread = BIO_read(wbio, buf->str, buf->size);\n        if (nread <= 0) {\n            set_err(SW_ERROR_SSL_BAD_PROTOCOL);\n            return false;\n        }\n\n        ssize_t written = 0;\n        while (written < nread) {\n            ssize_t n = uring_send(buf->str + written, nread - written);\n            if (n > 0) {\n                written += n;\n            } else if (n == 0) {\n                errno = SW_ERROR_SSL_RESET;\n                return false;\n            } else {\n                if (errno == EINTR) {\n                    continue;\n                }\n                set_err(errno);\n                return false;\n            }\n        }\n    }\n\n    return true;\n}\n\nbool UringSocket::ssl_bio_read() {\n    auto buf = get_read_buffer();\n    ssize_t rv = uring_recv(buf->str, buf->size);\n    if (rv > 0) {\n        int written = BIO_write(rbio, buf->str, rv);\n        if (written != rv) {\n            set_err(SW_ERROR_SSL_BAD_PROTOCOL);\n            return false;\n        }\n        return true;\n    } else if (rv == 0) {\n        set_err(SW_ERROR_SSL_RESET);\n        return false;\n    } else {\n        if (errno == EINTR) {\n            return ssl_bio_read();\n        }\n        set_err(errno);\n        return false;\n    }\n}\n\nbool UringSocket::ssl_bio_prepare() {\n    if (BIO_ctrl_pending(wbio) > 0) {\n        if (!ssl_bio_write()) {\n            check_return_value(-1);\n            return false;\n        }\n    }\n    return true;\n}\n\nbool UringSocket::ssl_bio_perform(int rc, const char *fn) {\n    if (!ssl_bio_prepare()) {\n        return false;\n    }\n\n    int error = SSL_get_error(socket->ssl, rc);\n    if (error == SSL_ERROR_WANT_WRITE) {\n        if (ssl_bio_write()) {\n        _error:\n            check_return_value(-1);\n            return false;\n        }\n        return true;\n    } else if (error == SSL_ERROR_WANT_READ) {\n        if (!ssl_bio_read()) {\n            goto _error;\n        }\n        return true;\n    } else if (error == SSL_ERROR_ZERO_RETURN) {\n        swoole_debug(\"%s(fd=%d) return zero value\", fn, socket->get_fd());\n        error = SW_ERROR_SSL_RESET;\n        goto _error;\n    } else if (error == SSL_ERROR_SYSCALL) {\n        goto _error;\n    } else {\n        ulong_t err_code = ERR_get_error();\n        if (err_code) {\n            char error_buf[512];\n            ERR_error_string_n(err_code, error_buf, sizeof(error_buf));\n            swoole_notice(\"%s(fd=%d) to server[%s:%d] failed. Error: %s[%d|%d]\",\n                          fn,\n                          socket->get_fd(),\n                          socket->info.get_addr(),\n                          socket->info.get_port(),\n                          error_buf,\n                          error,\n                          ERR_GET_REASON(err_code));\n            set_err(SW_ERROR_SSL_BAD_PROTOCOL, error_buf);\n        } else {\n            set_err(SW_ERROR_SSL_BAD_PROTOCOL);\n        }\n        return false;\n    }\n}\n\nbool UringSocket::ssl_handshake() {\n    if (ssl_handshaked) {\n        set_err(SW_ERROR_WRONG_OPERATION);\n        return false;\n    }\n    if (sw_unlikely(!is_available(SW_EVENT_RDWR))) {\n        return false;\n    }\n\n    /**\n     * If the ssl_context is empty, it indicates that this socket was not a connection\n     * returned by a server socket accept, and a new ssl_context needs to be created.\n     */\n    if (ssl_context->context == nullptr && !ssl_context_create()) {\n        return false;\n    }\n    if (!ssl_create(get_ssl_context())) {\n        return false;\n    }\n    rbio = BIO_new(BIO_s_mem());\n    wbio = BIO_new(BIO_s_mem());\n    SSL_set_bio(socket->ssl, rbio, wbio);\n\n    const char *fn;\n    if (ssl_is_server) {\n        fn = \"ssl_accept\";\n        SSL_set_accept_state(socket->ssl);\n    } else {\n        fn = \"ssl_connect\";\n        SSL_set_connect_state(socket->ssl);\n    }\n\n    while (true) {\n        auto rs = SSL_do_handshake(socket->ssl);\n        if (rs == 1) {\n            break;\n        }\n        if (ssl_bio_perform(rs, fn)) {\n            continue;\n        } else {\n            return false;\n        }\n    }\n\n    if (ssl_context->verify_peer) {\n        if (!ssl_verify(ssl_context->allow_self_signed)) {\n            return false;\n        }\n    }\n    ssl_handshaked = true;\n\n    return true;\n}\n\nssize_t UringSocket::ssl_readv(network::IOVector *io_vector) {\n    ssize_t retval, total_bytes = 0;\n\n    do {\n        retval = ssl_recv(io_vector->get_iterator()->iov_base, io_vector->get_iterator()->iov_len);\n        total_bytes += retval > 0 ? retval : 0;\n        io_vector->update_iterator(retval);\n    } while (retval > 0 && io_vector->get_remain_count() > 0);\n\n    return total_bytes > 0 ? total_bytes : retval;\n}\n\nssize_t UringSocket::ssl_writev(network::IOVector *io_vector) {\n    ssize_t retval, total_bytes = 0;\n\n    do {\n        retval = ssl_send(io_vector->get_iterator()->iov_base, io_vector->get_iterator()->iov_len);\n        total_bytes += retval > 0 ? retval : 0;\n        io_vector->update_iterator(retval);\n    } while (retval > 0 && io_vector->get_remain_count() > 0);\n\n    return total_bytes > 0 ? total_bytes : retval;\n}\n\nssize_t UringSocket::ssl_recv(void *_buf, size_t _n) {\n    while (true) {\n        int n = SSL_read(socket->ssl, _buf, _n);\n        if (!ssl_bio_prepare()) {\n            return -1;\n        }\n        if (n > 0) {\n            return n;\n        }\n        if (!ssl_bio_perform(n, \"ssl_recv\")) {\n            return -1;\n        }\n    }\n}\n\nssize_t UringSocket::ssl_send(const void *_buf, size_t _n) {\n    while (true) {\n        int n = SSL_write(socket->ssl, _buf, _n);\n        if (!ssl_bio_prepare()) {\n            return -1;\n        }\n        if (n > 0) {\n            return n;\n        }\n        if (!ssl_bio_perform(n, \"ssl_send\")) {\n            return -1;\n        }\n    }\n}\n\nssize_t UringSocket::ssl_sendfile(const File &file, off_t *offset, size_t size) {\n    char buf[SW_BUFFER_SIZE_BIG];\n    size_t total = 0;\n\n    while (total < size) {\n        ssize_t readn = size > sizeof(buf) ? sizeof(buf) : size;\n        ssize_t n = file.pread(buf, readn, *offset);\n        if (n <= 0) {\n            swoole_sys_warning(\"pread() failed\");\n            break;\n        }\n\n        ssize_t ret = ssl_send(buf, n);\n        if (ret > 0) {\n            *offset += ret;\n            total += ret;\n            swoole_trace_log(SW_TRACE_REACTOR, \"fd=%d, readn=%ld, n=%ld, ret=%ld\", socket->get_fd(), readn, n, ret);\n        } else if (ret == 0) {\n            return total;\n        } else {\n            switch (socket->catch_write_error(errno)) {\n            case SW_ERROR:\n                swoole_sys_warning(\"write() failed\");\n                return total;\n            case SW_CLOSE:\n                return total;\n            case SW_WAIT:\n            default:\n                break;\n            }\n        }\n    }\n\n    return total;\n}\n};  // namespace coroutine\n};  // namespace swoole\n#endif\n"
  },
  {
    "path": "src/lock/barrier.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_lock.h\"\n\nnamespace swoole {\n\n#define BARRIER_USEC 10000\n\nvoid Barrier::init(bool shared, int count) {\n#ifdef SW_USE_PTHREAD_BARRIER\n    if (shared) {\n        pthread_barrierattr_setpshared(&barrier_attr_, PTHREAD_PROCESS_SHARED);\n        pthread_barrier_init(&barrier_, &barrier_attr_, count);\n    } else {\n        pthread_barrier_init(&barrier_, nullptr, count);\n    }\n    shared_ = shared;\n#else\n    barrier_ = 0;\n    count_ = count;\n#endif\n}\n\nvoid Barrier::wait() {\n#ifdef SW_USE_PTHREAD_BARRIER\n    pthread_barrier_wait(&barrier_);\n#else\n    sw_atomic_add_fetch(&barrier_, 1);\n    SW_LOOP {\n        if (barrier_ == count_) {\n            break;\n        }\n        usleep(BARRIER_USEC);\n        sw_atomic_memory_barrier();\n    }\n#endif\n}\n\nvoid Barrier::destroy() {\n#ifdef SW_USE_PTHREAD_BARRIER\n    pthread_barrier_destroy(&barrier_);\n    if (shared_) {\n        pthread_barrierattr_destroy(&barrier_attr_);\n    }\n#endif\n}\n\n};  // namespace swoole\n"
  },
  {
    "path": "src/lock/coroutine_lock.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: NathanFreeman  <mariasocute@163.com>                         |\n  +----------------------------------------------------------------------+\n */\n\n#include \"swoole_lock.h\"\n\n#ifdef HAVE_IOURING_FUTEX\n#include \"swoole_iouring.h\"\n#else\n#include \"swoole_coroutine_system.h\"\nusing swoole::coroutine::System;\n#endif\n\nnamespace swoole {\nCoroutineLock::CoroutineLock(bool shared) : Lock(COROUTINE_LOCK, shared) {\n    if (shared) {\n        value = (sw_atomic_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_t));\n    } else {\n        value = new sw_atomic_t;\n    }\n    *value = 0;\n}\n\nCoroutineLock::~CoroutineLock() {\n    if (shared_) {\n        sw_mem_pool()->free((void *) value);\n    } else {\n        delete value;\n    }\n    value = nullptr;\n}\n\nint CoroutineLock::lock(int operation, int _) {\n    if (operation & LOCK_NB) {\n        return lock_impl(false);\n    } else {\n        return lock_impl(true);\n    }\n}\n\nint CoroutineLock::unlock() {\n    Coroutine *current_coroutine = Coroutine::get_current();\n    if (current_coroutine == nullptr) {\n        swoole_warning(\"The coroutine lock can only be used in a coroutine environment\");\n        return SW_ERROR_CO_OUT_OF_COROUTINE;\n    }\n\n    if (*value == 0) {\n        return 0;\n    }\n\n    *value = 0;\n    cid = 0;\n    coroutine = nullptr;\n\n#ifdef HAVE_IOURING_FUTEX\n    return Iouring::futex_wakeup((uint32_t *) value) >= 0 ? 0 : errno;\n#else\n    return 0;\n#endif\n}\n\nint CoroutineLock::lock_impl(bool blocking) {\n    Coroutine *current_coroutine = Coroutine::get_current();\n    if (current_coroutine == nullptr) {\n        swoole_warning(\"The coroutine lock can only be used in a coroutine environment\");\n        return SW_ERROR_CO_OUT_OF_COROUTINE;\n    }\n\n    if (current_coroutine == static_cast<Coroutine *>(coroutine) && current_coroutine->get_cid() == cid) {\n        return 0;\n    }\n\n    int result = 0;\n#ifndef HAVE_IOURING_FUTEX\n    double second = SW_FILE_LOCK_DEFAULT_SECOND;\n#endif\n\n    while (true) {\n        if (sw_atomic_cmp_set(value, 0, 1)) {\n            break;\n        }\n\n        if (!blocking) {\n            return EBUSY;\n        }\n\n#ifdef HAVE_IOURING_FUTEX\n        result = Iouring::futex_wait((uint32_t *) value);\n        if (result != 0) {\n            return errno;\n        }\n#else\n        if (System::sleep(second) != SW_OK) {\n            return SW_ERROR_CO_CANCELED;\n        }\n        // Limit the maximum waiting time to 0.1 second; otherwise, this exponential time waiting\n        // will make it increasingly difficult to acquire the lock.\n        second = (second >= SW_FILE_LOCK_MAX_SECOND) ? SW_FILE_LOCK_DEFAULT_SECOND\n                                                     : SW_MIN(second * 2, SW_FILE_LOCK_MAX_SECOND);\n#endif\n    }\n\n    cid = current_coroutine->get_cid();\n    coroutine = (void *) current_coroutine;\n    return result;\n}\n}  // namespace swoole\n"
  },
  {
    "path": "src/lock/mutex.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_lock.h\"\n\nnamespace swoole {\n\nstruct MutexImpl {\n    pthread_mutex_t lock_;\n    pthread_mutexattr_t attr_;\n};\n\nMutex::Mutex(bool shared) : Lock(MUTEX, shared) {\n    if (shared) {\n        impl = (MutexImpl *) sw_mem_pool()->alloc(sizeof(*impl));\n        if (impl == nullptr) {\n            throw std::bad_alloc();\n        }\n    } else {\n        impl = new MutexImpl();\n    }\n\n    pthread_mutexattr_init(&impl->attr_);\n    if (shared) {\n        pthread_mutexattr_setpshared(&impl->attr_, PTHREAD_PROCESS_SHARED);\n    }\n    if (pthread_mutex_init(&impl->lock_, &impl->attr_) != 0) {\n        throw std::system_error(errno, std::generic_category(), \"pthread_mutex_init() failed\");\n    }\n}\n\nint Mutex::lock(int operation, int timeout_msec) {\n    if (operation & LOCK_NB) {\n        return pthread_mutex_trylock(&impl->lock_);\n    }\n    if (timeout_msec > 0) {\n#ifdef HAVE_MUTEX_TIMEDLOCK\n        timespec timeo;\n        realtime_get(&timeo);\n        realtime_add(&timeo, timeout_msec);\n        return pthread_mutex_timedlock(&impl->lock_, &timeo);\n#else\n        return sw_wait_for([this]() { return pthread_mutex_trylock(&impl->lock_) == 0; }, timeout_msec) ? 0 : ETIMEDOUT;\n#endif\n    }\n    return pthread_mutex_lock(&impl->lock_);\n}\n\nint Mutex::unlock() {\n    return pthread_mutex_unlock(&impl->lock_);\n}\n\nMutex::~Mutex() {\n    pthread_mutexattr_destroy(&impl->attr_);\n    pthread_mutex_destroy(&impl->lock_);\n    if (shared_) {\n        sw_mem_pool()->free(impl);\n    } else {\n        delete impl;\n    }\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "src/lock/rw_lock.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_lock.h\"\n\n#ifdef HAVE_RWLOCK\n\nnamespace swoole {\n\nstruct RWLockImpl {\n    pthread_rwlock_t lock_;\n    pthread_rwlockattr_t attr_;\n};\n\nRWLock::RWLock(bool shared) : Lock(RW_LOCK, shared) {\n    if (shared) {\n        impl = (RWLockImpl *) sw_mem_pool()->alloc(sizeof(*impl));\n        if (impl == nullptr) {\n            throw std::bad_alloc();\n        }\n    } else {\n        impl = new RWLockImpl();\n    }\n\n    pthread_rwlockattr_init(&impl->attr_);\n    if (shared) {\n        pthread_rwlockattr_setpshared(&impl->attr_, PTHREAD_PROCESS_SHARED);\n    }\n    if (pthread_rwlock_init(&impl->lock_, &impl->attr_) != 0) {\n        throw std::system_error(errno, std::generic_category(), \"pthread_rwlock_init() failed\");\n    }\n}\n\nstatic int rwlock_timed_lock_rd(pthread_rwlock_t *rwlock, int timeout_msec) {\n#ifdef HAVE_RWLOCK_TIMEDRDLOCK\n    timespec timeo;\n    realtime_get(&timeo);\n    realtime_add(&timeo, timeout_msec);\n    return pthread_rwlock_timedrdlock(rwlock, &timeo);\n#else\n    return sw_wait_for([rwlock]() { return pthread_rwlock_tryrdlock(rwlock) == 0; }, timeout_msec) ? 0 : ETIMEDOUT;\n#endif\n}\n\nstatic int rwlock_timed_lock_wr(pthread_rwlock_t *rwlock, int timeout_msec) {\n#ifdef HAVE_RWLOCK_TIMEDRDLOCK\n    timespec timeo;\n    realtime_get(&timeo);\n    realtime_add(&timeo, timeout_msec);\n    return pthread_rwlock_timedwrlock(rwlock, &timeo);\n#else\n    return sw_wait_for([rwlock]() { return pthread_rwlock_trywrlock(rwlock) == 0; }, timeout_msec) ? 0 : ETIMEDOUT;\n#endif\n}\n\nint RWLock::lock(int operation, int timeout_msec) {\n    if (operation & LOCK_NB) {\n        if (operation & LOCK_SH) {\n            return pthread_rwlock_tryrdlock(&impl->lock_);\n        } else {\n            return pthread_rwlock_trywrlock(&impl->lock_);\n        }\n    } else {\n        if (timeout_msec > 0) {\n            if (operation & LOCK_SH) {\n                return rwlock_timed_lock_rd(&impl->lock_, timeout_msec);\n            } else {\n                return rwlock_timed_lock_wr(&impl->lock_, timeout_msec);\n            }\n        } else {\n            if (operation & LOCK_SH) {\n                return pthread_rwlock_rdlock(&impl->lock_);\n            } else {\n                return pthread_rwlock_wrlock(&impl->lock_);\n            }\n        }\n    }\n}\n\nint RWLock::unlock() {\n    return pthread_rwlock_unlock(&impl->lock_);\n}\n\nRWLock::~RWLock() {\n    pthread_rwlockattr_destroy(&impl->attr_);\n    pthread_rwlock_destroy(&impl->lock_);\n    if (shared_) {\n        sw_mem_pool()->free(impl);\n    } else {\n        delete impl;\n    }\n}\n\n}  // namespace swoole\n#endif\n"
  },
  {
    "path": "src/lock/spin_lock.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_lock.h\"\n\n#ifdef HAVE_SPINLOCK\nnamespace swoole {\nSpinLock::SpinLock(bool shared) : Lock(SPIN_LOCK, shared) {\n    if (shared) {\n        impl = (pthread_spinlock_t *) sw_mem_pool()->alloc(sizeof(*impl));\n        if (impl == nullptr) {\n            throw std::bad_alloc();\n        }\n    } else {\n        impl = new pthread_spinlock_t();\n    }\n\n    if (pthread_spin_init(impl, shared) != 0) {\n        throw std::system_error(errno, std::generic_category(), \"pthread_spin_init() failed\");\n    }\n}\n\nint SpinLock::lock(int operation, int timeout_msec) {\n    if (operation & LOCK_NB) {\n        return pthread_spin_trylock(impl);\n    }\n    if (timeout_msec > 0) {\n        return sw_wait_for([this]() { return pthread_spin_trylock(impl) == 0; }, timeout_msec) ? 0 : ETIMEDOUT;\n    }\n    return pthread_spin_lock(impl);\n}\n\nint SpinLock::unlock() {\n    return pthread_spin_unlock(impl);\n}\n\nSpinLock::~SpinLock() {\n    pthread_spin_destroy(impl);\n    if (shared_) {\n        sw_mem_pool()->free((void *) impl);\n    } else {\n        delete impl;\n    }\n}\n}  // namespace swoole\n#endif\n"
  },
  {
    "path": "src/memory/fixed_pool.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_memory.h\"\n\nnamespace swoole {\n\nstruct FixedPoolSlice {\n    uint8_t lock;\n    FixedPoolSlice *next;\n    FixedPoolSlice *prev;\n    char data[0];\n};\n\nstruct FixedPoolImpl {\n    void *memory;\n    size_t size;\n\n    FixedPoolSlice *head;\n    FixedPoolSlice *tail;\n\n    // total memory size\n    uint32_t slice_num;\n\n    // memory usage\n    uint32_t slice_use;\n\n    // Fixed slice size, not include the memory used by FixedPoolSlice\n    uint32_t slice_size;\n    bool shared;\n    bool allocated;\n\n    void init();\n};\n\n/**\n * create new FixedPool, random alloc/free fixed size memory\n */\nFixedPool::FixedPool(uint32_t slice_num, uint32_t slice_size, bool shared) {\n    if (slice_num < 2) {\n        throw Exception(SW_ERROR_INVALID_PARAMS);\n    }\n    slice_size = SW_MEM_ALIGNED_SIZE(slice_size);\n    size_t size = slice_num * (sizeof(FixedPoolSlice) + slice_size);\n    size_t alloc_size = size + sizeof(*impl);\n    void *memory = shared ? ::sw_shm_malloc(alloc_size) : ::sw_malloc(alloc_size);\n    if (!memory) {\n        throw std::bad_alloc();\n    }\n\n    impl = static_cast<FixedPoolImpl *>(memory);\n    memory = static_cast<char *>(memory) + sizeof(*impl);\n    sw_memset_zero(impl, sizeof(*impl));\n\n    impl->shared = shared;\n    impl->slice_num = slice_num;\n    impl->slice_size = slice_size;\n    impl->size = size;\n    impl->memory = memory;\n    impl->allocated = true;\n    impl->init();\n}\n\n/**\n * create new FixedPool, Using the given memory\n */\nFixedPool::FixedPool(uint32_t slice_size, void *memory, size_t size, bool shared) {\n    impl = (FixedPoolImpl *) memory;\n    memory = (char *) memory + sizeof(*impl);\n    sw_memset_zero(impl, sizeof(*impl));\n    impl->shared = shared;\n    impl->slice_size = slice_size;\n    impl->size = size - sizeof(*impl);\n    uint32_t slice_num = impl->size / (slice_size + sizeof(FixedPoolSlice));\n    if (slice_num < 2) {\n        throw Exception(SW_ERROR_INVALID_PARAMS);\n    }\n    impl->slice_num = slice_num;\n    impl->memory = memory;\n    impl->allocated = false;\n    impl->init();\n}\n\nsize_t FixedPool::sizeof_struct_slice() {\n    return sizeof(FixedPoolSlice);\n}\n\nsize_t FixedPool::sizeof_struct_impl() {\n    return sizeof(FixedPoolImpl);\n}\n\n/**\n * linked list\n */\nvoid FixedPoolImpl::init() {\n    void *cur = memory;\n    void *max = (char *) memory + size;\n    do {\n        auto *slice = static_cast<FixedPoolSlice *>(cur);\n        sw_memset_zero(slice, sizeof(FixedPoolSlice));\n\n        if (head != nullptr) {\n            head->prev = slice;\n            slice->next = head;\n        } else {\n            tail = slice;\n        }\n\n        head = slice;\n        cur = (char *) cur + (sizeof(FixedPoolSlice) + slice_size);\n\n        if (cur < max) {\n            slice->prev = static_cast<FixedPoolSlice *>(cur);\n        } else {\n            slice->prev = nullptr;\n            break;\n        }\n\n    } while (true);\n}\n\nuint32_t FixedPool::get_number_of_spare_slice() const {\n    return impl->slice_num - impl->slice_use;\n}\n\nuint32_t FixedPool::get_number_of_total_slice() const {\n    return impl->slice_num;\n}\n\nuint32_t FixedPool::get_slice_size() const {\n    return impl->slice_size;\n}\n\nvoid *FixedPool::alloc(uint32_t size) {\n    FixedPoolSlice *slice = impl->head;\n    if (slice->lock) {\n        swoole_set_last_error(SW_ERROR_MALLOC_FAIL);\n        assert(get_number_of_spare_slice() == 0);\n        return nullptr;\n    }\n\n    slice->lock = 1;\n    impl->slice_use++;\n\n    // move next slice to head (idle list)\n    impl->head = slice->next;\n    impl->head->prev = nullptr;\n\n    // move this slice to tail (busy list)\n    impl->tail->next = slice;\n    slice->next = nullptr;\n    slice->prev = impl->tail;\n    impl->tail = slice;\n\n    return slice->data;\n}\n\nvoid FixedPool::free(void *ptr) {\n    auto *slice = reinterpret_cast<FixedPoolSlice *>(static_cast<char *>(ptr) - sizeof(FixedPoolSlice));\n\n    assert(ptr > impl->memory && static_cast<char *>(ptr) < static_cast<char *>(impl->memory) + impl->size);\n    assert(slice->lock == 1);\n\n    impl->slice_use--;\n    slice->lock = 0;\n\n    if (slice == impl->head) {\n        return;\n    }\n\n    if (slice == impl->tail) {\n        slice->prev->next = nullptr;\n        impl->tail = slice->prev;\n    } else {\n        slice->prev->next = slice->next;\n        slice->next->prev = slice->prev;\n    }\n\n    // move slice to head (idle)\n    slice->prev = nullptr;\n    slice->next = impl->head;\n    impl->head->prev = slice;\n    impl->head = slice;\n}\n\nFixedPool::~FixedPool() {\n    if (!impl->allocated) {\n        return;\n    }\n    if (impl->shared) {\n        ::sw_shm_free(impl);\n    } else {\n        ::sw_free(impl);\n    }\n}\n\nvoid FixedPool::debug(int max_lines) const {\n    int line = 0;\n    FixedPoolSlice *slice = impl->head;\n\n    printf(\"===============================%s=================================\\n\", __FUNCTION__);\n    while (slice != nullptr) {\n        if (slice->next == slice) {\n            printf(\"-------------------@@@@@@@@@@@@@@@@@@@@@@----------------\\n\");\n        }\n\n        printf(\"#%d\\t\", line);\n        printf(\"slice[%p]\\t\", slice);\n        printf(\"prev=%p\\t\", slice->prev);\n        printf(\"next=%p\\t\", slice->next);\n        printf(\"tag=%d\\t\", slice->lock);\n        printf(\"data=%p\\n\", slice->data);\n\n        slice = slice->next;\n        if (line++ > max_lines) {\n            break;\n        }\n    }\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "src/memory/global_memory.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_memory.h\"\n\n#include <vector>\n#include <mutex>\n\n#define SW_MIN_PAGE_SIZE 4096\n\nnamespace swoole {\n\nstruct GlobalMemoryImpl {\n    bool shared;\n    uint32_t pagesize;\n    std::mutex lock;\n    std::vector<char *> pages;\n    uint32_t alloc_offset = 0;\n    pid_t create_pid;\n\n    GlobalMemoryImpl(uint32_t _pagesize, bool _shared);\n    ~GlobalMemoryImpl();\n    char *new_page();\n};\n\nstruct MemoryBlock {\n    uint32_t size;\n    uint32_t reserved;\n    char memory[0];\n};\n\n/**\n * After the memory is allocated,\n * it will not be released until it is recycled by OS when the process exits\n */\nGlobalMemory::GlobalMemory(uint32_t pagesize, bool shared) {\n    assert(pagesize >= SW_MIN_PAGE_SIZE);\n    impl = new GlobalMemoryImpl(pagesize, shared);\n}\n\nGlobalMemoryImpl::GlobalMemoryImpl(uint32_t _pagesize, bool _shared) {\n    shared = _shared;\n    pagesize = SW_MEM_ALIGNED_SIZE_EX(_pagesize, swoole_pagesize());\n    create_pid = getpid();\n\n    if (new_page() == nullptr) {\n        throw std::bad_alloc();\n    }\n}\n\nGlobalMemoryImpl::~GlobalMemoryImpl() {\n    for (auto page : pages) {\n        shared ? ::sw_shm_free(page) : ::sw_free(page);\n    }\n}\n\nchar *GlobalMemoryImpl::new_page() {\n    auto page = static_cast<char *>(shared ? sw_shm_malloc(pagesize) : sw_malloc(pagesize));\n    if (page == nullptr) {\n        return nullptr;\n    }\n\n    pages.push_back(page);\n    alloc_offset = 0;\n\n    return page;\n}\n\n/**\n * The returned memory must be initialized to 0\n */\nvoid *GlobalMemory::alloc(uint32_t size) {\n    MemoryBlock *block;\n    size = SW_MEM_ALIGNED_SIZE(size);\n    uint32_t alloc_size = sizeof(*block) + size;\n    std::unique_lock<std::mutex> lock(impl->lock);\n\n    if (alloc_size > impl->pagesize) {\n        swoole_warning(\"failed to alloc %d bytes, exceed the maximum size[%d]\", size, impl->pagesize);\n        return nullptr;\n    }\n\n    if (impl->shared and impl->create_pid != getpid()) {\n        GlobalMemoryImpl *old_impl = impl;\n        impl = new GlobalMemoryImpl(old_impl->pagesize, old_impl->shared);\n    }\n\n    swoole_trace(\"alloc_size=%u, size=%u\", alloc_size, size);\n\n    if (impl->alloc_offset + alloc_size > impl->pagesize) {\n        char *page = impl->new_page();\n        if (page == nullptr) {\n            swoole_warning(\"alloc memory error\");\n            return nullptr;\n        }\n    }\n\n    block = reinterpret_cast<MemoryBlock *>(impl->pages.back() + impl->alloc_offset);\n    impl->alloc_offset += alloc_size;\n\n    block->size = size;\n\n    sw_memset_zero(block->memory, size);\n    return block->memory;\n}\n\nvoid GlobalMemory::free(void *ptr) {}\n\nsize_t GlobalMemory::capacity() const {\n    return impl->pagesize - impl->alloc_offset;\n}\n\nsize_t GlobalMemory::get_memory_size() const {\n    return impl->pagesize * impl->pages.size();\n}\n\nGlobalMemory::~GlobalMemory() {\n    delete impl;\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "src/memory/ring_buffer.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_memory.h\"\n\nnamespace swoole {\nstruct RingBufferItem;\nstruct RingBufferImpl {\n    void *memory;\n    bool shared;\n    uint8_t status;\n    uint32_t size;\n    uint32_t alloc_offset;\n    uint32_t collect_offset;\n    uint32_t alloc_count;\n    sw_atomic_t free_count;\n\n    void collect();\n    RingBufferItem *get_item(uint32_t offset) const;\n};\n\nstruct RingBufferItem {\n    uint16_t lock;\n    uint16_t index;\n    uint32_t length;\n    char data[0];\n};\n\n#ifdef SW_RINGBUFFER_DEBUG\nstatic void swRingBuffer_print(swRingBuffer *object, char *prefix);\n\nstatic void swRingBuffer_print(swRingBuffer *object, char *prefix) {\n    printf(\"%s: size=%d, status=%d, alloc_count=%d, free_count=%d, offset=%d, next_offset=%d\\n\",\n           prefix,\n           impl->size,\n           impl->status,\n           impl->alloc_count,\n           impl->free_count,\n           impl->alloc_offset,\n           impl->collect_offset);\n}\n#endif\n\nRingBuffer::RingBuffer(uint32_t size, bool shared) {\n    size = SW_MEM_ALIGNED_SIZE(size);\n    void *mem = (shared == 1) ? sw_shm_malloc(size) : sw_malloc(size);\n    if (mem == nullptr) {\n        throw std::bad_alloc();\n    }\n\n    impl = static_cast<RingBufferImpl *>(mem);\n    mem = static_cast<char *>(mem) + sizeof(*impl);\n    sw_memset_zero(impl, sizeof(*impl));\n\n    impl->size = size - sizeof(*impl);\n    impl->shared = shared;\n    impl->memory = mem;\n\n    swoole_debug(\"memory: ptr=%p\", mem);\n}\n\nRingBufferItem *RingBufferImpl::get_item(uint32_t offset) const {\n    return reinterpret_cast<RingBufferItem *>(static_cast<char *>(memory) + offset);\n}\n\nvoid RingBufferImpl::collect() {\n    for (uint32_t i = 0; i < free_count; i++) {\n        const auto *item = get_item(collect_offset);\n        if (item->lock == 0) {\n            const uint32_t n_size = item->length + sizeof(RingBufferItem);\n            collect_offset += n_size;\n            if (collect_offset + sizeof(RingBufferItem) > size || collect_offset >= size) {\n                collect_offset = 0;\n                status = 0;\n            }\n            sw_atomic_fetch_sub(&free_count, 1);\n        } else {\n            break;\n        }\n    }\n}\n\nvoid *RingBuffer::alloc(uint32_t size) {\n    assert(size > 0);\n\n    RingBufferItem *item;\n    uint32_t capacity;\n\n    size = SW_MEM_ALIGNED_SIZE(size);\n    uint32_t alloc_size = size + sizeof(RingBufferItem);\n\n    if (impl->free_count > 0) {\n        impl->collect();\n    }\n\n    if (impl->status == 0) {\n        if (impl->alloc_offset + alloc_size >= (impl->size - sizeof(RingBufferItem))) {\n            uint32_t skip_n = impl->size - impl->alloc_offset;\n            if (skip_n >= sizeof(RingBufferItem)) {\n                item = impl->get_item(impl->alloc_offset);\n                item->lock = 0;\n                item->length = skip_n - sizeof(RingBufferItem);\n                sw_atomic_t *free_count = &impl->free_count;\n                sw_atomic_fetch_add(free_count, 1);\n            }\n            impl->alloc_offset = 0;\n            impl->status = 1;\n            capacity = impl->collect_offset - impl->alloc_offset;\n        } else {\n            capacity = impl->size - impl->alloc_offset;\n        }\n    } else {\n        capacity = impl->collect_offset - impl->alloc_offset;\n    }\n\n    if (capacity < alloc_size) {\n        return nullptr;\n    }\n\n    item = impl->get_item(impl->alloc_offset);\n    item->lock = 1;\n    item->length = size;\n    item->index = impl->alloc_count;\n\n    impl->alloc_offset += alloc_size;\n    impl->alloc_count++;\n\n    swoole_debug(\"alloc: ptr=%p\", (void *) (item->data - (char *) impl->memory));\n\n    return item->data;\n}\n\nvoid RingBuffer::free(void *ptr) {\n    auto *item = reinterpret_cast<RingBufferItem *>(static_cast<char *>(ptr) - sizeof(RingBufferItem));\n\n    assert(ptr >= impl->memory);\n    assert(static_cast<char *>(ptr) <= static_cast<char *>(impl->memory) + impl->size);\n    assert(item->lock == 1);\n\n    if (item->lock != 1) {\n        swoole_debug(\"invalid free: index=%d, ptr=%p\", item->index, (void *) (item->data - (char *) impl->memory));\n    } else {\n        item->lock = 0;\n    }\n\n    swoole_debug(\"free: ptr=%p\", (void *) (item->data - (char *) impl->memory));\n\n    sw_atomic_t *free_count = &impl->free_count;\n    sw_atomic_fetch_add(free_count, 1);\n}\n\nRingBuffer::~RingBuffer() {\n    if (impl->shared) {\n        sw_shm_free(impl);\n    } else {\n        sw_free(impl);\n    }\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "src/memory/shared_memory.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_memory.h\"\n\n#include <sys/mman.h>\n\n#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)\n#define MAP_ANONYMOUS MAP_ANON\n#endif\n\nnamespace swoole {\n\nstruct SharedMemory {\n    size_t size_;\n\n    static void *alloc(size_t size);\n    static void free(void *ptr);\n\n    static SharedMemory *fetch_object(void *ptr) {\n        return reinterpret_cast<SharedMemory *>(static_cast<char *>(ptr) - sizeof(SharedMemory));\n    }\n};\n\nvoid *SharedMemory::alloc(size_t size) {\n    void *mem;\n    int tmpfd = -1;\n    int flags = MAP_SHARED;\n    SharedMemory object;\n\n    size = SW_MEM_ALIGNED_SIZE(size);\n    size += sizeof(SharedMemory);\n\n#ifdef MAP_ANONYMOUS\n    flags |= MAP_ANONYMOUS;\n#else\n    File zerofile(\"/dev/zero\", O_RDWR);\n    if (!zerofile.ready()) {\n        return nullptr;\n    }\n    tmpfd = zerofile.get_fd();\n#endif\n    mem = mmap(nullptr, size, PROT_READ | PROT_WRITE, flags, tmpfd, 0);\n#ifdef MAP_FAILED\n    if (mem == MAP_FAILED)\n#else\n    if (!mem)\n#endif\n    {\n        swoole_sys_warning(\"mmap(%lu) failed\", size);\n        return nullptr;\n    } else {\n        object.size_ = size;\n        memcpy(mem, &object, sizeof(object));\n        return static_cast<char *>(mem) + sizeof(object);\n    }\n}\n\nvoid SharedMemory::free(void *ptr) {\n    SharedMemory *object = SharedMemory::fetch_object(ptr);\n    size_t size = object->size_;\n    if (munmap(object, size) < 0) {\n        swoole_sys_warning(\"munmap(%p, %lu) failed\", object, size);\n    }\n}\n\n}  // namespace swoole\n\nusing swoole::SharedMemory;\n\nvoid *sw_shm_malloc(size_t size) {\n    return SharedMemory::alloc(size);\n}\n\nvoid *sw_shm_calloc(size_t num, size_t _size) {\n    return SharedMemory::alloc(num * _size);\n}\n\nint sw_shm_protect(void *ptr, int flags) {\n    SharedMemory *object = SharedMemory::fetch_object(ptr);\n    return mprotect(object, object->size_, flags);\n}\n\nvoid sw_shm_free(void *ptr) {\n    SharedMemory::free(ptr);\n}\n\nvoid *sw_shm_realloc(void *ptr, size_t new_size) {\n    SharedMemory *object = SharedMemory::fetch_object(ptr);\n    void *new_ptr = sw_shm_malloc(new_size);\n    if (new_ptr == nullptr) {\n        return nullptr;\n    }\n    memcpy(new_ptr, ptr, object->size_);\n    SharedMemory::free(ptr);\n    return new_ptr;\n}\n"
  },
  {
    "path": "src/memory/table.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_table.h\"\n#include \"swoole_hash.h\"\n#include \"swoole_util.h\"\n\n#include <thread>\n\nnamespace swoole {\n\nTable *Table::make(uint32_t rows_size, float conflict_proportion) {\n    if (rows_size >= SW_TABLE_MAX_ROW_SIZE) {\n        rows_size = SW_TABLE_MAX_ROW_SIZE;\n    } else {\n        uint32_t i = 6;\n        while ((1U << i) < rows_size) {\n            i++;\n        }\n        rows_size = 1 << i;\n    }\n\n    if (conflict_proportion > 1.0) {\n        conflict_proportion = 1.0;\n    } else if (conflict_proportion < SW_TABLE_CONFLICT_PROPORTION) {\n        conflict_proportion = SW_TABLE_CONFLICT_PROPORTION;\n    }\n\n    auto table = static_cast<Table *>(sw_mem_pool()->alloc(sizeof(Table)));\n    if (table == nullptr) {\n        return nullptr;\n    }\n    table->mutex = new Mutex(true);\n    table->iterator = nullptr;\n    table->column_map = new std::unordered_map<std::string, TableColumn *>;\n    table->column_list = new std::vector<TableColumn *>;\n    table->size = rows_size;\n    table->mask = rows_size - 1;\n    table->conflict_proportion = conflict_proportion;\n#ifdef SW_TABLE_USE_PHP_HASH\n    table->hash_func = swoole_hash_php;\n#else\n    table->hash_func = swoole_hash_austin;\n#endif\n\n    return table;\n}\n\nbool Table::add_column(const std::string &_name, enum TableColumn::Type _type, size_t _size) {\n    if (_type < TableColumn::TYPE_INT || _type > TableColumn::TYPE_STRING) {\n        swoole_warning(\"unknown column type\");\n        return false;\n    }\n\n    auto col = new TableColumn(_name, _type, _size);\n    col->index = item_size;\n    item_size += col->size;\n    column_map->emplace(_name, col);\n    column_list->push_back(col);\n\n    return true;\n}\n\nTableColumn *Table::get_column(const std::string &key) const {\n    auto i = column_map->find(key);\n    if (i == column_map->end()) {\n        return nullptr;\n    } else {\n        return i->second;\n    }\n}\n\nbool Table::exists(const char *key, uint16_t keylen) const {\n    TableRow *_rowlock = nullptr;\n    const TableRow *row = get(key, keylen, &_rowlock);\n    _rowlock->unlock();\n    return row != nullptr;\n}\n\nTableIterator::TableIterator(size_t row_size) {\n    current_ = (TableRow *) sw_malloc(row_size);\n    if (!current_) {\n        throw std::bad_alloc();\n    }\n    mutex_ = new Mutex(true);\n    row_memory_size_ = row_size;\n    reset();\n}\n\nvoid TableIterator::reset() {\n    absolute_index = 0;\n    collision_index = 0;\n    sw_memset_zero(current_, row_memory_size_);\n}\n\nTableIterator::~TableIterator() {\n    if (current_) {\n        sw_free(current_);\n    }\n    delete mutex_;\n}\n\nsize_t Table::calc_memory_size() const {\n    /**\n     * table size + conflict size\n     */\n    size_t _row_num = size * (1 + conflict_proportion);\n\n    /*\n     * header + data\n     */\n    size_t _row_memory_size = sizeof(TableRow) + item_size;\n\n    /**\n     * row data & header\n     */\n    size_t _memory_size = _row_num * _row_memory_size;\n\n    /**\n     * memory pool for conflict rows\n     */\n    _memory_size += FixedPool::sizeof_struct_impl() + ((_row_num - size) * FixedPool::sizeof_struct_slice());\n\n    /**\n     * for iterator, Iterate through all the elements\n     */\n    _memory_size += size * sizeof(TableRow *);\n\n    swoole_trace(\"_memory_size=%lu, _row_num=%lu, _row_memory_size=%lu\", _memory_size, _row_num, _row_memory_size);\n\n    return _memory_size;\n}\n\nsize_t Table::get_memory_size() const {\n    return memory_size;\n}\n\nuint32_t Table::get_available_slice_num() const {\n    lock();\n    uint32_t num = pool->get_number_of_spare_slice();\n    unlock();\n    return num;\n}\n\nuint32_t Table::get_total_slice_num() const {\n    return pool->get_number_of_total_slice();\n}\n\nbool Table::create() {\n    if (created) {\n        return false;\n    }\n\n    size_t _memory_size = calc_memory_size();\n    size_t _row_memory_size = sizeof(TableRow) + item_size;\n\n    void *_memory = sw_shm_malloc(_memory_size);\n    if (_memory == nullptr) {\n        return false;\n    }\n    memory = _memory;\n\n    rows = static_cast<TableRow **>(_memory);\n    _memory = static_cast<char *>(_memory) + size * sizeof(TableRow *);\n    _memory_size -= size * sizeof(TableRow *);\n\n    for (size_t i = 0; i < size; i++) {\n        rows[i] = reinterpret_cast<TableRow *>(static_cast<char *>(_memory) + (_row_memory_size * i));\n        memset(rows[i], 0, sizeof(TableRow));\n    }\n\n    _memory = static_cast<char *>(_memory) + _row_memory_size * size;\n    _memory_size -= _row_memory_size * size;\n    pool = new FixedPool(_row_memory_size, _memory, _memory_size, true);\n    iterator = new TableIterator(_row_memory_size);\n    memory_size = _memory_size;\n    created = true;\n\n    return true;\n}\n\nvoid Table::destroy() {\n#ifdef SW_TABLE_DEBUG\n    printf(\"swoole_table: size=%ld, conflict_count=%d, conflict_max_level=%d, insert_count=%d\\n\",\n           size,\n           conflict_count,\n           conflict_max_level,\n           insert_count);\n#endif\n\n    auto i = column_map->begin();\n    while (i != column_map->end()) {\n        delete i->second;\n        column_map->erase(i++);\n    }\n    delete column_map;\n    delete column_list;\n    delete iterator;\n    delete pool;\n    if (memory) {\n        sw_shm_free(memory);\n    }\n    memory = nullptr;\n    delete mutex;\n    sw_mem_pool()->free(this);\n}\n\nvoid TableRow::lock() {\n    sw_atomic_t *lock = &lock_;\n    uint32_t i, n;\n    long t = 0;\n\n    while (true) {\n        if (*lock == 0 && sw_atomic_cmp_set(lock, 0, 1)) {\n        _success:\n            lock_pid = getpid();\n            return;\n        }\n        if (SW_CPU_NUM > 1) {\n            for (n = 1; n < SW_SPINLOCK_LOOP_N; n <<= 1) {\n                for (i = 0; i < n; i++) {\n                    sw_atomic_cpu_pause();\n                }\n                if (*lock == 0 && sw_atomic_cmp_set(lock, 0, 1)) {\n                    goto _success;\n                }\n            }\n        }\n        /**\n         * The process occupied by the resource no longer exists,\n         * indicating that OOM occurred during the locking process,\n         * forced to unlock\n         */\n        if (kill(lock_pid, 0) < 0 && errno == ESRCH) {\n            *lock = 1;\n            swoole_warning(\"lock process[%d] not exists, force unlock\", lock_pid);\n            goto _success;\n        }\n        /**\n         * Mark time\n         */\n        if (t == 0) {\n            t = swoole::time<std::chrono::milliseconds>(true);\n        }\n        /**\n         * The deadlock time exceeds 2 seconds (SW_TABLE_FORCE_UNLOCK_TIME),\n         * indicating that the lock process has OOM,\n         * and the PID has been reused, forcing the unlock\n         */\n        else if ((swoole::time<std::chrono::milliseconds>(true) - t) > SW_TABLE_FORCE_UNLOCK_TIME) {\n            *lock = 1;\n            swoole_warning(\"timeout, force unlock\");\n            goto _success;\n        }\n        std::this_thread::yield();\n    }\n}\n\nvoid Table::forward() const {\n    iterator->lock();\n    for (; iterator->absolute_index < size; iterator->absolute_index++) {\n        TableRow *row = get_by_index(iterator->absolute_index);\n        if (row == nullptr) {\n            continue;\n        }\n        row->lock();\n        if (row->next == nullptr) {\n            iterator->absolute_index++;\n            memcpy(iterator->current_, row, iterator->row_memory_size_);\n            row->unlock();\n            iterator->unlock();\n            return;\n        } else {\n            uint32_t i = 0;\n            TableRow *tmp_row = row;\n            for (;; i++) {\n                if (tmp_row == nullptr) {\n                    iterator->collision_index = 0;\n                    break;\n                }\n                if (i == iterator->collision_index) {\n                    iterator->collision_index++;\n                    memcpy(iterator->current_, tmp_row, iterator->row_memory_size_);\n                    row->unlock();\n                    iterator->unlock();\n                    return;\n                }\n                tmp_row = tmp_row->next;\n            }\n        }\n        row->unlock();\n    }\n    sw_memset_zero(iterator->current_, sizeof(TableRow));\n    iterator->unlock();\n}\n\nTableRow *Table::get(const char *key, uint16_t keylen, TableRow **rowlock) const {\n    check_key_length(&keylen);\n\n    TableRow *row = hash(key, keylen);\n\n    *rowlock = row;\n    row->lock();\n\n    for (;;) {\n        if (sw_mem_equal(row->key, row->key_len, key, keylen)) {\n            if (!row->active) {\n                row = nullptr;\n            }\n            break;\n        } else if (row->next == nullptr) {\n            row = nullptr;\n            break;\n        } else {\n            row = row->next;\n        }\n    }\n\n    return row;\n}\n\nTableRow *Table::set(const char *key, uint16_t keylen, TableRow **rowlock, int *out_flags) {\n    check_key_length(&keylen);\n\n    TableRow *row = hash(key, keylen);\n    *rowlock = row;\n    row->lock();\n    int _out_flags = 0;\n\n    if (row->active) {\n        uint32_t _conflict_level = 1;\n        while (!sw_mem_equal(row->key, row->key_len, key, keylen)) {\n            if (row->next == nullptr) {\n                conflict_count++;\n                if (_conflict_level > conflict_max_level) {\n                    conflict_max_level = _conflict_level;\n                }\n                TableRow *new_row = alloc_row();\n                if (!new_row) {\n                    return nullptr;\n                }\n                init_row(new_row, key, keylen);\n                _out_flags |= SW_TABLE_FLAG_NEW_ROW;\n                row->next = new_row;\n                row = new_row;\n                break;\n            } else {\n                row = row->next;\n                _out_flags |= SW_TABLE_FLAG_CONFLICT;\n                _conflict_level++;\n            }\n        }\n    } else {\n        init_row(row, key, keylen);\n        _out_flags |= SW_TABLE_FLAG_NEW_ROW;\n    }\n\n    if (out_flags) {\n        *out_flags = _out_flags;\n    }\n\n    if (_out_flags & SW_TABLE_FLAG_NEW_ROW) {\n        sw_atomic_fetch_add(&(insert_count), 1);\n    } else {\n        sw_atomic_fetch_add(&(update_count), 1);\n    }\n\n    return row;\n}\n\nbool Table::del(const char *key, uint16_t keylen) {\n    check_key_length(&keylen);\n\n    TableRow *row = hash(key, keylen);\n    // no exists\n    if (!row->active) {\n        return false;\n    }\n\n    TableRow *tmp, *prev = nullptr;\n\n    row->lock();\n    if (row->next == nullptr) {\n        if (sw_mem_equal(row->key, row->key_len, key, keylen)) {\n            row->clear();\n        } else {\n            goto _not_exists;\n        }\n    } else {\n        tmp = row;\n        while (tmp) {\n            if (sw_mem_equal(tmp->key, tmp->key_len, key, keylen)) {\n                break;\n            }\n            prev = tmp;\n            tmp = tmp->next;\n        }\n\n        if (tmp == nullptr) {\n        _not_exists:\n            row->unlock();\n\n            return false;\n        }\n\n        // when the deleting element is root, should move the first element's data to root,\n        // and remove the element from the collision list.\n        if (tmp == row) {\n            tmp = tmp->next;\n            row->next = tmp->next;\n            memcpy(row->key, tmp->key, tmp->key_len + 1);\n            row->key_len = tmp->key_len;\n            memcpy(row->data, tmp->data, item_size);\n        } else {\n            prev->next = tmp->next;\n        }\n        free_row(tmp);\n    }\n\n    sw_atomic_fetch_add(&(delete_count), 1);\n    sw_atomic_fetch_sub(&(row_num), 1);\n    row->unlock();\n\n    return true;\n}\n\nTableColumn::TableColumn(const std::string &_name, Type _type, size_t _size) {\n    index = 0;\n    name = _name;\n    type = _type;\n    switch (_type) {\n    case TYPE_INT:\n        size = sizeof(long);\n        break;\n    case TYPE_FLOAT:\n        size = sizeof(double);\n        break;\n    case TYPE_STRING:\n        size = _size + sizeof(TableStringLength);\n        break;\n    default:\n        abort();\n        break;\n    }\n}\n\nvoid TableColumn::clear(TableRow *row) const {\n    if (type == TYPE_STRING) {\n        row->set_value(this, nullptr, 0);\n    } else if (type == TYPE_FLOAT) {\n        double _value = 0;\n        row->set_value(this, &_value, 0);\n    } else {\n        long _value = 0;\n        row->set_value(this, &_value, 0);\n    }\n}\n\nvoid TableRow::set_value(const TableColumn *col, const void *value, size_t vlen) {\n    switch (col->type) {\n    case TableColumn::TYPE_INT:\n        memcpy(data + col->index, value, sizeof(long));\n        break;\n    case TableColumn::TYPE_FLOAT:\n        memcpy(data + col->index, value, sizeof(double));\n        break;\n    default:\n        if (vlen > (col->size - sizeof(TableStringLength))) {\n            swoole_warning(\"[key=%s,field=%s]string value is too long\", key, col->name.c_str());\n            vlen = col->size - sizeof(TableStringLength);\n        }\n        if (value == nullptr) {\n            vlen = 0;\n        }\n        memcpy(data + col->index, &vlen, sizeof(TableStringLength));\n        if (vlen > 0) {\n            memcpy(data + col->index + sizeof(TableStringLength), value, vlen);\n        }\n        break;\n    }\n}\n\nvoid TableRow::get_value(const TableColumn *col, double *dval) const {\n    memcpy(dval, data + col->index, sizeof(*dval));\n}\n\nvoid TableRow::get_value(const TableColumn *col, long *lval) const {\n    memcpy(lval, data + col->index, sizeof(*lval));\n}\n\nvoid TableRow::get_value(const TableColumn *col, char **value, TableStringLength *len) {\n    memcpy(len, data + col->index, sizeof(*len));\n    *value = data + col->index + sizeof(*len);\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "src/network/address.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_socket.h\"\n\n#include <regex>\n\nstatic bool IN_IS_ADDR_LOOPBACK(const in_addr *a) {\n    return a->s_addr == htonl(INADDR_LOOPBACK);\n}\n\nnamespace swoole {\nnamespace network {\nstatic thread_local char tmp_address[INET6_ADDRSTRLEN];\n\nconst char *Address::addr_str(int family, const void *addr) {\n    if (inet_ntop(family, addr, tmp_address, sizeof(tmp_address))) {\n        return tmp_address;\n    }\n    return nullptr;\n}\n\nbool Address::verify_ip(int family, const std::string &str) {\n    return inet_pton(family, str.c_str(), tmp_address) == 1;\n}\n\nbool Address::verify_port(const int port, const bool for_connect) {\n    if (port < 0 || port > 65535) {\n        return false;\n    }\n    if (for_connect && port == 0) {\n        return false;\n    }\n    return true;\n}\n\nconst char *Address::get_addr() const {\n    if (Socket::is_inet4(type)) {\n        return addr_str(AF_INET, &addr.inet_v4.sin_addr);\n    }\n    if (Socket::is_inet6(type)) {\n        return addr_str(AF_INET6, &addr.inet_v6.sin6_addr);\n    }\n    if (Socket::is_local(type)) {\n        return addr.un.sun_path;\n    }\n    return \"unknown\";\n}\n\nbool Address::empty() const {\n    return type == 0;\n}\n\nint Address::get_port() const {\n    if (Socket::is_inet4(type)) {\n        return ntohs(addr.inet_v4.sin_port);\n    }\n    if (Socket::is_inet6(type)) {\n        return ntohs(addr.inet_v6.sin6_port);\n    }\n    return 0;\n}\n\nvoid Address::set_port(int _port) {\n    if (Socket::is_inet4(type)) {\n        addr.inet_v4.sin_port = htons(_port);\n    } else if (Socket::is_inet6(type)) {\n        addr.inet_v6.sin6_port = htons(_port);\n    }\n}\n\nbool Address::assign(SocketType _type, const std::string &_host, int _port, bool _resolve_name) {\n    type = _type;\n    const char *host = _host.c_str();\n\n    if (!verify_port(_port)) {\n        swoole_set_last_error(SW_ERROR_BAD_PORT);\n        return false;\n    }\n\n    if (Socket::is_inet4(_type)) {\n        addr.inet_v4.sin_family = AF_INET;\n        addr.inet_v4.sin_port = htons(_port);\n        len = sizeof(addr.inet_v4);\n\n        if (inet_pton(AF_INET, host, &addr.inet_v4.sin_addr.s_addr) != 1) {\n            if (!_resolve_name) {\n                swoole_set_last_error(SW_ERROR_BAD_HOST_ADDR);\n                return false;\n            }\n            if (gethostbyname(AF_INET, host, reinterpret_cast<char *>(&addr.inet_v4.sin_addr)) < 0) {\n                swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n                return false;\n            }\n        }\n    } else if (Socket::is_inet6(_type)) {\n        addr.inet_v6.sin6_family = AF_INET6;\n        addr.inet_v6.sin6_port = htons(_port);\n        len = sizeof(addr.inet_v6);\n        if (inet_pton(AF_INET6, host, addr.inet_v6.sin6_addr.s6_addr) != 1) {\n            if (!_resolve_name) {\n                swoole_set_last_error(SW_ERROR_BAD_HOST_ADDR);\n                return false;\n            }\n            if (gethostbyname(AF_INET6, host, reinterpret_cast<char *>(&addr.inet_v6.sin6_addr)) < 0) {\n                swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n                return false;\n            }\n        }\n    } else if (Socket::is_local(_type)) {\n        if (_host.length() >= sizeof(addr.un.sun_path) - 1) {\n            swoole_set_last_error(SW_ERROR_NAME_TOO_LONG);\n            return false;\n        }\n        addr.un.sun_family = AF_UNIX;\n        swoole_strlcpy(addr.un.sun_path, host, sizeof(addr.un.sun_path));\n        addr.un.sun_path[sizeof(addr.un.sun_path) - 1] = 0;\n        len = sizeof(addr.un.sun_path);\n    } else {\n        swoole_set_last_error(SW_ERROR_BAD_SOCKET_TYPE);\n        return false;\n    }\n\n    return true;\n}\n\nconst char *Address::type_str(SocketType type) {\n    if (Socket::is_inet4(type)) {\n        return \"IPv4\";\n    }\n    if (Socket::is_inet6(type)) {\n        return \"IPv6\";\n    }\n    if (Socket::is_local(type)) {\n        return \"UnixSocket\";\n    }\n    return \"Unknown\";\n}\n\nbool Address::assign(const std::string &url) {\n    static const std::regex unix_pattern(R\"(^(unix|udg)://(/[^?#]+))\");\n    static const std::regex inet4_pattern(R\"(^(tcp|udp)://([^:\\[]+):(\\d+)$)\");\n    static const std::regex inet6_pattern(R\"(^(tcp|udp)://\\[([^\\]]+)\\]:(\\d+)$)\");\n    std::smatch match;\n\n    if (std::regex_match(url, match, unix_pattern)) {\n        std::string proto = match[1];\n        std::string path = match[2];\n        type = proto == \"unix\" ? SW_SOCK_UNIX_STREAM : SW_SOCK_UNIX_DGRAM;\n        return assign(type, path, 0);\n    }\n    if (std::regex_match(url, match, inet4_pattern)) {\n        std::string proto = match[1];\n        std::string host = match[2];\n        int port = std::stoi(match[3]);\n        type = proto == \"tcp\" ? SW_SOCK_TCP : SW_SOCK_UDP;\n        return assign(type, host, port);\n    }\n    if (std::regex_match(url, match, inet6_pattern)) {\n        std::string proto = match[1];\n        std::string host = match[2];\n        int port = std::stoi(match[3]);\n        type = proto == \"tcp\" ? SW_SOCK_TCP6 : SW_SOCK_UDP6;\n        return assign(type, host, port);\n    }\n    swoole_error_log(SW_LOG_NOTICE, SW_ERROR_BAD_HOST_ADDR, \"Invalid address '%s'\", url.c_str());\n    return false;\n}\n\nbool Address::is_loopback_addr() const {\n    if (Socket::is_inet4(type)) {\n        return IN_IS_ADDR_LOOPBACK(&addr.inet_v4.sin_addr);\n    }\n    if (Socket::is_inet6(type)) {\n        return IN6_IS_ADDR_LOOPBACK(&addr.inet_v6.sin6_addr);\n    }\n    return false;\n}\n}  // namespace network\n}  // namespace swoole\n"
  },
  {
    "path": "src/network/client.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_string.h\"\n#include \"swoole_socket.h\"\n#include \"swoole_timer.h\"\n#include \"swoole_protocol.h\"\n#include \"swoole_client.h\"\n#include \"swoole_proxy.h\"\n#include \"swoole_async.h\"\n\n#include <cassert>\n\nnamespace swoole {\nnamespace network {\nstatic int Client_inet_addr(Client *cli, const char *host, int port);\nstatic int Client_tcp_connect_sync(Client *cli, const char *host, int port, double _timeout, int nonblock);\nstatic int Client_tcp_connect_async(Client *cli, const char *host, int port, double timeout, int nonblock);\nstatic int Client_udp_connect(Client *cli, const char *host, int port, double _timeout, int udp_connect);\n\nstatic ssize_t Client_tcp_send_sync(Client *cli, const char *data, size_t length, int flags);\nstatic ssize_t Client_tcp_send_async(Client *cli, const char *data, size_t length, int flags);\nstatic ssize_t Client_udp_send(Client *cli, const char *data, size_t length, int flags);\n\nstatic int Client_tcp_sendfile_sync(Client *cli, const char *filename, off_t offset, size_t length);\nstatic int Client_tcp_sendfile_async(Client *cli, const char *filename, off_t offset, size_t length);\n\nstatic ssize_t Client_tcp_recv_sync(Client *cli, char *data, size_t len, int flags);\nstatic ssize_t Client_udp_recv(Client *cli, char *data, size_t len, int flags);\n\nstatic int Client_onDgramRead(Reactor *reactor, Event *event);\nstatic int Client_onStreamRead(Reactor *reactor, Event *event);\nstatic int Client_onWrite(Reactor *reactor, Event *event);\nstatic int Client_onError(Reactor *reactor, Event *event);\nstatic void Client_onTimeout(Timer *timer, TimerNode *tnode);\nstatic void Client_onResolveCompleted(AsyncEvent *event);\nstatic int Client_onPackage(const Protocol *proto, Socket *conn, const RecvData *rdata);\n\nstatic sw_inline void execute_onConnect(Client *cli) {\n    if (cli->timer) {\n        swoole_timer_del(cli->timer);\n        cli->timer = nullptr;\n    }\n    cli->onConnect(cli);\n}\n\nvoid Client::init_reactor(Reactor *reactor) {\n    reactor->set_handler(SW_FD_STREAM_CLIENT, SW_EVENT_READ, Client_onStreamRead);\n    reactor->set_handler(SW_FD_DGRAM_CLIENT, SW_EVENT_READ, Client_onDgramRead);\n    reactor->set_handler(SW_FD_STREAM_CLIENT, SW_EVENT_WRITE, Client_onWrite);\n    reactor->set_handler(SW_FD_STREAM_CLIENT, SW_EVENT_ERROR, Client_onError);\n}\n\nClient::Client(SocketType _type, bool _async) : async(_async) {\n    fd_type = Socket::is_stream(_type) ? SW_FD_STREAM_CLIENT : SW_FD_DGRAM_CLIENT;\n    socket = make_socket(_type, fd_type, (async ? SW_SOCK_NONBLOCK : 0) | SW_SOCK_CLOEXEC);\n    if (socket == nullptr) {\n        swoole_sys_warning(\"socket() failed\");\n        return;\n    }\n\n    socket->object = this;\n    input_buffer_size = SW_CLIENT_BUFFER_SIZE;\n    socket->chunk_size = SW_SEND_BUFFER_SIZE;\n\n    if (socket->is_stream()) {\n        recv_ = Client_tcp_recv_sync;\n        if (async) {\n            connect_ = Client_tcp_connect_async;\n            send_ = Client_tcp_send_async;\n            sendfile_ = Client_tcp_sendfile_async;\n        } else {\n            connect_ = Client_tcp_connect_sync;\n            send_ = Client_tcp_send_sync;\n            sendfile_ = Client_tcp_sendfile_sync;\n        }\n    } else {\n        connect_ = Client_udp_connect;\n        recv_ = Client_udp_recv;\n        send_ = Client_udp_send;\n    }\n\n    Socket::get_domain_and_type(_type, &sock_domain_, &sock_type_);\n    remote_addr.type = _type;\n\n    protocol.package_length_type = 'N';\n    protocol.package_length_size = 4;\n    protocol.package_body_offset = 0;\n    protocol.package_max_length = SW_INPUT_BUFFER_SIZE;\n    protocol.onPackage = Client_onPackage;\n}\n\nint Client::bind(const std::string &addr, int port) const {\n    if (socket->set_reuse_addr() < 0) {\n        swoole_sys_warning(\"setsockopt(%d, SO_REUSEADDR) failed\", socket->get_fd());\n    }\n    if (socket->bind(addr, port) < 0) {\n        swoole_set_last_error(errno);\n        return SW_ERR;\n    }\n    return SW_OK;\n}\n\nvoid Client::set_timeout(double timeout, TimeoutType type) const {\n    socket->set_timeout(timeout, type);\n}\n\nbool Client::has_timedout() const {\n    return socket->has_timedout();\n}\n\nvoid Client::set_socks5_proxy(const std::string &host, int port, const std::string &user, const std::string &pwd) {\n    socks5_proxy.reset(Socks5Proxy::create(get_socket_type(), host, port, user, pwd));\n}\n\nvoid Client::set_http_proxy(const std::string &host, int port, const std::string &user, const std::string &pwd) {\n    http_proxy.reset(HttpProxy::create(host, port, user, pwd));\n}\n\nint Client::sleep() {\n    int ret;\n    if (socket->events & SW_EVENT_WRITE) {\n        ret = swoole_event_set(socket, SW_EVENT_WRITE);\n    } else {\n        ret = swoole_event_del(socket);\n    }\n    if (ret == SW_OK) {\n        sleep_ = true;\n    }\n    return ret;\n}\n\nint Client::wakeup() {\n    int ret;\n    if (socket->events & SW_EVENT_WRITE) {\n        ret = swoole_event_set(socket, SW_EVENT_READ | SW_EVENT_WRITE);\n    } else {\n        ret = swoole_event_add(socket, SW_EVENT_READ);\n    }\n    if (ret == SW_OK) {\n        sleep_ = false;\n    }\n    return ret;\n}\n\nint Client::sendto(const std::string &host, int port, const char *data, size_t len) const {\n    if (!socket->is_dgram()) {\n        swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT);\n        swoole_warning(\"only supports SWOOLE_SOCK_(UDP/UDP6/UNIX_DGRAM)\");\n        return SW_ERR;\n    }\n\n    Address tmp_addr;\n    if (!tmp_addr.assign(socket->socket_type, host, port, !async)) {\n        return SW_ERR;\n    }\n\n    if (socket->sendto(tmp_addr, data, len, 0) < 0) {\n        swoole_set_last_error(errno);\n        return SW_ERR;\n    }\n\n    return SW_OK;\n}\n\nint Client::get_peer_name(Address *addr) const {\n    if (socket->is_dgram()) {\n        *addr = remote_addr;\n        return SW_OK;\n    } else {\n        return socket->get_peer_name(addr);\n    }\n}\n\nint Client::shutdown(int _how) {\n    if (!socket || closed) {\n        return SW_ERR;\n    }\n    swoole_trace_log(SW_TRACE_CLIENT, \"how=%d, fd=%d\", _how, socket->fd);\n    if (_how == SHUT_RD) {\n        if (shutdown_read || shutdown_rw || ::shutdown(socket->fd, SHUT_RD)) {\n            return SW_ERR;\n        } else {\n            shutdown_read = true;\n            return SW_OK;\n        }\n    } else if (_how == SHUT_WR) {\n        if (shutdown_write || shutdown_rw || ::shutdown(socket->fd, SHUT_WR) < 0) {\n            return SW_ERR;\n        } else {\n            shutdown_write = true;\n            return SW_OK;\n        }\n    } else if (_how == SHUT_RDWR) {\n        if (shutdown_rw || ::shutdown(socket->fd, SHUT_RDWR) < 0) {\n            return SW_ERR;\n        } else {\n            shutdown_read = true;\n            return SW_OK;\n        }\n    } else {\n        swoole_set_last_error(EINVAL);\n        return SW_ERR;\n    }\n}\n\nbool Client::socks5_handshake(const char *recv_data, size_t length) {\n    auto send_fn = [this](const char *buf, size_t len) { return send(buf, len); };\n    return socks5_proxy->handshake(recv_data, length, send_fn);\n}\n\n#ifdef SW_SUPPORT_DTLS\nvoid Client::enable_dtls() {\n    ssl_context->protocols = SW_SSL_DTLS;\n    socket->dtls = 1;\n    socket->chunk_size = SW_SSL_BUFFER_SIZE;\n    send_ = Client_tcp_send_sync;\n    recv_ = Client_tcp_recv_sync;\n}\n#endif\n\nint Client::enable_ssl_encrypt() {\n    if (ssl_context) {\n        return SW_ERR;\n    }\n    ssl_context = std::make_shared<SSLContext>();\n    open_ssl = true;\n#ifdef SW_SUPPORT_DTLS\n    if (socket->is_dgram()) {\n        enable_dtls();\n    }\n#else\n    {\n        swoole_warning(\"DTLS support require openssl-1.1 or later\");\n        return SW_ERR;\n    }\n#endif\n    return SW_OK;\n}\n\nint Client::ssl_handshake() const {\n    if (socket->ssl_state == SW_SSL_STATE_READY) {\n        return SW_ERR;\n    }\n    if (!ssl_context->ready()) {\n        ssl_context->http_v2 = http2;\n        if (!ssl_context->create()) {\n            return SW_ERR;\n        }\n    }\n    if (!socket->ssl) {\n        socket->ssl_send_ = 1;\n        if (socket->ssl_create(ssl_context.get(), SW_SSL_CLIENT) < 0) {\n            swoole_set_last_error(SW_ERROR_SSL_CREATE_SESSION_FAILED);\n            return SW_ERR;\n        }\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n        if (!ssl_context->tls_host_name.empty()) {\n            SSL_set_tlsext_host_name(socket->ssl, ssl_context->tls_host_name.c_str());\n        }\n#endif\n    }\n    if (socket->ssl_connect() < 0) {\n        return SW_ERR;\n    }\n    if (socket->ssl_state == SW_SSL_STATE_READY && ssl_context->verify_peer) {\n        if (ssl_verify(ssl_context->allow_self_signed) < 0) {\n            return SW_ERR;\n        }\n    }\n    return SW_OK;\n}\n\nint Client::ssl_verify(int allow_self_signed) const {\n    if (!socket->ssl_verify(allow_self_signed)) {\n        return SW_ERR;\n    }\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n    if (!ssl_context->tls_host_name.empty() && !socket->ssl_check_host(ssl_context->tls_host_name.c_str())) {\n        return SW_ERR;\n    }\n#endif\n    return SW_OK;\n}\n\nstatic int Client_inet_addr(Client *cli, const char *host, int port) {\n    if (!cli->host_preseted) {\n        // enable socks5 proxy\n        if (cli->socks5_proxy) {\n            cli->socks5_proxy->target_host = host;\n            cli->socks5_proxy->target_port = port;\n\n            host = cli->socks5_proxy->host.c_str();\n            port = cli->socks5_proxy->port;\n        }\n\n        // enable http proxy\n        if (cli->http_proxy) {\n            cli->http_proxy->target_host = host;\n            cli->http_proxy->target_port = port;\n\n            host = cli->http_proxy->host.c_str();\n            port = cli->http_proxy->port;\n        }\n\n        cli->server_host = host;\n        cli->server_port = port;\n\n        cli->host_preseted = true;\n    }\n\n    if (!cli->server_addr.assign(cli->socket->socket_type, host, port, !cli->async)) {\n        if (!cli->dns_completed && swoole_get_last_error() == SW_ERROR_BAD_HOST_ADDR) {\n            cli->wait_dns = true;\n        } else {\n            return SW_ERR;\n        }\n    }\n\n    return SW_OK;\n}\n\nClient::~Client() {\n    if (!socket) {\n        return;\n    }\n    assert(socket->fd != 0);\n    // remove from reactor\n    if (!closed) {\n        close();\n    }\n    // clear buffer\n    if (buffer) {\n        delete buffer;\n        buffer = nullptr;\n    }\n    if (async) {\n        // The object must be set to empty here, indicating that the client has been released in the onClose callback\n        socket->object = nullptr;\n        socket->free();\n    } else {\n        delete socket;\n    }\n}\n\n/**\n * There are two situations, 1 While maintaining the connection, the user actively calls the close function to close the\n * connection. 2 Received readable or writable, close event, and executed the close function at the underlying level\n */\nint Client::close() {\n    if (socket == nullptr || closed) {\n        return SW_ERR;\n    }\n    closed = true;\n    auto _socket = socket;\n\n    if (open_ssl && ssl_context) {\n        if (socket->ssl) {\n            socket->ssl_close();\n        }\n    }\n\n    if (socket->socket_type == SW_SOCK_UNIX_DGRAM) {\n        unlink(socket->info.addr.un.sun_path);\n    }\n    if (async) {\n        // remove from reactor\n        if (!socket->removed) {\n            swoole_event_del(socket);\n        }\n        if (timer) {\n            swoole_timer_del(timer);\n            timer = nullptr;\n        }\n        // execute `onClose` callback\n        if (active) {\n            active = false;\n            // In the `onClose` callback, the destructor may be executed and the C++ object will be released\n            onClose(this);\n        }\n    } else {\n        active = false;\n    }\n\n    if (_socket->object == nullptr) {\n        return SW_OK;\n    }\n\n    // Set `socket->fd` to -1 to prevent duplicate closure of file descriptors\n    const int fd = socket->fd;\n    socket->fd = -1;\n    swoole_trace_log(SW_TRACE_CLIENT, \"fd=%d\", fd);\n\n    return ::close(fd);\n}\n\nstatic int Client_tcp_connect_sync(Client *cli, const char *host, int port, double timeout, int nonblock) {\n    cli->set_timeout(timeout);\n    if (timeout > 0) {\n        cli->socket->set_kernel_timeout(timeout);\n    }\n\n    if (Client_inet_addr(cli, host, port) < 0) {\n        return SW_ERR;\n    }\n\n    if (nonblock) {\n        auto rc = cli->socket->connect_async(cli->server_addr);\n        if (rc == SW_READY) {\n            return SW_OK;\n        }\n        if (rc == SW_WAIT) {\n            cli->async_connect = true;\n        }\n        return SW_ERR;\n    }\n\n    int ret = cli->socket->connect_sync(cli->server_addr);\n    if (ret >= 0) {\n        cli->active = true;\n        auto recv_buf = sw_tg_buffer();\n\n        if (cli->socks5_proxy) {\n            const auto ctx = cli->socks5_proxy.get();\n            const auto len = ctx->pack_negotiate_request();\n            if (cli->send(ctx->buf, len) < 0) {\n                return SW_ERR;\n            }\n            ctx->state = SW_SOCKS5_STATE_HANDSHAKE;\n            while (true) {\n                const ssize_t n = cli->recv(recv_buf->str, recv_buf->size, 0);\n                if (n > 0 && cli->socks5_handshake(recv_buf->str, n)) {\n                    if (cli->socks5_proxy->state == SW_SOCKS5_STATE_READY) {\n                        break;\n                    }\n                    continue;\n                }\n                return SW_ERR;\n            }\n        } else if (cli->http_proxy) {\n            auto target_host = cli->get_http_proxy_host_name();\n            const size_t n_write = cli->http_proxy->pack(recv_buf, target_host);\n            if (cli->send(recv_buf->str, n_write, 0) < 0) {\n                return SW_ERR;\n            }\n            const ssize_t n_read = cli->recv(recv_buf->str, recv_buf->size, 0);\n            if (n_read <= 0) {\n                return SW_ERR;\n            }\n            recv_buf->length = n_read;\n            if (!cli->http_proxy->handshake(recv_buf)) {\n                return SW_ERR;\n            }\n        }\n\n        if (cli->open_ssl && cli->ssl_handshake() < 0) {\n            return SW_ERR;\n        }\n    }\n\n    return ret;\n}\n\nstatic int Client_dns_lookup(Client *cli) {\n    AsyncEvent ev{};\n    auto req = new GethostbynameRequest(cli->server_host, cli->sock_domain_);\n    ev.data = std::shared_ptr<AsyncRequest>(req);\n    ev.object = cli;\n    ev.handler = async::handler_gethostbyname;\n    ev.callback = Client_onResolveCompleted;\n\n    return async::dispatch(&ev) == nullptr ? SW_ERR : SW_OK;\n}\n\nstatic int Client_tcp_connect_async(Client *cli, const char *host, int port, double timeout, int nonblock) {\n    int ret;\n\n    if (!(cli->onConnect && cli->onError && cli->onClose && cli->onReceive)) {\n        swoole_warning(\"onConnect/onError/onReceive/onClose callback have not set\");\n        return SW_ERR;\n    }\n\n    cli->set_timeout(timeout);\n\n    if (!cli->buffer) {\n        cli->buffer = new String(cli->input_buffer_size);\n    }\n\n    if (cli->onBufferFull && cli->buffer_high_watermark == 0) {\n        cli->buffer_high_watermark = cli->socket->buffer_size * 0.8;\n    }\n\n    if (Client_inet_addr(cli, host, port) < 0) {\n        return SW_ERR;\n    }\n\n    if (cli->wait_dns) {\n        return Client_dns_lookup(cli);\n    }\n\n    do {\n        ret = cli->socket->connect(cli->server_addr);\n        if (ret < 0 && errno == EINTR) {\n            continue;\n        }\n        if ((ret < 0 && errno == EINPROGRESS) || ret == 0) {\n            /**\n             * A return value of 0 indicates that the connection has been successfully established,\n             * and there is no need to add a timer to check for connection timeouts.\n             */\n            if (ret < 0 && timeout > 0) {\n                cli->timer = swoole_timer_add(timeout, false, Client_onTimeout, cli);\n                if (!cli->timer) {\n                    return SW_ERR;\n                }\n            }\n            /**\n             * Regardless of whether the connection has been successfully established or is still in progress,\n             *  listen for writable events to handle the proxy and SSL handshake within those events.\n             */\n            return swoole_event_add(cli->socket, SW_EVENT_WRITE);\n        }\n        cli->active = false;\n        cli->socket->removed = 1;\n        cli->close();\n        if (cli->onError) {\n            cli->onerror_called = true;\n            cli->onError(cli);\n        }\n        break;\n    } while (true);\n\n    return ret;\n}\n\nstatic ssize_t Client_tcp_send_async(Client *cli, const char *data, size_t length, int flags) {\n    ssize_t n = length;\n    if (swoole_event_write(cli->socket, data, length) < 0) {\n        if (swoole_get_last_error() == SW_ERROR_OUTPUT_BUFFER_OVERFLOW) {\n            n = -1;\n            cli->high_watermark = true;\n        } else {\n            return SW_ERR;\n        }\n    }\n    if (cli->onBufferFull && !cli->high_watermark &&\n        cli->socket->get_out_buffer_length() >= cli->buffer_high_watermark) {\n        cli->high_watermark = true;\n        cli->onBufferFull(cli);\n    }\n    return n;\n}\n\nstatic ssize_t Client_tcp_send_sync(Client *cli, const char *data, size_t length, int flags) {\n    return cli->socket->send_sync(data, length, flags);\n}\n\nstatic int Client_tcp_sendfile_sync(Client *cli, const char *filename, off_t offset, size_t length) {\n    if (cli->socket->sendfile_sync(filename, offset, length) < 0) {\n        swoole_set_last_error(errno);\n        return SW_ERR;\n    }\n    return SW_OK;\n}\n\nstatic int Client_tcp_sendfile_async(Client *cli, const char *filename, off_t offset, size_t length) {\n    if (cli->socket->sendfile_async(filename, offset, length) < 0) {\n        swoole_set_last_error(errno);\n        return SW_ERR;\n    }\n    if (swoole_event_add_or_update(cli->socket, SW_EVENT_WRITE) == SW_ERR) {\n        return SW_ERR;\n    }\n    return SW_OK;\n}\n\nstatic ssize_t Client_tcp_recv_sync(Client *cli, char *data, size_t len, int flags) {\n    auto rv = cli->socket->recv_sync(data, len, flags);\n    /**\n     * To maintain forward compatibility, the recv system call returns EAGAIN after a timeout,\n     * while the poll() function returns 0 on timeout without setting errno,\n     * which should be set to either EAGAIN or ETIMEDOUT.\n     */\n    if (rv == -1 && swoole_get_last_error() == SW_ERROR_SOCKET_POLL_TIMEOUT) {\n        errno = EAGAIN;\n    }\n    return rv;\n}\n\nstatic int Client_udp_connect(Client *cli, const char *host, int port, double timeout, int udp_connect) {\n    cli->set_timeout(timeout);\n    if (!cli->async && timeout > 0) {\n        cli->socket->set_kernel_timeout(timeout);\n    }\n    cli->sock_flags_ = udp_connect;\n\n    if (Client_inet_addr(cli, host, port) < 0) {\n        return SW_ERR;\n    }\n\n    if (cli->async && !cli->onReceive) {\n        swoole_warning(\"`onReceive` callback have not set\");\n        return SW_ERR;\n    }\n\n    if (cli->wait_dns && cli->async) {\n        /**\n         * Domain name resolution is required, and UDP connect cannot return immediately.\n         * If an `onError` callback is not set, the caller will not receive any notification in case of a resolution\n         * failure. Therefore, it is essential to set the onError callback; otherwise, an error will be returned\n         * immediately.\n         */\n        if (!cli->onError) {\n            swoole_warning(\"`onError` callback have not set\");\n            return SW_ERR;\n        }\n        return Client_dns_lookup(cli);\n    }\n\n    cli->active = true;\n    cli->socket->set_buffer_size(Socket::default_buffer_size);\n\n    if (timeout > 0) {\n        cli->socket->set_timeout(timeout);\n    }\n\n    if (cli->socket->socket_type == SW_SOCK_UNIX_DGRAM) {\n        sockaddr_un *client_addr = &cli->socket->info.addr.un;\n        sprintf(client_addr->sun_path, \"/tmp/swoole-client.%d.%d.sock\", getpid(), cli->socket->fd);\n        client_addr->sun_family = AF_UNIX;\n        unlink(client_addr->sun_path);\n\n        if (bind(cli->socket->fd, reinterpret_cast<sockaddr *>(client_addr), sizeof(cli->socket->info.addr.un)) < 0) {\n            swoole_sys_warning(\"bind(%s) failed\", client_addr->sun_path);\n            return SW_ERR;\n        }\n    }\n\n    if (cli->open_ssl)\n#ifdef SW_SUPPORT_DTLS\n    {\n        udp_connect = 1;\n        cli->enable_dtls();\n    }\n#else\n    {\n        swoole_warning(\"DTLS support require openssl-1.1 or later\");\n        return SW_ERR;\n    }\n#endif\n\n    if (udp_connect != 1) {\n        goto _connect_ok;\n    }\n\n    if (cli->socket->connect(cli->server_addr) == 0) {\n        cli->socket->clean();\n    _connect_ok:\n        if (cli->async && cli->onConnect) {\n            if (swoole_event_add(cli->socket, SW_EVENT_READ) < 0) {\n                return SW_ERR;\n            }\n            execute_onConnect(cli);\n        }\n        if (cli->open_ssl && cli->ssl_handshake() < 0) {\n            return SW_ERR;\n        }\n        return SW_OK;\n    } else {\n        cli->active = false;\n        cli->socket->removed = 1;\n        cli->close();\n        if (cli->async && cli->onError) {\n            cli->onerror_called = true;\n            cli->onError(cli);\n        }\n        return SW_ERR;\n    }\n}\n\nstatic ssize_t Client_udp_send(Client *cli, const char *data, size_t len, int flags) {\n    ssize_t n = cli->socket->sendto(cli->server_addr, data, len, 0);\n    if (n < 0 || n < (ssize_t) len) {\n        return SW_ERR;\n    } else {\n        return n;\n    }\n}\n\nstatic ssize_t Client_udp_recv(Client *cli, char *data, size_t length, int flags) {\n    if (cli->async) {\n        return cli->socket->recvfrom_sync(data, length, flags, &cli->remote_addr);\n    } else {\n        return cli->socket->recvfrom(data, length, flags, &cli->remote_addr);\n    }\n}\n\nstatic int Client_onPackage(const Protocol *proto, Socket *conn, const RecvData *rdata) {\n    auto *cli = static_cast<Client *>(conn->object);\n    cli->onReceive(cli, rdata->data, rdata->info.len);\n    return conn->close_wait ? SW_ERR : SW_OK;\n}\n\nstatic int Client_onStreamRead(Reactor *reactor, Event *event) {\n    ssize_t n;\n    auto *cli = (Client *) event->socket->object;\n    char *buf = cli->buffer->str + cli->buffer->length;\n    ssize_t buf_size = cli->buffer->size - cli->buffer->length;\n    bool do_ssl_handshake = cli->open_ssl;\n\n    if (cli->http_proxy && cli->http_proxy->state != SW_HTTP_PROXY_STATE_READY) {\n        n = event->socket->recv(buf, buf_size, 0);\n        if (n <= 0) {\n            swoole_set_last_error(SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR);\n        _connect_fail:\n            cli->active = false;\n            cli->close();\n            if (cli->onError) {\n                cli->onError(cli);\n            }\n            return SW_OK;\n        }\n        cli->buffer->length += n;\n        if (!cli->http_proxy->handshake(cli->buffer)) {\n            swoole_set_last_error(SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR);\n            goto _connect_fail;\n        }\n        cli->http_proxy->state = SW_HTTP_PROXY_STATE_READY;\n        cli->buffer->clear();\n        if (!do_ssl_handshake) {\n            execute_onConnect(cli);\n            return SW_OK;\n        }\n    }\n\n    if (cli->socks5_proxy && cli->socks5_proxy->state != SW_SOCKS5_STATE_READY) {\n        n = event->socket->recv(buf, buf_size, 0);\n        if (n <= 0) {\n            swoole_set_last_error(SW_ERROR_SOCKS5_HANDSHAKE_FAILED);\n            goto _connect_fail;\n        }\n        cli->buffer->length += n;\n        if (!cli->socks5_handshake(buf, buf_size)) {\n            swoole_set_last_error(SW_ERROR_SOCKS5_HANDSHAKE_FAILED);\n            goto _connect_fail;\n        }\n        if (cli->socks5_proxy->state != SW_SOCKS5_STATE_READY) {\n            return SW_OK;\n        }\n        cli->buffer->clear();\n        if (!do_ssl_handshake) {\n            execute_onConnect(cli);\n            return SW_OK;\n        }\n    }\n\n    if (cli->open_ssl && cli->socket->ssl_state != SW_SSL_STATE_READY) {\n        if (cli->ssl_handshake() < 0) {\n            swoole_set_last_error(SW_ERROR_SSL_HANDSHAKE_FAILED);\n            goto _connect_fail;\n        }\n        if (cli->socket->ssl_state != SW_SSL_STATE_READY) {\n            return SW_OK;\n        } else {\n            execute_onConnect(cli);\n            return SW_OK;\n        }\n    }\n\n    if (cli->open_eof_check || cli->open_length_check) {\n        Socket *conn = cli->socket;\n        Protocol *protocol = &cli->protocol;\n\n        if (cli->open_eof_check) {\n            n = protocol->recv_with_eof_protocol(conn, cli->buffer);\n        } else {\n            n = protocol->recv_with_length_protocol(conn, cli->buffer);\n        }\n\n        if (n < 0) {\n            if (!cli->closed) {\n                cli->close();\n            }\n            return SW_OK;\n        } else {\n            if (conn->removed == 0 && cli->remove_delay) {\n                cli->sleep();\n                cli->remove_delay = false;\n            }\n            return SW_OK;\n        }\n    }\n\n    n = event->socket->recv(buf, buf_size, 0);\n    if (n < 0) {\n        switch (event->socket->catch_read_error(errno)) {\n        case SW_ERROR:\n            swoole_sys_warning(\"Read from socket[%d] failed\", event->fd);\n            return SW_OK;\n        case SW_CLOSE:\n            goto __close;\n        case SW_WAIT:\n            return SW_OK;\n        default:\n            return SW_OK;\n        }\n    } else if (n == 0) {\n    __close:\n        return cli->close();\n    } else {\n        cli->onReceive(cli, buf, n);\n        return SW_OK;\n    }\n    return SW_OK;\n}\n\nstatic int Client_onDgramRead(Reactor *reactor, Event *event) {\n    auto *cli = (Client *) event->socket->object;\n    char buffer[SW_BUFFER_SIZE_UDP];\n\n    int n = Client_udp_recv(cli, buffer, sizeof(buffer), 0);\n    if (n < 0) {\n        return SW_ERR;\n    } else {\n        cli->onReceive(cli, buffer, n);\n    }\n    return SW_OK;\n}\n\nstatic int Client_onError(Reactor *reactor, Event *event) {\n    auto *cli = (Client *) event->socket->object;\n    if (cli->active) {\n        return cli->close();\n    } else {\n        Client_onWrite(reactor, event);\n    }\n    return SW_OK;\n}\n\nstatic void Client_onTimeout(Timer *timer, TimerNode *tnode) {\n    auto *cli = (Client *) tnode->data;\n    swoole_set_last_error(ETIMEDOUT);\n\n    cli->timer = nullptr;\n\n    if (cli->open_ssl && cli->socket->ssl_state != SW_SSL_STATE_READY) {\n        cli->active = false;\n    }\n    if (cli->socks5_proxy && cli->socks5_proxy->state != SW_SOCKS5_STATE_READY) {\n        cli->active = false;\n    } else if (cli->http_proxy && cli->http_proxy->state != SW_HTTP_PROXY_STATE_READY) {\n        cli->active = false;\n    }\n\n    cli->close();\n    if (cli->onError) {\n        cli->onError(cli);\n    }\n}\n\nstatic void Client_onResolveCompleted(AsyncEvent *event) {\n    auto *req = dynamic_cast<GethostbynameRequest *>(event->data.get());\n\n    auto *cli = static_cast<Client *>(event->object);\n    cli->wait_dns = false;\n    cli->dns_completed = true;\n\n    if (event->error == 0) {\n        /**\n         * In the callback function, the application layer cannot obtain the return value of the connect function,\n         *  so it must call `onError` to notify the caller.\n         */\n        double timeout = cli->socket->get_timeout(SW_TIMEOUT_CONNECT);\n        if (cli->connect(req->addr.c_str(), cli->server_port, timeout, cli->sock_flags_) == SW_ERR &&\n            !cli->onerror_called) {\n            goto _error;\n        }\n    } else {\n        swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n    _error:\n        cli->socket->removed = 1;\n        cli->close();\n        if (cli->onError) {\n            cli->onerror_called = true;\n            cli->onError(cli);\n        }\n    }\n}\n\nstatic int Client_onWrite(Reactor *reactor, Event *event) {\n    auto *cli = (Client *) event->socket->object;\n    Socket *_socket = cli->socket;\n    int ret;\n    int err;\n\n    if (cli->active) {\n        if (cli->open_ssl && _socket->ssl_state == SW_SSL_STATE_WAIT_STREAM) {\n            if (cli->ssl_handshake() < 0) {\n                swoole_set_last_error(SW_ERROR_SSL_HANDSHAKE_FAILED);\n                goto _connect_fail;\n            } else if (_socket->ssl_state == SW_SSL_STATE_READY) {\n                goto _connect_success;\n            } else {\n                if (_socket->ssl_want_read) {\n                    swoole_event_set(event->socket, SW_EVENT_READ);\n                }\n                return SW_OK;\n            }\n        }\n        if (Reactor::_writable_callback(reactor, event) < 0) {\n            return SW_ERR;\n        }\n        if (cli->onBufferEmpty && cli->high_watermark &&\n            _socket->get_out_buffer_length() <= cli->buffer_low_watermark) {\n            cli->high_watermark = false;\n            cli->onBufferEmpty(cli);\n        }\n        return SW_OK;\n    }\n\n    ret = _socket->get_option(SOL_SOCKET, SO_ERROR, &err);\n    swoole_set_last_error(err);\n    if (ret < 0) {\n        swoole_sys_warning(\"getsockopt(%d) failed\", event->fd);\n        return SW_ERR;\n    }\n\n    // success\n    if (swoole_get_last_error() == 0) {\n        // listen read event\n        swoole_event_set(event->socket, SW_EVENT_READ);\n        // connected\n        cli->active = true;\n        // socks5 proxy\n        if (cli->socks5_proxy && cli->socks5_proxy->state == SW_SOCKS5_STATE_WAIT) {\n            const auto len = cli->socks5_proxy->pack_negotiate_request();\n            cli->socks5_proxy->state = SW_SOCKS5_STATE_HANDSHAKE;\n            return cli->send(cli->socks5_proxy->buf, len, 0);\n        }\n        // http proxy\n        if (cli->http_proxy && cli->http_proxy->state == SW_HTTP_PROXY_STATE_WAIT) {\n            auto proxy_buf = sw_tg_buffer();\n            auto host_name = cli->get_http_proxy_host_name();\n            size_t n = cli->http_proxy->pack(proxy_buf, host_name);\n            swoole_trace_log(SW_TRACE_HTTP_CLIENT, \"proxy request: <<EOF\\n%.*sEOF\", (int) n, proxy_buf->str);\n            return cli->send(proxy_buf->str, n, 0);\n        }\n        if (cli->open_ssl) {\n            if (cli->ssl_handshake() < 0) {\n                swoole_set_last_error(SW_ERROR_SSL_HANDSHAKE_FAILED);\n                goto _connect_fail;\n            } else {\n                _socket->ssl_state = SW_SSL_STATE_WAIT_STREAM;\n            }\n            return SW_OK;\n        }\n    _connect_success:\n        execute_onConnect(cli);\n    } else {\n    _connect_fail:\n        cli->active = false;\n        cli->close();\n        cli->onError(cli);\n    }\n\n    return SW_OK;\n}\n\n}  // namespace network\n}  // namespace swoole\n"
  },
  {
    "path": "src/network/dns.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_coroutine_socket.h\"\n#include \"swoole_coroutine_system.h\"\n#include \"swoole_coroutine_api.h\"\n#include \"swoole_util.h\"\n\n#include <string>\n#include <iostream>\n#include <vector>\n#include <unordered_map>\n#include <sstream>\n#include <fstream>\n\n#define SW_PATH_HOSTS \"/etc/hosts\"\n\n#ifdef SW_USE_CARES\n#include <ares.h>\n#endif\n\nusing swoole::NameResolver;\nusing swoole::coroutine::System;\nusing swoole::network::Address;\n\nSW_API bool swoole_load_resolv_conf() {\n    FILE *fp;\n    char line[100];\n    char buf[16] = {};\n\n    if ((fp = fopen(SwooleG.dns_resolvconf_path.c_str(), \"rt\")) == nullptr) {\n        swoole_sys_warning(\"fopen(%s) failed\", SwooleG.dns_resolvconf_path.c_str());\n        return false;\n    }\n\n    while (fgets(line, 100, fp)) {\n        if (strncmp(line, \"nameserver\", 10) == 0) {\n            strcpy(buf, strtok(line, \" \"));\n            strcpy(buf, strtok(nullptr, \"\\n\"));\n            break;\n        }\n    }\n    fclose(fp);\n\n    if (strlen(buf) == 0) {\n        return false;\n    }\n    swoole_set_dns_server(buf);\n    return true;\n}\n\nSW_API void swoole_set_dns_server(const std::string &server) {\n    char *_port;\n    int dns_server_port = SW_DNS_SERVER_PORT;\n    char dns_server_host[32];\n    strcpy(dns_server_host, server.c_str());\n    if ((_port = strchr(const_cast<char *>(server.c_str()), ':'))) {\n        dns_server_port = sw_atoi(_port + 1);\n        if (!Address::verify_port(dns_server_port, true)) {\n            dns_server_port = SW_DNS_SERVER_PORT;\n        }\n        dns_server_host[_port - server.c_str()] = '\\0';\n    }\n    SwooleG.dns_server.host = dns_server_host;\n    SwooleG.dns_server.port = dns_server_port;\n}\n\nSW_API swoole::DnsServer swoole_get_dns_server() {\n    if (SwooleG.dns_server.host.empty()) {\n        swoole_load_resolv_conf();\n    }\n    return SwooleG.dns_server;\n}\n\nSW_API void swoole_set_hosts_path(const std::string &hosts_file) {\n    SwooleG.dns_hosts_path = hosts_file;\n}\n\nSW_API void swoole_name_resolver_add(const NameResolver &resolver, bool append) {\n    if (append) {\n        SwooleG.name_resolvers.push_back(resolver);\n    } else {\n        SwooleG.name_resolvers.push_front(resolver);\n    }\n}\n\nSW_API void swoole_name_resolver_each(\n    const std::function<swTraverseOperation(const std::list<NameResolver>::iterator &iter)> &fn) {\n    for (auto iter = SwooleG.name_resolvers.begin(); iter != SwooleG.name_resolvers.end(); ++iter) {\n        const swTraverseOperation op = fn(iter);\n        if (op == SW_TRAVERSE_REMOVE) {\n            SwooleG.name_resolvers.erase(iter++);\n        } else if (op == SW_TRAVERSE_STOP) {\n            break;\n        }\n    }\n}\n\nSW_API std::string swoole_name_resolver_lookup(const std::string &host_name, NameResolver::Context *ctx) {\n    if (SwooleG.name_resolvers.empty()) {\n        goto _dns_lookup;\n    }\n    for (auto &name_resolver : SwooleG.name_resolvers) {\n        std::string result = name_resolver.resolve(host_name, ctx, name_resolver.private_data);\n        if (!result.empty() || ctx->final_) {\n            return result;\n        }\n    }\n_dns_lookup:\n    /*\n     * Use DNS to resolve host name by default\n     */\n    if (swoole_coroutine_is_in()) {\n        return System::gethostbyname(host_name, ctx->type, ctx->timeout);\n    } else {\n        return swoole::network::gethostbyname(ctx->type, host_name);\n    }\n}\n\nnamespace swoole {\nnamespace coroutine {\n\nenum RecordType {\n    SW_DNS_A_RECORD = 0x01,     // Lookup IPv4 address\n    SW_DNS_AAAA_RECORD = 0x1c,  // Lookup IPv6 address\n    SW_DNS_MX_RECORD = 0x0f     // Lookup mail server for domain\n};\n\nenum DNSError {\n    SW_DNS_NOT_EXIST,  // Error: address does not exist\n    SW_DNS_TIMEOUT,    // Lookup time expired\n    SW_DNS_ERROR       // No memory or other error\n};\n\n/* Struct for the DNS Header */\nstruct RecordHeader {\n    uint16_t id;\n    uchar rd : 1;\n    uchar tc : 1;\n    uchar aa : 1;\n    uchar opcode : 4;\n    uchar qr : 1;\n    uchar rcode : 4;\n    uchar z : 3;\n    uchar ra : 1;\n    uint16_t qdcount;\n    uint16_t ancount;\n    uint16_t nscount;\n    uint16_t arcount;\n};\n\n/* Struct for the flags for the DNS Question */\nstruct Q_FLAGS {\n    uint16_t qtype;\n    uint16_t qclass;\n};\n\n/* Struct for the flags for the DNS RRs */\nstruct RR_FLAGS {\n    uint16_t type;\n    uint16_t rdclass;\n    uint32_t ttl;\n    uint16_t rdlength;\n};\n\nstatic uint16_t dns_request_id = 1;\n\nstatic int domain_encode(const char *src, int n, char *dest);\nstatic void domain_decode(char *str);\nstatic std::string parse_ip_address(void *vaddr, int type);\n\nstd::string get_ip_by_hosts(const std::string &search_domain) {\n    std::ifstream file(SwooleG.dns_hosts_path.empty() ? SW_PATH_HOSTS : SwooleG.dns_hosts_path);\n    if (!file.is_open()) {\n        return \"\";\n    }\n\n    std::string line;\n    std::string domain;\n    std::string txtaddr;\n    std::vector<std::string> domains;\n    std::unordered_map<std::string, std::string> result{};\n\n    while (getline(file, line)) {\n        std::string::size_type ops = line.find_first_of('#');\n        if (ops != std::string::npos) {\n            line[ops] = '\\0';\n        }\n\n        if (line[0] == '\\n' || line[0] == '\\0' || line[0] == '\\r') {\n            continue;\n        }\n\n        std::istringstream stream(line);\n        while (stream >> domain) {\n            domains.push_back(domain);\n        }\n        if (domains.empty() || domains.size() == 1) {\n            domains.clear();\n            continue;\n        }\n\n        txtaddr = domains[0];\n        for (size_t i = 1; i < domains.size(); i++) {\n            result.insert(std::make_pair(domains[i], txtaddr));\n        }\n\n        auto iter = result.find(search_domain);\n        if (iter != result.end()) {\n            return iter->second;\n        } else {\n            result.clear();\n            domains.clear();\n            continue;\n        }\n    }\n\n    return \"\";\n}\n\nstatic std::string parse_ip_address(void *vaddr, int type) {\n    auto addr = static_cast<unsigned char *>(vaddr);\n    std::string ip_addr;\n    if (type == AF_INET) {\n        char buff[4 * 4 + 3 + 1];\n        sw_snprintf(buff, sizeof(buff), \"%u.%u.%u.%u\", addr[0], addr[1], addr[2], addr[3]);\n        return ip_addr.assign(buff);\n    } else if (type == AF_INET6) {\n        for (int i = 0; i < 16; i += 2) {\n            if (i > 0) {\n                ip_addr.append(\":\");\n            }\n            char buf[4 + 1];\n            size_t n = sw_snprintf(buf, sizeof(buf), \"%02x%02x\", addr[i], addr[i + 1]);\n            ip_addr.append(buf, n);\n        }\n    } else {\n        assert(0);\n    }\n    return ip_addr;\n}\n\nstd::vector<std::string> dns_lookup_impl_with_socket(const char *domain, int family, double timeout) {\n    Q_FLAGS *qflags = nullptr;\n    char packet[SW_BUFFER_SIZE_STD];\n    RecordHeader *header = nullptr;\n    int steps = 0;\n    std::vector<std::string> result;\n\n    if (SwooleG.dns_server.host.empty() && !swoole_load_resolv_conf()) {\n        swoole_set_last_error(SW_ERROR_DNSLOOKUP_NO_SERVER);\n        return result;\n    }\n\n    header = reinterpret_cast<RecordHeader *>(packet);\n    int _request_id = dns_request_id++;\n    header->id = htons(_request_id);\n    header->qr = 0;\n    header->opcode = 0;\n    header->aa = 0;\n    header->tc = 0;\n    header->rd = 1;\n    header->ra = 0;\n    header->z = 0;\n    header->rcode = 0;\n    header->qdcount = htons(1);\n    header->ancount = 0x0000;\n    header->nscount = 0x0000;\n    header->arcount = 0x0000;\n\n    steps = sizeof(RecordHeader);\n\n    char *_domain_name = &packet[steps];\n\n    const int len = strlen(domain);\n    if (domain_encode(domain, len, _domain_name) < 0) {\n        swoole_warning(\"invalid domain[%s]\", domain);\n        return result;\n    }\n\n    steps += (strlen((const char *) _domain_name) + 1);\n\n    qflags = reinterpret_cast<Q_FLAGS *>(&packet[steps]);\n    qflags->qtype = htons(family == AF_INET6 ? SW_DNS_AAAA_RECORD : SW_DNS_A_RECORD);\n    qflags->qclass = htons(0x0001);\n    steps += sizeof(Q_FLAGS);\n\n    Socket _sock(SW_SOCK_UDP);\n    if (timeout > 0) {\n        _sock.set_timeout(timeout);\n    }\n    if (!_sock.sendto(SwooleG.dns_server.host, SwooleG.dns_server.port, (char *) packet, steps)) {\n        swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n        return result;\n    }\n\n    /**\n     * response\n     */\n    header = nullptr;\n    qflags = nullptr;\n    RR_FLAGS *rrflags = nullptr;\n\n    uchar rdata[10][254];\n    uint32_t type[10];\n    sw_memset_zero(rdata, sizeof(rdata));\n\n    steps = 0;\n\n    char name[10][254];\n    int i;\n\n    auto ret = _sock.recv(packet, sizeof(packet) - 1);\n    if (ret <= 0) {\n        swoole_set_last_error(_sock.errCode == ECANCELED ? SW_ERROR_CO_CANCELED : SW_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n        return result;\n    }\n\n    packet[ret] = 0;\n    header = reinterpret_cast<RecordHeader *>(packet);\n    steps = sizeof(RecordHeader);\n\n    _domain_name = &packet[steps];\n    domain_decode(_domain_name);\n    steps = steps + (strlen(_domain_name) + 2);\n\n    qflags = reinterpret_cast<Q_FLAGS *>(&packet[steps]);\n    (void) qflags;\n    steps = steps + sizeof(Q_FLAGS);\n\n    int ancount = ntohs(header->ancount);\n    if (ancount > 10) {\n        ancount = 10;\n    }\n    /* Parsing the RRs from the reply packet */\n    for (i = 0; i < ancount; ++i) {\n        type[i] = 0;\n        /* Parsing the NAME portion of the RR */\n        char *temp = &packet[steps];\n        int j = 0;\n        while (*temp != 0) {\n            if ((uchar) (*temp) == 0xc0) {\n                ++temp;\n                temp = &packet[(uint8_t) *temp];\n            } else {\n                name[i][j] = *temp;\n                ++j;\n                ++temp;\n            }\n        }\n        name[i][j] = '\\0';\n\n        domain_decode(name[i]);\n        steps = steps + 2;\n\n        /* Parsing the RR flags of the RR */\n        rrflags = (RR_FLAGS *) &packet[steps];\n        steps = steps + sizeof(RR_FLAGS) - 2;\n\n        /* Parsing the IPv4 address in the RR */\n        type[i] = ntohs(rrflags->type);\n        for (j = 0; j < ntohs(rrflags->rdlength); ++j) {\n            rdata[i][j] = (uchar) packet[steps + j];\n        }\n\n        /* Parsing the canonical name in the RR */\n        if (ntohs(rrflags->type) == 5) {\n            temp = &packet[steps];\n            j = 0;\n            while (*temp != 0) {\n                if ((uchar) (*temp) == 0xc0) {\n                    ++temp;\n                    temp = &packet[(uint8_t) *temp];\n                } else {\n                    rdata[i][j] = *temp;\n                    ++j;\n                    ++temp;\n                }\n            }\n            rdata[i][j] = '\\0';\n            domain_decode((char *) rdata[i]);\n            type[i] = ntohs(rrflags->type);\n        }\n        steps = steps + ntohs(rrflags->rdlength);\n    }\n\n    int request_id = ntohs(header->id);\n    // bad response\n    if (request_id != _request_id) {\n        swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n        return result;\n    }\n    for (i = 0; i < ancount; i++) {\n        if (type[i] != SW_DNS_A_RECORD && type[i] != SW_DNS_AAAA_RECORD) {\n            continue;\n        }\n        result.push_back(parse_ip_address(rdata[i], type[i] == SW_DNS_A_RECORD ? AF_INET : AF_INET6));\n    }\n    if (result.empty()) {\n        swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n    }\n    return result;\n}\n\n/**\n * The function converts the dot-based hostname into the DNS format\n * (i.e. www.apple.com into 3www5apple3com0)\n */\nstatic int domain_encode(const char *src, int n, char *dest) {\n    if (src[n] == '.') {\n        return SW_ERR;\n    }\n\n    int pos = 0;\n    int len = 0;\n    memcpy(dest + 1, src, n + 1);\n    dest[n + 1] = '.';\n    dest[n + 2] = 0;\n    src = dest + 1;\n    n++;\n\n    for (int i = 0; i < n; i++) {\n        if (src[i] == '.') {\n            len = i - pos;\n            dest[pos] = len;\n            pos += len + 1;\n        }\n    }\n    dest[pos] = 0;\n    return SW_OK;\n}\n\n/**\n * This function converts a DNS-based hostname into dot-based format\n * (i.e. 3www5apple3com0 into www.apple.com)\n */\nstatic void domain_decode(char *str) {\n    size_t i;\n    for (i = 0; i < strlen(str); i++) {\n        uint32_t len = str[i];\n        for (size_t j = 0; j < len; j++) {\n            str[i] = str[i + 1];\n            i++;\n        }\n        str[i] = '.';\n    }\n    str[i - 1] = '\\0';\n}\n\n#ifdef SW_USE_CARES\nstruct ResolvContext {\n    ares_channel channel;\n    ares_options ares_opts;\n    int ares_flags;\n    int error;\n    bool completed;\n    Coroutine *co;\n    std::shared_ptr<bool> defer_task_cancelled;\n    std::unordered_map<int, network::Socket *> sockets;\n    std::vector<std::string> result;\n};\n\nstd::vector<std::string> dns_lookup_impl_with_cares(const char *domain, int family, double timeout) {\n    if (!swoole_event_isset_handler(SW_FD_CARES, SW_EVENT_READ)) {\n        ares_library_init(ARES_LIB_INIT_ALL);\n        swoole_event_set_handler(SW_FD_CARES, SW_EVENT_READ, [](Reactor *reactor, Event *event) -> int {\n            auto ctx = static_cast<ResolvContext *>(event->socket->object);\n            swoole_trace_log(SW_TRACE_CARES, \"[event callback] readable event, fd=%d\", event->socket->fd);\n            ares_process_fd(ctx->channel, event->fd, ARES_SOCKET_BAD);\n            return SW_OK;\n        });\n        swoole_event_set_handler(SW_FD_CARES, SW_EVENT_WRITE, [](Reactor *reactor, Event *event) -> int {\n            auto ctx = static_cast<ResolvContext *>(event->socket->object);\n            swoole_trace_log(SW_TRACE_CARES, \"[event callback] writable event, fd=%d\", event->socket->fd);\n            ares_process_fd(ctx->channel, ARES_SOCKET_BAD, event->fd);\n            return SW_OK;\n        });\n        sw_reactor()->add_destroy_callback([](void *_data) { ares_library_cleanup(); }, nullptr);\n    }\n\n    ResolvContext ctx{};\n    Coroutine *co = Coroutine::get_current_safe();\n    ctx.co = co;\n    ctx.completed = false;\n    ctx.defer_task_cancelled = std::make_shared<bool>(false);\n    char lookups[] = \"fb\";\n    int res;\n    ctx.ares_opts.lookups = lookups;\n    ctx.ares_opts.timeout = timeout * 1000;\n    ctx.ares_opts.tries = SwooleG.dns_tries;\n    ctx.ares_opts.sock_state_cb_data = &ctx;\n    ctx.ares_opts.sock_state_cb = [](void *arg, int fd, int readable, int writable) {\n        auto ctx = static_cast<ResolvContext *>(arg);\n        int events = 0;\n        if (readable) {\n            events |= SW_EVENT_READ;\n        }\n        if (writable) {\n            events |= SW_EVENT_WRITE;\n        }\n\n        swoole_trace_log(SW_TRACE_CARES, \"[sock_state_cb], fd=%d, readable=%d, writable=%d\", fd, readable, writable);\n\n        network::Socket *_socket = nullptr;\n        if (ctx->sockets.find(fd) == ctx->sockets.end()) {\n            if (events == 0) {\n                swoole_warning(\"error events, fd=%d\", fd);\n                return;\n            }\n            _socket = make_socket(fd, SW_FD_CARES);\n            _socket->object = ctx;\n            ctx->sockets[fd] = _socket;\n        } else {\n            _socket = ctx->sockets[fd];\n            if (events == 0) {\n                swoole_trace_log(SW_TRACE_CARES, \"[del event], fd=%d\", fd);\n                swoole_event_del(_socket);\n                _socket->fd = -1;\n                _socket->free();\n                ctx->sockets.erase(fd);\n                return;\n            }\n        }\n\n        if (_socket->events) {\n            swoole_event_set(_socket, events);\n            swoole_trace_log(SW_TRACE_CARES, \"[set event] fd=%d, events=%d\", fd, events);\n        } else {\n            swoole_event_add(_socket, events);\n            swoole_trace_log(SW_TRACE_CARES, \"[add event] fd=%d, events=%d\", fd, events);\n        }\n    };\n    ctx.ares_flags = ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES | ARES_OPT_SOCK_STATE_CB | ARES_OPT_LOOKUPS;\n\n    if ((res = ares_init_options(&ctx.channel, &ctx.ares_opts, ctx.ares_flags)) != ARES_SUCCESS) {\n        swoole_warning(\"ares_init_options() failed, Error: %s[%d]\", ares_strerror(res), res);\n        goto _return;\n    }\n\n    if (!SwooleG.dns_server.host.empty()) {\n#if (ARES_VERSION >= 0x010b00)\n        struct ares_addr_port_node servers;\n        servers.family = AF_INET;\n        servers.next = nullptr;\n        inet_pton(AF_INET, SwooleG.dns_server.host.c_str(), &servers.addr.addr4);\n        servers.tcp_port = 0;\n        servers.udp_port = SwooleG.dns_server.port;\n        ares_set_servers_ports(ctx.channel, &servers);\n#elif (ARES_VERSION >= 0x010701)\n        struct ares_addr_node servers;\n        servers.family = AF_INET;\n        servers.next = nullptr;\n        inet_pton(AF_INET, SwooleG.dns_server_host.c_str(), &servers.addr.addr4);\n        ares_set_servers(ctx.channel, &servers);\n        if (SwooleG.dns_server_port != SW_DNS_SERVER_PORT) {\n            swoole_warning(\"not support to set port of dns server\");\n        }\n#else\n        swoole_warning(\"not support to set dns server\");\n#endif\n    }\n\n    ares_gethostbyname(\n        ctx.channel,\n        domain,\n        family,\n        [](void *data, int status, int timeouts, struct hostent *hostent) {\n            auto ctx = static_cast<ResolvContext *>(data);\n\n            swoole_trace_log(SW_TRACE_CARES, \"[cares callback] status=%d, timeouts=%d\", status, timeouts);\n\n            if (timeouts > 0) {\n                ctx->error = SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT;\n                goto _resume;\n            }\n\n            if (status != ARES_SUCCESS) {\n                ctx->error = status;\n                goto _resume;\n            }\n\n            if (hostent->h_addr_list) {\n                char **paddr = hostent->h_addr_list;\n                while (*paddr != nullptr) {\n                    ctx->result.emplace_back(parse_ip_address(*paddr, hostent->h_addrtype));\n                    paddr++;\n                }\n            }\n        _resume:\n            if (ctx->co && ctx->co->is_suspending()) {\n                auto _cancelled = ctx->defer_task_cancelled;\n                swoole_event_defer(\n                    [_cancelled](void *data) {\n                        if (*_cancelled) {\n                            return;\n                        }\n                        auto *co = static_cast<Coroutine *>(data);\n                        co->resume();\n                    },\n                    ctx->co);\n                ctx->co = nullptr;\n            } else {\n                ctx->completed = true;\n            }\n        },\n        &ctx);\n\n    if (ctx.error || ctx.completed) {\n        goto _destroy;\n    }\n\n    co->yield_ex(timeout);\n    if (co->is_canceled()) {\n        ares_cancel(ctx.channel);\n    } else if (co->is_timedout()) {\n        ares_process_fd(ctx.channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);\n        ctx.error = ARES_ETIMEOUT;\n    } else {\n        swoole_trace_log(SW_TRACE_CARES, \"lookup success, result_count=%lu\", ctx.result.size());\n    }\n_destroy:\n    if (ctx.error) {\n        switch (ctx.error) {\n        case ARES_ECANCELLED:\n            swoole_set_last_error(SW_ERROR_CO_CANCELED);\n            break;\n        case ARES_ETIMEOUT:\n            swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT);\n            break;\n        default:\n            swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n            break;\n        }\n    }\n    *ctx.defer_task_cancelled = true;\n    ares_destroy(ctx.channel);\n_return:\n    return ctx.result;\n}\n#endif\n\nstd::vector<std::string> dns_lookup(const char *domain, int family, double timeout) {\n    family = family == AF_INET6 ? AF_INET6 : AF_INET;  // only support IPv4 and IPv6\n#ifdef SW_USE_CARES\n    return dns_lookup_impl_with_cares(domain, family, timeout);\n#else\n    return dns_lookup_impl_with_socket(domain, family, timeout);\n#endif\n}\n\n}  // namespace coroutine\n\n/**\n * blocking-IO, Use in synchronous mode or AIO thread pool\n */\nnamespace network {\n\n#ifndef HAVE_GETHOSTBYNAME2_R\n#include <mutex>\nstatic std::mutex g_gethostbyname2_lock;\n#endif\n\n#ifdef HAVE_GETHOSTBYNAME2_R\nint gethostbyname(int flags, const char *name, char *addr) {\n    int _af = flags & (~SW_DNS_LOOKUP_RANDOM);\n    int index = 0;\n    int rc, err;\n    int buf_len = 256;\n    hostent hbuf{};\n    hostent *result;\n\n    char *buf = static_cast<char *>(sw_malloc(buf_len));\n    if (!buf) {\n        return SW_ERR;\n    }\n    memset(buf, 0, buf_len);\n    while ((rc = ::gethostbyname2_r(name, _af, &hbuf, buf, buf_len, &result, &err)) == ERANGE) {\n        buf_len *= 2;\n        char *tmp = static_cast<char *>(sw_realloc(buf, buf_len));\n        if (nullptr == tmp) {\n            sw_free(buf);\n            return SW_ERR;\n        } else {\n            buf = tmp;\n        }\n    }\n\n    if (0 != rc || nullptr == result) {\n        sw_free(buf);\n        return SW_ERR;\n    }\n\n    union {\n        char v4[INET_ADDRSTRLEN];\n        char v6[INET6_ADDRSTRLEN];\n    } addr_list[SW_DNS_HOST_BUFFER_SIZE]{};\n\n    int i = 0;\n    for (i = 0; i < SW_DNS_HOST_BUFFER_SIZE; i++) {\n        if (hbuf.h_addr_list[i] == nullptr) {\n            break;\n        }\n        if (_af == AF_INET) {\n            memcpy(addr_list[i].v4, hbuf.h_addr_list[i], hbuf.h_length);\n        } else {\n            memcpy(addr_list[i].v6, hbuf.h_addr_list[i], hbuf.h_length);\n        }\n    }\n    if (_af == AF_INET) {\n        memcpy(addr, addr_list[index].v4, hbuf.h_length);\n    } else {\n        memcpy(addr, addr_list[index].v6, hbuf.h_length);\n    }\n\n    sw_free(buf);\n\n    return SW_OK;\n}\n#else\nint gethostbyname(int flags, const char *name, char *addr) {\n    int __af = flags & (~SW_DNS_LOOKUP_RANDOM);\n    int index = 0;\n\n    std::lock_guard<std::mutex> _lock(g_gethostbyname2_lock);\n\n    struct hostent *host_entry;\n    if (!(host_entry = ::gethostbyname2(name, __af))) {\n        return SW_ERR;\n    }\n\n    union {\n        char v4[INET_ADDRSTRLEN];\n        char v6[INET6_ADDRSTRLEN];\n    } addr_list[SW_DNS_HOST_BUFFER_SIZE];\n\n    int i = 0;\n    for (i = 0; i < SW_DNS_HOST_BUFFER_SIZE; i++) {\n        if (host_entry->h_addr_list[i] == nullptr) {\n            break;\n        }\n        if (__af == AF_INET) {\n            memcpy(addr_list[i].v4, host_entry->h_addr_list[i], host_entry->h_length);\n        } else {\n            memcpy(addr_list[i].v6, host_entry->h_addr_list[i], host_entry->h_length);\n        }\n    }\n    if (__af == AF_INET) {\n        memcpy(addr, addr_list[index].v4, host_entry->h_length);\n    } else {\n        memcpy(addr, addr_list[index].v6, host_entry->h_length);\n    }\n    return SW_OK;\n}\n#endif\n\nstd::string gethostbyname(int type, const std::string &name) {\n    char addr[sizeof(in6_addr)];\n    if (gethostbyname(type, name.c_str(), addr) == SW_OK) {\n        return Address::addr_str(type, addr);\n    }\n    swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n    return {};\n}\n\nint getaddrinfo(GetaddrinfoRequest *req) {\n    addrinfo *result = nullptr;\n    addrinfo *ptr = nullptr;\n    addrinfo hints{};\n\n    hints.ai_family = req->family;\n    hints.ai_socktype = req->socktype;\n    hints.ai_protocol = req->protocol;\n\n    int ret = ::getaddrinfo(req->hostname.c_str(), req->service.c_str(), &hints, &result);\n    if (ret != 0) {\n        req->error = ret;\n        return SW_ERR;\n    }\n\n    int i = 0;\n    for (ptr = result; ptr != nullptr; ptr = ptr->ai_next, i++) {\n    }\n    req->count = SW_MIN(i, SW_DNS_HOST_BUFFER_SIZE);\n    req->results.resize(req->count);\n\n    for (ptr = result, i = 0; ptr != nullptr; ptr = ptr->ai_next, i++) {\n        switch (ptr->ai_family) {\n        case AF_INET:\n            memcpy(&req->results[i], ptr->ai_addr, sizeof(struct sockaddr_in));\n            break;\n        case AF_INET6:\n            memcpy(&req->results[i], ptr->ai_addr, sizeof(struct sockaddr_in6));\n            break;\n        default:\n            swoole_warning(\"unknown socket family[%d]\", ptr->ai_family);\n            break;\n        }\n        if (i == SW_DNS_HOST_BUFFER_SIZE) {\n            break;\n        }\n    }\n    ::freeaddrinfo(result);\n    req->error = 0;\n\n    return SW_OK;\n}\n\nint gethostbyname(GethostbynameRequest *req) {\n    const auto rv = gethostbyname(req->family, req->name);\n    if (rv.empty()) {\n        swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n        return SW_ERR;\n    }\n    req->addr = rv;\n    return SW_OK;\n}\n}  // namespace network\n\nvoid GetaddrinfoRequest::parse_result(std::vector<std::string> &retval) const {\n    for (auto &addr : results) {\n        const char *addr_str;\n        if (family == AF_INET6) {\n            addr_str = Address::addr_str(family, &addr.sin6_addr);\n        } else {\n            addr_str = Address::addr_str(family, &((sockaddr_in *) &addr)->sin_addr);\n        }\n        if (addr_str) {\n            retval.emplace_back(addr_str);\n        }\n    }\n}\n}  // namespace swoole\n"
  },
  {
    "path": "src/network/socket.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_socket.h\"\n#include \"swoole_signal.h\"\n#include \"swoole_util.h\"\n#include \"swoole_string.h\"\n#include \"swoole_timer.h\"\n\n#include <utility>\n#include <memory>\n\nnamespace swoole {\nnamespace network {\n\ndouble Socket::default_dns_timeout = SW_SOCKET_DEFAULT_DNS_TIMEOUT;\ndouble Socket::default_connect_timeout = SW_SOCKET_DEFAULT_CONNECT_TIMEOUT;\ndouble Socket::default_read_timeout = SW_SOCKET_DEFAULT_READ_TIMEOUT;\ndouble Socket::default_write_timeout = SW_SOCKET_DEFAULT_WRITE_TIMEOUT;\nuint32_t Socket::default_buffer_size = SW_SOCKET_BUFFER_SIZE;\n\nIOVector::IOVector(const iovec *_iov, int _iovcnt) {\n    iov = new iovec[_iovcnt + _iovcnt];\n    iov_iterator = iov + _iovcnt;\n    count = remain_count = _iovcnt;\n\n    memcpy(iov, _iov, sizeof(*_iov) * _iovcnt);\n    memcpy(iov_iterator, _iov, sizeof(*_iov) * _iovcnt);\n}\n\nIOVector::~IOVector() {\n    delete[] iov;\n}\n\nvoid IOVector::update_iterator(ssize_t _n) {\n    size_t total_bytes = 0;\n    size_t _offset_bytes = 0;\n    int _index = 0;\n\n    if (_n <= 0 || remain_count == 0) {\n        return;\n    }\n\n    SW_LOOP_N(remain_count) {\n        total_bytes += iov_iterator[i].iov_len;\n        if (static_cast<ssize_t>(total_bytes) >= _n) {\n            _offset_bytes = iov_iterator[i].iov_len - (total_bytes - _n);\n            _index = i;\n\n            if (_offset_bytes == iov_iterator[i].iov_len) {\n                _index++;\n                _offset_bytes = 0;\n            }\n            // update remain_count, index, offset_bytes\n            remain_count -= _index;\n            index += _index;\n            offset_bytes = i > 0 ? 0 : offset_bytes;\n            offset_bytes += _offset_bytes;\n            if (remain_count == 0) {\n                // iov should not be modified, prevent valgrind from checking for invalid read\n                return;\n            }\n            iov_iterator += _index;\n            iov_iterator->iov_base = static_cast<char *>(iov_iterator->iov_base) + _offset_bytes;\n            iov_iterator->iov_len = iov_iterator->iov_len - _offset_bytes;\n\n            return;\n        }\n    }\n\n    // represents the length of _n greater than total_bytes\n    abort();\n}\n\nstatic bool check_sendfile_parameters(const File *file, off_t begin, size_t length, off_t *end) {\n    auto filename = file->get_path().c_str();\n    if (!file->ready()) {\n        swoole_sys_warning(\"open('%s') failed\", filename);\n        return false;\n    }\n\n    FileStatus file_stat;\n    if (!file->stat(&file_stat)) {\n        swoole_sys_warning(\"fstat('%s') failed\", filename);\n        return false;\n    }\n\n    if (file_stat.st_size == 0) {\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_FILE_EMPTY, \"cannot send empty file '%s'\", filename);\n        return false;\n    }\n\n    if (length == 0) {\n        *end = file_stat.st_size;\n    } else {\n        *end = begin + static_cast<off_t>(length);\n    }\n\n    if (begin < 0 || *end > file_stat.st_size) {\n        swoole_error_log(\n            SW_LOG_WARNING, SW_ERROR_INVALID_PARAMS, \"length[%ld] or offset[%ld] is invalid\", length, (long) begin);\n        return false;\n    }\n\n    return true;\n}\n\nstatic size_t get_sendfile_chunk_size(off_t begin, off_t end) {\n    size_t real_length = end - begin;\n    return real_length > SW_SENDFILE_CHUNK_SIZE ? SW_SENDFILE_CHUNK_SIZE : real_length;\n}\n\nint Socket::what_event_want(int default_event) const {\n    if (ssl && (ssl_want_write || ssl_want_read)) {\n        return ssl_want_write ? SW_EVENT_WRITE : SW_EVENT_READ;\n    }\n    return default_event;\n}\n\n#define CHECK_RETURN_VALUE(rv, be_zero_return)                                                                         \\\n    if (rv < 0) {                                                                                                      \\\n        if (errno == EINTR || catch_error(errno) == SW_WAIT) {                                                         \\\n            return SW_CONTINUE;                                                                                        \\\n        }                                                                                                              \\\n        swoole_set_last_error(errno);                                                                                  \\\n        return SW_ERROR;                                                                                               \\\n    } else if (rv == 0) {                                                                                              \\\n        return be_zero_return;                                                                                         \\\n    }\n\nbool Socket::wait_for(const std::function<ReturnCode()> &fn, int event, int timeout_msec) {\n    double began_at = 0;\n    if (timeout_msec > 0) {\n        began_at = microtime();\n    }\n\n    if (!nonblock) {\n        set_nonblock();\n    }\n\n    if (ssl) {\n        ssl_clear_error();\n    }\n\n    while (true) {\n        switch (fn()) {\n        case SW_ERROR:\n            return false;\n        case SW_READY:\n            return true;\n        case SW_CONTINUE:\n            /**\n             * The ENOBUFS error indicates that the operating system currently lacks sufficient available memory,\n             * requiring waiting for the kernel to reclaim memory. Event listening for writes is also ineffective—the\n             * only recourse is to sleep for 10 milliseconds while awaiting kernel memory recovery.\n             */\n            if (has_kernel_nobufs()) {\n                usleep(10 * 1000);\n                continue;\n            }\n            break;\n        default:\n            break;\n        }\n\n        auto rv = wait_event(timeout_msec, what_event_want(event));\n        if (rv == SW_ERR && ((errno == EINTR && dont_restart) || errno != EINTR)) {\n            return false;\n        }\n\n        if (timeout_msec > 0) {\n            timeout_msec -= sec2msec(microtime() - began_at);\n            swoole_trace_log(SW_TRACE_CLIENT, \"timeout_ms=%d\", timeout_msec);\n            if (timeout_msec <= 0) {\n                swoole_set_last_error(ETIMEDOUT);\n                errno = ETIMEDOUT;\n                return false;\n            }\n        }\n    }\n\n    return false;\n}\n\nint Socket::sendfile_sync(const char *filename, off_t offset, size_t length) {\n    off_t end;\n    File file(filename, O_RDONLY);\n\n    if (!check_sendfile_parameters(&file, offset, length, &end)) {\n        return SW_ERR;\n    }\n\n    auto corked = false;\n    if (end - offset > SW_SOCKET_CORK_MIN_SIZE) {\n        corked = cork();\n    }\n\n    auto rv = wait_for(\n        [this, &file, &offset, end]() {\n            size_t sent_bytes = get_sendfile_chunk_size(offset, end);\n            ssize_t n = sendfile(file, &offset, sent_bytes);\n            CHECK_RETURN_VALUE(n, SW_ERROR);\n            return offset < end ? SW_CONTINUE : SW_READY;\n        },\n        SW_EVENT_WRITE,\n        sec2msec(write_timeout));\n\n    if (corked) {\n        uncork();\n    }\n\n    return rv ? SW_OK : SW_ERR;\n}\n\nssize_t Socket::writev_sync(const struct iovec *iov, size_t iovcnt) {\n    ssize_t bytes = 0;\n\n    auto rv = wait_for(\n        [this, &bytes, iov, iovcnt]() {\n            ssize_t n = writev(iov, iovcnt);\n            CHECK_RETURN_VALUE(n, SW_READY);\n            bytes = n;\n            return SW_READY;\n        },\n        SW_EVENT_WRITE,\n        sec2msec(write_timeout));\n\n    return rv ? bytes : -1;\n}\n\nswReturnCode Socket::connect_async(const Address &sa) {\n    set_nonblock();\n    while (true) {\n        auto ret = connect(sa);\n        if (ret < 0) {\n            if (errno == EINTR) {\n                continue;\n            }\n            swoole_set_last_error(errno);\n            return errno == EINPROGRESS ? SW_WAIT : SW_ERROR;\n        }\n        break;\n    }\n    return SW_READY;\n}\n\nint Socket::connect_sync(const Address &sa) {\n    auto rc = connect_async(sa);\n    if (rc != SW_WAIT) {\n        return rc == SW_READY ? SW_OK : SW_ERR;\n    }\n    double timeout = connect_timeout;\n    if (wait_event(timeout > 0 ? sec2msec(timeout) : timeout, SW_EVENT_WRITE) < 0) {\n        swoole_set_last_error(ETIMEDOUT);\n        return SW_ERR;\n    }\n    int err;\n    socklen_t len = sizeof(len);\n    int ret = get_option(SOL_SOCKET, SO_ERROR, &err, &len);\n    if (ret < 0) {\n        swoole_set_last_error(errno);\n        return SW_ERR;\n    }\n    if (err != 0) {\n        swoole_set_last_error(err);\n        return SW_ERR;\n    }\n    set_block();\n    return SW_OK;\n}\n\n/**\n * clear socket buffer.\n */\nvoid Socket::clean() const {\n    char buf[2048];\n    while (::recv(fd, buf, sizeof(buf), MSG_DONTWAIT) > 0) {\n    };\n}\n\n/**\n * Wait socket can read or write.\n */\nint Socket::wait_event(int timeout_ms, int _events) const {\n    pollfd event;\n    event.fd = fd;\n    event.events = translate_events_to_poll(_events);\n\n    if (timeout_ms < 0) {\n        timeout_ms = -1;\n    }\n\n    while (true) {\n        int ret = poll(&event, 1, timeout_ms);\n        if (ret == 0) {\n            swoole_set_last_error(SW_ERROR_SOCKET_POLL_TIMEOUT);\n            errno = EAGAIN;\n            return SW_ERR;\n        }\n        if (ret < 0) {\n            if (errno != EINTR) {\n                swoole_set_last_error(errno);\n                return SW_ERR;\n            }\n            if (dont_restart) {\n                swoole_set_last_error(errno);\n                return SW_ERR;\n            }\n            swoole_signal_dispatch();\n            continue;\n        }\n        return SW_OK;\n    }\n\n    return SW_OK;\n}\n\nssize_t Socket::send_sync(const void *_data, size_t _len, int flags) {\n    ssize_t bytes = 0;\n\n    auto rv = wait_for(\n        [this, _data, _len, flags, &bytes]() {\n            ssize_t n = send((char *) _data + bytes, _len - bytes, flags);\n            CHECK_RETURN_VALUE(n, SW_READY);\n            bytes += n;\n            return bytes == (ssize_t) _len ? SW_READY : SW_CONTINUE;\n        },\n        SW_EVENT_WRITE,\n        sec2msec(write_timeout));\n\n    return rv ? bytes : -1;\n}\n\nssize_t Socket::recv_sync(void *_data, size_t _len, int flags) {\n    ssize_t bytes = 0;\n\n    auto rv = wait_for(\n        [this, _data, _len, flags, &bytes]() {\n            /**\n             * In non-blocking mode, the MSG_WAITALL flag must be cleared. Otherwise, receiving a small amount of data\n             * may cause the poll listener to return 1 for readable events, yet the recv operation returns -1 with errno\n             * set to EAGAIN, resulting in an infinite loop.\n             */\n            ssize_t n = recv((char *) _data + bytes, _len - bytes, flags & ~MSG_WAITALL);\n            CHECK_RETURN_VALUE(n, SW_READY);\n            bytes += n;\n            if (flags & MSG_WAITALL) {\n                return bytes == (ssize_t) _len ? SW_READY : SW_CONTINUE;\n            } else {\n                return SW_READY;\n            }\n        },\n        SW_EVENT_READ,\n        sec2msec(read_timeout));\n\n    return rv ? bytes : -1;\n}\n\nSocket *Socket::accept() {\n    auto *socket = new Socket();\n    socket->removed = 1;\n    socket->socket_type = socket_type;\n    socket->info.len = sizeof(socket->info);\n#ifdef HAVE_ACCEPT4\n    int flags = SOCK_CLOEXEC;\n    if (nonblock) {\n        flags |= SOCK_NONBLOCK;\n    }\n    socket->fd = ::accept4(fd, reinterpret_cast<sockaddr *>(&socket->info.addr), &socket->info.len, flags);\n#else\n    socket->fd = ::accept(fd, (struct sockaddr *) &socket->info.addr, &socket->info.len);\n    if (socket->fd >= 0) {\n        set_fd_option(nonblock, 1);\n    }\n#endif\n    if (socket->fd < 0) {\n        delete socket;\n        return nullptr;\n    }\n    socket->info.type = socket_type;\n    socket->nonblock = nonblock;\n    socket->cloexec = 1;\n    return socket;\n}\n\nssize_t Socket::sendto_sync(const Address &sa, const void *_buf, size_t _n, int flags) {\n    ssize_t bytes = 0;\n\n    auto rv = wait_for(\n        [this, &sa, _buf, _n, flags, &bytes]() {\n            ssize_t n = sendto(sa, _buf, _n, flags);\n            CHECK_RETURN_VALUE(n, SW_READY);\n            bytes = n;\n            return SW_READY;\n        },\n        SW_EVENT_WRITE,\n        sec2msec(write_timeout));\n\n    return rv ? bytes : -1;\n}\n\nssize_t Socket::recvfrom(char *buf, size_t len, int flags, sockaddr *addr, socklen_t *addr_len) const {\n    ssize_t n = 0;\n    SW_LOOP_N(SW_SOCKET_RETRY_COUNT) {\n        n = ::recvfrom(fd, buf, len, flags, addr, addr_len);\n        if (n < 0 && errno == EINTR) {\n            continue;\n        }\n        break;\n    }\n    return n;\n}\n\nssize_t Socket::recvfrom_sync(char *buf, size_t len, int flags, Address *sa) {\n    return recvfrom_sync(buf, len, flags, &sa->addr.ss, &sa->len);\n}\n\nssize_t Socket::recvfrom_sync(char *buf, size_t len, int flags, sockaddr *addr, socklen_t *addr_len) {\n    ssize_t bytes = 0;\n\n    auto rv = wait_for(\n        [this, buf, len, flags, addr, addr_len, &bytes]() {\n            ssize_t n = recvfrom(buf, len, flags, addr, addr_len);\n            CHECK_RETURN_VALUE(n, SW_READY);\n            bytes = n;\n            return SW_READY;\n        },\n        SW_EVENT_READ,\n        sec2msec(read_timeout));\n\n    return rv ? bytes : -1;\n}\n\nstatic void socket_free_defer(void *ptr) {\n    auto *sock = static_cast<Socket *>(ptr);\n    if (sock->is_local() && sock->bound) {\n        ::unlink(sock->get_addr());\n    }\n    if (sock->fd != -1 && close(sock->fd) != 0) {\n        swoole_sys_warning(\"close(%d) failed\", sock->fd);\n    }\n    delete sock;\n}\n\nvoid Socket::free() {\n    if (recv_timer) {\n        swoole_timer_del(recv_timer);\n    }\n    if (send_timer) {\n        swoole_timer_del(send_timer);\n    }\n\n    delete in_buffer;\n    delete out_buffer;\n\n    if (swoole_event_is_available()) {\n        if (fd != -1 && !removed) {\n            swoole_event_del(this);\n        }\n        swoole_event_defer(socket_free_defer, this);\n    } else {\n        socket_free_defer(this);\n    }\n}\n\nint Socket::get_name() {\n    info.len = sizeof(info.addr);\n    if (getsockname(fd, &info.addr.ss, &info.len) < 0) {\n        return -1;\n    }\n    info.type = socket_type;\n    return 0;\n}\n\nint Socket::get_peer_name(Address *sa) const {\n    sa->len = sizeof(sa->addr);\n    sa->type = socket_type;\n    if (::getpeername(fd, &sa->addr.ss, &sa->len) != 0) {\n        return SW_ERR;\n    }\n    return SW_OK;\n}\n\nint Socket::set_tcp_nopush(int nopush) {\n#ifdef TCP_CORK\n    if (set_option(IPPROTO_TCP, TCP_CORK, nopush) == SW_ERR) {\n        return -1;\n    } else {\n        tcp_nopush = nopush;\n        return 0;\n    }\n#else\n    return -1;\n#endif\n}\n\nint Socket::bind(const std::string &_host, int port) {\n    Address addr;\n    if (!addr.assign(socket_type, _host, port, false)) {\n        return SW_ERR;\n    }\n    return bind(addr);\n}\n\nint Socket::bind(const struct sockaddr *sa, socklen_t len) {\n    if (::bind(fd, sa, len) < 0) {\n        return SW_ERR;\n    }\n    bound = 1;\n    return SW_OK;\n}\n\nint Socket::listen(int backlog) {\n    if (::listen(fd, backlog <= 0 ? SW_BACKLOG : backlog) < 0) {\n        return SW_ERR;\n    }\n    listened = 1;\n    return SW_OK;\n}\n\nbool Socket::set_buffer_size(uint32_t _buffer_size) const {\n    return set_send_buffer_size(_buffer_size) && set_recv_buffer_size(_buffer_size);\n}\n\nbool Socket::set_recv_buffer_size(uint32_t _buffer_size) const {\n    return set_option(SOL_SOCKET, SO_RCVBUF, _buffer_size) == 0;\n}\n\nbool Socket::set_send_buffer_size(uint32_t _buffer_size) const {\n    return set_option(SOL_SOCKET, SO_SNDBUF, _buffer_size) == 0;\n}\n\nbool Socket::check_liveness() {\n    char buf;\n    errno = 0;\n    ssize_t retval = peek(&buf, sizeof(buf), MSG_DONTWAIT);\n    return !(retval == 0 || (retval < 0 && catch_read_error(errno) == SW_CLOSE));\n}\n\nbool Socket::set_tcp_nodelay(int nodelay) {\n    if (set_option(IPPROTO_TCP, TCP_NODELAY, nodelay) == SW_ERR) {\n        return false;\n    } else {\n        tcp_nodelay = nodelay;\n        return true;\n    }\n}\n\nbool Socket::cork() {\n    if (tcp_nopush) {\n        return false;\n    }\n#ifdef TCP_CORK\n    if (set_tcp_nopush(1) < 0) {\n        swoole_sys_warning(\"set_tcp_nopush(fd=%d, ON) failed\", fd);\n        return false;\n    }\n#endif\n    // Need to turn off tcp nodelay when using nopush\n    if (tcp_nodelay && !set_tcp_nodelay(0)) {\n        swoole_sys_warning(\"set_tcp_nodelay(fd=%d, OFF) failed\", fd);\n    }\n    return true;\n}\n\nbool Socket::uncork() {\n    if (!tcp_nopush) {\n        return false;\n    }\n#ifdef TCP_CORK\n    if (set_tcp_nopush(0) < 0) {\n        swoole_sys_warning(\"set_tcp_nopush(fd=%d, OFF) failed\", fd);\n        return false;\n    }\n#endif\n    // Restore tcp_nodelay setting\n    if (enable_tcp_nodelay && tcp_nodelay == 0 && !set_tcp_nodelay(1)) {\n        swoole_sys_warning(\"set_tcp_nodelay(fd=%d, ON) failed\", fd);\n        return false;\n    }\n    return true;\n}\n\nSocket *Socket::dup() const {\n    auto *_socket = new Socket();\n    *_socket = *this;\n    _socket->fd = ::dup(fd);\n    return _socket;\n}\n\nstatic bool _set_timeout(int fd, int type, double timeout) {\n    timeval timeo;\n    timeo.tv_sec = (int) timeout;\n    timeo.tv_usec = (int) ((timeout - timeo.tv_sec) * 1000 * 1000);\n    int ret = setsockopt(fd, SOL_SOCKET, type, &timeo, sizeof(timeo));\n    if (ret < 0) {\n        swoole_sys_warning(\"setsockopt(SO_SNDTIMEO, %s) failed\", type == SO_SNDTIMEO ? \"SEND\" : \"RECV\");\n        return false;\n    } else {\n        return true;\n    }\n}\n\nstatic bool _fcntl_set_option(int sock, int nonblock, int cloexec) {\n    int opts, ret;\n\n    if (nonblock >= 0) {\n        do {\n            opts = fcntl(sock, F_GETFL);\n        } while (opts < 0 && errno == EINTR);\n\n        if (opts < 0) {\n            swoole_sys_warning(\"fcntl(%d, GETFL) failed\", sock);\n        }\n\n        if (nonblock) {\n            opts = opts | O_NONBLOCK;\n        } else {\n            opts = opts & ~O_NONBLOCK;\n        }\n\n        do {\n            ret = fcntl(sock, F_SETFL, opts);\n        } while (ret < 0 && errno == EINTR);\n\n        if (ret < 0) {\n            swoole_sys_warning(\"fcntl(%d, SETFL, opts) failed\", sock);\n            return false;\n        }\n    }\n\n#ifdef FD_CLOEXEC\n    if (cloexec >= 0) {\n        do {\n            opts = fcntl(sock, F_GETFD);\n        } while (opts < 0 && errno == EINTR);\n\n        if (opts < 0) {\n            swoole_sys_warning(\"fcntl(%d, GETFL) failed\", sock);\n        }\n\n        if (cloexec) {\n            opts = opts | FD_CLOEXEC;\n        } else {\n            opts = opts & ~FD_CLOEXEC;\n        }\n\n        do {\n            ret = fcntl(sock, F_SETFD, opts);\n        } while (ret < 0 && errno == EINTR);\n\n        if (ret < 0) {\n            swoole_sys_warning(\"fcntl(%d, SETFD, opts) failed\", sock);\n            return false;\n        }\n    }\n#endif\n\n    return true;\n}\n\nbool Socket::set_fd_option(int _nonblock, int _cloexec) {\n    if (_fcntl_set_option(fd, _nonblock, _cloexec)) {\n        nonblock = _nonblock;\n        cloexec = _cloexec;\n        return true;\n    } else {\n        return false;\n    }\n}\n\nvoid Socket::set_timeout(double timeout, int type) {\n    if (timeout == 0) {\n        return;\n    }\n    if (type & SW_TIMEOUT_DNS) {\n        dns_timeout = timeout;\n    }\n    if (type & SW_TIMEOUT_CONNECT) {\n        connect_timeout = timeout;\n    }\n    if (type & SW_TIMEOUT_READ) {\n        read_timeout = timeout;\n    }\n    if (type & SW_TIMEOUT_WRITE) {\n        write_timeout = timeout;\n    }\n}\n\ndouble Socket::get_timeout(TimeoutType type) const {\n    SW_ASSERT_1BYTE(type);\n    if (type == SW_TIMEOUT_DNS) {\n        return dns_timeout;\n    } else if (type == SW_TIMEOUT_CONNECT) {\n        return connect_timeout;\n    } else if (type == SW_TIMEOUT_READ) {\n        return read_timeout;\n    } else if (type == SW_TIMEOUT_WRITE) {\n        return write_timeout;\n    } else {\n        assert(0);\n        return -1;\n    }\n}\n\nbool Socket::has_timedout() const {\n    return errno == EAGAIN || errno == ETIMEDOUT || swoole_get_last_error() == SW_ERROR_SOCKET_POLL_TIMEOUT;\n}\n\nbool Socket::has_kernel_nobufs() {\n    return std::exchange(kernel_nobufs, 0);\n}\n\nbool Socket::set_kernel_read_timeout(double timeout) {\n    if (_set_timeout(fd, SO_SNDTIMEO, timeout)) {\n        write_timeout = timeout;\n        return true;\n    } else {\n        return false;\n    }\n}\n\nbool Socket::set_kernel_write_timeout(double timeout) {\n    if (_set_timeout(fd, SO_RCVTIMEO, timeout)) {\n        read_timeout = timeout;\n        return true;\n    } else {\n        return false;\n    }\n}\n\nint Socket::handle_sendfile() {\n    Buffer *buffer = out_buffer;\n    BufferChunk *chunk = buffer->front();\n    auto *task = (SendfileRequest *) chunk->value.ptr;\n\n    if (task->corked == 0) {\n        if (task->end - task->begin > SW_SOCKET_CORK_MIN_SIZE) {\n            task->corked = cork() ? 1 : -1;\n        } else {\n            task->corked = -1;\n        }\n    }\n\n    size_t sendn = get_sendfile_chunk_size(task->begin, task->end);\n    ssize_t rv = sendfile(task->file, &task->begin, sendn);\n\n    swoole_trace(\"rv=%ld|begin=%ld|sendn=%lu|end=%lu\", rv, (long) task->begin, sendn, task->end);\n\n    if (rv <= 0) {\n        switch (catch_write_error(errno)) {\n        case SW_ERROR:\n            swoole_sys_warning(\"sendfile(%s, %ld, %zu) failed\", task->get_filename(), (long) task->begin, sendn);\n            buffer->pop();\n            return SW_OK;\n        case SW_CLOSE:\n            close_wait = 1;\n            return SW_ERR;\n        case SW_WAIT:\n            send_wait = 1;\n            return SW_ERR;\n        default:\n            break;\n        }\n    } else {\n        if (send_timer) {\n            last_sent_time = time<std::chrono::milliseconds>(true);\n        }\n    }\n\n    // sendfile completed\n    if (task->begin == task->end) {\n        if (task->corked == 1) {\n            uncork();\n            task->corked = 0;\n        }\n        buffer->pop();\n    }\n\n    return SW_OK;\n}\n\n/**\n * send buffer to client\n */\nint Socket::handle_send() {\n    Buffer *buffer = out_buffer;\n    BufferChunk *chunk = buffer->front();\n    uint32_t sendn = chunk->length - chunk->offset;\n\n    if (sendn == 0) {\n        buffer->pop();\n        return SW_OK;\n    }\n\n    ssize_t ret = send(chunk->value.str + chunk->offset, sendn, 0);\n    if (ret < 0) {\n        switch (catch_write_error(errno)) {\n        case SW_ERROR:\n            swoole_sys_warning(\"send to fd[%d] failed\", fd);\n            break;\n        case SW_CLOSE:\n            close_wait = 1;\n            return SW_ERR;\n        case SW_WAIT:\n            send_wait = 1;\n            return SW_ERR;\n        default:\n            break;\n        }\n        return SW_OK;\n    }\n    // chunk full send\n    else if (ret == sendn) {\n        buffer->pop();\n    } else {\n        chunk->offset += ret;\n        // kernel is not fully processing and socket buffer is full\n        if (ret < sendn) {\n            send_wait = 1;\n            return SW_ERR;\n        }\n    }\n    return SW_OK;\n}\n\nstatic void Socket_sendfile_destructor(BufferChunk *chunk) {\n    auto *task = static_cast<SendfileRequest *>(chunk->value.ptr);\n    delete task;\n}\n\nssize_t Socket::sendfile(const File &fp, off_t *offset, size_t length) {\n    if (ssl) {\n        return ssl_sendfile(fp, offset, length);\n    } else {\n        return swoole_sendfile(fd, fp.get_fd(), offset, length);\n    }\n}\n\nint Socket::sendfile_async(const char *filename, off_t offset, size_t length) {\n    std::unique_ptr<SendfileRequest> task(new SendfileRequest(filename, offset));\n\n    if (!check_sendfile_parameters(&task->file, offset, length, &task->end)) {\n        return SW_ERR;\n    }\n\n    if (out_buffer == nullptr) {\n        out_buffer = new Buffer(SW_SEND_BUFFER_SIZE);\n    }\n\n    BufferChunk *chunk = out_buffer->alloc(BufferChunk::TYPE_SENDFILE, 0);\n    chunk->value.ptr = task.release();\n    chunk->destroy = Socket_sendfile_destructor;\n\n    return SW_OK;\n}\n\nssize_t Socket::recv(void *_buf, size_t _n, int _flags) {\n    ssize_t total_bytes = 0;\n\n    do {\n        if (ssl) {\n            ssize_t retval = 0;\n            while (static_cast<size_t>(total_bytes) < _n) {\n                retval = ssl_recv(static_cast<char *>(_buf) + total_bytes, _n - total_bytes);\n                if (retval <= 0) {\n                    if (total_bytes == 0) {\n                        total_bytes = retval;\n                    }\n                    break;\n                } else {\n                    total_bytes += retval;\n                    if (!(nonblock || (_flags & MSG_WAITALL))) {\n                        break;\n                    }\n                }\n            }\n        } else {\n            total_bytes = ::recv(fd, _buf, _n, _flags);\n        }\n    } while (total_bytes < 0 && (errno == EINTR && !dont_restart));\n\n    if (total_bytes > 0) {\n        total_recv_bytes += total_bytes;\n        if (recv_timer) {\n            last_received_time = time<std::chrono::milliseconds>(true);\n        }\n    }\n\n    // The POLLHUP event is triggered, but Socket::recv returns EAGAIN\n    if (total_bytes < 0 && catch_read_error(errno) == SW_WAIT && event_hup) {\n        total_bytes = 0;\n    }\n\n    swoole_trace_log(SW_TRACE_SOCKET, \"recv %ld/%ld bytes, errno=%d\", total_bytes, _n, errno);\n\n    return total_bytes;\n}\n\nssize_t Socket::send(const void *_buf, size_t _n, int _flags) {\n    ssize_t retval;\n\n    do {\n        if (ssl) {\n            retval = ssl_send(_buf, _n);\n        } else {\n            retval = ::send(fd, _buf, _n, _flags);\n        }\n    } while (retval < 0 && (errno == EINTR && !dont_restart));\n\n    if (retval > 0) {\n        total_send_bytes += retval;\n        if (send_timer) {\n            last_sent_time = time<std::chrono::milliseconds>(true);\n        }\n    }\n\n    swoole_trace_log(SW_TRACE_SOCKET, \"send %ld/%ld bytes, errno=%d\", retval, _n, errno);\n\n    return retval;\n}\n\nssize_t Socket::send_async(const void *_buf, size_t _n) {\n    if (!swoole_event_is_available()) {\n        return send_sync(_buf, _n, 0);\n    } else {\n        return swoole_event_write(this, _buf, _n);\n    }\n}\n\nssize_t Socket::read_sync(void *_buf, size_t _len) {\n    ssize_t bytes = 0;\n\n    auto rv = wait_for(\n        [this, _buf, _len, &bytes]() {\n            ssize_t n = read((char *) _buf + bytes, _len - bytes);\n            CHECK_RETURN_VALUE(n, SW_READY);\n            bytes += n;\n            return SW_READY;\n        },\n        SW_EVENT_READ,\n        sec2msec(read_timeout));\n\n    return rv ? bytes : -1;\n}\n\nssize_t Socket::write_sync(const void *_buf, size_t _len) {\n    ssize_t bytes = 0;\n\n    auto rv = wait_for(\n        [this, _buf, _len, &bytes]() {\n            ssize_t n = write((char *) _buf + bytes, _len - bytes);\n            CHECK_RETURN_VALUE(n, SW_READY);\n            bytes += n;\n            return SW_READY;\n        },\n        SW_EVENT_WRITE,\n        sec2msec(write_timeout));\n\n    return rv ? bytes : -1;\n}\n\nssize_t Socket::readv(IOVector *io_vector) {\n    ssize_t retval;\n\n    do {\n        if (ssl) {\n            retval = ssl_readv(io_vector);\n        } else {\n            retval = ::readv(fd, io_vector->get_iterator(), io_vector->get_remain_count());\n            io_vector->update_iterator(retval);\n        }\n    } while (retval < 0 && errno == EINTR);\n\n    return retval;\n}\n\nssize_t Socket::writev(IOVector *io_vector) {\n    ssize_t retval;\n\n    do {\n        if (ssl) {\n            retval = ssl_writev(io_vector);\n        } else {\n            retval = ::writev(fd, io_vector->get_iterator(), io_vector->get_remain_count());\n            io_vector->update_iterator(retval);\n        }\n    } while (retval < 0 && errno == EINTR);\n\n    return retval;\n}\n\nssize_t Socket::peek(void *_buf, size_t _n, int _flags) const {\n    ssize_t retval;\n    _flags |= MSG_PEEK;\n    do {\n        if (ssl) {\n            retval = SSL_peek(ssl, _buf, _n);\n        } else {\n            retval = ::recv(fd, _buf, _n, _flags);\n        }\n    } while (retval < 0 && errno == EINTR);\n\n    swoole_trace_log(SW_TRACE_SOCKET, \"peek %ld/%ld bytes, errno=%d\", retval, _n, errno);\n\n    return retval;\n}\n\nint Socket::catch_error(const int err) {\n    switch (err) {\n    case EFAULT:\n        abort();\n        return SW_ERROR;\n    case EBADF:\n    case ENOENT:\n        return SW_INVALID;\n    case ECONNRESET:\n    case ECONNABORTED:\n    case EPIPE:\n    case ENOTCONN:\n    case ETIMEDOUT:\n    case ECONNREFUSED:\n    case ENETDOWN:\n    case ENETUNREACH:\n    case EHOSTDOWN:\n    case EHOSTUNREACH:\n    case SW_ERROR_SSL_BAD_CLIENT:\n    case SW_ERROR_SSL_RESET:\n        return SW_CLOSE;\n    case EAGAIN:\n#if EAGAIN != EWOULDBLOCK\n    case EWOULDBLOCK:\n#endif\n    case 0:\n        return SW_WAIT;\n    case ENOBUFS:\n        kernel_nobufs = true;\n        return SW_WAIT;\n    default:\n        return SW_ERROR;\n    }\n}\n\nSocketType Socket::convert_to_type(const int domain, const int type) {\n    if (domain == AF_INET && type == SOCK_STREAM) {\n        return SW_SOCK_TCP;\n    } else if (domain == AF_INET6 && type == SOCK_STREAM) {\n        return SW_SOCK_TCP6;\n    } else if (domain == AF_UNIX && type == SOCK_STREAM) {\n        return SW_SOCK_UNIX_STREAM;\n    } else if (domain == AF_INET && type == SOCK_DGRAM) {\n        return SW_SOCK_UDP;\n    } else if (domain == AF_INET6 && type == SOCK_DGRAM) {\n        return SW_SOCK_UDP6;\n    } else if (domain == AF_UNIX && type == SOCK_DGRAM) {\n        return SW_SOCK_UNIX_DGRAM;\n    } else if (domain == AF_INET && type == SOCK_RAW) {\n        return SW_SOCK_RAW;\n    } else if (domain == AF_INET6 && type == SOCK_RAW) {\n        return SW_SOCK_RAW6;\n    } else {\n        return SW_SOCK_RAW;\n    }\n}\n\nSocketType Socket::convert_to_type(std::string &host) {\n    if (host.compare(0, 6, \"unix:/\", 0, 6) == 0) {\n        host = host.substr(sizeof(\"unix:\") - 1);\n        host.erase(0, host.find_first_not_of('/') - 1);\n        return SW_SOCK_UNIX_STREAM;\n    }\n    if (host.find(':') != std::string::npos) {\n        return SW_SOCK_TCP6;\n    }\n    return SW_SOCK_TCP;\n}\n\nint Socket::get_domain_and_type(SocketType type, int *sock_domain, int *sock_type) {\n    switch (type) {\n    case SW_SOCK_TCP6:\n        *sock_domain = AF_INET6;\n        *sock_type = SOCK_STREAM;\n        break;\n    case SW_SOCK_UNIX_STREAM:\n        *sock_domain = AF_UNIX;\n        *sock_type = SOCK_STREAM;\n        break;\n    case SW_SOCK_UDP:\n        *sock_domain = AF_INET;\n        *sock_type = SOCK_DGRAM;\n        break;\n    case SW_SOCK_UDP6:\n        *sock_domain = AF_INET6;\n        *sock_type = SOCK_DGRAM;\n        break;\n    case SW_SOCK_UNIX_DGRAM:\n        *sock_domain = AF_UNIX;\n        *sock_type = SOCK_DGRAM;\n        break;\n    case SW_SOCK_TCP:\n        *sock_domain = AF_INET;\n        *sock_type = SOCK_STREAM;\n        break;\n    case SW_SOCK_RAW:\n        *sock_domain = AF_INET;\n        *sock_type = SOCK_RAW;\n        break;\n    case SW_SOCK_RAW6:\n        *sock_domain = AF_INET6;\n        *sock_type = SOCK_RAW;\n        break;\n    default:\n        return SW_ERR;\n    }\n\n    return SW_OK;\n}\n\n#ifndef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT\nstatic int ssl_check_name(const char *name, ASN1_STRING *pattern) {\n    char *s, *end;\n    size_t slen, plen;\n\n    s = (char *) name;\n    slen = strlen(name);\n\n    uchar *p = ASN1_STRING_data(pattern);\n    plen = ASN1_STRING_length(pattern);\n\n    if (swoole_strcaseeq(s, slen, (char *) p, plen)) {\n        return SW_OK;\n    }\n\n    if (plen > 2 && p[0] == '*' && p[1] == '.') {\n        plen -= 1;\n        p += 1;\n\n        end = s + slen;\n        s = swoole_strlchr(s, end, '.');\n\n        if (s == nullptr) {\n            return SW_ERR;\n        }\n\n        slen = end - s;\n\n        if (swoole_strcaseeq(s, slen, (char *) p, plen)) {\n            return SW_OK;\n        }\n    }\n    return SW_ERR;\n}\n#endif\n\nbool Socket::ssl_check_host(const char *tls_host_name) const {\n    X509 *cert = ssl_get_peer_certificate();\n    if (cert == nullptr) {\n        return false;\n    }\n#ifdef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT\n    /* X509_check_host() is only available in OpenSSL 1.0.2+ */\n    if (X509_check_host(cert, tls_host_name, strlen(tls_host_name), 0, nullptr) != 1) {\n        swoole_warning(\"X509_check_host(): no match\");\n        goto _failed;\n    }\n    goto _found;\n#else\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    altnames = (STACK_OF(GENERAL_NAME) *) X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr);\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            swoole_trace(\"SSL subjectAltName: \\\"%.*s\\\"\", ASN1_STRING_length(str), ASN1_STRING_data(str));\n\n            if (ssl_check_name(tls_host_name, str) == SW_OK) {\n                swoole_trace(\"SSL subjectAltName: match\");\n                GENERAL_NAMES_free(altnames);\n                goto _found;\n            }\n        }\n\n        swoole_trace(\"SSL subjectAltName: no match\");\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    sname = X509_get_subject_name(cert);\n\n    if (sname == nullptr) {\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        swoole_trace(\"SSL commonName: \\\"%.*s\\\"\", ASN1_STRING_length(str), ASN1_STRING_data(str));\n\n        if (ssl_check_name(tls_host_name, str) == SW_OK) {\n            swoole_trace(\"SSL commonName: match\");\n            goto _found;\n        }\n    }\n    swoole_trace(\"SSL commonName: no match\");\n#endif\n\n_failed:\n    X509_free(cert);\n    return false;\n\n_found:\n    X509_free(cert);\n    return true;\n}\n\nbool Socket::ssl_verify(bool allow_self_signed) const {\n    long err = SSL_get_verify_result(ssl);\n    switch (err) {\n    case X509_V_OK:\n        break;\n    case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:\n        if (allow_self_signed) {\n            break;\n        } else {\n            swoole_error_log(\n                SW_LOG_NOTICE, SW_ERROR_SSL_VERIFY_FAILED, \"self signed certificate from fd#%d is not allowed\", fd);\n            return false;\n        }\n    default:\n        swoole_error_log(SW_LOG_NOTICE,\n                         SW_ERROR_SSL_VERIFY_FAILED,\n                         \"can not verify peer from fd#%d with error#%ld: %s\",\n                         fd,\n                         err,\n                         X509_verify_cert_error_string(err));\n        return false;\n    }\n\n    return true;\n}\n\nX509 *Socket::ssl_get_peer_certificate() const {\n    if (!ssl) {\n        return nullptr;\n    }\n    return SSL_get_peer_certificate(ssl);\n}\n\nSTACK_OF(X509) * Socket::ssl_get_peer_cert_chain() const {\n    if (!ssl) {\n        return nullptr;\n    }\n    return SSL_get_peer_cert_chain(ssl);\n}\n\nstatic int _ssl_read_x509_file(X509 *cert, char *buffer, size_t length) {\n    BIO *bio = BIO_new(BIO_s_mem());\n    ON_SCOPE_EXIT {\n        BIO_free(bio);\n    };\n\n    if (bio == nullptr) {\n        swoole_warning(\"BIO_new() failed\");\n        return -1;\n    }\n\n    if (PEM_write_bio_X509(bio, cert) == 0) {\n        swoole_warning(\"PEM_write_bio_X509() failed\");\n        return -1;\n    }\n\n    int len = BIO_pending(bio);\n    if (len < 0 && len > static_cast<int>(length)) {\n        swoole_warning(\"certificate length[%d] is too big\", len);\n        return -1;\n    }\n    return BIO_read(bio, buffer, len);\n}\n\nstd::vector<std::string> Socket::ssl_get_peer_cert_chain(int limit) const {\n    std::vector<std::string> list;\n    STACK_OF(X509) *chain = ssl_get_peer_cert_chain();\n    if (chain == nullptr) {\n        return list;\n    }\n    auto n = sk_X509_num(chain);\n\n#ifdef OPENSSL_IS_BORINGSSL\n    n = std::min((int) n, limit);\n#else\n    n = std::min(n, limit);\n#endif\n\n    SW_LOOP_N(n) {\n        X509 *cert = sk_X509_value(chain, i);\n        auto rv = _ssl_read_x509_file(cert, sw_tg_buffer()->str, sw_tg_buffer()->size);\n        if (rv > 0) {\n            list.emplace_back(sw_tg_buffer()->str, rv);\n        }\n    }\n    return list;\n}\n\nbool Socket::ssl_get_peer_certificate(String *buf) const {\n    int n = ssl_get_peer_certificate(buf->str, buf->size);\n    if (n < 0) {\n        return false;\n    } else {\n        buf->length = n;\n        return true;\n    }\n}\n\nint Socket::ssl_get_peer_certificate(char *buffer, size_t length) const {\n    X509 *cert = ssl_get_peer_certificate();\n    if (cert == nullptr) {\n        return SW_ERR;\n    }\n    ON_SCOPE_EXIT {\n        if (cert) {\n            X509_free(cert);\n        }\n    };\n    return _ssl_read_x509_file(cert, buffer, length);\n}\n\nconst char *Socket::ssl_get_error_reason(int *reason) {\n    ulong_t error = ERR_get_error();\n    if (reason) {\n        *reason = ERR_GET_REASON(error);\n    }\n    return ERR_reason_error_string(error);\n}\n\nReturnCode Socket::ssl_accept() {\n    ssl_clear_error();\n\n    int n = SSL_accept(ssl);\n    /**\n     * The TLS/SSL handshake was successfully completed\n     */\n    if (n == 1) {\n        ssl_state = SW_SSL_STATE_READY;\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\n#ifdef SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS\n        if (ssl->s3) {\n            ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;\n        }\n#endif\n#endif\n        return SW_READY;\n    }\n    /**\n     * The TLS/SSL handshake was not successful but was shutdown.\n     */\n    else if (n == 0) {\n        return SW_ERROR;\n    }\n\n    long err = SSL_get_error(ssl, n);\n    if (err == SSL_ERROR_WANT_READ) {\n        ssl_want_read = 1;\n        ssl_want_write = 0;\n        return SW_WAIT;\n    } else if (err == SSL_ERROR_WANT_WRITE) {\n        ssl_want_read = 0;\n        ssl_want_write = 1;\n        return SW_WAIT;\n    } else if (err == SSL_ERROR_SSL) {\n        int reason;\n        const char *error_string = ssl_get_error_reason(&reason);\n        swoole_warning(\"bad SSL client[%s:%d], reason=%d, error_string=%s\",\n                       info.get_addr(),\n                       info.get_port(),\n                       reason,\n                       error_string);\n        return SW_ERROR;\n    } else if (err == SSL_ERROR_SYSCALL) {\n#ifdef SW_SUPPORT_DTLS\n        if (dtls && errno == 0) {\n            ssl_want_read = 1;\n            return SW_WAIT;\n        }\n#endif\n        return SW_ERROR;\n    }\n    swoole_warning(\"SSL_do_handshake() failed. Error: %s[%ld|%d]\", strerror(errno), err, errno);\n    return SW_ERROR;\n}\n\nint Socket::ssl_connect() {\n    ssl_clear_error();\n\n    int n = SSL_connect(ssl);\n    if (n == 1) {\n        ssl_state = SW_SSL_STATE_READY;\n\n#ifdef SW_LOG_TRACE_OPEN\n        const char *ssl_version = SSL_get_version(ssl);\n        const char *ssl_cipher = SSL_get_cipher_name(ssl);\n        swoole_trace_log(SW_TRACE_SSL, \"connected (%s %s)\", ssl_version, ssl_cipher);\n#endif\n\n        return SW_OK;\n    }\n\n    long err = SSL_get_error(ssl, n);\n    if (err == SSL_ERROR_WANT_READ) {\n        ssl_want_read = 1;\n        ssl_want_write = 0;\n        ssl_state = SW_SSL_STATE_WAIT_STREAM;\n        return SW_OK;\n    } else if (err == SSL_ERROR_WANT_WRITE) {\n        ssl_want_read = 0;\n        ssl_want_write = 1;\n        ssl_state = SW_SSL_STATE_WAIT_STREAM;\n        return SW_OK;\n    } else if (err == SSL_ERROR_ZERO_RETURN) {\n        swoole_debug(\"SSL_connect(fd=%d) closed\", fd);\n        swoole_set_last_error(SW_ERROR_SSL_RESET);\n        return SW_ERR;\n    } else if (err == SSL_ERROR_SYSCALL) {\n        if (n) {\n            swoole_set_last_error(errno);\n            return SW_ERR;\n        }\n    } else {\n        swoole_set_last_error(SW_ERROR_SSL_HANDSHAKE_FAILED);\n    }\n\n    ulong_t err_code = ERR_get_error();\n    char error_buf[512];\n    ERR_error_string_n(err_code, error_buf, sizeof(error_buf));\n    swoole_notice(\"ssl_connect(fd=%d) to server[%s:%d] failed. Error: %s[%ld|%d]\",\n                  fd,\n                  info.get_addr(),\n                  info.get_port(),\n                  error_buf,\n                  err,\n                  ERR_GET_REASON(err_code));\n\n    return SW_ERR;\n}\n\nssize_t Socket::ssl_sendfile(const File &fp, off_t *_offset, size_t _size) {\n    char buf[SW_BUFFER_SIZE_BIG];\n    ssize_t readn = _size > sizeof(buf) ? sizeof(buf) : _size;\n\n    ssize_t n = fp.pread(buf, readn, *_offset);\n    if (n > 0) {\n        ssize_t ret = ssl_send(buf, n);\n        if (ret < 0) {\n            if (catch_write_error(errno) == SW_ERROR) {\n                swoole_sys_warning(\"write() failed\");\n            }\n        } else {\n            *_offset += ret;\n        }\n        swoole_trace_log(SW_TRACE_REACTOR, \"fd=%d, readn=%ld, n=%ld, ret=%ld\", fd, readn, n, ret);\n        return ret;\n    } else {\n        swoole_sys_warning(\"pread() failed\");\n        return SW_ERR;\n    }\n}\n\nbool Socket::ssl_shutdown() {\n    if (ssl_closed_) {\n        return false;\n    }\n    if (SSL_in_init(ssl)) {\n        return false;\n    }\n    /**\n     * If the peer close first, local should be set to quiet mode and do not send any data,\n     * otherwise the peer will send RST segment.\n     */\n    if (ssl_quiet_shutdown) {\n        SSL_set_quiet_shutdown(ssl, 1);\n    }\n\n    int mode = SSL_get_shutdown(ssl);\n    SSL_set_shutdown(ssl, mode | SSL_RECEIVED_SHUTDOWN | SSL_SENT_SHUTDOWN);\n\n    int n = SSL_shutdown(ssl);\n    ssl_closed_ = 1;\n    swoole_trace(\"SSL_shutdown: %d\", n);\n\n    int sslerr = 0;\n    /* before 0.9.8m SSL_shutdown() returned 0 instead of -1 on errors */\n    if (n != 1 && ERR_peek_error()) {\n        sslerr = SSL_get_error(ssl, n);\n        swoole_trace(\"SSL_get_error: %d\", sslerr);\n    }\n\n    if (!(n == 1 || sslerr == 0 || sslerr == SSL_ERROR_ZERO_RETURN)) {\n        int reason;\n        const char *error_string = ssl_get_error_reason(&reason);\n        swoole_warning(\"SSL_shutdown() failed, reason=%d, error_string=%s\", reason, error_string);\n        return false;\n    }\n\n    return true;\n}\n\nvoid Socket::ssl_close() {\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    if (!ssl_closed_) {\n        ssl_shutdown();\n    }\n    SSL_free(ssl);\n    ssl = nullptr;\n}\n\nvoid Socket::ssl_catch_error() const {\n    int level = SW_LOG_NOTICE;\n    int reason = ERR_GET_REASON(ERR_peek_error());\n\n#if 0\n    /* handshake failures */\n    switch (reason)\n    {\n    case SSL_R_BAD_CHANGE_CIPHER_SPEC: /*  103 */\n    case SSL_R_BLOCK_CIPHER_PAD_IS_WRONG: /*  129 */\n    case SSL_R_DIGEST_CHECK_FAILED: /*  149 */\n    case SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST: /*  151 */\n    case SSL_R_EXCESSIVE_MESSAGE_SIZE: /*  152 */\n    case SSL_R_LENGTH_MISMATCH:/*  159 */\n    case SSL_R_NO_CIPHERS_PASSED:/*  182 */\n    case SSL_R_NO_CIPHERS_SPECIFIED:/*  183 */\n    case SSL_R_NO_COMPRESSION_SPECIFIED: /*  187 */\n    case SSL_R_NO_SHARED_CIPHER:/*  193 */\n    case SSL_R_RECORD_LENGTH_MISMATCH: /*  213 */\n#ifdef SSL_R_PARSE_TLSEXT\n    case SSL_R_PARSE_TLSEXT:/*  227 */\n#endif\n    case SSL_R_UNEXPECTED_MESSAGE:/*  244 */\n    case SSL_R_UNEXPECTED_RECORD:/*  245 */\n    case SSL_R_UNKNOWN_ALERT_TYPE: /*  246 */\n    case SSL_R_UNKNOWN_PROTOCOL:/*  252 */\n    case SSL_R_WRONG_VERSION_NUMBER:/*  267 */\n    case SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC: /*  281 */\n#ifdef SSL_R_RENEGOTIATE_EXT_TOO_LONG\n    case SSL_R_RENEGOTIATE_EXT_TOO_LONG:/*  335 */\n    case SSL_R_RENEGOTIATION_ENCODING_ERR:/*  336 */\n    case SSL_R_RENEGOTIATION_MISMATCH:/*  337 */\n#endif\n#ifdef SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED\n    case SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED: /*  338 */\n#endif\n#ifdef SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING\n    case SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING:/*  345 */\n#endif\n#ifdef SSL_R_INAPPROPRIATE_FALLBACK\n    case SSL_R_INAPPROPRIATE_FALLBACK: /*  373 */\n#endif\n    case 1000:/* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */\n    case SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE:/* 1010 */\n    case SSL_R_SSLV3_ALERT_BAD_RECORD_MAC:/* 1020 */\n    case SSL_R_TLSV1_ALERT_DECRYPTION_FAILED:/* 1021 */\n    case SSL_R_TLSV1_ALERT_RECORD_OVERFLOW:/* 1022 */\n    case SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE:/* 1030 */\n    case SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE:/* 1040 */\n    case SSL_R_SSLV3_ALERT_NO_CERTIFICATE:/* 1041 */\n    case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:/* 1042 */\n    case SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE: /* 1043 */\n    case SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED:/* 1044 */\n    case SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED:/* 1045 */\n    case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:/* 1046 */\n    case SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER:/* 1047 */\n    case SSL_R_TLSV1_ALERT_UNKNOWN_CA:/* 1048 */\n    case SSL_R_TLSV1_ALERT_ACCESS_DENIED:/* 1049 */\n    case SSL_R_TLSV1_ALERT_DECODE_ERROR:/* 1050 */\n    case SSL_R_TLSV1_ALERT_DECRYPT_ERROR:/* 1051 */\n    case SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION:/* 1060 */\n    case SSL_R_TLSV1_ALERT_PROTOCOL_VERSION:/* 1070 */\n    case SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY:/* 1071 */\n    case SSL_R_TLSV1_ALERT_INTERNAL_ERROR:/* 1080 */\n    case SSL_R_TLSV1_ALERT_USER_CANCELLED:/* 1090 */\n    case SSL_R_TLSV1_ALERT_NO_RENEGOTIATION: /* 1100 */\n        level = SW_LOG_WARNING;\n        break;\n#endif\n\n    swoole_error_log(level,\n                     SW_ERROR_SSL_BAD_PROTOCOL,\n                     \"SSL connection#%d[%s:%d] protocol error[%d]\",\n                     fd,\n                     info.get_addr(),\n                     info.get_port(),\n                     reason);\n}\n\nssize_t Socket::ssl_recv(void *_buf, size_t _n) {\n    ssl_clear_error();\n\n    int n = SSL_read(ssl, _buf, _n);\n    if (n < 0) {\n        int _errno = SSL_get_error(ssl, n);\n        switch (_errno) {\n        case SSL_ERROR_WANT_READ:\n            ssl_want_read = 1;\n            errno = EAGAIN;\n            return SW_ERR;\n\n        case SSL_ERROR_WANT_WRITE:\n            ssl_want_write = 1;\n            errno = EAGAIN;\n            return SW_ERR;\n\n        case SSL_ERROR_SYSCALL:\n            return errno == 0 ? 0 : SW_ERR;\n\n        case SSL_ERROR_SSL:\n            ssl_catch_error();\n            errno = SW_ERROR_SSL_BAD_CLIENT;\n            return SW_ERR;\n\n        default:\n            break;\n        }\n    }\n    return n;\n}\n\nssize_t Socket::ssl_send(const void *_buf, size_t _n) {\n    ssl_clear_error();\n\n#ifdef SW_SUPPORT_DTLS\n    if (dtls && chunk_size && _n > chunk_size) {\n        _n = chunk_size;\n    }\n#endif\n\n    int n = SSL_write(ssl, _buf, _n);\n    if (n < 0) {\n        int _errno = SSL_get_error(ssl, n);\n        switch (_errno) {\n        case SSL_ERROR_WANT_READ:\n            ssl_want_read = 1;\n            errno = EAGAIN;\n            return SW_ERR;\n\n        case SSL_ERROR_WANT_WRITE:\n            ssl_want_write = 1;\n            errno = EAGAIN;\n            return SW_ERR;\n\n        case SSL_ERROR_SYSCALL:\n            errno = SW_ERROR_SSL_RESET;\n            return SW_ERR;\n\n        case SSL_ERROR_SSL:\n            ssl_catch_error();\n            errno = SW_ERROR_SSL_BAD_CLIENT;\n            return SW_ERR;\n\n        default:\n            break;\n        }\n    }\n    return n;\n}\n\nssize_t Socket::ssl_readv(IOVector *io_vector) {\n    ssize_t retval, total_bytes = 0;\n\n    do {\n        retval = ssl_recv(io_vector->get_iterator()->iov_base, io_vector->get_iterator()->iov_len);\n        total_bytes += retval > 0 ? retval : 0;\n        io_vector->update_iterator(retval);\n    } while (retval > 0 && io_vector->get_remain_count() > 0);\n\n    return total_bytes > 0 ? total_bytes : retval;\n}\n\nssize_t Socket::ssl_writev(IOVector *io_vector) {\n    ssize_t retval, total_bytes = 0;\n\n    do {\n        retval = ssl_send(io_vector->get_iterator()->iov_base, io_vector->get_iterator()->iov_len);\n        total_bytes += retval > 0 ? retval : 0;\n        io_vector->update_iterator(retval);\n    } while (retval > 0 && io_vector->get_remain_count() > 0);\n\n    return total_bytes > 0 ? total_bytes : retval;\n}\n\nint Socket::ssl_create(SSLContext *ssl_context, int _flags) {\n    ssl_clear_error();\n\n    ssl = SSL_new(ssl_context->get_context());\n    if (ssl == nullptr) {\n        swoole_warning(\"SSL_new() failed\");\n        return SW_ERR;\n    }\n    if (!SSL_set_fd(ssl, fd)) {\n        ulong_t err = ERR_peek_error();\n        swoole_warning(\"SSL_set_fd() failed. Error: %s[%lu]\", ERR_reason_error_string(err), err);\n        return SW_ERR;\n    }\n    if (_flags & SW_SSL_CLIENT) {\n        SSL_set_connect_state(ssl);\n    } else if (_flags & SW_SSL_SERVER) {\n        SSL_set_accept_state(ssl);\n    }\n    if (SSL_set_ex_data(ssl, swoole_ssl_get_ex_connection_index(), this) == 0) {\n        swoole_warning(\"SSL_set_ex_data() failed\");\n        return SW_ERR;\n    }\n\n#ifdef OPENSSL_IS_BORINGSSL\n    SSL_set_enable_ech_grease(ssl, ssl_context->grease);\n#endif\n\n    ssl_state = 0;\n    return SW_OK;\n}\n}  // namespace network\n\nusing network::Socket;\n\nSocket *make_socket(SocketType type, FdType fd_type, int flags) {\n    int sock_domain;\n    int sock_type;\n\n    if (Socket::get_domain_and_type(type, &sock_domain, &sock_type) < 0) {\n        swoole_warning(\"unknown socket type [%d]\", type);\n        errno = ESOCKTNOSUPPORT;\n        swoole_set_last_error(errno);\n        return nullptr;\n    }\n\n    return make_socket(type, fd_type, sock_domain, sock_type, 0, flags);\n}\n\nSocket *make_socket(SocketType type, FdType fd_type, int sock_domain, int sock_type, int socket_protocol, int flags) {\n    int sockfd = swoole::socket(sock_domain, sock_type, socket_protocol, flags);\n    if (sockfd < 0) {\n        swoole_set_last_error(errno);\n        return nullptr;\n    }\n\n    auto _socket = make_socket(sockfd, fd_type);\n    _socket->nonblock = !!(flags & SW_SOCK_NONBLOCK);\n    _socket->cloexec = !!(flags & SW_SOCK_CLOEXEC);\n    _socket->socket_type = type;\n    return _socket;\n}\n\nint socket(int sock_domain, int sock_type, int socket_protocol, int flags) {\n    bool nonblock = flags & SW_SOCK_NONBLOCK;\n    bool cloexec = flags & SW_SOCK_CLOEXEC;\n\n#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)\n    int sock_flags = 0;\n    if (nonblock) {\n        sock_flags |= SOCK_NONBLOCK;\n    }\n    if (cloexec) {\n        sock_flags |= SOCK_CLOEXEC;\n    }\n    int sockfd = ::socket(sock_domain, sock_type | sock_flags, socket_protocol);\n    if (sockfd < 0) {\n        return sockfd;\n    }\n#else\n    int sockfd = ::socket(sock_domain, sock_type, socket_protocol);\n    if (sockfd < 0) {\n        return sockfd;\n    }\n    if (nonblock || cloexec) {\n        if (!network::_fcntl_set_option(sockfd, nonblock ? 1 : -1, cloexec ? 1 : -1)) {\n            close(sockfd);\n            return sockfd;\n        }\n    }\n#endif\n    return sockfd;\n}\n\nSocket *make_server_socket(SocketType type, const char *address, int port, int backlog) {\n    Socket *sock = swoole::make_socket(type, SW_FD_STREAM_SERVER, SW_SOCK_CLOEXEC);\n    if (sock == nullptr) {\n        swoole_sys_warning(\"socket() failed\");\n        return nullptr;\n    }\n    if (sock->bind(address, port) < 0) {\n        swoole_sys_warning(\"bind(%d, %s:%d, %d) failed\", sock->get_fd(), address, port, backlog);\n        goto __cleanup;\n    }\n    if (sock->is_stream() && sock->listen(backlog) < 0) {\n        swoole_sys_warning(\"listen(%d, %s:%d, %d) failed\", sock->get_fd(), address, port, backlog);\n        goto __cleanup;\n    }\n    if (sock->get_name() < 0) {\n        swoole_sys_warning(\"getsockname(%d) failed\", sock->get_fd());\n    __cleanup:\n        sock->free();\n        return nullptr;\n    }\n    return sock;\n}\n\nSocket *make_socket(int fd, FdType fd_type) {\n    auto *socket = new Socket();\n    socket->fd = fd;\n    socket->fd_type = fd_type;\n    socket->removed = 1;\n    return socket;\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "src/network/stream.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_string.h\"\n#include \"swoole_socket.h\"\n#include \"swoole_protocol.h\"\n#include \"swoole_client.h\"\n\nnamespace swoole {\nnamespace network {\n\nstatic void Stream_onConnect(Client *cli) {\n    auto *stream = static_cast<Stream *>(cli->object);\n    if (stream->cancel) {\n        cli->close();\n    }\n    *reinterpret_cast<uint32_t *>(stream->buffer->str) = ntohl(stream->buffer->length - 4);\n    if (cli->send(stream->buffer->str, stream->buffer->length, 0) < 0) {\n        cli->close();\n    } else {\n        delete stream->buffer;\n        stream->buffer = nullptr;\n    }\n}\n\nstatic void Stream_onError(const Client *cli) {\n    auto *stream = static_cast<Stream *>(cli->object);\n    stream->errCode = swoole_get_last_error();\n\n    swoole_error_log(SW_LOG_WARNING,\n                     SW_ERROR_SERVER_CONNECT_FAIL,\n                     \" connect() failed (%d: %s) while connecting to worker process\",\n                     stream->errCode,\n                     swoole_strerror(stream->errCode));\n\n    if (!stream->response) {\n        return;\n    }\n\n    stream->response(stream, nullptr, 0);\n    delete stream;\n}\n\nstatic void Stream_onReceive(const Client *cli, const char *data, uint32_t length) {\n    auto *stream = static_cast<Stream *>(cli->object);\n    if (length == 4) {\n        cli->socket->close_wait = 1;\n    } else {\n        stream->response(stream, data + 4, length - 4);\n    }\n}\n\nstatic void Stream_onClose(Client *cli) {\n    swoole_event_defer(\n        [](void *data) {\n            const auto *cli = static_cast<Client *>(data);\n            delete static_cast<Stream *>(cli->object);\n        },\n        cli);\n}\n\nStream::Stream(const char *dst_host, int dst_port, SocketType type) : client(type, true) {\n    if (client.socket == nullptr) {\n        return;\n    }\n\n    client.onConnect = Stream_onConnect;\n    client.onReceive = Stream_onReceive;\n    client.onError = Stream_onError;\n    client.onClose = Stream_onClose;\n    client.object = this;\n\n    client.open_length_check = true;\n    set_protocol(&client.protocol);\n\n    if (client.connect(dst_host, dst_port, -1, 0) < 0) {\n        swoole_sys_warning(\"failed to connect to [%s:%d]\", dst_host, dst_port);\n        return;\n    }\n    connected = true;\n}\n\nStream *Stream::create(const char *dst_host, int dst_port, SocketType type) {\n    auto *stream = new Stream(dst_host, dst_port, type);\n    if (!stream->connected) {\n        delete stream;\n        return nullptr;\n    } else {\n        return stream;\n    }\n}\n\nStream::~Stream() {\n    delete buffer;\n}\n\n/**\n * Stream Protocol: Length(32bit/Network Byte Order) + Body\n */\nvoid Stream::set_protocol(Protocol *protocol) {\n    protocol->get_package_length = Protocol::default_length_func;\n    protocol->package_length_type = 'N';\n    protocol->package_length_size = swoole_type_size(protocol->package_length_type);\n    protocol->package_body_offset = 4;\n    protocol->package_length_offset = 0;\n}\n\nvoid Stream::set_max_length(uint32_t max_length) {\n    client.protocol.package_max_length = max_length;\n}\n\nint Stream::send(const char *data, size_t length) {\n    assert(data != nullptr);\n    assert(length > 0);\n    if (buffer == nullptr) {\n        buffer = new String(swoole_size_align(length + 4, swoole_pagesize()));\n        buffer->length = 4;\n    }\n    buffer->append(data, length);\n    return SW_OK;\n}\n\nssize_t Stream::recv_sync(Socket *sock, void *_buf, size_t _len) {\n    int tmp = 0;\n    ssize_t ret = sock->recv_sync(&tmp, sizeof(tmp), MSG_WAITALL);\n    if (ret <= 0) {\n        return SW_ERR;\n    }\n    const int length = static_cast<int>(ntohl(tmp));\n    if (length <= 0 || length > static_cast<int>(_len)) {\n        return SW_ERR;\n    }\n    return sock->recv_sync(_buf, length, MSG_WAITALL);\n}\n\n}  // namespace network\n}  // namespace swoole\n"
  },
  {
    "path": "src/os/async_thread.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_socket.h\"\n#include \"swoole_reactor.h\"\n#include \"swoole_pipe.h\"\n#include \"swoole_async.h\"\n#include \"swoole_util.h\"\n#include \"swoole_thread.h\"\n\n#include <thread>\n#include <atomic>\n#include <unordered_map>\n#include <chrono>\n#include <condition_variable>\n#include <mutex>\n#include <queue>\n#include <system_error>\n\nstatic std::mutex async_thread_lock;\nstatic std::shared_ptr<swoole::async::ThreadPool> async_thread_pool;\n\nswoole::AsyncThreads *sw_async_threads() {\n    return SwooleTG.async_threads;\n}\n\nnamespace swoole {\nnamespace async {\n//-------------------------------------------------------------------------------\nclass EventQueue {\n  public:\n    void push(AsyncEvent *event) {\n        queue_.push(event);\n    }\n\n    AsyncEvent *pop() {\n        if (queue_.empty()) {\n            return nullptr;\n        }\n        AsyncEvent *retval = queue_.front();\n        queue_.pop();\n        return retval;\n    }\n\n    double get_max_wait_time() const {\n        if (queue_.empty()) {\n            return 0;\n        }\n        const AsyncEvent *event = queue_.front();\n        return microtime() - event->timestamp;\n    }\n\n    size_t count() const {\n        return queue_.size();\n    }\n\n    bool empty() const {\n        return queue_.empty();\n    }\n\n  private:\n    std::queue<AsyncEvent *> queue_;\n};\n\nclass ThreadPool {\n  public:\n    ThreadPool(size_t _core_worker_num, size_t _worker_num, double _max_wait_time, double _max_idle_time) {\n        running = false;\n\n        core_worker_num = _core_worker_num == 0 ? SW_CPU_NUM : SW_MAX(1, _core_worker_num);\n        worker_num = _worker_num == 0 ? SW_CPU_NUM * SW_AIO_THREAD_NUM_MULTIPLE : SW_MAX(core_worker_num, _worker_num);\n        max_wait_time = _max_wait_time == 0 ? SW_AIO_TASK_MAX_WAIT_TIME : _max_wait_time;\n        max_idle_time = _max_idle_time == 0 ? SW_AIO_THREAD_MAX_IDLE_TIME : _max_idle_time;\n    }\n\n    ~ThreadPool() {\n        shutdown();\n    }\n\n    bool is_running() const {\n        return running;\n    }\n\n    bool start() {\n        running = true;\n        current_task_id = 0;\n        for (size_t i = 0; i < core_worker_num; i++) {\n            create_thread(true);\n        }\n        return true;\n    }\n\n    bool shutdown() {\n        if (!running) {\n            return false;\n        }\n\n        event_mutex.lock();\n        running = false;\n        event_mutex.unlock();\n        _cv.notify_all();\n\n        for (auto &i : threads) {\n            std::thread *_thread = i.second;\n            if (_thread->joinable()) {\n                _thread->join();\n            }\n            delete _thread;\n        }\n        threads.clear();\n\n        return true;\n    }\n\n    void schedule() {\n        if (n_waiting == 0 && threads.size() < worker_num && max_wait_time > 0) {\n            double _max_wait_time = queue_.get_max_wait_time();\n            if (_max_wait_time > max_wait_time) {\n                size_t n = 1;\n                /**\n                 * maybe we can find a better strategy\n                 */\n                if (threads.size() + n > worker_num) {\n                    n = worker_num - threads.size();\n                }\n                swoole_trace_log(SW_TRACE_AIO,\n                                 \"Create %zu thread due to wait %fs, we will have %zu threads\",\n                                 n,\n                                 _max_wait_time,\n                                 threads.size() + n);\n                while (n--) {\n                    create_thread();\n                }\n            }\n        }\n    }\n\n    AsyncEvent *dispatch(const AsyncEvent *request) {\n        auto _event_copy = new AsyncEvent(*request);\n        event_mutex.lock();\n        schedule();\n        _event_copy->task_id = current_task_id++;\n        _event_copy->timestamp = microtime();\n        _event_copy->pipe_socket = SwooleTG.async_threads->write_socket;\n        queue_.push(_event_copy);\n        event_mutex.unlock();\n        _cv.notify_one();\n        swoole_debug(\"push and notify one: %f\", microtime());\n        return _event_copy;\n    }\n\n    size_t get_worker_num() const {\n        return threads.size();\n    }\n\n    size_t get_queue_size() {\n        std::unique_lock<std::mutex> lock(event_mutex);\n        return queue_.count();\n    }\n\n    void release_thread(std::thread::id tid) {\n        auto i = threads.find(tid);\n        if (i == threads.end()) {\n            swoole_warning(\"AIO thread#%s is missing\", swoole_thread_id_to_str(tid).c_str());\n            return;\n        }\n        std::thread *_thread = i->second;\n        swoole_trace_log(SW_TRACE_AIO,\n                         \"release idle thread#%s, we have %zu now\",\n                         swoole_thread_id_to_str(tid).c_str(),\n                         threads.size() - 1);\n        if (_thread->joinable()) {\n            _thread->join();\n        }\n        threads.erase(i);\n        delete _thread;\n    }\n\n    static void release_callback(AsyncEvent *event) {\n        auto *tid = static_cast<std::thread::id *>(event->object);\n        SwooleTG.async_threads->pool->release_thread(*tid);\n        delete tid;\n        // balance\n        SwooleTG.async_threads->task_num++;\n    }\n\n    void notify_one() {\n        _cv.notify_one();\n    }\n\n  private:\n    void create_thread(bool is_core_worker = false);\n    void main_func(bool is_core_worker);\n\n    size_t core_worker_num;\n    size_t worker_num;\n    double max_wait_time;\n    double max_idle_time;\n\n    bool running;\n\n    std::atomic<size_t> n_waiting{0};\n    std::atomic<size_t> n_closing{0};\n    size_t current_task_id = 0;\n    std::unordered_map<std::thread::id, std::thread *> threads;\n    EventQueue queue_;\n    std::mutex event_mutex;\n    std::condition_variable _cv;\n};\n\nvoid ThreadPool::main_func(const bool is_core_worker) {\n    bool exit_flag = false;\n    swoole_thread_init(false);\n\n    while (running) {\n        bool timeout = false;\n        std::unique_lock<std::mutex> lock(event_mutex);\n        ++n_waiting;\n        if (is_core_worker || max_idle_time <= 0) {\n            _cv.wait(lock, [this] { return !queue_.empty() || !running; });\n        } else {\n            timeout = !_cv.wait_for(lock,\n                                    std::chrono::microseconds(static_cast<long>(max_idle_time) * 1000 * 1000),\n                                    [this] { return !queue_.empty() || !running; });\n        }\n        --n_waiting;\n\n        AsyncEvent *event = queue_.pop();\n        lock.unlock();\n        swoole_debug(\"%s: %f\", event ? \"pop 1 event\" : \"no event\", microtime());\n        if (event) {\n            if (sw_unlikely(event->handler == nullptr)) {\n                event->error = SW_ERROR_AIO_BAD_REQUEST;\n                event->retval = -1;\n            } else if (sw_unlikely(event->canceled)) {\n                event->error = SW_ERROR_AIO_CANCELED;\n                event->retval = -1;\n            } else {\n                event->handler(event);\n            }\n\n            swoole_trace_log(SW_TRACE_AIO,\n                             \"aio_thread %s. ret=%ld, error=%d\",\n                             event->retval > 0 ? \"ok\" : \"failed\",\n                             event->retval,\n                             event->error);\n\n        _send_event:\n            if (event->pipe_socket->write_sync(&event, sizeof(event)) <= 0) {\n                swoole_sys_warning(\"sendto swoole_aio_pipe_write failed\");\n                delete event;\n            }\n            // exit\n            if (exit_flag) {\n                --n_closing;\n                break;\n            }\n        } else if (timeout) {\n            if (n_closing != 0) {\n                // wait for the next round\n                continue;\n            }\n            /* notifies the main thread to release this thread */\n            event = new AsyncEvent;\n            event->object = new std::thread::id(std::this_thread::get_id());\n            event->callback = release_callback;\n            event->pipe_socket = SwooleG.aio_default_socket;\n            event->canceled = false;\n\n            ++n_closing;\n            exit_flag = true;\n            goto _send_event;\n        }\n    }\n    swoole_thread_clean(false);\n}\n\nvoid ThreadPool::create_thread(const bool is_core_worker) {\n    try {\n        auto *_thread = new std::thread([this, is_core_worker]() { main_func(is_core_worker); });\n        threads[_thread->get_id()] = _thread;\n    } catch (const std::system_error &e) {\n        swoole_sys_notice(\"create aio thread failed, please check your system configuration or adjust aio_worker_num\");\n    }\n}\n\nAsyncEvent *dispatch(const AsyncEvent *request) {\n    if (sw_unlikely(!SwooleTG.async_threads)) {\n        SwooleTG.async_threads = new AsyncThreads();\n    }\n    AsyncEvent *event = SwooleTG.async_threads->pool->dispatch(request);\n    if (sw_likely(event)) {\n        SwooleTG.async_threads->task_num++;\n    }\n    return event;\n}\n\n//-------------------------------------------------------------------------------\n}  // namespace async\n\nint AsyncThreads::callback(Reactor *reactor, Event *event) {\n    AsyncEvent *events[SW_AIO_EVENT_NUM];\n    ssize_t n = event->socket->read(events, sizeof(AsyncEvent *) * SW_AIO_EVENT_NUM);\n    if (n < 0) {\n        swoole_sys_warning(\"read() aio events failed\");\n        return SW_ERR;\n    }\n    for (size_t i = 0; i < n / sizeof(AsyncEvent *); i++) {\n        AsyncEvent *_event = events[i];\n        if (!_event->canceled) {\n            _event->callback(_event);\n        }\n        SwooleTG.async_threads->task_num--;\n        delete _event;\n    }\n\n    return SW_OK;\n}\n\nsize_t AsyncThreads::get_worker_num() const {\n    return pool ? pool->get_worker_num() : 0;\n}\n\nsize_t AsyncThreads::get_queue_size() const {\n    return pool ? pool->get_queue_size() : 0;\n}\n\nvoid AsyncThreads::notify_one() const {\n    if (pool) {\n        pool->notify_one();\n    }\n}\n\nAsyncThreads::AsyncThreads() {\n    if (!SwooleTG.reactor) {\n        swoole_warning(\"no event loop, cannot initialized\");\n        throw Exception(SW_ERROR_WRONG_OPERATION);\n    }\n\n    pipe = new Pipe(false);\n    if (!pipe->ready()) {\n        delete pipe;\n        pipe = nullptr;\n        swoole_throw_error(SW_ERROR_SYSTEM_CALL_FAIL);\n        return;\n    }\n\n    read_socket = pipe->get_socket(false);\n    write_socket = pipe->get_socket(true);\n    read_socket->fd_type = SW_FD_AIO;\n    write_socket->fd_type = SW_FD_AIO;\n\n    swoole_event_add(read_socket, SW_EVENT_READ);\n\n    sw_reactor()->add_destroy_callback([](void *data) {\n        if (!SwooleTG.async_threads) {\n            return;\n        }\n        swoole_event_del(SwooleTG.async_threads->read_socket);\n        delete SwooleTG.async_threads;\n        SwooleTG.async_threads = nullptr;\n    });\n\n    sw_reactor()->set_exit_condition(Reactor::EXIT_CONDITION_AIO_TASK, [](Reactor *reactor, size_t &event_num) -> bool {\n        if (SwooleTG.async_threads && SwooleTG.async_threads->task_num == 0) {\n            event_num--;\n        }\n        return true;\n    });\n\n    async_thread_lock.lock();\n    if (!async_thread_pool) {\n        async_thread_pool = std::make_shared<async::ThreadPool>(\n            SwooleG.aio_core_worker_num, SwooleG.aio_worker_num, SwooleG.aio_max_wait_time, SwooleG.aio_max_idle_time);\n    }\n    if (!async_thread_pool->is_running()) {\n        async_thread_pool->start();\n    }\n    pool = async_thread_pool;\n    async_thread_lock.unlock();\n\n    SwooleG.aio_default_socket = write_socket;\n    SwooleTG.async_threads = this;\n}\n\nAsyncThreads::~AsyncThreads() {\n    pool.reset();\n    async_thread_lock.lock();\n    /**\n     * When the reference count is 1, it means that all reactor threads have ended\n     * and all aio threads can be terminated.\n     */\n    if (async_thread_pool.use_count() == 1) {\n        async_thread_pool->shutdown();\n    }\n    async_thread_lock.unlock();\n    pipe->close();\n    read_socket = nullptr;\n    write_socket = nullptr;\n    delete pipe;\n    pipe = nullptr;\n}\n};  // namespace swoole\n"
  },
  {
    "path": "src/os/base.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2018 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_socket.h\"\n#include \"swoole_async.h\"\n#include \"swoole_signal.h\"\n#include \"swoole_api.h\"\n\n#include <pwd.h>\n#include <grp.h>\n\n#if defined(__linux__)\n#include <sys/prctl.h>\n#elif defined(__FreeBSD__)\n#include <sys/procctl.h>\n#endif\n\n#if defined(__APPLE__) && defined(HAVE_CCRANDOMGENERATEBYTES)\n#include <Availability.h>\n#if (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000) ||                         \\\n    (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000)\n#define OPENSSL_APPLE_CRYPTO_RANDOM 1\n#include <CommonCrypto/CommonCryptoError.h>\n#include <CommonCrypto/CommonRandom.h>\n#endif\n#endif\n\n#include <thread>\n#include <sstream>\n\n#if __APPLE__\nint swoole_daemon(int nochdir, int noclose) {\n    pid_t pid;\n\n    if (!nochdir && chdir(\"/\") != 0) {\n        swoole_sys_warning(\"chdir() failed\");\n        return -1;\n    }\n\n    if (!noclose) {\n        int fd = open(\"/dev/null\", O_RDWR);\n        if (fd < 0) {\n            swoole_sys_warning(\"open() failed\");\n            return -1;\n        }\n\n        if (dup2(fd, 0) < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0) {\n            close(fd);\n            swoole_sys_warning(\"dup2() failed\");\n            return -1;\n        }\n\n        close(fd);\n    }\n\n    pid = swoole_fork(SW_FORK_DAEMON);\n    if (pid < 0) {\n        swoole_sys_warning(\"fork() failed\");\n        return -1;\n    }\n    if (pid > 0) {\n        _exit(0);\n    }\n    if (setsid() < 0) {\n        swoole_sys_warning(\"setsid() failed\");\n        return -1;\n    }\n    return 0;\n}\n#else\nint swoole_daemon(int nochdir, int noclose) {\n    if (swoole_fork(SW_FORK_PRECHECK) < 0) {\n        return -1;\n    }\n    auto rv = daemon(nochdir, noclose);\n    if (rv == 0) {\n    \t/**\n    \t * The daemon function forks the process multiple times, and the pid changes,\n    \t * which can lead to PHP assertion failures.\n    \t * After PHP 8.5, it is required that the process must call `refresh_memory_manager()` after forking,\n    \t * but this does not seem to take into account the invocation of the daemon function.\n    \t * If not modified, it will crash during shutdown, and users will think it is a bug in Swoole.\n    \t */\n        if (swoole_isset_hook(SW_GLOBAL_HOOK_AFTER_FORK)) {\n            swoole_call_hook(SW_GLOBAL_HOOK_AFTER_FORK, nullptr);\n        }\n    }\n    return rv;\n}\n#endif\n\n#ifdef HAVE_GETRANDOM\n#include <sys/random.h>\n#else\nstatic ssize_t getrandom(void *buffer, size_t size, unsigned int __flags) {\n#if defined(HAVE_CCRANDOMGENERATEBYTES)\n    /*\n     * arc4random_buf on macOS uses ccrng_generate internally from which\n     * the potential error is silented to respect the portable arc4random_buf interface contract\n     */\n    if (CCRandomGenerateBytes(buffer, size) == kCCSuccess) {\n        return size;\n    }\n    return -1;\n#elif defined(HAVE_ARC4RANDOM)\n    arc4random_buf(buffer, size);\n    return size;\n#else\n    int fd = open(\"/dev/urandom\", O_RDONLY);\n    if (fd < 0) {\n        return -1;\n    }\n\n    size_t read_bytes;\n    ssize_t n;\n    for (read_bytes = 0; read_bytes < size; read_bytes += (size_t) n) {\n        n = read(fd, (char *) buffer + read_bytes, size - read_bytes);\n        if (n <= 0) {\n            break;\n        }\n    }\n\n    close(fd);\n\n    return read_bytes;\n#endif\n}\n#endif\n\n#ifdef __ANDROID__\nstatic ssize_t getrandom(char *buf, size_t buflen, uint flags) {\n    int fd = open(\"/dev/urandom\", O_RDONLY);\n    if (fd < 0) {\n        return -1;\n    }\n    ssize_t n = read(fd, buf, buflen);\n    close(fd);\n    return n;\n}\n\nint pthread_getname_np(pthread_t thread, char *buf, size_t len) {\n    sw_snprintf(buf, len, \"thread-%lu\", (unsigned long) thread);\n    return 0;\n}\n#endif\n\nsize_t swoole_random_bytes(char *buf, size_t size) {\n    size_t read_bytes = 0;\n\n    while (read_bytes < size) {\n        size_t amount_to_read = size - read_bytes;\n        ssize_t n = getrandom(buf + read_bytes, amount_to_read, 0);\n        if (n == -1) {\n            if (errno == EINTR || errno == EAGAIN) {\n                continue;\n            } else {\n                break;\n            }\n        }\n        read_bytes += (size_t) n;\n    }\n\n    return read_bytes;\n}\n\nbool swoole_is_root_user() {\n    return geteuid() == 0;\n}\n\nvoid swoole_set_isolation(const std::string &group_, const std::string &user_, const std::string &chroot_) {\n    group *_group = nullptr;\n    passwd *_passwd = nullptr;\n    // get group info\n    if (!group_.empty()) {\n        _group = getgrnam(group_.c_str());\n        if (!_group) {\n            swoole_warning(\"get group [%s] info failed\", group_.c_str());\n        }\n    }\n    // get user info\n    if (!user_.empty()) {\n        _passwd = getpwnam(user_.c_str());\n        if (!_passwd) {\n            swoole_warning(\"get user [%s] info failed\", user_.c_str());\n        }\n    }\n    // set process group\n    if (_group && setgid(_group->gr_gid) < 0) {\n        swoole_sys_warning(\"setgid to [%s] failed\", group_.c_str());\n    }\n    // set process user\n    if (_passwd && setuid(_passwd->pw_uid) < 0) {\n        swoole_sys_warning(\"setuid to [%s] failed\", user_.c_str());\n    }\n    // chroot\n    if (!chroot_.empty()) {\n        if (::chroot(chroot_.c_str()) == 0) {\n            if (chdir(\"/\") < 0) {\n                swoole_sys_warning(\"chdir('/') failed\");\n            }\n        } else {\n            swoole_sys_warning(\"chroot('%s') failed\", chroot_.c_str());\n        }\n    }\n}\n\nvoid swoole_set_process_death_signal(int signal) {\n#if defined(__linux__)\n    prctl(PR_SET_PDEATHSIG, signal);\n#elif defined(__FreeBSD__)\n    procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &signal);\n#else\n#warning \"no `PDEATHSIG` supports\"\n#endif\n}\n\n#ifdef HAVE_CPU_AFFINITY\nint swoole_set_cpu_affinity(cpu_set_t *set) {\n#ifdef __FreeBSD__\n    return cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(*set), set);\n#else\n    return sched_setaffinity(getpid(), sizeof(*set), set);\n#endif\n}\n\nint swoole_get_cpu_affinity(cpu_set_t *set) {\n#ifdef __FreeBSD__\n    return cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(*set), set);\n#else\n    return sched_getaffinity(getpid(), sizeof(*set), set);\n#endif\n}\n#endif\n\n#if defined(__linux__)\n#include <sys/syscall.h> /* syscall(SYS_gettid) */\n#elif defined(__FreeBSD__)\n#include <pthread_np.h> /* pthread_getthreadid_np() */\n#elif defined(__OpenBSD__)\n#include <unistd.h> /* getthrid() */\n#elif defined(_AIX)\n#include <sys/thread.h> /* thread_self() */\n#elif defined(__NetBSD__)\n#include <lwp.h> /* _lwp_self() */\n#elif defined(__CYGWIN__) || defined(WIN32)\n#include <windows.h> /* GetCurrentThreadId() */\n#endif\n\nlong swoole_thread_get_native_id() {\n#ifdef __APPLE__\n    uint64_t native_id;\n    (void) pthread_threadid_np(NULL, &native_id);\n#elif defined(__linux__)\n    pid_t native_id = syscall(SYS_gettid);\n#elif defined(__FreeBSD__)\n    int native_id = pthread_getthreadid_np();\n#elif defined(__OpenBSD__)\n    pid_t native_id = getthrid();\n#elif defined(_AIX)\n    tid_t native_id = thread_self();\n#elif defined(__NetBSD__)\n    lwpid_t native_id = _lwp_self();\n#elif defined(__CYGWIN__) || defined(WIN32)\n    DWORD native_id = GetCurrentThreadId();\n#endif\n    return native_id;\n}\n\nstatic bool check_pthread_return_value(int rc) {\n    if (rc == 0) {\n        return true;\n    } else {\n        swoole_set_last_error(rc);\n        return false;\n    }\n}\n\nbool swoole_thread_set_name(const char *name) {\n#if defined(__APPLE__)\n    return check_pthread_return_value(pthread_setname_np(name));\n#else\n    return check_pthread_return_value(pthread_setname_np(pthread_self(), name));\n#endif\n}\n\nbool swoole_thread_get_name(char *buf, size_t len) {\n    return check_pthread_return_value(pthread_getname_np(pthread_self(), buf, len));\n}\n\nstd::string swoole_thread_id_to_str(std::thread::id id) {\n    std::stringstream ss;\n    ss << id;\n    return ss.str();\n}\n\nnamespace swoole {\nGethostbynameRequest::GethostbynameRequest(std::string _name, int _family) : name(std::move(_name)), family(_family) {}\n\nGetaddrinfoRequest::GetaddrinfoRequest(\n    std::string _hostname, int _family, int _socktype, int _protocol, std::string _service)\n    : hostname(std::move(_hostname)), service(std::move(_service)) {\n    family = _family;\n    socktype = _socktype;\n    protocol = _protocol;\n    count = 0;\n    error = 0;\n}\n\nnamespace async {\nvoid handler_gethostbyname(AsyncEvent *event) {\n    auto req = dynamic_cast<GethostbynameRequest *>(event->data.get());\n    event->retval = network::gethostbyname(req);\n    if (event->retval < 0) {\n        event->error = swoole_get_last_error();\n    } else {\n        event->error = 0;\n    }\n}\n\nvoid handler_getaddrinfo(AsyncEvent *event) {\n    auto req = dynamic_cast<GetaddrinfoRequest *>(event->data.get());\n    event->retval = network::getaddrinfo(req);\n    event->error = req->error;\n}\n}  // namespace async\n}  // namespace swoole\n"
  },
  {
    "path": "src/os/file.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_file.h\"\n\nint swoole_tmpfile(char *filename) {\n#if defined(HAVE_MKOSTEMP) && defined(HAVE_EPOLL)\n    int tmp_fd = mkostemp(filename, O_WRONLY | O_CREAT);\n#else\n    int tmp_fd = mkstemp(filename);\n#endif\n\n    if (tmp_fd < 0) {\n        swoole_sys_warning(\"mkstemp('%s') failed\", filename);\n        return SW_ERR;\n    } else {\n        return tmp_fd;\n    }\n}\n\nnamespace swoole {\n\nssize_t file_get_size(FILE *fp) {\n    fflush(fp);\n    return file_get_size(fileno(fp));\n}\n\nssize_t file_get_size(const std::string &filename) {\n    File file(filename, File::READ);\n    if (!file.ready()) {\n        swoole_set_last_error(errno);\n        return -1;\n    }\n    return file.get_size();\n}\n\nssize_t file_get_size(int fd) {\n    FileStatus file_stat;\n    if (fstat(fd, &file_stat) < 0) {\n        swoole_set_last_error(errno);\n        return -1;\n    }\n    if (!S_ISREG(file_stat.st_mode)) {\n        swoole_set_last_error(EISDIR);\n        return -1;\n    }\n    return file_stat.st_size;\n}\n\nstd::shared_ptr<String> file_get_contents(const std::string &filename) {\n    File fp(filename, O_RDONLY);\n    if (!fp.ready()) {\n        swoole_sys_warning(\"open('%s') failed\", filename.c_str());\n        return nullptr;\n    }\n\n    ssize_t filesize = fp.get_size();\n    if (filesize < 0) {\n        return nullptr;\n    } else if (filesize == 0) {\n        swoole_error_log(SW_LOG_TRACE, SW_ERROR_FILE_EMPTY, \"file[%s] is empty\", filename.c_str());\n        return nullptr;\n    } else if (filesize > SwooleG.max_file_content) {\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_FILE_TOO_LARGE, \"file[%s] is too large\", filename.c_str());\n        return nullptr;\n    }\n\n    std::shared_ptr<String> content = std::make_shared<String>(filesize + 1);\n    ssize_t read_bytes = fp.read_all(content->str, filesize);\n    content->length = read_bytes;\n    content->str[read_bytes] = '\\0';\n    return content;\n}\n\nFile make_tmpfile() {\n    char *tmpfile = sw_tg_buffer()->str;\n    size_t l = swoole_strlcpy(tmpfile, SwooleG.task_tmpfile.c_str(), SW_TASK_TMP_PATH_SIZE);\n    int tmp_fd = swoole_tmpfile(tmpfile);\n    if (tmp_fd < 0) {\n        return File(-1);\n    } else {\n        return {tmp_fd, std::string(tmpfile, l)};\n    }\n}\n\nbool file_put_contents(const std::string &filename, const char *content, size_t length) {\n    if (length == 0) {\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_FILE_EMPTY, \"content is empty\");\n        return false;\n    }\n    if (length > SwooleG.max_file_content) {\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_FILE_TOO_LARGE, \"content is too large\");\n        return false;\n    }\n    File file(filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);\n    if (!file.ready()) {\n        swoole_sys_warning(\"open('%s') failed\", filename.c_str());\n        return false;\n    }\n    return file.write_all(content, length);\n}\n\nbool file_exists(const std::string &filename) {\n    return access(filename.c_str(), F_OK) == 0;\n}\n\nFile::File(const std::string &path, int oflags) {\n    fd_ = -1;\n    open(path, oflags);\n}\n\nFile::File(const std::string &path, int oflags, int mode) {\n    fd_ = -1;\n    open(path, oflags, mode);\n}\n\nbool File::open(const std::string &path, int oflags, int mode) {\n    if (fd_ != -1) {\n        ::close(fd_);\n    }\n    if (oflags & CREATE) {\n        fd_ = ::open(path.c_str(), oflags, mode == 0 ? 0644 : mode);\n    } else {\n        fd_ = ::open(path.c_str(), oflags);\n    }\n    path_ = path;\n    flags_ = oflags;\n    return ready();\n}\n\nbool File::close() {\n    if (fd_ == -1) {\n        return false;\n    }\n    int tmp_fd = fd_;\n    fd_ = -1;\n    return ::close(tmp_fd) == 0;\n}\n\nbool File::stat(FileStatus *_stat) const {\n    if (::fstat(fd_, _stat) < 0) {\n        swoole_sys_warning(\"fstat() failed\");\n        return false;\n    }\n    return true;\n}\n\nFile::~File() {\n    if (fd_ >= 0) {\n        ::close(fd_);\n    }\n}\n\nstatic swReturnCode catch_fs_error(const ssize_t rv, const int error) {\n    if (rv == 0) {\n        return SW_CLOSE;\n    }\n    if (error == EINTR || error == EAGAIN || error == EWOULDBLOCK) {\n        return SW_CONTINUE;\n    }\n    return SW_ERROR;\n}\n\nsize_t File::write_all(const void *data, size_t len) const {\n    size_t written_bytes = 0;\n    while (written_bytes < len) {\n        ssize_t n;\n        if (flags_ & APPEND) {\n            n = write((char *) data + written_bytes, len - written_bytes);\n        } else {\n            n = pwrite((char *) data + written_bytes, len - written_bytes, written_bytes);\n        }\n        if (n > 0) {\n            written_bytes += n;\n        } else {\n            const auto rc = catch_fs_error(n, errno);\n            if (rc == SW_ERROR) {\n                swoole_sys_warning(\"pwrite(%d, %p, %lu, %lu) failed\", fd_, data, len - written_bytes, written_bytes);\n            } else if (rc == SW_CONTINUE) {\n                continue;\n            }\n            break;\n        }\n    }\n    return written_bytes;\n}\n\nsize_t File::read_all(void *buf, size_t len) const {\n    size_t read_bytes = 0;\n    while (read_bytes < len) {\n        ssize_t n = pread((char *) buf + read_bytes, len - read_bytes, read_bytes);\n        if (n > 0) {\n            read_bytes += n;\n        } else {\n            const auto rc = catch_fs_error(n, errno);\n            if (rc == SW_ERROR) {\n                swoole_sys_warning(\"pread(%d, %p, %lu, %lu) failed\", fd_, buf, len - read_bytes, read_bytes);\n            } else if (rc == SW_CONTINUE) {\n                continue;\n            }\n            break;\n        }\n    }\n    return read_bytes;\n}\n\nssize_t File::read_line(void *_buf, size_t _n) const {\n    char *buf = (char *) _buf;\n    auto offset = get_offset();\n    ssize_t read_bytes = read(buf, _n - 1);\n    if (read_bytes <= 0) {\n        return read_bytes;\n    }\n    for (ssize_t i = 0; i < read_bytes; ++i) {\n        if (buf[i] == '\\0' || buf[i] == '\\n') {\n            buf[i + 1] = '\\0';\n            set_offset(offset + i + 1);\n            return i + 1;\n        }\n    }\n    buf[read_bytes] = '\\0';\n    set_offset(offset + read_bytes + 1);\n    return read_bytes;\n}\n\nstd::shared_ptr<String> File::read_content() const {\n    ssize_t n = 0;\n    auto data = std::make_shared<String>(SW_BUFFER_SIZE_STD);\n    while (true) {\n        n = read(data->str + data->length, data->size - data->length);\n        if (n <= 0) {\n            break;\n        }\n        data->grow((size_t) n);\n    }\n    return data;\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "src/os/msg_queue.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_msg_queue.h\"\n\nnamespace swoole {\n#ifdef HAVE_MSGQUEUE\n#include <sys/ipc.h>\n#include <sys/msg.h>\n\nbool MsgQueue::destroy() {\n    if (msgctl(msg_id_, IPC_RMID, nullptr) < 0) {\n        swoole_sys_warning(\"msgctl(%d, IPC_RMID) failed\", msg_id_);\n        return false;\n    }\n    msg_id_ = -1;\n    return true;\n}\n\nvoid MsgQueue::set_blocking(const bool blocking) {\n    if (blocking == 0) {\n        flags_ = flags_ | IPC_NOWAIT;\n    } else {\n        flags_ = flags_ & (~IPC_NOWAIT);\n    }\n}\n\nMsgQueue::MsgQueue(key_t msg_key, bool blocking, int perms) {\n    if (perms <= 0 || perms >= 01000) {\n        perms = 0666;\n    }\n    msg_key_ = msg_key;\n    flags_ = 0;\n    perms_ = perms;\n    blocking_ = blocking;\n    msg_id_ = msgget(msg_key, IPC_CREAT | perms);\n    if (msg_id_ < 0) {\n        swoole_sys_warning(\"msgget() failed\");\n    } else {\n        set_blocking(blocking);\n    }\n}\n\nMsgQueue::~MsgQueue() {\n    // private queue must be destroyed\n    if (msg_key_ == IPC_PRIVATE && msg_id_ >= 0) {\n        destroy();\n    }\n}\n\nssize_t MsgQueue::pop(QueueNode *data, size_t mdata_size) const {\n    ssize_t ret = msgrcv(msg_id_, data, mdata_size, data->mtype, flags_);\n    if (ret < 0) {\n        swoole_set_last_error(errno);\n        if (errno != ENOMSG && errno != EINTR) {\n            swoole_sys_warning(\"msgrcv(%d, %zu, %ld) failed\", msg_id_, mdata_size, data->mtype);\n        }\n    }\n    return ret;\n}\n\nbool MsgQueue::push(const QueueNode *in, size_t mdata_length) const {\n    while (true) {\n        if (msgsnd(msg_id_, in, mdata_length, flags_) == 0) {\n            return true;\n        }\n        if (errno == EINTR) {\n            continue;\n        }\n        if (errno != EAGAIN) {\n            swoole_sys_warning(\"msgsnd(%d, %lu, %ld) failed\", msg_id_, mdata_length, in->mtype);\n        }\n        swoole_set_last_error(errno);\n        break;\n    }\n    return false;\n}\n\nbool MsgQueue::stat(size_t *queue_num, size_t *queue_bytes) const {\n    msqid_ds _stat;\n    if (msgctl(msg_id_, IPC_STAT, &_stat) == 0) {\n        *queue_num = _stat.msg_qnum;\n#ifndef __NetBSD__\n        *queue_bytes = _stat.msg_cbytes;\n#else\n        *queue_bytes = __stat._msg_cbytes;\n#endif\n        return true;\n    }\n    return false;\n}\n\nbool MsgQueue::set_capacity(size_t queue_bytes) const {\n    msqid_ds _stat;\n    if (msgctl(msg_id_, IPC_STAT, &_stat) != 0) {\n        return false;\n    }\n    _stat.msg_qbytes = queue_bytes;\n    if (msgctl(msg_id_, IPC_SET, &_stat)) {\n        swoole_sys_warning(\"msgctl(msqid=%d, IPC_SET, msg_qbytes=%lu) failed\", msg_id_, queue_bytes);\n        return false;\n    }\n    return true;\n}\n#else\nMsgQueue::MsgQueue(key_t msg_key, bool blocking, int perms) {\n    swoole_error(\"current platform does not support `sysvmsg`\");\n}\n\nvoid MsgQueue::set_blocking(bool blocking) {}\n\nbool MsgQueue::set_capacity(size_t queue_bytes) const {\n    return false;\n}\n\nbool MsgQueue::push(const QueueNode *in, size_t mdata_length) const {\n    return false;\n}\n\nssize_t MsgQueue::pop(QueueNode *out, size_t mdata_size) const {\n    return -1;\n}\n\nbool MsgQueue::stat(size_t *queue_num, size_t *queue_bytes) const {\n    return false;\n}\n\nbool MsgQueue::destroy() {\n    return false;\n}\n\nMsgQueue::~MsgQueue() {\n}\n#endif\n}  // namespace swoole\n"
  },
  {
    "path": "src/os/pipe.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_pipe.h\"\n#include \"swoole_socket.h\"\n\nnamespace swoole {\nusing network::Socket;\n\nvoid SocketPair::init_socket(int master_fd, int worker_fd) {\n    master_socket = make_socket(master_fd, SW_FD_PIPE);\n    worker_socket = make_socket(worker_fd, SW_FD_PIPE);\n    set_blocking(blocking);\n}\n\nPipe::Pipe(bool _blocking) : SocketPair(_blocking) {\n    if (pipe(socks) < 0) {\n        swoole_sys_warning(\"pipe() failed\");\n        return;\n    }\n    // socks[0]: (read end)\n    // socks[1]: (write end)\n    init_socket(socks[1], socks[0]);\n}\n\nvoid SocketPair::set_blocking(bool _blocking) {\n    if (_blocking) {\n        worker_socket->set_block();\n        master_socket->set_block();\n    } else {\n        worker_socket->set_nonblock();\n        master_socket->set_nonblock();\n    }\n    blocking = _blocking;\n}\n\nssize_t SocketPair::read(void *data, size_t length) const {\n    if (blocking) {\n        return worker_socket->read_sync(data, length);\n    } else {\n        return worker_socket->read(data, length);\n    }\n}\n\nssize_t SocketPair::write(const void *data, size_t length) const {\n    if (blocking) {\n        return master_socket->write_sync(data, length);\n    } else {\n        return master_socket->write(data, length);\n    }\n}\n\nvoid SocketPair::clean() const {\n    char buf[1024];\n    while (worker_socket->wait_event(0, SW_EVENT_READ) == SW_OK) {\n        if (worker_socket->read(buf, sizeof(buf)) <= 0) {\n            break;\n        }\n    }\n}\n\nbool SocketPair::close(int which) {\n    if (which == SW_PIPE_CLOSE_MASTER) {\n        if (master_socket == nullptr) {\n            return false;\n        }\n        master_socket->free();\n        master_socket = nullptr;\n    } else if (which == SW_PIPE_CLOSE_WORKER) {\n        if (worker_socket == nullptr) {\n            return false;\n        }\n        worker_socket->free();\n        worker_socket = nullptr;\n    } else {\n        close(SW_PIPE_CLOSE_MASTER);\n        close(SW_PIPE_CLOSE_WORKER);\n    }\n    return true;\n}\n\nSocketPair::~SocketPair() {\n    if (master_socket) {\n        close(SW_PIPE_CLOSE_MASTER);\n    }\n    if (worker_socket) {\n        close(SW_PIPE_CLOSE_WORKER);\n    }\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "src/os/process_pool.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_memory.h\"\n#include \"swoole_socket.h\"\n#include \"swoole_string.h\"\n#include \"swoole_msg_queue.h\"\n#include \"swoole_pipe.h\"\n#include \"swoole_server.h\"\n#include \"swoole_util.h\"\n#include \"swoole_process_pool.h\"\n#include \"swoole_client.h\"\n\nSW_THREAD_LOCAL swoole::WorkerGlobal SwooleWG = {};\n\nnamespace swoole {\n\nusing network::Socket;\nusing network::Stream;\n\nstatic inline swReturnCode catch_system_error(int error) {\n    switch (error) {\n    case SW_SUCCESS:\n    case EAGAIN:\n    case EINTR:\n        return SW_CONTINUE;\n    default:\n        return SW_ERROR;\n    }\n}\n\nstatic inline void worker_end_callback() {\n    swoole_timer_select();\n    swoole_signal_dispatch();\n}\n\n/**\n * Process manager\n */\nint ProcessPool::create(uint32_t _worker_num, key_t _msgqueue_key, swIPCMode _ipc_mode) {\n#ifndef HAVE_MSGQUEUE\n    if (_ipc_mode == SW_IPC_MSGQUEUE) {\n        swoole_warning(\"current platform does not support `sysvmsg`\");\n        return SW_ERR;\n    }\n#endif\n    worker_num = _worker_num;\n    /**\n     * Shared memory is used here\n     */\n    workers = static_cast<Worker *>(sw_mem_pool()->alloc(_worker_num * sizeof(Worker)));\n    if (workers == nullptr) {\n        swoole_sys_warning(\"malloc[1] failed\");\n        return SW_ERR;\n    }\n\n    if (create_message_box(SW_MESSAGE_BOX_SIZE) < 0) {\n        return SW_ERR;\n    }\n\n    if (_ipc_mode == SW_IPC_MSGQUEUE) {\n        use_msgqueue = 1;\n        msgqueue_key = _msgqueue_key;\n        queue = new MsgQueue(msgqueue_key);\n        if (!queue->ready()) {\n            delete queue;\n            queue = nullptr;\n            return SW_ERR;\n        }\n    } else if (_ipc_mode == SW_IPC_UNIXSOCK) {\n        pipes = new std::vector<std::shared_ptr<UnixSocket>>;\n        SW_LOOP_N(_worker_num) {\n            auto sock = new UnixSocket(true, SOCK_DGRAM);\n            if (!sock->ready()) {\n                delete sock;\n                delete pipes;\n                pipes = nullptr;\n                return SW_ERR;\n            }\n            pipes->emplace_back(sock);\n            workers[i].pipe_master = sock->get_socket(true);\n            workers[i].pipe_worker = sock->get_socket(false);\n            workers[i].pipe_object = sock;\n        }\n    } else if (_ipc_mode == SW_IPC_SOCKET) {\n        use_socket = 1;\n        stream_info_ = new StreamInfo();\n    } else {\n        _ipc_mode = SW_IPC_NONE;\n    }\n\n    map_ = new std::unordered_map<pid_t, Worker *>;\n    ipc_mode = _ipc_mode;\n    main_loop = run_with_task_protocol;\n    protocol_type_ = SW_PROTOCOL_TASK;\n    max_packet_size_ = SW_INPUT_BUFFER_SIZE;\n    max_wait_time = SW_WORKER_MAX_WAIT_TIME;\n\n    SW_LOOP_N(_worker_num) {\n        workers[i].pool = this;\n    }\n\n    return SW_OK;\n}\n\nint ProcessPool::create_message_box(size_t memory_size) {\n    message_box = Channel::make(memory_size, sizeof(EventData), SW_CHAN_LOCK | SW_CHAN_SHM);\n    if (message_box == nullptr) {\n        return SW_ERR;\n    }\n    return SW_OK;\n}\n\nint ProcessPool::create_message_bus() {\n    if (ipc_mode != SW_IPC_UNIXSOCK) {\n        swoole_error_log(\n            SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, \"not support, ipc_mode must be SW_IPC_UNIXSOCK\");\n        return SW_ERR;\n    }\n    if (message_bus) {\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_WRONG_OPERATION, \"the message bus has been created\");\n        return SW_ERR;\n    }\n    auto *msg_id = static_cast<sw_atomic_long_t *>(sw_mem_pool()->alloc(sizeof(sw_atomic_long_t)));\n    if (msg_id == nullptr) {\n        swoole_sys_warning(\"malloc[1] failed\");\n        return SW_ERR;\n    }\n    *msg_id = 1;\n    message_bus = new MessageBus();\n    message_bus->set_id_generator([msg_id]() { return sw_atomic_fetch_add(msg_id, 1); });\n    size_t ipc_max_size;\n#ifndef __linux__\n    ipc_max_size = SW_IPC_MAX_SIZE;\n#else\n    int bufsize;\n    /**\n     * Get the maximum ipc[unix socket with dgram] transmission length\n     */\n    if (workers[0].pipe_master->get_option(SOL_SOCKET, SO_SNDBUF, &bufsize) != 0) {\n        bufsize = SW_IPC_MAX_SIZE;\n    }\n    ipc_max_size = SW_MIN(bufsize, SW_IPC_BUFFER_MAX_SIZE);\n#endif\n    message_bus->set_buffer_size(ipc_max_size);\n    if (!message_bus->alloc_buffer()) {\n        return SW_ERR;\n    }\n    return SW_OK;\n}\n\nint ProcessPool::listen(const char *socket_file, int backlog) const {\n    if (ipc_mode != SW_IPC_SOCKET) {\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, \"not support, ipc_mode must be SW_IPC_SOCKET\");\n        return SW_ERR;\n    }\n    stream_info_->socket_file = sw_strdup(socket_file);\n    if (stream_info_->socket_file == nullptr) {\n        return SW_ERR;\n    }\n    stream_info_->socket_port = 0;\n    stream_info_->socket = make_server_socket(SW_SOCK_UNIX_STREAM, stream_info_->socket_file, 0, backlog);\n    if (!stream_info_->socket) {\n        return SW_ERR;\n    }\n    return SW_OK;\n}\n\nint ProcessPool::listen(const char *host, int port, int backlog) const {\n    if (ipc_mode != SW_IPC_SOCKET) {\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, \"not support, ipc_mode must be SW_IPC_SOCKET\");\n        return SW_ERR;\n    }\n    stream_info_->socket_file = sw_strdup(host);\n    if (stream_info_->socket_file == nullptr) {\n        return SW_ERR;\n    }\n    stream_info_->socket_port = port;\n    stream_info_->socket = make_server_socket(SW_SOCK_TCP, host, port, backlog);\n    if (!stream_info_->socket) {\n        return SW_ERR;\n    }\n    return SW_OK;\n}\n\nvoid ProcessPool::set_protocol(ProtocolType _protocol_type) {\n    switch (_protocol_type) {\n    case SW_PROTOCOL_TASK:\n        main_loop = run_with_task_protocol;\n        break;\n    case SW_PROTOCOL_STREAM:\n        main_loop = run_with_stream_protocol;\n        break;\n    case SW_PROTOCOL_MESSAGE:\n        main_loop = run_with_message_protocol;\n        break;\n    default:\n        abort();\n        break;\n    }\n    protocol_type_ = _protocol_type;\n}\n\nint ProcessPool::start_check() {\n    if (ipc_mode == SW_IPC_SOCKET && (stream_info_ == nullptr || stream_info_->socket == nullptr)) {\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_WRONG_OPERATION, \"must first listen to an tcp port\");\n        return SW_ERR;\n    }\n\n    running = started = true;\n    master_pid = getpid();\n    swoole_set_worker_type(SW_MASTER);\n\n    if (async) {\n        main_loop = run_async;\n    }\n\n    SW_LOOP_N(worker_num) {\n        workers[i].pool = this;\n        workers[i].id = start_id + i;\n        workers[i].type = type;\n        if (workers[i].pipe_worker) {\n            workers[i].pipe_worker->buffer_size = UINT_MAX;\n        }\n        if (workers[i].pipe_master) {\n            workers[i].pipe_master->buffer_size = UINT_MAX;\n        }\n    }\n\n    return SW_OK;\n}\n\n/**\n * start workers\n */\nint ProcessPool::start() {\n    if (start_check() < 0) {\n        return SW_ERR;\n    }\n\n    if (onStart) {\n        onStart(this);\n    }\n\n    SW_LOOP_N(worker_num) {\n        if (spawn(&(workers[i])) < 0) {\n            return SW_ERR;\n        }\n    }\n\n    return SW_OK;\n}\n\nint ProcessPool::schedule() {\n    // schedule by system message queue\n    if (schedule_by_sysvmsg) {\n        return 0;\n    }\n\n    uint32_t target_worker_id = 0;\n    uint8_t found = 0;\n\n    for (uint32_t i = 0; i < worker_num + 1; i++) {\n        target_worker_id = sw_atomic_fetch_add(&round_id, 1) % worker_num;\n        if (workers[target_worker_id].is_idle()) {\n            found = 1;\n            break;\n        }\n    }\n    if (found == 0) {\n        scheduler_warning = 1;\n    }\n    return target_worker_id;\n}\n\nint ProcessPool::response(const char *data, uint32_t length) const {\n    if (data == nullptr || length == 0 || stream_info_ == nullptr || stream_info_->last_connection == nullptr ||\n        stream_info_->response_buffer == nullptr) {\n        swoole_set_last_error(SW_ERROR_INVALID_PARAMS);\n        return SW_ERR;\n    }\n    stream_info_->response_buffer->append(data, length);\n    return SW_OK;\n}\n\nbool ProcessPool::send_message(WorkerId worker_id, const char *message, size_t l_message) const {\n    Worker *worker = get_worker(worker_id);\n    if (message_bus) {\n        SendData _task{};\n        _task.info.reactor_id = swoole_get_worker_id();\n        _task.info.len = l_message;\n        _task.data = message;\n        return message_bus->write(worker->pipe_master, &_task);\n    } else {\n        return worker->pipe_master->send_async(message, l_message);\n    }\n}\n\nint ProcessPool::push_message(const EventData *msg) const {\n    if (message_box->push(msg, msg->size()) < 0) {\n        return SW_ERR;\n    }\n    return swoole_kill(master_pid, SIGIO);\n}\n\nint ProcessPool::push_message(uint8_t _type, const void *data, size_t length) const {\n    if (!message_box) {\n        return SW_ERR;\n    }\n\n    EventData msg;\n    assert(length < sizeof(msg.data));\n\n    msg.info = {};\n    msg.info.type = _type;\n    msg.info.len = length;\n    memcpy(msg.data, data, length);\n\n    return push_message(&msg);\n}\n\nint ProcessPool::pop_message(void *data, size_t size) const {\n    if (!message_box) {\n        return SW_ERR;\n    }\n    return message_box->pop(data, size);\n}\n\nswResultCode ProcessPool::dispatch(EventData *data, int *dst_worker_id) {\n    if (use_socket) {\n        Stream *stream = Stream::create(stream_info_->socket_file, 0, SW_SOCK_UNIX_STREAM);\n        if (!stream) {\n            return SW_ERR;\n        }\n        stream->response = nullptr;\n        if (stream->send(reinterpret_cast<char *>(data), data->size()) < 0) {\n            stream->cancel = true;\n            delete stream;\n            return SW_ERR;\n        }\n        return SW_OK;\n    }\n\n    if (*dst_worker_id < 0) {\n        *dst_worker_id = schedule();\n    }\n\n    *dst_worker_id += start_id;\n    Worker *worker = get_worker(*dst_worker_id);\n\n    if (worker->send_pipe_message(data, data->size(), SW_PIPE_MASTER | SW_PIPE_NONBLOCK) < 0) {\n        swoole_warning(\"send %d bytes to worker#%d failed\", data->size(), *dst_worker_id);\n        return SW_ERR;\n    }\n\n    return SW_OK;\n}\n\nswResultCode ProcessPool::dispatch_sync(const char *data, uint32_t len) const {\n    assert(use_socket);\n\n    network::Client client(stream_info_->socket->socket_type, false);\n    if (!client.ready()) {\n        return SW_ERR;\n    }\n    if (client.connect(stream_info_->socket_file, stream_info_->socket_port, -1, 0) < 0) {\n        return SW_ERR;\n    }\n    uint32_t packed_len = htonl(len);\n    if (client.send((char *) &packed_len, 4, 0) < 0) {\n        return SW_ERR;\n    }\n    if (client.send(data, len, 0) < 0) {\n        return SW_ERR;\n    }\n    client.close();\n    return SW_OK;\n}\n\nswResultCode ProcessPool::dispatch_sync(EventData *data, int *dst_worker_id) {\n    if (use_socket) {\n        return dispatch_sync(reinterpret_cast<char *>(data), data->size());\n    }\n\n    if (*dst_worker_id < 0) {\n        *dst_worker_id = schedule();\n    }\n\n    *dst_worker_id += start_id;\n    Worker *worker = get_worker(*dst_worker_id);\n\n    if (worker->send_pipe_message(data, data->size(), SW_PIPE_MASTER) < 0) {\n        swoole_warning(\"send %d bytes to worker#%d failed\", data->size(), *dst_worker_id);\n        return SW_ERR;\n    }\n    return SW_OK;\n}\n\nbool ProcessPool::reload() {\n    if (reload_task) {\n        return false;\n    }\n    if (onBeforeReload) {\n        onBeforeReload(this);\n    }\n    reload_task = new ReloadTask();\n    if (max_wait_time) {\n        reload_task->add_timeout_killer(max_wait_time);\n    }\n    reload_count++;\n    reload_init = true;\n    reload_last_time = ::time(nullptr);\n    return true;\n}\n\nvoid ProcessPool::stop(Worker *worker) {\n    worker->shutdown();\n\n    if (!swoole_event_is_available()) {\n        return;\n    }\n\n    auto reactor = sw_reactor();\n    if (worker->pipe_worker && !worker->pipe_worker->removed) {\n        swoole_event_del(worker->pipe_worker);\n    }\n\n    if (onWorkerExit) {\n        reactor->set_end_callback(Reactor::PRIORITY_TRY_EXIT, [this, worker](Reactor *reactor) {\n            onWorkerExit(this, worker);\n            if (reactor->if_exit()) {\n                reactor->running = false;\n            }\n        });\n    }\n}\n\nvoid ProcessPool::reopen_logger() {\n    sw_logger()->reopen();\n\n    if (is_master()) {\n        kill_all_workers(SIGWINCH);\n    }\n}\n\nvoid ProcessPool::kill_all_workers(int signo) {\n    SW_LOOP_N(worker_num) {\n        swoole_kill(workers[i].pid, signo);\n    }\n}\n\nbool ProcessPool::shutdown() {\n    if (is_master()) {\n        running = false;\n        return true;\n    } else {\n        return swoole_kill(master_pid, SIGTERM) == 0;\n    }\n}\n\npid_t ProcessPool::spawn(Worker *worker) {\n    pid_t pid = swoole_fork(0);\n    int ret_code = 0;\n\n    switch (pid) {\n    // child\n    case 0:\n        worker->init();\n        worker->pid = getpid();\n        swoole_set_worker_type(SW_WORKER);\n        swoole_set_worker_id(worker->id);\n        swoole_set_worker_pid(worker->pid);\n        SwooleWG.worker = worker;\n        if (async) {\n            if (swoole_event_init(SW_EVENTLOOP_WAIT_EXIT) < 0) {\n                exit(254);\n            }\n            sw_reactor()->ptr = this;\n        }\n        if (onWorkerStart != nullptr) {\n            onWorkerStart(this, worker);\n        }\n        if (main_loop) {\n            ret_code = main_loop(this, worker);\n        }\n        if (onWorkerStop != nullptr) {\n            onWorkerStop(this, worker);\n        }\n        exit(ret_code);\n        break;\n    case -1:\n        swoole_sys_warning(\"fork() failed\");\n        break;\n        // parent\n    default:\n        // remove old process\n        if (worker->pid) {\n            map_->erase(worker->pid);\n        }\n        worker->pid = pid;\n        // insert new process\n        map_->emplace(std::make_pair(pid, worker));\n        break;\n    }\n    return pid;\n}\n\nvoid ProcessPool::set_max_request(uint32_t _max_request, uint32_t _max_request_grace) {\n    max_request = _max_request;\n    max_request_grace = _max_request_grace;\n}\n\nbool ProcessPool::is_worker_running(Worker *worker) const {\n    return running && !worker->is_shutdown() && !worker->has_exceeded_max_request();\n}\n\nvoid ProcessPool::at_worker_enter(Worker *worker) const {\n    if (worker->pipe_worker) {\n        worker->pipe_worker->dont_restart = 1;\n    }\n    if (ipc_mode == SW_IPC_UNIXSOCK) {\n        if (swoole_timer_is_available()) {\n            sw_timer()->reinit(true);\n        } else {\n            swoole_timer_create(true);\n        }\n    }\n}\n\nvoid ProcessPool::at_worker_exit(Worker *worker) {\n    if (swoole_timer_is_available()) {\n        swoole_timer_free();\n    }\n}\n\nint ProcessPool::run_with_task_protocol(ProcessPool *pool, Worker *worker) {\n    struct {\n        long mtype;\n        EventData buf;\n    } out{};\n\n    ssize_t n = 0;\n\n    out.buf.info.server_fd = worker->id;\n\n    if (pool->schedule_by_sysvmsg) {\n        out.mtype = 0;\n    } else {\n        out.mtype = worker->id + 1;\n    }\n\n    pool->at_worker_enter(worker);\n    while (pool->is_worker_running(worker)) {\n        /**\n         * fetch task\n         */\n        if (pool->use_msgqueue) {\n            n = pool->queue->pop((QueueNode *) &out, sizeof(out.buf));\n            if (n < 0 && catch_system_error(errno) == SW_ERROR) {\n                swoole_sys_warning(\"[Worker#%d] msgrcv(%d) failed\", worker->id, pool->queue->get_id());\n                break;\n            }\n        } else if (pool->use_socket) {\n            Socket *conn = pool->stream_info_->socket->accept();\n            if (conn == nullptr) {\n                if (catch_system_error(errno) == SW_ERROR) {\n                    swoole_sys_warning(\n                        \"[Worker#%d] accept(%d) failed\", worker->id, pool->stream_info_->socket->get_fd());\n                    break;\n                } else {\n                    goto _end;\n                }\n            }\n            n = Stream::recv_sync(conn, &out.buf, sizeof(out.buf));\n            if (n <= 0) {\n                conn->free();\n                goto _end;\n            }\n            pool->stream_info_->last_connection = conn;\n        } else {\n            worker->pipe_worker->set_timeout(msec2sec(swoole_timer_get_next_msec()), SW_TIMEOUT_READ);\n            n = worker->pipe_worker->read_sync(&out.buf, sizeof(out.buf));\n            if (n < 0 && catch_system_error(errno) == SW_ERROR) {\n                swoole_sys_warning(\"[Worker#%d] read(%d) failed\", worker->id, worker->pipe_worker->fd);\n                break;\n            }\n        }\n\n        if (n < 0) {\n            goto _end;\n        }\n        if (n != (ssize_t) out.buf.size()) {\n            swoole_warning(\"[Worker#%d] bad task packet, The received data-length[%ld] is inconsistent with the \"\n                           \"packet-length[%ld]\",\n                           worker->id,\n                           n,\n                           out.buf.info.len + sizeof(out.buf.info));\n        }\n        if (pool->onTask(pool, worker, &out.buf) < 0) {\n            swoole_warning(\"[Worker#%d] the execution of task#%ld has failed\", worker->id, pool->get_task_id(&out.buf));\n        }\n        if (pool->use_socket && pool->stream_info_->last_connection) {\n            int _end = 0;\n            pool->stream_info_->last_connection->send_sync((void *) &_end, sizeof(_end));\n            pool->stream_info_->last_connection->free();\n            pool->stream_info_->last_connection = nullptr;\n        }\n\n    _end:\n        worker_end_callback();\n    }\n    pool->at_worker_exit(worker);\n\n    return SW_OK;\n}\n\nint ProcessPool::recv_packet(Reactor *reactor, Event *event) {\n    auto *pool = static_cast<ProcessPool *>(reactor->ptr);\n    ssize_t n = event->socket->read(pool->packet_buffer, pool->max_packet_size_);\n    if (n < 0 && errno != EINTR) {\n        swoole_sys_warning(\"failed to read(%d) pipe\", event->fd);\n    }\n    RecvData msg{};\n    msg.info.reactor_id = -1;\n    msg.info.len = n;\n    msg.data = pool->packet_buffer;\n    pool->onMessage(pool, &msg);\n    return SW_OK;\n}\n\nint ProcessPool::recv_message(Reactor *reactor, Event *event) {\n    auto *pool = static_cast<ProcessPool *>(reactor->ptr);\n    if (pool->message_bus->read(event->socket) <= 0) {\n        return SW_OK;\n    }\n    auto pipe_buffer = pool->message_bus->get_buffer();\n    auto packet = pool->message_bus->get_packet();\n    RecvData msg;\n    msg.info = pipe_buffer->info;\n    msg.info.len = packet.length;\n    msg.data = packet.data;\n    pool->onMessage(pool, &msg);\n    pool->message_bus->pop();\n    return SW_OK;\n}\n\nint ProcessPool::run_async(ProcessPool *pool, Worker *worker) {\n    if (pool->ipc_mode == SW_IPC_UNIXSOCK && pool->onMessage) {\n        swoole_event_add(worker->pipe_worker, SW_EVENT_READ);\n        if (pool->message_bus) {\n            swoole_event_set_handler(SW_FD_PIPE, SW_EVENT_READ, recv_message);\n        } else {\n            pool->packet_buffer = new char[pool->max_packet_size_];\n            if (pool->stream_info_) {\n                pool->stream_info_->response_buffer = new String(SW_BUFFER_SIZE_STD);\n            }\n            swoole_event_set_handler(SW_FD_PIPE, SW_EVENT_READ, recv_packet);\n        }\n    }\n    return swoole_event_wait();\n}\n\nint ProcessPool::run_with_stream_protocol(ProcessPool *pool, Worker *worker) {\n    ssize_t n;\n    RecvData msg{};\n    msg.info.reactor_id = -1;\n\n    pool->packet_buffer = new char[pool->max_packet_size_];\n    if (pool->stream_info_) {\n        pool->stream_info_->response_buffer = new String(SW_BUFFER_SIZE_STD);\n    }\n\n    auto *outbuf = reinterpret_cast<QueueNode *>(pool->packet_buffer);\n    outbuf->mtype = 0;\n\n    pool->at_worker_enter(worker);\n    while (pool->is_worker_running(worker)) {\n        /**\n         * fetch task\n         */\n        if (pool->use_msgqueue) {\n            n = pool->queue->pop(outbuf, SW_MSGMAX);\n            /**\n             * A fatal error has occurred; the message queue is no longer available, and the loop must be exited.\n             */\n            if (n < 0 && catch_system_error(errno) == SW_ERROR) {\n                swoole_sys_warning(\"[Worker#%d] msgrcv(%d) failed\", worker->id, pool->queue->get_id());\n                break;\n            }\n            swoole_trace_log(SW_TRACE_WORKER, \"pop from MsgQ#%d %lu bytes\", pool->queue->get_id(), (ulong_t) n);\n            msg.info.len = n - sizeof(msg.info);\n            msg.data = outbuf->mdata;\n            outbuf->mtype = 0;\n        } else if (pool->use_socket) {\n            Socket *conn = pool->stream_info_->socket->accept();\n            if (conn == nullptr) {\n                if (catch_system_error(errno) == SW_ERROR) {\n                    swoole_sys_warning(\n                        \"[Worker#%d] accept(%d) failed\", worker->id, pool->stream_info_->socket->get_fd());\n                    break;\n                } else {\n                    goto _end;\n                }\n            }\n            uint32_t packet_len = 0;\n            if (conn->recv_sync(&packet_len, sizeof(packet_len), MSG_WAITALL) <= 0) {\n                goto _close;\n            }\n            n = ntohl(packet_len);\n            /**\n             * Errors occurring during client connections do not affect subsequent requests,\n             * they continue after closure.\n             */\n            if (n <= 0) {\n                goto _close;\n            } else if (n > pool->max_packet_size_) {\n                goto _close;\n            }\n            if (conn->recv_sync(pool->packet_buffer, n, MSG_WAITALL) <= 0) {\n            _close:\n                conn->free();\n                goto _end;\n            }\n            msg.data = pool->packet_buffer;\n            pool->stream_info_->last_connection = conn;\n        } else {\n            worker->pipe_worker->set_timeout(msec2sec(swoole_timer_get_next_msec()), SW_TIMEOUT_READ);\n            n = worker->pipe_worker->read_sync(pool->packet_buffer, pool->max_packet_size_);\n            if (n < 0 && catch_system_error(errno) == SW_ERROR) {\n                swoole_sys_warning(\"[Worker#%d] read(%d) failed\", worker->id, worker->pipe_worker->fd);\n                break;\n            }\n            msg.data = pool->packet_buffer;\n        }\n\n        if (n < 0) {\n            goto _end;\n        }\n\n        msg.info.len = n;\n        pool->onMessage(pool, &msg);\n\n        if (pool->use_socket && pool->stream_info_->last_connection) {\n            String *resp_buf = pool->stream_info_->response_buffer;\n            if (resp_buf && resp_buf->length > 0) {\n                int _l = htonl(resp_buf->length);\n                pool->stream_info_->last_connection->send_sync(&_l, sizeof(_l));\n                pool->stream_info_->last_connection->send_sync(resp_buf->str, resp_buf->length);\n                resp_buf->clear();\n            }\n            pool->stream_info_->last_connection->free();\n            pool->stream_info_->last_connection = nullptr;\n        }\n\n    _end:\n        worker_end_callback();\n    }\n    pool->at_worker_exit(worker);\n\n    return SW_OK;\n}\n\nint ProcessPool::run_with_message_protocol(ProcessPool *pool, Worker *worker) {\n    if (pool->ipc_mode != SW_IPC_UNIXSOCK) {\n        swoole_error_log(\n            SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, \"not support, ipc_mode must be SW_IPC_UNIXSOCK\");\n        return SW_ERR;\n    }\n\n    auto fn = [&]() -> ReturnCode {\n        while (true) {\n            if (worker->pipe_worker->wait_event(swoole_timer_get_next_msec(), SW_EVENT_READ) < 0) {\n                return errno == EINTR ? SW_CONTINUE : SW_ERROR;\n            }\n            auto rv = pool->message_bus->read(worker->pipe_worker);\n            if (rv < 0) {\n                return errno == EINTR ? SW_CONTINUE : SW_ERROR;\n            } else if (rv > 0) {\n                auto pipe_buffer = pool->message_bus->get_buffer();\n                auto packet = pool->message_bus->get_packet();\n                RecvData msg;\n                msg.info = pipe_buffer->info;\n                msg.info.len = packet.length;\n                msg.data = packet.data;\n                pool->onMessage(pool, &msg);\n                pool->message_bus->pop();\n                return SW_READY;\n            }\n        }\n    };\n\n    if (pool->message_bus == nullptr) {\n        pool->create_message_bus();\n    }\n\n    pool->at_worker_enter(worker);\n    while (pool->is_worker_running(worker)) {\n        switch (fn()) {\n        case SW_CONTINUE:\n            worker_end_callback();\n            break;\n        case SW_READY:\n            break;\n        case SW_ERROR:\n        default:\n            swoole_sys_warning(\"[Worker #%d]failed to read data from pipe\", worker->id);\n            worker->shutdown();\n            break;\n        }\n    }\n    pool->at_worker_exit(worker);\n\n    return SW_OK;\n}\n\nvoid ProcessPool::add_worker(Worker *worker) const {\n    map_->emplace(worker->pid, worker);\n}\n\nbool ProcessPool::del_worker(const Worker *worker) const {\n    return map_->erase(worker->pid) > 0;\n}\n\nWorker *ProcessPool::get_worker_by_pid(pid_t pid) const {\n    const auto iter = map_->find(pid);\n    if (iter == map_->end()) {\n        return nullptr;\n    }\n    return iter->second;\n}\n\nvoid ProcessPool::set_type(int _type) {\n    type = _type;\n    for (uint32_t i = 0; i < worker_num; i++) {\n        workers[i].type = type;\n    }\n}\n\nvoid ProcessPool::set_start_id(int _start_id) {\n    start_id = _start_id;\n    for (uint32_t i = 0; i < worker_num; i++) {\n        workers[i].id = start_id + i;\n    }\n}\n\nbool ProcessPool::wait_detached_worker(std::unordered_set<pid_t> &detached_workers, pid_t pid) {\n    auto iter = detached_workers.find(pid);\n    if (iter == detached_workers.end()) {\n        swoole_warning(\"received an exit signal from an unknown child process[pid=%d]\", pid);\n        return false;\n    }\n    detached_workers.erase(iter);\n    return true;\n}\n\nbool ProcessPool::detach() {\n    if (!running || !message_box) {\n        return false;\n    }\n\n    auto worker = SwooleWG.worker;\n    worker->shutdown();\n    if (async) {\n        swoole_event_del(worker->pipe_worker);\n    }\n\n    WorkerStopMessage msg;\n    msg.pid = worker->pid;\n    msg.worker_id = worker->id;\n    if (push_message(SW_WORKER_MESSAGE_STOP, &msg, sizeof(msg)) < 0) {\n        return false;\n    }\n\n    running = false;\n\n    return true;\n}\n\nint ProcessPool::wait() {\n    std::unordered_set<pid_t> detached_workers;\n\n    while (running) {\n        ExitStatus exit_status = wait_process();\n        const auto wait_error = errno;\n\n        swoole_signal_dispatch();\n\n        if (sw_timer()) {\n            sw_timer()->select();\n        }\n\n        if (read_message) {\n            EventData msg;\n            while (pop_message(&msg, sizeof(msg)) > 0) {\n                if (!running) {\n                    continue;\n                }\n                if (msg.info.type != SW_WORKER_MESSAGE_STOP && onWorkerMessage) {\n                    onWorkerMessage(this, &msg);\n                    continue;\n                }\n                WorkerStopMessage worker_stop_msg;\n                memcpy(&worker_stop_msg, msg.data, sizeof(worker_stop_msg));\n                Worker *exit_worker = get_worker_by_pid(worker_stop_msg.pid);\n                if (exit_worker == nullptr) {\n                    continue;\n                }\n                pid_t new_pid = spawn(exit_worker);\n                if (new_pid < 0) {\n                    swoole_sys_warning(\"fork worker process failed\");\n                    return SW_ERR;\n                }\n                detached_workers.insert(worker_stop_msg.pid);\n                map_->erase(worker_stop_msg.pid);\n            }\n            read_message = false;\n        }\n\n        if (exit_status.get_pid() < 0) {\n            if (!running) {\n                break;\n            }\n            if (!reload_task) {\n                if (wait_error > 0 && wait_error != EINTR) {\n                    swoole_sys_warning(\"wait() failed\");\n                }\n                continue;\n            }\n        }\n\n        if (running) {\n            if (reload_init) {\n                reload_init = false;\n                reload_task->add_workers(workers, worker_num);\n                goto _kill_worker;\n            } else if (exit_status.get_pid() < 0) {\n                continue;\n            }\n\n            Worker *exit_worker = get_worker_by_pid(exit_status.get_pid());\n            if (exit_worker == nullptr) {\n                if (onWorkerNotFound) {\n                    onWorkerNotFound(this, exit_status);\n                } else {\n                    wait_detached_worker(detached_workers, exit_status.get_pid());\n                }\n                continue;\n            }\n\n            if (!exit_status.is_normal_exit()) {\n                exit_worker->report_error(exit_status);\n                if (onWorkerError) {\n                    onWorkerError(this, exit_worker, exit_status);\n                }\n            }\n            pid_t new_pid = spawn(exit_worker);\n            if (new_pid < 0) {\n                swoole_sys_warning(\"Fork worker process failed\");\n                return SW_ERR;\n            }\n            map_->erase(exit_status.get_pid());\n            if (reload_task) {\n                reload_task->remove(exit_status.get_pid());\n            }\n        }\n\n        if (reload_task) {\n            if (reload_task->is_completed()) {\n                delete reload_task;\n                reload_task = nullptr;\n                if (onAfterReload) {\n                    onAfterReload(this);\n                }\n            } else {\n            _kill_worker:\n                reload_task->kill_one();\n            }\n        }\n    }\n\n    uint32_t i;\n    int status;\n    Worker *worker;\n    running = false;\n\n    delete reload_task;\n\n    if (onShutdown) {\n        onShutdown(this);\n    }\n\n    // concurrent kill\n    for (i = 0; i < worker_num; i++) {\n        worker = &workers[i];\n        if (swoole_kill(worker->pid, SIGTERM) < 0) {\n            swoole_sys_warning(\"kill(%d, SIGTERM) failed\", worker->pid);\n            continue;\n        }\n    }\n    if (max_wait_time) {\n        swoole_timer_add((long) max_wait_time * 1000, false, [this](Timer *, TimerNode *) { kill_all_workers(); });\n    }\n    for (i = 0; i < worker_num; i++) {\n        worker = &workers[i];\n        if (swoole_waitpid(worker->pid, &status, 0) < 0) {\n            swoole_sys_warning(\"waitpid(%d) failed\", worker->pid);\n        }\n        break;\n    }\n    started = false;\n\n    return SW_OK;\n}\n\nvoid ProcessPool::destroy() {\n    if (pipes) {\n        delete pipes;\n        pipes = nullptr;\n    }\n\n    if (queue) {\n        delete queue;\n        queue = nullptr;\n    }\n\n    if (stream_info_) {\n        if (stream_info_->socket) {\n            unlink(stream_info_->socket_file);\n            sw_free(stream_info_->socket_file);\n        }\n        if (stream_info_->socket) {\n            stream_info_->socket->free();\n            stream_info_->socket = nullptr;\n        }\n        delete stream_info_->response_buffer;\n        delete stream_info_;\n        stream_info_ = nullptr;\n    }\n\n    if (packet_buffer) {\n        delete[] packet_buffer;\n        packet_buffer = nullptr;\n    }\n\n    if (map_) {\n        delete map_;\n        map_ = nullptr;\n    }\n\n    if (message_box) {\n        message_box->destroy();\n        message_box = nullptr;\n    }\n\n    if (message_bus) {\n        delete message_bus;\n        message_bus = nullptr;\n    }\n\n    sw_mem_pool()->free(workers);\n}\n\nvoid Worker::init() {\n    start_time = ::time(nullptr);\n    request_count = 0;\n    set_status_to_idle();\n    SwooleWG.running = true;\n    SwooleWG.shutdown = false;\n}\n\nvoid Worker::set_max_request(uint32_t max_request, uint32_t max_request_grace) {\n    if (max_request > 0 && max_request_grace > 0) {\n        max_request += swoole_system_random(1, max_request_grace);\n    }\n    SwooleWG.max_request = max_request;\n}\n\nbool Worker::has_exceeded_max_request() const {\n    return SwooleWG.max_request > 0 && request_count >= SwooleWG.max_request;\n}\n\nvoid Worker::shutdown() {\n    status = SW_WORKER_EXIT;\n    SwooleWG.shutdown = true;\n}\n\nbool Worker::is_shutdown() {\n    return SwooleWG.shutdown;\n}\n\nbool Worker::is_running() {\n    return SwooleWG.running;\n}\n\nssize_t Worker::send_pipe_message(const void *buf, size_t n, int flags) const {\n    Socket *pipe_sock;\n\n    if (flags & SW_PIPE_MASTER) {\n        pipe_sock = pipe_master;\n    } else {\n        pipe_sock = pipe_worker;\n    }\n\n    // message-queue\n    if (pool->use_msgqueue) {\n        struct {\n            long mtype;\n            EventData buf;\n        } msg;\n\n        msg.mtype = id + 1;\n        memcpy(&msg.buf, buf, n);\n\n        swoole_trace_log(SW_TRACE_WORKER, \"push to MsgQ#%d %lu bytes\", pool->queue->get_id(), (ulong_t) n);\n\n        return pool->queue->push((QueueNode *) &msg, n) ? n : -1;\n    }\n\n    if ((flags & SW_PIPE_NONBLOCK) && swoole_event_is_available()) {\n        return swoole_event_write(pipe_sock, buf, n);\n    } else {\n        return pipe_sock->send_sync(buf, n);\n    }\n}\n\nvoid Worker::report_error(const ExitStatus &exit_status) const {\n    swoole_warning(\"worker(pid=%d, id=%d) abnormal exit, status=%d, signal=%d\"\n                   \"%s\",\n                   exit_status.get_pid(),\n                   id,\n                   exit_status.get_code(),\n                   exit_status.get_signal(),\n                   exit_status.get_signal() == SIGSEGV ? SwooleG.bug_report_message.c_str() : \"\");\n}\n\nvoid ReloadTask::add_workers(Worker *list, size_t n) {\n    SW_LOOP_N(n) {\n        workers[list[i].pid] = &list[i];\n        kill_queue.push(list[i].pid);\n    }\n}\n\nvoid ReloadTask::add_timeout_killer(int timeout) {\n    timer = swoole_timer_add(sec2msec(timeout), false, [this](Timer *, TimerNode *) {\n        kill_all();\n        timer = nullptr;\n    });\n}\n\nbool ReloadTask::remove(pid_t pid) {\n    auto iter = workers.find(pid);\n    if (iter != workers.end()) {\n        workers.erase(iter);\n        return true;\n    } else {\n        return false;\n    }\n}\n\nReloadTask::~ReloadTask() {\n    if (timer) {\n        swoole_timer_del(timer);\n        timer = nullptr;\n    }\n}\n\nvoid ReloadTask::kill_all(int signal_number) {\n    for (auto &kv : workers) {\n        if (swoole_kill(kv.first, signal_number) < 0) {\n            if (errno == ECHILD || errno == ESRCH) {\n                continue;\n            }\n            swoole_sys_warning(\"failed to kill(%d, SIGTERM) worker#[%d]\", kv.first, kv.second->id);\n        } else if (signal_number == SIGKILL) {\n            swoole_warning(\"force kill worker process(pid=%d, id=%d)\", kv.first, kv.second->id);\n        }\n    }\n\n    clear_queue();\n}\n\nvoid ReloadTask::kill_one(int signal_number) {\n    while (!kill_queue.empty()) {\n        auto pid = kill_queue.front();\n        kill_queue.pop();\n        auto iter = workers.find(pid);\n        if (iter == workers.end()) {\n            continue;\n        }\n        if (swoole_kill(pid, signal_number) < 0) {\n            if (errno == ECHILD || errno == ESRCH) {\n                workers.erase(iter);\n                continue;\n            }\n            swoole_sys_warning(\"kill(%d, SIGTERM) [%d] failed\", pid, iter->second->id);\n        }\n        break;\n    }\n}\n\nvoid ReloadTask::clear_queue() {\n    while (!kill_queue.empty()) {\n        kill_queue.pop();\n    }\n}\n}  // namespace swoole\n\nswoole::WorkerId swoole_get_worker_id() {\n    return SwooleWG.id;\n}\n\npid_t swoole_get_worker_pid() {\n    return SwooleWG.pid;\n}\n\nint swoole_get_worker_type() {\n    return SwooleWG.type;\n}\n\nvoid swoole_set_worker_id(swoole::WorkerId worker_id) {\n    SwooleWG.id = worker_id;\n}\n\nvoid swoole_set_worker_pid(pid_t pid) {\n    SwooleWG.pid = pid;\n}\n\nvoid swoole_set_worker_type(int type) {\n    SwooleWG.type = type;\n}\n\nchar swoole_get_worker_symbol() {\n    switch (swoole_get_worker_type()) {\n    case SW_MASTER:\n        return '#';\n    case SW_MANAGER:\n        return '$';\n    case SW_WORKER:\n        return '*';\n    case SW_TASK_WORKER:\n        return '^';\n    case SW_USER_WORKER:\n        return '@';\n    default:\n        return '%';\n    }\n}\n"
  },
  {
    "path": "src/os/sendfile.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_socket.h\"\n\n#if defined(__linux__)\n#include <sys/sendfile.h>\n\nssize_t swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) {\n\treturn sendfile(out_fd, in_fd, offset, size);\n}\n#elif defined(HAVE_SENDFILE) && defined(HAVE_KQUEUE)\n#include <sys/socket.h>\n#include <sys/uio.h>\n\nssize_t swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) {\n    ssize_t ret;\n\n#ifdef __MACH__\n    struct sf_hdtr hdtr;\n    hdtr.headers = nullptr;\n    hdtr.hdr_cnt = 0;\n    hdtr.trailers = nullptr;\n    hdtr.trl_cnt = 0;\n#else\n    off_t sent_bytes;\n#endif\n\n_do_sendfile:\n#ifdef __MACH__\n    ret = sendfile(in_fd, out_fd, *offset, (off_t *) &size, &hdtr, 0);\n#else\n    ret = sendfile(in_fd, out_fd, *offset, size, 0, &sent_bytes, 0);\n#endif\n\n    // sent_bytes = (off_t)size;\n    swoole_trace(\n        \"send file, ret: %zd, out_fd:%d, in_fd:%d, offset:%jd, size:%zu\", ret, out_fd, in_fd, (intmax_t) *offset, size);\n\n#ifdef __MACH__\n    *offset += size;\n#else\n    *offset += sent_bytes;\n#endif\n\n    if (ret == -1) {\n        if (errno == EINTR) {\n            goto _do_sendfile;\n        } else {\n            return ret;\n        }\n    } else if (ret == 0) {\n        return size;\n    } else {\n        swoole_sys_warning(\"sendfile failed\");\n        return SW_ERR;\n    }\n    return SW_OK;\n}\n#elif !defined(HAVE_SENDFILE)\nssize_t swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) {\n    char buf[SW_BUFFER_SIZE_BIG];\n    size_t readn = size > sizeof(buf) ? sizeof(buf) : size;\n    ssize_t n = pread(in_fd, buf, readn, *offset);\n\n    if (n > 0) {\n        ssize_t ret = write(out_fd, buf, n);\n        if (ret < 0) {\n            swoole_sys_warning(\"write() failed\");\n        } else {\n            *offset += ret;\n        }\n        return ret;\n    } else {\n        swoole_sys_warning(\"pread() failed\");\n        return SW_ERR;\n    }\n}\n#endif\n"
  },
  {
    "path": "src/os/signal.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_signal.h\"\n#include \"swoole_socket.h\"\n#include \"swoole_reactor.h\"\n\n#ifdef HAVE_SIGNALFD\n#include <sys/signalfd.h>\n#endif\n\n#include <csignal>\n\n#ifdef HAVE_KQUEUE\n#ifdef USE_KQUEUE_IDE_HELPER\n#include \"helper/kqueue.h\"\n#else\n#include <sys/event.h>\n#endif\n#endif\n#ifdef __NetBSD__\n#include <sys/param.h>\n#endif\n\nusing swoole::Event;\nusing swoole::Reactor;\nusing swoole::Signal;\nusing swoole::SignalHandler;\nusing swoole::network::Socket;\n\n#ifdef HAVE_SIGNALFD\nstatic SignalHandler swoole_signalfd_set(int signo, SignalHandler handler);\nstatic bool swoole_signalfd_create();\nstatic bool swoole_signalfd_setup(Reactor *reactor);\nstatic int swoole_signalfd_event_callback(Reactor *reactor, Event *event);\nstatic void swoole_signalfd_clear();\n#endif\n\n#ifdef HAVE_KQUEUE\nstatic SignalHandler swoole_signal_kqueue_set(int signo, SignalHandler handler);\nstatic void swoole_signal_kqueue_clear();\n#endif\n\nstatic void signal_handler_safety(int signo);\nstatic void signal_handler_simple(int signo);\n\n#ifdef HAVE_SIGNALFD\nstatic sigset_t signalfd_mask;\nstatic int signal_fd = -1;\nstatic pid_t signalfd_create_pid;\nstatic Socket *signal_socket = nullptr;\nstatic inline bool swoole_signalfd_is_available() {\n    return signal_fd != -1;\n}\n#endif\nstatic Signal signals[SW_SIGNO_MAX];\nstatic bool triggered_signals[SW_SIGNO_MAX];\n\nchar *swoole_signal_to_str(int sig) {\n    static char buf[64];\n    snprintf(buf, sizeof(buf), \"%s\", strsignal(sig));\n    if (strchr(buf, ':') == nullptr) {\n        size_t len = strlen(buf);\n        snprintf(buf + len, sizeof(buf) - len, \": %d\", sig);\n    }\n    return buf;\n}\n\nvoid swoole_signal_block_all() {\n    if (SwooleTG.signal_blocking_all) {\n        return;\n    }\n    sigset_t mask;\n    sigfillset(&mask);\n    int ret = pthread_sigmask(SIG_BLOCK, &mask, nullptr);\n    if (ret < 0) {\n        swoole_sys_warning(\"pthread_sigmask(SIG_BLOCK) failed\");\n    } else {\n        SwooleTG.signal_blocking_all = true;\n    }\n}\n\nvoid swoole_signal_unblock_all() {\n    if (!SwooleTG.signal_blocking_all) {\n        return;\n    }\n    sigset_t mask;\n    sigfillset(&mask);\n    int ret = pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);\n    if (ret < 0) {\n        swoole_sys_warning(\"pthread_sigmask(SIG_UNBLOCK) failed\");\n    } else {\n        SwooleTG.signal_blocking_all = false;\n    }\n}\n\n/**\n * set new signal handler and return origin signal handler\n */\nSignalHandler swoole_signal_set(int signo, SignalHandler func, int restart, int mask) {\n    // ignore\n    if (func == nullptr) {\n        func = SIG_IGN;\n    }\n    // clear\n    else if ((long) func == -1) {\n        func = SIG_DFL;\n    }\n\n    if (func == SIG_IGN || func == SIG_DFL) {\n        signals[signo].handler = nullptr;\n        signals[signo].activated = false;\n    }\n\n    struct sigaction act{}, oact{};\n    act.sa_handler = func;\n    if (mask) {\n        sigfillset(&act.sa_mask);\n    } else {\n        sigemptyset(&act.sa_mask);\n    }\n    act.sa_flags = 0;\n    if (restart) {\n        act.sa_flags |= SA_RESTART;\n    }\n    if (sigaction(signo, &act, &oact) < 0) {\n        return nullptr;\n    }\n    return oact.sa_handler;\n}\n\nSW_API bool swoole_signal_isset(int signo) {\n    return signals[signo].handler && signals[signo].activated;\n}\n\n/**\n * set new signal handler and return origin signal handler\n */\nSignalHandler swoole_signal_set(int signo, SignalHandler handler, bool safety) {\n#ifdef HAVE_SIGNALFD\n    if (SwooleG.enable_signalfd && swoole_event_is_available()) {\n        return swoole_signalfd_set(signo, handler);\n    }\n#endif\n#ifdef HAVE_KQUEUE\n    // SIGCHLD can not be monitored by kqueue, if blocked by SIG_IGN\n    // see https://www.freebsd.org/cgi/man.cgi?kqueue\n    // if there's no main reactor, signals cannot be monitored either\n    if (SwooleG.enable_kqueue && swoole_event_is_available() && signo != SIGCHLD) {\n        return swoole_signal_kqueue_set(signo, handler);\n    }\n#endif\n\n    signals[signo].handler = handler;\n    signals[signo].activated = true;\n    signals[signo].signo = signo;\n    return swoole_signal_set(signo, safety ? signal_handler_safety : signal_handler_simple, 0, 0);\n}\n\nstatic void signal_handler_safety(int signo) {\n    triggered_signals[signo] = true;\n    SwooleG.signal_dispatch = true;\n}\n\nstatic void signal_handler_simple(int signo) {\n    if (sw_reactor()) {\n        signal_handler_safety(signo);\n    } else {\n        static int _lock = 0;\n        // discard signal\n        if (_lock) {\n            return;\n        }\n        _lock = 1;\n        swoole_signal_callback(signo);\n        _lock = 0;\n    }\n}\n\nvoid swoole_signal_dispatch() {\n    if (!SwooleG.signal_dispatch) {\n        return;\n    }\n    SW_LOOP_N(SW_SIGNO_MAX) {\n        if (triggered_signals[i]) {\n            swoole_signal_callback(i);\n            triggered_signals[i] = false;\n        }\n    }\n    SwooleG.signal_dispatch = false;\n}\n\nvoid swoole_signal_callback(int signo) {\n    if (signo >= SW_SIGNO_MAX) {\n        swoole_warning(\"signal[%d] numberis invalid\", signo);\n        return;\n    }\n    SignalHandler callback = signals[signo].handler;\n    if (!callback) {\n        swoole_error_log(\n            SW_LOG_WARNING, SW_ERROR_UNREGISTERED_SIGNAL, SW_UNREGISTERED_SIGNAL_FMT, swoole_signal_to_str(signo));\n        return;\n    }\n    if (callback == SIG_IGN || callback == SIG_DFL) {\n        return;\n    }\n    callback(signo);\n}\n\nSignalHandler swoole_signal_get_handler(int signo) {\n    if (signo >= SW_SIGNO_MAX) {\n        swoole_warning(\"signal[%d] numberis invalid\", signo);\n        return nullptr;\n    } else {\n        return signals[signo].handler;\n    }\n}\n\nuint32_t swoole_signal_get_listener_num() {\n    return SwooleG.signal_listener_num + SwooleG.signal_async_listener_num;\n}\n\nvoid swoole_signal_clear() {\n#ifdef HAVE_SIGNALFD\n    if (SwooleG.enable_signalfd && swoole_signalfd_is_available()) {\n        swoole_signalfd_clear();\n        return;\n    }\n#endif\n\n#ifdef HAVE_KQUEUE\n    if (SwooleG.enable_kqueue) {\n        swoole_signal_kqueue_clear();\n        return;\n    }\n#endif\n\n    SW_LOOP_N(SW_SIGNO_MAX) {\n        if (signals[i].activated) {\n            swoole_signal_set(signals[i].signo, reinterpret_cast<SignalHandler>(-1), 1, 0);\n        }\n    }\n    sw_memset_zero(signals, sizeof(signals));\n}\n\n#ifdef HAVE_SIGNALFD\nvoid swoole_signalfd_init() {\n    sigemptyset(&signalfd_mask);\n    sw_memset_zero(signals, sizeof(signals));\n}\n\n/**\n * set new signal handler and return origin signal handler\n */\nstatic SignalHandler swoole_signalfd_set(int signo, SignalHandler handler) {\n    SignalHandler origin_handler = nullptr;\n\n    if (handler == nullptr && signals[signo].activated) {\n        sigdelset(&signalfd_mask, signo);\n        sw_memset_zero(&signals[signo], sizeof(Signal));\n    } else {\n        sigaddset(&signalfd_mask, signo);\n        origin_handler = signals[signo].handler;\n        signals[signo].handler = handler;\n        signals[signo].signo = signo;\n        signals[signo].activated = true;\n    }\n\n    if (swoole_signalfd_is_available()) {\n        sigprocmask(SIG_SETMASK, &signalfd_mask, nullptr);\n        signalfd(signal_fd, &signalfd_mask, SFD_NONBLOCK | SFD_CLOEXEC);\n    }\n    swoole_signalfd_setup(sw_reactor());\n\n    return origin_handler;\n}\n\nstatic void swoole_signalfd_close() {\n    if (!swoole_signalfd_is_available()) {\n        return;\n    }\n    signal_socket->fd = -1;\n    signal_socket->free();\n    close(signal_fd);\n    signal_socket = nullptr;\n    signal_fd = -1;\n}\n\nstatic bool swoole_signalfd_create() {\n    if (swoole_signalfd_is_available()) {\n        return false;\n    }\n\n    signal_fd = signalfd(-1, &signalfd_mask, SFD_NONBLOCK | SFD_CLOEXEC);\n    if (signal_fd < 0) {\n        swoole_sys_warning(\"signalfd() failed\");\n        return false;\n    }\n    signal_socket = swoole::make_socket(signal_fd, SW_FD_SIGNAL);\n    if (sigprocmask(SIG_BLOCK, &signalfd_mask, nullptr) == -1) {\n        swoole_sys_warning(\"sigprocmask() failed\");\n        swoole_signalfd_close();\n        return false;\n    }\n    signalfd_create_pid = getpid();\n    SwooleG.signal_fd = signal_fd;\n\n    return true;\n}\n\nbool swoole_signalfd_setup(Reactor *reactor) {\n    if (!swoole_signalfd_is_available() && !swoole_signalfd_create()) {\n        return false;\n    }\n    if (!swoole_event_isset_handler(SW_FD_SIGNAL, SW_EVENT_READ)) {\n        swoole_event_set_handler(SW_FD_SIGNAL, SW_EVENT_READ, swoole_signalfd_event_callback);\n        reactor->set_exit_condition(Reactor::EXIT_CONDITION_SIGNALFD, [](Reactor *reactor, size_t &event_num) -> bool {\n            event_num--;\n            return true;\n        });\n        reactor->add_destroy_callback([](void *) {\n            // child process removes signal socket, parent process will not be able to trigger signal\n            if (signal_socket && signalfd_create_pid == getpid()) {\n                swoole_event_del(signal_socket);\n            }\n        });\n    }\n    if (!(signal_socket->events & SW_EVENT_READ) && swoole_event_add(signal_socket, SW_EVENT_READ) < 0) {\n        return false;\n    }\n    reactor->erase_end_callback(Reactor::PRIORITY_SIGNAL_CALLBACK);\n    return true;\n}\n\nstatic void swoole_signalfd_clear() {\n    if (!swoole_signalfd_is_available()) {\n        return;\n    }\n\n    if (sigprocmask(SIG_UNBLOCK, &signalfd_mask, nullptr) < 0) {\n        swoole_sys_warning(\"sigprocmask(SIG_UNBLOCK) failed\");\n    }\n    sw_memset_zero(signals, sizeof(signals));\n    sw_memset_zero(&signalfd_mask, sizeof(signalfd_mask));\n\n    swoole_signalfd_close();\n}\n\nstatic int swoole_signalfd_event_callback(Reactor *reactor, Event *event) {\n    signalfd_siginfo siginfo;\n    ssize_t n = read(event->fd, &siginfo, sizeof(siginfo));\n    if (n < 0) {\n        swoole_sys_warning(\"read from signalfd failed\");\n        return SW_OK;\n    }\n    if (siginfo.ssi_signo >= SW_SIGNO_MAX) {\n        swoole_warning(\"unknown signal[%d]\", siginfo.ssi_signo);\n        return SW_OK;\n    }\n    if (signals[siginfo.ssi_signo].activated) {\n        SignalHandler handler = signals[siginfo.ssi_signo].handler;\n        if (handler == SIG_IGN) {\n            return SW_OK;\n        } else if (handler) {\n            handler(siginfo.ssi_signo);\n        } else {\n            swoole_error_log(SW_LOG_WARNING,\n                             SW_ERROR_UNREGISTERED_SIGNAL,\n                             SW_UNREGISTERED_SIGNAL_FMT,\n                             swoole_signal_to_str(siginfo.ssi_signo));\n        }\n    }\n\n    return SW_OK;\n}\n#endif\n\n#ifdef HAVE_KQUEUE\n/**\n * set new signal handler and return origin signal handler\n */\nstatic SignalHandler swoole_signal_kqueue_set(int signo, SignalHandler handler) {\n    struct kevent ev;\n    SignalHandler origin_handler = nullptr;\n    Reactor *reactor = sw_reactor();\n\n    // clear signal\n    if (handler == nullptr) {\n        signal(signo, SIG_DFL);\n        sw_memset_zero(&signals[signo], sizeof(Signal));\n        EV_SET(&ev, signo, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL);\n    }\n    // add/update signal\n    else {\n        signal(signo, SIG_IGN);\n        origin_handler = signals[signo].handler;\n        signals[signo].handler = handler;\n        signals[signo].signo = signo;\n        signals[signo].activated = true;\n#if !defined(__NetBSD__) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000)\n        auto sigptr = &signals[signo];\n#else\n        auto sigptr = reinterpret_cast<intptr_t>(&signals[signo]);\n#endif\n        // save swSignal* as udata\n        EV_SET(&ev, signo, EVFILT_SIGNAL, EV_ADD, 0, 0, sigptr);\n    }\n    int n = kevent(reactor->native_handle, &ev, 1, nullptr, 0, nullptr);\n    if (n < 0 && sw_unlikely(handler)) {\n        swoole_sys_warning(\"kevent set signal[%d] error\", signo);\n    }\n\n    return origin_handler;\n}\n\nstatic void swoole_signal_kqueue_clear() {\n    SW_LOOP_N(SW_SIGNO_MAX) {\n        if (signals[i].activated && swoole_event_is_available()) {\n            signals[i].activated = false;\n            signals[i].handler = nullptr;\n            swoole_signal_kqueue_set(signals[i].signo, nullptr);\n        }\n    }\n    sw_memset_zero(signals, sizeof(signals));\n}\n#endif\n"
  },
  {
    "path": "src/os/timer.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_timer.h\"\n#include \"swoole_signal.h\"\n\n#include <csignal>\n#include <sys/time.h>\n\nnamespace swoole {\nstatic int SystemTimer_set(Timer *timer, long next_msec);\n\nvoid Timer::init_with_system_timer() {\n    set = SystemTimer_set;\n    close = [](Timer *timer) { SystemTimer_set(timer, -1); };\n    swoole_signal_set(SIGALRM, [](int sig) { SwooleG.signal_alarm = true; });\n}\n\nstatic int SystemTimer_set(Timer *timer, long next_msec) {\n    itimerval timer_set{};\n    if (next_msec > 0) {\n        timer_set.it_interval = {next_msec / 1000, static_cast<int>((next_msec % 1000) * 1000)};\n        timer_set.it_value = timer_set.it_interval;\n    }\n    return setitimer(ITIMER_REAL, &timer_set, nullptr) < 0 ? SW_ERR : SW_OK;\n}\n\nvoid realtime_get(timespec *time) {\n    auto now = std::chrono::system_clock::now();\n    auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch());\n    time->tv_sec = ns.count() / SW_NUM_BILLION;\n    time->tv_nsec = ns.count() % SW_NUM_BILLION;\n}\n\nvoid realtime_add(timespec *time, const int64_t add_msec) {\n    time->tv_sec += add_msec / 1000;\n    time->tv_nsec += add_msec % 1000 * SW_NUM_MILLION;\n    if (time->tv_nsec >= SW_NUM_BILLION) {\n        int secs = time->tv_nsec / SW_NUM_BILLION;\n        time->tv_sec += secs;\n        time->tv_nsec -= secs * SW_NUM_BILLION;\n    }\n}\n}  // namespace swoole\n"
  },
  {
    "path": "src/os/unix_socket.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_pipe.h\"\n#include \"swoole_socket.h\"\n\nnamespace swoole {\nUnixSocket::UnixSocket(bool blocking, int _protocol) : SocketPair(blocking), protocol_(_protocol) {\n    if (socketpair(AF_UNIX, protocol_, 0, socks) < 0) {\n        swoole_sys_warning(\"socketpair() failed\");\n        return;\n    }\n    init_socket(socks[1], socks[0]);\n    set_buffer_size(network::Socket::default_buffer_size);\n}\n\nbool UnixSocket::set_buffer_size(size_t _size) const {\n    if (!master_socket->set_buffer_size(_size)) {\n        return false;\n    }\n    if (!worker_socket->set_buffer_size(_size)) {\n        return false;\n    }\n    return true;\n}\n}  // namespace swoole\n"
  },
  {
    "path": "src/os/wait.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_process_pool.h\"\n#include \"swoole_coroutine.h\"\n#include \"swoole_coroutine_api.h\"\n#include \"swoole_coroutine_system.h\"\n#include \"swoole_iouring.h\"\n#include \"swoole_signal.h\"\n\n#include <list>\n#include <unordered_map>\n\nusing namespace swoole;\nusing swoole::coroutine::System;\n\nstruct WaitTask {\n    Coroutine *co;\n    pid_t pid;\n    int status;\n};\n\n/**\n * Wait, waitpid, and signal cannot be used in a multithreaded environment;\n * they are only applicable to the main thread. There is no need to treat them as thread-local variables.\n */\nstatic std::list<WaitTask *> wait_list;\nstatic std::unordered_map<pid_t, WaitTask *> waitpid_map;\nstatic std::unordered_map<pid_t, int> child_processes;\n\nbool signal_ready = false;\n\nstatic void signal_handler(int signo) {\n    if (signo != SIGCHLD) {\n        return;\n    }\n\n    while (true) {\n        auto exit_status = swoole::wait_process(-1, WNOHANG);\n        if (exit_status.get_pid() <= 0) {\n            break;\n        }\n\n        WaitTask *task = nullptr;\n        if (waitpid_map.find(exit_status.get_pid()) != waitpid_map.end()) {\n            task = waitpid_map[exit_status.get_pid()];\n        } else if (!wait_list.empty()) {\n            task = wait_list.front();\n        } else {\n            child_processes[exit_status.get_pid()] = exit_status.get_status();\n        }\n\n        if (task) {\n            task->status = exit_status.get_status();\n            task->pid = exit_status.get_pid();\n            task->co->resume();\n        }\n    }\n}\n\nstatic void signal_init() {\n    if (!signal_ready) {\n        Reactor *reactor = SwooleTG.reactor;\n        swoole_signal_set(SIGCHLD, signal_handler);\n\n        reactor->set_exit_condition(Reactor::EXIT_CONDITION_WAIT_PID, [](Reactor *reactor, size_t &event_num) -> bool {\n            return swoole_coroutine_wait_count() == 0;\n        });\n\n        reactor->add_destroy_callback([](void *) {\n            signal_ready = false;\n            swoole_signal_clear();\n        });\n\n        signal_ready = true;\n    }\n}\n\n#if SW_USE_IOURING\nstatic pid_t iouring_waitpid(pid_t _pid, int *_stat_loc, int _options, double timeout = -1) {\n    auto rs = Iouring::waitpid(_pid, _stat_loc, _options, timeout);\n    if (rs < 0) {\n        swoole_set_last_error(errno);\n    }\n    return rs;\n}\n#endif\n\npid_t System::wait(int *_stat_loc, double timeout) {\n    return System::waitpid(-1, _stat_loc, 0, timeout);\n}\n\npid_t System::waitpid_safe(pid_t _pid, int *_stat_loc, int _options) {\n    if (sw_unlikely(SwooleTG.reactor == nullptr || !Coroutine::get_current() || (_options & WNOHANG))) {\n        return ::waitpid(_pid, _stat_loc, _options);\n    }\n\n#if SW_USE_IOURING\n    return iouring_waitpid(_pid, _stat_loc, _options);\n#endif\n\n    pid_t retval = -1;\n    wait_for([_pid, &retval, _stat_loc]() -> bool {\n        retval = ::waitpid(_pid, _stat_loc, WNOHANG);\n        return retval != 0;\n    });\n\n    return retval;\n}\n\n/**\n * @error: errno & swoole_get_last_error()\n */\npid_t System::waitpid(pid_t _pid, int *_stat_loc, int _options, double timeout) {\n#if SW_USE_IOURING\n    return iouring_waitpid(_pid, _stat_loc, _options, timeout);\n#endif\n    if (_pid < 0) {\n        if (!child_processes.empty()) {\n            auto i = child_processes.begin();\n            pid_t pid = i->first;\n            *_stat_loc = i->second;\n            child_processes.erase(i);\n            return pid;\n        }\n    } else {\n        auto i = child_processes.find(_pid);\n        if (i != child_processes.end()) {\n            *_stat_loc = i->second;\n            child_processes.erase(i);\n            return _pid;\n        }\n    }\n\n    if (sw_unlikely(SwooleTG.reactor == nullptr || !Coroutine::get_current() || (_options & WNOHANG))) {\n        return ::waitpid(_pid, _stat_loc, _options);\n    }\n\n    /* try once if failed to init the task, and must register SIGCHLD before try waitpid, or may lose the SIGCHLD\n     */\n    WaitTask task;\n    signal_init();\n    task.pid = ::waitpid(_pid, _stat_loc, _options | WNOHANG);\n    if (task.pid != 0) {\n        return task.pid;\n    }\n\n    task.pid = -1;\n    task.status = 0;\n    task.co = Coroutine::get_current();\n\n    /* enqueue */\n    if (_pid < 0) {\n        wait_list.push_back(&task);\n    } else {\n        waitpid_map[_pid] = &task;\n    }\n\n    task.co->yield_ex(timeout);\n\n    /* dequeue */\n    if (_pid < 0) {\n        if (task.pid > 0) {\n            wait_list.pop_front();\n        } else {\n            /* timeout so we should remove it from the list */\n            wait_list.remove(&task);\n        }\n    } else {\n        waitpid_map.erase(_pid);\n    }\n\n    /* clear and assign result */\n    if (task.pid > 0) {\n        *_stat_loc = task.status;\n    } else if (task.co->is_timedout()) {\n        errno = ETIMEDOUT;\n        swoole_set_last_error(ETIMEDOUT);\n    }\n\n    return task.pid;\n}\n\nextern \"C\" {\nsize_t swoole_coroutine_wait_count() {\n    return wait_list.size() + waitpid_map.size();\n}\n\npid_t swoole_coroutine_wait(int *_stat_loc) {\n    return System::wait(_stat_loc);\n}\n\npid_t swoole_coroutine_waitpid(pid_t _pid, int *_stat_loc, int _options) {\n    return System::waitpid(_pid, _stat_loc, _options);\n}\n}\n\npid_t swoole_waitpid(pid_t _pid, int *_stat_loc, int _options) {\n    pid_t retval;\n    SW_LOOP {\n        retval = waitpid(_pid, _stat_loc, _options);\n        if (!(retval < 0 && errno == EINTR)) {\n            break;\n        }\n        swoole_signal_dispatch();\n        if (sw_timer()) {\n            sw_timer()->select();\n        }\n    }\n    return retval;\n}\n"
  },
  {
    "path": "src/protocol/base.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2017 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_string.h\"\n#include \"swoole_socket.h\"\n#include \"swoole_protocol.h\"\n\nnamespace swoole {\n#define proto_error(_sock, _error, _fmt, ...)                                                                          \\\n    network::Address _remote_addr;                                                                                     \\\n    if (_sock->get_peer_name(&_remote_addr) < 0) {                                                                     \\\n        _remote_addr = _sock->info;                                                                                    \\\n    }                                                                                                                  \\\n    swoole_error_log(                                                                                                  \\\n        SW_LOG_WARNING, _error, _fmt \"<%s:%d>\", ##__VA_ARGS__, _remote_addr.get_addr(), _remote_addr.get_port())\n\n/**\n * return the package total length\n */\nssize_t Protocol::default_length_func(const Protocol *protocol, network::Socket *socket, PacketLength *pl) {\n    uint16_t length_offset = protocol->package_length_offset;\n    uint8_t package_length_size =\n        protocol->get_package_length_size ? protocol->get_package_length_size(socket) : protocol->package_length_size;\n\n    if (package_length_size == 0) {\n        // protocol error\n        return SW_ERR;\n    }\n    /**\n     * no have length field, wait more data\n     */\n    if (pl->buf_size < length_offset + package_length_size) {\n        pl->header_len = length_offset + package_length_size;\n        return 0;\n    }\n    int64_t body_length = swoole_unpack(protocol->package_length_type, pl->buf + length_offset);\n    // Length error\n    // Protocol length is not legitimate, out of bounds or exceed the allocated length\n    if (body_length < 0) {\n        return SW_ERR;\n    }\n    swoole_debug(\"length=%ld\", protocol->package_body_offset + body_length);\n    pl->header_len = protocol->package_length_size;\n\n    // total package length\n    return protocol->package_body_offset + body_length;\n}\n\nint Protocol::recv_split_by_eof(network::Socket *socket, String *buffer) const {\n    RecvData rdata{};\n\n    if (buffer->length < package_eof_len) {\n        return SW_CONTINUE;\n    }\n\n    ssize_t n = buffer->split(package_eof, package_eof_len, [&](const char *data, size_t length) -> int {\n        rdata.info.len = length;\n        rdata.data = data;\n        if (onPackage(this, socket, &rdata) < 0) {\n            return false;\n        }\n        if (socket->removed) {\n            return false;\n        }\n        return true;\n    });\n\n    if (socket->removed || n < 0) {\n        return SW_CLOSE;\n    } else if (n == 0) {\n        return SW_CONTINUE;\n    } else if (n < (ssize_t) buffer->length) {\n        off_t offset;\n        buffer->reduce(n);\n        offset = buffer->length - package_eof_len;\n        buffer->offset = offset > 0 ? offset : 0;\n    } else {\n        buffer->clear();\n    }\n\n    if (socket->ssl) {\n        return SW_CONTINUE;\n    }\n\n    return SW_OK;\n}\n\n/**\n * @return SW_ERR: close the connection\n * @return SW_OK: continue\n */\nint Protocol::recv_with_length_protocol(network::Socket *socket, String *buffer) const {\n    RecvData rdata{};\n    PacketLength pl{};\n    ssize_t package_length;\n    uint8_t _package_length_size = get_package_length_size ? get_package_length_size(socket) : package_length_size;\n    uint32_t recv_size = 0;\n    ssize_t recv_n = 0;\n\n    // protocol error\n    if (get_package_length_size && _package_length_size == 0) {\n        return SW_ERR;\n    }\n\n    if (socket->skip_recv) {\n        socket->skip_recv = 0;\n        goto _do_get_length;\n    }\n\n_do_recv:\n    if (socket->removed) {\n        return SW_OK;\n    }\n    if (buffer->offset > 0) {\n        recv_size = buffer->offset - buffer->length;\n    } else {\n        recv_size = package_length_offset + _package_length_size;\n    }\n\n    recv_n = socket->recv(buffer->str + buffer->length, recv_size, 0);\n    if (recv_n < 0) {\n        switch (socket->catch_read_error(errno)) {\n        case SW_ERROR:\n            swoole_sys_warning(\"recv(%d, %d) failed\", socket->fd, recv_size);\n            return SW_OK;\n        case SW_CLOSE:\n            return SW_ERR;\n        default:\n            return SW_OK;\n        }\n    } else if (recv_n == 0) {\n        return SW_ERR;\n    } else {\n        buffer->length += recv_n;\n\n        if (socket->recv_wait) {\n            if (buffer->length >= (size_t) buffer->offset) {\n            _do_dispatch:\n                rdata.info.len = buffer->offset;\n                rdata.data = buffer->str;\n                if (onPackage(this, socket, &rdata) < 0) {\n                    return SW_ERR;\n                }\n                if (socket->removed) {\n                    return SW_OK;\n                }\n                socket->recv_wait = 0;\n\n                if (buffer->length > (size_t) buffer->offset) {\n                    buffer->reduce(buffer->offset);\n                    goto _do_get_length;\n                } else {\n                    buffer->clear();\n                }\n            }\n            if (socket->ssl) {\n                goto _do_recv;\n            }\n            return SW_OK;\n        } else {\n        _do_get_length:\n            pl.buf = buffer->str;\n            pl.buf_size = buffer->length;\n            // TODO: support dynamic calculation of header buffer length (recv_size)\n            pl.header_len = 0;\n            package_length = get_package_length(this, socket, &pl);\n            // invalid package, close connection.\n            if (package_length < 0) {\n                proto_error(socket,\n                            SW_ERROR_PACKAGE_MALFORMED_DATA,\n                            \"received %zu bytes of malformed data, remote_addr=\",\n                            buffer->length);\n                return SW_ERR;\n            }\n            // no length\n            else if (package_length == 0) {\n                if (buffer->length == recv_size) {\n                    swoole_error_log(SW_LOG_WARNING,\n                                     SW_ERROR_PACKAGE_LENGTH_NOT_FOUND,\n                                     \"bad request, no length found in %zu bytes, remote_addr=\",\n                                     buffer->length);\n                    return SW_ERR;\n                } else {\n                    return SW_OK;\n                }\n            } else if (package_length > package_max_length) {\n                proto_error(socket,\n                            SW_ERROR_PACKAGE_LENGTH_TOO_LARGE,\n                            \"the received packet length %ld is too large, remote_addr=\",\n                            package_length);\n                return SW_ERR;\n            }\n            // get length success\n            else {\n                if (buffer->size < (size_t) package_length) {\n                    buffer->extend(package_length);\n                }\n                socket->recv_wait = 1;\n                buffer->offset = package_length;\n\n                if (buffer->length >= (size_t) package_length) {\n                    goto _do_dispatch;\n                } else {\n                    goto _do_recv;\n                }\n            }\n        }\n    }\n    return SW_OK;\n}\n\n/**\n * @return SW_ERR: close the connection\n * @return SW_OK: continue\n */\nint Protocol::recv_with_eof_protocol(network::Socket *socket, String *buffer) const {\n    bool recv_again = false;\n    size_t buf_size;\n    RecvData rdata{};\n\n_recv_data:\n    buf_size = buffer->size - buffer->length;\n    char *buf_ptr = buffer->str + buffer->length;\n\n    if (buf_size > SW_BUFFER_SIZE_STD) {\n        buf_size = SW_BUFFER_SIZE_STD;\n    }\n\n    ssize_t n = socket->recv(buf_ptr, buf_size, 0);\n    if (n < 0) {\n        switch (socket->catch_read_error(errno)) {\n        case SW_ERROR:\n            swoole_sys_warning(\"recv from socket#%d failed\", socket->fd);\n            return SW_OK;\n        case SW_CLOSE:\n            return SW_ERR;\n        default:\n            return SW_OK;\n        }\n    } else if (n == 0) {\n        return SW_ERR;\n    } else {\n        buffer->length += n;\n\n        if (buffer->length < package_eof_len) {\n            return SW_OK;\n        }\n\n        if (split_by_eof) {\n            int retval = recv_split_by_eof(socket, buffer);\n            if (retval == SW_CONTINUE) {\n                recv_again = true;\n            } else if (retval == SW_CLOSE) {\n                return SW_ERR;\n            } else {\n                return SW_OK;\n            }\n        } else if (memcmp(buffer->str + buffer->length - package_eof_len, package_eof, package_eof_len) == 0) {\n            buffer->offset = buffer->length;\n            rdata.info.len = buffer->length;\n            rdata.data = buffer->str;\n            if (onPackage(this, socket, &rdata) < 0) {\n                return SW_ERR;\n            }\n            if (socket->removed) {\n                return SW_OK;\n            }\n            buffer->clear();\n            if (socket->ssl && SSL_pending(socket->ssl) > 0) {\n                goto _recv_data;\n            }\n            return SW_OK;\n        }\n\n        // over max length, will discard\n        if (buffer->length == package_max_length) {\n            proto_error(socket,\n                        SW_ERROR_PACKAGE_LENGTH_TOO_LARGE,\n                        \"The received data packet is too large, length=%lu\",\n                        (ulong_t) buffer->length);\n            return SW_ERR;\n        }\n\n        // buffer is full, may have not read data\n        if (buffer->length == buffer->size) {\n            recv_again = true;\n            if (buffer->size < package_max_length) {\n                uint32_t extend_size = swoole_size_align(buffer->size * 2, swoole_pagesize());\n                if (extend_size > package_max_length) {\n                    extend_size = package_max_length;\n                }\n                buffer->extend(extend_size);\n            }\n        }\n        // no eof\n        if (recv_again) {\n            goto _recv_data;\n        }\n    }\n    return SW_OK;\n}\n\n}  // namespace swoole\n\nint64_t swoole_unpack(char type, const void *data) {\n    switch (type) {\n    /*-------------------------8bit-----------------------------*/\n    case 'c':  // signed char\n        return *(const int8_t *) data;\n    case 'C':  // unsigned char\n        return *(const uint8_t *) data;\n\n    /*-------------------------16bit-----------------------------*/\n    case 's': {  // signed short (16 bit, host byte order)\n        int16_t val;\n        memcpy(&val, data, sizeof(val));\n        return val;\n    }\n    case 'S': {  // unsigned short (16 bit, host byte order)\n        uint16_t val;\n        memcpy(&val, data, sizeof(val));\n        return val;\n    }\n    case 'n': {  // unsigned short (16 bit, big endian byte order)\n        uint16_t val;\n        memcpy(&val, data, sizeof(val));\n        return ntohs(val);\n    }\n    case 'v': {  // unsigned short (16 bit, little endian byte order)\n        uint16_t val;\n        memcpy(&val, data, sizeof(val));\n#if SW_BYTE_ORDER == SW_LITTLE_ENDIAN\n        return val;\n#else\n        return swoole_swap_endian16(val);\n#endif\n    }\n\n    /*-------------------------Machine dependent size-----------------------------*/\n    case 'i': {  // signed integer (machine dependent size and byte order)\n        int val;\n        memcpy(&val, data, sizeof(val));\n        return val;\n    }\n    case 'I': {  // unsigned integer (machine dependent size and byte order)\n        unsigned int val;\n        memcpy(&val, data, sizeof(val));\n        return val;\n    }\n\n    /*-------------------------32bit-----------------------------*/\n    case 'l': {  // signed long (32 bit, host byte order)\n        int32_t val;\n        memcpy(&val, data, sizeof(val));\n        return val;\n    }\n    case 'L': {  // unsigned long (32 bit, host byte order)\n        uint32_t val;\n        memcpy(&val, data, sizeof(val));\n        return val;\n    }\n    case 'N': {  // unsigned long (32 bit, big endian byte order)\n        uint32_t val;\n        memcpy(&val, data, sizeof(val));\n        return ntohl(val);\n    }\n    case 'V': {  // unsigned long (32 bit, little endian byte order)\n        uint32_t val;\n        memcpy(&val, data, sizeof(val));\n#if SW_BYTE_ORDER == SW_LITTLE_ENDIAN\n        return val;\n#else\n        return swoole_swap_endian32(val);\n#endif\n    }\n\n    /*-------------------------64bit-----------------------------*/\n    case 'q': {  // signed long long (64 bit, host byte order)\n        int64_t val;\n        memcpy(&val, data, sizeof(val));\n        return val;\n    }\n    case 'Q': {  // unsigned long long (64 bit, host byte order)\n        uint64_t val;\n        memcpy(&val, data, sizeof(val));\n        return val;\n    }\n    case 'J': {  // unsigned long long (64 bit, big endian byte order)\n        uint64_t val;\n        memcpy(&val, data, sizeof(val));\n        return swoole_ntoh64(val);  // Network byte order (big endian) to host byte order\n    }\n    case 'P': {  // unsigned long long (64 bit, little endian byte order)\n        uint64_t val;\n        memcpy(&val, data, sizeof(val));\n#if SW_BYTE_ORDER == SW_LITTLE_ENDIAN\n        return val;\n#else\n        return swoole_swap_endian64(val);\n#endif\n    }\n    default: {\n        swoole_error_log(SW_LOG_ERROR, SW_ERROR_INVALID_PARAMS, \"Invalid format specifier '%c'\", type);\n        return -1;\n    }\n    }\n}\n\nuint64_t swoole_hton64(uint64_t value) {\n#if SW_BYTE_ORDER == SW_LITTLE_ENDIAN\n    return swoole_swap_endian64(value);\n#else\n    return value;\n#endif\n}\n\nuint64_t swoole_ntoh64(uint64_t value) {\n#if SW_BYTE_ORDER == SW_LITTLE_ENDIAN\n    return swoole_swap_endian64(value);\n#else\n    return value;\n#endif\n}\n"
  },
  {
    "path": "src/protocol/dtls.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_server.h\"\n\n#ifdef SW_SUPPORT_DTLS\nnamespace swoole {\nnamespace dtls {\n//-------------------------------------------------------------------------------\n\nint BIO_write(BIO *b, const char *data, int dlen) {\n    swoole_trace_log(SW_TRACE_SSL, \"BIO_write(%d)\", dlen);\n\n    auto *session = (Session *) BIO_get_data(b);\n    return session->socket->write(data, dlen);\n}\n\nint BIO_read(BIO *b, char *data, int len) {\n    auto *session = (Session *) BIO_get_data(b);\n    BIO_clear_retry_flags(b);\n\n    if (!session->rxqueue.empty()) {\n        Buffer *buffer = session->rxqueue.front();\n\n        swoole_trace(\"BIO_read(%d, peek=%d)=%d\", len, session->peek_mode, buffer->length);\n\n        int n = (buffer->length <= len) ? buffer->length : len;\n        memmove(data, buffer->data, n);\n\n        if (!session->peek_mode) {\n            session->rxqueue.pop_front();\n            sw_free(buffer);\n        }\n\n        return n;\n    } else {\n        BIO_set_retry_read(b);\n        return -1;\n    }\n}\n\nlong BIO_ctrl(BIO *b, int cmd, long lval, void *ptrval) {\n    long retval = 0;\n    auto *session = (Session *) BIO_get_data(b);\n\n    swoole_trace_log(SW_TRACE_SSL, \"BIO_ctrl(BIO[%p], cmd[%d], lval[%ld], ptrval[%p])\", b, cmd, lval, ptrval);\n\n    switch (cmd) {\n    case BIO_CTRL_EOF:\n        return session->rxqueue.empty();\n    case BIO_CTRL_GET_CLOSE:\n        return BIO_get_shutdown(b);\n    case BIO_CTRL_SET_CLOSE:\n        BIO_set_shutdown(b, (int) lval);\n        break;\n    case BIO_CTRL_WPENDING:\n        return 0;\n    case BIO_CTRL_PENDING:\n        return (long) session->get_buffer_length();\n\n    case BIO_CTRL_FLUSH:\n    case BIO_CTRL_DGRAM_SET_CONNECTED:\n    case BIO_CTRL_DGRAM_SET_PEER:\n        retval = 1;\n        break;\n    case BIO_CTRL_DGRAM_GET_PEER:\n        if (ptrval) {\n            memcpy(ptrval, &session->socket->info, sizeof(session->socket->info.addr));\n        }\n        retval = 1;\n        break;\n    case BIO_CTRL_DGRAM_QUERY_MTU:\n    case BIO_CTRL_DGRAM_GET_FALLBACK_MTU:\n        retval = 1500;\n        break;\n#ifdef BIO_CTRL_DGRAM_GET_MTU_OVERHEAD\n    case BIO_CTRL_DGRAM_GET_MTU_OVERHEAD:\n        retval = 96;  // random guess\n        break;\n#endif\n#ifdef BIO_CTRL_DGRAM_SET_PEEK_MODE\n    case BIO_CTRL_DGRAM_SET_PEEK_MODE:\n        ((Session *) BIO_get_data(b))->peek_mode = !!lval;\n        retval = 1;\n        break;\n#endif\n    case BIO_CTRL_PUSH:\n    case BIO_CTRL_POP:\n    case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:\n        retval = 0;\n        break;\n#if OPENSSL_VERSION_NUMBER >= 0x30000000L\n    case BIO_CTRL_GET_KTLS_SEND:\n    case BIO_CTRL_GET_KTLS_RECV:\n        retval = 0;\n        break;\n#endif\n    default:\n        swoole_warning(\"unknown cmd: %d\", cmd);\n        retval = 0;\n        break;\n    }\n\n    return retval;\n}\n\nint BIO_create(BIO *b) {\n    return 1;\n}\n\nint BIO_destroy(BIO *b) {\n    swoole_trace_log(SW_TRACE_SSL, \"BIO_destroy(BIO[%p])\\n\", b);\n    return 1;\n}\n\nstatic BIO_METHOD *_bio_methods = nullptr;\nstatic int dtls_session_index = 0;\n\nBIO_METHOD *BIO_get_methods() {\n    if (_bio_methods) {\n        return _bio_methods;\n    }\n\n    dtls_session_index = BIO_get_new_index();\n    _bio_methods = BIO_meth_new(dtls_session_index | BIO_TYPE_SOURCE_SINK, \"swoole_dtls_bio\");\n\n    BIO_meth_set_write(_bio_methods, BIO_write);\n    BIO_meth_set_read(_bio_methods, BIO_read);\n    BIO_meth_set_ctrl(_bio_methods, BIO_ctrl);\n    BIO_meth_set_create(_bio_methods, BIO_create);\n    BIO_meth_set_destroy(_bio_methods, BIO_destroy);\n\n#ifdef OPENSSL_IS_BORINGSSL\n    BIO_meth_set_ctrl(_bio_methods, (long (*)(BIO *, int, long, void *)) BIO_ctrl);\n#else\n#if OPENSSL_VERSION_NUMBER > 0x1010007fL\n    BIO_meth_set_callback_ctrl(_bio_methods, (long (*)(BIO *, int, BIO_info_cb *)) BIO_callback_ctrl);\n#else\n    BIO_meth_set_callback_ctrl(_bio_methods, (long (*)(BIO *, int, bio_info_cb *)) BIO_callback_ctrl);\n#endif\n#endif\n\n    return _bio_methods;\n}\n\nvoid BIO_meth_free() {\n    if (_bio_methods) {\n        BIO_meth_free(_bio_methods);\n    }\n\n    _bio_methods = nullptr;\n}\n\nvoid Session::append(const char *data, ssize_t len) {\n    auto *buffer = static_cast<Buffer *>(sw_malloc(sizeof(Buffer) + len));\n    buffer->length = len;\n    memcpy(buffer->data, data, buffer->length);\n    rxqueue.push_back(buffer);\n}\n\nbool Session::init() {\n    if (socket->ssl) {\n        return false;\n    }\n\n    if (socket->ssl_create(ctx.get(), SW_SSL_SERVER) < 0) {\n        swoole_set_last_error(SW_ERROR_SSL_CREATE_SESSION_FAILED);\n        return false;\n    }\n\n    socket->set_buffer_size(Socket::default_buffer_size);\n    socket->dtls = 1;\n\n    BIO *bio = BIO_new(BIO_get_methods());\n    BIO_set_data(bio, (void *) this);\n    BIO_set_init(bio, 1);\n    SSL_set_bio(socket->ssl, bio, bio);\n\n    return true;\n}\n\nbool Session::listen() {\n#ifdef OPENSSL_IS_BORINGSSL\n    return SSL_is_dtls(socket->ssl);\n#else\n\n    if (listened) {\n        return false;\n    }\n\n    ERR_clear_error();\n\n    int retval = DTLSv1_listen(socket->ssl, nullptr);\n    if (retval == 0) {\n        return true;\n    } else if (retval < 0) {\n        int reason = ERR_GET_REASON(ERR_peek_error());\n        swoole_warning(\"DTLSv1_listen() failed, client[%s:%d], reason=%d, error_string=%s\",\n                       socket->get_addr(),\n                       socket->get_port(),\n                       reason,\n                       swoole_ssl_get_error());\n        return false;\n    } else {\n        listened = true;\n    }\n    return true;\n#endif\n}\n\n//-------------------------------------------------------------------------------\n}  // namespace dtls\n}  // namespace swoole\n#endif\n"
  },
  {
    "path": "src/protocol/http.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_http.h\"\n#include \"swoole_proxy.h\"\n#include \"swoole_base64.h\"\n\n#include \"swoole_util.h\"\n#include \"swoole_http2.h\"\n#include \"swoole_websocket.h\"\n#include \"swoole_static_handler.h\"\n\n#include \"thirdparty/multipart_parser.h\"\n\nusing swoole::http_server::Request;\nusing swoole::http_server::StaticHandler;\nusing swoole::network::SendfileTask;\nusing swoole::network::Socket;\n\n// clang-format off\nstatic const char *method_strings[] = {\n    \"DELETE\", \"GET\", \"HEAD\", \"POST\", \"PUT\", \"PATCH\", \"CONNECT\", \"OPTIONS\", \"TRACE\", \"COPY\", \"LOCK\", \"MKCOL\", \"MOVE\",\n    \"PROPFIND\", \"PROPPATCH\", \"UNLOCK\", \"REPORT\", \"MKACTIVITY\", \"CHECKOUT\", \"MERGE\", \"M-SEARCH\", \"NOTIFY\",\n    \"SUBSCRIBE\", \"UNSUBSCRIBE\", \"PURGE\", \"PRI\",\n};\n// clang-format on\n\nnamespace swoole {\nHttpProxy *HttpProxy::create(const std::string &host, int port, const std::string &user, const std::string &pwd) {\n    auto http_proxy = new HttpProxy();\n    http_proxy->host = host;\n    http_proxy->port = port;\n    if (!user.empty() && !pwd.empty()) {\n        http_proxy->username = user;\n        http_proxy->password = pwd;\n    }\n    return http_proxy;\n}\n\nstd::string HttpProxy::get_auth_str() const {\n    char auth_buf[256];\n    char encode_buf[512];\n    size_t n = sw_snprintf(auth_buf,\n                           sizeof(auth_buf),\n                           \"%.*s:%.*s\",\n                           (int) username.length(),\n                           username.c_str(),\n                           (int) password.length(),\n                           password.c_str());\n    base64_encode((unsigned char *) auth_buf, n, encode_buf);\n    return {encode_buf};\n}\n\nsize_t HttpProxy::pack(const String *send_buffer, const std::string &host_name) const {\n    if (!password.empty()) {\n        auto auth_str = get_auth_str();\n        return sw_snprintf(send_buffer->str,\n                           send_buffer->size,\n                           SW_HTTP_PROXY_FMT \"Proxy-Authorization: Basic %.*s\\r\\n\\r\\n\",\n                           (int) target_host.length(),\n                           target_host.c_str(),\n                           target_port,\n                           (int) host_name.length(),\n                           host_name.c_str(),\n                           target_port,\n                           (int) auth_str.length(),\n                           auth_str.c_str());\n    } else {\n        return sw_snprintf(send_buffer->str,\n                           send_buffer->size,\n                           SW_HTTP_PROXY_FMT \"\\r\\n\",\n                           (int) target_host.length(),\n                           target_host.c_str(),\n                           target_port,\n                           (int) host_name.length(),\n                           host_name.c_str(),\n                           target_port);\n    }\n}\n\nbool HttpProxy::handshake(const String *recv_buffer) {\n    bool ret = false;\n    char *buf = recv_buffer->str;\n    size_t len = recv_buffer->length;\n    int state = 0;\n    char *p = buf;\n    char *pe = buf + len;\n\n    if (recv_buffer->length < sizeof(SW_HTTP_PROXY_HANDSHAKE_RESPONSE) - 1) {\n        return false;\n    }\n\n    for (; p < buf + len; p++) {\n        if (state == 0) {\n            if (SW_STR_ISTARTS_WITH(p, pe - p, \"HTTP/1.1\") || SW_STR_ISTARTS_WITH(p, pe - p, \"HTTP/1.0\")) {\n                state = 1;\n                p += sizeof(\"HTTP/1.x\") - 1;\n            } else {\n                break;\n            }\n        } else if (state == 1) {\n            if (isspace(*p)) {\n                continue;\n            } else {\n                if (SW_STR_ISTARTS_WITH(p, pe - p, \"200\")) {\n                    state = 2;\n                    p += sizeof(\"200\") - 1;\n                } else {\n                    swoole_set_last_error(SW_ERROR_HTTP_PROXY_HANDSHAKE_FAILED);\n                    break;\n                }\n            }\n        } else if (state == 2) {\n            ret = true;\n            break;\n            /**\n             * The response message is generally \"Connection established,\"\n             * although it is not specified in the RFC documents, and thus will not be checked for now.\n             */\n#if SW_HTTP_PROXY_CHECK_MESSAGE\n            if (isspace(*p)) {\n                continue;\n            } else {\n                if (SW_STR_ISTARTS_WITH(p, pe - p, \"Connection established\")) {\n                    ret = true;\n                }\n                break;\n            }\n#endif\n        }\n    }\n\n    return ret;\n}\n\nnamespace http_server {\n//-----------------------------------------------------------------\n\nstatic int multipart_on_header_field(multipart_parser *p, const char *at, size_t length) {\n    auto *request = static_cast<Request *>(p->data);\n    request->form_data_->current_header_name = at;\n    request->form_data_->current_header_name_len = length;\n\n    swoole_trace(\"header_field: at=%.*s, length=%lu\", (int) length, at, length);\n    return 0;\n}\n\nstatic int multipart_on_header_value(multipart_parser *p, const char *at, size_t length) {\n    swoole_trace(\"header_value: at=%.*s, length=%lu\", (int) length, at, length);\n\n    auto *request = static_cast<Request *>(p->data);\n    auto *form_data = request->form_data_;\n\n    form_data->multipart_buffer_->append(form_data->current_header_name, form_data->current_header_name_len);\n    form_data->multipart_buffer_->append(SW_STRL(\": \"));\n    form_data->multipart_buffer_->append(at, length);\n    form_data->multipart_buffer_->append(SW_STRL(\"\\r\\n\"));\n\n    if (SW_STRCASEEQ(form_data->current_header_name, form_data->current_header_name_len, \"content-disposition\")) {\n        ParseCookieCallback cb = [request, form_data, p](char *key, size_t key_len, char *value, size_t value_len) {\n            if (SW_STRCASEEQ(key, key_len, \"filename\")) {\n                memcpy(form_data->upload_tmpfile->str,\n                       form_data->upload_tmpfile_fmt_.c_str(),\n                       form_data->upload_tmpfile_fmt_.length());\n                form_data->upload_tmpfile->str[form_data->upload_tmpfile_fmt_.length()] = 0;\n                form_data->upload_filesize = 0;\n                int tmpfile = swoole_tmpfile(form_data->upload_tmpfile->str);\n                if (tmpfile < 0) {\n                    request->excepted = true;\n                    return false;\n                }\n\n                FILE *fp = fdopen(tmpfile, \"wb+\");\n                if (fp == nullptr) {\n                    swoole_sys_warning(\"fopen(%s) failed\", form_data->upload_tmpfile->str);\n                    return false;\n                }\n                p->fp = fp;\n\n                return false;\n            }\n            return true;\n        };\n        parse_cookie(at, length, cb);\n    }\n\n    return 0;\n}\n\nstatic int multipart_on_data(multipart_parser *p, const char *at, size_t length) {\n    auto request = (Request *) p->data;\n    auto form_data = request->form_data_;\n    swoole_trace(\"on_data: length=%lu\", length);\n\n    if (!p->fp) {\n        if (form_data->multipart_buffer_->length + length > request->max_length_) {\n            request->excepted = 1;\n            request->unavailable = 1;\n            return 1;\n        }\n        form_data->multipart_buffer_->append(at, length);\n        return 0;\n    }\n\n    form_data->upload_filesize += length;\n    if (form_data->upload_filesize > form_data->upload_max_filesize) {\n        request->excepted = 1;\n        request->too_large = 1;\n        return 1;\n    }\n\n    ssize_t n = fwrite(at, sizeof(char), length, p->fp);\n    if (n != (off_t) length) {\n        fclose(p->fp);\n        p->fp = nullptr;\n        request->excepted = 1;\n        request->unavailable = 1;\n        swoole_sys_warning(\"failed to write upload file\");\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic int multipart_on_header_complete(multipart_parser *p) {\n    swoole_trace(\"on_header_complete\");\n    auto *request = static_cast<Request *>(p->data);\n    auto *form_data = request->form_data_;\n    if (p->fp) {\n        form_data->multipart_buffer_->append(SW_STRL(SW_HTTP_UPLOAD_FILE \": \"));\n        form_data->multipart_buffer_->append(form_data->upload_tmpfile->str, strlen(form_data->upload_tmpfile->str));\n    }\n    request->multipart_header_parsed = 1;\n    form_data->multipart_buffer_->append(SW_STRL(\"\\r\\n\"));\n    return 0;\n}\n\nstatic int multipart_on_data_end(multipart_parser *p) {\n    swoole_trace(\"on_data_end\\n\");\n    auto *request = static_cast<Request *>(p->data);\n    auto *form_data = request->form_data_;\n    request->multipart_header_parsed = 0;\n    if (p->fp) {\n        form_data->multipart_buffer_->append(SW_STRL(\"\\r\\n\" SW_HTTP_UPLOAD_FILE));\n        fflush(p->fp);\n        fclose(p->fp);\n        p->fp = nullptr;\n    }\n    form_data->multipart_buffer_->append(SW_STRL(\"\\r\\n\"));\n    return 0;\n}\n\nstatic int multipart_on_part_begin(multipart_parser *p) {\n    swoole_trace(\"on_part_begin\");\n    auto *request = static_cast<Request *>(p->data);\n    auto *form_data = request->form_data_;\n    form_data->multipart_buffer_->append(p->boundary, p->boundary_length);\n    form_data->multipart_buffer_->append(SW_STRL(\"\\r\\n\"));\n    return 0;\n}\n\nstatic int multipart_on_body_end(multipart_parser *p) {\n    auto *request = static_cast<Request *>(p->data);\n    auto *form_data = request->form_data_;\n    form_data->multipart_buffer_->append(p->boundary, p->boundary_length);\n    form_data->multipart_buffer_->append(SW_STRL(\"--\"));\n\n    request->content_length_ = form_data->multipart_buffer_->length - request->header_length_;\n    request->tried_to_dispatch = 1;\n\n#if 0\n    /**\n     * Replace content-length with the actual value\n     */\n    char *ptr = request->multipart_buffer_->str - (sizeof(\"\\r\\n\\r\\n\") - 1);\n    char *ptr_end = request->multipart_buffer_->str + (request->multipart_buffer_->length - (sizeof(\"\\r\\n\\r\\n\") - 1));\n\n    for (; ptr < ptr_end; ptr++) {\n        if (SW_STR_ISTARTS_WITH(ptr, ptr_end - ptr, \"Content-Length:\")) {\n            ptr += (sizeof(\"Content-Length:\") - 1);\n            // skip spaces\n            while (*ptr == ' ') {\n                ptr++;\n            }\n            break;\n        }\n    }\n\n    std::string actual_content_length = std::to_string(request->content_length_);\n    memcpy(ptr, actual_content_length.c_str(), actual_content_length.length());\n\n    ptr += actual_content_length.length();\n    SW_LOOP {\n        if (*ptr == '\\r') {\n            break;\n        } else {\n            *ptr = ' ';\n            ptr++;\n        }\n    }\n#endif\n\n    swoole_trace(\"end, buffer=%.*s\", (int) form_data->multipart_buffer_->length, form_data->multipart_buffer_->str);\n\n    return 0;\n}\n\nstatic constexpr multipart_parser_settings mt_parser_settings = {\n    multipart_on_header_field,\n    multipart_on_header_value,\n    multipart_on_data,\n    multipart_on_part_begin,\n    multipart_on_header_complete,\n    multipart_on_data_end,\n    multipart_on_body_end,\n};\n\nstatic thread_local char http_status_message[128];\n\n// clang-format off\nint list_of_status_code[128] = {\n    100, 101, 102, 103,\n    200, 201, 202, 203, 204, 205, 206, 207, 208, 226,\n    300, 301, 302, 303, 304, 305, 306, 307, 308,\n    400, 401, 402, 403, 404, 405, 406, 407, 408, 409,\n    410, 411, 412, 413, 414, 415, 416, 417, 418, 421,\n    422, 423, 424, 425, 426, 428, 429, 431, 451,\n    500, 501, 502, 503, 504, 505, 506, 507, 508, 510, 511,\n    -1,\n};\n// clang-format on\n\nconst char *get_status_message(int code) {\n    switch (code) {\n    case 100:\n        return \"100 Continue\";\n    case 101:\n        return \"101 Switching Protocols\";\n    case 102:\n        return \"102 Processing\";\n    case 103:\n        return \"103 Early Hints\";\n    case 201:\n        return \"201 Created\";\n    case 202:\n        return \"202 Accepted\";\n    case 203:\n        return \"203 Non-Authoritative Information\";\n    case 204:\n        return \"204 No Content\";\n    case 205:\n        return \"205 Reset Content\";\n    case 206:\n        return \"206 Partial Content\";\n    case 207:\n        return \"207 Multi-Status\";\n    case 208:\n        return \"208 Already Reported\";\n    case 226:\n        return \"226 IM Used\";\n    case 300:\n        return \"300 Multiple Choices\";\n    case 301:\n        return \"301 Moved Permanently\";\n    case 302:\n        return \"302 Found\";\n    case 303:\n        return \"303 See Other\";\n    case 304:\n        return \"304 Not Modified\";\n    case 305:\n        return \"305 Use Proxy\";\n    case 306:\n        return \"306 (Unused)\";\n    case 307:\n        return \"307 Temporary Redirect\";\n    case 308:\n        return \"308 Permanent Redirect\";\n    case 400:\n        return \"400 Bad Request\";\n    case 401:\n        return \"401 Unauthorized\";\n    case 402:\n        return \"402 Payment Required\";\n    case 403:\n        return \"403 Forbidden\";\n    case 404:\n        return \"404 Not Found\";\n    case 405:\n        return \"405 Method Not Allowed\";\n    case 406:\n        return \"406 Not Acceptable\";\n    case 407:\n        return \"407 Proxy Authentication Required\";\n    case 408:\n        return \"408 Request Timeout\";\n    case 409:\n        return \"409 Conflict\";\n    case 410:\n        return \"410 Gone\";\n    case 411:\n        return \"411 Length Required\";\n    case 412:\n        return \"412 Precondition Failed\";\n    case 413:\n        return \"413 Payload Too Large\";\n    case 414:\n        return \"414 URI Too Long\";\n    case 415:\n        return \"415 Unsupported Media Type\";\n    case 416:\n        return \"416 Range Not Satisfiable\";\n    case 417:\n        return \"417 Expectation Failed\";\n    case 418:\n        return \"418 I'm a teapot\";\n    case 421:\n        return \"421 Misdirected Request\";\n    case 422:\n        return \"422 Unprocessable Entity\";\n    case 423:\n        return \"423 Locked\";\n    case 424:\n        return \"424 Failed Dependency\";\n    case 425:\n        return \"425 Too Early\";\n    case 426:\n        return \"426 Upgrade Required\";\n    case 428:\n        return \"428 Precondition Required\";\n    case 429:\n        return \"429 Too Many Requests\";\n    case 431:\n        return \"431 Request Header Fields Too Large\";\n    case 451:\n        return \"451 Unavailable For Legal Reasons\";\n    case 500:\n        return \"500 Internal Server Error\";\n    case 501:\n        return \"501 Not Implemented\";\n    case 502:\n        return \"502 Bad Gateway\";\n    case 503:\n        return \"503 Service Unavailable\";\n    case 504:\n        return \"504 Gateway Timeout\";\n    case 505:\n        return \"505 HTTP Version Not Supported\";\n    case 506:\n        return \"506 Variant Also Negotiates\";\n    case 507:\n        return \"507 Insufficient Storage\";\n    case 508:\n        return \"508 Loop Detected\";\n    case 510:\n        return \"510 Not Extended\";\n    case 511:\n        return \"511 Network Authentication Required\";\n    case 200:\n    case 0:\n        return \"200 OK\";\n    default:\n        sw_snprintf(http_status_message, sizeof(http_status_message), \"%d Unknown Status\", code);\n        return http_status_message;\n    }\n}\n\nvoid parse_cookie(const char *at, size_t length, const ParseCookieCallback &cb) {\n    char *value;\n    const char *separator = \";\\0\";\n    size_t key_len = 0;\n    char *strtok_buf = nullptr;\n\n    char *_c = sw_tg_buffer()->str;\n    memcpy(_c, at, length);\n    _c[length] = '\\0';\n\n    char *key = strtok_r(_c, separator, &strtok_buf);\n    while (key) {\n        size_t value_len;\n        value = strchr(key, '=');\n\n        while (isspace(*key)) {\n            key++;\n        }\n\n        if (key == value || *key == '\\0') {\n            goto next_cookie;\n        }\n\n        if (value) {\n            *value++ = '\\0';\n            value_len = strlen(value);\n        } else {\n            value = (char *) \"\";\n            value_len = 0;\n        }\n\n        key_len = strlen(key);\n        if (!cb(key, key_len, value, value_len)) {\n            break;\n        }\n    next_cookie:\n        key = strtok_r(nullptr, separator, &strtok_buf);\n    }\n}\n\nbool parse_multipart_boundary(\n    const char *at, size_t length, size_t offset, char **out_boundary_str, int *out_boundary_len) {\n    while (offset < length) {\n        if (at[offset] == ' ' || at[offset] == ';') {\n            offset++;\n            continue;\n        }\n\n        if (offset + sizeof(\"boundary=\") - 1 <= length &&\n            SW_STR_ISTARTS_WITH(at + offset, length - offset, \"boundary=\")) {\n            offset += sizeof(\"boundary=\") - 1;\n            break;\n        }\n\n        void *delimiter = memchr((void *) (at + offset), ';', length - offset);\n        if (delimiter == nullptr) {\n            return false;\n        } else {\n            offset += (const char *) delimiter - (at + offset);\n        }\n    }\n\n    if (offset >= length) {\n        return false;\n    }\n\n    int boundary_len = length - offset;\n    char *boundary_str = (char *) at + offset;\n    // find eof of boundary\n    if (boundary_len > 0) {\n        // find ';'\n        char *semicolon = (char *) memchr(boundary_str, ';', boundary_len);\n        if (semicolon) {\n            boundary_len = semicolon - boundary_str;\n        }\n    }\n    if (boundary_len <= 0) {\n        return false;\n    }\n    // trim '\"'\n    if (boundary_len >= 2 && boundary_str[0] == '\"' && boundary_str[boundary_len - 1] == '\"') {\n        boundary_str++;\n        boundary_len -= 2;\n        if (boundary_len <= 0) {\n            return false;\n        }\n    }\n    *out_boundary_str = boundary_str;\n    *out_boundary_len = boundary_len;\n\n    return true;\n}\n\nstatic int url_htoi(const char *s) {\n    int c = ((unsigned char *) s)[0];\n    if (isupper(c)) {\n        c = tolower(c);\n    }\n    int value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;\n\n    c = ((unsigned char *) s)[1];\n    if (isupper(c)) {\n        c = tolower(c);\n    }\n    value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;\n\n    return (value);\n}\n\n/* return value: length of decoded string */\nsize_t url_decode(char *str, size_t len) {\n    char *dest = str;\n    char *data = str;\n\n    while (len--) {\n        if (*data == '+') {\n            *dest = ' ';\n        } else if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1)) && isxdigit((int) *(data + 2))) {\n            *dest = (char) url_htoi(data + 1);\n            data += 2;\n            len -= 2;\n        } else {\n            *dest = *data;\n        }\n        data++;\n        dest++;\n    }\n    *dest = '\\0';\n\n    return dest - str;\n}\n\nchar *url_encode(char const *str, size_t len) {\n    static uchar hexchars[] = \"0123456789ABCDEF\";\n\n    size_t x, y;\n    char *ret = (char *) sw_malloc(len * 3);\n\n    for (x = 0, y = 0; len--; x++, y++) {\n        char c = str[x];\n\n        ret[y] = c;\n        if ((c < '0' && c != '-' && c != '.') || (c < 'A' && c > '9') || (c > 'Z' && c < 'a' && c != '_') ||\n            (c > 'z' && c != '~')) {\n            ret[y++] = '%';\n            ret[y++] = hexchars[(unsigned char) c >> 4];\n            ret[y] = hexchars[(unsigned char) c & 15];\n        }\n    }\n    ret[y] = '\\0';\n\n    do {\n        size_t size = y + 1;\n        char *tmp = (char *) sw_malloc(size);\n        memcpy(tmp, ret, size);\n        sw_free(ret);\n        ret = tmp;\n    } while (false);\n\n    return ret;\n}\n\nint Request::get_protocol() {\n    char *p = buffer_->str;\n    char *pe = p + buffer_->length;\n    char state = 0;\n\n    if (buffer_->length < (sizeof(\"GET / HTTP/1.x\\r\\n\") - 1)) {\n        return SW_ERR;\n    }\n\n    // HTTP Method\n    SW_LOOP_N(sizeof(method_strings) / sizeof(char *) - 1) {\n        auto method_str = method_strings[i];\n        auto n = strlen(method_str);\n        if (memcmp(p, method_str, n) == 0) {\n            method = i + 1;\n            p += n;\n            goto _found_method;\n        }\n    }\n    // HTTP2 Connection Preface\n    if (memcmp(p, SW_STRL(\"PRI\")) == 0) {\n        method = SW_HTTP_PRI;\n        if (buffer_->length >= (sizeof(SW_HTTP2_PRI_STRING) - 1) && memcmp(p, SW_STRL(SW_HTTP2_PRI_STRING)) == 0) {\n            buffer_->offset = sizeof(SW_HTTP2_PRI_STRING) - 1;\n            return SW_OK;\n        } else {\n            goto _excepted;\n        }\n    } else {\n    _excepted:\n        excepted = 1;\n        return SW_ERR;\n    }\n\n    // HTTP Version\n_found_method:\n    for (; p < pe; p++) {\n        switch (state) {\n        case 0:\n            if (*p == ' ') {\n                continue;\n            }\n            state = 1;\n            url_offset_ = p - buffer_->str;\n            break;\n        case 1:\n            if (*p == ' ') {\n                state = 2;\n                url_length_ = p - buffer_->str - url_offset_;\n                continue;\n            } else if (*p == '\\r' || *p == '\\n') {\n                goto _excepted;\n            }\n            break;\n        case 2:\n            if (*p == ' ') {\n                continue;\n            }\n            if ((size_t) (pe - p) < (sizeof(\"HTTP/1.x\") - 1)) {\n                return SW_ERR;\n            }\n            if (memcmp(p, SW_STRL(\"HTTP/1.1\")) == 0) {\n                version = SW_HTTP_VERSION_11;\n                goto _end;\n            } else if (memcmp(p, SW_STRL(\"HTTP/1.0\")) == 0) {\n                version = SW_HTTP_VERSION_10;\n                goto _end;\n            } else {\n                goto _excepted;\n            }\n        default:\n            break;\n        }\n    }\n_end:\n    p += sizeof(\"HTTP/1.x\") - 1;\n    request_line_length_ = buffer_->offset = p - buffer_->str;\n    return SW_OK;\n}\n\n/**\n * simple get headers info\n */\nvoid Request::parse_header_info() {\n    // header field start\n    char *p = buffer_->str + request_line_length_ + (sizeof(\"\\r\\n\") - 1);\n    // point-end: start + strlen(all-header) without strlen(\"\\r\\n\\r\\n\")\n    char *pe = buffer_->str + header_length_ - (sizeof(\"\\r\\n\\r\\n\") - 1);\n\n    for (; p < pe; p++) {\n        if (*(p - 1) == '\\n' && *(p - 2) == '\\r') {\n            if (SW_STR_ISTARTS_WITH(p, pe - p, \"Content-Length:\")) {\n                // strlen(\"Content-Length:\")\n                p += (sizeof(\"Content-Length:\") - 1);\n                // skip spaces\n                while (*p == ' ') {\n                    p++;\n                }\n                content_length_ = strtoull(p, nullptr, 10);\n                known_length = 1;\n            } else if (SW_STR_ISTARTS_WITH(p, pe - p, \"Connection:\")) {\n                // strlen(\"Connection:\")\n                p += (sizeof(\"Connection:\") - 1);\n                // skip spaces\n                while (*p == ' ') {\n                    p++;\n                }\n                if (SW_STR_ISTARTS_WITH(p, pe - p, \"keep-alive\")) {\n                    keep_alive = 1;\n                }\n            } else if (SW_STR_ISTARTS_WITH(p, pe - p, \"Transfer-Encoding:\")) {\n                // strlen(\"Transfer-Encoding:\")\n                p += (sizeof(\"Transfer-Encoding:\") - 1);\n                // skip spaces\n                while (*p == ' ') {\n                    p++;\n                }\n                if (SW_STR_ISTARTS_WITH(p, pe - p, \"chunked\")) {\n                    chunked = 1;\n                }\n            } else if (SW_STR_ISTARTS_WITH(p, pe - p, \"Content-Type:\")) {\n                p += (sizeof(\"Content-Type:\") - 1);\n                while (*p == ' ') {\n                    p++;\n                }\n                if (SW_STR_ISTARTS_WITH(p, pe - p, \"multipart/form-data\")) {\n                    form_data_ = new FormData();\n                    form_data_->multipart_boundary_buf = p + (sizeof(\"multipart/form-data\") - 1);\n                    form_data_->multipart_boundary_len = strchr(p, '\\r') - form_data_->multipart_boundary_buf;\n                }\n            }\n        }\n    }\n\n    header_parsed = 1;\n    if (chunked && known_length && content_length_ == 0) {\n        nobody_chunked = 1;\n    }\n}\n\nbool Request::init_multipart_parser(const Server *server) {\n    char *boundary_str;\n    int boundary_len;\n    if (!parse_multipart_boundary(\n            form_data_->multipart_boundary_buf, form_data_->multipart_boundary_len, 0, &boundary_str, &boundary_len)) {\n        return false;\n    }\n\n    form_data_->multipart_parser_ = multipart_parser_init(boundary_str, boundary_len, &mt_parser_settings);\n    if (!form_data_->multipart_parser_) {\n        swoole_warning(\"multipart_parser_init() failed\");\n        return false;\n    }\n    form_data_->multipart_parser_->data = this;\n\n    auto tmp_buffer = new String(SW_BUFFER_SIZE_BIG);\n    tmp_buffer->append(buffer_->str + header_length_, buffer_->length - header_length_);\n    form_data_->multipart_buffer_ = buffer_;\n    form_data_->multipart_buffer_->length = header_length_;\n    buffer_ = tmp_buffer;\n    form_data_->upload_tmpfile_fmt_ = server->upload_tmp_dir + \"/swoole.upfile.XXXXXX\";\n    form_data_->upload_tmpfile = new String(form_data_->upload_tmpfile_fmt_);\n    form_data_->upload_max_filesize = server->upload_max_filesize;\n\n    return true;\n}\n\nvoid Request::destroy_multipart_parser() {\n    auto tmp_buffer = buffer_;\n    delete tmp_buffer;\n    buffer_ = form_data_->multipart_buffer_;\n    form_data_->multipart_buffer_ = nullptr;\n    if (form_data_->multipart_parser_->fp) {\n        fclose(form_data_->multipart_parser_->fp);\n        unlink(form_data_->upload_tmpfile->str);\n    }\n    multipart_parser_free(form_data_->multipart_parser_);\n    form_data_->multipart_parser_ = nullptr;\n    delete form_data_->upload_tmpfile;\n    form_data_->upload_tmpfile = nullptr;\n    delete form_data_;\n    form_data_ = nullptr;\n}\n\nbool Request::parse_multipart_data(String *buffer) {\n    excepted = 0;\n    ssize_t n = multipart_parser_execute(form_data_->multipart_parser_, buffer->str, buffer->length);\n    swoole_trace(\"multipart_parser_execute: buffer->length=%lu, n=%lu\\n\", buffer->length, n);\n    if (n < 0) {\n        int l_error =\n            multipart_parser_error_msg(form_data_->multipart_parser_, sw_tg_buffer()->str, sw_tg_buffer()->size);\n        swoole_error_log(SW_LOG_NOTICE,\n                         SW_ERROR_SERVER_INVALID_REQUEST,\n                         \"parse multipart body failed, reason: %.*s\",\n                         l_error,\n                         sw_tg_buffer()->str);\n        return false;\n    } else if ((size_t) n != buffer->length) {\n        swoole_error_log(SW_LOG_NOTICE,\n                         SW_ERROR_SERVER_INVALID_REQUEST,\n                         \"parse multipart body failed, %zu/%zu bytes processed\",\n                         n,\n                         buffer->length);\n        return excepted;\n    }\n    buffer->clear();\n    return true;\n}\n\nRequest::~Request() {\n    if (form_data_) {\n        destroy_multipart_parser();\n    }\n}\n\nbool Request::has_expect_header() const {\n    char *buf = buffer_->str;\n    size_t len = buffer_->length;\n\n    char *pe = buf + len;\n\n    for (char *p = buf; p < pe; p++) {\n        if (*p == '\\r' && (size_t) (pe - p) > sizeof(\"\\r\\nExpect\")) {\n            p += 2;\n            if (SW_STR_ISTARTS_WITH(p, pe - p, \"Expect: \")) {\n                p += sizeof(\"Expect: \") - 1;\n                if (SW_STR_ISTARTS_WITH(p, pe - p, \"100-continue\")) {\n                    return true;\n                } else {\n                    return false;\n                }\n            } else {\n                p++;\n            }\n        }\n    }\n    return false;\n}\n\nint Request::get_header_length() {\n    char *p = buffer_->str + buffer_->offset;\n    char *pe = buffer_->str + buffer_->length;\n\n    for (; p <= pe - (sizeof(\"\\r\\n\\r\\n\") - 1); p++) {\n        if (memcmp(p, SW_STRL(\"\\r\\n\\r\\n\")) == 0) {\n            // strlen(header) + strlen(\"\\r\\n\\r\\n\")\n            header_length_ = buffer_->offset = p - buffer_->str + (sizeof(\"\\r\\n\\r\\n\") - 1);\n            return SW_OK;\n        }\n    }\n\n    buffer_->offset = p - buffer_->str;\n    return SW_ERR;\n}\n\nint Request::get_chunked_body_length() {\n    const char *p = buffer_->str + buffer_->offset;\n    const char *pe = buffer_->str + buffer_->length;\n\n    /**\n     * Ending with SW_HTTP_CHUNK_EOF indicates that the HTTP request may have been fully received,\n     * but this is not certain. It is still necessary to skip over the data sections based on\n     * the length of each chunk of the HTTP data until SW_HTTP_CHUNK_EOF (\\0\\r\\n\\r\\n) is found.\n     */\n    if (static_cast<size_t>(pe - p) < sizeof(SW_HTTP_CHUNK_EOF) - 1 ||\n        memcmp(pe - sizeof(SW_HTTP_CHUNK_EOF) + 1, SW_STRL(SW_HTTP_CHUNK_EOF)) != 0) {\n        return SW_ERR;\n    }\n\n    while (true) {\n        char *endptr;\n        size_t chunk_length = strtoul(p, &endptr, 16);\n        if (endptr == nullptr || *endptr != '\\r') {\n            break;\n        }\n\n        swoole_trace_log(SW_TRACE_HTTP, \"chunk_length=%zu, chunk_len_str=%.*s\\n\", chunk_length, (int) (endptr - p), p);\n\n        // Found the HTTP Chunk EOF\n        if (chunk_length == 0) {\n            known_length = 1;\n            content_length_ = endptr - (buffer_->str + header_length_) + 4;\n            return SW_OK;\n        } else {\n            // chunk length [hex str] + CRLF + data + CRLF\n            p = endptr + 2 + chunk_length + 2;\n            // Continue to parse the next segment of HTTP CHUNK data.\n            if (p < pe) {\n                continue;\n            }\n            /**\n             * If the calculated data length exceeds the length of the currently received data,\n             * then SW_HTTP_CHUNK_EOF may be part of the data,\n             * necessitating the reception of additional data and recalculating the HTTP Chunk length.\n             */\n            return SW_ERR;\n        }\n        break;\n    }\n\n    excepted = 1;\n    return SW_ERR;\n}\n\nstd::string Request::get_header(const char *name) const {\n    size_t name_len = strlen(name);\n    char *p = buffer_->str + url_offset_ + url_length_ + 10;\n    char *pe = buffer_->str + header_length_;\n\n    char *buffer = nullptr;\n    char *colon = nullptr;\n\n    int state = 0;\n    int i = 0;\n\n    bool is_error_header_name = false;\n\n    for (; p < pe; p++) {\n        switch (state) {\n        case 0:\n            if (SW_STR_ISTARTS_WITH(p, pe - p, \"\\r\\n\")) {\n                i = 0;\n                is_error_header_name = false;\n                break;\n            }\n\n            if (!is_error_header_name && swoole_str_istarts_with(p, pe - p, name, name_len)) {\n                colon = p + name_len;\n                if (colon[0] != ':' || i > 1) {\n                    is_error_header_name = true;\n                    break;\n                }\n\n                p += name_len;\n                state = 1;\n            }\n\n            i++;\n            break;\n        case 1:\n            if (!isspace(*p)) {\n                buffer = p;\n                state = 2;\n            }\n            break;\n        case 2:\n            if (SW_STR_ISTARTS_WITH(p, pe - p, \"\\r\\n\")) {\n                return {buffer, static_cast<size_t>(p - buffer)};\n            }\n            break;\n        default:\n            break;\n        }\n    }\n\n    return {};\n}\n\nint get_method(const char *method_str, size_t method_len) {\n    int i = 0;\n    for (; i < SW_HTTP_PRI; i++) {\n        if (swoole_strcaseeq(method_strings[i], strlen(method_strings[i]), method_str, method_len)) {\n            return i + 1;\n        }\n    }\n    return -1;\n}\n\nconst char *get_method_string(int method) {\n    if (method <= 0 || method > SW_HTTP_PRI) {\n        return nullptr;\n    }\n    return method_strings[method - 1];\n}\n\nint dispatch_request(Server *serv, const Protocol *proto, Socket *_socket, const RecvData *rdata) {\n    if (serv->is_unavailable()) {\n        _socket->send(SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET), 0);\n        return SW_ERR;\n    }\n    return Server::dispatch_task(proto, _socket, rdata);\n}\n\n//-----------------------------------------------------------------\nstatic void protocol_status_error(Socket *socket, const Connection *conn) {\n    swoole_error_log(SW_LOG_WARNING,\n                     SW_ERROR_PROTOCOL_ERROR,\n                     \"unexpected protocol status of session#%ld<%s:%d>\",\n                     conn->session_id,\n                     conn->info.get_addr(),\n                     conn->info.get_port());\n}\n\nssize_t get_package_length(const Protocol *protocol, Socket *socket, PacketLength *pl) {\n    auto *conn = static_cast<Connection *>(socket->object);\n    if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) {\n        return websocket::get_package_length(protocol, socket, pl);\n    } else if (conn->http2_stream) {\n        return http2::get_frame_length(protocol, socket, pl);\n    } else {\n        protocol_status_error(socket, conn);\n        return SW_ERR;\n    }\n}\n\nuint8_t get_package_length_size(Socket *socket) {\n    auto *conn = (Connection *) socket->object;\n    if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) {\n        return SW_WEBSOCKET_FRAME_HEADER_SIZE;\n    } else if (conn->http2_stream) {\n        return SW_HTTP2_FRAME_HEADER_SIZE;\n    } else {\n        protocol_status_error(socket, conn);\n        return 0;\n    }\n}\n\nint dispatch_frame(const Protocol *proto, Socket *socket, const RecvData *rdata) {\n    auto *conn = (Connection *) socket->object;\n    if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) {\n        return websocket::dispatch_frame(proto, socket, rdata);\n    } else if (conn->http2_stream) {\n        return Server::dispatch_task(proto, socket, rdata);\n    } else {\n        protocol_status_error(socket, conn);\n        return SW_ERR;\n    }\n}\n}  // namespace http_server\n}  // namespace swoole\n"
  },
  {
    "path": "src/protocol/http2.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2017 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_socket.h\"\n#include \"swoole_http2.h\"\n#include \"swoole_protocol.h\"\n\nusing swoole::PacketLength;\nusing swoole::Protocol;\nusing swoole::network::Socket;\n\nnamespace swoole {\nnamespace http2 {\n\nstatic Settings default_settings = {\n    SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE,\n    SW_HTTP2_DEFAULT_ENABLE_PUSH,\n    SW_HTTP2_DEFAULT_MAX_CONCURRENT_STREAMS,\n    SW_HTTP2_DEFAULT_INIT_WINDOW_SIZE,\n    SW_HTTP2_DEFAULT_MAX_FRAME_SIZE,\n    SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE,\n};\n\nvoid put_default_setting(enum swHttp2SettingId id, uint32_t value) {\n    switch (id) {\n    case SW_HTTP2_SETTING_HEADER_TABLE_SIZE:\n        default_settings.header_table_size = value;\n        break;\n    case SW_HTTP2_SETTINGS_ENABLE_PUSH:\n        default_settings.enable_push = value;\n        break;\n    case SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:\n        default_settings.max_concurrent_streams = value;\n        break;\n    case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE:\n        default_settings.init_window_size = value;\n        break;\n    case SW_HTTP2_SETTINGS_MAX_FRAME_SIZE:\n        default_settings.max_frame_size = value;\n        break;\n    case SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:\n        default_settings.max_header_list_size = value;\n        break;\n    default:\n        assert(0);\n        break;\n    }\n}\n\nuint32_t get_default_setting(enum swHttp2SettingId id) {\n    switch (id) {\n    case SW_HTTP2_SETTING_HEADER_TABLE_SIZE:\n        return default_settings.header_table_size;\n    case SW_HTTP2_SETTINGS_ENABLE_PUSH:\n        return default_settings.enable_push;\n    case SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:\n        return default_settings.max_concurrent_streams;\n    case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE:\n        return default_settings.init_window_size;\n    case SW_HTTP2_SETTINGS_MAX_FRAME_SIZE:\n        return default_settings.max_frame_size;\n    case SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:\n        return default_settings.max_header_list_size;\n    default:\n        assert(0);\n        return 0;\n    }\n}\n\nstatic inline void pack_setting_item(char *_buf, enum swHttp2SettingId _id, uint32_t _value) {\n    uint16_t id;\n    uint32_t value;\n    id = htons(_id);\n    memcpy(_buf, &id, sizeof(id));\n    value = htonl(_value);\n    memcpy(_buf + 2, &value, sizeof(value));\n}\n\nsize_t pack_setting_frame(char *buf, const Settings &settings, bool server_side) {\n    char *p = buf;\n    size_t size = SW_HTTP2_SETTING_OPTION_SIZE * (server_side ? 5 : 6);\n    set_frame_header(p, SW_HTTP2_TYPE_SETTINGS, size, 0, 0);\n    p += SW_HTTP2_FRAME_HEADER_SIZE;\n\n    pack_setting_item(p, SW_HTTP2_SETTING_HEADER_TABLE_SIZE, settings.header_table_size);\n    p += SW_HTTP2_SETTING_OPTION_SIZE;\n\n    if (!server_side) {\n        pack_setting_item(p, SW_HTTP2_SETTINGS_ENABLE_PUSH, settings.enable_push);\n        p += SW_HTTP2_SETTING_OPTION_SIZE;\n    }\n\n    pack_setting_item(p, SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, settings.max_concurrent_streams);\n    p += SW_HTTP2_SETTING_OPTION_SIZE;\n\n    pack_setting_item(p, SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE, settings.init_window_size);\n    p += SW_HTTP2_SETTING_OPTION_SIZE;\n\n    pack_setting_item(p, SW_HTTP2_SETTINGS_MAX_FRAME_SIZE, settings.max_frame_size);\n    p += SW_HTTP2_SETTING_OPTION_SIZE;\n\n    pack_setting_item(p, SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, settings.max_header_list_size);\n    p += SW_HTTP2_SETTING_OPTION_SIZE;\n\n    return p - buf;\n}\n\nReturnCode unpack_setting_data(const char *buf,\n                               ssize_t length,\n                               const std::function<ReturnCode(uint16_t, uint32_t)> &cb) {\n    uint16_t id = 0;\n    uint32_t value = 0;\n\n    while (length > 0) {\n        id = ntohs(*(uint16_t *) (buf));\n        value = ntohl(*(uint32_t *) (buf + sizeof(uint16_t)));\n\n        auto rc = cb(id, value);\n        if (rc != SW_SUCCESS) {\n            return rc;\n        }\n\n        buf += sizeof(id) + sizeof(value);\n        length -= sizeof(id) + sizeof(value);\n    }\n\n    return SW_SUCCESS;\n}\n\nint send_setting_frame(Protocol *protocol, Socket *_socket) {\n    char setting_frame[SW_HTTP2_SETTING_FRAME_SIZE];\n    size_t n = pack_setting_frame(setting_frame, default_settings, true);\n    return _socket->send(setting_frame, n, 0);\n}\n\n/**\n +-----------------------------------------------+\n |                 Length (24)                   |\n +---------------+---------------+---------------+\n |   Type (8)    |   Flags (8)   |\n +-+-------------+---------------+-------------------------------+\n |R|                 Stream Identifier (31)                      |\n +=+=============================================================+\n |                   Frame Payload (0...)                      ...\n +---------------------------------------------------------------+\n */\nssize_t get_frame_length(const Protocol *protocol, Socket *conn, PacketLength *pl) {\n    if (pl->buf_size < SW_HTTP2_FRAME_HEADER_SIZE) {\n        return 0;\n    }\n    return get_length(pl->buf) + SW_HTTP2_FRAME_HEADER_SIZE;\n}\n\nconst char *get_type(int type) {\n    switch (type) {\n    case SW_HTTP2_TYPE_DATA:\n        return \"DATA\";\n    case SW_HTTP2_TYPE_HEADERS:\n        return \"HEADERS\";\n    case SW_HTTP2_TYPE_PRIORITY:\n        return \"PRIORITY\";\n    case SW_HTTP2_TYPE_RST_STREAM:\n        return \"RST_STREAM\";\n    case SW_HTTP2_TYPE_SETTINGS:\n        return \"SETTINGS\";\n    case SW_HTTP2_TYPE_PUSH_PROMISE:\n        return \"PUSH_PROMISE\";\n    case SW_HTTP2_TYPE_PING:\n        return \"PING\";\n    case SW_HTTP2_TYPE_GOAWAY:\n        return \"GOAWAY\";\n    case SW_HTTP2_TYPE_WINDOW_UPDATE:\n        return \"WINDOW_UPDATE\";\n    case SW_HTTP2_TYPE_CONTINUATION:\n        return \"CONTINUATION\";\n    default:\n        return \"UNKNOWN\";\n    }\n}\n\nint get_type_color(int type) {\n    switch (type) {\n    case SW_HTTP2_TYPE_DATA:\n    case SW_HTTP2_TYPE_WINDOW_UPDATE:\n        return SW_COLOR_MAGENTA;\n    case SW_HTTP2_TYPE_HEADERS:\n    case SW_HTTP2_TYPE_SETTINGS:\n    case SW_HTTP2_TYPE_PUSH_PROMISE:\n    case SW_HTTP2_TYPE_CONTINUATION:\n        return SW_COLOR_GREEN;\n    case SW_HTTP2_TYPE_PING:\n    case SW_HTTP2_TYPE_PRIORITY:\n        return SW_COLOR_WHITE;\n    case SW_HTTP2_TYPE_RST_STREAM:\n    case SW_HTTP2_TYPE_GOAWAY:\n    default:\n        return SW_COLOR_RED;\n    }\n}\n\n}  // namespace http2\n}  // namespace swoole\n"
  },
  {
    "path": "src/protocol/message_bus.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2015 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_message_bus.h\"\n#include \"swoole_process_pool.h\"\n\n#include <cassert>\n\nusing swoole::network::Address;\nusing swoole::network::Socket;\n\nnamespace swoole {\n\nPacketPtr MessageBus::get_packet() const {\n    PacketPtr pkt;\n    if (buffer_->info.flags & SW_EVENT_DATA_PTR) {\n        memcpy(&pkt, buffer_->data, sizeof(pkt));\n    } else if (buffer_->info.flags & SW_EVENT_DATA_OBJ_PTR) {\n        String *object;\n        memcpy(&object, buffer_->data, sizeof(object));\n        pkt.data = object->str;\n        pkt.length = object->length;\n    } else {\n        pkt.data = buffer_->data;\n        pkt.length = buffer_->info.len;\n    }\n\n    return pkt;\n}\n\nbool MessageBus::alloc_buffer() {\n    void *_ptr = allocator_->malloc(buffer_size_);\n    if (_ptr) {\n        buffer_ = (PipeBuffer *) _ptr;\n        sw_memset_zero(&buffer_->info, sizeof(buffer_->info));\n        return true;\n    } else {\n        return false;\n    }\n}\n\nvoid MessageBus::pass(const SendData *task) const {\n    memcpy(&buffer_->info, &task->info, sizeof(buffer_->info));\n    if (task->info.len > 0) {\n        buffer_->info.flags = SW_EVENT_DATA_PTR;\n        PacketPtr pkt{task->info.len, (char *) task->data};\n        buffer_->info.len = sizeof(pkt);\n        memcpy(buffer_->data, &pkt, sizeof(pkt));\n    }\n}\n\nchar *MessageBus::move_packet() {\n    uint64_t msg_id = buffer_->info.msg_id;\n    auto iter = packet_pool_.find(msg_id);\n    if (iter != packet_pool_.end()) {\n        auto str = iter->second.get();\n        char *val = str->str;\n        str->str = nullptr;\n        return val;\n    } else {\n        return nullptr;\n    }\n}\n\nString *MessageBus::get_packet_buffer() {\n    String *packet_buffer = nullptr;\n\n    auto iter = packet_pool_.find(buffer_->info.msg_id);\n    if (iter == packet_pool_.end()) {\n        if (!buffer_->is_begin()) {\n            return nullptr;\n        }\n        packet_buffer = make_string(buffer_->info.len, allocator_);\n        packet_pool_.emplace(buffer_->info.msg_id, std::shared_ptr<String>(packet_buffer));\n    } else {\n        packet_buffer = iter->second.get();\n    }\n\n    return packet_buffer;\n}\n\nReturnCode MessageBus::prepare_packet(uint16_t &recv_chunk_count, String *packet_buffer) {\n    recv_chunk_count++;\n    if (!buffer_->is_end()) {\n        /**\n         * if the reactor thread sends too many chunks to the worker process,\n         * the worker process may receive chunks all the time,\n         * resulting in the worker process being unable to handle other tasks.\n         * in order to make the worker process handle tasks fairly,\n         * the maximum number of consecutive chunks received by the worker is limited.\n         */\n        if (recv_chunk_count >= SW_WORKER_MAX_RECV_CHUNK_COUNT) {\n            swoole_trace_log(SW_TRACE_WORKER,\n                             \"worker#%d receives the chunk data to the maximum[%d], return to event loop\",\n                             swoole_get_worker_id(),\n                             recv_chunk_count);\n            return SW_WAIT;\n        }\n        return SW_CONTINUE;\n    } else {\n        /**\n         * Because we don't want to split the EventData parameters into DataHead and data,\n         * we store the value of the worker_buffer pointer in EventData.data.\n         * The value of this pointer will be fetched in the Server::get_pipe_packet() function.\n         */\n        buffer_->info.flags |= SW_EVENT_DATA_OBJ_PTR;\n        memcpy(buffer_->data, &packet_buffer, sizeof(packet_buffer));\n        swoole_trace(\"msg_id=%\" PRIu64 \", len=%u\", buffer_->info.msg_id, buffer_->info.len);\n\n        return SW_READY;\n    }\n}\n\n/**\n * @return -1: a fatal error has occurred and needs to be terminated\n * @return 0: continue\n * @return >0: success\n */\nssize_t MessageBus::read(Socket *sock) {\n    ssize_t recv_n = 0;\n    uint16_t recv_chunk_count = 0;\n    DataHead *info = &buffer_->info;\n    struct iovec buffers[2];\n\n_read_from_pipe:\n    recv_n = recv(sock->get_fd(), info, sizeof(buffer_->info), MSG_PEEK);\n    if (recv_n < 0) {\n        if (sock->catch_read_error(errno) == SW_WAIT) {\n            return SW_OK;\n        }\n        return SW_ERR;\n    } else if (recv_n == 0) {\n        swoole_warning(\"receive data from socket#%d returns 0\", sock->get_fd());\n        return SW_ERR;\n    }\n\n    if (!buffer_->is_chunked()) {\n        return sock->read(buffer_, sizeof(buffer_->info) + buffer_->info.len);\n    }\n\n    auto packet_buffer = get_packet_buffer();\n    if (packet_buffer == nullptr) {\n        swoole_error_log(SW_LOG_WARNING,\n                         SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA,\n                         \"abnormal pipeline data, msg_id=%\" PRIu64 \", pipe_fd=%d, reactor_id=%d\",\n                         info->msg_id,\n                         sock->get_fd(),\n                         info->reactor_id);\n        // Read data from the socket buffer and discard it.\n        recv(sock->get_fd(), info, sizeof(buffer_->info), 0);\n        return SW_OK;\n    }\n\n    size_t remain_len = buffer_->info.len - packet_buffer->length;\n    buffers[0].iov_base = info;\n    buffers[0].iov_len = sizeof(buffer_->info);\n    buffers[1].iov_base = packet_buffer->str + packet_buffer->length;\n    buffers[1].iov_len = SW_MIN(buffer_size_ - sizeof(buffer_->info), remain_len);\n\n    recv_n = readv(sock->get_fd(), buffers, 2);\n    if (recv_n == 0) {\n        swoole_warning(\"receive pipeline data error, pipe_fd=%d, reactor_id=%d\", sock->get_fd(), info->reactor_id);\n        return SW_ERR;\n    }\n    if (recv_n < 0 && sock->catch_read_error(errno) == SW_WAIT) {\n        return SW_OK;\n    }\n    if (recv_n > 0) {\n        packet_buffer->length += (recv_n - sizeof(buffer_->info));\n        swoole_trace(\"append msgid=%\" PRIu64 \", buffer=%p, n=%ld\", buffer_->info.msg_id, packet_buffer, recv_n);\n    }\n\n    switch (prepare_packet(recv_chunk_count, packet_buffer)) {\n    case SW_READY:\n        return recv_n;\n    case SW_CONTINUE:\n        goto _read_from_pipe;\n    case SW_WAIT:\n        return SW_OK;\n    default:\n        assert(0);\n        return SW_ERR;\n    }\n}\n\n/**\n * Notice: only supports dgram type socket\n */\nssize_t MessageBus::read_with_buffer(Socket *sock) {\n    ssize_t recv_n;\n    uint16_t recv_chunk_count = 0;\n\n_read_from_pipe:\n    recv_n = sock->read(buffer_, buffer_size_);\n    if (recv_n < 0) {\n        if (sock->catch_read_error(errno) == SW_WAIT) {\n            return SW_OK;\n        }\n        return SW_ERR;\n    } else if (recv_n == 0) {\n        swoole_warning(\"receive data from socket#%d returns 0\", sock->get_fd());\n        return SW_ERR;\n    }\n\n    recv_chunk_count++;\n\n    if (!buffer_->is_chunked()) {\n        return recv_n;\n    }\n\n    String *packet_buffer = get_packet_buffer();\n    if (packet_buffer == nullptr) {\n        swoole_error_log(SW_LOG_WARNING,\n                         SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA,\n                         \"abnormal pipeline data, msg_id=%\" PRIu64 \", pipe_fd=%d, reactor_id=%d\",\n                         buffer_->info.msg_id,\n                         sock->get_fd(),\n                         buffer_->info.reactor_id);\n        return SW_ERR;\n    }\n    packet_buffer->append(buffer_->data, recv_n - sizeof(buffer_->info));\n\n    switch (prepare_packet(recv_chunk_count, packet_buffer)) {\n    case SW_READY:\n        return recv_n;\n    case SW_CONTINUE:\n        goto _read_from_pipe;\n    case SW_WAIT:\n        return SW_OK;\n    default:\n        assert(0);\n        return SW_ERR;\n    }\n}\n\nbool MessageBus::write(Socket *sock, SendData *resp) const {\n    const char *payload = resp->data;\n    uint32_t l_payload = resp->info.len;\n    off_t offset = 0;\n    uint32_t copy_n;\n\n    struct iovec iov[2];\n\n    uint64_t msg_id = id_generator_();\n    uint32_t max_length = buffer_size_ - sizeof(resp->info);\n    resp->info.msg_id = msg_id;\n\n    auto send_fn = [](Socket *sock, const iovec *iov, size_t iovcnt) {\n        if (swoole_event_is_available()) {\n            return swoole_event_writev(sock, iov, iovcnt);\n        } else {\n            return sock->writev_sync(iov, iovcnt);\n        }\n    };\n\n    if (l_payload == 0 || payload == nullptr) {\n        resp->info.flags = 0;\n        resp->info.len = 0;\n        iov[0].iov_base = &resp->info;\n        iov[0].iov_len = sizeof(resp->info);\n        return send_fn(sock, iov, 1) == (ssize_t) iov[0].iov_len;\n    }\n\n    if (!always_chunked_transfer_ && l_payload <= max_length) {\n        resp->info.flags = 0;\n        resp->info.len = l_payload;\n        iov[0].iov_base = &resp->info;\n        iov[0].iov_len = sizeof(resp->info);\n        iov[1].iov_base = (void *) payload;\n        iov[1].iov_len = l_payload;\n\n        if (send_fn(sock, iov, 2) == (ssize_t) (sizeof(resp->info) + l_payload)) {\n            return true;\n        }\n        if (sock->catch_write_pipe_error(errno) == SW_REDUCE_SIZE && max_length > SW_IPC_MSG_MIN) {\n            max_length = SW_IPC_MSG_MIN;\n        } else {\n            return false;\n        }\n    }\n\n    resp->info.flags = SW_EVENT_DATA_CHUNK | SW_EVENT_DATA_BEGIN;\n    resp->info.len = l_payload;\n\n    while (l_payload > 0) {\n        if (l_payload > max_length) {\n            copy_n = max_length;\n        } else {\n            resp->info.flags |= SW_EVENT_DATA_END;\n            copy_n = l_payload;\n        }\n\n        iov[0].iov_base = &resp->info;\n        iov[0].iov_len = sizeof(resp->info);\n        iov[1].iov_base = (void *) (payload + offset);\n        iov[1].iov_len = copy_n;\n\n        swoole_trace(\"finish, type=%d|len=%u\", resp->info.type, copy_n);\n\n        if (send_fn(sock, iov, 2) < 0) {\n            if (sock->catch_write_pipe_error(errno) == SW_REDUCE_SIZE && max_length > SW_IPC_MSG_MIN) {\n                max_length = SW_IPC_MSG_MIN;\n                if (resp->info.flags & SW_EVENT_DATA_END) {\n                    resp->info.flags &= ~SW_EVENT_DATA_END;\n                }\n                continue;\n            }\n            return false;\n        }\n\n        if (resp->info.flags & SW_EVENT_DATA_BEGIN) {\n            resp->info.flags &= ~SW_EVENT_DATA_BEGIN;\n        }\n\n        l_payload -= copy_n;\n        offset += copy_n;\n    }\n\n    return true;\n}\n\nsize_t MessageBus::get_memory_size() const {\n    size_t size = buffer_size_;\n    for (auto &p : packet_pool_) {\n        size += p.second->size;\n    }\n    return size;\n}\n\nvoid MessageBus::init_pipe_socket(const Socket *sock) {\n    int pipe_fd = sock->get_fd();\n    if ((size_t) pipe_fd >= pipe_sockets_.size()) {\n        pipe_sockets_.resize(pipe_fd + 1);\n    }\n    auto _socket = make_socket(pipe_fd, SW_FD_PIPE);\n    _socket->buffer_size = UINT_MAX;\n    if (!_socket->nonblock) {\n        _socket->set_nonblock();\n    }\n    pipe_sockets_[pipe_fd] = _socket;\n}\n\nMessageBus::~MessageBus() {\n    for (auto _socket : pipe_sockets_) {\n        if (_socket) {\n            _socket->fd = -1;\n            _socket->free();\n        }\n    }\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "src/protocol/mime_type.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2015 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_mime_type.h\"\n\n#include <algorithm>\n\nnamespace swoole {\nnamespace mime_type {\n\nstatic std::unordered_map<std::string, std::string> map_(\n    {{\"ez\", \"application/andrew-inset\"},\n     {\"aw\", \"application/applixware\"},\n     {\"atom\", \"application/atom+xml\"},\n     {\"atomcat\", \"application/atomcat+xml\"},\n     {\"atomsvc\", \"application/atomsvc+xml\"},\n     {\"bdoc\", \"application/bdoc\"},\n     {\"ccxml\", \"application/ccxml+xml\"},\n     {\"cdmia\", \"application/cdmi-capability\"},\n     {\"cdmic\", \"application/cdmi-container\"},\n     {\"cdmid\", \"application/cdmi-domain\"},\n     {\"cdmio\", \"application/cdmi-object\"},\n     {\"cdmiq\", \"application/cdmi-queue\"},\n     {\"cu\", \"application/cu-seeme\"},\n     {\"mpd\", \"application/dash+xml\"},\n     {\"davmount\", \"application/davmount+xml\"},\n     {\"dbk\", \"application/docbook+xml\"},\n     {\"dssc\", \"application/dssc+der\"},\n     {\"xdssc\", \"application/dssc+xml\"},\n     {\"ecma\", \"application/ecmascript\"},\n     {\"emma\", \"application/emma+xml\"},\n     {\"epub\", \"application/epub+zip\"},\n     {\"exi\", \"application/exi\"},\n     {\"pfr\", \"application/font-tdpfr\"},\n     {\"woff\", \"application/font-woff\"},\n     {\"geojson\", \"application/geo+json\"},\n     {\"gml\", \"application/gml+xml\"},\n     {\"gpx\", \"application/gpx+xml\"},\n     {\"gxf\", \"application/gxf\"},\n     {\"gz\", \"application/gzip\"},\n     {\"hjson\", \"application/hjson\"},\n     {\"stk\", \"application/hyperstudio\"},\n     {\"ink\", \"application/inkml+xml\"},\n     {\"inkml\", \"application/inkml+xml\"},\n     {\"ipfix\", \"application/ipfix\"},\n     {\"jar\", \"application/java-archive\"},\n     {\"war\", \"application/java-archive\"},\n     {\"ear\", \"application/java-archive\"},\n     {\"apk\", \"application/vnd.android.package-archive\"},\n     {\"ser\", \"application/java-serialized-object\"},\n     {\"class\", \"application/java-vm\"},\n     {\"js\", \"application/javascript\"},\n     {\"mjs\", \"application/javascript\"},\n     {\"json\", \"application/json\"},\n     {\"map\", \"application/json\"},\n     {\"json5\", \"application/json5\"},\n     {\"jsonml\", \"application/jsonml+json\"},\n     {\"jsonld\", \"application/ld+json\"},\n     {\"lostxml\", \"application/lost+xml\"},\n     {\"hqx\", \"application/mac-binhex40\"},\n     {\"cpt\", \"application/mac-compactpro\"},\n     {\"mads\", \"application/mads+xml\"},\n     {\"webmanifest\", \"application/manifest+json\"},\n     {\"mrc\", \"application/marc\"},\n     {\"mrcx\", \"application/marcxml+xml\"},\n     {\"ma\", \"application/mathematica\"},\n     {\"nb\", \"application/mathematica\"},\n     {\"mb\", \"application/mathematica\"},\n     {\"mathml\", \"application/mathml+xml\"},\n     {\"mbox\", \"application/mbox\"},\n     {\"mscml\", \"application/mediaservercontrol+xml\"},\n     {\"metalink\", \"application/metalink+xml\"},\n     {\"meta4\", \"application/metalink4+xml\"},\n     {\"mets\", \"application/mets+xml\"},\n     {\"mods\", \"application/mods+xml\"},\n     {\"m21\", \"application/mp21\"},\n     {\"mp21\", \"application/mp21\"},\n     {\"mp4s\", \"application/mp4\"},\n     {\"m4p\", \"application/mp4\"},\n     {\"doc\", \"application/msword\"},\n     {\"docx\", \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\"},\n     {\"dot\", \"application/msword\"},\n     {\"mxf\", \"application/mxf\"},\n     {\"bin\", \"application/octet-stream\"},\n     {\"dms\", \"application/octet-stream\"},\n     {\"lrf\", \"application/octet-stream\"},\n     {\"mar\", \"application/octet-stream\"},\n     {\"so\", \"application/octet-stream\"},\n     {\"dist\", \"application/octet-stream\"},\n     {\"distz\", \"application/octet-stream\"},\n     {\"pkg\", \"application/octet-stream\"},\n     {\"bpk\", \"application/octet-stream\"},\n     {\"dump\", \"application/octet-stream\"},\n     {\"elc\", \"application/octet-stream\"},\n     {\"deploy\", \"application/octet-stream\"},\n     {\"exe\", \"application/octet-stream\"},\n     {\"dll\", \"application/octet-stream\"},\n     {\"deb\", \"application/octet-stream\"},\n     {\"dmg\", \"application/octet-stream\"},\n     {\"iso\", \"application/octet-stream\"},\n     {\"img\", \"application/octet-stream\"},\n     {\"msi\", \"application/octet-stream\"},\n     {\"msp\", \"application/octet-stream\"},\n     {\"msm\", \"application/octet-stream\"},\n     {\"buffer\", \"application/octet-stream\"},\n     {\"oda\", \"application/oda\"},\n     {\"opf\", \"application/oebps-package+xml\"},\n     {\"ogx\", \"application/ogg\"},\n     {\"omdoc\", \"application/omdoc+xml\"},\n     {\"onetoc\", \"application/onenote\"},\n     {\"onetoc2\", \"application/onenote\"},\n     {\"onetmp\", \"application/onenote\"},\n     {\"onepkg\", \"application/onenote\"},\n     {\"oxps\", \"application/oxps\"},\n     {\"xer\", \"application/patch-ops-error+xml\"},\n     {\"pdf\", \"application/pdf\"},\n     {\"pgp\", \"application/pgp-encrypted\"},\n     {\"asc\", \"application/pgp-signature\"},\n     {\"sig\", \"application/pgp-signature\"},\n     {\"prf\", \"application/pics-rules\"},\n     {\"p10\", \"application/pkcs10\"},\n     {\"p7m\", \"application/pkcs7-mime\"},\n     {\"p7c\", \"application/pkcs7-mime\"},\n     {\"p7s\", \"application/pkcs7-signature\"},\n     {\"p8\", \"application/pkcs8\"},\n     {\"ac\", \"application/pkix-attr-cert\"},\n     {\"cer\", \"application/pkix-cert\"},\n     {\"crl\", \"application/pkix-crl\"},\n     {\"pkipath\", \"application/pkix-pkipath\"},\n     {\"pki\", \"application/pkixcmp\"},\n     {\"pls\", \"application/pls+xml\"},\n     {\"ai\", \"application/postscript\"},\n     {\"eps\", \"application/postscript\"},\n     {\"ps\", \"application/postscript\"},\n     {\"pskcxml\", \"application/pskc+xml\"},\n     {\"raml\", \"application/raml+yaml\"},\n     {\"rdf\", \"application/rdf+xml\"},\n     {\"rif\", \"application/reginfo+xml\"},\n     {\"rnc\", \"application/relax-ng-compact-syntax\"},\n     {\"rl\", \"application/resource-lists+xml\"},\n     {\"rld\", \"application/resource-lists-diff+xml\"},\n     {\"rs\", \"application/rls-services+xml\"},\n     {\"gbr\", \"application/rpki-ghostbusters\"},\n     {\"mft\", \"application/rpki-manifest\"},\n     {\"roa\", \"application/rpki-roa\"},\n     {\"rsd\", \"application/rsd+xml\"},\n     {\"rss\", \"application/rss+xml\"},\n     {\"rtf\", \"application/rtf\"},\n     {\"sbml\", \"application/sbml+xml\"},\n     {\"scq\", \"application/scvp-cv-request\"},\n     {\"scs\", \"application/scvp-cv-response\"},\n     {\"spq\", \"application/scvp-vp-request\"},\n     {\"spp\", \"application/scvp-vp-response\"},\n     {\"sdp\", \"application/sdp\"},\n     {\"setpay\", \"application/set-payment-initiation\"},\n     {\"setreg\", \"application/set-registration-initiation\"},\n     {\"shf\", \"application/shf+xml\"},\n     {\"smi\", \"application/smil+xml\"},\n     {\"smil\", \"application/smil+xml\"},\n     {\"rq\", \"application/sparql-query\"},\n     {\"srx\", \"application/sparql-results+xml\"},\n     {\"gram\", \"application/srgs\"},\n     {\"grxml\", \"application/srgs+xml\"},\n     {\"sru\", \"application/sru+xml\"},\n     {\"ssdl\", \"application/ssdl+xml\"},\n     {\"ssml\", \"application/ssml+xml\"},\n     {\"tei\", \"application/tei+xml\"},\n     {\"teicorpus\", \"application/tei+xml\"},\n     {\"tfi\", \"application/thraud+xml\"},\n     {\"tsd\", \"application/timestamped-data\"},\n     {\"vxml\", \"application/voicexml+xml\"},\n     {\"wasm\", \"application/wasm\"},\n     {\"wgt\", \"application/widget\"},\n     {\"hlp\", \"application/winhlp\"},\n     {\"wsdl\", \"application/wsdl+xml\"},\n     {\"wspolicy\", \"application/wspolicy+xml\"},\n     {\"xaml\", \"application/xaml+xml\"},\n     {\"xdf\", \"application/xcap-diff+xml\"},\n     {\"xenc\", \"application/xenc+xml\"},\n     {\"xhtml\", \"application/xhtml+xml\"},\n     {\"xht\", \"application/xhtml+xml\"},\n     {\"xml\", \"application/xml\"},\n     {\"xsl\", \"application/xml\"},\n     {\"xsd\", \"application/xml\"},\n     {\"rng\", \"application/xml\"},\n     {\"dtd\", \"application/xml-dtd\"},\n     {\"xop\", \"application/xop+xml\"},\n     {\"xpl\", \"application/xproc+xml\"},\n     {\"xslt\", \"application/xslt+xml\"},\n     {\"xspf\", \"application/xspf+xml\"},\n     {\"mxml\", \"application/xv+xml\"},\n     {\"xhvml\", \"application/xv+xml\"},\n     {\"xvml\", \"application/xv+xml\"},\n     {\"xvm\", \"application/xv+xml\"},\n     {\"yang\", \"application/yang\"},\n     {\"yin\", \"application/yin+xml\"},\n     {\"zip\", \"application/zip\"},\n     {\"*3gpp\", \"audio/3gpp\"},\n     {\"adp\", \"audio/adpcm\"},\n     {\"au\", \"audio/basic\"},\n     {\"snd\", \"audio/basic\"},\n     {\"mid\", \"audio/midi\"},\n     {\"midi\", \"audio/midi\"},\n     {\"kar\", \"audio/midi\"},\n     {\"rmi\", \"audio/midi\"},\n     {\"*mp3\", \"audio/mp3\"},\n     {\"m4a\", \"audio/mp4\"},\n     {\"mp4a\", \"audio/mp4\"},\n     {\"mpga\", \"audio/mpeg\"},\n     {\"mp2\", \"audio/mpeg\"},\n     {\"mp2a\", \"audio/mpeg\"},\n     {\"mp3\", \"audio/mpeg\"},\n     {\"m2a\", \"audio/mpeg\"},\n     {\"m3a\", \"audio/mpeg\"},\n     {\"oga\", \"audio/ogg\"},\n     {\"ogg\", \"audio/ogg\"},\n     {\"spx\", \"audio/ogg\"},\n     {\"s3m\", \"audio/s3m\"},\n     {\"sil\", \"audio/silk\"},\n     {\"wav\", \"audio/wav\"},\n     {\"*wav\", \"audio/wave\"},\n     {\"weba\", \"audio/webm\"},\n     {\"xm\", \"audio/xm\"},\n     {\"ttc\", \"font/collection\"},\n     {\"otf\", \"font/otf\"},\n     {\"ttf\", \"font/ttf\"},\n     {\"*woff\", \"font/woff\"},\n     {\"woff2\", \"font/woff2\"},\n     {\"apng\", \"image/apng\"},\n     {\"bmp\", \"image/bmp\"},\n     {\"cgm\", \"image/cgm\"},\n     {\"g3\", \"image/g3fax\"},\n     {\"gif\", \"image/gif\"},\n     {\"ief\", \"image/ief\"},\n     {\"jp2\", \"image/jp2\"},\n     {\"jpg2\", \"image/jp2\"},\n     {\"jpeg\", \"image/jpeg\"},\n     {\"jpg\", \"image/jpeg\"},\n     {\"jpe\", \"image/jpeg\"},\n     {\"jpm\", \"image/jpm\"},\n     {\"jpx\", \"image/jpx\"},\n     {\"jpf\", \"image/jpx\"},\n     {\"ktx\", \"image/ktx\"},\n     {\"png\", \"image/png\"},\n     {\"sgi\", \"image/sgi\"},\n     {\"svg\", \"image/svg+xml\"},\n     {\"svgz\", \"image/svg+xml\"},\n     {\"tiff\", \"image/tiff\"},\n     {\"tif\", \"image/tiff\"},\n     {\"webp\", \"image/webp\"},\n     {\"disposition-notification\", \"message/disposition-notification\"},\n     {\"u8msg\", \"message/global\"},\n     {\"u8dsn\", \"message/global-delivery-status\"},\n     {\"u8mdn\", \"message/global-disposition-notification\"},\n     {\"u8hdr\", \"message/global-headers\"},\n     {\"eml\", \"message/rfc822\"},\n     {\"mime\", \"message/rfc822\"},\n     {\"gltf\", \"model/gltf+json\"},\n     {\"glb\", \"model/gltf-binary\"},\n     {\"igs\", \"model/iges\"},\n     {\"iges\", \"model/iges\"},\n     {\"msh\", \"model/mesh\"},\n     {\"mesh\", \"model/mesh\"},\n     {\"silo\", \"model/mesh\"},\n     {\"wrl\", \"model/vrml\"},\n     {\"vrml\", \"model/vrml\"},\n     {\"x3db\", \"model/x3d+binary\"},\n     {\"x3dbz\", \"model/x3d+binary\"},\n     {\"x3dv\", \"model/x3d+vrml\"},\n     {\"x3dvz\", \"model/x3d+vrml\"},\n     {\"x3d\", \"model/x3d+xml\"},\n     {\"x3dz\", \"model/x3d+xml\"},\n     {\"appcache\", \"text/cache-manifest\"},\n     {\"manifest\", \"text/cache-manifest\"},\n     {\"ics\", \"text/calendar\"},\n     {\"ifb\", \"text/calendar\"},\n     {\"coffee\", \"text/coffeescript\"},\n     {\"litcoffee\", \"text/coffeescript\"},\n     {\"css\", \"text/css\"},\n     {\"csv\", \"text/csv\"},\n     {\"html\", \"text/html\"},\n     {\"htm\", \"text/html\"},\n     {\"shtml\", \"text/html\"},\n     {\"jade\", \"text/jade\"},\n     {\"jsx\", \"text/jsx\"},\n     {\"less\", \"text/less\"},\n     {\"markdown\", \"text/markdown\"},\n     {\"md\", \"text/markdown\"},\n     {\"mml\", \"text/mathml\"},\n     {\"n3\", \"text/n3\"},\n     {\"txt\", \"text/plain\"},\n     {\"text\", \"text/plain\"},\n     {\"conf\", \"text/plain\"},\n     {\"def\", \"text/plain\"},\n     {\"list\", \"text/plain\"},\n     {\"log\", \"text/plain\"},\n     {\"in\", \"text/plain\"},\n     {\"ini\", \"text/plain\"},\n     {\"rtx\", \"text/richtext\"},\n     {\"*rtf\", \"text/rtf\"},\n     {\"sgml\", \"text/sgml\"},\n     {\"sgm\", \"text/sgml\"},\n     {\"shex\", \"text/shex\"},\n     {\"slim\", \"text/slim\"},\n     {\"slm\", \"text/slim\"},\n     {\"stylus\", \"text/stylus\"},\n     {\"styl\", \"text/stylus\"},\n     {\"tsv\", \"text/tab-separated-values\"},\n     {\"t\", \"text/troff\"},\n     {\"tr\", \"text/troff\"},\n     {\"roff\", \"text/troff\"},\n     {\"man\", \"text/troff\"},\n     {\"me\", \"text/troff\"},\n     {\"ms\", \"text/troff\"},\n     {\"ttl\", \"text/turtle\"},\n     {\"uri\", \"text/uri-list\"},\n     {\"uris\", \"text/uri-list\"},\n     {\"urls\", \"text/uri-list\"},\n     {\"vcard\", \"text/vcard\"},\n     {\"vtt\", \"text/vtt\"},\n     {\"*xml\", \"text/xml\"},\n     {\"yaml\", \"text/yaml\"},\n     {\"yml\", \"text/yaml\"},\n     {\"3gp\", \"video/3gpp\"},\n     {\"3gpp\", \"video/3gpp\"},\n     {\"3g2\", \"video/3gpp2\"},\n     {\"h261\", \"video/h261\"},\n     {\"h263\", \"video/h263\"},\n     {\"h264\", \"video/h264\"},\n     {\"jpgv\", \"video/jpeg\"},\n     {\"*jpm\", \"video/jpm\"},\n     {\"jpgm\", \"video/jpm\"},\n     {\"mj2\", \"video/mj2\"},\n     {\"mjp2\", \"video/mj2\"},\n     {\"ts\", \"video/mp2t\"},\n     {\"mp4\", \"video/mp4\"},\n     {\"mp4v\", \"video/mp4\"},\n     {\"mpg4\", \"video/mp4\"},\n     {\"mpeg\", \"video/mpeg\"},\n     {\"mpg\", \"video/mpeg\"},\n     {\"mpe\", \"video/mpeg\"},\n     {\"m1v\", \"video/mpeg\"},\n     {\"m2v\", \"video/mpeg\"},\n     {\"ogv\", \"video/ogg\"},\n     {\"qt\", \"video/quicktime\"},\n     {\"mov\", \"video/quicktime\"},\n     {\"webm\", \"video/webm\"},\n     // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types\n     {\"aac\", \"audio/aac\"},\n     {\"abw\", \"application/x-abiword\"},\n     {\"arc\", \"application/octet-stream\"},\n     {\"avi\", \"video/x-msvideo\"},\n     {\"azw\", \"application/vnd.amazon.ebook\"},\n     {\"bz\", \"application/x-bzip\"},\n     {\"bz2\", \"application/x-bzip2\"},\n     {\"csh\", \"application/x-csh\"},\n     {\"eot\", \"application/vnd.ms-fontobject\"},\n     {\"es\", \"application/ecmascript\"},\n     {\"ico\", \"image/x-icon\"},\n     {\"mpkg\", \"application/vnd.apple.installer+xml\"},\n     {\"odp\", \"application/vnd.oasis.opendocument.presentation\"},\n     {\"ods\", \"application/vnd.oasis.opendocument.spreadsheet\"},\n     {\"odt\", \"application/vnd.oasis.opendocument.text\"},\n     {\"ppt\", \"application/vnd.ms-powerpoint\"},\n     {\"pptx\", \"application/vnd.openxmlformats-officedocument.presentationml.presentation\"},\n     {\"rar\", \"application/x-rar-compressed\"},\n     {\"sh\", \"application/x-sh\"},\n     {\"swf\", \"application/x-shockwave-flash\"},\n     {\"tar\", \"application/x-tar\"},\n     {\"vsd\", \"application/vnd.visio\"},\n     {\"xls\", \"application/vnd.ms-excel\"},\n     {\"xlsx\", \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\"},\n     {\"xul\", \"application/vnd.mozilla.xul+xml\"},\n     {\"7z\", \"application/x-7z-compressed\"}});\n\nstatic const std::string octet_stream(\"application/octet-stream\");\n\nstatic std::string get_suffix(const std::string &filename) {\n    std::string suffix = std::string(filename).substr(filename.find_last_of('.') + 1);\n    std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::tolower);\n    return suffix;\n}\n\nconst std::unordered_map<std::string, std::string> &list() {\n    return map_;\n}\n\nconst std::string &get(const std::string &filename) {\n    std::string suffix = get_suffix(filename);\n    auto i = map_.find(suffix);\n    if (i != map_.end()) {\n        return i->second;\n    }\n    return octet_stream;\n}\n\nbool add(const std::string &suffix, const std::string &mime_type) {\n    if (map_.find(suffix) == map_.end()) {\n        map_[suffix] = mime_type;\n        return true;\n    } else {\n        return false;\n    }\n}\n\nvoid set(const std::string &suffix, const std::string &mime_type) {\n    map_[suffix] = mime_type;\n}\n\nbool del(const std::string &suffix) {\n    if (map_.find(suffix) == map_.end()) {\n        return false;\n    } else {\n        map_.erase(suffix);\n        return true;\n    }\n}\n\nbool exists(const std::string &filename) {\n    std::string suffix = get_suffix(filename);\n    return map_.find(suffix) != map_.end();\n}\n\n}  // namespace mime_type\n}  // namespace swoole\n"
  },
  {
    "path": "src/protocol/mqtt.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2015 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n | Author: Xinhua Guo  <guoxinhua@swoole.com>                           |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_mqtt.h\"\n#include \"swoole_protocol.h\"\n\nusing swoole::network::Socket;\n\nnamespace swoole {\nnamespace mqtt {\n\nvoid print_package(Packet *pkg) {\n    printf(\"type=%d, length=%d\\n\", pkg->type, pkg->length);\n}\n\nvoid set_protocol(Protocol *protocol) {\n    protocol->package_length_size = SW_MQTT_MAX_LENGTH_SIZE;\n    protocol->package_length_offset = 1;\n    protocol->package_body_offset = 0;\n    protocol->get_package_length = get_package_length;\n}\n\n// recv variable_header packet twice may cause that the '*data' contain the payload data,\n// but there's no chance to read the next mqtt request ,because MQTT client will recv ACK blocking\n#define SW_MQTT_RECV_LEN_AGAIN 0\n\nssize_t get_package_length(const Protocol *protocol, Socket *conn, PacketLength *pl) {\n    //-1 cause the arg 'size' contain length_offset(1 byte len)\n    uint32_t recv_variable_header_size = (pl->buf_size - 1);\n    if (recv_variable_header_size < SW_MQTT_MIN_LENGTH_SIZE) {  // recv continue\n        return SW_MQTT_RECV_LEN_AGAIN;\n    }\n\n    uint8_t byte;\n    int mul = 1;\n    ssize_t length = 0;\n    ssize_t variable_header_byte_count = 0;\n    while (true) {\n        variable_header_byte_count++;\n        byte = pl->buf[variable_header_byte_count];\n        length += (byte & 127) * mul;\n        mul *= 128;\n        if ((byte & 128) == 0) {  // done! there is no surplus length byte\n            break;\n        }\n        if (variable_header_byte_count >= SW_MQTT_MAX_LENGTH_SIZE) {\n            swoole_error_log(SW_LOG_WARNING,\n                             SW_ERROR_PACKAGE_LENGTH_TOO_LARGE,\n                             \"bad request, the variable header size is larger than %d\",\n                             SW_MQTT_MAX_LENGTH_SIZE);\n            return SW_ERR;\n        }\n        if (variable_header_byte_count >= recv_variable_header_size) {  // length not enough\n            return SW_MQTT_RECV_LEN_AGAIN;\n        }\n    }\n    // payload_length + variable_header_byte_count + length_offset(1)\n    return length + variable_header_byte_count + 1;\n}\n\n}  // namespace mqtt\n}  // namespace swoole\n"
  },
  {
    "path": "src/protocol/redis.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2015 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_string.h\"\n#include \"swoole_socket.h\"\n#include \"swoole_protocol.h\"\n#include \"swoole_server.h\"\n#include \"swoole_redis.h\"\n\nnamespace swoole {\nnamespace redis {\n\nstruct Request {\n    uint8_t state;\n\n    int n_lines_total;\n    int n_lines_received;\n\n    int n_bytes_total;\n    int n_bytes_received;\n\n    int offset;\n};\n\nconst char *get_number(const char *p, int *_ret) {\n    char *endptr;\n    p++;\n    int ret = strtol(p, &endptr, 10);\n    if (strncmp(SW_CRLF, endptr, SW_CRLF_LEN) == 0) {\n        p += (endptr - p) + SW_CRLF_LEN;\n        *_ret = ret;\n        return p;\n    } else {\n        return nullptr;\n    }\n}\n\nint recv_packet(Protocol *protocol, Connection *conn, String *buffer) {\n    const char *p, *pe;\n    int ret;\n    char *buf_ptr;\n    size_t buf_size;\n    RecvData rdata{};\n    Request *request;\n    network::Socket *socket = conn->socket;\n\n    if (conn->object == nullptr) {\n        request = static_cast<Request *>(sw_malloc(sizeof(Request)));\n        if (!request) {\n            swoole_warning(\"malloc(%ld) failed\", sizeof(Request));\n            return SW_ERR;\n        }\n        sw_memset_zero(request, sizeof(Request));\n        conn->object = request;\n    } else {\n        request = static_cast<Request *>(conn->object);\n    }\n\n_recv_data:\n    buf_ptr = buffer->str + buffer->length;\n    buf_size = buffer->size - buffer->length;\n\n    int n = socket->recv(buf_ptr, buf_size, 0);\n    if (n < 0) {\n        switch (socket->catch_read_error(errno)) {\n        case SW_ERROR:\n            swoole_sys_warning(\"recv from socket#%d failed\", conn->fd);\n            return SW_OK;\n        case SW_CLOSE:\n            return SW_ERR;\n        default:\n            return SW_OK;\n        }\n    } else if (n == 0) {\n        return SW_ERR;\n    } else {\n        buffer->length += n;\n\n        if (strncmp(buffer->str + buffer->length - SW_CRLF_LEN, SW_CRLF, SW_CRLF_LEN) != 0) {\n            if (buffer->size < protocol->package_max_length) {\n                uint32_t extend_size = swoole_size_align(buffer->size * 2, swoole_pagesize());\n                if (extend_size > protocol->package_max_length) {\n                    extend_size = protocol->package_max_length;\n                }\n                buffer->extend(extend_size);\n            } else if (buffer->length == buffer->size) {\n            _package_too_big:\n                swoole_warning(\"Package is too big. package_length=%ld\", buffer->length);\n                return SW_ERR;\n            }\n            goto _recv_data;\n        }\n\n        p = buffer->str;\n        pe = p + buffer->length;\n\n        do {\n            switch (request->state) {\n            case STATE_RECEIVE_TOTAL_LINE:\n                if (*p == '*') {\n                    if ((p = get_number(p, &ret)) == nullptr) {\n                        goto _failed;\n                    }\n                    request->n_lines_total = ret;\n                    request->state = STATE_RECEIVE_LENGTH;\n                    break;\n                }\n                /* no break */\n\n            case STATE_RECEIVE_LENGTH:\n                if (*p == '$') {\n                    if ((p = get_number(p, &ret)) == nullptr) {\n                        goto _failed;\n                    }\n                    if (ret < 0) {\n                        break;\n                    }\n                    if (ret + (p - buffer->str) > protocol->package_max_length) {\n                        goto _package_too_big;\n                    }\n                    request->n_bytes_total = ret;\n                    request->state = STATE_RECEIVE_STRING;\n                    break;\n                }\n                // integer\n                else if (*p == ':') {\n                    if ((p = get_number(p, &ret)) == nullptr) {\n                        goto _failed;\n                    }\n                    break;\n                }\n                /* no break */\n\n            case STATE_RECEIVE_STRING:\n                if (pe - p < request->n_bytes_total - request->n_bytes_received) {\n                    request->n_bytes_received += pe - p;\n                    return SW_OK;\n                } else {\n                    p += request->n_bytes_total + SW_CRLF_LEN;\n                    request->n_bytes_total = 0;\n                    request->n_lines_received++;\n                    request->state = STATE_RECEIVE_LENGTH;\n                    buffer->offset = buffer->length;\n\n                    if (request->n_lines_received == request->n_lines_total) {\n                        rdata.info.len = buffer->length;\n                        rdata.data = buffer->str;\n                        if (protocol->onPackage(protocol, socket, &rdata) < 0) {\n                            return SW_ERR;\n                        }\n                        if (socket->removed) {\n                            return SW_OK;\n                        }\n                        buffer->clear();\n                        sw_memset_zero(request, sizeof(Request));\n                        return SW_OK;\n                    }\n                }\n                break;\n\n            default:\n                goto _failed;\n            }\n        } while (p < pe);\n    }\n_failed:\n    swoole_warning(\"redis protocol error\");\n    return SW_ERR;\n}\n\nvoid format_nil(String *buf) {\n    buf->append(SW_STRL(SW_REDIS_RETURN_NIL));\n}\n\nvoid format(String *buf, ReplyType type, const std::string &value) {\n    if (type == REPLY_STATUS) {\n        if (value.empty()) {\n            buf->append(SW_STRL(\"+OK\\r\\n\"));\n        } else {\n            buf->format(\"+%.*s\\r\\n\", value.length(), value.c_str());\n        }\n    } else if (type == REPLY_ERROR) {\n        if (value.empty()) {\n            buf->append(SW_STRL(\"-ERR\\r\\n\"));\n        } else {\n            buf->format(\"-%.*s\\r\\n\", value.length(), value.c_str());\n        }\n    } else if (type == REPLY_STRING) {\n        if (!value.empty()) {\n            buf->format(\"$%zu\\r\\n\", value.length());\n            buf->append(value);\n            buf->append(SW_CRLF, SW_CRLF_LEN);\n        }\n    }\n}\n\nvoid format(String *buf, ReplyType type, long value) {\n    buf->format(\":%\" PRId64 \"\\r\\n\", value);\n}\n\nstd::vector<std::string> parse(const char *data, size_t len) {\n    int state = STATE_RECEIVE_TOTAL_LINE;\n\n    const char *p = data;\n    const char *pe = p + len;\n    int ret;\n    int length = 0;\n\n    std::vector<std::string> result;\n    do {\n        switch (state) {\n        case STATE_RECEIVE_TOTAL_LINE:\n            if (*p == '*' && ((p = get_number(p, &ret)))) {\n                state = STATE_RECEIVE_LENGTH;\n                break;\n            }\n            /* no break */\n\n        case STATE_RECEIVE_LENGTH:\n            if (*p == '$' && ((p = get_number(p, &ret)))) {\n                if (ret == -1) {\n                    break;\n                }\n                length = ret;\n                state = STATE_RECEIVE_STRING;\n                break;\n            }\n            // integer\n            else if (*p == ':' && ((p = get_number(p, &ret)))) {\n                result.push_back(std::to_string(ret));\n                break;\n            }\n            /* no break */\n\n        case STATE_RECEIVE_STRING:\n            result.emplace_back(p, length);\n            p += length + SW_CRLF_LEN;\n            state = STATE_RECEIVE_LENGTH;\n            break;\n\n        default:\n            break;\n        }\n    } while (p < pe);\n\n    return result;\n}\n}  // namespace redis\n}  // namespace swoole\n"
  },
  {
    "path": "src/protocol/socks5.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2017 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_proxy.h\"\n#include \"swoole_socket.h\"\n\nnamespace swoole {\nconst char *Socks5Proxy::strerror(int code) {\n    switch (code) {\n    case 0x01:\n        return \"General failure\";\n    case 0x02:\n        return \"Connection not allowed by ruleset\";\n    case 0x03:\n        return \"Network unreachable\";\n    case 0x04:\n        return \"Host unreachable\";\n    case 0x05:\n        return \"Connection refused by destination host\";\n    case 0x06:\n        return \"TTL expired\";\n    case 0x07:\n        return \"command not supported / protocol error\";\n    case 0x08:\n        return \"address type not supported\";\n    default:\n        return \"Unknown error\";\n    }\n}\n\nSocks5Proxy *Socks5Proxy::create(\n    int socket_type, const std::string &host, int port, const std::string &user, const std::string &pwd) {\n    if (user.length() > 250 || pwd.length() > 250) {\n        swoole_error_log(SW_LOG_NOTICE,\n                         SW_ERROR_SOCKS5_AUTH_FAILED,\n                         \"SOCKS5 username or password is too long, max length is 250 bytes\");\n        return nullptr;\n    }\n    auto socks5_proxy = new Socks5Proxy();\n    socks5_proxy->host = host;\n    socks5_proxy->port = port;\n    socks5_proxy->dns_tunnel = 1;\n    socks5_proxy->socket_type = socket_type;\n    if (!user.empty() && !pwd.empty()) {\n        socks5_proxy->username = user;\n        socks5_proxy->password = pwd;\n    }\n    return socks5_proxy;\n}\n\nssize_t Socks5Proxy::pack_negotiate_request() {\n    char *p = buf;\n    p[0] = SW_SOCKS5_VERSION_CODE;  // Version\n    p[1] = 0x01;\n    method = username.empty() ? SW_SOCKS5_METHOD_NO_AUTH : SW_SOCKS5_METHOD_AUTH;\n    p[2] = method;\n    return 3;\n}\n\nssize_t Socks5Proxy::pack_auth_request() {\n    char *p = buf;\n    // username\n    p[0] = 0x01;\n    p[1] = username.length();\n    p += 2;\n    if (!username.empty()) {\n        memcpy(p, username.c_str(), username.length());\n        p += username.length();\n    }\n    // password\n    p[0] = password.length();\n    p += 1;\n    if (!password.empty()) {\n        memcpy(p, password.c_str(), password.length());\n        p += password.length();\n    }\n    return p - buf;\n}\n\nbool Socks5Proxy::handshake(const char *rbuf,\n                            size_t rlen,\n                            const std::function<ssize_t(const char *buf, size_t len)> &send_fn) {\n    if (rlen < 2) {\n        swoole_error_log(\n            SW_LOG_NOTICE, SW_ERROR_SOCKS5_HANDSHAKE_FAILED, \"SOCKS5 handshake failed, data length is too short\");\n        return false;\n    }\n\n    const uchar resp_version = rbuf[0];\n    const uchar resp_result = rbuf[1];\n\n    if (state == SW_SOCKS5_STATE_HANDSHAKE) {\n        if (resp_version != SW_SOCKS5_VERSION_CODE) {\n            swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_VERSION, \"SOCKS version is not supported\");\n            return false;\n        }\n        if (method != resp_result) {\n            swoole_error_log(\n                SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_METHOD, \"SOCKS authentication method is not supported\");\n            return false;\n        }\n        // authenticate request\n        if (method == SW_SOCKS5_METHOD_AUTH) {\n            const auto len = pack_auth_request();\n            state = SW_SOCKS5_STATE_AUTH;\n            return send_fn(buf, len) == len;\n        }\n        // send connect request\n        else {\n        _send_connect_request:\n            state = SW_SOCKS5_STATE_CONNECT;\n            const auto len = pack_connect_request();\n            if (len < 0) {\n                return false;\n            }\n            return send_fn(buf, len) == len;\n        }\n    } else if (state == SW_SOCKS5_STATE_AUTH) {\n        if (resp_version != 0x01) {\n            swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_VERSION, \"SOCKS version is not supported\");\n            return false;\n        }\n        if (resp_result != 0) {\n            swoole_error_log(\n                SW_LOG_NOTICE, SW_ERROR_SOCKS5_AUTH_FAILED, \"SOCKS username/password authentication failed\");\n            return false;\n        }\n        goto _send_connect_request;\n    } else if (state == SW_SOCKS5_STATE_CONNECT) {\n        if (resp_version != SW_SOCKS5_VERSION_CODE) {\n            swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_VERSION, \"SOCKS version is not supported\");\n            return false;\n        }\n#if 0\n        uchar reg = recv_data[2];\n        uchar type = recv_data[3];\n        uint32_t ip = *(uint32_t *) (recv_data + 4);\n        uint16_t port = *(uint16_t *) (recv_data + 8);\n#endif\n        if (resp_result == 0) {\n            state = SW_SOCKS5_STATE_READY;\n            return true;\n        } else {\n            swoole_error_log(SW_LOG_NOTICE,\n                             SW_ERROR_SOCKS5_SERVER_ERROR,\n                             \"Socks5 server error, reason :%s\",\n                             Socks5Proxy::strerror(resp_result));\n            return false;\n        }\n    }\n    return true;\n}\n\nssize_t Socks5Proxy::pack_connect_request() {\n    char *p = buf;\n    p[0] = SW_SOCKS5_VERSION_CODE;\n    p[1] = 0x01;  // CONNECT command\n    p[2] = 0x00;  // Reserved byte\n    p += 3;\n\n    if (dns_tunnel) {\n        if (host.length() > 480) {\n            swoole_error_log(\n                SW_LOG_NOTICE, SW_ERROR_SOCKS5_AUTH_FAILED, \"SOCKS5 host is too long, max length is 480 bytes\");\n            return -1;\n        }\n        p[0] = 0x03;\n        p[1] = target_host.length();\n        p += 2;\n        memcpy(p, target_host.c_str(), target_host.length());\n        p += target_host.length();\n    } else {\n        network::Address target_addr;\n        if (!target_addr.assign(static_cast<SocketType>(socket_type), target_host, target_port, false)) {\n            swoole_error_log(\n                SW_LOG_NOTICE,\n                SW_ERROR_SOCKS5_HANDSHAKE_FAILED,\n                \"When disable SOCKS5 proxy DNS tunnel connection, the destination host must be an IP address.\");\n            return SW_ERR;\n        }\n        if (network::Socket::is_inet4(static_cast<SocketType>(socket_type))) {\n            p[0] = 0x01;  // IPv4 address type\n            p += 1;\n            memcpy(p, &target_addr.addr.inet_v4.sin_addr, sizeof(target_addr.addr.inet_v4.sin_addr));\n            p += sizeof(target_addr.addr.inet_v4.sin_addr);\n        } else if (network::Socket::is_inet6(static_cast<SocketType>(socket_type))) {\n            p[0] = 0x04;  // IPv6 address type\n            p += 1;\n            memcpy(p, &target_addr.addr.inet_v6.sin6_addr, sizeof(target_addr.addr.inet_v6.sin6_addr));\n            p += sizeof(target_addr.addr.inet_v6.sin6_addr);\n        } else {\n            swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_HANDSHAKE_FAILED, \"Unsupported socket type for SOCKS5\");\n            return SW_ERR;\n        }\n    }\n    const auto _target_port = htons(target_port);\n    memcpy(p, &_target_port, sizeof(_target_port));\n    p += 2;\n    return p - buf;\n}\n}  // namespace swoole\n"
  },
  {
    "path": "src/protocol/ssl.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_string.h\"\n#include \"swoole_socket.h\"\n#include \"swoole_ssl.h\"\n#include \"swoole_util.h\"\n\nusing swoole::SSLContext;\nusing swoole::network::Address;\nusing swoole::network::Socket;\n\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\n#error \"OpenSSL 1.1.0 or later is required\"\n#endif\n\nstatic bool openssl_init = false;\nstatic int ssl_connection_index = 0;\nstatic int ssl_port_index = 0;\n\nstatic int swoole_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\nstatic int swoole_ssl_set_default_dhparam(SSL_CTX *ssl_context);\n#endif\n\n#ifdef SW_SUPPORT_DTLS\nstatic int swoole_ssl_generate_cookie(SSL *ssl, uchar *cookie, uint *cookie_len);\nstatic int swoole_ssl_verify_cookie(SSL *ssl, const uchar *cookie, uint cookie_len);\n#endif\n\nstd::string swoole_ssl_get_version_message() {\n    return swoole::std_string::format(\"OPENSSL_VERSION: %s\\n\", OPENSSL_VERSION_TEXT);\n}\n\nvoid swoole_ssl_init() {\n    if (openssl_init) {\n        return;\n    }\n\n    if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG | OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS,\n                          nullptr)) {\n        swoole_error(\"OPENSSL_init_ssl() failed\");\n        return;\n    }\n\n    ssl_connection_index = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);\n    if (ssl_connection_index < 0) {\n        swoole_error(\"SSL_get_ex_new_index() failed\");\n        return;\n    }\n\n    ssl_port_index = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);\n    if (ssl_port_index < 0) {\n        swoole_error(\"SSL_get_ex_new_index() failed\");\n        return;\n    }\n\n    openssl_init = true;\n}\n\nint swoole_ssl_get_ex_connection_index() {\n    return ssl_connection_index;\n}\n\nint swoole_ssl_get_ex_port_index() {\n    return ssl_port_index;\n}\n\nvoid swoole_ssl_destroy() {\n    if (!openssl_init) {\n        return;\n    }\n    openssl_init = false;\n}\n\nstatic int ssl_error_cb(const char *str, size_t len, void *buf) {\n    auto s = static_cast<swoole::String *>(buf);\n    memcpy(s->str, str, len);\n    s->length = len;\n    s->set_null_terminated();\n    return 0;\n}\n\nconst char *swoole_ssl_get_error() {\n    sw_tg_buffer()->clear();\n    sw_tg_buffer()->set_null_terminated();\n    ERR_print_errors_cb(ssl_error_cb, sw_tg_buffer());\n    return sw_tg_buffer()->str;\n}\n\nstatic void swoole_ssl_info_callback(const SSL *ssl, int where, int ret) {\n    Socket *sock;\n\n    if (where & SSL_CB_HANDSHAKE_START) {\n        sock = (Socket *) SSL_get_ex_data(ssl, ssl_connection_index);\n\n        if (sock->ssl_state == SW_SSL_STATE_READY) {\n            sock->ssl_renegotiation = 1;\n            swoole_debug(\"SSL renegotiation\");\n        }\n    }\n\n    if ((where & SSL_CB_ACCEPT_LOOP) == SSL_CB_ACCEPT_LOOP) {\n        sock = (Socket *) SSL_get_ex_data(ssl, ssl_connection_index);\n\n        if (!sock->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            BIO *rbio = SSL_get_rbio(ssl);\n            BIO *wbio = SSL_get_wbio(ssl);\n\n            if (rbio != wbio) {\n                (void) BIO_set_write_buffer_size(wbio, SW_SSL_BUFFER_SIZE);\n                sock->ssl_handshake_buffer_set = 1;\n            }\n        }\n    }\n}\n\nnamespace swoole {\n\n#define HTTP2_H2_ALPN \"\\x02h2\"\n#define HTTP2_H2_16_ALPN \"\\x05h2-16\"\n#define HTTP2_H2_14_ALPN \"\\x05h2-14\"\n#define HTTP1_NPN \"\\x08http/1.1\"\n\n#define ssl_error(_fmt, ...)                                                                                           \\\n    long _ssl_error = ERR_get_error();                                                                                 \\\n    swoole_warning(_fmt \", Error: %s[%ld]\", ##__VA_ARGS__, ERR_reason_error_string(_ssl_error), _ssl_error);\n\n#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\nstatic int ssl_alpn_advertised(SSL *ssl, const uchar **out, uchar *outlen, const uchar *in, uint32_t inlen, void *arg) {\n    unsigned int protos_len;\n    const char *protos;\n\n    auto *cfg = (SSLContext *) arg;\n    if (cfg->http_v2) {\n        protos = HTTP2_H2_ALPN HTTP1_NPN;\n        protos_len = sizeof(HTTP2_H2_ALPN HTTP1_NPN) - 1;\n    } else {\n        protos = HTTP1_NPN;\n        protos_len = sizeof(HTTP1_NPN) - 1;\n    }\n\n    if (SSL_select_next_proto((uchar **) out, outlen, (const uchar *) protos, protos_len, in, inlen) !=\n        OPENSSL_NPN_NEGOTIATED) {\n        return SSL_TLSEXT_ERR_NOACK;\n    }\n    return SSL_TLSEXT_ERR_OK;\n}\n#endif\n\nstatic int ssl_passwd_callback(char *buf, int num, int verify, void *data) {\n    const auto *ctx = static_cast<SSLContext *>(data);\n    if (!ctx->passphrase.empty()) {\n        const int len = static_cast<int>(ctx->passphrase.length());\n        if (len < num - 1) {\n            memcpy(buf, ctx->passphrase.c_str(), len);\n            buf[len] = '\\0';\n            return len;\n        }\n    }\n    return 0;\n}\n\nbool SSLContext::create() {\n    if (!openssl_init) {\n        swoole_ssl_init();\n    }\n\n    const SSL_METHOD *method;\n#ifdef SW_SUPPORT_DTLS\n    if (protocols & SW_SSL_DTLS) {\n        method = DTLS_method();\n    } else\n#endif\n    {\n        method = SSLv23_method();\n    }\n    if (protocols == 0) {\n        protocols = SW_SSL_ALL;\n    }\n    context = SSL_CTX_new(method);\n    if (context == nullptr) {\n        ssl_error(\"SSL_CTX_new() failed\");\n        return false;\n    }\n\n#ifdef SSL_OP_MICROSOFT_SESS_ID_BUG\n    SSL_CTX_set_options(context, SSL_OP_MICROSOFT_SESS_ID_BUG);\n#endif\n\n#ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG\n    SSL_CTX_set_options(context, SSL_OP_NETSCAPE_CHALLENGE_BUG);\n#endif\n\n    /* server side options */\n#ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG\n    SSL_CTX_set_options(context, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG);\n#endif\n\n#ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER\n    SSL_CTX_set_options(context, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER);\n#endif\n\n#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING\n    /* this option allow a potential SSL 2.0 rollback (CAN-2005-2969) */\n    SSL_CTX_set_options(context, SSL_OP_MSIE_SSLV2_RSA_PADDING);\n#endif\n\n#ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG\n    SSL_CTX_set_options(context, SSL_OP_SSLEAY_080_CLIENT_DH_BUG);\n#endif\n\n#ifdef SSL_OP_TLS_D5_BUG\n    SSL_CTX_set_options(context, SSL_OP_TLS_D5_BUG);\n#endif\n\n#ifdef SSL_OP_TLS_BLOCK_PADDING_BUG\n    SSL_CTX_set_options(context, SSL_OP_TLS_BLOCK_PADDING_BUG);\n#endif\n\n#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS\n    SSL_CTX_set_options(context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);\n#endif\n\n#if OPENSSL_VERSION_NUMBER >= 0x009080dfL\n    /* only in 0.9.8m+ */\n    SSL_CTX_clear_options(context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1);\n#endif\n\n#ifdef SSL_OP_NO_SSLv2\n    if (!(protocols & SW_SSL_SSLv2)) {\n        SSL_CTX_set_options(context, SSL_OP_NO_SSLv2);\n    }\n#endif\n#ifdef SSL_OP_NO_SSLv3\n    if (!(protocols & SW_SSL_SSLv3)) {\n        SSL_CTX_set_options(context, SSL_OP_NO_SSLv3);\n    }\n#endif\n#ifdef SSL_OP_NO_TLSv1\n    if (!(protocols & SW_SSL_TLSv1)) {\n        SSL_CTX_set_options(context, SSL_OP_NO_TLSv1);\n    }\n#endif\n#ifdef SSL_OP_NO_TLSv1_1\n    SSL_CTX_clear_options(context, SSL_OP_NO_TLSv1_1);\n    if (!(protocols & SW_SSL_TLSv1_1)) {\n        SSL_CTX_set_options(context, SSL_OP_NO_TLSv1_1);\n    }\n#endif\n#ifdef SSL_OP_NO_TLSv1_2\n    SSL_CTX_clear_options(context, SSL_OP_NO_TLSv1_2);\n    if (!(protocols & SW_SSL_TLSv1_2) && !(protocols & SW_SSL_DTLS)) {\n        SSL_CTX_set_options(context, SSL_OP_NO_TLSv1_2);\n    }\n#endif\n#ifdef SSL_OP_NO_TLSv1_3\n    SSL_CTX_clear_options(context, SSL_OP_NO_TLSv1_3);\n    if (!(protocols & SW_SSL_TLSv1_3)) {\n        SSL_CTX_set_options(context, SSL_OP_NO_TLSv1_3);\n    }\n#endif\n\n#ifdef SSL_OP_NO_COMPRESSION\n    if (disable_compress) {\n        SSL_CTX_set_options(context, SSL_OP_NO_COMPRESSION);\n    }\n#endif\n\n#ifdef SSL_MODE_RELEASE_BUFFERS\n    SSL_CTX_set_mode(context, SSL_MODE_RELEASE_BUFFERS);\n#endif\n\n#ifdef SSL_MODE_NO_AUTO_CHAIN\n    SSL_CTX_set_mode(context, SSL_MODE_NO_AUTO_CHAIN);\n#endif\n\n    SSL_CTX_set_read_ahead(context, 1);\n    SSL_CTX_set_info_callback(context, swoole_ssl_info_callback);\n\n    if (!passphrase.empty()) {\n        SSL_CTX_set_default_passwd_cb_userdata(context, this);\n        SSL_CTX_set_default_passwd_cb(context, ssl_passwd_callback);\n    }\n\n    if (!cert_file.empty()) {\n        /*\n         * set the local certificate from CertFile\n         */\n        if (SSL_CTX_use_certificate_file(context, cert_file.c_str(), SSL_FILETYPE_PEM) <= 0) {\n            ssl_error(\"SSL_CTX_use_certificate_file(%s) failed\", cert_file.c_str());\n            return true;\n        }\n        /*\n         * if the crt file have many certificate entry ,means certificate chain\n         * we need call this function\n         */\n        if (SSL_CTX_use_certificate_chain_file(context, cert_file.c_str()) <= 0) {\n            ssl_error(\"SSL_CTX_use_certificate_chain_file(%s) failed\", cert_file.c_str());\n            return false;\n        }\n    }\n    if (!key_file.empty()) {\n        /*\n         * set the private key from KeyFile (maybe the same as CertFile)\n         */\n        if (SSL_CTX_use_PrivateKey_file(context, key_file.c_str(), SSL_FILETYPE_PEM) <= 0) {\n            ssl_error(\"SSL_CTX_use_PrivateKey_file(%s) failed\", key_file.c_str());\n            return false;\n        }\n        /*\n         * verify private key\n         */\n        if (!SSL_CTX_check_private_key(context)) {\n            ssl_error(\"SSL_CTX_check_private_key() failed\");\n            return false;\n        }\n    }\n\n#ifdef SW_SUPPORT_DTLS\n    if (protocols & SW_SSL_DTLS) {\n#ifndef OPENSSL_IS_BORINGSSL\n        SSL_CTX_set_cookie_generate_cb(context, swoole_ssl_generate_cookie);\n        SSL_CTX_set_cookie_verify_cb(context, swoole_ssl_verify_cookie);\n#endif\n    }\n#endif\n\n    auto rs = set_capath();\n    if (verify_peer) {\n        if (!rs) {\n            return false;\n        }\n    } else {\n        SSL_CTX_set_verify(context, SSL_VERIFY_NONE, nullptr);\n    }\n\n    if (http || http_v2) {\n        unsigned int protos_len;\n        const char *protos;\n        if (http_v2) {\n            protos = HTTP2_H2_ALPN HTTP1_NPN;\n            protos_len = sizeof(HTTP2_H2_ALPN HTTP1_NPN) - 1;\n        } else {\n            protos = HTTP1_NPN;\n            protos_len = sizeof(HTTP1_NPN) - 1;\n        }\n        if (SSL_CTX_set_alpn_protos(context, (const uchar *) protos, protos_len) < 0) {\n            ssl_error(\"SSL_CTX_set_alpn_protos(%s) failed\", protos);\n            return false;\n        }\n#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\n        SSL_CTX_set_alpn_select_cb(context, ssl_alpn_advertised, (void *) this);\n#endif\n        SSL_CTX_set_session_id_context(context, (const uchar *) \"HTTP\", sizeof(\"HTTP\") - 1);\n        SSL_CTX_set_session_cache_mode(context, SSL_SESS_CACHE_SERVER);\n    }\n\n#ifdef OPENSSL_IS_BORINGSSL\n    SSL_CTX_set_grease_enabled(context, grease);\n#endif\n\n    if (!client_cert_file.empty() && !set_client_certificate()) {\n        return false;\n    }\n\n    if (!set_ciphers()) {\n        swoole_warning(\"set_cipher() error\");\n        return false;\n    }\n\n    return true;\n}\n\nbool SSLContext::set_capath() const {\n    if (!cafile.empty() || !capath.empty()) {\n        const char *_cafile = cafile.empty() ? nullptr : cafile.c_str();\n        const char *_capath = capath.empty() ? nullptr : capath.c_str();\n        if (!SSL_CTX_load_verify_locations(context, _cafile, _capath)) {\n            return false;\n        }\n    } else {\n        if (!SSL_CTX_set_default_verify_paths(context)) {\n            ssl_error(\"SSL_CTX_set_default_verify_paths() failed\");\n            return false;\n        }\n    }\n\n    if (verify_depth > 0) {\n        SSL_CTX_set_verify_depth(context, verify_depth);\n    }\n\n    return true;\n}\n\nbool SSLContext::set_ciphers() const {\n#ifndef TLS1_2_VERSION\n    return true;\n#endif\n\n    if (!ciphers.empty()) {\n        if (SSL_CTX_set_cipher_list(context, ciphers.c_str()) == 0) {\n            ssl_error(\"SSL_CTX_set_cipher_list(\\\"%s\\\") failed\", ciphers.c_str());\n            return false;\n        }\n        if (prefer_server_ciphers && !SSL_CTX_set_options(context, SSL_OP_CIPHER_SERVER_PREFERENCE)) {\n            ssl_error(\"SSL_CTX_set_options(SSL_OP_CIPHER_SERVER_PREFERENCE) failed\");\n            return false;\n        }\n    }\n\n    if (!dhparam.empty() && !set_dhparam()) {\n        return false;\n    }\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\n    else {\n        swoole_ssl_set_default_dhparam(context);\n    }\n#endif\n    if (!ecdh_curve.empty() && !set_ecdh_curve()) {\n        return false;\n    }\n    return true;\n}\n\nbool SSLContext::set_client_certificate() const {\n    const char *_cert_file = client_cert_file.c_str();\n    int depth = verify_depth;\n\n    SSL_CTX_set_verify(context, SSL_VERIFY_PEER, swoole_ssl_verify_callback);\n    SSL_CTX_set_verify_depth(context, depth);\n\n    if (SSL_CTX_load_verify_locations(context, _cert_file, nullptr) == 0) {\n        ssl_error(\"SSL_CTX_load_verify_locations(\\\"%s\\\") failed\", _cert_file);\n        return false;\n    }\n\n    ERR_clear_error();\n    STACK_OF(X509_NAME) *list = SSL_load_client_CA_file(_cert_file);\n    if (list == nullptr) {\n        ssl_error(\"SSL_load_client_CA_file(\\\"%s\\\") failed\", _cert_file);\n        return false;\n    }\n\n    ERR_clear_error();\n    SSL_CTX_set_client_CA_list(context, list);\n\n    return true;\n}\n\nbool SSLContext::set_ecdh_curve() const {\n#ifndef OPENSSL_NO_ECDH\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#if (defined SSL_CTX_set1_curves_list || defined SSL_CTRL_SET_CURVES_LIST)\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    SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE);\n#if SSL_CTRL_SET_ECDH_AUTO\n    /* not needed in OpenSSL 1.1.0+ */\n    SSL_CTX_set_ecdh_auto(context, 1);\n#endif\n    if (strcmp(ecdh_curve.c_str(), \"auto\") == 0) {\n        return true;\n    }\n    if (SSL_CTX_set1_curves_list(context, ecdh_curve.c_str()) == 0) {\n        swoole_warning(\"SSL_CTX_set1_curves_list(\\\"%s\\\") failed\", ecdh_curve.c_str());\n        return false;\n    }\n#else\n    EC_KEY *ecdh;\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    int nid = OBJ_sn2nid(ecdh_curve.c_str());\n    if (nid == 0) {\n        swoole_warning(\"Unknown curve name \\\"%s\\\"\", ecdh_curve.c_str());\n        return false;\n    }\n\n    ecdh = EC_KEY_new_by_curve_name(nid);\n    if (ecdh == nullptr) {\n        swoole_warning(\"Unable to create curve \\\"%s\\\"\", ecdh_curve.c_str());\n        return false;\n    }\n\n    SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE);\n    SSL_CTX_set_tmp_ecdh(context, ecdh);\n\n    EC_KEY_free(ecdh);\n#endif\n#endif\n\n    return true;\n}\n\nbool SSLContext::set_dhparam() const {\n    const char *file = dhparam.c_str();\n\n    BIO *bio = BIO_new_file(file, \"r\");\n    if (bio == nullptr) {\n        ssl_error(\"BIO_new_file(%s) failed\", file);\n        return false;\n    }\n\n#if OPENSSL_VERSION_MAJOR >= 3\n    EVP_PKEY *pkey = PEM_read_bio_Parameters(bio, nullptr);\n    if (pkey == nullptr) {\n        ssl_error(\"PEM_read_bio_Parameters('%s') failed\", file);\n        BIO_free(bio);\n        return false;\n    }\n\n    if (SSL_CTX_set0_tmp_dh_pkey(context, pkey) != 1) {\n        ssl_error(\"SSL_CTX_set0_tmp_dh_pkey('%s') failed\", file);\n        EVP_PKEY_free(pkey);\n        BIO_free(bio);\n        return false;\n    }\n#else\n    DH *dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr);\n    if (dh == nullptr) {\n        ssl_error(\"PEM_read_bio_DHparams(%s) failed\", file);\n        BIO_free(bio);\n        return false;\n    }\n\n    SSL_CTX_set_tmp_dh(context, dh);\n\n    DH_free(dh);\n#endif\n\n    BIO_free(bio);\n\n    return true;\n}\n\nSSLContext::~SSLContext() {\n    SSL_CTX_free(context);\n}\n\n}  // namespace swoole\n\nstatic int swoole_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store) {\n#if 0\n    char *subject, *issuer;\n    int err, depth;\n    X509 *cert;\n    X509_NAME *sname, *iname;\n    X509_STORE_CTX_get_ex_data(x509_store, SSL_get_ex_data_X509_STORE_CTX_idx());\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    subject = sname ? X509_NAME_oneline(sname, nullptr, 0) : \"(none)\";\n\n    iname = X509_get_issuer_name(cert);\n    issuer = iname ? X509_NAME_oneline(iname, nullptr, 0) : \"(none)\";\n    swoole_warning(\"verify:%d, error:%d, depth:%d, subject:\\\"%s\\\", issuer:\\\"%s\\\"\", ok, err, depth, subject, issuer);\n\n    if (sname)\n    {\n        OPENSSL_free(subject);\n    }\n    if (iname)\n    {\n        OPENSSL_free(issuer);\n    }\n#endif\n\n    return 1;\n}\n\n#ifdef SW_SUPPORT_DTLS\n\n#define COOKIE_SECRET_LENGTH (32)\n\nstatic void calculate_cookie(SSL *ssl, uchar *cookie_secret, uint cookie_length) {\n    long rv = (long) ssl;\n    long inum = (cookie_length - (((long) cookie_secret) % sizeof(long))) / sizeof(long);\n    long i = 0;\n    long *ip = (long *) cookie_secret;\n    for (i = 0; i < inum; ++i, ++ip) {\n        *ip = rv;\n    }\n}\n\nstatic int swoole_ssl_generate_cookie(SSL *ssl, uchar *cookie, uint *cookie_len) {\n    uchar result[EVP_MAX_MD_SIZE];\n    uint length = 0, result_len;\n    Address sa{};\n\n    uchar cookie_secret[COOKIE_SECRET_LENGTH];\n    calculate_cookie(ssl, cookie_secret, sizeof(cookie_secret));\n\n    /* Read peer information */\n    (void) BIO_dgram_get_peer(SSL_get_wbio(ssl), &sa);\n\n    length = 0;\n    switch (sa.addr.ss.sa_family) {\n    case AF_INET:\n        length += sizeof(struct in_addr);\n        break;\n    case AF_INET6:\n        length += sizeof(struct in6_addr);\n        break;\n    default:\n        OPENSSL_assert(0);\n        break;\n    }\n\n    length += sizeof(in_port_t);\n    auto *buffer = (uchar *) OPENSSL_malloc(length);\n\n    if (buffer == nullptr) {\n        swoole_sys_warning(\"out of memory\");\n        return 0;\n    }\n\n    switch (sa.addr.ss.sa_family) {\n    case AF_INET:\n        memcpy(buffer, &sa.addr.inet_v4.sin_port, sizeof(in_port_t));\n        memcpy(buffer + sizeof(sa.addr.inet_v4.sin_port), &sa.addr.inet_v4.sin_addr, sizeof(struct in_addr));\n        break;\n    case AF_INET6:\n        memcpy(buffer, &sa.addr.inet_v6.sin6_port, sizeof(in_port_t));\n        memcpy(buffer + sizeof(in_port_t), &sa.addr.inet_v6.sin6_addr, sizeof(struct in6_addr));\n        break;\n    default:\n        OPENSSL_assert(0);\n        break;\n    }\n\n    HMAC(EVP_sha1(), (const void *) cookie_secret, COOKIE_SECRET_LENGTH, buffer, length, result, &result_len);\n    OPENSSL_free(buffer);\n\n    memcpy(cookie, result, result_len);\n    *cookie_len = result_len;\n\n    return 1;\n}\n\nstatic int swoole_ssl_verify_cookie(SSL *ssl, const uchar *cookie, uint cookie_len) {\n    uint result_len = 0;\n    uchar result[COOKIE_SECRET_LENGTH];\n\n    swoole_ssl_generate_cookie(ssl, result, &result_len);\n\n    return cookie_len == result_len && memcmp(result, cookie, result_len) == 0;\n}\n#endif\n\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\nstatic int swoole_ssl_set_default_dhparam(SSL_CTX *ssl_context) {\n    DH *dh;\n    static unsigned char dh1024_p[] = {\n        0xBB, 0xBC, 0x2D, 0xCA, 0xD8, 0x46, 0x74, 0x90, 0x7C, 0x43, 0xFC, 0xF5, 0x80, 0xE9, 0xCF, 0xDB,\n        0xD9, 0x58, 0xA3, 0xF5, 0x68, 0xB4, 0x2D, 0x4B, 0x08, 0xEE, 0xD4, 0xEB, 0x0F, 0xB3, 0x50, 0x4C,\n        0x6C, 0x03, 0x02, 0x76, 0xE7, 0x10, 0x80, 0x0C, 0x5C, 0xCB, 0xBA, 0xA8, 0x92, 0x26, 0x14, 0xC5,\n        0xBE, 0xEC, 0xA5, 0x65, 0xA5, 0xFD, 0xF1, 0xD2, 0x87, 0xA2, 0xBC, 0x04, 0x9B, 0xE6, 0x77, 0x80,\n        0x60, 0xE9, 0x1A, 0x92, 0xA7, 0x57, 0xE3, 0x04, 0x8F, 0x68, 0xB0, 0x76, 0xF7, 0xD3, 0x6C, 0xC8,\n        0xF2, 0x9B, 0xA5, 0xDF, 0x81, 0xDC, 0x2C, 0xA7, 0x25, 0xEC, 0xE6, 0x62, 0x70, 0xCC, 0x9A, 0x50,\n        0x35, 0xD8, 0xCE, 0xCE, 0xEF, 0x9E, 0xA0, 0x27, 0x4A, 0x63, 0xAB, 0x1E, 0x58, 0xFA, 0xFD, 0x49,\n        0x88, 0xD0, 0xF6, 0x5D, 0x14, 0x67, 0x57, 0xDA, 0x07, 0x1D, 0xF0, 0x45, 0xCF, 0xE1, 0x6B, 0x9B};\n\n    static unsigned char dh1024_g[] = {0x02};\n    dh = DH_new();\n    if (dh == nullptr) {\n        swoole_warning(\"DH_new() failed\");\n        return SW_ERR;\n    }\n\n    dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), nullptr);\n    dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), nullptr);\n\n    if (dh->p == nullptr || dh->g == nullptr) {\n        DH_free(dh);\n    }\n    SSL_CTX_set_tmp_dh(ssl_context, dh);\n    DH_free(dh);\n    return SW_OK;\n}\n#endif\n"
  },
  {
    "path": "src/protocol/websocket.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_server.h\"\n#include \"swoole_websocket.h\"\n\nusing swoole::Connection;\nusing swoole::Protocol;\nusing swoole::Server;\nusing swoole::String;\nusing swoole::network::Socket;\n\nnamespace swoole {\nnamespace websocket {\n/*  The following is websocket data frame:\n +-+-+-+-+-------+-+-------------+-------------------------------+\n 0                   1                   2                   3   |\n 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |\n +-+-+-+-+-------+-+-------------+-------------------------------+\n |F|R|R|R| opcode|M| Payload len |    Extended payload length    |\n |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |\n |N|V|V|V|       |S|             |   (if payload len==126/127)   |\n | |1|2|3|       |K|             |                               |\n +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +\n |     Extended payload length continued, if payload len == 127  |\n + - - - - - - - - - - - - - - - +-------------------------------+\n |                               |Masking-key, if MASK set to 1  |\n +-------------------------------+-------------------------------+\n | Masking-key (continued)       |          Payload Data         |\n +-------------------------------- - - - - - - - - - - - - - - - +\n :                     Payload Data continued ...                :\n + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +\n |                     Payload Data continued ...                |\n +---------------------------------------------------------------+\n */\nstatic ssize_t get_package_length_impl(PacketLength *pl) {\n    // need more data\n    if (pl->buf_size < SW_WEBSOCKET_HEADER_LEN) {\n        return 0;\n    }\n    const char *buf = pl->buf;\n    char mask = (buf[1] >> 7) & 0x1;\n    // 0-125\n    uint64_t payload_length = buf[1] & 0x7f;\n    pl->header_len = SW_WEBSOCKET_HEADER_LEN;\n    buf += SW_WEBSOCKET_HEADER_LEN;\n\n    // uint16_t, 2byte\n    if (payload_length == SW_WEBSOCKET_EXT16_LENGTH) {\n        pl->header_len += sizeof(uint16_t);\n        if (pl->buf_size < pl->header_len) {\n            return 0;\n        }\n        payload_length = ntohs(*((uint16_t *) buf));\n    }\n    // uint64_t, 8byte\n    else if (payload_length == SW_WEBSOCKET_EXT64_LENGTH) {\n        pl->header_len += sizeof(uint64_t);\n        if (pl->buf_size < pl->header_len) {\n            return 0;\n        }\n        payload_length = swoole_ntoh64(*((uint64_t *) buf));\n    }\n    if (mask) {\n        pl->header_len += SW_WEBSOCKET_MASK_LEN;\n        if (pl->buf_size < pl->header_len) {\n            return 0;\n        }\n    }\n    if ((ssize_t) payload_length < 0) {\n        return -1;\n    }\n    swoole_trace_log(\n        SW_TRACE_LENGTH_PROTOCOL, \"header_length=%u, payload_length=%\" PRIu64, pl->header_len, payload_length);\n\n    return (ssize_t) pl->header_len + (ssize_t) payload_length;\n}\n\nssize_t get_package_length(const Protocol *protocol, Socket *conn, PacketLength *pl) {\n    return get_package_length_impl(pl);\n}\n\nvoid mask(char *data, size_t len, const char *mask_key) {\n    size_t n = len / 8;\n    uint64_t mask_key64 = ((uint64_t) (*((uint32_t *) mask_key)) << 32) | *((uint32_t *) mask_key);\n    size_t i;\n\n    for (i = 0; i < n; i++) {\n        ((uint64_t *) data)[i] ^= mask_key64;\n    }\n\n    for (i = n * 8; i < len; i++) {\n        data[i] ^= mask_key[i % SW_WEBSOCKET_MASK_LEN];\n    }\n}\n\nbool encode(String *buffer, const char *data, size_t length, uint8_t opcode, uint8_t _flags) {\n    int pos = 0;\n    char frame_header[16];\n    auto *header = (Header *) frame_header;\n    header->FIN = !!(_flags & FLAG_FIN);\n    header->OPCODE = opcode;\n    header->RSV1 = !!(_flags & FLAG_RSV1);\n    header->RSV2 = 0;\n    header->RSV3 = 0;\n    header->MASK = !!(_flags & FLAG_MASK);\n    pos = 2;\n\n    if (length < SW_WEBSOCKET_EXT16_LENGTH) {\n        header->LENGTH = length;\n    } else if (length <= SW_WEBSOCKET_EXT16_MAX_LEN) {\n        header->LENGTH = SW_WEBSOCKET_EXT16_LENGTH;\n        auto *length_ptr = (uint16_t *) (frame_header + pos);\n        *length_ptr = htons(length);\n        pos += sizeof(*length_ptr);\n    } else {\n        header->LENGTH = SW_WEBSOCKET_EXT64_LENGTH;\n        auto *length_ptr = (uint64_t *) (frame_header + pos);\n        *length_ptr = swoole_hton64(length);\n        pos += sizeof(*length_ptr);\n    }\n    buffer->append(frame_header, pos);\n    /**\n     * frame body\n     */\n    if (header->MASK) {\n        buffer->append(SW_WEBSOCKET_MASK_DATA, SW_WEBSOCKET_MASK_LEN);\n        if (_flags & FLAG_ENCODE_HEADER_ONLY) {\n            return false;\n        }\n        if (length > 0) {\n            size_t offset = buffer->length;\n            // Warn: buffer may be extended, string pointer will change\n            buffer->append(data, length);\n            mask(buffer->str + offset, length, SW_WEBSOCKET_MASK_DATA);\n        }\n    } else {\n        if (length > 0 and !(_flags & FLAG_ENCODE_HEADER_ONLY)) {\n            buffer->append(data, length);\n        }\n    }\n\n    return true;\n}\n\nbool decode(Frame *frame, char *data, size_t length) {\n    frame->header.OPCODE = data[0] & 0xf;\n    frame->header.RSV1 = (data[0] >> 6) & 0x1;\n    frame->header.RSV2 = (data[0] >> 5) & 0x1;\n    frame->header.RSV3 = (data[0] >> 4) & 0x1;\n    frame->header.FIN = (data[0] >> 7) & 0x1;\n    frame->header.MASK = (data[1] >> 7) & 0x1;\n    frame->header.LENGTH = data[1] & 0x7f;\n\n    PacketLength pl{data, (uint32_t) length, 0};\n    ssize_t total_length = get_package_length_impl(&pl);\n    if (total_length <= 0 || length < (size_t) total_length) {\n        swoole_error_log(SW_LOG_WARNING,\n                         SW_ERROR_WEBSOCKET_INCOMPLETE_PACKET,\n                         \"incomplete packet, expected length is %zu, actual length is %zu\",\n                         total_length,\n                         length);\n        return false;\n    }\n\n    frame->payload_length = total_length - pl.header_len;\n    frame->header_length = pl.header_len;\n\n    swoole_trace_log(SW_TRACE_WEBSOCKET,\n                     \"decode frame, payload_length=%ld, mask=%d, opcode=%d\",\n                     frame->payload_length,\n                     frame->header.MASK,\n                     frame->header.OPCODE);\n\n    if (frame->payload_length == 0) {\n        frame->payload = nullptr;\n    } else {\n        frame->payload = data + frame->header_length;\n        if (frame->header.MASK) {\n            memcpy(frame->mask_key, frame->payload - SW_WEBSOCKET_MASK_LEN, SW_WEBSOCKET_MASK_LEN);\n            mask(frame->payload, frame->payload_length, frame->mask_key);\n        }\n    }\n\n    return true;\n}\n\nbool pack_close_frame(String *buffer, int code, const char *reason, size_t length, uint8_t flags) {\n    if (sw_unlikely(length > SW_WEBSOCKET_CLOSE_REASON_MAX_LEN)) {\n        swoole_warning(\"the max length of close reason is %d\", SW_WEBSOCKET_CLOSE_REASON_MAX_LEN);\n        return false;\n    }\n\n    char payload[SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_CLOSE_CODE_LEN + SW_WEBSOCKET_CLOSE_REASON_MAX_LEN];\n    payload[0] = (char) ((code >> 8 & 0xFF));\n    payload[1] = (char) ((code & 0xFF));\n    if (length > 0) {\n        memcpy(payload + SW_WEBSOCKET_CLOSE_CODE_LEN, reason, length);\n    }\n    flags |= FLAG_FIN;\n    if (!encode(buffer, payload, SW_WEBSOCKET_CLOSE_CODE_LEN + length, OPCODE_CLOSE, flags)) {\n        return false;\n    }\n    return true;\n}\n\nvoid print_frame(const Frame *frame) {\n    sw_printf(\"FIN: %x, RSV1: %d, RSV2: %d, RSV3: %d, opcode: %d, MASK: %d, length: %ld\\n\",\n              frame->header.FIN,\n              frame->header.RSV1,\n              frame->header.RSV2,\n              frame->header.RSV3,\n              frame->header.OPCODE,\n              frame->header.MASK,\n              frame->payload_length);\n\n    if (frame->payload_length) {\n        sw_printf(\"payload: %.*s\\n\", (int) frame->payload_length, frame->payload);\n    }\n}\n\nint dispatch_frame(const Protocol *proto, Socket *_socket, const RecvData *rdata) {\n    auto *serv = (Server *) proto->private_data_2;\n    auto *conn = (Connection *) _socket->object;\n    RecvData dispatch_data{};\n    String send_frame{};\n    const char *data = rdata->data;\n    const uint32_t length = rdata->info.len;\n    char buf[SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_CLOSE_CODE_LEN + SW_WEBSOCKET_CLOSE_REASON_MAX_LEN];\n    send_frame.str = buf;\n    send_frame.size = sizeof(buf);\n\n    Frame ws;\n    if (!decode(&ws, const_cast<char *>(data), length)) {\n        return SW_ERR;\n    }\n\n    String *frame_buffer;\n    int frame_length;\n    ListenPort *port;\n\n    size_t offset;\n    switch (ws.header.OPCODE) {\n    case OPCODE_CONTINUATION:\n        frame_buffer = conn->websocket_buffer;\n        if (frame_buffer == nullptr) {\n            swoole_warning(\"bad frame[opcode=0]. remote_addr=%s:%d\", conn->info.get_addr(), conn->info.get_port());\n            return SW_ERR;\n        }\n        offset = length - ws.payload_length;\n        frame_length = length - offset;\n        port = serv->get_port_by_fd(conn->fd);\n        // frame data overflow\n        if (frame_buffer->length + frame_length > port->protocol.package_max_length) {\n            swoole_warning(\n                \"websocket frame is too big, remote_addr=%s:%d\", conn->info.get_addr(), conn->info.get_port());\n            return SW_ERR;\n        }\n        // merge incomplete data\n        frame_buffer->append(data + offset, frame_length);\n        // frame is finished, do dispatch\n        if (ws.header.FIN) {\n            dispatch_data.info.ext_flags = conn->websocket_buffer->offset | FLAG_FIN;\n            dispatch_data.info.len = frame_buffer->length;\n            dispatch_data.data = frame_buffer->str;\n            Server::dispatch_task(proto, _socket, &dispatch_data);\n            delete frame_buffer;\n            conn->websocket_buffer = nullptr;\n        }\n        break;\n\n    case OPCODE_TEXT:\n    case OPCODE_BINARY: {\n        offset = length - ws.payload_length;\n        uint16_t ext_flags = get_ext_flags(ws.header.OPCODE, ws.get_flags());\n        if (!ws.header.FIN) {\n            if (conn->websocket_buffer) {\n                swoole_warning(\"merging incomplete frame, bad request. remote_addr=%s:%d\",\n                               conn->info.get_addr(),\n                               conn->info.get_port());\n                return SW_ERR;\n            }\n            conn->websocket_buffer = new String(data + offset, length - offset);\n            conn->websocket_buffer->offset = ext_flags;\n        } else {\n            dispatch_data.info.ext_flags = ext_flags;\n            dispatch_data.info.len = length - offset;\n            dispatch_data.data = data + offset;\n            Server::dispatch_task(proto, _socket, &dispatch_data);\n        }\n        break;\n    }\n    case OPCODE_PING:\n    case OPCODE_PONG:\n        if (length >= (sizeof(buf) - SW_WEBSOCKET_HEADER_LEN)) {\n            swoole_warning(\"%s frame application data is too big. remote_addr=%s:%d\",\n                           ws.header.OPCODE == OPCODE_PING ? \"ping\" : \"pong\",\n                           conn->info.get_addr(),\n                           conn->info.get_port());\n            return SW_ERR;\n        } else if (length == SW_WEBSOCKET_HEADER_LEN) {\n            dispatch_data.data = nullptr;\n            dispatch_data.info.len = 0;\n        } else {\n            offset = ws.header.MASK ? SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_MASK_LEN : SW_WEBSOCKET_HEADER_LEN;\n            dispatch_data.info.len = length - offset;\n            dispatch_data.data = dispatch_data.info.len == 0 ? nullptr : data + offset;\n        }\n        dispatch_data.info.ext_flags = get_ext_flags(ws.header.OPCODE, ws.get_flags());\n        Server::dispatch_task(proto, _socket, &dispatch_data);\n        break;\n\n    case OPCODE_CLOSE:\n        if ((length - SW_WEBSOCKET_HEADER_LEN) > SW_WEBSOCKET_CLOSE_REASON_MAX_LEN) {\n            return SW_ERR;\n        }\n\n        if (conn->websocket_status != STATUS_CLOSING) {\n            // Dispatch the frame with the same format of message frame\n            offset = length - ws.payload_length;\n            dispatch_data.info.ext_flags = get_ext_flags(ws.header.OPCODE, ws.get_flags());\n            dispatch_data.info.len = length - offset;\n            dispatch_data.data = data + offset;\n            Server::dispatch_task(proto, _socket, &dispatch_data);\n\n            // Client attempt to close\n            send_frame.str[0] = 0x88;  // FIN | OPCODE: WEBSOCKET_OPCODE_CLOSE\n            send_frame.str[1] = ws.payload_length;\n            // Get payload and return it as it is\n            memcpy(send_frame.str + SW_WEBSOCKET_HEADER_LEN, data + length - ws.payload_length, ws.payload_length);\n            send_frame.length = SW_WEBSOCKET_HEADER_LEN + ws.payload_length;\n            _socket->send(send_frame.str, send_frame.length, 0);\n        } else {\n            // Server attempt to close, frame sent by swoole_websocket_server->disconnect()\n            conn->websocket_status = 0;\n        }\n\n        return SW_ERR;\n\n    default:\n        swoole_warning(\"unknown opcode [%d]\", ws.header.OPCODE);\n        break;\n    }\n    return SW_OK;\n}\n}  // namespace websocket\n}  // namespace swoole\n"
  },
  {
    "path": "src/reactor/base.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_socket.h\"\n#include \"swoole_signal.h\"\n#include \"swoole_reactor.h\"\n#include \"swoole_timer.h\"\n\n#include \"swoole_api.h\"\n\nnamespace swoole {\nusing network::Socket;\n\n#ifdef SW_USE_MALLOC_TRIM\n#ifdef __APPLE__\n#include <sys/malloc.h>\n#else\n#include <malloc.h>\n#endif\n#endif\n\n#ifdef HAVE_EPOLL\nReactorImpl *make_reactor_epoll(Reactor *_reactor, int max_events);\n#endif\n\n#ifdef HAVE_KQUEUE\nReactorImpl *make_reactor_kqueue(Reactor *_reactor, int max_events);\n#endif\n\nReactorImpl *make_reactor_poll(Reactor *_reactor, int max_events);\n\nvoid ReactorImpl::after_removal_failure(const Socket *_socket) const {\n    if (!_socket->silent_remove) {\n        swoole_error_log(SW_LOG_WARNING,\n                         SW_ERROR_EVENT_REMOVE_FAILED,\n                         \"failed to delete events[fd=%d#%d, type=%d, events=%d, errno=%d]\",\n                         _socket->fd,\n                         reactor_->id,\n                         _socket->fd_type,\n                         _socket->events,\n                         errno);\n        swoole_print_backtrace_on_error();\n    }\n}\n\nReactor::Reactor(int max_event, Type _type) {\n    if (_type == TYPE_AUTO) {\n#ifdef HAVE_EPOLL\n        type_ = TYPE_EPOLL;\n#else\n        type_ = TYPE_POLL;\n#ifdef HAVE_KQUEUE\n        /**\n         * When kqueue is enabled, the Process mode of the Server module and functionalities such as Task operations,\n         * pipe messaging, and inter-process message forwarding that rely on pipe communication will be unavailable.\n         */\n        if (SwooleG.enable_kqueue) {\n            type_ = TYPE_KQUEUE;\n        }\n#endif\n#endif\n    } else {\n        type_ = _type;\n    }\n\n    switch (type_) {\n#ifdef HAVE_EPOLL\n    case TYPE_EPOLL:\n        impl = make_reactor_epoll(this, max_event);\n        break;\n#endif\n#ifdef HAVE_KQUEUE\n    case TYPE_KQUEUE:\n        impl = make_reactor_kqueue(this, max_event);\n        break;\n#endif\n    default:\n        impl = make_reactor_poll(this, max_event);\n        break;\n    }\n\n    ready_ = impl->ready();\n    if (!ready_) {\n        return;\n    }\n\n    timeout_msec = -1;\n    idle_task = {};\n    future_task = {};\n\n    write = _write;\n    writev = _writev;\n    close = _close;\n\n    default_write_handler = _writable_callback;\n\n    if (swoole_isset_hook(SW_GLOBAL_HOOK_ON_REACTOR_CREATE)) {\n        swoole_call_hook(SW_GLOBAL_HOOK_ON_REACTOR_CREATE, this);\n    }\n\n    set_end_callback(PRIORITY_DEFER_TASK, [](Reactor *reactor) {\n        CallbackManager *cm = reactor->defer_tasks;\n        if (cm) {\n            reactor->defer_tasks = nullptr;\n            cm->execute();\n            delete cm;\n        }\n    });\n\n    set_exit_condition(EXIT_CONDITION_DEFER_TASK,\n                       [](Reactor *reactor, size_t &event_num) -> bool { return reactor->defer_tasks == nullptr; });\n\n    set_end_callback(PRIORITY_IDLE_TASK, [](Reactor *reactor) {\n        if (reactor->idle_task.callback) {\n            reactor->idle_task.callback(reactor->idle_task.data);\n        }\n    });\n\n    if (swoole_is_main_thread()) {\n        set_end_callback(PRIORITY_SIGNAL_CALLBACK, [](Reactor *) { swoole_signal_dispatch(); });\n    }\n\n    set_end_callback(PRIORITY_TRY_EXIT, [](Reactor *reactor) {\n        if (reactor->wait_exit && reactor->if_exit()) {\n            reactor->running = false;\n        }\n    });\n\n#ifdef SW_USE_MALLOC_TRIM\n    if (swoole_is_main_thread()) {\n        set_end_callback(PRIORITY_MALLOC_TRIM, [](Reactor *reactor) {\n            time_t now = ::time(nullptr);\n            if (reactor->last_malloc_trim_time < now - SW_MALLOC_TRIM_INTERVAL) {\n                malloc_trim(SW_MALLOC_TRIM_PAD);\n                reactor->last_malloc_trim_time = now;\n            }\n        });\n    }\n#endif\n\n    set_exit_condition(EXIT_CONDITION_DEFAULT, [](Reactor *, size_t &event_num) -> bool { return event_num == 0; });\n}\n\nvoid Reactor::set_handler(const int fd_type, const int event, const ReactorHandler handler) {\n    if (isset_read_event(event)) {\n        read_handler[fd_type] = handler;\n    } else if (isset_write_event(event)) {\n        write_handler[fd_type] = handler;\n    } else if (isset_error_event(event)) {\n        error_handler[fd_type] = handler;\n    } else {\n        assert(0);\n    }\n}\n\nbool Reactor::isset_handler(const int fd_type, const int event) const {\n    if (isset_read_event(event)) {\n        return read_handler[fd_type] != nullptr;\n    } else if (isset_write_event(event)) {\n        return write_handler[fd_type] != nullptr;\n    } else if (isset_error_event(event)) {\n        return error_handler[fd_type] != nullptr;\n    } else {\n        return false;\n    }\n}\n\nbool Reactor::if_exit() {\n    size_t _event_num = get_event_num();\n    for (auto &kv : exit_conditions) {\n        if (kv.second(this, _event_num) == false) {\n            return false;\n        }\n    }\n    return true;\n}\n\nint Reactor::_close(Reactor *reactor, Socket *socket) {\n    swoole_trace_log(SW_TRACE_CLOSE, \"fd=%d\", socket->fd);\n    socket->free();\n    return SW_OK;\n}\n\nssize_t Reactor::write_func(const Reactor *reactor,\n                            Socket *socket,\n                            const size_t _len,\n                            const std::function<ssize_t()> &send_fn,\n                            const std::function<void(Buffer *buffer)> &append_fn) {\n    Buffer *buffer = socket->out_buffer;\n\n    if (socket->buffer_size == 0) {\n        socket->set_memory_buffer_size(Socket::default_buffer_size);\n    }\n\n    if (socket->nonblock == 0) {\n        socket->set_fd_option(1, -1);\n    }\n\n    if ((uint32_t) _len > socket->buffer_size) {\n        swoole_error_log(SW_LOG_WARNING,\n                         SW_ERROR_PACKAGE_LENGTH_TOO_LARGE,\n                         \"data packet is too large, cannot exceed the socket buffer size\");\n        return SW_ERR;\n    }\n\n    if (Buffer::empty(buffer)) {\n        ssize_t retval;\n        if (socket->ssl_send_) {\n            goto _alloc_buffer;\n        }\n    _do_send:\n        retval = send_fn();\n\n        if (retval > 0) {\n            if ((ssize_t) _len == retval) {\n                return retval;\n            } else {\n                goto _alloc_buffer;\n            }\n        } else if (socket->catch_write_error(errno) == SW_WAIT) {\n        _alloc_buffer:\n            if (!socket->out_buffer) {\n                buffer = new Buffer(socket->chunk_size);\n                socket->out_buffer = buffer;\n            }\n            if (!socket->isset_writable_event()) {\n                reactor->add_write_event(socket);\n            }\n            /**\n             * Part of the data has been successfully written to the kernel's socket buffer,\n             * and at this point, writing to the memory queue is permitted under any circumstances.\n             * Ensure that the async write operation either succeeds completely or fails entirely.\n             */\n            goto _append_buffer;\n        } else if (errno == EINTR) {\n            goto _do_send;\n        } else {\n            swoole_set_last_error(errno);\n            return SW_ERR;\n        }\n    } else {\n        if (buffer->length() + _len > socket->buffer_size) {\n            swoole_error_log(SW_LOG_WARNING,\n                             SW_ERROR_OUTPUT_BUFFER_OVERFLOW,\n                             \"socket#%d output buffer overflow: (%u/%u)\",\n                             socket->get_fd(),\n                             buffer->length(),\n                             socket->buffer_size);\n            return SW_ERR;\n        }\n    _append_buffer:\n        append_fn(buffer);\n    }\n    return _len;\n}\n\nssize_t Reactor::_write(Reactor *reactor, Socket *socket, const void *buf, size_t n) {\n    ssize_t send_bytes = 0;\n    auto send_fn = [&send_bytes, socket, buf, n]() -> ssize_t {\n        send_bytes = socket->send(buf, n, 0);\n        return send_bytes;\n    };\n    auto append_fn = [&send_bytes, buf, n](Buffer *buffer) {\n        ssize_t offset = send_bytes > 0 ? send_bytes : 0;\n        buffer->append(static_cast<const char *>(buf) + offset, n - offset);\n    };\n    return write_func(reactor, socket, n, send_fn, append_fn);\n}\n\nssize_t Reactor::_writev(Reactor *reactor, Socket *socket, const iovec *iov, size_t iovcnt) {\n    if (socket->ssl) {\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, \"does not support SSL\");\n        return SW_ERR;\n    }\n\n    ssize_t send_bytes = 0;\n    size_t n = 0;\n    SW_LOOP_N(iovcnt) {\n        n += iov[i].iov_len;\n    }\n    auto send_fn = [&send_bytes, socket, iov, iovcnt]() -> ssize_t {\n        send_bytes = socket->writev(iov, iovcnt);\n        return send_bytes;\n    };\n    auto append_fn = [&send_bytes, iov, iovcnt](Buffer *buffer) {\n        ssize_t offset = send_bytes > 0 ? send_bytes : 0;\n        buffer->append(iov, iovcnt, offset);\n    };\n    return write_func(reactor, socket, n, send_fn, append_fn);\n}\n\nint Reactor::_writable_callback(Reactor *reactor, Event *ev) {\n    int ret;\n\n    Socket *socket = ev->socket;\n    Buffer *buffer = socket->out_buffer;\n\n    while (!Buffer::empty(buffer)) {\n        BufferChunk *chunk = buffer->front();\n        if (chunk->type == BufferChunk::TYPE_CLOSE) {\n            return reactor->close(reactor, ev->socket);\n        } else if (chunk->type == BufferChunk::TYPE_SENDFILE) {\n            ret = socket->handle_sendfile();\n        } else {\n            ret = socket->handle_send();\n        }\n\n        if (ret < 0) {\n            if (socket->close_wait) {\n                return reactor->trigger_close_event(ev);\n            } else if (socket->send_wait) {\n                return SW_OK;\n            }\n        }\n    }\n\n    if (socket->send_timer) {\n        swoole_timer_del(socket->send_timer);\n        socket->send_timer = nullptr;\n    }\n\n    // remove EPOLLOUT event\n    if (Buffer::empty(buffer)) {\n        reactor->remove_write_event(ev->socket);\n    }\n\n    return SW_OK;\n}\n\nvoid Reactor::drain_write_buffer(Socket *socket) {\n    Event event = {};\n    event.socket = socket;\n    event.fd = socket->fd;\n\n    while (!Buffer::empty(socket->out_buffer)) {\n        if (socket->wait_event(static_cast<int>(sec2msec(Socket::default_write_timeout)), SW_EVENT_WRITE) == SW_ERR) {\n            break;\n        }\n        _writable_callback(this, &event);\n        if (socket->close_wait || socket->removed) {\n            break;\n        }\n    }\n}\n\nvoid Reactor::add_destroy_callback(const Callback &cb, void *data) {\n    destroy_callbacks.append(cb, data);\n}\n\nvoid Reactor::set_end_callback(const EndCallback _id, const std::function<void(Reactor *)> &fn) {\n    end_callbacks[_id] = fn;\n}\n\nvoid Reactor::erase_end_callback(const EndCallback _id) {\n    end_callbacks.erase(_id);\n}\n\n/**\n * Returns false, the reactor cannot be exited, the next condition is skipped\n * Returns true, the reactor can exit and will continue to execute the next conditional function\n */\nvoid Reactor::set_exit_condition(const ExitCondition _id, const std::function<bool(Reactor *, size_t &)> &fn) {\n    exit_conditions[_id] = fn;\n}\n\nvoid Reactor::defer(const Callback &cb, void *data) {\n    if (defer_tasks == nullptr) {\n        defer_tasks = new CallbackManager;\n    }\n    defer_tasks->append(cb, data);\n}\n\nvoid Reactor::execute_end_callbacks(bool _timed_out) {\n    timed_out = _timed_out;\n    for (auto &kv : end_callbacks) {\n        kv.second(this);\n    }\n}\n\nvoid Reactor::execute_begin_callback() const {\n    if (future_task.callback) {\n        future_task.callback(future_task.data);\n    }\n}\n\nReactor::~Reactor() {\n    destroyed = true;\n    destroy_callbacks.execute();\n    delete impl;\n    if (swoole_isset_hook(SW_GLOBAL_HOOK_ON_REACTOR_DESTROY)) {\n        swoole_call_hook(SW_GLOBAL_HOOK_ON_REACTOR_DESTROY, this);\n    }\n}\n}  // namespace swoole\n"
  },
  {
    "path": "src/reactor/epoll.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_socket.h\"\n#include \"swoole_reactor.h\"\n\n#ifdef HAVE_EPOLL\n#include <sys/epoll.h>\n#ifndef EPOLLRDHUP\n#error \"require linux kernel version 2.6.32 or later\"\n#endif\n\n#ifndef EPOLLONESHOT\n#error \"require linux kernel version 2.6.32 or later\"\n#endif\n\nnamespace swoole {\n\nusing network::Socket;\n\nclass ReactorEpoll final : public ReactorImpl {\n    int epfd_;\n    epoll_event *events_ = nullptr;\n\n  public:\n    ReactorEpoll(Reactor *_reactor, int max_events);\n    ~ReactorEpoll() override;\n    bool ready() override;\n    int add(Socket *socket, int events) override;\n    int set(Socket *socket, int events) override;\n    int del(Socket *_socket) override;\n    int wait() override;\n\n    static int get_events(const int fd_type) {\n        int events = 0;\n        if (Reactor::isset_read_event(fd_type)) {\n            events |= EPOLLIN;\n        }\n        if (Reactor::isset_write_event(fd_type)) {\n            events |= EPOLLOUT;\n        }\n        if (fd_type & SW_EVENT_ONCE) {\n            events |= EPOLLONESHOT;\n        }\n        if (Reactor::isset_error_event(fd_type)) {\n            events |= (EPOLLRDHUP | EPOLLHUP | EPOLLERR);\n        }\n        return events;\n    }\n};\n\nReactorImpl *make_reactor_epoll(Reactor *_reactor, int max_events) {\n    return new ReactorEpoll(_reactor, max_events);\n}\n\nReactorEpoll::ReactorEpoll(Reactor *_reactor, int max_events) : ReactorImpl(_reactor) {\n    epfd_ = epoll_create(512);\n    if (!ReactorEpoll::ready()) {\n        swoole_sys_warning(\"epoll_create() failed\");\n        return;\n    }\n\n    events_ = new epoll_event[max_events];\n    reactor_->max_event_num = max_events;\n    reactor_->native_handle = epfd_;\n}\n\nbool ReactorEpoll::ready() {\n    return epfd_ >= 0;\n}\n\nReactorEpoll::~ReactorEpoll() {\n    if (epfd_ >= 0) {\n        close(epfd_);\n    }\n    delete[] events_;\n}\n\nint ReactorEpoll::add(Socket *socket, int events) {\n    epoll_event e;\n\n    e.events = get_events(events);\n    e.data.ptr = socket;\n\n    if (epoll_ctl(epfd_, EPOLL_CTL_ADD, socket->fd, &e) < 0) {\n        swoole_sys_warning(\"[Reactor#%d] epoll_ctl(epfd=%d, EPOLL_CTL_ADD, fd=%d, fd_type=%d, events=%d) failed\",\n                           reactor_->id,\n                           epfd_,\n                           socket->fd,\n                           socket->fd_type,\n                           events);\n        swoole_print_backtrace_on_error();\n        return SW_ERR;\n    }\n\n    reactor_->_add(socket, events);\n    swoole_trace_log(\n        SW_TRACE_EVENT, \"add events[fd=%d#%d, type=%d, events=%d]\", socket->fd, reactor_->id, socket->fd_type, events);\n\n    return SW_OK;\n}\n\nint ReactorEpoll::del(Socket *_socket) {\n    if (_socket->removed) {\n        swoole_error_log(\n            SW_LOG_WARNING,\n            SW_ERROR_EVENT_REMOVE_FAILED,\n            \"[Reactor#%d] failed to delete events[fd=%d, fd_type=%d], this socket has already been removed\",\n            reactor_->id,\n            _socket->fd,\n            _socket->fd_type);\n        swoole_print_backtrace_on_error();\n        return SW_ERR;\n    }\n\n    if (epoll_ctl(epfd_, EPOLL_CTL_DEL, _socket->fd, nullptr) < 0) {\n        after_removal_failure(_socket);\n        /**\n         * Before removing it from the epoll event loop, the close operation has be executed,\n         * must cleanup related resources with this socket.\n         */\n        if (errno != EBADF && errno != ENOENT) {\n            swoole_sys_warning(\"[Reactor#%d] epoll_ctl(epfd=%d, EPOLL_CTL_DEL, fd=%d, fd_type=%d) failed\",\n                               reactor_->id,\n                               epfd_,\n                               _socket->fd,\n                               _socket->fd_type);\n            swoole_print_backtrace_on_error();\n            return SW_ERR;\n        }\n    }\n\n    swoole_trace_log(\n        SW_TRACE_REACTOR, \"remove event[reactor_id=%d|fd=%d|type=%d]\", reactor_->id, _socket->fd, _socket->fd_type);\n    reactor_->_del(_socket);\n\n    return SW_OK;\n}\n\nint ReactorEpoll::set(Socket *socket, int events) {\n    epoll_event e;\n\n    e.events = get_events(events);\n    e.data.ptr = socket;\n\n    int ret = epoll_ctl(epfd_, EPOLL_CTL_MOD, socket->fd, &e);\n    if (ret < 0) {\n        swoole_sys_warning(\"[Reactor#%d] epoll_ctl(epfd=%d, EPOLL_CTL_MOD, fd=%d, fd_type=%d, events=%d) failed\",\n                           reactor_->id,\n                           epfd_,\n                           socket->fd,\n                           socket->fd_type,\n                           events);\n        swoole_print_backtrace_on_error();\n        return SW_ERR;\n    }\n\n    swoole_trace_log(SW_TRACE_EVENT, \"set event[reactor_id=%d, fd=%d, events=%d]\", reactor_->id, socket->fd, events);\n    reactor_->_set(socket, events);\n\n    return SW_OK;\n}\n\nint ReactorEpoll::wait() {\n    Event event;\n    ReactorHandler handler;\n    int n, ret;\n\n    reactor_->before_wait();\n\n    while (reactor_->running) {\n        reactor_->execute_begin_callback();\n\n        n = epoll_wait(epfd_, events_, reactor_->max_event_num, reactor_->get_timeout_msec());\n        if (n < 0) {\n            if (!reactor_->catch_error()) {\n                swoole_sys_warning(\"[Reactor#%d] epoll_wait(epfd=%d, max_events=%d, timeout=%d) failed\",\n                                   reactor_->id,\n                                   epfd_,\n                                   reactor_->max_event_num,\n                                   reactor_->get_timeout_msec());\n                return SW_ERR;\n            } else {\n#ifdef SW_USE_IOURING\n                if (sw_likely(errno == EINTR && reactor_->iouring_interrupt_handler)) {\n                    reactor_->iouring_interrupt_handler(reactor_);\n                }\n#endif\n                goto _continue;\n            }\n        } else if (n == 0) {\n            reactor_->execute_end_callbacks(true);\n            SW_REACTOR_CONTINUE;\n        }\n        for (int i = 0; i < n; i++) {\n            event.reactor_id = reactor_->id;\n            event.socket = static_cast<Socket *>(events_[i].data.ptr);\n            event.type = event.socket->fd_type;\n            event.fd = event.socket->fd;\n\n            if (events_[i].events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP)) {\n                event.socket->event_hup = 1;\n            }\n            // read\n            if ((events_[i].events & EPOLLIN) && !event.socket->removed) {\n                handler = reactor_->get_handler(event.type, SW_EVENT_READ);\n                ret = handler(reactor_, &event);\n                if (ret < 0) {\n                    swoole_sys_warning(\"EPOLLIN handle failed [fd=%d, type=%d]\", event.fd, event.type);\n                }\n            }\n            // write\n            if ((events_[i].events & EPOLLOUT) && !event.socket->removed) {\n                handler = reactor_->get_handler(event.type, SW_EVENT_WRITE);\n                ret = handler(reactor_, &event);\n                if (ret < 0) {\n                    swoole_sys_warning(\"EPOLLOUT handle failed [fd=%d, type=%d]\", event.fd, event.type);\n                }\n            }\n            // error\n            if ((events_[i].events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP)) && !event.socket->removed) {\n                // ignore ERR and HUP, because event is already processed at IN and OUT handler.\n                if ((events_[i].events & EPOLLIN) || (events_[i].events & EPOLLOUT)) {\n                    continue;\n                }\n                handler = reactor_->get_error_handler(event.type);\n                ret = handler(reactor_, &event);\n                if (ret < 0) {\n                    swoole_sys_warning(\"EPOLLERR handle failed [fd=%d, type=%d]\", event.fd, event.type);\n                }\n            }\n            if (!event.socket->removed && (event.socket->events & SW_EVENT_ONCE)) {\n                reactor_->_del(event.socket);\n            }\n        }\n\n    _continue:\n        reactor_->execute_end_callbacks(false);\n        SW_REACTOR_CONTINUE;\n    }\n    return 0;\n}\n}  // namespace swoole\n#endif\n"
  },
  {
    "path": "src/reactor/kqueue.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_socket.h\"\n#include \"swoole_reactor.h\"\n#include \"swoole_signal.h\"\n\n#ifdef HAVE_KQUEUE\n\n#ifdef USE_KQUEUE_IDE_HELPER\n#include \"helper/kqueue.h\"\n#else\n#include <sys/event.h>\n#endif\n#ifdef __NetBSD__\n#include <sys/param.h>\n#endif\n\nnamespace swoole {\nusing network::Socket;\n\nclass ReactorKqueue : public ReactorImpl {\n    int epfd_;\n    int event_max_;\n    struct kevent *events_;\n\n    bool fetch_event(Event *event, void *udata) {\n        event->socket = (Socket *) udata;\n        event->fd = event->socket->fd;\n        event->type = event->socket->fd_type;\n        event->reactor_id = reactor_->id;\n\n        if (event->socket->removed) {\n            return false;\n        }\n        return true;\n    }\n\n    void del_once_socket(Socket *socket) {\n        if ((socket->events & SW_EVENT_ONCE) && !socket->removed) {\n            del(socket);\n        }\n    }\n\n  public:\n    ReactorKqueue(Reactor *reactor, int max_events);\n    ~ReactorKqueue();\n    bool ready() override;\n    int add(Socket *socket, int events) override;\n    int set(Socket *socket, int events) override;\n    int del(Socket *socket) override;\n    int wait() override;\n};\n\nReactorImpl *make_reactor_kqueue(Reactor *_reactor, int max_events) {\n    return new ReactorKqueue(_reactor, max_events);\n}\n\nReactorKqueue::ReactorKqueue(Reactor *reactor, int max_events) : ReactorImpl(reactor) {\n    epfd_ = kqueue();\n    if (epfd_ < 0) {\n        swoole_sys_warning(\"kqueue() failed\");\n        return;\n    }\n\n    reactor_->max_event_num = max_events;\n    reactor_->native_handle = epfd_;\n    event_max_ = max_events;\n    events_ = new struct kevent[max_events];\n}\n\nbool ReactorKqueue::ready() {\n    return epfd_ >= 0;\n}\n\nReactorKqueue::~ReactorKqueue() {\n    if (epfd_ >= 0) {\n        close(epfd_);\n    }\n    delete[] events_;\n}\n\nint ReactorKqueue::add(Socket *socket, int events) {\n    struct kevent e;\n    int ret;\n\n    int fd = socket->fd;\n    int fflags = 0;\n\n#if !defined(__NetBSD__) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000)\n    auto sobj = socket;\n#else\n    auto sobj = reinterpret_cast<intptr_t>(socket);\n#endif\n\n    if (Reactor::isset_read_event(events)) {\n#ifdef NOTE_EOF\n        fflags = NOTE_EOF;\n#endif\n        EV_SET(&e, fd, EVFILT_READ, EV_ADD, fflags, 0, sobj);\n        ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr);\n        if (ret < 0) {\n            swoole_error_log(SW_LOG_WARNING,\n                             SW_ERROR_EVENT_ADD_FAILED,\n                             \"add events[fd=%d, reactor_id=%d, type=%d, events=read] failed\",\n                             fd,\n                             reactor_->id,\n                             socket->fd_type);\n            swoole_print_backtrace_on_error();\n            return SW_ERR;\n        }\n    }\n\n    if (Reactor::isset_write_event(events)) {\n        EV_SET(&e, fd, EVFILT_WRITE, EV_ADD, 0, 0, sobj);\n        ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr);\n        if (ret < 0) {\n            swoole_error_log(SW_LOG_WARNING,\n                             SW_ERROR_EVENT_ADD_FAILED,\n                             \"add events[fd=%d, reactor_id=%d, type=%d, events=write] failed\",\n                             fd,\n                             reactor_->id,\n                             socket->fd_type);\n            swoole_print_backtrace_on_error();\n            return SW_ERR;\n        }\n    }\n\n    reactor_->_add(socket, events);\n    swoole_trace_log(SW_TRACE_EVENT, \"[THREAD #%d]epfd=%d, fd=%d, events=%d\", SwooleTG.id, epfd_, fd, socket->events);\n\n    return SW_OK;\n}\n\nint ReactorKqueue::set(Socket *socket, int events) {\n    struct kevent e;\n    int ret;\n\n    int fd = socket->fd;\n    int fflags = 0;\n\n#if !defined(__NetBSD__) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000)\n    auto sobj = socket;\n#else\n    auto sobj = reinterpret_cast<intptr_t>(socket);\n#endif\n\n    if (Reactor::isset_read_event(events)) {\n#ifdef NOTE_EOF\n        fflags = NOTE_EOF;\n#endif\n        EV_SET(&e, fd, EVFILT_READ, EV_ADD, fflags, 0, sobj);\n        ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr);\n        if (ret < 0) {\n            swoole_error_log(SW_LOG_WARNING, SW_ERROR_EVENT_UPDATE_FAILED, \"kqueue->set(%d, SW_EVENT_READ) failed\", fd);\n            swoole_print_backtrace_on_error();\n            return SW_ERR;\n        }\n    } else {\n        EV_SET(&e, fd, EVFILT_READ, EV_DELETE, 0, 0, sobj);\n        ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr);\n        if (ret < 0) {\n            swoole_error_log(SW_LOG_WARNING, SW_ERROR_EVENT_REMOVE_FAILED, \"kqueue->del(%d, SW_EVENT_READ) failed\", fd);\n            swoole_print_backtrace_on_error();\n            return SW_ERR;\n        }\n    }\n\n    if (Reactor::isset_write_event(events)) {\n        EV_SET(&e, fd, EVFILT_WRITE, EV_ADD, 0, 0, sobj);\n        ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr);\n        if (ret < 0) {\n            swoole_error_log(\n                SW_LOG_WARNING, SW_ERROR_EVENT_UPDATE_FAILED, \"kqueue->set(%d, SW_EVENT_WRITE) failed\", fd);\n            swoole_print_backtrace_on_error();\n            return SW_ERR;\n        }\n    } else {\n        EV_SET(&e, fd, EVFILT_WRITE, EV_DELETE, 0, 0, sobj);\n        ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr);\n        if (ret < 0) {\n            swoole_error_log(\n                SW_LOG_WARNING, SW_ERROR_EVENT_REMOVE_FAILED, \"kqueue->del(%d, SW_EVENT_WRITE) failed\", fd);\n            swoole_print_backtrace_on_error();\n            return SW_ERR;\n        }\n    }\n\n    reactor_->_set(socket, events);\n    swoole_trace_log(SW_TRACE_EVENT, \"[THREAD #%d]epfd=%d, fd=%d, events=%d\", SwooleTG.id, epfd_, fd, socket->events);\n\n    return SW_OK;\n}\n\nint ReactorKqueue::del(Socket *socket) {\n    struct kevent e;\n    int ret;\n    int fd = socket->fd;\n\n#if !defined(__NetBSD__) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000)\n    auto sobj = socket;\n#else\n    auto sobj = reinterpret_cast<intptr_t>(socket);\n#endif\n\n    if (socket->removed) {\n        swoole_error_log(\n            SW_LOG_WARNING, SW_ERROR_EVENT_REMOVE_FAILED, \"failed to delete event[%d], has been removed\", socket->fd);\n        swoole_print_backtrace_on_error();\n        return SW_ERR;\n    }\n\n    if (socket->events & SW_EVENT_READ) {\n        EV_SET(&e, fd, EVFILT_READ, EV_DELETE, 0, 0, sobj);\n        ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr);\n        if (ret < 0) {\n            after_removal_failure(socket);\n            if (errno != EBADF && errno != ENOENT) {\n                return SW_ERR;\n            }\n        }\n    }\n\n    if (socket->events & SW_EVENT_WRITE) {\n        EV_SET(&e, fd, EVFILT_WRITE, EV_DELETE, 0, 0, sobj);\n        ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr);\n        if (ret < 0) {\n            after_removal_failure(socket);\n            if (errno != EBADF && errno != ENOENT) {\n                return SW_ERR;\n            }\n        }\n    }\n\n    reactor_->_del(socket);\n    swoole_trace_log(SW_TRACE_EVENT, \"[THREAD #%d]epfd=%d, fd=%d\", SwooleTG.id, epfd_, fd);\n\n    return SW_OK;\n}\n\nint ReactorKqueue::wait() {\n    Event event;\n    ReactorHandler handler;\n\n    int i, n;\n    struct timespec t = {};\n    struct timespec *t_ptr;\n\n    reactor_->before_wait();\n\n    while (reactor_->running) {\n        reactor_->execute_begin_callback();\n\n        if (reactor_->timeout_msec > 0) {\n            t.tv_sec = reactor_->timeout_msec / 1000;\n            t.tv_nsec = (reactor_->timeout_msec - t.tv_sec * 1000) * 1000 * 1000;\n            t_ptr = &t;\n        } else if (reactor_->defer_tasks) {\n            t.tv_sec = 0;\n            t.tv_nsec = 0;\n            t_ptr = &t;\n        } else {\n            t_ptr = nullptr;\n        }\n\n        n = ::kevent(epfd_, nullptr, 0, events_, event_max_, t_ptr);\n        if (n < 0) {\n            if (!reactor_->catch_error()) {\n                swoole_sys_warning(\"kevent(epfd=%d) failed \", epfd_);\n                return SW_ERR;\n            } else {\n                goto _continue;\n            }\n        } else if (n == 0) {\n            reactor_->execute_end_callbacks(true);\n            SW_REACTOR_CONTINUE;\n        }\n\n        swoole_trace_log(SW_TRACE_EVENT, \"n %d events\", n);\n\n        for (i = 0; i < n; i++) {\n            struct kevent *kevent = &events_[i];\n            void *udata = (void *) kevent->udata;\n            if (!udata) {\n                continue;\n            }\n            switch (kevent->filter) {\n            case EVFILT_READ:\n            case EVFILT_WRITE: {\n                if (fetch_event(&event, udata)) {\n                    handler = reactor_->get_handler(event.type,\n                                                    kevent->filter == EVFILT_READ ? SW_EVENT_READ : SW_EVENT_WRITE);\n                    if (sw_unlikely(handler(reactor_, &event) < 0)) {\n                        swoole_sys_warning(\"kqueue event %s socket#%d handler failed\",\n                                           kevent->filter == EVFILT_READ ? \"read\" : \"write\",\n                                           event.fd);\n                        swoole_print_backtrace_on_error();\n                    }\n                    del_once_socket(event.socket);\n                }\n                break;\n            }\n            case EVFILT_SIGNAL: {\n                Signal *signal_data = (Signal *) udata;\n                if (signal_data->activated) {\n                    if (signal_data->handler) {\n                        if (sw_likely(signal_data->handler != SIG_IGN)) {\n                            signal_data->handler(signal_data->signo);\n                        }\n                    } else {\n                        swoole_error_log(SW_LOG_WARNING,\n                                         SW_ERROR_UNREGISTERED_SIGNAL,\n                                         SW_UNREGISTERED_SIGNAL_FMT,\n                                         swoole_signal_to_str(signal_data->signo));\n                    }\n                }\n                break;\n            }\n            default:\n                swoole_error_log(\n                    SW_LOG_WARNING, SW_ERROR_EVENT_UNKNOWN_DATA, \"unknown event filter[%d]\", kevent->filter);\n                break;\n            }\n        }\n\n    _continue:\n        reactor_->execute_end_callbacks(false);\n        SW_REACTOR_CONTINUE;\n    }\n    return 0;\n}\n}  // namespace swoole\n#endif\n"
  },
  {
    "path": "src/reactor/poll.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_socket.h\"\n#include \"swoole_reactor.h\"\n\nnamespace swoole {\nusing network::Socket;\n\nclass ReactorPoll final : public ReactorImpl {\n    pollfd *events_;\n    int max_events_;\n    int set_events() const;\n\n  public:\n    ReactorPoll(Reactor *_reactor, int max_events);\n    ~ReactorPoll() override;\n    bool ready() override {\n        return true;\n    };\n    int add(Socket *socket, int events) override;\n    int set(Socket *socket, int events) override;\n    int del(Socket *socket) override;\n    int wait() override;\n};\n\nReactorImpl *make_reactor_poll(Reactor *_reactor, int max_events) {\n    return new ReactorPoll(_reactor, max_events);\n}\n\nReactorPoll::ReactorPoll(Reactor *_reactor, int max_events) : ReactorImpl(_reactor) {\n    events_ = new pollfd[max_events];\n    max_events_ = max_events;\n    reactor_->max_event_num = max_events;\n}\n\nReactorPoll::~ReactorPoll() {\n    delete[] events_;\n}\n\nint ReactorPoll::set_events() const {\n    const auto sockets = reactor_->get_sockets();\n    int count = 0;\n    for (const auto pair : sockets) {\n        const auto _socket = pair.second;\n        events_[count].fd = _socket->fd;\n        events_[count].events = translate_events_to_poll(_socket->events);\n        events_[count].revents = 0;\n        count++;\n    }\n    return count;\n}\n\nint ReactorPoll::add(Socket *socket, const int events) {\n    if (reactor_->_exists(socket)) {\n        swoole_error_log(\n            SW_LOG_WARNING,\n            SW_ERROR_EVENT_ADD_FAILED,\n            \"[Reactor#%d] failed to add events[fd=%d, fd_type=%d, events=%d], the socket#%d is already exists\",\n            reactor_->id,\n            socket->fd,\n            socket->fd_type,\n            events,\n            socket->fd);\n        swoole_print_backtrace_on_error();\n        return SW_ERR;\n    }\n\n    if (reactor_->get_event_num() == static_cast<size_t>(max_events_)) {\n        swoole_error_log(\n            SW_LOG_WARNING, SW_ERROR_EVENT_ADD_FAILED, \"too many sockets, the max events is %d\", max_events_);\n        swoole_print_backtrace_on_error();\n        return SW_ERR;\n    }\n\n    swoole_trace(\"fd=%d, events=%d\", socket->fd, events);\n    reactor_->_add(socket, events);\n\n    return SW_OK;\n}\n\nint ReactorPoll::set(Socket *socket, int events) {\n    if (!reactor_->_exists(socket)) {\n        swoole_error_log(\n            SW_LOG_WARNING,\n            SW_ERROR_SOCKET_NOT_EXISTS,\n            \"[Reactor#%d] failed to set events[fd=%d, fd_type=%d, events=%d], the socket#%d has already been removed\",\n            reactor_->id,\n            socket->fd,\n            socket->fd_type,\n            events,\n            socket->fd);\n        swoole_print_backtrace_on_error();\n        return SW_ERR;\n    }\n\n    swoole_trace(\"fd=%d, events=%d\", socket->fd, events);\n    reactor_->_set(socket, events);\n\n    return SW_OK;\n}\n\nint ReactorPoll::del(Socket *socket) {\n    if (socket->removed) {\n        swoole_error_log(\n            SW_LOG_WARNING,\n            SW_ERROR_SOCKET_NOT_EXISTS,\n            \"[Reactor#%d] failed to delete events[fd=%d, fd_type=%d], the socket#%d has already been removed\",\n            reactor_->id,\n            socket->fd,\n            socket->fd_type,\n            socket->fd);\n        swoole_print_backtrace_on_error();\n        return SW_ERR;\n    }\n\n    if (!reactor_->_exists(socket)) {\n        swoole_error_log(SW_LOG_WARNING,\n                         SW_ERROR_SOCKET_NOT_EXISTS,\n                         \"[Reactor#%d] failed to delete events[fd=%d, fd_type=%d], the socket#%d is not exists\",\n                         reactor_->id,\n                         socket->fd,\n                         socket->fd_type,\n                         socket->fd);\n        swoole_print_backtrace_on_error();\n        return SW_ERR;\n    }\n    swoole_trace(\"fd=%d\", socket->fd);\n    reactor_->_del(socket);\n    return SW_OK;\n}\n\nint ReactorPoll::wait() {\n    Event event;\n    ReactorHandler handler;\n\n    reactor_->before_wait();\n\n    while (reactor_->running) {\n        reactor_->execute_begin_callback();\n        const int event_num = set_events();\n        int ret = poll(events_, event_num, reactor_->get_timeout_msec());\n        if (ret < 0) {\n            if (!reactor_->catch_error()) {\n                swoole_sys_warning(\"[Reactor#%d] poll(nfds=%d, timeout=%d) failed\",\n                                   reactor_->id,\n                                   event_num,\n                                   reactor_->get_timeout_msec());\n                break;\n            }\n        } else if (ret == 0) {\n            reactor_->execute_end_callbacks(true);\n            SW_REACTOR_CONTINUE;\n        } else {\n            for (int i = 0; i < event_num; i++) {\n                /**\n                 * A revents value of 0 indicates that no events have occurred on this socket,\n                 * so the next file descriptor should be checked;\n                 * likewise, if the file descriptor has already been removed, it should be skipped.\n                 * It is essential to check whether this file descriptor exists in the reactor,\n                 * as the event handler may remove a file descriptor with pending but already triggered readable or\n                 * writable events that have not yet been processed.\n                 */\n                if (events_[i].revents == 0 || !reactor_->exists(events_[i].fd)) {\n                    continue;\n                }\n\n                event.socket = reactor_->get_socket(events_[i].fd);\n                event.fd = events_[i].fd;\n                event.reactor_id = reactor_->id;\n                event.type = event.socket->fd_type;\n\n                if (events_[i].revents & (POLLHUP | POLLERR)) {\n                    event.socket->event_hup = 1;\n                }\n                swoole_trace(\"Event: fd=%d|reactor_id=%d|type=%d\", event.fd, reactor_->id, event.type);\n                // in\n                if ((events_[i].revents & POLLIN) && !event.socket->removed) {\n                    handler = reactor_->get_handler(event.type, SW_EVENT_READ);\n                    ret = handler(reactor_, &event);\n                    if (ret < 0) {\n                        swoole_sys_warning(\"POLLIN handle failed. fd=%d\", event.fd);\n                        swoole_print_backtrace_on_error();\n                    }\n                }\n                // out\n                if ((events_[i].revents & POLLOUT) && !event.socket->removed) {\n                    handler = reactor_->get_handler(event.type, SW_EVENT_WRITE);\n                    ret = handler(reactor_, &event);\n                    if (ret < 0) {\n                        swoole_sys_warning(\"POLLOUT handle failed. fd=%d\", event.fd);\n                        swoole_print_backtrace_on_error();\n                    }\n                }\n                // error\n                if ((events_[i].revents & (POLLHUP | POLLERR)) && !event.socket->removed) {\n                    // ignore ERR and HUP, because event is already processed at IN and OUT handler.\n                    if ((events_[i].revents & POLLIN) || (events_[i].revents & POLLOUT)) {\n                        continue;\n                    }\n                    handler = reactor_->get_error_handler(event.type);\n                    ret = handler(reactor_, &event);\n                    if (ret < 0) {\n                        swoole_sys_warning(\"POLLERR handle failed. fd=%d\", event.fd);\n                        swoole_print_backtrace_on_error();\n                    }\n                }\n                if (!event.socket->removed && (event.socket->events & SW_EVENT_ONCE)) {\n                    del(event.socket);\n                }\n            }\n        }\n        reactor_->execute_end_callbacks(false);\n        SW_REACTOR_CONTINUE;\n    }\n    return SW_OK;\n}\n\nint16_t translate_events_to_poll(int events) {\n    int16_t poll_events = 0;\n\n    if (events & SW_EVENT_READ) {\n        poll_events |= POLLIN;\n    }\n    if (events & SW_EVENT_WRITE) {\n        poll_events |= POLLOUT;\n    }\n\n    return poll_events;\n}\n\nint translate_events_from_poll(int16_t events) {\n    int sw_events = 0;\n\n    if (events & POLLIN) {\n        sw_events |= SW_EVENT_READ;\n    }\n    if (events & POLLOUT) {\n        sw_events |= SW_EVENT_WRITE;\n    }\n    // ignore ERR and HUP, because event is already processed at IN and OUT handler.\n    if ((events & POLLERR || events & POLLHUP) && !(events & POLLIN || events & POLLOUT)) {\n        sw_events |= SW_EVENT_ERROR;\n    }\n\n    return sw_events;\n}\n}  // namespace swoole\n"
  },
  {
    "path": "src/server/base.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_server.h\"\n\nnamespace swoole {\n\nFactory *Server::create_base_factory() {\n    reactor_num = worker_num;\n    connection_list = static_cast<Connection *>(sw_calloc(max_connection, sizeof(Connection)));\n    if (connection_list == nullptr) {\n        swoole_sys_warning(\"calloc[2](%zu) failed\", max_connection * sizeof(Connection));\n        return nullptr;\n    }\n    gs->connection_nums = static_cast<sw_atomic_t *>(sw_shm_calloc(worker_num, sizeof(sw_atomic_t)));\n    if (gs->connection_nums == nullptr) {\n        swoole_sys_warning(\"sw_shm_calloc(%ld) for gs->connection_nums failed\", worker_num * sizeof(sw_atomic_t));\n        return nullptr;\n    }\n\n    for (const auto port : ports) {\n        port->gs->connection_nums = static_cast<sw_atomic_t *>(sw_shm_calloc(worker_num, sizeof(sw_atomic_t)));\n        if (port->gs->connection_nums == nullptr) {\n            swoole_sys_warning(\"sw_shm_calloc(%ld) for port->connection_nums failed\", worker_num * sizeof(sw_atomic_t));\n            return nullptr;\n        }\n    }\n\n    return new BaseFactory(this);\n}\n\nvoid Server::destroy_base_factory() const {\n    sw_free(connection_list);\n    sw_shm_free((void *) gs->connection_nums);\n    for (auto port : ports) {\n        sw_shm_free((void *) port->gs->connection_nums);\n    }\n    gs->connection_nums = nullptr;\n}\n\nBaseFactory::BaseFactory(Server *server) : Factory(server) {}\n\nBaseFactory::~BaseFactory() = default;\n\nbool BaseFactory::start() {\n    return true;\n}\n\nbool BaseFactory::shutdown() {\n    return true;\n}\n\nbool BaseFactory::dispatch(SendData *task) {\n    Connection *conn = nullptr;\n\n    if (Server::is_stream_event(task->info.type)) {\n        conn = server_->get_connection(task->info.fd);\n        if (conn == nullptr || conn->active == 0) {\n            swoole_warning(\"dispatch[type=%d] failed, socket#%ld is not active\", task->info.type, task->info.fd);\n            return false;\n        }\n        // server active close, discard data.\n        if (conn->closed) {\n            swoole_warning(\"dispatch[type=%d] failed, socket#%ld is closed by server\", task->info.type, task->info.fd);\n            return false;\n        }\n        // converted fd to session_id\n        task->info.fd = conn->session_id;\n        task->info.server_fd = conn->server_fd;\n    }\n\n    if (task->info.len > 0) {\n        if (conn && conn->socket->recv_buffer && task->data == conn->socket->recv_buffer->str &&\n            conn->socket->recv_buffer->offset > 0 &&\n            conn->socket->recv_buffer->length == (size_t) conn->socket->recv_buffer->offset) {\n            task->info.flags |= SW_EVENT_DATA_POP_PTR;\n        }\n    }\n\n    auto bus = server_->get_worker_message_bus();\n    bus->pass(task);\n    server_->worker_accept_event(&bus->get_buffer()->info);\n\n    return true;\n}\n\n/**\n * only stream fd\n */\nbool BaseFactory::notify(DataHead *info) {\n    const auto conn = server_->get_connection(info->fd);\n    if (conn == nullptr || conn->active == 0) {\n        swoole_warning(\"dispatch[type=%d] failed, socket#%ld is not active\", info->type, info->fd);\n        return false;\n    }\n    // server active close, discard data.\n    if (conn->closed) {\n        swoole_warning(\"dispatch[type=%d] failed, session#%ld is closed by server\", info->type, conn->session_id);\n        return false;\n    }\n    // converted fd to session_id\n    info->fd = conn->session_id;\n    info->server_fd = conn->server_fd;\n    info->flags = SW_EVENT_DATA_NORMAL;\n\n    server_->worker_accept_event(info);\n\n    return true;\n}\n\nbool BaseFactory::end(SessionId session_id, int flags) {\n    SendData _send{};\n    _send.info.fd = session_id;\n    _send.info.len = 0;\n    _send.info.type = SW_SERVER_EVENT_CLOSE;\n    _send.info.reactor_id = swoole_get_worker_id();\n\n    Session *session = server_->get_session(session_id);\n    if (!session->fd) {\n        swoole_error_log(SW_LOG_TRACE,\n                         SW_ERROR_SESSION_NOT_EXIST,\n                         \"failed to close connection, session#%ld does not exist\",\n                         session_id);\n        return false;\n    }\n\n    if (server_->if_forward_message(session)) {\n        swoole_trace_log(SW_TRACE_SERVER,\n                         \"session_id=%ld, fd=%d, session->reactor_id=%d\",\n                         session_id,\n                         session->fd,\n                         session->reactor_id);\n        _send.info.type = SW_SERVER_EVENT_CLOSE_FORWARD;\n        return forward_message(session, &_send);\n    }\n\n    Connection *conn = server_->get_connection_verify_no_ssl(session_id);\n    if (conn == nullptr) {\n        swoole_set_last_error(SW_ERROR_SESSION_NOT_EXIST);\n        return false;\n    }\n    // Reset send buffer, Immediately close the connection.\n    if (flags & Server::CLOSE_RESET) {\n        conn->close_reset = 1;\n    }\n    // Server is initiative to close the connection\n    if (flags & Server::CLOSE_ACTIVELY) {\n        conn->close_actively = 1;\n    }\n\n    if (conn->closing) {\n        swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSING, \"session#%ld is closing\", session_id);\n        return false;\n    } else if (!(conn->close_force || conn->close_reset) && conn->closed) {\n        swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_CLOSED, \"session#%ld is closed\", session_id);\n        return false;\n    }\n\n    conn->closing = 1;\n    if (server_->onClose != nullptr && !conn->closed) {\n        DataHead info{};\n        info.fd = session_id;\n        if (conn->close_actively) {\n            info.reactor_id = -1;\n        } else {\n            info.reactor_id = conn->reactor_id;\n        }\n        info.server_fd = conn->server_fd;\n        server_->onClose(server_, &info);\n    }\n    conn->closing = 0;\n    conn->closed = 1;\n    conn->close_errno = 0;\n    network::Socket *_socket = conn->socket;\n\n    if (_socket == nullptr) {\n        swoole_warning(\"session#%ld->socket is nullptr\", session_id);\n        return false;\n    }\n\n    if (Buffer::empty(_socket->out_buffer) || (conn->close_reset || conn->peer_closed || conn->close_force)) {\n        Reactor *reactor = SwooleTG.reactor;\n        return Server::close_connection(reactor, _socket) == SW_OK;\n    } else {\n        _socket->out_buffer->alloc(BufferChunk::TYPE_CLOSE, 0);\n        conn->close_queued = 1;\n        return true;\n    }\n}\n\nbool BaseFactory::finish(SendData *data) {\n    SessionId session_id = data->info.fd;\n\n    Session *session = server_->get_session(session_id);\n    if (server_->if_forward_message(session)) {\n        swoole_trace_log(SW_TRACE_SERVER,\n                         \"session_id=%ld, fd=%d, session->reactor_id=%d\",\n                         session_id,\n                         session->fd,\n                         session->reactor_id);\n\n        if (data->info.type == SW_SERVER_EVENT_SEND_DATA || data->info.type == SW_SERVER_EVENT_SEND_FILE) {\n            return forward_message(session, data);\n        } else {\n            swoole_warning(\"unknown event type[%d]\", data->info.type);\n            return false;\n        }\n    } else {\n        return server_->send_to_connection(data) == SW_OK;\n    }\n}\n\nbool BaseFactory::forward_message(const Session *session, SendData *data) const {\n    const Worker *worker = server_->get_event_worker_pool()->get_worker(session->reactor_id);\n    swoole_trace_log(SW_TRACE_SERVER,\n                     \"fd=%d, worker_id=%d, type=%d, len=%u\",\n                     worker->pipe_master->get_fd(),\n                     session->reactor_id,\n                     data->info.type,\n                     data->info.len);\n\n    auto mb = server_->get_worker_message_bus();\n    auto sock = server_->is_thread_mode() ? mb->get_pipe_socket(worker->pipe_master) : worker->pipe_master;\n    if (!mb->write(sock, data)) {\n        swoole_sys_warning(\"failed to send %u bytes to pipe_master\", data->info.len);\n        return false;\n    }\n    return true;\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "src/server/manager.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_server.h\"\n#include \"swoole_util.h\"\n\n#include <unordered_map>\n#include <vector>\n\nnamespace swoole {\n/**\n * The functionality of the Manager class is similar to that of the ProcessPool,\n * but due to the more complex management requirements for processes in the Server module, the ProcessPool falls short.\n * Therefore, we add a new class, and the Manager class should reuse the ProcessPool code as much as possible.\n */\nstruct Manager {\n    bool reload_all_worker;\n    bool reload_task_worker;\n    bool force_kill;\n    Server *server_;\n\n    std::vector<pid_t> kill_workers;\n\n    void wait(Server *_server);\n    void terminate_all_worker();\n    void reopen_logger() const;\n\n    static void signal_handler(int sig);\n    static void timer_callback(Timer *timer, TimerNode *tnode);\n};\n\nvoid Manager::timer_callback(Timer *timer, TimerNode *tnode) {\n    auto *serv = static_cast<Server *>(tnode->data);\n    if (serv->isset_hook(Server::HOOK_MANAGER_TIMER)) {\n        serv->call_hook(Server::HOOK_MANAGER_TIMER, serv);\n    }\n}\n\nint Server::start_manager_process() {\n    if (!create_event_workers()) {\n        return SW_ERR;\n    }\n\n    if (get_event_worker_pool()->create_message_box(SW_MESSAGE_BOX_SIZE) == SW_ERR) {\n        return SW_ERR;\n    }\n\n    if (get_user_worker_num() > 0 && !create_user_workers()) {\n        return SW_ERR;\n    }\n\n    auto fn = [this]() {\n        gs->manager_pid = getpid();\n\n        if (task_worker_num > 0) {\n            if (get_task_worker_pool()->start() == SW_ERR) {\n                swoole_sys_error(\"failed to start task worker\");\n                return;\n            }\n        }\n\n        /*\n         * Must be set after ProcessPool:start(),\n         * the default ProcessPool will set type of the main process as SW_MASTER,\n         * while in server mode it should be SW_MANAGER\n         */\n        swoole_set_worker_type(SW_MANAGER);\n\n        SW_LOOP_N(worker_num) {\n            Worker *worker = get_worker(i);\n            if (factory_->spawn_event_worker(worker) < 0) {\n                swoole_sys_error(\"failed to fork event worker\");\n                return;\n            }\n        }\n\n        if (!user_worker_list.empty()) {\n            for (auto worker : user_worker_list) {\n                if (factory_->spawn_user_worker(worker) < 0) {\n                    swoole_sys_error(\"failed to fork user worker\");\n                    return;\n                }\n            }\n        }\n\n        Manager manager{};\n        manager.wait(this);\n    };\n\n    if (is_base_mode()) {\n        fn();\n    } else {\n        if (swoole_fork_exec(fn) < 0) {\n            swoole_sys_warning(\"failed fork manager process\");\n            return SW_ERR;\n        }\n    }\n    return SW_OK;\n}\n\nvoid Manager::wait(Server *_server) {\n    server_ = _server;\n    server_->manager_ = this;\n\n    ProcessPool *pool = server_->get_event_worker_pool();\n    ProcessPool *task_pool = server_->get_task_worker_pool();\n    pool->onWorkerMessage = Server::read_worker_message;\n    pool->max_wait_time = server_->max_wait_time;\n    _server->gs->manager_pid = pool->master_pid = task_pool->master_pid = getpid();\n\n    swoole_event_free();\n\n    swoole_signal_set(SIGHUP, nullptr);\n    swoole_signal_set(SIGCHLD, signal_handler);   // child process exit\n    swoole_signal_set(SIGTERM, signal_handler);   // shutdown\n    swoole_signal_set(SIGUSR1, signal_handler);   // reload all workers\n    swoole_signal_set(SIGUSR2, signal_handler);   // reload task workers\n    swoole_signal_set(SIGIO, signal_handler);     // read message\n    swoole_signal_set(SIGALRM, signal_handler);   // timer\n    swoole_signal_set(SIGWINCH, signal_handler);  // reopen log file\n#ifdef SIGRTMIN\n    swoole_signal_set(SIGRTMIN, signal_handler);\n#endif\n\n    if (_server->is_process_mode()) {\n        swoole_set_process_death_signal(SIGTERM);\n\n        _server->gs->manager_barrier.wait();\n\n        if (_server->reload_async && swoole_is_root_user() && !_server->user_.empty()) {\n            swoole_set_isolation(_server->group_, _server->user_, _server->chroot_);\n        }\n    }\n\n    if (_server->isset_hook(Server::HOOK_MANAGER_START)) {\n        _server->call_hook(Server::HOOK_MANAGER_START, _server);\n    }\n\n    if (_server->onManagerStart) {\n        _server->onManagerStart(_server);\n    }\n\n    if (_server->manager_alarm > 0) {\n        swoole_timer_add(sec2msec(_server->manager_alarm), true, timer_callback, _server);\n    }\n\n    while (_server->running) {\n        ExitStatus exit_status = wait_process();\n        const auto wait_error = errno;\n\n        swoole_signal_dispatch();\n\n        if (pool->read_message) {\n            EventData msg;\n            while (pool->pop_message(&msg, sizeof(msg)) > 0) {\n                if (!_server->running) {\n                    continue;\n                }\n                if (msg.info.type != SW_WORKER_MESSAGE_STOP && pool->onWorkerMessage) {\n                    pool->onWorkerMessage(pool, &msg);\n                    continue;\n                }\n                WorkerStopMessage worker_stop_msg;\n                memcpy(&worker_stop_msg, msg.data, sizeof(worker_stop_msg));\n                if (worker_stop_msg.worker_id >= _server->worker_num) {\n                    _server->factory_->spawn_task_worker(_server->get_worker(worker_stop_msg.worker_id));\n                } else {\n                    Worker *worker = _server->get_worker(worker_stop_msg.worker_id);\n                    _server->factory_->spawn_event_worker(worker);\n                }\n            }\n            pool->read_message = false;\n        }\n\n        if (sw_timer()) {\n            sw_timer()->select();\n        }\n\n        if (exit_status.get_pid() < 0) {\n            if (!pool->reload_task) {\n                if (wait_error > 0 && wait_error != EINTR) {\n                    swoole_sys_warning(\"wait() failed\");\n                }\n                continue;\n            }\n        }\n\n        if (_server->running) {\n            if (reload_all_worker) {  // reload task & event workers\n                pool->reload_init = reload_all_worker = false;\n                swoole_info(\"Server is reloading all workers now\");\n                if (_server->onBeforeReload != nullptr) {\n                    _server->onBeforeReload(_server);\n                }\n                auto reload_task = pool->reload_task;\n                reload_task->add_workers(_server->workers, _server->worker_num);\n                if (_server->task_worker_num > 0) {\n                    reload_task->add_workers(_server->get_task_worker_pool()->workers, _server->task_worker_num);\n                }\n                /**\n                 * When the `reload_async` option is enabled, the SIGTERM signal will be sent to all work processes\n                 * immediately. When the work process receives the signal, it will remove the event listening of the\n                 * work process pipeline, no longer receive data, and then send a process exit message to the management\n                 * process. When the management process receives the message, it will immediately create a new worker\n                 * process. At this time, there will be two processes at the same time. The old process will exit by\n                 * itself after completing the legacy tasks, and the new client request data and tasks will be processed\n                 * by the new worker process.\n                 */\n                if (_server->reload_async) {\n                    reload_task->kill_all(SIGTERM);\n                } else {\n                    goto _kill_worker;\n                }\n            } else if (reload_task_worker) {  // only reload task workers\n                pool->reload_init = reload_task_worker = false;\n                if (_server->task_worker_num == 0) {\n                    swoole_warning(\"cannot reload task workers, task workers is not started\");\n                    continue;\n                }\n                swoole_info(\"Server is reloading task workers now\");\n                if (_server->onBeforeReload != nullptr) {\n                    _server->onBeforeReload(_server);\n                }\n                auto reload_task = pool->reload_task;\n                reload_task->add_workers(_server->get_task_worker_pool()->workers, _server->task_worker_num);\n                goto _kill_worker;\n            } else if (exit_status.get_pid() < 0) {\n                continue;\n            }\n\n            // event workers\n            SW_LOOP_N(_server->worker_num) {\n                Worker *worker = _server->get_worker(i);\n                // find worker\n                if (exit_status.get_pid() != worker->pid) {\n                    continue;\n                }\n\n                // check the process return code and signal\n                _server->factory_->check_worker_exit_status(worker, exit_status);\n\n                do {\n                    if (_server->factory_->spawn_event_worker(worker) < 0) {\n                        SW_START_SLEEP;\n                        continue;\n                    }\n                    break;\n                } while (true);\n            }\n\n            // task worker\n            if (task_pool->map_) {\n                auto iter = task_pool->map_->find(exit_status.get_pid());\n                if (iter != task_pool->map_->end()) {\n                    _server->factory_->check_worker_exit_status(iter->second, exit_status);\n                    _server->factory_->spawn_task_worker(iter->second);\n                }\n            }\n            // user process\n            if (!_server->user_worker_map.empty()) {\n                Server::wait_other_worker(pool, exit_status);\n            }\n            if (pool->reload_task) {\n                pool->reload_task->remove(exit_status.get_pid());\n            }\n        }\n\n        if (pool->reload_task) {\n            // reload finish\n            if (pool->reload_task->is_completed()) {\n                delete pool->reload_task;\n                pool->reload_task = nullptr;\n                if (_server->onAfterReload != nullptr) {\n                    _server->onAfterReload(_server);\n                }\n            } else {\n            _kill_worker:\n                pool->reload_task->kill_one(SIGTERM);\n            }\n        }\n    }\n\n    delete pool->reload_task;\n\n    if (swoole_timer_is_available()) {\n        swoole_timer_free();\n        /**\n         * The timer will override the SIGALRM signal handler,\n         * which needs to be reset to the manager's signal handler.\n         */\n        swoole_signal_set(SIGALRM, signal_handler);\n    }\n    // wait child process\n    if (_server->max_wait_time) {\n        force_kill = true;\n        SW_LOOP_N(_server->worker_num) {\n            kill_workers.push_back(_server->workers[i].pid);\n        }\n        if (_server->task_worker_num > 0) {\n            SW_LOOP_N(task_pool->worker_num) {\n                kill_workers.push_back(task_pool->workers[i].pid);\n            }\n        }\n        if (!_server->user_worker_map.empty()) {\n            for (auto &kv : _server->user_worker_map) {\n                kill_workers.push_back(kv.second->pid);\n            }\n        }\n        /**\n         * Multiply max_wait_time by 2 to prevent conflict with worker\n         */\n        alarm(_server->max_wait_time * 2);\n    }\n    _server->factory_->kill_event_workers();\n    _server->factory_->kill_task_workers();\n    _server->factory_->kill_user_workers();\n    // force kill\n    if (_server->max_wait_time) {\n        alarm(0);\n    }\n    if (_server->onManagerStop) {\n        _server->onManagerStop(_server);\n    }\n}\n\nvoid Manager::terminate_all_worker() {\n    alarm(0);\n    swoole_error_log(SW_LOG_WARNING,\n                     SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT,\n                     \"wait timeout, all worker processes will be forcibly terminated\");\n    for (int &kill_worker : kill_workers) {\n        swoole_kill(kill_worker, SIGKILL);\n    }\n}\n\nvoid Manager::reopen_logger() const {\n    sw_logger()->reopen();\n\n    SW_LOOP_N(server_->get_all_worker_num()) {\n        const auto worker = server_->get_worker(i);\n        swoole_kill(worker->pid, SIGWINCH);\n    }\n}\n\nvoid Manager::signal_handler(int signo) {\n    Server *_server = sw_server();\n    if (!_server || !_server->manager_) {\n        return;\n    }\n    Manager *manager = _server->manager_;\n    ProcessPool *pool = _server->get_event_worker_pool();\n\n    switch (signo) {\n    case SIGTERM:\n        _server->running = false;\n        break;\n    case SIGUSR1:\n    case SIGUSR2:\n        _server->reload(signo == SIGUSR1);\n        break;\n    case SIGIO:\n        pool->read_message = true;\n        break;\n    case SIGALRM:\n        if (manager->force_kill) {\n            manager->terminate_all_worker();\n        }\n        break;\n    case SIGWINCH:\n        manager->reopen_logger();\n        break;\n    default:\n#ifdef SIGRTMIN\n        if (signo == SIGRTMIN) {\n            manager->reopen_logger();\n        }\n#endif\n        break;\n    }\n}\n\n/**\n * @return: success returns pid, failure returns SW_ERR.\n */\nint Server::wait_other_worker(ProcessPool *pool, const ExitStatus &exit_status) {\n    auto serv = static_cast<Server *>(pool->ptr);\n    Worker *exit_worker = nullptr;\n    int worker_type;\n\n    do {\n        if (serv->get_task_worker_pool()->map_) {\n            const auto iter = serv->get_task_worker_pool()->map_->find(exit_status.get_pid());\n            if (iter != serv->get_task_worker_pool()->map_->end()) {\n                worker_type = SW_TASK_WORKER;\n                exit_worker = iter->second;\n                break;\n            }\n        }\n        if (!serv->user_worker_map.empty()) {\n            const auto iter = serv->user_worker_map.find(exit_status.get_pid());\n            if (iter != serv->user_worker_map.end()) {\n                worker_type = SW_USER_WORKER;\n                exit_worker = iter->second;\n                break;\n            }\n        }\n        return SW_ERR;\n    } while (false);\n\n    serv->factory_->check_worker_exit_status(exit_worker, exit_status);\n\n    pid_t new_process_pid = -1;\n\n    switch (worker_type) {\n    case SW_TASK_WORKER:\n        new_process_pid = serv->factory_->spawn_task_worker(exit_worker);\n        break;\n    case SW_USER_WORKER:\n        new_process_pid = serv->factory_->spawn_user_worker(exit_worker);\n        break;\n    default:\n        /* never here */\n        abort();\n    }\n\n    return new_process_pid;\n}\n\n/**\n * [manager]\n */\nvoid Server::read_worker_message(ProcessPool *pool, EventData *msg) {\n    if (msg->info.type != SW_SERVER_EVENT_COMMAND_REQUEST) {\n        swoole_warning(\"unknown worker message type[%d]\", msg->info.type);\n        return;\n    }\n\n    const auto serv = static_cast<Server *>(pool->ptr);\n    int command_id = msg->info.server_fd;\n    const auto iter = serv->command_handlers.find(command_id);\n    if (iter == serv->command_handlers.end()) {\n        swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_INVALID_COMMAND, \"Unknown command[command_id=%d]\", command_id);\n        return;\n    }\n\n    Command::Handler handler = iter->second;\n    const auto result = handler(serv, std::string(msg->data, msg->info.len));\n\n    SendData task{};\n    task.info.fd = msg->info.fd;\n    task.info.reactor_id = 0;\n    task.info.server_fd = -1;\n    task.info.type = SW_SERVER_EVENT_COMMAND_RESPONSE;\n    task.info.len = result.length();\n    task.data = result.c_str();\n\n    serv->message_bus.write(serv->get_command_reply_socket(), &task);\n}\n\nbool Server::reload(bool reload_all_workers) const {\n    if (is_thread_mode()) {\n        return reload_worker_threads(reload_all_workers);\n    }\n\n    if (gs->manager_pid == 0) {\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, \"not supported with single process mode\");\n        return false;\n    }\n\n    if (getpid() != gs->manager_pid) {\n        return swoole_kill(get_manager_pid(), reload_all_workers ? SIGUSR1 : SIGUSR2) == 0;\n    }\n\n    if (!get_event_worker_pool()->reload()) {\n        return false;\n    }\n\n    if (reload_all_workers) {\n        manager_->reload_all_worker = true;\n    } else {\n        manager_->reload_task_worker = true;\n    }\n    return true;\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "src/server/master.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_server.h\"\n#include \"swoole_memory.h\"\n#include \"swoole_lock.h\"\n#include \"swoole_thread.h\"\n#include \"swoole_util.h\"\n#include \"swoole_hash.h\"\n\n#include \"swoole_api.h\"\n\n#include <cassert>\n#include <net/if.h>\n#include <ifaddrs.h>\n\nusing swoole::network::Address;\nusing swoole::network::SendfileTask;\nusing swoole::network::Socket;\n\nnamespace swoole {\n\nTimerCallback Server::get_timeout_callback(ListenPort *port, Reactor *reactor, Connection *conn) const {\n    return [this, port, conn, reactor](Timer *, TimerNode *) {\n        if (conn->protect) {\n            return;\n        }\n        long ms = time<std::chrono::milliseconds>(true);\n        swoole_trace_log(SW_TRACE_SERVER,\n                         \"timeout_callback, last_received_time=%f, last_sent_time=%f\",\n                         conn->socket->last_received_time,\n                         conn->socket->last_sent_time);\n        if (ms - conn->socket->last_received_time < port->max_idle_time &&\n            ms - conn->socket->last_sent_time < port->max_idle_time) {\n            return;\n        }\n        if (disable_notify || conn->closed || conn->close_force) {\n            close_connection(reactor, conn->socket);\n            return;\n        }\n        conn->close_force = 1;\n        Event _ev{};\n        _ev.fd = conn->fd;\n        _ev.socket = conn->socket;\n        reactor->trigger_close_event(&_ev);\n    };\n}\n\nvoid Server::disable_accept() {\n    enable_accept_timer = swoole_timer_add(\n        SW_ACCEPT_RETRY_TIME,\n        false,\n        [](Timer *timer, const TimerNode *tnode) {\n            auto *serv = static_cast<Server *>(tnode->data);\n            for (const auto port : serv->ports) {\n                if (port->is_dgram()) {\n                    continue;\n                }\n                swoole_event_add(port->socket, SW_EVENT_READ);\n            }\n            serv->enable_accept_timer = nullptr;\n        },\n        this);\n\n    if (enable_accept_timer == nullptr) {\n        return;\n    }\n\n    for (const auto port : ports) {\n        if (port->is_dgram()) {\n            continue;\n        }\n        swoole_event_del(port->socket);\n    }\n}\n\nvoid Server::call_command_callback(const int64_t request_id, const std::string &result) {\n    const auto iter = command_callbacks.find(request_id);\n    if (iter == command_callbacks.end()) {\n        swoole_error_log(SW_LOG_ERROR,\n                         SW_ERROR_SERVER_INVALID_COMMAND,\n                         \"Invalid command result[request_id=%\" PRId64 \"]\",\n                         request_id);\n        return;\n    }\n    iter->second(this, result);\n    command_callbacks.erase(request_id);\n}\n\nvoid Server::call_command_handler(MessageBus &mb, uint16_t worker_id, Socket *sock) {\n    const PipeBuffer *buffer = mb.get_buffer();\n    const int command_id = buffer->info.server_fd;\n    const auto iter = command_handlers.find(command_id);\n    if (iter == command_handlers.end()) {\n        swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_INVALID_COMMAND, \"Unknown command[command_id=%d]\", command_id);\n        return;\n    }\n\n    const Command::Handler handler = iter->second;\n    const auto packet = mb.get_packet();\n    const auto result = handler(this, std::string(packet.data, packet.length));\n\n    SendData task{};\n    task.info.fd = buffer->info.fd;\n    task.info.reactor_id = worker_id;\n    task.info.server_fd = -1;\n    task.info.type = SW_SERVER_EVENT_COMMAND_RESPONSE;\n    task.info.len = result.length();\n    task.data = result.c_str();\n\n    mb.write(sock, &task);\n}\n\nstd::string Server::call_command_handler_in_master(int command_id, const std::string &msg) {\n    auto iter = command_handlers.find(command_id);\n    if (iter == command_handlers.end()) {\n        swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_INVALID_COMMAND, \"Unknown command[%d]\", command_id);\n        return \"\";\n    }\n\n    Server::Command::Handler handler = iter->second;\n    return handler(this, msg);\n}\n\nint Server::accept_command_result(Reactor *reactor, Event *event) {\n    auto *serv = static_cast<Server *>(reactor->ptr);\n\n    if (serv->message_bus.read(event->socket) <= 0) {\n        return SW_OK;\n    }\n\n    auto packet = serv->message_bus.get_packet();\n    std::string result(packet.data, packet.length);\n\n    auto buffer = serv->message_bus.get_buffer();\n    serv->call_command_callback(buffer->info.fd, result);\n    serv->message_bus.pop();\n\n    return SW_OK;\n}\n\nint Server::accept_connection(Reactor *reactor, Event *event) {\n    auto serv = static_cast<Server *>(reactor->ptr);\n    auto listen_host = static_cast<ListenPort *>(event->socket->object);\n\n    for (int i = 0; i < SW_ACCEPT_MAX_COUNT; i++) {\n        Socket *sock = event->socket->accept();\n        if (sock == nullptr) {\n            switch (errno) {\n            case EAGAIN:\n                return SW_OK;\n            case EINTR:\n                continue;\n            default:\n                if (errno == EMFILE || errno == ENFILE) {\n                    serv->disable_accept();\n                }\n                swoole_sys_warning(\"accept() failed\");\n                return SW_OK;\n            }\n        }\n\n        swoole_trace(\"[Master] Accept new connection. maxfd=%d|minfd=%d|reactor_id=%d|conn=%d\",\n                     serv->get_maxfd(),\n                     serv->get_minfd(),\n                     reactor->id,\n                     sock->fd);\n\n        // too many connection\n        if (sock->fd >= (int) serv->max_connection) {\n            swoole_error_log(\n                SW_LOG_WARNING, SW_ERROR_SERVER_TOO_MANY_SOCKET, \"Too many connections [now: %d]\", sock->fd);\n            serv->abort_connection(reactor, listen_host, sock);\n            serv->disable_accept();\n            return SW_OK;\n        }\n\n        if (listen_host->ssl) {\n            if (!listen_host->ssl_create(sock)) {\n                serv->abort_connection(reactor, listen_host, sock);\n                return SW_OK;\n            }\n        } else {\n            sock->ssl = nullptr;\n        }\n\n        // add to connection_list\n        Connection *conn = serv->add_connection(listen_host, sock, event->fd);\n        if (conn == nullptr) {\n            serv->abort_connection(reactor, listen_host, sock);\n            return SW_OK;\n        }\n        sock->chunk_size = SW_SEND_BUFFER_SIZE;\n\n        if (serv->single_thread) {\n            if (serv->connection_incoming(reactor, conn) < 0) {\n                serv->abort_connection(reactor, listen_host, sock);\n                return SW_OK;\n            }\n        } else {\n            DataHead ev{};\n            ev.type = SW_SERVER_EVENT_INCOMING;\n            ev.fd = conn->session_id;\n            ev.reactor_id = conn->reactor_id;\n            ev.server_fd = event->fd;\n            if (serv->send_to_reactor_thread(reinterpret_cast<EventData *>(&ev), sizeof(ev), conn->session_id) < 0) {\n                serv->abort_connection(reactor, listen_host, sock);\n                return SW_OK;\n            }\n        }\n    }\n\n    return SW_OK;\n}\n\nint Server::connection_incoming(Reactor *reactor, Connection *conn) const {\n    ListenPort *port = get_port_by_server_fd(conn->server_fd);\n    if (port->max_idle_time > 0) {\n        const auto timeout_callback = get_timeout_callback(port, reactor, conn);\n        conn->socket->read_timeout = port->max_idle_time;\n        conn->socket->recv_timer = swoole_timer_add(sec2msec(port->max_idle_time), true, timeout_callback);\n    }\n    if (conn->socket->ssl) {\n        return reactor->add(conn->socket, SW_EVENT_READ);\n    }\n    // delay receive, wait resume command\n    if (!enable_delay_receive) {\n        if (reactor->add(conn->socket, SW_EVENT_READ) < 0) {\n            return SW_ERR;\n        }\n    }\n    // notify worker process\n    if (onConnect) {\n        if (!notify(conn, SW_SERVER_EVENT_CONNECT)) {\n            return SW_ERR;\n        }\n    }\n    return SW_OK;\n}\n\n#ifdef SW_SUPPORT_DTLS\ndtls::Session *Server::accept_dtls_connection(const ListenPort *port, const Address *sa) {\n    dtls::Session *session = nullptr;\n    Connection *conn = nullptr;\n\n    Socket *sock = make_socket(port->type, SW_FD_SESSION, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK);\n    if (!sock) {\n        return nullptr;\n    }\n\n    sock->set_reuse_addr();\n#ifdef HAVE_KQUEUE\n    sock->set_reuse_port();\n#endif\n\n    switch (port->type) {\n    case SW_SOCK_UDP:\n    case SW_SOCK_UDP6:\n        break;\n    default:\n        OPENSSL_assert(0);\n        break;\n    }\n\n    if (sock->bind(port->host, port->port) < 0) {\n        swoole_sys_warning(\"bind() failed\");\n        goto _cleanup;\n    }\n    if (sock->is_inet6()) {\n        sock->set_option(IPPROTO_IPV6, IPV6_V6ONLY, 0);\n    }\n    if (sock->connect(sa) < 0) {\n        swoole_sys_warning(\"connect(%s:%d) failed\", sa->get_addr(), sa->get_port());\n        goto _cleanup;\n    }\n\n    memcpy(&sock->info, sa, sizeof(*sa));\n    sock->chunk_size = SW_SSL_BUFFER_SIZE;\n\n    conn = add_connection(port, sock, port->socket->fd);\n    if (conn == nullptr) {\n        goto _cleanup;\n    }\n\n    session = port->create_dtls_session(sock);\n    if (session) {\n        return session;\n    }\n\n_cleanup:\n    if (conn) {\n        *conn = {};\n    }\n    sock->free();\n    return nullptr;\n}\n#endif\n\nvoid Server::set_max_connection(uint32_t _max_connection) {\n    if (connection_list != nullptr) {\n        swoole_warning(\"max_connection must be set before server create\");\n        return;\n    }\n    max_connection = _max_connection;\n    if (max_connection == 0) {\n        max_connection = SW_MIN(SW_MAX_CONNECTION, SwooleG.max_sockets);\n    } else if (max_connection > SW_SESSION_LIST_SIZE) {\n        max_connection = SW_SESSION_LIST_SIZE;\n        swoole_warning(\"max_connection is exceed the SW_SESSION_LIST_SIZE, it's reset to %u\", SW_SESSION_LIST_SIZE);\n    }\n    if (SwooleG.max_sockets > 0 && max_connection > SwooleG.max_sockets) {\n        max_connection = SwooleG.max_sockets;\n        swoole_warning(\"max_connection is exceed the maximum value, it's reset to %u\", SwooleG.max_sockets);\n    }\n}\n\nbool Server::set_document_root(const std::string &path) {\n    if (path.length() > PATH_MAX) {\n        swoole_error_log(\n            SW_LOG_WARNING, SW_ERROR_NAME_TOO_LONG, \"The length of document_root must be less than %d\", PATH_MAX);\n        return false;\n    }\n\n    char _realpath[PATH_MAX];\n    if (!realpath(path.c_str(), _realpath)) {\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_DIR_NOT_EXIST, \"document_root[%s] does not exist\", path.c_str());\n        return false;\n    }\n\n    document_root = std::string(_realpath);\n    return true;\n}\n\nvoid Server::add_http_compression_type(const std::string &type) {\n    if (http_compression_types == nullptr) {\n        http_compression_types = std::make_shared<std::unordered_set<std::string>>();\n    }\n    http_compression_types->emplace(type);\n}\n\nconst char *Server::get_startup_error_message() const {\n    auto error_msg = swoole_get_last_error_msg();\n    if (strlen(error_msg) == 0 && swoole_get_last_error() > 0) {\n        auto buf = sw_tg_buffer();\n        buf->clear();\n        buf->append(swoole_get_last_error());\n        buf->str[buf->length] = '\\0';\n        error_msg = buf->str;\n    }\n    return error_msg;\n}\n\nint Server::start_check() {\n    assert(is_created());\n    // disable notice when use SW_DISPATCH_ROUND and SW_DISPATCH_QUEUE\n    if (is_process_mode()) {\n        if (!is_support_unsafe_events()) {\n            if (onConnect) {\n                swoole_error_log(SW_LOG_WARNING,\n                                 SW_ERROR_SERVER_INVALID_CALLBACK,\n                                 \"cannot set 'onConnect' event when using dispatch_mode=%d\",\n                                 dispatch_mode);\n                onConnect = nullptr;\n            }\n            if (onClose) {\n                swoole_error_log(SW_LOG_WARNING,\n                                 SW_ERROR_SERVER_INVALID_CALLBACK,\n                                 \"cannot set 'onClose' event when using dispatch_mode=%d\",\n                                 dispatch_mode);\n                onClose = nullptr;\n            }\n            if (onBufferFull) {\n                swoole_error_log(SW_LOG_WARNING,\n                                 SW_ERROR_SERVER_INVALID_CALLBACK,\n                                 \"cannot set 'onBufferFull' event when using dispatch_mode=%d\",\n                                 dispatch_mode);\n                onBufferFull = nullptr;\n            }\n            if (onBufferEmpty) {\n                swoole_error_log(SW_LOG_WARNING,\n                                 SW_ERROR_SERVER_INVALID_CALLBACK,\n                                 \"cannot set 'onBufferEmpty' event when using dispatch_mode=%d\",\n                                 dispatch_mode);\n                onBufferEmpty = nullptr;\n            }\n            disable_notify = true;\n        }\n        if (!is_support_send_yield()) {\n            send_yield = false;\n        }\n    } else {\n        max_queued_bytes = 0;\n    }\n    if (task_worker_num > 0) {\n        if (onTask == nullptr) {\n            swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_INVALID_CALLBACK, \"require 'onTask' callback\");\n            return SW_ERR;\n        }\n    }\n    if (send_timeout > 0 && send_timeout < SW_TIMER_MIN_SEC) {\n        send_timeout = SW_TIMER_MIN_SEC;\n    }\n    if (heartbeat_check_interval > 0) {\n        for (auto port : ports) {\n            if (port->heartbeat_idle_time == 0) {\n                port->heartbeat_idle_time = heartbeat_check_interval * 2;\n            }\n        }\n    }\n    for (auto port : ports) {\n        if (port->protocol.package_max_length < SW_BUFFER_MIN_SIZE) {\n            port->protocol.package_max_length = SW_BUFFER_MIN_SIZE;\n        }\n        if (if_require_receive_callback(port, onReceive != nullptr)) {\n            swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_INVALID_CALLBACK, \"require 'onReceive' callback\");\n            return SW_ERR;\n        }\n        if (if_require_packet_callback(port, onPacket != nullptr)) {\n            swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_INVALID_CALLBACK, \"require 'onPacket' callback\");\n            return SW_ERR;\n        }\n        if (port->heartbeat_idle_time > 0) {\n            int expect_heartbeat_check_interval = port->heartbeat_idle_time > 2 ? port->heartbeat_idle_time / 2 : 1;\n            if (heartbeat_check_interval == 0 || heartbeat_check_interval > expect_heartbeat_check_interval) {\n                heartbeat_check_interval = expect_heartbeat_check_interval;\n            }\n        }\n        if (port->open_websocket_protocol) {\n            port->websocket_settings.compression = websocket_compression;\n        }\n    }\n\n    return SW_OK;\n}\n\nint Server::start_master_thread(Reactor *reactor) {\n    swoole_set_thread_type(THREAD_MASTER);\n    swoole_set_thread_id(single_thread ? 0 : reactor_num);\n\n    if (SwooleTG.timer && SwooleTG.timer->get_reactor() == nullptr) {\n        SwooleTG.timer->reinit();\n    }\n\n    init_signal_handler();\n\n    swoole_set_worker_type(SW_MASTER);\n\n    if (is_thread_mode()) {\n        swoole_set_worker_pid(swoole_thread_get_native_id());\n    } else if (is_process_mode()) {\n        swoole_set_worker_pid(getpid());\n    }\n\n    reactor->ptr = this;\n    reactor->set_handler(SW_FD_STREAM_SERVER, SW_EVENT_READ, accept_connection);\n\n    if (pipe_command) {\n        if (!single_thread) {\n            reactor->set_handler(SW_FD_PIPE, SW_EVENT_READ, accept_command_result);\n        }\n        reactor->add(pipe_command->get_socket(true), SW_EVENT_READ);\n    }\n\n    if ((master_timer = swoole_timer_add(1000L, true, Server::timer_callback, this)) == nullptr) {\n        swoole_event_free();\n        return SW_ERR;\n    }\n\n    if (!single_thread && !is_thread_mode()) {\n        reactor_thread_barrier.wait();\n    }\n    if (is_process_mode()) {\n        gs->manager_barrier.wait();\n    }\n    gs->master_pid = getpid();\n\n    if (isset_hook(HOOK_MASTER_START)) {\n        call_hook(HOOK_MASTER_START, this);\n    }\n\n    if (onStart) {\n        onStart(this);\n    }\n\n    return swoole_event_wait();\n}\n\nvoid Server::store_listen_socket() {\n    for (auto ls : ports) {\n        int sockfd = ls->socket->fd;\n        // save server socket to connection_list\n        connection_list[sockfd].fd = sockfd;\n        connection_list[sockfd].socket = ls->socket;\n        connection_list[sockfd].socket_type = ls->type;\n        connection_list[sockfd].object = ls;\n        connection_list[sockfd].info.assign(ls->type, ls->host, ls->port);\n        ls->socket->object = ls;\n        if (sockfd >= 0) {\n            set_minfd(sockfd);\n            set_maxfd(sockfd);\n        }\n    }\n}\n\n/**\n * only the memory of the Worker structure is allocated, no process is forked\n */\nbool Server::create_task_workers() {\n    key_t key = 0;\n    swIPCMode ipc_mode;\n\n    if (task_ipc_mode == TASK_IPC_MSGQUEUE || task_ipc_mode == TASK_IPC_PREEMPTIVE) {\n        key = message_queue_key;\n        ipc_mode = SW_IPC_MSGQUEUE;\n    } else if (task_ipc_mode == TASK_IPC_STREAM) {\n        ipc_mode = SW_IPC_SOCKET;\n    } else {\n        ipc_mode = SW_IPC_UNIXSOCK;\n    }\n\n    ProcessPool *pool = get_task_worker_pool();\n    *pool = {};\n    if (pool->create(task_worker_num, key, ipc_mode) < 0) {\n        swoole_warning(\"[Master] create task_workers failed\");\n        return false;\n    }\n\n    pool->set_max_request(task_max_request, task_max_request_grace);\n    pool->set_start_id(worker_num);\n    pool->set_type(SW_TASK_WORKER);\n\n    if (ipc_mode == SW_IPC_SOCKET) {\n        char sockfile[sizeof(struct sockaddr_un)];\n        snprintf(sockfile, sizeof(sockfile), \"/tmp/swoole.task.%d.sock\", gs->master_pid);\n        if (get_task_worker_pool()->listen(sockfile, 2048) < 0) {\n            return false;\n        }\n    }\n\n    /*\n     * For Server::task_sync(), create notify pipe and result shared memory.\n     */\n    task_results = static_cast<EventData *>(sw_shm_calloc(worker_num, sizeof(EventData)));\n    if (!task_results) {\n        swoole_warning(\"sw_shm_calloc(%d, %zu) for task_result failed\", worker_num, sizeof(EventData));\n        return false;\n    }\n    SW_LOOP_N(worker_num) {\n        auto _pipe = new Pipe(true);\n        if (!_pipe->ready()) {\n            sw_shm_free(task_results);\n            delete _pipe;\n            return false;\n        }\n        task_notify_pipes.emplace_back(_pipe);\n    }\n\n    if (!init_task_workers()) {\n        return false;\n    }\n\n    return true;\n}\n\nvoid Server::destroy_task_workers() const {\n    if (task_results) {\n        sw_shm_free(task_results);\n    }\n    get_task_worker_pool()->destroy();\n}\n\nbool Server::create_event_workers() {\n    SW_LOOP_N(worker_num) {\n        create_worker(get_worker(i));\n    }\n    return true;\n}\n\n/**\n * @description:\n *  only the memory of the Worker structure is allocated, no process is fork.\n *  called when the manager process start.\n * @return: SW_OK|SW_ERR\n */\nbool Server::create_user_workers() {\n    user_workers = static_cast<Worker *>(sw_shm_calloc(get_user_worker_num(), sizeof(Worker)));\n    if (user_workers == nullptr) {\n        swoole_sys_warning(\"sw_shm_calloc(%lu, %zu) for user_workers failed\", get_user_worker_num(), sizeof(Worker));\n        return false;\n    }\n\n    int i = 0;\n    for (const auto worker : user_worker_list) {\n        memcpy(&user_workers[i], worker, sizeof(user_workers[i]));\n        create_worker(worker);\n        i++;\n    }\n\n    return true;\n}\n\n/**\n * [Master]\n */\nvoid Server::create_worker(Worker *worker) {\n    worker->lock = new Mutex(true);\n    if (worker->pipe_object) {\n        store_pipe_fd(worker->pipe_object);\n    }\n}\n\nvoid Server::destroy_worker(Worker *worker) {\n    delete worker->lock;\n    worker->lock = nullptr;\n}\n\n/**\n * [Worker]\n */\nvoid Server::init_event_worker(Worker *worker) const {\n    worker->init();\n    worker->set_max_request(max_request, max_request_grace);\n}\n\nint Server::start() {\n    swoole_clear_last_error();\n    swoole_clear_last_error_msg();\n    if (start_check() < 0) {\n        return SW_ERR;\n    }\n    if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_START)) {\n        swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_START, this);\n    }\n    // cannot start 2 servers at the same time, please use process->exec.\n    if (!sw_atomic_cmp_set(&gs->start, 0, 1)) {\n        swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_ONLY_START_ONE, \"can only start one server\");\n        return SW_ERR;\n    }\n    // run as daemon\n    if (daemonize > 0) {\n        // redirect stdout/stderr to log file\n        if (sw_logger()->is_opened()) {\n            sw_logger()->redirect_stdout_and_stderr(true);\n        }\n        // redirect stdout/stderr to /dev/null\n        else {\n            swoole_redirect_stdout(\"/dev/null\");\n        }\n\n        if (swoole_daemon(0, 1) < 0) {\n            return SW_ERR;\n        }\n    }\n\n    gs->start_time = ::time(nullptr);\n\n    /**\n     * store to ProcessPool object\n     */\n    auto pool = get_event_worker_pool();\n    pool->ptr = this;\n    pool->workers = workers;\n    pool->worker_num = worker_num;\n    pool->use_msgqueue = 0;\n\n    SW_LOOP_N(worker_num) {\n        pool->workers[i].pool = pool;\n        pool->workers[i].id = i;\n        pool->workers[i].type = SW_WORKER;\n    }\n\n    if (!user_worker_list.empty()) {\n        uint32_t i = 0;\n        for (auto worker : user_worker_list) {\n            worker->id = worker_num + task_worker_num + i;\n            i++;\n        }\n    }\n\n    running = true;\n    // factory start\n    if (!factory_->start()) {\n        return SW_ERR;\n    }\n    // write PID file\n    if (!pid_file.empty()) {\n        size_t n = sw_snprintf(sw_tg_buffer()->str, sw_tg_buffer()->size, \"%d\", getpid());\n        file_put_contents(pid_file, sw_tg_buffer()->str, n);\n    }\n    int ret;\n    if (is_base_mode()) {\n        ret = start_reactor_processes();\n    } else if (is_process_mode()) {\n        ret = start_reactor_threads();\n    } else if (is_thread_mode()) {\n        ret = start_worker_threads();\n    } else {\n        abort();\n        return SW_ERR;\n    }\n    // failed to start\n    if (ret < 0) {\n        return SW_ERR;\n    }\n    destroy();\n    // remove PID file\n    if (!pid_file.empty()) {\n        unlink(pid_file.c_str());\n    }\n    return SW_OK;\n}\n\n/**\n * initializing server config, set default\n */\nServer::Server(Mode _mode) {\n    reactor_num = SW_CPU_NUM > SW_REACTOR_MAX_THREAD ? SW_REACTOR_MAX_THREAD : SW_CPU_NUM;\n    worker_num = SW_CPU_NUM;\n    max_connection = SW_MIN(SW_MAX_CONNECTION, SwooleG.max_sockets);\n    mode_ = _mode;\n\n    // http server\n    http_compression = true;\n    http_compression_level = SW_Z_BEST_SPEED;\n    compression_min_length = SW_COMPRESSION_MIN_LENGTH_DEFAULT;\n\n    timezone_ = get_timezone();\n\n    gs = static_cast<ServerGS *>(sw_shm_malloc(sizeof(ServerGS)));\n    if (gs == nullptr) {\n        swoole_sys_warning(\"[Master] Fatal Error: failed to allocate memory for Server->gs\");\n        throw std::bad_alloc();\n    }\n    gs->pipe_packet_msg_id = 1;\n    gs->max_concurrency = UINT_MAX;\n\n    msg_id_generator = [this]() { return sw_atomic_fetch_add(&gs->pipe_packet_msg_id, 1); };\n    message_bus.set_id_generator(msg_id_generator);\n\n#ifdef SW_THREAD\n    worker_thread_start = [](std::shared_ptr<Thread>, const WorkerFn &fn) { fn(); };\n#endif\n\n    SwooleG.server = this;\n}\n\nServer::~Server() {\n    if (!is_shutdown() && getpid() == gs->master_pid) {\n        destroy();\n    }\n    for (auto port : ports) {\n        delete port;\n    }\n    sw_shm_free(gs);\n}\n\nWorker *Server::get_worker(uint16_t worker_id) const {\n    // Event Worker\n    if (worker_id < worker_num) {\n        return &(get_event_worker_pool()->workers[worker_id]);\n    }\n\n    // Task Worker\n    uint32_t task_worker_max = task_worker_num + worker_num;\n    if (worker_id < task_worker_max) {\n        return &(get_task_worker_pool()->workers[worker_id - worker_num]);\n    }\n\n    // User Worker\n    uint32_t user_worker_max = task_worker_max + user_worker_list.size();\n    if (worker_id < user_worker_max) {\n        return &(user_workers[worker_id - task_worker_max]);\n    }\n\n    return nullptr;\n}\n\nint Server::create() {\n    if (is_created()) {\n        return SW_ERR;\n    }\n\n    assert(!ports.empty());\n\n    if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_CREATE)) {\n        swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_CREATE, this);\n    }\n\n    if (!init_network_interface_addr_map()) {\n        return SW_ERR;\n    }\n\n    session_list = static_cast<Session *>(sw_shm_calloc(SW_SESSION_LIST_SIZE, sizeof(Session)));\n    if (session_list == nullptr) {\n        swoole_sys_warning(\"sw_shm_calloc(%d, %zu) for session_list failed\", SW_SESSION_LIST_SIZE, sizeof(Session));\n        return SW_ERR;\n    }\n\n    port_gs_list = static_cast<ServerPortGS *>(sw_shm_calloc(ports.size(), sizeof(ServerPortGS)));\n    if (port_gs_list == nullptr) {\n        swoole_sys_warning(\n            \"sw_shm_calloc(%zu, %zu) for port_connection_num_array failed\", ports.size(), sizeof(ServerPortGS));\n        return SW_ERR;\n    }\n\n    int index = 0;\n    for (auto port : ports) {\n        port->gs = &port_gs_list[index++];\n    }\n\n    if (enable_static_handler and locations == nullptr) {\n        locations = std::make_shared<std::unordered_set<std::string>>();\n    }\n\n    if (http_compression_types && !http_compression_types->empty()) {\n        http_compression = true;\n    }\n\n    // Max Connections\n    uint32_t minimum_connection = (worker_num + task_worker_num) * 2 + 32;\n    if (!ports.empty()) {\n        minimum_connection += ports.back()->get_fd();\n    }\n    if (max_connection < minimum_connection) {\n        auto real_max_connection = SW_MAX(minimum_connection + 1, SwooleG.max_sockets);\n        swoole_warning(\n            \"max_connection must be bigger than %u, it's reset to %u\", minimum_connection, real_max_connection);\n        max_connection = real_max_connection;\n    }\n    // Reactor Thread Num\n    if (reactor_num > SW_CPU_NUM * SW_MAX_THREAD_NCPU) {\n        swoole_warning(\n            \"reactor_num == %d, Too many threads, reset to max value %d\", reactor_num, SW_CPU_NUM * SW_MAX_THREAD_NCPU);\n        reactor_num = SW_CPU_NUM * SW_MAX_THREAD_NCPU;\n    } else if (reactor_num == 0) {\n        reactor_num = SW_CPU_NUM;\n    }\n    if (single_thread) {\n        reactor_num = 1;\n    }\n    // Worker Process Num\n    if (worker_num > SW_CPU_NUM * SW_MAX_WORKER_NCPU) {\n        swoole_warning(\n            \"worker_num == %d, Too many processes, reset to max value %d\", worker_num, SW_CPU_NUM * SW_MAX_WORKER_NCPU);\n        worker_num = SW_CPU_NUM * SW_MAX_WORKER_NCPU;\n    }\n    if (worker_num < reactor_num) {\n        reactor_num = worker_num;\n    }\n    // TaskWorker Process Num\n    if (task_worker_num > 0) {\n        if (task_worker_num > SW_CPU_NUM * SW_MAX_WORKER_NCPU) {\n            swoole_warning(\"task_worker_num == %d, Too many processes, reset to max value %d\",\n                           task_worker_num,\n                           SW_CPU_NUM * SW_MAX_WORKER_NCPU);\n            task_worker_num = SW_CPU_NUM * SW_MAX_WORKER_NCPU;\n        }\n    }\n    workers = static_cast<Worker *>(sw_shm_calloc(worker_num, sizeof(Worker)));\n    if (workers == nullptr) {\n        swoole_sys_warning(\"sw_shm_calloc(%d, %zu) for workers failed\", worker_num, sizeof(Worker));\n        return SW_ERR;\n    }\n\n    if (is_base_mode()) {\n        factory_ = create_base_factory();\n    } else if (is_thread_mode()) {\n        factory_ = create_thread_factory();\n    } else {\n        factory_ = create_process_factory();\n    }\n    if (!factory_) {\n        return SW_ERR;\n    }\n\n    if (task_worker_num > 0 && !create_task_workers()) {\n        return SW_ERR;\n    }\n\n    if (swoole_isset_hook(SW_GLOBAL_HOOK_AFTER_SERVER_CREATE)) {\n        swoole_call_hook(SW_GLOBAL_HOOK_AFTER_SERVER_CREATE, this);\n    }\n\n    return SW_OK;\n}\n\nvoid Server::clear_timer() {\n    if (master_timer) {\n        swoole_timer_del(master_timer);\n        master_timer = nullptr;\n    }\n    if (heartbeat_timer) {\n        swoole_timer_del(heartbeat_timer);\n        heartbeat_timer = nullptr;\n    }\n    if (enable_accept_timer) {\n        swoole_timer_del(enable_accept_timer);\n        enable_accept_timer = nullptr;\n    }\n}\n\nbool Server::shutdown() {\n    if (sw_unlikely(!is_started())) {\n        swoole_set_last_error(SW_ERROR_WRONG_OPERATION);\n        return false;\n    }\n\n    /**\n     * In thread mode, the worker thread masks all signals, and only a specific signal is processed.\n     * Sending a signal to its own process can inform the main thread to prepare for exit.\n     */\n    if (is_thread_mode() && is_master_thread()) {\n        stop_master_thread();\n        return true;\n    }\n\n    if (is_process_mode()) {\n        /**\n         * The working process may be using a non-root user, and it will not be possible to send a signal to the master\n         * process. A shutdown command must be sent via a pipe.\n         */\n        SendData _send{};\n        _send.info.type = SW_SERVER_EVENT_SHUTDOWN_SIGNAL;\n        if (is_process_mode()) {\n            return get_reactor_pipe_socket(0, 0)->send_async(&_send, sizeof(_send)) > 0;\n        }\n    } else {\n        pid_t pid;\n        if (is_base_mode()) {\n            pid = get_manager_pid() == 0 ? get_master_pid() : get_manager_pid();\n        } else {\n            pid = get_master_pid();\n        }\n\n        if (swoole_kill(pid, SIGTERM) < 0) {\n            swoole_error_log(\n                SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, \"failed to shutdown, kill(%d, SIGTERM) failed\", pid);\n            return false;\n        }\n    }\n\n    return true;\n}\n\nbool Server::signal_handler_reload(bool reload_all_workers) const {\n    const auto rv = reload(reload_all_workers);\n    sw_logger()->reopen();\n    return rv;\n}\n\nbool Server::signal_handler_read_message() const {\n    get_event_worker_pool()->read_message = true;\n    return true;\n}\n\nbool Server::signal_handler_reopen_logger() const {\n    swoole_trace_log(SW_TRACE_SERVER, \"reopen log file ['%s']\", sw_logger()->get_file());\n    sw_logger()->reopen();\n\n    if (is_process_mode()) {\n        swoole_kill(gs->manager_pid, SIGWINCH);\n    }\n\n    return true;\n}\n\nvoid Server::stop_master_thread() {\n    Reactor *reactor = SwooleTG.reactor;\n    reactor->set_wait_exit(true);\n    for (auto port : ports) {\n        if (port->is_dgram() && !is_base_mode()) {\n            continue;\n        }\n        if (!port->socket->removed) {\n            reactor->del(port->socket);\n        }\n    }\n    if (pipe_command) {\n        reactor->del(pipe_command->get_socket(true));\n    }\n    clear_timer();\n    if (max_wait_time > 0) {\n        time_t shutdown_time = std::time(nullptr);\n        auto fn = [shutdown_time, this](Reactor *reactor, size_t &) {\n            time_t now = std::time(nullptr);\n            if (now - shutdown_time > max_wait_time) {\n                swoole_error_log(SW_LOG_WARNING,\n                                 SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT,\n                                 \"graceful shutdown failed, forced termination\");\n                reactor->running = false;\n            }\n            return true;\n        };\n        reactor->set_exit_condition(Reactor::EXIT_CONDITION_FORCED_TERMINATION, fn);\n    }\n    if (is_thread_mode()) {\n        stop_worker_threads();\n    }\n    if (is_process_mode() && single_thread) {\n        get_thread(0)->shutdown(reactor);\n    }\n}\n\nbool Server::signal_handler_shutdown() {\n    swoole_trace_log(SW_TRACE_SERVER, \"shutdown begin\");\n    if (is_base_mode()) {\n        if (gs->manager_pid > 0) {\n            running = false;\n        } else {\n            // single process worker, exit directly\n            get_event_worker_pool()->running = false;\n            stop_async_worker(sw_worker());\n        }\n        return true;\n    }\n    if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN)) {\n        swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN, this);\n    }\n    if (onBeforeShutdown) {\n        onBeforeShutdown(this);\n    }\n    running = false;\n    stop_master_thread();\n    swoole_trace_log(SW_TRACE_SERVER, \"shutdown end\");\n    return true;\n}\n\nbool Server::signal_handler_child_exit() const {\n    if (!running) {\n        return false;\n    }\n    if (is_base_mode()) {\n        return false;\n    }\n    int status;\n    pid_t pid = waitpid(-1, &status, WNOHANG);\n    if (pid > 0 && pid == gs->manager_pid) {\n        swoole_warning(\"Fatal Error: manager process exit. status=%d, signal=[%s]\",\n                       WEXITSTATUS(status),\n                       swoole_signal_to_str(WTERMSIG(status)));\n    }\n    return true;\n}\n\nvoid Server::destroy() {\n    if (!factory_) {\n        return;\n    }\n\n    swoole_trace_log(SW_TRACE_SERVER, \"release service\");\n    if (swoole_isset_hook(SW_GLOBAL_HOOK_AFTER_SERVER_SHUTDOWN)) {\n        swoole_call_hook(SW_GLOBAL_HOOK_AFTER_SERVER_SHUTDOWN, this);\n    }\n\n    if (is_process_mode()) {\n        swoole_trace_log(SW_TRACE_SERVER, \"terminate reactor threads\");\n        /**\n         * Wait until all the end of the thread\n         */\n        join_reactor_thread();\n    }\n\n    /**\n     * The position of the following code cannot be modified.\n     * We need to ensure that in SWOOLE_PROCESS mode, all SW_WRITE_EVENT events in the reactor thread are fully\n     * completed. Therefore, the worker process must wait for the reactor thread to exit first; otherwise, the main\n     * thread will keep waiting for the reactor thread to exit.\n     */\n    if (is_started()) {\n        factory_->shutdown();\n    }\n\n    SW_LOOP_N(worker_num) {\n        Worker *worker = &workers[i];\n        destroy_worker(worker);\n    }\n\n    release_pipe_buffers();\n\n    for (auto port : ports) {\n        port->close();\n    }\n\n    if (user_workers) {\n        sw_shm_free(user_workers);\n        user_workers = nullptr;\n    }\n\n    swoole_signal_clear();\n\n    gs->start = 0;\n    gs->shutdown = 1;\n\n    if (onShutdown) {\n        onShutdown(this);\n    }\n\n    SW_LOOP_N(SW_MAX_HOOK_TYPE) {\n        if (hooks[i]) {\n            auto l = static_cast<std::list<Callback> *>(hooks[i]);\n            hooks[i] = nullptr;\n            delete l;\n        }\n    }\n\n    if (is_base_mode()) {\n        destroy_base_factory();\n    } else if (is_thread_mode()) {\n        destroy_thread_factory();\n    } else {\n        destroy_process_factory();\n    }\n\n    if (task_worker_num > 0) {\n        swoole_trace_log(SW_TRACE_SERVER, \"terminate task workers\");\n        destroy_task_workers();\n    }\n\n    sw_shm_free(session_list);\n    sw_shm_free(port_gs_list);\n    sw_shm_free(workers);\n\n    session_list = nullptr;\n    port_gs_list = nullptr;\n    workers = nullptr;\n\n    delete factory_;\n    factory_ = nullptr;\n\n    SwooleG.server = nullptr;\n}\n\n/**\n * worker to master process\n */\nbool Server::feedback(Connection *conn, enum ServerEventType event) {\n    SendData _send{};\n    _send.info.type = event;\n    _send.info.fd = conn->session_id;\n    _send.info.reactor_id = conn->reactor_id;\n\n    if (is_process_mode()) {\n        return send_to_reactor_thread(\n                   reinterpret_cast<EventData *>(&_send.info), sizeof(_send.info), conn->session_id) > 0;\n    } else {\n        return send_to_connection(&_send) == SW_OK;\n    }\n}\n\nbool Server::command(WorkerId process_id,\n                     Command::ProcessType process_type,\n                     const std::string &name,\n                     const std::string &msg,\n                     const Command::Callback &fn) {\n    if (!is_started()) {\n        return false;\n    }\n    auto iter = commands.find(name);\n    if (iter == commands.end()) {\n        swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SERVER_INVALID_COMMAND, \"Unknown command[%s]\", name.c_str());\n        return false;\n    }\n\n    if (is_process_mode() && !is_master()) {\n        swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, \"command() can only be used in master process\");\n        return false;\n    } else if (is_base_mode() && sw_worker()->id != 0) {\n        swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, \"command() can only be used in worker process 0\");\n        return false;\n    }\n\n    if (is_base_mode() && process_type == Command::EVENT_WORKER && process_id == 0) {\n        process_type = Command::MASTER;\n    }\n\n    if (is_process_mode() && process_type == Command::REACTOR_THREAD && process_id == reactor_num) {\n        process_type = Command::MASTER;\n        process_id = 0;\n    }\n\n    int command_id = iter->second.id;\n    int64_t request_id = command_current_request_id++;\n    Socket *pipe_sock;\n\n    SendData task{};\n    task.info.fd = request_id;\n    task.info.reactor_id = process_id;\n    task.info.server_fd = command_id;\n    task.info.type = SW_SERVER_EVENT_COMMAND_REQUEST;\n    task.info.len = msg.length();\n    task.data = msg.c_str();\n\n    command_callbacks[request_id] = fn;\n\n    if (!(process_type & iter->second.accepted_process_types)) {\n        swoole_error_log(SW_LOG_NOTICE, SW_ERROR_OPERATION_NOT_SUPPORT, \"unsupported [process_type]\");\n    _fail:\n        command_callbacks.erase(request_id);\n        return false;\n    }\n\n    if (process_type == Command::REACTOR_THREAD) {\n        if (!is_process_mode()) {\n            swoole_error_log(SW_LOG_NOTICE, SW_ERROR_OPERATION_NOT_SUPPORT, \"unsupported [server_mode]\");\n            goto _fail;\n        }\n        if (process_id >= reactor_num) {\n            swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, \"invalid thread_id[%d]\", process_id);\n            goto _fail;\n        }\n        pipe_sock = get_worker(process_id)->pipe_worker;\n    } else if (process_type == Command::EVENT_WORKER) {\n        if (process_id >= worker_num) {\n            swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, \"invalid worker_id[%d]\", process_id);\n            goto _fail;\n        }\n        pipe_sock = get_worker(process_id)->pipe_master;\n    } else if (process_type == Command::TASK_WORKER) {\n        if (process_id >= task_worker_num) {\n            swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, \"invalid task_worker_id[%d]\", process_id);\n            goto _fail;\n        }\n        EventData buf;\n        if (!task_pack(&buf, msg.c_str(), msg.length())) {\n            goto _fail;\n        }\n        buf.info.type = SW_SERVER_EVENT_COMMAND_REQUEST;\n        buf.info.fd = request_id;\n        buf.info.server_fd = command_id;\n        int _dst_worker_id = process_id;\n        if (!this->task(&buf, &_dst_worker_id)) {\n            goto _fail;\n        }\n        return true;\n    } else if (process_type == Command::MANAGER) {\n        EventData buf;\n        if (msg.length() >= sizeof(buf.data)) {\n            swoole_error_log(SW_LOG_NOTICE,\n                             SW_ERROR_DATA_LENGTH_TOO_LARGE,\n                             \"message is too large, maximum length is %lu, the given length is %lu\",\n                             sizeof(buf.data),\n                             msg.length());\n            goto _fail;\n        }\n        memset(&buf.info, 0, sizeof(buf.info));\n        buf.info.type = SW_SERVER_EVENT_COMMAND_REQUEST;\n        buf.info.fd = request_id;\n        buf.info.server_fd = command_id;\n        buf.info.len = msg.length();\n        memcpy(buf.data, msg.c_str(), msg.length());\n        if (get_event_worker_pool()->push_message(&buf) < 0) {\n            goto _fail;\n        }\n        return true;\n    } else if (process_type == Command::MASTER) {\n        auto result = call_command_handler_in_master(command_id, msg);\n        fn(this, result);\n        return true;\n    } else {\n        swoole_error_log(SW_LOG_NOTICE, SW_ERROR_OPERATION_NOT_SUPPORT, \"unsupported [process_type]\");\n        goto _fail;\n    }\n    if (!message_bus.write(pipe_sock, &task)) {\n        goto _fail;\n    }\n    return true;\n}\n\nvoid Server::store_pipe_fd(UnixSocket *p) {\n    Socket *master_socket = p->get_socket(true);\n    Socket *worker_socket = p->get_socket(false);\n\n    connection_list[master_socket->fd].object = p;\n    connection_list[worker_socket->fd].object = p;\n\n    if (master_socket->fd > get_maxfd()) {\n        set_maxfd(master_socket->fd);\n    }\n    if (worker_socket->fd > get_maxfd()) {\n        set_maxfd(worker_socket->fd);\n    }\n}\n\n/**\n * @process Worker\n */\nbool Server::send(SessionId session_id, const void *data, uint32_t length) const {\n    SendData _send{};\n    _send.info.fd = session_id;\n    _send.info.type = SW_SERVER_EVENT_SEND_DATA;\n    _send.data = (char *) data;\n    _send.info.len = length;\n    if (factory_->finish(&_send)) {\n        sw_atomic_fetch_add(&gs->response_count, 1);\n        sw_atomic_fetch_add(&gs->total_send_bytes, length);\n        ListenPort *port = get_port_by_session_id(session_id);\n        if (port) {\n            sw_atomic_fetch_add(&port->gs->response_count, 1);\n            sw_atomic_fetch_add(&port->gs->total_send_bytes, length);\n        }\n        if (sw_worker()) {\n            sw_worker()->response_count++;\n        }\n        return true;\n    }\n    return false;\n}\n\nbool Server::has_kernel_nobufs_error(SessionId session_id) const {\n    auto conn = get_connection(session_id);\n    if (!conn || !conn->socket) {\n        return false;\n    }\n    if (is_process_mode()) {\n        return get_reactor_pipe_socket(session_id, conn->reactor_id)->has_kernel_nobufs();\n    } else {\n        return conn->socket->has_kernel_nobufs();\n    }\n}\n\nint Server::schedule_worker(int fd, SendData *data) {\n    uint32_t key = 0;\n\n    if (dispatch_func) {\n        int id = dispatch_func(this, get_connection(fd), data);\n        if (id != DISPATCH_RESULT_USERFUNC_FALLBACK) {\n            return id;\n        }\n    }\n\n    // polling mode\n    if (dispatch_mode == DISPATCH_ROUND) {\n        key = sw_atomic_fetch_add(&worker_round_id, 1);\n    }\n    // Using the FD touch access to hash\n    else if (dispatch_mode == DISPATCH_FDMOD) {\n        key = fd;\n    }\n    // Using the IP touch access to hash\n    else if (dispatch_mode == DISPATCH_IPMOD) {\n        Connection *conn = get_connection(fd);\n        Address *addr;\n        if (conn == nullptr) {\n            auto *packet = (DgramPacket *) data->data;\n            addr = &packet->socket_addr;\n        } else {\n            addr = &conn->info;\n        }\n        if (Socket::is_inet4(addr->type)) {\n            key = ntohl(addr->addr.inet_v4.sin_addr.s_addr);\n        } else {\n            key = swoole_hash_php((char *) &addr->addr.inet_v6, sizeof(addr->addr.inet_v6));\n        }\n    } else if (dispatch_mode == DISPATCH_UIDMOD) {\n        Connection *conn = get_connection(fd);\n        if (conn == nullptr || conn->uid == 0) {\n            key = fd;\n        } else {\n            key = conn->uid;\n        }\n    } else if (dispatch_mode == DISPATCH_CO_CONN_LB) {\n        Connection *conn = get_connection(fd);\n        if (conn == nullptr) {\n            return fd % worker_num;\n        }\n        if (conn->worker_id < 0) {\n            conn->worker_id = get_lowest_load_worker_id();\n        }\n        return conn->worker_id;\n    } else if (dispatch_mode == DISPATCH_CO_REQ_LB) {\n        return get_lowest_load_worker_id();\n    } else if (dispatch_mode == DISPATCH_CONCURRENT_LB) {\n        return get_lowest_concurrent_worker_id();\n    }\n    // deliver tasks to idle worker processes\n    else {\n        return get_idle_worker_id();\n    }\n\n    return key % worker_num;\n}\n\n/**\n * [Master] send to client or append to out_buffer\n * @return SW_OK or SW_ERR\n */\nint Server::send_to_connection(const SendData *_send) const {\n    const SessionId session_id = _send->info.fd;\n    const char *_send_data = _send->data;\n    uint32_t _send_length = _send->info.len;\n\n    Connection *conn;\n    if (_send->info.type != SW_SERVER_EVENT_CLOSE) {\n        conn = get_connection_verify(session_id);\n    } else {\n        conn = get_connection_verify_no_ssl(session_id);\n    }\n    if (!conn) {\n        if (_send->info.type == SW_SERVER_EVENT_SEND_DATA) {\n            swoole_error_log(SW_LOG_TRACE,\n                             SW_ERROR_SESSION_NOT_EXIST,\n                             \"send %d byte failed, session#%ld does not exist\",\n                             _send_length,\n                             session_id);\n        } else {\n            swoole_error_log(SW_LOG_TRACE,\n                             SW_ERROR_SESSION_NOT_EXIST,\n                             \"send event[%d] failed, session#%ld does not exist\",\n                             _send->info.type,\n                             session_id);\n        }\n        return SW_ERR;\n    }\n\n    int fd = conn->fd;\n    Reactor *reactor = SwooleTG.reactor;\n    ListenPort *port = get_port_by_server_fd(conn->server_fd);\n\n    if (!single_thread) {\n        assert(fd % reactor_num == reactor->id);\n        assert(fd % reactor_num == SwooleTG.id);\n    }\n\n    if (!is_process_mode() && conn->overflow) {\n        if (send_yield) {\n            swoole_set_last_error(SW_ERROR_OUTPUT_SEND_YIELD);\n        } else {\n            swoole_error_log(SW_LOG_WARNING, SW_ERROR_OUTPUT_BUFFER_OVERFLOW, \"socket#%d output buffer overflow\", fd);\n        }\n        return SW_ERR;\n    }\n\n    Socket *_socket = conn->socket;\n\n    /**\n     * Reset send buffer, Immediately close the connection.\n     */\n    if (_send->info.type == SW_SERVER_EVENT_CLOSE && (conn->close_reset || conn->close_force || conn->peer_closed)) {\n        goto _close_fd;\n    }\n    /**\n     * pause recv data\n     */\n    else if (_send->info.type == SW_SERVER_EVENT_PAUSE_RECV) {\n        if (_socket->removed || !(_socket->events & SW_EVENT_READ)) {\n            return SW_OK;\n        }\n        if (_socket->events & SW_EVENT_WRITE) {\n            return reactor->set(conn->socket, SW_EVENT_WRITE);\n        } else {\n            return reactor->del(conn->socket);\n        }\n    }\n    /**\n     * resume recv data\n     */\n    else if (_send->info.type == SW_SERVER_EVENT_RESUME_RECV) {\n        if (!_socket->removed || (_socket->events & SW_EVENT_READ)) {\n            return SW_OK;\n        }\n        if (_socket->events & SW_EVENT_WRITE) {\n            return reactor->set(_socket, SW_EVENT_READ | SW_EVENT_WRITE);\n        } else {\n            return reactor->add(_socket, SW_EVENT_READ);\n        }\n    }\n\n    if (Buffer::empty(_socket->out_buffer)) {\n        /**\n         * close connection.\n         */\n        if (_send->info.type == SW_SERVER_EVENT_CLOSE) {\n        _close_fd:\n            reactor->close(reactor, _socket);\n            return SW_OK;\n        }\n        // Direct send\n        if (_send->info.type != SW_SERVER_EVENT_SEND_FILE) {\n            if (!_socket->direct_send) {\n                goto _buffer_send;\n            }\n\n        _direct_send:\n            ssize_t n = _socket->send(_send_data, _send_length, 0);\n            if (n == _send_length) {\n                conn->last_send_time = microtime();\n                return SW_OK;\n            } else if (n > 0) {\n                _send_data += n;\n                _send_length -= n;\n                goto _buffer_send;\n            } else if (errno == EINTR) {\n                goto _direct_send;\n            } else {\n                goto _buffer_send;\n            }\n        }\n        // buffer send\n        else {\n        _buffer_send:\n            if (!_socket->out_buffer) {\n                _socket->out_buffer = new Buffer(SW_SEND_BUFFER_SIZE);\n            }\n        }\n    }\n\n    // close connection\n    if (_send->info.type == SW_SERVER_EVENT_CLOSE) {\n        _socket->out_buffer->alloc(BufferChunk::TYPE_CLOSE, 0);\n        conn->close_queued = 1;\n    }\n    // sendfile to client\n    else if (_send->info.type == SW_SERVER_EVENT_SEND_FILE) {\n        auto *task = (SendfileTask *) _send_data;\n        if (conn->socket->sendfile_async(task->filename, task->offset, task->length) < 0) {\n            return false;\n        }\n    }\n    // send data\n    else {\n        // connection is closed\n        if (conn->peer_closed) {\n            swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED_BY_CLIENT, \"socket#%d is closed by client\", fd);\n            return false;\n        }\n        // connection output buffer overflow\n        if (_socket->out_buffer->length() >= _socket->buffer_size) {\n            if (send_yield) {\n                swoole_set_last_error(SW_ERROR_OUTPUT_SEND_YIELD);\n            } else {\n                swoole_error_log(\n                    SW_LOG_WARNING, SW_ERROR_OUTPUT_BUFFER_OVERFLOW, \"connection#%d output buffer overflow\", fd);\n            }\n            conn->overflow = 1;\n            if (onBufferEmpty && onBufferFull == nullptr) {\n                conn->high_watermark = 1;\n            }\n        }\n\n        _socket->out_buffer->append(_send_data, _send_length);\n        conn->send_queued_bytes = _socket->out_buffer->length();\n\n        if (onBufferFull && conn->high_watermark == 0 && _socket->out_buffer->length() >= port->buffer_high_watermark) {\n            notify(conn, SW_SERVER_EVENT_BUFFER_FULL);\n            conn->high_watermark = 1;\n        }\n    }\n\n    if (port->max_idle_time > 0 && _socket->send_timer == nullptr) {\n        const auto timeout_callback = get_timeout_callback(port, reactor, conn);\n        _socket->read_timeout = port->max_idle_time;\n        _socket->last_sent_time = time<std::chrono::milliseconds>(true);\n        _socket->send_timer = swoole_timer_add(sec2msec(port->max_idle_time), true, timeout_callback);\n        swoole_trace_log(SW_TRACE_SERVER,\n                         \"added send_timer[id=%ld], port->max_idle_time=%f\",\n                         _socket->send_timer->id,\n                         port->max_idle_time);\n    }\n\n    if (!_socket->isset_writable_event()) {\n        reactor->add_write_event(_socket);\n    }\n\n    return SW_OK;\n}\n\n/**\n * use in master process\n */\nbool Server::notify(Connection *conn, ServerEventType event) const {\n    DataHead notify_event{};\n    notify_event.type = event;\n    notify_event.reactor_id = conn->reactor_id;\n    notify_event.fd = conn->fd;\n    notify_event.server_fd = conn->server_fd;\n    return factory_->notify(&notify_event);\n}\n\n/**\n * @process Worker\n */\nbool Server::sendfile(SessionId session_id, const char *file, uint32_t l_file, off_t offset, size_t length) const {\n    if (sw_unlikely(session_id <= 0)) {\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_SESSION_INVALID_ID, \"invalid fd[%ld]\", session_id);\n        return false;\n    }\n\n    if (sw_unlikely(is_master())) {\n        swoole_error_log(\n            SW_LOG_ERROR, SW_ERROR_SERVER_SEND_IN_MASTER, \"can't send data to the connections in master process\");\n        return false;\n    }\n\n    char _buffer[SW_IPC_BUFFER_SIZE];\n    auto *req = reinterpret_cast<SendfileTask *>(_buffer);\n\n    // file name size\n    if (sw_unlikely(l_file > sizeof(_buffer) - sizeof(*req) - 1)) {\n        swoole_error_log(SW_LOG_WARNING,\n                         SW_ERROR_NAME_TOO_LONG,\n                         \"sendfile name[%.8s...] length %u is exceed the max name len %u\",\n                         file,\n                         l_file,\n                         (uint32_t)(SW_IPC_BUFFER_SIZE - sizeof(SendfileTask) - 1));\n        return false;\n    }\n    // string must be zero termination (for `state` system call)\n    swoole_strlcpy(req->filename, file, sizeof(_buffer) - sizeof(*req));\n\n    // check state\n    struct stat file_stat;\n    if (stat(req->filename, &file_stat) < 0) {\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, \"stat(%s) failed\", req->filename);\n        return false;\n    }\n    if (!S_ISREG(file_stat.st_mode)) {\n        swoole_error_log(SW_LOG_WARNING,\n                         SW_ERROR_SERVER_IS_NOT_REGULAR_FILE,\n                         \"the path[%s] given is not a regular file\",\n                         req->filename);\n        return false;\n    }\n    if (file_stat.st_size <= offset) {\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, \"file[offset=%ld] is empty\", (long) offset);\n        return false;\n    }\n    req->offset = offset;\n    req->length = length;\n\n    // construct send data\n    SendData send_data{};\n    send_data.info.fd = session_id;\n    send_data.info.type = SW_SERVER_EVENT_SEND_FILE;\n    send_data.info.len = sizeof(SendfileTask) + l_file + 1;\n    send_data.data = _buffer;\n\n    return factory_->finish(&send_data);\n}\n\n/**\n * [Worker] Returns the number of bytes sent\n */\nbool Server::sendwait(SessionId session_id, const void *data, uint32_t length) const {\n    Connection *conn = get_connection_verify(session_id);\n    if (!conn) {\n        swoole_error_log(SW_LOG_TRACE,\n                         SW_ERROR_SESSION_NOT_EXIST,\n                         \"send %d byte failed, because session#%ld is not exists\",\n                         length,\n                         session_id);\n        return false;\n    }\n    return conn->socket->send_sync(data, length) == length;\n}\n\nvoid Server::call_hook(HookType type, void *arg) {\n    assert(type <= HOOK_END);\n    swoole::hook_call(hooks, type, arg);\n}\n\n/**\n * [Worker]\n */\nbool Server::close(SessionId session_id, bool reset) const {\n    return factory_->end(session_id, reset ? (CLOSE_ACTIVELY | CLOSE_RESET) : CLOSE_ACTIVELY);\n}\n\nbool Server::send_pipe_message(WorkerId worker_id, EventData *msg) {\n    msg->info.type = SW_SERVER_EVENT_PIPE_MESSAGE;\n\n    return send_to_worker_from_worker(get_worker(worker_id), msg, msg->size(), SW_PIPE_MASTER | SW_PIPE_NONBLOCK) > 0;\n}\n\nbool Server::send_pipe_message(WorkerId worker_id, const char *data, size_t len) {\n    EventData buf;\n    if (!task_pack(&buf, data, len)) {\n        return false;\n    }\n    return send_pipe_message(worker_id, &buf);\n}\n\nvoid Server::init_signal_handler() const {\n    swoole_signal_set(SIGPIPE, nullptr);\n    swoole_signal_set(SIGHUP, nullptr);\n    if (is_process_mode()) {\n        swoole_signal_set(SIGCHLD, master_signal_handler);\n    } else {\n        swoole_signal_set(SIGIO, master_signal_handler);\n    }\n    swoole_signal_set(SIGUSR1, master_signal_handler);\n    swoole_signal_set(SIGUSR2, master_signal_handler);\n    swoole_signal_set(SIGTERM, master_signal_handler);\n    swoole_signal_set(SIGWINCH, master_signal_handler);\n#ifdef SIGRTMIN\n    swoole_signal_set(SIGRTMIN, master_signal_handler);\n#endif\n\n    if (SwooleG.signal_fd > 0) {\n        set_minfd(SwooleG.signal_fd);\n    }\n}\n\nvoid Server::timer_callback(Timer *timer, TimerNode *tnode) {\n    auto *serv = static_cast<Server *>(tnode->data);\n    time_t now = ::time(nullptr);\n    if (serv->scheduler_warning && serv->warning_time < now) {\n        serv->scheduler_warning = false;\n        serv->warning_time = now;\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_NO_IDLE_WORKER, \"No idle worker is available\");\n    }\n\n    auto task_pool = serv->get_task_worker_pool();\n    if (task_pool->scheduler_warning && task_pool->warning_time < now) {\n        task_pool->scheduler_warning = 0;\n        task_pool->warning_time = now;\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_NO_IDLE_WORKER, \"No idle task worker is available\");\n    }\n\n    if (serv->hooks[Server::HOOK_MASTER_TIMER]) {\n        serv->call_hook(Server::HOOK_MASTER_TIMER, serv);\n    }\n\n    if (!serv->is_running()) {\n        sw_reactor()->running = false;\n        serv->stop_master_thread();\n    }\n}\n\nint Server::add_worker(Worker *worker) {\n    if (is_created()) {\n        swoole_error_log(SW_LOG_ERROR, SW_ERROR_WRONG_OPERATION, \"must add worker before server is created\");\n        return SW_ERR;\n    }\n    user_worker_list.push_back(worker);\n    worker->id = user_worker_list.size() - 1;\n    return worker->id;\n}\n\nvoid Server::add_hook(Server::HookType type, const Callback &func, int push_back) {\n    swoole::hook_add(hooks, (int) type, func, push_back);\n}\n\nbool Server::add_command(const std::string &name, int accepted_process_types, const Command::Handler &func) {\n    if (is_started()) {\n        return false;\n    }\n    if (commands.find(name) != commands.end()) {\n        return false;\n    }\n    if (!is_base_mode() && pipe_command == nullptr) {\n        auto _pipe = new UnixSocket(false, SOCK_DGRAM);\n        if (!_pipe->ready()) {\n            delete _pipe;\n            return false;\n        }\n        pipe_command = _pipe;\n    }\n    int command_id = command_current_id++;\n    Command command{\n        command_id,\n        accepted_process_types,\n        name,\n    };\n    commands.emplace(name, command);\n    command_handlers[command_id] = func;\n    return true;\n}\n\nvoid Server::check_port_type(const ListenPort *ls) {\n    if (ls->is_dgram()) {\n        // dgram socket, setting socket buffer size\n        ls->socket->set_buffer_size(ls->socket_buffer_size);\n        have_dgram_sock = true;\n        dgram_port_num++;\n        if (ls->type == SW_SOCK_UDP) {\n            udp_socket_ipv4 = ls->socket;\n        } else if (ls->type == SW_SOCK_UDP6) {\n            udp_socket_ipv6 = ls->socket;\n        } else if (ls->type == SW_SOCK_UNIX_DGRAM) {\n            dgram_socket = ls->socket;\n        }\n    } else {\n        have_stream_sock = true;\n    }\n}\n\nbool Server::is_healthy_connection(double now, const Connection *conn) const {\n    if (conn->protect || conn->last_recv_time == 0) {\n        return true;\n    }\n    auto lp = get_port_by_session_id(conn->session_id);\n    if (!lp) {\n        return true;\n    }\n    if (lp->heartbeat_idle_time == 0) {\n        return true;\n    }\n    if (conn->last_recv_time > now - lp->heartbeat_idle_time) {\n        return true;\n    }\n    return false;\n}\n\n/**\n * Return the number of ports successfully\n */\nint Server::add_systemd_socket() {\n    int pid;\n    if (!swoole_get_env(\"LISTEN_PID\", &pid) && getpid() != pid) {\n        swoole_warning(\"invalid LISTEN_PID\");\n        return 0;\n    }\n\n    int n = swoole_get_systemd_listen_fds();\n    if (n <= 0) {\n        return 0;\n    }\n\n    int count = 0;\n\n    int start_fd;\n    if (!swoole_get_env(\"LISTEN_FDS_START\", &start_fd)) {\n        start_fd = SW_SYSTEMD_FDS_START;\n    } else if (start_fd < 0) {\n        swoole_warning(\"invalid LISTEN_FDS_START\");\n        return 0;\n    }\n\n    for (int sock = start_fd; sock < start_fd + n; sock++) {\n        std::unique_ptr<ListenPort> ptr(new ListenPort(this));\n        ListenPort *ls = ptr.get();\n\n        if (!ls->import(sock)) {\n            continue;\n        }\n\n        // O_NONBLOCK & O_CLOEXEC\n        ls->socket->set_fd_option(1, 1);\n\n        ptr.release();\n        check_port_type(ls);\n        ports.push_back(ls);\n        ls->object_id = ports.size();\n        count++;\n    }\n\n    return count;\n}\n\nListenPort *Server::add_port(SocketType type, const char *host, int port) {\n    if (is_created()) {\n        swoole_error_log(SW_LOG_ERROR, SW_ERROR_WRONG_OPERATION, \"must add port before server is created\");\n        return nullptr;\n    }\n    if (ports.size() >= SW_MAX_LISTEN_PORT) {\n        swoole_error_log(SW_LOG_ERROR,\n                         SW_ERROR_SERVER_TOO_MANY_LISTEN_PORT,\n                         \"up to %d listening ports are allowed\",\n                         SW_MAX_LISTEN_PORT);\n        return nullptr;\n    }\n    if (!Socket::is_local(type) && !Address::verify_port(port)) {\n        swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_INVALID_LISTEN_PORT, \"invalid port [%d]\", port);\n        return nullptr;\n    }\n    if (strlen(host) + 1 > SW_HOST_MAXSIZE) {\n        swoole_error_log(SW_LOG_ERROR,\n                         SW_ERROR_NAME_TOO_LONG,\n                         \"address '%s' exceeds the limit of %ld characters\",\n                         host,\n                         SW_HOST_MAXSIZE - 1);\n        return nullptr;\n    }\n\n    std::unique_ptr<ListenPort> ptr(new ListenPort(this));\n    ListenPort *ls = ptr.get();\n\n    ls->type = type;\n    ls->port = port;\n    ls->host = host;\n\n    if (type & SW_SOCK_SSL) {\n        type = static_cast<SocketType>(type & (~SW_SOCK_SSL));\n        ls->type = type;\n        ls->ssl = 1;\n        ls->ssl_context_init();\n    }\n\n    if (ls->create_socket() < 0) {\n        swoole_set_last_error(errno);\n        return nullptr;\n    }\n\n    check_port_type(ls);\n    ptr.release();\n    ports.push_back(ls);\n    ls->object_id = ports.size();\n    return ls;\n}\n\nvoid Server::master_signal_handler(int signo) {\n    swoole_trace_log(SW_TRACE_SERVER, \"signal[%d] %s triggered in %d\", signo, swoole_signal_to_str(signo), getpid());\n\n    Server *serv = sw_server();\n    if (!SwooleG.running || !serv || !serv->is_running()) {\n        return;\n    }\n\n    switch (signo) {\n    case SIGTERM:\n        serv->signal_handler_shutdown();\n        break;\n    case SIGCHLD:\n        serv->signal_handler_child_exit();\n        break;\n    case SIGUSR1:\n    case SIGUSR2:\n        serv->signal_handler_reload(signo == SIGUSR1);\n        break;\n    case SIGIO:\n        serv->signal_handler_read_message();\n        break;\n    case SIGWINCH:\n        serv->signal_handler_reopen_logger();\n        break;\n    default:\n#ifdef SIGRTMIN\n        if (signo == SIGRTMIN) {\n            serv->signal_handler_reopen_logger();\n        }\n#endif\n        break;\n    }\n}\n\nvoid Server::foreach_connection(const std::function<void(Connection *)> &callback) const {\n    for (int fd = get_minfd(); fd <= get_maxfd(); fd++) {\n        Connection *conn = get_connection(fd);\n        if (is_valid_connection(conn)) {\n            callback(conn);\n        }\n    }\n}\n\nvoid Server::abort_connection(Reactor *reactor, const ListenPort *ls, Socket *_socket) const {\n    sw_atomic_fetch_add(&gs->abort_count, 1);\n    sw_atomic_fetch_add(&ls->gs->abort_count, 1);\n    if (_socket->object) {\n        reactor->close(reactor, _socket);\n    } else {\n        _socket->free();\n    }\n}\n\n// see https://github.com/swoole/swoole-src/issues/5407\n// see https://github.com/swoole/swoole-src/issues/5432\nvoid Server::reset_worker_counter(Worker *worker) const {\n    auto value = worker->concurrency;\n    if (value > 0 && sw_atomic_value_cmp_set(&worker->concurrency, value, 0) == value) {\n        sw_atomic_sub_fetch(&gs->concurrency, value);\n        if ((int) gs->concurrency < 0) {\n            gs->concurrency = 0;\n        }\n    }\n    worker->request_count = 0;\n    worker->response_count = 0;\n    worker->dispatch_count = 0;\n}\n\nvoid Server::abort_worker(Worker *worker) const {\n    reset_worker_counter(worker);\n\n    if (is_base_mode()) {\n        SW_LOOP_N(SW_SESSION_LIST_SIZE) {\n            Session *session = get_session(i);\n            if (session->reactor_id == worker->id) {\n                session->fd = 0;\n            }\n        }\n    }\n}\n\nbool Server::init_network_interface_addr_map() {\n    struct ifaddrs *ifaddr = nullptr;\n    if (getifaddrs(&ifaddr) != 0) {\n        swoole_sys_warning(\"getifaddrs() failed\");\n        return false;\n    }\n\n    uint16_t index = 1;\n    for (struct ifaddrs *ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {\n        if (!ifa->ifa_addr || !(ifa->ifa_flags & IFF_UP)) {\n            continue;\n        }\n        network::Address na{};\n        if (ifa->ifa_addr->sa_family == AF_INET) {\n            struct sockaddr_in *sin = (struct sockaddr_in *) ifa->ifa_addr;\n            na.addr.inet_v4.sin_family = AF_INET;\n            na.addr.inet_v4.sin_addr = sin->sin_addr;\n            na.type = SW_SOCK_TCP;\n            local_addr_v4_map.emplace(index++, na);\n        } else if (ifa->ifa_addr->sa_family == AF_INET6) {\n            struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) ifa->ifa_addr;\n            na.addr.inet_v6.sin6_family = AF_INET6;\n            na.addr.inet_v6.sin6_addr = sin6->sin6_addr;\n            na.type = SW_SOCK_TCP6;\n            local_addr_v6_map.emplace(index++, na);\n        }\n    }\n\n    freeifaddrs(ifaddr);\n    return true;\n}\n\nuint16_t Server::get_local_addr_index(network::Address *addr) {\n    if (addr->type == SW_SOCK_TCP) {\n        for (auto kv : local_addr_v4_map) {\n            if (memcmp(addr->addr_v4(), kv.second.addr_v4(), sizeof(*addr->addr_v4())) == 0) {\n                return kv.first;\n            }\n        }\n    } else {\n        for (auto kv : local_addr_v6_map) {\n            if (memcmp(addr->addr_v6(), kv.second.addr_v6(), sizeof(*addr->addr_v6())) == 0) {\n                return kv.first;\n            }\n        }\n    }\n    return 0;\n}\n\nconst char *Server::get_local_addr(Connection *conn) {\n    if (conn->socket_type == SW_SOCK_TCP) {\n        auto iter = local_addr_v4_map.find(conn->local_addr_index);\n        if (iter != local_addr_v4_map.end()) {\n            return iter->second.get_addr();\n        } else {\n            return \"127.0.0.1\";\n        }\n    } else if (conn->socket_type == SW_SOCK_TCP6) {\n        auto iter = local_addr_v6_map.find(conn->local_addr_index);\n        if (iter != local_addr_v6_map.end()) {\n            return iter->second.get_addr();\n        } else {\n            return \"::1\";\n        }\n    } else {\n        return get_port_by_server_fd(conn->server_fd)->host.c_str();\n    }\n}\n\nconst char *Server::get_remote_addr(Connection *conn) {\n    return conn->info.get_addr();\n}\n\n/**\n * new connection\n */\nConnection *Server::add_connection(const ListenPort *ls, Socket *_socket, int server_fd) {\n    int fd = _socket->fd;\n\n    Connection *connection = &(connection_list[fd]);\n    ReactorId reactor_id = is_base_mode() ? swoole_get_worker_id() : fd % reactor_num;\n    *connection = {};\n\n    sw_spinlock(&gs->spinlock);\n    SessionId session_id = gs->session_round;\n    // get session id\n    SW_LOOP_N(max_connection) {\n        Session *session = get_session(++session_id);\n        // available slot\n        if (session->fd == 0) {\n            session->fd = fd;\n            session->id = session_id;\n            session->reactor_id = reactor_id;\n            goto _find_available_slot;\n        }\n    }\n    sw_spinlock_release(&gs->spinlock);\n    swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_TOO_MANY_SOCKET, \"no available session slot, fd=%d\", fd);\n    return nullptr;\n\n_find_available_slot:\n    sw_spinlock_release(&gs->spinlock);\n    gs->session_round = session_id;\n    connection->session_id = session_id;\n\n    _socket->object = connection;\n    _socket->removed = 1;\n    _socket->buffer_size = ls->socket_buffer_size;\n    _socket->write_timeout = _socket->read_timeout = 0;\n\n    // TCP Nodelay\n    if (ls->open_tcp_nodelay && ls->socket->is_tcp()) {\n        if (!_socket->set_tcp_nodelay()) {\n            swoole_sys_warning(\"setsockopt(TCP_NODELAY) failed\");\n        }\n        _socket->enable_tcp_nodelay = true;\n    }\n\n    // socket recv buffer size\n    if (ls->kernel_socket_recv_buffer_size > 0) {\n        if (ls->socket->set_option(SOL_SOCKET, SO_RCVBUF, ls->kernel_socket_recv_buffer_size) != 0) {\n            swoole_sys_warning(\"setsockopt(SO_RCVBUF, %d) failed\", ls->kernel_socket_recv_buffer_size);\n        }\n    }\n\n    // socket send buffer size\n    if (ls->kernel_socket_send_buffer_size > 0) {\n        if (ls->socket->set_option(SOL_SOCKET, SO_SNDBUF, ls->kernel_socket_send_buffer_size) != 0) {\n            swoole_sys_warning(\"setsockopt(SO_SNDBUF, %d) failed\", ls->kernel_socket_send_buffer_size);\n        }\n    }\n\n    connection->fd = fd;\n    connection->reactor_id = reactor_id;\n    connection->server_fd = server_fd;\n    connection->last_recv_time = connection->connect_time = microtime();\n    connection->active = 1;\n    connection->worker_id = -1;\n    connection->socket_type = ls->type;\n    connection->socket = _socket;\n    connection->ssl = _socket->ssl != nullptr;\n\n    memcpy(&connection->info.addr, &_socket->info.addr, _socket->info.len);\n    connection->info.len = _socket->info.len;\n    connection->info.type = connection->socket_type;\n\n    connection->socket->get_name();\n    connection->local_port = connection->socket->info.get_port();\n    connection->local_addr_index = get_local_addr_index(&connection->socket->info);\n\n    if (!ls->ssl) {\n        _socket->direct_send = 1;\n    }\n\n    lock();\n    if (fd > get_maxfd()) {\n        set_maxfd(fd);\n    } else if (fd < get_minfd()) {\n        set_minfd(fd);\n    }\n    unlock();\n\n    gs->accept_count++;\n    ls->gs->accept_count++;\n    if (is_base_mode()) {\n        sw_atomic_fetch_add(&gs->connection_nums[reactor_id], 1);\n        sw_atomic_fetch_add(&ls->gs->connection_nums[reactor_id], 1);\n    } else {\n        sw_atomic_fetch_add(&gs->connection_num, 1);\n        sw_atomic_fetch_add(&ls->gs->connection_num, 1);\n    }\n\n    return connection;\n}\n\nvoid Server::init_ipc_max_size() {\n    ipc_max_size = SW_IPC_BUFFER_MAX_SIZE;\n}\n\nvoid Server::init_pipe_sockets(MessageBus *mb) const {\n    assert(is_started());\n    size_t n = get_core_worker_num();\n\n    SW_LOOP_N(n) {\n        const auto worker = get_worker(i);\n        if (i >= worker_num && task_ipc_mode != TASK_IPC_UNIXSOCK) {\n            continue;\n        }\n        mb->init_pipe_socket(worker->pipe_master);\n        mb->init_pipe_socket(worker->pipe_worker);\n    }\n}\n\n/**\n * allocate memory for Server::pipe_buffers\n */\nint Server::create_pipe_buffers() {\n    message_bus.set_buffer_size(ipc_max_size);\n    return message_bus.alloc_buffer() ? SW_OK : SW_ERR;\n}\n\nvoid Server::release_pipe_buffers() {\n    message_bus.free_buffer();\n}\n\nuint32_t Server::get_idle_worker_num() const {\n    uint32_t idle_worker_num = 0;\n\n    for (uint32_t i = 0; i < worker_num; i++) {\n        Worker *worker = get_worker(i);\n        if (worker->is_idle()) {\n            idle_worker_num++;\n        }\n    }\n\n    return idle_worker_num;\n}\n\nint Server::get_idle_task_worker_num() const {\n    uint32_t idle_worker_num = 0;\n\n    for (uint32_t i = worker_num; i < (worker_num + task_worker_num); i++) {\n        const Worker *worker = get_worker(i);\n        if (worker->is_idle()) {\n            idle_worker_num++;\n        }\n    }\n\n    return idle_worker_num;\n}\n\nint Server::get_tasking_num() const {\n    // TODO Why need to reset ?\n    int tasking_num = gs->tasking_num;\n    if (tasking_num < 0) {\n        tasking_num = gs->tasking_num = 0;\n    }\n    return tasking_num;\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "src/server/port.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_server.h\"\n#include \"swoole_http.h\"\n#include \"swoole_http2.h\"\n#include \"swoole_websocket.h\"\n#include \"swoole_client.h\"\n#include \"swoole_mqtt.h\"\n#include \"swoole_redis.h\"\n#include \"swoole_ssl.h\"\n\nusing swoole::http_server::Request;\nusing swoole::network::Address;\nusing swoole::network::Socket;\n\nnamespace swoole {\n\nListenPort::ListenPort(Server *server) {\n    object_id = 0;\n    protocol.package_length_type = 'N';\n    protocol.package_length_size = 4;\n    protocol.package_body_offset = 4;\n    protocol.package_max_length = SW_INPUT_BUFFER_SIZE;\n\n    protocol.package_eof_len = sizeof(SW_DATA_EOF) - 1;\n    memcpy(protocol.package_eof, SW_DATA_EOF, protocol.package_eof_len);\n\n    protocol.private_data_2 = server;\n}\n\nbool ListenPort::ssl_add_sni_cert(const std::string &name, const std::shared_ptr<SSLContext> &ctx) {\n    if (!ssl_context_create(ctx.get())) {\n        return false;\n    }\n    sni_contexts.emplace(name, ctx);\n    return true;\n}\n\nbool ListenPort::ssl_matches_wildcard_name(const char *subject_name, const char *cert_name) {\n    const char *wildcard = nullptr;\n\n    if (strcasecmp(subject_name, cert_name) == 0) {\n        return true;\n    }\n\n    /* wildcard, if present, must only be present in the left-most component */\n    if (!((wildcard = strchr(cert_name, '*'))) || memchr(cert_name, '.', wildcard - cert_name)) {\n        return false;\n    }\n\n    /* 1) prefix, if not empty, must match subject */\n    ptrdiff_t prefix_len = wildcard - cert_name;\n    if (prefix_len && strncasecmp(subject_name, cert_name, prefix_len) != 0) {\n        return false;\n    }\n\n    size_t suffix_len = strlen(wildcard + 1);\n    size_t subject_len = strlen(subject_name);\n    if (suffix_len <= subject_len) {\n        /* 2) suffix must match\n         * 3) no . between prefix and suffix\n         **/\n        return strcasecmp(wildcard + 1, subject_name + subject_len - suffix_len) == 0 &&\n               memchr(subject_name + prefix_len, '.', subject_len - suffix_len - prefix_len) == nullptr;\n    }\n\n    return false;\n}\n\nint ListenPort::ssl_server_sni_callback(SSL *ssl, int *al, void *arg) {\n    const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);\n    if (!server_name) {\n        return SSL_TLSEXT_ERR_NOACK;\n    }\n\n    auto *port = static_cast<ListenPort *>(SSL_get_ex_data(ssl, swoole_ssl_get_ex_port_index()));\n\n    if (port->sni_contexts.empty()) {\n        return SSL_TLSEXT_ERR_NOACK;\n    }\n\n    for (auto &sni_context : port->sni_contexts) {\n        if (ssl_matches_wildcard_name(server_name, sni_context.first.c_str())) {\n            SSL_set_SSL_CTX(ssl, sni_context.second->get_context());\n            return SSL_TLSEXT_ERR_OK;\n        }\n    }\n\n    return SSL_TLSEXT_ERR_NOACK;\n}\n\n#ifdef SW_SUPPORT_DTLS\ndtls::Session *ListenPort::create_dtls_session(Socket *sock) const {\n    auto *session = new dtls::Session(sock, ssl_context);\n    if (!session->init()) {\n        delete session;\n        return nullptr;\n    }\n    dtls_sessions->emplace(sock->get_fd(), session);\n    return session;\n}\n#endif\n\nbool ListenPort::ssl_context_init() {\n    ssl_context = std::make_shared<SSLContext>();\n    ssl_context->prefer_server_ciphers = 1;\n    ssl_context->session_tickets = 0;\n    ssl_context->stapling = 1;\n    ssl_context->stapling_verify = 1;\n    ssl_context->ciphers = SW_SSL_CIPHER_LIST;\n    ssl_context->ecdh_curve = SW_SSL_ECDH_CURVE;\n\n    if (is_dgram()) {\n#ifdef SW_SUPPORT_DTLS\n        ssl_context->protocols = SW_SSL_DTLS;\n        dtls_sessions = new std::unordered_map<int, dtls::Session *>;\n#else\n        swoole_warning(\"DTLS support require openssl-1.1 or later\");\n        return false;\n#endif\n    }\n    return true;\n}\n\nbool ListenPort::ssl_init() const {\n    if (!ssl_context_create(ssl_context.get())) {\n        return false;\n    }\n    if (!sni_contexts.empty()) {\n        SSL_CTX_set_tlsext_servername_callback(ssl_context->get_context(), ssl_server_sni_callback);\n    }\n    return true;\n}\n\nbool ListenPort::ssl_create(Socket *sock) {\n    if (sock->ssl_create(ssl_context.get(), SW_SSL_SERVER) < 0) {\n        swoole_set_last_error(SW_ERROR_SSL_CREATE_SESSION_FAILED);\n        return false;\n    }\n    if (SSL_set_ex_data(sock->ssl, swoole_ssl_get_ex_port_index(), this) == 0) {\n        swoole_warning(\"SSL_set_ex_data() failed\");\n        return false;\n    }\n    return true;\n}\n\nbool ListenPort::ssl_context_create(SSLContext *context) const {\n    if (context->cert_file.empty() || context->key_file.empty()) {\n        swoole_error_log(SW_LOG_ERROR, SW_ERROR_WRONG_OPERATION, \"require `ssl_cert_file` and `ssl_key_file` options\");\n        return false;\n    }\n    if (open_http_protocol) {\n        context->http = 1;\n    }\n    if (open_http2_protocol) {\n        context->http_v2 = 1;\n    }\n    if (!context->create()) {\n        swoole_warning(\"failed to create ssl content\");\n        return false;\n    }\n    return true;\n}\n\nint ListenPort::listen() {\n    // listen stream socket\n    if (!listening && socket->listen(backlog) < 0) {\n        swoole_sys_warning(\"listen(%s:%d, %d) failed\", host.c_str(), port, backlog);\n        return SW_ERR;\n    }\n    listening = true;\n\n#ifdef TCP_DEFER_ACCEPT\n    if (tcp_defer_accept) {\n        if (socket->set_option(IPPROTO_TCP, TCP_DEFER_ACCEPT, tcp_defer_accept) != 0) {\n            swoole_sys_warning(\"setsockopt(TCP_DEFER_ACCEPT) failed\");\n        }\n    }\n#endif\n\n#ifdef SO_ACCEPTFILTER\n    if (tcp_defer_accept) {\n        struct accept_filter_arg a;\n        memset(&a, 0, sizeof(a));\n        strcpy(a.af_name, \"httpready\");\n        if (socket->set_option(SOL_SOCKET, SO_ACCEPTFILTER, &a, sizeof(a)) != 0) {\n            swoole_sys_warning(\"setsockopt(SO_ACCEPTFILTER) failed\");\n        }\n    }\n#endif\n\n#ifdef TCP_FASTOPEN\n    if (tcp_fastopen) {\n        if (socket->set_option(IPPROTO_TCP, TCP_FASTOPEN, tcp_fastopen) != 0) {\n            swoole_sys_warning(\"setsockopt(TCP_FASTOPEN) failed\");\n        }\n    }\n#endif\n\n#ifdef SO_KEEPALIVE\n    if (open_tcp_keepalive == 1) {\n        if (socket->set_option(SOL_SOCKET, SO_KEEPALIVE, 1) != 0) {\n            swoole_sys_warning(\"setsockopt(SO_KEEPALIVE) failed\");\n        }\n#ifdef TCP_KEEPIDLE\n        if (socket->set_option(IPPROTO_TCP, TCP_KEEPIDLE, tcp_keepidle) < 0) {\n            swoole_sys_warning(\"setsockopt(TCP_KEEPIDLE) failed\");\n        }\n        if (socket->set_option(IPPROTO_TCP, TCP_KEEPINTVL, tcp_keepinterval) < 0) {\n            swoole_sys_warning(\"setsockopt(TCP_KEEPINTVL) failed\");\n        }\n        if (socket->set_option(IPPROTO_TCP, TCP_KEEPCNT, tcp_keepcount) < 0) {\n            swoole_sys_warning(\"setsockopt(TCP_KEEPCNT) failed\");\n        }\n#endif\n#ifdef TCP_USER_TIMEOUT\n        if (tcp_user_timeout > 0 && socket->set_option(IPPROTO_TCP, TCP_USER_TIMEOUT, tcp_user_timeout) != 0) {\n            swoole_sys_warning(\"setsockopt(TCP_USER_TIMEOUT) failed\");\n        }\n#endif\n    }\n#endif\n\n    if (buffer_high_watermark == 0) {\n        buffer_high_watermark = socket_buffer_size * 0.8;\n    }\n\n    return SW_OK;\n}\n\nvoid ListenPort::init_protocol() {\n    if (is_dgram() && !is_dtls()) {\n        return;\n    }\n\n    if (open_eof_check) {\n        if (protocol.package_eof_len > SW_DATA_EOF_MAXLEN) {\n            protocol.package_eof_len = SW_DATA_EOF_MAXLEN;\n        }\n        protocol.onPackage = Server::dispatch_task;\n        onRead = readable_callback_eof;\n    } else if (open_length_check) {\n        if (protocol.package_length_type != '\\0') {\n            protocol.get_package_length = Protocol::default_length_func;\n        }\n        protocol.onPackage = Server::dispatch_task;\n        onRead = readable_callback_length;\n    } else if (open_http_protocol) {\n        if (open_http2_protocol && open_websocket_protocol) {\n            protocol.get_package_length = http_server::get_package_length;\n            protocol.get_package_length_size = http_server::get_package_length_size;\n            protocol.onPackage = http_server::dispatch_frame;\n        } else if (open_http2_protocol) {\n            protocol.package_length_size = SW_HTTP2_FRAME_HEADER_SIZE;\n            protocol.get_package_length = http2::get_frame_length;\n            protocol.onPackage = Server::dispatch_task;\n        } else if (open_websocket_protocol) {\n            protocol.package_length_size = SW_WEBSOCKET_FRAME_HEADER_SIZE;\n            protocol.get_package_length = websocket::get_package_length;\n            protocol.onPackage = websocket::dispatch_frame;\n        }\n        protocol.package_length_offset = 0;\n        protocol.package_body_offset = 0;\n        onRead = readable_callback_http;\n    } else if (open_mqtt_protocol) {\n        mqtt::set_protocol(&protocol);\n        protocol.onPackage = Server::dispatch_task;\n        onRead = readable_callback_length;\n    } else if (open_redis_protocol) {\n        protocol.onPackage = Server::dispatch_task;\n        onRead = readable_callback_redis;\n    } else {\n        onRead = readable_callback_raw;\n    }\n}\n\nvoid ListenPort::set_eof_protocol(const std::string &eof, bool find_from_right) {\n    open_eof_check = true;\n    protocol.split_by_eof = !find_from_right;\n    protocol.package_eof_len = std::min(eof.length(), sizeof(protocol.package_eof));\n    memcpy(protocol.package_eof, eof.c_str(), protocol.package_eof_len);\n}\n\nvoid ListenPort::set_length_protocol(uint32_t length_offset, char length_type, uint32_t body_offset) {\n    open_length_check = true;\n    protocol.package_length_type = length_type;\n    protocol.package_length_size = swoole_type_size(length_type);\n    protocol.package_length_offset = length_offset;\n    protocol.package_body_offset = body_offset;\n}\n\nvoid ListenPort::set_stream_protocol() {\n    open_length_check = true;\n    network::Stream::set_protocol(&protocol);\n}\n\n/**\n * @description: import listen port from socket-fd\n */\nbool ListenPort::import(int sock) {\n    int _type;\n\n    auto tmp_sock = socket = new Socket();\n    tmp_sock->fd = sock;\n\n    // get socket type\n    if (socket->get_option(SOL_SOCKET, SO_TYPE, &_type) < 0) {\n        swoole_sys_warning(\"getsockopt(%d, SOL_SOCKET, SO_TYPE) failed\", sock);\n    _fail:\n        tmp_sock->move_fd();\n        delete tmp_sock;\n        return false;\n    }\n\n    if (tmp_sock->get_name() < 0) {\n        swoole_sys_warning(\"getsockname(%d) failed\", sock);\n        goto _fail;\n    }\n\n    int optval;\n    if (tmp_sock->get_option(SOL_SOCKET, SO_ACCEPTCONN, &optval) < 0) {\n        swoole_sys_warning(\"getsockopt(%d, SOL_SOCKET, SO_ACCEPTCONN) failed\", sock);\n        goto _fail;\n    }\n\n    if (optval == 0) {\n        swoole_error_log(SW_LOG_WARNING, EINVAL, \"the socket[%d] is not a listening socket\", sock);\n        goto _fail;\n    }\n\n    socket = tmp_sock;\n    int _family = socket->info.addr.ss.sa_family;\n    socket->socket_type = socket->info.type = type = Socket::convert_to_type(_family, _type);\n    host = socket->info.get_addr();\n    port = socket->info.get_port();\n    listening = true;\n\n    socket->fd_type = socket->is_dgram() ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER;\n    socket->removed = 1;\n\n    return true;\n}\n\nvoid ListenPort::clear_protocol() {\n    open_eof_check = false;\n    open_length_check = false;\n    open_http_protocol = false;\n    open_websocket_protocol = false;\n    open_http2_protocol = false;\n    open_mqtt_protocol = false;\n    open_redis_protocol = false;\n}\n\nint ListenPort::readable_callback_raw(Reactor *reactor, ListenPort *port, Event *event) {\n    auto _socket = event->socket;\n    auto conn = static_cast<Connection *>(_socket->object);\n    auto serv = static_cast<Server *>(reactor->ptr);\n    auto buffer = serv->get_recv_buffer(_socket);\n    RecvData rdata{};\n\n    ssize_t n = _socket->recv(buffer->str, buffer->size, 0);\n    if (n < 0) {\n        switch (_socket->catch_read_error(errno)) {\n        case SW_ERROR:\n            swoole_sys_warning(\"recv from connection#%d failed\", event->fd);\n            return SW_OK;\n        case SW_CLOSE:\n            conn->close_errno = errno;\n            goto _close_fd;\n        default:\n            return SW_OK;\n        }\n    } else if (n == 0) {\n    _close_fd:\n        reactor->trigger_close_event(event);\n        return SW_OK;\n    } else {\n        buffer->offset = buffer->length = n;\n        rdata.info.len = n;\n        rdata.data = buffer->str;\n        return Server::dispatch_task(&port->protocol, _socket, &rdata);\n    }\n}\n\nint ListenPort::readable_callback_length(Reactor *reactor, ListenPort *port, Event *event) {\n    auto _socket = event->socket;\n    auto conn = static_cast<Connection *>(_socket->object);\n    auto protocol = &port->protocol;\n    auto serv = static_cast<Server *>(reactor->ptr);\n    auto buffer = serv->get_recv_buffer(_socket);\n\n    if (protocol->recv_with_length_protocol(_socket, buffer) < 0) {\n        swoole_trace(\"Close Event.FD=%d|From=%d\", event->fd, event->reactor_id);\n        conn->close_errno = errno;\n        reactor->trigger_close_event(event);\n    }\n\n    /**\n     * if the length is 0, which means the onPackage has been called, we can free the buffer.\n     */\n    if (_socket->recv_buffer && _socket->recv_buffer->length == 0 &&\n        _socket->recv_buffer->size > SW_BUFFER_SIZE_BIG * 2) {\n        delete _socket->recv_buffer;\n        _socket->recv_buffer = nullptr;\n    }\n\n    return SW_OK;\n}\n\n#define CLIENT_INFO_FMT \" from session#%ld on %s:%d\"\n#define CLIENT_INFO_ARGS conn->session_id, port->host.c_str(), port->port\n\n/**\n * For Http Protocol\n */\nint ListenPort::readable_callback_http(Reactor *reactor, ListenPort *port, Event *event) {\n    Socket *_socket = event->socket;\n    auto *conn = static_cast<Connection *>(_socket->object);\n    auto *serv = static_cast<Server *>(reactor->ptr);\n    RecvData dispatch_data{};\n\n    if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) {\n        if (conn->http_upgrade == 0) {\n            port->destroy_http_request(conn);\n            conn->websocket_status = websocket::STATUS_ACTIVE;\n            conn->http_upgrade = 1;\n        }\n        return readable_callback_length(reactor, port, event);\n    }\n\n    if (conn->http2_stream) {\n        return readable_callback_length(reactor, port, event);\n    }\n\n    Request *request = nullptr;\n    Protocol *protocol = &port->protocol;\n\n    if (conn->object == nullptr) {\n        request = new Request();\n        conn->object = request;\n    } else {\n        request = static_cast<Request *>(conn->object);\n    }\n\n    if (!request->buffer_) {\n        request->buffer_ = serv->get_recv_buffer(_socket);\n    }\n\n    String *buffer = request->buffer_;\n\n_recv_data:\n    ssize_t n = _socket->recv(buffer->str + buffer->length, buffer->size - buffer->length, 0);\n    if (n < 0) {\n        switch (_socket->catch_read_error(errno)) {\n        case SW_ERROR:\n            swoole_sys_warning(\"recv from connection#%d failed\", event->fd);\n            return SW_OK;\n        case SW_CLOSE:\n            conn->close_errno = errno;\n            goto _close_fd;\n        default:\n            return SW_OK;\n        }\n    }\n\n    if (n == 0) {\n        if (false) {\n        _bad_request:\n            _socket->send(SW_STRL(SW_HTTP_BAD_REQUEST_PACKET), 0);\n        }\n        if (false) {\n        _too_large:\n            _socket->send(SW_STRL(SW_HTTP_REQUEST_ENTITY_TOO_LARGE_PACKET), 0);\n        }\n        if (false) {\n        _unavailable:\n            _socket->send(SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET), 0);\n        }\n    _close_fd:\n        port->destroy_http_request(conn);\n        reactor->trigger_close_event(event);\n        return SW_OK;\n    }\n\n    buffer->length += n;\n\n_parse:\n    if (request->method == 0 && request->get_protocol() < 0) {\n        if (!request->excepted && buffer->length < SW_HTTP_HEADER_MAX_SIZE) {\n            return SW_OK;\n        }\n        swoole_error_log(SW_LOG_TRACE,\n                         SW_ERROR_HTTP_INVALID_PROTOCOL,\n                         \"Bad Request: unknown protocol\" CLIENT_INFO_FMT,\n                         CLIENT_INFO_ARGS);\n        goto _bad_request;\n    }\n\n    if (request->method > SW_HTTP_PRI) {\n        swoole_error_log(SW_LOG_TRACE,\n                         SW_ERROR_HTTP_INVALID_PROTOCOL,\n                         \"Bad Request: unknown HTTP method\" CLIENT_INFO_FMT,\n                         CLIENT_INFO_ARGS);\n        goto _bad_request;\n    } else if (request->method == SW_HTTP_PRI) {\n        if (sw_unlikely(!port->open_http2_protocol)) {\n            swoole_error_log(SW_LOG_TRACE,\n                             SW_ERROR_HTTP_INVALID_PROTOCOL,\n                             \"Bad Request: can not handle HTTP2 request\" CLIENT_INFO_FMT,\n                             CLIENT_INFO_ARGS);\n            goto _bad_request;\n        }\n        conn->http2_stream = 1;\n        http2::send_setting_frame(protocol, _socket);\n        if (buffer->length == sizeof(SW_HTTP2_PRI_STRING) - 1) {\n            port->destroy_http_request(conn);\n            buffer->clear();\n            return SW_OK;\n        }\n        buffer->reduce(buffer->offset);\n        port->destroy_http_request(conn);\n        conn->socket->skip_recv = 1;\n        return readable_callback_length(reactor, port, event);\n    }\n\n    // http header is not the end\n    if (request->header_length_ == 0) {\n        if (request->get_header_length() < 0) {\n            if (buffer->size == buffer->length) {\n                swoole_error_log(SW_LOG_TRACE,\n                                 SW_ERROR_HTTP_INVALID_PROTOCOL,\n                                 \"Bad Request: request header size is too large\" CLIENT_INFO_FMT,\n                                 CLIENT_INFO_ARGS);\n                goto _bad_request;\n            }\n            goto _recv_data;\n        }\n    }\n\n    // parse http header and got http body length\n    if (!request->header_parsed) {\n        request->parse_header_info();\n        request->max_length_ = protocol->package_max_length;\n        swoole_trace_log(SW_TRACE_SERVER,\n                         \"content-length=%\" PRIu64 \", keep-alive=%u, chunked=%u\",\n                         request->content_length_,\n                         request->keep_alive,\n                         request->chunked);\n        if (request->form_data_) {\n            if (serv->upload_max_filesize > 0 &&\n                request->header_length_ + request->content_length_ > request->max_length_) {\n                request->init_multipart_parser(serv);\n\n                buffer = request->buffer_;\n            } else {\n                delete request->form_data_;\n                request->form_data_ = nullptr;\n            }\n        }\n    }\n\n    if (request->form_data_) {\n        if (!request->multipart_header_parsed && memmem(buffer->str, buffer->length, SW_STRL(\"\\r\\n\\r\\n\")) == nullptr) {\n            return SW_OK;\n        }\n        if (!request->parse_multipart_data(buffer)) {\n            goto _bad_request;\n        }\n        if (request->too_large) {\n            goto _too_large;\n        }\n        if (request->unavailable) {\n            goto _unavailable;\n        }\n        if (!request->tried_to_dispatch) {\n            return SW_OK;\n        }\n        request->destroy_multipart_parser();\n        buffer = request->buffer_;\n    }\n\n    // content length (equal to 0) or (field not found but not chunked)\n    if (!request->tried_to_dispatch) {\n        // recv nobody_chunked eof\n        if (request->nobody_chunked) {\n            if (buffer->length < request->header_length_ + (sizeof(SW_HTTP_CHUNK_EOF) - 1)) {\n                goto _recv_data;\n            }\n            request->header_length_ += (sizeof(SW_HTTP_CHUNK_EOF) - 1);\n        }\n        request->tried_to_dispatch = 1;\n        // (know content-length is equal to 0) or (no content-length field and no chunked)\n        if (request->content_length_ == 0 && (request->known_length || !request->chunked)) {\n            buffer->offset = request->header_length_;\n            // send static file content directly in the reactor thread\n            if (!serv->enable_static_handler || !serv->select_static_handler(request, conn)) {\n                // dynamic request, dispatch to worker\n                dispatch_data.info.len = request->header_length_;\n                dispatch_data.data = buffer->str;\n                if (http_server::dispatch_request(serv, protocol, _socket, &dispatch_data) < 0) {\n                    goto _close_fd;\n                }\n            }\n            if (!conn->active || _socket->removed) {\n                return SW_OK;\n            }\n            if (buffer->length > request->header_length_) {\n                // http pipeline, multi requests, parse the next one\n                buffer->reduce(request->header_length_);\n                request->clean();\n                goto _parse;\n            } else {\n                port->destroy_http_request(conn);\n                buffer->clear();\n                return SW_OK;\n            }\n        }\n    }\n\n    size_t request_length;\n    if (request->chunked) {\n        /* unknown length, should find chunked eof */\n        if (request->get_chunked_body_length() < 0) {\n            if (request->excepted) {\n                swoole_error_log(SW_LOG_TRACE,\n                                 SW_ERROR_HTTP_INVALID_PROTOCOL,\n                                 \"Bad Request: protocol error when parse chunked length\" CLIENT_INFO_FMT,\n                                 CLIENT_INFO_ARGS);\n                goto _bad_request;\n            }\n            request_length = buffer->size + SW_BUFFER_SIZE_BIG;\n            if (request_length > protocol->package_max_length) {\n                swoole_error_log(SW_LOG_WARNING,\n                                 SW_ERROR_HTTP_INVALID_PROTOCOL,\n                                 \"Request Entity Too Large: request length (chunked) has already been greater than the \"\n                                 \"package_max_length(%u)\" CLIENT_INFO_FMT,\n                                 protocol->package_max_length,\n                                 CLIENT_INFO_ARGS);\n                goto _too_large;\n            }\n            if (buffer->length == buffer->size) {\n                buffer->extend(request_length);\n            }\n            goto _recv_data;\n        } else {\n            request_length = request->header_length_ + request->content_length_;\n        }\n        swoole_trace_log(\n            SW_TRACE_SERVER, \"received chunked eof, real content-length=%\" PRIu64, request->content_length_);\n    } else {\n        request_length = request->header_length_ + request->content_length_;\n        if (request_length > protocol->package_max_length) {\n            swoole_error_log(SW_LOG_WARNING,\n                             SW_ERROR_HTTP_INVALID_PROTOCOL,\n                             \"Request Entity Too Large: header-length (%u) + content-length (%\" PRIu64\n                             \") is greater than the \"\n                             \"package_max_length(%u)\" CLIENT_INFO_FMT,\n                             request->header_length_,\n                             request->content_length_,\n                             protocol->package_max_length,\n                             CLIENT_INFO_ARGS);\n            goto _too_large;\n        }\n\n        if (request_length > buffer->size) {\n            buffer->extend(request_length);\n        }\n\n        if (buffer->length < request_length) {\n            // Expect: 100-continue\n            if (request->has_expect_header()) {\n                _socket->send(SW_STRL(SW_HTTP_100_CONTINUE_PACKET), 0);\n            } else {\n                swoole_trace_log(\n                    SW_TRACE_SERVER,\n                    \"PostWait: request->content_length=%\" PRIu64 \", buffer->length=%zu, request->header_length=%d\\n\",\n                    request->content_length_,\n                    buffer->length,\n                    request->header_length_);\n            }\n            goto _recv_data;\n        }\n    }\n\n    // discard the redundant data\n    if (buffer->length > request_length) {\n        swoole_error_log(SW_LOG_TRACE,\n                         SW_ERROR_HTTP_INVALID_PROTOCOL,\n                         \"Invalid Request: %zu bytes has been discard\" CLIENT_INFO_FMT,\n                         buffer->length - request_length,\n                         CLIENT_INFO_ARGS);\n        buffer->length = request_length;\n    }\n\n    buffer->offset = request_length;\n    dispatch_data.data = buffer->str;\n    dispatch_data.info.len = buffer->length;\n\n    if (http_server::dispatch_request(serv, protocol, _socket, &dispatch_data) < 0) {\n        goto _close_fd;\n    }\n\n    if (conn->active && !_socket->removed) {\n        port->destroy_http_request(conn);\n        if (_socket->recv_buffer && _socket->recv_buffer->size > SW_BUFFER_SIZE_BIG * 2) {\n            delete _socket->recv_buffer;\n            _socket->recv_buffer = nullptr;\n        } else {\n            buffer->clear();\n        }\n    }\n\n    return SW_OK;\n}\n\nint ListenPort::readable_callback_redis(Reactor *reactor, ListenPort *port, Event *event) {\n    auto _socket = event->socket;\n    auto conn = static_cast<Connection *>(_socket->object);\n    auto protocol = &port->protocol;\n    auto serv = static_cast<Server *>(reactor->ptr);\n    auto buffer = serv->get_recv_buffer(_socket);\n\n    if (redis::recv_packet(protocol, conn, buffer) < 0) {\n        conn->close_errno = errno;\n        reactor->trigger_close_event(event);\n    }\n\n    return SW_OK;\n}\n\nint ListenPort::readable_callback_eof(Reactor *reactor, ListenPort *port, Event *event) {\n    Socket *_socket = event->socket;\n    auto *conn = static_cast<Connection *>(_socket->object);\n    Protocol *protocol = &port->protocol;\n    auto *serv = static_cast<Server *>(reactor->ptr);\n\n    String *buffer = serv->get_recv_buffer(_socket);\n    if (!buffer) {\n        reactor->trigger_close_event(event);\n        return SW_ERR;\n    }\n\n    if (protocol->recv_with_eof_protocol(_socket, buffer) < 0) {\n        conn->close_errno = errno;\n        reactor->trigger_close_event(event);\n    }\n\n    // If the length is 0, which means the onPackage has been called, we can free the buffer.\n    if (_socket->recv_buffer && _socket->recv_buffer->length == 0 &&\n        _socket->recv_buffer->size > SW_BUFFER_SIZE_BIG * 2) {\n        delete _socket->recv_buffer;\n        _socket->recv_buffer = nullptr;\n    }\n\n    return SW_OK;\n}\n\nvoid ListenPort::close() {\n    if (ssl) {\n        if (ssl_context) {\n            ssl_context.reset();\n        }\n#ifdef SW_SUPPORT_DTLS\n        delete dtls_sessions;\n#endif\n    }\n\n    if (socket) {\n        socket->free();\n        socket = nullptr;\n    }\n\n    // remove unix socket file\n    if (type == SW_SOCK_UNIX_STREAM || type == SW_SOCK_UNIX_DGRAM) {\n        unlink(host.c_str());\n    }\n}\n\nconst char *ListenPort::get_protocols() const {\n    if (is_dgram()) {\n        return \"dgram\";\n    }\n    if (open_eof_check) {\n        return \"eof\";\n    } else if (open_length_check) {\n        return \"length\";\n    } else if (open_http_protocol) {\n        if (open_http2_protocol && open_websocket_protocol) {\n            return \"http|http2|websocket\";\n        } else if (open_http2_protocol) {\n            return \"http|http2\";\n        } else if (open_websocket_protocol) {\n            return \"http|websocket\";\n        } else {\n            return \"http\";\n        }\n    } else if (open_mqtt_protocol) {\n        return \"mqtt\";\n    } else if (open_redis_protocol) {\n        return \"redis\";\n    } else {\n        return \"raw\";\n    }\n}\n\nsize_t ListenPort::get_connection_num() const {\n    if (gs->connection_nums) {\n        size_t num = 0;\n        for (uint32_t i = 0; i < sw_server()->worker_num; i++) {\n            num += gs->connection_nums[i];\n        }\n        return num;\n    } else {\n        return gs->connection_num;\n    }\n}\n\nint ListenPort::create_socket() {\n    auto *server = static_cast<Server *>(protocol.private_data_2);\n    if (socket) {\n#if defined(__linux__) && defined(HAVE_REUSEPORT)\n        if (server->enable_reuse_port) {\n            close_socket();\n        } else\n#endif\n        {\n            return SW_OK;\n        }\n    }\n\n    socket =\n        make_socket(type, is_dgram() ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK);\n    if (socket == nullptr) {\n        swoole_set_last_error(errno);\n        return SW_ERR;\n    }\n\n#if defined(SW_SUPPORT_DTLS) && defined(HAVE_KQUEUE)\n    if (is_dtls()) {\n        socket->set_reuse_port();\n    }\n#endif\n\n#if defined(__linux__) && defined(HAVE_REUSEPORT)\n    if (server->enable_reuse_port) {\n        if (socket->set_reuse_port() < 0) {\n            goto __cleanup;\n        }\n    }\n#endif\n\n    Address addr;\n    if (!addr.assign(type, host, port, true)) {\n        auto type_str = Address::type_str(type);\n        swoole_warning(\"Invalid %s address '%s:%d'\", type_str, host.c_str(), port);\n        goto __cleanup;\n    }\n\n    if (socket->set_reuse_addr() < 0) {\n        swoole_sys_warning(\"setsockopt(%d, SO_REUSEADDR) failed\", socket->get_fd());\n    }\n\n    if (socket->bind(addr) < 0) {\n        goto __cleanup;\n    }\n\n    if (socket->get_name() < 0) {\n    __cleanup:\n        swoole_set_last_error(errno);\n        socket->free();\n        return SW_ERR;\n    }\n\n    port = socket->get_port();\n\n    return SW_OK;\n}\n\nvoid ListenPort::close_socket() {\n    if (::close(socket->fd) < 0) {\n        swoole_sys_warning(\"close(%d) failed\", socket->fd);\n    }\n    delete socket;\n    socket = nullptr;\n}\n\nvoid ListenPort::destroy_http_request(Connection *conn) {\n    const auto request = static_cast<Request *>(conn->object);\n    if (!request) {\n        return;\n    }\n    delete request;\n    conn->object = nullptr;\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "src/server/process.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_server.h\"\n\nnamespace swoole {\n\nusing network::Socket;\n\nFactory *Server::create_process_factory() {\n    /**\n     * init reactor thread pool\n     */\n    reactor_threads = new ReactorThread[reactor_num]();\n    /**\n     * alloc the memory for connection_list\n     */\n    connection_list = static_cast<Connection *>(sw_shm_calloc(max_connection, sizeof(Connection)));\n    if (connection_list == nullptr) {\n        swoole_sys_warning(\"sw_shm_calloc(%u, %zu) for connection_list failed\", max_connection, sizeof(Connection));\n        return nullptr;\n    }\n    reactor_pipe_num = worker_num / reactor_num;\n\n    reactor_thread_barrier.init(false, reactor_num + 1);\n    gs->manager_barrier.init(true, 2);\n\n    return new ProcessFactory(this);\n}\n\nvoid Server::destroy_process_factory() {\n    sw_shm_free(connection_list);\n    delete[] reactor_threads;\n\n    reactor_thread_barrier.destroy();\n    gs->manager_barrier.destroy();\n\n    if (get_event_worker_pool()->message_box) {\n        get_event_worker_pool()->message_box->destroy();\n    }\n}\n\nProcessFactory::ProcessFactory(Server *server) : Factory(server) {}\n\nProcessFactory::~ProcessFactory() = default;\n\n/**\n * kill and wait all user process\n */\nvoid Factory::kill_user_workers() const {\n    if (server_->user_worker_map.empty()) {\n        return;\n    }\n\n    for (const auto &kv : server_->user_worker_map) {\n        swoole_kill(kv.second->pid, SIGTERM);\n    }\n\n    for (const auto &kv : server_->user_worker_map) {\n        int _stat_loc;\n        if (swoole_waitpid(kv.second->pid, &_stat_loc, 0) < 0) {\n            swoole_sys_warning(\"waitpid(%d) failed\", kv.second->pid);\n        }\n    }\n}\n\n/**\n * [Manager] kill and wait all event worker process\n */\nvoid Factory::kill_event_workers() const {\n    int status;\n\n    if (server_->worker_num == 0) {\n        return;\n    }\n\n    SW_LOOP_N(server_->worker_num) {\n        swoole_trace_log(SW_TRACE_SERVER, \"kill worker#%d[pid=%d]\", server_->workers[i].id, server_->workers[i].pid);\n        swoole_kill(server_->workers[i].pid, SIGTERM);\n    }\n    SW_LOOP_N(server_->worker_num) {\n        swoole_trace_log(SW_TRACE_SERVER, \"wait worker#%d[pid=%d]\", server_->workers[i].id, server_->workers[i].pid);\n        if (swoole_waitpid(server_->workers[i].pid, &status, 0) < 0) {\n            swoole_sys_warning(\"waitpid(%d) failed\", server_->workers[i].pid);\n        }\n    }\n}\n\n/**\n * [Manager] kill and wait task worker process\n */\nvoid Factory::kill_task_workers() const {\n    int status;\n    if (server_->task_worker_num == 0) {\n        return;\n    }\n\n    auto pool = server_->get_task_worker_pool();\n    pool->kill_all_workers(SIGTERM);\n\n    SW_LOOP_N(server_->task_worker_num) {\n        swoole_trace_log(SW_TRACE_SERVER, \"wait worker#%d[pid=%d]\", pool->workers[i].id, pool->workers[i].pid);\n        if (swoole_waitpid(pool->workers[i].pid, &status, 0) < 0) {\n            swoole_sys_warning(\"waitpid(%d) failed\", pool->workers[i].pid);\n        }\n    }\n}\n\npid_t Factory::spawn_event_worker(Worker *worker) const {\n    pid_t pid = swoole_fork(0);\n\n    if (pid < 0) {\n        swoole_sys_warning(\"failed to fork event worker\");\n        return SW_ERR;\n    } else if (pid == 0) {\n        worker->pid = getpid();\n        swoole_set_worker_id(worker->id);\n        swoole_set_worker_pid(worker->pid);\n        swoole_set_worker_type(SW_EVENT_WORKER);\n        SwooleWG.worker = worker;\n    } else {\n        worker->pid = pid;\n        return pid;\n    }\n\n    if (server_->is_base_mode()) {\n        server_->get_event_worker_pool()->main_loop(server_->get_event_worker_pool(), worker);\n    } else {\n        server_->start_event_worker(worker);\n    }\n\n    exit(0);\n    return 0;\n}\n\npid_t Factory::spawn_user_worker(Worker *worker) const {\n    pid_t pid = swoole_fork(0);\n    if (worker->pid) {\n        server_->user_worker_map.erase(worker->pid);\n    }\n    if (pid < 0) {\n        swoole_sys_warning(\"failed to spawn the user worker\");\n        return SW_ERR;\n    }\n    // child\n    else if (pid == 0) {\n        worker->pid = getpid();\n        swoole_set_worker_type(SW_USER_WORKER);\n        swoole_set_worker_id(worker->id);\n        swoole_set_worker_pid(worker->pid);\n        SwooleWG.worker = worker;\n        server_->onUserWorkerStart(server_, worker);\n        exit(0);\n    }\n    // parent\n    else {\n        /**\n         * worker: local memory\n         * user_workers: shared memory\n         */\n        server_->get_worker(worker->id)->pid = worker->pid = pid;\n        server_->user_worker_map.emplace(pid, worker);\n        return pid;\n    }\n}\n\npid_t Factory::spawn_task_worker(Worker *worker) const {\n    return server_->get_task_worker_pool()->spawn(worker);\n}\n\nvoid Factory::check_worker_exit_status(Worker *worker, const ExitStatus &exit_status) const {\n    if (exit_status.get_status() != 0) {\n        worker->report_error(exit_status);\n        server_->call_worker_error_callback(worker, exit_status);\n    }\n}\n\nbool ProcessFactory::shutdown() {\n    int status;\n\n    if (swoole_kill(server_->gs->manager_pid, SIGTERM) < 0) {\n        swoole_sys_warning(\"kill(%d) failed\", server_->gs->manager_pid);\n    }\n\n    if (swoole_waitpid(server_->gs->manager_pid, &status, 0) < 0) {\n        swoole_sys_warning(\"waitpid(%d) failed\", server_->gs->manager_pid);\n    }\n\n    return SW_OK;\n}\n\nbool Server::create_worker_pipes() {\n    SW_LOOP_N(worker_num) {\n        auto _sock = new UnixSocket(true, SOCK_DGRAM);\n        if (!_sock->ready()) {\n            delete _sock;\n            return false;\n        }\n\n        worker_pipes.emplace_back(_sock);\n        workers[i].pipe_master = _sock->get_socket(true);\n        workers[i].pipe_worker = _sock->get_socket(false);\n        workers[i].pipe_object = _sock;\n    }\n\n    init_ipc_max_size();\n    if (create_pipe_buffers() < 0) {\n        return false;\n    }\n    return true;\n}\n\nbool ProcessFactory::start() {\n    if (!server_->create_worker_pipes()) {\n        return false;\n    }\n    return server_->start_manager_process() == SW_OK;\n}\n\n/**\n * [ReactorThread] notify info to worker process\n */\nbool ProcessFactory::notify(DataHead *ev) {\n    SendData task;\n    task.info = *ev;\n    task.data = nullptr;\n    return dispatch(&task);\n}\n\n/**\n * [ReactorThread] dispatch request to worker\n */\nbool ProcessFactory::dispatch(SendData *task) {\n    // the task->info.fd is real fd, not session_id, it will be converted to session after dispatch\n    int fd = static_cast<int>(task->info.fd);\n\n    int target_worker_id = server_->schedule_worker(fd, task);\n    if (target_worker_id < 0) {\n        switch (target_worker_id) {\n        case Server::DISPATCH_RESULT_DISCARD_PACKET:\n            return false;\n        case Server::DISPATCH_RESULT_CLOSE_CONNECTION:\n            // TODO: close connection\n            return false;\n        default:\n            swoole_warning(\"invalid target worker id[%d]\", target_worker_id);\n            return false;\n        }\n    }\n\n    if (Server::is_stream_event(task->info.type)) {\n        Connection *conn = server_->get_connection(fd);\n        if (conn == nullptr || conn->active == 0) {\n            swoole_warning(\"dispatch[type=%d] failed, connection#%d is not active\", task->info.type, fd);\n            return false;\n        }\n        // server active close, discard data.\n        if (conn->closed) {\n            // Connection has been closed by server\n            if (!(task->info.type == SW_SERVER_EVENT_CLOSE && conn->close_force)) {\n                return false;\n            }\n        }\n        // converted fd to session_id\n        task->info.fd = conn->session_id;\n        task->info.server_fd = conn->server_fd;\n    }\n\n    Worker *worker = server_->get_worker(target_worker_id);\n\n    if (task->info.type == SW_SERVER_EVENT_RECV_DATA) {\n        sw_atomic_fetch_add(&worker->dispatch_count, 1);\n    }\n\n    SendData _task;\n    memcpy(&_task, task, sizeof(SendData));\n\n    MessageBus *mb = &server_->get_thread(swoole_get_thread_id())->message_bus;\n    Socket *sock = mb->get_pipe_socket(worker->pipe_master);\n    return mb->write(sock, &_task);\n}\n\nstatic bool process_is_supported_send_yield(Server *serv, const Connection *conn) {\n    if (!serv->is_hash_dispatch_mode()) {\n        return false;\n    } else {\n        return serv->schedule_worker(conn->fd, nullptr) == static_cast<int>(swoole_get_worker_id());\n    }\n}\n\n/**\n * [Worker] send to client, proxy by reactor\n */\nbool ProcessFactory::finish(SendData *resp) {\n    /**\n     * More than the output buffer\n     */\n    if (resp->info.len > server_->output_buffer_size) {\n        swoole_error_log(SW_LOG_WARNING,\n                         SW_ERROR_DATA_LENGTH_TOO_LARGE,\n                         \"The length of data [%u] exceeds the output buffer size[%u], \"\n                         \"please use the sendfile, chunked transfer mode or adjust the output_buffer_size\",\n                         resp->info.len,\n                         server_->output_buffer_size);\n        return false;\n    }\n\n    SessionId session_id = resp->info.fd;\n    Connection *conn;\n    if (resp->info.type != SW_SERVER_EVENT_CLOSE) {\n        conn = server_->get_connection_verify(session_id);\n    } else {\n        conn = server_->get_connection_verify_no_ssl(session_id);\n    }\n    if (!conn) {\n        if (resp->info.type != SW_SERVER_EVENT_CLOSE) {\n            swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, \"session#%ld does not exists\", session_id);\n        }\n        return false;\n    } else if ((conn->closed || conn->peer_closed) && resp->info.type != SW_SERVER_EVENT_CLOSE) {\n        swoole_error_log(SW_LOG_TRACE,\n                         SW_ERROR_SESSION_CLOSED,\n                         \"send %d bytes failed, because session#%ld is closed\",\n                         resp->info.len,\n                         session_id);\n        return false;\n    } else if (conn->overflow &&\n               (resp->info.type == SW_SERVER_EVENT_SEND_DATA || resp->info.type == SW_SERVER_EVENT_SEND_FILE)) {\n        if (server_->send_yield && process_is_supported_send_yield(server_, conn)) {\n            swoole_set_last_error(SW_ERROR_OUTPUT_SEND_YIELD);\n        } else {\n            swoole_error_log(SW_LOG_WARNING,\n                             SW_ERROR_OUTPUT_BUFFER_OVERFLOW,\n                             \"send failed, session=%ld output buffer overflow\",\n                             session_id);\n        }\n        return false;\n    }\n\n    SendData task;\n    memcpy(&task, resp, sizeof(SendData));\n    task.info.fd = session_id;\n    task.info.reactor_id = conn->reactor_id;\n    task.info.server_fd = swoole_get_worker_id();\n\n    swoole_trace(\"worker_id=%d, type=%d\", task.info.server_fd, task.info.type);\n\n    return server_->message_bus.write(server_->get_reactor_pipe_socket(session_id, task.info.reactor_id), &task);\n}\n\nbool ProcessFactory::end(SessionId session_id, int flags) {\n    SendData _send{};\n    DataHead info{};\n\n    _send.info.fd = session_id;\n    _send.info.len = 0;\n    _send.info.type = SW_SERVER_EVENT_CLOSE;\n\n    Connection *conn = server_->get_connection_verify_no_ssl(session_id);\n    if (!conn) {\n        swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, \"session#%ld does not exists\", session_id);\n        return false;\n    }\n    // Reset send buffer, Immediately close the connection.\n    if (flags & Server::CLOSE_RESET) {\n        conn->close_reset = 1;\n    }\n    // Server is initiative to close the connection\n    if (flags & Server::CLOSE_ACTIVELY) {\n        conn->close_actively = 1;\n    }\n\n    swoole_trace_log(SW_TRACE_CLOSE, \"session_id=%ld, fd=%d\", session_id, conn->fd);\n\n    DataHead ev = {};\n\n    /**\n     * Only close actively needs to determine whether it is in the process of connection binding.\n     * If the worker process is not currently bound to this connection,\n     * MUST forward to the correct worker process\n     */\n    if (conn->close_actively) {\n        Worker *worker;\n        bool hash = server_->is_hash_dispatch_mode();\n        int worker_id = hash ? server_->schedule_worker(conn->fd, nullptr) : conn->fd % server_->worker_num;\n        if (server_->is_worker() && (!hash || worker_id == (int) swoole_get_worker_id())) {\n            goto _close;\n        }\n        worker = server_->get_worker(worker_id);\n        ev.type = SW_SERVER_EVENT_CLOSE;\n        ev.fd = session_id;\n        ev.reactor_id = conn->reactor_id;\n        return server_->send_to_worker_from_worker(worker, &ev, sizeof(ev), SW_PIPE_MASTER) > 0;\n    }\n\n_close:\n    if (conn->closing) {\n        swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSING, \"session#%ld is closing\", session_id);\n        return false;\n    } else if (!(conn->close_force || conn->close_reset) && conn->closed) {\n        swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_CLOSED, \"session#%ld is closed\", session_id);\n        return false;\n    }\n\n    if (server_->onClose != nullptr && !conn->closed) {\n        info.fd = session_id;\n        if (conn->close_actively) {\n            info.reactor_id = -1;\n        } else {\n            info.reactor_id = conn->reactor_id;\n        }\n        info.server_fd = conn->server_fd;\n        conn->closing = 1;\n        server_->onClose(server_, &info);\n        conn->closing = 0;\n    }\n    conn->closed = 1;\n    conn->close_errno = 0;\n    return finish(&_send);\n}\n}  // namespace swoole\n"
  },
  {
    "path": "src/server/reactor_process.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_server.h\"\n\nnamespace swoole {\nusing network::Socket;\n\nstatic int ReactorProcess_onPipeRead(Reactor *reactor, Event *event);\nstatic int ReactorProcess_onClose(Reactor *reactor, Event *event);\nstatic void ReactorProcess_onTimeout(Timer *timer, TimerNode *tnode);\n\nint Server::start_reactor_processes() {\n    single_thread = true;\n\n    // listen TCP\n    if (have_stream_sock == 1) {\n        for (auto ls : ports) {\n            if (ls->is_stream()) {\n#if defined(__linux__) && defined(HAVE_REUSEPORT)\n                if (!enable_reuse_port) {\n#endif\n                    // listen server socket\n                    if (ls->listen() < 0) {\n                        return SW_ERR;\n                    }\n#if defined(__linux__) && defined(HAVE_REUSEPORT)\n                } else {\n                    ls->close_socket();\n                }\n#endif\n            }\n        }\n    }\n\n    ProcessPool *pool = get_event_worker_pool();\n    *pool = {};\n    if (pool->create(worker_num, 0, SW_IPC_UNIXSOCK) < 0) {\n        return SW_ERR;\n    }\n    pool->set_max_request(max_request, max_request_grace);\n\n    /**\n     * store to ProcessPool object\n     */\n    pool->ptr = this;\n    pool->max_wait_time = max_wait_time;\n    pool->use_msgqueue = 0;\n    pool->main_loop = reactor_process_main_loop;\n    pool->onWorkerNotFound = wait_other_worker;\n    memcpy(workers, pool->workers, sizeof(*workers) * worker_num);\n    pool->workers = workers;\n\n    SW_LOOP_N(worker_num) {\n        pool->workers[i].pool = pool;\n        pool->workers[i].id = i;\n        pool->workers[i].type = SW_WORKER;\n    }\n\n    init_ipc_max_size();\n    if (create_pipe_buffers() < 0) {\n        return SW_ERR;\n    }\n\n    if (is_single_worker()) {\n        Worker *worker = &pool->workers[0];\n        SwooleWG.worker = worker;\n        int retval = reactor_process_main_loop(pool, worker);\n        if (retval == SW_OK) {\n            pool->destroy();\n        }\n        return retval;\n    }\n\n    return start_manager_process();\n}\n\nstatic int ReactorProcess_onPipeRead(Reactor *reactor, Event *event) {\n    SendData _send;\n    auto *serv = static_cast<Server *>(reactor->ptr);\n    auto *factory = serv->factory_;\n    auto *pipe_buffer = serv->message_bus.get_buffer();\n    auto *worker = serv->get_worker(reactor->id);\n\n    ssize_t retval = serv->message_bus.read(event->socket);\n    if (retval <= 0) {\n        return SW_OK;\n    }\n\n    switch (pipe_buffer->info.type) {\n    case SW_SERVER_EVENT_PIPE_MESSAGE: {\n        serv->onPipeMessage(serv, reinterpret_cast<EventData *>(pipe_buffer));\n        break;\n    }\n    case SW_SERVER_EVENT_FINISH: {\n        serv->onFinish(serv, reinterpret_cast<EventData *>(pipe_buffer));\n        break;\n    }\n    case SW_SERVER_EVENT_SHUTDOWN: {\n        serv->stop_async_worker(worker);\n        break;\n    }\n    case SW_SERVER_EVENT_SEND_FILE: {\n        _send.info = pipe_buffer->info;\n        _send.data = pipe_buffer->data;\n        factory->finish(&_send);\n        break;\n    }\n    case SW_SERVER_EVENT_SEND_DATA: {\n        if (pipe_buffer->info.reactor_id < 0 || pipe_buffer->info.reactor_id >= (int16_t) serv->get_all_worker_num()) {\n            swoole_warning(\"invalid worker_id=%d\", pipe_buffer->info.reactor_id);\n            return SW_OK;\n        }\n        auto packet = serv->message_bus.get_packet();\n        memcpy(&_send.info, &pipe_buffer->info, sizeof(_send.info));\n        _send.info.type = SW_SERVER_EVENT_RECV_DATA;\n        _send.data = packet.data;\n        _send.info.len = packet.length;\n        factory->finish(&_send);\n        break;\n    }\n    case SW_SERVER_EVENT_CLOSE:\n    case SW_SERVER_EVENT_CLOSE_FORWARD: {\n        factory->end(pipe_buffer->info.fd, Server::CLOSE_ACTIVELY);\n        break;\n    }\n    case SW_SERVER_EVENT_COMMAND_REQUEST: {\n        serv->call_command_handler(serv->message_bus, sw_worker()->id, serv->get_worker(0)->pipe_master);\n        break;\n    }\n    case SW_SERVER_EVENT_COMMAND_RESPONSE: {\n        int64_t request_id = pipe_buffer->info.fd;\n        auto packet = serv->message_bus.get_packet();\n        serv->call_command_callback(request_id, std::string(packet.data, packet.length));\n        break;\n    }\n    default:\n        break;\n    }\n\n    serv->message_bus.pop();\n\n    return SW_OK;\n}\n\nint Server::reactor_process_main_loop(ProcessPool *pool, Worker *worker) {\n    auto *serv = static_cast<Server *>(pool->ptr);\n    swoole_set_worker_type(SW_EVENT_WORKER);\n    swoole_set_worker_id(worker->id);\n    swoole_set_worker_pid(getpid());\n\n    serv->init_event_worker(worker);\n\n    if (!SwooleTG.reactor) {\n        if (swoole_event_init(0) < 0) {\n            return SW_ERR;\n        }\n    }\n\n    Reactor *reactor = SwooleTG.reactor;\n\n    if (SwooleTG.timer && SwooleTG.timer->get_reactor() == nullptr) {\n        SwooleTG.timer->reinit();\n    }\n\n    serv->worker_signal_init();\n\n    serv->gs->connection_nums[worker->id] = 0;\n\n    for (auto ls : serv->ports) {\n#if defined(__linux__) and defined(HAVE_REUSEPORT)\n        if (ls->is_stream() && serv->enable_reuse_port) {\n            if (ls->create_socket() < 0) {\n                swoole_event_free();\n                return SW_ERR;\n            }\n\n            if (ls->listen() < 0) {\n                return SW_ERR;\n            }\n        }\n#endif\n        ls->gs->connection_nums[worker->id] = 0;\n        if (reactor->add(ls->socket, SW_EVENT_READ) < 0) {\n            return SW_ERR;\n        }\n    }\n\n    reactor->id = worker->id;\n    reactor->ptr = serv;\n    reactor->max_socket = serv->get_max_connection();\n\n    reactor->close = close_connection;\n\n    // set event handler\n    // connect\n    reactor->set_handler(SW_FD_STREAM_SERVER, SW_EVENT_READ, accept_connection);\n    // close\n    reactor->default_error_handler = ReactorProcess_onClose;\n    // pipe\n    reactor->set_handler(SW_FD_PIPE, SW_EVENT_READ, ReactorProcess_onPipeRead);\n\n    serv->store_listen_socket();\n\n    if (worker->pipe_worker) {\n        worker->pipe_worker->set_nonblock();\n        worker->pipe_master->set_nonblock();\n        if (reactor->add(worker->pipe_worker, SW_EVENT_READ) < 0) {\n            return SW_ERR;\n        }\n        if (reactor->add(worker->pipe_master, SW_EVENT_READ) < 0) {\n            return SW_ERR;\n        }\n    }\n\n    // task workers\n    if (serv->task_worker_num > 0) {\n        if (serv->task_ipc_mode == Server::TASK_IPC_UNIXSOCK) {\n            SW_LOOP_N(serv->get_task_worker_pool()->worker_num) {\n                serv->get_task_worker_pool()->workers[i].pipe_master->set_nonblock();\n            }\n        }\n    }\n\n    serv->init_reactor(reactor);\n\n    if (worker->id == 0) {\n        serv->gs->master_pid = getpid();\n        if (serv->onStart && !serv->gs->onstart_called) {\n            serv->gs->onstart_called = true;\n            serv->onStart(serv);\n        }\n    }\n\n    if ((serv->master_timer = swoole_timer_add(1000L, true, timer_callback, serv)) == nullptr) {\n    _fail:\n        swoole_event_free();\n        return SW_ERR;\n    }\n\n    serv->worker_start_callback(worker);\n\n    /**\n     * for heartbeat check\n     */\n    if (serv->heartbeat_check_interval > 0) {\n        serv->heartbeat_timer =\n            swoole_timer_add(sec2msec(serv->heartbeat_check_interval), true, ReactorProcess_onTimeout, reactor);\n        if (serv->heartbeat_timer == nullptr) {\n            goto _fail;\n        }\n    }\n\n    int retval = reactor->wait();\n\n    /**\n     * Close all connections\n     */\n    serv->foreach_connection([serv](Connection *conn) { serv->close(conn->session_id, true); });\n\n    /**\n     * call internal serv hooks\n     */\n    if (serv->isset_hook(HOOK_WORKER_CLOSE)) {\n        void *hook_args[2];\n        hook_args[0] = serv;\n        hook_args[1] = (void *) (uintptr_t) worker->id;\n        serv->call_hook(HOOK_WORKER_CLOSE, hook_args);\n    }\n\n    swoole_event_free();\n    serv->worker_stop_callback(worker);\n\n    return retval;\n}\n\nstatic int ReactorProcess_onClose(Reactor *reactor, Event *event) {\n    int fd = event->fd;\n    auto *serv = (Server *) reactor->ptr;\n    Connection *conn = serv->get_connection(fd);\n    if (conn == nullptr || conn->active == 0) {\n        return SW_ERR;\n    }\n    if (event->socket->removed) {\n        return Server::close_connection(reactor, event->socket);\n    }\n    if (reactor->del(event->socket) == 0) {\n        if (conn->close_queued) {\n            return Server::close_connection(reactor, event->socket);\n        } else {\n            /**\n             * peer_closed indicates that the client has closed the connection\n             * and the connection is no longer available.\n             */\n            conn->peer_closed = 1;\n            return serv->notify(conn, SW_SERVER_EVENT_CLOSE) ? SW_OK : SW_ERR;\n        }\n    } else {\n        return SW_ERR;\n    }\n}\n\nstatic void ReactorProcess_onTimeout(Timer *timer, TimerNode *tnode) {\n    auto *reactor = static_cast<Reactor *>(tnode->data);\n    auto *serv = static_cast<Server *>(reactor->ptr);\n    Event notify_ev{};\n    double now = microtime();\n\n    notify_ev.type = SW_FD_SESSION;\n\n    serv->foreach_connection([serv, reactor, now, &notify_ev](Connection *conn) {\n        if (serv->is_healthy_connection(now, conn)) {\n            return;\n        }\n        if (conn->socket->ssl && conn->socket->ssl_state != SW_SSL_STATE_READY) {\n            Server::close_connection(reactor, conn->socket);\n            return;\n        }\n        if (serv->disable_notify || conn->close_force) {\n            Server::close_connection(reactor, conn->socket);\n            return;\n        }\n        conn->close_force = 1;\n        notify_ev.fd = conn->fd;\n        notify_ev.socket = conn->socket;\n        notify_ev.reactor_id = conn->reactor_id;\n        ReactorProcess_onClose(reactor, &notify_ev);\n    });\n}\n}  // namespace swoole\n"
  },
  {
    "path": "src/server/reactor_thread.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_server.h\"\n#include \"swoole_hash.h\"\n#include \"swoole_util.h\"\n\n#include <cassert>\n\nusing std::unordered_map;\n\nnamespace swoole {\nusing namespace network;\n\nstatic int ReactorThread_onPipeWrite(Reactor *reactor, Event *ev);\nstatic int ReactorThread_onPipeRead(Reactor *reactor, Event *ev);\nstatic int ReactorThread_onRead(Reactor *reactor, Event *ev);\nstatic int ReactorThread_onWrite(Reactor *reactor, Event *ev);\nstatic int ReactorThread_onPacketReceived(Reactor *reactor, Event *event);\nstatic int ReactorThread_onClose(Reactor *reactor, Event *event);\nstatic void ReactorThread_resume_data_receiving(Timer *timer, TimerNode *tnode);\n\nstatic inline ReturnCode ReactorThread_verify_ssl_state(Reactor *reactor, ListenPort *port, Socket *_socket) {\n    auto serv = static_cast<Server *>(reactor->ptr);\n    if (!_socket->ssl || _socket->ssl_state == SW_SSL_STATE_READY) {\n        return SW_CONTINUE;\n    }\n\n    ReturnCode code = _socket->ssl_accept();\n    if (code != SW_READY) {\n        return code;\n    }\n\n    auto conn = static_cast<Connection *>(_socket->object);\n    conn->ssl_ready = 1;\n    if (!port->get_ssl_client_cert_file().empty()) {\n        if (!_socket->ssl_get_peer_certificate(sw_tg_buffer())) {\n            if (port->get_ssl_verify_peer()) {\n                return SW_ERROR;\n            }\n        } else {\n            if (!port->get_ssl_verify_peer() || _socket->ssl_verify(port->get_ssl_allow_self_signed())) {\n                SendData task;\n                task.info.fd = _socket->fd;\n                task.info.type = SW_SERVER_EVENT_CONNECT;\n                task.info.reactor_id = reactor->id;\n                task.info.len = sw_tg_buffer()->length;\n                task.data = sw_tg_buffer()->str;\n                serv->factory_->dispatch(&task);\n                goto _delay_receive;\n            } else {\n                return SW_ERROR;\n            }\n        }\n    }\n\n    if (serv->onConnect) {\n        serv->notify(static_cast<Connection *>(_socket->object), SW_SERVER_EVENT_CONNECT);\n    }\n_delay_receive:\n    if (serv->enable_delay_receive) {\n        if (reactor->del(_socket) < 0) {\n            return SW_ERROR;\n        }\n    }\n\n    return SW_READY;\n}\n\n/**\n * for udp\n */\nstatic int ReactorThread_onPacketReceived(Reactor *reactor, Event *event) {\n    int fd = event->fd;\n\n    auto serv = static_cast<Server *>(reactor->ptr);\n    Connection *server_sock = serv->get_connection(fd);\n    Socket *sock = server_sock->socket;\n    SendData task = {};\n    auto pkt = reinterpret_cast<DgramPacket *>(sw_tg_buffer()->str);\n\n    task.info.server_fd = fd;\n    task.info.reactor_id = SwooleTG.id;\n    task.info.type = SW_SERVER_EVENT_RECV_DGRAM;\n    task.info.time = microtime();\n\n    pkt->socket_addr.type = pkt->socket_type = server_sock->socket_type;\n\n_do_recvfrom:\n\n    ssize_t ret = sock->recvfrom(pkt->data, sw_tg_buffer()->size - sizeof(*pkt), 0, &pkt->socket_addr);\n    if (ret <= 0) {\n        if (errno == EAGAIN) {\n            return SW_OK;\n        } else {\n            swoole_sys_warning(\"recvfrom(%d) failed\", fd);\n            return SW_ERR;\n        }\n    }\n\n#ifdef SW_SUPPORT_DTLS\n    auto port = static_cast<ListenPort *>(server_sock->object);\n\n    if (port->is_dtls()) {\n        dtls::Session *session = serv->accept_dtls_connection(port, &pkt->socket_addr);\n        if (!session) {\n            return SW_ERR;\n        }\n\n        session->append(pkt->data, ret);\n\n        if (!session->listen()) {\n            return Server::close_connection(reactor, session->socket);\n        }\n\n        auto conn = static_cast<Connection *>(session->socket->object);\n        if (serv->single_thread) {\n            if (serv->connection_incoming(reactor, conn) < 0) {\n                reactor->close(reactor, session->socket);\n                return SW_OK;\n            }\n        } else {\n            DataHead ev{};\n            ev.type = SW_SERVER_EVENT_INCOMING;\n            ev.fd = conn->session_id;\n            ev.reactor_id = conn->reactor_id;\n            if (serv->send_to_reactor_thread(reinterpret_cast<EventData *>(&ev), sizeof(ev), conn->session_id) < 0) {\n                reactor->close(reactor, session->socket);\n                return SW_OK;\n            }\n        }\n\n        return SW_OK;\n    }\n#endif\n\n    if (pkt->socket_type == SW_SOCK_UDP) {\n        task.info.fd = *reinterpret_cast<int *>(&pkt->socket_addr.addr.inet_v4.sin_addr);\n    } else {\n        task.info.fd = swoole_crc32(pkt->socket_addr.get_addr(), pkt->socket_addr.len);\n    }\n\n    pkt->length = ret;\n    task.info.len = sizeof(*pkt) + ret;\n    task.data = reinterpret_cast<char *>(pkt);\n\n    if (!serv->factory_->dispatch(&task)) {\n        return SW_ERR;\n    } else {\n        goto _do_recvfrom;\n    }\n}\n\n/**\n * close connection\n */\nint Server::close_connection(Reactor *reactor, Socket *socket) {\n    auto serv = static_cast<Server *>(reactor->ptr);\n    auto conn = static_cast<Connection *>(socket->object);\n    ListenPort *port = serv->get_port_by_fd(socket->fd);\n\n    if (conn->timer) {\n        swoole_timer_del(conn->timer);\n    }\n\n    if (!socket->removed && reactor->del(socket) < 0) {\n        return SW_ERR;\n    }\n\n    sw_atomic_fetch_add(&serv->gs->close_count, 1);\n    sw_atomic_fetch_add(&port->gs->close_count, 1);\n\n    if (serv->is_base_mode()) {\n        sw_atomic_fetch_sub(&serv->gs->connection_nums[reactor->id], 1);\n        sw_atomic_fetch_sub(&port->gs->connection_nums[reactor->id], 1);\n    } else {\n        sw_atomic_fetch_sub(&serv->gs->connection_num, 1);\n        sw_atomic_fetch_sub(&port->gs->connection_num, 1);\n    }\n\n    swoole_trace(\"Close Event.fd=%d|from=%d\", socket->fd, reactor->id);\n\n    if (socket->ssl) {\n        conn->socket->ssl_quiet_shutdown = conn->peer_closed;\n        conn->socket->ssl_close();\n    }\n#ifdef SW_SUPPORT_DTLS\n    if (socket->dtls) {\n        dtls::Session *session = port->dtls_sessions->find(socket->fd)->second;\n        port->dtls_sessions->erase(socket->fd);\n        delete session;\n    }\n#endif\n\n    // free the reception memory buffer\n    if (socket->recv_buffer) {\n        delete socket->recv_buffer;\n        socket->recv_buffer = nullptr;\n    }\n\n    if (port->open_http_protocol && conn->object) {\n        port->destroy_http_request(conn);\n    }\n    if (port->open_redis_protocol && conn->object) {\n        sw_free(conn->object);\n        conn->object = nullptr;\n    }\n\n#ifdef SW_USE_SOCKET_LINGER\n    if (conn->close_force || conn->close_reset) {\n        struct linger linger;\n        linger.l_onoff = 1;\n        linger.l_linger = 0;\n        if (conn->socket->set_option(SOL_SOCKET, SO_LINGER, &linger, sizeof(struct linger)) != 0) {\n            swoole_sys_warning(\"setsockopt(SO_LINGER) failed\");\n        }\n    }\n#endif\n\n    Session *session = serv->get_session(conn->session_id);\n    session->fd = 0;\n    /**\n     * reset maxfd, for connection_list\n     */\n    int fd = socket->fd;\n\n    serv->lock();\n    if (fd == serv->get_maxfd()) {\n        int find_max_fd = fd - 1;\n        swoole_trace_log(SW_TRACE_SERVER, \"set_maxfd=%d|close_fd=%d\", find_max_fd, fd);\n        // find the new max_fd\n        for (; !serv->is_valid_connection(serv->get_connection(find_max_fd)) && find_max_fd > serv->get_minfd();\n             find_max_fd--) {\n            // pass\n        }\n        serv->set_maxfd(find_max_fd);\n    }\n    serv->unlock();\n\n    *conn = {};\n    return Reactor::_close(reactor, socket);\n}\n\n/**\n * close the connection\n */\nstatic int ReactorThread_onClose(Reactor *reactor, Event *event) {\n    auto *serv = static_cast<Server *>(reactor->ptr);\n    int fd = event->fd;\n    DataHead notify_ev{};\n    Socket *socket = event->socket;\n\n    assert(fd % serv->reactor_num == reactor->id);\n    assert(fd % serv->reactor_num == SwooleTG.id);\n\n    notify_ev.reactor_id = reactor->id;\n    notify_ev.fd = fd;\n    notify_ev.type = SW_SERVER_EVENT_CLOSE;\n\n    swoole_trace_log(SW_TRACE_CLOSE, \"client[fd=%d] close the connection\", fd);\n\n    Connection *conn = serv->get_connection(fd);\n    if (conn == nullptr || conn->active == 0) {\n        return SW_ERR;\n    } else if (serv->disable_notify) {\n        Server::close_connection(reactor, socket);\n        return SW_OK;\n    } else if (reactor->del(socket) == 0) {\n        if (conn->close_queued) {\n            Server::close_connection(reactor, socket);\n            return SW_OK;\n        } else {\n            /**\n             * peer_closed indicates that the client has closed the connection\n             * and the connection is no longer available.\n             */\n            conn->peer_closed = 1;\n            return serv->factory_->notify(&notify_ev);\n        }\n    } else {\n        return SW_ERR;\n    }\n}\n\nvoid ReactorThread::shutdown(Reactor *reactor) {\n    auto *serv = static_cast<Server *>(reactor->ptr);\n    // stop listen UDP Port\n    if (serv->have_dgram_sock == 1) {\n        for (auto ls : serv->ports) {\n            if (ls->is_dgram()) {\n                if (ls->socket->fd % serv->reactor_num != reactor->id) {\n                    continue;\n                }\n                if (!ls->socket->removed) {\n                    reactor->del(ls->socket);\n                }\n            }\n        }\n    }\n\n    if (heartbeat_timer) {\n        swoole_timer_del(heartbeat_timer);\n        heartbeat_timer = nullptr;\n    }\n\n    reactor->add_destroy_callback([serv, reactor](void *) {\n        serv->foreach_connection([reactor](Connection *conn) {\n            if (conn->reactor_id == reactor->id) {\n                Server::close_connection(reactor, conn->socket);\n            }\n        });\n    });\n\n    if (serv->is_thread_mode()) {\n        serv->stop_async_worker(serv->get_worker(reactor->id));\n        return;\n    }\n\n    SW_LOOP_N(serv->worker_num) {\n        if (i % serv->reactor_num != reactor->id) {\n            continue;\n        }\n        Socket *socket = message_bus.get_pipe_socket(serv->get_worker_pipe_master(i));\n        reactor->remove_read_event(socket);\n    }\n\n    serv->foreach_connection([serv, reactor](Connection *conn) {\n        if (conn->fd % serv->reactor_num != reactor->id) {\n            return;\n        }\n        if (!conn->peer_closed && !conn->socket->removed) {\n            reactor->remove_read_event(conn->socket);\n        }\n    });\n\n    reactor->set_wait_exit(true);\n}\n\nint ReactorThread::close_connection(Reactor *reactor, SessionId session_id) {\n    auto *serv = static_cast<Server *>(reactor->ptr);\n    Connection *conn = serv->get_connection_verify_no_ssl(session_id);\n    if (!conn) {\n        swoole_error_log(SW_LOG_TRACE,\n                         SW_ERROR_SESSION_NOT_EXIST,\n                         \"force close connection failed, session#%ld does not exist\",\n                         session_id);\n        return SW_OK;\n    }\n\n    if (serv->disable_notify || conn->close_force) {\n        return Server::close_connection(reactor, conn->socket);\n    }\n\n    /**\n     * SSL connections that have not completed the handshake,\n     * do not need to notify the workers, just close\n     */\n    if (conn->ssl && !conn->ssl_ready) {\n        return Server::close_connection(reactor, conn->socket);\n    }\n    conn->close_force = 1;\n    Event _ev = {};\n    _ev.fd = conn->fd;\n    _ev.socket = conn->socket;\n    reactor->trigger_close_event(&_ev);\n\n    return SW_OK;\n}\n\n/**\n * receive data from worker process pipe\n */\nstatic int ReactorThread_onPipeRead(Reactor *reactor, Event *ev) {\n    SendData _send;\n    auto *serv = static_cast<Server *>(reactor->ptr);\n    ReactorThread *thread = serv->get_thread(reactor->id);\n\n    SW_LOOP {\n        PipeBuffer *resp = thread->message_bus.get_buffer();\n        ssize_t n = thread->message_bus.read_with_buffer(ev->socket);\n        if (n <= 0) {\n            return SW_OK;\n        }\n        switch (resp->info.type) {\n        case SW_SERVER_EVENT_INCOMING: {\n            Connection *conn = serv->get_connection_verify_no_ssl(resp->info.fd);\n            if (conn && serv->connection_incoming(reactor, conn) < 0) {\n                reactor->close(reactor, conn->socket);\n            }\n            break;\n        }\n        case SW_SERVER_EVENT_COMMAND_REQUEST: {\n            serv->call_command_handler(thread->message_bus, thread->id, thread->pipe_command);\n            break;\n        }\n        case SW_SERVER_EVENT_COMMAND_RESPONSE: {\n            auto packet = thread->message_bus.get_packet();\n            serv->call_command_callback(resp->info.fd, std::string(packet.data, packet.length));\n            break;\n        }\n        case SW_SERVER_EVENT_SHUTDOWN: {\n            thread->shutdown(reactor);\n            break;\n        }\n        case SW_SERVER_EVENT_SHUTDOWN_SIGNAL: {\n            swoole_kill(getpid(), SIGTERM);\n            break;\n        }\n        case SW_SERVER_EVENT_FINISH: {\n            serv->onFinish(serv, reinterpret_cast<EventData *>(resp));\n            break;\n        }\n        case SW_SERVER_EVENT_PIPE_MESSAGE: {\n            serv->onPipeMessage(serv, reinterpret_cast<EventData *>(resp));\n            break;\n        }\n        case SW_SERVER_EVENT_CLOSE_FORCE: {\n            thread->close_connection(reactor, resp->info.fd);\n            break;\n        }\n        case SW_SERVER_EVENT_CLOSE_FORWARD: {\n            serv->factory_->end(resp->info.fd, Server::CLOSE_ACTIVELY);\n            break;\n        }\n        default: {\n            PacketPtr packet = thread->message_bus.get_packet();\n            _send.info = resp->info;\n            _send.info.len = packet.length;\n            _send.data = packet.data;\n            serv->send_to_connection(&_send);\n            break;\n        }\n        }\n        thread->message_bus.pop();\n    }\n\n    return SW_OK;\n}\n\n/**\n * [ReactorThread] worker pipe can write.\n */\nstatic int ReactorThread_onPipeWrite(Reactor *reactor, Event *ev) {\n    int ret;\n\n    auto *serv = static_cast<Server *>(reactor->ptr);\n    Buffer *buffer = ev->socket->out_buffer;\n\n    while (!Buffer::empty(buffer)) {\n        const BufferChunk *chunk = buffer->front();\n        const auto *send_data = reinterpret_cast<EventData *>(chunk->value.str);\n\n        // server actively closed connection, should discard the data\n        if (Server::is_stream_event(send_data->info.type)) {\n            // send_data->info.fd is session_id\n            Connection *conn = serv->get_connection_verify(send_data->info.fd);\n            if (conn) {\n                conn->last_send_time = microtime();\n                if (conn->closed) {\n                    swoole_error_log(SW_LOG_NOTICE,\n                                     SW_ERROR_SESSION_CLOSED_BY_SERVER,\n                                     \"Session#%ld is closed by server\",\n                                     send_data->info.fd);\n                _discard:\n                    buffer->pop();\n                    continue;\n                }\n            } else if (serv->discard_timeout_request) {\n                swoole_error_log(SW_LOG_WARNING,\n                                 SW_ERROR_SESSION_DISCARD_TIMEOUT_DATA,\n                                 \"[1] ignore data[%u bytes] received from session#%ld\",\n                                 send_data->info.len,\n                                 send_data->info.fd);\n                goto _discard;\n            }\n        }\n\n        ret = ev->socket->send(chunk->value.str, chunk->length, 0);\n        if (ret < 0) {\n            return (ev->socket->catch_write_error(errno) == SW_WAIT) ? SW_OK : SW_ERR;\n        } else {\n            buffer->pop();\n        }\n    }\n\n    if (Buffer::empty(buffer)) {\n        if (reactor->remove_write_event(ev->socket) < 0) {\n            swoole_sys_warning(\"reactor->set(%d) failed\", ev->fd);\n        }\n    }\n\n    return SW_OK;\n}\n\nvoid Server::init_reactor(Reactor *reactor) {\n    // support 64K packet\n    if (have_dgram_sock) {\n        sw_tg_buffer()->extend();\n    }\n    // UDP Packet\n    reactor->set_handler(SW_FD_DGRAM_SERVER, SW_EVENT_READ, ReactorThread_onPacketReceived);\n    // Write\n    reactor->set_handler(SW_FD_SESSION, SW_EVENT_WRITE, ReactorThread_onWrite);\n    // Read\n    reactor->set_handler(SW_FD_SESSION, SW_EVENT_READ, ReactorThread_onRead);\n\n    // listen the all tcp port\n    for (auto port : ports) {\n        port->init_protocol();\n    }\n}\n\nstatic int ReactorThread_onRead(Reactor *reactor, Event *event) {\n    auto *serv = static_cast<Server *>(reactor->ptr);\n    Connection *conn = serv->get_connection(event->fd);\n    /**\n     * invalid event\n     * The server has been actively closed the connection, the client also initiated off, fd has been reused.\n     */\n    if (!conn || conn->server_fd == 0) {\n        return SW_OK;\n    }\n    ListenPort *port = serv->get_port_by_fd(event->fd);\n#ifdef SW_SUPPORT_DTLS\n    if (port->is_dtls()) {\n        dtls::Buffer *buffer = static_cast<dtls::Buffer *>(sw_malloc(sizeof(*buffer) + SW_BUFFER_SIZE_UDP));\n        buffer->length = event->socket->read(buffer->data, SW_BUFFER_SIZE_UDP);\n        dtls::Session *session = port->dtls_sessions->find(event->fd)->second;\n        session->append(buffer);\n        if (!session->listened && !session->listen()) {\n            serv->abort_connection(reactor, port, event->socket);\n            return SW_OK;\n        }\n    }\n#endif\n    ReturnCode code = ReactorThread_verify_ssl_state(reactor, port, event->socket);\n    switch (code) {\n    case SW_ERROR:\n        serv->abort_connection(reactor, port, event->socket);\n        return SW_OK;\n    case SW_READY:\n#ifdef SW_SUPPORT_DTLS\n        if (event->socket->dtls) {\n            return SW_OK;\n        }\n#endif\n        break;\n    case SW_WAIT:\n        return SW_OK;\n    case SW_CONTINUE:\n        break;\n    default:\n        abort();\n    }\n\n    conn->last_recv_time = microtime();\n    long last_recv_bytes = event->socket->total_recv_bytes;\n\n    int retval = port->onRead(reactor, port, event);\n\n    long socket_recv_bytes = event->socket->total_recv_bytes - last_recv_bytes;\n    if (socket_recv_bytes > 0) {\n        sw_atomic_fetch_add(&port->gs->total_recv_bytes, socket_recv_bytes);\n        sw_atomic_fetch_add(&serv->gs->total_recv_bytes, socket_recv_bytes);\n    }\n    if (!conn->active) {\n        return retval;\n    }\n    if (serv->is_process_mode() && serv->max_queued_bytes && conn->recv_queued_bytes > serv->max_queued_bytes) {\n        conn->waiting_time = 1;\n        conn->timer =\n            swoole_timer_add((long) conn->waiting_time, false, ReactorThread_resume_data_receiving, event->socket);\n        if (conn->timer) {\n            reactor->remove_read_event(event->socket);\n        }\n    }\n    return retval;\n}\n\nstatic int ReactorThread_onWrite(Reactor *reactor, Event *ev) {\n    int ret;\n    auto serv = static_cast<Server *>(reactor->ptr);\n    auto socket = ev->socket;\n    int fd = ev->fd;\n    auto port = serv->get_port_by_fd(fd);\n\n    if (serv->is_process_mode()) {\n        assert(fd % serv->reactor_num == reactor->id);\n        assert(fd % serv->reactor_num == SwooleTG.id);\n    }\n\n    Connection *conn = serv->get_connection(fd);\n    if (conn == nullptr || conn->active == 0) {\n        return SW_ERR;\n    }\n\n    swoole_trace_log(SW_TRACE_REACTOR,\n                     \"fd=%d, conn->close_notify=%d, serv->disable_notify=%d, conn->close_force=%d\",\n                     fd,\n                     conn->close_notify,\n                     serv->disable_notify,\n                     conn->close_force);\n\n    if (conn->close_notify) {\n        if (socket->ssl && socket->ssl_state != SW_SSL_STATE_READY) {\n            return Server::close_connection(reactor, socket);\n        }\n        serv->notify(conn, SW_SERVER_EVENT_CLOSE);\n        conn->close_notify = 0;\n        return SW_OK;\n    } else if (serv->disable_notify && conn->close_force) {\n        return Server::close_connection(reactor, socket);\n    }\n\n    while (!Buffer::empty(socket->out_buffer)) {\n        BufferChunk *chunk = socket->out_buffer->front();\n        if (chunk->type == BufferChunk::TYPE_CLOSE) {\n            return reactor->close(reactor, socket);\n        } else if (chunk->type == BufferChunk::TYPE_SENDFILE) {\n            ret = socket->handle_sendfile();\n        } else {\n            ret = socket->handle_send();\n            if (SW_OK == ret) {\n                conn->send_queued_bytes = socket->out_buffer->length();\n            }\n        }\n\n        if (ret < 0) {\n            if (socket->close_wait) {\n                conn->close_errno = errno;\n                return reactor->trigger_close_event(ev);\n            } else if (socket->send_wait) {\n                break;\n            }\n        }\n    }\n\n    if (conn->overflow && socket->get_out_buffer_length() < socket->buffer_size) {\n        conn->overflow = 0;\n    }\n\n    if (serv->onBufferEmpty && conn->high_watermark) {\n        if (socket->get_out_buffer_length() <= port->buffer_low_watermark) {\n            conn->high_watermark = 0;\n            serv->notify(conn, SW_SERVER_EVENT_BUFFER_EMPTY);\n        }\n    }\n\n    if (socket->send_timer) {\n        if (Buffer::empty(socket->out_buffer)) {\n            swoole_timer_del(socket->send_timer);\n            socket->send_timer = nullptr;\n        } else {\n            swoole_timer_delay(socket->send_timer, port->max_idle_time);\n        }\n    }\n\n    // remove EPOLLOUT event\n    if (!conn->peer_closed && !socket->removed && Buffer::empty(socket->out_buffer)) {\n        reactor->set(socket, SW_EVENT_READ);\n    }\n    return SW_OK;\n}\n\nvoid Server::heartbeat_check(Timer *timer, TimerNode *tnode) {\n    double now = microtime();\n    auto reactor = static_cast<Reactor *>(tnode->data);\n    auto serv = static_cast<Server *>(reactor->ptr);\n    ReactorThread *thread = serv->get_thread(reactor->id);\n\n    serv->foreach_connection([=](Connection *conn) {\n        SessionId session_id = conn->session_id;\n        if (session_id <= 0) {\n            return;\n        }\n        if (conn->reactor_id != reactor->id) {\n            return;\n        }\n        if (serv->is_healthy_connection(now, conn)) {\n            return;\n        }\n        thread->close_connection(reactor, session_id);\n    });\n}\n\n/**\n * [master]\n */\nint Server::start_reactor_threads() {\n    if (swoole_event_init(0) < 0) {\n        return SW_ERR;\n    }\n\n    Reactor *reactor = sw_reactor();\n\n    for (const auto port : ports) {\n        if (port->is_dgram()) {\n            continue;\n        }\n        if (port->listen() < 0) {\n            swoole_event_free();\n            return SW_ERR;\n        }\n        reactor->add(port->socket, SW_EVENT_READ);\n    }\n\n    store_listen_socket();\n\n    if (single_thread) {\n        get_thread(0)->init(this, reactor, 0);\n        goto _init_master_thread;\n    }\n    /**\n     * multi-threads\n     */\n    else {\n        /**\n         * set a special id\n         */\n        reactor->id = reactor_num;\n        SwooleTG.id = reactor_num;\n    }\n\n    SW_LOOP_N(reactor_num) {\n        get_thread(i)->thread = std::thread([=]() {\n            swoole_thread_init(false);\n            reactor_thread_main_loop(this, i);\n            swoole_thread_clean(false);\n        });\n    }\n\n_init_master_thread:\n\n    /**\n     * heartbeat thread\n     */\n    if (heartbeat_check_interval >= 1) {\n        if (single_thread) {\n            heartbeat_timer = swoole_timer_add(sec2msec(heartbeat_check_interval), true, heartbeat_check, reactor);\n        } else {\n            start_heartbeat_thread();\n        }\n    }\n\n    return start_master_thread(reactor);\n}\n\nint ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) {\n    reactor->ptr = serv;\n    reactor->id = reactor_id;\n    reactor->wait_exit = false;\n    reactor->max_socket = serv->get_max_connection();\n    reactor->close = Server::close_connection;\n    reactor->default_error_handler = ReactorThread_onClose;\n\n    reactor->set_handler(SW_FD_PIPE, SW_EVENT_READ, ReactorThread_onPipeRead);\n    reactor->set_handler(SW_FD_PIPE, SW_EVENT_WRITE, ReactorThread_onPipeWrite);\n\n    // listen UDP port\n    if (serv->have_dgram_sock == 1) {\n        for (auto ls : serv->ports) {\n            if (ls->is_stream()) {\n                continue;\n            }\n            int server_fd = ls->socket->fd;\n            if (server_fd % serv->reactor_num != reactor_id) {\n                continue;\n            }\n            Connection *serv_sock = serv->get_connection(server_fd);\n            serv_sock->fd = server_fd;\n            serv_sock->socket_type = ls->type;\n            serv_sock->object = ls;\n            ls->thread_id = pthread_self();\n            if (reactor->add(ls->socket, SW_EVENT_READ) < 0) {\n                return SW_ERR;\n            }\n        }\n    }\n\n    serv->init_reactor(reactor);\n    serv->init_pipe_sockets(&message_bus);\n\n    if (serv->is_thread_mode()) {\n        Worker *worker = serv->get_worker(reactor_id);\n        serv->init_event_worker(worker);\n        auto pipe_worker = message_bus.get_pipe_socket(worker->pipe_worker);\n        reactor->add(pipe_worker, SW_EVENT_READ);\n\n        if (serv->heartbeat_check_interval > 0) {\n            heartbeat_timer =\n                swoole_timer_add(sec2msec(serv->heartbeat_check_interval), true, Server::heartbeat_check, reactor);\n        }\n    }\n\n    if (serv->pipe_command) {\n        auto pipe_socket = serv->pipe_command->get_socket(false);\n        message_bus.init_pipe_socket(pipe_socket);\n        pipe_command = message_bus.get_pipe_socket(pipe_socket);\n        pipe_command->buffer_size = UINT_MAX;\n    }\n\n    message_bus.set_id_generator(serv->msg_id_generator);\n    message_bus.set_buffer_size(serv->ipc_max_size);\n    message_bus.set_always_chunked_transfer();\n    if (!message_bus.alloc_buffer()) {\n        return SW_ERR;\n    }\n\n    SW_LOOP_N(serv->worker_num) {\n        if (i % serv->reactor_num != reactor_id) {\n            continue;\n        }\n        Socket *socket = message_bus.get_pipe_socket(serv->get_worker_pipe_master(i));\n        if (reactor->add(socket, SW_EVENT_READ) < 0) {\n            return SW_ERR;\n        }\n        /**\n         * It will only send data to the notify pipeline synchronously,\n         * which is thread-safe and does not require separate memory\n         */\n        if (notify_pipe == nullptr) {\n            notify_pipe = serv->workers[i].pipe_worker;\n        }\n    }\n\n    return SW_OK;\n}\n\nvoid ReactorThread::clean() {\n    message_bus.free_buffer();\n}\n\nvoid Server::reactor_thread_main_loop(Server *serv, int reactor_id) {\n    ReactorThread *thread = serv->get_thread(reactor_id);\n    thread->id = reactor_id;\n    SwooleTG.message_bus = &thread->message_bus;\n\n    if (swoole_event_init(0) < 0) {\n        return;\n    }\n\n    if (serv->is_thread_mode()) {\n        serv->call_worker_start_callback(serv->get_worker(reactor_id));\n    } else {\n        swoole_set_thread_id(reactor_id);\n        swoole_set_thread_type(Server::THREAD_REACTOR);\n    }\n\n    Reactor *reactor = sw_reactor();\n    if (thread->init(serv, reactor, reactor_id) < 0) {\n        return;\n    }\n\n    // wait other thread\n    if (serv->is_process_mode()) {\n        serv->reactor_thread_barrier.wait();\n    }\n    // main loop\n    swoole_event_wait();\n    if (serv->is_thread_mode()) {\n        serv->call_worker_stop_callback(serv->get_worker(reactor_id));\n    }\n    thread->clean();\n}\n\nstatic void ReactorThread_resume_data_receiving(Timer *timer, TimerNode *tnode) {\n    auto *_socket = static_cast<Socket *>(tnode->data);\n    auto *conn = static_cast<Connection *>(_socket->object);\n\n    if (conn->recv_queued_bytes > sw_server()->max_queued_bytes) {\n        if (conn->waiting_time != 1024) {\n            conn->waiting_time *= 2;\n        }\n        conn->timer = swoole_timer_add((long) conn->waiting_time, false, ReactorThread_resume_data_receiving, _socket);\n        if (conn->timer) {\n            return;\n        }\n    }\n\n    timer->get_reactor()->add_read_event(_socket);\n    conn->timer = nullptr;\n}\n\n/**\n * dispatch request data [only data frame]\n */\nint Server::dispatch_task(const Protocol *proto, Socket *_socket, const RecvData *rdata) {\n    auto *serv = static_cast<Server *>(proto->private_data_2);\n    SendData task;\n\n    auto *conn = static_cast<Connection *>(_socket->object);\n    const ListenPort *port = serv->get_port_by_fd(conn->fd);\n\n    sw_memset_zero(&task.info, sizeof(task.info));\n    task.info.server_fd = conn->server_fd;\n    task.info.reactor_id = conn->reactor_id;\n    task.info.ext_flags = rdata->info.ext_flags;\n    task.info.type = SW_SERVER_EVENT_RECV_DATA;\n    task.info.time = conn->last_recv_time;\n\n    swoole_trace(\"dispatch task, size=%u bytes\", rdata->info.len);\n\n    task.info.fd = conn->fd;\n    task.info.len = rdata->info.len;\n    task.data = rdata->data;\n\n    if (rdata->info.len > 0) {\n        sw_atomic_fetch_add(&conn->recv_queued_bytes, rdata->info.len);\n        swoole_trace_log(SW_TRACE_SERVER,\n                         \"session_id=%ld, len=%d, qb=%d\",\n                         conn->session_id,\n                         rdata->info.len,\n                         conn->recv_queued_bytes);\n    }\n\n    if (!serv->factory_->dispatch(&task)) {\n        if (rdata->info.len > 0) {\n            sw_atomic_fetch_sub(&conn->recv_queued_bytes, rdata->info.len);\n        }\n        return SW_ERR;\n    } else {\n        if (serv->is_process_mode()) {\n            ReactorThread *thread = serv->get_thread(conn->reactor_id);\n            thread->dispatch_count++;\n        }\n        sw_atomic_fetch_add(&serv->gs->dispatch_count, 1);\n        sw_atomic_fetch_add(&port->gs->dispatch_count, 1);\n        return SW_OK;\n    }\n}\n\nvoid Server::join_heartbeat_thread() {\n    /**\n     * Shutdown heartbeat thread\n     */\n    if (heartbeat_thread.joinable()) {\n        swoole_trace_log(SW_TRACE_SERVER, \"terminate heartbeat thread\");\n        heartbeat_thread.join();\n    }\n}\n\nvoid Server::join_reactor_thread() {\n    if (single_thread) {\n        return;\n    }\n\n    if (heartbeat_check_interval > 0) {\n        join_heartbeat_thread();\n    }\n\n    for (int i = 0; i < reactor_num; i++) {\n        ReactorThread *thread = get_thread(i);\n        if (!thread->thread.joinable()) {\n            continue;\n        }\n        if (thread->notify_pipe) {\n            DataHead ev = {};\n            ev.type = SW_SERVER_EVENT_SHUTDOWN;\n            thread->notify_pipe->send_sync((void *) &ev, sizeof(ev));\n        }\n        thread->thread.join();\n    }\n}\n\nvoid Server::start_heartbeat_thread() {\n    heartbeat_thread = std::thread([this]() {\n        swoole_signal_block_all();\n        swoole_set_thread_type(THREAD_HEARTBEAT);\n        swoole_set_thread_type(reactor_num + 1);\n\n        while (running) {\n            double now = microtime();\n            foreach_connection([this, now](Connection *conn) {\n                SessionId session_id = conn->session_id;\n                if (session_id <= 0) {\n                    return;\n                }\n                if (is_healthy_connection(now, conn)) {\n                    return;\n                }\n                DataHead ev{};\n                ev.type = SW_SERVER_EVENT_CLOSE_FORCE;\n                // convert fd to session_id, in order to verify the connection before the force close connection\n                ev.fd = session_id;\n                get_reactor_pipe_socket(session_id, conn->reactor_id)->send_sync(&ev, sizeof(ev));\n            });\n            sleep(heartbeat_check_interval);\n        }\n    });\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "src/server/static_handler.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_static_handler.h\"\n#include \"swoole_util.h\"\n\n#include <string>\n#include <dirent.h>\n#include <algorithm>\n#include <sstream>\n#include <regex>\n\nnamespace swoole {\nnamespace http_server {\nbool StaticHandler::is_modified(const std::string &date_if_modified_since) const {\n    char date_tmp[64];\n    if (date_if_modified_since.empty() || date_if_modified_since.length() > sizeof(date_tmp) - 1) {\n        return false;\n    }\n\n    struct tm tm3 {};\n    memcpy(date_tmp, date_if_modified_since.c_str(), date_if_modified_since.length());\n    date_tmp[date_if_modified_since.length()] = 0;\n\n    const char *date_format = nullptr;\n\n    if (strptime(date_tmp, SW_HTTP_RFC1123_DATE_GMT, &tm3) != nullptr) {\n        date_format = SW_HTTP_RFC1123_DATE_GMT;\n    } else if (strptime(date_tmp, SW_HTTP_RFC1123_DATE_UTC, &tm3) != nullptr) {\n        date_format = SW_HTTP_RFC1123_DATE_UTC;\n    } else if (strptime(date_tmp, SW_HTTP_RFC850_DATE, &tm3) != nullptr) {\n        date_format = SW_HTTP_RFC850_DATE;\n    } else if (strptime(date_tmp, SW_HTTP_ASCTIME_DATE, &tm3) != nullptr) {\n        date_format = SW_HTTP_ASCTIME_DATE;\n    }\n    return date_format && mktime(&tm3) - (time_t) serv->timezone_ >= get_file_mtime();\n}\n\nbool StaticHandler::is_modified_range(const std::string &date_range) const {\n    if (date_range.empty()) {\n        return false;\n    }\n\n    tm tm3{};\n    const char *date_format = nullptr;\n\n    if (strptime(date_range.c_str(), SW_HTTP_RFC1123_DATE_GMT, &tm3) != nullptr) {\n        date_format = SW_HTTP_RFC1123_DATE_GMT;\n    } else if (strptime(date_range.c_str(), SW_HTTP_RFC1123_DATE_UTC, &tm3) != nullptr) {\n        date_format = SW_HTTP_RFC1123_DATE_UTC;\n    } else if (strptime(date_range.c_str(), SW_HTTP_RFC850_DATE, &tm3) != nullptr) {\n        date_format = SW_HTTP_RFC850_DATE;\n    } else if (strptime(date_range.c_str(), SW_HTTP_ASCTIME_DATE, &tm3) != nullptr) {\n        date_format = SW_HTTP_ASCTIME_DATE;\n    }\n    time_t file_mtime = get_file_mtime();\n    tm *tm_file_mtime = gmtime(&file_mtime);\n    return date_format && mktime(&tm3) != mktime(tm_file_mtime);\n}\n\nstd::string StaticHandler::get_date() {\n    char date_[64];\n    time_t now = ::time(nullptr);\n    tm *tm1 = gmtime(&now);\n    strftime(date_, sizeof(date_), \"%a, %d %b %Y %H:%M:%S %Z\", tm1);\n    return date_;\n}\n\nstd::string StaticHandler::get_date_last_modified() const {\n    char date_last_modified[64];\n    time_t file_mtime = get_file_mtime();\n    tm *tm2 = gmtime(&file_mtime);\n    strftime(date_last_modified, sizeof(date_last_modified), \"%a, %d %b %Y %H:%M:%S %Z\", tm2);\n    return date_last_modified;\n}\n\nbool StaticHandler::get_absolute_path() {\n    char abs_path[PATH_MAX];\n    if (!realpath(filename, abs_path)) {\n        return false;\n    }\n\n    size_t abs_path_len = strlen(abs_path);\n    if (abs_path_len >= PATH_MAX) {\n        return false;\n    }\n    memcpy(filename, abs_path, abs_path_len + 1);\n    l_filename = abs_path_len;\n    return true;\n}\n\nbool StaticHandler::try_serve() {\n    serv->apply_rewrite_rules(this);\n\n    char *p = filename;\n    const char *url = request_url.c_str();\n    size_t url_length = request_url.length();\n    /**\n     * discard the url parameter\n     * [/test.jpg?version=1#position] -> [/test.jpg]\n     */\n    auto params = (char *) memchr(url, '?', url_length);\n    if (params == nullptr) {\n        params = (char *) memchr(url, '#', url_length);\n    }\n    size_t n = params ? params - url : url_length;\n\n    const std::string &document_root = serv->get_document_root();\n    const size_t l_document_root = document_root.length();\n\n    memcpy(p, document_root.c_str(), l_document_root);\n    p += l_document_root;\n\n    if (!serv->locations->empty()) {\n        for (const auto &i : *serv->locations) {\n            if (swoole_str_istarts_with(url, url_length, i.c_str(), i.size())) {\n                last = true;\n            }\n        }\n        if (!last) {\n            return false;\n        }\n    }\n\n    if (l_document_root + n >= PATH_MAX) {\n        return catch_error();\n    }\n\n    memcpy(p, url, n);\n    p += n;\n    *p = '\\0';\n    if (!dir_path.empty()) {\n        dir_path.clear();\n    }\n    dir_path = std::string(url, n);\n\n    l_filename = url_decode(filename, p - filename);\n    filename[l_filename] = '\\0';\n\n    // The file does not exist\n    if (lstat(filename, &file_stat) < 0) {\n        return catch_error();\n    }\n\n    // The filename is relative path, allows for the resolution of symbolic links.\n    // This path is formed by concatenating the document root and that is permitted for access.\n    if (is_absolute_path()) {\n        if (is_link()) {\n            // Use the realpath function to resolve a symbolic link to its actual path.\n            if (!get_absolute_path()) {\n                return catch_error();\n            }\n            if (lstat(filename, &file_stat) < 0) {\n                return catch_error();\n            }\n        }\n    } else {\n        if (!get_absolute_path() || !is_located_in_document_root()) {\n            return catch_error();\n        }\n    }\n\n    if (serv->http_index_files && !serv->http_index_files->empty() && is_dir()) {\n        return true;\n    }\n\n    if (serv->http_autoindex && is_dir()) {\n        return true;\n    }\n\n    if (!mime_type::exists(filename) && !last) {\n        return false;\n    }\n\n    if (!is_file()) {\n        return false;\n    }\n\n    return true;\n}\n\nbool StaticHandler::try_serve_index_file() {\n    if (serv->http_index_files && !serv->http_index_files->empty() && is_dir()) {\n        if (!get_dir_files()) {\n            return false;\n        }\n        index_file = intersection(*serv->http_index_files, dir_files);\n\n        if (has_index_file() && !set_filename(index_file)) {\n            return false;\n        }\n        if (!has_index_file() && !is_enabled_auto_index()) {\n            return false;\n        }\n    }\n    return true;\n}\n\nsize_t StaticHandler::make_index_page(String *buffer) {\n    get_dir_files();\n\n    if (dir_path.back() != '/') {\n        dir_path.append(\"/\");\n    }\n\n    buffer->format_impl(String::FORMAT_APPEND | String::FORMAT_GROW,\n                        \"<html>\\n\"\n                        \"<head>\\n\"\n                        \"\\t<meta charset='UTF-8'>\\n<title>Index of %s</title>\"\n                        \"</head>\\n\"\n                        \"<body>\\n\" SW_HTTP_PAGE_CSS \"<h1>Index of %s</h1>\"\n                        \"\\t<ul>\\n\",\n                        dir_path.c_str(),\n                        dir_path.c_str());\n\n    for (const auto &dir_file : dir_files) {\n        if (dir_file == \".\" || (dir_path == \"/\" && dir_file == \"..\")) {\n            continue;\n        }\n        buffer->format_impl(String::FORMAT_APPEND | String::FORMAT_GROW,\n                            \"\\t\\t<li><a href=%s%s>%s</a></li>\\n\",\n                            dir_path.c_str(),\n                            dir_file.c_str(),\n                            dir_file.c_str());\n    }\n\n    buffer->append(SW_STRL(\"\\t</ul>\\n\" SW_HTTP_POWER_BY \"</body>\\n</html>\\n\"));\n\n    return buffer->length;\n}\n\nbool StaticHandler::get_dir_files() {\n    if (!dir_files.empty()) {\n        return true;\n    }\n\n    if (!is_dir()) {\n        return false;\n    }\n\n    DIR *dir = opendir(filename);\n    if (dir == nullptr) {\n        return false;\n    }\n\n    struct dirent *ptr;\n    while ((ptr = readdir(dir)) != nullptr) {\n        dir_files.insert(ptr->d_name);\n    }\n\n    closedir(dir);\n\n    return true;\n}\n\nbool StaticHandler::set_filename(const std::string &_filename) {\n    char *p = filename + l_filename;\n\n    if (*p != '/') {\n        *p = '/';\n        p += 1;\n    }\n\n    memcpy(p, _filename.c_str(), _filename.length());\n    p += _filename.length();\n    *p = 0;\n\n    if (lstat(filename, &file_stat) < 0) {\n        return false;\n    }\n\n    if (!is_file()) {\n        return false;\n    }\n\n    return true;\n}\n\nvoid StaticHandler::parse_range(const char *range, const char *if_range) {\n    task_t _task{};\n    _task.length = 0;\n    // range\n    if (range && '\\0' != *range) {\n        const char *p = range;\n        // bytes=\n        if (!SW_STR_ISTARTS_WITH(p, strlen(range), \"bytes=\")) {\n            _task.offset = 0;\n            _task.length = content_length = get_filesize();\n            tasks.push_back(_task);\n            return;\n        }\n        p += 6;\n        size_t start, end, size = 0, cutoff = SIZE_MAX / 10, cutlim = SIZE_MAX % 10, suffix,\n                           _content_length = get_filesize();\n        content_length = 0;\n        for (;;) {\n            start = 0;\n            end = 0;\n            suffix = 0;\n\n            while (*p == ' ') {\n                p++;\n            }\n\n            if (*p != '-') {\n                if (*p < '0' || *p > '9') {\n                    status_code = SW_HTTP_RANGE_NOT_SATISFIABLE;\n                    return;\n                }\n\n                while (*p >= '0' && *p <= '9') {\n                    if (start >= cutoff && (start > cutoff || (size_t) (*p - '0') > cutlim)) {\n                        status_code = SW_HTTP_RANGE_NOT_SATISFIABLE;\n                        return;\n                    }\n\n                    start = start * 10 + (*p++ - '0');\n                }\n\n                while (*p == ' ') {\n                    p++;\n                }\n\n                if (*p++ != '-') {\n                    status_code = SW_HTTP_RANGE_NOT_SATISFIABLE;\n                    return;\n                }\n\n                while (*p == ' ') {\n                    p++;\n                }\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                status_code = SW_HTTP_RANGE_NOT_SATISFIABLE;\n                return;\n            }\n\n            while (*p >= '0' && *p <= '9') {\n                if (end >= cutoff && (end > cutoff || (size_t) (*p - '0') > cutlim)) {\n                    status_code = SW_HTTP_RANGE_NOT_SATISFIABLE;\n                    return;\n                }\n\n                end = end * 10 + (*p++ - '0');\n            }\n\n            while (*p == ' ') {\n                p++;\n            }\n\n            if (*p != ',' && *p != '\\0' && *p != '\\r') {\n                status_code = SW_HTTP_RANGE_NOT_SATISFIABLE;\n                return;\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            if (start < end) {\n                if (size > SIZE_MAX - (end - start)) {\n                    status_code = SW_HTTP_RANGE_NOT_SATISFIABLE;\n                    return;\n                }\n                size += end - start;\n                _task.offset = start;\n                _task.length = end - start;\n                content_length += sw_snprintf(_task.part_header,\n                                              sizeof(_task.part_header),\n                                              \"%s--%s\\r\\n\"\n                                              \"Content-Type: %s\\r\\n\"\n                                              \"Content-Range: bytes %zu-%zu/%zu\\r\\n\\r\\n\",\n                                              tasks.empty() ? \"\" : \"\\r\\n\",\n                                              get_boundary().c_str(),\n                                              get_mimetype().c_str(),\n                                              (size_t) _task.offset,\n                                              end - 1,\n                                              get_filesize()) +\n                                  _task.length;\n                tasks.push_back(_task);\n            } else if (start == 0) {\n                break;\n            }\n\n            if (*p++ != ',' || '\\r' == *p || '\\0' == *p) {\n                break;\n            }\n        }\n    }\n    if (_task.length > 0) {\n        if (1 == tasks.size()) {\n            content_length = _task.length;\n        } else {\n            end_part = std::string(\"\\r\\n--\") + get_boundary() + \"--\\r\\n\";\n            content_length += end_part.size();\n        }\n        status_code = SW_HTTP_PARTIAL_CONTENT;\n    } else {\n        _task.offset = 0;\n        _task.length = content_length = get_filesize();\n        tasks.push_back(_task);\n    }\n    // if-range\n    if (if_range) {\n        if (is_modified_range(if_range)) {\n            tasks.clear();\n            _task.offset = 0;\n            _task.length = content_length = get_filesize();\n            tasks.push_back(_task);\n            status_code = SW_HTTP_OK;\n        }\n    }\n}\n}  // namespace http_server\n\nvoid Server::add_rewrite_rule(const std::string &pattern, const std::string &replacement) {\n    if (rewrite_rules == nullptr) {\n        rewrite_rules = std::make_shared<std::vector<http_server::RewriteRule>>();\n    }\n\n    http_server::RewriteRule rule;\n    rule.replacement = replacement;\n\n    if (pattern.length() >= 2 && pattern.at(0) == '~' && pattern.at(pattern.length() - 1) == '~') {\n        rule.pattern = pattern.substr(1, pattern.length() - 2);\n        rule.is_regex = true;\n    } else {\n        rule.pattern = pattern;\n        rule.is_regex = false;\n    }\n    rewrite_rules->emplace_back(rule);\n}\n\nvoid Server::add_static_handler_location(const std::string &location) {\n    if (locations == nullptr) {\n        locations = std::make_shared<std::unordered_set<std::string>>();\n    }\n    locations->emplace(location);\n}\n\nvoid Server::add_static_handler_index_files(const std::string &file) {\n    if (http_index_files == nullptr) {\n        http_index_files = std::make_shared<std::vector<std::string>>();\n    }\n\n    auto iter = std::find(http_index_files->begin(), http_index_files->end(), file);\n    if (iter == http_index_files->end()) {\n        http_index_files->emplace_back(file);\n    }\n}\n\nbool Server::select_static_handler(const http_server::Request *request, const Connection *conn) {\n    const char *url = request->buffer_->str + request->url_offset_;\n    size_t url_length = request->url_length_;\n\n    http_server::StaticHandler handler(this, url, url_length);\n    if (!handler.try_serve()) {\n        return false;\n    }\n\n    char header_buffer[1024];\n    SendData response;\n    response.info.fd = conn->session_id;\n    response.info.type = SW_SERVER_EVENT_SEND_DATA;\n\n    if (handler.status_code == SW_HTTP_NOT_FOUND) {\n        response.info.len = sw_snprintf(header_buffer,\n                                        sizeof(header_buffer),\n                                        \"HTTP/1.1 %s\\r\\n\"\n                                        \"Server: \" SW_HTTP_SERVER_SOFTWARE \"\\r\\n\"\n                                        \"Content-Length: %zu\\r\\n\"\n                                        \"\\r\\n%s\",\n                                        http_server::get_status_message(SW_HTTP_NOT_FOUND),\n                                        sizeof(SW_HTTP_PAGE_404) - 1,\n                                        SW_HTTP_PAGE_404);\n        response.data = header_buffer;\n        send_to_connection(&response);\n\n        return true;\n    }\n\n    auto date_str = handler.get_date();\n    auto date_str_last_modified = handler.get_date_last_modified();\n\n    std::string date_if_modified_since = request->get_header(\"If-Modified-Since\");\n    if (!date_if_modified_since.empty() && handler.is_modified(date_if_modified_since)) {\n        response.info.len = sw_snprintf(header_buffer,\n                                        sizeof(header_buffer),\n                                        \"HTTP/1.1 304 Not Modified\\r\\n\"\n                                        \"Connection: %s\\r\\n\"\n                                        \"Date: %s\\r\\n\"\n                                        \"Last-Modified: %s\\r\\n\"\n                                        \"Server: %s\\r\\n\\r\\n\",\n                                        request->keep_alive ? \"keep-alive\" : \"close\",\n                                        date_str.c_str(),\n                                        date_str_last_modified.c_str(),\n                                        SW_HTTP_SERVER_SOFTWARE);\n        response.data = header_buffer;\n        send_to_connection(&response);\n\n        return true;\n    }\n\n    /**\n     * if http_index_files is enabled, need to search the index file first.\n     * if the index file is found, set filename to index filename.\n     */\n    if (!handler.try_serve_index_file()) {\n        return false;\n    }\n\n    /**\n     * the index file was not found in the current directory,\n     * if http_autoindex is enabled, should show the list of files in the current directory.\n     */\n    if (!handler.has_index_file() && handler.is_enabled_auto_index() && handler.is_dir()) {\n        sw_tg_buffer()->clear();\n        size_t body_length = handler.make_index_page(sw_tg_buffer());\n\n        response.info.len = sw_snprintf(header_buffer,\n                                        sizeof(header_buffer),\n                                        \"HTTP/1.1 200 OK\\r\\n\"\n                                        \"Connection: %s\\r\\n\"\n                                        \"Content-Length: %ld\\r\\n\"\n                                        \"Content-Type: text/html\\r\\n\"\n                                        \"Date: %s\\r\\n\"\n                                        \"Last-Modified: %s\\r\\n\"\n                                        \"Server: %s\\r\\n\\r\\n\",\n                                        request->keep_alive ? \"keep-alive\" : \"close\",\n                                        static_cast<long>(body_length),\n                                        date_str.c_str(),\n                                        date_str_last_modified.c_str(),\n                                        SW_HTTP_SERVER_SOFTWARE);\n        response.data = header_buffer;\n        send_to_connection(&response);\n\n        response.info.len = body_length;\n        response.data = sw_tg_buffer()->str;\n        send_to_connection(&response);\n        return true;\n    }\n\n    handler.parse_range(request->get_header(\"Range\").c_str(), request->get_header(\"If-Range\").c_str());\n    auto tasks = handler.get_tasks();\n\n    std::stringstream header_stream;\n    if (1 == tasks.size()) {\n        if (SW_HTTP_PARTIAL_CONTENT == handler.status_code) {\n            header_stream << \"Content-Range: bytes \" << tasks[0].offset << \"-\"\n                          << (tasks[0].length + tasks[0].offset - 1) << \"/\" << handler.get_filesize() << \"\\r\\n\";\n        } else {\n            header_stream << \"Accept-Ranges: bytes\\r\\n\";\n        }\n    }\n\n    response.info.len = sw_snprintf(\n        header_buffer,\n        sizeof(header_buffer),\n        \"HTTP/1.1 %s\\r\\n\"\n        \"Connection: %s\\r\\n\"\n        \"Content-Length: %ld\\r\\n\"\n        \"Content-Type: %s\\r\\n\"\n        \"%s\"\n        \"Date: %s\\r\\n\"\n        \"Last-Modified: %s\\r\\n\"\n        \"Server: %s\\r\\n\\r\\n\",\n        http_server::get_status_message(handler.status_code),\n        request->keep_alive ? \"keep-alive\" : \"close\",\n        SW_HTTP_HEAD == request->method ? 0 : handler.get_content_length(),\n        SW_HTTP_HEAD == request->method ? handler.get_mimetype().c_str() : handler.get_content_type().c_str(),\n        header_stream.str().c_str(),\n        date_str.c_str(),\n        date_str_last_modified.c_str(),\n        SW_HTTP_SERVER_SOFTWARE);\n\n    response.data = header_buffer;\n\n    // Use tcp_nopush to improve sending efficiency\n    conn->socket->cork();\n\n    // Send HTTP header\n    send_to_connection(&response);\n\n    // Send HTTP body\n    if (SW_HTTP_HEAD != request->method) {\n        if (!tasks.empty()) {\n            size_t task_size = sizeof(network::SendfileTask) + strlen(handler.get_filename()) + 1;\n            auto task = static_cast<network::SendfileTask *>(sw_malloc(task_size));\n            strcpy(task->filename, handler.get_filename());\n            if (tasks.size() > 1) {\n                for (const auto &i : tasks) {\n                    response.info.type = SW_SERVER_EVENT_SEND_DATA;\n                    response.info.len = strlen(i.part_header);\n                    response.data = i.part_header;\n                    send_to_connection(&response);\n\n                    task->offset = i.offset;\n                    task->length = i.length;\n                    response.info.type = SW_SERVER_EVENT_SEND_FILE;\n                    response.info.len = task_size;\n                    response.data = reinterpret_cast<char *>(task);\n                    send_to_connection(&response);\n                }\n\n                response.info.type = SW_SERVER_EVENT_SEND_DATA;\n                response.info.len = handler.get_end_part().length();\n                response.data = handler.get_end_part().c_str();\n                send_to_connection(&response);\n            } else if (tasks[0].length > 0) {\n                task->offset = tasks[0].offset;\n                task->length = tasks[0].length;\n                response.info.type = SW_SERVER_EVENT_SEND_FILE;\n                response.info.len = task_size;\n                response.data = reinterpret_cast<char *>(task);\n                send_to_connection(&response);\n            }\n            sw_free(task);\n        }\n    }\n\n    // Close the connection if keepalive is not used\n    if (!request->keep_alive) {\n        response.info.type = SW_SERVER_EVENT_CLOSE;\n        response.info.len = 0;\n        response.data = nullptr;\n        send_to_connection(&response);\n    }\n\n    return true;\n}\n\nbool Server::apply_rewrite_rules(http_server::StaticHandler *handler) {\n    if (!rewrite_rules || rewrite_rules->empty()) {\n        return false;\n    }\n\n    bool rewritten = false;\n    auto current_url = handler->get_request_url();\n\n    for (const auto &rule : *rewrite_rules) {\n        if (rule.is_regex) {\n            try {\n                std::regex pattern(rule.pattern);\n                std::string rewritten_url;\n\n                if (std::regex_search(current_url, pattern)) {\n                    rewritten_url = std::regex_replace(current_url, pattern, rule.replacement);\n                    if (rewritten_url != current_url) {\n                        handler->set_request_url(rewritten_url);\n                        rewritten = true;\n                        break;\n                    }\n                }\n            } catch (const std::regex_error &e) {\n                continue;\n            }\n        } else {\n            if (starts_with(current_url, rule.pattern)) {\n                std::string rewritten_url = rule.replacement + current_url.substr(rule.pattern.length());\n                handler->set_request_url(rewritten_url);\n                rewritten = true;\n                break;\n            }\n        }\n    }\n\n    return rewritten;\n}\n}  // namespace swoole\n"
  },
  {
    "path": "src/server/task_worker.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_server.h\"\n\nnamespace swoole {\nusing network::Socket;\n\nstatic void TaskWorker_signal_init(ProcessPool *pool);\nstatic int TaskWorker_onPipeReceive(Reactor *reactor, Event *event);\nstatic int TaskWorker_loop_async(ProcessPool *pool, Worker *worker);\nstatic void TaskWorker_onStart(ProcessPool *pool, Worker *worker);\nstatic void TaskWorker_onStop(ProcessPool *pool, Worker *worker);\nstatic int TaskWorker_onTask(ProcessPool *pool, Worker *worker, EventData *task);\n\nstatic SW_THREAD_LOCAL EventData *latest_task = nullptr;\n\n/**\n * after pool->create, before pool->start\n */\nbool Server::init_task_workers() {\n    ProcessPool *pool = get_task_worker_pool();\n    pool->ptr = this;\n    pool->onTask = TaskWorker_onTask;\n    pool->onWorkerStart = TaskWorker_onStart;\n    pool->onWorkerStop = TaskWorker_onStop;\n    /**\n     * Make the task worker support asynchronous\n     */\n    if (task_enable_coroutine) {\n        if (task_ipc_mode == TASK_IPC_MSGQUEUE || task_ipc_mode == TASK_IPC_PREEMPTIVE) {\n            swoole_error_log(\n                SW_LOG_WARNING, SW_ERROR_WRONG_OPERATION, \"cannot use msgqueue when task_enable_coroutine is enable\");\n            return false;\n        }\n        pool->main_loop = TaskWorker_loop_async;\n    }\n    if (task_ipc_mode == TASK_IPC_PREEMPTIVE) {\n        pool->schedule_by_sysvmsg = true;\n    }\n    SW_LOOP_N(task_worker_num) {\n        create_worker(&pool->workers[i]);\n    }\n    return true;\n}\n\nstatic int TaskWorker_call_command_handler(const ProcessPool *pool, const Worker *worker, EventData *req) {\n    auto *serv = static_cast<Server *>(pool->ptr);\n    int command_id = serv->get_command_id(req);\n    const auto iter = serv->command_handlers.find(command_id);\n    if (iter == serv->command_handlers.end()) {\n        swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_INVALID_COMMAND, \"Unknown command[%d]\", command_id);\n        return SW_OK;\n    }\n\n    Server::Command::Handler handler = iter->second;\n    PacketPtr packet;\n    if (!Server::task_unpack(req, sw_tg_buffer(), &packet)) {\n        return SW_OK;\n    }\n\n    auto result = handler(serv, std::string(packet.data, packet.length));\n\n    SendData task{};\n    task.info.fd = serv->get_task_id(req);\n    task.info.reactor_id = worker->id;\n    task.info.server_fd = -1;\n    task.info.type = SW_SERVER_EVENT_COMMAND_RESPONSE;\n    task.info.len = result.length();\n    task.data = result.c_str();\n\n    return serv->message_bus.write(serv->get_command_reply_socket(), &task) ? SW_OK : SW_ERR;\n}\n\nstatic int TaskWorker_onTask(ProcessPool *pool, Worker *worker, EventData *task) {\n    int ret = SW_OK;\n    auto *serv = static_cast<Server *>(pool->ptr);\n    latest_task = task;\n\n    worker->set_status_to_busy();\n    if (task->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) {\n        serv->onPipeMessage(serv, task);\n    } else if (task->info.type == SW_SERVER_EVENT_SHUTDOWN) {\n        worker->shutdown();\n        if (swoole_event_is_available()) {\n            serv->stop_async_worker(worker);\n        }\n        return SW_OK;\n    } else if (task->info.type == SW_SERVER_EVENT_COMMAND_REQUEST) {\n        ret = TaskWorker_call_command_handler(pool, worker, task);\n    } else {\n        ret = serv->onTask(serv, task);\n        /**\n         * only server task as requests,\n         * do not increase the count for pipeline communication and command processing.\n         */\n        worker->add_request_count();\n    }\n    worker->set_status_to_idle();\n\n    return ret;\n}\n\nvoid Server::task_dump(EventData *task) {\n    char buf[1024];\n    task->info.dump(buf, sizeof(buf));\n    sw_printf(\"%s\", buf);\n\n    if (task->info.ext_flags & SW_TASK_TMPFILE) {\n        auto pkg = reinterpret_cast<PacketTask *>(task->data);\n        sw_printf(\"Task[tmpfile]=%.*s\\n\", (int) pkg->length, pkg->tmpfile);\n    }\n}\n\nbool Server::task_pack(EventData *task, const void *_data, size_t _length) {\n    task->info = {};\n    task->info.type = SW_SERVER_EVENT_TASK;\n    task->info.fd = SwooleG.current_task_id++;\n    task->info.reactor_id = swoole_get_worker_id();\n    task->info.time = microtime();\n\n    if (_length < SW_IPC_MAX_SIZE - sizeof(task->info)) {\n        memcpy(task->data, _data, _length);\n        task->info.len = _length;\n        return true;\n    }\n\n    PacketTask pkg{};\n    File file = make_tmpfile();\n    if (!file.ready()) {\n        return false;\n    }\n\n    if (file.write_all(_data, _length) != _length) {\n        swoole_warning(\"write to tmpfile failed\");\n        return false;\n    }\n\n    task->info.len = sizeof(pkg);\n    task->info.ext_flags |= SW_TASK_TMPFILE;\n    swoole_strlcpy(pkg.tmpfile, file.get_path().c_str(), sizeof(pkg.tmpfile));\n    pkg.length = _length;\n    memcpy(task->data, &pkg, sizeof(pkg));\n\n    return true;\n}\n\nbool Server::task(EventData *_task, int *dst_worker_id, bool blocking) {\n    sw_atomic_fetch_add(&gs->tasking_num, 1);\n\n    swResultCode retval;\n    if (blocking) {\n        retval = get_task_worker_pool()->dispatch_sync(_task, dst_worker_id);\n    } else {\n        retval = get_task_worker_pool()->dispatch(_task, dst_worker_id);\n    }\n\n    if (retval == SW_OK) {\n        sw_atomic_fetch_add(&gs->task_count, 1);\n        return true;\n    }\n\n    sw_atomic_fetch_sub(&gs->tasking_num, 1);\n    return false;\n}\n\nbool Server::task_sync(EventData *_task, int *dst_worker_id, double timeout) {\n    uint64_t notify;\n    EventData *task_result = get_task_result();\n    sw_memset_zero(task_result, sizeof(*task_result));\n    Pipe *pipe = task_notify_pipes.at(swoole_get_worker_id()).get();\n    TaskId task_id = get_task_id(_task);\n\n    pipe->clean();\n    pipe->set_timeout(timeout);\n\n    if (!task(_task, dst_worker_id, true)) {\n        return false;\n    }\n\n    SW_LOOP {\n        if (pipe->read(&notify, sizeof(notify)) > 0) {\n            if (get_task_id(task_result) != task_id) {\n                continue;\n            }\n            return true;\n        }\n        break;\n    }\n\n    return false;\n}\n\nint Server::MultiTask::find(TaskId task_id) {\n    auto iter = map.find(task_id);\n    if (iter != map.end()) {\n        return iter->second;\n    } else {\n        return -1;\n    }\n}\n\nbool Server::task_sync(MultiTask &mtask, double timeout) {\n    WorkerId worker_id = swoole_get_worker_id();\n    uint64_t notify;\n    EventData *task_result = get_task_result();\n    task_result->info = {};\n    Pipe *pipe = task_notify_pipes.at(worker_id).get();\n    Worker *worker = get_worker(worker_id);\n    int dst_worker_id;\n\n    File fp = make_tmpfile();\n    if (!fp.ready()) {\n        swoole_set_last_error(errno);\n        return false;\n    }\n\n    std::string file_path = fp.get_path();\n    fp.close();\n\n    auto finish_count = reinterpret_cast<int *>(task_result->data);\n\n    worker->lock->lock();\n    *finish_count = 0;\n\n    swoole_strlcpy(task_result->data + 4, file_path.c_str(), SW_TASK_TMP_PATH_SIZE);\n    worker->lock->unlock();\n\n    // clear history task\n    pipe->clean();\n\n    auto n_task = mtask.count;\n    SW_LOOP_N(mtask.count) {\n        EventData buf;\n        TaskId task_id = mtask.pack(i, &buf);\n        if (task_id < 0) {\n            swoole_warning(\"task pack failed\");\n            goto _fail;\n        }\n        buf.info.ext_flags |= SW_TASK_WAITALL;\n        sw_atomic_fetch_add(&gs->tasking_num, 1);\n        dst_worker_id = -1;\n        if (!task(&buf, &dst_worker_id, true)) {\n            swoole_warning(\"failed to dispatch task\");\n            task_id = -1;\n        _fail:\n            mtask.fail(i);\n            n_task--;\n        } else {\n            sw_atomic_fetch_sub(&gs->tasking_num, 1);\n        }\n        mtask.map[task_id] = i;\n    }\n\n    if (n_task == 0) {\n        swoole_set_last_error(SW_ERROR_TASK_DISPATCH_FAIL);\n        return false;\n    }\n\n    if (timeout > 0) {\n        pipe->set_timeout(timeout);\n    }\n\n    double stated_at = microtime();\n    while (*finish_count < n_task) {\n        const int ret = pipe->read(&notify, sizeof(notify));\n        if (ret <= 0) {\n            break;\n        }\n        if (timeout > 0 && microtime() - stated_at > timeout) {\n            break;\n        }\n    }\n\n    worker->lock->lock();\n    auto content = file_get_contents(file_path);\n    worker->lock->unlock();\n\n    if (!content) {\n        return false;\n    }\n\n    do {\n        auto *result = reinterpret_cast<EventData *>(content->str + content->offset);\n        int index = mtask.find(get_task_id(result));\n        if (index != -1) {\n            mtask.unpack(index, result);\n        }\n        content->offset += result->size();\n    } while (content->offset < 0 || (size_t) content->offset < content->length);\n    // delete tmp file\n    unlink(file_path.c_str());\n\n    return true;\n}\n\nbool Server::task_unpack(EventData *task, String *buffer, PacketPtr *packet) {\n    if (!(task->info.ext_flags & SW_TASK_TMPFILE)) {\n        packet->data = task->data;\n        packet->length = task->info.len;\n        return true;\n    }\n\n    PacketTask _pkg{};\n    memcpy(&_pkg, task->data, sizeof(_pkg) - 1);\n\n    File fp(_pkg.tmpfile, O_RDONLY);\n    if (!fp.ready()) {\n        swoole_sys_warning(\"open(%s) failed\", _pkg.tmpfile);\n        return false;\n    }\n    if (buffer->size < _pkg.length) {\n        buffer->extend(_pkg.length);\n    }\n    if (fp.read_all(buffer->str, _pkg.length) != _pkg.length) {\n        return false;\n    }\n    if (!(task->info.ext_flags & SW_TASK_PEEK)) {\n        unlink(_pkg.tmpfile);\n    }\n    buffer->length = _pkg.length;\n    packet->data = buffer->str;\n    packet->length = buffer->length;\n    return true;\n}\n\nstatic void TaskWorker_signal_init(ProcessPool *pool) {\n    auto *serv = static_cast<Server *>(pool->ptr);\n    if (serv->is_thread_mode()) {\n        return;\n    }\n    swoole_signal_set(SIGHUP, nullptr);\n    swoole_signal_set(SIGPIPE, nullptr);\n    swoole_signal_set(SIGUSR1, nullptr);\n    swoole_signal_set(SIGUSR2, nullptr);\n    swoole_signal_set(SIGTERM, Server::worker_signal_handler);\n    swoole_signal_set(SIGWINCH, Server::worker_signal_handler);\n#ifdef SIGRTMIN\n    swoole_signal_set(SIGRTMIN, Server::worker_signal_handler);\n#endif\n}\n\nstatic void TaskWorker_onStart(ProcessPool *pool, Worker *worker) {\n    auto serv = static_cast<Server *>(pool->ptr);\n\n    swoole_set_worker_id(worker->id);\n    swoole_set_worker_type(SW_TASK_WORKER);\n    /**\n     * Make the task worker support asynchronous\n     */\n    if (serv->task_enable_coroutine) {\n        if (swoole_event_init(0) < 0) {\n            swoole_error(\"[TaskWorker] create reactor failed\");\n            return;\n        }\n        SwooleG.enable_signalfd = 1;\n    } else {\n        SwooleG.enable_signalfd = 0;\n        SwooleTG.reactor = nullptr;\n    }\n\n    TaskWorker_signal_init(pool);\n    serv->worker_start_callback(worker);\n\n    worker->init();\n    worker->set_max_request(pool->max_request, pool->max_request_grace);\n}\n\nstatic void TaskWorker_onStop(ProcessPool *pool, Worker *worker) {\n    swoole_event_free();\n    auto *serv = static_cast<Server *>(pool->ptr);\n    serv->worker_stop_callback(worker);\n}\n\n/**\n * receive data from worker process\n */\nstatic int TaskWorker_onPipeReceive(Reactor *reactor, Event *event) {\n    EventData task;\n    auto *pool = static_cast<ProcessPool *>(reactor->ptr);\n    Worker *worker = sw_worker();\n    auto *serv = static_cast<Server *>(pool->ptr);\n\n    if (event->socket->read(&task, sizeof(task)) > 0) {\n        int retval = pool->onTask(pool, worker, &task);\n        // maximum number of requests, process will exit.\n        if (worker->has_exceeded_max_request()) {\n            serv->stop_async_worker(worker);\n        }\n        return retval;\n    } else {\n        swoole_sys_warning(\"read(%d, %ld) failed\", event->fd, sizeof(task));\n        return SW_ERR;\n    }\n}\n\n/**\n * async task worker\n */\nstatic int TaskWorker_loop_async(ProcessPool *pool, Worker *worker) {\n    auto *serv = static_cast<Server *>(pool->ptr);\n    Socket *socket = serv->get_worker_pipe_worker_in_message_bus(worker);\n    worker->set_status_to_idle();\n\n    socket->set_nonblock();\n    sw_reactor()->ptr = pool;\n    swoole_event_add(socket, SW_EVENT_READ);\n    swoole_event_set_handler(SW_FD_PIPE, SW_EVENT_READ, TaskWorker_onPipeReceive);\n\n    for (uint i = 0; i < serv->worker_num + serv->task_worker_num; i++) {\n        worker = serv->get_worker(i);\n        serv->get_worker_pipe_worker_in_message_bus(worker)->buffer_size = UINT_MAX;\n        serv->get_worker_pipe_master_in_message_bus(worker)->buffer_size = UINT_MAX;\n    }\n\n    return swoole_event_wait();\n}\n\n/**\n * Send the task result to worker\n */\nbool Server::finish(const char *data, size_t data_len, int flags, const EventData *current_task) {\n    if (task_worker_num < 1) {\n        swoole_warning(\"cannot use Server::task()/Server::finish() method, because no set [task_worker_num]\");\n        return false;\n    }\n    if (current_task == nullptr) {\n        current_task = latest_task;\n    }\n    if (current_task->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) {\n        swoole_warning(\"Server::task()/Server::finish() is not supported in onPipeMessage callback\");\n        return false;\n    }\n    if (current_task->info.ext_flags & SW_TASK_NOREPLY) {\n        swoole_warning(\"Server::finish() can only be used in the worker process\");\n        return false;\n    }\n\n    uint16_t source_worker_id = current_task->info.reactor_id;\n    Worker *worker = get_worker(source_worker_id);\n\n    if (worker == nullptr) {\n        swoole_warning(\"invalid worker_id[%d]\", source_worker_id);\n        return false;\n    }\n\n    ssize_t retval;\n    // for swoole_server_task\n    if (current_task->info.ext_flags & SW_TASK_NONBLOCK) {\n        // write to file\n        EventData buf;\n        if (!task_pack(&buf, data, data_len)) {\n            swoole_warning(\"large task pack failed()\");\n            return false;\n        }\n        // callback function\n        if (current_task->info.ext_flags & SW_TASK_CALLBACK) {\n            flags |= SW_TASK_CALLBACK;\n        } else if (current_task->info.ext_flags & SW_TASK_COROUTINE) {\n            flags |= SW_TASK_COROUTINE;\n        }\n        buf.info.ext_flags |= flags;\n        buf.info.type = SW_SERVER_EVENT_FINISH;\n        buf.info.fd = get_task_id(current_task);\n\n        if (worker->pool->use_socket && worker->pool->stream_info_->last_connection) {\n            uint32_t _len = htonl(data_len);\n            retval = worker->pool->stream_info_->last_connection->send_sync((void *) &_len, sizeof(_len));\n            if (retval > 0) {\n                retval = worker->pool->stream_info_->last_connection->send_sync(data, data_len);\n            }\n        } else {\n            retval = send_to_worker_from_worker(worker, &buf, buf.size(), SW_PIPE_MASTER);\n        }\n    } else {\n        uint64_t flag = 1;\n\n        /**\n         * Use worker shm store the result\n         */\n        EventData *result = &(task_results[source_worker_id]);\n        Pipe *pipe = task_notify_pipes.at(source_worker_id).get();\n\n        // lock worker\n        worker->lock->lock();\n\n        if (current_task->info.ext_flags & SW_TASK_WAITALL) {\n            auto *finish_count = reinterpret_cast<sw_atomic_t *>(result->data);\n            char *_tmpfile = result->data + 4;\n            File file(_tmpfile, O_APPEND | O_WRONLY);\n            if (file.ready()) {\n                EventData buf;\n                if (!task_pack(&buf, data, data_len)) {\n                    swoole_warning(\"large task pack failed()\");\n                    buf.info.len = 0;\n                }\n                buf.info.ext_flags |= flags;\n                buf.info.type = SW_SERVER_EVENT_FINISH;\n                buf.info.fd = get_task_id(current_task);\n                size_t bytes = buf.size();\n                swoole_trace_log(SW_TRACE_SERVER, \"write %zu bytes to tmp file '%s'\", bytes, _tmpfile);\n\n                if (file.write_all(&buf, bytes) != bytes) {\n                    swoole_sys_warning(\"write(%s, %ld) failed\", _tmpfile, bytes);\n                }\n                sw_atomic_fetch_add(finish_count, 1);\n            }\n        } else {\n            if (!task_pack(result, data, data_len)) {\n                // unlock worker\n                worker->lock->unlock();\n                swoole_warning(\"large task pack failed()\");\n                return false;\n            }\n            result->info.ext_flags |= flags;\n            result->info.type = SW_SERVER_EVENT_FINISH;\n            result->info.fd = get_task_id(current_task);\n        }\n\n        // unlock worker\n        worker->lock->unlock();\n\n        while (true) {\n            retval = pipe->write(&flag, sizeof(flag));\n            auto _sock = pipe->get_socket(true);\n            if (retval < 0 && _sock->catch_write_error(errno) == SW_WAIT) {\n                if (_sock->wait_event(-1, SW_EVENT_WRITE) == 0) {\n                    continue;\n                }\n            }\n            break;\n        }\n    }\n    if (retval < 0) {\n        if (errno == EAGAIN || errno == ETIMEDOUT || swoole_get_last_error() == SW_ERROR_SOCKET_POLL_TIMEOUT) {\n            swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT, \"send result to worker timed out\");\n        } else {\n            swoole_sys_warning(\"send result to worker failed\");\n        }\n        return false;\n    }\n    return true;\n}\n}  // namespace swoole\n"
  },
  {
    "path": "src/server/thread.cc",
    "content": "/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n#include \"swoole_server.h\"\n#include \"swoole_thread.h\"\n\nnamespace swoole {\nusing network::Socket;\n\nenum ManagerCommand {\n    CMD_RELOAD = 0x1001,\n    CMD_MANAGER_EXIT = 0x1002,\n};\n\nstatic inline Worker *cmd_ptr(const ManagerCommand cmd) {\n    return reinterpret_cast<Worker *>(cmd);\n}\n\nFactory *Server::create_thread_factory() {\n#ifndef SW_THREAD\n    swoole_error_log(SW_LOG_ERROR,\n                     SW_ERROR_OPERATION_NOT_SUPPORT,\n                     \"Thread support is not enabled, cannot create server with MODE_THREAD\");\n    return nullptr;\n#endif\n    reactor_num = worker_num;\n    connection_list = static_cast<Connection *>(sw_calloc(max_connection, sizeof(Connection)));\n    if (connection_list == nullptr) {\n        swoole_sys_warning(\"calloc[2](%d) failed\", static_cast<int>(max_connection * sizeof(Connection)));\n        return nullptr;\n    }\n    reactor_threads = new ReactorThread[reactor_num]();\n    reactor_pipe_num = 1;\n    return new ThreadFactory(this);\n}\n\nvoid Server::destroy_thread_factory() const {\n    sw_free(connection_list);\n    delete[] reactor_threads;\n}\n\nThreadFactory::ThreadFactory(Server *server) : BaseFactory(server) {\n    threads_.resize(server_->get_all_worker_num() + 1);\n    SW_LOOP_N(server_->get_all_worker_num() + 1) {\n        threads_[i] = std::make_shared<Thread>();\n    }\n}\n\nThreadFactory::~ThreadFactory() {\n    ThreadFactory::shutdown();\n}\n\nbool ThreadFactory::start() {\n    if (!server_->create_worker_pipes()) {\n        return false;\n    }\n    if (!server_->create_event_workers()) {\n        return false;\n    }\n    if (server_->task_worker_num > 0 && server_->get_task_worker_pool()->start_check() < 0) {\n        return false;\n    }\n    if (server_->get_user_worker_num() > 0 && !server_->create_user_workers()) {\n        return false;\n    }\n    return true;\n}\n\nbool ThreadFactory::shutdown() {\n    for (const auto &thread : threads_) {\n        if (thread->joinable()) {\n            thread->join();\n        }\n    }\n    return true;\n}\n\nvoid ThreadFactory::at_thread_enter(WorkerId id, int worker_type) {\n    swoole_thread_init(false);\n\n    swoole_set_worker_type(worker_type);\n    swoole_set_worker_id(id);\n    swoole_set_worker_pid(swoole_thread_get_native_id());\n\n    swoole_set_thread_id(id);\n    swoole_set_thread_type(Server::THREAD_WORKER);\n\n    swoole_trace_log(SW_TRACE_THREAD, \"at_thread_enter=%d\", id);\n}\n\nvoid ThreadFactory::push_to_wait_queue(Worker *worker) {\n    lock_.lock();\n    queue_.push(worker);\n    lock_.unlock();\n    cv_.notify_one();\n\n    swoole_trace_log(SW_TRACE_THREAD, \"push [%p] to wait queue\", worker);\n}\n\nvoid ThreadFactory::at_thread_exit(Worker *worker) {\n    if (worker) {\n        push_to_wait_queue(worker);\n    }\n\n    swoole_trace_log(SW_TRACE_THREAD, \"at_thread_exit=%d\", worker->id);\n    swoole_thread_clean(false);\n}\n\nvoid ThreadFactory::create_message_bus() const {\n    auto mb = new MessageBus();\n    mb->set_id_generator(server_->msg_id_generator);\n    mb->set_buffer_size(server_->ipc_max_size);\n    mb->set_always_chunked_transfer();\n    if (!mb->alloc_buffer()) {\n        throw std::bad_alloc();\n    }\n    server_->init_pipe_sockets(mb);\n    SwooleTG.message_bus = mb;\n}\n\nvoid ThreadFactory::destroy_message_bus() {\n    SwooleTG.message_bus->clear();\n    delete SwooleTG.message_bus;\n    SwooleTG.message_bus = nullptr;\n}\n\nvoid ThreadFactory::spawn_event_worker(WorkerId i) {\n    threads_[i]->start([=]() {\n        at_thread_enter(i, SW_EVENT_WORKER);\n\n        Worker *worker = server_->get_worker(i);\n        worker->type = SW_EVENT_WORKER;\n        worker->pid = swoole_get_worker_pid();\n        SwooleWG.worker = worker;\n        server_->worker_thread_start(threads_[i], [=]() { Server::reactor_thread_main_loop(server_, i); });\n\n        at_thread_exit(worker);\n    });\n}\n\nvoid ThreadFactory::spawn_task_worker(WorkerId i) {\n    threads_[i]->start([=]() {\n        at_thread_enter(i, SW_TASK_WORKER);\n\n        create_message_bus();\n        Worker *worker = server_->get_worker(i);\n        worker->type = SW_TASK_WORKER;\n        worker->pid = swoole_get_worker_pid();\n        worker->set_status_to_idle();\n        SwooleWG.worker = worker;\n        const auto pool = server_->get_task_worker_pool();\n        server_->worker_thread_start(threads_[i], [=]() {\n            if (pool->onWorkerStart != nullptr) {\n                pool->onWorkerStart(pool, worker);\n            }\n            pool->main_loop(pool, worker);\n            if (pool->onWorkerStop != nullptr) {\n                pool->onWorkerStop(pool, worker);\n            }\n        });\n        destroy_message_bus();\n\n        at_thread_exit(worker);\n    });\n}\n\nvoid ThreadFactory::spawn_user_worker(WorkerId i) {\n    threads_[i]->start([=]() {\n        at_thread_enter(i, SW_USER_WORKER);\n\n        create_message_bus();\n        Worker *worker = server_->get_worker(i);\n        worker->type = SW_USER_WORKER;\n        worker->pid = swoole_get_worker_pid();\n        SwooleWG.worker = worker;\n        server_->worker_thread_start(threads_[i], [=]() { server_->onUserWorkerStart(server_, worker); });\n        destroy_message_bus();\n\n        at_thread_exit(worker);\n    });\n}\n\nvoid ThreadFactory::spawn_manager_thread(WorkerId i) {\n    threads_[i]->start([=]() {\n        at_thread_enter(i, SW_MANAGER);\n\n        swoole_timer_create(true);\n\n        server_->worker_thread_start(threads_[i], [=]() {\n            if (server_->onManagerStart) {\n                server_->onManagerStart(server_);\n            }\n            wait();\n            if (server_->onManagerStop) {\n                server_->onManagerStop(server_);\n            }\n        });\n\n        if (server_->running) {\n            swoole_warning(\"Fatal Error: manager thread exits abnormally\");\n        }\n\n        /*\n         * In the function that closes the timer, the scheduler is called again;\n         * therefore, it is essential to set the scheduler to null after the timer has been consumed.\n         */\n        if (swoole_timer_is_available()) {\n            swoole_timer_free();\n        }\n\n        at_thread_exit(nullptr);\n    });\n}\n\nvoid ThreadFactory::wait() {\n    while (true) {\n        std::unique_lock<std::mutex> lock(lock_);\n        int64_t cv_timeout_ms_ = swoole_timer_get_next_msec();\n        if (cv_timeout_ms_ > 0) {\n            cv_.wait_for(lock, std::chrono::milliseconds(cv_timeout_ms_), [this] { return !queue_.empty(); });\n        } else {\n            cv_.wait(lock, [this] { return !queue_.empty(); });\n        }\n\n        swoole_trace_log(SW_TRACE_THREAD, \"manager thread is waiting for worker exit, queue size: %zu\", queue_.size());\n\n        if (!queue_.empty()) {\n            Worker *exited_worker = queue_.front();\n            queue_.pop();\n            lock.unlock();\n\n            if (exited_worker == cmd_ptr(CMD_RELOAD)) {\n                goto _do_reload;\n            }\n            if (exited_worker == cmd_ptr(CMD_MANAGER_EXIT)) {\n                break;\n            }\n\n            swoole_trace_log(SW_TRACE_THREAD,\n                             \"worker(type=%d, tid=%d, id=%d) exit, status=%d\",\n                             exited_worker->type,\n                             exited_worker->pid,\n                             exited_worker->id,\n                             exited_worker->status);\n\n            auto thread = threads_[exited_worker->id];\n            int status_code = thread->get_exit_status();\n            if (status_code != 0) {\n                ExitStatus exit_status(exited_worker->pid, status_code << 8);\n                server_->call_worker_error_callback(exited_worker, exit_status);\n                swoole_trace_log(SW_TRACE_THREAD,\n                                 \"worker(tid=%d, id=%d) abnormal exit, status=%d\",\n                                 exit_status.get_pid(),\n                                 exited_worker->id,\n                                 exit_status.get_code());\n            }\n\n            thread->join();\n            swoole_trace_log(SW_TRACE_THREAD, \"thread=%d join\", exited_worker->id);\n\n            switch (exited_worker->type) {\n            case SW_EVENT_WORKER:\n                spawn_event_worker(exited_worker->id);\n                break;\n            case SW_TASK_WORKER:\n                spawn_task_worker(exited_worker->id);\n                break;\n            case SW_USER_WORKER:\n                spawn_user_worker(exited_worker->id);\n                break;\n            default:\n                abort();\n                break;\n            }\n        }\n\n        if (sw_timer()) {\n            sw_timer()->select();\n        }\n        if (server_->running && reloading) {\n        _do_reload:\n            do_reload();\n        }\n    }\n}\n\nThreadReloadTask::ThreadReloadTask(Server *_server, bool _reload_all_workers) {\n    server_ = _server;\n    worker_num = server_->get_core_worker_num();\n    // If only reloading task workers, skip the event workers.\n    reloaded_num = _reload_all_workers ? 0 : server_->worker_num;\n}\n\nvoid ThreadFactory::do_reload() {\n    if (!reload_task) {\n        reload_task = std::make_shared<ThreadReloadTask>(server_, reload_all_workers);\n        if (server_->onBeforeReload) {\n            server_->onBeforeReload(server_);\n        }\n    }\n    server_->kill_worker(reload_task->reloaded_num++);\n    if (reload_task->is_completed()) {\n        reload_task.reset();\n        reloading = 0;\n        if (server_->onAfterReload) {\n            server_->onAfterReload(server_);\n        }\n    }\n}\n\nbool ThreadFactory::reload(bool _reload_all_workers) {\n    auto _what = _reload_all_workers ? \"all\" : \"task\";\n\n    if (server_->task_worker_num == 0 && !_reload_all_workers) {\n        swoole_error_log(SW_LOG_WARNING,\n                         SW_ERROR_OPERATION_NOT_SUPPORT,\n                         \"Cannot reload %s workers, task workers are not started\",\n                         _what);\n        return false;\n    }\n\n    // Prevent duplicate submission of reload requests.\n    if (!sw_atomic_cmp_set(&reloading, 0, 1)) {\n        swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT);\n        return false;\n    }\n\n    reload_all_workers = _reload_all_workers;\n    if (!server_->is_manager()) {\n        swoole_info(\"Send a notification to the manager process to prepare for restarting %s worker processes.\", _what);\n        push_to_wait_queue(cmd_ptr(CMD_RELOAD));\n    } else {\n        swoole_info(\"Server is reloading %s workers now\", _what);\n        do_reload();\n    }\n\n    return true;\n}\n\nWorkerId ThreadFactory::get_manager_thread_id() const {\n    return server_->get_all_worker_num();\n}\n\nWorkerId ThreadFactory::get_master_thread_id() const {\n    return server_->get_all_worker_num() + 1;\n}\n\nvoid ThreadFactory::terminate_manager_thread() {\n    swoole_trace_log(SW_TRACE_THREAD, \"notify manager thread to exit\");\n    push_to_wait_queue(cmd_ptr(CMD_MANAGER_EXIT));\n\n    /**\n     * When terminating the service, the management thread may still be joining other worker threads,\n     * so it is essential to first reclaim the management thread to ensure it has exited.\n     * During the shutdown, the running flag has already been set to false,\n     * which means the management thread might not have reclaimed all worker threads and may have exited prematurely.\n     * At this point, it is necessary to loop through and reclaim the remaining worker threads.\n     */\n    auto manager_thread_id = get_manager_thread_id();\n    threads_[manager_thread_id]->join();\n\n    swoole_trace_log(SW_TRACE_THREAD, \"manager thread is exited\");\n}\n\nint Server::start_worker_threads() {\n    auto *_factory = dynamic_cast<ThreadFactory *>(factory_);\n\n    if (task_worker_num > 0) {\n        SW_LOOP_N(task_worker_num) {\n            _factory->spawn_task_worker(worker_num + i);\n        }\n    }\n\n    SW_LOOP_N(worker_num) {\n        _factory->spawn_event_worker(i);\n    }\n\n    if (!user_worker_list.empty()) {\n        for (size_t i = 0; i < user_worker_list.size(); i++) {\n            _factory->spawn_user_worker(task_worker_num + worker_num + i);\n        }\n    }\n\n    auto manager_thread_id = _factory->get_manager_thread_id();\n    _factory->spawn_manager_thread(manager_thread_id);\n\n    if (swoole_event_init(0) < 0) {\n        return SW_ERR;\n    }\n\n    Reactor *reactor = sw_reactor();\n    for (const auto port : ports) {\n        if (port->is_dgram()) {\n            continue;\n        }\n        if (port->listen() < 0) {\n            swoole_event_free();\n            return SW_ERR;\n        }\n        reactor->add(port->socket, SW_EVENT_READ);\n    }\n\n    SwooleTG.id = reactor->id = _factory->get_master_thread_id();\n    store_listen_socket();\n\n    return start_master_thread(reactor);\n}\n\nvoid Server::stop_worker_threads() {\n    auto *_factory = dynamic_cast<ThreadFactory *>(factory_);\n    _factory->terminate_manager_thread();\n\n    SW_LOOP_N(get_core_worker_num()) {\n        kill_worker(i);\n    }\n}\n\nbool Server::reload_worker_threads(bool reload_all_workers) const {\n    auto *_factory = dynamic_cast<ThreadFactory *>(factory_);\n    return _factory->reload(reload_all_workers);\n}\n\n}  // namespace swoole\n"
  },
  {
    "path": "src/server/worker.cc",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include <sys/uio.h>\n#include <sys/mman.h>\n\n#include \"swoole_server.h\"\n#include \"swoole_memory.h\"\n#include \"swoole_coroutine.h\"\n\n#include \"swoole_api.h\"\n\nnamespace swoole {\nusing namespace network;\n\nstatic int Worker_onPipeReceive(Reactor *reactor, Event *event);\nstatic void Worker_reactor_try_to_exit(Reactor *reactor);\n\nstatic void Worker_reopen_logger() {\n    if (sw_logger()) {\n        sw_logger()->reopen();\n    }\n}\n\nvoid Server::worker_signal_init() const {\n    if (is_thread_mode()) {\n        return;\n    }\n    swoole_signal_set(SIGHUP, nullptr);\n    swoole_signal_set(SIGPIPE, SIG_IGN);\n    swoole_signal_set(SIGUSR1, nullptr);\n    swoole_signal_set(SIGUSR2, nullptr);\n    swoole_signal_set(SIGTERM, worker_signal_handler);\n    swoole_signal_set(SIGWINCH, worker_signal_handler);\n#ifdef SIGRTMIN\n    swoole_signal_set(SIGRTMIN, worker_signal_handler);\n#endif\n}\n\nvoid Server::worker_signal_handler(int signo) {\n    if (!SwooleG.running || !sw_server() || !sw_worker() || !sw_server()->is_running()) {\n        return;\n    }\n    switch (signo) {\n    case SIGTERM:\n        if (swoole_event_is_available()) {\n            sw_server()->stop_async_worker(sw_worker());\n        } else {\n            sw_worker()->shutdown();\n        }\n        break;\n    case SIGWINCH:\n        Worker_reopen_logger();\n        break;\n    default:\n#ifdef SIGRTMIN\n        if (signo == SIGRTMIN) {\n            Worker_reopen_logger();\n        }\n#endif\n        break;\n    }\n}\n\nstatic sw_inline bool Worker_discard_data(const Server *serv, const Connection *conn, const DataHead *info) {\n    if (conn == nullptr) {\n        if (serv->disable_notify && !serv->discard_timeout_request) {\n            return false;\n        }\n        goto _discard_data;\n    } else {\n        if (conn->closed) {\n            goto _discard_data;\n        } else {\n            return false;\n        }\n    }\n_discard_data:\n    swoole_error_log(SW_LOG_WARNING,\n                     SW_ERROR_SESSION_DISCARD_TIMEOUT_DATA,\n                     \"[2] ignore data[%u bytes] received from session#%ld\",\n                     info->len,\n                     info->fd);\n    return true;\n}\n\ntypedef std::function<int(Server *, RecvData *)> TaskCallback;\n\nstatic sw_inline void Worker_do_task(Server *serv, Worker *worker, const DataHead *info, const TaskCallback &callback) {\n    RecvData recv_data;\n    auto packet = serv->get_worker_message_bus()->get_packet();\n    recv_data.info = *info;\n    recv_data.info.len = packet.length;\n    recv_data.data = packet.data;\n\n    if (callback(serv, &recv_data) == SW_OK) {\n        worker->add_request_count();\n        sw_atomic_fetch_add(&serv->gs->request_count, 1);\n    }\n}\n\nvoid Server::worker_accept_event(DataHead *info) {\n    Worker *worker = sw_worker();\n    worker->set_status_to_busy();\n\n    switch (info->type) {\n    case SW_SERVER_EVENT_RECV_DATA: {\n        Connection *conn = get_connection_verify(info->fd);\n        if (conn) {\n            if (info->len > 0) {\n                auto packet = get_worker_message_bus()->get_packet();\n                sw_atomic_fetch_sub(&conn->recv_queued_bytes, packet.length);\n                swoole_trace_log(SW_TRACE_SERVER,\n                                 \"[Worker] session_id=%ld, len=%lu, qb=%d\",\n                                 conn->session_id,\n                                 packet.length,\n                                 conn->recv_queued_bytes);\n            }\n            conn->last_dispatch_time = info->time;\n        }\n        if (!Worker_discard_data(this, conn, info)) {\n            Worker_do_task(this, worker, info, onReceive);\n        }\n        break;\n    }\n    case SW_SERVER_EVENT_RECV_DGRAM: {\n        Worker_do_task(this, worker, info, onPacket);\n        break;\n    }\n    case SW_SERVER_EVENT_CLOSE: {\n        Connection *conn = get_connection_verify_no_ssl(info->fd);\n        if (conn && conn->ssl_client_cert && conn->ssl_client_cert_pid == swoole_get_worker_pid()) {\n            delete conn->ssl_client_cert;\n            conn->ssl_client_cert = nullptr;\n        }\n        factory_->end(info->fd, false);\n        break;\n    }\n    case SW_SERVER_EVENT_CONNECT: {\n        // SSL client certificate\n        if (info->len > 0) {\n            Connection *conn = get_connection_verify_no_ssl(info->fd);\n            if (conn) {\n                auto packet = get_worker_message_bus()->get_packet();\n                conn->ssl_client_cert = new String(packet.data, packet.length);\n                conn->ssl_client_cert_pid = swoole_get_worker_pid();\n            }\n        }\n        if (onConnect) {\n            onConnect(this, info);\n        }\n        break;\n    }\n\n    case SW_SERVER_EVENT_BUFFER_FULL: {\n        if (onBufferFull) {\n            onBufferFull(this, info);\n        }\n        break;\n    }\n    case SW_SERVER_EVENT_BUFFER_EMPTY: {\n        if (onBufferEmpty) {\n            onBufferEmpty(this, info);\n        }\n        break;\n    }\n    case SW_SERVER_EVENT_FINISH: {\n        onFinish(this, reinterpret_cast<EventData *>(get_worker_message_bus()->get_buffer()));\n        break;\n    }\n    case SW_SERVER_EVENT_PIPE_MESSAGE: {\n        onPipeMessage(this, reinterpret_cast<EventData *>(get_worker_message_bus()->get_buffer()));\n        break;\n    }\n    case SW_SERVER_EVENT_COMMAND_REQUEST: {\n        call_command_handler(message_bus, worker->id, pipe_command->get_socket(false));\n        break;\n    }\n    case SW_SERVER_EVENT_SHUTDOWN: {\n        stop_async_worker(worker);\n        break;\n    }\n    default:\n        swoole_warning(\"[Worker] error event[type=%d]\", (int) info->type);\n        break;\n    }\n\n    worker->set_status_to_idle();\n\n    // maximum number of requests, process will exit.\n    if (worker->has_exceeded_max_request()) {\n        if (is_thread_mode()) {\n            Reactor *reactor = sw_reactor();\n            get_thread(reactor->id)->shutdown(reactor);\n        } else {\n            stop_async_worker(worker);\n        }\n    }\n}\n\nvoid Server::worker_start_callback(Worker *worker) {\n    if (swoole_is_root_user()) {\n        swoole_set_isolation(group_, user_, chroot_);\n    }\n\n    SW_LOOP_N(worker_num + task_worker_num) {\n        if (worker->id == i) {\n            continue;\n        }\n        Worker *other_worker = get_worker(i);\n        if (is_worker() && other_worker->pipe_master) {\n            other_worker->pipe_master->set_nonblock();\n        }\n    }\n\n    worker->set_status_to_idle();\n\n    if (is_process_mode()) {\n        sw_shm_protect(session_list, PROT_READ);\n    }\n\n    call_worker_start_callback(worker);\n}\n\nvoid Server::worker_stop_callback(Worker *worker) {\n    call_worker_stop_callback(worker);\n}\n\nvoid Server::call_worker_start_callback(Worker *worker) {\n    void *hook_args[2];\n    hook_args[0] = this;\n    hook_args[1] = (void *) (uintptr_t) worker->id;\n\n    if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_START)) {\n        swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_START, hook_args);\n    }\n    if (isset_hook(HOOK_WORKER_START)) {\n        call_hook(HOOK_WORKER_START, hook_args);\n    }\n\n    swoole_clear_last_error();\n    swoole_clear_last_error_msg();\n\n    if (onWorkerStart) {\n        onWorkerStart(this, worker);\n    }\n}\n\nvoid Server::call_worker_stop_callback(Worker *worker) {\n    void *hook_args[2];\n    hook_args[0] = this;\n    hook_args[1] = (void *) (uintptr_t) worker->id;\n\n    if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_STOP)) {\n        swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_STOP, hook_args);\n    }\n    if (onWorkerStop) {\n        onWorkerStop(this, worker);\n    }\n\n    if (!get_worker_message_bus()->empty()) {\n        swoole_error_log(\n            SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA, \"unprocessed data in the worker process buffer\");\n        get_worker_message_bus()->clear();\n    }\n\n    SwooleWG.running = false;\n    if (SwooleWG.worker_copy) {\n        delete SwooleWG.worker_copy;\n        SwooleWG.worker_copy = nullptr;\n        SwooleWG.worker = nullptr;\n    }\n}\n\nvoid Server::call_worker_error_callback(Worker *worker, const ExitStatus &status) {\n    if (onWorkerError != nullptr) {\n        onWorkerError(this, worker, status);\n    }\n    /**\n     * The work process has exited unexpectedly, requiring a cleanup of the shared memory state.\n     * This must be done between the termination of the old process and the initiation of the new one;\n     * otherwise, data contention may occur.\n     */\n    if (worker->type == SW_EVENT_WORKER) {\n        abort_worker(worker);\n    }\n}\n\nbool Server::kill_worker(int worker_id) {\n    auto current_worker = sw_worker();\n    if (!current_worker && worker_id < 0) {\n        swoole_error_log(\n            SW_LOG_WARNING, SW_ERROR_WRONG_OPERATION, \"kill worker in non worker process requires specifying an id\");\n        return false;\n    }\n\n    worker_id = worker_id < 0 ? swoole_get_worker_id() : worker_id;\n    const Worker *worker = get_worker(worker_id);\n    if (worker == nullptr) {\n        swoole_error_log(SW_LOG_WARNING, SW_ERROR_INVALID_PARAMS, \"the worker_id[%d] is invalid\", worker_id);\n        return false;\n    }\n\n    swoole_trace_log(SW_TRACE_SERVER, \"kill worker#%d\", worker_id);\n\n    DataHead event = {};\n    event.type = SW_SERVER_EVENT_SHUTDOWN;\n    return send_to_worker_from_worker(worker, &event, sizeof(event), SW_PIPE_MASTER) != -1;\n}\n\nvoid Server::stop_async_worker(Worker *worker) {\n    worker->shutdown();\n    if (worker->type == SW_EVENT_WORKER) {\n        reset_worker_counter(worker);\n    }\n\n    // forced termination\n    Reactor *reactor = sw_reactor();\n    if (reload_async == 0) {\n        reactor->running = false;\n        return;\n    }\n\n    // The worker process is shutting down now.\n    if (reactor->wait_exit) {\n        return;\n    }\n\n    // Separated from the event worker process pool\n    SwooleWG.worker_copy = new Worker{};\n    *SwooleWG.worker_copy = *worker;\n    SwooleWG.worker = worker;\n    auto pipe_worker = get_worker_pipe_worker_in_message_bus(worker);\n\n    if (pipe_worker && !pipe_worker->removed) {\n        reactor->remove_read_event(pipe_worker);\n    }\n\n    if (is_base_mode()) {\n        if (is_event_worker()) {\n            if (worker->id == 0 && get_event_worker_pool()->running == 0) {\n                if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN)) {\n                    swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN, this);\n                }\n                if (onBeforeShutdown) {\n                    onBeforeShutdown(this);\n                }\n            }\n            if (worker->pipe_master && !worker->pipe_master->removed) {\n                reactor->remove_read_event(worker->pipe_master);\n            }\n            for (auto ls : ports) {\n                reactor->del(ls->socket);\n            }\n            foreach_connection([reactor](Connection *conn) {\n                if (!conn->peer_closed && !conn->socket->removed) {\n                    reactor->remove_read_event(conn->socket);\n                }\n            });\n            clear_timer();\n        }\n    } else if (is_process_mode()) {\n        WorkerStopMessage msg;\n        msg.pid = getpid();\n        msg.worker_id = worker->id;\n\n        if (get_event_worker_pool()->push_message(SW_WORKER_MESSAGE_STOP, &msg, sizeof(msg)) < 0) {\n            swoole_sys_warning(\"failed to push WORKER_STOP message\");\n        }\n    } else if (is_thread_mode()) {\n        if (is_event_worker()) {\n            /**\n             * The thread mode will use the master pipe to forward messages,\n             * and it may listen for writable events on this pipe,\n             * which need to be removed before the worker thread exits.\n             */\n            SW_LOOP_N(worker_num) {\n                if (i % reactor_num == reactor->id) {\n                    auto pipe_master = get_worker_pipe_master_in_message_bus(i);\n                    if (!pipe_master->removed) {\n                        reactor->remove_read_event(pipe_master);\n                    }\n                }\n            }\n            /**\n             * Only the readable events are removed;\n             * at this point, there may still be ongoing events for sending data.\n             * The connection will be completely closed only when the reactor is destroyed.\n             */\n            foreach_connection([reactor](Connection *conn) {\n                if (conn->reactor_id == reactor->id && !conn->peer_closed && !conn->socket->removed) {\n                    reactor->remove_read_event(conn->socket);\n                }\n            });\n        }\n    } else {\n        assert(0);\n    }\n\n    reactor->set_wait_exit(true);\n    reactor->set_end_callback(Reactor::PRIORITY_TRY_EXIT, Worker_reactor_try_to_exit);\n    SwooleWG.exit_time = ::time(nullptr);\n\n    Worker_reactor_try_to_exit(reactor);\n}\n\nstatic void Worker_reactor_try_to_exit(Reactor *reactor) {\n    Server *serv;\n    if (sw_likely(swoole_get_worker_type() != SW_TASK_WORKER)) {\n        serv = static_cast<Server *>(reactor->ptr);\n    } else {\n        auto pool = static_cast<ProcessPool *>(reactor->ptr);\n        serv = static_cast<Server *>(pool->ptr);\n    }\n\n    bool has_call_worker_exit_func = false;\n    while (true) {\n        if (reactor->if_exit()) {\n            reactor->running = false;\n        } else {\n            if (serv->onWorkerExit && !has_call_worker_exit_func) {\n                has_call_worker_exit_func = true;\n                serv->onWorkerExit(serv, sw_worker());\n                continue;\n            }\n            int remaining_time = serv->max_wait_time - (::time(nullptr) - SwooleWG.exit_time);\n            if (remaining_time <= 0) {\n                swoole_error_log(\n                    SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, \"worker exit timeout, forced termination\");\n                reactor->running = false;\n            } else {\n                int timeout_msec = remaining_time * 1000;\n                if (reactor->timeout_msec < 0 || reactor->timeout_msec > timeout_msec) {\n                    reactor->timeout_msec = timeout_msec;\n                }\n            }\n        }\n        break;\n    }\n}\n\nvoid Server::drain_worker_pipe() const {\n    for (uint32_t i = 0; i < worker_num + task_worker_num; i++) {\n        Worker *worker = get_worker(i);\n        if (sw_reactor()) {\n            if (worker->pipe_worker) {\n                sw_reactor()->drain_write_buffer(worker->pipe_worker);\n            }\n            if (worker->pipe_master) {\n                sw_reactor()->drain_write_buffer(worker->pipe_master);\n            }\n        }\n    }\n}\n\nvoid Server::clean_worker_connections(Worker *worker) {\n    swoole_trace_log(SW_TRACE_WORKER, \"clean connections\");\n    sw_reactor()->destroyed = true;\n    if (sw_likely(is_base_mode())) {\n        foreach_connection([](Connection *conn) { close_connection(sw_reactor(), conn->socket); });\n    } else if (is_thread_mode()) {\n        foreach_connection([worker](Connection *conn) {\n            if (conn->reactor_id == worker->id) {\n                close_connection(sw_reactor(), conn->socket);\n            }\n        });\n    }\n}\n\n/**\n * main loop [Worker]\n * Only used in SWOOLE_PROCESS mode\n */\nint Server::start_event_worker(Worker *worker) {\n    swoole_set_worker_id(worker->id);\n    swoole_set_worker_type(SW_EVENT_WORKER);\n\n    init_event_worker(worker);\n\n    if (swoole_event_init(0) < 0) {\n        return SW_ERR;\n    }\n\n    worker_signal_init();\n\n    Reactor *reactor = SwooleTG.reactor;\n    /**\n     * set pipe buffer size\n     */\n    for (uint32_t i = 0; i < worker_num + task_worker_num; i++) {\n        const Worker *_worker = get_worker(i);\n        if (_worker->pipe_master) {\n            _worker->pipe_master->buffer_size = UINT_MAX;\n        }\n        if (_worker->pipe_worker) {\n            _worker->pipe_worker->buffer_size = UINT_MAX;\n        }\n    }\n\n    worker->pipe_worker->set_nonblock();\n    reactor->ptr = this;\n    reactor->add(worker->pipe_worker, SW_EVENT_READ);\n    reactor->set_handler(SW_FD_PIPE, SW_EVENT_READ, Worker_onPipeReceive);\n\n    if (dispatch_mode == DISPATCH_CO_CONN_LB || dispatch_mode == DISPATCH_CO_REQ_LB) {\n        reactor->set_end_callback(Reactor::PRIORITY_WORKER_CALLBACK,\n                                  [worker](Reactor *) { worker->coroutine_num = Coroutine::count(); });\n    }\n\n    worker_start_callback(worker);\n\n    // main loop\n    const auto rv = reactor->wait();\n    // drain pipe buffer\n    drain_worker_pipe();\n    // reactor free\n    swoole_event_free();\n    // worker shutdown\n    worker_stop_callback(worker);\n\n    delete buffer_pool;\n\n    return rv;\n}\n\n/**\n * [Worker/TaskWorker/Master] Send data to ReactorThread\n */\nssize_t Server::send_to_reactor_thread(const EventData *ev_data, size_t sendn, SessionId session_id) const {\n    Socket *pipe_sock = get_reactor_pipe_socket(session_id, ev_data->info.reactor_id);\n    if (swoole_event_is_available()) {\n        return swoole_event_write(pipe_sock, ev_data, sendn);\n    } else {\n        return pipe_sock->send_sync(ev_data, sendn);\n    }\n}\n\n/**\n * send message from worker to another worker\n */\nssize_t Server::send_to_worker_from_worker(const Worker *dst_worker, const void *buf, size_t len, int flags) {\n    return dst_worker->send_pipe_message(buf, len, flags);\n}\n\n/**\n * receive data from reactor\n * This function is intended solely for process mode; in thread or base mode, `ReactorThread_onRead()` will be executed.\n */\nstatic int Worker_onPipeReceive(Reactor *reactor, Event *event) {\n    auto *serv = static_cast<Server *>(reactor->ptr);\n    auto *pipe_buffer = serv->get_worker_message_bus()->get_buffer();\n\n    if (serv->get_worker_message_bus()->read(event->socket) <= 0) {\n        return SW_OK;\n    }\n\n    serv->worker_accept_event(&pipe_buffer->info);\n    serv->get_worker_message_bus()->pop();\n\n    return SW_OK;\n}\n}  // namespace swoole\n"
  },
  {
    "path": "src/wrapper/event.cc",
    "content": "/**\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_reactor.h\"\n#include \"swoole_client.h\"\n#include \"swoole_coroutine_socket.h\"\n#include \"swoole_coroutine_system.h\"\n\nusing swoole::Callback;\nusing swoole::Reactor;\nusing swoole::ReactorHandler;\nusing swoole::network::Socket;\n\nint swoole_event_init(int flags) {\n    auto *reactor = new Reactor(SW_REACTOR_MAXEVENTS);\n    if (!reactor->ready()) {\n        return SW_ERR;\n    }\n\n    if (flags & SW_EVENTLOOP_WAIT_EXIT) {\n        reactor->wait_exit = true;\n    }\n\n    swoole::coroutine::Socket::init_reactor(reactor);\n    swoole::coroutine::System::init_reactor(reactor);\n    swoole::network::Client::init_reactor(reactor);\n\n    SwooleTG.reactor = reactor;\n\n    return SW_OK;\n}\n\nint swoole_event_add(Socket *socket, int events) {\n    return SwooleTG.reactor->add(socket, events);\n}\n\nint swoole_event_add_or_update(Socket *_socket, int event) {\n    if (event == SW_EVENT_READ) {\n        return SwooleTG.reactor->add_read_event(_socket);\n    } else if (event == SW_EVENT_WRITE) {\n        return SwooleTG.reactor->add_write_event(_socket);\n    } else {\n        assert(0);\n        return SW_ERR;\n    }\n}\n\nint swoole_event_set(Socket *socket, int events) {\n    return SwooleTG.reactor->set(socket, events);\n}\n\nint swoole_event_del(Socket *socket) {\n    return SwooleTG.reactor->del(socket);\n}\n\nint swoole_event_wait() {\n    Reactor *reactor = SwooleTG.reactor;\n    int retval = 0;\n    if (!reactor->wait_exit or !reactor->if_exit()) {\n        retval = reactor->wait();\n    }\n    swoole_event_free();\n    return retval;\n}\n\nint swoole_event_free() {\n    if (!SwooleTG.reactor) {\n        return SW_ERR;\n    }\n    delete SwooleTG.reactor;\n    SwooleTG.reactor = nullptr;\n    return SW_OK;\n}\n\nvoid swoole_event_defer(const Callback &cb, void *private_data) {\n    SwooleTG.reactor->defer(cb, private_data);\n}\n\nssize_t swoole_event_write(Socket *socket, const void *data, size_t len) {\n    return SwooleTG.reactor->write(SwooleTG.reactor, socket, data, len);\n}\n\nssize_t swoole_event_writev(Socket *socket, const iovec *iov, size_t iovcnt) {\n    return SwooleTG.reactor->writev(SwooleTG.reactor, socket, iov, iovcnt);\n}\n\nvoid swoole_event_set_handler(const int fd_type, const int event, const ReactorHandler handler) {\n    SwooleTG.reactor->set_handler(fd_type, event, handler);\n}\n\nbool swoole_event_isset_handler(const int fd_type, const int event) {\n    return SwooleTG.reactor->isset_handler(fd_type, event);\n}\n\nbool swoole_event_is_available() {\n    return SwooleTG.reactor && !SwooleTG.reactor->destroyed;\n}\n\nbool swoole_event_is_running() {\n    return SwooleTG.reactor && SwooleTG.reactor->running;\n}\n\nSocket *swoole_event_get_socket(int fd) {\n    return SwooleTG.reactor->get_socket(fd);\n}\n"
  },
  {
    "path": "src/wrapper/http.cc",
    "content": "/**\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_http.h\"\n#include \"swoole_server.h\"\n\n#include \"swoole_llhttp.h\"\n#include \"thirdparty/multipart_parser.h\"\n\nnamespace swoole {\nnamespace http_server {\n\nstatic int http_request_on_url(llhttp_t *parser, const char *at, size_t length);\nstatic int http_request_on_body(llhttp_t *parser, const char *at, size_t length);\nstatic int http_request_on_header_field(llhttp_t *parser, const char *at, size_t length);\nstatic int http_request_on_header_value(llhttp_t *parser, const char *at, size_t length);\nstatic int http_request_on_headers_complete(llhttp_t *parser);\nstatic int http_request_message_complete(llhttp_t *parser);\n\nstatic int multipart_body_on_header_field(multipart_parser *p, const char *at, size_t length);\nstatic int multipart_body_on_header_value(multipart_parser *p, const char *at, size_t length);\nstatic int multipart_body_on_data(multipart_parser *p, const char *at, size_t length);\nstatic int multipart_body_on_header_complete(multipart_parser *p);\nstatic int multipart_body_on_data_end(multipart_parser *p);\n\n// clang-format off\nstatic constexpr llhttp_settings_t http_parser_settings =\n{\n    nullptr,                                // on_message_begin\n    nullptr,                                // on_protocol\n    http_request_on_url,                    // on_url\n    nullptr,                                // on_status\n    nullptr,                                // on_method\n    nullptr,                                // on_version\n    http_request_on_header_field,           // on_header_field\n    http_request_on_header_value,           // on_header_value\n    nullptr,                                // on_chunk_extension_name\n    nullptr,                                // on_chunk_extension_value\n    http_request_on_headers_complete,       // on_headers_complete\n    http_request_on_body,                   // on_body\n    http_request_message_complete,          // on_message_complete\n    nullptr,                                // on_protocol_complete\n    nullptr,                                // on_url_complete\n    nullptr,                                // on_status_complete\n    nullptr,                                // on_method_complete\n    nullptr,                                // on_version_complete\n    nullptr,                                // on_header_field_complete\n    nullptr,                                // on_header_value_complete\n    nullptr,                                // on_chunk_extension_name_complete\n    nullptr,                                // on_chunk_extension_value_complete\n    nullptr,                                // on_chunk_header\n    nullptr,                                // on_chunk_complete\n    nullptr,                                // on_reset\n};\n\nstatic  constexpr multipart_parser_settings mt_parser_settings = {\n    multipart_body_on_header_field,\n    multipart_body_on_header_value,\n    multipart_body_on_data,\n    nullptr,\n    multipart_body_on_header_complete,\n    multipart_body_on_data_end,\n    nullptr,\n};\n// clang-format on\n\nstruct ContextImpl {\n    llhttp_t parser;\n    multipart_parser *mt_parser;\n\n    std::string current_header_name;\n    std::string current_input_name;\n    std::string current_form_data_name;\n    String *form_data_buffer;\n\n    bool completed = false;\n    bool is_beginning = true;\n\n    bool parse(Context &ctx, const char *at, size_t length) {\n        swoole_llhttp_parser_init(&parser, HTTP_REQUEST, static_cast<void *>(&ctx));\n        swoole_llhttp_parser_execute(&parser, &http_parser_settings, at, length);\n        return parser.error == HPE_OK && completed;\n    }\n};\n\nstatic int http_request_on_url(llhttp_t *parser, const char *at, size_t length) {\n    const char *query_start = (const char *) memchr(at, '?', length);\n    size_t path_len = query_start ? (size_t) (query_start - at) : length;\n\n    auto *ctx = static_cast<Context *>(parser->data);\n    ctx->request_path = std::string(at, path_len);\n\n    if (!query_start || (length - path_len) <= 1) {\n        return 0;\n    }\n\n    const char *query_str = query_start + 1;\n    size_t query_len = length - path_len - 1;\n    ctx->query_string = std::string(query_str, query_len);\n    return 0;\n}\n\nstatic int http_request_on_header_field(llhttp_t *parser, const char *at, size_t length) {\n    auto *ctx = static_cast<Context *>(parser->data);\n    ctx->impl->current_header_name = std::string(at, length);\n    return 0;\n}\n\nstatic int http_request_on_header_value(llhttp_t *parser, const char *at, size_t length) {\n    auto *ctx = static_cast<Context *>(parser->data);\n    ContextImpl *impl = ctx->impl;\n    ctx->headers[impl->current_header_name] = std::string(at, length);\n\n    if ((parser->method == HTTP_POST || parser->method == HTTP_PUT || parser->method == HTTP_DELETE ||\n         parser->method == HTTP_PATCH) &&\n        SW_STRCASEEQ(impl->current_header_name.c_str(), impl->current_header_name.length(), \"content-type\")) {\n        if (SW_STR_ISTARTS_WITH(at, length, \"application/x-www-form-urlencoded\")) {\n            ctx->post_form_urlencoded = true;\n        } else if (SW_STR_ISTARTS_WITH(at, length, \"multipart/form-data\")) {\n            size_t offset = sizeof(\"multipart/form-data\") - 1;\n            char *boundary_str;\n            int boundary_len;\n            if (!parse_multipart_boundary(at, length, offset, &boundary_str, &boundary_len)) {\n                return -1;\n            }\n            impl->mt_parser = multipart_parser_init(boundary_str, boundary_len, &mt_parser_settings);\n            impl->form_data_buffer = new String(SW_BUFFER_SIZE_STD);\n            impl->mt_parser->data = ctx;\n            swoole_trace_log(SW_TRACE_HTTP, \"form_data, boundary_str=%s\", boundary_str);\n        }\n    }\n    return 0;\n}\n\nstatic int http_request_on_headers_complete(llhttp_t *parser) {\n    auto *ctx = static_cast<Context *>(parser->data);\n    ctx->version = parser->http_major * 100 + parser->http_minor;\n    ctx->server_protocol = std::string(ctx->version == 101 ? \"HTTP/1.1\" : \"HTTP/1.0\");\n    ctx->keepalive = llhttp_should_keep_alive(parser);\n    return 0;\n}\n\nstatic int http_request_on_body(llhttp_t *parser, const char *at, size_t length) {\n    if (length == 0) {\n        return 0;\n    }\n\n    auto *ctx = static_cast<Context *>(parser->data);\n    auto *impl = ctx->impl;\n\n    if (impl->mt_parser != nullptr) {\n        multipart_parser *multipart_parser = impl->mt_parser;\n        if (impl->is_beginning) {\n            /* Compatibility: some clients may send extra EOL */\n            do {\n                if (*at != '\\r' && *at != '\\n') {\n                    break;\n                }\n                at++;\n                length--;\n            } while (length != 0);\n            impl->is_beginning = false;\n        }\n        size_t n = multipart_parser_execute(multipart_parser, at, length);\n        if (sw_unlikely(n != length)) {\n            swoole_error_log(SW_LOG_WARNING,\n                             SW_ERROR_SERVER_INVALID_REQUEST,\n                             \"parse multipart body failed, %zu/%zu bytes processed\",\n                             n,\n                             length);\n        }\n    } else {\n        ctx->body.append(at, length);\n    }\n\n    return impl->completed ? HPE_PAUSED : 0;\n}\n\nstatic int multipart_body_on_header_field(multipart_parser *p, const char *at, size_t length) {\n    auto *ctx = static_cast<Context *>(p->data);\n    ContextImpl *impl = ctx->impl;\n    return http_request_on_header_field(&impl->parser, at, length);\n}\n\nstatic int multipart_body_on_header_value(multipart_parser *p, const char *at, size_t length) {\n    auto *ctx = static_cast<Context *>(p->data);\n    ContextImpl *impl = ctx->impl;\n    const char *header_name = impl->current_header_name.c_str();\n    size_t header_len = impl->current_header_name.length();\n\n    if (SW_STRCASEEQ(header_name, header_len, \"content-disposition\")) {\n        std::unordered_map<std::string, std::string> info;\n        ParseCookieCallback cb = [&info](char *key, size_t key_len, char *value, size_t value_len) {\n            info[std::string(key, key_len)] = std::string(value, value_len);\n            return true;\n        };\n        parse_cookie(at, length, cb);\n        auto name = info.find(\"name\");\n        auto filename = info.find(\"filename\");\n        if (filename == info.end()) {\n            impl->current_form_data_name = name->second;\n        } else {\n            impl->current_input_name = filename->second;\n        }\n    } else if (SW_STRCASEEQ(header_name, header_len, SW_HTTP_UPLOAD_FILE)) {\n        /**\n         * When the \"SW_HTTP_UPLOAD_FILE\" header appears in the request, it indicates that the uploaded file has been\n         * saved in a temporary file. The binary content in the message body will be replaced with the temporary\n         * filename. However, the Content-Length still reflects the original message size, causing llhttp to believe\n         * there is still data to be received. As a result, llhttp fails to trigger the message callback. Therefore, we\n         * need to set `ctx->completed = 1` to indicate that the message processing is complete.\n         */\n        impl->completed = true;\n        ctx->files[impl->current_form_data_name] = std::string(at, length);\n    }\n\n    return 0;\n}\n\nstatic int multipart_body_on_data(multipart_parser *p, const char *at, size_t length) {\n    auto *ctx = static_cast<Context *>(p->data);\n    const auto *impl = ctx->impl;\n    if (!impl->current_form_data_name.empty()) {\n        impl->form_data_buffer->append(at, length);\n        return 0;\n    }\n    if (p->fp == nullptr) {\n        return 0;\n    }\n    ssize_t n = fwrite(at, sizeof(char), length, p->fp);\n    if (n != (off_t) length) {\n        ctx->files[impl->current_form_data_name] = \"ERROR(1)\";\n        fclose(p->fp);\n        p->fp = nullptr;\n        swoole_sys_warning(\"write upload file failed\");\n    }\n    return 0;\n}\n\nstatic int multipart_body_on_header_complete(multipart_parser *p) {\n    auto *ctx = static_cast<Context *>(p->data);\n    const auto *impl = ctx->impl;\n    if (impl->current_input_name.empty()) {\n        return 0;\n    }\n\n    if (ctx->files.find(impl->current_form_data_name) != ctx->files.end()) {\n        return 0;\n    }\n\n    char file_path[SW_HTTP_UPLOAD_TMPDIR_SIZE] = \"/tmp/swoole.upfile.XXXXXX\";\n    int tmpfile = swoole_tmpfile(file_path);\n    if (tmpfile < 0) {\n        return 0;\n    }\n\n    FILE *fp = fdopen(tmpfile, \"wb+\");\n    if (fp == nullptr) {\n        swoole_sys_warning(\"fopen(%s) failed\", file_path);\n        return 0;\n    }\n    p->fp = fp;\n    ctx->files[impl->current_form_data_name] = file_path;\n\n    return 0;\n}\n\nstatic int multipart_body_on_data_end(multipart_parser *p) {\n    auto *ctx = static_cast<Context *>(p->data);\n    ContextImpl *impl = ctx->impl;\n\n    if (!impl->current_form_data_name.empty()) {\n        ctx->form_data[impl->current_form_data_name] = impl->form_data_buffer->to_std_string();\n        impl->form_data_buffer->clear();\n    }\n\n    if (p->fp != nullptr) {\n        fclose(p->fp);\n        p->fp = nullptr;\n    }\n\n    impl->current_header_name.clear();\n    impl->current_input_name.clear();\n    impl->current_form_data_name.clear();\n\n    return 0;\n}\n\nstatic int http_request_message_complete(llhttp_t *p) {\n    const auto *ctx = static_cast<Context *>(p->data);\n    auto *impl = ctx->impl;\n\n    if (impl->form_data_buffer) {\n        delete impl->form_data_buffer;\n        impl->form_data_buffer = nullptr;\n    }\n\n    impl->completed = true;\n    return HPE_PAUSED;\n}\n\nbool Context::end(const char *data, size_t length) {\n    char buf[1024];\n    sw_tg_buffer()->clear();\n    sw_tg_buffer()->append(SW_STRL(\"HTTP/1.1 \"));\n    sw_tg_buffer()->append(get_status_message(response.code));\n    sw_tg_buffer()->append(SW_STRL(\"\\r\\n\"));\n    if (length > 0) {\n        response.headers[\"Content-Length\"] = std::to_string(length);\n    }\n    for (auto &iter : response.headers) {\n        size_t n = sw_snprintf(buf, sizeof(buf), \"%s: %s\\r\\n\", iter.first.c_str(), iter.second.c_str());\n        sw_tg_buffer()->append(buf, n);\n    }\n    sw_tg_buffer()->append(SW_STRL(\"\\r\\n\"));\n    if (!server_->send(session_id_, sw_tg_buffer()->str, sw_tg_buffer()->length)) {\n        swoole_warning(\"failed to send HTTP header\");\n        return false;\n    }\n    if (length > 0 && !server_->send(session_id_, data, length)) {\n        swoole_warning(\"failed to send HTTP body\");\n        return false;\n    }\n    if (!keepalive) {\n        server_->close(session_id_, false);\n    }\n    return true;\n}\n\nContext::~Context() {\n    for (auto &kv : files) {\n        if (file_exists(kv.second)) {\n            unlink(kv.second.c_str());\n        }\n    }\n}\n\nstatic std::function<void(Context &ctx)> http_server_on_request;\n\nstd::shared_ptr<Server> listen(const std::string &addr, const std::function<void(Context &ctx)> &cb, int mode) {\n    auto server = std::make_shared<Server>(static_cast<Server::Mode>(mode));\n    auto index = addr.find(':');\n    if (index == std::string::npos) {\n        swoole_warning(\"incorrect server listening address\");\n        return nullptr;\n    }\n\n    std::string host = addr.substr(0, index);\n    if (host.empty()) {\n        host = \"0.0.0.0\";\n    }\n\n    int port = sw_atoi(addr.substr(index + 1).c_str());\n    auto port_object = server->add_port(SW_SOCK_TCP, host.c_str(), port);\n    if (!port_object) {\n        return nullptr;\n    }\n\n    http_server_on_request = cb;\n\n    server->onReceive = [](Server *server, RecvData *req) {\n        SessionId session_id = req->info.fd;\n        Connection *conn = server->get_connection_verify_no_ssl(session_id);\n        if (!conn) {\n            swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, \"session[%ld] is closed\", session_id);\n            return SW_OK;\n        }\n        ContextImpl impl;\n        Context ctx(server, session_id, &impl);\n        if (impl.parse(ctx, req->data, req->info.len)) {\n            http_server_on_request(ctx);\n        } else {\n            server->send(req->session_id(), SW_STRL(SW_HTTP_BAD_REQUEST_PACKET));\n        }\n        return SW_OK;\n    };\n\n    port_object->open_http_protocol = true;\n\n    if (server->create() == SW_ERR) {\n        return nullptr;\n    }\n\n    return server;\n}\n}  // namespace http_server\n}  // namespace swoole\n"
  },
  {
    "path": "src/wrapper/timer.cc",
    "content": "/**\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"swoole_timer.h\"\n\nusing swoole::sec2msec;\nusing swoole::Timer;\nusing swoole::TimerCallback;\nusing swoole::TimerNode;\n\nbool swoole_timer_is_available() {\n    return SwooleTG.timer != nullptr;\n}\n\nTimerNode *swoole_timer_add(double timeout, bool persistent, const TimerCallback &callback, void *private_data) {\n    if (sw_unlikely(timeout < SW_TIMER_MIN_SEC)) {\n        timeout = SW_TIMER_MIN_SEC;\n    }\n    return swoole_timer_add(sec2msec(timeout), persistent, callback, private_data);\n}\n\nTimer *swoole_timer_create(bool manually_trigger) {\n    SwooleTG.timer = new Timer(manually_trigger);\n    return SwooleTG.timer;\n}\n\nSW_API int64_t swoole_timer_get_next_msec() {\n    if (sw_unlikely(!swoole_timer_is_available())) {\n        return -1;\n    }\n    return SwooleTG.timer->get_next_msec();\n}\n\nTimerNode *swoole_timer_add(long ms, bool persistent, const TimerCallback &callback, void *private_data) {\n    if (sw_unlikely(!swoole_timer_is_available())) {\n        swoole_timer_create(false);\n    }\n    return SwooleTG.timer->add(ms, persistent, private_data, callback);\n}\n\nbool swoole_timer_del(TimerNode *tnode) {\n    if (sw_unlikely(!swoole_timer_is_available())) {\n        swoole_warning(\"timer is not available\");\n        return false;\n    }\n    return SwooleTG.timer->remove(tnode);\n}\n\nvoid swoole_timer_delay(TimerNode *tnode, long delay_ms) {\n    if (sw_unlikely(!swoole_timer_is_available())) {\n        swoole_warning(\"timer is not available\");\n        return;\n    }\n    return SwooleTG.timer->delay(tnode, delay_ms);\n}\n\nlong swoole_timer_after(long ms, const TimerCallback &callback, void *private_data) {\n    if (ms <= 0) {\n        swoole_warning(\"Timer must be greater than 0\");\n        return SW_ERR;\n    }\n    const auto tnode = swoole_timer_add(ms, false, callback, private_data);\n    if (sw_unlikely(!tnode)) {\n        return SW_ERR;\n    }\n    return tnode->id;\n}\n\nlong swoole_timer_tick(long ms, const TimerCallback &callback, void *private_data) {\n    if (sw_unlikely(ms <= 0)) {\n        swoole_warning(\"Timer must be greater than 0\");\n        return SW_ERR;\n    }\n    const auto tnode = swoole_timer_add(ms, true, callback, private_data);\n    if (sw_unlikely(!tnode)) {\n        return SW_ERR;\n    }\n    return tnode->id;\n}\n\nbool swoole_timer_exists(long timer_id) {\n    if (sw_unlikely(!swoole_timer_is_available())) {\n        swoole_warning(\"timer is not available\");\n        return false;\n    }\n    TimerNode *tnode = SwooleTG.timer->get(timer_id);\n    return (tnode && !tnode->removed);\n}\n\nbool swoole_timer_clear(long timer_id) {\n    if (sw_unlikely(!swoole_timer_is_available())) {\n        swoole_warning(\"timer is not available\");\n        return false;\n    }\n    return SwooleTG.timer->remove(SwooleTG.timer->get(timer_id));\n}\n\nTimerNode *swoole_timer_get(long timer_id) {\n    if (sw_unlikely(!swoole_timer_is_available())) {\n        swoole_warning(\"timer is not available\");\n        return nullptr;\n    }\n    return SwooleTG.timer->get(timer_id);\n}\n\nvoid swoole_timer_free() {\n    if (!swoole_timer_is_available()) {\n        swoole_print_backtrace();\n        swoole_warning(\"timer is not available\");\n        return;\n    }\n    delete SwooleTG.timer;\n    SwooleTG.timer = nullptr;\n}\n\nvoid swoole_timer_select() {\n    if (sw_likely(swoole_timer_is_available())) {\n        SwooleTG.timer->select();\n    }\n}\n"
  },
  {
    "path": "tests/CONTRIBUTION",
    "content": "Tianfeng Han @ Swoole Group <https://github.com/swoole>\nTwosee @ Swoole Group <https://github.com/swoole>\nXiaofeng Chu @ Zan Group <https://github.com/youzan>\n"
  },
  {
    "path": "tests/README.md",
    "content": "# PHP Unit-test\n\nRun these tests to make certain that the swoole extension you installed can work well.\n\n## Preparation\ntry to run `./init` to initialize the databases.\n\n|               | mysql                                 | redis                               |\n| ------------- | ------------------------------------- | ----------------------------------- |\n| path (env)    | $MYSQL_SERVER_PATH                    | $REDIS_SERVER_PATH                  |\n| path (actions) | ${actions}/data/run/mysqld/mysqld.sock | ${actions}/data/run/redis/redis.sock |\n| host (raw)    | 127.0.0.1                             | 127.0.0.1                           |\n| host (docker) | mysql                                 | redis                               |\n| port          | 3306                                  | 6379                                |\n| user          | root                                  | -                                   |\n| password      | root                                  | root (optional)                     |\n| database      | test                                  | 0                                   |\n\n## How to run\n1. just run `./start.sh`\n2. or use`./start.sh ./swoole_*` command to run a part of the tests\n3. or use `./start.sh base` command to run base tests\n\n## Defaults\n\n| Config    | Enable   |\n| --------- | -------- |\n| show-diff | yes      |\n| show-mem  | yes      |\n| show-slow | 1000(ms) |\n\n## Log files\n\n| suffix | intro                                         |\n| ------ | --------------------------------------------- |\n| diff   | show the differents between output and expect |\n| out    | script output                                 |\n| exp    | expect output                                 |\n| log    | all above                                     |\n| php    | php temp script file                          |\n\n## Clean\nRun `./clean` to remove all of the tests log files.\n\n## Contribute the test script\nRun `./new [test-script-filename]`\n\nE.g. : `./new ./swoole_coroutine/co_sleep.phpt`\n\nIt will generate the test script file and auto open on your ide (MacOS only).\n\n![](https://cdn.jsdelivr.net/gh/sy-records/staticfile/images/swoole/generate-example.gif)\n\n## Code Style\n`PSR1/PSR2`\n"
  },
  {
    "path": "tests/clean",
    "content": "#!/usr/bin/env php\n<?php\nfunction clean($dir)\n{\n    $files = scandir($dir);\n    foreach ($files as $file) {\n        $full_path = \"{$dir}/{$file}\";\n        if ($file[0]=== '.') {\n            continue;\n        }\n        if (is_dir($full_path)) {\n            clean($full_path);\n            continue;\n        }\n        $extension = pathinfo($file, PATHINFO_EXTENSION);\n        if (in_array($extension, ['diff', 'exp', 'log', 'out', 'php', 'sh'], true)) {\n            if (is_file($full_path)) {\n                echo \"DELETE: {$full_path}\\n\";\n                unlink($full_path);\n            }\n        }\n    }\n}\n\n$dirs = scandir(__DIR__);\nforeach ($dirs as $dir) {\n    if (strpos($dir, 'swoole_') === 0) {\n        clean(__DIR__ . '/' . $dir);\n    }\n}\n"
  },
  {
    "path": "tests/include/api/curl_multi.php",
    "content": "<?php\nfunction swoole_test_curl_multi($options = []) {\n    $mh = curl_multi_init();\n    swoole_test_curl_multi_ex($mh, $options);\n    curl_multi_close($mh);\n}\n\nfunction swoole_test_curl_multi_ex($mh, $options = []) {\n    $ch1 = curl_init();\n    $ch2 = curl_init();\n\n    // 设置URL和相应的选项\n    curl_setopt($ch1, CURLOPT_URL, \"https://www.baidu.com/\");\n    curl_setopt($ch1, CURLOPT_HEADER, 0);\n    curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1);\n\n    curl_setopt($ch2, CURLOPT_URL, \"https://www.gov.cn/\");\n    curl_setopt($ch2, CURLOPT_HEADER, 0);\n    curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1);\n\n    $mh = curl_multi_init();\n\n    curl_multi_add_handle($mh, $ch1);\n    curl_multi_add_handle($mh, $ch2);\n\n    $active = null;\n    // 执行批处理句柄\n    do {\n        $mrc = curl_multi_exec($mh, $active);\n    } while ($mrc == CURLM_CALL_MULTI_PERFORM);\n\n    if (isset($options['select_twice'])) {\n        if (isset($options['sleep'])) {\n            unset($options['sleep']);\n        }\n        go(function() use($mh) {\n            Co::sleep(0.005);\n            curl_multi_select($mh);\n        });\n    }\n\n    if (isset($options['sleep'])) {\n        Co::sleep($options['sleep']);\n    }\n\n    while ($active && $mrc == CURLM_OK) {\n        $n = curl_multi_select($mh);\n        if ($n != -1) {\n            do {\n                $mrc = curl_multi_exec($mh, $active);\n            } while ($mrc == CURLM_CALL_MULTI_PERFORM);\n        }\n    }\n\n    $info1 = curl_multi_info_read($mh);\n    $info2 = curl_multi_info_read($mh);\n    $info3 = curl_multi_info_read($mh);\n\n    Assert::eq($info1['msg'], CURLMSG_DONE);\n    Assert::eq($info2['msg'], CURLMSG_DONE);\n    Assert::eq($info3, false);\n\n    Assert::contains(curl_multi_getcontent($ch1), 'baidu.com');\n    Assert::contains(curl_multi_getcontent($ch2), '中国政府网');\n\n    curl_multi_remove_handle($mh, $ch1);\n    curl_multi_remove_handle($mh, $ch2);\n\n    curl_multi_close($mh);\n}\n"
  },
  {
    "path": "tests/include/api/exit.php",
    "content": "<?php\ndeclare(strict_types=1);\nif (PHP_VERSION_ID>= 80400) {\n    $exit_status_list = [\n        1,\n        'exit',\n        0,\n    ];\n} else {\n    $exit_status_list = [\n        'undef',\n        null,\n        true,\n        false,\n        1,\n        1.1,\n        'exit',\n        ['exit' => 'ok'],\n        (object)['exit' => 'ok'],\n        STDIN,\n        0\n    ];\n}\n\nfunction route()\n{\n    controller();\n}\n\nfunction controller()\n{\n    your_code();\n}\n\nfunction your_code()\n{\n    global $exit_status_list;\n    co::sleep(.001);\n    $exit_status = array_shift($exit_status_list);\n    if ($exit_status === 'undef') {\n        exit;\n    } else {\n        exit($exit_status);\n    }\n}\n\n$chan = new Swoole\\Coroutine\\Channel;\n\ngo(function () use ($chan, $exit_status_list) {\n    foreach ($exit_status_list as $val) {\n        $chan->push($val);\n    }\n});\n\nfor ($i = 0; $i < count($exit_status_list); $i++) {\n    go(function () use ($exit_status_list, $chan) {\n        try {\n            // in coroutine\n            route();\n        } catch (\\Swoole\\ExitException $e) {\n            Assert::assert($e->getFlags() & SWOOLE_EXIT_IN_COROUTINE);\n            $exit_status = $chan->pop();\n            if (PHP_VERSION_ID < 80400) {\n                $exit_status = $exit_status === 'undef' ? null : $exit_status;\n            }\n            Assert::same($e->getStatus(), $exit_status);\n            var_dump($e->getStatus());\n            // exit coroutine\n            return;\n        }\n        echo \"never here\\n\";\n    });\n}\n\nSwoole\\Event::wait();\n"
  },
  {
    "path": "tests/include/api/http_server.php",
    "content": "<?php\n/**\n * @var $pm \\ProcessManager\n */\nglobal $pm;\n\n$http = new Swoole\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_BASE);\n$http->set(array(\n    'log_file' => '/dev/null',\n    \"http_parse_post\" => 1,\n    \"upload_tmp_dir\" => \"/tmp\",\n));\n$http->on(\"WorkerStart\", function (Swoole\\Server $serv) {\n    global $pm;\n    if ($pm) {\n        $pm->wakeup();\n    }\n});\n$http->on('request', function ($request, Swoole\\Http\\Response $response) use ($pm) {\n    $route = $request->server['request_uri'];\n    if ($route == '/info') {\n        $response->end($request->header['user-agent']);\n        return;\n    } elseif ($route == '/cookies') {\n        $response->end(@json_encode($request->cookie));\n        return;\n    } elseif ($route == '/get') {\n        $response->end(@json_encode($request->get));\n        return;\n    } elseif ($route == '/post') {\n        $response->end(@json_encode($request->post));\n        return;\n    } elseif ($route == '/get_file') {\n        $response->sendfile(TEST_IMAGE);\n        return;\n    } elseif ($route == '/upload_file') {\n        $response->end(json_encode([\n            'files' => $request->files,\n            'md5' => md5_file($request->files['test_jpg']['tmp_name']),\n            'post' => $request->post\n        ]));\n        return;\n    } elseif ($route == '/gzip') {\n        $response->gzip(5);\n        $content = co::readFile(__DIR__ . '/../../../README.md');\n        $response->end($content);\n        return;\n    } else {\n        return;\n    }\n});\n$http->start();\n"
  },
  {
    "path": "tests/include/api/http_test_cases.php",
    "content": "<?php\n\nuse Swoole\\Coroutine\\Socket;\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Coroutine\\Http\\Client as HttpClient;\nuse Swoole\\Coroutine\\Http2\\Client as Http2Client;\nuse Swoole\\Http2\\Request as Http2Request;\nuse function Swoole\\Coroutine\\run;\n\nfunction http_compression_types_test(ProcessManager $pm)\n{\n    run(function () use ($pm) {\n        $cli = new HttpClient('127.0.0.1', $pm->getFreePort());\n        $cli->setHeaders(['Accept-Encoding' => 'gzip',]);\n        $cli->get('/html?bytes=' . rand(8192, 65536));\n        Assert::eq($cli->getHeaders()['content-encoding'], 'gzip');\n\n        $cli = new HttpClient('127.0.0.1', $pm->getFreePort());\n        $cli->setHeaders(['Accept-Encoding' => 'gzip',]);\n        $cli->get('/json?bytes=' . rand(8192, 65536));\n        Assert::eq($cli->getHeaders()['content-encoding'], 'gzip');\n\n        $cli = new HttpClient('127.0.0.1', $pm->getFreePort());\n        $cli->setHeaders(['Accept-Encoding' => 'gzip',]);\n        $cli->get('/raw?bytes=' . rand(8192, 65536));\n        Assert::assert(!isset($cli->getHeaders()['content-encoding']));\n    });\n}\n\nfunction http2_compression_types_test(ProcessManager $pm)\n{\n    run(function () use ($pm) {\n        $cli = new Http2Client('127.0.0.1', $pm->getFreePort());\n        Assert::true($cli->connect());\n\n        $req1 = new Http2Request;\n        $req1->path = '/html?bytes=' . rand(8192, 65536);\n        $req1->headers = ['Accept-Encoding' => 'gzip',];\n        $stream1 = $cli->send($req1);\n\n        $req2 = new Http2Request;\n        $req2->path = '/json?bytes=' . rand(8192, 65536);\n        $req2->headers = ['Accept-Encoding' => 'gzip',];\n        $stream2 = $cli->send($req2);\n\n        $req3 = new Http2Request;\n        $req3->path = '/raw?bytes=' . rand(8192, 65536);\n        $req3->headers = ['Accept-Encoding' => 'gzip',];\n        $stream3 = $cli->send($req3);\n\n        $n = 3;\n        $list = [];\n        while($n--) {\n            $resp = $cli->recv();\n            $list[$resp->streamId] = $resp;\n        }\n\n        Assert::eq($list[$stream1]->headers['content-encoding'], 'gzip');\n        Assert::eq($list[$stream2]->headers['content-encoding'], 'gzip');\n        Assert::assert(!isset($list[$stream3]->headers['content-encoding']));\n    });\n}\n\n/**\n * @param ProcessManager $pm\n * @param array $sizes\n * @throw RuntimeException\n */\nfunction form_data_test(ProcessManager $pm, array $sizes = [])\n{\n    if (count($sizes) == 0) {\n        throw new \\RuntimeException(\"size array cannot be empty\");\n    }\n    run(function () use ($pm, $sizes) {\n        $client = new Client(SWOOLE_SOCK_TCP);\n        Assert::true($client->connect('127.0.0.1', $pm->getFreePort()));\n        $req = file_get_contents(SOURCE_ROOT_PATH . '/core-tests/fuzz/cases/req1.bin');\n        $len = strlen($req);\n\n        $begin = 0;\n        foreach ($sizes as $end) {\n            if ($end >= $len) {\n                throw new \\RuntimeException(\"error offset[$end]\");\n            }\n            Assert::eq($client->send(substr($req, $begin, $end)), $end - $begin);\n            usleep(10000);\n        }\n\n        $end = $sizes[count($sizes) - 1];\n        if ($len - $end > 0) {\n            Assert::eq($client->send(substr($req, $end)), $len - $end);\n            usleep(10000);\n        }\n\n        $resp = '';\n        $length = 0;\n        $header = '';\n\n        while (true) {\n            $data = $client->recv();\n            if ($data == false) {\n                throw new RuntimeException(\"recv failed, error: \" . $client->errMsg . \", resp: \" . $resp);\n            }\n            $resp .= $data;\n            if ($length == 0) {\n                $crlf_pos = strpos($resp, \"\\r\\n\\r\\n\");\n                if ($crlf_pos === false) {\n                    continue;\n                }\n                $header = substr($resp, 0, $crlf_pos);\n                if (!preg_match('#Content-Length:\\s(\\d+)#i', $header, $match)) {\n                    throw new RuntimeException(\"no match Content-Length\");\n                }\n                $length = strlen($header) + 4 + $match[1];\n            }\n            if (strlen($resp) == $length) {\n                break;\n            }\n        }\n        Assert::assert($header);\n        $body = substr($resp, strlen($header) + 4);\n        if (!$body) {\n            var_dump($header);\n        }\n        Assert::assert($body);\n        $data = json_decode($body);\n        Assert::assert(is_object($data));\n        Assert::minLength($data->test, 80);\n        Assert::minLength($data->hello, 120);\n        Assert::minLength($data->world, 1024);\n        $client->close();\n    });\n    $pm->kill();\n    echo \"DONE\\n\";\n}\n\nfunction getHttpBody(string $s): string\n{\n    return str_replace(EOF, '', explode(\"\\r\\n\\r\\n\", $s)[1] ?? '');\n}\n\nfunction generateChunkBody(array $a): string\n{\n    $s = '';\n    foreach ($a as $c) {\n        $s .= dechex(strlen($c)) . \"\\r\\n\" . $c . \"\\r\\n\";\n    }\n    return $s . \"0\\r\\n\";\n}\n\nfunction chunked_request(ProcessManager $pm)\n{\n    run(function () use ($pm) {\n        $request_empty_chunked =\n            \"DELETE /locks?password=9c1858261b4337b49af4fb8c57a9ec98&lock_id=1&amount=1.2&c=6331b32ac32f4c128ce0016114e11dbd&lang=zh_CN HTTP/1.1\\r\\n\" .\n            \"x-real-ip: 10.2.100.1\\r\\n\" .\n            \"x-forwarded-server: kitchen.pool-x.net\\r\\n\" .\n            \"accept: application/json\\r\\n\" .\n            \"origin: http://pool-x.net\\r\\n\" .\n            \"user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36\\r\\n\" .\n            \"sec-fetch-site: cross-site\\r\\n\" .\n            \"sec-fetch-mode: cors\\r\\n\" .\n            \"referer: http://pool-x.net/assets/staking\\r\\n\" .\n            \"accept-encoding: gzip, deflate, br\\r\\n\" .\n            \"accept-language: zh-CN,zh;q=0.9,be;q=0.8,ru;q=0.7\\r\\n\" .\n            \"cookie: gr_user_id=1696256d-0a68-486f-a507-74191e74dbc6; grwng_uid=2682d2d1-4de3-407d-9946-5df333a44bef; _ga=GA1.2.224995769.1577363886; X-TRACE=w60NOEhe/g1irg2+SHF63xNYUS2H/vJUtP40DAUMqGQ=; a46016b4ef684522_gr_last_sent_cs1=265sy72; a46016b4ef684522_gr_session_id=45d1c2ec-dd54-4005-af9e-a01ccad4473b; a46016b4ef684522_gr_last_sent_sid_with_cs1=45d1c2ec-dd54-4005-af9e-a01ccad4473b; a46016b4ef684522_gr_session_id_45d1c2ec-dd54-4005-af9e-a01ccad4473b=true; SESSION=ZGExNmI1ODYtZTQzNi00MWQ0LTk1NzAtNzYzOTE3NDFjZDc5; _gid=GA1.2.951149480.1577691293; a46016b4ef684522_gr_cs1=265sy72\\r\\n\" .\n            \"x-domain: kitchen.pool-x.net\\r\\n\" .\n            \"x-session-id: da16b586-e436-41d4-9570-76391741cd79\\r\\n\" .\n            \"x-device-id: \\r\\n\" .\n            \"x-origin-domain-id: pool\\r\\n\" .\n            \"x-forwarded-proto: http\\r\\n\" .\n            \"uber-trace-id: ffaf3497a6deee40%3A8afa1564e1e0783f%3Affaf3497a6deee40%3A1\\r\\n\" .\n            \"x-forwarded-port: 80\\r\\n\" .\n            \"x-forwarded-for: 127.0.0.1\\r\\n\" .\n            \"x-user-id: 5dd5fbc9e316c178d6930678\\r\\n\" .\n            \"x-domain-id: pool\\r\\n\" .\n            \"kyc-country: \\r\\n\" .\n            \"kyc-status: \\r\\n\" .\n            \"x-forwarded-host: kitchen.pool-x.net\\r\\n\" .\n            \"x-forwarded-prefix: /pool-staking\\r\\n\" .\n            \"gateway-type: WEB\\r\\n\" .\n            \"lang: zh_CN\\r\\n\" .\n            \"Transfer-Encoding: chunked\\r\\n\" .\n            \"Host: 10.2.1.51:9526\\r\\n\" .\n            \"Connection: Keep-Alive\\r\\n\" .\n            \"\\r\\n\" .\n            \"0\\r\\n\" .\n            \"\\r\\n\";\n        $request_zero_length =\n            \"GET /locks?currency=&start_at=1576771200000&end_at=1577721599999&pageSize=20&page=1&c=6331b32ac32f4c128ce0016114e11dbd&lang=zh_CN&_t=1577694714586 HTTP/1.1\\r\\n\" .\n            \"x-real-ip: 10.2.100.1\\r\\n\" .\n            \"x-forwarded-server: kitchen.pool-x.net\\r\\n\" .\n            \"accept: application/json\\r\\n\" .\n            \"origin: http://pool-x.net\\r\\n\" .\n            \"user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36\\r\\n\" .\n            \"sec-fetch-site: cross-site\\r\\n\" .\n            \"sec-fetch-mode: cors\\r\\n\" .\n            \"referer: http://pool-x.net/assets/staking\\r\\n\" .\n            \"accept-encoding: gzip, deflate, br\\r\\n\" .\n            \"accept-language: zh-CN,zh;q=0.9,be;q=0.8,ru;q=0.7\\r\\n\" .\n            \"cookie: gr_user_id=1696256d-0a68-486f-a507-74191e74dbc6; grwng_uid=2682d2d1-4de3-407d-9946-5df333a44bef; _ga=GA1.2.224995769.1577363886; X-TRACE=w60NOEhe/g1irg2+SHF63xNYUS2H/vJUtP40DAUMqGQ=; a46016b4ef684522_gr_last_sent_cs1=265sy72; a46016b4ef684522_gr_session_id=45d1c2ec-dd54-4005-af9e-a01ccad4473b; a46016b4ef684522_gr_last_sent_sid_with_cs1=45d1c2ec-dd54-4005-af9e-a01ccad4473b; a46016b4ef684522_gr_session_id_45d1c2ec-dd54-4005-af9e-a01ccad4473b=true; SESSION=ZGExNmI1ODYtZTQzNi00MWQ0LTk1NzAtNzYzOTE3NDFjZDc5; _gid=GA1.2.951149480.1577691293; a46016b4ef684522_gr_cs1=265sy72\\r\\n\" .\n            \"x-domain: kitchen.pool-x.net\\r\\n\" .\n            \"x-session-id: da16b586-e436-41d4-9570-76391741cd79\\r\\n\" .\n            \"x-device-id: \\r\\n\" .\n            \"x-origin-domain-id: pool\\r\\n\" .\n            \"x-forwarded-proto: http\\r\\n\" .\n            \"uber-trace-id: df854c374e6d4fde%3Ada6b1dc2e4e112b5%3Adf854c374e6d4fde%3A0\\r\\n\" .\n            \"x-forwarded-port: 80\\r\\n\" .\n            \"x-forwarded-for: 127.0.0.1\\r\\n\" .\n            \"x-user-id: 5dd5fbc9e316c178d6930678\\r\\n\" .\n            \"x-domain-id: pool\\r\\n\" .\n            \"kyc-country: \\r\\n\" .\n            \"kyc-status: \\r\\n\" .\n            \"x-forwarded-host: kitchen.pool-x.net\\r\\n\" .\n            \"x-forwarded-prefix: /pool-staking\\r\\n\" .\n            \"gateway-type: WEB\\r\\n\" .\n            \"lang: zh_CN\\r\\n\" .\n            \"Content-Length: 0\\r\\n\" .\n            \"Host: 10.2.1.51:9526\\r\\n\" .\n            \"Connection: Keep-Alive\\r\\n\" .\n            \"\\r\\n\";\n        $request_chunked_body_array = ['FOO', 'BAR', 'CHAR', str_repeat('Z', mt_rand(10, 1000))];\n        $request_chunked_body = generateChunkBody($request_chunked_body_array);\n        $request_chunked = \"POST / HTTP/1.1\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n{$request_chunked_body}\\r\\n\";\n        $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::true($socket->connect('127.0.0.1', $pm->getFreePort()));\n        Assert::true($socket->setProtocol([\n            'open_eof_check' => true,\n            'package_eof' => EOF\n        ]));\n        /* chunked */\n        $ret = $socket->sendAll($request_empty_chunked);\n        Assert::same($ret, strlen($request_empty_chunked));\n        $ret = $socket->recvPacket();\n        Assert::isEmpty(getHttpBody($ret));\n        /* pipeline */\n        for ($n = MAX_REQUESTS_LOW; $n--;) {\n            $ret = $socket->sendAll($request_zero_length);\n            Assert::same($ret, strlen($request_zero_length));\n        }\n        for ($n = MAX_REQUESTS_LOW; $n--;) {\n            $ret = $socket->recvPacket();\n            Assert::same(getHttpBody($ret), getHttpBody($request_zero_length));\n        }\n        /* chunked */\n        for ($n = MAX_REQUESTS_LOW; $n--;) {\n            $ret = $socket->sendAll($request_chunked);\n            Assert::same($ret, strlen($request_chunked));\n            $ret = $socket->recvPacket();\n            Assert::same(getHttpBody($ret), implode('', $request_chunked_body_array));\n        }\n    });\n    echo \"SUCCESS\\n\";\n    $pm->kill();\n}\n\nfunction http_get_with_co_socket(string $domain, ?callable $cb = null)\n{\n    $cli = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, 0);\n    $cli->setProtocol(['open_ssl' => true,]);\n\n    if (!$cli->connect($domain, 443)) {\n        echo \"ERROR\\n\";\n    }\n\n    $http = \"GET / HTTP/1.1\\r\\nAccept: */*User-Agent: Lowell-Agent\\r\\nHost: $domain\\r\\nConnection: Keep-Alive\\r\\n\"\n        . \"Keep-Alive: on\\r\\n\\r\\n\";\n    if (!$cli->send($http)) {\n        echo \"ERROR\\n\";\n    }\n\n    $content = '';\n    $length = 0;\n    while (true) {\n        $read = $cli->recv();\n        if (empty($read)) {\n            var_dump($read);\n            break;\n        }\n        $content .= $read;\n        if ($length == 0) {\n            if (preg_match('#Content-Length: (\\d+)#i', $content, $match)) {\n                $length = intval($match[1]);\n            }\n        }\n        $header_length = strpos($content, \"\\r\\n\\r\\n\");\n        if (strlen($content) == $length + $header_length + 4) {\n            break;\n        }\n    }\n\n    if ($cb) {\n        $cb($cli, $content);\n    }\n    $cli->close();\n    return $content;\n}\n"
  },
  {
    "path": "tests/include/api/swoole_callback/swoole_cannot_destroy_active_lambda_function.php",
    "content": "<?php\n\n// Cannot destroy active lambda swoole_function\n// 先用nc起一个 swoole_server\n// nc -4lk 9090\n\nsend(\"hello\", function($cli, $data) {\n    var_dump($data);\n    send(\"hello\", function($cli, $data) {\n        var_dump($data);\n        send(\"hello\", function($cli, $data) {\n            var_dump($data);\n        });\n    });\n});\n\n\n\nfunction send($str, $onRecv)\n{\n    static $client;\n\n    if ($client === null) {\n        $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);\n\n        $client->on(\"error\", function($cli) { echo \"error\"; });\n        $client->on(\"close\", function($cli) { echo \"close\"; });\n\n        $client->on(\"connect\", function($cli) use($str, $onRecv) {\n            send($str, $onRecv);\n        });\n    }\n\n    // !!! Fatal error: Cannot destroy active lambda swoole_function\n    $client->on(\"receive\", $onRecv);\n\n    if ($client->isConnected()) {\n        $client->send(\"PING\");\n    } else {\n        $client->connect(\"127.0.0.1\", 9090);\n    }\n}\n"
  },
  {
    "path": "tests/include/api/swoole_client/connect_timeout.php",
    "content": "<?php\n$cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);\n$cli->on(\"connect\", function(Swoole\\Client $cli) {\n    Assert::true(false, 'never here');\n});\n$cli->on(\"receive\", function(Swoole\\Client $cli, $data) {\n    Assert::true(false, 'never here');\n});\n$cli->on(\"error\", function(Swoole\\Client $cli) { echo \"connect timeout\\n\"; });\n$cli->on(\"close\", function(Swoole\\Client $cli) { echo \"close\\n\"; });\n$cli->connect(\"11.11.11.11\", 9000, 0.5);\n"
  },
  {
    "path": "tests/include/api/swoole_client/connect_twice.php",
    "content": "<?php\n\n$start = microtime(true);\n\n$cli = new Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\n$cli->on(\"connect\", function(Swoole\\Async\\Client $cli) {\n    Assert::true(false, 'never here');\n});\n$cli->on(\"receive\", function(Swoole\\Async\\Client $cli, $data) {\n    Assert::true(false, 'never here');\n});\n$cli->on(\"error\", function(Swoole\\Async\\Client $cli) {\n    echo \"error\\n\";\n});\n$cli->on(\"close\", function(Swoole\\Async\\Client $cli) {\n    echo \"close\\n\";\n});\n\nfunction refcount($var)\n{\n    ob_start();\n    debug_zval_dump($var);\n    preg_match('/refcount\\((?<refcount>\\d)\\)/', ob_get_clean(), $matches);\n    return intval($matches[\"refcount\"]) - 3;\n}\n\n@$cli->connect(\"11.11.11.11\", 9000, 0.1);\n@$cli->connect(\"11.11.11.11\", 9000, 0.1);\n@$cli->connect(\"11.11.11.11\", 9000, 0.1);\n@$cli->connect(\"11.11.11.11\", 9000, 0.1);\n@$cli->connect(\"11.11.11.11\", 9000, 0.1);\nSwoole\\Event::wait();\n// xdebug_debug_zval(\"cli\");\n// echo refcount($cli); // php7无效\n"
  },
  {
    "path": "tests/include/api/swoole_client/http_get.php",
    "content": "<?php\n\nuse Swoole\\Client;\n\nfunction client_http_v10_get(Client $client)\n{\n    Assert::assert($client->connect('httpbin.org', 80, 10));\n    Assert::assert($client->send(\"GET / HTTP/1.1\\r\\nHost: httpbin.org\\r\\nConnection: close\\r\\n\\r\\n\"));\n\n    $resp = '';\n    while (true) {\n        $data = $client->recv();\n        if ($data === '' || $data === false) {\n            break;\n        }\n        $resp .= $data;\n    }\n\n    Assert::assert(str_starts_with($resp, 'HTTP/1.1 200 OK'));\n    Assert::assert(str_contains($resp, 'https://github.com/requests/httpbin'));\n}\n"
  },
  {
    "path": "tests/include/api/swoole_client/opcode_client.php",
    "content": "<?php\n\n\nrequire_once __DIR__ . \"/../../../include/bootstrap.php\";\n\n\n\n// suicide(5000);\n\n$cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);\n\n/** @noinspection PhpVoidFunctionResultUsedInspection */\nassert($cli->set([\n    'open_length_check' => 1,\n    'package_length_type' => 'N',\n    'package_length_offset' => 0,\n    'package_body_offset' => 0,\n]));\n\n$cli->on(\"connect\", function(Swoole\\Client $cli) {\n    Swoole\\Timer::clear($cli->timeo_id);\n    Assert::true($cli->isConnected());\n\n});\n\n$cli->on(\"receive\", function(Swoole\\Client $cli, $data){\n\n    $cli->close();\n    Assert::false($cli->isConnected());\n});\n\n$cli->on(\"error\", function(Swoole\\Client $cli) {\n    Swoole\\Timer::clear($cli->timeo_id);\n    echo \"ERROR\";\n});\n\n$cli->on(\"close\", function(Swoole\\Client $cli) {\n    Swoole\\Timer::clear($cli->timeo_id);\n    echo \"CLOSE\";\n});\n\n$cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT);\n\n$cli->timeo_id = Swoole\\Timer::after(1000, function() use($cli) {\n    debug_log(\"connect timeout\");\n    $cli->close();\n    Assert::false($cli->isConnected());\n});\n"
  },
  {
    "path": "tests/include/api/swoole_client/simple_client.php",
    "content": "<?php\n\n\nrequire_once __DIR__ . \"/../../../include/bootstrap.php\";\n\n\n\nsuicide(5000);\n\n\n$cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);\n\n/** @noinspection PhpVoidFunctionResultUsedInspection */\nassert($cli->set([\n    // TODO test\n    // 'open_eof_check' => true,\n    // 'package_eof' => \"\\r\\n\\r\\n\",\n\n    // TODO\n    // \"socket_buffer_size\" => 1,\n]));\n\n$cli->on(\"connect\", function(Swoole\\Client $cli) {\n    Swoole\\Timer::clear($cli->timeo_id);\n\n    // TODO getSocket BUG\n    // assert(is_resource($cli->getSocket()));\n    /*\n    $cli->getSocket();\n    // Warning: swoole_client_async::getSocket(): unable to obtain socket family Error: Bad file descriptor[9].\n    $cli->getSocket();\n     */\n\n\n    Assert::true($cli->isConnected());\n    $cli->send(RandStr::gen(1024, RandStr::ALL));\n    // $cli->sendfile(__DIR__.'/test.txt');\n});\n\n$cli->on(\"receive\", function(Swoole\\Client $cli, $data){\n    $recv_len = strlen($data);\n    debug_log(\"receive: len $recv_len\");\n    $cli->send(RandStr::gen(1024, RandStr::ALL));\n    $cli->close();\n    Assert::false($cli->isConnected());\n});\n\n$cli->on(\"error\", function(Swoole\\Client $cli) {\n    Swoole\\Timer::clear($cli->timeo_id);\n    debug_log(\"error\");\n});\n\n$cli->on(\"close\", function(Swoole\\Client $cli) {\n    Swoole\\Timer::clear($cli->timeo_id);\n    debug_log(\"close\");\n});\n\n$cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT);\n$cli->timeo_id = Swoole\\Timer::after(1000, function() use($cli) {\n    debug_log(\"connect timeout\");\n    $cli->close();\n    Assert::false($cli->isConnected());\n});\n"
  },
  {
    "path": "tests/include/api/swoole_client/socket_free.php",
    "content": "<?php\n\n// swoole socket 复用BUG\n\nfunction onClose(Swoole\\Client $cli) {\n    $fd = \\EventUtil::getSocketFd($cli->getSocket());\n    echo \"close fd <$fd>\\n\";\n}\n\nfunction onError(Swoole\\Client $cli) {\n    $fd = \\EventUtil::getSocketFd($cli->getSocket());\n    echo \"error fd <$fd>\\n\";\n}\n\n$host = \"127.0.0.1\";\n$port = 8050;\n\n$cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);\n$cli->on(\"receive\", function(Swoole\\Client $cli, $data){ });\n$cli->on(\"error\", \"onError\");\n$cli->on(\"close\", \"onClose\");\n\n$cli->on(\"connect\", function(Swoole\\Client $cli) use($host, $port) {\n    $fd = \\EventUtil::getSocketFd($cli->getSocket());\n    echo \"connected fd <$fd>\\n\";\n    $cli->close(); // close(fd)\n\n\n    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n    $newCli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);\n    $newCli->on(\"receive\", function(Swoole\\Client $cli, $data){ });\n    $newCli->on(\"error\", \"onError\");\n    $newCli->on(\"close\", \"onClose\");\n    $newCli->on(\"connect\", function(Swoole\\Client $newCli) use($cli)  {\n        $fd = \\EventUtil::getSocketFd($cli->getSocket());\n        echo \"connected fd <$fd>, reuse!!!\\n\";\n\n        echo \"free socket\\n\";\n        $cli->__destruct();\n        echo \"send\\n\";\n        $r = $newCli->send(\"HELLO\");\n    });\n    $newCli->connect($host, $port);\n    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n});\n\n$cli->connect($host, $port);\n"
  },
  {
    "path": "tests/include/api/swoole_http_server/http_server.php",
    "content": "<?php\n\nrequire_once __DIR__ . \"/../../../include/bootstrap.php\";\n\nclass HttpServer\n{\n    /**\n     * @var Swoole\\Http\\Server\n     */\n    public $httpServ;\n\n    public function __construct($host = HTTP_SERVER_HOST, $port = HTTP_SERVER_PORT, $ssl = false)\n    {\n        if ($ssl) {\n            $this->httpServ = new Swoole\\Http\\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n        } else {\n            $this->httpServ = new Swoole\\Http\\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n        }\n\n        $config = [\n            // 输出限制\n            \"output_buffer_size\" => 1024 * 1024 * 1024,\n            \"max_connection\" => 10240,\n            \"pipe_buffer_size\" => 1024 * 1024 * 1024,\n            // 'enable_port_reuse' => true,\n            'user' => 'www-data',\n            'group' => 'www-data',\n            'dispatch_mode' => 3,\n            'open_tcp_nodelay' => 1,\n            'open_cpu_affinity' => 1,\n            'daemonize' => 0,\n            'reactor_num' => 1,\n            'worker_num' => 2,\n            'max_request' => 100000,\n            'log_file' => TEST_LOG_FILE,\n\n            /*\n            'package_max_length' => 1024 * 1024 * 2\n            'open_length_check' => 1,\n            'package_length_type' => 'N',\n            'package_length_offset' => 0,\n            'package_body_offset' => 0,\n            'open_nova_protocol' => 1,\n            */\n        ];\n\n        if ($ssl) {\n            $config['ssl_cert_file'] = SSL_FILE_DIR . '/server.crt';\n            $config['ssl_key_file'] = SSL_FILE_DIR . '/server.key';\n        }\n        $this->httpServ->set($config);\n    }\n\n    public function start()\n    {\n        $this->httpServ->on('start', [$this, 'onStart']);\n        $this->httpServ->on('shutdown', [$this, 'onShutdown']);\n\n        $this->httpServ->on('workerStart', [$this, 'onWorkerStart']);\n        $this->httpServ->on('workerStop', [$this, 'onWorkerStop']);\n        $this->httpServ->on('workerError', [$this, 'onWorkerError']);\n\n        $this->httpServ->on('connect', [$this, 'onConnect']);\n        $this->httpServ->on('receive', [$this, 'onReceive']);\n        $this->httpServ->on('request', [$this, 'onRequest']);\n\n        $this->httpServ->on('close', [$this, 'onClose']);\n\n        $this->httpServ->start();\n    }\n\n    public function onConnect()\n    {\n        debug_log(\"connecting ......\");\n    }\n\n    public function onClose()\n    {\n        debug_log(\"closing .....\");\n    }\n\n    public function onStart(\\swoole_http_server $swooleServer)\n    {\n        debug_log(\"swoole_server starting .....\");\n    }\n\n    public function onShutdown(\\swoole_http_server $swooleServer)\n    {\n        debug_log(\"swoole_server shutdown .....\");\n    }\n\n    public function onWorkerStart(\\swoole_http_server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId starting .....\");\n    }\n\n    public function onWorkerStop(\\swoole_http_server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId stopping ....\");\n    }\n\n    public function onWorkerError(\\swoole_http_server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo)\n    {\n        debug_log(\"worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]...\");\n    }\n\n    public function onReceive(\\swoole_http_server $swooleServer, $fd, $fromId, $data)\n    {\n        $recv_len = strlen($data);\n        debug_log(\"receive: len $recv_len\");\n        $swooleServer->send($fd, RandStr::gen($recv_len, RandStr::ALL));\n    }\n\n    public function onRequest(\\Swoole\\Http\\Request $request, \\Swoole\\Http\\Response $response)\n    {\n        $uri = $request->server[\"request_uri\"];\n        if ($uri === \"/favicon.ico\")  {\n            $response->status(404);\n            $response->end();\n            return;\n        }\n\n        testSetCookie:\n        {\n            $name = \"name\";\n            $value = \"value\";\n            // $expire = $request->swoole_server[\"request_time\"] + 3600;\n            $expire = 0;\n            $path = \"/\";\n            $domain = \"\";\n            $secure = false;\n            $httpOnly = true;\n            // string $name [, string $value = \"\" [, int $expire = 0 [, string $path = \"\" [, string $domain = \"\" [, bool $secure = false [, bool $httponly = false ]]]]]]\n            $response->cookie($name, $value, $expire, $path, $domain, $secure, $httpOnly);\n            $expect = \"name=value; path=/; httponly\";\n            assert(in_array($expect, $response->cookie, true));\n        }\n\n\n        if ($uri === \"/ping\")  {\n            $this->httpServ->send($request->fd, \"HTTP/1.1 200 OK\\r\\nContent-Length: 4\\r\\n\\r\\npong\\r\\n\");\n            return;\n        }\n\n        if ($uri === \"/gzip\")  {\n            $level = 9;\n            $response->gzip($level);\n            $response->end(RandStr::gen(1024 * 1024 * 2, RandStr::ALL));\n            return;\n        }\n\n        if ($uri === \"/info\") {\n            ob_start();\n            print(\"request_uri: {$uri}\\n\");\n            print(\"request_method: {$request->server['request_method']}\\n\");\n\n            if (property_exists($request, \"get\")) {\n                print(\"get:\" . var_export($request->get, true) . \"\\n\");\n            }\n            if (property_exists($request, \"post\")) {\n                print(\"post:\" . var_export($request->post, true) . \"\\n\");\n            }\n            if (property_exists($request, \"cookie\")) {\n                print(\"cookie:\" . var_export($request->cookie, true) . \"\\n\");\n            }\n            if (property_exists($request, \"header\")) {\n                print(\"header:\" . var_export($request->header, true) . \"\\n\");\n            }\n\n            $response->end(nl2br(ob_get_clean()));\n            return;\n        }\n\n\n\n        if ($uri === \"/uri\") {\n            $response->end($request->server['request_uri']);\n            return;\n        }\n\n        if ($uri === \"/method\") {\n            $response->end($request->server['request_method']);\n            return;\n        }\n\n        if ($uri === \"/get\") {\n            if (!empty($request->get)) {\n                $response->end(json_encode($request->get));\n            } else {\n                $response->end(\"null\");\n            }\n            return;\n        }\n\n        if ($uri === \"/post\") {\n            if (property_exists($request, \"post\")) {\n                $response->end(json_encode($request->post));\n            } else {\n                $response->end(\"{}\");\n            }\n            return;\n        }\n\n        if ($uri === \"/cookie\") {\n            if (property_exists($request, \"cookie\")) {\n                $response->end(json_encode($request->cookie));\n            } else {\n                $response->end(\"{}\");\n            }\n            return;\n        }\n\n        if ($uri === \"/header\") {\n            if (property_exists($request, \"header\")) {\n                $response->end(json_encode($request->header));\n            } else {\n                $response->end(\"{}\");\n            }\n            return;\n        }\n\n        if ($uri === \"/sleep\") {\n            Swoole\\Timer::after(1000, function() use($response) {\n                $response->end();\n            });\n            return;\n        }\n\n        if ($uri === \"/404\") {\n            $response->status(404);\n            $response->end();\n            return;\n        }\n\n        if ($uri === \"/302\") {\n            $response->header(\"Location\", \"http://www.swoole.com/\");\n            $response->status(302);\n            $response->end();\n            return;\n        }\n\n        if ($uri === \"/code\") {\n            $response->end(highlight_string(file_get_contents(__FILE__), true));\n            return;\n        }\n\n        if ($uri === \"/json\") {\n            $response->header(\"Content-Type\", \"application/json\");\n            $response->end(json_encode($request->server, JSON_PRETTY_PRINT));\n            return;\n        }\n\n        if ($uri === \"/chunked\") {\n            $write = function($str) use($request) { return $this->httpServ->send($request->fd, $str); };\n\n            $write(\"HTTP/1.1 200 OK\\r\\n\");\n            $write(\"Content-Encoding: chunked\\r\\n\");\n            $write(\"Transfer-Encoding: chunked\\r\\n\");\n            $write(\"Content-Type: text/html\\r\\n\");\n            $write(\"Connection: keep-alive\\r\\n\");\n            $write(\"\\r\\n\");\n\n            // \"0\\r\\n\\r\\n\" finish\n            $writeChunk = function($str = \"\") use($write) {\n                $hexLen = dechex(strlen($str));\n                return $write(\"$hexLen\\r\\n$str\\r\\n\");\n            };\n            $timer = Swoole\\Timer::tick(200, function() use(&$timer, $writeChunk) {\n                static $i = 0;\n                $str = RandStr::gen($i++ % 40 + 1, RandStr::CHINESE) . \"<br>\";\n                if ($writeChunk($str) === false) {\n                    Swoole\\Timer::clear($timer);\n                }\n            });\n            return;\n        }\n\n        if ($uri === \"/content_length\") {\n            // $body = $request->rawcontent();\n            if (property_exists($request, \"header\")) {\n                if (isset($request->header['content-length'])) {\n                    $response->end($request->header['content-length']);\n                } else {\n                    $response->end(0);\n                }\n                return;\n            }\n        }\n\n        if ($uri === \"/rawcontent\") {\n            $response->end($request->rawcontent());\n            return;\n        }\n\n        if ($uri === \"/file\") {\n            $response->header(\"Content-Type\", \"text\");\n            $response->header(\"Content-Disposition\", \"attachment; filename=\\\"test.php\\\"\");\n            // TODO 这里会超时\n            $response->sendfile(__FILE__);\n        }\n\n        if ($uri === \"/rawcookie\") {\n            $response->cookie($name, $value, $expire, $path, $domain, $secure, $httpOnly);\n            $response->rawcookie(\"rawcontent\", $request->rawcontent());\n        }\n\n        $response->end(\"Hello World!\");\n    }\n}\n"
  },
  {
    "path": "tests/include/api/swoole_http_server/http_server_without_response.php",
    "content": "<?php\n\n$host = isset($argv[1]) ? $argv[1] : HTTP_SERVER_HOST;\n$port = isset($argv[2]) ? $argv[2] : HTTP_SERVER_PORT;\n\n$httpServer = new Swoole\\Http\\Server($host, $port, SWOOLE_PROCESS);\n$httpServer->on(\"request\", function ($request, $response) {\n});\n\n$httpServer->start();\n"
  },
  {
    "path": "tests/include/api/swoole_http_server/simple_http_server.php",
    "content": "<?php\n\nrequire_once __DIR__ . \"/http_server.php\";\n\n$host = isset($argv[1]) ? $argv[1] : HTTP_SERVER_HOST;\n$port = isset($argv[2]) ? $argv[2] : HTTP_SERVER_PORT;\n\n(new HttpServer($host, $port, false))->start();"
  },
  {
    "path": "tests/include/api/swoole_http_server/simple_https_server.php",
    "content": "<?php\nrequire_once __DIR__ . \"/http_server.php\";\n\n\n/*\nclass swoole_http_server extends swoole_server\n{\n    public swoole_function on($name, $cb) {} // 与 tcp swoole_server 的on接受的eventname 不同\n}\nclass swoole_http_response\n{\n    public swoole_function cookie() {}\n    public swoole_function rawcookie() {}\n    public swoole_function status() {}\n    public swoole_function gzip() {}\n    public swoole_function header() {}\n    public swoole_function write() {}\n    public swoole_function end() {}\n    public swoole_function sendfile() {}\n}\nclass swoole_http_request\n{\npublic swoole_function rawcontent() {}\n}\n */\n\n$host = isset($argv[1]) ? $argv[1] : HTTP_SERVER_HOST;\n$port = isset($argv[2]) ? $argv[2] : HTTP_SERVER_PORT;\n\n(new HttpServer($host, $port, true))->start();"
  },
  {
    "path": "tests/include/api/swoole_server/TestServer.php",
    "content": "<?php\n/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2017 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\nabstract class TestServer\n{\n    protected $index = array();\n    protected $recv_bytes = 0;\n    protected $count = 0;\n    protected $show_lost_package = false;\n\n    public static $PKG_NUM;\n    const LEN_MIN = 0;\n    const LEN_MAX = 200;\n\n    /**\n     * @var swoole_server\n     */\n    protected $serv;\n\n    abstract function onReceive($serv, $fd, $reactor_id, $data);\n\n    function __construct(int $port, bool $base = false)\n    {\n        $mode = $base ? SWOOLE_BASE : SWOOLE_PROCESS;\n        $serv = new Swoole\\Server('127.0.0.1', $port, $mode);\n        $serv->on('Connect', [$this, 'onConnect']);\n        $serv->on('receive', [$this, '_receive']);\n        $serv->on('workerStart', [$this, 'onWorkerStart']);\n        $serv->on('Close', [$this, 'onClose']);\n        $this->serv = $serv;\n    }\n\n    function onConnect($serv, $fd, $reactor_id)\n    {\n\n    }\n\n    function _receive($serv, $fd, $reactor_id, $data)\n    {\n        $this->count++;\n        $this->recv_bytes += strlen($data);\n        $this->onReceive($serv, $fd, $reactor_id, $data);\n        if ($this->count == self::$PKG_NUM)\n        {\n            $serv->send($fd, \"end\\n\");\n        }\n    }\n\n    function onClose($serv, $fd, $reactor_id)\n    {\n        echo \"Total count={$this->count}, bytes={$this->recv_bytes}\\n\";\n        if ($this->show_lost_package)\n        {\n            for ($i = 0; $i < self::$PKG_NUM; $i++)\n            {\n                if (!isset($this->index[$i]))\n                {\n                    echo \"lost package#$i\\n\";\n                }\n            }\n        }\n        $this->count = $this->recv_bytes = 0;\n        unset($this->index);\n        $this->index = array();\n    }\n\n    function set($conf)\n    {\n        $this->serv->set($conf);\n    }\n\n    function start()\n    {\n        $this->serv->start();\n    }\n\n    function onWorkerStart($serv, $id)\n    {\n        //sleep(1);\n    }\n\n    static function random()\n    {\n        return rand(self::LEN_MIN, self::LEN_MAX);\n    }\n}\n"
  },
  {
    "path": "tests/include/api/swoole_server/multi_protocol_server.php",
    "content": "<?php\n\nrequire_once __DIR__ . \"/../../../include/bootstrap.php\";\n\n//(new OpcodeServer('127.0.0.1', 9999, 9998, 9997))->start(PHP_INT_MAX);\n\n$host = isset($argv[1]) ? $argv[1] : null;\n$port = isset($argv[2]) ? $argv[2] : null;\n$port1 = isset($argv[3]) ? $argv[3] : null;\n$port2 = isset($argv[4]) ? $argv[4] : null;\n\n(new OpcodeServer($host, $port, $port1, $port2))->start();\n\nclass OpcodeServer\n{\n    /**\n     * @var Swoole\\Server\n     */\n    public $swooleServer;\n\n    public function __construct($host, $port, $port1, $port2)\n    {\n\t    $this->swooleServer = new Swoole\\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n        $this->swooleServer->set([\n            'dispatch_mode' => 3,\n            'worker_num' => 2,\n\n            'open_eof_split' => true,\n            'package_eof' => \"\\r\\n\",\n        ]);\n\n        $this->swooleServer->on(\"receive\", function(Swoole\\Server $server, $fd, $fromReactorId, $recv) use($port) {\n            Assert::same(intval($recv), $port);\n            $r = $server->send($fd, opcode_encode(\"return\", $port));\n            assert($r !== false);\n        });\n\n        $serv1 = $this->swooleServer->listen(TCP_SERVER_HOST, $port1, SWOOLE_SOCK_TCP);\n        assert($serv1 !== false);\n\n\n        $serv1->set([\n           'open_eof_split' => true,\n           'package_eof' => \"\\r\",\n        ]);\n\n        $serv1->on(\"receive\", function(Swoole\\Server $server, $fd, $fromReactorId, $recv) use($port1) {\n            Assert::same(intval($recv), $port1);\n            $r = $server->send($fd, opcode_encode(\"return\", $port1));\n            assert($r !== false);\n        });\n\n\n        $serv2 = $this->swooleServer->listen(TCP_SERVER_HOST, $port2, SWOOLE_SOCK_TCP);\n        assert($serv2 !== false);\n\n        $serv2->set([\n            'open_eof_split' => true,\n            'package_eof' => \"\\n\",\n        ]);\n\n\n        $serv2->on(\"receive\", function(Swoole\\Server $server, $fd, $fromReactorId, $recv) use($port2) {\n            Assert::same(intval($recv), $port2);\n            $r = $server->send($fd, opcode_encode(\"return\", $port2));\n            assert($r !== false);\n        });\n\n    }\n\n    public function start($lifetime = 1000)\n    {\n        $this->lifetime = $lifetime;\n\n        $this->swooleServer->on('start', [$this, 'onStart']);\n        $this->swooleServer->on('shutdown', [$this, 'onShutdown']);\n\n        $this->swooleServer->on('workerStart', [$this, 'onWorkerStart']);\n        $this->swooleServer->on('workerStop', [$this, 'onWorkerStop']);\n        $this->swooleServer->on('workerError', [$this, 'onWorkerError']);\n\n        $this->swooleServer->on('connect', [$this, 'onConnect']);\n\n        $this->swooleServer->on('close', [$this, 'onClose']);\n\n        $this->swooleServer->start();\n    }\n\n    public function onConnect() { }\n    public function onClose() { }\n    public function onStart(Swoole\\Server $swooleServer) { }\n    public function onShutdown(Swoole\\Server $swooleServer) { }\n    public function onWorkerStart(Swoole\\Server $swooleServer, $workerId)\n    {\n        if ($workerId === 0) {\n            Swoole\\Timer::after($this->lifetime, function() {\n                $this->swooleServer->shutdown();\n                kill_self_and_descendant(getmypid());\n                /*\n                \\Swoole\\Process::signal(SIGTERM, swoole_function() {\n                    $this->swooleServer->shutdown();\n                });\n                \\Swoole\\Process::kill(0, SIGTERM);\n                */\n            });\n        }\n    }\n    public function onWorkerStop(Swoole\\Server $swooleServer, $workerId) { }\n    public function onWorkerError(Swoole\\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { }\n}\n"
  },
  {
    "path": "tests/include/api/swoole_server/opcode_server.php",
    "content": "<?php\n\nrequire_once __DIR__ . \"/../../../include/bootstrap.php\";\n\n// (new OpcodeServer('127.0.0.1', 9999))->start(PHP_INT_MAX);\n\n$host = isset($argv[1]) ? $argv[1] : HTTP_SERVER_HOST;\n$port = isset($argv[2]) ? $argv[2] : HTTP_SERVER_PORT;\n$port1 = isset($argv[3]) ? $argv[3] : null;\n$port2 = isset($argv[4]) ? $argv[4] : null;\n\n(new OpcodeServer($host, $port, $port1, $port2))->start();\n\nclass OpcodeServer\n{\n    /**\n     * @var Swoole\\Server\n     */\n    public $swooleServer;\n\n    public function __construct($host, $port, $port1 = null, $port2 = null)\n    {\n\t    $this->swooleServer = new Swoole\\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n        $this->swooleServer->set([\n            'dispatch_mode' => 3,\n            'worker_num' => 2,\n            'task_worker_num' => 2,\n            'open_length_check' => 1,\n            'package_length_type' => 'N',\n            'package_length_offset' => 0,\n            'package_body_offset' => 0,\n            'heartbeat_idle_time' => 20,\n        ]);\n\n        if ($port1) {\n            $serv1 = $this->swooleServer->addListener(TCP_SERVER_HOST, $port1, SWOOLE_SOCK_TCP);\n            assert($serv1 !== false);\n        }\n        if ($port2) {\n            $serv2 = $this->swooleServer->addListener(TCP_SERVER_HOST, $port2, SWOOLE_SOCK_TCP);\n            assert($serv2 !== false);\n        }\n    }\n\n    public function start($lifetime = 1000)\n    {\n        $this->lifetime = $lifetime;\n\n        $this->swooleServer->on('start', [$this, 'onStart']);\n        $this->swooleServer->on('shutdown', [$this, 'onShutdown']);\n\n        $this->swooleServer->on('workerStart', [$this, 'onWorkerStart']);\n        $this->swooleServer->on('workerStop', [$this, 'onWorkerStop']);\n        $this->swooleServer->on('workerError', [$this, 'onWorkerError']);\n\n        $this->swooleServer->on('connect', [$this, 'onConnect']);\n        $this->swooleServer->on('receive', [$this, 'onReceive']);\n\n        $this->swooleServer->on('close', [$this, 'onClose']);\n\n        $this->swooleServer->on('task', [$this, 'onTask']);\n        $this->swooleServer->on('finish', [$this, 'onFinish']);\n        $this->swooleServer->on('pipeMessage', [$this, 'onPipeMessage']);\n        $this->swooleServer->on('packet', [$this, 'onPacket']);\n\n        $this->swooleServer->start();\n    }\n\n    public function onConnect() { }\n    public function onClose() { }\n    public function onStart(Swoole\\Server $swooleServer) { }\n    public function onShutdown(Swoole\\Server $swooleServer) { }\n    public function onWorkerStart(Swoole\\Server $swooleServer, $workerId)\n    {\n        if ($workerId === 0) {\n            Swoole\\Timer::after($this->lifetime, function() {\n                $this->swooleServer->shutdown();\n                kill_self_and_descendant(getmypid());\n                /*\n                \\Swoole\\Process::signal(SIGTERM, swoole_function() {\n                    $this->swooleServer->shutdown();\n                });\n                \\Swoole\\Process::kill(0, SIGTERM);\n                */\n            });\n        }\n    }\n    public function onWorkerStop(Swoole\\Server $swooleServer, $workerId) { }\n    public function onWorkerError(Swoole\\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo) { }\n    public function onReceive(Swoole\\Server $swooleServer, $fd, $fromReactorId, $recv)\n    {\n        list($op, $args) = opcode_decode($recv);\n\n        switch($op) {\n            case \"sendMessage\":\n                list($msg, $toWorkerId) = $args;\n                $r = $swooleServer->sendMessage(json_encode([\n                    \"fd\" => $fd,\n                    \"msg\" => $msg,\n                ]), $toWorkerId);\n                Assert::true($r);\n                return;\n\n            case \"sendfile\":\n                $len = filesize(__FILE__);\n                $r = $swooleServer->send($fd, pack(\"N\", $len + 4));\n                Assert::true($r);\n                assert($r !== false);\n                $r = $swooleServer->sendfile($fd, __FILE__);\n                Assert::true($r);\n                return;\n\n            case \"shutdown\":\n                $r = $swooleServer->shutdown();\n                assert($r !== false);\n                Assert::true($r);\n                $r = $swooleServer->send($fd, opcode_encode(\"return\", $r));\n                Assert::true($r);\n                return;\n\n            case \"stop\":\n                $r = $swooleServer->stop();\n                assert($r !== false);\n                Assert::true($r);\n                $r = $swooleServer->send($fd, opcode_encode(\"return\", $r));\n                Assert::true($r);\n                return;\n\n            default:\n                if (method_exists($swooleServer, $op)) {\n                    $r = call_user_func_array([$swooleServer, $op], $args);\n                    if (is_resource($r)) {\n                        $r = true;\n                    }\n                    $r = $swooleServer->send($fd, opcode_encode(\"return\", $r));\n                    Assert::true($r);\n                    return;\n                } else {\n\n                }\n        }\n    }\n\n    public function onTask(Swoole\\Server $swooleServer, $taskId, $fromWorkerId, $recv)\n    {\n        $recv = json_decode($recv);\n        Assert::same(json_last_error(), JSON_ERROR_NONE);\n        return json_encode($recv);\n    }\n\n    public function onFinish(Swoole\\Server $swooleServer, $taskId, $recv)\n    {\n        $recv = json_decode($recv);\n        Assert::same(json_last_error(), JSON_ERROR_NONE);\n        Assert::true(isset($recv[\"fd\"]) && isset($recv[\"data\"]));\n        $this->swooleServer->send($recv[\"fd\"], opcode_encode(\"return\", $recv[\"data\"]));\n    }\n\n    public function onPipeMessage(Swoole\\Server $swooleServer, $fromWorkerId, $recv)\n    {\n        $recv = json_decode($recv, true);\n        Assert::same(json_last_error(), JSON_ERROR_NONE);\n        Assert::true(isset($recv[\"fd\"]) && isset($recv[\"msg\"]));\n        $this->swooleServer->send($recv[\"fd\"], opcode_encode(\"return\", $recv[\"msg\"]));\n    }\n\n    public function onPacket(Swoole\\Server $swooleServer, $data, array $clientInfo)\n    {\n\n    }\n}\n"
  },
  {
    "path": "tests/include/api/swoole_server/reconnect_fail/tcp_client.php",
    "content": "<?php\nini_set(\"memory_limit\", \"1024m\");\n\nfunction reconn() {\n    echo \"Reconnect\\n\";\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);\n    $cli->on(\"connect\", function(Swoole\\Client $cli) {\n        // client 发送 大包数据\n        $cli->send(str_repeat(\"\\0\", 1024 * 1024 * 1.9));\n    });\n    $cli->on(\"receive\", function(Swoole\\Client $cli, $data) {\n        $cli->send($data);\n    });\n    $cli->on(\"error\", function(Swoole\\Client $cli) { echo \"error\\n\"; });\n    $cli->on(\"close\", function(Swoole\\Client $cli) { echo \"close\\n\"; reconn(); });\n    $cli->connect('127.0.0.1', 9001);\n}\n\nreconn();\n"
  },
  {
    "path": "tests/include/api/swoole_server/reconnect_fail/tcp_serv.php",
    "content": "<?php\n\nfunction debug_log($str, $handle = STDERR)\n{\n    if ($handle === STDERR) {\n        $tpl = \"\\033[31m[%d %s] %s\\033[0m\\n\";\n    } else {\n        $tpl = \"[%d %s] %s\\n\";\n    }\n    if (is_resource($handle)) {\n        fprintf($handle, $tpl, posix_getpid(), date(\"Y-m-d H:i:s\", time()), $str);\n    } else {\n        printf($tpl, posix_getpid(), date(\"Y-m-d H:i:s\", time()), $str);\n    }\n}\n\n(new TcpServer())->start();\n\nclass TcpServer\n{\n    /**\n     * @var Swoole\\Server\n     */\n    public $swooleServer;\n\n    public function __construct()\n    {\n        $this->swooleServer = new Swoole\\Server('127.0.0.1', 9001, SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n        $this->swooleServer->set([\n            // \"output_buffer_size\" => 1024 * 1024 * 1024, // 输出限制\n\n            'max_connection'    => 10240,\n            'pipe_buffer_size'  => 1024 * 1024 * 2,\n//            'pipe_buffer_size'  => 1024,\n\n            'dispatch_mode'     => 3,\n            'open_tcp_nodelay'  => 1,\n            'open_cpu_affinity' => 1,\n            'daemonize'         => 0,\n            'reactor_num'       => 1,\n            'worker_num'        => 2,\n            'max_request'       => 100000,\n        ]);\n    }\n\n    public function start()\n    {\n        $this->swooleServer->on('start', [$this, 'onStart']);\n        $this->swooleServer->on('shutdown', [$this, 'onShutdown']);\n\n        $this->swooleServer->on('workerStart', [$this, 'onWorkerStart']);\n        $this->swooleServer->on('workerStop', [$this, 'onWorkerStop']);\n        $this->swooleServer->on('workerError', [$this, 'onWorkerError']);\n\n        $this->swooleServer->on('connect', [$this, 'onConnect']);\n        $this->swooleServer->on('receive', [$this, 'onReceive']);\n\n        $this->swooleServer->on('close', [$this, 'onClose']);\n\n        $this->swooleServer->start();\n    }\n\n    public function onConnect()\n    {\n        debug_log(\"connecting ......\");\n    }\n\n    public function onClose()\n    {\n        debug_log(\"closing .....\");\n    }\n\n    public function onStart(Swoole\\Server $swooleServer)\n    {\n        debug_log(\"swoole_server starting .....\");\n    }\n\n    public function onShutdown(Swoole\\Server $swooleServer)\n    {\n        debug_log(\"swoole_server shutdown .....\");\n    }\n\n    public function onWorkerStart(Swoole\\Server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId starting .....\");\n    }\n\n    public function onWorkerStop(Swoole\\Server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId stopping ....\");\n    }\n\n    public function onWorkerError(Swoole\\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo)\n    {\n        debug_log(\"worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]...\");\n    }\n\n    public function onReceive(Swoole\\Server $swooleServer, $fd, $fromId, $data)\n    {\n        echo \"close $fd\\n\";\n//        var_dump($swooleServer->shutdown());\n        // swoole_server 接受数据之后立马关闭连接\n        var_dump($swooleServer->close($fd));\n        // $swooleServer->send($fd, $data);\n    }\n}\n"
  },
  {
    "path": "tests/include/api/swoole_server/server_manager_process_exit.php",
    "content": "<?php\n\nrequire_once __DIR__ . \"/../../../include/bootstrap.php\";\n\n\nif (pcntl_fork() === 0) {\n    suicide(1000);\n    exit();\n}\n\n\nif (pcntl_fork() === 0) {\n    suicide(1000);\n    exit();\n}\n\n\nif (pcntl_fork() === 0) {\n    suicide(1000);\n\n\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);\n\n    /** @noinspection PhpVoidFunctionResultUsedInspection */\n    assert($cli->set([\n        \"socket_buffer_size\" => 1024,\n    ]));\n\n    $cli->on(\"connect\", function(Swoole\\Client $cli) {\n        Swoole\\Timer::clear($cli->timeo_id);\n        Assert::true($cli->isConnected());\n        $cli->send(str_repeat(\"\\0\", 1024));\n    });\n\n    $cli->on(\"receive\", function(Swoole\\Client $cli, $data){\n        $recv_len = strlen($data);\n        debug_log(\"receive: len $recv_len\");\n        $cli->send(str_repeat(\"\\0\", $recv_len));\n    });\n\n    $cli->on(\"error\", function(Swoole\\Client $cli) {\n        Swoole\\Timer::clear($cli->timeo_id);\n        debug_log(\"error\");\n    });\n\n    $cli->on(\"close\", function(Swoole\\Client $cli) {\n        Swoole\\Timer::clear($cli->timeo_id);\n        debug_log(\"close\");\n    });\n\n    $cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT);\n    $cli->timeo_id = Swoole\\Timer::after(1000, function() use($cli) {\n        debug_log(\"connect timeout\");\n        $cli->close();\n        Assert::false($cli->isConnected());\n    });\n    exit();\n}\n\n(new TcpServer())->start();\n\nclass TcpServer\n{\n    /**\n     * @var Swoole\\Server\n     */\n    public $swooleServer;\n\n    public function __construct()\n    {\n        $this->swooleServer = new Swoole\\Server(TCP_SERVER_HOST, TCP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n        $this->swooleServer->set([\n            \"output_buffer_size\" => 1024 * 1024 * 1024, // 输出限制\n            \"max_connection\" => 10240,\n            \"pipe_buffer_size\" => 1024 * 1024 * 1024,\n            'daemonize' => 0,\n            'worker_num' => 2,\n            'max_request' => 100000,\n            'reactor_num' => 1,\n            // 'package_max_length' => 1024 * 1024 * 2\n        ]);\n    }\n\n    public function start()\n    {\n        $this->swooleServer->on('start', [$this, 'onStart']);\n        $this->swooleServer->on('shutdown', [$this, 'onShutdown']);\n\n        $this->swooleServer->on('workerStart', [$this, 'onWorkerStart']);\n        $this->swooleServer->on('workerStop', [$this, 'onWorkerStop']);\n        $this->swooleServer->on('workerError', [$this, 'onWorkerError']);\n\n        $this->swooleServer->on('connect', [$this, 'onConnect']);\n        $this->swooleServer->on('receive', [$this, 'onReceive']);\n\n        $this->swooleServer->on('close', [$this, 'onClose']);\n\n        $this->swooleServer->start();\n    }\n\n    public function onConnect()\n    {\n        debug_log(\"connecting ......\");\n    }\n\n    public function onClose()\n    {\n        debug_log(\"closing .....\");\n    }\n\n    public function onStart(Swoole\\Server $swooleServer)\n    {\n        debug_log(\"swoole_server starting .....\");\n    }\n\n    public function onShutdown(Swoole\\Server $swooleServer)\n    {\n        debug_log(\"swoole_server shutdown .....\");\n    }\n\n    public function onWorkerStart(Swoole\\Server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId starting .....\");\n    }\n\n    public function onWorkerStop(Swoole\\Server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId stopping ....\");\n    }\n\n    public function onWorkerError(Swoole\\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo)\n    {\n        debug_log(\"worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]...\");\n    }\n\n    public function onReceive(Swoole\\Server $swooleServer, $fd, $fromId, $data)\n    {\n        $recv_len = strlen($data);\n        debug_log(\"receive: len $recv_len\");\n        $swooleServer->send($fd, str_repeat(\"\\0\", $recv_len));\n    }\n}\n"
  },
  {
    "path": "tests/include/api/swoole_server/server_send_fast_recv_slow.php",
    "content": "<?php\n\nrequire_once __DIR__ . \"/../../../include/bootstrap.php\";\n\n\nif (pcntl_fork() === 0) {\n    suicide(3000);\n\n\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);\n\n    /** @noinspection PhpVoidFunctionResultUsedInspection */\n    assert($cli->set([\n        \"socket_buffer_size\" => 1,\n    ]));\n\n    $cli->on(\"connect\", function(Swoole\\Client $cli) {\n        Swoole\\Timer::clear($cli->timeo_id);\n\n        // TODO getSocket BUG\n        // assert(is_resource($cli->getSocket()));\n        /*\n        $cli->getSocket();\n        // Warning: swoole_client_async::getSocket(): unable to obtain socket family Error: Bad file descriptor[9].\n        $cli->getSocket();\n         */\n\n\n        Assert::true($cli->isConnected());\n        $cli->send(str_repeat(\"\\0\", 1024));\n        // $cli->sendfile(__DIR__.'/test.txt');\n    });\n\n    $cli->on(\"receive\", function(Swoole\\Client $cli, $data){\n        $recv_len = strlen($data);\n        debug_log(\"receive: len $recv_len\");\n        $cli->send(str_repeat(\"\\0\", 1024));\n    });\n\n    $cli->on(\"error\", function(Swoole\\Client $cli) {\n        Swoole\\Timer::clear($cli->timeo_id);\n        debug_log(\"error\");\n    });\n\n    $cli->on(\"close\", function(Swoole\\Client $cli) {\n        Swoole\\Timer::clear($cli->timeo_id);\n        debug_log(\"close\");\n    });\n\n    $cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT);\n    $cli->timeo_id = Swoole\\Timer::after(1000, function() use($cli) {\n        debug_log(\"connect timeout\");\n        $cli->close();\n        Assert::false($cli->isConnected());\n    });\n\n    exit();\n}\n\n\n(new TcpServer())->start();\n\nclass TcpServer\n{\n    /**\n     * @var Swoole\\Server\n     */\n    public $swooleServer;\n\n    public function __construct()\n    {\n        $this->swooleServer = new Swoole\\Server(TCP_SERVER_HOST, TCP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n        $this->swooleServer->set([\n            \"output_buffer_size\" => 1024 * 1024 * 1024, // 输出限制\n            \"max_connection\" => 10240,\n            \"pipe_buffer_size\" => 1024 * 1024 * 1024,\n\n            // 'log_file' => __DIR__ . '/send_fast_recv_slow.log',\n            'daemonize' => 0,\n            'worker_num' => 2,\n            'max_request' => 100000,\n            'reactor_num' => 1,\n            // 'package_max_length' => 1024 * 1024 * 2\n        ]);\n    }\n\n    public function start()\n    {\n        $this->swooleServer->on('start', [$this, 'onStart']);\n        $this->swooleServer->on('shutdown', [$this, 'onShutdown']);\n\n        $this->swooleServer->on('workerStart', [$this, 'onWorkerStart']);\n        $this->swooleServer->on('workerStop', [$this, 'onWorkerStop']);\n        $this->swooleServer->on('workerError', [$this, 'onWorkerError']);\n\n        $this->swooleServer->on('connect', [$this, 'onConnect']);\n        $this->swooleServer->on('receive', [$this, 'onReceive']);\n\n        $this->swooleServer->on('close', [$this, 'onClose']);\n\n        $this->swooleServer->start();\n    }\n\n    public function onConnect()\n    {\n        debug_log(\"connecting ......\");\n    }\n\n    public function onClose()\n    {\n        debug_log(\"closing .....\");\n    }\n\n    public function onStart(Swoole\\Server $swooleServer)\n    {\n        debug_log(\"swoole_server starting .....\");\n    }\n\n    public function onShutdown(Swoole\\Server $swooleServer)\n    {\n        debug_log(\"swoole_server shutdown .....\");\n    }\n\n    public function onWorkerStart(Swoole\\Server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId starting .....\");\n    }\n\n    public function onWorkerStop(Swoole\\Server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId stopping ....\");\n    }\n\n    public function onWorkerError(Swoole\\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo)\n    {\n        debug_log(\"worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]...\");\n    }\n\n    public function onReceive(Swoole\\Server $swooleServer, $fd, $fromId, $data)\n    {\n        $recv_len = strlen($data);\n        debug_log(\"receive: len $recv_len\");\n        $swooleServer->send($fd, str_repeat(\"\\0\", $recv_len));\n    }\n}\n"
  },
  {
    "path": "tests/include/api/swoole_server/simple_server.php",
    "content": "<?php\n\nrequire_once __DIR__ . \"/../../../include/bootstrap.php\";\n/*\nif (pcntl_fork() === 0) {\n    require_once __DIR__ . \"/../swoole_client_async/simple_client.php\";\n    exit();\n}*/\n\n(new TcpServer())->start();\n\nclass TcpServer\n{\n    /**\n     * @var Swoole\\Server\n     */\n    public $swooleServer;\n\n    public function __construct()\n    {\n\t$this->swooleServer = new Swoole\\Server(TCP_SERVER_HOST, TCP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n        $this->swooleServer->set([\n            // \"output_buffer_size\" => 1024 * 1024 * 1024, // 输出限制\n            \"max_connection\" => 10240,\n            \"pipe_buffer_size\" => 1024 * 1024 * 1024,\n\n            // 'enable_port_reuse' => true,\n            // 'user' => 'www-data',\n            // 'group' => 'www-data',\n\n//            'log_file' => __DIR__ . '/simple_server.log',\n            'dispatch_mode' => 2,\n            'open_tcp_nodelay' => 1,\n            'open_cpu_affinity' => 1,\n            'daemonize' => 0,\n            'reactor_num' => 1,\n            'worker_num' => 1,\n            'max_request' => 100000,\n            // 'package_max_length' => 1024 * 1024 * 2\n            /*\n            'open_length_check' => 1,\n            'package_length_type' => 'N',\n            'package_length_offset' => 0,\n            'package_body_offset' => 0,\n            'open_nova_protocol' => 1,\n            */\n        ]);\n    }\n\n    public function start()\n    {\n        $this->swooleServer->on('start', [$this, 'onStart']);\n        $this->swooleServer->on('shutdown', [$this, 'onShutdown']);\n\n        $this->swooleServer->on('workerStart', [$this, 'onWorkerStart']);\n        $this->swooleServer->on('workerStop', [$this, 'onWorkerStop']);\n        $this->swooleServer->on('workerError', [$this, 'onWorkerError']);\n\n        $this->swooleServer->on('connect', [$this, 'onConnect']);\n        $this->swooleServer->on('receive', [$this, 'onReceive']);\n\n        $this->swooleServer->on('close', [$this, 'onClose']);\n\n        $this->swooleServer->start();\n    }\n\n    public function onConnect()\n    {\n\t    debug_log(\"connecting ......\");\n    }\n\n    public function onClose()\n    {\n        debug_log(\"closing .....\");\n    }\n\n    public function onStart(Swoole\\Server $swooleServer)\n    {\n        debug_log(\"swoole_server starting .....\");\n    }\n\n    public function onShutdown(Swoole\\Server $swooleServer)\n    {\n        debug_log(\"swoole_server shutdown .....\");\n    }\n\n    public function onWorkerStart(Swoole\\Server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId starting .....\");\n    }\n\n    public function onWorkerStop(Swoole\\Server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId stopping ....\");\n    }\n\n    public function onWorkerError(Swoole\\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo)\n    {\n        debug_log(\"worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]...\");\n    }\n\n    public function onReceive(Swoole\\Server $swooleServer, $fd, $fromId, $data)\n    {\n        if (trim($data) == 'shutdown')\n        {\n            $swooleServer->shutdown();\n            return;\n        }\n        $recv_len = strlen($data);\n        debug_log(\"receive: len $recv_len\");\n\t    $swooleServer->send($fd, RandStr::gen($recv_len, RandStr::ALL));\n        // $swooleServer->close($fd);\n    }\n}\n"
  },
  {
    "path": "tests/include/api/swoole_server/simple_tcp_server.php",
    "content": "<?php\n\nrequire_once __DIR__ . \"/../../../include/bootstrap.php\";\n\n//(new TcpServer($argv[1], $argv[2]))->start();\n$host = isset($argv[1]) ? $argv[1] : TCP_SERVER_HOST;\n$port = isset($argv[2]) ? $argv[2] : TCP_SERVER_PORT;\n\n$server = new TcpServer($host, $port);\n$server->start();\n\nclass TcpServer\n{\n    /**\n     * @var Swoole\\Server\n     */\n    public $swooleServer;\n\n    public function __construct($host, $port)\n    {\n        echo \"swoole_server host:$host, port:$port\\n\";\n        $this->swooleServer = new Swoole\\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n        $this->swooleServer->set([\n            \"pipe_buffer_size\" => 1024 * 1024 * 1024,\n            'dispatch_mode' => 3,\n            'open_tcp_nodelay' => 1,\n            'open_cpu_affinity' => 1,\n            //'daemonize' => 1,\n            'reactor_num' => 2,\n            'worker_num' => 4,\n            'max_request' => 100000,\n        ]);\n    }\n\n    public function start()\n    {\n        $this->swooleServer->on('start', [$this, 'onStart']);\n        $this->swooleServer->on('shutdown', [$this, 'onShutdown']);\n\n        $this->swooleServer->on('workerStart', [$this, 'onWorkerStart']);\n        $this->swooleServer->on('workerStop', [$this, 'onWorkerStop']);\n        $this->swooleServer->on('workerError', [$this, 'onWorkerError']);\n\n        $this->swooleServer->on('connect', [$this, 'onConnect']);\n        $this->swooleServer->on('receive', [$this, 'onReceive']);\n\n        $this->swooleServer->on('close', [$this, 'onClose']);\n\n        $this->swooleServer->start();\n    }\n\n    public function onConnect()\n    {\n\t    debug_log(\"connecting ......\");\n    }\n\n    public function onClose()\n    {\n        debug_log(\"closing .....\");\n    }\n\n    public function onStart(Swoole\\Server $swooleServer)\n    {\n        debug_log(\"swoole_server starting .....\");\n    }\n\n    public function onShutdown(Swoole\\Server $swooleServer)\n    {\n        debug_log(\"swoole_server shutdown .....\");\n    }\n\n    public function onWorkerStart(Swoole\\Server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId starting .....\");\n        if ($workerId == 0) {\n            Swoole\\Timer::after(5000, function () {\n                $this->swooleServer->shutdown();\n            });\n        }\n    }\n\n    public function onWorkerStop(Swoole\\Server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId stopping ....\");\n    }\n\n    public function onWorkerError(Swoole\\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo)\n    {\n        debug_log(\"worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]...\");\n    }\n\n    public function onReceive(Swoole\\Server $swooleServer, $fd, $fromId, $data)\n    {\n        //echo \"swoole_server receive data: $data\\n\";\n        $recv_len = strlen($data);\n        debug_log(\"receive: len $recv_len\");\n\n\t    //$swooleServer->send($fd, RandStr::gen($recv_len, RandStr::ALL));\n\n \t\t$filename = __DIR__ . \"/testsendfile.txt\";\n\t\t$swooleServer->sendfile($fd, $filename);\n\t}\n}\n"
  },
  {
    "path": "tests/include/api/swoole_server/simple_udp_server.php",
    "content": "<?php\n\nrequire_once __DIR__ . \"/../../../include/bootstrap.php\";\n\n(new UdpServer())->start();\n\nclass UdpServer\n{\n    public $swooleServer;\n\n    public function __construct()\n    {\n\t    $this->swooleServer = new Swoole\\Server(UDP_SERVER_HOST, UDP_SERVER_PORT, SWOOLE_PROCESS, SWOOLE_SOCK_UDP);\n        $this->swooleServer->set([\n            \"max_connection\" => 1000,\n            'dispatch_mode' => 3,\n            'daemonize' => 0,\n            'reactor_num' => 4,\n            'worker_num' => 8,\n            'max_request' => 1000,\n        ]);\n    }\n\n    public function start()\n    {\n        $this->swooleServer->on('start', [$this, 'onStart']);\n        $this->swooleServer->on('shutdown', [$this, 'onShutdown']);\n\n        $this->swooleServer->on('workerStart', [$this, 'onWorkerStart']);\n        $this->swooleServer->on('workerStop', [$this, 'onWorkerStop']);\n        $this->swooleServer->on('workerError', [$this, 'onWorkerError']);\n\n        $this->swooleServer->on('connect', [$this, 'onConnect']);\n        $this->swooleServer->on('Packet',  [$this, 'onPacket']);\n        $this->swooleServer->on('close', [$this, 'onClose']);\n\n        $this->swooleServer->start();\n    }\n\n    public function onConnect()\n    {\n\t    debug_log(\"connecting ......\");\n    }\n\n    public function onClose()\n    {\n        debug_log(\"closing .....\");\n    }\n\n    public function onStart(Swoole\\Server $swooleServer)\n    {\n        debug_log(\"swoole_server starting .....\");\n    }\n\n    public function onShutdown(Swoole\\Server $swooleServer)\n    {\n        debug_log(\"swoole_server shutdown .....\");\n    }\n\n    public function onWorkerStart(Swoole\\Server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId starting .....\");\n        Swoole\\Timer::after(3000, function() {\n            $this->swooleServer->shutdown();\n        });\n    }\n\n    public function onWorkerStop(Swoole\\Server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId stopping ....\");\n    }\n\n    public function onWorkerError(Swoole\\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo)\n    {\n        debug_log(\"worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]...\");\n    }\n\n    //UDP: 收到数据帧事件\n    public function onPacket(Swoole\\Server $swooleServer, $data, $clientInfo)\n    {\n        if (trim($data) == 'shutdown')\n        {\n            $swooleServer->shutdown();\n            return;\n        }\n        //echo \"clientInfo: $clientInfo, receive: $data\\n\";\n        $swooleServer->sendto($clientInfo['address'], $clientInfo['port'], $data);\n    }\n}\n"
  },
  {
    "path": "tests/include/api/swoole_server/tcp_serv.php",
    "content": "<?php\nrequire_once dirname(dirname(__DIR__)).'/bootstrap.php';\n\nclass TcpServer\n{\n    /**\n     * @var Swoole\\Server\n     */\n    public $swooleServer;\n\n    public function __construct()\n    {\n        global $argv;\n\n        $this->swooleServer = new Swoole\\Server($argv[1], $argv[2], SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n        $this->swooleServer->set([\n            // \"output_buffer_size\" => 1024 * 1024 * 1024, // 输出限制\n\n            'log_file' => TEST_LOG_FILE,\n            'max_connection'    => 10240,\n            'pipe_buffer_size'  => 1024 * 1024 * 1024,\n\n            'dispatch_mode'     => 3,\n            'open_tcp_nodelay'  => 1,\n            'open_cpu_affinity' => 1,\n            'daemonize'         => 0,\n            'reactor_num'       => 1,\n            'worker_num'        => 2,\n            'max_request'       => 100000,\n        ]);\n    }\n\n    public function start()\n    {\n        $this->swooleServer->on('start', [$this, 'onStart']);\n        $this->swooleServer->on('shutdown', [$this, 'onShutdown']);\n\n        $this->swooleServer->on('workerStart', [$this, 'onWorkerStart']);\n        $this->swooleServer->on('workerStop', [$this, 'onWorkerStop']);\n        $this->swooleServer->on('workerError', [$this, 'onWorkerError']);\n\n        $this->swooleServer->on('connect', [$this, 'onConnect']);\n        $this->swooleServer->on('receive', [$this, 'onReceive']);\n\n        $this->swooleServer->on('close', [$this, 'onClose']);\n\n        $this->swooleServer->start();\n    }\n\n    public function onConnect()\n    {\n//        print(\"connecting ......\");\n    }\n\n    public function onClose()\n    {\n//        print(\"closing .....\");\n    }\n\n    public function onStart(Swoole\\Server $swooleServer)\n    {\n//        print(\"swoole_server starting .....\");\n    }\n\n    public function onShutdown(Swoole\\Server $swooleServer)\n    {\n//        print(\"swoole_server shutdown .....\");\n    }\n\n    public function onWorkerStart(Swoole\\Server $swooleServer, $workerId)\n    {\n//        print(\"worker #$workerId starting .....\");\n    }\n\n    public function onWorkerStop(Swoole\\Server $swooleServer, $workerId)\n    {\n//        print(\"worker #$workerId stopping ....\");\n    }\n\n    public function onWorkerError(Swoole\\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo)\n    {\n//        print(\"worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]...\");\n    }\n\n    public function onReceive(Swoole\\Server $swooleServer, $fd, $fromId, $data)\n    {\n        static $i;\n        if ($i > USE_VALGRIND ? 200 : 20000)\n        {\n            $swooleServer->close($fd);\n            $swooleServer->shutdown();\n            @unlink(__DIR__ . '/swoole.log');\n        }\n        else\n        {\n            $swooleServer->send($fd, $data);\n        }\n        $i++;\n    }\n}\n\n(new TcpServer())->start();\n"
  },
  {
    "path": "tests/include/api/swoole_server/tcp_task_server.php",
    "content": "<?php\n\nrequire_once __DIR__ . \"/../../../include/bootstrap.php\";\n$host = isset($argv[1]) ? $argv[1] : TCP_SERVER_HOST;\n$port = isset($argv[2]) ? $argv[2] : TCP_SERVER_PORT;\n$server = new TcpServer($host, $port);\n$server->start();\n\nclass TcpServer\n{\n    public $swooleServer;\n\n    public function __construct($host, $port)\n    {\n        echo \"swoole_server host:$host, port:$port\\n\";\n        $this->swooleServer = new Swoole\\Server($host, $port, SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n        $this->swooleServer->set([\n            \"pipe_buffer_size\" => 1024 * 1024 * 1024,\n            'dispatch_mode' => 3,\n            'open_tcp_nodelay' => 1,\n            'open_cpu_affinity' => 1,\n            //'daemonize' => 1,\n            'reactor_num' => 2,\n            'worker_num' => 4,\n\t\t\t'task_worker_num' => 8,\n            'max_request' => 100000,\n\t\t\t'log_file' => '/tmp/swoole_server.log',\n        ]);\n    }\n\n    public function start()\n    {\n        $this->swooleServer->on('start', [$this, 'onStart']);\n        $this->swooleServer->on('shutdown', [$this, 'onShutdown']);\n\n        $this->swooleServer->on('workerStart', [$this, 'onWorkerStart']);\n        $this->swooleServer->on('workerStop', [$this, 'onWorkerStop']);\n        $this->swooleServer->on('workerError', [$this, 'onWorkerError']);\n\n        $this->swooleServer->on('connect', [$this, 'onConnect']);\n        $this->swooleServer->on('receive', [$this, 'onReceive']);\n        $this->swooleServer->on('task', [$this, 'onTask']);\n        $this->swooleServer->on('finish', [$this, 'onFinish']);\n\n        $this->swooleServer->on('close', [$this, 'onClose']);\n\n        $this->swooleServer->start();\n    }\n\n    public function onConnect()\n    {\n\t    debug_log(\"connecting ......\");\n    }\n\n    public function onClose()\n    {\n        debug_log(\"closing .....\");\n    }\n\n    public function onStart(Swoole\\Server $swooleServer)\n    {\n        debug_log(\"swoole_server starting .....\");\n    }\n\n    public function onShutdown(Swoole\\Server $swooleServer)\n    {\n        debug_log(\"swoole_server shutdown .....\");\n    }\n\n    public function onWorkerStart(Swoole\\Server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId starting .....\");\n        if ($workerId == 0) {\n            //Swoole\\Timer::after(5000, function () {\n            //    $this->swooleServer->shutdown();\n            //});\n        }\n    }\n\n    public function onWorkerStop(Swoole\\Server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId stopping ....\");\n    }\n\n    public function onWorkerError(Swoole\\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo)\n    {\n        debug_log(\"worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]...\");\n    }\n\n    public function onReceive(Swoole\\Server $swooleServer, $fd, $fromId, $data)\n    {\n        //echo \"swoole_server receive data: $data\\n\";\n\t\t$param = array(\n\t\t\t'fd' => $fd,\n\t\t\t'data' => $data,\n\t\t);\n\t\t$swooleServer->task(json_encode($param));\n\t\t//echo \"send data to task worker.\\n\";\n\t}\n\n    public function onTask(Swoole\\Server $swooleServer, $task_id, $fromId, $data)\n    {\n        $task_data = json_decode($data, true);\n        $swooleServer->finish($task_data);\n    }\n\n    public function onFinish(Swoole\\Server $swooleServer, $worker_task_id, $task_data)\n    {\n        $swooleServer->send($task_data['fd'], \"OK\");\n    }\n}\n"
  },
  {
    "path": "tests/include/api/swoole_server/testsendfile.txt",
    "content": "testsendfile.txt\n"
  },
  {
    "path": "tests/include/api/swoole_thread/putenv.php",
    "content": "<?php\nrequire __DIR__ . '/../../../include/bootstrap.php';\n$args = Swoole\\Thread::getArguments();\n$val = $args[0] . '_' . time() . '_' . uniqid();\nputenv('TEST_THREAD_' . $args[0] . '=' . $val);\nAssert::eq(getenv('TEST_THREAD_' . $args[0]), $val);\n"
  },
  {
    "path": "tests/include/api/swoole_thread/sleep.php",
    "content": "<?php\n$us = random_int(100, 200000);\nusleep($us);\n"
  },
  {
    "path": "tests/include/api/swoole_timer/accurate_test.php",
    "content": "<?php\n\nrequire_once __DIR__ . \"/../../../include/bootstrap.php\";\n\n//swoole_function Swoole\\Timer::after($ms, $callback, $param = null) {}\n//swoole_function Swoole\\Timer::tick($ms, $callback) {}\n//swoole_function Swoole\\Timer::clear($timer_id) {}\n\n\nfunction after()\n{\n    $start = microtime(true);\n    Swoole\\Timer::after(1000, function() use($start) {\n        echo microtime(true) - $start, \"\\n\";\n        after();\n    });\n}\n\n//for ($i = 0; $i < 10000; $i++) {\n    after();\n//}\n"
  },
  {
    "path": "tests/include/api/swoole_timer/fixRate_vs_fixDelay.php",
    "content": "<?php\n\nfunction fixRate(callable $callable, $interval)\n{\n    return Swoole\\Timer::tick($interval, $callable);\n}\n\nfunction fixDelay(callable $callable, $interval)\n{\n    return Swoole\\Timer::after($interval, function() use($callable, $interval) {\n        call_user_func($callable);\n        fixDelay($callable, $interval);\n    });\n}\n\nfunction randBlock()\n{\n    $n = mt_rand(0, 10);\n    for ($i = 0; $i < 1000000 * $n; $i++) {}\n}\n\n/*\n$t = microtime(true);\nfixDelay(swoole_function() use(&$t) {\n    echo number_format(microtime(true) - $t, 3), PHP_EOL;\n    randBlock();\n    $t = microtime(true);\n}, 1000);\n//*/\n/*\n1.007\n1.005\n1.005\n1.004\n1.003\n1.004\n1.002\n1.006\n1.006\n1.005\n1.004\n1.002\n1.006\n1.004\n1.002\n*/\n\n\n/*\n$t = microtime(true);\nfixRate(swoole_function() use(&$t) {\n    echo number_format(microtime(true) - $t, 3), PHP_EOL;\n    randBlock();\n    $t = microtime(true);\n}, 1000);\n*/\n/*\n1.003\n0.759\n1.005\n0.538\n1.002\n1.003\n0.763\n1.005\n0.247\n1.004\n1.004\n0.270\n1.005\n0.199\n1.000\n0.335\n1.005\n1.006\n0.239\n1.004\n0.119\n*/\n"
  },
  {
    "path": "tests/include/api/swoole_timer/invalid_args.php",
    "content": "<?php\n\nrequire_once __DIR__ . \"/../../../include/bootstrap.php\";\n\n//swoole_function Swoole\\Timer::after($ms, $callback, $param = null) {}\n//swoole_function Swoole\\Timer::tick($ms, $callback) {}\n//swoole_function Swoole\\Timer::clear($timer_id) {}\n\nSwoole\\Timer::after(-1, function(){ });\nSwoole\\Timer::tick(-1, function() { });\nSwoole\\Timer::after(86400001, function(){ });\nSwoole\\Timer::tick(86400001, function() { });\nSwoole\\Timer::clear(-1);\n\nfor ($i = 0; $i < 1000; $i++) {\n    Swoole\\Timer::clear(Swoole\\Timer::after(1, function() {}));\n}\n\n//Swoole\\Timer::after(1, null);\n//Swoole\\Timer::after(1, \"strlen\");\n\nfunction sw_timer_pass_ref(&$ref_func) {\n    Swoole\\Timer::after(1, $ref_func);\n}\n$func = function() {};\nsw_timer_pass_ref($func);\n"
  },
  {
    "path": "tests/include/api/swoole_timer/multi_timer.php",
    "content": "<?php\n\nrequire_once __DIR__ . \"/../../../include/bootstrap.php\";\n\nfor ($j = 0; $j < 100; $j++) {\n    Swoole\\Timer::after(1, function() use($j){\n        echo $j, \"\\n\";\n    });\n}\n"
  },
  {
    "path": "tests/include/api/swoole_timer/register_shutdown_priority.php",
    "content": "<?php\n\n// first shutdown func\n// timer after\nfunction func1()\n{\n    register_shutdown_function(function() {\n        echo \"first shutdown func\\n\";\n    });\n\n//    $start = microtime(true);\n    Swoole\\Timer::after(1, function() /*use($start)*/ {\n        echo \"timer after\\n\";\n//        echo (microtime(true) - $start) * 1000 - 1;\n    });\n}\n\n// first shutdown func\n// timer after\nfunction func1_2()\n{\n    $order4 = function() { echo \"first shutdown func\\n\"; };\n    $order5 = function() { Swoole\\Event::wait(); };\n    $order6 = function() { echo \"timer after\\n\";};\n\n    // order 1\n    register_shutdown_function($order4);\n    // order 2\n    register_shutdown_function($order5);\n    // order 3\n    Swoole\\Timer::after(1, $order6);\n}\n\n\n// timer after\n// first shutdown func\nfunction func2()\n{\n    register_shutdown_function(function() {\n        echo \"first shutdown func\\n\";\n    });\n\n    Swoole\\Timer::after(1, function() {\n        echo \"timer after\\n\";\n    });\n\n    Swoole\\Event::wait();\n}\n\n\n// first shutdown func\n// timer after\n// second shutdown func\nfunction func3()\n{\n    register_shutdown_function(function() {\n        echo \"first shutdown func\\n\";\n    });\n\n    Swoole\\Timer::after(1, function() {\n        echo \"timer after\\n\";\n        register_shutdown_function(function() {\n            echo \"second shutdown func\\n\";\n        });\n    });\n\n}\n\n\nfunction recv_shutdow()\n{\n    register_shutdown_function(function() {\n        echo \"shutdown\\n\";\n        recv_shutdow();\n    });\n}\n\n\n\n//recv_shutdow();\n//func1();\nfunc1_2();\n//func2();\n//func3();\n\n\n"
  },
  {
    "path": "tests/include/api/swoole_websocket_server/send_large_request_data.php",
    "content": "<?php\n\nrequire_once __DIR__ . \"/../../../include/bootstrap.php\";\nrequire \"websocket_client.php\";\n\nfunction send_large_request_data($host, $port)\n{\n    $client = new WebSocketClient($host, $port);\n    if (!$client->connect())\n    {\n        echo \"send failed, errCode={$client->errCode}\\n\";\n        return false;\n    }\n\n    $data = str_repeat(\"data\", 40000);\n    for ($i = 0; $i < 100; $i++)\n    {\n        if (!$client->send($data))\n        {\n            echo \"send failed, errCode={$client->errCode}\\n\";\n            return false;\n        }\n        $response = $client->recv();\n        Assert::same($response, \"SUCCESS\", \"response failed\");\n    }\n    return true;\n}\n"
  },
  {
    "path": "tests/include/api/swoole_websocket_server/send_small_request_data.php",
    "content": "<?php\n\nrequire_once __DIR__ . \"/../../../include/bootstrap.php\";\nrequire \"websocket_client.php\";\n\nfunction send_small_request_data($host, $port)\n{\n    $client = new WebSocketClient($host, $port);\n    $client->connect();\n\n    $data = \"\";\n    for ($i = 0; $i < 100; $i++)\n    {\n        $client->send($data);\n        $response = $client->recv();\n        Assert::same($response, \"SUCCESS\", \"response failed\");\n    }\n}"
  },
  {
    "path": "tests/include/api/swoole_websocket_server/swoole_websocket_server.php",
    "content": "<?php\n\nrequire_once __DIR__ . \"/../../../include/bootstrap.php\";\n\nclass WebSocketServer\n{\n    /**\n     * @var Swoole\\WebSocket\\Server\n     */\n    public $webSocketServ;\n\n    public function __construct($host = WEBSOCKET_SERVER_HOST, $port = WEBSOCKET_SERVER_PORT)\n    {\n        $this->webSocketServ = new Swoole\\WebSocket\\Server($host, $port, SWOOLE_PROCESS);\n\n        $this->webSocketServ->set([\n            // 输出限制\n            \"output_buffer_size\" => 1024 * 1024 * 1024,\n\n            \"max_connection\" => 10240,\n            \"pipe_buffer_size\" => 1024 * 1024 * 1024,\n\n            // 'enable_port_reuse' => true,\n            'user' => 'www-data',\n            'group' => 'www-data',\n\n            // 'log_file' => __DIR__.'/swoole.log',\n            'open_tcp_nodelay' => 1,\n            'open_cpu_affinity' => 1,\n            'daemonize' => 0,\n            'reactor_num' => 1,\n            'worker_num' => 2,\n            'max_request' => 100000,\n        ]);\n\n    }\n\n    public function start()\n    {\n        $this->webSocketServ->on('start', [$this, 'onStart']);\n        $this->webSocketServ->on('shutdown', [$this, 'onShutdown']);\n\n        $this->webSocketServ->on('workerStart', [$this, 'onWorkerStart']);\n        $this->webSocketServ->on('workerStop', [$this, 'onWorkerStop']);\n        $this->webSocketServ->on('workerError', [$this, 'onWorkerError']);\n\n        $this->webSocketServ->on('connect', [$this, 'onConnect']);\n        $this->webSocketServ->on('request', [$this, 'onRequest']);\n\n        $this->webSocketServ->on('open', [$this, 'onOpen']);\n        $this->webSocketServ->on('message', [$this, 'onMessage']);\n\n        $this->webSocketServ->on('close', [$this, 'onClose']);\n\n        $this->webSocketServ->start();\n    }\n\n    public function onConnect()\n    {\n        debug_log(\"connecting ......\");\n    }\n\n    public function onClose()\n    {\n        debug_log(\"closing .....\");\n    }\n\n    public function onStart(Swoole\\WebSocket\\Server $swooleServer)\n    {\n        debug_log(\"swoole_server starting .....\");\n    }\n\n    public function onShutdown(Swoole\\WebSocket\\Server $swooleServer)\n    {\n        debug_log(\"swoole_server shutdown .....\");\n    }\n\n    public function onWorkerStart(Swoole\\WebSocket\\Server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId starting .....\");\n    }\n\n    public function onWorkerStop(Swoole\\WebSocket\\Server $swooleServer, $workerId)\n    {\n        debug_log(\"worker #$workerId stopping ....\");\n    }\n\n    public function onWorkerError(Swoole\\WebSocket\\Server $swooleServer, $workerId, $workerPid, $exitCode, $sigNo)\n    {\n        debug_log(\"worker error happening [workerId=$workerId, workerPid=$workerPid, exitCode=$exitCode, signalNo=$sigNo]...\");\n    }\n\n    public function onRequest(\\Swoole\\Http\\Request $request, \\Swoole\\Http\\Response $response)\n    {\n        $response->end(\"Hello World!\");\n    }\n\n    public function onOpen(Swoole\\WebSocket\\Server  $server, $request)\n    {\n        debug_log(\"{$request->fd} opened\");\n    }\n\n    public function onMessage(Swoole\\WebSocket\\Server  $server, $frame)\n    {\n        $server->push($frame->fd, \"SUCCESS\");\n    }\n}\n\n$host = isset($argv[1]) ? $argv[1] : WEBSOCKET_SERVER_HOST;\n$port = isset($argv[2]) ? $argv[2] : WEBSOCKET_SERVER_PORT;\n\n$wsServer = new WebSocketServer($host, $port);\n$wsServer->start();\n"
  },
  {
    "path": "tests/include/api/swoole_websocket_server/websocket_client.php",
    "content": "<?php\n\nclass WebSocketClient\n{\n    const VERSION = '0.1.4';\n    const TOKEN_LENGHT = 16;\n    const TYPE_ID_WELCOME = 0;\n    const TYPE_ID_PREFIX = 1;\n    const TYPE_ID_CALL = 2;\n    const TYPE_ID_CALLRESULT = 3;\n    const TYPE_ID_ERROR = 4;\n    const TYPE_ID_SUBSCRIBE = 5;\n    const TYPE_ID_UNSUBSCRIBE = 6;\n    const TYPE_ID_PUBLISH = 7;\n    const TYPE_ID_EVENT = 8;\n    private $key;\n    private $host;\n    private $port;\n    private $path;\n    /**\n     * @var swoole_client\n     */\n    private $socket;\n    private $buffer = '';\n    private $origin = null;\n    /**\n     * @var bool\n     */\n    private $connected = false;\n\n    /**\n     * @param string $host\n     * @param int    $port\n     * @param string $path\n     */\n    function __construct($host = '127.0.0.1', $port = 8080, $path = '/', $origin = null)\n    {\n        $this->host = $host;\n        $this->port = $port;\n        $this->path = $path;\n        $this->origin = $origin;\n        $this->key = $this->generateToken(self::TOKEN_LENGHT);\n    }\n\n    /**\n     * Disconnect on destruct\n     */\n    function __destruct()\n    {\n        $this->disconnect();\n    }\n\n    /**\n     * Connect client to swoole_server\n     *\n     * @return $this\n     */\n    public function connect()\n    {\n        $this->socket = new Swoole\\Client(SWOOLE_SOCK_TCP);\n        if (!$this->socket->connect($this->host, $this->port))\n        {\n            return false;\n        }\n        $this->socket->send($this->createHeader());\n        return $this->recv();\n    }\n\n    public function getSocket()\n    {\n        return $this->socket;\n    }\n\n    /**\n     * Disconnect from swoole_server\n     */\n    public function disconnect()\n    {\n        $this->connected = false;\n        $this->socket->close();\n    }\n\n    public function recv()\n    {\n        $data = $this->socket->recv();\n        if ($data === false)\n        {\n            echo \"Error: {$this->socket->errMsg}\";\n            return false;\n        }\n        $this->buffer .= $data;\n        $recv_data = $this->parseData($this->buffer);\n        if ($recv_data)\n        {\n            $this->buffer = '';\n            return $recv_data;\n        }\n        else\n        {\n            return false;\n        }\n    }\n\n    /**\n     * @param        $data\n     * @param string $type\n     * @param bool   $masked\n     */\n    public function send($data, $type = 'text', $masked = true)\n    {\n        return $this->socket->send($this->hybi10Encode($data, $type, $masked));\n    }\n\n    /**\n     * Parse received data\n     *\n     * @param $response\n     */\n    private function parseData($response)\n    {\n        if (!$this->connected && isset($response['Sec-Websocket-Accept']))\n        {\n            if (base64_encode(pack('H*', sha1($this->key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')))\n                === $response['Sec-Websocket-Accept']\n            )\n            {\n                $this->connected = true;\n            }\n            else\n            {\n                throw new \\Exception(\"error response key.\");\n            }\n        }\n        return $this->hybi10Decode($response);\n    }\n\n    /**\n     * Create header for websocket client\n     *\n     * @return string\n     */\n    private function createHeader()\n    {\n        $host = $this->host;\n        if ($host === '127.0.0.1' || $host === '0.0.0.0')\n        {\n            $host = 'localhost';\n        }\n        return \"GET {$this->path} HTTP/1.1\" . \"\\r\\n\" .\n        \"Origin: {$this->origin}\" . \"\\r\\n\" .\n        \"Host: {$host}:{$this->port}\" . \"\\r\\n\" .\n        \"Sec-WebSocket-Key: {$this->key}\" . \"\\r\\n\" .\n        \"User-Agent: PHPWebSocketClient/\" . self::VERSION . \"\\r\\n\" .\n        \"Upgrade: websocket\" . \"\\r\\n\" .\n        \"Connection: Upgrade\" . \"\\r\\n\" .\n        \"Sec-WebSocket-Protocol: wamp\" . \"\\r\\n\" .\n        \"Sec-WebSocket-Version: 13\" . \"\\r\\n\" . \"\\r\\n\";\n    }\n\n    /**\n     * Parse raw incoming data\n     *\n     * @param $header\n     *\n     * @return array\n     */\n    private function parseIncomingRaw($header)\n    {\n        $retval = array();\n        $content = \"\";\n        $fields = explode(\"\\r\\n\", preg_replace('/\\x0D\\x0A[\\x09\\x20]+/', ' ', $header));\n        foreach ($fields as $field)\n        {\n            if (preg_match('/([^:]+): (.+)/m', $field, $match))\n            {\n                $match[1] = preg_replace_callback('/(?<=^|[\\x09\\x20\\x2D])./',\n                    function ($matches)\n                    {\n                        return strtoupper($matches[0]);\n                    },\n                    strtolower(trim($match[1])));\n                if (isset($retval[$match[1]]))\n                {\n                    $retval[$match[1]] = array($retval[$match[1]], $match[2]);\n                }\n                else\n                {\n                    $retval[$match[1]] = trim($match[2]);\n                }\n            }\n            else\n            {\n                if (preg_match('!HTTP/1\\.\\d (\\d)* .!', $field))\n                {\n                    $retval[\"status\"] = $field;\n                }\n                else\n                {\n                    $content .= $field . \"\\r\\n\";\n                }\n            }\n        }\n        $retval['content'] = $content;\n        return $retval;\n    }\n\n    /**\n     * Generate token\n     *\n     * @param int $length\n     *\n     * @return string\n     */\n    private function generateToken($length)\n    {\n        $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"§$%&/()=[]{}';\n        $useChars = array();\n        // select some random chars:\n        for ($i = 0; $i < $length; $i++)\n        {\n            $useChars[] = $characters[mt_rand(0, strlen($characters) - 1)];\n        }\n        // Add numbers\n        array_push($useChars, rand(0, 9), rand(0, 9), rand(0, 9));\n        shuffle($useChars);\n        $randomString = trim(implode('', $useChars));\n        $randomString = substr($randomString, 0, self::TOKEN_LENGHT);\n        return base64_encode($randomString);\n    }\n\n    /**\n     * Generate token\n     *\n     * @param int $length\n     *\n     * @return string\n     */\n    public function generateAlphaNumToken($length)\n    {\n        $characters = str_split('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789');\n        srand((float)microtime() * 1000000);\n        $token = '';\n        do\n        {\n            shuffle($characters);\n            $token .= $characters[mt_rand(0, (count($characters) - 1))];\n        } while (strlen($token) < $length);\n        return $token;\n    }\n\n    /**\n     * @param        $payload\n     * @param string $type\n     * @param bool   $masked\n     *\n     * @return bool|string\n     */\n    private function hybi10Encode($payload, $type = 'text', $masked = true)\n    {\n        $frameHead = array();\n        $frame = '';\n        $payloadLength = strlen($payload);\n        switch ($type)\n        {\n            case 'text':\n                // first byte indicates FIN, Text-Frame (10000001):\n                $frameHead[0] = 129;\n                break;\n            case 'close':\n                // first byte indicates FIN, Close Frame(10001000):\n                $frameHead[0] = 136;\n                break;\n            case 'ping':\n                // first byte indicates FIN, Ping frame (10001001):\n                $frameHead[0] = 137;\n                break;\n            case 'pong':\n                // first byte indicates FIN, Pong frame (10001010):\n                $frameHead[0] = 138;\n                break;\n        }\n        // set mask and payload length (using 1, 3 or 9 bytes)\n        if ($payloadLength > 65535)\n        {\n            $payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8);\n            $frameHead[1] = ($masked === true) ? 255 : 127;\n            for ($i = 0; $i < 8; $i++)\n            {\n                $frameHead[$i + 2] = bindec($payloadLengthBin[$i]);\n            }\n            // most significant bit MUST be 0 (close connection if frame too big)\n            if ($frameHead[2] > 127)\n            {\n                $this->close(1004);\n                return false;\n            }\n        }\n        elseif ($payloadLength > 125)\n        {\n            $payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8);\n            $frameHead[1] = ($masked === true) ? 254 : 126;\n            $frameHead[2] = bindec($payloadLengthBin[0]);\n            $frameHead[3] = bindec($payloadLengthBin[1]);\n        }\n        else\n        {\n            $frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength;\n        }\n        // convert frame-head to string:\n        foreach (array_keys($frameHead) as $i)\n        {\n            $frameHead[$i] = chr($frameHead[$i]);\n        }\n        if ($masked === true)\n        {\n            // generate a random mask:\n            $mask = array();\n            for ($i = 0; $i < 4; $i++)\n            {\n                $mask[$i] = chr(rand(0, 255));\n            }\n            $frameHead = array_merge($frameHead, $mask);\n        }\n        $frame = implode('', $frameHead);\n        // append payload to frame:\n        for ($i = 0; $i < $payloadLength; $i++)\n        {\n            $frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];\n        }\n        return $frame;\n    }\n\n    /**\n     * @param $data\n     *\n     * @return null|string\n     */\n    private function hybi10Decode($data)\n    {\n        if (empty($data))\n        {\n            return null;\n        }\n        $bytes = $data;\n        $dataLength = '';\n        $mask = '';\n        $coded_data = '';\n        $decodedData = '';\n        $secondByte = sprintf('%08b', ord($bytes[1]));\n        $masked = ($secondByte[0] == '1') ? true : false;\n        $dataLength = ($masked === true) ? ord($bytes[1]) & 127 : ord($bytes[1]);\n        if ($masked === true)\n        {\n            if ($dataLength === 126)\n            {\n                $mask = substr($bytes, 4, 4);\n                $coded_data = substr($bytes, 8);\n            }\n            elseif ($dataLength === 127)\n            {\n                $mask = substr($bytes, 10, 4);\n                $coded_data = substr($bytes, 14);\n            }\n            else\n            {\n                $mask = substr($bytes, 2, 4);\n                $coded_data = substr($bytes, 6);\n            }\n            for ($i = 0; $i < strlen($coded_data); $i++)\n            {\n                $decodedData .= $coded_data[$i] ^ $mask[$i % 4];\n            }\n        }\n        else\n        {\n            if ($dataLength === 126)\n            {\n                $decodedData = substr($bytes, 4);\n            }\n            elseif ($dataLength === 127)\n            {\n                $decodedData = substr($bytes, 10);\n            }\n            else\n            {\n                $decodedData = substr($bytes, 2);\n            }\n        }\n        return $decodedData;\n    }\n}\n"
  },
  {
    "path": "tests/include/api/syntax_error.txt",
    "content": "<?php\nxd xde sdf;\n"
  },
  {
    "path": "tests/include/api/tcp_server.php",
    "content": "<?php\n$serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n$serv->set([\n//            'log_file' => __DIR__ . '/simple_server.log',\n    'dispatch_mode' => 2,\n    'daemonize' => 0,\n    'worker_num' => 1,\n]);\n\n$serv->on('workerStart', function (Swoole\\Server $serv)\n{\n    /**\n     * @var $pm ProcessManager\n     */\n    global $pm;\n    $pm->wakeup();\n});\n\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data)\n{\n    if (trim($data) == 'shutdown')\n    {\n        $serv->shutdown();\n        return;\n    }\n    $recv_len = strlen($data);\n    $serv->send($fd, RandStr::gen($recv_len, RandStr::ALL));\n});\n\n$serv->start();\n"
  },
  {
    "path": "tests/include/api/test_classes/A.php",
    "content": "<?php\nclass SwooleTestClassA {\n\n}"
  },
  {
    "path": "tests/include/api/test_classes/A2.php",
    "content": "<?php\nnew SwooleTestClassA2();\n"
  },
  {
    "path": "tests/include/api/test_classes/B.php",
    "content": "<?php\nclass SwooleTestClassB {\n\n}"
  },
  {
    "path": "tests/include/bootstrap.php",
    "content": "<?php\n/**\n * This file is part of Swoole, for internal use only\n *\n * @link     https://www.swoole.com\n * @contact  team@swoole.com\n * @license  https://github.com/swoole/library/blob/master/LICENSE\n */\n\nrequire_once __DIR__ . '/config.php'; // (`once` because it may be required in skip when we run phpt)\nrequire  __DIR__ . '/../../tools/bootstrap.php';\n\n// PHP settings\nerror_reporting(E_ALL ^ E_DEPRECATED);\nini_set('memory_limit', '1024M');\nassert_options(ASSERT_ACTIVE, 1);\nassert_options(ASSERT_WARNING, 1);\nassert_options(ASSERT_BAIL, 0);\n\n// Swoole settings\nswoole_async_set([\n    'disable_dns_cache' => true,\n    'dns_lookup_random' => true,\n]);\n\n// Run default remote object server\nswoole_library_set_option('default_remote_object_server_worker_num', 2);\nswoole_init_default_remote_object_server();\n\nCo::set([\n    'socket_timeout' => 5\n]);\n\nif (empty(getenv('SWOOLE_DEBUG'))) {\n    Co::set([\n        'log_level' => SWOOLE_LOG_INFO,\n        'trace_flags' => 0,\n        'enable_deadlock_check' => false,\n    ]);\n}\n\n$traceFlags = getenv('SWOOLE_TRACE_FLAGS');\nif ($traceFlags) {\n    $_traceFlags = 0;\n    if (is_numeric($traceFlags)) {\n        $_traceFlags = intval($traceFlags);\n    } else {\n        eval('$_traceFlags = ' . $traceFlags . ';');\n    }\n    Co::set([\n        'log_level' => 0,\n        'trace_flags' => $_traceFlags\n    ]);\n}\n\n// Components\nrequire __DIR__ . '/lib/vendor/autoload.php';\n\nclass_alias(SwooleTest\\ProcessManager::class, ProcessManager::class);\nclass_alias(SwooleTest\\ServerManager::class, ServerManager::class);\nclass_alias(SwooleTest\\RandStr::class, RandStr::class);\nclass_alias(SwooleTest\\TcpStat::class, TcpStat::class);\n\nclass Assert extends SwooleTest\\Assert\n{\n    protected static $throwException = false;\n}\n"
  },
  {
    "path": "tests/include/config.php",
    "content": "<?php\n/**\n * This file is part of Swoole.\n *\n * @link     https://www.swoole.com\n * @contact  team@swoole.com\n * @license  https://github.com/swoole/library/blob/master/LICENSE\n */\n\ndeclare(strict_types=1);\n\nuse Swoole\\NameResolver\\Consul;\nuse Swoole\\NameResolver\\Redis;\n\n/**\n * This file is part of Swoole, for internal use only\n *\n * @see     https://www.swoole.com/\n * @contact  team@swoole.com\n * @license  https://github.com/swoole/library/blob/master/LICENSE\n */\n\nrequire_once __DIR__ . '/functions.php';\n\n/* ============== Env =============== */\ndefine('IS_MAC_OS', stripos(PHP_OS, 'Darwin') !== false);\ndefine('IS_IN_CI', (bool) getenv('GITHUB_ACTIONS') or file_exists('/.cienv'));\ndefine('IS_PHPTESTSING', (bool) getenv('PHPT'));\ndefine('USE_VALGRIND', getenv('USE_ZEND_ALLOC') === '0');\ndefine('HAS_SSL', defined('SWOOLE_SSL'));\ndefine('HAS_HTTP2', class_exists('Swoole\\Http2\\Request', false));\ndefine('DEV_NULL', '/dev/null');\n\n/* ============== Files ============== */\ndefine('SOURCE_ROOT_PATH', realpath(__DIR__ . '/../../'));\ndefine('TESTS_ROOT_PATH', realpath(__DIR__ . '/../'));\ndefine('TESTS_API_PATH', TESTS_ROOT_PATH . '/include/api');\ndefine('TESTS_LIB_PATH', TESTS_ROOT_PATH . '/include/lib');\ndefine('TRAVIS_DIR_PATH', __DIR__ . '/../../travis/');\ndefine('TEST_IMAGE', __DIR__ . '/../../examples/test.jpg');\ndefine('TEST_LINK_IMAGE', __DIR__ . '/../../examples/test_link.jpg');\ndefine('TEST_IMAGE2', __DIR__ . '/../../docs/swoole-logo.svg');\ndefine('TEST_LOG_FILE', '/tmp/swoole.log');\ndefine('TEST_PID_FILE', '/tmp/swoole.pid');\ndefine('SSL_FILE_DIR', __DIR__ . '/ssl_certs/');\ndefine('DOCUMENT_ROOT', __DIR__ . '/../../examples/www');\ndefine('TEST_USER_AGENT', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36');\n\n/* ============ Socket ============ */\nif (IS_MAC_OS) {\n    define('DGRAM_MAX_SIZE', 4096);\n} else {\n    define('DGRAM_MAX_SIZE', 8192);\n}\n\n/* ============ Servers ============ */\ndefine('SERVER_MODE_RANDOM', array_random([SWOOLE_BASE, SWOOLE_PROCESS]));\ndefine('UNIXSOCK_PATH', '/tmp/unix-sock-test.sock');\n\ndefine('TCP_SERVER_HOST', '127.0.0.1');\ndefine('TCP_SERVER_PORT', 9001);\n\ndefine('HTTP_SERVER_HOST', '127.0.0.1');\ndefine('HTTP_SERVER_PORT', 9002);\ndefine('WEBSOCKET_SERVER_HOST', '127.0.0.1');\ndefine('WEBSOCKET_SERVER_PORT', 9003);\n\ndefine('UDP_SERVER_HOST', '127.0.0.1');\ndefine('UDP_SERVER_PORT', 9003);\n\n/* ============== MySQL ============== */\ndefine('MYSQL_SERVER_PATH', getenv('MYSQL_SERVER_PATH')\n    ?: (IS_IN_CI ? TRAVIS_DIR_PATH . '/data/run/mysqld/mysqld.sock'\n        : (IS_MAC_OS ? '/tmp/mysql.sock' : '/var/run/mysqld/mysqld.sock')));\ndefine('MYSQL_SERVER_HOST', getenv('MYSQL_SERVER_HOST') ?: (IS_IN_CI ? 'mysql' : '127.0.0.1'));\ndefine('MYSQL_SERVER_PORT', (int) (getenv('MYSQL_SERVER_PORT') ?: 3306));\ndefine('MYSQL_SERVER_USER', getenv('MYSQL_SERVER_USER') ?: 'root');\ndefine('MYSQL_SERVER_PWD', getenv('MYSQL_SERVER_PWD') ?: 'root');\ndefine('MYSQL_SERVER_DB', getenv('MYSQL_SERVER_DB') ?: 'test');\n\n/* ============== PostgreSQL ============== */\nif (IS_IN_CI) {\n    define('PGSQL_HOST', 'pgsql');\n    define('PGSQL_USER', 'root');\n    define('PGSQL_PASSWORD', 'root');\n    define('PGSQL_DBNAME', 'test');\n} else {\n    define('PGSQL_HOST', '127.0.0.1');\n    define('PGSQL_USER', 'postgres');\n    define('PGSQL_PASSWORD', 'postgres');\n    define('PGSQL_DBNAME', 'postgres');\n}\ndefine('PGSQL_PORT', '5432');\n\ndefine('PGSQL_CONNECTION_STRING', getenv('PGSQL_CONNECTION_STRING')\n    ?: ('host=' . PGSQL_HOST . ' port=' . PGSQL_PORT . ' dbname=' . PGSQL_DBNAME . ' user=' . PGSQL_USER . ' password=' . PGSQL_PASSWORD));\n\n/* ============== Oracle ============== */\ndefine('ORACLE_PORT', '1521');\ndefine('ORACLE_SERVICE_NAME', 'freepdb1');\ndefine('ORACLE_USER', 'system');\ndefine('ORACLE_PASSWORD', 'oracle');\nif (IS_IN_CI) {\n    define('ORACLE_TNS', 'oci:dbname=oracle:' . ORACLE_PORT . '/' . ORACLE_SERVICE_NAME . ';charset=AL32UTF8');\n} else {\n    define('ORACLE_TNS', 'oci:dbname=127.0.0.1:' . ORACLE_PORT . '/' . ORACLE_SERVICE_NAME . ';charset=AL32UTF8');\n}\n\n/* ============== Firebird ============== */\nif (IS_IN_CI) {\n    define('FIREBIRD_HOST', 'firebirdsql');\n} else {\n    define('FIREBIRD_HOST', '127.0.0.1');\n}\n\ndefine('FIREBIRD_USER', 'test');\ndefine('FIREBIRD_PASSWORD', 'test');\ndefine('FIREBIRD_DBNAME', '/var/lib/firebird/data/test.fdb');\ndefine('FIREBIRD_PORT', '3050');\ndefine('FIREBIRD_DSN', 'firebird:dbname=' . FIREBIRD_HOST . '/' . FIREBIRD_PORT . ':' . FIREBIRD_DBNAME);\n\n/* ============== Sqlite ============== */\ndefine('SQLITE_DSN', 'sqlite::memory:');\n\n/* ============== Redis ============== */\ndefine('REDIS_SERVER_PATH', getenv('REDIS_SERVER_PATH')\n    ?: (IS_IN_CI ? TRAVIS_DIR_PATH . '/data/run/redis/redis.sock'\n        : (IS_MAC_OS ? '/tmp/redis.sock' : '/var/run/redis/redis-server.sock')));\ndefine('REDIS_SERVER_HOST', getenv('REDIS_SERVER_HOST') ?: (IS_IN_CI && !IS_MAC_OS ? 'redis' : '127.0.0.1'));\ndefine('REDIS_SERVER_PORT', (int) (getenv('REDIS_SERVER_PORT') ?: 6379));\ndefine('REDIS_SERVER_PWD', getenv('REDIS_SERVER_PWD') ?: 'root');\ndefine('REDIS_SERVER_DB', (int) (getenv('REDIS_SERVER_DB') ?: 0));\n\nif (getenv('SWOOLE_TEST_IN_DOCKER')) {\n    if (!empty($info = shell_exec('docker ps 2>&1 | grep httpbin 2>&1'))\n        && preg_match('/\\s+?[^:]+:(\\d+)->\\d+\\/tcp\\s+/', $info, $matches)\n        && is_numeric($matches[1])) {\n        define('HTTPBIN_SERVER_PORT_IN_DOCKER', (int) $matches[1]);\n    }\n}\n\n/* ============== ODBC ============== */\nif (IS_IN_CI) {\n    define('ODBC_DSN', 'odbc:mysql-test');\n} else {\n    define('ODBC_DSN', 'odbc:mysql-test');\n}\n\ndefine('SWOOLE_TEST_ECHO', empty(getenv('SWOOLE_TEST_NO_ECHO')));\n\n/* ============== Http ============== */\nif (IS_IN_CI && !IS_MAC_OS) {\n    define('HTTPBIN_SERVER_HOST', 'httpbin');\n    define('HTTPBIN_SERVER_PORT', 80);\n    define('HTTPBIN_LOCALLY', true);\n} elseif (defined('HTTPBIN_SERVER_PORT_IN_DOCKER')) {\n    define('HTTPBIN_SERVER_HOST', '127.0.0.1');\n    define('HTTPBIN_SERVER_PORT', HTTPBIN_SERVER_PORT_IN_DOCKER);\n    define('HTTPBIN_LOCALLY', true);\n} elseif (getenv('HTTPBIN_SERVER_HOST')) {\n    define('HTTPBIN_SERVER_HOST', getenv('HTTPBIN_SERVER_HOST'));\n    define('HTTPBIN_SERVER_PORT', (int) getenv('HTTPBIN_SERVER_PORT'));\n    define('HTTPBIN_LOCALLY', true);\n} else {\n    define('HTTPBIN_SERVER_HOST', 'httpbin.org');\n    define('HTTPBIN_SERVER_PORT', 80);\n}\n\nif (IS_IN_CI) {\n    define('TEST_HTTP2_SERVERPUSH_URL', 'https://golang-h2demo:4430/serverpush');\n    define('TEST_NAME_RESOLVER', [\n        'class' => Redis::class,\n        'server_url' => 'tcp://' . REDIS_SERVER_HOST . ':' . REDIS_SERVER_PORT,\n    ]);\n} else {\n    define('TEST_HTTP2_SERVERPUSH_URL', 'https://127.0.0.1:4430/serverpush');\n    define('TEST_NAME_RESOLVER', [\n        'class' => Consul::class,\n        'server_url' => 'http://127.0.0.1:8500',\n    ]);\n}\n\nif (IS_IN_CI) {\n    define('TEST_DOMAIN_1', 'www.google.com');\n    define('TEST_DOMAIN_2', 'www.yahoo.com');\n} else {\n    define('TEST_DOMAIN_1', 'www.baidu.com');\n    define('TEST_DOMAIN_2', 'www.qq.com');\n}\n\ndefine('TEST_DOMAIN_3', 'www.gov.cn');\n\ndefine('TEST_MAX_CPU_EXEC_DURATION', 12); // msec\n\n/* =============== IP ================ */\ndefine('IP_REGEX', '/^(?:[\\d]{1,3}\\.){3}[\\d]{1,3}$/');\n\n/* ============= Proxy ============== */\ndefine('HTTP_PROXY_HOST', IS_IN_CI ? 'tinyproxy' : '127.0.0.1');\ndefine('HTTP_PROXY_PORT', IS_IN_CI ? 8888 : 1080);\ndefine('SOCKS5_PROXY_HOST', IS_IN_CI ? 'socks5' : '127.0.0.1');\ndefine('SOCKS5_PROXY_PORT', 1080);\n\n/* ============== Pressure ============== */\ndefine('PRESSURE_LOW', 1);\ndefine('PRESSURE_MID', 2);\ndefine('PRESSURE_NORMAL', 3);\n\nif (IS_MAC_OS) {\n    define('PRESSURE_LEVEL', 1);\n} else {\n    define(\n        'PRESSURE_LEVEL',\n        USE_VALGRIND ? (IS_IN_CI ? PRESSURE_LOW - 1 : PRESSURE_LOW) : ((IS_IN_CI || swoole_cpu_num() === 1) ? PRESSURE_MID : PRESSURE_NORMAL)\n    );\n}\n\n\n/* ============== Time ============== */\ndefine('SERVER_PREHEATING_TIME', 0.1);\ndefine('REQUESTS_WAIT_TIME', [0.005, 0.005, 0.05, 0.1][PRESSURE_LEVEL]);\n\n/* ============== Times ============== */\ndefine('MAX_CONCURRENCY', [16, 32, 64, 256][PRESSURE_LEVEL]);\ndefine('MAX_CONCURRENCY_MID', [8, 16, 32, 128][PRESSURE_LEVEL]);\ndefine('MAX_CONCURRENCY_LOW', [4, 8, 16, 64][PRESSURE_LEVEL]);\ndefine('MAX_REQUESTS', [12, 24, 50, 100][PRESSURE_LEVEL]);\ndefine('MAX_REQUESTS_MID', [8, 16, 32, 64][PRESSURE_LEVEL]);\ndefine('MAX_REQUESTS_LOW', [4, 8, 10, 25][PRESSURE_LEVEL]);\ndefine('MAX_LOOPS', [12, 24, 100, 1000][PRESSURE_LEVEL] * 1000);\ndefine('MAX_PROCESS_NUM', [2, 4, 6, 8][PRESSURE_LEVEL]);\ndefine('MAX_PACKET_NUM', [1024, 2048, 4096, 10000][PRESSURE_LEVEL]);\n\n/* ============== FTP ============== */\ndefine('FTP_HOST', IS_IN_CI ? 'ftp' : '127.0.0.1');\ndefine('FTP_PORT', 21);\ndefine('FTP_USER', 'admin');\ndefine('FTP_PASS', 'admin');\ndefine('FTP_TEST_FILE', IS_IN_CI ? 'test.txt' : 'work/test.txt');\n"
  },
  {
    "path": "tests/include/functions.php",
    "content": "<?php\n/**\n * This file is part of Swoole.\n *\n * @link     https://www.swoole.com\n * @contact  team@swoole.com\n * @license  https://github.com/swoole/library/blob/master/LICENSE\n */\n\ndeclare(strict_types=1);\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Coroutine\\Socket;\nuse Swoole\\Event;\nuse Swoole\\Http2\\Request;\nuse Swoole\\Process;\nuse Swoole\\Runtime;\nuse Swoole\\Thread;\nuse Swoole\\Timer;\n\n/**\n * This file is part of Swoole, for internal use only\n *\n * @see     https://www.swoole.com\n * @contact  team@swoole.com\n * @license  https://github.com/swoole/library/blob/master/LICENSE\n */\n\nrequire_once __DIR__ . '/config.php';\n\nfunction switch_process(): void\n{\n    usleep((USE_VALGRIND ? 100 : 25) * 1000);\n}\n\nfunction clear_php()\n{\n    shell_exec(\"ps -A | grep php | grep -v phpstorm | grep -v 'run-tests' | awk '{print $1}' | xargs kill -9 > /dev/null 2>&1\");\n}\n\nfunction puts($msg)\n{\n    echo $msg . \"\\n\";\n}\n\nfunction top(int $pid)\n{\n    static $available;\n    $available = $available ?? !(IS_MAC_OS || empty(shell_exec('top help 2>&1 | grep -i usage')));\n    if (!$available) {\n        return false;\n    }\n    while (true) {\n        $top = @shell_exec(\"top -b -n 1 -p {$pid}\");\n        if (empty($top)) {\n            trigger_error(\"top {$pid} failed: \" . swoole_strerror(swoole_errno()), E_USER_WARNING);\n            return false;\n        }\n        break;\n    }\n    $top = explode(\"\\n\", $top);\n    $top = array_combine(preg_split('/\\s+/', trim($top[6])), preg_split('/\\s+/', trim($top[7])));\n    return $top;\n}\n\nfunction is_busybox_ps(): bool\n{\n    static $bool;\n    $bool = $bool ?? !empty(shell_exec('ps --help 2>&1 | grep -i busybox'));\n    return $bool;\n}\n\nfunction kill_process_by_name(string $name)\n{\n    shell_exec('ps aux | grep \"' . $name . '\" | grep -v grep | awk \\'{ print $' . (is_busybox_ps() ? '1' : '2') . '}\\' | xargs kill');\n}\n\nfunction get_process_pid_by_name(string $name): int\n{\n    return (int) shell_exec('ps aux | grep \"' . $name . '\" | grep -v grep | awk \\'{ print $' . (is_busybox_ps() ? '1' : '2') . '}\\'');\n}\n\nfunction is_musl_libc(): bool\n{\n    static $bool;\n    $bool = $bool ?? !empty(shell_exec('ldd 2>&1 | grep -i musl'));\n    return $bool;\n}\n\nfunction get_one_free_port(): int\n{\n    /**\n     * The Swoole coroutine socket delays releasing file descriptors (fd),\n     * which prevents ports from being released immediately.\n     * Therefore, it is essential to disable runtime hooks.\n     */\n    $flags = Runtime::getHookFlags();\n    Runtime::enableCoroutine(0);\n    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or exit('Unable to create socket: ' . socket_strerror(socket_last_error()) . PHP_EOL);\n    socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1) or exit('Unable to set socket option: ' . socket_strerror(socket_last_error()) . PHP_EOL);\n    socket_set_option($socket, SOL_SOCKET, SO_REUSEPORT, 1) or exit('Unable to set socket option: ' . socket_strerror(socket_last_error()) . PHP_EOL);\n    socket_bind($socket, '127.0.0.1', 0) or exit('Unable to bind socket: ' . socket_strerror(socket_last_error()) . PHP_EOL);\n    socket_getsockname($socket, $addr, $port);\n    socket_close($socket);\n    Runtime::enableCoroutine($flags);\n    return $port;\n}\n\nfunction get_constant_port(string $str, int $base = 9500): int\n{\n    return $base + crc32($str) % 10000;\n}\n\nfunction get_one_free_port_ipv6(): int\n{\n    $hookFlags = Runtime::getHookFlags();\n    Runtime::enableCoroutine(0);\n    $server = @stream_socket_server('tcp://[::1]:0');\n    if (!$server) {\n        $port = -1;\n    } else {\n        $name = stream_socket_get_name($server, false);\n        if (empty($name)) {\n            $port = -1;\n        } else {\n            $port = explode(']:', $name)[1];\n        }\n    }\n\n    Runtime::enableCoroutine($hookFlags);\n    return $port;\n}\n\nfunction set_socket_coro_buffer_size(Socket $cosocket, int $size)\n{\n    $cosocket->setOption(SOL_SOCKET, SO_SNDBUF, $size);\n    $cosocket->setOption(SOL_SOCKET, SO_RCVBUF, $size);\n}\n\nfunction approximate($expect, $actual, float $ratio = 0.1): bool\n{\n    $ret = $actual * (1 - $ratio) < $expect && $actual * (1 + $ratio) > $expect;\n    if (!$ret) {\n        trigger_error(\"approximate: expect {$expect}, but got {$actual}\\n\", E_USER_WARNING);\n    }\n    return $ret;\n}\n\nfunction time_approximate($expect, $actual, float $ratio = 0.1)\n{\n    return USE_VALGRIND || approximate($expect, $actual, $ratio);\n}\n\nfunction ms_random(float $a, float $b): float\n{\n    return mt_rand(intval($a * 1000), intval($b * 1000)) / 1000;\n}\n\nfunction string_pop_front(string &$s, int $length): string\n{\n    $r = substr($s, 0, $length);\n    $s = substr($s, $length);\n    return $r;\n}\n\nfunction array_random(array $array)\n{\n    return $array[mt_rand(0, count($array) - 1)];\n}\n\nfunction phpt_echo(...$args)\n{\n    if (!SWOOLE_TEST_ECHO) {\n        return;\n    }\n    global $argv;\n    if (substr($argv[0], -5) === '.phpt') {\n        foreach ($args as $arg) {\n            if (!is_string($arg)) {\n                var_export($arg);\n                echo PHP_EOL;\n            } else {\n                echo $arg;\n            }\n        }\n    }\n}\n\nfunction phpt_var_dump(...$args)\n{\n    global $argv;\n    if (str_ends_with($argv[0], '.phpt')) {\n        var_dump(...$args);\n    }\n}\n\nfunction phpt_show_usage()\n{\n    global $argv;\n    if (str_ends_with($argv[0], '.phpt')) {\n        var_dump('memory:' . memory_get_usage());\n        var_dump('coroutine:' . var_export(co::stats(), true));\n    }\n}\n\nfunction httpPost($url, $data)\n{\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, $url);\n    curl_setopt($ch, CURLOPT_HEADER, 0);\n    curl_setopt($ch, CURLOPT_POST, 1);\n    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Expect:']);\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n    $res = curl_exec($ch);\n    curl_close($ch);\n    return $res;\n}\n\nfunction httpRequest(string $uri, array $options = [])\n{\n    $url_info = parse_url($uri);\n    $scheme = $url_info['scheme'] ?? 'http';\n    $domain = $url_info['host'] ?? '127.0.0.1';\n    $path = $url_info['path'] ?? null ?: '/';\n    $query = $url_info['query'] ?? null ? \"?{$url_info['query']}\" : '';\n    $port = (int) ($url_info['port'] ?? null ?: 80);\n    $http2 = $options['http2'] ?? false;\n    $connect_args = [$domain, $port, $scheme === 'https' || $port === 443];\n    if ($http2) {\n        $cli = new Swoole\\Coroutine\\Http2\\Client(...$connect_args);\n        $request = new Request();\n    } else {\n        $cli = new Swoole\\Coroutine\\Http\\Client(...$connect_args);\n        $request = null;\n    }\n    $cli->set($options + ['timeout' => 5]);\n    if (isset($options['method'])) {\n        if ($http2) {\n            $request->method = $options['method'];\n        } else {\n            $cli->setMethod($options['method']);\n        }\n    }\n    if (isset($options['headers'])) {\n        if ($http2) {\n            $request->headers = $options['headers'];\n        } else {\n            $cli->setHeaders($options['headers']);\n        }\n    }\n    if (isset($options['data'])) {\n        if ($http2) {\n            $request->data = $options['data'];\n        } else {\n            $cli->setData($options['data']);\n        }\n    }\n    if (is_array($options['download'] ?? null)) {\n        if ($http2) {\n            throw new RuntimeException('HTTP2 not support download');\n        }\n        $cli->download(...array_values($options['download']));\n        return $cli;\n    }\n    if ($http2) {\n        if (!$cli->connect()) {\n            throw new RuntimeException(\"HTTP2 connect {$domain}:{$port} failed: {$cli->errMsg}\");\n        }\n        $request->path = \"{$path}{$query}\";\n        if (!$cli->send($request)) {\n            throw new RuntimeException(\"HTTP2 send request to {$uri} failed: {$cli->errMsg}\");\n        }\n        if (!($response = $cli->recv())) {\n            throw new RuntimeException(\"HTTP2 recv from {$uri} failed: {$cli->errMsg}\");\n        }\n        return [\n            'statusCode' => $response->statusCode,\n            'headers' => $response->headers,\n            'set_cookie_headers' => $response->set_cookie_headers,\n            'body' => $response->data,\n        ];\n    }\n    $redirect_times = $options['redirect'] ?? 3;\n    while (true) {\n        if (!$cli->execute($path . $query)) {\n            throw new RuntimeException(\"HTTP execute {$uri} failed: {$cli->errMsg}\");\n        }\n        if ($redirect_times-- && ($cli->headers['location'] ?? null) && $cli->headers['location'][0] === '/') {\n            $path = $cli->headers['location'];\n            $query = '';\n            continue;\n        }\n        break;\n    }\n    return [\n        'statusCode' => $cli->statusCode,\n        'headers' => $cli->headers,\n        'set_cookie_headers' => $cli->set_cookie_headers,\n        'body' => $cli->body,\n    ];\n}\n\nfunction httpGetStatusCode(string $uri, array $options = [])\n{\n    return httpRequest($uri, $options)['statusCode'];\n}\n\nfunction httpGetHeaders(string $uri, array $options = [])\n{\n    return httpRequest($uri, $options)['headers'];\n}\n\nfunction httpGetBody(string $uri, array $options = [])\n{\n    return httpRequest($uri, $options)['body'];\n}\n\nfunction content_hook_replace(string $content, array $kv_map): string\n{\n    foreach ($kv_map as $key => $val) {\n        $content = str_replace(\"{{{$key}}}\", (string) $val, $content);\n    }\n    return $content;\n}\n\nfunction tcp_length_types(): array\n{\n    return [\n        'c' => 1,\n        'C' => 1,\n        's' => 2,\n        'S' => 2,\n        'n' => 2,\n        'v' => 2,\n        'l' => 4,\n        'L' => 4,\n        'N' => 4,\n        'V' => 4,\n    ];\n}\n\nfunction tcp_type_length(string $type = 'n'): int\n{\n    $map = tcp_length_types();\n    if (strlen($type) === 1) {\n        return $map[$type] ?? 0;\n    }\n    $len = 0;\n    for ($n = 0; $n < strlen($type); $n++) {\n        $len += $map[$type[$n]] ?? 0;\n    }\n    return $len;\n}\n\nfunction tcp_head(int $length, string $type = 'n'): string\n{\n    return pack($type, $length);\n}\n\nfunction tcp_pack(string $data, string $type = 'n'): string\n{\n    return pack($type, strlen($data)) . $data;\n}\n\nfunction tcp_length(string $head, string $type = 'n'): int\n{\n    return unpack($type, $head)[1];\n}\n\nfunction tcp_unpack(string $data, string $type = 'n'): string\n{\n    $type_length = tcp_type_length($type);\n    return substr($data, $type_length, unpack($type, substr($data, 0, $type_length))[1]);\n}\n\nfunction var_dump_return(...$data): string\n{\n    ob_start();\n    foreach ($data as $d) {\n        var_dump($d);\n    }\n    return ob_get_clean();\n}\n\nfunction get_safe_random(int $length = 32, $original = false): string\n{\n    $raw = base64_encode(RandStr::getBytes($original ? $length : $length * 2));\n    if (!$original) {\n        $raw = substr(str_replace(['/', '+', '='], '', $raw), 0, $length);\n    }\n    return $raw;\n}\n\nfunction get_big_random(int $length = 1024 * 1024)\n{\n    if ($length < 1024 * 1024 || $length % 1024 !== 0) {\n        throw new InvalidArgumentException('Invalid length ' . $length);\n    }\n    return str_repeat(get_safe_random(1024), $length / 1024);\n}\n\nfunction makeCoTcpClient($host, $port, ?callable $onConnect = null, ?callable $onReceive = null)\n{\n    go(function () use ($host, $port, $onConnect, $onReceive) {\n        $cli = new Client(SWOOLE_SOCK_TCP);\n        assert($cli->set([\n            'open_length_check' => 1,\n            'package_length_type' => 'N',\n            'package_length_offset' => 0,\n            'package_body_offset' => 0,\n        ]));\n        $r = $cli->connect($host, $port, 1);\n        Assert::assert($r);\n\n        if ($onConnect) {\n            $onConnect($cli);\n        }\n        $recv = $cli->recv();\n        if ($onReceive) {\n            $onReceive($cli, $recv);\n        }\n    });\n}\n\nfunction opcode_encode($op, $data)\n{\n    $r = json_encode([$op, $data]);\n    Assert::same(json_last_error(), JSON_ERROR_NONE);\n    return pack('N', strlen($r) + 4) . $r;\n}\n\nfunction opcode_decode($raw)\n{\n    $json = substr($raw, 4);\n    $r = json_decode($json, true);\n    Assert::same(json_last_error(), JSON_ERROR_NONE);\n    assert(is_array($r) && count($r) === 2);\n    return $r;\n}\n\nfunction kill_self_and_descendant($pid)\n{\n    if (PHP_OS === 'Darwin') {\n        return;\n    }\n    $pids = findDescendantPids($pid);\n    foreach ($pids as $pid) {\n        posix_kill($pid, SIGKILL);\n    }\n    posix_kill($pid, SIGKILL);\n}\n\n/**\n * fork 一个进程把父进程pid通过消息队列传给子进程，延时把父进程干掉\n */\nfunction killself_in_syncmode(int $lifetime = 1000, int $sig = SIGKILL): void\n{\n    $proc = new Process(function (Process $proc) use ($lifetime, $sig) {\n        $pid = $proc->pop();\n        $proc->freeQueue();\n        usleep($lifetime * 1000);\n        Process::kill((int) $pid, $sig);\n        $proc->exit();\n    }, true);\n    $proc->useQueue();\n    $proc->push((string) posix_getpid());\n    $proc->start();\n}\n\n/**\n * 异步模式用定时器干掉自己\n * @param int $lifetime\n * @param int $sig\n * @return mixed\n */\nfunction suicide($lifetime, $sig = SIGKILL, ?callable $cb = null)\n{\n    return Timer::after($lifetime, function () use ($lifetime, $sig, $cb) {\n        if ($cb) {\n            $cb();\n        }\n        echo \"suicide after {$lifetime} ms\\n\";\n        posix_kill(posix_getpid(), $sig);\n    });\n}\n\n// 查找某pid的所有子孙pid\nfunction findDescendantPids($pid)\n{\n    [$pinfo] = pstree();\n    $y = function ($pid) use (&$y, $pinfo) {\n        if (isset($pinfo[$pid])) {\n            [, $childs] = $pinfo[$pid];\n            $pids = $childs;\n            foreach ($childs as $child) {\n                $pids = array_merge($pids, $y($child));\n            }\n            return $pids;\n        }\n        return [];\n    };\n    return $y($pid);\n}\n\n/**\n * @return array [pinfo, tree]\n *               tree [\n *               ppid\n *               [...child pids]\n *               ]\n *               list(ppid, array childs) = tree[pid]\n */\nfunction pstree()\n{\n    $pinfo = [];\n    $iter = new DirectoryIterator('/proc');\n    foreach ($iter as $item) {\n        $pid = $item->getFilename();\n        if ($item->isDir() && ctype_digit($pid)) {\n            $stat = file_get_contents(\"/proc/{$pid}/stat\");\n            $info = explode(' ', $stat);\n            $pinfo[$pid] = [intval($info[3]), []/* , $info */];\n        }\n    }\n    foreach ($pinfo as $pid => $info) {\n        [$ppid] = $info;\n        $ppid = intval($ppid);\n        $pinfo[$ppid][1][] = $pid;\n    }\n    $y = function ($pid, $path = []) use (&$y, $pinfo) {\n        if (isset($pinfo[$pid])) {\n            if (isset($pinfo[$pid][0])) {\n                [$ppid] = $pinfo[$pid];\n            } else {\n                $ppid = null;\n            }\n            $path[] = $pid;\n            return $y($ppid, $path);\n        }\n        return array_reverse($path);\n    };\n    $tree = [];\n    foreach ($pinfo as $pid => $info) {\n        $path = $y($pid);\n        $node = &$tree;\n        foreach ($path as $id) {\n            if (!isset($node[$id])) {\n                $node[$id] = [];\n            }\n            $node = &$node[$id];\n        }\n    }\n    return [$pinfo, $tree];\n}\n\nfunction debug_log($str, $handle = STDERR)\n{\n    if ($handle === STDERR) {\n        $tpl = \"\\033[31m[%d %s] %s\\033[0m\\n\";\n    } else {\n        $tpl = \"[%d %s] %s\\n\";\n    }\n    if (is_resource($handle)) {\n        fprintf($handle, $tpl, posix_getpid(), date('Y-m-d H:i:s', time()), $str);\n    } else {\n        printf($tpl, posix_getpid(), date('Y-m-d H:i:s', time()), $str);\n    }\n}\n\nfunction arrayEqual(array $a, array $b, $strict = true)\n{\n    if (($a && !$b) || (!$a && $b)) {\n        return false;\n    }\n    if ($strict) {\n        foreach ($a as $k => $v) {\n            if (!array_key_exists($k, $b)) {\n                return false;\n            }\n            if (gettype($v) !== gettype($b[$k])) {\n                return false;\n            }\n            if (is_array($v) && arrayEqual($v, $b[$k]) === false) {\n                return false;\n            }\n        }\n        return true;\n    }\n    $aks = array_keys($a);\n    $bks = array_keys($b);\n    sort($aks);\n    sort($bks);\n    return $aks === $bks;\n}\n\nfunction check_tcp_port(string $host, int $port): bool\n{\n    return (bool) @fsockopen($host, $port);\n}\n\nfunction start_server($file, $host, $port, $redirect_file = '/dev/null', $ext1 = null, $ext2 = null, $debug = false)\n{\n    $php_executable = getenv('TEST_PHP_EXECUTABLE') ?: PHP_BINARY;\n    $cmd_args = getenv('TEST_PHP_ARGS');\n    $fdSpec = [\n        0 => STDIN,\n        1 => STDOUT,\n        2 => STDERR,\n    ];\n    /*if (substr(PHP_OS, 0, 3) == 'WIN') {\n        $cmd = \"$php_executable $cmd_args $file\";\n        $opts = [\"bypass_shell\" => true,  \"suppress_errors\" => true];\n        $handle = proc_open(addslashes($cmd), $fdSpec, $pipes, null, null, $opts);\n    } else {\n        $cmd = \"exec $php_executable $file > $redirect_file 2>&1\";\n        $handle = proc_open($cmd, $fdSpec, $pipes);\n    }*/\n    // 必须加exec, 否咋proc_terminate结束不了server进程 ！！！！！！\n    if ($debug) {\n        $cmd = \"exec {$php_executable} {$file} {$host} {$port} {$ext1} {$ext2}\";\n        echo '[SHELL_EXEC]' . $cmd . \"\\n\";\n    } else {\n        $cmd = \"exec {$php_executable} {$file} {$host} {$port} {$ext1} {$ext2} > {$redirect_file} 2>&1\";\n    }\n    // $cmd = \"exec $php_executable $file $host $port\";\n    $handle = proc_open($cmd, $fdSpec, $pipes);\n    if ($handle === false) {\n        exit(__FUNCTION__ . ' fail');\n    }\n    make_sure_server_listen_success:\n\n        $i = 0;\n    $fp = null;\n    while (($i++ < 30) && !($fp = @fsockopen($host, $port))) {\n        usleep(10000);\n    }\n    if ($fp) {\n        fclose($fp);\n    }\n\n    // linux上有问题，client端事件循环还没起起来就会先调用这个shutdown回调, 结束了子进程\n    // 第二个shutdown_function swoole才会把子进程的事件循环起来\n    //    register_shutdown_function(function() use($handle, $redirect_file) {\n    //        proc_terminate($handle, SIGTERM);\n    //        @unlink($redirect_file);\n    //    });\n    swoole_async_set(['enable_coroutine' => false]); // need use exit\n    return function () use ($handle, $redirect_file) {\n        // @unlink($redirect_file);\n        proc_terminate($handle, SIGTERM);\n        Event::exit();\n        exit;\n    };\n}\n\nfunction swoole_fork_exec(callable $fn, bool $redirect_stdin_and_stdout = false, int $pipe_type = SOCK_DGRAM, bool $enable_coroutine = false)\n{\n    $process = new Process(...func_get_args());\n    if (!$process->start()) {\n        return false;\n    }\n    return $process::wait();\n}\n\nfunction php_fork_exec(callable $fn, $f_stdout = '/dev/null', $f_stderr = null)\n{\n    $pid = pcntl_fork();\n    if ($pid < 0) {\n        exit('fork fail');\n    }\n    if ($pid === 0) {\n        fclose(STDOUT);\n        $STDOUT = fopen($f_stdout, 'w');\n        if ($f_stderr !== null) {\n            fclose(STDERR);\n            $STDERR = fopen($f_stderr, 'w');\n        }\n        $fn();\n        exit;\n    }\n    pcntl_waitpid($pid, $status);\n    return ['pid' => $pid, 'status', $status];\n}\n\n/**\n * spawn_exec\n * @param null|string $cmd command\n * @param null|string $input code\n * @param null|int $tv_sec timeout sec\n * @param null|int $tv_usec timeout usec\n * @param null|string $cwd change work dir\n * @param null|array $env env\n * @return array [out, err]\n */\nfunction spawn_exec($cmd, $input = null, $tv_sec = null, $tv_usec = null, $cwd = null, ?array $env = null)\n{\n    $out = $err = null;\n    $winOpt = ['suppress_errors' => true, 'binary_pipes' => true];\n    $proc = proc_open($cmd, [\n        0 => ['pipe', 'r'],\n        1 => ['pipe', 'w'],\n        2 => ['pipe', 'w'],\n    ], $pipes, $cwd, $env, $winOpt);\n    assert($proc !== false);\n    if ($input !== null) {\n        $n = fwrite($pipes[0], $input);\n        if (strlen($input) !== $n) {\n            goto closePipes;\n        }\n    }\n    // 必须关闭\n    assert(fclose($pipes[0]));\n    unset($pipes[0]);\n    // 防止select立即返回, 消耗cpu\n    assert(!($tv_sec === 0 && $tv_usec === 0));\n    while (true) {\n        $r = $pipes;\n        $w = null;\n        $e = null;\n        /* 隐藏被信号或者其他系统调用打断 产生的错误 */\n        set_error_handler(function () {});\n        $n = @stream_select($r, $w, $e, $tv_sec, $tv_usec);\n        restore_error_handler();\n        if ($n === false) {\n            break;\n        }\n        if ($n === 0) {\n            // 超时kill -9\n            assert(proc_terminate($proc, SIGKILL));\n            throw new RuntimeException(\"exec {$cmd} time out\");\n        }\n        if ($n > 0) {\n            foreach ($r as $handle) {\n                if ($handle === $pipes[1]) {\n                    $_ = &$out;\n                } else {\n                    if ($handle === $pipes[2]) {\n                        $_ = &$err;\n                    } else {\n                        $_ = '';\n                    }\n                }\n                $line = fread($handle, 8192);\n                $isEOF = $line === '';\n                if ($isEOF) {\n                    break 2;\n                }\n                $_ .= $line;\n            }\n        }\n    }\n    closePipes:\n    foreach ($pipes as $fd => $pipe) {\n        if (is_resource($pipe)) {\n            @fclose($pipe);\n        }\n        unset($pipes[$fd]);\n    }\n    return [$out, $err];\n}\n\nfunction parent_child($parentFunc, $childFunc)\n{\n    $pid = pcntl_fork();\n    if ($pid < 0) {\n        echo 'ERROR';\n        exit;\n    }\n    if ($pid === 0) {\n        $childFunc();\n        exit;\n    }\n    $parentFunc($pid);\n}\n\nfunction readfile_with_lock($file): string\n{\n    $fp = fopen($file, 'r+');\n    flock($fp, LOCK_SH);\n    $data = '';\n    while (!feof($fp)) {\n        $data .= fread($fp, 8192);\n    }\n    fclose($fp);\n    return $data;\n}\n\nfunction dump_to_file($file, $data)\n{\n    $fp = fopen($file, 'w+');\n    $out = bin2hex($data);\n    $lines = str_split($out, 160);\n    foreach ($lines as $l) {\n        fwrite($fp, $l . \"\\n\");\n    }\n    fclose($fp);\n}\n\nfunction curl_type_assert($ch, $resource_type, $class_type): void\n{\n    if (PHP_VERSION_ID >= 80000) {\n        Assert::isInstanceOf($ch, $class_type);\n    } else {\n        Assert::eq(get_resource_type($ch), $resource_type);\n    }\n}\n\nfunction swoole_get_variance($avg, $array, $is_swatch = false): bool|float\n{\n    $count = count($array);\n    if ($count == 1 && $is_swatch) {\n        return false;\n    }\n    if ($count > 0) {\n        $total_var = 0;\n        foreach ($array as $lv) {\n            $total_var += pow($lv - $avg, 2);\n        }\n        if ($count == 1 && $is_swatch) {\n            return false;\n        }\n        return $is_swatch ? sqrt($total_var / (count($array) - 1)) : sqrt($total_var / count($array));\n    }\n    return false;\n}\n\nfunction swoole_get_average($array): float|int\n{\n    return array_sum($array) / count($array);\n}\n\nfunction assert_server_stats($stats): void\n{\n    Assert::keyExists($stats, 'connection_num');\n    Assert::keyExists($stats, 'request_count');\n}\n\nfunction assert_upload_file($file, $tmp_name, $name, $type, $size, $error = 0): void\n{\n    Assert::notEmpty($file);\n    Assert::eq($file['tmp_name'], $tmp_name);\n    Assert::eq($file['name'], $name);\n    Assert::eq($file['type'], $type);\n    Assert::eq($file['size'], $size);\n    Assert::eq($file['error'], $error);\n}\n\nfunction swoole_loop_n($n, $fn): void\n{\n    for ($i = 0; $i < $n; $i++) {\n        $fn($i);\n    }\n}\n\nfunction swoole_loop($fn)\n{\n    $i = 0;\n    while (true) {\n        $fn($i++);\n    }\n}\n\nfunction build_ftp_url(string $path = ''): string\n{\n    return 'ftp://' . FTP_USER . ':' . FTP_PASS . '@' . FTP_HOST . ':' . FTP_PORT . '/' . $path;\n}\n\nfunction get_thread_name(): string\n{\n    return trim(file_get_contents('/proc/' . posix_getpid() . '/task/' . Thread::getNativeId() . '/comm'));\n}\n\nfunction mkdir_if_not_exists(string $string): void\n{\n    if (!is_dir($string)) {\n        mkdir($string, 0777, true);\n    }\n}\n\nfunction array_arrange($array): array\n{\n    $list = array_keys(array_flip($array));\n    sort($list);\n    return $list;\n}\n"
  },
  {
    "path": "tests/include/lib/composer.json",
    "content": "{\n  \"name\": \"swoole_tests/lib\",\n  \"description\": \"for swoole tests\",\n  \"authors\": [\n    {\n      \"name\": \"tianfeng.han\",\n      \"email\": \"rango@swoole.com\"\n    }\n  ],\n  \"autoload\": {\n    \"psr-4\": {\n      \"SwooleTest\\\\\": \"src\"\n    }\n  },\n  \"require\": {\n    \"guzzlehttp/guzzle\": \"^7.3\",\n    \"guzzlehttp/promises\": \"^1.4\",\n    \"guzzlehttp/psr7\": \"^1.7\",\n    \"php-http/async-client-implementation\": \"^1.0\",\n    \"php-http/client-common\": \"^1.5|^2.0\",\n    \"php-http/discovery\": \"^1.6.1\",\n    \"php-http/httplug\": \"^1.1|^2.0\",\n    \"php-http/message\": \"^1.5\",\n    \"psr/http-factory\": \"^1.0\",\n    \"symfony/http-client\": \"^5.3\",\n    \"nyholm/psr7\": \"^1.4\",\n    \"friendsofphp/php-cs-fixer\": \"^3.3\",\n    \"php-http/message-factory\": \"^1.1\",\n    \"predis/predis\": \"^2.2\"\n  },\n  \"config\": {\n    \"allow-plugins\": {\n      \"php-http/discovery\": true\n    }\n  }\n}\n"
  },
  {
    "path": "tests/include/lib/src/Assert.php",
    "content": "<?php\n\n/*\n * This file is part of the swoole/assert package\n * forked from the repository webmozart/assert.\n *\n * (c) Bernhard Schussek <bschussek@gmail.com>\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nnamespace SwooleTest;\n\nuse ArrayAccess;\nuse BadMethodCallException;\nuse Closure;\nuse Countable;\nuse Exception;\nuse RuntimeException;\nuse Throwable;\nuse Traversable;\n\n/**\n * Efficient assertions to validate the input/output of your methods.\n *\n * @method static bool nullOrString($value, $message = '')\n * @method static bool nullOrStringNotEmpty($value, $message = '')\n * @method static bool nullOrInteger($value, $message = '')\n * @method static bool nullOrIntegerish($value, $message = '')\n * @method static bool nullOrFloat($value, $message = '')\n * @method static bool nullOrNumeric($value, $message = '')\n * @method static bool nullOrNatural($value, $message = '')\n * @method static bool nullOrBoolean($value, $message = '')\n * @method static bool nullOrScalar($value, $message = '')\n * @method static bool nullOrObject($value, $message = '')\n * @method static bool nullOrResource($value, $type = null, $message = '')\n * @method static bool nullOrIsCallable($value, $message = '')\n * @method static bool nullOrIsArray($value, $message = '')\n * @method static bool nullOrIsTraversable($value, $message = '')\n * @method static bool nullOrIsArrayAccessible($value, $message = '')\n * @method static bool nullOrIsCountable($value, $message = '')\n * @method static bool nullOrIsIterable($value, $message = '')\n * @method static bool nullOrIsInstanceOf($value, $class, $message = '')\n * @method static bool nullOrNotInstanceOf($value, $class, $message = '')\n * @method static bool nullOrIsInstanceOfAny($value, $classes, $message = '')\n * @method static bool nullOrIsEmpty($value, $message = '')\n * @method static bool nullOrNotEmpty($value, $message = '')\n * @method static bool nullOrTrue($value, $message = '')\n * @method static bool nullOrFalse($value, $message = '')\n * @method static bool nullOrIp($value, $message = '')\n * @method static bool nullOrIpv4($value, $message = '')\n * @method static bool nullOrIpv6($value, $message = '')\n * @method static bool nullOrUniqueValues($values, $message = '')\n * @method static bool nullOrEq($value, $expect, $message = '')\n * @method static bool nullOrNotEq($value, $expect, $message = '')\n * @method static bool nullOrSame($value, $expect, $message = '')\n * @method static bool nullOrNotSame($value, $expect, $message = '')\n * @method static bool nullOrGreaterThan($value, $limit, $message = '')\n * @method static bool nullOrGreaterThanEq($value, $limit, $message = '')\n * @method static bool nullOrLessThan($value, $limit, $message = '')\n * @method static bool nullOrLessThanEq($value, $limit, $message = '')\n * @method static bool nullOrRange($value, $min, $max, $message = '')\n * @method static bool nullOrOneOf($value, $values, $message = '')\n * @method static bool nullOrContains($value, $subString, $message = '')\n * @method static bool nullOrNotContains($value, $subString, $message = '')\n * @method static bool nullOrNotWhitespaceOnly($value, $message = '')\n * @method static bool nullOrStartsWith($value, $prefix, $message = '')\n * @method static bool nullOrStartsWithLetter($value, $message = '')\n * @method static bool nullOrEndsWith($value, $suffix, $message = '')\n * @method static bool nullOrRegex($value, $pattern, $message = '')\n * @method static bool nullOrNotRegex($value, $pattern, $message = '')\n * @method static bool nullOrAlpha($value, $message = '')\n * @method static bool nullOrDigits($value, $message = '')\n * @method static bool nullOrAlnum($value, $message = '')\n * @method static bool nullOrLower($value, $message = '')\n * @method static bool nullOrUpper($value, $message = '')\n * @method static bool nullOrLength($value, $length, $message = '')\n * @method static bool nullOrMinLength($value, $min, $message = '')\n * @method static bool nullOrMaxLength($value, $max, $message = '')\n * @method static bool nullOrLengthBetween($value, $min, $max, $message = '')\n * @method static bool nullOrFileExists($value, $message = '')\n * @method static bool nullOrFile($value, $message = '')\n * @method static bool nullOrDirectory($value, $message = '')\n * @method static bool nullOrReadable($value, $message = '')\n * @method static bool nullOrWritable($value, $message = '')\n * @method static bool nullOrClassExists($value, $message = '')\n * @method static bool nullOrSubclassOf($value, $class, $message = '')\n * @method static bool nullOrInterfaceExists($value, $message = '')\n * @method static bool nullOrImplementsInterface($value, $interface, $message = '')\n * @method static bool nullOrPropertyExists($value, $property, $message = '')\n * @method static bool nullOrPropertyNotExists($value, $property, $message = '')\n * @method static bool nullOrMethodExists($value, $method, $message = '')\n * @method static bool nullOrMethodNotExists($value, $method, $message = '')\n * @method static bool nullOrKeyExists($value, $key, $message = '')\n * @method static bool nullOrKeyNotExists($value, $key, $message = '')\n * @method static bool nullOrCount($value, $key, $message = '')\n * @method static bool nullOrMinCount($value, $min, $message = '')\n * @method static bool nullOrMaxCount($value, $max, $message = '')\n * @method static bool nullOrIsList($value, $message = '')\n * @method static bool nullOrIsMap($value, $message = '')\n * @method static bool nullOrCountBetween($value, $min, $max, $message = '')\n * @method static bool nullOrUuid($values, $message = '')\n * @method static bool nullOrThrows($expression, $class = 'Exception', $message = '')\n * @method static bool allString($values, $message = '')\n * @method static bool allStringNotEmpty($values, $message = '')\n * @method static bool allInteger($values, $message = '')\n * @method static bool allIntegerish($values, $message = '')\n * @method static bool allFloat($values, $message = '')\n * @method static bool allNumeric($values, $message = '')\n * @method static bool allNatural($values, $message = '')\n * @method static bool allBoolean($values, $message = '')\n * @method static bool allScalar($values, $message = '')\n * @method static bool allObject($values, $message = '')\n * @method static bool allResource($values, $type = null, $message = '')\n * @method static bool allIsCallable($values, $message = '')\n * @method static bool allIsArray($values, $message = '')\n * @method static bool allIsTraversable($values, $message = '')\n * @method static bool allIsArrayAccessible($values, $message = '')\n * @method static bool allIsCountable($values, $message = '')\n * @method static bool allIsIterable($values, $message = '')\n * @method static bool allIsInstanceOf($values, $class, $message = '')\n * @method static bool allNotInstanceOf($values, $class, $message = '')\n * @method static bool allIsInstanceOfAny($values, $classes, $message = '')\n * @method static bool allNull($values, $message = '')\n * @method static bool allNotNull($values, $message = '')\n * @method static bool allIsEmpty($values, $message = '')\n * @method static bool allNotEmpty($values, $message = '')\n * @method static bool allTrue($values, $message = '')\n * @method static bool allFalse($values, $message = '')\n * @method static bool allIp($values, $message = '')\n * @method static bool allIpv4($values, $message = '')\n * @method static bool allIpv6($values, $message = '')\n * @method static bool allUniqueValues($values, $message = '')\n * @method static bool allEq($values, $expect, $message = '')\n * @method static bool allNotEq($values, $expect, $message = '')\n * @method static bool allSame($values, $expect, $message = '')\n * @method static bool allNotSame($values, $expect, $message = '')\n * @method static bool allGreaterThan($values, $limit, $message = '')\n * @method static bool allGreaterThanEq($values, $limit, $message = '')\n * @method static bool allLessThan($values, $limit, $message = '')\n * @method static bool allLessThanEq($values, $limit, $message = '')\n * @method static bool allRange($values, $min, $max, $message = '')\n * @method static bool allOneOf($values, $values, $message = '')\n * @method static bool allContains($values, $subString, $message = '')\n * @method static bool allNotContains($values, $subString, $message = '')\n * @method static bool allNotWhitespaceOnly($values, $message = '')\n * @method static bool allStartsWith($values, $prefix, $message = '')\n * @method static bool allStartsWithLetter($values, $message = '')\n * @method static bool allEndsWith($values, $suffix, $message = '')\n * @method static bool allRegex($values, $pattern, $message = '')\n * @method static bool allNotRegex($values, $pattern, $message = '')\n * @method static bool allAlpha($values, $message = '')\n * @method static bool allDigits($values, $message = '')\n * @method static bool allAlnum($values, $message = '')\n * @method static bool allLower($values, $message = '')\n * @method static bool allUpper($values, $message = '')\n * @method static bool allLength($values, $length, $message = '')\n * @method static bool allMinLength($values, $min, $message = '')\n * @method static bool allMaxLength($values, $max, $message = '')\n * @method static bool allLengthBetween($values, $min, $max, $message = '')\n * @method static bool allFileExists($values, $message = '')\n * @method static bool allFile($values, $message = '')\n * @method static bool allDirectory($values, $message = '')\n * @method static bool allReadable($values, $message = '')\n * @method static bool allWritable($values, $message = '')\n * @method static bool allClassExists($values, $message = '')\n * @method static bool allSubclassOf($values, $class, $message = '')\n * @method static bool allInterfaceExists($values, $message = '')\n * @method static bool allImplementsInterface($values, $interface, $message = '')\n * @method static bool allPropertyExists($values, $property, $message = '')\n * @method static bool allPropertyNotExists($values, $property, $message = '')\n * @method static bool allMethodExists($values, $method, $message = '')\n * @method static bool allMethodNotExists($values, $method, $message = '')\n * @method static bool allKeyExists($values, $key, $message = '')\n * @method static bool allKeyNotExists($values, $key, $message = '')\n * @method static bool allCount($values, $key, $message = '')\n * @method static bool allMinCount($values, $min, $message = '')\n * @method static bool allMaxCount($values, $max, $message = '')\n * @method static bool allCountBetween($values, $min, $max, $message = '')\n * @method static bool allIsList($values, $message = '')\n * @method static bool allIsMap($values, $message = '')\n * @method static bool allUuid($values, $message = '')\n * @method static bool allThrows($expressions, $class = 'Exception', $message = '')\n *\n * @since  2.0\n *\n * @author Bernhard Schussek <bschussek@gmail.com>\n */\nclass Assert\n{\n    protected static $throwException = true;\n    protected static $maxStringLength = 1024;\n\n    public static function setThrowException(bool $b)\n    {\n        static::$throwException = $b;\n    }\n\n    public static function assert($value, $message = ''): bool\n    {\n        if (!$value) {\n            static::reportInvalidArgument($message);\n            return false;\n        }\n        return true;\n    }\n\n    public static function string($value, $message = ''): bool\n    {\n        if (!is_string($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a string. Got: %s',\n                static::typeToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function stringNotEmpty($value, $message = ''): bool\n    {\n        return static::string($value, $message) && static::notEq($value, '', $message);\n    }\n\n    public static function integer($value, $message = ''): bool\n    {\n        if (!is_int($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected an integer. Got: %s',\n                static::typeToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function integerish($value, $message = ''): bool\n    {\n        if (!is_numeric($value) || $value != (int)$value) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected an integerish value. Got: %s',\n                static::typeToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function float($value, $message = ''): bool\n    {\n        if (!is_float($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a float. Got: %s',\n                static::typeToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function numeric($value, $message = ''): bool\n    {\n        if (!is_numeric($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a numeric. Got: %s',\n                static::typeToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function natural($value, $message = ''): bool\n    {\n        if (!is_int($value) || $value < 0) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a non-negative integer. Got %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function boolean($value, $message = ''): bool\n    {\n        if (!is_bool($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a boolean. Got: %s',\n                static::typeToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function scalar($value, $message = ''): bool\n    {\n        if (!is_scalar($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a scalar. Got: %s',\n                static::typeToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function object($value, $message = ''): bool\n    {\n        if (!is_object($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected an object. Got: %s',\n                static::typeToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function resource($value, $type = null, $message = ''): bool\n    {\n        if (!is_resource($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a resource. Got: %s',\n                static::typeToString($value)\n            ));\n            return false;\n        }\n        if ($type && $type !== get_resource_type($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a resource of type %2$s. Got: %s',\n                static::typeToString($value),\n                $type\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function isCallable($value, $message = ''): bool\n    {\n        if (!is_callable($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a callable. Got: %s',\n                static::typeToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function isArray($value, $message = ''): bool\n    {\n        if (!is_array($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected an array. Got: %s',\n                static::typeToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function isArrayAccessible($value, $message = ''): bool\n    {\n        if (!is_array($value) && !($value instanceof ArrayAccess)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected an array accessible. Got: %s',\n                static::typeToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function isCountable($value, $message = ''): bool\n    {\n        if (!is_array($value) && !($value instanceof Countable)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a countable. Got: %s',\n                static::typeToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function isIterable($value, $message = ''): bool\n    {\n        if (!is_array($value) && !($value instanceof Traversable)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected an iterable. Got: %s',\n                static::typeToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function isInstanceOf($value, $class, $message = ''): bool\n    {\n        if (!($value instanceof $class)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected an instance of %2$s. Got: %s',\n                static::typeToString($value),\n                $class\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function notInstanceOf($value, $class, $message = ''): bool\n    {\n        if ($value instanceof $class) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected an instance other than %2$s. Got: %s',\n                static::typeToString($value),\n                $class\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function isInstanceOfAny($value, array $classes, $message = ''): bool\n    {\n        foreach ($classes as $class) {\n            if ($value instanceof $class) {\n                return true;\n            }\n        }\n        static::reportInvalidArgument(sprintf(\n            $message ?: 'Expected an instance of any of %2$s. Got: %s',\n            static::typeToString($value),\n            implode(', ', array_map(['static', 'valueToString'], $classes))\n        ));\n        return false;\n    }\n\n    public static function isEmpty($value, $message = ''): bool\n    {\n        if (!empty($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected an empty value. Got: %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function notEmpty($value, $message = ''): bool\n    {\n        if (empty($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a non-empty value. Got: %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function null($value, $message = ''): bool\n    {\n        if (null !== $value) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected null. Got: %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function notNull($value, $message = ''): bool\n    {\n        if (null === $value) {\n            static::reportInvalidArgument(\n                $message ?: 'Expected a value other than null.'\n            );\n            return false;\n        }\n        return true;\n    }\n\n    public static function true($value, $message = ''): bool\n    {\n        if (true !== $value) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to be true. Got: %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function false($value, $message = ''): bool\n    {\n        if (false !== $value) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to be false. Got: %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function ip($value, $message = ''): bool\n    {\n        if (false === filter_var($value, FILTER_VALIDATE_IP)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to be an IP. Got: %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function ipv4($value, $message = ''): bool\n    {\n        if (false === filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to be an IPv4. Got: %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function ipv6($value, $message = ''): bool\n    {\n        if (false === filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to be an IPv6. Got %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function uniqueValues(array $values, $message = ''): bool\n    {\n        $allValues = count($values);\n        $uniqueValues = count(array_unique($values));\n        if ($allValues !== $uniqueValues) {\n            $difference = $allValues - $uniqueValues;\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected an array of unique values, but %s of them %s duplicated',\n                $difference,\n                (1 === $difference ? 'is' : 'are')\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function eq($value, $expect, $message = ''): bool\n    {\n        if ($expect != $value) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value equal to %2$s. Got: %s',\n                static::valueToString($value),\n                static::valueToString($expect)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function notEq($value, $expect, $message = ''): bool\n    {\n        if ($expect == $value) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a different value than %s.',\n                static::valueToString($expect)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function same($value, $expect, $message = ''): bool\n    {\n        if ($expect !== $value) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value identical to %2$s. Got: %s',\n                static::valueToString($value),\n                static::valueToString($expect)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function notSame($value, $expect, $message = ''): bool\n    {\n        if ($expect === $value) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value not identical to %s.',\n                static::valueToString($expect)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function greaterThan($value, $limit, $message = ''): bool\n    {\n        if ($value <= $limit) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value greater than %2$s. Got: %s',\n                static::valueToString($value),\n                static::valueToString($limit)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function greaterThanEq($value, $limit, $message = ''): bool\n    {\n        if ($value < $limit) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value greater than or equal to %2$s. Got: %s',\n                static::valueToString($value),\n                static::valueToString($limit)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function lessThan($value, $limit, $message = ''): bool\n    {\n        if ($value >= $limit) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value less than %2$s. Got: %s',\n                static::valueToString($value),\n                static::valueToString($limit)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function lessThanEq($value, $limit, $message = ''): bool\n    {\n        if ($value > $limit) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value less than or equal to %2$s. Got: %s',\n                static::valueToString($value),\n                static::valueToString($limit)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function range($value, $min, $max, $message = ''): bool\n    {\n        if ($value < $min || $value > $max) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value between %2$s and %3$s. Got: %s',\n                static::valueToString($value),\n                static::valueToString($min),\n                static::valueToString($max)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function approximate($value, $actual, float $ratio = 0.1): bool\n    {\n        $ret = $actual * (1 - $ratio) < $value && $actual * (1 + $ratio) > $value;\n        if (!$ret) {\n            static::reportInvalidArgument(\n                \"Expected a value approximate {$value}, but got {$actual}\\n\"\n            );\n        }\n        return $ret;\n    }\n\n    public static function oneOf($value, array $values, $message = ''): bool\n    {\n        if (!in_array($value, $values, true)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected one of: %2$s. Got: %s',\n                static::valueToString($value),\n                implode(', ', array_map(['static', 'valueToString'], $values))\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function contains($value, $subString, $message = ''): bool\n    {\n        if (false === strpos($value, $subString)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to contain %2$s. Got: %s',\n                static::valueToString($value),\n                static::valueToString($subString)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function notContains($value, $subString, $message = ''): bool\n    {\n        if (false !== strpos($value, $subString)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: '%2$s was not expected to be contained in a value. Got: %s',\n                static::valueToString($value),\n                static::valueToString($subString)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function notWhitespaceOnly($value, $message = ''): bool\n    {\n        if (preg_match('/^\\s*$/', $value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a non-whitespace string. Got: %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function startsWith($value, $prefix, $message = ''): bool\n    {\n        if (0 !== strpos($value, $prefix)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to start with %2$s. Got: %s',\n                static::valueToString($value),\n                static::valueToString($prefix)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function startsWithLetter($value, $message = ''): bool\n    {\n        $valid = isset($value[0]);\n\n        if ($valid) {\n            $locale = setlocale(LC_CTYPE, 0);\n            setlocale(LC_CTYPE, 'C');\n            $valid = ctype_alpha($value[0]);\n            setlocale(LC_CTYPE, $locale);\n        }\n\n        if (!$valid) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to start with a letter. Got: %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function endsWith($value, $suffix, $message = ''): bool\n    {\n        if ($suffix !== substr($value, -static::strlen($suffix))) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to end with %2$s. Got: %s',\n                static::valueToString($value),\n                static::valueToString($suffix)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function regex($value, $pattern, $message = ''): bool\n    {\n        if (!preg_match($pattern, $value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'The value %s does not match the expected pattern.',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function notRegex($value, $pattern, $message = ''): bool\n    {\n        if (preg_match($pattern, $value, $matches, PREG_OFFSET_CAPTURE)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'The value %s matches the pattern %s (at offset %d).',\n                static::valueToString($value),\n                static::valueToString($pattern),\n                $matches[0][1]\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function alpha($value, $message = ''): bool\n    {\n        $locale = setlocale(LC_CTYPE, 0);\n        setlocale(LC_CTYPE, 'C');\n        $valid = !ctype_alpha($value);\n        setlocale(LC_CTYPE, $locale);\n\n        if ($valid) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to contain only letters. Got: %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function digits($value, $message = ''): bool\n    {\n        $locale = setlocale(LC_CTYPE, 0);\n        setlocale(LC_CTYPE, 'C');\n        $valid = !ctype_digit($value);\n        setlocale(LC_CTYPE, $locale);\n\n        if ($valid) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to contain digits only. Got: %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function alnum($value, $message = ''): bool\n    {\n        $locale = setlocale(LC_CTYPE, 0);\n        setlocale(LC_CTYPE, 'C');\n        $valid = !ctype_alnum($value);\n        setlocale(LC_CTYPE, $locale);\n\n        if ($valid) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to contain letters and digits only. Got: %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function lower($value, $message = ''): bool\n    {\n        $locale = setlocale(LC_CTYPE, 0);\n        setlocale(LC_CTYPE, 'C');\n        $valid = !ctype_lower($value);\n        setlocale(LC_CTYPE, $locale);\n\n        if ($valid) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to contain lowercase characters only. Got: %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function upper($value, $message = ''): bool\n    {\n        $locale = setlocale(LC_CTYPE, 0);\n        setlocale(LC_CTYPE, 'C');\n        $valid = !ctype_upper($value);\n        setlocale(LC_CTYPE, $locale);\n\n        if ($valid) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to contain uppercase characters only. Got: %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function length($value, $length, $message = ''): bool\n    {\n        if ($length !== static::strlen($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to contain %2$s characters. Got: %s',\n                static::valueToString($value),\n                $length\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function minLength($value, $min, $message = ''): bool\n    {\n        if (static::strlen($value) < $min) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to contain at least %2$s characters. Got: %s',\n                static::valueToString($value),\n                $min\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function maxLength($value, $max, $message = ''): bool\n    {\n        if (static::strlen($value) > $max) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to contain at most %2$s characters. Got: %s',\n                static::valueToString($value),\n                $max\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function lengthBetween($value, $min, $max, $message = ''): bool\n    {\n        $length = static::strlen($value);\n\n        if ($length < $min || $length > $max) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s',\n                static::valueToString($value),\n                $min,\n                $max\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function fileExists($value, $message = ''): bool\n    {\n        static::string($value);\n\n        if (!file_exists($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'The file %s does not exist.',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function file($value, $message = ''): bool\n    {\n        static::fileExists($value, $message);\n\n        if (!is_file($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'The path %s is not a file.',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function directory($value, $message = ''): bool\n    {\n        static::fileExists($value, $message);\n\n        if (!is_dir($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'The path %s is no directory.',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function readable($value, $message = ''): bool\n    {\n        if (!is_readable($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'The path %s is not readable.',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function writable($value, $message = ''): bool\n    {\n        if (!is_writable($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'The path %s is not writable.',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function classExists($value, $message = ''): bool\n    {\n        if (!class_exists($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected an existing class name. Got: %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function subclassOf($value, $class, $message = ''): bool\n    {\n        if (!is_subclass_of($value, $class)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected a sub-class of %2$s. Got: %s',\n                static::valueToString($value),\n                static::valueToString($class)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function interfaceExists($value, $message = ''): bool\n    {\n        if (!interface_exists($value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected an existing interface name. got %s',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function implementsInterface($value, $interface, $message = ''): bool\n    {\n        if (!in_array($interface, class_implements($value))) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected an implementation of %2$s. Got: %s',\n                static::valueToString($value),\n                static::valueToString($interface)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function propertyExists($classOrObject, $property, $message = ''): bool\n    {\n        if (!property_exists($classOrObject, $property)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected the property %s to exist.',\n                static::valueToString($property)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function propertyNotExists($classOrObject, $property, $message = ''): bool\n    {\n        if (property_exists($classOrObject, $property)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected the property %s to not exist.',\n                static::valueToString($property)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function methodExists($classOrObject, $method, $message = ''): bool\n    {\n        if (!method_exists($classOrObject, $method)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected the method %s to exist.',\n                static::valueToString($method)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function methodNotExists($classOrObject, $method, $message = ''): bool\n    {\n        if (method_exists($classOrObject, $method)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected the method %s to not exist.',\n                static::valueToString($method)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function keyExists($array, $key, $message = ''): bool\n    {\n        if (!(isset($array[$key]) || array_key_exists($key, $array))) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected the key %s to exist.',\n                static::valueToString($key)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function keyNotExists($array, $key, $message = ''): bool\n    {\n        if (isset($array[$key]) || array_key_exists($key, $array)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected the key %s to not exist.',\n                static::valueToString($key)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function count($array, $number, $message = ''): bool\n    {\n        return static::eq(\n            count($array),\n            $number,\n            $message ?: sprintf('Expected an array to contain %d elements. Got: %d.', $number, count($array))\n        );\n    }\n\n    public static function minCount($array, $min, $message = ''): bool\n    {\n        if (count($array) < $min) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected an array to contain at least %2$d elements. Got: %d',\n                count($array),\n                $min\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function maxCount($array, $max, $message = ''): bool\n    {\n        if (count($array) > $max) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected an array to contain at most %2$d elements. Got: %d',\n                count($array),\n                $max\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function countBetween($array, $min, $max, $message = ''): bool\n    {\n        $count = count($array);\n\n        if ($count < $min || $count > $max) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Expected an array to contain between %2$d and %3$d elements. Got: %d',\n                $count,\n                $min,\n                $max\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function isList($array, $message = ''): bool\n    {\n        if (!is_array($array) || !$array || array_keys($array) !== range(0, count($array) - 1)) {\n            static::reportInvalidArgument(\n                $message ?: 'Expected list - non-associative array.'\n            );\n            return false;\n        }\n        return true;\n    }\n\n    public static function isMap($array, $message = ''): bool\n    {\n        if (\n            !is_array($array) ||\n            !$array ||\n            array_keys($array) !== array_filter(array_keys($array), function ($key) {\n                return is_string($key);\n            })\n        ) {\n            static::reportInvalidArgument(\n                $message ?: 'Expected map - associative array with string keys.'\n            );\n            return false;\n        }\n        return true;\n    }\n\n    public static function uuid($value, $message = ''): bool\n    {\n        $value = str_replace(['urn:', 'uuid:', '{', '}'], '', $value);\n\n        // The nil UUID is special form of UUID that is specified to have all\n        // 128 bits set to zero.\n        if ('00000000-0000-0000-0000-000000000000' === $value) {\n            return true;\n        }\n\n        if (!preg_match('/^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/', $value)) {\n            static::reportInvalidArgument(sprintf(\n                $message ?: 'Value %s is not a valid UUID.',\n                static::valueToString($value)\n            ));\n            return false;\n        }\n        return true;\n    }\n\n    public static function throws(Closure $expression, $class = 'Exception', $message = ''): bool\n    {\n        static::string($class);\n\n        $actual = 'none';\n\n        try {\n            $expression();\n        } catch (Exception $e) {\n            $actual = get_class($e);\n            if ($e instanceof $class) {\n                return true;\n            }\n        } catch (Throwable $e) {\n            $actual = get_class($e);\n            if ($e instanceof $class) {\n                return true;\n            }\n        }\n\n        static::reportInvalidArgument($message ?: sprintf(\n            'Expected to throw \"%s\", got \"%s\"',\n            $class,\n            $actual\n        ));\n        return false;\n    }\n\n    public static function __callStatic($name, $arguments): bool\n    {\n        if ('nullOr' === substr($name, 0, 6)) {\n            if (null !== $arguments[0]) {\n                $method = lcfirst(substr($name, 6));\n                if (!call_user_func_array(['static', $method], $arguments)) {\n                    return false;\n                }\n            }\n\n            return true;\n        }\n\n        if ('all' === substr($name, 0, 3)) {\n            if (!static::isIterable($arguments[0])) {\n                return false;\n            }\n\n            $method = lcfirst(substr($name, 3));\n            $args = $arguments;\n\n            foreach ($arguments[0] as $entry) {\n                $args[0] = $entry;\n                if (!call_user_func_array(['static', $method], $args)) {\n                    return false;\n                }\n            }\n\n            return true;\n        }\n\n        throw new BadMethodCallException('No such method: ' . $name);\n    }\n\n    protected static function valueToString($value): string\n    {\n        if (null === $value) {\n            return 'null';\n        }\n\n        if (true === $value) {\n            return 'true';\n        }\n\n        if (false === $value) {\n            return 'false';\n        }\n\n        if (is_array($value)) {\n            return 'array(' . count($value) . ')';\n        }\n\n        if (is_object($value)) {\n            if (method_exists($value, '__toString')) {\n                return get_class($value) . ': ' . self::valueToString($value->__toString());\n            }\n            return get_class($value);\n        }\n\n        if (is_resource($value)) {\n            return 'resource';\n        }\n\n        if (is_string($value)) {\n            $length = strlen($value);\n            if ($length > static::$maxStringLength) {\n                $value = substr($value, 0, static::$maxStringLength) . '...';\n            }\n            return 'string(' . $length . ') \"' . $value . '\"';\n        }\n\n        return (string)$value;\n    }\n\n    protected static function typeToString($value): string\n    {\n        return is_object($value) ? get_class($value) : gettype($value);\n    }\n\n    protected static function strlen($value): int\n    {\n        if (!function_exists('mb_detect_encoding')) {\n            return strlen($value);\n        }\n\n        if (false === $encoding = mb_detect_encoding($value)) {\n            return strlen($value);\n        }\n\n        return mb_strwidth($value, $encoding);\n    }\n\n    protected static function reportInvalidArgument(string $message = '')\n    {\n        $e = new RuntimeException($message);\n        if (static::$throwException) {\n            throw $e;\n        } else {\n            $file = $e->getFile();\n            $line = $e->getLine();\n            $msg = $e->getMessage();\n            $trace = $e->getTraceAsString();\n            foreach ($e->getTrace() as $call) {\n                $file = $call['file'] ?? 'Unknown';\n                $line = $call['line'] ?? 0;\n                if ($file !== __FILE__) {\n                    break;\n                }\n            }\n            echo \"\\nAssert failed: \" . (empty($msg) ? '' : \"{$msg} \") . \"in {$file} on line {$line}\\nStack trace: \\n{$trace}\\n\";\n        }\n    }\n\n    private function __construct()\n    {\n    }\n}\n"
  },
  {
    "path": "tests/include/lib/src/ChildProcess.php",
    "content": "<?php\n\nnamespace SwooleTest;\n\nuse Swoole\\Process;\n\nclass ChildProcess\n{\n    protected $process;\n\n    protected function __construct($script)\n    {\n        $this->process = new Process(function (Process $worker) use ($script) {\n            $worker->exec('/bin/sh', ['-c', $script]);\n        }, true, SOCK_STREAM, false);\n        $this->process->start();\n    }\n\n    public function read()\n    {\n        return $this->process->read();\n    }\n\n    public function write(string $data): void\n    {\n        $this->process->write($data);\n    }\n\n    static function exec(string $script): ChildProcess\n    {\n        return new self($script);\n    }\n}\n"
  },
  {
    "path": "tests/include/lib/src/CoServer.php",
    "content": "<?php\n/**\n * Author: Twosee <twose@qq.com>\n * Date: 2019/3/28 10:30 AM\n */\n\nnamespace SwooleTest;\n\nuse Co\\Socket;\nuse RuntimeException;\n\nclass CoServer\n{\n    /** @var Socket */\n    protected $server = null;\n    /** @var Socket[] */\n    protected $connections = [];\n    /** @var callable */\n    protected $connectHandler = null;\n    /** @var callable */\n    protected $dataHandler = null;\n\n    public static function createTcp(string $host = '127.0.0.1', int $port = 0, int $backlog = 128): self\n    {\n        return new self([\n            'host' => $host,\n            'port' => $port,\n            'backlog' => $backlog\n        ]);\n    }\n\n    public static function createTcpGreeting(...$args): self\n    {\n        $server = self::createTcp(...$args);\n        $server->setConnectHandler(function (Socket $conn) {\n            $conn->sendAll('Hello Swoole');\n        });\n        return $server;\n    }\n\n    public static function createHttpHelloWorld(...$args): self\n    {\n        $server = self::createTcp(...$args);\n        $server->setDataHandler(function (Socket $conn, string $data) {\n            if (strpos($data, 'HTTP/1.0') !== false || stripos($data, 'Connection: closed') !== false) {\n                $conn->keep_alive = false;\n            }\n            if (strrpos($data, \"\\r\\n\\r\\n\") !== false) {\n                $conn->sendAll(\"HTTP/1.1 200 OK\\r\\nConnection: keep-alive\\r\\nContent-Length: 0\\r\\n\\r\\n\");\n                if (!($conn->keep_alive ?? true)) {\n                    $conn->close();\n                }\n            }\n        });\n        return $server;\n    }\n\n    public function __construct(array $options)\n    {\n        $this->server = new Socket(\n            $options['domain'] ?? AF_INET,\n            $options['type'] ?? SOCK_STREAM,\n            $options['protocol'] ?? IPPROTO_IP\n        );\n        if (!$this->server->bind($options['host'] ?? '127.0.0.1', $options['port'] ?? 9501)) {\n            throw new RuntimeException(\"bind failed due to {$this->server->errMsg}\");\n        }\n        if (!$this->server->listen($options['backlog'] ?? 128)) {\n            throw new RuntimeException(\"listen failed due to {$this->server->errMsg}\");\n        }\n    }\n\n    public function getPort(): int\n    {\n        return ($this->server->getsockname() ?: [])['port'] ?? 0;\n    }\n\n    public function setConnectHandler(callable $handler)\n    {\n        $this->connectHandler = $handler;\n    }\n\n    public function setDataHandler(callable $handler)\n    {\n        $this->dataHandler = $handler;\n    }\n\n    public function run()\n    {\n        go(function () {\n            while ($conn = $this->server->accept(-1)) {\n                $this->connections[$conn->fd] = $conn;\n                go(function () use ($conn) {\n                    defer(function () use ($conn) {\n                        unset($this->connections[$conn->fd]);\n                    });\n                    if ($handler = $this->connectHandler) {\n                        if ($handler($conn) === false) {\n                            return;\n                        }\n                    }\n                    while ($data = $conn->recv(8192, -1)) {\n                        if ($handler = $this->dataHandler) {\n                            if ($handler($conn, $data) === false) {\n                                return;\n                            }\n                        }\n                    }\n                });\n            }\n            foreach ($this->connections as $conn) {\n                $conn->close();\n            }\n            $this->server->close();\n        });\n    }\n\n    public function shutdown()\n    {\n        $this->server->close();\n    }\n}\n"
  },
  {
    "path": "tests/include/lib/src/CurlManager.php",
    "content": "<?php\n\nnamespace SwooleTest;\n\nuse Swoole\\Process;\nuse Swoole;\nuse function Swoole\\Coroutine\\run as run;\n\nclass CurlManager\n{\n    protected $port;\n    protected $nativeCurl = false;\n\n    function __construct() {\n        $this->nativeCurl = defined('SWOOLE_HOOK_NATIVE_CURL');\n    }\n\n    function disableNativeCurl() {\n        $this->nativeCurl = false;\n    }\n\n    function getUrlBase()\n    {\n        return \"http://127.0.0.1:{$this->port}\";\n    }\n\n    protected function runCliServer($port)\n    {\n        $proc = new Process(function (Process $p) use ($port) {\n            $exec = \"/usr/bin/env php -t \" . __DIR__ . \" -n -S 127.0.0.1:{$port} \" . __DIR__ . \"/responder/get.php\";\n            $p->exec('/bin/sh', ['-c', $exec]);\n        }, true, 1);\n\n        $proc->start();\n        while (1) {\n            usleep(10000);\n            if (@file_get_contents($this->getUrlBase() . '/')) {\n                break;\n            }\n        }\n        return $proc;\n    }\n\n    function run(callable $fn, $createCliServer = true)\n    {\n        if ($createCliServer) {\n            $this->port = get_one_free_port();\n            $proc = $this->runCliServer($this->port);\n        } else {\n            $proc = null;\n        }\n\n        global $argc, $argv;\n        if (!($argc > 1 and $argv[1] == 'ori')) {\n            $flags = $this->nativeCurl ? SWOOLE_HOOK_NATIVE_CURL : SWOOLE_HOOK_CURL;\n            Swoole\\Runtime::enableCoroutine($flags);\n        }\n\n        run(function () use ($fn, $proc) {\n            $fn(\"127.0.0.1:{$this->port}\");\n            if ($proc) {\n                Swoole\\Process::kill($proc->pid);\n            }\n        });\n\n        if ($createCliServer) {\n            Process::wait();\n        }\n    }\n}\n"
  },
  {
    "path": "tests/include/lib/src/DbWrapper.php",
    "content": "<?php\n\nnamespace SwooleTest;\n\nuse Swoole\\Coroutine\\MySQL;\n\nclass DbWrapper\n{\n    /**\n     * @var MySQL\n     */\n    private $mysql;\n\n    private $config;\n\n    public function connect($config)\n    {\n        $mysql = new \\mysqli();\n        $res = $mysql->connect(\n            $config['host'],\n            $config['user'],\n            $config['password'],\n            $config['database'],\n            $config['port'],\n        );\n\n        if (false === $res) {\n            throw new RuntimeException($mysql->connect_error, $mysql->errno);\n        } else {\n            $this->mysql = $mysql;\n            $this->config = $config;\n        }\n\n        return $res;\n    }\n\n    public function __call($name, $arguments)\n    {\n        // $result = $this->mysql->{$name}(...$arguments);\n        // $result = call_user_func_array([$this->mysql, $name], $arguments);\n        $result = $this->mysql->query($arguments[0]);\n        if (false === $result) {\n            if (!$this->mysql->connected) {\n                $this->connect($this->config);\n                return call_user_func_array([$this->mysql, $name], $arguments);\n            }\n            if (!empty($this->mysql->errno)) {\n                throw new RuntimeException($this->mysql->error, $this->mysql->errno);\n            }\n        }\n\n        return $result->fetch_all();\n    }\n}\n"
  },
  {
    "path": "tests/include/lib/src/LengthServer.php",
    "content": "<?php\n/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2017 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\nnamespace SwooleTest;\n\nuse Swoole;\nuse \\RandStr;\n\nabstract class LengthServer\n{\n    protected $setting;\n    protected $index = array();\n    protected $recv_bytes = 0;\n    protected $count = 0;\n    protected $show_lost_package = false;\n\n    static public $pkg_num = 100000;\n    static public $pkg_len_min = 100;\n    static public $pkg_len_max = 200000;\n    static public $random_bytes = true;\n\n    /**\n     * @var Swoole\\Coroutine\\Server\n     */\n    protected $serv;\n\n    protected $debug = false;\n\n    function onReceive($data)\n    {\n        $header = unpack('Nlen/Nindex/Nsid', substr($data, 0, 12));\n        if ($header['index'] % 1000 == 0 and $this->debug) {\n            echo \"#{$header['index']} recv package. sid={$header['sid']}, length=\" . strlen($data) . \", bytes={$this->recv_bytes}\\n\";\n        }\n        if ($header['index'] > self::$pkg_num) {\n            echo \"invalid index #{$header['index']}\\n\";\n        }\n        $this->index[$header['index']] = true;\n    }\n\n    abstract function onWorkerStart();\n\n    /**\n     * TestServer_Co constructor.\n     * @param int $port\n     * @param bool $ssl\n     * @throws \\Swoole\\Exception\n     */\n    function __construct(int $port, bool $ssl = false)\n    {\n        $serv = new  Swoole\\Coroutine\\Server('127.0.0.1', $port, $ssl);\n        $this->serv = $serv;\n        $this->setting = [\n            'open_length_check' => true,\n            'package_max_length' => 1024 * 1024,\n            'package_length_type' => 'N',\n            'package_length_offset' => 0,\n            'package_body_offset' => 4,\n        ];\n    }\n\n    /**\n     * @param $conn \\Swoole\\Coroutine\\Server\\Connection\n     * @param $data\n     */\n    function _receive($conn, $data)\n    {\n        $this->count++;\n        $this->recv_bytes += strlen($data);\n        $this->onReceive($data);\n        if ($this->count == self::$pkg_num) {\n            $conn->send(\"end\\n\");\n        }\n    }\n\n    function onClose()\n    {\n        echo \"Total count={$this->count}, bytes={$this->recv_bytes}\\n\";\n        if ($this->show_lost_package) {\n            for ($i = 0; $i < self::$pkg_num; $i++) {\n                if (!isset($this->index[$i])) {\n                    echo \"lost package#$i\\n\";\n                }\n            }\n        }\n        $this->count = $this->recv_bytes = 0;\n        unset($this->index);\n        $this->index = array();\n    }\n\n    function set($conf)\n    {\n        $this->setting += $conf;\n    }\n\n    function start()\n    {\n        $this->serv->set($this->setting);\n        $this->serv->handle(function ($conn) {\n            while (true) {\n                $data = $conn->recv();\n                if (!$data) {\n                    $this->onClose();\n                    break;\n                } else {\n                    $this->_receive($conn, $data);\n                }\n            }\n        });\n        $this->onWorkerStart();\n        $this->serv->start();\n    }\n\n    /**\n     * @return string\n     * @throws Exception\n     */\n    static function getPacket()\n    {\n        static $index = 0;\n        $sid = rand(10000000, 99999999);\n        $n = rand(self::$pkg_len_min, self::$pkg_len_max);\n\n        $data = self::$random_bytes ? RandStr::getBytes($n) : (new \\Swoole\\StringObject('A'))->repeat($n)->toString();\n        return pack('NNN', $n + 8, $index++, $sid) . $data;\n    }\n}\n"
  },
  {
    "path": "tests/include/lib/src/MQTT/Helper.php",
    "content": "<?php\n\nnamespace SwooleTest\\MQTT;\n\nclass Helper\n{\n    public static function getHeader($data)\n    {\n        $byte = ord($data[0]);\n\n        $header['type'] = ($byte & 0xF0) >> 4;\n        $header['dup'] = ($byte & 0x08) >> 3;\n        $header['qos'] = ($byte & 0x06) >> 1;\n        $header['retain'] = $byte & 0x01;\n\n        return $header;\n    }\n\n    public static function encodePublish($data)\n    {\n        $cmd = 3 << 4;\n        $body = pack('n', strlen($data['topic'])) . $data['topic'] . $data['content'];\n\n        $length = strlen($body);\n        $head = chr($cmd) . self::writeBodyLength($length);\n        return $head . $body;\n    }\n\n    protected static function writeBodyLength($length)\n    {\n        $string = '';\n        do {\n            $digit = $length % 128;\n            $length = $length >> 7;\n            if ($length > 0) {\n                $digit = ($digit | 0x80);\n            }\n            $string .= chr($digit);\n        } while ($length > 0);\n        return $string;\n    }\n\n    public static function encodePing(int $cmd)\n    {\n        $cmd = $cmd << 4;\n        return chr($cmd) . self::writeBodyLength(0);\n    }\n}\n"
  },
  {
    "path": "tests/include/lib/src/MysqlPool.php",
    "content": "<?php\n\nnamespace SwooleTest;\n\nuse RuntimeException;\nuse Swoole\\Coroutine\\Channel;\n\nclass MysqlPool\n{\n    /**\n     * @var MysqlPool\n     */\n    private static $instance;\n\n    /**\n     * @var Channel\n     */\n    private $pool;\n\n    /**\n     * @var array\n     */\n    private $config;\n\n    /**\n     * MysqlPool constructor.\n     * @param array $config\n     */\n    public function __construct(array $config = [])\n    {\n        if (empty($this->pool)) {\n            $this->config = $config;\n            $this->pool = new Channel($this->config['pool_size']);\n\n            for ($index = 0; $index < $this->config['pool_size']; $index++) {\n                // $mysql = new MyDb();\n                $mysql = new DbWrapper();\n                $res = $mysql->connect($config);\n                if ($res === false) {\n                    throw new RuntimeException(\"failed to connect mysql server.\");\n                } else {\n                    $this->put($mysql);\n                }\n            }\n        }\n    }\n\n    public function put($mySQL)\n    {\n        $this->pool->push($mySQL);\n    }\n\n    public function get()\n    {\n        /**\n         * @var mysqli\n         */\n        $mysql = $this->pool->pop($this->config['pool_get_timeout']);\n        if ($mysql === false) {\n            throw new RuntimeException('Get mysql timeout, all mysql connection is used');\n        }\n\n        return $mysql;\n    }\n\n    /**\n     * @param array $config\n     * @return MysqlPool\n     */\n    public static function getInstance(array $config = [])\n    {\n        if (!empty(self::$instance)) {\n            return self::$instance;\n        }\n\n        if (empty($config)) {\n            throw new RuntimeException('Mysql config empty');\n        }\n        self::$instance = new static($config);\n\n        return self::$instance;\n    }\n\n    /**\n     * @return mixed\n     * @desc 获取当时连接池可用对象\n     */\n    public function getLength()\n    {\n        return $this->pool->length();\n    }\n}\n"
  },
  {
    "path": "tests/include/lib/src/ProcessManager.php",
    "content": "<?php\n/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2017 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\nnamespace SwooleTest;\n\nuse RuntimeException;\nuse Swoole\\Atomic;\nuse Swoole\\Event;\nuse Swoole\\Process;\n\nclass ProcessManager\n{\n    /**\n     * @var Atomic\n     */\n    protected $atomic;\n    protected $alone = false;\n    protected $onlyChild = false;\n    protected $onlyParent = false;\n    protected $freePorts = [];\n    protected $randomFunc = 'get_safe_random';\n    protected $randomData = [[]];\n    protected $randomDataArray = [];\n\n    /**\n     * wait wakeup 1s default\n     */\n    protected $waitTimeout = 1.0;\n\n    public $parentFunc;\n    public $childFunc;\n    public $async = false;\n    public $useConstantPorts = false;\n\n    protected $childPid;\n    protected $childExitStatus = 255;\n    protected $expectExitSignal = [0, SIGTERM];\n    protected $parentFirst = false;\n    protected $killed = false;\n    /**\n     * @var Process\n     */\n    protected $childProcess;\n    protected $logFileHandle;\n\n    public function __construct()\n    {\n        $this->atomic = new Atomic(0);\n    }\n\n    public function setParent(callable $func)\n    {\n        $this->parentFunc = $func;\n    }\n\n    public function parentFirst()\n    {\n        $this->parentFirst = true;\n    }\n\n    public function childFirst()\n    {\n        $this->parentFirst = false;\n    }\n\n    public function setChild(callable $func)\n    {\n        $this->childFunc = $func;\n    }\n\n    public function getChildPid(): int\n    {\n        return $this->childPid;\n    }\n\n    public function setWaitTimeout(int $value)\n    {\n        $this->waitTimeout = $value;\n    }\n\n    //等待信息\n    public function wait()\n    {\n        if ($this->alone || $this->waitTimeout == 0) {\n            return false;\n        }\n        return $this->atomic->wait($this->waitTimeout);\n    }\n\n    //唤醒等待的进程\n    public function wakeup()\n    {\n        if ($this->alone) {\n            return false;\n        }\n        return $this->atomic->wakeup();\n    }\n\n    public function runParentFunc($pid = 0)\n    {\n        if (!$this->parentFunc) {\n            return (function () { $this->kill(); })();\n        } else {\n            return call_user_func($this->parentFunc, $pid);\n        }\n    }\n\n    public function setLogFile($file)\n    {\n        $this->logFileHandle = fopen($file, \"a+\");\n    }\n\n    public function writeLog($msg)\n    {\n        fwrite($this->logFileHandle, $msg . PHP_EOL);\n    }\n\n    /**\n     * @param int $index\n     * @return mixed\n     */\n    public function getFreePort($index = 0)\n    {\n        return $this->freePorts[$index];\n    }\n\n    public function setRandomFunc($func)\n    {\n        $this->randomFunc = $func;\n    }\n\n    public function initRandomData(int $size, int $len = null)\n    {\n        $this->initRandomDataEx(1, $size, $len);\n    }\n\n    /**\n     * 生成一个随机字节组成的数组\n     * @param int $n\n     * @param int $len 默认为0，表示随机产生长度\n     * @param bool $base64\n     * @throws \\Exception\n     */\n    public function initRandomDataArray($n = 1, $len = 0, bool $base64 = false)\n    {\n        while ($n--) {\n            if ($len == 0) {\n                $len = rand(1024, 1 * 1024 * 1024);\n            }\n            $bytes = random_bytes($len);\n            $this->randomDataArray[] = $base64 ? base64_encode($bytes) : $bytes;\n        }\n    }\n\n    /**\n     * @param $index\n     * @return mixed\n     */\n    public function getRandomDataElement(int $index = 0)\n    {\n        if (!isset($this->randomDataArray[$index])) {\n            throw new RuntimeException(\"out of array\");\n        }\n        return $this->randomDataArray[$index];\n    }\n\n    public function getRandomData()\n    {\n        return $this->getRandomDataEx(0);\n    }\n\n    public function getRandomDataSize(): int\n    {\n        return $this->getRandomDataSizeEx(0);\n    }\n\n    public function initRandomDataEx(int $block_num, int $size, ...$arguments)\n    {\n        $arguments = array_reverse($arguments);\n        $shift = 0;\n        foreach ($arguments as $index => $argument) {\n            if ($argument === null) {\n                $shift++;\n            } else {\n                break;\n            }\n        }\n        while ($shift--) {\n            array_shift($arguments);\n        }\n        $arguments = array_reverse($arguments);\n        $func = $this->randomFunc;\n        for ($b = 0; $b < $block_num; $b++) {\n            for ($n = $size; $n--;) {\n                $this->randomData[$b][] = $func(...$arguments);\n            }\n        }\n    }\n\n    public function getRandomDataEx(int $block_id)\n    {\n        if (!empty($this->randomData[$block_id])) {\n            return array_shift($this->randomData[$block_id]);\n        } else {\n            throw new RuntimeException('Out of the bound');\n        }\n    }\n\n    public function getRandomDataSizeEx(int $block_id): int\n    {\n        return count($this->randomData[$block_id]);\n    }\n\n    public function runChildFunc()\n    {\n        return call_user_func($this->childFunc);\n    }\n\n    /**\n     *  Kill Child Process\n     * @param bool $force\n     */\n    public function kill(bool $force = false)\n    {\n        if (!defined('PCNTL_ESRCH')) {\n            define('PCNTL_ESRCH', 3);\n        }\n        if (!$this->alone and !$this->killed and $this->childPid) {\n            $this->killed = true;\n            if ($force || (!@Process::kill($this->childPid) && swoole_errno() !== PCNTL_ESRCH)) {\n                if (!@Process::kill($this->childPid, SIGKILL) && swoole_errno() !== PCNTL_ESRCH) {\n                    exit('KILL CHILD PROCESS ERROR');\n                }\n            }\n        }\n    }\n\n    /**\n     * @param int $num\n     * @param int $increment Only used for constant port number, must be a constant\n     * @return void\n     */\n    public function initFreePorts(int $num = 1, int $increment = 0): void\n    {\n        for ($i = $num; $i--;) {\n            $this->freePorts[] = $this->useConstantPorts ? (9500 + $num - $i + count($this->freePorts) + $increment) : get_one_free_port();\n        }\n    }\n\n    public function initFreeIPv6Ports(int $num = 1): void\n    {\n        for ($i = $num; $i--;) {\n            $this->freePorts[] = $this->useConstantPorts ? (9500 + $num - $i + count($this->freePorts)) : get_one_free_port_ipv6();\n        }\n    }\n\n    public function run($redirectStdout = false)\n    {\n        global $argv, $argc;\n        if ($argc > 1) {\n            $this->useConstantPorts = true;\n            $this->alone = true;\n            $this->initFreePorts();\n            if ($argv[1] == 'child') {\n                $this->onlyChild = true;\n            } elseif ($argv[1] == 'parent') {\n                $this->onlyParent = true;\n            } else {\n                throw new RuntimeException(\"bad parameter \\$1\\n\");\n            }\n        }\n        $this->initFreePorts();\n        if ($this->alone) {\n            if ($this->onlyChild) {\n                return $this->runChildFunc();\n            } elseif ($this->onlyParent) {\n                return $this->runParentFunc();\n            }\n            $this->alone = false;\n        }\n\n        $this->childProcess = new Process(function () {\n            if ($this->parentFirst) {\n                $this->wait();\n            }\n            $this->runChildFunc();\n            exit;\n        }, $redirectStdout, $redirectStdout);\n        if (!$this->childProcess || !$this->childProcess->start()) {\n            exit(\"ERROR: CAN NOT CREATE PROCESS\\n\");\n        }\n        register_shutdown_function(function () {\n            $this->kill();\n        });\n        if (!$this->parentFirst) {\n            $this->wait();\n        }\n        $this->runParentFunc($this->childPid = $this->childProcess->pid);\n        Event::wait();\n        $waitInfo = Process::wait(true);\n        $this->childExitStatus = $waitInfo['code'];\n        if (!in_array($waitInfo['signal'], $this->expectExitSignal)) {\n            throw new RuntimeException(\"Unexpected exit code {$waitInfo['signal']}\");\n        }\n\n        return true;\n    }\n\n    public function getChildOutput()\n    {\n        $this->childProcess->setBlocking(false);\n        $output = '';\n        while (1) {\n            $data = @$this->childProcess->read();\n            if (!$data) {\n                break;\n            } else {\n                $output .= $data;\n            }\n        }\n        return $output;\n    }\n\n    public function expectExitCode($code = 0)\n    {\n        if (!is_array($code)) {\n            $code = [$code];\n        }\n        if (!in_array($this->childExitStatus, $code)) {\n            throw new RuntimeException(\"Unexpected exit code {$this->childExitStatus}\");\n        }\n    }\n\n    function getChildExitStatus() {\n        return $this->childExitStatus;\n    }\n\n    public function setExpectExitSignal($signal = 0)\n    {\n        if (!is_array($signal)) {\n            $signal = [$signal];\n        }\n        $this->expectExitSignal = $signal;\n    }\n\n    static function exec(callable $fn)\n    {\n        $pm = new static();\n        $pm->setWaitTimeout(0);\n        $pm->parentFunc = function () {\n        };\n        $pm->childFunc = function () use ($pm, $fn) {\n            $fn($pm);\n        };\n        $pm->childFirst();\n        $pm->run(true);\n\n        return $pm;\n    }\n}\n"
  },
  {
    "path": "tests/include/lib/src/RandStr.php",
    "content": "<?php\n/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2017 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\nnamespace SwooleTest;\n\n\nclass RandStr\n{\n    const ALPHA = 1;\n    const NUM = 2;\n    const CHINESE = 4;\n    const ALL = self::ALPHA | self::NUM | self::CHINESE;\n\n    const __ALPHA = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n    const __NUM = \"0123456789\";\n    const __CHINESE2000 = \"五于天末开下理事画现玫珠表珍列玉平不来与屯妻到互寺城霜载直进吉协南才垢圾夫无坟增示赤过志地雪支三夺大厅左丰百右历面帮原胡春克太磁砂灰达成顾肆友龙本村枯林械相查可机格析极检构术样档杰棕杨李要权楷七革基苛式牙划或功贡攻匠菜共区芳燕东芝世节切芭药睛睦盯虎止旧占卤贞睡肯具餐眩瞳步眯瞎卢眼皮此量时晨果虹早昌蝇曙遇昨蝗明蛤晚景暗晃显晕电最归紧昆呈叶顺呆呀中虽吕另员呼听吸只史嘛啼吵喧叫啊哪吧哟车轩因困四辊加男轴力斩胃办罗罚较边思轨轻累同财央朵曲由则崭册几贩骨内风凡赠峭迪岂邮凤生行知条长处得各务向笔物秀答称入科秒秋管秘季委么第后持拓打找年提扣押抽手折扔失换扩拉朱搂近所报扫反批且肝采肛胆肿肋肌用遥朋脸胸及胶膛爱甩妥肥脂全会估休代个介保佃仙作伯仍从你信们偿伙亿他分公化钱针然钉氏外旬名甸负儿铁角欠多久匀乐炙锭包凶争色主计庆订度让刘训为高放诉衣认义方说就变这记离良充率闰半关亲并站间部曾商产瓣前闪交六立冰普帝决闻妆冯北汪法尖洒江小浊澡渐没少泊肖兴光注洋水淡学沁池当汉涨业灶类灯煤粘烛炽烟灿烽煌粗粉炮米料炒炎迷断籽娄宽寂审宫军宙客宾家空宛社实宵灾之官字安它怀导居民收慢避惭届必怕愉懈心习悄屡忱忆敢恨怪尼卫际承阿陈耻阳职阵出降孤阴队防联孙耿辽也子限取陛姨寻姑杂毁旭如舅九奶婚妨嫌录灵巡刀好妇妈姆对参戏台劝观矣牟能难允驻驼马邓艰双线结顷红引旨强细纲张绵级给约纺弱纱继综纪弛绿经比烃定守害一在工上是国和的有人我了发以瑟斑晴语伍残封都动什杆舍鞍伏邦悲韭源善着羚磊矿剧页万尤跋森棵酒宁歌臣茹哎莽酣谨甘黄腊垂道植申卡叔趾足虚玻晶暮临象坚界肃梨刊章朝蚕品喊带雷恩闸鸭轰曼黑柬温盆苯苦某荆匡芋棋柑奎霸震积余叙者复怎炸笺简数赣彻覆碧凰皑易肠派汽拿攀势抛看拜哲岳兵肢县甫拥解穆受貌豺豪橡毅衰畏丧众输夷份雁谷苏癸蹬察镜跑软鸟鸣岛印狈逛鲁渔免见杀便誓辩尺州亢亡丹亥孩俯肪激唯截颜冲头均壮兽敝幸夹旁辞滓疗嫉泵永函兆泰康否杯系党灭庶兼播凯撰挖巨启眉媚声蕊恭舔翌翻熟屈齿龄亨蒸滁椰服矛仓她施媳案淄巢扭那丸津食霓骚令通私云爸骤蕴雍幻慈每沸曳王土木目日口田山禾白月金言火已女又干士二十寸雨古犬石厂丁西戈卜曰虫川甲皿竹斤乃八夕文广辛门贝己巳乙尸羽耳臼巴弓阶而种自深农体路正论些资形重战使等合图新还想点其起性斗把里两应制育去气问展意接情油题利压组期毛群次但孔流席运质治位器指建活教统别更真将识先专常造修病老回验很特根团转总任调热改完集毫研尔求精层清确低再证劳被号装单据设场花传判究须青越轮做整即速织书状海斯议般千影推今德差始响觉液维破消试布需胜济效选话片牧备续标存身板述底规走除置配养敌华测准许技床端感非磨往圆照搞族神容亚段算适讲按值美态彪班麦削排该击素密候草何树属市径螺英快坏移材省武培著河京助升抓含苗副谈围射例致酸却短剂宣环落首波践府考刻靠够满住枝局菌周护岩师举元超模贫减扬亩球医校呢稻滑鼓刚写微略范供块套项倒卷创律远初优死毒圈伟控跟裂粮母练塞钢顶策留误粒础故丝焊株院冷弹错散盾视艺版烈零室血缺厘绝富喷柱望盘雄似巩益洲脱投送侧润盖挥距触星松获独混座依未突架冬湿偏纹执寨责阀吃稳硬价努奇预评读背损棉侵厚泥辟卵箱掌氧停溶营终孟待尽俄缩沙退讨奋胞幼迫旋征槽殖握担鲜钻逐脚盐编蜂急伤飞域露核缘游振操甚迅辉异序纸夜乡隶缸念兰映沟吗儒磷插埃燃欢补咱芽瓦倾碳演附耪裔斜灌欧献猪腐请透司危括靛脉囤若尾束暴企穗愈拖牛染既遍锻夏井费访吹荣铜沿替滚旱悟脑措贯藏隙濮徐吴赵陆沈蒋曹唐潘袁郭蔡戴薛姚宋韩谢姜严陶董郑程倪秦邵汤葛俞杜殷龚魏梁崔邹邱彭尹庄卞贾洪盛樊侯邢郁凌仇韦童翟付祁仲宗梅鲍祝谭钟庞乔虞郝傅焦熊浦柏狄裴柳戚房毕翁储聂莫贺茅屠杭尚诸芦鞠廖骆靳詹阮惠桑柯刁柴丛齐喻桂侍舒戎阎宦巫黎涂符厉糜冒钮郎霍甄姬祖卓晓祥萍忠俊斌宏玲勇峰宝霞丽娟敏琴健静福贵勤锦艳莉涛瑞跃仁泉连喜银亮宇慧鹏茂淑芹坤剑君翠彬恒礼侠智浩菊香蓉炳寿圣贤洁耀延翔芬绍琳颖栋巧铭敬淮登鸿宜莲庭孝泽政彩诚崇彦佩宪锡钧劲锋殿希迎堂裕鹤欣汝妹岭沛莹雅佳纯靖蕾俭蔚彤湘绪尧廷锁勋庚嘉伦娥详钦寅冠骏滨威捷亭巍楼呜娜旺晋悦咏焕昭枫琼慎杏仕仪珊桃谦航舜猛卿鼎咸陵镇召敦佐熙遵桥网闽挺菲禄耘锐潮鉴婉塔蚜描粤粱惮慨乌矩疾徊碍戒买笛痛锈锌匆矢溪荤惟陪掩耸棠祭槐憨狙忙辑奉忧飘沫怖悬厌欲谱瘤货蛊赴垣嚎履闯藩遁雀渠探涸滇钡诡弟秩渗痊捏茸诬枪狠弃摇倘贬庙汇肩捎怒帽寄岸搐饼违汕蝎炔擅掖傀闹蜡裸碱奠秉丑倍萧瞒萌歧勒煎谐梳携蛇箕臂皖坍奸胎赌魁患凿傣栈唁晤碑匪翅瘫烤汛狰捍袄瞩碘嗜绰毖瓶疤俺倦冉递葬骇伶擒谴搬睬盎丈粳袋暇颈屉阜邻篓拆脊镭趣鼠疹寐鼻澎椿倔蝴酿辈钨盂购釉逆诛粹凄桅娇菏瑶父抢浮晦拂葫揉壕弊冻笼箩氛舵凹型默闲菩驰啦篡孪瑚蜗午宴驯镶砚怠粥躁豁靡拴睁丘傈腋碟懂皆淤矗浸隘挛咬帛揩瘩妖荡斟疼哥撬铣拨味哇挞迹哈孺桓蚀萄命惫幂渤稗迂瞧菱躺礁贸赶尝郡咖笆扎裤卉割炕砸潦俏饥羹锗赦博衙摆漱畅码砍钎渡绒牢捡痪棍喂辨璃澳饮洼抿窟咯辰隋憋酋绅狱悔厄\";\n    // const __CHINESE3000 = \"啊阿埃挨哎唉哀皑癌蔼矮艾碍爱隘鞍氨安俺按暗岸胺案肮昂盎凹敖熬翱袄傲奥懊澳芭捌扒叭吧笆八疤巴拔跋靶把耙坝霸罢爸白柏百摆佰败拜稗斑班搬扳般颁板版扮拌伴瓣半办绊邦帮梆榜膀绑棒磅蚌镑傍谤苞胞包褒剥薄雹保堡饱宝抱报暴豹鲍爆杯碑悲卑北辈背贝钡倍狈备惫焙被奔苯本笨崩绷甭泵蹦迸逼鼻比鄙笔彼碧蓖蔽毕毙毖币庇痹闭敝弊必辟壁臂避陛鞭边编贬扁便变卞辨辩辫遍标彪膘表鳖憋别瘪彬斌濒滨宾摈兵冰柄丙秉饼炳病并玻菠播拨钵波博勃搏铂箔伯帛舶脖膊渤泊驳捕卜哺补埠不布步簿部怖擦猜裁材才财睬踩采彩菜蔡餐参蚕残惭惨灿苍舱仓沧藏操糙槽曹草厕策侧册测层蹭插叉茬茶查碴搽察岔差诧拆柴豺搀掺蝉馋谗缠铲产阐颤昌猖场尝常长偿肠厂敞畅唱倡超抄钞朝嘲潮巢吵炒车扯撤掣彻澈郴臣辰尘晨忱沉陈趁衬撑称城橙成呈乘程惩澄诚承逞骋秤吃痴持匙池迟弛驰耻齿侈尺赤翅斥炽充冲虫崇宠抽酬畴踌稠愁筹仇绸瞅丑臭初出橱厨躇锄雏滁除楚础储矗搐触处揣川穿椽传船喘串疮窗幢床闯创吹炊捶锤垂春椿醇唇淳纯蠢戳绰疵茨磁雌辞慈瓷词此刺赐次聪葱囱匆从丛凑粗醋簇促蹿篡窜摧崔催脆瘁粹淬翠村存寸磋撮搓措挫错搭达答瘩打大呆歹傣戴带殆代贷袋待逮怠耽担丹单郸掸胆旦氮但惮淡诞弹蛋当挡党荡档刀捣蹈倒岛祷导到稻悼道盗德得的蹬灯登等瞪凳邓堤低滴迪敌笛狄涤翟嫡抵底地蒂第帝弟递缔颠掂滇碘点典靛垫电佃甸店惦奠淀殿碉叼雕凋刁掉吊钓调跌爹碟蝶迭谍叠丁盯叮钉顶鼎锭定订丢东冬董懂动栋侗恫冻洞兜抖斗陡豆逗痘都督毒犊独读堵睹赌杜镀肚度渡妒端短锻段断缎堆兑队对墩吨蹲敦顿囤钝盾遁掇哆多夺垛躲朵跺舵剁惰堕蛾峨鹅俄额讹娥恶厄扼遏鄂饿恩而儿耳尔饵洱二贰发罚筏伐乏阀法珐藩帆番翻樊矾钒繁凡烦反返范贩犯饭泛坊芳方肪房防妨仿访纺放菲非啡飞肥匪诽吠肺废沸费芬酚吩氛分纷坟焚汾粉奋份忿愤粪丰封枫蜂峰锋风疯烽逢冯缝讽奉凤佛否夫敷肤孵扶拂辐幅氟符伏俘服浮涪福袱弗甫抚辅俯釜斧脯腑府腐赴副覆赋复傅付阜父腹负富讣附妇缚咐噶嘎该改概钙盖溉干甘杆柑竿肝赶感秆敢赣冈刚钢缸肛纲岗港杠篙皋高膏羔糕搞镐稿告哥歌搁戈鸽胳疙割革葛格蛤阁隔铬个各给根跟耕更庚羹埂耿梗工攻功恭龚供躬公宫弓巩汞拱贡共钩勾沟苟狗垢构购够辜菇咕箍估沽孤姑鼓古蛊骨谷股故顾固雇刮瓜剐寡挂褂乖拐怪棺关官冠观管馆罐惯灌贯光广逛瑰规圭硅归龟闺轨鬼诡癸桂柜跪贵刽辊滚棍锅郭国果裹过哈骸孩海氦亥害骇酣憨邯韩含涵寒函喊罕翰撼捍旱憾悍焊汗汉夯杭航壕嚎豪毫郝好耗号浩呵喝荷菏核禾和何合盒貉阂河涸赫褐鹤贺嘿黑痕很狠恨哼亨横衡恒轰哄烘虹鸿洪宏弘红喉侯猴吼厚候后呼乎忽瑚壶葫胡蝴狐糊湖弧虎唬护互沪户花哗华猾滑画划化话槐徊怀淮坏欢环桓还缓换患唤痪豢焕涣宦幻荒慌黄磺蝗簧皇凰惶煌晃幌恍谎灰挥辉徽恢蛔回毁悔慧卉惠晦贿秽会烩汇讳诲绘荤昏婚魂浑混豁活伙火获或惑霍货祸击圾基机畸稽积箕肌饥迹激讥鸡姬绩缉吉极棘辑籍集及急疾汲即嫉级挤几脊己蓟技冀季伎祭剂悸济寄寂计记既忌际妓继纪嘉枷夹佳家加荚颊贾甲钾假稼价架驾嫁歼监坚尖笺间煎兼肩艰奸缄茧检柬碱碱拣捡简俭剪减荐槛鉴践贱见键箭件健舰剑饯渐溅涧建僵姜将浆江疆蒋桨奖讲匠酱降蕉椒礁焦胶交郊浇骄娇嚼搅铰矫侥脚狡角饺缴绞剿教酵轿较叫窖揭接皆秸街阶截劫节桔杰捷睫竭洁结解姐戒藉芥界借介疥诫届巾筋斤金今津襟紧锦仅谨进靳晋禁近烬浸尽劲荆兢茎睛晶鲸京惊精粳经井警景颈静境敬镜径痉靖竟竞净炯窘揪究纠玖韭久灸九酒厩救旧臼舅咎就疚鞠拘狙疽居驹菊局咀矩举沮聚拒据巨具距踞锯俱句惧炬剧捐鹃娟倦眷卷绢撅攫抉掘倔爵觉决诀绝均菌钧军君峻俊竣浚郡骏喀咖卡咯开揩楷凯慨刊堪勘坎砍看康慷糠扛抗亢炕考拷烤靠坷苛柯棵磕颗科壳咳可渴克刻客课肯啃垦恳坑吭空恐孔控抠口扣寇枯哭窟苦酷库裤夸垮挎跨胯块筷侩快宽款匡筐狂框矿眶旷况亏盔岿窥葵奎魁傀馈愧溃坤昆捆困括扩廓阔垃拉喇蜡腊辣啦莱来赖蓝婪栏拦篮阑兰澜谰揽览懒缆烂滥琅榔狼廊郎朗浪捞劳牢老佬姥酪烙涝勒乐雷镭蕾磊累儡垒擂肋类泪棱楞冷厘梨犁黎篱狸离漓理李里鲤礼莉荔吏栗丽厉励砾历利僳例俐痢立粒沥隶力璃哩俩联莲连镰廉怜涟帘敛脸链恋炼练粮凉梁粱良两辆量晾亮谅撩聊僚疗燎寥辽潦了撂镣廖料列裂烈劣猎琳林磷霖临邻鳞淋凛赁吝拎玲菱零龄铃伶羚凌灵陵岭领另令溜琉榴硫馏留刘瘤流柳六龙聋咙笼窿隆垄拢陇楼娄搂篓漏陋芦卢颅庐炉掳卤虏鲁麓碌露路赂鹿潞禄录陆戮驴吕铝侣旅履屡缕虑氯律率滤绿峦挛孪滦卵乱掠略抡轮伦仑沦纶论萝螺罗逻锣箩骡裸落洛骆络妈麻玛码蚂马骂嘛吗埋买麦卖迈脉瞒馒蛮满蔓曼慢漫谩芒茫盲氓忙莽猫茅锚毛矛铆卯茂冒帽貌贸么玫枚梅酶霉煤没眉媒镁每美昧寐妹媚门闷们萌蒙檬盟锰猛梦孟眯醚靡糜迷谜弥米秘觅泌蜜密幂棉眠绵冕免勉娩缅面苗描瞄藐秒渺庙妙蔑灭民抿皿敏悯闽明螟鸣铭名命谬摸摹蘑模膜磨摩魔抹末莫墨默沫漠寞陌谋牟某拇牡亩姆母墓暮幕募慕木目睦牧穆拿哪呐钠那娜纳氖乃奶耐奈南男难囊挠脑恼闹淖呢馁内嫩能妮霓倪泥尼拟你匿腻逆溺蔫拈年碾撵捻念娘酿鸟尿捏聂孽啮镊镍涅您柠狞凝宁拧泞牛扭钮纽脓浓农弄奴努怒女暖虐疟挪懦糯诺哦欧鸥殴藕呕偶沤啪趴爬帕怕琶拍排牌徘湃派攀潘盘磐盼畔判叛乓庞旁耪胖抛咆刨炮袍跑泡呸胚培裴赔陪配佩沛喷盆砰抨烹澎彭蓬棚硼篷膨朋鹏捧碰坯砒霹批披劈琵毗啤脾疲皮匹痞僻屁譬篇偏片骗飘漂瓢票撇瞥拼频贫品聘乒坪苹萍平凭瓶评屏坡泼颇婆破魄迫粕剖扑铺仆莆葡菩蒲埔朴圃普浦谱曝瀑期欺栖戚妻七凄漆柒沏其棋奇歧畦崎脐齐旗祈祁骑起岂乞企启契砌器气迄弃汽泣讫掐洽牵扦钎铅千迁签仟谦乾黔钱钳前潜遣浅谴堑嵌欠歉枪呛腔羌墙蔷强抢橇锹敲悄桥瞧乔侨巧鞘撬翘峭俏窍切茄且怯窃钦侵亲秦琴勤芹擒禽寝沁青轻氢倾卿清擎晴氰情顷请庆琼穷秋丘邱球求囚酋泅趋区蛆曲躯屈驱渠取娶龋趣去圈颧权醛泉全痊拳犬券劝缺炔瘸却鹊榷确雀裙群然燃冉染瓤壤攘嚷让饶扰绕惹热壬仁人忍韧任认刃妊纫扔仍日戎茸蓉荣融熔溶容绒冗揉柔肉茹蠕儒孺如辱乳汝入褥软阮蕊瑞锐闰润若弱撒洒萨腮鳃塞赛三叁伞散桑嗓丧搔骚扫嫂瑟色涩森僧莎砂杀刹沙纱傻啥煞筛晒珊苫杉山删煽衫闪陕擅赡膳善汕扇缮墒伤商赏晌上尚裳梢捎稍烧芍勺韶少哨邵绍奢赊蛇舌舍赦摄射慑涉社设砷申呻伸身深娠绅神沈审婶甚肾慎渗声生甥牲升绳省盛剩胜圣师失狮施湿诗尸虱十石拾时什食蚀实识史矢使屎驶始式示士世柿事拭誓逝势是嗜噬适仕侍释饰氏市恃室视试收手首守寿授售受瘦兽蔬枢梳殊抒输叔舒淑疏书赎孰熟薯暑曙署蜀黍鼠属术述树束戍竖墅庶数漱恕刷耍摔衰甩帅栓拴霜双爽谁水睡税吮瞬顺舜说硕朔烁斯撕嘶思私司丝死肆寺嗣四伺似饲巳松耸怂颂送宋讼诵搜艘擞嗽苏酥俗素速粟僳塑溯宿诉肃酸蒜算虽隋随绥髓碎岁穗遂隧祟孙损笋蓑梭唆缩琐索锁所塌他它她塔獭挞蹋踏胎苔抬台泰酞太态汰坍摊贪瘫滩坛檀痰潭谭谈坦毯袒碳探叹炭汤塘搪堂棠膛唐糖倘躺淌趟烫掏涛滔绦萄桃逃淘陶讨套特藤腾疼誊梯剔踢锑提题蹄啼体替嚏惕涕剃屉天添填田甜恬舔腆挑条迢眺跳贴铁帖厅听烃汀廷停亭庭艇通桐酮瞳同铜彤童桶捅筒统痛偷投头透凸秃突图徒途涂屠土吐兔湍团推颓腿蜕褪退吞屯臀拖托脱鸵陀驮驼椭妥拓唾挖哇蛙洼娃瓦袜歪外豌弯湾玩顽丸烷完碗挽晚皖惋宛婉万腕汪王亡枉网往旺望忘妄威巍微危韦违桅围唯惟为潍维苇萎委伟伪尾纬未蔚味畏胃喂魏位渭谓尉慰卫瘟温蚊文闻纹吻稳紊问嗡翁瓮挝蜗涡窝我斡卧握沃巫呜钨乌污诬屋无芜梧吾吴毋武五捂午舞伍侮坞戊雾晤物勿务悟误昔熙析西硒矽晰嘻吸锡牺稀息希悉膝夕惜熄烯溪汐犀檄袭席习媳喜铣洗系隙戏细瞎虾匣霞辖暇峡侠狭下厦夏吓掀锨先仙鲜纤咸贤衔舷闲涎弦嫌显险现献县腺馅羡宪陷限线相厢镶香箱襄湘乡翔祥详想响享项巷橡像向象萧硝霄削哮嚣销消宵淆晓小孝校肖啸笑效楔些歇蝎鞋协挟携邪斜胁谐写械卸蟹懈泄泻谢屑薪芯锌欣辛新忻心信衅星腥猩惺兴刑型形邢行醒幸杏性姓兄凶胸匈汹雄熊休修羞朽嗅锈秀袖绣墟戌需虚嘘须徐许蓄酗叙旭序畜恤絮婿绪续轩喧宣悬旋玄选癣眩绚靴薛学穴雪血勋熏循旬询寻驯巡殉汛训讯逊迅压押鸦鸭呀丫芽牙蚜崖衙涯雅哑亚讶焉咽阉烟淹盐严研蜒岩延言颜阎炎沿奄掩眼衍演艳堰燕厌砚雁唁彦焰宴谚验殃央鸯秧杨扬佯疡羊洋阳氧仰痒养样漾邀腰妖瑶摇尧遥窑谣姚咬舀药要耀椰噎耶爷野冶也页掖业叶曳腋夜液一壹医揖铱依伊衣颐夷遗移仪胰疑沂宜姨彝椅蚁倚已乙矣以艺抑易邑屹亿役臆逸肄疫亦裔意毅忆义益溢诣议谊译异翼翌绎茵荫因殷音阴姻吟银淫寅饮尹引隐印英樱婴鹰应缨莹萤营荧蝇迎赢盈影颖硬映哟拥佣臃痈庸雍踊蛹咏泳涌永恿勇用幽优悠忧尤由邮铀犹油游酉有友右佑釉诱又幼迂淤于盂榆虞愚舆余俞逾鱼愉渝渔隅予娱雨与屿禹宇语羽玉域芋郁吁遇喻峪御愈欲狱育誉浴寓裕预豫驭鸳渊冤元垣袁原援辕园员圆猿源缘远苑愿怨院曰约越跃钥岳粤月悦阅耘云郧匀陨允运蕴酝晕韵孕匝砸杂栽哉灾宰载再在咱攒暂赞赃脏葬遭糟凿藻枣早澡蚤躁噪造皂灶燥责择则泽贼怎增憎曾赠扎喳渣札轧铡闸眨栅榨咋乍炸诈摘斋宅窄债寨瞻毡詹粘沾盏斩辗崭展蘸栈占战站湛绽樟章彰漳张掌涨杖丈帐账仗胀瘴障招昭找沼赵照罩兆肇召遮折哲蛰辙者锗蔗这浙珍斟真甄砧臻贞针侦枕疹诊震振镇阵蒸挣睁征狰争怔整拯正政帧症郑证芝枝支吱蜘知肢脂汁之织职直植殖执值侄址指止趾只旨纸志挚掷至致置帜峙制智秩稚质炙痔滞治窒中盅忠钟衷终种肿重仲众舟周州洲诌粥轴肘帚咒皱宙昼骤珠株蛛朱猪诸诛逐竹烛煮拄瞩嘱主著柱助蛀贮铸筑住注祝驻抓爪拽专砖转撰赚篆桩庄装妆撞壮状椎锥追赘坠缀谆准捉拙卓桌琢茁酌啄着灼浊兹咨资姿滋淄孜紫仔籽滓子自渍字鬃棕踪宗综总纵邹走奏揍租足卒族祖诅阻组钻纂嘴醉最罪尊遵昨左佐柞做作坐座\";\n\n    private static $strCache = [];\n\n    /**\n     * @param int $len\n     * @param int $type\n     * @return string\n     */\n    public static function gen($len = 10, $type = self::NUM | self::ALPHA)\n    {\n        $str = self::getChars($type);\n        $strLen = mb_strlen($str);\n\n        $ret = \"\";\n        for ($i = 0; $i < $len; $i++) {\n            // non safe rand\n            $ret .= mb_substr($str, rand(0, $strLen - 1), 1);\n        }\n        return $ret;\n    }\n\n    /**\n     * @param $n\n     * @return string\n     * @throws \\Exception\n     */\n    static function getBytes($n)\n    {\n        if (function_exists('openssl_random_pseudo_bytes')) {\n            return openssl_random_pseudo_bytes($n);\n        } elseif (function_exists('random_bytes')) {\n            return random_bytes($n);\n        } else {\n            return self::gen($n);\n        }\n    }\n\n    /**\n     * @param $mask\n     * @return mixed|string\n     */\n    private static function getChars($mask)\n    {\n        if (isset(static::$strCache[$mask])) {\n            return static::$strCache[$mask];\n        }\n\n        $str = \"\";\n        if ($mask & self::NUM) {\n            $str .= self::__NUM;\n        }\n\n        if ($mask & self::ALPHA) {\n            $str .= self::__ALPHA;\n        }\n\n        if ($mask & self::CHINESE) {\n            $str .= self::__CHINESE2000;\n        }\n\n        if ($str === \"\") {\n            $str .= self::NUM . self::ALPHA;\n        }\n\n        static::$strCache[$mask] = $str;\n        return $str;\n    }\n}\n"
  },
  {
    "path": "tests/include/lib/src/Redis/DBConnectException.php",
    "content": "<?php\n\nnamespace SwooleTest\\Redis;\n\n\nclass DBConnectException extends \\Exception\n{\n\n}"
  },
  {
    "path": "tests/include/lib/src/Redis/Lock.php",
    "content": "<?php\n\nnamespace SwooleTest\\Redis;\n\nclass Lock\n{\n    const EXPIRES = 5;\n\n    private $random;\n    private $expires;\n    private $keyMap = [];\n\n    public static function i(): self\n    {\n        return new self;\n    }\n\n    public function __construct()\n    {\n        $this->random = substr(md5(microtime()), 0, 8);\n    }\n\n    public function lock(string $key, int $expires = self::EXPIRES): bool\n    {\n        $this->expires = $expires;\n        $ret = Redis::main()->set($key, $this->random, ['nx', 'ex' => $this->expires]);\n        if ($ret) {\n            $this->keyMap[$key] = microtime(true);\n        }\n        return !!$ret;\n    }\n\n    public function unlock(string $key = null)\n    {\n        if ($key) {\n            // unlock one\n            if ($this->keyMap[$key] ?? false) {\n                if ($this->keyMap[$key] < microtime(true) - $this->expires) {\n                    return; // have already expired\n                } else {\n                    @Redis::main()->del($key);\n                }\n                unset($this->keyMap[$key]);\n            }\n        } else {\n            // unlock all\n            foreach ($this->keyMap as $key => $expires) {\n                $this->unlock($key);\n            }\n        }\n    }\n\n    public function __destruct()\n    {\n        $this->unlock();\n    }\n\n}"
  },
  {
    "path": "tests/include/lib/src/Redis/Redis.php",
    "content": "<?php\n\nnamespace SwooleTest\\Redis;\n\n/**\n * Class Redis\n * @package SwooleTest\\Bug\\Redis\n * @method del($key)\n * @method set($key, $value, $args)\n */\nClass Redis\n{\n    public $is_in_pool = false;\n\n    public $name;\n\n    /**\n     * @var \\Swoole\\Coroutine\\Redis\n     */\n    public $client;\n\n    public static function i(array $options)\n    {\n        $name = 'redis_' . $options['host'] . ':' . $options['port'];\n        $pool = SQLPool::i($name);\n        /**@var $redis Redis */\n        if (count($pool) > 0 && ($redis = $pool->shift()) && $redis->isConnect()) {\n            //满足 1.会话池里有空闲连接 2.返回了一个非空连接 3.Redis没有超时时间\n            $redis->is_in_pool = false;\n            return $redis;\n        }\n        return new self($name, $options);\n    }\n\n    public static function main(): self\n    {\n        return self::i([\n            'host' => REDIS_SERVER_HOST,\n            'port' => REDIS_SERVER_PORT\n        ]);\n    }\n\n    private function __construct(string $name, array $options)\n    {\n        $this->name = $name;\n        $this->client = new \\Swoole\\Coroutine\\Redis();\n        if (!$this->client->connect($options['host'], $options['port'])) {\n            new DBConnectException('[Redis: ' . $this->client->errCode . '] ' . $this->client->errMsg,\n                $this->client->errCode);\n        }\n    }\n\n    public function isConnect(): bool\n    {\n        return $this->client->connected ?? false;\n    }\n\n    public function __call(string $name, $params)\n    {\n        if ($this->is_in_pool) {\n            throw new \\BadMethodCallException('this redis client is in pool!');\n        }\n        $ret = call_user_func_array([$this->client, $name], $params);\n        $this->revert();\n        return $ret;\n    }\n\n    public function revert()\n    {\n        SQLPool::i($this->name)->push($this);\n        $this->is_in_pool = true;\n    }\n\n    public function __destruct()\n    {\n        $this->client->close();\n    }\n\n}\n"
  },
  {
    "path": "tests/include/lib/src/Redis/SQLPool.php",
    "content": "<?php\n\nnamespace SwooleTest\\Redis;\n\nclass SQLPool\n{\n    /**\n     * @var self\n     */\n    private static $instance;\n\n    /**\n     * @param string $name\n     * @return \\SplQueue\n     */\n    public static function i(string $name): \\SplQueue\n    {\n        if (!self::$instance) {\n            self::$instance = new self;\n        }\n        return self::$instance->$name ?? (self::$instance->$name = new \\SplQueue);\n    }\n\n    public static function release()\n    {\n        self::$instance = null;\n    }\n\n}\n"
  },
  {
    "path": "tests/include/lib/src/Samtleben/WebsocketClient.php",
    "content": "<?php\n/**\n * Very basic websocket client.\n * Supporting draft hybi-10.\n *\n * @author Simon Samtleben <web@lemmingzshadow.net>\n * @version 2011-10-18\n */\n\nnamespace SwooleTest\\Samtleben;\n\nclass WebsocketClient\n{\n\tprivate $_host;\n\tprivate $_port;\n\tprivate $_path;\n\tprivate $_origin;\n    /**\n     * @var \\Swoole\\Client\n     */\n\tprivate $_Socket = null;\n\tprivate $_connected = false;\n\n\tpublic function __construct() { }\n\n\tpublic function __destruct()\n\t{\n\t\t$this->disconnect();\n\t}\n\n\tpublic function sendRecv($data, $type = 'text', $masked = true)\n\t{\n\t\tif($this->_connected === false)\n\t\t{\n\t\t\ttrigger_error(\"Not connected\", E_USER_WARNING);\n\t\t\treturn false;\n\t\t}\n\t\tif( !is_string($data)) {\n\t\t\ttrigger_error(\"Not a string data was given.\", E_USER_WARNING);\n\t\t\treturn false;\n\t\t}\n\t\tif (strlen($data) == 0)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\t$res = $this->_Socket->send($this->_hybi10Encode($data, $type, $masked));\n\n\t\tif($res === 0 || $res === false)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\t$buf = $this->_Socket->recv(512);\n\n\t\t$out = $this->_hybi10Decode($buf);\n\t\treturn $out['payload'];\n\t}\n\n\tpublic function recvData() {\n\t\t$buf = $this->_Socket->recv(512);\n\t\t$out = $this->_hybi10Decode($buf);\n\t\treturn $out['payload'];\n\t}\n\n\tpublic function connect($host, $port, $path, $origin = false)\n    {\n        $this->_host = $host;\n        $this->_port = $port;\n        $this->_path = $path;\n        $this->_origin = $origin;\n\n        $key = base64_encode($this->_generateRandomString(16, false, true));\n        $header = \"GET \" . $path . \" HTTP/1.1\\r\\n\";\n        $header .= \"Host: \" . $host . \":\" . $port . \"\\r\\n\";\n        $header .= \"Upgrade: websocket\\r\\n\";\n        $header .= \"Connection: Upgrade\\r\\n\";\n        $header .= \"Sec-WebSocket-Key: \" . $key . \"\\r\\n\";\n        if ($origin !== false) {\n            $header .= \"Sec-WebSocket-Origin: \" . $origin . \"\\r\\n\";\n        }\n        $header .= \"Sec-WebSocket-Version: 13\\r\\n\";\n\n        $this->_Socket = new \\Swoole\\Client(SWOOLE_TCP, SWOOLE_SOCK_SYNC);\n        $this->_Socket->connect($host, $port, 1);\n        $this->_Socket->send($header . \"\\r\\n\");\n        $response = $this->_Socket->recv(1500);\n\n        preg_match('#Sec-WebSocket-Accept:\\s(.*)$#mU', $response, $matches);\n\n        if ($matches) {\n            $keyAccept = trim($matches[1]);\n            $expectedResonse = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));\n            $this->_connected = ($keyAccept === $expectedResonse) ? true : false;\n        }\n\n        return $this->_connected;\n    }\n\n\tpublic function checkConnection()\n\t{\n\t\t$this->_connected = false;\n\n\t\t// send ping:\n\t\t$data = 'ping?';\n\t\t$this->_Socket->send($this->_hybi10Encode($data, 'ping', true));\n\n\t\t$response = $this->_Socket->recv(300);\n\t\tif(empty($response))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\t$response = $this->_hybi10Decode($response);\n\t\tif(!is_array($response))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\tif(!isset($response['type']) || $response['type'] !== 'pong')\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\t$this->_connected = true;\n\t\treturn true;\n\t}\n\n\n\tpublic function disconnect()\n\t{\n\t\t$this->_connected = false;\n\t\t$this->_Socket->close();\n\t}\n\n\tpublic function reconnect()\n\t{\n\t\tsleep(10);\n\t\t$this->_connected = false;\n\t\tfclose($this->_Socket);\n\t\t$this->connect($this->_host, $this->_port, $this->_path, $this->_origin);\n\t}\n\n\tprivate function _generateRandomString($length = 10, $addSpaces = true, $addNumbers = true)\n\t{\n\t\t$characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"§$%&/()=[]{}';\n\t\t$useChars = array();\n\t\t// select some random chars:\n\t\tfor($i = 0; $i < $length; $i++)\n\t\t{\n\t\t\t$useChars[] = $characters[mt_rand(0, strlen($characters)-1)];\n\t\t}\n\t\t// add spaces and numbers:\n\t\tif($addSpaces === true)\n\t\t{\n\t\t\tarray_push($useChars, ' ', ' ', ' ', ' ', ' ', ' ');\n\t\t}\n\t\tif($addNumbers === true)\n\t\t{\n\t\t\tarray_push($useChars, rand(0,9), rand(0,9), rand(0,9));\n\t\t}\n\t\tshuffle($useChars);\n\t\t$randomString = trim(implode('', $useChars));\n\t\t$randomString = substr($randomString, 0, $length);\n\t\treturn $randomString;\n\t}\n\n\tprivate function _hybi10Encode($payload, $type = 'text', $masked = true)\n\t{\n\t\t$frameHead = array();\n\t\t$frame = '';\n\t\t$payloadLength = strlen($payload);\n\n\t\tswitch($type)\n\t\t{\n\t\t\tcase 'text':\n\t\t\t\t// first byte indicates FIN, Text-Frame (10000001):\n\t\t\t\t$frameHead[0] = 129;\n\t\t\tbreak;\n\n\t\t\tcase 'close':\n\t\t\t\t// first byte indicates FIN, Close Frame(10001000):\n\t\t\t\t$frameHead[0] = 136;\n\t\t\tbreak;\n\n\t\t\tcase 'ping':\n\t\t\t\t// first byte indicates FIN, Ping frame (10001001):\n\t\t\t\t$frameHead[0] = 137;\n\t\t\tbreak;\n\n\t\t\tcase 'pong':\n\t\t\t\t// first byte indicates FIN, Pong frame (10001010):\n\t\t\t\t$frameHead[0] = 138;\n\t\t\tbreak;\n\t\t}\n\n\t\t// set mask and payload length (using 1, 3 or 9 bytes)\n\t\tif($payloadLength > 65535)\n\t\t{\n\t\t\t$payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8);\n\t\t\t$frameHead[1] = ($masked === true) ? 255 : 127;\n\t\t\tfor($i = 0; $i < 8; $i++)\n\t\t\t{\n\t\t\t\t$frameHead[$i+2] = bindec($payloadLengthBin[$i]);\n\t\t\t}\n\t\t\t// most significant bit MUST be 0 (close connection if frame too big)\n\t\t\tif($frameHead[2] > 127)\n\t\t\t{\n\t\t\t\t$this->close(1004);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\telseif($payloadLength > 125)\n\t\t{\n\t\t\t$payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8);\n\t\t\t$frameHead[1] = ($masked === true) ? 254 : 126;\n\t\t\t$frameHead[2] = bindec($payloadLengthBin[0]);\n\t\t\t$frameHead[3] = bindec($payloadLengthBin[1]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength;\n\t\t}\n\n\t\t// convert frame-head to string:\n\t\tforeach(array_keys($frameHead) as $i)\n\t\t{\n\t\t\t$frameHead[$i] = chr($frameHead[$i]);\n\t\t}\n\t\tif($masked === true)\n\t\t{\n\t\t\t// generate a random mask:\n\t\t\t$mask = array();\n\t\t\tfor($i = 0; $i < 4; $i++)\n\t\t\t{\n\t\t\t\t$mask[$i] = chr(rand(0, 255));\n\t\t\t}\n\n\t\t\t$frameHead = array_merge($frameHead, $mask);\n\t\t}\n\t\t$frame = implode('', $frameHead);\n\n\t\t// append payload to frame:\n\t\t$framePayload = array();\n\t\tfor($i = 0; $i < $payloadLength; $i++)\n\t\t{\n\t\t\t$frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];\n\t\t}\n\n\t\treturn $frame;\n\t}\n\n\tprivate function _hybi10Decode($data)\n\t{\n\t\t$payloadLength = '';\n\t\t$mask = '';\n\t\t$unmaskedPayload = '';\n\t\t$decodedData = array();\n\n\t\t// estimate frame type:\n\t\t$firstByteBinary = sprintf('%08b', ord($data[0]));\n\t\t$secondByteBinary = sprintf('%08b', ord($data[1]));\n\t\t$opcode = bindec(substr($firstByteBinary, 4, 4));\n\t\t$isMasked = ($secondByteBinary[0] == '1') ? true : false;\n\t\t$payloadLength = ord($data[1]) & 127;\n\n\t\tswitch($opcode)\n\t\t{\n\t\t\t// text frame:\n\t\t\tcase 1:\n\t\t\t\t$decodedData['type'] = 'text';\n\t\t\tbreak;\n\n\t\t\tcase 2:\n\t\t\t\t$decodedData['type'] = 'binary';\n\t\t\tbreak;\n\n\t\t\t// connection close frame:\n\t\t\tcase 8:\n\t\t\t\t$decodedData['type'] = 'close';\n\t\t\tbreak;\n\n\t\t\t// ping frame:\n\t\t\tcase 9:\n\t\t\t\t$decodedData['type'] = 'ping';\n\t\t\tbreak;\n\n\t\t\t// pong frame:\n\t\t\tcase 10:\n\t\t\t\t$decodedData['type'] = 'pong';\n\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t\tbreak;\n\t\t}\n\n\t\tif($payloadLength === 126)\n\t\t{\n\t\t   $mask = substr($data, 4, 4);\n\t\t   $payloadOffset = 8;\n\t\t   $dataLength = bindec(sprintf('%08b', ord($data[2])) . sprintf('%08b', ord($data[3]))) + $payloadOffset;\n\t\t}\n\t\telseif($payloadLength === 127)\n\t\t{\n\t\t\t$mask = substr($data, 10, 4);\n\t\t\t$payloadOffset = 14;\n\t\t\t$tmp = '';\n\t\t\tfor($i = 0; $i < 8; $i++)\n\t\t\t{\n\t\t\t\t$tmp .= sprintf('%08b', ord($data[$i+2]));\n\t\t\t}\n\t\t\t$dataLength = bindec($tmp) + $payloadOffset;\n\t\t\tunset($tmp);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$mask = substr($data, 2, 4);\n\t\t\t$payloadOffset = 6;\n\t\t\t$dataLength = $payloadLength + $payloadOffset;\n\t\t}\n\n\t\tif($isMasked === true)\n\t\t{\n\t\t\tfor($i = $payloadOffset; $i < $dataLength; $i++)\n\t\t\t{\n\t\t\t\t$j = $i - $payloadOffset;\n\t\t\t\tif(isset($data[$i]))\n\t\t\t\t{\n\t\t\t\t\t$unmaskedPayload .= $data[$i] ^ $mask[$j % 4];\n\t\t\t\t}\n\t\t\t}\n\t\t\t$decodedData['payload'] = $unmaskedPayload;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$payloadOffset = $payloadOffset - 4;\n\t\t\t$decodedData['payload'] = substr($data, $payloadOffset);\n\t\t}\n\n\t\treturn $decodedData;\n\t}\n}\n"
  },
  {
    "path": "tests/include/lib/src/ServerManager.php",
    "content": "<?php\n/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2017 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\nnamespace SwooleTest;\n\n\nclass ServerManager\n{\n    protected $host;\n    protected $file;\n    public $port;\n\n    /**\n     * ServerManager constructor.\n     * @param $file\n     * @throws \\Exception\n     */\n    function __construct($file)\n    {\n        if (!is_file($file))\n        {\n            throw new \\Exception(\"server file [$file] not exists.\");\n        }\n        $this->file = $file;\n    }\n\n    function listen($host = '127.0.0.1', $port = 0)\n    {\n        $this->port = $port == 0 ? get_one_free_port() : $port;\n        $this->host = $host;\n    }\n\n    function run($debug = false)\n    {\n        return start_server($this->file, $this->host, $this->port, \"/dev/null\", null, null, $debug);\n    }\n}\n"
  },
  {
    "path": "tests/include/lib/src/TcpStat.php",
    "content": "<?php\n/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2017 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\n\nnamespace SwooleTest;\n\nclass TcpStat\n{\n    const SS_NETSTAT_TCP_STATE_MAP = [\n        \"established\"   => \"ESTABLISHED\",\n        \"syn-sent\"      => \"SYN_SENT\",\n        \"syn-recv\"      => \"SYN_RCVD\",\n        \"fin-wait-1\"    => \"FIN_WAIT_1\",\n        \"fin-wait-2\"    => \"FIN_WAIT_2\",\n        \"time-wait\"     => \"TIME_WAIT\",\n        \"closed\"        => \"CLOSED\",\n        \"close-wait\"    => \"CLOSE_WAIT\",\n        \"last-ack\"      => \"LAST_ACK\",\n        \"listen\"        => \"LISTEN\",\n        \"closing\"       => \"CLOSING\",\n    ];\n\n    public static function xCount($path)\n    {\n        if (PHP_OS === \"Darwin\") {\n            $n = `netstat -x | grep $path | wc -l`;\n            return intval(trim($n));\n        } else {\n            $n = `ss -x src $path | wc -l`;\n            return intval(trim($n)) - 1;\n        }\n    }\n\n    public static function count($host, $port, $states = [\"established\", \"time-wait\", \"close-wait\"]) {\n        if (!ip2long($host)) {\n            $host = gethostbyname($host);\n        }\n\n        $pipe = \"wc -l\";\n        $func = PHP_OS === \"Darwin\" ?  \"netstat\" : \"ss\";\n        $states = static::fmtTcpState($states, $func);\n\n        $info = [];\n        foreach ($states as $state) {\n            $ret = call_user_func([static::class, $func], $host, $port, $state, $pipe);\n            $info[$state] = intval(trim($ret)) - 1;\n        }\n\n        return $info;\n    }\n\n    private static function netstat($host, $port, $state, $pipe = \"\")\n    {\n        if ($pipe) {\n            $pipe = \" | $pipe\";\n        }\n        // $4 src $5 dst $6 stats\n        return `netstat -an | awk '(\\$5 == \"$host.$port\" && \\$6 == \"$state\") || NR==2  {print \\$0}' $pipe`;\n    }\n\n    private static function ss($host, $port, $state, $pipe = \"\")\n    {\n        if ($pipe) {\n            $pipe = \" | $pipe\";\n        }\n        return `ss state $state dst $host:$port $pipe`;\n    }\n\n    private static function fmtTcpState(array $states, $type)\n    {\n        $from = $to = [];\n        if ($type === \"ss\") {\n            $to = static::SS_NETSTAT_TCP_STATE_MAP;\n            $from = array_flip($to);\n        } else if ($type === \"netstat\") {\n            $from = static::SS_NETSTAT_TCP_STATE_MAP;\n            $to = array_flip($from);\n        }\n\n        $ret = [];\n        foreach ($states as $state) {\n            if (isset($to[$state])) {\n                $ret[] = $state;\n            } else if (isset($from[$state])) {\n                $ret[] = $from[$state];\n            }\n        }\n        return $ret;\n    }\n}\n"
  },
  {
    "path": "tests/include/lib/src/ThreadManager.php",
    "content": "<?php\n\nnamespace SwooleTest;\n\nuse Swoole\\Thread;\n\nclass ThreadManager extends ProcessManager\n{\n    public $useConstantPorts = true;\n\n    function run($redirectStdout = false): void\n    {\n        $args = Thread::getArguments();\n        if (empty($args)) {\n            ($this->parentFunc)();\n        } else {\n            ($this->childFunc)(...$args);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/include/lib/src/WaitRef.php",
    "content": "<?php\n/*\n +----------------------------------------------------------------------+\n | Swoole                                                               |\n +----------------------------------------------------------------------+\n | Copyright (c) 2012-2017 The Swoole Group                             |\n +----------------------------------------------------------------------+\n | This source file is subject to version 2.0 of the Apache license,    |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n | If you did not receive a copy of the Apache2.0 license and are unable|\n | to obtain it through the world-wide-web, please send a note to       |\n | license@swoole.com so we can mail you a copy immediately.            |\n +----------------------------------------------------------------------+\n | Author: Tianfeng Han  <rango@swoole.com>                             |\n +----------------------------------------------------------------------+\n */\n\nnamespace SwooleTest;\n\nuse Swoole;\n\nclass WaitRef\n{\n    private $cid;\n\n    public function __destruct()\n    {\n        Swoole\\Coroutine::resume($this->cid);\n    }\n\n    static function create()\n    {\n        return new static();\n    }\n\n    static function wait(WaitRef &$wr)\n    {\n        $wr->cid = Swoole\\Coroutine::getCid();\n        $wr = null;\n        Swoole\\Coroutine::yield();\n    }\n}\n"
  },
  {
    "path": "tests/include/lib/src/responder/get.php",
    "content": "<?php\n$test = isset($_GET['test']) ? $_GET['test'] : null;\nswitch ($test) {\n    case 'post':\n        var_dump($_POST);\n        break;\n    case 'getpost':\n        var_dump($_GET);\n        var_dump($_POST);\n        break;\n    case 'referer':\n        echo $_SERVER['HTTP_REFERER'];\n        break;\n    case 'auto_referer':\n        header('HTTP/1.1 302 Found');\n        header('location:get.php?test=referer');\n        exit;\n        break;\n    case 'useragent':\n        echo $_SERVER['HTTP_USER_AGENT'];\n        break;\n    case 'httpversion':\n        echo $_SERVER['SERVER_PROTOCOL'];\n        break;\n    case 'cookie':\n        echo $_COOKIE['foo'];\n        break;\n    case 'cookie_get':\n        echo json_encode($_COOKIE);\n        break;\n    case 'cookie_set':\n        echo json_encode(setcookie($_GET['cookie_name'], $_GET['cookie_value'], $_GET['cookie_expire']));\n        break;\n    case 'encoding':\n        echo $_SERVER['HTTP_ACCEPT_ENCODING'];\n        break;\n    case 'contenttype':\n        header('Content-Type: text/plain;charset=utf-8');\n        break;\n    case 'file':\n        if (isset($_FILES['file'])) {\n            echo $_FILES['file']['name'] . '|' . $_FILES['file']['type'] . '|' . $_FILES['file']['size'];\n        }\n        break;\n    case 'method':\n        echo $_SERVER['REQUEST_METHOD'];\n        break;\n    case 'redirect_301':\n        header('HTTP/1.1 301 Moved Permanently');\n        header('location:get.php?test=getpost');\n        exit;\n        break;\n    case 'redirect_302':\n        header('HTTP/1.1 302 Found');\n        header('location:get.php?test=getpost');\n        exit;\n        break;\n    case 'redirect_307':\n        header('HTTP/1.1 307 Temporary Redirect');\n        header('location:get.php?test=getpost');\n        exit;\n        break;\n    case 'redirect_308':\n        header('HTTP/1.1 308 Permanent Redirect');\n        header('location:get.php?test=getpost');\n        exit;\n        break;\n    case 'header_body':\n        header('abc: 123');\n        echo \"a\\nb\\nc\";\n        break;\n    case 'input':\n        echo file_get_contents('php://input');\n        break;\n    default:\n        echo \"Hello World!\\n\";\n        echo \"Hello World!\";\n        break;\n}\n"
  },
  {
    "path": "tests/include/macos/phpstorm.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nimport socket\nimport struct\nimport sys\nimport os\nimport time\n\n# see com.intellij.idea.SocketLock for the server side of this interface\n\nRUN_PATH = u'/Applications/PhpStorm.app'\nCONFIG_PATH = u'~/Library/Preferences/PhpStorm2017.3'\nSYSTEM_PATH = u'~/Library/Caches/PhpStorm2017.3'\n\n\ndef print_usage(cmd):\n    print(('Usage:\\n' +\n           '  {0} -h | -? | --help\\n' +\n           '  {0} [project_dir]\\n' +\n           '  {0} [-l|--line line] [project_dir|--temp-project] file[:line]\\n' +\n           '  {0} diff <left> <right>\\n' +\n           '  {0} merge <local> <remote> [base] <merged>').format(cmd))\n\n\ndef process_args(argv):\n    args = []\n\n    skip_next = False\n    for i, arg in enumerate(argv[1:]):\n        if arg == '-h' or arg == '-?' or arg == '--help':\n            print_usage(argv[0])\n            exit(0)\n        elif i == 0 and (arg == 'diff' or arg == 'merge' or arg == '--temp-project'):\n            args.append(arg)\n        elif arg == '-l' or arg == '--line':\n            args.append(arg)\n            skip_next = True\n        elif skip_next:\n            args.append(arg)\n            skip_next = False\n        else:\n            path = arg\n            if ':' in arg:\n                file_path, line_number = arg.rsplit(':', 1)\n                if line_number.isdigit():\n                    args.append('-l')\n                    args.append(line_number)\n                    path = file_path\n            args.append(os.path.abspath(path))\n\n    return args\n\n\ndef try_activate_instance(args):\n    port_path = os.path.join(CONFIG_PATH, 'port')\n    token_path = os.path.join(SYSTEM_PATH, 'token')\n    if not (os.path.exists(port_path) and os.path.exists(token_path)):\n        return False\n\n    with open(port_path) as pf:\n        port = int(pf.read())\n    with open(token_path) as tf:\n        token = tf.read()\n\n    s = socket.socket()\n    s.settimeout(0.3)\n    try:\n        s.connect(('127.0.0.1', port))\n    except (socket.error, IOError):\n        return False\n\n    found = False\n    while True:\n        try:\n            path_len = struct.unpack('>h', s.recv(2))[0]\n            path = s.recv(path_len).decode('utf-8')\n            if os.path.abspath(path) == os.path.abspath(CONFIG_PATH):\n                found = True\n                break\n        except (socket.error, IOError):\n            return False\n\n    if found:\n        cmd = 'activate ' + token + '\\0' + os.getcwd() + '\\0' + '\\0'.join(args)\n        if sys.version_info.major >= 3: cmd = cmd.encode('utf-8')\n        encoded = struct.pack('>h', len(cmd)) + cmd\n        s.send(encoded)\n        time.sleep(0.5)  # don't close the socket immediately\n        return True\n\n    return False\n\n\ndef start_new_instance(args):\n    if sys.platform == 'darwin':\n        if len(args) > 0:\n            args.insert(0, '--args')\n        os.execvp('open', ['-a', RUN_PATH] + args)\n    else:\n        bin_file = os.path.split(RUN_PATH)[1]\n        os.execv(RUN_PATH, [bin_file] + args)\n\n\nide_args = process_args(sys.argv)\nif not try_activate_instance(ide_args):\n    start_new_instance(ide_args)"
  },
  {
    "path": "tests/include/skipif.inc",
    "content": "<?php\ndefine('SWOOLE_COLOR_RED', 1);\ndefine('SWOOLE_COLOR_GREEN', 2);\ndefine('SWOOLE_COLOR_YELLOW', 3);\ndefine('SWOOLE_COLOR_BLUE', 4);\ndefine('SWOOLE_COLOR_MAGENTA', 5);\ndefine('SWOOLE_COLOR_CYAN', 6);\ndefine('SWOOLE_COLOR_WHITE', 7);\n\nskip(\n    'Linux only',\n    str_starts_with(PHP_OS, 'WIN'),\n    SWOOLE_COLOR_RED\n);\nskip(\n    'Swoole extension is required',\n    !extension_loaded('swoole'),\n    SWOOLE_COLOR_RED\n);\nskip(\n    'Please set \"swoole.use_shortname = On\"',\n    !SWOOLE_USE_SHORTNAME,\n    SWOOLE_COLOR_RED\n);\n\n(function () {\n    global $argv;\n    skip_if_php_version_lower_than('7.0');\n    if (!getenv('PHPT') && str_ends_with($argv[0], '.php')) {\n        skip('please read ' . dirname(__FILE__, 2) . '/README.md and try again');\n    }\n    if (preg_match('#/(swoole_[^/]+)/#', $_SERVER['SCRIPT_FILENAME'], $dir)) {\n        $dir = $dir[1];\n        if (stripos($dir, 'http2') !== false) {\n            skip_if_no_http2();\n        }\n    }\n})();\n\nfunction swoole_color(string $content, int $color): string\n{\n    return \"\\033[3{$color}m{$content}\\033[0m\";\n}\n\nfunction skip(string $reason, bool $is_skip = true, int $color = SWOOLE_COLOR_YELLOW)\n{\n    if ($is_skip) {\n        exit('skip ' . swoole_color($reason, $color) . \"\\n\");\n    }\n}\n\nfunction skip_if_php_version_lower_than($require_version = '7.0')\n{\n    if (version_compare(PHP_VERSION, $require_version, '<')) {\n        skip('need php version >= ' . $require_version);\n    }\n}\n\nfunction skip_if_php_version_ge($require_version = '7.0')\n{\n    if (version_compare(PHP_VERSION, $require_version, '>=')) {\n        skip('need php version >= ' . $require_version);\n    }\n}\n\nfunction skip_if_php_version_between($a, $b)\n{\n    if (version_compare(PHP_VERSION, $a, '>=') && version_compare(PHP_VERSION, $b, '<=')) {\n        skip(\"unsupported php version between {$a} and {$b}\");\n    }\n}\n\nfunction skip_if_not_debug_version()\n{\n    skip('only in swoole debug version', !SWOOLE_DEBUG);\n}\n\nfunction skip_if_ini_bool_equal_to(string $name, bool $value)\n{\n    if (((bool)ini_get($name)) === $value) {\n        $value = $value ? 'enable' : 'disable';\n        skip(\"{$name} is {$value}\");\n    }\n}\n\nfunction skip_if_no_nghttp(): void\n{\n    if (!str_contains(shell_exec(\"nghttp --version 2>&1\"), 'nghttp2')) {\n        skip('no nghttp');\n    }\n}\n\nfunction skip_if_constant_not_defined(string $constant_name): void\n{\n    require_once __DIR__ . '/config.php';\n    skip(\"{$constant_name} is not defined\", !defined($constant_name));\n}\n\nfunction skip_if_function_not_exist(string $function_name)\n{\n    skip(\"{$function_name} not exist\", !function_exists($function_name));\n}\n\nfunction skip_if_class_not_exist(string $class_name)\n{\n    skip(\"{$class_name} not exist\", !class_exists($class_name, false));\n}\n\nfunction skip_if_extension_not_exist(string $extension_name)\n{\n    skip(\"{$extension_name} not exist\", !extension_loaded($extension_name));\n}\n\nfunction skip_if_file_not_exist(string $filename)\n{\n    skip(\"file {$filename} is not exist\", !file_exists($filename));\n}\n\nfunction skip_if_command_not_found(string $command)\n{\n    skip(\"command {$command} not found\", empty(shell_exec(\"{$command} --help 2>&1 | grep -i usage\")));\n}\n\nfunction skip_if_no_ssl()\n{\n    if (!defined(\"SWOOLE_SSL\")) {\n        skip('no ssl');\n    }\n}\n\nfunction skip_if_openssl_version_lower_than($version = '1.0.0')\n{\n    skip_if_no_ssl();\n    $exist = preg_match('/openssl => openssl ([\\d\\.]+)/i', shell_exec(\"php --ri swoole\"), $match);\n    assert($exist);\n    if (version_compare($match[1], $version, '<')) {\n        skip(\"openssl version {$match[1]} is lower than {$version}\");\n    }\n}\n\nfunction skip_if_no_http2()\n{\n    skip('no http2', !class_exists(Swoole\\Http2\\Request::class, false));\n}\n\n\nfunction skip_if_no_ipv6()\n{\n    skip('no ipv6 address', !@stream_socket_server('tcp://[::1]:0'));\n}\n\nfunction skip_if_no_top()\n{\n    skip_if_darwin();\n    skip('top provided by busybox (not support)', !empty(shell_exec(\"top --help 2>&1 | grep -i busybox\")));\n    skip('no top', empty(shell_exec(\"top help 2>&1 | grep -i usage\")));\n}\n\nfunction skip_if_darwin()\n{\n    skip('not support on darwin', stripos(PHP_OS, 'Darwin') !== false);\n}\n\nfunction skip_if_darwin_todo($msg = 'Need to review')\n{\n    skip($msg, getenv('MACOS_DEV') === false && stripos(PHP_OS, 'Darwin') !== false);\n}\n\nfunction skip_if_not_darwin()\n{\n    skip('only support darwin', stripos(PHP_OS, 'Darwin') === false);\n}\n\nfunction skip_if_nts()\n{\n    skip('not support in nts', !defined('SWOOLE_THREAD'));\n}\n\nfunction skip_if_not_linux()\n{\n    skip('only support linux', PHP_OS !== 'Linux');\n}\n\nfunction skip_if_musl_libc()\n{\n    skip('not support when use musl libc', !empty(shell_exec(\"ldd 2>&1 | grep -i musl\")));\n}\n\nfunction skip_if_no_process_affinity()\n{\n    skip('no process affinity', !method_exists(Swoole\\Process::class, 'setaffinity'));\n}\n\nfunction skip_if_in_valgrind(string $reason = 'valgrind is too slow')\n{\n    skip($reason, getenv('USE_ZEND_ALLOC') === '0');\n}\n\nfunction skip_if_in_ci(string $reason = 'not support in CI')\n{\n    skip($reason, !!getenv('GITHUB_ACTIONS') or file_exists('/.cienv'));\n}\n\nfunction skip_if_in_docker(string $reason = 'not support in docker')\n{\n    skip($reason, file_exists('/.dockerenv'));\n}\n\nfunction skip_if_offline()\n{\n    if (getenv(\"SKIP_ONLINE_TESTS\")) {\n        skip('it\\'s the online test but you are offline');\n    }\n}\n\nfunction skip_if_no_proxy(string $host, string $port)\n{\n    skip('no available proxy', !check_tcp_port($host, $port));\n}\n\nfunction skip_if_no_http_proxy()\n{\n    require_once __DIR__ . '/config.php';\n    skip_if_no_proxy(HTTP_PROXY_HOST, HTTP_PROXY_PORT);\n}\n\nfunction skip_if_no_socks5_proxy()\n{\n    require_once __DIR__ . '/config.php';\n    skip_if_no_proxy(SOCKS5_PROXY_HOST, SOCKS5_PROXY_PORT);\n}\n\nfunction skip_if_pdo_not_support_mysql8()\n{\n    skip_if_no_database();\n    require_once __DIR__ . '/config.php';\n    try {\n        new PDO(\n            \"mysql:host=\" . MYSQL_SERVER_HOST . \";port=\" . MYSQL_SERVER_PORT . \";dbname=\" . MYSQL_SERVER_DB . \";charset=utf8\",\n            MYSQL_SERVER_USER, MYSQL_SERVER_PWD\n        );\n    } catch (\\PDOException $e) {\n        if ($e->getCode() === 2054) {\n            skip('pdo auth not support');\n        }\n    }\n}\n\nfunction skip_if_no_database()\n{\n    skip(\"no database\", !!getenv('SWOOLE_CI_IN_MACOS'));\n}\n\nfunction skip_if_not_mysql8()\n{\n    skip_if_no_database();\n    require_once __DIR__ . '/config.php';\n    $skip = true;\n    Swoole\\Coroutine\\run(function () use (&$skip) {\n        try {\n            if (preg_match(\n                '/caching_sha2_password/',\n                httpGetBody('http://' . MYSQL_SERVER_HOST . ':' . MYSQL_SERVER_PORT))\n            ) {\n                $skip = false;\n            }\n        } catch (Throwable $throwable) {\n            // ignored\n        }\n    });\n    if ($skip) {\n        skip('require mysql8');\n    }\n}\n\nfunction skip_if_not_redis5()\n{\n    require_once __DIR__ . '/config.php';\n    $skip = false;\n    go(function () use (&$skip) {\n        $redis = new Swoole\\Coroutine\\Redis();\n        $redis->setOptions(['compatibility_mode' => true]);\n        $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT);\n        $info = $redis->eval(\"return redis.call('info')\");\n        if (preg_match('/redis_version:(\\d+)/U', $info, $version)) {\n            if ($version[1] < 5) {\n                $skip = true;\n            }\n        }\n    });\n    Swoole\\Event::wait();\n    if ($skip) {\n        skip('require redis5');\n    }\n}\n\nfunction skip_unsupported(string $message = '')\n{\n    skip($message ?: 'the test cannot continue to work for some implementation reasons');\n}\n\nfunction skip_if_no_coroutine_get_execute_time()\n{\n    skip('no Swoole\\Coroutine::getExecuteTime', !method_exists(Swoole\\Coroutine::class, 'getExecuteTime'));\n}\n\nfunction skip_if_no_ftp()\n{\n    require_once __DIR__ . '/config.php';\n    skip('no available ftp server', !check_tcp_port(FTP_HOST, FTP_PORT));\n}\n\nfunction skip_if_not_root()\n{\n    skip('not root user', posix_geteuid() !== 0);\n}\n\nfunction skip_if_no_iouring() {\n    skip('no io-uring supports', !defined('SWOOLE_IOURING_DEFAULT'));\n}\n\nfunction skip_if_not_defined(string $name)\n{\n    skip(\"`$name` is not defined\", !defined($name));\n}\n"
  },
  {
    "path": "tests/include/ssl_certs/ca-cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDKDCCAhACCQCEAJc680CvRDANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD\nTjELMAkGA1UECAwCQkoxCzAJBgNVBAcMAkJKMQ8wDQYDVQQKDAZzd29vbGUxDzAN\nBgNVBAsMBnN3b29sZTELMAkGA1UEAwwCQ0EwHhcNMTgxMjA3MDc0OTA1WhcNMjgx\nMjA0MDc0OTA1WjBWMQswCQYDVQQGEwJDTjELMAkGA1UECAwCQkoxCzAJBgNVBAcM\nAkJKMQ8wDQYDVQQKDAZzd29vbGUxDzANBgNVBAsMBnN3b29sZTELMAkGA1UEAwwC\nQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN8IQ468DyDRIIMSx4\nv2Apmrbe1vIHYNOfW7DexM+DK8TX46l3v7SsLCHCYrKmEnl2SoDyjkDBQukrZRwQ\nlZ2VaNmllK3A3HQVl2tdFWHkLz/Qdq5bLnUEz1/tI33DcjJ8fo70wpz6pAgEsHZt\n+AX1MbZVWJDbMpH8SHaJduHsOD1WQo0qiJF1Z6PdrP9SPD2Z9g56b/TKA7BU9sd+\nN7QsChqQHFWjsyC80Hl6f2c+YaYjd4/KbunzIvhFIuK/bOGB0U+Cu2I8Fq4yGHFA\nF/i/+Rthabc9jxWsbbAMFB2b/TkDhuFrjH+J7Bbqd8cZdUshuQv6l3wU4m5bv5Zm\n7s1rAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABkr7mqcRBJEBG+argNC0n+IY6Df\nkd3YRgNhAdedUfyrG6C+SwaFiAkhgZQH8NuHEzANAnLZe8DlqYEsmWJGRT7jy0Pa\nI8a8PA8PajSAg3tp6wR7ST2E4ZiQwjJsDxaFI+yDMiP5uXyHEA9WHQZ9OiWRDO/9\nX8dJw/uuEk4dXdpyqTfNleHN/5TGJP07qrPSRr0+1+HVw1YZjgKsn7ivgAoaNxEe\nfDnhoF8WncswJLP3e5D0EG6zrod/8r6H009CJWGadaGWEtcvOoGq6E97mCwRrKmI\n25Fr/izhV9gvf/Uj+nDZfvJwdx7GjxoHM+D3uc/2D8AxRkAtIPcABTyvPvI=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/include/ssl_certs/ca-key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAzfCEOOvA8g0SCDEseL9gKZq23tbyB2DTn1uw3sTPgyvE1+Op\nd7+0rCwhwmKyphJ5dkqA8o5AwULpK2UcEJWdlWjZpZStwNx0FZdrXRVh5C8/0Hau\nWy51BM9f7SN9w3IyfH6O9MKc+qQIBLB2bfgF9TG2VViQ2zKR/Eh2iXbh7Dg9VkKN\nKoiRdWej3az/Ujw9mfYOem/0ygOwVPbHfje0LAoakBxVo7MgvNB5en9nPmGmI3eP\nym7p8yL4RSLiv2zhgdFPgrtiPBauMhhxQBf4v/kbYWm3PY8VrG2wDBQdm/05A4bh\na4x/iewW6nfHGXVLIbkL+pd8FOJuW7+WZu7NawIDAQABAoIBAG8NrW+4ikK9E6VB\nRzcSFHw7BzRh+wBJkbUUz+tBfJnCp5K4HTILOhXCcq4NZUMzITbN9H1Ygsj1GSIi\nkyDKXcEtWnj1FUG0YBEop2f52OEhLatv5JQdGFNwtMz/X5le4qTJ5rNMozRx8jT9\nJ9AjeKsBlM6vROY/fgPq26lDZ49mx9Rp8tIV4lps9cZ4YrsMu47tXTFjK2dYyVdh\nNbMNE1tgfIQd+ZH5HpLcEZas72y7djRC3UNRqlHJ6CvP1Kb98+ILa+CLj94mN+NV\nVvoqzLHLk6n7nam2FKVHMfHl0fSmQ1TQmI3dyyh62I7wpYAPi2Sk+s8UQvjRjKwI\nLCJkGZECgYEA+8DLpKnIvWKf864nknEt6XF9gV/Ft0Mld61rzkTJy5zVjD9FXqJl\nF8Cfn6RfSb1dw+wqXpoJd2R+WRTI60jCe9tfY8G7Kpmz6jV+At4jQ932QQyNRJnG\nyVYn8wz4XsPGgfz0OaFDZvIlIcQIEfkEOaow019+sU6K9S6oZsILvHMCgYEA0Wnf\nmZFlw6C9cbSb1f1Nl0YGm1NLMJuIcYudRx8OfcbNSlRFCQrVIjNcjIpnNE1ltd89\nPrRZJnkkX0lxkg9jPFqSeYcvhAksZrhO0a7ZeKHyH7F2c6E6LIqwZnoZ3tRUWzm+\nM7L5Bh6TS5G0/Y0tPB3UngRy6SWmkjTI1kCrJSkCgYEA+buHh4GRCyUxjHXe9Wrm\nY2NcX6EUrbWjDjPu9D/SefPK9oOxGa44YDaJYcCZcbmysO6uHQfqihAbMdznblbP\n7jNwTbHZK+oqyhNkPA4Fp4YgiOidnkZ5JjIcEQN1wfOtEDdQQXbSOOXNpdAjPUjr\nQxSruJJLSDGksJEnJkApijcCgYAvl24XinGkKe2j37XixfRSQrnRpvZZj59MnrwZ\no7c5YmrZT5l42pthcGbCEdouisBoutlCXN493h4kAZ7r4M9esf7D27MywJr0pUJU\naPZHchaCmWQgFy4PXV2FjI5Ak+Sv7smJGk151I0JCY4maWU0WlstpjkvPz1B0Pkr\np/q9MQKBgQDjVs5yfWb8PEwlw5oqkk8IyNVHKMz/62r7tfuT1gGr9OepWXv900fn\nZ7TavI6tr0x3zCblqTSDROUYYV6sadMoCMrHQ6yihwJNM4Htp2Qi7tJJs1onC8wE\np2gXL1fPFq0sjSaRcZhaxSrpF2KAfIPmoC17Li30A2iVe2FyLIQZvg==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/include/ssl_certs/ca-req.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIICmzCCAYMCAQAwVjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkJKMQswCQYDVQQH\nDAJCSjEPMA0GA1UECgwGc3dvb2xlMQ8wDQYDVQQLDAZzd29vbGUxCzAJBgNVBAMM\nAkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzfCEOOvA8g0SCDEs\neL9gKZq23tbyB2DTn1uw3sTPgyvE1+Opd7+0rCwhwmKyphJ5dkqA8o5AwULpK2Uc\nEJWdlWjZpZStwNx0FZdrXRVh5C8/0HauWy51BM9f7SN9w3IyfH6O9MKc+qQIBLB2\nbfgF9TG2VViQ2zKR/Eh2iXbh7Dg9VkKNKoiRdWej3az/Ujw9mfYOem/0ygOwVPbH\nfje0LAoakBxVo7MgvNB5en9nPmGmI3ePym7p8yL4RSLiv2zhgdFPgrtiPBauMhhx\nQBf4v/kbYWm3PY8VrG2wDBQdm/05A4bha4x/iewW6nfHGXVLIbkL+pd8FOJuW7+W\nZu7NawIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAMs2wMLBDqw2TxBy2wIM2ytp\nLupk/3QVkvMpBIiYD7Jbod5YRjkV+LagLq/KE10y0jWSGuQSpbJHypb/bXsvKcU7\n18uzM/CU08xz5WWQ7LqAvITT/4GHT/WNLn38WBkIG6qHYeaaaLcGdo53KlbQiYqq\n5An3sxVin1C5dHR2ew5bKMNh+Zfq8kNXIPQbKd2KcEDKPYvt7Zb92SOijpkK/SVb\nhWgHkGsyFothmIpdKarOsQ2E2SSyTtNUKws/ZcRq1KQs7qAxNT7As4vBAfCGv0Yv\ns7gohLXDPZDWvoXmRWOPiY7nYlTzxBDKXEwtEqN0ZJcQv9OHJveQ6SSK3o75EDM=\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "tests/include/ssl_certs/ca.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFijCCA3KgAwIBAgIBATANBgkqhkiG9w0BAQsFADBJMQswCQYDVQQGEwJDTjEL\nMAkGA1UECAwCTkUxCzAJBgNVBAoMAk5FMQ8wDQYDVQQLDAZSb290Q0ExDzANBgNV\nBAMMBlJvb3RDQTAgFw0xOTEyMTMwNjU3MjNaGA8yMTE5MTExOTA2NTcyM1owSTEL\nMAkGA1UEBhMCQ04xCzAJBgNVBAgMAk5FMQswCQYDVQQKDAJORTEPMA0GA1UECwwG\nUm9vdENBMQ8wDQYDVQQDDAZSb290Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\nggIKAoICAQDAEChFm9jAOzkqunXYIqoLMJ5GlpaQpznDOaA5eQ/kRE1eSq0SkkPv\nzfFOMdGcjvMSnke7xcUx0n4N4ATSlr/J0MXV3/4ATDmtP663aVyQRM+SDxSYRIBV\nepjKv8f7v9KMV1YgsnJD9KLaRHf2mZkVCEtD/eEAOaXyuk3IZgITnG6mynhhxJ9T\nICRDtqPZ7b2NBxk+DEjmhruQ+oEFwApMtLuIoeETCXfRu48b+ee4lHSzrAMZKKGy\nOPQ5BLA5GJXdRx/ien5BViHsD8bZygmAkTFwayVXtr8HG4OZMnaOTjJhCBvt5GMp\n2digT1bcwhYxh70NQ8FIQipRCOxsBObVxDglqVLLdjmREpD8OrGA4B++wNjSf8qH\nliX+Idu1CCGrNajKDDx2dFcaZW0e6ZpCTZt8QUC9hOfBUUjIASqw04dbPlLCzYAW\nVLPFAmHf/wAwjDoNLKtSbJxAPRW/uAorUnd90rAYWphH3aewgvu/njvzdLRBiyif\n13Xo6/dmuWpIA7uGHyUtZF48GmVa+9JMVIVW/UF9G2Hlzi/CN95Bn/vumIvFJEu3\nP9wqn+Ps/MlN4IL0VkRvPi7JYMyPQkCwF5QuI8pyzrOrUNKio1wtiehtY1YI8kVJ\nBy6bChiJn5/QAm/JUSfjnnfZs8sKwgrm9vUMyDs7aTMDkWkTUXdN/wIDAQABo3sw\neTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBD\nZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUSNkyUSoxslFEjb9QtExEqVeYLsAwHwYDVR0j\nBBgwFoAUSNkyUSoxslFEjb9QtExEqVeYLsAwDQYJKoZIhvcNAQELBQADggIBAElS\nC/4sgbqaed1BG2aKQv/iOXT+bZeIavp+ZbUGHm968MUREYcCVLfweyHLVasWpg7T\n82x07kkabfDtYPkVJM23JxX6cVzpktl7jBTnq4jMUSRi/Ueig4jBG8Hjlp/Y6Z37\n0Gr8OJaUorbmmZLDTMytW++Dqu7x/QLZ/zG24zQRFP+B6KyHjDEuhrM84KZoUMwI\n3U70EwkGgiQixXnuJ1l1mhoA52c/ettMEp8x/bJc6/thNQtU3NrdrnzEzBoYNevQ\nXNgDVI6pY8cYvUr/gfiRra+ZJJ2rdYhI6quWSxwvHCvmDQcfGGYH1v41TRZOjVYd\n+Kz2dpeOtsVYMdMW0UzsTD+fwo+2H6pOm3Y4dF/oaGlzu5oEg7aPolsX/3libt8s\nboPdeKmyh4MPZHIlHcXsPWQy0nl9Xj8LOf5OTAZOfyw6AaB0G4xVhgOiMa53fo9O\nR/mlBRr7j2oFu+duV5/htIDPO/FWT4KpWRg5ChdNkFckibVmaU/6RLdx/60aOZsh\nmNl3kqnPc+KWR/0o5xNuojg1PAU+9tdUxT/HEPdXEoedu2nzM1x4OTHhhqw2P2S/\nJb5TRfgsOav9LeYGGb3EKrcVrEt4iwRArLenMo2Hf5MwaRkHoIB+zgk6eEKzy6uY\nJCJ5q215xxaQrr/qXTW0yFoy2PHK8LNfZ78IGD2l\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/include/ssl_certs/ca.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIIBlzCCAQACAQAwVzELMAkGA1UEBhMCQ04xETAPBgNVBAgTCFpoZWppYW5nMREw\nDwYDVQQHEwhIYW5nemhvdTEOMAwGA1UEChMFTXkgQ0ExEjAQBgNVBAMTCWxvY2Fs\naG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAkkn6a1CZrX/cDofktWZT\nGDMIwIVfMsSOCSTBGTeQoCc7i/Zn6minMyJsD5+s3W6ctpja0JlwKt53ZVgNgrm9\nH6NZyKNKqdWY8ElZwKE7nJ1LGlpS6nbY+M2VVedZLetU66+pi5/tAOsewRXkDemI\n4xi6BRU4jIlvT84ovReklXUCAwEAAaAAMA0GCSqGSIb3DQEBBQUAA4GBAI+msopi\nc6QIu0yYwPix0DyC8FEONih0kOQ//oe1bMLwbox/uxSoRmoJGiOdF7jqZceA0k8H\n59SWMGzIUt5MRhDrye/7/LedXDVdCPacYLx7OzIlkBv9Dklk9o26jjT9u3Il7fQx\nOfcCm2Lk+cwv3xXm6ZtEmklqKbnAnKd1oBfv\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "tests/include/ssl_certs/ca.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIJKQIBAAKCAgEAwBAoRZvYwDs5Krp12CKqCzCeRpaWkKc5wzmgOXkP5ERNXkqt\nEpJD783xTjHRnI7zEp5Hu8XFMdJ+DeAE0pa/ydDF1d/+AEw5rT+ut2lckETPkg8U\nmESAVXqYyr/H+7/SjFdWILJyQ/Si2kR39pmZFQhLQ/3hADml8rpNyGYCE5xupsp4\nYcSfUyAkQ7aj2e29jQcZPgxI5oa7kPqBBcAKTLS7iKHhEwl30buPG/nnuJR0s6wD\nGSihsjj0OQSwORiV3Ucf4np+QVYh7A/G2coJgJExcGslV7a/BxuDmTJ2jk4yYQgb\n7eRjKdnYoE9W3MIWMYe9DUPBSEIqUQjsbATm1cQ4JalSy3Y5kRKQ/DqxgOAfvsDY\n0n/Kh5Yl/iHbtQghqzWoygw8dnRXGmVtHumaQk2bfEFAvYTnwVFIyAEqsNOHWz5S\nws2AFlSzxQJh3/8AMIw6DSyrUmycQD0Vv7gKK1J3fdKwGFqYR92nsIL7v54783S0\nQYson9d16Ov3ZrlqSAO7hh8lLWRePBplWvvSTFSFVv1BfRth5c4vwjfeQZ/77piL\nxSRLtz/cKp/j7PzJTeCC9FZEbz4uyWDMj0JAsBeULiPKcs6zq1DSoqNcLYnobWNW\nCPJFSQcumwoYiZ+f0AJvyVEn45532bPLCsIK5vb1DMg7O2kzA5FpE1F3Tf8CAwEA\nAQKCAgEAps9hqtNotcy6IfpAT7ALsBxR20z/CDO1X3G1TzVuL/uCltqU5wRSLPzZ\nD+kE/MzLIOiHtPZYBqWjBboDdkOn30iMCu24VdneXjJpNLM1ybd58IlYgcc9DwCj\no7RdpLE91NYl39AcnhjjOF8vMebFphbGCA+AVAm9r8tPszWlqBK9p+9CDugO2MuZ\ns5X1SVnKv6niqplQ16c9UPXBCZV5XJW8KzctnLMkdexIpyutrgqCMWq8ZiV8Cqac\ntsjzpSNNRhp3/uQUs/pzC+vH8XAgQKHaJqFolX4K+8lRysShY4dXCh9Y4ozx829Z\nTiKadrG7RNl3wQWP4YpV5nuxmz/Gm3Gr/5VIIyoiDm8H1D7zC06JZUOTms+c7lU+\nkv/eKMI9nhgfmoG0skqhYrRD5jTCinqp3tb9nb92Po2mlhRoUPA9d6QkWrccqGJI\noIQv+B9N6si81LWTua7F//nqyRy9la65529pD90OGUROh73+UmLPLGK/mPRiANRv\nnP8DIh3Yz2sIjfY8ZGrHAdDkoDEmFklNW7wn0/DikVounfSI0BrQxBF364crpRBD\ngeo/oW7LgHtgwvLUNU3WP/T97ghepYT2V3NjpbqP7Ap2L+krSfujGOVUUq4bVD5C\nU7H7WdpkqgNSLHeYe1Qoqg2zDlF/FvEAOq0NSklqr3ufIWlnbkECggEBAOTHVIUw\no0m7fA9outAI97NmhKJ1U41mDEglmxZNiDMC2k4DoWBJX4TzPb3oDVK5vB56jrHW\njlR3Rj6vb729epWJMvDsZjiwwoYZTjqJB71iM7I60r0D4tjGhKAnD2mE02fyCQWt\nOgerrcpDCbQldRCHsjSoHfhj1W6kRwGQUQjCx4mpCmng/3sDpdi/srNirPBK7qdK\nfltOr+v2twPIZf0jKlENL1UIQkk5mBpBIC9YXbmmwkPWD+kh5KAiH3PASoKh5qKo\nqWXPJki7bRwRtMS+tE4VqulNbk1GOJOVD1rUserC1CyA7yu2Lk0Tjapz0D5DI955\ntkVNLMJY8mri8H0CggEBANbqdfYUckh2MyixSjOKKIZB5jCy4C/rhmmvLZ8RydAX\nJpWMZIBF5fgLeGNFevkHK6Jaojaq0dVa7/2+R6CD0chYJQ6Pa5sAVbgA0L7r94bH\ntynpOEQ5Taw7IYuvdSg+YRuQeXjsRqziWuIsScdKN9azgL8vGF2TlQWME3nIn091\nUlUxRyVxQDF6n++C0tJbOW0QG6eo/UWaz20kmzfw2WzM5fJiEPlvqFyZyInf6f1Y\nQkORHLgyP6kMZlbSN6YlhR7OPj/r9IWv/qJmdow95oPV+fQgRdhnWMtK7QouXe0m\n62VyPRiKX5OpGl81ubwILEX1GsIR3ftA7+5DoTRB3SsCggEAMdI21cqBxMzPd4uX\nIzbQuoDY4Ygrad+PTSbjXzgDMRit8c9YlV9lLc7K41Blll5cWUbXTGMtG7fd1Ejg\nTLG2wFngfhb9K9tSgWGBZ73vsY5xM58bxY3TGOIif74B3TjP0RMkY9gVixYYArQW\nqOR/WdozNMRnwm/7Stz7mZuUChRg39mwKt6UxHVG6KzWfbo/bxTiZAwU+E1qgjh4\nIprBmx7NWFlGtX3W8ZtZRF5qY+UahBOMurkjPeHjp/vGXa7CizA2ybVxLnquG6i1\nWieA9leRbG/LiIMUQj/IklsjvOQU2ssS6Z4qd5p0+tQHRddywhxc81YOG4ly9d/o\n6t4BAQKCAQBpZZpsECLrvmvgbPHCzOKK1Q6am16uoYbNlYd+Fy8tyey2Mp+m+KCL\nQLTNmmlwbz/K4z/iJ2u4glxnyEVARuU+p1ulBFj1LvjawCufNgGyK4d8Gk/pxWB+\nMpGE+4stLUpaRkQMNXODnY6MlMOp5I/1zxo4qFP+sD3I9qUmeGkIgrTZ6szFZADj\nQwvkXfgeCXbkMZ4eXxW2aPGIyQcGLCu029d5QvaoHUYoA5MBarpJ8Fqu4B+BhcMK\nmHYn5fg8lvHt5Z9JoeHNHj52TmGc7U57IRjvzFP0iNpW+Gu/LnYJs6iFm7yyqEmv\nFKNGRgbMXAW6WTXCLxj5+yKvaqNICXT5AoIBAQDEMMshY8gSzP/ZuawnYTtHS5Cm\nwtsBKCaEZ81hwUJoxhczCBOO1ad9qvoxHumc+zJ4JrTRKrFa9Bi9V9ph9hQsEtE2\nBHTmRSLoJoBDcgq1wPd0IHOAdMx9ThEaGCVQtF3mYdGSPQjwbzqwQZEOptXYxSXY\n4LxNlP0D7JT5LKCqWmKpbczVVh7N/0S4/COJnEwp7yYnB258emksbvh/VhCz/GBB\nMxEH7kwFTIT1qSk9cWCSSp15i33rievMJbvxRx9WjFTAWj9ofgIuUzxY5zP+FOBo\nG+XV21OVfc2wEvFgs+piKYINVxpKwVHCCVP5FUrebpxCZX34xy4nbmgPbiaL\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/include/ssl_certs/ca.srl",
    "content": "BC864F1DFA88521C\n"
  },
  {
    "path": "tests/include/ssl_certs/client-cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDKDCCAhACCQCRX5mTEHplWDANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD\nTjELMAkGA1UECAwCQkoxCzAJBgNVBAcMAkJKMQ8wDQYDVQQKDAZzd29vbGUxDzAN\nBgNVBAsMBnN3b29sZTELMAkGA1UEAwwCQ0EwHhcNMTgxMjA3MDc1OTQzWhcNMjgx\nMjA0MDc1OTQzWjBWMQswCQYDVQQGEwJDTjELMAkGA1UECAwCQkoxCzAJBgNVBAcM\nAkJKMQ8wDQYDVQQKDAZzd29vbGUxDzANBgNVBAsMBnN3b29sZTELMAkGA1UEAwwC\nY24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDATA3sGW041+GHub7\nGgfAA1vcZP48Gw12F2jT15mX8SMjjQ8AutdnxG3OMt5lzgpw5+SVUgxK0nIdGg0f\nO0Rseh3VKYl7F2neUWzETfLo3qywVsu21fJiuxwtS8h+r/pM/0/drC20qbKzDEDT\nQ4hwVsCMjx0CUxZoFgiHBRhyIT6DqBdt8fylSfC0ocvLXI32Hl75QEhjrSqZqvHY\nz7cT5+YzuL/SQ5QOEtCGQarExhgqgQEtpD5ZGhbalDhrZDI/ZwRnA27I54s0VCTY\nZT6XNASf1UUH6xqV9IT6lY2GQ9rNkzKVjaG5/gJDcKPO+sBLsjzgsW4qJ8GiLPLY\ntyIZAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAICx9BbAwat+nF3Tcw3y4XyX0fpb\nJ3YXAyIESNKEh1F6IRVdPkcHTMd2YU583pGD38UJzWOVj92bBfRRXUbINnCoqM30\nQHiBfg0kQhc9UNKY+62urhSfUs4HGiyrRVwVbSDbqZpzg5MXa+msgktWMDL1KL8d\ngljnqn/Rad/hYKgjIGJ0MIk55qKl6z6tlVwIIPkdEm6FFcRhejLgkCpDC4WBwbmi\nU1hpSQfoRXICVyO96mL+/wpekZTAPFZB2LOaZOkT4/hGhlNVFZfcgws+hTh11EZb\nwfpgVYxk/T9MfgEyZGBtarUaNR/xobdnABo5eyxXrrd0RU7SjIrIRA5ORxE=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/include/ssl_certs/client-expired.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFtTCCA52gAwIBAgIBATANBgkqhkiG9w0BAQsFADBJMQswCQYDVQQGEwJDTjEL\nMAkGA1UECAwCTkUxCzAJBgNVBAoMAk5FMQ8wDQYDVQQLDAZSb290Q0ExDzANBgNV\nBAMMBlJvb3RDQTAeFw0yMDAxMTYwMzA3MTlaFw0yMDAxMTYwMzA3MTlaMEgxCzAJ\nBgNVBAYTAkNOMQswCQYDVQQIDAJORTELMAkGA1UECgwCTkUxCzAJBgNVBAsMAk5F\nMRIwEAYDVQQDDAkxMjcuMC4wLjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\nAoICAQC/GbFTW4fv4YtzjPpVVdXbKTkJv+od8A6azyI9sWwl5nj1NXV4rLw28/ku\n1xka5La67TQ5y44D4StGSsfLuVzehqWed9NqY2t+u/kE82TXyQ90OuabXZlUXfzV\nT6U2o1l36ko97Q9MYws4izVX4vE8zsCdatNM74kyEsUbHJ3Gut2IKLM148D7UuQo\niUhwmmvFCCTL9YdW1HjEtbgg3mzg8XFtAA4mzKv+VgHe4VnAA/Fh1dPtNXpoa7IA\nhheWS8DC2GN44/BegnBqG877tKfbooCyjBxkE9HKroj1y+f3FrGWCQGEAsHn6Za3\n61OQkYcjLQ2eZ+CC3rX0HBk+yY5S6hnTjQ7Z4aFBgsxnqg9WFICjXtI5RmkC6NoO\nwnFr8DJ3MUEhDE957wjSNPirlvrLxX6kyONVKHsChvBKu0iFgpd5VlLKoCxqwOib\ngEAJS960wkMVvY91LxDfp8waoAhT02S7ysiCll5aA4UyyHA4hOavOeHsv3hA0sq8\nHlbJ+duw/IHLGFlEXgBC/zVsKbVHliIU03oUBEEA0oimcDJGSClTzcPXL43xpqAg\nkb3I4GPFLhu1r/IexeLoUrkA4KHx1LSLzXf+P9Y1VFGOrRvI7zMS8R8LryqGr7mF\nxgOxcm2uS0i5/OTYYSk+mQVu609epLl5GgfHeNIQTQdnWHC6NQIDAQABo4GoMIGl\nMAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENl\ncnRpZmljYXRlMB0GA1UdDgQWBBTNzUGh5F+06+7lm/pnTMcjKC/6ODAfBgNVHSME\nGDAWgBRI2TJRKjGyUUSNv1C0TESpV5guwDALBgNVHQ8EBAMCBDAwHQYDVR0lBBYw\nFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4ICAQC33cEcngVk\nN2lqPiZ0fuHh/Rs+LbUv6OWBIdhdHiojVaN4kq3Z0eIvZ5x1saXca9ff/rk4Brk6\nK+qntCWNg9DAZ9ahjhsXAeDQJE1l7999cCJxJRa6MoBTHwHejC7XAhpVEf95Gl3C\nfNkDgEnBEisoy5/XL96TIOcZC/trkESXEKrPrKy+BwyikOaB1m3M2OFPFx+HomWO\nw4ah4c8BNK/MIRb0zj4TJ2djxOsX1lXt5DC/2AACX2n1H6l/iVJ5oxwzNJSycX9M\n/zAA0+lsedCiAiWZSkThinHbmu/c2QOmVIfwos/KlgXZTc7A36sYJ1wge8xYMR/0\nLV6PqVdoAmq8e9uUZFgyCl1Ojb4HojLRyuUUKYOV7ywODVcezNhk5z7ft2p3Kkgh\nbrQAd0crn3rmDoM8Uay/tovQgg84nIYWag6vpONDPR3XbQG3OvBTTdpsnphfMFvF\nClsam9jV8WGi+ZEg4ODnsE3eSbG1hBjEBQw7dKq5jPEZkedNyqTNGPhIOevYWITg\nnOSESi4ksKroJJCAG3IRA7B10maKItK+NuDXKELze/Xj9uE1kfuEUYm9DnjuxPqI\npMLTLS/1Z0NmCuwATcRJrm9gXZGdpVvrjuS2Fc5SkqZTbcRIzSbUf2cE2OJAKyj7\nVkKhgAJRKDCNQZ6wru5nzGgs/zyxH20t0A==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/include/ssl_certs/client-expired.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIIBmzCCAQQCAQAwWzELMAkGA1UEBhMCQ04xETAPBgNVBAgTCFpoZWppYW5nMREw\nDwYDVQQHEwhIYW5nemhvdTESMBAGA1UEChMJTXkgQ2xpZW50MRIwEAYDVQQDEwls\nb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJEUnBzXTTiyUmDb\nyhkQoQ/yH1zTnuIk5Meg1Bp0fp1l4kwiizdPbZkk4YkTT/HXdTE6822Cqho+CwGE\nVqWZyyd2AZmj87OGb4ZRCyyFzzjfEwdCTvyqZSUBoc1gvSGdEiaA4mXE87Y0XcMB\nBasOrfmO76nuzyaXLT7xDjrB+Qw5AgMBAAGgADANBgkqhkiG9w0BAQUFAAOBgQAx\nrsaWSV81/SCf+0af57Wr+BJfiGEutZpdmIe0ofPKfVfz7c8QKjqK+/xQb0INUaYd\nMUPjuLfvp06iCWyDPsfhsBRZMSDfFZDp8bnoVloVbP+yLL2Gd+h/a5iYjKTJ2FEt\nmDaoIXqbw7oHXXxfKKLP2iyUQCqbfJTC0XeJtFWJ3w==\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "tests/include/ssl_certs/client-expired.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIJKAIBAAKCAgEAvxmxU1uH7+GLc4z6VVXV2yk5Cb/qHfAOms8iPbFsJeZ49TV1\neKy8NvP5LtcZGuS2uu00OcuOA+ErRkrHy7lc3oalnnfTamNrfrv5BPNk18kPdDrm\nm12ZVF381U+lNqNZd+pKPe0PTGMLOIs1V+LxPM7AnWrTTO+JMhLFGxydxrrdiCiz\nNePA+1LkKIlIcJprxQgky/WHVtR4xLW4IN5s4PFxbQAOJsyr/lYB3uFZwAPxYdXT\n7TV6aGuyAIYXlkvAwthjeOPwXoJwahvO+7Sn26KAsowcZBPRyq6I9cvn9xaxlgkB\nhALB5+mWt+tTkJGHIy0Nnmfggt619BwZPsmOUuoZ040O2eGhQYLMZ6oPVhSAo17S\nOUZpAujaDsJxa/AydzFBIQxPee8I0jT4q5b6y8V+pMjjVSh7AobwSrtIhYKXeVZS\nyqAsasDom4BACUvetMJDFb2PdS8Q36fMGqAIU9Nku8rIgpZeWgOFMshwOITmrznh\n7L94QNLKvB5WyfnbsPyByxhZRF4AQv81bCm1R5YiFNN6FARBANKIpnAyRkgpU83D\n1y+N8aagIJG9yOBjxS4bta/yHsXi6FK5AOCh8dS0i813/j/WNVRRjq0byO8zEvEf\nC68qhq+5hcYDsXJtrktIufzk2GEpPpkFbutPXqS5eRoHx3jSEE0HZ1hwujUCAwEA\nAQKCAgEAjiXhZn0GX+Qoo/ow17syiYRfXiY4Uq6XXoZHFRbduE94mAV87ReoJURT\njYkIrp2EYwVAvODyMWaTlwTXG241a31Cwt6lD1UGx82xDQKzd2OisDWEBfuYq04C\nXPWSklLtoSpQsUGeCJb/6L58HnS8Nxwf4LhulqLzLaESpvkhT4r+cGK9848HpKri\n9lgv3zqugXBuVpy7YFZJwyDc5u4slSJj2c2ZuTizk0uMtqpv5UnJipnQu3j1Jak+\nTe9wqiaNKyJk067RxKtITVWEIG0gE1sMWkOodrTbb+VmEVfsozppPI5UZ0rZy9VQ\nfFx622W03PCNWdAxC4f4Vq10BeVNjIsF3w8l/ZUzKuUbMXhTGtIFLzK3jNpw+GMJ\nfqhEgvJJldWrJkRYiqlP8nbzwlGL1IYQJ4kwZaRzoqMAEKZT5dJ886sF/fYDCAwz\n0960gtX7m5TMrSOKxdEGX3lCqovYAkxgz9HdTVg1ITzshJE8A62wqTK18lnrzhXH\n7efh6QcXDJMkXZL850RDM7Wq6j6SZqpN6DDAU7+4JssLaAWuyWMAoRqhWrASg4Hr\nkoVe+ctb8uydbKRGZMSkvXDXwPfXc811jzoxAsf6lJeJgl20+eUCofzxina5KqHA\nvaYbul6IXhUKPjHhRywcUa2axstwRf9UNLakLsNjjDcQn1Y5fH0CggEBAPK+utiR\nEWvRncRttDA7BHCb9uMd1iVz4ZsOZzbSqaW2vc9heHGMjhlDA4v7F7MHhkMxjD3S\n0hjePGr+uwUM0jkHzN3/jRLgyPNqjalH9eubFQTaJuzm5MXEbJ7Y2GyXW9kEdz7E\n25D4RO0VQwzQh/ND104uBWhJvsuHQLJMwfvVcPZjBUeCEwvIynUvhev1zIqaS0lb\nENxkEe2ipUK2v2e5+tVsU033VkhEVqAgHOcg8yYzREfbqEU5CO7r53XIfF+kTZN9\nJfx035zwnBITD1WaUI/No/lwx/MlAzXnEyh5Ya9fHvcp0+Rp/mk6eamcEgkgiiKb\nCk4bfdX/4QSZFqsCggEBAMmJCQTfxSf0jm/zUVBvVXvLKqdUSf7h17dT/MbVfOEP\nEScYTkHMcnyYe9NB/yNRaa5eBJi9Tfhg+Hd2DO82kaZxmSJwwjJfd1rADELyNQDa\nxZLyIDFXngd8OVBIzKwSw2N6Dml8WWnS55LVXU3wsV+4SRNhqU0j47uPOtS3UoJu\nu8bzmOdvUEb4kzo24oKZzlFgS1GlHUwA7GtxedVH++5oqB4fifcfigKt6xM7kddL\n31G25RCVaj7jIiHHmMlmBPZkjO+E52ncSbH9gKuZtRww1ZTaM6Fk5YC5CrS8/kW9\nIqtW4sAe8inEbKk+m3edtWAHoe3uSIkltEEinqnK8p8CggEAFYnU3Fhq2k08GWy+\nezURXMiJ7Hb+RE526yIpCFVC1kBTsS3t2O1mwlkO3IHIBYuaXyXrUqT9HVXCuJ+n\ningrwwGtLNl68/zb97Uxop6O9HvLLqYIpCaMj/uww5tsAJ6heK0Lw5cGAGP3Aa5M\nR7vNBEm1teshcbJxam+yzt8+qxCyODprAoBJHlePlzxDPr+NiZLNMVISOevrJ1Gy\nJiw+6X44Am3dKmLS1hCyOvz+c4eZ637LBTE5SgMYhtKJtAq49meGtpp5H2BjYvJ6\ndqQYTTknAst1TDG0nB9hnIvHSGUUxNHE1ptwgKji+QVGPEVr2EF54D4veOS5kp5k\nodrPhwKCAQAtyrhE5j9a68NthoDEYPewcCJZ8Nn7Uv97xVabKyj7zjucgXZOlxCw\nZMlF4CoDsVzs5Knwtpq/w/DQ2/7mX6TKh+c+nxZQ8cYh1Z8zmusWLZ9U2zUbYQgk\nNUKkjlYLBxxkPviFWV7Ln8NEZryTmAwFbs8T203nnC8Dj/DTInBZ+KUr+G+WroE0\nDwkM4imneSEa9mFGxNiaS19srlT7dCa3Lgp9BbeIch8eqrvOG+vwWmn8nC0CDqEc\ndulreXmq0UZxYblwNueJ8ya/jZUt6HfxVttAvi+bDVzkWacoGAVCfU4iVlShSKbH\nfL0f23zhufDbSACwGkNa/jktz5rzg4yLAoIBAHVPjXB16AaRl7Uk1e5585kD7Vpd\n4K2uGnJsX7aMX1lGGm8OmlamhppgsIqECsgqM9uHLDDnW3CcZ/XTuxD/SQ1IBnZd\n7v9JZwPzMJp0JXr3nn/RqCnMZytd/PHE3z0tQvwz8IYtpehEGQekfJKD/wiuOwzn\nWHJMTr4zbbSQk7Pp+RDb7zXxODLj5YVoCasG1RyM7JtrnvAFadS8oEghKgApJUak\neSED2HxCpW5mj389qNkNgBQ9AeDizBHxSU7e2Kj0kvBCqnLiqnjlwYXQxTSdQ/sn\nKuea9MuJJe7+iKL4nttqzZSa0Vz4JRpPdkAZfTcr8CeNQSdEddPpEVmnJUw=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/include/ssl_certs/client-key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAwwEwN7BltONfhh7m+xoHwANb3GT+PBsNdhdo09eZl/EjI40P\nALrXZ8RtzjLeZc4KcOfklVIMStJyHRoNHztEbHod1SmJexdp3lFsxE3y6N6ssFbL\nttXyYrscLUvIfq/6TP9P3awttKmyswxA00OIcFbAjI8dAlMWaBYIhwUYciE+g6gX\nbfH8pUnwtKHLy1yN9h5e+UBIY60qmarx2M+3E+fmM7i/0kOUDhLQhkGqxMYYKoEB\nLaQ+WRoW2pQ4a2QyP2cEZwNuyOeLNFQk2GU+lzQEn9VFB+salfSE+pWNhkPazZMy\nlY2huf4CQ3CjzvrAS7I84LFuKifBoizy2LciGQIDAQABAoIBAAUtZuZTnJ+s2cBp\n2aAZFNOYQzwawlU+MqFMBdB6DO3nzRMIj7lpPIbYxEGRQC5bxhMxVPRR9VmXNMhp\nBKgrnqlypJAgRjIqcPkiGE0t9KESKGuWIlp8W68bYKgjNvOGIJMixDASfjQpvUDb\ntmzlgh3Zb8tLkW2aXyHsU9Wh4QBGrcW7faiu4CcC1kHJk5FUg6eSxN0vhZxCQgKP\n6i4aHPOKoLu3jI36EGERTiFeDDUhjFynRLYg8gfaQc93gzuZGZEMjtlmiTUPMrKn\n9O15Y5RIDFDQSRDngNbc60ieIJ1piqzHfL6X8BQxK+Pd/J/3SSfFuXGGfISYahGS\n67SzHAECgYEA831stnQB0R6AP5UGuJDnknjgmkH9C4a0EpV3YFb8CKdLN7x3mU3p\nwLPk0bpjObW+PHmUNZWsrcnKtuw+5yVFefO+bZp8KdFptcFI2cgEsLm/PIoclZyK\ndBE0s2LlCqg/XNYImTYamsRD8RO0DYoT3yFK0/oQJUdyejBenOXjq7kCgYEAzQYL\nzKW0BP794eCo//fxmcjNTKrY5TVErWBQS8RI7uqdOsYSnlkBM/62pYnuGX3//Ol5\n1fsuRbPBTNhdKMxAF+fjdGCgx6yxEcrt7d6aEp8hIDvix/YuXvYSe3HRSULsWboE\nyLxVNW7GaeXltcLa8chJzjv5QowPhFtMFnIeGWECgYASyc3WslLlkQXyYgx3t263\nBa7HGGpvNkC04mWowG2IOWh7b4aHBNURso/ogwpfDKAWSIdgLyF484Y54TblXSHk\nTCOYj4AfNfKDPNvjOiTqghq4kCuue7rPQ/ieEvZ7gQoKntVhBSS+ZoCbnJqbJNlt\nsUL14mjWh5HUzeCvGKoduQKBgQDAndSKEId/Rh05j/rWeJfoNgoC5GPfe1spU9o8\nRI2MwKi05g/p6o7BzaTFAz/JfWH5t1P6oABKBeYuDvv+712r1/UOihWjYm/82l0M\nZFz178CMaxbVbEkGeELG7RiFrrUkWypRePbIu2j3ZsCYMhfGnbgRZLD6P2H4pFlo\noQ/8IQKBgGicAkmrhW1GLtEXpUaJDW03M59DQDkJzCJwp2ah/BCOuo0yNt3MywpM\nFPLay8ZGapGrg2vZKd713w540zrQYLNFueP8IobxHRHYvpQM+DTiNr8vHX5ISyH2\nRhc1ZZklKakTefKwTdySi212GFgdV7nCHXvurQL7hvlusWlie5ih\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/include/ssl_certs/client-req.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIICmzCCAYMCAQAwVjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkJKMQswCQYDVQQH\nDAJCSjEPMA0GA1UECgwGc3dvb2xlMQ8wDQYDVQQLDAZzd29vbGUxCzAJBgNVBAMM\nAmNuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwwEwN7BltONfhh7m\n+xoHwANb3GT+PBsNdhdo09eZl/EjI40PALrXZ8RtzjLeZc4KcOfklVIMStJyHRoN\nHztEbHod1SmJexdp3lFsxE3y6N6ssFbLttXyYrscLUvIfq/6TP9P3awttKmyswxA\n00OIcFbAjI8dAlMWaBYIhwUYciE+g6gXbfH8pUnwtKHLy1yN9h5e+UBIY60qmarx\n2M+3E+fmM7i/0kOUDhLQhkGqxMYYKoEBLaQ+WRoW2pQ4a2QyP2cEZwNuyOeLNFQk\n2GU+lzQEn9VFB+salfSE+pWNhkPazZMylY2huf4CQ3CjzvrAS7I84LFuKifBoizy\n2LciGQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBABGKoAa56G5lDWbZEByBiKjJ\nLf7E74z8CSFLWKd4ytbS/qBJJndMz4oRsVO2etu4XlmRuZTrt1gJ1QXbQHm5b7b2\nRx8CgE2ZGTfWIMc6B111z6Rl5ahZRIt7KEduC4+hsEHeim7eoQNkWRz6XU+fBWYA\nIAIjteH22Id0LnQuEXPcxvhBycZwcxFCaLKbimltgZskXkNpe1FCYbU3ri9ZTt8H\n5MmQOPxyBr5aSWgmeJjLtrgGiH3hOkS494sYAxG+yrkKObDEa09xdP5+vS5Y8rDF\n2WODi1xjIZEypAYoDk+UrI6JY6oaSEdZitw4sLew7+kopMXBZcEbsyVuGfA9Iag=\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "tests/include/ssl_certs/client.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIETDCCAjQCFEFiPP89vh9xURoM0YYsuD6rFewiMA0GCSqGSIb3DQEBCwUAMEkx\nCzAJBgNVBAYTAkNOMQswCQYDVQQIDAJORTELMAkGA1UECgwCTkUxDzANBgNVBAsM\nBlJvb3RDQTEPMA0GA1UEAwwGUm9vdENBMB4XDTI1MDQwODA2MTEzMFoXDTI2MDQw\nODA2MTEzMFowfDELMAkGA1UEBhMCQ04xETAPBgNVBAgMCFNoYW5naGFpMREwDwYD\nVQQHDAhTaGFuZ2hhaTEPMA0GA1UECgwGU3dvb2xlMRMwEQYDVQQDDApVbml0IFRl\nc3RzMSEwHwYJKoZIhvcNAQkBFhJzZXJ2aWNlQHN3b29sZS5jb20wggEiMA0GCSqG\nSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvyeKPL5krERLYq+L1YmU5DMfuBnZ3JKv9\nflrdNOn1h9ETrasyMl3RCfsUf9d++kBTp9OZLx3SD6229g9qlR12xW9clheX7N+3\nwNydkVgAG6G+Tx07l3DqJj4U6trh+Fh251Mc5zld1dYFxwUJHfJlhpY9yD/JTFZs\nZZFB4Kjie5+RjnpwgiuZLzJw3GmF+ZoEppNQAbBLWM1KgGiyZ501qjpfKril6bkS\nTx+oFW8tVhv4MpfTbVArjxcl3ax01mM69ATZcChDcxgKzCh3X4pAqxAqSO8obxqS\nVWMwikmJ+f9fC4ZhbbVnMeYqtBd5BVj3IzOtK7ydyghv0cS4PhAvAgMBAAEwDQYJ\nKoZIhvcNAQELBQADggIBAHL+qmFwWY6Z8Y9A4btmP5nAqugyufJCAflQpPBSJ8Dq\nUunztrYA4sJZMMXDLCOr5ZhVT9zDCJgNELqgJChgVEKgj3TBsoEeOVTPxH84gUIX\nBcKMEZktNy/XudX3MlMWYL3avtjWeCcl91q2Vw4VgEzrnt1Rdq47XSHBt0jayF/I\n1OLxt2HiN74GiNkBl8lyMfx0umk1qFecKda42vkF0nsWeF20giaj3zsze0BRqgmw\nzQ2IuC9oiiufbnrExM0cjqIpGky+vAplw7jPFUMJ6jadjq5XxC5ROGmHdcXjHq9/\nmLZ2Cvb3m2LUgefyqq5T2tGBIJMK8OE7byGwsLlfZ8B50SCLKtaukvnbS9pMIS3H\nS459IoQS4ma24QHk0fBo0ond7cSXxGfrlmDKGEJxLp6hX5i2VpPT/RR2cjynM8XG\nDAyj4IzYbfQjDPgNGCXcfj1IydFreXzBM9ndf9eweJW32vbFmAwDV2FP6IaMkMv0\nycj6YyoSYSxs3tOur5JXAPTCzMIgGFhWTvHzuN0P7b0380aSzKyH8fmN5NqwcGUk\nXg29NSGtrf0yX39jkCYyewGd4eA7NvoDooZgFAaY9U+myCHXttYDN2DC41qxC+9F\nbK+RfzkG6fR5YIVhYtItGVWgOGwuu03exfBu4tzkqKCCwlEVoYXJJdCMVYQPGhPV\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/include/ssl_certs/client.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIICwTCCAakCAQAwfDELMAkGA1UEBhMCQ04xETAPBgNVBAgMCFNoYW5naGFpMREw\nDwYDVQQHDAhTaGFuZ2hhaTEPMA0GA1UECgwGU3dvb2xlMRMwEQYDVQQDDApVbml0\nIFRlc3RzMSEwHwYJKoZIhvcNAQkBFhJzZXJ2aWNlQHN3b29sZS5jb20wggEiMA0G\nCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvyeKPL5krERLYq+L1YmU5DMfuBnZ3\nJKv9flrdNOn1h9ETrasyMl3RCfsUf9d++kBTp9OZLx3SD6229g9qlR12xW9clheX\n7N+3wNydkVgAG6G+Tx07l3DqJj4U6trh+Fh251Mc5zld1dYFxwUJHfJlhpY9yD/J\nTFZsZZFB4Kjie5+RjnpwgiuZLzJw3GmF+ZoEppNQAbBLWM1KgGiyZ501qjpfKril\n6bkSTx+oFW8tVhv4MpfTbVArjxcl3ax01mM69ATZcChDcxgKzCh3X4pAqxAqSO8o\nbxqSVWMwikmJ+f9fC4ZhbbVnMeYqtBd5BVj3IzOtK7ydyghv0cS4PhAvAgMBAAGg\nADANBgkqhkiG9w0BAQsFAAOCAQEABvjAKmCbJLEUxsLB3z2Nn3qiWZ8G879/Z2sx\nhmbOnp2/h64LfXF+xneg6CN7aOaBEGujbuTAj3dIQQjhnO9xRS5I4cfylTcNz9MK\nzKeNV+Ukag4TnUpqpE6MiB3cosvanqpFeHVp9P98DDasxXYnSayIGUqWfJ9B8AFh\nSphMqDCn1mLD69DIi+Rk1C9Vj/+jb3ec3EuHpaP64/LV7fmvErxIfgSD2uLQtGeo\n36CKgxlae7t+OSN+4PczdBzrTY4asiS7QNnoHmtI92EHGNlgy4nxCjNXTSk987Iq\nnCZLAUxPxw2MAqKRClQxnQ7BIQVaWCd+NpM46LQULJp4C+QT1w==\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "tests/include/ssl_certs/client.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCvyeKPL5krERLY\nq+L1YmU5DMfuBnZ3JKv9flrdNOn1h9ETrasyMl3RCfsUf9d++kBTp9OZLx3SD622\n9g9qlR12xW9clheX7N+3wNydkVgAG6G+Tx07l3DqJj4U6trh+Fh251Mc5zld1dYF\nxwUJHfJlhpY9yD/JTFZsZZFB4Kjie5+RjnpwgiuZLzJw3GmF+ZoEppNQAbBLWM1K\ngGiyZ501qjpfKril6bkSTx+oFW8tVhv4MpfTbVArjxcl3ax01mM69ATZcChDcxgK\nzCh3X4pAqxAqSO8obxqSVWMwikmJ+f9fC4ZhbbVnMeYqtBd5BVj3IzOtK7ydyghv\n0cS4PhAvAgMBAAECggEALT2dZ7Fdz5Ygzg+KpRFyMJkH+UvDgE05UE+NDgIYtj6C\ngfh/pzcMKwjRDAkMylqYZI88J1/XYmDzmbNA63K99vu/+cmM/+YP+4gQnyKxTbHO\n0h4lTVlfz3sRHiwFMgPKrKtDGhyuCGbIoosCUkLZR+S31OWs+N8DHPmUxSoO/N5b\nytaS6Kpu1nC+l7YPf6YPRXkvLZYxLAZl7Z2KOecgSCpHQ6OMlZR8PvCndSfVX3yx\nK4nRftjhim83S4DnWXG4y4ENuMCI+biXvsQrGiYKtJmESNxQ1EsBChI7t931uku9\nQGMkpbYmExm0JVuF5P0B8uGTdQzS7WzUMs5hk5LsXQKBgQDOc1RHVWBP3cReD1y/\n9UiHSmPPTzW6rw5W/ctXDeTywSE261lAlenDdh6aptMguukp0DxH0ok6UT1i4O3S\ngswhY9A8ul0xxpciAjkcY986QA4SZZmuhH/W6JaGwVUu7w0kc0j+xN3mLMq6IyQy\ny0NE2OKZaaRAJ1JVh1KOUfWFOwKBgQDZ+qMNnsf7TeYqaRfLx+adZLfaqOcd1ifu\nzidLIVYvEZa8SLFXYdyFed+hPxrhkAeAUzg82plEXG3FvlHxBqSk8wv+jX2AYEkb\nUBZBahzFC6bhqwZNmcpoM/e0uDf0uXyVKS3n9Mtulf+EgFLKVFxwoT7vepVOXQSK\nl+SStJBhnQKBgF3MaG5AjBG9YIqb0Dgm6V0On2qBQpgfERTVGp3i4jKpLL4XD+YY\nNq1n7V+2jBZC1cz5n2nCbjbg0lMqfuM2KijBgMsWoWEMKX56JBiSB8ofcEVLhBeW\ne77YzgtLq7g3RHFbL78REwCROUp4zOmHEHknUU0Cgm3jb7UMVqXy9uohAoGASCsl\nROMnDtrxs2TsDfn3LnEkNe7p5gT7X6M9ezBkWl0WbOwcWvP5kO+SzM4Z1khbU286\nFVvCltGLPocOliA+7qXReMvbm19FlRdk5u986vg6nkiDY7nhO7LI9jpvvESVLMVf\nf4aau9hJqtLZlkdr+5ZHQf5x5GoDkOdNNA04KDkCgYBUwPXCdHPdoVHeHtcfYVnx\nlOZMWbH8dRNsd/8o+lw6OIJqdrKk2hbjukBjrL5SMG6wucgHGm4UYRO5qpLLywSF\nl50yOIZ9bZJvmUazImJ4PAHt7GMzw4GSSBdQwbAxr6Bn6w7ySLmL6tuUJTPszp/g\nhTI3L3GGVTaQnQrntKsoyQ==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "tests/include/ssl_certs/client.pem",
    "content": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCRFJwc1004slJg28oZEKEP8h9c\n057iJOTHoNQadH6dZeJMIos3T22ZJOGJE0/x13UxOvNtgqoaPgsBhFalmcsndgGZ\no/Ozhm+GUQsshc843xMHQk78qmUlAaHNYL0hnRImgOJlxPO2NF3DAQWrDq35ju+p\n7s8mly0+8Q46wfkMOQIDAQAB\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "tests/include/ssl_certs/dhparams.pem",
    "content": "-----BEGIN DH PARAMETERS-----\nMIIBCAKCAQEApP/KXarP09GXAzdxdCHg8QAbVeGxU2hc0O2zE6900PI7Jd1VDOAm\nAZ3UEGfwv7WLxrRUJC31/EbkHH3jjnt28De9r3vNdpt30nYQfYHTNnIodi0j566C\nCkIY5J0uby/luU5+cX/NpHwA8YCw7wtvt279mACaE99tmfjY7/enDDoO5R+AUZwk\nneRUDszaTLPLotYcJm6LSaUvp+22fKazorGx1OiK1/jN1qL+sSwrYW64C4/n17GW\nkGYllrHBU6pgknCFhZdzc6SefMNKbVD79kQnDPXvERtOxOR+kv4rwzfmJ6NB5Ahf\nWmz8RdZO5BhrIYqWVjMYi7VZmHtJkzA5FwIBAg==\n-----END DH PARAMETERS-----\n"
  },
  {
    "path": "tests/include/ssl_certs/mosquitto.org.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEAzCCAuugAwIBAgIUBY1hlCGvdj4NhBXkZ/uLUZNILAwwDQYJKoZIhvcNAQEL\nBQAwgZAxCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5Vbml0ZWQgS2luZ2RvbTEOMAwG\nA1UEBwwFRGVyYnkxEjAQBgNVBAoMCU1vc3F1aXR0bzELMAkGA1UECwwCQ0ExFjAU\nBgNVBAMMDW1vc3F1aXR0by5vcmcxHzAdBgkqhkiG9w0BCQEWEHJvZ2VyQGF0Y2hv\nby5vcmcwHhcNMjAwNjA5MTEwNjM5WhcNMzAwNjA3MTEwNjM5WjCBkDELMAkGA1UE\nBhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTES\nMBAGA1UECgwJTW9zcXVpdHRvMQswCQYDVQQLDAJDQTEWMBQGA1UEAwwNbW9zcXVp\ndHRvLm9yZzEfMB0GCSqGSIb3DQEJARYQcm9nZXJAYXRjaG9vLm9yZzCCASIwDQYJ\nKoZIhvcNAQEBBQADggEPADCCAQoCggEBAME0HKmIzfTOwkKLT3THHe+ObdizamPg\nUZmD64Tf3zJdNeYGYn4CEXbyP6fy3tWc8S2boW6dzrH8SdFf9uo320GJA9B7U1FW\nTe3xda/Lm3JFfaHjkWw7jBwcauQZjpGINHapHRlpiCZsquAthOgxW9SgDgYlGzEA\ns06pkEFiMw+qDfLo/sxFKB6vQlFekMeCymjLCbNwPJyqyhFmPWwio/PDMruBTzPH\n3cioBnrJWKXc3OjXdLGFJOfj7pP0j/dr2LH72eSvv3PQQFl90CZPFhrCUcRHSSxo\nE6yjGOdnz7f6PveLIB574kQORwt8ePn0yidrTC1ictikED3nHYhMUOUCAwEAAaNT\nMFEwHQYDVR0OBBYEFPVV6xBUFPiGKDyo5V3+Hbh4N9YSMB8GA1UdIwQYMBaAFPVV\n6xBUFPiGKDyo5V3+Hbh4N9YSMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL\nBQADggEBAGa9kS21N70ThM6/Hj9D7mbVxKLBjVWe2TPsGfbl3rEDfZ+OKRZ2j6AC\n6r7jb4TZO3dzF2p6dgbrlU71Y/4K0TdzIjRj3cQ3KSm41JvUQ0hZ/c04iGDg/xWf\n+pp58nfPAYwuerruPNWmlStWAXf0UTqRtg4hQDWBuUFDJTuWuuBvEXudz74eh/wK\nsMwfu1HFvjy5Z0iMDU8PUDepjVolOCue9ashlS4EB5IECdSR2TItnAIiIwimx839\nLdUdRudafMu5T5Xma182OC0/u/xRlEm+tvKGGmfFcN0piqVl8OrSPBgIlb+1IKJE\nm/XriWr/Cq4h/JfB7NTsezVslgkBaoU=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/include/ssl_certs/passwd.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIID/TCCAuWgAwIBAgIUI7h0MOJPvQH1ox33VVoLeul7iXowDQYJKoZIhvcNAQEL\nBQAwgY0xCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhTaGFuZ2hhaTERMA8GA1UEBwwI\nU2hhbmdoYWkxDzANBgNVBAoMBnN3b29sZTEPMA0GA1UECwwGc3dvb2xlMRMwEQYD\nVQQDDApjb3JlLXRlc3RzMSEwHwYJKoZIhvcNAQkBFhJzZXJ2aWNlQHN3b29sZS5j\nb20wHhcNMjUwNTE4MTAwNDA5WhcNMzUwNTE2MTAwNDA5WjCBjTELMAkGA1UEBhMC\nQ04xETAPBgNVBAgMCFNoYW5naGFpMREwDwYDVQQHDAhTaGFuZ2hhaTEPMA0GA1UE\nCgwGc3dvb2xlMQ8wDQYDVQQLDAZzd29vbGUxEzARBgNVBAMMCmNvcmUtdGVzdHMx\nITAfBgkqhkiG9w0BCQEWEnNlcnZpY2VAc3dvb2xlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBANbc0dtA8QGKlUcAVf/kwU62EWi0uemeoHUN3kS+\nq/QXaRY6QsH8bKPnk+0wv7M8maVL7Fp6QpHQVfIe3Nt9D446zasLZeMr//MzS1Pw\nZkq+pJFZNO60bcJeT1VV4Ey+zlORoTWqjZjo6eRHoe/FR20NLZnJLWtxlSeUM+cV\nU3TYC/1ovsdopM94YiRDl66TUC7yxNbD7iHZ0gZP6v0yPfcRgzME+nSeWWQEG4OR\nn5xJT5XX5tIAV4NqyUfdEqIFBPJxqhbxoJc77jRAd1USCsKF+GU7DN9J1h0ZsVZP\nrl+G3Ur3n38VwfNxlhatIfmS9DK556q6OUE2a2ih9on0VwMCAwEAAaNTMFEwHQYD\nVR0OBBYEFEy+sFb7FcfBaFXRR1/QBZQrnWmDMB8GA1UdIwQYMBaAFEy+sFb7FcfB\naFXRR1/QBZQrnWmDMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB\nAKe6PqJbDU5vHN97qM3xbdPFEfSDqFIyuCrdk5XiqdPaqWn7FomQELfAkUVsW+gD\nm2LuYl9UDG2KSemr/TuualaP+2uVkUeBq8mMqDHV3iM/bGGabpXYvvdlIo/RDAhi\n1K3Oh/TvLRv8YXmYC4kWXHEmYxaLjDFGqYCN1VgQjQ81+h0sT0liq9A+g8/heajF\n5QQAVjp8QjiqooS3PwTRtXpg8KrccAJwLJXjAMzBa4apd2aJ8NjHuxUhdW4orUAr\nV8hr/l482OtuKpakcdDbzLqn1SKh5Oq+cwqnDEr5JYCVb2jM5furKHBq0bfFRu5i\n5HxGsJON3fpgyq76dkRN7G8=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/include/ssl_certs/passwd_key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,97585DACA30B6F5D\n\nWPt38qe6lbdheiiW2uHV1G9l9A2Yr3GugE5iEaZTNQS9BjsZP6rpJRj6JdKwqkSr\n0Or4B5g+AsTMw1LQES+ap5l27RC4vBcjiSb3NqXoZvvp2qhfUcfjxrwILULTC+Zj\nsy6g6lD44cwbe1gs42nZrMTKdOUDgxwnH/1f4Nnz3kt7z2Sj/jnPGGr8HbWIazzx\naE1KMrBAwfHXyeg8FpMRJZnmK9Dim53X6qjEEi9qrAa2KFX/NU5mr8hpFVw6n4WV\n2HMvCr8qoHJuR9lrGaLFEwJyq5WT143L1pLBPV6FI+DppFM8nh6Ze1UqNW4kAH1N\nxCDl542Hv0XoTl26Yl/YnlBJneSfqm4KpI8RioYfekp5ygEJv9JXFJ7WRfPCJxAz\nN9kLS5ggEF4etcDr9jtIwMwvkGylL8Bg7BmkPPIwyelv2HudWQaV7NHqxotQSuIm\nTiLAeEfIPPMdAwuVRR5yHy8tPSwwwqiaf4GrY0hHfv6wMS8Auq0pqIpKToQKMB3J\n/FdLqbyEWS2PpwPWoHVXCr94wEGS2eUdTW4KxxFrCBoizZC7tS1NiznOJmmKMSme\n2ACEy7uVNFecT1d//5MfVW7hwEZIxnF0w3WKv20uOuREwBFh8TtQ/DPZr6hoxUBz\n8rizJrqnkQa1WoC2m0zM1ag5BU/Y6kNFiWbEh0O9ShRNlO3mZeB1KwfFooTpZqX9\nsldXrhg2Tikus9jRUM/XnnNb5d/iL/CYJIkS7lO1NItDlNUe09OjrWNqWm2oJPKB\ngAiaBUz3v/eSRviX1YdWrGxFGTZFB6HVnGhBpiWOYD7eRu/jVCAlZuK8XdF+dO66\n4eSD/51GDoRtgBz3fR7UWuXfmQCIbnjgTYOVrK25gZdbl9US4bBK3MC66wsG1K5U\nU8i/Ko+h9YwNJPnHDdiOT9S7l9QczStg10pnmj8jASZzmrD/Pm2FU6mqq0EXV4pF\nQ+3uSuRo74G9vkQBZNjyBDrDO6YwkNyHULL4+hN6QGcDZD1pLDNN2GnLasJi5ccb\nE6ptfIU8YrkV9pXdEALf64lE3AshNhREHrUSFBpNia7bDxjEePtMX/J0JTo/Z0b/\nDwKSFGMMPbDq3JkHcCzfrKArmjFoIja15A2Q0eDk6xRM++DZeBIduZu9j3oj7lzd\na/f24ODzw25SEOj6mxnfx5MxwNgNTnV4SkFy9R5EMkBIpMYuZ9P/nWfo4c7vhOQc\n2CrFi4uQxIz7pka0SJUUMuDFlVbHXDLQwYwf/NUwjwl2iyZaykPuk+xBhZKicGkk\nlFz59jgOPSPEp4FPsmvdEScrJXNa5y6Prmxl2RqY6cA7CJW6iAJvh3XLDuaWDEA7\nyQrtJB0R8MQw8tvpBCKl7CuDWOM3x4nMMjTM1ZgFMZdAJQbqqPPyfsuBbJ0JMGSh\nxCRlkrLuHj2zWKjKtFlVbUh7oCHz0aKQAsa2dBBAb8YliA+Bh+JMxXRPfbNC3W0E\nxmOHSIMoLfSt/jkxNZpaQGR/Hccu7AJ+DlPpOoJtevpps3BfFBlMiZTCrLkXFYwa\nbRE3IMBgNanbwivLCkkWP4MQEAy316G7aruJCx24ebidSGdu/ThNBQ==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/include/ssl_certs/server-cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDJzCCAg8CCQCRX5mTEHplVjANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD\nTjELMAkGA1UECAwCQkoxCzAJBgNVBAcMAkJKMQ8wDQYDVQQKDAZzd29vbGUxDzAN\nBgNVBAsMBnN3b29sZTELMAkGA1UEAwwCQ0EwHhcNMTgxMjA3MDc1NjUxWhcNMjgx\nMjA0MDc1NjUxWjBVMQswCQYDVQQGEwJDTjELMAkGA1UECAwCQkoxCzAJBgNVBAcM\nAkJKMQ8wDQYDVQQKDAZzd29vbGUxDzANBgNVBAsMBnN3b29sZTEKMAgGA1UEAwwB\nKjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANQSsIppKx1KEIZSyPlq\nVNA8NQJ04xsV0fUlQzehwAXTpM2Km9AMXKfLw3b4Z2dIE+O4HTc+2EpIo+oMdEeA\nTn1atAAqO5XDfrYfmnRdDCcZg7wxqdCJHh6OHNXv2K58OnmoyZBh5gPoR9LQuIUF\nC4p9TyE+qUxSwrzast2XkoX5JH+p6uKE/a7eDUMu8Dlx6E1Zs9aTNyyTBO3onK0N\niCZrguuOJDUnjSpSS4P8U74Wf4k7brl70QWS6KAXWC+12VSIKivJA8SZDIeQqgO+\nji5K1adQIxQR3g17QHOq8ZmrzwUWE+XdDCwTv7dkgcnt8yR4R3+fc9tMugqSS53/\nlDMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAyQZRigqe+J/jGj1b08jAci4Dfu6k\n4v52Hb0wvHvXi/C8ccL+g9f/9sLVKP5gdYsZVSf38wPNh9xTNBzbgKwUkXO37yHY\n8vXSt/coBkhvXAjQT4ICcKdH683mfD5Vuyq7m8QP6EbqvCo0s0of1I97j0VV3xf/\nSiSot3F++5jqaReLiDU4yjhSiACuZzyMBmqZml3jGEUg4s8+ruoj9t2PoeQv9fi8\nLHC+GnICEKCgg55SoXGKtsBdM1sewIdy5KHvMet9v00PkVBFFNDSt9wDugttjPCZ\nRbonE46/2/+qpGG+nVU3jIfUjAoHvXy30w1dBwTAemUC0yeN+xjl3OHMjA==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/include/ssl_certs/server-key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEA1BKwimkrHUoQhlLI+WpU0Dw1AnTjGxXR9SVDN6HABdOkzYqb\n0Axcp8vDdvhnZ0gT47gdNz7YSkij6gx0R4BOfVq0ACo7lcN+th+adF0MJxmDvDGp\n0IkeHo4c1e/Yrnw6eajJkGHmA+hH0tC4hQULin1PIT6pTFLCvNqy3ZeShfkkf6nq\n4oT9rt4NQy7wOXHoTVmz1pM3LJME7eicrQ2IJmuC644kNSeNKlJLg/xTvhZ/iTtu\nuXvRBZLooBdYL7XZVIgqK8kDxJkMh5CqA76OLkrVp1AjFBHeDXtAc6rxmavPBRYT\n5d0MLBO/t2SBye3zJHhHf59z20y6CpJLnf+UMwIDAQABAoIBABsaqGWIO3Jq3OHO\nAtGdvWk/yKafucbkawkh0g1yJtCX1CBQ7skQS2dGq5yw8sZIeAJKrbE53vrffdOT\n8+iPhXiP9yFuRF9HtRIhx+PHYJ4+rI+I6WM1oxvUnerZ1J+4UODrqZdCa9tj/In9\nExgtJOR2T364O3r9uNXaPbIEhT2J9m6+3/7+hk1Cux7FHYLQmPnSSnxNmtuugwx5\naA4Wbytf3zFWG+1sDZQI4fgx80IBQCOVZPw1j8WzByQJko5Y0hYeEpVhXhU2AT/O\nb0Jo8w+vP/Z9N6UOLH3TCuLNKckeG5Bem/yh3NtldJztnt+rOPO4kL33d6GONbfl\nLAKTuoECgYEA9xlpIna96ZT9Cot2vgeLUIK/xyMU9WmPNRavx3akz50AxoU3ep4p\nFwv1TarXn3pfaeAV34qzM4vxkPGQ7QF8j/qLrvjpXWQwfwiRqrC1T3S8J8FZegGc\n11ZzTURICniOtgNn5Ai/RM3y2tR+rOC2gTUpB7UUAW1cOGxZ/xNMPksCgYEA27ZK\nHNdoDClAW22gWRLn8bJLjOWdVcWpoI8zULRgfcyvm9ffbRaguRx0R+a3TbHZvT7c\nbxppYTn4+RXuIhM5hI4PxqVtW9hWQvzta9wy44EBXUebwq0NjVEV3X1iB3T7s5UN\naXf0p+jwPJeFYRjU46nSqHxLtCu+Z4N67itZsLkCgYBGmLf05L/1kF+LN7VBLZ6b\n7f8kwd0nEHWd0VF/R2Jm/pBOJ5lmKvHKZ9w5yGm6YpgIA8G8EECKpZzqsFmbnSUd\nbZOi9fKWgB1q7ePQRJRIky1+njsWJIfO7iAbHzsF4tPDJM311Jnw6nZMFxHqCnma\nCLokPZPtpHUImcxMmBLcaQKBgCDCsZJTXCRUElCPrBUcCJG1cCZ2JlufuYdDUvd5\nrdmUJ6spKRtvAFAZugzuje570mexc9TtL0FcD4+0gWl7TFxDgn6wM/o568LZS92F\nW+EoXze1cbg5bYfgy03XYEKUYuOIGhPb2xMeXJPsU+sCdNvteoe84NzgmOd+Z1oU\nBK+hAoGAXiQPmXfdk/hbxdiUwlClm9OGNH6Wd1jXiFRUcGLN43CYi1rRe0ziX+I0\n2TwxHu8RcUy4RLiUlOnB1aDyQu1sUHJd+rnDqSUph5ozsSUuUBuJJxm4g7dC/9kZ\nAbs7BkHltPaTAGKW9RV07XydJPLEfbDJ0WGzkxeADMRzeOlHkhQ=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/include/ssl_certs/server-req.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIICmjCCAYICAQAwVTELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkJKMQswCQYDVQQH\nDAJCSjEPMA0GA1UECgwGc3dvb2xlMQ8wDQYDVQQLDAZzd29vbGUxCjAIBgNVBAMM\nASowggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUErCKaSsdShCGUsj5\nalTQPDUCdOMbFdH1JUM3ocAF06TNipvQDFyny8N2+GdnSBPjuB03PthKSKPqDHRH\ngE59WrQAKjuVw362H5p0XQwnGYO8ManQiR4ejhzV79iufDp5qMmQYeYD6EfS0LiF\nBQuKfU8hPqlMUsK82rLdl5KF+SR/qerihP2u3g1DLvA5cehNWbPWkzcskwTt6Jyt\nDYgma4LrjiQ1J40qUkuD/FO+Fn+JO265e9EFkuigF1gvtdlUiCoryQPEmQyHkKoD\nvo4uStWnUCMUEd4Ne0BzqvGZq88FFhPl3QwsE7+3ZIHJ7fMkeEd/n3PbTLoKkkud\n/5QzAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAU1IPD//ftlyUZAoHrYBY0fkK\nROOB/3ALET2+3QYkQJKnDFSucrvYRU8nHuGVe2aE+ej1QZ5yOzicwRoOuixkD98V\ncg+6ngol96SHXoV4j2EkXYv0w9bTVlUdTUH8/SDfnNLvzzZdRNiw9fOlt15DWgfY\nMtJi5ReH555lLQ6JU+NNzyP+zAExx9uUdDd1H8DyIPjip5xexxKbXGSgPoDnGGVS\nzJ7SkzmlGcHR3XYqQglD6poqnvxhDm4O9L6cK74RIJzUr+lDOVWhVND+6zu8vrwW\nOqM2V2qN8MNu18umKfqXCgcWYFpwhc0fJktPXi8bE2knU9vbOLJHV5w0XET4AQ==\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "tests/include/ssl_certs/server.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFtzCCA5+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBJMQswCQYDVQQGEwJDTjEL\nMAkGA1UECAwCTkUxCzAJBgNVBAoMAk5FMQ8wDQYDVQQLDAZSb290Q0ExDzANBgNV\nBAMMBlJvb3RDQTAgFw0yMDAxMTYwMzA2MzZaGA8yMTE5MTIyMzAzMDYzNlowSDEL\nMAkGA1UEBhMCQ04xCzAJBgNVBAgMAk5FMQswCQYDVQQKDAJORTELMAkGA1UECwwC\nTkUxEjAQBgNVBAMMCTEyNy4wLjAuMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC\nAgoCggIBAKzZ4sOURW7+OVA+3UhpPEElSfAbnre4V7iCt/P79za1Fgp5gUmm+zW5\nJi+GzscNxFsVTEV6IL3V8nZd5EVjvgdCoN4GP4uu5r1aroRq1pv18+bwmKCkd/KY\nQ7KyYrCuPBtu79kItaqgzrKFz+xykH0Gcpcg8xnA5fsv1Jw1Gsu41XXzeSJmOkLK\noRVZ9aXV2X/8YxFQmtqwTp3wPthZFuZ3yVGLoXL0f8a8ipoxDR4IicEceSfLrpjm\nnTIqvQ+ZPKpjN2OgWPOFgiRdSnLKlCY4r1KDzPzEiJPNq2t2r8aG9M5JVi8CjvFA\nL6lNUWPeArcYkwveYEKD2+M0fPngO4UpksGOkHxxPP4NYP7qzCB9UHoenEyA0kwC\nwB09cXhfxspLHItnVdTOQwaP0hzfm9EAZnkxohIOczWPoswTj95BwJv/dUDu6GgI\ny+SMnVJICSz4HBohuEJQKued4Hn3b3zjOJjMjI2228u3uawLFybyIQwFCchr1IDL\n3yg5nVxefqRBCoh2+zoDcrJGsZTaZRl3KdirF95vAeCnV+CLp22zK3gU0IH6rG3H\nBBmloZySTPEF+TLqURKMhKicJmK1lSTOw7e3RSbFnolNVPPGFhP0XeEjCLc7bD8I\nOtaporulet0tPoe/7/rj6Purey+6chv68qwSuhIWYDoovzqRbUrpAgMBAAGjgagw\ngaUwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQg\nQ2VydGlmaWNhdGUwHQYDVR0OBBYEFG22cJXiXTp6pXrRuz8QX0AHgdvGMB8GA1Ud\nIwQYMBaAFEjZMlEqMbJRRI2/ULRMRKlXmC7AMAsGA1UdDwQEAwIEMDAdBgNVHSUE\nFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggIBABTXjyCW\nkX/zqjIQci8ryrmtQOEMa3Z5k3gPeyCHn60AK/QDLhSpLPBkpoKT9T2VU7Si7HPa\n4yhfJ3081wQsMcJIZ4ks40OYsQb17rYmzoUsJzsRfZ4vdgmQx2K3QQY6vkNFh01D\nTrWOfQeB+UnuzRgjLZP4l+7YCPx5/LcK/mfyUhFD6/AxSFi1qhGpQgs470+8kurd\nbCvTpzWaBODFlQIJ0pi5dG8MkLVdYA7iAWdqH5s6NGfsPdTz8EmuYv5inSZlTWw7\n+FLz1wGbXuvaeOxulvX5PIWzy1cWRmlio6y7rt1Kx1nhY3NuhM5w9I1Jx1aV7DY+\nPisziy5QsGrmlRfCF8Ks5vQSUCfLhwFLcKZ/44oKFx9ufmIorW/En5/FYX4OF5hQ\nTloB9C7bQnu+vFOWlxH3T1P1qb6B+n4RBk1rafkECj1VP85nbCj6yXlYGM9As1QT\nqf4tZHNJ7pGb8WWTJrUtvDYtsXtZWbktYzUUMbzdM01IXNqRuI7AEVY9P0VkGhKb\nbrT1UlshqMIR2PjXqRdm7WvfOZxLIA1EyOvTGRna9u4nZUc9iNsMTheys9AXoMzN\nu3aCPQJEYgQZJIkZaEAldIG3C3zOF0WGcj2kPObLzwd51xxOJGZVQoOJHyz8y4PD\nsWH/lnjRiy92v788PK5wttGg9lFZ8GI2y1GH\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/include/ssl_certs/server.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIIBmzCCAQQCAQAwWzELMAkGA1UEBhMCQ04xETAPBgNVBAgTCFpoZWppYW5nMREw\nDwYDVQQHEwhIYW5nemhvdTESMBAGA1UEChMJTXkgU2VydmVyMRIwEAYDVQQDEwls\nb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKg4oNy7liSNUU1T\ngZQEEKPJ1znNgbCzbEJ/QlsKhLMzIjiv+xaTvYFffUcZw++NwsCwQUYsdAsmDwQT\n4wdTr6JBKwYuKRnyL/l5N/h4VEmNh2MGz2NSqo66QNOiJMOYhmuxmcr08WXPr6Hp\nA+KtpQgNt2NFB0nSbb/EvyJgSx85AgMBAAGgADANBgkqhkiG9w0BAQUFAAOBgQA6\nhlKHAfMWwwyqdqsMPd+Q4LetmER+ARnMZTOMSMp1iFXEdTIZwA+WHfxdN+KJ4gnp\n3QWpoG+q4O2tyMZQDB9wSAY7LbFyHjzpqq1JJXL2Qpa71NqE0JRZEToAxuyrGf0n\nNCpZp0vHFERhgntLkNSM9sMFCWuB6dB1YfeCkTWCeA==\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "tests/include/ssl_certs/server.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIJJwIBAAKCAgEArNniw5RFbv45UD7dSGk8QSVJ8Buet7hXuIK38/v3NrUWCnmB\nSab7NbkmL4bOxw3EWxVMRXogvdXydl3kRWO+B0Kg3gY/i67mvVquhGrWm/Xz5vCY\noKR38phDsrJisK48G27v2Qi1qqDOsoXP7HKQfQZylyDzGcDl+y/UnDUay7jVdfN5\nImY6QsqhFVn1pdXZf/xjEVCa2rBOnfA+2FkW5nfJUYuhcvR/xryKmjENHgiJwRx5\nJ8uumOadMiq9D5k8qmM3Y6BY84WCJF1KcsqUJjivUoPM/MSIk82ra3avxob0zklW\nLwKO8UAvqU1RY94CtxiTC95gQoPb4zR8+eA7hSmSwY6QfHE8/g1g/urMIH1Qeh6c\nTIDSTALAHT1xeF/Gyksci2dV1M5DBo/SHN+b0QBmeTGiEg5zNY+izBOP3kHAm/91\nQO7oaAjL5IydUkgJLPgcGiG4QlAq553gefdvfOM4mMyMjbbby7e5rAsXJvIhDAUJ\nyGvUgMvfKDmdXF5+pEEKiHb7OgNyskaxlNplGXcp2KsX3m8B4KdX4IunbbMreBTQ\ngfqsbccEGaWhnJJM8QX5MupREoyEqJwmYrWVJM7Dt7dFJsWeiU1U88YWE/Rd4SMI\ntztsPwg61qmiu6V63S0+h7/v+uPo+6t7L7pyG/ryrBK6EhZgOii/OpFtSukCAwEA\nAQKCAgAuGaKQNRBbrPQOQeX0tRR7730Ly+IF7+V9cWesa+wgLCXnv0ZLJXzkdDAK\nBvNJRZO0EzcXZJ4s403aYepK03JQL2YGMTZrg8dIg1H+QRxh1jQpyMt8NMFE83VC\nyK7nnmCrlGflw5voGSFnnaa50khVAA453nDX0onD95rF2qf945sS7U/Ga+tn+vEB\nZ8/LXgC3UNH/QTc70j/1gajRPNmNuC76T7pMlYcqswhoXWK5Ot+6oGRzx7AWWZNd\nWOa5rU3vx6xzJsXylQ4DstnzSbI/i2i/IiXe2G/ws67fZ3U0BrRt/EM/IHiNQWg0\nyElH0GN6/9PgG3wi4VYY/9vBLRvSVLJEjKOiWnI07/vBfA816xPSog4RIiUHnepT\nMoqy2cdqyTyJA3kzohdhhr5ejYN67wxg0U5sriqIxDcnIBafHcsuQK22vpkPzkzC\nQsgwYTJzKjp6uC9UR7/+gNOsZxo894uU3rtuCTYVcEeELbnGguyk7M1uXdNRMQxT\n+5EMa/7RmLQAAPUd2Zl+SJv7ofD7gzJ4uwKuajVjYBPStnHRlDTG3aTxLToO/2it\n853MrgSxIwmEvXIFGTuGMXMrh7p+BYEwmReQb6zrihOM4cRqyUJBoU8t6b85ZIa4\nXbpG3upCQIAyDWsP9go/QMRpXJim+z4BfJBw78XhXJd/6Tb+MQKCAQEA4NgoNqf0\nnMQiMLJBVzIaNzwm/pThemJ9uvXbeJfEg7SZ0QABAEAbfvG7UIOcKpyOnp0UHDKa\nx2YdjzXRxnFdcr9NwfSDnSdcgeIz/AbleANtkt+tCuNP2mWU53cYg4uTpjboy2BC\nIrknH1uuJlJC7YtnoJptStqde3mw4P2AUCSpIz5yFkNQt1PzvHb7RjCZ/BFQDb00\nCJRn8P0Ak6m+K+SVW6aJTz6u0/Tw12rz1C+Vmp2o1EAQweQrqC+ia4M4QC3FYzxJ\nbUhCW03ky7bzkL9n+nVhUO7mNUAI3vOuUmBUHEtT3hfq/2iGR84jUOcTyZgNF+rO\nmMuiaBxnQdXidQKCAQEAxM1i62+vclWwFPjjGkrGR8+59R7C4mAjTVeAsNSk1yGh\n/V2UnGTIPp7o0se9PcMo3FORjdZ8s95un2VqU3qymXRV9fi6rbDXKWdQ7qN47xbk\nmfPmMD3/QnYZL2Lf2itpnW3FHqa9i6sIcoHtPMdKCGAoGTyIB7dV6jAcHewXzAt7\nyjAyt/43NFCo+rUYxGWDEQdXEweo4ObOai1uU0mJpzNF/Rw4pDvm2nVa3JmELIML\nBBe3YyNSRLVDxlm3JtEAWA4WYgiyHus9bOwNN0BRTCBBIPKbBH/Kc92wXG7OJynP\nFAxmjIOMWqJyrfsUze111dwVEDGtrAarbhPmpeNQJQKCAQAQpzHjSnFNoyE9Jha8\n1JMogCYLOQ+yNtVFIayNs//0GU5DMpLgZ98hedn7zqOkbFAL42p35C+ekSujTcMp\nxX+8XfruYKPW4fFyFUTwZ7pbNzXIaRSx98AUVu2+bw27wHzBFNQNLCX52GjTY8D0\nSad3XJniRU3O+34qhQravyvfw7dbSua8S2QREAC/driJrFPbXZTmT5so/4TrTnHa\nffn92kJNtXn6CpR8Umn8KonAr32PJBo7DjuvgKzTGiQXfnKOHlW+UdrGY1G23YAS\nrow/rk2POV5p1ifqzcFacgX5R/Uy20ls66yWGjHH47at6U+H4OEnYlbG7l4rA9+J\n+sN5AoIBAH15VXA2LO4Ao9s7M9A3jGOMlUsq6aXbFabh+zLC4LYow1b+qxlrSCBk\nwJxirM0L+KlAvrRn4+nqDy2hQ0EmIWilO4kvpBXJgeTr8jW0FA6b0fQ2BLUNUgnV\nsgj8SSyYMSYmLl90f7TkvuYC4Ha3uQPOCsLMTQ5eXXCKa+LH/AQj7jDiufOFKT05\nkAMII1pUPlRFEccYQxvhDAm2HXkMka8Aicbuj05mwrDV0bCEUDjw870QQeP7SSB1\nUVjNKnCdIFMD6FFoqef8o5UmiuaHI04d4mhXRW6A61skJKVKIDXn+hcWMOu1k27V\n07gJNSJ2G4LWZreEdO4Q8oukNJHfc+ECggEAZDW7RFC05XXvrErpztnfFDwT3cTt\nhdU0wzkTDv63i/U8tktrUKFrr51/9s6YR+Jk0H3k0QshkkZcGrZZxGVo5Wxi8BhL\nAkAIwOKSgqXjy7VEibb3+3DfjS4nYXTeJxN1uTStJwk7c03sJVHIg8cZ+30fIiPr\n0see5Lw7VGj4vYG0fSt0iEyjrl16Y5otMK9s0H1yh2hrRzfI2t4DltIIfAi25h4w\nEzO10q57QfPhjkw8J846xmTO9X4clolTMDM0qkN3Zhf5d6kwexPi0/4QUhSZjYGg\nfq0q62fl8tiVpZSNXYcU4tr+PYByWdCeH8ObmnLD1GD0yskjx07LJsbuEg==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/include/ssl_certs/server.pem",
    "content": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCoOKDcu5YkjVFNU4GUBBCjydc5\nzYGws2xCf0JbCoSzMyI4r/sWk72BX31HGcPvjcLAsEFGLHQLJg8EE+MHU6+iQSsG\nLikZ8i/5eTf4eFRJjYdjBs9jUqqOukDToiTDmIZrsZnK9PFlz6+h6QPiraUIDbdj\nRQdJ0m2/xL8iYEsfOQIDAQAB\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "tests/include/ssl_certs/sni_server_ca.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFejCCA2KgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwQzELMAkGA1UEBhMCR0Ix\nEDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQxEDAOBgNVBAMMB3Bo\ncC5uZXQwHhcNMTcxMTIyMTQyODIzWhcNMjcxMTIwMTQyODIzWjBVMQswCQYDVQQG\nEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UECgwHUEhQLm5ldDEQMA4GA1UE\nCwwHb3BlbnNzbDEQMA4GA1UEAwwHcGhwLm5ldDCCAiIwDQYJKoZIhvcNAQEBBQAD\nggIPADCCAgoCggIBAMeENPus84HfIaypUVybbn5ZZyu1gXSYZAnDYYHhOlHu5kFh\n26ohnHnoheapXVRbUtDZgUW5BztA+vDhKkM5G3U4p+YsqcSA/IkF2JQBkJH1jrxs\nXMDtXIA4/MzYAVtyySqoMsrbkSCDzvI8imsbZI9O1x02VgaqoaQ9JZvglt1nhTIY\nYpTbTKGCzMmrnFGHITeBsVose1ACFIAtqKqvUaA6TxcCtVcrENY3T3N/vEmoHHBg\nO7j9+UvYN/WRPNcv0aIOlro83O9xQuJdRCygPmcv/jkqWRKm2Pu0lEkvbMCvOJr/\nFz/kQg/6EVmIMsR/dgOMIjWjmpeTx24UJaNHUdNCeUbxIyTIyl9ISLWLM0Ipoq3L\n95ygH6FSESAOpc8rVE2A5YZCtKlwvogMx3C/tMH1gWwMlfxn0CopP4Jx6lngw3yn\nIpP0hnuCauQikBkyhsGu4J0Lbx5BsyDWQLwjiq4s8EcEy0vTdAvhDMRUGvpnjWaa\nzBDVVrzHaRFvp6ueh35xf+dYieGL5UTICyrrQX9wwSafHV90a87y6XU+RwA25Dmc\nV/kTn8/xkFv/4CbZp+0yArKpwXjqBMsQFbkFjT+Ky9MN+nedoFUMl7slgjpBs6RI\nT08Hoa7/kMZGtLCiGcRXlwiIhB00gDz2avUvFTxwik9ssjDqBJGzNbBvZGb/AgMB\nAAGjZjBkMB0GA1UdDgQWBBTjyuOBGnHnb+x20diIHG3sPuJFGjAfBgNVHSMEGDAW\ngBQ0s99BNsV4Dszph6PArogA8HP4TDASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1Ud\nDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAFrQMmCoSe8uJbJLqPGMPW4ZE\n0U81WWboc9FrSfezBYkTKuCjMTjQFv8oLp4pnuWZw22mHrOiz9izjNnXcR/rdVbG\nCip/JSHlTzHd3gnxSSd2diXvVrSb4zuLyN6WIficlsj3QdIP4Cdfv1pdHBTsRPbW\nvuHpwYqqjxPBjqvoVZyZRyRM1pqpxLCvxOt4S3z4uZXyIH2UI7yWxI6MvdHBZg5m\nTwiVi1gEQuRnYRy4sK9qJlBo5jERvzDr46W1e9Yz5sNMPfwWb64Hkkys3GqJt5am\nOKxvgF3lvIK90vBjenGDfYEENP+0qdmdpHQkxGM2cl9iPqW8iyRGBVjdbzTIeRgB\nu+CEbffSKWCpbm1Zcszv3FHMlsU4Px2pUTdLwEMgBQUPW/ngBMdRgVZE01GI7D1k\nAM3hO347eehQTWoyuHgsnB2uaANEeR/XFBGDFmbs5ggAzxxRsH1FxoHXrv3iDIPW\n9lKnSow+OZGcPKgaY5Nsregt4jO+pQqhzxlMa2QMfrMoGkEvrKNcDqeEI6KQa1SI\nZbq7sjf2w+VL0oQcse+gtg4q771dKy9V/XcfnPbiBAXfQqGj/v0LmZRn3ZRrkgzl\nCaZocqVRJZbqu7/iu/p9pDRMR03vhBWPn7iM8VqzkhKjAlD0rolnQqvOAXyPpd/O\nqYuYP+6Ymt/rUWXGVh0=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIFbDCCA1SgAwIBAgIJAJcrLHeFQrGEMA0GCSqGSIb3DQEBCwUAMEMxCzAJBgNV\nBAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5kMRAwDgYDVQQKDAdQSFAubmV0MRAwDgYD\nVQQDDAdwaHAubmV0MB4XDTE3MTExNTE5MTEwMVoXDTM4MDUyOTE5MTEwMVowQzEL\nMAkGA1UEBhMCR0IxEDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQx\nEDAOBgNVBAMMB3BocC5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC\nAQC4bIXbh+e+OCbLum2FYB8i6ZnOShp1teWNEE/8A4ot20wkPxLeXFSU1Egn+3BG\nfxHKrWxV/FOiL7bims/SfwptFkgNZkwE52HVk49zgQevG38smsnIGuBMXMKebLPT\nwlsIWRqCmZqEmh1wNcC9+qwoHoYjvJvMYfFvofiSc7qkTFsjoBVe+ho90vjYMznr\nBs9DlD88iXO0kKN0Bsu2igz68JWkUsoNeD6UKSir16SmQZxdFPCM0O0gxmOyz4NZ\nw8pJ5XGDJyS82fVp13tYCOBFhK0QBvvzaz/gN6De5arefm6cer9TX0HE+VvGhx4T\n2lqydFLfAVKTTNoZHOQk2mFVUm7dlMzLvsgNKdEZH6wAa12eBImCea+CyJNkbGLC\nnrN6a93iGqTft75sbDyvzRk/BdnxadkAPlOxgK1qGrOgeS0+7chPMh24MRoKj5bU\nlYGdcp7Y9qIW1hZXFfgPJO0yMOug/7J14jybp+YhymBIBTZYi1zbGqGNXdLPyp77\n9KTfyJE9KkbGegBvwqJmZTW8QoUDllopDjr1zaIqHWJqfAK7vGivu0iLVJHBRbRs\nw9c48RCjfIh7AkvFKsdIDt9SMdZOdm9D8SXosYPM1zMFQsFFRPh/nKyl9QnhWbwd\nVs1tIFDrb10Z2sn0pF/V0F0gubR5rvYs+3h/dLciLVFobQIDAQABo2MwYTAdBgNV\nHQ4EFgQUNLPfQTbFeA7M6YejwK6IAPBz+EwwHwYDVR0jBBgwFoAUNLPfQTbFeA7M\n6YejwK6IAPBz+EwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJ\nKoZIhvcNAQELBQADggIBAIkctDRCNcNVs2qP/O3zZsVZ1cqlqYNjcHIVHqNZRvVb\nbPtInZ/i/6BbwA5QM/giKpl2aMwetUnezjHVD+7/m24Mfs1FWyPD3joCMIANj7hR\nMNbmX1LQBMt/bD3E5trq9S9rZ++/ny4gn/z2jH3x+lzDMBDiam/wXbvSmyIUPUX6\nV8FFpkTBNERpurUvX1NEFLuiilWKrI7/u1qS11PGBcX3pNQNfhKNMtuFz74hAE7C\nRE8756CGu2bZzydYCarUXSNTXqNm5ly2J0UN6Al4myFwj/6XnLnDhXDAI8CaqDMD\nMwmxUzJvzuphNS085ZIntrSgPJESGozevEsf9bGD8GyoBQN/cSqOJCFfnJqxtKHb\nwNa2F78SXYsiWkRb5EzoyCeon+0tygG6gLBouVY8ouCerExkH9qzRLLzFInxmCm2\nill7c8Xmml4H2BxJE33dz/Qh7PM3eF2A2VriioLDj4+HOiWq/1BjdW6/Xiwx5iP2\nnC6Cex+lBt6HkP9WIiJbGxVeamF5jxNWWvLoUkrjFhpukjb5BPR9zFt7mAty9J/T\niQt26yylP4ucmL9KB6lv4ISW4Ri8mhMcYh6nF3tIYWZMCSQKqkyvymhKdLMZa20A\nIME2oxnfZcmsfMpM6BYsN4XDfGBeYctqqt2AFRNLkI4wFkFFdPTMcTi1q0iRH2Xk\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/include/ssl_certs/sni_server_cs.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAvy5NhzktzEdsHTbGB6vqYANms5rn1zXFmTJrGlWCwoIsNmTf\nahvZkrC1cCXTZ7fbPB8XQbpAtz2ZSU7OcwBW9B8okYUPo9zi/ptwcrgsQsN0hrcD\n8MBRUccevwime5fLvg8E9RJ/68y9y3BnRcVWYO2sAK9juTfidNjETU3Bb05oXv8D\nSD/6onXQu4uXDgsQ3cRXeld9UB0xazmQXyyiIqXc/cpTAnaEVYzn28aj7NlUbzNq\n511UXMXY44x9EcXWpPVZ7heNcJNzY5DCNzmtXKrt9yiMpWQcPXEzsESVxAMqib9u\nTFOlvVX17LIPxBG656PjTD9J1h6kBbMCUxzs7wIDAQABAoIBAQC85lBeY0X4ST3v\nI7bJz7kWQ2YP4uhfAdeLhoDDFWjNLffniwYhfwEc6xNri0R2f/jUT9gX7qORKwEx\nqPdeNCC2t67LElGg1FlJv2Z9Q7MgCKYzkdQH5s6y4e9kTHTLO/JpiceZKz1QTQ3f\nXOH9032E6nIAf0wmr6xHTgOwajrN8VI5BuPEMVmEwIw3AtYeqVuPCNKyGR4HUVkC\n2bAydnGngbRJRnNzmKcWJancxpHDGBSFqPyuXMFC7Jgo3ZmyCbGp99vuXVk/sW9x\n5aj94M9nRE0guk05ivH2/JZao2uLYkIgjFWlhNxKdWgWRk8DEuN4djC8mKS9YH1q\ncrYRToMhAoGBAOspUTtKP54mpZmyhxuDqj02JaJRzNTskPHsiF1UhtXuw7uT+ryV\nekUFLNXoFmn9mbx1WVaUvGH4qjilvQOxz7u++lz0ApqJEfyM3jc/cC40Y5zcuGSu\nEtbg+SyDoytlgMCIydJyrS7NNALSo5p5oG6XY2f8yd/DCAmo8LzypaHRAoGBANAf\nR1SlBMc/bOsi6GrJxcBVSCFMiKYiO5woL5aUKa9yM+UQuQ/6xbQ7Q+sOlt0FH3xo\nAJ2L60qTdjyXVtjOdtXs5ZC4l+C6AfnCx6yLr+fNc4SOYXEfqS4LZylgwKd9KyVB\nasspIW9Idbgebmi6vPyt9LDkIp0h1VuFGjkvQJK/AoGBAI4pbS0dprXyARyYW6sb\nfpgAmuG099IkrT9DUfCx/81myTclr2fAKal+BmvOIXaz0/OlMXvw8K19iVIzh7+r\nB70lJ+93p/dKM/BsLI5TsHqOO0YB/QsIXOVAHgJ2FfdPJnW+e9vYba+kZ/Po6PSi\n4ITaykJ8BIJcQgis89QWEGFxAoGBAJhQO+jzuDKF9ZWEf6ofrw0anOZZ16wWY5/e\nPS2rk3JmVxpuibHrKqPDt+ogTELHDAsFJmYmz3VNxHuFmrajK49Wh4/JuMVr/CQo\n6+8YcA1qa/94IFIlBLDBAafjujsZvOjQHnM+z8xcsGKmStF00Pjv6qNG4xoyd646\nFD4DmfOLAoGAWXehpopZKXE9gRAni881ucK6WqxPPBoofbozi09D0MmfarIVaSkv\njNVVHBfLWd7IEXTjiipPBeUqq6Jc3pscN1Vp4rrl8jTmVTdazEv0LuzpdUFqmNo2\nM+xw17uz9D9Q32/aW1Lar0PdIaL/wGEDEyzEBFwrGppcENLilPz8gzU=\n-----END RSA PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\nMIIFIjCCAwqgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwVTELMAkGA1UEBhMCR0Ix\nEDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQxEDAOBgNVBAsMB29w\nZW5zc2wxEDAOBgNVBAMMB3BocC5uZXQwHhcNMTgwMTE0MTgzNjEyWhcNMjYwNDAy\nMTgzNjEyWjBGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UE\nCgwHUEhQLm5ldDETMBEGA1UEAwwKY3MucGhwLm5ldDCCASIwDQYJKoZIhvcNAQEB\nBQADggEPADCCAQoCggEBAL8uTYc5LcxHbB02xger6mADZrOa59c1xZkyaxpVgsKC\nLDZk32ob2ZKwtXAl02e32zwfF0G6QLc9mUlOznMAVvQfKJGFD6Pc4v6bcHK4LELD\ndIa3A/DAUVHHHr8IpnuXy74PBPUSf+vMvctwZ0XFVmDtrACvY7k34nTYxE1NwW9O\naF7/A0g/+qJ10LuLlw4LEN3EV3pXfVAdMWs5kF8soiKl3P3KUwJ2hFWM59vGo+zZ\nVG8zauddVFzF2OOMfRHF1qT1We4XjXCTc2OQwjc5rVyq7fcojKVkHD1xM7BElcQD\nKom/bkxTpb1V9eyyD8QRuuej40w/SdYepAWzAlMc7O8CAwEAAaOCAQkwggEFMAkG\nA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVu\nU1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFHPfd8dK\nLz1R0Ck4WV1B9AWXd5DSMGwGA1UdIwRlMGOAFOPK44Eacedv7HbR2Igcbew+4kUa\noUekRTBDMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UECgwH\nUEhQLm5ldDEQMA4GA1UEAwwHcGhwLm5ldIICEAAwDgYDVR0PAQH/BAQDAgWgMBMG\nA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQB6WSIHEyDXLZxH\nhZjqSNQOA7Wc9Z2FCAiD29xYkGTL8WuPVGGP1mu4B92ytj+PMWwqSReDa7eTGLE7\nO7ozw9l+c+gNmHFNikSsGjlV2E8CToQOFMny+jAQYMSXf8UbTp9xDfgG02t/71hv\nSLWqdeHMLcR0xi0nBQH0vDOkwUbuWYqFa3jejHieGhykHM6CkIk6lqnyOEO+ooIF\nZsLprrg1ss/mXCPI6niP0hze55ERKdxI7Rk8sZ4pVkf2SUWqZrUS0aJ+Ymmwi6Xd\n2V7izq5N30PkJS8MtqII4FAjRBIkwPh0sy8PmW/DzkYU+lYQnDfYLKDFKcj8xJK/\no8oZUBsQltrSj0KlM9QuqxCTCBCy1nXZ9WHOhq+jdLiTc1Oi60uEHcUMrLK8aYc4\nHqIvZS6C2iwMI0d1OP3VxmAbMQ9yqRi+FbLYavJ3H40jrU9SYqdxa0BrTaz8MJNE\n6AEwgQDPChczSghvHME+Fs4mtGCY3TesbNZKVahQRjaFIhMZIZ4RP4CRc0bJOBG+\n8Me4+KHNsD2ki5b03wAN6C1P2QrMzI+gH9fXLZYp761ciDAsX6YIzrhHHYLxYpJH\nBkQKKs8dCQWE5IzgVrdlvC3Z1/l9om66wHqqx7nKnPfYs/Sfnwe9MpCD6xJrXiTm\nWS7NM6fbQpO9APNr7o0ZOjbbWFzlNw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/include/ssl_certs/sni_server_cs_cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFIjCCAwqgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwVTELMAkGA1UEBhMCR0Ix\nEDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQxEDAOBgNVBAsMB29w\nZW5zc2wxEDAOBgNVBAMMB3BocC5uZXQwHhcNMTgwMTE0MTgzNjEyWhcNMjYwNDAy\nMTgzNjEyWjBGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UE\nCgwHUEhQLm5ldDETMBEGA1UEAwwKY3MucGhwLm5ldDCCASIwDQYJKoZIhvcNAQEB\nBQADggEPADCCAQoCggEBAL8uTYc5LcxHbB02xger6mADZrOa59c1xZkyaxpVgsKC\nLDZk32ob2ZKwtXAl02e32zwfF0G6QLc9mUlOznMAVvQfKJGFD6Pc4v6bcHK4LELD\ndIa3A/DAUVHHHr8IpnuXy74PBPUSf+vMvctwZ0XFVmDtrACvY7k34nTYxE1NwW9O\naF7/A0g/+qJ10LuLlw4LEN3EV3pXfVAdMWs5kF8soiKl3P3KUwJ2hFWM59vGo+zZ\nVG8zauddVFzF2OOMfRHF1qT1We4XjXCTc2OQwjc5rVyq7fcojKVkHD1xM7BElcQD\nKom/bkxTpb1V9eyyD8QRuuej40w/SdYepAWzAlMc7O8CAwEAAaOCAQkwggEFMAkG\nA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVu\nU1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFHPfd8dK\nLz1R0Ck4WV1B9AWXd5DSMGwGA1UdIwRlMGOAFOPK44Eacedv7HbR2Igcbew+4kUa\noUekRTBDMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UECgwH\nUEhQLm5ldDEQMA4GA1UEAwwHcGhwLm5ldIICEAAwDgYDVR0PAQH/BAQDAgWgMBMG\nA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQB6WSIHEyDXLZxH\nhZjqSNQOA7Wc9Z2FCAiD29xYkGTL8WuPVGGP1mu4B92ytj+PMWwqSReDa7eTGLE7\nO7ozw9l+c+gNmHFNikSsGjlV2E8CToQOFMny+jAQYMSXf8UbTp9xDfgG02t/71hv\nSLWqdeHMLcR0xi0nBQH0vDOkwUbuWYqFa3jejHieGhykHM6CkIk6lqnyOEO+ooIF\nZsLprrg1ss/mXCPI6niP0hze55ERKdxI7Rk8sZ4pVkf2SUWqZrUS0aJ+Ymmwi6Xd\n2V7izq5N30PkJS8MtqII4FAjRBIkwPh0sy8PmW/DzkYU+lYQnDfYLKDFKcj8xJK/\no8oZUBsQltrSj0KlM9QuqxCTCBCy1nXZ9WHOhq+jdLiTc1Oi60uEHcUMrLK8aYc4\nHqIvZS6C2iwMI0d1OP3VxmAbMQ9yqRi+FbLYavJ3H40jrU9SYqdxa0BrTaz8MJNE\n6AEwgQDPChczSghvHME+Fs4mtGCY3TesbNZKVahQRjaFIhMZIZ4RP4CRc0bJOBG+\n8Me4+KHNsD2ki5b03wAN6C1P2QrMzI+gH9fXLZYp761ciDAsX6YIzrhHHYLxYpJH\nBkQKKs8dCQWE5IzgVrdlvC3Z1/l9om66wHqqx7nKnPfYs/Sfnwe9MpCD6xJrXiTm\nWS7NM6fbQpO9APNr7o0ZOjbbWFzlNw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/include/ssl_certs/sni_server_cs_key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAvy5NhzktzEdsHTbGB6vqYANms5rn1zXFmTJrGlWCwoIsNmTf\nahvZkrC1cCXTZ7fbPB8XQbpAtz2ZSU7OcwBW9B8okYUPo9zi/ptwcrgsQsN0hrcD\n8MBRUccevwime5fLvg8E9RJ/68y9y3BnRcVWYO2sAK9juTfidNjETU3Bb05oXv8D\nSD/6onXQu4uXDgsQ3cRXeld9UB0xazmQXyyiIqXc/cpTAnaEVYzn28aj7NlUbzNq\n511UXMXY44x9EcXWpPVZ7heNcJNzY5DCNzmtXKrt9yiMpWQcPXEzsESVxAMqib9u\nTFOlvVX17LIPxBG656PjTD9J1h6kBbMCUxzs7wIDAQABAoIBAQC85lBeY0X4ST3v\nI7bJz7kWQ2YP4uhfAdeLhoDDFWjNLffniwYhfwEc6xNri0R2f/jUT9gX7qORKwEx\nqPdeNCC2t67LElGg1FlJv2Z9Q7MgCKYzkdQH5s6y4e9kTHTLO/JpiceZKz1QTQ3f\nXOH9032E6nIAf0wmr6xHTgOwajrN8VI5BuPEMVmEwIw3AtYeqVuPCNKyGR4HUVkC\n2bAydnGngbRJRnNzmKcWJancxpHDGBSFqPyuXMFC7Jgo3ZmyCbGp99vuXVk/sW9x\n5aj94M9nRE0guk05ivH2/JZao2uLYkIgjFWlhNxKdWgWRk8DEuN4djC8mKS9YH1q\ncrYRToMhAoGBAOspUTtKP54mpZmyhxuDqj02JaJRzNTskPHsiF1UhtXuw7uT+ryV\nekUFLNXoFmn9mbx1WVaUvGH4qjilvQOxz7u++lz0ApqJEfyM3jc/cC40Y5zcuGSu\nEtbg+SyDoytlgMCIydJyrS7NNALSo5p5oG6XY2f8yd/DCAmo8LzypaHRAoGBANAf\nR1SlBMc/bOsi6GrJxcBVSCFMiKYiO5woL5aUKa9yM+UQuQ/6xbQ7Q+sOlt0FH3xo\nAJ2L60qTdjyXVtjOdtXs5ZC4l+C6AfnCx6yLr+fNc4SOYXEfqS4LZylgwKd9KyVB\nasspIW9Idbgebmi6vPyt9LDkIp0h1VuFGjkvQJK/AoGBAI4pbS0dprXyARyYW6sb\nfpgAmuG099IkrT9DUfCx/81myTclr2fAKal+BmvOIXaz0/OlMXvw8K19iVIzh7+r\nB70lJ+93p/dKM/BsLI5TsHqOO0YB/QsIXOVAHgJ2FfdPJnW+e9vYba+kZ/Po6PSi\n4ITaykJ8BIJcQgis89QWEGFxAoGBAJhQO+jzuDKF9ZWEf6ofrw0anOZZ16wWY5/e\nPS2rk3JmVxpuibHrKqPDt+ogTELHDAsFJmYmz3VNxHuFmrajK49Wh4/JuMVr/CQo\n6+8YcA1qa/94IFIlBLDBAafjujsZvOjQHnM+z8xcsGKmStF00Pjv6qNG4xoyd646\nFD4DmfOLAoGAWXehpopZKXE9gRAni881ucK6WqxPPBoofbozi09D0MmfarIVaSkv\njNVVHBfLWd7IEXTjiipPBeUqq6Jc3pscN1Vp4rrl8jTmVTdazEv0LuzpdUFqmNo2\nM+xw17uz9D9Q32/aW1Lar0PdIaL/wGEDEyzEBFwrGppcENLilPz8gzU=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/include/ssl_certs/sni_server_uk.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEArMpFePSNUHNHCiUWfsSPQxHaQTpDfogEISIMuGq5JhEuHWyl\nHRe51JWZGi39a/PY9rAClfg/qqPnuxwFDoovhQsq9Kd0JnsXW11CMSqiBimf8vyg\nGE/V2DIOiex7Ab8+5zEL7cCrNyXfDk+DuoT7Np3jaSE/u31OMUAzFz4/EL7Zqzlk\nIMkHB/GSwLIJB8VbHt4Hunf28bLreTcjO/5QpNkSFtJ1PvbWRv3WheR5ykQQWGKF\n9VgwYwS0Lu7YktLF2RGFZhIypUK8jazJiwsBRQ0faSCpAXTSPYh12GDegwAiKqGk\nL+fayUdWGnnpYTVOysYL8mCoi9Ar0BPgtHEVvwIDAQABAoIBAGpg7XyWMxpAno/k\nXYEWSo0kRJa75CnWW5R6fhJbwjlpglajONQ8czAqGeRARDFeI0lc+3qRJ8FKv7Q2\nf/Z/pNnoEj4liiDWz350X/gdIztgDRVv6rCNFj7QMps/eEuPDo8PZySs5bxCGJ2a\n3qLKlE7/za4/xhkMAEPaHppEvFb15dpomqP2Fjqei9afp8tD+xJv9BGtkFIy9acw\n94AWQi974kgW7ZzfEf8RqHM0ExKiESCc26knbcBu0tQ892YHggMccUMk/UrVgR5d\nvuGOBurlYGLBstSDSKCCe6la4N/FX3FiC3WhVNescnuTZPXcX3zebh7roGjgrTdd\n6UJYBIECgYEA09XYihdwW66G9Y09OatzlzBh1Rvzjv2Vl5NmWHHb6vh38H4PcOof\nS1pT0JvRzpt8uEs4i+Eda5PyQyp4iGtD+DQ07Dxh2jiixsE3PRFRG2wg2QCgDHnS\n6vgV1VOqA/A4on4nhxLZXxhX4YvHaJYlm1bZ+amG+j5XiI/NtHJm9k8CgYEA0NB9\nbAoaZnc06+T8rMaO45boX5SvYsCZbtY3VRl75SrbMal0IKfnXHELVHuei12LmlMs\nLuFgG8jGO3+ncxUqMytoGvrmk/cM9tCNbDKVHtFbqz0qt9lkDAwLsBougKnVsNDW\nAae/tz/wYXJct86unfLh0xtvkx5Iz1we4cS1fZECgYA8YR+vfK7R8xUUuIVTAsOX\nvHqmO6lYgH87DRCG9S0x9FB7g/LyxEjXOY/aTg+nTDpobUhY1nmESE8tRdXFTI27\nGWZcT3m0sZ/z9u6/wUfVAST7tWnpJHAx+TR/8bDsHnSGHF836O98Y3vpFeZosSNW\n+5J1zxRiD5LzocmIPXOLkQKBgEgHApxXx898YwbTj8zRGMysay89DFpV8RboUWHL\nTo83/y/cMbBp+kZKwAu+MGwGMndjJSRunUY4NRik6c+qh0nrORfFX+++Efy4529g\n60scEDC7Apc0J2x4Yze1cED1VD6PaqJbiKffKD2UwyKr6lOVSgwVtKDcm2Tbc9OQ\nlMHhAoGBAIPwYVxzWM6I6pr8x2TucpBZZReLytz7uzybMNvbKCrwlETbNNXubnfp\nnPuPKzpeRI0y26pIIAbijzFW0MXq+kIu3H7we8TaImsJ1AaQCTYeoLWBVcr6RISk\n3d4i7iT35aWCuhPVve0FNIv/u3jrqX2H2C2MXMiLOsw1GFxPvpi4\n-----END RSA PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\nMIIFIjCCAwqgAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwVTELMAkGA1UEBhMCR0Ix\nEDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQxEDAOBgNVBAsMB29w\nZW5zc2wxEDAOBgNVBAMMB3BocC5uZXQwHhcNMTgwMTE0MTgzNjMyWhcNMjYwNDAy\nMTgzNjMyWjBGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UE\nCgwHUEhQLm5ldDETMBEGA1UEAwwKdWsucGhwLm5ldDCCASIwDQYJKoZIhvcNAQEB\nBQADggEPADCCAQoCggEBAKzKRXj0jVBzRwolFn7Ej0MR2kE6Q36IBCEiDLhquSYR\nLh1spR0XudSVmRot/Wvz2PawApX4P6qj57scBQ6KL4ULKvSndCZ7F1tdQjEqogYp\nn/L8oBhP1dgyDonsewG/PucxC+3Aqzcl3w5Pg7qE+zad42khP7t9TjFAMxc+PxC+\n2as5ZCDJBwfxksCyCQfFWx7eB7p39vGy63k3Izv+UKTZEhbSdT721kb91oXkecpE\nEFhihfVYMGMEtC7u2JLSxdkRhWYSMqVCvI2syYsLAUUNH2kgqQF00j2Iddhg3oMA\nIiqhpC/n2slHVhp56WE1TsrGC/JgqIvQK9AT4LRxFb8CAwEAAaOCAQkwggEFMAkG\nA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVu\nU1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFJK58uJe\nLv8WDOR8TTLUcSxO1zCyMGwGA1UdIwRlMGOAFOPK44Eacedv7HbR2Igcbew+4kUa\noUekRTBDMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UECgwH\nUEhQLm5ldDEQMA4GA1UEAwwHcGhwLm5ldIICEAAwDgYDVR0PAQH/BAQDAgWgMBMG\nA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQDHayU795hFnQNj\nfuYV21sEmkSjgtp6X4rV9JfoCp8U2gieL4mizoCPx9Q8HYaAf98hPYTofGUaCSd+\nXlzd8lhLt9GCU+BsCxGtJocrjOQDhyhkioN8iirQRTtJpRVC6a4RCBGpenOD7Z7C\nZ32uGXohzFuigP/J8/SXBdHrlpNSa03iEkkisPR46mbGj06lFVOFHJDNKA3CnQwM\n3VfqvZt0ksZi/gAOol8dtrAd3k5JBfJ2BtqsPbycEGAhsGrTK/MeqiFcO8QZiXpN\nqf0wZzOMFZJ4HqTiHs4TMMQaDK8c4qfR0l1OE224ijcB5NlqMjGAZabMfHgNZcl7\nfYPX+POsOMaqubv98uoC0PCK8NCTyfB4V/el72EEoVfwKYOxm3eYXBEE2ZNyIci6\nYKYhNa9e2k+cFueYMztV06HTGgkcHcSJATP+3ISTRx3tj1BPmc4O4WAMEMx1E0gg\nPaVrBVAylGzt8kaMaOQMBImpsALSfutq6+KtQiu0OTGS3yVUGamV4AyRjuwF1/ZA\nkZbjmNEGsf9d2rzS89ckds24QUx0zrfJ56p7lRvyVK/pML7iadv8dUH3fKDDDerD\noocLjOjNxFGN9Woz+kfidA8Siu0zsfQ6CLPlaQrVAVQH4iMuCZraqMc3TqAiUKRj\niHKp6FFrRZmY8W8HEKHv98uGofn+3g==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/include/ssl_certs/sni_server_uk_cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFIjCCAwqgAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwVTELMAkGA1UEBhMCR0Ix\nEDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQxEDAOBgNVBAsMB29w\nZW5zc2wxEDAOBgNVBAMMB3BocC5uZXQwHhcNMTgwMTE0MTgzNjMyWhcNMjYwNDAy\nMTgzNjMyWjBGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UE\nCgwHUEhQLm5ldDETMBEGA1UEAwwKdWsucGhwLm5ldDCCASIwDQYJKoZIhvcNAQEB\nBQADggEPADCCAQoCggEBAKzKRXj0jVBzRwolFn7Ej0MR2kE6Q36IBCEiDLhquSYR\nLh1spR0XudSVmRot/Wvz2PawApX4P6qj57scBQ6KL4ULKvSndCZ7F1tdQjEqogYp\nn/L8oBhP1dgyDonsewG/PucxC+3Aqzcl3w5Pg7qE+zad42khP7t9TjFAMxc+PxC+\n2as5ZCDJBwfxksCyCQfFWx7eB7p39vGy63k3Izv+UKTZEhbSdT721kb91oXkecpE\nEFhihfVYMGMEtC7u2JLSxdkRhWYSMqVCvI2syYsLAUUNH2kgqQF00j2Iddhg3oMA\nIiqhpC/n2slHVhp56WE1TsrGC/JgqIvQK9AT4LRxFb8CAwEAAaOCAQkwggEFMAkG\nA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVu\nU1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFJK58uJe\nLv8WDOR8TTLUcSxO1zCyMGwGA1UdIwRlMGOAFOPK44Eacedv7HbR2Igcbew+4kUa\noUekRTBDMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UECgwH\nUEhQLm5ldDEQMA4GA1UEAwwHcGhwLm5ldIICEAAwDgYDVR0PAQH/BAQDAgWgMBMG\nA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQDHayU795hFnQNj\nfuYV21sEmkSjgtp6X4rV9JfoCp8U2gieL4mizoCPx9Q8HYaAf98hPYTofGUaCSd+\nXlzd8lhLt9GCU+BsCxGtJocrjOQDhyhkioN8iirQRTtJpRVC6a4RCBGpenOD7Z7C\nZ32uGXohzFuigP/J8/SXBdHrlpNSa03iEkkisPR46mbGj06lFVOFHJDNKA3CnQwM\n3VfqvZt0ksZi/gAOol8dtrAd3k5JBfJ2BtqsPbycEGAhsGrTK/MeqiFcO8QZiXpN\nqf0wZzOMFZJ4HqTiHs4TMMQaDK8c4qfR0l1OE224ijcB5NlqMjGAZabMfHgNZcl7\nfYPX+POsOMaqubv98uoC0PCK8NCTyfB4V/el72EEoVfwKYOxm3eYXBEE2ZNyIci6\nYKYhNa9e2k+cFueYMztV06HTGgkcHcSJATP+3ISTRx3tj1BPmc4O4WAMEMx1E0gg\nPaVrBVAylGzt8kaMaOQMBImpsALSfutq6+KtQiu0OTGS3yVUGamV4AyRjuwF1/ZA\nkZbjmNEGsf9d2rzS89ckds24QUx0zrfJ56p7lRvyVK/pML7iadv8dUH3fKDDDerD\noocLjOjNxFGN9Woz+kfidA8Siu0zsfQ6CLPlaQrVAVQH4iMuCZraqMc3TqAiUKRj\niHKp6FFrRZmY8W8HEKHv98uGofn+3g==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/include/ssl_certs/sni_server_uk_key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEArMpFePSNUHNHCiUWfsSPQxHaQTpDfogEISIMuGq5JhEuHWyl\nHRe51JWZGi39a/PY9rAClfg/qqPnuxwFDoovhQsq9Kd0JnsXW11CMSqiBimf8vyg\nGE/V2DIOiex7Ab8+5zEL7cCrNyXfDk+DuoT7Np3jaSE/u31OMUAzFz4/EL7Zqzlk\nIMkHB/GSwLIJB8VbHt4Hunf28bLreTcjO/5QpNkSFtJ1PvbWRv3WheR5ykQQWGKF\n9VgwYwS0Lu7YktLF2RGFZhIypUK8jazJiwsBRQ0faSCpAXTSPYh12GDegwAiKqGk\nL+fayUdWGnnpYTVOysYL8mCoi9Ar0BPgtHEVvwIDAQABAoIBAGpg7XyWMxpAno/k\nXYEWSo0kRJa75CnWW5R6fhJbwjlpglajONQ8czAqGeRARDFeI0lc+3qRJ8FKv7Q2\nf/Z/pNnoEj4liiDWz350X/gdIztgDRVv6rCNFj7QMps/eEuPDo8PZySs5bxCGJ2a\n3qLKlE7/za4/xhkMAEPaHppEvFb15dpomqP2Fjqei9afp8tD+xJv9BGtkFIy9acw\n94AWQi974kgW7ZzfEf8RqHM0ExKiESCc26knbcBu0tQ892YHggMccUMk/UrVgR5d\nvuGOBurlYGLBstSDSKCCe6la4N/FX3FiC3WhVNescnuTZPXcX3zebh7roGjgrTdd\n6UJYBIECgYEA09XYihdwW66G9Y09OatzlzBh1Rvzjv2Vl5NmWHHb6vh38H4PcOof\nS1pT0JvRzpt8uEs4i+Eda5PyQyp4iGtD+DQ07Dxh2jiixsE3PRFRG2wg2QCgDHnS\n6vgV1VOqA/A4on4nhxLZXxhX4YvHaJYlm1bZ+amG+j5XiI/NtHJm9k8CgYEA0NB9\nbAoaZnc06+T8rMaO45boX5SvYsCZbtY3VRl75SrbMal0IKfnXHELVHuei12LmlMs\nLuFgG8jGO3+ncxUqMytoGvrmk/cM9tCNbDKVHtFbqz0qt9lkDAwLsBougKnVsNDW\nAae/tz/wYXJct86unfLh0xtvkx5Iz1we4cS1fZECgYA8YR+vfK7R8xUUuIVTAsOX\nvHqmO6lYgH87DRCG9S0x9FB7g/LyxEjXOY/aTg+nTDpobUhY1nmESE8tRdXFTI27\nGWZcT3m0sZ/z9u6/wUfVAST7tWnpJHAx+TR/8bDsHnSGHF836O98Y3vpFeZosSNW\n+5J1zxRiD5LzocmIPXOLkQKBgEgHApxXx898YwbTj8zRGMysay89DFpV8RboUWHL\nTo83/y/cMbBp+kZKwAu+MGwGMndjJSRunUY4NRik6c+qh0nrORfFX+++Efy4529g\n60scEDC7Apc0J2x4Yze1cED1VD6PaqJbiKffKD2UwyKr6lOVSgwVtKDcm2Tbc9OQ\nlMHhAoGBAIPwYVxzWM6I6pr8x2TucpBZZReLytz7uzybMNvbKCrwlETbNNXubnfp\nnPuPKzpeRI0y26pIIAbijzFW0MXq+kIu3H7we8TaImsJ1AaQCTYeoLWBVcr6RISk\n3d4i7iT35aWCuhPVve0FNIv/u3jrqX2H2C2MXMiLOsw1GFxPvpi4\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/include/ssl_certs/sni_server_us.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA1QkC3tiNYDY+ZxMmPbagYUbMpzuXo9mVBvYh86bYZaeB7bts\nQCBK+6VD4D2LjR3RszpzmOzhJXjm8j0t+GeRS3OMIM75/BKAnixXicRSIb8zdIPz\nJP992vvMq8p46/XftAfBhAMOaCqcD85zpyX4PhfC9733nOyN4yqx58O4UhVTKih6\nW9/ldp3uwSYAW+HyoINnHls/bFO8vv60K5VIhkxK30LHnC5PvByGfuHOgrscEThs\njW0ESqO0+9l88KhGdmLgzvbBlGxNziCMfn0LcFH6p2ITc3foD4LSzGEFtJ92OZi3\nbuCOfbFsN7vWHTsEi89fRcCnWGtMwLUx0TCluQIDAQABAoIBAQCnsUQ1Lrl6trhA\nYu6DPbLZX+XQ7jPbonaQ2Ea5iOhmfIjmHdaEU+cyV1EqvseO+Z4MO0KraiuAV79T\nh50cIEpa3kW7vbFCHz5nQ/hUVdlg/yT93rASu5rSOctOnz64Xv8Ms948kDtS+9eF\nCbo4JMdX+VRbt4mmWP8HhqAsFACPexEoWxJcIxwFcI24GTGzySjemNjQzbmcVhzM\na4k6n8DolCL1cRS54C5Aaf5g4+IFDgyydcgZXp1lnX3MnqivSNkejnPnY55NcmrH\nX3ZWPlAi9GHOJE33uy8bGWnip7Tn4iTt6tJvjz/yP82TGACDg1B8XsKrqsuQLsoU\ncNBVGcQBAoGBAPteCgNmuNOpo4SRA1UVRw1WgnE8YtnNA6vYyVcTLSpqabq33UaD\n03L9CQsbHtj88U+E8OH24Iqj3U9x7QJfH8DVmWuBrlwez80JsKGnLdViHydjKcAz\nH2Cbv+SiWeaWXkFCkN4Jf7k3q0Ew4SG2LOq5PVUy/NB4bilbJD2ExKpZAoGBANj2\nHpwo35IQ4XfSSsGaCdn+8ajMcNUMMGZ6YkZqmVO4kogqobyrPL/2KE9ol/hlacw6\nU/6Digox5/wqruYfqyM8lqGOq2/0Xf7c4XfiOTS9Na4JN3OGzlyqPvcn2zdqhYFY\niHPu2RqpA+LhCHW9Zs8C1Bp/KAEPdRP6OabqVaphAoGACLrHVj7nBFLL3vq6RuYq\nRYhPl2cld7LrAbjRpTiBRQvVCCsCgERrv36SJdSXSanfJ4fSZcaRHb97HBs0w/RR\nwfypC1bBm2lmhhRkEfkgWlzCADgtZwNff5dpHqOUw7FNLK8HIO7rhJ8uT2FHMEiH\nXs94FdFjfknwaXdE1u4ZdmECgYEAgxfbkQHFbO2UPqErGGXp0/WOsS6ucpyF1jXW\nkbOxZ3vb1jjkNyrEbzzeSHTrdmRYk9UekWeLjfNvt9dWjKfP8V+XqJCbF+9wqCFw\nfs6LQEmfWMQq5DwtDqKznwVPGOHdPzVuZZaJSemb9oeAZBwINccAv+3bDyD23hZQ\npYFsN6ECgYEA33QYDNG/spki4D8rlxyxZ+1MdB/efnrGBhO8FsJpG5+AtmYhWgD9\nsl29+3aiRkmDznoy36z+hoeZePILEAKMcbHyXOymixOHPuaZJ95hbvq6sqd6WMAe\nw5tHnxlfEuu11zatolk6WiAmTmG3sZpN5Tqloq0Ye4dvlhVKNV3Bn3E=\n-----END RSA PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\nMIIFIjCCAwqgAwIBAgICEAQwDQYJKoZIhvcNAQELBQAwVTELMAkGA1UEBhMCR0Ix\nEDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQxEDAOBgNVBAsMB29w\nZW5zc2wxEDAOBgNVBAMMB3BocC5uZXQwHhcNMTgwMTE0MTgzNjQ0WhcNMjYwNDAy\nMTgzNjQ0WjBGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UE\nCgwHUEhQLm5ldDETMBEGA1UEAwwKdXMucGhwLm5ldDCCASIwDQYJKoZIhvcNAQEB\nBQADggEPADCCAQoCggEBANUJAt7YjWA2PmcTJj22oGFGzKc7l6PZlQb2IfOm2GWn\nge27bEAgSvulQ+A9i40d0bM6c5js4SV45vI9LfhnkUtzjCDO+fwSgJ4sV4nEUiG/\nM3SD8yT/fdr7zKvKeOv137QHwYQDDmgqnA/Oc6cl+D4Xwve995zsjeMqsefDuFIV\nUyooelvf5Xad7sEmAFvh8qCDZx5bP2xTvL7+tCuVSIZMSt9Cx5wuT7wchn7hzoK7\nHBE4bI1tBEqjtPvZfPCoRnZi4M72wZRsTc4gjH59C3BR+qdiE3N36A+C0sxhBbSf\ndjmYt27gjn2xbDe71h07BIvPX0XAp1hrTMC1MdEwpbkCAwEAAaOCAQkwggEFMAkG\nA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVu\nU1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFOXnUeNs\ngrKQb+EvG36DXjjDDmsFMGwGA1UdIwRlMGOAFOPK44Eacedv7HbR2Igcbew+4kUa\noUekRTBDMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UECgwH\nUEhQLm5ldDEQMA4GA1UEAwwHcGhwLm5ldIICEAAwDgYDVR0PAQH/BAQDAgWgMBMG\nA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQBDLwuLbx75ACSU\n5cF2L/D17JEnhlna62MgKNdpNLJSpaofK2Lk2BqsmnQf5JdkrXWMUN/DsmXZc9pq\n25XmprfABUP9Cx18KPVqLQ43Z9o+R9xI1Ospt5mrpxGp6l2BHSs/4G69nuPFpcIJ\niabnLYdUk2Z+64lPe4EMBrZH+pj4xn3JA59BACJYNYn0nLaw45DIAyzyLJ0vVSwc\n0JtjsztXQov4UqdWXxLRFfe2nEGoK8ZkTJ8ELcCYu6sNSBjw9Ech78uXN1BQOBTK\nlhAgN3FKqOp3hqf0umqf35gDvmWwLB/eptUYZ96gBYT0tbPA0P+YsW+iZmamxXma\nOdgg8iRcPxKl9bVPt57NLaDy/RQhOxTGXQs1Q2jp7UhzqoZDClwVSDxd6DEppAFA\nOZAY+Rsrm7VoCwVQ/1KbcJHmJ/79tArvaWJk3KHLGMpdZq4KwrC3hM8/QxYtyX/6\ncfnXvShBYCdfTGgNlj3t/mNAgp1ZB3s9ClGqRBR/P0Db+ryv5DuxYM6nzEB3Od0y\nkT5tHbXDQY+1HCExjOMi7Al0cmC2r3+oxDA4UjGv+npgcfeoxQhXmm/SQRiPdKlb\nvT0D594sLoB23jqA9bMehpxEyI7eGjfFUmXwMeu0tJhipvpJI3ogJoM+SCFTyLkc\n12cPiz/sR/ALhvhUJXTeUH8wxPjzbA==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/include/ssl_certs/sni_server_us_cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFIjCCAwqgAwIBAgICEAQwDQYJKoZIhvcNAQELBQAwVTELMAkGA1UEBhMCR0Ix\nEDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQxEDAOBgNVBAsMB29w\nZW5zc2wxEDAOBgNVBAMMB3BocC5uZXQwHhcNMTgwMTE0MTgzNjQ0WhcNMjYwNDAy\nMTgzNjQ0WjBGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UE\nCgwHUEhQLm5ldDETMBEGA1UEAwwKdXMucGhwLm5ldDCCASIwDQYJKoZIhvcNAQEB\nBQADggEPADCCAQoCggEBANUJAt7YjWA2PmcTJj22oGFGzKc7l6PZlQb2IfOm2GWn\nge27bEAgSvulQ+A9i40d0bM6c5js4SV45vI9LfhnkUtzjCDO+fwSgJ4sV4nEUiG/\nM3SD8yT/fdr7zKvKeOv137QHwYQDDmgqnA/Oc6cl+D4Xwve995zsjeMqsefDuFIV\nUyooelvf5Xad7sEmAFvh8qCDZx5bP2xTvL7+tCuVSIZMSt9Cx5wuT7wchn7hzoK7\nHBE4bI1tBEqjtPvZfPCoRnZi4M72wZRsTc4gjH59C3BR+qdiE3N36A+C0sxhBbSf\ndjmYt27gjn2xbDe71h07BIvPX0XAp1hrTMC1MdEwpbkCAwEAAaOCAQkwggEFMAkG\nA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVu\nU1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFOXnUeNs\ngrKQb+EvG36DXjjDDmsFMGwGA1UdIwRlMGOAFOPK44Eacedv7HbR2Igcbew+4kUa\noUekRTBDMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UECgwH\nUEhQLm5ldDEQMA4GA1UEAwwHcGhwLm5ldIICEAAwDgYDVR0PAQH/BAQDAgWgMBMG\nA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQBDLwuLbx75ACSU\n5cF2L/D17JEnhlna62MgKNdpNLJSpaofK2Lk2BqsmnQf5JdkrXWMUN/DsmXZc9pq\n25XmprfABUP9Cx18KPVqLQ43Z9o+R9xI1Ospt5mrpxGp6l2BHSs/4G69nuPFpcIJ\niabnLYdUk2Z+64lPe4EMBrZH+pj4xn3JA59BACJYNYn0nLaw45DIAyzyLJ0vVSwc\n0JtjsztXQov4UqdWXxLRFfe2nEGoK8ZkTJ8ELcCYu6sNSBjw9Ech78uXN1BQOBTK\nlhAgN3FKqOp3hqf0umqf35gDvmWwLB/eptUYZ96gBYT0tbPA0P+YsW+iZmamxXma\nOdgg8iRcPxKl9bVPt57NLaDy/RQhOxTGXQs1Q2jp7UhzqoZDClwVSDxd6DEppAFA\nOZAY+Rsrm7VoCwVQ/1KbcJHmJ/79tArvaWJk3KHLGMpdZq4KwrC3hM8/QxYtyX/6\ncfnXvShBYCdfTGgNlj3t/mNAgp1ZB3s9ClGqRBR/P0Db+ryv5DuxYM6nzEB3Od0y\nkT5tHbXDQY+1HCExjOMi7Al0cmC2r3+oxDA4UjGv+npgcfeoxQhXmm/SQRiPdKlb\nvT0D594sLoB23jqA9bMehpxEyI7eGjfFUmXwMeu0tJhipvpJI3ogJoM+SCFTyLkc\n12cPiz/sR/ALhvhUJXTeUH8wxPjzbA==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/include/ssl_certs/sni_server_us_key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA1QkC3tiNYDY+ZxMmPbagYUbMpzuXo9mVBvYh86bYZaeB7bts\nQCBK+6VD4D2LjR3RszpzmOzhJXjm8j0t+GeRS3OMIM75/BKAnixXicRSIb8zdIPz\nJP992vvMq8p46/XftAfBhAMOaCqcD85zpyX4PhfC9733nOyN4yqx58O4UhVTKih6\nW9/ldp3uwSYAW+HyoINnHls/bFO8vv60K5VIhkxK30LHnC5PvByGfuHOgrscEThs\njW0ESqO0+9l88KhGdmLgzvbBlGxNziCMfn0LcFH6p2ITc3foD4LSzGEFtJ92OZi3\nbuCOfbFsN7vWHTsEi89fRcCnWGtMwLUx0TCluQIDAQABAoIBAQCnsUQ1Lrl6trhA\nYu6DPbLZX+XQ7jPbonaQ2Ea5iOhmfIjmHdaEU+cyV1EqvseO+Z4MO0KraiuAV79T\nh50cIEpa3kW7vbFCHz5nQ/hUVdlg/yT93rASu5rSOctOnz64Xv8Ms948kDtS+9eF\nCbo4JMdX+VRbt4mmWP8HhqAsFACPexEoWxJcIxwFcI24GTGzySjemNjQzbmcVhzM\na4k6n8DolCL1cRS54C5Aaf5g4+IFDgyydcgZXp1lnX3MnqivSNkejnPnY55NcmrH\nX3ZWPlAi9GHOJE33uy8bGWnip7Tn4iTt6tJvjz/yP82TGACDg1B8XsKrqsuQLsoU\ncNBVGcQBAoGBAPteCgNmuNOpo4SRA1UVRw1WgnE8YtnNA6vYyVcTLSpqabq33UaD\n03L9CQsbHtj88U+E8OH24Iqj3U9x7QJfH8DVmWuBrlwez80JsKGnLdViHydjKcAz\nH2Cbv+SiWeaWXkFCkN4Jf7k3q0Ew4SG2LOq5PVUy/NB4bilbJD2ExKpZAoGBANj2\nHpwo35IQ4XfSSsGaCdn+8ajMcNUMMGZ6YkZqmVO4kogqobyrPL/2KE9ol/hlacw6\nU/6Digox5/wqruYfqyM8lqGOq2/0Xf7c4XfiOTS9Na4JN3OGzlyqPvcn2zdqhYFY\niHPu2RqpA+LhCHW9Zs8C1Bp/KAEPdRP6OabqVaphAoGACLrHVj7nBFLL3vq6RuYq\nRYhPl2cld7LrAbjRpTiBRQvVCCsCgERrv36SJdSXSanfJ4fSZcaRHb97HBs0w/RR\nwfypC1bBm2lmhhRkEfkgWlzCADgtZwNff5dpHqOUw7FNLK8HIO7rhJ8uT2FHMEiH\nXs94FdFjfknwaXdE1u4ZdmECgYEAgxfbkQHFbO2UPqErGGXp0/WOsS6ucpyF1jXW\nkbOxZ3vb1jjkNyrEbzzeSHTrdmRYk9UekWeLjfNvt9dWjKfP8V+XqJCbF+9wqCFw\nfs6LQEmfWMQq5DwtDqKznwVPGOHdPzVuZZaJSemb9oeAZBwINccAv+3bDyD23hZQ\npYFsN6ECgYEA33QYDNG/spki4D8rlxyxZ+1MdB/efnrGBhO8FsJpG5+AtmYhWgD9\nsl29+3aiRkmDznoy36z+hoeZePILEAKMcbHyXOymixOHPuaZJ95hbvq6sqd6WMAe\nw5tHnxlfEuu11zatolk6WiAmTmG3sZpN5Tqloq0Ye4dvlhVKNV3Bn3E=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/init",
    "content": "#!/usr/bin/env php\n<?php\n\nfunction read_sql_file(string $file)\n{\n    $comment_regex = '#(?<!:)//.*|/\\*(\\s|.)*?\\*/|--[^\\n]+#';\n    $lines = explode(\"\\n\", preg_replace($comment_regex, '', co::readFile($file)));\n    $init_sql = [];\n    $multi = false;\n    foreach ($lines as $index => $line) {\n        if (strlen($line) === 0) {\n            continue;\n        }\n        if (substr($line, -1, 1) !== ';') {\n            if (!$multi) {\n                $multi = true;\n                goto _new_line;\n            } else {\n                _append:\n                $end_line = &$init_sql[count($init_sql) - 1];\n                $end_line = $end_line . $line . \"\\n\";\n            }\n        } else {\n            if ($multi) {\n                $multi = false;\n                goto _append;\n            } else {\n                $multi = false;\n                _new_line:\n                $init_sql[] = \"{$line}\";\n            }\n        }\n    }\n\n    return $init_sql;\n}\n\n// 解析命令行参数\n$options = getopt('', ['mysql', 'firebird', 'odbc-mysql', 'help']);\n\n// 显示帮助信息\nif (isset($options['help'])) {\n    echo \"Usage: php init [options]\\n\";\n    echo \"Options:\\n\";\n    echo \"  --mysql      init MySQL database\\n\";\n    echo \"  --firebird   init Firebird database\\n\";\n    echo \"  --odbc-mysql init ODBC MySQL database\\n\";\n    echo \"  --help       Show help message\\n\";\n    echo \"default: init all databases\\n\";\n    exit(0);\n}\n\nrequire __DIR__ . '/include/config.php';\n\n$has_specific_option = isset($options['mysql']) || isset($options['firebird']) || isset($options['odbc-mysql']);\n$load_mysql = !$has_specific_option || isset($options['mysql']);\n$load_firebird = !$has_specific_option || isset($options['firebird']);\n$load_odbc_mysql = !$has_specific_option || isset($options['odbc-mysql']);\n\nrequire __DIR__ . '/swoole_ssh2/ssh2_test.inc';\nif ($load_firebird) {\n    require __DIR__ . '/swoole_pdo_firebird/pdo_firebird.inc';\n}\n\nswoole_library_set_option('default_remote_object_server_worker_num', 8);\nswoole_init_default_remote_object_server();\n\nSwoole\\Coroutine\\run(function () use ($load_mysql, $load_firebird, $load_odbc_mysql) {\n    echo \"[SSH2-init] Setting up SSH2 test environment...\\n\";\n\n    $ssh_user = TEST_SSH2_USER ;\n    $ssh_password = TEST_SSH2_PASS;\n\n    $user_exists = trim(shell_exec(\"id -u $ssh_user 2>/dev/null\"));\n    if (empty($user_exists)) {\n        echo \"[SSH2-init] Creating test user: $ssh_user\\n\";\n        exec(\"useradd -m $ssh_user\");\n        exec(\"echo '$ssh_user:$ssh_password' | chpasswd\");\n\n        exec(\"mkdir -p /home/$ssh_user/.ssh\");\n\n        echo \"[SSH2-init] Copying SSH test key pair...\\n\";\n\n        exec(\"cat \" . __DIR__ . '/swoole_ssh2/testkey_ed25519.pub' . \" > /home/$ssh_user/.ssh/authorized_keys\");\n        readfile(\"/home/$ssh_user/.ssh/authorized_keys\");\n        readfile('/etc/ssh/sshd_config');\n\n        exec(\"chmod 700 /home/$ssh_user/.ssh\");\n        exec(\"chown -R $ssh_user:$ssh_user /home/$ssh_user/.ssh\");\n        exec(\"ssh -vvv -i \" . __DIR__ . '/swoole_ssh2/testkey_ed25519' . \" $ssh_user@localhost\");\n\n        readfile('/var/log/auth.log');\n\n        echo \"[SSH2-init] SSH2 test user setup completed\\n\";\n    } else {\n        echo \"[SSH2-init] User $ssh_user already exists, skipping creation\\n\";\n    }\n\n    // 初始化MySQL数据库\n    if ($load_mysql) {\n        echo \"[DB-init] initialization MySQL database...\\n\";\n        $mysql = new mysqli();\n        $connected = $mysql->connect(MYSQL_SERVER_HOST,\n                MYSQL_SERVER_USER,\n                MYSQL_SERVER_PWD,\n                MYSQL_SERVER_DB,\n                MYSQL_SERVER_PORT);\n        if (!$connected) {\n            echo \"[DB-init] Connect failed! Error#{$mysql->connect_errno}: {$mysql->connect_error}\\n\";\n            exit(1);\n        }\n        $sql_file = read_sql_file(__DIR__ . '/test.sql');\n        foreach ($sql_file as $line) {\n            if (!$mysql->query($line)) {\n                echo \"[DB-init] Failed! Error#{$mysql->errno}: {$mysql->error}\\n\";\n                exit(1);\n            }\n        }\n        echo \"[DB-init] MySQL Done!\\n\";\n    }\n\n    // 初始化ODBC MySQL配置 - 独立控制\n    if ($load_odbc_mysql) {\n        echo \"[DB-init] initialization ODBC...\\n\";\n        echo `set -ex`;\n\n        file_put_contents('/etc/odbcinst.ini', \"[mysql]\" . PHP_EOL\n                . \"Driver=libmaodbc.so\" . PHP_EOL\n                . \"Description=MariaDB Connector/ODBC(Unicode)\" . PHP_EOL\n                . \"UsageCount=1\" . PHP_EOL\n        );\n        echo `odbcinst -q -d -n \"mysql\"`;\n\n        file_put_contents('/etc/odbc.ini', \"[mysql-test]\" . PHP_EOL\n                . \"Description = MySQL test database\" . PHP_EOL\n                . \"Trace = On\" . PHP_EOL\n                . \"TraceFile = stderr\" . PHP_EOL\n                . \"Driver = mysql\" . PHP_EOL\n                . \"SERVER = \" . MYSQL_SERVER_HOST . PHP_EOL\n                . \"USER = \" . MYSQL_SERVER_USER . PHP_EOL\n                . \"PASSWORD =\" . MYSQL_SERVER_PWD . PHP_EOL\n                . \"PORT = \" . MYSQL_SERVER_PORT . PHP_EOL\n                . \"DATABASE = \" . MYSQL_SERVER_DB);\n        echo `odbcinst -i -d -f /etc/odbc.ini`;\n\n        echo \"[DB-init] ODBC Done!\\n\";\n    }\n\n    // 初始化Firebird数据库\n    if ($load_firebird) {\n        echo \"[DB-init] initialization Firebird database...\\n\";\n        try {\n            $firebird = PdoFirebirdTest::create();\n            // 创建一些基础表结构 - 使用Firebird兼容的方式\n            try {\n                // 尝试查询表是否存在 - 使用单引号避免PHP解析$符号\n                $result = $firebird->query('SELECT 1 FROM rdb$relations WHERE rdb$relation_name = \"SWOOLE_TEST\"');\n                if (!$result || $result->fetchColumn() === false) {\n                    // 表不存在，创建表\n                    $firebird->exec('CREATE TABLE swoole_test (id INTEGER PRIMARY KEY, data VARCHAR(255))');\n                }\n            } catch (PDOException $e) {\n                // 如果查询失败，尝试直接创建表（可能是因为权限问题）\n                try {\n                    $firebird->exec('CREATE TABLE swoole_test (id INTEGER PRIMARY KEY, data VARCHAR(255))');\n                } catch (PDOException $e2) {\n                    // 如果表已存在，忽略此错误\n                    if (!strpos($e2->getMessage(), 'already exists')) {\n                        throw $e2;\n                    }\n                }\n            }\n            echo \"[DB-init] Firebird Done!\\n\";\n        } catch (PDOException $e) {\n            echo \"[DB-init] Firebird initialization failed: \" . $e->getMessage() . \"\\n\";\n        }\n    }\n});\n"
  },
  {
    "path": "tests/new",
    "content": "#!/usr/bin/env php\n<?php\ndefine('SWOOLE_COLOR_RED', 1);\ndefine('SWOOLE_COLOR_GREEN', 2);\ndefine('SWOOLE_COLOR_YELLOW', 3);\ndefine('SWOOLE_COLOR_BLUE', 4);\ndefine('SWOOLE_COLOR_MAGENTA', 5);\ndefine('SWOOLE_COLOR_CYAN', 6);\ndefine('SWOOLE_COLOR_WHITE', 7);\n\nfunction swoole_color(string $content, int $color): string\n{\n    return \"\\033[3{$color}m{$content}\\033[0m\";\n}\n\nfunction fgetsin(string $tip = '', bool $accept_empty = true): string\n{\n    do {\n        if ($tip) {\n            echo \"$tip: \";\n        }\n        $in = trim(fgets(STDIN));\n    } while (!$accept_empty && $in === '');\n\n    return $in;\n}\n\nfunction yes(string $content = ''): bool\n{\n    echo $content;\n    return fgetsin() === 'y';\n}\n\nif (empty($argv[1])) {\n    error_filename:\n    exit(\n        swoole_color(\"Please enter the correct file name or path! e.g.:\", SWOOLE_COLOR_RED) .\n        swoole_color(\"\\n\\\"./new ./swoole_coroutine\\\"\\n\", SWOOLE_COLOR_MAGENTA)\n    );\n}\n\n$filename = $argv[1];\nif (!pathinfo($filename, PATHINFO_EXTENSION)) {\n    $path = ['dirname' => $filename];\n} else {\n    $path = pathinfo($filename);\n}\n$path['dirname'] = trim($path['dirname'], './'); // i know arg2 is a list but it's no problem\n\n$replacement = [];\n$tip = swoole_color(\"[Test name]\", SWOOLE_COLOR_BLUE);\n$path['filename'] = fgetsin($tip, false);// use test name to be filename\n$tip = swoole_color(\"[Test intro]\", SWOOLE_COLOR_BLUE);\n$replacement['test_intro'] = fgetsin($tip, false);\n$this_dir_name = explode('/', $path['dirname']);\n$replacement['test_name'] = end($this_dir_name); // use dir name to be test name\n\n$filename = \"{$path['dirname']}/{$path['filename']}.phpt\";\n\n//if dir not exist, create it\nif (!is_dir(__DIR__ . \"/{$path['dirname']}\")) {\n    echo swoole_color(\n        \"The dir [{$path['dirname']}] is not exist, if you want to create it? [y/n]: \",\n        SWOOLE_COLOR_YELLOW\n    );\n    if (yes()) {\n        mkdir($path['dirname'], 0755, true);\n    } else {\n        exit(swoole_color('Can\\'t generate the test file in nonexistent dir!', SWOOLE_COLOR_RED));\n    }\n} elseif (file_exists($filename)) {\n    echo swoole_color(\n        \"The file [{$path['filename']}] is exist, if you want to overwrite it? [y/n]: \",\n        SWOOLE_COLOR_YELLOW\n    );\n    if (!yes()) {\n        exit(swoole_color('You should rename your test filename.', SWOOLE_COLOR_RED));\n    }\n}\n\n//calc dir deep\n$deep = 0;\n$temp = $filename;\nwhile (($temp = dirname($temp)) !== '.') {\n    $deep++;\n}\nif ($deep < 1) {\n    goto error_filename;\n}\n\n$template = file_get_contents(__DIR__ . '/template');\n$replacement['dir_deep'] = str_repeat('/..', $deep);\nforeach ($replacement as $key => $value) {\n    $template = str_replace(\"{{{$key}}}\", $value, $template);\n}\n\nif (file_put_contents($filename, $template)) {\n    echo swoole_color(\"Generate the test file successfully!\\n\", SWOOLE_COLOR_GREEN) .\n        \"[\" . __DIR__ . \"/$filename]\";\n    @shell_exec('/usr/bin/env git add ' . __DIR__ . \"/$filename\");\n    if (\\stripos(PHP_OS, 'Darwin') !== false) {\n        //MacOS\n        $pstorm = '/usr/local/bin/phpstorm';\n        if (file_exists($pstorm) || (\n                file_exists('/Applications/PhpStorm.app') &&\n                file_put_contents($pstorm, file_get_contents(__DIR__ . '/include/macos/phpstorm.py')) &&\n                chmod($pstorm, 0744)\n            )\n        ) {\n            @shell_exec(\"{$pstorm} {$filename}\");\n        }\n    }\n} else {\n    exit(\"\\nGenerate the test file failed!\");\n}\n"
  },
  {
    "path": "tests/pgsql.sql",
    "content": "DROP TABLE IF EXISTS weather;\nCREATE TABLE weather (\n        id SERIAL primary key NOT NULL,\n        city character varying(80),\n        temp_lo integer,\n        temp_hi integer,\n        prcp real,\n        date date);\nINSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27') RETURNING id;\nINSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ('Test2', 11, 22, 0.3, '1994-11-28') RETURNING id;\n\nDROP TABLE IF EXISTS oid;\nCREATE TABLE oid (\n        id SERIAL primary key NOT NULL,\n        oid oid);\n"
  },
  {
    "path": "tests/run-tests",
    "content": "#!/usr/bin/env php\n<?php\n/*\n   +----------------------------------------------------------------------+\n   | Copyright (c) The PHP Group                                          |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | https://php.net/license/3_01.txt                                     |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Authors: Ilia Alshanetsky <iliaa@php.net>                            |\n   |          Preston L. Bannister <pbannister@php.net>                   |\n   |          Marcus Boerger <helly@php.net>                              |\n   |          Derick Rethans <derick@php.net>                             |\n   |          Sander Roobol <sander@php.net>                              |\n   |          Andrea Faulds <ajf@ajf.me>                                  |\n   | (based on version by: Stig Bakken <ssb@php.net>)                     |\n   | (based on the PHP 3 test framework by Rasmus Lerdorf)                |\n   +----------------------------------------------------------------------+\n */\n\n/* $Id: ebcaabba02a5af6f5dc62dda027befb95385d5fc $ */\n\n/* Let there be no top-level code beyond this point:\n * Only functions and classes, thanks!\n *\n * Minimum required PHP version: 8.2.0\n */\n\nfunction show_usage(): void\n{\n    echo <<<HELP\nSynopsis:\n    php run-tests.php [options] [files] [directories]\n\nOptions:\n    -j<workers> Run up to <workers> simultaneous testing processes in parallel for\n                quicker testing on systems with multiple logical processors.\n                Note that this is experimental feature.\n\n    -l <file>   Read the testfiles to be executed from <file>. After the test\n                has finished all failed tests are written to the same <file>.\n                If the list is empty and no further test is specified then\n                all tests are executed (same as: -r <file> -w <file>).\n\n    -r <file>   Read the testfiles to be executed from <file>.\n\n    -w <file>   Write a list of all failed tests to <file>.\n\n    -a <file>   Same as -w but append rather then truncating <file>.\n\n    -W <file>   Write a list of all tests and their result status to <file>.\n\n    -c <file>   Look for php.ini in directory <file> or use <file> as ini.\n\n    -n          Pass -n option to the php binary (Do not use a php.ini).\n\n    -d foo=bar  Pass -d option to the php binary (Define INI entry foo\n                with value 'bar').\n\n    -g          Comma separated list of groups to show during test run\n                (possible values: PASS, FAIL, XFAIL, XLEAK, SKIP, BORK, WARN, LEAK, REDIRECT).\n\n    -m          Test for memory leaks with Valgrind (equivalent to -M memcheck).\n\n    -M <tool>   Test for errors with Valgrind tool.\n\n    -p <php>    Specify PHP executable to run.\n\n    -P          Use PHP_BINARY as PHP executable to run (default).\n\n    -q          Quiet, no user interaction (same as environment NO_INTERACTION).\n\n    -s <file>   Write output to <file>.\n\n    -x          Sets 'SKIP_SLOW_TESTS' environmental variable.\n\n    --offline   Sets 'SKIP_ONLINE_TESTS' environmental variable.\n\n    --verbose\n    -v          Verbose mode.\n\n    --help\n    -h          This Help.\n\n    --temp-source <sdir>  --temp-target <tdir> [--temp-urlbase <url>]\n                Write temporary files to <tdir> by replacing <sdir> from the\n                filenames to generate with <tdir>. In general, you want to make\n                <sdir> the path to your source files and <tdir> some patch in\n                your web page hierarchy with <url> pointing to <tdir>.\n\n    --keep-[all|php|skip|clean]\n                Do not delete 'all' files, 'php' test file, 'skip' or 'clean'\n                file.\n\n    --set-timeout [n]\n                Set timeout for individual tests, where [n] is the number of\n                seconds. The default value is 60 seconds, or 300 seconds when\n                testing for memory leaks.\n\n    --context [n]\n                Sets the number of lines of surrounding context to print for diffs.\n                The default value is 3.\n\n    --show-[all|php|skip|clean|exp|diff|out|mem]\n                Show 'all' files, 'php' test file, 'skip' or 'clean' file. You\n                can also use this to show the output 'out', the expected result\n                'exp', the difference between them 'diff' or the valgrind log\n                'mem'. The result types get written independent of the log format,\n                however 'diff' only exists when a test fails.\n\n    --show-slow [n]\n                Show all tests that took longer than [n] milliseconds to run.\n\n    --no-clean  Do not execute clean section if any.\n\n    --color\n    --no-color  Do/Don't colorize the result type in the test result.\n\n    --repeat [n]\n                Run the tests multiple times in the same process and check the\n                output of the last execution (CLI SAPI only).\n\n    --bless     Bless failed tests using scripts/dev/bless_tests.php.\n\nHELP;\n}\n\n/**\n * One function to rule them all, one function to find them, one function to\n * bring them all and in the darkness bind them.\n * This is the entry point and exit point überfunction. It contains all the\n * code that was previously found at the top level. It could and should be\n * refactored to be smaller and more manageable.\n */\nfunction main()\n{\n    /* This list was derived in a naïve mechanical fashion. If a member\n     * looks like it doesn't belong, it probably doesn't; cull at will.\n     */\n    global $DETAILED, $PHP_FAILED_TESTS, $SHOW_ONLY_GROUPS, $argc, $argv, $cfg,\n           $cfgfiles, $cfgtypes, $conf_passed, $end_time, $environment,\n           $exts_skipped, $exts_tested, $exts_to_test, $failed_tests_file,\n           $html_file, $html_output, $ignored_by_ext, $ini_overwrites, $is_switch,\n           $just_save_results, $log_format, $matches, $no_clean, $no_file_cache,\n           $optionals, $output_file, $pass_option_n, $pass_options,\n           $pattern_match, $php, $php_cgi, $phpdbg, $preload, $redir_tests,\n           $repeat, $result_tests_file, $slow_min_ms, $start_time, $switch,\n           $temp_source, $temp_target, $temp_urlbase, $test_cnt, $test_dirs,\n           $test_files, $test_idx, $test_list, $test_results, $testfile,\n           $user_tests, $valgrind, $sum_results, $shuffle;\n    // Parallel testing\n    global $workers, $workerID;\n\n    define('IS_WINDOWS', str_starts_with(PHP_OS, \"WIN\"));\n\n    $workerID = 0;\n    if (getenv(\"TEST_PHP_WORKER\")) {\n        $workerID = intval(getenv(\"TEST_PHP_WORKER\"));\n        run_worker();\n        return;\n    }\n\n    define('INIT_DIR', getcwd());\n\n    // change into the PHP source directory.\n    if (getenv('TEST_PHP_SRCDIR')) {\n        @chdir(getenv('TEST_PHP_SRCDIR'));\n    }\n    define('TEST_PHP_SRCDIR', getcwd());\n\n    if (!function_exists('proc_open')) {\n        echo <<<NO_PROC_OPEN_ERROR\n\n+-----------------------------------------------------------+\n|                       ! ERROR !                           |\n| The test-suite requires that proc_open() is available.    |\n| Please check if you disabled it in php.ini.               |\n+-----------------------------------------------------------+\n\nNO_PROC_OPEN_ERROR;\n        exit(200);\n    }\n\n    // If timezone is not set, use UTC.\n    if (ini_get('date.timezone') == '') {\n        date_default_timezone_set('UTC');\n    }\n\n    // Delete some security related environment variables\n    putenv('SSH_CLIENT=deleted');\n    putenv('SSH_AUTH_SOCK=deleted');\n    putenv('SSH_TTY=deleted');\n    putenv('SSH_CONNECTION=deleted');\n\n    set_time_limit(0);\n\n    ini_set('pcre.backtrack_limit', PHP_INT_MAX);\n\n    // delete as much output buffers as possible\n    while (@ob_end_clean()) {\n    }\n    if (ob_get_level()) {\n        echo \"Not all buffers were deleted.\\n\";\n    }\n\n    error_reporting(E_ALL);\n\n    $environment = $_ENV ?? array();\n    // Note: php.ini-development sets variables_order=\"GPCS\" not \"EGPCS\", in which case $_ENV is NOT populated.\n    //\t   detect and handle this case, or die or warn\n    if (empty($environment)) {\n        // not documented, but returns array of all environment variables\n        $environment = getenv();\n    }\n    if (empty($environment['TEMP'])) {\n        $environment['TEMP'] = sys_get_temp_dir();\n\n        if (empty($environment['TEMP'])) {\n            // for example, OpCache on Windows will fail in this case because child processes (for tests) will not get\n            // a TEMP variable, so GetTempPath() will fallback to c:\\windows, while GetTempPath() will return %TEMP% for parent\n            // (likely a different path). The parent will initialize the OpCache in that path, and child will fail to reattach to\n            // the OpCache because it will be using the wrong path.\n            die(\"TEMP environment is NOT set\");\n        } else {\n            if (count($environment) == 1) {\n                // not having other environment variables, only having TEMP, is probably ok, but strange and may make a\n                // difference in the test pass rate, so warn the user.\n                echo \"WARNING: Only 1 environment variable will be available to tests(TEMP environment variable)\" . PHP_EOL;\n            }\n        }\n    }\n    //\n    if (IS_WINDOWS && empty($environment[\"SystemRoot\"])) {\n        $environment[\"SystemRoot\"] = getenv(\"SystemRoot\");\n    }\n\n    // swoole patch (persistent config)\n    {\n        $environment['PHPT'] = $environment['PHPT'] ?? getenv('PHPT');\n        $environment['HAS_NETSTAT'] = getenv('HAS_NETSTAT') ?? (stripos(shell_exec(\"netstat --help 2>&1\"), 'usage') ? '1' : '0');\n        if ($environment['HAS_NETSTAT']) {\n            $environment['MYSQL_SERVER_PATH'] = getenv('MYSQL_SERVER_PATH') ?? (trim(shell_exec(\"netstat -ln | grep -o -m 1 -E '\\S*mysqld?\\.sock'\")) ?: null);\n            $environment['REDIS_SERVER_PATH'] = getenv('REDIS_SERVER_PATH') ?? (trim(shell_exec(\"netstat -ln | grep -o -m 1 -E '\\S*redis.*\\.sock'\")) ?: null);\n        }\n    }\n\n    $php = null;\n    $php_cgi = null;\n    $phpdbg = null;\n\n    if (getenv('TEST_PHP_EXECUTABLE')) {\n        $php = getenv('TEST_PHP_EXECUTABLE');\n\n        if ($php == 'auto') {\n            $php = TEST_PHP_SRCDIR . '/sapi/cli/php';\n            putenv(\"TEST_PHP_EXECUTABLE=$php\");\n\n            if (!getenv('TEST_PHP_CGI_EXECUTABLE')) {\n                $php_cgi = TEST_PHP_SRCDIR . '/sapi/cgi/php-cgi';\n\n                if (file_exists($php_cgi)) {\n                    putenv(\"TEST_PHP_CGI_EXECUTABLE=$php_cgi\");\n                } else {\n                    $php_cgi = null;\n                }\n            }\n        }\n        $environment['TEST_PHP_EXECUTABLE'] = $php;\n    }\n\n    if (getenv('TEST_PHP_CGI_EXECUTABLE')) {\n        $php_cgi = getenv('TEST_PHP_CGI_EXECUTABLE');\n\n        if ($php_cgi == 'auto') {\n            $php_cgi = TEST_PHP_SRCDIR . '/sapi/cgi/php-cgi';\n            putenv(\"TEST_PHP_CGI_EXECUTABLE=$php_cgi\");\n        }\n\n        $environment['TEST_PHP_CGI_EXECUTABLE'] = $php_cgi;\n    }\n\n    if (!getenv('TEST_PHPDBG_EXECUTABLE')) {\n        if (IS_WINDOWS && file_exists(dirname($php) . \"/phpdbg.exe\")) {\n            $phpdbg = realpath(dirname($php) . \"/phpdbg.exe\");\n        } elseif (file_exists(dirname($php) . \"/../../sapi/phpdbg/phpdbg\")) {\n            $phpdbg = realpath(dirname($php) . \"/../../sapi/phpdbg/phpdbg\");\n        } elseif (file_exists(\"./sapi/phpdbg/phpdbg\")) {\n            $phpdbg = realpath(\"./sapi/phpdbg/phpdbg\");\n        } elseif (file_exists(dirname($php) . \"/phpdbg\")) {\n            $phpdbg = realpath(dirname($php) . \"/phpdbg\");\n        } else {\n            $phpdbg = null;\n        }\n        if ($phpdbg) {\n            putenv(\"TEST_PHPDBG_EXECUTABLE=$phpdbg\");\n        }\n    }\n\n    if (getenv('TEST_PHPDBG_EXECUTABLE')) {\n        $phpdbg = getenv('TEST_PHPDBG_EXECUTABLE');\n\n        if ($phpdbg == 'auto') {\n            $phpdbg = TEST_PHP_SRCDIR . '/sapi/phpdbg/phpdbg';\n            putenv(\"TEST_PHPDBG_EXECUTABLE=$phpdbg\");\n        }\n\n        $environment['TEST_PHPDBG_EXECUTABLE'] = $phpdbg;\n    }\n\n    if (getenv('TEST_PHP_LOG_FORMAT')) {\n        $log_format = strtoupper(getenv('TEST_PHP_LOG_FORMAT'));\n    } else {\n        $log_format = 'LEODS';\n    }\n\n    // Check whether a detailed log is wanted.\n    if (getenv('TEST_PHP_DETAILED')) {\n        $DETAILED = getenv('TEST_PHP_DETAILED');\n    } else {\n        $DETAILED = 0;\n    }\n\n    junit_init();\n\n    if (getenv('SHOW_ONLY_GROUPS')) {\n        $SHOW_ONLY_GROUPS = explode(\",\", getenv('SHOW_ONLY_GROUPS'));\n    } else {\n        $SHOW_ONLY_GROUPS = [];\n    }\n\n    // Check whether user test dirs are requested.\n    if (getenv('TEST_PHP_USER')) {\n        $user_tests = explode(',', getenv('TEST_PHP_USER'));\n    } else {\n        $user_tests = [];\n    }\n\n    $exts_to_test = [];\n    $ini_overwrites = [\n        'output_handler=',\n        'open_basedir=',\n        'disable_functions=',\n        'output_buffering=Off',\n        'error_reporting=' . E_ALL,\n        'display_errors=1',\n        'display_startup_errors=1',\n        'log_errors=0',\n        'html_errors=0',\n        'track_errors=0',\n        'report_memleaks=1',\n        'report_zend_debug=0',\n        'docref_root=',\n        'docref_ext=.html',\n        'error_prepend_string=',\n        'error_append_string=',\n        'auto_prepend_file=',\n        'auto_append_file=',\n        'ignore_repeated_errors=0',\n        'precision=14',\n        'serialize_precision=-1',\n        'memory_limit=1024M',\n        'log_errors_max_len=0',\n        'opcache.fast_shutdown=0',\n        'opcache.file_update_protection=0',\n        'opcache.revalidate_freq=0',\n        'opcache.jit_hot_loop=1',\n        'opcache.jit_hot_func=1',\n        'opcache.jit_hot_return=1',\n        'opcache.jit_hot_side_exit=1',\n        'zend.assertions=1',\n        'zend.exception_ignore_args=0',\n        'zend.exception_string_param_max_len=15',\n        'short_open_tag=0',\n    ];\n\n    $no_file_cache = '-d opcache.file_cache= -d opcache.file_cache_only=0';\n\n    define('PHP_QA_EMAIL', 'qa-reports@lists.php.net');\n    define('QA_SUBMISSION_PAGE', 'https://qa.php.net/buildtest-process.php');\n    define('QA_REPORTS_PAGE', 'https://qa.php.net/reports');\n    define('TRAVIS_CI', (bool) getenv('TRAVIS'));\n\n    // Determine the tests to be run.\n\n    $test_files = [];\n    $redir_tests = [];\n    $test_results = [];\n    $PHP_FAILED_TESTS = [\n        'BORKED' => [],\n        'FAILED' => [],\n        'WARNED' => [],\n        'LEAKED' => [],\n        'XFAILED' => [],\n        'XLEAKED' => [],\n        'SLOW' => []\n    ];\n\n    // If parameters given assume they represent selected tests to run.\n    $result_tests_file = false;\n    $failed_tests_file = false;\n    $pass_option_n = false;\n    $pass_options = '';\n\n    $output_file = INIT_DIR . '/php_test_results_' . date('Ymd_Hi') . '.txt';\n\n    $just_save_results = false;\n    $valgrind = null;\n    $html_output = false;\n    $html_file = null;\n    $temp_source = null;\n    $temp_target = null;\n    $temp_urlbase = null;\n    $conf_passed = null;\n    $no_clean = false;\n    $slow_min_ms = INF;\n    $preload = false;\n    $shuffle = false;\n    $workers = null;\n\n    $cfgtypes = ['show', 'keep'];\n    $cfgfiles = ['skip', 'php', 'clean', 'out', 'diff', 'exp', 'mem'];\n    $cfg = [];\n\n    foreach ($cfgtypes as $type) {\n        $cfg[$type] = [];\n\n        foreach ($cfgfiles as $file) {\n            $cfg[$type][$file] = false;\n        }\n    }\n\n    if (!isset($argc, $argv) || !$argc) {\n        $argv = [__FILE__];\n        $argc = 1;\n    }\n\n    // swoole patch: show diff by default\n    $cfg['show']['diff'] = true;\n    $cfg['show']['mem'] = true;\n    $cfg['show']['slow'] = true;\n    $slow_min_ms = 1000;\n\n    if (getenv('TEST_PHP_ARGS')) {\n        $argv = array_merge($argv, explode(' ', getenv('TEST_PHP_ARGS')));\n        $argc = count($argv);\n    }\n\n    for ($i = 1; $i < $argc; $i++) {\n        $is_switch = false;\n        $switch = substr($argv[$i], 1, 1);\n        $repeat = str_starts_with($argv[$i], '-');\n\n        while ($repeat) {\n            if (!$is_switch) {\n                $switch = substr($argv[$i], 1, 1);\n            }\n\n            $is_switch = true;\n\n            if ($repeat) {\n                foreach ($cfgtypes as $type) {\n                    if (str_starts_with($switch, '--' . $type)) {\n                        foreach ($cfgfiles as $file) {\n                            if ($switch == '--' . $type . '-' . $file) {\n                                $cfg[$type][$file] = true;\n                                $is_switch = false;\n                                break;\n                            }\n                        }\n                    }\n                }\n            }\n\n            if (!$is_switch) {\n                $is_switch = true;\n                break;\n            }\n\n            $repeat = false;\n\n            switch ($switch) {\n                case 'j':\n                    $workers = substr($argv[$i], 2);\n                    if (!preg_match('/^\\d+$/', $workers) || $workers == 0) {\n                        error(\"'$workers' is not a valid number of workers, try e.g. -j16 for 16 workers\");\n                    }\n                    $workers = intval($workers, 10);\n                    // Don't use parallel testing infrastructure if there is only one worker.\n                    if ($workers === 1) {\n                        $workers = null;\n                    }\n                    break;\n                case 'r':\n                case 'l':\n                    $test_list = file($argv[++$i]);\n                    if ($test_list) {\n                        foreach ($test_list as $test) {\n                            $matches = [];\n                            if (preg_match('/^#.*\\[(.*)\\]\\:\\s+(.*)$/', $test, $matches)) {\n                                $redir_tests[] = [$matches[1], $matches[2]];\n                            } else {\n                                if (strlen($test)) {\n                                    $test_files[] = trim($test);\n                                }\n                            }\n                        }\n                    }\n                    if ($switch != 'l') {\n                        break;\n                    }\n                    $i--;\n                // no break\n                case 'w':\n                    $failed_tests_file = fopen($argv[++$i], 'w+t');\n                    break;\n                case 'a':\n                    $failed_tests_file = fopen($argv[++$i], 'a+t');\n                    break;\n                case 'W':\n                    $result_tests_file = fopen($argv[++$i], 'w+t');\n                    break;\n                case 'c':\n                    $conf_passed = $argv[++$i];\n                    break;\n                case 'd':\n                    $ini_overwrites[] = $argv[++$i];\n                    break;\n                case 'g':\n                    $SHOW_ONLY_GROUPS = explode(\",\", $argv[++$i]);\n                    break;\n                //case 'h'\n                case '--keep-all':\n                    foreach ($cfgfiles as $file) {\n                        $cfg['keep'][$file] = true;\n                    }\n                    break;\n                //case 'l'\n                case 'm':\n                    $valgrind = new RuntestsValgrind($environment);\n                    break;\n                case 'M':\n                    $valgrind = new RuntestsValgrind($environment, $argv[++$i]);\n                    break;\n                case 'n':\n                    if (!$pass_option_n) {\n                        $pass_options .= ' -n';\n                    }\n                    $pass_option_n = true;\n                    break;\n                case 'e':\n                    $pass_options .= ' -e';\n                    break;\n                case '--preload':\n                    $preload = true;\n                    break;\n                case '--no-clean':\n                    $no_clean = true;\n                    break;\n                case 'p':\n                    $php = $argv[++$i];\n                    putenv(\"TEST_PHP_EXECUTABLE=$php\");\n                    $environment['TEST_PHP_EXECUTABLE'] = $php;\n                    break;\n                case 'P':\n                    $php = PHP_BINARY;\n                    putenv(\"TEST_PHP_EXECUTABLE=$php\");\n                    $environment['TEST_PHP_EXECUTABLE'] = $php;\n                    break;\n                case 'q':\n                    putenv('NO_INTERACTION=1');\n                    $environment['NO_INTERACTION'] = 1;\n                    break;\n                //case 'r'\n                case 's':\n                    $output_file = $argv[++$i];\n                    $just_save_results = true;\n                    break;\n                case '--set-timeout':\n                    $environment['TEST_TIMEOUT'] = $argv[++$i];\n                    break;\n                case '--show-all':\n                    foreach ($cfgfiles as $file) {\n                        $cfg['show'][$file] = true;\n                    }\n                    break;\n                case '--show-slow':\n                    $slow_min_ms = $argv[++$i];\n                    break;\n                case '--temp-source':\n                    $temp_source = $argv[++$i];\n                    break;\n                case '--temp-target':\n                    $temp_target = $argv[++$i];\n                    if ($temp_urlbase) {\n                        $temp_urlbase = $temp_target;\n                    }\n                    break;\n                case '--temp-urlbase':\n                    $temp_urlbase = $argv[++$i];\n                    break;\n                case 'v':\n                case '--verbose':\n                    $DETAILED = true;\n                    break;\n                case 'x':\n                    $environment['SKIP_SLOW_TESTS'] = 1;\n                    break;\n                case '--offline':\n                    $environment['SKIP_ONLINE_TESTS'] = 1;\n                    break;\n                case '--shuffle':\n                    $shuffle = true;\n                    break;\n                case '--asan':\n                    $environment['USE_ZEND_ALLOC'] = 0;\n                    $environment['USE_TRACKED_ALLOC'] = 1;\n                    $environment['SKIP_ASAN'] = 1;\n                    $environment['SKIP_PERF_SENSITIVE'] = 1;\n\n                    $lsanSuppressions = __DIR__ . '/azure/lsan-suppressions.txt';\n                    if (file_exists($lsanSuppressions)) {\n                        $environment['LSAN_OPTIONS'] = 'suppressions=' . $lsanSuppressions\n                            . ':print_suppressions=0';\n                    }\n                    break;\n                //case 'w'\n                case '-':\n                    // repeat check with full switch\n                    $switch = $argv[$i];\n                    if ($switch != '-') {\n                        $repeat = true;\n                    }\n                    break;\n                case '--html':\n                    $html_file = fopen($argv[++$i], 'wt');\n                    $html_output = is_resource($html_file);\n                    break;\n                case '--version':\n                    echo '$Id: ebcaabba02a5af6f5dc62dda027befb95385d5fc $' . \"\\n\";\n                    exit(2);\n\n                default:\n                    echo \"Illegal switch '$switch' specified!\\n\";\n                case 'h':\n                case '-help':\n                case '--help':\n                    show_usage();\n                    exit(2);\n            }\n        }\n\n        if (!$is_switch) {\n            $testfile = realpath($argv[$i]);\n\n            if (!$testfile && str_contains($argv[$i], '*') && function_exists('glob')) {\n\n                if (str_ends_with($argv[$i], '.phpt')) {\n                    $pattern_match = glob($argv[$i]);\n                } else {\n                    if (preg_match(\"/\\*$/\", $argv[$i])) {\n                        $pattern_match = glob($argv[$i] . '.phpt');\n                    } else {\n                        die('Cannot find test file \"' . $argv[$i] . '\".' . PHP_EOL);\n                    }\n                }\n\n                if (is_array($pattern_match)) {\n                    $test_files = array_merge($test_files, $pattern_match);\n                }\n\n            } else {\n                if (is_dir($testfile)) {\n                    find_files($testfile);\n                } else {\n                    if (str_ends_with($testfile, '.phpt')) {\n                        $test_files[] = $testfile;\n                    } else {\n                        die('Cannot find test file \"' . $argv[$i] . '\".' . PHP_EOL);\n                    }\n                }\n            }\n        }\n    }\n\n    // Default to PHP_BINARY as executable\n    if (!isset($environment['TEST_PHP_EXECUTABLE'])) {\n        $php = PHP_BINARY;\n        putenv(\"TEST_PHP_EXECUTABLE=$php\");\n        $environment['TEST_PHP_EXECUTABLE'] = $php;\n    }\n\n    if ($conf_passed && strlen($conf_passed)) {\n        if (IS_WINDOWS) {\n            $pass_options .= \" -c \" . escapeshellarg($conf_passed);\n        } else {\n            $pass_options .= \" -c '\" . realpath($conf_passed) . \"'\";\n        }\n    }\n\n    $test_files = array_unique($test_files);\n    $test_files = array_merge($test_files, $redir_tests);\n\n    // Run selected tests.\n    $test_cnt = count($test_files);\n\n    if ($test_cnt) {\n        putenv('NO_INTERACTION=1');\n        verify_config();\n        write_information();\n        usort($test_files, \"test_sort\");\n        $start_time = time();\n\n        if (!$html_output) {\n            echo \"Running selected tests.\\n\";\n        } else {\n            show_start($start_time);\n        }\n\n        $test_idx = 0;\n        run_all_tests($test_files, $environment);\n        $end_time = time();\n\n        if ($html_output) {\n            show_end($end_time);\n        }\n\n        if ($failed_tests_file) {\n            fclose($failed_tests_file);\n        }\n\n        if ($result_tests_file) {\n            fclose($result_tests_file);\n        }\n\n        compute_summary();\n        if ($html_output) {\n            fwrite($html_file, \"<hr/>\\n\" . get_summary(false, true));\n        }\n        echo \"=====================================================================\";\n        echo get_summary(false, false);\n\n        if ($html_output) {\n            fclose($html_file);\n        }\n\n        if ($output_file != '' && $just_save_results) {\n            save_or_mail_results();\n        }\n\n        junit_save_xml();\n\n        if (getenv('REPORT_EXIT_STATUS') !== '0' &&\n            getenv('REPORT_EXIT_STATUS') !== 'no' && ($sum_results['FAILED'] || $sum_results['BORKED'] || $sum_results['LEAKED'])) {\n            exit(201);\n        }\n\n        return;\n    }\n\n    verify_config();\n    write_information();\n\n    // Compile a list of all test files (*.phpt).\n    $test_files = array();\n    $exts_tested = count($exts_to_test);\n    $exts_skipped = 0;\n    $ignored_by_ext = 0;\n    sort($exts_to_test);\n    $test_dirs = array();\n    $optionals = array('Zend', 'tests', 'ext', 'sapi');\n\n    foreach ($optionals as $dir) {\n        if (is_dir($dir)) {\n            $test_dirs[] = $dir;\n        }\n    }\n\n    // Convert extension names to lowercase\n    foreach ($exts_to_test as $key => $val) {\n        $exts_to_test[$key] = strtolower($val);\n    }\n\n    foreach ($test_dirs as $dir) {\n        find_files(TEST_PHP_SRCDIR . \"/{$dir}\", $dir == 'ext');\n    }\n\n    foreach ($user_tests as $dir) {\n        find_files($dir, $dir == 'ext');\n    }\n\n    $test_files = array_unique($test_files);\n    usort($test_files, \"test_sort\");\n\n    $start_time = time();\n    show_start($start_time);\n\n    $test_cnt = count($test_files);\n    $test_idx = 0;\n    run_all_tests($test_files, $environment);\n    $end_time = time();\n\n    if ($failed_tests_file) {\n        fclose($failed_tests_file);\n    }\n\n    if ($result_tests_file) {\n        fclose($result_tests_file);\n    }\n\n    // Summarize results\n\n    if (0 == count($test_results)) {\n        echo \"No tests were run.\\n\";\n        return;\n    }\n\n    compute_summary();\n\n    show_end($end_time);\n    show_summary();\n\n    if ($html_output) {\n        fclose($html_file);\n    }\n\n    save_or_mail_results();\n\n    junit_save_xml();\n    if (getenv('REPORT_EXIT_STATUS') !== '0' &&\n        getenv('REPORT_EXIT_STATUS') !== 'no' && ($sum_results['FAILED'] || $sum_results['LEAKED'])) {\n        exit(202);\n    }\n    exit(0);\n}\n\nfunction get_shortname($file): array|string\n{\n    return str_replace(TEST_PHP_SRCDIR . '/', '', $file);\n}\n\nfunction verify_config()\n{\n    global $php;\n\n    if (empty($php) || !file_exists($php)) {\n        error('environment variable TEST_PHP_EXECUTABLE must be set to specify PHP executable!');\n    }\n\n    if (!is_executable($php)) {\n        error(\"invalid PHP executable specified by TEST_PHP_EXECUTABLE  = $php\");\n    }\n}\n\nfunction write_information()\n{\n    global $php, $php_cgi, $phpdbg, $php_info, $user_tests, $ini_overwrites, $pass_options, $exts_to_test, $valgrind, $no_file_cache;\n\n    // Get info from php\n    $info_file = __DIR__ . '/run-test-info.php';\n    @unlink($info_file);\n    $php_info = '<?php echo \"\nPHP_SAPI    : \" , PHP_SAPI , \"\nPHP_VERSION : \" , phpversion() , \"\nZEND_VERSION: \" , zend_version() , \"\nPHP_OS      : \" , PHP_OS , \" - \" , php_uname() , \"\nINI actual  : \" , realpath(get_cfg_var(\"cfg_file_path\")) , \"\nMore .INIs  : \" , (function_exists(\\'php_ini_scanned_files\\') ? str_replace(\"\\n\",\"\", php_ini_scanned_files()) : \"** not determined **\"); ?>';\n    save_text($info_file, $php_info);\n    $info_params = array();\n    settings2array($ini_overwrites, $info_params);\n    $info_params = settings2params($info_params);\n    $php_info = shell_exec(\"$php $pass_options $info_params $no_file_cache \\\"$info_file\\\"\");\n    define('TESTED_PHP_VERSION', shell_exec(\"$php -n -r \\\"echo PHP_VERSION;\\\"\"));\n\n    if ($php_cgi && $php != $php_cgi) {\n        $php_info_cgi = shell_exec(\"$php_cgi $pass_options $info_params $no_file_cache -q \\\"$info_file\\\"\");\n        $php_info_sep = \"\\n---------------------------------------------------------------------\";\n        $php_cgi_info = \"$php_info_sep\\nPHP         : $php_cgi $php_info_cgi$php_info_sep\";\n    } else {\n        $php_cgi_info = '';\n    }\n\n    if ($phpdbg) {\n        $phpdbg_info = shell_exec(\"$phpdbg $pass_options $info_params $no_file_cache -qrr \\\"$info_file\\\"\");\n        $php_info_sep = \"\\n---------------------------------------------------------------------\";\n        $phpdbg_info = \"$php_info_sep\\nPHP         : $phpdbg $phpdbg_info$php_info_sep\";\n    } else {\n        $phpdbg_info = '';\n    }\n\n    if (function_exists('opcache_invalidate')) {\n        opcache_invalidate($info_file, true);\n    }\n    @unlink($info_file);\n\n    // load list of enabled extensions\n    save_text($info_file,\n        '<?php echo str_replace(\"Zend OPcache\", \"opcache\", implode(\",\", get_loaded_extensions())); ?>');\n    $exts_to_test = explode(',', shell_exec(\"$php $pass_options $info_params $no_file_cache \\\"$info_file\\\"\"));\n    // check for extensions that need special handling and regenerate\n    $info_params_ex = array(\n        'session' => array('session.auto_start=0'),\n        'tidy' => array('tidy.clean_output=0'),\n        'zlib' => array('zlib.output_compression=Off'),\n        'xdebug' => array('xdebug.default_enable=0'),\n        'mbstring' => array('mbstring.func_overload=0'),\n    );\n\n    foreach ($info_params_ex as $ext => $ini_overwrites_ex) {\n        if (in_array($ext, $exts_to_test)) {\n            $ini_overwrites = array_merge($ini_overwrites, $ini_overwrites_ex);\n        }\n    }\n\n    if (function_exists('opcache_invalidate')) {\n        opcache_invalidate($info_file, true);\n    }\n    @unlink($info_file);\n\n    // Write test context information.\n    echo \"\n=====================================================================\nPHP         : $php $php_info $php_cgi_info $phpdbg_info\nCWD         : \" . TEST_PHP_SRCDIR . \"\nExtra dirs  : \";\n    foreach ($user_tests as $test_dir) {\n        echo \"{$test_dir}\\n\t\t\t  \";\n    }\n    echo \"\nVALGRIND    : \" . ($valgrind ? $valgrind->getHeader() : 'Not used') . \"\n=====================================================================\n\";\n}\n\nfunction save_or_mail_results()\n{\n    global $sum_results, $just_save_results, $failed_test_summary,\n           $PHP_FAILED_TESTS, $php, $output_file;\n\n    /* We got failed Tests, offer the user to send an e-mail to QA team, unless NO_INTERACTION is set */\n    if (!getenv('NO_INTERACTION') && !TRAVIS_CI) {\n        $fp = fopen(\"php://stdin\", \"r+\");\n        if ($sum_results['FAILED'] || $sum_results['BORKED'] || $sum_results['WARNED'] || $sum_results['LEAKED']) {\n            echo \"\\nYou may have found a problem in PHP.\";\n        }\n        echo \"\\nThis report can be automatically sent to the PHP QA team at\\n\";\n        echo QA_REPORTS_PAGE . \" and https://news.php.net/php.qa.reports\\n\";\n        echo \"This gives us a better understanding of PHP's behavior.\\n\";\n        echo \"If you don't want to send the report immediately you can choose\\n\";\n        echo \"option \\\"s\\\" to save it.\tYou can then email it to \" . PHP_QA_EMAIL . \" later.\\n\";\n        echo \"Do you want to send this report now? [Yns]: \";\n        flush();\n\n        $user_input = fgets($fp, 10);\n        $just_save_results = (strtolower($user_input[0]) == 's');\n    }\n\n    if ($just_save_results || !getenv('NO_INTERACTION') || TRAVIS_CI) {\n        if ($just_save_results || TRAVIS_CI || strlen(trim($user_input)) == 0 || strtolower($user_input[0]) == 'y') {\n            /*\n             * Collect information about the host system for our report\n             * Fetch phpinfo() output so that we can see the PHP environment\n             * Make an archive of all the failed tests\n             * Send an email\n             */\n            if ($just_save_results) {\n                $user_input = 's';\n            }\n\n            /* Ask the user to provide an email address, so that QA team can contact the user */\n            if (TRAVIS_CI) {\n                $user_email = 'travis at php dot net';\n            } elseif (!strncasecmp($user_input, 'y', 1) || strlen(trim($user_input)) == 0) {\n                echo \"\\nPlease enter your email address.\\n(Your address will be mangled so that it will not go out on any\\nmailinglist in plain text): \";\n                flush();\n                $user_email = trim(fgets($fp, 1024));\n                $user_email = str_replace(\"@\", \" at \", str_replace(\".\", \" dot \", $user_email));\n            }\n\n            $failed_tests_data = '';\n            $sep = \"\\n\" . str_repeat('=', 80) . \"\\n\";\n            $failed_tests_data .= $failed_test_summary . \"\\n\";\n            $failed_tests_data .= get_summary(true, false) . \"\\n\";\n\n            if ($sum_results['FAILED']) {\n                foreach ($PHP_FAILED_TESTS['FAILED'] as $test_info) {\n                    $failed_tests_data .= $sep . $test_info['name'] . $test_info['info'];\n                    $failed_tests_data .= $sep . file_get_contents(realpath($test_info['output']));\n                    $failed_tests_data .= $sep . file_get_contents(realpath($test_info['diff']));\n                    $failed_tests_data .= $sep . \"\\n\\n\";\n                }\n                $status = \"failed\";\n            } else {\n                $status = \"success\";\n            }\n\n            $failed_tests_data .= \"\\n\" . $sep . 'BUILD ENVIRONMENT' . $sep;\n            $failed_tests_data .= \"OS:\\n\" . PHP_OS . \" - \" . php_uname() . \"\\n\\n\";\n            $ldd = $autoconf = $sys_libtool = $libtool = $compiler = 'N/A';\n\n            if (!IS_WINDOWS) {\n                /* If PHP_AUTOCONF is set, use it; otherwise, use 'autoconf'. */\n                if (getenv('PHP_AUTOCONF')) {\n                    $autoconf = shell_exec(getenv('PHP_AUTOCONF') . ' --version');\n                } else {\n                    $autoconf = shell_exec('autoconf --version');\n                }\n\n                /* Always use the generated libtool - Mac OSX uses 'glibtool' */\n                $libtool = shell_exec(INIT_DIR . '/libtool --version');\n\n                /* Use shtool to find out if there is glibtool present (MacOSX) */\n                $sys_libtool_path = shell_exec(__DIR__ . '/build/shtool path glibtool libtool');\n\n                if ($sys_libtool_path) {\n                    $sys_libtool = shell_exec(str_replace(\"\\n\", \"\", $sys_libtool_path) . ' --version');\n                }\n\n                /* Try the most common flags for 'version' */\n                $flags = array('-v', '-V', '--version');\n                $cc_status = 0;\n\n                foreach ($flags as $flag) {\n                    system(getenv('CC') . \" $flag >/dev/null 2>&1\", $cc_status);\n                    if ($cc_status == 0) {\n                        $compiler = shell_exec(getenv('CC') . \" $flag 2>&1\");\n                        break;\n                    }\n                }\n\n                $ldd = shell_exec(\"ldd $php 2>/dev/null\");\n            }\n\n            $failed_tests_data .= \"Autoconf:\\n$autoconf\\n\";\n            $failed_tests_data .= \"Bundled Libtool:\\n$libtool\\n\";\n            $failed_tests_data .= \"System Libtool:\\n$sys_libtool\\n\";\n            $failed_tests_data .= \"Compiler:\\n$compiler\\n\";\n            $failed_tests_data .= \"Bison:\\n\" . shell_exec('bison --version 2>/dev/null') . \"\\n\";\n            $failed_tests_data .= \"Libraries:\\n$ldd\\n\";\n            $failed_tests_data .= \"\\n\";\n\n            if (isset($user_email)) {\n                $failed_tests_data .= \"User's E-mail: \" . $user_email . \"\\n\\n\";\n            }\n\n            $failed_tests_data .= $sep . \"PHPINFO\" . $sep;\n            $failed_tests_data .= shell_exec($php . ' -ddisplay_errors=stderr -dhtml_errors=0 -i 2> /dev/null');\n\n            if (($just_save_results || !mail_qa_team($failed_tests_data, $status)) && !TRAVIS_CI) {\n                file_put_contents($output_file, $failed_tests_data);\n\n                if (!$just_save_results) {\n                    echo \"\\nThe test script was unable to automatically send the report to PHP's QA Team\\n\";\n                }\n\n                echo \"Please send \" . $output_file . \" to \" . PHP_QA_EMAIL . \" manually, thank you.\\n\";\n            } elseif (!getenv('NO_INTERACTION') && !TRAVIS_CI) {\n                fwrite($fp, \"\\nThank you for helping to make PHP better.\\n\");\n                fclose($fp);\n            }\n        }\n    }\n}\n\nfunction find_files($dir, $is_ext_dir = false, $ignore = false)\n{\n    global $test_files, $exts_to_test, $ignored_by_ext, $exts_skipped;\n\n    $o = opendir($dir) or error(\"cannot open directory: $dir\");\n\n    while (($name = readdir($o)) !== false) {\n\n        if (is_dir(\"{$dir}/{$name}\") && !in_array($name, array('.', '..', '.svn'))) {\n            $skip_ext = ($is_ext_dir && !in_array(strtolower($name), $exts_to_test));\n            if ($skip_ext) {\n                $exts_skipped++;\n            }\n            find_files(\"{$dir}/{$name}\", false, $ignore || $skip_ext);\n        }\n\n        // Cleanup any left-over tmp files from last run.\n        if (str_ends_with($name, '.tmp')) {\n            @unlink(\"$dir/$name\");\n            continue;\n        }\n\n        // Otherwise we're only interested in *.phpt files.\n        if (str_ends_with($name, '.phpt')) {\n            if ($ignore) {\n                $ignored_by_ext++;\n            } else {\n                $testfile = realpath(\"{$dir}/{$name}\");\n                $test_files[] = $testfile;\n            }\n        }\n    }\n\n    closedir($o);\n}\n\nfunction test_name($name)\n{\n    if (is_array($name)) {\n        return $name[0] . ':' . $name[1];\n    } else {\n        return $name;\n    }\n}\n\nfunction test_sort($a, $b)\n{\n    $a = test_name($a);\n    $b = test_name($b);\n\n    $ta = str_starts_with($a, TEST_PHP_SRCDIR . \"/tests\") ? 1 + (str_starts_with($a, TEST_PHP_SRCDIR . \"/tests/run-test\") ? 1 : 0) : 0;\n    $tb = str_starts_with($b, TEST_PHP_SRCDIR . \"/tests\") ? 1 + (str_starts_with($b, TEST_PHP_SRCDIR . \"/tests/run-test\") ? 1 : 0) : 0;\n\n    if ($ta == $tb) {\n        return strcmp($a, $b);\n    } else {\n        return $tb - $ta;\n    }\n}\n\n\n\n//\n// Send Email to QA Team\n//\n\nfunction mail_qa_team($data, $status = false)\n{\n    $url_bits = parse_url(QA_SUBMISSION_PAGE);\n\n    if ($proxy = getenv('http_proxy')) {\n        $proxy = parse_url($proxy);\n        $path = $url_bits['host'] . $url_bits['path'];\n        $host = $proxy['host'];\n        if (empty($proxy['port'])) {\n            $proxy['port'] = 80;\n        }\n        $port = $proxy['port'];\n    } else {\n        $path = $url_bits['path'];\n        $host = $url_bits['host'];\n        $port = empty($url_bits['port']) ? 80 : $port = $url_bits['port'];\n    }\n\n    $data = \"php_test_data=\" . urlencode(base64_encode(str_replace(\"\\00\", '[0x0]', $data)));\n    $data_length = strlen($data);\n\n    $fs = fsockopen($host, $port, $errno, $errstr, 10);\n\n    if (!$fs) {\n        return false;\n    }\n\n    $php_version = urlencode(TESTED_PHP_VERSION);\n\n    echo \"\\nPosting to \" . QA_SUBMISSION_PAGE . \"\\n\";\n    fwrite($fs, \"POST \" . $path . \"?status=$status&version=$php_version HTTP/1.1\\r\\n\");\n    fwrite($fs, \"Host: \" . $host . \"\\r\\n\");\n    fwrite($fs, \"User-Agent: QA Browser 0.1\\r\\n\");\n    fwrite($fs, \"Content-Type: application/x-www-form-urlencoded\\r\\n\");\n    fwrite($fs, \"Content-Length: \" . $data_length . \"\\r\\n\\r\\n\");\n    fwrite($fs, $data);\n    fwrite($fs, \"\\r\\n\\r\\n\");\n    fclose($fs);\n\n    return 1;\n}\n\n\n//\n//  Write the given text to a temporary file, and return the filename.\n//\n\nfunction save_text($filename, $text, $filename_copy = null)\n{\n    global $DETAILED;\n\n    if ($filename_copy && $filename_copy != $filename) {\n        if (file_put_contents($filename_copy, $text) === false) {\n            error(\"Cannot open file '\" . $filename_copy . \"' (save_text)\");\n        }\n    }\n\n    if (file_put_contents($filename, $text) === false) {\n        error(\"Cannot open file '\" . $filename . \"' (save_text)\");\n    }\n\n    if (1 < $DETAILED) echo \"\nFILE $filename {{{\n$text\n}}}\n\";\n}\n\n//\n//  Write an error in a format recognizable to Emacs or MSVC.\n//\n\nfunction error_report($testname, $logname, $tested)\n{\n    $testname = realpath($testname);\n    $logname = realpath($logname);\n\n    switch (strtoupper(getenv('TEST_PHP_ERROR_STYLE'))) {\n        case 'MSVC':\n            echo $testname . \"(1) : $tested\\n\";\n            echo $logname . \"(1) :  $tested\\n\";\n            break;\n        case 'EMACS':\n            echo $testname . \":1: $tested\\n\";\n            echo $logname . \":1:  $tested\\n\";\n            break;\n    }\n}\n\nfunction system_with_timeout($commandline, $env = null, $stdin = null, $captureStdIn = true, $captureStdOut = true, $captureStdErr = true)\n{\n    global $valgrind;\n\n    $data = '';\n\n    $bin_env = array();\n    foreach ((array)$env as $key => $value) {\n        $bin_env[$key] = $value;\n    }\n\n    $descriptorspec = array();\n    if ($captureStdIn) {\n        $descriptorspec[0] = array('pipe', 'r');\n    }\n    if ($captureStdOut) {\n        $descriptorspec[1] = array('pipe', 'w');\n    }\n    if ($captureStdErr) {\n        $descriptorspec[2] = array('pipe', 'w');\n    }\n    $proc = proc_open($commandline, $descriptorspec, $pipes, TEST_PHP_SRCDIR, $bin_env, array('suppress_errors' => true));\n\n    if (!$proc) {\n        return false;\n    }\n\n    if ($captureStdIn) {\n        if (!is_null($stdin)) {\n            fwrite($pipes[0], $stdin);\n        }\n        fclose($pipes[0]);\n        unset($pipes[0]);\n    }\n\n    $timeout = $valgrind ? 300 : ($env['TEST_TIMEOUT'] ?? 60);\n\n    while (true) {\n        /* hide errors from interrupted syscalls */\n        $r = $pipes;\n        $w = null;\n        $e = null;\n\n        $n = @stream_select($r, $w, $e, $timeout);\n\n        if ($n === false) {\n            break;\n        } else if ($n === 0) {\n            /* timed out */\n            $data .= \"\\n ** ERROR: process timed out **\\n\";\n            proc_terminate($proc, 9);\n            return $data;\n        } else if ($n > 0) {\n            // swoole patch: fix blocking read\n            if (SWOOLE_IS_MUSL_LIBC) {\n                if ($captureStdOut) {\n                    $line = fread($pipes[1], 8192);\n                } elseif ($captureStdErr) {\n                    $line = fread($pipes[2], 8192);\n                } else {\n                    $line = '';\n                }\n            } else {\n                $line = '';\n                if ($captureStdOut && in_array($pipes[1], $r)) {\n                    $line .= fread($pipes[1], 8192);\n                }\n                if ($captureStdErr && in_array($pipes[2], $r)) {\n                    $line .= fread($pipes[2], 8192);\n                }\n            }\n            if (strlen($line) == 0) {\n                /* EOF */\n                break;\n            }\n            $data .= $line;\n            if (strlen($data) > SWOOLE_TEST_OUTPUT_MAX_SIZE) {\n                echo \"FATAL ERROR: The output content exceeds the maximum size\\n\";\n                echo \"========================================================\\n\";\n                echo substr($data, 0, 65536);\n                exit(253);\n            }\n        }\n    }\n\n    $stat = proc_get_status($proc);\n\n    if ($stat['signaled']) {\n        $data .= \"\\nTermsig=\" . $stat['stopsig'] . \"\\n\";\n    }\n    if ($stat[\"exitcode\"] > 128 && $stat[\"exitcode\"] < 160) {\n        $data .= \"\\nTermsig=\" . ($stat[\"exitcode\"] - 128) . \"\\n\";\n    }\n\n    proc_close($proc);\n    return $data;\n}\n\nfunction run_all_tests($test_files, $env, $redir_tested = null)\n{\n    global $test_results, $failed_tests_file, $result_tests_file, $php, $test_idx;\n    // Parallel testing\n    global $PHP_FAILED_TESTS, $workers, $workerID, $workerSock;\n\n    /* Ignore -jN if there is only one file to analyze. */\n    if ($workers !== null && count($test_files) > 1 && !$workerID) {\n        run_all_tests_parallel($test_files, $env, $redir_tested);\n        return;\n    }\n\n    foreach ($test_files as $name) {\n        if (is_array($name)) {\n            $index = \"# $name[1]: $name[0]\";\n\n            if ($redir_tested) {\n                $name = $name[0];\n            }\n        } else if ($redir_tested) {\n            $index = \"# $redir_tested: $name\";\n        } else {\n            $index = $name;\n        }\n        $test_idx++;\n\n        if ($workerID) {\n            send_message($workerSock, [\n                    \"type\" => \"begin\",\n                    \"file\" => $name,\n            ]);\n        }\n\n        if ($workerID) {\n            $PHP_FAILED_TESTS = ['BORKED' => [], 'FAILED' => [], 'WARNED' => [], 'LEAKED' => [], 'XFAILED' => [], 'XLEAKED' => [], 'SLOW' => []];\n            ob_start();\n        }\n\n        $result = run_test($php, $name, $env);\n        if ($workerID) {\n            $resultText = ob_get_clean();\n        }\n\n        if (!is_array($name) && $result != 'REDIR') {\n            if ($workerID) {\n                send_message($workerSock, [\n                    \"type\" => \"test_result\",\n                    \"name\" => $name,\n                    \"index\" => $index,\n                    \"result\" => $result,\n                    \"text\" => $resultText,\n                    \"PHP_FAILED_TESTS\" => $PHP_FAILED_TESTS\n                ]);\n                continue;\n            }\n\n            $test_results[$index] = $result;\n            if ($failed_tests_file && ($result == 'XFAILED' || $result == 'XLEAKED' || $result == 'FAILED' || $result == 'WARNED' || $result == 'LEAKED')) {\n                fwrite($failed_tests_file, \"$index\\n\");\n            }\n            if ($result_tests_file) {\n                fwrite($result_tests_file, \"$result\\t$index\\n\");\n            }\n        }\n    }\n}\n\n/** The heart of parallel testing. */\nfunction run_all_tests_parallel($test_files, $env, $redir_tested) {\n    global $workers, $test_idx, $test_cnt, $test_results, $failed_tests_file, $result_tests_file, $PHP_FAILED_TESTS, $shuffle, $SHOW_ONLY_GROUPS, $valgrind;\n\n    // The PHP binary running run-tests.php, and run-tests.php itself\n    // This PHP executable is *not* necessarily the same as the tested version\n    $thisPHP = PHP_BINARY;\n    $thisScript = __FILE__;\n\n    $workerProcs = [];\n    $workerSocks = [];\n\n    echo \"=====================================================================\\n\";\n    echo \"========= WELCOME TO THE FUTURE: run-tests PARALLEL EDITION =========\\n\";\n    echo \"=====================================================================\\n\";\n\n    // Each test may specify a list of conflict keys. While a test that conflicts with\n    // key K is running, no other test that conflicts with K may run. Conflict keys are\n    // specified either in the --CONFLICTS-- section, or CONFLICTS file inside a directory.\n    $dirConflictsWith = [];\n    $fileConflictsWith = [];\n    $sequentialTests = [];\n    foreach ($test_files as $i => $file) {\n        $contents = file_get_contents($file);\n        if (preg_match('/^--CONFLICTS--(.+?)^--/ms', $contents, $matches)) {\n            $conflicts = parse_conflicts($matches[1]);\n        } else {\n            // Cache per-directory conflicts in a separate map, so we compute these only once.\n            $dir = dirname($file);\n            if (!isset($dirConflictsWith[$dir])) {\n                $dirConflicts = [];\n                if (file_exists($dir . '/CONFLICTS')) {\n                    $contents = file_get_contents($dir . '/CONFLICTS');\n                    $dirConflicts = parse_conflicts($contents);\n                }\n                $dirConflictsWith[$dir] = $dirConflicts;\n            }\n            $conflicts = $dirConflictsWith[$dir];\n        }\n\n        // For tests conflicting with \"all\", no other tests may run in parallel. We'll run these\n        // tests separately at the end, when only one worker is left.\n        if (in_array('all', $conflicts, true)) {\n            $sequentialTests[] = $file;\n            unset($test_files[$i]);\n        }\n\n        $fileConflictsWith[$file] = $conflicts;\n    }\n\n    // Some tests assume that they are executed in a certain order. We will be popping from\n    // $test_files, so reverse its order here. This makes sure that order is preserved at least\n    // for tests with a common conflict key.\n    $test_files = array_reverse($test_files);\n\n    // To discover parallelization issues it is useful to randomize the test order.\n    if ($shuffle) {\n        shuffle($test_files);\n    }\n    /* Don't start more workers than test files */\n    $workers = max(1, min($workers, count($test_files)));\n\n    echo \"Spawning workers… \";\n\n    // We use sockets rather than STDIN/STDOUT for comms because on Windows,\n    // those can't be non-blocking for some reason.\n    $listenSock = stream_socket_server(\"tcp://127.0.0.1:0\") or error(\"Couldn't create socket on localhost.\");\n    $sockName = stream_socket_get_name($listenSock, false);\n    // PHP is terrible and returns IPv6 addresses not enclosed by []\n    $portPos = strrpos($sockName, \":\");\n    $sockHost = substr($sockName, 0, $portPos);\n    if (str_contains($sockHost, \":\")) {\n        $sockHost = \"[$sockHost]\";\n    }\n    $sockPort = substr($sockName, $portPos + 1);\n    $sockUri = \"tcp://$sockHost:$sockPort\";\n    $totalFileCount = count($test_files);\n\n    for ($i = 1; $i <= $workers; $i++) {\n        $proc = proc_open(\n            $thisPHP . ' ' . escapeshellarg($thisScript),\n            [], // Inherit our stdin, stdout and stderr\n            $pipes,\n            NULL,\n            $_ENV + [\n                \"TEST_PHP_WORKER\" => $i,\n                \"TEST_PHP_URI\" => $sockUri,\n            ],\n            [\n                \"suppress_errors\" => TRUE,\n                'create_new_console' => TRUE,\n            ]\n        );\n        if ($proc === FALSE) {\n            kill_children($workerProcs);\n            error(\"Failed to spawn worker $i\");\n        }\n        $workerProcs[$i] = $proc;\n\n        $workerSock = stream_socket_accept($listenSock, 5);\n        if ($workerSock === FALSE) {\n            kill_children($workerProcs);\n            error(\"Failed to accept connection from worker $i\");\n        }\n\n        $greeting = base64_encode(serialize([\n                \"type\" => \"hello\",\n                \"workerID\" => $i,\n                \"GLOBALS\" => $GLOBALS,\n                \"constants\" => [\n                    \"INIT_DIR\" => INIT_DIR,\n                    \"TEST_PHP_SRCDIR\" => TEST_PHP_SRCDIR,\n                    \"PHP_QA_EMAIL\" => PHP_QA_EMAIL,\n                    \"QA_SUBMISSION_PAGE\" => QA_SUBMISSION_PAGE,\n                    \"QA_REPORTS_PAGE\" => QA_REPORTS_PAGE,\n                    \"TRAVIS_CI\" => TRAVIS_CI\n                ]\n            ])) . \"\\n\";\n\n        stream_set_timeout($workerSock, 5);\n        if (fwrite($workerSock, $greeting) === FALSE) {\n            kill_children($workerProcs);\n            error(\"Failed to send greeting to worker $i.\");\n        }\n\n        $rawReply = fgets($workerSock);\n        if ($rawReply === FALSE) {\n            kill_children($workerProcs);\n            error(\"Failed to read greeting reply from worker $i.\");\n        }\n\n        $reply = unserialize(base64_decode($rawReply));\n        if (!$reply || $reply[\"type\"] !== \"hello_reply\" || $reply[\"workerID\"] !== $i) {\n            kill_children($workerProcs);\n            error(\"Greeting reply from worker $i unexpected or could not be decoded: '$rawReply'\");\n        }\n\n        stream_set_timeout($workerSock, 0);\n        stream_set_blocking($workerSock, FALSE);\n\n        $workerSocks[$i] = $workerSock;\n\n        echo \"$i \";\n    }\n    echo \"… done!\\n\";\n    echo \"=====================================================================\\n\";\n    echo \"\\n\";\n\n    $rawMessageBuffers = [];\n    $testsInProgress = 0;\n\n    // Map from conflict key to worker ID.\n    $activeConflicts = [];\n    // Tests waiting due to conflicts. Map from conflict key to array.\n    $waitingTests = [];\n\n    escape:\n    while ($test_files || $sequentialTests || $testsInProgress > 0) {\n        $toRead = array_values($workerSocks);\n        $toWrite = NULL;\n        $toExcept = NULL;\n        if (stream_select($toRead, $toWrite, $toExcept, 10)) {\n            foreach ($toRead as $workerSock) {\n                $i = array_search($workerSock, $workerSocks);\n                if ($i === FALSE) {\n                    kill_children($workerProcs);\n                    error(\"Could not find worker stdout in array of worker stdouts, THIS SHOULD NOT HAPPEN.\");\n                }\n                while (FALSE !== ($rawMessage = fgets($workerSock))) {\n                    // work around fgets truncating things\n                    if (($rawMessageBuffers[$i] ?? '') !== '') {\n                        $rawMessage = $rawMessageBuffers[$i] . $rawMessage;\n                        $rawMessageBuffers[$i] = '';\n                    }\n                    if (!str_ends_with($rawMessage, \"\\n\")) {\n                        $rawMessageBuffers[$i] = $rawMessage;\n                        continue;\n                    }\n\n                    $message = unserialize(base64_decode($rawMessage));\n                    if (!$message) {\n                        kill_children($workerProcs);\n                        $stuff = fread($workerSock, 65536);\n                        error(\"Could not decode message from worker $i: '$rawMessage$stuff'\");\n                    }\n\n                    switch ($message[\"type\"]) {\n                        case \"tests_finished\":\n                            $testsInProgress--;\n                            foreach ($activeConflicts as $key => $workerId) {\n                                if ($workerId === $i) {\n                                    unset($activeConflicts[$key]);\n                                    if (isset($waitingTests[$key])) {\n                                        while ($test = array_pop($waitingTests[$key])) {\n                                            $test_files[] = $test;\n                                        }\n                                        unset($waitingTests[$key]);\n                                    }\n                                }\n                            }\n                            if (junit_enabled()) {\n                                junit_merge_results($message[\"junit\"]);\n                            }\n                        // intentional fall-through\n                        case \"ready\":\n                            // Schedule sequential tests only once we are down to one worker.\n                            if (count($workerProcs) === 1 && $sequentialTests) {\n                                $test_files = array_merge($test_files, $sequentialTests);\n                                $sequentialTests = [];\n                            }\n                            // Batch multiple tests to reduce communication overhead.\n                            // - When valgrind is used, communication overhead is relatively small,\n                            //   so just use a batch size of 1.\n                            // - If this is running a small enough number of tests,\n                            //   reduce the batch size to give batches to more workers.\n                            $files = [];\n                            $maxBatchSize = $valgrind ? 1 : ($shuffle ? 4 : 32);\n                            $averageFilesPerWorker = max(1, (int)ceil($totalFileCount / count($workerProcs)));\n                            $batchSize = min($maxBatchSize, $averageFilesPerWorker);\n                            while (count($files) <= $batchSize && $file = array_pop($test_files)) {\n                                foreach ($fileConflictsWith[$file] as $conflictKey) {\n                                    if (isset($activeConflicts[$conflictKey])) {\n                                        $waitingTests[$conflictKey][] = $file;\n                                        continue 2;\n                                    }\n                                }\n                                $files[] = $file;\n                            }\n                            if ($files) {\n                                foreach ($files as $file) {\n                                    foreach ($fileConflictsWith[$file] as $conflictKey) {\n                                        $activeConflicts[$conflictKey] = $i;\n                                    }\n                                }\n                                $testsInProgress++;\n                                send_message($workerSocks[$i], [\n                                    \"type\" => \"run_tests\",\n                                    \"test_files\" => $files,\n                                    \"env\" => $env,\n                                    \"redir_tested\" => $redir_tested\n                                ]);\n                            } else {\n                                proc_terminate($workerProcs[$i]);\n                                unset($workerProcs[$i]);\n                                unset($workerSocks[$i]);\n                                goto escape;\n                            }\n                            break;\n                        case \"begin\":\n                            if (!$SHOW_ONLY_GROUPS) {\n                                show_test($test_idx, get_shortname($message['file']));\n                            }\n                            break;\n                        case \"test_result\":\n                            list($name, $index, $result, $resultText) = [$message[\"name\"], $message[\"index\"], $message[\"result\"], $message[\"text\"]];\n                            foreach ($message[\"PHP_FAILED_TESTS\"] as $category => $tests) {\n                                $PHP_FAILED_TESTS[$category] = array_merge($PHP_FAILED_TESTS[$category], $tests);\n                            }\n                            $test_idx++;\n\n                            if (!$SHOW_ONLY_GROUPS) {\n                                clear_show_test();\n                            }\n\n                            echo $resultText;\n\n                            if (!is_array($name) && $result != 'REDIR') {\n                                $test_results[$index] = $result;\n\n                                if ($failed_tests_file && ($result == 'XFAILED' || $result == 'XLEAKED' || $result == 'FAILED' || $result == 'WARNED' || $result == 'LEAKED')) {\n                                    fwrite($failed_tests_file, \"$index\\n\");\n                                }\n                                if ($result_tests_file) {\n                                    fwrite($result_tests_file, \"$result\\t$index\\n\");\n                                }\n                            }\n                            break;\n                        case \"error\":\n                            kill_children($workerProcs);\n                            error(\"Worker $i reported error: $message[msg]\");\n                            break;\n                        case \"php_error\":\n                            kill_children($workerProcs);\n                            $error_consts = [\n                                'E_ERROR',\n                                'E_WARNING',\n                                'E_PARSE',\n                                'E_NOTICE',\n                                'E_CORE_ERROR',\n                                'E_CORE_WARNING',\n                                'E_COMPILE_ERROR',\n                                'E_COMPILE_WARNING',\n                                'E_USER_ERROR',\n                                'E_USER_WARNING',\n                                'E_USER_NOTICE',\n                                'E_STRICT', // TODO Cleanup when removed from Zend Engine.\n                                'E_RECOVERABLE_ERROR',\n                                'E_USER_DEPRECATED'\n                            ];\n                            $error_consts = array_combine(array_map('constant', $error_consts), $error_consts);\n                            error(\"Worker $i reported unexpected {$error_consts[$message['errno']]}: $message[errstr] in $message[errfile] on line $message[errline]\");\n                        default:\n                            kill_children($workerProcs);\n                            error(\"Unrecognised message type '$message[type]' from worker $i\");\n                    }\n                }\n            }\n        }\n    }\n\n    if (!$SHOW_ONLY_GROUPS) {\n        clear_show_test();\n    }\n\n    kill_children($workerProcs);\n\n    if ($testsInProgress < 0) {\n        error(\"$testsInProgress test batches “in progress”, which is less than zero. THIS SHOULD NOT HAPPEN.\");\n    }\n}\n\nfunction send_message($stream, array $message) {\n    $blocking = stream_get_meta_data($stream)[\"blocked\"];\n    stream_set_blocking($stream, true);\n    fwrite($stream, base64_encode(serialize($message)) . \"\\n\");\n    stream_set_blocking($stream, $blocking);\n}\n\nfunction kill_children(array $children) {\n    foreach ($children as $child) {\n        if ($child) {\n            proc_terminate($child);\n        }\n    }\n}\n\nfunction run_worker() {\n    global $workerID, $workerSock;\n\n    $sockUri = getenv(\"TEST_PHP_URI\");\n\n    $workerSock = stream_socket_client($sockUri, $_, $_, 5) or error(\"Couldn't connect to $sockUri\");\n\n    $greeting = fgets($workerSock);\n    $greeting = unserialize(base64_decode($greeting)) or die(\"Could not decode greeting\\n\");\n    if ($greeting[\"type\"] !== \"hello\" || $greeting[\"workerID\"] !== $workerID) {\n        error(\"Unexpected greeting of type $greeting[type] and for worker $greeting[workerID]\");\n    }\n\n    set_error_handler(function ($errno, $errstr, $errfile, $errline) use ($workerSock) {\n        if (error_reporting() & $errno) {\n            send_message($workerSock, compact('errno', 'errstr', 'errfile', 'errline') + [\n                    'type' => 'php_error'\n                ]);\n        }\n\n        return true;\n    });\n\n    foreach ($greeting[\"GLOBALS\"] as $var => $value) {\n        if ($var !== \"workerID\" && $var !== \"workerSock\" && $var !== \"GLOBALS\") {\n            $GLOBALS[$var] = $value;\n        }\n    }\n    foreach ($greeting[\"constants\"] as $const => $value) {\n        define($const, $value);\n    }\n\n    send_message($workerSock, [\n        \"type\" => \"hello_reply\",\n        \"workerID\" => $workerID\n    ]);\n\n    send_message($workerSock, [\n        \"type\" => \"ready\"\n    ]);\n\n    while (($command = fgets($workerSock))) {\n        $command = unserialize(base64_decode($command));\n\n        switch ($command[\"type\"]) {\n            case \"run_tests\":\n                run_all_tests($command[\"test_files\"], $command[\"env\"], $command[\"redir_tested\"]);\n                send_message($workerSock, [\n                    \"type\" => \"tests_finished\",\n                    \"junit\" => junit_enabled() ? $GLOBALS['JUNIT'] : null,\n                ]);\n                junit_init();\n                break;\n            default:\n                send_message($workerSock, [\n                    \"type\" => \"error\",\n                    \"msg\" => \"Unrecognised message type: $command[type]\"\n                ]);\n                break 2;\n        }\n    }\n}\n\n//\n//  Show file or result block\n//\nfunction show_file_block($file, $block, $section = null)\n{\n    global $cfg;\n\n    if ($cfg['show'][$file]) {\n\n        if (is_null($section)) {\n            $section = strtoupper($file);\n        }\n\n        static $max_length = 1024;\n        if (strlen($block) > $max_length) {\n            $block = substr($block, 0, $max_length) . '...';\n        }\n\n        echo \"\\n========\" . $section . \"========\\n\";\n        echo rtrim($block);\n        echo \"\\n========DONE========\\n\";\n    }\n}\n\n//\n//  Run an individual test case.\n//\nfunction run_test($php, $file, $env)\n{\n    global $log_format, $ini_overwrites, $PHP_FAILED_TESTS;\n    global $pass_options, $DETAILED, $IN_REDIRECT, $test_cnt, $test_idx;\n    global $valgrind, $temp_source, $temp_target, $cfg, $environment;\n    global $no_clean;\n    global $SHOW_ONLY_GROUPS;\n    global $no_file_cache;\n    global $slow_min_ms;\n    global $preload;\n    // Parallel testing\n    global $workerID;\n    $temp_filenames = null;\n    $org_file = $file;\n\n    if (isset($env['TEST_PHP_CGI_EXECUTABLE'])) {\n        $php_cgi = $env['TEST_PHP_CGI_EXECUTABLE'];\n    }\n\n    if (isset($env['TEST_PHPDBG_EXECUTABLE'])) {\n        $phpdbg = $env['TEST_PHPDBG_EXECUTABLE'];\n    }\n\n    if (is_array($file)) {\n        $file = $file[0];\n    }\n\n    if ($DETAILED) echo \"\n=================\nTEST $file\n\";\n\n    // Load the sections of the test file.\n    $section_text = array('TEST' => '');\n\n    $fp = fopen($file, \"rb\") or error(\"Cannot open test file: $file\");\n\n    $bork_info = null;\n\n    if (!feof($fp)) {\n        $line = fgets($fp);\n\n        if ($line === false) {\n            $bork_info = \"cannot read test\";\n        }\n    } else {\n        $bork_info = \"empty test [$file]\";\n    }\n    if ($bork_info === null && strncmp('--TEST--', $line, 8)) {\n        $bork_info = \"tests must start with --TEST-- [$file]\";\n    }\n\n    $section = 'TEST';\n    $secfile = false;\n    $secdone = false;\n\n    while (!feof($fp)) {\n        $line = fgets($fp);\n\n        if ($line === false) {\n            break;\n        }\n\n        // Match the beginning of a section.\n        if (preg_match('/^--([_A-Z0-9]+)--/', $line, $r)) {\n            $section = (string)$r[1];\n\n            if (isset($section_text[$section]) && $section_text[$section]) {\n                $bork_info = \"duplicated $section section\";\n            }\n\n            // check for unknown sections\n            if (!in_array($section, array(\n                'EXPECT', 'EXPECTF', 'EXPECTREGEX', 'EXPECTREGEX_EXTERNAL', 'EXPECT_EXTERNAL', 'EXPECTF_EXTERNAL', 'EXPECTHEADERS',\n                'EXPECT_85', 'EXPECTF_85', 'EXPECTREGEX_85', // PHP 8.5+\n                'POST', 'POST_RAW', 'GZIP_POST', 'DEFLATE_POST', 'PUT', 'GET', 'COOKIE', 'ARGS',\n                'FILE', 'FILEEOF', 'FILE_EXTERNAL', 'REDIRECTTEST',\n                'CAPTURE_STDIO', 'STDIN', 'CGI', 'PHPDBG',\n                'INI', 'ENV', 'EXTENSIONS',\n                'SKIPIF', 'XFAIL', 'XLEAK', 'CLEAN',\n                'CREDITS', 'DESCRIPTION', 'CONFLICTS', 'WHITESPACE_SENSITIVE',\n            ))) {\n                $bork_info = 'Unknown section \"' . $section . '\"';\n            }\n\n            $section_text[$section] = '';\n            $secfile = $section == 'FILE' || $section == 'FILEEOF' || $section == 'FILE_EXTERNAL';\n            $secdone = false;\n            continue;\n        }\n\n        // Add to the section text.\n        if (!$secdone) {\n            $section_text[$section] .= $line;\n        }\n\n        // End of actual test?\n        if ($secfile && preg_match('/^===DONE===\\s*$/', $line)) {\n            $secdone = true;\n        }\n    }\n\n    // the redirect section allows a set of tests to be reused outside\n    // a given test directory.\n    if ($bork_info === null) {\n        if (isset($section_text['REDIRECTTEST'])) {\n\n            if ($IN_REDIRECT) {\n                $bork_info = \"Can't redirect a test from within a redirected test\";\n            }\n\n        } else {\n\n            if (!isset($section_text['PHPDBG']) && isset($section_text['FILE']) + isset($section_text['FILEEOF']) + isset($section_text['FILE_EXTERNAL']) != 1) {\n                $bork_info = \"missing section --FILE--\";\n            }\n\n            if (isset($section_text['FILEEOF'])) {\n                $section_text['FILE'] = preg_replace(\"/[\\r\\n]+$/\", '', $section_text['FILEEOF']);\n                unset($section_text['FILEEOF']);\n            }\n\n            if (PHP_VERSION_ID >= 80500) {\n                if (isset($section_text['EXPECT_85'])) {\n                    $section_text['EXPECT'] = $section_text['EXPECT_85'];\n                    unset($section_text['EXPECT_85']);\n                }\n                if (isset($section_text['EXPECTF_85'])) {\n                    $section_text['EXPECTF'] = $section_text['EXPECTF_85'];\n                    unset($section_text['EXPECTF_85']);\n                }\n                if (isset($section_text['EXPECTREGEX_85'])) {\n                    $section_text['EXPECTREGEX'] = $section_text['EXPECTREGEX_85'];\n                    unset($section_text['EXPECTREGEX_85']);\n                }\n            }\n\n            foreach (array('FILE', 'EXPECT', 'EXPECTF', 'EXPECTREGEX') as $prefix) {\n                $key = $prefix . '_EXTERNAL';\n\n                if (isset($section_text[$key])) {\n                    // don't allow tests to retrieve files from anywhere but this subdirectory\n                    $section_text[$key] = dirname($file) . '/' . trim(str_replace('..', '', $section_text[$key]));\n\n                    if (file_exists($section_text[$key])) {\n                        $section_text[$prefix] = file_get_contents($section_text[$key]);\n                        unset($section_text[$key]);\n                    } else {\n                        $bork_info = \"could not load --\" . $key . \"-- \" . dirname($file) . '/' . trim($section_text[$key]);\n                    }\n                }\n            }\n\n            if ((isset($section_text['EXPECT']) + isset($section_text['EXPECTF']) + isset($section_text['EXPECTREGEX'])) != 1) {\n                $bork_info = \"missing section --EXPECT--, --EXPECTF-- or --EXPECTREGEX--\";\n            }\n        }\n    }\n    fclose($fp);\n\n    $shortname = get_shortname($file);\n    $tested_file = $shortname;\n\n    if ($bork_info !== null) {\n        show_result(\"BORK\", $bork_info, $tested_file);\n        $PHP_FAILED_TESTS['BORKED'][] = array(\n            'name' => $file,\n            'test_name' => '',\n            'output' => '',\n            'diff' => '',\n            'info' => \"$bork_info [$file]\",\n        );\n\n        junit_mark_test_as('BORK', $shortname, $tested_file, 0, $bork_info);\n        return 'BORKED';\n    }\n\n    if (isset($section_text['CAPTURE_STDIO'])) {\n        $captureStdIn = stripos($section_text['CAPTURE_STDIO'], 'STDIN') !== false;\n        $captureStdOut = stripos($section_text['CAPTURE_STDIO'], 'STDOUT') !== false;\n        $captureStdErr = stripos($section_text['CAPTURE_STDIO'], 'STDERR') !== false;\n    } else {\n        $captureStdIn = true;\n        $captureStdOut = true;\n        $captureStdErr = true;\n    }\n    if ($captureStdOut && $captureStdErr) {\n        $cmdRedirect = ' 2>&1';\n    } else {\n        $cmdRedirect = '';\n    }\n\n    $tested = trim($section_text['TEST']);\n\n    /* For GET/POST/PUT tests, check if cgi sapi is available and if it is, use it. */\n    if (array_key_exists('CGI', $section_text) || !empty($section_text['GET']) || !empty($section_text['POST']) || !empty($section_text['GZIP_POST']) || !empty($section_text['DEFLATE_POST']) || !empty($section_text['POST_RAW']) || !empty($section_text['PUT']) || !empty($section_text['COOKIE']) || !empty($section_text['EXPECTHEADERS'])) {\n        if (isset($php_cgi)) {\n            $php = $php_cgi . ' -C ';\n        } else if (IS_WINDOWS && file_exists(dirname($php) . \"/php-cgi.exe\")) {\n            $php = realpath(dirname($php) . \"/php-cgi.exe\") . ' -C ';\n        } else {\n            if (file_exists(dirname($php) . \"/../../sapi/cgi/php-cgi\")) {\n                $php = realpath(dirname($php) . \"/../../sapi/cgi/php-cgi\") . ' -C ';\n            } else if (file_exists(\"./sapi/cgi/php-cgi\")) {\n                $php = realpath(\"./sapi/cgi/php-cgi\") . ' -C ';\n            } else if (file_exists(dirname($php) . \"/php-cgi\")) {\n                $php = realpath(dirname($php) . \"/php-cgi\") . ' -C ';\n            } else {\n                show_result('SKIP', $tested, $tested_file, \"reason: CGI not available\");\n\n                junit_init_suite(junit_get_suitename_for($shortname));\n                junit_mark_test_as('SKIP', $shortname, $tested, 0, 'CGI not available');\n                return 'SKIPPED';\n            }\n        }\n        $uses_cgi = true;\n    }\n\n    /* For phpdbg tests, check if phpdbg sapi is available and if it is, use it. */\n    $extra_options = '';\n    if (array_key_exists('PHPDBG', $section_text)) {\n        if (!isset($section_text['STDIN'])) {\n            $section_text['STDIN'] = $section_text['PHPDBG'] . \"\\n\";\n        }\n\n        if (isset($phpdbg)) {\n            $php = $phpdbg . ' -qIb';\n\n            // Additional phpdbg command line options for sections that need to\n            // be run straight away. For example, EXTENSIONS, SKIPIF, CLEAN.\n            $extra_options = '-rr';\n        } else {\n            show_result('SKIP', $tested, $tested_file, \"reason: phpdbg not available\");\n\n            junit_init_suite(junit_get_suitename_for($shortname));\n            junit_mark_test_as('SKIP', $shortname, $tested, 0, 'phpdbg not available');\n            return 'SKIPPED';\n        }\n    }\n\n    if (!$SHOW_ONLY_GROUPS && !$workerID) {\n        show_test($test_idx, $shortname);\n        echo PHP_EOL;\n    }\n\n    if (is_array($IN_REDIRECT)) {\n        $temp_dir = $test_dir = $IN_REDIRECT['dir'];\n    } else {\n        $temp_dir = $test_dir = realpath(dirname($file));\n    }\n\n    if ($temp_source && $temp_target) {\n        $temp_dir = str_replace($temp_source, $temp_target, $temp_dir);\n    }\n\n    $main_file_name = basename($file, 'phpt');\n\n    $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'diff';\n    $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'log';\n    $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'exp';\n    $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'out';\n    $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'mem';\n    $sh_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'sh';\n    $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'php';\n    $test_file = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'php';\n    $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'skip.php';\n    $test_skipif = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'skip.php';\n    $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'clean.php';\n    $test_clean = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'clean.php';\n    $preload_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'preload.php';\n    $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'post';\n    $tmp_relative_file = str_replace(__DIR__ . DIRECTORY_SEPARATOR, '', $test_file) . 't';\n\n    if ($temp_source && $temp_target) {\n        $temp_skipif .= 's';\n        $temp_file .= 's';\n        $temp_clean .= 's';\n        $copy_file = $temp_dir . DIRECTORY_SEPARATOR . basename(is_array($file) ? $file[1] : $file) . '.phps';\n\n        if (!is_dir(dirname($copy_file))) {\n            mkdir(dirname($copy_file), 0777, true) or error(\"Cannot create output directory - \" . dirname($copy_file));\n        }\n\n        if (isset($section_text['FILE'])) {\n            save_text($copy_file, $section_text['FILE']);\n        }\n\n        $temp_filenames = array(\n            'file' => $copy_file,\n            'diff' => $diff_filename,\n            'log' => $log_filename,\n            'exp' => $exp_filename,\n            'out' => $output_filename,\n            'mem' => $memcheck_filename,\n            'sh' => $sh_filename,\n            'php' => $temp_file,\n            'skip' => $temp_skipif,\n            'clean' => $temp_clean\n        );\n    }\n\n    if (is_array($IN_REDIRECT)) {\n        $tested = $IN_REDIRECT['prefix'] . ' ' . trim($section_text['TEST']);\n        $tested_file = $tmp_relative_file;\n        $shortname = str_replace(TEST_PHP_SRCDIR . '/', '', $tested_file);\n    }\n\n    // unlink old test results\n    @unlink($diff_filename);\n    @unlink($log_filename);\n    @unlink($exp_filename);\n    @unlink($output_filename);\n    @unlink($memcheck_filename);\n    @unlink($sh_filename);\n    @unlink($temp_file);\n    @unlink($test_file);\n    @unlink($temp_skipif);\n    @unlink($test_skipif);\n    @unlink($tmp_post);\n    @unlink($temp_clean);\n    @unlink($test_clean);\n    @unlink($preload_filename);\n\n    // Reset environment from any previous test.\n    $env['REDIRECT_STATUS'] = '';\n    $env['QUERY_STRING'] = '';\n    $env['PATH_TRANSLATED'] = '';\n    $env['SCRIPT_FILENAME'] = '';\n    $env['REQUEST_METHOD'] = '';\n    $env['CONTENT_TYPE'] = '';\n    $env['CONTENT_LENGTH'] = '';\n    $env['TZ'] = '';\n\n    if (!empty($section_text['ENV'])) {\n\n        foreach (explode(\"\\n\", trim($section_text['ENV'])) as $e) {\n            $e = explode('=', trim($e), 2);\n\n            if (!empty($e[0]) && isset($e[1])) {\n                $env[$e[0]] = $e[1];\n            }\n        }\n    }\n\n    // Default ini settings\n    $ini_settings = $workerID ? array('opcache.cache_id' => \"worker$workerID\") : array();\n\n    // Additional required extensions\n    if (array_key_exists('EXTENSIONS', $section_text)) {\n        $ext_params = array();\n        settings2array($ini_overwrites, $ext_params);\n        $ext_params = settings2params($ext_params);\n        $ext_dir = shell_exec(\"$php $pass_options $extra_options $ext_params -d display_errors=0 -r \\\"echo ini_get('extension_dir');\\\"\");\n        $extensions = preg_split(\"/[\\n\\r]+/\", trim($section_text['EXTENSIONS']));\n        $loaded = explode(\",\", shell_exec(\"$php $pass_options $extra_options $ext_params -d display_errors=0 -r \\\"echo implode(',', get_loaded_extensions());\\\"\"));\n        $ext_prefix = IS_WINDOWS ? \"php_\" : \"\";\n        foreach ($extensions as $req_ext) {\n            if (!in_array($req_ext, $loaded)) {\n                if ($req_ext == 'opcache') {\n                    $ini_settings['zend_extension'][] = $ext_dir . DIRECTORY_SEPARATOR . $ext_prefix . $req_ext . '.' . PHP_SHLIB_SUFFIX;\n                } else {\n                    $ini_settings['extension'][] = $ext_dir . DIRECTORY_SEPARATOR . $ext_prefix . $req_ext . '.' . PHP_SHLIB_SUFFIX;\n                }\n            }\n        }\n    }\n\n    // additional ini overwrites\n    //$ini_overwrites[] = 'setting=value';\n    settings2array($ini_overwrites, $ini_settings);\n\n    $orig_ini_settings = settings2params($ini_settings);\n\n    // Any special ini settings\n    // these may overwrite the test defaults...\n    if (array_key_exists('INI', $section_text)) {\n        $section_text['INI'] = str_replace('{PWD}', dirname($file), $section_text['INI']);\n        $section_text['INI'] = str_replace('{TMP}', sys_get_temp_dir(), $section_text['INI']);\n        $replacement = IS_WINDOWS ? '\"' . PHP_BINARY . ' -r \\\"while ($in = fgets(STDIN)) echo $in;\\\" > $1\"' : 'tee $1 >/dev/null';\n        $section_text['INI'] = preg_replace('/{MAIL:(\\S+)}/', $replacement, $section_text['INI']);\n        settings2array(preg_split(\"/[\\n\\r]+/\", $section_text['INI']), $ini_settings);\n    }\n\n    $ini_settings = settings2params($ini_settings);\n\n    $env['TEST_PHP_EXTRA_ARGS'] = $pass_options . ' ' . $ini_settings;\n\n    // Check if test should be skipped.\n    $info = '';\n    $warn = false;\n\n    if (array_key_exists('SKIPIF', $section_text)) {\n\n        if (trim($section_text['SKIPIF'])) {\n            show_file_block('skip', $section_text['SKIPIF']);\n            save_text($test_skipif, $section_text['SKIPIF'], $temp_skipif);\n            $extra = !IS_WINDOWS ?\n                \"unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;\" : \"\";\n\n            if ($valgrind) {\n                $env['USE_ZEND_ALLOC'] = '0';\n                $env['ZEND_DONT_UNLOAD_MODULES'] = 1;\n            }\n\n            junit_start_timer($shortname);\n\n            $output = system_with_timeout(\"$extra $php $pass_options $extra_options -q $orig_ini_settings $no_file_cache -d display_errors=1 -d display_startup_errors=0 \\\"$test_skipif\\\"\", $env);\n            $output = trim($output);\n\n            junit_finish_timer($shortname);\n\n            if (!$cfg['keep']['skip']) {\n                @unlink($test_skipif);\n            }\n\n            if (!strncasecmp('skip', $output, 4)) {\n\n                if (preg_match('/^skip\\s*(.+)/i', $output, $m)) {\n                    show_result('SKIP', $tested, $tested_file, \"reason: $m[1]\", $temp_filenames);\n                } else {\n                    show_result('SKIP', $tested, $tested_file, '', $temp_filenames);\n                }\n\n                if (!$cfg['keep']['skip']) {\n                    @unlink($test_skipif);\n                }\n\n                $message = !empty($m[1]) ? $m[1] : '';\n                junit_mark_test_as('SKIP', $shortname, $tested, null, $message);\n                return 'SKIPPED';\n            }\n\n            if (!strncasecmp('info', $output, 4) && preg_match('/^info\\s*(.+)/i', $output, $m)) {\n                $info = \" (info: $m[1])\";\n            } elseif (!strncasecmp('warn', $output, 4) && preg_match('/^warn\\s+(.+)/i', $output, $m)) {\n                $warn = true; /* only if there is a reason */\n                $info = \" (warn: $m[1])\";\n            } elseif (!strncasecmp('xfail', $output, 5)) {\n                // Pretend we have an XFAIL section\n                $section_text['XFAIL'] = ltrim(substr($output, 5));\n            } elseif ($output !== '') {\n                show_result(\"BORK\", $output, $tested_file, 'reason: invalid output from SKIPIF', $temp_filenames);\n                $PHP_FAILED_TESTS['BORKED'][] = array(\n                    'name' => $file,\n                    'test_name' => '',\n                    'output' => '',\n                    'diff' => '',\n                    'info' => \"$output [$file]\",\n                );\n\n                junit_mark_test_as('BORK', $shortname, $tested, null, $output);\n                return 'BORKED';\n            }\n        }\n    }\n\n    if (!extension_loaded(\"zlib\")\n        && (array_key_exists(\"GZIP_POST\", $section_text)\n            || array_key_exists(\"DEFLATE_POST\", $section_text))) {\n        $message = \"ext/zlib required\";\n        show_result('SKIP', $tested, $tested_file, \"reason: $message\", $temp_filenames);\n        junit_mark_test_as('SKIP', $shortname, $tested, null, $message);\n        return 'SKIPPED';\n    }\n\n    if (isset($section_text['REDIRECTTEST'])) {\n        $test_files = array();\n\n        $IN_REDIRECT = eval($section_text['REDIRECTTEST']);\n        $IN_REDIRECT['via'] = \"via [$shortname]\\n\\t\";\n        $IN_REDIRECT['dir'] = realpath(dirname($file));\n        $IN_REDIRECT['prefix'] = trim($section_text['TEST']);\n\n        if (!empty($IN_REDIRECT['TESTS'])) {\n\n            if (is_array($org_file)) {\n                $test_files[] = $org_file[1];\n            } else {\n                $GLOBALS['test_files'] = $test_files;\n                find_files($IN_REDIRECT['TESTS']);\n\n                foreach ($GLOBALS['test_files'] as $f) {\n                    $test_files[] = array($f, $file);\n                }\n            }\n            $test_cnt += count($test_files) - 1;\n            $test_idx--;\n\n            show_redirect_start($IN_REDIRECT['TESTS'], $tested, $tested_file);\n\n            // set up environment\n            $redirenv = array_merge($environment, $IN_REDIRECT['ENV']);\n            $redirenv['REDIR_TEST_DIR'] = realpath($IN_REDIRECT['TESTS']) . DIRECTORY_SEPARATOR;\n\n            usort($test_files, \"test_sort\");\n            run_all_tests($test_files, $redirenv, $tested);\n\n            show_redirect_ends($IN_REDIRECT['TESTS'], $tested, $tested_file);\n\n            // a redirected test never fails\n            $IN_REDIRECT = false;\n\n            junit_mark_test_as('PASS', $shortname, $tested);\n            return 'REDIR';\n\n        } else {\n\n            $bork_info = \"Redirect info must contain exactly one TEST string to be used as redirect directory.\";\n            show_result(\"BORK\", $bork_info, '', '', $temp_filenames);\n            $PHP_FAILED_TESTS['BORKED'][] = array(\n                'name' => $file,\n                'test_name' => '',\n                'output' => '',\n                'diff' => '',\n                'info' => \"$bork_info [$file]\",\n            );\n        }\n    }\n\n    if (is_array($org_file) || isset($section_text['REDIRECTTEST'])) {\n\n        if (is_array($org_file)) {\n            $file = $org_file[0];\n        }\n\n        $bork_info = \"Redirected test did not contain redirection info\";\n        show_result(\"BORK\", $bork_info, '', '', $temp_filenames);\n        $PHP_FAILED_TESTS['BORKED'][] = array(\n            'name' => $file,\n            'test_name' => '',\n            'output' => '',\n            'diff' => '',\n            'info' => \"$bork_info [$file]\",\n        );\n\n        junit_mark_test_as('BORK', $shortname, $tested, null, $bork_info);\n\n        return 'BORKED';\n    }\n\n    // We've satisfied the preconditions - run the test!\n    if (isset($section_text['FILE'])) {\n        show_file_block('php', $section_text['FILE'], 'TEST');\n        save_text($test_file, $section_text['FILE'], $temp_file);\n    } else {\n        $test_file = $temp_file = \"\";\n    }\n\n    if (array_key_exists('GET', $section_text)) {\n        $query_string = trim($section_text['GET']);\n    } else {\n        $query_string = '';\n    }\n\n    $env['REDIRECT_STATUS'] = '1';\n    if (empty($env['QUERY_STRING'])) {\n        $env['QUERY_STRING'] = $query_string;\n    }\n    if (empty($env['PATH_TRANSLATED'])) {\n        $env['PATH_TRANSLATED'] = $test_file;\n    }\n    if (empty($env['SCRIPT_FILENAME'])) {\n        $env['SCRIPT_FILENAME'] = $test_file;\n    }\n\n    if (array_key_exists('COOKIE', $section_text)) {\n        $env['HTTP_COOKIE'] = trim($section_text['COOKIE']);\n    } else {\n        $env['HTTP_COOKIE'] = '';\n    }\n\n    $args = isset($section_text['ARGS']) ? ' -- ' . $section_text['ARGS'] : '';\n\n    if ($preload && !empty($test_file)) {\n        save_text($preload_filename, \"<?php opcache_compile_file('$test_file');\");\n        $local_pass_options = $pass_options;\n        unset($pass_options);\n        $pass_options = $local_pass_options;\n        $pass_options .= \" -d opcache.preload=\" . $preload_filename;\n    }\n\n    if (array_key_exists('POST_RAW', $section_text) && !empty($section_text['POST_RAW'])) {\n\n        $post = trim($section_text['POST_RAW']);\n        $raw_lines = explode(\"\\n\", $post);\n\n        $request = '';\n        $started = false;\n\n        foreach ($raw_lines as $line) {\n\n            if (empty($env['CONTENT_TYPE']) && preg_match('/^Content-Type:(.*)/i', $line, $res)) {\n                $env['CONTENT_TYPE'] = trim(str_replace(\"\\r\", '', $res[1]));\n                continue;\n            }\n\n            if ($started) {\n                $request .= \"\\n\";\n            }\n\n            $started = true;\n            $request .= $line;\n        }\n\n        $env['CONTENT_LENGTH'] = strlen($request);\n        $env['REQUEST_METHOD'] = 'POST';\n\n        if (empty($request)) {\n            junit_mark_test_as('BORK', $shortname, $tested, null, 'empty $request');\n            return 'BORKED';\n        }\n\n        save_text($tmp_post, $request);\n        $cmd = \"$php $pass_options $ini_settings -f \\\"$test_file\\\"$cmdRedirect < \\\"$tmp_post\\\"\";\n\n    } elseif (array_key_exists('PUT', $section_text) && !empty($section_text['PUT'])) {\n\n        $post = trim($section_text['PUT']);\n        $raw_lines = explode(\"\\n\", $post);\n\n        $request = '';\n        $started = false;\n\n        foreach ($raw_lines as $line) {\n\n            if (empty($env['CONTENT_TYPE']) && preg_match('/^Content-Type:(.*)/i', $line, $res)) {\n                $env['CONTENT_TYPE'] = trim(str_replace(\"\\r\", '', $res[1]));\n                continue;\n            }\n\n            if ($started) {\n                $request .= \"\\n\";\n            }\n\n            $started = true;\n            $request .= $line;\n        }\n\n        $env['CONTENT_LENGTH'] = strlen($request);\n        $env['REQUEST_METHOD'] = 'PUT';\n\n        if (empty($request)) {\n            junit_mark_test_as('BORK', $shortname, $tested, null, 'empty $request');\n            return 'BORKED';\n        }\n\n        save_text($tmp_post, $request);\n        $cmd = \"$php $pass_options $ini_settings -f \\\"$test_file\\\"$cmdRedirect < \\\"$tmp_post\\\"\";\n\n    } else if (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) {\n\n        $post = trim($section_text['POST']);\n        $content_length = strlen($post);\n        save_text($tmp_post, $post);\n\n        $env['REQUEST_METHOD'] = 'POST';\n        if (empty($env['CONTENT_TYPE'])) {\n            $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';\n        }\n\n        if (empty($env['CONTENT_LENGTH'])) {\n            $env['CONTENT_LENGTH'] = $content_length;\n        }\n\n        $cmd = \"$php $pass_options $ini_settings -f \\\"$test_file\\\"$cmdRedirect < \\\"$tmp_post\\\"\";\n\n    } else if (array_key_exists('GZIP_POST', $section_text) && !empty($section_text['GZIP_POST'])) {\n\n        $post = trim($section_text['GZIP_POST']);\n        $post = gzencode($post, 9, FORCE_GZIP);\n        $env['HTTP_CONTENT_ENCODING'] = 'gzip';\n\n        save_text($tmp_post, $post);\n        $content_length = strlen($post);\n\n        $env['REQUEST_METHOD'] = 'POST';\n        $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';\n        $env['CONTENT_LENGTH'] = $content_length;\n\n        $cmd = \"$php $pass_options $ini_settings -f \\\"$test_file\\\"$cmdRedirect < \\\"$tmp_post\\\"\";\n\n    } else if (array_key_exists('DEFLATE_POST', $section_text) && !empty($section_text['DEFLATE_POST'])) {\n        $post = trim($section_text['DEFLATE_POST']);\n        $post = gzcompress($post, 9);\n        $env['HTTP_CONTENT_ENCODING'] = 'deflate';\n        save_text($tmp_post, $post);\n        $content_length = strlen($post);\n\n        $env['REQUEST_METHOD'] = 'POST';\n        $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';\n        $env['CONTENT_LENGTH'] = $content_length;\n\n        $cmd = \"$php $pass_options $ini_settings -f \\\"$test_file\\\"$cmdRedirect < \\\"$tmp_post\\\"\";\n\n    } else {\n\n        $env['REQUEST_METHOD'] = 'GET';\n        $env['CONTENT_TYPE'] = '';\n        $env['CONTENT_LENGTH'] = '';\n\n        $cmd = \"$php $pass_options $ini_settings -f \\\"$test_file\\\" $args$cmdRedirect\";\n    }\n\n    if ($valgrind) {\n        $env['USE_ZEND_ALLOC'] = '0';\n        $env['ZEND_DONT_UNLOAD_MODULES'] = 1;\n\n        $cmd = $valgrind->wrapCommand($cmd, $memcheck_filename, str_contains($test_file, \"pcre\"));\n    }\n\n    if ($DETAILED) echo \"\nCONTENT_LENGTH  = \" . $env['CONTENT_LENGTH'] . \"\nCONTENT_TYPE    = \" . $env['CONTENT_TYPE'] . \"\nPATH_TRANSLATED = \" . $env['PATH_TRANSLATED'] . \"\nQUERY_STRING    = \" . $env['QUERY_STRING'] . \"\nREDIRECT_STATUS = \" . $env['REDIRECT_STATUS'] . \"\nREQUEST_METHOD  = \" . $env['REQUEST_METHOD'] . \"\nSCRIPT_FILENAME = \" . $env['SCRIPT_FILENAME'] . \"\nHTTP_COOKIE     = \" . $env['HTTP_COOKIE'] . \"\nCOMMAND $cmd\n\";\n\n    junit_start_timer($shortname);\n    $hrtime = hrtime();\n    $startTime = $hrtime[0] * 1000000000 + $hrtime[1];\n\n    $out = system_with_timeout($cmd, $env, $section_text['STDIN'] ?? null, $captureStdIn, $captureStdOut, $captureStdErr);\n\n    junit_finish_timer($shortname);\n    $hrtime = hrtime();\n    $time = $hrtime[0] * 1000000000 + $hrtime[1] - $startTime;\n    if ($time >= $slow_min_ms * 1000000) {\n        $PHP_FAILED_TESTS['SLOW'][] = array(\n            'name' => $file,\n            'test_name' => (is_array($IN_REDIRECT) ? $IN_REDIRECT['via'] : '') . $tested . \" [$tested_file]\",\n            'output' => '',\n            'diff' => '',\n            'info' => $time / 1000000000,\n        );\n    }\n\n    if (array_key_exists('CLEAN', $section_text) && (!$no_clean || $cfg['keep']['clean'])) {\n\n        if (trim($section_text['CLEAN'])) {\n            show_file_block('clean', $section_text['CLEAN']);\n            save_text($test_clean, trim($section_text['CLEAN']), $temp_clean);\n\n            if (!$no_clean) {\n                $clean_params = array();\n                settings2array($ini_overwrites, $clean_params);\n                $clean_params = settings2params($clean_params);\n                $extra = !IS_WINDOWS ?\n                    \"unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;\" : \"\";\n                system_with_timeout(\"$extra $php $pass_options $extra_options -q $clean_params $no_file_cache \\\"$test_clean\\\"\", $env);\n            }\n\n            if (!$cfg['keep']['clean']) {\n                @unlink($test_clean);\n            }\n        }\n    }\n\n    @unlink($preload_filename);\n\n    $leaked = false;\n    $passed = false;\n\n    if ($valgrind) { // leak check\n        // swoole patch: leak log\n        $leaked = file_get_contents($memcheck_filename);\n        $leaked = preg_replace('/==\\d+== Conditional jump[\\s\\S]+?==\\d+== (?:\\n|$)/', '', $leaked);\n        if (empty(preg_replace('/\\s/', '', $leaked))) {\n            $leaked = false;\n            @unlink($memcheck_filename);\n        }\n    }\n\n    // Does the output match what is expected?\n    $output = preg_replace(\"/\\r\\n/\", \"\\n\", trim($out));\n\n    /* when using CGI, strip the headers from the output */\n    $headers = array();\n\n    if (!empty($uses_cgi) && preg_match(\"/^(.*?)\\r?\\n\\r?\\n(.*)/s\", $out, $match)) {\n        $output = trim($match[2]);\n        $rh = preg_split(\"/[\\n\\r]+/\", $match[1]);\n\n        foreach ($rh as $line) {\n            if (str_contains($line, ':')) {\n                $line = explode(':', $line, 2);\n                $headers[trim($line[0])] = trim($line[1]);\n            }\n        }\n    }\n\n    $failed_headers = false;\n\n    if (isset($section_text['EXPECTHEADERS'])) {\n        $want = array();\n        $wanted_headers = array();\n        $lines = preg_split(\"/[\\n\\r]+/\", $section_text['EXPECTHEADERS']);\n\n        foreach ($lines as $line) {\n            if (str_contains($line, ':')) {\n                $line = explode(':', $line, 2);\n                $want[trim($line[0])] = trim($line[1]);\n                $wanted_headers[] = trim($line[0]) . ': ' . trim($line[1]);\n            }\n        }\n\n        $output_headers = array();\n\n        foreach ($want as $k => $v) {\n\n            if (isset($headers[$k])) {\n                $output_headers[] = $k . ': ' . $headers[$k];\n            }\n\n            if (!isset($headers[$k]) || $headers[$k] != $v) {\n                $failed_headers = true;\n            }\n        }\n\n        ksort($wanted_headers);\n        $wanted_headers = implode(\"\\n\", $wanted_headers);\n        ksort($output_headers);\n        $output_headers = implode(\"\\n\", $output_headers);\n    }\n\n    show_file_block('out', $output);\n\n    if ($preload) {\n        $output = trim(preg_replace(\"/\\n?Warning: Can't preload [^\\n]*\\n?/\", \"\", $output));\n    }\n\n    if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) {\n\n        if (isset($section_text['EXPECTF'])) {\n            $wanted = trim($section_text['EXPECTF']);\n        } else {\n            $wanted = trim($section_text['EXPECTREGEX']);\n        }\n\n        show_file_block('exp', $wanted);\n        $wanted_re = preg_replace('/\\r\\n/', \"\\n\", $wanted);\n\n        if (isset($section_text['EXPECTF'])) {\n\n            // do preg_quote, but miss out any %r delimited sections\n            $temp = \"\";\n            $r = \"%r\";\n            $startOffset = 0;\n            $length = strlen($wanted_re);\n            while ($startOffset < $length) {\n                $start = strpos($wanted_re, $r, $startOffset);\n                if ($start !== false) {\n                    // we have found a start tag\n                    $end = strpos($wanted_re, $r, $start + 2);\n                    if ($end === false) {\n                        // unbalanced tag, ignore it.\n                        $end = $start = $length;\n                    }\n                } else {\n                    // no more %r sections\n                    $start = $end = $length;\n                }\n                // quote a non re portion of the string\n                $temp .= preg_quote(substr($wanted_re, $startOffset, $start - $startOffset), '/');\n                // add the re unquoted.\n                if ($end > $start) {\n                    $temp .= '(' . substr($wanted_re, $start + 2, $end - $start - 2) . ')';\n                }\n                $startOffset = $end + 2;\n            }\n            $wanted_re = $temp;\n\n            // Stick to basics\n            $wanted_re = str_replace('%e', '\\\\' . DIRECTORY_SEPARATOR, $wanted_re);\n            $wanted_re = str_replace('%s', '[^\\r\\n]+', $wanted_re);\n            $wanted_re = str_replace('%S', '[^\\r\\n]*', $wanted_re);\n            $wanted_re = str_replace('%a', '.+', $wanted_re);\n            $wanted_re = str_replace('%A', '.*', $wanted_re);\n            $wanted_re = str_replace('%w', '\\s*', $wanted_re);\n            $wanted_re = str_replace('%i', '[+-]?\\d+', $wanted_re);\n            $wanted_re = str_replace('%d', '\\d+', $wanted_re);\n            $wanted_re = str_replace('%x', '[0-9a-fA-F]+', $wanted_re);\n            $wanted_re = str_replace('%f', '[+-]?\\.?\\d+\\.?\\d*(?:[Ee][+-]?\\d+)?', $wanted_re);\n            $wanted_re = str_replace('%c', '.', $wanted_re);\n            // %f allows two points \"-.0.0\" but that is the best *simple* expression\n        }\n        /* DEBUG YOUR REGEX HERE\n                var_dump($wanted_re);\n                print(str_repeat('=', 80) . \"\\n\");\n                var_dump($output);\n                 */\n        if (preg_match(\"/^$wanted_re\\$/s\", $output)) {\n            $passed = true;\n            if (!$cfg['keep']['php']) {\n                @unlink($test_file);\n            }\n            @unlink($tmp_post);\n\n            if (!$leaked && !$failed_headers) {\n                if (isset($section_text['XFAIL'])) {\n                    $warn = true;\n                    $info = \" (warn: XFAIL section but test passes)\";\n                } else if (isset($section_text['XLEAK'])) {\n                    $warn = true;\n                    $info = \" (warn: XLEAK section but test passes)\";\n                } else {\n                    show_result(\"PASS\", $tested, $tested_file, '', $temp_filenames);\n                    junit_mark_test_as('PASS', $shortname, $tested);\n                    return 'PASSED';\n                }\n            }\n        }\n\n    } else {\n\n        $wanted = trim($section_text['EXPECT']);\n        $wanted = preg_replace('/\\r\\n/', \"\\n\", $wanted);\n        show_file_block('exp', $wanted);\n\n        // compare and leave on success\n        if (!strcmp($output, $wanted)) {\n            $passed = true;\n\n            if (!$cfg['keep']['php']) {\n                @unlink($test_file);\n            }\n            @unlink($tmp_post);\n\n            if (!$leaked && !$failed_headers) {\n                if (isset($section_text['XFAIL'])) {\n                    $warn = true;\n                    $info = \" (warn: XFAIL section but test passes)\";\n                } elseif (isset($section_text['XLEAK'])) {\n                    $warn = true;\n                    $info = \" (warn: XLEAK section but test passes)\";\n                } else {\n                    show_result(\"PASS\", $tested, $tested_file, '', $temp_filenames);\n                    junit_mark_test_as('PASS', $shortname, $tested);\n                    return 'PASSED';\n                }\n            }\n        }\n\n        $wanted_re = null;\n    }\n\n    // Test failed so we need to report details.\n    if ($failed_headers) {\n        $passed = false;\n        $wanted = $wanted_headers . \"\\n--HEADERS--\\n\" . $wanted;\n        $output = $output_headers . \"\\n--HEADERS--\\n\" . $output;\n\n        if (isset($wanted_re)) {\n            $wanted_re = preg_quote($wanted_headers . \"\\n--HEADERS--\\n\", '/') . $wanted_re;\n        }\n    }\n\n    if ($leaked) {\n        $restype[] = isset($section_text['XLEAK']) ?\n            'XLEAK' : 'LEAK';\n    }\n\n    if ($warn) {\n        $restype[] = 'WARN';\n    }\n\n    if (!$passed) {\n        if (isset($section_text['XFAIL'])) {\n            $restype[] = 'XFAIL';\n            $info = '  XFAIL REASON: ' . rtrim($section_text['XFAIL']);\n        } else if (isset($section_text['XLEAK'])) {\n            $restype[] = 'XLEAK';\n            $info = '  XLEAK REASON: ' . rtrim($section_text['XLEAK']);\n        } else {\n            $restype[] = 'FAIL';\n        }\n    }\n\n    if (!$passed) {\n\n        // write .exp\n        if (str_contains($log_format, 'E') && file_put_contents($exp_filename, $wanted) === false) {\n            error(\"Cannot create expected test output - $exp_filename\");\n        }\n\n        // write .out\n        if (str_contains($log_format, 'O') && file_put_contents($output_filename, $output) === false) {\n            error(\"Cannot create test output - $output_filename\");\n        }\n\n        // write .diff\n        $diff = generate_diff($wanted, $wanted_re, $output);\n        if (is_array($IN_REDIRECT)) {\n            $orig_shortname = str_replace(TEST_PHP_SRCDIR . '/', '', $file);\n            $diff = \"# original source file: $orig_shortname\\n\" . $diff;\n        }\n        show_file_block('diff', $diff);\n\n        // swoole patch: clear port\n        if (preg_match('/bind\\([^:]+?:(?<port>\\d+?)\\)/', $diff, $matches)) {\n            $port = $matches['port'];\n            @shell_exec(\"lsof -i:{$port} | grep LISTEN | awk '{print $2}' | xargs kill -9 > /dev/null 2>&1\");\n        }\n\n        if (str_contains($log_format, 'D') && file_put_contents($diff_filename, $diff) === false) {\n            error(\"Cannot create test diff - $diff_filename\");\n        }\n\n        // write .sh\n        if (str_contains($log_format, 'S') && file_put_contents($sh_filename, \"#!/bin/sh\n\n{$cmd}\n\") === false) {\n            error(\"Cannot create test shell script - $sh_filename\");\n        }\n        chmod($sh_filename, 0755);\n\n        // write .log\n        if (str_contains($log_format, 'L') && file_put_contents($log_filename, \"\n---- EXPECTED OUTPUT\n$wanted\n---- ACTUAL OUTPUT\n$output\n---- FAILED\n\") === false) {\n            error(\"Cannot create test log - $log_filename\");\n            error_report($file, $log_filename, $tested);\n        }\n    }\n\n    if ($valgrind && $leaked && $cfg[\"show\"][\"mem\"]) {\n        show_file_block('mem', file_get_contents($memcheck_filename));\n    }\n\n    show_result(implode('&', $restype), $tested, $tested_file, $info, $temp_filenames);\n\n    foreach ($restype as $type) {\n        $PHP_FAILED_TESTS[$type . 'ED'][] = array(\n            'name' => $file,\n            'test_name' => (is_array($IN_REDIRECT) ? $IN_REDIRECT['via'] : '') . $tested . \" [$tested_file]\",\n            'output' => $output_filename,\n            'diff' => $diff_filename,\n            'info' => $info,\n        );\n    }\n\n    $diff = empty($diff) ? '' : preg_replace('/\\e/', '<esc>', $diff);\n\n    junit_mark_test_as($restype, $shortname, $tested, null, $info, $diff);\n\n    return $restype[0] . 'ED';\n}\n\nfunction comp_line($l1, $l2, $is_reg)\n{\n    if ($is_reg) {\n        return preg_match('/^' . $l1 . '$/s', $l2);\n    } else {\n        return !strcmp($l1, $l2);\n    }\n}\n\nfunction count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $idx2, $cnt1, $cnt2, $steps)\n{\n    $equal = 0;\n\n    while ($idx1 < $cnt1 && $idx2 < $cnt2 && comp_line($ar1[$idx1], $ar2[$idx2], $is_reg)) {\n        $idx1++;\n        $idx2++;\n        $equal++;\n        $steps--;\n    }\n    if (--$steps > 0) {\n        $eq1 = 0;\n        $st = $steps / 2;\n\n        for ($ofs1 = $idx1 + 1; $ofs1 < $cnt1 && $st-- > 0; $ofs1++) {\n            $eq = @count_array_diff($ar1, $ar2, $is_reg, $w, $ofs1, $idx2, $cnt1, $cnt2, $st);\n\n            if ($eq > $eq1) {\n                $eq1 = $eq;\n            }\n        }\n\n        $eq2 = 0;\n        $st = $steps;\n\n        for ($ofs2 = $idx2 + 1; $ofs2 < $cnt2 && $st-- > 0; $ofs2++) {\n            $eq = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $ofs2, $cnt1, $cnt2, $st);\n            if ($eq > $eq2) {\n                $eq2 = $eq;\n            }\n        }\n\n        if ($eq1 > $eq2) {\n            $equal += $eq1;\n        } else if ($eq2 > 0) {\n            $equal += $eq2;\n        }\n    }\n\n    return $equal;\n}\n\nfunction generate_array_diff($ar1, $ar2, $is_reg, $w)\n{\n    $idx1 = 0;\n    $cnt1 = @count($ar1);\n    $idx2 = 0;\n    $cnt2 = @count($ar2);\n    $diff = array();\n    $old1 = array();\n    $old2 = array();\n\n    while ($idx1 < $cnt1 && $idx2 < $cnt2) {\n\n        if (comp_line($ar1[$idx1], $ar2[$idx2], $is_reg)) {\n            $idx1++;\n            $idx2++;\n            continue;\n        } else {\n\n            $c1 = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1 + 1, $idx2, $cnt1, $cnt2, 10);\n            $c2 = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $idx2 + 1, $cnt1, $cnt2, 10);\n\n            if ($c1 > $c2) {\n                $old1[$idx1] = sprintf(\"%03d- \", $idx1 + 1) . $w[$idx1++];\n            } else if ($c2 > 0) {\n                $old2[$idx2] = sprintf(\"%03d+ \", $idx2 + 1) . $ar2[$idx2++];\n            } else {\n                $old1[$idx1] = sprintf(\"%03d- \", $idx1 + 1) . $w[$idx1++];\n                $old2[$idx2] = sprintf(\"%03d+ \", $idx2 + 1) . $ar2[$idx2++];\n            }\n        }\n    }\n\n    reset($old1);\n    $k1 = key($old1);\n    $l1 = -2;\n    reset($old2);\n    $k2 = key($old2);\n    $l2 = -2;\n\n    while ($k1 !== null || $k2 !== null) {\n\n        if ($k1 == $l1 + 1 || $k2 === null) {\n            $l1 = $k1;\n            $diff[] = current($old1);\n            $k1 = next($old1) ? key($old1) : null;\n        } else if ($k2 == $l2 + 1 || $k1 === null) {\n            $l2 = $k2;\n            $diff[] = current($old2);\n            $k2 = next($old2) ? key($old2) : null;\n        } else if ($k1 < $k2) {\n            $l1 = $k1;\n            $diff[] = current($old1);\n            $k1 = next($old1) ? key($old1) : null;\n        } else {\n            $l2 = $k2;\n            $diff[] = current($old2);\n            $k2 = next($old2) ? key($old2) : null;\n        }\n    }\n\n    while ($idx1 < $cnt1) {\n        $diff[] = sprintf(\"%03d- \", $idx1 + 1) . $w[$idx1++];\n    }\n\n    while ($idx2 < $cnt2) {\n        $diff[] = sprintf(\"%03d+ \", $idx2 + 1) . $ar2[$idx2++];\n    }\n\n    return $diff;\n}\n\nfunction generate_diff($wanted, $wanted_re, $output)\n{\n    $w = explode(\"\\n\", $wanted);\n    $o = explode(\"\\n\", $output);\n    $r = is_null($wanted_re) ? $w : explode(\"\\n\", $wanted_re);\n    $diff = generate_array_diff($r, $o, !is_null($wanted_re), $w);\n\n    return implode(PHP_EOL, $diff);\n}\n\nfunction error($message)\n{\n    echo \"ERROR: {$message}\\n\";\n    exit(240);\n}\n\nfunction settings2array($settings, &$ini_settings)\n{\n    foreach ($settings as $setting) {\n\n        if (str_contains($setting, '=')) {\n            $setting = explode(\"=\", $setting, 2);\n            $name = trim($setting[0]);\n            $value = trim($setting[1]);\n\n            if ($name == 'extension' || $name == 'zend_extension') {\n\n                if (!isset($ini_settings[$name])) {\n                    $ini_settings[$name] = array();\n                }\n\n                $ini_settings[$name][] = $value;\n\n            } else {\n                $ini_settings[$name] = $value;\n            }\n        }\n    }\n}\n\nfunction settings2params($ini_settings)\n{\n    $settings = '';\n\n    foreach($ini_settings as $name => $value) {\n\n        if (is_array($value)) {\n            foreach($value as $val) {\n                $val = addslashes($val);\n                $settings .= \" -d \\\"$name=$val\\\"\";\n            }\n        } else {\n            if (IS_WINDOWS && !empty($value) && $value[0] == '\"') {\n                $len = strlen($value);\n\n                if ($value[$len - 1] == '\"') {\n                    $value[0] = \"'\";\n                    $value[$len - 1] = \"'\";\n                }\n            } else {\n                $value = addslashes($value);\n            }\n\n            $settings .= \" -d \\\"$name=$value\\\"\";\n        }\n    }\n\n    return $settings;\n}\n\nfunction compute_summary()\n{\n    global $n_total, $test_results, $ignored_by_ext, $sum_results, $percent_results;\n\n    $n_total = count($test_results);\n    $n_total += $ignored_by_ext;\n    $sum_results = array(\n        'PASSED' => 0,\n        'WARNED' => 0,\n        'SKIPPED' => 0,\n        'FAILED' => 0,\n        'BORKED' => 0,\n        'LEAKED' => 0,\n        'XFAILED' => 0,\n        'XLEAKED' => 0\n    );\n\n    foreach ($test_results as $v) {\n        $sum_results[$v]++;\n    }\n\n    $sum_results['SKIPPED'] += $ignored_by_ext;\n    $percent_results = array();\n\n    foreach ($sum_results as $v => $n) {\n        $percent_results[$v] = (100.0 * $n) / $n_total;\n    }\n}\n\nfunction get_summary($show_ext_summary, $show_html)\n{\n    global $exts_skipped, $exts_tested, $n_total, $sum_results, $percent_results, $end_time, $start_time, $failed_test_summary, $PHP_FAILED_TESTS, $valgrind;\n\n    $x_total = $n_total - $sum_results['SKIPPED'] - $sum_results['BORKED'];\n\n    if ($x_total) {\n        $x_warned = (100.0 * $sum_results['WARNED']) / $x_total;\n        $x_failed = (100.0 * $sum_results['FAILED']) / $x_total;\n        $x_xfailed = (100.0 * $sum_results['XFAILED']) / $x_total;\n        $x_xleaked = (100.0 * $sum_results['XLEAKED']) / $x_total;\n        $x_leaked = (100.0 * $sum_results['LEAKED']) / $x_total;\n        $x_passed = (100.0 * $sum_results['PASSED']) / $x_total;\n    } else {\n        $x_warned = $x_failed = $x_passed = $x_leaked = $x_xfailed = $x_xleaked = 0;\n    }\n\n    $summary = '';\n\n    if ($show_html) {\n        $summary .= \"<pre>\\n\";\n    }\n\n    if ($show_ext_summary) {\n        $summary .= '\n=====================================================================\nTEST RESULT SUMMARY\n---------------------------------------------------------------------\nExts skipped    : ' . sprintf('%4d', $exts_skipped) . '\nExts tested     : ' . sprintf('%4d', $exts_tested) . '\n---------------------------------------------------------------------\n';\n    }\n\n    $summary .= '\nNumber of tests : ' . sprintf('%4d', $n_total) . '          ' . sprintf('%8d', $x_total);\n\n    if ($sum_results['BORKED']) {\n        $summary .= '\nTests borked    : ' . sprintf('%4d (%5.1f%%)', $sum_results['BORKED'], $percent_results['BORKED']) . ' --------';\n    }\n\n    $summary .= '\nTests skipped   : ' . sprintf('%4d (%5.1f%%)', $sum_results['SKIPPED'], $percent_results['SKIPPED']) . ' --------\nTests warned    : ' . sprintf('%4d (%5.1f%%)', $sum_results['WARNED'], $percent_results['WARNED']) . ' ' . sprintf('(%5.1f%%)', $x_warned) . '\nTests failed    : ' . sprintf('%4d (%5.1f%%)', $sum_results['FAILED'], $percent_results['FAILED']) . ' ' . sprintf('(%5.1f%%)', $x_failed);\n\n    if ($sum_results['XFAILED']) {\n        $summary .= '\nExpected fail   : ' . sprintf('%4d (%5.1f%%)', $sum_results['XFAILED'], $percent_results['XFAILED']) . ' ' . sprintf('(%5.1f%%)', $x_xfailed);\n    }\n\n    if ($valgrind) {\n        $summary .= '\nTests leaked    : ' . sprintf('%4d (%5.1f%%)', $sum_results['LEAKED'], $percent_results['LEAKED']) . ' ' . sprintf('(%5.1f%%)', $x_leaked);\n        if ($sum_results['XLEAKED']) {\n            $summary .= '\nExpected leak   : ' . sprintf('%4d (%5.1f%%)', $sum_results['XLEAKED'], $percent_results['XLEAKED']) . ' ' . sprintf('(%5.1f%%)', $x_xleaked);\n        }\n    }\n\n    $summary .= '\nTests passed    : ' . sprintf('%4d (%5.1f%%)', $sum_results['PASSED'], $percent_results['PASSED']) . ' ' . sprintf('(%5.1f%%)', $x_passed) . '\n---------------------------------------------------------------------\nTime taken      : ' . sprintf('%4d seconds', $end_time - $start_time) . '\n=====================================================================\n';\n    $failed_test_summary = '';\n\n    if (count($PHP_FAILED_TESTS['SLOW'])) {\n        usort($PHP_FAILED_TESTS['SLOW'], function ($a, $b) {\n            return $a['info'] < $b['info'] ? 1 : -1;\n        });\n\n        $failed_test_summary .= '\n=====================================================================\nSLOW TEST SUMMARY\n---------------------------------------------------------------------\n';\n        foreach ($PHP_FAILED_TESTS['SLOW'] as $failed_test_data) {\n            $failed_test_summary .= sprintf('(%.3f s) ', $failed_test_data['info']) . $failed_test_data['test_name'] . \"\\n\";\n        }\n        $failed_test_summary .= \"=====================================================================\\n\";\n    }\n\n    if (count($PHP_FAILED_TESTS['XFAILED'])) {\n        $failed_test_summary .= '\n=====================================================================\nEXPECTED FAILED TEST SUMMARY\n---------------------------------------------------------------------\n';\n        foreach ($PHP_FAILED_TESTS['XFAILED'] as $failed_test_data) {\n            $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . \"\\n\";\n        }\n        $failed_test_summary .= \"=====================================================================\\n\";\n    }\n\n    if (count($PHP_FAILED_TESTS['BORKED'])) {\n        $failed_test_summary .= '\n=====================================================================\nBORKED TEST SUMMARY\n---------------------------------------------------------------------\n';\n        foreach ($PHP_FAILED_TESTS['BORKED'] as $failed_test_data) {\n            $failed_test_summary .= $failed_test_data['info'] . \"\\n\";\n        }\n\n        $failed_test_summary .= \"=====================================================================\\n\";\n    }\n\n    if (count($PHP_FAILED_TESTS['FAILED'])) {\n        $failed_test_summary .= '\n=====================================================================\nFAILED TEST SUMMARY\n---------------------------------------------------------------------\n';\n        foreach ($PHP_FAILED_TESTS['FAILED'] as $failed_test_data) {\n            $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . \"\\n\";\n        }\n        $failed_test_summary .= \"=====================================================================\\n\";\n    }\n    if (count($PHP_FAILED_TESTS['WARNED'])) {\n        $failed_test_summary .= '\n=====================================================================\nWARNED TEST SUMMARY\n---------------------------------------------------------------------\n';\n        foreach ($PHP_FAILED_TESTS['WARNED'] as $failed_test_data) {\n            $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . \"\\n\";\n        }\n\n        $failed_test_summary .= \"=====================================================================\\n\";\n    }\n\n    if (count($PHP_FAILED_TESTS['LEAKED'])) {\n        $failed_test_summary .= '\n=====================================================================\nLEAKED TEST SUMMARY\n---------------------------------------------------------------------\n';\n        foreach ($PHP_FAILED_TESTS['LEAKED'] as $failed_test_data) {\n            $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . \"\\n\";\n        }\n\n        $failed_test_summary .= \"=====================================================================\\n\";\n    }\n\n    if (count($PHP_FAILED_TESTS['XLEAKED'])) {\n        $failed_test_summary .= '\n=====================================================================\nEXPECTED LEAK TEST SUMMARY\n---------------------------------------------------------------------\n';\n        foreach ($PHP_FAILED_TESTS['XLEAKED'] as $failed_test_data) {\n            $failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . \"\\n\";\n        }\n\n        $failed_test_summary .= \"=====================================================================\\n\";\n    }\n\n    if ($failed_test_summary && !getenv('NO_PHPTEST_SUMMARY')) {\n        $summary .= $failed_test_summary;\n    }\n\n    if ($show_html) {\n        $summary .= \"</pre>\";\n    }\n\n    return $summary;\n}\n\nfunction show_start($start_time)\n{\n    global $html_output, $html_file;\n\n    if ($html_output) {\n        fwrite($html_file, \"<h2>Time Start: \" . date('Y-m-d H:i:s', $start_time) . \"</h2>\\n\");\n        fwrite($html_file, \"<table>\\n\");\n    }\n\n    echo \"TIME START \" . date('Y-m-d H:i:s', $start_time) . \"\\n=====================================================================\\n\";\n}\n\nfunction show_end($end_time)\n{\n    global $html_output, $html_file;\n\n    if ($html_output) {\n        fwrite($html_file, \"</table>\\n\");\n        fwrite($html_file, \"<h2>Time End: \" . date('Y-m-d H:i:s', $end_time) . \"</h2>\\n\");\n    }\n\n    echo \"=====================================================================\\nTIME END \" . date('Y-m-d H:i:s', $end_time) . \"\\n\";\n}\n\nfunction show_summary()\n{\n    global $html_output, $html_file;\n\n    if ($html_output) {\n        fwrite($html_file, \"<hr/>\\n\" . get_summary(true, true));\n    }\n\n    echo get_summary(true, false);\n}\n\nfunction show_redirect_start($tests, $tested, $tested_file)\n{\n    global $html_output, $html_file, $line_length, $SHOW_ONLY_GROUPS;\n\n    if ($html_output) {\n        fwrite($html_file, \"<tr><td colspan='3'>---&gt; $tests ($tested [$tested_file]) begin</td></tr>\\n\");\n    }\n\n    if (!$SHOW_ONLY_GROUPS || in_array('REDIRECT', $SHOW_ONLY_GROUPS)) {\n        echo \"REDIRECT $tests ($tested [$tested_file]) begin\\n\";\n    } else {\n        clear_show_test();\n    }\n}\n\nfunction show_redirect_ends($tests, $tested, $tested_file)\n{\n    global $html_output, $html_file, $line_length, $SHOW_ONLY_GROUPS;\n\n    if ($html_output) {\n        fwrite($html_file, \"<tr><td colspan='3'>---&gt; $tests ($tested [$tested_file]) done</td></tr>\\n\");\n    }\n\n    if (!$SHOW_ONLY_GROUPS || in_array('REDIRECT', $SHOW_ONLY_GROUPS)) {\n        echo \"REDIRECT $tests ($tested [$tested_file]) done\\n\";\n    } else {\n        clear_show_test();\n    }\n}\n\nfunction show_test($test_idx, $shortname)\n{\n    global $test_cnt;\n    global $line_length;\n\n    // swoole patch: pretty output\n    $str = \"TEST $test_idx/$test_cnt [$shortname]\";\n    $line_length = strlen($str);\n    echo $str;\n    flush();\n}\n\nfunction clear_show_test() {\n    global $line_length;\n    // Parallel testing\n    global $workerID;\n\n    if (!$workerID) {\n        // Write over the last line to avoid random trailing chars on next echo\n        echo str_repeat(\" \", intval($line_length)), \"\\r\";\n    }\n}\n\nfunction parse_conflicts(string $text) : array {\n    // Strip comments\n    $text = preg_replace('/#.*/', '', $text);\n    return array_map('trim', explode(\"\\n\", trim($text)));\n}\n\nfunction show_result($result, $tested, $tested_file, $extra = '', $temp_filenames = null)\n{\n    global $html_output, $html_file, $temp_target, $temp_urlbase, $line_length, $SHOW_ONLY_GROUPS;\n\n    // swoole patch: color print\n    switch ($result) {\n        case 'PASS':\n            $color = SWOOLE_COLOR_GREEN;\n            break;\n        case 'LEAK':\n            $result = 'ERROR';\n            $color = SWOOLE_COLOR_MAGENTA;\n            break;\n        case 'BORK':\n            $color = SWOOLE_COLOR_BLUE;\n            break;\n        case 'SKIP':\n            $color = SWOOLE_COLOR_YELLOW;\n            break;\n        default:\n            $color = SWOOLE_COLOR_RED;\n    }\n    $result = \"\\033[3{$color}m[$result]\\033[0m\";\n\n    if (!$SHOW_ONLY_GROUPS || in_array($result, $SHOW_ONLY_GROUPS)) {\n        echo \"$result $tested [$tested_file] $extra\\n\";\n    } else if (!$SHOW_ONLY_GROUPS) {\n        clear_show_test();\n    }\n\n    if ($html_output) {\n\n        if (isset($temp_filenames['file']) && file_exists($temp_filenames['file'])) {\n            $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['file']);\n            $tested = \"<a href='$url'>$tested</a>\";\n        }\n\n        if (isset($temp_filenames['skip']) && file_exists($temp_filenames['skip'])) {\n\n            if (empty($extra)) {\n                $extra = \"skipif\";\n            }\n\n            $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['skip']);\n            $extra = \"<a href='$url'>$extra</a>\";\n\n        } else if (empty($extra)) {\n            $extra = \"&nbsp;\";\n        }\n\n        if (isset($temp_filenames['diff']) && file_exists($temp_filenames['diff'])) {\n            $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['diff']);\n            $diff = \"<a href='$url'>diff</a>\";\n        } else {\n            $diff = \"&nbsp;\";\n        }\n\n        if (isset($temp_filenames['mem']) && file_exists($temp_filenames['mem'])) {\n            $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['mem']);\n            $mem = \"<a href='$url'>leaks</a>\";\n        } else {\n            $mem = \"&nbsp;\";\n        }\n\n        fwrite(\n            $html_file,\n            \"<tr>\" .\n            \"<td>$result</td>\" .\n            \"<td>$tested</td>\" .\n            \"<td>$extra</td>\" .\n            \"<td>$diff</td>\" .\n            \"<td>$mem</td>\" .\n            \"</tr>\\n\"\n        );\n    }\n}\n\nfunction junit_init()\n{\n    // Check whether a junit log is wanted.\n    global $workerID;\n    $JUNIT = getenv('TEST_PHP_JUNIT');\n    if (empty($JUNIT)) {\n        $GLOBALS['JUNIT'] = false;\n        return;\n    }\n    if ($workerID) {\n        $fp = null;\n    } else if (!$fp = fopen($JUNIT, 'w')) {\n        error(\"Failed to open $JUNIT for writing.\");\n    }\n    $GLOBALS['JUNIT'] = array(\n        'fp' => $fp,\n        'name' => 'PHP',\n        'test_total' => 0,\n        'test_pass' => 0,\n        'test_fail' => 0,\n        'test_error' => 0,\n        'test_skip' => 0,\n        'test_warn' => 0,\n        'execution_time' => 0,\n        'suites' => array(),\n        'files' => array()\n    );\n}\n\nfunction junit_save_xml()\n{\n    global $JUNIT;\n    if (!junit_enabled()) return;\n\n    $xml = '<' . '?' . 'xml version=\"1.0\" encoding=\"UTF-8\"' . '?' . '>' . PHP_EOL;\n    $xml .= sprintf(\n        '<testsuites name=\"%s\" tests=\"%s\" failures=\"%d\" errors=\"%d\" skip=\"%d\" time=\"%s\">' . PHP_EOL,\n        $JUNIT['name'],\n        $JUNIT['test_total'],\n        $JUNIT['test_fail'],\n        $JUNIT['test_error'],\n        $JUNIT['test_skip'],\n        $JUNIT['execution_time']\n    );\n    $xml .= junit_get_suite_xml();\n    $xml .= '</testsuites>';\n    fwrite($JUNIT['fp'], $xml);\n}\n\nfunction junit_get_suite_xml($suite_name = '')\n{\n    global $JUNIT;\n\n    $result = \"\";\n\n    foreach ($JUNIT['suites'] as $suite_name => $suite) {\n        $result .= sprintf(\n            '<testsuite name=\"%s\" tests=\"%s\" failures=\"%d\" errors=\"%d\" skip=\"%d\" time=\"%s\">' . PHP_EOL,\n            $suite['name'],\n            $suite['test_total'],\n            $suite['test_fail'],\n            $suite['test_error'],\n            $suite['test_skip'],\n            $suite['execution_time']\n        );\n\n        if (!empty($suite_name)) {\n            foreach ($suite['files'] as $file) {\n                $result .= $JUNIT['files'][$file]['xml'];\n            }\n        }\n\n        $result .= '</testsuite>' . PHP_EOL;\n    }\n\n    return $result;\n}\n\nfunction junit_enabled()\n{\n    global $JUNIT;\n    return !empty($JUNIT);\n}\n\n/**\n * @param array|string $type\n * @param string $file_name\n * @param string $test_name\n * @param int|string $time\n * @param string $message\n * @param string $details\n * @return void\n */\nfunction junit_mark_test_as($type, $file_name, $test_name, $time = null, $message = '', $details = '')\n{\n    global $JUNIT;\n    if (!junit_enabled()) return;\n\n    $suite = junit_get_suitename_for($file_name);\n\n    junit_suite_record($suite, 'test_total');\n\n    $time = $time ?? junit_get_timer($file_name);\n    junit_suite_record($suite, 'execution_time', $time);\n\n    $escaped_details = htmlspecialchars($details, ENT_QUOTES, 'UTF-8');\n    $escaped_details = preg_replace_callback('/[\\0-\\x08\\x0B\\x0C\\x0E-\\x1F]/', function ($c) {\n        return sprintf('[[0x%02x]]', ord($c[0]));\n    }, $escaped_details);\n    $escaped_message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8');\n\n    $escaped_test_name = htmlspecialchars($file_name . ' (' . $test_name . ')', ENT_QUOTES);\n    $JUNIT['files'][$file_name]['xml'] = \"<testcase name='$escaped_test_name' time='$time'>\\n\";\n\n    if (is_array($type)) {\n        $output_type = $type[0] . 'ED';\n        $temp = array_intersect(array('XFAIL', 'XLEAK', 'FAIL', 'WARN'), $type);\n        $type = reset($temp);\n    } else {\n        $output_type = $type . 'ED';\n    }\n\n    if ('PASS' == $type || 'XFAIL' == $type || 'XLEAK' == $type) {\n        junit_suite_record($suite, 'test_pass');\n    } elseif ('BORK' == $type) {\n        junit_suite_record($suite, 'test_error');\n        $JUNIT['files'][$file_name]['xml'] .= \"<error type='$output_type' message='$escaped_message'/>\\n\";\n    } elseif ('SKIP' == $type) {\n        junit_suite_record($suite, 'test_skip');\n        $JUNIT['files'][$file_name]['xml'] .= \"<skipped>$escaped_message</skipped>\\n\";\n    } elseif ('WARN' == $type) {\n        junit_suite_record($suite, 'test_warn');\n        $JUNIT['files'][$file_name]['xml'] .= \"<warning>$escaped_message</warning>\\n\";\n    } elseif ('FAIL' == $type) {\n        junit_suite_record($suite, 'test_fail');\n        $JUNIT['files'][$file_name]['xml'] .= \"<failure type='$output_type' message='$escaped_message'>$escaped_details</failure>\\n\";\n    } else {\n        junit_suite_record($suite, 'test_error');\n        $JUNIT['files'][$file_name]['xml'] .= \"<error type='$output_type' message='$escaped_message'>$escaped_details</error>\\n\";\n    }\n\n    $JUNIT['files'][$file_name]['xml'] .= \"</testcase>\\n\";\n\n}\n\nfunction junit_suite_record($suite, $param, $value = 1)\n{\n    global $JUNIT;\n\n    $JUNIT[$param] += $value;\n    $JUNIT['suites'][$suite][$param] += $value;\n}\n\nfunction junit_get_timer($file_name)\n{\n    global $JUNIT;\n    if (!junit_enabled()) return 0;\n\n    if (isset($JUNIT['files'][$file_name]['total'])) {\n        return number_format($JUNIT['files'][$file_name]['total'], 4);\n    }\n\n    return 0;\n}\n\nfunction junit_start_timer($file_name)\n{\n    global $JUNIT;\n    if (!junit_enabled()) return;\n\n    if (!isset($JUNIT['files'][$file_name]['start'])) {\n        $JUNIT['files'][$file_name]['start'] = microtime(true);\n\n        $suite = junit_get_suitename_for($file_name);\n        junit_init_suite($suite);\n        $JUNIT['suites'][$suite]['files'][$file_name] = $file_name;\n    }\n}\n\nfunction junit_get_suitename_for($file_name)\n{\n    return junit_path_to_classname(dirname($file_name));\n}\n\nfunction junit_path_to_classname($file_name)\n{\n    global $JUNIT;\n\n    if (!junit_enabled()) return '';\n\n    $ret = $JUNIT['name'];\n    $_tmp = array();\n\n    // lookup whether we're in the PHP source checkout\n    $max = 5;\n    if (is_file($file_name)) {\n        $dir = dirname(realpath($file_name));\n    } else {\n        $dir = realpath($file_name);\n    }\n    do {\n        array_unshift($_tmp, basename($dir));\n        $chk = $dir . DIRECTORY_SEPARATOR . \"main\" . DIRECTORY_SEPARATOR . \"php_version.h\";\n        $dir = dirname($dir);\n    } while (!file_exists($chk) && --$max > 0);\n    if (file_exists($chk)) {\n        if ($max) {\n            array_shift($_tmp);\n        }\n        foreach ($_tmp as $p) {\n            $ret .= \".\" . preg_replace(\",[^a-z0-9]+,i\", \".\", $p);\n        }\n        return $ret;\n    }\n\n    return $JUNIT['name'] . '.' . str_replace(array(DIRECTORY_SEPARATOR, '-'), '.', $file_name);\n}\n\nfunction junit_init_suite($suite_name)\n{\n    global $JUNIT;\n    if (!junit_enabled()) return;\n\n    if (!empty($JUNIT['suites'][$suite_name])) {\n        return;\n    }\n\n    $JUNIT['suites'][$suite_name] = array(\n        'name' => $suite_name,\n        'test_total' => 0,\n        'test_pass' => 0,\n        'test_fail' => 0,\n        'test_error' => 0,\n        'test_skip' => 0,\n        'test_warn' => 0,\n        'files' => array(),\n        'execution_time' => 0,\n    );\n}\n\nfunction junit_finish_timer($file_name)\n{\n    global $JUNIT;\n    if (!junit_enabled()) return;\n\n    if (!isset($JUNIT['files'][$file_name]['start'])) {\n        error(\"Timer for $file_name was not started!\");\n    }\n\n    if (!isset($JUNIT['files'][$file_name]['total'])) {\n        $JUNIT['files'][$file_name]['total'] = 0;\n    }\n\n    $start = $JUNIT['files'][$file_name]['start'];\n    $JUNIT['files'][$file_name]['total'] += microtime(true) - $start;\n    unset($JUNIT['files'][$file_name]['start']);\n}\n\nfunction junit_merge_results($junit)\n{\n    global $JUNIT;\n    $JUNIT['test_total'] += $junit['test_total'];\n    $JUNIT['test_pass']  += $junit['test_pass'];\n    $JUNIT['test_fail']  += $junit['test_fail'];\n    $JUNIT['test_error'] += $junit['test_error'];\n    $JUNIT['test_skip']  += $junit['test_skip'];\n    $JUNIT['test_warn']  += $junit['test_warn'];\n    $JUNIT['execution_time'] += $junit['execution_time'];\n    $JUNIT['files'] += $junit['files'];\n    foreach ($junit['suites'] as $name => $suite) {\n        if (!isset($JUNIT['suites'][$name])) {\n            $JUNIT['suites'][$name] = $suite;\n            continue;\n        }\n\n        $SUITE =& $JUNIT['suites'][$name];\n        $SUITE['test_total'] += $suite['test_total'];\n        $SUITE['test_pass']  += $suite['test_pass'];\n        $SUITE['test_fail']  += $suite['test_fail'];\n        $SUITE['test_error'] += $suite['test_error'];\n        $SUITE['test_skip']  += $suite['test_skip'];\n        $SUITE['test_warn']  += $suite['test_warn'];\n        $SUITE['execution_time'] += $suite['execution_time'];\n        $SUITE['files'] += $suite['files'];\n    }\n}\n\nclass RuntestsValgrind\n{\n    protected $version = '';\n    protected $header = '';\n    protected $version_3_3_0 = false;\n    protected $version_3_8_0 = false;\n    protected $tool = null;\n\n    public function getVersion()\n    {\n        return $this->version;\n    }\n\n    public function getHeader()\n    {\n        return $this->header;\n    }\n\n    public function __construct(array $environment, string $tool = 'memcheck')\n    {\n        $this->tool = $tool;\n        $header = system_with_timeout(\"valgrind --tool={$this->tool} --version\", $environment);\n        if (!$header) {\n            error(\"Valgrind returned no version info for {$this->tool}, cannot proceed.\\n\".\n                \"Please check if Valgrind is installed and the tool is named correctly.\");\n        }\n        $count = 0;\n        $version = preg_replace(\"/valgrind-(\\d+)\\.(\\d+)\\.(\\d+)([.\\w_-]+)?(\\s+)/\", '$1.$2.$3', $header, 1, $count);\n        if ($count != 1) {\n            error(\"Valgrind returned invalid version info (\\\"{$header}\\\") for {$this->tool}, cannot proceed.\");\n        }\n        $this->version = $version;\n        $this->header = sprintf(\n            \"%s (%s)\", trim($header), $this->tool);\n        $this->version_3_3_0 = version_compare($version, '3.3.0', '>=');\n        $this->version_3_8_0 = version_compare($version, '3.8.0', '>=');\n    }\n\n    public function wrapCommand($cmd, $memcheck_filename, $check_all)\n    {\n        $vcmd = \"valgrind -q --tool={$this->tool} --trace-children=yes\";\n        if ($check_all) {\n            $vcmd .= ' --smc-check=all';\n        }\n        // swoole patch\n        $vcmd .= ' --undef-value-errors=no';\n\n        /* --vex-iropt-register-updates=allregs-at-mem-access is necessary for phpdbg watchpoint tests */\n        if ($this->version_3_8_0) {\n            /* valgrind 3.3.0+ doesn't have --log-file-exactly option */\n            return \"$vcmd --vex-iropt-register-updates=allregs-at-mem-access --log-file=$memcheck_filename $cmd\";\n        } elseif ($this->version_3_3_0) {\n            return \"$vcmd --vex-iropt-precise-memory-exns=yes --log-file=$memcheck_filename $cmd\";\n        } else {\n            return \"$vcmd --vex-iropt-precise-memory-exns=yes --log-file-exactly=$memcheck_filename $cmd\";\n        }\n    }\n}\n\n// swoole patch: color const\ndefine('SWOOLE_TEST_TIMEOUT', file_exists('/.cienv') ? 10 : 30);\ndefine('SWOOLE_COLOR_RED', 1);\ndefine('SWOOLE_COLOR_GREEN', 2);\ndefine('SWOOLE_COLOR_YELLOW', 3);\ndefine('SWOOLE_COLOR_BLUE', 4);\ndefine('SWOOLE_COLOR_MAGENTA', 5);\ndefine('SWOOLE_COLOR_CYAN', 6);\ndefine('SWOOLE_COLOR_WHITE', 7);\ndefine('SWOOLE_TEST_OUTPUT_MAX_SIZE', 64 * 1024 * 1024);\n// swoole patch: libc check\ndefine('SWOOLE_IS_MUSL_LIBC', !empty(shell_exec(\"ldd 2>&1 | grep -i musl\")));\n\nmain();\n"
  },
  {
    "path": "tests/start.sh",
    "content": "#!/bin/bash\n__CURRENT__=`pwd`\n__DIR__=$(cd \"$(dirname \"$0\")\";pwd)\n\nclear_php()\n{\n  ps -A | grep \\.php$ | grep -v phpstorm | grep -v php-fpm | awk '{print $1}' | xargs kill -9 > /dev/null 2>&1\n}\n\n## before tests\nclear_php\nif [ `ulimit -n` -le 16384 ]; then\n    ulimit -n 16384 > /dev/null 2>&1\nfi\n# run tests\nif [ -z \"${TEST_PHP_EXECUTABLE}\" ]; then\n    export TEST_PHP_EXECUTABLE=`which php`\nfi\n\nif [ -z \"${1}\" ]; then\n    glob=\"swoole_*\"\nelse\n    if [ \"${1}x\" = \"basex\" ]; then\n        glob=\"\\\n        swoole_atomic \\\n        swoole_event \\\n        swoole_function \\\n        swoole_global \\\n        swoole_process \\\n        swoole_process_pool \\\n        swoole_table \\\n        swoole_coroutine* \\\n        swoole_channel_coro \\\n        swoole_client_coro \\\n        swoole_http_client_coro \\\n        swoole_http2_client_coro \\\n        swoole_server \\\n        swoole_http_server \\\n        swoole_websocket_server \\\n        swoole_redis_server \\\n        swoole_socket_coro \\\n        swoole_runtime\"\n        if [ ${#} -gt 1 ]; then\n            args=\"${@}\"\n            args=\"${args#* }\"\n            glob=\"${args} ${glob}\"\n        fi\n    else\n        glob=\"$@\"\n        if [ \"${glob:0:6}\" = \"tests/\" ]; then\n            glob=\"${glob#tests/}\"\n        fi\n    fi\nfi\n\nif [ $? -eq 0 ]; then\n    PHPT=1 ${TEST_PHP_EXECUTABLE} -d \"memory_limit=1024m\" ${__DIR__}/run-tests ${glob}\nfi\n\n# after tests\nclear_php\nrm -f /tmp/swoole.log > /dev/null 2>&1\n"
  },
  {
    "path": "tests/swoole_atomic/atomic.phpt",
    "content": "--TEST--\nswoole_atomic: add/sub/get/cmpset\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$atomic = new Swoole\\Atomic(1);\n\nAssert::same($atomic->add(199), 200);\nAssert::same($atomic->sub(35), 165);\nAssert::same($atomic->get(), 165);\nAssert::assert($atomic->cmpset(165, 1));\nAssert::assert(!$atomic->cmpset(1555, 0));\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_atomic/dtor_in_child.phpt",
    "content": "--TEST--\nswoole_atomic: destruct objects in child processe\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$atomic = new Swoole\\Atomic;\n\n$p = new Swoole\\Process(function () use ($atomic) {\n    $atomic->wait();\n    echo \"Child OK\\n\";\n    exit(0);\n});\n$p->start();\n\nusleep(200000);\necho \"Master OK\\n\";\n$atomic->wakeup(1);\n$status = Swoole\\Process::wait();\n?>\n--EXPECT--\nMaster OK\nChild OK\n"
  },
  {
    "path": "tests/swoole_atomic/multi_wakeup.phpt",
    "content": "--TEST--\nswoole_atomic: multi wakeup\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager();\n$pm->setWaitTimeout(5);\n$s = microtime(true);\n$pm->parentFunc = function () use ($pm, $s) {\n    echo \"WAKED\\n\";\n    $s = microtime(true) - $s;\n    Assert::assert($s < 1);\n    usleep(1000);\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set(['worker_num' => 4, 'log_file' => '/dev/null']);\n    $server->on('workerStart', function () use ($pm) {\n        Assert::assert($pm->wakeup());\n    });\n    $server->on('request', function () { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nWAKED\n"
  },
  {
    "path": "tests/swoole_atomic/wait.phpt",
    "content": "--TEST--\nswoole_atomic: wakeup & wait\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$atomic = new Swoole\\Atomic;\nconst N = 4;\n\n$workers = [];\n\nfor ($i = 0; $i < 4; $i++)\n{\n    $p = new Swoole\\Process(function () use ($atomic)\n    {\n        $atomic->wait();\n        echo \"Child OK\\n\";\n    });\n    $p->start();\n    $workers[$i] = $p;\n}\n\nusleep(200000);\necho \"Master OK\\n\";\n$atomic->wakeup(N);\n\nfor ($i = 0; $i < 4; $i++)\n{\n    $status = Swoole\\Process::wait();\n}\n?>\n--EXPECT--\nMaster OK\nChild OK\nChild OK\nChild OK\nChild OK\n"
  },
  {
    "path": "tests/swoole_atomic/wait_and_wakeup.phpt",
    "content": "--TEST--\nswoole_atomic: wait & wakeup\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$atomic = new Swoole\\Atomic(1);\nvar_dump($atomic->wakeup(), $atomic->get());\n\n$atomic = new Swoole\\Atomic(0);\nvar_dump($atomic->wakeup(), $atomic->get());\n\n$atomic = new Swoole\\Atomic(0);\nvar_dump($atomic->wait(1), $atomic->get());\n\n$atomic = new Swoole\\Atomic(1);\nvar_dump($atomic->wait(1), $atomic->get());\n\n?>\n--EXPECT--\nbool(true)\nint(1)\nbool(true)\nint(1)\nbool(false)\nint(0)\nbool(true)\nint(0)\n"
  },
  {
    "path": "tests/swoole_atomic/wait_ex.phpt",
    "content": "--TEST--\nswoole_atomic: wakeup & wait ex\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$atomic = new Swoole\\Atomic();\n$server = new Swoole\\Http\\Server('127.0.0.1', get_one_free_port(), SWOOLE_PROCESS);\n$server->set(['worker_num' => 4, 'log_file' => '/dev/null']);\n$server->on('WorkerStart', function (Swoole\\Http\\Server $server, int $wid) use ($atomic) {\n    if ($wid === 0) {\n        sleep(1);\n        $atomic->wakeup(3);\n        sleep(1);\n        $server->shutdown();\n    } else {\n        $s = microtime(true);\n        echo \"sleeping...\\n\";\n        $atomic->wait(-1);\n        echo \"I'm wide awake\\n\";\n        $s = microtime(true) - $s;\n        time_approximate(1, $s, 0.2);\n    }\n});\n$server->on('Request', function () { });\n$server->start();\n?>\n--EXPECT--\nsleeping...\nsleeping...\nsleeping...\nI'm wide awake\nI'm wide awake\nI'm wide awake\n"
  },
  {
    "path": "tests/swoole_channel_coro/1.phpt",
    "content": "--TEST--\nswoole_channel_coro: consumer first with select mode\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nexit(\"skip for select\");\n ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan();\n//consumer first with select mode\n$num = 10;\ngo(function () use ($c1,$num) {\n    $read_list = [$c1];\n    $write_list = null;\n    echo \"select yield\\n\";\n    $result = chan::select($read_list, $write_list, 2);\n    echo \"select resume res: \".var_export($result,1).\"\\n\";\n    if ($read_list)\n    {\n        foreach($read_list as $ch)\n        {\n            for ($i=0;$i<$num;$i++)\n            {\n                $ret = $ch->pop();\n                echo \"pop [#$i] ret:\".var_export($ret,1).\"\\n\";\n            }\n        }\n    }\n});\n\ngo(function () use ($c1,$num) {\n    echo \"push start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        $ret = $c1->push(\"data-$i\");\n        echo \"push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n\n});\necho \"main end\\n\";\n?>\n--EXPECT--\nselect yield\npush start\nmain end\nselect resume res: true\npop [#0] ret:'data-0'\npush [#0] ret:true\npop [#1] ret:'data-1'\npush [#1] ret:true\npop [#2] ret:'data-2'\npush [#2] ret:true\npop [#3] ret:'data-3'\npush [#3] ret:true\npop [#4] ret:'data-4'\npush [#4] ret:true\npop [#5] ret:'data-5'\npush [#5] ret:true\npop [#6] ret:'data-6'\npush [#6] ret:true\npop [#7] ret:'data-7'\npush [#7] ret:true\npop [#8] ret:'data-8'\npush [#8] ret:true\npop [#9] ret:'data-9'\npush [#9] ret:true\n"
  },
  {
    "path": "tests/swoole_channel_coro/10.phpt",
    "content": "--TEST--\nswoole_channel_coro: 10\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    $chan = new chan(3);\n    go(function () use ($chan) {\n        co::sleep(0.001);\n        $chan->push(\"data\");\n    });\n    Assert::same($chan->pop(0.001), \"data\");\n    Assert::false($chan->pop(0.001));\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_channel_coro/2.phpt",
    "content": "--TEST--\nswoole_channel_coro: consumer first without select mode\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan();\n//consumer first without select mode\n$num = 10;\ngo(function () use ($c1, $num) {\n    echo \"pop start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        $ret = $c1->pop();\n        echo \"pop [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n});\n\ngo(function () use ($c1,$num) {\n    echo \"push start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        $ret = $c1->push(\"data-$i\");\n        echo \"push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n\n});\necho \"main end\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECT--\npop start\npush start\npop [#0] ret:'data-0'\npush [#0] ret:true\npop [#1] ret:'data-1'\npush [#1] ret:true\npop [#2] ret:'data-2'\npush [#2] ret:true\npop [#3] ret:'data-3'\npush [#3] ret:true\npop [#4] ret:'data-4'\npush [#4] ret:true\npop [#5] ret:'data-5'\npush [#5] ret:true\npop [#6] ret:'data-6'\npush [#6] ret:true\npop [#7] ret:'data-7'\npush [#7] ret:true\npop [#8] ret:'data-8'\npush [#8] ret:true\npop [#9] ret:'data-9'\npush [#9] ret:true\nmain end\n"
  },
  {
    "path": "tests/swoole_channel_coro/3.phpt",
    "content": "--TEST--\nswoole_channel_coro: product first with select mode\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nexit(\"skip for select\");\n ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan(1);\n//product first with select mode\n$num = 10;\ngo(function () use ($c1,$num) {\n    echo \"push start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        $ret = $c1->push(\"data-$i\");\n        echo \"push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n});\n\ngo(function () use ($c1,$num) {\n    $read_list = [$c1];\n    $write_list = null;\n    echo \"select yield\\n\";\n    $result = chan::select($read_list, $write_list, 2);\n    echo \"select resume res: \".var_export($result,1).\"\\n\";\n    if ($read_list)\n    {\n        foreach($read_list as $ch)\n        {\n            for ($i=0;$i<$num;$i++)\n            {\n                $ret = $ch->pop();\n                echo \"pop [#$i] ret:\".var_export($ret,1).\"\\n\";\n            }\n        }\n    }\n});\necho \"main end\\n\";\n?>\n--EXPECT--\npush start\npush [#0] ret:true\nselect yield\nselect resume res: true\npop [#0] ret:'data-0'\nmain end\npop [#1] ret:'data-1'\npush [#1] ret:true\npop [#2] ret:'data-2'\npush [#2] ret:true\npop [#3] ret:'data-3'\npush [#3] ret:true\npop [#4] ret:'data-4'\npush [#4] ret:true\npop [#5] ret:'data-5'\npush [#5] ret:true\npop [#6] ret:'data-6'\npush [#6] ret:true\npop [#7] ret:'data-7'\npush [#7] ret:true\npop [#8] ret:'data-8'\npush [#8] ret:true\npop [#9] ret:'data-9'\npush [#9] ret:true\n"
  },
  {
    "path": "tests/swoole_channel_coro/4.phpt",
    "content": "--TEST--\nswoole_channel_coro: product first without select mode\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan(1);\n//product first without select mode\n$num = 10;\ngo(function () use ($c1,$num) {\n    echo \"push start\\n\";\n    for ($i = 0; $i < $num; $i++) {\n        $ret = $c1->push(\"data-$i\");\n        echo \"push [#$i] ret:\" . var_export($ret, 1) . \"\\n\";\n    }\n});\n\ngo(function () use ($c1, $num) {\n    echo \"pop start\\n\";\n    for ($i = 0; $i < $num; $i++) {\n        $ret = $c1->pop();\n        echo \"pop [#$i] ret:\" . var_export($ret, 1) . \"\\n\";\n    }\n});\necho \"main end\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECT--\npush start\npush [#0] ret:true\npop start\npush [#1] ret:true\npop [#0] ret:'data-0'\npush [#2] ret:true\npop [#1] ret:'data-1'\npush [#3] ret:true\npop [#2] ret:'data-2'\npush [#4] ret:true\npop [#3] ret:'data-3'\npush [#5] ret:true\npop [#4] ret:'data-4'\npush [#6] ret:true\npop [#5] ret:'data-5'\npush [#7] ret:true\npop [#6] ret:'data-6'\npush [#8] ret:true\npop [#7] ret:'data-7'\npush [#9] ret:true\npop [#8] ret:'data-8'\npop [#9] ret:'data-9'\nmain end\n"
  },
  {
    "path": "tests/swoole_channel_coro/5.phpt",
    "content": "--TEST--\nswoole_channel_coro: push with sleep\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nexit(\"skip for select\");\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan();\n\n$num = 10;\ngo(function () use ($c1,$num) {\n    $read_list = [$c1];\n    $write_list = null;\n    echo \"select yield\\n\";\n    $result = chan::select($read_list, $write_list, 2);\n    echo \"select resume res: \".var_export($result,1).\"\\n\";\n    if ($read_list)\n    {\n        foreach($read_list as $ch)\n        {\n            for ($i=0;$i<$num;$i++)\n            {\n                $ret = $ch->pop();\n                echo \"pop [#$i] ret:\".var_export($ret,1).\"\\n\";\n            }\n        }\n    }\n});\n\ngo(function () use ($c1,$num) {\n    echo \"push start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        if ($i == 2) {\n            echo \"start sleep\\n\";\n            co:sleep(1);\n            echo \"end sleep\\n\";\n        }\n        $ret = $c1->push(\"data-$i\");\n        echo \"push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n\n});\necho \"main end\\n\";\n?>\n--EXPECT--\nselect yield\npush start\nmain end\nselect resume res: true\npop [#0] ret:'data-0'\npush [#0] ret:true\npop [#1] ret:'data-1'\npush [#1] ret:true\nstart sleep\nend sleep\npop [#2] ret:'data-2'\npush [#2] ret:true\npop [#3] ret:'data-3'\npush [#3] ret:true\npop [#4] ret:'data-4'\npush [#4] ret:true\npop [#5] ret:'data-5'\npush [#5] ret:true\npop [#6] ret:'data-6'\npush [#6] ret:true\npop [#7] ret:'data-7'\npush [#7] ret:true\npop [#8] ret:'data-8'\npush [#8] ret:true\npop [#9] ret:'data-9'\npush [#9] ret:true\n"
  },
  {
    "path": "tests/swoole_channel_coro/6.phpt",
    "content": "--TEST--\nswoole_channel_coro: push with sleep\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$chan = new chan(1);\n\ngo(function () use ($chan) {\n    echo \"coro1 start\\n\";\n    for ($i = 0; $i < 2; $i++) {\n        $result = $chan->pop();\n        var_dump($result);\n    }\n    echo 'pop over!'. PHP_EOL;\n});\n\ngo(function () use ($chan){\n    echo \"coro2 start\\n\";\n    $retval = [2,23,2];\n    $chan->push($retval);\n    echo \"coro2 end\\n\";\n});\n\ngo(function () use ($chan){\n    echo \"coro3 start\\n\";\n    $eee = \"hello word\";\n    $chan->push($eee);\n    echo \"coro3 end\\n\";\n});\n\necho 'master end' . PHP_EOL;\nSwoole\\Event::wait();\n?>\n--EXPECT--\ncoro1 start\ncoro2 start\narray(3) {\n  [0]=>\n  int(2)\n  [1]=>\n  int(23)\n  [2]=>\n  int(2)\n}\ncoro2 end\ncoro3 start\nstring(10) \"hello word\"\npop over!\ncoro3 end\nmaster end\n"
  },
  {
    "path": "tests/swoole_channel_coro/7.phpt",
    "content": "--TEST--\nswoole_channel_coro: push and pop\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine as co;\n\n$chan = new co\\Channel(2);\n$n = 10;\nfor ($i = 0; $i < $n; $i++) {\n    go(function () use ($i,$chan) {\n        $chan->push($i);\n    });\n};\n\ngo(function ()use ($chan){\n    $bool = true;\n    for ($i = 0; $i < 10; $i++)  {\n        $data = $chan->pop();\n        if ($data===false) {\n            $bool = false;\n        }\n        var_dump($data);\n    }\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nint(0)\nint(1)\nint(2)\nint(3)\nint(4)\nint(5)\nint(6)\nint(7)\nint(8)\nint(9)\n"
  },
  {
    "path": "tests/swoole_channel_coro/8.phpt",
    "content": "--TEST--\nswoole_channel_coro: pop priority\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine as co;\n\n$chan = new co\\Channel(2);\n$n = 4;\n\nfor ($i = 0; $i < $n; $i++) {\n    go(function () use ($i, $chan) {\n        echo \"[pop]\".var_export($chan->pop(), 1).\"\\n\";\n    });\n};\n\nSwoole\\Timer::after(500, function () use ($chan) {\n    for ($i = 0; $i < 6; $i++)  {\n        $chan->push($i);\n    }\n});\n\ngo(function ()use ($chan){\n    for ($i = 0; $i < 4; $i++)  {\n        $chan->push($i);\n    }\n    for ($i = 0; $i < 6; $i++)  {\n        echo \"[pop & push]\".var_export($chan->pop($i), 1).\"\\n\";\n    }\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\n[pop]0\n[pop]1\n[pop]2\n[pop]3\n[pop & push]0\n[pop & push]1\n[pop & push]2\n[pop & push]3\n[pop & push]4\n[pop & push]5\n"
  },
  {
    "path": "tests/swoole_channel_coro/9.phpt",
    "content": "--TEST--\nswoole_channel_coro: pop priority\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine as co;\n\n$chan = new co\\Channel(2);\n\nfor ($i = 0; $i < 4; $i++) {\n    go(function () use ($i, $chan) {\n        $chan->push($i);\n    });\n};\n\nSwoole\\Timer::after(200, function () use ($chan) {\n    for ($i = 0; $i < 6; $i++)  {\n        $chan->push($i);\n    }\n});\n\ngo(function () use ($chan){\n    for ($i = 0; $i < 2; $i++)  {\n        echo \"[read]\".var_export($chan->pop(), 1).\"\\n\";\n    }\n    for ($i = 0; $i < 8; $i++)  {\n        echo \"[read & write]\".var_export($chan->pop(), 1).\"\\n\";\n    }\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\n[read]0\n[read]1\n[read & write]2\n[read & write]3\n[read & write]0\n[read & write]1\n[read & write]2\n[read & write]3\n[read & write]4\n[read & write]5\n"
  },
  {
    "path": "tests/swoole_channel_coro/basic.phpt",
    "content": "--TEST--\nswoole_channel_coro: coro channel\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine as co;\n$chan = new co\\Channel(1);\nco::create(function () use ($chan) {\n    for($i=1; $i<=10; $i++) {\n        co::sleep(0.01);\n        $chan->push($i);\n        echo \"$i\\n\";\n    }\n});\n\nco::create(function () use ($chan) {\n    for($i=0; $i<10; $i++) {\n        $data = $chan->pop();\n        Assert::assert(!empty($data));\n    }\n});\n\nSwoole\\Event::wait();\n\n?>\n--EXPECT--\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n"
  },
  {
    "path": "tests/swoole_channel_coro/benchmark.phpt",
    "content": "--TEST--\nswoole_channel_coro: 100W benchmark\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$time = [];\n\n// spl queue\n$time['splQueue'] = microtime(true);\n$queue = new SplQueue;\nfor ($i = MAX_LOOPS; $i--;) {\n    $queue->enqueue($i);\n}\n$i = MAX_LOOPS;\nwhile (!$queue->isEmpty()) {\n    Assert::same((--$i), $queue->dequeue());\n}\n$time['splQueue'] = microtime(true) - $time['splQueue'];\n\n// channel\ngo(function () use (&$time) {\n    $time['channel_raw'] = microtime(true);\n    $chan = new Chan(MAX_LOOPS);\n    for ($i = MAX_LOOPS; $i--;) {\n        $chan->push($i);\n    }\n    $i = MAX_LOOPS;\n    while (!$chan->isEmpty()) {\n        Assert::same((--$i), $chan->pop());\n    }\n    $time['channel_raw'] = microtime(true) - $time['channel_raw'];\n});\n\n// channel with scheduler\n$chan = new Chan;\ngo(function () use (&$time, $chan) {\n    co::sleep(0.1);\n    $time['channel_scheduler'] = microtime(true);\n    for ($i = MAX_LOOPS; $i--;) {\n        $chan->push($i);\n    }\n    $chan->push(false);\n});\ngo(function () use (&$time, $chan) {\n    $i = MAX_LOOPS;\n    while (($ret = $chan->pop()) !== false) {\n        Assert::same((--$i), $ret);\n    }\n    $time['channel_scheduler'] = microtime(true) - $time['channel_scheduler'];\n    $chan->close();\n});\n\nSwoole\\Event::wait();\nvar_dump($time);\n$diff = $time['channel_raw'] - $time['splQueue'];\nvar_dump($diff);\n?>\n--EXPECTF--\narray(3) {\n  [\"splQueue\"]=>\n  float(%f)\n  [\"channel_raw\"]=>\n  float(%f)\n  [\"channel_scheduler\"]=>\n  float(%f)\n}\nfloat(%f)\n"
  },
  {
    "path": "tests/swoole_channel_coro/blocking_timeout.phpt",
    "content": "--TEST--\nswoole_channel_coro: blocking and timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nCo::set(['hook_flags' => 0]);\n\n$chan = new chan();\n\ngo(function () use ($chan){\n    $data = $chan->pop(0.5);\n    Assert::assert($data);\n    $data = $chan->pop(0.5);\n    Assert::false($data);\n});\n\ngo(function () use ($chan) {\n    sleep(1);\n    $chan->push(999955);\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_channel_coro/bug_1947.phpt",
    "content": "--TEST--\nswoole_channel_coro: channel by return value\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $foo = foo();\n    $ret = $foo->pop(0.001);\n    Assert::false($ret);\n});\nfunction foo()\n{\n    $chan = new \\Swoole\\Coroutine\\Channel();\n    go(function () use ($chan) {\n        // nothing\n    });\n    return $chan;\n}\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_channel_coro/bug_clear_timer.phpt",
    "content": "--TEST--\nswoole_channel_coro: the bug about timeout timer\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nexit(\"skip for select\");\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n// this sript will hanging up and remove out of tests\n// $chan = new chan(1);\n\n// go(function () use ($chan) {\n//     co::sleep(0.1);\n//     $chan->push('foo');\n// });\n\n// go(function () use ($chan) {\n//     $read = [\n//         $chan\n//     ];\n//     $write = [];\n//     $ret = chan::select($read, $write, 0.1);\n//     Assert::true($ret);\n//     Assert::same(count($read), 1);\n// });\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_channel_coro/chan_select_timeout.phpt",
    "content": "--TEST--\nswoole_channel_coro: coro channel select timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nexit(\"skip for select\");\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\n\n$chan = new co\\Channel(1);\n\ngo(function () use ($chan) {\n    $read_list = [$chan];\n    $write_list = null;\n    $result = chan::select($read_list, $write_list, 0.1);\n    Assert::false($result);\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_channel_coro/chan_stats.phpt",
    "content": "--TEST--\nswoole_channel_coro: coro channel stats\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$chan = new chan(10);\n\ngo(function () use ($chan) {\n    $chan->push(1);\n    $chan->push(2);\n    $chan->push(\"hello world\");\n    $chan->push([1, 3, 4, 4, 6]);\n    Assert::same($chan->stats()['queue_num'], 4);\n\n    $chan->pop();\n    $chan->pop();\n    $chan->pop();\n    $chan->pop();\n    Assert::same($chan->stats()['queue_num'], 0);\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_channel_coro/close.phpt",
    "content": "--TEST--\nswoole_channel_coro: coro channel stats\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    $chan = new \\chan(100);\n    $a = [];\n    $a['1'] = $chan;\n\n    go(function () use (&$chan) {\n        while (true) {\n            $data = $chan->pop();\n            if ($data == false) {\n                break;\n            }\n            print(\"chan get data :$data\\n\");\n        }\n        print(\"chan exit\\n\");\n    });\n\n    $frame = '11';\n    $chan->push($frame);\n    co::sleep(0.2);\n    print(\"chan close \" . json_encode($chan->stats()) . \"\\n\");\n    $chan->close();\n    co::sleep(0.2);\n    print(\"chan END\\n\");\n    unset($a['1']);\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nchan get data :11\nchan close {\"consumer_num\":1,\"producer_num\":0,\"queue_num\":0}\nchan exit\nchan END\n"
  },
  {
    "path": "tests/swoole_channel_coro/coro_wait.phpt",
    "content": "--TEST--\nswoole_channel_coro: coroutine wait\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\nskip_if_no_database();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Event;\nuse Swoole\\Http\\Server;\n\n$pm = new ProcessManager();\n\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        Assert::assert(!empty($data));\n        $json = json_decode($data, true);\n        Assert::assert(is_array($json));\n        Assert::true(isset($json['www.qq.com']));\n        Assert::greaterThan($json['www.qq.com'], 1024);\n        Assert::true(isset($json['www.163.com']));  \n        Assert::greaterThan($json['www.163.com'], 1024);\n        $pm->kill();\n    });\n    Event::wait();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->on('WorkerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('request', function ($req, $resp) {\n        $chan = new chan(2);\n        go(function () use ($chan) {\n            $cli = new Client('www.qq.com', 443, true);\n            $cli->set(['timeout' => 10]);\n            $cli->setHeaders([\n                'Host' => 'www.qq.com',\n                'User-Agent' => 'Chrome/49.0.2587.3',\n                'Accept' => 'text/html,application/xhtml+xml,application/xml',\n                'Accept-Encoding' => 'gzip',\n            ]);\n            $ret = $cli->get('/');\n            if ($ret) {\n                $chan->push(['www.qq.com' => strlen($cli->body)]);\n            } else {\n                $chan->push(['www.qq.com' => 0]);\n            }\n        });\n\n        go(function () use ($chan) {\n            $cli = new Client('www.163.com', 443, true);\n            $cli->set(['timeout' => 10]);\n            $cli->setHeaders([\n                'Host' => 'www.163.com',\n                'User-Agent' => 'Chrome/49.0.2587.3',\n                'Accept' => 'text/html,application/xhtml+xml,application/xml',\n                'Accept-Encoding' => 'gzip',\n            ]);\n            $ret = $cli->get('/');\n            if ($ret) {\n                $chan->push(['www.163.com' => strlen($cli->body)]);\n            } else {\n                $chan->push(['www.163.com' => 0]);\n            }\n        });\n\n        $result = [];\n        for ($i = 0; $i < 2; $i++) {\n            $result += $chan->pop();\n        }\n        $resp->end(json_encode($result));\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_channel_coro/discard.phpt",
    "content": "--TEST--\nswoole_channel_coro: discard\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Scheduler;\nuse Swoole\\Coroutine\\Channel;\n\n$scheduler = new Scheduler();\n$scheduler->add(function () {\n    $chan = new Channel(1);\n    $chan->push(1, -1);\n    var_dump('push success');\n    $chan->push(1, -1);\n});\n$scheduler->start();\nvar_dump('scheduler end');\n\n?>\n--EXPECTF--\nstring(12) \"push success\"\nstring(13) \"scheduler end\"\n[%s]\tWARNING\tChannel::~Channel() (ERRNO 10003): channel is destroyed, 1 producers will be discarded\n"
  },
  {
    "path": "tests/swoole_channel_coro/fibonacci.phpt",
    "content": "--TEST--\nswoole_channel_coro: fibonacci\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nexit(\"skip for select\");\n ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan();\n$c2 = new chan();\nfunction fibonacci($c1, $c2)\n{\n    go(function () use ($c1, $c2) {\n        $a = 0;\n        $b = 1;\n        while(1) {\n            $read_list = [$c2];\n            $write_list = [$c1];\n            $result = chan::select($read_list, $write_list, 2);\n            if ($write_list) {\n                $t = $a + $b;\n                $a = $b;\n                $b = $t;\n                $c1->push($a);\n            }\n            if ($read_list) {\n                $ret = $c2->pop();\n                if ($ret === 1) {\n                    return 1;\n                }\n            }\n        }\n    });\n}\n$num = 10;\ngo(function () use ($c1, $c2, $num) {\n    for ($i = 0; $i < $num; $i ++) {\n        $ret = $c1->pop();\n        echo \"fibonacci @$i $ret\\n\";\n    }\n    $c2->push(1);\n});\nfibonacci($c1, $c2);\n?>\n--EXPECT--\nfibonacci @0 1\nfibonacci @1 1\nfibonacci @2 2\nfibonacci @3 3\nfibonacci @4 5\nfibonacci @5 8\nfibonacci @6 13\nfibonacci @7 21\nfibonacci @8 34\nfibonacci @9 55\n"
  },
  {
    "path": "tests/swoole_channel_coro/http2.phpt",
    "content": "--TEST--\nswoole_channel_coro: http2 mode\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nfor ($c = MAX_CONCURRENCY; $c--;) {\n    go(function () {\n        for ($n = MAX_REQUESTS; $n--;) {\n            $recv = new Chan;\n            $send = new Chan;\n            $rand = get_safe_random(mt_rand(1, 1024));\n            go(function () use ($recv) {\n                co::sleep(0.001);\n                $recv->push(new stdClass()); // response\n            });\n            go(function () use ($send, $rand) {\n                $data = $send->pop();\n                if (Assert::assert($data === $rand)) {\n                    co::sleep(0.001);\n                    $send->push(true); // send ok\n                }\n            });\n            $ret = $send->push($rand);\n            Assert::assert($ret);\n            $response = $recv->pop();\n            Assert::isInstanceOf($response, stdClass::class);\n        }\n    });\n}\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_channel_coro/hybird_chan.phpt",
    "content": "--TEST--\nswoole_channel_coro: hybird channel\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nexit(\"skip for select\");\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan();\n$c2 = new chan(10);\n$num = 10;\ngo(function () use ($c2,$num) {\n    for ($i=0;$i<$num;$i++)\n    {\n        $ret = $c2->push(\"chan2-$i\");\n        echo \"chan 2 push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n});\ngo(function () use ($c1,$num) {\n    $read_list = [$c1];\n    $write_list = null;\n    $result = chan::select($read_list, $write_list, 2);\n    echo \"select resume res: \".var_export($result,1).\"\\n\";\n    if ($read_list)\n    {\n        foreach($read_list as $ch)\n        {\n            for ($i=0;$i<$num;$i++)\n            {\n                $ret = $ch->pop();\n                echo \"chan1 pop [#$i] ret:\".var_export($ret,1).\"\\n\";\n            }\n        }\n    }\n});\n\ngo(function () use ($c1,$num) {\n    echo \"chan1 push start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        if ($i == 2) {\n            echo \"start sleep\\n\";\n            co:sleep(1);\n            echo \"end sleep\\n\";\n        }\n        $ret = $c1->push(\"chan1-$i\");\n        echo \"chan1 push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n\n});\n\ngo(function () use ($c2,$num) {\n    echo \"chan2 pop start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        $ret = $c2->pop();\n        echo \"chan2 pop [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n});\necho \"main end\\n\";\n?>\n--EXPECT--\nchan 2 push [#0] ret:true\nchan 2 push [#1] ret:true\nchan 2 push [#2] ret:true\nchan 2 push [#3] ret:true\nchan 2 push [#4] ret:true\nchan 2 push [#5] ret:true\nchan 2 push [#6] ret:true\nchan 2 push [#7] ret:true\nchan 2 push [#8] ret:true\nchan 2 push [#9] ret:true\nchan1 push start\nchan2 pop start\nchan2 pop [#0] ret:'chan2-0'\nchan2 pop [#1] ret:'chan2-1'\nchan2 pop [#2] ret:'chan2-2'\nchan2 pop [#3] ret:'chan2-3'\nchan2 pop [#4] ret:'chan2-4'\nchan2 pop [#5] ret:'chan2-5'\nchan2 pop [#6] ret:'chan2-6'\nchan2 pop [#7] ret:'chan2-7'\nchan2 pop [#8] ret:'chan2-8'\nchan2 pop [#9] ret:'chan2-9'\nmain end\nselect resume res: true\nchan1 pop [#0] ret:'chan1-0'\nchan1 push [#0] ret:true\nchan1 pop [#1] ret:'chan1-1'\nchan1 push [#1] ret:true\nstart sleep\nend sleep\nchan1 pop [#2] ret:'chan1-2'\nchan1 push [#2] ret:true\nchan1 pop [#3] ret:'chan1-3'\nchan1 push [#3] ret:true\nchan1 pop [#4] ret:'chan1-4'\nchan1 push [#4] ret:true\nchan1 pop [#5] ret:'chan1-5'\nchan1 push [#5] ret:true\nchan1 pop [#6] ret:'chan1-6'\nchan1 push [#6] ret:true\nchan1 pop [#7] ret:'chan1-7'\nchan1 push [#7] ret:true\nchan1 pop [#8] ret:'chan1-8'\nchan1 push [#8] ret:true\nchan1 pop [#9] ret:'chan1-9'\nchan1 push [#9] ret:true\n"
  },
  {
    "path": "tests/swoole_channel_coro/hybird_chan2.phpt",
    "content": "--TEST--\nswoole_channel_coro: hybird channel select\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nexit(\"skip for select\");\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan();\n$c2 = new chan(10);\n$num = 10;\ngo(function () use ($c2,$num) {\n    for ($i=0;$i<$num;$i++)\n    {\n        $ret = $c2->push(\"chan2-$i\");\n        echo \"chan 2 push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n});\n\ngo(function () use ($c1,$c2,$num) {\n    $ori_list = $read_list = [$c1,$c2];\n    $write_list = null;\n    $result = chan::select($read_list, $write_list, 2);\n    echo \"select resume res: \".var_export($result,1).\"\\n\";\n\n    if ($ori_list)\n    {\n        foreach ($ori_list as $chan => $ch)\n        {\n            for ($i=0;$i<$num;$i++)\n            {\n                $ret = $ch->pop();\n                $chan_id = $chan + 1;\n                echo \"chan{$chan_id} pop [#$i] ret:\".var_export($ret,1).\"\\n\";\n            }\n        }\n    }\n});\n\ngo(function () use ($c1,$num) {\n    echo \"chan1 push start\\n\";\n    for ($i=0;$i<$num;$i++)\n    {\n        if ($i == 2) {\n            echo \"start sleep\\n\";\n            co:sleep(1);\n            echo \"end sleep\\n\";\n        }\n        $ret = $c1->push(\"chan1-$i\");\n        echo \"chan1 push [#$i] ret:\".var_export($ret,1).\"\\n\";\n    }\n\n});\necho \"main end\\n\";\n?>\n--EXPECT--\nchan 2 push [#0] ret:true\nchan 2 push [#1] ret:true\nchan 2 push [#2] ret:true\nchan 2 push [#3] ret:true\nchan 2 push [#4] ret:true\nchan 2 push [#5] ret:true\nchan 2 push [#6] ret:true\nchan 2 push [#7] ret:true\nchan 2 push [#8] ret:true\nchan 2 push [#9] ret:true\nselect resume res: true\nchan1 push start\nmain end\nchan1 pop [#0] ret:'chan1-0'\nchan1 push [#0] ret:true\nchan1 pop [#1] ret:'chan1-1'\nchan1 push [#1] ret:true\nstart sleep\nend sleep\nchan1 pop [#2] ret:'chan1-2'\nchan1 push [#2] ret:true\nchan1 pop [#3] ret:'chan1-3'\nchan1 push [#3] ret:true\nchan1 pop [#4] ret:'chan1-4'\nchan1 push [#4] ret:true\nchan1 pop [#5] ret:'chan1-5'\nchan1 push [#5] ret:true\nchan1 pop [#6] ret:'chan1-6'\nchan1 push [#6] ret:true\nchan1 pop [#7] ret:'chan1-7'\nchan1 push [#7] ret:true\nchan1 pop [#8] ret:'chan1-8'\nchan1 push [#8] ret:true\nchan1 pop [#9] ret:'chan1-9'\nchan2 pop [#0] ret:'chan2-0'\nchan2 pop [#1] ret:'chan2-1'\nchan2 pop [#2] ret:'chan2-2'\nchan2 pop [#3] ret:'chan2-3'\nchan2 pop [#4] ret:'chan2-4'\nchan2 pop [#5] ret:'chan2-5'\nchan2 pop [#6] ret:'chan2-6'\nchan2 pop [#7] ret:'chan2-7'\nchan2 pop [#8] ret:'chan2-8'\nchan2 pop [#9] ret:'chan2-9'\nchan1 push [#9] ret:true\n"
  },
  {
    "path": "tests/swoole_channel_coro/hybird_chan3.phpt",
    "content": "--TEST--\nswoole_channel_coro: hybird channel select\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$size = 2;\n$chan = new chan($size);\n$coro_num = $size * 2;\n\ngo(function () use ($size, $chan) {\n    for ($i=0; $i < $size; $i++) {\n        $chan->push($i);\n    }\n});\n\nfor ($i=0; $i < $coro_num; $i++) {\n    go(function () use ($i, $chan, $coro_num) {\n        for ($j = 0; $j < $coro_num; $j ++) {\n            $c = $chan->pop();\n            echo  \"@\".$j . \"->coro:\" . $i . \" pop chan id :\" . var_export($c, 1) . PHP_EOL;\n            co::sleep(0.2);\n            $ret = $chan->push($c);\n        }\n    });\n}\nSwoole\\Event::wait();\n?>\n--EXPECTF--\n@0->coro:%d pop chan id :%d\n@0->coro:%d pop chan id :%d\n@0->coro:%d pop chan id :%d\n@0->coro:%d pop chan id :%d\n@1->coro:%d pop chan id :%d\n@1->coro:%d pop chan id :%d\n@1->coro:%d pop chan id :%d\n@1->coro:%d pop chan id :%d\n@2->coro:%d pop chan id :%d\n@2->coro:%d pop chan id :%d\n@2->coro:%d pop chan id :%d\n@2->coro:%d pop chan id :%d\n@3->coro:%d pop chan id :%d\n@3->coro:%d pop chan id :%d\n@3->coro:%d pop chan id :%d\n@3->coro:%d pop chan id :%d\n"
  },
  {
    "path": "tests/swoole_channel_coro/lock.phpt",
    "content": "--TEST--\nswoole_channel_coro: lock\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nclass CoLock\n{\n    private $chan;\n\n    function __construct()\n    {\n        $chan = new chan(1);\n        $chan->push(true);\n        $this->chan = $chan;\n    }\n\n    function lock()\n    {\n        return $this->chan->pop();\n    }\n\n    function unlock()\n    {\n        return $this->chan->push(true);\n    }\n}\n\nclass Test\n{\n    static $num = 2;\n\n    static function process(CoLock $lock)\n    {\n        co::sleep(0.001);\n        //这里需要操作全局对象，有可能会有上下文的问题\n        //使用 chan 实现协程锁\n        $lock->lock();\n        if (Test::$num > 0) {\n            co::sleep(0.02);\n            Test::$num--;\n            $lock->unlock();\n        } else {\n            $lock->unlock();\n            echo \"fail\\n\";\n        }\n    }\n}\n\ngo(function () {\n    $lock = new CoLock;\n    $n = 3;\n    while ($n--) {\n        go('Test::process', $lock);\n    }\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nfail\n"
  },
  {
    "path": "tests/swoole_channel_coro/no_ctor.phpt",
    "content": "--TEST--\nswoole_channel_coro: no ctor\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nclass MyChan extends Swoole\\Coroutine\\Channel\n{\n    function __construct($size = null)\n    {\n\n    }\n}\n\n$pm = ProcessManager::exec(function () {\n    go(function () {\n        $chan = new MyChan(100);\n        $chan->pop();\n    });\n});\n\nAssert::contains($pm->getChildOutput(), \"must call constructor first\");\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_channel_coro/pool.phpt",
    "content": "--TEST--\nswoole_channel_coro: connection pool\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nclass RedisPool\n{\n    /**@var \\Swoole\\Coroutine\\Channel */\n    protected $pool;\n\n    /**\n     * RedisPool constructor.\n     * @param int $size max connections\n     */\n    public function __construct(int $size = MAX_CONCURRENCY_LOW)\n    {\n        $this->pool = new \\Swoole\\Coroutine\\Channel($size);\n        for ($i = 0; $i < $size; $i++) {\n            $redis = new \\redis();\n            $res = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT);\n            if ($res == false) {\n                throw new \\RuntimeException(\"failed to connect redis server.\");\n            } else {\n                $this->put($redis);\n            }\n        }\n    }\n\n    public function get(): \\redis\n    {\n        return $this->pool->pop();\n    }\n\n    public function put(\\redis $redis)\n    {\n        $this->pool->push($redis);\n    }\n\n    public function close(): void\n    {\n        $this->pool->close();\n        $this->pool = null;\n    }\n}\n\n$count = 0;\n\\Swoole\\Runtime::setHookFlags(SWOOLE_HOOK_ALL);\ngo(function () {\n    $pool = new RedisPool();\n    // max concurrency num is more than max connections\n    // but it's no problem, channel will help you with scheduling\n    for ($c = 0; $c < MAX_CONCURRENCY_MID; $c++) {\n        go(function () use ($pool, $c) {\n            for ($n = 0; $n < MAX_REQUESTS; $n++) {\n                $redis = $pool->get();\n                if (Assert::assert($redis->set(\"awesome-{$c}-{$n}\", 'swoole'))) {\n                    if (Assert::assert($redis->get(\"awesome-{$c}-{$n}\") === 'swoole')) {\n                        if (Assert::assert($redis->delete(\"awesome-{$c}-{$n}\"))) {\n                            global $count;\n                            $count++;\n                        }\n                    }\n                }\n                $pool->put($redis);\n            }\n        });\n    }\n});\n\nSwoole\\Event::wait();\nAssert::same($count, MAX_CONCURRENCY_MID * MAX_REQUESTS);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_channel_coro/pop_after_close.phpt",
    "content": "--TEST--\nswoole_channel_coro: pop after close\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Channel;\n\nuse function Swoole\\Coroutine\\run;\n\nrun(function () {\n    $chan = new Channel();\n\n    go(function () use ($chan) {\n        for ($i = 1; $i <= 3; $i++) {\n            if ($chan->push($i)) {\n                echo \"push ok\\n\";\n            }\n        }\n        $chan->close();\n    });\n\n    go(function () use ($chan) {\n        while (true) {\n            $data = $chan->pop();\n            var_dump($data);\n            if (!$data) {\n                break;\n            }\n        }\n    });\n});\n?>\n--EXPECT--\npush ok\npush ok\nint(1)\npush ok\nint(2)\nint(3)\nbool(false)\n"
  },
  {
    "path": "tests/swoole_channel_coro/pop_close1.phpt",
    "content": "--TEST--\nswoole_channel_coro: pop close 1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan();\n$c1->close();\n\ngo(function () use ($c1) {\n    $ret = $c1->pop();\n    echo \"pop ret:\".var_export($ret,1).\" error:\".$c1->errCode.\"\\n\";\n});\n?>\n--EXPECTF--\npop ret:false error:-2\n"
  },
  {
    "path": "tests/swoole_channel_coro/pop_timeout1.phpt",
    "content": "--TEST--\nswoole_channel_coro: pop timeout 1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan();\n\ngo(function () use ($c1) {\n    $ret = $c1->pop(1);\n    echo \"pop ret:\".var_export($ret,1).\" error:\".$c1->errCode.\"\\n\";\n\n});\n?>\n--EXPECTF--\npop ret:false error:-1\n"
  },
  {
    "path": "tests/swoole_channel_coro/pop_timeout2.phpt",
    "content": "--TEST--\nswoole_channel_coro: pop timeout 2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan();\n\ngo(function () use ($c1) {\n    $ret = $c1->pop(1);\n    echo \"pop ret:\".var_export($ret,1).\" error:\".$c1->errCode.\"\\n\";\n\n});\n\ngo(function () use ($c1) {\n    co::sleep(2);\n    echo \"sleep 2\\n\";\n    $ret = $c1->push(\"chan-1\");\n    echo \"chan push ret:\".var_export($ret,1).\"\\n\";\n});\n?>\n--EXPECTF--\npop ret:false error:-1\nsleep 2\nchan push ret:true\n"
  },
  {
    "path": "tests/swoole_channel_coro/pop_timeout3.phpt",
    "content": "--TEST--\nswoole_channel_coro: pop timeout 3\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan();\n\ngo(function () use ($c1) {\n    $ret = $c1->pop(1);\n    echo \"pop ret:\".var_export($ret,1).\" error:\".$c1->errCode.\"\\n\";\n});\n\ngo(function () use ($c1) {\n    co::sleep(0.5);\n    $ret = $c1->push(\"chan-1\");\n    echo \"chan push ret:\".var_export($ret,1).\" error:\".$c1->errCode.\"\\n\";\n});\nSwoole\\Event::wait();\n?>\n--EXPECTF--\npop ret:'chan-1' error:0\nchan push ret:true error:0\n"
  },
  {
    "path": "tests/swoole_channel_coro/pop_timeout4.phpt",
    "content": "--TEST--\nswoole_channel_coro: pop timeout 4\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan();\n\ngo(function () use ($c1) {\n    $ret = $c1->pop(1);\n    echo \"pop ret:\".var_export($ret,1).\" error:\".$c1->errCode.\"\\n\";\n\n});\n\ngo(function () use ($c1) {\n    $ret = $c1->push(\"chan-1\");\n    echo \"chan push ret:\".var_export($ret,1).\" error:\".$c1->errCode.\"\\n\";\n});\n?>\n--EXPECTF--\npop ret:'chan-1' error:0\nchan push ret:true error:0\n"
  },
  {
    "path": "tests/swoole_channel_coro/pop_timeout5.phpt",
    "content": "--TEST--\nswoole_channel_coro: pop timeout hanging up\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nexit(\"skip for hanging up\");\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan();\n\ngo(function () use ($c1) {\n    $ret = $c1->pop();\n    echo \"pop ret:\".var_export($ret,1).\" error:\".$c1->errCode.\"\\n\";\n});\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_channel_coro/pop_timeout6.phpt",
    "content": "--TEST--\nswoole_channel_coro: pop timeout 6\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan();\n\ngo(function () use ($c1) {\n    $ret = $c1->pop();\n    echo \"pop ret:\".var_export($ret,1).\" error:\".$c1->errCode.\"\\n\";\n\n});\n\ngo(function () use ($c1) {\n    $ret = $c1->push(\"chan-1\");\n    echo \"chan push ret:\".var_export($ret,1).\" error:\".$c1->errCode.\"\\n\";\n});\n?>\n--EXPECT--\npop ret:'chan-1' error:0\nchan push ret:true error:0\n"
  },
  {
    "path": "tests/swoole_channel_coro/pop_timeout7.phpt",
    "content": "--TEST--\nswoole_channel_coro: pop timeout 7\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan();\n\ngo(function () use ($c1) {\n    $ret = $c1->pop(0.5);\n    echo \"pop ret:\".var_export($ret,1).\" error:\".$c1->errCode.\"\\n\";\n\n    $ret = $c1->pop(1);\n    echo \"pop ret:\".var_export($ret,1).\"\\n\";\n\n});\n\ngo(function () use ($c1) {\n    co::sleep(1);\n    echo \"sleep 1\\n\";\n    $ret = $c1->push(\"chan-1\");\n    echo \"chan push ret:\".var_export($ret,1).\"\\n\";\n});\nSwoole\\Event::wait();\n?>\n--EXPECTF--\npop ret:false error:-1\nsleep 1\npop ret:'chan-1'\nchan push ret:true\n"
  },
  {
    "path": "tests/swoole_channel_coro/pop_timeout8.phpt",
    "content": "--TEST--\nswoole_channel_coro: pop timeout 8\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan();\n\nclass T {\n    public $c;\n    function __construct()\n    {\n        $this->c = new chan();\n        echo \"__construct\\n\";\n    }\n\n    function __destruct()\n    {\n        go(function(){\n            echo \"__destruct\\n\";\n            $ret = $this->c->pop(0.5);\n            echo \"pop ret:\".var_export($ret,1).\" error:\".$this->c->errCode.\"\\n\";\n        });\n\n    }\n}\n$t = new T();\nunset($t);\n?>\n--EXPECTF--\n__construct\n__destruct\npop ret:false error:-1\n"
  },
  {
    "path": "tests/swoole_channel_coro/push_close1.phpt",
    "content": "--TEST--\nswoole_channel_coro: push close 1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c1 = new chan();\n$c1->close();\n\ngo(function () use ($c1) {\n    $ret = $c1->push(1);\n    echo \"push ret:\".var_export($ret,1).\" error:\".$c1->errCode.\"\\n\";\n});\n?>\n--EXPECTF--\npush ret:false error:-2\n"
  },
  {
    "path": "tests/swoole_channel_coro/push_timeout1.phpt",
    "content": "--TEST--\nswoole_channel_coro: pop timeout 1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$channel = new Swoole\\Coroutine\\Channel(1);\ngo(function () use ($channel) {\n    $ret = $channel->push('foo', 0.001);\n    Assert::true($ret);\n    $ret = $channel->push('foo', 0.001);\n    Assert::true($ret);\n});\nfor ($n = MAX_REQUESTS; $n--;) {\n    go(function () use ($channel) {\n        $ret = $channel->push('foo', 0.001);\n        Assert::false($ret);\n        Assert::same($channel->errCode, SWOOLE_CHANNEL_TIMEOUT);\n    });\n}\ngo(function () use ($channel) {\n    $ret = $channel->pop();\n    Assert::same($ret, 'foo');\n});\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_channel_coro/push_timeout2.phpt",
    "content": "--TEST--\nswoole_channel_coro: pop timeout 2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$chan = new \\Swoole\\Coroutine\\Channel(1);\n\ngo(function () use ($chan) {\n    co::sleep(0.5);\n    Assert::same($chan->pop(0.1), 1);\n    Assert::same($chan->pop(0.1), 'swoole');\n});\n\ngo(function () use ($chan) {\n    Assert::assert($chan->push(1, 0.1));\n    Assert::assert(!$chan->push(2, 0.1));\n    Assert::assert($chan->push('swoole', 1));\n});\n\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_channel_coro/push_timeout3.phpt",
    "content": "--TEST--\nswoole_channel_coro: push timeout 3\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$channel = new Swoole\\Coroutine\\Channel(1);\n\ngo(function () use ($channel) {\n    Assert::assert($channel->push(1, 0.1));\n    Assert::assert(!$channel->push(1, 0.1));\n});\n\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_channel_coro/push_timeout4.phpt",
    "content": "--TEST--\nswoole_channel_coro: push timeout 3\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$channel = new Swoole\\Coroutine\\Channel(1);\n\ngo(function () use ($channel) {\n    Assert::assert($channel->push(1, 0.1));\n    Assert::assert($channel->push(1, 0.1));\n});\n\ngo(function () use ($channel) {\n    Assert::same($channel->pop(0.1), 1);\n});\n\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_channel_coro/type.phpt",
    "content": "--TEST--\nswoole_channel_coro: type test\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$exit_status_list = [\n    null,\n    1,\n    1.1,\n    'exit',\n    ['exit' => 'ok'],\n    (object)['exit' => 'ok'],\n    STDIN\n];\n\n\n$chan = new Swoole\\Coroutine\\Channel;\n\ngo(function () use ($chan, $exit_status_list)\n{\n    foreach ($exit_status_list as $val)\n    {\n        Assert::assert($chan->push($val));\n    }\n});\n\ngo(function () use ($chan, $exit_status_list)\n{\n    foreach ($exit_status_list as $_val)\n    {\n        $val = $chan->pop();\n        Assert::same($val, $_val);\n    }\n});\n\n\nSwoole\\Event::wait();\n\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_client_async/base.phpt",
    "content": "--TEST--\nswoole_client_async: Swoole\\Async\\Client connect & send & close\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/simple_server.php\";\nstart_server($simple_tcp_server, TCP_SERVER_HOST, TCP_SERVER_PORT);\n\nsuicide(5000);\n\n$cli = new Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\n\n$cli->on(\"connect\", function(Swoole\\Async\\Client $cli) {\n    Assert::true($cli->isConnected());\n    $cli->send(RandStr::gen(1024, RandStr::ALL));\n});\n\n$cli->on(\"receive\", function(Swoole\\Async\\Client $cli, $data){\n    $recv_len = strlen($data);\n    // print(\"receive: len $recv_len\");\n    $cli->send(RandStr::gen(1024, RandStr::ALL));\n    $cli->close();\n    Assert::false($cli->isConnected());\n});\n\n$cli->on(\"error\", function(Swoole\\Async\\Client $cli) {\n    print(\"error\");\n});\n\n$cli->on(\"close\", function(Swoole\\Async\\Client $cli) {\n    Swoole\\Event::exit();\n    echo \"SUCCESS\";\n});\n\n$cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT, 0.2);\nSwoole\\Event::wait();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_client_async/big_package_memory_leak.phpt",
    "content": "--TEST--\nswoole_client_async: big_package_memory_leak\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nini_set('swoole.display_errors', false);\n\n$port = get_one_free_port();\n$tcp_server = __DIR__ . \"/../include/api/swoole_server/tcp_serv.php\";\n$closeServer = start_server($tcp_server, '127.0.0.1', $port);\n\n$mem = memory_get_usage(true);\nini_set(\"memory_limit\", \"100m\");\n$cli = new Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\n$cli->set(['socket_buffer_size' => 2 * 1024 * 1024]);\n$cli->on(\"connect\", function (Swoole\\Async\\Client $cli) {\n    $cli->send(str_repeat(\"\\0\", 1024 * 1024 * 1.9));\n});\n$cli->on(\"receive\", function (Swoole\\Async\\Client $cli, $data) {\n    $cli->send($data);\n});\n$cli->on(\"error\", function (Swoole\\Async\\Client $cli) {\n    echo \"error\";\n});\n$cli->on(\"close\", function (Swoole\\Async\\Client $cli) use ($closeServer) {\n    echo \"closed\\n\";\n    $closeServer();\n});\n$cli->connect('127.0.0.1', $port);\nAssert::same(memory_get_usage(true), $mem);\necho \"SUCCESS\\n\";\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nSUCCESS\nclosed\n"
  },
  {
    "path": "tests/swoole_client_async/buffer_full.phpt",
    "content": "--TEST--\nswoole_client_async: onBufferFull & onBufferEmpty\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Async\\Client;\n\n$port = get_one_free_port();\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($port) {\n    Co::set(['log_level' => 5, 'display_errors' => false]);\n    $client = new Client(SWOOLE_SOCK_TCP);\n    $client->set(['socket_buffer_size' => 1 * 1024 * 1024,]);\n    $client->buffer = array();\n\n    $countBufferEmpty = 0;\n    $countBufferFull = 0;\n\n    $client->on(\"connect\", function (Client $cli) {\n        for ($i = 0; $i < 1024; $i++) {\n            $data = str_repeat('A', 8192);\n            if ($cli->send($data) === false and $cli->errCode == 1008) {\n                $cli->buffer[] = $data;\n            }\n        }\n    });\n\n    $client->on(\"receive\", function (Client $cli, $data) {\n        $cli->send(pack('N', 8) . 'shutdown');\n        $cli->close();\n        Assert::same($data, md5_file(TEST_IMAGE));\n    });\n\n    $client->on(\"error\", function ($cli) {\n        echo \"Connect failed\\n\";\n    });\n\n    $client->on(\"close\", function ($cli) {\n\n    });\n\n    $client->on(\"bufferEmpty\", function (Client $cli) use (&$countBufferEmpty) {\n        $countBufferEmpty++;\n        foreach ($cli->buffer as $k => $data) {\n            if ($cli->send($data) === false and $cli->errCode == 1008) {\n                break;\n            } else {\n                unset($cli->buffer[$k]);\n            }\n        }\n        if (count($cli->buffer) == 0) {\n            $cli->close();\n        }\n    });\n\n    $client->on(\"bufferFull\", function (Client $cli) use (&$countBufferFull) {\n        $countBufferFull++;\n    });\n\n    $client->connect(TCP_SERVER_HOST, $port, 0.5);\n    Swoole\\Event::wait();\n\n    Assert::greaterThanEq($countBufferEmpty, 1);\n    Assert::greaterThanEq($countBufferFull, 1);\n};\n\n$pm->childFunc = function () use ($pm, $port) {\n    $socket = stream_socket_server(\"tcp://0.0.0.0:{$port}\", $errno, $errstr) or die(\"$errstr ($errno)<br />\\n\");\n    $pm->wakeup();\n    while ($conn = stream_socket_accept($socket)) {\n        for ($i = 0; $i < 4; $i++) {\n            usleep(500000);\n            for ($j = 0; $j < 256; $j++) {\n                $data = fread($conn, 8192);\n            }\n        }\n        fclose($conn);\n        break;\n    }\n    fclose($socket);\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_async/connect_dns.phpt",
    "content": "--TEST--\nswoole_client_async: connect & dns\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$cli = new Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\n\n$cli->on(\"connect\", function (Swoole\\Async\\Client $cli) {\n    Assert::true($cli->isConnected());\n    $cli->send(\"GET / HTTP/1.1\\r\\nHost: www.baidu.com\\r\\nUser-Agent: curl/7.50.1-DEV\\r\\nAccept: */*\\r\\n\\r\\n\");\n});\n\n$cli->on(\"receive\", function (Swoole\\Async\\Client $cli, $data) {\n    Assert::assert(strlen($data) > 0);\n    $cli->close();\n    Assert::false($cli->isConnected());\n    echo \"DONE\\n\";\n});\n\n$cli->on(\"error\", function (Swoole\\Async\\Client $cli) {\n    echo \"ERROR\\n\";\n});\n\n$cli->on(\"close\", function (Swoole\\Async\\Client $cli) {\n    echo \"SUCCESS\\n\";\n});\n\n$cli->connect(\"www.baidu.com\", 80, 2.0);\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nSUCCESS\nDONE\n"
  },
  {
    "path": "tests/swoole_client_async/connect_refuse.phpt",
    "content": "--TEST--\nswoole_client_async: connect refuse\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$cli = new Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\n$cli->on(\"connect\", function(Swoole\\Async\\Client $cli) {\n    Assert::true(false, 'never here');\n});\n$cli->on(\"receive\", function(Swoole\\Async\\Client $cli, $data) {\n    Assert::true(false, 'never here');\n});\n$cli->on(\"error\", function(Swoole\\Async\\Client $cli) { echo \"error\\n\"; });\n$cli->on(\"close\", function(Swoole\\Async\\Client $cli) { echo \"close\\n\"; });\n\n$cli->connect('127.0.0.1', 65535);\n?>\n--EXPECT--\nerror\n"
  },
  {
    "path": "tests/swoole_client_async/connect_refuse_udg.phpt",
    "content": "--TEST--\nswoole_client_async: connect refuse with unix dgram\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$cli = new Swoole\\Async\\Client(SWOOLE_SOCK_UNIX_DGRAM);\n$cli->on(\"connect\", function(Swoole\\Async\\Client $cli) {\n    Assert::true(false, 'never here');\n});\n$cli->on(\"receive\", function(Swoole\\Async\\Client $cli, $data) {\n    Assert::true(false, 'never here');\n});\n$cli->on(\"error\", function(Swoole\\Async\\Client $cli) { echo \"error\\n\"; });\n$cli->on(\"close\", function(Swoole\\Async\\Client $cli) { echo \"close\\n\"; });\n\n@$cli->connect(\"/test.sock\", 0, 0.5, 1);\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nerror\n"
  },
  {
    "path": "tests/swoole_client_async/connect_refuse_unix.phpt",
    "content": "--TEST--\nswoole_client_async: connect refuse with unix stream\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$cli = new Swoole\\Async\\Client(SWOOLE_SOCK_UNIX_STREAM);\n$cli->on(\"connect\", function(Swoole\\Async\\Client $cli) {\n    Assert::true(false, 'never here');\n});\n$cli->on(\"receive\", function(Swoole\\Async\\Client $cli, $data) {\n    Assert::true(false, 'never here');\n});\n$cli->on(\"error\", function(Swoole\\Async\\Client $cli) { echo \"error\\n\"; });\n$cli->on(\"close\", function(Swoole\\Async\\Client $cli) { echo \"close\\n\"; });\n\n@$cli->connect(\"/test.sock\");\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nerror\n"
  },
  {
    "path": "tests/swoole_client_async/connect_timeout.phpt",
    "content": "--TEST--\nswoole_client_async: connect_host_not_found\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$start = microtime(true);\n\n$cli = new Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\n$cli->on(\"connect\", function(Swoole\\Async\\Client $cli) {\n    Assert::true(false, 'never here');\n});\n$cli->on(\"receive\", function(Swoole\\Async\\Client $cli, $data) {\n    Assert::true(false, 'never here');\n});\n$cli->on(\"error\", function(Swoole\\Async\\Client $cli) {\n    echo \"error\\n\";\n});\n$cli->on(\"close\", function(Swoole\\Async\\Client $cli) {\n    echo \"close\\n\";\n});\n\n$cli->connect(\"192.0.0.1\", 9000, 0.1);\n?>\n--EXPECT--\nerror\n"
  },
  {
    "path": "tests/swoole_client_async/connect_twice.phpt",
    "content": "--TEST--\nswoole_client_async: connect twice\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/../include/api/swoole_client/connect_twice.php';\n?>\n--EXPECT--\nerror\n"
  },
  {
    "path": "tests/swoole_client_async/enableSSL.phpt",
    "content": "--TEST--\nswoole_client_async: enableSSL\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$cli = new Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\n\n$cli->on(\"connect\", function (Swoole\\Async\\Client $cli) {\n    Assert::true($cli->isConnected());\n    echo 'connected' . PHP_EOL;\n    $cli->enableSSL(function ($cli) {\n        echo \"SSL READY\\n\";\n        $cli->send(\"GET / HTTP/1.1\\r\\nHost: www.baidu.com\\r\\nUser-Agent: curl/7.50.1-DEV\\r\\nAccept: */*\\r\\n\\r\\n\");\n    });\n});\n\n$cli->on(\"receive\", function (Swoole\\Async\\Client $cli, $data) {\n    Assert::assert(strlen($data) > 0);\n    Assert::contains($data, 'www.baidu.com');\n    $cli->close();\n    Assert::false($cli->isConnected());\n    echo \"DONE\\n\";\n});\n\n$cli->on(\"error\", function (Swoole\\Async\\Client $cli) {\n    echo \"ERROR\\n\";\n});\n\n$cli->on(\"close\", function (Swoole\\Async\\Client $cli) {\n    echo \"SUCCESS\\n\";\n});\n\n$cli->connect(\"www.baidu.com\", 443, 2.0);\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nconnected\nSSL READY\nSUCCESS\nDONE\n"
  },
  {
    "path": "tests/swoole_client_async/enableSSL_bad_callback.phpt",
    "content": "--TEST--\nswoole_client_async: enableSSL with bad callback\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$cli = new Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\ntry {\n    $res = $cli->enableSSL();\n} catch (Exception $e) {\n    Assert::contains($e->getMessage(), 'require `onSslReady` callback');\n}\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_client_async/enableSSL_before_connect.phpt",
    "content": "--TEST--\nswoole_client_async: enableSSL before connect\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$cli = new Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\n$res = $cli->enableSSL(function ($cli) {\n    echo \"SSL READY\\n\";\n    $cli->send(\"GET / HTTP/1.1\\r\\nHost: www.baidu.com\\r\\nUser-Agent: curl/7.50.1-DEV\\r\\nAccept: */*\\r\\n\\r\\n\");\n});\nAssert::false($res);\n\n?>\n--EXPECTF--\nWarning: Swoole\\Async\\Client::enableSSL(): client is not connected to server in %s on line %d\n"
  },
  {
    "path": "tests/swoole_client_async/eof.phpt",
    "content": "--TEST--\nswoole_client_async: eof protocol [async]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$port = get_one_free_port();\n$pm->parentFunc = function ($pid) use ($port) {\n    $client = new Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\n    $client->set(['open_eof_check' => true, 'open_eof_split' => true, \"package_eof\" => \"\\r\\n\\r\\n\"]);\n\n    $client->on(\"connect\", function (Swoole\\Async\\Client $cli) {\n        $cli->send(\"recv\\r\\n\\r\\n\");\n    });\n\n    $client->on(\"receive\", function (Swoole\\Async\\Client $cli, $pkg) use ($pid) {\n        static $i = 0;\n        $i++;\n\n        Assert::assert($pkg != false);\n        Assert::assert(str_ends_with($pkg, \"\\r\\n\\r\\n\"));\n\n        //小包\n        if ($i <= 1000) {\n            Assert::assert($pkg and strlen($pkg) <= 2048);\n            if ($i == 1000) {\n                echo \"SUCCESS\\n\";\n            }\n        } //慢速发送\n        elseif ($i <= 1100) {\n            Assert::assert($pkg and strlen($pkg) <= 8192);\n            if ($i == 1100) {\n                echo \"SUCCESS\\n\";\n            }\n        } //大包\n        else {\n            $_pkg = unserialize(substr($pkg, 0, strlen($pkg) - 4));\n            Assert::assert(is_array($_pkg));\n            Assert::same($_pkg['i'], $i - 1100 - 1);\n            Assert::same(md5($_pkg['data']), $_pkg['md5']);\n            Assert::lengthBetween($_pkg['data'], 20000, 256 * 1024 * 1.5);\n            if ($i == 2100) {\n                echo \"SUCCESS\\n\";\n                $cli->close();\n                Swoole\\Process::kill($pid);\n            }\n        }\n    });\n\n    $client->on(\"error\", function (Swoole\\Async\\Client $cli) {\n        echo \"ERROR\\n\";\n    });\n\n    $client->on(\"close\", function (Swoole\\Async\\Client $cli) {\n        echo \"CLOSE\\n\";\n        Swoole\\Event::exit();\n    });\n\n    if (!$client->connect('127.0.0.1', $port, 0.5, 0)) {\n        echo \"Over flow. errno=\" . $client->errCode;\n        die(\"\\n\");\n    }\n};\n\n$pm->childFunc = function () use ($pm, $port) {\n    $serv = new Swoole\\Server('127.0.0.1', $port, SWOOLE_BASE);\n    $serv->set(array(\n        'package_eof' => \"\\r\\n\\r\\n\",\n        'open_eof_check' => true,\n        'open_eof_split' => true,\n        'package_max_length' => 1024 * 1024 * 2,\n        'socket_buffer_size' => 128 * 1024 * 1024,\n        'worker_num' => 1,\n        'log_file' => TEST_LOG_FILE,\n        'send_yield' => true,\n    ));\n    $serv->on(\"WorkerStart\", function (\\Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n        //小包\n        for ($i = 0; $i < 1000; $i++) {\n            $serv->send($fd, str_repeat('A', rand(100, 2000)) . \"\\r\\n\\r\\n\");\n        }\n        //慢速发送\n        for ($i = 0; $i < 100; $i++) {\n            $serv->send($fd, str_repeat('A', rand(1000, 2000)));\n            usleep(rand(10000, 50000));\n            $serv->send($fd, str_repeat('A', rand(2000, 4000)) . \"\\r\\n\\r\\n\");\n        }\n        //大包\n        for ($i = 0; $i < 1000; $i++) {\n            $data = base64_encode(random_bytes(random_int(20000, 256 * 1024)));\n            $md5 = md5($data);\n            $serv->send($fd, serialize(['i' => $i, 'md5' => $md5, 'data' => $data]) . \"\\r\\n\\r\\n\");\n        }\n    });\n    $serv->start();\n};\n$pm->async = true;\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\nSUCCESS\nSUCCESS\nCLOSE\n"
  },
  {
    "path": "tests/swoole_client_async/eof_close.phpt",
    "content": "--TEST--\nswoole_client_async: eof protocol [async] [close]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\n    $client->set(['open_eof_check' => true, 'open_eof_split' => true, \"package_eof\" => \"\\r\\n\\r\\n\"]);\n\n    $client->on(\"connect\", function (Swoole\\Async\\Client $cli) {\n        $cli->send(\"recv\\r\\n\\r\\n\");\n    });\n\n    $client->on(\"receive\", function (Swoole\\Async\\Client $cli, $pkg) use ($pid, $pm) {\n        echo \"RECEIVED\\n\";\n        $cli->close();\n        $pm->kill();\n    });\n\n    $client->on(\"error\", function (Swoole\\Async\\Client $cli) {\n        print(\"error\");\n    });\n\n    $client->on(\"close\", function (Swoole\\Async\\Client $cli) {\n        echo \"CLOSED\\n\";\n    });\n\n    if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) {\n        echo \"Over flow. errno=\" . $client->errCode;\n        die(\"\\n\");\n    }\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set(array(\n        'package_eof' => \"\\r\\n\\r\\n\",\n        'open_eof_check' => true,\n        'open_eof_split' => true,\n        'package_max_length' => 1024 * 1024 * 2, //2M\n        'socket_buffer_size' => 128 * 1024 * 1024,\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on(\"WorkerStart\", function (\\Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n        $serv->send($fd, str_repeat('A', rand(100, 2000)) . \"\\r\\n\\r\\n\");\n    });\n    $serv->start();\n};\n$pm->async = true;\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nRECEIVED\nCLOSED\n"
  },
  {
    "path": "tests/swoole_client_async/getSocket_bug.phpt",
    "content": "--TEST--\nswoole_client_async: getSocket debug\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nif (method_exists('Swoole\\Async\\Client', 'getSocket') === false) {\n    exit(\"require sockets supports.\");\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/simple_server.php\";\nstart_server($simple_tcp_server, TCP_SERVER_HOST, TCP_SERVER_PORT);\n\n$timer = suicide(1000);\n\n$cli = new Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\n\n$cli->on(\"connect\", function (Swoole\\Async\\Client $cli) use ($timer) {\n    // getSocket BUG\n    $cli->getSocket();\n    $cli->getSocket();\n\n    echo \"SUCCESS\\n\";\n    /*\n    @$cli->getSocket();\n    $err = error_get_last();\n    Assert::same($err[\"message\"], \"swoole_client_async::getSocket(): unable to obtain socket family Error: Bad file descriptor[9].\");\n    */\n    $cli->close();\n    Swoole\\Timer::clear($timer);\n});\n\n$cli->on(\"receive\", function (Swoole\\Async\\Client $cli, $data) {\n});\n$cli->on(\"error\", function (Swoole\\Async\\Client $cli) {\n    echo \"error\\n\";\n});\n$cli->on(\"close\", function (Swoole\\Async\\Client $cli) {\n});\n\n$cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT, 1);\nSwoole\\Event::wait();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_client_async/getpeername.phpt",
    "content": "--TEST--\nswoole_client_async: getsockpeername\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) {\n    $cli = new \\Swoole\\Async\\Client(SWOOLE_SOCK_UDP);\n\n    $cli->on(\"connect\", function (\\Swoole\\Async\\Client $cli) {\n        Assert::true($cli->isConnected());\n        $cli->send(\"test\");\n    });\n\n    $cli->on(\"receive\", function (\\Swoole\\Async\\Client $cli, $data) {\n        $i = $cli->getpeername();\n        Assert::assert($i !== false);\n        $cli->send('shutdown');\n        $cli->close();\n    });\n\n    $cli->on(\"close\", function (\\Swoole\\Async\\Client $cli) {\n        echo \"SUCCESS\\n\";\n    });\n\n    $r = $cli->connect(UDP_SERVER_HOST, UDP_SERVER_PORT, 1);\n    Assert::assert($r);\n    Swoole\\Event::wait();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new \\Swoole\\Server(UDP_SERVER_HOST, UDP_SERVER_PORT, SWOOLE_BASE, SWOOLE_SOCK_UDP);\n    $serv->set([\"worker_num\" => 1, 'log_file' => '/dev/null']);\n    $serv->on(\"WorkerStart\", function (\\Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"Packet\", function (\\Swoole\\Server $serv, $data, $clientInfo) {\n        if (trim($data) == 'shutdown') {\n            $serv->shutdown();\n            return;\n        }\n        $serv->sendto($clientInfo['address'], $clientInfo['port'], $data);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_client_async/getsockname.phpt",
    "content": "--TEST--\nswoole_client_async: Swoole\\Async\\Client getsockname\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/simple_server.php\";\nstart_server($simple_tcp_server, TCP_SERVER_HOST, TCP_SERVER_PORT);\n\n$timer = suicide(5000);\n\n$cli = new \\Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\n\n$cli->on(\"connect\", function (Swoole\\Async\\Client $cli) use ($timer) {\n    Assert::true($cli->isConnected());\n\n    $i = $cli->getsockname();\n    Assert::assert($i !== false);\n    Assert::same($i[\"host\"], '127.0.0.1');\n\n    $cli->close();\n    Swoole\\Timer::clear($timer);\n});\n\n$cli->on(\"receive\", function (Swoole\\Async\\Client $cli, $data) {\n});\n\n$cli->on(\"error\", function (Swoole\\Async\\Client $cli) {\n    echo \"error\";\n});\n\n$cli->on(\"close\", function (Swoole\\Async\\Client $cli) {\n    echo \"SUCCESS\";\n    Swoole\\Event::exit();\n});\n\n$cli->connect(TCP_SERVER_HOST, TCP_SERVER_PORT, 1);\nSwoole\\Event::wait();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_client_async/length_protocol.phpt",
    "content": "--TEST--\nswoole_client_async: length protocol [async]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\n    $client->set([\n        'open_length_check' => true,\n        'package_max_length' => 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n    ]);\n\n    $client->on(\"connect\", function (Swoole\\Async\\Client $cli) {\n        $cli->send(\"recv\\r\\n\\r\\n\");\n    });\n\n    $client->on(\"receive\", function (Swoole\\Async\\Client $cli, $pkg) use ($pid) {\n        static $i = 0;\n        $i++;\n\n        //小包\n        if ($i <= 1000) {\n            Assert::assert($pkg and strlen($pkg) <= 2048);\n            if ($i == 1000) {\n                echo \"SUCCESS\\n\";\n            }\n            return;\n        } //慢速发送\n        elseif ($i <= 1100) {\n            Assert::assert($pkg and strlen($pkg) <= 8192);\n            if ($i == 1100) {\n                echo \"SUCCESS\\n\";\n            }\n            return;\n        } //大包\n        else {\n            Assert::assert($pkg != false);\n            $_pkg = unserialize(substr($pkg, 4));\n            Assert::assert(is_array($_pkg));\n            Assert::same($_pkg['i'], $i - 1100 - 1);\n            Assert::lengthBetween($_pkg['data'], 20000, 256 * 1024);\n            if ($i == 2100) {\n                echo \"SUCCESS\\n\";\n                $cli->close();\n                Swoole\\Process::kill($pid);\n            }\n        }\n    });\n\n    $client->on(\"error\", function (Swoole\\Async\\Client $cli) {\n        print(\"error\");\n    });\n\n    $client->on(\"close\", function (Swoole\\Async\\Client $cli) {\n        Swoole\\Event::exit();\n    });\n\n    if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) {\n        echo \"Over flow. errno=\" . $client->errCode;\n        die(\"\\n\");\n    }\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set(array(\n        \"worker_num\" => 1,\n        'send_yield' => true,\n        'log_file' => '/tmp/swoole.log',\n    ));\n    $serv->on(\"WorkerStart\", function (\\Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n        //小包\n        for ($i = 0; $i < 1000; $i++) {\n            $data = str_repeat('A', rand(100, 2000));\n            $serv->send($fd, pack('N', strlen($data)) . $data);\n        }\n        //慢速发送\n        for ($i = 0; $i < 100; $i++) {\n            $data = str_repeat('A', rand(3000, 6000));\n            $n = rand(1000, 2000);\n            $serv->send($fd, pack('N', strlen($data)) . substr($data, 0, $n));\n            usleep(rand(10000, 50000));\n            $serv->send($fd, substr($data, $n));\n        }\n        //大包\n        for ($i = 0; $i < 1000; $i++) {\n            $data = serialize(['i' => $i, 'data' => str_repeat('A', rand(20000, 256 * 1024))]);\n            $serv->send($fd, pack('N', strlen($data)) . $data);\n        }\n    });\n    $serv->start();\n};\n\n$pm->async = true;\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\nSUCCESS\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_client_async/length_protocol_func.phpt",
    "content": "--TEST--\nswoole_client_async: length protocol func\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\n    $client->set([\n        'open_length_check' => true,\n        'package_max_length' => 1024 * 1024,\n        'package_length_func' => function ($data) {\n            $n = strpos($data, '|');\n            if ($n == false) {\n                return -1;\n            } else {\n                return intval(substr($data, 0, $n)) + $n + 1;\n            }\n        },\n    ]);\n    $client->on(\"connect\", function (Swoole\\Async\\Client $cli) {\n        $int = rand(1000, 5000);\n        $data = json_encode(['data' => RandStr::gen($int), 'index' => 2, 'len' => $int]);\n        $cli->send(pack('N', strlen($data) + 4) . $data);\n    });\n\n    $client->on(\"receive\", function (Swoole\\Async\\Client $cli, $pkg) use ($pid) {\n        Assert::assert($pkg != false and strlen($pkg) > 100);\n        Swoole\\Process::kill($pid);\n        $cli->close();\n    });\n\n    $client->on(\"error\", function (Swoole\\Async\\Client $cli) {\n        print(\"error\");\n    });\n\n    $client->on(\"close\", function (Swoole\\Async\\Client $cli) {\n        Swoole\\Event::exit();\n    });\n\n    if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) {\n        echo \"Over flow. errno=\" . $client->errCode;\n        die(\"\\n\");\n    }\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'package_max_length' => 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 0,\n    ]);\n    $serv->on(\"WorkerStart\", function (\\Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n        $data = str_repeat('A', rand(100, 2000));\n        $serv->send($fd, strlen($data) . \"|\" . $data);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_async/port_invalid.phpt",
    "content": "--TEST--\nswoole_client_async: port invalid\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$cli = new Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\n\n$cli->on(\"connect\", function (Swoole\\Async\\Client $cli) {\n\n});\n\n$cli->on(\"receive\", function (Swoole\\Async\\Client $cli, $data) {\n});\n\n$cli->on(\"error\", function (Swoole\\Async\\Client $cli) {\n\n});\n\n$cli->on(\"close\", function (Swoole\\Async\\Client $cli) {\n\n});\n\nAssert::false(@$cli->connect(\"www.baidu.com\", null, 2.0));\nAssert::same(swoole_last_error(), SWOOLE_ERROR_INVALID_PARAMS);\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_async/sendfile.phpt",
    "content": "--TEST--\nswoole_client_async: async sendfile\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$port = get_one_free_port();\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($port) {\n    $client = new Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\n    $client->on(\"connect\", function (Swoole\\Async\\Client $cli) {\n        $cli->send(pack('N', filesize(TEST_IMAGE)));\n        $ret = $cli->sendfile(TEST_IMAGE);\n        Assert::assert($ret);\n    });\n    $client->on(\"receive\", function (Swoole\\Async\\Client $cli, $data) {\n        $cli->send(pack('N', 8) . 'shutdown');\n        $cli->close();\n        Assert::same($data, md5_file(TEST_IMAGE));\n    });\n    $client->on(\"error\", function ($cli) {\n        echo \"Connect failed\\n\";\n    });\n    $client->on(\"close\", function ($cli) {\n\n    });\n    $client->connect(TCP_SERVER_HOST, $port, 0.5);\n    Swoole\\Event::wait();\n};\n\n$pm->childFunc = function () use ($pm, $port) {\n    $serv = new \\Swoole\\Server(TCP_SERVER_HOST, $port, SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'dispatch_mode' => 1,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n        'package_max_length' => 2000000,\n    ]);\n    $serv->on(\"WorkerStart\", function (\\Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (\\Swoole\\Server $serv, $fd, $rid, $data) {\n        if (substr($data, 4, 8) == 'shutdown') {\n            $serv->shutdown();\n            return;\n        }\n        $serv->send($fd, md5(substr($data, 4)));\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_async/sleep_wake.phpt",
    "content": "--TEST--\nswoole_client_async: Swoole\\Async\\Client sleep & sleep\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $cli = new \\Swoole\\Async\\Client(SWOOLE_SOCK_TCP);\n\n    $cli->on(\"connect\", function (Swoole\\Async\\Client $cli) {\n        Assert::true($cli->isConnected());\n        $r = $cli->sleep();\n        Assert::assert($r);\n        swoole_timer_after(200, function () use ($cli) {\n            $r = $cli->wakeup();\n            Assert::assert($r);\n        });\n        $cli->send(RandStr::gen(1024, RandStr::ALL));\n    });\n\n    $cli->on(\"receive\", function (Swoole\\Async\\Client $cli, $data) {\n        $recv_len = strlen($data);\n        $cli->send(RandStr::gen(1024, RandStr::ALL));\n        $cli->close();\n        Assert::false($cli->isConnected());\n    });\n\n    $cli->on(\"error\", function (Swoole\\Async\\Client $cli) {\n        echo \"error\";\n    });\n\n    $cli->on(\"close\", function (Swoole\\Async\\Client $cli) {\n        echo \"SUCCESS\";\n    });\n\n    $cli->connect('127.0.0.1', $pm->getFreePort(), 0.1);\n    Swoole\\Event::wait();\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm) {\n    include __DIR__ . \"/../include/api/tcp_server.php\";\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_client_coro/bug_2346.phpt",
    "content": "--TEST--\nswoole_client_coro: #2346 method timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $client->set([\n            'open_eof_split' => false,\n            'open_length_check' => true,\n            'package_length_type' => 'N',\n            'package_length_offset' => 4,\n            'package_body_offset' => 8,\n            'package_max_length' => 2 * 1024 * 1024\n        ]);\n        if ($client->connect('127.0.0.1', $pm->getFreePort(), 0.1)) {\n            // 0.2\n            $s = microtime(true);\n            Assert::assert(@!$client->recv(0.2));\n            Assert::same($client->errCode, SOCKET_ETIMEDOUT);\n            approximate(0.2, microtime(true) - $s);\n            // -1 & 0.3\n            go(function () use ($client) {\n                co::sleep(0.3);\n                $client->close();\n            });\n            Assert::assert(@!$client->recv(-1)); // connection closed\n            Assert::same($client->errCode, SOCKET_ECANCELED);\n            approximate(0.5, microtime(true) - $s);\n            // canceled\n            echo \"DONE\\n\";\n        }\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $server->on('receive', function () { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_client_coro/close.phpt",
    "content": "--TEST--\nswoole_client_coro: close actively by client\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $cli->set([\n            'open_length_check' => true,\n            'package_max_length' => 1024 * 1024,\n            'package_length_type' => 'N',\n            'package_length_offset' => 0,\n            'package_body_offset' => 4,\n        ]);\n        $cli->connect('127.0.0.1', $pm->getFreePort());\n        Assert::assert($cli->socket instanceof Swoole\\Coroutine\\Socket);\n        $data = str_repeat('A', 1025);\n        $cli->send(pack('N', strlen($data)).$data);\n        co::sleep(0.2);\n        $retData = $cli->recv();\n        Assert::assert(is_string($retData) and strlen($retData) > 0);\n        /** use valgrind to check memory */\n        $cli->close();\n        Assert::eq($cli->socket, null);\n        Assert::assert(!$cli->connected);\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'worker_num' => 1,\n        //'dispatch_mode'         => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'package_max_length' => 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n    ]);\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data)\n    {\n        $data = str_repeat('B', 1025);\n        $serv->send($fd, pack('N', strlen($data)) . $data);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/close_in_other_co.phpt",
    "content": "--TEST--\nswoole_client_coro: close in other coroutine\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$cid = go(function () {\n    $sock = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    Assert::assert($sock->bind('127.0.0.1', 9601));\n    Assert::assert($sock->listen(512));\n    $conn = $sock->accept();\n    Assert::assert($conn);\n    Assert::isInstanceOf($conn, Swoole\\Coroutine\\Socket::class);\n    Co::yield();\n});\n\n$client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n\ngo(function () use ($client) {\n    $client->connect('127.0.0.1', 9601);\n    $data = @$client->recv();\n    //socket is closed\n    Assert::assert(!$data );\n    Assert::eq($client->errCode, SOCKET_ECANCELED);\n});\n\ngo(function () use ($client, $cid) {\n    co::sleep(.01);\n    $client->close();\n    co::sleep(.01);\n    co::resume($cid);\n});\n\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_client_coro/close_resume.phpt",
    "content": "--TEST--\nswoole_client_coro: (length protocol) resume in onClose callback\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    go(function () use ($pm) {\n        $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n        $cli->set([\n            'open_length_check' => true,\n            'package_max_length' => 1024 * 1024,\n            'package_length_type' => 'N',\n            'package_length_offset' => 0,\n            'package_body_offset' => 4,\n        ]);\n        $cli->connect('127.0.0.1', $pm->getFreePort());\n        $data = str_repeat('A', 1025);\n        $cli->send(pack('N', strlen($data)).$data);\n        co::sleep(0.2);\n        $retData = $cli->recv();\n        Assert::assert(is_string($retData) and strlen($retData) > 0);\n        $retData = $cli->recv();\n        Assert::same($retData, '');\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'worker_num' => 1,\n        //'dispatch_mode'         => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'package_max_length' => 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n    ]);\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data)\n    {\n        $data = str_repeat('B', 1025);\n        $serv->send($fd, pack('N', strlen($data)) . $data);\n        $serv->close($fd);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/close_socket_property.phpt",
    "content": "--TEST--\nswoole_client_coro: close socket property\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n    Assert::true($cli->connect('www.baidu.com', 80));\n    Assert::true($cli->connected);\n    Assert::true($cli->socket->close());\n    Assert::false($cli->close());\n    Assert::eq($cli->errCode, SWOOLE_ERROR_CLIENT_NO_CONNECTION);\n    Assert::false($cli->connected);\n    Assert::null($cli->socket);\n    Assert::true($cli->connect('www.baidu.com', 80));\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/close_twice.phpt",
    "content": "--TEST--\nswoole_client_coro: close twice\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n    $cli->connect('www.baidu.com', 80);\n    Assert::true($cli->close());\n    Assert::false($cli->close());\n    Assert::eq($cli->errCode, SWOOLE_ERROR_CLIENT_NO_CONNECTION);\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/connect_dns_timeout.phpt",
    "content": "--TEST--\nswoole_client_coro: connect dns timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nCo::set(['socket_dns_timeout' => 0.005]);\n\nCo\\run(function () {\n    $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n    Assert::assert(!@$cli->connect('xxx.66xx.6855.xxx.xx'.rand(1000, 9999) . time(), 80));\n    Assert::same($cli->errCode, SWOOLE_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/connect_timeout.phpt",
    "content": "--TEST--\nswoole_client_coro: connect timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    co::set([\n        'socket_connect_timeout' => 0.1\n    ]);\n    $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n    $s = microtime(true);\n    Assert::assert(!@$cli->connect('140.207.135.104', 1));\n    Assert::same($cli->errCode, SOCKET_ETIMEDOUT);\n    $s = microtime(true) - $s;\n    phpt_var_dump($s);\n    time_approximate($s, 0.1, 0.2);\n    $s = microtime(true);\n    Assert::assert(!@$cli->connect('140.207.135.104', 1, $random_timeout = mt_rand(100, 1000) / 1000));\n    Assert::same($cli->errCode, SOCKET_ETIMEDOUT);\n    $s = microtime(true) - $s;\n    phpt_var_dump($s);\n    time_approximate($random_timeout, $s, 0.2);\n    echo \"DONE\\n\";\n});\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_client_coro/connect_with_dns.phpt",
    "content": "--TEST--\nswoole_client_coro: connect with dns\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n    Assert::true($cli->connect('www.qq.com', 80));\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/dtls.phpt",
    "content": "--TEST--\nswoole_client_coro: dtls\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\run(\n        function () use ($pm) {\n            $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP | SWOOLE_SSL);\n            if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n                exit(\"connect failed\\n\");\n            }\n            $client->send(\"hello world\");\n            Assert::same($client->recv(), \"Swoole hello world\");\n            $pm->kill();\n        }\n    );\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP | SWOOLE_SSL);\n    $serv->set([\n        //'log_file' => '/dev/null',\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function ($serv, $fd, $tid, $data) {\n        $serv->send($fd, \"Swoole $data\");\n    });\n    $serv->on('packet', function ($serv, $fd, $tid, $data) {\n        $serv->send($fd, \"Swoole $data\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/enableSSL.phpt",
    "content": "--TEST--\nswoole_client_coro: enableSSL\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\n\nuse function Swoole\\Coroutine\\run;\n\nrun(function () {\n    $client = new Client(SWOOLE_SOCK_TCP);\n    $client->connect('www.baidu.com', 443);\n    $client->enableSSL();\n\n    $http = \"GET / HTTP/1.0\\r\\nAccept: */*User-Agent: Lowell-Agent\\r\\nHost: www.baidu.com\\r\\nConnection: Close\\r\\n\\r\\n\";\n    if (!$client->send($http)) {\n        echo \"ERROR\\n\";\n    }\n\n    $content = '';\n    while (true) {\n        $read = $client->recv();\n        if (empty($read)) {\n            break;\n        }\n        $content .= $read;\n    }\n    $client->close();\n    Assert::assert(strpos($content, 'map.baidu.com') !== false);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/eof.phpt",
    "content": "--TEST--\nswoole_client_coro: tcp client with eof\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->initFreePorts(2);\n\n$pm->parentFunc = function () use ($pm)\n{\n    go(function () use ($pm) {\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE);\n\n    $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP);\n    $port2->set(['open_eof_check' => true, \"package_eof\" => \"\\r\\n\\r\\n\"]);\n\n    $port2->on('Receive', function ($serv, $fd, $rid, $data)\n    {\n        $serv->send($fd, \"Swoole: $data\\r\\n\\r\\n\");\n    });\n\n    $http->set(array(\n        //'log_file' => '/dev/null'\n    ));\n    $http->on(\"WorkerStart\", function (Swoole\\Server $serv)\n    {\n        /**\n         * @var $pm ProcessManager\n         */\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm)\n    {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $cli->set(['open_eof_check' => true, \"package_eof\" => \"\\r\\n\\r\\n\"]);\n        if (!$cli->connect('127.0.0.1', $pm->getFreePort(1)))\n        {\n            fail:\n            $response->end(\"ERROR\\n\");\n            return;\n        }\n        if (!$cli->send(\"hello\\r\\n\\r\\n\"))\n        {\n            goto fail;\n        }\n        $ret = $cli->recv();\n        if (!$ret)\n        {\n            goto fail;\n        }\n        $response->end(\"OK\\n\");\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_client_coro/eof_02.phpt",
    "content": "--TEST--\nswoole_client_coro: tcp client with eof [02]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\nclass MyPool\n{\n    protected $pool;\n\n    public function __construct()\n    {\n        $this->pool = new SplQueue();\n    }\n\n    public function put($mysql)\n    {\n        $this->pool->enqueue($mysql);\n    }\n\n    public function get()\n    {\n        //有空闲连接\n        if (count($this->pool) > 0) {\n            return $this->pool->dequeue();\n        }\n\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP | SWOOLE_KEEP);\n        $client->set(array(\n            'open_eof_split' => true, //打开EOF_SPLIT检测\n            'package_eof' => \"\\r\\n\",\n        ));\n        $res = $client->connect('127.0.0.1', 8000, 3);\n        if ($res == false) {\n            echo \"create connect failed, errCode=\".$client->errCode.\"\\n\";\n            return false;\n        } else {\n            return $client;\n        }\n    }\n}\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () {\n        $pool = new MyPool();\n        for ($j = 0; $j < 2; $j++)\n        {\n            $con = [];\n            for ($i = 0; $i < 4; $i++)\n            {\n                $client = $pool->get();\n                $client->send('hello' . $i . \"\\r\\n\");\n                $con[] = $client;\n            }\n            co::sleep(0.1);\n            foreach ($con as $key => $value)\n            {\n                echo \"recv:\" . trim($value->recv()) . \"\\n\";\n                $pool->put($value);\n            }\n        }\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('0.0.0.0', 8000, SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $serv->set(array(\n        'open_eof_split' => true,\n        'package_eof' => \"\\r\\n\",\n    ));\n    $serv->on('receive', function ($serv, $fd, $rid, $data) {\n        $ret = \"server reply {\" . trim($data) . \"} \\r\\n\";\n        $serv->send($fd, $ret);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nrecv:server reply {hello0}\nrecv:server reply {hello1}\nrecv:server reply {hello2}\nrecv:server reply {hello3}\nrecv:server reply {hello0}\nrecv:server reply {hello1}\nrecv:server reply {hello2}\nrecv:server reply {hello3}\n"
  },
  {
    "path": "tests/swoole_client_coro/eof_03.phpt",
    "content": "--TEST--\nswoole_client_coro: eof with smtp qq\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_ci();\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $client = new Co\\Client(SWOOLE_TCP);\n    $client->set([\n        'open_eof_check' => true,\n        'package_eof' => \"\\r\\n\",\n    ]);\n    $client->connect('smtp.qq.com', 25, 5);\n    go(function () use ($client) {\n        $n = 9;\n        while ($n--) {\n            $data = $client->recv();\n            echo $data;\n            if (empty($data)) {\n                break;\n            }\n        }\n    });\n    $client->send(\"ehlo smtp.qq.com\\r\\n\");\n});\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\n220 smtp.qq.com Esmtp QQ Mail Server\n250-smtp.qq.com\n250-PIPELINING\n250-SIZE 73400320\n250-STARTTLS\n250-AUTH LOGIN PLAIN\n250-AUTH=LOGIN\n250-MAILCOMPRESS\n250 8BITMIME\nDONE\n"
  },
  {
    "path": "tests/swoole_client_coro/eof_04.phpt",
    "content": "--TEST--\nswoole_client_coro: eof with multi packages\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->initRandomData(MAX_REQUESTS);\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $client = new Co\\Client(SWOOLE_TCP);\n        $client->set([\n            'open_eof_check' => true,\n            'package_eof' => \"\\r\\n\",\n        ]);\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            exit(\"ERROR\\n\");\n        }\n        go(function () use ($pm, $client) {\n            $n = $pm->getRandomDataSize();\n            while ($n--) {\n                $data = $client->recv();\n                if (empty($data)) {\n                    break;\n                }\n                Assert::same(rtrim($data, \"\\r\\n\"), $pm->getRandomData());\n            }\n        });\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $server->set(['log_file' => '/dev/null']);\n    $server->on('connect', function (Swoole\\Server $server, int $fd) use ($pm) {\n        do {\n            $data = '';\n            for ($n = mt_rand(1, $pm->getRandomDataSize()); $n--;) {\n                $data .= $pm->getRandomData() . \"\\r\\n\";\n            }\n            $server->send($fd, $data);\n        } while ($pm->getRandomDataSize() > 0);\n    });\n    $server->on('receive', function () { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_client_coro/export_socket.phpt",
    "content": "--TEST--\nswoole_client_coro: close socket property\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n    Assert::true($cli->connect('www.baidu.com', 80));\n    Assert::true($cli->connected);\n    $socket = $cli->exportSocket();\n    $socket->close();\n    Assert::false($cli->recv());\n    Assert::false($cli->close());\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/fixed_package.phpt",
    "content": "--TEST--\nswoole_client_coro: fixed package\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->setRandomFunc('mt_rand');\n$pm->initRandomDataEx(1, MAX_REQUESTS, 0, 65535);\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $client->set([\n            'open_length_check' => true,\n            'package_length_func' => function (string $data) {\n                if (strlen($data) >= 2) {\n                    return 2;\n                }\n                return 0;\n            }\n        ]);\n        if ($client->connect('127.0.0.1', $pm->getFreePort(), 1)) {\n            for ($n = MAX_REQUESTS; $n--;) {\n                $data = $client->recv();\n                if (!$data) {\n                    echo \"ERROR\\n\";\n                    break;\n                }\n                Assert::same($data, pack('n', $pm->getRandomData()));\n            }\n        }\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $server->on('connect', function (Swoole\\Server $server, int $fd) use ($pm) {\n        for ($n = MAX_REQUESTS; $n--;) {\n            $server->send($fd, pack('n', $pm->getRandomData()));\n        }\n    });\n    $server->on('receive', function () { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_client_coro/getpeername.phpt",
    "content": "--TEST--\nswoole_client_coro: getpeername\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\n\nCo\\run(\n    function () {\n        $conn = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $conn->connect('www.baidu.com', 80);\n        $info = $conn->getpeername();\n        Assert::eq($info['host'], System::gethostbyname('www.baidu.com'));\n        Assert::eq($info['port'], 80);\n    }\n);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/getsockname.phpt",
    "content": "--TEST--\nswoole_client_coro: getsockname\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nCo\\run(\n    function () {\n        $conn = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $conn->connect('www.baidu.com', 80);\n        $info = $conn->getsockname();\n        Assert::assert(filter_var($info['host'], FILTER_VALIDATE_IP));\n        Assert::greaterThan($info['port'], 0);\n    }\n);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/isConnected.phpt",
    "content": "--TEST--\nswoole_client_coro: is connect\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse SwooleTest\\ProcessManager;\nuse Swoole\\Server;\nuse Swoole\\Event;\nuse Swoole\\Coroutine\\Client;\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Client(SWOOLE_SOCK_TCP);\n        Assert::false($cli->isConnected());\n        if (!$cli->connect('127.0.0.1', $pm->getFreePort())) {\n            echo \"ERROR\\n\";\n        }\n        Assert::true($cli->isConnected());\n        $cli->close();\n        Assert::false($cli->isConnected());\n        $pm->kill();\n    });\n    Event::wait();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE);\n    $serv->set(array(\n        'log_file' => '/dev/null'\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv) {\n        global $pm;\n        $pm->wakeup();\n    });\n    $serv->on('Receive', function () {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/length_01.phpt",
    "content": "--TEST--\nswoole_client_coro: (length protocol) wrong packet\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    go(function () use ($pm) {\n        $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n        $cli->set([\n            'open_length_check' => true,\n            'package_max_length' => 1024 * 1024,\n            'package_length_type' => 'N',\n            'package_length_offset' => 0,\n            'package_body_offset' => 4,\n        ]);\n        $cli->connect('127.0.0.1', $pm->getFreePort());\n        $data = str_repeat('A', 1025);\n        $cli->send(pack('N', strlen($data)) . $data);\n        $retData = $cli->recv();\n        Assert::assert($retData != false);\n        $len = unpack('Nlen', $retData)['len'];\n        Assert::same(strlen($retData), $len + 4);\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'worker_num' => 1,\n        //'dispatch_mode'         => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'package_max_length' => 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n    ]);\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data)\n    {\n        $len = rand(512*1024, 1024*1024 - 4);\n        $data = pack('N',$len ).str_repeat('C', $len);\n        $chunks = str_split($data, 8192 * 16);\n        foreach ($chunks as $c) {\n            $serv->send($fd, $c);\n            usleep(rand(10000, 99999));\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_client_coro/length_02.phpt",
    "content": "--TEST--\nswoole_client_coro: (length protocol) wrong packet\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$port = get_one_free_port();\n$pm->parentFunc = function ($pid) use ($pm, $port)\n{\n    go(function () use ($port) {\n        $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n        $cli->set([\n            'open_length_check' => true,\n            'package_max_length' => 1024 * 1024,\n            'package_length_type' => 'N',\n            'package_length_offset' => 0,\n            'package_body_offset' => 4,\n        ]);\n        $cli->connect('127.0.0.1', $port);\n        $data = str_repeat('A', 1025);\n        $cli->send(pack('N', strlen($data)).$data);\n        $retData = $cli->recv();\n        Assert::same($retData, '');\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $port) {\n    $serv = new Swoole\\Server('127.0.0.1', $port, SWOOLE_BASE);\n    $serv->set([\n        'worker_num' => 1,\n        //'dispatch_mode'         => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'package_max_length' => 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n    ]);\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data)\n    {\n        $serv->send($fd, pack('N', 1223));\n        $serv->close($fd);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_client_coro/length_03.phpt",
    "content": "--TEST--\nswoole_client_coro: length protocol 03\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse SwooleTest\\ProcessManager;\nuse Swoole\\Server;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $client->set([\n            'open_length_check' => true,\n            'package_max_length' => 1024 * 1024,\n            'package_length_type' => 'N',\n            'package_length_offset' => 0,\n            'package_body_offset' => 4,\n        ]);\n        if (!$client->connect('127.0.0.1', $pm->getFreePort(), 5, 0)) {\n            echo \"Over flow. errno=\" . $client->errCode;\n            die(\"\\n\");\n        }\n\n        $client->send(\"recv\\r\\n\\r\\n\");\n        //小包\n        for ($i = 0; $i < 1000; $i++) {\n            $pkg = $client->recv();\n            Assert::assert($pkg and strlen($pkg) <= 2048);\n        }\n        echo \"SUCCESS\\n\";\n        //慢速发送\n        for ($i = 0; $i < 100; $i++) {\n            $pkg = $client->recv();\n            Assert::assert($pkg and strlen($pkg) <= 8192);\n        }\n        echo \"SUCCESS\\n\";\n        //大包\n        for ($i = 0; $i < 1000; $i++) {\n            $pkg = $client->recv();\n            Assert::assert($pkg != false);\n            $_pkg = unserialize(substr($pkg, 4));\n            Assert::assert(is_array($_pkg));\n            Assert::same($_pkg['i'], $i);\n            Assert::assert(strlen($_pkg['data']) > 8192 and strlen($_pkg['data']) <= 256 * 1024);\n        }\n        echo \"SUCCESS\\n\";\n        $client->close();\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set(array(\n        'package_max_length' => 1024 * 1024 * 2, //2M\n        'socket_buffer_size' => 256 * 1024 * 1024,\n        \"worker_num\" => 1,\n        'log_file' => '/tmp/swoole.log',\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n        //小包\n        for ($i = 0; $i < 1000; $i++) {\n            $data = str_repeat('A', rand(100, 2000));\n            $serv->send($fd, pack('N', strlen($data)) . $data);\n        }\n        //慢速发送\n        for ($i = 0; $i < 100; $i++) {\n            $data = str_repeat('A', rand(3000, 6000));\n            $n = rand(1000, 2000);\n            $serv->send($fd, pack('N', strlen($data)) . substr($data, 0, $n));\n            usleep(rand(10000, 50000));\n            $serv->send($fd, substr($data, $n));\n        }\n        //大包\n        for ($i = 0; $i < 1000; $i++) {\n            $data = serialize(['i' => $i, 'data' => str_repeat('A', rand(20000, 256 * 1024))]);\n            $serv->send($fd, pack('N', strlen($data)) . $data);\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\nSUCCESS\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_client_coro/length_04.phpt",
    "content": "--TEST--\nswoole_client_coro: (length protocol) no body\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst N = MAX_REQUESTS * 10;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    go(function () use ($pm) {\n        $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n        $cli->set([\n            'open_length_check' => true,\n            'package_max_length' => 1024 * 1024,\n            'package_length_type' => 'n',\n            'package_length_offset' => 0,\n            'package_body_offset' => 0,\n        ]);\n        $cli->connect('127.0.0.1', $pm->getFreePort());\n        $cli->send(pack('n', 2));\n\n        $count = N;\n        while($count--)\n        {\n            $data = $cli->recv();\n            $header = unpack('nlen', $data);\n            Assert::same(strlen($data), 2);\n            Assert::same($header['len'], 2);\n        }\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'worker_num' => 1,\n        //'dispatch_mode'         => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'package_max_length' => 1024 * 1024,\n        'package_length_type' => 'n',\n        'package_length_offset' => 0,\n        'package_body_offset' => 0,\n    ]);\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data)\n    {\n        $header = unpack('nlen', $data);\n        Assert::same(strlen($data), 2);\n        Assert::same($header['len'], 2);\n        $count = N;\n        while($count--)\n        {\n            $serv->send($fd, pack('n', 2));\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_client_coro/length_protocol_func.phpt",
    "content": "--TEST--\nswoole_client_coro: length protocol func\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function() use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $client->set([\n            'open_length_check' => true,\n            'package_max_length' => 1024 * 1024,\n            'package_length_func' => function ($data) {\n                $n = strpos($data, '|');\n                if ($n == false)\n                {\n                    return -1;\n                }\n                else\n                {\n                    return intval(substr($data, 0, $n)) + $n + 1;\n                }\n            },\n        ]);\n        if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) {\n            echo \"Over flow. errno=\" . $client->errCode;\n            die(\"\\n\");\n        }\n        $int = rand(1000, 5000);\n        $data = json_encode(['data' => RandStr::gen($int), 'index' => 2, 'len' => $int]);\n        $client->send(pack('N', strlen($data) + 4) . $data);\n        $pkg = $client->recv();\n        Assert::assert($pkg != false and strlen($pkg) > 100);\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'package_max_length' => 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 0,\n    ]);\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data)\n    {\n        $data = str_repeat('A', rand(100, 2000));\n        $serv->send($fd, strlen($data) . \"|\" . $data);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/length_types.phpt",
    "content": "--TEST--\nswoole_client_coro: tcp package length check\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->initRandomData(count(tcp_length_types()) * MAX_REQUESTS);\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        foreach (tcp_length_types() as $length_type => $type_length) {\n            $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n            $client->set([\n                'open_eof_split' => false,\n                'open_length_check' => true,\n                'package_length_type' => $length_type,\n                'package_length_offset' => 0,\n                'package_body_offset' => $type_length\n            ]);\n            if ($client->connect('127.0.0.1', $pm->getFreePort(), 0.1)) {\n                for ($n = MAX_REQUESTS; $n--;) {\n                    $data = $pm->getRandomData();\n                    $recv = substr($client->recv(-1), $type_length);\n                    if (!Assert::assert($recv === $data)) {\n                        echo \"ERROR\\n\";\n                        break;\n                    }\n                }\n            }\n        }\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $server->on('connect', function (Swoole\\Server $server, int $fd) use ($pm) {\n        $length_type = array_keys(tcp_length_types())[$fd - 1];\n        for ($n = MAX_REQUESTS; $n--;) {\n            Assert::assert($server->send($fd, tcp_pack($pm->getRandomData(), $length_type)));\n        }\n    });\n    $server->on('receive', function () { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_client_coro/read_and_write.phpt",
    "content": "--TEST--\nswoole_client_coro: send and recv with channel\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    go(function () use ($pm) {\n        $client = new Co\\Client(SWOOLE_SOCK_TCP);\n        $client->connect(\"127.0.0.1\",  $pm->getFreePort());\n\n        $send_queue = new chan(150);\n        $recv_queue = new chan(150);\n\n        //读协程\n        go(function () use ($recv_queue, $send_queue, $client) {\n            while(true) {\n                $data = $client->recv();\n                if (empty($data)) {\n                    //连接关闭了，结束\n                    $send_queue->close();\n                    $recv_queue->close();\n                    $client->close();\n                    break;\n                } else {\n                    $recv_queue->push($data);\n                }\n            }\n            echo \"read-co stop\\n\";\n        });\n\n        //写协程\n        go(function () use ($send_queue, $client) {\n            while(true) {\n                $data = $send_queue->pop();\n                if (empty($data)) {\n                    //通道已关闭\n                    break;\n                } else {\n                    $client->send($data);\n                }\n            }\n            echo \"write-co stop\\n\";\n        });\n\n        //启动 $n 个协程做消费者\n        $n = 100;\n        while ($n--) {\n            go(function () use ($recv_queue, $n) {\n                for ($i = 0; $i < 100; $i++) {\n                    //收到了数据\n                    $data = $recv_queue->pop();\n                }\n                //echo \"consumer-co $n stop\\n\";\n            });\n        }\n\n        //启动 $n 个协程随机发送数据\n        $n = 100;\n        while ($n--) {\n            go(function () use ($send_queue, $n) {\n                for ($i = 0; $i < 100; $i++) {\n                    //投递任务\n                    $send_queue->push(\"hello world $i \".rand(1000000, 9999999).\"\\r\\n\");\n                }\n                //echo \"producer $n stop\\n\";\n            });\n        }\n    });\n\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'worker_num' => 1,\n        'open_eof_split' => true,\n        'package_eof' => \"\\r\\n\",\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm)\n    {\n        $pm->wakeup();\n    });\n\n    $serv->on('receive', function ($serv, $fd, $tid, $data)\n    {\n        static $i = 0;\n        $serv->send($fd, \"Swoole: $data\");\n        $i++;\n        if ($i == 10000) {\n            $serv->close($fd);\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nwrite-co stop\nread-co stop\n"
  },
  {
    "path": "tests/swoole_client_coro/reconnect.phpt",
    "content": "--TEST--\nswoole_client_coro: reconnect 1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Coroutine\\Client;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $flag = 0;\n        $client = new Client(SWOOLE_SOCK_TCP);\n        reconnect:\n        if (!$client->connect('127.0.0.1', 9501)) {\n            /**\n            * if we want to reconnect server, we should call $client->close() first\n            */\n            Assert::eq($client->errCode, SOCKET_EISCONN);\n            Assert::eq($client->errMsg, swoole_strerror(SOCKET_EISCONN));\n        }\n\n        $pm->kill();\n\n        $data = $client->recv();\n        if (empty($data)) {\n            if ($flag === 0) {\n                $flag += 1;\n                goto reconnect;\n            }\n        }\n        echo \"DONE\\n\";\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', 9501);\n    $serv->set([\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on('start', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('Receive', function () {\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_client_coro/reconnect_2.phpt",
    "content": "--TEST--\nswoole_client_coro: reconnect 2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Coroutine\\Client;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $flag = 0;\n        $client = new Client(SWOOLE_SOCK_TCP);\n\n        $n = 2;\n        while ($n--) {\n            Assert::true($client->connect('127.0.0.1', 9501));\n            Assert::true($client->close());\n        }\n        echo \"DONE\\n\";\n    });\n\n    $pm->kill();\n\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', 9501);\n    $serv->set([\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on('start', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('Receive', function () {\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_client_coro/reconnect_3.phpt",
    "content": "--TEST--\nswoole_client_coro: reconnect 3\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Coroutine\\Client;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $flag = 0;\n        $client = new Client(SWOOLE_SOCK_TCP);\n\n        $n = 2;\n        while ($n--) {\n            Assert::true($client->connect('127.0.0.1', 9501));\n            go(function () use ($client) {\n                while (1) {\n                    if (!$client->recv()) {\n                        break;\n                    }\n                }\n                Assert::true($client->close());\n            });\n            Assert::false($client->close());\n            Assert::eq($client->errCode, SWOOLE_ERROR_CO_SOCKET_CLOSE_WAIT);\n        }\n        echo \"DONE\\n\";\n    });\n\n    $pm->kill();\n\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', 9501);\n    $serv->set([\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on('start', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('Receive', function () {\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_client_coro/recv_after_close.phpt",
    "content": "--TEST--\nswoole_client_coro: recv after close\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $cli->connect('127.0.0.1', $pm->getFreePort());\n\n        $cli->send(\"hello world\\n\");\n        co::sleep(0.2);\n        $retData = $cli->recv();\n        Assert::assert($retData === false);\n        Assert::assert($cli->errCode === SOCKET_ECONNRESET);\n        $cli->close();\n        Assert::assert(!$cli->connected);\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'worker_num' => 1,\n        //'dispatch_mode'         => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'package_max_length' => 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n    ]);\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data)\n    {\n        $serv->close($fd);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/recv_bad_packet.phpt",
    "content": "--TEST--\nswoole_client_coro: recv bad packet\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst SIZE = 2 * 1024 * 1024;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) {\n        go(function () use ($pm, $i) {\n            $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n            $cli->set([\n                'open_length_check' => true,\n                'package_max_length' => 4 * 1024 * 1024,\n                'package_length_type' => 'N',\n                'package_length_offset' => 0,\n                'package_body_offset' => 4,\n            ]);\n            if ($cli->connect('127.0.0.1', $pm->getFreePort(), 0.2) == false) {\n                echo \"ERROR\\n\";\n                return;\n            }\n            for ($i = 0; $i < 3; $i++) {\n                $sid = strval(rand(100000, 999999));\n                $send_data = str_repeat('A', 1000) . $sid;\n                $cli->send(pack('N', strlen($send_data)) . $send_data);\n                $data = $cli->recv();\n                Assert::isEmpty($data);\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        \"worker_num\" => 4,\n        'log_level' => SWOOLE_LOG_ERROR,\n        'open_length_check' => true,\n        'package_max_length' => 4 * 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n    ));\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n        $len = rand(1024, 8192);\n        $send_data = str_repeat('A', $len);\n        //bad packet\n        $serv->send($fd, pack('N', SIZE) . $send_data);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/recv_timeout.phpt",
    "content": "--TEST--\nswoole_client_coro: recv timeout\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    go(function () use ($pm) {\n        $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n        $cli->connect('127.0.0.1', $pm->getFreePort(), -1);\n        $data = str_repeat('A', 1025);\n        $cli->send(pack('N', strlen($data)) . $data);\n        $retData = @$cli->recv(0.5);\n        Assert::false($retData);\n        Assert::same($cli->errCode, SOCKET_ETIMEDOUT);\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data)\n    {\n        //no response\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_client_coro/recv_timeout2.phpt",
    "content": "--TEST--\nswoole_client_coro: recv timeout [2]\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n        $cli->connect('127.0.0.1', $pm->getFreePort(), 0.5);\n        $data = str_repeat('A', 1025);\n        $cli->send(pack('N', strlen($data)) . $data);\n        $s = microtime(true);\n        $retData = @$cli->recv();\n        Assert::false($retData);\n        Assert::same($cli->errCode, SOCKET_ETIMEDOUT);\n        approximate(0.5, microtime(true) - $s);\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data)\n    {\n        //no response\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_client_coro/recvfrom.phpt",
    "content": "--TEST--\nswoole_client_coro: recvfrom 1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$free_port = get_one_free_port();\n\ngo(function () use ($free_port) {\n    $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_DGRAM, 0);\n    $socket->bind('127.0.0.1', $free_port);\n    $peer = null;\n    echo $socket->recvfrom($peer);\n    $socket->sendto($peer['address'], $peer['port'], \"server\");\n});\n\ngo(function () use ($free_port) {\n    $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n    $cli->sendto('127.0.0.1', $free_port, \"hello\\n\");\n    $addr = null;\n    $port = null;\n    $cli->recvfrom(1024, $addr, $port);\n    Assert::same($addr, '127.0.0.1');\n    Assert::same($port, $free_port);\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nhello\n"
  },
  {
    "path": "tests/swoole_client_coro/recvfrom_2.phpt",
    "content": "--TEST--\nswoole_client_coro: recvfrom 2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nconst N = 10;\n$free_port = get_one_free_port();\n\ngo(function () use ($free_port) {\n    $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n    $cli->set([\n        'bind_address' => '127.0.0.1',\n        'bind_port' => $free_port,\n    ]);\n    $n = N;\n    while ($n--) {\n        $data = $cli->recvfrom(1024, $addr, $port);\n        Assert::same($data, 'hello');\n    }\n    echo \"DONE\\n\";\n});\n\ngo(function () use ($free_port) {\n    $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_DGRAM, 0);\n    $n = N;\n    while ($n--) {\n        $socket->sendto('127.0.0.1', $free_port, \"hello\");\n        Co::sleep(0.01);\n    }\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_client_coro/recvfrom_timeout.phpt",
    "content": "--TEST--\nswoole_client_coro: timeout of udp client\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$port = get_one_free_port();\n\nCo\\Run(function () use ($port) {\n    $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n    $cli->set([\n        'timeout' => 0.2,\n    ]);\n    if (!Assert::assert($cli->connect('192.0.0.1', $port))) {\n        return;\n    }\n    Assert::assert($cli->send(\"hello\"));\n    // default timeout\n    $s = microtime(true);\n    $ret = @$cli->recvfrom(1024, $peer);\n    $s = microtime(true) - $s;\n    Assert::assert($s > 0.2 && $s < 0.5, $s);\n    Assert::eq($ret, false);\n    Assert::eq($cli->errCode, SOCKET_ETIMEDOUT);\n    $cli->close();\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/recvfrom_timeout2.phpt",
    "content": "--TEST--\nswoole_client_coro: timeout of udp client[2]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$port = get_one_free_port();\n\nCo\\Run(function () use ($port) {\n    $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n    Assert::assert($cli->sendto('192.0.0.1', $port, 'hello'));\n    $cli->set([\n        'timeout' => 0.2,\n    ]);\n    // default timeout\n    $s = microtime(true);\n    $ret = @$cli->recvfrom(1024, $peer);\n    $s = microtime(true) - $s;\n    Assert::assert($s > 0.2 && $s < 0.5, $s);\n    Assert::eq($ret, false);\n    Assert::eq($cli->errCode, SOCKET_ETIMEDOUT);\n    $cli->close();\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/send_big.phpt",
    "content": "--TEST--\nswoole_client_coro: send\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    go(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $r = $client->connect(\"127.0.0.1\", $pm->getFreePort(), 0.5);\n        Assert::assert($r);\n\n        set_socket_coro_buffer_size($client->exportSocket(), 65536);\n\n        $header = \"POST /post.php HTTP/1.1\\r\\n\";\n        $header .= \"Host: weibo.com\\r\\n\";\n        $header .= \"Content-Type: application/x-www-form-urlencoded\\r\\n\";\n        $header .= \"Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4,ja;q=0.2\\r\\n\";\n\n        $_postData = ['data' => urlencode(RandStr::getBytes(128*1024)), 'message' => '', 'code' => 120];\n        $_postBody = http_build_query($_postData).\"_END\\r\\n\\r\\n\";\n        $header .=  \"Content-Length: \" . strlen($_postBody);\n\n        Assert::assert($client->send($header));\n        Assert::assert($client->send($_postBody));\n\n        $data = $client->recv(5);\n        Assert::same($data, \"HTTP/1.1 200 OK\\r\\n\\r\\n\");\n        $client->close();\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'kernel_socket_recv_buffer_size' => 65536,\n        'kernel_socket_send_buffer_size' => 65536,\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on('connect', function (Swoole\\Server $serv, $fd)\n    {\n\n    });\n    $serv->on('receive', function ($serv, $fd, $tid, $data)\n    {\n        usleep(5000);\n        if (substr($data, -8, 8) == \"_END\\r\\n\\r\\n\") {\n            $serv->send($fd, \"HTTP/1.1 200 OK\\r\\n\\r\\n\");\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/sendfile.phpt",
    "content": "--TEST--\nswoole_client_coro: sendfile\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    Co\\Run(function ()  use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $r = $client->connect(TCP_SERVER_HOST, $pm->getFreePort(), 0.5);\n        Assert::assert($r);\n        $client->send(pack('N', filesize(TEST_IMAGE)));\n        $ret = $client->sendfile(TEST_IMAGE);\n        Assert::assert($ret);\n\n        $data = $client->recv();\n        $client->send(pack('N', 8) . 'shutdown');\n        $client->close();\n        Assert::same($data, md5_file(TEST_IMAGE));\n    });\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'dispatch_mode' => 1,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n        'package_max_length' => 2000000,\n    ]);\n    $serv->on(\"WorkerStart\", function (Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $rid, $data)\n    {\n        if (substr($data, 4, 8) == 'shutdown')\n        {\n            $serv->shutdown();\n            return;\n        }\n        $serv->send($fd, md5(substr($data, 4)));\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/sendto.phpt",
    "content": "--TEST--\nswoole_client_coro: sendto\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\n\nrun(function () {\n    $port = get_one_free_port();\n\n    go(function () use ($port) {\n        $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_DGRAM, 0);\n        $socket->bind('127.0.0.1', $port);\n        $peer = null;\n        $ret = $socket->recvfrom($peer);\n        Assert::assert($ret, 'hello');\n        $ret = $socket->recvfrom($peer);\n        Assert::assert($ret, 'hello');\n        echo \"DONE\\n\";\n    });\n\n    go(function () use ($port) {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n        $cli->sendto('127.0.0.1', $port, \"hello\\n\");\n        $cli->sendto('localhost', $port, \"hello\\n\");\n        Assert::false($cli->sendto('error_domain', $port, \"hello\\n\"));\n        Assert::assert($cli->errCode, 704);\n        Assert::assert($cli->errMsg, 'DNS Lookup resolve failed');\n    });\n});\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_client_coro/ssl.phpt",
    "content": "--TEST--\nswoole_client_coro: ssl client\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_no_ssl();\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    if (!$cli->connect('www.baidu.com', 443)) {\n        echo \"ERROR\\n\";\n    }\n\n    $http = \"GET / HTTP/1.0\\r\\nAccept: */*User-Agent: Lowell-Agent\\r\\nHost: www.baidu.com\\r\\nConnection: Close\\r\\n\\r\\n\";\n    if (!$cli->send($http)) {\n        echo \"ERROR\\n\";\n    }\n\n    $content = '';\n    while (true) {\n        $read = $cli->recv();\n        if (empty($read)) {\n            break;\n        }\n        $content .= $read;\n    }\n    $cli->close();\n    Assert::assert(strpos($content, 'map.baidu.com') !== false);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/ssl_verify.phpt",
    "content": "--TEST--\nswoole_client_coro: ssl verify\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_no_ssl();\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine;\n\nCo::set(['log_file' => TEST_LOG_FILE]);\n\nCoroutine\\run(function () {\n    $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $client->set([\n        'ssl_cafile' => SSL_FILE_DIR . '/mosquitto.org.crt',\n        'ssl_verify_peer' => true,\n    ]);\n    $ret = $client->connect('test.mosquitto.org', 8883);\n    Assert::true($ret);\n\n    $client2 = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $client2->set([\n        'ssl_cafile' => SSL_FILE_DIR . '/mosquitto.org.crt',\n        'ssl_verify_peer' => true,\n    ]);\n    $ret = $client2->connect('baidu.com', 443);\n    Assert::false($ret);\n    Assert::eq($client2->errCode, SWOOLE_ERROR_SSL_VERIFY_FAILED);\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_coro/tcp_client.phpt",
    "content": "--TEST--\nswoole_client_coro: tcp client\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->initFreePorts(2);\n\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    go(function () use ($pm) {\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE);\n\n    $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP);\n    $port2->set([]);\n    $port2->on('Receive', function ($serv, $fd, $rid, $data)\n    {\n        $serv->send($fd, \"Swoole: $data\");\n    });\n\n    $http->set(array(\n        'log_file' => '/dev/null'\n    ));\n    $http->on(\"WorkerStart\", function (Swoole\\Server $serv)\n    {\n        /**\n         * @var $pm ProcessManager\n         */\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm)\n    {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        if (!$cli->connect('127.0.0.1', $pm->getFreePort(1)))\n        {\n            fail:\n            $response->end(\"ERROR\\n\");\n            return;\n        }\n        if (!$cli->send(\"hello\"))\n        {\n            goto fail;\n        }\n        $ret = $cli->recv();\n        if (!$ret)\n        {\n            goto fail;\n        }\n        $response->end(\"OK\\n\");\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_client_coro/tcp_nodelay.phpt",
    "content": "--TEST--\nswoole_client_coro: tcp client\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->initFreePorts(2);\n\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    go(function () use ($pm) {\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE);\n\n    $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP);\n    $port2->set([]);\n    $port2->on('Receive', function ($serv, $fd, $rid, $data)\n    {\n        $serv->send($fd, \"Swoole: $data\");\n    });\n\n    $http->set(array(\n        'log_file' => '/dev/null'\n    ));\n    $http->on(\"WorkerStart\", function (Swoole\\Server $serv)\n    {\n        /**\n         * @var $pm ProcessManager\n         */\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm)\n    {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $cli->set(['open_tcp_nodelay' => false]);\n        if (!$cli->connect('127.0.0.1', $pm->getFreePort(1)))\n        {\n            fail:\n            $response->end(\"ERROR\\n\");\n            return;\n        }\n        if (!$cli->send(\"hello\"))\n        {\n            goto fail;\n        }\n        $ret = $cli->recv();\n        if (!$ret)\n        {\n            goto fail;\n        }\n        $response->end(\"OK\\n\");\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_client_coro/timeout.phpt",
    "content": "--TEST--\nswoole_client_coro: timeout of udp client\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$port = get_one_free_port();\n\n$cid = go(function () use ($port) {\n    $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_DGRAM, 0);\n    $socket->bind('127.0.0.1', $port);\n    $peer = null;\n    $socket->recvfrom($peer);\n    echo \"recvfrom client\\n\";\n});\n\ngo(function () use ($port) {\n    co::set([\n        'socket_connect_timeout' => 0.5,\n        'socket_timeout' => 0.1\n    ]);\n\n    $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n    if (!Assert::assert($cli->connect('127.0.0.1', $port))) {\n        return;\n    }\n\n    Assert::assert($cli->send(\"hello\"));\n\n    // default timeout\n    $s = microtime(true);\n    $ret = @$cli->recv();\n    $s = microtime(true) - $s;\n    Assert::assert($s > 0.08 && $s < 0.12, $s);\n    Assert::assert(!$ret, var_dump_return($ret));\n\n    // custom timeout\n    $s = microtime(true);\n    $ret = @$cli->recv(0.5);\n    $s = microtime(true) - $s;\n    Assert::assert($s > 0.45 && $s < 0.55, $s);\n    Assert::assert(!$ret, var_dump_return($ret));\n\n    // default timeout\n    $s = microtime(true);\n    $ret = @$cli->recv();\n    $s = microtime(true) - $s;\n    Assert::assert($s > 0.08 && $s < 0.12, $s);\n    Assert::assert(!$ret, var_dump_return($ret));\n\n    $cli->close();\n    echo \"TIMEOUT\\n\";\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nrecvfrom client\nTIMEOUT\n"
  },
  {
    "path": "tests/swoole_client_coro/udp_client.phpt",
    "content": "--TEST--\nswoole_client_coro: udp client\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->initFreePorts(2);\n\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    go(function () use ($pm) {\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE);\n\n    $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_UDP);\n    $port2->set([]);\n    $port2->on('Packet', function ($serv, $data, $client)\n    {\n        $serv->sendto($client['address'], $client['port'], \"Swoole: $data\");\n    });\n\n    $http->set(array(\n        'log_file' => '/dev/null'\n    ));\n    $http->on(\"WorkerStart\", function (Swoole\\Server $serv)\n    {\n        /**\n         * @var $pm ProcessManager\n         */\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm)\n    {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n        if (!$cli->connect('127.0.0.1', $pm->getFreePort(1)))\n        {\n            fail:\n            $response->end(\"ERROR\\n\");\n            return;\n        }\n        if (!$cli->send(\"hello\"))\n        {\n            goto fail;\n        }\n        $ret = $cli->recv();\n        if (!$ret)\n        {\n            goto fail;\n        }\n        $response->end(\"OK\\n\");\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_client_coro/udp_recv_failed.phpt",
    "content": "--TEST--\nswoole_client_coro: udp recv failed\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n    $ret = $cli->connect('127.0.0.1', get_one_free_port(), 3);\n    echo \"connect ret:\".var_export($ret,1).\"\\n\";\n\n    $ret = $cli->send(\"hello\");\n    echo \"send ret:\".var_export($ret,1).\"\\n\";\n\n    $ret = @$cli->recv();\n    echo \"recv ret:\".var_export($ret,1).\"\\n\";\n    Assert::same($cli->errCode, SOCKET_ECONNREFUSED);\n    $cli->close();\n});\n?>\n--EXPECT--\nconnect ret:true\nsend ret:5\nrecv ret:false\n"
  },
  {
    "path": "tests/swoole_client_coro/unsock_dgram.phpt",
    "content": "--TEST--\nswoole_client_coro: unix socket dgram\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Coroutine\\Client;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\Run(function () {\n        $client = new Client(SWOOLE_SOCK_UNIX_DGRAM);\n        $client->set([\n            'bind_address' => __DIR__ . '/client.sock',\n            'bind_port' => 0,\n        ]);\n        $r = $client->connect(UNIXSOCK_PATH, 0, -1);\n        if ($r === false) {\n            echo \"ERROR\";\n            exit;\n        }\n        $client->send(\"SUCCESS\");\n        echo $client->recv();\n        $client->close();\n    });\n    @unlink(UNIXSOCK_PATH);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server(UNIXSOCK_PATH, 0, SWOOLE_PROCESS, SWOOLE_SOCK_UNIX_DGRAM);\n    $serv->set([\"worker_num\" => 1, 'log_file' => '/dev/null',]);\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"packet\", function (Server $serv, $data, $addr) {\n        $serv->send($addr['address'], 'SUCCESS'.PHP_EOL);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_client_coro/unsock_stream.phpt",
    "content": "--TEST--\nswoole_client_coro: unix socket stream\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Server;\nuse Swoole\\Coroutine\\Client;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\Run(function (){\n        $client = new Client(SWOOLE_SOCK_UNIX_STREAM);\n        $r = $client->connect(UNIXSOCK_PATH, 0, -1);\n        if ($r === false) {\n            echo \"ERROR\";\n            exit;\n        }\n        $client->send(\"SUCCESS\");\n        usleep(100 * 1000);\n        echo $client->recv() . \"\\n\";\n        $client->close();\n    });\n    @unlink(UNIXSOCK_PATH);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server(UNIXSOCK_PATH, 0, SWOOLE_BASE, SWOOLE_SOCK_UNIX_STREAM);\n    $serv->set([\"worker_num\" => 1, 'log_file' => '/dev/null']);\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $reactorId, $data) {\n        $serv->send($fd, 'SUCCESS');\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_client_sync/bind_address.phpt",
    "content": "--TEST--\nswoole_client_sync: bind address\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Client;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    $client = new Client(SWOOLE_SOCK_TCP);\n    $client->set([\n        'bind_address' => '127.0.0.1',\n        'bind_port' => $pm->getFreePort(),\n    ]);\n\n    Assert::false($client->connect('127.0.0.1', 9501));\n    Assert::eq($client->errCode, SOCKET_EADDRINUSE);\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end('OK');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_client_sync/connect_1.phpt",
    "content": "--TEST--\nswoole_client_sync: connect 1 - 1\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nini_set('swoole.display_errors', 'off');\n\n$cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n$r = $cli->connect(\"11.11.11.11\", 80, 0.5);\nAssert::false($r);\nAssert::eq($cli->errCode, SOCKET_ETIMEDOUT);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_sync/connect_2.phpt",
    "content": "--TEST--\nswoole_client_sync: connect 1 - 2\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nkillself_in_syncmode(1000, SIGTERM);\n\n$cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n$r = $cli->connect(TEST_DOMAIN_3, 443);\nAssert::assert($r);\n$cli->close();\necho \"SUCCESS\";\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_client_sync/connect_3.phpt",
    "content": "--TEST--\nswoole_client_sync: connect 1 - 3 nonblocking connect & select\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $r = $cli->connect('127.0.0.1', $pm->getFreePort(), 1);\n    Assert::assert($r);\n    $r = $w = $e = [$cli];\n    $n = swoole_client_select($r, $w, $e, 0);\n    Assert::same($n, 1);\n    Assert::same(count($w), 1);\n    Assert::same(count($e), 0);\n    Assert::same(count($r), 0);\n    $cli->close();\n    echo \"SUCCESS\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end('OK');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_client_sync/enableSSL.phpt",
    "content": "--TEST--\nswoole_client_sync: enableSSL\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$cli = new Swoole\\Client(SWOOLE_SOCK_TCP);\nAssert::true($cli->connect(\"www.baidu.com\", 443, 2.0));\n\nif ($cli->enableSSL()) {\n    echo \"SSL READY\\n\";\n    $cli->send(\"GET / HTTP/1.1\\r\\nHost: www.baidu.com\\r\\nConnection: close\\r\\nUser-Agent: curl/7.50.1-DEV\\r\\nAccept: */*\\r\\n\\r\\n\");\n}\n\n$resp = '';\nwhile (true) {\n    $data = $cli->recv();\n    if ($data == false) {\n        break;\n    }\n    $resp .= $data;\n}\n\nAssert::assert(strlen($resp) > 0);\nAssert::contains($resp, 'www.baidu.com');\n$cli->close();\necho \"DONE\\n\";\n?>\n--EXPECT--\nSSL READY\nDONE\n"
  },
  {
    "path": "tests/swoole_client_sync/enableSSL_2.phpt",
    "content": "--TEST--\nswoole_client_sync: enableSSL\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$cli = new Swoole\\Client(SWOOLE_SOCK_TCP);\nAssert::true($cli->connect(\"www.baidu.com\", 443, 2.0));\n\ntry {\n    $cli->enableSSL(function (){});\n} catch (\\Throwable $e) {\n    Assert::contains($e->getMessage(), 'not support `onSslReady` callback');\n}\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_sync/eof.phpt",
    "content": "--TEST--\nswoole_client_sync: eof protocol [sync]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$port = get_one_free_port();\n$pm->parentFunc = function ($pid) use ($port) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $client->set(['open_eof_check' => true, \"package_eof\" => \"\\r\\n\\r\\n\"]);\n    if (!$client->connect('127.0.0.1', $port, 5, 0)) {\n        echo \"Over flow. errno=\" . $client->errCode;\n        die(\"\\n\");\n    }\n\n    $client->send(\"recv\\r\\n\\r\\n\");\n\n    //小包\n    for ($i = 0; $i < 1000; $i++) {\n        $pkg = $client->recv();\n        Assert::assert($pkg and strlen($pkg) <= 2048);\n    }\n    echo \"SUCCESS\\n\";\n    //慢速发送\n    for ($i = 0; $i < 100; $i++) {\n        $pkg = $client->recv();\n        Assert::assert($pkg and strlen($pkg) <= 8192);\n    }\n    echo \"SUCCESS\\n\";\n    //大包\n    for ($i = 0; $i < 1000; $i++) {\n        $pkg = $client->recv();\n        Assert::assert($pkg != false);\n        $_pkg = swoole_substr_unserialize($pkg, 0, strlen($pkg) - 4);\n        Assert::assert(is_array($_pkg));\n        Assert::same($_pkg['i'], $i);\n        Assert::assert(strlen($_pkg['data']) > 8192 and strlen($_pkg['data']) <= 256 * 1024);\n    }\n    echo \"SUCCESS\\n\";\n    $client->close();\n\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm, $port) {\n    $serv = new Swoole\\Server('127.0.0.1', $port, SWOOLE_BASE);\n    $serv->set(array(\n        'package_eof' => \"\\r\\n\\r\\n\",\n        'open_eof_check' => true,\n        'open_eof_split' => true,\n        'package_max_length' => 1024 * 1024 * 2, //2M\n        'socket_buffer_size' => 256 * 1024 * 1024,\n        \"worker_num\" => 1,\n        'log_file' => '/tmp/swoole.log',\n    ));\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n        //小包\n        for ($i = 0; $i < 1000; $i++) {\n            $serv->send($fd, str_repeat('A', rand(100, 2000)) . \"\\r\\n\\r\\n\");\n        }\n        //慢速发送\n        for ($i = 0; $i < 100; $i++) {\n            $serv->send($fd, str_repeat('A', rand(1000, 2000)));\n            usleep(rand(10000, 50000));\n            $serv->send($fd, str_repeat('A', rand(2000, 4000)) . \"\\r\\n\\r\\n\");\n        }\n        //大包\n        for ($i = 0; $i < 1000; $i++) {\n            $serv->send($fd, serialize(['i' => $i, 'data' => str_repeat('A', rand(20000, 256 * 1024))]) . \"\\r\\n\\r\\n\");\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\nSUCCESS\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_client_sync/eof_close.phpt",
    "content": "--TEST--\nswoole_client_sync: eof protocol [sync]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Client;\n\nconst EOF = \"\\r\\n\\r\\n\";\n$pkg = random_bytes(rand(128 * 1024, 256 * 1024));\n$pm = new ProcessManager;\n$port = get_one_free_port();\n$pm->parentFunc = function ($pid) use ($port, $pkg, $pm) {\n    $client = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $client->set(['open_eof_check' => true, \"package_eof\" => EOF]);\n    if (!$client->connect('127.0.0.1', $port, 5, 0)) {\n        echo \"Over flow. errno=\" . $client->errCode;\n        die(\"\\n\");\n    }\n\n    $client->send(\"recv\\r\\n\\r\\n\");\n    $recvPkg = $client->recv();\n    Assert::assert($recvPkg != false);\n    $_pkg = swoole_substr_unserialize($recvPkg, 0, strlen($recvPkg) - 4);\n    Assert::assert(is_array($_pkg));\n    Assert::eq($_pkg['data'], $pkg);\n    $recvPkg = $client->recv();\n    Assert::same($recvPkg, '');\n    echo \"SUCCESS\\n\";\n    $client->close();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $port, $pkg) {\n    $serv = new Server('127.0.0.1', $port, SWOOLE_BASE);\n    $serv->set(array(\n        'package_eof' => \"\\r\\n\\r\\n\",\n        'open_eof_check' => true,\n        'open_eof_split' => true,\n        'package_max_length' => 1024 * 1024 * 2,\n        'socket_buffer_size' => 256 * 1024 * 1024,\n        'log_file' => TEST_LOG_FILE,\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($pkg) {\n        $serv->send($fd, serialize(['data' => $pkg]) . EOF);\n        $serv->close($fd);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_client_sync/eof_timeout.phpt",
    "content": "--TEST--\nswoole_client_sync: eof timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\nini_set(\"swoole.display_errors\", \"Off\");\n\n$pm->initFreePorts(2);\n$pm->parentFunc = function ($pid) use ($pm) {\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP);\n    $cli->set(['open_eof_check' => true, \"package_eof\" => \"\\r\\n\\r\\n\"]);\n    if (!$cli->connect('127.0.0.1', $pm->getFreePort(1), 0.5)) {\n        fail:\n        echo \"ERROR\\n\";\n        Swoole\\Process::kill($pid);\n        return;\n    }\n    //no eof, should be timeout here\n    if (!$cli->send(\"hello\")) {\n        goto fail;\n    }\n    $ret = $cli->recv();\n    if (!$ret) {\n        goto fail;\n    }\n    echo \"OK\\n\";\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE);\n\n    $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP);\n    $port2->set(['open_eof_check' => true, \"package_eof\" => \"\\r\\n\\r\\n\"]);\n\n    $port2->on('Receive', function ($serv, $fd, $rid, $data) {\n        $serv->send($fd, \"Swoole: $data\\r\\n\\r\\n\");\n    });\n\n    $http->set([\n        //'log_file' => '/dev/null'\n    ]);\n    $http->on(\"WorkerStart\", function (Swoole\\Server $serv) {\n        /**\n         * @var $pm ProcessManager\n         */\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end(\"OK\\n\");\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nERROR\n"
  },
  {
    "path": "tests/swoole_client_sync/http_proxy.phpt",
    "content": "--TEST--\nswoole_client_sync: http client with http_proxy\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_http_proxy();\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire TESTS_API_PATH . '/swoole_client/http_get.php';\n\n$cli = new Swoole\\Client(SWOOLE_TCP);\n$cli->set([\n    'timeout' => 30,\n    'http_proxy_host' => HTTP_PROXY_HOST,\n    'http_proxy_port' => HTTP_PROXY_PORT\n]);\nclient_http_v10_get($cli);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_sync/keep1.phpt",
    "content": "--TEST--\nswoole_client_sync: long connection\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n\n    $client1 = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_KEEP | SWOOLE_SYNC);\n    $r = @$client1->connect(TCP_SERVER_HOST, $pm->getFreePort(), 0.5);\n    Assert::true($r);\n\n    $client2 = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_KEEP | SWOOLE_SYNC);\n    $r = @$client2->connect(TCP_SERVER_HOST, $pm->getFreePort(), 0.5);\n    Assert::true($r);\n\n    Assert::assert($client1->sock != $client2->sock);\n\n    @$client1->close(true);\n\n    $client2->send(\"hello\");\n    echo $client2->recv();\n    @$client2->close(true);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('receive', function (Swoole\\Server $serv, $fd, $tid, $data) {\n        $serv->send($fd, \"Swoole $data\\n\");\n    });\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nSwoole hello\n"
  },
  {
    "path": "tests/swoole_client_sync/keep2.phpt",
    "content": "--TEST--\nswoole_client_sync: long connection[2]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n\n$client1 = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_KEEP | SWOOLE_SYNC);\n$r = @$client1->connect(TCP_SERVER_HOST, 9999, 0.5);\nAssert::false($r);\n@$client1->close();\n\n$client2 = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_KEEP | SWOOLE_SYNC);\n$r = @$client2->connect(TCP_SERVER_HOST, 9999, 0.5);\nAssert::false($r);\n\nAssert::same($client1->sock, $client2->sock);\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_sync/keep3.phpt",
    "content": "--TEST--\nswoole_client_sync: long connection[3]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    $client1 = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_KEEP | SWOOLE_SYNC);\n    $r = $client1->connect(TCP_SERVER_HOST, $pm->getFreePort(), 0.5);\n    Assert::true($r);\n    $client1->send(\"hello\");\n    echo $client1->recv();\n    $client1->close();\n\n    $client2 = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_KEEP | SWOOLE_SYNC);\n    $r = $client2->connect(TCP_SERVER_HOST, $pm->getFreePort(), 0.5);\n    Assert::true($r);\n    $client2->send(\"hello\");\n    echo $client2->recv();\n    $client2->close();\n\n    Assert::same($client1->sock, $client2->sock);\n\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('receive', function (Swoole\\Server $serv, $fd, $tid, $data) {\n        $serv->send($fd, \"Swoole $data\\n\");\n    });\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nSwoole hello\nSwoole hello\n"
  },
  {
    "path": "tests/swoole_client_sync/keep4.phpt",
    "content": "--TEST--\nswoole_client_sync: long connection[4]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n\n    $client1 = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_KEEP | SWOOLE_SYNC, SWOOLE_SOCK_SYNC, \"id\");\n    $r = @$client1->connect(TCP_SERVER_HOST, $pm->getFreePort(), 0.5);\n    Assert::true($r);\n\n    $client2 = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_KEEP | SWOOLE_SYNC, SWOOLE_SOCK_SYNC, \"id\");\n    $r = @$client2->connect(TCP_SERVER_HOST, $pm->getFreePort(), 0.5);\n    Assert::true($r);\n\n    Assert::assert($client1->sock != $client2->sock);\n\n    @$client1->close(true);\n\n    $client2->send(\"hello\");\n    echo $client2->recv();\n    @$client2->close(true);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('receive', function (Swoole\\Server $serv, $fd, $tid, $data) {\n        $serv->send($fd, \"Swoole $data\\n\");\n    });\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nSwoole hello\n"
  },
  {
    "path": "tests/swoole_client_sync/keep5.phpt",
    "content": "--TEST--\nswoole_client_sync: long connection[5]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n\n    $client1 = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_KEEP | SWOOLE_SYNC);\n    $client1->id=\"id\";\n    $r = @$client1->connect(TCP_SERVER_HOST, $pm->getFreePort(), 0.5);\n    Assert::true($r);\n\n    $client2 = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_KEEP | SWOOLE_SYNC);\n    $client2->id=\"id\";\n    $r = @$client2->connect(TCP_SERVER_HOST, $pm->getFreePort(), 0.5);\n    Assert::true($r);\n\n    Assert::assert($client1->sock != $client2->sock);\n\n    @$client1->close(true);\n\n    $client2->send(\"hello\");\n    echo $client2->recv();\n    @$client2->close(true);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('receive', function (Swoole\\Server $serv, $fd, $tid, $data) {\n        $serv->send($fd, \"Swoole $data\\n\");\n    });\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nSwoole hello\n"
  },
  {
    "path": "tests/swoole_client_sync/keep6.phpt",
    "content": "--TEST--\nswoole_client_sync: long connection[6]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n\n    $client1 = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_KEEP | SWOOLE_SYNC);\n    $r = @$client1->connect(TCP_SERVER_HOST, $pm->getFreePort(), 0.5);\n    Assert::true($r);\n    $client1->send(\"hello\");\n    echo $client1->recv();\n    $client1->close();\n\n    usleep(10000);\n\n    $client2 = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_KEEP | SWOOLE_SYNC);\n    /**\n     * recreate socket\n     */\n    $r = $client2->connect(TCP_SERVER_HOST, $pm->getFreePort(), 0.5);\n    Assert::true($r);\n    $client2->send(\"hello\");\n    echo $client2->recv();\n    $client2->close();\n\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('receive', function (Swoole\\Server $serv, $fd, $tid, $data) {\n        $serv->send($fd, \"Swoole $data\\n\");\n        usleep(5000);\n        $serv->close($fd);\n    });\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nSwoole hello\nSwoole hello\n"
  },
  {
    "path": "tests/swoole_client_sync/length_protocol.phpt",
    "content": "--TEST--\nswoole_client_sync: length protocol [sync]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Client;\nuse Swoole\\Process;\nuse Swoole\\Server;\n\nconst N_1 = IS_MAC_OS ? 400 : 1000;\nconst N_2 = 100;\nconst N_3 = IS_MAC_OS ? 400 : 1000;\n\n$port = get_one_free_port();\n$pm = new ProcessManager();\n$pm->parentFunc = function ($pid) use ($port) {\n    $client = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $client->set([\n        'open_length_check' => true,\n        'package_max_length' => 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n    ]);\n    if (!$client->connect('127.0.0.1', $port, 5, 0)) {\n        echo 'Over flow. errno=' . $client->errCode;\n        exit(\"\\n\");\n    }\n\n    $client->send(\"recv\\r\\n\\r\\n\");\n\n    // 小包\n    for ($i = 0; $i < N_1; $i++) {\n        $pkg = $client->recv();\n        Assert::assert($pkg and strlen($pkg) <= 2048);\n    }\n    echo \"SUCCESS\\n\";\n    // 慢速发送\n    for ($i = 0; $i < N_2; $i++) {\n        $pkg = $client->recv();\n        Assert::assert($pkg and strlen($pkg) <= 8192);\n    }\n    echo \"SUCCESS\\n\";\n    // 大包\n    for ($i = 0; $i < N_3; $i++) {\n        $pkg = $client->recv();\n        Assert::assert($pkg != false);\n        $_pkg = unserialize(substr($pkg, 4));\n        Assert::assert(is_array($_pkg));\n        Assert::same($_pkg['i'], $i);\n        Assert::assert(strlen($_pkg['data']) > 8192 and strlen($_pkg['data']) <= 256 * 1024);\n    }\n    echo \"SUCCESS\\n\";\n    $client->close();\n\n    Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm, $port) {\n    $serv = new Server('127.0.0.1', $port, SWOOLE_BASE);\n    $serv->set([\n        'package_max_length' => 1024 * 1024 * 2, // 2M\n        'socket_buffer_size' => 256 * 1024 * 1024,\n        'worker_num' => 1,\n        'log_file' => '/tmp/swoole.log',\n    ]);\n    $serv->on('WorkerStart', function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n        // 小包\n        for ($i = 0; $i < N_1; $i++) {\n            $data = str_repeat('A', rand(100, 2000));\n            $serv->send($fd, pack('N', strlen($data)) . $data);\n        }\n        // 慢速发送\n        for ($i = 0; $i < N_2; $i++) {\n            $data = str_repeat('A', rand(3000, 6000));\n            $n = rand(1000, 2000);\n            $serv->send($fd, pack('N', strlen($data)) . substr($data, 0, $n));\n            usleep(rand(10000, 50000));\n            $serv->send($fd, substr($data, $n));\n        }\n        // 大包\n        for ($i = 0; $i < N_3; $i++) {\n            $data = serialize(['i' => $i, 'data' => str_repeat('A', rand(20000, 256 * 1024))]);\n            $serv->send($fd, pack('N', strlen($data)) . $data);\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\nSUCCESS\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_client_sync/length_protocol_02.phpt",
    "content": "--TEST--\nswoole_client_sync: length protocol 02 [sync]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $client->set([\n        'open_length_check' => true,\n        'package_max_length' => 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 0,\n    ]);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) {\n        echo \"Over flow. errno=\" . $client->errCode;\n        die(\"\\n\");\n    }\n\n    $int = rand(1000, 5000);\n    $data = json_encode(['data' => RandStr::gen($int), 'index' => 2, 'len' => $int]);\n    $client->send(pack('N', strlen($data) + 4) . $data);\n    $pkg = $client->recv();\n    Assert::assert($pkg != false and strlen($pkg) > 100);\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'package_max_length' => 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 0,\n    ]);\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n        $data = str_repeat('A', rand(100, 2000));\n        $serv->send($fd, pack('N', strlen($data) + 4) . $data);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_sync/length_protocol_func.phpt",
    "content": "--TEST--\nswoole_client_sync: length protocol func\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $client->set([\n        'open_length_check' => true,\n        'package_max_length' => 1024 * 1024,\n        'package_length_func' => function ($data) {\n            $n = strpos($data, '|');\n            if ($n == false)\n            {\n                return -1;\n            }\n            else\n            {\n                return intval(substr($data, 0, $n)) + $n + 1;\n            }\n        },\n    ]);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) {\n        echo \"Over flow. errno=\" . $client->errCode;\n        die(\"\\n\");\n    }\n\n    $int = rand(1000, 5000);\n    $data = json_encode(['data' => RandStr::gen($int), 'index' => 2, 'len' => $int]);\n    $client->send(pack('N', strlen($data) + 4) . $data);\n    $pkg = $client->recv();\n    Assert::assert($pkg != false and strlen($pkg) > 100);\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'package_max_length' => 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 0,\n    ]);\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data)\n    {\n        $data = str_repeat('A', rand(100, 2000));\n        $serv->send($fd, strlen($data) . \"|\" . $data);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_sync/recv_in_task.phpt",
    "content": "--TEST--\nswoole_client_sync: recv in task worker\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', 0, SWOOLE_BASE);\n    $http->set(['worker_num' => 1, 'task_worker_num'=>1, 'log_file' => '/dev/null']);\n    $http->on('workerStart', function (Swoole\\Server $server, int $worker_id) use ($pm) {\n        if (!$server->taskworker) {\n            // start logic in task\n            $server->task(1, 0);\n        }\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($http) {\n        usleep(100 * 1000);\n        $response->end('OK');\n    });\n    $http->on('Task', function(Swoole\\Server $serv, int $task_id, int $src_worker_id, $data) use ($pm) {\n            //  trigger timer constantly in order to trigger the EINTR internally\n            Swoole\\Timer::tick(1, function(){});\n            // send request\n            $cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n            $p = $serv->ports[0]->port;\n            Assert::assert($cli->connect('127.0.0.1', $p, 3));\n            $request = \"GET / HTTP/1.1\\r\\n\\r\\n\";\n            $cli->send($request);\n            $response = @$cli->recv(); // the server will block by 100ms, so it will surely get EINTR internally by the task timer\n            if (!$response) {\n                if ($cli->errCode == SOCKET_EINTR) {\n                    echo \"EINTR\\n\";\n                }\n            } else {\n                echo \"SUCCESS\\n\";\n            }\n            $pm->wakeup();\n    });\n    $http->on('Finish', function(){});\n    $http->addProcess(new Swoole\\Process(function (Swoole\\Process $p) {\n                }));\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_client_sync/recv_timeout.phpt",
    "content": "--TEST--\nswoole_client_sync: recv timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $r = $client->connect(TCP_SERVER_HOST, $pm->getFreePort(), 0.5);\n    Assert::assert($r);\n    $client->send(pack('N', filesize(TEST_IMAGE)));\n    $data = @$client->recv();\n    Assert::false($data);\n    Assert::same($client->errCode, SOCKET_EAGAIN);\n    $client->close();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Swoole\\Server $serv, $fd, $rid, $data) {\n        //do nothing\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_sync/recv_with_open_eof_check.phpt",
    "content": "--TEST--\nswoole_client_sync: recv witch open_eof_check and check errCode\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    $client = new \\Swoole\\Client(SWOOLE_SOCK_TCP);\n\n    $client->set([\n        'open_eof_check'     => true,\n        'package_eof'        => \"\\n\",\n        'package_max_length' => 1024 * 1024 * 2,\n    ]);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n        throw new Exception(\"connect failed\");\n    }\n\n    $data = @$client->recv(1024 * 1024 * 2);\n    Assert::false($data);\n    Assert::eq(SOCKET_EAGAIN, $client->errCode);\n    $client->close();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_PROCESS);\n\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n    ]);\n\n    $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) {\n    });\n\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_client_sync/select.phpt",
    "content": "--TEST--\nswoole_client_sync: select\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Client;\nuse Swoole\\Server;\nuse SwooleTest\\ProcessManager;\n\nconst TIMEOUT = 0.05;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    $clients = [];\n\n    for($i=0; $i< 4; $i++) {\n        $client = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n        $ret = $client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0);\n        if(!$ret) {\n            echo \"Connect Server fail.errCode=\".$client->errCode;\n        } else {\n            $client->send(\"HELLO WORLD\\n\");\n            $clients[$client->sock] = $client;\n        }\n    }\n\n    $s = microtime(true);\n    while (!empty($clients)) {\n        $write = $error = array();\n        $read = array_values($clients);\n        $n = swoole_select($read, $write, $error, TIMEOUT);\n        if ($n > 0) {\n            foreach ($read as $index => $c) {\n                echo \"Recv #{$c->sock}: \" . $c->recv() . \"\\n\";\n                unset($clients[$c->sock]);\n            }\n            continue;\n        } else if ($n == 0) {\n            echo \"TIMEOUT\\n\";\n        } else {\n            echo \"ERROR\\n\";\n        }\n        break;\n    }\n\n    Assert::greaterThanEq(microtime(true) - $s, TIMEOUT);\n\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv)  use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Swoole\\Server $serv, $fd, $rid, $data) {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nTIMEOUT\n"
  },
  {
    "path": "tests/swoole_client_sync/select_null.phpt",
    "content": "--TEST--\nswoole_client_sync: select\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Client;\nuse Swoole\\Server;\nuse SwooleTest\\ProcessManager;\n\nconst TIMEOUT = 0.05;\n\n$pm = new ProcessManager();\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    if (!$client->connect(TCP_SERVER_HOST, $pm->getFreePort(), -1)) {\n        exit(\"connect failed. Error: {$client->errCode}\\n\");\n    }\n    $r = [$client];\n    $w = $e = null;\n    $client->send(\"hello world\\n\");\n    swoole_select($r, $w, $e);\n    echo $client->recv();\n    $client->close();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on('WorkerStart', function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('Receive', function (Server $serv, $fd, $rid, $data) {\n        $serv->send($fd, \"hello world\\n\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nhello world\n"
  },
  {
    "path": "tests/swoole_client_sync/send_recv.phpt",
    "content": "--TEST--\nswoole_client_sync: send & recv\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    Assert::assert($cli->connect('127.0.0.1', $pm->getFreePort()));\n    $request = \"GET / HTTP/1.1\\r\\n\\r\\n\";\n    Assert::same($cli->send($request), strlen($request));\n    usleep(100 * 1000);\n    $response = $cli->recv();\n    Assert::assert($response && substr($response, 0, 4) === 'HTTP');\n    echo \"SUCCESS\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end('OK');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_client_sync/sendfile.phpt",
    "content": "--TEST--\nswoole_client_sync: sync sendfile\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$port = get_one_free_port();\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($port) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $r = $client->connect(TCP_SERVER_HOST, $port, 0.5);\n    Assert::assert($r);\n    $client->send(pack('N', filesize(TEST_IMAGE)));\n    $ret = $client->sendfile(TEST_IMAGE);\n    Assert::assert($ret);\n\n    $data = $client->recv();\n    $client->send(pack('N', 8) . 'shutdown');\n    $client->close();\n    Assert::same($data, md5_file(TEST_IMAGE));\n};\n\n$pm->childFunc = function () use ($pm, $port) {\n    $serv = new Swoole\\Server(TCP_SERVER_HOST, $port, SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'dispatch_mode' => 1,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n        'package_max_length' => 2000000,\n    ]);\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Swoole\\Server $serv, $fd, $rid, $data) {\n        if (substr($data, 4, 8) == 'shutdown') {\n            $serv->shutdown();\n            return;\n        }\n        $serv->send($fd, md5(substr($data, 4)));\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_sync/sendto.phpt",
    "content": "--TEST--\nswoole_client_sync: sendto\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip('fixme');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Client;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    $cli = new Client(SWOOLE_SOCK_UDP);\n\n    Assert::true($cli->sendto('127.0.0.1', $pm->getFreePort(), \"packet-1\"));\n    Assert::true($cli->sendto('localhost', $pm->getFreePort(), \"packet-2\"));\n    Assert::false($cli->sendto('error_domain', $pm->getFreePort(), \"hello\"));\n    Assert::assert($cli->errCode, SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n    Assert::true($cli->sendto('localhost', $pm->getFreePort(), \"packet-3\"));\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_DGRAM, 0);\n        $socket->bind('127.0.0.1', $pm->getFreePort());\n        $pm->wakeup();\n        $peer = null;\n        $ret = $socket->recvfrom($peer);\n        Assert::eq($ret, 'packet-1');\n        $ret = $socket->recvfrom($peer);\n        Assert::eq($ret, 'packet-2');\n        $ret = $socket->recvfrom($peer);\n        Assert::eq($ret, 'packet-3');\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nWarning: Swoole\\Client::sendto(): sendto to server[error_domain:%d] failed. Error: DNS Lookup resolve failed[704] in %ssendto.php on line %d\nDONE\n"
  },
  {
    "path": "tests/swoole_client_sync/socks5_proxy.phpt",
    "content": "--TEST--\nswoole_client_sync: http client with socks5 proxy\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_socks5_proxy();\nskip_if_offline();\nskip_if_in_ci();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire TESTS_API_PATH . '/swoole_client/http_get.php';\n\n$cli = new Swoole\\Client(SWOOLE_TCP);\n$cli->set([\n    'timeout' => 30,\n    'socks5_host' => SOCKS5_PROXY_HOST,\n    'socks5_port' => SOCKS5_PROXY_PORT\n]);\nclient_http_v10_get($cli);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_sync/ssl_recv_timeout.phpt",
    "content": "--TEST--\nswoole_client_sync: ssl recv timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC);\n    $r = $cli->connect('127.0.0.1', $pm->getFreePort(), 5);\n    Assert::assert($r);\n    $cli->send(\"hello world\\n\");\n    $time = time();\n    $data = $cli->recv(1024);\n    Assert::assert((time() - $time) < 2);\n    Assert::same($data, \"Swoole hello world\\n\");\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $serv->set([\n        'log_file' => '/dev/null',\n        'ssl_cert_file' => SSL_FILE_DIR.'/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR.'/server.key',\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $tid, $data) {\n        $serv->send($fd, \"Swoole $data\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_sync/sync_send_recv.phpt",
    "content": "--TEST--\nswoole_client_sync: udp sync client send & recv\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/simple_udp_server.php\";\nstart_server($simple_tcp_server, UDP_SERVER_HOST, UDP_SERVER_PORT);\n\n$client = new Swoole\\Client(SWOOLE_SOCK_UDP);\n$client->connect(UDP_SERVER_HOST, UDP_SERVER_PORT);\n\n$data = \"UdpSendto\";\n$client->send($data);\nusleep(100 * 1000);\n$message = $client->recv();\necho \"FromServer:$message\\n\";\necho \"SUCCESS\\n\";\n\n?>\n--EXPECTF--\nFromServer:UdpSendto\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_client_sync/udg_send_timeout.phpt",
    "content": "--TEST--\nswoole_client_sync: udg send timeout\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nconst N = 65507;\nuse Swoole\\Client;\n\ndefine('SOCKET_FILE', __DIR__ . '/server.sock');\n$socket = stream_socket_server('udg://' . SOCKET_FILE, $errno, $errstr, STREAM_SERVER_BIND);\nusleep(100000);\n\n$client = new Client(SWOOLE_SOCK_UNIX_DGRAM);\n$client->connect(SOCKET_FILE, 0, 0.3);\n$s = microtime(true);\n\nwhile (true) {\n    $re = $ret = $client->sendto(SOCKET_FILE, 0, str_repeat('B', N));\n    if ($re == false) {\n        break;\n    }\n}\nunlink(SOCKET_FILE);\nAssert::lessThan(microtime(true) - $s, 0.8);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_client_sync/udp_client_sendto.phpt",
    "content": "--TEST--\nswoole_client_sync: udp sync client sendto\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/simple_udp_server.php\";\nstart_server($simple_tcp_server, UDP_SERVER_HOST, UDP_SERVER_PORT);\n\n$client = new Swoole\\Client(SWOOLE_SOCK_UDP);\n$client->connect(UDP_SERVER_HOST, UDP_SERVER_PORT);\n\n$ret = $client->sendto(UDP_SERVER_HOST, UDP_SERVER_PORT, \"TestUdpClientSendto\");\n$message = $client->recv();\necho \"FromServer:{$message}\\n\";\necho \"SUCCESS\";\n\n?>\n--EXPECT--\nFromServer:TestUdpClientSendto\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_coroutine/after_start_server_1.phpt",
    "content": "--TEST--\nswoole_coroutine: co::create after server start [1]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ndefine('SECRET', SwooleTest\\RandStr::getBytes(rand(1024, 8192)));\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        Assert::notEmpty(!empty($data));\n        Assert::same($data, SECRET);\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n\n    $http->set([\n        'log_file' => '/dev/null',\n        \"worker_num\" => 1,\n    ]);\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function ($request, Swoole\\Http\\Response $response) {\n        $response->end(SECRET);\n    });\n\n    $http->start();\n\n    echo \"server shutdown\\n\";\n\n    go(function () use ($pm) {\n        Co::readFile(__FILE__);\n        echo \"co shutdown\\n\";\n    });\n    Swoole\\Event::wait();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nserver shutdown\nco shutdown\n"
  },
  {
    "path": "tests/swoole_coroutine/all_asleep.phpt",
    "content": "--TEST--\nswoole_coroutine: all asleep\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nCo::set([\n    'enable_deadlock_check' => true,\n]);\n\nfunction test1()\n{\n    $f = function () {\n        Co::yield();\n    };\n    $f();\n}\n\nfunction test2()\n{\n    Co::yield();\n}\n\nCo\\run(function () {\n    go(function () {\n        test1();\n    });\n    go(function () {\n        test2();\n    });\n    Co::sleep(0.1);\n});\necho \"DONE\\n\";\n?>\n--EXPECTF--\n\n =================================================================\n  [FATAL ERROR]: all coroutines (count: 2) are asleep - deadlock!\n =================================================================\n\n  [Coroutine-3]\n -----------------------------------------------------------------\n%A\n%A\n%A\n\n  [Coroutine-2]\n -----------------------------------------------------------------\n%A\n%A\n%A\n%A\n\n =================================================================\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine/array_walk.phpt",
    "content": "--TEST--\nswoole_coroutine: array_walk\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nCo\\run(function () {\n    for ($n = 2; $n--;) {\n        go(function () {\n            $array = range(0, 1);\n            array_walk($array, function ($item) {\n                Co::sleep([0.01, 0.001][$item]);\n            });\n        });\n    }\n});\necho \"DONE\\n\";\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine/array_walk_deep.phpt",
    "content": "--TEST--\nswoole_coroutine: array_walk\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nCo\\run(function () {\n    for ($n = 2; $n--;) {\n        go(function () {\n            $array = range(0, 1);\n            array_walk($array, function ($item) use ($array) {\n                array_walk($array, function ($item) use ($array) {\n                    array_walk($array, function ($item) use ($array) {\n                        Co::sleep([0.01, 0.001][$item]);\n                    });\n                    Co::sleep([0.01, 0.001][$item]);\n                    array_walk($array, function ($item) use ($array) {\n                        Co::sleep([0.01, 0.001][$item]);\n                    });\n                });\n                Co::sleep([0.01, 0.001][$item]);\n                array_walk($array, function ($item) use ($array) {\n                    array_walk($array, function ($item) use ($array) {\n                        Co::sleep([0.01, 0.001][$item]);\n                    });\n                    Co::sleep([0.01, 0.001][$item]);\n                    array_walk($array, function ($item) use ($array) {\n                        Co::sleep([0.01, 0.001][$item]);\n                    });\n                });\n            });\n        });\n    }\n});\necho \"DONE\\n\";\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine/async_callback/event_cycle.phpt",
    "content": "--TEST--\nswoole_coroutine/async_callback: event cycle\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Event;\n\nconst N = 4;\n\n$GLOBALS['count'] = 0;\n$GLOBALS['logs'] = [];\n\nCo\\run(function () {\n    Event::cycle(function () {\n        $GLOBALS['count']++;\n        if ($GLOBALS['count'] == N) {\n            Event::cycle(null);\n        }\n        Co::sleep(0.02);\n        $GLOBALS['logs'] [] = \"cycle\\n\";\n    });\n\n    $n = N;\n    while ($n--) {\n        Co::sleep(0.01);\n        $GLOBALS['logs'] [] = \"sleep\\n\";\n    }\n});\n\n$str = implode('', $GLOBALS['logs']);\nAssert::eq(substr_count($str, 'cycle'), N);\nAssert::eq(substr_count($str, 'sleep'), N);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/async_callback/signal.phpt",
    "content": "--TEST--\nswoole_coroutine/async_callback: signal\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Process;\n\nCo\\run(function () {\n    Process::signal(SIGUSR1, function ($signo) {\n        Co::sleep(0.5);\n        Assert::eq($signo, SIGUSR1);\n        echo \"Done\\n\";\n    });\n\n    Co::sleep(0.01);\n    Process::kill(posix_getpid(), SIGUSR1);\n    Co::sleep(0.02);\n});\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_coroutine/async_callback/timer.phpt",
    "content": "--TEST--\nswoole_coroutine/async_callback: timer\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Timer;\n\n$GLOBALS['count'] = 0;\n\nCo\\run(function () {\n    Timer::tick(50, function ($timer) {\n        $GLOBALS['count']++;\n        if ($GLOBALS['count'] == 5) {\n            Timer::clear($timer);\n        }\n        Co::sleep(0.5);\n        echo \"tick\\n\";\n    });\n});\n?>\n--EXPECT--\ntick\ntick\ntick\ntick\ntick\n"
  },
  {
    "path": "tests/swoole_coroutine/autoload.phpt",
    "content": "--TEST--\nswoole_coroutine: autoload\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nspl_autoload_register(function ($class) {\n    co::sleep(0.001); // coroutine context switch\n    if ($class == 'SwooleTestClassA') {\n        require TESTS_ROOT_PATH . '/include/api/test_classes/A.php';\n    }\n});\n\nSwoole\\Coroutine\\run(function () {\n    for ($i = 0; $i < 2; ++$i)\n    {\n        go(static function (): void {\n            var_dump(new SwooleTestClassA());\n        });\n    }\n});\n\n?>\n--EXPECTF--\nobject(SwooleTestClassA)#%d (0) {\n}\nobject(SwooleTestClassA)#%d (0) {\n}\n"
  },
  {
    "path": "tests/swoole_coroutine/autoload_not_found.phpt",
    "content": "--TEST--\nswoole_coroutine: autoload not found\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nspl_autoload_register(function ($class) {\n    if ($class == 'SwooleTestClassA2') {\n        require TESTS_ROOT_PATH . '/include/api/test_classes/A2.php';\n    }\n});\n\nCo\\run( function() {\n    try {\n        var_dump(new SwooleTestClassA2());\n    } catch (\\Throwable $e) {\n        Assert::contains($e->getMessage(), 'Class \"SwooleTestClassA2\" not found');\n        echo \"DONE\\n\";\n    }\n\n});\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine/autoload_not_found_not_in_coroutine.phpt",
    "content": "--TEST--\nswoole_coroutine: autoload not found and not in coroutine\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nspl_autoload_register(function ($class) {\n    if ($class == 'SwooleTestClassA2') {\n        require TESTS_ROOT_PATH . '/include/api/test_classes/A2.php';\n    }\n});\n\nvar_dump(new SwooleTestClassA2());\n\n?>\n--EXPECTF--\nFatal error: Uncaught Error: Class \"SwooleTestClassA2\" not found in %s:%d\nStack trace:\n#0 %s(%d): %s\n#1 %s(%d): %s\n#2 {main}\n  thrown in %s on line %d\n"
  },
  {
    "path": "tests/swoole_coroutine/autoload_not_in_coroutine.phpt",
    "content": "--TEST--\nswoole_coroutine: autoload not in coroutine\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nspl_autoload_register(function ($class) {\n    if ($class == 'SwooleTestClassA') {\n        require TESTS_ROOT_PATH . '/include/api/test_classes/A.php';\n    }\n});\n\nvar_dump(new SwooleTestClassA());\nvar_dump(new SwooleTestClassA());\n\n?>\n--EXPECTF--\nobject(SwooleTestClassA)#%d (0) {\n}\nobject(SwooleTestClassA)#%d (0) {\n}\n"
  },
  {
    "path": "tests/swoole_coroutine/bailout/co_redis_in_shutdown_function.phpt",
    "content": "--TEST--\nswoole_coroutine/bailout: call co redis in shutdown function\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Event;\n\nCo\\run(function () {\n    $redis = new \\redis();\n    $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT);\n    register_shutdown_function(function () use ($redis) {\n        $redis->get('key');\n    });\n    usleep(10000);\n});\n\nEvent::wait();\n?>\n--EXPECTF--\nFatal error: Uncaught Swoole\\Error: API must be called in the coroutine in %s:%d\nStack trace:\n#0 %s(%d): Redis->get('key')\n#1 [internal function]: {closure%S}()\n#2 {main}\n  thrown in %s on line %d\n"
  },
  {
    "path": "tests/swoole_coroutine/bailout/error.phpt",
    "content": "--TEST--\nswoole_coroutine/bailout: error\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Event;\n\nregister_shutdown_function(function () {\n    echo 'shutdown' . PHP_EOL;\n});\ngo(function () {\n    throw new Error;\n});\nEvent::wait();\n?>\n--EXPECTF--\nFatal error: Uncaught Error in %s:%d\nStack trace:\n%A\n  thrown in %s on line %d\nshutdown\n"
  },
  {
    "path": "tests/swoole_coroutine/bailout/error_in.phpt",
    "content": "--TEST--\nswoole_coroutine/bailout: error in the coroutine\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Event;\nSwoole\\Runtime::enableCoroutine();\n\n$func = function () {\n    echo 'aaa' . PHP_EOL;\n};\n\n$socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\ngo(function () use ($socket, $func) {\n    $socket->connect('192.0.0.1', 80);\n    defer($func);\n    defer('bbb');\n    defer(function () use ($func) {\n        echo 'ccc' . PHP_EOL;\n        var_dump($func);\n    });\n});\n\nfunction bbb()\n{\n    echo 'bbb' . PHP_EOL;\n}\n\ngo(function () {\n    $fp = stream_socket_client(\"tcp://127.0.0.1:3306\", $errno, $errstr, 1);\n    echo fread($fp, 8192) . PHP_EOL;\n});\n\ngo(function () {\n    a();\n});\nEvent::wait();\n?>\n--EXPECTF--\nFatal error: Uncaught Error: Call to undefined function a() in %s:%d\nStack trace:\n%A\n  thrown in %s on line %d\n"
  },
  {
    "path": "tests/swoole_coroutine/bailout/error_internal.phpt",
    "content": "--TEST--\nswoole_coroutine/bailout: error\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\ngo(function () {\n    $a = str_repeat('A', 1024 * 1024 * 1024 * 1024);\n    co::sleep(0.1);\n});\n?>\n--EXPECTF--\nFatal error: Allowed memory size of %d bytes exhausted %s\n--EXPECTF_85--\nFatal error: Allowed memory size of %d bytes exhausted %s\nStack trace:\n#0 %s(%d): str_repeat('A', %d)\n#1 [internal function]: {%s:%d}()\n#2 {main}\n"
  },
  {
    "path": "tests/swoole_coroutine/bailout/error_internal2.phpt",
    "content": "--TEST--\nswoole_coroutine/bailout: error\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\ngo(function () {\n    go(function () {\n        $a = str_repeat('A', 1024 * 1024 * 1024 * 1024);\n        co::sleep(0.1);\n    });\n});\n?>\n--EXPECTF--\nFatal error: Allowed memory size of %d bytes exhausted %s\n--EXPECTF_85--\nFatal error: Allowed memory size of %d bytes exhausted %s\nStack trace:\n#0 %s(%d): str_repeat('A', %d)\n#1 [internal function]: {closure:{closure:%s:%d}:%d}()\n#2 {main}\n"
  },
  {
    "path": "tests/swoole_coroutine/bailout/error_out.phpt",
    "content": "--TEST--\nswoole_coroutine/bailout: error out of the coroutine\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Event;\nSwoole\\Runtime::enableCoroutine();\n\n$func = function () {\n    echo 'aaa' . PHP_EOL;\n};\n\n$socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\ngo(function () use ($socket, $func) {\n    $socket->connect('192.0.0.1', 80);\n    defer($func);\n    defer('bbb');\n    defer(function () use ($func) {\n        echo 'ccc' . PHP_EOL;\n        var_dump($func);\n    });\n});\n\nfunction bbb()\n{\n    echo 'bbb' . PHP_EOL;\n}\n\ngo(function () {\n    $fp = stream_socket_client(\"tcp://127.0.0.1:3306\", $errno, $errstr, 1);\n    echo fread($fp, 8192) . PHP_EOL;\n});\n\na();\n\nEvent::wait();\n?>\n--EXPECTF--\nFatal error: Uncaught Error: Call to undefined function a() in %s:%d\nStack trace:\n#0 {main}\n  thrown in %s on line %d\n"
  },
  {
    "path": "tests/swoole_coroutine/bailout/exit.phpt",
    "content": "--TEST--\nswoole_coroutine/bailout: error\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$process = new Swoole\\Process(function () {\n    register_shutdown_function(function () {\n        echo 'shutdown' . PHP_EOL;\n    });\n    go(function () {\n        try {\n            exit(0);\n        } catch (Swoole\\ExitException $e) {\n            echo $e->getMessage() . \"\\n\";\n        }\n    });\n});\n$process->start();\n$status = $process::wait();\nif (Assert::isArray($status)) {\n    list($pid, $code, $signal) = array_values($status);\n    Assert::greaterThan($pid, 0);\n    Assert::same($code, 0);\n    Assert::same($signal, 0);\n}\n?>\n--EXPECT--\nswoole exit\nshutdown\n"
  },
  {
    "path": "tests/swoole_coroutine/before_create_server_1.phpt",
    "content": "--TEST--\nswoole_coroutine: co::create before server create [1]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_unsupported();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ndefine('SECRET', SwooleTest\\RandStr::getBytes(rand(1024, 8192)));\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        Assert::notEmpty($data);\n        Assert::same($data, SECRET);\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        Co::sleep(0.1);\n        $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n        $http->set([\n            'log_file' => '/dev/null',\n            \"worker_num\" => 1,\n        ]);\n        $http->on(\"WorkerStart\", function ($serv, $wid) use ($pm) {\n            $pm->wakeup();\n        });\n        $http->on(\"request\", function ($request, Swoole\\Http\\Response $response) {\n            $response->end(SECRET);\n        });\n\n        $http->start();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine/before_create_server_2.phpt",
    "content": "--TEST--\nswoole_coroutine: co::create before server create [2]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ndefine('SECRET', SwooleTest\\RandStr::getBytes(rand(1024, 8192)));\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        Assert::notEmpty(!empty($data));\n        Assert::same($data, SECRET);\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        co::sleep(0.1);\n        echo \"co shutdown\\n\";\n    });\n    Swoole\\Event::wait();\n\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n\n    $http->set([\n        'log_file' => '/dev/null',\n        \"worker_num\" => 1,\n    ]);\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function ($request, Swoole\\Http\\Response $response) {\n        $response->end(SECRET);\n    });\n\n    $http->start();\n\n    echo \"server shutdown\\n\";\n};\n\n$pm->childFirst();\n$pm->run();\n\n\n\n?>\n--EXPECT--\nco shutdown\nserver shutdown\n"
  },
  {
    "path": "tests/swoole_coroutine/before_create_server_3.phpt",
    "content": "--TEST--\nswoole_coroutine: co::create before server create [2]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ndefine('SECRET', SwooleTest\\RandStr::getBytes(rand(1024, 8192)));\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        Assert::notEmpty(!empty($data));\n        Assert::same($data, SECRET);\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        Co::readFile(__FILE__);\n        echo \"co shutdown\\n\";\n    });\n    Swoole\\Event::wait();\n\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n\n    $http->set([\n        'log_file' => '/dev/null',\n        \"worker_num\" => 1,\n    ]);\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function ($request, Swoole\\Http\\Response $response) {\n        $response->end(SECRET);\n    });\n\n    $http->start();\n\n    echo \"server shutdown\\n\";\n};\n\n$pm->childFirst();\n$pm->run();\n\n\n\n?>\n--EXPECT--\nco shutdown\nserver shutdown\n"
  },
  {
    "path": "tests/swoole_coroutine/bug_2387.phpt",
    "content": "--TEST--\nswoole_coroutine: call_user_func_array\n--SKIPIF--\n<?php \nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_database();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse SwooleTest\\MysqlPool;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/list\");\n        Assert::assert(!empty($data) && count(json_decode($data, true)) > 0);\n        $pm->kill();\n    });\n    \\Swoole\\Event::wait();\n};\n$pm->childFunc = function () use ($pm) {\n    $config = [\n        'host' => MYSQL_SERVER_HOST,\n        'port' => MYSQL_SERVER_PORT,\n        'user' => MYSQL_SERVER_USER,\n        'password' => MYSQL_SERVER_PWD,\n        'database' => MYSQL_SERVER_DB,\n        'timeout' => 0.5,\n        'charset' => 'utf8mb4',\n        'strict_type' => true,\n        'pool_size' => '3',\n        'pool_get_timeout' => 0.5,\n    ];\n    $httpServer = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $httpServer->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 1,\n        'hook_flags' => SWOOLE_HOOK_ALL,\n    ]);\n    $httpServer->on('WorkerStart', function (Swoole\\Http\\Server $server) use ($pm, $config) {\n        try {\n            MysqlPool::getInstance($config);\n        } catch (\\Exception $e) {\n            echo $e->getMessage() . PHP_EOL;\n            $server->shutdown();\n        } catch (\\Throwable $throwable) {\n            echo $throwable->getMessage() . PHP_EOL;\n            $server->shutdown();\n        }\n        $pm->wakeup();\n    });\n    $httpServer->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        if ($request->server['path_info'] == '/list') {\n            go(function () use ($request, $response) {\n                try {\n                    $pool = MysqlPool::getInstance();\n                    $mysql = $pool->get();\n                    defer(function () use ($mysql) {\n                        MysqlPool::getInstance()->put($mysql);\n                        echo \"size = \" . MysqlPool::getInstance()->getLength() . PHP_EOL;\n                    });\n                    $result = $mysql->query(\"show tables\");\n                    $response->end(json_encode($result));\n                } catch (\\Exception $e) {\n                    $response->end($e->getMessage());\n                }\n            });\n        }\n    });\n    $httpServer->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nsize = 3\n"
  },
  {
    "path": "tests/swoole_coroutine/bug_5699.phpt",
    "content": "--TEST--\nswoole_coroutine: Github bug #5699\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nini_set('swoole.enable_fiber_mock', 'On');\nuse function Swoole\\Coroutine\\run;\n\nrun(function() {\n    sleep(1);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/c_stack_size.phpt",
    "content": "--TEST--\nswoole_coroutine: c_stack_size\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst K = 1024;\nconst M = K * 1024;\nconst G = M * 1024;\n\n// echo \"default 2M\\n\";\n$info = co::stats();\nAssert::assert($info['c_stack_size'] == 2 * M);\nAssert::assert($info['coroutine_num'] == 0);\nAssert::assert($info['coroutine_peak_num'] == 0);\n\nco::set(['c_stack_size' => 1 * M]);\nfor ($n = 100; $n--;) {\n    go(function () { co::sleep(0.001); });\n}\n// echo \"1M\\n\";\n$info = co::stats();\nAssert::assert($info['c_stack_size'] == M);\nAssert::assert($info['coroutine_num'] == 100);\nAssert::assert($info['coroutine_peak_num'] == 100);\n\nco::set(['c_stack_size' => 1 * K]); // will be extend\nfor ($n = 100; $n--;) {\n    go(function () { co::sleep(0.001); });\n}\n// echo \"256K\\n\";\n$info = co::stats();\nAssert::assert($info['c_stack_size'] == 64 * K);\nAssert::assert($info['coroutine_num'] == 200);\nAssert::assert($info['coroutine_peak_num'] == 200);\n\nco::set(['c_stack_size' => 511 * K]); // will be aligned\nfor ($n = 100; $n--;) {\n    go(function () { co::sleep(0.001); });\n}\n// echo \"512K\\n\";\n$info = co::stats();\nAssert::assert($info['c_stack_size'] == 512 * K);\nAssert::assert($info['coroutine_num'] == 300);\nAssert::assert($info['coroutine_peak_num'] == 300);\n\nco::set(['c_stack_size' => 1 * G]); // will be limit\nfor ($n = 100; $n--;) {\n    go(function () { co::sleep(0.001); });\n}\n// echo \"16M\\n\";\n$info = co::stats();\nAssert::assert($info['c_stack_size'] == 16 * M);\nAssert::assert($info['coroutine_num'] == 400);\nAssert::assert($info['coroutine_peak_num'] == 400);\n\nco::set(['c_stack_size' => -1]); // will be limit\nfor ($n = 100; $n--;) {\n    go(function () { co::sleep(0.001); });\n}\n// echo \"16M\\n\";\n$info = co::stats();\nAssert::assert($info['c_stack_size'] == 16 * M);\nAssert::assert($info['coroutine_num'] == 500);\nAssert::assert($info['coroutine_peak_num'] == 500);\n?>\nDONE\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine/call_not_exists_func.phpt",
    "content": "--TEST--\nswoole_coroutine: call_not_exists_func\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    go(function () use ($pm) {\n        Assert::throws(function () use ($pm) {\n            httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        }, Exception::class);\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['worker_num' => 1]);\n    $http->on('workerStart', function () use ($pm) {\n        none();\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        co::sleep(0.001);\n        throw new Exception('whoops');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nFatal error: Uncaught Error: Call to undefined function none() in %s:%d\nStack trace:\n%A\n  thrown in %s on line %d\n[%s]\tERROR\tphp_swoole_server_rshutdown() (ERRNO %d): Fatal error: Uncaught Error: Call to undefined function none() in %s:%d\nStack trace:\n%A\n  thrown in %s on line %d\n"
  },
  {
    "path": "tests/swoole_coroutine/call_user_func_array.phpt",
    "content": "--TEST--\nswoole_coroutine: call_user_func_array\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nclass A {\n\tpublic function foo($params) {\n        echo \"$params\\n\";\n        call_user_func_array([$this, \"bar\"], [\"bar\"]);\t\t\n\t}\n\tprotected function bar($params) {\n        echo \"$params\\n\";\n\t}\n}\n$a = new A;\ncall_user_func_array([$a, \"foo\"], [\"foo\"]);\n?>\n--EXPECT--\nfoo\nbar\n"
  },
  {
    "path": "tests/swoole_coroutine/call_user_func_array2.phpt",
    "content": "--TEST--\nswoole_coroutine: call_user_func_array 2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nclass A\n{\n    function bar()\n    {\n        echo \"bar\\n\";\n        co::sleep(.02);\n        $result = co::gethostbyname('www.tsinghua.edu.cn');\n        echo \"end\\n\";\n        return $result;\n    }\n}\n\ngo(function () {\n    $a = new A;\n    $result = call_user_func_array([$a, 'bar'], []);\n    Assert::same($result, gethostbyname('www.tsinghua.edu.cn'));\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\nbar\nend\n"
  },
  {
    "path": "tests/swoole_coroutine/call_with_args.phpt",
    "content": "--TEST--\nswoole_coroutine: coro with args\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nclass TestCo\n{\n    public function foo(...$args)\n    {\n        co::sleep(0.001);\n        $cid = go(function () {\n            co::yield();\n        });\n        co::resume($cid);\n        echo @$this->test;\n\n        foreach ($args as $index => $arg) {\n            $recv = new Chan;\n            $send = new Chan;\n            $data = $args[$index];\n            go(function () use ($recv, $data) {\n                co::sleep(0.001);\n                $recv->push($data); // response\n            });\n            go(function () use ($send, $data) {\n                $data = $send->pop();\n                if (Assert::assert($data === $data)) {\n                    co::sleep(0.001);\n                    $send->push(true); // send ok\n                }\n            });\n            $ret = $send->push($data);\n            Assert::assert($ret);\n            $response = $recv->pop();\n            Assert::same($response, $data);\n        }\n    }\n}\n\n$php_args = [\n    'undef',\n    null,\n    true,\n    false,\n    1,\n    1.1,\n    str_repeat('exit', 1),\n    array_merge(['exit' => 'ok'], []),\n    (object)['exit' => 'ok'],\n    STDIN,\n    0\n];\n\ngo([new TestCo, 'foo'], ...$php_args);\n\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_coroutine/callback.phpt",
    "content": "--TEST--\nswoole_coroutine: coro callback\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nclass TestCo\n{\n    public function foo()\n    {\n        co::sleep(0.001);\n        $cid = go(function () {\n            co::yield();\n        });\n        co::resume($cid);\n        echo @$this->test;\n    }\n}\n\nfor ($c = MAX_CONCURRENCY; $c--;) {\n    go([new TestCo, 'foo']);\n}\n\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_coroutine/cancel/channel_pop.phpt",
    "content": "--TEST--\nswoole_coroutine/cancel: pop from channel\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\nrun(function () {\n    $chan = new Coroutine\\Channel(4);\n    $cid = Coroutine::getCid();\n    go(function () use ($cid) {\n        System::sleep(0.002);\n        Assert::true(Coroutine::cancel($cid));\n    });\n    Assert::eq($chan->pop(100), false);\n    Assert::assert(Coroutine::isCanceled());\n    Assert::eq($chan->errCode, SWOOLE_CHANNEL_CANCELED);\n    echo \"Done\\n\";\n});\n\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_coroutine/cancel/channel_push.phpt",
    "content": "--TEST--\nswoole_coroutine/cancel: push to channel\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\nrun(function () {\n    $chan = new Coroutine\\Channel(1);\n    $cid = Coroutine::getCid();\n    go(function () use ($cid) {\n        System::sleep(0.002);\n        Assert::true(Coroutine::cancel($cid));\n    });\n\n    Assert::assert($chan->push(\"hello world [1]\", 100));\n    Assert::eq(Coroutine::isCanceled(), false);\n    Assert::eq($chan->errCode, SWOOLE_CHANNEL_OK);\n\n    Assert::eq($chan->push(\"hello world [2]\", 100), false);\n    Assert::eq(Coroutine::isCanceled(), true);\n    Assert::eq($chan->errCode, SWOOLE_CHANNEL_CANCELED);\n\n    echo \"Done\\n\";\n});\n\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_coroutine/cancel/error.phpt",
    "content": "--TEST--\nswoole_coroutine/cancel: error\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\nrun(function () {\n    Assert::false(Coroutine::cancel(Coroutine::getCid()));\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_CANNOT_CANCEL);\n    \n    Assert::false(Coroutine::cancel(999));\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_NOT_EXISTS);\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/cancel/gethostbyname.phpt",
    "content": "--TEST--\nswoole_coroutine/cancel: gethostbyname\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Event;\nuse Swoole\\Coroutine\\System;\n\nrun(function () {\n    $cid = Coroutine::getCid();\n    Event::defer(function () use ($cid) {\n        Assert::true(Coroutine::cancel($cid));\n    });\n    $retval = System::gethostbyname('www.baidu.com');\n    echo \"Done\\n\";\n    Assert::eq($retval, false);\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_CANCELED);\n});\n\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_coroutine/cancel/kill.phpt",
    "content": "--TEST--\nswoole_coroutine/cancel: kill\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\nskip(\"experimental\");\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\nrun(function () {\n    $cid = go(function () {\n        while (true) {\n            System::sleep(0.1);\n            echo \"co 2 running\\n\";\n        }\n        var_dump('end');\n    });\n\n    System::sleep(0.3);\n    Co::cancel($cid, true);\n\n    System::sleep(0.2);\n    echo \"co 1 end\\n\";\n});\n?>\n--EXPECT--\nco 2 running\nco 2 running\nco 1 end\n"
  },
  {
    "path": "tests/swoole_coroutine/cancel/sleep.phpt",
    "content": "--TEST--\nswoole_coroutine/cancel: sleep\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\nrun(function () {\n    $cid = Coroutine::getCid();\n    go(function () use ($cid) {\n        System::sleep(0.002);\n        Assert::true(Coroutine::cancel($cid));\n        Assert::false(Coroutine::isCanceled());\n    });\n    $retval = System::sleep(10000);\n    echo \"Done\\n\";\n    Assert::eq($retval, false);\n    Assert::assert(Coroutine::isCanceled());\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_CANCELED);\n});\n\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_coroutine/cancel/socket.phpt",
    "content": "--TEST--\nswoole_coroutine/cancel: socket\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\nrun(function() {\n    $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_DGRAM, 0);\n    $socket->bind('127.0.0.1', 9601);\n    // server\n    $cid = go(function () use ($socket) {\n        while (true) {\n            $peer = null;\n            $data = $socket->recvfrom($peer);\n            Assert::assert(empty($data));\n            Assert::assert($socket->errCode == SOCKET_ECANCELED);\n            break;\n        }\n        echo \"DONE\\n\";\n    });\n    \n    // client\n    co::sleep(0.1);\n    Assert::true(Coroutine::cancel($cid));\n});\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine/cancel/suspend.phpt",
    "content": "--TEST--\nswoole_coroutine/cancel: suspend\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\nrun(function () {\n    $cid = Coroutine::getCid();\n    go(function () use ($cid) {\n        System::sleep(0.002);\n        Assert::true(Coroutine::cancel($cid));\n    });\n    $retval = Coroutine::suspend();\n    echo \"Done\\n\";\n    Assert::eq($retval, false);\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_CANCELED);\n});\n\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_coroutine/cancel/throw_exception.phpt",
    "content": "--TEST--\nswoole_coroutine/cancel: throw exception\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\nrun(function () {\n    $cid = go(function () {\n        try {\n            while (true) {\n                System::sleep(0.1);\n                echo \"co 2 running\\n\";\n            }\n            var_dump('end');\n        } catch (Swoole\\Coroutine\\CanceledException $e) {\n            var_dump('cancelled');\n        }\n    });\n\n    System::sleep(0.3);\n    Co::cancel($cid, true);\n\n    System::sleep(0.2);\n    echo \"co 1 end\\n\";\n});\n?>\n--EXPECT--\nco 2 running\nco 2 running\nstring(9) \"cancelled\"\nco 1 end\n"
  },
  {
    "path": "tests/swoole_coroutine/cancel/wait.phpt",
    "content": "--TEST--\nswoole_coroutine/cancel: wait/waitpid\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Process;\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\n$proc = new Process(function (){\n    sleep(100);\n}, false, 0);\n$proc->start();\n\nrun(function () use ($proc) {\n    $cid = Coroutine::getCid();\n    go(function () use ($cid) {\n        System::sleep(0.002);\n        Assert::true(Coroutine::cancel($cid));\n    });\n\n    $retval = System::wait();\n    echo \"Done\\n\";\n    Process::kill($proc->pid, SIGKILL);\n    Assert::eq($retval, false);\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_CANCELED);\n});\n\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_coroutine/cancel/wait_event.phpt",
    "content": "--TEST--\nswoole_coroutine/cancel: waitEvent\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\nrun(function () {\n    $fp = stream_socket_client('tcp://www.baidu.com:80/', $errno, $errmsg, 1);\n    Assert::assert($fp);\n\n    $cid = Coroutine::getCid();\n    go(function () use ($cid) {\n        System::sleep(0.002);\n        Assert::true(Coroutine::cancel($cid));\n    });\n\n    $retval = System::waitEvent($fp);\n    echo \"Done\\n\";\n    Assert::eq($retval, false);\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_CANCELED);\n});\n\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_coroutine/cancel/wait_signal.phpt",
    "content": "--TEST--\nswoole_coroutine/cancel: waitSignal\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\nrun(function () {\n    $cid = Coroutine::getCid();\n    go(function () use ($cid) {\n        System::sleep(0.002);\n        Assert::true(Coroutine::cancel($cid));\n    });\n    $retval = System::waitSignal(SIGTERM);\n    echo \"Done\\n\";\n    Assert::eq($retval, false);\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_CANCELED);\n});\n\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_coroutine/check.phpt",
    "content": "--TEST--\nswoole_coroutine: check if is in the coroutine\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ndefine('RUN_IN_CHILD', true);\n\n$map = [\n    function () {\n        Co::sleep(0.001);\n        Assert::assert(0); // never here\n    },\n    function () {\n        Co::yield();\n    },\n    function () {\n        defer(function () { });\n    },\n    function () {\n        $chan = new Chan;\n        $chan->push('foo');\n        $chan->push('bar');\n        Assert::assert(0); // never here\n    },\n    function () {\n        (new Chan)->pop();\n        Assert::assert(0); // never here\n    },\n    function () {\n        Co::readFile(__FILE__);\n        Assert::assert(0); // never here\n    },\n    function () {\n        Co::writeFile(TEST_LOG_FILE, 'foo');\n        Assert::assert(0); // never here\n    },\n    function () {\n        Co::gethostbyname('www.swoole.com');\n        Assert::assert(0); // never here\n    },\n    function () {\n        Co::getaddrinfo('www.swoole.com');\n        Assert::assert(0); // never here\n    },\n//    function () {\n//        Co::statvfs(__DIR__);\n//    },\n    function () {\n        Co::exec('echo');\n        Assert::assert(0); // never here\n    },\n    function () {\n        swoole_async_dns_lookup_coro('127.0.0.1');\n        Assert::assert(0); // never here\n    },\n    function () {\n        (new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP))->connect('127.0.0.1', 1234);\n        Assert::assert(0); // never here\n    },\n    function () {\n        (new Co\\Client(SWOOLE_SOCK_TCP))->connect('127.0.0.1', 1234);\n        Assert::assert(0); // never here\n    },\n    function () {\n        (new Co\\Http\\Client('127.0.0.1', 1234))->get('/');\n        Assert::assert(0); // never here\n    },\n];\n\nif (class_exists(Co\\Http2\\Client::class)) {\n    $map[] = function () {\n        (new Co\\Http2\\Client('127.0.0.1', 1234))->connect();\n        Assert::assert(0); // never here\n    };\n}\n$info_list = [];\nforeach ($map as $i => $f) {\n    $GLOBALS['f'] = $f;\n    if (RUN_IN_CHILD == false) {\n        $f();\n        continue;\n    }\n    $process = new Swoole\\Process(function () {\n        function a()\n        {\n            b();\n        }\n\n        function b()\n        {\n            c();\n        }\n\n        function c()\n        {\n            try {\n                $GLOBALS['f']();\n            } catch (Error $e) {\n                // can not be caught\n            }\n        }\n\n        a();\n    }, true);\n    $process->start();\n    $info = $process->read(8192);\n    $process::wait();\n    if (Assert::contains($info, 'Swoole\\\\Error')) {\n        $_info = trim($info);\n        if (PHP_VERSION_ID >= 80400) {\n            $_info = preg_replace('/(\\#0.+?: )[^\\n]+/', '$1%s', $_info, 1);\n            $_info = preg_replace('/(: )[^\\n]+( in )/', '$1%s$2', $_info, 1);\n            $_info = preg_replace('/closure:[^(:]+:?\\(?\\d+\\)?/', 'closure', $_info);\n            $_info = preg_replace('/\\/[^(:]+:?\\(?\\d+\\)?/', '%s:%d', $_info);\n        } else {\n            $_info = preg_replace('/(\\#0.+?: )[^\\n]+/', '$1%s', $_info, 1);\n            $_info = preg_replace('/(: )[^\\n]+( in )/', '$1%s$2', $_info, 1);\n            $_info = preg_replace('/\\/[^(:]+:?\\(?\\d+\\)?/', '%s:%d', $_info);\n        }\n        $info_list[] = $_info;\n        if (!Assert::assert($info_list[0] === $_info)) {\n            var_dump($map[$i]);\n            var_dump($info_list[0]);\n            var_dump($info);\n            exit;\n        }\n    }\n}\necho current($info_list);\nSwoole\\Event::wait();\n?>\n--EXPECT--\nFatal error: %s in %s:%d\nStack trace:\n#0 %s:%d: %s\n#1 %s:%d: {closure}()\n#2 %s:%d: c()\n#3 %s:%d: b()\n#4 %s:%d: a()\n#5 [internal function]: {closure}(Object(Swoole\\Process))\n#6 %s:%d: Swoole\\Process->start()\n#7 {main}\n  thrown in %s:%d\n"
  },
  {
    "path": "tests/swoole_coroutine/cid.phpt",
    "content": "--TEST--\nswoole_coroutine: cid increment\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nfor ($n = 0; $n < MAX_LOOPS; $n++) {\n    go(function () use ($n) {\n        Assert::same(co::getCid(), $n + 1);\n    });\n}\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine/create_after_rshutdown.phpt",
    "content": "--TEST--\nswoole_coroutine: create coroutine after RSHTUDOWN\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nregister_shutdown_function(function () {\n    go(function () {\n        co::sleep(.01);\n        echo \"DONE\\n\";\n    });\n    Swoole\\Event::wait();\n});\nexit(0);\n\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine/current.phpt",
    "content": "--TEST--\nswoole_coroutine: current cid\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nAssert::same(Co::getuid(), -1);\ngo(function () {\n    Assert::same(Co::getuid(), 1);\n    Co::sleep(1);\n    Assert::same(Co::getuid(), 1);\n});\ngo(function () {\n    Assert::same(Co::getuid(), 2);\n});\nAssert::same(Co::getuid(), -1);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/dead_lock.phpt",
    "content": "--TEST--\nswoole_coroutine: dead lock\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Process;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    for ($n = 3; $n--;) {\n        $ret = Process::wait(false);\n        Assert::isEmpty($ret);\n        switch_process();\n    }\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $pm->wakeup();\n    Coroutine::set([\n        'exit_condition' => function () {\n            return Coroutine::stats()['coroutine_num'] === 0;\n        }\n    ]);\n    Coroutine::create(function () {\n        $channel = new Coroutine\\Channel;\n        $channel->pop();\n    });\n};\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine/defer/defer.phpt",
    "content": "--TEST--\nswoole_coroutine/defer: coro defer\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\ngo(function () {\n    defer(function () {\n        echo \"10\\n\";\n        Assert::same(co::getuid(), 1);\n        co::sleep(.001);\n        Assert::same(co::getuid(), 1);\n        echo \"11\\n\";\n        defer(function () {\n            echo \"14\\n\";\n            Assert::same(co::getuid(), 1);\n            co::sleep(.001);\n            Assert::same(co::getuid(), 1);\n            echo \"15\\n\";\n        });\n        defer(function () {\n            echo \"12\\n\";\n            Assert::same(co::getuid(), 1);\n            co::sleep(.001);\n            Assert::same(co::getuid(), 1);\n            echo \"13\\n\";\n        });\n    });\n    defer(function () {\n        echo \"8\\n\";\n        Assert::same(co::getuid(), 1);\n        co::sleep(.001);\n        Assert::same(co::getuid(), 1);\n        echo \"9\\n\";\n    });\n    echo \"0\\n\";\n    Assert::same(co::getuid(), 1);\n    co::sleep(.001);\n    Assert::same(co::getuid(), 1);\n    echo \"1\\n\";\n    defer(function () {\n        echo \"4\\n\";\n        Assert::same(co::getuid(), 1);\n        co::sleep(.001);\n        Assert::same(co::getuid(), 1);\n        echo \"5\\n\";\n        defer(function () {\n            echo \"6\\n\";\n            Assert::same(co::getuid(), 1);\n            co::sleep(.001);\n            Assert::same(co::getuid(), 1);\n            echo \"7\\n\";\n        });\n    });\n    defer(function () {\n        echo \"2\\n\";\n        Assert::same(co::getuid(), 1);\n        co::sleep(.001);\n        Assert::same(co::getuid(), 1);\n        echo \"3\\n\";\n    });\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n"
  },
  {
    "path": "tests/swoole_coroutine/defer/defer_close.phpt",
    "content": "--TEST--\nswoole_coroutine/defer: coro defer\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    $obj = new class\n    {\n        public $resource;\n\n        public function close()\n        {\n            $this->resource = null;\n        }\n    };\n    defer(function () use ($obj) {\n        $obj->close();\n    });\n    $obj->resource = $file = fopen(__FILE__, 'r+');\n    defer(function () use ($obj) {\n        Assert::assert(is_resource($obj->resource));\n        fclose($obj->resource);\n        echo \"closed\\n\";\n    });\n    throw new Exception('something wrong');\n    echo \"never here\\n\";\n});\nSwoole\\Event::wait();\n?>\n--EXPECTF--\n\nFatal error: Uncaught Exception: something wrong in %s:%d\nStack trace:\n%A\n  thrown in %s on line %d\nclosed\n"
  },
  {
    "path": "tests/swoole_coroutine/defer/defer_exception.phpt",
    "content": "--TEST--\nswoole_coroutine/defer: coro defer with exception\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\ngo(function () {\n    go(function () {\n        $foo = 1;\n        $bar = 'cha';\n        defer(function () use ($foo, $bar) {\n            echo \"defer 1\\n\";\n            Assert::same($foo, 1);\n            Assert::same($bar, 'cha');\n        });\n        $foo = 2;\n        $bar = 'gua';\n        defer(function () use ($foo, &$bar) {\n            echo \"defer 2\\n\";\n            Assert::same($foo, 2);\n            Assert::assert($bar !== 'gua'); // because of &\n        });\n        $foo = 3;\n        $bar = 'zha';\n        echo $foo, \"\\n\", $bar, \"\\n\";\n        throw new Exception('something wrong');\n        echo \"never here\\n\";\n    });\n});\nSwoole\\Event::wait();\n?>\n--EXPECTF--\n3\nzha\n\nFatal error: Uncaught Exception: something wrong in %s:%d\nStack trace:\n%A\n  thrown in %s/tests/swoole_coroutine/defer/defer_exception.php on line %d\ndefer 2\ndefer 1\n"
  },
  {
    "path": "tests/swoole_coroutine/defer/defer_in_try.phpt",
    "content": "--TEST--\nswoole_coroutine/defer: coro defer\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    try {\n        $obj = new class\n        {\n            public $resource;\n\n            public function close()\n            {\n                $this->resource = null;\n            }\n        };\n        defer(function () use ($obj) {\n            $obj->close();\n        });\n        $obj->resource = $file = fopen(__FILE__, 'r+');\n        defer(function () use ($obj) {\n            Assert::assert(is_resource($obj->resource));\n            fclose($obj->resource);\n            echo \"closed\\n\";\n        });\n        throw new Exception('something wrong');\n        echo \"never here\\n\";\n    } catch (Exception $e) {\n        echo \"catch it\\n\";\n    } finally {\n        echo \"finally done\\n\";\n    }\n});\n?>\n--EXPECT--\ncatch it\nfinally done\nclosed\n"
  },
  {
    "path": "tests/swoole_coroutine/destruct/destruct1.phpt",
    "content": "--TEST--\nswoole_coroutine/destruct: destruct1\n--SKIPIF--\n<?php require  __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\nclass T1\n{\n    function __construct()\n    {\n\n    }\n    function test()\n    {\n        echo \"call function\\n\";\n    }\n\n    function __destruct()\n    {\n        go(function () {\n            echo \"coro start\\n\";\n            co::sleep(.001);\n            echo \"coro exit\\n\";\n        });\n    }\n}\n\n$t = new T1();\n$t->test();\nunset($t);\necho \"end\\n\";\n?>\n--EXPECT--\ncall function\ncoro start\nend\ncoro exit\n"
  },
  {
    "path": "tests/swoole_coroutine/destruct/destruct2.phpt",
    "content": "--TEST--\nswoole_coroutine/destruct: destruct2\n--SKIPIF--\n<?php require  __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\nclass T2\n{\n    function __construct()\n    {\n\n    }\n    function test()\n    {\n        echo \"call function\\n\";\n    }\n\n    function __destruct()\n    {\n        go(function () {\n            echo \"coro start\\n\";\n            co::sleep(.001);\n            echo \"coro exit\\n\";\n        });\n    }\n}\n\n$t = new T2();\n$t->test();\necho \"end\\n\";\n?>\n--EXPECTF--\ncall function\nend\n\nFatal error: go(): can not use coroutine in __destruct after php_request_shutdown %s\n--EXPECTF_85--\ncall function\nend\n\nFatal error: go(): can not use coroutine in __destruct after php_request_shutdown %s\nStack trace:\n#0 %s(%d): go(Object(Closure))\n#1 [internal function]: T2->__destruct()\n#2 {main}\n"
  },
  {
    "path": "tests/swoole_coroutine/destruct/destruct3.phpt",
    "content": "--TEST--\nswoole_coroutine/destruct: destruct 3\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; skip_if_php_version_lower_than('8.1'); ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\n\n$t = new class() {\n    function __construct()\n    {\n\n    }\n\n    function test()\n    {\n        echo \"test\\n\";\n    }\n\n    function dtor()\n    {\n        echo \"dtor\\n\";\n        go(function () {\n            throw new Exception('error');\n        });\n    }\n\n    function __destruct()\n    {\n        $this->dtor();\n    }\n};\n\nCo\\go(function () use ($t) {\n    Co::sleep(0.01);\n    $t->test();\n    $GLOBALS['obj'] = $t;\n});\nSwoole\\Event::wait();\n?>\n--EXPECTF--\ntest\ndtor\n\nFatal error: Uncaught Exception: error in %s:%d\nStack trace:\n#0 [internal function]: class@anonymous->{closure%S()\n#1 {main}\n  thrown in %s on line %d\n"
  },
  {
    "path": "tests/swoole_coroutine/dnslookup_1.phpt",
    "content": "--TEST--\nswoole_coroutine: swoole_async_dns_lookup_coro\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\nuse function Swoole\\Coroutine\\run;\n\nrun(function () {\n    $host = System::dnsLookup('www.baidu.com');\n    Assert::assert(filter_var($host, FILTER_VALIDATE_IP) !== false);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/dnslookup_2.phpt",
    "content": "--TEST--\nswoole_coroutine: async dns lookup timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\nuse function Swoole\\Coroutine\\run;\n\nrun(function () {\n    $host = Swoole\\Coroutine::dnsLookup('www.' . uniqid() . '.' . uniqid(), 0.5);\n    Assert::eq($host, false);\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/dnslookup_3.phpt",
    "content": "--TEST--\nswoole_coroutine: async dns lookup [3]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    $host = swoole_async_dns_lookup_coro('www.' . uniqid() . '.' . uniqid(), 15);\n    Assert::eq($host, false);\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/dnslookup_4.phpt",
    "content": "--TEST--\nswoole_coroutine: bad dns server\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\nuse function Swoole\\Coroutine\\run;\n\nrun(function () {\n    Co::set(['dns_server' => '192.0.0.1:10053']);\n    $host = Swoole\\Coroutine::dnsLookup('www.baidu.com', 0.5);\n    Assert::eq($host, false);\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/dnslookup_5.phpt",
    "content": "--TEST--\nswoole_coroutine: async dns lookup [5]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\nuse function Swoole\\Coroutine\\run;\n\nrun(function () {\n    $ip = System::dnsLookup('localhost');\n    Assert::eq($ip, '127.0.0.1');\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/dnslookup_ipv6.phpt",
    "content": "--TEST--\nswoole_coroutine: dns Lookup IPv6\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\nuse function Swoole\\Coroutine\\run;\n\nrun(function () {\n    $host = System::dnsLookup('www.google.com', 2, AF_INET6);\n    Assert::assert(filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/dnslookup_query_hosts.phpt",
    "content": "--TEST--\nswoole_coroutine: dnslookup query hosts\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\nuse function Swoole\\Coroutine\\run;\n\nrun(function () {\n    Assert::eq(System::dnsLookup('localhost', 3, AF_INET), '127.0.0.1');\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/empty.phpt",
    "content": "--TEST--\nswoole_coroutine: coro empty\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    echo \"co[1] start\\n\";\n    echo \"co[1] exit\\n\";\n});\n?>\n--EXPECT--\nco[1] start\nco[1] exit\n"
  },
  {
    "path": "tests/swoole_coroutine/eval.phpt",
    "content": "--TEST--\nswoole_coroutine: eval\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nAssert::same(Co::stats()['coroutine_num'], 0);\n\ngo(function () {\n    echo \"start 1\\n\";\n    eval('Co::sleep(0.5);');\n    echo \"end 1\\n\";\n});\ngo(function () {\n    eval(' echo \"start 2\\n\" ;');\n    Co::sleep(0.5);\n    echo \"end 2\\n\";\n});\necho \"main end\\n\";\n?>\n--EXPECT--\nstart 1\nstart 2\nmain end\nend 1\nend 2\n"
  },
  {
    "path": "tests/swoole_coroutine/exception/core_error.phpt",
    "content": "--TEST--\nswoole_coroutine/exception: fatal error\n--SKIPIF--\n<?php require  __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nregister_shutdown_function(function (){\n    echo \"shutdown\\n\";\n});\nCo\\run(function () {\n    include TESTS_ROOT_PATH.'/include/api/syntax_error.txt';\n    sleep(1);\n    echo \"error\\n\";\n});\necho \"end\\n\";\n?>\n--EXPECTF--\nParse error: syntax error, unexpected identifier \"xde\" in %s on line %d\nshutdown\n"
  },
  {
    "path": "tests/swoole_coroutine/exception/defer1.phpt",
    "content": "--TEST--\nswoole_coroutine/exception: defer 1\n--SKIPIF--\n<?php require  __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Coroutine;\nuse Swoole\\Process;\n\n$process = new Process(function () {\n    register_shutdown_function(function () {\n        echo \"shutdown\\n\";\n    });\n\n    Co\\run(function () {\n        echo \"co-1 begin\\n\";\n\n        Coroutine::create(static function () {\n            echo \"co-2 begin\\n\";\n            Coroutine::defer(static function () {\n                echo \"defer task begin\\n\";\n                throw new Exception();\n                echo \"defer task end\\n\";\n            });\n            throw new Exception();\n            echo \"co-2 end\\n\";\n        });\n\n        echo \"co-1 end\\n\";\n    });\n    echo \"done\\n\";\n}, true, SOCK_STREAM);\n$process->start();\n$status = Process::wait();\nif (Assert::isArray($status)) {\n    list($pid, $code, $signal) = array_values($status);\n    Assert::greaterThan($pid, 0);\n\n    $out = $process->read();\n    Assert::contains($out, 'co-1 begin');\n    Assert::contains($out, 'co-2 begin');\n    Assert::contains($out, 'defer task begin');\n    Assert::contains($out, 'shutdown');\n    Assert::contains($out, 'Fatal error: Uncaught Exception');\n    Assert::notContains($out, 'co-1 end');\n    Assert::same($code, 255);\n}\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_coroutine/exception/defer2.phpt",
    "content": "--TEST--\nswoole_coroutine/exception: defer 2\n--SKIPIF--\n<?php require  __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Coroutine;\n\nregister_shutdown_function(function (){\n    echo \"shutdown\\n\";\n});\n\nCo\\run(function () {\n    echo \"co-1 begin\\n\";\n\n    Coroutine::create(static function () {\n        echo \"co-2 begin\\n\";\n        Coroutine::defer(static function () {\n            echo \"never execute\\n\";\n        });\n        Coroutine::defer(static function () {\n            echo \"defer task begin\\n\";\n            throw new Exception();\n            echo \"defer task end\\n\";\n        });\n        echo \"co-2 end\\n\";\n    });\n\n    echo \"co-1 end\\n\";\n});\necho \"done\\n\";\n?>\n--EXPECTF--\nco-1 begin\nco-2 begin\nco-2 end\ndefer task begin\n\nFatal error: Uncaught Exception in %s:%d\nStack trace:\n#0 [internal function]: {closure%S}(NULL)\n#1 {main}\n  thrown in %s on line %d\nshutdown\n"
  },
  {
    "path": "tests/swoole_coroutine/exception/dtor.phpt",
    "content": "--TEST--\nswoole_coroutine/exception: throw in destructor\n--SKIPIF--\n<?php require  __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Coroutine;\n\nregister_shutdown_function(function (){\n    echo \"shutdown\\n\";\n});\n\nCo\\run(function () {\n    echo \"co-1 begin\\n\";\n    Coroutine::create([new class {\n        public function test() {\n            echo 'test', PHP_EOL;\n        }\n        public function __destruct() {\n            throw new Exception();\n        } \n    }, 'test']);\n\n    echo \"co-1 end\\n\";\n});\necho \"done\\n\";\n?>\n--EXPECTF--\nco-1 begin\ntest\n\nFatal error: Uncaught Exception in %s:%d\nStack trace:\n#0 %s(%d): class@anonymous->__destruct()\n%A\n  thrown in %s on line %d\nshutdown\n"
  },
  {
    "path": "tests/swoole_coroutine/exception/empty.phpt",
    "content": "--TEST--\nswoole_coroutine/exception: IO empty Exception\n--SKIPIF--\n<?php require  __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\ngo(function () {\n    try {\n        echo \"start\\n\";\n        throw new Exception('coro Exception');\n    } catch (Exception $e) {\n        echo 'Caught exception: ',  $e->getMessage(), \"\\n\";\n    } finally {\n        echo \"finally.\\n\";\n    }\n});\necho \"end\\n\";\n\n?>\n--EXPECT--\nstart\nCaught exception: coro Exception\nfinally.\nend\n"
  },
  {
    "path": "tests/swoole_coroutine/exception/error.phpt",
    "content": "--TEST--\nswoole_coroutine/exception: error\n--SKIPIF--\n<?php require  __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nregister_shutdown_function(function (){\n    echo \"shutdown\\n\";\n});\nCo\\run(function () {\n    echo \"start\\n\";\n    throw new Exception('coro Exception');\n    Co::sleep(.001);\n    echo \"after sleep\\n\";\n});\necho \"end\\n\";\n?>\n--EXPECTF--\nstart\n\nFatal error: Uncaught Exception: coro Exception %s\nStack trace:\n%A\n  thrown in %s/tests/swoole_coroutine/exception/error.php on line %d\nshutdown\n"
  },
  {
    "path": "tests/swoole_coroutine/exception/error1.phpt",
    "content": "--TEST--\nswoole_coroutine/exception: double catch\n--SKIPIF--\n<?php require  __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\ntry {\n    go(function () {\n        try {\n            echo \"start\\n\";\n            throw new Exception('coro Exception');\n        } catch (Exception $e) {\n            echo 'Caught exception: ',  $e->getMessage(), \"\\n\";\n        } finally {\n            echo \"finally in.\\n\";\n        }\n    });    \n} catch (Exception $e) {\n    echo 'Caught exception: ',  $e->getMessage(), \"\\n\";\n} finally {\n    echo \"finally out.\\n\";\n}\n\necho \"end\\n\";\n\n?>\n--EXPECT--\nstart\nCaught exception: coro Exception\nfinally in.\nfinally out.\nend\n"
  },
  {
    "path": "tests/swoole_coroutine/exception/error2.phpt",
    "content": "--TEST--\nswoole_coroutine/exception: zend_error\n--SKIPIF--\n<?php require  __DIR__ . '/../../include/skipif.inc';\nskip_if_extension_not_exist('soap');?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\ngo(function () {\n    try {\n        echo \"start\\n\";\n        $wsdl = 'http://127.0.0.1:59999/soap.cls?wsdl';\n        $option = array_merge([\n            'connection_timeout' => 1,\n            'location' => $wsdl,\n            'features' => 1,\n            'exceptions' => true,\n        ], []);\n        $client = new \\SoapClient($wsdl, $option);\n    } catch (\\Exception $e) {\n        echo $e->getMessage();\n    }\n});\necho \"end\\n\";\n\n?>\n--EXPECTF--\nstart\nSOAP-ERROR: Parsing WSDL: Couldn't load from '%s' : failed to load external entity \"%s\"\nend\n"
  },
  {
    "path": "tests/swoole_coroutine/exception/error3.phpt",
    "content": "--TEST--\nswoole_coroutine/exception: internal_function error\n--SKIPIF--\n<?php require  __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nregister_shutdown_function(function () {\n    echo \"shutdown\\n\";\n});\nCo\\run(function (){\n    echo \"start\\n\";\n\n    go('intdiv', 1, 0);\n\n    echo \"end\\n\";\n});\necho \"done\\n\";\n--EXPECTF--\nstart\n\nFatal error: Uncaught DivisionByZeroError:%s:%d\nStack trace:\n%A\n  thrown in %s on line %d\nshutdown\n"
  },
  {
    "path": "tests/swoole_coroutine/exception/fatal_error.phpt",
    "content": "--TEST--\nswoole_coroutine/exception: fatal error\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Process;\n\n$process = new Process(function () {\n    register_shutdown_function(function () {\n        echo \"shutdown\\n\";\n    });\n    Co\\run(function () {\n        call_func_not_exists();\n        sleep(1);\n        echo \"co end\\n\";\n    });\n}, true, SOCK_STREAM);\n$process->start();\n$status = Process::wait();\nif (Assert::isArray($status)) {\n    list($pid, $code, $signal) = array_values($status);\n    Assert::greaterThan($pid, 0);\n\n    $out = $process->read();\n    Assert::contains($out, 'Uncaught Error: Call to undefined function call_func_not_exists()');\n    Assert::contains($out, 'shutdown');\n    Assert::notContains($out, 'co end');\n    Assert::same($code, 255);\n}\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/exception/nested.phpt",
    "content": "--TEST--\nswoole_coroutine/exception: exception before yield\n--SKIPIF--\n<?php require  __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\ngo(function () {\n    try {\n        echo \"start\\n\";\n        go(function(){\n            try {\n                echo \"sub start\\n\";\n                throw new Exception('sub coro Exception');\n                co::sleep(.001);\n                echo \"after go2 sleep\\n\";\n            } catch (Exception $e) {\n                echo 'Caught exception: ',  $e->getMessage(), \"\\n\";\n            } finally {\n                echo \"finally 2\\n\";\n            }\n        });\n        echo \"after go1 sleep\\n\";\n    } catch (Exception $e) {\n        echo 'Caught exception: ',  $e->getMessage(), \"\\n\";\n    } finally {\n        echo \"finally 1\\n\";\n    }\n});\n    echo \"end\\n\";\n\n?>\n--EXPECT--\nstart\nsub start\nCaught exception: sub coro Exception\nfinally 2\nafter go1 sleep\nfinally 1\nend\n"
  },
  {
    "path": "tests/swoole_coroutine/exception/yield.phpt",
    "content": "--TEST--\nswoole_coroutine/exception: exception after yield\n--SKIPIF--\n<?php require  __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\ngo(function () {\n    try {\n        echo \"start\\n\";\n        co::sleep(.001);\n        echo \"after sleep\\n\";\n        throw new Exception('coro Exception');\n    } catch (Exception $e) {\n        echo 'Caught exception: ',  $e->getMessage(), \"\\n\";\n    } finally {\n        echo \"finally.\\n\";\n    }\n});\n    echo \"end\\n\";\n\n?>\n--EXPECT--\nstart\nend\nafter sleep\nCaught exception: coro Exception\nfinally.\n"
  },
  {
    "path": "tests/swoole_coroutine/exception/yield1.phpt",
    "content": "--TEST--\nswoole_coroutine/exception: exception before yield\n--SKIPIF--\n<?php require  __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\ngo(function () {\n    try {\n        echo \"start\\n\";\n        throw new Exception('coro Exception');\n        co::sleep(.001);\n        echo \"after sleep\\n\";\n    } catch (Exception $e) {\n        echo 'Caught exception: ',  $e->getMessage(), \"\\n\";\n    } finally {\n        echo \"finally.\\n\";\n    }\n});\n    echo \"end\\n\";\n\n?>\n--EXPECT--\nstart\nCaught exception: coro Exception\nfinally.\nend\n"
  },
  {
    "path": "tests/swoole_coroutine/exception.phpt",
    "content": "--TEST--\nswoole_coroutine: throw exception\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    go(function () use ($pm) {\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['worker_num' => 1]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        co::sleep(0.001);\n        throw new Exception('whoops');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nFatal error: Uncaught Exception: whoops in %s:%d\nStack trace:\n%A\n  thrown in %s on line %d\n[%s]\tERROR\tphp_swoole_server_rshutdown() (ERRNO %d): Fatal error: Uncaught Exception: whoops in %s:%d\nStack trace:\n%A\n  thrown in %s on line %d\n"
  },
  {
    "path": "tests/swoole_coroutine/execute_time.phpt",
    "content": "--TEST--\nswoole_coroutine: getExecuteTime\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_coroutine_get_execute_time();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\nrun(function(){\n    $i = 1000000;\n    while($i > 0) {\n        $a = 9999 ^ 10000;\n        $i--;\n    }\n    $execution_time = Swoole\\Coroutine::getExecuteTime();\n\n    go(function(){\n        $time = 2;\n    \tSwoole\\Runtime::enableCoroutine($flags = false);\n    \tsleep($time);\n    \t$execution_time = Swoole\\Coroutine::getExecuteTime();\n    \tSwoole\\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL);\n    \tsleep($time);\n    \tAssert::assert(Swoole\\Coroutine::getExecuteTime() - $execution_time < 1000);\n    });\n\n    go(function(){\n        $time = 2;\n    \tSwoole\\Runtime::enableCoroutine($flags = false);\n    \tsleep($time);\n    \t$execution_time = Swoole\\Coroutine::getExecuteTime();\n    \tSwoole\\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL);\n    \tsleep($time);\n\n    \tgo(function(){\n    \t    $time = 2;\n    \t\tSwoole\\Runtime::enableCoroutine($flags = false);\n    \t\tsleep($time);\n    \t\t$execution_time = Swoole\\Coroutine::getExecuteTime();\n    \t\tSwoole\\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL);\n    \t\tsleep($time);\n    \t\tAssert::assert(Swoole\\Coroutine::getExecuteTime() - $execution_time < 1000);\n    \t});\n\n    \tAssert::assert(Swoole\\Coroutine::getExecuteTime() - $execution_time < 1000);\n    });\n\n    Assert::assert(Swoole\\Coroutine::getExecuteTime() - $execution_time < 1000);\n    echo 'DONE';\n});\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine/exists.phpt",
    "content": "--TEST--\nswoole_coroutine: exists\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    go(function () {\n        go(function () {\n            Co::sleep(0.001);\n            var_dump(Co::exists(Co::getPcid())); // 1: true\n        });\n        go(function () {\n            Co::sleep(0.003);\n            var_dump(Co::exists(Co::getPcid())); // 3: false\n        });\n        Co::sleep(0.002);\n        var_dump(Co::exists(Co::getPcid())); // 2: false\n    });\n});\n?>\n--EXPECT--\nbool(true)\nbool(false)\nbool(false)\n"
  },
  {
    "path": "tests/swoole_coroutine/exit.phpt",
    "content": "--TEST--\nswoole_coroutine: exit\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_php_version_ge('8.4');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire TESTS_API_PATH . '/exit.php';\n?>\n--EXPECTF--\nNULL\nNULL\nbool(true)\nbool(false)\nint(1)\nfloat(1.1)\nstring(4) \"exit\"\narray(1) {\n  [\"exit\"]=>\n  string(2) \"ok\"\n}\nobject(stdClass)#%d (%d) {\n  [\"exit\"]=>\n  string(2) \"ok\"\n}\nresource(%d) of type (stream)\nint(0)\n"
  },
  {
    "path": "tests/swoole_coroutine/exit_84.phpt",
    "content": "--TEST--\nswoole_coroutine: exit\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_php_version_lower_than('8.4');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire TESTS_API_PATH . '/exit.php';\n?>\n--EXPECTF--\nint(1)\nstring(4) \"exit\"\nint(0)\n"
  },
  {
    "path": "tests/swoole_coroutine/exit_exception_backtrace.phpt",
    "content": "--TEST--\nswoole_coroutine: exit exception backtrace\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_php_version_ge('8.4');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nfunction foo()\n{\n    bar(get_safe_random());\n}\n\nfunction bar(string $random)\n{\n    char(mt_rand(0, PHP_INT_MAX));\n}\n\nfunction char(int $random)\n{\n    exit;\n}\n\ngo(function () {\n    co::sleep(0.001);\n});\ngo(function () {\n    foo();\n});\n?>\n--EXPECTF--\nFatal error: Uncaught Swoole\\ExitException: swoole exit in %s/tests/swoole_coroutine/exit_exception_backtrace.php:%d\nStack trace:\n#0 %s/tests/swoole_coroutine/exit_exception_backtrace.php(%d): char(%d)\n#1 %s/tests/swoole_coroutine/exit_exception_backtrace.php(%d): bar('%s...')\n#2 %s/tests/swoole_coroutine/exit_exception_backtrace.php(%d): foo()\n%A\n  thrown in %s/tests/swoole_coroutine/exit_exception_backtrace.php on line %d\n"
  },
  {
    "path": "tests/swoole_coroutine/exit_exception_backtrace_84.phpt",
    "content": "--TEST--\nswoole_coroutine: exit exception backtrace\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_php_version_lower_than('8.4');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nfunction foo()\n{\n    bar(get_safe_random());\n}\n\nfunction bar(string $random)\n{\n    char(mt_rand(0, PHP_INT_MAX));\n}\n\nfunction char(int $random)\n{\n    exit;\n}\n\ngo(function () {\n    co::sleep(0.001);\n});\ngo(function () {\n    foo();\n});\n?>\n--EXPECTF--\nFatal error: Uncaught Swoole\\ExitException: swoole exit in %s:%d\nStack trace:\n#0 %s(%d): exit()\n#1 %s(%d): char(%d)\n#2 %s(%d): bar('%s')\n#3 %s(%d): foo()\n#4 [internal function]: {closure:%s:%d}()\n#5 {main}\n  thrown in %s on line %d\n"
  },
  {
    "path": "tests/swoole_coroutine/fatal_error.phpt",
    "content": "--TEST--\nswoole_coroutine: fatal error\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nCo::set([\n    'enable_deadlock_check' => true,\n]);\n\nregister_shutdown_function(function () {\n   echo \"shutdown\\n\";\n});\n\nCo\\run(function () {\n    test_not_found();\n});\necho \"DONE\\n\";\n?>\n--EXPECTF--\nFatal error: Uncaught Error: Call to undefined function test_not_found() in %s:%d\nStack trace:\n%A\n  thrown in %s on line %d\nshutdown\n"
  },
  {
    "path": "tests/swoole_coroutine/forbidden_case/array_map.phpt",
    "content": "--TEST--\nswoole_coroutine/forbidden_case: coro array map\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\nco::create(function() {\n    array_map(\"test\",array(\"func start\\n\"));\n    echo \"co end\\n\";\n});\nfunction test($p) {\n    echo $p;\n    co::sleep(.001);\n    echo \"func end\\n\";\n}\necho \"main end\\n\";\n?>\n--EXPECT--\nfunc start\nmain end\nfunc end\nco end\n"
  },
  {
    "path": "tests/swoole_coroutine/forbidden_case/call_user.phpt",
    "content": "--TEST--\nswoole_coroutine/forbidden_case: coro call user func\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\nco::create(function() {\n    $name = \"call_user_func\";\n    $return = $name(\"test\");\n    echo \"co end\\n\";\n});\n\nfunction test() {\n    echo \"func start\\n\";\n    co::sleep(.001);\n    echo \"func end\\n\";\n}\necho \"main end\\n\";\n?>\n--EXPECT--\nfunc start\nmain end\nfunc end\nco end\n"
  },
  {
    "path": "tests/swoole_coroutine/forbidden_case/invoke.phpt",
    "content": "--TEST--\nswoole_coroutine/forbidden_case: coro invoke\n--SKIPIF--\n<?php require  __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\nco::create(function() {\n    $function = new ReflectionFunction('foo');\n    $function->invoke();\n    echo \"co end\\n\";\n});\nfunction foo() {\n   echo \"func start\\n\";\n   co::sleep(.001);\n   echo \"func end\\n\";\n}\necho \"main end\\n\";\n?>\n--EXPECT--\nfunc start\nmain end\nfunc end\nco end\n"
  },
  {
    "path": "tests/swoole_coroutine/getContext.phpt",
    "content": "--TEST--\nswoole_coroutine: getContext\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nfunction func(callable $fn, ...$args)\n{\n    go(function () use ($fn, $args) {\n        $fn(...$args);\n        echo 'Coroutine#' . Co::getCid() . ' exit' . PHP_EOL;\n    });\n}\n\n/**\n * Compatibility for lower version\n * @param object|Resource $object\n * @return int\n */\nfunction php_object_id($object)\n{\n    static $id = 0;\n    static $map = [];\n    $hash = spl_object_hash($object);\n    return $map[$hash] ?? ($map[$hash] = ++$id);\n}\n\nclass Resource\n{\n    public function __construct()\n    {\n        echo __CLASS__ . '#' . php_object_id((object)$this) . ' constructed' . PHP_EOL;\n    }\n\n    public function __destruct()\n    {\n        echo __CLASS__ . '#' . php_object_id((object)$this) . ' destructed' . PHP_EOL;\n    }\n}\n\n$context = new Co\\Context();\nAssert::assert($context instanceof ArrayObject);\nAssert::assert(Co::getContext() === null);\nfunc(function () {\n    $context = Co::getContext();\n    Assert::assert($context instanceof Co\\Context);\n    $context['resource1'] = new Resource;\n    $context->resource2 = new Resource;\n    func(function () {\n        Co::getContext()['resource3'] = new Resource;\n        Co::yield();\n        Co::getContext()['resource3']->resource4 = new Resource;\n        Co::getContext()->resource5 = new Resource;\n    });\n});\nCo::resume(2);\n?>\n--EXPECT--\nResource#1 constructed\nResource#2 constructed\nResource#3 constructed\nCoroutine#1 exit\nResource#2 destructed\nResource#1 destructed\nResource#4 constructed\nResource#5 constructed\nCoroutine#2 exit\nResource#5 destructed\nResource#3 destructed\nResource#4 destructed\n"
  },
  {
    "path": "tests/swoole_coroutine/getElasped.phpt",
    "content": "--TEST--\nswoole_coroutine: getElapsed\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nCo\\run(function () {\n    var_dump(Co::getElapsed(1000));\n    var_dump(Co::getElapsed(-1));\n    co::sleep(.001);\n    var_dump(Co::getElapsed() === Co::getElapsed(Co::getCid()));\n});\n\n?>\n--EXPECT--\nint(-1)\nint(-1)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_coroutine/getPcid.phpt",
    "content": "--TEST--\nswoole_coroutine: getPcid\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nvar_dump(Co::getPcid());\ngo(function () {\n    var_dump(Co::getPcid());\n    go(function () {\n        var_dump(Co::getPcid());\n        go(function () {\n            var_dump(Co::getPcid());\n            go(function () {\n                var_dump(Co::getPcid());\n            });\n            go(function () {\n                var_dump(Co::getPcid());\n            });\n            go(function () {\n                var_dump(Co::getPcid());\n            });\n        });\n        var_dump(Co::getPcid());\n    });\n    var_dump(Co::getPcid());\n});\nvar_dump(Co::getPcid());\n?>\n--EXPECT--\nbool(false)\nint(-1)\nint(1)\nint(2)\nint(3)\nint(3)\nint(3)\nint(1)\nint(-1)\nbool(false)\n"
  },
  {
    "path": "tests/swoole_coroutine/getPcid_by_random_cid.phpt.phpt",
    "content": "--TEST--\nswoole_coroutine: getPcid by random cid\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nvar_dump(Co::getPcid(-1)); // -1\nvar_dump(Co::getPcid(0)); // -1\ngo(function () {\n    var_dump(Co::getPcid(0)); // -1\n    var_dump(Co::getPcid(1)); // -1\n    var_dump(Co::getPcid(2)); // false\n    go(function () {\n        var_dump(Co::getPcid(0)); // 1\n        var_dump(Co::getPcid(1)); // -1\n        go(function () {\n            var_dump(Co::getPcid(0)); // 2\n            var_dump(Co::getPcid(1)); // -1\n            var_dump(Co::getPcid(2)); // 1\n            var_dump(Co::getPcid(3)); // 2\n            var_dump(Co::getPcid(4)); // false\n        });\n        var_dump(Co::getPcid(2)); // 1\n        var_dump(Co::getPcid(3)); // false\n    });\n});\n\n$cid = Co::getCid();\n$traces = [];\ndo {\n    $traces[] = Co::getBackTrace($cid);\n    $cid = Co::getPcid();\n    var_dump($cid); // false\n} while ($cid !== false);\n?>\n--EXPECT--\nbool(false)\nbool(false)\nint(-1)\nint(-1)\nbool(false)\nint(1)\nint(-1)\nint(2)\nint(-1)\nint(1)\nint(2)\nbool(false)\nint(1)\nbool(false)\nbool(false)\n"
  },
  {
    "path": "tests/swoole_coroutine/gethostbyname.phpt",
    "content": "--TEST--\nswoole_coroutine: gethostbyname and dns cache\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse  Swoole\\Coroutine\\System;\nuse  Swoole\\Coroutine;\n\nuse function Swoole\\Coroutine\\run;\n\nrun(function () {\n    $map = IS_IN_CI ? [\n        'www.google.com' => null,\n        'www.youtube.com' => null,\n        'www.facebook.com' => null,\n    ] : [\n        'www.baidu.com' => null,\n        'www.taobao.com' => null,\n        'www.qq.com' => null,\n    ];\n\n    $first_time = microtime(true);\n    for ($n = MAX_CONCURRENCY_LOW; $n--;) {\n        foreach ($map as $host => &$ip) {\n            $ip = System::gethostbyname($host);\n            Assert::assert(preg_match(IP_REGEX, $ip));\n        }\n    }\n    unset($ip);\n    $first_time = microtime(true) - $first_time;\n    phpt_var_dump($map);\n\n    $cache_time = microtime(true);\n    for ($n = MAX_CONCURRENCY_LOW; $n--;) {\n        foreach ($map as $host => $ip) {\n            $_ip = System::gethostbyname($host);\n            Assert::same($ip, $_ip);\n        }\n    }\n    $cache_time = microtime(true) - $cache_time;\n\n    $no_cache_time = microtime(true);\n    for ($n = MAX_CONCURRENCY_LOW; $n--;) {\n        swoole_clear_dns_cache();\n        $ip = System::gethostbyname(array_rand($map));\n        Assert::assert(preg_match(IP_REGEX, $ip));\n    }\n    $no_cache_time = microtime(true) - $no_cache_time;\n\n    $chan = new Chan(MAX_CONCURRENCY_LOW);\n    $no_cache_multi_time = microtime(true);\n    for ($c = MAX_CONCURRENCY_LOW; $c--;) {\n        go(function () use ($map, $chan) {\n            swoole_clear_dns_cache();\n            $ip = System::gethostbyname(array_rand($map));\n            Assert::assert(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4));\n            $chan->push(Assert::assert(preg_match(IP_REGEX, $ip)));\n        });\n    }\n    for ($c = MAX_CONCURRENCY_LOW; $c--;) {\n        $chan->pop();\n    }\n    $no_cache_multi_time = microtime(true) - $no_cache_multi_time;\n\n    phpt_var_dump($first_time, $cache_time, $no_cache_time, $no_cache_multi_time);\n    if (!IS_IN_CI) {\n        Assert::assert($cache_time < 0.01);\n        Assert::assert($cache_time < $first_time);\n        Assert::assert($cache_time < $no_cache_time);\n        Assert::assert($cache_time < $no_cache_multi_time);\n        Assert::assert($no_cache_multi_time < $no_cache_time);\n    }\n});\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_coroutine/iterator.phpt",
    "content": "--TEST--\nswoole_coroutine: iterator\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $i = Co::list();\n    // 1\n    var_dump($i->current());\n    $i->next();\n    var_dump($i->current());\n    $i->rewind();\n    go(function () use ($i) {\n        Co::sleep(0.1);\n        // 1\n        var_dump($i->current());\n        $i->next();\n        var_dump($i->current());\n        // 3\n        $i3 = Co::list();\n        var_dump($i3->current());\n        $i3->next();\n        var_dump($i3->current());\n    });\n    // 2\n    $i2 = Co::list();\n    var_dump($i2->current());\n    $i2->next();\n    var_dump($i2->current());\n    $i2->rewind();\n});\n?>\n--EXPECT--\nint(1)\nNULL\nint(2)\nint(1)\nint(1)\nNULL\nint(2)\nNULL\n"
  },
  {
    "path": "tests/swoole_coroutine/join/1.phpt",
    "content": "--TEST--\nswoole_coroutine/join: 1\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\n\nrun(function () {\n    $result = [];\n    Assert::true(Coroutine::join([\n        go(function () use (&$result) {\n            $result['baidu'] = file_get_contents(\"https://www.baidu.com/\");\n        }),\n        go(function () use (&$result) {\n            $result['taobao'] = file_get_contents(\"https://www.taobao.com/\");\n        })\n    ]));\n\n    echo \"ALL DONE\\n\";\n    Assert::contains($result['baidu'], 'baidu.com');\n    Assert::contains($result['taobao'], 'taobao.com');\n});\n?>\n--EXPECT--\nALL DONE\n"
  },
  {
    "path": "tests/swoole_coroutine/join/2.phpt",
    "content": "--TEST--\nswoole_coroutine/join: 2\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\nconst N = 10;\n\nrun(function () {\n    $cid_list = [];\n    $result = 0;\n\n    $s = microtime(true);\n    for ($i = 0; $i < N; $i++) {\n        $cid_list[] = go(function () use ($i, &$result) {\n            System::sleep(.3);\n            $result++;\n        });\n    }\n\n    Assert::true(Coroutine::join($cid_list));\n    Assert::assert(approximate(0.3, microtime(true) - $s));\n    Assert::eq($result, N);\n    echo \"ALL DONE\\n\";\n});\n?>\n--EXPECT--\nALL DONE\n"
  },
  {
    "path": "tests/swoole_coroutine/join/3.phpt",
    "content": "--TEST--\nswoole_coroutine/join: 3\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\nini_set('swoole.display_errors', 'off');\n\nrun(function () {\n    $current_cid = Coroutine::getCid();\n    $cid_list = [];\n    Assert::false(Coroutine::join($cid_list));\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_INVALID_PARAMS);\n\n    Assert::false(Coroutine::join([$current_cid]));\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_WRONG_OPERATION);\n\n    Assert::false(Coroutine::join([9999]));\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_INVALID_PARAMS);\n\n    $cid_list = [];\n    $cid_list[] = go(function () {\n        System::sleep(.5);\n    });\n    Assert::false(Coroutine::join($cid_list, 0.01));\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_TIMEDOUT);\n\n    $cid_list = [];\n    $cid_list[] = go(function () {\n        System::sleep(.5);\n    });\n\n    go(function () use($current_cid) {\n        System::sleep(.001);\n        Coroutine::cancel($current_cid);\n    });\n\n    Assert::false(Coroutine::join($cid_list));\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_CANCELED);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/join/4.phpt",
    "content": "--TEST--\nswoole_coroutine/join: 4\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\nini_set('swoole.display_errors', 'off');\n\nrun(function () {\n    $current_cid = Coroutine::getCid();\n    $cid = go(function () use ($current_cid) {\n        System::sleep(.1);\n        Swoole\\Event::defer(function () use ($current_cid) {\n            echo \"DEFER CALLBACK\\n\";\n            Coroutine::cancel($current_cid);\n        });\n    });\n    $cid_list[] = $cid;\n\n    Assert::false(Coroutine::join($cid_list));\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_CANCELED);\n    Assert::false(Coroutine::exists($cid));\n    echo \"DONE\\n\";\n});\n?>\n--EXPECT--\nDEFER CALLBACK\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine/join/5.phpt",
    "content": "--TEST--\nswoole_coroutine/join: 5\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\nini_set('swoole.display_errors', 'off');\n\nrun(function () {\n    $cid_list[] = go(function () {\n        System::sleep(.1);\n    });\n    // concurrency join\n    Swoole\\Event::defer(function () use ($cid_list) {\n        go(function () use ($cid_list) {\n            Assert::false(Coroutine::join($cid_list));\n            Assert::eq(swoole_last_error(), SWOOLE_ERROR_WRONG_OPERATION);\n            echo \"DONE 2\\n\";\n        });\n    });\n    Assert::true(Coroutine::join($cid_list));\n    echo \"DONE 1\\n\";\n});\n?>\n--EXPECT--\nDONE 2\nDONE 1\n"
  },
  {
    "path": "tests/swoole_coroutine/join/6.phpt",
    "content": "--TEST--\nswoole_coroutine/join: 6\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\nini_set('swoole.display_errors', 'off');\n\nrun(function () {\n    $cid = go(function () {\n        System::sleep(.1);\n    });\n    $cid_list[] = $cid;\n    $cid_list[] = 99999; // not exists\n    Assert::true(Coroutine::join($cid_list));\n    Assert::false(Coroutine::exists($cid));\n    echo \"DONE\\n\";\n});\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine/join/7.phpt",
    "content": "--TEST--\nswoole_coroutine/join: 7\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\nini_set('swoole.display_errors', 'off');\n\nrun(function () {\n    $current_cid = Coroutine::getCid();\n    $cid = go(function () use ($current_cid) {\n        System::sleep(.1);\n    });\n    $cid_list[] = $cid;\n\n    Swoole\\Event::defer(function () use ($current_cid) {\n        echo \"DEFER CALLBACK\\n\";\n        Coroutine::cancel($current_cid);\n    });\n\n    Assert::false(Coroutine::join($cid_list));\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_CANCELED);\n    Assert::true(Coroutine::exists($cid));\n    echo \"DONE\\n\";\n});\n?>\n--EXPECT--\nDEFER CALLBACK\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine/join/8.phpt",
    "content": "--TEST--\nswoole_coroutine/join: 8\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\nini_set('swoole.display_errors', 'off');\n\nrun(function () {\n    $cid_list[] = 88888;\n    $cid_list[] = 99999;\n    Assert::false(Coroutine::join($cid_list));\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_INVALID_PARAMS);\n    echo \"DONE\\n\";\n});\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine/kernel_coroutine.phpt",
    "content": "--TEST--\nswoole_coroutine: kernel coroutine\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst N = 4;\nconst T = 0.02;\n\nCo\\run(function () {\n    go(function () {\n        for ($i = 0; $i < N; $i++) {\n            Co::sleep(T);\n        }\n    });\n    swoole_test_kernel_coroutine(N, T);\n});\nAssert::eq(Co::stats()['coroutine_peak_num'], 3);\necho \"Done\\n\";\n\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_coroutine/list_and_backtrace.phpt",
    "content": "--TEST--\nswoole_coroutine: getBackTrace form listCoroutines\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    go(function () {\n        go(function () {\n            $main = go(function () {\n                Co::yield();\n                // ......\n                $list = Co::list();\n                $list->asort();\n                foreach ($list as $cid) {\n                    var_dump($cid);\n                    var_dump(Co::getBackTrace($cid));\n                }\n            });\n            go(function () use ($main) {\n                go(function () {\n                    Co::sleep(0.001);\n                });\n                go(function () {\n                    Co::readFile(__FILE__);\n                });\n                go(function () {\n                    Co::getaddrinfo('localhost');\n                });\n                go(function () use ($main) {\n                    Co::resume($main);\n                });\n            });\n        });\n    });\n});\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nint(1)\narray(%d) {\n  %A\n}\nint(2)\narray(%d) {\n  %A\n}\nint(3)\narray(%d) {\n  %A\n}\nint(4)\narray(%d) {\n  %A\n}\nint(5)\narray(%d) {\n  %A\n}\nint(6)\narray(%d) {\n  %A\n}\nint(7)\narray(%d) {\n  %A\n}\nint(8)\narray(%d) {\n  %A\n}\nint(9)\narray(%d) {\n  %A\n}\n"
  },
  {
    "path": "tests/swoole_coroutine/max_num.phpt",
    "content": "--TEST--\nswoole_coroutine: cid map max num\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nconst MAX_N = 1000;\nco::set([\n    'max_coroutine' => MAX_N\n]);\nfor ($c = MAX_N + 1; $c--;) {\n    $ret = go(function () {\n        co::sleep(0.001);\n    });\n}\n$info = co::stats();\nAssert::same($info['c_stack_size'], 2097152);\nAssert::same($info['coroutine_num'], MAX_N);\nAssert::same($info['coroutine_peak_num'], MAX_N);\n?>\n--EXPECTF--\nWarning: go(): exceed max number of coroutine %d in %s/tests/swoole_coroutine/max_num.php on line %d\n"
  },
  {
    "path": "tests/swoole_coroutine/max_num_limit.phpt",
    "content": "--TEST--\nswoole_coroutine: max num limit\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nAssert::assert(SWOOLE_CORO_MAX_NUM_LIMIT === PHP_INT_MAX);\necho SWOOLE_CORO_MAX_NUM_LIMIT;\n?>\n--EXPECTF--\n%d\n"
  },
  {
    "path": "tests/swoole_coroutine/nested1.phpt",
    "content": "--TEST--\nswoole_coroutine: coro channel\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    echo \"co[1] start\\n\";\n    co::sleep(.01);\n    go(function () {\n        echo \"co[2] start\\n\";\n        co::sleep(.01);\n        echo \"co[2] exit\\n\";\n    });\n    echo \"co[1] exit\\n\";\n});\necho \"end\\n\";\n?>\n--EXPECT--\nco[1] start\nend\nco[2] start\nco[1] exit\nco[2] exit\n"
  },
  {
    "path": "tests/swoole_coroutine/nested2.phpt",
    "content": "--TEST--\nswoole_coroutine: coro nested2\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    echo \"co[1] start\\n\";\n    go(function () {\n        echo \"co[2] start\\n\";\n        co::sleep(.02);\n        echo \"co[2] exit\\n\";\n    });\n    co::sleep(.01);\n    echo \"co[1] exit\\n\";\n});\necho \"end\\n\";\n?>\n--EXPECT--\nco[1] start\nco[2] start\nend\nco[1] exit\nco[2] exit\n"
  },
  {
    "path": "tests/swoole_coroutine/nested3.phpt",
    "content": "--TEST--\nswoole_coroutine: coro nested3\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    echo \"co[1] start\\n\";\n    go(function () {\n        echo \"co[2] start\\n\";\n        co::sleep(.01);\n        echo \"co[2] exit\\n\";\n    });\n    co::sleep(.02);\n    echo \"co[1] exit\\n\";\n});\necho \"end\\n\";\n?>\n--EXPECT--\nco[1] start\nco[2] start\nend\nco[2] exit\nco[1] exit\n"
  },
  {
    "path": "tests/swoole_coroutine/nested_empty.phpt",
    "content": "--TEST--\nswoole_coroutine: coro nested empty\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    echo \"co[1] start\\n\";\n    go(function () {\n        echo \"co[2] start\\n\";\n        echo \"co[2] exit\\n\";\n    });\n    echo \"co[1] exit\\n\";\n});\n?>\n--EXPECT--\nco[1] start\nco[2] start\nco[2] exit\nco[1] exit\n"
  },
  {
    "path": "tests/swoole_coroutine/nested_uid.phpt",
    "content": "--TEST--\nswoole_coroutine: coro nested strict\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nAssert::same(Co::getuid(), -1);\ngo(function () {\n    Assert::same(Co::getuid(), 1);\n    Co::sleep(0.01);\n    Assert::same(Co::getuid(), 1);\n});\nAssert::same(Co::getuid(), -1);\ngo(function () {\n    Assert::same(Co::getuid(), 2);\n\n    go(function () {\n        Assert::same(Co::getuid(), 3);\n        go(function () {\n            Assert::same(Co::getuid(), 4);\n            go(function () {\n                Assert::same(Co::getuid(), 5);\n                Co::sleep(0.01);\n                Assert::same(Co::getuid(), 5);\n            });\n            Assert::same(Co::getuid(), 4);\n        });\n        Assert::same(Co::getuid(), 3);\n    });\n    Assert::same(Co::getuid(), 2);\n});\nAssert::same(Co::getuid(), -1);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/new_process.phpt",
    "content": "--TEST--\nswoole_coroutine: new process\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $process = new Swoole\\Process(function () { });\n    $process->start();\n});\n?>\n--EXPECTF--\nFatal error: Uncaught Swoole\\Error: must be forked outside the coroutine in %s:%d\nStack trace:\n#0 %s(5): Swoole\\Process->start()\n%A\n  thrown in %s on line %d\n"
  },
  {
    "path": "tests/swoole_coroutine/new_server.phpt",
    "content": "--TEST--\nswoole_coroutine: new server\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $server = new Swoole\\Server('127.0.0.1', 0, SWOOLE_PROCESS);\n    $server->on('receive', function () {\n    });\n    $server->start();\n});\n?>\n--EXPECTF--\nWarning: Swoole\\Server::start(): The event-loop has already been created, unable to start %s in %s on line %d\n"
  },
  {
    "path": "tests/swoole_coroutine/no_inline_func.phpt",
    "content": "--TEST--\nswoole_coroutine: coro not inline function\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\n\necho \"start\\n\";\nco::create(function () {\n    $ret = test();\n    echo $ret;\n});\nfunction test()\n{\n    echo \"start func\\n\";\n    co::sleep(0.001);\n    echo \"end func\\n\";\n    return \"return func params\\n\";\n}\necho \"end\\n\";\n?>\n--EXPECT--\nstart\nstart func\nend\nend func\nreturn func params\n"
  },
  {
    "path": "tests/swoole_coroutine/output/concurrency.phpt",
    "content": "--TEST--\nswoole_coroutine/output: concurrency\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nob_start();\necho \"cid 0\\n\";\n\nCo\\run(function () {\n    ob_start();\n    echo \"cid 1\\n\";\n\n    $list = [];\n    $n = MAX_REQUESTS;\n    while ($n--) {\n        $list[] = Co\\go(function () {\n            ob_start();\n            $cid = co::getCid();\n            echo \"cid {$cid} [1]\\n\";\n            usleep(random_int(1000, 5000));\n            echo \"cid {$cid} [2]\\n\";\n            usleep(random_int(1000, 5000));\n            echo \"cid {$cid} [3]\\n\";\n            usleep(random_int(1000, 5000));\n            Assert::eq(\n                ob_get_clean(),\n                implode('', [\n                    \"cid {$cid} [1]\\n\",\n                    \"cid {$cid} [2]\\n\",\n                    \"cid {$cid} [3]\\n\",\n                ])\n            );\n        });\n    }\n    co::join($list);\n\n    Assert::eq(ob_get_clean(), \"cid 1\\n\");\n});\n\nAssert::eq(ob_get_clean(), \"cid 0\\n\");\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/output/create.phpt",
    "content": "--TEST--\nswoole_coroutine/output: main output global\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nob_start();\necho \"0\\n\";\ngo(function () {\n    ob_start();\n    echo \"1\\n\";\n    go(function () {\n        ob_start();\n        echo \"2\\n\";\n    }); // close 2\n});// close 1\n// close 0\n?>\n--EXPECT--\n2\n1\n0\n"
  },
  {
    "path": "tests/swoole_coroutine/output/in_nested_co.phpt",
    "content": "--TEST--\nswoole_coroutine/output: use ob_* in nest co\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nCo\\run(function () {\n    ob_start();\n    echo \"2\\n\"; // [#1] yield\n    go(function () {\n        echo \"1\\n\"; // [#2] output: 1\n        fgets(fopen(__FILE__, 'r')); // yield\n        // [#4] resume\n        ob_start(); // to buffer\n        echo \"4\\n\";\n    }); // [#5] destroyed and output: 4\n    echo \"3\\n\";\n}); // [#3] destroyed and output: 2 3\n?>\n--EXPECT--\n1\n2\n3\n4\n"
  },
  {
    "path": "tests/swoole_coroutine/output/ob_main.phpt",
    "content": "--TEST--\nswoole_coroutine/output: main output global\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nob_start();\necho 'aaa';\nCo\\run(function () {\n    ob_start();\n    echo 'bbb';\n    fgets(fopen(__FILE__, 'r'));\n    Assert::same(ob_get_clean(), 'bbb');\n});\nAssert::same(ob_get_clean(), 'aaa');\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/output/output_control.phpt",
    "content": "--TEST--\nswoole_coroutine/output: ob_* in coroutine\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nob_start();\necho 'main';\n// #co1\ngo(function () {\n    ob_start();\n    echo \"foo\\n\";\n    $ob_1 = (ob_get_status(true));\n    // yield and it will switch to #co2\n    co::sleep(0.1);\n    // resume to ob_1\n    Assert::same($ob_1, (ob_get_status(true)));\n    ob_start(); // ob_2\n    echo \"bar\\n\";\n    Assert::same(ob_get_status()['level'], 1);\n    ob_start(); // ob_3\n    // yield and it will switch to #co3\n    co::sleep(0.2);\n    // resume to ob_3\n    Assert::same(ob_get_status()['level'], 2);\n    echo \"baz\\n\";\n    Assert::same(ob_get_clean(), \"baz\\n\"); // clean ob_3\n    echo ob_get_clean(); // ob_1, ob_2, expect foo\\n bar\\n;\n});\n\n// #co2\ngo(function () {\n    Assert::same(ob_get_status(true), []); //empty\n    Assert::assert(!ob_get_contents());\n    co::sleep(0.001);\n    Assert::assert(!ob_get_clean());\n});\n\n// #co3\ngo(function () {\n    co::sleep(0.2);\n    Assert::same(ob_get_status(true), []); //empty\n});\nAssert::same(ob_get_clean(), 'main');\n?>\n--EXPECT--\nfoo\nbar\n"
  },
  {
    "path": "tests/swoole_coroutine/parallel1.phpt",
    "content": "--TEST--\nswoole_coroutine: coro parallel1\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    echo \"co[1] start\\n\";\n    co::sleep(.01);\n    echo \"co[1] exit\\n\";\n});\n\ngo(function () {\n    echo \"co[2] start\\n\";\n    co::sleep(.02);\n    echo \"co[2] exit\\n\";\n});\necho \"end\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECT--\nco[1] start\nco[2] start\nend\nco[1] exit\nco[2] exit\n"
  },
  {
    "path": "tests/swoole_coroutine/parallel2.phpt",
    "content": "--TEST--\nswoole_coroutine: coro parallel2\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    echo \"co[1] start\\n\";\n    co::sleep(.02);\n    echo \"co[1] exit\\n\";\n});\n\ngo(function () {\n    echo \"co[2] start\\n\";\n    co::sleep(.01);\n    echo \"co[2] exit\\n\";\n});\necho \"end\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECT--\nco[1] start\nco[2] start\nend\nco[2] exit\nco[1] exit\n"
  },
  {
    "path": "tests/swoole_coroutine/parallel3.phpt",
    "content": "--TEST--\nswoole_coroutine: coro parallel3\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    echo \"co[1] start\\n\";\n    co::sleep(.02);\n    echo \"co[1] exit\\n\";\n});\n\ngo(function () {\n    echo \"co[2] start\\n\";\n    go(function () {\n        echo \"co[3] start\\n\";\n        co::sleep(.03);\n        echo \"co[3] exit\\n\";\n    });\n    co::sleep(.01);\n    echo \"co[2] exit\\n\";\n});\necho \"end\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECT--\nco[1] start\nco[2] start\nco[3] start\nend\nco[2] exit\nco[1] exit\nco[3] exit\n"
  },
  {
    "path": "tests/swoole_coroutine/pdo_error_handing.phpt",
    "content": "--TEST--\nswoole_coroutine: error handing bug by pdo\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_pdo_not_support_mysql8();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    new PDO(\n        \"mysql:host=\" . MYSQL_SERVER_HOST . \";port=\" . MYSQL_SERVER_PORT . \";dbname=\" . MYSQL_SERVER_DB . \";charset=utf8\",\n        MYSQL_SERVER_USER, MYSQL_SERVER_PWD\n    );\n});\ngo(function () {\n    fopen(__DIR__ . '/file_not_exist', 'r');\n});\nSwoole\\Event::wait();\n\n?>\n--EXPECTF--\nWarning: fopen(%s): %s to open stream: No such file or directory in %s on line %d\n"
  },
  {
    "path": "tests/swoole_coroutine/private_access.phpt",
    "content": "--TEST--\nswoole_coroutine: $this private access in PHP70 (EG(scope))\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_no_database();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n(new Bar)->foo();\n\nclass Bar\n{\n    static private $s_private = 's_private';\n    static protected $s_protect = 's_protect';\n    static public $s_public = 's_public';\n\n    private $private = 'private';\n    protected $protect = 'protect';\n    public $public = 'public';\n\n    public function foo()\n    {\n        \\Swoole\\Runtime::setHookFlags(SWOOLE_HOOK_ALL);\n\n        go(function () {\n            var_dump(self::$s_private);\n            var_dump(self::$s_protect);\n            var_dump(self::$s_public);\n            var_dump($this->private);\n            var_dump($this->protect);\n            var_dump($this->public);\n            co::sleep(.001);\n            var_dump(self::$s_private);\n            var_dump(self::$s_protect);\n            var_dump(self::$s_public);\n            var_dump($this->private);\n            var_dump($this->protect);\n            var_dump($this->public);\n        });\n        var_dump(self::$s_private);\n        var_dump(self::$s_protect);\n        var_dump(self::$s_public);\n        var_dump($this->private);\n        var_dump($this->protect);\n        var_dump($this->public);\n        go(function () {\n            var_dump(self::$s_private);\n            var_dump(self::$s_protect);\n            var_dump(self::$s_public);\n            var_dump($this->private);\n            var_dump($this->protect);\n            var_dump($this->public);\n            $mysql = new mysqli();\n            $res = $mysql->connect(\n                MYSQL_SERVER_HOST,\n                MYSQL_SERVER_USER,\n                MYSQL_SERVER_PWD,\n                MYSQL_SERVER_DB,\n                MYSQL_SERVER_PORT,\n            );\n            Assert::assert($res);\n            $ret = $mysql->query('show tables', 1)->fetch_all();\n            Assert::assert(is_array($ret));\n            Assert::assert(count($ret) > 0);\n            var_dump(self::$s_private);\n            var_dump(self::$s_protect);\n            var_dump(self::$s_public);\n            var_dump($this->private);\n            var_dump($this->protect);\n            var_dump($this->public);\n        });\n        $cid = go(function () {\n            var_dump(self::$s_private);\n            var_dump(self::$s_protect);\n            var_dump(self::$s_public);\n            var_dump($this->private);\n            var_dump($this->protect);\n            var_dump($this->public);\n            Co::yield();\n            var_dump(self::$s_private);\n            var_dump(self::$s_protect);\n            var_dump(self::$s_public);\n            var_dump($this->private);\n            var_dump($this->protect);\n            var_dump($this->public);\n        });\n        go(function () use ($cid) {\n            var_dump(self::$s_private);\n            var_dump(self::$s_protect);\n            var_dump(self::$s_public);\n            var_dump($this->private);\n            var_dump($this->protect);\n            var_dump($this->public);\n            Co::resume($cid);\n            var_dump(self::$s_private);\n            var_dump(self::$s_protect);\n            var_dump(self::$s_public);\n            var_dump($this->private);\n            var_dump($this->protect);\n            var_dump($this->public);\n        });\n        go(function () {\n            var_dump(self::$s_private);\n            var_dump(self::$s_protect);\n            var_dump(self::$s_public);\n            var_dump($this->private);\n            var_dump($this->protect);\n            var_dump($this->public);\n            Co::sleep(0.001);\n            var_dump(self::$s_private);\n            var_dump(self::$s_protect);\n            var_dump(self::$s_public);\n            var_dump($this->private);\n            var_dump($this->protect);\n            var_dump($this->public);\n        });\n        var_dump(self::$s_private);\n        var_dump(self::$s_protect);\n        var_dump(self::$s_public);\n        var_dump($this->private);\n        var_dump($this->protect);\n        var_dump($this->public);\n    }\n}\n\n?>\n--EXPECT--\nstring(9) \"s_private\"\nstring(9) \"s_protect\"\nstring(8) \"s_public\"\nstring(7) \"private\"\nstring(7) \"protect\"\nstring(6) \"public\"\nstring(9) \"s_private\"\nstring(9) \"s_protect\"\nstring(8) \"s_public\"\nstring(7) \"private\"\nstring(7) \"protect\"\nstring(6) \"public\"\nstring(9) \"s_private\"\nstring(9) \"s_protect\"\nstring(8) \"s_public\"\nstring(7) \"private\"\nstring(7) \"protect\"\nstring(6) \"public\"\nstring(9) \"s_private\"\nstring(9) \"s_protect\"\nstring(8) \"s_public\"\nstring(7) \"private\"\nstring(7) \"protect\"\nstring(6) \"public\"\nstring(9) \"s_private\"\nstring(9) \"s_protect\"\nstring(8) \"s_public\"\nstring(7) \"private\"\nstring(7) \"protect\"\nstring(6) \"public\"\nstring(9) \"s_private\"\nstring(9) \"s_protect\"\nstring(8) \"s_public\"\nstring(7) \"private\"\nstring(7) \"protect\"\nstring(6) \"public\"\nstring(9) \"s_private\"\nstring(9) \"s_protect\"\nstring(8) \"s_public\"\nstring(7) \"private\"\nstring(7) \"protect\"\nstring(6) \"public\"\nstring(9) \"s_private\"\nstring(9) \"s_protect\"\nstring(8) \"s_public\"\nstring(7) \"private\"\nstring(7) \"protect\"\nstring(6) \"public\"\nstring(9) \"s_private\"\nstring(9) \"s_protect\"\nstring(8) \"s_public\"\nstring(7) \"private\"\nstring(7) \"protect\"\nstring(6) \"public\"\nstring(9) \"s_private\"\nstring(9) \"s_protect\"\nstring(8) \"s_public\"\nstring(7) \"private\"\nstring(7) \"protect\"\nstring(6) \"public\"\nstring(9) \"s_private\"\nstring(9) \"s_protect\"\nstring(8) \"s_public\"\nstring(7) \"private\"\nstring(7) \"protect\"\nstring(6) \"public\"\nstring(9) \"s_private\"\nstring(9) \"s_protect\"\nstring(8) \"s_public\"\nstring(7) \"private\"\nstring(7) \"protect\"\nstring(6) \"public\"\n"
  },
  {
    "path": "tests/swoole_coroutine/resume_loop.phpt",
    "content": "--TEST--\nswoole_coroutine: resume loop\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$cos = [];\nfor ($n = 500; $n--;) {\n    $cos[] = go(function () {\n        global $cos;\n        Co::yield();\n        if (count($cos) > 0) {\n            Co::resume(array_shift($cos));\n        }\n    });\n}\nCo::resume(array_shift($cos));\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine/scheduler.phpt",
    "content": "--TEST--\nswoole_coroutine: coroutine scheduler\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$co1 = go(function () {\n    co::yield();\n    echo \"co1\\n\";\n    go(function () {\n        co::yield();\n        echo \"co4\\n\";\n    });\n});\n\ngo(function () use ($co1) {\n    go(function () {\n        co::sleep(.001);\n        echo \"co3\\n\";\n        co::resume(4);\n    });\n    co::resume($co1);\n    echo \"co2\\n\";\n});\n\n?>\n--EXPECT--\nco1\nco2\nco3\nco4\n"
  },
  {
    "path": "tests/swoole_coroutine/signal_listener.phpt",
    "content": "--TEST--\nswoole_coroutine: signal listener\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Process;\n\nswoole_async_set(['enable_coroutine' => false]);\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    for ($n = 3; $n--;) {\n        $ret = Process::wait(false);\n        Assert::isEmpty($ret);\n        switch_process();\n    }\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $pm->wakeup();\n    Coroutine::set([\n        'exit_condition' => function () {\n            return Coroutine::stats()['signal_listener_num'] === 0;\n        }\n    ]);\n    Process::signal(SIGTERM, function () {\n        echo 'SIGTERM' . PHP_EOL;\n        Process::signal(SIGTERM, null);\n        exit(123);\n    });\n};\n$pm->childFirst();\n$pm->run();\n$pm->expectExitCode(123);\n\n?>\n--EXPECT--\nSIGTERM\n"
  },
  {
    "path": "tests/swoole_coroutine/stats.phpt",
    "content": "--TEST--\nswoole_coroutine: current stats\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nAssert::same(Co::stats()['coroutine_num'], 0);\nAssert::same(Co::stats()['coroutine_peak_num'], 0);\ngo(function () {\n    Assert::same(Co::stats()['coroutine_num'], 1);\n    Assert::same(Co::stats()['coroutine_peak_num'], 1);\n    Co::sleep(0.5);\n    Assert::same(Co::stats()['coroutine_num'], 2);\n    Assert::same(Co::stats()['coroutine_peak_num'], 2);\n});\ngo(function () {\n    Assert::same(Co::stats()['coroutine_num'], 2);\n    Assert::same(Co::stats()['coroutine_peak_num'], 2);\n    Co::sleep(0.5);\n    Assert::same(Co::stats()['coroutine_num'], 1);\n    Assert::same(Co::stats()['coroutine_peak_num'], 2);\n});\nAssert::same(Co::stats()['coroutine_num'], 2);\nAssert::same(Co::stats()['coroutine_peak_num'], 2);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/timeout.phpt",
    "content": "--TEST--\nswoole_coroutine: set timeout\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Runtime;\nuse Swoole\\Coroutine\\WaitGroup;\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine\\TimeoutException;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_ALL);\nrun(function() {\n\t$waitGroup = new WaitGroup();\n\tgo(function() use ($waitGroup) {\n\t\t$waitGroup->add();\n    \tAssert::true(Coroutine::setTimeLimit(2.5));\n    \tsleep(1);\n    \t$waitGroup->done();\n    });\n\n    go(function() use ($waitGroup) {\n    \ttry {\n    \t    $waitGroup->add();\n    \t\tAssert::true(Coroutine::setTimeLimit(1.5));\n            sleep(2);\n    \t} catch (TimeoutException $e) {\n    \t\techo \"timeout\";\n    \t} finally {\n    \t    $waitGroup->done();\n    \t}\n    });\n    $waitGroup->wait();\n});\n?>\n--EXPECT--\ntimeout\n"
  },
  {
    "path": "tests/swoole_coroutine/use_process.phpt",
    "content": "--TEST--\nswoole_coroutine: user process\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Client;\nuse Swoole\\Process;\nuse Swoole\\Server;\nuse SwooleTest\\ProcessManager;\n\n$pm = new ProcessManager();\n\nconst SIZE = 8192 * 5;\nconst TIMES = 10;\n\n$pm->parentFunc = function () use ($pm) {\n    $client = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $client->set([\n        'open_eof_check' => true,\n        'package_eof' => \"\\r\\n\\r\\n\",\n    ]);\n    $r = $client->connect('127.0.0.1', $pm->getFreePort(), -1);\n    if ($r === false) {\n        echo 'ERROR';\n        exit;\n    }\n    $client->send('SUCCESS');\n    for ($i = 0; $i < TIMES; $i++) {\n        $ret = $client->recv();\n        Assert::same(strlen($ret), SIZE + 4);\n    }\n    $client->close();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n\n    $proc = new Process(function ($process) use ($serv) {\n        $data = json_decode($process->read(), true);\n        for ($i = 0; $i < TIMES / 2; $i++) {\n            go(function () use ($serv, $data, $i) {\n                // echo \"user sleep start\\n\";\n                co::sleep(0.01);\n                // echo \"user sleep end\\n\";\n                $serv->send($data['fd'], str_repeat('A', SIZE) . \"\\r\\n\\r\\n\");\n                // echo \"user process $i send ok\\n\";\n            });\n        }\n    }, false, true);\n\n    $serv->addProcess($proc);\n    $serv->on('WorkerStart', function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('Receive', function (Server $serv, $fd, $reactorId, $data) use ($proc) {\n        $proc->write(json_encode([\n            'fd' => $fd,\n        ]));\n        for ($i = 0; $i < TIMES / 2; $i++) {\n            go(function () use ($serv, $fd, $i) {\n                // echo \"worker sleep start\\n\";\n                co::sleep(0.01);\n                // echo \"worker sleep end\\n\";\n                $serv->send($fd, str_repeat('A', SIZE) . \"\\r\\n\\r\\n\");\n                // echo \"worker send $i ok\\n\";\n            });\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine/user_coroutine.phpt",
    "content": "--TEST--\nswoole_coroutine: user coroutine\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_ci('foreign network dns error');\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine\\Http\\Client as HttpClient;\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm)\n{\n    go(function () use ($pm) {\n        $data= httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        Assert::assert(strlen($data) > 1024);\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm)\n{\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(array(\n        'log_file' => '/dev/null'\n    ));\n    $http->on(\"WorkerStart\", function (Swoole\\Server $serv)\n    {\n        /**\n         * @var $pm ProcessManager\n         */\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response)\n    {\n        Swoole\\Coroutine::create(function () use ($response)\n        {\n            $url = 'http://news.bitauto.com/xinche/';\n            $components = parse_url($url);\n\n            if (!isset($components['host']))\n            {\n                throw new \\Exception(\"{$url} parse no host\");\n            }\n\n            $host = $components['host'];\n\n            $ip = swoole_async_dns_lookup_coro($host);\n            $port = isset($components['port']) ? $components['port'] : 80;\n            $client = new HttpClient($ip, $port);\n\n            $client->setHeaders([\n                'Host' => $host,\n                'User-Agent' => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:28.0) Gecko/20100101 Firefox/28.0',\n            ]);\n            $client->set(['timeout' => 10]);\n            $client->get(isset($components['path']) ? $components['path'] : '/');\n            $response->end($client->body);\n        });\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine/user_coroutine_2.phpt",
    "content": "--TEST--\nswoole_coroutine: user coroutine\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nSwoole\\Coroutine::create(function ()\n{\n    echo \"OK\\n\";\n});\n\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_coroutine_lock/lock.phpt",
    "content": "--TEST--\nswoole_coroutine_lock: lock\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Lock;\nuse Swoole\\Runtime;\nuse Swoole\\Http\\Server;\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine\\WaitGroup;\n\nif (defined('SWOOLE_IOURING_SQPOLL')) {\n\tswoole_async_set([\n\t    'iouring_workers' => 32,\n\t    'iouring_entries' => 20000,\n\t    'iouring_flag' => SWOOLE_IOURING_SQPOLL\n\t]);\n}\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n\tRuntime::enableCoroutine(SWOOLE_HOOK_ALL);\n    run(function () use ($pm) {\n        $waitGroup = new WaitGroup();\n        go(function () use ($pm, $waitGroup) {\n            $waitGroup->add();\n            $resp = httpPost(\"http://127.0.0.1:{$pm->getFreePort()}?value=1\", []);\n            $respData = json_decode($resp, true);\n            var_dump($respData);\n            $waitGroup->done();\n        });\n        go(function () use ($pm, $waitGroup) {\n            $waitGroup->add();\n            $resp = httpPost(\"http://127.0.0.1:{$pm->getFreePort()}?value=2\", []);\n            $respData = json_decode($resp, true);\n            var_dump($respData);\n            $waitGroup->done();\n        });\n\t\tgo(function () use ($pm, $waitGroup) {\n            $waitGroup->add();\n            $resp = httpPost(\"http://127.0.0.1:{$pm->getFreePort()}?value=3\", []);\n            $respData = json_decode($resp, true);\n            var_dump($respData);\n            $waitGroup->done();\n        });\n\n\t\t$waitGroup->wait();\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    swoole_async_set([\n        'log_file' => '/dev/null',\n    ]);\n\t$lock = new Lock(true);\n\tAssert::false($lock->lock());\n    Assert::false($lock->unlock());\n    Assert::eq($lock->errCode, SWOOLE_ERROR_CO_OUT_OF_COROUTINE);\n    $serv = new Server('127.0.0.1', $pm->getFreePort());\n    $serv->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 4,\n        'enable_coroutine' => true,\n        'hook_flags' => SWOOLE_HOOK_ALL\n    ]);\n\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('request', function ($req, $resp) use ($lock) {\n        $resp->header('Content-Type', 'text/plain');\n        if ($req->get['value'] == 1 || $req->get['value'] == 2) {\n            $lock->lock();\n            if ($req->get['value'] == 1) {\n                sleep(1);\n            }\n            $resp->end(json_encode(['result' => 'lock' . $req->get['value']]) . PHP_EOL);\n            $lock->unlock();\n        } else {\n            $resp->end(json_encode(['result' => 'value 3']) . PHP_EOL);\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\narray(1) {\n  [\"result\"]=>\n  string(7) \"value 3\"\n}\narray(1) {\n  [\"result\"]=>\n  string(5) \"lock1\"\n}\narray(1) {\n  [\"result\"]=>\n  string(5) \"lock2\"\n}\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine_lock/trylock.phpt",
    "content": "--TEST--\nswoole_coroutine_lock: trylock\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Lock;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\nif (defined('SWOOLE_IOURING_SQPOLL')) {\n    swoole_async_set([\n        'iouring_workers' => 32,\n        'iouring_entries' => 20000,\n        'iouring_flag' => SWOOLE_IOURING_SQPOLL\n    ]);\n}\n\nRuntime::enableCoroutine(SWOOLE_HOOK_ALL);\nrun(function () {\n    $lock = new Lock(false);\n    Assert::eq($lock->lock(LOCK_NB), true);\n    go(function () use ($lock) {\n        Assert::eq($lock->lock(LOCK_NB), false);\n        $s = microtime(true);\n        Assert::eq($lock->lock(), true);\n        Assert::assert(microtime(true) - $s >= 0.05);\n        echo \"co2 end\\n\";\n    });\n\n    System::sleep(0.05);\n    Assert::eq($lock->unlock(), true);\n    echo \"co1 end\\n\";\n});\necho \"DONE\\n\";\n?>\n--EXPECT--\nco1 end\nco2 end\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine_lock/trylock2.phpt",
    "content": "--TEST--\nswoole_coroutine_lock: coroutine try lock\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nuse Swoole\\Coroutine\\Lock;\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine\\WaitGroup;\n\nif (defined('SWOOLE_IOURING_SQPOLL')) {\n    swoole_async_set([\n        'iouring_workers' => 32,\n        'iouring_entries' => 20000,\n        'iouring_flag' => SWOOLE_IOURING_SQPOLL\n    ]);\n}\n\n$lock = new Lock(false);\n\nrun(function () use ($argv, $lock) {\n    $waitGroup = new WaitGroup();\n    go(function () use ($waitGroup, $lock) {\n        $waitGroup->add();\n        $lock->lock();\n        usleep(100000);\n        var_dump(1);\n        $lock->unlock();\n        $waitGroup->done();\n    });\n\n    go(function () use ($waitGroup, $lock) {\n        $waitGroup->add();\n        if (!$lock->lock(LOCK_NB)) {\n            var_dump('lock failed');\n        }\n        $waitGroup->done();\n    });\n\n    $waitGroup->wait();\n});\n?>\n--EXPECTF--\nstring(11) \"lock failed\"\nint(1)\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/getOptions.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler: getOptions\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$sch = new Co\\Scheduler;\n$sch->set(['max_coroutine' => 100, 'deadlock_check_max_stack' => 64, ]);\n$options = $sch->getOptions();\nASsert::isArray($options);\nAssert::eq($options['deadlock_check_max_stack'], 64);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/hook_flags.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler: hook_flags\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$sch = new Swoole\\Coroutine\\Scheduler();\n\n$sch->set(['hook_flags' => SWOOLE_HOOK_ALL,]);\n\n$sch->add(function ($t, $n) {\n    usleep($t);\n    echo \"$n\\n\";\n}, 200000, 'A');\n\n$sch->add(function ($t, $n) {\n    usleep($t);\n    echo \"$n\\n\";\n}, 100000, 'B');\n\n$sch->add(function () {\n    var_dump(Co::getCid());\n});\n\n$sch->start();\n\n?>\n--EXPECTF--\nint(%d)\nB\nA\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/hook_flags_2.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler: hook_flags\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst URL = \"http://www.gov.cn/\";\nconst KEYWORDS = '中国政府网';\n// 1\n$sch = new Swoole\\Coroutine\\Scheduler();\n$sch->set(['hook_flags' => SWOOLE_HOOK_ALL,]);\n$sch->add(function () {\n    Assert::contains(file_get_contents(URL), KEYWORDS);\n});\n$sch->start();\n\nAssert::contains(file_get_contents(URL), KEYWORDS);\n\n// 2\n$sch = new Swoole\\Coroutine\\Scheduler();\n$sch->set(['hook_flags' => SWOOLE_HOOK_ALL,]);\n$sch->add(function ()  {\n    Assert::contains(file_get_contents(URL), KEYWORDS);\n});\n$sch->start();\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/parallel.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler: user yield and resume1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$sch = new Co\\Scheduler;\n\n$sch->set(['max_coroutine' => 100]);\n\n$sch->parallel(10, function ($t, $n) {\n    Co::sleep($t);\n    echo \"Co \".Co::getCid().\"\\n\";\n}, 0.05, 'A');\n\n$sch->start();\n\n?>\n--EXPECTF--\nCo %d\nCo %d\nCo %d\nCo %d\nCo %d\nCo %d\nCo %d\nCo %d\nCo %d\nCo %d\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/preemptive/disable.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler/preemptive: swoole_coroutine_scheduler/disable\n--SKIPIF--\n<?php \nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_not_linux();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$default = 10;\n$max_msec = 10;\n\nco::set([\n    'enable_preemptive_scheduler' => true, \n    'hook_flags' => 0,\n]);\n\n$start = microtime(1);\necho \"start\\n\";\n$flag = 1;\n\ngo(function () use (&$flag, $max_msec, $start) {\n    Swoole\\Coroutine::disableScheduler();\n    echo \"coro 1 start to loop\\n\";\n    $i = 0;\n    while ($flag) {\n        $i++;\n        $m = microtime(1);\n        usleep(500000);\n        if (($m-$start) * 1000 > 500) {\n            echo \"coro 1 exec more 500ms and break\\n\";\n            break;\n        }\n    }\n    echo \"coro 1 can exit\\n\";\n    Swoole\\Coroutine::enableScheduler();\n});\n\n$end = microtime(1);\n$msec = ($end - $start) * 1000;\n\ngo(function () use (&$flag) {\n    echo \"coro 2 set flag = false\\n\";\n    $flag = false;\n});\necho \"end\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nstart\ncoro 1 start to loop\ncoro 1 exec more 500ms and break\ncoro 1 can exit\ncoro 2 set flag = false\nend\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/preemptive/disable2.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler/preemptive: swoole_coroutine_scheduler/disable2\n--SKIPIF--\n<?php \nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_not_linux();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$default = 10;\n$max_msec = 10;\nco::set(['enable_preemptive_scheduler' => true]);\n\n$start = microtime(1);\necho \"start\\n\";\n$flag = 1;\n\ngo(function () use (&$flag, $max_msec, $start) {\n    echo \"coro 1 start to loop\\n\";\n    $i = 0;\n    while ($flag) {\n        $i++;\n    }\n    echo \"coro 1 can exit\\n\";\n});\n\n$end = microtime(1);\n$msec = ($end - $start) * 1000;\n\ngo(function () use (&$flag) {\n    echo \"coro 2 set flag = false\\n\";\n    $flag = false;\n});\necho \"end\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nstart\ncoro 1 start to loop\ncoro 2 set flag = false\nend\ncoro 1 can exit\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/preemptive/do-while.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler/preemptive: do-while with opcache enable\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_not_linux();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$max_msec = 10;\nco::set(['enable_preemptive_scheduler' => true]);\n$default = 10;\n$start = microtime(true);\necho \"start\\n\";\n$flag = 1;\n\ngo(function () use (&$flag, $max_msec) {\n    echo \"coro 1 start to loop\\n\";\n    $i = 0;\n    while ($flag) {\n        $i++;\n    }\n    echo \"coro 1 can exit\\n\";\n});\n\n$end = microtime(true);\n$msec = ($end - $start) * 1000;\nUSE_VALGRIND || Assert::lessThanEq(abs($msec - $max_msec), $default);\n\ngo(function () use (&$flag) {\n    echo \"coro 2 set flag = false\\n\";\n    $flag = false;\n});\necho \"end\\n\";\n\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nstart\ncoro 1 start to loop\ncoro 2 set flag = false\nend\ncoro 1 can exit\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/preemptive/do-while2.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler/preemptive: do-while without opcache enable\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_not_linux();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$max_msec = 10;\nco::set(['enable_preemptive_scheduler' => true]);\n$default = 10;\n$start = microtime(1);\necho \"start\\n\";\n$flag = 1;\n\ngo(function () use (&$flag) {\n    echo \"coro 1 start to loop\\n\";\n    $i = 0;\n    do {\n        $i++;\n    } while ($flag);\n    echo \"coro 1 can exit\\n\";\n});\n\n$end = microtime(1);\n$msec = ($end - $start) * 1000;\nUSE_VALGRIND || Assert::lessThanEq(abs($msec - $max_msec), $default);\n\ngo(function () use (&$flag) {\n    echo \"coro 2 set flag = false\\n\";\n    $flag = false;\n});\necho \"end\\n\";\n\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nstart\ncoro 1 start to loop\ncoro 2 set flag = false\nend\ncoro 1 can exit\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/preemptive/do-while3.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler/preemptive: do-while\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_not_linux();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$max_msec = 10;\nco::set(['enable_preemptive_scheduler' => true]);\n$default = 10;\n$start = microtime(1);\necho \"start\\n\";\n$flag = 1;\n\ngo(function () use (&$flag, $max_msec) {\n    echo \"coro 1 start to loop\\n\";\n    $i = 0;\n    while ($flag) {\n        $i++;\n    }\n    echo \"coro 1 can exit\\n\";\n});\n\n$end = microtime(1);\n$msec = ($end - $start) * 1000;\nUSE_VALGRIND || Assert::lessThanEq(abs($msec - $max_msec), $default );\n\ngo(function () use (&$flag) {\n    echo \"coro 2 set flag = false\\n\";\n    $flag = false;\n});\necho \"end\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nstart\ncoro 1 start to loop\ncoro 2 set flag = false\nend\ncoro 1 can exit\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/preemptive/for.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler/preemptive: for\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_not_linux();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$max_msec = 10;\nco::set(['enable_preemptive_scheduler' => true]);\n$default = 10;\n$start = microtime(1);\necho \"start\\n\";\n$flag = 1;\n\ngo(function () use (&$flag) {\n    echo \"coro 1 start to loop\\n\";\n    $i = 0;\n    for (;;) {\n        if (!$flag) {\n            break;\n        }\n        $i++;\n    }\n    echo \"coro 1 can exit\\n\";\n});\n\n$end = microtime(1);\n$msec = ($end - $start) * 1000;\nUSE_VALGRIND || Assert::lessThanEq(abs($msec - $max_msec), $default);\n\ngo(function () use (&$flag) {\n    echo \"coro 2 set flag = false\\n\";\n    $flag = false;\n});\necho \"end\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nstart\ncoro 1 start to loop\ncoro 2 set flag = false\nend\ncoro 1 can exit\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/preemptive/for2.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler/preemptive: for\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_not_linux();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$max_msec = 10;\nco::set(['enable_preemptive_scheduler' => true]);\n$default = 10;\n$start = microtime(1);\necho \"start\\n\";\n$flag = 1;\n\ngo(function () use (&$flag) {\n    echo \"coro 1 start to loop\\n\";\n    $i = 0;\n    for (; ;) {\n        if (!$flag) {\n            break;\n        }\n        $i++;\n    }\n    echo \"coro 1 can exit\\n\";\n});\n\n$end = microtime(1);\n$msec = ($end - $start) * 1000;\nUSE_VALGRIND || Assert::lessThanEq(abs($msec - $max_msec), $default);\n\ngo(function () use (&$flag) {\n    echo \"coro 2 set flag = false\\n\";\n    $flag = false;\n});\necho \"end\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nstart\ncoro 1 start to loop\ncoro 2 set flag = false\nend\ncoro 1 can exit\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/preemptive/goto.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler/preemptive: goto \n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_not_linux();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$max_msec = 10;\nco::set(['enable_preemptive_scheduler' => true]);\n$default = 10;\n$start = microtime(1);\necho \"start\\n\";\n$flag = 1;\n\ngo(function () use (&$flag) {\n    echo \"coro 1 start to loop\\n\";\n    $i = 0;\n    loop:\n    $i++;\n    if (!$flag) {\n        goto end;\n    }\n    goto loop;\n    end:\n    echo \"coro 1 can exit\\n\";\n});\n\n$end = microtime(1);\n$msec = ($end - $start) * 1000;\nUSE_VALGRIND || Assert::lessThanEq(abs($msec - $max_msec), $default);\n\ngo(function () use (&$flag) {\n    echo \"coro 2 set flag = false\\n\";\n    $flag = false;\n});\necho \"end\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nstart\ncoro 1 start to loop\ncoro 2 set flag = false\nend\ncoro 1 can exit\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/preemptive/goto2.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler/preemptive: goto2\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_not_linux();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$max_msec = 10;\nco::set(['enable_preemptive_scheduler' => true]);\n$default = TEST_MAX_CPU_EXEC_DURATION;\n$start = microtime(1);\necho \"start\\n\";\n$flag = 1;\n\ngo(function () use (&$flag) {\n    echo \"coro 1 start to loop\\n\";\n    $i = 0;\n    loop:\n    $i++;\n    if (!$flag) {\n        goto end;\n    }\n    goto loop;\n    end:\n    echo \"coro 1 can exit\\n\";\n});\n\n$end = microtime(1);\n$msec = ($end - $start) * 1000;\nUSE_VALGRIND || Assert::lessThanEq(abs($msec - $max_msec), $default);\ngo(function () use (&$flag) {\n    echo \"coro 2 set flag = false\\n\";\n    $flag = false;\n});\necho \"end\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nstart\ncoro 1 start to loop\ncoro 2 set flag = false\nend\ncoro 1 can exit\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/preemptive/timer.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler/preemptive: child coroutine timer\n--SKIPIF--\n<?php \nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nco::set(['enable_preemptive_scheduler' => true]);\ngo(function (){\n    $exit = false;\n    while (true){\n        $res = Swoole\\Coroutine::stats();\n        $num = $res['coroutine_num'];\n        if ($num < 10){\n            go(function () use(&$exit){\n                Swoole\\Coroutine::sleep(1);\n                $exit = true;\n            });\n        }\n        if ($exit) {            \n            break;\n        }\n    }\n    echo \"coro exit\\n\";\n});\necho \"main end\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nmain end\ncoro exit\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/preemptive/while.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler/preemptive: while with opcache enable\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_not_linux();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$default = TEST_MAX_CPU_EXEC_DURATION;\n$max_msec = 10;\nco::set(['enable_preemptive_scheduler' => true]);\n\n$start = microtime(1);\necho \"start\\n\";\n$flag = 1;\n\ngo(function () use (&$flag, $max_msec) {\n    echo \"coro 1 start to loop\\n\";\n    $i = 0;\n    while ($flag) {\n        $i++;\n    }\n    echo \"coro 1 can exit\\n\";\n});\n\n$end = microtime(1);\n$msec = ($end - $start) * 1000;\nUSE_VALGRIND || Assert::lessThanEq(abs($msec - $max_msec), $default);\n\ngo(function () use (&$flag) {\n    echo \"coro 2 set flag = false\\n\";\n    $flag = false;\n});\necho \"end\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nstart\ncoro 1 start to loop\ncoro 2 set flag = false\nend\ncoro 1 can exit\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/preemptive/while2.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler/preemptive: while without opcache enable\n--SKIPIF--\n<?php  \nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_not_linux();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$max_msec = 10;\nco::set(['enable_preemptive_scheduler' => true]);\n$default = 10;\n$start = microtime(1);\necho \"start\\n\";\n$flag = 1;\n\ngo(function () use (&$flag, $max_msec) {\n    echo \"coro 1 start to loop\\n\";\n    $i = 0;\n    while ($flag) {\n        $i++;\n    }\n    echo \"coro 1 can exit\\n\";\n});\n\n$end = microtime(1);\n$msec = ($end - $start) * 1000;\nUSE_VALGRIND || Assert::lessThanEq(abs($msec - $max_msec), $default);\n\ngo(function () use (&$flag) {\n    echo \"coro 2 set flag = false\\n\";\n    $flag = false;\n});\necho \"end\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nstart\ncoro 1 start to loop\ncoro 2 set flag = false\nend\ncoro 1 can exit\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/preemptive/while3.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler/preemptive: while\n--SKIPIF--\n<?php \nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_not_linux();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$default = 10;\n$max_msec = 10;\nco::set(['enable_preemptive_scheduler' => true]);\n\n$start = microtime(1);\necho \"start\\n\";\n$flag = 1;\n\ngo(function () use (&$flag, $max_msec) {\n    echo \"coro 1 start to loop\\n\";\n    $i = 0;\n    while ($flag) {\n        $i++;\n    }\n    echo \"coro 1 can exit\\n\";\n});\n\n$end = microtime(1);\n$msec = ($end - $start) * 1000;\nUSE_VALGRIND || Assert::lessThanEq(abs($msec - $max_msec), $default);\n\ngo(function () use (&$flag) {\n    echo \"coro 2 set flag = false\\n\";\n    $flag = false;\n});\necho \"end\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nstart\ncoro 1 start to loop\ncoro 2 set flag = false\nend\ncoro 1 can exit\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/repeat.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler: user yield and resume1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n\\Swoole\\Coroutine\\run(function () {\n    echo 'scheduler 1: begin' . PHP_EOL;\n    Swoole\\Coroutine\\System::sleep(0.1);\n    echo 'scheduler 1: end' . PHP_EOL;\n});\n\necho 'sleep: begin' . PHP_EOL;\nusleep(100_000);\necho 'sleep: end' . PHP_EOL;\n\n\\Swoole\\Coroutine\\run(function () {\n    echo 'scheduler 2: begin' . PHP_EOL;\n    Swoole\\Coroutine\\System::sleep(0.1);\n    echo 'scheduler 2: end' . PHP_EOL;\n});\n\necho 'DONE' . PHP_EOL;\n\n?>\n--EXPECT--\nscheduler 1: begin\nscheduler 1: end\nsleep: begin\nsleep: end\nscheduler 2: begin\nscheduler 2: end\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/resume1.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler: user yield and resume1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\n\n$id = go(function(){\n    $id = co::getUid();\n    echo \"start coro $id\\n\";\n    co::yield();\n    echo \"resume coro $id @1\\n\";\n    co::yield();\n    echo \"resume coro $id @2\\n\";\n});\necho \"start to resume $id @1\\n\";\nco::resume($id);\necho \"start to resume $id @2\\n\";\nco::resume($id);\necho \"main\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECT--\nstart coro 1\nstart to resume 1 @1\nresume coro 1 @1\nstart to resume 1 @2\nresume coro 1 @2\nmain\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/resume2.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler: user yield and resume2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\n\n$map = [];\n$id = go(function() use (&$map){\n    $id = co::getUid();\n    echo \"start coro $id\\n\";\n    $id2 = go(function(){\n        $id2 = co::getUid();\n        echo \"start coro $id2\\n\";\n        co::yield();\n        echo \"resume coro $id2\\n\";\n    });\n    $map[2] = $id2;\n    co::yield();\n    echo \"resume coro $id\\n\";\n});\n$map[1] = $id;\necho \"start to resume {$map[2]}\\n\";\nco::resume($map[2]);\necho \"start to resume {$map[1]}\\n\";\nco::resume($map[1]);\necho \"main\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECT--\nstart coro 1\nstart coro 2\nstart to resume 2\nresume coro 2\nstart to resume 1\nresume coro 1\nmain\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/resume3.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler: user yield and resume3\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\n\n$map = [];\n$id = go(function() use (&$map){\n    $id = co::getUid();\n    echo \"start coro $id\\n\";\n    $id2 = go(function(){\n        $id2 = co::getUid();\n        echo \"start coro $id2\\n\";\n        co::yield();\n        echo \"resume coro $id2\\n\";\n    });\n    $map[2] = $id2;\n    co::yield();\n    echo \"resume coro $id\\n\";\n});\n$map[1] = $id;\necho \"start to resume {$map[1]}\\n\";\nco::resume($map[1]);\necho \"start to resume {$map[2]}\\n\";\nco::resume($map[2]);\necho \"main\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECT--\nstart coro 1\nstart coro 2\nstart to resume 1\nresume coro 1\nstart to resume 2\nresume coro 2\nmain\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/resume4.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler: user yield and resume4\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nCo::yield();\n$id = go(function () {\n    $id = Co::getUid();\n    echo \"start coro $id\\n\";\n    Co::yield();\n    echo \"resume coro $id\\n\";\n});\necho \"start to resume $id\\n\";\nCo::resume($id);\necho \"main\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nFatal error: Uncaught Swoole\\Error: API must be called in the coroutine in %s:%d\nStack trace:\n#0 %s(3): Swoole\\Coroutine::yield()\n#1 {main}\n  thrown in %s on line %d\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/resume5.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler: user yield and resume4\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\n\ngo(function () {\n    $main = co::getuid();\n    echo \"start to create coro\\n\";\n    go(function () use ($main) {\n        echo \"coro 2\\n\";\n        co::sleep(0.1);\n        echo \"resume\\n\";\n        co::resume($main);\n    });\n    echo \"before yield\\n\";\n    co::yield();\n    echo \"after yield\\n\";\n});\necho \"main\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nstart to create coro\ncoro 2\nbefore yield\nmain\nresume\nafter yield\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/resume6.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler: user yield and resume4\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\n\ngo(function () {\n    echo \"coro 1 start\\n\";\n    co::yield();\n    echo \"coro 1 end\\n\";\n});\necho \"main 1\\n\";\ngo(function () {\n    echo \"coro 2 start\\n\";\n    co::resume(1);\n    echo \"coro 2 end\\n\";\n});\necho \"main 2\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECTF--\ncoro 1 start\nmain 1\ncoro 2 start\ncoro 1 end\ncoro 2 end\nmain 2\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/start.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler: user yield and resume1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$sch = new Co\\Scheduler;\n\n$sch->set(['max_coroutine' => 100]);\n\n$sch->add(function ($t, $n) {\n    Co::sleep($t);\n    echo \"$n\\n\";\n}, 0.2, 'A');\n\n$sch->add(function ($t, $n) {\n    Co::sleep($t);\n    echo \"$n\\n\";\n}, 0.1, 'B');\n\n$sch->add(function () {\n    var_dump(Co::getCid());\n});\n\n$sch->start();\n\n?>\n--EXPECTF--\nint(%d)\nB\nA\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/start_in_server_shutdown.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler: start in server onShutdown callback\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\n\n$file = file_get_contents(__FILE__);\n\n$server = new Swoole\\Server('127.0.0.1', get_one_free_port(), SWOOLE_PROCESS);\n$server->set(['worker_num' => 2, 'log_level' => SWOOLE_LOG_WARNING,]);\n\n$server->on('WorkerStart', function (Swoole\\Server $server, int $worker_id) use ($file) {\n    if ($worker_id == 1) {\n        Swoole\\Timer::after(200, function () use ($server, $file) {\n            System::sleep(0.1);\n            echo \"[1] Co \" . Co::getCid() . \"\\n\";\n            Assert::same(System::readFile(__FILE__), $file);\n            $server->shutdown();\n        });\n    }\n});\n\n$server->on('Receive', function (Swoole\\Server $server, $fd, $reactor_id, $data) {\n});\n\n$server->on('shutdown', function () use ($file) {\n    $sch = new Co\\Scheduler;\n    $sch->add(function ($t, $n) use ($file) {\n        System::sleep($t);\n        echo \"[2] Co \" . Co::getCid() . \"\\n\";\n        Assert::same(System::readFile(__FILE__), $file);\n    }, 0.05, 'A');\n    $sch->start();\n});\n$server->start();\n?>\n--EXPECTF--\n[1] Co 2\n[2] Co 1\n"
  },
  {
    "path": "tests/swoole_coroutine_scheduler/start_in_server_worker_stop.phpt",
    "content": "--TEST--\nswoole_coroutine_scheduler: start in server onShutdown callback\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\n\n$file = file_get_contents(__FILE__);\n\n$server = new Swoole\\Server('127.0.0.1', get_one_free_port(), SWOOLE_PROCESS);\n$server->set(['worker_num' => 2, 'log_level' => SWOOLE_LOG_WARNING,]);\n\n$server->on('WorkerStart', function (Swoole\\Server $server, int $worker_id) use ($file) {\n    if ($worker_id == 1) {\n        Swoole\\Timer::after(200, function () use ($server, $file) {\n            System::sleep(0.1);\n            echo \"[1] Co \" . Co::getCid() . \"\\n\";\n            Assert::same(System::readFile(__FILE__), $file);\n            $server->shutdown();\n        });\n    }\n});\n\n$server->on('Receive', function (Swoole\\Server $server, $fd, $reactor_id, $data) {\n});\n\n$server->on('workerStop', function (Swoole\\Server $server, int $worker_id) use ($file) {\n    if ($worker_id == 1) {\n        $sch = new Co\\Scheduler;\n        $sch->add(function ($t, $n) use ($file) {\n            System::sleep($t);\n            echo \"[2] Co \" . Co::getCid() . \"\\n\";\n            Assert::same(System::readFile(__FILE__), $file);\n        }, 0.05, 'A');\n        $sch->start();\n    }\n});\n\n$server->start();\n?>\n--EXPECT--\n[1] Co 2\n[2] Co 3\n"
  },
  {
    "path": "tests/swoole_coroutine_system/aio_thread_num.phpt",
    "content": "--TEST--\nswoole_coroutine_system: gethostbyname\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse  Swoole\\Coroutine\\System;\nuse  Swoole\\Coroutine;\n\nuse function Swoole\\Coroutine\\run;\n\nCoroutine::set([\n    'aio_core_worker_num' => swoole_cpu_num(),\n    'aio_max_wait_time' => 0.0005\n]);\n\nrun(function () {\n    System::readFile(__FILE__);\n});\n\n$sch = new Swoole\\Coroutine\\Scheduler();\n$sch->set(['dns_cache_capacity' => 0]);\n$sch->add(function () {\n    static $worker_num = 0;\n    $n = 100;\n    while ($n--) {\n        $worker_num = max($worker_num, Coroutine::stats()['aio_worker_num']);\n        Swoole\\Coroutine\\System::sleep(0.001);\n    }\n    Assert::greaterThan($worker_num, swoole_cpu_num());\n    phpt_var_dump($worker_num);\n});\n$sch->parallel(swoole_cpu_num() * [4, 16, 32, 64][PRESSURE_LEVEL], function () {\n    System::sleep(mt_rand(1, 5) / 1000);\n    $result = Swoole\\Coroutine\\System::getaddrinfo('www.baidu.com');\n    Assert::notEmpty($result);\n});\n$sch->start();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine_system/getaddrinfo.phpt",
    "content": "--TEST--\nswoole_coroutine_system: getaddrinfo\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nCo\\run(function () {\n    $ip_list = Swoole\\Coroutine\\System::getaddrinfo('www.baidu.com', AF_INET);\n    Assert::assert(!empty($ip_list) and is_array($ip_list));\n    foreach ($ip_list as $ip) {\n        Assert::assert(preg_match(IP_REGEX, $ip));\n    }\n    echo \"DONE\\n\";\n});\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine_system/getaddrinfo_timeout.phpt",
    "content": "--TEST--\nswoole_coroutine_system: getaddrinfo timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_not_root();\nskip_if_in_ci();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$tmpdir = '/tmp/' . uniqid();\n\nmkdir_if_not_exists($tmpdir . '/etc');\nchroot($tmpdir);\nif (!is_file('/etc/resolv.conf')) {\n    file_put_contents('/etc/resolv.conf', \"nameserver 192.168.8.8\\noptions timeout:1 retry:1\\n\");\n}\n\nCo\\run(static function () {\n    $res = Swoole\\Coroutine\\System::getaddrinfo(\n        domain: 'swoole-non-existent-domain',\n        protocol: 0,\n        service: '',\n        timeout: 0.5,\n    );\n    Assert::false($res);\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_TIMEDOUT);\n    echo \"DONE\\n\";\n});\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine_system/gethostbyname.phpt",
    "content": "--TEST--\nswoole_coroutine_system: gethostbyname\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Coroutine::create(function () {\n    $ip = Swoole\\Coroutine\\System::gethostbyname('www.baidu.com');\n    Assert::assert($ip != false);\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine_system/gethostbyname_ipv6.phpt",
    "content": "--TEST--\nswoole_coroutine_system: gethostbyname for IPv6\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Coroutine::create(function () {\n    $ip = Swoole\\Coroutine\\System::gethostbyname('ipv6.google.com', AF_INET6);\n    phpt_var_dump($ip);\n    Assert::notEmpty($ip);\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine_system/gethostbyname_timeout.phpt",
    "content": "--TEST--\nswoole_coroutine_system: gethostbyname timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Coroutine::create(function () {\n    $result = Swoole\\Coroutine\\System::gethostbyname(\"wwww.xxxx.cccn.xer\" . time(), AF_INET, 0.001);\n    Assert::eq($result, false);\n    Assert::same(swoole_last_error(), SWOOLE_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT);\n    co::sleep(0.1);\n    echo \"NEXT\\n\";\n    $result = Swoole\\Coroutine\\System::gethostbyname(\"www.github.com\", AF_INET, 1);\n    Assert::notEmpty($result);\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\nNEXT\n"
  },
  {
    "path": "tests/swoole_coroutine_system/readfile.phpt",
    "content": "--TEST--\nswoole_coroutine_system: readFile\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    $content = Swoole\\Coroutine\\System::readFile(TEST_IMAGE);\n    Assert::same(md5_file(TEST_IMAGE), md5($content));\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine_system/sleep.phpt",
    "content": "--TEST--\nswoole_coroutine_system: sleep\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Coroutine::create(function () {\n    Swoole\\Coroutine\\System::sleep(0.5);\n    echo \"OK\";\n});\n\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_coroutine_system/wait.phpt",
    "content": "--TEST--\nswoole_coroutine_system: wait\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Process;\n\n$atomic = new Atomic;\n\n$process = new Process(function () use ($atomic) {\n    $atomic->wait(10);\n});\n$process->start();\n\nCoroutine\\run(function () use ($process, $atomic) {\n    for ($n = MAX_REQUESTS; $n--;) {\n        $status = System::wait(0.001);\n        Assert::false($status);\n        Assert::same(swoole_last_error(), SOCKET_ETIMEDOUT);\n    }\n    $atomic->wakeup();\n    $status = System::wait(1);\n    Assert::same($status['pid'], $process->pid);\n    var_dump($status);\n});\n\n?>\n--EXPECTF--\narray(3) {\n  [\"pid\"]=>\n  int(%d)\n  [\"code\"]=>\n  int(0)\n  [\"signal\"]=>\n  int(0)\n}\n"
  },
  {
    "path": "tests/swoole_coroutine_system/waitEvent.phpt",
    "content": "--TEST--\nswoole_coroutine_system: waitEvent\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\n\nCoroutine\\run(function () {\n    $client = stream_socket_client('tcp://www.qq.com:80', $errno, $errstr, 30);\n    $events = Coroutine::waitEvent($client, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE);\n    Assert::eq($events, SWOOLE_EVENT_WRITE);\n    fwrite($client, \"GET / HTTP/1.1\\r\\nHost: www.qq.com\\r\\n\\r\\n\");\n    $events = Coroutine::waitEvent($client, SWOOLE_EVENT_READ);\n    Assert::eq($events, SWOOLE_EVENT_READ);\n    $response = fread($client, 8192);\n    Assert::contains($response, 'www.qq.com');\n});\n\necho \"DONE\\n\";\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine_system/waitPid.phpt",
    "content": "--TEST--\nswoole_coroutine_system: waitPid\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Process;\n\n$atomic = new Atomic;\n\n$processFast = new Process(function () {\n    usleep(1000);\n});\n$processFast->start();\n\n$processSlow = new Process(function () use ($atomic) {\n    $atomic->wait(10);\n    usleep(10 * 1000);\n});\n$processSlow->start();\n\nCoroutine\\run(function () use ($processFast, $processSlow, $atomic) {\n    for ($n = MAX_REQUESTS; $n--;) {\n        $status = System::waitPid($processSlow->pid, 0.001);\n        Assert::false($status);\n        Assert::same(swoole_last_error(), SOCKET_ETIMEDOUT);\n    }\n    $atomic->wakeup();\n    $status = System::waitPid($processSlow->pid, 1);\n    Assert::same($status['pid'], $processSlow->pid);\n    var_dump($status);\n    $status = System::waitPid($processFast->pid);\n    Assert::same($status['pid'], $processFast->pid);\n    var_dump($status);\n});\n\n?>\n--EXPECTF--\narray(3) {\n  [\"pid\"]=>\n  int(%d)\n  [\"code\"]=>\n  int(0)\n  [\"signal\"]=>\n  int(0)\n}\narray(3) {\n  [\"pid\"]=>\n  int(%d)\n  [\"code\"]=>\n  int(0)\n  [\"signal\"]=>\n  int(0)\n}\n"
  },
  {
    "path": "tests/swoole_coroutine_system/waitSignal.phpt",
    "content": "--TEST--\nswoole_coroutine_system: waitSignal\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Process;\n\n$atomic = new Atomic;\n\n$pid = getmypid();\n$killer = new Process(function () use ($pid, $atomic) {\n    $atomic->wait();\n    echo \"2\\n\";\n    switch_process();\n    Process::kill($pid, SIGUSR1);\n    $atomic->wait();\n    echo \"6\\n\";\n    switch_process();\n    Process::kill($pid, SIGUSR2);\n    echo \"8\\n\";\n});\n$killer->start();\n\nCoroutine\\run(function () use ($atomic) {\n    Coroutine::sleep(0.001);\n    switch_process();\n    $atomic->wakeup();\n    echo \"1\\n\";\n    Assert::eq(System::waitSignal(SIGUSR1), SIGUSR1);\n    echo \"3\\n\";\n    Assert::false(System::waitSignal(SIGUSR2, 0.01));\n    echo \"4\\n\";\n    $atomic->wakeup();\n    echo \"5\\n\";\n    Assert::eq(System::waitSignal(SIGUSR2), SIGUSR2);\n    echo \"7\\n\";\n    System::wait();\n    echo \"9\\n\";\n});\n\n?>\n--EXPECT--\n1\n2\n3\n4\n5\n6\n8\n7\n9\n"
  },
  {
    "path": "tests/swoole_coroutine_system/waitSignal_2.phpt",
    "content": "--TEST--\nswoole_coroutine_system: waitSignal 2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Process;\n\n$atomic = new Atomic;\n\n$pid = getmypid();\n$killer = new Process(function () use ($pid, $atomic) {\n    $atomic->wait();\n    echo \"2\\n\";\n    switch_process();\n    Process::kill($pid, SIGUSR1);\n    $atomic->wait();\n    echo \"6\\n\";\n    switch_process();\n    Process::kill($pid, SIGUSR2);\n    echo \"8\\n\";\n});\n$killer->start();\n\nCoroutine\\run(function () use ($atomic) {\n    Coroutine::sleep(0.001);\n    switch_process();\n    $atomic->wakeup();\n    echo \"1\\n\";\n    $list = [SIGUSR1, SIGUSR2, SIGIO];\n    Assert::eq(System::waitSignal($list), SIGUSR1);\n    echo \"3\\n\";\n    Assert::false(System::waitSignal($list, 0.01));\n    echo \"4\\n\";\n    $atomic->wakeup();\n    echo \"5\\n\";\n    Assert::eq(System::waitSignal($list), SIGUSR2);\n    echo \"7\\n\";\n    System::wait();\n    echo \"9\\n\";\n});\n\n?>\n--EXPECT--\n1\n2\n3\n4\n5\n6\n8\n7\n9\n"
  },
  {
    "path": "tests/swoole_coroutine_system/writefile.phpt",
    "content": "--TEST--\nswoole_coroutine_system: writeFile\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$content = file_get_contents(TEST_IMAGE);\n$filename = __DIR__ . '/tmp_file.jpg';\nSwoole\\Coroutine::create(function () use ($filename, $content) {\n    $n = Swoole\\Coroutine\\System::writeFile($filename, $content);\n    Assert::same(md5_file($filename), md5_file(TEST_IMAGE));\n    Assert::same($n, filesize($filename));\n    unlink($filename);\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine_system/writefile_append.phpt",
    "content": "--TEST--\nswoole_coroutine_system: writeFile use FILE_APPEND\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\nuse function Swoole\\Coroutine\\run;\n\n$filename = __DIR__ . '/tmp_file.txt';\nrun(function () use ($filename) {\n    $n = 0;\n    $n += System::writeFile($filename, \"first line\\n\", FILE_APPEND);\n    $n += System::writeFile($filename, \"second line\\n\", FILE_APPEND);\n    $n += System::writeFile($filename, \"third line\\n\", FILE_APPEND);\n    Assert::same($n, filesize($filename));\n    Assert::same(md5_file($filename), md5(System::readFile($filename)));\n    unlink($filename);\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine_util/dns_lookup.phpt",
    "content": "--TEST--\nswoole_coroutine_util: dns lookup\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\nskip_if_no_database();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(array(\n        'log_file' => '/dev/null'\n    ));\n    $http->on(\"WorkerStart\", function (Swoole\\Server $serv) {\n        /**\n         * @var $pm ProcessManager\n         */\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $host = swoole_async_dns_lookup_coro(TEST_DOMAIN_3);\n        if ($host) {\n            $response->end(\"OK\\n\");\n        } else {\n            $response->end(\"ERROR: \" . swoole_last_error() . \"\\n\");\n        }\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_coroutine_util/exec.phpt",
    "content": "--TEST--\nswoole_coroutine_util: exec\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_command_not_found('md5sum');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    $data = co::exec('md5sum ' . TEST_IMAGE);\n    Assert::same($data['code'], 0);\n    Assert::same($data['signal'], 0);\n    Assert::same(strstr($data['output'], ' ', true), md5_file(TEST_IMAGE));\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine_util/exec_sleep.phpt",
    "content": "--TEST--\nswoole_coroutine_util: coroutine exec\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$s = microtime(true);\nfor ($i = MAX_PROCESS_NUM; $i--;) {\n    go(function () {\n        co::exec('sleep 1');\n    });\n}\nSwoole\\Event::wait();\n$s = microtime(true) - $s;\ntime_approximate(1, $s, 0.5);\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine_util/fgets.phpt",
    "content": "--TEST--\nswoole_coroutine_util: fgets\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nCo\\run(function () {\n    $file = __DIR__ . '/../../examples/server/mixed.php';\n\n    Swoole\\Runtime::enableCoroutine(SWOOLE_HOOK_ALL);\n    $coroutine = [];\n    $fp = fopen($file, \"r\");\n    while (!feof($fp)) {\n        $coroutine [] = fgets($fp);\n    }\n\n    Swoole\\Runtime::enableCoroutine(false);\n    $standard = [];\n    $fp = fopen($file, \"r\");\n    while (!feof($fp)) {\n        $standard [] = fgets($fp);\n    }\n\n    Assert::same($standard, $coroutine);\n    echo \"DONE\\n\";\n});\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine_util/fread.phpt",
    "content": "--TEST--\nswoole_coroutine_util: fread\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nCo\\run(function () {\n    $fp = fopen(TEST_IMAGE, 'r');\n    if ($fp) {\n        $data = fread($fp, 1024 * 1024);\n        Assert::same(md5($data), md5_file(TEST_IMAGE));\n    } else {\n        echo \"ERROR\\n\";\n    }\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine_util/fwrite.phpt",
    "content": "--TEST--\nswoole_coroutine_util: fwrite\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nCo\\run(function () {\n    $file = __DIR__ . '/tmp';\n    $fp = fopen($file, 'w+');\n    $data = RandStr::gen(8192 * 8);\n    if ($fp) {\n        $ret = fwrite($fp, $data);\n        if ($ret) {\n            Assert::same(md5($data), md5_file($file));\n            unlink($file);\n            return;\n        }\n    }\n    unlink($file);\n    echo \"ERROR\\n\";\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine_util/list_coroutine.phpt",
    "content": "--TEST--\nswoole_coroutine_util: listCoroutines\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$co_list = [];\n\nforeach(range(1, 10) as $i) {\n    $co_list[] = go(function () use ($i) {\n        co::sleep(.4);\n    });\n}\n\ngo(function () use ($co_list) {\n    co::sleep(.2);\n    $coros = Co::listCoroutines();\n    $list_2 = iterator_to_array($coros);\n    Assert::same(array_values(array_diff($list_2, $co_list)), [Co::getUid(),]);\n});\n\nSwoole\\Event::wait();\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_coroutine_util/task_worker.phpt",
    "content": "--TEST--\nswoole_coroutine_util: sleep in Task-Worker\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $cli->set(['open_eof_check' => true, \"package_eof\" => \"\\r\\n\\r\\n\"]);\n    $cli->connect('127.0.0.1', $pm->getFreePort(), 5) or die(\"ERROR\");\n    $cli->send(\"task-01\") or die(\"ERROR\");\n    echo trim($cli->recv()) . \"\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    ini_set('swoole.display_errors', 'Off');\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        \"worker_num\" => 1,\n        'task_worker_num' => 2,\n        'log_file' => '/dev/null',\n        'task_enable_coroutine' => true\n    ));\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n        $serv->task([$fd, 'sleep']);\n    });\n\n    $serv->on('task', function (Swoole\\Server $serv, $task) {\n        list($fd) = $task->data;\n        co::sleep(0.2);\n        $serv->send($fd, \"sleep\\r\\n\\r\\n\");\n    });\n\n    $serv->on('finish', function (Swoole\\Server $serv, $fd, $rid, $data)\n    {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nsleep\n"
  },
  {
    "path": "tests/swoole_coroutine_wait_group/base.phpt",
    "content": "--TEST--\nswoole_coroutine_wait_group: base\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Coroutine\\WaitGroup;\n\n$wg = new WaitGroup;\nCo\\run(function () use ($wg) {\n    go(function () use ($wg) {\n        $wg->add();\n        Assert::same(\n            file_get_contents(__FILE__),\n            System::readFile(__FILE__)\n        );\n        echo \"TASK[1] DONE\\n\";\n        $wg->done();\n    });\n    $cid = go(function () use ($wg) {\n        $wg->add();\n        Assert::true(Co::yield());\n        echo \"TASK[2] DONE\\n\";\n        $wg->done();\n    });\n    go(function () use ($wg, $cid) {\n        $wg->add();\n        Assert::notSame(Co::sleep(0.001), false);\n        Co::resume($cid);\n        echo \"TASK[3] DONE\\n\";\n        $wg->done();\n    });\n    $wg->wait();\n});\n?>\n--EXPECTF--\nTASK[%d] DONE\nTASK[%d] DONE\nTASK[%d] DONE\n"
  },
  {
    "path": "tests/swoole_coroutine_wait_group/defer.phpt",
    "content": "--TEST--\nswoole_coroutine_wait_group: run in defer\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nCo\\run(function () {\n    $wg = new Swoole\\Coroutine\\WaitGroup();\n    foreach (range(1, 2) as $i) {\n        var_dump(\"add $i\");\n        $wg->add();\n        Co\\go(function () use ($wg, $i) {\n            var_dump(\"start $i\");\n            defer(function () use ($wg, $i) {\n                var_dump(\"defer $i\");\n                $wg->done();\n                var_dump(\"done $i\");\n            });\n            var_dump(\"end $i\");\n        });\n    }\n\n    var_dump(\"wait\");\n    $wg->wait();\n    var_dump(\"finish\");\n});\n\n?>\n--EXPECT--\nstring(5) \"add 1\"\nstring(7) \"start 1\"\nstring(5) \"end 1\"\nstring(7) \"defer 1\"\nstring(6) \"done 1\"\nstring(5) \"add 2\"\nstring(7) \"start 2\"\nstring(5) \"end 2\"\nstring(7) \"defer 2\"\nstring(6) \"done 2\"\nstring(4) \"wait\"\nstring(6) \"finish\"\n"
  },
  {
    "path": "tests/swoole_coroutine_wait_group/empty.phpt",
    "content": "--TEST--\nswoole_coroutine_wait_group: empty\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$wg = new Swoole\\Coroutine\\WaitGroup;\n$wg->add(1);\n$wg->add(-1);\n$wg->wait();\n$wg->add(1);\n$wg->done();\n$wg->wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_coroutine_wait_group/logic.phpt",
    "content": "--TEST--\nswoole_coroutine_wait_group: logic\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $wg = new Swoole\\Coroutine\\WaitGroup;\n    Assert::throws(function () use ($wg) {\n        $wg->add(-1);\n    }, LogicException::class);\n    $wg->add(1);\n    go(function () use ($wg) {\n        Co::sleep(0.001);\n        Assert::throws(function () use ($wg) {\n            $wg->add(1);\n        }, LogicException::class);\n        $wg->done();\n    });\n    $wg->wait();\n    Assert::throws(function () use ($wg) {\n        $wg->done();\n    }, LogicException::class);\n    echo \"DONE\\n\";\n});\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_curl/abnormal_response/1.phpt",
    "content": "--TEST--\nswoole_curl/abnormal_response: 1\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () use ($pm) {\n        $ch = curl_init();\n        $code = uniqid('swoole_');\n        $url = \"http://127.0.0.1:\" . $pm->getFreePort() . \"/?code=\" . urlencode($code);\n\n        curl_setopt($ch, CURLOPT_URL, $url);\n        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n        curl_setopt($ch, CURLOPT_HEADER, 0);\n        $output = curl_exec($ch);\n        Assert::isEmpty($output);\n        Assert::oneOf(curl_errno($ch), [CURLE_RECV_ERROR, CURLE_GOT_NOTHING]);\n        curl_close($ch);\n    });\n    $pm->kill();\n    echo \"Done\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Server(\"127.0.0.1\", $pm->getFreePort());\n    $server->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $server->on(\"start\", function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('Connect', function ($serv, $fd, $wid) {\n        $serv->close($fd);\n    });\n    $server->on('Receive', function ($serv, $fd, $wid, $data) {\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/abnormal_response/2.phpt",
    "content": "--TEST--\nswoole_curl/abnormal_response: 2\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () use ($pm) {\n        $ch = curl_init();\n        $code = uniqid('swoole_');\n        $url = \"http://127.0.0.1:\" . $pm->getFreePort() . \"/?code=\" . urlencode($code);\n\n        curl_setopt($ch, CURLOPT_URL, $url);\n        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n        curl_setopt($ch, CURLOPT_HEADER, 0);\n        $output = curl_exec($ch);\n        Assert::isEmpty($output);\n        Assert::eq(curl_errno($ch), CURLE_PARTIAL_FILE);\n        curl_close($ch);\n    });\n    $pm->kill();\n    echo \"Done\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Server(\"127.0.0.1\", $pm->getFreePort());\n    $server->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $server->on(\"start\", function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('Receive', function ($serv, $fd, $wid, $data) {\n        usleep(100);\n        $serv->send($fd, \"HTTP/1.1 200 OK\\r\\n\" .\n            \"Content-Type: text/html; charset=UTF-8\\r\\n\" .\n            \"Content-Length: 1256\\r\\n\");\n        usleep(10000);\n        $serv->close($fd);\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/basic/1.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_exec() function with basic functionality\n--CREDITS--\nSebastian Deutsch\n<sebastian.deutsch@9elements.com>\nTestFest 2009 - AFUP - Jean-Marc Fontaine\n<jmf@durcommefaire.net>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new SwooleTest\\CurlManager();\n\n$cm->run(function ($host) {\n    // start testing\n    echo \"*** Testing curl_exec() : basic functionality ***\\n\";\n\n    $url = $host. \"/get.php?test=get\";\n    $ch = curl_init();\n\n    ob_start(); // start output buffering\n    curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use\n    $ok = curl_exec($ch);\n    curl_close($ch);\n    $curl_content = ob_get_contents();\n    ob_end_clean();\n\n    if ($ok) {\n        var_dump($curl_content);\n    } else {\n        echo \"curl_exec returned false\";\n    }\n});\n\n?>\n--EXPECTF--\n*** Testing curl_exec() : basic functionality ***\nstring(25) \"Hello World!\nHello World!\"\n"
  },
  {
    "path": "tests/swoole_curl/basic/10.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_error() & curl_errno() function with problematic proxy\n--CREDITS--\nTestFest 2009 - AFUP - Perrick Penet <perrick@noparking.net>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php\n\tif (!extension_loaded(\"curl\")) print \"skip\";\n\t$addr = \"www.\".uniqid().\".\".uniqid();\n\tif (gethostbyname($addr) != $addr) {\n\t\tprint \"skip catch all dns\";\n\t}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n    $url = \"http://www.example.org\";\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_PROXY, uniqid() . \":\" . uniqid());\n    curl_setopt($ch, CURLOPT_URL, $url);\n\n    curl_exec($ch);\n    Assert::eq(curl_errno($ch), CURLE_COULDNT_RESOLVE_PROXY);\n    curl_close($ch);\n    echo \"DONE\\n\";\n}, false);\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_curl/basic/11.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_opt() function with COOKIE\n--CREDITS--\nTestFest 2009 - AFUP - Xavier Gorse <xgorse@elao.com>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\n/* Prototype  : bool curl_setopt(resource ch, int option, mixed value)\n * Description: Set an option for a cURL transfer\n * Source code: ext/curl/interface.c\n * Alias to functions:\n */\n\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n    // start testing\n    echo '*** Testing curl with cookie ***' . \"\\n\";\n\n    $url = \"{$host}/get.php?test=cookie\";\n    $ch = curl_init();\n\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_COOKIE, 'foo=bar');\n    curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use\n\n    $curl_content = curl_exec($ch);\n    curl_close($ch);\n\n    var_dump( $curl_content );\n});\n\n\n?>\n===DONE===\n--EXPECTF--\n*** Testing curl with cookie ***\nstring(3) \"bar\"\n===DONE===\n"
  },
  {
    "path": "tests/swoole_curl/basic/12.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_opt() function with CURLOPT_HTTP_VERSION/CURL_HTTP_VERSION_1_0\n--CREDITS--\nTestFest 2009 - AFUP - Xavier Gorse <xgorse@elao.com>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\n/* Prototype  : bool curl_setopt(resource ch, int option, mixed value)\n * Description: Set an option for a cURL transfer\n * Source code: ext/curl/interface.c\n * Alias to functions:\n */\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->disableNativeCurl();\n$cm->run(function ($host) {\n\n    // start testing\n    echo '*** Testing curl with HTTP/1.0 ***' . \"\\n\";\n\n    $url = \"{$host}/get.php?test=httpversion\";\n    $ch = curl_init();\n\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);\n    curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use\n\n    $curl_content = curl_exec($ch);\n    curl_close($ch);\n\n    var_dump( $curl_content );\n\n});\n\n?>\n===DONE===\n--EXPECTF--\n*** Testing curl with HTTP/1.0 ***\n\nWarning: swoole_curl_setopt(): CURLOPT_HTTP_VERSION[%d] is not supported in %s on line %d\nstring(8) \"HTTP/1.1\"\n===DONE===\n"
  },
  {
    "path": "tests/swoole_curl/basic/13.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_opt() function with CURLOPT_HTTP_VERSION/CURL_HTTP_VERSION_1_1\n--CREDITS--\nTestFest 2009 - AFUP - Xavier Gorse <xgorse@elao.com>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\n/* Prototype  : bool curl_setopt(resource ch, int option, mixed value)\n * Description: Set an option for a cURL transfer\n * Source code: ext/curl/interface.c\n * Alias to functions:\n */\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n    // start testing\n    echo '*** Testing curl with HTTP/1.1 ***' . \"\\n\";\n\n    $url = \"{$host}/get.php?test=httpversion\";\n    $ch = curl_init();\n\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);\n    curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use\n\n    $curl_content = curl_exec($ch);\n    curl_close($ch);\n\n    var_dump( $curl_content );\n\n});\n\n\n?>\n===DONE===\n--EXPECTF--\n*** Testing curl with HTTP/1.1 ***\nstring(8) \"HTTP/1.1\"\n===DONE===\n"
  },
  {
    "path": "tests/swoole_curl/basic/14.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_init() function with basic functionality\n--CREDITS--\nJean-Marc Fontaine <jmf@durcommefaire.net>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php if (!extension_loaded(\"curl\")) exit(\"skip curl extension not loaded\"); ?>\n--FILE--\n<?php\n\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->disableNativeCurl();\n$cm->run(function ($host) {\n    $ch = curl_init();\n    Assert::isInstanceOf($ch, Swoole\\Curl\\Handler::class);\n\n}, false);\n\n?>\n===DONE===\n--EXPECT--\n===DONE===\n"
  },
  {
    "path": "tests/swoole_curl/basic/15.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_init() function with $url parameter defined\n--CREDITS--\nJean-Marc Fontaine <jmf@durcommefaire.net>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php if (!extension_loaded(\"curl\")) exit(\"skip curl extension not loaded\"); ?>\n--FILE--\n<?php\n\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n    $url = 'http://www.example.com/';\n    $ch = curl_init($url);\n\n    Assert::same($url, curl_getinfo($ch, CURLINFO_EFFECTIVE_URL));\n}, false);\n\n?>\n===DONE===\n--EXPECT--\n===DONE===\n"
  },
  {
    "path": "tests/swoole_curl/basic/19.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_getinfo() function with CURLINFO_EFFECTIVE_URL parameter\n--CREDITS--\nJean-Marc Fontaine <jmf@durcommefaire.net>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n\n    $url = \"http://{$host}/get.php?test=\";\n    $ch  = curl_init();\n\n    curl_setopt($ch, CURLOPT_URL, $url);\n    curl_exec($ch);\n    $info = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);\n    var_dump($url == $info);\n    curl_close($ch);\n});\n?>\n===DONE===\n--EXPECTF--\nHello World!\nHello World!bool(true)\n===DONE===\n"
  },
  {
    "path": "tests/swoole_curl/basic/2.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_opt() function with CURLOPT_RETURNTRANSFER parameter set to 1\n--CREDITS--\nSebastian Deutsch <sebastian.deutsch@9elements.com>\nTestFest 2009 - AFUP - Jean-Marc Fontaine <jmf@durcommefaire.net>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n    // start testing\n    echo '*** Testing curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); ***' . \"\\n\";\n\n    $url = \"{$host}/get.php?test=get\";\n    $ch = curl_init();\n\n    ob_start(); // start output buffering\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use\n\n    $curl_content = curl_exec($ch);\n    curl_close($ch);\n\n    var_dump( $curl_content );\n});\n?>\n===DONE===\n--EXPECTF--\n*** Testing curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); ***\nstring(25) \"Hello World!\nHello World!\"\n===DONE===\n"
  },
  {
    "path": "tests/swoole_curl/basic/20.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_getinfo() function with CURLINFO_HTTP_CODE parameter\n--CREDITS--\nJean-Marc Fontaine <jmf@durcommefaire.net>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n    $url = \"{$host}/get.php?test=\";\n    $ch  = curl_init();\n    curl_setopt($ch, CURLOPT_URL, $url);\n    curl_exec($ch);\n    var_dump(curl_getinfo($ch, CURLINFO_HTTP_CODE));\n    curl_close($ch);\n});\n?>\n===DONE===\n--EXPECTF--\nHello World!\nHello World!int(200)\n===DONE===\n"
  },
  {
    "path": "tests/swoole_curl/basic/21.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_getinfo() function with CURLINFO_CONTENT_TYPE parameter\n--CREDITS--\nJean-Marc Fontaine <jmf@durcommefaire.net>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n\n    $url  = \"{$host}/get.php?test=contenttype\";\n\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, $url);\n    curl_exec($ch);\n    var_dump(curl_getinfo($ch, CURLINFO_CONTENT_TYPE));\n    curl_close($ch);\n});\n\n?>\n===DONE===\n--EXPECTF--\nstring(24) \"text/plain;charset=utf-8\"\n===DONE===\n"
  },
  {
    "path": "tests/swoole_curl/basic/22.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_setopt() function with CURLOPT_FOLLOWLOCATION parameter\n--CREDITS--\nJean-Marc Fontaine <jmf@durcommefaire.net>\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse SwooleTest\\CurlManager;\n\n$cm = new CurlManager();\n$cm->run(function ($host) {\n    // co::set(['log_level' => 0, 'trace_flags' => SWOOLE_TRACE_ALL]);\n    // CURLOPT_FOLLOWLOCATION = true\n    $urls = [\n        \"{$host}/get.php?test=redirect_301\",\n        \"{$host}/get.php?test=redirect_302\",\n        \"{$host}/get.php?test=redirect_307\",\n        \"{$host}/get.php?test=redirect_308\",\n    ];\n    foreach ($urls as $url) {\n        $ch = curl_init();\n        curl_setopt($ch, CURLOPT_URL, $url);\n        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);\n        curl_setopt($ch, CURLOPT_POST, 1);\n        curl_setopt($ch, CURLOPT_POSTFIELDS, 'id=123&name=swoole');\n        curl_exec($ch);\n        $info = curl_getinfo($ch);\n\n        Assert::eq($info['redirect_count'], 1);\n\n        curl_close($ch);\n    }\n\n    // CURLOPT_FOLLOWLOCATION = false\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, $url);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_exec($ch);\n    $info = curl_getinfo($ch);\n\n    Assert::eq($info['redirect_count'], 0);\n    Assert::assert(\"http://{$host}/get.php?test=getpost\" === $info['redirect_url']);\n\n    curl_close($ch);\n});\n?>\n===DONE===\n--EXPECTF--\narray(1) {\n  [\"test\"]=>\n  string(7) \"getpost\"\n}\narray(0) {\n}\narray(1) {\n  [\"test\"]=>\n  string(7) \"getpost\"\n}\narray(0) {\n}\narray(1) {\n  [\"test\"]=>\n  string(7) \"getpost\"\n}\narray(2) {\n  [\"id\"]=>\n  string(3) \"123\"\n  [\"name\"]=>\n  string(6) \"swoole\"\n}\narray(1) {\n  [\"test\"]=>\n  string(7) \"getpost\"\n}\narray(2) {\n  [\"id\"]=>\n  string(3) \"123\"\n  [\"name\"]=>\n  string(6) \"swoole\"\n}\n===DONE===\n"
  },
  {
    "path": "tests/swoole_curl/basic/23.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_setopt() function with CURLOPT_HEADER parameter set to 1\n--CREDITS--\nJean-Marc Fontaine <jmf@durcommefaire.net>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"{$host}/get.php?test=header_body\");\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HEADER, 1);\n    $result = curl_exec($ch);\n\n    $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);\n    $headerContent = substr($result, 0, $headerSize);\n    $body = substr($result, $headerSize);\n\n    Assert::assert(false !== strpos($headerContent, 'abc: 123'));\n\n    var_dump($body);\n\n    curl_close($ch);\n});\n\n?>\n===DONE===\n--EXPECTF--\nstring(5) \"a\nb\nc\"\n===DONE===\n"
  },
  {
    "path": "tests/swoole_curl/basic/24.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_opt() function with setting auto referer\n--CREDITS--\nSebastian Deutsch <sebastian.deutsch@9elements.com>\nTestFest 2009 - AFUP - Jean-Marc Fontaine <jmf@durcommefaire.net>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\n/* Prototype  : bool curl_setopt(resource ch, int option, mixed value)\n * Description: Set an option for a cURL transfer\n * Source code: ext/curl/interface.c\n * Alias to functions:\n */\n\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->disableNativeCurl();\n$cm->run(function ($host) {\n\n    // start testing\n    echo '*** Testing curl setting auto referer ***' . \"\\n\";\n\n    $url = \"{$host}/get.php?test=auto_referer\";\n    $ch = curl_init();\n\n    ob_start();\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);\n    curl_setopt($ch, CURLOPT_AUTOREFERER, true);\n    curl_setopt($ch, CURLOPT_URL, $url);\n\n    $curl_content = curl_exec($ch);\n\n    curl_close($ch);\n\n    Assert::assert(\"http://{$host}/get.php?test=auto_referer\" === $curl_content);\n});\n\n?>\n===DONE===\n--EXPECTF--\n*** Testing curl setting auto referer ***\n===DONE===\n"
  },
  {
    "path": "tests/swoole_curl/basic/25.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_multi_getcontent() function\n--CREDITS--\nSebastian Deutsch <sebastian.deutsch@9elements.com>\nTestFest 2009 - AFUP - Jean-Marc Fontaine <jmf@durcommefaire.net>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\n/* Prototype  : bool curl_setopt(resource ch, int option, mixed value)\n * Description: Set an option for a cURL transfer\n * Source code: ext/curl/interface.c\n * Alias to functions:\n */\nrequire __DIR__ . '/../../include/bootstrap.php';\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n    // start testing\n    echo '*** Testing curl method curl_multi_getcontent ***' . \"\\n\";\n    $url = \"{$host}/get.php?test=curl_multi_getcontent\";\n\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, $url);\n    curl_exec($ch);\n    Assert::null(curl_multi_getcontent($ch));\n    curl_close($ch);\n    echo PHP_EOL;\n\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, $url);\n    Assert::null(curl_multi_getcontent($ch));\n    curl_close($ch);\n\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_URL, $url);\n    $curl_content = curl_exec($ch);\n    Assert::same(curl_multi_getcontent($ch), $curl_content);\n    curl_close($ch);\n\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_URL, $url);\n    Assert::same(curl_multi_getcontent($ch), '');\n    curl_close($ch);\n});\n?>\n===DONE===\n--EXPECTF--\n*** Testing curl method curl_multi_getcontent ***\nHello World!\nHello World!\n===DONE===\n"
  },
  {
    "path": "tests/swoole_curl/basic/3.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_opt() function with POST parameters\n--CREDITS--\nSebastian Deutsch <sebastian.deutsch@9elements.com>\nTestFest 2009 - AFUP - Jean-Marc Fontaine <jmf@durcommefaire.net>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n\n    // start testing\n    echo '*** Testing curl sending through GET an POST ***' . \"\\n\";\n\n    $url = \"{$host}/get.php?test=getpost&get_param=Hello%20World\";\n    $ch = curl_init();\n\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_POST, 1);\n    curl_setopt($ch, CURLOPT_POSTFIELDS, \"Hello=World&Foo=Bar&Person=John%20Doe\");\n    curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use\n\n    $curl_content = curl_exec($ch);\n    curl_close($ch);\n\n    var_dump( $curl_content );\n});\n?>\n===DONE===\n--EXPECTF--\n*** Testing curl sending through GET an POST ***\nstring(208) \"array(2) {\n  [\"test\"]=>\n  string(7) \"getpost\"\n  [\"get_param\"]=>\n  string(11) \"Hello World\"\n}\narray(3) {\n  [\"Hello\"]=>\n  string(5) \"World\"\n  [\"Foo\"]=>\n  string(3) \"Bar\"\n  [\"Person\"]=>\n  string(8) \"John Doe\"\n}\n\"\n===DONE===\n"
  },
  {
    "path": "tests/swoole_curl/basic/4.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_opt() function with setting referer\n--CREDITS--\nSebastian Deutsch <sebastian.deutsch@9elements.com>\nTestFest 2009 - AFUP - Jean-Marc Fontaine <jmf@durcommefaire.net>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\n/* Prototype  : bool curl_setopt(resource ch, int option, mixed value)\n * Description: Set an option for a cURL transfer\n * Source code: ext/curl/interface.c\n * Alias to functions:\n */\n\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n\n    // start testing\n    echo '*** Testing curl setting referer ***' . \"\\n\";\n\n    $url = \"{$host}/get.php?test=referer\";\n    $ch = curl_init();\n\n    ob_start(); // start output buffering\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_REFERER, 'http://www.refer.er');\n    curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use\n\n    $curl_content = curl_exec($ch);\n    curl_close($ch);\n\n    var_dump($curl_content);\n});\n\n?>\n===DONE===\n--EXPECTF--\n*** Testing curl setting referer ***\nstring(19) \"http://www.refer.er\"\n===DONE===\n"
  },
  {
    "path": "tests/swoole_curl/basic/5.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_opt() function with user agent\n--CREDITS--\nSebastian Deutsch <sebastian.deutsch@9elements.com>\nTestFest 2009 - AFUP - Jean-Marc Fontaine <jmf@durcommefaire.net>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\n/* Prototype  : bool curl_setopt(resource ch, int option, mixed value)\n * Description: Set an option for a cURL transfer\n * Source code: ext/curl/interface.c\n * Alias to functions:\n */\n\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n    // start testing\n    echo '*** Testing curl with user agent ***' . \"\\n\";\n\n    $url = \"{$host}/get.php?test=useragent\";\n    $ch = curl_init();\n\n    ob_start(); // start output buffering\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_USERAGENT, 'cURL phpt');\n    curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use\n\n    $curl_content = curl_exec($ch);\n    curl_close($ch);\n\n    var_dump( $curl_content );\n\n});\n\n\n?>\n===DONE===\n--EXPECTF--\n*** Testing curl with user agent ***\nstring(9) \"cURL phpt\"\n===DONE===\n"
  },
  {
    "path": "tests/swoole_curl/basic/7.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_error() & curl_errno() function without url\n--CREDITS--\nTestFest 2009 - AFUP - Perrick Penet <perrick@noparking.net>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php if (!extension_loaded(\"curl\")) print \"skip\"; ?>\n--FILE--\n<?php\n\n//In January 2008 , level 7.18.0 of the curl lib, many of the messages changed.\n//The final crlf was removed. This test is coded to work with or without the crlf.\n\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n\n    $ch = curl_init();\n\n    curl_exec($ch);\n    Assert::contains(curl_error($ch), 'No URL set');\n    Assert::eq(curl_errno($ch), CURLE_URL_MALFORMAT);\n    curl_close($ch);\n\n    echo \"Done\\n\";\n}, false);\n\n?>\n--EXPECTF--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/basic/8.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_error() & curl_errno() function with problematic host\n--CREDITS--\nTestFest 2009 - AFUP - Perrick Penet <perrick@noparking.net>\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_extension_not_exist('curl');\n$addr = \"www.\" . uniqid() . \".\" . uniqid();\nif (gethostbyname($addr) != $addr) {\n    exit('skip catch all dns');\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n    $url = \"http://www.\" . uniqid() . \".\" . uniqid();\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, $url);\n    curl_exec($ch);\n    var_dump(curl_error($ch));\n    var_dump(curl_errno($ch));\n    curl_close($ch);\n}, false);\n?>\n--EXPECTF--\n%s resolve%s\nint(6)\n"
  },
  {
    "path": "tests/swoole_curl/basic/9.phpt",
    "content": "--TEST--\nswoole_curl/basic: Test curl_error() & curl_errno() function with problematic protocol\n--CREDITS--\nTestFest 2009 - AFUP - Perrick Penet <perrick@noparking.net>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php if (!extension_loaded(\"curl\")) print \"skip\"; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->disableNativeCurl();\n$cm->run(function ($host) {\n    $url = uniqid().\"://www.\".uniqid().\".\".uniqid();\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, $url);\n\n    curl_exec($ch);\n    var_dump(curl_error($ch));\n    var_dump(curl_errno($ch));\n    curl_close($ch);\n\n}, false);\n?>\n--EXPECTREGEX--\nstring\\(\\d+\\) \".+URL.+\"\nint\\(\\d\\)\n"
  },
  {
    "path": "tests/swoole_curl/cancel.phpt",
    "content": "--TEST--\nswoole_curl: sleep\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Runtime;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\n\nuse function Swoole\\Coroutine\\run;\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    $s = microtime(true);\n    run(function () use ($pm) {\n        $ch = curl_init();\n        $code = uniqid('swoole_');\n        $url = \"http://127.0.0.1:\".$pm->getFreePort().\"/?code=\".urlencode($code);\n\n        curl_setopt($ch, CURLOPT_URL, $url);\n        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n        curl_setopt($ch, CURLOPT_HEADER, 0);\n\n        $cid = Coroutine::getCid();\n        go(function () use ($cid) {\n            System::sleep(0.01);\n            Assert::true(Coroutine::cancel($cid));\n            Assert::false(Coroutine::isCanceled());\n        });\n\n        $output = curl_exec($ch);\n        Assert::isEmpty($output);\n        Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_CANCELED);\n        Assert::eq(curl_errno($ch), CURLE_ABORTED_BY_CALLBACK);\n        Assert::contains(curl_error($ch), 'Operation was aborted by an application callback');\n        curl_close($ch);\n    });\n    Assert::lessThan(microtime(true) - $s, 0.5);\n    $pm->kill();\n    echo \"Done\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n\n    $http->on(\"start\", function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Request $request, Response $response) {\n        usleep(300000);\n        $response->end(\"Hello World\\n\".$request->get['code']);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/close_before_resume.phpt",
    "content": "--TEST--\nswoole_curl: close before resume\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n$pm = new SwooleTest\\ProcessManager;\n\nconst N = 8;\n\n$pm->parentFunc = function () use ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    $s = microtime(true);\n    run(function () use ($pm) {\n        $ch = curl_init();\n        $code = uniqid('swoole_');\n        $url = \"http://127.0.0.1:\".$pm->getFreePort().\"/?code=\".urlencode($code);\n\n        curl_setopt($ch, CURLOPT_URL, $url);\n        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n        curl_setopt($ch, CURLOPT_HEADER, 0);\n        curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) {\n            return strlen($strHeader);\n        });\n\n        go(function() use ($ch) {\n            Co::sleep(0.1);\n            $res = curl_close($ch);\n            echo \"close [1]\\n\";\n            Assert::true($res);\n        });\n\n        $output = curl_exec($ch);\n        var_dump($output);\n\n        Assert::eq($output, \"Hello World\\n\".$code);\n        if ($output === false) {\n            echo \"CURL Error:\" . curl_error($ch);\n        }\n        curl_close($ch);\n        echo \"close [2]\\n\";\n    });\n    Assert::lessThan(microtime(true) - $s, 0.5);\n    $pm->kill();\n    echo \"Done\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set(['worker_num' => N, 'log_file' => '/dev/null']);\n\n    $http->on(\"start\", function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Request $request, Response $response) {\n        usleep(300000);\n        $response->end(\"Hello World\\n\".$request->get['code']);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nFatal error: Uncaught Swoole\\Error: This cURL handle is currently executing in coroutine#%d, cannot be operated in %s:%d\nStack trace:\n#0 %s(%d): curl_close(%s)\n%A\n  thrown in %s on line %d\n\n [Coroutine-%d] Stack trace:\n -------------------------------------------------------------------\n#0 %s(%d): curl_exec(Object(CurlHandle))\n#1 [internal function]: {%s}()\n"
  },
  {
    "path": "tests/swoole_curl/concurrent.phpt",
    "content": "--TEST--\nswoole_curl: Concurrent request\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n$pm = new SwooleTest\\ProcessManager;\n\nconst N = 8;\n\n$pm->parentFunc = function () use ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    $s = microtime(true);\n    run(function () use ($pm) {\n        $fn = function () use ($pm) {\n            $ch = curl_init();\n            $code = uniqid('swoole_');\n            $url = \"http://127.0.0.1:\".$pm->getFreePort().\"/?code=\".urlencode($code);\n\n            curl_setopt($ch, CURLOPT_URL, $url);\n            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n            curl_setopt($ch, CURLOPT_HEADER, 0);\n            curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) {\n                return strlen($strHeader);\n            });\n            $output = curl_exec($ch);\n            Assert::eq($output, \"Hello World\\n\".$code);\n            if ($output === false) {\n                echo \"CURL Error:\" . curl_error($ch);\n            }\n            curl_close($ch);\n            echo \"DONE\\n\";\n        };\n\n        $n = N;\n        while($n--) {\n            go($fn);\n        }\n    });\n    Assert::lessThan(microtime(true) - $s, 0.5);\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set(['worker_num' => N, 'log_file' => '/dev/null']);\n\n    $http->on(\"start\", function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Request $request, Response $response) {\n        usleep(rand(1000, 30000));\n        $response->end(\"Hello World\\n\".$request->get['code']);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\nDONE\nDONE\nDONE\nDONE\nDONE\nDONE\nDONE\n"
  },
  {
    "path": "tests/swoole_curl/coro_read.phpt",
    "content": "--TEST--\nswoole_curl: coroutine read\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Runtime;\nuse SwooleTest\\ProcessManager;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse function Swoole\\Coroutine\\run;\n\n$data = random_bytes(100 * 1024 * 1024);\nfile_put_contents('download.txt', $data);\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () use ($pm) {\n        $fileHandle = fopen('download.txt', 'r');\n\n        $url = $url = \"http://127.0.0.1:\" . $pm->getFreePort();\n        $ch = curl_init();\n        curl_setopt($ch, CURLOPT_URL, $url);\n        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n        curl_setopt($ch, CURLOPT_HEADER, 0);\n        curl_setopt($ch, CURLOPT_UPLOAD, true);\n        curl_setopt($ch, CURLOPT_INFILE, $fileHandle);\n        curl_setopt($ch, CURLOPT_INFILESIZE, filesize('download.txt'));\n        $result = curl_exec($ch);\n        curl_close($ch);\n        fclose($fileHandle);\n    });\n    $pm->kill();\n    echo \"Done\\n\";\n};\n\n$pm->childFunc = function () use ($pm, $data) {\n    $http = new Swoole\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set(['worker_num' => 2, 'log_file' => '/dev/null', 'package_max_length' => 110 * 1024 * 1024]);\n\n    $http->on(\"start\", function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Request $request, Response $response) use ($data) {\n        var_dump($request->getContent() == $data);\n        $response->end('Hello World');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nbool(true)\nDone\n--CLEAN--\n<?php unlink('download.txt'); ?>\n"
  },
  {
    "path": "tests/swoole_curl/coro_write.phpt",
    "content": "--TEST--\nswoole_curl: coroutine write\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Runtime;\nuse SwooleTest\\ProcessManager;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse function Swoole\\Coroutine\\run;\n\n$data = random_bytes(100 * 1024 * 1024);\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm, $data) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () use ($pm, $data) {\n        $fileHandle = fopen('download.txt', 'wb');\n\n        $url = $url = \"http://127.0.0.1:\" . $pm->getFreePort();\n        $ch = curl_init();\n        curl_setopt($ch, CURLOPT_URL, $url);\n        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n        curl_setopt($ch, CURLOPT_HEADER, 0);\n        curl_setopt($ch, CURLOPT_FILE, $fileHandle);\n        $result = curl_exec($ch);\n        curl_close($ch);\n        fclose($fileHandle);\n        Assert::true(file_get_contents('download.txt') == $data);\n    });\n    $pm->kill();\n    echo \"Done\\n\";\n};\n\n$pm->childFunc = function () use ($pm, $data) {\n    $http = new Swoole\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set(['worker_num' => 2, 'log_file' => '/dev/null', 'package_max_length' => 110 * 1024 * 1024]);\n\n    $http->on(\"start\", function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Request $request, Response $response) use ($data) {\n        $response->end($data);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nDone\n--CLEAN--\n<?php unlink('download.txt'); ?>\n"
  },
  {
    "path": "tests/swoole_curl/coro_write_header.phpt",
    "content": "--TEST--\nswoole_curl: coroutine write\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Runtime;\nuse SwooleTest\\ProcessManager;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () use ($pm) {\n        $fileHandle = fopen('download.txt', 'wb');\n\n        $url = $url = \"http://127.0.0.1:\" . $pm->getFreePort();\n        $ch = curl_init();\n        curl_setopt($ch, CURLOPT_URL, $url);\n        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n        curl_setopt($ch, CURLOPT_HEADER, 0);\n        curl_setopt($ch, CURLOPT_WRITEHEADER, $fileHandle);\n        $result = curl_exec($ch);\n        curl_close($ch);\n        fclose($fileHandle);\n        echo file_get_contents('download.txt');\n    });\n    $pm->kill();\n    echo \"Done\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set(['worker_num' => 2, 'log_file' => '/dev/null', 'package_max_length' => 110 * 1024 * 1024]);\n\n    $http->on(\"start\", function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Request $request, Response $response) {\n        $response->end('Hello World');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nHTTP/1.1 200 OK\nServer: swoole-http-server\nDate: %s\nConnection: keep-alive\nContent-Type: text/html\nContent-Length: 11\n\nDone\n--CLEAN--\n<?php unlink('download.txt'); ?>\n"
  },
  {
    "path": "tests/swoole_curl/error.phpt",
    "content": "--TEST--\nswoole_curl: error\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n$s = microtime(true);\nrun(function () {\n    $ch = curl_init();\n    $code = uniqid('swoole_');\n    $url = 'http://127.0.0.1:49494/?code=' . urlencode($code);\n\n    curl_setopt($ch, CURLOPT_URL, $url);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HEADER, 0);\n    curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) {\n        return strlen($strHeader);\n    });\n\n    $output = curl_exec($ch);\n    Assert::isEmpty($output);\n    Assert::eq(curl_errno($ch), CURLE_COULDNT_CONNECT);\n    Assert::eq(preg_match('#Failed to connect to 127.0.0.1 port \\d+#i', curl_error($ch)), 1);\n\n    $info = curl_getinfo($ch);\n    Assert::isArray($info);\n    Assert::eq($info['http_code'], 0);\n    curl_close($ch);\n});\necho \"Done\\n\";\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/event_exit.phpt",
    "content": "--TEST--\nswoole_curl: event exit\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nrun(function () {\n    go(function () {\n        Co::sleep(0.1);\n        Swoole\\Event::exit();\n    });\n\n    foreach ([6, 7, 8] as $os) {\n        foreach ([10, 11, 12] as $version) {\n            $ch = curl_init();\n            curl_setopt_array($ch, [\n                CURLOPT_URL => sprintf('https://rpm.nodesource.com/pub_%d.x/el/%d/x86_64/', $version, $os),\n                CURLOPT_RETURNTRANSFER => true\n            ]);\n            $response = curl_exec($ch);\n            curl_close($ch);\n        }\n    }\n});\necho \"Done\\n\";\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/exec_twice.phpt",
    "content": "--TEST--\nswoole_curl: exec twice\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n$pm = new SwooleTest\\ProcessManager;\n\nconst N = 8;\n\n$pm->parentFunc = function () use ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    $s = microtime(true);\n    run(function () use ($pm) {\n        $ch = curl_init();\n        $code = uniqid('swoole_');\n        $url = \"http://127.0.0.1:\".$pm->getFreePort().\"/?code=\".urlencode($code);\n\n        curl_setopt($ch, CURLOPT_URL, $url);\n        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n        curl_setopt($ch, CURLOPT_HEADER, 0);\n        curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) {\n            return strlen($strHeader);\n        });\n\n        go(function() use ($ch) {\n            Co::sleep(0.1);\n            echo \"co 2 exec\\n\";\n            var_dump(curl_exec($ch), curl_errno($ch));\n        });\n\n        echo \"co 1 exec\\n\";\n        $output = curl_exec($ch);\n        Assert::eq($output, \"Hello World\\n\".$code);\n        if ($output === false) {\n            echo \"CURL Error:\" . curl_error($ch);\n        }\n        curl_close($ch);\n        echo \"close [2]\\n\";\n    });\n    Assert::lessThan(microtime(true) - $s, 0.5);\n    $pm->kill();\n    echo \"Done\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set(['worker_num' => N, 'log_file' => '/dev/null']);\n\n    $http->on(\"start\", function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Request $request, Response $response) {\n        usleep(300000);\n        $response->end(\"Hello World\\n\".$request->get['code']);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nco 1 exec\nco 2 exec\n\nFatal error: Uncaught Swoole\\Error: This cURL handle is currently executing in coroutine#%d, cannot be operated in %s:%d\nStack trace:\n#0 %s(%d): curl_exec(%s)\n%A\n  thrown in %s on line %d\n\n [Coroutine-%d] Stack trace:\n -------------------------------------------------------------------\n#0 %s(%d): curl_exec(Object(CurlHandle))\n#1 [internal function]: {%s}()\n"
  },
  {
    "path": "tests/swoole_curl/fatal_error_in_callback.phpt",
    "content": "--TEST--\nswoole_curl: error\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n$s = microtime(true);\nrun(function () {\n    $ch = curl_init();\n    $code = uniqid('swoole_');\n    if (IS_IN_CI) {\n        $domain = 'www.google.com';\n    } else {\n        $domain = 'www.baidu.com';\n    }\n    $url = \"https://{$domain}/?code=\" . urlencode($code);\n\n    curl_setopt($ch, CURLOPT_URL, $url);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HEADER, 0);\n    curl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($ch, $strHeader) {\n        trigger_error(\"test\", E_USER_ERROR);\n        return strlen($strHeader);\n    });\n\n    register_shutdown_function(function () use ($ch) {\n        try {\n            curl_close($ch);\n        } catch (throwable $e) {\n            trigger_error($e->getMessage(), E_USER_WARNING);\n        }\n    });\n\n    curl_exec($ch);\n    echo \"Exec\\n\";\n    curl_close($ch);\n});\necho \"Done\\n\";\n?>\n--EXPECTF--\nFatal error: test in %s on line %d\n\nWarning: curl_close(): Attempt to close cURL handle from a callback in %s on line %d\n--EXPECTF_85--\nFatal error: test in %s on line %d\nStack trace:\n#0 %s(%d): trigger_error('test', %d)\n#1 [internal function]: {closure:{closure:%s:%d}:%d}(Object(CurlHandle), '%s...')\n#2 %s(%d): curl_exec(Object(CurlHandle))\n#3 [internal function]: {closure:%s:%d}()\n#4 {main}\n\nWarning: curl_close(): Attempt to close cURL handle from a callback in %s on line %d\n"
  },
  {
    "path": "tests/swoole_curl/ftp.phpt",
    "content": "--TEST--\nswoole_curl: ftp\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_ftp();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nrun(function () {\n    $fileName = __DIR__ . '/upload/curl_testdata1.txt';\n    $fp = fopen($fileName, 'r');\n    $ftpUrl = build_ftp_url('1.txt');\n\n    // upload\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, $ftpUrl);\n    curl_setopt($ch, CURLOPT_UPLOAD, true);\n    curl_setopt($ch, CURLOPT_INFILE, $fp);\n    curl_setopt($ch, CURLOPT_INFILESIZE, filesize($fileName));\n    Assert::true(curl_exec($ch));\n    Assert::eq(curl_errno($ch), 0);\n    Assert::eq(curl_error($ch), '');\n    curl_close($ch);\n    fclose($fp);\n\n    // download\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, $ftpUrl);\n    curl_setopt($ch, \\CURLOPT_RETURNTRANSFER, 1);\n    Assert::eq(curl_exec($ch), file_get_contents($fileName));\n    Assert::eq(curl_errno($ch), 0);\n    Assert::eq(curl_error($ch), '');\n    curl_close($ch);\n});\necho \"Done\\n\";\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/guzzle/cancel.phpt",
    "content": "--TEST--\nswoole_curl/guzzle: cancel\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_LIB_PATH . '/vendor/autoload.php';\n\nuse Swoole\\Runtime;\nuse GuzzleHttp\\Handler\\CurlMultiHandler;\nuse GuzzleHttp\\Promise;\nuse GuzzleHttp\\Psr7\\Request;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nrun(function () {\n    $handler = new CurlMultiHandler();\n    $responses = [];\n    for ($i = 0; $i < 10; $i++) {\n        $response = $handler(new Request('GET', 'http://httpbin.org/get'), []);\n        $response->cancel();\n        $responses[] = $response;\n    }\n    foreach ($responses as $r) {\n        Assert::true(Promise\\Is::rejected($r));\n    }\n    echo 'Done' . PHP_EOL;\n});\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/guzzle/cannot_cancel_finished.phpt",
    "content": "--TEST--\nswoole_curl/guzzle: cannot_cancel_finished\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_LIB_PATH . '/vendor/autoload.php';\n\nuse Swoole\\Runtime;\nuse GuzzleHttp\\Handler\\CurlMultiHandler;\nuse GuzzleHttp\\Promise;\nuse GuzzleHttp\\Psr7\\Request;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nrun(function () {\n    $handler = new CurlMultiHandler();\n    $response = $handler(new Request('GET', 'http://httpbin.org/get'), []);\n    $response->wait();\n    $response->cancel();\n    Assert::true(Promise\\Is::fulfilled($response));\n    echo 'Done' . PHP_EOL;\n});\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/guzzle/cookie.phpt",
    "content": "--TEST--\nswoole_curl/guzzle: cookie\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_LIB_PATH . '/vendor/autoload.php';\n\nuse Swoole\\Runtime;\nuse GuzzleHttp\\Client;\nuse GuzzleHttp\\Cookie\\CookieJar;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nrun(function () {\n    $client = new Client();\n    $jar = CookieJar::fromArray(\n        [\n            'some_cookie' => 'foo',\n            'other_cookie' => 'barbaz1234'\n        ],\n        'httpbin.org'\n    );\n    $r = $client->request('GET', 'http://httpbin.org/cookies', [\n        'cookies' => $jar\n    ]);\n    Assert::eq($r->getStatusCode(), 200);\n    Assert::eq(json_decode($r->getBody()->getContents(), true)['cookies']['some_cookie'], 'foo');\n    echo 'Done' . PHP_EOL;\n});\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/guzzle/promise.phpt",
    "content": "--TEST--\nswoole_curl/guzzle: promise\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_LIB_PATH . '/vendor/autoload.php';\n\nuse Swoole\\Runtime;\nuse GuzzleHttp\\Client;\nuse GuzzleHttp\\Promise;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nrun(function () {\n    $client = new Client(['base_uri' => 'https://httpbin.org']);\n\n    // Initiate each request but do not block\n    $promises = [\n        'a' => $client->requestAsync('POST', '/post', ['json' => ['data' => 'hello test1!']]),\n        'b'   => $client->requestAsync('POST', '/post', ['json' => ['data' => 'hello test2!']]),\n        'c'  => $client->requestAsync('POST', '/post', ['json' => ['data' => 'hello test3!']]),\n    ];\n\n    // Wait on all of the requests to complete.\n    $results = GuzzleHttp\\Promise\\Utils::unwrap($promises);\n\n    // You can access each result using the key provided to the unwrap\n    // function.\n    echo json_decode($results['a']->getBody()->getContents())->data . PHP_EOL;\n    echo $results['b']->getHeaderLine('Content-Type') . PHP_EOL;\n    echo 'Done' . PHP_EOL;\n});\n?>\n--EXPECT--\n{\"data\":\"hello test1!\"}\napplication/json\nDone\n"
  },
  {
    "path": "tests/swoole_curl/guzzle/request_async.phpt",
    "content": "--TEST--\nswoole_curl/guzzle: request async\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_LIB_PATH . '/vendor/autoload.php';\n\nuse Swoole\\Runtime;\nuse GuzzleHttp\\Client;\nuse Psr\\Http\\Message\\ResponseInterface;\nuse GuzzleHttp\\Exception\\RequestException;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nrun(function () {\n    $client = new Client();\n    $promise = $client->requestAsync('GET', 'http://httpbin.org/get');\n    $promise->then(\n        function (ResponseInterface $res) {\n            echo $res->getStatusCode() .PHP_EOL;\n        },\n        function (RequestException $e) {\n            echo $e->getMessage() . PHP_EOL;\n            echo $e->getRequest()->getMethod() . PHP_EOL;\n        }\n    );\n    $promise->wait();\n    echo 'Done' . PHP_EOL;\n});\n?>\n--EXPECT--\n200\nDone\n"
  },
  {
    "path": "tests/swoole_curl/guzzle/request_on_stats.phpt",
    "content": "--TEST--\nswoole_curl/guzzle: request on_stats\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_LIB_PATH . '/vendor/autoload.php';\n\nuse Swoole\\Runtime;\nuse GuzzleHttp\\Client;\nuse GuzzleHttp\\TransferStats;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nrun(function () {\n    $client = new Client();\n    $host = 'http://httpbin.org/stream/1024';\n    $client->request('GET', $host, [\n        'on_stats' => function (TransferStats $stats) use ($host) {\n            Assert::eq($stats->getEffectiveUri(), $host);\n\n            // You must check if a response was received before using the\n            // response object.\n            if ($stats->hasResponse()) {\n                Assert::eq($stats->getResponse()->getStatusCode(), 200);\n            } else {\n                // Error data is handler specific. You will need to know what\n                // type of error data your handler uses before using this\n                // value.\n                var_dump($stats->getHandlerErrorData());\n            }\n        }\n    ]);\n    echo 'Done' . PHP_EOL;\n});\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/guzzle/send_async.phpt",
    "content": "--TEST--\nswoole_curl/guzzle: send async\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_LIB_PATH . '/vendor/autoload.php';\n\nuse Swoole\\Runtime;\nuse GuzzleHttp\\Client;\nuse GuzzleHttp\\Psr7\\Request;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nrun(function () {\n    $client = new Client();\n    $response = $client->request('GET', 'https://httpbin.org');\n\n    echo $response->getStatusCode(), PHP_EOL; // 200\n    echo $response->getHeaderLine('content-type'), PHP_EOL;\n\n    // Send an asynchronous request.\n    $request = new Request('GET', 'http://httpbin.org');\n    $promise = $client->sendAsync($request)->then(function ($response) {\n        echo 'I completed! ' . $response->getStatusCode() . PHP_EOL;\n    });\n\n    $promise->wait();\n    echo 'Done' . PHP_EOL;\n});\n?>\n--EXPECT--\n200\ntext/html; charset=utf-8\nI completed! 200\nDone\n"
  },
  {
    "path": "tests/swoole_curl/guzzle.phpt",
    "content": "--TEST--\nswoole_curl: guzzle\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire_once TESTS_LIB_PATH . '/vendor/autoload.php';\n\nuse Swoole\\Coroutine\\Barrier;\nuse Swoole\\Runtime;\nuse GuzzleHttp\\Client;\nuse GuzzleHttp\\Promise;\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nregister_shutdown_function(function (){\n   phpt_show_usage();\n});\n\nconst N = 4;\n\nrun(function () {\n    $barrier = Barrier::make();\n    $result = [];\n    go(function () use ($barrier, &$result) {\n        $client = new Client();\n        $promises = [\n            'baidu' => $client->getAsync('http://www.baidu.com/'),\n            'qq' => $client->getAsync('https://www.qq.com/'),\n            'zhihu' => $client->getAsync('http://www.zhihu.com/')\n        ];\n        $responses = Promise\\Utils::unwrap($promises);\n        Assert::contains($responses['baidu']->getBody(), '百度');\n        Assert::contains($responses['qq']->getBody(), '腾讯');\n        Assert::contains($responses['zhihu']->getBody(), '知乎');\n        $result['task_1'] = 'OK';\n    });\n\n    go(function () use ($barrier, &$result) {\n        $client = new Client(['base_uri' => 'https://httpbin.org/']);\n        $n = N;\n        $data = $promises = [];\n        while ($n--) {\n            $key = 'req_' . $n;\n            $data[$key] = uniqid('swoole_test');\n            $promises[$key] = $client->getAsync('/base64/' . base64_encode($data[$key]));\n        }\n        $responses = Promise\\Utils::unwrap($promises);\n\n        $n = N;\n        while ($n--) {\n            $key = 'req_' . $n;\n            Assert::eq($responses[$key]->getBody(), $data[$key]);\n        }\n        $result['task_2'] = 'OK';\n    });\n\n    Barrier::wait($barrier);\n    Assert::eq($result['task_1'], 'OK');\n    Assert::eq($result['task_2'], 'OK');\n    echo 'Done' . PHP_EOL;\n});\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/https.phpt",
    "content": "--TEST--\nswoole_curl: https\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nconst N = 8;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n$s = microtime(true);\nrun(function () {\n    $n = N;\n    while($n--) {\n        go(function() {\n            $ch = curl_init();\n            $code = uniqid('swoole_');\n            if (IS_IN_CI) {\n                $domain = 'www.google.com';\n            } else {\n                $domain = 'www.baidu.com';\n            }\n            $url = \"https://{$domain}/?code=\".urlencode($code);\n\n            curl_setopt($ch, CURLOPT_URL, $url);\n            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n            curl_setopt($ch, CURLOPT_HEADER, 0);\n            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT , 2);\n            curl_setopt($ch, CURLOPT_TIMEOUT, 5);\n            curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) {\n                return strlen($strHeader);\n            });\n\n            $output = curl_exec($ch);\n            if ($output === false) {\n                echo \"CURL Error:\" . curl_error($ch);\n            }\n            Assert::notEmpty($output);\n            Assert::greaterThan(strlen($output), 8192);\n            curl_close($ch);\n        });\n    }\n});\necho \"Done\\n\";\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/keepalive.phpt",
    "content": "--TEST--\nswoole_curl: keepalive\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () use ($pm) {\n        $ch = curl_init();\n        $code = uniqid('swoole_');\n\n        $execFn = function () use ($ch, $code, $pm) {\n            $url = \"http://127.0.0.1:\" . $pm->getFreePort() . \"/?code=\" . urlencode($code);\n            curl_setopt($ch, CURLOPT_URL, $url);\n            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n            curl_setopt($ch, CURLOPT_HEADER, 0);\n            curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) {\n                return strlen($strHeader);\n            });\n            $output = curl_exec($ch);\n            $info = curl_getinfo($ch);\n            Assert::eq($output, \"Hello World\\n\" . $code);\n            if ($output === false) {\n                exit(\"CURL Error:\" . curl_error($ch));\n            }\n            return $info;\n        };\n\n        echo \"co 1 exec\\n\";\n        $info1 = $execFn();\n\n        Co::sleep(0.1);\n\n        echo \"co 2 exec\\n\";\n        $info2 = $execFn();\n\n        Assert::eq($info1['local_port'], $info2['local_port']);\n\n        curl_close($ch);\n    });\n    $pm->kill();\n    echo \"Done\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set(['worker_num' => 2, 'log_file' => '/dev/null']);\n\n    $http->on(\"start\", function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Request $request, Response $response) {\n        $response->end(\"Hello World\\n\" . $request->get['code']);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nco 1 exec\nco 2 exec\nDone\n"
  },
  {
    "path": "tests/swoole_curl/multi/1.phpt",
    "content": "--TEST--\nswoole_curl/multi: 1\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_API_PATH.'/curl_multi.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () {\n    swoole_test_curl_multi();\n    echo \"Done\\n\";\n});\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/multi/2.phpt",
    "content": "--TEST--\nswoole_curl/multi: 2\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_API_PATH.'/curl_multi.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () {\n    swoole_test_curl_multi(['sleep' => 0.2]);\n    echo \"Done\\n\";\n});\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/multi/3.phpt",
    "content": "--TEST--\nswoole_curl/multi: 3\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_API_PATH . '/curl_multi.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () {\n    $n = 4;\n    while ($n--) {\n        go(function () {\n            swoole_test_curl_multi();\n            echo \"Done\\n\";\n        });\n    }\n});\n?>\n--EXPECT--\nDone\nDone\nDone\nDone\n"
  },
  {
    "path": "tests/swoole_curl/multi/4.phpt",
    "content": "--TEST--\nswoole_curl/multi: 4\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_API_PATH.'/curl_multi.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () {\n    $n = 4;\n    while ($n--) {\n        go(function () {\n            swoole_test_curl_multi(['sleep' => 0.2]);\n            echo \"Done\\n\";\n        });\n    }\n});\n?>\n--EXPECT--\nDone\nDone\nDone\nDone\n"
  },
  {
    "path": "tests/swoole_curl/multi/5.phpt",
    "content": "--TEST--\nswoole_curl/multi: 5\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_API_PATH . '/curl_multi.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_ALL);\nrun(function () {\n    $ch1 = curl_init();\n    curl_setopt($ch1, CURLOPT_URL, TEST_DOMAIN_1);\n    curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1);\n\n    $ch2 = curl_init();\n    curl_setopt($ch2, CURLOPT_URL, TEST_DOMAIN_2);\n    curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1);\n\n    $mh = curl_multi_init();\n    curl_multi_add_handle($mh, $ch1);\n    curl_multi_add_handle($mh, $ch2);\n\n    do {\n        $mrc = curl_multi_exec($mh, $active);\n    } while ($mrc == CURLM_CALL_MULTI_PERFORM);\n\n    while ($active && $mrc == CURLM_OK) {\n        Assert::true(curl_multi_select($mh) != -1);\n        do {\n            $mrc = curl_multi_exec($mh, $active);\n        } while ($mrc == CURLM_CALL_MULTI_PERFORM);\n    }\n    echo \"DONE\\n\";\n});\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_curl/multi/6.phpt",
    "content": "--TEST--\nswoole_curl/multi: 6\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_API_PATH . '/curl_multi.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_ALL);\nrun(function () {\n    $ch = curl_init();\n\n    curl_setopt($ch, CURLOPT_URL, 'https://' . TEST_DOMAIN_1);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HEADERFUNCTION, static function (CurlHandle $curl, string $headerLine): int {\n        throw new Exception('testh');\n    });\n\n    Assert::throws(static function () use ($ch): void {\n        @curl_exec($ch);\n    }, Exception::class, message: 'testh');\n\n    $ch = curl_init();\n\n    curl_setopt($ch, CURLOPT_URL, 'https://' . TEST_DOMAIN_2);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_WRITEFUNCTION, static function (CurlHandle $curl, string $data): int {\n        throw new Exception('testw');\n    });\n\n    Assert::throws(static function () use ($ch): void {\n        curl_exec($ch);\n    }, Exception::class, message: 'testw');\n\n    $ch = curl_init();\n\n    curl_setopt($ch, CURLOPT_URL, 'https://' . TEST_DOMAIN_1);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_UPLOAD, 1);\n    curl_setopt($ch, CURLOPT_INFILE, STDIN);\n    curl_setopt($ch, CURLOPT_READFUNCTION, static function (CurlHandle $curl, mixed $fd, int $length): int {\n        throw new Exception('testr');\n    });\n\n    Assert::throws(static function () use ($ch): void {\n        curl_exec($ch);\n    }, Exception::class, message: 'testr');\n\n    echo \"Done\\n\";\n});\n?>\n--EXPECTF--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/multi/add_after_easy_exec.phpt",
    "content": "--TEST--\nswoole_curl/multi: add handle after easy exec\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded(\"curl\")) exit(\"skip curl extension not loaded\");\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nuse SwooleTest\\CurlManager;\n\n$cm = new CurlManager();\n$cm->run(function ($host) {\n    $ch1 = curl_init();\n    curl_setopt($ch1, CURLOPT_URL, \"{$host}/get.php?test=get\");\n    curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1);\n    $rs = curl_exec($ch1);\n    Assert::eq($rs, \"Hello World!\\nHello World!\");\n\n    $mh1 = curl_multi_init();\n    Assert::eq(curl_multi_add_handle($mh1, $ch1), 0);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_curl/multi/bug4393.phpt",
    "content": "--TEST--\nswoole_curl/multi: guzzle\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_LIB_PATH . '/vendor/autoload.php';\n\nuse Swoole\\Runtime;\nuse GuzzleHttp\\Client;\nuse GuzzleHttp\\Promise;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nrun(function () {\n    $guzzle = new Client();\n\n    $test = function () use ($guzzle) {\n        if (IS_IN_CI) {\n            $promises = [\n                'qq' => $guzzle->getAsync('https://www.qq.com/'),\n                'baidu' => $guzzle->getAsync('http://www.baidu.com/'),\n            ];\n        } else {\n            $promises = [\n                'httpbin' => $guzzle->getAsync('https://www.httpbin.org/'),\n                'nghttp2' => $guzzle->getAsync('https://nghttp2.org/'),\n            ];\n        }\n\n        $responses = [];\n        foreach (Promise\\Utils::settle($promises)->wait() as $k => $v) {\n            $responses[$k] = $v['value'];\n        }\n\n        if (IS_IN_CI) {\n            Assert::contains($responses['baidu']->getBody(), '百度');\n            Assert::contains($responses['qq']->getBody(), '腾讯');\n        } else {\n            Assert::contains($responses['httpbin']->getBody(), 'httpbin');\n            Assert::contains($responses['nghttp2']->getBody(), 'nghttp2');\n        }\n    };\n\n    $n = 2;\n    while ($n--) {\n        $s = microtime(true);\n        $test();\n        Assert::lessThan(microtime(true) - $s, 3.0);\n    }\n\n    echo 'Done' . PHP_EOL;\n});\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/multi/bug48203_multi.phpt",
    "content": "--TEST--\nswoole_curl/multi: Variation of bug #48203 with curl_multi_exec (Crash when file pointers passed to curl are closed before calling curl_multi_exec)\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse SwooleTest\\CurlManager;\n\nfunction checkForClosedFilePointer($target_url, $curl_option, $description) {\n    $fp = fopen(__DIR__ . '/bug48203.tmp', 'w');\n\n    $ch1 = curl_init();\n    $ch2 = curl_init();\n\n    $options = array(\n        CURLOPT_RETURNTRANSFER => 1,\n        $curl_option => $fp,\n        CURLOPT_URL => $target_url,\n    );\n\n    // we also need to set CURLOPT_VERBOSE to test CURLOPT_STDERR properly\n    if (CURLOPT_STDERR == $curl_option) {\n        $options[CURLOPT_VERBOSE] = 1;\n    }\n\n    if (CURLOPT_INFILE == $curl_option) {\n        $options[CURLOPT_UPLOAD] = 1;\n    }\n\n    curl_setopt_array($ch1, $options);\n    curl_setopt_array($ch2, $options);\n\n    fclose($fp); // <-- premature close of $fp caused a crash!\n\n    $mh = curl_multi_init();\n\n    curl_multi_add_handle($mh, $ch1);\n    curl_multi_add_handle($mh, $ch2);\n\n    $active = 0;\n    do {\n        curl_multi_exec($mh, $active);\n    } while ($active > 0);\n\n    curl_multi_remove_handle($mh, $ch1);\n    curl_multi_remove_handle($mh, $ch2);\n    curl_multi_close($mh);\n\n    // Force curl to output results\n    fflush(STDERR);\n    fflush(STDOUT);\n\n    echo \"Ok for $description\\n\";\n}\n\n$cm = new CurlManager();\n$cm->run(function ($host) {\n    $url = \"{$host}/\";\n\n    $options_to_check = array(\n        \"CURLOPT_STDERR\", \"CURLOPT_WRITEHEADER\", \"CURLOPT_FILE\", \"CURLOPT_INFILE\"\n    );\n\n    foreach($options_to_check as $option) {\n        checkForClosedFilePointer($url, constant($option), $option);\n    }\n});\n?>\n--CLEAN--\n<?php @unlink(__DIR__ . '/bug48203.tmp'); ?>\n--EXPECTF--\nWarning: curl_multi_add_handle(): CURLOPT_STDERR resource has gone away, resetting to stderr in %s on line %d\n%A\nWarning: curl_multi_add_handle(): CURLOPT_STDERR resource has gone away, resetting to stderr in %s on line %d\n%A\nOk for CURLOPT_STDERR\n\nWarning: curl_multi_add_handle(): CURLOPT_WRITEHEADER resource has gone away, resetting to default in %s on line %d\n\nWarning: curl_multi_add_handle(): CURLOPT_WRITEHEADER resource has gone away, resetting to default in %s on line %d\nOk for CURLOPT_WRITEHEADER\n\nWarning: curl_multi_add_handle(): CURLOPT_FILE resource has gone away, resetting to default in %s on line %d\n\nWarning: curl_multi_add_handle(): CURLOPT_FILE resource has gone away, resetting to default in %s on line %d\n%AOk for CURLOPT_FILE\n\nWarning: curl_multi_add_handle(): CURLOPT_INFILE resource has gone away, resetting to default in %s on line %d\n\nWarning: curl_multi_add_handle(): CURLOPT_INFILE resource has gone away, resetting to default in %s on line %d\nOk for CURLOPT_INFILE\n"
  },
  {
    "path": "tests/swoole_curl/multi/bug67643.phpt",
    "content": "--TEST--\nswoole_curl/multi: Bug #67643 (curl_multi_getcontent returns '' when RETURNTRANSFER isn't set)\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('curl')) print 'skip';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () {\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, 'file://'. __FILE__);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n\n    $mh = curl_multi_init();\n    curl_multi_add_handle($mh, $ch);\n\n    $running = 0;\n    do {\n        curl_multi_exec($mh, $running);\n    } while($running > 0);\n\n    $results = curl_multi_getcontent($ch);\n\n    curl_multi_remove_handle($mh, $ch);\n    curl_multi_close($mh);\n\n    Assert::contains($results, 'Bug #67643');\n    echo 'Done'.PHP_EOL;\n});\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/multi/bug71523.phpt",
    "content": "--TEST--\nswoole_curl/multi: Bug #71523 (Copied handle with new option CURLOPT_HTTPHEADER crashes while curl_multi_exec)\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded(\"curl\")) {\n    exit(\"skip curl extension not loaded\");\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () {\n    $base = curl_init('http://www.baidu.com/');\n    curl_setopt($base, CURLOPT_RETURNTRANSFER, true);\n    $mh = curl_multi_init();\n\n    for ($i = 0; $i < 2; ++$i) {\n        $ch = curl_copy_handle($base);\n        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Foo: Bar']);\n        curl_multi_add_handle($mh, $ch);\n    }\n\n    do {\n        curl_multi_exec($mh, $active);\n    } while ($active);\n});\n?>\nokey\n--EXPECT--\nokey\n"
  },
  {
    "path": "tests/swoole_curl/multi/bug76675.phpt",
    "content": "--TEST--\nswoole_curl/multi: Bug #76675 (Segfault with H2 server push write/writeheader handlers)\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_no_database();\nif (getenv('SKIP_ONLINE_TESTS')) {\n    exit('skip online test');\n}\n$curl_version = curl_version();\nif ($curl_version['version_number'] < 0x080100) {\n    exit(\"skip: test may crash with curl < 8.1.0\\n\");\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\n$fn = function () {\n    $transfers = 1;\n    $callback = function ($parent, $passed) use (&$transfers) {\n        curl_setopt($passed, CURLOPT_WRITEFUNCTION, function ($ch, $data) {\n            echo 'Received ' . strlen($data);\n            return strlen($data);\n        });\n        $transfers++;\n        return CURL_PUSH_OK;\n    };\n    $mh = curl_multi_init();\n    curl_multi_setopt($mh, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);\n    curl_multi_setopt($mh, CURLMOPT_PUSHFUNCTION, $callback);\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, TEST_HTTP2_SERVERPUSH_URL);\n    curl_setopt($ch, CURLOPT_HTTP_VERSION, 3);\n    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);\n    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_multi_add_handle($mh, $ch);\n    $active = null;\n    do {\n        $status = curl_multi_exec($mh, $active);\n        phpt_echo(\"active={$active}, status={$status}\\n\");\n        do {\n            $info = curl_multi_info_read($mh);\n            phpt_echo($info);\n            if ($info !== false && $info['msg'] == CURLMSG_DONE) {\n                $handle = $info['handle'];\n                if ($handle !== null) {\n                    $transfers--;\n                    curl_multi_remove_handle($mh, $handle);\n                    curl_close($handle);\n                }\n            }\n        } while ($info);\n        curl_multi_select($mh);\n    } while ($transfers);\n    curl_multi_close($mh);\n};\n\nif (swoole_array_default_value($argv, 1) == 'ori') {\n    $fn();\n} else {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run($fn);\n}\n?>\n--EXPECTREGEX--\n(Received \\d+)+\n"
  },
  {
    "path": "tests/swoole_curl/multi/bug77535.phpt",
    "content": "--TEST--\nswoole_curl/multi: Bug #77535 (Invalid callback, h2 server push)\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php\nif (getenv(\"SKIP_ONLINE_TESTS\")) {\n    die(\"skip online test\");\n}\n$curl_version = curl_version();\nif ($curl_version['version_number'] < 0x073d00) {\n    exit(\"skip: test may crash with curl < 7.61.0\");\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nclass MyHttpClient\n{\n    private $mh;\n    private $curl;\n\n    public function sendRequest()\n    {\n        echo __METHOD__.'[1]'.PHP_EOL;\n        if (false === $this->mh = curl_multi_init()) {\n            throw new \\RuntimeException('Unable to create a new cURL multi handle');\n        }\n\n        $this->addServerPushCallback();\n        echo __METHOD__.'[2]'.PHP_EOL;\n\n        $this->curl = curl_init();\n        curl_setopt($this->curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);\n        curl_setopt($this->curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);\n        curl_setopt($this->curl, CURLOPT_HEADER, false);\n        curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, false);\n        curl_setopt($this->curl, CURLOPT_FAILONERROR, false);\n        curl_setopt($this->curl, CURLOPT_URL, TEST_HTTP2_SERVERPUSH_URL);\n        curl_setopt($this->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);\n        curl_setopt($this->curl, CURLOPT_HEADERFUNCTION, function ($ch, $data) {\n            return \\strlen($data);\n        });\n        curl_setopt($this->curl, CURLOPT_WRITEFUNCTION, function ($ch, $data) {\n            return \\strlen($data);\n        });\n        curl_multi_add_handle($this->mh, $this->curl);\n        echo __METHOD__.'[3]'.PHP_EOL;\n\n        $stillRunning = null;\n        while (true) {\n            do {\n                $mrc = curl_multi_exec($this->mh, $stillRunning);\n            } while (CURLM_CALL_MULTI_PERFORM === $mrc);\n\n            $info = curl_multi_info_read($this->mh);\n            while (false !== $info && $info['msg'] == CURLMSG_DONE) {\n                if (CURLMSG_DONE !== $info['msg']) {\n                    continue;\n                }\n                echo \"Start handle request.\\n\";\n                return;\n            }\n        }\n    }\n\n    private function addServerPushCallback(): void\n    {\n        echo __METHOD__.PHP_EOL;\n        $callback = static function () {\n            return CURL_PUSH_OK;\n        };\n\n        curl_multi_setopt($this->mh, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);\n        curl_multi_setopt($this->mh, CURLMOPT_PUSHFUNCTION, $callback);\n    }\n}\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nrun(function () {\n    $buzz = new MyHttpClient();\n    $buzz->sendRequest();\n    $buzz = null;\n});\n?>\n--EXPECT--\nMyHttpClient::sendRequest[1]\nMyHttpClient::addServerPushCallback\nMyHttpClient::sendRequest[2]\nMyHttpClient::sendRequest[3]\nStart handle request.\n"
  },
  {
    "path": "tests/swoole_curl/multi/bug77946.phpt",
    "content": "--TEST--\nswoole_curl/multi: Bug #77946 (Errored cURL resources returned by curl_multi_info_read() must be compatible with curl_errno() and curl_error())\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php\n\nif (!extension_loaded('curl')) {\n    exit('skip curl extension not loaded');\n}\n\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nrun(function () {\n    $urls = array(\n        'unknown://scheme.tld',\n    );\n\n    $mh = curl_multi_init();\n\n    foreach ($urls as $i => $url) {\n        $conn[$i] = curl_init($url);\n        curl_multi_add_handle($mh, $conn[$i]);\n    }\n\n    do {\n        $status = curl_multi_exec($mh, $active);\n        $info = curl_multi_info_read($mh);\n        if (false !== $info) {\n            Assert::eq($info['result'], 1);\n            Assert::eq(curl_errno($info['handle']), CURLE_UNSUPPORTED_PROTOCOL);\n            Assert::contains(curl_error($info['handle']), 'Protocol \"unknown\" not supported');\n        }\n    } while ($status === CURLM_CALL_MULTI_PERFORM || $active);\n\n    foreach ($urls as $i => $url) {\n        curl_close($conn[$i]);\n    }\n\n    curl_multi_close($mh);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_curl/multi/close.phpt",
    "content": "--TEST--\nswoole_curl/multi: clean handle\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_API_PATH.'/curl_multi.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () {\n    $ch1 = curl_init();\n    curl_setopt($ch1, CURLOPT_URL, \"http://www.baidu.com/\");\n    curl_setopt($ch1, CURLOPT_HEADER, 0);\n    curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1);\n\n    $mh = curl_multi_init();\n    curl_multi_add_handle($mh, $ch1);\n    curl_multi_close($mh);\n\n    echo \"Done\\n\";\n});\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/multi/curl_basic_018.phpt",
    "content": "--TEST--\nswoole_curl/multi: Test curl_setopt() with curl_multi function with basic functionality\n--CREDITS--\nTestFest 2009 - AFUP - Thomas Rabaix <thomas.rabaix@gmail.com>\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse SwooleTest\\CurlManager;\n$cm = new CurlManager();\n$cm->run(function ($host) {\n    // start testing\n    echo \"*** Testing curl_exec() : basic functionality ***\\n\";\n\n    $url = \"{$host}/get.inc?test=get\";\n    $chs = array(\n        0 => curl_init(),\n        1 => curl_init(),\n        2 => curl_init(),\n    );\n\n    ob_start(); // start output buffering\n\n    $options = array(\n        CURLOPT_RETURNTRANSFER => 1,\n        CURLOPT_URL => $url,\n    );\n\n    curl_setopt_array($chs[0], $options); //set the options\n    curl_setopt_array($chs[1], $options); //set the options\n    curl_setopt_array($chs[2], $options); //set the options\n\n    $mh = curl_multi_init();\n\n    // add handlers\n    curl_multi_add_handle($mh, $chs[0]);\n    curl_multi_add_handle($mh, $chs[1]);\n    curl_multi_add_handle($mh, $chs[2]);\n\n    $running=null;\n    //execute the handles\n    do {\n        curl_multi_exec($mh, $running);\n    } while ($running > 0);\n\n    $curl_content = '';\n    $curl_content .= curl_multi_getcontent($chs[0]);\n    $curl_content .= curl_multi_getcontent($chs[1]);\n    $curl_content .= curl_multi_getcontent($chs[2]);\n\n    //close the handles\n    curl_multi_remove_handle($mh, $chs[0]);\n    curl_multi_remove_handle($mh, $chs[1]);\n    curl_multi_remove_handle($mh, $chs[2]);\n    curl_multi_close($mh);\n\n    var_dump( $curl_content );\n});\n?>\n--EXPECT--\n*** Testing curl_exec() : basic functionality ***\nstring(75) \"Hello World!\nHello World!Hello World!\nHello World!Hello World!\nHello World!\"\n"
  },
  {
    "path": "tests/swoole_curl/multi/curl_copy_handle_variation4.phpt",
    "content": "--TEST--\nswoole_curl/multi: curl_copy_handle() allows to post CURLFile multiple times with curl_multi_exec()\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse SwooleTest\\CurlManager;\n$cm = new CurlManager();\n$cm->run(function ($host) {\n    $ch1 = curl_init();\n    curl_setopt($ch1, CURLOPT_SAFE_UPLOAD, 1);\n    curl_setopt($ch1, CURLOPT_URL, \"{$host}/get.php?test=file\");\n    // curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1);\n\n    $filename = __DIR__ . '/АБВ.txt';\n    file_put_contents($filename, \"Test.\");\n    $file = curl_file_create($filename);\n    $params = array('file' => $file);\n    var_dump(curl_setopt($ch1, CURLOPT_POSTFIELDS, $params));\n\n    $ch2 = curl_copy_handle($ch1);\n    $ch3 = curl_copy_handle($ch1);\n\n    $mh = curl_multi_init();\n    curl_multi_add_handle($mh, $ch1);\n    curl_multi_add_handle($mh, $ch2);\n    do {\n        $status = curl_multi_exec($mh, $active);\n        if ($active) {\n            curl_multi_select($mh);\n        }\n    } while ($active && $status == CURLM_OK);\n\n    curl_multi_remove_handle($mh, $ch1);\n    curl_multi_remove_handle($mh, $ch2);\n    curl_multi_remove_handle($mh, $ch3);\n    curl_multi_close($mh);\n});\necho PHP_EOL;\n?>\n===DONE===\n--EXPECT--\nbool(true)\nАБВ.txt|application/octet-stream|5АБВ.txt|application/octet-stream|5\n===DONE===\n--CLEAN--\n<?php\n@unlink(__DIR__ . '/АБВ.txt');\n?>\n"
  },
  {
    "path": "tests/swoole_curl/multi/curl_int_cast.phpt",
    "content": "--TEST--\nswoole_curl/multi: Casting CurlHandle to int returns object ID\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () {\n    $handle1 = curl_init();\n    var_dump((int)$handle1);\n    $handle2 = curl_init();\n    var_dump((int)$handle2);\n\n// NB: Unlike resource IDs, object IDs are reused.\n    unset($handle2);\n    $handle3 = curl_init();\n    var_dump((int)$handle3);\n\n// Also works for CurlMultiHandle.\n    $handle4 = curl_multi_init();\n    var_dump((int)$handle4);\n});\n\n?>\n--EXPECTF--\nint(%d)\nint(%d)\nint(%d)\nint(%d)\n"
  },
  {
    "path": "tests/swoole_curl/multi/curl_multi_close_basic.phpt",
    "content": "--TEST--\nswoole_curl/multi: curl_multi_close\n--CREDITS--\nStefan Koopmanschap <stefan@php.net>\n#testfest Utrecht 2009\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('curl')) print 'skip';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () {\n    $ch = curl_multi_init();\n    curl_multi_close($ch);\n    curl_type_assert($ch, 'Swoole-Coroutine-cURL-Multi-Handle', CurlMultiHandle::class);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_curl/multi/curl_multi_close_basic001.phpt",
    "content": "--TEST--\nswoole_curl/multi: curl_multi_close return false when supplied resource not valid cURL multi handle\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('curl')) print 'skip';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () {\n    $cmh = curl_multi_init();\n    curl_type_assert($cmh, 'Swoole-Coroutine-cURL-Multi-Handle', CurlMultiHandle::class);\n    $multi_close_result = curl_multi_close($cmh);\n    Assert::null($multi_close_result);\n    curl_type_assert($cmh, 'Swoole-Coroutine-cURL-Multi-Handle', CurlMultiHandle::class);\n    curl_multi_close($cmh);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_curl/multi/curl_multi_close_reference.phpt",
    "content": "--TEST--\nswoole_curl/multi: curl_multi_close closed by cleanup functions\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('curl')) print 'skip';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () {\n    $mh = curl_multi_init();\n    $array = array($mh);\n    $array[] = &$array;\n\n    curl_multi_add_handle($mh, curl_init());\n    curl_multi_add_handle($mh, curl_init());\n    curl_multi_add_handle($mh, curl_init());\n    curl_multi_add_handle($mh, curl_init());\n    echo \"DONE\\n\";\n});\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_curl/multi/curl_multi_errno_strerror_001.phpt",
    "content": "--TEST--\nswoole_curl/multi: curl_multi_errno and curl_multi_strerror basic test\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded(\"curl\")) {\n        exit(\"skip curl extension not loaded\");\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = ProcessManager::exec(function ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () {\n        $mh = curl_multi_init();\n        $errno = curl_multi_errno($mh);\n        echo $errno . PHP_EOL;\n        echo curl_multi_strerror($errno) . PHP_EOL;\n\n        try {\n            curl_multi_setopt($mh, -1, -1);\n        } catch (ValueError $exception) {\n            echo $exception->getMessage() . \"\\n\";\n        }\n\n        $errno = curl_multi_errno($mh);\n        echo $errno . PHP_EOL;\n        echo curl_multi_strerror($errno) . PHP_EOL;\n    });\n});\n$output = $pm->getChildOutput();\n\nAssert::contains($output, \"0\\nNo error\");\nif (PHP_VERSION_ID < 80000) {\n    Assert::contains($output, \"Warning: curl_multi_setopt(): Invalid curl multi configuration option\");\n} else {\n    Assert::contains($output, \"0\\nNo error\");\n    Assert::contains($output, \"curl_multi_setopt(): Argument #2 (\\$option) is not a valid cURL multi option\");\n}\nAssert::contains($output, \"6\\nUnknown option\");\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_curl/multi/curl_multi_getcontent_basic3.phpt",
    "content": "--TEST--\nswoole_curl/multi: Curl_multi_getcontent() basic test with different sources (local file/http)\n--CREDITS--\nRein Velt (rein@velt.org)\n#TestFest Utrecht 20090509\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse SwooleTest\\CurlManager;\n\n$cm = new CurlManager();\n$cm->run(function ($host) {\n    //CURL_MULTI_GETCONTENT TEST\n\n    //CREATE RESOURCES\n    $ch1 = curl_init();\n    $ch2 = curl_init();\n\n    //SET URL AND OTHER OPTIONS\n    curl_setopt($ch1, CURLOPT_URL, \"{$host}/get.inc?test=getpost&get_param=Hello%20World\");\n    curl_setopt($ch2, CURLOPT_URL, \"file://\" . __DIR__ . DIRECTORY_SEPARATOR . \"curl_testdata2.txt\");\n    curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);\n    curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);\n\n    //CREATE MULTIPLE CURL HANDLE\n    $mh = curl_multi_init();\n\n    //ADD THE 2 HANDLES\n    curl_multi_add_handle($mh, $ch1);\n    curl_multi_add_handle($mh, $ch2);\n\n    //EXECUTE\n    $running = 0;\n    do {\n        curl_multi_exec($mh, $running);\n    } while ($running > 0);\n\n    $results1 = curl_multi_getcontent($ch1);\n    $results2 = curl_multi_getcontent($ch2);\n\n    //CLOSE\n    curl_multi_remove_handle($mh, $ch1);\n    curl_multi_remove_handle($mh, $ch2);\n    curl_multi_close($mh);\n\n    echo $results1;\n    echo $results2;\n});\necho \"CURL2\\n\";\n?>\n--EXPECT--\narray(2) {\n  [\"test\"]=>\n  string(7) \"getpost\"\n  [\"get_param\"]=>\n  string(11) \"Hello World\"\n}\narray(0) {\n}\nCURL2\n"
  },
  {
    "path": "tests/swoole_curl/multi/curl_multi_info_read.phpt",
    "content": "--TEST--\nswoole_curl/multi: array curl_multi_info_read ( resource $mh [, int &$msgs_in_queue = NULL ] );\n--CREDITS--\nmarcosptf - <marcosptf@yahoo.com.br> - @phpsp - sao paulo - br\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('curl')) { print(\"skip\"); }\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () {\n    $urls = array(\n        \"file://\" . __DIR__ . \"/curl_testdata1.txt\",\n        \"file://\" . __DIR__ . \"/curl_testdata2.txt\",\n    );\n\n    $mh = curl_multi_init();\n    foreach ($urls as $i => $url) {\n        $conn[$i] = curl_init($url);\n        curl_setopt($conn[$i], CURLOPT_RETURNTRANSFER, 1);\n        curl_multi_add_handle($mh, $conn[$i]);\n    }\n\n    do {\n        $status = curl_multi_exec($mh, $active);\n    } while ($status === CURLM_CALL_MULTI_PERFORM || $active);\n\n    while ($info = curl_multi_info_read($mh)) {\n        Assert::count($info, 3);\n    }\n\n    foreach ($urls as $i => $url) {\n        curl_close($conn[$i]);\n    }\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_curl/multi/curl_multi_init_basic.phpt",
    "content": "--TEST--\nswoole_curl/multi: Test curl_multi_init()\n--CREDITS--\nMark van der Velden\n#testfest Utrecht 2009\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php if (!extension_loaded(\"curl\")) print \"skip\"; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () {\n    // start testing\n    echo \"*** Testing curl_multi_init(void); ***\\n\";\n\n    //create the multiple cURL handle\n    $mh = curl_multi_init();\n\n    curl_type_assert($mh, 'Swoole-Coroutine-cURL-Multi-Handle', CurlMultiHandle::class);\n\n    curl_multi_close($mh);\n    curl_type_assert($mh, 'Swoole-Coroutine-cURL-Multi-Handle', CurlMultiHandle::class);\n});\n?>\n--EXPECTF--\n*** Testing curl_multi_init(void); ***\n"
  },
  {
    "path": "tests/swoole_curl/multi/curl_multi_segfault.phpt",
    "content": "--TEST--\nswoole_curl/multi: Segfault due to libcurl connection caching\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded(\"curl\")) exit(\"skip curl extension not loaded\");\nif (false === getenv('PHP_CURL_FTP_REMOTE_SERVER'))  exit(\"skip PHP_CURL_FTP_REMOTE_SERVER env variable is not defined\");\nif (false === getenv('PHP_CURL_FTP_REMOTE_USER'))  exit(\"skip PHP_CURL_FTP_REMOTE_USER env variable is not defined\");\nif (false === getenv('PHP_CURL_FTP_REMOTE_PASSWD'))  exit(\"skip PHP_CURL_FTP_REMOTE_PASSWD env variable is not defined\");\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () {\n    $host = getenv('PHP_CURL_FTP_REMOTE_SERVER');\n    $username = getenv('PHP_CURL_FTP_REMOTE_USER');\n    $password = getenv('PHP_CURL_FTP_REMOTE_PASSWD');\n\n    // FTP this script to a server\n    $fp = fopen(__FILE__, \"r\");\n    $url = \"ftp://$username:$password@$host/\";\n\n    $ch = curl_init();\n\n    curl_setopt($ch, CURLOPT_URL, $url);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n\n    //force passive connection\n    curl_setopt($ch, CURLOPT_FTP_USE_EPSV, 0);\n    curl_setopt($ch, CURLOPT_FTP_SKIP_PASV_IP, 1);\n\n    $cmh = curl_multi_init();\n    curl_multi_add_handle($cmh, $ch);\n\n    $active = null;\n\n    do {\n        $mrc = curl_multi_exec($cmh, $active);\n    } while ($mrc == CURLM_CALL_MULTI_PERFORM);\n\n\n    while ($active && $mrc == CURLM_OK) {\n        if (curl_multi_select($cmh) != -1) {\n            do {\n                $mrc = curl_multi_exec($cmh, $active);\n            } while ($mrc == CURLM_CALL_MULTI_PERFORM);\n        }\n    }\n\n    var_dump(is_string(curl_multi_getcontent($ch)));\n    curl_multi_remove_handle($cmh, $ch);\n    curl_close($ch);\n    curl_multi_close($cmh);\n});\n?>\n--EXPECT--\nbool(true)\n"
  },
  {
    "path": "tests/swoole_curl/multi/curl_multi_select_basic1.phpt",
    "content": "--TEST--\nswoole_curl/multi: Test curl_multi_select()\n--CREDITS--\nIvo Jansch <ivo@ibuildings.com>\n#testfest Utrecht 2009\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php if (!extension_loaded(\"curl\")) print \"skip\"; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () {\n    // create the multiple cURL handle\n    $mh = curl_multi_init();\n    echo curl_multi_select($mh).\"\\n\";\n\n    curl_multi_close($mh);\n});\n?>\n--EXPECTF--\n%r(0|-1)%r\n"
  },
  {
    "path": "tests/swoole_curl/multi/curl_multi_setopt_basic001.phpt",
    "content": "--TEST--\nswoole_curl/multi: curl_multi_setopt basic test\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded(\"curl\")) {\n        exit(\"skip curl extension not loaded\");\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = ProcessManager::exec(function ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () {\n        $mh = curl_multi_init();\n        var_dump(curl_multi_setopt($mh, CURLMOPT_PIPELINING, 0));\n        try {\n            curl_multi_setopt($mh, -1, 0);\n        } catch (ValueError $exception) {\n            echo $exception->getMessage() . \"\\n\";\n        }\n        curl_multi_close($mh);\n    });\n});\n$output = $pm->getChildOutput();\n\nAssert::contains($output, 'bool(true)');\n\nif (PHP_VERSION_ID < 80000) {\n    Assert::contains($output, 'Warning: curl_multi_setopt(): Invalid curl multi configuration option');\n} else {\n    Assert::contains($output, 'curl_multi_setopt(): Argument #2 ($option) is not a valid cURL multi option');\n}\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_curl/multi/dtor.phpt",
    "content": "--TEST--\nswoole_curl/multi: multi dtor\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_API_PATH . '/curl_multi.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () {\n    $ch1 = curl_init();\n    curl_setopt($ch1, CURLOPT_URL, \"http://www.baidu.com/\");\n    curl_setopt($ch1, CURLOPT_HEADER, 0);\n    curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1);\n\n    $mh = curl_multi_init();\n    curl_multi_add_handle($mh, $ch1);\n    echo \"Done\\n\";\n});\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/multi/multiple_binding.phpt",
    "content": "--TEST--\nswoole_curl/multi: multiple binding\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded(\"curl\")) exit(\"skip curl extension not loaded\");\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nuse SwooleTest\\CurlManager;\n$cm = new CurlManager();\n$cm->run(function ($host) {\n    $ch1 = curl_init();\n    curl_setopt($ch1, CURLOPT_URL, \"{$host}/get.php?test=get\");\n\n    $mh1 = curl_multi_init();\n    Assert::eq(curl_multi_add_handle($mh1, $ch1), 0);\n\n    $mh2 = curl_multi_init();\n    Assert::eq(curl_multi_add_handle($mh2, $ch1), CURLM_ADDED_ALREADY);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_curl/multi/no_hook.phpt",
    "content": "--TEST--\nswoole_curl/multi: no hook\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_API_PATH.'/curl_multi.php';\n\nuse Swoole\\Runtime;\n\nRuntime::enableCoroutine(0);\nswoole_test_curl_multi();\necho \"Done\\n\";\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/multi/select_cancel.phpt",
    "content": "--TEST--\nswoole_curl/multi: select twice\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_API_PATH . '/curl_multi.php';\n\nuse Swoole\\Runtime;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Server;\nuse Swoole\\Coroutine\\Server\\Connection;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\nconst TIMEOUT = 1.5;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () use ($pm) {\n        $mh = curl_multi_init();\n\n        $add_handle = function ($url) use($mh) {\n            $ch = curl_init();\n            curl_setopt($ch, CURLOPT_URL, $url);\n            curl_setopt($ch, CURLOPT_HEADER, 0);\n            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n            curl_multi_add_handle($mh, $ch);\n            return $ch;\n        };\n\n        $ch1 = $add_handle(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n\n        $active = null;\n\n        do {\n            $mrc = curl_multi_exec($mh, $active);\n        } while ($mrc == CURLM_CALL_MULTI_PERFORM);\n\n        $now = microtime(true);\n\n        $cid = Coroutine::getCid();\n        go(function () use($cid) {\n            System::sleep(0.005);\n            Coroutine::cancel($cid);\n        });\n\n        while ($active && $mrc == CURLM_OK) {\n            $n = curl_multi_select($mh, TIMEOUT);\n            phpt_var_dump('return value：'.$n);\n            phpt_var_dump('swoole error：'.swoole_last_error());\n            if ($n != -1) {\n                do {\n                    $mrc = curl_multi_exec($mh, $active);\n                } while ($mrc == CURLM_CALL_MULTI_PERFORM);\n            }\n            if (Coroutine::isCanceled()) {\n                Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_CANCELED);\n                echo \"CANCELED\\n\";\n                break;\n            }\n        }\n\n        Assert::eq(curl_multi_info_read($mh), false);\n        curl_multi_remove_handle($mh, $ch1);\n        curl_multi_close($mh);\n    });\n    $pm->kill();\n    echo \"Done\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $http->on(\"start\", function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function (Request $request, Response $response) {\n        sleep(20000);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nCANCELED\nDone\n"
  },
  {
    "path": "tests/swoole_curl/multi/select_timeout.phpt",
    "content": "--TEST--\nswoole_curl/multi: select timeout\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_API_PATH . '/curl_multi.php';\n\nuse Swoole\\Runtime;\nuse Swoole\\Coroutine\\Server;\nuse Swoole\\Coroutine\\Server\\Connection;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\nconst TIMEOUT = 0.5;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () use ($pm) {\n        $mh = curl_multi_init();\n\n        $add_handle = function ($url) use($mh) {\n            $ch = curl_init();\n            curl_setopt($ch, CURLOPT_URL, $url);\n            curl_setopt($ch, CURLOPT_HEADER, 0);\n            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n            curl_multi_add_handle($mh, $ch);\n            return $ch;\n        };\n\n        $ch1 = $add_handle(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n\n        $active = null;\n\n        do {\n            $mrc = curl_multi_exec($mh, $active);\n        } while ($mrc == CURLM_CALL_MULTI_PERFORM);\n\n        $now = microtime(true);\n\n        while ($active && $mrc == CURLM_OK) {\n            $tm = microtime(true);\n            $n = curl_multi_select($mh, TIMEOUT);\n            Assert::lessThan(microtime(true) - $tm, TIMEOUT + 0.05);\n\n            $error = swoole_last_error();\n            phpt_var_dump('select return value: '.$n);\n            phpt_var_dump('swoole error: '.$error);\n            if ($n != -1) {\n                do {\n                    $mrc = curl_multi_exec($mh, $active);\n                    phpt_var_dump('exec return value: '.$mrc);\n                } while ($mrc == CURLM_CALL_MULTI_PERFORM);\n            }\n            if (microtime(true) - $now >= TIMEOUT * 4) {\n                echo \"TIMEOUT\\n\";\n                break;\n            }\n        }\n\n        Assert::eq(curl_multi_info_read($mh), false);\n        curl_multi_remove_handle($mh, $ch1);\n        curl_multi_close($mh);\n    });\n    $pm->kill();\n    echo \"Done\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set(['worker_num' => 1, 'log_file' => '/dev/null', 'max_wait_time' => 1,]);\n    $http->on(\"start\", function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function (Request $request, Response $response) {\n        sleep(20000);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nTIMEOUT\nDone\n"
  },
  {
    "path": "tests/swoole_curl/multi/select_twice.phpt",
    "content": "--TEST--\nswoole_curl/multi: select twice\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_API_PATH.'/curl_multi.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () {\n    $n = 4;\n    while ($n--) {\n        go(function () {\n            swoole_test_curl_multi(['select_twice' => true]);\n            echo \"Done\\n\";\n        });\n    }\n});\n?>\n--EXPECTF--\nFatal error: Uncaught Swoole\\Error: This cURL handle is currently executing in coroutine#%d, cannot be operated in %s:%d\nStack trace:\n#0 %s(%d): curl_multi_select(%s)\n%A\n  thrown in %s on line %d\n\n [Coroutine-%d] Stack trace:\n -------------------------------------------------------------------\n#0 %s(%d): curl_multi_select(Object(CurlMultiHandle))\n#1 %s(%d): swoole_test_curl_multi_ex(Object(CurlMultiHandle), Array)\n#2 %s(%d): swoole_test_curl_multi(Array)\n#3 [internal function]: {%s}()\n"
  },
  {
    "path": "tests/swoole_curl/non_exclusive.phpt",
    "content": "--TEST--\nswoole_curl: non-exclusive\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () use ($pm) {\n        $ch = curl_init();\n        $code = uniqid('swoole_');\n        $url = \"http://127.0.0.1:\".$pm->getFreePort().\"/?code=\".urlencode($code);\n\n        curl_setopt($ch, CURLOPT_URL, $url);\n        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n        curl_setopt($ch, CURLOPT_HEADER, 0);\n\n        $header_count = 0;\n        curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) use (&$header_count) {\n            Assert::eq(curl_getinfo($ch, CURLINFO_HTTP_CODE), 200);\n            $header_count++;\n            return strlen($strHeader);\n        });\n\n        $output = curl_exec($ch);\n        Assert::eq($output, \"Hello World\\n\".$code);\n        if ($output === false) {\n            echo \"CURL Error:\" . curl_error($ch);\n        }\n        Assert::greaterThan($header_count, 1);\n        curl_close($ch);\n        echo \"Close\\n\";\n    });\n    $pm->kill();\n    echo \"Done\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server(\"127.0.0.1\", $pm->getFreePort());\n    $http->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n\n    $http->on(\"start\", function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Request $request, Response $response) {\n        usleep(30000);\n        $response->end(\"Hello World\\n\".$request->get['code']);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nClose\nDone\n"
  },
  {
    "path": "tests/swoole_curl/setopt/1.phpt",
    "content": "--TEST--\nswoole_curl/setopt: curl_setopt_array() function - tests setting multiple cURL options with curl_setopt_array()\n--CREDITS--\nMattijs Hoitink mattijshoitink@gmail.com\n#Testfest Utrecht 2009\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\n/*\n * Prototype:     bool curl_setopt_array(resource $ch, array $options)\n * Description:   Sets multiple options for a cURL session.\n * Source:        ext/curl/interface.c\n * Documentation: http://wiki.php.net/qa/temp/ext/curl\n */\n\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n\n    // Use the set Environment variable\n    $url = \"{$host}/get.php?test=get\";\n\n// Start the test\n    echo '== Starting test curl_setopt_array($ch, $options); ==' . \"\\n\";\n\n// curl handler\n    $ch = curl_init();\n\n// options for the curl handler\n    $options = array(\n        CURLOPT_URL => $url,\n        CURLOPT_RETURNTRANSFER => 1\n    );\n\n\n    curl_setopt_array($ch, $options);\n    $returnContent = curl_exec($ch);\n    curl_close($ch);\n\n    var_dump($returnContent);\n});\n\n\n?>\n--EXPECT--\n== Starting test curl_setopt_array($ch, $options); ==\nstring(25) \"Hello World!\nHello World!\"\n"
  },
  {
    "path": "tests/swoole_curl/setopt/3.phpt",
    "content": "--TEST--\nswoole_curl/setopt: curl_setopt() call with CURLOPT_HTTPHEADER\n--CREDITS--\nPaul Sohier\n#phptestfest utrecht\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->disableNativeCurl();\n$cm->run(function ($host) {\n    // start testing\n    echo \"*** curl_setopt() call with CURLOPT_HTTPHEADER\\n\";\n\n    $url = \"{$host}/\";\n    $ch = curl_init();\n\n    curl_setopt($ch, CURLOPT_HTTPHEADER, 1);\n\n    $curl_content = curl_exec($ch);\n    curl_close($ch);\n\n    var_dump( $curl_content );\n\n    $ch = curl_init();\n\n    ob_start(); // start output buffering\n    curl_setopt($ch, CURLOPT_HTTPHEADER, array());\n    curl_setopt($ch, CURLOPT_URL, $host);\n\n    $curl_content = curl_exec($ch);\n    ob_end_clean();\n    curl_close($ch);\n\n    var_dump( $curl_content );\n\n});\n\n\n?>\n--EXPECTF--\n*** curl_setopt() call with CURLOPT_HTTPHEADER\n\nWarning: swoole_curl_setopt(): You must pass either an object or an array with the CURLOPT_HTTPHEADER argument in %s on line %d\nbool(false)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_curl/setopt/4.phpt",
    "content": "--TEST--\nswoole_curl/setopt: curl_setopt() call with CURLOPT_RETURNTRANSFER\n--CREDITS--\nPaul Sohier\n#phptestfest utrecht\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n\n// start testing\n    echo \"*** curl_setopt() call with CURLOPT_RETURNTRANSFER set to 1\\n\";\n\n    $url = \"{$host}/\";\n    $ch = curl_init();\n\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_URL, $url);\n\n    $curl_content = curl_exec($ch);\n    curl_close($ch);\n\n    var_dump( $curl_content );\n\n    echo \"*** curl_setopt() call with CURLOPT_RETURNTRANSFER set to 0\\n\";\n\n    $ch = curl_init();\n\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0);\n    curl_setopt($ch, CURLOPT_URL, $url);\n    ob_start();\n    $curl_content = curl_exec($ch);\n    ob_end_clean();\n    curl_close($ch);\n\n    var_dump( $curl_content );\n\n});\n\n?>\n--EXPECTF--\n*** curl_setopt() call with CURLOPT_RETURNTRANSFER set to 1\nstring(%d) \"%a\"\n*** curl_setopt() call with CURLOPT_RETURNTRANSFER set to 0\nbool(true)\n"
  },
  {
    "path": "tests/swoole_curl/setopt/5_skip.phpt",
    "content": "--TEST--\nswoole_curl/setopt: cURL option CURLOPT_READFUNCTION\n--CREDITS--\nWHITE new media architects - Jeroen Vermeulen\n#testfest Utrecht 2009\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_unsupported();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nfunction custom_readfunction($oCurl, $hReadHandle, $iMaxOut)\n{\n  $sData = fread($hReadHandle,$iMaxOut-10); # -10 to have space to add \"custom:\"\n  if (!empty($sData))\n  {\n    $sData = \"custom:\".$sData;\n  }\n  return $sData;\n}\n\n$sFileBase  = dirname(__FILE__).DIRECTORY_SEPARATOR.'curl_opt_CURLOPT_READFUNCTION';\n$sReadFile  = $sFileBase.'_in.tmp';\n$sWriteFile = $sFileBase.'_out.tmp';\n$sWriteUrl  = 'file://'.$sWriteFile;\n\nfile_put_contents($sReadFile,'contents of tempfile');\n$hReadHandle = fopen($sReadFile, 'r');\n\n$oCurl = curl_init();\ncurl_setopt($oCurl, CURLOPT_URL,          $sWriteUrl);\ncurl_setopt($oCurl, CURLOPT_UPLOAD,       1);\ncurl_setopt($oCurl, CURLOPT_READFUNCTION, \"custom_readfunction\" );\ncurl_setopt($oCurl, CURLOPT_INFILE,       $hReadHandle );\ncurl_exec($oCurl);\ncurl_close($oCurl);\n\nfclose ($hReadHandle);\n\n$sOutput = file_get_contents($sWriteFile);\nvar_dump($sOutput);\n?>\n===DONE===\n--CLEAN--\n<?php\n$sFileBase  = dirname(__FILE__).DIRECTORY_SEPARATOR.'curl_opt_CURLOPT_READFUNCTION';\n$sReadFile  = $sFileBase.'_in.tmp';\n$sWriteFile = $sFileBase.'_out.tmp';\nunlink($sReadFile);\nunlink($sWriteFile);\n?>\n--EXPECT--\nstring(27) \"custom:contents of tempfile\"\n===DONE===\n"
  },
  {
    "path": "tests/swoole_curl/setopt/filetime_1.phpt",
    "content": "--TEST--\nswoole_curl/setopt: CURLOPT_FILETIME\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n\n    $ch = curl_init();\n\n    $options = array(\n        CURLOPT_URL => 'https://static.zhihu.com/heifetz/chunks/5946.4600cc0c1b3dcecac17c.js',\n        CURLOPT_RETURNTRANSFER => 1,\n        CURLOPT_FILETIME => true,\n        CURLOPT_NOBODY => true,\n    );\n\n    curl_setopt_array($ch, $options);\n    curl_exec($ch);\n    $info = curl_getinfo($ch);\n    Assert::assert(!empty($info['filetime']));\n    Assert::greaterThanEq($info['filetime'], 1000000);\n    curl_close($ch);\n    echo \"END\\n\";\n}, false);\n\n?>\n--EXPECT--\nEND\n"
  },
  {
    "path": "tests/swoole_curl/setopt/filetime_2.phpt",
    "content": "--TEST--\nswoole_curl/setopt: CURLOPT_FILETIME [return -1]\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n    $url = \"{$host}/get.php?test=get\";\n    $ch = curl_init();\n\n    $options = array(\n        CURLOPT_URL => $url,\n        CURLOPT_RETURNTRANSFER => 1,\n        CURLOPT_FILETIME => true,\n    );\n\n    curl_setopt_array($ch, $options);\n    curl_exec($ch);\n    $info = curl_getinfo($ch);\n    Assert::assert(!empty($info['filetime']));\n    Assert::eq($info['filetime'], -1);\n    curl_close($ch);\n    echo \"END\\n\";\n});\n\n?>\n--EXPECT--\nEND\n"
  },
  {
    "path": "tests/swoole_curl/setopt/header_out.phpt",
    "content": "--TEST--\nswoole_curl/setopt: CURLINFO_HEADER_OUT\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\n/*\n * Prototype:     bool curl_setopt_array(resource $ch, array $options)\n * Description:   Sets multiple options for a cURL session.\n * Source:        ext/curl/interface.c\n * Documentation: http://wiki.php.net/qa/temp/ext/curl\n */\n\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n    $url = \"{$host}/post.php?test=get\";\n\n    $ch = curl_init();\n\n    $options = array(\n        CURLOPT_URL => $url,\n        CURLINFO_HEADER_OUT => true,\n        CURLOPT_RETURNTRANSFER => 1,\n        CURLOPT_POST => 1,\n        CURLOPT_POSTFIELDS => 'id=123&name=swoole',\n    );\n\n    curl_setopt_array($ch, $options);\n    $returnContent = curl_exec($ch);\n    Assert::assert($returnContent);\n    $info = curl_getinfo($ch);\n    Assert::assert($info['request_header']);\n    curl_close($ch);\n    echo \"END\\n\";\n});\n\n?>\n--EXPECT--\nEND\n"
  },
  {
    "path": "tests/swoole_curl/setopt/infile.phpt",
    "content": "--TEST--\nswoole_curl/setopt: CURLOPT_INFILE\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n    $url = 'http://' . HTTPBIN_SERVER_HOST . ':' . HTTPBIN_SERVER_PORT . '/put';\n    $fp = fopen(__FILE__, \"r\");\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_USERPWD, 'user:password');\n    curl_setopt($ch, CURLOPT_URL, $url);\n    curl_setopt($ch, CURLOPT_PUT, 1);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_INFILE, $fp);\n    curl_setopt($ch, CURLOPT_INFILESIZE, filesize(__FILE__));\n    $http_result = curl_exec($ch);\n    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);\n    var_dump($http_code);\n    $http_body = json_decode($http_result, true);\n    var_dump($http_body['headers']['Authorization']);\n    Assert::same($http_body['data'], file_get_contents(__FILE__));\n    curl_close($ch);\n    fclose($fp);\n});\n\n?>\n--EXPECT--\nint(200)\nstring(26) \"Basic dXNlcjpwYXNzd29yZA==\"\n"
  },
  {
    "path": "tests/swoole_curl/setopt/nobody.phpt",
    "content": "--TEST--\nswoole_curl/setopt: CURLOPT_NOBODY\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\n/*\n * Prototype:     bool curl_setopt_array(resource $ch, array $options)\n * Description:   Sets multiple options for a cURL session.\n * Source:        ext/curl/interface.c\n * Documentation: http://wiki.php.net/qa/temp/ext/curl\n */\n\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n    $url = \"{$host}/get.php?test=get\";\n\n    $ch = curl_init();\n\n    $options = array(\n        CURLOPT_URL => $url,\n        CURLOPT_RETURNTRANSFER => 1,\n        CURLOPT_NOBODY => true,\n    );\n\n    curl_setopt_array($ch, $options);\n    $returnContent = curl_exec($ch);\n    Assert::isEmpty($returnContent);\n    curl_close($ch);\n    echo \"END\\n\";\n});\n\n?>\n--EXPECT--\nEND\n"
  },
  {
    "path": "tests/swoole_curl/share/1.phpt",
    "content": "--TEST--\nswoole_curl/share: Basic curl_share test\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new SwooleTest\\CurlManager();\n\n$cm->run(function ($host) {\n    $sh = curl_share_init();\n\n    $ch1 = curl_init();\n    curl_setopt($ch1, CURLOPT_URL, 'file://' . dirname(__DIR__) . '/upload/curl_testdata1.txt');\n    curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);\n    curl_setopt($ch1, CURLOPT_SHARE, $sh);\n\n    $ch2 = curl_init();\n    curl_setopt($ch2, CURLOPT_URL, 'file://' . dirname(__DIR__) . '/upload/curl_testdata2.txt');\n    curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);\n    curl_setopt($ch2, CURLOPT_SHARE, $sh);\n\n    // Make sure nothing bad handles if the share handle is unset early.\n    unset($sh);\n\n    var_dump(curl_exec($ch1));\n    var_dump(curl_exec($ch2));\n});\n\n\n?>\n--EXPECT--\nstring(6) \"CURL1\n\"\nstring(6) \"CURL2\n\"\n"
  },
  {
    "path": "tests/swoole_curl/share/5.phpt",
    "content": "--TEST--\nswoole_curl/share: Basic curl_share test\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new SwooleTest\\CurlManager();\n\n$cm->run(function ($host) {\n    $sh = curl_share_init();\n    curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);\n\n    $cookie_value = 'swoole';\n    $cookie_name = 'test_cookie';\n\n    $url = \"{$host}/get.php?test=cookie_set&cookie_name={$cookie_name}&cookie_value={$cookie_value}&cookie_expire=\" . (time() + 3600);\n\n    $ch1 = curl_init($url);\n    curl_setopt($ch1, CURLOPT_SHARE, $sh);\n    curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch1, CURLOPT_COOKIEFILE, \"\");\n    $rs1 = curl_exec($ch1);\n    Assert::notEmpty($rs1);\n    Assert::eq(json_decode($rs1), true);\n\n    $url = \"{$host}/get.php?test=cookie_get\";\n    $ch2 = curl_init($url);\n    curl_setopt($ch2, CURLOPT_SHARE, $sh);\n    curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch2, CURLOPT_COOKIEFILE, \"\");\n    $rs2 = curl_exec($ch2);\n    Assert::notEmpty($rs2);\n    Assert::eq(json_decode($rs2)->$cookie_name, $cookie_value);\n    curl_share_close($sh);\n\n    curl_close($ch1);\n    curl_close($ch2);\n});\n\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_curl/sleep.phpt",
    "content": "--TEST--\nswoole_curl: sleep\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n$pm = new SwooleTest\\ProcessManager;\n\nconst N = 8;\n\n$pm->parentFunc = function () use ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    $s = microtime(true);\n    run(function () use ($pm) {\n        $n = N;\n        while($n--) {\n            go(function() use ($pm) {\n                $ch = curl_init();\n                $code = uniqid('swoole_');\n                $url = \"http://127.0.0.1:\".$pm->getFreePort().\"/?code=\".urlencode($code);\n\n                curl_setopt($ch, CURLOPT_URL, $url);\n                curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n                curl_setopt($ch, CURLOPT_HEADER, 0);\n                curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) {\n                    return strlen($strHeader);\n                });\n\n                $output = curl_exec($ch);\n                Assert::eq($output, \"Hello World\\n\".$code);\n                if ($output === false) {\n                    echo \"CURL Error:\" . curl_error($ch);\n                }\n                curl_close($ch);\n            });\n        }\n    });\n    Assert::lessThan(microtime(true) - $s, 0.5);\n    $pm->kill();\n    echo \"Done\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set(['worker_num' => N, 'log_file' => '/dev/null']);\n\n    $http->on(\"start\", function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Request $request, Response $response) {\n        usleep(300000);\n        $response->end(\"Hello World\\n\".$request->get['code']);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/ssl/version.phpt",
    "content": "--TEST--\nswoole_curl/ssl: Test SSL_VERSION option\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php if (!extension_loaded(\"curl\")) print \"skip\"; ?>\n--FILE--\n<?php\n\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n    $ch = curl_init();\n\n    curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);\n    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);\n    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_URL, \"https://www.baidu.com/\");\n    $result = curl_exec($ch);\n    Assert::assert($result);\n    Assert::contains($result, '百度');\n    curl_close($ch);\n\n}, false);\n\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_curl/symfony-noco.phpt",
    "content": "--TEST--\nswoole_curl: symfony http client\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire_once TESTS_LIB_PATH . '/vendor/autoload.php';\n\nuse Swoole\\Runtime;\nuse Symfony\\Component\\HttpClient\\HttpClient as SymfonyHttpClient;\nuse Symfony\\Component\\HttpClient\\HttplugClient as SymfonyHttplugClient;\nuse Http\\Client\\Common\\PluginClient;\nuse Http\\Discovery\\Psr17FactoryDiscovery;\n\n$httpClient = new SymfonyHttplugClient(\n    SymfonyHttpClient::create(['max_duration' => 5])\n);\n$uid = uniqid();\n$req = Psr17FactoryDiscovery::findRequestFactory()\n    ->createRequest('POST', 'http://www.httpbin.org/post')\n    ->withHeader('Content-Type', 'application/json')\n    ->withBody(Psr17FactoryDiscovery::findStreamFactory()->createStream(json_encode(['key' => $uid])));\n\n$res = (new PluginClient($httpClient))->sendAsyncRequest($req)->wait();\n\n$json = $res->getBody()->getContents();\nAssert::notEmpty($json);\n$data_1 = json_decode($json);\n$data_2 = json_decode($data_1->data);\nAssert::eq($data_2->key, $uid);\necho 'Done' . PHP_EOL;\n\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/symfony.phpt",
    "content": "--TEST--\nswoole_curl: symfony http client\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire_once TESTS_LIB_PATH . '/vendor/autoload.php';\n\nuse Swoole\\Runtime;\nuse Symfony\\Component\\HttpClient\\HttpClient as SymfonyHttpClient;\nuse Symfony\\Component\\HttpClient\\HttplugClient as SymfonyHttplugClient;\nuse Http\\Client\\Common\\PluginClient;\nuse Http\\Discovery\\Psr17FactoryDiscovery;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nrun(function () {\n    $httpClient = new SymfonyHttplugClient(\n        SymfonyHttpClient::create(['max_duration' => 5])\n    );\n    $uid = uniqid();\n    $req = Psr17FactoryDiscovery::findRequestFactory()\n        ->createRequest('POST', 'http://www.httpbin.org/post')\n        ->withHeader('Content-Type', 'application/json')\n        ->withBody(Psr17FactoryDiscovery::findStreamFactory()->createStream(json_encode(['key' => $uid])));\n\n    $res = (new PluginClient($httpClient))->sendAsyncRequest($req)->wait();\n\n    $json = $res->getBody()->getContents();\n    Assert::notEmpty($json);\n    $data_1 = json_decode($json);\n    $data_2 = json_decode($data_1->data);\n    Assert::eq($data_2->key, $uid);\n    echo 'Done' . PHP_EOL;\n});\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/template",
    "content": "require __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n\n\n});"
  },
  {
    "path": "tests/swoole_curl/timer_coredump.phpt",
    "content": "--TEST--\nswoole_curl: timer coredump\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nfunction test()\n{\n    $curl = curl_init();\n    curl_setopt($curl, CURLOPT_URL, 'https://httpbin.org/');\n    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);\n    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);\n    curl_exec($curl);\n    curl_close($curl);\n}\n\nrun('test');\nrun('test');\n\necho \"Done\\n\";\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_curl/undefined_behavior/0.phpt",
    "content": "--TEST--\nswoole_curl/undefined_behavior: 0\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = ProcessManager::exec(function ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () {\n        $GLOBALS['ch'] = curl_init();\n    });\n    Runtime::enableCoroutine(0);\n    curl_close($GLOBALS['ch']);\n});\n$output = $pm->getChildOutput();\nif (PHP_VERSION_ID < 80000) {\n    $pm->expectExitCode(0);\n    Assert::contains($output, \"Warning: curl_close(): supplied resource is not a valid cURL handle resource\");\n} else {\n    $pm->expectExitCode(0);\n}\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_curl/undefined_behavior/1.phpt",
    "content": "--TEST--\nswoole_curl/undefined_behavior: 1\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = ProcessManager::exec(function ($pm) {\n    $ch = curl_init();\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () use ($ch) {\n        curl_close($ch);\n    });\n});\n$output = $pm->getChildOutput();\nif (PHP_VERSION_ID < 80000) {\n    $pm->expectExitCode(255);\n    Assert::contains($output, \"curl_close(): supplied resource is not a valid Swoole-Coroutine-cURL-Handle resource\");\n} else {\n    $pm->expectExitCode(0);\n}\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_curl/undefined_behavior/2.phpt",
    "content": "--TEST--\nswoole_curl/undefined_behavior: 2\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\n$pm = ProcessManager::exec(function ($pm) {\n    $ch = curl_init();\n\n    $test_fn = function () use ($ch) {\n        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n        curl_setopt($ch, CURLOPT_URL, \"http://www.gov.cn/\");\n\n        $curl_content = curl_exec($ch);\n        Assert::contains($curl_content, '中国政府网');\n        curl_close($ch);\n    };\n\n    $test_fn();\n\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () use ($test_fn) {\n        $test_fn();\n    });\n});\n$output = $pm->getChildOutput();\nif (PHP_VERSION_ID < 80000) {\n    $pm->expectExitCode(255);\n    Assert::contains($output, \"curl_setopt(): supplied resource is not a valid Swoole-Coroutine-cURL-Handle resource\");\n} else {\n    $pm->expectExitCode(0);\n}\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_curl/undefined_behavior/3.phpt",
    "content": "--TEST--\nswoole_curl/undefined_behavior: 3\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = ProcessManager::exec(function ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    $ch = curl_init();\n    run(function () use ($ch) {\n        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n        curl_setopt($ch, CURLOPT_URL, \"http://www.gov.cn/\");\n\n        $curl_content = curl_exec($ch);\n        Assert::contains($curl_content, '中国政府网');\n    });\n    Runtime::enableCoroutine(0);\n    curl_close($ch);\n});\n$output = $pm->getChildOutput();\nif (PHP_VERSION_ID < 80000) {\n    $pm->expectExitCode(0);\n    Assert::contains($output, \"curl_close(): supplied resource is not a valid cURL handle resource\");\n} else {\n    $pm->expectExitCode(0);\n}\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_curl/undefined_behavior/4.phpt",
    "content": "--TEST--\nswoole_curl/undefined_behavior: 4\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = ProcessManager::exec(function ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    $ch = curl_init();\n    run(function () use ($ch) {\n        Runtime::enableCoroutine(0);\n        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n        curl_setopt($ch, CURLOPT_URL, \"http://www.gov.cn/\");\n\n        $curl_content = curl_exec($ch);\n        Assert::contains($curl_content, '中国政府网');\n        curl_close($ch);\n    });\n});\n$output = $pm->getChildOutput();\nif (PHP_VERSION_ID < 80000) {\n    $pm->expectExitCode(0);\n    Assert::contains($output, \"curl_close(): supplied resource is not a valid cURL handle resource\");\n} else {\n    $pm->expectExitCode(0);\n}\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_curl/undefined_behavior/5.phpt",
    "content": "--TEST--\nswoole_curl/undefined_behavior: 5\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = ProcessManager::exec(function ($pm) {\n    $mh = curl_multi_init();\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () use ($mh) {\n        curl_multi_close($mh);\n    });\n});\n$output = $pm->getChildOutput();\nif (PHP_VERSION_ID < 80000) {\n    $pm->expectExitCode(255);\n    Assert::contains($output, \"curl_multi_close(): supplied resource is not a valid Swoole-Coroutine-cURL-Multi-Handle resource\");\n} else {\n    $pm->expectExitCode(0);\n}\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_curl/undefined_behavior/6.phpt",
    "content": "--TEST--\nswoole_curl/undefined_behavior: 6\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_API_PATH.'/curl_multi.php';\n\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\n$pm = ProcessManager::exec(function ($pm) {\n    $mh = curl_multi_init();\n\n    $test_fn = function () use ($mh) {\n        swoole_test_curl_multi_ex($mh);\n    };\n\n    $test_fn();\n\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () use ($test_fn) {\n        $test_fn();\n    });\n});\n$output = $pm->getChildOutput();\n$pm->expectExitCode(0);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_curl/undefined_behavior/7.phpt",
    "content": "--TEST--\nswoole_curl/undefined_behavior: 7\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = ProcessManager::exec(function ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () {\n        $GLOBALS['mh'] = curl_multi_init();\n    });\n    Runtime::enableCoroutine(0);\n    curl_multi_close($GLOBALS['mh']);\n});\n$output = $pm->getChildOutput();\nif (PHP_VERSION_ID < 80000) {\n    $pm->expectExitCode(0);\n    Assert::contains($output, \"Warning: curl_multi_close(): supplied resource is not a valid cURL Multi Handle resource\");\n} else {\n    $pm->expectExitCode(0);\n}\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_curl/undefined_behavior/8.phpt",
    "content": "--TEST--\nswoole_curl/undefined_behavior: 8\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\n//  Create a curl resource before the coroutine hook\n$ch = curl_init();\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () use ($ch) {\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_URL, \"https://www.gov.cn/\");\n    $curl_content = curl_exec($ch);\n    Assert::false($curl_content);\n});\ncurl_close($ch);\nRuntime::enableCoroutine(0);\n?>\n--EXPECTF--\nWarning: curl_exec(): The given handle is not initialized in coroutine in %s on line %d\n"
  },
  {
    "path": "tests/swoole_curl/upload/1.phpt",
    "content": "--TEST--\nswoole_curl/upload: CURL file uploading\n--INI--\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\nskip(\"tmp skip\");\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nSwoole\\Coroutine::set([\n    'print_backtrace_on_error' => true,\n    // 'enable_kqueue' => true,\n]);\n\n$cm = new \\SwooleTest\\CurlManager();\n$cm->run(function ($host) {\n\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"{$host}/get.php?test=file\");\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n\n    testcurl($ch, __DIR__ . '/curl_testdata1.txt');\n    testcurl($ch, __DIR__ . '/curl_testdata1.txt', 'text/plain');\n    testcurl($ch, __DIR__ . '/curl_testdata1.txt', '', 'foo.txt');\n    testcurl($ch, __DIR__ . '/curl_testdata1.txt', 'text/plain', 'foo.txt');\n\n    $file = new CurlFile(__DIR__ . '/curl_testdata1.txt');\n    $file->setMimeType('text/plain');\n    var_dump($file->getMimeType());\n    var_dump($file->getFilename());\n    curl_setopt($ch, CURLOPT_POSTFIELDS, array(\"file\" => $file));\n    var_dump(curl_exec($ch));\n\n    $file = curl_file_create(__DIR__ . '/curl_testdata1.txt');\n    $file->setPostFilename('foo.txt');\n    var_dump($file->getPostFilename());\n    curl_setopt($ch, CURLOPT_POSTFIELDS, array(\"file\" => $file));\n    var_dump(curl_exec($ch));\n\n    try {\n        curl_setopt($ch, CURLOPT_SAFE_UPLOAD, 0);\n    } catch (throwable $e) {\n        trigger_error($e->getMessage(), E_USER_WARNING);\n    }\n    $params = array('file' => '@' . __DIR__ . '/curl_testdata1.txt');\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $params);\n    var_dump(curl_exec($ch));\n\n    curl_setopt($ch, CURLOPT_SAFE_UPLOAD, true);\n    $params = array('file' => '@' . __DIR__ . '/curl_testdata1.txt');\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $params);\n    var_dump(curl_exec($ch));\n\n    curl_setopt($ch, CURLOPT_URL, \"{$host}/get.php?test=post\");\n    $params = array('file' => '@' . __DIR__ . '/curl_testdata1.txt');\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $params);\n    var_dump(curl_exec($ch));\n\n    curl_close($ch);\n});\n\nfunction testcurl($ch, $name, $mime = '', $postname = '')\n{\n\tif(!empty($postname)) {\n\t\t$file = new CurlFile($name, $mime, $postname);\n\t} else if(!empty($mime)) {\n\t\t$file = new CurlFile($name, $mime);\n\t} else {\n\t\t$file = new CurlFile($name);\n\t}\n\tcurl_setopt($ch, CURLOPT_POSTFIELDS, array(\"file\" => $file));\n\t$ret = curl_exec($ch) or die(\"ERROR: Code=\".curl_errno($ch). \"Msg=\".curl_error($ch).\"\\n\");\n\tvar_dump($ret);\n}\n?>\n--EXPECTF--\nstring(%d) \"curl_testdata1.txt|application/octet-stream|6\"\nstring(%d) \"curl_testdata1.txt|text/plain|6\"\nstring(%d) \"foo.txt|application/octet-stream|6\"\nstring(%d) \"foo.txt|text/plain|6\"\nstring(%d) \"text/plain\"\nstring(%d) \"%s/curl_testdata1.txt\"\nstring(%d) \"curl_testdata1.txt|text/plain|6\"\nstring(%d) \"foo.txt\"\nstring(%d) \"foo.txt|application/octet-stream|6\"\n\nWarning: %s(): Disabling safe uploads is no longer supported in %s on line %d\nstring(0) \"\"\nstring(0) \"\"\nstring(%d) \"array(1) {\n  [\"file\"]=>\n  string(%d) \"@%s/curl_testdata1.txt\"\n}\n\"\n"
  },
  {
    "path": "tests/swoole_curl/upload/2.phpt",
    "content": "--TEST--\nswoole_curl/upload: CURL file uploading\n--INI--\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire_once TESTS_LIB_PATH . '/vendor/autoload.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nrun(function () {\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"https://httpbin.org/anything\");\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    $file = new CurlFile(TEST_IMAGE);\n    curl_setopt($ch, CURLOPT_POSTFIELDS, array(\"swoole_file\" => $file));\n    $result = curl_exec($ch);\n    Assert::notEmpty($result);\n    $json = json_decode($result);\n    Assert::notEmpty($json);\n    Assert::notEmpty($json->files->swoole_file);\n    $prefix = 'data:application/octet-stream;base64,';\n    Assert::startsWith($json->files->swoole_file, $prefix);\n    $data = substr($json->files->swoole_file, strlen($prefix));\n    Assert::eq(md5(base64_decode($data)), md5_file(TEST_IMAGE));\n});\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_curl/upload/3.phpt",
    "content": "--TEST--\nswoole_curl/upload: CURL file uploading[INFILE]\n--INI--\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nuse Swoole\\Runtime;\nuse SwooleTest\\CurlManager;\n\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$cm = new CurlManager();\n$cm->run(function ($host) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_ALL);\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"{$host}/get.php?test=input\");\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_PUT, 1);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    $fp = fopen(TEST_IMAGE, 'r');\n    curl_setopt($ch, CURLOPT_INFILE, $fp);\n    curl_setopt($ch, CURLOPT_HTTPHEADER, array(\"Expect:\"));\n    curl_setopt($ch, CURLOPT_INFILESIZE, filesize(TEST_IMAGE));\n\n    $http_result = curl_exec($ch);\n    Assert::eq(md5($http_result), md5_file(TEST_IMAGE));\n});\n\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_curl/upload/curl_testdata1.txt",
    "content": "CURL1\n"
  },
  {
    "path": "tests/swoole_curl/upload/curl_testdata2.txt",
    "content": "CURL2\n"
  },
  {
    "path": "tests/swoole_curl/yield_in_callback.phpt",
    "content": "--TEST--\nswoole_curl: suspend in callback\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n$pm = new SwooleTest\\ProcessManager;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\nrun(function () use ($pm) {\n    $ch = curl_init();\n    $code = uniqid('swoole_');\n    $url = \"http://www.baidu.com/?code=\".urlencode($code);\n\n    curl_setopt($ch, CURLOPT_URL, $url);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HEADER, 0);\n\n    $header_count = 0;\n    curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) use (&$header_count) {\n        Assert::eq(curl_getinfo($ch, CURLINFO_HTTP_CODE), 200);\n        Assert::eq(md5_file(__FILE__), md5(file_get_contents(__FILE__)));\n        Co::sleep(0.05);\n        $header_count++;\n        return strlen($strHeader);\n    });\n\n    echo \"exec\\n\";\n    $output = curl_exec($ch);\n    Assert::contains($output, \"baidu.com\");\n    if ($output === false) {\n        echo \"CURL Error:\" . curl_error($ch);\n    }\n    echo \"exec end\\n\";\n    Assert::greaterThan($header_count, 1);\n    curl_close($ch);\n    echo \"Close\\n\";\n});\n\n?>\n--EXPECT--\nexec\nexec end\nClose\n"
  },
  {
    "path": "tests/swoole_event/add_after_server_start.phpt",
    "content": "--TEST--\nswoole_event: add event after server start\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n\nconst FILE = __DIR__ . '/tmp_result.txt';\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $pm->kill();\n    $str = swoole_string(file_get_contents(FILE));\n    Assert::true($str->contains('HTTP/1.1 302 Moved Temporarily') or $str->contains('HTTP/1.1 301 Moved Permanently'));\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on(\"start\", function (Server $serv) use ($pm) {\n        $fp = stream_socket_client(\"tcp://\" . TEST_DOMAIN_3 . \":80\", $errno, $errstr, 30);\n        fwrite($fp, \"GET / HTTP/1.1\\r\\nHost: \" . TEST_DOMAIN_3 . \"\\r\\n\\r\\n\");\n\n        Swoole\\Event::add($fp, function ($fp) use ($pm) {\n            $resp = fread($fp, 8192);\n            Swoole\\Event::del($fp);\n            fclose($fp);\n            file_put_contents(FILE, $resp);\n            $pm->wakeup();\n        });\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\nunlink(FILE);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_event/cycle.phpt",
    "content": "--TEST--\nswoole_event: cycle\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$n = 0;\nAssert::false(Swoole\\Event::cycle(null));\nSwoole\\Event::cycle(function () use (&$n) {\n    echo \"cycle [$n]\\n\";\n    $n++;\n    if ($n == 3) {\n        Swoole\\Event::cycle(null);\n    }\n});\nSwoole\\Timer::after(100, function () {\n    echo \"timer [1]\\n\";\n    Swoole\\Timer::after(100, function () {\n        echo \"timer [2]\\n\";\n    });\n    Swoole\\Event::defer(function () {\n        echo \"defer [2]\\n\";\n    });\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\ncycle [0]\ntimer [1]\ndefer [2]\ncycle [1]\ntimer [2]\ncycle [2]\n"
  },
  {
    "path": "tests/swoole_event/defer.phpt",
    "content": "--TEST--\nswoole_event: Swoole\\Event::defer\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Event::defer(function () {\n    echo \"defer [1]\\n\";\n});\nSwoole\\Timer::after(100, function () {\n    echo \"timer [1]\\n\";\n    Swoole\\Timer::after(100, function () {\n        echo \"timer [2]\\n\";\n    });\n    Swoole\\Event::defer(function () {\n        echo \"defer [2]\\n\";\n    });\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\ndefer [1]\ntimer [1]\ndefer [2]\ntimer [2]\n"
  },
  {
    "path": "tests/swoole_event/defer_with_sleep.phpt",
    "content": "--TEST--\nswoole_event: Swoole\\Event::defer and sleep\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    co::sleep(0.001);\n    echo \"timer [1]\\n\";\n    Swoole\\Event::defer(function () {\n        echo \"defer [2]\\n\";\n        go(function () {\n            co::sleep(0.001);\n            echo \"timer [2]\\n\";\n        });\n    });\n});\nSwoole\\Event::defer(function () {\n    echo \"defer [1]\\n\";\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\ndefer [1]\ntimer [1]\ndefer [2]\ntimer [2]\n"
  },
  {
    "path": "tests/swoole_event/defer_without_io.phpt",
    "content": "--TEST--\nswoole_event: Swoole\\Event::defer without io\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Event::defer(function () {\n    echo \"defer [1]\\n\";\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\ndefer [1]\n"
  },
  {
    "path": "tests/swoole_event/del.phpt",
    "content": "--TEST--\nswoole_event: Swoole\\Event::del\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$fp = stream_socket_client(\"tcp://www.qq.com:80\", $errno, $errstr, 30);\nfwrite($fp, \"GET / HTTP/1.1\\r\\nHost: www.qq.com\\r\\n\\r\\n\");\n\nSwoole\\Event::add($fp, function($fp) {\n    $resp = fread($fp, 8192);\n    //socket处理完成后，从epoll事件中移除socket\n    Swoole\\Event::del($fp);\n    fclose($fp);\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_event/del_after_close.phpt",
    "content": "--TEST--\nswoole_event: del after close\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Event;\n\nCo::set(['log_level' => SWOOLE_LOG_ERROR]);\n\n$cli = new Swoole\\Client(SWOOLE_SOCK_TCP);\n\n$cli->connect(\"www.qq.com\", 80);\n\n$fd = $cli->sock;\n\nEvent::add($fd, function ($fd) use ($cli) {\n    $resp = $cli->recv(8192);\n    Swoole\\Event::del($fd);\n    $cli->close();\n});\n\nEvent::write($fd, \"GET / HTTP/1.1\\r\\nHost: www.qq.com\\r\\n\\r\\n\");\n\n$cli->close();\n\nif (Event::isset($fd)) {\n    if (!Event::del($fd)) {\n        echo \"Unable to release fd {$fd} from EventLoop\\n\";\n    } else {\n        echo \"FD {$fd} released from EventLoop\\n\";\n    }\n}\n\n$eventNum = Swoole\\Coroutine::stats()['event_num'];\nvar_dump($eventNum);\n\nEvent::wait();\n?>\n--EXPECTF--\nFD %d released from EventLoop\nint(0)\n"
  },
  {
    "path": "tests/swoole_event/deprecated_event_wait.phpt",
    "content": "--TEST--\nswoole_event: deprecated_event_wait\n--SKIPIF--\n<?php\n\nuse SebastianBergmann\\CodeCoverage\\Report\\PHP;\n\nuse function Swoole\\Coroutine\\run;\n\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nerror_reporting(E_ALL & E_DEPRECATED);\n\nrun(function () {\n    throw new Exception(\"Error Processing Request\", 1);\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_event/dispatch.phpt",
    "content": "--TEST--\nswoole_event: dispatch\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$id = Swoole\\Timer::tick(100, function () {\n    echo \"Tick\\n\";\n});\n\n$n = 5;\nwhile ($n--) {\n    echo \"loop\\n\";\n    Swoole\\Event::dispatch();\n}\n\nSwoole\\Timer::clear($id);\n\n?>\n--EXPECT--\nloop\nloop\nTick\nloop\nTick\nloop\nTick\nloop\nTick\n"
  },
  {
    "path": "tests/swoole_event/function_alias.phpt",
    "content": "--TEST--\nswoole_event: function alias\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nvar_dump(\n    function_exists('swoole_event_add') &&\n    function_exists('swoole_event_del') &&\n    function_exists('swoole_event_set') &&\n    function_exists('swoole_event_isset') &&\n    function_exists('swoole_event_dispatch') &&\n    function_exists('swoole_event_defer') &&\n    function_exists('swoole_event_cycle') &&\n    function_exists('swoole_event_write') &&\n    function_exists('swoole_event_wait') &&\n    function_exists('swoole_event_exit')\n);\n\n?>\n--EXPECTF--\nbool(true)\n"
  },
  {
    "path": "tests/swoole_event/isset.phpt",
    "content": "--TEST--\nswoole_event: Swoole\\Event::isset\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$fp = stream_socket_client(\"tcp://www.qq.com:80\", $errno, $errstr, 30);\nfwrite($fp, \"GET / HTTP/1.1\\r\\nHost: www.qq.com\\r\\n\\r\\n\");\n\nSwoole\\Event::add($fp, function ($fp) {\n    $resp = fread($fp, 8192);\n    //socket处理完成后，从epoll事件中移除socket\n    Swoole\\Event::del($fp);\n    fclose($fp);\n});\n\nAssert::true(Swoole\\Event::isset($fp, SWOOLE_EVENT_READ));\nAssert::false(Swoole\\Event::isset($fp, SWOOLE_EVENT_WRITE));\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_event/kqueue.phpt",
    "content": "--TEST--\nswoole_event: write()\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_not_darwin();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nswoole_async_set([\n    'enable_kqueue' => true,\n]);\n\n$fp = stream_socket_client(\"tcp://www.qq.com:80\", $errno, $errstr, 30);\n\nSwoole\\Event::add($fp, function($fp) {\n    $resp = fread($fp, 8192);\n\n    Swoole\\Event::del($fp);\n    fclose($fp);\n\n    echo \"SUCCESS\\n\";\n\n    Swoole\\Timer::after(100, function () {\n        posix_kill(posix_getpid(), SIGIO);\n        Swoole\\Timer::after(100, function () {\n            echo \"Done\\n\";\n        });\n    });\n});\n\nSwoole\\Event::write($fp, \"GET / HTTP/1.1\\r\\nHost: www.qq.com\\r\\n\\r\\n\");\n\nSwoole\\Process::signal(SIGIO, function () {\n    echo \"SIGIO received\\n\";\n    Swoole\\Process::signal(SIGIO, null);\n});\n\necho \"Finish\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECT--\nFinish\nSUCCESS\nSIGIO received\nDone\n"
  },
  {
    "path": "tests/swoole_event/rshutdown.phpt",
    "content": "--TEST--\nswoole_event: read stdin\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nerror_reporting(E_ALL & E_DEPRECATED);\n\nSwoole\\Event::add(STDIN, function ($fp) {\n    var_dump(fread($fp, 1024));\n    Swoole\\Event::del(STDIN);\n});\n\nSwoole\\Timer::after(100, function () {\n    Swoole\\Event::del(STDIN);\n    fclose(STDIN);\n});\n\n?>\n--EXPECTF--\nDeprecated: swoole_event_rshutdown(): Event::wait() in shutdown function is deprecated in Unknown on line 0\nstring(0) \"\"\n"
  },
  {
    "path": "tests/swoole_event/set.phpt",
    "content": "--TEST--\nswoole_event: Swoole\\Event::set\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$fp = stream_socket_client(\"tcp://www.qq.com:80\", $errno, $errstr, 30);\n\nSwoole\\Event::add($fp, function ($fp) {\n    $resp = fread($fp, 8192);\n\n    //socket处理完成后，从epoll事件中移除socket\n    Swoole\\Event::del($fp);\n    fclose($fp);\n\n    echo \"read_callback: SUCCESS\\n\";\n});\n\n# 设置写事件回调函数，这会替换掉原有的写事件回调函数\nSwoole\\Event::set($fp, null, function ($fp) {\n    $resp = fread($fp, 8192);\n\n    //socket处理完成后，从epoll事件中移除socket\n    Swoole\\Event::del($fp);\n    fclose($fp);\n\n    echo \"write_callback: SUCCESS\\n\";\n}, SWOOLE_EVENT_WRITE);\n\nSwoole\\Event::write($fp, \"GET / HTTP/1.1\\r\\nHost: www.qq.com\\r\\n\\r\\n\");\necho \"Finish\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECT--\nFinish\nwrite_callback: SUCCESS\n"
  },
  {
    "path": "tests/swoole_event/simple.phpt",
    "content": "--TEST--\nswoole_event: Swoole\\Event::exit\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Timer::tick(1, function() {\n    echo \"tick\\n\";\n    Swoole\\Event::exit();\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\ntick\n"
  },
  {
    "path": "tests/swoole_event/sockets.phpt",
    "content": "--TEST--\nswoole_event: add event after server start\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_extension_not_exist('sockets');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Event;\n\nconst N = 10;\nconst GREETING_MESSAGE = 'hi swoole';\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die(\"Unable to create socket\\n\");\n    socket_set_nonblock($socket) or die(\"Unable to set nonblock on socket\\n\");\n\n    function socket_onRead($socket)\n    {\n        static $i = 0;\n        $line = socket_read($socket, 8192);\n        if (!$line) {\n            exit(\"ERROR\\n\");\n        }\n        Assert::eq($line, \"Swoole: \" . GREETING_MESSAGE);\n        if ($i > 10) {\n            echo \"DONE\\n\";\n            Event::del($socket);\n            socket_close($socket);\n        } else {\n            usleep(10000);\n            $i++;\n            Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE);\n        }\n    }\n\n    function socket_onWrite($socket)\n    {\n        socket_write($socket, GREETING_MESSAGE);\n        Event::set($socket, null, null, SWOOLE_EVENT_READ);\n    }\n\n    function socket_onConnect($socket)\n    {\n        $err = socket_get_option($socket, SOL_SOCKET, SO_ERROR);\n        if ($err == 0) {\n            echo \"CONNECTED\\n\";\n            Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ);\n            socket_write($socket, GREETING_MESSAGE);\n        } else {\n            echo \"connect server failed\\n\";\n            Event::del($socket);\n            socket_close($socket);\n        }\n    }\n\n    Event::add($socket, 'socket_onRead', 'socket_onConnect', SWOOLE_EVENT_WRITE);\n    socket_connect($socket, '127.0.0.1', $pm->getFreePort());\n    Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set(array(\n        'log_file' => '/dev/null',\n    ));\n    $serv->on(\"start\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n        $serv->send($fd, \"Swoole: $data\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nCONNECTED\nDONE\n"
  },
  {
    "path": "tests/swoole_event/sync_client_1.phpt",
    "content": "--TEST--\nswoole_event: sync client\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Client;\nuse Swoole\\Event;\n\nswoole_async_set(['enable_coroutine' => false]);\n\n$fp = new Client(SWOOLE_SOCK_TCP);\n\n// sync connect\n$fp->connect('www.qq.com', 80);\n\nEvent::add($fp, function($fp) {\n    $resp = $fp->recv(8192);\n    Assert::contains($resp, 'Location: https://www.qq.com/');\n\n    Event::del($fp);\n    $fp->close();\n\n    echo \"Done\\n\";\n});\n\nEvent::write($fp, \"GET / HTTP/1.1\\r\\nHost: www.qq.com\\r\\n\\r\\n\");\nEvent::wait();\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_event/sync_client_2.phpt",
    "content": "--TEST--\nswoole_event: sync client\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Client;\nuse Swoole\\Event;\n\nswoole_async_set(['enable_coroutine' => false]);\n\n$fp = new Client(SWOOLE_SOCK_TCP);\n// async connect\n$result = $fp->connect('www.qq.com', 80, 1, 1);\n\nAssert::true($result);\nAssert::eq($fp->errCode, SOCKET_EINPROGRESS);\n\nEvent::add($fp, null, function (Client $fp) {\n    $fp->send(\"GET / HTTP/1.1\\r\\nHost: www.qq.com\\r\\n\\r\\n\");\n    Event::set($fp, function ($fp) {\n        $resp = $fp->recv(8192);\n        Assert::contains($resp, 'Location: https://www.qq.com/');\n\n        Event::del($fp);\n        $fp->close();\n\n        echo \"Done\\n\";\n    }, null, SWOOLE_EVENT_READ);\n}, SWOOLE_EVENT_WRITE);\n\nEvent::wait();\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_event/wait.phpt",
    "content": "--TEST--\nswoole_event: wait (auto)\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nregister_shutdown_function(function () {\n    echo \"register 1\\n\";\n    register_shutdown_function(function () {\n        echo \"register 3\\n\";\n        register_shutdown_function(function () {\n            echo \"register 7\\n\";\n        });\n    });\n});\n\ngo(function () {\n    co::sleep(0.1);\n    register_shutdown_function(function () {\n        echo \"register 4\\n\";\n    });\n    register_shutdown_function(function () {\n        echo \"register 5\\n\";\n    });\n});\n\nregister_shutdown_function(function () {\n    echo \"register 2\\n\";\n    register_shutdown_function(function () {\n        echo \"register 6\\n\";\n        register_shutdown_function(function () {\n            echo \"register 8\\n\";\n        });\n    });\n});\n?>\n--EXPECT--\nregister 1\nregister 2\nregister 3\nregister 4\nregister 5\nregister 6\nregister 7\nregister 8\n"
  },
  {
    "path": "tests/swoole_event/write.phpt",
    "content": "--TEST--\nswoole_event: write()\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$fp = stream_socket_client(\"tcp://www.qq.com:80\", $errno, $errstr, 30);\n\nSwoole\\Event::add($fp, function($fp) {\n    $resp = fread($fp, 8192);\n\n    //socket处理完成后，从epoll事件中移除socket\n    Swoole\\Event::del($fp);\n    fclose($fp);\n\n    echo \"SUCCESS\\n\";\n});\n\nSwoole\\Event::write($fp, \"GET / HTTP/1.1\\r\\nHost: www.qq.com\\r\\n\\r\\n\");\n\necho \"Finish\\n\";\nSwoole\\Event::wait();\n?>\n--EXPECT--\nFinish\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_feature/cross_close/client.phpt",
    "content": "--TEST--\nswoole_feature/cross_close: client\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new ProcessManager();\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n        Assert::assert($cli->connect('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($cli->connected);\n        echo \"RECV\\n\";\n        go(function () use ($pm, $cli) {\n            Co::sleep(0.001);\n            echo \"CLOSE\\n\";\n            $cli->close();\n            $pm->kill();\n            echo \"DONE\\n\";\n        });\n        Assert::false(@$cli->recv(-1));\n        Assert::same($cli->errCode, SOCKET_ECANCELED);\n        echo \"CLOSED\\n\";\n        Assert::assert(!$cli->connected);\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::assert($server->bind('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($server->listen());\n        go(function () use ($pm, $server) {\n            if (Assert::assert(($conn = $server->accept()) && $conn instanceof Co\\Socket)) {\n                switch_process();\n                co::sleep(5);\n                $conn->close();\n            }\n            $server->close();\n        });\n        $pm->wakeup();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nRECV\nCLOSE\nCLOSED\nDONE\n"
  },
  {
    "path": "tests/swoole_feature/cross_close/client_by_server.phpt",
    "content": "--TEST--\nswoole_feature/cross_close: client closed by server\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Co\\Client;\nuse Co\\Socket;\n\n$pm = new ProcessManager();\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Client(SWOOLE_SOCK_TCP);\n        Assert::assert($cli->connect('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($cli->connected);\n        echo \"RECV\\n\";\n        Assert::same($cli->recv(-1), '');\n        echo \"CLOSED\\n\";\n        while ($ret = @$cli->send(get_safe_random())) {\n            continue;\n        }\n        if ($cli->errCode) {\n            Assert::same($cli->errCode, SOCKET_EPIPE);\n        }\n        while ($ret = @$cli->recv(-1)) {\n            continue;\n        }\n        if ($ret === false) {\n            Assert::same($cli->errCode, IS_MAC_OS ? SOCKET_EPIPE : SOCKET_ECONNRESET);\n        }\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::assert($server->bind('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($server->listen());\n        go(function () use ($pm, $server) {\n            if (Assert::assert(($conn = $server->accept()) && $conn instanceof Socket)) {\n                switch_process();\n                echo \"CLOSE\\n\";\n                $conn->close();\n                switch_process();\n            }\n            $server->close();\n        });\n        $pm->wakeup();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nRECV\nCLOSE\nCLOSED\n"
  },
  {
    "path": "tests/swoole_feature/cross_close/full_duplex.phpt",
    "content": "--TEST--\nswoole_feature/cross_close: full duplex\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new ProcessManager();\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n        Assert::assert($cli->connect('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($cli->connected);\n        set_socket_coro_buffer_size($cli->exportSocket(), 8192);\n        go(function () use ($pm, $cli) {\n            Co::sleep(0.001);\n            echo \"CLOSE\\n\";\n            $cli->close();\n            $pm->kill();\n            echo \"DONE\\n\";\n        });\n        go(function () use ($cli) {\n            echo \"SEND\\n\";\n            $size = 16 * 1024 * 1024;\n            Assert::lessThan($cli->send(str_repeat('S', $size)), $size);\n            Assert::assert(!$cli->connected);\n            Assert::eq($cli->errCode, SOCKET_ECANCELED);\n            echo \"SEND CLOSED\\n\";\n        });\n        go(function () use ($cli) {\n            echo \"RECV\\n\";\n            Assert::false($cli->recv(-1));\n            Assert::assert(!$cli->connected);\n            Assert::eq($cli->errCode, SOCKET_ECANCELED);\n            echo \"RECV CLOSED\\n\";\n        });\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::assert($server->bind('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($server->listen());\n        go(function () use ($pm, $server) {\n            if (Assert::assert(($conn = $server->accept()) && $conn instanceof Co\\Socket)) {\n                switch_process();\n                co::sleep(5);\n                $conn->close();\n            }\n            $server->close();\n        });\n        $pm->wakeup();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSEND\nRECV\nCLOSE\nSEND CLOSED\nRECV CLOSED\nDONE\n"
  },
  {
    "path": "tests/swoole_feature/cross_close/full_duplex_by_server.phpt",
    "content": "--TEST--\nswoole_feature/cross_close: full duplex and close by server\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Co\\Client;\nuse Co\\Socket;\nuse Swoole\\Event;\n\n$pm = new ProcessManager();\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Client(SWOOLE_SOCK_TCP);\n        Assert::assert($cli->connect('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($cli->connected);\n        set_socket_coro_buffer_size($cli->exportSocket(), 65536);\n        go(function () use ($cli) {\n            echo \"SEND\\n\";\n            $size = 16 * 1024 * 1024;\n            $str = str_repeat('S', $size);\n            Assert::assert($cli->send($str) < $size);\n            usleep(1000);\n            Assert::assert($cli->send($str) < $size);\n            Assert::same($cli->errCode, SOCKET_EPIPE);\n            echo \"SEND CLOSED\\n\";\n        });\n        go(function () use ($cli) {\n            echo \"RECV\\n\";\n            Assert::eq($cli->recv(-1), '');\n            // Assert::same($cli->errCode, SOCKET_ECONNRESET);\n            echo \"RECV CLOSED\\n\";\n        });\n        $pm->wakeup();\n    });\n    Event::wait();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::assert($server->bind('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($server->listen());\n        go(function () use ($pm, $server) {\n            if (Assert::assert(($conn = $server->accept()) && $conn instanceof Socket)) {\n                $pm->wait();\n                echo \"CLOSE\\n\";\n                $conn->close();\n                switch_process();\n            }\n            $server->close();\n        });\n        $pm->wakeup();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nSEND\nRECV\nCLOSE\n%s CLOSED\n%s CLOSED\nDONE\n"
  },
  {
    "path": "tests/swoole_feature/cross_close/http.phpt",
    "content": "--TEST--\nswoole_feature/cross_close: http client\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager();\n\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $http = new Co\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        echo \"GET\\n\";\n        go(function () use ($pm, $http) {\n            Co::sleep(0.001);\n            echo \"CLOSE\\n\";\n            $http->close();\n            $pm->kill();\n            echo \"DONE\\n\";\n        });\n        Assert::assert(!$http->get('/'));\n        echo \"CLOSED\\n\";\n        Assert::same($http->statusCode, SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET);\n        Assert::same($http->errCode, SOCKET_ECANCELED);\n        Assert::assert(empty($http->body));\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set(['log_file' => '/dev/null']);\n    $server->on('workerStart', function () use ($pm) { $pm->wakeup(); });\n    $server->on('request', function ($request, Swoole\\Http\\Response $response) use ($server) {\n        switch_process();\n        co::sleep(3);\n        $server->close($response->fd);\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nGET\nCLOSE\nCLOSED\nDONE\n"
  },
  {
    "path": "tests/swoole_feature/cross_close/http_by_server.phpt",
    "content": "--TEST--\nswoole_feature/cross_close: http client closed by server\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new ProcessManager();\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $http = new Co\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        echo \"GET\\n\";\n        Assert::assert(!$http->get('/'));\n        echo \"CLOSED\\n\";\n        Assert::same($http->statusCode, SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET);\n        Assert::same($http->errCode, SOCKET_ECONNRESET);\n        Assert::assert(empty($http->body));\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $server->set(['log_file' => '/dev/null']);\n    $server->on('workerStart', function () use ($pm) { $pm->wakeup(); });\n    $server->on('request', function ($request, Swoole\\Http\\Response $response) use ($server) {\n        switch_process();\n        echo \"CLOSE\\n\";\n        $server->close($response->fd);\n        switch_process();\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nGET\nCLOSE\nCLOSED\n"
  },
  {
    "path": "tests/swoole_feature/cross_close/php_stream_full_duplex.phpt",
    "content": "--TEST--\nswoole_feature/cross_close: full duplex (php stream)\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\n\nSwoole\\Runtime::enableCoroutine();\n$pm = new ProcessManager();\n$pm->parentFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $cli = stream_socket_client(\"tcp://127.0.0.1:{$pm->getFreePort()}\", $errno, $errstr, 1);\n        Assert::true(!$errno);\n        go(function () use ($pm, $cli) {\n            Co::sleep(0.001);\n            echo \"CLOSE\\n\";\n            Assert::eq(get_resource_type($cli), 'stream');\n            Assert::true(fclose($cli));\n            // double close\n            Assert::eq(get_resource_type($cli), 'Unknown');\n            // Assert::true(!@fclose($cli));\n            $pm->kill();\n            echo \"DONE\\n\";\n        });\n        go(function () use ($cli) {\n            echo \"SEND\\n\";\n            $size = 64 * 1024 * 1024;\n            Assert::eq(get_resource_type($cli), 'stream');\n            Assert::true(@fwrite($cli, str_repeat('S', $size)) < $size);\n            Assert::eq(get_resource_type($cli), 'Unknown');\n            // Assert::true(!@fclose($cli));\n            echo \"SEND CLOSED\\n\";\n        });\n        go(function () use ($cli) {\n            echo \"RECV\\n\";\n            Assert::eq(get_resource_type($cli), 'stream');\n            Assert::true(empty(fread($cli, 8192)));\n            Assert::eq(get_resource_type($cli), 'Unknown');\n            // Assert::true(!@fclose($cli));\n            echo \"RECV CLOSED\\n\";\n        });\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $server = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::true($server->bind('127.0.0.1', $pm->getFreePort()));\n        Assert::true($server->listen());\n        go(function () use ($pm, $server) {\n            if (Assert::true(($conn = $server->accept()) && $conn instanceof Co\\Socket)) {\n                switch_process();\n                co::sleep(5);\n                $conn->close();\n            }\n            $server->close();\n        });\n        $pm->wakeup();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSEND\nRECV\nCLOSE\nSEND CLOSED\nRECV CLOSED\nDONE\n"
  },
  {
    "path": "tests/swoole_feature/cross_close/php_stream_full_duplex_by_server.phpt",
    "content": "--TEST--\nswoole_feature/cross_close: full duplex and close by server (php stream)\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\n$pm = new ProcessManager();\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = stream_socket_client(\"tcp://127.0.0.1:{$pm->getFreePort()}\", $errno, $errstr, 1);\n        Assert::true(!$errno);\n        go(function () use ($cli) {\n            echo \"SEND\\n\";\n            $size = 64 * 1024 * 1024;\n            Assert::true(@fwrite($cli, str_repeat('S', $size)) < $size);\n            echo \"SEND CLOSED\\n\";\n        });\n        go(function () use ($cli) {\n            echo \"RECV\\n\";\n            Assert::true(!fread($cli, 8192));\n            echo \"RECV CLOSED\\n\";\n        });\n        $pm->wakeup();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::true($server->bind('127.0.0.1', $pm->getFreePort()));\n        Assert::true($server->listen());\n        go(function () use ($pm, $server) {\n            if (Assert::true(($conn = $server->accept()) && $conn instanceof Co\\Socket)) {\n                $pm->wait();\n                echo \"CLOSE\\n\";\n                $conn->close();\n                switch_process();\n            }\n            $server->close();\n        });\n        $pm->wakeup();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nSEND\nRECV\nCLOSE\n%s CLOSED\n%s CLOSED\nDONE\n"
  },
  {
    "path": "tests/swoole_feature/cross_close/redis.phpt",
    "content": "--TEST--\nswoole_feature/cross_close: redis\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new ProcessManager();\n$pm->initRandomData(1);\n$pm->parentFunc = function () use ($pm) {\n    Swoole\\Runtime::setHookFlags(SWOOLE_HOOK_ALL);\n    $redis = new \\redis;\n    go(function () use ($pm, $redis) {\n        $redis->connect('127.0.0.1', $pm->getFreePort());\n        go(function () use ($pm, $redis) {\n            echo \"GET\\n\";\n            go(function () use ($pm, $redis) {\n                co::sleep(0.001);\n                echo \"CLOSE\\n\";\n                Assert::assert($redis->close());\n                echo \"DONE\\n\";\n                $pm->kill();\n            });\n            try {\n                $ret = $redis->get($pm->getRandomData());\n            } catch (\\RedisException $e) {\n                $ret = false;\n                echo \"CLOSED\\n\";\n            }\n            Assert::assert(!$ret);\n        });\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::assert($server->bind('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($server->listen());\n        go(function () use ($pm, $server) {\n            $pm->wakeup();\n            if (Assert::assert(($conn = $server->accept()) && $conn instanceof Co\\Socket)) {\n                switch_process();\n                $data = $conn->recv();\n                $random = $pm->getRandomData();\n                $random_len = strlen($random);\n                Assert::same($data, \"*2\\r\\n$3\\r\\nGET\\r\\n\\${$random_len}\\r\\n{$random}\\r\\n\");\n                switch_process();\n                co::sleep(5);\n                $conn->close();\n            }\n            $server->close();\n        });\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nGET\nCLOSE\nCLOSED\nDONE\n"
  },
  {
    "path": "tests/swoole_feature/cross_close/redis_by_server.phpt",
    "content": "--TEST--\nswoole_feature/cross_close: redis closed by server\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new ProcessManager();\n$pm->initRandomData(1);\n$pm->parentFunc = function () use ($pm) {\n    Swoole\\Runtime::setHookFlags(SWOOLE_HOOK_ALL);\n    go(function () use ($pm) {\n        $redis = new \\redis;\n        Assert::assert($redis->connect('127.0.0.1', $pm->getFreePort()));\n        echo \"GET\\n\";\n        try {\n            $redis->get($pm->getRandomData());\n        } catch (\\RedisException $e) {\n            echo \"CLOSED\\n\";\n        }\n        $pm->kill();\n        echo \"DONE\\n\";\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    Assert::assert($server->bind('127.0.0.1', $pm->getFreePort()));\n    Assert::assert($server->listen());\n    go(function () use ($pm, $server) {\n        if (Assert::assert(($conn = $server->accept()) && $conn instanceof Co\\Socket)) {\n            switch_process();\n            $data = $conn->recv();\n            $random = $pm->getRandomData();\n            $random_len = strlen($random);\n            Assert::same($data, \"*2\\r\\n$3\\r\\nGET\\r\\n\\${$random_len}\\r\\n{$random}\\r\\n\");\n            echo \"CLOSE\\n\";\n            $conn->close();\n            switch_process();\n        }\n        $server->close();\n    });\n    $pm->wakeup();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nGET\nCLOSE\nCLOSED\nDONE\n"
  },
  {
    "path": "tests/swoole_feature/cross_close/stream.phpt",
    "content": "--TEST--\nswoole_feature/cross_close: stream\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine\\System;\n\n$pm = ProcessManager::exec(function ($pm) {\n    Swoole\\Runtime::enableCoroutine();\n    run(function () {\n        $fp = stream_socket_client('tcp://' . REDIS_SERVER_HOST . ':' . REDIS_SERVER_PORT, $errno, $errstr, 1);\n        if (!$fp) {\n            exit(\"$errstr ($errno)\\n\");\n        } else {\n            go(function () use ($fp) {\n                System::sleep(0.001);\n                echo \"CLOSE\\n\";\n                fclose($fp);\n                echo \"DONE\\n\";\n            });\n            echo \"READ\\n\";\n            Assert::assert(!fread($fp, 1024));\n            echo \"CLOSED\\n\";\n            fclose($fp);\n        }\n    });\n});\n\n$output = $pm->getChildOutput();\nAssert::contains($output, \"READ\\nCLOSE\\nCLOSED\\n\");\n\nif (PHP_VERSION_ID < 80500) {\n    Assert::contains($output, \"Fatal error: Uncaught TypeError: fclose(): supplied resource is not a valid stream resource\");\n} else {\n    Assert::contains($output, 'Fatal error: Uncaught TypeError: fclose(): Argument #1 ($stream) must be an open stream resource');\n}\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_feature/cross_close/stream_by_server.phpt",
    "content": "--TEST--\nswoole_feature/cross_close: stream closed by server\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $fp = stream_socket_client(\"tcp://127.0.0.1:{$pm->getFreePort()}\", $errno, $errstr, 1);\n        if (!$fp) {\n            exit(\"$errstr ($errno)\\n\");\n        } else {\n            echo \"WRITE\\n\";\n            Assert::same(fwrite($fp, ($data = tcp_pack(\"Hello Swoole Server!\"))), strlen($data));\n            echo \"READ\\n\";\n            Assert::same(fread($fp, 1024), '');\n            echo \"CLOSED\\n\";\n            fclose($fp);\n            echo \"DONE\\n\";\n        }\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $ctx = stream_context_create(['socket' => ['so_reuseaddr' => true, 'backlog' => MAX_CONCURRENCY_MID]]);\n        $server = stream_socket_server(\n            \"tcp://127.0.0.1:{$pm->getFreePort()}\",\n            $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctx\n        );\n        if (!$server) {\n            exit(\"$errstr ($errno)\\n\");\n        } else {\n            go(function () use ($server) {\n                if ($conn = stream_socket_accept($server, 1)) {\n                    switch_process();\n                    Assert::same(fread($conn, tcp_length(fread($conn, tcp_type_length()))), \"Hello Swoole Server!\");\n                    echo \"CLOSE\\n\";\n                    fclose($conn);\n                    switch_process();\n                }\n                fclose($server);\n            });\n        }\n        $pm->wakeup();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n\nWRITE\nREAD\nCLOSE\nCLOSED\nDONE\n"
  },
  {
    "path": "tests/swoole_feature/full_duplex/client.phpt",
    "content": "--TEST--\nswoole_feature/full_duplex: client\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nconst CHUNK_SIZE = 128 * 1024; // 128K\nconst CHUNK_NUM = 8; // 1M\nconst BUFFER_SIZE = CHUNK_SIZE / 2; // 64K\n\n$pm = new ProcessManager;\n$pm->initRandomDataEx(MAX_CONCURRENCY_LOW, MAX_REQUESTS_LOW, CHUNK_SIZE);\n$pm->parentFunc = function ($pid) use ($pm) {\n    global $closer;\n    $closer = go(function () {\n        $closer = co::getCid();\n        $timer = Swoole\\Timer::after(10 * 1000, function () use ($closer) {\n            echo \"TIMEOUT\\n\";\n            co::resume($closer);\n        });\n        co::yield();\n        if (Swoole\\Timer::exists($timer)) {\n            Swoole\\Timer::clear($timer);\n        }\n        global $clients;\n        foreach ($clients as $client) {\n            $client->close();\n        }\n    });\n    for ($c = 0; $c < MAX_CONCURRENCY_LOW; $c++) {\n        go(function () use ($pm, $c) {\n            global $clients;\n            $clients[] = $client = new Co\\Client(SWOOLE_SOCK_TCP);\n            $ret = $client->connect('127.0.0.1', $pm->getFreePort(), -1);\n            if (!Assert::assert($ret)) {\n                throw new RuntimeException('connect failed');\n            } else {\n                set_socket_coro_buffer_size($client->exportSocket(), BUFFER_SIZE);\n                $client->set([\n                    'open_eof_check' => true,\n                    'package_eof' => \"\\n\"\n                ]);\n            }\n            // read\n            go(function () use ($pm, $client, $c) {\n                for ($n = 0; $n < MAX_REQUESTS_LOW; $n++) {\n                    // id\n                    if (!$client->send(tcp_head($c))) {\n                        break;\n                    }\n                    // length\n                    if (!$client->send(tcp_head(CHUNK_SIZE * CHUNK_NUM, 'N'))) {\n                        break;\n                    }\n                    // data\n                    $data = $pm->getRandomDataEx($c);\n                    for ($p = CHUNK_NUM; $p--;) {\n                        $send_n = 0;\n                        do {\n                            $n_bytes = $client->send(substr($data, $send_n));\n                            if (!$n_bytes) {\n                                break;\n                            }\n                            $send_n += $n_bytes;\n                        } while ($send_n !== CHUNK_SIZE);\n                    }\n                }\n            });\n            // write\n            go(function () use ($pm, $client) {\n                while (($id = $client->recv(-1))) {\n                    global $count, $closer;\n                    $id = rtrim($id);\n                    @$count[$id]++;\n                    if (array_sum($count) === MAX_CONCURRENCY_LOW * MAX_REQUESTS_LOW * CHUNK_NUM) {\n                        phpt_var_dump($count);\n                        co::resume($closer);\n                        echo \"DONE\\n\";\n                    }\n                }\n            });\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::assert($server->bind('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($server->listen(MAX_CONCURRENCY));\n        while ($conn = $server->accept(-1)) {\n            if (!Assert::assert($conn instanceof Co\\Socket)) {\n                throw new RuntimeException('accept failed');\n            } else {\n                set_socket_coro_buffer_size($conn, BUFFER_SIZE);\n            }\n            go(function () use ($pm, $conn) {\n                while (true) {\n                    // id\n                    $head = $conn->recv(tcp_type_length(), -1);\n                    if (!$head || ($id = tcp_length($head)) < 0) {\n                        break;\n                    }\n                    // length\n                    $length = tcp_length($conn->recv(tcp_type_length('N'), -1), 'N');\n                    // data\n                    $verify = $pm->getRandomDataEx($id);\n                    do {\n                        $data = '';\n                        $need_n = CHUNK_SIZE;\n                        do {\n                            $tmp = $conn->recv($need_n, -1);\n                            if (!$tmp) {\n                                break;\n                            }\n                            $data .= $tmp;\n                            $need_n = CHUNK_SIZE - strlen($data);\n                        } while ($need_n !== 0);\n                        if (!Assert::assert($data === $verify)) {\n                            break;\n                        }\n                        $length -= strlen($data);\n                        $conn->send(\"{$id}\\n\");\n                    } while ($length > 0);\n                }\n            });\n        }\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_feature/full_duplex/socket.phpt",
    "content": "--TEST--\nswoole_feature/full_duplex: socket\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nconst CHUNK_SIZE = 128 * 1024; // 128K\nconst CHUNK_NUM = 8; // 1M\nconst BUFFER_SIZE = CHUNK_SIZE / 2; // 64K\n\n$pm = new ProcessManager;\n$pm->initRandomDataEx(MAX_CONCURRENCY_LOW, MAX_REQUESTS_LOW, CHUNK_SIZE);\n$pm->parentFunc = function ($pid) use ($pm) {\n    global $closer;\n    $closer = go(function () {\n        $closer = co::getCid();\n        $timer = Swoole\\Timer::after(10 * 1000, function () use ($closer) {\n            echo \"TIMEOUT\\n\";\n            co::resume($closer);\n        });\n        co::yield();\n        if (Swoole\\Timer::exists($timer)) {\n            Swoole\\Timer::clear($timer);\n        }\n        global $sockets;\n        foreach ($sockets as $socket) {\n            $socket->close();\n        }\n    });\n    for ($c = 0; $c < MAX_CONCURRENCY_LOW; $c++) {\n        go(function () use ($pm, $c) {\n            global $sockets;\n            $sockets[] = $socket = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n            $ret = $socket->connect('127.0.0.1', $pm->getFreePort(), -1);\n            if (!Assert::assert($ret)) {\n                throw new RuntimeException('connect failed');\n            } else {\n                set_socket_coro_buffer_size($socket, BUFFER_SIZE);\n            }\n            // read\n            go(function () use ($pm, $socket, $c) {\n                for ($n = 0; $n < MAX_REQUESTS_LOW; $n++) {\n                    // id\n                    if (!$socket->send(tcp_head($c))) {\n                        break;\n                    }\n                    // length\n                    if (!$socket->send(tcp_head(CHUNK_SIZE * CHUNK_NUM, 'N'))) {\n                        break;\n                    }\n                    // data\n                    $data = $pm->getRandomDataEx($c);\n                    for ($p = CHUNK_NUM; $p--;) {\n                        $send_n = 0;\n                        do {\n                            $n_bytes = $socket->send(substr($data, $send_n));\n                            if (!$n_bytes) {\n                                break;\n                            }\n                            $send_n += $n_bytes;\n                        } while ($send_n !== CHUNK_SIZE);\n                    }\n                }\n            });\n            // write\n            go(function () use ($pm, $socket) {\n                while ($data = $socket->recv(tcp_type_length(), -1)) {\n                    global $count, $closer;\n                    @$count[tcp_length($data)]++;\n                    if (array_sum($count) === MAX_CONCURRENCY_LOW * MAX_REQUESTS_LOW * CHUNK_NUM) {\n                        phpt_var_dump($count);\n                        co::resume($closer);\n                    }\n                }\n            });\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::assert($server->bind('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($server->listen(MAX_CONCURRENCY));\n        while ($conn = $server->accept(-1)) {\n            if (!Assert::assert($conn instanceof Co\\Socket)) {\n                throw new RuntimeException('accept failed');\n            } else {\n                set_socket_coro_buffer_size($conn, BUFFER_SIZE);\n            }\n            go(function () use ($pm, $conn) {\n                while (true) {\n                    // id\n                    $head = $conn->recv(tcp_type_length(), -1);\n                    if (!$head || ($id = tcp_length($head)) < 0) {\n                        break;\n                    }\n                    // length\n                    $length = tcp_length($conn->recv(tcp_type_length('N'), -1), 'N');\n                    // data\n                    $verify = $pm->getRandomDataEx($id);\n                    do {\n                        $data = '';\n                        $need_n = CHUNK_SIZE;\n                        do {\n                            $data .= $conn->recv($need_n, -1);\n                            $need_n = CHUNK_SIZE - strlen($data);\n                        } while ($need_n !== 0);\n                        if (!Assert::assert($data === $verify)) {\n                            break;\n                        }\n                        $length -= strlen($data);\n                        $conn->send(tcp_head($id));\n                    } while ($length > 0);\n                }\n            });\n        }\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_feature/full_duplex/socket_ssl.phpt",
    "content": "--TEST--\nswoole_feature/full_duplex: socket\n--SKIPIF--\n<?php use Co\\Socket;\n\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nconst CHUNK_SIZE = 128 * 1024; // 128K\nconst CHUNK_NUM = 8; // 1M\nconst BUFFER_SIZE = CHUNK_SIZE / 2; // 64K\n\n$pm = new ProcessManager;\n$pm->initRandomDataEx(MAX_CONCURRENCY_LOW, MAX_REQUESTS_LOW, CHUNK_SIZE);\n$pm->parentFunc = function ($pid) use ($pm) {\n    global $closer;\n    $closer = go(function () {\n        $closer = co::getCid();\n        $timer = Swoole\\Timer::after(10 * 1000, function () use ($closer) {\n            echo \"TIMEOUT\\n\";\n            co::resume($closer);\n        });\n        co::yield();\n        if (Swoole\\Timer::exists($timer)) {\n            Swoole\\Timer::clear($timer);\n        }\n        global $sockets;\n        foreach ($sockets as $socket) {\n            $socket->close();\n        }\n    });\n    for ($c = 0; $c < MAX_CONCURRENCY_LOW; $c++) {\n        go(function () use ($pm, $c) {\n            global $sockets;\n            $sockets[] = $socket = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n            $socket->setProtocol(['open_ssl' => true]);\n            $ret = $socket->connect('127.0.0.1', $pm->getFreePort(), -1);\n            if (!Assert::assert($ret)) {\n                throw new RuntimeException('connect failed');\n            } else {\n                set_socket_coro_buffer_size($socket, BUFFER_SIZE);\n            }\n            // read\n            go(function () use ($pm, $socket, $c) {\n                for ($n = 0; $n < MAX_REQUESTS_LOW; $n++) {\n                    // id\n                    if (!$socket->send(tcp_head($c))) {\n                        break;\n                    }\n                    // length\n                    if (!$socket->send(tcp_head(CHUNK_SIZE * CHUNK_NUM, 'N'))) {\n                        break;\n                    }\n                    // data\n                    $data = $pm->getRandomDataEx($c);\n                    for ($p = CHUNK_NUM; $p--;) {\n                        $send_n = 0;\n                        do {\n                            $n_bytes = $socket->send(substr($data, $send_n));\n                            if (!$n_bytes) {\n                                break;\n                            }\n                            $send_n += $n_bytes;\n                        } while ($send_n !== CHUNK_SIZE);\n                    }\n                }\n            });\n            // write\n            go(function () use ($pm, $socket) {\n                while ($data = $socket->recv(tcp_type_length(), -1)) {\n                    global $count, $closer;\n                    @$count[tcp_length($data)]++;\n                    if (array_sum($count) === MAX_CONCURRENCY_LOW * MAX_REQUESTS_LOW * CHUNK_NUM) {\n                        phpt_var_dump($count);\n                        co::resume($closer);\n                    }\n                }\n            });\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        $server->setProtocol([\n            'open_ssl' => true,\n            'ssl_cert_file' => SSL_FILE_DIR.'/client.crt',\n            'ssl_key_file' => SSL_FILE_DIR.'/client.key'\n        ]);\n        Assert::assert($server->bind('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($server->listen(MAX_CONCURRENCY));\n        /** @var $conn Socket */\n        while ($conn = $server->accept(-1)) {\n            if (!Assert::assert($conn instanceof Co\\Socket)) {\n                throw new RuntimeException('accept failed');\n            } else {\n                set_socket_coro_buffer_size($conn, BUFFER_SIZE);\n            }\n            go(function () use ($pm, $conn) {\n                if (!Assert::true($conn->sslHandshake())) {\n                    return;\n                }\n                while (true) {\n                    // id\n                    $head = $conn->recv(tcp_type_length(), -1);\n                    if (!$head || ($id = tcp_length($head)) < 0) {\n                        break;\n                    }\n                    // length\n                    $length = tcp_length($conn->recv(tcp_type_length('N'), -1), 'N');\n                    // data\n                    $verify = $pm->getRandomDataEx($id);\n                    do {\n                        $data = '';\n                        $need_n = CHUNK_SIZE;\n                        do {\n                            $data .= $conn->recv($need_n, -1);\n                            $need_n = CHUNK_SIZE - strlen($data);\n                        } while ($need_n !== 0);\n                        if (!Assert::assert($data === $verify)) {\n                            break;\n                        }\n                        $length -= strlen($data);\n                        $conn->send(tcp_head($id));\n                    } while ($length > 0);\n                }\n            });\n        }\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_feature/full_duplex/websocket.phpt",
    "content": "--TEST--\nswoole_feature/full_duplex: websocket\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    for ($c = MAX_CONCURRENCY_MID; $c--;) {\n        go(function () use ($pm, $c) {\n            $cli = new Co\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            $cli->set(['timeout' => -1]);\n            $ret = $cli->upgrade('/');\n            Assert::true($ret);\n            if ($ret) {\n                $randoms = [];\n                for ($n = MAX_REQUESTS; $n--;) {\n                    $randoms[] = get_safe_random();\n                }\n                go(function () use ($cli, $randoms) {\n                    for ($n = MAX_REQUESTS; $n--;) {\n                        $ret = $cli->push(json_encode([$n, $randoms[$n]]));\n                        Assert::true($ret);\n                    }\n                });\n                go(function () use ($cli, $randoms) {\n                    for ($n = MAX_REQUESTS; $n--;) {\n                        $frame = $cli->recv();\n                        list($_n, $data) = json_decode($frame->data);\n                        Assert::same($randoms[$_n], $data);\n                    }\n                });\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set(['log_file' => '/dev/null']);\n    $serv->on('workerStart', function () use ($pm) { $pm->wakeup(); });\n    $serv->on('message', function (Swoole\\WebSocket\\Server $server, Swoole\\WebSocket\\Frame $frame) {\n        $server->push($frame->fd, $frame->data);\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_ftp/001.phpt",
    "content": "--TEST--\nFTP login\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn){\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n    var_dump(ftp_raw($ftp, 'HELP'));\n    var_dump(ftp_raw($ftp, 'HELP HELP'));\n\n    var_dump(ftp_close($ftp));\n});\n?>\n--EXPECT--\nbool(true)\narray(4) {\n  [0]=>\n  string(55) \"214-There is help available for the following commands:\"\n  [1]=>\n  string(5) \" USER\"\n  [2]=>\n  string(5) \" HELP\"\n  [3]=>\n  string(15) \"214 end of list\"\n}\narray(1) {\n  [0]=>\n  string(39) \"214 Syntax: HELP [<SP> <string>] <CRLF>\"\n}\nbool(true)\n"
  },
  {
    "path": "tests/swoole_ftp/002.phpt",
    "content": "--TEST--\nFTP login (SSL)\n--SKIPIF--\n<?php\nif (!function_exists(\"ftp_ssl_connect\")) die(\"skip ftp_ssl is disabled\");\n?>\n--FILE--\n<?php\n$ssl = 1;\n$fn = require 'server.inc';\nCo\\run(function () use ($fn){\n    $ftp = ftp_ssl_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n    var_dump(ftp_raw($ftp, 'HELP'));\n    var_dump(ftp_raw($ftp, 'HELP HELP'));\n\n    var_dump(ftp_close($ftp));\n});\n?>\n--EXPECT--\nbool(true)\narray(4) {\n  [0]=>\n  string(55) \"214-There is help available for the following commands:\"\n  [1]=>\n  string(5) \" USER\"\n  [2]=>\n  string(5) \" HELP\"\n  [3]=>\n  string(15) \"214 end of list\"\n}\narray(1) {\n  [0]=>\n  string(39) \"214 Syntax: HELP [<SP> <string>] <CRLF>\"\n}\nbool(true)\n"
  },
  {
    "path": "tests/swoole_ftp/003.phpt",
    "content": "--TEST--\nFTP cwd\n--FILE--\n<?php\n$fn = require 'server.inc';\nCo\\run(function () use ($fn){\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n\n    var_dump(ftp_pwd($ftp));\n\n    var_dump(ftp_chdir($ftp, 'mydir'));\n    var_dump(ftp_pwd($ftp));\n\n    var_dump(ftp_chdir($ftp, '/xpto/mydir'));\n    var_dump(ftp_pwd($ftp));\n\n    var_dump(ftp_cdup($ftp));\n    var_dump(ftp_pwd($ftp));\n\n    var_dump(ftp_chdir($ftp, '..'));\n    var_dump(ftp_pwd($ftp));\n\n    var_dump(ftp_close($ftp));\n});\n?>\n--EXPECT--\nbool(true)\nstring(1) \"/\"\nbool(true)\nstring(6) \"/mydir\"\nbool(true)\nstring(11) \"/xpto/mydir\"\nbool(true)\nstring(5) \"/xpto\"\nbool(true)\nstring(1) \"/\"\nbool(true)\n"
  },
  {
    "path": "tests/swoole_ftp/004.phpt",
    "content": "--TEST--\nFTP with bogus parameters\n--FILE--\n<?php\n$fn = require 'server.inc';\nCo\\run(function () use ($fn){\n    // Negative timeout\n    try {\n        ftp_connect('127.0.0.1', 0, -3);\n    } catch (ValueError $exception) {\n        echo $exception->getMessage() . \"\\n\";\n    }\n\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n    var_dump(ftp_login($ftp, 'user', 'bogus'));\n\n    var_dump(ftp_quit($ftp));\n});\n?>\n--EXPECTF--\nftp_connect(): Argument #3 ($timeout) must be greater than 0\nbool(true)\n\nWarning: ftp_login(): Not logged in. in %s on line %d\nbool(false)\nbool(false)\n"
  },
  {
    "path": "tests/swoole_ftp/005.phpt",
    "content": "--TEST--\nFTP with bogus server responses\n--FILE--\n<?php\n$bogus = 1;\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn){\n    $port = $fn();\n    $ftp = ftp_connect('127.0.0.1', $port);\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'anonymous', 'mail@example.com'));\n\n    var_dump(ftp_alloc($ftp, 400));\n    var_dump(ftp_cdup($ftp));\n    var_dump(ftp_chdir($ftp, '~'));\n    var_dump(ftp_chmod($ftp, 0666, 'x'));\n    var_dump(ftp_delete($ftp, 'x'));\n    var_dump(ftp_exec($ftp, 'x'));\n    try {\n        ftp_fget($ftp, STDOUT, 'x', 0);\n    } catch (ValueError $exception) {\n        echo $exception->getMessage() . \"\\n\";\n    }\n\n    try {\n        ftp_fput($ftp, 'x', fopen(__FILE__, 'r'), 0);\n    } catch (ValueError $exception) {\n        echo $exception->getMessage() . \"\\n\";\n    }\n\n    try {\n        ftp_get($ftp, 'x', 'y', 0);\n    } catch (ValueError $exception) {\n        echo $exception->getMessage() . \"\\n\";\n    }\n\n    var_dump(ftp_mdtm($ftp, 'x'));\n    var_dump(ftp_mkdir($ftp, 'x'));\n    var_dump(ftp_nb_continue($ftp));\n\n    try {\n        ftp_nb_fget($ftp, STDOUT, 'x', 0);\n    } catch (ValueError $exception) {\n        echo $exception->getMessage() . \"\\n\";\n    }\n\n    try {\n        ftp_nb_fput($ftp, 'x', fopen(__FILE__, 'r'), 0);\n    } catch (ValueError $exception) {\n        echo $exception->getMessage() . \"\\n\";\n    }\n\n    var_dump(ftp_systype($ftp));\n    var_dump(ftp_pwd($ftp));\n    var_dump(ftp_size($ftp, ''));\n    var_dump(ftp_rmdir($ftp, ''));\n});\n?>\n--EXPECTF--\nbool(true)\nbool(false)\n\nWarning: ftp_cdup(): Command not implemented (1). in %s005.php on line %d\nbool(false)\n\nWarning: ftp_chdir(): Command not implemented (2). in %s005.php on line %d\nbool(false)\n\nWarning: ftp_chmod(): Command not implemented (3). in %s005.php on line %d\nbool(false)\n\nWarning: ftp_delete(): Command not implemented (4). in %s005.php on line %d\nbool(false)\n\nWarning: ftp_exec(): Command not implemented (5). in %s005.php on line %d\nbool(false)\nftp_fget(): Argument #4 ($mode) must be either FTP_ASCII or FTP_BINARY\nftp_fput(): Argument #4 ($mode) must be either FTP_ASCII or FTP_BINARY\nftp_get(): Argument #4 ($mode) must be either FTP_ASCII or FTP_BINARY\nint(-1)\n\nWarning: ftp_mkdir(): Command not implemented (7). in %s005.php on line %d\nbool(false)\n\nWarning: ftp_nb_continue(): No nbronous transfer to continue in %s005.php on line %d\nint(0)\nftp_nb_fget(): Argument #4 ($mode) must be either FTP_ASCII or FTP_BINARY\nftp_nb_fput(): Argument #4 ($mode) must be either FTP_ASCII or FTP_BINARY\n\nWarning: ftp_systype(): Command not implemented (8). in %s005.php on line %d\nbool(false)\n\nWarning: ftp_pwd(): Command not implemented (9). in %s005.php on line %d\nbool(false)\nint(-1)\n\nWarning: ftp_rmdir(): Command not implemented (11). in %s005.php on line %d\nbool(false)\n"
  },
  {
    "path": "tests/swoole_ftp/007.phpt",
    "content": "--TEST--\nFTP with bogus resource\n--CREDITS--\nMichael Paul da Rosa <michael [at] michaelpaul [dot] com [dot] br>\nPHP TestFest Dublin 2017\n--FILE--\n<?php\nCo\\run(function () {\n    $ftp = tmpfile();\n\n    try {\n        var_dump(ftp_login($ftp, 'user', 'pass'));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_pwd($ftp));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_cdup($ftp));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_chdir($ftp, '~'));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_exec($ftp, 'x'));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_raw($ftp, 'x'));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_mkdir($ftp, '/'));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_rmdir($ftp, '/'));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_chmod($ftp, 7777, '/'));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_alloc($ftp, 7777));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_nlist($ftp, '/'));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_rawlist($ftp, '~'));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_mlsd($ftp, '~'));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_systype($ftp));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_fget($ftp, $ftp, 'remote', 7777));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_nb_fget($ftp, $ftp, 'remote', 7777));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_pasv($ftp, false));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_get($ftp, 'local', 'remote', 7777));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_nb_get($ftp, 'local', 'remote', 7777));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_nb_continue($ftp));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_fput($ftp, 'remote', $ftp, 9999));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_nb_fput($ftp, 'remote', $ftp, 9999));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_put($ftp, 'remote', 'local', 9999));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_append($ftp, 'remote', 'local', 9999));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_nb_put($ftp, 'remote', 'local', 9999));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_size($ftp, '~'));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_mdtm($ftp, '~'));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_rename($ftp, 'old', 'new'));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_delete($ftp, 'gone'));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_site($ftp, 'localhost'));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_close($ftp));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_set_option($ftp, 1, 2));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        var_dump(ftp_get_option($ftp, 1));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n\n    fclose($ftp);\n});\n?>\n--EXPECT--\nftp_login(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_pwd(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_cdup(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_chdir(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_exec(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_raw(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_mkdir(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_rmdir(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_chmod(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_alloc(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_nlist(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_rawlist(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_mlsd(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_systype(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_fget(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_nb_fget(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_pasv(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_get(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_nb_get(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_nb_continue(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_fput(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_nb_fput(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_put(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_append(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_nb_put(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_size(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_mdtm(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_rename(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_delete(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_site(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_close(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_set_option(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\nftp_get_option(): Argument #1 ($ftp) must be of type FTP\\Connection, resource given\n"
  },
  {
    "path": "tests/swoole_ftp/bug27809.phpt",
    "content": "--TEST--\nBug #27809 (ftp_systype returns null)\n--FILE--\n<?php\n$bug27809=true;\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'anonymous', 'IEUser@'));\n    var_dump(ftp_systype($ftp));\n});\n?>\n--EXPECT--\nbool(true)\nstring(6) \"OS/400\"\n"
  },
  {
    "path": "tests/swoole_ftp/bug37799.phpt",
    "content": "--TEST--\nBug #37799 (ftp_ssl_connect() falls back to non-ssl connection)\n--SKIPIF--\n<?php\nif (!function_exists(\"ftp_ssl_connect\")) die(\"skip ftp_ssl is disabled\");\n?>\n--FILE--\n<?php\n$bug37799=$ssl=1;\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $port = $fn();\n\n    $ftp = ftp_ssl_connect('127.0.0.1', $port);\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n\n    ftp_close($ftp);\n});\n?>\n--EXPECTF--\nWarning: ftp_login(): %rdummy|bogus msg%r in %sbug37799.php on line %d\nbool(false)\n"
  },
  {
    "path": "tests/swoole_ftp/bug39458-2.phpt",
    "content": "--TEST--\nBug #39458 (ftp_nlist() returns false on empty directories (other server behaviour))\n--FILE--\n<?php\n$bug39458=1;\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $port = $fn();\n\n    $ftp = ftp_connect('127.0.0.1', $port);\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n\n    var_dump(ftp_nlist($ftp, ''));\n    var_dump(ftp_nlist($ftp, 'emptydir'));\n    var_dump(ftp_nlist($ftp, 'bogusdir'));\n\n    ftp_close($ftp);\n});\n?>\n--EXPECT--\nbool(true)\narray(3) {\n  [0]=>\n  string(5) \"file1\"\n  [1]=>\n  string(5) \"file1\"\n  [2]=>\n  string(9) \"file\nb0rk\"\n}\narray(0) {\n}\nbool(false)\n"
  },
  {
    "path": "tests/swoole_ftp/bug39458.phpt",
    "content": "--TEST--\nBug #39458 (ftp_nlist() returns false on empty directories)\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $port = $fn();\n\n    $ftp = ftp_connect('127.0.0.1', $port);\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n\n    var_dump(ftp_nlist($ftp, ''));\n    var_dump(ftp_nlist($ftp, 'emptydir'));\n    var_dump(ftp_nlist($ftp, 'bogusdir'));\n\n    ftp_close($ftp);\n});\n?>\n--EXPECT--\nbool(true)\narray(3) {\n  [0]=>\n  string(5) \"file1\"\n  [1]=>\n  string(5) \"file1\"\n  [2]=>\n  string(9) \"file\nb0rk\"\n}\narray(0) {\n}\nbool(false)\n"
  },
  {
    "path": "tests/swoole_ftp/bug39583-2.phpt",
    "content": "--TEST--\nBug #39583 (FTP always transfers in binary mode)\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $port = $fn();\n\n    $ftp = ftp_connect('127.0.0.1', $port);\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n\n    $source_file = __FILE__;\n    $destination_file = basename(__FILE__);\n\n    // upload the file\n    $upload = ftp_put($ftp, $destination_file, $source_file, FTP_BINARY);\n\n    // check upload status\n    if (!$upload) {\n           echo \"FTP upload has failed!\";\n       } else {\n           echo \"Uploaded $source_file as $destination_file\";\n       }\n\n    // close the FTP stream\n    ftp_close($ftp);\n});\n?>\n--EXPECTF--\nbool(true)\nUploaded %sbug39583-2.php as bug39583-2.php\n"
  },
  {
    "path": "tests/swoole_ftp/bug39583.phpt",
    "content": "--TEST--\nBug #39583 (FTP always transfers in binary mode)\n--FILE--\n<?php\n$bug39583=1;\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $port = $fn();\n\n    $ftp = ftp_connect('127.0.0.1', $port);\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n\n    $source_file = __FILE__;\n    $destination_file = basename(__FILE__);\n\n    // upload the file\n    $upload = ftp_put($ftp, $destination_file, $source_file, FTP_ASCII);\n\n    // check upload status\n    if (!$upload) {\n           echo \"FTP upload has failed!\";\n       } else {\n           echo \"Uploaded $source_file as $destination_file\";\n       }\n\n    // close the FTP stream\n    ftp_close($ftp);\n});\n?>\n--EXPECTF--\nbool(true)\nUploaded %sbug39583.php as bug39583.php\n"
  },
  {
    "path": "tests/swoole_ftp/bug7216-2.phpt",
    "content": "--TEST--\nBug #7216 (ftp_mkdir returns nothing (2))\n--FILE--\n<?php\n$fn = require 'server.inc';\nCo\\run(function () use ($fn) {\n    $port = $fn();\n    // 连接FTP服务器\n    $ftp = ftp_connect('127.0.0.1', $port);\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    // 使用匿名登录\n    var_dump(ftp_login($ftp, 'anonymous', 'IEUser@'));\n\n    // 测试ftp_mkdir函数行为\n    var_dump(ftp_mkdir($ftp, 'CVS'));\n\n    // 关闭连接\n    ftp_close($ftp);\n});\n--EXPECT--\nbool(true)\nstring(20) \"/path/to/ftproot/CVS\"\n"
  },
  {
    "path": "tests/swoole_ftp/bug7216.phpt",
    "content": "--TEST--\nBug #7216 (ftp_mkdir returns nothing when server response is \"257 OK.\")\n--FILE--\n<?php\n$bug7216 = true;\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $port = $fn();\n    $ftp = ftp_connect('127.0.0.1', $port);\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'anonymous', 'IEUser@'));\n    var_dump(ftp_mkdir($ftp, 'CVS'));\n});\n?>\n--EXPECT--\nbool(true)\nstring(3) \"CVS\"\n"
  },
  {
    "path": "tests/swoole_ftp/bug79100.phpt",
    "content": "--TEST--\nBug #79100 (Wrong FTP error messages)\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\n$GLOBALS['bug79100'] = true;\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $port = $fn();\n    $ftp = ftp_connect(\"127.0.0.1\", $port);\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n    var_dump(ftp_set_option($ftp, FTP_TIMEOUT_SEC, 1));\n    ftp_systype($ftp);\n\n    ftp_close($ftp);\n});\n?>\n--EXPECTF--\nbool(true)\nbool(true)\n\nWarning: ftp_systype(): %rConnection|Operation%r timed out in %s on line %d\n"
  },
  {
    "path": "tests/swoole_ftp/bug80901.phpt",
    "content": "--TEST--\nBug #80901 (Info leak in ftp extension)\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\n// 设置bug80901标志\n$GLOBALS['bug80901'] = true;\n\n// 引入服务器配置\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    // 启动测试服务器（在协程内部）\n    $port = $fn();\n\n    // 连接FTP服务器\n    $ftp = ftp_connect(\"127.0.0.1\", $port);\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n    ftp_systype($ftp);\n\n    ftp_close($ftp);\n});\n?>\n--EXPECTF--\nbool(true)\n\nWarning: ftp_systype(): **************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** in %s on line %d\n"
  },
  {
    "path": "tests/swoole_ftp/cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDhTCCAm2gAwIBAgIJAN75FFz+owOAMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV\nBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\naWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCTEyNy4wLjAuMTAeFw0xNjA3MjUxMjM3\nMTJaFw0yNjA3MjMxMjM3MTJaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l\nLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV\nBAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALtG\nAIrNUDItISfpAqztL2TFEWEHLGTzCEh5Ag2sdMD7UYbqIPHLOE4EINv+dqEMM0Nz\nLYnw7ChtVegXT907xCaQcmeDFSdhqze4L8zawDfnn4syB8XAwGYJfpstYwe3nO6+\n0WvLSb1A5TYNeyoXjwlAUKElxkeWAo51uhR41GDhDQ9GgpqX1ccAhmSoUhgIRSzf\n6f4KE3WTdzl1p12ZtkYHB8Jo2jB/JXnwGOz6isLnpRvkex4B7sUX+7u1MqK/e1X7\nHi1G/VkaAfC2SOfjTePtGBDBXrQ1arYXDPRA04sgFzSh55l7lC/4HasQ/jAb3h95\ndcEIqyc69iioaN1c1NcCAwEAAaNQME4wHQYDVR0OBBYEFNv3kefb1H+6/6CpjiBi\n+I2s9E90MB8GA1UdIwQYMBaAFNv3kefb1H+6/6CpjiBi+I2s9E90MAwGA1UdEwQF\nMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIzSEWpHSaBs7KduBRXX5+qFxBN6OCPl\n7ID0rxAOYfw7ruzbmwgOpBgMIHGn+KqA6CmQI0jh9bZbv5TV2aFpFsUihugPc2lW\n5EshCozxlEPmIJNsO8jDqPE4w3m4KiVTscRWjBa5cco+lwLDqboerm2l7vvrtr6B\npgLaZct1c73MouvoJSCGK5EOGW7jsgaxjxJ3UZug+24Ko1wulO2cgBLhda9Ilrnx\nCIKI9h8Z2WVWuVQfyCyO1g7XkJgkBec77OhxD+m4onzPY6waqnnhmFOBcS+gKgBV\njHeK9DCvZ9zet3EyEp6fyQOOtsC+gU0piYgfsQL7aCp5oLe+fjTiuUY=\n-----END CERTIFICATE-----\n-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7RgCKzVAyLSEn\n6QKs7S9kxRFhByxk8whIeQINrHTA+1GG6iDxyzhOBCDb/nahDDNDcy2J8OwobVXo\nF0/dO8QmkHJngxUnYas3uC/M2sA355+LMgfFwMBmCX6bLWMHt5zuvtFry0m9QOU2\nDXsqF48JQFChJcZHlgKOdboUeNRg4Q0PRoKal9XHAIZkqFIYCEUs3+n+ChN1k3c5\ndaddmbZGBwfCaNowfyV58Bjs+orC56Ub5HseAe7FF/u7tTKiv3tV+x4tRv1ZGgHw\ntkjn403j7RgQwV60NWq2Fwz0QNOLIBc0oeeZe5Qv+B2rEP4wG94feXXBCKsnOvYo\nqGjdXNTXAgMBAAECggEAD7yDISa9fWnjZlojGmrX16zjl/alWVo+sPBSJtn9+ZVk\ntWSJHihIc+3O4Q2R5FiFGj7cbcHr5j3BwT3sPRfflKoAowgVx/hiDc2RXrJnAouZ\nEXZDxu86e5iCpgF7V9OrATjRmjA74wZH/HHHjrLqFwnrfI8TCULmthfYag35Mqax\nqrIEzvSuYdaGxblNe+ZfnVEDW2F9DLBGcma0ffUlJp8AvV7bpo8Rj/JovPxit/VS\nUdwSRxwSAugctFpmcGlFkoQfxUx1WdEy8hjopLrayMjCCJvRUL4+C4zT9r9PBHOj\nfCSbJ+ajQIoRrgaL9bURk8BFMHY8+yMUsWEYVSmFAQKBgQDtOvPkhvZsNocUB5nJ\nti3SXyDQ6OZQuKKHPSeDV/EvmZKeNlrQ1ZnwXLP3vkcedDOkt1nEVq/hUewpjt08\n2MvmMwJBQEnmbTzMf43DtlXsStdP1lhYaFbU4iMM5zRfyBHDu1GPZEPXvKKpJUk0\nM+jYIyTAP3mcZhqDKn0mPVP7VwKBgQDKFy9DtWFKxCvhFQvHx7YeZiVWJbIst/O4\nZyuPVAErni0hzSeCkmm7+F9hgEdPSLRcSaeTWP4L0u1cixECKboIhoNs38aft7o3\nMdnI2RDSEKtKX2uVuhvpGDNuGpBAc8Qu8iCiv5INSC36ZhD1h++O/TiiUdgRJ3yX\nyeG7ej+CgQKBgCR5F95e0aw5hfMSaBaXJ9xcO9Niu2ZVvMdGI7kR4EcNOXmRqczJ\nym0mE5VXb9/Cxd3hQq/pFAl0avbIvEMKoe62kPYvSC1hRiO6yLT6Z6N4rjncHqEZ\nCaCZVAI72dWQEQsi1ZtSMwwMOIYA8YxRHs98N75HBA+DszfPZIZoj2zpAoGBAJHp\nB3ElhmeLF/tdTLIj2bQ9H/wBH5H2Bvw/UU4c4vNxMzjSfRJjUAAtpgAptFLkNYTk\nkR9sA5DZ7BMDPXaIVg9Nv5peP3SWHNc5IPtI7kIdUu9R0cW7J+e2V3vJphlC/ITA\nwRuAoZ0BXmEKTHhae3aMEdXwrcZE8kpNsrO/4hcBAoGBAMISiPJPuxAX1UtqcxTa\nmDJfnQ2gxRu6AK9VmXqo0X4IBxDSnTjcL0huUlS849wgsE5oTXgdYb2hn+TXM5JJ\nNsEXLhV09X1mrk4M4LV1npd0mYxvFsO4+p+IX5YLiahInmQtq0gx3DWE8wouVFER\n4yzfp27z8MZT8Qvr/ZI9lzWd\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "tests/swoole_ftp/dead-resource.phpt",
    "content": "--TEST--\nAttempt to use a closed FTP\\Connection\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n    var_dump(ftp_close($ftp));\n\n    try {\n        var_dump(ftp_login($ftp, 'user', 'pass'));\n        echo \"Login did not throw\\n\";\n    } catch (ValueError $ex) {\n        echo \"Exception: \", $ex->getMessage(), \"\\n\";\n    }\n});\n?>\n--EXPECT--\nbool(true)\nbool(true)\nException: FTP\\Connection is already closed"
  },
  {
    "path": "tests/swoole_ftp/filesize_large.phpt",
    "content": "--TEST--\nVerify php can handle filesizes >32bit\n--SKIPIF--\n<?php\nif (2147483647 == PHP_INT_MAX) {\n    die('skip 64-bit only');\n}\n?>\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    ftp_login($ftp, 'user', 'pass');\n    var_dump(ftp_size($ftp, 'largefile'));\n\n    ftp_close($ftp);\n});\n?>\n--EXPECT--\nint(5368709120)"
  },
  {
    "path": "tests/swoole_ftp/ftp_alloc_basic1.phpt",
    "content": "--TEST--\nTesting ftp_alloc returns true\n--CREDITS--\nRodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>\n#testfest PHPSP on 2009-06-20\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n    ftp_login($ftp, 'user', 'pass');\n\n    var_dump(ftp_alloc($ftp, 1024));\n});\n?>\n--EXPECT--\nbool(true)"
  },
  {
    "path": "tests/swoole_ftp/ftp_alloc_basic2.phpt",
    "content": "--TEST--\nTesting ftp_alloc returns true\n--CREDITS--\nRodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>\n#testfest PHPSP on 2009-06-20\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n    ftp_login($ftp, 'user', 'pass');\n\n    var_dump(ftp_alloc($ftp, 1024, $result));\n    var_dump($result);\n});\n?>\n--EXPECT--\nbool(true)\nstring(20) \"1024 bytes allocated\""
  },
  {
    "path": "tests/swoole_ftp/ftp_append.phpt",
    "content": "--TEST--\nftp_append() create new file and append something\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n\n    $fooPath = __DIR__ . '/ftp_append_foo';\n    file_put_contents($fooPath, 'foo');\n    var_dump(ftp_append($ftp, 'ftp_append_foobar', $fooPath, FTP_BINARY));\n\n    $barPath = __DIR__ . '/ftp_append_bar';\n    file_put_contents($barPath, 'bar');\n    var_dump(ftp_append($ftp, 'ftp_append_foobar', $barPath, FTP_BINARY));\n\n    $fooBarPath = __DIR__ . '/ftp_append_foobar';\n    var_dump(file_get_contents($fooBarPath));\n\n    ftp_close($ftp);\n});\n?>\n--CLEAN--\n<?php\n$fooPath = __DIR__ . '/ftp_append_foo';\nunlink($fooPath);\n$barPath = __DIR__ . '/ftp_append_bar';\nunlink($barPath);\n$fooBarPath = __DIR__ . '/ftp_append_foobar';\nunlink($fooBarPath);\n?>\n--EXPECT--\nbool(true)\nbool(true)\nbool(true)\nstring(6) \"foobar\""
  },
  {
    "path": "tests/swoole_ftp/ftp_chmod_basic.phpt",
    "content": "--TEST--\nTesting ftp_chmod returns file mode\n--CREDITS--\nRodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>\n#testfest PHPSP on 2009-06-20\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n    ftp_login($ftp, 'user', 'pass');\n\n    var_dump(ftp_chmod($ftp, 0644, 'test.txt'));\n});\n?>\n--EXPECT--\nint(420)"
  },
  {
    "path": "tests/swoole_ftp/ftp_connect_001.phpt",
    "content": "--TEST--\nftp_connect - return FALSE if connection fails and Waning is generated\n--FILE--\n<?php\nCo\\run(function () {\n    $ftp = ftp_connect('dummy-host-name', 21, 5);\n    var_dump($ftp);\n});\n?>\n--EXPECTF--\nWarning: ftp_connect(): getaddrinfo for 'dummy-host-name' failed, error: %s in %s on line %d\nbool(false)\n"
  },
  {
    "path": "tests/swoole_ftp/ftp_constructor.phpt",
    "content": "--TEST--\nAttempt to instantiate an FTP\\Connection directly\n--FILE--\n<?php\nCo\\run(function () {\n    try {\n        new FTP\\Connection();\n    } catch (Error $ex) {\n        echo \"Exception: \", $ex->getMessage(), \"\\n\";\n    }\n});\n?>\n--EXPECT--\nException: Cannot directly construct FTP\\Connection, use ftp_connect() or ftp_ssl_connect() instead"
  },
  {
    "path": "tests/swoole_ftp/ftp_delete.phpt",
    "content": "--TEST--\nTesting ftp_delete basic functionality\n--CREDITS--\nGabriel Caruso (carusogabriel34@gmail.com)\nContributed by Ward Cappelle <wardcappelle@gmail.com>\nUser Group: PHP-WVL & PHPGent #PHPTestFest\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    $ftp or die(\"Couldn't connect to the server\");\n\n    echo \"Test case #1: removal of existing file from FTP, should return true:\", PHP_EOL;\n    var_dump(ftp_delete($ftp, 'file1'));\n\n    echo \"Test case #2: removal of non-existent file from FTP, should return false:\", PHP_EOL;\n    var_dump(ftp_delete($ftp, 'false-file.boo'));\n\n    ftp_close($ftp);\n});\n?>\n--EXPECTF--\nTest case #1: removal of existing file from FTP, should return true:\nbool(true)\nTest case #2: removal of non-existent file from FTP, should return false:\n\nWarning: ftp_delete(): No such file or directory in %s on line %d\nbool(false)"
  },
  {
    "path": "tests/swoole_ftp/ftp_exec_basic.phpt",
    "content": "--TEST--\nTesting ftp_exec returns true\n--CREDITS--\nRodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>\n#testfest PHPSP on 2009-06-20\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_exec($ftp, 'ls -al'));\n});\n?>\n--EXPECT--\nbool(true)"
  },
  {
    "path": "tests/swoole_ftp/ftp_fget_basic.phpt",
    "content": "--TEST--\nFTP ftp_fget file for both binary and ASCII transfer modes\n--CREDITS--\nNathaniel McHugh\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n\n    //test simple text transfer\n    $fp = tmpfile();\n    var_dump(ftp_fget($ftp, $fp ,'a story.txt', FTP_ASCII));\n    fseek($fp, 0);\n    echo fgets($fp);\n\n    $position = ftell($fp);\n    //test binary data transfer\n    var_dump(ftp_fget($ftp, $fp, 'binary data.bin', FTP_BINARY));\n    fseek($fp, $position);\n    echo json_encode(fgets($fp)), \"\\n\";\n\n    //test non-existent file request\n    ftp_fget($ftp, $fp ,'a warning.txt', FTP_ASCII);\n\n    //remove file\n    fclose($fp);\n});\n?>\n--EXPECTF--\nbool(true)\nbool(true)\nFor sale: baby shoes, never worn.\nbool(true)\n\"BINARYFoo\\u0000Bar\\r\\n\"\n\nWarning: ftp_fget(): a warning: No such file or directory  in %sftp_fget_basic.php on line %d"
  },
  {
    "path": "tests/swoole_ftp/ftp_fget_basic1.phpt",
    "content": "--TEST--\nTesting ftp_fget ignore autoresume if autoseek is switched off\n--CREDITS--\nRodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>\n#testfest PHPSP on 2009-06-20\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    if (!$ftp) die(\"Couldn't connect to the server\");\n    ftp_set_option($ftp, FTP_AUTOSEEK, false);\n\n    $local_file = __DIR__ . DIRECTORY_SEPARATOR . \"ftp_fget_basic1.txt\";\n    $handle = fopen($local_file, 'w');\n\n    var_dump(ftp_fget($ftp, $handle, 'fget.txt', FTP_ASCII, FTP_AUTORESUME));\n    var_dump(file_get_contents($local_file));\n});\n?>\n--CLEAN--\n<?php\n@unlink(__DIR__ . DIRECTORY_SEPARATOR . \"ftp_fget_basic1.txt\");\n?>\n--EXPECT--\nbool(true)\nstring(12) \"ASCIIFooBar\n\""
  },
  {
    "path": "tests/swoole_ftp/ftp_fget_basic2.phpt",
    "content": "--TEST--\nTesting ftp_fget autoresume\n--CREDITS--\nRodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>\n#testfest PHPSP on 2009-06-20\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    $local_file = __DIR__ . DIRECTORY_SEPARATOR . \"ftp_fget_basic2.txt\";\n    file_put_contents($local_file, 'ASCIIFoo');\n    $handle = fopen($local_file, 'a');\n\n    var_dump(ftp_fget($ftp, $handle, 'fgetresume.txt', FTP_ASCII, FTP_AUTORESUME));\n    var_dump(file_get_contents($local_file));\n});\n?>\n--CLEAN--\n<?php\n@unlink(__DIR__ . DIRECTORY_SEPARATOR . \"ftp_fget_basic2.txt\");\n?>\n--EXPECT--\nbool(true)\nstring(12) \"ASCIIFooBar\n\""
  },
  {
    "path": "tests/swoole_ftp/ftp_fget_basic3.phpt",
    "content": "--TEST--\nTesting ftp_fget resume parameter\n--CREDITS--\nRodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>\n#testfest PHPSP on 2009-06-20\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    $local_file = __DIR__ . DIRECTORY_SEPARATOR . \"ftp_fget_basic3.txt\";\n    file_put_contents($local_file, 'ASCIIFoo');\n    $handle = fopen($local_file, 'a');\n\n    var_dump(ftp_fget($ftp, $handle, 'fgetresume.txt', FTP_ASCII, 8));\n    var_dump(file_get_contents($local_file));\n});\n?>\n--CLEAN--\n<?php\n@unlink(__DIR__ . DIRECTORY_SEPARATOR . \"ftp_fget_basic3.txt\");\n?>\n--EXPECT--\nbool(true)\nstring(12) \"ASCIIFooBar\n\""
  },
  {
    "path": "tests/swoole_ftp/ftp_fput.phpt",
    "content": "--TEST--\nTesting ftp_fput basic functionality\n--CREDITS--\nGabriel Caruso (carusogabriel34@gmail.com)\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    $ftp or die(\"Couldn't connect to the server\");\n\n    $destination_file = basename(__FILE__);\n    $source_file = fopen(__FILE__, 'r');\n\n    var_dump(ftp_fput($ftp, $destination_file, $source_file, FTP_ASCII));\n});\n?>\n--EXPECT--\nbool(true)"
  },
  {
    "path": "tests/swoole_ftp/ftp_fput_ascii_over_4_kib.phpt",
    "content": "--TEST--\nTesting ftp_fput basic functionality\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    $ftp or die(\"Couldn't connect to the server\");\n\n    $filename = \"large_file.txt\";\n    $filepath = __DIR__ . \"/\" . $filename;\n\n    // Test on boundary of 2 buffers\n    for ($i = 4094; $i < 4098; $i++) {\n        $contents = str_repeat(\"a\", $i) . \"\\n\" . str_repeat(\"b\", 10);\n        file_put_contents($filepath, $contents);\n        var_dump(ftp_put($ftp, \"large_file.txt\", $filepath, FTP_ASCII));\n    }\n});\n?>\n--CLEAN--\n<?php\n@unlink(__DIR__ . \"/large_file.txt\");\n?>\n--EXPECT--\nbool(true)\nbool(true)\nbool(true)\nbool(true)"
  },
  {
    "path": "tests/swoole_ftp/ftp_get_basic.phpt",
    "content": "--TEST--\nFTP ftp_get file for both binary and ASCII transfer modes\n--CREDITS--\nNathaniel McHugh\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n\n    //test simple text transfer\n    $tmpfname = tempnam(__DIR__, \"ftp_test\");\n    var_dump(ftp_get($ftp, $tmpfname ,'a story.txt', FTP_ASCII));\n    echo file_get_contents($tmpfname);\n    unlink($tmpfname);\n\n    //test binary data transfer\n    $tmpfname = tempnam(__DIR__, \"ftp_test\");\n    var_dump(ftp_get($ftp, $tmpfname, 'binary data.bin', FTP_BINARY));\n    echo json_encode(file_get_contents($tmpfname)), \"\\n\";\n    unlink($tmpfname);\n\n    //test non-existent file request\n    $tmpfname = tempnam(__DIR__, \"ftp_test\");\n    ftp_get($ftp, $tmpfname ,'a warning.txt', FTP_ASCII);\n});\n?>\n--EXPECTF--\nbool(true)\nbool(true)\nFor sale: baby shoes, never worn.\nbool(true)\n\"BINARYFoo\\u0000Bar\\r\\n\"\n\nWarning: ftp_get(): a warning: No such file or directory  in %sftp_get_basic.php on line %d"
  },
  {
    "path": "tests/swoole_ftp/ftp_get_option.phpt",
    "content": "--TEST--\nTesting ftp_get_option basic functionality\n--CREDITS--\nGabriel Caruso (carusogabriel34@gmail.com)\n--FILE--\n<?php\n$fn = require 'server.inc';\ndefine('FOO_BAR', 10);\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    $ftp or die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_get_option($ftp, FTP_TIMEOUT_SEC));\n    var_dump(ftp_get_option($ftp, FTP_AUTOSEEK));\n    var_dump(ftp_get_option($ftp, FTP_USEPASVADDRESS));\n\n    try {\n        ftp_get_option($ftp, FOO_BAR);\n    } catch (ValueError $exception) {\n        echo $exception->getMessage() . \"\\n\";\n    }\n});\n?>\n--EXPECTF--\nint(%d)\nbool(true)\nbool(true)\nftp_get_option(): Argument #2 ($option) must be one of FTP_TIMEOUT_SEC, FTP_AUTOSEEK, or FTP_USEPASVADDRESS"
  },
  {
    "path": "tests/swoole_ftp/ftp_mdtm_basic.phpt",
    "content": "--TEST--\nTest the File Modification Time as described in http://tools.ietf.org/html/rfc3659#section-3.1\n--CREDITS--\nNathaniel McHugh\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n\n\n    date_default_timezone_set('UTC');\n\n    $time = ftp_mdtm($ftp, \"A\");\n    echo date(\"F d Y H:i:s u\",$time), PHP_EOL;\n\n    $time = ftp_mdtm($ftp, \"B\");\n    echo date(\"F d Y H:i:s u\",$time), PHP_EOL;\n\n    $time = ftp_mdtm($ftp, \"C\");\n    echo date(\"F d Y H:i:s u\",$time), PHP_EOL;\n\n    $time = ftp_mdtm($ftp, \"D\");\n    var_dump($time);\n\n    $time = ftp_mdtm($ftp, \"19990929043300 File6\");\n    echo date(\"F d Y H:i:s u\",$time), PHP_EOL;\n\n    $time = ftp_mdtm($ftp, \"MdTm 19990929043300 file6\");\n    var_dump($time);\n});\n?>\n--EXPECT--\nbool(true)\nJune 15 1998 10:00:45 000000\nJune 15 1998 10:00:45 000000\nJuly 05 1998 13:23:16 000000\nint(-1)\nOctober 05 1999 21:31:02 000000\nint(-1)"
  },
  {
    "path": "tests/swoole_ftp/ftp_mlsd.phpt",
    "content": "--TEST--\nftp_mlsd() return parsed lines\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n\n    var_dump(ftp_mlsd($ftp, '.'));\n\n    ftp_close($ftp);\n});\n?>\n--EXPECTF--\nbool(true)\n\nWarning: ftp_mlsd(): Missing pathname in MLSD response in %s on line %d\n\nWarning: ftp_mlsd(): Malformed fact in MLSD response in %s on line %d\n\nWarning: ftp_mlsd(): Malformed fact in MLSD response in %s on line %d\narray(4) {\n  [0]=>\n  array(8) {\n    [\"name\"]=>\n    string(1) \".\"\n    [\"modify\"]=>\n    string(14) \"20170127230002\"\n    [\"perm\"]=>\n    string(7) \"flcdmpe\"\n    [\"type\"]=>\n    string(4) \"cdir\"\n    [\"unique\"]=>\n    string(11) \"811U4340002\"\n    [\"UNIX.group\"]=>\n    string(2) \"33\"\n    [\"UNIX.mode\"]=>\n    string(4) \"0755\"\n    [\"UNIX.owner\"]=>\n    string(2) \"33\"\n  }\n  [1]=>\n  array(8) {\n    [\"name\"]=>\n    string(2) \"..\"\n    [\"modify\"]=>\n    string(14) \"20170127230002\"\n    [\"perm\"]=>\n    string(7) \"flcdmpe\"\n    [\"type\"]=>\n    string(4) \"pdir\"\n    [\"unique\"]=>\n    string(11) \"811U4340002\"\n    [\"UNIX.group\"]=>\n    string(2) \"33\"\n    [\"UNIX.mode\"]=>\n    string(4) \"0755\"\n    [\"UNIX.owner\"]=>\n    string(2) \"33\"\n  }\n  [2]=>\n  array(9) {\n    [\"name\"]=>\n    string(6) \"foobar\"\n    [\"modify\"]=>\n    string(14) \"20170126121225\"\n    [\"perm\"]=>\n    string(5) \"adfrw\"\n    [\"size\"]=>\n    string(4) \"4729\"\n    [\"type\"]=>\n    string(4) \"file\"\n    [\"unique\"]=>\n    string(11) \"811U4340CB9\"\n    [\"UNIX.group\"]=>\n    string(2) \"33\"\n    [\"UNIX.mode\"]=>\n    string(4) \"0644\"\n    [\"UNIX.owner\"]=>\n    string(2) \"33\"\n  }\n  [3]=>\n  array(3) {\n    [\"name\"]=>\n    string(9) \"path;name\"\n    [\"fact\"]=>\n    string(6) \"val=ue\"\n    [\"empty\"]=>\n    string(0) \"\"\n  }\n}"
  },
  {
    "path": "tests/swoole_ftp/ftp_mlsd_empty_directory.phpt",
    "content": "--TEST--\nftp_mlsd() must not return false on empty directories\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'user', 'pass'));\n\n    var_dump(ftp_mlsd($ftp, 'emptydir'));\n    var_dump(ftp_mlsd($ftp, 'bogusdir'));\n\n    ftp_close($ftp);\n});\n?>\n--EXPECT--\nbool(true)\narray(0) {\n}\nbool(false)"
  },
  {
    "path": "tests/swoole_ftp/ftp_mlsd_missing_directory.phpt",
    "content": "--TEST--\nTesting ftp_mlsd returns false on server error\n--CREDITS--\nAndreas Treichel <gmblar+github [at] gmail [dot] com>\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_mlsd($ftp, 'no_exists/'));\n});\n?>\n--EXPECT--\nbool(false)"
  },
  {
    "path": "tests/swoole_ftp/ftp_nb_continue.phpt",
    "content": "--TEST--\nTesting whether ftp_nb_continue() fetches more data\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $file = \"mediumfile.txt\";\n\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    $local_file = __DIR__ . DIRECTORY_SEPARATOR . $file;\n    touch($local_file);\n\n    $r = ftp_nb_get($ftp, $local_file, $file, FTP_BINARY);\n    while ($r == FTP_MOREDATA) {\n        $r = ftp_nb_continue($ftp);\n    }\n    ftp_close($ftp);\n\n    echo file_get_contents($local_file);\n});\n?>\n--CLEAN--\n<?php\n@unlink(__DIR__ . DIRECTORY_SEPARATOR . \"mediumfile.txt\");\n?>\n--EXPECT--\nThis is line 0 of the test data.\nThis is line 1 of the test data.\nThis is line 2 of the test data.\nThis is line 3 of the test data.\nThis is line 4 of the test data.\nThis is line 5 of the test data.\nThis is line 6 of the test data.\nThis is line 7 of the test data.\nThis is line 8 of the test data.\nThis is line 9 of the test data.\nThis is line 10 of the test data.\nThis is line 11 of the test data.\nThis is line 12 of the test data.\nThis is line 13 of the test data.\nThis is line 14 of the test data.\nThis is line 15 of the test data.\nThis is line 16 of the test data.\nThis is line 17 of the test data.\nThis is line 18 of the test data.\nThis is line 19 of the test data.\nThis is line 20 of the test data.\nThis is line 21 of the test data.\nThis is line 22 of the test data.\nThis is line 23 of the test data.\nThis is line 24 of the test data.\nThis is line 25 of the test data.\nThis is line 26 of the test data.\nThis is line 27 of the test data.\nThis is line 28 of the test data.\nThis is line 29 of the test data.\nThis is line 30 of the test data.\nThis is line 31 of the test data.\nThis is line 32 of the test data.\nThis is line 33 of the test data.\nThis is line 34 of the test data.\nThis is line 35 of the test data.\nThis is line 36 of the test data.\nThis is line 37 of the test data.\nThis is line 38 of the test data.\nThis is line 39 of the test data.\nThis is line 40 of the test data.\nThis is line 41 of the test data.\nThis is line 42 of the test data.\nThis is line 43 of the test data.\nThis is line 44 of the test data.\nThis is line 45 of the test data.\nThis is line 46 of the test data.\nThis is line 47 of the test data.\nThis is line 48 of the test data.\nThis is line 49 of the test data.\nThis is line 50 of the test data.\nThis is line 51 of the test data.\nThis is line 52 of the test data.\nThis is line 53 of the test data.\nThis is line 54 of the test data.\nThis is line 55 of the test data.\nThis is line 56 of the test data.\nThis is line 57 of the test data.\nThis is line 58 of the test data.\nThis is line 59 of the test data.\nThis is line 60 of the test data.\nThis is line 61 of the test data.\nThis is line 62 of the test data.\nThis is line 63 of the test data.\nThis is line 64 of the test data.\nThis is line 65 of the test data.\nThis is line 66 of the test data.\nThis is line 67 of the test data.\nThis is line 68 of the test data.\nThis is line 69 of the test data.\nThis is line 70 of the test data.\nThis is line 71 of the test data.\nThis is line 72 of the test data.\nThis is line 73 of the test data.\nThis is line 74 of the test data.\nThis is line 75 of the test data.\nThis is line 76 of the test data.\nThis is line 77 of the test data.\nThis is line 78 of the test data.\nThis is line 79 of the test data.\nThis is line 80 of the test data.\nThis is line 81 of the test data.\nThis is line 82 of the test data.\nThis is line 83 of the test data.\nThis is line 84 of the test data.\nThis is line 85 of the test data.\nThis is line 86 of the test data.\nThis is line 87 of the test data.\nThis is line 88 of the test data.\nThis is line 89 of the test data.\nThis is line 90 of the test data.\nThis is line 91 of the test data.\nThis is line 92 of the test data.\nThis is line 93 of the test data.\nThis is line 94 of the test data.\nThis is line 95 of the test data.\nThis is line 96 of the test data.\nThis is line 97 of the test data.\nThis is line 98 of the test data.\nThis is line 99 of the test data.\nThis is line 100 of the test data.\nThis is line 101 of the test data.\nThis is line 102 of the test data.\nThis is line 103 of the test data.\nThis is line 104 of the test data.\nThis is line 105 of the test data.\nThis is line 106 of the test data.\nThis is line 107 of the test data.\nThis is line 108 of the test data.\nThis is line 109 of the test data.\nThis is line 110 of the test data.\nThis is line 111 of the test data.\nThis is line 112 of the test data.\nThis is line 113 of the test data.\nThis is line 114 of the test data.\nThis is line 115 of the test data.\nThis is line 116 of the test data.\nThis is line 117 of the test data.\nThis is line 118 of the test data.\nThis is line 119 of the test data.\nThis is line 120 of the test data.\nThis is line 121 of the test data.\nThis is line 122 of the test data.\nThis is line 123 of the test data.\nThis is line 124 of the test data.\nThis is line 125 of the test data.\nThis is line 126 of the test data.\nThis is line 127 of the test data.\nThis is line 128 of the test data.\nThis is line 129 of the test data.\nThis is line 130 of the test data.\nThis is line 131 of the test data.\nThis is line 132 of the test data.\nThis is line 133 of the test data.\nThis is line 134 of the test data.\nThis is line 135 of the test data.\nThis is line 136 of the test data.\nThis is line 137 of the test data.\nThis is line 138 of the test data.\nThis is line 139 of the test data.\nThis is line 140 of the test data.\nThis is line 141 of the test data.\nThis is line 142 of the test data.\nThis is line 143 of the test data.\nThis is line 144 of the test data.\nThis is line 145 of the test data.\nThis is line 146 of the test data.\nThis is line 147 of the test data.\nThis is line 148 of the test data.\nThis is line 149 of the test data."
  },
  {
    "path": "tests/swoole_ftp/ftp_nb_fget_basic1.phpt",
    "content": "--TEST--\nTesting ftp_nb_fget ignore autoresume if autoseek is switched off\n--CREDITS--\nRodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>\n#testfest PHPSP on 2009-06-20\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    if (!$ftp) die(\"Couldn't connect to the server\");\n    ftp_set_option($ftp, FTP_AUTOSEEK, false);\n\n    $local_file = __DIR__ . DIRECTORY_SEPARATOR . \"ftp_nb_fget_basic1.txt\";\n    $handle = fopen($local_file, 'w');\n\n    var_dump(ftp_nb_fget($ftp, $handle, 'fget.txt', FTP_ASCII, FTP_AUTORESUME));\n    var_dump(file_get_contents($local_file));\n});\n?>\n--CLEAN--\n<?php\n@unlink(__DIR__ . DIRECTORY_SEPARATOR . \"ftp_nb_fget_basic1.txt\");\n?>\n--EXPECT--\nint(2)\nstring(12) \"ASCIIFooBar\n\""
  },
  {
    "path": "tests/swoole_ftp/ftp_nb_fget_basic2.phpt",
    "content": "--TEST--\nTesting ftp_nb_fget autoresume\n--CREDITS--\nRodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>\n#testfest PHPSP on 2009-06-20\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    $local_file = __DIR__ . DIRECTORY_SEPARATOR . \"ftp_nb_fget_basic2.txt\";\n    file_put_contents($local_file, 'ASCIIFoo');\n    $handle = fopen($local_file, 'a');\n\n    var_dump(ftp_nb_fget($ftp, $handle, 'fgetresume.txt', FTP_ASCII, FTP_AUTORESUME));\n    var_dump(file_get_contents($local_file));\n});\n?>\n--CLEAN--\n<?php\n@unlink(__DIR__ . DIRECTORY_SEPARATOR . \"ftp_nb_fget_basic2.txt\");\n?>\n--EXPECT--\nint(2)\nstring(12) \"ASCIIFooBar\n\""
  },
  {
    "path": "tests/swoole_ftp/ftp_nb_fget_basic3.phpt",
    "content": "--TEST--\nTesting ftp_nb_fget resume parameter\n--CREDITS--\nRodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>\n#testfest PHPSP on 2009-06-20\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    $local_file = __DIR__ . DIRECTORY_SEPARATOR . \"ftp_nb_fget_basic3.txt\";\n    file_put_contents($local_file, 'ASCIIFoo');\n    $handle = fopen($local_file, 'a');\n\n    var_dump(ftp_nb_fget($ftp, $handle, 'fgetresume.txt', FTP_ASCII, 8));\n    var_dump(file_get_contents($local_file));\n});\n?>\n--CLEAN--\n<?php\n@unlink(__DIR__ . DIRECTORY_SEPARATOR . \"ftp_nb_fget_basic3.txt\");\n?>\n--EXPECT--\nint(2)\nstring(12) \"ASCIIFooBar\n\""
  },
  {
    "path": "tests/swoole_ftp/ftp_nb_fput.phpt",
    "content": "--TEST--\nTesting ftp_nb_fput basic functionality\n--CREDITS--\nGabriel Caruso (carusogabriel34@gmail.com)\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    $ftp or die(\"Couldn't connect to the server\");\n\n    $destination_file = basename(__FILE__);\n    $source_file = fopen(__FILE__, 'r');\n\n    var_dump(ftp_nb_fput($ftp, $destination_file, $source_file, FTP_ASCII));\n});\n?>\n--EXPECT--\nint(1)"
  },
  {
    "path": "tests/swoole_ftp/ftp_nb_get_large.phpt",
    "content": "--TEST--\nTesting ftp_nb_fget can handle large files incl. resume\n--SKIPIF--\n<?php\nif (2147483647 == PHP_INT_MAX) {\n    die('skip ot supported on this system');\n}\nif (disk_free_space(__DIR__) < 10*1024*1024*1024) {\n    die('skip Not enough disk space');\n}\n?>\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    $local_file = __DIR__ . DIRECTORY_SEPARATOR . \"ftp_nb_get_large.txt\";\n    touch($local_file);\n    ftp_nb_get($ftp, $local_file, 'fget_large.txt', FTP_BINARY, 5368709119);\n    $fp = fopen($local_file, 'r');\n    fseek($fp, 5368709119);\n    var_dump(fread($fp, 1));\n    var_dump(filesize($local_file));\n    fclose($fp);\n});\n?>\n--CLEAN--\n<?php\n@unlink(__DIR__ . DIRECTORY_SEPARATOR . \"ftp_nb_get_large.txt\");\n?>\n--EXPECT--\nstring(1) \"X\"\nint(5368709120)"
  },
  {
    "path": "tests/swoole_ftp/ftp_nb_put.phpt",
    "content": "--TEST--\nTesting ftp_nb_put basic functionality\n--CREDITS--\nGabriel Caruso (carusogabriel34@gmail.com)\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    $ftp or die(\"Couldn't connect to the server\");\n\n    $destination_file = basename(__FILE__);\n    $source_file = __FILE__;\n\n    var_dump(ftp_nb_put($ftp, $destination_file, $source_file, FTP_ASCII));\n});\n?>\n--EXPECT--\nint(1)"
  },
  {
    "path": "tests/swoole_ftp/ftp_pasv.phpt",
    "content": "--TEST--\nTesting ftp_pasv basic funcionality\n--CREDITS--\nGabriel Caruso (carusogabriel34@gmail.com)\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    $ftp or die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_pasv($ftp, false));\n});\n?>\n--EXPECT--\nbool(true)"
  },
  {
    "path": "tests/swoole_ftp/ftp_rawlist_basic1.phpt",
    "content": "--TEST--\nTesting ftp_rawlist basic functionality\n--CREDITS--\nGabriel Caruso (carusogabriel34@gmail.com)\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    $ftp or die(\"Couldn't connect to the server\");\n\n    $result = ftp_rawlist($ftp, 'www/');\n    var_dump(is_array($result));\n    var_dump($result);\n    ftp_close($ftp);\n});\n?>\n--EXPECT--\nbool(true)\narray(3) {\n  [0]=>\n  string(5) \"file1\"\n  [1]=>\n  string(5) \"file1\"\n  [2]=>\n  string(9) \"file\nb0rk\"\n}"
  },
  {
    "path": "tests/swoole_ftp/ftp_rawlist_basic2.phpt",
    "content": "--TEST--\nTesting ftp_rawlist returns false on server error\n--CREDITS--\nRodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>\n#testfest PHPSP on 2009-06-20\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_rawlist($ftp, 'no_exists/'));\n});\n?>\n--EXPECT--\nbool(false)"
  },
  {
    "path": "tests/swoole_ftp/ftp_rename_basic1.phpt",
    "content": "--TEST--\nFTP basic ftp_rename calls\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    ftp_login($ftp, 'user', 'pass');\n\n    var_dump(ftp_rename($ftp, 'existing_file', 'nonexisting_file'));\n    var_dump(ftp_rename($ftp, 'nonexisting_file', 'nonexisting_file'));\n});\n?>\n--EXPECTF--\nbool(true)\n\nWarning: ftp_rename(): No such file or directory in %sftp_rename_basic1.php on line %d\nbool(false)\n"
  },
  {
    "path": "tests/swoole_ftp/ftp_rmdir_basic.phpt",
    "content": "--TEST--\nTesting ftp_rmdir returns true\n--CREDITS--\nRodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>\n#testfest PHPSP on 2009-06-20\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n    ftp_login($ftp, 'user', 'pass');\n\n    var_dump(ftp_rmdir($ftp, 'www/'));\n});\n?>\n--EXPECT--\nbool(true)"
  },
  {
    "path": "tests/swoole_ftp/ftp_set_option.phpt",
    "content": "--TEST--\nTesting ftp_set_option basic functionality\n--CREDITS--\nGabriel Caruso (carusogabriel34@gmail.com)\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    $ftp or die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_set_option($ftp, FTP_TIMEOUT_SEC, 10));\n    var_dump(ftp_set_option($ftp, FTP_AUTOSEEK, false));\n    var_dump(ftp_set_option($ftp, FTP_USEPASVADDRESS, true));\n});\n?>\n--EXPECT--\nbool(true)\nbool(true)\nbool(true)"
  },
  {
    "path": "tests/swoole_ftp/ftp_set_option_errors.phpt",
    "content": "--TEST--\nTesting ftp_set_option errors while setting up\n--CREDITS--\nGabriel Caruso (carusogabriel34@gmail.com)\n--FILE--\n<?php\n$fn = require 'server.inc';\ndefine('FOO_BAR', 10);\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    ftp_login($ftp, 'user', 'pass');\n    $ftp or die(\"Couldn't connect to the server\");\n\n    $options = [\n        [ 'option' => FTP_TIMEOUT_SEC, 'value' => 0 ],\n        [ 'option' => FTP_TIMEOUT_SEC, 'value' => '0' ],\n        [ 'option' => FTP_USEPASVADDRESS, 'value' => ['1'] ],\n        [ 'option' => FTP_AUTOSEEK, 'value' => 'true' ],\n        [ 'option' => FOO_BAR, 'value' => 1 ],\n    ];\n    foreach ($options as $option) try {\n        var_dump(ftp_set_option($ftp, $option['option'], $option['value']));\n    } catch (\\Throwable $ex) {\n        echo \"Exception: \", $ex->getMessage(), \"\\n\";\n    }\n});\n?>\n--EXPECT--\nException: ftp_set_option(): Argument #3 ($value) must be greater than 0 for the FTP_TIMEOUT_SEC option\nException: ftp_set_option(): Argument #3 ($value) must be of type int for the FTP_TIMEOUT_SEC option, string given\nException: ftp_set_option(): Argument #3 ($value) must be of type bool for the FTP_USEPASVADDRESS option, array given\nException: ftp_set_option(): Argument #3 ($value) must be of type bool for the FTP_AUTOSEEK option, string given\nException: ftp_set_option(): Argument #2 ($option) must be one of FTP_TIMEOUT_SEC, FTP_AUTOSEEK, or FTP_USEPASVADDRESS"
  },
  {
    "path": "tests/swoole_ftp/ftp_site_basic.phpt",
    "content": "--TEST--\nftp_site function basic functionality\n--CREDITS--\nGabriel Caruso (carusogabriel34@gmail.com)\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    $ftp or die(\"Couldn't connect to the server\");\n\n    ftp_login($ftp, 'user', 'pass') or die(\"Couldn't login into the server\");\n\n    var_dump(ftp_site($ftp, 'CHMOD 0600 file'));\n    var_dump(ftp_site($ftp, 'foo bar baz'));\n});\n?>\n--EXPECTF--\nbool(true)\n\nWarning: ftp_site(): Syntax error, command unrecognized. in %s on line %d\nbool(false)"
  },
  {
    "path": "tests/swoole_ftp/ftp_ssl_connect_error.phpt",
    "content": "--TEST--\nTest ftp_ssl_connect() function : error conditions\n--SKIPIF--\n<?php\nif (!function_exists(\"ftp_ssl_connect\")) die(\"skip ftp_ssl is disabled\");\n?>\n--FILE--\n<?php\nCo\\run(function () {\n    echo \"*** Testing ftp_ssl_connect() function : error conditions ***\\n\";\n    echo \"\\n-- Testing ftp_ssl_connect() function on failure --\\n\";\n    var_dump(ftp_ssl_connect('totes.invalid'));\n\n    echo \"\\n-- Testing ftp_ssl_connect() function timeout exception for value 0 --\\n\";\n    try {\n        ftp_ssl_connect('totes.invalid', 21, 0);\n    } catch (ValueError $exception) {\n        echo $exception->getMessage() . \"\\n\";\n    }\n\n    echo \"===DONE===\\n\";\n});\n?>\n--EXPECTF--\n*** Testing ftp_ssl_connect() function : error conditions ***\n\n-- Testing ftp_ssl_connect() function on failure --\n\nWarning: ftp_ssl_connect(): getaddrinfo for '%s' failed, error: %s in %s on line %d\nbool(false)\n\n-- Testing ftp_ssl_connect() function timeout exception for value 0 --\nftp_ssl_connect(): Argument #3 ($timeout) must be greater than 0\n===DONE===\n"
  },
  {
    "path": "tests/swoole_ftp/gh10521.phpt",
    "content": "--TEST--\nGH-10521 (ftp_get/ftp_nb_get resumepos offset is maximum 10GB)\n--SKIPIF--\n<?php\nif (PHP_INT_SIZE != 8) die(\"skip: 64-bit only\");\n?>\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'anonymous', 'IEUser@'));\n\n    $local_file = __DIR__ . DIRECTORY_SEPARATOR . \"gh10521.txt\";\n\n    foreach ([12345678910, 9223372036854775807] as $size) {\n        $handle = fopen($local_file, 'w');\n        // Doesn't actually succeed in transferring a file. The file transfer gets aborted by our fake server.\n        // We just want to see if the offset was correctly received.\n        ftp_fget($ftp, $handle, 'gh10521', FTP_ASCII, $size);\n        fclose($handle);\n    }\n});\n?>\n--CLEAN--\n<?php\n@unlink(__DIR__ . DIRECTORY_SEPARATOR . \"gh10521.txt\");\n?>\n--EXPECTF--\nbool(true)\n\n%s: ftp_fget(): Can't open data connection (12345678910). in %s on line %d\n\n%s: ftp_fget(): Can't open data connection (9223372036854775807). in %s on line %d"
  },
  {
    "path": "tests/swoole_ftp/gh10562.phpt",
    "content": "--TEST--\nGH-10562 (Memory leak with consecutive ftp_nb_fget)\n--FILE--\n<?php\n$fn = require 'server.inc';\n\nCo\\run(function () use ($fn) {\n    $ftp = ftp_connect('127.0.0.1', $fn());\n    if (!$ftp) die(\"Couldn't connect to the server\");\n\n    var_dump(ftp_login($ftp, 'anonymous', 'IEUser@'));\n\n    $local_file = __DIR__ . DIRECTORY_SEPARATOR . \"gh10562.txt\";\n    $fout = fopen($local_file, \"w\");\n\n    // This requests more data, but we don't do the loop to fetch it.\n    $ret = ftp_nb_fget($ftp, $fout, \"fget\", FTP_BINARY, 0);\n    var_dump($ret == FTP_MOREDATA);\n\n    // This aborts the previous request, fetches the whole \"a story\" file.\n    $ret = ftp_nb_fget($ftp, $fout, \"a story\", FTP_BINARY, 0);\n    while ($ret == FTP_MOREDATA) {\n        $ret = ftp_nb_continue($ftp);\n    }\n\n    fclose($fout);\n\n    echo file_get_contents($local_file), \"\\n\";\n});\n?>\n--CLEAN--\n<?php\n@unlink(__DIR__ . DIRECTORY_SEPARATOR . \"gh10562.txt\");\n?>\n--EXPECT--\nbool(true)\nbool(true)\nBINARYFooBar\nFor sale: baby shoes, never worn."
  },
  {
    "path": "tests/swoole_ftp/server.inc",
    "content": "<?php\n/**\n * This file is part of Swoole.\n *\n * @link     https://www.swoole.com\n * @contact  team@swoole.com\n * @license  https://github.com/swoole/library/blob/master/LICENSE\n */\n\ndeclare(strict_types=1);\n\n$socket = null;\n$errno = 0;\n$context = stream_context_create(['ssl' => ['local_cert' => dirname(__FILE__) . '/cert.pem']]);\n\n$socket = stream_socket_server('tcp://127.0.0.1:0', $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $context);\nif (!$socket) {\n    echo \"{$errstr} ({$errno})\\n\";\n    exit(\"could not start/bind the ftp server\\n\");\n}\n\n$socket_name = stream_socket_get_name($socket, false);\n$port = (int) substr($socket_name, strrpos($socket_name, ':') + 1);\n\n$pid = pcntl_fork();\n\n// server\nif ($pid == 0) {\n    function dump_and_exit($buf)\n    {\n        var_dump($buf);\n        exit;\n    }\n\n    function anonymous()\n    {\n        return $GLOBALS['user'] === 'anonymous';\n    }\n\n    /* quick&dirty realpath() like function */\n    function change_dir($dir)\n    {\n        global $cwd;\n\n        if ($dir[0] == '/') {\n            $cwd = $dir;\n            return;\n        }\n\n        $cwd = \"{$cwd}/{$dir}\";\n\n        do {\n            $old = $cwd;\n            $cwd = preg_replace('@/?[^/]+/\\.\\.@', '', $cwd);\n        } while ($old != $cwd);\n\n        $cwd = strtr($cwd, ['//' => '/']);\n        if (!$cwd) {\n            $cwd = '/';\n        }\n    }\n\n    $s = stream_socket_accept($socket);\n    if (!$s) {\n        exit(\"Error accepting a new connection\\n\");\n    }\n\n    fputs($s, \"220----- PHP FTP server 0.3 -----\\r\\n220 Service ready\\r\\n\");\n    $buf = fread($s, 2048);\n\n    function user_auth($buf)\n    {\n        global $user, $s, $ssl, $bug37799;\n\n        if (!empty($ssl)) {\n            if ($buf !== \"AUTH TLS\\r\\n\") {\n                fputs($s, \"500 Syntax error, command unrecognized.\\r\\n\");\n                dump_and_exit($buf);\n            }\n\n            if (empty($bug37799)) {\n                fputs($s, \"234 auth type accepted\\r\\n\");\n            } else {\n                fputs($s, \"666 dummy\\r\\n\");\n                sleep(1);\n                fputs($s, \"666 bogus msg\\r\\n\");\n                exit;\n            }\n\n            if (!stream_socket_enable_crypto($s, true, STREAM_CRYPTO_METHOD_SSLv23_SERVER)) {\n                exit(\"SSLv23 handshake failed.\\n\");\n            }\n\n            if (!preg_match('/^PBSZ \\d+\\r\\n$/', $buf = fread($s, 2048))) {\n                fputs($s, \"501 bogus data\\r\\n\");\n                dump_and_exit($buf);\n            }\n\n            fputs($s, \"200 OK\\r\\n\");\n            $buf = fread($s, 2048);\n\n            if ($buf !== \"PROT P\\r\\n\") {\n                fputs($s, \"504 Wrong protection.\\r\\n\");\n                dump_and_exit($buf);\n            }\n\n            fputs($s, \"200 OK\\r\\n\");\n\n            $buf = fread($s, 2048);\n        }\n\n        if ($buf == \"AUTH TLS\\r\\n\") {\n            fputs($s, \"500 not supported.\\r\\n\");\n            return;\n        }\n        if (!preg_match('/^USER (\\w+)\\r\\n$/', $buf, $m)) {\n            fputs($s, \"500 Syntax error, command unrecognized.\\r\\n\");\n            dump_and_exit($buf);\n        }\n        $user = $m[1];\n        if ($user !== 'user' && $user !== 'anonymous') {\n            fputs($s, \"530 Not logged in.\\r\\n\");\n            exit;\n        }\n\n        if (anonymous()) {\n            fputs($s, \"230 Anonymous user logged in\\r\\n\");\n        } else {\n            fputs($s, \"331 User name ok, need password\\r\\n\");\n\n            if (!preg_match('/^PASS (\\w+)\\r\\n$/', $buf = fread($s, 100), $m)) {\n                fputs($s, \"500 Syntax error, command unrecognized.\\r\\n\");\n                dump_and_exit($buf);\n            }\n\n            $pass = $m[1];\n            if ($pass === 'pass') {\n                fputs($s, \"230 User logged in\\r\\n\");\n            } else {\n                fputs($s, \"530 Not logged in.\\r\\n\");\n                exit;\n            }\n        }\n    }\n\n    user_auth($buf);\n\n    $cwd = '/';\n    $num_bogus_cmds = 0;\n\n    while ($buf = fread($s, 4098)) {\n        if (!empty($bogus)) {\n            fputs($s, '502 Command not implemented (' . $num_bogus_cmds++ . \").\\r\\n\");\n        } elseif ($buf === \"HELP\\r\\n\") {\n            fputs($s, \"214-There is help available for the following commands:\\r\\n\");\n            fputs($s, \" USER\\r\\n\");\n            fputs($s, \" HELP\\r\\n\");\n            fputs($s, \"214 end of list\\r\\n\");\n        } elseif ($buf === \"HELP HELP\\r\\n\") {\n            fputs($s, \"214 Syntax: HELP [<SP> <string>] <CRLF>\\r\\n\");\n        } elseif ($buf === \"PWD\\r\\n\") {\n            fputs($s, \"257 \\\"{$cwd}\\\" is current directory.\\r\\n\");\n        } elseif ($buf === \"CDUP\\r\\n\") {\n            change_dir('..');\n            fputs($s, \"250 CDUP command successful.\\r\\n\");\n        } elseif ($buf === \"SYST\\r\\n\") {\n            if (isset($bug27809)) {\n                fputs($s, \"215   OS/400 is the remote operating system. The TCP/IP version is \\\"V5R2M0\\\"\\r\\n\");\n            } elseif (isset($bug79100)) {\n                // do nothing so test hits timeout\n            } elseif (isset($bug80901)) {\n                fputs($s, \"\\r\\n\" . str_repeat('*', 4096) . \"\\r\\n\");\n            } else {\n                fputs($s, \"215 UNIX Type: L8.\\r\\n\");\n            }\n        } elseif ($buf === \"TYPE A\\r\\n\") {\n            $ascii = true;\n            fputs($s, \"200 OK\\r\\n\");\n        } elseif ($buf === \"AUTH SSL\\r\\n\") {\n            $ascii = true;\n            fputs($s, \"500 not supported\\r\\n\");\n        } elseif ($buf === \"TYPE I\\r\\n\") {\n            $ascii = false;\n            fputs($s, \"200 OK\\r\\n\");\n        } elseif ($buf === \"QUIT\\r\\n\") {\n            fputs($s, \"221 Bye\\r\\n\");\n            break;\n        } elseif (preg_match(\"~^PORT (\\\\d+),(\\\\d+),(\\\\d+),(\\\\d+),(\\\\d+),(\\\\d+)\\r\\n$~\", $buf, $m)) {\n            $host = \"{$m[1]}.{$m[2]}.{$m[3]}.{$m[4]}\";\n            $port = ((int) $m[5] << 8) + (int) $m[6];\n            fputs($s, \"200 OK.\\r\\n\");\n        } elseif (preg_match(\"~^STOR ([\\\\w/.-]+)\\r\\n$~\", $buf, $m)) {\n            fputs($s, \"150 File status okay; about to open data connection\\r\\n\");\n\n            if (empty($pasv)) {\n                if (!$fs = stream_socket_client(\"tcp://{$host}:{$port}\")) {\n                    fputs($s, \"425 Can't open data connection\\r\\n\");\n                    continue;\n                }\n\n                $data = stream_get_contents($fs);\n                $orig = file_get_contents(dirname(__FILE__) . '/' . $m[1]);\n\n                if (isset($ascii) && !$ascii && $orig === $data) {\n                    fputs($s, \"226 Closing data Connection.\\r\\n\");\n                } elseif ((!empty($ascii) || isset($bug39583)) && $data === strtr($orig, [\"\\r\\n\" => \"\\n\", \"\\r\" => \"\\n\", \"\\n\" => \"\\r\\n\"])) {\n                    fputs($s, \"226 Closing data Connection.\\r\\n\");\n                } else {\n                    var_dump($data);\n                    var_dump($orig);\n                    fputs($s, \"552 Requested file action aborted.\\r\\n\");\n                }\n                fclose($fs);\n            } else {\n                $data = file_get_contents('nm2.php');\n                $orig = file_get_contents(dirname(__FILE__) . '/' . $m[1]);\n                if ($orig === $data) {\n                    fputs($s, \"226 Closing data Connection.\\r\\n\");\n                } else {\n                    var_dump($data);\n                    var_dump($orig);\n                    fputs($s, \"552 Requested file action aborted.\\r\\n\");\n                }\n            }\n        } elseif (preg_match(\"~^APPE ([\\\\w/.-]+)\\r\\n$~\", $buf, $m)) {\n            fputs($s, \"150 File status okay; about to open data connection\\r\\n\");\n\n            if (empty($pasv)) {\n                if (!$fs = stream_socket_client(\"tcp://{$host}:{$port}\")) {\n                    fputs($s, \"425 Can't open data connection\\r\\n\");\n                    continue;\n                }\n\n                $data = stream_get_contents($fs);\n                file_put_contents(__DIR__ . '/' . $m[1], $data, FILE_APPEND);\n                fputs($s, \"226 Closing data Connection.\\r\\n\");\n                fclose($fs);\n            } else {\n                $data = stream_get_contents($fs);\n                file_put_contents(__DIR__ . '/' . $m[1], $data, FILE_APPEND);\n                fputs($s, \"226 Closing data Connection.\\r\\n\");\n                fclose($fs);\n            }\n        } elseif (preg_match(\"~^CWD ([A-Za-z./]+)\\r\\n$~\", $buf, $m)) {\n            if (isset($bug77680)) {\n                fputs($s, \"550 Directory change to {$m[1]} failed: file does not exist\\r\\n\");\n                var_dump($buf);\n            } else {\n                change_dir($m[1]);\n                fputs($s, \"250 CWD command successful.\\r\\n\");\n            }\n        } elseif (preg_match(\"~^NLST(?: ([A-Za-z./]+))?\\r\\n$~\", $buf, $m)) {\n            if (isset($m[1]) && (($m[1] === 'bogusdir') || ($m[1] === '/bogusdir'))) {\n                fputs($s, \"250 {$m[1]}: No such file or directory\\r\\n\");\n                continue;\n            }\n\n            // there are some servers that don't open the ftp-data socket if there's nothing to send\n            if (isset($bug39458, $m[1]) && $m[1] === 'emptydir') {\n                fputs($s, \"226 Transfer complete.\\r\\n\");\n                continue;\n            }\n\n            if (empty($pasv)) {\n                fputs($s, \"150 File status okay; about to open data connection\\r\\n\");\n                if (!$fs = stream_socket_client(\"tcp://{$host}:{$port}\")) {\n                    fputs($s, \"425 Can't open data connection\\r\\n\");\n                    continue;\n                }\n            } else {\n                fputs($s, \"125 Data connection already open; transfer starting.\\r\\n\");\n                $fs = $pasvs;\n            }\n\n            if ((!empty($ssl)) && (!stream_socket_enable_crypto($pasvs, true, STREAM_CRYPTO_METHOD_SSLv23_SERVER))) {\n                exit(\"SSLv23 handshake failed.\\n\");\n            }\n\n            if (empty($m[1]) || $m[1] !== 'emptydir') {\n                fputs($fs, \"file1\\r\\nfile1\\r\\nfile\\nb0rk\\r\\n\");\n            }\n\n            fputs($s, \"226 Closing data Connection.\\r\\n\");\n            fclose($fs);\n        } elseif (preg_match(\"~^MKD ([A-Za-z./]+)\\r\\n$~\", $buf, $m)) {\n            if (isset($bug7216)) {\n                fputs($s, \"257 OK.\\r\\n\");\n            } else {\n                if (isset($bug77680)) {\n                    var_dump($buf);\n                }\n                fputs($s, \"257 \\\"/path/to/ftproot{$cwd}{$m[1]}\\\" created.\\r\\n\");\n            }\n        } elseif (preg_match('/^USER /', $buf)) {\n            user_auth($buf);\n        } elseif (preg_match('/^MDTM ([\\w\\h]+)/', $buf, $matches)) {\n            switch ($matches[1]) {\n                case 'A':\n                    fputs($s, \"213 19980615100045.014\\r\\n\");\n                    break;\n                case 'B':\n                    fputs($s, \"213 19980615100045.014\\r\\n\");\n                    break;\n                case 'C':\n                    fputs($s, \"213 19980705132316\\r\\n\");\n                    break;\n                case '19990929043300 File6':\n                    fputs($s, \"213 19991005213102\\r\\n\");\n                    break;\n                default:\n                    fputs($s, \"550 No file named \\\"{$matches[1]}\\\"\\r\\n\");\n                    break;\n            }\n        } elseif (preg_match('/^RETR ([\\/]*[\\w\\h]+)/', $buf, $matches)) {\n            if (!empty($pasv)) {\n            } elseif (!$fs = stream_socket_client(\"tcp://{$host}:{$port}\")) {\n                fputs($s, \"425 Can't open data connection\\r\\n\");\n                continue;\n            }\n\n            switch ($matches[1]) {\n                case 'pasv':\n                    fputs($s, \"150 File status okay; about to open data connection.\\r\\n\");\n                    // the data connection is handled in another forked process\n                    // called from outside this while loop\n                    fputs($s, \"226 Closing data Connection.\\r\\n\");\n                    break;\n                case 'a story':\n                    fputs($s, \"150 File status okay; about to open data connection.\\r\\n\");\n                    fputs($fs, \"For sale: baby shoes, never worn.\\r\\n\");\n                    fputs($s, \"226 Closing data Connection.\\r\\n\");\n                    break;\n                case 'binary data':\n                    fputs($s, \"150 File status okay; about to open data connection.\\r\\n\");\n                    $transfer_type = $ascii ? 'ASCII' : 'BINARY';\n                    fputs($fs, $transfer_type . \"Foo\\0Bar\\r\\n\");\n                    fputs($s, \"226 Closing data Connection.\\r\\n\");\n                    break;\n                case 'fget':\n                    fputs($s, \"150 File status okay; about to open data connection.\\r\\n\");\n                    $transfer_type = $ascii ? 'ASCII' : 'BINARY';\n                    fputs($fs, $transfer_type . \"FooBar\\r\\n\");\n                    fputs($s, \"226 Closing data Connection.\\r\\n\");\n                    break;\n                case 'fgetresume':\n                    fputs($s, \"150 File status okay; about to open data connection.\\r\\n\");\n                    $transfer_type = $ascii ? 'ASCII' : 'BINARY';\n                    fputs($fs, \"Bar\\r\\n\");\n                    fputs($s, \"226 Closing data Connection.\\r\\n\");\n                    break;\n                case 'fget_large':\n                    fputs($s, \"150 File status okay; about to open data connection.\\r\\n\");\n                    $transfer_type = $ascii ? 'ASCII' : 'BINARY';\n                    if ($GLOBALS['rest_pos'] == '5368709119') {\n                        fputs($fs, 'X');\n                    } else {\n                        fputs($fs, 'Y');\n                    }\n                    fputs($s, \"226 Closing data Connection.\\r\\n\");\n                    break;\n                case 'mediumfile':\n                    fputs($s, \"150 File status okay; about to open data connection.\\r\\n\");\n                    for ($i = 0; $i < 150; $i++) {\n                        fputs($fs, \"This is line {$i} of the test data.\\n\");\n                    }\n                    fputs($s, \"226 Closing data Connection.\\r\\n\");\n                    break;\n                case '/bug73457':\n                    fputs($s, \"150 File status okay; about to open data connection.\\r\\n\");\n                    break;\n                case 'gh10521':\n                    // Just a side channel for getting the received file size.\n                    fputs($s, \"425 Can't open data connection (\" . $GLOBALS['rest_pos'] . \").\\r\\n\");\n                    break;\n                default:\n                    fputs($s, \"550 {$matches[1]}: No such file or directory \\r\\n\");\n                    break;\n            }\n            if (isset($fs)) {\n                fclose($fs);\n            }\n        } elseif (preg_match('/^PASV/', $buf, $matches)) {\n            $pasv = true;\n            $host = '127.0.0.1';\n            $i = 0;\n\n            if (empty($bug73457)) {\n                if (!empty($ssl)) {\n                    $soc = stream_socket_server('tcp://127.0.0.1:0', $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $context);\n                } else {\n                    $soc = stream_socket_server('tcp://127.0.0.1:0');\n                }\n                if (!$soc) {\n                    echo \"{$errstr} ({$errno})\\n\";\n                    exit(\"could not bind passive port\\n\");\n                }\n\n                $soc_name = stream_socket_get_name($soc, false);\n                $pasv_port = (int) substr($soc_name, strrpos($soc_name, ':') + 1);\n            } else {\n                $pasv_port = 1234;\n            }\n\n            $p2 = $pasv_port % ((int) 1 << 8);\n            $p1 = ($pasv_port - $p2) / ((int) 1 << 8);\n            fputs($s, \"227 Entering Passive Mode. (127,0,0,1,{$p1},{$p2})\\r\\n\");\n\n            if (empty($bug73457)) {\n                $pasvs = stream_socket_accept($soc, 10);\n            }\n        } elseif (preg_match('/^EPSV/', $buf, $matches)) {\n            fputs($s, \"550 Extended passsive mode not supported.\\r\\n\");\n        } elseif (preg_match('/^SITE EXEC/', $buf, $matches)) {\n            fputs($s, \"200 OK\\r\\n\");\n        } elseif (preg_match('/^RMD/', $buf, $matches)) {\n            fputs($s, \"250 OK\\r\\n\");\n        } elseif (preg_match('/^SITE CHMOD/', $buf, $matches)) {\n            fputs($s, \"200 OK\\r\\n\");\n        } elseif (preg_match('/^DELE ([\\w\\h]+)/', $buf, $matches)) {\n            if (isset($matches[1]) && in_array($matches[1], ['file1', \"file\\nb0rk\"])) {\n                fputs($s, \"250 Delete successful\\r\\n\");\n            } else {\n                fputs($s, \"550 No such file or directory\\r\\n\");\n            }\n        } elseif (preg_match('/^ALLO (\\d+)/', $buf, $matches)) {\n            fputs($s, '200 ' . $matches[1] . \" bytes allocated\\r\\n\");\n        } elseif (preg_match('/^LIST www\\//', $buf, $matches)) {\n            if (empty($pasv)) {\n                fputs($s, \"150 File status okay; about to open data connection\\r\\n\");\n                if (!$fs = stream_socket_client(\"tcp://{$host}:{$port}\")) {\n                    fputs($s, \"425 Can't open data connection\\r\\n\");\n                    continue;\n                }\n            } else {\n                fputs($s, \"125 Data connection already open; transfer starting.\\r\\n\");\n                $fs = $pasvs;\n            }\n\n            if ((!empty($ssl)) && (!stream_socket_enable_crypto($pasvs, true, STREAM_CRYPTO_METHOD_SSLv23_SERVER))) {\n                exit(\"SSLv23 handshake failed.\\n\");\n            }\n\n            fputs($fs, \"file1\\r\\nfile1\\r\\nfile\\nb0rk\\r\\n\");\n            fputs($s, \"226 Closing data Connection.\\r\\n\");\n            fclose($fs);\n\n            fputs($s, \"226 Transfer complete\\r\\n\");\n        } elseif (preg_match('/^LIST no_exists\\//', $buf, $matches)) {\n            fputs($s, \"425 Error establishing connection\\r\\n\");\n        } elseif (preg_match('/^REST (\\d+)/', $buf, $matches)) {\n            $GLOBALS['rest_pos'] = $matches[1];\n            fputs($s, \"350 OK\\r\\n\");\n        } elseif (preg_match('/^SIZE largefile/', $buf)) {\n            fputs($s, \"213 5368709120\\r\\n\");\n        } elseif (preg_match('/^RNFR existing_file/', $buf, $matches)) {\n            fputs($s, \"350 File or directory exists, ready for destination name\\r\\n\");\n        } elseif (preg_match('/^RNFR nonexisting_file/', $buf, $matches)) {\n            fputs($s, \"550 No such file or directory\\r\\n\");\n        } elseif (preg_match('/^RNTO nonexisting_file/', $buf, $matches)) {\n            fputs($s, \"250 Rename successful\\r\\n\");\n        } elseif (preg_match('/^MLSD no_exists\\//', $buf, $matches)) {\n            fputs($s, \"425 Error establishing connection\\r\\n\");\n        } elseif (preg_match(\"~^MLSD(?: ([A-Za-z./]+))?\\r\\n$~\", $buf, $m)) {\n            if (isset($m[1]) && (($m[1] === 'bogusdir') || ($m[1] === '/bogusdir'))) {\n                fputs($s, \"250 {$m[1]}: No such file or directory\\r\\n\");\n                continue;\n            }\n\n            // there are some servers that don't open the ftp-data socket if there's nothing to send\n            if (isset($bug39458, $m[1]) && $m[1] === 'emptydir') {\n                fputs($s, \"226 Transfer complete.\\r\\n\");\n                continue;\n            }\n\n            if (empty($pasv)) {\n                fputs($s, \"150 File status okay; about to open data connection\\r\\n\");\n                if (!$fs = stream_socket_client(\"tcp://{$host}:{$port}\")) {\n                    fputs($s, \"425 Can't open data connection\\r\\n\");\n                    continue;\n                }\n            } else {\n                fputs($s, \"125 Data connection already open; transfer starting.\\r\\n\");\n                $fs = $pasvs;\n            }\n\n            if ((!empty($ssl)) && (!stream_socket_enable_crypto($pasvs, true, STREAM_CRYPTO_METHOD_SSLv23_SERVER))) {\n                exit(\"SSLv23 handshake failed.\\n\");\n            }\n\n            if (empty($m[1]) || $m[1] !== 'emptydir') {\n                fputs($fs, \"modify=20170127230002;perm=flcdmpe;type=cdir;unique=811U4340002;UNIX.group=33;UNIX.mode=0755;UNIX.owner=33; .\\r\\n\");\n                fputs($fs, \"modify=20170127230002;perm=flcdmpe;type=pdir;unique=811U4340002;UNIX.group=33;UNIX.mode=0755;UNIX.owner=33; ..\\r\\n\");\n                fputs($fs, \"modify=20170126121225;perm=adfrw;size=4729;type=file;unique=811U4340CB9;UNIX.group=33;UNIX.mode=0644;UNIX.owner=33; foobar\\r\\n\");\n                fputs($fs, \"fact=val=ue;empty=; path;name\\r\\n\");\n                fputs($fs, \"no_space\\r\\n\");\n                fputs($fs, \"no_semi pathname\\r\\n\");\n                fputs($fs, \"no_eq; pathname\\r\\n\");\n            }\n\n            fputs($s, \"226 Closing data Connection.\\r\\n\");\n            fclose($fs);\n        } elseif (preg_match('/^SIZE \\/bug73457/', $buf)) {\n            fputs($s, \"213 10\\r\\n\");\n        } elseif (preg_match('/^SITE/', $buf)) {\n            fputs($s, \"500 Syntax error, command unrecognized.\\r\\n\");\n        } else {\n            dump_and_exit($buf);\n        }\n    }\n    exit;\n}\n\nfclose($socket);\n\nreturn function () use ($pid, $port) {\n    Co\\defer(function () use ($pid, $port) {\n        $out = Co\\System::waitpid($pid);\n        assert($out['code'] == 0);\n    });\n    return $port;\n};\n"
  },
  {
    "path": "tests/swoole_function/substr_json_decode.phpt",
    "content": "--TEST--\nswoole_function: substr_json_decode\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_function_not_exist('swoole_substr_json_decode');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$a['hello'] = base64_encode(random_bytes(1000));\n$a['world'] = 'hello';\n$a['int'] = rand(1, 999999);\n$a['list'] = ['a,', 'b', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'];\n\n$val = json_encode($a);\n$str = pack('N', strlen($val)).$val.\"\\r\\n\";\n\n$l = strlen($str) - 6;\nAssert::eq(swoole_substr_json_decode($str, 4, 0, true), $a);\nAssert::eq(@swoole_substr_json_decode($str, 0, -1, true), false);\nAssert::eq(@swoole_substr_json_decode($str, 6, 0, true), false);\nAssert::eq(@swoole_substr_json_decode($str, strlen($str) + 10, 0, true), false);\nAssert::eq(@swoole_substr_json_decode($str, - (strlen($str) + 5), 0, true), false);\n// offset is negative\nAssert::eq(swoole_substr_json_decode($str, -(strlen($str)-4), $l, true), $a);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_function/substr_unserialize.phpt",
    "content": "--TEST--\nswoole_function: ext_unserialize\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$a['hello'] = base64_encode(random_bytes(1000));\n$a['world'] = 'hello';\n$a['int'] = rand(1, 999999);\n$a['list'] = ['a,', 'b', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'];\n\n$val = serialize($a);\n$str = pack('N', strlen($val)) . $val . \"\\r\\n\";\n\n$l = strlen($str) - 6;\nAssert::eq(swoole_substr_unserialize($str, 4, $l), $a);\nAssert::eq(@swoole_substr_unserialize($str, 4), $a);\nAssert::eq(@swoole_substr_unserialize($str, 0), false);\nAssert::eq(@swoole_substr_unserialize($str, 6), false);\nAssert::eq(@swoole_substr_unserialize($str, 4, $l - 4), false);\nAssert::eq(@swoole_substr_unserialize($str, strlen($str) + 10), false);\nAssert::eq(@swoole_substr_unserialize($str, - (strlen($str) + 5)), false);\n// offset is negative\nAssert::eq(swoole_substr_unserialize($str, -(strlen($str)-4), $l), $a);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_function/swoole_clear_dns_cache.phpt",
    "content": "--TEST--\nswoole_function: test swoole_clear_dns_cache\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nswoole_clear_dns_cache();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_function/swoole_cpu_num.phpt",
    "content": "--TEST--\nswoole_function: swoole_cpu_num\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$cpu_num = swoole_cpu_num();\necho \"cpu_num: $cpu_num\";\n\n?>\n--EXPECTF--\ncpu_num: %d\n"
  },
  {
    "path": "tests/swoole_function/swoole_error_log.phpt",
    "content": "--TEST--\nswoole_function: swoole_error_log\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst LOG_FILE = __DIR__ . '/log';\nif (is_file(LOG_FILE)) {\n    unlink(LOG_FILE);\n}\n\nconst ERROR_1 = 888888;\nconst ERROR_2 = 999999;\n\nswoole_async_set(['log_file' => LOG_FILE]);\nswoole_error_log(SWOOLE_LOG_NOTICE, \"hello 1\");\nswoole_error_log_ex(SWOOLE_LOG_NOTICE, ERROR_1, \"hello 2\");\n\nswoole_ignore_error(ERROR_2);\nswoole_error_log_ex(SWOOLE_LOG_NOTICE, ERROR_2, \"hello 3\");\n\n$content = file_get_contents(LOG_FILE);\nAssert::contains($content, 'hello 1');\nAssert::contains($content, 'hello 2');\nAssert::contains($content, '(ERRNO ' . ERROR_1 . ')');\nAssert::notContains($content, 'hello 3');\nunlink(LOG_FILE);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_function/swoole_get_local_ip.phpt",
    "content": "--TEST--\nswoole_function: get local ip\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$ips = swoole_get_local_ip();\nforeach ($ips as $ip) {\n    Assert::same(filter_var($ip, FILTER_VALIDATE_IP), $ip);\n    Assert::assert(strstr($ip, \".\", true) !== \"127\");\n}\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_function/swoole_get_local_mac.phpt",
    "content": "--TEST--\nswoole_function: get mac address\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$macs = swoole_get_local_mac();\nAssert::assert(is_array($macs));\nforeach ($macs as $mac) {\n    Assert::same(filter_var($mac, FILTER_VALIDATE_MAC), $mac);\n}\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_function/swoole_set_process_name.phpt",
    "content": "--TEST--\nswoole_function: set process name\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_darwin();\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$name = \"SWOOLE_PROCESS_TEST_\" . rand(1, 100);\nswoole_set_process_name($name);\n$count = (int) trim(shell_exec(\"ps aux|grep $name|grep -v grep|wc -l\"));\nAssert::same($count, 1);\necho \"SUCCESS\";\n\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_function/swoole_strerror.phpt",
    "content": "--TEST--\nswoole_function: swoole_strerror\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nif (!is_musl_libc()) {\n    Assert::assert(\n        swoole_strerror(IS_MAC_OS ? 4 : -4 /*EAI_FAIL*/, SWOOLE_STRERROR_GAI) ===\n        'Non-recoverable failure in name resolution'\n    );\n    Assert::assert(\n        swoole_strerror(2 /*NO_ADDRESS*/, SWOOLE_STRERROR_DNS) ===\n        'Host name lookup failure'\n    );\n}\necho swoole_strerror(SOCKET_ECONNRESET) . \"\\n\";\necho swoole_strerror(SWOOLE_ERROR_FILE_NOT_EXIST) . \"\\n\";\nif (!is_musl_libc()) {\n    $unknown = swoole_strerror(SWOOLE_ERROR_MALLOC_FAIL - 1);\n    $sw_unknown = swoole_strerror(SWOOLE_ERROR_MALLOC_FAIL - 1, SWOOLE_STRERROR_SWOOLE);\n    Assert::same($unknown, $sw_unknown);\n} else {\n    Assert::same(swoole_strerror(SWOOLE_ERROR_MALLOC_FAIL - 1), 'No error information');\n}\n?>\n--EXPECT--\nConnection reset by peer\nFile not exist\n"
  },
  {
    "path": "tests/swoole_function/swoole_version.phpt",
    "content": "--TEST--\nswoole_function: swoole_version\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$version = swoole_version();\necho \"swoole_version: $version\";\n\n?>\n--EXPECTF--\nswoole_version: %s\n"
  },
  {
    "path": "tests/swoole_global/channel_construct_check.phpt",
    "content": "--TEST--\nswoole_global: socket construct check\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = ProcessManager::exec(function () {\n    go(function () {\n        $chan = new class () extends Co\\Channel {\n            public function __construct($size = 1)\n            {\n                // parent::__construct($size);  // without parent call\n            }\n        };\n        $chan->push('123');\n    });\n});\nAssert::contains($pm->getChildOutput(), \"must call constructor first\");\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_global/closed_stdout.phpt",
    "content": "--TEST--\nswoole_global: handle closed STDOUT/STDERR without exception\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nfclose(STDERR);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_global/create_deny.phpt",
    "content": "--TEST--\nswoole_global: deny create object\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    try {\n        new Swoole\\Coroutine;\n    } catch (Error $e) {\n        echo $e->getMessage() . PHP_EOL;\n    }\n    try {\n        new Swoole\\Event;\n    } catch (Error $e) {\n        echo $e->getMessage() . PHP_EOL;\n    }\n    try {\n        new Swoole\\Runtime;\n    } catch (Error $e) {\n        echo $e->getMessage() . PHP_EOL;\n    }\n    try {\n        new Swoole\\Timer;\n    } catch (Error $e) {\n        echo $e->getMessage() . PHP_EOL;\n    }\n});\n?>\n--EXPECT--\nThe object of Swoole\\Coroutine can not be created for security reasons\nThe object of Swoole\\Event can not be created for security reasons\nThe object of Swoole\\Runtime can not be created for security reasons\nThe object of Swoole\\Timer can not be created for security reasons\n"
  },
  {
    "path": "tests/swoole_global/function_alias.phpt",
    "content": "--TEST--\nswoole_global: function alias\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nvar_dump(function_exists('go') && function_exists('defer'));\n\n?>\n--EXPECTF--\nbool(true)\n"
  },
  {
    "path": "tests/swoole_global/serialize_deny.phpt",
    "content": "--TEST--\nswoole_global: deny serialize and unserialize\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    try {\n        $hcc = new \\Swoole\\Atomic();\n        serialize($hcc);\n        Assert::true(false, 'never here');\n    } catch (\\Exception $exception) {\n        Assert::same(strpos($exception->getMessage(), 'Serialization'), 0);\n    }\n    try {\n        $hcc = new \\Swoole\\Client(SWOOLE_TCP);\n        serialize($hcc);\n        Assert::true(false, 'never here');\n    } catch (\\Exception $exception) {\n        Assert::same(strpos($exception->getMessage(), 'Serialization'), 0);\n    }\n    try {\n        $hcc = new \\Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        serialize($hcc);\n        Assert::true(false, 'never here');\n    } catch (\\Exception $exception) {\n        Assert::same(strpos($exception->getMessage(), 'Serialization'), 0);\n    }\n    try {\n        $hcc = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1');\n        serialize($hcc);\n        Assert::true(false, 'never here');\n    } catch (\\Exception $exception) {\n        Assert::same(strpos($exception->getMessage(), 'Serialization'), 0);\n    }\n    try {\n        $hcc = new \\Swoole\\Table(1);\n        serialize($hcc);\n        Assert::true(false, 'never here');\n    } catch (\\Exception $exception) {\n        Assert::same(strpos($exception->getMessage(), 'Serialization'), 0);\n    }\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_global/socket_construct_check.phpt",
    "content": "--TEST--\nswoole_global: socket construct check\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = ProcessManager::exec(function () {\n    go(function () {\n        $socket = new class (1, 2, 3) extends Co\\Socket {\n            public function __construct($domain, $type, $protocol)\n            {\n                // parent::__construct($domain, $type, $protocol); // without parent call\n            }\n        };\n        $socket->connect('127.0.0.1', 12345);\n    });\n});\nAssert::contains($pm->getChildOutput(), \"must call constructor first\");\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_global/too_many_objects.phpt",
    "content": "--TEST--\nswoole_global: too many objects\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$object_store = [];\nfor ($n = 65536; $n--;) {\n    $object_store[] = new stdClass();\n}\n$server = new Swoole\\WebSocket\\Server('127.0.0.1', get_one_free_port(), SWOOLE_BASE);\n$tcp_server = $server->listen('127.0.0.1', get_one_free_port(), SWOOLE_TCP);\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_global/unset_deny.phpt",
    "content": "--TEST--\nswoole_global: deny unset properties and clone\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$chan = new Chan;\n$chan->test = 1;\nAssert::same($chan->test, 1);\nunset($chan->test);\nAssert::true(!isset($chan->test));\n\n// clone error\ntry {\n    $chan = clone $chan;\n} catch (Error $e) {\n    echo \"{$e->getMessage()}\\n\";\n}\n\n// unset error\ntry {\n    unset($chan->errCode);\n} catch (Error $e) {\n    echo \"{$e->getMessage()}\\n\";\n    Assert::true(isset($chan->errCode));\n}\n?>\n--EXPECT--\nTrying to clone an uncloneable object of class Swoole\\Coroutine\\Channel\nProperty errCode of class Swoole\\Coroutine\\Channel cannot be unset\n"
  },
  {
    "path": "tests/swoole_global/unset_property_01.phpt",
    "content": "--TEST--\nswoole_global: unset internal class property\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$c = new chan;\nunset($c->capacity);\n?>\n--EXPECTF--\nFatal error: Uncaught Error: Property capacity of class Swoole\\Coroutine\\Channel cannot be unset in %s/tests/%s/unset%s.php:%d\nStack trace:\n#0 {main}\n  thrown in %s\n"
  },
  {
    "path": "tests/swoole_global/unset_property_02.phpt",
    "content": "--TEST--\nswoole_global: unset user class's parent internal property\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nclass C extends chan\n{\n    public $t = 1;\n}\n$c = new C;\nunset($c->capacity);\n?>\n--EXPECTF--\nFatal error: Uncaught Error: Property capacity of class %s cannot be unset in %s:%d\nStack trace:\n#0 {main}\n  thrown in %s\n"
  },
  {
    "path": "tests/swoole_global/unset_property_03.phpt",
    "content": "--TEST--\nswoole_global: unset user class's own property\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nclass C extends chan\n{\n    public $t = 1;\n}\n$c = new C;\nunset($c->t);\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/bug_5127.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: Github #5127 When use swoole in php 8.2，Swoole\\Http2\\Request may throw ErrorException：Creation of dynamic property $usePipelineRead\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$request = new Swoole\\Http2\\Request();\n$request->usePipelineRead = true;\necho 'DONE';\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/connect_twice.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: connect twice\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Channel;\nuse Swoole\\Coroutine\\Http2\\Client;\nuse Swoole\\Http2\\Request;\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\nrun(function () {\n    $client = new Client('httpbin.org', 443, true);\n    $chan = new Channel(1);\n    go(function () use ($client, $chan) {\n        $client->connect();\n        $req = new Request();\n        $uuid = uniqid();\n        $req->method = 'GET';\n        $req->path = '/base64/' . base64_encode($uuid);\n        $client->send($req);\n        $chan->push(true);\n        $resp = $client->recv();\n        Assert::notNull($resp);\n        Assert::eq($resp->statusCode, 200);\n        Assert::eq($resp->data, $uuid);\n        $chan->pop();\n    });\n    go(function () use ($client, $chan) {\n        Assert::eq($client->connect(), false);\n        $uuid = uniqid();\n        $req = new Request();\n        $req->method = 'GET';\n        $req->path = '/base64/' . base64_encode($uuid);\n        $client->send($req);\n        $chan->push(true);\n        Assert::eq($client->recv(), false);\n        $chan->pop();\n    });\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/cookies.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: cookies\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->connect();\n        $request = new Swoole\\Http2\\Request;\n        for ($n = MAX_REQUESTS; $n--;) {\n            $request->cookies = [];\n            for ($k = 32; $k--;) {\n                $request->cookies[get_safe_random()] = get_safe_random();\n            }\n            Assert::assert($cli->send($request));\n            $response = $cli->recv(1);\n            Assert::same('OK', $response->data);\n            Assert::same($request->cookies, $response->cookies);\n        }\n        echo \"DONE\\n\";\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true\n    ]);\n    $http->on('workerStart', function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        foreach ($request->cookie as $name => $value) {\n            $response->cookie($name, $value);\n        }\n        $response->end('OK');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/error.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: cookies\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $cli = new Swoole\\Coroutine\\Http2\\Client('192.0.0.1', 9000);\n    $cli->set(['timeout' => 0.1]);\n    Assert::false($cli->connect());\n    Assert::same($cli->errCode, SOCKET_ETIMEDOUT);\n    Assert::false($cli->send(new Swoole\\Http2\\Request));\n    Assert::same($cli->errCode, SWOOLE_ERROR_CLIENT_NO_CONNECTION);\n    Assert::false($cli->recv(1));\n    Assert::same($cli->errCode, SWOOLE_ERROR_CLIENT_NO_CONNECTION);\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/goaway.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: http2 go away\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $domain = 'nghttp2.org';\n    $cli = new Swoole\\Coroutine\\Http2\\Client($domain, 443, true);\n    $cli->set([\n        'timeout' => 1,\n        'ssl_host_name' => $domain\n    ]);\n    if (!$cli->connect()) {\n        return; // we can't connect to this website without proxy in China so we skip it.\n    }\n    $cli->goaway(SWOOLE_HTTP2_ERROR_NO_ERROR, '[GOAWAY] nothing~bye~bye~');\n    Assert::assert(!$cli->recv(-1));\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/headers.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: http2 headers auto to lower\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $domain = 'mail.qq.com';\n    $cli = new Swoole\\Coroutine\\Http2\\Client($domain, 443, true);\n    $cli->set([\n        'timeout' => 10,\n        'ssl_host_name' => $domain\n    ]);\n    $cli->connect();\n\n    $req = new Swoole\\Http2\\Request;\n    $req->path = '/';\n    // auto to-lower\n    $req->headers = [\n        'Host' => $domain,\n        'User-Agent' => 'Chrome/49.0.2587.3',\n        'Accept' => 'text/html,application/xhtml+xml,application/xml',\n        'Accept-encoding' => 'gzip',\n    ];\n    for ($n = 5; $n--;) {\n        Assert::assert($cli->send($req));\n        $response = $cli->recv();\n        echo \"{$response->statusCode}\\n\";\n    }\n});\n?>\n--EXPECT--\n200\n200\n200\n200\n200\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/host.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: host\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $domain = '127.0.0.1';\n        $cli = new Swoole\\Coroutine\\Http2\\Client($domain, $pm->getFreePort(), true);\n        $cli->set(['timeout' => 5]);\n        $cli->connect();\n        $request = new Swoole\\Http2\\Request;\n        for ($n = MAX_REQUESTS; $n--;) {\n            $request->path = '/';\n            $request->headers = [\n                'user-agent' => 'Chrome/49.0.2587.3',\n                'accept' => 'text/html,application/xhtml+xml,application/xml',\n                'connection' => 'keep-alive'\n            ];\n            for ($i = 32; $i--;) {\n                $request->headers[md5(mt_rand(1, 65535))] = sha1(get_safe_random(32));\n            }\n            Assert::assert($cli->send($request));\n            $response = $cli->recv();\n            Assert::same($response->statusCode, 200);\n            Assert::same(json_encode($request->headers), $response->data);\n            unset(\n                $response->headers['host'],\n                $response->headers['server'],\n                $response->headers['date'],\n                $response->headers['content-type'],\n                $response->headers['content-length']\n            );\n            Assert::same($request->headers, $response->headers);\n        }\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $http->set([\n        // 'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'http_compression' => false,\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key'\n    ]);\n    $http->on(\"WorkerStart\", function () use ($pm) { $pm->wakeup(); });\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($http) {\n        Assert::same($request->header['host'], \"{$http->host}:{$http->port}\");\n        unset($request->header['host']);\n        foreach ($request->header as $name => $value) {\n            $response->header($name, $value);\n        }\n        $response->end(json_encode($request->header));\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/http_proxy.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: get with http_proxy\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_no_http_proxy();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http2\\Client;\nuse Swoole\\Http2\\Request;\n\nCo\\run(function () {\n    $domain = 'cloudflare.com';\n    $c = new Client($domain, 443, true);\n    $c->set([\n        'timeout' => 10,\n        'ssl_host_name' => $domain,\n        'http_proxy_host' => HTTP_PROXY_HOST,\n        'http_proxy_port' => HTTP_PROXY_PORT,\n    ]);\n    Assert::true($c->connect(), var_dump_return($c));\n    $r = new Request();\n    $r->method = 'GET';\n    $r->path = '/';\n    $r->headers = [\n        'host' => $domain,\n    ];\n    $c->send($r);\n    $response = $c->recv();\n    Assert::notEmpty($response);\n    Assert::eq($response->statusCode, 301);\n    Assert::eq($response->headers['location'], 'https://www.cloudflare.com/');\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/huge_headers.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: huge headers\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip('internal changes');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $domain = '127.0.0.1';\n        $cli = new Swoole\\Coroutine\\Http2\\Client($domain, $pm->getFreePort(), true);\n        $cli->set(['timeout' => 5]);\n        $cli->connect();\n        $request = new Swoole\\Http2\\Request;\n        for ($n = MAX_REQUESTS; $n--;) {\n            $request->path = '/';\n            $request->headers = [\n                'host' => $domain,\n                'user-agent' => 'Chrome/49.0.2587.3',\n                'accept' => 'text/html,application/xhtml+xml,application/xml',\n                'connection' => 'keep-alive'\n            ];\n            for ($i = 32; $i--;) {\n                $request->headers[md5(mt_rand(1, 65535))] = sha1(get_safe_random(32));\n            }\n            Assert::assert($cli->send($request));\n            $response = $cli->recv();\n            Assert::same($response->statusCode, 200);\n            Assert::same(json_encode($request->headers), $response->data);\n            unset(\n                $response->headers['server'],\n                $response->headers['date'],\n                $response->headers['content-type'],\n                $response->headers['content-length']\n            );\n            Assert::same($request->headers, $response->headers);\n        }\n        for ($i = 32; $i--;) {\n            $request->headers[md5(mt_rand(1, 65535))] = sha1(get_safe_random(32));\n        }\n        Assert::assert(!$cli->send($request));\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $http->set([\n        // 'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'http_compression' => false,\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key'\n    ]);\n    $http->on(\"WorkerStart\", function () use ($pm) { $pm->wakeup(); });\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        foreach ($request->header as $name => $value) {\n            $response->header($name, $value);\n        }\n        $response->end(json_encode($request->header));\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nWarning: Swoole\\Coroutine\\Http2\\Client::send(): header cannot bigger than remote max_header_list_size %d in %s on line %d\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/issues_2374.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: Github#2374\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $domain = 'mail.qq.com';\n    $cli = new Swoole\\Coroutine\\Http2\\Client($domain, 443, true);\n    $cli->set([\n        'timeout' => 10,\n        'ssl_host_name' => $domain\n    ]);\n    $cli->connect();\n\n    $req = new Swoole\\Http2\\Request;\n    $req->path = '/';\n    // auto to-lower\n    $req->headers = [\n        'Host' => $domain,\n        'User-Agent' => 'Chrome/49.0.2587.3',\n        'Accept' => 'text/html,application/xhtml+xml,application/xml',\n        'Accept-encoding' => 'gzip',\n        'number' => 2333\n    ];\n    for ($n = 5; $n--;) {\n        Assert::assert($cli->send($req));\n        $response = $cli->recv();\n        echo \"{$response->statusCode}\\n\";\n    }\n});\n?>\n--EXPECT--\n200\n200\n200\n200\n200\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/max-frame-size.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: max frame size\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nif (strpos(shell_exec(\"nghttpd --version 2>&1\"), 'nghttp2') === false) {\n    skip('no nghttpd');\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        co::sleep(0.1);\n        $cli = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->connect();\n\n        $req = new Swoole\\Http2\\Request;\n        $req->path = \"/test.jpg\";\n        Assert::greaterThanEq($cli->send($req), 1);\n        $resp = $cli->recv();\n        Assert::contains($resp->headers['server'], 'nghttpd');\n        Assert::eq($resp->data, file_get_contents(TEST_IMAGE));\n\n        shell_exec(\"ps -A | grep nghttpd | awk '{print $1}' | xargs kill -9 > /dev/null 2>&1\");\n        echo \"DONE\\n\";\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $root = ROOT_DIR . '/examples';\n    $pm->wakeup();\n    shell_exec(\"nghttpd -v -d {$root}/ -a 0.0.0.0 {$pm->getFreePort()} --no-tls&\");\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/multi.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: multi\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $domain = 'nghttp2.org';\n    $cli = new Swoole\\Coroutine\\Http2\\Client($domain, 443, true);\n    $cli->set([\n        'timeout' => -1,\n        'ssl_host_name' => $domain\n    ]);\n    Assert::true($cli->connect());\n    Assert::true($cli->connected);\n\n    $req = new Swoole\\Http2\\Request;\n    $req->path = '/';\n    $req->headers = [\n        'Host' => $domain,\n        \"User-Agent\" => 'Chrome/49.0.2587.3',\n        'Accept' => 'text/html,application/xhtml+xml,application/xml',\n        'Accept-encoding' => 'gzip',\n    ];\n    /**@var $response swoole\\http2\\response */\n    $i = 4;\n    while ($i--) {\n        Assert::assert($cli->send($req));\n    }\n    $stream_map = [];\n    $responses_headers_count_map = [];\n    $i = 0;\n    while ($cli->connected) {\n        // it's for the test, you should make timeout bigger\n        $response = $cli->recv(0.1);\n        if ($response) {\n            echo \"$response->statusCode\\n\";\n            $responses_headers_count_map[] = count($response->headers);\n            Assert::contains($response->data, 'nghttp2.org');\n            $stream_map[] = $response->streamId;\n            if (++$i === 4) {\n                break;\n            }\n        }\n    }\n    Assert::assert(empty(array_diff([1, 3, 5, 7], $stream_map)));\n    Assert::lessThanEq(count(array_unique($responses_headers_count_map)), 2);\n    Assert::assert($responses_headers_count_map[0] > 10);\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n200\n200\n200\n200\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/no-gzip.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: http2 without gzip and recv big data (window-update)\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_ci('travis network');\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $domain = 'www.zhihu.com';\n    $cli = new Swoole\\Coroutine\\Http2\\Client($domain, 443, true);\n    $cli->set([\n        'timeout' => -1,\n        'ssl_host_name' => $domain\n    ]);\n    $cli->connect();\n\n    $req = new Swoole\\Http2\\Request;\n    $req->path = '/signup?next=/';\n    $req->headers = [\n        'Host' => $domain,\n        \"User-Agent\" => 'Chrome/49.0.2587.3',\n        'Accept' => 'text/html,application/xhtml+xml,application/xml',\n        'Accept-encoding' => ''\n    ];\n    $i = 5;\n    while ($i--) {\n        Assert::assert($cli->send($req));\n    }\n    $i = 5;\n    $map = [];\n    while ($i--) {\n        /**@var $response swoole_http2_response */\n        $response = $cli->recv();\n        Assert::same($response->statusCode, 200);\n        Assert::assert(strpos($response->data, 'zhihu') !== false);\n        $map[] = $response->streamId;\n    }\n    Assert::assert(!array_diff($map, [1, 3, 5, 7, 9]));\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/number.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: number\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Http2\\Client;\nuse Swoole\\Http\\Server;\nuse Swoole\\Http2\\Request;\nuse Swoole\\Http2\\Response;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        Assert::true($client->connect());\n        $streams = [];\n        $request = new Request;\n        for ($n = MAX_REQUESTS; $n--;) {\n            $request->data = $n;\n            $streams[$client->send($request)] = $n;\n        }\n        for ($n = MAX_REQUESTS; $n--;) {\n            /** @var $response Response */\n            $response = $client->recv();\n            Assert::same($streams[$response->streamId], (int)$response->data);\n        }\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true\n    ]);\n    $http->on('request', function (\\Swoole\\Http\\Request $request, \\Swoole\\Http\\Response $response) {\n        Coroutine::sleep(mt_rand(1, MAX_REQUESTS) / 1000);\n        $response->end($request->rawContent());\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/ping.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: http2 ping\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nCo::set(['log_level' => SWOOLE_LOG_WARNING]);\ngo(function () {\n    $cli = new Swoole\\Coroutine\\Http2\\Client('nghttp2.org', 443, true);\n    $cli->set(['timeout' => 5,]);\n    if (!$cli->connect()) {\n        return; // we can't connect to this website without proxy in China so we skip it.\n    }\n    $ret = $cli->ping();\n    Assert::assert($ret);\n    Co::sleep(0.5);\n    Assert::greaterThan($cli->send(new Swoole\\Http2\\Request), 0);\n    /** @var $response Swoole\\Http2\\Response */\n    $response = $cli->recv();\n    if (!$response && $cli->errCode === SOCKET_ETIMEDOUT) {\n        return;\n    }\n    if ($response && ((string) $response->statusCode)[0] === '5') {\n        return;\n    }\n    Assert::contains($response->data, 'nghttp2');\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/pipeline.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: pipeline\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->initRandomData(MAX_REQUESTS, 8);\n$pm->parentFunc = function ($pid) use ($pm) {\n    Swoole\\Coroutine\\run(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort());\n        Assert::true($client->connect());\n        /** @var $channels Swoole\\Coroutine\\Channel[] */\n        $channels = [];\n        for ($n = MAX_REQUESTS; $n--;) {\n            $request = new Swoole\\Http2\\Request;\n            $request->pipeline = true;\n            $streamId = $client->send($request);\n            if (Assert::greaterThan($streamId, 0)) {\n                $data = $pm->getRandomData();\n                for ($i = 0; $i < strlen($data); $i++) {\n                    $client->write($streamId, $data[$i], $i === (strlen($data) - 1));\n                }\n                $channels[$streamId] = $channel = new Swoole\\Coroutine\\Channel;\n                Swoole\\Coroutine::create(function () use ($streamId, $channel, $data) {\n                    /** @var $response Swoole\\Http2\\Response */\n                    $response = $channel->pop();\n                    $response->headers += ($channel->pop())->headers;\n                    Assert::same($response->streamId, $streamId);\n                    unset($response->headers['date']);\n                    Assert::same($response->headers, [\n                        'content-type' => 'application/srpc',\n                        'trailer' => 'srpc-status, srpc-message',\n                        'server' => 'swoole-http-server',\n                        'content-length' => '8',\n                        'srpc-status' => '0',\n                        'srpc-message' => '',\n                    ]);\n                    Assert::same($response->data, $data);\n                });\n            }\n        }\n        while (true) {\n            /** @var $response Swoole\\Http2\\Response */\n            $response = $client->read();\n            $channels[$response->streamId]->push($response);\n            if (!$response->pipeline) {\n                unset($channels[$response->streamId]);\n            }\n            if (empty($channels)) {\n                break;\n            }\n        }\n    });\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        $response->header('content-type', 'application/srpc');\n        $response->header('trailer', 'srpc-status, srpc-message');\n        $trailer = [\n            \"srpc-status\" => '0',\n            \"srpc-message\" => ''\n        ];\n        foreach ($trailer as $trailer_name => $trailer_value) {\n            $response->trailer($trailer_name, $trailer_value);\n        }\n        $response->end($pm->getRandomData());\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/post.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: http2 without gzip and recv big data (window-update)\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $domain = 'www.zhihu.com';\n    $cli = new Swoole\\Coroutine\\Http2\\Client($domain, 443, true);\n    $cli->set([\n        'timeout' => -1,\n        'ssl_host_name' => $domain\n    ]);\n    $cli->connect();\n\n    $req = new Swoole\\Http2\\Request;\n    $req->method = 'POST';\n    $req->path = '/api/v4/answers/300000000/voters';\n    $req->headers = [\n        'host' => $domain,\n        \"user-agent\" => 'Chrome/49.0.2587.3',\n        'accept' => 'text/html,application/xhtml+xml,application/xml',\n        'accept-encoding' => 'gzip'\n    ];\n    $req->data = '{\"type\":\"up\"}';\n    $cli->send($req);\n    $response = $cli->recv();\n    Assert::true(in_array(json_decode($response->data)->error->code, [602, 10002, 100], true));\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/send-cookies.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: nghttp2 big data with ssl\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nif (strpos(`nghttpd --version 2>&1`, 'nghttp2') === false) {\n    skip('no nghttpd');\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        co::sleep(0.1);\n        $cli = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->connect();\n\n        $filename = pathinfo(__FILE__, PATHINFO_BASENAME);\n        $req = new Swoole\\Http2\\Request;\n        $req->path = \"/{$filename}\";\n        $req->cookies = [\n            'foo' => 'bar',\n            'bar' => 'char'\n        ];\n        for ($n = MAX_REQUESTS; $n--;) {\n            Assert::assert($cli->send($req));\n            $response = $cli->recv(1);\n            Assert::same($response->data, co::readFile(__FILE__));\n        }\n        `ps -A | grep nghttpd | awk '{print $1}' | xargs kill -9 > /dev/null 2>&1`;\n        echo \"DONE\\n\";\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $root = __DIR__;\n    `nghttpd -v -d {$root}/ -a 0.0.0.0 {$pm->getFreePort()} --no-tls&`;\n    $pm->wakeup();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/send_only_bug.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: send only without recv and use sleep\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $domain = 'www.zhihu.com';\n    $cli = new Swoole\\Coroutine\\Http2\\Client($domain, 443, true);\n    $cli->set([\n        'timeout' => 5,\n        'ssl_host_name' => $domain\n    ]);\n    $cli->connect();\n    $req = new Swoole\\Http2\\Request;\n    $req->path = '/';\n    $req->headers = [\n        'host' => $domain,\n        \"user-agent\" => 'Chrome/49.0.2587.3',\n        'accept' => 'text/html,application/xhtml+xml,application/xml',\n        'accept-encoding' => 'gzip'\n    ];\n    Assert::assert($cli->send($req));\n    // not recv here (core dump before ver < 4.0.3)\n    co::sleep(1);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/set-cookies.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: http2 response cookies\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $domain = 'www.bing.com';\n    $cli = new Swoole\\Coroutine\\Http2\\Client($domain, 443, true);\n    $cli->set([\n        'timeout' => 10,\n        'ssl_host_name' => $domain\n    ]);\n    $cli->connect();\n\n    $req = new Swoole\\Http2\\Request;\n    $req->path = '/';\n    $req->headers = [\n        'Host' => $domain,\n        \"User-Agent\" => 'Chrome/49.0.2587.3',\n        'Accept' => 'text/html,application/xhtml+xml,application/xml',\n        'Accept-encoding' => 'gzip',\n    ];\n    Assert::assert($cli->send($req));\n    /**@var $response swoole_http2_response */\n    $response = $cli->recv();\n    Assert::assert(is_array($response->headers));\n    Assert::assert(count($response->set_cookie_headers) >= 3);\n    // check cookies\n    foreach ($response->set_cookie_headers as $set_cookie_header) {\n        $cookie = explode(';', $set_cookie_header, 2)[0];\n        list($key, $value) = explode('=', $cookie, 2);\n        Assert::same($response->cookies[$key], $value);\n    }\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/sock_type_unix.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: sock type unix\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http2\\Request;\nuse Swoole\\Http2\\Response;\nuse Swoole\\Coroutine\\Http2\\Client;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $client = new Client('unix:/' . UNIXSOCK_PATH, 0, false);\n        Assert::eq($client->connect(), true);\n        $req = new Request();\n        $req->method = 'POST';\n        $req->path = '/';\n        $client->send($req);\n        $result = $client->recv();\n        Assert::eq($result->data, \"OK\");\n\n        $client = new Client('unix://' . UNIXSOCK_PATH, 0, false);\n        Assert::eq($client->connect(), true);\n        $req = new Request();\n        $req->method = 'POST';\n        $req->path = '/';\n        $client->send($req);\n        $result = $client->recv();\n        Assert::eq($result->data, \"OK\");\n\n        $client = new Client('unix:///' . UNIXSOCK_PATH, 0, false);\n        Assert::eq($client->connect(), true);\n        $req = new Request();\n        $req->method = 'POST';\n        $req->path = '/';\n        $client->send($req);\n        $result = $client->recv();\n        Assert::eq($result->data, \"OK\");\n    });\n    Swoole\\Event::wait();\n    @unlink(UNIXSOCK_PATH);\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Server(UNIXSOCK_PATH, 0, SWOOLE_BASE, SWOOLE_SOCK_UNIX_STREAM);\n    $server->set([\n        'worker_num' => 1,\n        'open_http2_protocol' => true\n    ]);\n    $server->on(\"workerStart\", function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (\\Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end('OK');\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http2_client_coro/wrong_headers.phpt",
    "content": "--TEST--\nswoole_http2_client_coro: http2 with wrong headers\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $domain = 'mail.qq.com';\n    $cli = new Swoole\\Coroutine\\Http2\\Client($domain, 443, true);\n    $cli->set([\n        'timeout' => 10,\n        'ssl_host_name' => $domain\n    ]);\n    $cli->connect();\n\n    $req = new Swoole\\Http2\\Request;\n    $req->path = '/';\n    $req->headers = 1;\n    Assert::assert($cli->send($req));\n    Assert::assert(is_array($req->headers)); // check array\n    /**@var $response swoole_http2_response */\n    $response = $cli->recv();\n    echo $response->statusCode;\n    Assert::assert(stripos($response->data, 'tencent') !== false);\n});\n?>\n--EXPECT--\n200\n"
  },
  {
    "path": "tests/swoole_http2_server/413.phpt",
    "content": "--TEST--\nswoole_http2_server: 413\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_nghttp();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $file = tempnam('/tmp', 'swoole_http2_');\n    file_put_contents($file, str_repeat('A', 2 * 1024 * 1024));\n    Assert::assert(empty($res = `nghttp -d {$file} https://127.0.0.1:{$pm->getFreePort()}/ > /dev/stdout 2>/dev/null`));\n    $pm->kill();\n    unlink($file);\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'package_max_length' => 1024 * 1024,\n        'open_http2_protocol' => true,\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key'\n    ]);\n    $http->on(\"WorkerStart\", function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end($request->getContent());\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_server/big_data.phpt",
    "content": "--TEST--\nswoole_http2_server: big data\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $domain = '127.0.0.1';\n        $cli = new Swoole\\Coroutine\\Http2\\Client($domain, $pm->getFreePort(), true);\n        $cli->set([\n            'timeout' => 10,\n            'ssl_cert_file' => SSL_FILE_DIR . '/client-cert.pem',\n            'ssl_key_file' => SSL_FILE_DIR . '/client-key.pem'\n        ]);\n        Assert::assert($cli->connect());\n\n        $req = new Swoole\\Http2\\Request;\n        $req->method = 'POST';\n        $req->path = '/';\n        $req->headers = [\n            'Host' => $domain,\n            \"User-Agent\" => 'Chrome/49.0.2587.3',\n            'Accept' => 'text/html,application/xhtml+xml,application/xml',\n            'Accept-encoding' => 'gzip'\n        ];\n        for ($n = MAX_REQUESTS; $n--;) {\n            $req->data = get_safe_random(65535 + mt_rand(0, 65535));\n            Assert::assert($cli->send($req));\n            $res = $cli->recv();\n            Assert::same($res->statusCode, 200);\n            Assert::same(md5($req->data), md5($res->data));\n        }\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $http->set([\n            'worker_num' => 1,\n            'log_file' => '/dev/null',\n            'open_http2_protocol' => true,\n            'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n            'ssl_key_file' => SSL_FILE_DIR . '/server.key'\n        ] + (IS_IN_CI ? [] : [\n            'ssl_verify_peer' => true,\n            'ssl_allow_self_signed' => true,\n            'ssl_client_cert_file' => SSL_FILE_DIR . '/ca-cert.pem'\n        ])\n    );\n    $http->on(\"WorkerStart\", function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end($request->rawcontent());\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_server/compression.phpt",
    "content": "--TEST--\nswoole_http2_server: conpression with http2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $domain = '127.0.0.1';\n        $cli = new Swoole\\Coroutine\\Http2\\Client($domain, $pm->getFreePort(), true);\n        $cli->set([\n            'timeout' => -1,\n            'ssl_cert_file' => SSL_FILE_DIR . '/client-cert.pem',\n            'ssl_key_file' => SSL_FILE_DIR . '/client-key.pem'\n        ]);\n        $cli->connect();\n\n        $req = new Swoole\\Http2\\Request;\n        $req->path = '/';\n        $req->headers = [\n            'Host' => $domain,\n            \"User-Agent\" => 'Chrome/49.0.2587.3',\n            'Accept' => 'text/html,application/xhtml+xml,application/xml',\n            'Accept-encoding' => 'gzip'\n        ];\n        for ($n = MAX_REQUESTS; $n--;) {\n            Assert::assert($cli->send($req));\n            $response = $cli->recv();\n            Assert::same($response->statusCode, 200);\n            Assert::same(md5_file(__DIR__ . '/../../README.md'), md5($response->data));\n        }\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $http->set([\n            'log_file' => '/dev/null',\n            'open_http2_protocol' => true,\n            'http_gzip_level' => 9,\n            'http_compression' => true,\n            'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n            'ssl_key_file' => SSL_FILE_DIR . '/server.key'\n        ] + (IS_IN_CI ? [] : [\n            'ssl_verify_peer' => true,\n            'ssl_allow_self_signed' => true,\n            'ssl_client_cert_file' => SSL_FILE_DIR . '/ca-cert.pem'\n        ])\n    );\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function ($request, Swoole\\Http\\Response $response) {\n        $response->end(co::readFile(__DIR__ . '/../../README.md'));\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_server/compression_types.phpt",
    "content": "--TEST--\nswoole_http2_server: compression types\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/../include/api/http_test_cases.php';\n\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Server;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    http2_compression_types_test($pm);\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $http->set([\n        'http_compression_types' => [\n            'text/html',\n            'application/json'\n        ],\n        'open_http2_protocol' => true,\n    ]);\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function (Request $request, Response $response) {\n        if ($request->server['request_uri'] == '/html') {\n            $response->end(str_repeat('A', $request->get['bytes']));\n        } elseif ($request->server['request_uri'] == '/json') {\n            $response->setHeader('Content-Type', 'application/json');\n            $response->end(str_repeat('B', $request->get['bytes']));\n        } elseif ($request->server['request_uri'] == '/raw') {\n            $response->setHeader('Content-Type', 'text/raw');\n            $response->end(str_repeat('C', $request->get['bytes']));\n        }\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http2_server/getMethod.phpt",
    "content": "--TEST--\nswoole_http2_server: getMethod\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Server;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort());\n        Assert::true($cli->connect());\n        $req = new Swoole\\Http2\\Request();\n        $req->method = 'POST';\n        $req->path = '/api';\n        $req->headers = [\n            'user-agent' => 'Chrome/49.0.2587.3',\n            'accept' => 'text/html,application/xhtml+xml,application/xml',\n            'accept-encoding' => 'gzip'\n        ];\n        $req->data = '{\"type\":\"up\"}';\n        $cli->send($req);\n        $response = $cli->recv();\n        $json = json_decode($response->data);\n        Assert::same($json->request_method, 'POST');\n        Assert::same($json->getMethod, 'POST');\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('::', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP6);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true\n    ]);\n    $http->on('workerStart', function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) {\n        $request_method = $request->server['request_method'];\n        $getMethod = $request->getMethod();\n        $response->end(json_encode(compact('request_method', 'getMethod'), JSON_PRETTY_PRINT) . \"\\n\");\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_server/goaway.phpt",
    "content": "--TEST--\nswoole_http2_server: goaway\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->set(['timeout' => 10]);\n        Assert::true($cli->connect());\n        Assert::greaterThan($streamId = $cli->send(new Swoole\\Http2\\Request), 0);\n        $cli->recv();\n        Assert::same($cli->serverLastStreamId, $streamId);\n        Assert::same($cli->errCode, SWOOLE_HTTP2_ERROR_NO_ERROR);\n        Assert::same($cli->errMsg, 'NO_ERROR');\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true\n    ]);\n    $http->on('workerStart', function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->goaway(SWOOLE_HTTP2_ERROR_NO_ERROR, 'NO_ERROR');\n        $response->end($request->rawcontent());\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_server/http2_headers.phpt",
    "content": "--TEST--\nswoole_http2_server: array headers\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Server;\n\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager();\n$pm->parentFunc = function ($pid) use ($pm) {\n    $output = shell_exec(\"curl --http2-prior-knowledge --silent -I http://127.0.0.1:{$pm->getFreePort()}\");\n    Assert::contains($output, 'HTTP/2 200');\n    Assert::contains($output, 'test-value: a');\n    Assert::contains($output, 'test-value: d5678');\n    Assert::contains($output, 'test-value: e');\n    Assert::contains($output, 'test-value: 5678');\n    Assert::contains($output, 'test-value: 3.1415926');\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n    ]);\n    $http->on('workerStart', function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) {\n        $response->header('test-value', [\n            \"a\\r\\n\",\n            'd5678',\n            \"e  \\n \",\n            null,\n            5678,\n            3.1415926,\n        ]);\n        $response->end('<h1>Hello Swoole.</h1>');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_server/issue_4365.phpt",
    "content": "--TEST--\nswoole_http2_server: github issue#4365\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_darwin();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\n\n$pm = new ProcessManager();\n\nconst N = 265537;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    if (Assert::assert(!empty($res = shell_exec(\"curl -s --http2-prior-knowledge http://127.0.0.1:{$pm->getFreePort()}/ > /dev/stdout 2>/dev/null\")))) {\n        Assert::length($res, N);\n        echo \"DONE\\n\";\n    }\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('0.0.0.0', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $http->set([\n        'open_http2_protocol' => true,\n        'enable_reuse_port' => true,\n        'enable_coroutine' => false,\n        'log_level' => 1,\n        'log_file' => TEST_LOG_FILE,\n    ]);\n    $http->on('request', function ($request, $response) {\n        $response->end(str_repeat('x', N - 4) . \"\\r\\n\\r\\n\");\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http2_server/max_concurrency.phpt",
    "content": "--TEST--\nswoole_http2_server: max_concurrency\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Coroutine;\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\nconst SERVER_MAX_CONCURRENCY = 4;\nconst GREETING_MESSAGE = 'hello world';\n\n$pm = new ProcessManager;\n$pm->initFreePorts();\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n\n        $n = SERVER_MAX_CONCURRENCY;\n        // 200, low concurrency\n        $cid_list = [];\n        while ($n--) {\n            $cid_list[] = go(function () use ($pm) {\n                $cli = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort());\n                $cli->set(['timeout' => 10]);\n                Assert::true($cli->connect());\n                Assert::greaterThan($streamId = $cli->send(new Swoole\\Http2\\Request), 0);\n                $response = $cli->recv();\n                Assert::eq($response->statusCode, 200);\n                Assert::eq($response->data, GREETING_MESSAGE);\n            });\n        }\n\n        System::sleep(0.005);\n\n        // 503, high concurrency\n        $n = SERVER_MAX_CONCURRENCY;\n        while ($n--) {\n            $cid_list[] = go(function () use ($pm) {\n                $cli = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort());\n                $cli->set(['timeout' => 10]);\n                Assert::true($cli->connect());\n                Assert::greaterThan($streamId = $cli->send(new Swoole\\Http2\\Request), 0);\n                $response = $cli->recv();\n                Assert::eq($response->statusCode, 503);\n            });\n        }\n\n        // wait\n        Coroutine::join($cid_list);\n\n        // low concurrency\n        $n = SERVER_MAX_CONCURRENCY;\n        $cid_list = [];\n        while ($n--) {\n            $cid_list[] = go(function () use ($pm) {\n                $cli = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort());\n                $cli->set(['timeout' => 10]);\n                Assert::true($cli->connect());\n                Assert::greaterThan($streamId = $cli->send(new Swoole\\Http2\\Request), 0);\n                $response = $cli->recv();\n                Assert::eq($response->statusCode, 200);\n                Assert::eq($response->data, GREETING_MESSAGE);\n            });\n        }\n\n        Coroutine::join($cid_list);\n        echo \"DONE\\n\";\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set([\n        'log_file' => '/dev/null',\n        'max_concurrency' => SERVER_MAX_CONCURRENCY,\n        'open_http2_protocol' => true,\n    ]);\n    $http->on('start', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        System::sleep(0.1);\n        $response->end(GREETING_MESSAGE);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http2_server/nghttp2_big_data.phpt",
    "content": "--TEST--\nswoole_http2_server: nghttp2 big data with ssl\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_nghttp();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $file = TEST_IMAGE;\n    if (Assert::assert(!empty($res = `nghttp -d {$file} https://127.0.0.1:{$pm->getFreePort()}/ > /dev/stdout 2>/dev/null`))) {\n        Assert::same(md5($res), md5_file($file));\n    }\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key'\n    ]);\n    $http->on(\"WorkerStart\", function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end($request->getContent());\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_server/no_compression.phpt",
    "content": "--TEST--\nswoole_http2_server: no compression with http2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $domain = '127.0.0.1';\n        $cli = new Swoole\\Coroutine\\Http2\\Client($domain, $pm->getFreePort(), true);\n        $cli->set([\n            'timeout' => -1,\n            'ssl_cert_file' => SSL_FILE_DIR . '/client-cert.pem',\n            'ssl_key_file' => SSL_FILE_DIR . '/client-key.pem'\n        ]);\n        $cli->connect();\n\n        $req = new Swoole\\Http2\\Request;\n        $req->path = '/';\n        $req->headers = [\n            'Host' => $domain,\n            \"User-Agent\" => 'Chrome/49.0.2587.3',\n            'Accept' => 'text/html,application/xhtml+xml,application/xml',\n        ];\n        for ($n = MAX_REQUESTS; $n--;) {\n            Assert::assert($cli->send($req));\n            $response = $cli->recv();\n            Assert::same($response->statusCode, 200);\n            Assert::same(md5_file(__DIR__ . '/../../README.md'), md5($response->data));\n        }\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $http->set([\n            'log_file' => '/dev/null',\n            'open_http2_protocol' => true,\n            'http_compression' => false,\n            'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n            'ssl_key_file' => SSL_FILE_DIR . '/server.key'\n        ] + (IS_IN_CI ? [] : [\n            'ssl_verify_peer' => true,\n            'ssl_allow_self_signed' => true,\n            'ssl_client_cert_file' => SSL_FILE_DIR . '/ca-cert.pem'\n        ])\n    );\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end(co::readFile(__DIR__ . '/../../README.md'));\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_server/ping.phpt",
    "content": "--TEST--\nswoole_http2_server: ping\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->set(['timeout' => 10]);\n        Assert::true($cli->connect());\n        Assert::greaterThan($streamId = $cli->send(new Swoole\\Http2\\Request), 0);\n        $s = microtime(true);\n        $response = $cli->recv();\n        time_approximate(0.5, microtime(true) - $s);\n        Assert::same($response->streamId, $streamId);\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true\n    ]);\n    $http->on('workerStart', function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        Swoole\\Timer::tick(100, function (int $id) use ($request, $response) {\n            Assert::true($response->ping());\n            if (@++$GLOBALS['i'] === 5) {\n                $response->end($request->rawcontent());\n                Swoole\\Timer::clear($id);\n            }\n        });\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_server/sendfile.phpt",
    "content": "--TEST--\nswoole_http2_server: sendfile with http2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Request;\n\nforeach ([SWOOLE_BASE, SWOOLE_PROCESS] as $mode) {\n    phpt_var_dump('current mode is ' . ['BASE', 'PROCESS'][$mode - 1]);\n    $pm = new ProcessManager;\n    $pm->parentFunc = function () use ($pm) {\n        Coroutine\\run(function () use ($pm) {\n            $cli = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort(), false);\n            $cli->set(['timeout' => -1]);\n            Assert::true($cli->connect());\n            for ($n = MAX_REQUESTS; $n--;) {\n                Assert::assert($cli->send(new Swoole\\Http2\\Request));\n            }\n            for ($n = MAX_REQUESTS; $n--;) {\n                $response = $cli->recv();\n                if (Assert::notEmpty($response)) {\n                    if (Assert::same(md5_file(__DIR__ . '/../../README.md'), md5($response->data))) {\n                        if ($response->statusCode !== 200) {\n                            echo 'missing header' . PHP_EOL;\n                        }\n                    }\n                } else {\n                    break;\n                }\n            }\n        });\n        $pm->kill();\n    };\n    $pm->childFunc = function () use ($pm, $mode) {\n        $http = new Server('127.0.0.1', $pm->getFreePort(), $mode);\n        $http->set([\n            'log_file' => '/dev/null',\n            'worker_num' => 1,\n            'open_http2_protocol' => true\n        ]);\n        $http->on('workerStart', function () use ($pm) {\n            $pm->wakeup();\n        });\n        $http->on('request', function (Request $request, Response $response) {\n            if (!Assert::true($response->sendfile(__DIR__ . '/../../README.md'))) {\n                echo swoole_strerror(swoole_last_error()) . PHP_EOL;\n            }\n        });\n        $http->start();\n    };\n    $pm->childFirst();\n    $pm->run();\n}\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_server/sendfile_content_type.phpt",
    "content": "--TEST--\nswoole_http2_server: sendfile [content-type]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Request;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Coroutine\\run(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort(), false);\n        $cli->set(['timeout' => -1]);\n        Assert::true($cli->connect());\n        Assert::assert($cli->send(new Swoole\\Http2\\Request));\n        $response = $cli->recv();\n        Assert::notEmpty($response);\n        Assert::same(md5_file(TEST_IMAGE), md5($response->data));\n        Assert::same($response->statusCode, 200);\n        Assert::same($response->headers['content-type'], 'image/jpeg');\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 1,\n        'open_http2_protocol' => true\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) {\n        if (!Assert::true($response->sendfile(TEST_IMAGE))) {\n            echo swoole_strerror(swoole_last_error()) . PHP_EOL;\n        }\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_server/sendfile_set_content_type.phpt",
    "content": "--TEST--\nswoole_http2_server: sendfile [set content-type]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Request;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Coroutine\\run(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort(), false);\n        $cli->set(['timeout' => -1]);\n        Assert::true($cli->connect());\n        Assert::assert($cli->send(new Swoole\\Http2\\Request));\n        $response = $cli->recv();\n        Assert::notEmpty($response);\n        Assert::same($response->statusCode, 200);\n        Assert::same($response->headers['content-type'], 'application/javascript');\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 1,\n        'open_http2_protocol' => true\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) {\n        $response->setHeader('Content-Type', 'application/javascript');\n        if (!Assert::true($response->sendfile(__FILE__))) {\n            echo swoole_strerror(swoole_last_error()) . PHP_EOL;\n        }\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_server/server_addr.phpt",
    "content": "--TEST--\nswoole_http2_server: add server addr for http2 server\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$output = shell_exec('ip addr show');\npreg_match_all('/inet (\\d+\\.\\d+\\.\\d+\\.\\d+)\\//', $output, $matches);\n$ips = $matches[1];\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm, $ips) {\n    go(function () use ($pm, $ips) {\n        $domain = $ips[1];\n        $cli = new Swoole\\Coroutine\\Http2\\Client($domain, $pm->getFreePort(), true);\n        $cli->set([\n            'timeout' => 10,\n            'ssl_cert_file' => SSL_FILE_DIR . '/client-cert.pem',\n            'ssl_key_file' => SSL_FILE_DIR . '/client-key.pem'\n        ]);\n        Assert::assert($cli->connect());\n\n        $req = new Swoole\\Http2\\Request;\n        $req->method = 'POST';\n        $req->path = '/';\n        $req->headers = [\n            'Host' => $domain,\n            \"User-Agent\" => 'Chrome/49.0.2587.3',\n            'Accept' => 'text/html,application/xhtml+xml,application/xml',\n            'Accept-encoding' => 'gzip'\n        ];\n        for ($n = MAX_REQUESTS; $n--;) {\n            $req->data = get_safe_random(65535 + mt_rand(0, 65535));\n            Assert::assert($cli->send($req));\n            $res = $cli->recv();\n            Assert::same($res->statusCode, 200);\n            Assert::same(md5($req->data), md5($res->data));\n        }\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFunc = function () use ($pm, $ips) {\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $http->set([\n            'worker_num' => 1,\n            'log_file' => '/dev/null',\n            'open_http2_protocol' => true,\n            'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n            'ssl_key_file' => SSL_FILE_DIR . '/server.key'\n        ] + (IS_IN_CI ? [] : [\n            'ssl_verify_peer' => true,\n            'ssl_allow_self_signed' => true,\n            'ssl_client_cert_file' => SSL_FILE_DIR . '/ca-cert.pem'\n        ])\n    );\n    $http->on(\"WorkerStart\", function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($ips) {\n        $server = $request->server;\n        Assert::eq($server['server_addr'], $ips[1]);\n        Assert::eq($server['remote_addr'], $ips[1]);\n        Assert::true($server['server_port'] != $server['remote_port']);\n        $response->end($request->rawcontent());\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_server/static_handler.phpt",
    "content": "--TEST--\nswoole_http2_server: static handler\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Request;\n\ndefine('DOC_ROOT', realpath(__DIR__ . '/../..'));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    Swoole\\Coroutine\\run(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort(), false);\n        $cli->set([\n            'timeout' => -1,\n        ]);\n        $cli->connect();\n\n        $req = new Swoole\\Http2\\Request;\n        $req->path = '/README.md';\n        //request\n        for ($n = MAX_REQUESTS; $n--;) {\n            Assert::assert($cli->send($req));\n        }\n        //response\n        for ($n = MAX_REQUESTS; $n--;) {\n            $response = $cli->recv();\n            Assert::same($response->statusCode, 200);\n            Assert::same(md5_file(DOC_ROOT . '/README.md'), md5($response->data));\n        }\n    });\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $http->set([\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'enable_static_handler' => true,\n        'document_root' => DOC_ROOT,\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) use ($http) {\n        $response->end('hello world');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http2_server/streaming.phpt",
    "content": "--TEST--\nswoole_http2_server: streaming\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_nghttp();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse SwooleTest\\ChildProcess;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $proc = ChildProcess::exec(\"nghttp http://127.0.0.1:{$pm->getFreePort()}/\");\n    $out = '';\n    while($line = $proc->read()) {\n        $out .= $line;\n        if (str_contains($line, 'hello world, end')) {\n            break;\n        }\n    }\n    echo $out;\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n    ]);\n    $http->on('WorkerStart', function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('Request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $n = 5;\n        while ($n--) {\n            $response->write(\"hello world, #$n\\n\");\n            Co\\System::sleep(0.1);\n        }\n        $response->end(\"hello world, end\\n\");\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nhello world, #4\nhello world, #3\nhello world, #2\nhello world, #1\nhello world, #0\nhello world, end\n"
  },
  {
    "path": "tests/swoole_http2_server/streaming_2.phpt",
    "content": "--TEST--\nswoole_http2_server: streaming 2\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_nghttp();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse SwooleTest\\ChildProcess;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $proc = ChildProcess::exec(\"nghttp http://127.0.0.1:{$pm->getFreePort()}/\");\n    $out = '';\n    while($line = $proc->read()) {\n        $out .= $line;\n        if (str_contains($line, 'hello world, #0')) {\n            break;\n        }\n    }\n    echo $out;\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n    ]);\n    $http->on('WorkerStart', function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('Request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $n = 5;\n        while ($n--) {\n            $response->write(\"hello world, #$n\\n\");\n            Co\\System::sleep(0.1);\n        }\n        $response->end();\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nhello world, #4\nhello world, #3\nhello world, #2\nhello world, #1\nhello world, #0\n"
  },
  {
    "path": "tests/swoole_http2_server/trailer.phpt",
    "content": "--TEST--\nswoole_http2_server: trailer\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->initRandomData(MAX_REQUESTS);\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort());\n        Assert::true($cli->connect());\n        /** @var $channels Swoole\\Coroutine\\Channel[] */\n        $channels = [];\n        for ($n = MAX_REQUESTS; $n--;) {\n            $streamId = $cli->send(new Swoole\\Http2\\Request);\n            if (Assert::greaterThan($streamId, 0)) {\n                $channels[$streamId] = $channel = new Swoole\\Coroutine\\Channel;\n                go(function () use ($streamId, $channel) {\n                    /** @var $response Swoole\\Http2\\Response */\n                    $response = $channel->pop();\n                    Assert::same($response->streamId, $streamId);\n                    $headers = $response->headers;\n                    unset($headers['date']);\n                    Assert::same($headers, [\n                        'content-type' => 'application/srpc',\n                        'trailer' => 'srpc-status, srpc-message',\n                        'server' => 'swoole-http-server',\n                        'content-length' => '32',\n                        'srpc-status' => '0',\n                        'srpc-message' => '',\n                    ]);\n                });\n            }\n        }\n        while (true) {\n            /** @var $response Swoole\\Http2\\Response */\n            $response = $cli->recv();\n            $channels[$response->streamId]->push($response);\n            unset($channels[$response->streamId]);\n            if (empty($channels)) {\n                break;\n            }\n        }\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        $response->header('content-type', 'application/srpc');\n        $response->header('trailer', 'srpc-status, srpc-message');\n        $trailer = [\n            \"srpc-status\" => '0',\n            \"srpc-message\" => ''\n        ];\n        foreach ($trailer as $trailer_name => $trailer_value) {\n            $response->trailer($trailer_name, $trailer_value);\n        }\n        $response->end($pm->getRandomData());\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http2_server/worker_max_concurrency.phpt",
    "content": "--TEST--\nswoole_http2_server: worker_max_concurrency\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Coroutine;\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\nconst SERVER_WORKER_MAX_CONCURRENCY = 4;\nconst GREETING_MESSAGE = 'hello world';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        $n = SERVER_WORKER_MAX_CONCURRENCY * 3;\n        $cid_list = [];\n        while ($n--) {\n            $cid_list[] = go(function () use ($pm) {\n                $cli = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort());\n                Assert::true($cli->connect());\n                Assert::greaterThan($streamId = $cli->send(new Swoole\\Http2\\Request), 0);\n                $response = $cli->recv();\n                Assert::eq($response->statusCode, 200);\n                Assert::eq($response->data, GREETING_MESSAGE);\n            });\n        }\n\n        // wait\n        Coroutine::join($cid_list);\n\n        $cli = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort());\n        Assert::true($cli->connect());\n        $req = new Swoole\\Http2\\Request;\n        $req->path = '/stats';\n        Assert::greaterThan($streamId = $cli->send($req), 0);\n        $response = $cli->recv();\n        Assert::eq($response->statusCode, 200);\n        $json = json_decode($response->data);\n        Assert::true(isset($json->coroutine_peek_num));\n        Assert::eq($json->coroutine_peek_num, SERVER_WORKER_MAX_CONCURRENCY);\n\n        echo \"DONE\\n\";\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n//    $mode = SWOOLE_BASE;\n    $mode = SERVER_MODE_RANDOM;\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), $mode);\n    $http->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 1,\n        'open_http2_protocol' => true,\n        'worker_max_concurrency' => SERVER_WORKER_MAX_CONCURRENCY,\n    ]);\n    $http->on('start', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm, $http) {\n        if ($request->server['request_uri'] == '/stats') {\n            $response->end(json_encode($http->stats()));\n            return;\n        }\n        System::sleep(0.1);\n        $response->end(GREETING_MESSAGE);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http2_server_coro/cookies.phpt",
    "content": "--TEST--\nswoole_http2_server_coro: cookies\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->connect();\n        $requests = [];\n        for ($n = MAX_REQUESTS; $n--;) {\n            $request = new Swoole\\Http2\\Request;\n            $request->headers = ['id' => $n];\n            $request->cookies = [];\n            for ($k = 32; $k--;) {\n                $request->cookies[get_safe_random()] = get_safe_random();\n            }\n            $requests[$n] = $request;\n            Assert::assert($cli->send($request));\n        }\n        for ($n = MAX_REQUESTS; $n--;) {\n            $response = $cli->recv(1);\n            if (Assert::isInstanceOf($response, Swoole\\Http2\\Response::class)) {\n                $request = $requests[$response->headers['id']];\n                Assert::same('OK', $response->data);\n                Assert::same($request->cookies, $response->cookies);\n            }\n        }\n        echo \"DONE\\n\";\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    Coroutine\\run(function () use ($pm) {\n        Coroutine::create(function () use ($pm) {\n            $http = new Swoole\\Coroutine\\Http\\Server('127.0.0.1', $pm->getFreePort());\n            $http->set([\n                'log_file' => '/dev/null',\n                'open_http2_protocol' => true\n            ]);\n            $http->handle('/', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n                $response->header('id', $request->header['id']);\n                foreach ($request->cookie as $name => $value) {\n                    $response->cookie($name, $value);\n                }\n                $response->end('OK');\n            });\n            $http->start();\n        });\n    });\n};\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/204.phpt",
    "content": "--TEST--\nswoole_http_client_coro: http 204 no content\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_ci('travis network');\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    $url_info = parse_url('http://httpbin.org/status/204');\n    $domain = $url_info['host'];\n    $path = $url_info['path'];\n    $cli = new Swoole\\Coroutine\\Http\\Client($domain);\n    $cli->set(['timeout' => 5]);\n    $cli->setHeaders([\n        'Host' => $domain,\n        'User-Agent' => 'Chrome/49.0.2587.3',\n        'Accept' => 'text/html,application/xhtml+xml,application/xml',\n        'Accept-Encoding' => 'gzip'\n    ]);\n    Assert::assert($cli->post($path, []));\n    Assert::same($cli->statusCode, 204);\n    Assert::assert(empty($cli->body));\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/addData.phpt",
    "content": "--TEST--\nswoole_http_client_coro: addData\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->addData(co::readFile(TEST_IMAGE), 'test.jpg', 'image/jpeg', 'test.jpg');\n        $cli->post('/upload_file', array('name' => 'rango'));\n        Assert::same($cli->statusCode, 200);\n        $ret = json_decode($cli->body, true);\n        Assert::assert($ret and is_array($ret));\n        Assert::same(md5_file(TEST_IMAGE), $ret['md5']);\n        $cli->close();\n    });\n    Swoole\\Event::wait();\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    include __DIR__ . '/../include/api/http_server.php';\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/alias.phpt",
    "content": "--TEST--\nswoole_http_client_coro: alias\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $domain = 'www.baidu.com';\n    $cli = new Swoole\\Coroutine\\Http\\Client($domain, 443, true);\n    $cli->set([\n        'timeout' => 10,\n        'ssl_host_name' => $domain\n    ]);\n    $random = get_safe_random(16);\n    Assert::assert($cli->get('/'));\n    Assert::contains($cli->getBody(), 'baidu.com');\n    Assert::same($cli->getStatusCode(), 200);\n    Assert::assert(count($cli->getHeaders()) > 5);\n    Assert::assert(count($cli->getCookies()) > 2);\n    echo \"DONE\\n\";\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/another_coroutine.phpt",
    "content": "--TEST--\nswoole_http_client_coro: illegal another coroutine\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_unsupported();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    $process = new Swoole\\Process(function (Swoole\\Process $worker) use ($pm) {\n        function get(Swoole\\Coroutine\\Http\\Client $client)\n        {\n            $client->get('/');\n        }\n\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        go(function () use ($cli) {\n            (function () use ($cli) {\n                (function () use ($cli) {\n                    co::sleep(0.001);\n                    get($cli);\n                })();\n            })();\n        });\n        go(function () use ($cli) {\n            $cli->get('/');\n        });\n        Swoole\\Event::wait();\n    }, false);\n    $process->start();\n    Swoole\\Process::wait();\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $server->set(['log_file' => '/dev/null']);\n    $server->on('workerStart', function (Swoole\\Http\\Server $server) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm, $server) {\n        co::sleep(0.1);\n        $server->shutdown();\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n[%s]\tERROR\t(PHP Fatal Error: %d):\nSwoole\\Coroutine\\Http\\Client::get: Socket#%d has already been bound to another coroutine#%d, reading of the same socket in multiple coroutines at the same time is not allowed\nStack trace:\n#0  Swoole\\Coroutine\\Http\\Client->get() called at [%s:%d]\n#1  get() called at [%s:%d]\n#2  {closure}() called at [%s:%d]\n#3  {closure}() called at [%s:%d]\n"
  },
  {
    "path": "tests/swoole_http_client_coro/auto_reconnect.phpt",
    "content": "--TEST--\nswoole_http_client_coro: auto reconnect\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->initRandomData(MAX_REQUESTS);\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->set(['timeout' => -1]);\n        for ($n = MAX_REQUESTS; $n--;) {\n            Assert::assert($cli->get('/'));\n            Assert::same($cli->body, $pm->getRandomData(), var_dump_return($cli));\n            co::sleep(0.005);\n        }\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"OK\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm, $server) {\n        $response->end($pm->getRandomData());\n        co::sleep(0.001);\n        $server->close($request->fd);\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_client_coro/bind_address.phpt",
    "content": "--TEST--\nswoole_http_client_coro: bind address and port\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Coroutine\\WaitGroup;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $wg = new WaitGroup();\n\n        $wg->add(5);\n        //test1: valid address\n        go(function () use ($pm, $wg) {\n            $client = new Client('127.0.0.1', $pm->getFreePort());\n            $bindAddress = current(swoole_get_local_ip());\n            $bindPort = get_one_free_port();\n\n            $client->set([\n                'bind_address' => $bindAddress,\n                'bind_port' => $bindPort,\n            ]);\n            $client->post('/validaddress', ['bind_address' => $bindAddress, 'bind_port' => $bindPort]);\n            $wg->done();\n        });\n\n        // test2: invalid address\n        go(function () use ($pm, $wg) {\n            $client = new Client('127.0.0.1', $pm->getFreePort());\n            $bindAddress = 11111;\n            $bindPort = get_one_free_port();\n\n            $client->set([\n                'bind_address' => $bindAddress,\n                'bind_port' => $bindPort,\n            ]);\n            $client->post('/invalidaddress', ['bind_address' => $bindAddress, 'bind_port' => $bindPort]);\n            $wg->done();\n        });\n\n        // test3: invalid port\n        go(function () use ($pm, $wg) {\n            $client = new Client('127.0.0.1', $pm->getFreePort());\n            $bindAddress = current(swoole_get_local_ip());\n            $bindPort = -1;\n\n            $client->set([\n                'bind_address' => $bindAddress,\n                'bind_port' => -1,\n            ]);\n            $client->post('/invalidport', ['bind_address' => $bindAddress, 'bind_port' => $bindPort]);\n            $wg->done();\n        });\n\n        // test4: not bind port\n        go(function () use ($pm, $wg) {\n            $client = new Client('127.0.0.1', $pm->getFreePort());\n            $bindAddress = current(swoole_get_local_ip());\n            $bindPort = null;\n\n            $client->set([\n                'bind_address' => $bindAddress,\n            ]);\n            $client->post('/notbindport', ['bind_address' => $bindAddress, 'bind_port' => $bindPort]);\n            $wg->done();\n        });\n\n        //test5: request baidu.com\n        go(function () use ($pm, $wg) {\n            $client = new Client('www.baidu.com', 80);\n            $bindAddress = current(swoole_get_local_ip());\n            $bindPort = get_one_free_port();\n\n            $client->set([\n                'bind_address' => $bindAddress,\n                'bind_port' => $bindPort,\n            ]);\n            Assert::true($client->get('/'));\n\n            $client = new Client('www.baidu.com', 80);\n            $bindAddress = '127.0.0.1';\n            $bindPort = get_one_free_port();\n\n            $client->set([\n                'bind_address' => $bindAddress,\n                'bind_port' => $bindPort,\n            ]);\n            Assert::false($client->get('/'));\n            $wg->done();\n        });\n\n        $wg->wait();\n\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->get('/stop?hello=1');\n        echo $client->body . PHP_EOL;\n        echo \"DONE\\n\";\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $server = new Server('127.0.0.1', $pm->getFreePort());\n        $server->handle('/validaddress', function (Request $request, Response $response) {\n            Assert::eq($request->server['remote_addr'], $request->post['bind_address']);\n            Assert::eq($request->server['remote_port'], $request->post['bind_port']);\n        });\n        $server->handle('/invalidaddress', function (Request $request, Response $response) {\n            Assert::eq($request->post['bind_address'], '11111');\n            Assert::eq($request->server['remote_addr'], '127.0.0.1');\n            Assert::notEq($request->server['remote_port'], $request->post['bind_port']);\n        });\n        $server->handle('/invalidport', function (Request $request, Response $response) {\n            Assert::eq($request->post['bind_port'], '-1');\n            Assert::eq($request->server['remote_addr'], $request->post['bind_address']);\n            Assert::greaterThan($request->server['remote_port'], 0);\n        });\n        $server->handle('/notbindport', function (Request $request, Response $response) {\n            Assert::keyNotExists($request->post, 'bind_port');\n            Assert::eq($request->server['remote_addr'], $request->post['bind_address']);\n            Assert::greaterThan($request->server['remote_port'], 0);\n        });\n        $server->handle('/stop', function ($request, $response) use ($server) {\n            $response->end(\"<h1>Stop</h1>\");\n            $server->shutdown();\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n<h1>Stop</h1>\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/bug_2661.phpt",
    "content": "--TEST--\nswoole_http_client_coro: #2611 bound error with dns resolve and cross close\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_ci('travis network');\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Coroutine\\Run(function () {\n    $client = new Swoole\\Coroutine\\Http\\Client('www.zhe800.com', 443, true);\n\n    function foo($ch, $client)\n    {\n        mt_srand();\n        $rand = mt_rand(100000, 999999999);\n        $path = \"/email_subscribe?email=\" . $rand . \"@\" . substr(md5(microtime(true)), 0, 8) . \".com\";\n        $client->get($path);\n        echo \"push is \" . $path . \" \" . Co::getCid() . \"\\n\";\n        $client->close();\n        $ch->push($path);\n    }\n\n    function bar($client)\n    {\n        $length = 10;\n        $ch = new Swoole\\Coroutine\\Channel($length);\n        for ($i = 0; $i < $length; $i++) {\n            go('foo', $ch, $client);\n        }\n\n        for ($i = 0; $i < $length; $i++) {\n            go(function ($ch) {\n                $data = $ch->pop(1);\n                echo \"pop is \" . $data . \"\\n\";\n            }, $ch);\n        }\n    }\n\n    bar($client);\n});\n\n?>\n--EXPECTF--\nFatal error: Uncaught Swoole\\Error: Socket#%d has already been bound to another coroutine#%d, reading or writing of the same socket in coroutine#%d at the same time is not allowed in %s:%d\nStack trace:\n#0 %s(%d): Swoole\\Coroutine\\Http\\Client->get('%s')\n#1 {main}\n  thrown in %s on line %d\n"
  },
  {
    "path": "tests/swoole_http_client_coro/bug_3118.phpt",
    "content": "--TEST--\nswoole_http_client_coro: Github #3118\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\n\nCoroutine\\run(function () {\n    $client = new Coroutine\\Http\\Client(HTTPBIN_SERVER_HOST, HTTPBIN_SERVER_PORT);\n    $client->set(['timeout' => 10]);\n    $codes = [200, 201, 304, 301, 302, 303,];\n    foreach ($codes as $code) {\n        $client->get(\"/status/{$code}\");\n        Assert::same($client->getStatusCode(), $code);\n    }\n});\n\necho \"DONE\\n\"\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/compression_with_big_data.phpt",
    "content": "--TEST--\nswoole_http_client_coro: compression with big data\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->initRandomData(1, [1, 4, 8, 32][PRESSURE_LEVEL] * 1024 * 1024);\n$pm->parentFunc = function () use ($pm) {\n    Co\\Run(function () use ($pm) {\n        $random = $pm->getRandomData();\n        foreach ([[], ['download' => ['/', TEST_LOG_FILE]]] as $download) {\n            foreach (['deflate', 'gzip', 'br'] as $compression) {\n                $response = httpRequest(\n                    \"http://127.0.0.1:{$pm->getFreePort()}\",\n                    ['headers' => ['Accept-Encoding' => $compression]] + $download\n                );\n                Assert::same(\n                    empty($download) ? $response['body'] : file_get_contents(TEST_LOG_FILE),\n                    $random\n                );\n                if (empty($download)) {\n                    phpt_var_dump($response['headers']['content-encoding'] ?? 'no-compression');\n                    var_dump($response['headers']['content-encoding'] ?? $compression);\n                }\n            }\n        }\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set(['log_file' => '/dev/null']);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        static $random;\n        if (!$random) {\n            $random = $pm->getRandomData();\n        }\n        $response->end($random);\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nstring(7) \"deflate\"\nstring(4) \"gzip\"\nstring(2) \"br\"\n"
  },
  {
    "path": "tests/swoole_http_client_coro/connect_timeout.phpt",
    "content": "--TEST--\nswoole_http_client_coro: connect timeout\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    $cli = new Swoole\\Coroutine\\Http\\Client('140.207.135.104', 99, true);\n    $cli->setHeaders([\n        'Host' => \"login.wx.qq.com\",\n        \"User-Agent\" => 'Chrome/49.0.2587.3',\n        'Accept' => 'text/html,application/xhtml+xml,application/xml',\n        'Accept-Encoding' => 'gzip',\n    ]);\n    $random_timeout = mt_rand(100, 1000) / 1000;\n    phpt_var_dump($random_timeout);\n    $cli->set(['connect_timeout' => $random_timeout]);\n    $s = microtime(true);\n    $cli->get('/');\n    $s = microtime(true) - $s;\n    time_approximate($random_timeout, $s);\n    echo $cli->body;\n    $cli->close();\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/connection_close.phpt",
    "content": "--TEST--\nswoole_http_client_coro: connection close\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        var_dump($client->get('/close'));\n        var_dump($client->getHeaders());\n\n        var_dump($client->get('/keep_alive'));\n        var_dump($client->getHeaders());\n        $client->close();\n\n        var_dump($client->errMsg);\n        echo \"DONE\\n\";\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort());\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('request', function($request, $response) {\n        if ($request->server['request_uri'] == '/close') {\n            $response->header('connection', 'close');\n        }\n        $response->end();\n    });\n\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nbool(true)\narray(5) {\n  [\"connection\"]=>\n  string(5) \"close\"\n  [\"server\"]=>\n  string(18) \"swoole-http-server\"\n  [\"date\"]=>\n  string(%d) \"%s\"\n  [\"content-type\"]=>\n  string(9) \"text/html\"\n  [\"content-length\"]=>\n  string(1) \"0\"\n}\nbool(true)\narray(5) {\n  [\"server\"]=>\n  string(18) \"swoole-http-server\"\n  [\"date\"]=>\n  string(%d) \"%s\"\n  [\"connection\"]=>\n  string(10) \"keep-alive\"\n  [\"content-type\"]=>\n  string(9) \"text/html\"\n  [\"content-length\"]=>\n  string(1) \"0\"\n}\nstring(0) \"\"\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/construct_failed.phpt",
    "content": "--TEST--\nswoole_http_client_coro: construct failed\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$http = new Co\\Http\\Client('');\n?>\n--EXPECTF--\nFatal error: Uncaught Swoole\\Coroutine\\Http\\Client\\Exception: host is empty in %s/tests/swoole_http_client_coro/construct_failed.php:3\nStack trace:\n#0 %s/tests/swoole_http_client_coro/construct_failed.php(3): Swoole\\Coroutine\\Http\\Client->__construct('')\n#1 {main}\n  thrown in %s/tests/swoole_http_client_coro/construct_failed.php on line 3\n"
  },
  {
    "path": "tests/swoole_http_client_coro/cookies_set_bug.phpt",
    "content": "--TEST--\nswoole_http_client_coro: cookies set bug\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_ci('travis network');\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nfunction getCookies()\n{\n    $result = [];\n    var_dump($result); // must be empty array\n    return $result;\n}\n\ngo(function () {\n    $url_info = parse_url('http://httpbin.org/cookies/set/a/1');\n    $domain = $url_info['host'];\n    $path = $url_info['path'];\n    $cli = new Swoole\\Coroutine\\Http\\Client($domain);\n    $cli->set(['timeout' => 5]);\n    $cli->setHeaders([\n        'Host' => $domain,\n        'User-Agent' => 'Chrome/49.0.2587.3',\n        'Accept' => 'text/html,application/xhtml+xml,application/xml',\n        'Accept-Encoding' => 'gzip'\n    ]);\n\n    // first request\n    $cookies = getCookies();\n    $cli->setCookies($cookies);\n\n    Assert::assert($cli->get($path));\n\n    // second request\n    $cookies = getCookies();\n    $cli->setCookies($cookies);\n\n    Assert::assert($cli->get('/cookies'));\n});\n\n?>\n--EXPECTF--\narray(0) {\n}\narray(0) {\n}\n"
  },
  {
    "path": "tests/swoole_http_client_coro/defer.phpt",
    "content": "--TEST--\nswoole_http_client_coro: error handler\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->initRandomData(MAX_REQUESTS);\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->set(['timeout' => 1]);\n\n        // normal\n        for ($n = MAX_REQUESTS; $n--;) {\n            Assert::assert($cli->get('/'));\n            Assert::same($cli->body, $pm->getRandomData());\n        }\n\n        // failed when recv response\n        $retry_time = microtime(true);\n        for ($n = MAX_REQUESTS; $n--;) {\n            Assert::assert(!$cli->get('/'));\n        }\n        $retry_time = microtime(true) - $retry_time;\n\n        $pm->kill();\n        $pm->wait();\n\n        // failed when connect\n        $failed_time = microtime(true);\n        for ($n = MAX_REQUESTS; $n--;) {\n            Assert::assert(!$cli->get('/'));\n            Assert::same($cli->errCode, SOCKET_ECONNREFUSED);\n            Assert::same($cli->statusCode, SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED, $cli->statusCode);\n        }\n        $failed_time = microtime(true) - $failed_time;\n\n        phpt_var_dump($retry_time, $failed_time);\n        Assert::assert($retry_time > $failed_time * 2);\n    });\n    Swoole\\Event::wait();\n    echo \"OK\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm, $server) {\n        static $i = 0;\n        $i++;\n        if ($i <= MAX_REQUESTS) {\n            co::sleep(0.1 / MAX_REQUESTS);\n            $response->end($pm->getRandomData());\n        } else {\n            $server->close($request->fd);\n        }\n    });\n    $server->start();\n    $pm->wakeup();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_client_coro/defer_02.phpt",
    "content": "--TEST--\nswoole_http_client_coro: defer concurrency\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$randomData = [];\nfor ($n = MAX_CONCURRENCY; $n--;) {\n    $randomData[] = get_safe_random(rand(1024, 8192));\n}\n\n$pm->parentFunc = function () use ($pm, $randomData) {\n    go(function () use ($pm, $randomData) {\n        $clients = [];\n        // normal\n        for ($n = MAX_CONCURRENCY; $n--;) {\n            $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            $cli->set(['timeout' => 1]);\n            $cli->setDefer();\n            $clients[$n] = $cli;\n            $retval = $cli->get('/?n=' . $n);\n            Assert::assert($retval);\n            if (!$retval)\n            {\n                var_dump($cli->errCode);\n            }\n        }\n        for ($n = MAX_CONCURRENCY; $n--;) {\n            $cli = $clients[$n];\n            $cli->recv();\n            Assert::same($cli->statusCode, 200);\n            Assert::same($cli->body, $randomData[$n]);\n        }\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"OK\\n\";\n};\n$pm->childFunc = function () use ($pm, $randomData) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm, $server, $randomData) {\n        $response->end($randomData[$request->get['n']]);\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_client_coro/disable_keep_alive.phpt",
    "content": "--TEST--\nswoole_http_client_coro: disable keep alive\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nCo\\run(function () {\n    $host = 'news.qq.com';\n    $cli = new Swoole\\Coroutine\\Http\\Client($host, 443, true);\n    $cli->set([\n        'timeout' => 10,\n        'keep_alive' => false\n    ]);\n    $cli->setHeaders(['Host' => $host]);\n    $cli->get('/');\n    Assert::same($cli->statusCode, 200);\n\n    Assert::true($cli->get('/ch/tech/'));\n    Assert::same($cli->statusCode, 200);\n\n    // failed clear\n    $cli->set([\n        'timeout' => 0.001\n    ]);\n    Assert::false($cli->get('/ch/tech/'));\n    Assert::assert(empty($cli->headers));\n    Assert::assert(empty($cli->body));\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/download.phpt",
    "content": "--TEST--\nswoole_http_client_coro: download file and download offset\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_ci('travis network');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$count = 0;\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, &$count) {\n    $raw_file_size = filesize(TEST_IMAGE);\n    $raw_file_content = file_get_contents(TEST_IMAGE);\n    for ($c = MAX_CONCURRENCY_LOW; $c--;) {\n        go(function () use ($pm, &$count, $c, $raw_file_size, $raw_file_content) {\n            $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            $cli->set(['timeout' => 5]);\n            $filename = '/tmp/test-' . $c . '.jpg';\n            $offset = mt_rand(0, $raw_file_size);\n            $cli->setHeaders(['Range' => \"bytes=$offset-\"]);\n            Assert::assert($cli->download('/', $filename, 0));\n            // assert length\n            if (!Assert::assert($raw_file_size === ($offset + filesize($filename)))) {\n                goto _end;\n            }\n            // read content\n            $raw_file = fopen(TEST_IMAGE, 'r+');\n            fseek($raw_file, $offset);\n            if (!Assert::assert(co::fread($raw_file) === co::readFile($filename))) {\n                goto _end;\n            }\n\n            $count++;\n            _end:\n            @unlink($filename);\n            $cli->setHeaders([]);\n            $cli->get('/');\n            Assert::same($cli->body, $raw_file_content);\n        });\n    }\n    Swoole\\Event::wait();\n    Assert::same($count, MAX_CONCURRENCY_LOW);\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set(['log_file' => '/dev/null']);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $offset = (int) @explode('-', explode('=', $request->header['range'])[1])[0];\n        $response->sendfile(TEST_IMAGE, $offset);\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/download_302.phpt",
    "content": "--TEST--\nswoole_http_client_coro: http download 302\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst FILE = '/tmp/download.html';\n\nCo\\run(function () {\n    $cli = new Swoole\\Coroutine\\Http\\Client(HTTPBIN_SERVER_HOST, HTTPBIN_SERVER_PORT);\n    $cli->download('/absolute-redirect/1', FILE);\n    Assert::contains(file_get_contents(FILE), 'Redirecting');\n    if (((string)$cli->statusCode)[0] === '3') {\n        $cli->download($cli->headers['location'], FILE);\n    }\n    if (Assert::contains(json_decode(file_get_contents(FILE), true)['url'], 'get')) {\n        echo \"OK\\n\";\n    }\n});\n@unlink(FILE);\n\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_client_coro/download_failed.phpt",
    "content": "--TEST--\nswoole_http_client_coro: http download io failure\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst FILE = '/tmp/download.html';\n\n@unlink(FILE);\nCo\\run(function () {\n    $server = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    Assert::assert($server->bind('127.0.0.1', 0));\n    Assert::assert($server->listen());\n    $oort = $server->getsockname()['port'];\n    go(function () use ($server) {\n        $client = $server->accept();\n        while ($client->recv(1)) {\n            CO::sleep(0.01);\n        }\n        $server->close();\n    });\n    $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $oort);\n    $cli->set(['timeout' => 0.1]);\n    Assert::false($cli->download('/get', FILE));\n    Assert::false(file_exists(FILE));\n    echo \"OK\\n\";\n});\nCo\\run(function () {\n    $server = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    Assert::assert($server->bind('127.0.0.1', 0));\n    Assert::assert($server->listen());\n    $oort = $server->getsockname()['port'];\n    go(function () use ($server) {\n        $client = $server->accept();\n        $client->send(\"HTTP/1.1 200 OK\\r\\nContent-Length: 99999\\r\\n\\r\\n\");\n        while ($client->send('a')) {\n            CO::sleep(0.001);\n        }\n        $server->close();\n    });\n    $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $oort);\n    $cli->set(['timeout' => 0.1]);\n    Assert::false($cli->download('/get', FILE));\n    Assert::true(file_exists(FILE));\n    echo \"OK\\n\";\n});\n@unlink(FILE);\n\n?>\n--EXPECT--\nOK\nOK\n"
  },
  {
    "path": "tests/swoole_http_client_coro/download_filename_bug.phpt",
    "content": "--TEST--\nswoole_http_client_coro: The bug of the filename parameter of download()\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nclass C1\n{\n    protected $f;\n\n    protected $savedFileName;\n\n    public function __construct($f)\n    {\n        $this->f = $f;\n    }\n\n    public function withSavedFileName($savedFileName)\n    {\n        $self = clone $this;\n        $self->savedFileName = $savedFileName;\n        return $self;\n    }\n\n    public function getSavedFileName()\n    {\n        return $this->savedFileName;\n    }\n\n}\n\nfunction download($pm, $fileName)\n{\n    $basename = substr($fileName, 0, -2);\n    $fileName = $basename . '.jpg';\n    $c1 = new C1($fileName);\n    $c1 = $c1->withSavedFileName($fileName);\n\n    $client = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n    $client->set(['timeout' => 5]);\n\n    $client->download('/', $fileName);\n    Assert::true(file_get_contents(TEST_IMAGE) == file_get_contents($fileName));\n}\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use($pm) {\n        download($pm, '/tmp/test-1.*');\n    });\n\n    Co\\run(function () use($pm) {\n        download($pm, '/tmp/test-2.*');\n    });\n\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set(['log_file' => '/dev/null']);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->sendfile(TEST_IMAGE);\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/duplicate_header.phpt",
    "content": "--TEST--\nswoole_http_client_coro: duplicate header\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$uuid = uniqid();\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm, $uuid) {\n    Co\\run(function () use ($pm, $uuid) {\n        $client = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        Assert::true($client->get('/'));\n        Assert::eq($client->headers['values-1'], ['hello', 'swoole', $uuid]);\n        Assert::eq($client->headers['values-2'], ['hello', $uuid]);\n        $pm->kill();\n    });\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm, $uuid) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function ($request, Swoole\\Http\\Response $response) use ($uuid) {\n        $response->header('values-1', ['hello', 'swoole', $uuid]);\n        $response->header('values-2', ['hello', $uuid]);\n        $response->end('OK');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/error_handler.phpt",
    "content": "--TEST--\nswoole_http_client_coro: error handler\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Server;\nuse Swoole\\Coroutine\\System;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager;\n$pm->initRandomData(MAX_CONCURRENCY_MID);\n\n$rdata = [];\nfor ($c = MAX_CONCURRENCY_MID; $c--;) {\n    $rdata[] = $pm->getRandomData();\n}\n\n$pm->parentFunc = function () use ($pm, $rdata) {\n    run(function () use ($pm, $rdata) {\n        $cli_map = [];\n        $data_map = [];\n        for ($c = MAX_CONCURRENCY_MID; $c--;) {\n            $cli_map[$c] = $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            $cli->setDefer(true);\n            $cli->get('/?c=' . $c);\n            $data_map[$cli->socket->fd] = $rdata[$c];\n        }\n        foreach ($cli_map as $cli) {\n            Assert::assert($cli->recv());\n            Assert::same($cli->body, $data_map[$cli->socket->fd]);\n        }\n\n        $pm->kill();\n        $pm->wait();\n\n        System::sleep(0.2);\n\n        // when we enable reconnect, we will get connect error\n        foreach ($cli_map as $cli) {\n            $cli->setDefer(false);\n            Assert::assert(!$cli->get('/'));\n            Assert::same($cli->statusCode, SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED);\n            Assert::same($cli->errCode, SOCKET_ECONNREFUSED);\n        }\n    });\n    echo \"OK\\n\";\n};\n$pm->childFunc = function () use ($pm, $rdata) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Request $request, Response $response) use ($pm, $server, $rdata) {\n        static $i = 0;\n        $i++;\n        if ($i <= MAX_CONCURRENCY_MID) {\n            $response->end($rdata[$request->get['c']]);\n        } else {\n            $server->close($request->fd);\n        }\n    });\n    $server->on('shutdown', function (Server $server) use ($pm) {\n        foreach ($server->connections as $fd) {\n            $server->close($fd);\n        }\n        $pm->wakeup();\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_client_coro/get.phpt",
    "content": "--TEST--\nswoole_http_client_coro: http client\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\nskip_if_darwin_todo();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\Socket;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Server;\n\n$pm = new ProcessManager();\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n    ]);\n    $http->on('WorkerStart', function (Server $serv) {\n        /*\n         * @var $pm ProcessManager\n         */\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) {\n        $domain = TEST_DOMAIN_3;\n        $cli = new Client($domain, 443, true);\n        $cli->set(['timeout' => 10]);\n        $cli->setHeaders([\n            'Host' => $domain,\n            'User-Agent' => TEST_USER_AGENT,\n            'Accept' => 'text/html,application/xhtml+xml,application/xml',\n            'Accept-Encoding' => 'gzip',\n        ]);\n        $ret = $cli->get('/');\n        Assert::assert($cli->socket instanceof Socket);\n        if (!$ret) {\n            $response->end('ERROR:' . $cli->errCode . \"\\n\");\n            return;\n        }\n        $response->end(\"OK\\n\");\n        $cli->close();\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_client_coro/get_header_out_after_close.phpt",
    "content": "--TEST--\nswoole_http_client_coro: getHeaderOut after close\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\n\nco::create(function () {\n    $http = new Swoole\\Coroutine\\Http\\Client('httpbin.org', 80, false);\n    $http->set([\n        'timeout' => -1,\n        'keep_alive' => false,\n    ]);\n    $http->execute('/get');\n    swoole_string($http->getHeaderOut())->contains('httpbin.org');\n});\nSwoole\\Event::wait();\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/get_twice.phpt",
    "content": "--TEST--\nswoole_http_client_coro: get twice\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine\\Http\\Client;\nconst N = 2;\n\nSwoole\\Coroutine\\Run(function () {\n    $client = new Client('www.baidu.com', 443, true);\n    $client->set(['timeout' => 5,]);\n    for ($i = 0; $i < N; $i++) {\n        $rand = mt_rand(100000, 999999999);\n        $path = \"/index.php?email=\" . $rand . \"@\" . substr(md5(microtime(true)), 0, 8) . \".com\";\n        $result = $client->get($path);\n        if (!$result) {\n            var_dump(\"ERROR: \".$client->getStatusCode());\n        }\n        Assert::assert($result);\n        Assert::assert($client->getStatusCode() == 200);\n        $client->close();\n    }\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/get_twice_keepalive.phpt",
    "content": "--TEST--\nswoole_http_client_coro: get twice and keepalive\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine\\Http\\Client;\nconst N = 2;\n\nSwoole\\Coroutine\\Run(function () {\n    $client = new Client('www.baidu.com', 443, true);\n    $client->set(['timeout' => 5,]);\n    for ($i = 0; $i < N; $i++) {\n        $rand = mt_rand(100000, 999999999);\n        $path = \"/index.php?email=\" . $rand . \"@\" . substr(md5(microtime(true)), 0, 8) . \".com\";\n        Assert::assert($client->get($path));\n        Assert::assert($client->getStatusCode() == 200);\n    }\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/get_without_content_length.phpt",
    "content": "--TEST--\nswoole_http_client_coro: http GET without Content-Length header\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    co::create(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->set([\n            'timeout' => 10\n        ]);\n        $cli->setHeaders([\n            'Connection' => 'close',\n            'Accept' => '*/*'\n        ]);\n        $ret = $cli->get('/');\n        Assert::true($ret);\n        Assert::same($cli->statusCode, 200);\n        Assert::assert(strlen($cli->body) > 1024 * 5);\n        $pm->kill();\n        echo \"OK\\n\";\n    });\n    Swoole\\Event::wait();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set(array(\n        'log_file' => '/dev/null'\n    ));\n    $serv->on('WorkerStart', function (Swoole\\Server $serv)\n    {\n        /**\n         * @var $pm ProcessManager\n         */\n        global $pm;\n        $pm->wakeup();\n    });\n    $serv->on('receive', function ($serv, $fd, $threadId, $data)\n    {\n        $serv->send($fd, \"HTTP/1.1 200 OK\\r\\nServer: nginx\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\n\\r\\n\");\n        foreach (range(0, 5) as $i) {\n            co::sleep(0.1);\n            $serv->send($fd, str_repeat('A', rand(1024, 2048)));\n        }\n        $serv->close($fd);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_client_coro/h2c_upgrade.phpt",
    "content": "--TEST--\nswoole_http_client_coro: upgrade bug\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $host = 'httpbin.org';\n    $cli = new Swoole\\Coroutine\\Http\\Client($host, 443, true);\n    $cli->set(['timeout' => 10]);\n    $cli->setHeaders([\n        'host' => $host, // wrong case auto fix\n        'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',\n        'Accept-Language' => 'zh-CN,zh;q=0.9',\n        'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.5478.400 QQBrowser/10.1.1550.400',\n        'Accept-Encoding' => 'gzip',\n    ]);\n    $ret = $cli->get('/');\n    Assert::assert($ret);\n    Assert::assert(str_contains($cli->body, 'httpbin.org'));\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/head_method.phpt",
    "content": "--TEST--\nswoole_http_client_coro: http client with HEAD method\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Coroutine::create(function ()\n{\n    $cli = new \\Swoole\\Coroutine\\Http\\Client('www.baidu.com', 80);\n    $cli->set(['timeout' => 10]);\n    $cli->setMethod('HEAD');\n    $cli->get('/');\n    Assert::same($cli->statusCode, 200);\n    Assert::assert(count($cli->headers) > 0);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/host.phpt",
    "content": "--TEST--\nswoole_http_client_coro: host\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nrequire __DIR__ . '/../include/config.php';\nskip('HTTPBIN_SERVER_PORT can not be 80', HTTPBIN_SERVER_PORT === 80);\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $body = json_decode(httpGetBody('http://' . HTTPBIN_SERVER_HOST . ':' . HTTPBIN_SERVER_PORT . '/get'), true);\n    Assert::same((int)(parse_url($body['headers']['Host']))['port'], HTTPBIN_SERVER_PORT);\n    echo \"DONE\\n\";\n});\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/http_chunk.phpt",
    "content": "--TEST--\nswoole_http_client_coro: http chunk\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Event;\n\nconst N = 8;\n$chunks = [];\n$body = '';\n$n = N;\nwhile ($n--) {\n    $chunk = base64_encode(random_bytes(random_int(256, 4096)));\n    $body .= $chunk;\n    $chunks[] = $chunk;\n}\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm, $chunks, $body) {\n    Co\\run(function () use ($pm, $chunks, $body) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        Assert::assert($cli->get('/'));\n        Assert::eq($cli->getBody(), $body);\n    });\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm, $chunks) {\n    Co\\run(function () use ($pm, $chunks) {\n        Event::defer(function () use ($pm) {\n            $pm->wakeup();\n        });\n        $server = new Swoole\\Coroutine\\Http\\Server('127.0.0.1', $pm->getFreePort());\n        $server->handle('/', function ($req, $resp) use ($server, $chunks) {\n            foreach ($chunks as $chunk) {\n                $resp->write($chunk);\n                usleep(mt_rand(10, 50) * 100);\n            }\n        });\n        $server->start();\n    });\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/http_proxy.phpt",
    "content": "--TEST--\nswoole_http_client_coro: http client with http_proxy\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_http_proxy();\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $domain = 'www.baidu.com';\n    $cli = new Swoole\\Coroutine\\Http\\Client($domain, 443, true);\n    // $cli->setHeaders(['Host' => $domain]); // without host header it can also work well\n    $cli->set([\n        'timeout' => 30,\n        'http_proxy_host' => HTTP_PROXY_HOST,\n        'http_proxy_port' => HTTP_PROXY_PORT\n    ]);\n    $result = $cli->get('/');\n    Assert::assert($result);\n    Assert::assert(stripos($cli->body, '百度') !== false);\n    echo \"DONE\\n\";\n});\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/http_proxy_443.phpt",
    "content": "--TEST--\nswoole_http_client_coro: http client with http_proxy\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_http_proxy();\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $domain = 'mail.qq.com';\n        $cli = new Swoole\\Coroutine\\Http\\Client($domain, 443, true);\n        $cli->setHeaders(['Host' => $domain]);\n        $cli->set([\n            'timeout'         => 30,\n            'http_proxy_host' => HTTP_PROXY_HOST,\n            'http_proxy_port' => HTTP_PROXY_PORT\n        ]);\n        $result = $cli->get('/');\n        Assert::assert($result);\n        Assert::assert(stripos($cli->body, 'tencent') !== false);\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $pm->wakeup();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/http_proxy_with_host_port.phpt",
    "content": "--TEST--\nswoole_http_client_coro: http client with http_proxy and host and port\n--SKIPIF--\n<?php\nrequire __DIR__.'/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__.'/../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', 1234);\n        $cli->set([\n            'timeout' => 30,\n            'http_proxy_host' => '127.0.0.1',\n            'http_proxy_port' => $pm->getFreePort(),\n        ]);\n        $cli->setHeaders([\n            'Host' => '127.0.0.1:1234',\n        ]);\n        $cli->get('/');\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'log_file'       => '/dev/null',\n        'open_eof_check' => true,\n        'package_eof'    => \"\\r\\n\\r\\n\",\n    ]);\n    $server->on('Receive', function ($server, $fd, $reactor_id, $data) {\n        echo $data;\n        $server->close($fd);\n    });\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nGET http://127.0.0.1:1234/ HTTP/1.1\nHost: 127.0.0.1:1234\nConnection: keep-alive\nAccept-Encoding: gzip, deflate, br\n"
  },
  {
    "path": "tests/swoole_http_client_coro/http_upload_big.phpt",
    "content": "--TEST--\nswoole_http_client_coro: upload a big file\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $content = str_repeat(get_safe_random(1024), 5 * 1024);\n        file_put_contents('/tmp/test.jpg', $content);\n        $cli->addFile('/tmp/test.jpg', 'test.jpg');\n        $cli->setHeaders([\n            'md5' => md5($content),\n        ]);\n        $ret = $cli->post('/', ['name' => 'rango']);\n        Assert::assert($ret);\n        Assert::assert(count($cli->headers) > 0);\n        Assert::same($cli->statusCode, 200);\n        Assert::eq($cli->body, 'success');\n        $cli->close();\n        @unlink('/tmp/test.jpg');\n        echo \"DONE\\n\";\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n\n    $http->set([\n        'log_file' => '/dev/null',\n        'package_max_length' => 10 * 1024 * 1024,\n    ]);\n\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        Assert::eq(md5_file($request->files['test_jpg']['tmp_name']) ,$request->header['md5']);\n        $response->end('success');\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/https.phpt",
    "content": "--TEST--\nswoole_http_client_coro: https client\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    $cli = new Swoole\\Coroutine\\Http\\Client('www.baidu.com', 443, true);\n    $cli->set(['timeout' => 10]);\n    $cli->setHeaders([\n        'Host' => 'www.baidu.com',\n        'User-Agent' => 'Chrome/49.0.2587.3',\n        'Accept' => 'text/html,application/xhtml+xml,application/xml',\n        'Accept-Encoding' => 'gzip',\n    ]);\n    $ret = ($cli->get('/'));\n    if (!$ret) {\n        echo(\"ERROR\\n\");\n        var_dump($cli->errCode);\n        return;\n    } else {\n        echo(\"OK\\n\");\n        $cli->close();\n    }\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_client_coro/https_upload_big.phpt",
    "content": "--TEST--\nswoole_http_client_coro: upload a big file\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\n\ndefine('SSL_DIR', realpath(__DIR__.'/../../examples/ssl'));\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort(), true);\n        $content = str_repeat(get_safe_random(1024), 5 * 1024);\n        file_put_contents('/tmp/test.jpg', $content);\n        $cli->addFile('/tmp/test.jpg', 'test.jpg');\n        $cli->setHeaders([\n            'md5' => md5($content),\n        ]);\n        $ret = $cli->post('/', ['name' => 'rango']);\n        Assert::assert($ret);\n        Assert::same($cli->statusCode, 200);\n        Assert::eq($cli->body, 'success');\n        $cli->close();\n        @unlink('/tmp/test.jpg');\n        echo \"DONE\\n\";\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n\n    $http->set([\n        'log_file' => '/dev/null',\n        'package_max_length' => 10 * 1024 * 1024,\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n    ]);\n\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        Assert::eq(md5_file($request->files['test_jpg']['tmp_name']) ,$request->header['md5']);\n        $response->end('success');\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/issue_2664.phpt",
    "content": "--TEST--\nswoole_http_client_coro: set_cookie_headers\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\Run(function () use ($pm) {\n        var_dump(httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}\")['set_cookie_headers']);\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set(['log_file' => '/dev/null']);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        $time_left = time() + 84600;\n        $response->cookie('key1', 'val1', $time_left, '/', 'id.test.com');\n        $response->cookie('key1', '', 0, '/', 'test.com');\n        $response->cookie('key2', 'val2', $time_left, '/', 'id.test.com');\n        $response->cookie('key2', '', 0, '/', 'test.com');\n        $response->end(\"<h1>Hello Swoole. #\" . rand(1000, 9999) . \"</h1>\");\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\narray(4) {\n  [0]=>\n  string(91) \"key1=val1; expires=%s; Max-Age=84600; path=/; domain=id.test.com\"\n  [1]=>\n  string(87) \"key1=deleted; expires=%s; Max-Age=0; path=/; domain=test.com\"\n  [2]=>\n  string(91) \"key2=val2; expires=%s; Max-Age=84600; path=/; domain=id.test.com\"\n  [3]=>\n  string(87) \"key2=deleted; expires=%s; Max-Age=0; path=/; domain=test.com\"\n}\n"
  },
  {
    "path": "tests/swoole_http_client_coro/keep_alive.phpt",
    "content": "--TEST--\nswoole_http_client_coro: really keep alive\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip('too slow');\nif (getenv(\"SKIP_SLOW_TESTS\")) {\n    die(\"skip slow test\");\n}\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $cli = new Swoole\\Coroutine\\Http\\Client('pecl.php.net', 443, true);\n    $cli->set(['timeout' => 5]);\n    Assert::assert($cli->get('/'));\n    Assert::assert(strpos($cli->body, 'pecl') !== false);\n    co::sleep(75);\n    Assert::assert($cli->get('/'));\n    Assert::assert(strpos($cli->body, 'pecl') !== false);\n});\nSwoole\\Event::wait();\necho \"OK\\n\";\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_client_coro/long_domain.phpt",
    "content": "--TEST--\nswoole_http_client_coro: long domain\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_ci('travis network');\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nfunction http_get(string $url)\n{\n    $url_info = parse_url($url);\n    $domain = $url_info['host'];\n    $cli = new Swoole\\Coroutine\\Http\\Client($domain);\n    $cli->set(['timeout' => 5]);\n    $cli->setHeaders([\n        'Host' => $domain,\n        'User-Agent' => 'Chrome/49.0.2587.3',\n        'Accept' => 'text/html,application/xhtml+xml,application/xml',\n        'Accept-Encoding' => 'gzip'\n    ]);\n    Assert::assert($cli->get('/'));\n    Assert::same($cli->statusCode, 200);\n    Assert::assert(!empty($cli->body));\n}\n\ngo(function () {\n    http_get('http://888888888888888888888888888888888888888888888888888888888888888.com');\n});\ngo(function () {\n    http_get('http://www.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com');\n});\ngo(function () {\n    http_get('http://www.mamashuojiusuannizhucedeyumingzaichanggoogledounengsousuochulai.cn');\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/lowercase_header.phpt",
    "content": "--TEST--\nswoole_http_client_coro: lowercase header\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort(), false);\n        $cli->set(['timeout' => 10,\n            'lowercase_header' => false,\n        ]);\n        Assert::assert($cli->get('/'));\n        Assert::assert(array_key_exists('Hello-world', $cli->getHeaders()));\n        Assert::assert(!array_key_exists('hello-world', $cli->getHeaders()));\n\n\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort(), false);\n        Assert::assert($cli->get('/'));\n        Assert::assert(!array_key_exists('Hello-world', $cli->getHeaders()));\n        Assert::assert(array_key_exists('hello-world', $cli->getHeaders()));\n\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null'\n    ]);\n    $http->on('WorkerStart', function (Swoole\\Server $serv) {\n        /**\n         * @var $pm ProcessManager\n         */\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->header('Hello-world', 'swoole', false);\n        $response->end();\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/multi.phpt",
    "content": "--TEST--\nswoole_http_client_coro: multi http client\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    $cli1 = new Swoole\\Coroutine\\Http\\Client('www.baidu.com', 443, true);\n    $cli1->set(['timeout' => 10]);\n    $cli1->setHeaders([\n        'Host' => 'www.baidu.com',\n        'User-Agent' => 'Chrome/49.0.2587.3',\n        'Accept' => 'text/html,application/xhtml+xml,application/xml',\n        'Accept-Encoding' => 'gzip',\n    ]);\n    $cli1->setDefer(1);\n\n    $cli2 = new Swoole\\Coroutine\\Http\\Client('www.qq.com', 443, true);\n    $cli2->set(['timeout' => 10]);\n    $cli2->setHeaders([\n        'Host' => 'www.qq.com',\n        'User-Agent' => 'Chrome/49.0.2587.3',\n        'Accept' => 'text/html,application/xhtml+xml,application/xml',\n        'Accept-Encoding' => 'gzip',\n    ]);\n    $cli2->setDefer(1);\n\n    $ret1 = ($cli1->get('/'));\n    $ret2 = ($cli2->get('/'));\n    if (!$ret1 or !$ret2)    {\n        echo \"ERROR\\n\";\n        var_dump($cli1->errCode, $cli1->errMsg);\n        var_dump($cli2->errCode, $cli2->errMsg);\n    }\n    else\n    {\n        Assert::assert($cli1->recv());\n        Assert::assert($cli2->recv());\n        Assert::contains($cli1->body, \"baidu\");\n        Assert::contains($cli2->body, \"Tencent\");\n        $cli1->close();\n        $cli2->close();\n        echo \"OK\\n\";\n    }\n});\n\nSwoole\\Event::wait();\n\n\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_client_coro/multi_and_reuse.phpt",
    "content": "--TEST--\nswoole_http_client_coro: reuse defer client\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    function createDeferCli(string $host, bool $ssl = false): Swoole\\Coroutine\\Http\\Client\n    {\n        $cli = new Swoole\\Coroutine\\Http\\Client($host, $ssl ? 443 : 80, $ssl);\n        $cli->set(['timeout' => 10]);\n        $cli->setHeaders([\n            'Host' => $host,\n            'User-Agent' => 'Chrome/49.0.2587.3',\n            'Accept' => 'text/html,application/xhtml+xml,application/xml',\n            'Accept-Encoding' => 'gzip',\n        ]);\n        $cli->setDefer(true);\n\n        return $cli;\n    }\n\n    $baidu = createDeferCli('www.baidu.com', true);\n    $qq = createDeferCli('news.qq.com', true);\n\n    //first\n    $baidu->get('/');\n    $qq->get('/');\n    $baidu->recv(10);\n    $qq->recv(10);\n    Assert::same($baidu->statusCode, 200);\n    Assert::assert(stripos($baidu->body, 'baidu') !== false);\n    Assert::same($qq->statusCode, 200);\n    Assert::assert(stripos($qq->body, 'tencent') !== false);\n\n    //reuse\n    $baidu->get('/duty/');\n    $qq->get('/ch/tech/');\n    $baidu->recv(10);\n    $qq->recv(10);\n    Assert::same($baidu->statusCode, 200);\n    Assert::assert(stripos($baidu->body, 'baidu') !== false);\n    Assert::same($qq->statusCode, 200);\n    Assert::assert(stripos($qq->body, 'tencent') !== false);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/parser.phpt",
    "content": "--TEST--\nswoole_http_client_coro: http header field normal chars\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_function_not_exist('curl_init');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n//    static const uint8_t normal_url_char[256] = {\n//        /*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */\n//        0,       0,       0,       0,       0,       0,       0,       0,\n//    /*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */\n//            0,       0,       0,       0,       0,       0,       0,       0,\n//    /*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */\n//            0,       0,       0,       0,       0,       0,       0,       0,\n//    /*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */\n//            0,       0,       0,       0,       0,       0,       0,       0,\n//    /*  32 sp    33  !    34  \"    35  #    36  $    37  %    38  &    39  '  */\n//            0,       1,       1,       0,       1,       1,       1,       1,\n//    /*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */\n//            1,       1,       1,       1,       1,       1,       1,       1,\n//    /*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */\n//            1,       1,       1,       1,       1,       1,       1,       1,\n//    /*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */\n//            1,       1,       1,       1,       1,       1,       1,       0,\n//    /*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */\n//            1,       1,       1,       1,       1,       1,       1,       1,\n//    /*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */\n//            1,       1,       1,       1,       1,       1,       1,       1,\n//    /*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */\n//            1,       1,       1,       1,       1,       1,       1,       1,\n//    /*  88  X    89  Y    90  Z    91  [    92  \\    93  ]    94  ^    95  _  */\n//            1,       1,       1,       1,       1,       1,       1,       1,\n//    /*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */\n//            1,       1,       1,       1,       1,       1,       1,       1,\n//    /* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */\n//            1,       1,       1,       1,       1,       1,       1,       1,\n//    /* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */\n//            1,       1,       1,       1,       1,       1,       1,       1,\n//    /* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */\n//            1,       1,       1,       1,       1,       1,       1,       0 };\n\n/* Tokens as defined by rfc 2616. Also lowercases them.\n *        token       = 1*<any CHAR except CTLs or separators>\n *     separators     = \"(\" | \")\" | \"<\" | \">\" | \"@\"\n *                    | \",\" | \";\" | \":\" | \"\\\" | <\">\n *                    | \"/\" | \"[\" | \"]\" | \"?\" | \"=\"\n *                    | \"{\" | \"}\" | SP | HT\n */\n\nstatic $normal_chars = [\n    '!',\n    '$',\n    '%',\n    '&',\n    '*',\n    '+',\n    '-',\n    '.',\n    '\\'',\n\n    // not support numeric header name in swoole\n    // '0',\n    // '1',\n    // '2',\n    // '3',\n    // '4',\n    // '5',\n    // '6',\n    // '7',\n    // '8',\n    // '9',\n\n    // will be split and not allowed as a start in http parser\n    // ':',\n\n    // case insensitive\n    // 'A',\n    // 'B',\n    // 'C',\n    // 'D',\n    // 'E',\n    // 'F',\n    // 'G',\n    // 'H',\n    // 'I',\n    // 'J',\n    // 'K',\n    // 'L',\n    // 'M',\n    // 'N',\n    // 'O',\n    // 'P',\n    // 'Q',\n    // 'R',\n    // 'S',\n    // 'T',\n    // 'U',\n    // 'V',\n    // 'W',\n    // 'X',\n    // 'Y',\n    // 'Z',\n\n    '^',\n    '_',\n    '`',\n\n    'a',\n    'b',\n    'c',\n    'd',\n    'e',\n    'f',\n    'g',\n    'h',\n    'i',\n    'j',\n    'k',\n    'l',\n    'm',\n    'n',\n    'o',\n    'p',\n    'q',\n    'r',\n    's',\n    't',\n    'u',\n    'v',\n    'w',\n    'x',\n    'y',\n    'z',\n\n    '|',\n    '~',\n\n    // not allowed as a start in http parser\n    '\"',  // start at 40\n    'x(',\n    'x)',\n    'x,',\n    '/',\n    'x;',\n    'x<',\n    'x=',\n    'x>',\n    'x@',\n    'x[',\n    'x\\\\',\n    'x]',\n    'x{',\n    '}',\n];\n\n$start = 40;\n\n$pm = new ProcessManager;\n$pm->initRandomData((count($normal_chars) + 1) * 4);\n$pm->parentFunc = function () use ($pm, &$normal_chars, $start) {\n    // use curl\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:{$pm->getFreePort()}/\");\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HEADER, 1);\n    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);\n    curl_setopt($ch, CURLOPT_TIMEOUT, 10);\n    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Accept-Encoding: gzip']);\n    curl_setopt($ch, CURLOPT_ENCODING, \"gzip\");\n    $output = curl_exec($ch);\n    curl_close($ch);\n    list($headers, $body) = explode(\"\\r\\n\\r\\n\", $output);\n    $headers = explode(\"\\r\\n\", $headers);\n    array_shift($headers);\n    foreach ($headers as $header) {\n        list($name, $value) = explode(': ', $header);\n        if (in_array(strtolower($name), $normal_chars)) {\n            Assert::same($value, ($s = $pm->getRandomData()));\n        }\n    }\n    Assert::same($body, $pm->getRandomData());\n\n    // use swoole http client\n    go(function () use ($pm, &$normal_chars, $start) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->set(['timeout' => 1]);\n        Assert::assert($cli->get('/valid'));\n        foreach ($cli->headers as $name => $value) {\n            if (in_array($name, $normal_chars)) {\n                Assert::same($value, $pm->getRandomData());\n            }\n        }\n        Assert::same($cli->body, $pm->getRandomData());\n\n        for ($i = $start; $i < sizeof($normal_chars); $i++) {\n            $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            $cli->set(['timeout' => 1]);\n            Assert::false($cli->get('/invalid?key=' . $i));\n            Assert::same($cli->errMsg, \"Http invalid protocol\");\n            $cli->close();\n        }\n    });\n\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set(['log_file' => '/dev/null']);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        global $normal_chars;\n        global $start;\n        if ($request->server['request_uri'] == '/') {\n            foreach ($normal_chars as $char) {\n                $response->header($char, $pm->getRandomData());\n            }\n        } else if ($request->server['request_uri'] == '/valid') {\n            for ($i = 0; $i < $start; $i++) {\n                $response->header($normal_chars[$i], $pm->getRandomData());\n            }\n        } else {\n            $response->header($normal_chars[$request->get['key']], $pm->getRandomData());\n        }\n\n        $response->end($pm->getRandomData());\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/post_array.phpt",
    "content": "--TEST--\nswoole_http_client_coro: add array data\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $uri = 'http://' . HTTPBIN_SERVER_HOST . ':' . HTTPBIN_SERVER_PORT . '/post';\n    $data = [];\n    for ($n = MAX_REQUESTS; $n--;) {\n        $data[get_safe_random()] = get_safe_random();\n    }\n    $body = httpGetBody($uri, ['method' => 'POST', 'data' => $data]);\n    $form = json_decode($body, true)['form'];\n    Assert::eq($form, $data);\n});\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/reconnect_but_failed.phpt",
    "content": "--TEST--\nswoole_http_client_coro: reconnect but failed\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->initRandomData(MAX_REQUESTS);\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->set(['timeout' => 1]);\n\n        for ($n = MAX_REQUESTS; $n--;) {\n            $ret = $cli->get('/');\n            Assert::same($ret, !($n % 2));\n            if ($ret) {\n                Assert::same($cli->body, $pm->getRandomData());\n            }\n        }\n\n        $pm->kill();\n        usleep(100000);\n\n        Assert::assert(!$cli->get('/'));\n        Assert::same($cli->errCode, SOCKET_ECONNREFUSED);\n        Assert::same($cli->statusCode, SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED);\n        for ($n = MAX_REQUESTS; $n--;) {\n            Assert::assert(!$cli->get('/'));\n            Assert::same($cli->errCode, SOCKET_ECONNREFUSED);\n            Assert::same($cli->statusCode, SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED);\n        }\n    });\n    Swoole\\Event::wait();\n    echo \"OK\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'enable_coroutine' => false,\n    ]);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm, $server) {\n        static $i = 0;\n        $i++;\n        if ($i % 2) {\n            $server->close($request->fd);\n        } else {\n            $response->end($pm->getRandomData());\n        }\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_client_coro/recv_slow_timeout.phpt",
    "content": "--TEST--\nswoole_http_client_coro: recv_all data from slow server\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\Socket;\nuse Swoole\\Event;\n\n$pm = new ProcessManager();\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($c = MAX_CONCURRENCY_LOW; $c--;) {\n        go(function () use ($pm) {\n            $cli = new Client('127.0.0.1', $pm->getFreePort());\n            $cli->set(['timeout' => 1]);\n            $s = microtime(true);\n            $ret = $cli->get('/');\n            $s = microtime(true) - $s;\n            phpt_var_dump($s);\n            if (Assert::assert(!$ret)) {\n                Assert::assert($cli->errCode === SOCKET_ETIMEDOUT);\n                Assert::assert($cli->statusCode === SWOOLE_HTTP_CLIENT_ESTATUS_REQUEST_TIMEOUT);\n                time_approximate(1, $s);\n            }\n            $cli->close();\n        });\n    }\n    Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Socket(AF_INET, SOCK_STREAM, 0);\n        Assert::assert($server->bind('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($server->listen());\n        $pm->wakeup();\n        while ($client = $server->accept()) {\n            go(function () use ($server, $client) {\n                Assert::assert($client instanceof Socket);\n                $data\n                    = \"HTTP/1.1 200 OK\\r\\n\"\n                    . \"Connection: keep-alive\\r\\n\"\n                    . \"Server: gunicorn/19.9.0\\r\\n\"\n                    . \"Date: Wed, 26 Dec 2018 23:56:51 GMT\\r\\n\"\n                    . \"Content-Type: text/html; charset=utf-8\\r\\n\"\n                    . \"Content-Length: 10122\\r\\n\"\n                    . \"Access-Control-Allow-Origin: *\\r\\n\"\n                    . \"Access-Control-Allow-Credentials: true\\r\\n\"\n                    . \"Via: 1.1 vegur\\r\\n\"\n                    . \"\\r\\n\";\n                for ($n = 0; $n < strlen($data); $n++) {\n                    var_dump_return(\"send {$n}\\n\");\n                    $client->send($data[$n]);\n                    usleep(mt_rand(10, 800) * 1000);\n                }\n            });\n        }\n    });\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/recv_timeout.phpt",
    "content": "--TEST--\nswoole_http_client_coro: recv timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$GLOBALS['socket'] = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n$GLOBALS['socket']->bind('127.0.0.1');\n$GLOBALS['socket']->listen();\n$GLOBALS['port'] = (int)$GLOBALS['socket']->getsockname()['port'];\ngo(function () {\n    for ($c = MAX_CONCURRENCY_MID; $c--;) {\n        $conn = $GLOBALS['socket']->accept();\n        Assert::assert($conn instanceof Co\\Socket);\n        $GLOBALS['connections'][] = $conn;\n    }\n});\nfor ($c = MAX_CONCURRENCY_MID; $c--;) {\n    go(function () {\n        $cli = new Co\\Http\\Client('127.0.0.1', $GLOBALS['port']);\n        $cli->setDefer();\n        $config_timeout = mt_rand(100, 500) / 1000;\n        $cli->set(['timeout' => $config_timeout]);\n        Assert::assert($cli->get('/'));\n        $arg_timeout = mt_rand(100, 500) / 1000;\n        $s = microtime(true);\n        if (mt_rand(0, 1)) {\n            $ret = $cli->recv();\n            time_approximate($config_timeout, microtime(true) - $s);\n        } else {\n            $ret = $cli->recv($arg_timeout);\n            time_approximate($arg_timeout, microtime(true) - $s);\n        }\n        Assert::assert(!$ret);\n        Assert::assert($cli->errCode === SOCKET_ETIMEDOUT);\n    });\n}\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/set_basic_auth.phpt",
    "content": "--TEST--\nswoole_http_client_coro: http client set basic auth\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $cli = new Swoole\\Coroutine\\Http\\Client(HTTPBIN_SERVER_HOST, HTTPBIN_SERVER_PORT);\n    $cli->set(['timeout' => 10]);\n    $cli->setHeaders([\n        'host' => 'httpbin.org',\n        'User-Agent' => 'Chrome/49.0.2587.3',\n        'Accept' => 'text/html,application/xhtml+xml,application/xml',\n        'Accept-Encoding' => 'gzip',\n    ]);\n    $username = get_safe_random();\n    $password = get_safe_random();\n    $cli->setBasicAuth($username, $password);\n    $ret = $cli->get(\"/basic-auth/{$username}/{$password}\");\n    if ($ret && !empty($cli->statusCode === 200)) {\n        echo \"OK\\n\";\n    } else {\n        echo \"ERROR\\n\";\n    }\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_client_coro/slow_server.phpt",
    "content": "--TEST--\nswoole_http_client_coro: slow server\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine as co;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    co::create(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->set([\n            'timeout' => 10\n        ]);\n        $cli->setHeaders([\n            'Connection' => 'close',\n            'Accept' => '*/*'\n        ]);\n        $ret = $cli->get('/');\n        Assert::true($ret);\n        Assert::same($cli->statusCode, 200);\n        Assert::assert(strlen($cli->body) > 1024 * 5);\n\n        Assert::same($cli->headers['server'], 'nginx');\n        Assert::same($cli->headers['x-server'], 'swoole');\n        Assert::same($cli->headers['content-type'], 'text/html');\n        Assert::eq($cli->headers['content-length'], strlen($cli->body) );\n\n        $pm->kill();\n        echo \"OK\\n\";\n    });\n    Swoole\\Event::wait();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set(array(\n        'log_file' => '/dev/null'\n    ));\n    $serv->on('WorkerStart', function (Swoole\\Server $serv)\n    {\n        /**\n         * @var $pm ProcessManager\n         */\n        global $pm;\n        $pm->wakeup();\n    });\n    $serv->on('receive', function ($serv, $fd, $threadId, $data)\n    {\n        $html = base64_encode(random_bytes(rand(1024, 65536)));\n        $len = strlen($html);\n        $data = \"HTTP/1.1 200 OK\\r\\nServer: nginx\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: $len\\r\\nX-Server: swoole\\r\\n\\r\\n$html\";\n        $chunks = str_split($data, 5);\n        foreach ($chunks as $out) {\n            $serv->send($fd, $out);\n            usleep(100);\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_client_coro/socks5_proxy.phpt",
    "content": "--TEST--\nswoole_http_client_coro: socks5 proxy\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_socks5_proxy();\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function ()\n{\n    $domain = 'www.google.com';\n    $cli = new Swoole\\Coroutine\\Http\\Client($domain, 443, true);\n\n    $cli->setHeaders(['Host' => $domain]);\n    $cli->set([\n        'timeout'     => 5,\n        'socks5_host' => SOCKS5_PROXY_HOST,\n        'socks5_port' => SOCKS5_PROXY_PORT,\n    ]);\n\n    $ret = $cli->get('/');\n    if (!$ret)\n    {\n        die(\"ERROR\\n\");\n    }\n    Assert::same($cli->statusCode, 200);\n    Assert::assert(stripos($cli->body, 'google.com') !== false);\n    $cli->close();\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/socks5_proxy_ipv6.phpt",
    "content": "--TEST--\nswoole_http_client_coro: socks5 proxy with IPv6\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_socks5_proxy();\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\System;\nuse function Swoole\\Coroutine\\run;\n\nrun(function () {\n    $domain = 'www.qq.com';\n    $ipv6 = System::gethostbyname($domain, AF_INET6);\n    $client = new Client($ipv6, 443, true);\n    $client->setHeaders([\n        'Host' => $domain,\n    ]);\n\n    $client->set([\n        'ssl_host_name' => $domain,\n        'socks5_host' => SOCKS5_PROXY_HOST,\n        'socks5_port' => SOCKS5_PROXY_PORT,\n    ]);\n\n    Assert::true($client->get('/'));\n    $json = json_decode($client->body);\n    Assert::eq($json->code, 403);\n    $client->close();\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/ssl.phpt",
    "content": "--TEST--\nswoole_http_client_coro: error handler\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $domain = 'httpbin.org';\n    $cli = new Swoole\\Coroutine\\Http\\Client($domain, 443, true);\n    $cli->set([\n        'timeout' => 10,\n        'ssl_host_name' => $domain\n    ]);\n    $random = get_safe_random(16);\n    Assert::assert($cli->get('/get?foo=' . $random));\n    Assert::assert(strpos($cli->body, $random) !== false);\n    echo \"DONE\\n\";\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/ssl_host_name.phpt",
    "content": "--TEST--\nswoole_http_client_coro: https client with ssl_host_name\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    $c = new Co\\Http\\Client('httpbin.org', 443, true);\n    $c->set([\n        'timeout' => 5,\n        'ssl_host_name' => 'httpbin.org'\n    ]);\n    $c->get('/');\n    Assert::assert(strlen($c->body) > 0);\n    Assert::same($c->statusCode, 200);\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/ssl_verify_peer_1.phpt",
    "content": "--TEST--\nswoole_http_client_coro: ssl_verify_peer [1]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_openssl_version_lower_than('1.1.0');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Coroutine\\Http\\Client;\n\n$pm = new SwooleTest\\ProcessManager;\n\nCo::set(['log_level' => SWOOLE_LOG_WARNING]);\n\ndefine('SSL_DIR', realpath(__DIR__.'/../../examples/ssl'));\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        //allow_self_signed\n        $client = new Client('127.0.0.1', $pm->getFreePort(), true);\n        $client->set([\n            'ssl_verify_peer' => true,\n            'ssl_allow_self_signed' => true,\n        ]);\n        $client->setHeaders([\n            'Host' => \"localhost\",\n            \"User-Agent\" => 'Chrome/49.0.2587.3',\n            'Accept' => 'text/html,application/xhtml+xml,application/xml',\n            'Accept-Encoding' => 'gzip',\n        ]);\n        $result = $client->get(\"/\");\n        Assert::eq($result, true);\n        Assert::eq($client->getBody(), \"OK\");\n\n        //no allow_self_signed\n        $client = new Client('127.0.0.1', $pm->getFreePort(), true);\n        $client->set([\n            'ssl_verify_peer' => true,\n            'ssl_allow_self_signed' => false,\n        ]);\n        $client->setHeaders([\n            'Host' => \"localhost\",\n            \"User-Agent\" => 'Chrome/49.0.2587.3',\n            'Accept' => 'text/html,application/xhtml+xml,application/xml',\n            'Accept-Encoding' => 'gzip',\n        ]);\n        $result = $client->get(\"/\");\n        Assert::eq($result, false);\n        Assert::eq($client->getStatusCode(), -1);\n        Assert::eq($client->errCode, SWOOLE_ERROR_SSL_VERIFY_FAILED);\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $server->set([\n        'log_file' => '/dev/null',\n        'ssl_cert_file' => SSL_DIR . '/ssl.crt',\n        'ssl_key_file' => SSL_DIR . '/ssl.key',\n    ]);\n    $server->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Request $request, Response $response) {\n        $response->end('OK');\n    });\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/ssl_verify_peer_2.phpt",
    "content": "--TEST--\nswoole_http_client_coro: ssl_verify_peer [2]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\System;\n\nCo\\Run(function () {\n    $client = new Client('www.baidu.com', 443, true);\n    $client->set([\n        'ssl_verify_peer' => true,\n        'ssl_allow_self_signed' => true,\n    ]);\n    $result = $client->get(\"/\");\n    Assert::eq($result, true);\n    Assert::eq($client->getStatusCode(), 200);\n    $info = openssl_x509_parse($client->getPeerCert());\n    Assert::contains($info['name'], 'baidu.com');\n    Assert::contains($client->getBody(), 'baidu');\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/timeout_before_connect.phpt",
    "content": "--TEST--\nswoole_http_client_coro: use timeout and timeout before connect\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_ci('foreign network dns error');\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $host = 'www.qq.com';\n    $requestHeaders = [\n        'Host' => $host\n    ];\n\n    $ip = Co::gethostbyname($host);\n    $cli1 = new Swoole\\Coroutine\\Http\\Client($ip, 443, true);\n    $cli1->setHeaders($requestHeaders);\n    $cli1->set(['timeout' => 0.001]);\n    $cli1->setDefer(true);\n    Assert::false($cli1->get('/'));\n    Assert::false($cli1->recv());\n    Assert::same($cli1->errCode, SOCKET_ETIMEDOUT);\n    Assert::same($cli1->statusCode, SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED);\n\n    $cli2 = new Swoole\\Coroutine\\Http\\Client($ip, 443, true);\n    $cli2->setHeaders($requestHeaders);\n    $cli2->setDefer(true);\n\n    Assert::false($cli1->get('/'));\n    Assert::true($cli2->get('/'));\n    Assert::false($cli1->recv());\n    Assert::true($cli2->recv());\n\n    Assert::same($cli1->errCode, SOCKET_ETIMEDOUT);\n    Assert::same($cli1->statusCode, SWOOLE_HTTP_CLIENT_ESTATUS_CONNECT_FAILED);\n\n    Assert::same($cli2->errCode, 0);\n    Assert::assert($cli2->statusCode === 200 && strpos($cli2->body, 'tencent') !== false);\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/timeout_when_recv.phpt",
    "content": "--TEST--\nswoole_http_client_coro: timeout in recv\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$port = get_one_free_port();\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm, $port) {\n    go(function () use ($pm, $port) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $port);\n        $cli->set(['timeout' => 0.1]);\n        $cli->setHeaders([\n            'hello' => 'swoole'\n        ]);\n        $cli->setDefer();\n        $cli->get('/');\n        Assert::assert(!$cli->recv());\n        Assert::assert(empty($cli->body));\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n};\n\n$pm->childFunc = function () use ($pm, $port) {\n    $serv = new Swoole\\Http\\Server('127.0.0.1', $port, SWOOLE_BASE);\n    $serv->set([\n        'log_file' => '/dev/null'\n    ]);\n    $serv->on('WorkerStart', function (Swoole\\Http\\Server $serv) {\n        global $pm;\n        $pm->wakeup();\n    });\n    $serv->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        Assert::same($request->header['hello'], 'swoole');\n        co::sleep(2);\n        $response->end('ok!');\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/unixsocket.phpt",
    "content": "--TEST--\nswoole_http_client_coro: http unix-socket\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    for ($c = MAX_CONCURRENCY; $c--;) {\n        go(function () use ($pm) {\n            $client = new Swoole\\Coroutine\\Http\\Client('unix:' . str_repeat('/', mt_rand(0, 2)) . UNIXSOCK_PATH);\n            for ($n = MAX_REQUESTS; $n--;) {\n                Assert::assert($client->get('/'), \"statusCode={$client->statusCode}, error={$client->errCode}\");\n                Assert::same($client->body, 'Hello Swoole!');\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    echo \"SUCCESS\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server(UNIXSOCK_PATH, 0, SERVER_MODE_RANDOM, SWOOLE_UNIX_STREAM);\n    $server->set(['log_file' => '/dev/null']);\n    $server->on(\\Swoole\\Constant::EVENT_START, function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end('Hello Swoole!');\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_http_client_coro/upload.phpt",
    "content": "--TEST--\nswoole_http_client_coro: upload file\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    go(function() use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->addFile(TEST_IMAGE, 'test.jpg');\n        $cli->post('/upload_file', array('name' => 'rango'));\n        Assert::same($cli->statusCode, 200);\n        $ret = json_decode($cli->body, true);\n        Assert::assert($ret and is_array($ret));\n        Assert::same(md5_file(TEST_IMAGE), $ret['md5']);\n        $cli->close();\n    });\n    Swoole\\Event::wait();\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    include __DIR__ . '/../include/api/http_server.php';\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/upload_huge.phpt",
    "content": "--TEST--\nswoole_http_client_coro: upload a big file\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_constant_not_defined('HTTPBIN_LOCALLY');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $cli = new Swoole\\Coroutine\\Http\\Client(HTTPBIN_SERVER_HOST, HTTPBIN_SERVER_PORT);\n    $cli->set(['timeout' => 10]);\n    $content = str_repeat(get_safe_random(IS_IN_CI ? 16 : 64), 1024 * 1024); // 64M\n    file_put_contents('/tmp/test.jpg', $content);\n    $cli->addFile('/tmp/test.jpg', 'test.jpg');\n    $ret = $cli->post('/post', ['name' => 'twosee']);\n    if ($ret) {\n        Assert::assert(count($cli->headers) > 0);\n        Assert::assert($cli->statusCode === 200);\n        $body = json_decode($cli->body, true);\n        Assert::assert($body['files']['test.jpg'] === $content);\n        echo \"SUCCESS\\n\";\n    }\n    $cli->close();\n    @unlink('/tmp/test.jpg');\n});\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_http_client_coro/upload_with_null_args.phpt",
    "content": "--TEST--\nswoole_http_client_coro: upload file with null args\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->addFile(TEST_IMAGE, 'test.jpg', null, null, 0, 0);\n        $cli->post('/upload_file', ['name' => 'rango']);\n        Assert::same($cli->statusCode, 200);\n        $ret = json_decode($cli->body, true);\n        Assert::assert($ret and is_array($ret));\n        Assert::same($ret['files']['test_jpg']['name'], 'test.jpg');\n        Assert::same($ret['files']['test_jpg']['type'], 'image/jpeg');\n        Assert::assert(preg_match('#/tmp/swoole\\.upfile\\.#', $ret['files']['test_jpg']['tmp_name']));\n        Assert::same($ret['files']['test_jpg']['error'], 0);\n        Assert::same($ret['files']['test_jpg']['size'], filesize(TEST_IMAGE));\n        Assert::same(md5_file(TEST_IMAGE), $ret['md5']);\n        $cli->close();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    include __DIR__ . '/../include/api/http_server.php';\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/1.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: client & server\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_offline();\nskip_if_darwin_todo();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Co\\http\\Client;\nuse Swoole\\Event;\nuse Swoole\\Http\\Request;\nuse Swoole\\WebSocket\\Server;\n\n$pm = new ProcessManager();\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Client('127.0.0.1', $pm->getFreePort());\n        $cli->set(['timeout' => -1]);\n        $cli->setHeaders([]);\n        $ret = $cli->upgrade('/');\n\n        if (!$ret) {\n            echo \"ERROR\\n\";\n            return;\n        }\n        echo $cli->recv()->data;\n        for ($i = 0; $i < 5; $i++) {\n            $cli->push('hello server');\n            echo $cli->recv()->data;\n            co::sleep(0.1);\n        }\n    });\n    Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $ws = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $ws->set([\n        'log_file' => '/dev/null',\n    ]);\n    $ws->on('WorkerStart', function (Server $serv) {\n        /*\n         * @var $pm ProcessManager\n         */\n        global $pm;\n        $pm->wakeup();\n    });\n\n    $ws->on('open', function (Server $serv, Request $request) {\n        $ip = Co::gethostbyname(TEST_DOMAIN_1);\n        if ($ip) {\n            $serv->push($request->fd, \"start\\n\");\n        } else {\n            $serv->push($request->fd, 'error: ' . swoole_last_error() . \"\\n\");\n        }\n    });\n\n    $ws->on('message', function ($serv, $frame) {\n        co::sleep(0.1);\n        $serv->push($frame->fd, \"hello client\\n\");\n    });\n\n    $ws->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nstart\nhello client\nhello client\nhello client\nhello client\nhello client\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/auto_pong.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: auto pong\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $ret = $client->upgrade('/');\n        $client->push('hello world', SWOOLE_WEBSOCKET_OPCODE_TEXT);\n        while ($client->recv()) {\n            $client->push('hello world', SWOOLE_WEBSOCKET_OPCODE_TEXT);\n        }\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n        'open_websocket_pong_frame' => true\n    ]);\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $data = [];\n    $count = 0;\n    $server->on('message', function (Server $server, Frame $frame) use ($pm, &$data, &$count) {\n        if ($count == 1000) {\n            $server->disconnect($frame->fd);\n            for ($i = 0; $i < 1000; $i++) {\n                Assert::true($data[$i]->opcode == SWOOLE_WEBSOCKET_OPCODE_PONG);\n            }\n            return;\n        }\n\n        if ($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_PONG) {\n            $count++;\n            $data[] = $frame;\n        }\n\n        $server->push($frame->fd, \"hello world\", SWOOLE_WEBSOCKET_OPCODE_TEXT);\n        $ping = new Frame();\n        $ping->data = $frame->data;\n        $ping->opcode = SWOOLE_WEBSOCKET_OPCODE_PING;\n        $server->push($frame->fd, $ping);\n    });\n\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/bug_01.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: handshake + frame\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\WebSocket\\Server as WebSockerServer;\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $cli = new Co\\http\\Client('127.0.0.1', $pm->getFreePort());\n        $ret = $cli->upgrade('/');\n        if (!$ret) {\n            echo \"ERROR\\n\";\n            return;\n        }\n        echo \"CONNECTED\\n\";\n        echo $cli->recv()->data;\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $ws = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $ws->set(array(\n        'log_file' => '/dev/null'\n    ));\n    $ws->on('WorkerStart', function (Server $serv) {\n        global $pm;\n        $pm->wakeup();\n    });\n\n    $ws->on('receive', function ($serv, $fd, $threadId, $data) {\n        $sendData = \"HTTP/1.1 101 Switching Protocols\\r\\n\";\n        $sendData .= \"Upgrade: websocket\\r\\nConnection: Upgrade\\r\\nSec-Websocket-Accept: IFpdKwYy9wdo4gTldFLHFh3xQE0=\\r\\n\";\n        $sendData .= \"Sec-Websocket-Version: 13\\r\\nServer: swoole-http-server\\r\\n\\r\\n\";\n        $sendData .= WebSockerServer::pack(\"hello world\\n\");\n        $serv->send($fd, $sendData);\n    });\n\n    $ws->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nCONNECTED\nhello world\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/bug_02.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: bug use client in server\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->set(['timeout' => -1]);\n        $ret = $cli->upgrade('/');\n        Assert::assert($ret);\n        echo $cli->recv()->data;\n        for ($i = 0; $i < 5; $i++) {\n            $cli->push(\"hello server\\n\", SWOOLE_WEBSOCKET_OPCODE_TEXT, true);\n            echo ($cli->recv(1))->data;\n            co::sleep(0.1);\n        }\n        $cli->close();\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $ws = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $ws->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 1\n    ]);\n    $ws->on('workerStart', function (Swoole\\WebSocket\\Server  $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $ws->on('open', function (Swoole\\WebSocket\\Server  $ws, Swoole\\Http\\Request $request) {\n        $ws->push($request->fd, \"server: hello, welcome\\n\");\n    });\n    $ws->on('message', function (Swoole\\WebSocket\\Server  $ws, Swoole\\WebSocket\\Frame $frame) {\n        echo \"client: {$frame->data}\";\n        $frame->data = str_replace('server', 'client', $frame->data);\n        $ws->push($frame->fd, \"server-reply: {$frame->data}\");\n    });\n    $ws->on('close', function (Swoole\\WebSocket\\Server  $ws, int $fd) {\n        echo \"client-{$fd} is closed\\n\";\n    });\n    $ws->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nserver: hello, welcome\nclient: hello server\nserver-reply: hello client\nclient: hello server\nserver-reply: hello client\nclient: hello server\nserver-reply: hello client\nclient: hello server\nserver-reply: hello client\nclient: hello server\nserver-reply: hello client\nclient-1 is closed\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/close_socket.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: close socket\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Co\\http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->set(['timeout' => -1]);\n        $cli->setHeaders([]);\n        $ret = $cli->upgrade('/');\n        if (!$ret) {\n            echo \"ERROR\\n\";\n            return;\n        }\n        Assert::assert($cli->socket->close());\n        Assert::false($cli->recv());\n        Assert::eq($cli->errCode, SWOOLE_ERROR_CLIENT_NO_CONNECTION);\n        Assert::false($cli->push('hello server'));\n        Assert::eq($cli->errCode, SWOOLE_ERROR_CLIENT_NO_CONNECTION);\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $ws = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $ws->set(array(\n        'log_file' => '/dev/null'\n    ));\n    $ws->on('WorkerStart', function (Swoole\\Server $serv) {\n        global $pm;\n        $pm->wakeup();\n    });\n    $ws->on('open', function ($serv, Swoole\\Http\\Request $request) {\n\n    });\n    $ws->on('message', function ($serv, $frame) {\n        $serv->push($frame->fd, \"hello client\\n\");\n    });\n    $ws->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/continue_frame_finish_flag.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: continue frame finish flag\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$data1 = bin2hex(random_bytes(10 * 1024));\n$data2 = bin2hex(random_bytes(20 * 2048));\n$data3 = bin2hex(random_bytes(40 * 4096));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n        $client->push($data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n        $client->push($data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $client->push($data3, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n        $frame = $client->recv();\n        Assert::true($frame->data == $data1 . $data2 . $data3);\n        Assert::eq($frame->opcode, SWOOLE_WEBSOCKET_OPCODE_TEXT);\n        Assert::eq($frame->finish, true);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $data1, $data2, $data3) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $server->set([\n        'package_max_length' => 100 * 1024 * 1024,\n    ]);\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('message', function (Server $server, Frame $frame) use ($pm, $data1, $data2, $data3) {\n        Assert::true($frame->data == $data1 . $data2 . $data3);\n        Assert::eq($frame->opcode, SWOOLE_WEBSOCKET_OPCODE_TEXT);\n        Assert::eq($frame->finish, true);\n        $server->push($frame->fd, $data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n        $server->push($frame->fd, $data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $server->push($frame->fd, $data3, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/continue_frame_finish_flag2.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: continue frame finish flag - 2\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$data1 = bin2hex(random_bytes(10 * 1024));\n$data2 = bin2hex(random_bytes(20 * 2048));\n$data3 = bin2hex(random_bytes(40 * 4096));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n        $client->push($data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n        $client->push($data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $client->push($data3, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n        $frame = $client->recv();\n        Assert::true($frame->data == $data1 . $data2 . $data3);\n        Assert::eq($frame->opcode, SWOOLE_WEBSOCKET_OPCODE_TEXT);\n        Assert::eq($frame->finish, true);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $server = new Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/', function ($request, $response) use ($data1, $data2, $data3) {\n            $response->upgrade();\n            $frame = $response->recv();\n            Assert::true($frame->data == $data1 . $data2 . $data3);\n            Assert::eq($frame->opcode, SWOOLE_WEBSOCKET_OPCODE_TEXT);\n            Assert::eq($frame->finish, true);\n            $response->push($data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n            $response->push($data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n            $response->push($data3, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/continue_frames.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: client continue frames - 1\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$data1 = bin2hex(random_bytes(10 * 1024));\n$data2 = bin2hex(random_bytes(20 * 2048));\n$data3 = bin2hex(random_bytes(40 * 4096));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n        $client->push($data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n        $client->push($data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $client->push($data3, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n        $frame = $client->recv();\n        Assert::true($frame->data == $data1 . $data2 . $data3);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $data1, $data2, $data3) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $server->set([\n        'package_max_length' => 100 * 1024 * 1024,\n    ]);\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('message', function (Server $server, Frame $frame) use ($pm, $data1, $data2, $data3) {\n        Assert::true($frame->data == $data1 . $data2 . $data3);\n        $server->push($frame->fd, $data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n        $server->push($frame->fd, $data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $server->push($frame->fd, $data3, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/continue_frames2.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: client continue frames - 2\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$data1 = bin2hex(random_bytes(10 * 1024));\n$data2 = bin2hex(random_bytes(20 * 2048));\n$data3 = bin2hex(random_bytes(40 * 4096));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n        $client->push($data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n        $client->push($data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $client->push($data3, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n        $frame = $client->recv();\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $data1, $data2, $data3) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $server->set([\n        'package_max_length' => 100 * 1024 * 1024,\n    ]);\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('message', function (Server $server, Frame $frame) use ($pm, $data1, $data2, $data3) {\n        Assert::true($frame->data == $data1 . $data2 . $data3);\n        $server->push($frame->fd, $data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n        $server->push($frame->fd, $data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $server->push($frame->fd, $data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n        $server->push($frame->fd, $data3, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n%s All fragments of a message, except for the initial frame, must use the continuation frame opcode(0).\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/continue_frames3.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: client continue frames - 3\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$data1 = bin2hex(random_bytes(10 * 1024));\n$data2 = bin2hex(random_bytes(20 * 2048));\n$data3 = bin2hex(random_bytes(40 * 4096));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n        $client->push($data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n        $client->push($data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $client->push($data3, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n        $frame = $client->recv();\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $data1, $data2, $data3) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $server->set([\n        'package_max_length' => 100 * 1024 * 1024,\n    ]);\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('message', function (Server $server, Frame $frame) use ($pm, $data1, $data2, $data3) {\n        Assert::true($frame->data == $data1 . $data2 . $data3);\n        $server->push($frame->fd, $data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n%s A continuation frame cannot stand alone and MUST be preceded by an initial frame whose opcode indicates either text or binary data.\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/continue_frames4.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: client continue frames - 4\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$data1 = bin2hex(random_bytes(10 * 1024));\n$data2 = bin2hex(random_bytes(20 * 2048));\n$data3 = bin2hex(random_bytes(40 * 4096));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $results = [];\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n        $client->push('111', SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_FIN);\n        $results[] = $client->recv();\n        Assert::true($results[0]->data == $data1 . $data2 . $data3);\n        $client->push('222', SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_FIN);\n        $results[] = $client->recv();\n        Assert::true($results[1]->data == $data3 . $data2 . $data1);\n        Assert::true($results[0]->data != $results[1]->data);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $data1, $data2, $data3) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n        'package_max_length' => 100 * 1024 * 1024,\n    ]);\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('message', function (Server $server, Frame $frame) use ($pm, $data1, $data2, $data3) {\n        if ($frame->data == '111') {\n            $server->push($frame->fd, $data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n            $server->push($frame->fd, $data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n            $server->push($frame->fd, $data3, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n        } else {\n            $server->push($frame->fd, $data3, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n            $server->push($frame->fd, $data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n            $server->push($frame->fd, $data1, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n        }\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/control_frame_compress.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: control frame can not compress\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse Swoole\\WebSocket\\CloseFrame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->set(['websocket_compression' => true]);\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n\n\t\t$closeFrame = new CloseFrame();\n        $closeFrame->opcode = SWOOLE_WEBSOCKET_OPCODE_CLOSE;\n        $closeFrame->code = SWOOLE_WEBSOCKET_CLOSE_NORMAL;\n        $closeFrame->reason = 'hahahahaha';\n        $closeFrame->flags = 0;\n        $client->push($closeFrame, SWOOLE_WEBSOCKET_FLAG_RSV1 | SWOOLE_WEBSOCKET_FLAG_COMPRESS);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $server->set([\n        'websocket_compression' => true,\n        'open_websocket_close_frame' => true,\n        'package_max_length' => 300 * 1024 * 1024,\n    ]);\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('message', function (Server $server, Frame $frame) use ($pm) {\n        Assert::true(($frame->flags & SWOOLE_WEBSOCKET_FLAG_RSV1) == 0);\n        Assert::true(($frame->flags & SWOOLE_WEBSOCKET_FLAG_COMPRESS) == 0);\n        Assert::true(($frame->flags & SWOOLE_WEBSOCKET_FLAG_FIN) == 1);\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_CLOSE);\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/control_frame_fragmented.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: Control frames must not be fragmented\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse Swoole\\WebSocket\\CloseFrame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->set([\n            'websocket_compression' => true,\n            'open_websocket_close_frame' => true,\n        ]);\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n\n\t\t$closeFrame = new CloseFrame();\n        $closeFrame->opcode = SWOOLE_WEBSOCKET_OPCODE_CLOSE;\n        $closeFrame->code = SWOOLE_WEBSOCKET_CLOSE_NORMAL;\n        $closeFrame->reason = 'hahahahaha';\n        $closeFrame->flags = 0;\n        $client->push($closeFrame, 0);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $server->set([\n        'websocket_compression' => true,\n        'open_websocket_close_frame' => true,\n        'package_max_length' => 300 * 1024 * 1024,\n    ]);\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('message', function (Server $server, Frame $frame) use ($pm) {\n        Assert::true(($frame->flags & SWOOLE_WEBSOCKET_FLAG_RSV1) == 0);\n        Assert::true(($frame->flags & SWOOLE_WEBSOCKET_FLAG_COMPRESS) == 0);\n        Assert::true(($frame->flags & SWOOLE_WEBSOCKET_FLAG_FIN) == 1);\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_CLOSE);\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/disconnect.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: test disconnect function\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $ret = $client->upgrade('/');\n        Assert::true($client->disconnect(SWOOLE_WEBSOCKET_CLOSE_NORMAL, 'close it'));\n        Assert::true($client->connected == 0);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n        'open_websocket_close_frame' => true\n    ]);\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('message', function (Server $server, Frame $frame) {\n        var_dump($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_CLOSE);\n        var_dump($frame->reason == 'close it');\n    });\n\n    $server->on('close', function (Swoole\\Server $server, int $fd, int $reactorId) {\n        var_dump($reactorId);\n    });\n\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nbool(true)\nbool(true)\nint(0)\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/open_websocket_frame.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: open websocket frame\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$data1 = bin2hex(random_bytes(10 * 1024));\n$data2 = bin2hex(random_bytes(20 * 2048));\n$data3 = bin2hex(random_bytes(40 * 4096));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->set([\n            'open_websocket_ping_frame' => true,\n            'open_websocket_pong_frame' => true,\n            'open_websocket_close_frame' => true,\n        ]);\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n        $client->push('Hello World!!!');\n        $frame = $client->recv();\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_PING);\n        $frame = $client->recv();\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_PONG);\n        $frame = $client->recv();\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_CLOSE);\n        $frame = $client->recv();\n        Assert::true($frame == '');\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $data1, $data2, $data3) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $server->set([\n        'package_max_length' => 100 * 1024 * 1024,\n    ]);\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('message', function (Server $server, Frame $frame) use ($pm, $data1, $data2, $data3) {\n        Assert::true($frame->data == 'Hello World!!!');\n        $ping = new Frame();\n        $ping->opcode = SWOOLE_WEBSOCKET_OPCODE_PING;\n        $server->push($frame->fd, $ping);\n\n        $pong = new Frame();\n        $pong->opcode = SWOOLE_WEBSOCKET_OPCODE_PONG;\n        $server->push($frame->fd, $pong);\n\n        $server->disconnect($frame->fd);\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/ping.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: test ping function\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->set(['open_websocket_pong_frame' => true]);\n        $ret = $client->upgrade('/');\n        Assert::true($client->ping('Hello World'));\n        $frame = $client->recv();\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_PONG);\n        Assert::true($frame->data == 'Hello World');\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n    ]);\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('message', function (Server $server, Frame $frame) {\n    });\n\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/priority.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: control frame priority - 1\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$data1 = bin2hex(random_bytes(10 * 1024));\n$data2 = bin2hex(random_bytes(20 * 2048));\n$data3 = bin2hex(random_bytes(40 * 4096));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->set([\n            'open_websocket_ping_frame' => true\n        ]);\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n        $client->push('Hello World!!!');\n        $frame = $client->recv();\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_PING);\n        $frame = $client->recv();\n        Assert::true($frame->data == $data1 . $data2 . $data2 . $data2 . $data2 . $data3);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $data1, $data2, $data3) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $server->set([\n        'package_max_length' => 100 * 1024 * 1024,\n    ]);\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('message', function (Server $server, Frame $frame) use ($pm, $data1, $data2, $data3) {\n        Assert::true($frame->data == 'Hello World!!!');\n        $server->push($frame->fd, $data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n\n        $ping = new Frame();\n        $ping->opcode = SWOOLE_WEBSOCKET_OPCODE_PING;\n        $ping->data = 'PING';\n        $server->push($frame->fd, $ping);\n\n        $server->push($frame->fd, $data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $server->push($frame->fd, $data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $server->push($frame->fd, $data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $server->push($frame->fd, $data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $server->push($frame->fd, $data3, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/priority1.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: control frame priority - 2\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$data1 = bin2hex(random_bytes(10 * 1024));\n$data2 = bin2hex(random_bytes(20 * 2048));\n$data3 = bin2hex(random_bytes(40 * 4096));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->set([\n            'open_websocket_close_frame' => true\n        ]);\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n        $client->push('Hello World!!!');\n        $frame = $client->recv();\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_CLOSE);\n        $frame = $client->recv();\n        Assert::true($frame == '');\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $data1, $data2, $data3) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $server->set([\n        'package_max_length' => 100 * 1024 * 1024,\n    ]);\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('message', function (Server $server, Frame $frame) use ($pm, $data1, $data2, $data3) {\n        Assert::true($frame->data == 'Hello World!!!');\n        $server->push($frame->fd, $data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n        $server->push($frame->fd, $data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $server->push($frame->fd, $data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n\n        $server->disconnect($frame->fd);\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/send_more_continue_frame.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: send more continue frames - websocket server\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$data1 = bin2hex(random_bytes(10 * 1024));\n$data2 = bin2hex(random_bytes(20 * 2048));\n$data3 = bin2hex(random_bytes(40 * 4096));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $results = [];\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->set(['websocket_compression' => true]);\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n        $client->push('111', SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_FIN);\n        $frame = $client->recv();\n        Assert::true($frame->data == $data1 . $data2 . $data2 . $data2 . $data3);\n        $frame = $client->recv();\n        Assert::true($frame->data == $data2 . $data1 . $data3);\n        $frame = $client->recv();\n        Assert::true($frame->data == $data3 . $data2 . $data1);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $data1, $data2, $data3) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n        'package_max_length' => 100 * 1024 * 1024,\n        'websocket_compression' => true\n    ]);\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('message', function (Server $server, Frame $frame) use ($pm, $data1, $data2, $data3) {\n        $context = deflate_init(ZLIB_ENCODING_RAW);\n        $server->push($frame->fd, deflate_add($context, $data1, ZLIB_SYNC_FLUSH), SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_COMPRESS | SWOOLE_WEBSOCKET_FLAG_RSV1);\n        $server->push($frame->fd, deflate_add($context, $data2, ZLIB_SYNC_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $server->push($frame->fd, deflate_add($context, $data2, ZLIB_SYNC_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $server->push($frame->fd, deflate_add($context, $data2, ZLIB_SYNC_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $server->push($frame->fd, deflate_add($context, $data3, ZLIB_FINISH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n\n        $server->push($frame->fd, deflate_add($context, $data2, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_COMPRESS | SWOOLE_WEBSOCKET_FLAG_RSV1);\n        $server->push($frame->fd, deflate_add($context, $data1, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $server->push($frame->fd, deflate_add($context, $data3, ZLIB_FINISH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n\n        $server->push($frame->fd, deflate_add($context, $data3, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_COMPRESS | SWOOLE_WEBSOCKET_FLAG_RSV1);\n        $server->push($frame->fd, deflate_add($context, $data2, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $server->push($frame->fd, deflate_add($context, $data1, ZLIB_FINISH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/send_more_continue_frame2.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: send more continue frames - coroutine websocket server\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$data1 = bin2hex(random_bytes(10 * 1024));\n$data2 = bin2hex(random_bytes(20 * 2048));\n$data3 = bin2hex(random_bytes(40 * 4096));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $results = [];\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->set(['websocket_compression' => true]);\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n        $client->push('111', SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_FIN);\n        $frame = $client->recv();\n        Assert::true($frame->data == $data1 . $data2 . $data2 . $data2 . $data3);\n        $frame = $client->recv();\n        Assert::true($frame->data == $data2 . $data1 . $data3);\n        $frame = $client->recv();\n        Assert::true($frame->data == $data3 . $data2 . $data1);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $server = new Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->set(['websocket_compression' => true]);\n        $server->handle('/', function ($request, $response) use ($data1, $data2, $data3) {\n            $response->upgrade();\n            $frame = $response->recv();\n                    $context = deflate_init(ZLIB_ENCODING_RAW);\n                    $response->push(deflate_add($context, $data1, ZLIB_SYNC_FLUSH), SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_COMPRESS | SWOOLE_WEBSOCKET_FLAG_RSV1);\n                    $response->push(deflate_add($context, $data2, ZLIB_SYNC_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n                    $response->push(deflate_add($context, $data2, ZLIB_SYNC_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n                    $response->push(deflate_add($context, $data2, ZLIB_SYNC_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n                    $response->push(deflate_add($context, $data3, ZLIB_FINISH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n\n                    $response->push(deflate_add($context, $data2, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_COMPRESS | SWOOLE_WEBSOCKET_FLAG_RSV1);\n                    $response->push(deflate_add($context, $data1, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n                    $response->push(deflate_add($context, $data3, ZLIB_FINISH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n\n                    $response->push(deflate_add($context, $data3, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_COMPRESS | SWOOLE_WEBSOCKET_FLAG_RSV1);\n                    $response->push(deflate_add($context, $data2, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n                    $response->push(deflate_add($context, $data1, ZLIB_FINISH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/server_push_first.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: websocket server push first\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse SwooleTest\\WaitRef;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->initFreePorts();\n$pm->initRandomDataArray(2, 0, true);\n\nCo\\run(function () use ($pm) {\n    $server = new Co\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), false);\n    go(function () use ($pm, $server) {\n        $server->handle(\n            '/websocket',\n            function ($request, $ws) use ($pm) {\n                $ws->upgrade();\n                $ws->push($pm->getRandomDataElement(0));\n                $ws->push($pm->getRandomDataElement(1));\n            }\n        );\n        $server->handle(\n            '/shutdown',\n            function ($request, $response) use ($server) {\n                echo \"shutdown\\n\";\n                $response->status(200);\n                $server->shutdown();\n            }\n        );\n        $server->start();\n    });\n\n    go(function () use ($pm, $server) {\n        $wr = WaitRef::create();\n        $childs = [];\n        for ($c = MAX_CONCURRENCY_LOW; $c--;) {\n            $childs[] = go(function () use ($pm, $wr) {\n                $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n                $cli->set(['timeout' => 5]);\n                $ret = $cli->upgrade('/websocket');\n                Assert::assert($ret);\n                $ret = $cli->recv();\n                Assert::same($ret->data, $pm->getRandomDataElement(0));\n                $ret = $cli->recv();\n                Assert::same($ret->data, $pm->getRandomDataElement(1));\n            });\n        }\n        WaitRef::wait($wr);\n        echo \"DONE\\n\";\n        $server->shutdown();\n    });\n});\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/ssl_1.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: ssl recv\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\nskip(\"unavailable, waiting for review\");\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nCo\\run(function ()  {\n    $cli = new Co\\http\\Client('echo.websocket.org', 443, true);\n    $ret = $cli->upgrade('/');\n\n    if (!$ret) {\n        echo \"ERROR\\n\";\n        return;\n    }\n    $n = 16;\n    while ($n--) {\n        $data = base64_encode(random_bytes(rand(1, 16*1024)));\n        $cli->push($data);\n        $frame = $cli->recv();\n        Assert::true(is_object($frame));\n        Assert::eq($frame->data, $data);\n    }\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/ssl_2.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: ssl recv [2]\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n//Co::set(['log_level' => SWOOLE_LOG_TRACE, 'trace_flags' => SWOOLE_TRACE_ALL]);\n\nCo\\run(function ()  {\n    $cli = new Co\\http\\Client('www.bitmex.com', 443, true);\n    if (($http_proxy_conf = getenv('https_proxy'))) {\n        $uri = parse_url($http_proxy_conf);\n        $cli->set([\n            'socks5_host' => $uri['host'],\n            'socks5_port' => $uri['port'],\n        ]);\n    }\n    $ret = $cli->upgrade('/realtime');\n    if (!$ret) {\n        echo \"ERROR\\n\";\n        return;\n    }\n    echo \"CONNECT SUCCESS, StatusCode={$cli->getStatusCode()}\\n\";\n    $n = 16;\n    $cli->push('{\"op\": \"subscribe\", \"args\": [\"orderBookL2_25:XBTUSD\"]}');\n    while ($n--) {\n        $frame = $cli->recv();\n        if (!$frame or empty($frame->data)) {\n            echo \"ERROR $n [2]\\n\";\n            var_dump($cli->errCode, $cli->errMsg);\n            break;\n        }\n    }\n    echo \"FINISH\\n\";\n});\n?>\n--EXPECT--\nCONNECT SUCCESS, StatusCode=101\nFINISH\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/timeout.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: websocket client & server\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Co\\http\\Client('127.0.0.1', $pm->getFreePort());\n        $ret = $cli->upgrade('/');\n\n        if (!$ret)\n        {\n            echo \"ERROR\\n\";\n            return;\n        }\n        echo $cli->recv()->data;\n        $cli->push('hello server');\n\n        Assert::false($cli->recv(.1));\n        Assert::same($cli->errCode, SOCKET_ETIMEDOUT);\n        $cli->errCode = 0;\n\n        Assert::false($cli->recv(.1));\n        Assert::same($cli->errCode, SOCKET_ETIMEDOUT);\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $ws = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $ws->set(array(\n        'log_file' => '/dev/null'\n    ));\n    $ws->on('WorkerStart', function (Swoole\\Server $serv) {\n        /**\n         * @var $pm ProcessManager\n         */\n        global $pm;\n        $pm->wakeup();\n    });\n\n    $ws->on('open', function ($serv, Swoole\\Http\\Request $request) {\n        $serv->push($request->fd, \"start\\n\");\n    });\n\n    $ws->on('message', function ($serv, $frame) {\n\n    });\n\n    $ws->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nstart\n"
  },
  {
    "path": "tests/swoole_http_client_coro/websocket/upgrade_after_get.phpt",
    "content": "--TEST--\nswoole_http_client_coro/websocket: client & server\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Coroutine\\HTTP\\Client;\n$pm = new ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\run(function () use ($pm) {   \n        $client = new Client('127.0.0.1', $pm->getFreePort(), false);\n        $client->setHeaders([\n            \"User-Agent\" => 'Chrome/49.0.2587.3',\n            'Accept' => 'text/html,application/xhtml+xml,application/xml',\n            'Accept-Encoding' => 'gzip',\n        ]);\n           \n        Assert::assert($client->get('/'));\n        echo $client->getBody();\n    \n        Assert::assert($client->upgrade('/'));\n\n        echo $client->recv(2)->data;\n        $client->push(\"hello\");\n        echo $client->recv(2)->data;\n\n        $client->close();\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $ws = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $ws->set(array(\n        'log_file' => '/dev/null'\n    ));\n    $ws->on('WorkerStart', function (Swoole\\Server $serv) {\n        /**\n         * @var $pm ProcessManager\n         */\n        global $pm;\n        $pm->wakeup();\n    });\n\n    $ws->on('open', function ($serv, Swoole\\Http\\Request $request) {\n        $serv->push($request->fd, \"msg 1\\n\");\n    });\n\n    $ws->on('message', function ($serv, $frame) {\n        co::sleep(0.1);\n        $serv->push($frame->fd, \"msg 2\\n\");\n    });\n\n    $ws->on('request', function ($req, $resp) {\n        $resp->end(\"OK\\n\");\n    });\n\n    $ws->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\nmsg 1\nmsg 2\n"
  },
  {
    "path": "tests/swoole_http_client_coro/write_func_1.phpt",
    "content": "--TEST--\nswoole_http_client_coro: write func 1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Event;\n\nconst N = 8;\n$chunks = [];\n$n = N;\nwhile ($n--) {\n    $chunks[] = base64_encode(random_bytes(random_int(256, 4096)));\n}\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm, $chunks) {\n    Co\\run(function () use ($pm, $chunks) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $index = 0;\n        $cli->set(['write_func' => function ($client, $data) use ($chunks, &$index) {\n            Assert::eq($chunks[$index], $data);\n            $index++;\n        }]);\n        Assert::assert($cli->get('/'));\n    });\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm, $chunks) {\n    Co\\run(function () use ($pm, $chunks) {\n        Event::defer(function () use ($pm) {\n            $pm->wakeup();\n        });\n        $server = new Swoole\\Coroutine\\Http\\Server('127.0.0.1', $pm->getFreePort());\n        $server->handle('/', function ($req, $resp) use ($server, $chunks) {\n            foreach ($chunks as $chunk) {\n                $resp->write($chunk);\n                usleep(mt_rand(10, 50) * 1000);\n            }\n        });\n        $server->start();\n    });\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_client_coro/write_func_2.phpt",
    "content": "--TEST--\nswoole_http_client_coro: write func 1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Event;\n\nconst N = 16;\n$chunks = [];\n$n = N;\nwhile ($n--) {\n    $chunks[] = base64_encode(random_bytes(random_int(256, 4096)));\n}\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm, $chunks) {\n    Co\\run(function () use ($pm, $chunks) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $index = 0;\n        $cli->set(['write_func' => function ($client, $data) use ($chunks, &$index) {\n            Assert::eq($chunks[$index], $data);\n            $index++;\n            if ($index == N / 2) {\n                // reset connection\n                $client->close();\n            }\n        }]);\n        Assert::false($cli->get('/'));\n        Assert::eq($cli->getStatusCode(), SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET);\n    });\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm, $chunks) {\n    Co\\run(function () use ($pm, $chunks) {\n        Event::defer(function () use ($pm) {\n            $pm->wakeup();\n        });\n        $server = new Swoole\\Coroutine\\Http\\Server('127.0.0.1', $pm->getFreePort());\n        $server->handle('/', function ($req, $resp) use ($server, $chunks) {\n            foreach ($chunks as $chunk) {\n                $resp->write($chunk);\n                usleep(mt_rand(10, 50) * 1000);\n            }\n            $resp->end();\n        });\n        $server->start();\n    });\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/0.phpt",
    "content": "--TEST--\nswoole_http_server: basic functions\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$html = base64_encode(random_bytes(rand(2048, 65536)));\n\n$pm->parentFunc = function ($pid) use ($pm, $html) {\n    go(function () use ($pm, $html) {\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        Assert::same($data, $html);\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm, $html) {\n    $serv = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('request', function ($req, $resp) use ($html) {\n        Assert::true($resp->isWritable());\n        $resp->end($html);\n        Assert::false($resp->isWritable());\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/100-continue.phpt",
    "content": "--TEST--\nswoole_http_server: 100-continue\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_function_not_exist('curl_init');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:{$pm->getFreePort()}\");\n    curl_setopt($ch, CURLOPT_HEADER, 0);\n    curl_setopt($ch, CURLOPT_POST, 1);\n    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Expect: 100-continue']);\n\n    $file = TEST_IMAGE;\n    $post_data = array('test' => str_repeat('a', 80));\n    if (function_exists(\"curl_file_create\")) {\n        $cfile = curl_file_create($file);\n        $post_data['file'] = $cfile;\n    } else {\n        $post_data['file'] = '@' . $file;\n    }\n\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);  //POST数据\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n    $res = curl_exec($ch);\n    Assert::assert(!empty($res));\n    Assert::same($res, md5_file($file));\n    curl_close($ch);\n\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n\n    $http->set([\n        'log_file' => '/dev/null',\n    ]);\n\n    $http->on(\"WorkerStart\", function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end(md5_file($request->files['file']['tmp_name']));\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/If_Modified_Since.phpt",
    "content": "--TEST--\nswoole_http_server: If-Modified-Since\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    foreach ([false, true] as $http2) {\n        Swoole\\Coroutine\\run(function () use ($pm, $http2) {\n            $data2 = file_get_contents(TEST_IMAGE);\n\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\");\n            $lastModified = $response['headers']['last-modified'];\n            Assert::same($response['statusCode'], 200);\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['headers' => ['-If-Modified-Since' => 'aaaa', 'If-Modified-Since' => $lastModified]]);\n            Assert::same($response['statusCode'], 304);\n        });\n    }\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    Assert::true(swoole_mime_type_add('moc', 'application/x-mocha'));\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'enable_static_handler' => true,\n        'document_root' => dirname(dirname(__DIR__)) . '/examples/',\n        'static_handler_locations' => ['/static', '/']\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) use ($http) {\n        $response->end('hello world');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/accept_encoding.phpt",
    "content": "--TEST--\nswoole_http_server: accept encoding type\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nfunction curl_request(string $type, string $url) {\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, $url);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HTTPHEADER, [\"Accept-Encoding: {$type}\"]);\n    curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $headerLine) use ($type) {\n        if (stripos($headerLine, 'Content-Encoding:') !== false) {\n            Assert::true(stripos($headerLine, $type) !== false);\n        }\n        return strlen($headerLine);\n    });\n    curl_exec($ch);\n    curl_close($ch);\n}\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm)\n{\n    Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n    run(function () use ($pm) {\n        $url = \"http://127.0.0.1:\".$pm->getFreePort();\n        curl_request('br', $url);\n        curl_request('gzip', $url);\n        curl_request('deflate', $url);\n        curl_request('zstd', $url);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $http->set([\n        'http_compression' => true,\n    ]);\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function (Request $request, Response $response) {\n        $response->end(co::readFile(__DIR__ . '/../../README.md'));\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/buffer_output_size.phpt",
    "content": "--TEST--\nswoole_http_server: buffer output size\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ndefine('RANDOM_CHAR', get_safe_random(1));\ndefine('OUTPUT_BUFFER_SIZE', pow(2, 12));\ndefine('HTTP_HEADER_SIZE', pow(2, 8));\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $response = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}\", ['timeout' => 0.1]);\n        Assert::same(strrpos($response, RANDOM_CHAR) + 1, OUTPUT_BUFFER_SIZE - HTTP_HEADER_SIZE);\n        Assert::throws(function () use ($pm) {\n            $response = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/full\", ['timeout' => 0.1]);\n        }, Exception::class);\n        echo file_get_contents(TEST_LOG_FILE);\n        $pm->kill();\n        echo \"DONE\\n\";\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    @unlink(TEST_LOG_FILE);\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'log_file' => TEST_LOG_FILE,\n        'http_compression' => false,\n        'output_buffer_size' => OUTPUT_BUFFER_SIZE,\n    ]);\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($server) {\n        $length = $request->server['request_uri'] === '/full' ? OUTPUT_BUFFER_SIZE + 4096 : OUTPUT_BUFFER_SIZE - HTTP_HEADER_SIZE;\n        $response->end(str_repeat(RANDOM_CHAR, $length));\n    });\n    $server->start();\n    @unlink(TEST_LOG_FILE);\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n[%s]\tWARNING\tProcessFactory::finish() (ERRNO %d): The length of data [%d] exceeds the output buffer size[%d], please use the sendfile, chunked transfer mode or adjust the output_buffer_size\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/bug_2368.phpt",
    "content": "--TEST--\nswoole_http_server: bug Github#2368\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ndefine('COOKIE', 'this is !@#Auth=Cookie「}」『』P{}!@#Auth=Cookie「}」『』P{}!@#Auth=Cookie「}」『』P{}!@#Auth=Cookie「}」『』P{}');\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        Assert::assert($cli->get('/'));\n        Assert::same($cli->statusCode, 200);\n        Assert::assert($cli->set_cookie_headers ===\n            [\n                'name=' . urlencode(COOKIE),\n            ]\n        );\n    });\n    Swoole\\Event::wait();\n    echo \"SUCCESS\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set(array(\n        'log_file' => '/dev/null',\n    ));\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->cookie('name', COOKIE);\n        $response->end();\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_http_server/bug_2444.phpt",
    "content": "--TEST--\nswoole_http_server: bug #2444\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_no_database();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/test\") . PHP_EOL;\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set(['log_file' => '/dev/null', 'hook_flags' => SWOOLE_HOOK_ALL]);\n    $server->on('start', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        switch ($request->server['request_uri']) {\n            case '/test':\n                $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n                $cli->get('/');\n                if (!Assert::assert($cli->statusCode === 200)) {\n                    _error:\n                    $response->status(500);\n                    $response->end('ERROR');\n                    return;\n                }\n                $cli->close();\n                $db = new mysqli();\n                $db->set_opt(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, 1);\n                if (!Assert::assert($db->connect(MYSQL_SERVER_HOST,\n                    MYSQL_SERVER_USER,\n                    MYSQL_SERVER_PWD,\n                    MYSQL_SERVER_DB,\n                    MYSQL_SERVER_PORT))) {\n                    goto _error;\n                }\n                if (!Assert::assert($db->query('select 1')->fetch_all()[0][0] === 1)) {\n                    goto _error;\n                }\n                $db->close();\n                break;\n        }\n        $response->end('OK');\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_server/bug_2608.phpt",
    "content": "--TEST--\nswoole_http_server: bug #2608\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Server;\nuse Swoole\\Coroutine;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    foreach ([false, true] as $http2) {\n        Coroutine\\run(function () use ($pm, $http2) {\n            $data = httpGetBody(\n                \"http://127.0.0.1:{$pm->getFreePort()}/examples/test.jpg\",\n                ['http2' => $http2]\n            );\n            Assert::assert(!empty($data));\n            Assert::assert(md5($data) === md5_file(TEST_IMAGE));\n\n            $data = httpGetBody(\n                \"http://127.0.0.1:{$pm->getFreePort()}/../../examples/test.jpg\",\n                ['http2' => $http2]\n            );\n            Assert::same($data, \"hello world\");\n        });\n    }\n    $pm->kill();\n    unlink(__DIR__ . '/examples');\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'enable_static_handler' => true,\n        'document_root' => __DIR__,\n        'static_handler_locations' => [\"/examples\",]\n    ]);\n    $http->on('workerStart', function ($serv, $wid) use ($pm) {\n        if (!file_exists(__DIR__ . '/examples')) {\n            symlink(dirname(dirname(__DIR__)) . '/examples/', __DIR__ . '/examples');\n        }\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) {\n        $response->end('hello world');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/bug_2751.phpt",
    "content": "--TEST--\nswoole_http_server: bug 2751\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Constant;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        echo httpGetStatusCode(\"http://127.0.0.1:{$pm->getFreePort()}/test™\") . PHP_EOL;\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['log_file' => '/dev/null']);\n    $http->on(Constant::EVENT_WORKER_START, function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on(Constant::EVENT_REQUEST, function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        var_dump('never here');\n        $response->end('OK');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\n400\n"
  },
  {
    "path": "tests/swoole_http_server/bug_2786.phpt",
    "content": "--TEST--\nswoole_http_server: Bug Github#2786\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:\" . $pm->getFreePort() . '/');\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_POSTFIELDS, \"[]\");\n    curl_setopt($ch, CURLOPT_POST, 1);\n    $headers = [];\n    $headers[] = 'Transfer-Encoding: chunked';\n    $headers[] = 'Content-Type: application/json;charset=UTF-8';\n    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);\n    $result = curl_exec($ch);\n    curl_close($ch);\n    echo $result . PHP_EOL;\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        Assert::same($request->server['request_method'], 'POST');\n        $response->end('OK');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_server/bug_2947.phpt",
    "content": "--TEST--\nswoole_http_server: Bug Github#2947\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->initRandomData(1);\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $headers = httpGetHeaders(\n            \"http://127.0.0.1:{$pm->getFreePort()}\",\n            [\n                'headers' => ['Accept-Encoding' => 'gzip, br'],\n                'data' => $pm->getRandomData()\n            ]\n        );\n        $encoding = $headers['content-encoding'] ?? '';\n        if (defined('SWOOLE_HAVE_BROTLI')) {\n            Assert::same($encoding, 'br');\n        } elseif (defined('SWOOLE_HAVE_ZLIB')) {\n            Assert::same($encoding, 'gzip');\n        }\n        if (defined('SWOOLE_HAVE_COMPRESSION')) {\n            phpt_var_dump($encoding);\n        }\n    });\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        Assert::same($request->server['request_method'], 'POST');\n        Assert::same($request->rawContent(), $pm->getRandomData());\n        $response->end(str_repeat('OK', 16));\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/bug_2988.phpt",
    "content": "--TEST--\nswoole_http_server: bug Github#2988\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst ILLEGAL_REQUEST = \"GET / HTTP/1.1\\r\\nAccept: gzip\\r\\n\\r\\n\";\n\n$pm = new ProcessManager;\n$pm->initRandomData(1);\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        if (Assert::true($client->connect('127.0.0.1', $pm->getFreePort()))) {\n            if (Assert::eq($client->sendAll(ILLEGAL_REQUEST), strlen(ILLEGAL_REQUEST))) {\n                $response = $client->recv();\n                phpt_var_dump($response);\n                Assert::contains($response, $pm->getRandomData());\n            }\n        }\n    });\n    echo \"SUCCESS\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set(['log_file' => '/dev/null']);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        $response->end($pm->getRandomData());\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_http_server/bug_4261.phpt",
    "content": "--TEST--\nswoole_http_server: bug Github#4261\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    $uuid = urlencode(uniqid('swoole'));\n    $port = $pm->getFreePort();\n    $out = shell_exec(\"curl -sS --location --request POST 'http://127.0.0.1:{$port}' -H 'Content-Type:multipart/form-data;charset=UTF-8' --form 'token=$uuid'\");\n    Assert::contains($out, $uuid);\n    $pm->kill();\n    echo \"SUCCESS\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['log_file' => '/dev/null']);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        $response->end(var_export($request->post, true));\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_http_server/bug_4857.phpt",
    "content": "--TEST--\nswoole_http_server: bug Github#4857  Invalid \"Transfer-Encoding: chunked\" header appended\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->initRandomData(1);\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n\n        // without special content-length\n        $headers = httpGetHeaders(\n            \"http://127.0.0.1:{$pm->getFreePort()}?encoding=1\",\n            [\n                'headers' => ['Accept-Encoding' => 'gzip, br'],\n            ]\n        );\n        var_dump($headers);\n\n        // without content-length\n        $headers = httpGetHeaders(\"http://127.0.0.1:{$pm->getFreePort()}\");\n        var_dump($headers);\n\n        // with content-length\n        $headers = httpGetHeaders(\"http://127.0.0.1:{$pm->getFreePort()}?normal=1\");\n        var_dump($headers);\n    });\n\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        $data = '宛如繁星般，宛如皎月般';\n        if (isset($request->get['normal'])) {\n            $response->header('Content-Length', mb_strlen($data));\n            $response->end($data);\n        } elseif (isset($request->get['encoding'])) {\n            $response->header('Content-Length', 1000);\n            $response->end($data);\n        } else {\n            $response->header('Content-Length', 100);\n            $response->write($data);\n            $response->end();\n        }\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n%s\narray(6) {\n  [\"server\"]=>\n  string(18) \"swoole-http-server\"\n  [\"date\"]=>\n  string(%d) %s\n  [\"connection\"]=>\n  string(10) \"keep-alive\"\n  [\"content-type\"]=>\n  string(9) \"text/html\"\n  [\"content-encoding\"]=>\n  string(%d) %s\n  [\"content-length\"]=>\n  string(%d) %s\n}\n%s\narray(5) {\n  [\"server\"]=>\n  string(18) \"swoole-http-server\"\n  [\"date\"]=>\n  string(%d) %s\n  [\"connection\"]=>\n  string(10) \"keep-alive\"\n  [\"content-type\"]=>\n  string(9) \"text/html\"\n  [\"transfer-encoding\"]=>\n  string(7) \"chunked\"\n}\n%s\narray(6) {\n  [\"server\"]=>\n  string(18) \"swoole-http-server\"\n  [\"date\"]=>\n  string(%d) %s\n  [\"connection\"]=>\n  string(10) \"keep-alive\"\n  [\"content-type\"]=>\n  string(9) \"text/html\"\n  [\"content-encoding\"]=>\n  string(%d) %s\n  [\"content-length\"]=>\n  string(%d) %s\n}\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/bug_5107.phpt",
    "content": "--TEST--\nswoole_http_server: bug Github#5107  Error response status\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->initRandomData(1);\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $headers = httpGetHeaders(\"http://127.0.0.1:{$pm->getFreePort()}\");\n        var_dump($headers);\n    });\n\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n         $response->status(200, \"status\");\n         $response->end(\"Hello World\");\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\narray(5) {\n  [\"server\"]=>\n  string(18) \"swoole-http-server\"\n  [\"date\"]=>\n  string(%d) %s\n  [\"connection\"]=>\n  string(10) \"keep-alive\"\n  [\"content-type\"]=>\n  string(9) \"text/html\"\n  [\"content-length\"]=>\n  string(2) \"11\"\n}\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/bug_5114.phpt",
    "content": "--TEST--\nswoole_http_server: bug #5114\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Swoole\\Coroutine\\run(function () use ($pm) {\n        $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/http/UPPER.TXT\");\n        Assert::same($response['statusCode'], 200);\n        Assert::same($response['headers']['content-type'], 'text/plain');\n        Assert::same($response['body'], \"HELLO WORLD!\\n\");\n\n        $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/http/test.txt\");\n        Assert::same($response['statusCode'], 200);\n        Assert::same($response['headers']['content-type'], 'text/plain');\n        Assert::same($response['body'], \"hello world!\\n\");\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'enable_static_handler' => true,\n        'document_root' => dirname(dirname(__DIR__)) . '/examples/',\n        'static_handler_locations' => ['/static', '/']\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) use ($http) {\n        $response->end('hello world');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/bug_5146.phpt",
    "content": "--TEST--\nswoole_http_server: Github#5146 HTTP服务器，添加响应cookie时，如果设置了过期时间会内存泄漏\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Swoole\\Coroutine\\run(function () use ($pm) {\n        httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}\");\n        httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}\");\n        httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}\");\n        httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}\");\n        httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}\");\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) use ($http) {\n        $previous = memory_get_usage();\n        $response->cookie(\n            'test_cookie',\n            'hello',\n            time() + (24 * 60 * 60)\n        );\n\n        global $previous;\n        global $item;\n        $current = memory_get_usage();\n        $stats = [\n            'id' => $http->getWorkerId(),\n            'item' => $item++,\n            'prev_mem' => $previous,\n            'curr_mem' => $current,\n            'diff_mem' => $current - $previous,\n        ];\n        $previous = $current;\n\n        echo json_encode($stats), PHP_EOL;\n        $response->end('test response');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n{\"id\":%d,\"item\":null,\"prev_mem\":null,\"curr_mem\":%d,\"diff_mem\":%d}\n{\"id\":%d,\"item\":%d,\"prev_mem\":%d,\"curr_mem\":%d,\"diff_mem\":%d}\n{\"id\":%d,\"item\":%d,\"prev_mem\":%d,\"curr_mem\":%d,\"diff_mem\":0}\n{\"id\":%d,\"item\":%d,\"prev_mem\":%d,\"curr_mem\":%d,\"diff_mem\":0}\n{\"id\":%d,\"item\":%d,\"prev_mem\":%d,\"curr_mem\":%d,\"diff_mem\":0}\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/bug_5186.phpt",
    "content": "--TEST--\nswoole_http_server: GitHub issue #5186\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $content = \"POST / HTTP/1.1\\r\\nHost: 127.0.0.1:{$pm->getFreePort()}\\r\\nConnection: keep-alive\\r\\nContent-Length: 44\\r\\nContent-Type: multipart/form-data; boundary=----WebKitFormBoundaryOldDnwBESVoBBtI5\\r\\nAccept-Encoding: gzip, deflate\\r\\n\\r\\n------WebKitFormBoundaryOldDnwBESVoBBtI5--\\r\\n\\r\\n\";\n        $client = new Client(SWOOLE_SOCK_TCP);\n        $client->connect('127.0.0.1', $pm->getFreePort(), 0.5);\n        $client->send($content);\n        $client->close();\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) use ($http) {\n        var_dump($request->files);\n        $response->end('Hello World');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\nNULL\n"
  },
  {
    "path": "tests/swoole_http_server/bug_6007.phpt",
    "content": "--TEST--\nswoole_http_server: Github bug #6007\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->get('/');\n        Assert::assert($cli->set_cookie_headers ===\n            [\n                'userId=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=my.web.site; HttpOnly; Partitioned',\n            ]\n        );\n    });\n    Swoole\\Event::wait();\n    echo \"SUCCESS\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $cookie = new Swoole\\Http\\Cookie();\n        $cookie->withName('userId')\n            ->withValue('') // <--\n            ->withExpires(time() - 84600)\n            ->withPath('/')\n            ->withDomain('my.web.site')\n            ->withHttpOnly(true)\n            ->withPartitioned(true);\n        $response->setCookie($cookie);\n        $response->end();\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_http_server/bug_compression_level.phpt",
    "content": "--TEST--\nswoole_http_server: bug http_compression_level not work\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_constant_not_defined('SWOOLE_HAVE_COMPRESSION');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst HTTP_GET_REQUEST = \"GET / HTTP/1.1\\r\\nAccept-Encoding: gzip, deflate, br\\r\\n\\r\\n\";\nconst MIN_COMPRESSION_LEVEL = 0;\nconst MAX_COMPRESSION_LEVEL = 9;\n\n$randomBytes = str_repeat(get_safe_random(256), 1024);\n$contentLengthArray = [];\n\nfor ($level = MIN_COMPRESSION_LEVEL; $level <= MAX_COMPRESSION_LEVEL; $level++) {\n    $pm = new ProcessManager;\n    $pm->parentFunc = function () use ($pm) {\n        Co\\run(function () use ($pm) {\n            $client = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n            if (Assert::true($client->connect('127.0.0.1', $pm->getFreePort()))) {\n                if (Assert::eq($client->sendAll(HTTP_GET_REQUEST), strlen(HTTP_GET_REQUEST))) {\n                    $response = $client->recv();\n                    if (Assert::greaterThan(preg_match('/Content-Length: (\\d+)/', $response, $match), 0)) {\n                        global $contentLengthArray;\n                        $contentLengthArray[] = intval($match[1]);\n                    }\n                }\n            }\n            $client->close();\n        });\n        $pm->kill();\n    };\n    $pm->childFunc = function () use ($pm, $level, $randomBytes) {\n        phpt_var_dump($level);\n        $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n        $http->set([\n            'log_file' => '/dev/null',\n            'http_compression' => true,\n            'http_compression_level' => $level\n        ]);\n        $http->on('workerStart', function () use ($pm) {\n            $pm->wakeup();\n        });\n        $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($http, $randomBytes) {\n            $response->end($randomBytes);\n        });\n        $http->start();\n    };\n    $pm->childFirst();\n    $pm->run();\n}\n$sortedContentLengthArray = $contentLengthArray;\nrsort($sortedContentLengthArray);\nphpt_var_dump($contentLengthArray);\nphpt_var_dump($sortedContentLengthArray);\nif (!Assert::same($sortedContentLengthArray, $contentLengthArray)) {\n    var_dump($contentLengthArray);\n    var_dump($sortedContentLengthArray);\n}\necho \"DONE\\n\";\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/bug_get_request_data_after_end.phpt",
    "content": "--TEST--\nswoole_http_server: bug get request data after response end\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->initRandomData(1);\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\", ['data' => $pm->getRandomData()]) . PHP_EOL;\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['log_file' => '/dev/null']);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end('OK');\n        switch_process();\n        Assert::notEmpty($request->rawContent());\n        Assert::notEmpty($request->getData());\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_server/callback_new_obj_method.phpt",
    "content": "--TEST--\nswoole_http_server: http server callback use new object method\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    for ($i = MAX_CONCURRENCY_LOW; $i--;) {\n        go(function () use ($pm) {\n            $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            for ($i = MAX_REQUESTS_LOW; $i--;) {\n                Assert::assert($cli->get('/'));\n                Assert::same($cli->statusCode, 200);\n                Assert::same($cli->body, 'Hello Swoole!');\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n\n    class TestCo\n    {\n        public function foo(Swoole\\Http\\Request $request, Swoole\\Http\\Response $response)\n        {\n            co::sleep(0.001);\n            $cid = go(function () use ($response) {\n                co::yield();\n                $response->end('Hello Swoole!');\n            });\n            co::resume($cid);\n            echo @$this->test;\n        }\n    }\n\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $http->on('request', [new TestCo, 'foo']);\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/callback_new_static_method.phpt",
    "content": "--TEST--\nswoole_http_server: http server callback use static method\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    for ($i = MAX_CONCURRENCY_LOW; $i--;) {\n        go(function () use ($pm) {\n            $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            for ($i = MAX_REQUESTS_LOW; $i--;) {\n                Assert::assert($cli->get('/'));\n                Assert::same($cli->statusCode, 200);\n                Assert::same($cli->body, 'Hello Swoole!');\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n\n    class TestCo\n    {\n        private static $test = '';\n\n        public static function foo(Swoole\\Http\\Request $request, Swoole\\Http\\Response $response)\n        {\n            co::sleep(0.001);\n            $cid = go(function () use ($response) {\n                co::yield();\n                $response->end('Hello Swoole!');\n            });\n            co::resume($cid);\n            echo self::$test;\n        }\n    }\n\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $http->on('request', [TestCo::class, 'foo']);\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/callback_string.phpt",
    "content": "--TEST--\nswoole_http_server: http server callback use function by string\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    for ($i = MAX_CONCURRENCY_LOW; $i--;) {\n        go(function () use ($pm) {\n            $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            for ($i = MAX_REQUESTS_LOW; $i--;) {\n                Assert::assert($cli->get('/'));\n                Assert::same($cli->statusCode, 200);\n                Assert::same($cli->body, 'Hello Swoole!');\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n\n    function foo(Swoole\\Http\\Request $request, Swoole\\Http\\Response $response)\n    {\n        static $test = '';\n        co::sleep(0.001);\n        $cid = go(function () use ($response) {\n            co::yield();\n            $response->end('Hello Swoole!');\n        });\n        co::resume($cid);\n        echo $test;\n    }\n\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $http->on('request', 'foo');\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/callback_with_internal_function.phpt",
    "content": "--TEST--\nswoole_http_server: http server callback use new object method\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $http->on('request', 'var_dump');\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nobject(Swoole\\Http\\Request)#%d (%d) {\n  [\"fd\"]=>\n  int(1)\n  %A\"header\"]=>\n  array(3) {\n    [\"host\"]=>\n    string(%d) \"%s\"\n    [\"connection\"]=>\n    string(10) \"keep-alive\"\n    [\"accept-encoding\"]=>\n    string(%d) \"%s\"\n  }\n  [\"server\"]=>\n  array(11) {\n    [\"request_method\"]=>\n    string(3) \"GET\"\n    [\"request_uri\"]=>\n    string(1) \"/\"\n    [\"path_info\"]=>\n    string(1) \"/\"\n    [\"request_time\"]=>\n    int(%d)\n    [\"request_time_float\"]=>\n    float(%f)\n    [\"server_protocol\"]=>\n    string(8) \"HTTP/1.1\"\n    [\"server_port\"]=>\n    int(%d)\n    [\"remote_port\"]=>\n    int(%d)\n    [\"server_addr\"]=>\n    string(9) \"127.0.0.1\"\n    [\"remote_addr\"]=>\n    string(9) \"127.0.0.1\"\n    [\"master_time\"]=>\n    int(%d)\n  }\n  [\"cookie\"]=>\n  NULL\n  [\"get\"]=>\n  NULL\n  [\"files\"]=>\n  NULL\n  [\"post\"]=>\n  NULL\n  [\"tmpfiles\"]=>\n  NULL\n}\nobject(Swoole\\Http\\Response)#%d (%d) {\n  [\"fd\"]=>\n  int(1)\n  [\"socket\"]=>\n  NULL\n  [\"header\"]=>\n  NULL\n  [\"cookie\"]=>\n  NULL\n  [\"trailer\"]=>\n  NULL\n}\n"
  },
  {
    "path": "tests/swoole_http_server/callback_with_private.phpt",
    "content": "--TEST--\nswoole_http_server: http server with private callback\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->setWaitTimeout(0);\n$pm->parentFunc = function () {\n};\n$pm->childFunc = function () use ($pm) {\n\n    class TestCo_9\n    {\n        private function foo(Swoole\\Http\\Request $request, Swoole\\Http\\Response $response)\n        {\n            co::sleep(0.001);\n            $cid = go(function () use ($response) {\n                co::yield();\n                $response->end('Hello Swoole!');\n            });\n            co::resume($cid);\n            echo @$this->test;\n        }\n    }\n\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $http->on('request', [new TestCo_9, 'foo']);\n    $http->start();\n};\n$pm->childFirst();\n$pm->run(true);\n//Fatal Error\n$pm->expectExitCode(255);\n$output = $pm->getChildOutput();\nAssert::contains($output, \"Swoole\\Server\\Port::on(): function 'TestCo_9::foo' is not callable\");\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/callback_with_protected.phpt",
    "content": "--TEST--\nswoole_http_server: http server with protected callback\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse SwooleTest\\ProcessManager;\n\n$pm = ProcessManager::exec(function ($pm) {\n    class TestCo\n    {\n        protected function foo(Swoole\\Http\\Request $request, Swoole\\Http\\Response $response)\n        {\n            co::sleep(0.001);\n            $cid = go(function () use ($response) {\n                co::yield();\n                $response->end('Hello Swoole!');\n            });\n            co::resume($cid);\n            echo @$this->test;\n        }\n    }\n\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $http->on('request', [new TestCo, 'foo']);\n    $http->start();\n});\n//Fatal Error\n$pm->expectExitCode(255);\n$output = $pm->getChildOutput();\nAssert::contains($output, 'Swoole\\Server\\Port::on(): function \\'TestCo::foo\\' is not callable');\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/chunk.phpt",
    "content": "--TEST--\nswoole_http_server: http chunk\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        Assert::assert(!empty($data));\n        Assert::eq(md5($data), md5_file(TEST_IMAGE));\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n\n    $http->set([\n        //'log_file' => '/dev/null',\n    ]);\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $data = str_split(file_get_contents(TEST_IMAGE), 8192);\n        foreach ($data as $chunk) {\n            Assert::true($response->write($chunk));\n        }\n        Assert::true($response->end());\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/chunk_with_end_data.phpt",
    "content": "--TEST--\nswoole_http_server: send data in the end method with chunked encoding\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        Assert::assert(!empty($data));\n        Assert::eq(md5($data), md5_file(TEST_IMAGE));\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n\n    $http->set([\n        //'log_file' => '/dev/null',\n    ]);\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $data = str_split(file_get_contents(TEST_IMAGE), 8192);\n        foreach ($data as $k => $chunk) {\n            if ($k == count($data) - 1) {\n                break;\n            }\n            $response->write($chunk);\n        }\n        $response->end($data[count($data) - 1]);\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/chunked_pipeline_request.phpt",
    "content": "--TEST--\nswoole_http_server: chunked and pipeline request\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/../include/api/http_test_cases.php';\n\nconst EOF = \"EOF\";\n\n$pm = new ProcessManager;\n$pm->initRandomData(1);\n$pm->parentFunc = function () use ($pm) {\n    chunked_request($pm);\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set([\n        'log_file' => '/dev/null',\n        // 'log_level' => SWOOLE_LOG_DEBUG,\n        // 'trace_flags' => SWOOLE_TRACE_ALL,\n        'http_compression' => false,\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        $response->end($request->rawContent() . EOF);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_http_server/client_ca.phpt",
    "content": "--TEST--\nswoole_http_server: ssl client ca\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$html = base64_encode(random_bytes(rand(2048, 65536)));\n\n$pm->parentFunc = function ($pid) use ($pm, $html) {\n    go(function () use ($pm, $html) {\n        $commnd = \"curl https://127.0.0.1:\" . $pm->getFreePort() . \" --cert \" . SSL_FILE_DIR. \"/client.crt\" .\n        \" --key \". SSL_FILE_DIR. \"/client.key -k -vvv --stderr /tmp/client_ca.txt\";\n        $out = shell_exec($commnd);\n        Assert::eq($out, $html);\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm, $html) {\n    $serv = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $serv->set([\n        'log_file' => '/dev/null',\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n        'ssl_verify_peer' => true,\n        'ssl_verify_depth' => 10,\n        'ssl_cafile' => SSL_FILE_DIR . '/ca.crt',\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('request', function ($req, $resp) use ($html) {\n        $resp->end($html);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/client_compress.phpt",
    "content": "--TEST--\nswoole_http_server: compress response by http client\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $data = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}\", ['headers' => ['accept-encoding' => 'br']]);\n        $headers = $data['headers'];\n        Assert::assert(!empty($headers));\n        Assert::eq($headers['content-encoding'], 'gzip');\n        Assert::eq($data['body'], file_get_contents(TEST_IMAGE));\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->on(\"WorkerStart\", function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function ($request, $response) {\n        $gzipEncoded = gzencode(file_get_contents(TEST_IMAGE), 9, FORCE_GZIP);\n        $response->setHeader('Content-Encoding', 'gzip');\n        $response->end($gzipEncoded);\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/co_switching.phpt",
    "content": "--TEST--\nswoole_http_server: co switching\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    for ($i = MAX_CONCURRENCY; $i--;) {\n        go(function () use ($pm) {\n            $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            for ($i = MAX_REQUESTS; $i--;) {\n                Assert::assert($cli->get('/'));\n                Assert::same($cli->statusCode, 200);\n                Assert::same($cli->body, 'Hello Swoole!');\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'worker_num' => swoole_cpu_num()\n    ]);\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($http) {\n        go(function () {\n            for ($i = 5; $i--;) {\n                co::sleep(0.001);\n            }\n        });\n        $response->end('Hello Swoole!');\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/compression.phpt",
    "content": "--TEST--\nswoole_http_server: http_compression\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm)\n{\n    go(function () use ($pm) {\n        $data =  httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        Assert::assert(md5_file(__DIR__ . '/../../README.md') == md5($data));\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n\n    $http->set([\n        'http_gzip_level' => 9,\n        'http_compression' => true,\n    ]);\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function ($request, Swoole\\Http\\Response $response) {\n        $response->end(co::readFile(__DIR__ . '/../../README.md'));\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/compression_min_length.phpt",
    "content": "--TEST--\nswoole_http_server: compression_min_length\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse function Swoole\\Coroutine\\run;\nuse Swoole\\Coroutine\\Http\\Client;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm)\n{\n    run(function () use ($pm) {\n        $cli = new Client('127.0.0.1', $pm->getFreePort());\n        $cli->setHeaders(['Accept-Encoding' => 'gzip', ]);\n        $cli->get('/?bytes=128');\n        Assert::eq($cli->getHeaders()['content-encoding'], 'gzip');\n\n        $cli = new Client('127.0.0.1', $pm->getFreePort());\n        $cli->setHeaders(['Accept-Encoding' => 'gzip', ]);\n        $cli->get('/?bytes=127');\n        Assert::assert(!isset($cli->getHeaders()['content-encoding']));\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm)\n{\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $http->set(['compression_min_length' => 128,]);\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function ($request, Swoole\\Http\\Response $response) {\n        $response->end(str_repeat('A', $request->get['bytes']));\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/compression_types.phpt",
    "content": "--TEST--\nswoole_http_server: compression types\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/../include/api/http_test_cases.php';\n\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Server;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    http_compression_types_test($pm);\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $http->set([\n        'http_compression_types' => [\n            'text/html',\n            'application/json'\n        ],\n    ]);\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function (Request $request, Response $response) {\n        if ($request->server['request_uri'] == '/html') {\n            $response->end(str_repeat('A', $request->get['bytes']));\n        } elseif ($request->server['request_uri'] == '/json') {\n            $response->setHeader('Content-Type', 'application/json');\n            $response->end(str_repeat('B', $request->get['bytes']));\n        } elseif ($request->server['request_uri'] == '/raw') {\n            $response->setHeader('Content-Type', 'text/raw');\n            $response->end(str_repeat('C', $request->get['bytes']));\n        }\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/cookieAlias.phpt",
    "content": "--TEST--\nswoole_http_server: cookie alias\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\Run(function () use ($pm) {\n        var_dump(httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}\")['set_cookie_headers']);\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set(['log_file' => '/dev/null']);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        $cookie = new Swoole\\Http\\Cookie();\n        $cookie->withName('key1')\n            ->withValue('val1')\n            ->withExpires(time() + 84600)\n            ->withPath('/')\n            ->withDomain('id.test.com')\n            ->withSecure(true)\n            ->withHttpOnly(true)\n            ->withSameSite('None')\n            ->withPriority('High')\n            ->withPartitioned(true);\n        $response->setCookie($cookie);\n        $response->setCookie('key1', 'val1', time() + 84600, '/', 'id.test.com', true, true, 'None', 'High', true);\n        $response->setRawCookie('key1', 'val1', time() + 84600, '/', 'id.test.com', true, true, 'None', 'High', true);\n\n        $cookie->withValue('');\n        $response->setCookie($cookie);\n        $response->end(\"<h1>Hello Swoole. #\" . rand(1000, 9999) . \"</h1>\");\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\narray(4) {\n  [0]=>\n  string(152) \"key1=val1; expires=%s; Max-Age=84600; path=/; domain=id.test.com; secure; HttpOnly; SameSite=None; Priority=High; Partitioned\"\n  [1]=>\n  string(152) \"key1=val1; expires=%s; path=/; domain=id.test.com; secure; HttpOnly; SameSite=None; Priority=High; Partitioned\"\n  [2]=>\n  string(152) \"key1=val1; expires=%s; Max-Age=84600; path=/; domain=id.test.com; secure; HttpOnly; SameSite=None; Priority=High; Partitioned\"\n  [3]=>\n  string(151) \"key1=deleted; expires=%s; Max-Age=0; path=/; domain=id.test.com; secure; HttpOnly; SameSite=None; Priority=High; Partitioned\"\n}\n"
  },
  {
    "path": "tests/swoole_http_server/cookie_delete.phpt",
    "content": "--TEST--\nswoole_http_server: delete cookie (same behavior with php-fpm)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cookie = '123_,; abc';\n        Assert::assert($cli->get('/?cookie=' . urlencode($cookie)));\n        Assert::same($cli->statusCode, 200);\n        Assert::assert($cli->set_cookie_headers ===\n            [\n                'cookie1=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0',\n                'cookie2=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0',\n                'cookie3=cookie3',\n                'cookie4=cookie4',\n                'cookie5=cookie5; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0',\n                'cookie6=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0',\n                'cookie7=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0',\n                'cookie8=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0',\n            ]\n        );\n    });\n    Swoole\\Event::wait();\n    echo \"SUCCESS\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->cookie('cookie1', null);\n        $response->cookie('cookie2', '');\n        $response->cookie('cookie3', 'cookie3', 0); // must be > 0\n        $response->cookie('cookie4', 'cookie4', -1); // must be > 0\n        $response->cookie('cookie5', 'cookie5', 1);\n        $response->cookie('cookie6', null, 0);\n        $response->cookie('cookie7', null, -1);\n        $response->cookie('cookie8', null, 1);\n        $response->end();\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_http_server/cookie_samesite.phpt",
    "content": "--TEST--\nswoole_http_server: cookie with samesite\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->get('/');\n        Assert::assert($cli->set_cookie_headers ===\n            [\n                'a=123; SameSite=Lax',\n            ]\n        );\n    });\n    Swoole\\Event::wait();\n    echo \"SUCCESS\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->cookie('a', '123', 0, '', '', false, false, 'Lax');\n        $response->end();\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_http_server/cookie_vs_rawcookie.phpt",
    "content": "--TEST--\nswoole_http_server: cookie vs rawcookie\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cookie = '123_,; abc';\n        $cookie_encoded = urlencode($cookie);\n        Assert::assert($cli->get('/?cookie=' . $cookie_encoded));\n        Assert::same($cli->statusCode, 200);\n        Assert::eq($cli->set_cookie_headers, [\n            'cookie=' . $cookie_encoded,\n            'rawcookie=' . $cookie_encoded,\n        ]);\n    });\n    for ($i = MAX_CONCURRENCY_LOW; $i--;) {\n        go(function () use ($pm) {\n            $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            $random = get_safe_random();\n            Assert::assert($cli->get('/?cookie=' . $random));\n            Assert::same($cli->statusCode, 200);\n            Assert::assert($cli->set_cookie_headers ===\n                [\n                    'cookie=' . urlencode($random),\n                    'rawcookie=' . $random\n                ]\n            );\n        });\n    }\n    Swoole\\Event::wait();\n    echo \"SUCCESS\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $cookie = $request->get['cookie'];\n        $response->cookie('cookie', $cookie);\n        $response->rawcookie('rawcookie', urlencode($cookie));\n        $response->end();\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_http_server/cookies.phpt",
    "content": "--TEST--\nswoole_http_server: cookies\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$cookies = array (\n    '8MLP_5753_saltkey' => 'RSU8HYED',\n    '8MLP_5753_lastvisit' => '1426120671',\n    'attentiondomain' => '2z.cn,chinaz.com,kuaishang.cn,cxpcms.com',\n    '8MLP_5753_security_cookiereport' => 'c014Hgufskpv55xgM9UaB/ZZdMrcN0QqBYdcGomTu8OlTDWzTA0z',\n    '8MLP_5753_ulastactivity' => 'e4a1aRIbgdzoRDd8NlT5CMIwLnWjyjr2hWyfn6T5g82RitUOdf3o',\n    'mytool_user' => 'uSHVgCUFWf5Sv2Y8tKytQRUJW3wMVT3rw5xQLNGQFIsod4C6vYWeGA==',\n    'PHPSESSID' => 't3hp9h4o8rb3956t5pajnsfab1',\n    '8MLP_5753_st_p' => '1024432|1428040399|f7599ba9053aa27e12e9e597a4c372ce',\n    '8MLP_5753_st_t' => '1024432|1428040402|46d40e02d899b10b431822eb1d39f6a1',\n    '8MLP_5753_forum_lastvisit' => 'D_140_1427103032D_165_1427427405D_168_1427870172D_167_1427870173D_166_1428021390D_163_1428040402',\n    '8MLP_5753_sid' => 'k25gxK',\n    'cmstop_page-view-mode' => 'view',\n    'cmstop_rememberusername' => 'error',\n    'cmstop_auth' => 'Jcn2qzVn9nsjqtodER9OphcW3PURDWNx6mO7j0Zbb9k=',\n    'cmstop_username' => 'error',\n    'Hm_lvt_aecc9715b0f5d5f7f34fba48a3c511d6' => '1427967317,1428021376,1428036617,1428040224',\n    'Hm_lpvt_aecc9715b0f5d5f7f34fba48a3c511d6' => '1428050417',\n    'YjVmNm_timeout' => '0',\n);\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm, $cookies) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort(), 1))\n    {\n        exit(\"connect failed. Error: {$client->errCode}\\n\");\n    }\n    $header = \"GET /index.php HTTP/1.1\\r\\n\";\n    $header .= \"Host: 127.0.0.1\\r\\n\";\n    $header .= \"Connection: keep-alive\\r\\n\";\n    $header .= \"Cache-Control: max-age=0\\r\\n\";\n\n    $cookieStr = '';\n    foreach($cookies as $k => $v)\n    {\n        $cookieStr .= \"$k=$v; \";\n    }\n    $cookieStr .= \"end=1\";\n    $cookies['end'] = \"1\";\n\n    $header .= \"Cookie: $cookieStr\\r\\n\";\n    $header .= \"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\\r\\n\";\n    $header .= \"User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36\\r\\n\";\n    $header .= \"\\r\\n\";\n    $_sendStr = $header;\n\n    $client->send($_sendStr);\n    $data = $client->recv();\n    $client->close();\n\n    list(, $_respCookieStr) = explode(\"\\r\\n\\r\\n\", $data);\n\n    $respCookie = json_decode($_respCookieStr, true);\n    Assert::same($respCookie, $cookies);\n\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n\n    $http->set(['log_file' => '/dev/null']);\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end(json_encode($request->cookie));\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/cookies_parse.phpt",
    "content": "--TEST--\nswoole_http_server: cookies parse\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse SwooleTest\\ProcessManager;\nuse Swoole\\Coroutine\\Http\\Client;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        $client = new Client('127.0.0.1', $pm->getFreePort(), false);\n        $client->setHeaders([\n            'Cookie' => 'PHPSESSID=5359a08f4ddbf825f0e99a3393e5dc9e; HttpOnly; q=URVVma5UgEDm9RmQvBfXs7rCEG9hs9td9CXXmBRQ'\n        ]);\n        $client->get('/');\n        $client->close();\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->on(\"Start\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('request', function ($request, $response) use ($serv){\n        var_dump($request->cookie);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\narray(3) {\n  [\"PHPSESSID\"]=>\n  string(32) \"5359a08f4ddbf825f0e99a3393e5dc9e\"\n  [\"HttpOnly\"]=>\n  string(0) \"\"\n  [\"q\"]=>\n  string(40) \"URVVma5UgEDm9RmQvBfXs7rCEG9hs9td9CXXmBRQ\"\n}\n"
  },
  {
    "path": "tests/swoole_http_server/create_request.phpt",
    "content": "--TEST--\nswoole_http_server: parse request\n--SKIPIF--\n<?php\n\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\n\n$data = \"GET /index.html?hello=world&test=2123 HTTP/1.1\\r\\n\";\n$data .= \"Host: 127.0.0.1\\r\\n\";\n$data .= \"Connection: keep-alive\\r\\n\";\n$data .= \"Pragma: no-cache\\r\\n\";\n$data .= \"Cache-Control: no-cache\\r\\n\";\n$data .= \"Upgrade-Insecure-Requests: \\r\\n\";\n$data .= \"User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36\\r\\n\";\n$data .= \"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\\r\\n\";\n$data .= \"Accept-Encoding: gzip, deflate, br\\r\\n\";\n$data .= \"Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7,ja;q=0.6\\r\\n\";\n$data .= \"Cookie: env=pretest; phpsessid=fcccs2af8673a2f343a61a96551c8523d79ea; username=hantianfeng\\r\\n\";\n\n$req = Request::create();\nAssert::count($req->header, 0);\nAssert::false($req->isCompleted());\n\n$data1 = substr($data, 0, rand(100, 600));\n$data2 = substr($data, strlen($data1));\n\nAssert::eq($req->parse($data1), strlen($data1));\nAssert::false($req->isCompleted());\nAssert::eq($req->parse($data2), strlen($data2));\nAssert::false($req->isCompleted());\nAssert::eq($req->parse(\"\\r\\n\"), 2);\n\nAssert::true($req->isCompleted());\nAssert::false($req->parse('error data'));\n\nAssert::eq(\"GET\", $req->getMethod());\n\nAssert::greaterThan(count($req->header), 4);\nAssert::eq(count($req->cookie), 3);\n\nAssert::eq($req->getData(), $data.\"\\r\\n\");\n\n$req2 = Request::create(['parse_cookie' => false]);\nAssert::eq($req2->parse($data . \"\\r\\n\"), strlen($data) + 2);\nAssert::null($req2->cookie);\n\n$data = \"POST /index.html?hello=world&test=2123 HTTP/1.1\\r\\n\";\n$data .= \"Host: 127.0.0.1\\r\\n\";\n$data .= \"Connection: keep-alive\\r\\n\";\n$data .= \"Pragma: no-cache\\r\\n\";\n$data .= \"Cache-Control: no-cache\\r\\n\";\n$data .= \"Upgrade-Insecure-Requests: \\r\\n\";\n$data .= \"User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36\\r\\n\";\n$data .= \"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\\r\\n\";\n$data .= \"Accept-Encoding: gzip, deflate, br\\r\\n\";\n$data .= \"Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7,ja;q=0.6\\r\\n\";\n$data .= \"Cookie: env=pretest; phpsessid=fcccs2af8673a2f343a61a96551c8523d79ea; username=hantianfeng\\r\\n\";\n\n$req3 = Request::create();\n$req3->parse($data);\nAssert::eq(\"POST\", $req3->getMethod());\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/data_parse.phpt",
    "content": "--TEST--\nswoole_http_server: http server data parse test\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nfunction getRandomData(int $num): array\n{\n    $data = [];\n    foreach (range(1, $num) as $_) {\n        $key = substr(get_safe_random(32), 0, mt_rand(1, 32));\n        $value = substr(get_safe_random(64), 0, mt_rand(0, 64));\n        $data[$key] = $value;\n    }\n\n    return $data;\n}\n\nfunction arrayToMultipartString(array $var, string $boundary): string\n{\n    $ret = '';\n    foreach ($var as $name => $value) {\n        $value = (string)($value);\n        $ret .= \"--{$boundary}\\r\\nContent-Disposition: form-data; name=\\\"{$name}\\\"\\r\\n\\r\\n{$value}\\r\\n\";\n    }\n    $ret .= \"--{$boundary}--\\r\\n\";\n\n    return $ret;\n}\n\nfunction sendData(string $host, int $port, array $get, array $post): string\n{\n    $client = new Co\\Client(SWOOLE_SOCK_TCP);\n    if (!$client->connect($host, $port, 1)) {\n        exit(\"connect failed. Error: {$client->errCode}\\n\");\n    }\n\n    $get = http_build_query($get);\n    $content_type = '';\n    switch (mt_rand(0, 1)) {\n        case 0:\n            {\n                $content_type = 'application/x-www-form-urlencoded';\n                $post = http_build_query($post);\n                break;\n            }\n        case 1:\n            {\n                $boundary = '++++' . md5(get_safe_random(16));\n                $content_type = \"multipart/form-data; boundary={$boundary}\";\n                $post = arrayToMultipartString($post, $boundary);\n                break;\n            }\n        case 2:\n            {\n                $content_type = 'application/json';\n                $post = json_encode($post);\n                break;\n            }\n    }\n\n    $content_length = strlen($post);\n    $CR = \"\\r\";\n    $data = <<<HTTP\nPOST /?{$get} HTTP/1.1{$CR}\nHost: {$host}{$CR}\nConnection: closed{$CR}\nAccept: */*{$CR}\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36{$CR}\nContent-Type: {$content_type}{$CR}\nContent-Length: {$content_length}{$CR}\n{$CR}\n{$post}\nHTTP;\n\n    Assert::assert($client->send($data));\n    $data = '';\n    while ($tmp = $client->recv()) {\n        $data .= $tmp;\n    }\n    return $data;\n}\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    for ($c = MAX_CONCURRENCY; $c--;) {\n        go(function () use ($pm) {\n            $get = getRandomData(50);\n            $post = getRandomData(100);\n            $ret = sendData('127.0.0.1', $pm->getFreePort(), $get, $post);\n            list($_, $body) = explode(\"\\r\\n\\r\\n\", $ret);\n            Assert::same($body, var_dump_return($get, $post));\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['log_file' => '/dev/null']);\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($http) {\n        $response->end(var_dump_return($request->get, $request->post));\n        $http->close($request->fd);\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/disable_compression.phpt",
    "content": "--TEST--\nswoole_http_server: disable compression\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        Assert::true($client->get('/'));\n        Assert::eq(md5_file(__DIR__ . '/../../README.md'), md5($client->getBody()));\n        Assert::keyNotExists($client->headers, 'content-encoding');\n        $pm->kill();\n    });\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function ($request, Swoole\\Http\\Response $response) {\n        // Set Content-Encoding header to empty to disable compression\n        $response->header('Content-Encoding', '');\n        $response->end(co::readFile(__DIR__ . '/../../README.md'));\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/disable_coroutine.phpt",
    "content": "--TEST--\nswoole_http_server: disable coroutine and use go\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        for ($n = 0; $n > MAX_REQUESTS; $n++) {\n            Assert::assert(httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\") == $n);\n        }\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'enable_coroutine' => false, // close build-in coroutine\n    ]);\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        go(function () use ($response) {\n            co::sleep(0.001);\n            $cid = go(function () use ($response) {\n                co::yield();\n                $response->end(Co::getuid());\n            });\n            co::resume($cid);\n        });\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/dispatch_mode_7.phpt",
    "content": "--TEST--\nswoole_http_server: dispatch_mode=7\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        Assert::eq($data, \"hello\");\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server(\"0.0.0.0\", $pm->getFreePort(), SWOOLE_PROCESS);\n\n    $http->set([\n        'reactor_num' => 2,\n        'worker_num' => 2,\n        'dispatch_mode' => 7,\n        'reload_async' => true,\n        'log_level' => SWOOLE_LOG_WARNING\n    ]);\n\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on('request', function ($request, Swoole\\Http\\Response $response) use ($http) {\n        $response->write(\"hello\");\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/duplicate_header.phpt",
    "content": "--TEST--\nswoole_http_server: duplicate header\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:{$pm->getFreePort()}/\");\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_HTTPHEADER, [\n        'X-Test-Header1: value1',\n        'X-Test-Header2: value2',\n        'X-Test-Header2: value3',\n        'X-Test-Header3: value4',\n        'X-Test-Header3: value5',\n        'X-Test-Header3: value6',\n    ]);\n    curl_setopt($ch, CURLOPT_HEADER, true);\n    echo curl_exec($ch);\n    curl_close($ch);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'worker_num' => 1,\n        'enable_coroutine' => false,\n        'log_file' => '/dev/null'\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $msg = \"hello world\";\n        Assert::eq($request->header['x-test-header1'], 'value1');\n        Assert::eq($request->header['x-test-header2'], ['value2', 'value3']);\n        Assert::eq($request->header['x-test-header3'], ['value4', 'value5', 'value6']);\n        $response->header(\"content-length\", strlen($msg) . \" \");\n        $response->header(\"Test-Value\", [\n            \"a\\r\\n\",\n            \"b1234 \",\n            \"d5678\",\n            \"e  \\n \",\n            null,\n            5678,\n            3.1415926,\n        ]);\n        $response->end($msg);\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nHTTP/1.1 200 OK\nContent-Length: 11\nTest-Value: a\nTest-Value: b1234\nTest-Value: d5678\nTest-Value: e\nTest-Value: 5678\nTest-Value: 3.1415926\nServer: swoole-http-server\nDate: %s\nConnection: keep-alive\nContent-Type: text/html\n\nhello world\n"
  },
  {
    "path": "tests/swoole_http_server/enable_coroutine.phpt",
    "content": "--TEST--\nswoole_http_server: enable_coroutine setting in server\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\") . \"\\n\";\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/co\") . \"\\n\";\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/co\") . \"\\n\";\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/co\") . \"\\n\";\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set([\n        'enable_coroutine' => false, // close build-in coroutine\n        'worker_num' => 1,\n        'log_level' => SWOOLE_LOG_NONE,\n    ]);\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->header(\"Content-Type\", \"text/plain\");\n        if ($request->server['request_uri'] == '/co') {\n            go(function () use ($response) {\n                $response->end(Co::getuid());\n            });\n        } else {\n            $response->end(Co::getuid());\n        }\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n-1\n1\n2\n3\n"
  },
  {
    "path": "tests/swoole_http_server/error_1203.phpt",
    "content": "--TEST--\nswoole_http_server: http_compression\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm)\n{\n    go(function () use ($pm) {\n        try {\n            $data =  httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        } catch(Exception $e) {\n            Assert::contains($e->getMessage(), 'Connection reset by peer');\n        }\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n\n    $http->set([\n        'http_compression' => false,\n        'log_file' => '/dev/null',\n        'buffer_output_size' => 128 * 1024,\n    ]);\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function ($request, Swoole\\Http\\Response $response) {\n        Assert::eq($response->end(str_repeat('A', 256 * 1024)), false);\n        Assert::eq(swoole_last_error(), SWOOLE_ERROR_DATA_LENGTH_TOO_LARGE);\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/error_413.phpt",
    "content": "--TEST--\nswoole_http_server: 413 error\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:\" . $pm->getFreePort() . '/');\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_POST, 1);\n    $post_data = ['data' => str_repeat('A', 65536)];\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);\n\n    $result = curl_exec($ch);\n    Assert::isEmpty($result);\n    $info = curl_getinfo($ch);\n    Assert::eq($info['http_code'], 413);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n\n    $http->set(['package_max_length' => 8192, 'log_file' => '/dev/null']);\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n\n    $http->on('request', function ($req, Swoole\\Http\\Response $resp)  {\n        $resp->end('hello');\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/event_stream.phpt",
    "content": "--TEST--\nswoole_http_server: event stream\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nconst N = 128;\n$pm = new ProcessManager;\n\n$data = [];\nfor ($i = N; $i--;) {\n    $data[] = 'data: ' . base64_encode(random_bytes(random_int(16, 128))) . \"\\n\\n\";\n}\n$data[] = 'data: [DONE]' . \"\\n\\n\";\n\n$pm->parentFunc = function () use ($pm, $data) {\n    Co\\run(function () use ($pm, $data) {\n        $client = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n\n        $index = 0;\n        $buffer = '';\n        $client->set([\n            'timeout' => 60,\n            'write_func' => function($client, $chunk) use (&$buffer, &$index, $data) {\n                $buffer .= $chunk;\n                while(true) {\n                    $position = mb_strpos($buffer, \"\\n\\n\");\n                    if ($position === false) {\n                        break;\n                    }\n\n                    Assert::eq(mb_substr($buffer, 0, $position) . \"\\n\\n\", $data[$index]);\n                    $buffer = mb_substr($buffer, $position + 2);\n                    $index++;\n                }\n\n                return true;\n            }\n        ]);\n        Assert::true($client->get('/'));\n        Assert::isEmpty($client->getBody());\n        Assert::keyNotExists($client->headers, 'content-length');\n        Assert::eq($client->headers['content-type'], \"text/event-stream\");\n        $pm->kill();\n    });\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm, $data) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->on('WorkerStart', function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on('request', function ($req, Swoole\\Http\\Response $resp) use ($http, $data) {\n        $resp->header(\"Content-Type\", \"text/event-stream\");\n        $resp->header(\"Cache-Control\", \"no-cache\");\n        $resp->header(\"Connection\", \"keep-alive\");\n        $resp->header(\"X-Accel-Buffering\", \"no\");\n\n        for ($i = 0; $i < N; $i++) {\n            Co::sleep(0.01);\n            $resp->write($data[$i]);\n        }\n\n        $resp->write(\"data: [DONE]\\n\\n\");\n        $resp->end();\n        Co::sleep(0.05);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/event_stream2.phpt",
    "content": "--TEST--\nswoole_http_server: event stream 2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nconst N = 128;\n$pm = new ProcessManager;\n\n$data = [];\nfor ($i = N; $i--;) {\n    $data[] = 'data: ' . base64_encode(random_bytes(random_int(16, 128))) . \"\\n\\n\";\n}\n\n$pm->parentFunc = function () use ($pm, $data) {\n    Co\\run(function () use ($pm, $data) {\n        $client = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        Assert::true($client->get('/'));\n        Assert::isEmpty($client->getBody());\n        Assert::keyNotExists($client->headers, 'content-length');\n        Assert::eq($client->headers['content-type'], \"text/event-stream\");\n        for ($i = 0; $i < N; $i++) {\n            Co::sleep(0.01);\n            $line1 = $client->socket->recvLine();\n            $line2 = $client->socket->recvLine();\n            Assert::eq($line1 . $line2, $data[$i]);\n        }\n        $pm->kill();\n    });\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm, $data) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->on('WorkerStart', function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on('request', function ($req, Swoole\\Http\\Response $resp) use ($http, $data) {\n        $resp->header(\"Content-Type\", \"text/event-stream\");\n        $resp->header(\"Cache-Control\", \"no-cache\");\n        $resp->header(\"Connection\", \"keep-alive\");\n        $resp->header(\"X-Accel-Buffering\", \"no\");\n        $resp->header('Content-Encoding', '');\n        $resp->header(\"Content-Length\", '');\n        $resp->end();\n        Co::sleep(0.05);\n        for ($i = 0; $i < N; $i++) {\n            Co::sleep(0.01);\n            $http->send($resp->fd, $data[$i]);\n        }\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/form_data_1.phpt",
    "content": "--TEST--\nswoole_http_server: form data 1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/../include/api/http_test_cases.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$pm = new ProcessManager;\n$pm->initFreePorts();\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    form_data_test($pm, [250]);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $http->set(['log_file' => '/dev/null']);\n    $http->on('WorkerStart', function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('Request', function (Request $request, Response $response) use ($http) {\n        $response->end(json_encode($request->post));\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/form_data_with_charset.phpt",
    "content": "--TEST--\nswoole_http_server: http server parse form data with charset\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ndefine('EOF', get_safe_random());\n\nfunction getRandomData(int $num): array\n{\n    $data = [];\n    foreach (range(1, $num) as $_) {\n        $key = substr(get_safe_random(32), 0, mt_rand(1, 32));\n        $value = substr(get_safe_random(64), 0, mt_rand(0, 64));\n        $data[$key] = $value;\n    }\n\n    return $data;\n}\n\nfunction arrayToMultipartString(array $var, string $boundary): string\n{\n    $ret = '';\n    foreach ($var as $name => $value) {\n        $value = (string)($value);\n        $ret .= \"--{$boundary}\\r\\nContent-Disposition: form-data; name=\\\"{$name}\\\"\\r\\n\\r\\n{$value}\\r\\n\";\n    }\n    $ret .= \"--{$boundary}--\\r\\n\";\n\n    return $ret;\n}\n\nfunction sendData(string $host, int $port, array $get, array $post): string\n{\n    $client = new Co\\Client(SWOOLE_SOCK_TCP);\n    if (!$client->connect($host, $port, 1)) {\n        exit(\"connect failed. Error: {$client->errCode}\\n\");\n    }\n\n    $get = http_build_query($get);\n    $boundary = '++++' . md5(get_safe_random(16));\n    $content_type = \"multipart/form-data; boundary={$boundary}; charset=UTF-8\";\n    $post = arrayToMultipartString($post, $boundary);\n\n    $content_length = strlen($post);\n    $CR = \"\\r\";\n    $data = <<<HTTP\nPOST /?{$get} HTTP/1.1{$CR}\nHost: {$host}{$CR}\nConnection: closed{$CR}\nAccept: */*{$CR}\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36{$CR}\nContent-Type: {$content_type}{$CR}\nContent-Length: {$content_length}{$CR}\n{$CR}\n{$post}\nHTTP;\n\n    Assert::assert($client->send($data));\n    $data = '';\n    while ($ret = $client->recv()) {\n        $data .= $ret;\n        if (strrpos($data, EOF) !== false) {\n            $data = substr($data, 0, strlen($data) - strlen(EOF));\n            break;\n        }\n    }\n    return $data;\n}\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        foreach (range(1, 100) as $_) {\n            $get = getRandomData(50);\n            $post = getRandomData(100);\n            $ret = sendData('127.0.0.1', $pm->getFreePort(), $get, $post);\n            list($_, $body) = explode(\"\\r\\n\\r\\n\", $ret);\n            Assert::same($body, var_dump_return($get, $post));\n        }\n        $pm->kill();\n        echo \"DONE\\n\";\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['log_file' => '/dev/null']);\n    $http->on('WorkerStart', function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('Request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($http) {\n        $response->end(var_dump_return($request->get, $request->post));\n        $http->send($request->fd, EOF);\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/head_method.phpt",
    "content": "--TEST--\nswoole_http_server: HEAD method\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$data = json_encode([\n    'code' => 'ok',\n    'error' => false,\n    'payload' => 'Hello World'\n]);\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm, $data) {\n\n    //request 1, HEAD\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:\" . $pm->getFreePort() . '/');\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HEAD');\n    curl_setopt($ch, CURLOPT_NOBODY, true);\n\n    $result = curl_exec($ch);\n    Assert::isEmpty($result);\n    $info = curl_getinfo($ch);\n    Assert::eq(strlen($data), $info['download_content_length']);\n\n    //request 2, GET\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:\" . $pm->getFreePort() . '/');\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    $result = curl_exec($ch);\n    Assert::eq($data, $result);\n\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $data) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n\n    $http->on('request', function ($req, Swoole\\Http\\Response $resp) use ($data) {\n        $resp->header('Content-Type', 'application/json');\n        if ($req->server['request_method'] == 'HEAD') {\n            $resp->header('Content-Length', strlen($data));\n            $resp->end();\n            return;\n        }\n        $resp->end($data);\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/headers_sent.phpt",
    "content": "--TEST--\nswoole_http_server: headers sent (coroutine disabled)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:{$pm->getFreePort()}/\");\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    echo curl_exec($ch);\n    echo curl_exec($ch);\n    curl_close($ch);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set([\n        'worker_num' => 1,\n        'enable_coroutine' => false,\n        'log_file' => '/dev/null'\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        ob_start();\n        echo 'Test';\n        $output = ob_get_clean();\n        $response->write('buffered_output=' . $output . \"\\n\");\n        $response->write('headers_sent=' . (int)headers_sent() . \"\\n\");\n        $response->end();\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nbuffered_output=Test\nheaders_sent=0\nbuffered_output=Test\nheaders_sent=0\n"
  },
  {
    "path": "tests/swoole_http_server/headers_sent_coroutine.phpt",
    "content": "--TEST--\nswoole_http_server: headers sent (coroutine enabled)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:{$pm->getFreePort()}/\");\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    echo curl_exec($ch);\n    echo curl_exec($ch);\n    curl_close($ch);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set([\n        'worker_num' => 1,\n        'enable_coroutine' => true,\n        'log_file' => '/dev/null'\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        ob_start();\n        echo 'Test';\n        $output = ob_get_clean();\n        $response->write('buffered_output=' . $output . \"\\n\");\n        $response->write('headers_sent=' . (int)headers_sent() . \"\\n\");\n        $response->end();\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nbuffered_output=Test\nheaders_sent=0\nbuffered_output=Test\nheaders_sent=0\n"
  },
  {
    "path": "tests/swoole_http_server/http_autoindex.phpt",
    "content": "--TEST--\nswoole_http_server: http_autoindex\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Server;\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    Coroutine\\run(function () use ($pm) {\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        $files = scan_dir(DOCUMENT_ROOT);\n        foreach ($files as $f) {\n            Assert::contains($data, basename($f));\n        }\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/dir1\");\n        $files = scan_dir(DOCUMENT_ROOT.'/dir1');\n        foreach ($files as $f) {\n            Assert::contains($data, basename($f));\n        }\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/dir2\");\n        $files = scan_dir(DOCUMENT_ROOT.'/dir2');\n        foreach ($files as $f) {\n            Assert::contains($data, basename($f));\n        }\n        $pm->kill();\n    });\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'enable_static_handler' => true,\n        'document_root' => DOCUMENT_ROOT,\n        'http_autoindex' => true,\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) {\n        $response->end(\"dynamic request\");\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/http_index_files.phpt",
    "content": "--TEST--\nswoole_http_server: http_index_files\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Server;\n\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $index_content = file_get_contents(DOCUMENT_ROOT . '/index.html');\n        $dir2_index_txt_content = file_get_contents(DOCUMENT_ROOT . '/dir2/index.txt');\n\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        Assert::same($data, $index_content);\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/dir1\");\n        Assert::same($data, 'dynamic request');\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/dir2\");\n        Assert::assert($data, $dir2_index_txt_content);\n\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n\n    $http->set([\n        'log_file' => '/dev/null',\n        'enable_static_handler' => true,\n        'document_root' => DOCUMENT_ROOT,\n        'http_index_files' => ['index.html', 'index.txt'],\n    ]);\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Request $request, Response $response) {\n        $response->end(\"dynamic request\");\n    });\n\n    $http->start();\n\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/http_index_files_autoindex.phpt",
    "content": "--TEST--\nswoole_http_server: http_index_files_autoindex\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Server;\n\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $index_content = file_get_contents(DOCUMENT_ROOT . '/index.html');\n        $dir2_index_txt_content = file_get_contents(DOCUMENT_ROOT . '/dir2/index.txt');\n\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        Assert::same($data, $index_content);\n\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/dir1\");\n        $files = scan_dir(DOCUMENT_ROOT.'/dir1');\n        foreach ($files as $f) {\n            Assert::contains($data, basename($f));\n        }\n\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/dir2\");\n        Assert::assert($data, $dir2_index_txt_content);\n\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n\n    $http->set([\n        'log_file' => '/dev/null',\n        'enable_static_handler' => true,\n        'document_root' => DOCUMENT_ROOT,\n        'http_index_files' => ['index.html', 'index.txt'],\n        'http_autoindex' => true,\n    ]);\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Request $request, Response $response) {\n        $response->end(\"dynamic request\");\n    });\n\n    $http->start();\n\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/https.phpt",
    "content": "--TEST--\nswoole_http_server: https\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$html = base64_encode(random_bytes(rand(2048, 65536)));\n\n$pm->parentFunc = function ($pid) use ($pm, $html) {\n    go(function () use ($pm, $html) {\n        $data = httpGetBody(\"https://127.0.0.1:{$pm->getFreePort()}/\");\n        Assert::same($data, $html);\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm, $html) {\n    $serv = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $serv->set([\n        'log_file' => '/dev/null',\n        'ssl_cert_file' => SSL_FILE_DIR.'/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR.'/server.key',\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('request', function ($req, $resp) use ($html) {\n        $resp->end($html);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/issue_2360.phpt",
    "content": "--TEST--\nswoole_http_server: issue 2360 (Swoole\\Http\\Server silently fails to read requests)\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Co\\Http\\Client;\nuse Swoole\\Event;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Server;\n\ndefine('SOCKET_BUFFER_SIZE', 2 << mt_rand(10, 13)); // 1024 ~ 8192\nphpt_echo('SOCKET_BUFFER_SIZE=' . SOCKET_BUFFER_SIZE . PHP_EOL);\n\n$pm = new ProcessManager();\n$pm->setRandomFunc(function () {\n    $size = mt_rand(SOCKET_BUFFER_SIZE, SOCKET_BUFFER_SIZE << 3); // 1024 ~ 65536\n    $data = '';\n    for ($i = 0; $i < $size; $i++) {\n        $data .= sprintf('%01x', $i % 16);\n    }\n    return $data;\n});\n$pm->initRandomDataEx(1, MAX_REQUESTS);\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Client('127.0.0.1', $pm->getFreePort());\n        $cli->set(['socket_buffer_size' => SOCKET_BUFFER_SIZE]);\n        for ($n = MAX_REQUESTS; $n--;) {\n            $data = $pm->getRandomData();\n            Assert::true($cli->post('/', $data));\n            Assert::same($cli->statusCode, 200);\n            Assert::same($cli->body, $data);\n            phpt_echo('posting ' . strlen($data) . \" bytes\\n\");\n        }\n        $cli->close();\n    });\n    Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'log_file' => '/dev/null',\n        'socket_buffer_size' => SOCKET_BUFFER_SIZE,\n    ]);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Request $request, Response $response) use ($pm) {\n        phpt_echo(\"received {$request->header['content-length']} bytes\\n\");\n        if (Assert::assert($request->rawContent() === $pm->getRandomData())) {\n            $response->end($request->rawContent());\n        }\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/json_encode.phpt",
    "content": "--TEST--\nswoole_http_server: json_encode or serialize Swoole\\Http\\Request::class OR Swoole\\Http\\Response::class\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/test\") . PHP_EOL;\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set(['log_file' => '/dev/null']);\n    $server->on('start', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        Assert::true($request->fd > 0);\n        Assert::true($response->fd > 0);\n\n        $result = json_decode(json_encode($request), true);\n        Assert::true($result['fd'] > 0);\n\n        $result = json_decode(json_encode($response), true);\n        Assert::true($result['fd'] > 0);\n\n        $response->end('OK');\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_server/json_encode2.phpt",
    "content": "--TEST--\nswoole_http_server: json_encode or serialize Swoole\\Http\\Request::class OR Swoole\\Http\\Response::class\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/test\") . PHP_EOL;\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set(['log_file' => '/dev/null']);\n    $server->on('start', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        $result = json_decode(json_encode($request), true);\n        Assert::true($result['fd'] > 0);\n\n        $result = json_decode(json_encode($response), true);\n        Assert::true($result['fd'] > 0);\n\n        Assert::true($request->fd > 0);\n        Assert::true($response->fd > 0);\n\n        $response->end('OK');\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_server/large_url.phpt",
    "content": "--TEST--\nswoole_http_server: large url\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $client = new Co\\Client(SWOOLE_SOCK_TCP);\n        if (!$client->connect('127.0.0.1', $pm->getFreePort(), 1)) {\n            exit(\"connect failed. Error: {$client->errCode}\\n\");\n        }\n        $header = \"GET /home/explore/?a=\" . str_repeat('A', $len = rand(1024, 2048)) . \" HTTP/1.1\\r\\n\";\n        $header .= \"Host: 127.0.0.1\\r\\n\";\n        $header .= \"Connection: keep-alive\\r\\n\";\n        $header .= \"Cache-Control: max-age=0\\r\\n\";\n        $cookie = \"8MLP_5753_saltkey=RSU8HYED; 8MLP_5753_lastvisit=1426120671; pgv_pvi=1454765056; CNZZDATA1000008050=684878078-1426123263-http%253A%252F%252Fcznews-team.chinaz.com%252F%7C1426485386; attentiondomain=2z.cn%2cchinaz.com%2ckuaishang.cn%2ccxpcms.com; CNZZDATA33217=cnzz_eid%3D1036784254-1426122273-http%253A%252F%252Fcznews-team.chinaz.com%252F%26ntime%3D1427414208; CNZZDATA433095=cnzz_eid%3D1613871160-1426123273-http%253A%252F%252Fcznews-team.chinaz.com%252F%26ntime%3D1427848205; CNZZDATA1254679775=309722566-1427851758-http%253A%252F%252Fcznews-team.chinaz.com%252F%7C1427851758; 8MLP_5753_security_cookiereport=c014Hgufskpv55xgM9UaB%2FZZdMrcN0QqBYdcGomTu8OlTDWzTA0z; 8MLP_5753_ulastactivity=e4a1aRIbgdzoRDd8NlT5CMIwLnWjyjr2hWyfn6T5g82RitUOdf3o; 8MLP_5753_auth=9351LJpv7Xa%2FPUylJDQgRiAONZ5HysOaj%2BqRGb6jYmpqZpRkVc2ibPXm7LAfArC%2FpIpY2Fx%2B59AHqzr843qozZWxWNZi; mytool_user=uSHVgCUFWf5Sv2Y8tKytQRUJW3wMVT3rw5xQLNGQFIsod4C6vYWeGA==; 8MLP_5753_lip=220.160.111.22%2C1428036585; pgv_si=s4245709824; PHPSESSID=t3hp9h4o8rb3956t5pajnsfab1; 8MLP_5753_st_p=1024432%7C1428040399%7Cf7599ba9053aa27e12e9e597a4c372ce; 8MLP_5753_viewid=tid_7701248; 8MLP_5753_smile=5D1; 8MLP_5753_st_t=1024432%7C1428040402%7C46d40e02d899b10b431822eb1d39f6a1; 8MLP_5753_forum_lastvisit=D_140_1427103032D_165_1427427405D_168_1427870172D_167_1427870173D_166_1428021390D_163_1428040402; 8MLP_5753_sid=k25gxK; 8MLP_5753_lastact=1428040403%09misc.php%09patch; cmstop_page-view-mode=view; cmstop_rememberusername=error; cmstop_auth=Jcn2qzVn9nsjqtodER9OphcW3PURDWNx6mO7j0Zbb9k%3D; cmstop_userid=6; cmstop_username=error; Hm_lvt_aecc9715b0f5d5f7f34fba48a3c511d6=1427967317,1428021376,1428036617,1428040224; Hm_lpvt_aecc9715b0f5d5f7f34fba48a3c511d6=1428050417; YjVmNm_timeout=0\";\n        $header .= \"Cookie: $cookie\\r\\n\";\n        $header .= \"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\\r\\n\";\n        $header .= \"User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36\\r\\n\";\n        $header .= \"\\r\\n\";\n        $_sendStr = $header;\n        $client->send(substr($_sendStr, 0, 512));\n        usleep(200000);\n        $client->send(substr($_sendStr, 512, 512));\n        usleep(200000);\n        $client->send(substr($_sendStr, 1024));\n        $data = $client->recv();\n        $client->close();\n        if (Assert::assert(!empty($data))) {\n            Assert::same((int)explode(\"\\r\\n\\r\\n\", $data)[1], $len);\n        }\n        $pm->kill();\n        echo \"DONE\\n\";\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['log_file' => '/dev/null']);\n    $http->on('workerStart', function () use ($pm) { $pm->wakeup(); });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->header(\"Content-Type\", \"text/plain\");\n        $response->end(strlen($request->get['a']));\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/max-age.phpt",
    "content": "--TEST--\nswoole_http_server: cookies (max-age)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $uri = \"http://127.0.0.1:{$pm->getFreePort()}\";\n        $cookies = httpRequest($uri)['set_cookie_headers'];\n\n        var_dump(strpos($cookies[0], 'test=123456789') !== false);\n        var_dump(strpos($cookies[0], 'expires='.date('D, d-M-Y H:i:s \\G\\M\\T', time() + 3600)) !== false);\n        var_dump(strpos($cookies[0], 'Max-Age=3600') !== false);\n        var_dump(strpos($cookies[0], 'path=/') !== false);\n        var_dump(strpos($cookies[0], 'domain=example.com') !== false);\n        var_dump(strpos($cookies[0], 'secure') !== false);\n        var_dump(strpos($cookies[0], 'HttpOnly') !== false);\n        var_dump(strpos($cookies[0], 'SameSite=None') !== false);\n        var_dump(strpos($cookies[1], 'test=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT') !== false);\n        var_dump(strpos($cookies[1], 'Max-Age=0') !== false);\n    });\n\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        $response->cookie('test', '123456789', time() + 3600, '/', 'example.com', true, true, 'None');\n        $response->cookie('test', '');\n        $response->end();\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/max_concurrency.phpt",
    "content": "--TEST--\nswoole_http_server: max_concurrency\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Coroutine;\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\nconst SERVER_MAX_CONCURRENCY = 4;\nconst GREETING_MESSAGE = 'hello world';\n\n$pm = new ProcessManager;\n$pm->initFreePorts();\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        $url = 'http://127.0.0.1:' . $pm->getFreePort(0) . '/index.html';\n        $n = SERVER_MAX_CONCURRENCY;\n        // 200\n        $cid_list = [];\n        while ($n--) {\n            $cid_list[] = go(function () use ($url) {\n                $c = Swoole\\Coroutine\\Http\\get($url);\n                Assert::eq($c->getStatusCode(), 200);\n                Assert::eq($c->getBody(), GREETING_MESSAGE);\n            });\n        }\n\n        System::sleep(0.005);\n\n        // 403, high concurrency\n        $n = SERVER_MAX_CONCURRENCY;\n        while ($n--) {\n            $cid_list[] = go(function () use ($url) {\n                $c = Swoole\\Coroutine\\Http\\get($url);\n                Assert::eq($c->getStatusCode(), 503);\n            });\n        }\n\n        // wait\n        Coroutine::join($cid_list);\n\n        // low concurrency\n        $n = SERVER_MAX_CONCURRENCY;\n        $cid_list = [];\n        while ($n--) {\n            $cid_list[] = go(function () use ($url) {\n                $c = Swoole\\Coroutine\\Http\\get($url);\n                Assert::eq($c->getStatusCode(), 200);\n                Assert::eq($c->getBody(), GREETING_MESSAGE);\n            });\n        }\n\n        Coroutine::join($cid_list);\n        echo \"DONE\\n\";\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set(['log_file' => '/dev/null', 'max_concurrency' => SERVER_MAX_CONCURRENCY]);\n    $http->on('start', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        System::sleep(0.1);\n        $response->end(GREETING_MESSAGE);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/max_coro_num.phpt",
    "content": "--TEST--\nswoole_http_server: max coro num\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->initRandomData(2);\n$pm->parentFunc = function ($pid) use ($pm) {\n    switch_process();\n    go(function () use ($pm) {\n        $statusCode = httpGetStatusCode(\"http://127.0.0.1:{$pm->getFreePort()}\", ['timeout' => 0.1]);\n        echo PHP_EOL . $statusCode . PHP_EOL;\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    Co::set(['max_coroutine' => 1]);\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set(['log_file' => '/dev/null']);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n        Co::yield();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        echo \"never here\\n\";\n        $response->end($pm->getRandomData());\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nWarning: Swoole\\Server::start(): exceed max number of coroutine 1 in %s on line %d\n\nWarning: Swoole\\Server::start(): Swoole\\Http\\Server->onRequest handler error in %s on line %d\n\n503\n"
  },
  {
    "path": "tests/swoole_http_server/max_execution_time.phpt",
    "content": "--TEST--\nswoole_http_server: max execution time\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->initRandomData(2);\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $result = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}\");\n        var_dump($result);\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n\n    $http->set(\n        [\n            'enable_coroutine' => true,\n            'hook_flags' => SWOOLE_HOOK_ALL,\n        ]\n    );\n\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        try {\n            Swoole\\Coroutine::setTimeLimit(1);\n            sleep(5);\n            $response->header('Content-Type', 'text/plain');\n            $response->end('Hello World');\n        } catch (\\Throwable $e) {\n            Assert::true($e instanceof \\Swoole\\Coroutine\\TimeoutException);\n            $response->end('execution timeout');\n        }\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nstring(17) \"execution timeout\"\n"
  },
  {
    "path": "tests/swoole_http_server/max_input_vars.phpt",
    "content": "--TEST--\nswoole_http_server: max_input_vars\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Swoole\\Coroutine\\run(function () use ($pm) {\n        $maxInputVars = ini_get('max_input_vars') + 10;\n        $data = [];\n        $cookies = [];\n        $temp = 'max_input_vars';\n        for ($i = 0; $i < $maxInputVars; $i++) {\n            $data[$temp . $i] = $temp;\n            $cookies[] = $temp . $i.'='.$temp;\n        }\n\n        // post method\n        httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}\", ['data' => $data, 'headers' => ['Cookie' => implode(';', $cookies)]]);\n\n        // get method\n        httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}?\".http_build_query($data));\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response){\n        $maxInputVars = ini_get('max_input_vars');\n        if ($request->get) {\n            var_dump(count($request->get) == $maxInputVars);\n        }\n\n        if ($request->post) {\n            var_dump(count($request->post) == $maxInputVars);\n        }\n\n        if ($request->cookie) {\n            var_dump(count($request->cookie) == $maxInputVars);\n        }\n        $response->end();\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n%s To increase the limit change max_input_vars in php.ini.\n%s To increase the limit change max_input_vars in php.ini.\nbool(true)\nbool(true)\n%s To increase the limit change max_input_vars in php.ini.\nbool(true)\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/mixed_server.phpt",
    "content": "--TEST--\nswoole_http_server: http mixed server\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_http2();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$tcp_options = [\n    'open_length_check' => true,\n    'package_length_type' => 'n',\n    'package_length_offset' => 0,\n    'package_body_offset' => 2,\n];\n\n$pm = new ProcessManager;\n$pm->initFreePorts(2);\n// client side\n$pm->parentFunc = function ($pid) use ($pm, $tcp_options) {\n    go(function () use ($pm, $tcp_options) {\n        // http\n        $http_client = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort(0));\n        Assert::assert($http_client->post('/', 'Swoole Http'));\n        var_dump($http_client->body);\n\n        // http2\n        $http2_client = new Swoole\\Coroutine\\Http2\\Client('127.0.0.1', $pm->getFreePort(0));\n        $http2_client->connect();\n        $http2_request = new Swoole\\Http2\\Request;\n        $http2_request->method = 'POST';\n        $http2_request->data = 'Swoole Http2';\n        $http2_client->send($http2_request);\n        $http2_response = $http2_client->recv();\n        var_dump($http2_response->data);\n\n        // websocket\n        $http_client->upgrade('/');\n        $http_client->push('Swoole Websocket');\n        var_dump($http_client->recv()->data);\n\n        // tcp\n        $tcp_client = new Swoole\\Coroutine\\Client(SWOOLE_TCP);\n        $tcp_client->set($tcp_options);\n        $tcp_client->connect('127.0.0.1', $pm->getFreePort(1));\n        $tcp_client->send(tcp_pack('Swoole Tcp'));\n        var_dump(tcp_unpack($tcp_client->recv()));\n\n        $pm->kill();\n    });\n};\n// server side\n$pm->childFunc = function () use ($pm, $tcp_options) {\n    $server = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true\n    ]);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    // http && http2\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end('Hello ' . $request->rawcontent());\n    });\n    // websocket\n    $server->on('message', function (Swoole\\WebSocket\\Server  $server, Swoole\\WebSocket\\Frame $frame) {\n        $server->push($frame->fd, 'Hello ' . $frame->data);\n    });\n    // tcp\n    $tcp_server = $server->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_TCP);\n    $tcp_server->set($tcp_options);\n    $tcp_server->on('receive', function (Swoole\\Server $server, int $fd, int $reactor_id, string $data) {\n        $server->send($fd, tcp_pack('Hello ' . tcp_unpack($data)));\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nstring(17) \"Hello Swoole Http\"\nstring(18) \"Hello Swoole Http2\"\nstring(22) \"Hello Swoole Websocket\"\nstring(16) \"Hello Swoole Tcp\"\n"
  },
  {
    "path": "tests/swoole_http_server/new_cookie.phpt",
    "content": "--TEST--\nswoole_http_server: new cookie\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Cookie;\n$cookie = new Cookie();\n$cookie->withName('test')\n    ->withValue('123456789')\n    ->withExpires(time() + 3600)\n    ->withPath('/path')\n    ->withDomain('example.com')\n    ->withSecure(true)\n    ->withHttpOnly(true)\n    ->withSameSite('None');\n\nvar_dump($cookie->toArray());\n$cookie->reset();\nvar_dump($cookie->toArray());\n?>\n--EXPECTF--\narray(11) {\n  [\"name\"]=>\n  string(4) \"test\"\n  [\"value\"]=>\n  string(9) \"123456789\"\n  [\"path\"]=>\n  string(5) \"/path\"\n  [\"domain\"]=>\n  string(11) \"example.com\"\n  [\"sameSite\"]=>\n  string(4) \"None\"\n  [\"priority\"]=>\n  string(0) \"\"\n  [\"encode\"]=>\n  bool(true)\n  [\"expires\"]=>\n  int(%d)\n  [\"secure\"]=>\n  bool(true)\n  [\"httpOnly\"]=>\n  bool(true)\n  [\"partitioned\"]=>\n  bool(false)\n}\narray(11) {\n  [\"name\"]=>\n  string(0) \"\"\n  [\"value\"]=>\n  string(0) \"\"\n  [\"path\"]=>\n  string(0) \"\"\n  [\"domain\"]=>\n  string(0) \"\"\n  [\"sameSite\"]=>\n  string(0) \"\"\n  [\"priority\"]=>\n  string(0) \"\"\n  [\"encode\"]=>\n  bool(true)\n  [\"expires\"]=>\n  int(0)\n  [\"secure\"]=>\n  bool(false)\n  [\"httpOnly\"]=>\n  bool(false)\n  [\"partitioned\"]=>\n  bool(false)\n}\n"
  },
  {
    "path": "tests/swoole_http_server/no_compression.phpt",
    "content": "--TEST--\nswoole_http_server: no compression\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm)\n{\n    go(function () use ($pm) {\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        Assert::assert(md5_file(__DIR__ . '/../../README.md') == md5($data));\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm)\n{\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $http->set(['http_compression' => false,]);\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function ($request, Swoole\\Http\\Response $response) {\n        $response->end(co::readFile(__DIR__ . '/../../README.md'));\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/numeric_header_name.phpt",
    "content": "--TEST--\nswoole_http_server: numeric header name\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        Assert::true($client->get('/'));\n        Assert::eq($client->headers['12345'], 'hello');\n        Assert::eq($client->headers['12345.678'], 'world');\n        $pm->kill();\n    });\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function ($request, Swoole\\Http\\Response $response) {\n        $response->header(12345, 'hello');\n        $response->header(12345.678, 'world');\n        $response->end('OK');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/objectCookie.phpt",
    "content": "--TEST--\nswoole_http_server: new cookie\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\Run(function () use ($pm) {\n        var_dump(httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}\")['set_cookie_headers']);\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set(['log_file' => '/dev/null']);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        $cookie = new Swoole\\Http\\Cookie();\n        $cookie->withName('key1')\n            ->withValue('val1')\n            ->withExpires(time() + 84600)\n            ->withPath('/')\n            ->withDomain('id.test.com')\n            ->withSecure(true)\n            ->withHttpOnly(true)\n            ->withSameSite('None')\n            ->withPriority('High')\n            ->withPartitioned(true);\n        $response->setCookie($cookie);\n        $cookie->withValue('');\n        $response->setCookie($cookie);\n        $response->end(\"<h1>Hello Swoole. #\" . rand(1000, 9999) . \"</h1>\");\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\narray(2) {\n  [0]=>\n  string(152) \"key1=val1; expires=%s; Max-Age=84600; path=/; domain=id.test.com; secure; HttpOnly; SameSite=None; Priority=High; Partitioned\"\n  [1]=>\n  string(151) \"key1=deleted; expires=%s; Max-Age=0; path=/; domain=id.test.com; secure; HttpOnly; SameSite=None; Priority=High; Partitioned\"\n}\n"
  },
  {
    "path": "tests/swoole_http_server/objectCookieMemory.phpt",
    "content": "--TEST--\nswoole_http_server: new cookie memory\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Swoole\\Coroutine\\run(function () use ($pm) {\n        httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}\");\n        httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}\");\n        httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}\");\n        httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}\");\n        httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}\");\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) use ($http) {\n        $previous = memory_get_usage();\n        $cookie = new Swoole\\Http\\Cookie();\n        $i = 10000;\n        while($i--) {\n            $cookie->withName('key1')\n                ->withValue('val1')\n                ->withExpires(time() + 84600)\n                ->withPath('/')\n                ->withDomain('id.test.com')\n                ->withSecure(true)\n                ->withHttpOnly(true)\n                ->withSameSite('None')\n                ->withPriority('High')\n                ->withPartitioned(true);\n        }\n\n        global $previous;\n        global $item;\n        $current = memory_get_usage();\n        $stats = [\n            'id' => $http->getWorkerId(),\n            'item' => $item++,\n            'prev_mem' => $previous,\n            'curr_mem' => $current,\n            'diff_mem' => $current - $previous,\n        ];\n        $previous = $current;\n\n        echo json_encode($stats), PHP_EOL;\n        $response->end('test response');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n{\"id\":%d,\"item\":null,\"prev_mem\":null,\"curr_mem\":%d,\"diff_mem\":%d}\n{\"id\":%d,\"item\":%d,\"prev_mem\":%d,\"curr_mem\":%d,\"diff_mem\":%d}\n{\"id\":%d,\"item\":%d,\"prev_mem\":%d,\"curr_mem\":%d,\"diff_mem\":0}\n{\"id\":%d,\"item\":%d,\"prev_mem\":%d,\"curr_mem\":%d,\"diff_mem\":0}\n{\"id\":%d,\"item\":%d,\"prev_mem\":%d,\"curr_mem\":%d,\"diff_mem\":0}\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/octane_bug_651.phpt",
    "content": "--TEST--\nswoole_http_server: Octane bug 651 https://github.com/laravel/octane/issues/651\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        Assert::true($client->get('/'));\n        Assert::eq($client->getBody(), 'timeout');\n        $pm->kill();\n    });\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n    $http->set(['log_file' => '/dev/null']);\n\n    $timerTable = new Swoole\\Table(250);\n    $timerTable->column('worker_pid', Swoole\\Table::TYPE_INT);\n    $timerTable->column('time', Swoole\\Table::TYPE_INT);\n    $timerTable->column('fd', Swoole\\Table::TYPE_INT);\n    $timerTable->create();\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on('start', function ($server) use ($timerTable) {\n        Swoole\\Timer::tick(500, function ($id) use ($timerTable, $server) {\n            foreach ($timerTable as $workerId => $row) {\n                if ((time() - $row['time']) > 3) {\n                    $timerTable->del($workerId);\n                    $newRes = Swoole\\Http\\Response::create($server, $row['fd']);;\n                    if ($newRes) {\n                        Swoole\\Timer::clear($id);\n                        $newRes->status(408);\n                        $newRes->end('timeout');\n                        Swoole\\Process::kill($row['worker_pid'], 9);\n                        return;\n                    }\n                }\n            }\n        });\n    });\n\n    $http->on('Request', function ($request, $response) use ($http, $timerTable) {\n        $timerTable->set($http->getWorkerId(), [\n            'worker_pid' => $http->getWorkerPid(),\n            'time' => time(),\n            'fd' => $request->fd,\n        ]);\n        sleep(10);\n        $response->end('Hello');\n        $timerTable->del($http->getWorkerId());\n    });\n\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/pipeline.phpt",
    "content": "--TEST--\nswoole_http_server: http pipeline\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst N = 10;\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort(), 1)) {\n        exit(\"connect failed. Error: {$client->errCode}\\n\");\n    }\n\n    $host = 'localhost';\n    foreach (range(1, N) as $_) {\n        $get = http_build_query(['a' => str_repeat('A', rand(1024, 4096)), 'b' => 3.1415926]);\n        $CR = \"\\r\";\n        $data = <<<HTTP\nGET /?{$get} HTTP/1.1{$CR}\nHost: {$host}{$CR}\nConnection: closed{$CR}\nAccept: */*{$CR}\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36{$CR}\n{$CR}\\n\nHTTP;\n        $client->send($data);\n    }\n\n    $html = '';\n    while (1) {\n        $data = $client->recv();\n        if (!$data) {\n            echo \"ERROR\\n\";\n            break;\n        }\n        $html .= $data;\n        if (substr_count($html, \"HTTP/1.1 200 OK\") == N) {\n            break;\n        }\n    }\n\n    $pm->kill();\n    Assert::same(substr_count($html, \"HTTP/1.1 200 OK\"), N);\n    Assert::same(substr_count($html, \"swoole-http-server\"), N);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['log_file' => '/dev/null']);\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end(var_dump_return($request->get, $request->server));\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/post.phpt",
    "content": "--TEST--\nswoole_http_server: post\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$html = base64_encode(random_bytes(rand(2048, 65536 * 2)));\n\n$pm->parentFunc = function ($pid) use ($pm, $html) {\n    Co\\run(function () use ($pm, $html) {\n        $index = rand(8192, strlen($html) - 8192);\n        $reqData = [\n            'data1' => substr($html, 0, $index),\n            'data2' => substr($html, $index)\n        ];\n        $resp = httpPost(\"http://127.0.0.1:{$pm->getFreePort()}/\", $reqData);\n        Assert::assert($resp);\n        $respData = json_decode($resp, true);\n        Assert::same($respData['data1'], $reqData['data1']);\n        Assert::same($respData['data2'], $reqData['data2']);\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $html) {\n    $mode = SERVER_MODE_RANDOM;\n    $serv = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), $mode);\n    $serv->set([\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('request', function ($req, $resp) use ($html) {\n        $resp->end(json_encode($req->post));\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/purge_method.phpt",
    "content": "--TEST--\nswoole_http_server: PURGE method\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$html = '<h1>Purged</h1>';\n\n$pm->parentFunc = function ($pid) use ($pm, $html) {\n    go(function () use ($pm, $html) {\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\", ['method' => 'PURGE']);\n        Assert::same($data, $html);\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm, $html) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n    ]);\n    $http->on('workerStart', function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function ($request, $response) use ($html) {\n        if ($request->server['request_method'] == 'PURGE') {\n            $response->end($html);\n            return;\n        }\n        $response->end();\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/range.phpt",
    "content": "--TEST--\nswoole_http_server: range\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    foreach ([false, true] as $http2) {\n        Swoole\\Coroutine\\run(function () use ($pm, $http2) {\n            $data2 = file_get_contents(TEST_IMAGE);\n\n            // range\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-15']]);\n            Assert::same($response['statusCode'], 206);\n            Assert::same(bin2hex($response['body']), bin2hex(substr($data2, 0, 16)));\n            Assert::same('bytes 0-15/218787', $response['headers']['content-range']);\n            $lastModified = $response['headers']['last-modified'] ?? null;\n            Assert::notNull($lastModified);\n            Assert::null($response['headers']['accept-ranges'] ?? null);\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-']]);\n            Assert::same($response['statusCode'], 206);\n            Assert::same('bytes 0-218786/218787', $response['headers']['content-range']);\n            Assert::same(bin2hex($response['body']), bin2hex($data2));\n            // exit;\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'headers' => ['Range' => 'bytes=16-31']]);\n            Assert::same($response['statusCode'], 206);\n            Assert::same('bytes 16-31/218787', $response['headers']['content-range']);\n            Assert::same(bin2hex($response['body']), bin2hex(substr($data2, 16, 16)));\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'headers' => ['Range' => 'bytes=-16']]);\n            Assert::same($response['statusCode'], 206);\n            Assert::same('bytes 218771-218786/218787', $response['headers']['content-range']);\n            Assert::same(bin2hex($response['body']), bin2hex(substr($data2, -16)));\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'headers' => ['Range' => 'bytes=128-']]);\n            Assert::same($response['statusCode'], 206);\n            Assert::same('bytes 128-218786/218787', $response['headers']['content-range']);\n            Assert::same(bin2hex($response['body']), bin2hex(substr($data2, 128)));\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-0,-1']]);\n            Assert::same($response['statusCode'], 206);\n            Assert::isEmpty($response['headers']['content-range'] ?? null);\n            Assert::notEq(preg_match('/multipart\\/byteranges; boundary=(.+)/', $response['headers']['content-type'] ?? '', $matches), false);\n            $boundary = $matches[1];\n            $expect = sprintf(<<<BIN\n--{$boundary}\nContent-Type: image/jpeg\nContent-Range: bytes 0-0/218787\n\n%s\n--{$boundary}\nContent-Type: image/jpeg\nContent-Range: bytes 218786-218786/218787\n\n%s\n--{$boundary}--\n\nBIN\n            , substr($data2, 0, 1), substr($data2, -1));\n            $expect = str_replace(PHP_EOL, \"\\r\\n\", $expect);\n            Assert::same(bin2hex($expect), bin2hex($response['body']));\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-15,32-63']]);\n            Assert::same($response['statusCode'], 206);\n            Assert::notEq(preg_match('/multipart\\/byteranges; boundary=(.+)/', $response['headers']['content-type'] ?? '', $matches), false);\n            $boundary = $matches[1];\n            $expect = sprintf(<<<BIN\n--{$boundary}\nContent-Type: image/jpeg\nContent-Range: bytes 0-15/218787\n\n%s\n--{$boundary}\nContent-Type: image/jpeg\nContent-Range: bytes 32-63/218787\n\n%s\n--{$boundary}--\n\nBIN\n            , substr($data2, 0, 16), substr($data2, 32, 32));\n            $expect = str_replace(PHP_EOL, \"\\r\\n\", $expect);\n            Assert::same(bin2hex($expect), bin2hex($response['body']));\n\n            // if-range\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-15', 'If-Range' => $lastModified]]);\n            Assert::same($response['statusCode'], 206);\n            Assert::same(bin2hex($response['body']), bin2hex(substr($data2, 0, 16)));\n\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-15', 'If-Range' => 'test']]);\n            Assert::same($response['statusCode'], 206);\n            Assert::same(bin2hex($response['body']), bin2hex(substr($data2, 0, 16)));\n\n            $lastModifiedTime = strtotime($lastModified);\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-15', 'If-Range' => date(DATE_RFC7231, $lastModifiedTime - 1)]]);\n            Assert::same($response['statusCode'], 200);\n            Assert::same(bin2hex($response['body']), bin2hex($data2));\n\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'headers' => ['Range' => 'bytes=0-15', 'If-Range' => date(DATE_RFC7231, $lastModifiedTime + 1)]]);\n            Assert::same($response['statusCode'], 200);\n            Assert::same(bin2hex($response['body']), bin2hex($data2));\n\n            // head\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'method' => 'HEAD']);\n            Assert::same($response['statusCode'], 200);\n            Assert::isEmpty($response['body']);\n            Assert::same($response['headers']['accept-ranges'], 'bytes');\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'method' => 'HEAD', 'headers' => ['Range' => 'bytes=0-15']]);\n            Assert::same($response['statusCode'], 206);\n            Assert::same('bytes 0-15/218787', $response['headers']['content-range']);\n            Assert::isEmpty($response['body']);\n            Assert::null($response['headers']['accept-ranges'] ?? null);\n\n            // data boundary\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'headers' => ['Range' => 'abc']]);\n            Assert::same($response['statusCode'], 200);\n            Assert::same(bin2hex($response['body']), bin2hex($data2));\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'headers' => ['Range' => 'bytes=abc']]);\n            Assert::same($response['statusCode'], 416);\n            Assert::isEmpty($response['body']);\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'headers' => ['Range' => 'bytes=-999999']]);\n            Assert::same($response['statusCode'], 206);\n            Assert::same(bin2hex($response['body']), bin2hex($data2));\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'headers' => ['Range' => 'bytes=999999']]);\n            Assert::same($response['statusCode'], 416);\n            Assert::isEmpty($response['body']);\n        });\n    }\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    Assert::true(swoole_mime_type_add('moc', 'application/x-mocha'));\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'enable_static_handler' => true,\n        'document_root' => dirname(dirname(__DIR__)) . '/examples/',\n        'static_handler_locations' => ['/static', '/']\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) use ($http) {\n        $response->end('hello world');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/range2.phpt",
    "content": "--TEST--\nswoole_http_server: range - confusing header\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    foreach ([false, true] as $http2) {\n        Swoole\\Coroutine\\run(function () use ($pm, $http2) {\n            $data2 = file_get_contents(TEST_IMAGE);\n\n            // range\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2, 'headers' => ['-Range' => 'none', 'Range' => 'bytes=0-15']]);\n            Assert::same($response['statusCode'], 206);\n        });\n    }\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    Assert::true(swoole_mime_type_add('moc', 'application/x-mocha'));\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'enable_static_handler' => true,\n        'document_root' => dirname(dirname(__DIR__)) . '/examples/',\n        'static_handler_locations' => ['/static', '/']\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) use ($http) {\n        $response->end('hello world');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/rawContent.phpt",
    "content": "--TEST--\nswoole_http_server: raw-content\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse function Co\\run;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        $randomData = get_safe_random();\n        $httpClient = new Co\\http\\Client(HTTP_SERVER_HOST, $pm->getFreePort(), false);\n        $httpClient->setMethod(\"POST\");\n        $httpClient->setData($randomData);\n\n        $ok = $httpClient->execute(\"/rawContent\");\n        Assert::assert($ok);\n        Assert::same($httpClient->statusCode, 200);\n        Assert::same($httpClient->errCode, 0);\n        Assert::same($httpClient->body, $randomData);\n\n        $ok = $httpClient->execute(\"/getContent\");\n        Assert::assert($ok);\n        Assert::same($httpClient->statusCode, 200);\n        Assert::same($httpClient->errCode, 0);\n        Assert::same($httpClient->body, $randomData);\n        $pm->kill();\n        echo \"DONE\\n\";\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['worker_num' => 1]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        if ($request->server['request_uri'] === '/rawContent') {\n            $response->end($request->rawContent());\n        } else {\n            $response->end($request->getContent());\n        }\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/rawCookie.phpt",
    "content": "--TEST--\nswoole_http_server: raw-cookie\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$simple_http_server = __DIR__ . \"/../include/api/swoole_http_server/simple_http_server.php\";\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function() use ($pm) {\n        $httpClient = new Co\\http\\Client(HTTP_SERVER_HOST, $pm->getFreePort(), false);\n        $httpClient->setMethod(\"POST\");\n        $httpClient->setData(\"HELLO\");\n        $ok = $httpClient->execute(\"/rawcookie\");\n        Assert::assert($ok);\n        Assert::same($httpClient->statusCode, 200);\n        Assert::same($httpClient->errCode, 0);\n        Assert::same($httpClient->body, \"Hello World!\");\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFunc = function () use ($pm, $simple_http_server) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['worker_num' => 1]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $name = \"name\";\n        $value = \"value\";\n        // $expire = $request->swoole_server[\"request_time\"] + 3600;\n        $expire = 0;\n        $path = \"/\";\n        $domain = \"\";\n        $secure = false;\n        $httpOnly = true;\n        // string $name [, string $value = \"\" [, int $expire = 0 [, string $path = \"\" [, string $domain = \"\" [, bool $secure = false [, bool $httponly = false ]]]]]]\n        $response->cookie($name, $value, $expire, $path, $domain, $secure, $httpOnly);\n        $expect = \"name=value; path=/; HttpOnly\";\n        Assert::assert(in_array($expect, $response->cookie, true));\n        $response->cookie($name, $value, $expire, $path, $domain, $secure, $httpOnly);\n        $response->rawcookie(\"rawcontent\", $request->rawcontent());\n        $response->end(\"Hello World!\");\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/redirect.phpt",
    "content": "--TEST--\nswoole_http_server: http redirect\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/nonexistent\");\n        Assert::assert(!empty($data));\n        Assert::assert(md5($data) === md5_file(TEST_IMAGE));\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n\n    $http->set([\n        'log_file' => '/dev/null',\n        'enable_static_handler' => true,\n        'document_root' => dirname(dirname(__DIR__)) . '/examples/',\n    ]);\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function ($request, Swoole\\Http\\Response $response) {\n        if ($request->server['path_info'] == '/nonexistent') {\n            $response->redirect('/test.jpg');\n        }\n    });\n\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/reset_concurrency_with_base.phpt",
    "content": "--TEST--\nswoole_http_server: reset concurrency [SWOOLE_BASE]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Table;\nuse Swoole\\Atomic;\nuse function Swoole\\Coroutine\\run;\n\nconst N = 64;\n\n$counter = new Atomic(0);\n$table = new Table(1024);\n$table->column('pid', Table::TYPE_INT);\n$table->create();\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $n = N;\n        $coroutines = [];\n        while ($n--) {\n            $coroutines[] = go(function () use ($pm) {\n                $client = new Client('127.0.0.1', $pm->getFreePort());\n                $client->set(['timeout' => 10]);\n                Assert::eq($client->get('/'), false);\n                Assert::eq($client->getStatusCode(), SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET);\n            });\n        }\n\n        Co::join($coroutines);\n        Co::sleep(0.1);\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        Assert::assert($client->get('/'));\n        $stats = json_decode($client->getBody());\n        Assert::eq($stats->concurrency, 1);\n        $pm->kill();\n        echo \"DONE\\n\";\n    });\n};\n\n$pm->childFunc = function () use ($pm, $counter, $table) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'worker_num' => 4,\n        'max_concurrency' => 160,\n        'log_file' => '/dev/null',\n    ]);\n    $http->on('workerStart', function ($server, $wid) use ($pm, $table) {\n        if ($wid === 0) {\n            $pm->wakeup();\n        }\n        $pid = posix_getpid();\n        $table->set('worker_' . $wid, ['pid' => $pid]);\n        // echo \"Worker #{$wid}(pid=$pid) is started\\n\";\n    });\n    $http->on('request', function (Request $request, Response $response) use ($http, $counter, $table) {\n        $c = $counter->add();\n        if ($c < N) {\n            Co::sleep(100);\n        } elseif ($c == N) {\n            $stats = $http->stats();\n            Assert::eq($stats['concurrency'], N);\n            $pid = posix_getpid();\n            foreach ($table as $val) {\n                if ($val['pid'] !== $pid) {\n                    posix_kill($val['pid'], SIGKILL);\n                }\n            }\n            posix_kill($pid, SIGKILL);\n        } else {\n            $stats = $http->stats();\n            Assert::eq($stats['concurrency'], 1);\n            $response->end(json_encode($stats));\n        }\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/reset_concurrency_with_process.phpt",
    "content": "--TEST--\nswoole_http_server: reset concurrency [SWOOLE_PROCESS]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Table;\nuse Swoole\\Atomic;\nuse function Swoole\\Coroutine\\run;\n\nconst N = 64;\n\n$counter = new Atomic(0);\n$table = new Table(1024);\n$table->column('pid', Table::TYPE_INT);\n$table->create();\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $n = N;\n        $coroutines = [];\n        while ($n--) {\n            $coroutines[] = go(function () use ($pm) {\n                $client = new Client('127.0.0.1', $pm->getFreePort());\n                $client->set(['timeout' => 10]);\n                Assert::eq($client->get('/'), false);\n                Assert::eq($client->getStatusCode(), SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET);\n            });\n        }\n\n        Co::sleep(0.1);\n        $pm->wait();\n\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        Assert::assert($client->get('/'));\n        $stats = json_decode($client->getBody());\n        Assert::eq($stats->concurrency, 1);\n\n        /**\n         * PROCESS 模式下 Worker 进程退出时连接不会被关闭，这与 BASE 模式不同，因此需要先关闭服务器，其他正在运行的协程才会获得返回值\n         */\n        $pm->kill();\n\n        Co::join($coroutines);\n        echo \"DONE\\n\";\n    });\n};\n\n$pm->childFunc = function () use ($pm, $counter, $table) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set([\n        'worker_num' => 4,\n        'max_concurrency' => 160,\n        'log_file' => '/dev/null',\n    ]);\n    $http->on('workerStart', function ($server, $wid) use ($pm, $table) {\n        if ($wid === 0) {\n            $pm->wakeup();\n        }\n        $pid = posix_getpid();\n        $table->set('worker_' . $wid, ['pid' => $pid]);\n        // echo \"Worker #{$wid}(pid=$pid) is started\\n\";\n    });\n    $http->on('request', function (Request $request, Response $response) use ($http, $counter, $table) {\n        $c = $counter->add();\n        if ($c < N) {\n            Co::sleep(100);\n        } elseif ($c == N) {\n            $stats = $http->stats();\n            Assert::eq($stats['concurrency'], N);\n            $pid = posix_getpid();\n            foreach ($table as $val) {\n                if ($val['pid'] !== $pid) {\n                    posix_kill($val['pid'], SIGKILL);\n                }\n            }\n            posix_kill($pid, SIGKILL);\n        } else {\n            $stats = $http->stats();\n            Assert::eq($stats['concurrency'], 1);\n            $response->end(json_encode($stats));\n        }\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/response_create.phpt",
    "content": "--TEST--\nswoole_http_server: response create\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Constant;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(\n        function () use ($pm) {\n            $body = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}\", ['timeout' => 0.1]);\n            Assert::eq($body, 'hello world');\n            $pm->kill();\n        }\n    );\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([Constant::OPTION_LOG_FILE => '/dev/null']);\n    $serv->on(\n        Constant::EVENT_WORKER_START,\n        function () use ($pm) {\n            $pm->wakeup();\n        }\n    );\n    $serv->on(\n        'Receive',\n        function ($serv, $fd, $tid, $data) {\n            $resp = Swoole\\Http\\Response::create($serv, $fd);\n            $resp->end(\"hello world\");\n        }\n    );\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/send_500_to_client.phpt",
    "content": "--TEST--\nswoole_http_server: When the process restarts, send a 500 status code to the clients waiting in the queue\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Coroutine\\WaitGroup;\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nuse Swoole\\Coroutine\\Http\\Client;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Runtime::setHookFlags(SWOOLE_HOOK_ALL);\n    $statusCodes = [];\n    run(function () use ($pm, &$statusCodes) {\n        $waitGroup = new WaitGroup();\n        $index = 0;\n        go(function () use ($waitGroup, $pm, &$index, &$statusCodes) {\n            $waitGroup->add();\n            $client = new Client('127.0.0.1', $pm->getFreePort());\n            $client->set(['timeout' => 15]);\n            $client->get('/?id=' . $index++);\n            if (!isset($statusCodes[$client->statusCode])) {\n                $statusCodes[$client->statusCode] = 0;\n            }\n            $statusCodes[$client->statusCode]++;\n            $waitGroup->done();\n        });\n\n        sleep(1);\n\n        for ($i = 0; $i < 10; $i++) {\n            go(function () use ($waitGroup, $pm, &$index, &$statusCodes) {\n                $waitGroup->add();\n                $client = new Client('127.0.0.1', $pm->getFreePort());\n                $client->set(['timeout' => 15]);\n                $client->get('/?id=' . $index++);\n                if (!isset($statusCodes[$client->statusCode])) {\n                    $statusCodes[$client->statusCode] = 0;\n                }\n                $statusCodes[$client->statusCode]++;\n                $waitGroup->done();\n            });\n        }\n\n        $waitGroup->wait();\n        $pm->kill();\n    });\n    Assert::greaterThanEq($statusCodes[503], 8);\n    Assert::lessThanEq($statusCodes[200], 3);\n    echo 'DONE';\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'enable_coroutine' => true,\n        'worker_max_concurrency' => 1,\n        'max_wait_time' => 10,\n        'reload_async' => true,\n        'hook_flags' => SWOOLE_HOOK_ALL,\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) use ($http) {\n        $http->reload();\n        sleep(2);\n        $response->end(\"id=\" . $request->get['id']);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/send_empty_file.phpt",
    "content": "--TEST--\nswoole_http_server: send empty file\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst TMP_FILE = '/tmp/sendfile.txt';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        file_put_contents(TMP_FILE, '');\n        $recv_file = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}\");\n        unlink(TMP_FILE);\n        Assert::same($recv_file, '');\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['worker_num' => 1]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->sendfile(TMP_FILE);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/send_yield.phpt",
    "content": "--TEST--\nswoole_http_server: http chunk with send yield\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nconst ONE_MEGABYTES = 1024 * 1024;\nforeach ([SWOOLE_BASE, SWOOLE_PROCESS] as $mode) {\n    $pm = new ProcessManager;\n    $pm->initRandomData(1, 64 * ONE_MEGABYTES);\n    $pm->parentFunc = function ($pid) use ($pm) {\n        Swoole\\Coroutine\\run(function () use ($pm) {\n            $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n            Assert::assert($data === $pm->getRandomData());\n            phpt_var_dump(strlen($data));\n        });\n        $pm->kill();\n        echo \"DONE\\n\";\n    };\n    $pm->childFunc = function () use ($pm, $mode) {\n        $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), $mode);\n        $http->set([\n            'log_file' => '/dev/null',\n            'send_yield' => true,\n            'http_compression' => false\n        ]);\n        $http->on('workerStart', function () use ($pm) {\n            $pm->wakeup();\n        });\n        $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n            $data = $pm->getRandomData();\n            $data_len = strlen($data);\n            $offset = 0;\n            do {\n                $send_bytes = min($data_len - $offset, ONE_MEGABYTES);\n                Assert::assert($response->write(substr($data, $offset, $send_bytes)) === true);\n                $offset += $send_bytes;\n            } while ($offset < $data_len);\n            $response->end();\n        });\n        $http->start();\n    };\n    $pm->childFirst();\n    $pm->run();\n}\n?>\n--EXPECT--\nDONE\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/sendfile.phpt",
    "content": "--TEST--\nswoole_http_server: sendfile\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($i = MAX_REQUESTS; $i--;) {\n        $send_file = get_safe_random(mt_rand(0, 65535 * 10));\n        file_put_contents('/tmp/sendfile.txt', $send_file);\n        $recv_file = file_get_contents(\"http://127.0.0.1:{$pm->getFreePort()}\");\n        Assert::same($send_file, $recv_file);\n    }\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->header('Content-Type', 'application/octet-stream');\n        $response->header('Content-Disposition', 'attachment; filename=recvfile.txt');\n        $response->sendfile('/tmp/sendfile.txt');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/sendfile_client_reset.phpt",
    "content": "--TEST--\nswoole_http_server: client reset when sending file\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Request;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Runtime;\n\nconst TMP_FILE = '/tmp/sendfile.txt';\n$send_file = get_safe_random(mt_rand(0, rand(16, 32) * 1024 * 1024) + rand(1024, 65536));\nfile_put_contents(TMP_FILE, $send_file);\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_ALL);\n    Co\\run(function () use ($pm) {\n        $client = new Client(SWOOLE_SOCK_TCP);\n        $client->set(['socket_buffer_size' => 128 * 1024]);\n        Assert::true($client->connect('127.0.0.1', $pm->getFreePort()));\n        $client->send(\"GET / HTTP/1.1\\r\\nHost: localhost\\r\\n\\r\\n\");\n        $resp = '';\n\n        Co\\go(function () use ($pm) {\n            $file = file_get_contents('http://127.0.0.1:' . $pm->getFreePort());\n            Assert::eq(md5_file(TMP_FILE), md5($file));\n        });\n\n        while (true) {\n            $data = $client->recv();\n            System::sleep(0.01);\n            Assert::notEmpty($data);\n            $resp .= $data;\n            if (strlen($resp) > 2 * 1024 * 1024) {\n                $client->close();\n                break;\n            }\n        }\n    });\n\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) {\n        $response->header('Content-Type', 'application/octet-stream');\n        $response->header('Content-Disposition', 'attachment; filename=recvfile.txt');\n        $response->sendfile(TMP_FILE);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\nunlink(TMP_FILE);\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/sendfile_dir.phpt",
    "content": "--TEST--\nswoole_http_server: sendfile dir\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $recv_file = @file_get_contents(\"http://127.0.0.1:{$pm->getFreePort()}\");\n    Assert::eq($recv_file, false);\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function ($request, $response) {\n        $filename = '/tmp';\n        $response->header('Content-Type', 'application/octet-stream', true);\n        Assert::eq(@$response->sendfile($filename), false);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/sendfile_link.phpt",
    "content": "--TEST--\nswoole_http_server: sendfile link\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst FILE = '/tmp/sendfile.txt';\nconst LINK = '/tmp/sendfile.txt.link';\n\n$send_file = get_safe_random(mt_rand(0, 65535 * 10));\nfile_put_contents(FILE, $send_file);\nlink(FILE, LINK);\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm, $send_file) {\n    $recv_file = @file_get_contents(\"http://127.0.0.1:{$pm->getFreePort()}\");\n    Assert::eq($recv_file, $send_file);\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function ($request, $response) {\n        $response->header('Content-Type', 'application/octet-stream', true);\n        Assert::eq($response->sendfile(LINK), true);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\nunlink(LINK);\nunlink(FILE);\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/sendfile_no_keepalive.phpt",
    "content": "--TEST--\nswoole_http_server: sendfile with dispatch_mode=7\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Server;\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        $n = MAX_CONCURRENCY_LOW;\n        while ($n--) {\n            go(function () use ($pm) {\n                $cli = new Client('127.0.0.1', $pm->getFreePort());\n                $cli->setHeaders(['KeepAlive' => 'off', 'Connection' => 'close']);\n                for ($i = MAX_REQUESTS_LOW; $i--;) {\n                    $cli->get('/');\n                    Assert::contains($cli->getBody(), 'swoole_http_server: sendfile with dispatch_mode=7');\n                }\n            });\n        }\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('0.0.0.0', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n    $http->set([\n        'log_file' => '/dev/null',\n        'dispatch_mode' => 7,\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->sendfile(__FILE__);\n    });\n\n    $http->on('message', function(){});\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/sendfile_with_dispatch_mode_7.phpt",
    "content": "--TEST--\nswoole_http_server: sendfile with dispatch_mode=7\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Server;\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        $n = MAX_CONCURRENCY_LOW;\n        while ($n--) {\n            go(function () use ($pm) {\n                $cli = new Client('127.0.0.1', $pm->getFreePort());\n                for ($i = MAX_REQUESTS_LOW; $i--;) {\n                    $cli->get('/');\n                    Assert::contains($cli->getBody(), 'swoole_http_server: sendfile with dispatch_mode=7');\n                }\n            });\n        }\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('0.0.0.0', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n    $http->set([\n        'log_file' => '/dev/null',\n        'dispatch_mode' => 7,\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->sendfile(__FILE__);\n    });\n\n    $http->on('message', function(){});\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/sendfile_with_ssl.phpt",
    "content": "--TEST--\nswoole_http_server: sendfile with ssl\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($i = MAX_REQUESTS; $i--;) {\n        $send_file = get_safe_random(mt_rand(0, 65535 * 10));\n        file_put_contents('/tmp/sendfile.txt', $send_file);\n\n        $ctxArr = [\n            'verify_peer' => false,\n        ];\n        $ctx = stream_context_create(['ssl' => $ctxArr]);\n        $recv_file = file_get_contents(\"https://127.0.0.1:{$pm->getFreePort()}\", false, $ctx);\n        Assert::same($send_file, $recv_file);\n    }\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->header('Content-Type', 'application/octet-stream');\n        $response->header('Content-Disposition', 'attachment; filename=recvfile.txt');\n        $response->sendfile('/tmp/sendfile.txt');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/server_addr.phpt",
    "content": "--TEST--\nswoole_http_server: add server addr\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$output = shell_exec('ip addr show');\npreg_match_all('/inet (\\d+\\.\\d+\\.\\d+\\.\\d+)\\//', $output, $matches);\n$ips = $matches[1];\n\n$pm = new ProcessManager;\n$pm->initRandomData(1);\n$pm->parentFunc = function () use ($pm, $ips) {\n    Co\\run(function () use ($pm, $ips) {\n        $body = httpGetBody(\"http://{$ips[1]}:{$pm->getFreePort()}\");\n        Assert::eq($body, 'Hello World');\n    });\n\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm, $ips) {\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm, $ips) {\n        $server = $request->server;\n        Assert::eq($server['server_addr'], $ips[1]);\n        Assert::eq($server['remote_addr'], $ips[1]);\n        Assert::true($server['server_port'] != $server['remote_port']);\n        $response->status(200, \"status\");\n        $response->end(\"Hello World\");\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/server_addr2.phpt",
    "content": "--TEST--\nswoole_http_server: add server addr2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->initRandomData(1);\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $body = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}\");\n        Assert::eq($body, 'Hello World');\n    });\n\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        $server = $request->server;\n        Assert::eq($server['server_addr'], '127.0.0.1');\n        Assert::eq($server['remote_addr'], '127.0.0.1');\n        Assert::true($server['server_port'] != $server['remote_port']);\n        $response->status(200, \"status\");\n        $response->end(\"Hello World\");\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/set_content_length.phpt",
    "content": "--TEST--\nswoole_http_server: allow setting content length header\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$data = str_repeat('a', 100);\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm, $data) {\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:\" . $pm->getFreePort() . '/');\n    curl_setopt($ch, CURLOPT_HEADER, 1);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    $response = curl_exec($ch);\n    $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);\n    $header = substr($response, 0, $header_size);\n    Assert::assert(strrpos($header, 'Content-Length: 50') > 0);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $data) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n\n    $http->on('request', function ($req, Swoole\\Http\\Response $resp) use ($data) {\n        $resp->header('Content-Type', 'application/json');\n        $resp->header('Content-Length', 50);\n        $resp->end($data);\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/shutdown_in_event_worker.phpt",
    "content": "--TEST--\nswoole_http_server: shutdown in worker process\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n    });\n    usleep(100000);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'worker_num' => 2,\n        'log_file' => '/dev/null',\n    ]);\n\n    $serv->on('ManagerStart', function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $serv->on('workerStop', function ($server) {\n        echo \"worker exit\\n\";\n    });\n\n    $serv->on('Request', function (Request $request, Response $response) use ($serv, $pm) {\n        $response->end('Hello Swoole');\n        $serv->shutdown();\n        $pm->wakeup();\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nworker exit\nworker exit\n"
  },
  {
    "path": "tests/swoole_http_server/shutdown_in_task_worker.phpt",
    "content": "--TEST--\nswoole_http_server: shutdown in task process\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n    });\n    usleep(100000);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'worker_num' => 2,\n        'task_worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n\n    $serv->on('ManagerStart', function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $serv->on('workerStop', function ($server) {\n        echo \"worker exit\\n\";\n    });\n\n    $serv->on('Task', function ($server, $taskId, $workerId, $data) use ($pm) {\n        $server->shutdown();\n        $pm->wakeup();\n    });\n\n    $serv->on('Request', function (Request $request, Response $response) use ($serv) {\n        $response->end('Hello Swoole');\n        $serv->task('a');\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nworker exit\nworker exit\nworker exit\n"
  },
  {
    "path": "tests/swoole_http_server/slow_client.phpt",
    "content": "--TEST--\nswoole_http_server: slow client\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$pm = new ProcessManager;\n$pm->initFreePorts();\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n    $client->connect(\"127.0.01\", $pm->getFreePort());\n    $html = base64_encode(random_bytes(rand(1024, 65536)));\n    $len = strlen($html);\n    $data = \"POST /index.html HTTP/1.1\\r\\nServer: nginx\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: $len\\r\\nX-Server: swoole\\r\\n\\r\\n$html\";\n    $chunks = str_split($data, rand(5, 255));\n    foreach ($chunks as $out) {\n        $client->send($out);\n        usleep(100);\n    }\n\n    $data = $client->recv();\n    Assert::stringNotEmpty($data);\n    Assert::true(swoole_string($data)->contains('HTTP/1.1 200 OK'));\n    $pm->kill();\n    echo \"OK\\n\";\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n\n    $http->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 1,\n    ]);\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Request $request, Response $response) {\n        Assert::same($request->header['server'], 'nginx');\n        Assert::same($request->header['x-server'], 'swoole');\n        Assert::same($request->header['content-type'], 'text/html');\n        Assert::eq($request->header['content-length'], strlen($request->getContent()));\n        $response->end(\"OK\");\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_server/slow_large_post.phpt",
    "content": "--TEST--\nswoole_http_server: slow large post\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\nconst N1 = 2048;\nconst N2 = 8192;\nconst KEY = 'test_key';\ndefine('VALUE', random_bytes(N1 + N2));\n\n$pm = new ProcessManager;\n$pm->initFreePorts();\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n    $client->connect(\"127.0.0.1\", $pm->getFreePort());\n    $post_data = KEY . '=' . urlencode(VALUE);\n    $len = strlen($post_data);\n    $data = \"POST /index.html HTTP/1.1\\r\\nServer: nginx\\r\\nContent-Type: application/x-www-form-urlencoded\\r\\nConnection: close\\r\\nContent-Length: $len\\r\\nX-Server: swoole\\r\\n\\r\\n$post_data\";\n\n    $client->send(substr($data, 0, N1));\n    usleep(30000);\n    $client->send(substr($data, N1, N2));\n    usleep(30000);\n    $client->send(substr($data, N1 + N2));\n\n    $data = $client->recv();\n    Assert::stringNotEmpty($data);\n    Assert::true(swoole_string($data)->contains('HTTP/1.1 200 OK'));\n    $pm->kill();\n    echo \"OK\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n\n    $http->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 1,\n    ]);\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Request $request, Response $response) {\n        Assert::same($request->header['server'], 'nginx');\n        Assert::same($request->header['x-server'], 'swoole');\n        Assert::same($request->header['content-type'], 'application/x-www-form-urlencoded');\n        Assert::eq($request->header['content-length'], strlen($request->getContent()));\n        Assert::eq(VALUE, $request->post[KEY]);\n        $response->end(\"OK\");\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_server/sni/server.phpt",
    "content": "--TEST--\nswoole_http_server/sni: server\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Server;\n\n$pm = new ProcessManager;\n$pm->useConstantPorts = true;\n\n$pm->parentFunc = function () use ($pm) {\n    $flags = STREAM_CLIENT_CONNECT;\n    $ctxArr = [\n        'cafile' => __DIR__ . '/sni_server_ca.pem',\n        'capture_peer_cert' => true,\n        'verify_peer' => false,\n    ];\n\n    $port = $pm->getFreePort();\n    $ctxArr['peer_name'] = 'cs.php.net';\n    $ctx = stream_context_create(['ssl' => $ctxArr]);\n    $client = stream_socket_client(\"tls://127.0.0.1:$port\", $errno, $errstr, 1, $flags, $ctx);\n    $cert = stream_context_get_options($ctx)['ssl']['peer_certificate'];\n    var_dump(openssl_x509_parse($cert)['subject']['CN']);\n\n    $ctxArr['peer_name'] = 'uk.php.net';\n    $ctx = stream_context_create(['ssl' => $ctxArr]);\n    $client = @stream_socket_client(\"tls://127.0.0.1:$port\", $errno, $errstr, 1, $flags, $ctx);\n    $cert = stream_context_get_options($ctx)['ssl']['peer_certificate'];\n    var_dump(openssl_x509_parse($cert)['subject']['CN']);\n\n    $ctxArr['peer_name'] = 'us.php.net';\n    $ctx = stream_context_create(['ssl' => $ctxArr]);\n    $client = @stream_socket_client(\"tls://127.0.0.1:$port\", $errno, $errstr, 1, $flags, $ctx);\n    $cert = stream_context_get_options($ctx)['ssl']['peer_certificate'];\n    var_dump(openssl_x509_parse($cert)['subject']['CN']);\n\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $http->set([\n        'log_file' => '/dev/null',\n        'ssl_cert_file' => SSL_FILE_DIR.'/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR.'/server.key',\n        'ssl_protocols' => SWOOLE_SSL_TLSv1_2 | SWOOLE_SSL_TLSv1_3 | SWOOLE_SSL_TLSv1_1 | SWOOLE_SSL_SSLv2,\n        'ssl_sni_certs' => [\n            \"cs.php.net\" => [\n                'ssl_cert_file' => SSL_FILE_DIR . \"/sni_server_cs_cert.pem\",\n                'ssl_key_file' => SSL_FILE_DIR . \"/sni_server_cs_key.pem\"\n            ],\n            \"uk.php.net\" => [\n                'ssl_cert_file' => SSL_FILE_DIR . \"/sni_server_uk_cert.pem\",\n                'ssl_key_file' => SSL_FILE_DIR . \"/sni_server_uk_key.pem\"\n            ],\n            \"us.php.net\" => [\n                'ssl_cert_file' => SSL_FILE_DIR . \"/sni_server_us_cert.pem\",\n                'ssl_key_file' =>  SSL_FILE_DIR . \"/sni_server_us_key.pem\",\n            ],\n        ]\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) {\n        $response->end(\"hello world\");\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nstring(%d) \"cs.php.net\"\nstring(%d) \"uk.php.net\"\nstring(%d) \"us.php.net\"\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/static_handler/locations.phpt",
    "content": "--TEST--\nswoole_http_server/static_handler: static handler with locations\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Server;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    foreach ([false, true] as $http2) {\n        Swoole\\Coroutine\\run(function () use ($pm, $http2) {\n            $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/examples/test.jpg\", ['http2' => $http2]);\n            Assert::assert(!empty($data));\n            Assert::assert(md5($data) === md5_file(TEST_IMAGE));\n\n            /**\n             * 命中location，但文件不存在，直接返回 404\n             */\n            $status = httpGetStatusCode(\"http://127.0.0.1:{$pm->getFreePort()}/examples/test2.jpg\", ['http2' => $http2]);\n            Assert::assert($status == 404);\n\n            /**\n             * 动态请求\n             */\n            $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2]);\n            Assert::assert($data == TEST_IMAGE);\n        });\n    }\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'enable_static_handler' => true,\n        'document_root' => dirname(dirname(dirname(__DIR__))) . '/',\n        'static_handler_locations' => ['/examples']\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) {\n        $response->end(TEST_IMAGE);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/static_handler/mimetype_not_exists.phpt",
    "content": "--TEST--\nswoole_http_server/static_handler: mimetype not exists\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Server;\nuse function  Swoole\\Coroutine\\Http\\get;\n\ndefine('TEST_DOCUMENT_ROOT', dirname(__DIR__, 3) . '/');\ndefine('TEST_RANDOM_BYTES', random_bytes(rand(2048, 8192)));\nconst TEST_FILE = TEST_DOCUMENT_ROOT . '/examples/exists.xyz';\n\nfile_put_contents(TEST_FILE, TEST_RANDOM_BYTES);\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        Assert::eq(get(\"http://127.0.0.1:{$pm->getFreePort()}/examples/not_exists.xyz\")->getStatusCode(), 404);\n        Assert::eq(get(\"http://127.0.0.1:{$pm->getFreePort()}/not_exists.xyz\")->getStatusCode(), 500);\n\n        $resp = get(\"http://127.0.0.1:{$pm->getFreePort()}/examples/exists.xyz\");\n        Assert::eq($resp->getBody(), TEST_RANDOM_BYTES);\n        Assert::eq($resp->getHeaders()['content-type'], 'application/octet-stream');\n    });\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'enable_static_handler' => true,\n        'document_root' => TEST_DOCUMENT_ROOT,\n        'static_handler_locations' => ['/examples']\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) {\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n//unlink(TEST_FILE);\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/static_handler/read_link_2.phpt",
    "content": "--TEST--\nswoole_http_server/static_handler: link to a file outside the document root\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Server;\n\n$doc_root = __DIR__ . '/docroot';\n$image_dir = 'image/';\nmkdir($doc_root);\nmkdir($doc_root . '/' . $image_dir);\n$image_link = $doc_root . '/' . $image_dir . '/image.jpg';\nsymlink(TEST_IMAGE, $image_link);\n\n$cleanup_fn = function () use ($doc_root, $image_dir, $image_link) {\n    if (is_file($image_link)) {\n        unlink($image_link);\n    }\n    rmdir($doc_root . '/' . $image_dir);\n    rmdir($doc_root);\n};\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm, $doc_root, $image_dir, $image_link) {\n    Swoole\\Coroutine\\run(function () use ($pm, $doc_root, $image_dir, $image_link) {\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/{$image_dir}/image.jpg\");\n        Assert::assert(md5($data) === md5_file(TEST_IMAGE));\n    });\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm, $doc_root, $image_dir, $image_link) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'enable_static_handler' => true,\n        'document_root' => $doc_root,\n        'static_handler_locations' => ['/image']\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) {\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n$cleanup_fn();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/static_handler/read_link_file.phpt",
    "content": "--TEST--\nswoole_http_server/static_handler: static handler with locations\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Server;\n\nsymlink(TEST_IMAGE, TEST_LINK_IMAGE);\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Swoole\\Coroutine\\run(function () use ($pm) {\n            $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/examples/test_link.jpg\");\n            if (is_file(TEST_LINK_IMAGE)) {\n                unlink(TEST_LINK_IMAGE);\n            }\n            Assert::assert(md5($data) === md5_file(TEST_IMAGE));\n    });\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'enable_static_handler' => true,\n        'document_root' => dirname(__DIR__, 3) . '/',\n        'static_handler_locations' => ['/examples']\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) {\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/static_handler/relative_path.phpt",
    "content": "--TEST--\nswoole_http_server/static_handler: static handler with relative path\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Server;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    foreach ([false, true] as $http2) {\n        Swoole\\Coroutine\\run(function () use ($pm, $http2) {\n            $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/examples/test.jpg\", ['http2' => $http2]);\n            Assert::notEmpty($data);\n            Assert::same(md5($data), md5_file(TEST_IMAGE));\n\n            /**\n             * 命中location，但文件不存在，直接返回 404\n             */\n            $status = httpGetStatusCode(\"http://127.0.0.1:{$pm->getFreePort()}/examples/test2.jpg\", ['http2' => $http2]);\n            Assert::same($status, 404);\n\n            /**\n             * 动态请求\n             */\n            $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2]);\n            Assert::same($data, TEST_IMAGE);\n        });\n    }\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'enable_static_handler' => true,\n        'document_root' => __DIR__ . '/../../../',\n        'static_handler_locations' => ['/examples']\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) use ($http) {\n        $response->end(TEST_IMAGE);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/static_handler/relative_path_2.phpt",
    "content": "--TEST--\nswoole_http_server/static_handler: static handler with relative path [2]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Server;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    foreach ([false, true] as $http2) {\n        Swoole\\Coroutine\\run(function () use ($pm, $http2) {\n            $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/../examples/test.jpg\", ['http2' => $http2]);\n            Assert::notEmpty($data);\n            Assert::same(md5($data), md5_file(TEST_IMAGE));\n\n            $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/../docs/swoole-logo.svg\", ['http2' => $http2]);\n            Assert::eq(\"hello world\", $data);\n        });\n    }\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'enable_static_handler' => true,\n        'document_root' => SOURCE_ROOT_PATH . '/examples',\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) use ($http) {\n        $response->end(\"hello world\");\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/static_handler/relative_path_3.phpt",
    "content": "--TEST--\nswoole_http_server/static_handler: doc root with same prefix\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Server;\n\n$doc1_root = __DIR__ . '/docroot';\n$doc2_root = __DIR__ . '/docroot2';\nmkdir($doc1_root);\nmkdir($doc2_root);\nfile_put_contents($doc1_root . '/image.jpg', file_get_contents(TEST_IMAGE));\nfile_put_contents($doc2_root . '/uuid.txt', uniqid());\n\n$cleanup_fn = function () use ($doc1_root, $doc2_root) {\n    unlink($doc1_root . '/image.jpg');\n    unlink($doc2_root . '/uuid.txt');\n    rmdir($doc1_root);\n    rmdir($doc2_root);\n};\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm, $doc1_root, $doc2_root) {\n    Swoole\\Coroutine\\run(function () use ($pm, $doc1_root, $doc2_root) {\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/../docroot/image.jpg\");\n        Assert::assert(md5($data) === md5_file(TEST_IMAGE));\n\n        $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/../docroot2/uuid.txt\");\n        Assert::isEmpty($data);\n    });\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm, $doc1_root, $doc2_root) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'enable_static_handler' => true,\n        'document_root' => $doc1_root,\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) {\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n$cleanup_fn();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/static_handler/urldecode.phpt",
    "content": "--TEST--\nswoole_http_server/static_handler: http url decode (#2676)\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\n\n$pm = new ProcessManager;\n$filename = 'PHP是世界上最好的语言.txt';\n$filepath = __DIR__ . '/' . $filename;\n$content = get_safe_random();\nfile_put_contents($filepath, $content);\nregister_shutdown_function(function () use ($filepath) {\n    @unlink($filepath);\n});\n$pm->parentFunc = function () use ($pm, $filename, $content) {\n    foreach ([false, true] as $http2) {\n        Coroutine\\run(function () use ($pm, $http2, $filename, $content) {\n            $data = httpGetBody(\n                \"http://127.0.0.1:{$pm->getFreePort()}/\" . urlencode($filename),\n                ['http2' => $http2]\n            );\n            Assert::same($data, $content);\n        });\n    }\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'enable_static_handler' => true,\n        'document_root' => __DIR__\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        var_dump('never here');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/static_handler.phpt",
    "content": "--TEST--\nswoole_http_server: static file handler\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    foreach ([false, true] as $http2) {\n        Swoole\\Coroutine\\run(function () use ($pm, $http2) {\n            $data = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\", ['http2' => $http2]);\n            Assert::assert(!empty($data));\n            $data2 = file_get_contents(TEST_IMAGE);\n            for ($i = 0; $i < strlen($data); $i++) {\n                if (!isset($data2[$i])) {\n                    echo \"no index {$i}\\n\";\n                    var_dump(substr($data, $i));\n                    break;\n                }\n                if ($data[$i] != $data2[$i]) {\n                    var_dump($i);\n                    break;\n                }\n            }\n            Assert::same(md5($data), md5_file(TEST_IMAGE));\n\n            #3084\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/http/empty.txt\");\n            Assert::same($response['statusCode'], 200);\n            Assert::isEmpty($response['body']);\n\n            #3131\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/http/moc.moc\");\n            Assert::same($response['statusCode'], 200);\n            Assert::same($response['body'], file_get_contents(__DIR__ . '/../../examples/http/moc.moc'));\n            Assert::same($response['headers']['content-type'], 'application/x-mocha');\n        });\n    }\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    Assert::true(swoole_mime_type_add('moc', 'application/x-mocha'));\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n        'open_http2_protocol' => true,\n        'enable_static_handler' => true,\n        'document_root' => dirname(dirname(__DIR__)) . '/examples/',\n        'static_handler_locations' => ['/static', '/']\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) use ($http) {\n        $response->end('hello world');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/task/enable_coroutine.phpt",
    "content": "--TEST--\nswoole_http_server/task: use async io and coroutine in task process\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$randoms = [];\nfor ($n = MAX_REQUESTS; $n--;) {\n    $randoms[] = get_safe_random(mt_rand(0, 65536));\n}\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        for ($n = MAX_REQUESTS; $n--;) {\n            if (!Assert::assert(($res = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/task?n={$n}\")) === 'OK')) {\n                echo \"{$res}\\n\";\n                break;\n            }\n        }\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'log_file' => '/dev/null',\n        'task_worker_num' => 1,\n        'task_enable_coroutine' => true\n    ]);\n    $server->on('workerStart', function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($server) {\n        global $randoms;\n        $n = $request->get['n'];\n        switch ($request->server['path_info']) {\n            case '/task':\n                {\n                    list($ret_n, $ret_random) = $server->taskCo([$n], 1)[0];\n                    if ($ret_n !== $n) {\n                        $response->end(\"ERROR MATCH {$ret_n} with {$n}\");\n                        return;\n                    } elseif ($ret_random !== $randoms[$n]) {\n                        $response->end(\"ERROR EQUAL {$ret_n}(\" . strlen($ret_random) . \") with {$n}(\" . strlen($randoms[$n]) . \")\");\n                        return;\n                    }\n                    $response->end('OK');\n                    break;\n                }\n            case '/random':\n                {\n                    $response->end($randoms[$n]);\n                    break;\n                }\n        }\n    });\n    $server->on('task', function (Swoole\\Http\\Server $server, Swoole\\Server\\Task $task) use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->get(\"/random?n={$task->data}\");\n        $task->finish([$task->data, $cli->body]);\n    });\n    $server->on('finish', function () { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/task/enable_coroutine_with_wrong_usage.phpt",
    "content": "--TEST--\nswoole_http_server/task: use async io and coroutine in task process\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    usleep(100 * 1000);\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'log_file' => '/dev/null',\n        'task_worker_num' => 1,\n        'task_enable_coroutine' => true\n    ]);\n    $server->on('workerStart', function (Swoole\\Http\\Server $server, int $wid) use ($pm) {\n        if ($wid === 0) {\n            $server->taskCo(['foo'], 1);\n        }\n    });\n    $server->on('workerError', function (Swoole\\Http\\Server $server) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function () { });\n    $server->on('task', function (Swoole\\Http\\Server $server, Swoole\\Server\\Task $task) use ($pm) {\n        $server->finish('bar');\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nFatal error: Swoole\\Server::finish(): please use Swoole\\Server\\Task->finish instead when task_enable_coroutine is enable in %s/task/enable_coroutine_with_wrong_usage.php on line %d\n--EXPECTF_85--\nFatal error: Swoole\\Server::finish(): please use Swoole\\Server\\Task->finish instead when task_enable_coroutine is enable in %s/task/enable_coroutine_with_wrong_usage.php on line %d\nStack trace:\n#0 %s(%d): Swoole\\Server->finish('bar')\n#1 [internal function]: {closure:{closure:%s:%d}:%d}(Object(Swoole\\Http\\Server), Object(Swoole\\Server\\Task))\n#2 {main}\n"
  },
  {
    "path": "tests/swoole_http_server/task/use_object.phpt",
    "content": "--TEST--\nswoole_http_server/task: task_use_object\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Server;\nuse Swoole\\Server\\Task;\n\n$server = new Server('127.0.0.1', get_one_free_port(), SWOOLE_PROCESS);\n$server->set([\n    'log_file' => '/dev/null',\n    'task_worker_num' => 1,\n    'task_use_object' => true,\n]);\n$server->on('request', function (Request $request, Response $response) use ($server) {\n    $response->end(\"Hello Swoole\\n\");\n});\n$server->on('managerStart', function (Server $server) {\n    $server->task('');\n});\n$server->on('task', function ($_, Task $task) use ($server) {\n    var_dump(func_num_args());\n    var_dump(func_get_args()[1]);\n    Assert::same($task->flags & SWOOLE_TASK_NOREPLY, SWOOLE_TASK_NOREPLY);\n    usleep(100000);\n    $server->shutdown();\n});\n$server->start();\n?>\n--EXPECTF--\nint(2)\nobject(Swoole\\Server\\Task)#%d (%d) {\n  [\"data\"]=>\n  string(0) \"\"\n  [\"dispatch_time\"]=>\n  float(%f)\n  [\"id\"]=>\n  int(0)\n  [\"worker_id\"]=>\n  int(0)\n  [\"flags\"]=>\n  int(%d)\n}\n"
  },
  {
    "path": "tests/swoole_http_server/tmp-content-type.phpt",
    "content": "--TEST--\nswoole_http_server: tmp content-type\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire_once TESTS_LIB_PATH . '/vendor/autoload.php';\n\nuse Swoole\\Runtime;\nuse GuzzleHttp\\Client as GuzzleHttpClient;\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL);\n\nregister_shutdown_function(function (){\n   phpt_show_usage();\n});\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $client = new GuzzleHttpClient();\n        $baseUrl = 'http://127.0.0.1:' . $pm->getFreePort();\n        $res = $client->post($baseUrl . '/', [\n            'multipart' => [\n                [\n                    'name' => 'file',\n                    'contents' => fopen(__FILE__, 'r'),\n                    'filename' => basename(__FILE__),\n                    'headers' => ['Content-Type' => 'application/php-script']\n                ],\n            ],\n        ]);\n\n        $status = $res->getStatusCode();\n        $body = $res->getBody()->getContents();\n        Assert::eq($status, 200);\n        $result = json_decode($body, true);\n        Assert::eq($result['file']['name'], basename(__FILE__));\n        Assert::eq($result['file']['type'], 'application/php-script');\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'log_file' => '/dev/null',\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) use ($http) {\n        $response->end(json_encode($request->files));\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/too_many_special_chars_in_cookie.phpt",
    "content": "--TEST--\nswoole_http_server: too many special chars in cookie\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->setRandomFunc(function () {\n    static $str = '!#$&\\'()*+/:;=?@{}『』';\n    return str_shuffle(str_repeat($str, mt_rand(128, 1024) / strlen($str)));\n});\n$pm->initRandomDataEx(1, MAX_REQUESTS);\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        for ($n = MAX_REQUESTS; $n--;) {\n            Assert::assert($cli->get('/'));\n            Assert::same($cli->statusCode, 200);\n            Assert::same($cli->cookies['foo'], $pm->getRandomData());\n        }\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set(['log_file' => '/dev/null']);\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n        static $pre_cookie;\n        if ($pre_cookie) {\n            Assert::same($request->cookie['foo'], $pre_cookie);\n        }\n        $response->cookie('foo', $pre_cookie = $pm->getRandomData(), time() + 60 * 30, '/');\n        $response->end();\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/trailer.phpt",
    "content": "--TEST--\nswoole_http_server: trailer\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Swoole\\Coroutine\\run(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->get('/');\n        Assert::eq(md5('hello world'), $cli->headers['content-md5']);\n        $pm->kill();\n        echo \"DONE\\n\";\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->header('trailer', 'Content-MD5');\n        $data = 'hello world';\n        Assert::true($response->write($data));\n        Assert::true($response->trailer('Content-MD5', md5($data)));\n        Assert::true($response->end());\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/unixsocket.phpt",
    "content": "--TEST--\nswoole_http_server: http unix-socket\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    for ($c = MAX_CONCURRENCY; $c--;) {\n        go(function () use ($pm) {\n            $client = new Swoole\\Coroutine\\Client(SWOOLE_UNIX_STREAM);\n            Assert::assert($client->connect(UNIXSOCK_PATH, 0, -1));\n            for ($n = MAX_REQUESTS; $n--;) {\n                $client->send(\"GET / HTTP/1.1\\r\\n\\r\\n\");\n                list($headers, $body) = explode(\"\\r\\n\\r\\n\", @$client->recv());\n                Assert::assert(count(explode(\"\\n\", $headers)) >= 5);\n                Assert::same($body, 'Hello Swoole!');\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    echo \"SUCCESS\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server(UNIXSOCK_PATH, 0, SERVER_MODE_RANDOM, SWOOLE_UNIX_STREAM);\n    $server->set(['log_file' => '/dev/null']);\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end('Hello Swoole!');\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_http_server/unixsocket2.phpt",
    "content": "--TEST--\nswoole_http_server: http unix socket [2]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Response;\n\nconst SOCKET = __DIR__ . '/server.sock';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_UNIX_SOCKET_PATH, SOCKET);\n    curl_setopt($ch, CURLOPT_URL, \"http://localhost/?a=hello&b=12345&test=world\");\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n    $output = curl_exec($ch);\n    var_dump($output);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server(SOCKET, 0, SWOOLE_PROCESS, SWOOLE_SOCK_UNIX_STREAM);\n    $serv->set([\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('request', function ($req, Response $resp) {\n        $resp->end(json_encode($req->get, true));\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nstring(40) \"{\"a\":\"hello\",\"b\":\"12345\",\"test\":\"world\"}\"\n"
  },
  {
    "path": "tests/swoole_http_server/unset_response_header.phpt",
    "content": "--TEST--\nswoole_http_server: unset header of response\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        Assert::assert($cli->get('/'));\n        echo \"{$cli->statusCode}\\n\";\n        Assert::true(!isset($cli->headers['foo']));\n        Assert::same($cli->headers['bar'], 'Foo');\n        echo \"{$cli->body}\\n\";\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['log_file' => '/dev/null']);\n    $http->on(\"workerStart\", function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->header('Foo', 'Bar');\n        $response->status(500);\n        try {\n            unset($response->header);\n        } catch (Error $e) {\n            echo $e->getMessage() . PHP_EOL;\n            $response->header('Foo', null);\n        }\n        $response->header('Bar', 'Foo');\n        $response->end(\"just an 500 error for fun\\n\");\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nProperty header of class Swoole\\Http\\Response cannot be unset\n500\njust an 500 error for fun\n"
  },
  {
    "path": "tests/swoole_http_server/upload.phpt",
    "content": "--TEST--\nswoole_http_server: upload 01\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_function_not_exist('curl_init');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:{$pm->getFreePort()}\");\n    curl_setopt($ch, CURLOPT_HEADER, 0);\n    curl_setopt($ch, CURLOPT_POST, 1); //设置为POST方式\n    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));\n\n    $file = TEST_IMAGE;\n\n    $post_data = array('test' => str_repeat('a', 80));\n\n    if (function_exists(\"curl_file_create\")) {\n        $cfile = curl_file_create($file);\n        $post_data['file'] = $cfile;\n    } else {\n        $post_data['file'] = '@' . $file;\n    }\n\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);  //POST数据\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n    $res = curl_exec($ch);\n    Assert::assert(!empty($res));\n    Assert::same($res, md5_file($file));\n    curl_close($ch);\n\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n\n    $http->set([\n        'log_file' => '/dev/null',\n    ]);\n\n    $http->on(\"WorkerStart\", function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end(md5_file($request->files['file']['tmp_name']));\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/upload4.phpt",
    "content": "--TEST--\nswoole_http_server: upload 04\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_function_not_exist('curl_init');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\n\nconst FILENAME = \"test-+=.jpg\";\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:{$pm->getFreePort()}\");\n    curl_setopt($ch, CURLOPT_HEADER, 0);\n    curl_setopt($ch, CURLOPT_POST, 1); //设置为POST方式\n    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));\n\n    $file = TEST_IMAGE;\n\n    $post_data = array('test' => str_repeat('a', 80));\n\n    $cfile = curl_file_create($file);\n    $cfile->setPostFilename(FILENAME);\n    $post_data['file'] = $cfile;\n\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);  //POST数据\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n    $res = curl_exec($ch);\n    Assert::assert(!empty($res));\n    Assert::eq($res, FILENAME);\n    curl_close($ch);\n\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n\n    $http->set([\n        'log_file' => '/dev/null'\n    ]);\n\n    $http->on(\"WorkerStart\", function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end($request->files['file']['name']);\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/uploadFile.phpt",
    "content": "--TEST--\nswoole_http_server: upload file\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_function_not_exist('curl_init');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:{$pm->getFreePort()}\");\n    curl_setopt($ch, CURLOPT_HEADER, 0);\n    curl_setopt($ch, CURLOPT_POST, 1);\n    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Expect:']);\n\n    $post_data = ['test' => str_repeat('a', 80)];\n\n    if (function_exists(\"curl_file_create\")) {\n        $cfile = curl_file_create(TEST_IMAGE);\n        $post_data['upfile'] = $cfile;\n    } else {\n        $post_data['upfile'] = '@' . TEST_IMAGE;\n    }\n\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);  //POST数据\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n    echo curl_exec($ch);\n    curl_close($ch);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set(['log_file' => '/dev/null']);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        if (\n            empty($request->files['upfile']) ||\n            md5_file(TEST_IMAGE) !== md5_file($request->files['upfile']['tmp_name']) ||\n            !is_uploaded_file($request->files['upfile']['tmp_name'])\n        ) {\n            $response->end(\"ERROR\\n\");\n        } else {\n            $response->end(\"OK\\n\");\n        }\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_server/upload_02.phpt",
    "content": "--TEST--\nswoole_http_server: upload raw\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $sock = stream_socket_client(\"tcp://127.0.0.1:{$pm->getFreePort()}\");\n    $length = mt_rand(100000, 200000);\n    $content = \"POST / HTTP/1.1\\r\\n\" .\n        \"Host: local.swoole.com\\r\\n\" .\n        \"Content-Type: multipart/form-data; boundary=Boundary+D80E45AE1BB1E1E1\\r\\n\" .\n        \"Connection: keep-alive\\r\\n\" .\n        \"Accept: */*\\r\\n\" .\n        \"User-Agent: SCRM/1.0.1 (iPhone; iOS 10.3.3; Scale/2.00)\\r\\n\" .\n        \"Accept-Language: zh-Hans-CN;q=1\\r\\n\" .\n        \"Content-Length: {{content_length}}\\r\\n\" .\n        \"\\r\\n\" .\n        \"--Boundary+D80E45AE1BB1E1E1\\r\\n\" .\n        \"Content-Disposition: form-data; name=\\\"folder_id\\\"\\r\\n\" .\n        \"\\r\\n\" .\n        \"0\\r\\n\" .\n        \"--Boundary+D80E45AE1BB1E1E1\\r\\n\" .\n        \"Content-Disposition: form-data; name=\\\"name\\\"\\r\\n\" .\n        \"\\r\\n\" .\n        \"IMG_0941(17).png\\r\\n\" .\n        \"--Boundary+D80E45AE1BB1E1E1\\r\\n\" .\n        \"Content-Disposition: form-data; name=\\\"servers_id\\\"\\r\\n\" .\n        \"\\r\\n\" .\n        \"1000\\r\\n\" .\n        \"--Boundary+D80E45AE1BB1E1E1\\r\\n\" .\n        \"Content-Disposition: form-data; name=\\\"size\\\"\\r\\n\" .\n        \"\\r\\n\" .\n        \"103972\\r\\n\" .\n        \"--Boundary+D80E45AE1BB1E1E1\\r\\n\" .\n        \"Content-Disposition: form-data; name=\\\"token\\\"\\r\\n\" .\n        \"\\r\\n\" .\n        \"08ccbb0e11cb5cbc718c71fa7be2adfb\\r\\n\" .\n        \"--Boundary+D80E45AE1BB1E1E1\\r\\n\" .\n        \"Content-Disposition: form-data; name=\\\"type\\\"\\r\\n\" .\n        \"\\r\\n\" .\n        \"0\\r\\n\" .\n        \"--Boundary+D80E45AE1BB1E1E1\\r\\n\" .\n        \"Content-Disposition: form-data; name=\\\"file\\\"; filename=\\\"IMG_0941(17).png\\\"\\r\\n\" .\n        \"Content-Type: application/octet-stream\\r\\n\\r\\n\" .\n        \"{{file_content}}\\r\\n\" .\n        \"--Boundary+D80E45AE1BB1E1E1--\\r\\n\";\n    $content = content_hook_replace(\n        $content, [\n            'content_length' => $length,\n            'file_content' => str_repeat(get_safe_random(1), $length - 718)\n        ]\n    );\n    fwrite($sock, $content);\n    stream_set_chunk_size($sock, 2 * 1024 * 1024);\n    $data = fread($sock, 2 * 1024 * 1024);\n    Assert::assert(!empty($data));\n    $json = json_decode(explode(\"\\r\\n\\r\\n\", $data, 2)[1], true);\n    Assert::assert(is_array($json));\n    Assert::true(isset($json['file']));\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set(['log_file' => '/dev/null']);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end(json_encode($request->files + $request->post));\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/upload_03.phpt",
    "content": "--TEST--\nswoole_http_server: upload raw\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $sock = stream_socket_client(\"tcp://127.0.0.1:{$pm->getFreePort()}\");\n    $boundary = \"Boundary+D80E45AE1BB1E1E1\";\n    $body = implode(\"\\r\\n\", [\n        \"--$boundary\\r\\nContent-Disposition: form-data; name=\\\"folder_id\\\"\\r\\n\\r\\n999999955\",\n        \"--$boundary\\r\\nContent-Disposition: form-data; name=\\\"name\\\"\\r\\n\\r\\n\" . str_repeat('A', rand(100, 200)),\n        \"--$boundary--\"\n    ]);\n    $body .= \"\\r\\n\";\n    $len = strlen($body);\n    $data = implode(\"\\r\\n\", [\n        \"POST /file_service/v3/file/upload_do HTTP/1.1\",\n        \"Content-Type: multipart/form-data; boundary=$boundary; error=bad\",\n        \"Content-Length: $len\",\n        \"\\r\\n\",\n        $body,\n    ]);\n    fwrite($sock, $data);\n    stream_set_chunk_size($sock, 2 * 1024 * 1024);\n    $data = fread($sock, 2 * 1024 * 1024);\n    Assert::assert(!empty($data));\n    $json = json_decode(explode(\"\\r\\n\\r\\n\", $data, 2)[1], true);\n    Assert::assert(is_array($json));\n    Assert::true(isset($json['folder_id']));\n    Assert::true(isset($json['name']));\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n\n    $http->set(['log_file' => '/dev/null']);\n\n    $http->on(\"WorkerStart\", function ($serv, $wid) {\n        global $pm;\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end(json_encode($request->post));\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/upload_big_file.phpt",
    "content": "--TEST--\nswoole_http_server: upload big file\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_function_not_exist('curl_init');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:{$pm->getFreePort()}\");\n    curl_setopt($ch, CURLOPT_HEADER, 0);\n    curl_setopt($ch, CURLOPT_POST, 1); //设置为POST方式\n    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));\n\n    $file = TEST_IMAGE;\n\n    $post_data = array(\n        'test' => str_repeat('a', 80),\n        'hello' => base64_encode(random_bytes(rand(10, 128))),\n        'world' => base64_encode(random_bytes(rand(1024, 8192))),\n    );\n\n    if (function_exists(\"curl_file_create\")) {\n        $cfile = curl_file_create($file);\n        $post_data['file'] = $cfile;\n    } else {\n        $post_data['file'] = '@' . $file;\n    }\n\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);  //POST数据\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n\n    curl_setopt($ch, CURLOPT_TIMEOUT, 1000);\n\n    $res = curl_exec($ch);\n    Assert::assert(!empty($res));\n    Assert::same($res, md5_file($file));\n    curl_close($ch);\n\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n\n    $http->set([\n        'log_file' => '/dev/null',\n        'package_max_length' => 64 * 1024,\n        'upload_max_filesize' => 8 * 1024 * 1024,\n    ]);\n\n    $http->on(\"WorkerStart\", function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end(md5_file($request->files['file']['tmp_name']));\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/upload_file_array_default.phpt",
    "content": "--TEST--\nswoole_http_server: upload files array default format\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_function_not_exist('curl_init');\nskip_if_function_not_exist('curl_file_create');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    $formData = [\n        'file'              => curl_file_create(TEST_IMAGE, 'application/octet-stream', 'image.jpg'),\n        'form[file]'        => curl_file_create(TEST_IMAGE, 'image/jpeg', 'photo.jpg'),\n        'form[group][file]' => curl_file_create(TEST_IMAGE2, 'image/svg+xml', 'swoole-logo.svg'),\n    ];\n\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:{$pm->getFreePort()}\");\n    curl_setopt($ch, CURLOPT_HEADER, 0);\n    curl_setopt($ch, CURLOPT_POST, 1);\n    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Expect:']);\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $formData);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n    $result = curl_exec($ch);\n    curl_close($ch);\n\n    $json = json_decode($result, true);\n\n    assert_upload_file($json['file'], '/tmp/swoole.upfile.fixture1', 'image.jpg', 'application/octet-stream', filesize(TEST_IMAGE), 0);\n    assert_upload_file($json['form']['file'], '/tmp/swoole.upfile.fixture2', 'photo.jpg', 'image/jpeg', filesize(TEST_IMAGE), 0);\n    assert_upload_file($json['form']['group']['file'], '/tmp/swoole.upfile.fixture3', 'swoole-logo.svg', 'image/svg+xml', filesize(TEST_IMAGE2), 0);\n\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set([\n        'log_file' => '/dev/null',\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $files = $request->files;\n        if (!is_array($files)\n            || empty($files['file']['tmp_name'])\n            || empty($files['form']['file']['tmp_name'])\n            || empty($files['form']['group']['file']['tmp_name'])\n        ) {\n            $response->end();\n            return;\n        }\n        $files['file']['tmp_name']                  = '/tmp/swoole.upfile.fixture1';\n        $files['form']['file']['tmp_name']          = '/tmp/swoole.upfile.fixture2';\n        $files['form']['group']['file']['tmp_name'] = '/tmp/swoole.upfile.fixture3';\n        $response->end(json_encode($files));\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/upload_file_array_parsed.phpt",
    "content": "--TEST--\nswoole_http_server: upload files array parsed format\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_function_not_exist('curl_init');\nskip_if_function_not_exist('curl_file_create');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager();\n\n$pm->parentFunc = function () use ($pm) {\n    $formData = [\n        'file' => curl_file_create(TEST_IMAGE, 'application/octet-stream', 'image.jpg'),\n        'form[file]' => curl_file_create(TEST_IMAGE, 'image/jpeg', 'photo.jpg'),\n        'form[group][file]' => curl_file_create(TEST_IMAGE2, 'image/svg+xml', 'swoole-logo.svg'),\n    ];\n\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:{$pm->getFreePort()}\");\n    curl_setopt($ch, CURLOPT_HEADER, 0);\n    curl_setopt($ch, CURLOPT_POST, 1);\n    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Expect:']);\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $formData);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n    $result = curl_exec($ch);\n    curl_close($ch);\n\n    $json = json_decode($result, true);\n\n    assert_upload_file($json['file'], '/tmp/swoole.upfile.fixture1', 'image.jpg', 'application/octet-stream', filesize(TEST_IMAGE), 0);\n    assert_upload_file($json['form'], [\n        'file' => '/tmp/swoole.upfile.fixture2',\n        'group' => [\n            'file' => '/tmp/swoole.upfile.fixture3',\n        ],\n    ], [\n        'file' => 'photo.jpg',\n        'group' => [\n            'file' => 'swoole-logo.svg',\n        ],\n    ], [\n        'file' => 'image/jpeg',\n        'group' => [\n            'file' => 'image/svg+xml',\n        ],\n    ], [\n        'file' => filesize(TEST_IMAGE),\n        'group' => [\n            'file' => filesize(TEST_IMAGE2),\n        ],\n    ], [\n        'file' => 0,\n        'group' => [\n            'file' => 0,\n        ],\n    ]);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set([\n        'log_file' => '/dev/null',\n        'http_parse_files' => true,\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $files = $request->files;\n        if (!is_array($files)\n            || empty($files['file']['tmp_name'])\n            || empty($files['form']['tmp_name']['file'])\n            || empty($files['form']['tmp_name']['group']['file'])\n        ) {\n            $response->end();\n            return;\n        }\n        $files['file']['tmp_name'] = '/tmp/swoole.upfile.fixture1';\n        $files['form']['tmp_name']['file'] = '/tmp/swoole.upfile.fixture2';\n        $files['form']['tmp_name']['group']['file'] = '/tmp/swoole.upfile.fixture3';\n        $response->end(json_encode($files));\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/upload_file_empty.phpt",
    "content": "--TEST--\nswoole_http_server: upload files empty\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    $boundary = \"------------------------d3f990cdce762596\";\n    $body = implode(\"\\r\\n\", [\n        \"--$boundary\",\n        'Content-Disposition: form-data; name=\"file1\"; filename=\"empty.txt\"',\n        'Content-Type: text/plain',\n        '',\n        '',\n        \"--$boundary\",\n        'Content-Disposition: form-data; name=\"file2\"; filename=\"\"',\n        'Content-Type: application/octet-stream',\n        '',\n        '',\n        \"--$boundary--\",\n        '',\n    ]);\n    $request = implode(\"\\r\\n\", [\n        'POST / HTTP/1.1',\n        \"Content-Type: multipart/form-data; boundary=$boundary\",\n        'Content-Length: ' . strlen($body),\n        '',\n        $body,\n    ]);\n\n    $sock = stream_socket_client(\"tcp://127.0.0.1:{$pm->getFreePort()}\");\n    fwrite($sock, $request);\n    stream_set_chunk_size($sock, 2 * 1024 * 1024);\n    $response = fread($sock, 2 * 1024 * 1024);\n    fclose($sock);\n    [$header, $body] = explode(\"\\r\\n\\r\\n\", $response);\n    $json = json_decode($body, true);\n    Assert::true(is_array($json));\n    Assert::true(isset($json['file1']));\n    assert_upload_file($json['file1'], '/tmp/swoole.upfile.fixture1', 'empty.txt', 'text/plain', 0, 0);\n\n    Assert::true(isset($json['file2']));\n    assert_upload_file($json['file2'], '', '', '', 0, 4);\n\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set([\n        'log_file' => '/dev/null'\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $files = $request->files;\n        if (isset($files['file1']['tmp_name'])) {\n            $files['file1']['tmp_name'] = '/tmp/swoole.upfile.fixture1';\n        }\n        $response->end(json_encode($files));\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/upload_max_filesize.phpt",
    "content": "--TEST--\nswoole_http_server: upload max filesize\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_function_not_exist('curl_init');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, \"http://127.0.0.1:{$pm->getFreePort()}\");\n    curl_setopt($ch, CURLOPT_HEADER, 0);\n    curl_setopt($ch, CURLOPT_POST, 1); //设置为POST方式\n    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));\n\n    $file = TEST_IMAGE;\n\n    $post_data = array(\n        'test' => str_repeat('a', 80),\n        'hello' => base64_encode(random_bytes(rand(10, 128))),\n        'world' => base64_encode(random_bytes(rand(1024, 8192))),\n    );\n\n    if (function_exists(\"curl_file_create\")) {\n        $cfile = curl_file_create($file);\n        $post_data['file'] = $cfile;\n    } else {\n        $post_data['file'] = '@' . $file;\n    }\n\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);  //POST数据\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n\n    curl_setopt($ch, CURLOPT_TIMEOUT, 1000);\n\n    $res = curl_exec($ch);\n    Assert::isEmpty(($res));\n    Assert::eq(curl_getinfo($ch)['http_code'], 413);\n    curl_close($ch);\n\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n\n    $http->set([\n        'log_file' => '/dev/null',\n        'package_max_length' => 64 * 1024,\n        'upload_max_filesize' => 128 * 1024,\n    ]);\n\n    $http->on(\"WorkerStart\", function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end(md5_file($request->files['file']['tmp_name']));\n    });\n\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server/url_rewrite.phpt",
    "content": "--TEST--\nswoole_http_server: url rewrite\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$file1 = __DIR__ .'/static/3.html';\n$file2 = __DIR__ .'/static/article/settings.html';\n\nif (is_dir(__DIR__.'/static') === false) {\n    mkdir(__DIR__.'/static');\n}\n\nif (is_dir(__DIR__.'/static/article') === false) {\n    mkdir(__DIR__.'/static/article');\n}\n\nfile_put_contents($file1, 'user_john_action_settings');\nfile_put_contents($file2, 'post_99_query_foo=bar');\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm, $file1, $file2) {\n    foreach ([false, true] as $http2) {\n        Swoole\\Coroutine\\run(function () use ($pm, $http2, $file1, $file2) {\n            $options = ['http2' => $http2];\n\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/view/post/3\", $options);\n            Assert::same($response['statusCode'], 200);\n            Assert::same($response['body'], 'user_john_action_settings');\n\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/view/post/99\", $options);\n            Assert::same($response['statusCode'], 404);\n\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/article/settings.html\", $options);\n            Assert::same($response['statusCode'], 200);\n            Assert::same($response['body'], 'post_99_query_foo=bar');\n\n            $response = httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/article/not_exists.html\", $options);\n            Assert::same($response['statusCode'], 404);\n        });\n    }\n    echo \"DONE\\n\";\n    $pm->kill();\n    unlink($file1);\n    unlink($file2);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort());\n    $http->set([\n        'log_file' => '/dev/null',\n        'document_root' => __DIR__,\n        'enable_static_handler' => true,\n        'open_http2_protocol' => true,\n        'static_handler_locations' => ['/static'],\n        'url_rewrite_rules' => [\n            '~^/view/post/(\\d+)$~' => '/static/$1.html',\n            '/article/' => '/static/article/'\n        ]\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Request $request, Response $response) {\n        $response->end('dynamic_response');\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/worker_max_concurrency.phpt",
    "content": "--TEST--\nswoole_http_server: worker_max_concurrency\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Coroutine;\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\nconst SERVER_WORKER_MAX_CONCURRENCY = 4;\nconst GREETING_MESSAGE = 'hello world';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        $url = 'http://127.0.0.1:' . $pm->getFreePort(0) . '/';\n        $n = SERVER_WORKER_MAX_CONCURRENCY * 3;\n        $cid_list = [];\n        while ($n--) {\n            $cid_list[] = go(function () use ($url) {\n                $c = Swoole\\Coroutine\\Http\\get($url);\n                Assert::eq($c->getStatusCode(), 200);\n                Assert::eq($c->getBody(), GREETING_MESSAGE);\n            });\n        }\n\n        // wait\n        Coroutine::join($cid_list);\n        $c = Swoole\\Coroutine\\Http\\get($url.'stats');\n        Assert::eq($c->getStatusCode(), 200);\n        $json = json_decode($c->getBody());\n        Assert::true(isset($json->coroutine_peek_num));\n        Assert::eq($json->coroutine_peek_num, SERVER_WORKER_MAX_CONCURRENCY);\n\n        echo \"DONE\\n\";\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n//    $mode = SWOOLE_BASE;\n    $mode = SERVER_MODE_RANDOM;\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), $mode);\n    $http->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 1,\n        'worker_max_concurrency' => SERVER_WORKER_MAX_CONCURRENCY,\n    ]);\n    $http->on('start', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm, $http) {\n        if ($request->server['request_uri'] == '/stats') {\n            $response->end(json_encode($http->stats()));\n            return;\n        }\n        System::sleep(0.1);\n        $response->end(GREETING_MESSAGE);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server/zstd.phpt",
    "content": "--TEST--\nswoole_http_server: support zstd compress\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse SwooleTest\\ProcessManager;\nuse Swoole\\Coroutine\\Http\\Client;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager();\n$data = base64_encode(random_bytes(1024 * 1024));\n\n$pm->parentFunc = function ($pid) use ($pm, $data) {\n    run(function () use ($pm, $data) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->setHeaders(['Accept-Encoding' => 'zstd']);\n        $client->get('/');\n        Assert::true($client->body == $data);\n        Assert::true($client->headers['content-encoding'] == 'zstd');\n        Assert::true($client->headers['content-length'] != strlen($client->body));\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $data) {\n    $serv = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort());\n    $serv->set([\n        'compression_level' => 20\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('request', function ($req, $resp) use ($data) {\n        $resp->end($data);\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/bad_request.phpt",
    "content": "--TEST--\nswoole_http_server_coro: bad request\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Coroutine\\Socket;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\nconst REQUEST = \"GET / HTTP/1.1\\r\\nContent-Length: invalid\\r\\n\\r\\n\";\n\nCoroutine\\run(function () {\n    $server = new Server('127.0.0.1', 0);\n    Coroutine::create(function () use ($server) {\n        $server->handle('/', function (Request $request, Response $response) use ($server) {\n            echo \"never here\\n\";\n        });\n        $server->start();\n    });\n    Coroutine::sleep(0.001);\n    Coroutine::create(function () use ($server) {\n        $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        if (Assert::true($socket->connect('127.0.0.1', $server->port, -1))) {\n            $ret = $socket->sendAll(REQUEST);\n            Assert::same($ret, strlen(REQUEST));\n            Assert::contains($socket->recv(), 'HTTP/1.1 400 Bad Request');\n            $server->shutdown();\n            echo \"DONE\\n\";\n        }\n    });\n});\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/buffer_clear.phpt",
    "content": "--TEST--\nswoole_http_server_coro: websocket buffer clear\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Event;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\Http\\Server;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n\tgo(function() use ($pm) {\n\t\t$client = new Client('127.0.0.1', $pm->getFreePort());\n\t\t$ret = $client->upgrade('/');\n\t\t$client->set(['open_websocket_pong_frame' => true]);\n\t\t$client->push('hello world');\n\t\t$client->push('ping', SWOOLE_WEBSOCKET_OPCODE_PING);\n\t\t$frame1 = $client->recv();\n\t\t$frame2 = $client->recv();\n\t\tAssert::eq($frame1->data, 'received: hello world');\n\t\tAssert::eq($frame1->opcode, SWOOLE_WEBSOCKET_OPCODE_TEXT);\n\t\tAssert::eq($frame2->data, 'ping');\n\t\tAssert::eq($frame2->opcode, SWOOLE_WEBSOCKET_OPCODE_PONG);\n\t});\n\n\tEvent::wait();\n\t$pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n\tgo(function () use ($pm) {\n\t\t$server = new Server('127.0.0.1', $pm->getFreePort(), false);\n        $server->handle('/', function (Request $request, Response $response) {\n            $response->upgrade();\n\t\t\twhile ($frame = $response->recv()) {\n\t\t\t\t$response->push('received: ' . $frame->data);\n\t\t\t}\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n    Event::wait();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/bug_2682.phpt",
    "content": "--TEST--\nswoole_http_server_coro: bug 2682 getData/getRawContent\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ndefine('PORT', get_one_free_port());\ndefine(\n    'REQUEST',\n    \"POST /api HTTP/1.1\\r\\nHost: localhost\\r\\nUser-Agent: curl\\r\\nAccept: */*\\r\\nContent-Type: application/json\\r\\nContent-Length: 28\\r\\n\\r\\n{\\\"id\\\": 1,\\\"name\\\": \\\"xiaoming\\\"}\"\n);\n\nCo\\run(function () {\n    go(function () {\n        $server = new Swoole\\Coroutine\\Http\\Server('127.0.0.1', PORT, false);\n        $server->handle('/api', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($server) {\n            var_dump(str_replace(\"\\r\\n\", PHP_EOL, $request->getData()));\n            echo PHP_EOL;\n            var_dump($request->rawContent());\n            $response->end('OK');\n            Co::sleep(0.01);\n            $server->shutdown();\n        });\n        $server->start();\n    });\n    go(function () {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_TCP);\n        $client->connect('127.0.0.1', PORT);\n        $client->send(REQUEST);\n        Assert::contains($client->recv(), '200 OK');\n        $client->close();\n    });\n});\n\n?>\n--EXPECTF--\nstring(143) \"POST /api HTTP/1.1\nHost: localhost\nUser-Agent: curl\nAccept: */*\nContent-Type: application/json\nContent-Length: 28\n\n{\"id\": 1,\"name\": \"xiaoming\"}\"\n\nstring(28) \"{\"id\": 1,\"name\": \"xiaoming\"}\"\n"
  },
  {
    "path": "tests/swoole_http_server_coro/bug_3025.phpt",
    "content": "--TEST--\nswoole_http_server_coro: bug 3025 getData\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\nCoroutine\\run(function () {\n    $server = new Server('127.0.0.1', 0);\n    Coroutine::create(function () use ($server) {\n        $server->handle('/', function (Request $request, Response $response) {\n            static $length;\n            if (!isset($length)) {\n                $length = strlen($request->getData());\n            } else {\n                Assert::same(strlen($request->getData()), $length);\n            }\n        });\n        $server->start();\n    });\n    Coroutine::create(function () use ($server) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $server->port);\n        for ($n = MAX_REQUESTS; $n--;) {\n            $cli->get('/');\n        }\n        $server->shutdown();\n        echo \"DONE\\n\";\n    });\n});\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/bug_4519.phpt",
    "content": "--TEST--\nswoole_http_server_coro: bug #4519\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager;\n$pm->initFreePorts();\n\n$port = $pm->getFreePort();\n$data = str_repeat('你好你好你好', 10000);\n$length = strlen($data);\n\n$pm->parentFunc = function ($pid) use ($pm, $data, $port) {\n    run(function () use ($pm, $data, $port) {\n        $client = new Client('127.0.0.1', $port);\n        $client->setHeaders([\n            'Content-type' => 'application/x-www-form-urlencoded',\n        ]);\n        $client->post('/api', ['test' => $data]);\n        $client->close();\n        $pm->kill();\n        echo \"DONE\\n\";\n    });\n};\n\n$pm->childFunc = function () use ($pm, $length, $port) {\n    run(function () use ($pm, $length, $port) {\n        $server = new Server('127.0.0.1', $port, false);\n        $server->handle('/api', function (Request $request, Response $response) use ($length){\n            Assert::assert(sizeof($request->post) == 1 && strlen($request->post['test']) == $length);\n        });\n        Swoole\\Process::signal(SIGTERM, function () use ($server) {\n            $server->shutdown();\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/bug_no_handle.phpt",
    "content": "--TEST--\nswoole_http_server_coro: bug no handle\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Http\\Status;\nuse Swoole\\Http2;\n\nCoroutine\\run(function () {\n    $server = new Coroutine\\Http\\Server('127.0.0.1', 0);\n    Coroutine::create(function () use ($server) {\n        $server->start();\n    });\n    Coroutine::create(function () use ($server) {\n        $cli = new Coroutine\\Http\\Client('127.0.0.1', $server->port);\n        for ($n = MAX_REQUESTS; $n--;) {\n            $cli->get('/');\n            Assert::same($cli->statusCode, Status::NOT_FOUND);\n        }\n        $cli->close();\n        if (defined('SWOOLE_USE_HTTP2')) {\n            $cli = new Coroutine\\Http2\\Client('127.0.0.1', $server->port);\n            $cli->connect();\n            for ($n = MAX_REQUESTS; $n--;) {\n                $cli->send(new Http2\\Request);\n                $response = $cli->recv();\n                Assert::same($response->statusCode, Status::NOT_FOUND);\n            }\n            $cli->close();\n        }\n        $server->shutdown();\n        echo \"DONE\\n\";\n    });\n});\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/check_cookie_crlf.phpt",
    "content": "--TEST--\nswoole_http_server_coro: check if the HTTP cookie contains CRLF\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->get('/rawcookie');\n        $headers = $client->getHeaders();\n        Assert::false(isset($headers['malicious-header']));\n        Assert::false(isset($headers['set-cookie']));\n\n        $client->get('/cookie');\n        $headers = $client->getHeaders();\n        Assert::false(isset($headers['malicious-header']));\n        Assert::true(isset($headers['set-cookie']));\n\n        $client->close();\n        $pm->kill();\n        echo \"DONE\\n\";\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $server = new Server('127.0.0.1', $pm->getFreePort());\n\n        $server->handle('/rawcookie', function (Request $request, Response $response) {\n            $value = \"cn\\r\\nmalicious-header:injected\\r\\nContent-Length:27\\r\\n\\r\\n<h3>malicious response body\";\n            $response->rawcookie('lang', $value);\n            $response->end('hello world');\n        });\n\n        $server->handle('/cookie', function (Request $request, Response $response) {\n            $value = \"cn\\r\\nmalicious-header:injected\\r\\nContent-Length:27\\r\\n\\r\\n<h3>malicious response body\";\n            $response->cookie('lang', $value);\n            $response->end('hello world');\n        });\n\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nWarning: Swoole\\Http\\Response::rawcookie(): The value cannot contain \",\", \";\", \" \", \"\\t\", \"\\r\", \"\\n\", \"\\013\", or \"\\014\" in %s\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/check_http_header_crlf.phpt",
    "content": "--TEST--\nswoole_http_server_coro: check if the HTTP header contains CRLF\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->get('/?r=AAA%0d%0amalicious-header:injected');\n        $headers = $client->getHeaders();\n        Assert::false(isset($headers['malicious-header']));\n        $client->close();\n        $pm->kill();\n        echo \"DONE\\n\";\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $server = new Server('127.0.0.1', $pm->getFreePort());\n\n        $server->handle('/', function (Request $request, Response $response) {\n            $response->header('Location', $request->get['r']);\n            $response->status(302);\n            $response->end('Redirecting...');\n        });\n\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nWarning: Swoole\\Http\\Response::end(): Header may not contain more than a single header, new line detected in %s\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/chunked_pipeline_request.phpt",
    "content": "--TEST--\nswoole_http_server_coro: chunked and pipeline request\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/../include/api/http_test_cases.php';\n\nconst EOF = \"EOF\";\n\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Process;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager;\n$pm->initRandomData(1);\n$pm->parentFunc = function () use ($pm) {\n    chunked_request($pm);\n};\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $server = new Server('127.0.0.1', $pm->getFreePort(), false);\n        $server->handle('/', function (Request $request, Response $response) {\n            $response->end($request->rawContent() . EOF);\n        });\n        Process::signal(SIGTERM, function () use ($server) {\n            $server->shutdown();\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_http_server_coro/close_socket.phpt",
    "content": "--TEST--\nswoole_http_server_coro: close socket\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Coroutine\\Http\\Client;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        for ($i = 0; $i < 2; $i++) {\n            $cli = new Client('127.0.0.1', $pm->getFreePort());\n            Assert::assert($cli->get('/'));\n            Assert::assert(str_contains($cli->headers['server'], 'BWS') or str_contains($cli->headers['server'], 'bfe'));\n        }\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    Coroutine\\run(function () use ($pm) {\n        $server = new Server('127.0.0.1', $pm->getFreePort());\n        $server->handle('/', function (Request $request, Response $response) {\n            $response->detach();\n            $socket = $response->socket;\n            $headers = [];\n            $checkHttpCode = false;\n            $isHeaderSended = false;\n            $httpCode = 200;\n\n            $curl = curl_init();\n            curl_setopt($curl, CURLOPT_URL, 'https://www.baidu.com/');\n            curl_setopt($curl, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$headers, &$httpCode, &$checkHttpCode) {\n                if (!$checkHttpCode) {\n                    $checkHttpCode = true;\n                    preg_match('/HTTP\\/[0-9.]+\\s(\\d+)\\s(.*)/', $header, $matches, PREG_OFFSET_CAPTURE, 0);\n                    if (!empty($matches)) {\n                        $httpCode = $matches[1][0];\n                        return strlen($header);\n                    }\n                }\n                $content = trim($header);\n\n                if (empty($content)) return strlen($header);\n                list($key, $value) = explode(\": \", $content);\n                if (in_array(strtolower($key), ['content-length', 'transfer-encoding'])) return strlen($header);\n                $headers[$key] = $value;\n                return strlen($header);\n            });\n\n            curl_setopt($curl, CURLOPT_WRITEFUNCTION, function ($curl, $str) use ($response, &$socket, &$headers, &$isHeaderSended, &$httpCode) {\n                if (!$isHeaderSended) {\n                    $isHeaderSended = true;\n                    // $response->status($httpCode);\n                    if ($httpCode == 200) {\n                        $socket->send(\"HTTP/1.1 {$httpCode} OK\\r\\n\");\n                    } else {\n                        $socket->send(\"HTTP/1.1 {$httpCode} ERROR\\r\\n\");\n                    }\n\n                    foreach (array_merge([\n                        'Content-Type' => 'application/octet-stream',\n                        'X-Accel-Buffering' => 'no',\n                        'X-Server' => 'webserver/1.0'\n                    ], $headers) as $k => $v) {\n                        $socket->send(\"{$k}: {$v}\\r\\n\");\n                    }\n                    $socket->send(\"\\r\\n\");\n                }\n                return strlen($str);\n            });\n\n            curl_exec($curl);\n            curl_close($curl);\n\n            return $socket->close();\n        });\n\n        $server->set([\n            'http_compression' => false,\n            'http_parse_post' => false,\n            'http_parse_files' => false,\n        ]);\n        go(function () use ($server) {\n            $server->start();\n        });\n        go(function () use ($server) {\n            if (System::waitSignal(SIGTERM)) {\n                $server->shutdown();\n            }\n        });\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/compress_continue_frames.phpt",
    "content": "--TEST--\nswoole_http_server_coro: compress continue frames\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$data1 = bin2hex(random_bytes(10 * 1024));\n$data2 = bin2hex(random_bytes(20 * 2048));\n$data3 = bin2hex(random_bytes(40 * 4096));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->set([\n            'websocket_compression' => true\n        ]);\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n        $context = deflate_init(ZLIB_ENCODING_RAW);\n        $client->push(deflate_add($context, $data1, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_COMPRESS | SWOOLE_WEBSOCKET_FLAG_RSV1);\n        $client->push(deflate_add($context, $data2, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $client->push(deflate_add($context, $data3, ZLIB_FINISH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n        $frame = $client->recv();\n        Assert::true($frame->data == $data1 . $data2 . $data3);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $server = new Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->set(['websocket_compression' => true]);\n        $server->handle('/', function ($request, $response) use ($data1, $data2, $data3) {\n            $response->upgrade();\n            $frame = $response->recv();\n            Assert::true($frame->data == $data1 . $data2 . $data3);\n            $context = deflate_init(ZLIB_ENCODING_RAW);\n            $response->push(deflate_add($context, $data1, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_COMPRESS | SWOOLE_WEBSOCKET_FLAG_RSV1);\n            $response->push(deflate_add($context, $data2, ZLIB_NO_FLUSH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n            $response->push(deflate_add($context, $data3, ZLIB_FINISH), SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/compression_min_length.phpt",
    "content": "--TEST--\nswoole_http_server_coro: compression_min_length\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\ndefine('TEST_PORT', get_one_free_port());\n\nRuntime::setHookFlags(SWOOLE_HOOK_ALL);\n\nrun(function () {\n    go(function () {\n        $server = new Server(\"127.0.0.1\", TEST_PORT, false);\n        $server->set(['compression_min_length' => 128,]);\n        $server->handle('/test', function ($request, $response) {\n            $response->end(str_repeat('A', $request->get['bytes']));\n        });\n        $server->handle('/shutdown', function ($request, $response) use ($server) {\n            $response->end(\"shutdown\");\n            $server->shutdown();\n        });\n        $server->start();\n    });\n    \n    go(function () {\n        $cli = new Client('127.0.0.1', TEST_PORT);\n        $cli->setHeaders(['Accept-Encoding' => 'gzip', ]);\n        $cli->get('/test?bytes=128');\n        Assert::eq($cli->getHeaders()['content-encoding'], 'gzip');\n\n        $cli = new Client('127.0.0.1', TEST_PORT);\n        $cli->setHeaders(['Accept-Encoding' => 'gzip', ]);\n        $cli->get('/test?bytes=127');\n        Assert::assert(!isset($cli->getHeaders()['content-encoding']));\n\n        file_get_contents('http://127.0.0.1:' . TEST_PORT . '/shutdown');\n    });\n});\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/compression_types.phpt",
    "content": "--TEST--\nswoole_http_server_coro: compression types\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/../include/api/http_test_cases.php';\n\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Request;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    http_compression_types_test($pm);\n    echo \"DONE\\n\";\n    file_get_contents('http://127.0.0.1:' . $pm->getFreePort() . '/shutdown');\n};\n$pm->childFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $server = new Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->set([\n            'http_compression_types' => [\n                'text/html',\n                'application/json'\n            ],\n        ]);\n        $server->handle('/', function (Request $request, Response $response) {\n            if ($request->server['request_uri'] == '/html') {\n                $response->end(str_repeat('A', $request->get['bytes']));\n            } elseif ($request->server['request_uri'] == '/json') {\n                $response->setHeader('Content-Type', 'application/json');\n                $response->end(str_repeat('B', $request->get['bytes']));\n            } elseif ($request->server['request_uri'] == '/raw') {\n                $response->setHeader('Content-Type', 'text/raw');\n                $response->end(str_repeat('C', $request->get['bytes']));\n            }\n        });\n        $server->handle('/shutdown', function ($request, $response) use ($server) {\n            $response->end(\"shutdown\");\n            $server->shutdown();\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/continue_frames.phpt",
    "content": "--TEST--\nswoole_http_server_coro: client continue frames - 1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Request;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$data1 = bin2hex(random_bytes(10 * 1024));\n$data2 = bin2hex(random_bytes(20 * 2048));\n$data3 = bin2hex(random_bytes(40 * 4096));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n        $client->push($data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n        $client->push($data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $client->push($data3, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n        $frame = $client->recv();\n        Assert::true($frame->data == $data1 . $data2 . $data3);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $server = new Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/', function ($request, $response) use ($data1, $data2, $data3) {\n            $response->upgrade();\n            $frame = $response->recv();\n            Assert::true($frame->data == $data1 . $data2 . $data3);\n            $response->push($data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n            $response->push($data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n            $response->push($data3, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/continue_frames2.phpt",
    "content": "--TEST--\nswoole_http_server_coro: client continue frames - 2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Request;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$data1 = bin2hex(random_bytes(10 * 1024));\n$data2 = bin2hex(random_bytes(20 * 2048));\n$data3 = bin2hex(random_bytes(40 * 4096));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n        $client->push($data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n        $client->push($data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $client->push($data3, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n        $frame = $client->recv();\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $server = new Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/', function ($request, $response) use ($data1, $data2, $data3) {\n            $response->upgrade();\n            $frame = $response->recv();\n            Assert::true($frame->data == $data1 . $data2 . $data3);\n            $response->push($data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n            $response->push($data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n            $response->push($data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n            $response->push($data3, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n%s All fragments of a message, except for the initial frame, must use the continuation frame opcode(0).\n"
  },
  {
    "path": "tests/swoole_http_server_coro/continue_frames3.phpt",
    "content": "--TEST--\nswoole_http_server_coro: client continue frames - 3\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Request;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$data1 = bin2hex(random_bytes(10 * 1024));\n$data2 = bin2hex(random_bytes(20 * 2048));\n$data3 = bin2hex(random_bytes(40 * 4096));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n        $client->push($data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n        $client->push($data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        $client->push($data3, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n        $frame = $client->recv();\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $server = new Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/', function ($request, $response) use ($data1, $data2, $data3) {\n            $response->upgrade();\n            $frame = $response->recv();\n            Assert::true($frame->data == $data1 . $data2 . $data3);\n            $response->push($data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n%s A continuation frame cannot stand alone and MUST be preceded by an initial frame whose opcode indicates either text or binary data.\n"
  },
  {
    "path": "tests/swoole_http_server_coro/continue_frames4.phpt",
    "content": "--TEST--\nswoole_http_server_coro: client continue frames - 4\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Request;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$data1 = bin2hex(random_bytes(10 * 1024));\n$data2 = bin2hex(random_bytes(20 * 2048));\n$data3 = bin2hex(random_bytes(40 * 4096));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $results = [];\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n        $client->push('111', SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_FIN);\n        $results[] = $client->recv();\n        Assert::true($results[0]->data == $data1 . $data2 . $data3);\n        $client->push('222', SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_FIN);\n        $results[] = $client->recv();\n        Assert::true($results[1]->data == $data3 . $data2 . $data1);\n        Assert::true($results[0]->data != $results[1]->data);\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm, $data1, $data2, $data3) {\n    Co\\run(function () use ($pm, $data1, $data2, $data3) {\n        $server = new Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/', function ($request, $response) use ($data1, $data2, $data3) {\n            $response->upgrade();\n            while ($frame = $response->recv()) {\n                if ($frame->data == '111') {\n                    $response->push($data1, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n                    $response->push($data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n                    $response->push($data3, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n                } else {\n                    $response->push($data3, SWOOLE_WEBSOCKET_OPCODE_TEXT, 0);\n                    $response->push($data2, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, 0);\n                    $response->push($data1, SWOOLE_WEBSOCKET_OPCODE_CONTINUATION, SWOOLE_WEBSOCKET_FLAG_FIN);\n                }\n            }\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/control_frame_compress.phpt",
    "content": "--TEST--\nswoole_http_server_coro: control frame can not compress\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse Swoole\\WebSocket\\CloseFrame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->set([\n            'websocket_compression' => true,\n            'open_websocket_close_frame' => true,\n        ]);\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n\n        $client->push('A');\n        $frame = $client->recv();\n        Assert::true(($frame->flags & SWOOLE_WEBSOCKET_FLAG_RSV1) == 0);\n        Assert::true(($frame->flags & SWOOLE_WEBSOCKET_FLAG_COMPRESS) == 0);\n        Assert::true($frame->flags == SWOOLE_WEBSOCKET_FLAG_FIN);\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_CLOSE);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $server = new Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->set(['websocket_compression' => true]);\n        $server->handle('/', function ($request, $response) {\n            $response->upgrade();\n            $closeFrame = new CloseFrame();\n            $closeFrame->opcode = SWOOLE_WEBSOCKET_OPCODE_CLOSE;\n            $closeFrame->code = SWOOLE_WEBSOCKET_CLOSE_NORMAL;\n            $closeFrame->reason = 'hahahahaha';\n            $response->push($closeFrame, SWOOLE_WEBSOCKET_FLAG_RSV1 | SWOOLE_WEBSOCKET_FLAG_COMPRESS);\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/control_frame_fragmented.phpt",
    "content": "--TEST--\nswoole_http_server_coro: Control frames must not be fragmented\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse Swoole\\WebSocket\\CloseFrame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->set([\n            'websocket_compression' => true,\n            'open_websocket_close_frame' => true,\n        ]);\n        $ret = $client->upgrade('/');\n        Assert::assert($ret);\n\n        $client->push('A');\n        $frame = $client->recv();\n        Assert::true(($frame->flags & SWOOLE_WEBSOCKET_FLAG_RSV1) == 0);\n        Assert::true(($frame->flags & SWOOLE_WEBSOCKET_FLAG_COMPRESS) == 0);\n        Assert::true($frame->flags == SWOOLE_WEBSOCKET_FLAG_FIN);\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_CLOSE);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $server = new Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->set(['websocket_compression' => true]);\n        $server->handle('/', function ($request, $response) {\n            $response->upgrade();\n            $closeFrame = new CloseFrame();\n            $closeFrame->opcode = SWOOLE_WEBSOCKET_OPCODE_CLOSE;\n            $closeFrame->code = SWOOLE_WEBSOCKET_CLOSE_NORMAL;\n            $closeFrame->reason = 'hahahahaha';\n            $closeFrame->flags = 0;\n            $response->push($closeFrame, 0);\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/crash-bad-return-type.phpt",
    "content": "--TEST--\nswoole_http_server_coro: crash - bad return type\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$port = get_one_free_port();\n\nCo::set(['log_file' => '/dev/null']);\n\ngo(function () use ($port) {\n    $server = new Co\\Http\\Server(\"127.0.0.1\", $port, true);\n    $server->set([\n        'open_tcp_nodelay' => true,\n        'ssl_cert_file' => SSL_FILE_DIR.'/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR.'/server.key',\n    ]);\n    $server->handle('/', function ($request, $response) {\n        $response->end(\"<h1>Index</h1>\");\n    });\n    $server->handle('/stop', function ($request, $response) use ($server) {\n        $response->end(\"<h1>Stop</h1>\");\n        $server->shutdown();\n    });\n    $server->start();\n});\n\ngo(function () use ($port) {\n    try {\n        echo httpGetBody(\"http://127.0.0.1:{$port}/\") . PHP_EOL;\n    } catch (Throwable $e) {\n        Assert::contains($e->getMessage(), 'Connection reset by peer');\n        echo \"Bad Client\\n\";\n    }\n    echo httpGetBody(\"https://127.0.0.1:{$port}/\") . PHP_EOL;\n    echo httpGetBody(\"https://127.0.0.1:{$port}/stop?hello=1\") . PHP_EOL;\n});\nSwoole\\Event::wait();\n\n?>\n--EXPECT--\nBad Client\n<h1>Index</h1>\n<h1>Stop</h1>\n"
  },
  {
    "path": "tests/swoole_http_server_coro/create_response.phpt",
    "content": "--TEST--\nswoole_http_server_coro: create response\n--SKIPIF--\n<?php\n\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Server;\nuse Swoole\\Coroutine\\Server\\Connection;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$port = get_one_free_port();\n\ndefine('GREETER', 'hello world');\n\nCo\\run(function () use ($port) {\n\n    $server = new Server('0.0.0.0', $port, false);\n\n    go(function () use ($server) {\n        $server->handle(function (Connection $conn) use ($server) {\n            $req = Request::create();\n            while(true) {\n                $data = $conn->recv();\n                if (strlen($data) != $req->parse($data) or $req->isCompleted()) {\n                    break;\n                }\n            }\n            Assert::eq($req->get['value'], 1);\n            Assert::eq($req->get['hello'], 'world');\n            $resp = Response::create($conn->exportSocket());\n            $resp->header('X-Server', 'swoole');\n            $resp->end(GREETER);\n\n            $server->shutdown();\n        });\n        $server->start();\n    });\n\n    go(function () use ($port, $server) {\n        $httpClient = new Co\\Http\\Client(HTTP_SERVER_HOST, $port, false);\n        $httpClient->setMethod(\"POST\");\n        $httpClient->setData(\"HELLO\");\n        $ok = $httpClient->execute(\"/rawcookie?hello=world&value=1\");\n        Assert::assert($ok);\n        Assert::same($httpClient->statusCode, 200);\n        Assert::same($httpClient->errCode, 0);\n        Assert::eq($httpClient->getHeaders()['x-server'], 'swoole');\n        Assert::same($httpClient->getBody(), GREETER);\n        $server->shutdown();\n    });\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/create_response_2.phpt",
    "content": "--TEST--\nswoole_http_server_coro: create response [2]\n--SKIPIF--\n<?php\n\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Server;\nuse Swoole\\Coroutine\\Server\\Connection;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$port = get_one_free_port();\n\ndefine('GREETER', 'hello world');\n\nCo\\run(function () use ($port) {\n\n    $server = new Server('0.0.0.0', $port, false);\n\n    go(function () use ($server) {\n        $server->handle(function (Connection $conn) use ($server) {\n            $req = Request::create();\n            while(true) {\n                $data = $conn->recv();\n                if (strlen($data) != $req->parse($data) or $req->isCompleted()) {\n                    break;\n                }\n            }\n            Assert::eq($req->get['value'], 1);\n            Assert::eq($req->get['hello'], 'world');\n            $resp = Response::create([$conn->exportSocket(), $req]);\n            $resp->header('X-Server', 'swoole');\n            $resp->end(GREETER);\n\n            $server->shutdown();\n        });\n        $server->start();\n    });\n\n    go(function () use ($port, $server) {\n        $httpClient = new Co\\Http\\Client(HTTP_SERVER_HOST, $port, false);\n        $httpClient->setMethod(\"POST\");\n        $httpClient->setData(\"HELLO\");\n        $ok = $httpClient->execute(\"/rawcookie?hello=world&value=1\");\n        Assert::assert($ok);\n        Assert::same($httpClient->statusCode, 200);\n        Assert::same($httpClient->errCode, 0);\n        Assert::eq($httpClient->getHeaders()['x-server'], 'swoole');\n        Assert::same($httpClient->getBody(), GREETER);\n        $server->shutdown();\n    });\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/disconnect.phpt",
    "content": "--TEST--\nswoole_http_server_coro: test disconnect function\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine\\Http\\Client;\nuse SwooleTest\\ProcessManager as ProcessManager;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->set([\n            'open_websocket_close_frame' => true\n        ]);\n        $ret = $client->upgrade('/');\n        $client->push('Hello World');\n        $frame = $client->recv();\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_CLOSE);\n        Assert::true($frame->reason == 'close it');\n        Assert::true($client->recv() == '');\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $server = new Server('127.0.0.1', $pm->getFreePort(), false);\n        $server->handle('/', function (Request $request, Response $response) {\n            $response->upgrade();\n            $response->recv();\n            Assert::true($response->disconnect(SWOOLE_WEBSOCKET_CLOSE_NORMAL, 'close it'));\n        });\n\n        $pm->wakeup();\n        $server->start();\n    });\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/error_404.phpt",
    "content": "--TEST--\nswoole_http_server_coro: 404 error\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        Assert::assert(httpGetStatusCode(\"http://127.0.0.1:{$pm->getFreePort()}/test\") == 404);\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/stop?hello=1\") . PHP_EOL;\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Co\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/hello', function ($request, $response) {\n            $response->end(\"<h1>hello</h1>\");\n        });\n        $server->handle('/stop', function ($request, $response) use ($server) {\n            $response->end(\"<h1>Stop</h1>\");\n            $server->shutdown();\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n<h1>Stop</h1>\n"
  },
  {
    "path": "tests/swoole_http_server_coro/error_413.phpt",
    "content": "--TEST--\nswoole_http_server_coro: error 413\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\ndefine('TEST_PORT', get_one_free_port());\n\ngo(function () {\n    $server = new Co\\Http\\Server(\"127.0.0.1\", TEST_PORT, false);\n    $server->set(['package_max_length' => 65536, ]);\n    $server->handle('/', function ($request, $response) {\n        $response->end(serialize($request->server));\n    });\n    $server->handle('/shutdown', function ($request, $response) use ($server) {\n        $response->end(\"shutdown\");\n        $server->shutdown();\n    });\n    $server->start();\n});\n\ngo(function () {\n    $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', TEST_PORT, false);\n    $cli->set(['timeout' => 5]);\n    Assert::assert($cli->post('/world/index?b=455', ['value' => str_repeat('A', 128 * 1024 )]));\n    Assert::assert($cli->getStatusCode() == 413);\n    file_get_contents('http://127.0.0.1:' . TEST_PORT . '/shutdown');\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/form_data_1.phpt",
    "content": "--TEST--\nswoole_http_server_coro: form data 1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/../include/api/http_test_cases.php';\n\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Process;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager;\n$pm->initFreePorts();\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    form_data_test($pm, [250]);\n};\n\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $server = new Server('127.0.0.1', $pm->getFreePort(), false);\n        $server->handle('/', function (Request $request, Response $response) {\n            $response->end(json_encode($request->post));\n        });\n        Process::signal(SIGTERM, function () use ($server) {\n            $server->shutdown();\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/form_data_2.phpt",
    "content": "--TEST--\nswoole_http_server_coro: form data 2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/../include/api/http_test_cases.php';\n\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Process;\nuse function Swoole\\Coroutine\\run;\n\nconst OFFSET = 216;\n\n$pm = new ProcessManager;\n$pm->initFreePorts();\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    form_data_test($pm, [225]);\n};\n\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $server = new Server('127.0.0.1', $pm->getFreePort(), false);\n        $server->handle('/', function (Request $request, Response $response) {\n            $response->end(json_encode($request->post));\n        });\n        Process::signal(SIGTERM, function () use ($server) {\n            $server->shutdown();\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/graceful_shutdown.phpt",
    "content": "--TEST--\r\nswoole_http_server_coro: graceful shutdown\r\n--SKIPIF--\r\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\r\n--FILE--\r\n<?php\r\nrequire __DIR__ . '/../include/bootstrap.php';\r\n\r\nuse Swoole\\Event;\r\nuse Swoole\\Runtime;\r\n\r\n$pm = new ProcessManager;\r\n$pm->parentFunc = function () use ($pm) {\r\n    $errors = '';\r\n    Runtime::setHookFlags(SWOOLE_HOOK_ALL);\r\n    Co\\run(function () use ($pm, &$errors) {\r\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\") . PHP_EOL;\r\n\r\n        go(function () use ($pm, &$errors) {\r\n            try {\r\n                echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/sleep\") . PHP_EOL;\r\n            } catch (Throwable $e) {\r\n                $errors .= $e->getMessage() . PHP_EOL;\r\n            }\r\n        });\r\n\r\n        go(function () use ($pm, &$errors) {\r\n            usleep(5000);\r\n            echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/shutdown\") . PHP_EOL;\r\n            try {\r\n                echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\") . PHP_EOL;\r\n            } catch (Throwable $e) {\r\n                $errors .= $e->getMessage() . PHP_EOL;\r\n                echo \"done\\n\";\r\n            }\r\n        });\r\n    });\r\n\r\n    Assert::contains($errors, 'Connection reset by peer');\r\n};\r\n$pm->childFunc = function () use ($pm) {\r\n    Co\\run(function () use ($pm) {\r\n        $server = new Co\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), false);\r\n        $server->handle('/', function ($request, $response) {\r\n            $response->end(\"index\");\r\n        });\r\n        $server->handle('/sleep', function ($request, $response) {\r\n            Co::sleep(0.2);\r\n            $response->end(\"sleep\");\r\n        });\r\n        $server->handle('/shutdown', function ($request, $response) use ($server) {\r\n            $response->end(\"shutdown\");\r\n            $server->shutdown();\r\n        });\r\n        $pm->wakeup();\r\n        $server->start();\r\n    });\r\n};\r\n$pm->childFirst();\r\n$pm->run();\r\n?>\r\n--EXPECT--\r\nindex\r\nshutdown\r\nsleep\r\ndone\n"
  },
  {
    "path": "tests/swoole_http_server_coro/handle.phpt",
    "content": "--TEST--\nswoole_http_server_coro: handle\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\") . PHP_EOL;\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/test/index/\") . PHP_EOL;\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/stop?hello=1\") . PHP_EOL;\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Co\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/', function ($request, $response) {\n            $response->end(\"<h1>Index</h1>\");\n        });\n        $server->handle('/test', function ($request, $response) {\n            $response->end(\"<h1>Test</h1>\");\n        });\n        $server->handle('/stop', function ($request, $response) use ($server) {\n            $response->end(\"<h1>Stop</h1>\");\n            $server->shutdown();\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n<h1>Index</h1>\n<h1>Test</h1>\n<h1>Stop</h1>\n"
  },
  {
    "path": "tests/swoole_http_server_coro/http2.phpt",
    "content": "--TEST--\nswoole_http_server_coro: http2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http2\\Request;\nuse Swoole\\Http2\\Response;\nuse Swoole\\Coroutine\\Http2\\Client;\n\n$pm = new SwooleTest\\ProcessManager;\n\nconst N = 10;\n\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        Assert::true($client->connect());\n        $streams = [];\n        $request = new Request;\n        for ($n = N; $n--;) {\n            $request->data = $n;\n            $streams[$client->send($request)] = $n;\n        }\n        for ($n = N; $n--;) {\n            /** @var $response Response */\n            $response = $client->recv();\n            Assert::same($streams[$response->streamId], (int)$response->data);\n        }\n        echo \"DONE\\n\";\n        $kill_request = new Request;\n        $kill_request->path = '/stop';\n        $client->send($kill_request);\n        $response = $client->recv();\n        Assert::assert($response instanceof Response);\n        echo $response->data, \"\\n\";\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $server = new Swoole\\Coroutine\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n            $response->end($request->rawContent());\n        });\n        $server->handle('/stop', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response  $response) use ($server) {\n            $response->end(\"STOP\");\n            $server->shutdown();\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\nSTOP\n"
  },
  {
    "path": "tests/swoole_http_server_coro/http2_ssl.phpt",
    "content": "--TEST--\nswoole_http_server_coro: http2 + SSL\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$port = get_one_free_port();\n\n\\Swoole\\Runtime::setHookFlags(SWOOLE_HOOK_ALL);\n\ngo(function () use ($port) {\n    $server = new Co\\Http\\Server(\"127.0.0.1\", $port, true);\n    $server->set([\n        'open_http2_protocol' => true,\n        'open_tcp_nodelay' => true,\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n    ]);\n    $server->handle('/', function ($request, $response) {\n        $response->end(\"<h1>Index</h1>\");\n    });\n    $server->handle('/stop', function ($request, $response) use ($server) {\n        $response->end(\"<h1>Stop</h1>\");\n        $server->shutdown();\n    });\n    $server->start();\n});\n\ngo(function () use ($port) {\n    echo shell_exec(\"curl --no-progress-meter --http2 -k https://127.0.0.1:$port/\") . PHP_EOL;\n    echo shell_exec(\"curl --no-progress-meter --http2 -k https://127.0.0.1:$port/stop\") . PHP_EOL;\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n<h1>Index</h1>\n<h1>Stop</h1>\n"
  },
  {
    "path": "tests/swoole_http_server_coro/https.phpt",
    "content": "--TEST--\nswoole_http_server_coro: https\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$port = get_one_free_port();\n\ngo(function () use ($port) {\n    $server = new Co\\Http\\Server(\"127.0.0.1\", $port, true);\n    $server->set(['open_tcp_nodelay' => true,\n        'ssl_cert_file' => SSL_FILE_DIR.'/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR.'/server.key',\n    ]);\n    $server->handle('/', function ($request, $response) {\n        $response->end(\"<h1>Index</h1>\");\n    });\n    $server->handle('/stop', function ($request, $response) use ($server) {\n        $response->end(\"<h1>Stop</h1>\");\n        $server->shutdown();\n    });\n    $server->start();\n});\n\ngo(function () use ($port) {\n    echo httpGetBody(\"https://127.0.0.1:{$port}/\") . PHP_EOL;\n    echo httpGetBody(\"https://127.0.0.1:{$port}/stop?hello=1\") . PHP_EOL;\n});\nSwoole\\Event::wait();\n\n?>\n--EXPECT--\n<h1>Index</h1>\n<h1>Stop</h1>\n"
  },
  {
    "path": "tests/swoole_http_server_coro/ipv6.phpt",
    "content": "--TEST--\nswoole_http_server_coro: ipv6\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_ipv6();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n\n\\Swoole\\Runtime::enableCoroutine();\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        echo file_get_contents(\"http://[::1]:{$pm->getFreePort()}/\") . PHP_EOL;\n        echo file_get_contents(\"http://[::1]:{$pm->getFreePort()}/stop?hello=1\") . PHP_EOL;\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Co\\Http\\Server(\"::1\", $pm->getFreePort(), false);\n        $server->handle('/', function ($request, $response) {\n            $response->end(\"<h1>Index</h1>\");\n        });\n        $server->handle('/stop', function ($request, $response) use ($server) {\n            $response->end(\"<h1>Stop</h1>\");\n            $server->shutdown();\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n<h1>Index</h1>\n<h1>Stop</h1>\n"
  },
  {
    "path": "tests/swoole_http_server_coro/keepalive.phpt",
    "content": "--TEST--\nswoole_http_server_coro: keepalive\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\ndefine('TEST_PORT', get_one_free_port());\n\ngo(function () {\n    $server = new Co\\Http\\Server(\"127.0.0.1\", TEST_PORT, false);\n    $server->handle('/', function ($request, $response) {\n        $response->end(serialize($request->server));\n    });\n    $server->handle('/shutdown', function ($request, $response) use ($server) {\n        $response->end(\"shutdown\");\n        $server->shutdown();\n    });\n    $server->start();\n});\n\ngo(function () {\n    $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', TEST_PORT, false);\n    $cli->set(['timeout' => 5]);\n    Assert::assert($cli->get('/hello?a=x3'));\n    $data1 = unserialize($cli->body);\n    Assert::assert($cli->get('/world/index?b=455'));\n    $data2 = unserialize($cli->body);\n    Assert::assert($data1['remote_port'] == $data2['remote_port']);\n    file_get_contents('http://127.0.0.1:' . TEST_PORT . '/shutdown');\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/open_frame.phpt",
    "content": "--TEST--\nswoole_http_server_coro: handle frame by user\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse Swoole\\WebSocket\\CloseFrame;\nuse SwooleTest\\ProcessManager as ProcessManager;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->set([\n            'open_websocket_ping_frame' => true,\n            'open_websocket_pong_frame' => true,\n            'open_websocket_close_frame' => true,\n        ]);\n        $ret = $client->upgrade('/');\n        $client->push('Hello World');\n        $frame = $client->recv();\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_PING);\n        $frame = $client->recv();\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_PONG);\n        $frame = $client->recv();\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_CLOSE);\n        Assert::true($frame->code == SWOOLE_WEBSOCKET_CLOSE_NORMAL);\n        Assert::true($frame->reason == \"lalalala FUMEI\");\n        $frame = $client->recv();\n        Assert::true($frame == '');\n        $client->disconnect();\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $server = new Server('127.0.0.1', $pm->getFreePort(), false);\n        $server->set([\n            'open_websocket_ping_frame' => true,\n            'open_websocket_pong_frame' => true,\n            'open_websocket_close_frame' => true,\n        ]);\n        $server->handle('/', function (Request $request, Response $response) {\n            $response->upgrade();\n            $ping = new Frame();\n            $ping->opcode = SWOOLE_WEBSOCKET_OPCODE_PING;\n            $response->push($ping);\n\n            $pong = new Frame();\n            $pong->opcode = SWOOLE_WEBSOCKET_OPCODE_PONG;\n            $response->push($pong);\n\n            $close = new CloseFrame();\n            $close->opcode = SWOOLE_WEBSOCKET_OPCODE_CLOSE;\n            $close->code = SWOOLE_WEBSOCKET_CLOSE_NORMAL;\n            $close->reason = \"lalalala FUMEI\";\n            $response->push($close);\n        });\n\n        $pm->wakeup();\n        $server->start();\n    });\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/ping.phpt",
    "content": "--TEST--\nswoole_http_server_coro: test ping function\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->set(['open_websocket_ping_frame' => true]);\n        Assert::true($client->upgrade('/'));\n        Assert::true($client->push('Hello World'));\n        $frame = $client->recv();\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_PING);\n        Assert::true($frame->data == 'Hello World');\n        Assert::true($client->ping());\n        $frame = $client->recv();\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_PING);\n        Assert::true($frame->data == '');\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $server = new Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/', function ($request, $response) {\n            $response->upgrade();\n            $response->recv();\n            $response->ping('Hello World');\n            $response->ping();\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/pipeline.phpt",
    "content": "--TEST--\nswoole_http_server_coro: pipeline\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Coroutine\\Socket;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\nconst REQUEST = \"GET / HTTP/1.1\\r\\n\\r\\n\";\n\nCoroutine\\run(function () {\n    $server = new Server('127.0.0.1', 0);\n    Coroutine::create(function () use ($server) {\n        $server->handle('/', function (Request $request, Response $response) use ($server) {\n            static $count = 0;\n            $response->end(\"OK\\n\");\n            if (++$count === MAX_CONCURRENCY * MAX_REQUESTS) {\n                echo \"DONE\\n\";\n                $server->shutdown();\n            }\n        });\n        $server->start();\n    });\n    for ($c = MAX_CONCURRENCY; $c--;) {\n        Coroutine::sleep(0.001);\n        Coroutine::create(function () use ($server) {\n            $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n            if (Assert::true($socket->connect('127.0.0.1', $server->port, -1))) {\n                $ret = $socket->sendAll(str_repeat(REQUEST, MAX_REQUESTS));\n                Assert::same($ret, strlen(REQUEST) * MAX_REQUESTS);\n            } else {\n                throw new RuntimeException('Connect failed: ' . $socket->errMsg);\n            }\n            while (!empty($socket->recv())) {\n                // pass\n            }\n        });\n    }\n});\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/post_array.phpt",
    "content": "--TEST--\nswoole_http_server_coro: post array data\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$port = get_one_free_port();\n\ngo(function () use ($port) {\n    $server = new Co\\Http\\Server(\"127.0.0.1\", $port, false);\n    $server->handle('/post', function ($request, $response) {\n        $response->end(json_encode(['form' => $request->post]));\n    });\n    $server->handle('/stop', function ($request, $response) use ($server) {\n        $response->end(\"<h1>Stop</h1>\");\n        $server->shutdown();\n    });\n    $server->start();\n});\n\ngo(function () use ($port) {\n    $uri = 'http://127.0.0.1:' . $port;\n    $data = [];\n    for ($n = MAX_REQUESTS; $n--;) {\n        $data[get_safe_random()] = get_safe_random();\n    }\n    $body = httpGetBody($uri . '/post', ['method' => 'POST', 'data' => $data]);\n    $form = json_decode($body, true)['form'];\n    Assert::same($form, $data);\n\n    echo httpGetBody($uri . \"/stop?hello=1\") . PHP_EOL;\n});\n\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\n<h1>Stop</h1>\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/random_port.phpt",
    "content": "--TEST--\nswoole_http_server_coro: bind random port\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function() {\n    $server = new Co\\Http\\Server(\"127.0.0.1\", 0);\n    Assert::assert($server->port > 0 and $server->port < 65535);\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/rawContent_get_big_data.phpt",
    "content": "--TEST--\nswoole_http_server_coro: rawContent get big data\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->initRandomData(1, 64 * 1024);\n$pm->parentFunc = function () use ($pm) {\n    run(function () use($pm) {\n        httpRequest(\"http://127.0.0.1:{$pm->getFreePort()}/\", [\n            'method' => 'POST',\n            'data' => $pm->getRandomData(),\n        ]);\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    run(function () use($pm) {\n        $server = new Server('127.0.0.1', $pm->getFreePort());\n        $server->handle('/', function (Request $request, Response $response) use ($pm) {\n            Assert::assert($request->rawContent() === $pm->getRandomData());\n            Assert::length($request->rawContent(), 64 * 1024);\n        });\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/remote_addr.phpt",
    "content": "--TEST--\nswoole_http_server_coro: remote_addr\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n\nuse Swoole\\Http\\Request;\n\n$pm->parentFunc = function () use ($pm) {\n    go(\n        function () use ($pm) {\n            $client = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            Assert::assert($client->get('/'));\n            $data = $client->getBody();\n            Assert::assert($data);\n            $json = json_decode($data);\n            $info = $client->getsockname();\n            Assert::eq($json->remote_addr, $info['address']);\n            Assert::eq($json->remote_port, $info['port']);\n            httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/stop\") . PHP_EOL;\n        }\n    );\n};\n\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Co\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/', function (Request $request, $response) {\n            $response->end(json_encode($request->server));\n        });\n        $server->handle('/stop', function ($request, $response) use ($server) {\n            $response->end(\"<h1>Stop</h1>\");\n            $server->shutdown();\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/restart.phpt",
    "content": "--TEST--\nswoole_http_server_coro: graceful shutdown\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $n = 2;\n        while ($n--) {\n            echo \"[$n]\", httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\") . PHP_EOL;\n            echo \"[$n]\", httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/shutdown\") . PHP_EOL;\n            usleep(150000);\n        }\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $server = new Co\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/', function ($request, $response) {\n            $response->end(\"index\");\n        });\n        $server->handle('/shutdown', function ($request, $response) use ($server) {\n            $response->end(\"shutdown\");\n            $server->shutdown();\n        });\n        $pm->wakeup();\n\n        $n = 2;\n        while ($n--) {\n            $server->start();\n            usleep(100000);\n        }\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n[1]index\n[1]shutdown\n[0]index\n[0]shutdown\n"
  },
  {
    "path": "tests/swoole_http_server_coro/reuse_port.phpt",
    "content": "--TEST--\nswoole_http_server_coro: reuse port\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Constant;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Coroutine\\Scheduler;\nuse Swoole\\Process;\nuse Swoole\\Process\\Pool;\nuse SwooleTest\\ProcessManager;\n\n$pm = new ProcessManager();\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $sch = new Scheduler();\n    $pids = [];\n    $sch->parallel(10, function () use ($pm, &$pids) {\n        $cli = new Client('127.0.0.1', $pm->getFreePort());\n        $ret = $cli->get('/hello');\n        if (!$ret) {\n            echo \"ERROR [3]\\n\";\n            return;\n        }\n        $result = unserialize($cli->getBody());\n        if (!$result) {\n            echo \"ERROR [4]\\n\";\n            return;\n        }\n        $pids[$result['wid']] = 1;\n    });\n    $sch->start();\n    Assert::eq(count($pids), IS_MAC_OS ? 1 : 2);\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $atomic = new Atomic();\n    $pool = new Pool(2);\n    $pool->set(['enable_coroutine' => true]);\n    $pool->on(Constant::EVENT_WORKER_START, function ($pool, $id) use ($pm, $atomic) {\n        $server = new Server('127.0.0.1', $pm->getFreePort(), false, true);\n        $server->handle('/', function ($request, $response) {\n            $response->end(serialize(['wid' => posix_getpid()]));\n        });\n        if ($atomic->add() == 2) {\n            $pm->wakeup();\n        }\n        Process::signal(SIGTERM, function () use ($server) {\n            $server->shutdown();\n        });\n        $server->start();\n    });\n    $pool->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/sendfile.phpt",
    "content": "--TEST--\nswoole_http_server_coro: sendfile\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    for ($i = MAX_REQUESTS; $i--;) {\n        $send_file = get_safe_random(mt_rand(0, 65535 * 10));\n        file_put_contents('/tmp/sendfile.txt', $send_file);\n        $recv_file = file_get_contents(\"http://127.0.0.1:{$pm->getFreePort()}/test.jpg\");\n        Assert::same(md5($send_file), md5($recv_file));\n    }\n    file_get_contents(\"http://127.0.0.1:{$pm->getFreePort()}/shutdown\");\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Co\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/', function ($request, $response) {\n            $response->end(\"<h1>Index</h1>\");\n        });\n        $server->handle('/test.jpg', function ($request, $response) {\n            $response->header('Content-Type', 'application/octet-stream');\n            $response->header('Content-Disposition', 'attachment; filename=recvfile.txt');\n            $response->sendfile('/tmp/sendfile.txt');\n        });\n        $server->handle('/shutdown', function ($request, $response) use ($server) {\n            echo \"shutdown\\n\";\n            $response->status(200);\n            $response->end();\n            $server->shutdown();\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nshutdown\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/server_addr.phpt",
    "content": "--TEST--\nswoole_http_server_coro: add server addr\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager;\n$pm->initFreePorts();\n\n$port = $pm->getFreePort();\n\n$output = shell_exec('ip addr show');\npreg_match_all('/inet (\\d+\\.\\d+\\.\\d+\\.\\d+)\\//', $output, $matches);\n$ips = $matches[1];\n\n$pm->parentFunc = function ($pid) use ($pm, $port, $ips) {\n    run(function () use ($pm, $port, $ips) {\n        $client = new Client($ips[1], $port);\n        $client->get('/');\n        $client->close();\n        $pm->kill();\n    });\n};\n\n$pm->childFunc = function () use ($pm, $port, $ips) {\n    run(function () use ($pm, $port, $ips) {\n        $server = new Server('0.0.0.0', $port, false);\n        $server->handle('/', function (Request $request, Response $response) use ($ips){\n            $server = $request->server;\n            Assert::eq($server['server_addr'], $ips[1]);\n            Assert::eq($server['remote_addr'], $ips[1]);\n            Assert::true($server['server_port'] != $server['remote_port']);\n        });\n\n        Swoole\\Process::signal(SIGTERM, function () use ($server) {\n            $server->shutdown();\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/slow_client.phpt",
    "content": "--TEST--\nswoole_http_server_coro: slow client\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager;\n$pm->initFreePorts();\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n    $client->connect(\"127.0.01\", $pm->getFreePort());\n    $html = base64_encode(random_bytes(rand(1024, 65536)));\n    $len = strlen($html);\n    $data = \"POST /index.html HTTP/1.1\\r\\nServer: nginx\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: $len\\r\\nX-Server: swoole\\r\\n\\r\\n$html\";\n    $chunks = str_split($data, rand(5, 255));\n    foreach ($chunks as $out) {\n        $client->send($out);\n        usleep(100);\n    }\n\n    $data = $client->recv();\n    Assert::stringNotEmpty($data);\n    Assert::true(swoole_string($data)->contains('HTTP/1.1 200 OK'));\n    $pm->kill();\n    echo \"OK\\n\";\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    run(function () use($pm) {\n        $server = new Server('127.0.0.1', $pm->getFreePort());\n        $server->handle('/', function (Request $request, Response $response) use ($pm) {\n            Assert::same($request->header['server'], 'nginx');\n            Assert::same($request->header['x-server'], 'swoole');\n            Assert::same($request->header['content-type'], 'text/html');\n            Assert::eq($request->header['content-length'], strlen($request->getContent()));\n            $response->end(\"OK\");\n        });\n        $server->start();\n    });\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_server_coro/slow_large_post.phpt",
    "content": "--TEST--\nswoole_http_server_coro: slow large post\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse function Swoole\\Coroutine\\run;\n\nconst N1 = 2048;\nconst N2 = 8192;\nconst KEY = 'test_key';\ndefine('VALUE', random_bytes(N1 + N2));\n\n$pm = new ProcessManager;\n$pm->initFreePorts();\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n    $client->connect(\"127.0.0.1\", $pm->getFreePort());\n    $post_data = KEY . '=' . urlencode(VALUE);\n    $len = strlen($post_data);\n    $data = \"POST /index.html HTTP/1.1\\r\\nServer: nginx\\r\\nContent-Type: application/x-www-form-urlencoded\\r\\nConnection: close\\r\\nContent-Length: $len\\r\\nX-Server: swoole\\r\\n\\r\\n$post_data\";\n\n    $client->send(substr($data, 0, N1));\n    usleep(30000);\n    $client->send(substr($data, N1, N2));\n    usleep(30000);\n    $client->send(substr($data, N1 + N2));\n\n    $data = $client->recv();\n    Assert::stringNotEmpty($data);\n    Assert::true(swoole_string($data)->contains('HTTP/1.1 200 OK'));\n    $pm->kill();\n    echo \"OK\\n\";\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    run(function () use($pm) {\n        $server = new Server('127.0.0.1', $pm->getFreePort());\n        $server->handle('/', function (Request $request, Response $response) use ($pm) {\n            Assert::same($request->header['server'], 'nginx');\n            Assert::same($request->header['x-server'], 'swoole');\n            Assert::same($request->header['content-type'], 'application/x-www-form-urlencoded');\n            Assert::eq($request->header['content-length'], strlen($request->getContent()));\n            Assert::eq(VALUE, $request->post[KEY]);\n            $response->end(\"OK\");\n        });\n        $server->start();\n    });\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_http_server_coro/ssl_bad_client.phpt",
    "content": "--TEST--\nswoole_http_server_coro: bad client\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Co\\Client;\nuse Co\\Http\\Server;\nuse Swoole\\Event;\nuse SwooleTest\\ProcessManager;\n\n$pm = new ProcessManager();\n\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $port = $pm->getFreePort();\n        $client = new Client(SWOOLE_SOCK_TCP); // 同步阻塞\n        if (!$client->connect('127.0.0.1', $port)) {\n            exit(\"connect failed\\n\");\n        }\n        $client->send('hello world');\n        while (true) {\n            $data = $client->recv();\n            if (!$data) {\n                break;\n            }\n        }\n        echo httpGetBody(\"https://127.0.0.1:{$port}/stop?hello=1\") . PHP_EOL;\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Server('127.0.0.1', $pm->getFreePort(), true);\n        $server->set([\n            'open_tcp_nodelay' => true,\n            'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n            'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n        ]);\n        $server->handle('/', function ($request, $response) {\n            $response->end('<h1>Index</h1>');\n        });\n        $server->handle('/stop', function ($request, $response) use ($server) {\n            $response->end('<h1>Stop</h1>');\n            $server->shutdown();\n        });\n        $server->start();\n    });\n    Event::wait();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n[%s]\tWARNING\tSocket::ssl_accept(): bad SSL client[127.0.0.1:%d], reason=%d, error_string=%s\n<h1>Stop</h1>\n"
  },
  {
    "path": "tests/swoole_http_server_coro/tcp_nodelay.phpt",
    "content": "--TEST--\nswoole_http_server_coro: tcp nodelay\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$port = get_one_free_port();\n\ngo(function () use ($port) {\n    $server = new Co\\Http\\Server(\"127.0.0.1\", $port, false);\n    $server->set(['open_tcp_nodelay' => true,]);\n    $server->handle('/', function ($request, $response) {\n        $response->end(\"<h1>Index</h1>\");\n    });\n    $server->handle('/stop', function ($request, $response) use ($server) {\n        $response->end(\"<h1>Stop</h1>\");\n        $server->shutdown();\n    });\n    $server->start();\n});\n\ngo(function () use ($port) {\n    echo httpGetBody(\"http://127.0.0.1:{$port}/\") . PHP_EOL;\n    echo httpGetBody(\"http://127.0.0.1:{$port}/stop?hello=1\") . PHP_EOL;\n});\nSwoole\\Event::wait();\n\n?>\n--EXPECT--\n<h1>Index</h1>\n<h1>Stop</h1>\n"
  },
  {
    "path": "tests/swoole_http_server_coro/upload.phpt",
    "content": "--TEST--\nswoole_http_server_coro: upload 01\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_function_not_exist('curl_init');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nCo::set(['log_level' => 0, 'trace_flags' => SWOOLE_TRACE_HTTP]);\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $uri = \"http://127.0.0.1:{$pm->getFreePort()}\";\n    $ch = curl_init();\n    curl_setopt($ch, CURLOPT_URL, $uri.'/upload');\n    curl_setopt($ch, CURLOPT_HEADER, 0);\n    curl_setopt($ch, CURLOPT_POST, 1); //设置为POST方式\n    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));\n\n    $file = TEST_IMAGE;\n\n    $post_data = array('test' => str_repeat('a', 80));\n\n    if (function_exists(\"curl_file_create\"))\n    {\n        $cfile = curl_file_create($file);\n        $post_data['file'] = $cfile;\n    }\n    else\n    {\n        $post_data['file'] = '@' . $file;\n    }\n\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);  //POST数据\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n    $res = curl_exec($ch);\n    Assert::assert(!empty($res));\n    Assert::same($res, md5_file($file));\n    curl_close($ch);\n};\n\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Co\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/upload', function ($request, $response) use ($server) {\n            $response->end(md5_file($request->files['file']['tmp_name']));\n            $server->shutdown();\n        });\n        $server->start();\n    });\n    $pm->wakeup();\n    Swoole\\Event::wait();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/websocket.phpt",
    "content": "--TEST--\nswoole_http_server_coro: websocket greeter and reply twice\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$count = 0;\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, &$count) {\n    for ($c = MAX_CONCURRENCY; $c--;) {\n        go(function () use ($pm, &$count) {\n            global $count;\n            $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            $cli->set(['timeout' => 5]);\n            $ret = $cli->upgrade('/websocket');\n            Assert::assert($ret);\n            $data = sha1(get_safe_random(mt_rand(1, 1024)));\n            for ($n = MAX_REQUESTS; $n--;) {\n                $cli->push($data);\n                $ret = $cli->recv();\n                Assert::same($ret->data, \"Hello {$data}!\");\n                $ret = $cli->recv();\n                Assert::same($ret->data, \"How are you, {$data}?\");\n                Assert::same($cli->cookies['test-file'], __FILE__);\n                Assert::same($cli->headers['x-swoole'], 'hello');\n                $count++;\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    Assert::same($count, (MAX_CONCURRENCY * MAX_REQUESTS));\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Co\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/websocket', function ($request, $ws) {\n            $ws->header('x-swoole', 'hello');\n            $ws->cookie('test-file', __FILE__);\n            $ws->upgrade();\n            while (true) {\n                $frame = $ws->recv();\n                if ($frame === false) {\n                    echo \"error : \" . swoole_last_error() . \"\\n\";\n                    break;\n                } else if ($frame == '') {\n                    break;\n                } else {\n                    Assert::greaterThan($frame->fd, 0);\n                    $ws->push(\"Hello {$frame->data}!\");\n                    $ws->push(\"How are you, {$frame->data}?\");\n                }\n            }\n        });\n        $server->handle('/shutdown', function ($request, $response) use ($server) {\n            echo \"shutdown\\n\";\n            $response->status(200);\n            $server->shutdown();\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/websocket_close.phpt",
    "content": "--TEST--\nswoole_http_server_coro: close websocket connection\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, &$count) {\n    go(function () use ($pm) {\n        global $count;\n        $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->set(['timeout' => 5]);\n        $ret = $cli->upgrade('/websocket');\n        Assert::assert($ret);\n        $data = sha1(get_safe_random(mt_rand(0, 1024)));\n        $cli->push($data);\n        $ret = $cli->recv();\n        Assert::same($ret->data, \"Hello {$data}!\");\n        $s = microtime(true);\n        // An empty string was received, indicating that the connection has been closed by the peer\n        $ret = $cli->recv();\n        Assert::lessThan(microtime(true) - $s, 0.002);\n        Assert::same($ret, \"\");\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Co\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/websocket', function ($request, $ws) {\n            $ws->upgrade();\n            $frame = $ws->recv();\n            if ($frame === false) {\n                echo \"error : \" . swoole_last_error() . \"\\n\";\n            } else if ($frame === '' or $frame->data === '') {\n                echo \"close\\n\";\n            } else {\n                Assert::greaterThan($frame->fd, 0);\n                $ws->push(\"Hello {$frame->data}!\");\n                $ws->close();\n            }\n            System::sleep(0.5);\n        });\n        $server->handle('/shutdown', function ($request, $response) use ($server) {\n            echo \"shutdown\\n\";\n            $response->status(200);\n            $server->shutdown();\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/websocket_compression.phpt",
    "content": "--TEST--\nswoole_http_server_coro: websocket compression\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse function Swoole\\Coroutine\\run;\n\nrun(function () {\n    $server = new Server('127.0.0.1');\n    $server->set([\n        'websocket_compression' => true,\n    ]);\n    $server->handle('/', function (Request $request, Response $ws) {\n        if (($request->header['upgrade'] ?? '') === 'websocket') {\n            if ($ws->upgrade()) {\n                while ($frame = $ws->recv()) {\n                    $ws->push($frame);\n                }\n            }\n        } else {\n            $ws->status(400);\n            $ws->end();\n        }\n    });\n\n    Coroutine::create(function () use ($server) {\n        $cli = new Client('127.0.0.1', $server->port);\n        $cli->set([\n            'timeout' => 5,\n            'websocket_compression' => true\n        ]);\n        $ret = $cli->upgrade('/');\n        if (!Assert::assert($ret)) {\n            return;\n        }\n        for ($n = MAX_REQUESTS; $n--;) {\n            $data = get_safe_random();\n            $cli->push(\n                $data,\n                SWOOLE_WEBSOCKET_OPCODE_TEXT,\n                SWOOLE_WEBSOCKET_FLAG_FIN | SWOOLE_WEBSOCKET_FLAG_COMPRESS\n            );\n            $frame = $cli->recv();\n            if (!Assert::same($frame->data, $data)) {\n                return;\n            }\n            if (!Assert::eq($frame->flags & SWOOLE_WEBSOCKET_FLAG_COMPRESS, defined('SWOOLE_HAVE_ZLIB'))) {\n                return;\n            }\n        }\n        $server->shutdown();\n        echo \"DONE\\n\";\n    });\n\n    $server->start();\n});\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/websocket_mixed.phpt",
    "content": "--TEST--\nswoole_http_server_coro: bad request\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Coroutine\\Socket;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\nconst REQUEST =\n    \"GET /websocket HTTP/1.1\\r\\n\" .\n    \"Host: 127.0.0.1\\r\\n\" .\n    \"Connection: Upgrade\\r\\n\" .\n    \"Upgrade: websocket\\r\\n\" .\n    \"Sec-WebSocket-Version: 13\\r\\n\" .\n    \"Sec-WebSocket-Key: ZE5FYi8lZlZBbnlrTmxYKQ==\\r\\n\\r\\n\" .\n    \"\\x81\\x0cHello Swoole\";\n\nCoroutine\\run(function () {\n    $server = new Server('127.0.0.1', 0);\n    Coroutine::create(function () use ($server) {\n        $server->handle('/', function (Request $request, Response $websocket) use ($server) {\n            $websocket->upgrade();\n            $frame = $websocket->recv();\n            if ($frame) {\n                $websocket->push($frame);\n            }\n        });\n        $server->start();\n    });\n    Coroutine::sleep(0.001);\n    Coroutine::create(function () use ($server) {\n        $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        if (Assert::true($socket->connect('127.0.0.1', $server->port, -1))) {\n            $ret = $socket->sendAll(REQUEST);\n            Assert::same($ret, strlen(REQUEST));\n            Assert::contains($socket->recv(), 'Hello Swoole');\n            $server->shutdown();\n            echo \"DONE\\n\";\n        }\n    });\n});\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_http_server_coro/websocket_ping.phpt",
    "content": "--TEST--\nswoole_http_server_coro: websocket ping\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\WebSocket\\Frame;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Client('127.0.0.1', $pm->getFreePort());\n        $cli->set([\n            'timeout' => 5,\n            'open_websocket_ping_frame' => true,\n            'open_websocket_pong_frame' => true,\n            'open_websocket_close_frame' => true,\n        ]);\n        $ret = $cli->upgrade('/websocket');\n        Assert::assert($ret);\n        $cli->push('Swoole');\n        $ret = $cli->recv();\n        Assert::same($ret->opcode, WEBSOCKET_OPCODE_PING);\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/websocket', function ($request, $ws) {\n            $ws->upgrade();\n            while (true) {\n                $frame = $ws->recv();\n                if ($frame === false) {\n                    echo \"error : \" . swoole_last_error() . \"\\n\";\n                    break;\n                } else if ($frame === '') {\n                    break;\n                } else {\n                    usleep(10000);\n                    $ws->ping();\n                }\n            }\n        });\n        $server->start();\n        $pm->wakeup();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_http_server_coro/websocket_ping_pong.phpt",
    "content": "--TEST--\nswoole_http_server_coro: websocket ping pong\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\WebSocket\\Frame;\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new Client('127.0.0.1', $pm->getFreePort());\n        $cli->set([\n            'timeout' => 5,\n            'open_websocket_ping_frame' => true,\n            'open_websocket_pong_frame' => true,\n            'open_websocket_close_frame' => true,\n        ]);\n        $ret = $cli->upgrade('/websocket');\n        Assert::assert($ret);\n        $cli->push('Swoole');\n        $ret = $cli->recv();\n        Assert::same($ret->data, \"How are you, Swoole?\");\n        $ret = $cli->recv();\n        Assert::same($ret->opcode, WEBSOCKET_OPCODE_PING);\n        $pingFrame = new Frame;\n        $pingFrame->opcode = WEBSOCKET_OPCODE_PING;\n        // 发送 PING\n        $cli->push($pingFrame);\n        $ret = $cli->recv();\n        Assert::same($ret->opcode, WEBSOCKET_OPCODE_PONG);\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->set([\n            'open_websocket_ping_frame' => true,\n            'open_websocket_pong_frame' => true,\n            'open_websocket_close_frame' => true,\n        ]);\n        $server->handle('/websocket', function ($request, $ws) {\n            $ws->upgrade();\n            while (true) {\n                $frame = $ws->recv();\n                if ($frame === false) {\n                    echo \"error : \" . swoole_last_error() . \"\\n\";\n                    break;\n                } else if ($frame === '') {\n                    break;\n                } else {\n                    if ($frame->opcode === 9) {\n                        $pFrame = new Frame;\n                        $pFrame->opcode = WEBSOCKET_OPCODE_PONG;\n                        $ws->push($pFrame);\n                    } else {\n                        $ws->push(\"How are you, {$frame->data}?\");\n                        $pFrame = new Frame;\n                        // 发送 PING\n                        $pFrame->opcode = WEBSOCKET_OPCODE_PING;\n                        $ws->push($pFrame);\n                    }\n                }\n            }\n        });\n        $server->start();\n        $pm->wakeup();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_iouring/mix.phpt",
    "content": "--TEST--\nswoole_iouring: support io_uring\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_iouring();\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\nuse Swoole\\Coroutine\\WaitGroup;\nrequire __DIR__ . '/../include/bootstrap.php';\n\nRuntime::enableCoroutine(SWOOLE_HOOK_ALL);\n\n$setting = [\n    'iouring_workers' => 32,\n    'iouring_entries' => 30000,\n];\n\nif (defined('SWOOLE_IOURING_SQPOLL')) {\n    $setting['iouring_flag'] = SWOOLE_IOURING_SQPOLL;\n}\n\nswoole_async_set($setting);\n\n$results = [];\nfor ($i = 1; $i <= 10000; $i++) {\n    $results[$i] = random_bytes(rand(8192, 8192 * 3));\n}\n\nrun(function() use ($results) {\n    $filesize = 1048576;\n    $content = random_bytes($filesize);\n    $fileName = '/tmp/test_file';\n    Assert::eq(file_put_contents($fileName, $content), 1048576);\n    var_dump(stat($fileName));\n    for ($i = 0; $i < 100; $i++) {\n        Assert::eq(filesize($fileName), 1048576);\n        Assert::eq(file_get_contents($fileName), $content);\n    }\n    unlink($fileName);\n    Assert::true(!file_exists($fileName));\n\n    $stream = fopen($fileName, 'w');\n    fwrite($stream, $content);\n    if (PHP_VERSION_ID >= 80100) {\n        Assert::true(fdatasync($stream));\n        Assert::true(fsync($stream));\n    }\n    Assert::eq(file_get_contents($fileName), $content);\n    var_dump(fstat($stream));\n    fclose($stream);\n    unlink($fileName);\n\n    file_put_contents($fileName, $content);\n    rename($fileName, $fileName.'aaa');\n    Assert::true(!file_exists($fileName));\n    Assert::true(file_exists($fileName.'aaa'));\n    unlink($fileName.'aaa');\n\n    $directory = '/tmp/a/b/c/d/e/f';\n    mkdir($directory, 0755, true);\n    Assert::true(is_dir($directory));\n    rmdir($directory);\n    Assert::true(!is_dir($directory));\n\n\t$waitGroup = new WaitGroup();\n    for ($i = 1; $i <= 10000; $i++) {\n        go(function() use ($waitGroup, $i, $results){\n            $waitGroup->add();\n            $filename = '/tmp/file'.$i;\n            file_put_contents($filename, $results[$i]);\n            Assert::true($results[$i] == file_get_contents($filename));\n            file_put_contents($filename, $results[$i], FILE_APPEND);\n            file_put_contents($filename, $results[$i], FILE_APPEND);\n            Assert::true(strlen($results[$i]) * 3 == strlen(file_get_contents($filename)));\n\n            $stream = fopen($filename, 'r+');\n            $size = rand(1, filesize($filename));\n            Assert::true(ftruncate($stream, $size));\n            fclose($stream);\n            Assert::true($size == strlen(file_get_contents($filename)));\n            $waitGroup->done();\n        });\n    }\n\t$waitGroup->wait();\n    echo 'SUCCESS';\n});\n?>\n--EXPECTF--\narray(26) {\n  [0]=>\n  int(%d)\n  [1]=>\n  int(%d)\n  [2]=>\n  int(%d)\n  [3]=>\n  int(%d)\n  [4]=>\n  int(%d)\n  [5]=>\n  int(%d)\n  [6]=>\n  int(%d)\n  [7]=>\n  int(%d)\n  [8]=>\n  int(%d)\n  [9]=>\n  int(%d)\n  [10]=>\n  int(%d)\n  [11]=>\n  int(%d)\n  [12]=>\n  int(%d)\n  [\"dev\"]=>\n  int(%d)\n  [\"ino\"]=>\n  int(%d)\n  [\"mode\"]=>\n  int(%d)\n  [\"nlink\"]=>\n  int(%d)\n  [\"uid\"]=>\n  int(%d)\n  [\"gid\"]=>\n  int(%d)\n  [\"rdev\"]=>\n  int(%d)\n  [\"size\"]=>\n  int(%d)\n  [\"atime\"]=>\n  int(%d)\n  [\"mtime\"]=>\n  int(%d)\n  [\"ctime\"]=>\n  int(%d)\n  [\"blksize\"]=>\n  int(%d)\n  [\"blocks\"]=>\n  int(%d)\n}\narray(26) {\n  [0]=>\n  int(%d)\n  [1]=>\n  int(%d)\n  [2]=>\n  int(%d)\n  [3]=>\n  int(%d)\n  [4]=>\n  int(%d)\n  [5]=>\n  int(%d)\n  [6]=>\n  int(%d)\n  [7]=>\n  int(%d)\n  [8]=>\n  int(%d)\n  [9]=>\n  int(%d)\n  [10]=>\n  int(%d)\n  [11]=>\n  int(%d)\n  [12]=>\n  int(%d)\n  [\"dev\"]=>\n  int(%d)\n  [\"ino\"]=>\n  int(%d)\n  [\"mode\"]=>\n  int(%d)\n  [\"nlink\"]=>\n  int(%d)\n  [\"uid\"]=>\n  int(%d)\n  [\"gid\"]=>\n  int(%d)\n  [\"rdev\"]=>\n  int(%d)\n  [\"size\"]=>\n  int(%d)\n  [\"atime\"]=>\n  int(%d)\n  [\"mtime\"]=>\n  int(%d)\n  [\"ctime\"]=>\n  int(%d)\n  [\"blksize\"]=>\n  int(%d)\n  [\"blocks\"]=>\n  int(%d)\n}\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_iouring/setting.phpt",
    "content": "--TEST--\nswoole_iouring: iouring setting test\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_no_iouring();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->initRandomData(1);\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $headers = httpGetHeaders(\"http://127.0.0.1:{$pm->getFreePort()}\");\n    });\n\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $http->set([\n        'iouring_flag' => SWOOLE_IOURING_SQPOLL,\n        'iouring_entries' => 4096,\n        'iouring_workers' => 16\n    ]);\n    $http->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($pm) {\n         $response->status(200, \"status\");\n         $response->end(\"Hello World\");\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_library/array_object/base.phpt",
    "content": "--TEST--\nswoole_library/array_object: base\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$data = ['foo', 'bar', 'char' => 'dua'];\n$array = _array();\nAssert::true($array->isEmpty());\n$array->__construct($data);\nAssert::false($array->isEmpty());\nAssert::same($array->__toArray(), $data);\nAssert::same($array[0], $data[0]);\nAssert::isIterable($array);\nAssert::eq($array, _array()->unserialize($array->serialize()));\n$array->push('OK');\n$array->pushBack($array->pop());\necho $array->popBack() . PHP_EOL;\nAssert::same($array->count(), count($data));\nAssert::true($array->clear()->isEmpty());\nAssert::isEmpty($array->__toArray());\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_library/database/mysqli.phpt",
    "content": "--TEST--\nswoole_library/database: pdo\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_file_not_exist(__DIR__ . '/../../../library/example/mysqli/base.php');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire __DIR__ . '/../../../library/example/mysqli/base.php';\n?>\n--EXPECTF--\nUse %fs for %d queries\n"
  },
  {
    "path": "tests/swoole_library/database/pdo.phpt",
    "content": "--TEST--\nswoole_library/database: pdo\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_file_not_exist(__DIR__ . '/../../../library/example/pdo/base.php');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire __DIR__ . '/../../../library/example/pdo/base.php';\n?>\n--EXPECTF--\nUse %fs for %d queries\n"
  },
  {
    "path": "tests/swoole_library/database/redis.phpt",
    "content": "--TEST--\nswoole_library/database: pdo\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_file_not_exist(__DIR__ . '/../../../library/example/redis/base.php');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire __DIR__ . '/../../../library/example/redis/base.php';\n?>\n--EXPECTF--\nUse %fs for %d queries\n"
  },
  {
    "path": "tests/swoole_library/exec/exec/1.phpt",
    "content": "--TEST--\nswoole_library/exec/exec: Test exec\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../../include/skipif.inc';\nskip_if_command_not_found('md5sum');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    $data = exec('md5sum ' . TEST_IMAGE, $output, $returnVar);\n    Assert::same($returnVar, 0);\n    Assert::same(strstr(implode(PHP_EOL, $output), ' ', true), md5_file(TEST_IMAGE));\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_library/exec/exec/2.phpt",
    "content": "--TEST--\nswoole_library/exec/exec: Fix $output result inconsistency\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../../include/bootstrap.php';\n\n$fileName = __DIR__ . '/exec_test.php';\nfile_put_contents($fileName, \"1   \\r\\n2\\r\\n3\\r\\n\");\n\nexec('php ' . $fileName, $output1);\n\nSwoole\\Runtime::enableCoroutine();\ngo(function () use ($output1, $fileName) {\n    exec('php ' . $fileName, $output2);\n    Assert::same($output2, $output1);\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_library/exec/shell_exec/1.phpt",
    "content": "--TEST--\nswoole_library/exec/shell_exec: shell_exec\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../../include/skipif.inc';\nskip_if_command_not_found('md5sum');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    $output = shell_exec('md5sum ' . TEST_IMAGE);\n    Assert::same(strstr($output, ' ', true), md5_file(TEST_IMAGE));\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_library/multibyte_string_object/base.phpt",
    "content": "--TEST--\nswoole_library/multibyte_string_object: base\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$str = swoole_mbstring('我是中国人');\nvar_dump((string) $str->substr(0));\nvar_dump((string) $str->substr(2, 2));\nvar_dump($str->contains('中国'));\nvar_dump($str->contains('美国'));\nvar_dump($str->startsWith('我'));\nvar_dump($str->endsWith('不是'));\nvar_dump($str->length());\n\n?>\n--EXPECT--\nstring(15) \"我是中国人\"\nstring(6) \"中国\"\nbool(true)\nbool(false)\nbool(true)\nbool(false)\nint(5)\n"
  },
  {
    "path": "tests/swoole_library/name_resolver/1.phpt",
    "content": "--TEST--\nswoole_library/name_resolver: resolve\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_no_database();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Http\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\NameResolver\\Consul;\nuse Swoole\\NameResolver\\Redis;\n\nuse function Swoole\\Coroutine\\run;\n\nconst SERVICE_NAME = 'test_service';\nconst REQ_N = 16;\nconst PORT_N = 3;\n\n$config = TEST_NAME_RESOLVER;\n$ns = new $config['class']($config['server_url']);\nCoroutine::set(['name_resolver' => [$ns]]);\n\n$html = base64_encode(random_bytes(rand(2048, 65536 * 2)));\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->initFreePorts(PORT_N);\n\n$pm->parentFunc = function ($pid) use ($pm, $ns, $html) {\n    Coroutine::set(['name_resolver' => [$ns]]);\n    run(function () use ($html) {\n        swoole_loop_n(REQ_N, function () use ($html) {\n            $client = new Client(SERVICE_NAME);\n            $client->set(['max_retries' => PORT_N]);\n            $r = $client->get('/');\n            Assert::true($r);\n            Assert::eq($client->getBody(), $html);\n        });\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $ns, $html) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(0), SERVER_MODE_RANDOM);\n    $serv->addListener('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP);\n    $serv->set([\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on(\"workerStart\", function ($serv, $workerId) use ($pm, $ns) {\n        if ($workerId == 0) {\n            swoole_loop_n(PORT_N, function ($i) use ($pm, $ns) {\n                $ns->join(SERVICE_NAME, '127.0.0.1', $pm->getFreePort($i));\n            });\n            $pm->wakeup();\n        }\n    });\n    $serv->on('request', function ($req, $resp) use ($pm, $html) {\n        if ($req->server['server_port'] == $pm->getFreePort(1)) {\n            $resp->status(503);\n            $resp->end();\n            return;\n        }\n        $resp->end($html);\n    });\n    $serv->on('beforeShutdown', function ($serv) use ($pm, $ns) {\n        swoole_loop_n(PORT_N, function ($i) use ($pm, $ns) {\n            $ns->leave(SERVICE_NAME, '127.0.0.1', $pm->getFreePort($i));\n        });\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_library/name_resolver/lookup.phpt",
    "content": "--TEST--\nswoole_library/name_resolver: lookup\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_no_database();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\n\nuse function Swoole\\Coroutine\\run;\n\n$config = TEST_NAME_RESOLVER;\n$ns = new $config['class']($config['server_url']);\nCoroutine::set(['name_resolver' => [$ns]]);\n\nconst N = 4;\n\nrun(function () use ($ns) {\n    $test_name = 'test_resolver_1';\n    $nodes = [];\n    swoole_loop_n(N, function () use (&$nodes, $test_name, $ns) {\n        $node = ['port' => rand(1, 9999), 'ip' => '192.168.1.' . rand(1, 255)];\n        $nodes[] = $node;\n        $ns->join($test_name, $node['ip'], $node['port']);\n    });\n\n    $ctx = new Swoole\\NameResolver\\Context(AF_INET, true);\n    swoole_loop_n(N * 2, function ($i) use (&$nodes, $test_name, $ns, $ctx) {\n        $rs = swoole_name_resolver_lookup($test_name, $ctx);\n        Assert::notEmpty($rs);\n        [$ip, $port] = explode(':', $rs);\n        $node = ['ip' => $ip, 'port' => $port];\n        Assert::true(in_array($node, $nodes));\n    });\n\n    swoole_loop_n(N, function ($i) use (&$nodes, $test_name, $ns) {\n        $ns->leave($test_name, $nodes[$i]['ip'], $nodes[$i]['port']);\n    });\n});\necho \"DONE\\n\";\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_library/string_object/base.phpt",
    "content": "--TEST--\nswoole_library/string_object: base\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$string = _string('www.swoole.com ')->rtrim();\n$array = $string->split('.');\nAssert::same($array->count(), 3);\nAssert::same((string)$string->substr(_string($array[0])->length() + 1), 'swoole.com');\nAssert::same((string)$string->upper(), 'WWW.SWOOLE.COM');\necho $string->upper()->substr(-8, 1) . 'K' . PHP_EOL;\nAssert::same((string)$string, 'www.swoole.com');\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_library/wait_group/normal.phpt",
    "content": "--TEST--\nswoole_library/wait_group: base\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\ngo(function () {\n    $wg = new Swoole\\Coroutine\\WaitGroup;\n\t$ret = [];\n    for($i=0;$i<10;$i++) {\n        $wg->add();\n        go(function() use ($wg, $i, &$ret) {\t\t\n            co::sleep(0.1);\t\t\t\n\t\t\t$ret[$i] = $i;\n            $wg->done();\n        });\n    }\n\t$wg->wait();\n\tAssert::assert(count($ret) == 10);\n});\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_library/wait_group/timeout.phpt",
    "content": "--TEST--\nswoole_library/wait_group: base\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\ngo(function () {\n\t$start_time = microtime(1);\n    $wg = new Swoole\\Coroutine\\WaitGroup;\n\t$ret = [];\n    for($i=0;$i<10;$i++) {\n        $wg->add();\n        go(function() use ($wg, $i, &$ret) {\t\t\n\t\t\t$time = rand(5,15)/10;\n            co::sleep($time);\t\t\t\n\t\t\t$ret[$i] = $time;\n            $wg->done();\n        });\n    }\n    $std_time = 1;\n\t$wg->wait($std_time);\n\t$end_time = microtime(1);\n\t$used_time = $end_time - $start_time;\n\techo \"all done, use time $used_time\\n\";\n\tAssert::assert(abs($std_time - $used_time) < 0.5);\n});\n?>\n--EXPECTF--\nall done, use time %s\n"
  },
  {
    "path": "tests/swoole_lock/bug_5984.phpt",
    "content": "--TEST--\nswoole_lock: Github Bug #5984\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine as Co;\nCo::set(['hook_flags' => SWOOLE_HOOK_ALL & ~SWOOLE_HOOK_CURL]);\n$fileEx = '/tmp/test_ex.txt';\nfile_put_contents($fileEx, '0');\n\n$lockEx = file_get_contents($fileEx);\nCo\\run(function () use ($fileEx) {\n\tfor ($i = 0; $i < 50; $i++) {\n\t\tgo(function () use ($i, $fileEx) {\n\t\t\t$fp = fopen($fileEx, 'r+');\n\t\t\tflock($fp, LOCK_EX);\n\t\t\t$val = (int) file_get_contents($fileEx);\n\t\t\tusleep(100);\n\t\t\tfile_put_contents($fileEx, (string) ($val + 1));\n\t\t\tflock($fp, LOCK_UN);\n\t\t\tfclose($fp);\n\t\t});\n\t}\n});\n\n$val = (int) file_get_contents($fileEx);\necho $val === 50 ? \"✓ 通过 (计数=$val)\\n\" : \"✗ 失败 (计数=$val, 预期50)\\n\";\n?>\n--EXPECT--\n✓ 通过 (计数=50)\n"
  },
  {
    "path": "tests/swoole_lock/lock_nb.phpt",
    "content": "--TEST--\nswoole_lock: lock nb\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$lock = new Swoole\\Lock();\nAssert::true($lock->lock());\n\n$ret = $lock->lock(LOCK_EX | LOCK_NB);\nAssert::false($ret);\n\n$lock->unlock();\nAssert::true($lock->lock(LOCK_EX | LOCK_NB));\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_lock/lock_read.phpt",
    "content": "--TEST--\nswoole_lock: lock read\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Process;\nuse Swoole\\Lock;\n\n$lock = new Lock(Lock::RWLOCK);\n$begin = microtime(true);\n$process1 = new Process(function ($p) use ($lock) {\n    $lock->lock(LOCK_SH);\n    usleep(200_000);\n    $lock->unlock();\n});\n$process1->start();\n\n$process2 = new Process(function ($p) use ($lock) {\n    $lock->lock(LOCK_SH);\n    usleep(200_000);\n    $lock->unlock();\n});\n$process2->start();\n\nProcess::wait();\nProcess::wait();\n\n// Using shared locks, two processes will get locks at the same time and execute them concurrently\nAssert::lessThan(microtime(true) - $begin, 0.35);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_lock/lock_timeout.phpt",
    "content": "--TEST--\nswoole_lock: lock timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$lock = new Swoole\\Lock();\nvar_dump($lock->lock());\n\n$start = microtime(true);\n$ret = $lock->lock(LOCK_EX, 0.2);\nAssert::false($ret);\n$end = microtime(true);\n\nAssert::eq($lock->errCode, SOCKET_ETIMEDOUT);\nAssert::greaterThanEq($end - $start, 0.2);\n\n?>\n--EXPECT--\nbool(true)\n"
  },
  {
    "path": "tests/swoole_lock/mutex.phpt",
    "content": "--TEST--\nswoole_lock: mutex\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process;\nuse Swoole\\Lock;\n\n$fp = STDOUT;\n\n$lock = new Lock(SWOOLE_MUTEX);\n$pid = posix_getpid();\nfwrite($fp, \"[Master] Create Lock\\n\");\n$lock->lock();\n\n$process1 = new Process(function ($p) use ($lock, $fp) {\n    fwrite($fp, \"[Child 1] Wait Lock\\n\");\n    $lock->lock();\n    usleep(10);\n    fwrite($fp, \"[Child 1] Get Lock\\n\");\n    $lock->unlock();\n    fwrite($fp, \"[Child 1] exit\\n\");\n});\n$process1->start();\n\n$process2 = new Process(function ($p) use ($lock, $fp) {\n    fwrite($fp, \"[Child 2] Sleep\\n\");\n    sleep(1);\n    fwrite($fp, \"[Child 2] Release Lock\\n\");\n    $lock->unlock();\n    fwrite($fp, \"[Child 2] exit\\n\");\n});\n$process2->start();\n\nProcess::wait();\nProcess::wait();\n?>\n--EXPECTF--\n[Master] Create Lock\n[Child 1] Wait Lock\n[Child 2] Sleep\n[Child 2] Release Lock\n[Child 2] exit\n[Child 1] Get Lock\n[Child 1] exit\n"
  },
  {
    "path": "tests/swoole_lock/spinlock_timeout.phpt",
    "content": "--TEST--\nswoole_lock: lock timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_not_defined('SWOOLE_SPINLOCK');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$lock = new Swoole\\Lock(SWOOLE_SPINLOCK);\nvar_dump($lock->lock());\n\n$start = microtime(true);\n$ret = $lock->lock(LOCK_EX, 0.2);\nAssert::false($ret);\n$end = microtime(true);\n\nAssert::eq($lock->errCode, SOCKET_ETIMEDOUT);\nAssert::greaterThanEq($end - $start, 0.2);\n\n?>\n--EXPECT--\nbool(true)\n"
  },
  {
    "path": "tests/swoole_pdo_firebird/base.phpt",
    "content": "--TEST--\nswoole_pdo_firebird: test hook pdo_firebird\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire_once __DIR__ . '/../include/bootstrap.php';\nrequire_once __DIR__ . '/pdo_firebird.inc';\nPdoFirebirdTest::skip();\n?>\n--FILE--\n<?php\nrequire_once __DIR__ . '/../include/bootstrap.php';\nrequire_once __DIR__ . '/pdo_firebird.inc';\n\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags' => SWOOLE_HOOK_PDO_FIREBIRD]);\nrun(static function (): void {\n    $db = PdoFirebirdTest::create();\n\n    $db->exec('CREATE TABLE test_table (id INTEGER PRIMARY KEY, name VARCHAR(50))');\n\n    $db->exec(\"INSERT INTO test_table VALUES (1, 'Firebird Test')\");\n\n    $stmt = $db->query('SELECT * FROM test_table');\n    $result = $stmt->fetch(PDO::FETCH_ASSOC);\n\n    $stmt = null;\n    var_dump($result);\n\n    $db->exec('DROP TABLE test_table');\n});\necho \"DONE\\n\";\n?>\n--EXPECTF--\narray(2) {\n  [\"ID\"]=>\n  int(1)\n  [\"NAME\"]=>\n  string(13) \"Firebird Test\"\n}\nDONE\n"
  },
  {
    "path": "tests/swoole_pdo_firebird/pdo_firebird.inc",
    "content": "<?php\ndeclare(strict_types=1);\n\nclass PdoFirebirdTest\n{\n    public static function skip() {\n        try {\n            $db = self::create();\n        } catch (PDOException $e) {\n            die(\"skip \" . $e->getMessage());\n        }\n    }\n\n    public static function create(): PDO\n    {\n        return new PDO(FIREBIRD_DSN, FIREBIRD_USER, FIREBIRD_PASSWORD);\n    }\n}\n"
  },
  {
    "path": "tests/swoole_pdo_firebird/query.phpt",
    "content": "--TEST--\nswoole_pdo_firebird: test query\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire_once __DIR__ . '/../include/bootstrap.php';\nrequire_once __DIR__ . '/pdo_firebird.inc';\nPdoFirebirdTest::skip();\n?>\n--FILE--\n<?php\nrequire_once __DIR__ . '/../include/bootstrap.php';\nrequire_once __DIR__ . '/pdo_firebird.inc';\n\nconst N = 10;\n\nCo::set(['hook_flags' => SWOOLE_HOOK_PDO_FIREBIRD]);\nCo\\run(static function (): void {\n    $db = PdoFirebirdTest::create();\n\n    // 准备测试表\n    try {\n        $db->exec('DROP TABLE concurrent_test');\n        try {\n            $db->exec('COMMIT');\n        } catch (PDOException $e) {\n            // 忽略COMMIT失败的错误\n        }\n    } catch (PDOException $e) {\n        // 表不存在时的错误可以忽略\n    }\n\n    // 创建测试表\n    $db->exec('CREATE TABLE concurrent_test (id INTEGER PRIMARY KEY, name VARCHAR(100), age INTEGER)');\n\n    $stmt = $db->prepare('INSERT INTO concurrent_test (id, name, age) values (?, ?, ?)');\n\n    $list = [];\n    for ($i = 0; $i < N; $i++) {\n        $id = $i + 1;\n        $name = base64_encode(random_bytes(8));\n        $age = random_int(18, 35);\n        $stmt->bindValue(1, $id);\n        $stmt->bindValue(2, $name);\n        $stmt->bindValue(3, $age);\n        $stmt->execute();\n\n        $list[] = [\n            'id' => $id,\n            'name' => $name,\n            'age' => $age\n        ];\n    }\n\n    // 创建通道用于同步\n    $channel = new Co\\Channel(N);\n\n    foreach ($list as $rs) {\n        Co\\go(function () use ($rs, $channel) {\n            $db = PdoFirebirdTest::create();\n            try {\n                $statement = $db->query('SELECT * FROM concurrent_test WHERE id = ' . $rs['id'] . ' ROWS 1');\n                $result = $statement->fetch(PDO::FETCH_ASSOC);\n                Assert::eq($result['ID'], $rs['id']);\n                Assert::eq($result['NAME'], $rs['name']);\n                Assert::eq($result['AGE'], $rs['age']);\n            } catch (PDOException $e) {\n                echo \"Error in coroutine: \" . $e->getMessage() . \"\\n\";\n            }\n            // 通知主协程当前协程已完成\n            $channel->push(true);\n        });\n    }\n\n    // 等待所有协程完成\n    for ($i = 0; $i < N; $i++) {\n        $channel->pop();\n    }\n\n    // 所有协程完成后再清理测试表\n    try {\n        $db->exec('DROP TABLE concurrent_test');\n    } catch (PDOException $e) {\n        // 忽略清理错误\n    }\n});\n\necho \"Done\\n\";\n?>\n--EXPECTF--\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_firebird/sleep.phpt",
    "content": "--TEST--\nswoole_pdo_firebird: test hook firebird sleep\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire_once __DIR__ . '/../include/bootstrap.php';\nrequire_once __DIR__ . '/pdo_firebird.inc';\nPdoFirebirdTest::skip();\n?>\n--FILE--\n<?php\nrequire_once __DIR__ . '/../include/bootstrap.php';\nrequire_once __DIR__ . '/pdo_firebird.inc';\n\nconst N = 20;\n\nCo::set(['hook_flags' => SWOOLE_HOOK_PDO_FIREBIRD]);\nCo\\run(static function (): void {\n    $sleep_count = 0;\n    Co\\go(function () use (&$sleep_count) {\n        $n = N;\n        while ($n--) {\n            Co::sleep(0.002);\n            $sleep_count++;\n        }\n    });\n\n    $db = PdoFirebirdTest::create();\n\n    $iterations = 50;\n    for ($i = 0; $i < $iterations; $i++) {\n        $statement = $db->query('SELECT COUNT(*) FROM RDB$RELATIONS');\n        $statement->fetch();\n    }\n\n    Assert::greaterThanEq($sleep_count, 10);\n});\necho \"Done\\n\";\n?>\n--EXPECTF--\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_firebird/transaction.phpt",
    "content": "--TEST--\nswoole_pdo_firebird: test transaction\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire_once __DIR__ . '/../include/bootstrap.php';\nrequire_once __DIR__ . '/pdo_firebird.inc';\nPdoFirebirdTest::skip();\n?>\n--FILE--\n<?php\nrequire_once __DIR__ . '/../include/bootstrap.php';\nrequire_once __DIR__ . '/pdo_firebird.inc';\n\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags' => SWOOLE_HOOK_PDO_FIREBIRD]);\nrun(static function (): void {\n    $db = PdoFirebirdTest::create();\n\n    // 先尝试删除可能存在的表（忽略错误）\n    try {\n        $db->exec('DROP TABLE transaction_test');\n        // 使用try-catch包围COMMIT\n        try {\n            $db->exec('COMMIT');\n        } catch (PDOException $e) {\n            // 忽略COMMIT失败的错误\n        }\n    } catch (PDOException $e) {\n        // 表不存在时的错误可以忽略\n    }\n\n    // 创建测试表 - 将value改为test_value以避免保留关键字冲突\n    $db->exec('CREATE TABLE transaction_test (id INTEGER PRIMARY KEY, test_value VARCHAR(50))');\n\n    try {\n        // 开始事务\n        $db->beginTransaction();\n\n        // 插入数据 - 更新列名\n        $db->exec(\"INSERT INTO transaction_test VALUES (1, 'Value 1')\");\n        $db->exec(\"INSERT INTO transaction_test VALUES (2, 'Value 2')\");\n\n        // 提交事务\n        $db->commit();\n\n        // 验证数据已插入\n        $stmt = $db->query('SELECT COUNT(*) FROM transaction_test');\n        $count = $stmt->fetchColumn();\n        echo \"Count after commit: \", $count, \"\\n\";\n\n        // 再次开始事务\n        $db->beginTransaction();\n        $db->exec(\"INSERT INTO transaction_test VALUES (3, 'Value 3')\");\n\n        // 回滚事务\n        $db->rollback();\n\n        // 验证回滚后的数据\n        $stmt = $db->query('SELECT COUNT(*) FROM transaction_test');\n        $count = $stmt->fetchColumn();\n        echo \"Count after rollback: \", $count, \"\\n\";\n\n    } catch (PDOException $e) {\n        echo \"Error: \", $e->getMessage(), \"\\n\";\n    }\n\n    // 清理测试表\n    try {\n        $db->exec('DROP TABLE transaction_test');\n    } catch (PDOException $e) {\n        // 如果删除失败，尝试使用更安全的方式处理事务\n        try {\n            // 首先尝试回滚任何活动事务\n            $db->rollBack();\n        } catch (PDOException $e) {\n            // 忽略回滚失败的错误\n        }\n        // 再次尝试删除表\n        try {\n            $db->exec('DROP TABLE transaction_test');\n        } catch (PDOException $e) {\n            // 忽略第二次删除失败的错误\n        }\n    }\n});\n\necho \"DONE\\n\";\n?>\n--EXPECT--\nCount after commit: 2\nCount after rollback: 2\nDONE\n"
  },
  {
    "path": "tests/swoole_pdo_odbc/base.phpt",
    "content": "--TEST--\nswoole_pdo_odbc: test hook pdo_odbc\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nrequire __DIR__ . '/../include/bootstrap.php';\n\nCo\\run(static function (): void {\n    Co\\go(function () {\n        $pdo = new PDO(ODBC_DSN);\n        $statement = $pdo->prepare('show tables');\n        $statement->execute();\n        Assert::greaterThan(count($statement->fetchAll(PDO::FETCH_COLUMN)), 1);\n    });\n\n    Co\\go(function () {\n        $pdo = new PDO(ODBC_DSN);\n        $statement = $pdo->prepare('show tables');\n        $statement->execute();\n        Assert::greaterThan(count($statement->fetchAll(PDO::FETCH_COLUMN)), 1);\n    });\n});\n\necho \"DONE\\n\";\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_pdo_odbc/blocking.phpt",
    "content": "--TEST--\nswoole_pdo_odbc: test hook pgsql\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst N = 20;\n\nCo\\run(static function (): void {\n    $sleep_count = 0;\n    Co\\go(function () use (&$sleep_count) {\n        $n = N;\n        while ($n--) {\n            Co::sleep(0.002);\n            $sleep_count++;\n        }\n    });\n    // disable pdo_pgsql hook\n    Swoole\\Runtime::enableCoroutine(0);\n    $pdo = new PDO(ODBC_DSN);\n    $statement = $pdo->prepare('SELECT sleep(1) ss');\n    $statement->execute();\n    Assert::eq($sleep_count, 0);\n    Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'ss');\n});\n\necho \"Done\\n\";\n?>\n--EXPECTF--\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_odbc/query.phpt",
    "content": "--TEST--\nswoole_pdo_odbc: test query\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst N = 10;\n\nCo\\run(static function (): void {\n    $pdo = new PDO(ODBC_DSN);\n\n    $stmt = $pdo->prepare('INSERT INTO ckl (name, domain, path) values (?, ?, ?)');\n\n    $list = [];\n    for ($i = 0; $i < N; $i++) {\n        $row = [\n            'name' => base64_encode(random_bytes(8)),\n            'domain' => 'domain-' . random_int(10000, 99999),\n            'path' => '/' . uniqid() . '/' . $i,\n        ];\n        $list[] = $row;\n        $stmt->bindValue(1, $row['name']);\n        $stmt->bindValue(2, $row['domain']);\n        $stmt->bindValue(3, $row['path']);\n        $stmt->execute();\n    }\n\n    foreach ($list as $rs) {\n        Co\\go(function () use ($rs) {\n            $pdo = new PDO(ODBC_DSN);\n            $statement = $pdo->query('select name, domain, path from ckl where path = \"' . $rs['path'] . '\" limit 1');\n            Assert::eq($statement->fetch(PDO::FETCH_ASSOC), $rs);\n        });\n    }\n});\n\necho \"Done\\n\";\n?>\n--EXPECTF--\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_odbc/race.phpt",
    "content": "--TEST--\nswoole_pdo_odbc: race\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nrequire __DIR__ . '/../include/bootstrap.php';\n$begin = microtime(true);\n\nCo\\run(static function (): void {\n    $pdo = new PDO(ODBC_DSN);\n    $msg = [];\n    $n = 2;\n    while ($n--) {\n        Co\\go(function () use ($pdo, &$msg) {\n            $statement = $pdo->prepare('SELECT sleep(1) ss');\n            try {\n                $statement->execute();\n                Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'ss');\n            } catch (\\PDOException $e) {\n                $msg[] = $e->getMessage();\n            }\n        });\n    }\n    Assert::count($msg, 0);\n});\n\nAssert::greaterThanEq(microtime(true) - $begin, 2);\necho \"Done\\n\";\n?>\n--EXPECTF--\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_odbc/server_info.phpt",
    "content": "--TEST--\nswoole_pdo_odbc: test hook pgsql\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_php_version_lower_than('8.1');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nCo\\run(static function (): void {\n    $pdo = new PDO(ODBC_DSN);\n    $info = $pdo->getAttribute(PDO::ATTR_SERVER_INFO);\n    Assert::eq(strtolower($info), 'mysql');\n});\n\necho \"Done\\n\";\n?>\n--EXPECTF--\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_odbc/sleep.phpt",
    "content": "--TEST--\nswoole_pdo_odbc: test hook pgsql\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst N = 20;\n\nCo\\run(static function (): void {\n    $sleep_count = 0;\n    Co\\go(function () use (&$sleep_count) {\n        $n = N;\n        while ($n--) {\n            Co::sleep(0.002);\n            $sleep_count++;\n        }\n    });\n    $pdo = new PDO(ODBC_DSN);\n    $statement = $pdo->prepare('SELECT sleep(1) ss');\n    $statement->execute();\n    Assert::eq($sleep_count, N);\n    Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'ss');\n});\n\necho \"Done\\n\";\n?>\n--EXPECTF--\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_odbc/transaction.phpt",
    "content": "--TEST--\nswoole_pdo_odbc: test query\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n\n\nCo\\run(static function (): void {\n    $pdo = new PDO(ODBC_DSN);\n\n    $insertToTable = function ($pdo) {\n        $stmt = $pdo->prepare('INSERT INTO ckl (name, domain, path) values (?, ?, ?)');\n        $row = [\n            'name' => base64_encode(random_bytes(8)),\n            'domain' => 'domain-' . random_int(10000, 99999),\n            'path' => '/' . uniqid() . '/' . 0,\n        ];\n        $stmt->bindValue(1, $row['name']);\n        $stmt->bindValue(2, $row['domain']);\n        $stmt->bindValue(3, $row['path']);\n        $stmt->execute();\n    };\n\n    $countTable = function ($pdo) {\n        return $pdo->query('select count(*) c from ckl')->fetch(PDO::FETCH_ASSOC)['c'];\n    };\n\n    $insertToTable($pdo);\n    var_dump('insert');\n\n    $c1 = $countTable($pdo);\n\n    $pdo->beginTransaction();\n    $insertToTable($pdo);\n    $pdo->rollBack();\n    var_dump('rollback');\n\n    Assert::eq($countTable($pdo), $c1);\n\n    $pdo->beginTransaction();\n    $insertToTable($pdo);\n    $pdo->commit();\n    var_dump('commit');\n\n    Assert::eq($countTable($pdo), $c1 + 1);\n});\n\necho \"Done\\n\";\n?>\n--EXPECTF--\nstring(6) \"insert\"\nstring(8) \"rollback\"\nstring(6) \"commit\"\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/bug41996.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: PDO OCI Bug #41996 (Problem accessing Oracle ROWID)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $db = PdoOracleTest::create();\n    $stmt = $db->prepare('SELECT rowid FROM dual');\n    $stmt->execute();\n    $row = $stmt->fetch();\n    var_dump(strlen($row[0]) > 0);\n});\n?>\n--EXPECT--\nbool(true)\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/bug44301.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: PDO OCI Bug #44301 (Segfault when an exception is thrown on persistent connections)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $db = PdoOracleTest::create();\n    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n    $db->setAttribute(PDO::ATTR_PERSISTENT, true);\n    try {\n        $stmt = $db->prepare('SELECT * FROM no_table');\n        $stmt->execute();\n    } catch (PDOException $e) {\n        print $e->getMessage();\n    }\n    $db = null;\n});\n?>\n--EXPECTF--\nSQLSTATE[HY000]: General error: 942 OCIStmtExecute: ORA-00942: table or view %Sdoes not exist\n (%soci_statement.c:%d)\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/bug46274.phpt",
    "content": "--TEST--\nswoole_pdo_oracle:ATTR_STRINGIFY_FETCHES and blob)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $db = PdoOracleTest::create();\n    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n    $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);\n\n    try {\n        $db->exec(\"DROP TABLE test_one_blob\");\n    } catch (Exception $e) {\n    }\n\n    $db->beginTransaction();\n\n    $db->query('CREATE TABLE test_one_blob (id INT NOT NULL, blob1 BLOB)');\n\n    $stmt = $db->prepare(\"INSERT INTO test_one_blob (id, blob1) VALUES (:id, EMPTY_BLOB()) RETURNING blob1 INTO :foo\");\n\n    $data = 'foo';\n    $blob = fopen('php://memory', 'a');\n    fwrite($blob, $data);\n    rewind($blob);\n\n    $id = 1;\n    $stmt->bindparam(':id', $id);\n    $stmt->bindparam(':foo', $blob, PDO::PARAM_LOB);\n    $stmt->execute();\n\n    $data = '';\n    $blob = fopen('php://memory', 'a');\n    fwrite($blob, $data);\n    rewind($blob);\n\n    $id = 1;\n    $stmt->bindparam(':id', $id);\n    $stmt->bindparam(':foo', $blob, PDO::PARAM_LOB);\n    $stmt->execute();\n\n    $res = $db->query(\"SELECT blob1 from test_one_blob\");\n    // Resource\n    var_dump($res->fetch());\n\n    // Empty string\n    var_dump($res->fetch());\n\n    $db->exec(\"DROP TABLE test_one_blob\");\n});\n?>\n--EXPECT--\narray(2) {\n  [\"blob1\"]=>\n  string(3) \"foo\"\n  [0]=>\n  string(3) \"foo\"\n}\narray(2) {\n  [\"blob1\"]=>\n  string(0) \"\"\n  [0]=>\n  string(0) \"\"\n}\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/bug46274_2.phpt",
    "content": "--TEST--\nswoole_pdo_oracle:ATTR_STRINGIFY_FETCHES and blob)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $db = PdoOracleTest::create();\n    $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);\n\n    try {\n        $db->exec(\"DROP TABLE test_one_blob\");\n    } catch (Exception $e) {\n    }\n\n    $db->beginTransaction();\n\n    $db->query('CREATE TABLE test_one_blob (id INT NOT NULL, blob1 BLOB)');\n\n    $stmt = $db->prepare(\"INSERT INTO test_one_blob (id, blob1) VALUES (:id, EMPTY_BLOB()) RETURNING blob1 INTO :foo\");\n\n    $data = 'foo';\n    $blob = fopen('php://memory', 'a');\n    fwrite($blob, $data);\n    rewind($blob);\n\n    $id = 1;\n    $stmt->bindparam(':id', $id);\n    $stmt->bindparam(':foo', $blob, PDO::PARAM_LOB);\n    $stmt->execute();\n\n    $data = '';\n    $blob = fopen('php://memory', 'a');\n    fwrite($blob, $data);\n    rewind($blob);\n\n    $id = 1;\n    $stmt->bindparam(':id', $id);\n    $stmt->bindparam(':foo', $blob, PDO::PARAM_LOB);\n    $stmt->execute();\n\n    $res = $db->query(\"SELECT blob1 from test_one_blob\");\n    // Resource\n    var_dump($row = $res->fetch());\n    var_dump(fread($row[0], 1024));\n    fclose($row[0]);\n\n    // Empty string\n    var_dump($row = $res->fetch());\n    var_dump(fread($row[0], 1024));\n    fclose($row[0]);\n\n    $db->exec(\"DROP TABLE test_one_blob\");\n});\n?>\n--EXPECTF--\narray(2) {\n  [\"blob1\"]=>\n  resource(%d) of type (stream)\n  [0]=>\n  resource(%d) of type (stream)\n}\nstring(3) \"foo\"\narray(2) {\n  [\"blob1\"]=>\n  resource(%d) of type (stream)\n  [0]=>\n  resource(%d) of type (stream)\n}\nstring(0) \"\"\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/bug54379.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: UTF-8 output gets truncated)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $db = PdoOracleTest::create();\n    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n    try {\n            $db->exec(\"DROP TABLE test\");\n    } catch (Exception $e) {\n    }\n    $db->exec(\"CREATE TABLE test (col1 NVARCHAR2(20))\");\n    $db->exec(\"INSERT INTO test VALUES('12345678901234567890')\");\n    $db->exec(\"INSERT INTO test VALUES('あいうえおかきくけこさしすせそたちつてと')\");\n    $stmt = $db->prepare(\"SELECT * FROM test\");\n    $stmt->execute();\n    var_dump($stmt->fetchAll(PDO::FETCH_ASSOC));\n    $db->exec(\"DROP TABLE test\");\n});\n?>\n--EXPECT--\narray(2) {\n  [0]=>\n  array(1) {\n    [\"col1\"]=>\n    string(20) \"12345678901234567890\"\n  }\n  [1]=>\n  array(1) {\n    [\"col1\"]=>\n    string(60) \"あいうえおかきくけこさしすせそたちつてと\"\n  }\n}\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/bug57702.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: PDO OCI Bug #57702 (Multi-row BLOB fetches)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $db = PdoOracleTest::create();\n    // Note the PDO test setup sets PDO::ATTR_STRINGIFY_FETCHES to true\n    // (and sets PDO::ATTR_CASE to PDO::CASE_LOWER)\n\n    $query = \"begin execute immediate 'drop table bug57702'; exception when others then if sqlcode <> -942 then raise; end if; end;\";\n    $stmt = $db->prepare($query);\n    $stmt->execute();\n\n    $query = \"create table bug57702 (id number, data1 blob, data2 blob)\";\n    $stmt = $db->prepare($query);\n    $stmt->execute();\n\n    function do_insert($db, $id, $data1, $data2)\n    {\n        $db->beginTransaction();\n        $stmt = $db->prepare(\"insert into bug57702 (id, data1, data2) values (:id, empty_blob(), empty_blob()) returning data1, data2 into :blob1, :blob2\");\n        $stmt->bindParam(':id', $id);\n        $stmt->bindParam(':blob1', $blob1, PDO::PARAM_LOB);\n        $stmt->bindParam(':blob2', $blob2, PDO::PARAM_LOB);\n        $blob1 = null;\n        $blob2 = null;\n        $stmt->execute();\n\n        fwrite($blob1, $data1);\n        fclose($blob1);\n        fwrite($blob2, $data2);\n        fclose($blob2);\n        $db->commit();\n    }\n\n    do_insert($db, 1, \"row 1 col 1\", \"row 1 col 2\");\n    do_insert($db, 2, \"row 2 col 1\", \"row 2 col 2\");\n\n    ////////////////////\n\n    echo \"First Query\\n\";\n\n    // Fetch it back\n    $stmt = $db->prepare('select data1, data2 from bug57702 order by id');\n    $stmt->execute();\n    $row = $stmt->fetch(PDO::FETCH_ASSOC);\n    var_dump($row['data1']);\n    var_dump($row['data2']);\n    $row = $stmt->fetch(PDO::FETCH_ASSOC);\n    var_dump($row['data1']);\n    var_dump($row['data2']);\n\n    ////////////////////\n\n    echo \"\\nSecond Query\\n\";\n\n    foreach($db->query(\"select data1 as d1, data2 as d2 from bug57702 order by id\") as $row) {\n        var_dump($row['d1']);\n        var_dump($row['d2']);\n    }\n\n    ////////////////////\n\n    echo \"\\nThird Query\\n\";\n\n    $stmt = $db->prepare('select data1 as d3_1, data2 as d3_2 from bug57702 order by id');\n\n    $rs = $stmt->execute();\n    $stmt->bindColumn('d3_1' , $clob1, PDO::PARAM_LOB);\n    $stmt->bindColumn('d3_2' , $clob2, PDO::PARAM_LOB);\n\n    while ($stmt->fetch(PDO::FETCH_BOUND)) {\n        var_dump($clob1);\n        var_dump($clob2);\n    }\n\n    ////////////////////\n\n    echo \"\\nFourth Query\\n\";\n\n    $a = array();\n    $i = 0;\n    foreach($db->query(\"select data1 as d4_1, data2 as d4_2 from bug57702 order by id\") as $row) {\n        $a[$i][0] = $row['d4_1'];\n        $a[$i][1] = $row['d4_2'];\n        $i++;\n    }\n\n    for ($i = 0; $i < count($a); $i++) {\n        var_dump($a[$i][0]);\n        var_dump($a[$i][1]);\n    }\n\n    ////////////////////\n\n    echo \"\\nFifth Query\\n\";\n\n    $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);  // Let's use streams\n\n    // Since each column only has one lob descriptor, the last row is\n    // shown twice because the lob descriptor for each column is reused in\n    // the stream\n\n    $a = array();\n    $i = 0;\n    foreach($db->query(\"select data1 as d4_1, data2 as d4_2 from bug57702 order by id\") as $row) {\n        $a[$i][0] = $row['d4_1'];\n        $a[$i][1] = $row['d4_2'];\n        $i++;\n    }\n\n    for ($i = 0; $i < count($a); $i++) {\n        var_dump(stream_get_contents($a[$i][0]));\n        var_dump(stream_get_contents($a[$i][1]));\n    }\n\n    ////////////////////\n\n    echo \"\\nSixth Query\\n\";\n\n    $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);  // Let's use streams\n\n    $a = array();\n    $i = 0;\n    foreach($db->query(\"select data1 as d4_1, data2 as d4_2 from bug57702 order by id\") as $row) {\n        $a[$i][0] = $row['d4_1'];\n        $a[$i][1] = $row['d4_2'];\n        var_dump(stream_get_contents($a[$i][0]));\n        var_dump(stream_get_contents($a[$i][1]));\n        $i++;\n    }\n\n    // Cleanup\n    $query = \"drop table bug57702\";\n    $stmt = $db->prepare($query);\n    $stmt->execute();\n\n    print \"done\\n\";\n\n});\n?>\n--EXPECTF--\nFirst Query\nresource(%d) of type (stream)\nresource(%d) of type (stream)\nresource(%d) of type (stream)\nresource(%d) of type (stream)\n\nSecond Query\nresource(%d) of type (stream)\nresource(%d) of type (stream)\nresource(%d) of type (stream)\nresource(%d) of type (stream)\n\nThird Query\nresource(%d) of type (stream)\nresource(%d) of type (stream)\nresource(%d) of type (stream)\nresource(%d) of type (stream)\n\nFourth Query\nresource(%d) of type (stream)\nresource(%d) of type (stream)\nresource(%d) of type (stream)\nresource(%d) of type (stream)\n\nFifth Query\nstring(11) \"row 2 col 1\"\nstring(11) \"row 2 col 2\"\nstring(11) \"row 2 col 1\"\nstring(11) \"row 2 col 2\"\n\nSixth Query\nstring(11) \"row 1 col 1\"\nstring(11) \"row 1 col 2\"\nstring(11) \"row 2 col 1\"\nstring(11) \"row 2 col 2\"\ndone\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/bug60994.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: PDO OCI Bug #60994 (Reading a multibyte CLOB caps at 8192 characters)\n--SKIPIF--\n<?php\nif (PHP_VERSION < 80100) {\n\trequire __DIR__ . '/../include/skipif.inc';\n\tskip('php version 8.1 or higher');\n}\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $dbh = PdoOracleTest::create();\n    $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL);\n    $dbh->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);\n\n    $dbh->exec('CREATE TABLE pdo_oci_bug60994 (id NUMBER, data CLOB, data2 NCLOB)');\n\n    $id = null;\n    $insert = $dbh->prepare('INSERT INTO pdo_oci_bug60994 (id, data, data2) VALUES (:id, :data, :data2)');\n    $insert->bindParam(':id', $id, \\PDO::PARAM_STR);\n    $select = $dbh->prepare(\"SELECT data, data2 FROM pdo_oci_bug60994 WHERE id = :id\");\n\n\n    echo PHP_EOL, 'Test 1:  j', PHP_EOL;\n    $string1 = 'abc' . str_repeat('j', 8187) . 'xyz'; // 8193 chars total works fine here (even 1 million works fine, subject to memory_limit)\n    $id = 1;\n    $insert->bindParam(':data', $string1, \\PDO::PARAM_STR, strlen($string1)); // length in bytes\n    $insert->bindParam(':data2', $string1, \\PDO::PARAM_STR, strlen($string1));\n    $insert->execute();\n    $select->bindParam(':id', $id, \\PDO::PARAM_STR);\n    $select->execute();\n    $row = $select->fetch();\n    $stream1 = stream_get_contents($row['DATA']);\n    $start1  = mb_substr($stream1, 0, 10);\n    $ending1 = mb_substr($stream1, -10);\n    echo 'size of string1 is ', strlen($string1), ' bytes, ', mb_strlen($string1), ' chars.', PHP_EOL;\n    echo 'size of stream1 is ', strlen($stream1), ' bytes, ', mb_strlen($stream1), ' chars.', PHP_EOL;\n    echo 'beg  of stream1 is ', $start1, PHP_EOL;\n    echo 'end  of stream1 is ', $ending1, PHP_EOL;\n    if ($string1 != $stream1 || $stream1 != stream_get_contents($row['DATA2'])) {\n        echo 'Expected nclob value to match clob value for stream1', PHP_EOL;\n    }\n\n    echo PHP_EOL, 'Test 2:  £', PHP_EOL;\n    $string2 = 'abc' . str_repeat('£', 8187) . 'xyz'; // 8193 chars total is when it breaks\n    $id = 2;\n    $insert->bindParam(':data', $string2, \\PDO::PARAM_STR, strlen($string2)); // length in bytes\n    $insert->bindParam(':data2', $string2, \\PDO::PARAM_STR, strlen($string2));\n    $insert->execute();\n    $select->bindParam(':id', $id, \\PDO::PARAM_STR);\n    $select->execute();\n    $row = $select->fetch();\n    $stream2 = stream_get_contents($row['DATA']);\n    $start2  = mb_substr($stream2, 0, 10);\n    $ending2 = mb_substr($stream2, -10);\n    echo 'size of string2 is ', strlen($string2), ' bytes, ', mb_strlen($string2), ' chars.', PHP_EOL;\n    echo 'size of stream2 is ', strlen($stream2), ' bytes, ', mb_strlen($stream2), ' chars.', PHP_EOL;\n    echo 'beg  of stream2 is ', $start2, PHP_EOL;\n    echo 'end  of stream2 is ', $ending2, PHP_EOL;\n    if ($string2 != $stream2 || $stream2 != stream_get_contents($row['DATA2'])) {\n        echo 'Expected nclob value to match clob value for stream2', PHP_EOL;\n    }\n\n    echo PHP_EOL, 'Test 3:  Җ', PHP_EOL;\n    $string3 = 'abc' . str_repeat('Җ', 8187) . 'xyz'; // 8193 chars total is when it breaks\n    $id = 3;\n    $insert->bindParam(':data', $string3, \\PDO::PARAM_STR, strlen($string3)); // length in bytes\n    $insert->bindParam(':data2', $string3, \\PDO::PARAM_STR, strlen($string3));\n    $insert->execute();\n    $select->bindParam(':id', $id, \\PDO::PARAM_STR);\n    $select->execute();\n    $row = $select->fetch();\n    $stream3 = stream_get_contents($row['DATA']);\n    $start3  = mb_substr($stream3, 0, 10);\n    $ending3 = mb_substr($stream3, -10);\n    echo 'size of string3 is ', strlen($string3), ' bytes, ', mb_strlen($string3), ' chars.', PHP_EOL;\n    echo 'size of stream3 is ', strlen($stream3), ' bytes, ', mb_strlen($stream3), ' chars.', PHP_EOL;\n    echo 'beg  of stream3 is ', $start3, PHP_EOL;\n    echo 'end  of stream3 is ', $ending3, PHP_EOL;\n    if ($string3 != $stream3 || $stream3 != stream_get_contents($row['DATA2'])) {\n        echo 'Expected nclob value to match clob value for stream3', PHP_EOL;\n    }\n\n    echo PHP_EOL, 'Test 4:  の', PHP_EOL;\n    $string4 = 'abc' . str_repeat('の', 8187) . 'xyz'; // 8193 chars total is when it breaks\n    $id = 4;\n    $insert->bindParam(':data', $string4, \\PDO::PARAM_STR, strlen($string4)); // length in bytes\n    $insert->bindParam(':data2', $string4, \\PDO::PARAM_STR, strlen($string4));\n    $insert->execute();\n    $select->bindParam(':id', $id, \\PDO::PARAM_STR);\n    $select->execute();\n    $row = $select->fetch();\n    $stream4 = stream_get_contents($row['DATA']);\n    $start4  = mb_substr($stream4, 0, 10);\n    $ending4 = mb_substr($stream4, -10);\n    echo 'size of string4 is ', strlen($string4), ' bytes, ', mb_strlen($string4), ' chars.', PHP_EOL;\n    echo 'size of stream4 is ', strlen($stream4), ' bytes, ', mb_strlen($stream4), ' chars.', PHP_EOL;\n    echo 'beg  of stream4 is ', $start4, PHP_EOL;\n    echo 'end  of stream4 is ', $ending4, PHP_EOL;\n    if ($string4 != $stream4 || $stream4 != stream_get_contents($row['DATA2'])) {\n        echo 'Expected nclob value to match clob value for stream4', PHP_EOL;\n    }\n    $dbh->exec('DROP TABLE pdo_oci_bug60994');\n});\n\n?>\n--EXPECT--\nTest 1:  j\nsize of string1 is 8193 bytes, 8193 chars.\nsize of stream1 is 8193 bytes, 8193 chars.\nbeg  of stream1 is abcjjjjjjj\nend  of stream1 is jjjjjjjxyz\n\nTest 2:  £\nsize of string2 is 16380 bytes, 8193 chars.\nsize of stream2 is 16380 bytes, 8193 chars.\nbeg  of stream2 is abc£££££££\nend  of stream2 is £££££££xyz\n\nTest 3:  Җ\nsize of string3 is 16380 bytes, 8193 chars.\nsize of stream3 is 16380 bytes, 8193 chars.\nbeg  of stream3 is abcҖҖҖҖҖҖҖ\nend  of stream3 is ҖҖҖҖҖҖҖxyz\n\nTest 4:  の\nsize of string4 is 24567 bytes, 8193 chars.\nsize of stream4 is 24567 bytes, 8193 chars.\nbeg  of stream4 is abcののののののの\nend  of stream4 is のののののののxyz\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/bug_33707.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: PDO OCI Bug #33707 (Errors in select statements not reported)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $db = PdoOracleTest::create();\n    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);\n    $rs = $db->query('select blah from a_table_that_does_not_exist');\n    var_dump($rs);\n    var_dump($db->errorInfo());\n});\n?>\n--EXPECTF--\nbool(false)\narray(3) {\n  [0]=>\n  string(5) \"HY000\"\n  [1]=>\n  int(942)\n  [2]=>\n  string(%d) \"OCIStmtExecute: ORA-00942: table or view %Sdoes not exist\n (%s:%d)\"\n}\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/checkliveness.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: PDO OCI checkliveness (code coverage)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    try {\n        $db = new PDO(ORACLE_TNS, ORACLE_USER, ORACLE_PASSWORD, array(PDO::ATTR_PERSISTENT => true));\n    }\n    catch (PDOException $e) {\n        echo 'Connection failed: ' . $e->getMessage();\n        exit;\n    }\n\n    // This triggers the call to check liveness\n    try {\n        $db = new PDO(ORACLE_TNS, ORACLE_USER, ORACLE_PASSWORD, array(PDO::ATTR_PERSISTENT => true));\n    }\n    catch (PDOException $e) {\n        echo 'Connection failed: ' . $e->getMessage();\n        exit;\n    }\n\n    $db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);\n\n    try {\n        $stmt = $db->prepare('SELECT * FROM dual');\n        $stmt->execute();\n        $row = $stmt->fetch();\n        var_dump($row);\n    } catch (PDOException $e) {\n        print $e->getMessage();\n    }\n\n    $db = null;\n});\n?>\n--EXPECT--\narray(2) {\n  [\"DUMMY\"]=>\n  string(1) \"X\"\n  [0]=>\n  string(1) \"X\"\n}\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/coroutint.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: PDO OCI coroutine\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_ALL]);\nrun(function() {\n    $db = PdoOracleTest::create();\n    $db->exec(\"create table test (id int)\");\n    for($i = 0; $i < 10; $i++) {\n        go(function () use($db, $i){\n            $stmt = $db->prepare(\"insert into test values (?)\");\n            $stmt->execute([$i]);\n            $stmt = $db->prepare(\"select id from test where id = ?\");\n            $stmt->execute([$i]);\n            var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == $i);\n        });\n    }\n    sleep(1);\n    $db->exec(\"drop table test\");\n});\n\n?>\n--EXPECT--\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/oci_success_with_info.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: Handling OCI_SUCCESS_WITH_INFO\n--SKIPIF--\n<?php\nif (PHP_VERSION < 80100) {\n\trequire __DIR__ . '/../include/skipif.inc';\n\tskip('php version 8.1 or higher');\n}\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nfunction connectAsAdmin(): PDO {\n    return PdoOracleTest::create();\n}\n\nfunction connectAsUser(string $username, string $password): PDO {\n    return new PDO(ORACLE_TNS, $username, $password);\n}\n\nfunction dropProfile(PDO $conn): void {\n    $conn->exec(<<<'SQL'\nBEGIN\n    EXECUTE IMMEDIATE 'DROP PROFILE BUG77120_PROFILE CASCADE';\nEXCEPTION\n    WHEN OTHERS THEN\n        IF SQLCODE != -2380 THEN\n            RAISE;\n        END IF;\nEND;\nSQL\n    );\n}\n\nfunction dropUser(PDO $conn): void {\n    $conn->exec(<<<'SQL'\nBEGIN\n    EXECUTE IMMEDIATE 'DROP USER BUG77120_USER CASCADE';\nEXCEPTION\n    WHEN OTHERS THEN\n        IF SQLCODE != -1918 THEN\n            RAISE;\n        END IF;\nEND;\nSQL\n    );\n}\n\nfunction triggerCompilationError(PDO $conn): void {\n    $conn->exec(<<<'SQL'\nCREATE OR REPLACE FUNCTION BUG77120(INT A) RETURN INT\nAS\nBEGIN\n    RETURN 0;\nEND;\nSQL\n    );\n}\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $conn = connectAsAdmin();\n\n    dropUser($conn);\n    dropProfile($conn);\n\n    $password = bin2hex(random_bytes(8));\n\n    $conn->exec('CREATE PROFILE BUG77120_PROFILE LIMIT PASSWORD_LIFE_TIME 1/86400 PASSWORD_GRACE_TIME 1');\n    $conn->exec('CREATE USER BUG77120_USER IDENTIFIED BY \"' . $password . '\" PROFILE BUG77120_PROFILE');\n    $conn->exec('GRANT CREATE SESSION TO BUG77120_USER');\n\n    // let the password expire\n    sleep(3); // 2 seconds is causing random test failures\n\n    $conn = connectAsUser('BUG77120_USER', $password);\n    var_dump($conn->errorInfo());\n\n    $conn = connectAsAdmin();\n    dropUser($conn);\n    dropProfile($conn);\n\n    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);\n    triggerCompilationError($conn);\n    var_dump($conn->errorInfo());\n\n    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n    triggerCompilationError($conn);\n    var_dump($conn->errorInfo());\n});\n\n?>\n--EXPECTF--\narray(3) {\n  [0]=>\n  string(5) \"HY000\"\n  [1]=>\n  int(28002)\n  [2]=>\n  string(%d) \"OCISessionBegin: OCI_SUCCESS_WITH_INFO: ORA-28002: %s\n (%s:%d)\"\n}\narray(3) {\n  [0]=>\n  string(5) \"HY000\"\n  [1]=>\n  int(24344)\n  [2]=>\n  string(%d) \"OCIStmtExecute: OCI_SUCCESS_WITH_INFO: ORA-24344: %s\n (%s:%d)\"\n}\narray(3) {\n  [0]=>\n  string(5) \"HY000\"\n  [1]=>\n  int(24344)\n  [2]=>\n  string(%d) \"OCIStmtExecute: OCI_SUCCESS_WITH_INFO: ORA-24344: %s\n (%s:%d)\"\n}\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_attr_action.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: Setting session action\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $dbh = PdoOracleTest::create();\n    $query = 'select action from v$session where sid = sys_context(\\'USERENV\\', \\'SID\\')';\n    $stmt = $dbh->query($query);\n    $row = $stmt->fetch();\n    echo 'ACTION NOT SET: ';\n    var_dump($row['action']);\n\n    var_dump($dbh->setAttribute(PDO::OCI_ATTR_ACTION, \"some action\"));\n\n    $stmt = $dbh->query($query);\n    $row = $stmt->fetch();\n    echo 'ACTION SET: ';\n    var_dump($row['action']);\n\n    var_dump($dbh->setAttribute(PDO::OCI_ATTR_ACTION, \"something else!\"));\n\n    $stmt = $dbh->query($query);\n    $row = $stmt->fetch();\n    echo 'ACTION RESET: ';\n    var_dump($row['action']);\n\n    var_dump($dbh->setAttribute(PDO::OCI_ATTR_ACTION, null));\n\n    $stmt = $dbh->query($query);\n    $row = $stmt->fetch();\n    echo 'ACTION NULLED: ';\n    var_dump($row['action']);\n\n    echo \"Done\\n\";\n});\n?>\n--EXPECT--\nACTION NOT SET: NULL\nbool(true)\nACTION SET: string(11) \"some action\"\nbool(true)\nACTION RESET: string(15) \"something else!\"\nbool(true)\nACTION NULLED: NULL\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_1.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: Basic autocommit functionality\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $dbh = PdoOracleTest::create();\n    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);\n    $dbh->exec(\"drop table pdo_ac_tab\");\n    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n\n    print \"PDO::ATTR_AUTOCOMMIT: Default: \";\n    var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT));\n\n    echo \"Change setting to false - \";\n\n    $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, false);\n\n    print \"PDO::ATTR_AUTOCOMMIT: \";\n    var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT));\n\n    echo \"Change setting back to true - \";\n\n    $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, true);\n\n    print \"PDO::ATTR_AUTOCOMMIT: \";\n    var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT));\n\n    // Use 2nd connection to check that autocommit does commit\n\n    echo \"Insert data\\n\";\n    $dbh->exec(\"create table pdo_ac_tab (col1 varchar2(20))\");\n    $dbh->exec(\"insert into pdo_ac_tab (col1) values ('some data')\");\n\n    $dbh2 = PdoOracleTest::create();\n\n    echo \"Second connection should be able to see committed data\\n\";\n    $s = $dbh2->prepare(\"select col1 from pdo_ac_tab\");\n    $s->execute();\n    while ($r = $s->fetch()) {\n        echo \"Data is: \" . $r[0] . \"\\n\";\n    }\n\n    $dbh->exec(\"drop table pdo_ac_tab\");\n\n    echo \"Done\\n\";\n});\n?>\n--EXPECT--\nPDO::ATTR_AUTOCOMMIT: Default: bool(true)\nChange setting to false - PDO::ATTR_AUTOCOMMIT: bool(false)\nChange setting back to true - PDO::ATTR_AUTOCOMMIT: bool(true)\nInsert data\nSecond connection should be able to see committed data\nData is: some data\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_2.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: beginTransaction and native transactions\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $dbh = PdoOracleTest::create();\n    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);\n    $dbh->exec(\"drop table pdo_ac_tab\");\n    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n\n    $dbh->exec(\"create table pdo_ac_tab (col1 varchar2(25))\");\n\n    echo \"Test 1 Check beginTransaction insertion\\n\";\n\n    $dbh->beginTransaction();\n    try {\n        $dbh->exec(\"insert into pdo_ac_tab (col1) values ('data 1')\");\n        $dbh->exec(\"insert into pdo_ac_tab (col1) values ('data 2')\");\n        $dbh->commit();\n    }\n    catch (PDOException $e) {\n        echo \"Caught unexpected exception at line \" . __LINE__ . \"\\n\";\n        echo $e->getMessage() . \"\\n\";\n        $dbh->rollback();\n    }\n\n    echo \"Test 2 Cause an exception and test beginTransaction rollback\\n\";\n\n    $dbh->beginTransaction();\n    try {\n        $dbh->exec(\"insert into pdo_ac_tab (col1) values ('not committed #1')\");\n        $dbh->exec(\"insert into pdo_ac_tab (col1) values ('data that is too long to fit and will barf')\");\n        $dbh->commit();\n    }\n    catch (PDOException $e) {\n        echo \"Caught expected exception at line \" . __LINE__ . \"\\n\";\n        echo $e->getMessage() . \"\\n\";\n        $dbh->rollback();\n    }\n\n    echo \"Test 3 Setting ATTR_AUTOCOMMIT to true will commit and end the transaction\\n\";\n\n    $dbh->exec(\"insert into pdo_ac_tab (col1) values ('data 3')\");\n    $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, true);\n    print \"PDO::ATTR_AUTOCOMMIT: \";\n    var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT));\n    try {\n        $dbh->rollback();\n    }\n    catch (PDOException $e) {\n        echo \"Caught expected exception at line \" . __LINE__ . \"\\n\";\n        echo $e->getMessage() . \"\\n\";\n    }\n\n    echo \"Test 4 Setting ATTR_AUTOCOMMIT to false will commit and end the transaction\\n\";\n\n    $dbh->beginTransaction();\n    $dbh->exec(\"insert into pdo_ac_tab (col1) values ('data 4')\");\n    $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, false);\n    print \"PDO::ATTR_AUTOCOMMIT: \";\n    var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT));\n    try {\n        $dbh->rollback();\n    }\n    catch (PDOException $e) {\n        echo \"Caught expected exception at line \" . __LINE__ . \"\\n\";\n        echo $e->getMessage() . \"\\n\";\n    }\n\n    echo \"Test 5 Handle transactions ourselves\\n\";\n\n    print \"PDO::ATTR_AUTOCOMMIT: \";\n    var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT));\n\n    $dbh->exec(\"insert into pdo_ac_tab (col1) values ('not committed #2')\");\n    $dbh->exec(\"rollback\");\n    $dbh->exec(\"insert into pdo_ac_tab (col1) values ('data 5')\");\n    $dbh->exec(\"insert into pdo_ac_tab (col1) values ('data 6')\");\n\n    $dbh->exec(\"commit\");\n\n    // Open new connection to really verify what was inserted\n\n    $dbh2 = PdoOracleTest::create();\n\n    echo \"Query Results are:\\n\";\n    $s = $dbh2->prepare(\"select col1 from pdo_ac_tab\");\n    $s->execute();\n    while ($r = $s->fetch()) {\n        echo $r[0] . \"\\n\";\n    }\n\n    echo \"Done\\n\";\n});\n?>\n--EXPECTF--\nTest 1 Check beginTransaction insertion\nTest 2 Cause an exception and test beginTransaction rollback\nCaught expected exception at line %d\nSQLSTATE[HY000]: General error: 12899 OCIStmtExecute: ORA-12899: %s\n%s\nTest 3 Setting ATTR_AUTOCOMMIT to true will commit and end the transaction\nPDO::ATTR_AUTOCOMMIT: bool(true)\nCaught expected exception at line %d\nThere is no active transaction\nTest 4 Setting ATTR_AUTOCOMMIT to false will commit and end the transaction\nPDO::ATTR_AUTOCOMMIT: bool(false)\nCaught expected exception at line %d\nThere is no active transaction\nTest 5 Handle transactions ourselves\nPDO::ATTR_AUTOCOMMIT: bool(false)\nQuery Results are:\ndata 1\ndata 2\ndata 3\ndata 4\ndata 5\ndata 6\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_attr_autocommit_3.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: closing a connection in non-autocommit mode commits data\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $dbh = PdoOracleTest::create();\n\n    // Check connection can be created with AUTOCOMMIT off\n    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);\n    $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, false);\n    $dbh->exec(\"drop table pdo_ac_tab\");\n\n    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n\n    print \"PDO::ATTR_AUTOCOMMIT: \";\n    var_dump($dbh->getAttribute(PDO::ATTR_AUTOCOMMIT));\n\n    echo \"Insert data\\n\";\n\n    $dbh->exec(\"create table pdo_ac_tab (col1 varchar2(20))\");\n\n    $dbh->exec(\"insert into pdo_ac_tab (col1) values ('some data')\");\n\n    $dbh = null; // close first connection\n\n    echo \"Second connection should be able to see committed data\\n\";\n    $dbh2 = PdoOracleTest::create();\n    $s = $dbh2->prepare(\"select col1 from pdo_ac_tab\");\n    $s->execute();\n    while ($r = $s->fetch()) {\n        echo \"Data is: \" . $r[0] . \"\\n\";\n    }\n\n    $dbh2->exec(\"drop table pdo_ac_tab\");\n\n    echo \"Done\\n\";\n});\n?>\n--EXPECT--\nPDO::ATTR_AUTOCOMMIT: bool(false)\nInsert data\nSecond connection should be able to see committed data\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_attr_call_timeout.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: Setting and using call timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nif (getenv('SKIP_SLOW_TESTS')) die('skip slow tests excluded by request');\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\nif (strcasecmp(getenv('PDOTEST_USER'), \"system\") && strcasecmp(getenv('PDOTEST_USER'), \"sys\")) {\n    die(\"skip needs to be run as a user with access to DBMS_LOCK\");\n}\n\n$dbh = PdoOracleTest::create();\npreg_match('/^[[:digit:]]+/', $dbh->getAttribute(PDO::ATTR_CLIENT_VERSION), $matches);\nif (!(isset($matches[0]) && $matches[0] >= 18)) {\n    die(\"skip works only with Oracle 18c or greater version of Oracle client libraries\");\n}\n\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nfunction mysleep($dbh, $t)\n{\n    $stmt = $dbh->prepare(\"begin dbms_lock.sleep(:t); end;\");\n\n    if (!$stmt) {\n        $error = $dbh->errorInfo();\n        echo \"Prepare error was \", $error[2], \"\\n\";\n        return;\n    }\n    $stmt->bindParam(\":t\", $t, PDO::PARAM_INT);\n\n    $r = $stmt->execute();\n    if ($r) {\n        echo \"Execute succeeded\\n\";\n    } else {\n        $error = $dbh->errorInfo();\n        echo \"Execute error was \", $error[2], \"\\n\";\n    }\n}\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $dbh = PdoOracleTest::create();\n    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);\n\n    echo \"Test 1\\n\";\n\n    $dbh->setAttribute(PDO::OCI_ATTR_CALL_TIMEOUT, 4000); // milliseconds\n\n    echo \"call timeout:\\n\";\n    var_dump($dbh->getAttribute(PDO::OCI_ATTR_CALL_TIMEOUT));\n\n    $r = mysleep($dbh, 8); // seconds\n});\n?>\n===DONE===\n<?php exit(0); ?>\n--EXPECTF--\nTest 1\ncall timeout:\nint(4000)\nExecute error was OCIStmtExecute: ORA-%r(03136|03156)%r: %s\n (%s:%d)\n===DONE===\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_attr_case.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: Column Case\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nfunction do_query1($dbh)\n{\n    var_dump($dbh->getAttribute(PDO::ATTR_CASE));\n    $s = $dbh->prepare(\"select dummy from dual\");\n    $s->execute();\n    while ($r = $s->fetch(PDO::FETCH_ASSOC)) {\n        var_dump($r);\n    }\n}\n\nfunction do_query2($dbh, $mode)\n{\n    echo \"Mode desired is $mode\\n\";\n    $s = $dbh->prepare(\"select dummy from dual\", array(PDO::ATTR_CASE, $mode));\n    $s->execute();\n    while ($r = $s->fetch(PDO::FETCH_ASSOC)) {\n        var_dump($r);\n    }\n}\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $dbh = PdoOracleTest::create();\n    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n\n    echo \"Test 1 - Force column names to lower case\\n\";\n    $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);\n    do_query1($dbh);\n\n    echo \"Test 2 - Leave column names as returned by the database driver\\n\";\n    $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL);\n    do_query1($dbh);\n\n    echo \"Test 3 - Force column names to upper case\\n\";\n    $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER);\n    do_query1($dbh);\n\n    echo \"Test 4 - Setting on statement has no effect.  Attempt lower case but get upper\\n\";\n    $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); // reset\n    do_query2($dbh, PDO::CASE_LOWER);\n\n    echo \"Done\\n\";\n});\n?>\n--EXPECT--\nTest 1 - Force column names to lower case\nint(2)\narray(1) {\n  [\"dummy\"]=>\n  string(1) \"X\"\n}\nTest 2 - Leave column names as returned by the database driver\nint(0)\narray(1) {\n  [\"DUMMY\"]=>\n  string(1) \"X\"\n}\nTest 3 - Force column names to upper case\nint(1)\narray(1) {\n  [\"DUMMY\"]=>\n  string(1) \"X\"\n}\nTest 4 - Setting on statement has no effect.  Attempt lower case but get upper\nMode desired is 2\narray(1) {\n  [\"DUMMY\"]=>\n  string(1) \"X\"\n}\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_attr_client.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: Client version\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $dbh = PdoOracleTest::create();\n    echo \"ATTR_CLIENT_VERSION: \";\n    $cv = $dbh->getAttribute(PDO::ATTR_CLIENT_VERSION);\n    var_dump($cv);\n\n    $s = explode(\".\", $cv);\n    if (count($s) > 1 && (($s[0] == 10 && $s[1] >= 2) || $s[0] >= 11)) {\n        if (count($s) != 5) {\n            echo \"Wrong number of values in array\\nVersion was: \";\n            var_dump($cv);\n        } else {\n            echo \"Version OK, so far as can be portably checked\\n\";\n        }\n    } else {\n        if (count($s) != 2) {\n            echo \"Wrong number of values in array\\nVersion was: \";\n            var_dump($cv);\n        } else {\n            echo \"Version OK, so far as can be portably checked\\n\";\n        }\n    }\n\n    echo \"Done\\n\";\n});\n?>\n--EXPECTF--\nATTR_CLIENT_VERSION: string(%d) \"%d.%s\"\nVersion OK, so far as can be portably checked\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_attr_client_identifier.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: Setting session client identifier\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $query = 'select client_identifier from v$session where sid = sys_context(\\'USERENV\\', \\'SID\\')';\n\n    $dbh = PdoOracleTest::create();\n\n    $stmt = $dbh->query($query);\n    $row = $stmt->fetch();\n    echo 'CLIENT_IDENTIFIER NOT SET: ';\n    var_dump($row['client_identifier']);\n\n    var_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_IDENTIFIER, \"some client identifier\"));\n\n    $stmt = $dbh->query($query);\n    $row = $stmt->fetch();\n    echo 'CLIENT_IDENTIFIER SET: ';\n    var_dump($row['client_identifier']);\n\n    var_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_IDENTIFIER, \"something else!\"));\n\n    $stmt = $dbh->query($query);\n    $row = $stmt->fetch();\n    echo 'CLIENT_IDENTIFIER RESET: ';\n    var_dump($row['client_identifier']);\n\n    var_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_IDENTIFIER, null));\n\n    $stmt = $dbh->query($query);\n    $row = $stmt->fetch();\n    echo 'CLIENT_IDENTIFIER NULLED: ';\n    var_dump($row['client_identifier']);\n\n    echo \"Done\\n\";\n});\n?>\n--EXPECT--\nCLIENT_IDENTIFIER NOT SET: NULL\nbool(true)\nCLIENT_IDENTIFIER SET: string(22) \"some client identifier\"\nbool(true)\nCLIENT_IDENTIFIER RESET: string(15) \"something else!\"\nbool(true)\nCLIENT_IDENTIFIER NULLED: NULL\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_attr_client_info.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: Setting session client info\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n\t$query = 'select client_info from v$session where sid = sys_context(\\'USERENV\\', \\'SID\\')';\n    $dbh = PdoOracleTest::create();\n\t$stmt = $dbh->query($query);\n\t$row = $stmt->fetch();\n\techo 'CLIENT_INFO NOT SET: ';\n\tvar_dump($row['client_info']);\n\n\tvar_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_INFO, \"some client info\"));\n\n\t$stmt = $dbh->query($query);\n\t$row = $stmt->fetch();\n\techo 'CLIENT_INFO SET: ';\n\tvar_dump($row['client_info']);\n\n\tvar_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_INFO, \"something else!\"));\n\n\t$stmt = $dbh->query($query);\n\t$row = $stmt->fetch();\n\techo 'CLIENT_INFO RESET: ';\n\tvar_dump($row['client_info']);\n\n\tvar_dump($dbh->setAttribute(PDO::OCI_ATTR_CLIENT_INFO, null));\n\n\t$stmt = $dbh->query($query);\n\t$row = $stmt->fetch();\n\techo 'CLIENT_INFO NULLED: ';\n\tvar_dump($row['client_info']);\n\n\techo \"Done\\n\";\n});\n?>\n--EXPECT--\nCLIENT_INFO NOT SET: NULL\nbool(true)\nCLIENT_INFO SET: string(16) \"some client info\"\nbool(true)\nCLIENT_INFO RESET: string(15) \"something else!\"\nbool(true)\nCLIENT_INFO NULLED: NULL\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_attr_drivername.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: verify driver name\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $dbh = PdoOracleTest::create();\n    var_dump($dbh->getAttribute(PDO::ATTR_DRIVER_NAME));\n    echo \"Done\\n\";\n});\n?>\n--EXPECT--\nstring(3) \"oci\"\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_attr_module.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: Setting session module\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\n\nrun(function() {\n    $query = 'select module from v$session where sid = sys_context(\\'USERENV\\', \\'SID\\')';\n\n    $dbh = PdoOracleTest::create();\n\n    var_dump($dbh->setAttribute(PDO::OCI_ATTR_MODULE, \"some module\"));\n\n    $stmt = $dbh->query($query);\n    $row = $stmt->fetch();\n    echo 'MODULE SET: ';\n    var_dump($row['module']);\n\n    var_dump($dbh->setAttribute(PDO::OCI_ATTR_MODULE, \"something else!\"));\n\n    $stmt = $dbh->query($query);\n    $row = $stmt->fetch();\n    echo 'MODULE RESET: ';\n    var_dump($row['module']);\n\n    var_dump($dbh->setAttribute(PDO::OCI_ATTR_MODULE, null));\n\n    $stmt = $dbh->query($query);\n    $row = $stmt->fetch();\n    echo 'MODULE NULLED: ';\n    var_dump($row['module']);\n\n    echo \"Done\\n\";\n});\n?>\n--EXPECT--\nbool(true)\nMODULE SET: string(11) \"some module\"\nbool(true)\nMODULE RESET: string(15) \"something else!\"\nbool(true)\nMODULE NULLED: NULL\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_attr_nulls_1.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: Oracle Nulls\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nfunction do_query($dbh)\n{\n    var_dump($dbh->getAttribute(PDO::ATTR_ORACLE_NULLS));\n    $s = $dbh->prepare(\"select '' as myempty, null as mynull from dual\");\n    $s->execute();\n    while ($r = $s->fetch()) {\n        var_dump($r[0]);\n        var_dump($r[1]);\n    }\n}\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $dbh = PdoOracleTest::create();\n    print \"PDO::ATTR_ORACLE_NULLS: Default: \";\n    do_query($dbh);\n\n    print \"PDO::ATTR_ORACLE_NULLS: PDO::NULL_NATURAL: \";\n    $dbh->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_NATURAL); // No conversion.\n\n    do_query($dbh);\n\n    print \"PDO::ATTR_ORACLE_NULLS: PDO::NULL_EMPTY_STRING: \";\n    $dbh->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_EMPTY_STRING); // Empty string is converted to NULL.\n\n    do_query($dbh);\n\n    print \"PDO::ATTR_ORACLE_NULLS: PDO::NULL_TO_STRING: \";\n    $dbh->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_TO_STRING); // NULL is converted to an empty string.\n\n    do_query($dbh);\n\n    echo \"Done\\n\";\n});\n?>\n--EXPECT--\nPDO::ATTR_ORACLE_NULLS: Default: int(0)\nNULL\nNULL\nPDO::ATTR_ORACLE_NULLS: PDO::NULL_NATURAL: int(0)\nNULL\nNULL\nPDO::ATTR_ORACLE_NULLS: PDO::NULL_EMPTY_STRING: int(1)\nNULL\nNULL\nPDO::ATTR_ORACLE_NULLS: PDO::NULL_TO_STRING: int(2)\nstring(0) \"\"\nstring(0) \"\"\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_1.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: Set prefetch on connection\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $dbh = PdoOracleTest::create();\n    echo \"Test connect\\n\";\n    $dbh->setAttribute(PDO::ATTR_PREFETCH, 101);\n\n    echo $dbh->getAttribute(PDO::ATTR_PREFETCH), \"\\n\";\n\n    // Verify can fetch\n    $s = $dbh->prepare(\"select dummy from dual\" );\n    $s->execute();\n    while ($r = $s->fetch()) {\n        echo $r[0] . \"\\n\";\n    }\n\n    echo \"Test set 102\\n\";\n    $dbh->setAttribute(PDO::ATTR_PREFETCH, 102);\n    echo $dbh->getAttribute(PDO::ATTR_PREFETCH), \"\\n\";\n\n    // Verify can fetch\n    $s = $dbh->prepare(\"select dummy from dual\" );\n    $s->execute();\n    while ($r = $s->fetch()) {\n        echo $r[0] . \"\\n\";\n    }\n\n    echo \"Test set -1: (Uses 0)\\n\";\n    $dbh->setAttribute(PDO::ATTR_PREFETCH, -1);\n    echo $dbh->getAttribute(PDO::ATTR_PREFETCH), \"\\n\";\n\n    // Verify can fetch\n    $s = $dbh->prepare(\"select dummy from dual\" );\n    $s->execute();\n    while ($r = $s->fetch()) {\n        echo $r[0] . \"\\n\";\n    }\n\n    echo \"Test set PHP_INT_MAX: (Uses default)\\n\";\n    $dbh->setAttribute(PDO::ATTR_PREFETCH, PHP_INT_MAX);\n    echo $dbh->getAttribute(PDO::ATTR_PREFETCH), \"\\n\";\n\n    // Verify can fetch\n    $s = $dbh->prepare(\"select dummy from dual\" );\n    $s->execute();\n    while ($r = $s->fetch()) {\n        echo $r[0] . \"\\n\";\n    }\n\n    echo \"Done\\n\";\n});\n?>\n--EXPECT--\nTest connect\n101\nX\nTest set 102\n102\nX\nTest set -1: (Uses 0)\n0\nX\nTest set PHP_INT_MAX: (Uses default)\n100\nX\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_attr_prefetch_2.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: prefetch on statements\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $dbh = PdoOracleTest::create();\n    $s = $dbh->prepare(\"select '' as myempty, null as mynull from dual\", array(PDO::ATTR_PREFETCH => 101));\n\n    echo \"Test 1: Can't set prefetch after prepare\\n\";\n    var_dump($s->setAttribute(PDO::ATTR_PREFETCH, 102));\n\n    // Verify can fetch\n    $s = $dbh->prepare(\"select dummy from dual\" );\n    $s->execute();\n    while ($r = $s->fetch()) {\n        echo $r[0] . \"\\n\";\n    }\n\n    echo \"Test 2: Turn off prefetching\\n\";\n    $s = $dbh->prepare(\"select '' as myempty, null as mynull from dual\", array(PDO::ATTR_PREFETCH => 0));\n    $s = $dbh->prepare(\"select dummy from dual\" );\n    $s->execute();\n    while ($r = $s->fetch()) {\n        echo $r[0] . \"\\n\";\n    }\n\n    echo \"Done\\n\";\n});\n?>\n--EXPECTF--\nTest 1: Can't set prefetch after prepare\n\nFatal error: Uncaught PDOException: SQLSTATE[IM001]: Driver does not support this function: This driver doesn't support setting attributes in %s:%d\nStack trace:\n#0 %s(%d): PDOStatement->setAttribute(1, 102)\n%A\n  thrown in %s on line %d\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_attr_server.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: Server version and info\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $dbh = PdoOracleTest::create();\n    echo \"Test 1\\n\";\n    echo \"ATTR_SERVER_VERSION: \";\n    var_dump($dbh->getAttribute(PDO::ATTR_SERVER_VERSION));\n\n    echo \"Test 2\\n\";\n    echo \"ATTR_SERVER_INFO\\n\";\n    $si = $dbh->getAttribute(PDO::ATTR_SERVER_INFO);\n    $pos = strpos($si, \"Oracle\");\n    if ($pos === 0) {\n        echo \"Found 'Oracle' at position $pos as expected\\n\";\n    } else {\n        echo \"Unexpected result.  Server info was:\\n\";\n        var_dump($si);\n    }\n\n    echo \"Done\\n\";\n});\n?>\n--EXPECTF--\nTest 1\nATTR_SERVER_VERSION: string(%d) \"%d.%d.%d.%d.%d\"\nTest 2\nATTR_SERVER_INFO\nFound 'Oracle' at position 0 as expected\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_class_constants.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: PDO OCI specific class constants\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\n\n$expected = [\n    'OCI_ATTR_CLIENT_INFO'        => true,\n    'OCI_ATTR_ACTION'             => true,\n    'OCI_ATTR_CLIENT_IDENTIFIER'  => true,\n    'OCI_ATTR_MODULE'             => true,\n    'OCI_ATTR_CALL_TIMEOUT'       => true,\n];\n\n$ref = new ReflectionClass('PDO');\n$constants = $ref->getConstants();\n$values = [];\n\nforeach ($constants as $name => $value) {\n    if (substr($name, 0, 8) == 'OCI_ATTR') {\n        if (!isset($values[$value])) {\n            $values[$value] = [$name];\n        } else {\n            $values[$value][] = $name;\n        }\n\n        if (isset($expected[$name])) {\n            unset($expected[$name]);\n            unset($constants[$name]);\n        }\n\n        } else {\n            unset($constants[$name]);\n        }\n}\n\nif (!empty($constants)) {\n    printf(\"[001] Dumping list of unexpected constants\\n\");\n    var_dump($constants);\n}\n\nif (!empty($expected)) {\n    printf(\"[002] Dumping list of missing constants\\n\");\n    var_dump($expected);\n}\n\nif (!empty($values)) {\n    foreach ($values as $value => $constants) {\n        if (count($constants) > 1) {\n            printf(\"[003] Several constants share the same value '%s'\\n\", $value);\n            var_dump($constants);\n        }\n    }\n}\n\nprint \"done!\";\n?>\n--EXPECT--\ndone!\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_debugdumpparams.phpt",
    "content": "--TEST--\nswoole_pdo_oracle:debugDumpParams() truncates query)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $db = PdoOracleTest::create();\n    $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);\n$stmt = $db->query(\"\nSELECT '\n    Dumps the information contained by a prepared statement directly on the output. It will provide the SQL query in use, the number of parameters used (Params), the list of parameters, with their name, type (paramtype) as an integer, their key name or position, and the position in the query (if this is supported by the PDO driver, otherwise, it will be -1).\n    This is a debug function, which dump directly the data on the normal output.\n    Tip:\n    As with anything that outputs its result directly to the browser, the output-control functions can be used to capture the output of this function, and save it in a string (for example).\n    This will only dumps the parameters in the statement at the moment of the dump. Extra parameters are not stored in the statement, and not displayed.\n' FROM DUAL\n\");\nvar_dump($stmt->debugDumpParams());\n});\n?>\n--EXPECTF--\nSQL: [%d] \nSELECT '\n    Dumps the information contained by a prepared statement directly on the output. It will provide the SQL query in use, the number of parameters used (Params), the list of parameters, with their name, type (paramtype) as an integer, their key name or position, and the position in the query (if this is supported by the PDO driver, otherwise, it will be -1).\n    This is a debug function, which dump directly the data on the normal output.\n    Tip:\n    As with anything that outputs its result directly to the browser, the output-control functions can be used to capture the output of this function, and save it in a string (for example).\n    This will only dumps the parameters in the statement at the moment of the dump. Extra parameters are not stored in the statement, and not displayed.\n' FROM DUAL\n\nParams:  0\nNULL\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_fread_1.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: check fread() EOF\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $dbh = PdoOracleTest::create();\n    $dbh->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);\n\n    // Initialization\n    $stmtarray = array(\n        \"begin execute immediate 'drop table pdo_oci_fread_tab'; exception when others then null; end;\",\n        \"create table pdo_oci_fread_tab (id number, data clob)\",\n        \"declare\n        lob1 clob := 'abc' || lpad('j',4020,'j') || 'xyz';\n       begin\n        insert into pdo_oci_fread_tab (id,data) values (1, lob1);\n      end;\"\n    );\n\n    foreach ($stmtarray as $stmt) {\n        $dbh->exec($stmt);\n    }\n\n    echo \"Test 1\\n\";\n\n    $s = $dbh->query(\"select data from pdo_oci_fread_tab where id = 1\");\n    $r = $s->fetch();\n    $sh = $r['data'];\n\n    while (1) {\n        $buffer = fread($sh,1024);\n        if (!$buffer) {\n            break;\n        }\n        echo '*'.$buffer.'*';\n    }\n    echo \"\\n\";\n    fclose($sh);\n\n    // Clean up\n\n    $stmtarray = array(\n        \"drop table pdo_oci_fread_tab\"\n    );\n\n    foreach ($stmtarray as $stmt) {\n        $dbh->exec($stmt);\n    }\n});\n?>\n--EXPECT--\nTest 1\n*abcjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj**jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj**jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj**jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjxyz*\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_phpinfo.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: phpinfo() output\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $db = PdoOracleTest::create();\n    ob_start();\n    phpinfo();\n    $tmp = ob_get_contents();\n    ob_end_clean();\n\n    $reg = 'coroutine_oracle => enabled';\n    if (!preg_match(\"/$reg/\", $tmp)) {\n        printf(\"[001] Cannot find OCI PDO driver line in phpinfo() output\\n\");\n    }\n\n    print \"done!\";\n});\n?>\n--EXPECT--\ndone!\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_quote1.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: Test PDO->quote() for PDO_OCI\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $db = PdoOracleTest::create();\n    $db->query(\"create table poq_tab (t varchar2(100))\");\n    $stmt = $db->prepare('select * from poq_tab');\n\n    // The intent is that the fetched data be identical to the unquoted string.\n    // Remember!: use bind variables instead of PDO->quote()\n\n    $a = array(\"\", \"a\", \"ab\", \"abc\", \"ab'cd\", \"a\\b\\n\", \"'\", \"''\", \"a'\", \"'z\", \"a''b\", '\"');\n    foreach ($a as $u) {\n        $q = $db->quote($u);\n        echo \"Unquoted : \";\n        var_dump($u);\n        echo \"Quoted   : \";\n        var_dump($q);\n\n        $db->exec(\"delete from poq_tab\");\n\n        $db->query(\"insert into poq_tab (t) values($q)\");\n        $stmt->execute();\n        var_dump($stmt->fetchAll(PDO::FETCH_ASSOC));\n    }\n\n    echo \"Done\\n\";\n    $db->exec(\"drop table poq_tab\");\n});\n?>\n--EXPECT--\nUnquoted : string(0) \"\"\nQuoted   : string(2) \"''\"\narray(1) {\n  [0]=>\n  array(1) {\n    [\"t\"]=>\n    NULL\n  }\n}\nUnquoted : string(1) \"a\"\nQuoted   : string(3) \"'a'\"\narray(1) {\n  [0]=>\n  array(1) {\n    [\"t\"]=>\n    string(1) \"a\"\n  }\n}\nUnquoted : string(2) \"ab\"\nQuoted   : string(4) \"'ab'\"\narray(1) {\n  [0]=>\n  array(1) {\n    [\"t\"]=>\n    string(2) \"ab\"\n  }\n}\nUnquoted : string(3) \"abc\"\nQuoted   : string(5) \"'abc'\"\narray(1) {\n  [0]=>\n  array(1) {\n    [\"t\"]=>\n    string(3) \"abc\"\n  }\n}\nUnquoted : string(5) \"ab'cd\"\nQuoted   : string(8) \"'ab''cd'\"\narray(1) {\n  [0]=>\n  array(1) {\n    [\"t\"]=>\n    string(5) \"ab'cd\"\n  }\n}\nUnquoted : string(4) \"a\\b\n\"\nQuoted   : string(6) \"'a\\b\n'\"\narray(1) {\n  [0]=>\n  array(1) {\n    [\"t\"]=>\n    string(4) \"a\\b\n\"\n  }\n}\nUnquoted : string(1) \"'\"\nQuoted   : string(4) \"''''\"\narray(1) {\n  [0]=>\n  array(1) {\n    [\"t\"]=>\n    string(1) \"'\"\n  }\n}\nUnquoted : string(2) \"''\"\nQuoted   : string(6) \"''''''\"\narray(1) {\n  [0]=>\n  array(1) {\n    [\"t\"]=>\n    string(2) \"''\"\n  }\n}\nUnquoted : string(2) \"a'\"\nQuoted   : string(5) \"'a'''\"\narray(1) {\n  [0]=>\n  array(1) {\n    [\"t\"]=>\n    string(2) \"a'\"\n  }\n}\nUnquoted : string(2) \"'z\"\nQuoted   : string(5) \"'''z'\"\narray(1) {\n  [0]=>\n  array(1) {\n    [\"t\"]=>\n    string(2) \"'z\"\n  }\n}\nUnquoted : string(4) \"a''b\"\nQuoted   : string(8) \"'a''''b'\"\narray(1) {\n  [0]=>\n  array(1) {\n    [\"t\"]=>\n    string(4) \"a''b\"\n  }\n}\nUnquoted : string(1) \"\"\"\nQuoted   : string(3) \"'\"'\"\narray(1) {\n  [0]=>\n  array(1) {\n    [\"t\"]=>\n    string(1) \"\"\"\n  }\n}\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_stmt_getcolumnmeta.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: PDOStatement->getColumnMeta\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\necho \"Preparations before the test\\n\";\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    try {\n        $db = PdoOracleTest::create();\n        $db->exec(<<<SQL\n    BEGIN\n       EXECUTE IMMEDIATE 'DROP TABLE test';\n    EXCEPTION\n       WHEN OTHERS THEN\n          IF SQLCODE != -942 THEN\n         RAISE;\n          END IF;\n    END;\n    SQL\n    );\n        $db->exec(\"CREATE TABLE test(id INT)\");\n\n        $db->beginTransaction();\n\n        $stmt = $db->prepare('SELECT id FROM test ORDER BY id ASC');\n\n        echo \"Test 1. calling function with invalid parameters\\n\";\n\n        // execute() has not been called yet\n        // NOTE: no warning\n        $tmp = $stmt->getColumnMeta(0);\n        printf(\" 1.1 Expecting false got %s\\n\", var_export($tmp, true));\n\n        echo(\" 1.2 \");\n        $stmt->execute();\n        // PDOStatement::getColumnMeta() expects exactly 1 argument, 0 given in\n        try {\n            $tmp = $stmt->getColumnMeta();\n        } catch (ArgumentCountError $e) {\n            if (false !== $tmp) {\n                printf(\"[1.2] Expecting false got %s\\n\", var_export($tmp, true));\n            }\n            echo $e->getMessage(), \"\\n\";\n        }\n\n        // invalid offset\n        echo \" 1.3 \";\n        try {\n            $tmp = $stmt->getColumnMeta(-1);\n        } catch (ValueError $e) {\n            if (false !== $tmp) {\n                printf(\"[1.3] Expecting false got %s\\n\", var_export($tmp, true));\n            }\n            echo $e->getMessage(), \"\\n\";\n        }\n\n        // PDOStatement::getColumnMeta(): Argument #1 must be of type int, array given in\n        echo \" 1.4 \";\n        try {\n            $tmp = $stmt->getColumnMeta(array());\n        } catch (TypeError $e) {\n            if (false !== $tmp)\n                printf(\"[1.4] Expecting false got %s\\n\", var_export($tmp, true));\n            echo $e->getMessage(), \"\\n\";\n        }\n\n        // PDOStatement::getColumnMeta() expects exactly 1 argument, 2 given in\n        echo \" 1.5 \";\n        try {\n            $tmp = $stmt->getColumnMeta(1, 1);\n        } catch (ArgumentCountError $e) {\n            if (false !== $tmp)\n                printf(\"[1.5] Expecting false got %s\\n\", var_export($tmp, true));\n            echo $e->getMessage(), \"\\n\";\n        }\n\n        // invalid offset\n        $tmp = $stmt->getColumnMeta(1);\n        printf(\" 1.6 Expecting false because of invalid offset got %s\\n\", var_export($tmp, true));\n\n        echo \"Test 2. testing return values\\n\";\n        echo \"Test 2.1 testing array returned\\n\";\n\n        $stmt = $db->prepare('SELECT id FROM test ORDER BY id ASC');\n        $stmt->execute();\n        $native = $stmt->getColumnMeta(0);\n        if (count($native) == 0) {\n            printf(\"[008] Meta data seems wrong, %s / %s\\n\",\n                var_export($native, true), var_export($emulated, true));\n        }\n\n\n        function test_return($meta, $offset, $native_type, $pdo_type){\n            if (empty($meta)) {\n                printf(\"[%03d + 2] getColumnMeta() failed, %d - %s\\n\", $offset,\n                    $stmt->errorCode(), var_export($stmt->errorInfo(), true));\n                return false;\n            }\n            $elements = array('flags', 'scale', 'name', 'len', 'precision', 'pdo_type');\n            foreach ($elements as $k => $element)\n                if (!isset($meta[$element])) {\n                    printf(\"[%03d + 3] Element %s missing, %s\\n\", $offset,\n                        $element, var_export($meta, true));\n                    return false;\n                }\n\n            if (!is_null($native_type)) {\n                if (!isset($meta['native_type'])) {\n                    printf(\"[%03d + 4] Element native_type missing, %s\\n\", $offset,\n                        var_export($meta, true));\n                    return false;\n                }\n\n                if (!is_array($native_type))\n                    $native_type = array($native_type);\n\n                $found = false;\n                foreach ($native_type as $k => $type) {\n                    if ($meta['native_type'] == $type) {\n                        $found = true;\n                        break;\n                    }\n                }\n\n                if (!$found) {\n                    printf(\"[%03d + 5] Expecting native type %s, %s\\n\", $offset,\n                        var_export($native_type, true), var_export($meta, true));\n                    return false;\n                }\n            }\n\n            if (!is_null($pdo_type) && ($meta['pdo_type'] != $pdo_type)) {\n                printf(\"[%03d + 6] Expecting PDO type %s got %s (%s)\\n\", $offset,\n                    $pdo_type, var_export($meta, true), var_export($meta['native_type']));\n                return false;\n            }\n\n            return true;\n        }\n\n\n        function test_meta(&$db, $offset, $sql_type, $value, $native_type, $pdo_type) {\n\n            $db->exec(<<<SQL\n    BEGIN\n       EXECUTE IMMEDIATE 'DROP TABLE test';\n    EXCEPTION\n       WHEN OTHERS THEN\n          IF SQLCODE != -942 THEN\n             RAISE;\n          END IF;\n    END;\n    SQL\n    );\n\n            $sql = sprintf('CREATE TABLE test(id INT, label %s)', $sql_type);\n            $stmt = $db->prepare($sql);\n            $stmt->execute();\n\n            if (!$db->exec(sprintf(\"INSERT INTO test(id, label) VALUES (1, '%s')\", $value))) {\n                printf(\"[%03d] + 1] Insert failed, %d - %s\\n\", $offset,\n                    $db->errorCode(), var_export($db->errorInfo(), true));\n                return false;\n            }\n\n            $stmt = $db->prepare('SELECT id, label FROM test');\n            $stmt->execute();\n            $meta = $stmt->getColumnMeta(1);\n            return test_return($meta, $offset, $native_type, $pdo_type);\n        }\n\n        echo \"Test 2.2 testing numeric columns\\n\";\n\n        test_meta($db, 20, 'NUMBER'         , 0                    , 'NUMBER', PDO::PARAM_STR);\n        test_meta($db, 30, 'NUMBER'         , 256                  , 'NUMBER', PDO::PARAM_STR);\n        test_meta($db, 40, 'INT'            , 256                  , 'NUMBER', PDO::PARAM_STR);\n        test_meta($db, 50, 'INTEGER'        , 256                  , 'NUMBER', PDO::PARAM_STR);\n        test_meta($db, 60, 'NUMBER'         , 256.01               , 'NUMBER', PDO::PARAM_STR);\n        test_meta($db, 70, 'NUMBER'         , -8388608             , 'NUMBER', PDO::PARAM_STR);\n\n        test_meta($db, 80, 'NUMBER'         , 2147483648           , 'NUMBER', PDO::PARAM_STR);\n        test_meta($db, 90, 'NUMBER'         , 4294967295           , 'NUMBER', PDO::PARAM_STR);\n\n        test_meta($db, 100, 'DEC'             , 1.01               , 'NUMBER'       , PDO::PARAM_STR);\n        test_meta($db, 110, 'DECIMAL'         , 1.01               , 'NUMBER'       , PDO::PARAM_STR);\n        test_meta($db, 120, 'FLOAT'           , 1.01               , 'FLOAT'        , PDO::PARAM_STR);\n        test_meta($db, 130, 'DOUBLE PRECISION', 1.01               , 'FLOAT'        , PDO::PARAM_STR);\n        test_meta($db, 140, 'BINARY_FLOAT'    , 1.01               , 'BINARY_FLOAT' , PDO::PARAM_STR);\n        test_meta($db, 150, 'BINARY_DOUBLE'   , 1.01               , 'BINARY_DOUBLE', PDO::PARAM_STR);\n\n        echo \"Test 2.3 testing temporal columns\\n\";\n\n        $db->exec(\"alter session set nls_date_format='YYYY-MM-DD'\");\n        test_meta($db, 160, 'DATE'           , '2008-04-23'        , 'DATE', PDO::PARAM_STR);\n\n        echo \"Test 2.4 testing string columns\\n\";\n\n        test_meta($db, 170, 'CHAR(1)'       , 'a'                  , 'CHAR'     , PDO::PARAM_STR);\n        test_meta($db, 180, 'CHAR(10)'      , '0123456789'         , 'CHAR'     , PDO::PARAM_STR);\n        test_meta($db, 190, 'CHAR(255)'     , str_repeat('z', 255) , 'CHAR'     , PDO::PARAM_STR);\n        test_meta($db, 200, 'VARCHAR(1)'    , 'a'                  , 'VARCHAR2' , PDO::PARAM_STR);\n        test_meta($db, 210, 'VARCHAR(10)'   , '0123456789'         , 'VARCHAR2' , PDO::PARAM_STR);\n        test_meta($db, 220, 'VARCHAR(255)'  , str_repeat('z', 255) , 'VARCHAR2' , PDO::PARAM_STR);\n        test_meta($db, 230, 'VARCHAR2(1)'   , 'a'                  , 'VARCHAR2' , PDO::PARAM_STR);\n        test_meta($db, 240, 'VARCHAR2(10)'  , '0123456789'         , 'VARCHAR2' , PDO::PARAM_STR);\n        test_meta($db, 250, 'VARCHAR2(255)' , str_repeat('z', 255) , 'VARCHAR2' , PDO::PARAM_STR);\n\n        test_meta($db, 260, 'NCHAR(1)'      , 'a'                  , 'NCHAR'    , PDO::PARAM_STR);\n        test_meta($db, 270, 'NCHAR(10)'     , '0123456789'         , 'NCHAR'    , PDO::PARAM_STR);\n        test_meta($db, 280, 'NCHAR(255)'    , str_repeat('z', 255) , 'NCHAR'    , PDO::PARAM_STR);\n        test_meta($db, 290, 'NVARCHAR2(1)'  , 'a'                  , 'NVARCHAR2', PDO::PARAM_STR);\n        test_meta($db, 300, 'NVARCHAR2(10)' , '0123456789'         , 'NVARCHAR2', PDO::PARAM_STR);\n        test_meta($db, 310, 'NVARCHAR2(255)', str_repeat('z', 255) , 'NVARCHAR2', PDO::PARAM_STR);\n\n        echo \"Test 2.5 testing lobs columns\\n\";\n\n        test_meta($db, 320, 'CLOB'          , str_repeat('b', 255) , 'CLOB'    , PDO::PARAM_LOB);\n        test_meta($db, 330, 'BLOB'          , str_repeat('b', 256) , 'BLOB'    , PDO::PARAM_LOB);\n        test_meta($db, 340, 'NCLOB'         , str_repeat('b', 255) , 'NCLOB'   , PDO::PARAM_LOB);\n\n        test_meta($db, 350, 'LONG'          , str_repeat('b', 256) , 'LONG'    , PDO::PARAM_STR);\n        test_meta($db, 360, 'LONG RAW'      , str_repeat('b', 256) , 'LONG RAW', PDO::PARAM_STR);\n        test_meta($db, 370, 'RAW(256)'      , str_repeat('b', 256) , 'RAW'     , PDO::PARAM_STR);\n\n\n        $db->exec(<<<SQL\n    BEGIN\n       EXECUTE IMMEDIATE 'DROP TABLE test';\n    EXCEPTION\n       WHEN OTHERS THEN\n          IF SQLCODE != -942 THEN\n             RAISE;\n          END IF;\n    END;\n    SQL\n    );\n        echo \"Test 2.6 testing function return\\n\";\n\n        $stmt = $db->query('SELECT count(*) FROM dual');\n        $meta = $stmt->getColumnMeta(0);\n        test_return($meta, 380, 'NUMBER', PDO::PARAM_STR);\n        $stmt = $db->query(\"SELECT TO_DATE('2008-04-23') FROM dual\");\n        $meta = $stmt->getColumnMeta(0);\n        test_return($meta, 390, 'DATE', PDO::PARAM_STR);\n        $stmt = $db->query(\"SELECT TO_CHAR(542) FROM dual\");\n        $meta = $stmt->getColumnMeta(0);\n        test_return($meta, 400, 'VARCHAR2', PDO::PARAM_STR);\n\n\n        echo \"Test 2.7 testing flags returned\\n\";\n\n        $sql = sprintf('CREATE TABLE test(id INT NOT NULL, label INT NULL)');\n        $stmt = $db->prepare($sql);\n        $stmt->execute();\n        $db->exec('INSERT INTO test(id, label) VALUES (1, 1)');\n        $stmt = $db->query('SELECT id, label FROM test');\n        $meta = $stmt->getColumnMeta(0);\n        // verify the flags array contains a not_null flag and not nullable flags\n        if (!isset($meta['flags'])) {\n            printf(\"[1001] No flags contained in metadata %s\\n\", var_export($meta, true));\n        } else {\n            $flags = $meta['flags'];\n            $found = false;\n            foreach ($flags as $k => $flag) {\n                if ($flag == 'not_null')\n                    $found = true;\n                if ($flag == 'nullable')\n                    printf(\"[1003] Flags seem wrong %s\\n\", var_export($meta, true));\n            }\n            if (!$found)\n                printf(\"[1002] Flags seem wrong %s\\n\", var_export($meta, true));\n        }\n        $meta = $stmt->getColumnMeta(1);\n        // verify the flags array contains a nullable flag and not not_null flags\n        if (!isset($meta['flags'])) {\n            printf(\"[1003] No flags contained in metadata %s\\n\", var_export($meta, true));\n        } else {\n            $flags = $meta['flags'];\n            $found = false;\n            foreach ($flags as $k => $flag) {\n                if ($flag == 'not_null')\n                    printf(\"[1004] Flags seem wrong %s\\n\", var_export($meta, true));\n                if ($flag == 'nullable')\n                    $found = true;\n            }\n            if (!$found)\n                printf(\"[1005] Flags seem wrong %s\\n\", var_export($meta, true));\n        }\n\n    } catch (PDOException $e) {\n        // we should never get here, we use warnings, but never trust a system...\n        printf(\"[001] %s, [%s} %s\\n\",\n            $e->getMessage(), $db->errorInfo(), implode(' ', $db->errorInfo()));\n    }\n\n    $db->exec(<<<SQL\n    BEGIN\n       EXECUTE IMMEDIATE 'DROP TABLE test';\n    EXCEPTION\n       WHEN OTHERS THEN\n          IF SQLCODE != -942 THEN\n             RAISE;\n          END IF;\n    END;\n    SQL\n    );\n    print \"done!\";\n});\n?>\n--EXPECT--\nPreparations before the test\nTest 1. calling function with invalid parameters\n 1.1 Expecting false got false\n 1.2 PDOStatement::getColumnMeta() expects exactly 1 argument, 0 given\n 1.3 PDOStatement::getColumnMeta(): Argument #1 ($column) must be greater than or equal to 0\n 1.4 PDOStatement::getColumnMeta(): Argument #1 ($column) must be of type int, array given\n 1.5 PDOStatement::getColumnMeta() expects exactly 1 argument, 2 given\n 1.6 Expecting false because of invalid offset got false\nTest 2. testing return values\nTest 2.1 testing array returned\nTest 2.2 testing numeric columns\nTest 2.3 testing temporal columns\nTest 2.4 testing string columns\nTest 2.5 testing lobs columns\nTest 2.6 testing function return\nTest 2.7 testing flags returned\ndone!\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_stream_1.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: stream_get_contents length & offset test\n--SKIPIF--\n<?php\nif (PHP_VERSION < 80100) {\n\trequire __DIR__ . '/../include/skipif.inc';\n\tskip('php version 8.1 or higher');\n}\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $dbh = PdoOracleTest::create();\n\n    $dbh->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);\n\n    // Initialization\n\n    $stmtarray = array(\n        \"create table pdo_oci_stream_1_tab (id number, data clob)\",\n    );\n\n    foreach ($stmtarray as $stmt) {\n        $dbh->exec($stmt);\n    }\n\n    $dbh->exec(\"\n      declare\n        lob1 clob := 'abc' || lpad('j',30000,'j') || 'xyz';\n       begin\n        insert into pdo_oci_stream_1_tab (id,data) values (1, 'abcdefghijklmnopqrstuvwxyz');\n        insert into pdo_oci_stream_1_tab (id,data) values (2, lob1);\n      end;\");\n\n    echo \"Test 1\\n\";\n\n    $s = $dbh->prepare(\"select data from pdo_oci_stream_1_tab where id = 1\");\n    $s->execute();\n    $r = $s->fetch();\n\n    // stream_get_contents ( resource $handle [, int $maxlength = -1 [, int $offset = -1 ]] )\n    echo 'Read '.stream_get_contents($r['data'], 1, 1).\"$\\n\";  // b\n    echo 'Read '.stream_get_contents($r['data'], 2, 1).\"$\\n\";  // cd\n    echo 'Read '.stream_get_contents($r['data'], 2, 0).\"$\\n\";  // ab\n    echo 'Read '.stream_get_contents($r['data'], 26, 0).\"$\\n\"; // abcdefghijklmnopqrstuvwxyz\n    echo 'Read '.stream_get_contents($r['data'], 27, 0).\"$\\n\"; // abcdefghijklmnopqrstuvwxyz\n    echo 'Read '.stream_get_contents($r['data'], 27, 1).\"$\\n\"; // bcdefghijklmnopqrstuvwxyz\n    echo 'Read '.stream_get_contents($r['data'], 1, 20).\"$\\n\"; // u\n    echo 'Read '.stream_get_contents($r['data'], 1, 25).\"$\\n\"; // z\n    echo 'Read '.stream_get_contents($r['data'], 1, 26).\"$\\n\"; // <blank>\n    echo 'Read '.stream_get_contents($r['data'], 1, 0).\"$\\n\";  // a\n\n    echo \"\\nTest 2\\n\";\n\n    $s = $dbh->prepare(\"select data from pdo_oci_stream_1_tab where id = 2\");\n    $s->execute();\n    $r = $s->fetch();\n\n    echo 'Read '.stream_get_contents($r['data'], 5, 0).\"\\n\";           // abcjj\n    echo 'Read '.stream_get_contents($r['data'], 5, 2).\"\\n\";           // cjjjj\n    echo 'Read '.stream_get_contents($r['data'], 6, 1).\"\\n\";           // bcjjjj\n    echo 'Read '.strlen(stream_get_contents($r['data'], -1,0)).\"\\n\";   // 30006\n    echo 'Read '.strlen(stream_get_contents($r['data'], 0,0)).\"\\n\";    // 0\n    echo 'Read '.strlen(stream_get_contents($r['data'], 0,1)).\"\\n\";    // 0\n    echo 'Read '.strlen(stream_get_contents($r['data'], 10,100)).\"\\n\"; // 10\n    echo 'Read '.stream_get_contents($r['data'], 6, 30000).\"\\n\";       // jjjxyz\n    echo 'Read '.stream_get_contents($r['data'], 7, 30000).\"\\n\";       // jjjxyz\n    echo 'Read '.strlen(stream_get_contents($r['data'])).\"\\n\";         // 0\n    echo 'Read '.strlen(stream_get_contents($r['data'], 0)).\"\\n\";      // 0\n    echo 'Read '.strlen(stream_get_contents($r['data'], -1)).\"\\n\";     // 0\n    echo 'Read '.stream_get_contents($r['data'], -1, 30000).\"\\n\";      // jjjxyz\n\n    // Clean up\n\n    $stmtarray = array(\n        \"drop table pdo_oci_stream_1_tab\"\n    );\n\n    foreach ($stmtarray as $stmt) {\n        $dbh->exec($stmt);\n    }\n});\n?>\n--EXPECT--\nTest 1\nRead b$\nRead cd$\nRead ab$\nRead abcdefghijklmnopqrstuvwxyz$\nRead abcdefghijklmnopqrstuvwxyz$\nRead bcdefghijklmnopqrstuvwxyz$\nRead u$\nRead z$\nRead $\nRead a$\n\nTest 2\nRead abcjj\nRead cjjjj\nRead bcjjjj\nRead 30006\nRead 0\nRead 0\nRead 10\nRead jjjxyz\nRead jjjxyz\nRead 0\nRead 0\nRead 0\nRead jjjxyz\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_stream_2.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: Insert and fetch 1K records from a table that contains 1 number and 2 LOB columns (stress test)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nif (getenv('SKIP_SLOW_TESTS')) die('skip slow tests excluded by request');\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $db = PdoOracleTest::create();\n    $query = \"begin execute immediate 'drop table pdo_oci_stream_2'; exception when others then if sqlcode <> -942 then raise; end if; end;\";\n    $stmt = $db->prepare($query);\n    $stmt->execute();\n\n    $query = \"create table pdo_oci_stream_2 (id number, data1 blob, data2 blob)\";\n    $stmt = $db->prepare($query);\n    $stmt->execute();\n\n    function do_insert($db, $id, $data1, $data2)\n    {\n        $db->beginTransaction();\n        $stmt = $db->prepare(\"insert into pdo_oci_stream_2 (id, data1, data2) values (:id, empty_blob(), empty_blob()) returning data1, data2 into :blob1, :blob2\");\n        $stmt->bindParam(':id', $id);\n        $stmt->bindParam(':blob1', $blob1, PDO::PARAM_LOB);\n        $stmt->bindParam(':blob2', $blob2, PDO::PARAM_LOB);\n        $blob1 = null;\n        $blob2 = null;\n        $stmt->execute();\n\n        fwrite($blob1, $data1);\n        fclose($blob1);\n        fwrite($blob2, $data2);\n        fclose($blob2);\n        $db->commit();\n    }\n\n    $a1 = str_repeat('a', 4086);\n    $a2 = str_repeat('b', 4087);\n    $a3 = str_repeat('c', 4088);\n    $a4 = str_repeat('d', 4089);\n    $a5 = str_repeat('e', 4090);\n    $a6 = str_repeat('f', 4091);\n    $a7 = str_repeat('g', 4092);\n    $a8 = str_repeat('h', 4093);\n    $a9 = str_repeat('i', 4094);\n    $a10 = str_repeat('j', 4095);\n\n    printf(\"Inserting 1000 Records ... \");\n    for($i=0; $i<100; $i++) {\n        do_insert($db, $i * 10 + 1, $a1, $a10);\n        do_insert($db, $i * 10 + 2, $a2, $a9);\n        do_insert($db, $i * 10 + 3, $a3, $a8);\n        do_insert($db, $i * 10 + 4, $a4, $a7);\n        do_insert($db, $i * 10 + 5, $a5, $a6);\n        do_insert($db, $i * 10 + 6, $a6, $a5);\n        do_insert($db, $i * 10 + 7, $a7, $a4);\n        do_insert($db, $i * 10 + 8, $a8, $a3);\n        do_insert($db, $i * 10 + 9, $a9, $a2);\n        do_insert($db, $i * 10 + 10, $a10, $a1);\n    }\n    printf(\"Done\\n\");\n\n    /* Cleanup is done in pdo_oci_stream_2b.phpt */\n    //$db->exec(\"drop table pdo_oci_stream_2\");\n\n    $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);  // Let's use streams\n\n    // Since each column only has one lob descriptor, the last row is\n    // shown twice because the lob descriptor for each column is reused in\n    // the stream\n\n    $i = 0;\n    $j = 9;\n    $a_val = ord('a');\n    foreach($db->query(\"select data1 as d4_1, data2 as d4_2 from pdo_oci_stream_2 order by id\") as $row) {\n        $a = $row['d4_1'];\n        $a1 = $row['d4_2'];\n\n        $str1 = stream_get_contents($a);\n        $str2 = stream_get_contents($a1);\n\n        $str1len = strlen($str1);\n        $str2len = strlen($str2);\n\n        $b = ord($str1[0]);\n        $b1 = ord($str2[0]);\n\n        if (($b != ($a_val + $i)) && ($str1len != (4086 + $i)) &&\n            ($b1 != ($a_val + $j)) && ($str2len != (4086 + $j))) {\n            printf(\"There is a bug!\\n\");\n            printf(\"Col1:\\n\");\n            printf(\"a_val = %d\\n\", $a_val);\n            printf(\"b     = %d\\n\", $b);\n            printf(\"i     = %d\\n\", $i);\n            printf(\"str1len = %d\\n\", $str1len);\n\n            printf(\"Col2:\\n\");\n            printf(\"a_val = %d\\n\", $a_val);\n            printf(\"b1    = %d\\n\", $b1);\n            printf(\"j     = %d\\n\", $j);\n            printf(\"str2len = %d\\n\", $str1len);\n\n        }\n        $i++;\n        if ($i>9)\n            $i = 0;\n        $j--;\n        if ($j<0)\n            $j = 9;\n    }\n    echo \"Fetch operation done!\\n\";\n\n    /* Cleanup */\n    $db->exec(\"drop table pdo_oci_stream_2\");\n});\n?>\n--EXPECT--\nInserting 1000 Records ... Done\nFetch operation done!\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oci_templob_1.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: Test to verify all implicitly created temporary LOB are cleaned up\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?PHP\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $db = PdoOracleTest::create();\n    $clobquery1 = \"select TO_CLOB('Hello World') CLOB_DATA from dual\";\n    $clobquery2 = \"select TO_CLOB('Hello World') CLOB_DATA from dual\";\n    $clobquery3 = \"select TO_CLOB('Hello World') CLOB_DATA from dual\";\n    $clobquery4 = \"select TO_CLOB('Hello World') CLOB_DATA from dual\";\n    $clobquery5 = \"select TO_CLOB('Hello World') CLOB_DATA from dual\";\n    $clobquery6 = \"select TO_CLOB('Hello World') CLOB_DATA from dual\";\n    $clobquery7 = \"select TO_CLOB('Hello World') CLOB_DATA from dual\";\n    $clobquery8 = \"select TO_CLOB('Hello World') CLOB_DATA from dual\";\n    $clobquery9 = \"select TO_CLOB('Hello World') CLOB_DATA from dual\";\n    $clobquery10 = \"select TO_CLOB('Hello World') CLOB_DATA from dual\";\n\n    $stmt= $db->prepare($clobquery1);\n    $stmt->execute();\n    $row = $stmt->fetch();\n    $stmt= $db->prepare($clobquery2);\n    $stmt->execute();\n    $row = $stmt->fetch();\n    $stmt= $db->prepare($clobquery3);\n    $stmt->execute();\n    $row = $stmt->fetch();\n    $stmt= $db->prepare($clobquery4);\n    $stmt->execute();\n    $row = $stmt->fetch();\n    $stmt= $db->prepare($clobquery5);\n    $stmt->execute();\n    $row = $stmt->fetch();\n    $stmt= $db->prepare($clobquery6);\n    $stmt->execute();\n    $row = $stmt->fetch();\n    $stmt= $db->prepare($clobquery7);\n    $stmt->execute();\n    $row = $stmt->fetch();\n    $stmt= $db->prepare($clobquery8);\n    $stmt->execute();\n    $row = $stmt->fetch();\n    $stmt= $db->prepare($clobquery9);\n    $stmt->execute();\n    $row = $stmt->fetch();\n    $stmt= $db->prepare($clobquery10);\n    $stmt->execute();\n    $row = $stmt->fetch();\n\n    $query1 = \"SELECT SYS_CONTEXT('USERENV', 'SID') SID FROM DUAL\";\n\n    $stmt1 = $db->prepare($query1);\n    $stmt1->execute();\n\n    $row1 = $stmt1->fetch();\n    $sid_value = $row1[0];\n\n    $query2 = \"SELECT (CACHE_LOBS+NOCACHE_LOBS+ABSTRACT_LOBS) FROM V\\$TEMPORARY_LOBS WHERE SID = :SID_VALUE\";\n\n    $stmt2 = $db->prepare($query2);\n    $stmt2->bindParam(':SID_VALUE', $sid_value);\n    $stmt2->execute();\n\n    $row2 = $stmt2->fetch();\n    /* 1 temporary LOB still exists in V$TEMPORARY_LOBS since the destructor of $stmt is not yet called by PHP */\n    if ($row2[0] > 1)\n    {\n      echo \"TEMP_LOB is not yet cleared!\" . $row2[0] . \"\\n\";\n    }\n    else\n    {\n      echo \"Success! All the temporary LOB in previously closed statements are properly cleaned.\\n\";\n    }\n});\n?>\n--EXPECTF--\nTEMP_LOB is not yet cleared!10\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pdo_oracle.inc",
    "content": "<?php\ndeclare(strict_types=1);\n\nclass PdoOracleTest\n{\n     public static function skip() {\n        try {\n            $db = self::create();\n        } catch (PDOException $e) {\n            die(\"skip \" . $e->getMessage());\n        }\n     }\n\n    public static function create(): PDO\n    {\n        $db = new PDO(ORACLE_TNS, ORACLE_USER, ORACLE_PASSWORD);\n        $db->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);\n        return $db;\n    }\n}\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pecl_bug_11345.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: PECL PDO_OCI Bug #11345 (Test invalid character set name)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\n// This tests only part of PECL bug 11345.  The other part - testing\n// when the National Language Support (NLS) environment can't be\n// initialized - is very difficult to test portably.\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    try {\n        $dbh = new PDO('oci:dbname=xxx;charset=yyy', 'abc', 'def');\n    }\n    catch (PDOException $e) {\n        echo 'Connection failed: ' . $e->getMessage(). \"\\n\";\n    }\n});\n?>\n--EXPECTF--\nConnection failed: SQLSTATE[HY000]: OCINlsCharSetNameToId: unknown character set name (%s)\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/pecl_bug_6364.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: PECL PDO_OCI Bug #6364 (segmentation fault on stored procedure call with OUT binds)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nif (getenv('SKIP_ASAN')) die('skip leaks memory under asan');\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_ORACLE]);\nrun(function() {\n    $dbh = PdoOracleTest::create();\n    $dbh->exec (\"create table bug_6364_t (c1 varchar2(10), c2 varchar2(10), c3 varchar2(10), c4 varchar2(10), c5 varchar2(10))\");\n\n    $dbh->exec (\"create or replace procedure bug_6364_sp(p1 IN varchar2, p2 IN varchar2, p3 IN varchar2, p4 OUT varchar2, p5 OUT varchar2) as begin insert into bug_6364_t (c1, c2, c3) values (p1, p2, p3); p4 := 'val4'; p5 := 'val5'; end;\");\n\n    $stmt = $dbh->prepare(\"call bug_6364_sp('p1','p2','p3',?,?)\");\n\n    $out_param1 = \"a\";\n    $out_param2 = \"a\";\n\n    $stmt->bindParam(1, $out_param1,PDO::PARAM_STR, 1024);\n    $stmt->bindParam(2, $out_param2,PDO::PARAM_STR, 1024);\n\n    $stmt->execute() or die (\"Execution error: \" . var_dump($dbh->errorInfo()));\n\n    var_dump($out_param1);\n    var_dump($out_param2);\n\n    foreach ($dbh->query(\"select * from bug_6364_t\") as $row) {\n        var_dump($row);\n    }\n\n    print \"Done\\n\";\n\n    // Cleanup\n    $dbh->exec (\"drop procedure bug_6364_sp\");\n    $dbh->exec (\"drop table bug_6364_t\");\n});\n?>\n--EXPECT--\nstring(4) \"val4\"\nstring(4) \"val5\"\narray(10) {\n  [\"c1\"]=>\n  string(2) \"p1\"\n  [0]=>\n  string(2) \"p1\"\n  [\"c2\"]=>\n  string(2) \"p2\"\n  [1]=>\n  string(2) \"p2\"\n  [\"c3\"]=>\n  string(2) \"p3\"\n  [2]=>\n  string(2) \"p3\"\n  [\"c4\"]=>\n  NULL\n  [3]=>\n  NULL\n  [\"c5\"]=>\n  NULL\n  [4]=>\n  NULL\n}\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/transcation.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: PDO OCI transcation1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_ALL]);\nrun(function() {\n    $db = PdoOracleTest::create();\n    $db->exec('create table transcation1 (id int)');\n    go(function () use($db){\n        $db->beginTransaction();\n        $stmt = $db->prepare(\"insert into transcation1 values (?)\");\n        $stmt->execute([1]);\n        go(function () use($db){\n            $db->beginTransaction();\n            $stmt = $db->prepare(\"insert into transcation1 values (?)\");\n            $stmt->execute([2]);\n            $db->rollback();\n        });\n        sleep(2);\n        $db->commit();\n        $stmt = $db->prepare(\"select id from transcation1 where id = ?\");\n        $stmt->execute([1]);\n        var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == 1);\n    });\n    sleep(4);\n});\n?>\n--EXPECTF--\nFatal error: Uncaught PDOException: There is already an active transaction in %s:%d\nStack trace:\n#0 %s(%d): PDO->beginTransaction()\n%A\n  thrown in %s on line %d\n"
  },
  {
    "path": "tests/swoole_pdo_oracle/transcation2.phpt",
    "content": "--TEST--\nswoole_pdo_oracle: PDO OCI transcation2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\nPdoOracleTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_oracle.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_ALL]);\nrun(function() {\n    $db = PdoOracleTest::create();\n    $db->exec('create table transcation2 (id int)');\n\n    go(function() {\n        $db = PdoOracleTest::create();\n        $db->beginTransaction();\n        $stmt = $db->prepare(\"insert into transcation2 values (?)\");\n        $stmt->execute([1]);\n        $db->commit();\n        $stmt = $db->prepare(\"select id from transcation2 where id = ?\");\n        $stmt->execute([1]);\n        var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == 1);\n    });\n\n    go(function(){\n        $db = PdoOracleTest::create();\n        $db->beginTransaction();\n        $stmt = $db->prepare(\"insert into transcation2 values (?)\");\n        $stmt->execute([2]);\n        $db->commit();\n        $stmt = $db->prepare(\"select id from transcation2 where id = ?\");\n        $stmt->execute([2]);\n        var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == 2);\n    });\n    sleep(1);\n    $db->exec('drop table transcation2');\n});\n?>\n--EXPECT--\nbool(true)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_pdo_pgsql/base.phpt",
    "content": "--TEST--\nswoole_pdo_pgsql: test hook pgsql\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_pgsql.inc';\n\nCo\\run(static function (): void {\n    pdo_pgsql_test_inc::init();\n    Co\\go(function () {\n        $pdo = pdo_pgsql_test_inc::create();\n        $statement = $pdo->prepare('SELECT * FROM pg_catalog.pg_tables limit 1');\n        $statement->execute();\n        var_dump($statement->fetchAll(PDO::FETCH_COLUMN));\n    });\n\n    Co\\go(function () {\n        $pdo = pdo_pgsql_test_inc::create();\n        $statement = $pdo->prepare('SELECT * FROM pg_catalog.pg_tables limit 1');\n        $statement->execute();\n        var_dump($statement->fetchAll(PDO::FETCH_COLUMN));\n    });\n});\n\necho \"Done\\n\";\n?>\n--EXPECTF--\narray(1) {\n  [0]=>\n  string(%d) \"%s\"\n}\narray(1) {\n  [0]=>\n  string(%d) \"%s\"\n}\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_pgsql/blocking.phpt",
    "content": "--TEST--\nswoole_pdo_pgsql: test hook pgsql\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_pgsql.inc';\n\nconst N = 20;\n\nCo\\run(static function (): void {\n    $sleep_count = 0;\n    Co\\go(function () use (&$sleep_count) {\n        $n = N;\n        while ($n--) {\n            Co::sleep(0.002);\n            $sleep_count++;\n        }\n    });\n    // disable pdo_pgsql hook\n    Swoole\\Runtime::enableCoroutine(0);\n    $pdo = pdo_pgsql_test_inc::create();\n    $statement = $pdo->prepare('SELECT pg_sleep(1)');\n    $statement->execute();\n    Assert::eq($sleep_count, 0);\n    Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'pg_sleep');\n});\n\necho \"Done\\n\";\n?>\n--EXPECTF--\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_pgsql/bug_5635.phpt",
    "content": "--TEST--\nswoole_pdo_pgsql: Github bug #5635\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_pgsql.inc';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\WaitGroup;\nuse Swoole\\Coroutine\\Channel;\nuse function Swoole\\Coroutine\\run;\n\nini_set(\"memory_limit\", \"-1\");\n\n$pdo = pdo_pgsql_test_inc::create();\n$pdo->exec('create table bug_5635 (id int, data varchar(1024));');\n$pdo->exec(<<<EOL\nDO $$\nBEGIN\nFOR i IN 1..5000000 LOOP\nINSERT INTO bug_5635(id, data) VALUES (i, 'data' || i);\nEND LOOP;\nEND $$;\nEOL);\n\nCoroutine::set(['hook_flags' => SWOOLE_HOOK_PDO_PGSQL]);\nrun(function() {\n    $waitGroup = new WaitGroup();\n    $channel = new Channel(1);\n\n    Coroutine::create(function() use ($waitGroup, $channel) {\n        $start = time();\n        $waitGroup->add();\n        $pdo = pdo_pgsql_test_inc::create();\n        $stmt = $pdo->query(\"select * from bug_5635;\");\n        $data = $stmt->fetchAll();\n        Assert::true(count($data) == 5000000);\n        $channel->push($data ?? [], 10);\n        $waitGroup->done();\n        echo 'DONE' . PHP_EOL;\n    });\n\n    Coroutine::create(function() use ($waitGroup, $channel) {\n        $waitGroup->add();\n        $result = $channel->pop(0.5);\n        if (!$result) {\n            echo 'channel pop timeout' . PHP_EOL;\n        }\n        $waitGroup->done();\n    });\n\n    var_dump(1);\n    Coroutine::sleep(0.2);\n    var_dump(2);\n    $waitGroup->wait();\n});\n?>\n--CLEAN--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_pgsql.inc';\n$pdo = pdo_pgsql_test_inc::create();\n$pdo->exec('drop table bug_5635;');\n?>\n--EXPECTF--\nint(1)\nint(2)\nchannel pop timeout\nDONE\n"
  },
  {
    "path": "tests/swoole_pdo_pgsql/libpq_version.phpt",
    "content": "--TEST--\nswoole_pdo_pgsql: libpq version\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$version = shell_exec('php --ri swoole');\nAssert::true(str_contains($version, 'postgresql(libpq) version'));\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_pdo_pgsql/pdo_pgsql.inc",
    "content": "<?php\ndeclare(strict_types=1);\n\nclass pdo_pgsql_test_inc\n{\n    const TABLE = 'test_swoole_pgsql_user';\n\n    public static function init(): void\n    {\n        $pdo = self::create();\n        if (!self::tableExists($pdo, self::TABLE)) {\n            $pdo->exec('CREATE TABLE ' . self::TABLE . ' (\n                id SERIAL primary key NOT NULL,\n                name character varying(32),\n                age integer)');\n        }\n\n        $pdo->exec('TRUNCATE ' . self::TABLE);\n    }\n\n    public static function getTable()\n    {\n        return self::TABLE;\n    }\n\n    public static function tableExists($pdo, $table)\n    {\n        try {\n            $result = $pdo->query(\"SELECT 1 FROM {$table} LIMIT 1\");\n        } catch (Exception $e) {\n            return false;\n        }\n\n        return $result !== false;\n    }\n\n    public static function create(): PDO\n    {\n        $host = PGSQL_HOST;\n        $port = PGSQL_PORT;\n        $user = PGSQL_USER;\n        $password = PGSQL_PASSWORD;\n        $dbname = PGSQL_DBNAME;\n        return new PDO(\"pgsql:host={$host};port={$port};dbname={$dbname}\", $user, $password);\n    }\n}\n"
  },
  {
    "path": "tests/swoole_pdo_pgsql/query.phpt",
    "content": "--TEST--\nswoole_pdo_pgsql: test query\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_pgsql.inc';\n\nconst N = 10;\n\nCo\\run(static function (): void {\n    pdo_pgsql_test_inc::init();\n    $pdo = pdo_pgsql_test_inc::create();\n\n    $stmt = $pdo->prepare('INSERT INTO ' . pdo_pgsql_test_inc::getTable() . ' (name, age) values (?, ?)');\n\n    $list = [];\n    for ($i = 0; $i < N; $i++) {\n        $name = base64_encode(random_bytes(8));\n        $age = random_int(18, 35);\n        $stmt->bindValue(1, $name);\n        $stmt->bindValue(2, $age);\n        $stmt->execute();\n\n        $list[] = [\n            'id' => $pdo->lastInsertId(),\n            'name' => $name,\n            'age' => $age,\n        ];\n    }\n\n    foreach ($list as $rs) {\n        Co\\go(function () use ($rs) {\n            $pdo = pdo_pgsql_test_inc::create();\n            $statement = $pdo->query('select * from ' . pdo_pgsql_test_inc::getTable() . ' where id = ' . $rs['id'] . ' limit 1');\n            Assert::eq($statement->fetch(PDO::FETCH_ASSOC), $rs);\n        });\n    }\n});\n\necho \"Done\\n\";\n?>\n--EXPECTF--\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_pgsql/race.phpt",
    "content": "--TEST--\nswoole_pdo_pgsql: race\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_pgsql.inc';\n\nCo\\run(static function (): void {\n    $pdo = pdo_pgsql_test_inc::create();\n    $msg = [];\n    $n = 2;\n\n    while ($n--) {\n        Co\\go(function () use ($pdo, &$msg) {\n            $statement = $pdo->prepare('SELECT pg_sleep(1)');\n            try {\n                $statement->execute();\n                Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'pg_sleep');\n            } catch (\\PDOException $e) {\n                $msg[] = $e->getMessage();\n            }\n        });\n    }\n    Assert::count($msg, 1);\n    Assert::contains($msg[0], 'SQLSTATE[HY000]: General error');\n});\n\necho \"Done\\n\";\n?>\n--EXPECTF--\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_pgsql/sleep.phpt",
    "content": "--TEST--\nswoole_pdo_pgsql: test hook pgsql\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_pgsql.inc';\n\nconst N = 20;\n\nCo\\run(static function (): void {\n    $sleep_count = 0;\n    Co\\go(function () use (&$sleep_count) {\n        $n = N;\n        while ($n--) {\n            Co::sleep(0.002);\n            $sleep_count++;\n        }\n    });\n    $pdo = pdo_pgsql_test_inc::create();\n    $statement = $pdo->prepare('SELECT pg_sleep(1)');\n    $statement->execute();\n    Assert::eq($sleep_count, N);\n    Assert::keyExists($statement->fetchAll(PDO::FETCH_ASSOC)[0], 'pg_sleep');\n});\n\necho \"Done\\n\";\n?>\n--EXPECTF--\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_pgsql/transaction.phpt",
    "content": "--TEST--\nswoole_pdo_pgsql: test query\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_pgsql.inc';\n\nCo\\run(static function (): void {\n    pdo_pgsql_test_inc::init();\n    $pdo = pdo_pgsql_test_inc::create();\n\n    $stmt = $pdo->prepare('INSERT INTO ' . pdo_pgsql_test_inc::getTable() . ' (name, age) values (?, ?)');\n    $stmt->bindValue(1, base64_encode(random_bytes(8)));\n    $stmt->bindValue(2, random_int(18, 35));\n    $stmt->execute();\n\n    var_dump('insert');\n\n    Co::join([Co\\go(static function (): void {\n        $pdo = pdo_pgsql_test_inc::create();\n        try {\n            $pdo->beginTransaction();\n\n            $pdo->exec('DROP TABLE IF EXISTS ' . pdo_pgsql_test_inc::getTable());\n            throw new Exception('interrupt!!!');\n            $pdo->commit();\n        } catch (\\Exception $e) {\n            $pdo->rollBack();\n            var_dump('rollback');\n        }\n    })]);\n\n    var_dump('wait1');\n    var_dump(pdo_pgsql_test_inc::tableExists($pdo, pdo_pgsql_test_inc::getTable()));\n\n    Co::join([Co\\go(static function (): void {\n        $pdo = pdo_pgsql_test_inc::create();\n        try {\n            $pdo->beginTransaction();\n\n            $pdo->exec('DROP TABLE IF EXISTS ' . pdo_pgsql_test_inc::getTable());\n            $pdo->commit();\n        } catch (\\Exception $e) {\n            $pdo->rollBack();\n            var_dump($e->getMessage());\n        }\n    })]);\n\n    var_dump('wait2');\n    var_dump(pdo_pgsql_test_inc::tableExists($pdo, pdo_pgsql_test_inc::getTable()));\n});\n\necho \"Done\\n\";\n?>\n--EXPECTF--\nstring(6) \"insert\"\nstring(8) \"rollback\"\nstring(5) \"wait1\"\nbool(true)\nstring(5) \"wait2\"\nbool(false)\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug33841.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: PDO SQLite Bug #33841 (rowCount() does not work on prepared statements)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = PdoSqliteTest::create();\n\n    $db->exec('CREATE TABLE test (text)');\n\n    $stmt = $db->prepare(\"INSERT INTO test VALUES ( :text )\");\n    $stmt->bindParam(':text', $name);\n    $name = 'test1';\n    var_dump($stmt->execute(), $stmt->rowCount());\n\n    $stmt = $db->prepare(\"UPDATE test SET text = :text \");\n    $stmt->bindParam(':text', $name);\n    $name = 'test2';\n    var_dump($stmt->execute(), $stmt->rowCount());\n});\n?>\n--EXPECT--\nbool(true)\nint(1)\nbool(true)\nint(1)\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug35336.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:FETCH_CLASS + __set())\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nclass EEE {\n    function __set ($field, $value) {\n        echo \"hello world\\n\";\n    }\n}\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $a = new PDO(\"sqlite::memory:\");// pool (\"sqlite::memory:\");\n    $a->query (\"CREATE TABLE test (a integer primary key, b text)\");\n    $b = $a->prepare(\"insert into test (b) values (?)\");\n    $b->execute(array (5));\n    $rez = $a->query (\"SELECT * FROM test\")->fetchAll(PDO::FETCH_CLASS, 'EEE');\n\n    echo \"Done\\n\";\n});\n?>\n--EXPECT--\nhello world\nhello world\nDone\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug38334.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: Proper data-type support for PDO_SQLITE\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID < 80100) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.1 or higher');\n}\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO('sqlite::memory:');\n    $db->exec('CREATE TABLE test (i INTEGER , f DOUBLE, s VARCHAR(255))');\n    $db->exec('INSERT INTO test VALUES (42, 46.7, \"test\")');\n    var_dump($db->query('SELECT * FROM test')->fetch(PDO::FETCH_ASSOC));\n\n    // Check handling of integers larger than 32-bit.\n    $db->exec('INSERT INTO test VALUES (10000000000, 0.0, \"\")');\n    $i = $db->query('SELECT i FROM test WHERE f = 0.0')->fetchColumn(0);\n    if (PHP_INT_SIZE >= 8) {\n        var_dump($i === 10000000000);\n    } else {\n        var_dump($i === '10000000000');\n    }\n\n    // Check storing of strings into integer/float columns.\n    $db->exec('INSERT INTO test VALUES (\"test\", \"test\", \"x\")');\n    var_dump($db->query('SELECT * FROM test WHERE s = \"x\"')->fetch(PDO::FETCH_ASSOC));\n});\n?>\n--EXPECT--\narray(3) {\n  [\"i\"]=>\n  int(42)\n  [\"f\"]=>\n  float(46.7)\n  [\"s\"]=>\n  string(4) \"test\"\n}\nbool(true)\narray(3) {\n  [\"i\"]=>\n  string(4) \"test\"\n  [\"f\"]=>\n  string(4) \"test\"\n  [\"s\"]=>\n  string(1) \"x\"\n}\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug43831.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: Bug #43831 ($this gets mangled when extending PDO with persistent connection)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nclass Foo extends PDO {\n    function __construct($dsn) {\n        parent::__construct($dsn, null, null, array(PDO::ATTR_PERSISTENT => true));\n    }\n}\n\nclass Baz extends PDO {\n    function __construct($dsn) {\n        parent::__construct($dsn, null, null, array(PDO::ATTR_PERSISTENT => true));\n    }\n}\n\nclass Bar extends Baz {\n    function quux() {\n        echo get_class($this), \"\\n\";\n        $foo = new Foo(\"sqlite::memory:\");\n        echo get_class($this), \"\\n\";\n    }\n}\n\nclass MyPDO extends PDO {}\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $bar = new Bar(\"sqlite::memory:\");\n    $bar->quux();\n\n    $bar = new PDO(\"sqlite::memory:\", null, null, array(PDO::ATTR_PERSISTENT => true));\n    $baz = new MyPDO(\"sqlite::memory:\", null, null, array(PDO::ATTR_PERSISTENT => true));\n\n    var_dump($bar);\n    unset($bar);\n    var_dump($baz);\n    var_dump($bar);\n});\n?>\n--EXPECTF--\nBar\nBar\nobject(PDO)#%d (0) {\n}\nobject(MyPDO)#%d (0) {\n}\n\nWarning: Undefined variable $bar in %s on line %d\nNULL\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug44327_2_1.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:queryString property & numeric offsets / Crash)\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID >= 80100) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.0 or lower');\n}\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO('sqlite::memory:');\n\n    $x = $db->query('select 1 as queryString');\n    var_dump($x, $x->queryString);\n\n    $y = $x->fetch();\n    var_dump($y, @$y->queryString);\n\n    print \"--------------------------------------------\\n\";\n\n    $x = $db->query('select 1 as queryString');\n    var_dump($x, $x->queryString);\n\n    $y = $x->fetch(PDO::FETCH_LAZY);\n    var_dump($y, $y->queryString);\n});\n?>\n--EXPECTF--\nobject(PDOStatement)#%d (1) {\n  [\"queryString\"]=>\n  string(23) \"select 1 as queryString\"\n}\nstring(23) \"select 1 as queryString\"\narray(2) {\n  [\"queryString\"]=>\n  string(1) \"1\"\n  [0]=>\n  string(1) \"1\"\n}\nNULL\n--------------------------------------------\nobject(PDOStatement)#%d (1) {\n  [\"queryString\"]=>\n  string(23) \"select 1 as queryString\"\n}\nstring(23) \"select 1 as queryString\"\nobject(PDORow)#%d (1) {\n  [\"queryString\"]=>\n  string(1) \"1\"\n}\nstring(1) \"1\"\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug44327_2_2.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:queryString property & numeric offsets / Crash)\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID < 80100) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.1 or higher');\n}\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO('sqlite::memory:');\n\n    $x = $db->query('select 1 as queryString');\n    var_dump($x, $x->queryString);\n\n    $y = $x->fetch();\n    var_dump($y, @$y->queryString);\n\n    print \"--------------------------------------------\\n\";\n\n    $x = $db->query('select 1 as queryString');\n    var_dump($x, $x->queryString);\n\n    $y = $x->fetch(PDO::FETCH_LAZY);\n    var_dump($y, $y->queryString);\n});\n?>\n--EXPECTF--\nobject(PDOStatement)#%d (1) {\n  [\"queryString\"]=>\n  string(23) \"select 1 as queryString\"\n}\nstring(23) \"select 1 as queryString\"\narray(2) {\n  [\"queryString\"]=>\n  int(1)\n  [0]=>\n  int(1)\n}\nNULL\n--------------------------------------------\nobject(PDOStatement)#%d (1) {\n  [\"queryString\"]=>\n  string(23) \"select 1 as queryString\"\n}\nstring(23) \"select 1 as queryString\"\nobject(PDORow)#%d (1) {\n  [\"queryString\"]=>\n  string(23) \"select 1 as queryString\"\n}\nstring(23) \"select 1 as queryString\"\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug44327_3_1.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:queryString property & numeric offsets / Crash)\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID >= 80100) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.0 or lower');\n}\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO('sqlite::memory:');\n\n    $x = $db->query('select 1 as queryStringxx');\n    $y = $x->fetch(PDO::FETCH_LAZY);\n    var_dump($y, $y->queryString, $y->queryStringzz, $y->queryStringxx);\n\n    print \"---\\n\";\n\n    var_dump($y[5], $y->{3});\n});\n?>\n--EXPECTF--\nobject(PDORow)#%d (2) {\n  [\"queryString\"]=>\n  string(25) \"select 1 as queryStringxx\"\n  [\"queryStringxx\"]=>\n  string(1) \"1\"\n}\nstring(25) \"select 1 as queryStringxx\"\nNULL\nstring(1) \"1\"\n---\nNULL\nNULL\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug44327_3_2.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:queryString property & numeric offsets / Crash)\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID < 80100) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.1 or higher');\n}\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO('sqlite::memory:');\n\n    $x = $db->query('select 1 as queryStringxx');\n    $y = $x->fetch(PDO::FETCH_LAZY);\n    var_dump($y, $y->queryString, $y->queryStringzz, $y->queryStringxx);\n\n    print \"---\\n\";\n\n    var_dump($y[5], $y->{3});\n});\n?>\n--EXPECTF--\nobject(PDORow)#%d (2) {\n  [\"queryString\"]=>\n  string(25) \"select 1 as queryStringxx\"\n  [\"queryStringxx\"]=>\n  int(1)\n}\nstring(25) \"select 1 as queryStringxx\"\nNULL\nint(1)\n---\nNULL\nNULL\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug46139.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: Bug #46139 (PDOStatement->setFetchMode() forgets FETCH_PROPS_LATE)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\n\nclass Person {\n    public $test = NULL;\n    public function __construct() {\n        var_dump($this->test);\n    }\n}\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = PdoSqliteTest::create();\n\n    $stmt = $db->query(\"SELECT 'foo' test, 1\");\n    $stmt->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Person');\n    $r1 = $stmt->fetch();\n    printf(\"'%s'\\n\", $r1->test);\n\n    $stmt = $db->query(\"SELECT 'foo' test, 1\");\n    $stmt->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Person');\n    $r1 = $stmt->fetchAll();\n    printf(\"'%s'\\n\", $r1[0]->test);\n\n    $stmt = $db->query(\"SELECT 'foo' test, 1\");\n    $stmt->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Person');\n    $r1 = $stmt->fetch(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE);\n    printf(\"'%s'\\n\", $r1->test);\n});\n?>\n--EXPECT--\nNULL\n'foo'\nNULL\n'foo'\nNULL\n'foo'\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug46542.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: Bug #46542 Extending PDO class with a __call() function\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nclass A extends PDO\n{ function __call($m, $p) {print __CLASS__.\"::$m\\n\";} }\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $a = new A('sqlite:' . __DIR__ . '/dummy.db');\n\n    $a->truc();\n    $a->TRUC();\n});\n?>\n--CLEAN--\n<?php\nunlink(__DIR__ . '/dummy.db');\n?>\n--EXPECT--\nA::truc\nA::TRUC\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug48773.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:ATTR_STATEMENT_CLASS with ctor_args)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nclass bar extends PDOStatement {\n    private function __construct() {\n    }\n}\n\nclass foo extends PDO {\n    public $statementClass = 'bar';\n    function __construct($dsn, $username, $password, $driver_options = array()) {\n        $driver_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;\n        parent::__construct($dsn, $username, $password, $driver_options);\n\n        $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array($this->statementClass, array($this)));\n    }\n}\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new foo('sqlite::memory:', '', '');\n    $stmt = $db->query('SELECT 1');\n    var_dump($stmt);\n});\n?>\n--EXPECTF--\nobject(bar)#%d (1) {\n  [\"queryString\"]=>\n  string(8) \"SELECT 1\"\n}\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug50728.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: Bug #50728 (All PDOExceptions hardcode 'code' property to 0)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    try {\n        $a = new PDO(\"sqlite:/this/path/should/not/exist.db\");\n    } catch (PDOException $e) {\n        var_dump($e->getCode());\n    }\n});\n?>\n--EXPECT--\nint(14)\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug52487.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:FETCH_INTO leaks memory)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = PdoSqliteTest::create();\n    $stmt = $db->prepare(\"select 1 as attr\");\n    for ($i = 0; $i < 10; $i++) {\n        $stmt->setFetchMode(PDO::FETCH_INTO, new stdClass);\n    }\n\n    print \"ok\\n\";\n});\n?>\n--EXPECT--\nok\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug66033.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: Bug #66033 (Segmentation Fault when constructor of PDO statement throws an exception)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nclass DBStatement extends PDOStatement {\n    public $dbh;\n    protected function __construct($dbh) {\n        $this->dbh = $dbh;\n        throw new Exception(\"Blah\");\n    }\n}\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $pdo = new PDO('sqlite::memory:', null, null);\n    $pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('DBStatement',\n        array($pdo)));\n    $pdo->exec(\"CREATE TABLE IF NOT EXISTS messages (\n        id INTEGER PRIMARY KEY,\n        title TEXT,\n        message TEXT,\n        time INTEGER)\");\n\n    try {\n        $pdoStatement = $pdo->query(\"select * from messages\");\n    } catch (Exception $e) {\n        var_dump($e->getMessage());\n    }\n});\n?>\n--EXPECT--\nstring(4) \"Blah\"\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug70862.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: Testing sqliteCreateCollation()\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID >= 80200) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.1 or lower');\n}\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO('sqlite::memory:');\n    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n\n    $db->exec('CREATE TABLE test(field BLOB)');\n\n    $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0);\n    $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);\n\n    class HelloWrapper {\n        public function stream_open() { return true; }\n        public function stream_eof() { return true; }\n        public function stream_read() { return NULL; }\n        public function stream_stat() { return array(); }\n    }\n    stream_wrapper_register(\"hello\", \"HelloWrapper\");\n\n    $f = fopen(\"hello://there\", \"r\");\n\n    $stmt = $db->prepare('INSERT INTO test(field) VALUES (:para)');\n    $stmt->bindParam(\":para\", $f, PDO::PARAM_LOB);\n    $stmt->execute();\n\n    var_dump($f);\n});\n?>\n+++DONE+++\n--EXPECT--\nstring(0) \"\"\n+++DONE+++\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug70862_1.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: Testing sqliteCreateCollation()\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID < 80200) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.2 or higher');\n}\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO('sqlite::memory:');\n    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n\n    $db->exec('CREATE TABLE test(field BLOB)');\n\n    $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0);\n    $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);\n\n    class HelloWrapper {\n        public function stream_open() { return true; }\n        public function stream_eof() { return true; }\n        public function stream_read() { return NULL; }\n        public function stream_stat() { return array(); }\n    }\n    stream_wrapper_register(\"hello\", \"HelloWrapper\");\n\n    $f = fopen(\"hello://there\", \"r\");\n\n    $stmt = $db->prepare('INSERT INTO test(field) VALUES (:para)');\n    $stmt->bindParam(\":para\", $f, PDO::PARAM_LOB);\n    $stmt->execute();\n\n    var_dump($f);\n});\n?>\n+++DONE+++\n--EXPECTF--\n\nDeprecated: Creation of dynamic property HelloWrapper::$context is deprecated in %s on line %d\nstring(0) \"\"\n+++DONE+++\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug78192_1.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: PDO SQLite Bug #78192 SegFault when reuse statement after schema change\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID >= 80100) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.0 or lower');\n}\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $connection = new \\PDO('sqlite::memory:');\n    $connection->setAttribute(\\PDO::ATTR_ERRMODE, \\PDO::ERRMODE_EXCEPTION);\n    $connection->query('CREATE TABLE user (id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(255) NOT NULL)');\n\n    $stmt = $connection->prepare('INSERT INTO user (id, name) VALUES(:id, :name)');\n    $stmt->execute([\n        'id'   => 10,\n        'name' => 'test',\n    ]);\n\n    $stmt = $connection->prepare('SELECT * FROM user WHERE id = :id');\n    $stmt->execute(['id' => 10]);\n    var_dump($stmt->fetchAll(\\PDO::FETCH_ASSOC));\n\n    $connection->query('ALTER TABLE user ADD new_col VARCHAR(255)');\n    $stmt->execute(['id' => 10]);\n    var_dump($stmt->fetchAll(\\PDO::FETCH_ASSOC));\n});\n?>\n--EXPECT--\narray(1) {\n  [0]=>\n  array(2) {\n    [\"id\"]=>\n    string(2) \"10\"\n    [\"name\"]=>\n    string(4) \"test\"\n  }\n}\narray(1) {\n  [0]=>\n  array(3) {\n    [\"id\"]=>\n    string(2) \"10\"\n    [\"name\"]=>\n    string(4) \"test\"\n    [\"new_col\"]=>\n    NULL\n  }\n}\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug78192_2.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: PDO SQLite Bug #78192 SegFault when reuse statement after schema change\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID < 80100) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.1 or higher');\n}\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $connection = new \\PDO('sqlite::memory:');\n    $connection->setAttribute(\\PDO::ATTR_ERRMODE, \\PDO::ERRMODE_EXCEPTION);\n    $connection->query('CREATE TABLE user (id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(255) NOT NULL)');\n\n    $stmt = $connection->prepare('INSERT INTO user (id, name) VALUES(:id, :name)');\n    $stmt->execute([\n        'id'   => 10,\n        'name' => 'test',\n    ]);\n\n    $stmt = $connection->prepare('SELECT * FROM user WHERE id = :id');\n    $stmt->execute(['id' => 10]);\n    var_dump($stmt->fetchAll(\\PDO::FETCH_ASSOC));\n\n    $connection->query('ALTER TABLE user ADD new_col VARCHAR(255)');\n    $stmt->execute(['id' => 10]);\n    var_dump($stmt->fetchAll(\\PDO::FETCH_ASSOC));\n});\n?>\n--EXPECT--\narray(1) {\n  [0]=>\n  array(2) {\n    [\"id\"]=>\n    int(10)\n    [\"name\"]=>\n    string(4) \"test\"\n  }\n}\narray(1) {\n  [0]=>\n  array(3) {\n    [\"id\"]=>\n    int(10)\n    [\"name\"]=>\n    string(4) \"test\"\n    [\"new_col\"]=>\n    NULL\n  }\n}\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug79664_1.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:getColumnMeta fails on empty result set)\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID >= 80100) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.0 or lower');\n}\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $pdo = new PDO('sqlite::memory:', null, null, [\n    \tPDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,\n    ]);\n    $stmt = $pdo->query('select 1 where 0');\n    if ($stmt->columnCount()) {\n        var_dump($stmt->getColumnMeta(0));\n    }\n});\n?>\n--EXPECT--\narray(6) {\n  [\"native_type\"]=>\n  string(4) \"null\"\n  [\"flags\"]=>\n  array(0) {\n  }\n  [\"name\"]=>\n  string(1) \"1\"\n  [\"len\"]=>\n  int(-1)\n  [\"precision\"]=>\n  int(0)\n  [\"pdo_type\"]=>\n  int(2)\n}\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug79664_2.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:getColumnMeta fails on empty result set)\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID < 80100) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.1 or higher');\n}\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $pdo = new PDO('sqlite::memory:', null, null, [\n    \tPDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,\n    ]);\n    $stmt = $pdo->query('select 1 where 0');\n    if ($stmt->columnCount()) {\n        var_dump($stmt->getColumnMeta(0));\n    }\n});\n?>\n--EXPECT--\narray(6) {\n  [\"native_type\"]=>\n  string(4) \"null\"\n  [\"pdo_type\"]=>\n  int(0)\n  [\"flags\"]=>\n  array(0) {\n  }\n  [\"name\"]=>\n  string(1) \"1\"\n  [\"len\"]=>\n  int(-1)\n  [\"precision\"]=>\n  int(0)\n}\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug81740.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:quote() may return unquoted string)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\nif (PHP_INT_SIZE != 8) die(\"skip this test is for 64bit platforms only\");\nif (getenv(\"SKIP_SLOW_TESTS\")) die(\"skip slow test\");\n?>\n--INI--\nmemory_limit=-1\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $pdo = new PDO(\"sqlite::memory:\");\n    $string = str_repeat(\"a\", 0x80000000);\n    var_dump($pdo->quote($string));\n});\n?>\n--EXPECT--\nbool(false)\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug_42589.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: PDO SQLite Feature Request #42589 (getColumnMeta() should also return table name)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO(\"sqlite::memory:\");\n\n    $db->exec('CREATE TABLE test (field1 VARCHAR(10))');\n    $db->exec('INSERT INTO test VALUES(\"test\")');\n\n    $result = $db->query('SELECT * FROM test t1 LEFT JOIN test t2 ON t1.field1 = t2.field1');\n    $meta1 = $result->getColumnMeta(0);\n    $meta2 = $result->getColumnMeta(1);\n\n    var_dump(!empty($meta1['table']) && $meta1['table'] == 'test');\n    var_dump(!empty($meta2['table']) && $meta2['table'] == 'test');\n});\n?>\n--EXPECT--\nbool(true)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug_44159_sqlite_version_1.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: SQLite variant\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID >= 80100) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.0 or lower');\n}\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $pdo = new PDO(\"sqlite:\".__DIR__.\"/foo.db\");\n    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);\n\n    var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, NULL));\n    var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, 1));\n    var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, 'nonsense'));\n\n    @unlink(__DIR__.\"/foo.db\");\n});\n?>\n--EXPECT--\nbool(true)\nbool(true)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug_44159_sqlite_version_2.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: SQLite variant\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID < 80100) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.1 or higher');\n}\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $pdo = new PDO(\"sqlite:\".__DIR__.\"/foo.db\");\n    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);\n\n    try {\n        var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, NULL));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n    var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, 1));\n    try {\n        var_dump($pdo->setAttribute(PDO::NULL_TO_STRING, 'nonsense'));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n\n    @unlink(__DIR__.\"/foo.db\");\n});\n?>\n--EXPECT--\nAttribute value must be of type int for selected attribute, null given\nbool(true)\nAttribute value must be of type int for selected attribute, string given\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug_47769.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: Bug #47769 (Strange extends PDO)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nclass test extends PDO\n{\n    protected function isProtected() {\n        echo \"this is a protected method.\\n\";\n    }\n    private function isPrivate() {\n        echo \"this is a private method.\\n\";\n    }\n\n    public function quote($str, $paramtype = NULL): string|false {\n        $this->isProtected();\n        $this->isPrivate();\n        print $str .\"\\n\";\n\n        return $str;\n    }\n}\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $test = new test('sqlite::memory:');\n    $test->quote('foo');\n    $test->isProtected();\n});\n?>\n--EXPECTF--\nthis is a protected method.\nthis is a private method.\nfoo\n\nFatal error: Uncaught Error: Call to protected method test::isProtected() from global scope in %s:%d\nStack trace:\n%A\n  thrown in %s on line %d\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug_63916-2.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:PARAM_INT casts to 32bit int internally even on 64bit builds in pdo_sqlite\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\nif (PHP_INT_SIZE > 4) die('skip 32-bit only');\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $num = PHP_INT_MAX; // 32 bits\n    $conn = new PDO('sqlite::memory:');\n    $conn->query('CREATE TABLE users (id INTEGER NOT NULL, num INTEGER NOT NULL, PRIMARY KEY(id))');\n\n    $stmt = $conn->prepare('insert into users (id, num) values (:id, :num)');\n    $stmt->bindValue(':id', 1, PDO::PARAM_INT);\n    $stmt->bindValue(':num', $num, PDO::PARAM_INT);\n    $stmt->execute();\n\n    $stmt = $conn->query('SELECT num FROM users');\n    $result = $stmt->fetchAll(PDO::FETCH_COLUMN);\n\n    var_dump($num,$result[0]);\n});\n?>\n--EXPECT--\nint(2147483647)\nint(2147483647)\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug_63916_1.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:PARAM_INT casts to 32bit int internally even on 64bit builds in pdo_sqlite\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID >= 80100) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.0 or lower');\n}\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\nif (PHP_INT_SIZE < 8) die('skip');\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $num = 100004313234244; // exceeds 32 bits\n    $conn = new PDO('sqlite::memory:');\n    $conn->query('CREATE TABLE users (id INTEGER NOT NULL, num INTEGER NOT NULL, PRIMARY KEY(id))');\n\n    $stmt = $conn->prepare('insert into users (id, num) values (:id, :num)');\n    $stmt->bindValue(':id', 1, PDO::PARAM_INT);\n    $stmt->bindValue(':num', $num, PDO::PARAM_INT);\n    $stmt->execute();\n\n    $stmt = $conn->query('SELECT num FROM users');\n    $result = $stmt->fetchAll(PDO::FETCH_COLUMN);\n\n    var_dump($num,$result[0]);\n});\n?>\n--EXPECT--\nint(100004313234244)\nstring(15) \"100004313234244\"\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug_63916_2.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:PARAM_INT casts to 32bit int internally even on 64bit builds in pdo_sqlite\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID < 80100) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.1 or higher');\n}\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\nif (PHP_INT_SIZE < 8) die('skip');\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $num = 100004313234244; // exceeds 32 bits\n    $conn = new PDO('sqlite::memory:');\n    $conn->query('CREATE TABLE users (id INTEGER NOT NULL, num INTEGER NOT NULL, PRIMARY KEY(id))');\n\n    $stmt = $conn->prepare('insert into users (id, num) values (:id, :num)');\n    $stmt->bindValue(':id', 1, PDO::PARAM_INT);\n    $stmt->bindValue(':num', $num, PDO::PARAM_INT);\n    $stmt->execute();\n\n    $stmt = $conn->query('SELECT num FROM users');\n    $result = $stmt->fetchAll(PDO::FETCH_COLUMN);\n\n    var_dump($num,$result[0]);\n});\n?>\n--EXPECT--\nint(100004313234244)\nint(100004313234244)\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/bug_64705.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:__construct() fails\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $dsn = 'sqlite:./bug64705NonExistingDir/bug64705NonExistingDb';\n    try {\n        $pdo = new \\PDO($dsn, null, null);\n    } catch (\\PDOException $e) {\n        var_dump(!empty($e->errorInfo) && is_array($e->errorInfo));\n    }\n});\n?>\n--EXPECT--\nbool(true)\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/coroutine.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: PDO SQLITE coroutine\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO('sqlite::memory:');\n    $db->exec('create table test (id int)');\n    for($i = 0; $i <= 20; $i++) {\n        go(function() use ($i, $db) {\n            $stmt = $db->prepare('insert into test values(?)');\n            $stmt->execute([$i]);\n            $stmt = $db->prepare('select id from test where id = ?');\n            $stmt->execute([$i]);\n            var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == $i);\n        });\n    }\n});\n?>\n--EXPECT--\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/coroutine2.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: PDO SQLITE coroutine\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\n$db = new PDO('sqlite:test.db');\n$db->exec('create table if not exists test (id int)');\n$db->exec('delete from test');\n\nrun(function() {\n    for($i = 0; $i <= 20; $i++) {\n        go(function() use ($i) {\n            $db = new PDO('sqlite:test.db');\n            $stmt = $db->prepare('insert into test values(?)');\n            $stmt->execute([$i]);\n            $stmt = $db->prepare('select id from test where id = ?');\n            $stmt->execute([$i]);\n            var_dump($stmt->fetch(PDO::FETCH_ASSOC)['id'] == $i);\n        });\n    }\n});\n?>\n--EXPECT--\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/debugdumpparams_001.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:debugDumpParams() with bound params\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO('sqlite::memory:');\n\n    $x= $db->prepare('select :a, :b, ?');\n    $x->bindValue(':a', 1, PDO::PARAM_INT);\n    $x->bindValue(':b', 'foo');\n    $x->bindValue(3, 1313);\n    var_dump($x->debugDumpParams());\n});\n?>\n--EXPECT--\nSQL: [16] select :a, :b, ?\nParams:  3\nKey: Name: [2] :a\nparamno=-1\nname=[2] \":a\"\nis_param=1\nparam_type=1\nKey: Name: [2] :b\nparamno=-1\nname=[2] \":b\"\nis_param=1\nparam_type=2\nKey: Position #2:\nparamno=2\nname=[0] \"\"\nis_param=1\nparam_type=2\nNULL\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/gh9032.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: SQLite3 authorizer crashes on NULL values\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--INI--\nopen_basedir=.\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO(\"sqlite::memory:\", null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);\n\n    $db->exec('attach database \\':memory:\\' AS \"db1\"');\n    var_dump($db->exec('create table db1.r (id int)'));\n\n    try {\n        $st = $db->prepare('attach database :a AS \"db2\"');\n        $st->execute([':a' => ':memory:']);\n        var_dump($db->exec('create table db2.r (id int)'));\n    } catch (PDOException $ex) {\n        echo $ex->getMessage(), PHP_EOL;\n    }\n});\n?>\n--EXPECT--\nint(0)\nint(0)\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/open_basedir.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: PDO SQLite open_basedir check\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID < 80100) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.1 or higher');\n}\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--INI--\nopen_basedir=.\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nco::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function () {\n    chdir(__DIR__);\n\n    try {\n        $db = new PDO('sqlite:../not_in_open_basedir.sqlite');\n    } catch (Exception $e) {\n        echo $e->getMessage() . \"\\n\";\n    }\n    try {\n        $db = new PDO('sqlite:file:../not_in_open_basedir.sqlite');\n    } catch (Exception $e) {\n        echo $e->getMessage() . \"\\n\";\n    }\n    try {\n        $db = new PDO('sqlite:file:../not_in_open_basedir.sqlite?mode=ro');\n    } catch (Exception $e) {\n        echo $e->getMessage() . \"\\n\";\n    }\n});\n?>\n--EXPECT--\nopen_basedir prohibits opening ../not_in_open_basedir.sqlite\nopen_basedir prohibits opening file:../not_in_open_basedir.sqlite\nopen_basedir prohibits opening file:../not_in_open_basedir.sqlite?mode=ro\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/pdo_035.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: PDORow + get_parent_class()\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO('sqlite::memory:');\n    $db->exec('CREATE TABLE test (id int)');\n    $db->exec('INSERT INTO test VALUES (23)');\n\n    $stmt = $db->prepare('SELECT id FROM test');\n    $stmt->execute();\n    $result = $stmt->fetch(PDO::FETCH_LAZY);\n\n    echo get_class($result), \"\\n\";\n    var_dump(get_parent_class($result));\n\n    try {\n        $result->foo = 1;\n    } catch (Error $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        $result[0] = 1;\n    } catch (Error $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        unset($result->foo);\n    } catch (Error $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    try {\n        unset($result[0]);\n    } catch (Error $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n});\n?>\n--EXPECT--\nPDORow\nbool(false)\nCannot write to PDORow property\nCannot write to PDORow offset\nCannot unset PDORow property\nCannot unset PDORow offset\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/pdo_fetch_func_001.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:FETCH_FUNC\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID >= 80200) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.1 or lower');\n}\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO('sqlite::memory:');\n    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);\n\n    $db->exec('CREATE TABLE testing (id INTEGER , name VARCHAR)');\n    $db->exec('INSERT INTO testing VALUES(1, \"php\")');\n    $db->exec('INSERT INTO testing VALUES(2, \"\")');\n\n    $st = $db->query('SELECT * FROM testing');\n    $st->fetchAll(PDO::FETCH_FUNC, function($x, $y) use ($st) { var_dump($st); print \"data: $x, $y\\n\"; });\n\n    $st = $db->query('SELECT name FROM testing');\n    var_dump($st->fetchAll(PDO::FETCH_FUNC, 'strtoupper'));\n\n    try {\n        $st = $db->query('SELECT * FROM testing');\n        var_dump($st->fetchAll(PDO::FETCH_FUNC, 'nothing'));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n\n    try {\n        $st = $db->query('SELECT * FROM testing');\n        var_dump($st->fetchAll(PDO::FETCH_FUNC, ''));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n\n    try {\n        $st = $db->query('SELECT * FROM testing');\n        var_dump($st->fetchAll(PDO::FETCH_FUNC, NULL));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n\n    try {\n        $st = $db->query('SELECT * FROM testing');\n        var_dump($st->fetchAll(PDO::FETCH_FUNC, 1));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n\n    try {\n        $st = $db->query('SELECT * FROM testing');\n        var_dump($st->fetchAll(PDO::FETCH_FUNC, array('self', 'foo')));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n\n    class foo {\n        public function method($x) {\n            return \"--- $x ---\";\n        }\n    }\n    class bar extends foo {\n        public function __construct($db) {\n            $st = $db->query('SELECT * FROM testing');\n            var_dump($st->fetchAll(PDO::FETCH_FUNC, array($this, 'parent::method')));\n        }\n\n        static public function test($x, $y) {\n            return $x .'---'. $y;\n        }\n\n        private function test2($x, $y) {\n            return $x;\n        }\n\n        public function test3($x, $y) {\n            return $x .'==='. $y;\n        }\n    }\n\n    new bar($db);\n\n    $st = $db->query('SELECT * FROM testing');\n    var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test')));\n\n    try {\n        $st = $db->query('SELECT * FROM testing');\n        var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test2')));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n\n    try {\n        $st = $db->query('SELECT * FROM testing');\n        var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test3')));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n\n    try {\n        $st = $db->query('SELECT * FROM testing');\n        var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'inexistent')));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n});\n?>\n--EXPECTF--\nobject(PDOStatement)#%d (1) {\n  [\"queryString\"]=>\n  string(21) \"SELECT * FROM testing\"\n}\ndata: 1, php\nobject(PDOStatement)#%d (1) {\n  [\"queryString\"]=>\n  string(21) \"SELECT * FROM testing\"\n}\ndata: 2, \narray(2) {\n  [0]=>\n  string(3) \"PHP\"\n  [1]=>\n  string(0) \"\"\n}\nfunction \"nothing\" not found or invalid function name\nfunction \"\" not found or invalid function name\nPDOStatement::fetchAll(): Argument #2 must be a callable, null given\nno array or string given\ncannot access \"self\" when no class scope is active\narray(2) {\n  [0]=>\n  string(9) \"--- 1 ---\"\n  [1]=>\n  string(9) \"--- 2 ---\"\n}\narray(2) {\n  [0]=>\n  string(7) \"1---php\"\n  [1]=>\n  string(4) \"2---\"\n}\nnon-static method bar::test2() cannot be called statically\nnon-static method bar::test3() cannot be called statically\nclass bar does not have a method \"inexistent\"\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/pdo_fetch_func_001_1.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:FETCH_FUNC\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID < 80200) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.2 or higher');\n}\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO('sqlite::memory:');\n    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);\n\n    $db->exec('CREATE TABLE testing (id INTEGER , name VARCHAR)');\n    $db->exec('INSERT INTO testing VALUES(1, \"php\")');\n    $db->exec('INSERT INTO testing VALUES(2, \"\")');\n\n    $st = $db->query('SELECT * FROM testing');\n    $st->fetchAll(PDO::FETCH_FUNC, function($x, $y) use ($st) { var_dump($st); print \"data: $x, $y\\n\"; });\n\n    $st = $db->query('SELECT name FROM testing');\n    var_dump($st->fetchAll(PDO::FETCH_FUNC, 'strtoupper'));\n\n    try {\n        $st = $db->query('SELECT * FROM testing');\n        var_dump($st->fetchAll(PDO::FETCH_FUNC, 'nothing'));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n\n    try {\n        $st = $db->query('SELECT * FROM testing');\n        var_dump($st->fetchAll(PDO::FETCH_FUNC, ''));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n\n    try {\n        $st = $db->query('SELECT * FROM testing');\n        var_dump($st->fetchAll(PDO::FETCH_FUNC, NULL));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n\n    try {\n        $st = $db->query('SELECT * FROM testing');\n        var_dump($st->fetchAll(PDO::FETCH_FUNC, 1));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n\n    try {\n        $st = $db->query('SELECT * FROM testing');\n        var_dump($st->fetchAll(PDO::FETCH_FUNC, array('self', 'foo')));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n\n    class foo {\n        public function method($x) {\n            return \"--- $x ---\";\n        }\n    }\n    class bar extends foo {\n        public function __construct($db) {\n            $st = $db->query('SELECT * FROM testing');\n            var_dump($st->fetchAll(PDO::FETCH_FUNC, array($this, 'parent::method')));\n        }\n\n        static public function test($x, $y) {\n            return $x .'---'. $y;\n        }\n\n        private function test2($x, $y) {\n            return $x;\n        }\n\n        public function test3($x, $y) {\n            return $x .'==='. $y;\n        }\n    }\n\n    new bar($db);\n\n    $st = $db->query('SELECT * FROM testing');\n    var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test')));\n\n    try {\n        $st = $db->query('SELECT * FROM testing');\n        var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test2')));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n\n    try {\n        $st = $db->query('SELECT * FROM testing');\n        var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test3')));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n\n    try {\n        $st = $db->query('SELECT * FROM testing');\n        var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'inexistent')));\n    } catch (\\TypeError $e) {\n        echo $e->getMessage(), \\PHP_EOL;\n    }\n});\n?>\n--EXPECTF--\nobject(PDOStatement)#%d (1) {\n  [\"queryString\"]=>\n  string(21) \"SELECT * FROM testing\"\n}\ndata: 1, php\nobject(PDOStatement)#%d (1) {\n  [\"queryString\"]=>\n  string(21) \"SELECT * FROM testing\"\n}\ndata: 2, \narray(2) {\n  [0]=>\n  string(3) \"PHP\"\n  [1]=>\n  string(0) \"\"\n}\nfunction \"nothing\" not found or invalid function name\nfunction \"\" not found or invalid function name\nPDOStatement::fetchAll(): Argument #2 must be a callable, null given\nno array or string given\ncannot access \"self\" when no class scope is active\n\nDeprecated: Callables of the form [\"bar\", \"parent::method\"] are deprecated in %s on line %d\narray(2) {\n  [0]=>\n  string(9) \"--- 1 ---\"\n  [1]=>\n  string(9) \"--- 2 ---\"\n}\narray(2) {\n  [0]=>\n  string(7) \"1---php\"\n  [1]=>\n  string(4) \"2---\"\n}\nnon-static method bar::test2() cannot be called statically\nnon-static method bar::test3() cannot be called statically\nclass bar does not have a method \"inexistent\"\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/pdo_sqlite.inc",
    "content": "<?php\ndeclare(strict_types=1);\n\nclass PdoSqliteTest\n{\n     public static function skip() {\n        try {\n            $db = self::create();\n        } catch (PDOException $e) {\n            die(\"skip \" . $e->getMessage());\n        }\n     }\n\n    public static function create(): PDO\n    {\n        return new PDO(SQLITE_DSN);\n    }\n}\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/pdo_sqlite_extendederror_attr.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: Testing PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    echo \"Creating new PDO\" . PHP_EOL;\n    $db = new PDO('sqlite::memory:');\n    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);\n\n    $db->exec(\"CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )\");\n\n    echo \"Inserting first time which should succeed\" . PHP_EOL;\n    $db->exec(\"INSERT INTO dog VALUES (1, 'Annoying Dog', 1)\");\n    $errorInfo = $db->errorInfo();\n    echo sprintf(\"First Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)\", $errorInfo[0], $errorInfo[1]) . PHP_EOL;\n\n    echo \"Inserting second time which should fail\" . PHP_EOL;\n    $result = $db->exec(\"INSERT INTO dog VALUES (1, 'Annoying Dog', 1)\");\n    $errorInfo = $db->errorInfo();\n    echo sprintf(\"Second Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)\", $errorInfo[0], $errorInfo[1]) . PHP_EOL;\n\n\n    echo \"Creating new PDO with Extended Result Codes turned on\" . PHP_EOL;\n    $const = PHP_VERSION_ID >= 80500 ? PDO\\SQLITE::ATTR_EXTENDED_RESULT_CODES : PDO::SQLITE_ATTR_EXTENDED_RESULT_CODES;\n    $db = new PDO('sqlite::memory:', '', '', [$const => TRUE]);\n    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);\n\n    $db->exec(\"CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )\");\n\n    echo \"Inserting first time which should succeed\" . PHP_EOL;\n    $result = $db->exec(\"INSERT INTO dog VALUES (1, 'Annoying Dog', 1)\");\n    $errorInfo = $db->errorInfo();\n    echo sprintf(\"First (Extended) Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)\", $errorInfo[0], $errorInfo[1]) . PHP_EOL;\n\n    echo \"Inserting second time which should fail\" . PHP_EOL;\n    $result = $db->exec(\"INSERT INTO dog VALUES (1, 'Annoying Dog', 1)\");\n    $errorInfo = $db->errorInfo();\n    echo sprintf(\"Second (Extended) Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)\", $errorInfo[0], $errorInfo[1]) . PHP_EOL;\n});\n?>\n--EXPECT--\nCreating new PDO\nInserting first time which should succeed\nFirst Error Info: SQLSTATE Error Code: (00000), Driver Specific Error Code: ()\nInserting second time which should fail\nSecond Error Info: SQLSTATE Error Code: (23000), Driver Specific Error Code: (19)\nCreating new PDO with Extended Result Codes turned on\nInserting first time which should succeed\nFirst (Extended) Error Info: SQLSTATE Error Code: (00000), Driver Specific Error Code: ()\nInserting second time which should fail\nSecond (Extended) Error Info: SQLSTATE Error Code: (HY000), Driver Specific Error Code: (1555)\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/pdo_sqlite_filename_uri.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: Testing filename uri\n--SKIPIF--\n<?php\nif (PHP_VERSION_ID < 80100) {\n    require __DIR__ . '/../include/skipif.inc';\n    skip('php version 8.1 or higher');\n}\n\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    // create with default read-write|create mode\n    $filename = \"file:\" . __DIR__ . DIRECTORY_SEPARATOR . \"pdo_sqlite_filename_uri.db\";\n\n    $db = new PDO('sqlite:' . $filename);\n\n    var_dump($db->exec('CREATE TABLE test1 (id INT);'));\n\n    // create with readonly mode\n    $filename = \"file:\" . __DIR__ . DIRECTORY_SEPARATOR . \"pdo_sqlite_filename_uri.db?mode=ro\";\n\n    $db = new PDO('sqlite:' . $filename);\n\n    var_dump($db->exec('CREATE TABLE test2 (id INT);'));\n});\n?>\n--CLEAN--\n<?php\n$filename = __DIR__ . DIRECTORY_SEPARATOR . \"pdo_sqlite_filename_uri.db\";\nif (file_exists($filename)) {\n    unlink($filename);\n}\n?>\n--EXPECTF--\nint(0)\n\nFatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 8 attempt to write a readonly database in %s\nStack trace:\n%s\n%A\n  thrown in %s\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/pdo_sqlite_get_attribute.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: Testing getAttribute()\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $pdo = new PDO('sqlite::memory:');\n    var_dump($pdo->getAttribute(PDO::ATTR_SERVER_VERSION));\n    var_dump($pdo->getAttribute(PDO::ATTR_CLIENT_VERSION));\n});\n?>\n--EXPECTF--\nstring(%d) \"%s\"\nstring(%d) \"%s\"\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/pdo_sqlite_lastinsertid.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: Testing lastInsertId()\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO('sqlite::memory:');\n    $db->query('CREATE TABLE IF NOT EXISTS foo (id INT AUTO INCREMENT, name TEXT)');\n    $db->query('INSERT INTO foo VALUES (NULL, \"PHP\")');\n    $db->query('INSERT INTO foo VALUES (NULL, \"PHP6\")');\n    var_dump($db->query('SELECT * FROM foo'));\n    var_dump($db->errorInfo());\n    var_dump($db->lastInsertId());\n\n    $db->query('DROP TABLE foo');\n});\n?>\n--EXPECTF--\nobject(PDOStatement)#%d (1) {\n  [\"queryString\"]=>\n  string(17) \"SELECT * FROM foo\"\n}\narray(3) {\n  [0]=>\n  string(5) \"00000\"\n  [1]=>\n  NULL\n  [2]=>\n  NULL\n}\nstring(1) \"2\"\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/pdo_sqlite_open_flags.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: Testing open flags\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nco::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function () {\n    $filename = __DIR__ . DIRECTORY_SEPARATOR . 'pdo_sqlite_open_flags.db';\n\n    // Default open flag is read-write|create\n    $db = new PDO('sqlite:' . $filename, null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);\n\n    var_dump($db->exec('CREATE TABLE test1 (id INT);'));\n    if (PHP_VERSION_ID >= 80500) {\n        $key   = Pdo\\Sqlite::ATTR_OPEN_FLAGS;\n        $value = Pdo\\SQLITE::OPEN_READONLY;\n    } else {\n        $key   = PDO::SQLITE_ATTR_OPEN_FLAGS;\n        $value = PDO::SQLITE_OPEN_READONLY;\n    }\n    $db = new PDO('sqlite:' . $filename, null, null, [$key => $value, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);\n\n    var_dump($db->exec('CREATE TABLE test2 (id INT);'));\n\n    $db->exec('drop table test1');\n    $db->exec('drop table test2');\n});\n?>\n--CLEAN--\n<?php\n$filename = __DIR__ . DIRECTORY_SEPARATOR . 'pdo_sqlite_open_flags.db';\nif (file_exists($filename)) {\n    unlink($filename);\n}\n?>\n--EXPECTF--\nint(0)\n\nFatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 8 attempt to write a readonly database in %s\nStack trace:\n%s\n%A\n  thrown in %s\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/pdo_sqlite_statement_getattribute.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite:getAttribute()\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO('sqlite::memory:');\n\n    $st = $db->prepare('SELECT 1;');\n\n    $const = PHP_VERSION_ID >= 80500 ? PDO\\SQLITE::ATTR_READONLY_STATEMENT: PDO::SQLITE_ATTR_READONLY_STATEMENT;\n\n    var_dump($st->getAttribute($const));\n\n    $st = $db->prepare('CREATE TABLE test (a TEXT);');\n\n    var_dump($st->getAttribute($const));\n});\n?>\n--EXPECT--\nbool(true)\nbool(false)\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/pdo_sqlite_tostring_exception.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: __toString() exception during PDO Sqlite parameter binding\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nclass throws {\n    function __toString() {\n        throw new Exception(\"Sorry\");\n    }\n}\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO('sqlite::memory:');\n    $db->exec('CREATE TABLE t(id int, v varchar(255))');\n\n    $stmt = $db->prepare('INSERT INTO t VALUES(:i, :v)');\n    $param1 = 1234;\n    $stmt->bindValue('i', $param1);\n    $param2 = \"foo\";\n    $stmt->bindParam('v', $param2);\n\n    $param2 = new throws;\n\n    try {\n        $stmt->execute();\n    } catch (Exception $e) {\n        echo \"Exception thrown ...\\n\";\n    }\n\n    try {\n        $stmt->execute();\n    } catch (Exception $e) {\n        echo \"Exception thrown ...\\n\";\n    }\n\n    $query = $db->query(\"SELECT * FROM t\");\n    while ($row = $query->fetch(PDO::FETCH_ASSOC)) {\n        print_r($row);\n    }\n});\n?>\n--EXPECT--\nException thrown ...\nException thrown ...\n"
  },
  {
    "path": "tests/swoole_pdo_sqlite/pdo_sqlite_transaction.phpt",
    "content": "--TEST--\nswoole_pdo_sqlite: Testing transaction\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/pdo_sqlite.inc';\nPdoSqliteTest::skip();\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['hook_flags'=> SWOOLE_HOOK_PDO_SQLITE]);\nrun(function() {\n    $db = new PDO('sqlite::memory:');\n    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);\n\n    $db->beginTransaction();\n\n    $db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)');\n    $db->commit();\n\n    $db->beginTransaction();\n    $db->query('INSERT INTO foobar VALUES (NULL, \"PHP\")');\n    $db->query('INSERT INTO foobar VALUES (NULL, \"PHP6\")');\n    $db->rollback();\n\n    $r = $db->query('SELECT COUNT(*) FROM foobar');\n    var_dump($r->rowCount());\n\n\n    $db->query('DROP TABLE foobar');\n});\n?>\n--EXPECTF--\nint(0)\n\nWarning: PDO::query(): SQLSTATE[HY000]: General error: 6 database table is locked in %s on line %d\n"
  },
  {
    "path": "tests/swoole_process/alarm.phpt",
    "content": "--TEST--\nswoole_process: alarm\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process;\n\nProcess::signal(SIGALRM, function () {\n    static $i = 0;\n    echo \"#{$i}\\talarm\\n\";\n    $i++;\n    if ($i > 10) {\n        Process::alarm(-1);\n        Process::signal(SIGALRM, null);\n        Swoole\\Event::del(STDIN);\n        Swoole\\Event::exit();\n    }\n});\n\n//100ms\nProcess::alarm(10 * 1000);\n\n//never calback\nSwoole\\Event::add(STDIN, function () {});\n\nSwoole\\Event::wait();\n\n?>\n--EXPECT--\n#0\talarm\n#1\talarm\n#2\talarm\n#3\talarm\n#4\talarm\n#5\talarm\n#6\talarm\n#7\talarm\n#8\talarm\n#9\talarm\n#10\talarm\n"
  },
  {
    "path": "tests/swoole_process/close.phpt",
    "content": "--TEST--\nswoole_process: close\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n//$proc = new Swoole\\Process(swoole_function(Swoole\\Process $proc) {\n//    $proc->close();\n//});\n//$proc->start();\n//\n//$proc = new Swoole\\Process(swoole_function(Swoole\\Process $proc) {\n//    usleep(200000);\n//    // Assert::true(false, 'never here');\n//});\n//$proc->start();\n//$proc->close();\n//\n//\n//\\Swoole\\Process::wait(true);\n//\\Swoole\\Process::wait(true);\necho \"SUCCESS\";\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_process/coro/ipc.phpt",
    "content": "--TEST--\nswoole_process/coro: ipc with coroutine\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$proc1 = new Swoole\\Process(function (Swoole\\Process $proc) {\n    $socket = $proc->exportSocket();\n    echo $socket->recv();\n    $socket->send(\"hello proc2\\n\");\n    echo \"proc1 stop\\n\";\n}, false, 1, true);\n\nAssert::assert($proc1->start());\n\n$proc2 = new Swoole\\Process(function (Swoole\\Process $proc) use ($proc1) {\n    Co::sleep(0.01);\n    $socket = $proc1->exportSocket();\n    $socket->send(\"hello proc1\\n\");\n    echo $socket->recv();\n    echo \"proc2 stop\\n\";\n}, false, 0, true);\n\nAssert::assert($proc2->start());\n\nSwoole\\Process::wait(true);\nSwoole\\Process::wait(true);\n\n?>\n--EXPECT--\nhello proc1\nproc1 stop\nhello proc2\nproc2 stop\n"
  },
  {
    "path": "tests/swoole_process/coro/set_protocol.phpt",
    "content": "--TEST--\nswoole_process/coro: ipc with coroutine\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Process;\n\nconst N = MAX_REQUESTS_LOW;\n\n$proc1 = new Process(function (Process $proc) {\n    $socket = $proc->exportSocket();\n    $socket->setProtocol([\n        'open_length_check' => true,\n        'package_length_type' => 'n',\n        'package_length_offset' => 0,\n        'package_body_offset' => 2,\n    ]);\n\n    while ($data = $socket->recvPacket()) {\n        if (strlen($data) == 2) {\n            echo \"END\\n\";\n            return;\n        }\n        Assert::lengthBetween($data, 1024, 61000);\n    }\n    echo \"ERROR\\n\";\n}, false, 1, true);\n\nAssert::assert($proc1->start());\n\n$n = N;\nwhile ($n--) {\n    $len = rand(1024, 60000);\n    $pkg = pack('n', $len) . random_bytes($len);\n    $proc1->write($pkg);\n}\n$proc1->write(pack('n', 0));\nSwoole\\Process::wait(true);\n?>\n--EXPECT--\nEND\n"
  },
  {
    "path": "tests/swoole_process/coro/signal.phpt",
    "content": "--TEST--\nswoole_process/coro: signal with coroutine\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Process;\nuse Swoole\\Runtime;\nuse Swoole\\Event;\n\n$process = new Process(function ($process) {\n    Co::sleep(.2);\n}, false, SOCK_DGRAM, true);\n\n$process->start();\n\nProcess::signal(SIGCHLD, function ($sig) {\n    while ($ret = Process::wait(false)) {\n        echo \"PID={$ret['pid']}\\n\";\n    }\n});\n\ngo(function () {\n    Co::sleep(.3);\n    echo \"END\\n\";\n});\n\nEvent::wait();\n\n?>\n--EXPECTF--\nPID=%d\nEND\n"
  },
  {
    "path": "tests/swoole_process/coro/start.phpt",
    "content": "--TEST--\nswoole_process/coro: start with coroutine\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$proc = new Swoole\\Process(function () {\n    co::sleep(0.2);\n    echo \"SUCCESS\\n\";\n}, false, 1, true);\n\n$r = $proc->start();\nAssert::assert($r > 0);\n$proc->close();\n\n\\Swoole\\Process::wait(true);\n\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_process/ctor.phpt",
    "content": "--TEST--\nswoole_process: ctor\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$proc = new Swoole\\Process(function() {\n    Assert::true(false, 'never here');\n});\nunset($proc);\necho \"SUCCESS\";\n\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_process/daemon.phpt",
    "content": "--TEST--\nswoole_process: daemon\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse  Swoole\\Process;\n\n$sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);\n\n$process = new Process(function (Swoole\\Process $worker) use ($sockets) {\n    fclose($sockets[1]);\n    Process::daemon(1, 1, [null, $sockets[0], $sockets[0]]);\n\n    fwrite(STDOUT, \"ERROR 1\\n\");\n    fwrite(STDOUT, \"ERROR 2\\n\");\n    fwrite(STDOUT, \"ERROR 3\\n\");\n\n    fwrite(STDERR, \"ERROR 4\\n\");\n    fwrite(STDERR, \"ERROR 5\\n\");\n    fwrite(STDERR, \"END\\n\");\n}, true);\n$pid = $process->start();\n\nProcess::wait();\nfclose($sockets[0]);\n\nwhile (true) {\n    $fp = $sockets[1];\n    $line = fgets($fp);\n    if (empty($line)) {\n        break;\n    } else {\n        echo $line;\n        if ($line == \"END\\n\") {\n            break;\n        }\n    }\n}\n\n?>\n--EXPECT--\nERROR 1\nERROR 2\nERROR 3\nERROR 4\nERROR 5\nEND\n"
  },
  {
    "path": "tests/swoole_process/deamon.phpt",
    "content": "--TEST--\nswoole_process: deamon\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$proc = new Swoole\\Process(function(Swoole\\Process $proc) {\n    $r = \\Swoole\\Process::daemon();\n    Assert::assert($r);\n\n    $proc->push(posix_getpid());\n});\n$proc->useQueue();\n$forkPid = $proc->start();\n$demonPid = intval($proc->pop());\n\nAssert::assert($forkPid !== $demonPid);\n\n\\Swoole\\Process::kill($demonPid, SIGKILL);\n\n\\Swoole\\Process::wait(true);\n\\Swoole\\Process::wait(true);\necho \"SUCCESS\";\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_process/echo.py",
    "content": "import sys\n\ndef main():\n\ts = raw_input()\n\tprint \"Python: \" + s\n\nmain()\n"
  },
  {
    "path": "tests/swoole_process/enable_coroutine.phpt",
    "content": "--TEST--\nswoole_process: push\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$process = new Swoole\\Process(function(Swoole\\Process $worker) {\n    echo Co::getCid() . PHP_EOL;\n}, false, false, true);\n$process->start();\n$process::wait();\n\n$process = new Swoole\\Process(function(Swoole\\Process $worker) {\n    echo Co::getCid() . PHP_EOL;\n}, false, false, false);\n$process->set(['enable_coroutine' => true]);\n$process->start();\n$process::wait();\n\n?>\n--EXPECT--\n1\n1\n"
  },
  {
    "path": "tests/swoole_process/exception.phpt",
    "content": "--TEST--\nswoole_process: exception\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nabstract class AbstractProcess\n{\n    public abstract function run();\n\n    public abstract function onException(Throwable $e);\n\n    public function start()\n    {\n        $process = new Swoole\\Process(function (Swoole\\Process $process) {\n            Swoole\\Event::add($process->pipe, function (Swoole\\Process $process) { });\n            try {\n                $this->run();\n            } catch (Throwable $e) {\n                $this->onException($e);\n            }\n        });\n        $process->start();\n    }\n}\n\nclass Process6 extends AbstractProcess\n{\n    public function run()\n    {\n        AAAA();\n    }\n\n    public function onException(Throwable $e)\n    {\n        throw $e;\n    }\n}\n\n(new Process6())->start();\n\n?>\n--EXPECTF--\nFatal error: Uncaught Error: Call to undefined function AAAA() in %s:%d\nStack trace:\n#0 %s(%d): Process6->run()\n#1 [internal function]: AbstractProcess->{closure%S}(Object(Swoole\\Process))\n#2 %s(%d): Swoole\\Process->start()\n#3 %s(%d): AbstractProcess->start()\n#4 {main}\n  thrown in %s on line %d\n"
  },
  {
    "path": "tests/swoole_process/exec.phpt",
    "content": "--TEST--\nswoole_process: exec\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$proc = new Swoole\\Process(function(Swoole\\Process $proc) {\n    $proc->exec(\"/usr/bin/printf\", [\"HELLO\"]);\n}, true);\n$proc->start();\necho $proc->read();\n$proc->exec(\"/usr/bin/printf\", [\" WORLD\"]);\n\n\\Swoole\\Process::wait(true);\n?>\n--EXPECT--\nHELLO WORLD\n"
  },
  {
    "path": "tests/swoole_process/exit.phpt",
    "content": "--TEST--\nswoole_process: exit\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$proc = new Swoole\\Process(function(Swoole\\Process $proc) {\n    $proc->exit(9);\n});\n$pid = $proc->start();\n\n$i = \\Swoole\\Process::wait(true);\nAssert::same($i[\"code\"], 9);\n\necho \"SUCCESS\";\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_process/freeQueue.phpt",
    "content": "--TEST--\nswoole_process: freeQueue\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$proc = new Swoole\\Process(function() {});\n$r  = $proc->useQueue();\nAssert::assert($r);\n\n$proc->start();\n$r  = $proc->freeQueue();\nAssert::assert($r);\n\n\\Swoole\\Process::wait();\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_process/getaffinity.phpt",
    "content": "--TEST--\nswoole_process: getAffinity\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_process_affinity();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process;\n$r = Process::getAffinity();\nAssert::isArray($r);\nAssert::eq(count($r), swoole_cpu_num());\n\nAssert::assert(Process::setAffinity([0, 1]));\nAssert::eq(Process::getAffinity(), [0, 1]);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_process/ignore_sigpipe.phpt",
    "content": "--TEST--\nswoole_process: ignore SIGPIPE\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Co\\Client;\nuse Swoole\\Constant;\nuse Swoole\\Server;\nuse SwooleTest\\ProcessManager;\n\n$GLOBALS['data'] = base64_encode(random_bytes(128));\n\n$pm = new ProcessManager();\n$pm->setWaitTimeout(5);\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'hook_flags' => SWOOLE_HOOK_ALL,\n        'log_level' => SWOOLE_LOG_ERROR,\n    ]);\n    $serv->on('WorkerStart', function (Server $serv) use ($pm) {\n        $cli = new Client(SWOOLE_SOCK_TCP);\n        if ($cli->connect('127.0.0.1', $pm->getFreePort(), 1) == false) {\n            echo \"ERROR\\n\";\n            return;\n        }\n        while (1) {\n            usleep(10000);\n            if ($cli->send($GLOBALS['data']) == false) {\n                Assert::eq($cli->errCode, SOCKET_EPIPE);\n                break;\n            }\n        }\n        $pm->wakeup();\n    });\n\n    $serv->on(Constant::EVENT_CONNECT, function (Server $serv, $fd, $rid) {\n        $serv->close($fd);\n    });\n    $serv->on(Constant::EVENT_RECEIVE, function (Server $serv, $fd, $rid, $data) {});\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_process/ignore_sigpipe_2.phpt",
    "content": "--TEST--\nswoole_process: close\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Constant;\nuse Swoole\\Timer;\nuse Swoole\\Process;\nuse Swoole\\Atomic;\n\n$GLOBALS['data'] = base64_encode(random_bytes(128));\n$GLOBALS['atomic'] = new Atomic(0);\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->setWaitTimeout(5);\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n        if ($cli->connect('127.0.0.1', $pm->getFreePort(), 1) == false) {\n            echo \"ERROR\\n\";\n            return;\n        }\n        while (1) {\n            usleep(10000);\n            if ($cli->send($GLOBALS['data']) == false) {\n                Assert::eq($cli->errCode, SOCKET_EPIPE);\n                break;\n            }\n        }\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        \"worker_num\" => 1,\n        'hook_flags' => SWOOLE_HOOK_ALL,\n        'log_level' => SWOOLE_LOG_WARNING,\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $serv->on(Constant::EVENT_CONNECT, function (Server $serv, $fd, $rid) {\n        $serv->close($fd);\n    });\n    $serv->on(Constant::EVENT_RECEIVE, function (Server $serv, $fd, $rid, $data) {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_process/kill.phpt",
    "content": "--TEST--\nswoole_process: kill\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$proc = new Swoole\\Process(function() {\n    sleep(PHP_INT_MAX);\n});\n$pid = $proc->start();\nSwoole\\Process::kill($pid, SIGKILL);\n$i = \\Swoole\\Process::wait(true);\nAssert::same($i[\"signal\"], SIGKILL);\necho \"SUCCESS\";\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_process/msgq_capacity.phpt",
    "content": "--TEST--\nswoole_process: sysv msgqueue capacity\n--SKIPIF--\n<?php\nrequire __DIR__.'/../include/skipif.inc';\nskip_if_darwin();\n?>\n--FILE--\n<?php\nrequire __DIR__.'/../include/bootstrap.php';\n\n\nfunction callback_function(Swoole\\Process $worker)\n{\n}\n\n$process = new Swoole\\Process('callback_function', false, false);\n$process->useQueue(ftok(__DIR__, 1), 1, 1024 * 1024 * 64);\n\nconst N = 32 * 1024 * 1024;\n\n\n$bytes = 0;\nwhile ($bytes < N) {\n    $data = RandStr::getBytes(rand(4000, 8000));\n    $bytes += strlen($data);\n    $process->push($data);\n}\n\nAssert::assert($process->statQueue()['queue_bytes'] > N);\n\n$rd_bytes = 0;\nwhile ($rd_bytes < N) {\n    $recv = $process->pop();\n    $rd_bytes += strlen($recv);\n}\n\nAssert::same($process->statQueue()['queue_bytes'], 0);\n\n$process->freeQueue();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_process/name.phpt",
    "content": "--TEST--\nswoole_process: name\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_darwin();\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$name = \"SWOOLE_PROCESS_TEST_\" . rand(1, 100);\n\n$proc = new Swoole\\Process(function ($childProc) {\n    global $name;\n    $childProc->name($name);\n    sleep(PHP_INT_MAX);\n});\n\n$pid = $proc->start();\n$count = (int)trim(shell_exec(\"ps aux|grep $name|grep -v grep|wc -l\"));\nAssert::same($count, 1);\n\\Swoole\\Process::kill($pid, SIGKILL);\n\n\\Swoole\\Process::wait(true);\necho \"SUCCESS\";\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_process/null_callback.phpt",
    "content": "--TEST--\nswoole_process: null callback\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$process = new Swoole\\Process(function () { });\narray_walk($process, function (&$value) {\n    $value = null;\n});\n$process->start();\n\n?>\n--EXPECTF--\nWarning: Swoole\\Process::start(): illegal callback function in %s\n"
  },
  {
    "path": "tests/swoole_process/pop.phpt",
    "content": "--TEST--\nswoole_process: pop\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n// TODO 难道 queue不应该做成一个独立的组件，放在proc对象上啥意思\n$proc = new Swoole\\Process(function() { });\n$proc->useQueue();\n$proc->push(\"SUCCESS\");\necho $proc->pop();\n$proc->freeQueue();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_process/priority.phpt",
    "content": "--TEST--\nswoole_process: priority\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process;\n\nconst PRIORITY = 12;\n\n$process = new Process(function(Process $worker) {\n    $worker->setPriority(PRIO_PROCESS, PRIORITY);\n    $priority =  $worker->getPriority(PRIO_PROCESS);\n    Assert::eq($priority, PRIORITY);\n    usleep(20000);\n    $worker->exit(0);\n}, false, false);\n\n$pid = $process->start();\nProcess::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_process/priority_error.phpt",
    "content": "--TEST--\nswoole_process: priority [2]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process;\n\nconst PRIORITY = 12;\n\n$process = new Process(function(Process $worker) {\n\n}, false, false);\n\nAssert::eq($process->getPriority(-1000, posix_getpid()), false);\nAssert::eq(swoole_last_error(), SOCKET_EINVAL);\n\nAssert::eq($process->setPriority(-1000, posix_getpid(), PRIORITY), false);\nAssert::eq(swoole_last_error(), SOCKET_EINVAL);\n\nAssert::eq(@$process->getPriority(PRIO_USER, null), false);\nAssert::eq(swoole_last_error(), SWOOLE_ERROR_INVALID_PARAMS);\n\nAssert::eq(@$process->setPriority(PRIO_USER, PRIORITY, null), false);\nAssert::eq(swoole_last_error(), SWOOLE_ERROR_INVALID_PARAMS);\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_process/process_exec.phpt",
    "content": "--TEST--\nswoole_process: exec\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nif (!@file_exists('/usr/bin/python')) {\n    exit('skip if no python');\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$process = new Swoole\\Process('python_process', true);\n$pid = $process->start();\n\nfunction python_process(Swoole\\Process $worker)\n{\n    $worker->exec('/usr/bin/python', array(__DIR__ . \"/echo.py\"));\n}\n\n$process->write(\"Hello World\\n\");\necho $process->read();\n?>\nDone\n--EXPECTREGEX--\nPython: Hello World\nDone.*\n"
  },
  {
    "path": "tests/swoole_process/process_id.phpt",
    "content": "--TEST--\nswoole_process: Github bug #5825\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process;\nfor ($n = 1; $n <= 3; $n++) {\n    $process = new Process(function ($process) use ($n) {\n        Assert::same($process->id, $n);\n    });\n    Assert::same($process->id, $n);\n    $process->start();\n}\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_process/process_msgqueue.phpt",
    "content": "--TEST--\nswoole_process: sysv msgqueue\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nfunction callback_function(Swoole\\Process $worker){}\n\n$process = new Swoole\\Process('callback_function', false, false);\n$process->useQueue();\n\n$bytes = 0;\nforeach(range(1, 10) as $i)\n{\n    $data = \"hello worker[$i]\";\n    $bytes += strlen($data);\n    $process->push($data);\n}\n\n$queue = $process->statQueue();\n($queue['queue_num'] == 10 && $queue['queue_bytes'] == $bytes)\n    && $output = \"Success\\n\";\n\necho $output;\n$process->freeQueue();\n?>\nDone\n--EXPECTREGEX--\nSuccess\nDone.*\n"
  },
  {
    "path": "tests/swoole_process/process_push.phpt",
    "content": "--TEST--\nswoole_process: push\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process;\n\n$process = new Process(function(Process $worker) {\n\n  $recv = $worker->pop();\n\n  echo \"$recv\";\n  usleep(20000);\n\n  $worker->exit(0);\n}, false, false);\n\n$process->useQueue();\n$pid = $process->start();\n\n$process->push(\"hello worker\\n\");\nProcess::wait();\n?>\n--EXPECT--\nhello worker\n"
  },
  {
    "path": "tests/swoole_process/process_select.phpt",
    "content": "--TEST--\nswoole_process: select\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$process = new Swoole\\Process(function (Swoole\\Process $worker)\n{\n    $worker->write(\"hello master\\n\");\n    $worker->exit(0);\n}, false);\n\n$pid = $process->start();\n$r = array($process);\n$w = array();\n$e = array();\n$ret = swoole_select($r, $w, $e, 1.0);\necho $process->read();\n?>\nDone\n--EXPECTREGEX--\nhello master\nDone.*\n"
  },
  {
    "path": "tests/swoole_process/push.phpt",
    "content": "--TEST--\nswoole_process: push\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n//$proc = new Swoole\\Process(swoole_function() {});\n//$proc->useQueue();\n//$r = $proc->push(\"\\0\");\n// Assert::false($r);\n// TODO max data ?\n// $r = $proc->push(str_repeat(\"\\0\", 1024 * 1024 * 8));\n// Assert::false($r);\n//$proc->freeQueue();\n\n$proc = new Swoole\\Process(function() {});\n$proc->useQueue();\n$proc->start();\n$r = $proc->push(\"\\0\");\nAssert::true($r);\n$proc->freeQueue();\n\\Swoole\\Process::wait(true);\necho \"SUCCESS\";\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_process/read.phpt",
    "content": "--TEST--\nswoole_process: read\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$proc = new Swoole\\Process(function(Swoole\\Process $process) {\n    $r = $process->write(\"SUCCESS\");\n    Assert::same($r, 7);\n});\n$r = $proc->start();\nAssert::assert($r > 0);\n\nSwoole\\Timer::after(10, function() use($proc) {\n    echo $proc->read();\n    // Swoole\\Event::exit();\n});\n\n\\Swoole\\Process::wait(true);\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_process/redirect.phpt",
    "content": "--TEST--\nswoole_process: redirect\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$proc = new Swoole\\Process(function(Swoole\\Process $proc) {\n    echo \"SUCCESS\";\n}, true);\n\n$proc->start();\n$r = $proc->read();\necho \"READ: $r~\";\n\n\\Swoole\\Process::wait(true);\n?>\n--EXPECT--\nREAD: SUCCESS~\n"
  },
  {
    "path": "tests/swoole_process/setaffinity.phpt",
    "content": "--TEST--\nswoole_process: setAffinity\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_process_affinity();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$r = Swoole\\Process::setaffinity([0]);\nAssert::assert($r);\nif (swoole_cpu_num() > 1) {\n    $r = Swoole\\Process::setaffinity([0, 1]);\n    Assert::assert($r);\n}\necho \"SUCCESS\";\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_process/signal.phpt",
    "content": "--TEST--\nswoole_process: signal\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process;\n\n//父进程中先设置信号\nProcess::signal(SIGCHLD, function ()\n{\n    Process::signal(SIGCHLD, null);\n    Process::signal(SIGTERM, null);\n    Swoole\\Event::del(STDIN);\n    Swoole\\Timer::clearAll();\n    echo \"PARENT WAIT\\n\";\n});\n\n//测试被子进程覆盖信号\nProcess::signal(SIGTERM, function () {\n    //释放信号，否则底层会报内存泄漏\n    Process::signal(SIGTERM, null);\n    echo \"PARENT SIGTERM\\n\";\n    Swoole\\Event::exit();\n});\n\n$pid = (new Process(function ()\n{\n    Process::signal(SIGTERM, function ($sig) {\n        echo \"CHILD SIGTERM\\n\";\n        Process::signal(SIGTERM, function ($sig) {\n            echo \"CHILD EXIT\\n\";\n            Swoole\\Event::del(STDIN);\n        });\n    });\n\n    //never calback\n    Swoole\\Event::add(STDIN, function () {});\n\n}))->start();\n\nSwoole\\Timer::after(500, function() use ($pid) {\n    Process::kill($pid, SIGTERM);\n    Swoole\\Timer::after(500, function() use ($pid) {\n        Process::kill($pid, SIGTERM);\n    });\n});\n\n//never calback\nSwoole\\Event::add(STDIN, function ($fp) {\n    echo fread($fp, 8192);\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nCHILD SIGTERM\nCHILD EXIT\nPARENT WAIT\n"
  },
  {
    "path": "tests/swoole_process/signal_in_manager.phpt",
    "content": "--TEST--\nswoole_process: signal in manager\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Process;\nuse Swoole\\Server;\n\nconst PID_FILE = __DIR__ . '/manager.pid';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    usleep(100000);\n    $manager_pid = file_get_contents(PID_FILE);\n    Process::kill($manager_pid, SIGINT);\n    $pm->wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on('ManagerStart', function (Server $serv) use ($pm) {\n        file_put_contents(PID_FILE, $serv->getManagerPid());\n        Process::signal(SIGINT, function () use ($pm) {\n            echo \"SIGINT triggered\\n\";\n            $pm->wakeup();\n        });\n        $pm->wakeup();\n    });\n    $serv->on('Receive', function (Server $serv, $fd, $reactorId, $data) {\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\nunlink(PID_FILE);\n?>\n--EXPECT--\nSIGINT triggered\n"
  },
  {
    "path": "tests/swoole_process/signal_in_manager_2.phpt",
    "content": "--TEST--\nswoole_process: signal in manager with task worker\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Process;\nuse Swoole\\Server;\n\nconst PID_FILE = __DIR__ . '/manager.pid';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    usleep(100000);\n    $manager_pid = file_get_contents(PID_FILE);\n    Process::kill($manager_pid, SIGINT);\n    $pm->wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'task_worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on('ManagerStart', function (Server $serv) use ($pm) {\n        file_put_contents(PID_FILE, $serv->getManagerPid());\n        Process::signal(SIGINT, function () use ($pm) {\n            echo \"SIGINT triggered\\n\";\n            $pm->wakeup();\n        });\n        $pm->wakeup();\n    });\n    $serv->on('Task', function ($server, $taskId, $workerId, $data) {\n    });\n    $serv->on('Receive', function (Server $serv, $fd, $reactorId, $data) {\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\nunlink(PID_FILE);\n?>\n--EXPECT--\nSIGINT triggered\n"
  },
  {
    "path": "tests/swoole_process/signal_in_manager_3.phpt",
    "content": "--TEST--\nswoole_process: signal in manager with task worker - 2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Process;\nuse Swoole\\Server;\n\nconst PID_FILE = __DIR__ . '/manager.pid';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    usleep(100000);\n    $manager_pid = file_get_contents(PID_FILE);\n    Process::kill($manager_pid, SIGINT);\n    $pm->wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'worker_num' => 2,\n        'task_worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on('ManagerStart', function (Server $serv) use ($pm) {\n        file_put_contents(PID_FILE, $serv->getManagerPid());\n        Process::signal(SIGINT, function () use ($pm) {\n            echo \"SIGINT triggered\\n\";\n            $pm->wakeup();\n        });\n        $pm->wakeup();\n    });\n    $serv->on('workerStop', function ($server) {\n        echo \"worker exit\\n\";\n    });\n    $serv->on('Task', function ($server, $taskId, $workerId, $data) {\n    });\n    $serv->on('Receive', function (Server $serv, $fd, $reactorId, $data) {\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\nunlink(PID_FILE);\n?>\n--EXPECT--\nSIGINT triggered\nworker exit\nworker exit\nworker exit\n"
  },
  {
    "path": "tests/swoole_process/signal_in_task_worker.phpt",
    "content": "--TEST--\nswoole_process: signal in task worker\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Process;\nuse Swoole\\Server;\nuse Swoole\\Coroutine\\Client;\nuse function Swoole\\Coroutine\\run;\n\ndefine('PID_FILE', __DIR__.'/task_worker.pid');\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $_pid = file_get_contents(PID_FILE);\n    Process::kill($_pid, SIGINT);\n    $pm->wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'task_worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        if ($serv->taskworker) {\n            file_put_contents(PID_FILE, $serv->getWorkerPid());\n            Process::signal(SIGINT, function () use($pm) {\n                echo \"SIGINT triggered\\n\";\n                $pm->wakeup();\n            });\n            $pm->wakeup();\n        }\n    });\n    $serv->on(\"Task\", function (Server $serv) use ($pm) {\n\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $reactorId, $data) {\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\nunlink(PID_FILE);\n?>\n--EXPECT--\nSIGINT triggered\n"
  },
  {
    "path": "tests/swoole_process/signal_twice.phpt",
    "content": "--TEST--\nswoole_process: signal\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Process;\nuse function Swoole\\Coroutine\\run;\n\nconst N = 2;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $n = N;\n    while($n--) {\n        Process::kill($pid, SIGUSR1);\n        $pm->wait();\n    }\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $n = N;\n    while($n--) {\n        run(static function () use($n, $pm){\n            $running = true;\n            Process::signal(SIGUSR1, function() use(&$running, $n) {\n                $running = false;\n                echo 'sigusr1 one-'.$n.PHP_EOL;\n            });\n            $pm->wakeup();\n            go(static function () use(&$running) {\n                while ($running) {\n                    Co::sleep(0.1);\n                }\n            });\n        });\n    }\n    $pm->wakeup();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nsigusr1 one-1\nsigusr1 one-0\n"
  },
  {
    "path": "tests/swoole_process/start.phpt",
    "content": "--TEST--\nswoole_process: start\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$proc = new Swoole\\Process(function() {\n    echo \"SUCCESS\";\n});\n$r = $proc->start();\nAssert::assert($r > 0);\n$proc->close();\n\n\\Swoole\\Process::wait(true);\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_process/timeout.phpt",
    "content": "--TEST--\nswoole_process: pipe read timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$proc = new Swoole\\Process(function(Swoole\\Process $process) {\n    sleep(5);\n});\n$r = $proc->start();\nAssert::assert($r > 0);\nini_set(\"swoole.display_errors\", \"off\");\n$proc->setTimeout(0.5);\n$ret = $proc->read();\nAssert::false($ret);\nSwoole\\Process::kill($proc->pid, SIGKILL);\n\\Swoole\\Process::wait(true);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_process/useQueue.phpt",
    "content": "--TEST--\nswoole_process: useQueue\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$proc = new Swoole\\Process(function(Swoole\\Process $proc) {\n    echo $proc->pop();\n});\n$proc->useQueue();\n$proc->start();\n$proc->push(\"SUCCESS\");\n\n\\Swoole\\Process::wait(true);\n$proc->freeQueue();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_process/wait.phpt",
    "content": "--TEST--\nswoole_process: wait\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$proc = new Swoole\\Process(function() {});\n$pid = $proc->start();\n$info = \\Swoole\\Process::wait(true);\nAssert::same($pid, $info[\"pid\"]);\nAssert::same($info[\"code\"], 0);\nAssert::same($info[\"signal\"], 0);\n\n$proc = new Swoole\\Process(function() { exit(1); });\n$pid = $proc->start();\n$info = \\Swoole\\Process::wait(true);\nAssert::same($pid, $info[\"pid\"]);\nAssert::same($info[\"code\"], 1);\nAssert::same($info[\"signal\"], 0);\n\n$proc = new Swoole\\Process(function() { \\Swoole\\Process::kill(posix_getpid(), SIGTERM); });\n$pid = $proc->start();\n$info = \\Swoole\\Process::wait(true);\nAssert::same($pid, $info[\"pid\"]);\nAssert::same($info[\"code\"], 0);\nAssert::same($info[\"signal\"], SIGTERM);\n\necho \"SUCCESS\";\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_process/wait_signal.phpt",
    "content": "--TEST--\nswoole_process: wait signal\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process;\nuse Swoole\\Event;\n\nini_set('serialize_precision', -1);\nini_set('precision', -1);\n\n$proc = new Process(function(Process $process) {\n    swoole_async_set(['wait_signal' => true]);\n    Process::signal(SIGINT, function () {\n        echo \"SIGINT\\n\";\n        Process::signal(SIGINT, null);\n    });\n    echo \"START\\n\";\n    Event::wait();\n}, true, true);\n\n$r = $proc->start();\nAssert::assert($r > 0);\n\necho $proc->read();\nProcess::kill($r, SIGINT);\necho $proc->read();\n\n$retval = Process::wait(true);\nAssert::eq($retval['pid'], $r);\nAssert::eq($retval['code'], 0);\nAssert::eq($retval['signal'], 0);\n?>\n--EXPECT--\nSTART\nSIGINT\n"
  },
  {
    "path": "tests/swoole_process/write.phpt",
    "content": "--TEST--\nswoole_process: write\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$proc = new Swoole\\Process(function(Swoole\\Process $process) {\n    $r = $process->write(\"SUCCESS\");\n    Assert::same($r, 7);\n});\n$r = $proc->start();\nAssert::assert($r > 0);\n\nSwoole\\Timer::after(10, function() use($proc) {\n    echo $proc->read();\n});\n\n\\Swoole\\Process::wait(true);\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_process/write_in_worker.phpt",
    "content": "--TEST--\nswoole_process: write in worker\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Process;\nuse Swoole\\Server;\n\n$counter = new Atomic();\n$pm = new ProcessManager();\n$pm->parentFunc = function () use ($pm) {\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm, $counter) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $process = new Process(function (Process $process) use ($pm, $serv, $counter) {\n        if ($counter->get() != 1) {\n            $counter->set(1);\n            echo \"process start\\n\";\n            for ($i = 0; $i < 1024; $i++) {\n                $data = $process->read();\n                Assert::same(strlen($data), 8192);\n            }\n            echo \"process end\\n\";\n            $pm->wakeup();\n        }\n    });\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on('WorkerStart', function (Server $serv) use ($process, $pm) {\n        usleep(1);\n        for ($i = 0; $i < 1024; $i++) {\n            Assert::same($process->write(str_repeat('A', 8192)), 8192);\n        }\n    });\n    $serv->on('WorkerStop', function (Server $serv) use ($process) {\n        echo \"worker end\\n\";\n    });\n    $serv->on('Receive', function () {});\n    $serv->addProcess($process);\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nprocess start\nprocess end\nworker end\n"
  },
  {
    "path": "tests/swoole_process_pool/bug_2652.phpt",
    "content": "--TEST--\nswoole_process_pool: bug Github#2639\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst MSGQ_KEY = 0x666;\n$pool = new Swoole\\Process\\Pool(1);\n\n$pool->on('workerStart', function (Swoole\\Process\\Pool $pool, int $workerId) {\n    $pool->getProcess($workerId)->useQueue(MSGQ_KEY);\n    $pool->getProcess($workerId)->push('test');\n    Assert::same('test', $pool->getProcess($workerId)->pop());\n    $pool->shutdown();\n    sleep(20);\n    echo \"ERROR\\n\";\n});\n$pool->start();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_process_pool/create_websocket_server.phpt",
    "content": "--TEST--\nswoole_process_pool: create websocket server in process pool\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Server;\n\nuse function Swoole\\Coroutine\\run;\n\n$workerNum = 2;\n$port = get_one_free_port();\n\n$pool = new Swoole\\Process\\Pool($workerNum);\n\n$pool->on(\"WorkerStart\", function ($pool, $workerId) use ($port) {\n    if ($workerId === 0) {\n        $server = new Server('127.0.0.1', $port, SWOOLE_PROCESS);\n        $server->on(\"message\", function ($server, $frame) {\n            $server->push($frame->fd, $frame->data);\n        });\n        $server->start();\n    } elseif ($workerId === 1) {\n        run(function () use ($port, $pool) {\n            $client = new Client('127.0.0.1', $port);\n            while (!$client->upgrade('/')) {}\n            $data = 'data';\n            $client->push($data);\n            $frame = $client->recv();\n            Assert::eq($frame->data, $data);\n            $pool->shutdown();\n        });\n    }\n});\n\n$pool->start();\necho \"DONE\\n\";\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_process_pool/detach.phpt",
    "content": "--TEST--\nswoole_process_pool: detach\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process\\Pool;\nuse Swoole\\Atomic;\n\nconst N = 100;\n\n$atomic = new Atomic();\n\n$pm = new ProcessManager;\n$pm->initFreePorts();\n\n$pm->parentFunc = function ($pid) use ($pm, $atomic) {\n    foreach (range(1, 2) as $i) {\n        $fp = stream_socket_client(\"tcp://127.0.0.1:\" . $pm->getFreePort(), $errno, $errstr) or die(\"error: $errstr\\n\");\n        $msg = \"HELLO-{$i}\";\n        fwrite($fp, pack('N', strlen($msg)) . $msg);\n    }\n    $pm->wait();\n    Assert::eq($atomic->get(), N + 1);\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $atomic) {\n    $pool = new Pool(1, SWOOLE_IPC_SOCKET);\n\n    $pool->on('WorkerStart', function (Pool $pool, $workerId) use ($pm, $atomic) {\n        echo(\"[Worker #{$workerId}] WorkerStart\\n\");\n        if ($atomic->get() == 0) {\n            $pm->wakeup();\n        }\n    });\n\n    $pool->on('Message', function (Pool $pool, $msg) use ($pm, $atomic) {\n        if ($atomic->get() == 0) {\n            $atomic->add();\n            $pool->detach();\n            $n = N;\n            while ($n--) {\n                usleep(1000);\n                $atomic->add();\n            }\n            $pm->wakeup();\n        } else {\n            echo $msg . PHP_EOL;\n        }\n    });\n\n    $pool->listen('127.0.0.1', $pm->getFreePort());\n    $pool->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n[Worker #0] WorkerStart\n[Worker #0] WorkerStart\nHELLO-2\nDONE\n"
  },
  {
    "path": "tests/swoole_process_pool/enable_coroutine.phpt",
    "content": "--TEST--\nswoole_process_pool: enable coroutine\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pool = new Swoole\\Process\\Pool(1, SWOOLE_IPC_NONE, 0, true);\n$counter = new Swoole\\Atomic(0);\n\n$pool->on('workerStart', function (Swoole\\Process\\Pool $pool, int $workerId) use ($counter) {\n    if ($counter->get() <= 5) {\n        Co::sleep(0.05);\n        $counter->add(1);\n        echo \"hello world\\n\";\n    }\n});\n\n$pool->on(\"workerStop\", function ($pool, $data) use ($counter) {\n    echo \"worker stop\\n\";\n    if ($counter->get() > 5) {\n        $pool->shutdown();\n    }\n});\n\n$pool->start();\n?>\n--EXPECT--\nhello world\nworker stop\nhello world\nworker stop\nhello world\nworker stop\nhello world\nworker stop\nhello world\nworker stop\nhello world\nworker stop\n"
  },
  {
    "path": "tests/swoole_process_pool/enable_coroutine2.phpt",
    "content": "--TEST--\nswoole_process_pool: enable coroutine\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pool = new Swoole\\Process\\Pool(1, SWOOLE_IPC_NONE, 0, false);\n$pool->set(['enable_coroutine' => true]);\n\n$counter = new Swoole\\Atomic(0);\n\n$pool->on('workerStart', function (Swoole\\Process\\Pool $pool, int $workerId) use ($counter) {\n    if ($counter->get() <= 5) {\n        Co::sleep(0.05);\n        $counter->add(1);\n        echo \"hello world\\n\";\n    }\n});\n\n$pool->on(\"workerStop\", function ($pool, $data) use ($counter) {\n    echo \"worker stop\\n\";\n    if ($counter->get() > 5) {\n        $pool->shutdown();\n    }\n});\n\n$pool->start();\n?>\n--EXPECT--\nhello world\nworker stop\nhello world\nworker stop\nhello world\nworker stop\nhello world\nworker stop\nhello world\nworker stop\nhello world\nworker stop\n"
  },
  {
    "path": "tests/swoole_process_pool/export_socket.phpt",
    "content": "--TEST--\nswoole_process_pool: exportSocket\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pool = new Swoole\\Process\\Pool(2, SWOOLE_IPC_UNIXSOCK, 0, true);\n\n$pool->on('workerStart', function (Swoole\\Process\\Pool $pool, int $workerId) {\n    $process = $pool->getProcess(0);\n    $socket = $process->exportSocket();\n    if ($workerId == 0) {\n        echo $socket->recv();\n        $socket->send(\"hello proc1\\n\");\n        echo \"proc0 stop\\n\";\n    } else {\n        $socket->send(\"hello proc0\\n\");\n        echo $socket->recv();\n        echo \"proc1 stop\\n\";\n        $pool->shutdown();\n    }\n});\n\n$pool->start();\n?>\n--EXPECT--\nhello proc0\nproc0 stop\nhello proc1\nproc1 stop\n"
  },
  {
    "path": "tests/swoole_process_pool/getprocess_1.phpt",
    "content": "--TEST--\nswoole_process_pool: getProcess [1]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nif (function_exists('posix_getpid') == false) {\n    die(\"SKIP, no posix extension.\");\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pool = new Swoole\\Process\\Pool(1);\n$pool->on('workerStart', function (Swoole\\Process\\Pool $pool, int $workerId)\n{\n    $process = $pool->getProcess();\n    Assert::same($process->pid, posix_getpid());\n    $pool->shutdown();\n    sleep(20);\n    echo \"ERROR\\n\";\n});\n\n$pool->start();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_process_pool/getprocess_2.phpt",
    "content": "--TEST--\nswoole_process_pool: getProcess [2]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst N = 70000;\n\n$pool = new Swoole\\Process\\Pool(2, SWOOLE_IPC_UNIXSOCK);\n\n$pool->on('workerStart', function (Swoole\\Process\\Pool $pool, int $workerId) {\n    if ($workerId == 0) {\n        $process1 = $pool->getProcess();\n        $process2 = $pool->getProcess(1);\n        $process2->write(str_repeat('A', N));\n        Assert::same(@$pool->getProcess(2), false);\n\n        if ($process1->read() == 'shutdown') {\n            $pool->shutdown();\n        }\n    }\n});\n\n$pool->on(\"message\", function ($pool, $data) {\n    Assert::length($data, N);\n    $process1 = $pool->getProcess(0);\n    $process1->write(\"shutdown\");\n});\n\n$pool->start();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_process_pool/getprocess_3.phpt",
    "content": "--TEST--\nswoole_process_pool: getProcess [3]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process\\Pool;\nuse Swoole\\Process;\n\nconst N = 70000;\n\n$pool = new Pool(2, SWOOLE_IPC_UNIXSOCK);\n$pool->set(['max_wait_time' => 2]);\n\n$pool->on('workerStart', function (Pool $pool, int $workerId) {\n    if ($workerId == 0) {\n        usleep(100_000);\n        $process1 = $pool->getProcess(1);\n        phpt_var_dump($process1);\n        $pid1 = $process1->pid;\n        Process::kill($process1->pid, SIGTERM);\n        usleep(100_000);\n        $process2 = $pool->getProcess(1);\n        phpt_var_dump($process2);\n        $pid2 = $process2->pid;\n        Assert::notEq($pid1, $pid2);\n        $pool->shutdown();\n    }\n});\n\n$pool->on(\"message\", function ($pool, $data) {\n\n});\n\n$pool->start();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_process_pool/getprocess_4.phpt",
    "content": "--TEST--\nswoole_process_pool: get process 4 [async]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process\\Pool;\nuse Swoole\\Process;\n\nconst N = 70000;\n\n$pool = new Pool(2, SWOOLE_IPC_UNIXSOCK);\n$pool->set(['enable_coroutine' => true]);\n\n$pool->on('workerStart', function (Pool $pool, int $workerId) {\n    if ($workerId == 0) {\n        usleep(1000);\n        $process1 = $pool->getProcess(1);\n        phpt_var_dump($process1);\n        $pid1 = $process1->pid;\n        Process::kill($process1->pid, SIGTERM);\n        usleep(100000);\n        $process2 = $pool->getProcess(1);\n        phpt_var_dump($process2);\n        $pid2 = $process2->pid;\n        Assert::notEq($pid1, $pid2);\n        $pool->shutdown();\n    }\n});\n\n$pool->on(\"message\", function ($pool, $data) {\n\n});\n\n$pool->start();\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_process_pool/getprocess_5.phpt",
    "content": "--TEST--\nswoole_process_pool: getProcess [5]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Constant;\nuse Swoole\\Process\\Pool;\n\nconst N = 70000;\n\n$pool = new Pool(2, SWOOLE_IPC_UNIXSOCK, 0, true);\n\n$pool->on(Constant::EVENT_WORKER_START, function (Pool $pool, int $workerId) {\n    if ($workerId == 0) {\n        $process1 = $pool->getProcess();\n        $process2 = $pool->getProcess(1);\n        $process2->write(str_repeat('A', N));\n        Assert::same(@$pool->getProcess(2), false);\n\n        if ($process1->read() == 'shutdown') {\n            $pool->shutdown();\n        }\n    }\n});\n\n$pool->on(Constant::EVENT_MESSAGE, function ($pool, $data) {\n    Assert::length($data, N);\n    $process1 = $pool->getProcess(0);\n    $process1->write(\"shutdown\");\n});\n\n$pool->start();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_process_pool/master_callback.phpt",
    "content": "--TEST--\nswoole_process_pool: master callback\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pool = new Swoole\\Process\\Pool(1);\n\n$pool->on('workerStart', function (Swoole\\Process\\Pool $pool, int $workerId) {\n    echo \"worker start\\n\";\n    Assert::true($pool->workerRunning);\n    Assert::eq($pool->workerId, 0);\n    Assert::eq($pool->workerPid, posix_getpid());\n    pcntl_signal(SIGTERM, function (){\n\n    });\n    $pool->shutdown();\n    sleep(20);\n    echo \"worker exit\\n\";\n});\n\n$pool->on('workerStop', function (Swoole\\Process\\Pool $pool, int $workerId) {\n    Assert::false($pool->workerRunning);\n    echo \"worker stop\\n\";\n});\n\n$pool->on('start', function (Swoole\\Process\\Pool $pool) {\n    Assert::true($pool->running);\n    echo \"start\\n\";\n});\n\n$pool->on('shutdown', function (Swoole\\Process\\Pool $pool) {\n    Assert::false($pool->running);\n    echo \"shutdown\\n\";\n});\n\n$pool->start();\n?>\n--EXPECT--\nstart\nworker start\nshutdown\nworker exit\nworker stop\n"
  },
  {
    "path": "tests/swoole_process_pool/master_pid.phpt",
    "content": "--TEST--\nswoole_process_pool: master pid\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pool = new Swoole\\Process\\Pool(1);\n$pid = posix_getpid();\n$pool->on('workerStart', function (Swoole\\Process\\Pool $pool, int $workerId) use ($pid)\n{\n    Assert::assert($pool->master_pid == $pid);\n    posix_kill($pid, SIGTERM);\n    sleep(20);\n    echo \"ERROR\\n\";\n});\n\n$pool->start();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_process_pool/max_wait_time.phpt",
    "content": "--TEST--\nswoole_process_pool: max wait time\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Constant;\nuse Swoole\\Process\\Pool;\nuse Swoole\\Timer;\n\n(function () {\n    $atomic = new Atomic();\n    $pool = new Pool(4, SWOOLE_IPC_NONE);\n    $pool->set([\n        Constant::OPTION_ENABLE_COROUTINE => true,\n        Constant::OPTION_MAX_WAIT_TIME => 1,\n    ]);\n\n    $pool->on('workerStart', function (Pool $pool, int $workerId) use ($atomic): void {\n        echo \"workerStart: $workerId\" . PHP_EOL;\n        $atomic->wait(-1);\n    });\n\n    $pool->on('start', function () use ($pool): void {\n        Timer::after(500, function () use ($pool): void {\n            $pool->shutdown();\n        });\n        echo 'start' . PHP_EOL;\n    });\n\n    $pool->on('shutdown', function () use ($atomic): void {\n        echo 'shutdown' . PHP_EOL;\n    });\n\n    $pool->start();\n})();\n?>\n--EXPECTF--\nstart\nworkerStart: %d\nworkerStart: %d\nworkerStart: %d\nworkerStart: %d\nshutdown\n"
  },
  {
    "path": "tests/swoole_process_pool/message.phpt",
    "content": "--TEST--\nswoole_process_pool: simple send test\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n    Assert::assert($client->connect('127.0.0.1', 8089, 5));\n    $data = \"hello swoole!\";\n    $client->send(pack('N', strlen($data)) . $data);\n    $ret = $client->recv();\n    $len = unpack('Nlen', substr($ret, 0, 4))['len'];\n    $ret .= $client->recv($len - (strlen($ret) - 4));\n    $ret = substr($ret, 4, $len);\n    echo $ret;\n    $client->close();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $pool = new Swoole\\Process\\Pool(1, SWOOLE_IPC_SOCKET);\n\n    $pool->on('workerStart', function (Swoole\\Process\\Pool $pool, int $workerId) {\n        $client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n        Assert::assert($client->connect('127.0.0.1', 8089, 5));\n        $data = \"hello swoole! (from workerStart)\";\n        $client->send(pack('N', strlen($data)) . $data);\n        $client->close();\n    });\n\n    $pool->on(\"message\", function (Swoole\\Process\\Pool $pool, string $message) {\n        echo \"{$message}\\n\";\n        if ($message === \"hello swoole!\") {\n            $pool->write(\"hello \");\n            $pool->write(\"client!\");\n            $pool->write(\"\\n\");\n        }\n    });\n\n    $pool->listen('127.0.0.1', 8089);\n\n    $pool->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nhello swoole! (from workerStart)\nhello swoole!\nhello client!\n"
  },
  {
    "path": "tests/swoole_process_pool/message_async.phpt",
    "content": "--TEST--\nswoole_process_pool: message async [disable message bus]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Constant;\nuse Swoole\\Process\\Pool;\nuse Swoole\\Coroutine\\System;\n\nconst N = 256;\n\n$in = $out = [];\nfor ($i = N; $i--;) {\n    $in[] = random_bytes(random_int(1024, 8192));\n}\n\n$pool = new Pool(2, SWOOLE_IPC_UNIXSOCK);\n$pool->set([\n    'enable_coroutine' => true,\n]);\n\n$pool->on(Constant::EVENT_WORKER_START, function (Pool $pool, int $workerId) use ($in) {\n    if ($workerId == 0) {\n        foreach ($in as $item) {\n            Assert::true($pool->sendMessage($item, 1));\n            System::sleep(0.002);\n        }\n    }\n});\n\n$pool->on(Constant::EVENT_MESSAGE, function ($pool, $data) use (&$out, $in) {\n    $out[] = $data;\n    if (count($out) == N) {\n        Assert::eq($in, $out);\n        echo \"DONE\\n\";\n        $pool->shutdown();\n    }\n});\n\n$pool->start();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_process_pool/message_bus.phpt",
    "content": "--TEST--\nswoole_process_pool: message bus\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Constant;\nuse Swoole\\Process\\Pool;\n\nconst N = 256;\n\n$in = $out = [];\nfor ($i = N; $i--;) {\n    $in[] = random_bytes(random_int(8192, 1024 * 1024));\n}\n\n$pool = new Pool(2, SWOOLE_IPC_UNIXSOCK);\n$pool->set([\n    'enable_coroutine' => true,\n    'enable_message_bus' => true,\n]);\n\n$pool->on(Constant::EVENT_WORKER_START, function (Pool $pool, int $workerId) use ($in) {\n    if ($workerId == 0) {\n        foreach ($in as $item) {\n            Assert::true($pool->sendMessage($item, 1));\n            Co::sleep(0.002);\n        }\n    }\n});\n\n$pool->on(Constant::EVENT_MESSAGE, function ($pool, $data) use (&$out, $in) {\n    $out[] = $data;\n    if (count($out) == N) {\n        Assert::eq($in, $out);\n        echo \"DONE\\n\";\n        $pool->shutdown();\n    }\n});\n\n$pool->start();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_process_pool/message_bus_sync.phpt",
    "content": "--TEST--\nswoole_process_pool: message bus [sync]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Constant;\nuse Swoole\\Process\\Pool;\n\nconst N = 1;\n\n$in = $out = [];\nfor ($i = N; $i--;) {\n    $in[] = random_bytes(random_int(8192, 1024 * 1024));\n}\n\n$pool = new Pool(2, SWOOLE_IPC_UNIXSOCK);\n$pool->set([\n    'enable_message_bus' => true,\n]);\n\n$pool->on(Constant::EVENT_WORKER_START, function (Pool $pool, int $workerId) use ($in) {\n    if ($workerId == 0) {\n        foreach ($in as $item) {\n            Assert::true($pool->sendMessage($item, 1));\n            usleep(2000);\n        }\n    }\n});\n\n$pool->on(Constant::EVENT_MESSAGE, function ($pool, $data) use (&$out, $in) {\n    $out[] = $data;\n    if (count($out) == N) {\n        Assert::eq($in, $out);\n        echo \"DONE\\n\";\n        $pool->shutdown();\n    }\n});\n\n$pool->start();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_process_pool/msgqueue.phpt",
    "content": "--TEST--\nswoole_process_pool: sysv msgqueue\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nif (function_exists('msg_get_queue') == false) {\n    die(\"SKIP, no sysvmsg extension.\");\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Atomic;\n\nconst N = 100;\nconst MSGQ_KEY = 0x70000001;\n\n$pm = new ProcessManager;\n$atomic = new Atomic();\n\n$pm->parentFunc = function ($pid) use ($pm, $atomic) {\n    $seg = msg_get_queue(MSGQ_KEY);\n    foreach (range(1, N) as $i) {\n        $data = json_encode(['data' => base64_encode(random_bytes(1024)), 'id' => uniqid(), 'index' => $i,]);\n        msg_send($seg, $i, $data, false);\n    }\n};\n\n$pm->childFunc = function () use ($pm, $atomic) {\n    $pool = new Swoole\\Process\\Pool(1, SWOOLE_IPC_MSGQUEUE, MSGQ_KEY);\n\n    $pool->on('workerStart', function (Swoole\\Process\\Pool $pool, int $workerId) use ($pm) {\n        echo \"worker start\\n\";\n        $pm->wakeup();\n    });\n\n    $pool->on(\"message\", function (Swoole\\Process\\Pool $pool, string $message) use ($atomic) {\n        $data = json_decode($message, true);\n        Assert::assert($data);\n        Assert::assert(is_array($data));\n        Assert::same(strlen(base64_decode($data['data'])), 1024);\n        $atomic->add(1);\n        if ($atomic->get() == 100) {\n            $pool->shutdown();\n            echo \"DONE\\n\";\n        }\n    });\n\n    $pool->on('workerStop', function (Swoole\\Process\\Pool $pool, int $workerId) {\n        echo \"worker stop\\n\";\n    });\n\n    $pool->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nworker start\nDONE\nworker stop\n"
  },
  {
    "path": "tests/swoole_process_pool/msgqueue_2.phpt",
    "content": "--TEST--\nswoole_process_pool: sysv msgqueue [2]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nif (function_exists('msg_get_queue') == false) {\n    die(\"SKIP, no sysvmsg extension.\");\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process\\Pool;\n\nconst MSGQ_KEY = 0x70000001;\n\n$pool = new Pool(2, SWOOLE_IPC_MSGQUEUE, MSGQ_KEY);\n\n$pool->on('workerStart', function (Pool $pool, int $workerId) {\n    if ($workerId == 0) {\n        echo \"worker start\\n\";\n        Assert::true($pool->getProcess()->push('hello world' . PHP_EOL));\n    } else {\n        echo $pool->getProcess()->pop();\n        $pool->shutdown();\n    }\n});\n\n$pool->on('workerStop', function (Pool $pool, int $workerId) {\n    if ($workerId == 1) {\n        echo \"worker stop\\n\";\n    }\n});\n\n$pool->start();\n?>\n--EXPECT--\nworker start\nhello world\nworker stop\n"
  },
  {
    "path": "tests/swoole_process_pool/reload.phpt",
    "content": "--TEST--\nswoole_process_pool: reload\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nif (function_exists('msg_get_queue') == false) {\n    die(\"SKIP, no sysvmsg extension.\");\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\nconst PROC_NAME = 'swoole_unittest_process_pool';\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($i = 0; $i < 5; $i++) {\n        Swoole\\Process::kill($pid, SIGUSR1);\n        usleep(10000);\n        //判断进程是否存在\n        Assert::assert(intval(shell_exec(\"ps aux | grep \\\"\" . PROC_NAME . \"\\\" |grep -v grep| awk '{ print $2}'\")) > 0);\n    }\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    cli_set_process_title(PROC_NAME);\n\n    Co::set(['log_level' => SWOOLE_LOG_ERROR]);\n\n    $pool = new Swoole\\Process\\Pool(2);\n\n    $pool->on('workerStart', function (Swoole\\Process\\Pool $pool, int $workerId) use ($pm) {\n        $pm->wakeup();\n        Swoole\\Timer::tick(1000, function () use ($workerId) {\n            echo \"sleep [$workerId] \\n\";\n        });\n        Swoole\\Process::signal(SIGTERM, function () {\n            Swoole\\Event::exit();\n        });\n        Swoole\\Event::wait();\n    });\n\n    $pool->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_process_pool/reuse_port.phpt",
    "content": "--TEST--\nswoole_process_pool: co\\socket reuse port\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Constant;\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Coroutine\\Scheduler;\nuse Swoole\\Coroutine\\Socket;\nuse Swoole\\Process;\nuse Swoole\\Process\\Pool;\nuse SwooleTest\\ProcessManager;\n\n$pm = new ProcessManager();\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $sch = new Scheduler();\n    $pids = [];\n    $sch->parallel(10, function () use ($pm, &$pids) {\n        $cli = new Client(SWOOLE_SOCK_TCP);\n        if (!$cli->connect('127.0.0.1', $pm->getFreePort())) {\n            echo \"ERROR [1]\\n\";\n            return;\n        }\n        if (!$cli->send(\"hello\\n\")) {\n            echo \"ERROR [2]\\n\";\n            return;\n        }\n        $ret = $cli->recv();\n        if (!$ret) {\n            echo \"ERROR [3]\\n\";\n            return;\n        }\n        $result = unserialize($ret);\n        if (!$result) {\n            echo \"ERROR [4]\\n\";\n            return;\n        }\n        $pids[$result['wid']] = 1;\n    });\n    $sch->start();\n    Assert::eq(count($pids), IS_MAC_OS ? 1 : 2);\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $atomic = new Atomic();\n    $pool = new Pool(2);\n    $pool->set(['enable_coroutine' => true]);\n    $pool->on(Constant::EVENT_WORKER_START, function ($pool, $id) use ($pm, $atomic) {\n        $socket = new Socket(AF_INET, SOCK_STREAM, 0);\n        $socket->setOption(SOL_SOCKET, SO_REUSEPORT, true);\n        $socket->bind('127.0.0.1', $pm->getFreePort());\n        $socket->listen(128);\n\n        if ($atomic->add() == 2) {\n            $pm->wakeup();\n        }\n        Process::signal(SIGTERM, function () use ($socket) {\n            $socket->cancel();\n        });\n        while (true) {\n            $client = $socket->accept();\n            if (!$client) {\n                if ($socket->errCode == SOCKET_ECANCELED) {\n                    break;\n                }\n                continue;\n            }\n            co::sleep(0.005);\n            $data = $client->recv();\n            if (empty($data)) {\n                $client->close();\n                break;\n            }\n            $client->send(serialize(['wid' => $id]));\n        }\n    });\n    $pool->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_process_pool/shutdown.phpt",
    "content": "--TEST--\nswoole_process_pool: shutdown\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pool = new Swoole\\Process\\Pool(1);\n$pool->on('workerStart', function (Swoole\\Process\\Pool $pool, int $workerId)\n{\n    $pool->shutdown();\n    sleep(20);\n    echo \"ERROR\\n\";\n});\n\n$pool->start();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_process_pool/socket_coro.phpt",
    "content": "--TEST--\nswoole_process_pool: co\\socket\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Constant;\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Coroutine\\Scheduler;\nuse Swoole\\Coroutine\\Socket;\nuse Swoole\\Process;\nuse Swoole\\Process\\Pool;\nuse SwooleTest\\ProcessManager;\n\n$pm = new ProcessManager();\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $s = microtime(true);\n    $sch = new Scheduler();\n    $sch->parallel(2, function () use ($pm) {\n        $cli = new Client(SWOOLE_SOCK_TCP);\n        if (!$cli->connect('127.0.0.1', $pm->getFreePort())) {\n            echo \"ERROR\\n\";\n            return;\n        }\n        if (!$cli->send(\"hello\\n\")) {\n            return;\n        }\n        $ret = $cli->recv();\n        if (!$ret) {\n            return;\n        }\n        echo $ret;\n    });\n    $sch->start();\n    echo \"DONE\\n\";\n    Assert::lessThan(microtime(true) - $s, 0.25);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $socket = new Socket(AF_INET, SOCK_STREAM, 0);\n    $socket->bind('127.0.0.1', $pm->getFreePort());\n\n    $atomic = new Atomic();\n\n    $pool = new Pool(2);\n    $pool->set(['enable_coroutine' => true]);\n    $pool->on(Constant::EVENT_WORKER_START, function ($pool, $id) use ($socket, $pm, $atomic) {\n        $socket->listen(128);\n        if ($atomic->add() == 2) {\n            $pm->wakeup();\n        }\n        Process::signal(SIGTERM, function () use ($socket) {\n            $socket->cancel();\n        });\n        while (true) {\n            $client = $socket->accept();\n            if (!$client) {\n                if ($socket->errCode == SOCKET_ECANCELED) {\n                    break;\n                }\n                continue;\n            }\n            usleep(100000);\n            $data = $client->recv();\n            if (empty($data)) {\n                $client->close();\n                break;\n            }\n            $client->send(\"Server[{$id}]: {$data}\");\n        }\n        echo \"worker stop\\n\";\n    });\n\n    $pool->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECTF--\nServer[%d]: hello\nServer[%d]: hello\nDONE\nworker stop\nworker stop\n"
  },
  {
    "path": "tests/swoole_process_pool/start_pool_twice_in_same_process.phpt",
    "content": "--TEST--\nswoole_process_pool: start pool twice in the same process\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process\\Pool;\n\n$pool = new Swoole\\Process\\Pool(1);\n\n$pool->on(\"WorkerStart\", function (Pool $pool, $workerId) {\n    $pool->shutdown();\n});\n\n$pool->start();\n\n$pool = new Swoole\\Process\\Pool(1);\n\n$pool->on(\"WorkerStart\", function (Pool $pool, $workerId) {\n    $pool->shutdown();\n});\n\n$pool->start();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_process_pool/start_twice.phpt",
    "content": "--TEST--\nswoole_process_pool: start twice\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process\\Pool;\n\n$pool = new Swoole\\Process\\Pool(1);\n\n$pool->on(\"WorkerStart\", function (Pool $pool, $workerId) {\n    echo \"CHILD START\\n\";\n    $pool->shutdown();\n    sleep(1);\n});\n\n$pool->start();\necho \"START 1\\n\";\n$pool->start();\necho \"START 2\\n\";\n?>\n--EXPECT--\nCHILD START\nSTART 1\nCHILD START\nSTART 2\n"
  },
  {
    "path": "tests/swoole_process_pool/worker_exit_1.phpt",
    "content": "--TEST--\nswoole_process_pool: worker exit\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Timer;\n\n$pool = new Swoole\\Process\\Pool(1, SWOOLE_IPC_NONE, 0, true);\n\n$GLOBALS['running'] = true;\n$GLOBALS['count'] = 1;\n\n$pool->on('workerStart', function (Swoole\\Process\\Pool $pool, int $workerId) {\n    echo \"worker start\\n\";\n    Assert::eq($pool->workerId, $workerId);\n\n    $count = 0;\n    while ($GLOBALS['running']) {\n        Co::sleep(0.03);\n        echo \"sleep\\n\";\n        if (++$count === 3) {\n            $pool->shutdown();\n        }\n    }\n});\n\n$pool->on('workerStop', function ($pool, $data) {\n    echo \"worker stop\\n\";\n});\n\n$pool->on('workerExit', function ($pool, $data) {\n    $GLOBALS['count']++;\n    if ($GLOBALS['count'] == 3) {\n        $GLOBALS['running'] = false;\n    }\n    echo ('worker exit') . PHP_EOL;\n});\n\n$pool->start();\n?>\n--EXPECT--\nworker start\nsleep\nsleep\nsleep\nworker exit\nsleep\nworker exit\nsleep\nworker exit\nworker stop\n"
  },
  {
    "path": "tests/swoole_redis_server/big_packet.phpt",
    "content": "--TEST--\nswoole_redis_server: test big packet\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Redis\\Server;\n\ndefine('VALUE_LEN', 8192 * 128);\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $redis = new redis;\n    $redis->connect('127.0.0.1', $pm->getFreePort());\n    $redis->set('big_value', str_repeat('A', VALUE_LEN));\n    $ret = $redis->get('big_value');\n    Assert::same(strlen($ret ?? '' ?: ''), VALUE_LEN);\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->data = array();\n\n    $server->setHandler('GET', function ($fd, $data) use ($server) {\n        if (count($data) == 0) {\n            return Server::format(Server::ERROR, \"ERR wrong number of arguments for 'GET' command\");\n        }\n        $key = $data[0];\n        if (empty($server->data[$key])) {\n            $server->send($fd, Server::format(Server::NIL));\n        } else {\n            $server->send($fd, Server::format(Server::STRING, $server->data[$key]));\n        }\n    });\n\n    $server->setHandler('SET', function ($fd, $data) use ($server) {\n        if (count($data) < 2) {\n            return Server::format(Server::ERROR, \"ERR wrong number of arguments for 'SET' command\");\n        }\n        $key = $data[0];\n        $server->data[$key] = $data[1];\n        $server->send($fd, Server::format(Server::STATUS, 'OK'));\n    });\n\n    $server->on('WorkerStart', function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_redis_server/empty.phpt",
    "content": "--TEST--\nswoole_redis_server: empty\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Redis\\Server;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $redis = new Predis\\Client('tcp://127.0.0.1:' . $pm->getFreePort());\n    $map = $redis->get('map');\n    Assert::eq($map, [0, '', false, null]);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->setHandler('GET', function ($fd, $data) use ($server) {\n        $server->send($fd, Server::format(Server::SET, [0, '', false, null]));\n    });\n    $server->on('WorkerStart', function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_redis_server/format.phpt",
    "content": "--TEST--\nswoole_redis_server: format\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Redis\\Server;\necho Server::format(Server::ERROR);\necho Server::format(Server::ERROR, \"BAD REQUEST\");\necho Server::format(Server::NIL);\necho Server::format(Server::STATUS);\necho Server::format(Server::STATUS, \"SUCCESS\");\necho Server::format(Server::INT, 1000);\necho Server::format(Server::STRING, \"hello swoole\");\necho Server::format(Server::SET, [\"php\", \"is\", \"best\"]);\necho Server::format(Server::MAP, [\"php\" => '99', \"java\" => '88', \"c++\" => '666', 9999 => 'hello']);\n?>\n--EXPECT--\n-ERR\n-BAD REQUEST\n$-1\n+OK\n+SUCCESS\n:1000\n$12\nhello swoole\n*3\n$3\nphp\n$2\nis\n$4\nbest\n*8\n$3\nphp\n$2\n99\n$4\njava\n$2\n88\n$3\nc++\n$3\n666\n$4\n9999\n$5\nhello\n"
  },
  {
    "path": "tests/swoole_redis_server/getHandler.phpt",
    "content": "--TEST--\nswoole_redis_server: getHandler\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Redis\\Server;\n\n$server = new Server('127.0.0.1', 0, SWOOLE_BASE);\n\n$server->setHandler('GET', function ($fd, $data) use ($server) {\n    $server->send($fd, Server::format(Server::STRING, \"hello\"));\n});\n\n$callback = $server->getHandler('GET');\nAssert::assert(is_callable($callback));\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_redis_server/large_data_map.phpt",
    "content": "--TEST--\nswoole_redis_server: large data map\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Redis\\Server;\n\ndefine(\"N\", random_int(1000, 1000000));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $redis = new Predis\\Client('tcp://127.0.0.1:' . $pm->getFreePort());\n    $map = $redis->get('map');\n    Assert::notEmpty($map);\n\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->setHandler('GET', function ($fd, $data) use ($server) {\n        $key = $data[0];\n        if ($key == 'map') {\n            $out = Server::format(Server::MAP, ['a' => str_repeat('a', N), 'b' => str_repeat('b', N * 3)]);\n        }\n        $server->send($fd, $out);\n    });\n\n    $server->on('WorkerStart', function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_redis_server/nested_map.phpt",
    "content": "--TEST--\nswoole_redis_server: nested map\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Redis\\Server;\n\ndefine(\"UUID\", uniqid());\ndefine(\"NUMBER\", random_int(1, 1000000));\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $redis = new Predis\\Client('tcp://127.0.0.1:' . $pm->getFreePort());\n    $map = $redis->get('map');\n    Assert::notEmpty($map);\n    Assert::eq($map[0], 'uuid');\n    Assert::eq($map[1], UUID);\n    Assert::isArray($map[3]);\n    Assert::eq($map[5], NUMBER);\n    Assert::eq($map[3][1], NUMBER);\n    Assert::eq($map[3][2], UUID);\n\n    $set = $redis->get('set');\n    Assert::notEmpty($set);\n    Assert::eq($set[0], UUID);\n    Assert::isArray($set[1]);\n    Assert::eq($set[2], NUMBER);\n    Assert::eq($set[1][1], NUMBER);\n    Assert::eq($set[1][3], UUID);\n\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->setHandler('GET', function ($fd, $data) use ($server) {\n        $key = $data[0];\n        if ($key == 'map') {\n            $out = Server::format(Server::MAP, [\n                'uuid' => UUID,\n                'list' => [1, NUMBER, UUID],\n                'number' => NUMBER,\n            ]);\n        } elseif ($key == 'set') {\n            $out = Server::format(Server::SET, [\n                UUID,\n                ['number' => NUMBER, 'uuid' => UUID],\n                NUMBER,\n            ]);\n        } else {\n            $out = Server::format(Server::ERROR, 'bad key');\n        }\n        $server->send($fd, $out);\n    });\n\n    $server->on('WorkerStart', function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/accept.phpt",
    "content": "--TEST--\nswoole_runtime: accept\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_ssl();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\ngo(function () {\n    $opts = array(\n        'socket' => array(\n            'so_reuseaddr' => true,\n        ),\n    );\n    $ctx = stream_context_create($opts);\n    $socket = stream_socket_server(\"tcp://0.0.0.0:8000\", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctx);\n    if (!$socket) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        $conn = stream_socket_accept($socket);\n        fwrite($conn, 'The local time is ' . date('n/j/Y g:i a'));\n        fclose($conn);\n        fclose($socket);\n    }\n});\n\ngo(function () {\n    $fp = stream_socket_client(\"tcp://127.0.0.1:8000\", $errno, $errstr, 30);\n    if (!$fp) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        $data = fread($fp, 8192);\n        fclose($fp);\n        Assert::assert(strpos($data,'local time') !== false);\n    }\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/accept_timeout.phpt",
    "content": "--TEST--\nswoole_runtime: accept timeout\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\ngo(function () {\n    $opts = array(\n        'socket' => array(\n            'so_reuseaddr' => true,\n        ),\n    );\n    $ctx = stream_context_create($opts);\n    $socket = stream_socket_server(\"tcp://0.0.0.0:8000\", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctx);\n    if (!$socket) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        $conn = @stream_socket_accept($socket, 1);\n        Assert::false($conn);\n    }\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/async_protocol.phpt",
    "content": "--TEST--\nswoole_runtime: async protocol\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine(0);\nconst N = 5;\n\ngo(function () {\n    $socket = new Swoole\\Coroutine\\Socket(AF_UNIX, SOCK_DGRAM, 0);\n    $socket->bind(__DIR__ . '/test.sock');\n\n    for ($i = 0; $i < N; $i++) {\n        $peer = null;\n        $data = $socket->recvfrom($peer);\n        echo \"[Server] recv : $data\\n\";\n    }\n});\n\ngo(function () {\n    $fp = stream_socket_client(\"async.udg://\".__DIR__.\"/test.sock\", $errno, $errstr, 30);\n    if (!$fp) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        for ($i = 0; $i < N; $i++) {\n            fwrite($fp, \"hello-{$i}\");\n        }\n        fclose($fp);\n    }\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n[Server] recv : hello-0\n[Server] recv : hello-1\n[Server] recv : hello-2\n[Server] recv : hello-3\n[Server] recv : hello-4\n"
  },
  {
    "path": "tests/swoole_runtime/base.phpt",
    "content": "--TEST--\nswoole_runtime: base\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$server = SwooleTest\\CoServer::createTcpGreeting();\n$server->run();\nSwoole\\Runtime::enableCoroutine(SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_SLEEP);\ngo(function () {\n    usleep(1000);\n    echo '1' . PHP_EOL;\n});\necho '2' . PHP_EOL;\ngo(function () use ($server) {\n    $cli = stream_socket_client(\"tcp://127.0.0.1:{$server->getPort()}\", $errno, $errstr, 1);\n    $read = $write = [$cli];\n    $n = stream_select($read, $write, $except, 1);\n    echo 'select' . PHP_EOL;\n    Assert::same($n, 1);\n    Assert::count($read, 1);\n    Assert::count($write, 1);\n    fread($cli, 8192);\n    $n = stream_select($read, $write, $except, 1);\n    Assert::same($n, 1);\n    Assert::count($read, 0);\n    Assert::count($write, 1);\n});\necho '3' . PHP_EOL;\nSwoole\\Runtime::enableCoroutine(SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_FILE ^ SWOOLE_HOOK_STREAM_FUNCTION);\ngo(function () {\n    $read = [fopen(__FILE__, 'r')];\n    $n = stream_select($read, $write, $except, 1);\n    Assert::same($n, 1);\n    Assert::count($read, 1);\n    echo '4' . PHP_EOL;\n});\ngo(function () use ($server) {\n    usleep(10 * 1000);\n    echo 'sleep2' . PHP_EOL;\n    $server->shutdown();\n});\necho '5' . PHP_EOL;\nSwoole\\Runtime::enableCoroutine(SWOOLE_HOOK_ALL); // all\ngo(function () {\n    usleep(5 * 1000);\n    echo 'sleep1' . PHP_EOL;\n});\necho '6' . PHP_EOL;\ngo(function () use ($server) {\n    $read = [stream_socket_client(\"tcp://127.0.0.1:{$server->getPort()}\", $errno, $errstr, 1)];\n    $n = stream_select($read, $write, $except, 1);\n    Assert::same($n, 1);\n    Assert::count($read, 1);\n    echo 'select' . PHP_EOL;\n});\necho '7' . PHP_EOL;\nSwoole\\Event::wait();\nSwoole\\Runtime::enableCoroutine(0); // disable all\n?>\n--EXPECT--\n1\n2\n3\n4\n5\n6\n7\nselect\nselect\nsleep1\nsleep2\n"
  },
  {
    "path": "tests/swoole_runtime/bindto.phpt",
    "content": "--TEST--\nswoole_runtime: socket context bindto\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    Runtime::enableCoroutine();\n\n    run(function () use($pm) {\n        $context = stream_context_create([\n            'socket' => [\n                'bindto' => '0:9100',\n            ],\n        ]);\n\n        file_get_contents(\"http://127.0.0.1:{$pm->getFreePort()}\", false, $context);\n        $pm->kill();\n        echo \"Done\\n\";\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $server = new Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/', function (Request $request, Response $response) {\n            Assert::eq($request->server['remote_addr'], '127.0.0.1');\n            Assert::eq($request->server['remote_port'], 9100);\n            $response->end('success');\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_runtime/bindto2.phpt",
    "content": "--TEST--\nswoole_runtime: socket context bindto\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    Runtime::enableCoroutine();\n\n    run(function () use($pm) {\n        $context = stream_context_create([\n            'socket' => [\n                'bindto' => ':9100',\n            ],\n        ]);\n\n        file_get_contents(\"http://127.0.0.1:{$pm->getFreePort()}\", false, $context);\n        $pm->kill();\n        echo \"Done\\n\";\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $server = new Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/', function (Request $request, Response $response) {\n            Assert::eq($request->server['remote_addr'], '127.0.0.1');\n            Assert::eq($request->server['remote_port'], 9100);\n            $response->end('success');\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_runtime/block.phpt",
    "content": "--TEST--\nswoole_runtime: pdo create outside coroutine\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_pdo_not_support_mysql8();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine(false);\n$start = microtime(true);\n$pdo_map = [];\nfor ($i = 5; $i--;) {\n    $pdo_map[] = new PDO(\n        \"mysql:host=\" . MYSQL_SERVER_HOST . \";port=\" . MYSQL_SERVER_PORT . \";dbname=\" . MYSQL_SERVER_DB . \";charset=utf8\",\n        MYSQL_SERVER_USER, MYSQL_SERVER_PWD\n    );\n}\n\nSwoole\\Runtime::enableCoroutine(true);\nfor ($i = 5; $i--;) {\n    $pdo = $pdo_map[$i];\n    go(function () use ($pdo) {\n        $pdo->exec(\"SELECT sleep(0.1)\");\n        Assert::same($pdo->errorCode(), PDO::ERR_NONE);\n    });\n}\nSwoole\\Event::wait();\nAssert::assert((microtime(true) - $start) > 5 * 0.1);\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_runtime/bug_4657.phpt",
    "content": "--TEST--\nswoole_runtime: bug 4657\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Socket as BaseSocket;\nuse function Swoole\\Coroutine\\run;\n\n$socket = socket_create(AF_INET, SOCK_STREAM, 0);\nAssert::eq(get_class($socket), Socket::class);\n\nrun(function () {\n    $socket = socket_create(AF_INET, SOCK_STREAM, 0);\n    Assert::eq(get_class($socket), Swoole\\Coroutine\\Socket::class);\n    Assert::true($socket instanceof BaseSocket);\n});\n\n$socket = socket_create(AF_INET, SOCK_STREAM, 0);\nAssert::eq(get_class($socket), Socket::class);\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_runtime/bug_5104.phpt",
    "content": "--TEST--\nswoole_runtime: Github#5104 https://github.com/swoole/swoole-src/issues/5104\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nuse function Co\\run;\n\nrun(function () {\n    $socket1 = stream_socket_server(\"tcp://0.0.0.0:8000\");\n    $socket2 = stream_socket_client(\"tcp://example.com:80\");\n\n    stream_set_blocking($socket1, 0);\n    stream_set_blocking($socket2, 0);\n\n    $read = [$socket1, $socket2];\n    $write = [$socket2];\n    $except = null;\n    $timeout = null;\n    stream_select($read, $write, $except, $timeout, null);\n\n    if (in_array($socket1, $read)) {\n      $client = stream_socket_accept($socket1);\n      fwrite($client, \"Hello world!\\n\");\n      fclose($client);\n    }\n    if (in_array($socket2, $read)) {\n      $response = fread($socket2, 1024);\n    }\n    if (in_array($socket2, $write)) {\n      fwrite($socket2, \"GET / HTTP/1.1\\r\\nHost: example.com\\r\\n\\r\\n\");\n    }\n    echo 'DONE';\n});\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_runtime/cancel_sleep.phpt",
    "content": "--TEST--\nswoole_runtime: cancel sleep\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\nrun(function() {\n    $cid = Coroutine::getCid();\n    go(function() use ($cid) {\n        System::sleep(0.1);\n        Assert::true(Coroutine::cancel($cid));\n    });\n\n    Assert::false(System::sleep(2000));\n    echo \"success1\" . PHP_EOL;\n\n    $cids = [];\n    for($i = 0; $i < 100; $i++) {\n        $cids[] = go(function() use ($cid) {\n            Assert::false(System::sleep(100));\n        });\n    }\n\n    go(function() use ($cid) {\n        Assert::true(System::sleep(0.1));\n        echo \"success3\" . PHP_EOL;\n    });\n\n    foreach($cids as $cid) {\n        Assert::true(Coroutine::cancel($cid));\n    }\n\n    echo \"success2\" . PHP_EOL;\n});\n?>\n--EXPECT--\nsuccess1\nsuccess2\nsuccess3\n"
  },
  {
    "path": "tests/swoole_runtime/destruct.phpt",
    "content": "--TEST--\nswoole_runtime: socket destruct close\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$redis = new Redis();\n$redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT);\n$redis->rawCommand('CLIENT', 'KILL', 'TYPE', 'normal');\n$redis->close();\nusleep(100);\n\nSwoole\\Runtime::enableCoroutine();\n\n$timer_id = Swoole\\Timer::tick(1000 / MAX_CONCURRENCY_MID, function () {\n    $redis = new Redis();\n    $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT);\n    Assert::assert($redis->set('foo', 'bar'));\n    Assert::same($redis->get('foo'), 'bar');\n});\n\ngo(function () use ($timer_id) {\n    co::sleep(1);\n    Swoole\\Timer::clear($timer_id);\n    $redis = new Redis();\n    $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT);\n    $info = (array) $redis->info('clients');\n    phpt_var_dump($info);\n    Assert::same($info['connected_clients'], 1, var_dump_return($info));\n    echo \"DONE\\n\";\n});\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_runtime/enable_crypto.phpt",
    "content": "--TEST--\nswoole_runtime: stream_socket_enable_crypto\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_ssl();\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\ngo(function () {\n    $fp = stream_socket_client(\"tcp://www.baidu.com:443\", $errno, $errstr, 30);\n    if (!$fp) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        $http = \"GET / HTTP/1.0\\r\\nAccept: */*User-Agent: Lowell-Agent\\r\\nHost: www.baidu.com\\r\\nConnection: Close\\r\\n\\r\\n\";\n        stream_socket_enable_crypto($fp,true, STREAM_CRYPTO_METHOD_SSLv23_CLIENT);\n        fwrite($fp, $http);\n        $content = '';\n        while (!feof($fp)) {\n            $content .= fread($fp, 1024);\n        }\n        fclose($fp);\n        Assert::assert(strpos($content,'map.baidu.com') !== false);\n    }\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/exec/exec.phpt",
    "content": "--TEST--\nswoole_runtime/unsafe: dns\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = ProcessManager::exec(function () {\n    $fn_list = ['exec', 'shell_exec', 'system', 'passthru'];\n    Swoole\\Runtime::setHookFlags(SWOOLE_HOOK_ALL);\n    run(function () use ($fn_list)  {\n        $running = true;\n        $count = 0;\n        $cid = Co\\go(function () use (&$count, &$running)  {\n            while ($running) {\n                $count++;\n                sleep(1);\n            }\n        });\n        foreach ($fn_list as $fn) {\n            $rs = $fn(\"ping -4 www.gov.cn -c 3\");var_dump($rs);\n        }\n        $running = false;\n        Co::join([$cid]);\n        Assert::greaterThanEq($count, 9);\n    });\n});\n\nAssert::eq(substr_count($pm->getChildOutput(), 'ping statistics'), 3);\nAssert::eq($pm->getChildExitStatus(), 0);\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_runtime/file_hook/a.inc",
    "content": "<?php\n/**\n * for file hook tests\n */\nfunction testA()\n{\n    echo \"A\\n\";\n}\n\ntestA();"
  },
  {
    "path": "tests/swoole_runtime/file_hook/async_file.phpt",
    "content": "--TEST--\nswoole_runtime/file_hook: async file\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n// disable file hook\nSwoole\\Runtime::enableCoroutine(SWOOLE_HOOK_ALL & ~SWOOLE_HOOK_FILE);\n\n$count = 0;\n$running = true;\n\nCo\\run(function () {\n    $fp = fopen(\"async.file://\" . TEST_IMAGE, \"r\");\n    $content = '';\n\n    Co\\go(function () {\n        global $count, $running;\n        while($running) {\n            usleep(1000);\n            $count++;\n        }   \n    });\n\n    while(!feof($fp)) {\n        $content .= fread($fp, 512);\n    }\n    fclose($fp);\n\n    global $count, $running;\n    $running = false;\n    // Iouring is too fast, no coroutine switching will occur, and the file has already been completed\n    if (!defined('SWOOLE_IOURING_DEFAULT')) {\n        Assert::true($count >= 1);\n    }\n    Swoole\\Runtime::enableCoroutine(false);\n    Assert::same(md5($content), md5_file(TEST_IMAGE));\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/file_hook/b.inc",
    "content": "<?php\n/**\n * for file hook tests\n */\nfunction testB()\n{\n    echo \"B\\n\";\n}\n\ntestB();"
  },
  {
    "path": "tests/swoole_runtime/file_hook/bug_4327.phpt",
    "content": "--TEST--\nswoole_runtime/file_hook: bug #4372\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Barrier;\nuse function Swoole\\Coroutine\\run;\n\nrequire __DIR__.'/../../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL);\n\nconst __ROOT_DIR = 'tmp/';\n\nfunction createDirectories($protocol = \"\")\n{\n    $barrier = Barrier::make();\n    $first = \"$protocol/\" . __ROOT_DIR . rand(0, 1000);\n    $second = \"/\" . rand(0, 1000);\n    $third = \"/\" . rand(0, 1000) . \"/\";\n\n    for ($i = 0; $i < 5; $i++) {\n        Coroutine::create(static function () use ($i, $first, $second, $third, $barrier) {\n            if (!mkdir($directory = $first . $second . $third . $i, 0755, true) && !is_dir($directory)) {\n                throw new Exception(\"create directory failed\");\n            }\n            rmdir($directory);\n        });\n    }\n    echo \"SUCCESS\" . PHP_EOL;\n\n    Barrier::wait($barrier);\n    rmdir($first . $second . $third);\n    rmdir($first . $second);\n    rmdir($first);\n}\n\nrun(function () {\n    createDirectories();\n    createDirectories(\"file://\");\n});\n\nif (defined('SWOOLE_THREAD')) {\n    echo \"SUCCESS\" . PHP_EOL;\n    echo \"SUCCESS\" . PHP_EOL;\n} else {\n    run(function () {\n        Swoole\\Runtime::enableCoroutine(false);\n        createDirectories();\n        createDirectories(\"file://\");\n    });\n}\n?>\n--EXPECT--\nSUCCESS\nSUCCESS\nSUCCESS\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_runtime/file_hook/bug_ftell_2g.phpt",
    "content": "--TEST--\nswoole_runtime/file_hook: fseek ftell file larger than 2G bug\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nconst SIZES = [\n    2147724448,\n    4 * 1024 * 1024 * 1024,\n];\n\n$noHookResults = [];\n$fp = fopen(__FILE__, 'r+');\nforeach(SIZES as $size) {\n    Assert::same(0, fseek($fp, $size));\n    $noHookResults[$size] = ftell($fp);\n}\nfclose($fp);\n\nSwoole\\Runtime::enableCoroutine();\n\nCo\\run(function () use ($noHookResults) {\n    $hookResults = [];\n    $fp = fopen(__FILE__, 'r+');\n    foreach(SIZES as $size) {\n        Assert::same(0, fseek($fp, $size));\n        $hookResults[$size] = ftell($fp);\n    }\n    Assert::same($hookResults, $noHookResults);\n    fclose($fp);\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/file_hook/bug_scandir.phpt",
    "content": "--TEST--\nswoole_runtime/file_hook: bug #3792\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$testDir = sys_get_temp_dir() . '/swoole_scandir_bug';\n\nif (!is_dir($testDir)) {\n    mkdir($testDir);\n}\nfor ($i = 0; $i++ < 3;) {\n    touch(\"{$testDir}/{$i}.txt\");\n}\n\n\\Swoole\\Runtime::enableCoroutine(true);\n\\Swoole\\Coroutine\\run(\n    function () use ($testDir) {\n        for ($i = 0; $i < MAX_CONCURRENCY; $i++) {\n            go(\n                function () use ($testDir) {\n                    $files = scandir($testDir);\n                    Assert::same($files, [\n                        '.',\n                        '..',\n                        '1.txt',\n                        '2.txt',\n                        '3.txt',\n                    ]);\n                }\n            );\n        }\n    }\n);\n\necho \"DONE\\n\";\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_runtime/file_hook/fgets.phpt",
    "content": "--TEST--\nswoole_runtime/file_hook: fgets\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$testFn = function () {\n    $fp = fopen(__FILE__, 'r');\n    $lines[] = fgets($fp);\n    fclose($fp);\n    return $lines;\n};\n\nSwoole\\Runtime::enableCoroutine(false);\n$lines = $testFn();\n\nCo\\run(function () use ($testFn, $lines) {\n    Swoole\\Runtime::enableCoroutine();\n    $lines_2 = $testFn();\n    Swoole\\Runtime::enableCoroutine(false);\n    Assert::eq($lines, $lines_2);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/file_hook/flock.phpt",
    "content": "--TEST--\nswoole_runtime/file_hook: flock\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\\Swoole\\Runtime::enableCoroutine();\n\nfunction test_flock()\n{\n    $fp = fopen('/tmp/flock.log', 'w+');\n    Assert::assert(flock($fp, LOCK_EX));\n    Co::sleep(0.001);\n    $ret = fwrite($fp, ($date = date('Y-m-d H:i:s')));\n    Assert::same($ret, strlen($date));\n    flock($fp, LOCK_UN);\n    fclose($fp);\n}\n\n$num = mt_rand(2, 16);\nswoole_async_set(['thread_num' => $num]);\ngo(function () use ($num) {\n    for ($i = $num; $i--;) {\n        go('test_flock');\n    }\n});\nSwoole\\Event::wait();\necho \"DONE\\n\";\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_runtime/file_hook/fread.phpt",
    "content": "--TEST--\nswoole_runtime/file_hook: fread\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\ngo(function () {\n    $fp = fopen(__FILE__, 'r');\n    echo \"open\\n\";\n    $data = fread($fp, 1024);\n    echo \"read\\n\";\n    Swoole\\Runtime::enableCoroutine(false);\n    Assert::assert(!empty($data));\n    Assert::same(md5($data), md5_file(__FILE__));\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nopen\nread\n"
  },
  {
    "path": "tests/swoole_runtime/file_hook/include.phpt",
    "content": "--TEST--\nswoole_runtime/file_hook: include\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\ngo(function () {\n    include __DIR__.\"/a.inc\";\n    include __DIR__.\"/b.inc\";\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\nA\nB\n"
  },
  {
    "path": "tests/swoole_runtime/file_hook/include_2.phpt",
    "content": "--TEST--\nswoole_runtime/file_hook: include\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nspl_autoload_register(function ($class) {\n    if ($class == 'SwooleTestClassA') {\n        require TESTS_ROOT_PATH . '/include/api/test_classes/A.php';\n    } elseif ($class == 'SwooleTestClassB') {\n        require TESTS_ROOT_PATH . '/include/api/test_classes/B.php';\n    }\n});\n\nSwoole\\Coroutine\\run(function () {\n    for ($i = 0; $i < 4; $i++) {\n        go(function () use ($i) {\n            echo \"$i-1\\n\";\n            Assert::eq(Swoole\\Runtime::getHookFlags(), SWOOLE_HOOK_ALL);\n            if ($i % 2 == 1) {\n                Assert::assert(class_exists(SwooleTestClassB::class));\n            } else {\n                Assert::assert(class_exists(SwooleTestClassA::class));\n            }\n            echo \"$i-2\\n\";\n        });\n    }\n});\n\n?>\n--EXPECT--\n0-1\n0-2\n1-1\n1-2\n2-1\n2-2\n3-1\n3-2\n"
  },
  {
    "path": "tests/swoole_runtime/file_hook/lock_ex.phpt",
    "content": "--TEST--\nswoole_runtime/file_hook: file_put_contents with LOCK_EX\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\nconst FILE = __DIR__ . '/test.data';\n\n$n = 10;\nwhile ($n--) {\n    go(function () use ($n) {\n        $data = str_repeat('A', 8192 * 100);\n        file_put_contents(FILE, $data, LOCK_EX);\n        echo \"$n OK\\n\";\n    });\n}\n\nSwoole\\Event::wait();\nunlink(FILE);\n?>\n--EXPECTF--\n%d OK\n%d OK\n%d OK\n%d OK\n%d OK\n%d OK\n%d OK\n%d OK\n%d OK\n%d OK\n"
  },
  {
    "path": "tests/swoole_runtime/file_hook/lock_nb_1.phpt",
    "content": "--TEST--\nswoole_runtime/file_hook: file_put_contents with LOCK_NB\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\nconst FILE = __DIR__ . '/test.data';\n\nSwoole\\Runtime::enableCoroutine();\n\n$cid = 0;\n\ngo(function () use (&$cid)  {\n    $fp = fopen(FILE, 'w+');\n    Assert::true(flock($fp, LOCK_EX));\n    Co::resume($cid);\n    Co::sleep(0.01);\n    flock($fp, LOCK_UN);\n    fclose($fp);\n});\n\ngo(function () use (&$cid) {\n    $cid = Co::getCid();\n    Co::yield();\n    $fp = fopen(FILE, 'w+');\n    Assert::same(flock($fp, LOCK_NB | LOCK_EX), false);\n});\n\nSwoole\\Event::wait();\nunlink(FILE);\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_runtime/file_hook/lock_nb_2.phpt",
    "content": "--TEST--\nswoole_runtime/file_hook: file_put_contents with LOCK_NB[2]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\nconst FILE = __DIR__ . '/test.data';\n\nSwoole\\Runtime::enableCoroutine();\n\ngo(function () {\n    $fp = fopen(FILE, 'w+');\n    Assert::same(flock($fp, LOCK_NB | LOCK_EX), true);\n    echo \"[1] LOCK\\n\";\n    Co::sleep(0.01);\n    echo \"[1] UNLOCK\\n\";\n    flock($fp, LOCK_UN);\n});\n\ngo(function () {\n    Co::sleep(0.001);\n    $fp = fopen(FILE, 'w+');\n    echo \"[2] LOCK yield\\n\";\n    Assert::assert(flock($fp, LOCK_EX));\n    echo \"[2] LOCK resume\\n\";\n    flock($fp, LOCK_UN);\n    co:sleep(0.002);\n    echo \"[2] UNLOCK\\n\";\n    fclose($fp);\n});\n\nSwoole\\Event::wait();\nunlink(FILE);\n?>\n--EXPECTF--\n[1] LOCK\n[2] LOCK yield\n[1] UNLOCK\n[2] LOCK resume\n[2] UNLOCK\n"
  },
  {
    "path": "tests/swoole_runtime/file_hook/lock_sh.phpt",
    "content": "--TEST--\nswoole_runtime/file_hook: file_put_contents with LOCK_SH\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\n$n = 10;\nwhile ($n--) {\n    go(function () use ($n) {\n        $data = readfile_with_lock(TEST_IMAGE);\n        Assert::same(md5_file(TEST_IMAGE), md5($data));\n        echo \"$n OK\\n\";\n    });\n}\n\nSwoole\\Event::wait();\n?>\n--EXPECTF--\n%d OK\n%d OK\n%d OK\n%d OK\n%d OK\n%d OK\n%d OK\n%d OK\n%d OK\n%d OK\n"
  },
  {
    "path": "tests/swoole_runtime/file_hook/open_basedir.phpt",
    "content": "--TEST--\nswoole_runtime/file_hook: support open_basedir config\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\nrun(function() {\n    ini_set(\"open_basedir\",\"/tmp/:/tmp/tes\");\n    mkdir(\"/home/test/\",0755);\n    var_dump(mkdir(\"/tmp/test/\",0755));\n    var_dump(mkdir(\"/tmp/test1/\",0755));\n    var_dump(mkdir(\"/tmp/test/test\",0755, true));\n    rmdir(\"/tmp/test/test\");\n    rmdir(\"/tmp/test/\");\n    rmdir(\"/tmp/test1/\");\n});\n?>\n--EXPECTF--\n\nWarning: mkdir(): open_basedir restriction in effect. File(%s) is not within the allowed path(s): (%s) in %s on line 7\nbool(true)\nbool(true)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_runtime/file_hook/read.phpt",
    "content": "--TEST--\nswoole_runtime/file_hook: read file\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nfunction readfile_co($file)\n{\n    $fp = fopen($file, 'r+');\n    $content = '';\n    while (!feof($fp))\n    {\n        $data = fread($fp, 1024);\n        $content .= $data;\n    }\n    return $content;\n}\n\n$files = array(\n    [\n        'file' => SOURCE_ROOT_PATH . '/README.md',\n        'hash'  => '',\n    ],\n    [\n        'file' => SOURCE_ROOT_PATH . '/package.xml',\n        'hash'  => '',\n    ],\n    [\n        'file' => TEST_IMAGE,\n        'hash'  => '',\n    ],\n);\n\nforeach ($files as &$f)\n{\n    $f['hash'] = md5_file($f['file']);\n}\n\nSwoole\\Runtime::enableCoroutine();\n\nforeach ($files as $k => $v)\n{\n    go(function () use ($v, $k) {\n        $content = readfile_co($v['file']);\n        Assert::same(md5($content), $v['hash']);\n    });\n}\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/file_hook/readdir.phpt",
    "content": "--TEST--\nswoole_runtime/file_hook: readdir\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\n\n$list0 = scandir('/tmp');\nsort($list0);\n\nCo\\run(function () use ($list0) {\n    $handle = opendir('/tmp');\n    Assert::notEmpty($handle);\n    $list1 = [];\n    while (false !== ($entry = readdir($handle))) {\n        $list1[] = \"$entry\";\n    }\n    closedir($handle);\n    sort($list1);\n    Assert::eq($list0, $list1);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/file_lock/async_file.phpt",
    "content": "--TEST--\nswoole_runtime/file_lock: async file\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n// disable file hook\nSwoole\\Runtime::enableCoroutine(SWOOLE_HOOK_ALL & ~SWOOLE_HOOK_FILE);\n\n$FILE = __DIR__ . '/test.data';\n$startTime = microtime(true);\n\ngo(function () use ($startTime, $FILE) {\n    $f = fopen(\"async.file://\" . $FILE, 'w+');\n    flock($f, LOCK_EX);\n    co::sleep(0.1);\n    flock($f, LOCK_UN);\n\n    flock($f, LOCK_SH);\n    flock($f, LOCK_UN);\n    Assert::assert((microtime(true) - $startTime) < 1);\n});\n\ngo(function () use ($FILE) {\n    $f = fopen(\"async.file://\" . $FILE, 'w+');\n    flock($f, LOCK_SH);\n    co::sleep(2);\n    flock($f, LOCK_UN);\n});\n\nSwoole\\Event::wait();\nunlink($FILE);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/file_lock/lock_ex.phpt",
    "content": "--TEST--\nswoole_runtime/file_lock: file_lock_1\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n\\Swoole\\Runtime::enableCoroutine();\nconst FILE = __DIR__ . '/test.data';\n$startTime = microtime(true);\ngo(function () use ($startTime) {\n    $f = fopen(FILE, 'w+');\n    flock($f, LOCK_EX);\n    co::sleep(0.1);\n    flock($f, LOCK_UN);\n\n    flock($f, LOCK_SH);\n    flock($f, LOCK_UN);\n    Assert::assert((microtime(true) - $startTime) < 1);\n});\ngo(function () {\n    $f = fopen(FILE, 'w+');\n    flock($f, LOCK_SH);\n    co::sleep(2);\n    flock($f, LOCK_UN);\n});\nSwoole\\Event::wait();\nunlink(FILE);\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_runtime/file_lock/lock_nb.phpt",
    "content": "--TEST--\nswoole_runtime/file_lock: lock_nb\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nconst FILE = __DIR__ . '/test.data';\n\\Swoole\\Runtime::enableCoroutine();\ngo(function () {\n    $fp = fopen(FILE, 'w+');\n    Assert::assert(flock($fp, LOCK_EX));\n    $fp2 = fopen(FILE, 'w+');\n    Assert::assert(!flock($fp2, LOCK_EX | LOCK_NB));\n});\nSwoole\\Event::wait();\nunlink(FILE);\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_runtime/file_lock/lock_sh_1.phpt",
    "content": "--TEST--\nswoole_runtime/file_lock: lock_sh_1\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nconst FILE = __DIR__ . '/test.data';\n\\Swoole\\Runtime::enableCoroutine();\n$startTime = microtime(true);\ngo(function () {\n    $f = fopen(FILE, 'w+');\n    $ret = flock($f, LOCK_EX);\n    Assert::assert($ret);\n    co::sleep(0.3);\n    $ret = flock($f, LOCK_UN);\n    Assert::assert($ret);\n});\n\ngo(function () {\n    $f = fopen(FILE, 'w+');\n    $ret = flock($f, LOCK_SH);\n    Assert::assert($ret);\n    co::sleep(2);\n    $ret = flock($f, LOCK_UN);\n    Assert::assert($ret);\n});\n\ngo(function () use ($startTime) {\n    $f = fopen(FILE, 'w+');\n    $ret = flock($f, LOCK_SH);\n    Assert::assert($ret);\n    Assert::assert((microtime(true) - $startTime) < 1);\n    $ret = flock($f, LOCK_UN);\n    Assert::assert($ret);\n});\nSwoole\\Event::wait();\nunlink(FILE);\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_runtime/ftp_fopen_wrapper.phpt",
    "content": "--TEST--\nswoole_runtime: ftp fopen  wrapper\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_in_ci('failure');\nskip_if_no_ftp();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nCo\\run(function () {\n    $file = FTP_TEST_FILE;\n    unlink(build_ftp_url($file));\n\n    $n = 8192;\n    $fp = fopen(build_ftp_url($file), \"w\");\n    Assert::notEmpty($fp);\n    $bytes = random_bytes($n);\n    Assert::eq(fwrite($fp, $bytes), strlen($bytes));\n    fsync($fp);\n    fclose($fp);\n\n    $fp = fopen(build_ftp_url($file), \"r\");\n    Assert::notEmpty($fp);\n\n    $rbytes = '';\n    while (!feof($fp) and strlen($rbytes) < strlen($bytes)) {\n       $read = fread($fp, $n);\n       Assert::notEmpty($read);\n       $rbytes .= $read;\n    }\n\n    Assert::eq(strlen($read), strlen($bytes));\n    Assert::eq($read, $bytes);\n    fclose($fp);\n\n    echo \"DONE\\n\";\n});\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_runtime/get_hook_flags.phpt",
    "content": "--TEST--\nswoole_runtime: get hook flags\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\nAssert::same(Swoole\\Runtime::getHookFlags(), SWOOLE_HOOK_ALL);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/gethostbyname.phpt",
    "content": "--TEST--\nswoole_runtime: sleep\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$host = 'www.tsinghua.edu.cn';\n$ip1 = gethostbyname($host);\nSwoole\\Runtime::enableCoroutine();\nSwoole\\Coroutine\\run(function () use($ip1, $host) {\n    $ip2 = gethostbyname($host);\n    Assert::same($ip1, $ip2);\n\n    $iplist = gethostbynamel('www.taobao.com');\n    Assert::greaterThanEq(count($iplist), 1);\n});\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_runtime/hook_default.phpt",
    "content": "--TEST--\nswoole_runtime: enable hook by default\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nCo\\run(function () {\n    Assert::eq(Swoole\\Runtime::getHookFlags(), SWOOLE_HOOK_ALL);\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/hook_enable_coroutine.phpt",
    "content": "--TEST--\nswoole_runtime: enableCoroutine\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine(SWOOLE_HOOK_TCP);\n\nCo\\run(function () {\n    Assert::eq(Swoole\\Runtime::getHookFlags(), SWOOLE_HOOK_TCP);\n});\n\nCo\\run(function () {\n    Assert::eq(Swoole\\Runtime::getHookFlags(), SWOOLE_HOOK_TCP);\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/hook_set_flags.phpt",
    "content": "--TEST--\nswoole_runtime: set hook flags\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nCo::set(['hook_flags' => 0]);\n\nCo\\run(function () {\n    Assert::eq(Swoole\\Runtime::getHookFlags(), 0);\n});\n\nCo::set(['hook_flags' => SWOOLE_HOOK_CURL]);\n\nCo\\run(function () {\n    Assert::eq(Swoole\\Runtime::getHookFlags(), SWOOLE_HOOK_CURL);\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/library.phpt",
    "content": "--TEST--\nswoole_runtime: library\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$useShortName = !in_array(\n    strtolower(trim(str_replace('0', '',\n        ini_get_all('swoole')['swoole.use_shortname']['local_value']\n    ))),\n    ['', 'off', 'false'],\n    true\n);\nphpt_var_dump($useShortName, SWOOLE_USE_SHORTNAME);\nvar_dump(Assert::eq($useShortName, SWOOLE_USE_SHORTNAME));\n?>\n--EXPECT--\nbool(true)\n"
  },
  {
    "path": "tests/swoole_runtime/nonblock.phpt",
    "content": "--TEST--\nswoole_runtime: nonblock\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nCo\\run(function () {\n    $socket = stream_socket_client(\n        'tcp://www.baidu.com:80',\n        $errorCode,\n        $errorMessage,\n        1,\n        STREAM_CLIENT_CONNECT\n    );\n    stream_set_timeout($socket, 5);\n    stream_set_blocking($socket, false);\n    $receivedData = fread($socket, 1);\n    Assert::assert($receivedData === '');\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/out_of_coroutine.phpt",
    "content": "--TEST--\nswoole_runtime: out of coroutine\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine(SWOOLE_HOOK_TCP);\n\n$out = file_get_contents('http://www.baidu.com/');\nAssert::contains($out, 'About Baidu');\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/pdo.phpt",
    "content": "--TEST--\nswoole_runtime: pdo\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_pdo_not_support_mysql8();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n$count = 0;\n\nfunction mysql_sleep(float $time)\n{\n    $pdo = new PDO(\n        \"mysql:host=\" . MYSQL_SERVER_HOST . \";port=\" . MYSQL_SERVER_PORT . \";dbname=\" . MYSQL_SERVER_DB . \";charset=utf8\",\n        MYSQL_SERVER_USER, MYSQL_SERVER_PWD\n    );\n    $pdo->exec(\"SELECT sleep({$time})\");\n    if (Assert::assert($pdo->errorCode() ===  PDO::ERR_NONE)){\n        global $count;\n        $count++;\n    }\n}\n\nfunction onRequest()\n{\n    mysql_sleep(.1);\n}\n\n$start = microtime(true);\nfor ($i = MAX_CONCURRENCY_LOW; $i--;) {\n    go('onRequest');\n}\nSwoole\\Event::wait();\nAssert::same($count, MAX_CONCURRENCY_LOW);\nAssert::assert((microtime(true) - $start) < .5);\n//关闭协程，否则会致命错误\nSwoole\\Runtime::enableCoroutine(false);\nmysql_sleep(.1); //block IO\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_runtime/persistent.phpt",
    "content": "--TEST--\nswoole_runtime: socket persistent then destruct\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$redis = new Redis();\n$redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT);\n$redis->rawCommand('CLIENT', 'KILL', 'TYPE', 'normal');\n$redis->close();\nusleep(100);\n\nSwoole\\Runtime::enableCoroutine();\n\n$map = [];\n\n$timer_id = Swoole\\Timer::tick(1000 / MAX_CONCURRENCY_MID, function () use (&$map) {\n    $redis = new Redis();\n    $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT);\n    Assert::assert($redis->set('foo', 'bar'));\n    Assert::same($redis->get('foo'), 'bar');\n    $map[] = $redis;\n});\n\ngo(function () use ($timer_id, &$map) {\n    Co::sleep(1);\n    Swoole\\Timer::clear($timer_id);\n    $redis = new Redis();\n    $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT);\n    $info = (array)$redis->info('clients');\n    phpt_var_dump($info);\n    Assert::same($info['connected_clients'], count($map) + 1, var_dump_return($info));\n    $map = []; // destruct\n    Co::sleep(0.001); // defer close\n    switch_process();\n    $info = (array)$redis->info('clients');\n    phpt_var_dump($info);\n    Assert::same($info['connected_clients'], 1, var_dump_return($info));\n    echo \"DONE\\n\";\n});\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_runtime/proc/1.phpt",
    "content": "--TEST--\nswoole_runtime/proc: proc_open\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php # vim:syn=php\nif (!is_executable(\"/bin/cat\")) exit('skip');\nif (!function_exists(\"proc_open\")) exit('skip proc_open() is not available');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\ngo(function() {\n    $ds = array(\n        0 => array(\"pipe\", \"r\"),\n        1 => array(\"pipe\", \"w\"),\n        2 => array(\"pipe\", \"w\")\n    );\n\n    $cat = proc_open(\n        \"/bin/cat\",\n        $ds,\n        $pipes\n    );\n\n    proc_close($cat);\n    echo \"I didn't segfault!\\n\";\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nI didn't segfault!\n"
  },
  {
    "path": "tests/swoole_runtime/proc/2.phpt",
    "content": "--TEST--\nswoole_runtime/proc: proc_open\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n<?php\nif (!is_executable('/bin/sleep')) exit('skip no sleep');\nif (getenv('SKIP_SLOW_TESTS')) exit('skip slow test');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\ngo(function() {\n    $ds = array(array('pipe', 'r'));\n\n    $cat = proc_open(\n        '/bin/sleep 2',\n        $ds,\n        $pipes\n    );\n\n    usleep(20000); // let the OS run the sleep process before sending the signal\n\n    var_dump(proc_terminate($cat, 0)); // status check\n    usleep(20000);\n    var_dump(proc_get_status($cat));\n\n    var_dump(proc_terminate($cat)); // now really quit it\n    usleep(20000);\n    var_dump(proc_get_status($cat));\n\n    proc_close($cat);\n\n    echo \"Done!\\n\";\n});\nSwoole\\Event::wait();\n\n?>\n--EXPECTF--\nbool(true)\narray(8) {\n  [\"command\"]=>\n  string(12) \"/bin/sleep 2\"\n  [\"pid\"]=>\n  int(%d)\n  [\"running\"]=>\n  bool(true)\n  [\"signaled\"]=>\n  bool(false)\n  [\"stopped\"]=>\n  bool(false)\n  [\"exitcode\"]=>\n  int(-1)\n  [\"termsig\"]=>\n  int(0)\n  [\"stopsig\"]=>\n  int(0)\n}\nbool(true)\narray(8) {\n  [\"command\"]=>\n  string(12) \"/bin/sleep 2\"\n  [\"pid\"]=>\n  int(%d)\n  [\"running\"]=>\n  bool(false)\n  [\"signaled\"]=>\n  bool(true)\n  [\"stopped\"]=>\n  bool(false)\n  [\"exitcode\"]=>\n  int(-1)\n  [\"termsig\"]=>\n  int(15)\n  [\"stopsig\"]=>\n  int(0)\n}\nDone!\n"
  },
  {
    "path": "tests/swoole_runtime/proc/3.phpt",
    "content": "--TEST--\nswoole_runtime/proc: proc_open() with > 16 pipes\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\ninclude_once dirname(__FILE__) . \"/proc_open_pipes.inc\";\nSwoole\\Runtime::enableCoroutine();\n\ngo(function() {\n    for ($i = 3; $i<= 30; $i++) {\n        $spec[$i] = array('pipe', 'w');\n    }\n    $php = getenv(\"TEST_PHP_EXECUTABLE\");\n    $callee = create_sleep_script();\n    proc_open(\"$php -n $callee\", $spec, $pipes);\n\n    var_dump(count($spec));\n    var_dump($pipes);\n});\nSwoole\\Event::wait();\n?>\n--CLEAN--\n<?php\ninclude_once dirname(__FILE__) . \"/proc_open_pipes.inc\";\n\nunlink_sleep_script();\n\n?>\n--EXPECTF--\nint(28)\narray(28) {\n  [3]=>\n  resource(%d) of type (Unknown)\n  [4]=>\n  resource(%d) of type (Unknown)\n  [5]=>\n  resource(%d) of type (Unknown)\n  [6]=>\n  resource(%d) of type (Unknown)\n  [7]=>\n  resource(%d) of type (Unknown)\n  [8]=>\n  resource(%d) of type (Unknown)\n  [9]=>\n  resource(%d) of type (Unknown)\n  [10]=>\n  resource(%d) of type (Unknown)\n  [11]=>\n  resource(%d) of type (Unknown)\n  [12]=>\n  resource(%d) of type (Unknown)\n  [13]=>\n  resource(%d) of type (Unknown)\n  [14]=>\n  resource(%d) of type (Unknown)\n  [15]=>\n  resource(%d) of type (Unknown)\n  [16]=>\n  resource(%d) of type (Unknown)\n  [17]=>\n  resource(%d) of type (Unknown)\n  [18]=>\n  resource(%d) of type (Unknown)\n  [19]=>\n  resource(%d) of type (Unknown)\n  [20]=>\n  resource(%d) of type (Unknown)\n  [21]=>\n  resource(%d) of type (Unknown)\n  [22]=>\n  resource(%d) of type (Unknown)\n  [23]=>\n  resource(%d) of type (Unknown)\n  [24]=>\n  resource(%d) of type (Unknown)\n  [25]=>\n  resource(%d) of type (Unknown)\n  [26]=>\n  resource(%d) of type (Unknown)\n  [27]=>\n  resource(%d) of type (Unknown)\n  [28]=>\n  resource(%d) of type (Unknown)\n  [29]=>\n  resource(%d) of type (Unknown)\n  [30]=>\n  resource(%d) of type (Unknown)\n}\n"
  },
  {
    "path": "tests/swoole_runtime/proc/4.phpt",
    "content": "--TEST--\nswoole_runtime/proc: proc_open() with no pipes\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\ninclude_once dirname(__FILE__) . \"/proc_open_pipes.inc\";\nSwoole\\Runtime::enableCoroutine();\n\ngo(function() {\n\n    $spec = array();\n\n    $php = getenv(\"TEST_PHP_EXECUTABLE\");\n    $callee = create_sleep_script();\n    proc_open(\"$php -n $callee\", $spec, $pipes);\n\n    var_dump(count($spec));\n    var_dump($pipes);\n});\nSwoole\\Event::wait();\n\n?>\n--CLEAN--\n<?php\ninclude_once dirname(__FILE__) . \"/proc_open_pipes.inc\";\n\nunlink_sleep_script();\n\n?>\n--EXPECTF--\nint(0)\narray(0) {\n}\n"
  },
  {
    "path": "tests/swoole_runtime/proc/5.phpt",
    "content": "--TEST--\nswoole_runtime/proc: proc_open() with invalid pipes\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\ninclude_once dirname(__FILE__) . \"/proc_open_pipes.inc\";\n\nSwoole\\Runtime::enableCoroutine();\n\ngo(function() {\n\n    for ($i = 3; $i<= 5; $i++) {\n        $spec[$i] = array('pipe', 'w');\n    }\n\n    $php = getenv(\"TEST_PHP_EXECUTABLE\");\n    $callee = create_sleep_script();\n\n    $spec[$i] = array('pi');\n    proc_open(\"$php -n $callee\", $spec, $pipes);\n\n    $spec[$i] = 1;\n    proc_open(\"$php -n $callee\", $spec, $pipes);\n\n    $spec[$i] = array('pipe', \"test\");\n    proc_open(\"$php -n $callee\", $spec, $pipes);\n    var_dump($pipes);\n\n    $spec[$i] = array('file', \"test\", \"z\");\n    proc_open(\"$php -n $callee\", $spec, $pipes);\n    var_dump($pipes);\n\n    echo \"END\\n\";\n});\nSwoole\\Event::wait();\n\n?>\n--CLEAN--\n<?php\ninclude_once dirname(__FILE__) . \"/proc_open_pipes.inc\";\n\nunlink_sleep_script();\n\n?>\n--EXPECTF--\nWarning: proc_open(): pi is not a valid descriptor spec/mode in %s on line %d\n\nWarning: proc_open(): Descriptor item must be either an array or a File-Handle in %s on line %d\narray(4) {\n  [3]=>\n  resource(%d) of type (Unknown)\n  [4]=>\n  resource(%d) of type (Unknown)\n  [5]=>\n  resource(%d) of type (Unknown)\n  [6]=>\n  resource(%d) of type (Unknown)\n}\n\nWarning: proc_open(test): %s to open stream: %s in %s on line %d\narray(4) {\n  [3]=>\n  resource(%d) of type (Unknown)\n  [4]=>\n  resource(%d) of type (Unknown)\n  [5]=>\n  resource(%d) of type (Unknown)\n  [6]=>\n  resource(%d) of type (Unknown)\n}\nEND\n"
  },
  {
    "path": "tests/swoole_runtime/proc/close_after_wait.phpt",
    "content": "--TEST--\nswoole_runtime/proc: proc_close() after wait\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\ninclude_once dirname(__FILE__) . \"/proc_open_pipes.inc\";\nSwoole\\Runtime::enableCoroutine();\n\nCo\\run(function() {\n    $descriptorspec = array(\n        0 => array(\"pipe\", \"/dev/null\"),\n        1 => array(\"pipe\", \"/dev/null\"),\n        2 => array(\"pipe\", \"/dev/null\")\n    );\n\n    $proc = proc_open('/bin/sleep 30', $descriptorspec, $pipes);\n    Assert::notEmpty($proc);\n    echo \"wait begin\\n\";\n\n    go(function() use($proc) {\n        usleep(100000);\n        proc_terminate($proc);\n    });\n\n    $info = Co\\System::wait();\n    Assert::notEmpty($info);\n    echo \"wait end\\n\";\n    proc_close($proc);\n    echo \"proc_close end\\n\";\n});\n?>\n--EXPECTF--\nwait begin\nwait end\nproc_close end\n"
  },
  {
    "path": "tests/swoole_runtime/proc/proc_open_pipes.inc",
    "content": "<?php\n\nfunction create_sleep_script()\n{\n\t$fname = dirname(__FILE__) . DIRECTORY_SEPARATOR . \"proc_open_pipes_sleep.php\";\n\n\tif (!file_exists($fname)) {\n\t\tfile_put_contents($fname, \"<?php\\nsleep(1);\\n\");\n\t}\n\n\treturn $fname;\n}\n\nfunction unlink_sleep_script()\n{\n\t$fname = dirname(__FILE__) . DIRECTORY_SEPARATOR . \"proc_open_pipes_sleep.php\";\n\n\tif (file_exists($fname)) {\n\t\tunlink($fname);\n\t}\n}\n"
  },
  {
    "path": "tests/swoole_runtime/redis_connect.phpt",
    "content": "--TEST--\nswoole_runtime: hook stream redis connect\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_class_not_exist('Redis');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    $redis = new Redis;\n    Assert::assert($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT));\n    $redis->get(\"key\");\n});\ngo(function () {\n    $redis = new Redis;\n    Assert::assert($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT));\n    $redis->get(\"key\");\n});\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_runtime/redis_pconnect.phpt",
    "content": "--TEST--\nswoole_runtime: hook stream redis pconnect\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_class_not_exist('Redis');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    $redis = new Redis;\n    Assert::assert($redis->pconnect(REDIS_SERVER_HOST, REDIS_SERVER_PORT));\n    $redis->get(\"key\");\n});\ngo(function () {\n    $redis = new Redis;\n    Assert::assert($redis->pconnect(REDIS_SERVER_HOST, REDIS_SERVER_PORT));\n    $redis->get(\"key\");\n});\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_runtime/remote_object/dns.phpt",
    "content": "--TEST--\nswoole_runtime/remote_object: dns\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\nskip_if_in_ci('failure');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\n\nrun(function ()  {\n    Assert::true(checkdnsrr('www.baidu.com', 'A'));\n    Assert::true(dns_check_record('www.baidu.com', 'A'));\n\n    Assert::true(checkdnsrr('qq.com', 'MX'));\n    $mxhosts = [];\n    Assert::true(getmxrr('qq.com', $mxhosts));\n    Assert::greaterThanEq(count($mxhosts), 1);\n\n    Assert::true(getmxrr('qq.com', $mxhosts, $mxweights));\n    Assert::greaterThanEq(count($mxweights), 1);\n\n    $rs = dns_get_record('www.baidu.com', DNS_A);\n    Assert::greaterThanEq(count($rs), 1);\n    Assert::eq(gethostbyaddr('127.0.0.1'), 'localhost');\n});\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_runtime/sento.phpt",
    "content": "--TEST--\nswoole_runtime: stream_socket_sendto\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\ngo(function () {\n    $fp = stream_socket_client(\"tcp://www.baidu.com:80\", $errno, $errstr, 30);\n    if (!$fp) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        $http = \"GET / HTTP/1.0\\r\\nAccept: */*User-Agent: Lowell-Agent\\r\\nHost: www.baidu.com\\r\\nConnection: Close\\r\\n\\r\\n\";\n        stream_socket_sendto($fp, $http);\n        $content = '';\n        while (!feof($fp)) {\n            $content .= fread($fp, 1024);\n        }\n        fclose($fp);\n        Assert::assert(strpos($content,'map.baidu.com') !== false);\n    }\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/set_hook_flags.phpt",
    "content": "--TEST--\nswoole_runtime: set hook flags\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nRuntime::setHookFlags(SWOOLE_HOOK_ALL);\n\n$load = function ($name) {\n    $flags = Runtime::getHookFlags();\n    Runtime::setHookFlags(0);\n    usleep(10000);\n    eval('class InexistentClass{}');\n    Runtime::setHookFlags($flags);\n};\n\nspl_autoload_register($load);\nCo\\run(function () {\n\n    go(function () {\n        new InexistentClass;\n    });\n    go(function () {\n        new InexistentClass;\n    });\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/sleep.phpt",
    "content": "--TEST--\nswoole_runtime: sleep\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    // sleep\n    $s = microtime(true);\n    Assert::eq(sleep(1), 0);\n    time_approximate(1, microtime(true) - $s);\n    Assert::eq(sleep(0), 0);\n    try {\n        sleep(-1);\n    } catch (Throwable $e) {\n        Assert::contains($e->getMessage(), 'must be greater than or equal to 0');\n    }\n\n    // usleep\n    $s = microtime(true);\n    $t = ms_random(0.01, 0.1);\n    usleep($t * 1000 * 1000);\n    time_approximate($t, microtime(true) - $s);\n    usleep(0);\n    try {\n         usleep(-1);\n    } catch (Throwable $e) {\n        Assert::contains($e->getMessage(), 'must be greater than or equal to 0');\n    }\n\n    // time_nanosleep\n    try {\n        time_nanosleep(-1, 1);\n    } catch (Throwable $e) {\n        Assert::contains($e->getMessage(), 'must be greater than or equal to 0');\n    }\n\n    Assert::true(time_nanosleep(0, 1));\n    Assert::true(time_nanosleep(0, 1000 * 1000));\n\n    // time_sleep_until\n    $s = microtime(true);\n    Assert::true(time_sleep_until($s + 1));\n    time_approximate(1, microtime(true) - $s);\n    Assert::false(time_sleep_until($s));\n});\necho \"NON-BLOCKED\\n\";\nSwoole\\Event::wait();\necho \"\\nDONE\\n\";\n?>\n--EXPECTF--\nNON-BLOCKED\n\nWarning: time_sleep_until(): Argument #1 ($timestamp) must be greater than or equal to the current time in %s on line %d\n\nDONE\n"
  },
  {
    "path": "tests/swoole_runtime/sleep_yield.phpt",
    "content": "--TEST--\nswoole_runtime: sleep yield\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\nCo\\run(function () {\n    $timer1 = Swoole\\Timer::tick(1000, function () {\n        var_dump('a');\n    });\n    go(function () use ($timer1) {\n        for ($i = 0; $i < 10; ++$i) {\n            usleep(100000);\n        }\n        var_dump('b');\n        Swoole\\Timer::clear($timer1);\n    });\n});\n?>\n--EXPECTF--\nstring(1) \"a\"\nstring(1) \"b\"\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/bug46360.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: Bug 46360 - TCP_NODELAY constant (sock_get_option, sock_set_option)\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php if (!extension_loaded('sockets')) die('skip sockets extension not loaded'); ?>\n--CREDITS--\nFlorian Anderiasch\nfa@php.net\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n    var_dump(TCP_NODELAY);\n});\n?>\n--EXPECTF--\nint(%d)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/bug49341.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: add SO_REUSEPORT support for socket_set_option()\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('skip sockets extension not available.');\n}\nif (PHP_OS !== 'Darwin' && false === strpos(PHP_OS, 'BSD')) {\n    die('skip is not *BSD.');\n}?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\nvar_dump(defined('SO_REUSEPORT'));\n});\n?>\n--EXPECT--\nbool(true)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/bug63000.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: Multicast on OSX\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('skip sockets extension not available.');\n}?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);\nsocket_bind($socket, '0.0.0.0', 31057);\n\n$so = socket_set_option($socket, IPPROTO_IP, MCAST_JOIN_GROUP, array(\n    \"group\" => '224.0.0.251',\n    \"interface\" => 0,\n));\nvar_dump($so);\n});\n?>\n--EXPECT--\nbool(true)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/bug76839.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: socket_recvfrom may return an invalid 'from' address on MacOS\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (strtolower(substr(PHP_OS, 0, 3)) === 'win') {\n    die('skip not valid for Windows.');\n}\nif (!extension_loaded('sockets')) {\n    die('skip sockets extension not available.');\n}?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n\n// This bug only occurs when a specific portion of memory is unclean.\n// Unfortunately, looping around 10 times and using random paths is the\n// best way I could manage to reproduce this problem without modifying php itself :-/\n\nfor ($i = 0; $i < 10; $i++) {\n    $senderSocketPath = '/tmp/' . substr(md5(rand()), 0, rand(8, 16)) . '.sock';\n    $senderSocket = socket_create(AF_UNIX, SOCK_DGRAM, 0);\n    socket_bind($senderSocket, $senderSocketPath);\n\n    $receiverSocketPath = '/tmp/' . substr(md5(rand()), 0, rand(8, 16)) . '.sock';\n    $receiverSocket = socket_create(AF_UNIX, SOCK_DGRAM, 0);\n    socket_bind($receiverSocket, $receiverSocketPath);\n\n    // Send message from sender socket to receiver socket\n    socket_sendto($senderSocket, 'Ping!', 5, 0, $receiverSocketPath);\n\n    // Receive message on receiver socket\n    $from = '';\n    $message = '';\n    socket_recvfrom($receiverSocket, $message, 65535, 0, $from);\n    echo \"Received '$message'\\n\";\n\n    // Respond to the sender using the 'from' address from socket_recvfrom\n    socket_sendto($receiverSocket, 'Pong!', 5, 0, $from);\n    echo \"Responded to sender with 'Pong!'\\n\";\n\n    socket_close($receiverSocket);\n    unlink($receiverSocketPath);\n    socket_close($senderSocket);\n}\n});\n?>\n--EXPECT--\nReceived 'Ping!'\nResponded to sender with 'Pong!'\nReceived 'Ping!'\nResponded to sender with 'Pong!'\nReceived 'Ping!'\nResponded to sender with 'Pong!'\nReceived 'Ping!'\nResponded to sender with 'Pong!'\nReceived 'Ping!'\nResponded to sender with 'Pong!'\nReceived 'Ping!'\nResponded to sender with 'Pong!'\nReceived 'Ping!'\nResponded to sender with 'Pong!'\nReceived 'Ping!'\nResponded to sender with 'Pong!'\nReceived 'Ping!'\nResponded to sender with 'Pong!'\nReceived 'Ping!'\nResponded to sender with 'Pong!'\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/ipv4loop.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: IPv4 Loopback test\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\n    if (!extension_loaded('sockets')) {\n        die('skip sockets extension not available.');\n    }\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n    /* Setup socket server */\n    $server = socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp'));\n    if (!$server) {\n        die('Unable to create AF_INET socket [server]');\n    }\n\n    if (!socket_bind($server, '127.0.0.1', 0)) {\n        die(\"Unable to bind to 127.0.0.1\");\n    }\n\n    if (!socket_listen($server, 2)) {\n        die('Unable to listen on socket');\n    }\n\n    socket_getsockname($server, $unused, $port);\n\n    /* Connect to it */\n    $client = socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp'));\n    if (!$client) {\n        die('Unable to create AF_INET socket [client]');\n    }\n    if (!socket_connect($client, '127.0.0.1', $port)) {\n        die('Unable to connect to server socket');\n    }\n\n    /* Accept that connection */\n    $socket = socket_accept($server);\n    if (!$socket) {\n        die('Unable to accept connection');\n    }\n\n    socket_write($client, \"ABCdef123\\n\");\n\n    $data = socket_read($socket, 10, PHP_BINARY_READ);\n    var_dump($data);\n\n    socket_close($client);\n    socket_close($socket);\n    socket_close($server);\n});\n?>\n--EXPECT--\nstring(10) \"ABCdef123\n\"\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/ipv6_skipif.inc",
    "content": "<?php\nif (getenv(\"CI_NO_IPV6\") || !defined(\"AF_INET6\")) {\n    die('skip no IPv6 support');\n}\nif (@stream_socket_client('udp://[::1]:8888') === false)\n    die('skip no IPv6 support');\n\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/ipv6loop.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: IPv6 Loopback test\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\n    if (!extension_loaded('sockets')) {\n        die('skip sockets extension not available.');\n    }\n    require 'ipv6_skipif.inc';\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n    /* Setup socket server */\n    $server = socket_create(AF_INET6, SOCK_STREAM, getprotobyname('tcp'));\n    if (!$server) {\n        die('Unable to create AF_INET6 socket [server]');\n    }\n    $bound = false;\n    for($port = 31337; $port < 31357; ++$port) {\n        if (@socket_bind($server, '::1', $port)) {\n            $bound = true;\n            break;\n        }\n    }\n    if (!$bound) {\n        die(\"Unable to bind to [::1]:$port\");\n    }\n    if (!socket_listen($server, 2)) {\n        die('Unable to listen on socket');\n    }\n\n    /* Connect to it */\n    $client = socket_create(AF_INET6, SOCK_STREAM, getprotobyname('tcp'));\n    if (!$client) {\n        die('Unable to create AF_INET6 socket [client]');\n    }\n    if (!socket_connect($client, '::1', $port)) {\n        die('Unable to connect to server socket');\n    }\n\n    /* Accept that connection */\n    $socket = socket_accept($server);\n    if (!$socket) {\n        die('Unable to accept connection');\n    }\n\n    socket_write($client, \"ABCdef123\\n\");\n\n    $data = socket_read($socket, 10, PHP_BINARY_READ);\n    var_dump($data);\n\n    socket_close($client);\n    socket_close($socket);\n    socket_close($server);\n});\n?>\n--EXPECT--\nstring(10) \"ABCdef123\n\"\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/mcast_helpers.php.inc",
    "content": "<?php\nfunction checktimeout($sock, $limit) {\n    return;\n    $readfs = array($sock);\n    $writefs = $exceptfs = array();\n    if (socket_select($readfs, $writefs, $exceptfs, 0, $limit*1000) != 1) {\n        die(\"Socket read timeout hit. Can be a bug, a test bug, or a firewall issue.\");\n    }\n}\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/mcast_ipv4_recv.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: IPv4 receive options\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('skip sockets extension not available.');\n}\nif (getenv('SKIP_ONLINE_TESTS')) die('skip online test');\n$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);\n$br = socket_bind($s, '0.0.0.0', 0);\n$so = @socket_set_option($s, IPPROTO_IP, MCAST_JOIN_GROUP, array(\n    \"group\"\t=> '224.0.0.23',\n    \"interface\" => 'lo',\n));\nif ($so === false) {\n    die('skip interface \\'lo\\' is unavailable.');\n}\nif (!defined(\"MCAST_BLOCK_SOURCE\")) {\n    die('skip source operations are unavailable');\n}?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../../include/bootstrap.php';\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\ninclude __DIR__.\"/mcast_helpers.php.inc\";\n$domain = AF_INET;\n$level = IPPROTO_IP;\n$interface = \"lo\";\n$mcastaddr = '224.0.0.23';\n$sblock = \"127.0.0.1\";\n\necho \"creating send socket bound to 127.0.0.1\\n\";\n$sends1 = socket_create($domain, SOCK_DGRAM, SOL_UDP);\n$br = socket_bind($sends1, '127.0.0.1');\nvar_dump($br);\n\necho \"creating unbound socket and hoping the routing table causes an interface other than lo to be used for sending messages to $mcastaddr\\n\";\n$sends2 = socket_create($domain, SOCK_DGRAM, SOL_UDP);\nvar_dump($br);\n\necho \"creating receive socket\\n\";\n$s = socket_create($domain, SOCK_DGRAM, SOL_UDP);\nAssert::assert(is_object($s));\n$br = socket_bind($s, '0.0.0.0', 0);\nsocket_getsockname($s, $unused, $port);\nvar_dump($br);\n\n$so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(\n    \"group\"\t=> $mcastaddr,\n    \"interface\" => $interface,\n));\nvar_dump($so);\n\n$r = socket_sendto($sends1, $m = \"initial packet\", strlen($m), 0, $mcastaddr, $port);\nvar_dump($r);\n\n$i = 0;\nchecktimeout($s, 500);\nwhile (($str = socket_read($s, 3000)) !== FALSE) {\n    $i++;\n    echo \"$i> \", $str, \"\\n\";\n\nif ($i == 1) {\n    echo \"leaving group\\n\";\n    $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array(\n        \"group\"\t=> $mcastaddr,\n        \"interface\" => $interface,\n    ));\n    var_dump($so);\n    $r = socket_sendto($sends1, $m = \"ignored mcast packet\", strlen($m), 0, $mcastaddr, $port);\n    var_dump($r);\n    $r = socket_sendto($sends1, $m = \"unicast packet\", strlen($m), 0, \"127.0.0.1\", $port);\n    var_dump($r);\n}\nif ($i == 2) {\n    echo \"re-joining group\\n\";\n    $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(\n        \"group\"\t=> $mcastaddr,\n        \"interface\" => $interface,\n    ));\n    var_dump($so);\n    $r = socket_sendto($sends2, $m = \"ignored mcast packet (different interface)\", strlen($m), 0, $mcastaddr, $port);\n    var_dump($r);\n    $r = socket_sendto($sends1, $m = \"mcast packet\", strlen($m), 0, $mcastaddr, $port);\n    var_dump($r);\n}\nif ($i == 3) {\n    echo \"blocking source\\n\";\n    $so = socket_set_option($s, $level, MCAST_BLOCK_SOURCE, array(\n        \"group\"\t=> $mcastaddr,\n        \"interface\" => $interface,\n        \"source\" => $sblock,\n    ));\n    var_dump($so);\n    $r = socket_sendto($sends1, $m = \"ignored packet (blocked source)\", strlen($m), 0, $mcastaddr, $port);\n    var_dump($r);\n    $r = socket_sendto($sends1, $m = \"unicast packet\", strlen($m), 0, \"127.0.0.1\", $port);\n    var_dump($r);\n}\nif ($i == 4) {\n    echo \"unblocking source\\n\";\n    $so = socket_set_option($s, $level, MCAST_UNBLOCK_SOURCE, array(\n        \"group\"\t=> $mcastaddr,\n        \"interface\" => $interface,\n        \"source\" => $sblock,\n    ));\n    var_dump($so);\n    $r = socket_sendto($sends1, $m = \"mcast packet from 127.0.0.1\", strlen($m), 0, $mcastaddr, $port);\n    var_dump($r);\n}\nif ($i == 5) {\n    echo \"leaving group\\n\";\n    $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array(\n        \"group\"\t=> $mcastaddr,\n        \"interface\" => $interface,\n    ));\n    var_dump($so);\n    $r = socket_sendto($sends1, $m = \"ignored mcast packet\", strlen($m), 0, $mcastaddr, $port);\n    var_dump($r);\n    $r = socket_sendto($sends1, $m = \"unicast packet\", strlen($m), 0, \"127.0.0.1\", $port);\n    var_dump($r);\n}\nif ($i == 6) {\n    echo \"joining source group\\n\";\n    $so = socket_set_option($s, $level, MCAST_JOIN_SOURCE_GROUP, array(\n        \"group\"\t=> $mcastaddr,\n        \"interface\" => $interface,\n        \"source\" => $sblock,\n    ));\n    var_dump($so);\n    $r = socket_sendto($sends1, $m = \"mcast packet from 127.0.0.1\", strlen($m), 0, $mcastaddr, $port);\n    var_dump($r);\n}\nif ($i == 7) {\n    echo \"leaving source group\\n\";\n    $so = socket_set_option($s, $level, MCAST_LEAVE_SOURCE_GROUP, array(\n        \"group\"\t=> $mcastaddr,\n        \"interface\" => $interface,\n        \"source\" => $sblock,\n    ));\n    var_dump($so);\n    $r = socket_sendto($sends1, $m = \"ignored mcast packet\", strlen($m), 0, $mcastaddr, $port);\n    var_dump($r);\n    $r = socket_sendto($sends1, $m = \"unicast packet\", strlen($m), 0, \"127.0.0.1\", $port);\n    var_dump($r);\n}\nif ($i == 8) {\n/*\techo \"rjsg\\n\";\n    $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(\n        \"group\"\t=> $mcastaddr,\n        \"interface\" => $interface,\n    ));\n    var_dump($so);*/\n    break;\n}\n\n}\n});\n?>\n--EXPECTF--\ncreating send socket bound to 127.0.0.1\nbool(true)\ncreating unbound socket and hoping the routing table causes an interface other than lo to be used for sending messages to 224.0.0.23\nbool(true)\ncreating receive socket\nbool(true)\nbool(true)\nint(14)\n1> initial packet\nleaving group\nbool(true)\nint(20)\nint(14)\n2> unicast packet\nre-joining group\nbool(true)\nint(42)\nint(12)\n3> mcast packet\nblocking source\nbool(true)\nint(31)\nint(14)\n4> unicast packet\nunblocking source\nbool(true)\nint(27)\n5> mcast packet from 127.0.0.1\nleaving group\nbool(true)\nint(20)\nint(14)\n6> unicast packet\njoining source group\nbool(true)\nint(27)\n7> mcast packet from 127.0.0.1\nleaving source group\nbool(true)\nint(20)\nint(14)\n8> unicast packet\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/mcast_ipv4_send.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: IPv4 send options\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('skip sockets extension not available.');\n}\n$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP) or die(\"err\");\nif (socket_set_option($s, IPPROTO_IP, IP_MULTICAST_IF, 1) === false) {\n    die(\"skip interface 1 either doesn't exist or has no ipv4 address\");\n}?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n$domain = AF_INET;\n$level = IPPROTO_IP;\n$s = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die(\"err\");\n\necho \"Setting IP_MULTICAST_TTL\\n\";\n$r = socket_set_option($s, $level, IP_MULTICAST_TTL, 9);\nvar_dump($r);\n$r = socket_get_option($s, $level, IP_MULTICAST_TTL);\nvar_dump($r);\necho \"\\n\";\n\necho \"Setting IP_MULTICAST_LOOP\\n\";\n$r = socket_set_option($s, $level, IP_MULTICAST_LOOP, 0);\nvar_dump($r);\n$r = socket_get_option($s, $level, IP_MULTICAST_LOOP);\nvar_dump($r);\n$r = socket_set_option($s, $level, IP_MULTICAST_LOOP, 1);\nvar_dump($r);\n$r = socket_get_option($s, $level, IP_MULTICAST_LOOP);\nvar_dump($r);\necho \"\\n\";\n\necho \"Setting IP_MULTICAST_IF\\n\";\necho \"interface 0:\\n\";\n$r = socket_set_option($s, $level, IP_MULTICAST_IF, 0);\nvar_dump($r);\n$r = socket_get_option($s, $level, IP_MULTICAST_IF);\nvar_dump($r);\necho \"interface 1:\\n\";\n$r = socket_set_option($s, $level, IP_MULTICAST_IF, 1);\nvar_dump($r);\n$r = socket_get_option($s, $level, IP_MULTICAST_IF);\nvar_dump($r);\necho \"\\n\";\n});\n?>\n--EXPECT--\nSetting IP_MULTICAST_TTL\nbool(true)\nint(9)\n\nSetting IP_MULTICAST_LOOP\nbool(true)\nint(0)\nbool(true)\nint(1)\n\nSetting IP_MULTICAST_IF\ninterface 0:\nbool(true)\nint(0)\ninterface 1:\nbool(true)\nint(1)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/mcast_ipv4_send_error.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: IPv4 send options with unusual values\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('skip sockets extension not available.');\n}\n$domain = AF_INET;\n$level = IPPROTO_IP;\n$s = socket_create($domain, SOCK_DGRAM, SOL_UDP);\nif ($s === false) {\n    die(\"skip unable to create socket\");\n}\nif (socket_set_option($s, $level, IP_MULTICAST_IF, 1) === false) {\n    die(\"skip interface 1 either doesn't exist or has no ipv4 address\");\n}?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../../include/bootstrap.php';\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n    $domain = AF_INET;\n    $level = IPPROTO_IP;\n    $s = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die(\"err\");\n    Assert::assert(is_object($s));\n    echo \"Setting IP_MULTICAST_LOOP with 256\\n\";\n//if we had a simple cast to unsigned char, this would be the same as 0\n    $r = @socket_set_option($s, $level, IP_MULTICAST_LOOP, 256);\n    var_dump($r);\n    $r = @socket_get_option($s, $level, IP_MULTICAST_LOOP);\n    var_dump($r);\n\n    echo \"Setting IP_MULTICAST_LOOP with false\\n\";\n//should convert to (unsigned char)0\n    $r = @socket_set_option($s, $level, IP_MULTICAST_LOOP, false);\n    var_dump($r);\n    $r = @socket_get_option($s, $level, IP_MULTICAST_LOOP);\n    var_dump($r);\n\n    echo \"Setting IP_MULTICAST_TTL with 256\\n\";\n//if we had a simple cast to unsigned char, this would be the same as 0\n    try {\n        $r = @socket_set_option($s, $level, IP_MULTICAST_TTL, 256);\n        var_dump($r);\n    } catch (\\ValueError $e) {\n        echo $e->getMessage() . \\PHP_EOL;\n    }\n    $r = @socket_get_option($s, $level, IP_MULTICAST_TTL);\n    var_dump($r);\n\n    echo \"Setting IP_MULTICAST_TTL with \\\"254\\\"\\n\";\n    $r = @socket_set_option($s, $level, IP_MULTICAST_TTL, \"254\");\n    var_dump($r);\n    $r = @socket_get_option($s, $level, IP_MULTICAST_TTL);\n    var_dump($r);\n\n    echo \"Setting IP_MULTICAST_TTL with -1\\n\";\n//should give error, not be the same as 255\n    try {\n        $r = @socket_set_option($s, $level, IP_MULTICAST_TTL, -1);\n        var_dump($r);\n    } catch (\\ValueError $e) {\n        echo $e->getMessage() . \\PHP_EOL;\n    }\n    $r = @socket_get_option($s, $level, IP_MULTICAST_TTL);\n    var_dump($r);\n});\n?>\n--EXPECT--\nSetting IP_MULTICAST_LOOP with 256\nbool(true)\nint(1)\nSetting IP_MULTICAST_LOOP with false\nbool(true)\nint(0)\nSetting IP_MULTICAST_TTL with 256\nbool(false)\nint(1)\nSetting IP_MULTICAST_TTL with \"254\"\nbool(true)\nint(254)\nSetting IP_MULTICAST_TTL with -1\nbool(false)\nint(254)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/mcast_ipv6_recv.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: IPv6 receive options\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('skip sockets extension not available.');\n}\nif (!defined('IPPROTO_IPV6')) {\n    die('skip IPv6 not available.');\n}\n$s = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP);\nif ($s === false) {\n  die(\"skip unable to create socket\");\n}\n$br = socket_bind($s, '::', 0);\nsocket_getsockname($s, $unused, $port);\n/* On Linux, there is no route ff00::/8 by default on lo, which makes it\n * troublesome to send multicast traffic from lo, which we must since\n * we're dealing with interface-local traffic... */\n$so = @socket_set_option($s, IPPROTO_IPV6, MCAST_JOIN_GROUP, array(\n    \"group\"\t=> 'ff01::114',\n    \"interface\" => 0,\n));\nif ($so === false) {\n    die('skip unable to join multicast group on any interface.');\n}\n$r = socket_sendto($s, $m = \"testing packet\", strlen($m), 0, 'ff01::114', $port);\nif ($r === false) {\n    die('skip unable to send multicast packet.');\n}\n\nif (!defined(\"MCAST_JOIN_SOURCE_GROUP\"))\n    die('skip source operations are unavailable');\n\n$so = @socket_set_option($s, IPPROTO_IPV6, MCAST_LEAVE_GROUP, array(\n    \"group\"\t=> 'ff01::114',\n    \"interface\" => 0,\n));\n$so = @socket_set_option($s, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP, array(\n    \"group\"\t=> 'ff01::114',\n    \"interface\" => 0,\n    \"source\" => '2001::dead:beef',\n));\nif ($so === false) {\n    die('skip protocol independent multicast API is unavailable.');\n}?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../../include/bootstrap.php';\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\n//Runtime::setHookFlags(0);\n\nrun(function () {\n    include __DIR__ . \"/mcast_helpers.php.inc\";\n    $domain = AF_INET6;\n    $level = IPPROTO_IPV6;\n    $interface = 0;\n    $mcastaddr = 'ff01::114';\n    $sblock = \"?\";\n\n    echo \"creating send socket\\n\";\n    $sends1 = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die(\"err\");\n    Assert::true(is_object($sends1));\n\n    echo \"creating receive socket\\n\";\n    $s = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die(\"err\");\n    Assert::true(is_object($s));\n\n    $br = socket_bind($s, '::0', 0) or die(\"err\");\n    var_dump($br);\n    socket_getsockname($s, $unused, $port);\n\n    $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(\n        \"group\" => $mcastaddr,\n        \"interface\" => $interface,\n    )) or die(\"err\");\n    var_dump($so);\n\n    echo \"socket_sendto[1]\\n\";\n    $r = socket_sendto($sends1, $m = \"testing packet\", strlen($m), 0, $mcastaddr, $port);\n    var_dump($r);\n    checktimeout($s, 500);\n\n    echo \"socket_recvfrom[1]\\n\";\n    $r = socket_recvfrom($s, $str, 2000, 0, $from, $fromPort);\n    var_dump($r, $str, $from);\n    $sblock = $from;\n\n    $r = socket_sendto($sends1, $m = \"initial packet\", strlen($m), 0, $mcastaddr, $port);\n    var_dump($r);\n\n    $i = 0;\n    checktimeout($s, 500);\n    while (($str = socket_read($s, 3000)) !== false) {\n        $i++;\n        echo \"$i> \", $str, \"\\n\";\n\n        if ($i == 1) {\n            echo \"leaving group\\n\";\n            $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array(\n                \"group\" => $mcastaddr,\n                \"interface\" => $interface,\n            ));\n            var_dump($so);\n            $r = socket_sendto($sends1, $m = \"ignored mcast packet\", strlen($m), 0, $mcastaddr, $port);\n            var_dump($r);\n            $r = socket_sendto($sends1, $m = \"unicast packet\", strlen($m), 0, \"::1\", $port);\n            var_dump($r);\n        }\n        if ($i == 2) {\n            echo \"re-joining group\\n\";\n            $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(\n                \"group\" => $mcastaddr,\n                \"interface\" => $interface,\n            ));\n            var_dump($so);\n            $r = socket_sendto($sends1, $m = \"mcast packet\", strlen($m), 0, $mcastaddr, $port);\n            var_dump($r);\n        }\n        if ($i == 3) {\n            echo \"blocking source\\n\";\n            $so = socket_set_option($s, $level, MCAST_BLOCK_SOURCE, array(\n                \"group\" => $mcastaddr,\n                \"interface\" => $interface,\n                \"source\" => $sblock,\n            ));\n            var_dump($so);\n            $r = socket_sendto($sends1, $m = \"ignored packet (blocked source)\", strlen($m), 0, $mcastaddr, $port);\n            var_dump($r);\n            $r = socket_sendto($sends1, $m = \"unicast packet\", strlen($m), 0, \"::1\", $port);\n            var_dump($r);\n        }\n        if ($i == 4) {\n            echo \"unblocking source\\n\";\n            $so = socket_set_option($s, $level, MCAST_UNBLOCK_SOURCE, array(\n                \"group\" => $mcastaddr,\n                \"interface\" => $interface,\n                \"source\" => $sblock,\n            ));\n            var_dump($so);\n            $r = socket_sendto($sends1, $m = \"mcast packet\", strlen($m), 0, $mcastaddr, $port);\n            var_dump($r);\n        }\n        if ($i == 5) {\n            echo \"leaving group\\n\";\n            $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array(\n                \"group\" => $mcastaddr,\n                \"interface\" => $interface,\n            ));\n            var_dump($so);\n            $r = socket_sendto($sends1, $m = \"ignored mcast packet\", strlen($m), 0, $mcastaddr, $port);\n            var_dump($r);\n            $r = socket_sendto($sends1, $m = \"unicast packet\", strlen($m), 0, \"::1\", $port);\n            var_dump($r);\n        }\n        if ($i == 6) {\n            echo \"joining source group\\n\";\n            $so = socket_set_option($s, $level, MCAST_JOIN_SOURCE_GROUP, array(\n                \"group\" => $mcastaddr,\n                \"interface\" => $interface,\n                \"source\" => $sblock,\n            ));\n            var_dump($so);\n            $r = socket_sendto($sends1, $m = \"mcast packet from desired source\", strlen($m), 0, $mcastaddr, $port);\n            var_dump($r);\n        }\n        if ($i == 7) {\n            echo \"leaving source group\\n\";\n            $so = socket_set_option($s, $level, MCAST_LEAVE_SOURCE_GROUP, array(\n                \"group\" => $mcastaddr,\n                \"interface\" => $interface,\n                \"source\" => $sblock,\n            ));\n            var_dump($so);\n            $r = socket_sendto($sends1, $m = \"ignored mcast packet\", strlen($m), 0, $mcastaddr, $port);\n            var_dump($r);\n            $r = socket_sendto($sends1, $m = \"unicast packet\", strlen($m), 0, \"::1\", $port);\n            var_dump($r);\n        }\n        if ($i == 8) {\n            /*echo \"joining source group\\n\";\n            $so = socket_set_option($s, $level, MCAST_JOIN_SOURCE_GROUP, array(\n                \"group\"\t=> $mcastaddr,\n                \"interface\" => $interface,\n                \"source\" => $sblock,\n            ));\n            var_dump($so);*/\n            break;\n        }\n    }\n});\n?>\n--EXPECTF--\ncreating send socket\ncreating receive socket\nbool(true)\nbool(true)\nsocket_sendto[1]\nint(14)\nsocket_recvfrom[1]\nint(14)\nstring(14) \"testing packet\"\nstring(%d) \"%s\"\nint(14)\n1> initial packet\nleaving group\nbool(true)\nint(20)\nint(14)\n2> unicast packet\nre-joining group\nbool(true)\nint(12)\n3> mcast packet\nblocking source\nbool(true)\nint(31)\nint(14)\n4> unicast packet\nunblocking source\nbool(true)\nint(12)\n5> mcast packet\nleaving group\nbool(true)\nint(20)\nint(14)\n6> unicast packet\njoining source group\nbool(true)\nint(32)\n7> mcast packet from desired source\nleaving source group\nbool(true)\nint(20)\nint(14)\n8> unicast packet\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/mcast_ipv6_recv_limited.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: IPv6 receive options (limited)\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('skip sockets extension not available.');\n}\nif (!defined('IPPROTO_IPV6')) {\n    die('skip IPv6 not available.');\n}\n$s = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP);\n$br = socket_bind($s, '::', 3000);\n/* On Linux, there is no route ff00::/8 by default on lo, which makes it\n * troublesome to send multicast traffic from lo, which we must since\n * we're dealing with interface-local traffic... */\n$so = @socket_set_option($s, IPPROTO_IPV6, MCAST_JOIN_GROUP, array(\n    \"group\"\t=> 'ff01::114',\n    \"interface\" => 0,\n));\nif ($so === false) {\n    die('skip unable to join multicast group on any interface.');\n}\n$r = socket_sendto($s, $m = \"testing packet\", strlen($m), 0, 'ff01::114', 3000);\nif ($r === false) {\n    die('skip unable to send multicast packet.');\n}\n$so = @socket_set_option($s, IPPROTO_IPV6, MCAST_LEAVE_GROUP, array(\n    \"group\"\t=> 'ff01::114',\n    \"interface\" => 0,\n));\nif (defined(\"MCAST_JOIN_SOURCE_GROUP\")) {\n    $so = @socket_set_option($s, IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP, array(\n        \"group\"\t=> 'ff01::114',\n        \"interface\" => 0,\n        \"source\" => '2001::dead:beef',\n    ));\n    if ($so !== false) {\n        die('skip protocol independent multicast API is available.');\n    }\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../../include/bootstrap.php';\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n    include __DIR__ . \"/mcast_helpers.php.inc\";\n    $domain = AF_INET6;\n    $level = IPPROTO_IPV6;\n    $interface = 0;\n    $mcastaddr = 'ff01::114';\n    $sblock = \"?\";\n\n    echo \"creating send socket\\n\";\n    $sends1 = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die(\"err\");\n    Assert::assert(is_object($sends1));\n\n    echo \"creating receive socket\\n\";\n    $s = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die(\"err\");\n    Assert::assert(is_object($s));\n    $br = socket_bind($s, '::0', 3000) or die(\"err\");\n    var_dump($br);\n\n    $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(\n        \"group\" => $mcastaddr,\n        \"interface\" => $interface,\n    )) or die(\"err\");\n    var_dump($so);\n\n    $r = socket_sendto($sends1, $m = \"testing packet\", strlen($m), 0, $mcastaddr, 3000);\n    var_dump($r);\n    checktimeout($s, 500);\n    $r = socket_recvfrom($s, $str, 2000, 0, $from, $fromPort);\n    var_dump($r, $str, $from);\n    $sblock = $from;\n\n    $r = socket_sendto($sends1, $m = \"initial packet\", strlen($m), 0, $mcastaddr, 3000);\n    var_dump($r);\n\n    $i = 0;\n    checktimeout($s, 500);\n    while (($str = socket_read($s, 3000, 500)) !== false) {\n        $i++;\n        echo \"$i> \", $str, \"\\n\";\n\n        if ($i == 1) {\n            echo \"leaving group\\n\";\n            $so = socket_set_option($s, $level, MCAST_LEAVE_GROUP, array(\n                \"group\" => $mcastaddr,\n                \"interface\" => $interface,\n            ));\n            var_dump($so);\n            $r = socket_sendto($sends1, $m = \"ignored mcast packet\", strlen($m), 0, $mcastaddr, 3000);\n            var_dump($r);\n            $r = socket_sendto($sends1, $m = \"unicast packet\", strlen($m), 0, \"::1\", 3000);\n            var_dump($r);\n        }\n        if ($i == 2) {\n            echo \"re-joining group\\n\";\n            $so = socket_set_option($s, $level, MCAST_JOIN_GROUP, array(\n                \"group\" => $mcastaddr,\n                \"interface\" => $interface,\n            ));\n            var_dump($so);\n            $r = socket_sendto($sends1, $m = \"mcast packet\", strlen($m), 0, $mcastaddr, 3000);\n            var_dump($r);\n        }\n        if ($i == 3) {\n            break;\n        }\n    }\n});\n?>\n--EXPECTF--\ncreating send socket\ncreating receive socket\nbool(true)\nbool(true)\nint(14)\nint(14)\nstring(14) \"testing packet\"\nstring(%d) \"%s\"\nint(14)\n1> initial packet\nleaving group\nbool(true)\nint(20)\nint(14)\n2> unicast packet\nre-joining group\nbool(true)\nint(12)\n3> mcast packet\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/mcast_ipv6_send.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: IPv6 send options\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('skip sockets extension not available.');\n}\nif (getenv('CI_NO_IPV6') || !defined('IPPROTO_IPV6')) {\n    die('skip IPv6 not available.');\n}\n$level = IPPROTO_IPV6;\n$s = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP) or die(\"skip Can not create socket\");\nif (socket_set_option($s, $level, IPV6_MULTICAST_IF, 1) === false) {\n    die(\"skip interface 1 either doesn't exist or has no ipv6 address\");\n}\nskip_if_not_linux();\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n$domain = AF_INET6;\n$level = IPPROTO_IPV6;\n$s = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die(\"err\");\n\necho \"Setting IPV6_MULTICAST_TTL\\n\";\n$r = socket_set_option($s, $level, IPV6_MULTICAST_HOPS, 9);\nvar_dump($r);\n$r = socket_get_option($s, $level, IPV6_MULTICAST_HOPS);\nvar_dump($r);\necho \"\\n\";\n\necho \"Setting IPV6_MULTICAST_LOOP\\n\";\n$r = socket_set_option($s, $level, IPV6_MULTICAST_LOOP, 0);\nvar_dump($r);\n$r = socket_get_option($s, $level, IPV6_MULTICAST_LOOP);\nvar_dump($r);\n$r = socket_set_option($s, $level, IPV6_MULTICAST_LOOP, 1);\nvar_dump($r);\n$r = socket_get_option($s, $level, IPV6_MULTICAST_LOOP);\nvar_dump($r);\necho \"\\n\";\n\necho \"Setting IPV6_MULTICAST_IF\\n\";\necho \"interface 0:\\n\";\n$r = socket_set_option($s, $level, IPV6_MULTICAST_IF, 0);\nvar_dump($r);\n$r = socket_get_option($s, $level, IPV6_MULTICAST_IF);\nvar_dump($r);\necho \"interface 1:\\n\";\n$r = socket_set_option($s, $level, IPV6_MULTICAST_IF, 1);\nvar_dump($r);\n$r = socket_get_option($s, $level, IPV6_MULTICAST_IF);\nvar_dump($r);\necho \"\\n\";\n});\n?>\n--EXPECT--\nSetting IPV6_MULTICAST_TTL\nbool(true)\nint(9)\n\nSetting IPV6_MULTICAST_LOOP\nbool(true)\nint(0)\nbool(true)\nint(1)\n\nSetting IPV6_MULTICAST_IF\ninterface 0:\nbool(true)\nint(0)\ninterface 1:\nbool(true)\nint(1)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_accept_failure.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: socket_accept() failure\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n    var_dump(socket_accept($socket));\n});\n?>\n--EXPECTF--\nbool(false)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_bind.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: ext/sockets - socket_bind - basic test\n--CREDITS--\nFlorian Anderiasch\nfa@php.net\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\n    if (!extension_loaded('sockets')) {\n        die('skip - sockets extension not available.');\n    }\n    if (getenv(\"SKIP_ONLINE_TESTS\")) {\n        die(\"skip test requiring internet connection\");\n    }\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n    $s_c     = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n    $s_bind  = socket_bind($s_c, '0.0.0.0');\n    var_dump($s_bind);\n\n    // Connect to destination address\n    $s_conn  = socket_connect($s_c, 'www.php.net', 80);\n    var_dump($s_conn);\n\n    // Write\n    $request = 'GET / HTTP/1.1' . \"\\r\\n\";\n    $s_write = socket_write($s_c, $request);\n    var_dump($s_write);\n\n    // Close\n    $s_close = socket_close($s_c);\n    var_dump($s_close);\n});\n?>\n--EXPECT--\nbool(true)\nbool(true)\nint(16)\nNULL\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_clear_error.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: void socket_clear_error ([ resource $socket ] ) ;\n--CREDITS--\nmarcosptf - <marcosptf@yahoo.com.br> - #phparty7 - @phpsp - novatec/2015 - sao paulo - br\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n  die('SKIP sockets extension not available.');\n}\nif(substr(PHP_OS, 0, 3) == 'WIN' ) {\n    die('skip windows only test');\n}\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n    $socketConn = socket_connect($socket, \"127.0.0.1\", 21248);\n    var_dump(socket_last_error($socket));\n    socket_clear_error($socket);\n    var_dump(socket_last_error($socket));\n\n    socket_close($socket);\n    unset($socket);\n    unset($socketConn);\n});\n?>\n--EXPECTF--\nint(%d)\nint(%d)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_connect_params.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: ext/sockets - socket_connect - test with empty parameters\n--CREDITS--\nFlorian Anderiasch\nfa@php.net\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\n    if (!extension_loaded('sockets')) {\n        die('skip - sockets extension not available.');\n    }\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../../include/bootstrap.php';\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n    $s_c = socket_create_listen(0);\n    socket_getsockname($s_c, $addr, $port);\n\n    // wrong parameter count\n    try {\n        Assert::false(socket_connect($s_c));\n    } catch (\\ArgumentCountError $e) {\n        echo $e->getMessage() . \\PHP_EOL;\n    }\n    try {\n        Assert::false(socket_connect($s_c, '0.0.0.0'));\n    } catch (\\ValueError $e) {\n        echo $e->getMessage() . \\PHP_EOL;\n    }\n    $s_w = socket_connect($s_c, '0.0.0.0', $port);\n\n    socket_close($s_c);\n});\n?>\n--EXPECTF--\nToo few arguments to function swoole_socket_connect(), 1 passed and at least 2 expected\n\nWarning: Swoole\\Coroutine\\Socket::connect(): Invalid port argument[0] in %s on line %d\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_create_listen-nobind.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: Test if socket_create_listen() returns false, when it cannot bind to the port.\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('SKIP The sockets extension is not loaded.');\n}\n$filename = __FILE__ . '.root_check.tmp';\n$fp = fopen($filename, 'w');\nfclose($fp);\nif (fileowner($filename) == 0) {\n    unlink($filename);\n    die('SKIP Test cannot be run as root.');\n}\nunlink($filename);\nif (@socket_create_listen(80)) {\n    die('SKIP Test cannot be run in environment that will allow binding to port 80 (azure)');\n}?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../../include/bootstrap.php';\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n    $sock = socket_create_listen(80);\n    Assert::false($sock);\n    Assert::eq(socket_last_error(), SOCKET_EACCES);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_create_listen.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: Test if socket binds on 31338\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (substr(PHP_OS, 0, 3) == 'WIN') {\n    die('skip.. Not valid for Windows');\n}\nif (!extension_loaded('sockets')) {\n    die('SKIP The sockets extension is not loaded.');\n}?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n$sock = socket_create_listen(31338);\nsocket_getsockname($sock, $addr, $port);\nvar_dump($addr, $port);\n});\n?>\n--EXPECT--\nstring(7) \"0.0.0.0\"\nint(31338)\n--CREDITS--\nTill Klampaeckel, till@php.net\nPHP Testfest Berlin 2009-05-09\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_create_listen_used.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: ext/sockets - socket_create_listen - test for used socket\n--CREDITS--\nFlorian Anderiasch\nfa@php.net\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\n    if (!extension_loaded('sockets')) {\n        die('skip - sockets extension not available.');\n    }\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../../include/bootstrap.php';\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n    $rand = rand(1,999);\n    // wrong parameter count\n    $s_c_l = socket_create_listen(31330+$rand);\n    Assert::isInstanceOf($s_c_l, Swoole\\Coroutine\\Socket::class);\n    // default invocation\n    $s_c_l2 = socket_create_listen(31330+$rand);\n    Assert::false($s_c_l2);\n    socket_close($s_c_l);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_create_pair.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: Test for socket_create_pair()\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('SKIP The sockets extension is not loaded.');\n}?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../../include/bootstrap.php';\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n    $sockets = array();\n    if (strtolower(substr(PHP_OS, 0, 3)) == 'win') {\n        $domain = AF_INET;\n    } else {\n        $domain = AF_UNIX;\n    }\n    socket_create_pair($domain, SOCK_STREAM, 0, $sockets);\n    Assert::count($sockets, 2);\n    Assert::isInstanceOf($sockets[0], Swoole\\Coroutine\\Socket::class);\n    Assert::isInstanceOf($sockets[1], Swoole\\Coroutine\\Socket::class);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_getopt.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: mixed socket_getopt( resource $socket , int $level , int $optname ) ;\n--CREDITS--\nmarcosptf -\n<marcosptf@yahoo.com.br> - #phparty7 - @phpsp - novatec/2015 - sao paulo - br\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('skip sockets extension not available.');\n}\n?>\n--FILE--\n<?php\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n    $domain = AF_INET;\n    $level = IPPROTO_IP;\n    $s = socket_create($domain, SOCK_DGRAM, SOL_UDP) or die(\"err\");\n\n    echo \"Setting IP_MULTICAST_TTL\\n\";\n    $r = socket_set_option($s, $level, IP_MULTICAST_TTL, 9);\n    var_dump($r);\n    $r = socket_getopt($s, $level, IP_MULTICAST_TTL);\n    var_dump($r);\n    echo \"\\n\";\n\n    echo \"Setting IP_MULTICAST_LOOP\\n\";\n    $r = socket_set_option($s, $level, IP_MULTICAST_LOOP, 0);\n    var_dump($r);\n    $r = socket_getopt($s, $level, IP_MULTICAST_LOOP);\n    var_dump($r);\n    $r = socket_set_option($s, $level, IP_MULTICAST_LOOP, 1);\n    var_dump($r);\n    $r = socket_getopt($s, $level, IP_MULTICAST_LOOP);\n    var_dump($r);\n    echo \"\\n\";\n\n    echo \"Setting IP_MULTICAST_IF\\n\";\n    echo \"interface 0:\\n\";\n    $r = socket_set_option($s, $level, IP_MULTICAST_IF, 0);\n    var_dump($r);\n    $r = socket_getopt($s, $level, IP_MULTICAST_IF);\n    var_dump($r);\n    echo \"interface 1:\\n\";\n    $r = socket_set_option($s, $level, IP_MULTICAST_IF, 1);\n    var_dump($r);\n    $r = socket_getopt($s, $level, IP_MULTICAST_IF);\n    var_dump($r);\n    echo \"\\n\";\n\n    unset($domain);\n    unset($level);\n    socket_close($s);\n    unset($s);\n    unset($r);\n});\n?>\n--EXPECT--\nSetting IP_MULTICAST_TTL\nbool(true)\nint(9)\n\nSetting IP_MULTICAST_LOOP\nbool(true)\nint(0)\nbool(true)\nint(1)\n\nSetting IP_MULTICAST_IF\ninterface 0:\nbool(true)\nint(0)\ninterface 1:\nbool(true)\nint(1)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_getpeername.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: ext/sockets - socket_getpeername - basic test\n--CREDITS--\nFlorian Anderiasch\nfa@php.net\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\n    if (!extension_loaded('sockets')) {\n        die('skip sockets extension not available.');\n    }\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n    $s_c     = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n    $s_bind  = socket_bind($s_c, '0.0.0.0');\n    var_dump($s_bind);\n\n    // Connect to destination address\n    $s_peer  = socket_getpeername($s_c, $ip, $port);\n    var_dump($s_peer);\n    var_dump($ip);\n    var_dump($port);\n    socket_close($s_c);\n});\n?>\n--EXPECTF--\nbool(true)\nbool(false)\nNULL\nNULL\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_getpeername_ipv4loop.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: ext/sockets - socket_getpeername_ipv4loop - basic test\n--CREDITS--\nTatjana Andersen tatjana.andersen@redpill-linpro.com\n# TestFest 2009 - NorwayUG\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\n        if (!extension_loaded('sockets')) {\n                die('skip sockets extension not available.');\n        }\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n    /* Bind and connect sockets to localhost */\n    $localhost = '127.0.0.1';\n\n        /* Setup socket server */\n        $server = socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp'));\n        if (!$server) {\n                die('Unable to create AF_INET socket [server]');\n        }\n\n    $minport = 31337;\n    $maxport = 31356;\n    $bound = false;\n    for($port = $minport; $port <= $maxport; ++$port) {\n        if (@socket_bind($server, $localhost, $port)) {\n            $bound = true;\n            break;\n        }\n    }\n    if (!$bound) {\n                die('Unable to bind to '.$localhost);\n        }\n        if (!socket_listen($server, 2)) {\n                die('Unable to listen on socket');\n        }\n\n        /* Connect to it */\n        $client = socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp'));\n        if (!$client) {\n                die('Unable to create AF_INET socket [client]');\n        }\n        if (!socket_connect($client, $localhost, $port)) {\n                die('Unable to connect to server socket');\n        }\n\n        /* Accept that connection */\n        $socket = socket_accept($server);\n        if (!$socket) {\n                die('Unable to accept connection');\n        }\n\n    if (!socket_getpeername($client, $address, $peerport)) {\n        die('Unable to retrieve peer name');\n    }\n        var_dump($address, $port === $peerport);\n\n        socket_close($client);\n        socket_close($socket);\n        socket_close($server);\n});\n?>\n--EXPECT--\nstring(9) \"127.0.0.1\"\nbool(true)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_getpeername_ipv6loop.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: ext/sockets - socket_getpeername_ipv6loop - basic test\n--CREDITS--\nTatjana Andersen tatjana.andersen@redpill-linpro.com\n# TestFest 2009 - NorwayUG\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('skip sockets extension not available.');\n}\nrequire 'ipv6_skipif.inc';\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n    /* Bind and connect sockets to localhost */\n    $localhost = '::1';\n\n        /* Setup socket server */\n        $server = socket_create(AF_INET6, SOCK_STREAM, getprotobyname('tcp'));\n        if (!$server) {\n                die('Unable to create AF_INET6 socket [server]');\n        }\n\n    $minport = 31337;\n    $maxport = 31356;\n    $bound = false;\n    for($port = $minport; $port <= $maxport; ++$port) {\n        if (@socket_bind($server, $localhost, $port)) {\n            $bound = true;\n            break;\n        }\n    }\n    if (!$bound) {\n                die('Unable to bind to '.$localhost);\n        }\n        if (!socket_listen($server, 2)) {\n                die('Unable to listen on socket');\n        }\n\n        /* Connect to it */\n        $client = socket_create(AF_INET6, SOCK_STREAM, getprotobyname('tcp'));\n        if (!$client) {\n                die('Unable to create AF_INET6 socket [client]');\n        }\n        if (!socket_connect($client, $localhost, $port)) {\n                die('Unable to connect to server socket');\n        }\n\n        /* Accept that connection */\n        $socket = socket_accept($server);\n        if (!$socket) {\n            die('Unable to accept connection');\n        }\n\n    if (!socket_getpeername($client, $address, $peerport)) {\n        die('Unable to retrieve peer name');\n    }\n        var_dump($address, $port === $peerport);\n\n        socket_close($client);\n        socket_close($socket);\n        socket_close($server);\n});\n?>\n--EXPECT--\nstring(3) \"::1\"\nbool(true)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_getsockname.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: ext/sockets - socket_getsockname - basic test\n--CREDITS--\nFlorian Anderiasch\nfa@php.net\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\n    if (!extension_loaded('sockets')) {\n        die('skip sockets extension not available.');\n    }\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n    $s_c     = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n    $s_bind  = socket_bind($s_c, '0.0.0.0');\n    var_dump($s_bind);\n\n    // Connect to destination address\n    $s_conn  = socket_getsockname($s_c, $ip, $port);\n    var_dump($s_conn);\n    var_dump($ip);\n    var_dump($port);\n    socket_close($s_c);\n});\n?>\n--EXPECTF--\nbool(true)\nbool(true)\nstring(7) \"0.0.0.0\"\nint(%i)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_listen-wrongparams.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: Test parameter handling in socket_listen().\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (substr(PHP_OS, 0, 3) == 'WIN') {\n    die('skip.. Not valid for Windows');\n}\nif (!extension_loaded('sockets')) {\n    die('SKIP The sockets extension is not loaded.');\n} ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n    $socket = socket_create(AF_UNIX, SOCK_STREAM, 0);\n    var_dump(socket_listen($socket));\n});\n?>\n--EXPECTF--\nbool(false)\n--CREDITS--\nTill Klampaeckel, till@php.net\nBerlin TestFest 2009\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_read_params.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: ext/sockets - socket_read- test with incorrect parameter\n--CREDITS--\nFlorian Anderiasch\nfa@php.net\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('skip sockets extension not available.');\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n    $s_c_l = socket_create_listen(0);\n    $s_c = socket_read($s_c_l, 25);\n    Assert::false($s_c);\n    socket_close($s_c_l);\n});\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_select-wrongparams-2.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: Test parameter handling in socket_select().\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('SKIP The sockets extension is not loaded.');\n}\ndie('skip unsupport');\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n$sockets = null;\n$write   = null;\n$except  = null;\n$time    = 0;\n\ntry {\n    socket_select($sockets, $write, $except, $time);\n} catch (ValueError $exception) {\n    echo $exception->getMessage() . \"\\n\";\n}\n});\n?>\n--EXPECTF--\nsocket_select(): At least one array argument must be passed\n--CREDITS--\nTill Klampaeckel, till@php.net\nBerlin TestFest 2009\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_select.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: Test parameter handling in socket_select().\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('SKIP The sockets extension is not loaded.');\n}\ndie('skip unsupport');\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n$sockets = array();\nif (strtolower(substr(PHP_OS, 0, 3)) == 'win') {\n    $domain = AF_INET;\n} else {\n    $domain = AF_UNIX;\n}\nsocket_create_pair($domain, SOCK_STREAM, 0, $sockets);\n\n$write  = null;\n$except = null;\n$ref =& $sockets[0]; // bug #78038\nvar_dump(socket_select($sockets, $write, $except, 0));\n});\n?>\n--EXPECT--\nint(0)\n--CREDITS--\nTill Klampaeckel, till@php.net\nBerlin TestFest 2009\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_select_error.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: socket_select() error conditions\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) die('skip socket extension not available');\ndie('skip unsupport');\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n$r = $w = $e = ['no resource'];\ntry {\n    socket_select($r, $w, $e, 1);\n} catch (TypeError $ex) {\n    echo $ex->getMessage(), PHP_EOL;\n}\n});\n?>\n--EXPECT--\nsocket_select(): Argument #1 ($read) must only have elements of type Socket, string given\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv4_udp.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: Test if socket_recvfrom() receives data sent by socket_sendto() via IPv4 UDP\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('SKIP The sockets extension is not loaded.');\n}\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);\nif (!$socket) {\n    die('Unable to create AF_INET socket');\n}\n\nif (!socket_bind($socket, '127.0.0.1', 0)) {\n    die(sprintf(\n        'An error occurred while binding socket: %s',\n        socket_strerror(socket_last_error($socket))));\n}\n\nsocket_getsockname($socket, $address, $port);\n\n$msg = \"Ping!\";\n$len = strlen($msg);\n$sent = socket_sendto($socket, $msg, $len, 0, $address, $port);\nif ($sent === false) {\n    die(sprintf(\n        'An error occurred while sending to the socket: %s',\n        socket_strerror(socket_last_error($socket))));\n} else if ($sent != $len) {\n    die(sprintf(\n        '%d bytes have been sent instead of the %d bytes expected',\n        $sent, $len));\n}\n\n$wants = $len;\n$recvd = 0;\n$buf   = null;\n\nwhile ($recvd < $len) {\n    $bytes = socket_recvfrom(\n        $socket, $buffering, $wants, 0, $address, $port);\n\n    if (($bytes === false) && ($errno = socket_last_error($socket))) {\n        if ($errno = SOCKET_EAGAIN) {\n            socket_clear_error($socket);\n            continue;\n        }\n\n        die(sprintf(\n            'An error occurred while sending to the socket: %s',\n            socket_strerror($errno)));\n    }\n\n    $recvd += $bytes;\n    $wants -= $bytes;\n    $buf .= $buffering;\n}\n\nif ($recvd != $len) {\n    die(sprintf(\n        '%d bytes have been received instead of the %d bytes expected',\n        $recvd, $len));\n}\n\necho \"Received $buf from remote address $address and remote port $port\" . PHP_EOL;\n});\n?>\n--EXPECTF--\nReceived Ping! from remote address %s and remote port %d\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_ipv6_udp.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: Test if socket_recvfrom() receives data sent by socket_sendto() via IPv6 UDP\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('SKIP The sockets extension is not loaded.');\n}\nif (substr(PHP_OS, 0, 3) == 'WIN') {\n    die('skip Not valid for Windows');\n}\nrequire 'ipv6_skipif.inc';\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n$socket = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP);\nif (!$socket) {\n    die('Unable to create AF_INET6 socket');\n}\n\nif (!socket_bind($socket, '::1', 0)) {\n    die(sprintf(\n        'An error occurred while binding socket: %s',\n        socket_strerror(socket_last_error($socket))));\n}\n\nsocket_getsockname($socket, $address, $port);\n\n$msg = \"Ping!\";\n$len = strlen($msg);\n$sent = socket_sendto($socket, $msg, $len, 0, $address, $port);\nif ($sent === false) {\n    die(sprintf(\n        'An error occurred while sending to the socket: %s',\n        socket_strerror(socket_last_error($socket))));\n} else if ($sent != $len) {\n    die(sprintf(\n        '%d bytes have been sent instead of the %d bytes expected',\n        $sent, $len));\n}\n\n$wants = $len;\n$recvd = 0;\n$buf   = null;\n\nwhile ($recvd < $len) {\n    $bytes = socket_recvfrom(\n        $socket, $buffering, $wants, 0, $address, $port);\n\n    if (($bytes === false) && ($errno = socket_last_error($socket))) {\n        if ($errno = SOCKET_EAGAIN) {\n            socket_clear_error($socket);\n            continue;\n        }\n\n        die(sprintf(\n            'An error occurred while sending to the socket: %s',\n            socket_strerror($errno)));\n    }\n\n    $recvd += $bytes;\n    $wants -= $bytes;\n    $buf .= $buffering;\n}\n\nif ($recvd != $len) {\n    die(sprintf(\n        '%d bytes have been received instead of the %d bytes expected',\n        $recvd, $len));\n}\n\necho \"Received $buf from remote address $address and remote port $port\" . PHP_EOL;\n});\n?>\n--EXPECTF--\nReceived Ping! from remote address %s and remote port %d\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_sentto_recvfrom_unix.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: Test if socket_recvfrom() receives data sent by socket_sendto() through a Unix domain socket\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (substr(PHP_OS, 0, 3) == 'WIN') {\n    die('skip.. Not valid for Windows');\n}\nif (!extension_loaded('sockets')) {\n    die('SKIP The sockets extension is not loaded.');\n}?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../../include/bootstrap.php';\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n    $socket = socket_create(AF_UNIX, SOCK_DGRAM, 0);\n    if (!$socket) {\n        die('Unable to create AF_UNIX socket');\n    }\n    if (!socket_set_nonblock($socket)) {\n        die('Unable to set nonblocking mode for socket');\n    }\n    var_dump(socket_recvfrom($socket, $buf, 12, 0, $from, $port)); //false (EAGAIN, no warning)\n    $address = sprintf(\"/tmp/%s.sock\", uniqid());\n    if (!socket_bind($socket, $address)) {\n        die(\"Unable to bind to $address\");\n    }\n\n    $msg = \"Ping!\";\n    $len = strlen($msg);\n    $bytes_sent = socket_sendto($socket, $msg, $len, 0, $address);\n    if ($bytes_sent == -1) {\n        die('An error occurred while sending to the socket');\n    } else if ($bytes_sent != $len) {\n        die($bytes_sent . ' bytes have been sent instead of the ' . $len . ' bytes expected');\n    }\n\n    $from = \"\";\n    var_dump(socket_recvfrom($socket, $buf, 0, 0, $from));\n    $bytes_received = socket_recvfrom($socket, $buf, 12, 0, $from);\n    if ($bytes_received == -1) {\n        die('An error occurred while receiving from the socket');\n    } else if ($bytes_received != $len) {\n        die($bytes_received . ' bytes have been received instead of the ' . $len . ' bytes expected');\n    }\n    echo \"Received $buf\";\n\n    socket_close($socket);\n});\n?>\n--EXPECTF--\nbool(false)\nbool(false)\nReceived Ping!\n--CREDITS--\nFalko Menge <mail at falko-menge dot de>\nPHP Testfest Berlin 2009-05-09\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_set_block-retval.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: Test socket_set_block return values\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('SKIP The sockets extension is not loaded.');\n}\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n    $socket = socket_create_listen(31339);\n    var_dump(socket_set_block($socket));\n    socket_close($socket);\n\n    $socket2 = socket_create_listen(31340);\n    socket_close($socket2);\n    try {\n        var_dump(socket_set_block($socket2));\n    } catch (Error $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n});\n?>\n--EXPECT--\nbool(true)\nbool(false)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_set_nonblock-retval.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: Test socket_set_nonblock return values\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('SKIP The sockets extension is not loaded.');\n}\n?>\n--FILE--\n<?php\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n    $socket = socket_create_listen(31339);\n    var_dump(socket_set_nonblock($socket));\n    socket_close($socket);\n\n    $socket2 = socket_create_listen(31340);\n    socket_close($socket2);\n    try {\n        var_dump(socket_set_nonblock($socket2));\n    } catch (Error $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n});\n?>\n--EXPECT--\nbool(true)\nbool(false)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_set_nonblock.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: ext/sockets - socket_set_block - basic test\n--CREDITS--\nFlorian Anderiasch\nfa@php.net\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\n    if (!extension_loaded('sockets')) {\n        die('skip sockets extension not available.');\n    }\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../../include/bootstrap.php';\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n    $s_c_l = socket_create_listen(0);\n    socket_set_nonblock($s_c_l);\n    Assert::isInstanceOf($s_c_l, Swoole\\Coroutine\\Socket::class);\n    //socket_accept($s_c_l);\n    socket_close($s_c_l);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_set_option_bindtodevice.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic:SO_BINDTODEVICE\n--DESCRIPTION--\n-Bind to loopback 'lo' device (should exist)\n-Bind to unexisting device\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n        die('SKIP sockets extension not available.');\n}\nif (!defined(\"SO_BINDTODEVICE\")) {\n    die('SKIP SO_BINDTODEVICE not supported on this platform.');\n}\nif (!function_exists(\"posix_getuid\") || posix_getuid() != 0) {\n    die('SKIP SO_BINDTODEVICE requires root permissions.');\n}\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n\nif (!$socket) {\n        die('Unable to create AF_INET socket [socket]');\n}\n// wrong params\n$retval_1 = socket_set_option( $socket, SOL_SOCKET, SO_BINDTODEVICE, \"lo\");\nvar_dump($retval_1);\n$retval_2 = socket_set_option( $socket, SOL_SOCKET, SO_BINDTODEVICE, \"ethIDONOTEXIST\");\nvar_dump($retval_2);\n\nsocket_close($socket);\n});\n?>\n--EXPECTF--\nbool(true)\n\nWarning: Swoole\\Coroutine\\Socket::setOption(): setsockopt(%d) failed, Error: No such device[%d] in %s on line %d\nbool(false)\n--CREDITS--\nDamjan Cvetko, foreach.org\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_set_option_error_socket_option.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: Test if socket_set_option() returns 'Unable to set socket option' failure for invalid options\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('SKIP sockets extension not available.');\n}\nif (PHP_OS == 'Darwin') {\n    die('skip Not for OSX');\n}\n$filename = __FILE__ . '.root_check.tmp';\n$fp = fopen($filename, 'w');\nfclose($fp);\nif (fileowner($filename) == 0) {\n    unlink($filename);\n    die('SKIP Test cannot be run as root.');\n}\nunlink($filename);\n?>\n--FILE--\n<?php\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n    if (!$socket) {\n        die('Unable to create AF_INET socket [socket]');\n    }\n\n    socket_set_option($socket, SOL_SOCKET, 1, 1);\n    socket_close($socket);\n});\n?>\n--EXPECTF--\nWarning: Swoole\\Coroutine\\Socket::setOption(): setsockopt(%d) failed, Error: Permission denied[13] in %s on line %d\n--CREDITS--\nMoritz Neuhaeuser, info@xcompile.net\nPHP Testfest Berlin 2009-05-10\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_set_option_in6_pktinfo.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: socket_set_option() with IPV6_PKTINFO\n--SKIPIF--\n<?php \nrequire __DIR__ . '/../../../include/skipif.inc'; \nskip_if_darwin();\n?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('skip sockets extension not available.');\n}\n\nrequire 'ipv6_skipif.inc';\n\nif (!defined('IPPROTO_IPV6')) {\n    die('skip IPv6 not available.');\n}\nif (substr(PHP_OS, 0, 3) == 'WIN') {\n    die('skip Not for Windows!');\n}\nif (!defined('IPV6_PKTINFO')) {\n    die('skip IPV6_PKTINFO not available.');\n} ?>\n--FILE--\n<?php\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n    $s = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP) or die(\"err\");\n    var_dump(socket_set_option($s, IPPROTO_IPV6, IPV6_PKTINFO, []));\n    var_dump(socket_set_option($s, IPPROTO_IPV6, IPV6_PKTINFO, [\n        \"addr\" => '::1',\n        \"ifindex\" => 0\n    ]));\n//Oddly, Linux does not support IPV6_PKTINFO in sockgetopt().\n//See do_ipv6_getsockopt() on the kernel sources\n//A work-around with is sort-of possible (with IPV6_2292PKTOPTIONS),\n//but not worth it\n//var_dump(socket_get_option($s, IPPROTO_IPV6, IPV6_PKTINFO));\n});\n?>\n--EXPECTF--\nWarning: Swoole\\Coroutine\\Socket::setOption(): error converting user data (path: in6_pktinfo): The key 'addr' is required in %s on line %d\nbool(false)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_set_option_rcvtimeo.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic:SO_RCVTIMEO\n--DESCRIPTION--\n-wrong params\n-set/get params comparison\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('SKIP sockets extension not available.');\n}\n?>\n--FILE--\n<?php\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n    if (!$socket) {\n        die('Unable to create AF_INET socket [socket]');\n    }\n    socket_set_block($socket);\n\n//wrong params\n    try {\n        $retval_1 = socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, []);\n    } catch (\\ValueError $e) {\n        echo $e->getMessage() . \\PHP_EOL;\n    }\n\n//set/get comparison\n    $options = array(\"sec\" => 1, \"usec\" => 0);\n    $retval_2 = socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $options);\n    $retval_3 = socket_get_option($socket, SOL_SOCKET, SO_RCVTIMEO);\n\n    var_dump($retval_2);\n    var_dump($retval_3 === $options);\n    socket_close($socket);\n});\n?>\n--EXPECTF--\nWarning: Swoole\\Coroutine\\Socket::setOption(): no key \"sec\" passed in optval in %s on line %d\nbool(true)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_set_option_seolinger.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic:SO_SEOLINGER\n--DESCRIPTION--\n-wrong params\n-set/get params comparison\n-l_linger not given\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('SKIP sockets extension not available.');\n}\n?>\n--FILE--\n<?php\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n\n    if (!$socket) {\n        die('Unable to create AF_INET socket [socket]');\n    }\n\n    // wrong params\n    try {\n        socket_set_option($socket, SOL_SOCKET, SO_LINGER, []);\n    } catch (\\ValueError $e) {\n        echo $e->getMessage() . \\PHP_EOL;\n    }\n\n    // set/get comparison\n    $options = array(\"l_onoff\" => 1, \"l_linger\" => 1);\n    $retval_2 = socket_set_option($socket, SOL_SOCKET, SO_LINGER, $options);\n    var_dump($retval_2);\n\n    $retval_3 = socket_get_option($socket, SOL_SOCKET, SO_LINGER);\n\n    // l_linger not given\n    $options_2 = array(\"l_onoff\" => 1);\n    try {\n        var_dump(socket_set_option($socket, SOL_SOCKET, SO_LINGER, $options_2));\n    } catch (\\ValueError $e) {\n        echo $e->getMessage() . \\PHP_EOL;\n    }\n\n    var_dump($retval_3[\"l_linger\"] === $options[\"l_linger\"]);\n    // value of l_onoff is not always 1, Darwin returns 128\n    var_dump((bool)$retval_3[\"l_onoff\"] === (bool)$options[\"l_onoff\"]);\n\n    socket_close($socket);\n});\n?>\n--EXPECTF--\n\nWarning: Swoole\\Coroutine\\Socket::setOption(): no key \"l_onoff\" passed in optval in %s on line %d\nbool(true)\n\nWarning: Swoole\\Coroutine\\Socket::setOption(): no key \"l_linger\" passed in optval in %s on line %d\nbool(false)\nbool(true)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_set_option_sndtimeo.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic:SO_SNDTIMEO\n--DESCRIPTION--\n-wrong params\n-set/get params comparison\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n        die('SKIP sockets extension not available.');\n}\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\nif (!$socket) {\n        die('Unable to create AF_INET socket [socket]');\n}\nsocket_set_block($socket);\n\n//wrong params\ntry {\n    $retval_1 = socket_set_option( $socket, SOL_SOCKET, SO_SNDTIMEO, []);\n} catch (\\ValueError $e) {\n    echo $e->getMessage() . \\PHP_EOL;\n}\n\n//set/get comparison\n$options = array(\"sec\" => 1, \"usec\" => 0);\n$retval_2 = socket_set_option( $socket, SOL_SOCKET, SO_SNDTIMEO, $options);\n$retval_3 = socket_get_option( $socket, SOL_SOCKET, SO_SNDTIMEO);\n\nvar_dump($retval_2);\nvar_dump($retval_3 === $options);\nsocket_close($socket);\n});\n?>\n--EXPECTF--\nWarning: Swoole\\Coroutine\\Socket::setOption(): no key \"sec\" passed in optval %s on line %d\nbool(true)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_setopt_basic.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: Test socket_setopt() basic functionality\n--CREDITS--\nRodrigo Prado de Jesus <royopa [at] gmail [dot] com>\nUser Group: PHPSP #PHPTestFestBrasil\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('skip. Sockets extension is not available.');\n}\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\nif (!$socket) {\n    die('Unable to create AF_INET socket [socket]');\n}\nsocket_set_block($socket);\n\n//set/get comparison\n$options = array(\"sec\" => 1, \"usec\" => 0);\n$retval_1 = socket_setopt( $socket, SOL_SOCKET, SO_SNDTIMEO, $options);\n$retval_2 = socket_getopt( $socket, SOL_SOCKET, SO_SNDTIMEO);\n\nvar_dump($retval_1);\nvar_dump($retval_2 === $options);\nsocket_close($socket);\n});\n?>\n--EXPECT--\nbool(true)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_shutdown.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: bool socket_shutdown ( resource $socket [, int $how = 2 ] ) ;\n--CREDITS--\nmarcosptf - <marcosptf@yahoo.com.br> - #phparty7 - @phpsp - novatec/2015 - sao paulo - br\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (getenv(\"SKIP_ONLINE_TESTS\")) die(\"skip online test\");\nif (!extension_loaded('sockets')) {\n  die('SKIP sockets extension not available.');\n}\nif(substr(PHP_OS, 0, 3) == 'WIN' ) {\n    die('skip not for windows');\n}\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n    $host = \"yahoo.com\";\n    $port = 80;\n\n    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n    $socketConn = socket_connect($socket, $host, $port);\n    var_dump(socket_shutdown($socket, 0));\n    socket_close($socket);\n\n    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n    $socketConn = socket_connect($socket, $host, $port);\n    var_dump(socket_shutdown($socket, 1));\n    socket_close($socket);\n\n    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n    $socketConn = socket_connect($socket, $host, $port);\n    var_dump(socket_shutdown($socket, 2));\n    socket_close($socket);\n\n    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n    var_dump(socket_shutdown($socket, 0));\n\n    $socketConn = socket_connect($socket, $host, $port);\n    var_dump(socket_shutdown($socket, -1));\n    socket_close($socket);\n});\n?>\n--CLEAN--\n<?php\nunset($host);\nunset($port);\nunset($socket);\nunset($socketConn);\n?>\n--EXPECTF--\nbool(true)\nbool(true)\nbool(true)\nbool(false)\nbool(false)\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/socket_strerror.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: ext/sockets - socket_strerror - basic test\n--CREDITS--\nFlorian Anderiasch\nfa@php.net\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (!extension_loaded('sockets')) {\n    die('skip sockets extension not available.');\n}\nif (!stristr(PHP_OS, \"Linux\")) {\n    die('skip - test validates linux error strings only.');\n}\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n/* Only test one representative error code here,\n * as messages will differ depending on the used libc. */\nvar_dump(socket_strerror(1));\n});\n?>\n--EXPECT--\nstring(23) \"Operation not permitted\"\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/basic/unixloop.phpt",
    "content": "--TEST--\nswoole_runtime/sockets/basic: Unix domain socket Loopback test\n--SKIPIF--\n<?php require __DIR__ . '/../../../include/skipif.inc'; ?>\n<?php\nif (substr(PHP_OS, 0, 3) == 'WIN') {\n    die('skip.. Not valid for Windows');\n}\n    if (!extension_loaded('sockets')) {\n        die('skip sockets extension not available.');\n    }\n?>\n--FILE--\n<?php\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nRuntime::setHookFlags(SWOOLE_HOOK_SOCKETS);\n\nrun(function () {\n\n    $sock_path = sprintf(\"/tmp/%s.sock\", uniqid());\n\n    if (file_exists($sock_path))\n        die('Temporary socket already exists.');\n\n    /* Setup socket server */\n    $server = socket_create(AF_UNIX, SOCK_STREAM, 0);\n    if (!$server) {\n        die('Unable to create AF_UNIX socket [server]');\n    }\n    if (!socket_bind($server,  $sock_path)) {\n        die(\"Unable to bind to $sock_path\");\n    }\n    if (!socket_listen($server, 2)) {\n        die('Unable to listen on socket');\n    }\n\n    /* Connect to it */\n    $client = socket_create(AF_UNIX, SOCK_STREAM, 0);\n    if (!$client) {\n        die('Unable to create AF_UNIX socket [client]');\n    }\n    if (!socket_connect($client, $sock_path)) {\n        die('Unable to connect to server socket');\n    }\n\n    /* Accept that connection */\n    $socket = socket_accept($server);\n    if (!$socket) {\n        die('Unable to accept connection');\n    }\n\n    socket_write($client, \"ABCdef123\\n\");\n\n    $data = socket_read($socket, 10, PHP_BINARY_READ);\n    var_dump($data);\n\n    socket_close($client);\n    socket_close($socket);\n    socket_close($server);\n    @unlink($sock_path);\n});\n?>\n--EXPECT--\nstring(10) \"ABCdef123\n\"\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/error.phpt",
    "content": "--TEST--\nswoole_runtime/sockets: error\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_ALL);\n\n$port = get_one_free_port();\n\nrun(function () use($port) {\n    $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n    socket_connect($sock, '127.0.0.1', $port);\n\n    Assert::eq(socket_last_error(), SOCKET_ECONNREFUSED);\n    Assert::eq(socket_last_error(), socket_last_error($sock));\n\n    socket_clear_error();\n    Assert::eq(socket_last_error(), 0);\n    Assert::eq(socket_last_error($sock), SOCKET_ECONNREFUSED);\n    socket_clear_error($sock);\n    Assert::eq(socket_last_error($sock), 0);\n});\necho \"Done\\n\";\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/get_name.phpt",
    "content": "--TEST--\nswoole_runtime/sockets: getsockname & getpeername\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_ALL);\n\nrun(function () {\n    $sock = socket_create_listen(0);\n    Assert::true(socket_getsockname($sock, $server_addr, $server_port));\n\n    go(function () use ($server_addr, $server_port) {\n        $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n        socket_connect($sock, $server_addr, $server_port);\n\n        Assert::true(socket_getsockname($sock, $client_addr, $client_port));\n        socket_send($sock, \"$client_addr:$client_port\", 0, 0);\n        socket_recv($sock, $buf, 1024, 0);\n        Assert::eq($buf, \"$server_addr:$server_port\");\n\n        socket_getpeername($sock, $addr, $port);\n        Assert::eq($port, $server_port);\n\n        socket_close($sock);\n    });\n\n    $cli = socket_accept($sock);\n    $data = socket_read($cli, 1024);\n\n    Assert::true(socket_getpeername($cli, $addr, $port));\n    Assert::eq($data, \"$addr:$port\");\n    socket_write($cli, \"$server_addr:$server_port\");\n    usleep(10000);\n    socket_close($cli);\n});\necho \"Done\\n\";\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/import.phpt",
    "content": "--TEST--\nswoole_runtime/sockets: import\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_ALL);\n\nrun(function () {\n    $s = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0);\n\n    $s0 = reset($s);\n    $s1 = next($s);\n\n    $sock = socket_import_stream($s0);\n    Assert::notEmpty($sock);\n    socket_write($sock, \"test message\");\n    socket_close($sock);\n\n    var_dump(stream_get_contents($s1));\n});\necho \"Done\\n\";\n?>\n--EXPECT--\nstring(12) \"test message\"\nDone\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/nonblock.phpt",
    "content": "--TEST--\nswoole_runtime/sockets: timeout\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_ALL);\n$GLOBALS['port'] = get_one_free_port();\n\nrun(function () {\n    go(function () {\n        $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n        socket_bind($sock, '127.0.0.1', $GLOBALS['port']);\n        socket_listen($sock, 128);\n\n        $cli = socket_accept($sock);\n        $data = socket_read($cli, 1024);\n        usleep(50 * 1000);\n        socket_write($cli, \"Swoole: $data\");\n        socket_close($cli);\n    });\n\n\n    go(function () {\n        $s = microtime(true);\n        $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n        socket_connect($sock, '127.0.0.1', $GLOBALS['port']);\n        socket_send($sock, \"hello world\", 0, 0);\n\n        socket_set_nonblock($sock);\n        Assert::eq(socket_recv($sock, $buf, 1024, 0), false);\n        Assert::eq(socket_last_error($sock), SOCKET_ETIMEDOUT);\n\n        socket_set_block($sock);\n        $n = socket_recv($sock, $buf, 1024, 0);\n        Assert::greaterThanEq($n, 10);\n        Assert::eq(strlen($buf), $n);\n        Assert::eq($buf, 'Swoole: hello world');\n\n        socket_close($sock);\n    });\n});\necho \"Done\\n\";\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/socketpair.phpt",
    "content": "--TEST--\nswoole_runtime/sockets: tcp server\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_ALL);\n\nconst N = 4;\n\nrun(function () {\n    $pair = [];\n    Assert::true(socket_create_pair(AF_UNIX, SOCK_DGRAM, 0, $pair));\n\n    go(function () use ($pair) {\n        $n = N;\n        while ($n--) {\n            $data = \"hello co-2, #$n\\n\";\n            socket_write($pair[0], $data);\n        }\n\n        $n = N;\n        while ($n--) {\n            echo socket_read($pair[0], 1024);\n        }\n    });\n\n    go(function () use ($pair) {\n        $n = N;\n        while ($n--) {\n            $data = \"hello co-1, #$n\\n\";\n            socket_write($pair[1], $data);\n        }\n\n        $n = N;\n        while ($n--) {\n            echo socket_read($pair[1], 1024);\n        }\n    });\n\n});\necho \"Done\\n\";\n?>\n--EXPECT--\nhello co-2, #3\nhello co-2, #2\nhello co-2, #1\nhello co-2, #0\nhello co-1, #3\nhello co-1, #2\nhello co-1, #1\nhello co-1, #0\nDone\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/tcp_client.phpt",
    "content": "--TEST--\nswoole_runtime/sockets: tcp client\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nconst N = 8;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_ALL);\n\n$GLOBALS['time'] = [];\n$s = microtime(true);\nrun(function () {\n    $n = N;\n    while($n--) {\n        go(function() {\n            $s = microtime(true);\n            $domain = 'www.baidu.com';\n            $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n            socket_connect($sock, $domain, 80);\n            socket_write($sock, \"GET / HTTP/1.0\\r\\nHost: $domain\\r\\nConnection: close\\r\\nKeep-Alive: off\\r\\n\\r\\n\");\n\n            $html = '';\n            while(true) {\n                $data = socket_read($sock, 8192);\n                if ($data == '') {\n                    break;\n                }\n                $html .= $data;\n            }\n\n            Assert::greaterThanEq(strlen($html), 10000);\n            Assert::contains($html, 'baidu.com');\n            socket_close($sock);\n\n            $GLOBALS['time'][] = microtime(true) - $s;\n        });\n    }\n});\necho \"Done\\n\";\nAssert::lessThanEq(microtime(true) - $s, array_sum($GLOBALS['time']) / 3);\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/tcp_server.phpt",
    "content": "--TEST--\nswoole_runtime/sockets: tcp server\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nconst N = 8;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_ALL);\n\n$GLOBALS['port'] = get_one_free_port();\n$GLOBALS['time'] = [];\n$s = microtime(true);\nrun(function () {\n    go(function () {\n        $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n        socket_bind($sock, '127.0.0.1', $GLOBALS['port']);\n        socket_listen($sock, 128);\n\n        $n = N;\n        while ($n--) {\n            $cli = socket_accept($sock);\n            go(function () use ($cli) {\n                $data = socket_read($cli, 1024);\n                usleep(30 * 1000);\n                socket_write($cli, \"Swoole: $data\");\n                socket_close($cli);\n            });\n        }\n    });\n\n    $n = N;\n    while ($n--) {\n        go(function () {\n            $s = microtime(true);\n            $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n            socket_connect($sock, '127.0.0.1', $GLOBALS['port']);\n            socket_send($sock, \"hello world\", 0, 0);\n            socket_recv($sock, $buf, 1024, 0);\n            Assert::greaterThanEq(strlen($buf), 15);\n            Assert::eq($buf, 'Swoole: hello world');\n            socket_close($sock);\n            $GLOBALS['time'][] = microtime(true) - $s;\n        });\n    }\n});\necho \"Done\\n\";\nAssert::lessThanEq(microtime(true) - $s, array_sum($GLOBALS['time']) / 3);\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/timeout.phpt",
    "content": "--TEST--\nswoole_runtime/sockets: timeout\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine(SWOOLE_HOOK_ALL);\n$GLOBALS['port'] = get_one_free_port();\n\nrun(function () {\n    go(function () {\n        $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n        socket_bind($sock, '127.0.0.1', $GLOBALS['port']);\n        socket_listen($sock, 128);\n\n        $cli = socket_accept($sock);\n        $data = socket_read($cli, 1024);\n        usleep(60 * 1000);\n        socket_write($cli, \"Swoole: $data\");\n        socket_close($cli);\n    });\n\n\n    go(function () {\n        $s = microtime(true);\n        $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n        socket_connect($sock, '127.0.0.1', $GLOBALS['port']);\n        socket_send($sock, \"hello world\", 0, 0);\n\n        $timeout = array(\n            \"sec\"=> 0,\n            \"usec\"=> 50000,\n        );\n        socket_set_option($sock, SOL_SOCKET, SO_RCVTIMEO, $timeout);\n        Assert::eq(socket_get_option($sock, SOL_SOCKET, SO_RCVTIMEO), $timeout);\n        Assert::eq(socket_recv($sock, $buf, 1024, 0), false);\n        Assert::eq(socket_last_error($sock), SOCKET_ETIMEDOUT);\n        $n = socket_recv($sock, $buf, 1024, 0);\n        Assert::greaterThanEq($n, 10);\n        Assert::eq(strlen($buf), $n);\n        Assert::eq($buf, 'Swoole: hello world');\n        socket_close($sock);\n    });\n});\necho \"Done\\n\";\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_runtime/sockets/udp.phpt",
    "content": "--TEST--\nswoole_runtime/sockets: udp\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\nuse function Swoole\\Coroutine\\run;\n\nconst N = 8;\nconst GREETINGS = 'hello world';\n\n$GLOBALS['port'] = get_one_free_port();\n\nRuntime::enableCoroutine(SWOOLE_HOOK_ALL);\n\nrun(function () {\n    go(function () {\n        $sock = socket_create(AF_INET, SOCK_DGRAM, 0);\n        Assert::true(socket_bind($sock, '127.0.0.1', $GLOBALS['port']));\n\n        $n = N;\n        while ($n--) {\n            $len = socket_recvfrom($sock, $data, 1024, 0, $addr, $port);\n            Assert::eq($data, GREETINGS.\" from $addr:$port\");\n            $resp = \"Swoole: $data\";\n            socket_sendto($sock, $resp, strlen($resp), 0, $addr, $port);\n        }\n    });\n\n    $n = N;\n    while ($n--) {\n        go(function () {\n            $sock = socket_create(AF_INET, SOCK_DGRAM, 0);\n            Assert::true(socket_connect($sock, '127.0.0.1', $GLOBALS['port']));\n            socket_getsockname($sock, $addr, $port);\n            $pkt = GREETINGS.\" from $addr:$port\";\n            Assert::eq(socket_send($sock, $pkt, strlen($pkt), 0), strlen($pkt));\n            socket_recv($sock, $buf, 1024, 0);\n            Assert::eq($buf, \"Swoole: $pkt\");\n            socket_close($sock);\n        });\n    }\n});\necho \"Done\\n\";\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_runtime/ssl/capture_peer_cert.phpt",
    "content": "--TEST--\nswoole_runtime/ssl: capture_peer_cert\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_no_ssl();\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nfunction capture_peer_cert($domain)\n{\n    $g = stream_context_create([\n        \"ssl\" => [\n            \"capture_peer_cert\" => true,\n            'capture_peer_cert_chain' => true,\n            'verify_peer' => false\n        ]\n    ]);\n    $r = stream_socket_client(\"ssl://{$domain}:443\", $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $g);\n    if (!$r) {\n        return false;\n    }\n    $cont = stream_context_get_params($r);\n    if (!$cont) {\n        return false;\n    }\n    return $cont;\n}\n\nSwoole\\Runtime::setHookFlags(SWOOLE_HOOK_ALL);\n\nCo\\run(function () {\n    $result = capture_peer_cert('www.baidu.com');\n    $info1 = openssl_x509_parse($result[\"options\"][\"ssl\"][\"peer_certificate\"]);\n    Assert::isArray($info1);\n    Assert::contains($info1['name'], 'baidu.com');\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/ssl/client.phpt",
    "content": "--TEST--\nswoole_runtime/ssl: ssl client\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_no_ssl();\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\ngo(function () {\n    $fp = stream_socket_client(\"ssl://www.baidu.com:443\", $errno, $errstr, 30);\n    if (!$fp) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        $http = \"GET / HTTP/1.0\\r\\nAccept: */*User-Agent: Lowell-Agent\\r\\nHost: www.baidu.com\\r\\nConnection: Close\\r\\n\\r\\n\";\n        fwrite($fp, $http);\n        $content = '';\n        while (!feof($fp)) {\n            $content .= fread($fp, 1024);\n        }\n        fclose($fp);\n        Assert::assert(strpos($content,'map.baidu.com') !== false);\n    }\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/ssl/enable_crypto.phpt",
    "content": "--TEST--\nswoole_runtime/ssl: stream_socket_enable_crypto\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_no_ssl();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\n$ready = new Chan;\n\ngo(function () use ($ready) {\n    $context = stream_context_create();\n    stream_context_set_option($context, 'ssl', 'allow_self_signed', true);\n    stream_context_set_option($context, 'ssl', 'verify_peer', true);\n    stream_context_set_option($context, 'ssl', 'local_cert', SSL_FILE_DIR.'/server.crt');\n    stream_context_set_option($context, 'ssl', 'local_pk', SSL_FILE_DIR.'/server.key');\n    stream_context_set_option($context, 'ssl', 'cafile', SSL_FILE_DIR.'/ca.crt');\n\n    $socket = stream_socket_server(\"ssl://0.0.0.0:8000\", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $context);\n    if (!$socket) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        $ready->push(true);\n        $conn = stream_socket_accept($socket);\n\n        fwrite($conn, 'The local time is ' . date('n/j/Y g:i a'));\n        fclose($conn);\n        fclose($socket);\n        echo \"OK\\n\";\n    }\n});\n\ngo(function () use ($ready) {\n    $ready->pop();\n\n    $fp = stream_socket_client(\"tcp://127.0.0.1:8000\", $errno, $errstr, 30);\n    if (!$fp) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        stream_context_set_option($fp, [\"ssl\" => [\n            \"local_cert\" => SSL_FILE_DIR . '/client.crt',\n            \"local_pk\" => SSL_FILE_DIR . '/client.key',\n        ]]);\n        // Enable SSL encryption after the connection is established\n        Assert::assert(stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT));\n        $data = fread($fp, 8192);\n        fclose($fp);\n        Assert::assert(strpos($data, 'local time') !== false);\n        echo \"OK\\n\";\n    }\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nOK\nOK\n"
  },
  {
    "path": "tests/swoole_runtime/ssl/local_cert.phpt",
    "content": "--TEST--\nswoole_runtime/ssl: client with local_cert/local_pk\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_no_ssl();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\n$ready = new Chan;\n\ngo(function () use ($ready) {\n    $context = stream_context_create();\n    stream_context_set_option($context, 'ssl', 'allow_self_signed', true);\n    stream_context_set_option($context, 'ssl', 'verify_peer', true);\n    stream_context_set_option($context, 'ssl', 'local_cert', SSL_FILE_DIR.'/server.crt');\n    stream_context_set_option($context, 'ssl', 'local_pk', SSL_FILE_DIR.'/server.key');\n    stream_context_set_option($context, 'ssl', 'cafile', SSL_FILE_DIR.'/ca.crt');\n\n    $socket = stream_socket_server(\"ssl://0.0.0.0:8000\", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $context);\n    if (!$socket) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        $ready->push(true);\n        $conn = stream_socket_accept($socket);\n        fwrite($conn, 'The local time is ' . date('n/j/Y g:i a'));\n        fclose($conn);\n        fclose($socket);\n        echo \"OK\\n\";\n    }\n});\n\ngo(function () use ($ready) {\n    $ready->pop();\n\n    $context = stream_context_create();\n    stream_context_set_option($context, 'ssl', 'local_cert', SSL_FILE_DIR . '/client.crt');\n    stream_context_set_option($context, 'ssl', 'local_pk', SSL_FILE_DIR . '/client.key');\n\n    $fp = stream_socket_client(\"ssl://127.0.0.1:8000\", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context);\n    if (!$fp) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        $data = fread($fp, 8192);\n        fclose($fp);\n        Assert::assert(strpos($data, 'local time') !== false);\n        echo \"OK\\n\";\n    }\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nOK\nOK\n"
  },
  {
    "path": "tests/swoole_runtime/ssl/server.phpt",
    "content": "--TEST--\nswoole_runtime/ssl: ssl server\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_no_ssl();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\n$ready = new Chan;\n\ngo(function () use ($ready) {\n    $context = stream_context_create();\n    stream_context_set_option($context, 'ssl', 'allow_self_signed', true);\n    stream_context_set_option($context, 'ssl', 'verify_peer', false);\n    stream_context_set_option($context, 'ssl', 'local_cert', SSL_FILE_DIR.'/server.crt');\n    stream_context_set_option($context, 'ssl', 'local_pk', SSL_FILE_DIR.'/server.key');\n    $socket = stream_socket_server(\"ssl://0.0.0.0:8000\", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $context);\n    if (!$socket) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        $ready->push(true);\n        $conn = stream_socket_accept($socket);\n        fwrite($conn, 'The local time is ' . date('n/j/Y g:i a'));\n        fclose($conn);\n        fclose($socket);\n        echo \"OK\\n\";\n    }\n});\n\ngo(function () use ($ready) {\n    $ready->pop();\n    $fp = stream_socket_client(\"ssl://127.0.0.1:8000\", $errno, $errstr, 30);\n    if (!$fp) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        $data = fread($fp, 8192);\n        fclose($fp);\n        Assert::assert(strpos($data,'local time') !== false);\n        echo \"OK\\n\";\n    }\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nOK\nOK\n"
  },
  {
    "path": "tests/swoole_runtime/ssl/without_key.phpt",
    "content": "--TEST--\nswoole_runtime/ssl: client without local_pk\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_no_ssl();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\n$ready = new Chan;\n\ngo(function () use ($ready) {\n    $context = stream_context_create();\n    stream_context_set_option($context, 'ssl', 'allow_self_signed', true);\n    stream_context_set_option($context, 'ssl', 'verify_peer', true);\n    stream_context_set_option($context, 'ssl', 'local_cert', SSL_FILE_DIR.'/server.crt');\n    stream_context_set_option($context, 'ssl', 'local_pk', SSL_FILE_DIR.'/server.key');\n    stream_context_set_option($context, 'ssl', 'cafile', SSL_FILE_DIR.'/ca.crt');\n\n    $socket = stream_socket_server(\"ssl://0.0.0.0:8000\", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $context);\n    if (!$socket) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        $ready->push(true);\n        $conn = stream_socket_accept($socket);\n        fwrite($conn, 'The local time is ' . date('n/j/Y g:i a'));\n        fclose($conn);\n        fclose($socket);\n        echo \"OK\\n\";\n    }\n});\n\ngo(function () use ($ready) {\n    $ready->pop();\n\n    $context = stream_context_create();\n    stream_context_set_option($context, 'ssl', 'local_cert', SSL_FILE_DIR . '/client.crt');\n\n    $fp = stream_socket_client(\"ssl://127.0.0.1:8000\", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context);\n    if (!$fp) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        $data = fread($fp, 8192);\n        fclose($fp);\n        Assert::assert(strpos($data, 'local time') !== false);\n        echo \"OK\\n\";\n    }\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nWarning: stream_socket_client(): ssl require key file in %s on line %d\nOK\nOK\n"
  },
  {
    "path": "tests/swoole_runtime/stdin.phpt",
    "content": "--TEST--\nswoole_runtime: stdin\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\n\nuse Swoole\\Process;\n\nrequire __DIR__ . '/../include/bootstrap.php';\n\nco::set(['socket_read_timeout' => -1]);\n\n$proc = new Process(function ($p) {\n    Co\\run(function () use ($p) {\n        $p->write('start' . PHP_EOL);\n        go(function () {\n            co::sleep(0.05);\n            echo \"sleep\\n\";\n        });\n        echo fread(STDIN, 1024);\n    });\n}, true, SOCK_STREAM);\n$proc->start();\n\necho $proc->read();\n\nusleep(100000);\n$proc->write('hello world' . PHP_EOL);\n\necho $proc->read();\necho $proc->read();\n\nProcess::wait();\n?>\n--EXPECT--\nstart\nsleep\nhello world\n"
  },
  {
    "path": "tests/swoole_runtime/stream_context.phpt",
    "content": "--TEST--\nswoole_runtime: stream context\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\ngo(function () {\n    $opts = array(\n        'socket' => array(\n            'bindto' => '0:7000',\n        ),\n    );\n    $ctx = stream_context_create($opts);\n    $fp = stream_socket_client(\"tcp://www.baidu.com:80\", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $ctx);\n    if (!$fp) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        fwrite($fp, \"GET / HTTP/1.0\\r\\nHost: www.baidu.com\\r\\nAccept: */*\\r\\n\\r\\n\");\n        $content = '';\n        stream_set_timeout($fp, 5, 30000);\n        while (!feof($fp)) {\n            $content .= fread($fp, 8192);\n        }\n        fclose($fp);\n        Assert::assert(strpos($content,'map.baidu.com') !== false);\n    }\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/stream_context_pass_null.phpt",
    "content": "--TEST--\nswoole_runtime: stream context pass null\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\ngo(function() {\n   //This function internal send null stream context parameter to `php_stream_open_wrapper_ex`\n   $md5 = md5_file('https://www.baidu.com');\n   var_dump(!empty($md5));\n});\nSwoole\\Event::wait();\n\n?>\n--EXPECT--\nbool(true)\n"
  },
  {
    "path": "tests/swoole_runtime/stream_copy_to_stream_socket.phpt",
    "content": "--TEST--\nswoole_runtime: stream_copy_to_stream() with socket as $source\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n<?php\n$sockets = @stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0);\nif (!$sockets) {\n    exit('skip stream_socket_pair');\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Runtime;\n\nuse function Swoole\\Coroutine\\run;\n\nRuntime::enableCoroutine();\n\nrun(function () {\n    $port = get_one_free_port();\n    $uri = \"tcp://127.0.0.1:{$port}\";\n    go(function () use ($uri) {\n        $socket = stream_socket_server($uri, $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);\n        if (!$socket) {\n            echo \"{$errstr} ({$errno})<br />\\n\";\n        } else {\n            $local = stream_socket_accept($socket);\n            $remote = stream_socket_client('tcp://' . TEST_DOMAIN_3 . ':80', $errno, $errstr, 30, STREAM_CLIENT_CONNECT);\n            go(function () use ($local, $remote) {\n                stream_copy_to_stream($local, $remote);\n            });\n            stream_copy_to_stream($remote, $local);\n            fclose($local);\n            fclose($remote);\n            fclose($socket);\n        }\n    });\n    $fp = stream_socket_client($uri, $errno, $errstr, 30);\n    if (!$fp) {\n        echo \"{$errstr} ({$errno})<br />\\n\";\n    } else {\n        $http = \"GET / HTTP/1.0\\r\\nAccept: */*User-Agent: Lowell-Agent\\r\\nHost: \" . TEST_DOMAIN_3 . \"\\r\\nConnection: Close\\r\\n\\r\\n\";\n        fwrite($fp, $http);\n        $content = '';\n        while (!feof($fp)) {\n            $content .= fread($fp, 1024);\n        }\n        fclose($fp);\n        Assert::assert(strpos($content, 'Location: https://www.gov.cn/') !== false);\n    }\n    echo \"DONE\\n\";\n});\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_runtime/stream_get_meta_data.phpt",
    "content": "--TEST--\nswoole_runtime: stream_get_meta_data\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Runtime;\n\nfunction test($port) {\n    $fp = stream_socket_client(\"tcp://127.0.0.1:\".$port, $errno, $errstr, 2);\n    if (!$fp) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        //200ms\n        stream_set_timeout($fp, 0, 200000);\n        $http = \"GET / HTTP/1.0\\r\\nAccept: */*User-Agent: Lowell-Agent\\r\\nHost: www.baidu.com\\r\\nConnection: Close\\r\\n\\r\\n\";\n        fwrite($fp, $http);\n        $content = fread($fp, 1024);\n        Assert::isEmpty($content);\n        $res = stream_get_meta_data($fp);\n        Assert::false($res['eof']);\n        Assert::true($res['blocked']);\n        Assert::true($res['timed_out']);\n        fclose($fp);\n    }\n}\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $port = $pm->getFreePort();\n    test($port);\n    Runtime::setHookFlags(SWOOLE_HOOK_ALL);\n    Co\\run(function () use ($port) {\n        test($port);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Swoole\\Server $serv, $fd, $rid, $data) {\n        //donot send any\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/stream_select/base.phpt",
    "content": "--TEST--\nswoole_runtime/stream_select: base\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    $fp1 = stream_socket_client(\"tcp://\" . TEST_DOMAIN_1 . \":80\", $errno, $errstr, 30);\n    $fp2 = stream_socket_client(\"tcp://\" . TEST_DOMAIN_2 . \":80\", $errno, $errstr, 30);\n    if (!$fp1) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        fwrite($fp1, \"GET / HTTP/1.0\\r\\nHost: \" . TEST_DOMAIN_1 . \"\\r\\nUser-Agent: curl/7.58.0\\r\\nAccept: */*\\r\\n\\r\\n\");\n        $r_array = [$fp1, $fp2];\n        $w_array = $e_array = null;\n        $n = stream_select($r_array, $w_array, $e_array, 10);\n        Assert::same($n, 1);\n        Assert::same(count($r_array), 1);\n        Assert::same($r_array[0], $fp1);\n        $html = '';\n        while (!feof($fp1)) {\n            $html .= fgets($fp1, 1024);\n        }\n        Assert::assert(strlen($html) > 1024);\n        fclose($fp1);\n    }\n});\nSwoole\\Runtime::enableCoroutine(0);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/stream_select/blocked.phpt",
    "content": "--TEST--\nswoole_runtime/stream_select: blocked\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine(false);\n$fp1 = stream_socket_client(\"tcp://\" . TEST_DOMAIN_1 . \":80\", $errno, $errstr, 30);\n$fp2 = stream_socket_client(\"tcp://\" . TEST_DOMAIN_2 . \":80\", $errno, $errstr, 30);\nif (!$fp1) {\n    echo \"$errstr ($errno)<br />\\n\";\n} else {\n    fwrite($fp1, \"GET / HTTP/1.0\\r\\nHost: \" . TEST_DOMAIN_1 . \"\\r\\nUser-Agent: curl/7.58.0\\r\\nAccept: */*\\r\\n\\r\\n\");\n    $r_array = [$fp1, $fp2];\n    $w_array = $e_array = null;\n    $n = stream_select($r_array, $w_array, $e_array, 10);\n    Assert::assert($n == 1);\n    Assert::assert(count($r_array) == 1);\n    Assert::assert($r_array[0] == $fp1);\n    $html = '';\n    while (!feof($fp1)) {\n        $html .= fgets($fp1, 1024);\n    }\n    Assert::assert(strlen($html) > 1024);\n    fclose($fp1);\n}\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_runtime/stream_select/bug46024.phpt",
    "content": "--TEST--\nswoole_runtime/stream_select: Bug #46024 stream_select() doesn't return the correct number\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nif (!getenv('TEST_PHP_EXECUTABLE')) {\n    exit('skip TEST_PHP_EXECUTABLE not defined');\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    $php = realpath(getenv('TEST_PHP_EXECUTABLE'));\n    $pipes = [];\n    $proc = proc_open(\n        \"$php -n -i\"\n        , [0 => ['pipe', 'r'], 1 => ['pipe', 'w']]\n        , $pipes, __DIR__, [], []\n    );\n    var_dump($proc);\n    if (!$proc) {\n        exit(1);\n    }\n    $r = [$pipes[1]];\n    $w = [$pipes[0]];\n    $e = null;\n    $ret = stream_select($r, $w, $e, 1);\n    var_dump($ret === (count($r) + count($w)));\n    fread($pipes[1], 1);\n\n    $r = [$pipes[1]];\n    $w = [$pipes[0]];\n    $e = null;\n    $ret = stream_select($r, $w, $e, 1);\n    var_dump($ret === (count($r) + count($w)));\n\n    foreach ($pipes as $pipe) {\n        fclose($pipe);\n    }\n    proc_terminate($proc);\n    if (defined('SIGKILL')) {\n        proc_terminate($proc, SIGKILL);\n    } else {\n        proc_terminate($proc);\n    }\n    proc_close($proc);\n});\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nresource(%d) of type (process/coroutine)\nbool(true)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_runtime/stream_select/bug60120.phpt",
    "content": "--TEST--\nswoole_runtime/stream_select: Bug #60120 proc_open hangs with stdin/out with 2048+ bytes\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\nskip_if_not_linux();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    error_reporting(E_ALL);\n\n    if (substr(PHP_OS, 0, 3) == 'WIN') {\n        $cmd = PHP_BINARY . ' -n -r \"fwrite(STDOUT, $in = file_get_contents(\\'php://stdin\\')); fwrite(STDERR, $in);\"';\n    } else {\n        $cmd = PHP_BINARY . ' -n -r \\'fwrite(STDOUT, $in = file_get_contents(\"php://stdin\")); fwrite(STDERR, $in);\\'';\n    }\n    $descriptors = [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']];\n    $stdin = str_repeat('*', 1024 * 16) . '!';\n    $stdin = str_repeat('*', 2049);\n\n    $options = array_merge(['suppress_errors' => true, 'bypass_shell' => false]);\n    $process = proc_open($cmd, $descriptors, $pipes, getcwd(), [], $options);\n\n    foreach ($pipes as $pipe) {\n        stream_set_blocking($pipe, false);\n    }\n    $writePipes = [$pipes[0]];\n    $stdinLen = strlen($stdin);\n    $stdinOffset = 0;\n\n    unset($pipes[0]);\n\n    while ($pipes || $writePipes) {\n        $r = $pipes;\n        $w = $writePipes;\n        $e = null;\n        $n = stream_select($r, $w, $e, 60);\n\n        if (false === $n) {\n            break;\n        } elseif ($n === 0) {\n            proc_terminate($process);\n        }\n\n        if ($w) {\n            $written = fwrite($writePipes[0], substr($stdin, $stdinOffset), 8192);\n            if (false !== $written) {\n                $stdinOffset += $written;\n            }\n            if ($stdinOffset >= $stdinLen) {\n                fclose($writePipes[0]);\n                $writePipes = null;\n            }\n        }\n\n        foreach ($r as $pipe) {\n            $type = array_search($pipe, $pipes);\n            $data = fread($pipe, 8192);\n            var_dump($data);\n            if (false === $data || feof($pipe)) {\n                fclose($pipe);\n                unset($pipes[$type]);\n            }\n        }\n    }\n});\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nstring(2049) \"%s\"\nstring(2049) \"%s\"\nstring(0) \"\"\nstring(0) \"\"\n"
  },
  {
    "path": "tests/swoole_runtime/stream_select/bug60602.phpt",
    "content": "--TEST--\nswoole_runtime/stream_select: Bug #60602 proc_open() modifies environment if it contains arrays\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    $descs = [\n        0 => ['pipe', 'r'], // stdin\n        1 => ['pipe', 'w'], // stdout\n        2 => ['pipe', 'w'], // strerr\n    ];\n\n    $environment = ['test' => [1, 2, 3]];\n\n    $cmd = (substr(PHP_OS, 0, 3) == 'WIN') ? 'dir' : 'ls';\n    $p = proc_open($cmd, $descs, $pipes, '.', $environment);\n\n    if (is_resource($p)) {\n        $data = '';\n\n        while (1) {\n            $w = $e = null;\n            $n = stream_select($pipes, $w, $e, 300);\n\n            if ($n === false) {\n                echo \"no streams \\n\";\n                break;\n            } else {\n                if ($n === 0) {\n                    echo \"process timed out\\n\";\n                    proc_terminate($p, 9);\n                    break;\n                } else {\n                    if ($n > 0) {\n                        $line = fread($pipes[1], 8192);\n                        if (strlen($line) == 0) {\n                            /* EOF */\n                            break;\n                        }\n                        $data .= $line;\n                    }\n                }\n            }\n        }\n        var_dump(strlen($data));\n\n        $ret = proc_close($p);\n        var_dump($ret);\n        var_dump(is_array($environment['test']));\n    } else {\n        echo \"no process\\n\";\n    }\n});\nSwoole\\Event::wait();\n?>\n--EXPECTF--\n%s: Array to string conversion in %s on line %d\nint(%d)\nint(0)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_runtime/stream_select/bug64438.phpt",
    "content": "--TEST--\nswoole_runtime/stream_select: Bug #64438 proc_open hangs with stdin/out with 4097+ bytes\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_darwin_todo();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Event;\nuse Swoole\\Runtime;\n\nRuntime::enableCoroutine();\ngo(function () {\n    error_reporting(E_ALL);\n\n    if (substr(PHP_OS, 0, 3) == 'WIN') {\n        $cmd = PHP_BINARY . ' -n -r \"fwrite(STDOUT, $in = file_get_contents(\\'php://stdin\\')); fwrite(STDERR, $in);\"';\n    } else {\n        $cmd = PHP_BINARY . ' -n -r \\'fwrite(STDOUT, $in = file_get_contents(\"php://stdin\")); fwrite(STDERR, $in);\\'';\n    }\n    $descriptors = [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']];\n    $stdin = str_repeat('*', 4097);\n\n    $options = array_merge(['suppress_errors' => true, 'bypass_shell' => false]);\n    $process = proc_open($cmd, $descriptors, $pipes, getcwd(), [], $options);\n\n    foreach ($pipes as $pipe) {\n        stream_set_blocking($pipe, false);\n    }\n    $writePipes = [$pipes[0]];\n    $stdinLen = strlen($stdin);\n    $stdinOffset = 0;\n\n    unset($pipes[0]);\n\n    while ($pipes || $writePipes) {\n        $r = $pipes;\n        $w = $writePipes;\n        $e = null;\n        $n = stream_select($r, $w, $e, 60);\n\n        if ($n === false) {\n            break;\n        }\n        if ($n === 0) {\n            proc_terminate($process);\n        }\n        if ($w) {\n            $written = fwrite($writePipes[0], substr($stdin, $stdinOffset), 8192);\n            if ($written !== false) {\n                $stdinOffset += $written;\n            }\n            if ($stdinOffset >= $stdinLen) {\n                fclose($writePipes[0]);\n                $writePipes = null;\n            }\n        }\n\n        foreach ($r as $pipe) {\n            $type = array_search($pipe, $pipes);\n            $data = fread($pipe, 8192);\n            var_dump($data);\n            if ($data === false || feof($pipe)) {\n                fclose($pipe);\n                unset($pipes[$type]);\n            }\n        }\n    }\n});\nEvent::wait();\n?>\n--EXPECTF--\nstring(4097) \"%s\"\nstring(4097) \"%s\"\nstring(0) \"\"\nstring(0) \"\"\n"
  },
  {
    "path": "tests/swoole_runtime/stream_select/bug64770.phpt",
    "content": "--TEST--\nswoole_runtime/stream_select: Bug #64770 stream_select() fails with pipes from proc_open()\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    $descs = [\n        0 => ['pipe', 'r'], // stdin\n        1 => ['pipe', 'w'], // stdout\n        2 => ['pipe', 'w'], // strerr\n    ];\n\n    $other_opts = ['suppress_errors' => false];\n\n    $cmd = (substr(PHP_OS, 0, 3) == 'WIN') ? 'dir' : 'ls';\n    $p = proc_open($cmd, $descs, $pipes, '.', null, $other_opts);\n\n    if (is_resource($p)) {\n        $data = '';\n\n        while (1) {\n            $w = $e = null;\n            $n = stream_select($pipes, $w, $e, 300);\n\n            if ($n === false) {\n                echo \"no streams \\n\";\n                break;\n            } else {\n                if ($n === 0) {\n                    echo \"process timed out\\n\";\n                    proc_terminate($p, 9);\n                    break;\n                } else {\n                    if ($n > 0) {\n                        $line = fread($pipes[1], 8192);\n                        if (strlen($line) == 0) {\n                            /* EOF */\n                            break;\n                        }\n                        $data .= $line;\n                    }\n                }\n            }\n        }\n        var_dump(strlen($data));\n\n        $ret = proc_close($p);\n        var_dump($ret);\n    } else {\n        echo \"no process\\n\";\n    }\n});\nSwoole\\Event::wait();\n?>\n--EXPECTF--\nint(%d)\nint(0)\n"
  },
  {
    "path": "tests/swoole_runtime/stream_select/bug69521.phpt",
    "content": "--TEST--\nswoole_runtime/stream_select: Bug #69521 Segfault in gc_collect_cycles()\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    $serverUri = \"tcp://127.0.0.1:64321\";\n    $sock = stream_socket_server($serverUri, $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);\n\n    $fp = stream_socket_client($serverUri, $errNumber, $errString, 5, STREAM_CLIENT_CONNECT);\n\n    $written = 0;\n\n    $data = \"test\";\n    $written += fwrite($fp, substr($data, $written, 100));\n\n    $link = stream_socket_accept($sock);\n    fread($link, 1000);\n    fwrite($link, \"Sending bug 69521\\n\");\n    fclose($link);\n\n    while (!feof($fp)) {\n        $read = $write = [$fp];\n\n        if ($written === strlen($data)) {\n            $write = [];\n        }\n\n        stream_select($read, $write, $except, 0, 500000);\n\n        if (!empty($read)) {\n            echo fread($fp, 4);\n        }\n    }\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\nSending bug 69521\n"
  },
  {
    "path": "tests/swoole_runtime/stream_select/bug72075.phpt",
    "content": "--TEST--\nswoole_runtime/stream_select: Bug #72075 (Referencing socket resources breaks stream_select)\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    $r = [stream_socket_server(\"tcp://127.0.0.1:0\", $errno, $errStr)];\n    $w = null;\n    $e = null;\n\n    // Without this line, all is well:\n    $dummy =& $r[0];\n\n    print stream_select($r, $w, $e, 0.5) . PHP_EOL;\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n0\n"
  },
  {
    "path": "tests/swoole_runtime/stream_select/conflict.phpt",
    "content": "--TEST--\nswoole_runtime/stream_select: conflict\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_not_linux();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Runtime;\nuse Swoole\\Event;\n\nRuntime::enableCoroutine();\n\n// Co::set(['print_backtrace_on_error' => true]);\n\n$n = new Atomic(1);\n\ngo(function () use ($n) {\n    $server = stream_socket_server('tcp://0.0.0.0:8000', $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);\n    while ($n->get()) {\n        $conn = @stream_socket_accept($server, 0.1);\n        if ($conn) {\n            go(function () use ($conn) {\n                Assert::eq(fread($conn, 8192), '');\n                fclose($conn);\n                echo \"Server done\\n\";\n            });\n            break;\n        }\n    }\n});\n\ngo(function () use ($n) {\n    $fp1 = stream_socket_client('tcp://127.0.0.1:8000', $errno, $errstr, 30);\n    go(function () use ($fp1) {\n        Co::sleep(0.01);\n        $read = null;\n        $write = [$fp1];\n        $except = null;\n        $rs = stream_select($read, $write, $except, 5);\n        Assert::eq($rs, 0);\n        stream_socket_shutdown($fp1, STREAM_SHUT_RDWR);\n        echo \"shutdown\\n\";\n    });\n    $rs = fread($fp1, 8192);\n    Assert::eq($rs, '');\n    echo \"Client done\\n\";\n});\n\nEvent::wait();\n?>\n--EXPECTF--\n[%s]\tWARNING\tReactorEpoll::add(): [Reactor#0] epoll_ctl(epfd=%d, EPOLL_CTL_ADD, fd=%d, fd_type=%d, events=1024) failed, Error: File exists[17]\nshutdown\nServer done\nClient done\n"
  },
  {
    "path": "tests/swoole_runtime/stream_select/never_timeout.phpt",
    "content": "--TEST--\nswoole_runtime/stream_select: never timeout\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    $server1 = SwooleTest\\CoServer::createHttpHelloWorld();\n    $server1->run();\n    $server2 = SwooleTest\\CoServer::createHttpHelloWorld();\n    $server2->run();\n    $fp1 = stream_socket_client(\"tcp://127.0.0.1:{$server1->getPort()}\", $errno, $errstr, 1);\n    $fp2 = stream_socket_client(\"tcp://127.0.0.1:{$server2->getPort()}\", $errno, $errstr, 1);\n    if (Assert::resource($fp1)) {\n        fwrite($fp1, \"GET / HTTP/1.0\\r\\nHost: 127.0.0.1\\r\\nUser-Agent: curl/7.58.0\\r\\nAccept: */*\\r\\n\\r\\n\");\n        $r_array = [$fp1, $fp2];\n        $w_array = $e_array = null;\n        $n = stream_select($r_array, $w_array, $e_array, null);\n        Assert::assert($n == 1);\n        Assert::assert(count($r_array) == 1);\n        Assert::assert($r_array[0] == $fp1);\n        $response = '';\n        while (!feof($fp1)) {\n            $response .= fgets($fp1);\n        }\n        Assert::contains($response, '200 OK');\n        fclose($fp1);\n    }\n    $server1->shutdown();\n    $server2->shutdown();\n});\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_runtime/stream_select/preserve_keys.phpt",
    "content": "--TEST--\nswoole_runtime/stream_select: Bug #53427 + emulate_read (stream_select does not preserve keys)\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    $read[1] = fopen(__FILE__, 'r');\n    $read['myindex'] = fopen(__FILE__, 'r');\n    $write = null;\n    $except = null;\n\n    Assert::count($read, 2);\n    $n = stream_select($read, $write, $except, 0);\n    Assert::same($n, 2);\n    Assert::count($read, 2);\n    Assert::isEmpty($write);\n    Assert::isEmpty($except);\n    fread(reset($read), 1);\n    $n = stream_select($read, $write, $except, 0); // // emulate_read\n    Assert::same($n, 1);\n    Assert::count($read, 1);\n    Assert::isEmpty($write);\n    Assert::isEmpty($except);\n});\nSwoole\\Event::wait();\necho \"DONE\\n\"\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_runtime/stream_select/return_val.phpt",
    "content": "--TEST--\nswoole_runtime/stream_select: Bug the result of stream_select() is not equal to $read + $write + $error\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nif (!getenv('TEST_PHP_EXECUTABLE')) {\n    exit('skip TEST_PHP_EXECUTABLE not defined');\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\nCo\\run(function () {\n    $php = realpath(getenv('TEST_PHP_EXECUTABLE'));\n    $pipes = [];\n    $proc = proc_open(\n        \"$php\"\n        , [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']]\n        , $pipes, __DIR__, [], []\n    );\n    var_dump($proc);\n    if (!$proc) {\n        exit(1);\n    }\n    $r = $pipes;\n    $w = [];\n    $e = [];\n    $ret = stream_select($r, $w, $e, 1);\n    var_dump($ret, (count($r) + count($w) + count($e)));\n\n    foreach ($pipes as $pipe) {\n        fclose($pipe);\n    }\n    proc_terminate($proc);\n    if (defined('SIGKILL')) {\n        proc_terminate($proc, SIGKILL);\n    } else {\n        proc_terminate($proc);\n    }\n    proc_close($proc);\n});\n?>\n--EXPECTF--\nresource(%d) of type (process/coroutine)\nint(0)\nint(0)\n"
  },
  {
    "path": "tests/swoole_runtime/stream_select/rw_events.phpt",
    "content": "--TEST--\nswoole_runtime/stream_select: rw events\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Runtime;\n\nRuntime::enableCoroutine();\n\n$n = new Atomic(1);\n\ngo(function () use ($n) {\n    $server = stream_socket_server('tcp://0.0.0.0:8000', $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);\n    while ($n->get()) {\n        $conn = @stream_socket_accept($server, 0.1);\n        if ($conn) {\n            go(function () use ($conn) {\n                fwrite($conn, 'The local time is ' . date('n/j/Y g:i a'));\n                echo fread($conn, 8192);\n                fclose($conn);\n            });\n        }\n    }\n});\n\ngo(function () use ($n) {\n    $fp1 = stream_socket_client('tcp://127.0.0.1:8000', $errno, $errstr, 30);\n    $fp2 = stream_socket_client('tcp://127.0.0.1:8000', $errno, $errstr, 30);\n    $r_array = [$fp1, $fp2];\n    $w_array = [$fp1, $fp2];\n    $e_array = null;\n    usleep(1000);\n    $retval = stream_select($r_array, $w_array, $e_array, 10);\n    Assert::same($retval, 2);\n    Assert::same(count($r_array), 2);\n    Assert::same(count($w_array), 2);\n    $n->set(0);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/stream_select/stream_set_blocking.phpt",
    "content": "--TEST--\nswoole_runtime/stream_select: swoole_runtime/stream_set_blocking\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nif (!getenv('TEST_PHP_EXECUTABLE')) {\n    exit('skip TEST_PHP_EXECUTABLE not defined');\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nfile_put_contents(__DIR__ . '/child.php', '<?php echo \\'test\\', PHP_EOL;fread(STDIN, 1);');\nSwoole\\Runtime::enableCoroutine();\nCo\\run(function () {\n\n    $descriptorspec = [\n        ['pipe', 'r'],\n        ['pipe', 'w'], // stdout\n    ];\n    $p = proc_open('php ' . __DIR__ . '/child.php', $descriptorspec, $pipes);\n    Assert::resource($p);\n    foreach ($pipes as $pipe) {\n        Assert::true(stream_set_blocking($pipe, false));\n    }\n    while (true) {\n        $r = [$pipes[1]];\n        $w = [];\n        $e = [];\n        if (stream_select($r, $w, $e, 0) && $r) {\n            foreach ($r as $pipe) {\n                for ($i = 0; $i < 2; ++$i) {\n                    $time = microtime(true);\n                    // The second execution will not be blocked\n                    $data = @fread($pipe, 4096);\n                    Assert::lessThan(microtime(true) - $time, 1);\n                    var_dump($i . ':' . $data);\n                }\n            }\n            break;\n        }\n        sleep(1);\n    }\n    proc_close($p);\n});\nunlink(__DIR__ . '/child.php');\n?>\n--EXPECTF--\nstring(7) \"0:test\n\"\nstring(2) \"1:\"\n"
  },
  {
    "path": "tests/swoole_runtime/stream_select/timeout.phpt",
    "content": "--TEST--\nswoole_runtime/stream_select: timeout\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\ngo(function () {\n    Swoole\\Runtime::enableCoroutine();\n    $fp1 = stream_socket_client(\"tcp://www.baidu.com:80\", $errno, $errstr, 30);\n    if (!$fp1) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        $r_array = [$fp1];\n        $w_array = $e_array = null;\n        $s = microtime(true);\n        $timeout = ms_random(0.1, 0.5);\n        $n = stream_select($r_array, $w_array, $e_array, 0, $timeout * 1000000);\n        Assert::same($n, 0);\n        time_approximate($timeout, microtime(true) - $s);\n        echo \"SUCCESS\\n\";\n    }\n});\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_runtime/stream_socket_pair.phpt",
    "content": "--TEST--\nswoole_runtime: stream_socket_pair\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\n$pipe = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);\n\nCo::create(function()use(&$pipe){\n    foreach(range(0, 9) as $n) {\n        printf(\"Write byte: %d\\n\", fwrite($pipe[0], 'hello world '.$n));\n        Co::sleep(.01);\n    }\n    fclose($pipe[0]);\n});\n\nCo::create(function()use(&$pipe){\n    while ($buffer = fread($pipe[1], 1024)) {\n        printf(\"Read byte: %d, beffer: %s\\n\", strlen($buffer), $buffer);\n    }\n    fclose($pipe[1]);\n});\n\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nWrite byte: 13\nRead byte: 13, beffer: hello world 0\nWrite byte: 13\nRead byte: 13, beffer: hello world 1\nWrite byte: 13\nRead byte: 13, beffer: hello world 2\nWrite byte: 13\nRead byte: 13, beffer: hello world 3\nWrite byte: 13\nRead byte: 13, beffer: hello world 4\nWrite byte: 13\nRead byte: 13, beffer: hello world 5\nWrite byte: 13\nRead byte: 13, beffer: hello world 6\nWrite byte: 13\nRead byte: 13, beffer: hello world 7\nWrite byte: 13\nRead byte: 13, beffer: hello world 8\nWrite byte: 13\nRead byte: 13, beffer: hello world 9\n"
  },
  {
    "path": "tests/swoole_runtime/tcp-c10k.phpt",
    "content": "--TEST--\nswoole_runtime: server and client concurrency\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\n// php_stream tcp server & client with 12.8k requests in single process\n$port = get_one_free_port();\n\ngo(function () use ($port) {\n    $ctx = stream_context_create(['socket' => ['so_reuseaddr' => true, 'backlog' => MAX_CONCURRENCY_MID]]);\n    $socket = stream_socket_server(\n        \"tcp://0.0.0.0:{$port}\",\n        $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctx\n    );\n    if (!$socket) {\n        echo \"$errstr ($errno)\\n\";\n    } else {\n        $i = 0;\n        while ($conn = stream_socket_accept($socket, 1)) {\n            for ($n = MAX_REQUESTS; $n--;) {\n                $data = fread($conn, tcp_length(fread($conn, tcp_type_length())));\n                Assert::same($data, \"Hello Swoole Server #{$n}!\");\n                fwrite($conn, tcp_pack(\"Hello Swoole Client #{$n}!\"));\n            }\n            if (++$i === MAX_CONCURRENCY_MID) {\n                fclose($socket);\n                echo \"DONE\\n\";\n                break;\n            }\n        }\n    }\n});\nfor ($c = MAX_CONCURRENCY_MID; $c--;) {\n    go(function () use ($port) {\n        $fp = stream_socket_client(\"tcp://127.0.0.1:{$port}\", $errno, $errstr, 1);\n        if (!$fp) {\n            echo \"$errstr ($errno)\\n\";\n        } else {\n            for ($n = MAX_REQUESTS; $n--;) {\n                fwrite($fp, tcp_pack(\"Hello Swoole Server #{$n}!\"));\n                $data = fread($fp, tcp_length(fread($fp, tcp_type_length())));\n                Assert::same($data, \"Hello Swoole Client #{$n}!\");\n            }\n            fclose($fp);\n        }\n    });\n}\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_runtime/tcp.phpt",
    "content": "--TEST--\nswoole_runtime: tcp\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\ngo(function () {\n    $fp = stream_socket_client(\"tcp://www.baidu.com:80\", $errno, $errstr, 30);\n    if (!$fp) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        $http = \"GET / HTTP/1.0\\r\\nAccept: */*User-Agent: Lowell-Agent\\r\\nHost: www.baidu.com\\r\\nConnection: Close\\r\\n\\r\\n\";\n        fwrite($fp, $http);\n        $content = '';\n        while (!feof($fp)) {\n            $content .= fread($fp, 1024);\n        }\n        fclose($fp);\n        Assert::assert(strpos($content,'map.baidu.com') !== false);\n    }\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_runtime/udg.phpt",
    "content": "--TEST--\nswoole_runtime: udg\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\nconst N = 5;\n\ngo(function () {\n    $socket = new Swoole\\Coroutine\\Socket(AF_UNIX, SOCK_DGRAM, 0);\n    $socket->bind(__DIR__ . '/test.sock');\n\n    for ($i = 0; $i < N; $i++)\n    {\n        $peer = null;\n        $data = $socket->recvfrom($peer);\n        echo \"[Server] recv : $data\\n\";\n    }\n});\n\ngo(function () {\n    $fp = stream_socket_client(\"udg://\".__DIR__.\"/test.sock\", $errno, $errstr, 30);\n    if (!$fp) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        for ($i = 0; $i < N; $i++) {\n            fwrite($fp, \"hello-{$i}\");\n        }\n        fclose($fp);\n    }\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n[Server] recv : hello-0\n[Server] recv : hello-1\n[Server] recv : hello-2\n[Server] recv : hello-3\n[Server] recv : hello-4\n"
  },
  {
    "path": "tests/swoole_runtime/udp-c10k.phpt",
    "content": "--TEST--\nswoole_runtime: server and client concurrency\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\n// udp server & client with 12.8k requests in single process\n$port = get_one_free_port();\n\ngo(function () use ($port) {\n    $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_DGRAM, 0);\n    $socket->bind('127.0.0.1', $port);\n    $client_map = [];\n    for ($c = MAX_CONCURRENCY_MID; $c--;) {\n        for ($n = 0; $n < MAX_REQUESTS; $n++) {\n            $recv = $socket->recvfrom($peer);\n            $client_uid = \"{$peer['address']}:{$peer['port']}\";\n            $id = $client_map[$client_uid] = ($client_map[$client_uid] ?? -1) + 1;\n            Assert::same($recv, \"Client: Hello #{$id}!\");\n            $socket->sendto($peer['address'], $peer['port'], \"Server: Hello #{$id}!\");\n        }\n    }\n    $socket->close();\n    echo \"DONE\\n\";\n});\nfor ($c = MAX_CONCURRENCY_MID; $c--;) {\n    go(function () use ($port) {\n        $fp = stream_socket_client(\"udp://127.0.0.1:{$port}\", $errno, $errstr, 1);\n        if (!$fp) {\n            echo \"$errstr ($errno)\\n\";\n        } else {\n            for ($n = 0; $n < MAX_REQUESTS; $n++) {\n                fwrite($fp, \"Client: Hello #{$n}!\");\n                $recv = fread($fp, 1024);\n                list($_address, $_port) = explode(':', (stream_socket_get_name($fp, true)));\n                Assert::assert($_address === '127.0.0.1' && (int)$_port === $port);\n                Assert::same($recv, \"Server: Hello #{$n}!\");\n            }\n            fclose($fp);\n        }\n    });\n}\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_runtime/udp.phpt",
    "content": "--TEST--\nswoole_runtime: udp client\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\nconst N = 5;\n\ngo(function () {\n    $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_DGRAM, 0);\n    $socket->bind('127.0.0.1', 9601);\n    for ($i = 0; $i < N; $i++)\n    {\n        $peer = null;\n        $data = $socket->recvfrom($peer);\n        echo \"[Server] recvfrom[{$peer['address']}:{$peer['port']}] : $data\\n\";\n        $socket->sendto($peer['address'], $peer['port'], \"Swoole: $data\");\n    }\n});\n\ngo(function () {\n    $fp = stream_socket_client(\"udp://127.0.0.1:9601\", $errno, $errstr, 30);\n    if (!$fp) {\n        echo \"$errstr ($errno)<br />\\n\";\n    } else {\n        for ($i = 0; $i < N; $i++) {\n            fwrite($fp, \"hello-{$i}\");\n            $data = fread($fp, 1024);\n            list($address, $port) = explode(':', (stream_socket_get_name($fp, true)));\n            echo \"[Client] recvfrom[{$address}:{$port}] : $data\\n\";\n        }\n        fclose($fp);\n    }\n});\nSwoole\\Event::wait();\n?>\n--EXPECTF--\n[Server] recvfrom[127.0.0.1:%d] : hello-0\n[Client] recvfrom[127.0.0.1:9601] : Swoole: hello-0\n[Server] recvfrom[127.0.0.1:%d] : hello-1\n[Client] recvfrom[127.0.0.1:9601] : Swoole: hello-1\n[Server] recvfrom[127.0.0.1:%d] : hello-2\n[Client] recvfrom[127.0.0.1:9601] : Swoole: hello-2\n[Server] recvfrom[127.0.0.1:%d] : hello-3\n[Client] recvfrom[127.0.0.1:9601] : Swoole: hello-3\n[Server] recvfrom[127.0.0.1:%d] : hello-4\n[Client] recvfrom[127.0.0.1:9601] : Swoole: hello-4\n"
  },
  {
    "path": "tests/swoole_runtime/unix.phpt",
    "content": "--TEST--\nswoole_runtime: unix stream\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Socket;\nuse Swoole\\Event;\nuse Swoole\\Runtime;\n\nRuntime::enableCoroutine();\n\nconst N = 5;\nconst SOCK_FILE = '/tmp/test.sock';\n\nif (is_file(SOCK_FILE)) {\n    unlink(SOCK_FILE);\n}\n\ngo(function () {\n    $socket = new Socket(AF_UNIX, SOCK_STREAM, 0);\n    Assert::true($socket->bind(SOCK_FILE), 'bind error: ' . $socket->errCode);\n    Assert::true($socket->listen(), 'listen error: ' . $socket->errCode);\n\n    $client = $socket->accept();\n    Assert::notNull($client);\n\n    for ($i = 0; $i < N; $i++) {\n        $data = $client->recv();\n        $client->send(\"Swoole: {$data}\");\n    }\n\n    usleep(1000);\n});\n\ngo(function () {\n    $fp = stream_socket_client('unix://' . SOCK_FILE, $errno, $errstr, 30);\n    if (!$fp) {\n        echo \"{$errstr} ({$errno})<br />\\n\";\n    } else {\n        for ($i = 0; $i < N; $i++) {\n            fwrite($fp, \"hello-{$i}\");\n            $data = fread($fp, 1024);\n            [$address] = explode(':', stream_socket_get_name($fp, true));\n            $address = basename($address);\n            echo \"[Client] recvfrom[{$address}] : {$data}\\n\";\n        }\n        fclose($fp);\n    }\n});\nEvent::wait();\n?>\n--EXPECT--\n[Client] recvfrom[test.sock] : Swoole: hello-0\n[Client] recvfrom[test.sock] : Swoole: hello-1\n[Client] recvfrom[test.sock] : Swoole: hello-2\n[Client] recvfrom[test.sock] : Swoole: hello-3\n[Client] recvfrom[test.sock] : Swoole: hello-4\n"
  },
  {
    "path": "tests/swoole_runtime/unsafe/pcntl_fork.phpt",
    "content": "--TEST--\nswoole_runtime/unsafe: pcntl_fork\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\n\nrun(function ()  {\n    pcntl_fork();\n});\n?>\n--EXPECTF--\nWarning: pcntl_fork() has been disabled for security reasons in %s on line %d\n"
  },
  {
    "path": "tests/swoole_server/accept_zero.phpt",
    "content": "--TEST--\nswoole_server: accept zero fd\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Constant;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            exit(\"connect failed\\n\");\n        }\n        $client->send(\"stats\");\n        $data = $client->recv();\n        $json = json_decode($data, true);\n        Assert::eq($json[1], 1);\n        echo \"DONE\\n\";\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => TEST_LOG_FILE,\n    ]);\n    $serv->on(\"Start\", function ($serv) use ($pm) {\n        fclose(STDIN);\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) {\n        $serv->send($fd, json_encode(iterator_to_array($serv->connections)).\"\\r\\n\");\n    });\n    $serv->on(Constant::EVENT_CLOSE, function (Server $serv, $fd, $reactor_id) {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/addListener.phpt",
    "content": "--TEST--\nswoole_server: addlistener\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/opcode_server.php\";\n\nuse Swoole\\Server;\n\n$port1 = get_one_free_port();\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) {\n    Co\\Run(function () {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n        $r = $cli->connect(UDP_SERVER_HOST, UDP_SERVER_PORT, 1);\n        Assert::assert($r);\n        $cli->send(\"test\");\n        $i = $cli->getpeername();\n        Assert::assert($i !== false);\n        $cli->send('shutdown');\n        $cli->close();\n        echo \"SUCCESS\\n\";\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server(UDP_SERVER_HOST, UDP_SERVER_PORT, SWOOLE_BASE, SWOOLE_SOCK_UDP);\n    $serv->set([\"worker_num\" => 1, 'log_file' => '/dev/null']);\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"Packet\", function (Server $serv, $data, $clientInfo) {\n        if (trim($data) == 'shutdown') {\n            $serv->shutdown();\n            return;\n        }\n        $serv->sendto($clientInfo['address'], $clientInfo['port'], $data);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/addProcess.phpt",
    "content": "--TEST--\nswoole_server: addProcess\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\Run(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $r = $cli->connect(TCP_SERVER_HOST, $pm->getFreePort(), 1);\n        Assert::assert($r);\n        $cli->send(\"test\");\n        $data = $cli->recv();\n        Assert::same($data, 'test');\n        $cli->send('shutdown');\n        $cli->close();\n        echo \"SUCCESS\\n\";\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_PROCESS);\n    $process = new \\Swoole\\Process(function ($process) use ($serv) {\n        while (1) {\n            $msg = json_decode($process->read(), true);\n            $serv->send($msg['fd'], $msg['data']);\n        }\n    });\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $rid, $data) use ($process) {\n        if (trim($data) == 'shutdown') {\n            $serv->shutdown();\n            return;\n        } else {\n            $process->write(json_encode(['fd' => $fd, 'data' => $data]));\n        }\n    });\n    $serv->addProcess($process);\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/addProcess_base.phpt",
    "content": "--TEST--\nswoole_server: addProcess with SWOOLE_BASE\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\Run(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $r = $cli->connect(TCP_SERVER_HOST, $pm->getFreePort(), 1);\n        Assert::assert($r);\n        $cli->send(\"test\");\n        $data = $cli->recv();\n        Assert::same($data, 'test');\n        $cli->send('shutdown');\n        $cli->close();\n        echo \"SUCCESS\\n\";\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_BASE);\n    $process = new \\Swoole\\Process(function ($process) use ($serv) {\n        while (1) {\n            $msg = json_decode($process->read(), true);\n            $serv->send($msg['fd'], $msg['data']);\n        }\n    });\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $rid, $data) use ($process) {\n        if (trim($data) == 'shutdown') {\n            $serv->shutdown();\n            return;\n        } else {\n            $process->write(json_encode(['fd' => $fd, 'data' => $data]));\n        }\n    });\n    $serv->addProcess($process);\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/addProcess_with_error.phpt",
    "content": "--TEST--\nswoole_server: addProcess with fatal error\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$atomic = new Swoole\\Atomic;\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n\n    class Process4 extends Swoole\\Process\n    {\n        public function __construct()\n        {\n            parent::__construct([$this, 'run']);\n        }\n\n        public function run()\n        {\n            go(function () {\n                global $atomic;\n                if ($atomic->add() > 5) {\n                    global $pm;\n                    $pm->wakeup();\n                    Co::yield();\n                    return;\n                }\n                echo \"sleep start then \";\n                Co::sleep(0.01);\n                echo \"sleep end\\n\";\n                trigger_error('ERROR', E_USER_ERROR);\n            });\n        }\n    }\n\n    $server = new Swoole\\Server('127.0.0.1', get_one_free_port(), SWOOLE_PROCESS, SWOOLE_SOCK_UDP);\n    $server->set(['log_file' => '/dev/null']);\n    $server->on('packet', function () { });\n    $server->addProcess(new Process4);\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_error.php on line %d\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_error.php on line %d\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_error.php on line %d\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_error.php on line %d\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_error.php on line %d\nDONE\n--EXPECTF_85--\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_error.php on line %d\nStack trace:\n#0 %s(%d): trigger_error('ERROR', 256)\n#1 [internal function]: Process4->{closure:Process4::run():%d}()\n#2 {main}\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_error.php on line %d\nStack trace:\n#0 %s(%d): trigger_error('ERROR', 256)\n#1 [internal function]: Process4->{closure:Process4::run():%d}()\n#2 {main}\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_error.php on line %d\nStack trace:\n#0 %s(%d): trigger_error('ERROR', 256)\n#1 [internal function]: Process4->{closure:Process4::run():%d}()\n#2 {main}\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_error.php on line %d\nStack trace:\n#0 %s(%d): trigger_error('ERROR', 256)\n#1 [internal function]: Process4->{closure:Process4::run():%d}()\n#2 {main}\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_error.php on line %d\nStack trace:\n#0 %s(%d): trigger_error('ERROR', 256)\n#1 [internal function]: Process4->{closure:Process4::run():%d}()\n#2 {main}\nDONE\n"
  },
  {
    "path": "tests/swoole_server/addProcess_with_event_wait.phpt",
    "content": "--TEST--\nswoole_server: addProcess with event wait\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->setWaitTimeout(-1);\n$pm->parentFunc = function () use ($pm) {\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n\n    class Process5 extends Swoole\\Process\n    {\n        public function __construct()\n        {\n            parent::__construct([$this, 'run']);\n        }\n\n        public function run()\n        {\n            Swoole\\Timer::tick(100, function (int $id) use (&$i) {\n                global $pm;\n                if (++$i === 10) {\n                    Swoole\\Timer::clear($id);\n                    $pm->wakeup();\n                }\n                echo \"Tick {$i}\\n\";\n            });\n        }\n    }\n\n    $server = new Swoole\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_UDP);\n    $server->set(['log_file' => '/dev/null']);\n    $server->on('packet', function () { });\n    $server->addProcess(new Process5);\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nTick 1\nTick 2\nTick 3\nTick 4\nTick 5\nTick 6\nTick 7\nTick 8\nTick 9\nTick 10\n"
  },
  {
    "path": "tests/swoole_server/addProcess_with_tick.phpt",
    "content": "--TEST--\nswoole_server: addProcess with swoole_timer_tick fatal error\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$atomic = new Swoole\\Atomic;\n$pm = new SwooleTest\\ProcessManager;\n\nclass Process3 extends Swoole\\Process\n{\n    public function __construct()\n    {\n        parent::__construct([$this, 'run']);\n    }\n\n    public function run()\n    {\n        Swoole\\Timer::tick(100, function () {\n            global $atomic;\n            if ($atomic->add() > 5) {\n                global $pm;\n                $pm->wakeup();\n                Co::yield();\n                return;\n            }\n            echo \"sleep start then \";\n            Co::sleep(0.01);\n            echo \"sleep end\\n\";\n            trigger_error('ERROR', E_USER_ERROR);\n        });\n    }\n}\n\n$pm->parentFunc = function () use ($pm) {\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', get_one_free_port(), SWOOLE_PROCESS, SWOOLE_SOCK_UDP);\n    $server->set([\n        'log_file' => '/dev/null',\n//        'worker_num' => 1,\n    ]);\n    $server->on('packet', function () {\n    });\n    $server->addProcess(new Process3);\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_tick.php on line %d\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_tick.php on line %d\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_tick.php on line %d\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_tick.php on line %d\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_tick.php on line %d\nDONE\n--EXPECTF_85--\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_tick.php on line %d\nStack trace:\n#0 %s(%d): trigger_error('ERROR', 256)\n#1 [internal function]: Process3->{closure:Process3::run():%d}(%d)\n#2 {main}\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_tick.php on line %d\nStack trace:\n#0 %s(%d): trigger_error('ERROR', 256)\n#1 [internal function]: Process3->{closure:Process3::run():%d}(%d)\n#2 {main}\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_tick.php on line %d\nStack trace:\n#0 %s(%d): trigger_error('ERROR', 256)\n#1 [internal function]: Process3->{closure:Process3::run():%d}(%d)\n#2 {main}\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_tick.php on line %d\nStack trace:\n#0 %s(%d): trigger_error('ERROR', 256)\n#1 [internal function]: Process3->{closure:Process3::run():%d}(%d)\n#2 {main}\nsleep start then sleep end\n\nFatal error: ERROR in %s/tests/swoole_server/addProcess_with_tick.php on line %d\nStack trace:\n#0 %s(%d): trigger_error('ERROR', 256)\n#1 [internal function]: Process3->{closure:Process3::run():%d}(%d)\n#2 {main}\nDONE\n"
  },
  {
    "path": "tests/swoole_server/base/reload_1.phpt",
    "content": "--TEST--\nswoole_server/base: reload all workers\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$worker_num = 2;\n$task_worker_num = 3;\n\n$counter = [\n    'worker' => new Swoole\\Atomic(),\n    'task_worker' => new Swoole\\Atomic()\n];\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm, $counter, $worker_num, $task_worker_num) {\n    while (!file_exists(TEST_PID_FILE)) {\n        usleep(100 * 1000);\n    }\n    $pid = file_get_contents(TEST_PID_FILE);\n    $random = mt_rand(1, 5);\n    usleep(100 * 1000);\n    for ($n = $random; $n--;) {\n        Swoole\\Process::kill($pid, SIGUSR1);\n        usleep(200 * 1000);\n    }\n\n    /**@var $counter Swoole\\Atomic[] */\n    $total = $counter['worker']->get() - $worker_num;\n    $expect = $random * $worker_num;\n    Assert::same($total, $expect, \"[worker reload {$total} but expect {$expect}]\");\n\n    $total = $counter['task_worker']->get() - $task_worker_num;\n    $expect = $random * $task_worker_num;\n    Assert::same($total, $expect, \"[task worker reload {$total} but expect {$random}]\");\n\n    $log = file_get_contents(TEST_LOG_FILE);\n    $log = trim(preg_replace('/.+?\\s+?INFO\\s+?.+/', '', $log));\n    if (!Assert::assert(empty($log))) {\n        var_dump($log);\n    }\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm, $counter, $worker_num, $task_worker_num) {\n    @unlink(TEST_LOG_FILE);\n    @unlink(TEST_PID_FILE);\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'log_file' => TEST_LOG_FILE,\n        'pid_file' => TEST_PID_FILE,\n        'worker_num' => $worker_num,\n        'task_worker_num' => $task_worker_num,\n    ]);\n    $server->on('ManagerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('WorkerStart', function (Swoole\\Server $server, int $worker_id) use ($pm) {\n        /**@var $counter Swoole\\Atomic[] */\n        global $counter;\n        $atomic = $server->taskworker ? $counter['task_worker'] : $counter['worker'];\n        $atomic->add(1);\n    });\n    $server->on('Receive', function (Swoole\\Server $server, $fd, $reactor_id, $data) {\n    });\n    $server->on('Task', function () {\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/base/reload_2.phpt",
    "content": "--TEST--\nswoole_server/base: reload task workers\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$worker_num = 2;\n$task_worker_num = 5;\n\n$counter = [\n    'worker' => new Swoole\\Atomic(),\n    'task_worker' => new Swoole\\Atomic()\n];\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm, $counter, $worker_num, $task_worker_num) {\n    while (!file_exists(TEST_PID_FILE)) {\n        usleep(100 * 1000);\n    }\n    $pid = file_get_contents(TEST_PID_FILE);\n    $random = mt_rand(1, 5);\n    usleep(100 * 1000);\n    for ($n = $random; $n--;) {\n        Swoole\\Process::kill($pid, SIGUSR2);\n        usleep(200 * 1000);\n    }\n\n    /**@var $counter Swoole\\Atomic[] */\n    $total = $counter['worker']->get() - $worker_num;\n    Assert::same($total, 0, \"[worker reload {$total} but expect 0]\");\n\n    $total = $counter['task_worker']->get() - $task_worker_num;\n    $expect = $random * $task_worker_num;\n    Assert::same($total, $expect, \"[task worker reload {$total} but expect {$expect}]\");\n\n    $log = file_get_contents(TEST_LOG_FILE);\n    $log = trim(preg_replace('/.+?\\s+?INFO\\s+?.+/', '', $log));\n    if (!Assert::assert(empty($log))) {\n        var_dump($log);\n    }\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm, $counter, $worker_num, $task_worker_num) {\n    @unlink(TEST_LOG_FILE);\n    @unlink(TEST_PID_FILE);\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'log_file' => TEST_LOG_FILE,\n        'pid_file' => TEST_PID_FILE,\n        'worker_num' => $worker_num,\n        'task_worker_num' => $task_worker_num,\n    ]);\n    $server->on('ManagerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('WorkerStart', function (Swoole\\Server $server, int $worker_id) use ($pm) {\n        /**@var $counter Swoole\\Atomic[] */\n        global $counter;\n        $atomic = $server->taskworker ? $counter['task_worker'] : $counter['worker'];\n        $atomic->add(1);\n    });\n    $server->on('Receive', function (Swoole\\Server $server, $fd, $reactor_id, $data) {\n    });\n    $server->on('Task', function () {\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/base/shutdown.phpt",
    "content": "--TEST--\nswoole_server/base: shutdown\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->initRandomData(1);\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Co\\Client(SWOOLE_SOCK_TCP);\n        Assert::assert($client->connect('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($client->send($pm->getRandomData()) > 0);\n    });\n    $pm->wait(-1);\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => mt_rand(2, 4),\n        'log_file' => '/dev/null',\n    ]);\n    $server->on('start', function () use ($pm) {\n        echo \"START\\n\";\n    });\n    $server->on('managerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('receive', function (Swoole\\Server $server, int $fd, int $rid, string $data) use ($pm) {\n        Assert::same($data, $pm->getRandomData());\n        $server->shutdown();\n    });\n    $server->on('shutdown', function () use ($pm) {\n        echo \"SHUTDOWN\\n\";\n        $pm->wakeup();\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n$pm->expectExitCode(0);\n?>\n--EXPECT--\nSTART\nSHUTDOWN\n"
  },
  {
    "path": "tests/swoole_server/base/shutdown_single.phpt",
    "content": "--TEST--\nswoole_server/base: shutdown [single process]\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->initRandomData(1);\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $client = new Co\\Client(SWOOLE_SOCK_TCP);\n        Assert::assert($client->connect('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($client->send($pm->getRandomData()) > 0);\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $server->on('start', function (Server $server) use ($pm) {\n        echo \"START\\n\";\n        Assert::eq($server->getManagerPid(), 0);\n        Assert::eq($server->getMasterPid(), posix_getpid());\n        $pm->wakeup();\n    });\n    $server->on('receive', function (Server $server, int $fd, int $rid, string $data) use ($pm) {\n        Assert::same($data, $pm->getRandomData());\n        $server->shutdown();\n    });\n    $server->on('shutdown', function () {\n        echo \"SHUTDOWN\\n\";\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n$pm->expectExitCode(0);\n?>\n--EXPECT--\nSTART\nSHUTDOWN\n"
  },
  {
    "path": "tests/swoole_server/bigPipeMessage.phpt",
    "content": "--TEST--\nswoole_server: send big pipe message\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n$port = get_one_free_port();\nconst N = 800000;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($port, $pm)\n{\n    Co\\Run(function () use ($port) {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $r = $cli->connect(TCP_SERVER_HOST, $port, 1);\n        Assert::assert($r);\n        $cli->send(\"test\");\n        $data = $cli->recv();\n        echo $data;\n        $cli->close();\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $port)\n{\n    $serv = new Server(TCP_SERVER_HOST, $port, SWOOLE_PROCESS);\n    $serv->set([\n        \"worker_num\" => 2,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on(\"pipeMessage\", function ($serv, $worker_id, $data)\n    {\n        if (is_array($data) and strlen($data['data']) == N)\n        {\n            $serv->send($data['fd'], \"OK\\n\");\n        }\n    });\n    $serv->on(\"receive\", function ($serv, $fd, $rid, $data)\n    {\n        $data = str_repeat(\"A\", N);\n        $serv->sendMessage(array('data' => $data, 'fd' => $fd), 1 - $serv->worker_id);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_server/big_session_id.phpt",
    "content": "--TEST--\nswoole_server: big session id\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nconst N = 10;\n\ndefine('START_SESSION_ID', 1 << 32 - 1);\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\nglobal $count;\n$count = 0;\n$port = get_one_free_port();\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($port) {\n    global $count;\n    for ($i = 0; $i < N; $i++) {\n        go(function () use ($port, $i) {\n            $cli = new Client(SWOOLE_SOCK_TCP);\n            $r = $cli->connect(TCP_SERVER_HOST, $port, 1);\n            Assert::assert($r);\n            $data = $cli->recv();\n            Assert::same($data, (START_SESSION_ID + 1 + $i) . \"-OK\");\n            global $count;\n            $count++;\n            $cli->close();\n        });\n    }\n    Event::wait();\n    Assert::same($count, N);\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm, $port) {\n    $serv = new Server('127.0.0.1', $port, SWOOLE_BASE);\n    $serv->set(array(\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n        'start_session_id' => START_SESSION_ID,\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('connect', function (Server $serv, $fd, $rid) {\n        global $count;\n        $count++;\n        if ($count == N) {\n            Swoole\\Event::defer(function () use ($serv) {\n                foreach ($serv->connections as $fd) {\n                    $serv->send($fd, \"$fd-OK\");\n                }\n            });\n        }\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/big_udp_packet.phpt",
    "content": "--TEST--\nswoole_server: send big udp packet to server\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n//最大长度：65535 - UDP包头 8字节 + IP包头 20字节 = 65507\nconst N = 65507;\n$port = get_one_free_port();\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($port) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_UDP, SWOOLE_SOCK_SYNC);\n    if (!$client->connect('127.0.0.1', $port)) {\n        exit(\"connect failed\\n\");\n    }\n    $client->send(str_repeat('A', N));\n    $data = $client->recv();\n    Assert::same(strlen($data), N);\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm, $port) {\n    $serv = new Swoole\\Server('127.0.0.1', $port, SWOOLE_BASE, SWOOLE_SOCK_UDP);\n    $serv->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('packet', function ($serv, $data, $client) {\n        $serv->sendto($client['address'], $client['port'], str_repeat('B', strlen($data)));\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/bind.phpt",
    "content": "--TEST--\nswoole_server: bind\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/opcode_server.php\";\n$port = get_one_free_port();\n\nstart_server($simple_tcp_server, TCP_SERVER_HOST, $port);\n$timer = suicide(2000);\nusleep(500 * 1000);\n\nmakeCoTcpClient(TCP_SERVER_HOST, $port, function (Client $cli) use ($port) {\n    $r = $cli->send(opcode_encode(\"bind\", [2, 42]));\n    Assert::assert($r !== false);\n}, function (Client $cli, $recv) use ($timer) {\n    list($op, $binded) = opcode_decode($recv);\n    Assert::assert($binded);\n    $cli->close();\n    Timer::clear($timer);\n    echo \"SUCCESS\\n\";\n});\n\nEvent::wait();\n\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/bug_11000_01.phpt",
    "content": "--TEST--\nswoole_server: bug_11000_01\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process;\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->childFunc = function () {\n    $port = get_one_free_port();\n    $serv = new Server(TCP_SERVER_HOST, $port, SWOOLE_PROCESS);\n    $process = new Process(function ($process) use ($serv) {\n        usleep(10000);\n        $stats = $serv->stats();\n        Assert::isArray($stats);\n        Assert::keyExists($stats, 'connection_num');\n        Assert::keyExists($stats, 'request_count');\n        usleep(200000);\n        $serv->shutdown();\n    });\n    $serv->set(['worker_num' => 2, 'log_file' => '/dev/null']);\n    $serv->on('receive', function () { });\n    $serv->addProcess($process);\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/bug_1864.phpt",
    "content": "--TEST--\nswoole_server: bug Github#1864\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst N = 64;\nconst M = 512;\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    function run()\n    {\n        global $pm;\n        $client = new \\Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n        static $index = 0;\n        if ($client->connect('127.0.0.1', $pm->getFreePort()) == false) {\n            echo \"connect error\\n\";\n            return;\n        }\n        $count = rand(10240, 25600);\n        for ($i = 0; $i < M; $i++) {\n            $seq = $i % 10;\n            $len = rand(10240, 25600);\n            $message = \"|||len=$len|||index=$index|||\" . str_repeat(strval($seq), $len);\n            $data = pack('N', $index++) . pack('N', strlen($message)) . $message;\n            if ($client->send($data) == false) {\n                echo \"send failed, index=$index\\n\";\n                break;\n            }\n        }\n    }\n\n    for ($i = 0; $i < N; $i++) {\n        run();\n    }\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $ss = [\n        'daemonize' => 0,\n        'dispatch_mode' => 1,\n        'worker_num' => 1,\n        'backlog' => 512,\n        'max_request' => 0,\n        'enable_coroutine' => false,\n        'open_length_check' => true,\n        'package_max_length' => 1048576, // 1MB\n        'package_length_type' => 'N',\n        'package_length_offset' => 4, // | seq || size || message |\n        'package_body_offset' => 8, // seq + size\n        'socket_buffer_size' => 1048576 * 4,\n        'output_buffer_size' => 1048576,\n        'log_file' => TEST_LOG_FILE,\n    ];\n\n    $status = new Swoole\\Atomic(0);\n\n    $tcp = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n    $tcp->set($ss);\n    $tcp->on('receive', function (Server $server, $fd, $reactorID, $data) use ($status) {\n        $size = unpack('N', substr($data, 4, 4))[1];\n        if ($size !== strlen($data) - 8) {\n            $server->shutdown();\n            $status->set(1);\n        }\n    });\n    $tcp->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $tcp->on(\"shutdown\", function (Server $serv) use ($status) {\n        if ($status->get() == 1) {\n            exit(10);\n        }\n    });\n    $tcp->start();\n};\n\n$pm->childFirst();\n$pm->run();\n$pm->expectExitCode(0);\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/bug_2308.phpt",
    "content": "--TEST--\nswoole_server: bug Github#2308\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('0.0.0.0', 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $server->set([\n        'worker_num' => MAX_PROCESS_NUM,\n        'log_file' => '/dev/null',\n        'enable_coroutine' => false,\n        'hook_flags' => SWOOLE_HOOK_ALL,\n    ]);\n    $server->on('start', function () {\n        Swoole\\Coroutine::create(function () {\n            $redis = new \\Redis();\n            $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT);\n            $ret = $redis->set('foo', 'bar');\n            Assert::assert($ret);\n            $ret = $redis->get('foo');\n            Assert::same($ret, 'bar');\n        });\n    });\n    $server->on('workerStart', function ($server) {\n        Swoole\\Timer::after(1000, function () use ($server) {\n            $server->stop();\n        });\n    });\n    $server->on('Receive', function (Server $server, int $fd, int $reactor_id, string $data) {\n    });\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_server/bug_2313.phpt",
    "content": "--TEST--\nswoole_server: bug Github#2313\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', 9501, SWOOLE_PROCESS);\n    $process = new Swoole\\Process(function () { });\n    $server->addProcess($process);\n    var_dump($process->id);\n    $pm->wakeup();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nint(%d)\n"
  },
  {
    "path": "tests/swoole_server/bug_2639.phpt",
    "content": "--TEST--\nswoole_server: bug Github#2639\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_openssl_version_lower_than('1.1.0');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL);\n        $client->set([\n            'ssl_cert_file' => SSL_FILE_DIR . '/client-cert.pem',\n            'ssl_key_file' => SSL_FILE_DIR . '/client-key.pem',\n        ]);\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            exit(\"connect failed\\n\");\n        }\n        $client->send(\"hello world\");\n        $data = $client->recv();\n        Assert::assert($data);\n        $json = json_decode($data, true);\n        Assert::isArray($json);\n        Assert::same($json['subject']['O'], 'swoole');\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $serv->set([\n        'log_file' => '/dev/null',\n        'ssl_cert_file' => SSL_FILE_DIR . '/server-cert.pem',\n        'ssl_key_file' => SSL_FILE_DIR . '/server-key.pem',\n        'ssl_verify_peer' => true,\n        'ssl_allow_self_signed' => true,\n        'task_worker_num' => 1,\n        'ssl_client_cert_file' => SSL_FILE_DIR . '/ca-cert.pem',\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function ($serv, $fd, $tid, $data) {\n        $cert_file = $serv->getClientInfo($fd)['ssl_client_cert'];\n        $serv->send($fd, json_encode(openssl_x509_parse($cert_file)));\n        $serv->task(['fd' => $fd]);\n    });\n\n    $serv->on('task', function($serv, $taskId, $wid, $data) {\n        $info = $serv->getClientInfo($data['fd']);\n        Assert::isArray($info);\n        Assert::assert(!array_key_exists('ssl_client_cert', $info));\n    });\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/bug_2736.phpt",
    "content": "--TEST--\nswoole_server: bug Github#2736\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL);\n        $client->set(['timeout' => 2, 'open_eof_check' => true, 'package_eof' => \"\\r\\n\"]);\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            exit(\"connect failed\\n\");\n        }\n        $i = 230;\n        $data = \"hello world-\" . str_repeat('A', $i) . '- BB';\n        $package_length = pack('N', strlen($data));\n        $array = str_split($package_length . $data, 100);\n        foreach ($array as $value) {\n            $client->send($value);\n        }\n        $data = $client->recv();\n        Assert::assert($data);\n        echo \"DONE\\n\";\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $serv->set([\n        'ssl_cert_file' => SSL_FILE_DIR . '/server-cert.pem',\n        'ssl_key_file' => SSL_FILE_DIR . '/server-key.pem',\n        'open_length_check' => true,\n        'package_max_length' => 2097152,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n        'log_file' => TEST_LOG_FILE,\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) {\n        $_k = unpack('Nlen', substr($data, 0, 4));\n        $len = $_k['len'];\n        $body = substr($data, 4);\n        $serv->send($fd, json_encode(['len' => $len, 'body' => $body]) . \"\\r\\n\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/bug_aio.phpt",
    "content": "--TEST--\nswoole_server: bug aio\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ndefine('__FILE_CONTENTS__', file_get_contents(__FILE__));\n$pm = new SwooleTest\\ProcessManager;\n$pm->setWaitTimeout(0);\n$pm->parentFunc = function () {\n};\n$pm->childFunc = function () use ($pm) {\n    go(function () {\n        Assert::same(Co::readFile(__FILE__), __FILE_CONTENTS__); // will be discarded\n    });\n    Swoole\\Event::wait();\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $server->on('WorkerStart', function (Swoole\\Server $server, int $worker_id) use ($pm) {\n        echo 'read file' . PHP_EOL;\n        Assert::same(Co::readFile(__FILE__), __FILE_CONTENTS__);\n        echo 'read file ok' . PHP_EOL;\n        $pm->wakeup();\n        usleep(100000);\n        $server->shutdown();\n    });\n    $server->on('Receive', function () {\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nread file\nread file ok\n"
  },
  {
    "path": "tests/swoole_server/check_callback.phpt",
    "content": "--TEST--\nswoole_server: check callback\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Constant;\nuse Swoole\\Process;\n\nfunction test_create_server($class, $callback)\n{\n    $proc = new Process(function () use ($class) {\n        $server = new $class('127.0.0.1');\n        $server->start();\n    }, true, SOCK_STREAM, false);\n    $proc->start();\n    $result = Process::wait();\n    Assert::contains($proc->read(), 'require on'.ucfirst($callback).' callback');\n    Assert::eq($result['code'], 255);\n}\n\ntest_create_server(Swoole\\Server::class, Constant::EVENT_RECEIVE);\ntest_create_server(Swoole\\Http\\Server::class, Constant::EVENT_REQUEST);\ntest_create_server(Swoole\\WebSocket\\Server::class, Constant::EVENT_MESSAGE);\n\n$proc = new Process(function ()  {\n    $server = new Swoole\\Server('127.0.0.1', 0, SWOOLE_BASE, SWOOLE_SOCK_UDP);\n    $server->start();\n}, true, SOCK_STREAM, false);\n$proc->start();\n$result = Process::wait();\nAssert::contains($proc->read(), 'require onPacket callback');\nAssert::eq($result['code'], 255);\n\n$proc = new Process(function ()  {\n    $server = new Swoole\\Server('127.0.0.1', 0, SWOOLE_BASE, SWOOLE_SOCK_UDP);\n    $server->on(Constant::EVENT_PACKET, function () {});\n    $server->addlistener('127.0.0.1', 0, SWOOLE_SOCK_TCP);\n    $server->start();\n}, true, SOCK_STREAM, false);\n$proc->start();\n$result = Process::wait();\nAssert::contains($proc->read(), 'require onReceive callback');\nAssert::eq($result['code'], 255);\n\n$proc = new Process(function ()  {\n    $server = new Swoole\\Server('127.0.0.1', 0, SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $server->on(Constant::EVENT_RECEIVE, function () {});\n    $server->addlistener('127.0.0.1', 0, SWOOLE_SOCK_UDP);\n    $server->start();\n}, true, SOCK_STREAM, false);\n$proc->start();\n$result = Process::wait();\nAssert::contains($proc->read(), 'require onPacket callback');\nAssert::eq($result['code'], 255);\n\n$proc = new Process(function ()  {\n    $server = new Swoole\\Http\\Server('127.0.0.1', 0, SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $server->on(Constant::EVENT_REQUEST, function () {});\n    $server->addlistener('127.0.0.1', 0, SWOOLE_SOCK_UDP);\n    $server->start();\n}, true, SOCK_STREAM, false);\n$proc->start();\n$result = Process::wait();\nAssert::contains($proc->read(), 'require onPacket callback');\nAssert::eq($result['code'], 255);\n\n$proc = new Process(function ()  {\n    $server = new Swoole\\Http\\Server('127.0.0.1', 0, SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $server->on(Constant::EVENT_REQUEST, function () {});\n    $port = $server->addlistener('127.0.0.1', 0, SWOOLE_SOCK_TCP);\n    $port->set(['open_http_protocol' => false]);\n    $server->start();\n}, true, SOCK_STREAM, false);\n$proc->start();\n$result = Process::wait();\nAssert::contains($proc->read(), 'require onReceive callback');\nAssert::eq($result['code'], 255);\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/check_chunk_total_size.phpt",
    "content": "--TEST--\nswoole_server: check chunk total size\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Server;\nuse Swoole\\Client;\nuse Swoole\\Process;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    $client = new Client(SWOOLE_SOCK_TCP);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0))\n    {\n        echo \"Over flow. errno=\" . $client->errCode;\n        die(\"\\n\");\n    }\n\n    $data = str_repeat('A', 1 * 1024 * 1024) . \"\\r\\n\";\n    $chunk_size = 2048;\n    $len = strlen($data);\n    $chunk_num = intval($len / $chunk_size) + 1;\n\n    for ($i = 0; $i < $chunk_num; $i++)\n    {\n        if ($len < ($i + 1) * $chunk_size)\n        {\n            $sendn = $len - ($i * $chunk_size);\n        }\n        else\n        {\n            $sendn = $chunk_size;\n        }\n        $client->send(substr($data, $i * $chunk_size, $sendn));\n    }\n    $recv_data = $client->recv();\n    Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set(array(\n        'package_eof' => \"\\r\\n\",\n        'open_eof_check' => true,\n        \"worker_num\" => 1,\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data)\n    {\n        Assert::length($data, 1 * 1024 * 1024 + 2);\n        $serv->send($fd, \"shutdown\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/client_close_in_writable_event.phpt",
    "content": "--TEST--\nswoole_server: client close in writable event\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Client;\nuse Swoole\\Constant;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Client(SWOOLE_SOCK_TCP);\n    $client->set(['socket_buffer_size' => 128 * 1024]);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n        exit(\"connect failed\\n\");\n    }\n    $client->send(\"begin\");\n    $pm->wait();\n    usleep(100000);\n    $client->close();\n    $pm->wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'kernel_socket_send_buffer_size' => 128 * 1024,\n        'buffer_output_size' => 4 * 1024 * 1024,\n    ]);\n    $serv->on(Constant::EVENT_WORKER_START, function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(Constant::EVENT_CONNECT, function (Server $serv, $fd, $reactor_id) {\n        echo \"CONNECT $fd\\n\";\n    });\n    $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) use ($pm) {\n        $n = 8;\n        $serv->pause($fd);\n        while ($n--) {\n            $serv->send($fd, str_repeat('A', 2 * 1024 * 1024) . \"\\r\\n\");\n        }\n        $pm->wakeup();\n    });\n    $serv->on(Constant::EVENT_CLOSE, function (Server $serv, $fd, $reactor_id)  use ($pm) {\n        echo \"CLOSE $fd\\n\";\n        $pm->wakeup();\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\necho 'DONE'.PHP_EOL;\n?>\n--EXPECT--\nCONNECT 1\nCLOSE 1\nDONE\n"
  },
  {
    "path": "tests/swoole_server/close_force.phpt",
    "content": "--TEST--\nswoole_server: force to close\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Constant;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $client->set(['socket_buffer_size' => 128*1024]);\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            exit(\"connect failed\\n\");\n        }\n        $client->send(\"close\");\n        Co::sleep(2);\n        $data = '';\n\n        while(true) {\n            $ret = $client->recv();\n            if (empty($ret)) {\n                break;\n            }\n            $data .= $ret;\n            if (substr($ret, -2, 2) == \"\\r\\n\") {\n                break;\n            }\n        }\n        Assert::greaterThan(strlen($data), 8192);\n        echo \"DONE\\n\";\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => TEST_LOG_FILE,\n        'kernel_socket_send_buffer_size' => 128*1024,\n        'buffer_output_size' => 4*1024*1024,\n        'heartbeat_idle_time'      => 2,\n        'heartbeat_check_interval' => 1,\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) {\n        $serv->send($fd, str_repeat('A', 2 * 1024 * 1024).\"\\r\\n\");\n        $serv->close($fd);\n    });\n    $serv->on(Constant::EVENT_CLOSE, function (Server $serv, $fd, $reactor_id) {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/close_in_connect_callback.phpt",
    "content": "--TEST--\nswoole_server: close in onConnect callback\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Constant;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP );\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            exit(\"connect failed\\n\");\n        }\n        $client->send(\"close\");\n        $data = $client->recv();\n        Assert::eq($data, \"\");\n        echo \"DONE\\n\";\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 2,\n        'log_file' => TEST_LOG_FILE,\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) {\n    });\n    $serv->on(Constant::EVENT_CONNECT, function (Server $serv, $fd, $reactor_id) {\n        $serv->close($fd);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/close_in_manager.phpt",
    "content": "--TEST--\nswoole_server: close in manager process\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Constant;\nuse Swoole\\Timer;\n\nuse function Swoole\\Coroutine\\run;\n\nconst N  = 4;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        swoole_loop_n(N, function () use ($pm) {\n            go(function () use ($pm) {\n                $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n                if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n                    exit(\"connect failed\\n\");\n                }\n                $client->send(\"close\");\n                $data = $client->recv();\n                Assert::eq($data, \"\");\n            });\n        });\n    });\n    echo \"ALL DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 2,\n        'log_file' => TEST_LOG_FILE,\n    ]);\n    $serv->on(Constant::EVENT_MANAGER_START, function ($serv) use ($pm) {\n        Timer::after(200, function () use ($serv) {\n            foreach ($serv->connections as $fd) {\n                $serv->close($fd);\n            }\n        });\n        $pm->wakeup();\n    });\n    $serv->on(Constant::EVENT_RECEIVE, function (Server $serv, $fd, $reactor_id, $data) {\n    });\n    $serv->on(Constant::EVENT_CLOSE, function (Server $serv, $fd, $reactor_id) {\n        Assert::assert(posix_getpid() != $serv->manager_pid);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nALL DONE\n"
  },
  {
    "path": "tests/swoole_server/close_in_master.phpt",
    "content": "--TEST--\nswoole_server: close in master process\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Constant;\nuse Swoole\\Timer;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP );\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            exit(\"connect failed\\n\");\n        }\n        $client->send(\"close\");\n        $data = $client->recv();\n        Assert::eq($data, \"\");\n        echo \"DONE\\n\";\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => TEST_LOG_FILE,\n    ]);\n    $serv->on('start', function ($serv) {\n        Timer::tick(100, function ($timer) use ($serv) {\n            if ($serv->exists(1)) {\n                $serv->close(1);\n                Timer::clear($timer);\n            }\n        });\n    });\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/close_in_non_current_worker.phpt",
    "content": "--TEST--\nswoole_server: close in non-current worker\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Constant;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$socket = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_DGRAM, 0);\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP );\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            exit(\"connect failed\\n\");\n        }\n        $client->send(\"close\");\n        $data = $client->recv();\n        Assert::string($data);\n        Assert::length($data, 0);\n        echo \"DONE\\n\";\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n\n    global $socket;\n    $result[] = fgets($socket[1]);\n    $result[]  = fgets($socket[1]);\n\n    Assert::eq($result[0], $result[1]);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 2,\n        'log_file' => TEST_LOG_FILE,\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) {\n        global $socket;\n        fwrite($socket[0], $serv->worker_id.\"\\n\");\n        $serv->sendMessage(['close_fd' => $fd, 'worker_id' => $serv->worker_id], 1 - $serv->worker_id);\n    });\n    $serv->on(Constant::EVENT_CLOSE, function (Server $serv, $fd, $reactor_id) {\n        global $socket;\n        fwrite($socket[0], $serv->worker_id . \"\\n\");\n    });\n    $serv->on(Constant::EVENT_PIPE_MESSAGE, function (Server $serv, $workerId, $msg) {\n        Assert::true($serv->close($msg['close_fd']));\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/close_in_other_worker_with_base.phpt",
    "content": "--TEST--\nswoole_server: close in another process with base mode\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Constant;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$GLOBALS['atomic'] = new Swoole\\Atomic(0);\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n        if ($cli->connect('127.0.0.1', $pm->getFreePort(), 100) == false) {\n            echo \"ERROR\\n\";\n            return;\n        }\n        $data = base64_encode(random_bytes(128));\n        $cli->send($data);\n        Assert::same($cli->recv(), '');\n    });\n    $pm->kill();\n    Assert::eq($GLOBALS['atomic']->get(), 1);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set(array(\n        \"worker_num\" => 2,\n        'log_file' => TEST_LOG_FILE,\n    ));\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $serv->on(Constant::EVENT_PIPE_MESSAGE, function ($serv, $workerId, $msg) {\n        Assert::assert($serv->close($msg['fd']));\n        Assert::false($serv->close(99999));\n        Assert::eq($serv->getLastError(), SWOOLE_ERROR_SESSION_NOT_EXIST);\n    });\n\n    $serv->on(Constant::EVENT_RECEIVE, function (Swoole\\Server $serv, $fd, $rid, $data) {\n        $serv->sendMessage(['data' => $data, 'fd' => $fd], 1 - $serv->getWorkerId());\n    });\n\n    $serv->on(Constant::EVENT_CLOSE, function ($serv, $fd, $tid) {\n        $GLOBALS['atomic']->set($fd);\n    });\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/close_in_task_worker.phpt",
    "content": "--TEST--\nswoole_server: close in task worker\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Constant;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$socket = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_DGRAM, 0);\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP );\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            exit(\"connect failed\\n\");\n        }\n        $client->send(\"close\");\n        $data = $client->recv();\n        Assert::string($data);\n        Assert::length($data, 0);\n        echo \"DONE\\n\";\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n\n    global $socket;\n    $result[] = fgets($socket[1]);\n    $result[]  = fgets($socket[1]);\n\n    Assert::eq($result[0], $result[1]);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'task_worker_num' => 1,\n        'log_file' => TEST_LOG_FILE,\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) {\n        global $socket;\n        fwrite($socket[0], $serv->worker_id . \"\\n\");\n        $serv->task(['close_fd' => $fd, 'worker_id' => $serv->worker_id]);\n    });\n    $serv->on(Constant::EVENT_CLOSE, function (Server $serv, $fd, $reactor_id) {\n        global $socket;\n        fwrite($socket[0], $serv->worker_id . \"\\n\");\n    });\n    $serv->on(Constant::EVENT_TASK, function (Server $serv, $task_id, $worker_id, $msg) {\n        Assert::true($serv->close($msg['close_fd']));\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/close_max_fd.phpt",
    "content": "--TEST--\nswoole_server: close_max_fd\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        go(function() use ($pm) {\n            $client = new Co\\Client(SWOOLE_SOCK_TCP);\n            Assert::assert($client->connect('127.0.0.1', $pm->getFreePort()));\n            Assert::assert($client->send('test 1'));\n            $client->recv();\n            Co::sleep(1);\n            $client->send('ping 1');\n            Co::sleep(1);\n            $pm->kill();\n        });\n        go(function() use ($pm) {\n            $cli = new Co\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        });\n        go(function() use ($pm) {\n            $client = new Co\\Client(SWOOLE_SOCK_TCP);\n            Assert::assert($client->connect('127.0.0.1', $pm->getFreePort()));\n            $client->send('test 2');\n            Co::sleep(1);\n        });\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $log_file = __DIR__ . '/close_max_fd.log';\n    $fp = fopen($log_file, 'a');\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'worker_num' => 1,\n        'log_level' => SWOOLE_LOG_ERROR,\n    ]);\n\n    $server->on('receive', function (Swoole\\Server $serv, int $fd, int $rid, string $data) use ($fp) {\n        fputs($fp, \"recv: $data\\n\");\n        fputs($fp, 'getClientList: ' . implode(';', $serv->getClientList()) . \"\\n\");\n        Assert::true(!empty($serv->getClientList()));\n        foreach ($serv->connections as $_fd) {\n            fputs($fp, \"foreach: fd-{$_fd}\\n\");\n        }\n        $serv->send($fd, \"Server: \" . $data);\n    });\n\n    $server->on('close', function ($server, $fd) use ($fp) {\n        fputs($fp, \"close: fd-{$fd}\\n\");\n    });\n\n    $server->start();\n\n    fclose($fp);\n    $cnt = file_get_contents($log_file);\n\n    Assert::eq(substr_count($cnt, 'recv: test 1'), 1);\n    Assert::eq(substr_count($cnt, 'recv: test 2'), 1);\n    Assert::eq(substr_count($cnt, 'recv: ping 1'), 1);\n\n    Assert::eq(substr_count($cnt, 'close: fd-2'), 1);\n    Assert::eq(substr_count($cnt, 'close: fd-1'), 1);\n\n    Assert::eq(substr_count($cnt, 'foreach: fd-1'), 3);\n    Assert::eq(substr_count($cnt, 'foreach: fd-2'), 2);\n\n    Assert::eq(substr_count($cnt, 'getClientList: 1;2'), 2);\n    Assert::eq(substr_count($cnt, \"getClientList: 1\\n\"), 1);\n\n    unlink($log_file);\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/close_queued.phpt",
    "content": "--TEST--\nswoole_server: close queued\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Constant;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $client->set(['socket_buffer_size' => 128*1024]);\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            exit(\"connect failed\\n\");\n        }\n        $client->send(\"close\");\n        Co::sleep(1);\n        $data = '';\n\n        while(true) {\n            $ret = $client->recv();\n            if (empty($ret)) {\n                break;\n            }\n            $data .= $ret;\n            if (substr($ret, -2, 2) == \"\\r\\n\") {\n                break;\n            }\n        }\n        Assert::eq(strlen($data), 2*1024*1024 +2);\n        echo \"DONE\\n\";\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => TEST_LOG_FILE,\n        'kernel_socket_send_buffer_size' => 128*1024,\n        'buffer_output_size' => 4*1024*1024,\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) {\n        $serv->send($fd, str_repeat('A', 2 * 1024 * 1024).\"\\r\\n\");\n        $serv->close($fd);\n    });\n    $serv->on(Constant::EVENT_CLOSE, function (Server $serv, $fd, $reactor_id) {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/close_reset.phpt",
    "content": "--TEST--\nswoole_server: close with reset\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Constant;\nuse Swoole\\Timer;\nuse Swoole\\Coroutine\\Client;\n\n$pm = new SwooleTest\\ProcessManager;\n\nconst N = 4 * 1024 * 1024;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $client = new Client(SWOOLE_SOCK_TCP);\n        $client->set(['socket_buffer_size' => 128 * 1024]);\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            exit(\"connect failed\\n\");\n        }\n        $client->send(\"close\");\n        Co::sleep(1);\n        $data = '';\n\n        while (true) {\n            $ret = $client->recv();\n            if (empty($ret)) {\n                break;\n            }\n            $data .= $ret;\n            if (substr($ret, -2, 2) == \"\\r\\n\") {\n                break;\n            }\n        }\n        Assert::lessThan(strlen($data), N);\n        echo \"DONE\\n\";\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => TEST_LOG_FILE,\n        'kernel_socket_send_buffer_size' => 128 * 1024,\n        'socket_buffer_size' => 8 * 1024 * 1024,\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) {\n        $serv->send($fd, str_repeat('A', N) . \"\\r\\n\");\n        Assert::eq($serv->stats()['connection_num'], 1);\n        phpt_var_dump(\"close[0]\");\n        Assert::true($serv->close($fd));\n        usleep(50000);\n        phpt_var_dump(\"close[1]\");\n        Assert::false($serv->close($fd));\n        Assert::eq(swoole_last_error(), SWOOLE_ERROR_SESSION_CLOSED);\n        Assert::eq($serv->stats()['connection_num'], 1);\n        Timer::after(100, function () use ($fd, $serv) {\n            phpt_var_dump(\"close[2]\");\n            $serv->close($fd, true);\n            usleep(50000);\n            Assert::eq($serv->stats()['connection_num'], 0);\n        });\n    });\n    $serv->on(Constant::EVENT_CLOSE, function (Server $serv, $fd, $reactor_id) {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/command.phpt",
    "content": "--TEST--\nswoole_server: command [1]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$server = new Server('127.0.0.1', get_one_free_port(), SWOOLE_PROCESS);\n$server->addCommand('test_getpid', SWOOLE_SERVER_COMMAND_MASTER | SWOOLE_SERVER_COMMAND_EVENT_WORKER,\n    function ($server) {\n        return json_encode(['pid' => posix_getpid()]);\n    });\n$server->set([\n    'log_file' => '/dev/null',\n    'worker_num' => 2,\n]);\n\n$server->on('start', function (Server $serv) {\n    $result = $serv->command('test_getpid', 0, SWOOLE_SERVER_COMMAND_MASTER, ['type' => 'master']);\n    Assert::eq($result['pid'], $serv->getMasterPid());\n    $result = $serv->command('test_getpid', 1, SWOOLE_SERVER_COMMAND_EVENT_WORKER, ['type' => 'worker']);\n    Assert::eq($result['pid'], $serv->getWorkerPid(1));\n    $result = $serv->command('test_not_found', 1, SWOOLE_SERVER_COMMAND_EVENT_WORKER, ['type' => 'worker']);\n    Assert::false($result);\n\n    $serv->shutdown();\n});\n\n$server->on('request', function (Request $request, Response $response) {\n});\n$server->start();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/connections.phpt",
    "content": "--TEST--\nswoole_server: connection iterator\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nconst N = 10;\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\nglobal $count;\n$count = 0;\n$port = get_one_free_port();\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($port) {\n    global $count;\n    for ($i = 0; $i < N; $i++) {\n        go(function () use ($port) {\n            $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n            $r = $cli->connect(TCP_SERVER_HOST, $port, 1);\n            Assert::assert($r);\n            $data = $cli->recv();\n            Assert::same($data, \"OK\");\n            global $count;\n            $count++;\n            $cli->close();\n        });\n    }\n    Event::wait();\n    Assert::same($count, N);\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm, $port) {\n    $serv = new Server('127.0.0.1', $port, SWOOLE_BASE);\n    $serv->set(array(\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('connect', function (Server $serv, $fd, $rid) {\n        global $count;\n        $count++;\n        if ($count == N) {\n            Swoole\\Event::defer(function () use ($serv) {\n                foreach ($serv->connections as $fd) {\n                    $serv->send($fd, \"OK\");\n                }\n            });\n        }\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/discard_timeout_packet.phpt",
    "content": "--TEST--\nswoole_server: discard timeout packet\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\nconst TMP_LOG_FILE = '/tmp/swoole.server.log';\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $n = 2;\n        while($n--) {\n            $client = new Client(SWOOLE_SOCK_TCP);\n            $client->set([\n                'open_eof_check' => true,\n                'open_eof_split' => true,\n                \"package_eof\" => \"\\r\\n\",\n            ]);\n            $client->connect('127.0.0.1', $pm->getFreePort());\n            $client->send(\"Swoole\\r\\nhello world\\r\\nphp\\r\\njava\\r\\n\");\n            $client->close();\n        }\n        Co::sleep(0.8);\n    });\n    $pm->kill();\n    Assert::eq(substr_count(file_get_contents(TMP_LOG_FILE),\n        'Worker_discard_data() (ERRNO 1007): [2] ignore data'), 8);\n    unlink(TMP_LOG_FILE);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        \"package_eof\" => \"\\r\\n\",\n        'open_eof_check' => true,\n        'open_eof_split' => true,\n        'dispatch_mode' => 3,\n        'discard_timeout_request' => true,\n        \"worker_num\" => 1,\n        'log_file' => TMP_LOG_FILE,\n    ]);\n    $serv->on('workerStart', function (Server $serv) use ($pm) {\n        $pm->wakeup();\n        Co::sleep(0.5);\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n        $serv->send($fd, \"hello {$data}\\r\\n\\r\\n\");\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/dispatch_fallback.phpt",
    "content": "--TEST--\nswoole_server: dispatch_fallback\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nconst WORKER_N = 4;\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\nglobal $stats;\n$stats = array();\n$count = 0;\n$port = get_one_free_port();\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($port)\n{\n    global $count, $stats;\n    for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++)\n    {\n        go(function () use ($port) {\n            $cli = new Client(SWOOLE_SOCK_TCP);\n            $cli->set([\n                'package_eof' => \"\\r\\n\\r\\n\",\n                'open_eof_split' => true,\n            ]);\n            $r = $cli->connect(TCP_SERVER_HOST, $port, 1);\n            Assert::assert($r);\n            for ($i = 0; $i < MAX_REQUESTS; $i++)\n            {\n                $cli->send(\"hello world\\r\\n\\r\\n\");\n            }\n            $cli->count = 0;\n            for ($i = 0; $i < MAX_REQUESTS; $i++)\n            {\n                $data = $cli->recv();\n                global $stats;\n                $wid = trim($data);\n                if (isset($stats[$wid]))\n                {\n                    $stats[$wid]++;\n                }\n                else\n                {\n                    $stats[$wid] = 1;\n                }\n                $cli->count++;\n                if ($cli->count == MAX_REQUESTS)\n                {\n                    $cli->close();\n                }\n            }\n        });\n    }\n    Event::wait();\n    Swoole\\Process::kill($pid);\n    phpt_var_dump($stats);\n    foreach ($stats as $s)\n    {\n        Assert::same($s, MAX_REQUESTS * MAX_CONCURRENCY_MID / WORKER_N);\n    }\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm, $port)\n{\n    $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS);\n    $serv->set(array(\n        \"worker_num\" => WORKER_N,\n        'dispatch_mode' => 1,\n        'dispatch_func' => function ($serv, $fd, $type, $data) {\n            return SWOOLE_DISPATCH_RESULT_USERFUNC_FALLBACK;\n        },\n        'package_eof' => \"\\r\\n\\r\\n\",\n        'open_eof_split' => true,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data)\n    {\n        $serv->send($fd, $serv->worker_id . \"\\r\\n\\r\\n\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/dispatch_func.phpt",
    "content": "--TEST--\nswoole_server: dispatch_func\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip('not support ZTS', ZEND_THREAD_SAFE);\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    for ($c = MAX_CONCURRENCY_MID; $c--;) {\n        go(function () use ($pm) {\n            $client = new Co\\Client(SWOOLE_SOCK_UDP);\n            Assert::assert($client->connect('127.0.0.1', $pm->getFreePort()));\n            Assert::assert($client->send($data = get_safe_random()));\n            Assert::same($client->recv(), $data);\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n\n    function dispatch_packet($server, $fd, $type)\n    {\n        return $fd % $server->setting['worker_num'];\n    }\n\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_UDP);\n    $server->set([\n        'worker_num' => rand(4, 8),\n        'log_file' => '/dev/null',\n        'dispatch_func' => 'dispatch_packet'\n    ]);\n    $server->on(\"WorkerStart\", function (Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $server->on('packet', function (Server $server, $data, $client) {\n        $fd = unpack('L', pack('N', ip2long($client['address'])))[1];\n        Assert::same($fd % $server->setting['worker_num'], $server->worker_id);\n        $server->sendto($client['address'], $client['port'], $data);\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/dispatch_func_discard.phpt",
    "content": "--TEST--\nswoole_server: dispatch_func [discard]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n\n    go(function () use ($pm) {\n        $client = new Co\\Client(SWOOLE_SOCK_TCP);\n        Assert::assert($client->connect('127.0.0.1', $pm->getFreePort(), 0.2));\n\n        $data = str_repeat('A', 65534).\"\\r\\n\\r\\n\";\n        Assert::assert($client->send($data));\n        Assert::same(false, @$client->recv());\n    });\n    \n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n\n    function dispatch_packet($server, $fd, $type)\n    {\n        return -1;\n    }\n\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'log_file' => '/dev/null',\n        'open_eof_check' => true,\n        'package_eof' => \"\\r\\n\\r\\n\",\n        'dispatch_func' => 'dispatch_packet',\n    ]);\n    $server->on(\"WorkerStart\", function (Swoole\\Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $server->on('receive', function (Swoole\\Server $server, $fd, $threadId, $data) {\n        $server->send($fd, $data);\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/dispatch_func_memory_leak.phpt",
    "content": "--TEST--\nswoole_server: dispatch_func_memory_leak\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    for ($c = MAX_CONCURRENCY_MID; $c--;) {\n        go(function () use ($pm) {\n            $client = new Co\\Client(SWOOLE_SOCK_TCP);\n            Assert::assert($client->connect('127.0.0.1', $pm->getFreePort()));\n            Assert::assert($client->send(str_repeat(get_safe_random(1024), 32)));\n        });\n    }\n    Swoole\\Event::wait();\n    sleep(1);\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $mem_size = 0;\n    function dispatch_packet($server, $fd, $type, $data)\n    {\n        global $mem_size;\n        if ($mem_size) {\n            Assert::assert($mem_size + 128 * 1024 > memory_get_usage());\n        } else {\n            $mem_size = memory_get_usage();\n        }\n        return str_repeat('0', 1024 * 1024);\n    }\n\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n    $server->set([\n        'worker_num' => rand(4, 8),\n        'log_file' => '/dev/null',\n        'reactor_num' => 1,\n        'dispatch_func' => 'dispatch_packet',\n        'trace_flags' => SWOOLE_TRACE_EVENT,\n        'log_level' => 0,\n    ]);\n    $server->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('receive', function (Server $server, $data, $client) {\n\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/dispatch_mode_1.phpt",
    "content": "--TEST--\nswoole_server: dispatch_mode = 1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nconst WORKER_N = 4;\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\nuse Swoole\\Atomic;\n\nglobal $stats;\n$barrier = new Atomic(0);\n$stats = array();\n$count = 0;\n$port = get_one_free_port();\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($port) {\n    global $count, $stats;\n    for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) {\n        go(function () use ($port) {\n            $cli = new Client(SWOOLE_SOCK_TCP);\n            $cli->set([\n                'package_eof' => \"\\r\\n\\r\\n\",\n                'open_eof_split' => true,\n            ]);\n            $r = $cli->connect(TCP_SERVER_HOST, $port, 1);\n            Assert::assert($r);\n            for ($i = 0; $i < MAX_REQUESTS; $i++) {\n                $cli->send(\"hello world\\r\\n\\r\\n\");\n            }\n            $cli->count = 0;\n            for ($i = 0; $i < MAX_REQUESTS; $i++) {\n                $data = $cli->recv();\n                global $stats;\n                $wid = trim($data);\n                if (isset($stats[$wid])) {\n                    $stats[$wid]++;\n                } else {\n                    $stats[$wid] = 1;\n                }\n                $cli->count++;\n                if ($cli->count == MAX_REQUESTS) {\n                    $cli->close();\n                }\n            }\n        });\n    }\n    Event::wait();\n    Swoole\\Process::kill($pid);\n    phpt_var_dump($stats);\n    foreach ($stats as $s) {\n        Assert::same($s, MAX_REQUESTS * MAX_CONCURRENCY_MID / WORKER_N);\n    }\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm, $port, $barrier) {\n    $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS);\n    $serv->set(array(\n        \"worker_num\" => WORKER_N,\n        'dispatch_mode' => 1,\n        'package_eof' => \"\\r\\n\\r\\n\",\n        'open_eof_split' => true,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on('WorkerStart', function (Server $serv) use ($pm, $barrier) {\n        if ($barrier->add() == WORKER_N) {\n            $pm->wakeup();\n        }\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n        $serv->send($fd, $serv->worker_id . \"\\r\\n\\r\\n\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/dispatch_mode_10.phpt",
    "content": "--TEST--\nswoole_server: dispatch_mode = 10 [concurrent lb]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Constant;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Http\\Server;\nuse Swoole\\Table;\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\n$table = new Table(64);\n$table->column('count', Table::TYPE_INT);\n$table->create();\n\nconst N = IS_MAC_OS ? 256 : 1024;\nconst EOF = \"\\r\\n\\r\\n\";\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->magic_code = rand(10000000, 90000000);\n$pm->parentFunc = function ($pid) use ($pm, $table) {\n    run(function () use ($pm, $table) {\n        $rand = rand(1, 4);\n        $data = array(\n            'name' => __FILE__,\n            'sid' => $pm->magic_code,\n            'content' => str_repeat('A', 1024 * $rand),\n        );\n        $_serialize_data = serialize($data) . EOF;\n        $n = N;\n        while ($n--) {\n            for ($i = 0; $i < 16; $i++) {\n                go(function () use ($pm, $_serialize_data) {\n                    $client = new Client('127.0.0.1', $pm->getFreePort());\n                    Assert::true($client->post('/', $_serialize_data));\n                    Assert::eq($client->getStatusCode(), 200);\n                    Assert::eq($client->getBody(), \"SUCCESS\" . EOF);\n                });\n            }\n            System::sleep(0.002);\n        }\n    });\n\n    $pm->kill();\n\n    $array = array_column(iterator_to_array($table), 'count');\n    $standard_deviation = sqrt(swoole_get_variance(swoole_get_average($array), $array));\n    Assert::greaterThan($standard_deviation, 1);\n    Assert::lessThan($standard_deviation, 5);\n    echo 'DONE' . PHP_EOL;\n};\n\n$pm->childFunc = function () use ($pm, $table) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n\n    $serv->set(array(\n        'dispatch_mode' => SWOOLE_DISPATCH_CONCURRENT_LB,\n        'worker_num' => 4,\n        'log_file' => '/dev/null',\n    ));\n\n    $serv->on(Constant::EVENT_WORKER_START, function (Server $serv, $worker_id) use ($pm) {\n        if ($worker_id == 0) {\n            $pm->wakeup();\n        }\n    });\n\n    $serv->on(Constant::EVENT_REQUEST, function ($req, $resp) use ($pm, $table, $serv) {\n        $table->incr($serv->getWorkerId(), 'count');\n        if (rand(1000, 9999) % 10 == 0) {\n            System::sleep(0.5);\n        }\n        $_data = unserialize(rtrim($req->getContent()));\n        if ($_data and is_array($_data) and $_data['sid'] == $pm->magic_code) {\n            $resp->end(\"SUCCESS\" . EOF);\n        } else {\n            $resp->end(\"ERROR\" . EOF);\n        }\n    });\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/dispatch_mode_3.phpt",
    "content": "--TEST--\nswoole_server: dispatch_mode = 3\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\nskip_if_darwin_todo();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nconst WORKER_N = 16;\n\nuse Swoole\\Atomic;\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Event;\nuse Swoole\\Process;\nuse Swoole\\Server;\nuse SwooleTest\\ProcessManager;\n\nglobal $stats;\n$barrier = new Atomic(0);\n$stats = [];\n$count = 0;\n$port = get_one_free_port();\n\n$pm = new ProcessManager();\n$pm->parentFunc = function ($pid) use ($port) {\n    global $count, $stats;\n    for ($i = 0; $i < MAX_CONCURRENCY; $i++) {\n        go(function () use ($port) {\n            $cli = new Client(SWOOLE_SOCK_TCP);\n            $cli->set([\n                'package_eof' => \"\\r\\n\\r\\n\",\n                'open_eof_split' => true,\n            ]);\n            $r = $cli->connect(TCP_SERVER_HOST, $port, 1);\n            Assert::assert($r);\n            for ($i = 0; $i < MAX_REQUESTS; $i++) {\n                $cli->send(\"hello world\\r\\n\\r\\n\");\n            }\n            $cli->count = 0;\n            for ($i = 0; $i < MAX_REQUESTS; $i++) {\n                $data = $cli->recv();\n                global $stats;\n                $wid = trim($data);\n                if (isset($stats[$wid])) {\n                    $stats[$wid]++;\n                } else {\n                    $stats[$wid] = 1;\n                }\n                $cli->count++;\n                if ($cli->count == MAX_REQUESTS) {\n                    $cli->close();\n                }\n            }\n        });\n    }\n    Event::wait();\n    Process::kill($pid);\n    phpt_var_dump($stats);\n    Assert::eq(count($stats), WORKER_N);\n    Assert::lessThan($stats[5], MAX_REQUESTS);\n    Assert::lessThan($stats[10], MAX_REQUESTS);\n    Assert::same(array_sum($stats), MAX_REQUESTS * MAX_CONCURRENCY);\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm, $port, $barrier) {\n    $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => WORKER_N,\n        'dispatch_mode' => 3,\n        'package_eof' => \"\\r\\n\\r\\n\",\n        'enable_coroutine' => false,\n        'open_eof_split' => true,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on('WorkerStart', function (Server $serv) use ($pm, $barrier) {\n        if ($barrier->add() == WORKER_N) {\n            $pm->wakeup();\n        }\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n        if ($serv->worker_id == 10 or $serv->worker_id == 5) {\n            usleep(5000);\n        }\n        $serv->send($fd, $serv->worker_id . \"\\r\\n\\r\\n\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/dispatch_mode_7.phpt",
    "content": "--TEST--\nswoole_server: dispatch_mode = 7 [stream]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) {\n        echo \"Over flow. errno=\" . $client->errCode;\n        die(\"\\n\");\n    }\n\n    $data = array(\n        'name' => __FILE__,\n        'sid' => 1000236,\n        'content' => str_repeat('A', 8192 * rand(1, 3)),\n    );\n\n    $_serialize_data = serialize($data) . \"\\r\\n\\r\\n\";\n\n    $chunk_size = 2048;\n    $len = strlen($_serialize_data);\n    $chunk_num = intval($len / $chunk_size) + 1;\n    for ($i = 0; $i < $chunk_num; $i++) {\n        if ($len < ($i + 1) * $chunk_size) {\n            $sendn = $len - ($i * $chunk_size);\n        } else {\n            $sendn = $chunk_size;\n        }\n        $client->send(substr($_serialize_data, $i * $chunk_size, $sendn));\n        usleep(rand(100, 1000));\n    }\n    echo $client->recv();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        'package_eof' => \"\\r\\n\\r\\n\",\n        'open_eof_check' => true,\n        'open_eof_split' => true,\n        'dispatch_mode' => 7,\n        'package_max_length' => 1024 * 1024 * 2, //2M\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        \"reload_async\" => true,\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n        $_data = unserialize(rtrim($data));\n        if ($_data and is_array($_data) and $_data['sid'] == 1000236) {\n            $serv->send($fd, \"SUCCESS\\n\");\n        } else {\n            $serv->send($fd, \"ERROR\\n\");\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/dispatch_mode_8.phpt",
    "content": "--TEST--\nswoole_server: dispatch_mode = 8 [co conn lb]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Server;\nuse Swoole\\Table;\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\n$table = new Table(64);\n$table->column('count', Table::TYPE_INT);\n$table->create();\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->magic_code = rand(10000000, 90000000);\n$pm->parentFunc = function ($pid) use ($pm, $table) {\n    run(function () use ($pm, $table) {\n        $n = 200;\n        while ($n--) {\n            go(function () use ($pm, $table) {\n                $client = new Client(SWOOLE_SOCK_TCP);\n                if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) {\n                    echo \"Over flow. errno=\" . $client->errCode;\n                    die(\"\\n\");\n                }\n\n                $data = array(\n                    'name' => __FILE__,\n                    'sid' => $pm->magic_code,\n                    'content' => str_repeat('A', 8192 * rand(1, 3)),\n                );\n\n                $_serialize_data = serialize($data) . \"\\r\\n\\r\\n\";\n                $client->send($_serialize_data);\n                Assert::eq($client->recv(), \"SUCCESS\\n\");\n            });\n        }\n    });\n\n    $pm->kill();\n\n    $array = array_column(iterator_to_array($table), 'count');\n    $standard_deviation = sqrt(swoole_get_variance(swoole_get_average($array), $array));\n    Assert::greaterThan($standard_deviation, 1);\n    Assert::lessThan($standard_deviation, 5);\n    echo 'DONE' . PHP_EOL;\n};\n\n$pm->childFunc = function () use ($pm, $table) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        'package_eof' => \"\\r\\n\\r\\n\",\n        'open_eof_check' => true,\n        'open_eof_split' => true,\n        'dispatch_mode' => SWOOLE_DISPATCH_CO_CONN_LB,\n        'package_max_length' => 1024 * 1024 * 2,\n        \"worker_num\" => 4,\n        'log_file' => '/dev/null',\n        \"reload_async\" => true,\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv, $worker_id) use ($pm) {\n        if ($worker_id == 0) {\n            $pm->wakeup();\n        }\n    });\n    $serv->on('connect', function (Server $serv, $fd, $rid) use ($table) {\n        $table->incr($serv->getWorkerId(), 'count');\n        if (rand(1000, 9999) % 4 == 0) {\n            System::sleep(0.5);\n        }\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($pm) {\n        Assert::eq($serv->getClientInfo($fd)['worker_id'], $serv->getWorkerId());\n        $_data = unserialize(rtrim($data));\n        if ($_data and is_array($_data) and $_data['sid'] == $pm->magic_code) {\n            $serv->send($fd, \"SUCCESS\\n\");\n        } else {\n            $serv->send($fd, \"ERROR\\n\");\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/dispatch_mode_9.phpt",
    "content": "--TEST--\nswoole_server: dispatch_mode = 9 [co req lb]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Server;\nuse Swoole\\Table;\n\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\n$table = new Table(64);\n$table->column('count', Table::TYPE_INT);\n$table->create();\n\nconst N = 1024;\nconst EOF = \"\\r\\n\\r\\n\";\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->magic_code = rand(10000000, 90000000);\n$pm->parentFunc = function ($pid) use ($pm, $table) {\n    run(function () use ($pm, $table) {\n        $client = new Client(SWOOLE_SOCK_TCP);\n        $client->set(array(\n            'package_eof' => EOF,\n            'open_eof_check' => true,\n            'open_eof_split' => true,\n        ));\n        if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) {\n            echo \"Over flow. errno=\" . $client->errCode;\n            die(\"\\n\");\n        }\n\n        $rand = rand(1, 4);\n\n        $data = array(\n            'name' => __FILE__,\n            'sid' => $pm->magic_code,\n            'content' => str_repeat('A', 1024 * $rand),\n        );\n\n        $_serialize_data = serialize($data) . EOF;\n\n        go(function () use ($client) {\n            $n = N;\n            while ($n--) {\n                Assert::eq($client->recv(), \"SUCCESS\" . EOF);\n            }\n            $client->close();\n        });\n\n        $n = N;\n        while ($n--) {\n            $client->send($_serialize_data);\n            if ($n % 10 == 1) {\n                System::sleep(0.002);\n            }\n        }\n    });\n\n    $pm->kill();\n\n    $array = array_column(iterator_to_array($table), 'count');\n    $standard_deviation = sqrt(swoole_get_variance(swoole_get_average($array), $array));\n    Assert::greaterThan($standard_deviation, 1);\n    Assert::lessThan($standard_deviation, 5);\n    echo 'DONE' . PHP_EOL;\n};\n\n$pm->childFunc = function () use ($pm, $table) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n\n    $serv->set(array(\n        'package_eof' => \"\\r\\n\\r\\n\",\n        'open_eof_check' => true,\n        'open_eof_split' => true,\n        'dispatch_mode' => SWOOLE_DISPATCH_CO_REQ_LB,\n        'package_max_length' => 1024 * 1024 * 2,\n        \"worker_num\" => 4,\n        'log_file' => '/dev/null',\n        \"reload_async\" => true,\n    ));\n\n    $serv->on(\"WorkerStart\", function (Server $serv, $worker_id) use ($pm) {\n        if ($worker_id == 0) {\n            $pm->wakeup();\n        }\n    });\n\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($pm, $table) {\n        $table->incr($serv->getWorkerId(), 'count');\n        if (rand(1000, 9999) % 10 == 0) {\n            System::sleep(0.5);\n        }\n        $_data = unserialize(rtrim($data));\n        if ($_data and is_array($_data) and $_data['sid'] == $pm->magic_code) {\n            $serv->send($fd, \"SUCCESS\".EOF);\n        } else {\n            $serv->send($fd, \"ERROR\".EOF);\n        }\n    });\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/duplicate_registered.phpt",
    "content": "--TEST--\nswoole_server: duplicate registered\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$server = new Swoole\\Server('127.0.0.1', 0, SWOOLE_PROCESS);\n$server->on('start', function () { });\nAssert::same(true, !!'load Assert');\n$mem = null;\nfor ($n = 1000; $n--;) {\n    $server->on('start', function () { });\n    if ($mem === null) {\n        $mem = memory_get_usage();\n    }\n    Assert::same(memory_get_usage(), $mem);\n}\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/enable_coroutine.phpt",
    "content": "--TEST--\nswoole_server: reload with enable_coroutine\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Event;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Process;\nuse Swoole\\Server;\nuse SwooleTest\\ProcessManager;\n\n$pm = new ProcessManager();\n\n$pm->parentFunc = function () use ($pm) {\n    $list = [];\n    go(function () use (&$list, $pm) {\n        for ($i = 2; $i--;) {\n            for ($n = 5; $n--;) {\n                echo 'cid-' . httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/task?n={$n}\") . \"\\n\";\n            }\n            if ($i == 1) {\n                Process::kill(file_get_contents(TEST_PID_FILE), SIGUSR1);\n                $pm->wait();\n            }\n        }\n    });\n    Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'log_file' => TEST_LOG_FILE,\n        'pid_file' => TEST_PID_FILE,\n        'worker_num' => 1,\n    ]);\n    $server->on('WorkerStart', function (Server $server, int $worker_id) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Request $request, Response $response) {\n        System::sleep(0.01);\n        $response->end(co::getuid());\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\ncid-2\ncid-3\ncid-4\ncid-5\ncid-6\ncid-2\ncid-3\ncid-4\ncid-5\ncid-6\nDONE\n"
  },
  {
    "path": "tests/swoole_server/enable_delay_receive.phpt",
    "content": "--TEST--\nswoole_server: enable_delay_receive\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Co\\Client(SWOOLE_SOCK_TCP);\n        Assert::assert($client->connect('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($client->send('world'));\n        $s = microtime(true);\n        Assert::eq($client->recv(), \"hello world\");\n        time_approximate(0.3, microtime(true) - $s, 0.5);\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'worker_num' => 1,\n        'enable_delay_receive' => true,\n        'log_file' => '/dev/null'\n    ]);\n    $server->on('workerStart', function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('connect', function (Swoole\\Server $serv, int $fd, int $rid) {\n        Co::sleep(0.3);\n        $serv->confirm($fd);\n    });\n\n    $server->on('receive', function (Swoole\\Server $serv, int $fd, int $rid, string $data) {\n        $serv->send($fd, \"hello {$data}\");\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_server/enable_reuse_port.phpt",
    "content": "--TEST--\nswoole_server: enable_reuse_port\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n\nconst N = IS_IN_CI ? 32 : 128;\nconst W = 4;\n\n$pm = new SwooleTest\\ProcessManager;\n$count = new Swoole\\Atomic(0);\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $c = new Swoole\\Coroutine\\Scheduler();\n\n    $workerCounter = [];\n\n    $c->parallel(\n        N,\n        function () use ($pm, &$workerCounter) {\n            $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n            if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n                echo \"Over flow. errno=\" . $client->errCode;\n                die(\"\\n\");\n            }\n\n            $data = base64_decode(random_bytes(rand(1024, 8192))) . \"\\r\\n\\r\\n\";;\n            $client->send($data);\n            $data = $client->recv();\n            Assert::assert($data);\n            $json = json_decode($data);\n            if (!isset($workerCounter[$json->worker])) {\n                $workerCounter[$json->worker] = 0;\n            }\n            $workerCounter[$json->worker]++;\n        }\n    );\n\n    $c->start();\n    $pm->kill();\n\n    foreach ($workerCounter as $c) {\n        Assert::greaterThan($c, N / W / 2);\n    }\n};\n\n$pm->childFunc = function () use ($pm, $count) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set(\n        array(\n            'package_eof' => \"\\r\\n\\r\\n\",\n            'open_eof_check' => true,\n            'open_eof_split' => true,\n            'enable_reuse_port' => true,\n            'package_max_length' => 1024 * 1024 * 2, //2M\n            \"worker_num\" => W,\n            'log_file' => '/dev/null',\n        )\n    );\n    $serv->on(\n        \"workerStart\",\n        function (Server $serv) use ($pm, $count) {\n            $count->add(1);\n            if ($count->get() == $serv->setting['worker_num']) {\n                $pm->wakeup();\n            }\n        }\n    );\n    $serv->on(\n        'receive',\n        function (Server $serv, $fd, $rid, $data) {\n            $serv->send($fd, json_encode(['worker' => $serv->getWorkerId()]));\n        }\n    );\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/eof_protocol.phpt",
    "content": "--TEST--\nswoole_server: (eof protocol) recv 100k packet\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/../include/api/swoole_server/TestServer.php';\n\nTestServer::$PKG_NUM = MAX_PACKET_NUM;\n\nclass EofServer extends TestServer\n{\n    protected $show_lost_package = false;\n    function onReceive($serv, $fd, $reactor_id, $data)\n    {\n        $pkg = unserialize(rtrim($data));\n        if ($pkg['index'] % 1000 == 0)\n        {\n            //echo \"#{$pkg['index']} recv package. sid={$pkg['sid']}, length=\" . strlen($data) . \", md5=\".md5($data).\"\\n\";\n        }\n        if (!isset($pkg['index']))\n        {\n            exit(\"error packet\");\n        }\n        if ($pkg['index'] > self::$PKG_NUM)\n        {\n            echo \"invalid index #{$pkg['index']}\\n\";\n        }\n        $this->index[$pkg['index']] = true;\n    }\n    function onWorkerStart($serv, $wid)\n    {\n        global $pm;\n        $pm->wakeup();\n    }\n}\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort(), 2.0))\n    {\n        exit(\"connect failed\\n\");\n    }\n    $bytes = 0;\n    $pkg_bytes = 0;\n\n    for ($i = 0; $i < TestServer::$PKG_NUM; $i++)\n    {\n        $len = rand(1000, 1024 * 128 - 8);\n        $sid = rand(10000, 99999);\n\n        $array['index'] = $i;\n        $array['sid'] = $sid;\n        $array['len'] = $len;\n        $array['data'] = str_repeat('A', $len);\n        $pkt = serialize($array) . \"\\r\\n\\r\\n\";\n        $pkg_bytes += strlen($pkt);\n\n//        if ($i % 1000 == 0 or $i > 99000)\n//        {\n//            echo \"#{$i} send package. sid={$sid}, length=\" . ($len + 10) . \", total bytes={$pkg_bytes}\\n\";\n//        }\n        if (!$client->send($pkt))\n        {\n            break;\n        }\n        $bytes += strlen($pkt);\n    }\n    $recv = $client->recv();\n    echo $recv;\n\n//    echo \"send \".MAX_PACKET_NUM.\" packet sucess, send $bytes bytes\\n\";\n    $client->close();\n    usleep(1);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new EofServer($pm->getFreePort(), true);\n    $serv->set([\n//        'log_file' => '/dev/null',\n        'enable_coroutine'   => false,\n        'package_eof'        => \"\\r\\n\\r\\n\",\n        'open_eof_split'     => true,\n        'worker_num'         => 1,\n        'package_max_length' => 1024 * 1024 * 2,\n    ]);\n    $serv->start();\n};\n\n$pm->childFirst();\n//$pm->runParentFunc();\n$pm->run();\n?>\n--EXPECTREGEX--\nend\nTotal count=\\d+, bytes=\\d+\n"
  },
  {
    "path": "tests/swoole_server/eof_server.phpt",
    "content": "--TEST--\nswoole_server: eof server\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Client;\nuse Swoole\\Process;\nuse Swoole\\Server;\nuse SwooleTest\\ProcessManager;\n\n$pm = new ProcessManager();\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) {\n        echo 'Over flow. errno=' . $client->errCode;\n        exit(\"\\n\");\n    }\n\n    $data = [\n        'name' => __FILE__,\n        'sid' => 1000236,\n        'content' => str_repeat('A', 8192 * rand(1, 3)),\n    ];\n\n    $_serialize_data = serialize($data) . \"\\r\\n\\r\\n\";\n\n    $chunk_size = 2048;\n    $len = strlen($_serialize_data);\n    $chunk_num = intval($len / $chunk_size) + 1;\n    for ($i = 0; $i < $chunk_num; $i++) {\n        if ($len < ($i + 1) * $chunk_size) {\n            $sendn = $len - ($i * $chunk_size);\n        } else {\n            $sendn = $chunk_size;\n        }\n        $client->send(substr($_serialize_data, $i * $chunk_size, $sendn));\n        usleep(10000);\n    }\n    echo $client->recv();\n    Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'package_eof' => \"\\r\\n\\r\\n\",\n        'open_eof_check' => true,\n        'open_eof_split' => true,\n        'dispatch_mode' => 3,\n        'package_max_length' => 1024 * 1024 * 2, // 2M\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on('WorkerStart', function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n        $_data = unserialize(rtrim($data));\n        if ($_data and is_array($_data) and $_data['sid'] == 1000236) {\n            $serv->send($fd, 'SUCCESS');\n        } else {\n            $serv->send($fd, 'ERROR');\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/event/before_shutdown.phpt",
    "content": "--TEST--\nswoole_server/event: onBeforeShutdown\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Event;\nuse Swoole\\Server;\n\nconst SIZE = 8192 * 5;\nconst FILE = __DIR__ . '/tmp_result.txt';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->setLogFile(FILE);\n\n$pm->parentFunc = function ($pid) use ($pm) {\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on(\"start\", function (Server $serv) use ($pm) {\n        $pm->writeLog('master start');\n        Event::add(STDIN, function ($fp) {\n            echo fread($fp, 8192);\n        });\n        $serv->shutdown();\n    });\n    $serv->on(\"BeforeShutdown\", function (Server $serv) use ($pm) {\n        $pm->writeLog('before master shutdown');\n        Event::del(STDIN);\n    });\n    $serv->on(\"shutdown\", function (Server $serv) use ($pm) {\n        $pm->writeLog('master shutdown');\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $reactorId, $data) {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\necho file_get_contents(FILE);\nunlink(FILE);\n?>\n--EXPECT--\nmaster start\nbefore master shutdown\nmaster shutdown\n"
  },
  {
    "path": "tests/swoole_server/event/manager_start.phpt",
    "content": "--TEST--\nswoole_server/event: onManagerStart\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n\nconst SIZE = 8192* 5;\nconst FILE = __DIR__.'/tmp_resule.txt';\n\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    echo file_get_contents(FILE);\n    $pm->kill();\n    unlink(FILE);\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\"worker_num\" => 1, 'log_file' => '/dev/null']);\n    $serv->on(\"ManagerStart\", function (Server $serv) use ($pm) {\n        file_put_contents(FILE, 'manager start'.PHP_EOL);\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $reactorId, $data) {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nmanager start\n"
  },
  {
    "path": "tests/swoole_server/event/manager_stop.phpt",
    "content": "--TEST--\nswoole_server/event: onManagerStop\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Atomic;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->setWaitTimeout(5);\n\nconst FILE = __DIR__ . '/tmp_result.txt';\n\n$atomic = new Atomic();\n\n$pm->parentFunc = function () use ($pm) {\n    echo \"done\\n\";\n};\n\n$pm->childFunc = function () use ($pm, $atomic) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $serv->on(\"start\", function (Server $serv) use ($atomic) {\n        if ($atomic->add() == 2) {\n            $serv->shutdown();\n        }\n    });\n    $serv->on(\"ManagerStart\", function (Server $serv) use ($atomic) {\n        if ($atomic->add() == 2) {\n            $serv->shutdown();\n        }\n    });\n    $serv->on(\"ManagerStop\", function (Server $serv) use ($pm) {\n        file_put_contents(FILE, 'manager stop' . PHP_EOL);\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function () { });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\necho file_get_contents(FILE);\nunlink(FILE);\n\n?>\n--EXPECT--\ndone\nmanager stop\n"
  },
  {
    "path": "tests/swoole_server/event/shutdown.phpt",
    "content": "--TEST--\nswoole_server/event: onShutdown\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n\nconst SIZE = 8192 * 5;\nconst FILE = __DIR__ . '/tmp_resule.txt';\n\n$pm->parentFunc = function ($pid) use ($pm) {\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on(\"start\", function (Server $serv) {\n        $serv->shutdown();\n    });\n    $serv->on(\"shutdown\", function (Server $serv) use ($pm) {\n        file_put_contents(FILE, 'master shutdown' . PHP_EOL);\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $reactorId, $data) {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\necho file_get_contents(FILE);\nunlink(FILE);\n?>\n--EXPECT--\nmaster shutdown\n"
  },
  {
    "path": "tests/swoole_server/event/start.phpt",
    "content": "--TEST--\nswoole_server/event: onStart\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n\nconst SIZE = 8192* 5;\nconst FILE = __DIR__.'/tmp_resule.txt';\n\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    echo file_get_contents(FILE);\n    $pm->kill();\n    unlink(FILE);\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\"worker_num\" => 1, 'log_file' => '/dev/null']);\n    $serv->on(\"start\", function (Server $serv) use ($pm) {\n        file_put_contents(FILE, 'master start'.PHP_EOL);\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $reactorId, $data) {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nmaster start\n"
  },
  {
    "path": "tests/swoole_server/event/worker_exit.phpt",
    "content": "--TEST--\nswoole_server/event: onWorkerExit\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Constant;\nuse Swoole\\Process;\nuse Swoole\\Server;\nuse Swoole\\Timer;\nuse SwooleTest\\ProcessManager;\n\n$pm = new ProcessManager();\n$pm->setWaitTimeout(5);\n\nconst FILE = __DIR__ . '/tmp_result.txt';\n\n$atomic = new Atomic();\n\n$pm->setWaitTimeout(5);\n$pm->setLogFile(FILE);\n\n$pm->parentFunc = function () use ($pm) {\n    usleep(10000);\n    Process::kill($pm->getChildPid(), SIGUSR1);\n    echo \"done\\n\";\n};\n\n$pm->childFunc = function () use ($pm, $atomic) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n\n    $serv->on('start', function (Server $serv) use ($atomic, $pm) {\n        $pm->writeLog('master start');\n    });\n\n    $serv->on(Constant::EVENT_MANAGER_START, function (Server $serv) use ($atomic, $pm) {\n        usleep(10000);\n        $pm->writeLog('manager start');\n    });\n\n    $serv->on(Constant::EVENT_WORKER_START, function (Server $serv) use ($atomic, $pm) {\n        if ($atomic->get() == 0) {\n            usleep(20000);\n        }\n        $pm->writeLog('worker start, id=' . $serv->getWorkerId() . ', status=' . $serv->getWorkerStatus());\n\n        if ($atomic->add() == 2) {\n            usleep(10000);\n            $serv->shutdown();\n        } else {\n            $GLOBALS['timer'] = Timer::tick(100, function () use ($serv, $pm) {\n                $pm->writeLog(\n                    'tick, id=' . $serv->getWorkerId() . ', status=' . $serv->getWorkerStatus()\n                );\n                $pm->wakeup();\n            });\n        }\n    });\n\n    $serv->on(Constant::EVENT_WORKER_EXIT, function (Server $serv) use ($atomic, $pm) {\n        $pm->writeLog(\n            'worker exit, id=' . $serv->getWorkerId() . ', status=' . $serv->getWorkerStatus()\n        );\n        Timer::clear($GLOBALS['timer']);\n    });\n\n    $serv->on(Constant::EVENT_WORKER_STOP, function (Server $serv) use ($pm) {\n        $pm->writeLog('worker stop');\n    });\n\n    $serv->on('Receive', function () {});\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\necho file_get_contents(FILE);\nunlink(FILE);\n\n?>\n--EXPECT--\ndone\nmaster start\nmanager start\nworker start, id=0, status=2\ntick, id=0, status=2\nworker exit, id=0, status=3\nworker stop\nworker start, id=0, status=2\nworker stop\n"
  },
  {
    "path": "tests/swoole_server/exist.phpt",
    "content": "--TEST--\nswoole_server: exist\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/opcode_server.php\";\n$port = get_one_free_port();\n\nstart_server($simple_tcp_server, TCP_SERVER_HOST, $port);\n$timer = suicide(2000);\nusleep(500 * 1000);\n\nmakeCoTcpClient(TCP_SERVER_HOST, $port, function (Client $cli) {\n    $r = $cli->send(opcode_encode(\"exist\", [2]));\n    Assert::assert($r !== false);\n}, function (Client $cli, $recv) use ($timer) {\n    list($op, $data) = opcode_decode($recv);\n    Assert::true($data);\n    $cli->close();\n    Timer::clear($timer);\n    echo \"SUCCESS\\n\";\n});\nEvent::wait();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/force_reload.phpt",
    "content": "--TEST--\nswoole_server: force reload in process mode\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nif (swoole_cpu_num() === 1) {\n    skip('not support on machine with single cpu');\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Client;\nuse Swoole\\Process;\nuse Swoole\\Server;\n\nconst WORKER_NUM = 4;\nerror_reporting(0);\n\n$pm = new ProcessManager();\n$atomic = new Atomic();\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $n = WORKER_NUM;\n    $clients = [];\n    while ($n--) {\n        $client = new Client(SWOOLE_SOCK_TCP);\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            exit(\"connect failed\\n\");\n        }\n        $client->send('hello world');\n        $clients[] = $client;\n    }\n    switch_process();\n    // reload\n    Process::kill($pid, SIGUSR1);\n    sleep(3);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $atomic) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'worker_num' => WORKER_NUM,\n        'max_wait_time' => 1,\n        'enable_coroutine' => false,\n    ]);\n    $server->on('workerStart', function (Server $server, $worker_id) use ($pm, $atomic) {\n        $atomic->add(1);\n        if ($atomic->get() === WORKER_NUM) {\n            $pm->wakeup();\n        }\n    });\n    $server->on('receive', function ($serv, $fd, $tid, $data) {\n        while (true) {\n            sleep(100);\n        }\n    });\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\nAssert::eq($atomic->get(), WORKER_NUM * 2);\n?>\n--EXPECTF--\n[%s]\tINFO\tServer is reloading all workers now\n[%s]\tWARNING\tReloadTask::kill_all(): force kill worker process(pid=%d, id=%d)\n[%s]\tWARNING\tReloadTask::kill_all(): force kill worker process(pid=%d, id=%d)\n[%s]\tWARNING\tReloadTask::kill_all(): force kill worker process(pid=%d, id=%d)\n[%s]\tWARNING\tReloadTask::kill_all(): force kill worker process(pid=%d, id=%d)\n[%s]\tWARNING\tWorker::report_error(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9\n[%s]\tWARNING\tWorker::report_error(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9\n[%s]\tWARNING\tWorker::report_error(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9\n[%s]\tWARNING\tWorker::report_error(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9\n"
  },
  {
    "path": "tests/swoole_server/force_reload2.phpt",
    "content": "--TEST--\nswoole_server: force reload in base mode\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nerror_reporting(0);\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Server;\nuse Swoole\\Timer;\nuse SwooleTest\\ProcessManager;\n\n$atomic = new Atomic(1);\n$pm = new ProcessManager();\n$pm->setWaitTimeout(1000);\n$pm->parentFunc = function ($pid) use ($pm) {\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $atomic) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'worker_num' => 2,\n        'max_wait_time' => 1,\n        'enable_coroutine' => false,\n    ]);\n    $serv->on('WorkerStart', function (Server $server, $worker_id) use ($pm, $atomic) {\n        echo \"{$worker_id} [\" . $server->worker_pid . \"] start\\n\";\n        if ($worker_id == 0) {\n            if ($atomic->get() == 1) {\n                while (true) {\n                    sleep(10);\n                }\n            } else {\n                $pm->wakeup();\n            }\n        }\n\n        if ($worker_id == 1 and $atomic->get() == 1) {\n            Timer::after(1, function () use ($server, $worker_id, $atomic) {\n                $atomic->add(1);\n                echo \"{$worker_id} [\" . $server->worker_pid . \"] reload\\n\";\n                $server->reload();\n            });\n        }\n    });\n    $serv->on('WorkerStop', function (Server $server, $worker_id) use ($pm, $atomic) {\n        echo \"{$worker_id} [\" . $server->worker_pid . \"] stop\\n\";\n    });\n    $serv->on('receive', function ($serv, $fd, $tid, $data) {});\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n%d [%d] start\n%d [%d] start\n%d [%d] reload\n[%s]\tINFO\tServer is reloading all workers now\n%d [%d] stop\n%d [%d] start\n[%s]\tWARNING\tReloadTask::kill_all(): force kill worker process(pid=%d, id=%d)\n[%s]\tWARNING\tWorker::report_error(): worker(pid=%d, id=0) abnormal exit, status=0, signal=9\n%d [%d] start\n%d [%d] stop\n%d [%d] stop\n"
  },
  {
    "path": "tests/swoole_server/force_reload3.phpt",
    "content": "--TEST--\nswoole_server: force reload\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nerror_reporting(0);\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Timer;\n\n$atomic = new Swoole\\Atomic(1);\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm,$argv) {\n    sleep(2);\n    $script_name = $argv[0];\n    $ret = shell_exec(\"ps aux | grep $script_name | grep -v 'grep'\");\n    Assert::assert($ret != \"\");\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm,$atomic) {\n    $flag = 0;\n    $flag1 = 0;\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'log_file' => TEST_LOG_FILE,\n        \"worker_num\" => 2,\n        \"task_worker_num\" => 1,\n        \"max_wait_time\" => 1\n    ]);\n    $serv->on(\"WorkerStart\", function (Server $server, $worker_id) use ($pm, $atomic) {\n        $pm->wakeup();\n        Timer::after(50,function() use ($server, $worker_id, $atomic){\n            if ($atomic->get() == 1) {\n                $atomic->add(1);\n                $server->reload();\n            }\n        });\n    });\n    $serv->on('receive', function ($serv, $fd, $tid, $data) {\n    });\n    $serv->on('task', function ($serv,$task_id, $reactor_id, $params) {\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_server/force_reload4.phpt",
    "content": "--TEST--\nswoole_server: force reload (timer)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Timer;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->setWaitTimeout(30);\n$pm->parentFunc = function () use ($pm) {\n    echo \"OK\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'reload_async' => true,\n        'task_enable_coroutine' => true,\n        'max_wait_time' => 2,\n    ]);\n    $server->on(\"shutdown\", function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('workerStart', function (Swoole\\Server $server, int $wid) use ($pm) {\n        if ($wid === 0) {\n            Timer::tick(5000, function () {\n                echo 'tick';\n            });\n            Timer::after(500, function () use ($server) {\n                $server->shutdown();\n            });\n        }\n    });\n    $server->on('receive', function () { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n[%s]\tWARNING\t%s (ERRNO 9101): worker exit timeout, forced termination\nOK\n"
  },
  {
    "path": "tests/swoole_server/getCallback.phpt",
    "content": "--TEST--\nswoole_server: getCallback\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$server = new Swoole\\Server('127.0.0.1', 0, SWOOLE_PROCESS);\n$server->on('start', function ($server) { });\n$server->on('shutdown', function ($server) { });\nAssert::isInstanceOf($server->getCallback('start'), Closure::class);\nAssert::assert(is_callable($server->getCallback('start')));\n$cb = $server->getCallback('start');\nAssert::same($cb, $server->getCallback('start'));\nAssert::same($server->getCallback('Receive'), null);\n$server->on('receive', function () { });\nAssert::isInstanceOf($server->getCallback('receive'), Closure::class);\necho \"DONE\\n\"\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/getClientInfo.phpt",
    "content": "--TEST--\nswoole_server: get client info\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/opcode_server.php\";\n$port = get_one_free_port();\n\nstart_server($simple_tcp_server, TCP_SERVER_HOST, $port);\n\n$timer = suicide(2000);\nusleep(500 * 1000);\n\nmakeCoTcpClient(TCP_SERVER_HOST, $port, function (Client $cli) {\n    $r = $cli->send(opcode_encode(\"getClientInfo\", [2]));\n    Assert::assert($r !== false);\n}, function (Client $cli, $recv) use ($timer) {\n    list($op, $data) = opcode_decode($recv);\n    Assert::assert(is_array($data) && $data);\n    $cli->close();\n    Timer::clear($timer);\n    echo \"SUCCESS\\n\";\n});\nEvent::wait();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/getClientInfo_in_callback_of_close.phpt",
    "content": "--TEST--\nswoole_server: getClientInfo in callback of close event\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Constant;\nuse Swoole\\Timer;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP );\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            exit(\"connect failed\\n\");\n        }\n        $client->send(\"close\");\n        $data = $client->recv();\n        Assert::eq($data, \"\");\n        echo \"DONE\\n\";\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => TEST_LOG_FILE,\n    ]);\n    $serv->on('workerStart', function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) {\n        $serv->close($fd);\n    });\n    $serv->on('close', function (Server $serv, $fd, $wid) {\n        $info = $serv->getClientInfo($fd);\n        Assert::isArray($info);\n        Assert::eq($info['close_errno'], 0);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/getClientInfo_in_callback_of_close_2.phpt",
    "content": "--TEST--\nswoole_server: getClientInfo in callback of close event [2]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Constant;\nuse Swoole\\Timer;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP );\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            exit(\"connect failed\\n\");\n        }\n        $client->close();\n        echo \"DONE\\n\";\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => TEST_LOG_FILE,\n    ]);\n    $serv->on('workerStart', function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) {\n        $serv->close($fd);\n    });\n    $serv->on('close', function (Server $serv, $fd, $wid) {\n        $info = $serv->getClientInfo($fd);\n        Assert::isArray($info);\n        Assert::eq($info['close_errno'], 0);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/getClientList.phpt",
    "content": "--TEST--\nswoole_server: get client list\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/opcode_server.php\";\n$port = get_one_free_port();\n\nstart_server($simple_tcp_server, TCP_SERVER_HOST, $port);\n\n$timer = suicide(2000);\nusleep(500 * 1000);\n\nmakeCoTcpClient(TCP_SERVER_HOST, $port, function(Client $cli) {\n    $r = $cli->send(opcode_encode(\"getClientList\", []));\n    Assert::assert($r !== false);\n}, function(Client $cli, $recv) use ($timer) {\n    list($op, $data) = opcode_decode($recv);\n    Assert::assert(is_array($data) && count($data) === 1);\n    echo \"SUCCESS\\n\";\n    $cli->close();\n    Timer::clear($timer);\n});\nEvent::wait();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/getLastError.phpt",
    "content": "--TEST--\nswoole_server: get last error\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/opcode_server.php\";\n$port = get_one_free_port();\n\nstart_server($simple_tcp_server, TCP_SERVER_HOST, $port);\n\n$timer = suicide(2000);\nusleep(100 * 1000);\n\nmakeCoTcpClient(TCP_SERVER_HOST, $port, function (Client $cli) {\n    $r = $cli->send(opcode_encode(\"getLastError\", []));\n    Assert::assert($r !== false);\n}, function (Client $cli, $recv) use($timer) {\n    list($op, $data) = opcode_decode($recv);\n    Assert::same($data, 0);\n    $cli->close();\n    Swoole\\Timer::clear($timer);\n    echo \"SUCCESS\\n\";\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/getSocket.phpt",
    "content": "--TEST--\nswoole_server: getSocket\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\Run(function () use ($pm) {\n        $cli = new Client(SWOOLE_SOCK_TCP);\n        $r = $cli->connect(TCP_SERVER_HOST, $pm->getFreePort(), 1);\n        Assert::assert($r);\n        $cli->send(\"test\");\n        $data = $cli->recv();\n        Assert::same($data, 'Socket');\n        $cli->send('shutdown');\n        $cli->close();\n        echo \"SUCCESS\\n\";\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_PROCESS);\n    $socket = $serv->getSocket();\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $rid, $data) use ($socket) {\n        if (trim($data) == 'shutdown') {\n            $serv->shutdown();\n            return;\n        } else {\n            if (PHP_VERSION_ID > 80000) {\n                $serv->send($fd, get_class($socket));\n            } else {\n                $serv->send($fd, get_resource_type($socket));\n            }\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/getWorkerStatus.phpt",
    "content": "--TEST--\nswoole_server: getWorkerStatus\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Co\\Client(SWOOLE_SOCK_TCP);\n        Assert::assert($client->connect('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($client->send('world'));\n\n        $data = $client->recv();\n        Assert::assert($data);\n        $json = json_decode($data);\n        Assert::assert($json);\n\n        Assert::eq($json->current_worker, SWOOLE_WORKER_BUSY);\n        Assert::eq($json->another_worker, SWOOLE_WORKER_IDLE);\n\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'worker_num' => 2,\n        'log_file' => '/dev/null'\n    ]);\n    $server->on('workerStart', function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('receive', function (Swoole\\Server $serv, int $fd, int $rid, string $data) {\n        $serv->send($fd, json_encode([\n            'current_worker' => $serv->getWorkerStatus(),\n            'another_worker' => $serv->getWorkerStatus(1-$serv->getWorkerId()),\n        ]));\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/heartbeat.phpt",
    "content": "--TEST--\nswoole_server: heart beat false\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/opcode_server.php\";\n$port = get_one_free_port();\n\nstart_server($simple_tcp_server, TCP_SERVER_HOST, $port);\n\n$timer = suicide(2000);\nusleep(200 * 1000);\n\nmakeCoTcpClient(TCP_SERVER_HOST, $port, function (Client $cli) {\n    $r = $cli->send(opcode_encode(\"heartbeat\", [false]));\n    Assert::assert($r !== false);\n}, function (Client $cli, $recv) use ($timer) {\n    list($op, $data) = opcode_decode($recv);\n\n    $cli->close();\n    Timer::clear($timer);\n    echo \"SUCCESS\\n\";\n});\nEvent::wait();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/heartbeat_true.phpt",
    "content": "--TEST--\nswoole_server: heart beat (true)\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/opcode_server.php\";\n$port = get_one_free_port();\n\nstart_server($simple_tcp_server, TCP_SERVER_HOST, $port);\n\n$timer = suicide(2000);\nusleep(500 * 1000);\n\nmakeCoTcpClient(TCP_SERVER_HOST, $port, function (Client $cli) {\n    $r = $cli->send(opcode_encode(\"heartbeat\", [true]));\n    Assert::assert($r !== false);\n}, function (Client $cli, $recv) use ($timer) {\n    list($op, $data) = opcode_decode($recv);\n    $cli->close();\n    Timer::clear($timer);\n    echo \"SUCCESS\\n\";\n});\nEvent::wait();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/heartbeat_with_base.phpt",
    "content": "--TEST--\nswoole_server: heart beat with SWOOLE_BASE\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort(), 5, 0)) {\n        echo \"Over flow. errno=\" . $client->errCode;\n        die(\"\\n\");\n    }\n    $s1 = time();\n    Assert::same($client->recv(), '');\n    $s2 = time();\n    Assert::assert($s2 - $s1 > 1);\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set(array(\n        'heartbeat_check_interval' => 1,\n        'heartbeat_idle_time' => 2,\n        'worker_num' => 1,\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/http_protocol.phpt",
    "content": "--TEST--\nswoole_server: http request & response\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Constant;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Coroutine\\Http\\Client;\nuse function Swoole\\Coroutine\\run;\n\ndefine('GREETER', 'hello world');\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        $httpClient = new Client(HTTP_SERVER_HOST, $pm->getFreePort(), false);\n        $httpClient->setMethod(\"POST\");\n        $httpClient->setData(\"HELLO\");\n        $ok = $httpClient->execute(\"/rawcookie?hello=world&value=1\");\n        Assert::assert($ok);\n        Assert::same($httpClient->statusCode, 200);\n        Assert::same($httpClient->errCode, 0);\n        Assert::eq($httpClient->getHeaders()['x-server'], 'swoole');\n        Assert::same($httpClient->getBody(), GREETER);\n        echo \"DONE\\n\";\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'open_http_protocol' => true,\n    ]);\n    $serv->on(\"Start\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) {\n        $req = Request::create();\n        Assert::eq($req->parse($data), strlen($data));\n        \n        $resp = Response::create([$serv, $req], $fd);\n        $resp->header('X-Server', 'swoole');\n        $resp->end(GREETER);\n        Assert::eq($resp->fd, $fd);\n        Assert::eq($req->fd, $fd);\n    });\n    $serv->on(Constant::EVENT_CLOSE, function (Server $serv, $fd, $reactor_id) {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/idle_worekr_num.phpt",
    "content": "--TEST--\nswoole_server: idle_worker_num\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n\nuse Swoole\\Server;\nuse Swoole\\Client;\n\n$counter = new Swoole\\Atomic(0);\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n        exit(\"connect failed\\n\");\n    }\n    $client->send(\"hello world\");\n    $data = $client->recv();\n    Assert::assert($data);\n    $json = json_decode($data);\n    Assert::eq($json->idle_worker_num, 2);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $counter) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $serv->set([\n        'worker_num' => 3,\n        'log_level' => SWOOLE_LOG_ERROR,\n        'enable_coroutine' => false,\n    ]);\n    $serv->on(\"workerStart\", function (Server $serv, $wid) use ($pm, $counter) {\n        if ($counter->add(1) == $serv->setting['worker_num']) {\n            Swoole\\Event::defer(function () use ($pm) {\n                $pm->wakeup();\n            });\n        }\n    });\n    $serv->on('receive', function (Server $serv, $fd, $tid, $data) {\n        usleep(10000);\n        $serv->send($fd, json_encode($serv->stats()));\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_server/invalid_fd.phpt",
    "content": "--TEST--\nswoole_server: invalid fd\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nconst EOF = \"\\r\\n\\r\\n\";\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $client = new Co\\Client(SWOOLE_SOCK_TCP);\n        Assert::assert($client->connect('127.0.0.1', $pm->getFreePort()));\n        Assert::notEmpty($client->send(\"TEST\" . EOF));\n        Assert::notEmpty($client->recv());\n        switch_process();\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n        'open_eof_split' => true,\n        'package_eof' => EOF,\n    ]);\n    $server->on('workerStart', function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('receive', function (Swoole\\Server $serv, int $fd, int $rid, string $data) {\n        Assert::false($serv->send(null, \"hello {$fd}\"));\n        Assert::false($serv->send(-1, \"hello {$fd}\"));\n        Assert::false($serv->send(100, \"hello {$fd}\"));\n        Assert::false($serv->send(PHP_INT_MAX, \"hello {$fd}\"));\n        Assert::true($serv->send($fd, \"DONE\\n\"));\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nWarning: Swoole\\Server::send(): fd can not be null in %s/tests/swoole_server/invalid_fd.php on line %d\n\nWarning: Swoole\\Server::send(): invalid fd[-1] in %s/tests/swoole_server/invalid_fd.php on line %d\n"
  },
  {
    "path": "tests/swoole_server/invalid_option.phpt",
    "content": "--TEST--\nswoole_server: invalid option\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$serv = new Server('127.0.0.1', 0, SWOOLE_BASE);\n$options = [\n    'worker_num' => 1,\n    'backlog' => 128,\n    'invalid_option' => true,\n];\n\ntry {\n    $serv->set($options);\n} catch (\\Swoole\\Exception $e) {\n    echo $e->getMessage();\n}\n\n?>\n--EXPECTF--\nWarning: unsupported option [invalid_option] in @swoole/library/core/Server/Helper.php on line %d\n%A\n%A\n%A\n"
  },
  {
    "path": "tests/swoole_server/kill_user_process_01.phpt",
    "content": "--TEST--\nswoole_server: kill user process [SWOOLE_PROCESS]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_darwin();\n//skip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . \"/../include/bootstrap.php\";\nuse Swoole\\Server;\nconst WORKER_PROC_NAME = 'swoole_unittest_server_user_process';\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($i = 0; $i < 1; $i++)\n    {\n        //杀死进程\n        kill_process_by_name(WORKER_PROC_NAME);\n        usleep(10000);\n        //判断进程是否存在\n        Assert::assert(get_process_pid_by_name(WORKER_PROC_NAME) > 0);\n    }\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n//    $serv->set(\n//        ['log_file' => TEST_LOG_FILE,]\n//    );\n    $process2 = new Swoole\\Process(function ($worker) use ($serv, $pm) {\n        global $argv;\n        swoole_set_process_name(WORKER_PROC_NAME);\n        Swoole\\Process::signal(SIGTERM, function () {\n            Swoole\\Event::exit();\n        });\n        Swoole\\Timer::after(200000, function ($interval) use ($worker, $serv) {\n            echo \"OK\\n\";\n        });\n        $pm->wakeup();\n    }, false);\n    $serv->addProcess($process2);\n    $serv->set([\"worker_num\" => 2, 'log_file' => '/dev/null',]);\n    $serv->on(\"WorkerStart\", function (Server $serv, $worker_id) use ($pm) {\n\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $reactorId, $data)\n    {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/kill_user_process_02.phpt",
    "content": "--TEST--\nswoole_server: kill user process [SWOOLE_BASE]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_darwin();\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . \"/../include/bootstrap.php\";\nuse Swoole\\Server;\nconst WORKER_PROC_NAME = 'swoole_unittest_server_user_process';\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($i = 0; $i < 1; $i++)\n    {\n        //杀死进程\n        kill_process_by_name(WORKER_PROC_NAME);\n        usleep(10000);\n        //判断进程是否存在\n        Assert::assert(get_process_pid_by_name(WORKER_PROC_NAME) > 0);\n    }\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n//    $serv->set(\n//        ['log_file' => TEST_LOG_FILE,]\n//    );\n    $process2 = new Swoole\\Process(function ($worker) use ($serv, $pm) {\n        global $argv;\n        swoole_set_process_name(WORKER_PROC_NAME);\n        Swoole\\Process::signal(SIGTERM, function () {\n            Swoole\\Event::exit();\n        });\n        Swoole\\Timer::after(200000, function ($interval) use ($worker, $serv) {\n            echo \"OK\\n\";\n        });\n        $pm->wakeup();\n    }, false);\n    $serv->addProcess($process2);\n    $serv->set([\"worker_num\" => 2, 'log_file' => '/dev/null',]);\n    $serv->on(\"WorkerStart\", function (Server $serv, $worker_id) use ($pm) {\n\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $reactorId, $data)\n    {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/kill_worker_01.phpt",
    "content": "--TEST--\nswoole_server: kill worker [SWOOLE_BASE]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_darwin();\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . \"/../include/bootstrap.php\";\nuse Swoole\\Server;\nconst WORKER_PROC_NAME = 'swoole_unittest_server_event_worker';\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($i = 0; $i < 5; $i++)\n    {\n        //杀死进程\n        kill_process_by_name(WORKER_PROC_NAME);\n        usleep(10000);\n        //判断进程是否存在\n        Assert::assert(get_process_pid_by_name(WORKER_PROC_NAME) > 0);\n    }\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\"worker_num\" => 2, 'log_file' => '/dev/null',]);\n    $serv->on(\"WorkerStart\", function (Server $serv, $worker_id) use ($pm) {\n        if ($worker_id == 0)\n        {\n            swoole_set_process_name(WORKER_PROC_NAME);\n            $pm->wakeup();\n        }\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $reactorId, $data)\n    {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/kill_worker_02.phpt",
    "content": "--TEST--\nswoole_server: kill worker [SWOOLE_PROCESS]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_darwin();\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst WORKER_PROC_NAME = 'swoole_unittest_server_event_worker';\n$pm = new SwooleTest\\ProcessManager;\nuse Swoole\\Server;\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($i = 0; $i < 5; $i++)\n    {\n        //杀死进程\n        kill_process_by_name(WORKER_PROC_NAME);\n        usleep(10000);\n        //判断进程是否存在\n        Assert::assert(get_process_pid_by_name(WORKER_PROC_NAME) > 0);\n    }\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\"worker_num\" => 2, 'log_file' => '/dev/null',]);\n    $serv->on(\"WorkerStart\", function (Server $serv, $worker_id) use ($pm) {\n        if ($worker_id == 0)\n        {\n            swoole_set_process_name(WORKER_PROC_NAME);\n            $pm->wakeup();\n        }\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $reactorId, $data)\n    {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/last_time.phpt",
    "content": "--TEST--\nswoole_server: last time\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\Run(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $r = $cli->connect(TCP_SERVER_HOST, $pm->getFreePort(), 1);\n        Assert::assert($r);\n        $cli->send(\"test1\\r\\ntest2\\r\\n\");\n        $result = [json_decode($cli->recv(), true), json_decode($cli->recv(), true)];\n        Assert::lessThanEq($result[0]['php_time'] - $result[0]['last_dispatch_time'], 0.01);\n        Assert::greaterThanEq($result[1]['php_time'] - $result[1]['last_dispatch_time'], 0.3);\n        $cli->close();\n        echo \"SUCCESS\\n\";\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort());\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n        'open_eof_split' => true,\n        'package_eof' => \"\\r\\n\",\n    ]);\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $rid, $data) {\n        $info = $serv->getClientInfo($fd);\n        $info ['php_time'] = microtime(true);\n        $serv->send($fd, json_encode($info));\n        if (trim($data) == 'test1') {\n            usleep(300000);\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/length/00.phpt",
    "content": "--TEST--\nswoole_server/length: (length protocol) recv 100k packet\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire __DIR__ . '/../../include/api/swoole_server/TestServer.php';\n\nTestServer::$PKG_NUM = MAX_PACKET_NUM;\n\nclass PkgServer extends TestServer\n{\n    protected $show_lost_package = false;\n\n    function onReceive($serv, $fd, $reactor_id, $data)\n    {\n        $header = unpack('Nlen/Nindex/Nsid', substr($data, 0, 12));\n        if ($header['index'] % 1000 == 0) {\n            //echo \"#{$header['index']} recv package. sid={$header['sid']}, length=\" . strlen($data) . \", bytes={$this->recv_bytes}\\n\";\n        }\n        if ($header['index'] > self::$PKG_NUM) {\n            echo \"invalid index #{$header['index']}\\n\";\n        }\n        $this->index[$header['index']] = true;\n    }\n\n    function onWorkerStart($serv, $wid)\n    {\n        global $pm;\n        $pm->wakeup();\n    }\n}\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n        exit(\"connect failed\\n\");\n    }\n\n    $bytes = 0;\n    $pkg_bytes = 0;\n\n    for ($i = 0; $i < TestServer::$PKG_NUM; $i++) {\n        $len = rand(1000, 1024 * 128 - 8);\n        $sid = rand(10000, 99999);\n\n        $pkt = pack('NNN', $len + 8, $i, $sid);\n        $pkt .= str_repeat('A', $len);\n\n        $pkg_bytes += strlen($pkt);\n\n//        if ($i % 1000 == 0)\n//        {\n//            echo \"#{$i} send package. sid={$sid}, length=\" . ($len + 10) . \", total bytes={$pkg_bytes}\\n\";\n//        }\n        if (!$client->send($pkt)) {\n            break;\n        }\n        $bytes += strlen($pkt);\n    }\n\n    $recv = $client->recv();\n    echo $recv;\n    //echo \"send \".TestServer::$PKG_NUM.\" packet sucess, send $bytes bytes\\n\";\n    $client->close();\n\n    usleep(1);\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new PkgServer($pm->getFreePort(), true);\n    $serv->set([\n        'worker_num' => 1,\n        //'dispatch_mode'         => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'package_max_length' => 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n        'task_worker_num' => 0\n    ]);\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTREGEX--\nend\nTotal count=\\d+, bytes=\\d+\n"
  },
  {
    "path": "tests/swoole_server/length/01.phpt",
    "content": "--TEST--\nswoole_server/length: big packet\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\nif (!is_file('/proc/sys/net/core/wmem_max')) {\n    exit('skip not linux');\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$port = get_one_free_port();\n\nuse Swoole\\Server;\nuse SwooleTest\\ProcessManager;\nuse Swoole\\Atomic;\nuse Swoole\\Client;\n\n$max = file_get_contents('/proc/sys/net/core/wmem_max');\n$max = min(8 * 1024 * 1024, $max);\n\n$size = intval($max) * 2 - 32 - 4;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($port, $pm, $size) {\n    $cli = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $cli->connect(TCP_SERVER_HOST, $port, 1);\n    $data = str_repeat('A', $size);\n    $cli->send(pack('N', strlen($data)) . $data);\n    $recv_data = $cli->recv();\n    echo $recv_data;\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $port, $size) {\n    $serv = new Server(TCP_SERVER_HOST, $port, SWOOLE_PROCESS);\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'package_max_length' => 16 * 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n    ]);\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"receive\", function ($serv, $fd, $rid, $data) use ($size) {\n        Assert::assert(strlen($data) == $size + 4);\n        $serv->send($fd, \"OK\\n\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_server/length/02.phpt",
    "content": "--TEST--\nswoole_server/length: (length protocol) no body\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire __DIR__ . '/../../include/api/swoole_server/TestServer.php';\n\nTestServer::$PKG_NUM = MAX_PACKET_NUM;\n\nclass PkgServer_2 extends TestServer\n{\n    protected $show_lost_package = false;\n\n    function onReceive($serv, $fd, $reactor_id, $data)\n    {\n        static $index = 0;\n        $header = unpack('nlen', $data);\n        Assert::same(strlen($data), 2);\n        Assert::same($header['len'], 2);\n        if ($index % 1000 == 0) {\n            //echo \"#{$header['index']} recv package. sid={$header['sid']}, length=\" . strlen($data) . \", bytes={$this->recv_bytes}\\n\";\n        }\n        if ($index > self::$PKG_NUM) {\n            echo \"invalid index #{$index}\\n\";\n        }\n        $this->index[$index] = true;\n        $index++;\n    }\n\n    function onWorkerStart($serv, $wid)\n    {\n        global $pm;\n        $pm->wakeup();\n    }\n}\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n        exit(\"connect failed\\n\");\n    }\n\n    $bytes = 0;\n    $pkg_bytes = 0;\n\n    for ($i = 0; $i < TestServer::$PKG_NUM; $i++) {\n//        if ($i % 1000 == 0)\n//        {\n//            echo \"#{$i} send package. sid={$sid}, length=\" . ($len + 10) . \", total bytes={$pkg_bytes}\\n\";\n//        }\n        if (!$client->send(pack('n', 2))) {\n            break;\n        }\n        $bytes += 2;\n    }\n\n    $recv = $client->recv();\n    echo $recv;\n    //echo \"send \".TestServer::$PKG_NUM.\" packet sucess, send $bytes bytes\\n\";\n    $client->close();\n\n    usleep(1);\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new PkgServer_2($pm->getFreePort(), true);\n    $serv->set([\n        'worker_num' => 1,\n        //'dispatch_mode'         => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'package_max_length' => 1024 * 1024,\n        'package_length_type' => 'n',\n        'package_length_offset' => 0,\n        'package_body_offset' => 0,\n        'task_worker_num' => 0\n    ]);\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTREGEX--\nend\nTotal count=\\d+, bytes=\\d+\n"
  },
  {
    "path": "tests/swoole_server/length/03.phpt",
    "content": "--TEST--\nswoole_server/length: 8M packet\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$port = get_one_free_port();\n\n$size = 8 * 1024 * 1024;\n$_g_data = random_bytes($size);\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($port, $pm, $size, $_g_data) {\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $cli->connect(TCP_SERVER_HOST, $port, 1);\n    $cli->send(pack('N', strlen($_g_data)) . $_g_data);\n    $recv_data = $cli->recv();\n    echo $recv_data;\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $port, $size, $_g_data) {\n    $serv = new Server(TCP_SERVER_HOST, $port, SWOOLE_PROCESS);\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'package_max_length' => 16 * 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n    ]);\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"receive\", function ($serv, $fd, $rid, $data) use ($size, $_g_data) {\n        Assert::eq(strlen($data), $size + 4);\n        Assert::eq($_g_data, substr($data, 4));\n        $serv->send($fd, \"OK\\n\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_server/length/length_func.phpt",
    "content": "--TEST--\nswoole_server/length: package_length_func\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\n\nconst LEN = 3;\nconst COUNT = 12;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $cli->connect(TCP_SERVER_HOST, $pm->getFreePort(), 1);\n    $data = str_repeat('A', LEN * COUNT);\n    $cli->send($data);\n\n    $data = '';\n    while (1) {\n        $recv_data = $cli->recv();\n        if (!$recv_data) {\n            break;\n        }\n        $data .= $recv_data;\n        if (strlen($data) >= LEN * COUNT) {\n            break;\n        }\n    }\n    Assert::eq($data, str_repeat(\"OK\\n\", 12));\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'package_body_offset' => 4,\n        'package_length_func' => function(){\n            return 3;\n        }\n    ]);\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"receive\", function ($serv, $fd, $rid, $data) {\n        Assert::assert(strlen($data) == LEN);\n        $serv->send($fd, \"OK\\n\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/listen_fail.phpt",
    "content": "--TEST--\nswoole_server: listen fail\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nCo::set(['log_level' => SWOOLE_LOG_NONE]);\ntry {\n    $serv = new Swoole\\Server('192.0.0.1', 80, SWOOLE_PROCESS);\n} catch (Swoole\\Exception $e) {\n    Assert::same($e->getCode(), SOCKET_EADDRNOTAVAIL);\n    echo \"DONE\\n\";\n}\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/max_idle_time_1.phpt",
    "content": "--TEST--\nswoole_server: max_idle_time\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_extension_not_exist('sockets');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Atomic;\nuse Swoole\\Client;\n\ndefine('SOCK_FILE', __DIR__.'/server.sock');\n$pm = new SwooleTest\\ProcessManager;\n\n$time1 = new Atomic(0);\n$time2 = new Atomic(0);\n\n$pm->parentFunc = function ($pid) use ($pm, $time1, $time2) {\n    $client = new Client(SWOOLE_SOCK_UNIX_STREAM, SWOOLE_SOCK_SYNC);\n    if (!$client->connect(SOCK_FILE, 0, 0.5)) {\n        exit(\"connect failed\\n\");\n    }\n    $socket = $client->getSocket();\n    socket_set_option($socket, SOL_SOCKET, SO_SNDBUF, 65536);\n    socket_set_option($socket, SOL_SOCKET, SO_RCVBUF, 65536);\n    $client->send('hello world');\n    $s = microtime(true);\n    sleep(1);\n    usleep(200000);\n    Assert::greaterThanEq($time2->get() - $time1->get(), 1000);\n    $result = '';\n    while(true) {\n        $data = $client->recv();\n        if (empty($data)) {\n            break;\n        }\n        $result .= $data;\n    }\n    Assert::greaterThanEq(strlen($result), 8192);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $time1, $time2) {\n    $serv = new Server(SOCK_FILE, 0, SWOOLE_BASE, SWOOLE_SOCK_UNIX_STREAM);\n    $serv->set([\n        'worker_num' => 1,\n        'kernel_socket_send_buffer_size' => 65536,\n        'log_file' => '/dev/null',\n        'max_idle_time' => 1,\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('Receive', function ($serv, $fd, $tid, $data) use ($time1)  {\n        $time1->set(microtime(true) * 1000);\n        $serv->send($fd, str_repeat('A', 1024 * 1024 * 4));\n    });\n    $serv->on('close', function ($serv, $fd, $tid) use ($time2) {\n        $time2->set(microtime(true) * 1000);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/max_idle_time_2.phpt",
    "content": "--TEST--\nswoole_server: max_idle_time [recv]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_extension_not_exist('sockets');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Atomic;\nuse Swoole\\Client;\nuse Swoole\\Constant;\ndefine('SOCK_FILE', __DIR__.'/server.sock');\n$pm = new SwooleTest\\ProcessManager;\n\n$time1 = new Atomic(0);\n$time2 = new Atomic(0);\n\n$pm->parentFunc = function ($pid) use ($pm, $time1, $time2) {\n    $client = new Client(SWOOLE_SOCK_UNIX_STREAM, SWOOLE_SOCK_SYNC);\n    if (!$client->connect(SOCK_FILE, 0, 0.5)) {\n        exit(\"connect failed\\n\");\n    }\n    sleep(1);\n    usleep(200000);\n    Assert::greaterThanEq($time2->get() - $time1->get(), 1000);\n    $data = $client->recv();\n    Assert::isEmpty($data);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $time1, $time2) {\n    $serv = new Server(SOCK_FILE, 0, SWOOLE_BASE, SWOOLE_SOCK_UNIX_STREAM);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'max_idle_time' => 1,\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(Constant::EVENT_CONNECT, function ($serv, $fd, $tid) use ($time1) {\n        $time1->set(microtime(true) * 1000);\n    });\n    $serv->on('Receive', function ($serv, $fd, $tid, $data) use ($time1) {\n        $serv->send($fd, str_repeat('A', 1024 * 1024));\n    });\n    $serv->on(Constant::EVENT_CLOSE, function ($serv, $fd, $tid) use ($time2) {\n        $time2->set(microtime(true) * 1000);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/max_queued_bytes.phpt",
    "content": "--TEST--\nswoole_server: max_queued_bytes\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst N = 10 * 1024 * 1024;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->initFreePorts();\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n        echo \"FAILED\\n\";\n        $pm->kill();\n        return;\n    }\n    $bytes = 0;\n    while ($bytes < N) {\n        $write_n = $client->send(random_bytes(rand(1000, 80000)));\n        if ($write_n == false) {\n            break;\n        } else {\n            $bytes += $write_n;\n            phpt_echo(\"Client sent {$bytes} bytes\\n\");\n        }\n    }\n    Assert::assert($bytes > N);\n    $pm->wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'max_queued_bytes' => 1024 * 1024,\n    ]);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', static function ($serv, $fd, $reactor_id, $data) use ($pm) {\n        static $bytes;\n        $bytes += strlen($data);\n        phpt_echo(\"Server received {$bytes} bytes\\n\");\n        usleep(1000);\n        if ($bytes > N) {\n            $pm->wakeup();\n        }\n    });\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/max_request.phpt",
    "content": "--TEST--\nswoole_server: max_request\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$counter = new Swoole\\Atomic();\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $client = new Client(SWOOLE_SOCK_TCP);\n        $client->set([\n            \"open_eof_check\" => true,\n            \"open_eof_split\" => true,\n            \"package_eof\" => \"\\r\\n\\r\\n\",\n        ]);\n        $r = $client->connect('127.0.0.1', $pm->getFreePort(), -1);\n        if ($r === false) {\n            echo \"ERROR\";\n            exit;\n        }\n        for ($i = 0; $i < 4000; $i++) {\n            $data = \"PKG-$i\" . str_repeat('A', rand(100, 20000)) . \"\\r\\n\\r\\n\";\n            if ($client->send($data) === false) {\n                echo \"send error\\n\";\n                break;\n            }\n            $ret = $client->recv();\n            Assert::same(strlen($ret), strlen($data) + 8);\n        }\n        $client->close();\n    });\n\n    Event::wait();\n\n    global $counter;\n    Assert::assert($counter->get() > 10);\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        \"worker_num\" => 4,\n        'dispatch_mode' => 1,\n        \"open_eof_split\" => true,\n        \"package_eof\" => \"\\r\\n\\r\\n\",\n        'max_request' => 200,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on(\"WorkerStart\", function (Server $serv)  use ($pm)\n    {\n        global $counter;\n        $counter->add(1);\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $reactorId, $data)\n    {\n        $serv->send($fd, \"Server: $data\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/max_request_grace_disabled.phpt",
    "content": "--TEST--\nswoole_server: max_request_grace disabled\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function () use ($pm)\n{\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $client->set([\n        'open_eof_check' => true,\n        'package_eof' => \"\\n\",\n    ]);\n    Assert::assert($client->connect('127.0.0.1', $pm->getFreePort(), -1));\n    for ($i = 0; $i < 48; $i++) {\n        $client->send(\"request $i\\n\");\n        echo $client->recv();\n    }\n    $client->close();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num'        => 2,\n        'dispatch_mode'     => 1,\n        'max_request'       => 12,\n        'max_request_grace' => 0,\n        'open_eof_check'    => true,\n        'package_eof'       => \"\\n\",\n        'log_file'          => '/dev/null',\n    ]);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $count = 0;\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $reactorId, $data) use (&$count) {\n        $count++;\n        $serv->send($fd, \"Worker $serv->worker_id served $count request(s) since start\\n\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nWorker 0 served 1 request(s) since start\nWorker 1 served 1 request(s) since start\nWorker 0 served 2 request(s) since start\nWorker 1 served 2 request(s) since start\nWorker 0 served 3 request(s) since start\nWorker 1 served 3 request(s) since start\nWorker 0 served 4 request(s) since start\nWorker 1 served 4 request(s) since start\nWorker 0 served 5 request(s) since start\nWorker 1 served 5 request(s) since start\nWorker 0 served 6 request(s) since start\nWorker 1 served 6 request(s) since start\nWorker 0 served 7 request(s) since start\nWorker 1 served 7 request(s) since start\nWorker 0 served 8 request(s) since start\nWorker 1 served 8 request(s) since start\nWorker 0 served 9 request(s) since start\nWorker 1 served 9 request(s) since start\nWorker 0 served 10 request(s) since start\nWorker 1 served 10 request(s) since start\nWorker 0 served 11 request(s) since start\nWorker 1 served 11 request(s) since start\nWorker 0 served 12 request(s) since start\nWorker 1 served 12 request(s) since start\nWorker 0 served 1 request(s) since start\nWorker 1 served 1 request(s) since start\nWorker 0 served 2 request(s) since start\nWorker 1 served 2 request(s) since start\nWorker 0 served 3 request(s) since start\nWorker 1 served 3 request(s) since start\nWorker 0 served 4 request(s) since start\nWorker 1 served 4 request(s) since start\nWorker 0 served 5 request(s) since start\nWorker 1 served 5 request(s) since start\nWorker 0 served 6 request(s) since start\nWorker 1 served 6 request(s) since start\nWorker 0 served 7 request(s) since start\nWorker 1 served 7 request(s) since start\nWorker 0 served 8 request(s) since start\nWorker 1 served 8 request(s) since start\nWorker 0 served 9 request(s) since start\nWorker 1 served 9 request(s) since start\nWorker 0 served 10 request(s) since start\nWorker 1 served 10 request(s) since start\nWorker 0 served 11 request(s) since start\nWorker 1 served 11 request(s) since start\nWorker 0 served 12 request(s) since start\nWorker 1 served 12 request(s) since start\n"
  },
  {
    "path": "tests/swoole_server/max_request_grace_enabled.phpt",
    "content": "--TEST--\nswoole_server: max_request_grace enabled\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function () use ($pm)\n{\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $client->set([\n        'open_eof_check' => true,\n        'package_eof' => \"\\n\",\n    ]);\n    Assert::assert($client->connect('127.0.0.1', $pm->getFreePort(), -1));\n    for ($i = 0; $i < 16; $i++) {\n        $client->send(\"request $i\\n\");\n        echo $client->recv();\n    }\n    $client->close();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num'        => 2,\n        'dispatch_mode'     => 1,\n        'max_request'       => 4,\n        'max_request_grace' => 1000000000,\n        'open_eof_check'    => true,\n        'package_eof'       => \"\\n\",\n        'log_file'          => '/dev/null',\n    ]);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $count = 0;\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $reactorId, $data) use (&$count) {\n        $count++;\n        $serv->send($fd, \"Worker $serv->worker_id served $count request(s) since start\\n\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nWorker 0 served 1 request(s) since start\nWorker 1 served 1 request(s) since start\nWorker 0 served 2 request(s) since start\nWorker 1 served 2 request(s) since start\nWorker 0 served 3 request(s) since start\nWorker 1 served 3 request(s) since start\nWorker 0 served 4 request(s) since start\nWorker 1 served 4 request(s) since start\nWorker 0 served 5 request(s) since start\nWorker 1 served 5 request(s) since start\nWorker 0 served 6 request(s) since start\nWorker 1 served 6 request(s) since start\nWorker 0 served 7 request(s) since start\nWorker 1 served 7 request(s) since start\nWorker 0 served 8 request(s) since start\nWorker 1 served 8 request(s) since start\n"
  },
  {
    "path": "tests/swoole_server/max_request_threshold.phpt",
    "content": "--TEST--\nswoole_server: max_request threshold\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function () use ($pm)\n{\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $client->set([\n        'open_eof_check' => true,\n        'package_eof' => \"\\n\",\n    ]);\n    Assert::assert($client->connect('127.0.0.1', $pm->getFreePort(), -1));\n    for ($i = 0; $i < 40; $i++) {\n        $client->send(\"request $i\\n\");\n        echo $client->recv();\n    }\n    $client->close();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num'        => 2,\n        'dispatch_mode'     => 1,\n        'max_request'       => 10,\n        'open_eof_check'    => true,\n        'package_eof'       => \"\\n\",\n        'log_file'          => '/dev/null',\n    ]);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $count = 0;\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $reactorId, $data) use (&$count) {\n        $count++;\n        $serv->send($fd, \"Worker $serv->worker_id served $count request(s) since start\\n\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nWorker 0 served 1 request(s) since start\nWorker 1 served 1 request(s) since start\nWorker 0 served 2 request(s) since start\nWorker 1 served 2 request(s) since start\nWorker 0 served 3 request(s) since start\nWorker 1 served 3 request(s) since start\nWorker 0 served 4 request(s) since start\nWorker 1 served 4 request(s) since start\nWorker 0 served 5 request(s) since start\nWorker 1 served 5 request(s) since start\nWorker 0 served 6 request(s) since start\nWorker 1 served 6 request(s) since start\nWorker 0 served 7 request(s) since start\nWorker 1 served 7 request(s) since start\nWorker 0 served 8 request(s) since start\nWorker 1 served 8 request(s) since start\nWorker 0 served 9 request(s) since start\nWorker 1 served 9 request(s) since start\nWorker 0 served 10 request(s) since start\nWorker 1 served 10 request(s) since start\nWorker 0 served 1 request(s) since start\nWorker 1 served 1 request(s) since start\nWorker 0 served 2 request(s) since start\nWorker 1 served 2 request(s) since start\nWorker 0 served 3 request(s) since start\nWorker 1 served 3 request(s) since start\nWorker 0 served 4 request(s) since start\nWorker 1 served 4 request(s) since start\nWorker 0 served 5 request(s) since start\nWorker 1 served 5 request(s) since start\nWorker 0 served 6 request(s) since start\nWorker 1 served 6 request(s) since start\nWorker 0 served 7 request(s) since start\nWorker 1 served 7 request(s) since start\nWorker 0 served 8 request(s) since start\nWorker 1 served 8 request(s) since start\nWorker 0 served 9 request(s) since start\nWorker 1 served 9 request(s) since start\nWorker 0 served 10 request(s) since start\nWorker 1 served 10 request(s) since start\n"
  },
  {
    "path": "tests/swoole_server/memory_leak/length.phpt",
    "content": "--TEST--\nswoole_server/memory_leak: length\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Coroutine\\Client;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$counter1 = new Swoole\\Atomic\\Long();\n$counter2 = new Swoole\\Atomic\\Long();\n\n$n = MAX_REQUESTS;\n$chunks = [];\n$total = 0;\nwhile ($n--) {\n    $len = random_int(4 * 1024, 1024 * 1024);\n    $pkt = pack('N', $len) . random_bytes($len);\n    $chunks[] = $pkt;\n    $total += strlen($pkt);\n}\n\n$pm->setWaitTimeout(-1);\n\n$pm->parentFunc = function ($pid) use ($pm, $chunks, $total) {\n    $clients = [];\n    for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) {\n        go(function () use ($pm, $i, $chunks, &$clients, $total) {\n            $cli = new Client(SWOOLE_SOCK_TCP);\n            $cli->set([\n                'open_length_check' => true,\n                'package_max_length' => 4 * 1024 * 1024,\n                'package_length_type' => 'N',\n                'package_length_offset' => 0,\n                'package_body_offset' => 4,\n            ]);\n            if ($cli->connect('127.0.0.1', $pm->getFreePort(), 100) == false) {\n                echo \"ERROR\\n\";\n                return;\n            }\n            $count = 0;\n            foreach ($chunks as $data) {\n                $count += $cli->send($data);\n                usleep(10);\n            }\n            Assert::eq($count, $total);\n            $clients[] = $cli;\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->wait();\n    $pm->kill();\n};\n\nphpt_var_dump(\n    'total all: ' . number_format(MAX_CONCURRENCY_MID * $total) .\n    ', n packets: ' . MAX_REQUESTS .\n    ', n clients: ' . MAX_CONCURRENCY_MID .\n    ', total: ' . number_format($total)\n);\n\n$pm->childFunc = function () use ($pm, $counter1, $total, $counter2) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set(array(\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'package_max_length' => 4 * 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('connect', function (Server $serv, $fd, $rid) {\n        $GLOBALS['bytes_' . $fd] = 0;\n        $GLOBALS['count_' . $fd] = 0;\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($pm, $counter1, $total, $counter2) {\n        if ($counter1->get() == 0) {\n            $GLOBALS['memory_usage_1'] = memory_get_usage();\n        }\n        $counter1->add(strlen($data));\n        $counter2->add();\n        $GLOBALS['bytes_' . $fd] += strlen($data);\n        $GLOBALS['count_' . $fd]++;\n\n        if ($GLOBALS['count_' . $fd] == MAX_REQUESTS) {\n            phpt_var_dump(\n                'bytes: ' . number_format($counter1->get()) .\n                ', count: ' . $counter2->get() .\n                ', data: ' . strlen($data) .\n                ', client bytes: ' . number_format($GLOBALS['bytes_' . $fd]) .\n                ', client count: ' . $GLOBALS['count_' . $fd]\n            );\n        }\n\n        if ($counter1->get() == MAX_CONCURRENCY_MID * $total) {\n            $pm->wakeup();\n        }\n    });\n    $serv->on('close', function (Server $serv, $fd, $rid) {\n    });\n    $serv->on('WorkerStop', function () use ($total, $counter2) {\n        $GLOBALS['memory_usage_2'] = memory_get_usage();\n        Assert::lessThan($GLOBALS['memory_usage_2'] - $GLOBALS['memory_usage_1'], 8192);\n        Assert::eq($counter2->get(), MAX_CONCURRENCY_MID * MAX_REQUESTS);\n        echo \"DONE\\n\";\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/memory_leak/pipe_message.phpt",
    "content": "--TEST--\nswoole_server/memory_leak: task\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Coroutine\\Client;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$counter1 = new Swoole\\Atomic\\Long();\n$counter2 = new Swoole\\Atomic\\Long();\n\n$n = MAX_REQUESTS;\n$chunks = [];\n$total = 0;\nwhile ($n--) {\n    $len = random_int(4 * 1024, 1024 * 1024);\n    $chunks[] = random_bytes($len);\n    $total += $len;\n}\n\n$pm->setWaitTimeout(-1);\n\n$pm->parentFunc = function ($pid) use ($pm, $chunks) {\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($counter1, $counter2, $pm, $total, $chunks) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set(array(\n        'worker_num' => 2,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv, $wid) use ($pm, $chunks) {\n        $GLOBALS['memory_usage_1'] = memory_get_usage();\n        foreach ($chunks as $ch) {\n            Assert::greaterThan($serv->sendMessage($ch, 1 - $wid), 0);\n            usleep(10);\n        }\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $_data) use ($chunks) {\n\n    });\n    $serv->on('pipeMessage', function (Server $serv, $wid, $data) use ($counter2, $counter1, $pm, $total, $chunks) {\n        $counter1->add();\n        $counter2->add(strlen($data));\n        if ($counter2->get() == $total * 2) {\n            $pm->wakeup();\n        }\n    });\n    $serv->on('WorkerStop', function (Server $serv) use ($counter2, $total) {\n        $GLOBALS['memory_usage_2'] = memory_get_usage();\n        Assert::lessThan($GLOBALS['memory_usage_2'] - $GLOBALS['memory_usage_1'], 8192);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/memory_leak/task.phpt",
    "content": "--TEST--\nswoole_server/memory_leak: task\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Coroutine\\Client;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$GLOBALS['counter1'] = 0;\n$GLOBALS['counter2'] = 0;\n$GLOBALS['atomic'] = new Swoole\\Atomic;\n\n$n = MAX_REQUESTS;\n$chunks = [];\n$total = 0;\nwhile ($n--) {\n    $len = random_int(4 * 1024, 1024 * 1024);\n    $chunks[] = random_bytes($len);\n    $total += $len;\n}\n\n$pm->setWaitTimeout(-1);\n\n$pm->parentFunc = function ($pid) use ($pm, $chunks) {\n    go(function () use ($pm, $chunks) {\n        $cli = new Client(SWOOLE_SOCK_TCP);\n        if ($cli->connect('127.0.0.1', $pm->getFreePort(), 100) == false) {\n            echo \"ERROR\\n\";\n            return;\n        }\n        $cli->send(\"start\\n\");\n    });\n    Swoole\\Event::wait();\n    $pm->wait();\n    $pm->kill();\n};\n\n$GLOBALS['test_fn'] = function ($taskId, $data, $chunks) {\n    if ($GLOBALS['counter1'] == 0) {\n        $GLOBALS['memory_usage_1'] = memory_get_usage();\n    }\n    $GLOBALS['counter1']++;\n    $GLOBALS['counter2'] += (strlen($data));\n    Assert::eq($chunks[$taskId], $data);\n};\n\n$pm->childFunc = function () use ($pm, $total, $chunks) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set(array(\n        'worker_num' => 1,\n        'task_worker_num' => 1,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on('WorkerStart', function (Server $serv, $wid) use ($pm) {\n        if ($wid == 0) {\n            $pm->wakeup();\n        }\n        $GLOBALS['atomic']->add();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $_data) use ($chunks) {\n        foreach ($chunks as $ch) {\n            Assert::greaterThanEq($serv->task($ch), 0);\n            usleep(100);\n        }\n    });\n    $serv->on('finish', function (Server $serv, $taskId, $data) use ($pm, $total, $chunks) {\n        $GLOBALS['test_fn']($taskId, $data, $chunks);\n        if ($GLOBALS['counter2'] == $total) {\n            $pm->wakeup();\n        }\n    });\n    $serv->on('task', function (Server $serv, $taskId, $srcWorkerId, $data) use ($pm, $total, $chunks) {\n        $GLOBALS['test_fn']($taskId, $data, $chunks);\n        return $data;\n    });\n    $serv->on('WorkerStop', function (Server $serv) use ($total) {\n        $GLOBALS['memory_usage_2'] = memory_get_usage();\n        Assert::lessThan($GLOBALS['memory_usage_2'] - $GLOBALS['memory_usage_1'], 8192);\n        Assert::eq($GLOBALS['counter2'], $total);\n        $GLOBALS['atomic']->add();\n        echo \"DONE\\n\";\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\nAssert::eq($GLOBALS['atomic']->get(), 4);\n?>\n--EXPECT--\nDONE\nDONE\n"
  },
  {
    "path": "tests/swoole_server/memory_leak/tcp.phpt",
    "content": "--TEST--\nswoole_server/memory_leak: tcp\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Atomic\\Long;\nuse Swoole\\Server;\nuse Swoole\\Coroutine\\Client;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$counter = new Long();\n$n = MAX_REQUESTS;\n$chunks = [];\n$total = 0;\nwhile ($n--) {\n    $len = random_int(4 * 1024, 1024 * 1024);\n    $chunks[] = random_bytes($len);\n    $total += $len;\n}\n\n$pm->parentFunc = function ($pid) use ($pm, $chunks) {\n    $clients = [];\n    for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) {\n        go(function () use ($pm, $i, &$total, $chunks, &$clients) {\n            $cli = new Client(SWOOLE_SOCK_TCP);\n            if ($cli->connect('127.0.0.1', $pm->getFreePort(), 100) == false) {\n                echo \"ERROR\\n\";\n                return;\n            }\n            foreach ($chunks as $data) {\n                $cli->send($data);\n                usleep(100);\n            }\n            $clients[] = $cli;\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $counter, $total) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set(array(\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('connect', function (Server $serv, $fd, $rid) {\n\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($pm, $counter, $total) {\n        if ($counter->get() == 0) {\n            $GLOBALS['memory_usage_1'] = memory_get_usage();\n        }\n        if ($counter->add(strlen($data)) == MAX_CONCURRENCY_MID * $total) {\n            $pm->wakeup();\n        }\n    });\n    $serv->on('close', function (Server $serv, $fd, $rid) {\n    });\n    $serv->on('WorkerStop', function () use ($total) {\n        $GLOBALS['memory_usage_2'] = memory_get_usage();\n        Assert::lessThan($GLOBALS['memory_usage_2'] - $GLOBALS['memory_usage_1'], 8192);\n        echo \"DONE\\n\";\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/mqtt/length_offset.phpt",
    "content": "--TEST--\nswoole_server/mqtt: length_offset\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse SwooleTest\\MQTT\\Helper;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Co\\Client(SWOOLE_SOCK_TCP);\n        Assert::assert($client->connect('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($client->send(Helper::encodePublish([\n            'cmd' => 3,\n            'topic' => 'swoole/mqtt/test',\n            'content' => '{\"name\":\"swoole\", \"type\":\"mqtt\", \"data\":'. str_repeat(\"swoole\", 100) .'}']))\n        );\n        echo $client->recv();\n        $client->close();\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n        'open_mqtt_protocol' => 1,\n    ]);\n\n    $server->on('receive', function (Swoole\\Server $serv, int $fd, int $rid, string $data) {\n        $header = Helper::getHeader($data);\n        Assert::eq($header['type'], 3);\n        Assert::eq(strlen($data), 662);\n        $serv->send($fd, strlen($data));\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n662\n"
  },
  {
    "path": "tests/swoole_server/mqtt/recv_fail.phpt",
    "content": "--TEST--\nswoole_server/mqtt: recv_fail\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse SwooleTest\\MQTT\\Helper;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Co\\Client(SWOOLE_SOCK_TCP);\n        $client->set(['open_mqtt_protocol' => true]);\n        Assert::assert($client->connect('127.0.0.1', $pm->getFreePort()));\n        $buffer = Helper::encodePing(12); // PINGREQ\n//        $client->send($buffer);\n        $client->send($buffer[0]);\n        sleep(1);\n        $client->send($buffer[1]);\n        $response = $client->recv();\n        $header = Helper::getHeader($response);\n        var_dump($header);\n        Assert::eq($header['type'], 13); // PINGRESP\n        $client->close();\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n        'open_mqtt_protocol' => 1,\n    ]);\n\n    $server->on('receive', function (Swoole\\Server $serv, int $fd, int $rid, string $data) {\n        $header = Helper::getHeader($data);\n        Assert::eq($header['type'], 12);\n        $serv->send($fd, Helper::encodePing(13));\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\narray(4) {\n  [\"type\"]=>\n  int(13)\n  [\"dup\"]=>\n  int(0)\n  [\"qos\"]=>\n  int(0)\n  [\"retain\"]=>\n  int(0)\n}\n"
  },
  {
    "path": "tests/swoole_server/mqtt/send_big_pack.phpt",
    "content": "--TEST--\nswoole_server/mqtt: send_big_pack\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse SwooleTest\\MQTT\\Helper;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Co\\Client(SWOOLE_SOCK_TCP);\n        $client->set(['open_mqtt_protocol' => true, 'package_max_length' => 5 * 1024 *1024]);\n        Assert::assert($client->connect('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($client->send(Helper::encodePublish([\n            'cmd' => 3,\n            'topic' => 'swoole/mqtt/test',\n            'content' => '{\"name\":\"swoole\", \"type\":\"mqtt\", \"data\":'. str_repeat(\"sw\", 2 * 1024 * 1024 ) .'}']))\n        );\n        var_dump(Helper::getHeader($client->recv()));\n        $client->close();\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n        'open_mqtt_protocol' => 1,\n        'package_max_length' => 5 * 1024 *1024\n    ]);\n\n    $server->on('receive', function (Swoole\\Server $serv, int $fd, int $rid, string $data) {\n        $header = Helper::getHeader($data);\n        Assert::eq($header['type'], 3);\n        Assert::eq(strlen($data), 4194368);\n        $serv->send($fd, Helper::encodePing(13));\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\narray(4) {\n  [\"type\"]=>\n  int(13)\n  [\"dup\"]=>\n  int(0)\n  [\"qos\"]=>\n  int(0)\n  [\"retain\"]=>\n  int(0)\n}\n"
  },
  {
    "path": "tests/swoole_server/new_twice.phpt",
    "content": "--TEST--\nswoole_server: new twice\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $fn = function () use ($pm) {\n        $cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n        $cli->connect('127.0.0.1', $pm->getFreePort(), 0.5) or die(\"ERROR\");\n        $cli->send(\"signal\") or die(\"ERROR\");\n    };\n    $fn();\n    $pm->wait();\n    $fn();\n};\n\n$pm->childFunc = function () use ($pm) {\n    ini_set('swoole.display_errors', 'Off');\n    $n = 2;\n    while ($n--) {\n        $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n        $serv->set(array(\n            \"worker_num\" => 1,\n            'enable_coroutine' => false,\n            'log_file' => '/dev/null',\n        ));\n        $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n            echo \"WorkerStart\\n\";\n            $pm->wakeup();\n        });\n        $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n            $serv->shutdown();\n        });\n        $serv->start();\n    }\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nWorkerStart\nWorkerStart\n"
  },
  {
    "path": "tests/swoole_server/object/event.phpt",
    "content": "--TEST--\nswoole_server/object: event object\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Server\\Event;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            echo \"Over flow. errno=\" . $client->errCode;\n            die(\"\\n\");\n        }\n\n        $data = base64_encode(random_bytes(rand(1024, 8192))) . \"\\r\\n\\r\\n\";;\n        $client->send($data);\n        $recv_data = $client->recv();\n        Assert::assert($recv_data);\n        $json = json_decode($recv_data);\n        Assert::eq($json->data, $data);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set(\n        array(\n            \"worker_num\" => 1,\n            'event_object' => true,\n            'log_file' => '/dev/null',\n        )\n    );\n    $serv->on(\n        'WorkerStart',\n        function (Server $serv) use ($pm) {\n            $pm->wakeup();\n        }\n    );\n    $serv->on(\n        'Connect',\n        function (Server $serv, Event $object) {\n            Assert::eq($object->fd, 1);\n        }\n    );\n    $serv->on(\n        'Close',\n        function (Server $serv, Event $object) {\n            Assert::eq($object->fd, 1);\n        }\n    );\n    $serv->on(\n        'receive',\n        function (Server $serv, Event $object) {\n            $serv->send($object->fd, json_encode(['worker' => $serv->getWorkerId(), 'data' => $object->data]));\n        }\n    );\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/object/getManagerPid.phpt",
    "content": "--TEST--\nswoole_server/object: getMasterPid\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_function_not_exist('posix_getpid');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $json = json_decode(httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\"));\n        Assert::assert($json->result);\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $atomic = new \\Swoole\\Atomic(0);\n    $serv = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        'log_level' => SWOOLE_LOG_ERROR,\n    ));\n    $serv->on(\\Swoole\\Constant::EVENT_MANAGER_START, function (Swoole\\Server $serv) use ($pm, $atomic) {\n        $pm->wakeup();\n        $atomic->set(posix_getpid());\n    });\n    $serv->on('Request', function ($req, $resp) use ($serv, $atomic) {\n        $resp->end(json_encode(['result' => $atomic->get() == $serv->getManagerPid()]));\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/object/getMasterPid.phpt",
    "content": "--TEST--\nswoole_server/object: getMasterPid\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_function_not_exist('posix_getpid');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $json = json_decode(httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\"));\n        Assert::assert($json->result);\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $atomic = new \\Swoole\\Atomic(0);\n    $serv = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        'log_level' => SWOOLE_LOG_ERROR,\n    ));\n    $serv->on(\"start\", function (Swoole\\Server $serv) use ($pm, $atomic) {\n        $pm->wakeup();\n        $atomic->set(posix_getpid());\n    });\n    $serv->on('Request', function ($req, $resp) use ($serv, $atomic) {\n        $resp->end(json_encode(['result' => $atomic->get() == $serv->getMasterPid()]));\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/object/getWorkerId.phpt",
    "content": "--TEST--\nswoole_server/object: getWorkerId()\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $json = json_decode(httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\"));\n        Assert::assert($json->result);\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        'log_level' => SWOOLE_LOG_ERROR,\n    ));\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv, $workerId) use ($pm) {\n        $pm->wakeup();\n        $GLOBALS['pid'] = $workerId;\n    });\n    $serv->on('Request', function ($req, $resp) use ($serv) {\n        $resp->end(json_encode(['result' => $GLOBALS['pid'] == $serv->getWorkerId()]));\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/object/getWorkerPid.phpt",
    "content": "--TEST--\nswoole_server/object: getWorkerPid()\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $json = json_decode(httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\"));\n        Assert::assert($json->result);\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        'log_level' => SWOOLE_LOG_ERROR,\n    ));\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv, $workerId) use ($pm) {\n        $pm->wakeup();\n        $GLOBALS['pid'] = posix_getpid();\n    });\n    $serv->on('Request', function ($req, $resp) use ($serv) {\n        $resp->end(json_encode(['result' => $GLOBALS['pid'] == $serv->getWorkerPid()]));\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/object/packet.phpt",
    "content": "--TEST--\nswoole_server/object: packet object\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Server;\nuse Swoole\\Server\\Packet;\nuse SwooleTest\\ProcessManager;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager();\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        $client = new Client(SWOOLE_SOCK_UDP);\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            echo 'Over flow. errno=' . $client->errCode;\n            exit(\"\\n\");\n        }\n\n        $data = base64_encode(random_bytes(rand(1024, DGRAM_MAX_SIZE))) . \"\\r\\n\\r\\n\";\n        $client->send($data);\n        $recv_data = $client->recv();\n        Assert::eq($recv_data, $data);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP);\n    $serv->set(\n        [\n            'worker_num' => 1,\n            'event_object' => true,\n            'log_file' => '/dev/null',\n        ]\n    );\n    $serv->on(\n        'WorkerStart',\n        function (Server $serv) use ($pm) {\n            $pm->wakeup();\n        }\n    );\n    $serv->on(\n        'packet',\n        function (Server $serv, Packet $object) {\n            $serv->sendto($object->address, $object->port, $object->data, $object->server_socket);\n        }\n    );\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/object/pipe_message.phpt",
    "content": "--TEST--\nswoole_server/object: pipe message\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Server\\Packet;\nuse Swoole\\Server\\PipeMessage;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_UDP);\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            echo \"Over flow. errno=\" . $client->errCode;\n            die(\"\\n\");\n        }\n\n        $data = base64_encode(random_bytes(rand(1024, 8192))) . \"\\r\\n\\r\\n\";;\n        $client->send($data);\n        $recv_data = $client->recv();\n        Assert::eq($recv_data, $data);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_UDP);\n    $serv->set(\n        array(\n            \"worker_num\" => 2,\n            'event_object' => true,\n            'log_file' => '/dev/null',\n        )\n    );\n    $serv->on(\n        'WorkerStart',\n        function (Server $serv) use ($pm) {\n            $pm->wakeup();\n        }\n    );\n\n    $serv->on('pipeMessage', function (Server $serv, PipeMessage $msg) {\n        Assert::eq($msg->worker_id, 1 - $serv->getWorkerId());\n        Assert::eq($msg->source_worker_id, 1 - $serv->getWorkerId());\n        $object = $msg->data;\n        $serv->sendto($object->address, $object->port, $object->data, $object->server_socket);\n    });\n\n    $serv->on(\n        'packet',\n        function (Server $serv, Packet $object) {\n            $serv->sendMessage($object, 1 - $serv->getWorkerId());\n        }\n    );\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/object/status_info.phpt",
    "content": "--TEST--\nswoole_server/object: status info\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Constant;\nuse Swoole\\Event;\nuse Swoole\\Process;\nuse Swoole\\Server;\nuse Swoole\\Client;\nuse Swoole\\Server\\StatusInfo;\nuse Swoole\\Timer;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    $cli = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $cli->connect('127.0.0.1', $pm->getFreePort(), 10) or die(\"ERROR\");\n    $cli->send(\"task-01\") or die(\"ERROR\");\n    Assert::same($cli->recv(), \"hello world\");\n    $cli->close();\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n        'event_object' => true,\n        'enable_coroutine' => false,\n    ]);\n\n    $serv->on(\"ManagerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n        Timer::after(100, function () use ($serv) {\n            Process::kill($serv->getWorkerPid(0), SIGKILL);\n        });\n        Timer::after(200, function () use ($serv) {\n            $serv->sendMessage(\"exit\", 0);\n            Timer::after(100, function () use ($serv) {\n                $serv->send(1, 'hello world');\n            });\n        });\n    });\n\n    $serv->on(Constant::EVENT_PIPE_MESSAGE, function (Server $serv, $msg) {\n        if ($msg->data == 'exit') {\n            throw new RuntimeException(\"error\");\n        }\n    });\n\n    $serv->on(\"Receive\", function (Server $serv, $event) {\n\n    });\n\n    $serv->on(Constant::EVENT_WORKER_ERROR, function (Server $serv, StatusInfo $info) {\n        static $count = 0;\n        $count++;\n        if ($count == 1) {\n            Assert::eq($info->signal, SIGKILL);\n            Assert::eq($info->exit_code, 0);\n        } elseif ($count == 2) {\n            Assert::eq($info->signal, 0);\n            Assert::eq($info->exit_code, 255);\n        }\n    });\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n\nFatal error: Uncaught RuntimeException: error in %s:%d\nStack trace:\n#0 [internal function]: {closure%S}(Object(Swoole\\Server), Object(Swoole\\Server\\PipeMessage))\n#1 %s(%d): Swoole\\Server->start()\n#2 [internal function]: {closure%S}()\n#3 %s(%d): call_user_func(Object(Closure))\n#4 %s(%d): SwooleTest\\ProcessManager->runChildFunc()\n#5 [internal function]: SwooleTest\\ProcessManager->%s(Object(Swoole\\Process))\n#6 %s(%d): Swoole\\Process->start()\n#7 %s(%d): SwooleTest\\ProcessManager->run()\n#8 {main}\n  thrown in %s on line %d\nDONE\n"
  },
  {
    "path": "tests/swoole_server/object/task_result.phpt",
    "content": "--TEST--\nswoole_server/object: task result\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Client;\nuse Swoole\\Server\\TaskResult;\n\n$pm = new SwooleTest\\ProcessManager;\n\nconst N = 12;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $cli = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $cli->connect('127.0.0.1', $pm->getFreePort(), 10) or die(\"ERROR\");\n    $cli->send(\"task-01\") or die(\"ERROR\");\n    Assert::same($cli->recv(), \"hello world\");\n    $cli->close();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'log_level' => SWOOLE_LOG_NOTICE,\n        'task_worker_num' => 4,\n        'worker_num' => 1,\n        'event_object' => true,\n        'enable_coroutine' => false,\n    ]);\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('receive', function (Server $server, $object) {\n        $n = N;\n        while ($n--) {\n            $server->task($object);\n        }\n    });\n\n    $server->on('task', function ($server, $task) {\n        $server->finish(['data' => str_repeat('A', 8000), 'task' => $task->data]);\n    });\n\n    $server->on('finish', function ($server, $result) {\n        static $count = 0;\n        $count++;\n        if ($count == N) {\n            $server->send($result->data['task']->fd, 'hello world');\n        }\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_server/onReload.phpt",
    "content": "--TEST--\nswoole_server: server beforeRelaod and afterReload event\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nerror_reporting(0);\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Atomic;\n\n$WorkerStartAtomic = new Atomic(0);\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm,$argv) {\n    sleep(2);\n    $script_name = $argv[0];\n    $ret = shell_exec(\"ps aux | grep $script_name | grep -v 'grep'\");\n    Assert::assert($ret != \"\");\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm, $WorkerStartAtomic) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'log_file' => TEST_LOG_FILE,\n        \"worker_num\" => 2,\n        \"task_worker_num\" => 2,\n        \"max_wait_time\" => 1,\n    ]);\n    $serv->on(\"BeforeReload\", function (Server $serv) {\n        var_dump(\"BeforeReload\");\n    });\n    $serv->on(\"AfterReload\", function (Server $serv) {\n        var_dump(\"AfterReload\");\n    });\n    $serv->on(\"WorkerStart\", function (Server $serv, $worker_id) use ($pm, $WorkerStartAtomic) {\n        $WorkerStartAtomic->add(1);\n        if ($WorkerStartAtomic->get() === 4) {\n            $serv->reload();\n            $pm->wakeup();\n        }\n    });\n    $serv->on('Receive', function ($serv, $fd, $tid, $data) {\n    });\n    $serv->on('Task', function ($serv,$task_id, $reactor_id, $params) {\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nstring(12) \"BeforeReload\"\nstring(11) \"AfterReload\"\nDONE\n"
  },
  {
    "path": "tests/swoole_server/parse_option_to_size.phpt",
    "content": "--TEST--\nswoole_server: parse option value to size\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_php_version_lower_than('8.2.0');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$server = new Server('127.0.0.1', 0);\n$server->set([\n    'buffer_output_size' => '2M',\n]);\n$server->set([\n    'buffer_output_size' => 2 * 1024 * 1024,\n]);\n$server->set([\n    'buffer_output_size' => 'xxx--2M',\n]);\n?>\n--EXPECTF--\nFatal error: Swoole\\Server::set(): failed to parse 'xxx--2M' to size, Error: Invalid quantity \"xxx--2M\": no valid leading digits, interpreting as \"0\" for backwards compatibility in %s on line %d\n--EXPECTF_85--\nFatal error: Swoole\\Server::set(): failed to parse 'xxx--2M' to size, Error: Invalid quantity \"xxx--2M\": no valid leading digits, interpreting as \"0\" for backwards compatibility in %s on line %d\nStack trace:\n#0 %s(%d): Swoole\\Server->set(Array)\n#1 {main}\n"
  },
  {
    "path": "tests/swoole_server/pid_file.phpt",
    "content": "--TEST--\nswoole_server: pid_file\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nconst PID_FILE = __DIR__ . '/test.pid';\n$pm = new SwooleTest\\ProcessManager;\n\nuse Swoole\\Server;\n\n$pm->parentFunc = function ($pid) {\n    Assert::assert(is_file(PID_FILE));\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm) {\n    ini_set('swoole.display_errors', 'Off');\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        'worker_num' => 1,\n        'pid_file' => PID_FILE,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on(\"Start\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\nclearstatcache();\nAssert::assert(!is_file(PID_FILE));\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/protect.phpt",
    "content": "--TEST--\nswoole_server: protect\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/opcode_server.php\";\n$port = get_one_free_port();\n\nstart_server($simple_tcp_server, TCP_SERVER_HOST, $port);\n\n$timer = suicide(2000);\nusleep(500 * 1000);\n\nmakeCoTcpClient(TCP_SERVER_HOST, $port, function (Client $cli) {\n    $r = $cli->send(opcode_encode(\"protect\", [2]));\n    Assert::assert($r !== false);\n}, function (Client $cli, $recv) use ($timer) {\n    list($op, $data) = opcode_decode($recv);\n    Assert::true($data);\n    $cli->close();\n    Timer::clear($timer);\n    echo \"SUCCESS\";\n});\n\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/protect_false.phpt",
    "content": "--TEST--\nswoole_server: protect($fd, false)\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/opcode_server.php\";\n$port = get_one_free_port();\n\nstart_server($simple_tcp_server, TCP_SERVER_HOST, $port);\n\n$timer = suicide(2000);\nusleep(500 * 1000);\n\nmakeCoTcpClient(TCP_SERVER_HOST, $port, function (Client $cli) {\n    $r = $cli->send(opcode_encode(\"protect\", [2, false]));\n    Assert::assert($r !== false);\n}, function (Client $cli, $recv) use ($timer) {\n    list($op, $data) = opcode_decode($recv);\n    Assert::true($data);\n    $cli->close();\n    Timer::clear($timer);\n    echo \"SUCCESS\\n\";\n});\n\\Swoole\\Event::wait();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/reload.phpt",
    "content": "--TEST--\nswoole_server: reload\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/opcode_server.php\";\n$port = get_one_free_port();\n\nstart_server($simple_tcp_server, TCP_SERVER_HOST, $port);\n\n$timer = suicide(2000);\nusleep(500 * 1000);\n\nmakeCoTcpClient(TCP_SERVER_HOST, $port, function(Client $cli) {\n    $r = $cli->send(opcode_encode(\"reload\", [2]));\n    Assert::assert($r !== false);\n}, function(Client $cli, $recv) use ($timer) {\n    list($op, $data) = opcode_decode($recv);\n    Assert::true($data);\n    $cli->close();\n    Timer::clear($timer);\n    echo \"SUCCESS\";\n});\n\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/reload_async.phpt",
    "content": "--TEST--\nswoole_server: reload async\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$reloaded = new Swoole\\Atomic;\n$workerCounter = new Swoole\\Atomic;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->setWaitTimeout(-1);\n$pm->parentFunc = function () use ($pm) {\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm, $reloaded, $workerCounter) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'log_file' => '/dev/null',\n        'worker_num' => rand(2, swoole_cpu_num() * 2),\n        'max_wait_time' => 10,\n        'reload_async' => true,\n        'enable_coroutine' => false,\n    ]);\n    $server->on('WorkerStart', function (Swoole\\Server $server, int $worker_id) use ($pm, $reloaded, $workerCounter) {\n        $workerCounter->add(1);\n        if ($worker_id === 0 and $reloaded->get() != 1) {\n            $reloaded->set(1);\n            while ($workerCounter->get() < $server->setting['worker_num']) {\n                usleep(10000);\n            }\n            go(function () use ($pm) {\n                for ($n = 1; $n <= 5; $n++) {\n                    Co::sleep(0.1);\n                    echo \"{$n}\\n\";\n                }\n                echo \"RELOADED\\n\";\n                $pm->wakeup();\n            });\n            echo \"RELOAD\\n\";\n            Assert::assert($server->reload());\n        }\n    });\n    $server->on('Receive', function () { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nRELOAD\n1\n2\n3\n4\n5\nRELOADED\nDONE\n"
  },
  {
    "path": "tests/swoole_server/reload_process.phpt",
    "content": "--TEST--\nswoole_server: reload in process mode\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n//skip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$worker_num = $task_worker_num = swoole_cpu_num() * 2;\n$counter = [\n    'worker' => new Swoole\\Atomic(),\n    'task_worker' => new Swoole\\Atomic()\n];\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    global $counter, $worker_num, $task_worker_num;\n    while (!file_exists(TEST_PID_FILE)) {\n        usleep(100 * 1000);\n    }\n    $pid = file_get_contents(TEST_PID_FILE);\n    $random = mt_rand(1, 4);\n    usleep(100 * 1000);\n    for ($n = $random; $n--;) {\n        Swoole\\Process::kill($pid, SIGUSR1);\n        usleep(500 * 1000);\n        Swoole\\Process::kill($pid, SIGUSR2);\n        usleep(500 * 1000);\n    }\n\n    /**@var $counter Swoole\\Atomic[] */\n    $total = $counter['worker']->get() - $worker_num;\n    $expect = $random * $worker_num;\n    Assert::same($total, $expect, \"[worker reload {$total} but expect {$expect}]\");\n\n    $total = $counter['task_worker']->get() - $task_worker_num;\n    $expect = $random * $task_worker_num * 2;\n    Assert::same($total, $expect, \"[task worker reload {$total} but expect {$expect}]\");\n\n    $log = file_get_contents(TEST_LOG_FILE);\n    $log = trim(preg_replace('/.+?\\s+?INFO\\s+?.+/', '', $log));\n    if (!Assert::assert(empty($log))){\n        var_dump($log);\n    }\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    global $worker_num, $task_worker_num;\n    @unlink(TEST_LOG_FILE);\n    @unlink(TEST_PID_FILE);\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'log_file' => TEST_LOG_FILE,\n        'pid_file' => TEST_PID_FILE,\n        'worker_num' => $worker_num,\n        'task_worker_num' => $task_worker_num\n    ]);\n    $server->on('ManagerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('WorkerStart', function (Swoole\\Server $server, int $worker_id) use ($pm) {\n        /**@var $counter Swoole\\Atomic[] */\n        global $counter;\n        $atomic = $server->taskworker ? $counter['task_worker'] : $counter['worker'];\n        $atomic->add(1);\n    });\n    $server->on('Receive', function (Swoole\\Server $server, $fd, $reactor_id, $data) { });\n    $server->on('Task', function () { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/sendMessage_1.phpt",
    "content": "--TEST--\nswoole_server: send message [1]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/opcode_server.php\";\n$port = get_one_free_port();\n\nstart_server($simple_tcp_server, TCP_SERVER_HOST, $port);\n\n$timer = suicide(2000);\nusleep(500 * 1000);\n\nmakeCoTcpClient(TCP_SERVER_HOST, $port, function(Client $cli) use($port) {\n    $r = $cli->send(opcode_encode(\"sendMessage\", [\"SUCCESS\", 1]));\n    Assert::assert($r !== false);\n}, function(Client $cli, $recv) {\n    list($op, $msg) = opcode_decode($recv);\n    echo $msg;\n    global $timer;\n    $cli->close();\n    Timer::clear($timer);\n});\n\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/sendMessage_2.phpt",
    "content": "--TEST--\nswoole_server: send message [02]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $client->set([\n        'package_eof' => \"\\r\\n\",\n        'open_eof_check' => true,\n        'open_eof_split' => true,\n    ]);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n        exit(\"connect failed\\n\");\n    }\n    $list = [];\n    for ($i = 0; $i < 7; $i++) {\n        $data = $client->recv();\n        if ($data === false or $data === '') {\n            echo \"ERROR\\n\";\n            break;\n        }\n        $list[] = intval($data);\n    }\n    sort($list);\n    Assert::same($list, range(0, 6));\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n    $serv->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 4,\n        'task_worker_num' => 3,\n    ]);\n\n    $process = new \\Swoole\\Process(function ($process) use ($serv) {\n        while (true) {\n            $r = $process->read();\n            if (!$r) {\n                continue;\n            }\n            $cmd = json_decode($r, true);\n            for ($i = 0; $i < ($serv->setting['worker_num'] + $serv->setting['task_worker_num']); $i++) {\n                $serv->sendMessage(['worker_id' => $i, 'fd' => $cmd['fd']], $i);\n            }\n        }\n    });\n\n    $serv->addProcess($process);\n    $serv->on(\"workerStart\", function ($serv, $wid) use ($pm) {\n        if ($wid == 0) {\n            $pm->wakeup();\n        }\n    });\n    $serv->on('connect', function (Swoole\\Server $serv, $fd) use ($process) {\n        $process->write(json_encode([\"fd\" => $fd]));\n    });\n    $serv->on('receive', function ($serv, $fd, $reactor_id, $data) {\n\n    });\n\n    $serv->on('pipeMessage', function (Swoole\\Server $serv, $worker_id, $data) {\n        $serv->send($data['fd'], $data['worker_id'] . \"\\r\\n\");\n    });\n\n    $serv->on('task', function (Swoole\\Server $serv, $task_id, $worker_id, $data) {\n\n    });\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/sendMessage_3.phpt",
    "content": "--TEST--\nswoole_server: send message [3]\n--SKIPIF--\n<?php use Swoole\\Process;\n\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $client->set([\n        'package_eof' => \"\\r\\n\",\n        'open_eof_check' => true,\n        'open_eof_split' => true,\n    ]);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n        exit(\"connect failed\\n\");\n    }\n    echo $client->recv();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n    $serv->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 2,\n    ]);\n    $serv->on(\"workerStart\", function ($serv, $wid) use ($pm) {\n        if ($wid == 0) {\n            $pm->wakeup();\n        }\n    });\n    $serv->on('connect', function (Swoole\\Server $serv, $fd) {\n        $wid = $serv->getWorkerId();\n        $serv->sendMessage([\n            'fd' => $fd,\n            'worker_id' => $wid,\n            'data' => random_bytes(random_int(1024 * 1024, 2 * 1024 * 1024)),\n        ], 1 - $wid);\n    });\n    $serv->on('receive', function ($serv, $fd, $reactor_id, $data) {\n\n    });\n\n    $serv->on('pipeMessage', function (Swoole\\Server $serv, $worker_id, $data) {\n        $serv->send($data['fd'], \"OK\\r\\n\");\n    });\n\n    $serv->on('task', function (Swoole\\Server $serv, $task_id, $worker_id, $data) {\n\n    });\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_server/sendMessage_4.phpt",
    "content": "--TEST--\nswoole_server: send message [4]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip('Waiting for php to fix the problem with the serialization function. https://externals.io/message/118813');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $client->set([\n        'package_eof' => \"\\r\\n\",\n        'open_eof_check' => true,\n        'open_eof_split' => true,\n    ]);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n        exit(\"connect failed\\n\");\n    }\n    echo $client->recv();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n    $serv->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 2,\n    ]);\n    $serv->on(\"workerStart\", function ($serv, $wid) use ($pm) {\n        if ($wid == 0) {\n            $pm->wakeup();\n        }\n    });\n    $serv->on('connect', function (Swoole\\Server $serv, $fd) {\n        $wid = $serv->getWorkerId();\n        $serv->sendMessage([\n            'fd' => $fd,\n            'worker_id' => $wid,\n            'exception' => new \\Exception(__METHOD__),\n        ], 1 - $wid);\n    });\n    $serv->on('receive', function ($serv, $fd, $reactor_id, $data) {\n\n    });\n    $serv->on('pipeMessage', function (Swoole\\Server $serv, $worker_id, $data) {\n        $serv->send($data['fd'], \"OK\\r\\n\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_server/sendMessage_in_manager.phpt",
    "content": "--TEST--\nswoole_server: sendMessage in manager\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nuse Swoole\\Constant;\nuse Swoole\\Server;\n\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n\nconst N = 16;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $client->set([\n        'package_eof' => \"\\r\\n\",\n        'open_eof_check' => true,\n        'open_eof_split' => true,\n    ]);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n        exit(\"connect failed\\n\");\n    }\n    $list = [];\n    swoole_loop_n(N, function () use ($client, &$list) {\n        $msg = $client->recv();\n        $list[] = $msg;\n        Assert::contains($msg, 'msg-');\n    });\n    Assert::count($list, N);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP);\n    $serv->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 2,\n        'task_worker_num' => 2,\n    ]);\n    $serv->on(Constant::EVENT_MANAGER_START, function (Server $serv) use ($pm) {\n        $pm->wakeup();\n        $pm->wait();\n        usleep(10000);\n        swoole_loop_n(N, function ($i) use ($serv) {\n            $wid = rand(0, 3);\n            $serv->sendMessage(\"msg-\" . $i, $wid);\n        });\n    });\n    $serv->on(Constant::EVENT_CONNECT, function ($serv, $fd, $reactor_id) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(Constant::EVENT_RECEIVE, function ($serv, $fd, $reactor_id, $data) {\n    });\n    $serv->on(Constant::EVENT_TASK, function ($serv, $fd, $reactor_id, $data) {\n    });\n    $serv->on('pipeMessage', function (Server $serv, $worker_id, $data) {\n        foreach ($serv->connections as $fd) {\n            $serv->send($fd, $data . \"\\r\\n\");\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/send_2.phpt",
    "content": "--TEST--\nswoole_server: send big packet [2]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst VERBOSE = FALSE;\nconst SEND_N = 32;\nconst CONCURRENCY = 16;\nconst PKT_MIN_SIZE = 256 * 1024;\nconst PKT_MAX_SIZE = 1024 * 1024;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $total = 0;\n    for ($i = 0; $i < CONCURRENCY; $i++) {\n        go(function () use ($pm, $i, &$total) {\n            $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n            $cli->set([\n                'open_length_check' => true,\n                'package_max_length' => 4 * 1024 * 1024,\n                'package_length_type' => 'N',\n                'package_length_offset' => 0,\n                'package_body_offset' => 4,\n            ]);\n            if ($cli->connect('127.0.0.1', $pm->getFreePort(), 100) == false) {\n                echo \"ERROR\\n\";\n                return;\n            }\n            $n = SEND_N;\n            while ($n--) {\n                $data = $cli->recv();\n                Assert::assert($data);\n                $char = chr(ord('A') + $n % 10);\n                $info = unpack('Nlen', substr($data, 0, 4));\n                if (VERBOSE) {\n                    echo \"[Client] c=$i, n=$n, len={$info['len']}\\n---------------------------------------------------------------------\\n\";\n                }\n                Assert::same($info['len'], strlen($data) - 4);\n                Assert::same(str_repeat($char, 1024), substr($data, rand(4, $info['len'] - 1024 - 4), 1024));\n                $total += strlen($data);\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    echo $total . \" bytes\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        \"worker_num\" => 1,\n        'log_level' => SWOOLE_LOG_ERROR,\n        'open_length_check' => true,\n        'package_max_length' => 4 * 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n        'send_yield' => true,\n    ));\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('connect', function (Swoole\\Server $serv, $fd, $rid) {\n        if (VERBOSE) {\n            echo \"new client, fd=$fd\\n\";\n        }\n        $n = SEND_N;\n        while ($n--) {\n            $len = rand(PKT_MIN_SIZE, PKT_MAX_SIZE);\n            $send_data = str_repeat(chr(ord('A') + $n % 10), $len);\n            if (VERBOSE) {\n                echo \"[Server] c=$fd, n=$n, len=\" . (strlen($send_data) + 4) . \"\\n---------------------------------------------------------------------\\n\";\n            }\n            $retval = $serv->send($fd, pack('N', $len) . $send_data);\n            if ($retval === false) {\n                echo \"send error, code=\" . swoole_last_error() . \"\\n\";\n            }\n        }\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n%d bytes\n"
  },
  {
    "path": "tests/swoole_server/send_2m_in_task_worker.phpt",
    "content": "--TEST--\nswoole_server: send 2M data in task worker\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst SIZE = 2 * 1024 * 1024;\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $c = IS_MAC_OS ? 8 : MAX_CONCURRENCY_MID;\n    for ($i = 0; $i < $c; $i++) {\n        go(function () use ($pm, $i) {\n            $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n            $cli->set([\n                'open_length_check' => true,\n                'package_max_length' => 4 * 1024 * 1024,\n                'package_length_type' => 'N',\n                'package_length_offset' => 0,\n                'package_body_offset' => 4,\n            ]);\n            if ($cli->connect('127.0.0.1', $pm->getFreePort(), 2) == false) {\n                echo \"ERROR\\n\";\n                return;\n            }\n            for ($i = 0; $i < MAX_REQUESTS; $i++) {\n                $sid = strval(rand(10000000, 99999999));\n                $send_data = str_repeat('A', 1000) . $sid;\n                $cli->send(pack('N', strlen($send_data)) . $send_data);\n                $data = $cli->recv();\n                Assert::same(strlen($data), SIZE);\n                Assert::same($sid, substr($data, -8, 8));\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        \"worker_num\" => 2,\n        'log_level' => SWOOLE_LOG_ERROR,\n        'task_worker_num' => 2,\n        'open_length_check' => true,\n        'package_max_length' => 4 * 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n        'task_use_object' => true,\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n        $serv->task(['fd' => $fd, 'data' => $data]);\n    });\n    $serv->on('task', function (Server $serv, Server\\Task $task) {\n        $send_data = str_repeat('A', SIZE - 12) . substr($task->data['data'], -8, 8);\n        Assert::true($serv->send($task->data['fd'], pack('N', strlen($send_data)) . $send_data));\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/send_2m_in_user_process.phpt",
    "content": "--TEST--\nswoole_server: send 2M data in user process\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_darwin_todo();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst SIZE = 2 * 1024 * 1024;\n\nuse Co\\Client;\nuse Swoole\\Event;\nuse Swoole\\Process;\nuse Swoole\\Server;\nuse SwooleTest\\ProcessManager;\n\n$pm = new ProcessManager();\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) {\n        go(function () use ($pm, $i) {\n            $cli = new Client(SWOOLE_SOCK_TCP);\n            $cli->set([\n                'open_length_check' => true,\n                'package_max_length' => 4 * 1024 * 1024,\n                'package_length_type' => 'N',\n                'package_length_offset' => 0,\n                'package_body_offset' => 4,\n            ]);\n            if ($cli->connect('127.0.0.1', $pm->getFreePort(), 2) == false) {\n                echo \"ERROR\\n\";\n                return;\n            }\n            for ($i = 0; $i < MAX_REQUESTS; $i++) {\n                $sid = strval(rand(10000000, 99999999));\n                $send_data = str_repeat('A', 1000) . $sid;\n                $cli->send(pack('N', strlen($send_data)) . $send_data);\n                $data = $cli->recv();\n                Assert::same(strlen($data), SIZE);\n                Assert::same($sid, substr($data, -8, 8));\n            }\n        });\n    }\n    Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 2,\n        'task_worker_num' => 3,\n        'log_level' => SWOOLE_LOG_ERROR,\n        'open_length_check' => true,\n        'package_max_length' => 4 * 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n    ]);\n\n    $proc = new Process(function ($process) use ($serv) {\n        while (true) {\n            $pkt = $process->read();\n            if (!$pkt) {\n                break;\n            }\n            $data = unserialize($pkt);\n            $send_data = str_repeat('A', SIZE - 12) . substr($data['data'], -8, 8);\n            $serv->send($data['fd'], pack('N', strlen($send_data)) . $send_data);\n        }\n    }, false, 2);\n    $serv->addProcess($proc);\n\n    $serv->on('WorkerStart', function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($proc) {\n        $proc->write(serialize(['fd' => $fd, 'data' => $data]));\n    });\n\n    $serv->on('task', function (Server $serv, Server\\Task $task) {\n        echo \"ERROR\\n\";\n    });\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/send_3.phpt",
    "content": "--TEST--\nswoole_server: send big packet [3]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst SEND_N = 32;\nconst CONCURRENCY = 16;\nconst PKT_MIN_SIZE = 256 * 1024;\nconst PKT_MAX_SIZE = 1024 * 1024;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $total = 0;\n    for ($i = 0; $i < CONCURRENCY; $i++) {\n        go(function () use ($pm, $i, &$total) {\n            $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n            $cli->set([\n                'open_length_check' => true,\n                'package_max_length' => 4 * 1024 * 1024,\n                'package_length_type' => 'N',\n                'package_length_offset' => 0,\n                'package_body_offset' => 4,\n            ]);\n            if ($cli->connect('127.0.0.1', $pm->getFreePort(), 100) == false) {\n                echo \"ERROR\\n\";\n                return;\n            }\n            $n = SEND_N;\n            while ($n--) {\n                $data = $cli->recv();\n                Assert::assert($data);\n                $char = chr(ord('A') + $n % 10);\n                $info = unpack('Nlen', substr($data, 0, 4));\n\n//                echo \"c=$i, n=$n, len={$info['len']}\\n---------------------------------------------------------------------\\n\";\n                Assert::same($info['len'], strlen($data) - 4);\n                Assert::same(str_repeat($char, 1024), substr($data, rand(4, $info['len'] - 1024 - 4), 1024));\n                $total += strlen($data);\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    echo $total . \" bytes\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        'reactor_num' => 1,\n        \"worker_num\" => 4,\n        'log_level' => SWOOLE_LOG_ERROR,\n        'open_length_check' => true,\n        'package_max_length' => 4 * 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n        'send_yield' => true,\n    ));\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('connect', function (Swoole\\Server $serv, $fd, $rid) {\n        $n = SEND_N;\n        while ($n--) {\n            $len = rand(PKT_MIN_SIZE, PKT_MAX_SIZE);\n            $send_data = str_repeat(chr(ord('A') + $n % 10), $len);\n            $retval = $serv->send($fd, pack('N', $len) . $send_data);\n            if ($retval === false) {\n                echo \"send error, code=\".swoole_last_error().\"\\n\";\n            }\n        }\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n%d bytes\n"
  },
  {
    "path": "tests/swoole_server/send_big_packet.phpt",
    "content": "--TEST--\nswoole_server: send big packet\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst SIZE = 2 * 1024 * 1024;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($i = 0; $i < MAX_CONCURRENCY_LOW; $i++) {\n        go(function () use ($pm, $i) {\n            $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n            $cli->set([\n                'open_length_check' => true,\n                'package_max_length' => 4 * 1024 * 1024,\n                'package_length_type' => 'N',\n                'package_length_offset' => 0,\n                'package_body_offset' => 4,\n            ]);\n            if ($cli->connect('127.0.0.1', $pm->getFreePort(), 2) == false) {\n                echo \"ERROR\\n\";\n                return;\n            }\n            for ($i = 0; $i < MAX_REQUESTS_LOW; $i++) {\n                $sid = strval(rand(10000000, 99999999));\n                $send_data = str_repeat('A', 1000) . $sid;\n                $cli->send(pack('N', strlen($send_data)) . $send_data);\n                $data = $cli->recv();\n                Assert::same(strlen($data), SIZE);\n                Assert::same($sid, substr($data, -8, 8));\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        \"worker_num\" => 4,\n        'log_level' => SWOOLE_LOG_ERROR,\n        'open_length_check' => true,\n        'package_max_length' => 4 * 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n    ));\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n        $send_data = str_repeat('A', SIZE - 12) . substr($data, -8, 8);\n        $serv->send($fd, pack('N', strlen($send_data)) . $send_data);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/send_in_master.phpt",
    "content": "--TEST--\nswoole_server: send data in master process\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Constant;\nuse Swoole\\Timer;\nuse Swoole\\Atomic;\n\n$GLOBALS['data'] = base64_encode(random_bytes(128));\n$GLOBALS['atomic'] = new Atomic(0);\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n        if ($cli->connect('127.0.0.1', $pm->getFreePort(), 100) == false) {\n            echo \"ERROR\\n\";\n            return;\n        }\n        $cli->send($GLOBALS['data']);\n        Assert::same($cli->recv(), 'Swoole: '.$GLOBALS['data']);\n    });\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        \"worker_num\" => 2,\n        'log_file' => TEST_LOG_FILE,\n    ));\n    $serv->on(Constant::EVENT_START, function (Server $server) {\n        Timer::tick(50, function ($timer) use ($server) {\n            if ($GLOBALS['atomic']->get() == 0) {\n                return;\n            }\n            foreach ($server->connections as $fd) {\n                $server->send($fd, 'Swoole: ' . $GLOBALS['data']);\n            }\n            Timer::clear($timer);\n        });\n    });\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(Constant::EVENT_RECEIVE, function (Server $serv, $fd, $rid, $data) {\n        $GLOBALS['atomic']->set(1);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/send_in_other_worker_with_base.phpt",
    "content": "--TEST--\nswoole_server: send data in another process with base mode\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Constant;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n        if ($cli->connect('127.0.0.1', $pm->getFreePort(), 100) == false) {\n            echo \"ERROR\\n\";\n            return;\n        }\n        $data = base64_encode(random_bytes(128));\n        $cli->send($data);\n        Assert::same($cli->recv(), 'Swoole: '.$data);\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set(array(\n        \"worker_num\" => 2,\n        'log_file' => TEST_LOG_FILE,\n    ));\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(Constant::EVENT_PIPE_MESSAGE, function ($serv, $workerId, $msg) {\n        $serv->send($msg['fd'], 'Swoole: '.$msg['data']);\n    });\n    $serv->on(Constant::EVENT_RECEIVE, function (Swoole\\Server $serv, $fd, $rid, $data) {\n        $serv->sendMessage(['data' => $data, 'fd' => $fd], 1 - $serv->getWorkerId());\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/sendfile.phpt",
    "content": "--TEST--\nswoole_server: sendfile\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/opcode_server.php\";\n$port = get_one_free_port();\n\nstart_server($simple_tcp_server, TCP_SERVER_HOST, $port);\n\n$timer = suicide(2000);\nusleep(500 * 1000);\n\nmakeCoTcpClient(TCP_SERVER_HOST, $port, function (Client $cli) {\n    $r = $cli->send(opcode_encode(\"sendfile\", [2, __FILE__]));\n    Assert::assert($r !== false);\n}, function (Client $cli, $recv) {\n    $len = unpack(\"N\", substr($recv, 0, 4))[1];\n    Assert::same($len - 4, strlen(substr($recv, 4)));\n    global $timer;\n    $cli->close();\n    Timer::clear($timer);\n    echo \"SUCCESS\";\n});\n\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/sendfile_02.phpt",
    "content": "--TEST--\nswoole_server: sendfile [02]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_extension_not_exist('sockets');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm){\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP , SWOOLE_SOCK_SYNC); //同步阻塞\n    if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n        exit(\"connect failed\\n\");\n    }\n\n    $socket = $client->getSocket();\n    socket_set_option($socket, SOL_SOCKET, SO_SNDBUF, 65536);\n    socket_set_option($socket, SOL_SOCKET, SO_RCVBUF, 65536);\n\n    $N = filesize(TEST_IMAGE);\n    $bytes = 0;\n    while($bytes < $N) {\n        $n = rand(8192, 65536);\n        $r = $client->recv($n);\n        if (!$r) {\n            break;\n        }\n        usleep(10000);\n        $bytes += strlen($r);\n    }\n    Assert::same($bytes, $N);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $serv->set([\n        'log_file' => '/dev/null',\n        'kernel_socket_send_buffer_size' => 65536,\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('connect', function (Swoole\\Server $serv, $fd) {\n        Assert::true($serv->sendfile($fd, TEST_IMAGE));\n    });\n    $serv->on('receive', function ($serv, $fd, $reactor_id, $data) {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/sendfile_ssl.phpt",
    "content": "--TEST--\nswoole_server: sendfile with SSL\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_extension_not_exist('sockets');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞\n    if (!$client->connect('127.0.0.1', $pm->getFreePort()))\n    {\n        exit(\"connect failed\\n\");\n    }\n\n    $socket = $client->getSocket();\n    socket_set_option($socket, SOL_SOCKET, SO_SNDBUF, 65536);\n    socket_set_option($socket, SOL_SOCKET, SO_RCVBUF, 65536);\n\n    $N = filesize(TEST_IMAGE);\n    $bytes = 0;\n    $data = '';\n    while ($bytes < $N)\n    {\n        $n = rand(8192, 65536);\n        $r = $client->recv($n);\n        if (!$r)\n        {\n            break;\n        }\n        usleep(10000);\n        $bytes += strlen($r);\n        $data .= $r;\n    }\n    Assert::same($bytes, $N);\n    Assert::same(md5_file(TEST_IMAGE), md5($data));\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $serv->set([\n        //'log_file' => '/dev/null',\n        'kernel_socket_send_buffer_size' => 65536,\n        'ssl_cert_file' => SSL_FILE_DIR.'/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR.'/server.key',\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('connect', function (Swoole\\Server $serv, $fd) {\n        Assert::true($serv->sendfile($fd, TEST_IMAGE));\n    });\n    $serv->on('receive', function ($serv, $fd, $reactor_id, $data) {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/sendto_timeout.phpt",
    "content": "--TEST--\nswoole_server: sendto timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst N = 65507;\n\ndefine('SOCK_FILE', __DIR__.'/server.sock');\nswoole_async_set(['socket_send_timeout' => 0.5]);\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_UNIX_DGRAM, SWOOLE_SOCK_SYNC);\n    if (!$client->connect(SOCK_FILE, 0, 0.5)) {\n        exit(\"connect failed\\n\");\n    }\n    $client->send(str_repeat('A', N));\n    $s = microtime(true);\n    $pm->wait();\n    Assert::lessThan(microtime(true) - $s, 0.6);\n    $data = $client->recv();\n    Assert::same(strlen($data), N);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server(SOCK_FILE, 0, SWOOLE_BASE, SWOOLE_SOCK_UNIX_DGRAM);\n    $serv->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('packet', function ($serv, $data, $client) use ($pm) {\n        while (true) {\n            $re = $serv->send($client['address'], str_repeat('B', strlen($data)));\n            if ($re == false) {\n                break;\n            }\n        }\n        $pm->wakeup();\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/shutdown.phpt",
    "content": "--TEST--\nswoole_server: shutdown\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/opcode_server.php\";\n$port = get_one_free_port();\n\nstart_server($simple_tcp_server, TCP_SERVER_HOST, $port);\n\n$timer = suicide(2000);\nusleep(500 * 1000);\n\nmakeCoTcpClient(TCP_SERVER_HOST, $port, function(Client $cli) {\n    $r = $cli->send(opcode_encode(\"shutdown\", [2]));\n    Assert::assert($r !== false);\n}, function(Client $cli, $recv) {\n    list($op, $data) = opcode_decode($recv);\n    Assert::true($data);\n    global $timer;\n    $cli->close();\n    Timer::clear($timer);\n    echo \"SUCCESS\";\n});\n\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/shutdown_in_master.phpt",
    "content": "--TEST--\nswoole_server: shutdown in master process\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->initRandomData(1);\n\n$pm->parentFunc = function () use ($pm) {\n\n};\n\n$pm->childFunc = function () use ($pm) {\n    $mode = SERVER_MODE_RANDOM;\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), $mode);\n    $server->set([\n        'worker_num' => mt_rand(1, 4),\n        'log_file' => '/dev/null',\n    ]);\n    $server->on('start', function (Swoole\\Server $server) use ($pm, $mode) {\n        echo \"START [$mode]\\n\";\n        $pm->wakeup();\n        $server->shutdown();\n    });\n    $server->on('receive', function () { });\n    $server->on('shutdown', function () {\n        echo \"SHUTDOWN\\n\";\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n$pm->expectExitCode(0);\n?>\n--EXPECTF--\nSTART [%d]\nSHUTDOWN\n"
  },
  {
    "path": "tests/swoole_server/shutdown_in_process.phpt",
    "content": "--TEST--\nswoole_server: shutdown in process mode\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->initRandomData(1);\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $client = new Co\\Client(SWOOLE_SOCK_TCP);\n        Assert::assert($client->connect('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($client->send($pm->getRandomData()) > 0);\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set(['worker_num' => mt_rand(1, 4), 'log_file' => '/dev/null']);\n    $server->on('start', function () use ($pm) {\n        echo \"START\\n\";\n        $pm->wakeup();\n    });\n    $server->on('receive', function (Swoole\\Server $server, int $fd, int $rid, string $data) use ($pm) {\n        Assert::same($data, $pm->getRandomData());\n        $server->shutdown();\n    });\n    $server->on('shutdown', function () {\n        echo \"SHUTDOWN\\n\";\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n$pm->expectExitCode(0);\n?>\n--EXPECT--\nSTART\nSHUTDOWN\n"
  },
  {
    "path": "tests/swoole_server/shutdown_with_base_mode.phpt",
    "content": "--TEST--\nswoole_server: dispatch_mode = 7 [stream]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set(array(\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n        \"reload_async\" => true,\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/sigint_with_base.phpt",
    "content": "--TEST--\nswoole_server: register sigint handler with base mode\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Process;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    Process::kill($pid, SIGINT);\n    usleep(10000);\n    echo file_get_contents(TEST_LOG_FILE);\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 1,\n    ]);\n    $server->on('workerStart', function (Server $server) use ($pm) {\n        Process::signal(SIGINT, function () use ($server) {\n            file_put_contents(TEST_LOG_FILE, 'SIGINT, SHUTDOWN' . PHP_EOL);\n            $server->shutdown();\n        });\n        $pm->wakeup();\n    });\n    $server->on('Receive', function (Server $server, $fd, $reactorId, $data) {\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n@unlink(TEST_LOG_FILE);\n?>\n--EXPECT--\nSIGINT, SHUTDOWN\n"
  },
  {
    "path": "tests/swoole_server/sigint_with_process.phpt",
    "content": "--TEST--\nswoole_server:  register sigint handler with process mode\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    Swoole\\Process::kill($pid, SIGINT);\n    usleep(10000);\n    echo file_get_contents(TEST_LOG_FILE);\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 1,\n    ]);\n    $server->on('start', function (Swoole\\Server $server) use ($pm) {\n        \\Swoole\\Process::signal(SIGINT, function () use ($server) {\n            file_put_contents(TEST_LOG_FILE, 'SIGINT, SHUTDOWN' . PHP_EOL);\n            $server->shutdown();\n        });\n        $pm->wakeup();\n    });\n    $server->on('Receive', function (Swoole\\Server $server, $fd, $reactorId, $data) {\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n@unlink(TEST_LOG_FILE);\n?>\n--EXPECT--\nSIGINT, SHUTDOWN\n"
  },
  {
    "path": "tests/swoole_server/single_thread/heartbeat.phpt",
    "content": "--TEST--\nswoole_server/single_thread: heartbeat\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    Assert::isEmpty(@file_get_contents('http://127.0.0.1:' . $pm->getFreePort() . '/'));\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set([\n        'single_thread' => true,\n        'worker_num' => 1,\n        'heartbeat_idle_time' => 1,\n        'heartbeat_check_interval' => 1,\n    ]);\n    $http->on('WorkerStart', function (Swoole\\Http\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('Request', function ($request, $response) use ($http) {\n        sleep(3);\n        Assert::false($response->end('hello'));\n        Assert::eq($http->getLastError(), SWOOLE_ERROR_SESSION_NOT_EXIST);\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/single_thread/large_packet.phpt",
    "content": "--TEST--\nswoole_server/single_thread: large packet\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $url = 'http://127.0.0.1:' . $pm->getFreePort() . '/';\n    $filePath = tempnam('/tmp', 'swoole_test_');\n    $rdata = random_bytes(1024 * 1024);\n    file_put_contents($filePath, $rdata);\n    $ch = curl_init();\n\n    curl_setopt($ch, CURLOPT_URL, $url);\n    curl_setopt($ch, CURLOPT_POST, true);\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n    curl_setopt($ch, CURLOPT_HTTPHEADER, [\n        'Accept: text/html',\n        'Content-Type: multipart/form-data'\n    ]);\n    curl_setopt($ch, CURLOPT_POSTFIELDS, [\n        'file' => new CURLFile($filePath, 'text/html')\n    ]);\n    $response = curl_exec($ch);\n    if (curl_errno($ch)) {\n        echo 'ERROR: ' . curl_error($ch);\n    } else {\n        Assert::eq($response, md5($rdata));\n    }\n    curl_close($ch);\n    unlink($filePath);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set([\n        'single_thread' => true,\n        'worker_num' => 1,\n        'dispatch_mode' => 10,\n        'package_max_length' => '128m',\n    ]);\n    $http->on('WorkerStart', function (Swoole\\Http\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('Request', function ($request, $response) {\n        $response->end(md5_file($request->files['file']['tmp_name']));\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/single_thread/user_setting.phpt",
    "content": "--TEST--\nswoole_server/single_thread: user setting\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_not_root();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$log_file = tempnam('/tmp', 'swoole_test_');\nchmod($log_file, 0777);\nfile_put_contents($log_file, '');\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm, $log_file) {\n    $url = 'http://127.0.0.1:' . $pm->getFreePort() . '/';\n    posix_kill($pid, SIGUSR1);\n    sleep(1);\n    $output = file_get_contents($log_file);\n    Assert::contains($output, 'reloading all workers');\n    $pm->kill();\n    unlink($log_file);\n};\n\n$pm->childFunc = function () use ($pm, $log_file) {\n    $http = new Swoole\\Http\\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set([\n        'single_thread' => true,\n        'worker_num' => 1,\n        'user' => 'www-data',\n        'group' => 'www-data',\n        'log_file' => $log_file,\n    ]);\n    $http->on('WorkerStart', function (Swoole\\Http\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $http->on('Request', function ($request, $response) {\n        $response->end('hello');\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/sleep.phpt",
    "content": "--TEST--\nswoole_server: sleep\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Constant;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = new SwooleTest\\ProcessManager;\n\nconst N = 8;\n\n$pm->parentFunc = function () use ($pm) {\n    Co::set([Constant::OPTION_HOOK_FLAGS => SWOOLE_HOOK_ALL]);\n    run(function () use ($pm) {\n        $n = N;\n        $s = microtime(true);\n        $list = [];\n        while($n--) {\n            $list[] = go(function() use ($pm) {\n                $s = microtime(true);\n                $ch = curl_init();\n                $code = uniqid('swoole_');\n                $url = \"http://127.0.0.1:\".$pm->getFreePort().\"/?code=\".urlencode($code);\n\n                curl_setopt($ch, CURLOPT_URL, $url);\n                curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\n                curl_setopt($ch, CURLOPT_HEADER, 0);\n                curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) {\n                    return strlen($strHeader);\n                });\n\n                $output = curl_exec($ch);\n                Assert::eq($output, \"Hello World\\n\".$code);\n                if ($output === false) {\n                    echo \"CURL Error:\" . curl_error($ch);\n                }\n                curl_close($ch);\n            });\n        }\n\n        Co::join($list);\n\n        Assert::lessThan(microtime(true) - $s, 0.5);\n        echo \"Done\\n\";\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_BASE);\n\n    $http->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        Constant::OPTION_ENABLE_COROUTINE => true,\n        Constant::OPTION_HOOK_FLAGS => SWOOLE_HOOK_ALL,\n    ]);\n\n    $http->on(\"start\", function ($server) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $http->on(\"request\", function (Request $request, Response $response) {\n        usleep(300000);\n        $response->end(\"Hello World\\n\".$request->get['code']);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDone\n"
  },
  {
    "path": "tests/swoole_server/slow_client.phpt",
    "content": "--TEST--\nswoole_server: send big pipe message\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_extension_not_exist('sockets');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$port = get_one_free_port();\n\nconst N = 1024 * 1024 * 1;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($port)\n{\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞\n    if (!$client->connect('127.0.0.1', $port))\n    {\n        exit(\"connect failed\\n\");\n    }\n\n    $socket = $client->getSocket();\n    socket_set_option($socket, SOL_SOCKET, SO_SNDBUF, 65536);\n    socket_set_option($socket, SOL_SOCKET, SO_RCVBUF, 65536);\n\n    $bytes = 0;\n    while($bytes < N)\n    {\n        $n = rand(8192, 65536);\n        $r = $client->recv($n);\n        if (!$r)\n        {\n            break;\n        }\n        usleep(10000);\n        $bytes += strlen($r);\n    }\n    Assert::same($bytes, N);\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm, $port)\n{\n    $serv = new Swoole\\Server('127.0.0.1', $port, SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'kernel_socket_send_buffer_size' => 65536,\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on('connect', function (Swoole\\Server $serv, $fd)\n    {\n        $_send_data = str_repeat(\"A\", N);\n        $serv->send($fd, $_send_data);\n    });\n    $serv->on('receive', function ($serv, $fd, $reactor_id, $data)\n    {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/slow_master.phpt",
    "content": "--TEST--\nswoole_server: slow master\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_darwin_todo();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Client;\nuse Swoole\\Constant;\nuse Swoole\\Server;\nuse Swoole\\Timer;\n\n$data_chunks = [];\n\n$counter_server = new Swoole\\Atomic(0);\n$counter_client = new Swoole\\Atomic(0);\n\nfor ($i = 0; $i < MAX_REQUESTS; $i++) {\n    $rand = rand(8 * 1024, 1024 * 1024);\n    $data = pack('N', $rand) . str_repeat('A', $rand);\n    $data_chunks[] = $data;\n    $counter_client->add(strlen($data));\n}\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm, $counter_server, $counter_client, $data_chunks) {\n    $cli = new Client(SWOOLE_SOCK_TCP);\n    $r = $cli->connect(TCP_SERVER_HOST, $pm->getFreePort(), 5);\n    Assert::assert($r);\n    $cli->send('hello world');\n\n    $n = $counter_client->get();\n    $data = '';\n    while (strlen($data) < $n) {\n        $_recv = $cli->recv();\n        if (empty($_recv)) {\n            break;\n        }\n        $data .= $_recv;\n    }\n    Assert::eq($data, implode('', $data_chunks));\n    $cli->close();\n    $pm->kill();\n    Assert::greaterThanEq($counter_server->get(), 5);\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm, $counter_server, $counter_client, $data_chunks) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'single_thread' => true,\n    ));\n\n    $serv->on(Constant::EVENT_START, function () use ($pm)  {\n        Timer::after(5, function (){\n            usleep(300000);\n        });\n        $pm->wakeup();\n    });\n\n    $serv->on(Constant::EVENT_RECEIVE, function (Server $serv, $fd, $rid, $data) use ($counter_server, $counter_client, $data_chunks) {\n        $serv->timer = Timer::tick(50, function () use ($counter_server) {\n            $counter_server->add(1);\n        });\n        foreach ($data_chunks as $chunk) {\n            $serv->send($fd, $chunk);\n        }\n    });\n\n    $serv->on(Constant::EVENT_CLOSE, function ($serv) use ($pm)  {\n        Timer::clear($serv->timer);\n    });\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/slow_worker.phpt",
    "content": "--TEST--\nswoole_server: slow worker\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Client;\nuse Swoole\\Server;\n\n$data_chunks = [];\n\n$counter_server = new Swoole\\Atomic(0);\n$counter_client = new Swoole\\Atomic(0);\n\nfor ($i = 0; $i < MAX_REQUESTS; $i++) {\n    $rand = rand(8 * 1024, 1024 * 1024);\n    $data = pack('N', $rand) . str_repeat('A', $rand);\n    $data_chunks[] = $data;\n    $counter_client->add(strlen($data));\n}\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm, $counter_server, $counter_client, $data_chunks) {\n    $cli = new Client(SWOOLE_SOCK_TCP);\n    $r = $cli->connect(TCP_SERVER_HOST, $pm->getFreePort(), 5);\n    Assert::assert($r);\n    Assert::eq($cli->recv(), 'CONNECT OK'.PHP_EOL);\n\n    foreach ($data_chunks as $chunk) {\n        $cli->send($chunk);\n    }\n\n    $cli2 = new Client(SWOOLE_SOCK_TCP);\n    $r = $cli2->connect(TCP_SERVER_HOST, $pm->getFreePort(), 5);\n    Assert::assert($r);\n    Assert::eq($cli2->recv(), 'CONNECT OK'.PHP_EOL);\n\n    echo $cli->recv();\n    Assert::eq($counter_server->get(), $counter_client->get());\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $counter_server, $counter_client) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'package_max_length' => 16 * 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n    ));\n\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n        usleep(100000);\n    });\n\n    $serv->on(Swoole\\Constant::EVENT_CONNECT, function (Server $serv, $fd, $rid) {\n        $serv->send($fd, 'CONNECT OK'.PHP_EOL);\n    });\n\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($counter_server, $counter_client) {\n        $counter_server->add(strlen($data));\n        if ($counter_server->get() == $counter_client->get()) {\n            $serv->send($fd, \"DONE\\n\");\n        }\n    });\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/ssl/00.phpt",
    "content": "--TEST--\nswoole_server/ssl: basic\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞\n    if (!$client->connect('127.0.0.1', $pm->getFreePort()))\n    {\n        exit(\"connect failed\\n\");\n    }\n    $client->send(\"hello world\");\n    Assert::same($client->recv(), \"Swoole hello world\");\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $serv->set([\n        'log_file' => '/dev/null',\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function ($serv, $fd, $tid, $data) {\n        $serv->send($fd, \"Swoole $data\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/ssl/bad_client.phpt",
    "content": "--TEST--\nswoole_server/ssl: ssl bad client\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\ndefine('ERROR_FILE', __DIR__.'/ssl_error');\n\n$pm = new SwooleTest\\ProcessManager;\n$rdata = \"hello world\";\n\n$pm->parentFunc = function ($pid) use ($pm, $rdata) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞\n    if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n        exit(\"connect failed\\n\");\n    }\n    $client->send($rdata);\n    Assert::notEq($client->recv(), \"Swoole $rdata\");\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $serv->set(\n        [\n            'log_file' => ERROR_FILE,\n            'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n            'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n        ]\n    );\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function ($serv, $fd, $tid, $data) {\n        $serv->send($fd, \"Swoole $data\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\nreadfile(ERROR_FILE);\nunlink(ERROR_FILE);\n?>\n--EXPECTF--\n[%s]\tWARNING\tSocket::ssl_accept(): bad SSL client[127.0.0.1:%d], reason=%d, error_string=%s\n"
  },
  {
    "path": "tests/swoole_server/ssl/code/client.go",
    "content": "package main\n\nimport (\n    \"crypto/tls\"\n    \"fmt\"\n    \"io\"\n\t\"log\"\n\t\"os\"\n)\n\nfunc main() {\n\tconf := &tls.Config{\n\t\tInsecureSkipVerify: true,\n\t}\n\n\turl := fmt.Sprintf(\"127.0.0.1:%s\", os.Args[1]);\n\t   \n    conn, err := tls.Dial(\"tcp\", url, conf)\n    if err != nil {\n        log.Fatalln(err.Error())\n    }\n\n    buf := make([]byte, 1024)\n\n\t_, err = io.WriteString(conn, \"GET / HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\")\n\tif err != nil {\n\t\tlog.Fatalln(err.Error())\n\t}\n\tlen, err := conn.Read(buf)\n\tif err != nil {\n\t\tfmt.Println(err.Error())\n\t} else {\n\t\tfmt.Println(\"Receive From Server:\", string(buf[:len]))\n\t}\n}\n"
  },
  {
    "path": "tests/swoole_server/ssl/code/client.js",
    "content": "const tls = require('tls');\n\nconst options = {\n\trejectUnauthorized: false,\n};\n\nvar socket = tls.connect(process.argv[2], '127.0.0.1', options, () => {\n\tsocket.write('GET / HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n');\n});\n\nsocket.setEncoding('utf8');\nsocket.on('data', (data) => {\n  console.log(data);\n});\n\nsocket.on('end', () => {\n  console.log('Ended')\n});"
  },
  {
    "path": "tests/swoole_server/ssl/dtls.phpt",
    "content": "--TEST--\nswoole_server/ssl: dtls\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_UDP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞\n    if (!$client->connect('127.0.0.1', $pm->getFreePort()))\n    {\n        exit(\"connect failed\\n\");\n    }\n    $client->send(\"hello world\");\n    Assert::same($client->recv(), \"Swoole hello world\");\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP | SWOOLE_SSL);\n    $serv->set([\n        'log_file' => '/dev/null',\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function ($serv, $fd, $tid, $data) {\n        $serv->send($fd, \"Swoole $data\");\n    });\n    $serv->on('packet', function ($serv, $fd, $tid, $data) {\n        $serv->send($fd, \"Swoole $data\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/ssl/dtls_big_packet.phpt",
    "content": "--TEST--\nswoole_server/ssl: big dtls packet\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Client;\nuse Swoole\\Server;\nuse SwooleTest\\ProcessManager;\n\n$pm = new ProcessManager();\n\nconst PKT_LEN = IS_MAC_OS ? 8000 : 16000;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Client(SWOOLE_SOCK_UDP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); // 同步阻塞\n    if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n        exit(\"connect failed\\n\");\n    }\n    // TLS max record size = 16K\n    $client->send('hello world' . str_repeat('A', PKT_LEN));\n    Assert::same($client->recv(65535), 'Swoole hello world' . str_repeat('A', PKT_LEN));\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP | SWOOLE_SSL);\n    $serv->set([\n        'log_file' => '/dev/null',\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n    ]);\n    $serv->on('workerStart', function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function ($serv, $fd, $tid, $data) {\n        $serv->send($fd, \"Swoole {$data}\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/ssl/dtls_with_length_protocol.phpt",
    "content": "--TEST--\nswoole_server/ssl: dtls with length protocol\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Client;\nuse Swoole\\Server;\nuse SwooleTest\\ProcessManager;\n\n$size = IS_MAC_OS ? rand(4000, 8000) : rand(8192, 128000);\n$req = random_bytes($size);\n$resp = random_bytes($size);\n\n$pm = new ProcessManager();\n$pm->parentFunc = function ($pid) use ($pm, $req, $resp) {\n    $cli = new Client(SWOOLE_SOCK_UDP | SWOOLE_SSL, SWOOLE_SOCK_SYNC);\n    $cli->set([\n        'open_length_check' => true,\n        'package_max_length' => 16 * 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n        'socket_buffer_size' => 1024 * 1024,\n    ]);\n    $cli->connect(TCP_SERVER_HOST, $pm->getFreePort(), 5);\n    $cli->send(pack('N', strlen($req)) . $req);\n    $data = $cli->recv();\n    Assert::eq(bin2hex($resp), bin2hex(substr($data, 4)));\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $size, $req, $resp) {\n    $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP | SWOOLE_SSL);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n        'open_length_check' => true,\n        'package_max_length' => 16 * 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n        'socket_buffer_size' => 1024 * 1024,\n    ]);\n    $serv->on(\n        'WorkerStart',\n        function (Server $serv) use ($pm) {\n            $pm->wakeup();\n        }\n    );\n    $serv->on(\n        'connect',\n        function ($serv, $fd, $rid) {\n            // echo \"connect\\n\";\n        }\n    );\n    $serv->on(\n        'receive',\n        function ($serv, $fd, $rid, $data) use ($req, $resp) {\n            Assert::eq(bin2hex($req), bin2hex(substr($data, 4)));\n            Assert::assert($serv->send($fd, pack('N', strlen($resp)) . $resp));\n        }\n    );\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/ssl/golang.phpt",
    "content": "--TEST--\nswoole_server/ssl: golang client\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n$dir = __DIR__.'/code';\nchdir($dir);\n`go build -o go_client client.go >/dev/null 2>&1`;\nskip_if_file_not_exist( __DIR__.'/code/go_client');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n\ndefine('GO_CLIENT', __DIR__.'/code/go_client');\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $result = Co::exec(GO_CLIENT.' '.$pm->getFreePort());\n        Assert::eq($result['code'], 0);\n        Assert::contains($result['output'], 'swoole-http-server');\n    });\n    $pm->kill();\n    unlink(GO_CLIENT);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $serv->set([\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"request\", function ($request, $response) {\n        $response->header(\"Content-Type\", \"text/plain\");\n        $response->end(\"Hello World\\n\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/ssl/heartbeat_1.phpt",
    "content": "--TEST--\nswoole_server/ssl: heartbeat with bad client\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_in_valgrind();\nskip_if_darwin_todo();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Client;\nuse Swoole\\Process;\nuse Swoole\\Server;\nuse SwooleTest\\ProcessManager;\n\n$pm = new ProcessManager();\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort(), 5, 0)) {\n        echo 'Over flow. errno=' . $client->errCode;\n        exit(\"\\n\");\n    }\n    $s1 = time();\n    Assert::same($client->recv(), '');\n    $s2 = time();\n    Assert::assert($s2 - $s1 > 1);\n    Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $serv->set([\n        'heartbeat_check_interval' => 1,\n        'log_file' => '/dev/null',\n        'heartbeat_idle_time' => 1,\n        'ssl_cert_file' => SSL_FILE_DIR . '/server-cert.pem',\n        'ssl_key_file' => SSL_FILE_DIR . '/server-key.pem',\n    ]);\n    $serv->on('workerStart', function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {});\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/ssl/heartbeat_2.phpt",
    "content": "--TEST--\nswoole_server/ssl: heartbeat normal\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Client;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort(), 5, 0)) {\n        echo \"Over flow. errno=\" . $client->errCode;\n        die(\"\\n\");\n    }\n    $s1 = microtime(true);\n    Assert::same($client->recv(), '');\n    $s2 = microtime(true);\n    Assert::assert($s2 - $s1 > 1);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $serv->set(array(\n        'heartbeat_check_interval' => 1,\n        'log_file' => '/dev/null',\n        'heartbeat_idle_time' => 1,\n        'ssl_cert_file' => SSL_FILE_DIR . '/server-cert.pem',\n        'ssl_key_file' => SSL_FILE_DIR . '/server-key.pem',\n    ));\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/ssl/nodejs.phpt",
    "content": "--TEST--\nswoole_server/ssl: nodejs client\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_command_not_found('node');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $result = Co::exec('node '.__DIR__.'/code/client.js '.$pm->getFreePort());\n        Assert::eq($result['code'], 0);\n        Assert::contains($result['output'], 'swoole-http-server');\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Http\\Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $serv->set([\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"request\", function ($request, $response) {\n        $response->header(\"Content-Type\", \"text/plain\");\n        $response->end(\"Hello World\\n\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/ssl/ssl_send_wait.phpt",
    "content": "--TEST--\nswoole_server/ssl: send_wait support ssl\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    run(function () use ($pm)  {\n        $client = new Client('127.0.0.1', $pm->getFreePort(), true);\n\n        $ret = $client->get('/');\n        Assert::eq('swoole', $client->body);\n        $pm->kill();\n        echo \"DONE\\n\";\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n\n    $server->set([\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n        'ssl_ciphers' => 'ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP',\n        'ssl_protocols' => defined('SWOOLE_SSL_SSLv3') ? SWOOLE_SSL_SSLv3 : 0,\n        'ssl_verify_peer' => false,\n        'ssl_allow_self_signed' => true,\n    ]);\n\n    $server->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('Receive', function (Server $s, int $clientId, int $threadId, string $data) use ($server) {\n        $message = \"HTTP/1.1 200 OK\\r\\nContent-Length:6\\r\\nContent-Type: text/html; charset=UTF-8\\r\\n\\r\\nswoole\";\n        $server->sendwait($clientId, $message);\n    });\n\n    $server->start();\n\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/ssl/verify_01.phpt",
    "content": "--TEST--\nswoole_server/ssl: server verify client success\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_openssl_version_lower_than('1.1.0');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL);\n        $client->set([\n            'ssl_cert_file' => SSL_FILE_DIR . '/client-cert.pem',\n            'ssl_key_file' => SSL_FILE_DIR . '/client-key.pem',\n        ]);\n        if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n            exit(\"connect failed\\n\");\n        }\n        $client->send(\"hello world\");\n        Assert::same($client->recv(), \"Swoole hello world\");\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $serv->set([\n        'ssl_cert_file' => SSL_FILE_DIR . '/server-cert.pem',\n        'ssl_key_file' => SSL_FILE_DIR . '/server-key.pem',\n        'ssl_verify_peer' => true,\n        'ssl_allow_self_signed' => true,\n        'ssl_client_cert_file' => SSL_FILE_DIR . '/ca-cert.pem',\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function ($serv, $fd, $tid, $data) {\n        $serv->send($fd, \"Swoole $data\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/ssl/verify_02.phpt",
    "content": "--TEST--\nswoole_server/ssl: server verify client failed\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_openssl_version_lower_than('1.1.0');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC);\n    $client->set([\n        'ssl_cert_file' => SSL_FILE_DIR . '/client-expired.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/client-expired.key',\n    ]);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n        exit(\"connect failed\\n\");\n    }\n    $client->send(\"hello world\");\n    usleep(100 * 1000);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $serv->set([\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n        'ssl_verify_peer' => true,\n        'ssl_allow_self_signed' => true,\n        'ssl_client_cert_file' => SSL_FILE_DIR . '/ca.crt',\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function ($serv, $fd, $tid, $data) {\n        $serv->send($fd, \"Swoole $data\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n[%s]\tNOTICE\tSocket::ssl_verify() (ERRNO %d): can not verify peer from fd#%d with error#%d: certificate has expired\n"
  },
  {
    "path": "tests/swoole_server/ssl/verify_03.phpt",
    "content": "--TEST--\nswoole_server/ssl: server verify client failed\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_openssl_version_lower_than('1.1.0');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n\nuse Swoole\\Server;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n        exit(\"connect failed\\n\");\n    }\n    @$client->send(\"hello world\");\n    Assert::assert(@$client->recv() == \"\");\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $serv->set([\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n        'ssl_verify_peer' => true,\n        'ssl_allow_self_signed' => true,\n        'ssl_client_cert_file' => SSL_FILE_DIR . '/ca.crt',\n    ]);\n    $serv->on(\"workerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $tid, $data) {\n        $serv->send($fd, \"Swoole $data\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_server/start_twice.phpt",
    "content": "--TEST--\nswoole_server: start twice\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $cli->connect('127.0.0.1', $pm->getFreePort(), 0.5) or die(\"ERROR\");\n    $cli->send(\"signal\") or die(\"ERROR\");\n};\n\n$pm->childFunc = function () use ($pm) {\n    ini_set('swoole.display_errors', 'Off');\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set(array(\n        'worker_num' => 1,\n        'enable_coroutine' => false,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n        $serv->shutdown();\n    });\n    $n = 2;\n    while ($n--) {\n        $serv->start();\n    }\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nWarning: Swoole\\Server::start(): The server have been shutdown, unable to execute Swoole\\Server->start() in %s on line %d\n"
  },
  {
    "path": "tests/swoole_server/stats.phpt",
    "content": "--TEST--\nswoole_server: stats\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/opcode_server.php\";\n$port = get_one_free_port();\n\nstart_server($simple_tcp_server, TCP_SERVER_HOST, $port);\n\n$timer = suicide(2000);\nusleep(500 * 1000);\n\nmakeCoTcpClient(TCP_SERVER_HOST, $port, function(Client $cli) {\n    $r = $cli->send(opcode_encode(\"stats\", []));\n    Assert::assert($r !== false);\n}, function(Client $cli, $recv) {\n    list($op, $data) = opcode_decode($recv);\n    $cli->close();\n    global $timer;\n    TImer::clear($timer);\n    echo \"SUCCESS\";\n});\n\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/stats_file.phpt",
    "content": "--TEST--\nswoole_server: stats_file\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\n\nconst STATS_FILE = __DIR__ . '/stats.log';\n$rm_fn = function () {\n    if (is_file(STATS_FILE)) {\n        unlink(STATS_FILE);\n    }\n};\n$rm_fn();\n\n$pm = new ProcessManager;\n$pm->initFreePorts(1);\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm, $pid) {\n        httpRequest('http://127.0.0.1:' . $pm->getFreePort(0));\n        for ($i = 0; $i < 4; ++$i) {\n            Co::sleep(0.5);\n            $content = @file_get_contents(STATS_FILE);\n            if ('' != $content) {\n                $stats = [];\n                swoole_string($content)->split(\"\\n\")->each(function ($value, $key) use (&$stats) {\n                    [$k, $v] = swoole_string($value)->split(\":\");\n                    $stats[$k] = trim($v);\n                });\n                Assert::keyExists($stats, 'connection_num');\n                Assert::keyExists($stats, 'request_count');\n                break;\n            }\n        }\n    });\n    echo \"\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $mode = SERVER_MODE_RANDOM;\n    $worker_num = rand(1, 4);\n    phpt_var_dump(\"mode: $mode\\nworker_num: $worker_num\\n\");\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(0), $mode);\n    $server->set([\n        'stats_file' => STATS_FILE,\n        'log_file' => DEV_NULL,\n        'worker_num' => $worker_num,\n    ]);\n    $server->on('ManagerStart', function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function ($request, $response) {\n        $response->end(\"<h1>Hello Swoole. #\" . rand(1000, 9999) . \"</h1>\");\n    });\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n$rm_fn();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/stats_file_json.phpt",
    "content": "--TEST--\nswoole_server: stats_file [json]\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\n\nconst STATS_FILE = __DIR__ . '/stats.json';\n$rm_fn = function () {\n    if (is_file(STATS_FILE)) {\n        unlink(STATS_FILE);\n    }\n};\n$rm_fn();\n\n$pm = new ProcessManager;\n$pm->initFreePorts(1);\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm, $pid) {\n        httpRequest('http://127.0.0.1:' . $pm->getFreePort(0));\n        for ($i = 0; $i < 4; ++$i) {\n            Co::sleep(0.5);\n            $content = @file_get_contents(STATS_FILE);\n            if ('' != $content) {\n                $stats = json_decode($content, true);\n                assert_server_stats($stats);\n                break;\n            }\n        }\n    });\n    echo \"\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $mode = SERVER_MODE_RANDOM;\n    $worker_num = rand(1, 4);\n    phpt_var_dump(\"mode: $mode\\nworker_num: $worker_num\\n\");\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(0), $mode);\n    $server->set([\n        'stats_file' => STATS_FILE,\n        'log_file' => DEV_NULL,\n        'worker_num' => $worker_num,\n    ]);\n    $server->on('ManagerStart', function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function ($request, $response) {\n        $response->end(\"<h1>Hello Swoole. #\" . rand(1000, 9999) . \"</h1>\");\n    });\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n$rm_fn();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/stats_file_php.phpt",
    "content": "--TEST--\nswoole_server: stats_file [php]\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\n\nconst STATS_FILE = __DIR__ . '/stats.php';\n$rm_fn = function () {\n    if (is_file(STATS_FILE)) {\n        unlink(STATS_FILE);\n    }\n};\n$rm_fn();\n\n$pm = new ProcessManager;\n$pm->initFreePorts(1);\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm, $pid) {\n        httpRequest('http://127.0.0.1:' . $pm->getFreePort(0));\n        for ($i = 0; $i < 4; ++$i) {\n            Co::sleep(0.5);\n            if (!is_file(STATS_FILE)) {\n                continue;\n            }\n            $stats = include STATS_FILE;\n            if (empty($stats)) {\n                assert_server_stats($stats);\n                break;\n            }\n        }\n    });\n    echo \"\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $mode = SERVER_MODE_RANDOM;\n    $worker_num = rand(1, 4);\n    phpt_var_dump(\"mode: $mode\\nworker_num: $worker_num\\n\");\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(0), $mode);\n    $server->set([\n        'stats_file' => STATS_FILE,\n        'log_file' => DEV_NULL,\n        'worker_num' => $worker_num,\n    ]);\n    $server->on('ManagerStart', function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function ($request, $response) {\n        $response->end(\"<h1>Hello Swoole. #\" . rand(1000, 9999) . \"</h1>\");\n    });\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n$rm_fn();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/stop.phpt",
    "content": "--TEST--\nswoole_server: stop\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\n\n$simple_tcp_server = __DIR__ . \"/../include/api/swoole_server/opcode_server.php\";\n$port = get_one_free_port();\n\nstart_server($simple_tcp_server, TCP_SERVER_HOST, $port);\n\n$timer = suicide(2000);\nusleep(500 * 1000);\n\nmakeCoTcpClient(TCP_SERVER_HOST, $port, function(Client $cli) {\n    $r = $cli->send(opcode_encode(\"stop\", [2]));\n    Assert::assert($r !== false);\n}, function(Client $cli, $recv) {\n    list($op, $data) = opcode_decode($recv);\n    Assert::true($data);\n    $cli->close();\n    global $timer;\n    Timer::clear($timer);\n    echo \"SUCCESS\\n\";\n});\nEvent::wait();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/stop_in_workerStart.phpt",
    "content": "--TEST--\nswoole_server: stop worker in worker start\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$server = new Server('127.0.0.1', get_one_free_port(), SWOOLE_PROCESS);\n\n$atomic = new Swoole\\Atomic(0);\n\n$server->set([\n    'worker_num' => 1\n]);\n\n$server->on('Receive', function (Server $server, int $fd, int $reactorId, string $data) {});\n\n$server->on('start', function () use ($atomic) {});\n\n$server->on('WorkerStart', function (Server $server, int $workerId) {\n    usleep(50000);\n    $server->stop();\n});\n\n$server->on('WorkerStop', function (Server $server, int $workerId) use ($atomic) {\n    usleep(200000);\n    if ($atomic->add(1) == 1) {\n        $server->shutdown();\n    }\n});\n$server->start();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_server/systemd_fds.phpt",
    "content": "--TEST--\nswoole_server: systemd fds\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_in_ci();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Client;\n\ndefine('UNIX_SOCK_1', getenv('HOME') . '/swoole.test.uinx_stream.sock');\ndefine('UNIX_SOCK_2', getenv('HOME') . '/swoole.test.uinx_dgram.sock');\ndefine('HAVE_IPV6', boolval(@stream_socket_server('tcp://[::1]:0')));\n\nregister_shutdown_function(function () {\n    if (is_file(UNIX_SOCK_1)) {\n        unlink(UNIX_SOCK_1);\n    }\n    if (is_file(UNIX_SOCK_2)) {\n        unlink(UNIX_SOCK_2);\n    }\n});\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->initFreePorts(2);\nif (HAVE_IPV6) {\n    $pm->initFreeIPv6Ports(2);\n}\n$pm->parentFunc = function ($pid) use ($pm) {\n    $test_func = function ($type, $host, $port = 0) {\n        $client = new Client($type);\n        Assert::notEmpty($client->connect($host, $port));\n        $client->send(\"SUCCESS\");\n        Assert::eq($client->recv(), 'SUCCESS' . PHP_EOL);\n        $client->close();\n    };\n\n    $test_func(SWOOLE_SOCK_TCP, \"127.0.0.1\", $pm->getFreePort(0));\n    $test_func(SWOOLE_SOCK_UDP, \"127.0.0.1\", $pm->getFreePort(1));\n\n    if (HAVE_IPV6) {\n        $test_func(SWOOLE_SOCK_TCP6, \"::1\", $pm->getFreePort(2));\n        $test_func(SWOOLE_SOCK_UDP6, \"::1\", $pm->getFreePort(3));\n    }\n\n    $test_func(SWOOLE_SOCK_UNIX_STREAM, UNIX_SOCK_1);\n    $test_func(SWOOLE_SOCK_UNIX_DGRAM, UNIX_SOCK_2);\n\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $sockets = [];\n    $start_fd = swoole_array(scandir('/proc/self/fd'))->sort()->last();\n    putenv('LISTEN_FDS_START=' . $start_fd);\n\n    $sockets[] = stream_socket_server('tcp://127.0.0.1:' . $pm->getFreePort(0), $errno, $errstr);\n    $sockets[] = stream_socket_server('udp://0.0.0.0:' . $pm->getFreePort(1), $errno, $errstr, STREAM_SERVER_BIND);\n    if (HAVE_IPV6) {\n        $sockets[] = stream_socket_server('tcp://[::1]:' . $pm->getFreePort(2), $errno, $errstr);\n        $sockets[] = stream_socket_server('udp://[::]:' . $pm->getFreePort(3), $errno, $errstr, STREAM_SERVER_BIND);\n    }\n    $sockets[] = stream_socket_server('unix://' . UNIX_SOCK_1, $errno, $errstr);\n    $sockets[] = stream_socket_server('udg://' . UNIX_SOCK_2, $errno, $errstr, STREAM_SERVER_BIND);\n\n    putenv('LISTEN_PID=' . posix_getpid());\n    putenv('LISTEN_FDS=' . count($sockets));\n\n    $serv = new Server('SYSTEMD', 0, SWOOLE_BASE);\n\n    $serv->set([\"worker_num\" => 1, 'log_file' => '/dev/null',]);\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $serv->on(\"packet\", function (Server $serv, $data, $addr) {\n        // var_dump($addr);\n        $serv->sendto($addr['address'], $addr['port'] ?? 0, 'SUCCESS' . PHP_EOL);\n    });\n\n    $serv->on(\"receive\", function (Server $serv, $fd, $tid, $data) {\n        $serv->send($fd, 'SUCCESS' . PHP_EOL);\n    });\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/task/base.phpt",
    "content": "--TEST--\nswoole_server/task: task & finish\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\n$simple_tcp_server = __DIR__ . \"/../../include/api/swoole_server/tcp_task_server.php\";\n$port = get_one_free_port();\n$closeServer = start_server($simple_tcp_server, TCP_SERVER_HOST, $port);\n\nCo\\Run(function () use($port, $closeServer) {\n    $cli = new Client(SWOOLE_SOCK_TCP);\n    $r = $cli->connect(TCP_SERVER_HOST, $port);\n    Assert::true($r);\n    $cli->send(\"Test swoole_server::task Interface.\");\n    $data = $cli->recv();\n    Assert::same($data, \"OK\");\n    $cli->close();\n    Assert::false($cli->isConnected());\n    echo \"SUCCESS\\n\";\n});\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/task/bug_2585.phpt",
    "content": "--TEST--\nswoole_server/task: bug Github#2585\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_no_database();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    for ($i = MAX_CONCURRENCY_LOW; $i--;) {\n        go(function () use ($pm) {\n            $ret = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}\");\n            Assert::same($ret, 'Hello Swoole!');\n        });\n    }\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $http->set([\n        'log_file' => '/dev/null',\n        'task_worker_num' => 4,\n        'enable_coroutine' => false,\n        'task_enable_coroutine' => true\n    ]);\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($http) {\n        Assert::assert($response->detach());\n        if (mt_rand(0, 1)) {\n            $http->task($response->fd);\n        } else {\n            $http->task($response->fd, -1, function ($server, $taskId, $data) {\n                list($fd, $data) = $data;\n                $response = Swoole\\Http\\Response::create($fd);\n                $response->end($data);\n            });\n        }\n    });\n    $http->on('task', function (Swoole\\Http\\Server $server, Swoole\\Server\\Task $task) {\n        $fd = $task->data;\n        if (mt_rand(0, 1)) {\n            $task->finish([$fd, 'Hello Swoole!']);\n        } else {\n            $response = Swoole\\Http\\Response::create($fd);\n            $pdo = new PDO(\n                \"mysql:host=\" . MYSQL_SERVER_HOST . \";port=\" . MYSQL_SERVER_PORT . \";dbname=\" . MYSQL_SERVER_DB . \";charset=utf8\",\n                MYSQL_SERVER_USER, MYSQL_SERVER_PWD\n            );\n            $stmt = $pdo->query('SELECT \"Hello Swoole!\"');\n            Assert::assert($stmt->execute());\n            $ret = $stmt->fetchAll(PDO::FETCH_COLUMN)[0];\n            $response->end($ret);\n        }\n    });\n    $http->on('finish', function ($server, $taskId, $data) {\n        list($fd, $ret) = $data;\n        $response = Swoole\\Http\\Response::create($fd);\n        $response->end($ret);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/task/callback_is_null.phpt",
    "content": "--TEST--\nswoole_server/task: task callback is null\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $cli->set(['open_eof_check' => true, \"package_eof\" => \"\\r\\n\\r\\n\"]);\n    $cli->connect('127.0.0.1', $pm->getFreePort(), 0.5) or die(\"ERROR\");\n\n    $cli->send(\"task-01\") or die(\"ERROR\");\n    $res = json_decode(trim($cli->recv()), true);\n    Assert::same($res['code'], 0);\n    Assert::same($res['message'], 'hello world');\n    echo \"SUCCESS\\n\";\n\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    ini_set('swoole.display_errors', 'Off');\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        \"worker_num\" => 1,\n        'task_worker_num' => 2,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n        $serv->task(['type' => 'array', 'value' => $data, 'fd' => $fd], -1, null);\n    });\n    $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) {\n        return array(\"code\" => 0, 'message' => 'hello world', 'sid' => uniqid(), 'fd' => $data['fd']);\n    });\n    $serv->on('finish', function (Server $serv, $task_id, $data) {\n        $serv->send($data['fd'], json_encode($data) . \"\\r\\n\\r\\n\");\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/task/enable_coroutine.phpt",
    "content": "--TEST--\nswoole_server/task: task [enable_coroutine]\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\n$pm = new SwooleTest\\ProcessManager;\n\n$randoms = [];\nfor ($n = MAX_REQUESTS; $n--;)\n{\n    $randoms[] = random_bytes(mt_rand(0, 65536));\n}\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($n = MAX_REQUESTS; $n--;) {\n        go(function () use ($pm, $n) {\n            $c = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            $c->set(['timeout' => 5,]);\n            Assert::assert($c->get('/task?n='.$n));\n            Assert::same($c->body, \"OK\");\n        });\n    }\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm, $randoms) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 1,\n        'task_worker_num' => 1,\n        'task_enable_coroutine' => true,\n    ]);\n    $server->on('workerStart', function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($server, $randoms) {\n        $n = $request->get['n'];\n        list($ret_n, $ret_random) = $server->taskwait($n, 1);\n        if ($ret_n !== $n) {\n            $response->end(\"ERROR MATCH {$ret_n} with {$n}\");\n            return;\n        } elseif ($ret_random !== $randoms[$n]) {\n            $response->end(\"ERROR EQUAL {$ret_n}(\" . strlen($ret_random) . \") with {$n}(\" . strlen($randoms[$n]) . \")\");\n            return;\n        }\n        $response->end('OK');\n    });\n    $server->on('task', function (Swoole\\Http\\Server $server, Swoole\\Server\\Task $task) use ($pm, $randoms) {\n        Assert::same($task->worker_id, 0);\n        Assert::assert($task->flags > 0);\n        Assert::assert($task->id >= 0);\n        $n = $task->data;\n        co::sleep(0.002);\n        $task->finish([$n, $randoms[$n]]);\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/task/finish_timeout.phpt",
    "content": "--TEST--\nswoole_server/task: finish timeout\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\nskip_if_darwin_todo();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Client;\nuse Swoole\\Server;\nuse SwooleTest\\ProcessManager;\n\ndefine('TMP_LOG_FILE', __DIR__ . '/log_file');\nftruncate(fopen(TMP_LOG_FILE, 'w+'), 0);\n\n$pm = new ProcessManager();\n$pm->parentFunc = function ($pid) use ($pm) {\n    $cli = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $cli->connect('127.0.0.1', $pm->getFreePort(), 10) or exit('ERROR');\n    $cli->send('task-01') or exit('ERROR');\n    Assert::same($cli->recv(), 'hello world');\n    $cli->close();\n    usleep(1000000);\n    echo file_get_contents(TMP_LOG_FILE);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    swoole_ignore_error(SWOOLE_ERROR_SERVER_NO_IDLE_WORKER);\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'log_file' => TMP_LOG_FILE,\n        'log_level' => SWOOLE_LOG_NOTICE,\n        'task_worker_num' => 1,\n        'socket_send_timeout' => 1.0,\n        'socket_buffer_size' => 128 * 1024,\n        'worker_num' => 1,\n        'enable_coroutine' => false,\n    ]);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('receive', function (Server $server, $fd, $tid, $data) {\n        $server->task($fd);\n        usleep(1100000);\n    });\n    $server->on('task', function ($server, $task_id, $worker_id, string $fd) {\n        $n = 200;\n        $size = IS_MAC_OS ? 2000 : 8000;\n        while ($n--) {\n            if (!$server->finish(str_repeat('A', $size))) {\n                break;\n            }\n        }\n        $server->send($fd, 'hello world');\n    });\n    $server->on('finish', function ($server, $task_id, $data) {\n        \n    });\n    $server->on('close', function () {});\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\nunlink(TMP_LOG_FILE);\n?>\n--EXPECTF--\n[%s]\tWARNING\tServer::finish() (ERRNO %d): send result to worker timed out\n"
  },
  {
    "path": "tests/swoole_server/task/huge_data.phpt",
    "content": "--TEST--\nswoole_server/task: huge data\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->setRandomFunc('get_big_random');\n$pm->initRandomData(MAX_REQUESTS_LOW);\n$pm->parentFunc = function (int $pid) use ($pm) {\n    $uri = \"http://127.0.0.1:{$pm->getFreePort()}\";\n    go(function () use ($pm, $uri) {\n        for ($c = MAX_REQUESTS_LOW; $c--;) {\n            $data = $pm->getRandomData();\n            $body = httpGetBody($uri, ['data' => $data]);\n            Assert::same($body, $data);\n        }\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $http->set([\n        'log_file' => '/dev/null',\n        'task_worker_num' => 4\n    ]);\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($http) {\n        Assert::assert($response->detach());\n        $scope = IS_IN_CI ? [4, 16] : [16, 64];\n        $repeat = mt_rand(...$scope);\n        $http->task([\n            'fd' => $response->fd,\n            'repeat' => $repeat,\n            'data' => str_repeat($request->rawContent(), $repeat)\n        ]);\n    });\n    $http->on('task', function ($a, $b, $c, array $info) {\n        $response = Swoole\\Http\\Response::create($info['fd']);\n        $response->end(substr($info['data'], 0, strlen($info['data']) / $info['repeat']));\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/task/idle_worekr_num.phpt",
    "content": "--TEST--\nswoole_server/task: idle_worker_num\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n\nuse Swoole\\Server;\nuse Swoole\\Client;\nuse Swoole\\Atomic;\nuse Swoole\\Timer;\n\n$atomic = new Atomic();\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n        exit(\"connect failed\\n\");\n    }\n    $client->send(\"hello world\");\n    $data = $client->recv();\n    Assert::assert($data);\n    $json = json_decode($data);\n    Assert::eq($json->task_idle_worker_num, 1);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $atomic) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $serv->set([\n        'worker_num' => 1,\n        'task_worker_num' => 3,\n        'log_level' => SWOOLE_LOG_ERROR,\n        'task_use_object' => true,\n    ]);\n    $serv->on(\"workerStart\", function (Server $serv, $wid) use ($pm, $atomic) {\n        if ($atomic->add() == $serv->setting['worker_num'] + $serv->setting['task_worker_num']) {\n            Timer::after(1, function () use ($pm) {\n                $pm->wakeup();\n            });\n        }\n    });\n    $serv->on('receive', function (Server $serv, $fd, $tid, $data) {\n        $serv->task(['sleep' => 1,]);\n        $serv->task(['fd' => $fd,]);\n    });\n    $serv->on('Task', function (Server $server, Server\\Task $task) {\n        if (isset($task->data['sleep'])) {\n            sleep($task->data['sleep']);\n        } else {\n            $server->send($task->data['fd'], json_encode($server->stats()));\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/task/invalid_packet.phpt",
    "content": "--TEST--\nswoole_server/task: invalid packet\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\nskip_if_function_not_exist('msg_get_queue');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nconst MSGQ_KEY = 0x70001001;\n\n$file = __DIR__ . '/tmp.log';\nuse Swoole\\Atomic;\nuse Swoole\\Exception;\nuse Swoole\\Server;\nuse Swoole\\Server\\Task;\nuse SwooleTest\\ProcessManager;\n\n$result = new Atomic(0);\n$pm = new ProcessManager();\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $data = '{\"tid\":17732683638813521,\"out_trade_no\":\"dm5601993521\",\"runMethod\":\"\\Action\\Mpay\\Uni\\UniApiV3Act:jsonDrive\"}';\n    $queueId = msg_get_queue(MSGQ_KEY);\n    if ($queueId === false) {\n        throw new Exception('msg_get_queue() failed.');\n    }\n    Assert::true(msg_send($queueId, 1, str_repeat('\\n', 64) . 'hello' . $data));\n    Assert::true(msg_send($queueId, 1, Task::pack($data), false));\n    $pm->wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $file, $result) {\n    ini_set('swoole.display_errors', 'Off');\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'worker_num' => 2,\n        'task_worker_num' => 1,\n        'task_ipc_mode' => 3,\n        'message_queue_key' => MSGQ_KEY,\n        'log_file' => $file,\n    ]);\n    $serv->on('WorkerStart', function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {});\n    $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) use ($pm, $result) {\n        $pm->wakeup();\n        $result->add(1);\n    });\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\nusleep(100000);\n// echo file_get_contents($file);\n\nAssert::true(swoole_string(file_get_contents($file))->contains('bad task packet'));\nunlink($file);\nAssert::eq($result->get(), 1);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/task/kill_01.phpt",
    "content": "--TEST--\nswoole_server/task: kill task worker [SWOOLE_BASE]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_darwin();\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Server;\nconst PROC_NAME = 'swoole_unittest_server_task_worker';\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($i = 0; $i < 5; $i++) {\n        // 杀死进程\n        kill_process_by_name(PROC_NAME);\n        usleep(10000);\n        //判断进程是否存在\n        Assert::assert(get_process_pid_by_name(PROC_NAME) > 0);\n    }\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $cli->connect('127.0.0.1', $pm->getFreePort(), 10) or die(\"ERROR\");\n    $cli->send(\"task-01\") or die(\"ERROR\");\n    Assert::same($cli->recv(), \"task-01\");\n    $cli->close();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\"worker_num\" => 1, 'log_file' => TEST_LOG_FILE, 'task_worker_num' => 1,]);\n    $serv->on(\"WorkerStart\", function (Server $serv, $worker_id) use ($pm) {\n        if ($worker_id = 1) {\n            swoole_set_process_name(PROC_NAME);\n            $pm->wakeup();\n        }\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $reactorId, $data)\n    {\n        $serv->task(['fd' => $fd, 'data' => $data]);\n    });\n    $serv->on('task', function (Server $serv, $task_id, $worker_id, $data)\n    {\n        $serv->send($data['fd'], $data['data']);\n    });\n    $serv->on('finish', function (Server $serv, $fd, $rid, $data)\n    {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/task/kill_02.phpt",
    "content": "--TEST--\nswoole_server/task: kill task worker 01 [SWOOLE_BASE]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_darwin();\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Server;\nconst PROC_NAME = 'swoole_unittest_server_task_worker';\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($i = 0; $i < 5; $i++)\n    {\n        //杀死进程\n        kill_process_by_name(PROC_NAME);\n        usleep(10000);\n        //判断进程是否存在\n        Assert::assert(get_process_pid_by_name(PROC_NAME) > 0);\n    }\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\"worker_num\" => 1, 'log_file' => TEST_LOG_FILE, 'task_worker_num' => 1,]);\n    $serv->on(\"WorkerStart\", function (Server $serv, $worker_id) use ($pm) {\n        if ($worker_id = 1) {\n            swoole_set_process_name(PROC_NAME);\n            $pm->wakeup();\n        }\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $reactorId, $data)\n    {\n    });\n    $serv->on('task', function (Server $serv, $task_id, $worker_id, $data)\n    {\n        return array(\"code\" => 0, 'message' => 'hello world', 'sid' => uniqid());\n    });\n    $serv->on('finish', function (Server $serv, $fd, $rid, $data)\n    {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/task/kill_task_worker_02.phpt",
    "content": "--TEST--\nswoole_server/task: kill task worker [SWOOLE_PROCESS]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_darwin();\nskip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Server;\nconst PROC_NAME = 'swoole_unittest_server_task_worker';\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($i = 0; $i < 5; $i++)\n    {\n        //杀死进程\n        kill_process_by_name(PROC_NAME);\n        usleep(10000);\n        //判断进程是否存在\n        Assert::assert(get_process_pid_by_name(PROC_NAME) > 0);\n    }\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\"worker_num\" => 1, 'log_file' => '/dev/null', 'task_worker_num' => 1,]);\n    $serv->on(\"WorkerStart\", function (Server $serv, $worker_id) use ($pm) {\n        if ($worker_id = 1)\n        {\n            swoole_set_process_name(PROC_NAME);\n            $pm->wakeup();\n        }\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $reactorId, $data)\n    {\n    });\n    $serv->on('task', function (Server $serv, $task_id, $worker_id, $data)\n    {\n        return array(\"code\" => 0, 'message' => 'hello world', 'sid' => uniqid());\n    });\n    $serv->on('finish', function (Server $serv, $fd, $rid, $data)\n    {\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/task/scheduler_warning.phpt",
    "content": "--TEST--\nswoole_server/task: scheduler warning\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Client;\nuse Swoole\\Server;\n\nconst N = 3;\nconst LOG_FILE =  __DIR__.'/test.log';\n\n$counter = new Swoole\\Atomic(0);\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    $client = new Client(SWOOLE_SOCK_UDP, SWOOLE_SOCK_SYNC);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort(), 2)) {\n        exit(\"connect failed\\n\");\n    }\n    $client->send(\"ping\");\n    echo $client->recv();\n    sleep(2);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $counter) {\n    $serv = new Server(\"127.0.0.1\", $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_UDP);\n\n    $serv->set([\n        'worker_num' => 1,\n        'task_worker_num' => 2,\n        'log_file' => LOG_FILE,\n        'log_level' => SWOOLE_LOG_NOTICE,\n    ]);\n\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        if ($serv->taskworker) {\n            $pm->wakeup();\n        }\n    });\n\n    $serv->on('Packet', function (Server $serv, string $data, array $clientInfo) {\n        $n = N;\n        while ($n--) {\n            $serv->task(['data' => $data, 'client' => $clientInfo]);\n            usleep(10000);\n        }\n    });\n\n    $serv->on('Task', function (Server $serv, $taskId, int $workerId, $data) use ($pm, $counter) {\n        static $sleep = false;\n        if (!$sleep) {\n            $sleep = true;\n            sleep(1);\n        }\n        if ($counter->add() == N) {\n            $serv->sendto($data['client']['address'], $data['client']['port'], \"DONE\\n\");\n        }\n    });\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\necho file_get_contents(LOG_FILE);\nunlink(LOG_FILE);\n?>\n--EXPECTF--\nDONE\n[%s]\tWARNING\tServer::timer_callback() (ERRNO %d): No idle task worker is available\n"
  },
  {
    "path": "tests/swoole_server/task/task_callback.phpt",
    "content": "--TEST--\nswoole_server/task: task callback\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nuse Swoole\\Server;\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $cli->set(['open_eof_check' => true, \"package_eof\" => \"\\r\\n\\r\\n\"]);\n//    $cli->set(['open_eof_split' => true, 'package_eof' => \"\\r\\n\\r\\n\"]);\n    $cli->connect('127.0.0.1', $pm->getFreePort(), 0.5) or die(\"ERROR\");\n\n    $cli->send(\"task-01\") or die(\"ERROR\");\n\n    $res = json_decode(trim($cli->recv()), true);\n    Assert::same($res['code'], 0);\n    Assert::same($res['message'], 'hello world');\n\n    echo \"SUCCESS\\n\";\n\n    $res = json_decode(trim($cli->recv()), true);\n    Assert::same($res['code'], 0);\n    Assert::same($res['message'], 'hello world');\n    echo \"SUCCESS\\n\";\n\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    ini_set('swoole.display_errors', 'Off');\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        \"worker_num\" => 1,\n        'task_worker_num' => 2,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data)\n    {\n        $serv->task(['type' => 'array', 'value' => $data], -1, function ($serv, $taskId, $data) use ($fd)  {\n            $serv->send($fd, json_encode($data).\"\\r\\n\\r\\n\");\n        });\n        $serv->task(['type' => 'array', 'value' => $data, 'worker_id' => 0], 0, function ($serv, $taskId, $data) use ($fd)  {\n            $serv->send($fd, json_encode($data).\"\\r\\n\\r\\n\");\n        });\n    });\n\n    $serv->on('task', function (Server $serv, $task_id, $worker_id, $data)\n    {\n        return array(\"code\" => 0, 'message' => 'hello world', 'sid' => uniqid());\n    });\n\n    $serv->on('finish', function (Server $serv, $fd, $rid, $data)\n    {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/task/task_co.phpt",
    "content": "--TEST--\nswoole_server/task: task_co\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\n$pm = new SwooleTest\\ProcessManager;\n\n$randoms = [];\nfor ($n = MAX_REQUESTS; $n--;) {\n    $randoms[] = random_bytes(mt_rand(0, 65536));\n}\n\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        for ($n = MAX_REQUESTS; $n--;) {\n            if (!Assert::assert(($res = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/task?n={$n}\")) === 'OK')) {\n                echo \"{$res}\\n\";\n                break;\n            }\n        }\n    });\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $randoms) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 4,\n        'task_worker_num' => 4,\n        'task_enable_coroutine' => true\n    ]);\n    $server->on('workerStart', function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request',\n        function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($server, $randoms) {\n            $n = $request->get['n'];\n            switch ($request->server['path_info']) {\n                case '/task':\n                {\n                    list($ret_n, $ret_random) = $server->taskCo([$n], 1)[0];\n                    if ($ret_n !== $n) {\n                        $response->end(\"ERROR MATCH {$ret_n} with {$n}\");\n                        return;\n                    } elseif ($ret_random !== $randoms[$n]) {\n                        $response->end(\"ERROR EQUAL {$ret_n}(\" . strlen($ret_random) . \") with {$n}(\" . strlen($randoms[$n]) . \")\");\n                        return;\n                    }\n                    $response->end('OK');\n                    break;\n                }\n                case '/random':\n                {\n                    $response->end($randoms[$n]);\n                    break;\n                }\n            }\n        });\n    $server->on('task', function (Swoole\\Http\\Server $server, Swoole\\Server\\Task $task) use ($pm) {\n        $task->finish([$task->data, httpGetBody('http://127.0.0.1:' . $pm->getFreePort() . \"/random?n={$task->data}\")]);\n    });\n    $server->on('finish', function () {\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/task/task_enable_coroutine.phpt",
    "content": "--TEST--\nswoole_server/task: pdo in task and http response detach\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_pdo_not_support_mysql8();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    for ($i = MAX_CONCURRENCY_LOW; $i--;) {\n        go(function () use ($pm) {\n            $ret = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}\");\n            Assert::same($ret, 'Hello Swoole!');\n        });\n    }\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $http->set([\n        'log_file' => '/dev/null',\n        'task_worker_num' => 4,\n        'task_enable_coroutine' => true\n    ]);\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($http) {\n        Assert::assert($response->detach());\n        if (mt_rand(0, 1)) {\n            $http->task($response->fd);\n        } else {\n            $http->task($response->fd, -1, function ($server, $taskId, $data) {\n                list($fd, $data) = $data;\n                $response = Swoole\\Http\\Response::create($fd);\n                $response->end($data);\n            });\n        }\n    });\n    $http->on('task', function (Swoole\\Http\\Server $server, Swoole\\Server\\Task $task) {\n        $fd = $task->data;\n        if (mt_rand(0, 1)) {\n            $task->finish([$fd, 'Hello Swoole!']);\n        } else {\n            $response = Swoole\\Http\\Response::create($fd);\n            $pdo = new PDO(\n                \"mysql:host=\" . MYSQL_SERVER_HOST . \";port=\" . MYSQL_SERVER_PORT . \";dbname=\" . MYSQL_SERVER_DB . \";charset=utf8\",\n                MYSQL_SERVER_USER, MYSQL_SERVER_PWD\n            );\n            $stmt = $pdo->query('SELECT \"Hello Swoole!\"');\n            Assert::assert($stmt->execute());\n            $ret = $stmt->fetchAll(PDO::FETCH_COLUMN)[0];\n            $response->end($ret);\n        }\n    });\n    $http->on('finish', function ($server, $taskId, $data) {\n        list($fd, $ret) = $data;\n        $response = Swoole\\Http\\Response::create($fd);\n        $response->end($ret);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/task/task_enable_coroutine_return.phpt",
    "content": "--TEST--\nswoole_server/task: task enable coroutine with return value to finish\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_pdo_not_support_mysql8();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    for ($i = MAX_CONCURRENCY_LOW; $i--;) {\n        go(function () use ($pm) {\n            $ret = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}\");\n            Assert::assert($ret === 'Hello Swoole!');\n        });\n    }\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $http->set([\n        'log_file' => '/dev/null',\n        'task_worker_num' => 4,\n        'task_enable_coroutine' => true\n    ]);\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($http) {\n        Assert::assert($response->detach());\n        $http->task($response->fd, -1, function ($server, $taskId, $data) {\n            list($fd, $data) = $data;\n            $response = Swoole\\Http\\Response::create($fd);\n            $response->end($data);\n        });\n    });\n    $http->on('task', function (Swoole\\Http\\Server $server, Swoole\\Server\\Task $task) {\n        defer(function ($data) {\n            if (!empty($data)) {\n                list($task, $result) = $data;\n                $task->finish($result);\n            }\n        });\n        $fd = $task->data;\n        $pdo = new PDO(\n            \"mysql:host=\" . MYSQL_SERVER_HOST . \";port=\" . MYSQL_SERVER_PORT . \";dbname=\" . MYSQL_SERVER_DB . \";charset=utf8\",\n            MYSQL_SERVER_USER, MYSQL_SERVER_PWD\n        );\n        $stmt = $pdo->query('SELECT \"Hello Swoole!\"');\n        Assert::assert($stmt->execute());\n        $ret = $stmt->fetchAll(PDO::FETCH_COLUMN)[0];\n        return [$task, [$fd, $ret]];\n    });\n    $http->on('finish', function ($server, $taskId, $data) {\n        list($fd, $ret) = $data;\n        $response = Swoole\\Http\\Response::create($fd);\n        $response->end($ret);\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/task/task_in_manager.phpt",
    "content": "--TEST--\nswoole_server/task: task in manager\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server;\n$pm = new SwooleTest\\ProcessManager;\n$pm->setWaitTimeout(60);\n$pm->parentFunc = function ($pid) use ($pm) {\n    echo \"SUCCESS\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    ini_set('swoole.display_errors', 'Off');\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        \"worker_num\" => 1,\n        'task_worker_num' => 2,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on(\"managerStart\", function (Server $serv)  use ($pm)\n    {\n        $serv->task(['type' => 'array', 'value' => 'manager']);\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data)\n    {\n\n    });\n\n    $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) use($pm)\n    {\n        Assert::false($serv->finish(\"OK\"));\n        $pm->wakeup();\n    });\n\n    $serv->on('finish', function (Server $serv, $fd, $rid, $data)\n    {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/task/task_in_master.phpt",
    "content": "--TEST--\nswoole_server/task: task in master\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->setWaitTimeout(60);\n$pm->parentFunc = function ($pid) use ($pm) {\n    echo \"SUCCESS\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    ini_set('swoole.display_errors', 'Off');\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        \"worker_num\" => 1,\n        'task_worker_num' => 2,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on(\"start\", function (Server $serv)  use ($pm)\n    {\n        $serv->task(['type' => 'array', 'value' => 'master']);\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data)\n    {\n\n    });\n\n    $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) use($pm)\n    {\n        Assert::false($serv->finish(\"OK\"));\n        $pm->wakeup();\n    });\n\n    $serv->on('finish', function (Server $serv, $fd, $rid, $data)\n    {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/task/task_in_task_worker.phpt",
    "content": "--TEST--\nswoole_server/task: task in task worker\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->setWaitTimeout(60);\n$pm->parentFunc = function ($pid) use ($pm) {\n    echo \"SUCCESS\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    ini_set('swoole.display_errors', 'Off');\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        \"worker_num\" => 1,\n        'task_worker_num' => 1,\n        'log_file' => '/dev/null',\n    ));\n\n    $serv->on(\"WorkerStart\", function (Server $serv, $wid) use ($pm) {\n        if ($serv->taskworker) {\n            Assert::false(@$serv->task(['type' => 'array', 'value' => 'task worker']));\n            $pm->wakeup();\n        }\n    });\n\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data)\n    {\n\n    });\n\n    $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) use($pm)\n    {\n\n    });\n\n    $serv->on('finish', function (Server $serv, $fd, $rid, $data)\n    {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/task/task_in_user_process.phpt",
    "content": "--TEST--\nswoole_server/task: task in user process\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->setWaitTimeout(60);\n$pm->parentFunc = function ($pid) use ($pm) {\n    echo \"SUCCESS\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    ini_set('swoole.display_errors', 'Off');\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        \"worker_num\" => 1,\n        'task_worker_num' => 2,\n        'log_file' => '/dev/null',\n    ));\n\n    $process = new \\Swoole\\Process(function ($process) use ($serv)\n    {\n        $serv->task(['type' => 'array', 'value' => 'user process']);\n        sleep(60);\n    });\n\n    $serv->addProcess($process);\n\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data)\n    {\n\n    });\n\n    $serv->on('task', function (Swoole\\Server $serv, $task_id, $worker_id, $data) use($pm)\n    {\n        Assert::false($serv->finish(\"OK\"));\n        $pm->wakeup();\n    });\n\n    $serv->on('finish', function (Swoole\\Server $serv, $fd, $rid, $data)\n    {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/task/task_ipc_mode_2.phpt",
    "content": "--TEST--\nswoole_server/task: task_ipc_mode = 2\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$pm = new SwooleTest\\ProcessManager;\n$atomic = new Atomic;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}/\");\n        $pm->kill();\n    });\n};\n$pm->childFunc = function () use ($pm, $atomic) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $server->set([\n        'log_file' => '/dev/null',\n        'open_tcp_nodelay' => true,\n        'task_worker_num' => 4,\n        'worker_num' => 2,\n        'task_ipc_mode' => 2,\n        'dispatch_mode' => 2\n    ]);\n    $server->on('workerStart', function () use ($pm, $atomic) {\n        if ($atomic->add() == 6) {\n            $pm->wakeup();\n        }\n    });\n    $server->on('request', function (Request $request, Response $response) use ($server) {\n        $response->detach();\n        $server->task($response->fd);\n    });\n    $server->on('task', function ($server, $task_id, $worker_id, string $fd) {\n        $response = Response::create($fd);\n        $response->end(\"Hello Swoole!\\n\");\n    });\n    $server->on('finish', function () { });\n    $server->on('close', function () { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nHello Swoole!\n"
  },
  {
    "path": "tests/swoole_server/task/task_ipc_mode_3.phpt",
    "content": "--TEST--\nswoole_server/task: task_ipc_mode = 3\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Http\\Response;\nuse Swoole\\Http\\Request;\n\n$atomic = new Swoole\\Atomic;\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    go(function () use ($pm) {\n        echo httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}\");\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm, $atomic) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'log_file' => '/dev/null',\n        'open_tcp_nodelay' => true,\n        'worker_num' => 3,\n        'task_worker_num' => 4,\n        'task_ipc_mode' => 3,\n        'dispatch_mode' => 2\n    ]);\n    $server->on('workerStart', function () use ($pm, $atomic) {\n        if ($atomic->add() == 7) {\n            $pm->wakeup();\n        }\n    });\n    $server->on('request', function (Request $request, Response $response) use ($server) {\n        $response->detach();\n        $server->task($response->fd);\n    });\n    $server->on('task', function ($server, $task_id, $worker_id, string $fd) {\n        $response = Response::create($server, $fd);\n        $response->end(\"Hello Swoole!\\n\");\n    });\n    $server->on('finish', function () { });\n    $server->on('close', function () { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nHello Swoole!\n"
  },
  {
    "path": "tests/swoole_server/task/task_max_request.phpt",
    "content": "--TEST--\nswoole_server/task: task_max_request\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nconst N = 4000;\n\nuse Swoole\\Atomic;\nuse Swoole\\Process;\nuse Swoole\\Server;\n\n$counter1 = new Atomic(); // onTask\n$counter2 = new Atomic(); // onFinish\n$counter3 = new Atomic(); // task num\n\n$process = new Process(function () {\n    $serv = new Server('127.0.0.1', get_one_free_port(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'task_max_request' => 200,\n        'task_worker_num' => 4,\n        'log_file' => TEST_LOG_FILE,\n    ]);\n\n    $serv->on('WorkerStart', function (Server $serv, $worker_id) {\n        if (!$serv->taskworker) {\n            for ($i = 0; $i < N; $i++) {\n                $serv->task(['type' => 'php', 'data' => RandStr::gen(100)]);\n            }\n        } else {\n            global $counter3;\n            $counter3->add(1);\n        }\n    });\n\n    $serv->on('Receive', function (Server $serv, $fd, $reactorId, $data) {\n        $serv->send($fd, \"Server: {$data}\");\n    });\n\n    $serv->on('Task', function (Server $serv, $task_id, $workerId, $data) {\n        global $counter1;\n        $counter1->add(1);\n        return json_encode($data);\n    });\n\n    $serv->on('Finish', function (Server $swooleServer, $workerId, $task_data) {\n        global $counter2;\n        $counter2->add(1);\n        if ($counter2->get() == N) {\n            $swooleServer->shutdown();\n        }\n    });\n\n    $serv->start();\n}, false, false);\n$process->start();\n\nProcess::wait();\nAssert::same($counter1->get(), 4000);\nAssert::same($counter2->get(), 4000);\nAssert::assert($counter3->get() > 15);\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/task/task_pack.phpt",
    "content": "--TEST--\nswoole_server/task: task pack\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\nskip_if_function_not_exist('msg_get_queue');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nconst MSGQ_KEY = 0x70001001;\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$result = new Swoole\\Atomic(0);\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $task = new class(MSGQ_KEY, 0) {\n        protected $queueId;\n        protected $workerId;\n\n        function __construct($key, $workerId) {\n            $this->queueId = msg_get_queue($key);\n            if ($this->queueId === false) {\n                throw new \\Swoole\\Exception(\"msg_get_queue() failed.\");\n            }\n            $this->workerId = $workerId;\n        }\n\n        function dispatch($data) {\n            if (!msg_send($this->queueId, $this->workerId + 1, Swoole\\Server\\Task::pack($data), false)) {\n                return false;\n            } else {\n                return true;\n            }\n        }\n    };\n    //数组\n    $task->dispatch(array('data' => str_repeat('A', 1024), 'type' => 1));\n    //大包\n    $task->dispatch(array('data' => str_repeat('B', 1024 * 32), 'type' => 2));\n    //普通字符串\n    $task->dispatch(str_repeat('C', 512));\n};\n\n$pm->childFunc = function () use ($pm) {\n    ini_set('swoole.display_errors', 'Off');\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set(array(\n        \"worker_num\" => 1,\n        'task_worker_num' => 1,\n        'task_ipc_mode' => 3,\n        'message_queue_key' => MSGQ_KEY,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n\n    });\n    $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) {\n        global $result;\n        switch ($task_id) {\n            case 0:\n                Assert::isArray($data);\n                Assert::eq($data['type'], 1);\n                Assert::length($data['data'], 1024);\n                $result->add(1);\n                break;\n            case 1:\n                Assert::isArray($data);\n                Assert::eq($data['type'], 2);\n                Assert::length($data['data'], 1024 * 32);\n                $result->add(1);\n                break;\n            case 2:\n                Assert::assert(is_string($data));\n                Assert::length($data, 512);\n                $result->add(1);\n                $serv->shutdown();\n                break;\n            default:\n                break;\n        }\n    });\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\nAssert::eq($result->get(), 3);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/task/task_queue.phpt",
    "content": "--TEST--\nswoole_server/task: task queue\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nconst N = 2048;\nuse Swoole\\Server;\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $cli->connect('127.0.0.1', $pm->getFreePort(), 30) or die(\"ERROR\");\n    $cli->send(\"task-01\") or die(\"ERROR\");\n    echo $cli->recv();\n    $cli->close();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    ini_set('swoole.display_errors', 'Off');\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set(array(\n        'worker_num' => 1,\n        'task_worker_num' => 1,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on('WorkerStart', function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n        for ($i = 0; $i < 2048; $i++) {\n            $data = array('id' => $i, 'fd' => $fd, 'data' => RandStr::getBytes(rand(2048, 4096)));\n            if ($serv->task($data) === false) {\n                $serv->send($fd, \"ERROR\\n\");\n                return;\n            }\n        }\n    });\n    $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) {\n        if ($task_id == 0) {\n            sleep(1);\n        }\n        if ($task_id != $data['id']) {\n            echo \"ERROR, $task_id, {$data['id']}\\n\";\n        }\n        if ($data['id'] == N - 1) {\n            $serv->send($data['fd'], \"OK\");\n        }\n    });\n\n    $serv->on('finish', function (Server $serv, $fd, $rid, $data) {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_server/task/task_wait.phpt",
    "content": "--TEST--\nswoole_server/task: taskwait in coroutine\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nSwoole\\Runtime::enableCoroutine();\n$pm = new SwooleTest\\ProcessManager;\n\n$randoms = [];\nfor ($n = MAX_REQUESTS; $n--;)\n{\n    $randoms[] = random_bytes(mt_rand(0, 65536));\n}\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($n = MAX_REQUESTS; $n--;) {\n        go(function () use ($pm, $n) {\n            $c = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            $c->set(['timeout' => 5,]);\n            Assert::assert($c->get('/task?n='.$n));\n            Assert::same($c->body, \"OK\");\n        });\n    }\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm, $randoms) {\n    $server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'log_file' => '/dev/null',\n        'worker_num' => 1,\n        'task_worker_num' => 1,\n    ]);\n    $server->on('workerStart', function ($serv, $wid) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($server, $randoms) {\n        $n = $request->get['n'];\n        list($ret_n, $ret_random) = $server->taskwait($n, 1);\n        if ($ret_n !== $n) {\n            $response->end(\"ERROR MATCH {$ret_n} with {$n}\");\n            return;\n        } elseif ($ret_random !== $randoms[$n]) {\n            $response->end(\"ERROR EQUAL {$ret_n}(\" . strlen($ret_random) . \") with {$n}(\" . strlen($randoms[$n]) . \")\");\n            return;\n        }\n        $response->end('OK');\n    });\n    $server->on('task', function (Swoole\\Http\\Server $server, int $task_id, int $worker_id, string $n) use ($pm, $randoms) {\n        return [$n, $randoms[$n]];\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/task/timer.phpt",
    "content": "--TEST--\nswoole_server/task: timer\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_darwin_todo();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Server\\Task;\nuse Swoole\\Timer;\nuse Swoole\\WebSocket\\Frame;\nuse Swoole\\WebSocket\\Server;\nuse SwooleTest\\ProcessManager;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager();\n$pm->parentFunc = function (int $pid) use ($pm) {\n    run(function () use ($pm) {\n        $cli = new Client('127.0.0.1', $pm->getFreePort());\n        $cli->set(['websocket_compression' => true]);\n        $cli->upgrade('/');\n        $cli->push('Hello Swoole');\n        $data = $cli->recv(5);\n        Assert::eq($data->data, 'OK');\n        echo \"DONE\\n\";\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $http->set([\n        'worker_num' => 1,\n        'task_worker_num' => 1,\n        'event_object' => true,\n        'log_file' => '/dev/null',\n    ]);\n    $http->on('message', function (Server $server, Frame $frame) {\n        $server->task(['fd' => $frame->fd]);\n    });\n    $http->on('WorkerStart', function (Server $server, int $workerId) {\n        if ($server->taskworker) {\n            Timer::after(1, function () use ($server, $workerId) {\n                var_dump('after1 : ' . time());\n            });\n            // never callback\n            Timer::after(10000, function () use ($server, $workerId) {\n                var_dump('after2 : ' . time());\n            });\n        }\n    });\n    $http->on('task', function (Server $server, Task $task) {\n        var_dump('begin : ' . time());\n        Timer::after(2000, function () use ($server, $task) {\n            var_dump('end : ' . time());\n            Assert::true($server->push($task->data['fd'], 'OK'));\n        });\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nstring(19) \"after1 : %d\"\nstring(18) \"begin : %d\"\nstring(16) \"end : %d\"\nDONE\n"
  },
  {
    "path": "tests/swoole_server/task/unpack.phpt",
    "content": "--TEST--\nswoole_server/task: unpack\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Server\\Task;\n\n$data1 = random_bytes(random_int(16, 8000));\n$packed1 = Task::pack($data1);\nAssert::eq($data1, Task::unpack($packed1));\n\n$data2 = random_bytes(random_int(9000, 4 * 1024 * 1024));\n$packed2 = Task::pack($data2);\nAssert::eq($data2, Task::unpack($packed2));\n\n$data3 = [\n    'data' => random_bytes(random_int(16, 2000)),\n    'msg' => 'data 3',\n    'int' => random_int(1, 9999999),\n    'uniq' => uniqid(),\n];\n$packed3 = Task::pack($data3);\nAssert::same($data3, Task::unpack($packed3));\n\n$data4 = [\n    'data' => random_bytes(random_int(9000, 2 * 1024 * 1024)),\n    'msg' => 'data 4',\n    'int' => random_int(1, 9999999),\n    'uniq' => uniqid(),\n];\n$packed4 = Task::pack($data4);\nAssert::same($data4, Task::unpack($packed4));\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/task/without_onfinish.phpt",
    "content": "--TEST--\nswoole_server/task: task & finish\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    for ($i = MAX_CONCURRENCY_LOW; $i--;) {\n        go(function () use ($pm) {\n            $ret = httpGetBody(\"http://127.0.0.1:{$pm->getFreePort()}\");\n            Assert::same($ret, 'Hello Swoole!');\n        });\n    }\n    Swoole\\Event::wait();\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $http->set([\n        'log_file' => '/dev/null',\n        'task_worker_num' => 4\n    ]);\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($http) {\n        Assert::assert($response->detach());\n        $http->task($response->fd);\n    });\n    $http->on('task', function ($a, $b, $c, string $fd) {\n        $response = Swoole\\Http\\Response::create($fd);\n        $response->end('Hello Swoole!');\n        return null; // no on finish?\n    });\n    $http->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/taskWaitMulti.phpt",
    "content": "--TEST--\nswoole_server: taskWaitMulti\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$port = get_one_free_port();\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($port) {\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $cli->connect('127.0.0.1', $port, 0.5) or die(\"ERROR\");\n\n    $cli->send(\"task-01\") or die(\"ERROR\");\n    Assert::same($cli->recv(), 'OK');\n    $cli->send(\"task-02\") or die(\"ERROR\");\n    Assert::same($cli->recv(), 'OK');\n    $cli->close();\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm, $port) {\n    ini_set('swoole.display_errors', 'Off');\n    $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS);\n    $serv->set(array(\n        'worker_num' => 1,\n        'task_worker_num' => 1,\n        'enable_coroutine' => random_int(0, 100) > 50,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n        if ($data == 'task-01') {\n            $tasks[] = mt_rand(1000, 9999);\n            $tasks[] = mt_rand(1000, 9999);\n            $tasks[] = mt_rand(1000, 9999);\n            $tasks[] = mt_rand(1000, 9999);\n            $results = $serv->taskWaitMulti($tasks, 2);\n        } else {\n            $tasks[] = mt_rand(1000, 9999);\n            $tasks[] = mt_rand(1000, 9999);\n            $tasks[] = mt_rand(1000, 9999);\n            $tasks[] = mt_rand(1000, 9999);\n            $tasks[] = 0;\n            $results = $serv->taskWaitMulti($tasks, 0.2);\n        }\n        if (count($results) == 4) {\n            $serv->send($fd, 'OK');\n        } else {\n            $serv->send($fd, 'ERR');\n        }\n    });\n\n    $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) {\n        if ($data == 0) {\n            usleep(300000);\n        }\n        return $data;\n    });\n\n    $serv->on('finish', function (Server $serv, $fd, $rid, $data) {\n\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/taskwait_01.phpt",
    "content": "--TEST--\nswoole_server: taskwait [blocking]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_darwin_todo();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$port = get_one_free_port();\n\nuse Swoole\\Client;\nuse Swoole\\Process;\nuse Swoole\\Server;\nuse SwooleTest\\ProcessManager;\n\n$pm = new ProcessManager();\n$pm->parentFunc = function ($pid) use ($port) {\n    $cli = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $cli->connect('127.0.0.1', $port, 0.5) or exit('ERROR');\n\n    $cli->send('array-01') or exit('ERROR');\n    Assert::same($cli->recv(), 'OK');\n    $cli->send('array-02') or exit('ERROR');\n    Assert::same($cli->recv(), 'OK');\n    $cli->send('string-01') or exit('ERROR');\n    Assert::same($cli->recv(), 'OK');\n    $cli->send('string-02') or exit('ERROR');\n    Assert::same($cli->recv(), 'OK');\n    $cli->send('timeout') or exit('ERROR');\n    Assert::same($cli->recv(), 'OK');\n\n    Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm, $port) {\n    ini_set('swoole.display_errors', 'Off');\n    $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'task_worker_num' => 1,\n        'enable_coroutine' => false,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on('WorkerStart', function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n        if ($data == 'array-01') {\n            $res = $serv->taskwait(['type' => 'array', 'value' => $data]);\n            if (!empty($res['name'])) {\n                $serv->send($fd, 'OK');\n            } else {\n                $serv->send($fd, 'ERR');\n            }\n        } elseif ($data == 'array-02') {\n            $res = $serv->taskwait(['type' => 'string', 'value' => $data]);\n            if ($res == \"hello world\\n\") {\n                $serv->send($fd, 'OK');\n            } else {\n                $serv->send($fd, 'ERR');\n            }\n        } elseif ($data == 'string-01') {\n            $res = $serv->taskwait('array');\n            if (!empty($res['name'])) {\n                $serv->send($fd, 'OK');\n            } else {\n                $serv->send($fd, 'ERR');\n            }\n        } elseif ($data == 'string-02') {\n            $res = $serv->taskwait('string');\n            if ($res == \"hello world\\n\") {\n                $serv->send($fd, 'OK');\n            } else {\n                $serv->send($fd, 'ERR');\n            }\n        } elseif ($data == 'timeout') {\n            $res = $serv->taskwait('timeout', 0.2);\n            if ($res === false) {\n                $res = $serv->taskwait('string', 0.2);\n                if ($res === \"hello world\\n\") {\n                    $serv->send($fd, 'OK');\n                    return;\n                }\n            }\n            $serv->send($fd, 'ERR');\n        }\n    });\n\n    $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) {\n        if (is_array($data)) {\n            if ($data['type'] == 'array') {\n                return ['name' => 'rango', 'year' => 1987];\n            }\n            return \"hello world\\n\";\n        }\n        if ($data == 'array') {\n            return ['name' => 'rango', 'year' => 1987];\n        }\n        if ($data == 'string') {\n            return \"hello world\\n\";\n        }\n        if ($data == 'timeout') {\n            usleep(300000);\n            return \"task timeout\\n\";\n        }\n    });\n\n    $serv->on('finish', function (Server $serv, $fd, $rid, $data) {});\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/taskwait_02.phpt",
    "content": "--TEST--\nswoole_server: taskwait [coroutine]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_darwin_todo();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$port = get_one_free_port();\n\nuse Swoole\\Client;\nuse Swoole\\Process;\nuse Swoole\\Server;\nuse SwooleTest\\ProcessManager;\n\n$pm = new ProcessManager();\n$pm->parentFunc = function ($pid) use ($port) {\n    $cli = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $cli->connect('127.0.0.1', $port, 0.5) or exit('ERROR');\n\n    $cli->send('array-01') or exit('ERROR');\n    Assert::same($cli->recv(), 'OK');\n    $cli->send('array-02') or exit('ERROR');\n    Assert::same($cli->recv(), 'OK');\n    $cli->send('string-01') or exit('ERROR');\n    Assert::same($cli->recv(), 'OK');\n    $cli->send('string-02') or exit('ERROR');\n    Assert::same($cli->recv(), 'OK');\n    $cli->send('timeout') or exit('ERROR');\n    Assert::same($cli->recv(), 'OK');\n\n    Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm, $port) {\n    ini_set('swoole.display_errors', 'Off');\n    ini_set('display_errors', 'Off');\n    $serv = new Server('127.0.0.1', $port, SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'task_worker_num' => 1,\n        'log_file' => '/dev/null',\n        'enable_coroutine' => true,\n    ]);\n    $serv->on('WorkerStart', function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n        if ($data == 'array-01') {\n            $res = $serv->taskwait(['type' => 'array', 'value' => $data]);\n            if (!empty($res['name'])) {\n                $serv->send($fd, 'OK');\n            } else {\n                $serv->send($fd, 'ERR');\n            }\n        } elseif ($data == 'array-02') {\n            $res = $serv->taskwait(['type' => 'string', 'value' => $data]);\n            if ($res == \"hello world\\n\") {\n                $serv->send($fd, 'OK');\n            } else {\n                $serv->send($fd, 'ERR');\n            }\n        } elseif ($data == 'string-01') {\n            $res = $serv->taskwait('array');\n            if (!empty($res['name'])) {\n                $serv->send($fd, 'OK');\n            } else {\n                $serv->send($fd, 'ERR');\n            }\n        } elseif ($data == 'string-02') {\n            $res = $serv->taskwait('string');\n            if ($res == \"hello world\\n\") {\n                $serv->send($fd, 'OK');\n            } else {\n                $serv->send($fd, 'ERR');\n            }\n        } elseif ($data == 'timeout') {\n            $res = $serv->taskwait('timeout', 0.2);\n            if ($res === false) {\n                $res = $serv->taskwait('string', 0.2);\n                if ($res === \"hello world\\n\") {\n                    $serv->send($fd, 'OK');\n                    return;\n                }\n            }\n            $serv->send($fd, 'ERR');\n        }\n    });\n\n    $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) {\n        if (is_array($data)) {\n            if ($data['type'] == 'array') {\n                return ['name' => 'rango', 'year' => 1987];\n            }\n            return \"hello world\\n\";\n        }\n        if ($data == 'array') {\n            return ['name' => 'rango', 'year' => 1987];\n        }\n        if ($data == 'string') {\n            return \"hello world\\n\";\n        }\n        if ($data == 'timeout') {\n            usleep(300000);\n            return \"task timeout\\n\";\n        }\n    });\n\n    $serv->on('finish', function (Server $serv, $fd, $rid, $data) {});\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/unregistered_signal.phpt",
    "content": "--TEST--\nswoole_server: unregistered signal\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n//skip_if_in_valgrind();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $pid = file_get_contents(TEST_PID_FILE);\n    usleep(1000);\n    Swoole\\Process::kill($pid, SIGPIPE);\n    usleep(50000);\n    $pm->kill();\n    usleep(50000);\n    $log = file_get_contents(TEST_LOG_FILE);\n    echo $log, \"\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    @unlink(TEST_LOG_FILE);\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'log_file' => TEST_LOG_FILE,\n        'pid_file' => TEST_PID_FILE,\n        'worker_num' => 1,\n    ]);\n    $server->on('WorkerStart', function (Swoole\\Server $server, $worker_id) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('Receive', function (Swoole\\Server $server, $fd, $reactorId, $data) {\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\n[%s]\tWARNING\t%s (ERRNO 721): Unable to find callback function for signal Broken pipe: 13\n"
  },
  {
    "path": "tests/swoole_server/unsock_dgram.phpt",
    "content": "--TEST--\nswoole_server: unix socket dgram server\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Server;\nuse Swoole\\Client;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Client(SWOOLE_SOCK_UNIX_DGRAM, SWOOLE_SOCK_SYNC);\n    $r = $client->connect(UNIXSOCK_PATH, 0, -1);\n    if ($r === false)\n    {\n        echo \"ERROR\";\n        exit;\n    }\n    $client->send(\"SUCCESS\");\n    echo $client->recv();\n    $client->close();\n    @unlink(UNIXSOCK_PATH);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server(UNIXSOCK_PATH, 0, SWOOLE_PROCESS, SWOOLE_SOCK_UNIX_DGRAM);\n    $serv->set([\"worker_num\" => 1, 'log_file' => '/dev/null',]);\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"packet\", function (Server $serv, $data, $addr) {\n        $serv->send($addr['address'], 'SUCCESS'.PHP_EOL);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/unsock_stream.phpt",
    "content": "--TEST--\nswoole_server: unix socket stream server\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Server;\nuse Swoole\\Client;\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Client(SWOOLE_SOCK_UNIX_STREAM, SWOOLE_SOCK_SYNC);\n    $r = $client->connect(UNIXSOCK_PATH, 0, -1);\n    if ($r === false) {\n        echo \"ERROR\";\n        exit;\n    }\n    $client->send(\"SUCCESS\");\n    usleep(100 * 1000);\n    echo $client->recv() . \"\\n\";\n    $client->close();\n    @unlink(UNIXSOCK_PATH);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server(UNIXSOCK_PATH, 0, SWOOLE_BASE, SWOOLE_SOCK_UNIX_STREAM);\n    $serv->set([\"worker_num\" => 1, 'log_file' => '/dev/null']);\n    $serv->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $reactorId, $data) {\n        $serv->send($fd, 'SUCCESS');\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_server/user_process.phpt",
    "content": "--TEST--\nswoole_server: user process\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Server;\nuse Swoole\\Client;\n$pm = new SwooleTest\\ProcessManager;\n\nconst SIZE = 8192* 5;\n\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    $client = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $client->set([\"open_eof_check\" => true, \"package_eof\" => \"\\r\\n\\r\\n\"]);\n    $r = $client->connect('127.0.0.1', $pm->getFreePort(), -1);\n    if ($r === false)\n    {\n        echo \"ERROR\";\n        exit;\n    }\n    $client->send(\"SUCCESS\");\n    for($i=0; $i < 20; $i++) {\n      $ret = $client->recv();\n      Assert::same(strlen($ret), SIZE +4);\n    }\n    $client->close();\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\"worker_num\" => 1, 'log_file' => '/dev/null']);\n\n    $proc = new Swoole\\Process(function ($process) use ($serv){\n      //echo posix_getpid().\"\\n\";\n      while(true) {\n       $data = json_decode($process->read(), true);\n       //var_dump(SIZE);\n       for($i=0; $i < 10; $i++) {\n         $serv->send($data['fd'], str_repeat('A', SIZE).\"\\r\\n\\r\\n\");\n         //echo \"user process send ok\\n\";\n       }\n     }\n    }, false, true);\n\n    $serv->addProcess($proc);\n    $serv->on(\"WorkerStart\", function (Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $reactorId, $data) use ($proc)\n    {\n        $proc->write(json_encode(['fd' => $fd]));\n        for($i=0; $i < 10; $i++) {\n          $serv->send($fd, str_repeat('A', SIZE).\"\\r\\n\\r\\n\");\n          //echo \"worker send ok\\n\";\n        }\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/user_process_2.phpt",
    "content": "--TEST--\nswoole_server: user process [with coroutine]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Server;\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_PROCESS);\n    $process = new \\Swoole\\Process(function ($process) use ($serv, $pm) {\n        for ($i = 0; $i < 5; $i++) {\n            co::sleep(0.02);\n            echo \"$i OK\\n\";\n        }\n        $pm->wakeup();\n        //kill by SIGTERM\n        co::sleep(100);\n    }, false, 0, true);\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n    ]);\n\n    $serv->on(\"Receive\", function (Server $serv, $fd, $rid, $data) use ($process) {\n\n    });\n    $serv->addProcess($process);\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n0 OK\n1 OK\n2 OK\n3 OK\n4 OK\n"
  },
  {
    "path": "tests/swoole_server/user_process_force_exit.phpt",
    "content": "--TEST--\nswoole_server: process force exit\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Constant;\n\n$atomic = new Swoole\\Atomic;\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function () use ($pm) {\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n\n    $server = new Swoole\\Server('127.0.0.1', get_one_free_port(), SWOOLE_PROCESS, SWOOLE_SOCK_UDP);\n\n    $server->set([\n        Constant::OPTION_LOG_FILE => '/dev/null',\n        Constant::OPTION_MAX_WAIT_TIME => 1,\n    ]);\n\n    $server->on('packet', function () {\n    });\n    $server->addProcess(new Swoole\\Process(function () {\n        pcntl_signal(SIGTERM, function () {\n        });\n        Swoole\\Timer::tick(1000, function () {\n        });\n    }));\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_server/user_process_pid.phpt",
    "content": "--TEST--\nswoole_server: user process\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Process;\nuse Swoole\\Http\\Server;\nuse SwooleTest\\ProcessManager;\n\n$atomic = new Atomic;\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm, $atomic) {\n    $url = \"http://127.0.0.1:\" . $pm->getFreePort() . \"/\";\n    Assert::eq(file_get_contents($url), $atomic->get());\n    $pm->wait();\n    Assert::eq(file_get_contents($url), $atomic->get());\n    usleep(100000);\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm, $atomic) {\n    $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_PROCESS);\n    $process = new Process(function ($process) use ($serv, $pm, $atomic) {\n        $atomic->set(posix_getpid());\n        usleep(100000);\n        $pm->wakeup();\n    });\n    $serv->set([\n        'worker_num' => 2,\n        'task_worker_num' => 3,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on('Request', function ($req, $resp) use ($serv) {\n        $resp->end($serv->getWorkerPid(5));\n    });\n    $serv->on('task', function (){});\n    $serv->addProcess($process);\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server/wrong_eof_setting.phpt",
    "content": "--TEST--\nswoole_server: wrong eof setting\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$port = get_one_free_port();\n$pm->parentFunc = function () use ($pm) {\n    switch_process();\n    go(function () use ($pm) {\n        $client = new Client(SWOOLE_SOCK_TCP);\n        $client->set([\n            'open_eof_check' => true,\n            'open_eof_split' => true,\n            \"package_eof\" => \"\",\n        ]);\n        $client->connect('127.0.0.1', $pm->getFreePort());\n        $client->send(\"Swoole\\r\\n\\r\\n\");\n    });\n    Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $pm->wakeup();\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        \"package_eof\" => \"\",\n        'open_eof_check' => true,\n        'open_eof_split' => true,\n        \"worker_num\" => 1\n    ]);\n    $serv->on('workerStart', function (Server $serv) use ($pm) {\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n        $serv->send($fd, \"hello {$data}\\r\\n\\r\\n\");\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nFatal error: %s: package_eof cannot be an empty string in %s on line %d\n\nFatal error: %s: package_eof cannot be an empty string in %s on line %d\n\n--EXPECTF_85--\nFatal error: %s: package_eof cannot be an empty string in %s on line %d\nStack trace:\n#0 [internal function]: Swoole\\Server\\Port->set(Array)\n#1 %s(%d): Swoole\\Server->set(Array)\n#2 [internal function]: {closure:%s:%d}()\n#3 %s/tests/include/lib/src/ProcessManager.php(%d): call_user_func(Object(Closure))\n#4 %s/tests/include/lib/src/ProcessManager.php(%d): SwooleTest\\ProcessManager->runChildFunc()\n#5 [internal function]: SwooleTest\\ProcessManager->{closure:SwooleTest\\ProcessManager::run():298}(Object(Swoole\\Process))\n#6 %s/tests/include/lib/src/ProcessManager.php(%d): Swoole\\Process->start()\n#7 %s(%d): SwooleTest\\ProcessManager->run()\n#8 {main}\n\nFatal error: %s: package_eof cannot be an empty string in %s on line %d\nStack trace:\n#0 %s(%d): Swoole\\Coroutine\\Client->connect('127.0.0.1', %d)\n#1 [internal function]: {closure:{closure:%s:%d}:%d}()\n#2 {main}\n\n"
  },
  {
    "path": "tests/swoole_server/z_conn_10k.phpt",
    "content": "--TEST--\nswoole_server: 10k connections\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nrequire __DIR__ . '/../include/config.php';\nif ((int)`ulimit -n 2>&1` < MAX_CONCURRENCY_MID * MAX_REQUESTS) {\n    skip('ulimit -n failed');\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$count = 0;\n$client_map = [];\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    for ($c = MAX_CONCURRENCY_MID; $c--;) {\n        go(function () use ($pm, $c) {\n            for ($n = MAX_REQUESTS; $n--;) {\n                global $count, $client_map;\n                $client = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n                $client_map[\"{$c}.{$n}\"] = $client;\n                if ($client->connect('127.0.0.1', $pm->getFreePort(), -1)) {\n                    if (Assert::assert($client->recv() === 'Hello Swoole!')) {\n                        if (++$count === MAX_CONCURRENCY_MID * MAX_REQUESTS) {\n                            var_dump($count);\n                            echo \"DONE\\n\";\n                            $client_map = [];\n                            $pm->kill();\n                        }\n                        continue;\n                    }\n                } else {\n                    echo \"ERROR\\n\";\n                    $pm->kill();\n                    exit;\n                }\n            }\n        });\n    }\n    register_shutdown_function(function () {\n        global $client_map;\n        foreach ($client_map as $client) {\n            $client->close();\n        }\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        \"worker_num\" => swoole_cpu_num() * 2,\n        'log_file' => '/dev/null',\n        'max_connection' => MAX_CONCURRENCY_MID * MAX_REQUESTS\n    ]);\n    $server->on(\"WorkerStart\", function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('connect', function (Swoole\\Server $server, int $fd) {\n        global $count;\n        $count++;\n        $server->send($fd, 'Hello Swoole!');\n    });\n    $server->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nint(%d)\nDONE\n"
  },
  {
    "path": "tests/swoole_server_coro/length_1.phpt",
    "content": "--TEST--\nswoole_server_coro: (length protocol) 1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse SwooleTest\\LengthServer;\n\nclass TestServer_5 extends LengthServer\n{\n    protected $show_lost_package = false;\n\n    function onWorkerStart()\n    {\n        global $pm;\n        $pm->wakeup();\n    }\n\n    function onClose()\n    {\n        parent::onClose();\n        $this->serv->shutdown();\n    }\n}\n\nTestServer_5::$random_bytes = true;\nTestServer_5::$pkg_num = IS_IN_CI ? 1000 : 10000;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP);\n    if (!$client->connect('127.0.0.1', $pm->getFreePort()))\n    {\n        exit(\"connect failed\\n\");\n    }\n\n    $bytes = 0;\n    $pkg_bytes = 0;\n\n    for ($i = 0; $i < TestServer_5::$pkg_num; $i++)\n    {\n//        if ($i % 1000 == 0)\n//        {\n//            echo \"#{$i} send package. sid={$sid}, length=\" . ($len + 10) . \", total bytes={$pkg_bytes}\\n\";\n//        }\n        if (!$client->send(TestServer_5::getPacket()))\n        {\n            echo \"send [$i] failed.\\n\";\n            break;\n        }\n        $bytes += 2;\n    }\n\n    $recv = $client->recv();\n    echo $recv;\n    //echo \"send \".TestServer::$PKG_NUM.\" packet sucess, send $bytes bytes\\n\";\n    $client->close();\n};\n\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $serv = new TestServer_5($pm->getFreePort(), false);\n        $serv->start();\n    });\n    Swoole\\Event::wait();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nend\nTotal count=%d, bytes=%d\n"
  },
  {
    "path": "tests/swoole_server_coro/random_port.phpt",
    "content": "--TEST--\nswoole_server_coro: bind random port\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function() {\n    $server = new Co\\Server(\"127.0.0.1\", 0);\n    Assert::assert($server->port > 0 and $server->port < 65535);\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server_coro/reuse_port.phpt",
    "content": "--TEST--\nswoole_server_coro: reuse port\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Constant;\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Coroutine\\Scheduler;\nuse Swoole\\Coroutine\\Server;\nuse Swoole\\Coroutine\\Server\\Connection;\nuse Swoole\\Process;\nuse Swoole\\Process\\Pool;\nuse SwooleTest\\ProcessManager;\n\n$pm = new ProcessManager();\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $sch = new Scheduler();\n    $pids = [];\n    $sch->parallel(10, function () use ($pm, &$pids) {\n        $cli = new Client(SWOOLE_SOCK_TCP);\n        if (!$cli->connect('127.0.0.1', $pm->getFreePort())) {\n            echo \"ERROR [1]\\n\";\n            return;\n        }\n        if (!$cli->send(\"hello\\n\")) {\n            echo \"ERROR [2]\\n\";\n            return;\n        }\n        $ret = $cli->recv();\n        if (!$ret) {\n            echo \"ERROR [3]\\n\";\n            return;\n        }\n        $result = unserialize($ret);\n        if (!$result) {\n            echo \"ERROR [4]\\n\";\n            return;\n        }\n        $pids[$result['wid']] = 1;\n    });\n    $sch->start();\n    Assert::eq(count($pids), IS_MAC_OS ? 1 : 2);\n    echo \"DONE\\n\";\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $atomic = new Atomic();\n    $pool = new Pool(2);\n    $pool->set(['enable_coroutine' => true]);\n    $pool->on(Constant::EVENT_WORKER_START, function ($pool, $id) use ($pm, $atomic) {\n        $server = new Server('127.0.0.1', $pm->getFreePort(), false, true);\n        if ($atomic->add() == 2) {\n            $pm->wakeup();\n        }\n        Process::signal(SIGTERM, function () use ($server) {\n            $server->shutdown();\n        });\n        $server->handle(function (Connection $conn) {\n            co::sleep(0.005);\n            $data = $conn->recv();\n            if (empty($data)) {\n                $conn->close();\n            }\n            $conn->send(serialize(['wid' => posix_getpid()]));\n        });\n        $server->start();\n    });\n    $pool->start();\n};\n\n$pm->childFirst();\n$pm->run();\n\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_server_coro/ssl.phpt",
    "content": "--TEST--\nswoole_server_coro: ssl\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Server;\nuse Swoole\\Coroutine\\Server\\Connection;\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); //同步阻塞\n    if (!$client->connect('127.0.0.1', $pm->getFreePort())) {\n        exit(\"connect failed\\n\");\n    }\n    $client->send(\"hello world\");\n    Assert::same($client->recv(), \"Swoole hello world\");\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $server = new Server('0.0.0.0', $pm->getFreePort(), true);\n        $server->set([\n            'log_file' => '/dev/null',\n            'ssl_cert_file' => SSL_FILE_DIR.'/server.crt',\n            'ssl_key_file' => SSL_FILE_DIR.'/server.key',\n        ]);\n        $server->handle(function (Connection $conn) use ($server) {\n            $data = $conn->recv();\n            $conn->send(\"Swoole $data\");\n            $server->shutdown();\n        });\n        $pm->wakeup();\n        $server->start();\n    });\n    Swoole\\Event::wait();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server_coro/tcp.phpt",
    "content": "--TEST--\nswoole_server_coro: tcp\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n\nuse Swoole\\Coroutine\\Server;\nuse Swoole\\Coroutine\\Server\\Connection;\n\ngo(function () {\n\n    $server = new Server('0.0.0.0', 9601, false);\n\n    $server->handle(function (Connection $conn) use ($server) {\n        $data = $conn->recv();\n        $json = json_decode($data, true);\n        Assert::same($json['data'] ?? '', 'hello');\n        $conn->send(\"world\\n\");\n        $conn->close();\n\n        $server->shutdown();\n    });\n\n    $server->start();\n});\n\ngo(function () {\n    $conn = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    Assert::assert($conn->connect('127.0.0.1', 9601));\n    $conn->send(json_encode(['data' => 'hello']));\n    echo $conn->recv();\n});\n\nSwoole\\Event::wait();\n?>\n--EXPECT--\nworld\n"
  },
  {
    "path": "tests/swoole_server_port/connections.phpt",
    "content": "--TEST--\nswoole_server_port: connections\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Constant;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Coroutine\\Scheduler;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\WebSocket\\Server;\nuse SwooleTest\\ProcessManager;\n\n$pm = new ProcessManager();\n$pm->initFreePorts(2);\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    $sch = new Scheduler();\n    $conns_1 = [];\n    $conns_2 = [];\n\n    $sch->parallel(\n        3,\n        function () use ($pm, &$conns_1) {\n            $c = new Client(TCP_SERVER_HOST, $pm->getFreePort(0));\n            $c->upgrade('/');\n            $conns_1[] = $c->recv()->data;\n            $c->recv();\n        }\n    );\n    $sch->parallel(\n        2,\n        function () use ($pm, &$conns_2) {\n            $c = new Client(TCP_SERVER_HOST, $pm->getFreePort(1));\n            $c->upgrade('/');\n            $conns_2[] = $c->recv()->data;\n            $c->recv();\n        }\n    );\n\n    // all\n    $sch->add(\n        function () use ($pm, &$conns_1, &$conns_2) {\n            $c = new Client(TCP_SERVER_HOST, $pm->getFreePort(0));\n            $c->upgrade('/');\n            $conns_1[] = $c->recv()->data;\n\n            $c->push('all');\n            $frame = $c->recv();\n            Assert::assert($frame);\n            $json = json_decode($frame->data);\n            Assert::eq($json->count, 8);\n\n            $list1 = array_arrange($json->list);\n            $list2 = array_arrange(array_merge($conns_1, $conns_2));\n\n            Assert::eq($list1, $list2);\n        }\n    );\n\n    // port-0\n    $sch->add(\n        function () use ($pm, &$conns_1) {\n            $c = new Client(TCP_SERVER_HOST, $pm->getFreePort(0));\n            $c->upgrade('/');\n            $conns_1[] = $c->recv()->data;\n            $c->push('port-0');\n            $frame = $c->recv();\n            Assert::assert($frame);\n            $json = json_decode($frame->data);\n            Assert::eq($json->count, 5);\n            Assert::eq($json->list, $conns_1);\n        }\n    );\n\n    // port-1\n    $sch->add(\n        function () use ($pm, &$conns_2) {\n            $c = new Client(TCP_SERVER_HOST, $pm->getFreePort(1));\n            $c->upgrade('/');\n            $conns_2[] = $c->recv()->data;\n            $c->push('port-1');\n            $frame = $c->recv();\n            Assert::assert($frame);\n            $json = json_decode($frame->data);\n            Assert::eq($json->count, 3);\n            Assert::eq($json->list, $conns_2);\n        }\n    );\n\n    $sch->add(\n        function () use ($pm) {\n            System::sleep(.5);\n            $pm->kill();\n        }\n    );\n    $sch->start();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('0.0.0.0', $pm->getFreePort(0), SWOOLE_PROCESS);\n    $server->set([\n        Constant::OPTION_LOG_FILE => '/dev/null',\n        Constant::OPTION_WORKER_NUM => 1,\n    ]);\n    $server->on('open', function (Server $server, $request) {\n        $server->push($request->fd, $request->fd);\n    });\n    $server->on(\n        Constant::EVENT_WORKER_START,\n        function () use ($pm) {\n            $pm->wakeup();\n        }\n    );\n\n    $server->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP);\n\n    $server->on(\n        'message',\n        function (Server $server, $frame) {\n            if ($frame->data == 'all') {\n                $iterator = $server->connections;\n            } elseif ($frame->data == 'port-0') {\n                $iterator = $server->ports[0]->connections;\n            } elseif ($frame->data == 'port-1') {\n                $iterator = $server->ports[1]->connections;\n            } else {\n                return;\n            }\n\n            $data['count'] = count($iterator);\n            $data['list'] = array_values(iterator_to_array($iterator));\n            $server->push($frame->fd, json_encode($data));\n        }\n    );\n\n    $server->on('close', function ($ser, $fd) {});\n\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server_port/duplicate_registered.phpt",
    "content": "--TEST--\nswoole_server_port: duplicate registered\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$server = new Swoole\\Server('127.0.0.1', 0, SWOOLE_PROCESS);\n$server->on('receive', function () { });\nAssert::same(true, !!'load Assert');\n$mem = null;\nfor ($n = 1000; $n--;) {\n    $server->on('receive', function () { });\n    if ($mem === null) {\n        $mem = memory_get_usage();\n    }\n    Assert::same(memory_get_usage(), $mem);\n}\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_server_port/heartbeat_1.phpt",
    "content": "--TEST--\nswoole_server_port: heartbeat 1\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\go;\nuse function Swoole\\Coroutine\\run;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Server;\n\n$pm = new ProcessManager;\n$pm->initFreePorts(2);\n\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    run(function () use ($pm) {\n        go(function () use ($pm) {\n            $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n            $cli->connect('127.0.0.1', $pm->getFreePort(0));\n            for ($i = 0; $i < 2; ++$i) {\n                $cli->send('hello');\n                $data = $cli->recv();\n                if ($data != 'ok') {\n                    echo \"ERROR\\n\";\n                }\n                System::sleep(2);\n            }\n            System::sleep(3);\n            $cli->send('hello');\n            $data = $cli->recv();\n            if ($data == 'ok') {\n                echo \"ERROR\\n\";\n            }\n        });\n\n        go(function () use ($pm) {\n            $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n            $cli->connect('127.0.0.1', $pm->getFreePort(1));\n            for ($i = 0; $i < 2; ++$i) {\n                $cli->send('hello');\n                $data = $cli->recv();\n                if ($data != 'ok') {\n                    echo \"ERROR\\n\";\n                }\n                System::sleep(2);\n            }\n            System::sleep(3);\n            $cli->send('hello');\n            $data = $cli->recv();\n            if ($data == 'ok') {\n                echo \"ERROR\\n\";\n            } else {\n                echo \"OK\";\n            }\n        });\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $server = new Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE);\n    $server->set([\n        'heartbeat_check_interval' => 1,\n        'heartbeat_idle_time' => 3\n    ]);\n    $server->on('receive', function (Server $server, $fd, $reactorId, $data) {\n        $server->send($fd, 'ok');\n    });\n    $server->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $port2 = $server->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP);\n    // $port2->set([\n        // 'heartbeat_check_interval' => 1,\n        // 'heartbeat_idle_time' => 5\n    // ]);\n    $port2->on('receive', function (Server $server, $fd, $reactorId, $data) {\n        $server->send($fd, 'ok');\n    });\n\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_server_port/heartbeat_2.phpt",
    "content": "--TEST--\nswoole_server_port: heartbeat 2\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\go;\nuse function Swoole\\Coroutine\\run;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Server;\n\n$pm = new ProcessManager;\n$pm->initFreePorts(3);\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        $test_func = function ($port_index, $sleep_seconds) use ($pm) {\n            $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n            $cli->connect('127.0.0.1', $pm->getFreePort($port_index));\n            System::sleep($sleep_seconds);\n            return $cli->recv(0.01);\n        };\n        go(function () use ($test_func) {\n            Assert::same($test_func(0, 2), '');\n            echo \"DONE 0\\n\";\n        });\n        go(function () use ($test_func) {\n            Assert::same($test_func(1, 3), '');\n            echo \"DONE 1\\n\";\n        });\n        go(function () use ($test_func) {\n            Assert::same($test_func(2, 3.5), false);\n            echo \"DONE 2\\n\";\n        });\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE);\n    $server->set([\n        'heartbeat_check_interval' => 1,\n        'heartbeat_idle_time' => 1,\n    ]);\n    $server->on(\"WorkerStart\", function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('receive', function ($server, $fd, $reactorId, $data) {\n        $server->send($fd, 'ok');\n    });\n\n    $port2 = $server->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP);\n    $port2->set([\n        'heartbeat_idle_time' => 2,\n    ]);\n\n    $port3 = $server->listen('127.0.0.1', $pm->getFreePort(2), SWOOLE_SOCK_TCP);\n    $port3->set([\n        'heartbeat_idle_time' => 10,\n    ]);\n\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE 0\nDONE 1\nDONE 2\n"
  },
  {
    "path": "tests/swoole_server_port/heartbeat_3.phpt",
    "content": "--TEST--\nswoole_server_port: heartbeat 3\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_darwin_todo();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Server;\n\nuse function Swoole\\Coroutine\\go;\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager();\n$pm->initFreePorts(3);\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        $test_func = function ($port_index, $sleep_seconds) use ($pm) {\n            $cli = new Client(SWOOLE_SOCK_TCP);\n            $cli->connect('127.0.0.1', $pm->getFreePort($port_index));\n            System::sleep($sleep_seconds);\n            return $cli->recv(0.01);\n        };\n        go(function () use ($test_func) {\n            Assert::same($test_func(0, 3), false);\n            echo \"DONE 0\\n\";\n        });\n        go(function () use ($test_func) {\n            Assert::same($test_func(1, 2.3), '');\n            echo \"DONE 1\\n\";\n        });\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE);\n    $server->on('receive', function ($server, $fd, $reactorId, $data) {\n        $server->send($fd, 'ok');\n    });\n    $server->on('WorkerStart', function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n\n    $port2 = $server->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP);\n    $port2->set([\n        'heartbeat_idle_time' => 2,\n    ]);\n\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE 1\nDONE 0\n"
  },
  {
    "path": "tests/swoole_server_port/http.phpt",
    "content": "--TEST--\nswoole_server_port: http and tcp\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nini_set(\"swoole.display_errors\", \"Off\");\n\nconst TEST_STR = \"hello swooler\\n\";\n\n$pm = new ProcessManager;\n$pm->initFreePorts(2);\n\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $cli->set(['open_eof_check' => true, \"package_eof\" => \"\\r\\n\\r\\n\"]);\n        if (!$cli->connect('127.0.0.1', $pm->getFreePort(0), 0.5)) {\n            fail:\n            echo \"ERROR 1\\n\";\n            return;\n        }\n        //no eof, should be timeout here\n        if (!$cli->send(\"hello\\r\\n\\r\\n\")) {\n            goto fail;\n        }\n        $ret = $cli->recv();\n        if (!$ret) {\n            goto fail;\n        }\n        echo \"OK\\n\";\n    });\n\n    go(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort(1));\n        if ($cli->get(\"/\")) {\n            Assert::same($cli->body, TEST_STR);\n            Assert::same($cli->statusCode, 200);\n        } else {\n            echo \"ERROR 2\\n\";\n        }\n    });\n\n    Swoole\\Event::wait();\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE);\n\n    $server->set([\n        'open_eof_check' => true,\n        \"package_eof\" => \"\\r\\n\\r\\n\",\n        'log_file' => '/dev/null'\n    ]);\n\n    $server->on('Receive', function ($serv, $fd, $rid, $data) {\n        $serv->send($fd, \"Swoole: $data\\r\\n\\r\\n\");\n    });\n\n    $port2 = $server->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP);\n    $port2->set(['open_http_protocol' => true,]);\n    $port2->on(\"request\", function ($req, $resp) {\n        $resp->end(TEST_STR);\n    });\n\n    $server->on(\"WorkerStart\", function (Swoole\\Server $serv) {\n        /**\n         * @var $pm ProcessManager\n         */\n        global $pm;\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end(\"OK\\n\");\n    });\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_server_port/multi_port.phpt",
    "content": "--TEST--\nswoole_server_port: swoole server port\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Timer;\nuse Swoole\\Event;\nuse Swoole\\Server;\n\n$port1 = get_one_free_port();\n$port2 = get_one_free_port();\n$port3 = get_one_free_port();\n\nfunction makeTcpClient_without_protocol($host, $port, ?callable $onConnect = null, ?callable $onReceive = null)\n{\n    go(function () use ($host, $port, $onConnect, $onReceive) {\n        $cli = new Client(SWOOLE_SOCK_TCP);\n        $r = $cli->connect($host, $port, 1);\n        Assert::assert($r);\n        if ($onConnect) {\n            $onConnect($cli);\n        }\n        $recv = $cli->recv();\n        if ($onReceive) {\n            $onReceive($cli, $recv);\n        }\n    });\n}\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm, $port1, $port2, $port3)\n{\n    makeTcpClient_without_protocol(TCP_SERVER_HOST, $port1, function(Client $cli) use($port1) {\n        $r = $cli->send(\"$port1\\r\\n\");\n        Assert::assert($r !== false);\n    }, function(Client $cli, $data) use($port1) {\n        Assert::same((int)$data, $port1);\n        $cli->close();\n    });\n\n    makeTcpClient_without_protocol(TCP_SERVER_HOST, $port2, function(Client $cli) use($port2) {\n        $r = $cli->send(\"$port2\\n\");\n        Assert::assert($r !== false);\n    }, function(Client $cli, $data) use($port2) {\n        Assert::same((int)$data, $port2);\n        $cli->close();\n    });\n\n    makeTcpClient_without_protocol(TCP_SERVER_HOST, $port3, function(Client $cli) use($port1, $port3) {\n        $r = $cli->send(\"$port3\\r\");\n        Assert::assert($r !== false);\n    }, function(Client $cli, $data) use($port1, $port3) {\n        Assert::same((int)$data, $port1);\n        $cli->close();\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm,  $port1, $port2, $port3)\n{\n    $server = new Server('127.0.0.1', $port1, SWOOLE_PROCESS);\n    $server->set(array(\n        'log_file' => '/dev/null',\n        'worker_num' => 1,\n    ));\n\n    $p2 = $server->listen('127.0.0.1', $port2, SWOOLE_SOCK_TCP);\n    $p2->on('receive', function ($serv, $fd, $tid, $data) use ($port2)\n    {\n        $serv->send($fd, $port2);\n    });\n\n    $server->listen('127.0.0.1', $port3, SWOOLE_SOCK_TCP);\n\n    $server->on('Receive', function (Server $serv, $fd, $rid, $data)  use ($port1)\n    {\n        $serv->send($fd, \"$port1\");\n    });\n\n    $server->on(\"WorkerStart\", function (Server $serv)\n    {\n        /**\n         * @var $pm SwooleTest\\ProcessManager\n         */\n        global $pm;\n        $pm->wakeup();\n    });\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_server_port/sub_handshake.phpt",
    "content": "--TEST--\nswoole_server_port: sub server use websocket and handshake\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->initFreePorts(2);\n$pm->parentFunc = function () use ($pm) {\n    go(function () use ($pm) {\n        $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort(1));\n        $cli->set(['timeout' => 5]);\n        $ret = $cli->upgrade('/');\n        Assert::assert($ret);\n        $cli->push('Hello~');\n        $ret = $cli->recv();\n        var_dump($ret);\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $main_server = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE);\n    $main_server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->write('hello world');\n        $response->end();\n    });\n    $sub_server = $main_server->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP);\n    $sub_server->set([\n        'open_http_protocol' => true,\n        'open_websocket_protocol' => true\n    ]);\n    $sub_server->on('handshake', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $secWebSocketKey = $request->header['sec-websocket-key'];\n        $patten = '#^[+/0-9A-Za-z]{21}[AQgw]==$#';\n        if (0 === preg_match($patten, $secWebSocketKey) || 16 !== strlen(base64_decode($secWebSocketKey))) {\n            $response->end();\n            return false;\n        }\n        $key = base64_encode(sha1(\n            $request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',\n            true\n        ));\n        $headers = [\n            'Upgrade' => 'websocket',\n            'Connection' => 'Upgrade',\n            'Sec-WebSocket-Accept' => $key,\n            'Sec-WebSocket-Version' => '13'\n        ];\n        // failed: Error during WebSocket handshake:\n        // Response must not include 'Sec-WebSocket-Protocol' header if not present in request: websocket\n        if (isset($request->header['sec-websocket-protocol'])) {\n            $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol'];\n        }\n        foreach ($headers as $key => $val) {\n            $response->header($key, $val);\n        }\n        $response->status(101);\n        $response->end();\n        return true;\n    });\n    $sub_server->on('message', function (Swoole\\Http\\Server $server, Swoole\\WebSocket\\Frame $frame) {\n        var_dump($frame);\n        $response = new Swoole\\WebSocket\\Frame;\n        $response->data = 'OK';\n        $server->send($frame->fd, (string)$response);\n    });\n    $main_server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECTF--\nobject(Swoole\\WebSocket\\Frame)#%d (5) {\n  [\"fd\"]=>\n  int(1)\n  [\"data\"]=>\n  string(6) \"Hello~\"\n  [\"opcode\"]=>\n  int(1)\n  [\"flags\"]=>\n  int(%d)\n  [\"finish\"]=>\n  bool(true)\n}\nobject(Swoole\\WebSocket\\Frame)#%d (5) {\n  [\"fd\"]=>\n  int(%d)\n  [\"data\"]=>\n  string(2) \"OK\"\n  [\"opcode\"]=>\n  int(1)\n  [\"flags\"]=>\n  int(%d)\n  [\"finish\"]=>\n  bool(true)\n}\n"
  },
  {
    "path": "tests/swoole_server_port/tcp_eof.phpt",
    "content": "--TEST--\nswoole_server_port: tcp port with eof\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nini_set(\"swoole.display_errors\", \"Off\");\n\n$pm = new ProcessManager;\n$pm->initFreePorts(2);\n\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP);\n    $cli->set(['open_eof_check' => true, \"package_eof\" => \"\\r\\n\\r\\n\"]);\n    if (!$cli->connect('127.0.0.1', $pm->getFreePort(1), 0.5))\n    {\n        fail:\n        echo \"ERROR\\n\";\n        return;\n    }\n    //no eof, should be timeout here\n    if (!$cli->send(\"hello\\r\\n\\r\\n\"))\n    {\n        goto fail;\n    }\n    $ret = $cli->recv();\n    if (!$ret)\n    {\n        goto fail;\n    }\n    echo \"OK\\n\";\n    Swoole\\Process::kill($pid);\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $http = new Swoole\\Http\\Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE);\n\n    $port2 = $http->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP);\n    $port2->set(['open_eof_check' => true, \"package_eof\" => \"\\r\\n\\r\\n\"]);\n\n    $port2->on('Receive', function ($serv, $fd, $rid, $data)\n    {\n        $serv->send($fd, \"Swoole: $data\\r\\n\\r\\n\");\n    });\n\n    $http->set(array(\n        //'log_file' => '/dev/null'\n    ));\n    $http->on(\"WorkerStart\", function (Swoole\\Server $serv)\n    {\n        /**\n         * @var $pm ProcessManager\n         */\n        global $pm;\n        $pm->wakeup();\n    });\n    $http->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response)\n    {\n        $response->end(\"OK\\n\");\n    });\n    $http->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_socket_coro/accept.phpt",
    "content": "--TEST--\nswoole_socket_coro: accept\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    $sock = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    Assert::assert($sock->bind('127.0.0.1', 9601));\n    Assert::assert($sock->listen(512));\n    $conn = $sock->accept();\n    Assert::assert($conn);\n    Assert::isInstanceOf($conn, Swoole\\Coroutine\\Socket::class);\n\n    $data = $conn->recv();\n    $json = json_decode($data, true);\n    Assert::same($json['data'] ?? '', 'hello');\n    $conn->send(\"world\\n\");\n    $conn->close();\n});\n\ngo(function ()  {\n    $conn = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    Assert::assert($conn->connect('127.0.0.1', 9601));\n    $conn->send(json_encode(['data' => 'hello']));\n    echo $conn->recv();\n});\n?>\n--EXPECT--\nworld\n"
  },
  {
    "path": "tests/swoole_socket_coro/all.phpt",
    "content": "--TEST--\nswoole_socket_coro: recv/send all\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ndefine('BIG_RANDOM_DATA', str_repeat(get_safe_random(1024), 64 * 1024));\ndefine('BIG_RANDOM_DATA_LENGTH', strlen(BIG_RANDOM_DATA));\n$server = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n$port = get_one_free_port();\ngo(function () use ($server, $port) {\n    Assert::assert($server->bind('127.0.0.1', $port));\n    Assert::assert($server->listen(512));\n    $conn_map = [];\n    while ($conn = $server->accept()) {\n        Assert::assert($conn instanceof  Co\\Socket);\n        Assert::assert($conn->fd > 0);\n        $conn_map[$conn->fd] = $conn;\n        go(function () use ($conn) {\n            Assert::assert($conn instanceof Swoole\\Coroutine\\Socket);\n            Assert::assert($conn->recvAll(BIG_RANDOM_DATA_LENGTH) === BIG_RANDOM_DATA);\n            Assert::assert($conn->sendAll(BIG_RANDOM_DATA) === BIG_RANDOM_DATA_LENGTH);\n        });\n    }\n});\ngo(function () use ($server, $port) {\n    $conn = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    Assert::assert($conn->connect('127.0.0.1', $port));\n    Assert::assert($conn->sendAll(BIG_RANDOM_DATA) === BIG_RANDOM_DATA_LENGTH);\n    Assert::assert($conn->recvAll(BIG_RANDOM_DATA_LENGTH) === BIG_RANDOM_DATA);\n    Assert::assert($server->close());\n});\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/bound_error.phpt",
    "content": "--TEST--\nswoole_socket_coro: bound error\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Socket;\nuse Swoole\\Coroutine\\Client;\nuse Swoole\\Event;\n\n$port = get_one_free_port();\ngo(function () use ($port) {\n    $server = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    Assert::assert($server->bind('127.0.0.1', $port));\n    Assert::assert($server->listen());\n});\ngo(function () use ($port) {\n    $cli = new Client(SWOOLE_SOCK_TCP);\n    $ret = $cli->connect('127.0.0.1', $port);\n    Assert::true($ret);\n    go(function () use ($cli) {\n        $cli->recv();\n    });\n    $cli->recv();\n});\nEvent::wait();\n?>\n--EXPECTF--\nFatal error: Uncaught Swoole\\Error: Socket#%d has already been bound to another coroutine#%d, reading of the same socket in coroutine#%d at the same time is not allowed in %s:%d\nStack trace:\n#0 %s(%d): Swoole\\Coroutine\\Client->recv()\n#1 [internal function]: {%s}()\n#2 {main}\n  thrown in %s on line %d\n\n [Coroutine-3] Stack trace:\n -------------------------------------------------------------------\n#0 %s(%d): Swoole\\Coroutine\\Client->recv()\n#1 [internal function]: {%s}()\n"
  },
  {
    "path": "tests/swoole_socket_coro/cancel.phpt",
    "content": "--TEST--\nswoole_socket_coro: cancel\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_DGRAM, 0);\n$socket->bind('127.0.0.1', 9601);\n//Server\ngo(function () use ($socket) {\n    while (true) {\n        $peer = null;\n        $data = $socket->recvfrom($peer);\n        Assert::assert(empty($data));\n        Assert::assert($socket->errCode == SOCKET_ECANCELED);\n        break;\n    }\n    echo \"DONE\\n\";\n});\n\n//Client\ngo(function () use ($socket) {\n    co::sleep(0.1);\n    $socket->cancel();\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/check_writev_readv_param_type.phpt",
    "content": "--TEST--\nswoole_socket_coro: check writev and readv param type\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nuse Swoole\\Coroutine\\Socket;\n\nuse function Swoole\\Coroutine\\run;\n\nrequire __DIR__ . '/../include/bootstrap.php';\n\nrun(function () {\n    $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    try {\n        $iovector = [1];\n        $conn->writeVectorAll($iovector);\n\n    } catch (\\Exception $e) {\n        echo $e->getMessage() . PHP_EOL;\n    }\n\n    try {\n        $iovector = [1, '!'];\n        $conn->readVectorAll($iovector);\n    } catch (\\Exception $e) {\n        echo $e->getMessage() . PHP_EOL;\n    }\n});\n\necho \"DONE\\n\";\n?>\n--EXPECTF--\nItem #[0] must be of type string, %s given\nItem #[1] must be of type int, %s given\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/closed.phpt",
    "content": "--TEST--\nswoole_socket_coro: closed bad fd\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, 0);\n    Assert::assert($socket->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT));\n    Assert::assert($socket->close());\n    Assert::same($socket->errCode, 0);\n    Assert::assert(!$socket->bind('127.0.0.1', 9501));\n    Assert::same($socket->errCode, SOCKET_EBADF);\n    Assert::assert(!$socket->listen());\n    Assert::same($socket->errCode, SOCKET_EBADF);\n    Assert::assert(!$socket->accept());\n    Assert::same($socket->errCode, SOCKET_EBADF);\n    Assert::assert(!$socket->connect('127.0.0.1', 9501));\n    Assert::same($socket->errCode, SOCKET_EBADF);\n    Assert::assert(!$socket->send(get_safe_random()));\n    Assert::same($socket->errCode, SOCKET_EBADF);\n    Assert::assert(!$socket->recv());\n    Assert::same($socket->errCode, SOCKET_EBADF);\n    Assert::assert(!$socket->sendto('127.0.0.1', 9501, get_safe_random()));\n    Assert::same($socket->errCode, SOCKET_EBADF);\n    Assert::assert(!$socket->recvfrom($peer));\n    Assert::same($socket->errCode, SOCKET_EBADF);\n    Assert::assert(!$socket->getsockname());\n    Assert::same($socket->errCode, SOCKET_EBADF);\n    Assert::assert(!$socket->getpeername());\n    Assert::same($socket->errCode, SOCKET_EBADF);\n    echo \"DONE\\n\";\n});\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/complete_test.phpt",
    "content": "--TEST--\nswoole_socket_coro: complete test server&&client&&timeout(millisecond)\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Socket;\nuse Swoole\\Event;\n\n$pm = new ProcessManager();\n$port = get_one_free_port();\n$pm->parentFunc = function ($pid) use ($pm, $port) {\n    $socket = new Socket(AF_INET, SOCK_STREAM, 0);\n    Assert::isInstanceOf($socket, Socket::class);\n    Assert::same($socket->errCode, 0);\n    go(function () use ($socket, $port) {\n        Assert::assert($socket->connect('localhost', $port));\n        $i = 0.000;\n        while (true) {\n            $socket->send('hello');\n            $server_reply = $socket->recv(1024, 0.1);\n            Assert::same($server_reply, 'swoole');\n            co::sleep($i += .001); // after 10 times we sleep 0.01s to trigger server timeout\n            if ($i > .01) {\n                break;\n            }\n        }\n        co::sleep(0.5);\n        echo \"client exit\\n\";\n        $socket->close();\n    });\n    Event::wait();\n};\n\n$pm->childFunc = function () use ($pm, $port) {\n    $socket = new Socket(AF_INET, SOCK_STREAM, 0);\n    Assert::assert($socket->bind('127.0.0.1', $port));\n    Assert::assert($socket->listen(128));\n    go(function () use ($socket, $pm) {\n        $pm->wakeup();\n        $client = $socket->accept();\n        Assert::assert($client, 'error: ' . swoole_last_error());\n        Assert::isInstanceOf($client, Socket::class);\n        $i = 0;\n        while (true) {\n            $client_data = $client->recv(1024, 0.1);\n            if ($client->errCode > 0) {\n                Assert::same($client->errCode, SOCKET_ETIMEDOUT);\n                break;\n            }\n            $i++;\n            Assert::same($client_data, 'hello');\n            $client->send('swoole');\n        }\n        echo \"{$i}\\n\";\n        echo \"sever exit\\n\";\n        usleep(1);\n        $client->close();\n        $socket->close();\n    });\n    Event::wait();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n10\nsever exit\nclient exit\n"
  },
  {
    "path": "tests/swoole_socket_coro/concurrency.phpt",
    "content": "--TEST--\nswoole_socket_coro: concurrency\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst N = 100;\n\n$socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, 0);\n$socket->bind('127.0.0.1', 9601);\n$socket->listen(128);\n\ngo(function () use ($socket)\n{\n    for ($i = 0; $i < N; $i++)\n    {\n        $client = $socket->accept();\n        go(function () use ($client)\n        {\n            while (true)\n            {\n                $data = $client->recv();\n                if (empty($data))\n                {\n                    $client->close();\n                    break;\n                }\n                $client->send(\"Server: $data\");\n            }\n        });\n    }\n});\n\nfor ($i = 0; $i < N; $i++)\n{\n    go(function ()\n    {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $ret = $cli->connect('127.0.0.1', 9601);\n        if ($ret)\n        {\n            $cli->send(\"hello\\n\");\n            Assert::same($cli->recv(), 'Server: hello'.\"\\n\");\n            $cli->close();\n        }\n        else\n        {\n            echo \"ERROR\\n\";\n        }\n    });\n}\nSwoole\\Event::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_socket_coro/construct_parse_args_failed.phpt",
    "content": "--TEST--\nswoole_socket_coro: construct parse arguments failed\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_php_version_lower_than('7.2');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nvar_dump(new Co\\Socket());\n?>\n--EXPECTF--\nFatal error: Uncaught ArgumentCountError: Swoole\\Coroutine\\Socket::__construct() expects at least 2 %s, 0 given in %s/tests/swoole_socket_coro/construct_parse_args_failed.php:3\nStack trace:\n#0 %s/tests/swoole_socket_coro/construct_parse_args_failed.php(3): Swoole\\Coroutine\\Socket->__construct()\n#1 {main}\n  thrown in %s/tests/swoole_socket_coro/construct_parse_args_failed.php on line 3\n"
  },
  {
    "path": "tests/swoole_socket_coro/fd.phpt",
    "content": "--TEST--\nswoole_socket_coro: fd\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$sockets = [];\nfor ($n = MAX_REQUESTS; $n--;) {\n    $sockets[] = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    if (count($sockets) > 1) {\n        Assert::assert(end($sockets)->fd === prev($sockets)->fd + 1);\n    }\n}\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/getBoundCid.phpt",
    "content": "--TEST--\nswoole_socket_coro: getBoundCid\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager();\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $sock = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, 0);\n        Assert::assert($sock->connect('127.0.0.1', $pm->getFreePort()));\n        set_socket_coro_buffer_size($sock, 8192);\n        $write_co = $read_co = -1;\n        go(function () use ($pm, $sock, &$write_co, &$read_co) {\n            Co::sleep(0.001);\n            echo \"CLOSE\\n\";\n            Assert::eq($sock->getBoundCid(SWOOLE_EVENT_READ), $read_co);\n            Assert::eq($sock->getBoundCid(SWOOLE_EVENT_WRITE), $write_co);\n            $sock->close();\n            $pm->kill();\n            echo \"DONE\\n\";\n        });\n        $write_co = go(function () use ($sock) {\n            echo \"SEND\\n\";\n            $size = 16 * 1024 * 1024;\n            Assert::lessThan($sock->sendAll(str_repeat('S', $size)), $size);\n            Assert::eq($sock->errCode, SOCKET_ECANCELED);\n            echo \"SEND CLOSED\\n\";\n        });\n        $read_co = go(function () use ($sock) {\n            echo \"RECV\\n\";\n            Assert::false($sock->recv(-1));\n            Assert::eq($sock->errCode, SOCKET_ECANCELED);\n            echo \"RECV CLOSED\\n\";\n        });\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $server = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::assert($server->bind('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($server->listen());\n        go(function () use ($pm, $server) {\n            if (Assert::assert(($conn = $server->accept()) && $conn instanceof Co\\Socket)) {\n                switch_process();\n                co::sleep(5);\n                $conn->close();\n            }\n            $server->close();\n        });\n        $pm->wakeup();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nSEND\nRECV\nCLOSE\nSEND CLOSED\nRECV CLOSED\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/getopt/get.phpt",
    "content": "--TEST--\nswoole_socket_coro/getopt: getOption\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$domain = AF_INET;\n$level = IPPROTO_IP;\n\n$socket = new Co\\Socket($domain, SOCK_DGRAM, SOL_UDP);\n\necho \"Setting IP_MULTICAST_TTL\\n\";\n$r = $socket->setOption($level, IP_MULTICAST_TTL, 9);\nvar_dump($r);\n$r = $socket->getOption($level, IP_MULTICAST_TTL);\nvar_dump($r);\necho \"\\n\";\n\necho \"Setting IP_MULTICAST_LOOP\\n\";\n$r = $socket->setOption($level, IP_MULTICAST_LOOP, 0);\nvar_dump($r);\n$r = $socket->getOption($level, IP_MULTICAST_LOOP);\nvar_dump($r);\n$r = $socket->setOption($level, IP_MULTICAST_LOOP, 1);\nvar_dump($r);\n$r = $socket->getOption($level, IP_MULTICAST_LOOP);\nvar_dump($r);\necho \"\\n\";\n\necho \"Setting IP_MULTICAST_IF\\n\";\necho \"interface 0:\\n\";\n$r = $socket->setOption($level, IP_MULTICAST_IF, 0);\nvar_dump($r);\n$r = $socket->getOption($level, IP_MULTICAST_IF);\nvar_dump($r);\necho \"interface 1:\\n\";\n$r = $socket->setOption($level, IP_MULTICAST_IF, 1);\nvar_dump($r);\n$r = $socket->getOption($level, IP_MULTICAST_IF);\nvar_dump($r);\necho \"\\n\";\n?>\n--EXPECT--\nSetting IP_MULTICAST_TTL\nbool(true)\nint(9)\n\nSetting IP_MULTICAST_LOOP\nbool(true)\nint(0)\nbool(true)\nint(1)\n\nSetting IP_MULTICAST_IF\ninterface 0:\nbool(true)\nint(0)\ninterface 1:\nbool(true)\nint(1)\n"
  },
  {
    "path": "tests/swoole_socket_coro/getopt/tcpinfo.phpt",
    "content": "--TEST--\nswoole_socket_coro/getopt: tcp info\n--SKIPIF--\n<?php \nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_not_linux();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire __DIR__ . '/../../include/api/http_test_cases.php';\n\nuse function Swoole\\Coroutine\\run;\n\nrun(function () {\n    $content = http_get_with_co_socket('www.baidu.com', function ($cli, $content){\n        $info = $cli->getOption(SOL_TCP, TCP_INFO);\n        Assert::greaterThan($info['rcv_space'], 0);\n        Assert::greaterThan($info['rto'], 0);\n        Assert::greaterThan($info['rtt'], 0);\n        Assert::greaterThan($info['snd_mss'], 0);\n        Assert::greaterThan($info['rcv_mss'], 0);\n        echo \"DONE\\n\";\n    });\n    Assert::assert(strpos($content, 'map.baidu.com') !== false);\n});\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/getpeername.phpt",
    "content": "--TEST--\nswoole_socket_coro: getpeername\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\n\nCo\\run(\n    function () {\n        $conn = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        $conn->connect('www.baidu.com', 80);\n        $info = $conn->getpeername();\n        Assert::eq($info['address'], System::gethostbyname('www.baidu.com'));\n        Assert::eq($info['port'], 80);\n    }\n);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_socket_coro/getsockname.phpt",
    "content": "--TEST--\nswoole_socket_coro: getsockname\n--SKIPIF--\n<?php require __DIR__.'/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__.'/../include/bootstrap.php';\n$conn = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n$conn->bind('127.0.0.1');\n$info = $conn->getsockname();\nAssert::same($info['address'] ?? '', '127.0.0.1');\nAssert::greaterThan($info['port'], 0);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_socket_coro/icmp.phpt",
    "content": "--TEST--\nswoole_socket_coro: icmp\n--SKIPIF--\n<?php \nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_not_root();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nCo\\run(function () {\n    /* ICMP ping packet with a pre-calculated checksum */\n    $host = '127.0.0.1';\n    $package = \"\\x08\\x00\\x7d\\x4b\\x00\\x00\\x00\\x00PingHost\";\n    $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_RAW, 1);\n    Assert::assert($socket->connect($host));\n    $socket->send($package, strlen($package));\n    $pkt = $socket->recv(256);\n    Assert::notEmpty($pkt);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_socket_coro/import_1.phpt",
    "content": "--TEST--\nswoole_socket_coro: import 1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nCo\\run(function () {\n    $s = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0);\n\n    $s0 = reset($s);\n    $s1 = next($s);\n\n    $sock = Swoole\\Coroutine\\Socket::import($s0);\n    Assert::notEmpty($sock);\n    $sock->send(\"test message\");\n    $sock->close();\n\n    var_dump(stream_get_contents($s1));\n});\n?>\n--EXPECTF--\nstring(12) \"test message\"\n"
  },
  {
    "path": "tests/swoole_socket_coro/import_2.phpt",
    "content": "--TEST--\nswoole_socket_coro: import 2\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_php_version_lower_than('8.0');\nskip_if_extension_not_exist('sockets');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nCo\\run(function () {\n    var_dump(Swoole\\Coroutine\\Socket::import(fopen(__FILE__, \"rb\")));\n    try {\n        Swoole\\Coroutine\\Socket::import(socket_create(AF_INET, SOCK_DGRAM, SOL_UDP));\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n    $s = stream_socket_server(\"udp://127.0.0.1:0\", $errno, $errstr, STREAM_SERVER_BIND);\n    var_dump($s);\n    var_dump(fclose($s));\n    try {\n        Swoole\\Coroutine\\Socket::import($s);\n    } catch (TypeError $e) {\n        echo $e->getMessage(), \"\\n\";\n    }\n\n    echo \"Done.\";\n});\n?>\n--EXPECTF--\nWarning: Swoole\\Coroutine\\Socket::import(): Cannot represent a stream of type %s as a Socket Descriptor in %s on line %d\nbool(false)\nSwoole\\Coroutine\\Socket::import(): Argument #1 ($stream) must be of type resource, %s given\nresource(%d) of type (stream)\nbool(true)\nSwoole\\Coroutine\\Socket::import(): supplied resource is not a valid stream resource\nDone.\n"
  },
  {
    "path": "tests/swoole_socket_coro/import_3.phpt",
    "content": "--TEST--\nswoole_socket_coro: import 3\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nif (!extension_loaded('sockets')) {\n    die('SKIP sockets extension not available.');\n}\n$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);\n$br = @socket_bind($s, '0.0.0.0', 58379);\nif ($br === false)\n    die(\"SKIP IPv4/port 58379 not available\");\n$so = @socket_set_option($s, IPPROTO_IP, MCAST_JOIN_GROUP, array(\n    \"group\" => '224.0.0.23',\n    \"interface\" => \"lo\",\n));\nif ($so === false) {\n    die(\"SKIP joining group 224.0.0.23 on interface lo failed\");\n}\nsocket_close($s);\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nCo\\run(function () {\n    $stream = stream_socket_server(\"udp://0.0.0.0:58379\", $errno, $errstr, STREAM_SERVER_BIND);\n    $sock = Swoole\\Coroutine\\Socket::import($stream);\n    Assert::isInstanceOf($sock, Swoole\\Coroutine\\Socket::class);\n    $so = $sock->setOption(IPPROTO_IP, MCAST_JOIN_GROUP, array(\n        \"group\" => '224.0.0.23',\n        \"interface\" => \"lo\",\n    ));\n    var_dump($so);\n\n    $sendsock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);\n    Assert::isInstanceOf($sock, Swoole\\Coroutine\\Socket::class);\n\n    $br = socket_bind($sendsock, '127.0.0.1');\n    $so = socket_sendto($sendsock, $m = \"my message\", strlen($m), 0, \"224.0.0.23\", 58379);\n    var_dump($so);\n\n    stream_set_blocking($stream, 0);\n    var_dump(fread($stream, strlen($m)));\n    echo \"Done.\\n\";\n});\n?>\n--EXPECTF--\nbool(true)\nint(10)\nstring(10) \"my message\"\nDone.\n"
  },
  {
    "path": "tests/swoole_socket_coro/import_4.phpt",
    "content": "--TEST--\nswoole_socket_coro: import 4\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_php_version_lower_than('8.0');\nskip_if_extension_not_exist('sockets');\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nfunction test($stream, $sock)\n{\n    if ($stream !== null) {\n        echo \"stream_set_blocking \";\n        try {\n            print_r(intval(stream_set_blocking($stream, 0)));\n        } catch (Error $e) {\n            echo get_class($e), \": \", $e->getMessage(), \"\\n\";\n        }\n        echo \"\\n\";\n    }\n    if ($sock !== null) {\n        echo \"socket_set_block \";\n        try {\n            print_r(intval(socket_set_block($sock)));\n        } catch (Error $e) {\n            echo get_class($e), \": \", $e->getMessage(), \"\\n\";\n        }\n        echo \"\\n\";\n        echo \"socket_get_option \";\n        try {\n            print_r(intval(socket_get_option($sock, SOL_SOCKET, SO_TYPE)));\n        } catch (Error $e) {\n            echo get_class($e), \": \", $e->getMessage(), \"\\n\";\n        }\n        echo \"\\n\";\n    }\n    echo \"\\n\";\n}\n\nCo\\run(function () {\n    echo \"normal\\n\";\n    $stream0 = stream_socket_server(\"udp://0.0.0.0:0\", $errno, $errstr, STREAM_SERVER_BIND);\n    $sock0 = Swoole\\Coroutine\\Socket::import($stream0);\n    test($stream0, $sock0);\n\n    echo \"\\nunset stream\\n\";\n    $stream1 = stream_socket_server(\"udp://0.0.0.0:0\", $errno, $errstr, STREAM_SERVER_BIND);\n    $sock1 = Swoole\\Coroutine\\Socket::import($stream1);\n    unset($stream1);\n    test(null, $sock1);\n\n    echo \"\\nunset socket\\n\";\n    $stream2 = stream_socket_server(\"udp://0.0.0.0:0\", $errno, $errstr, STREAM_SERVER_BIND);\n    $sock2 = Swoole\\Coroutine\\Socket::import($stream2);\n    unset($sock2);\n    test($stream2, null);\n\n    echo \"\\nclose stream\\n\";\n    $stream3 = stream_socket_server(\"udp://0.0.0.0:0\", $errno, $errstr, STREAM_SERVER_BIND);\n    $sock3 = Swoole\\Coroutine\\Socket::import($stream3);\n    fclose($stream3);\n    test($stream3, $sock3);\n\n    echo \"\\nclose socket\\n\";\n    $stream4 = stream_socket_server(\"udp://0.0.0.0:0\", $errno, $errstr, STREAM_SERVER_BIND);\n    $sock4 = Swoole\\Coroutine\\Socket::import($stream4);\n    socket_close($sock4);\n    test($stream4, $sock4);\n\n    echo \"Done.\\n\";\n});\n?>\n--EXPECTF--\nnormal\nstream_set_blocking 1\nsocket_set_block 1\nsocket_get_option 2\n\n\nunset stream\nsocket_set_block 1\nsocket_get_option 2\n\n\nunset socket\nstream_set_blocking 1\n\n\nclose stream\nstream_set_blocking TypeError: stream_set_blocking(): supplied resource is not a valid stream resource\n\nsocket_set_block 1\nsocket_get_option 2\n\n\nclose socket\nstream_set_blocking TypeError: stream_set_blocking(): supplied resource is not a valid stream resource\n\nsocket_set_block 0\nsocket_get_option 0\n\nDone.\n--EXPECTF_85--\nnormal\nstream_set_blocking 1\nsocket_set_block 1\nsocket_get_option 2\n\n\nunset stream\nsocket_set_block 1\nsocket_get_option 2\n\n\nunset socket\nstream_set_blocking 1\n\n\nclose stream\nstream_set_blocking TypeError: stream_set_blocking(): Argument #1 ($stream) must be an open stream resource\n\nsocket_set_block 1\nsocket_get_option 2\n\n\nclose socket\nstream_set_blocking TypeError: stream_set_blocking(): Argument #1 ($stream) must be an open stream resource\n\nsocket_set_block 0\nsocket_get_option 0\n\nDone.\n"
  },
  {
    "path": "tests/swoole_socket_coro/import_5.phpt",
    "content": "--TEST--\nswoole_socket_coro: import 5\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nCo\\run(function () {\n    $sock = stream_socket_client(\n        'tcp://www.baidu.com:80',\n        $errno,\n        $errstr,\n        30\n    );\n    if (!$sock) {\n        echo \"$errstr ($errno)\";\n        return;\n    }\n    $socket = socket_import_stream($sock);\n    if ($socket) {\n        socket_write($socket, \"GET / HTTP/1.0\\r\\nHost: www.baidu.com\\r\\nAccept: */*\\r\\n\\r\\n\");\n        $content = '';\n        while (false !== ($tmp = socket_read($socket, 8192))) {\n            if ('' === $tmp) {\n                break;\n            }\n            $content .= $tmp;\n        }\n        socket_close($socket);\n        Assert::assert(strpos($content, 'map.baidu.com') !== false);\n    } else {\n        echo 'import failed';\n        return;\n    }\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_socket_coro/iov_max.phpt",
    "content": "--TEST--\nswoole_socket_coro: iov max\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nuse Swoole\\Coroutine\\Socket;\n\nuse function Swoole\\Coroutine\\run;\n\nrequire __DIR__ . '/../include/bootstrap.php';\n\nrun(function () {\n    $iovector = [];\n    $iovectorLength = [];\n\n    for ($i = 0; $i < SWOOLE_IOV_MAX + 1; $i++) {\n        $iovector[$i] = 'a';\n    }\n\n    for ($i = 0; $i < SWOOLE_IOV_MAX + 1; $i++) {\n        $iovectorLength[$i] = 1;\n    }\n\n    $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    Assert::false($conn->writeVectorAll($iovector));\n    Assert::eq($conn->errCode, SOCKET_EINVAL);\n    Assert::eq($conn->errMsg, \"The maximum of iov count is \" . SWOOLE_IOV_MAX);\n\n    Assert::false($conn->readVectorAll($iovectorLength));\n    Assert::eq($conn->errCode, SOCKET_EINVAL);\n    Assert::eq($conn->errMsg, \"The maximum of iov count is \" . SWOOLE_IOV_MAX);\n});\n\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/peek_and_checkLiveness.phpt",
    "content": "--TEST--\nswoole_socket_coro: peek and checkLiveness\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Socket;\n\n$pm = new ProcessManager;\n$pm->initRandomDataEx(MAX_CONCURRENCY_MID, 1, 1024);\n$pm->parentFunc = function () use ($pm) {\n    Coroutine\\run(function () use ($pm) {\n        for ($c = MAX_CONCURRENCY_MID; $c--;) {\n            Coroutine::create(function () use ($pm, $c) {\n                $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n                Assert::true($socket->connect('127.0.0.1', $pm->getFreePort()));\n                $random = get_safe_random();\n                Assert::same($socket->sendAll($random), strlen($random));\n                for ($n = 100; $n--;) {\n                    Coroutine::sleep(0.001);\n                    $data = $socket->peek(strlen($random));\n                    if ($data === $random) {\n                        break;\n                    }\n                }\n                Assert::greaterThan($n, 0);\n                /* clean the socket buffer */\n                $socket->recv();\n                /* then we check the liveness */\n                Assert::false($socket->checkLiveness());\n            });\n        }\n    });\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->on('WorkerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('Receive', function (Swoole\\Server $server, int $fd, int $rid, string $data) use ($pm) {\n        $server->send($fd, $data);\n        $server->close($fd);\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/protocol/bug_3586.phpt",
    "content": "--TEST--\nswoole_socket_coro/protocol: bug GitHub#3586\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Socket;\n\nconst GREETER = 'Hello Swoole';\nconst CO_NUM = 5;\n$GLOBALS['counter'] = 0;\n\nCoroutine\\run(function () {\n    $server = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    Coroutine::create(function () use ($server) {\n        Assert::assert($server->bind('127.0.0.1'));\n        $server->setProtocol([\n            'open_length_check' => true,\n            'package_length_func' => function (string $data) {\n                if (strlen($data) < strlen(GREETER)) {\n                    return 0;\n                } else {\n                    return strlen(GREETER);\n                }\n            }\n        ]);\n        Assert::assert($server->listen());\n        while (true) {\n            $client = $server->accept(-1);\n            if (!$client) {\n                break;\n            }\n            /* @var $client Socket */\n            Coroutine::create(function () use ($client, $server) {\n                Assert::isInstanceOf($client, Socket::class);\n                Assert::eq($client->recvPacket(), GREETER);\n                Assert::greaterThan($client->sendAll(GREETER), 1);\n                $client->close();\n                $GLOBALS['counter'] ++;\n                if ($GLOBALS['counter'] == CO_NUM) {\n                    $server->close();\n                }\n            });\n        }\n    });\n\n    for ($i = 0; $i < CO_NUM; $i++) {\n        Coroutine::create(function () use ($server) {\n            $client = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n            Assert::true($client->connect('127.0.0.1', $server->getsockname()['port']));\n            if (rand(0, 5) > 3) {\n                $client->send(substr(GREETER, 0, 5));\n                Co::sleep(0.1);\n                $client->send(substr(GREETER, 5));\n            } else {\n                $client->send(GREETER);\n            }\n            Assert::eq($client->recvPacket(), GREETER);\n            $client->close();\n        });\n    }\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_socket_coro/protocol/package_length_func.phpt",
    "content": "--TEST--\nswoole_socket_coro/protocol: package_length_func\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Socket;\n\nconst GREETER = 'Hello Swoole';\n\nCoroutine\\run(function () {\n    $server = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    Coroutine::create(function () use ($server) {\n        Assert::assert($server->bind('127.0.0.1'));\n        Assert::assert($server->listen());\n        while (true) {\n            $client = $server->accept(-1);\n            if (!$client) {\n                break;\n            }\n            /* @var $client Socket */\n            Coroutine::create(function () use ($client) {\n                Assert::isInstanceOf($client, Socket::class);\n                for ($n = 0; $n < strlen(GREETER); $n++) {\n                    Assert::eq($client->sendAll(GREETER[$n]), 1);\n                    Coroutine::sleep(0.01);\n                }\n                $client->close();\n            });\n        }\n        $server->close();\n    });\n    Coroutine::create(function () use ($server) {\n        $client = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::true($client->connect('127.0.0.1', $server->getsockname()['port']));\n        $client->setProtocol([\n            'open_length_check' => true,\n            'package_length_func' => function (string $data) {\n                if (strlen($data) < strlen(GREETER)) {\n                    return 0;\n                } else {\n                    return strlen(GREETER);\n                }\n            }\n        ]);\n        var_dump($client->recvPacket());\n        $client->close();\n        $server->close();\n    });\n});\n\n?>\n--EXPECT--\nstring(12) \"Hello Swoole\"\n"
  },
  {
    "path": "tests/swoole_socket_coro/readVectorAll.phpt",
    "content": "--TEST--\nswoole_socket_coro: readVector all\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nuse Swoole\\Coroutine\\Socket;\n\nuse function Swoole\\Coroutine\\run;\n\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$totalLength = 0;\n$iovector = [];\n$packedStr = '';\n\nfor ($i = 0; $i < 10; $i++) {\n    $iovector[$i] = str_repeat(get_safe_random(1024), 128);\n    $totalLength += strlen($iovector[$i]);\n    $packedStr .= $iovector[$i];\n}\n$totalLength2 = rand(strlen($packedStr) / 2, strlen($packedStr) - 1024 * 128);\n\nrun(function () {\n    $server = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    $port = get_one_free_port();\n\n    go(function () use ($server, $port) {\n        Assert::assert($server->bind('127.0.0.1', $port));\n        Assert::assert($server->listen(512));\n        $conn = $server->accept();\n        Assert::assert($conn instanceof Socket);\n        Assert::assert($conn->fd > 0);\n\n        global $packedStr, $iovector, $totalLength2;\n        Assert::assert($conn instanceof Socket);\n        $iov = [];\n        for ($i = 0; $i < 10; $i++) {\n            $iov[] = 1024 * 128;\n        }\n\n        Assert::eq($conn->readVectorAll($iov), $iovector);\n        Assert::eq(implode('', $conn->readVectorAll($iov)), substr($packedStr, 0, $totalLength2));\n\n        // has close\n        Assert::eq($conn->readVectorAll($iov), []);\n    });\n\n    go(function () use ($server, $port) {\n        global $packedStr, $totalLength, $totalLength2;\n\n        $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::assert($conn->connect('127.0.0.1', $port));\n\n        $ret = $conn->sendAll($packedStr);\n        Assert::eq($ret, $totalLength);\n\n        $ret = $conn->sendAll(substr($packedStr, 0, $totalLength2));\n        Assert::eq($ret, $totalLength2);\n\n        /**\n         *The TCP three-way handshake is handled by the kernel. After connect() succeeds,\n         * the server coroutine's accept() function does not return immediately.\n         * The client coroutine continues to execute sendAll() until an IO block occurs;\n         * only when a writable event is detected does it switch to the server coroutine, at which point accept() returns and proceeds to execute readVectorAll().\n         * If $server->close() is called, it may cause the server coroutine's accept() to return false.\n         */\n        $conn->close();\n    });\n});\n\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/readVectorAll_ssl.phpt",
    "content": "--TEST--\nswoole_socket_coro: readVectorAll with ssl\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Socket;\n\nuse function Swoole\\Coroutine\\run;\n\n$totalLength = 0;\n$iovector = [];\n$packedStr = '';\n\nfor ($i = 0; $i < 10; $i++) {\n    $iovector[$i] = str_repeat(get_safe_random(1024), 128);\n    $totalLength += strlen($iovector[$i]);\n    $packedStr .= $iovector[$i];\n}\n$totalLength2 = rand(strlen($packedStr) / 2, strlen($packedStr) - 1024 * 128);\n\n$pm = new ProcessManager();\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        global $totalLength, $packedStr;\n        $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        $conn->setProtocol([\n            'open_ssl' => true,\n        ]);\n        $conn->connect('127.0.0.1', $pm->getFreePort());\n\n        $ret = $conn->sendAll($packedStr);\n        Assert::eq($ret, $totalLength);\n        $conn->recv();\n        echo \"DONE\\n\";\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        global $totalLength, $iovector;\n        $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        $socket->setProtocol([\n            'open_ssl' => true,\n            'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n            'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n        ]);\n        Assert::assert($socket->bind('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($socket->listen(MAX_CONCURRENCY));\n\n        $pm->wakeup();\n        /** @var Socket */\n        $conn = $socket->accept();\n        Assert::assert($conn, 'error: ' . swoole_last_error());\n        $conn->sslHandshake();\n\n        $iov = [];\n        for ($i = 0; $i < 10; $i++) {\n            $iov[] = 1024 * 128;\n        }\n\n        Assert::eq($conn->readVectorAll($iov), $iovector);\n        $conn->send('close');\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/readVector_ssl.phpt",
    "content": "--TEST--\nswoole_socket_coro: readVector with ssl\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Socket;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager();\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n\n        $conn->setProtocol([\n            'open_ssl' => true,\n        ]);\n        $conn->connect('127.0.0.1', $pm->getFreePort());\n\n        $ret = $conn->send('helloworld');\n        Assert::eq($ret, strlen('helloworld'));\n        $conn->recv();\n        echo \"DONE\\n\";\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        global $totalLength, $packedStr;\n        $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        $socket->setProtocol([\n            'open_ssl' => true,\n            'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n            'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n        ]);\n        Assert::assert($socket->bind('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($socket->listen(MAX_CONCURRENCY));\n\n        $pm->wakeup();\n        /** @var Socket */\n        $conn = $socket->accept();\n        Assert::assert($conn, 'error: ' . swoole_last_error());\n        $conn->sslHandshake();\n\n        Assert::eq($conn->readVector([5, 5]), ['hello', 'world']);\n        $conn->send('close');\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/readVector_ssl_eagain.phpt",
    "content": "--TEST--\nswoole_socket_coro: readVector_ssl eagain\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Socket;\n\nuse function Swoole\\Coroutine\\run;\n\n$totalLength = 0;\n$iovector = [];\n$packedStr = '';\n\nfor ($i = 0; $i < 10; $i++) {\n    $iovector[$i] = str_repeat(get_safe_random(1024), 128);\n    $totalLength += strlen($iovector[$i]);\n    $packedStr .= $iovector[$i];\n}\n$totalLength2 = rand(strlen($packedStr) / 2, strlen($packedStr) - 1024 * 128);\n\n$pm = new ProcessManager();\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        global $totalLength, $packedStr;\n        $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        $conn->setProtocol([\n            'open_ssl' => true,\n        ]);\n        $conn->connect('127.0.0.1', $pm->getFreePort());\n\n        Coroutine::sleep(0.5);\n        $ret = $conn->sendAll($packedStr);\n        Assert::eq($ret, $totalLength);\n        $conn->recv();\n        echo \"DONE\\n\";\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        global $totalLength, $iovector;\n        $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        $socket->setProtocol([\n            'open_ssl' => true,\n            'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n            'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n        ]);\n        Assert::assert($socket->bind('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($socket->listen(MAX_CONCURRENCY));\n\n        $pm->wakeup();\n        /** @var Socket */\n        $conn = $socket->accept();\n        Assert::assert($conn, 'error: ' . swoole_last_error());\n        $conn->sslHandshake();\n\n        $iov = [];\n        for ($i = 0; $i < 10; $i++) {\n            $iov[] = 1024 * 128;\n        }\n\n        Assert::eq($conn->readVectorAll($iov), $iovector);\n        $conn->send('close');\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/readv.phpt",
    "content": "--TEST--\nswoole_socket_coro: readv test\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nuse Swoole\\Coroutine\\Socket;\nuse Swoole\\Coroutine\\Server;\nuse Swoole\\Coroutine\\Server\\Connection;\n\nuse function Swoole\\Coroutine\\run;\n\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        $conn->connect('127.0.0.1', $pm->getFreePort());\n        $conn->send('hello');\n        $iov = [5, 5];\n        $ret = $conn->readVector($iov);\n        Assert::same($ret, ['hello', 'world']);\n\n        $conn->send('hello');\n        $iov = [5, 7];\n        $ret = $conn->readVector($iov);\n        Assert::same($ret, ['hello', 'world']);\n\n        $conn->send('hello');\n        $iov = [5, 7, 7];\n        $ret = $conn->readVector($iov);\n        Assert::same($ret, ['hello', 'world']);\n        $pm->kill();\n        echo \"DONE\\n\";\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $server = new Server('127.0.0.1', $pm->getFreePort(), false);\n\n        $server->handle(function (Connection $conn) use ($server) {\n            while (true) {\n                $conn->recv();\n                $conn->send(\"helloworld\");\n            }\n        });\n\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/readv_eagain.phpt",
    "content": "--TEST--\nswoole_socket_coro: readv with eagain\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Socket;\n\nuse function Swoole\\Coroutine\\run;\n\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$totalLength = 0;\n$iovector = [];\n$packedStr = '';\n\n\nfor ($i = 0; $i < 10; $i++) {\n    $iovector[$i] = str_repeat(get_safe_random(1024), 128);\n    $totalLength += strlen($iovector[$i]);\n    $packedStr .= $iovector[$i];\n}\n$totalLength2 = rand(strlen($packedStr) / 2, strlen($packedStr) - 1024 * 128);\n\nrun(function () {\n    $server = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    $port = get_one_free_port();\n\n    go(function () use ($server, $port) {\n        Assert::assert($server->bind('127.0.0.1', $port));\n        Assert::assert($server->listen(512));\n        $conn = $server->accept();\n        Assert::assert($conn instanceof  Socket);\n        Assert::assert($conn->fd > 0);\n\n        global $packedStr, $iovector, $totalLength2;\n        Assert::assert($conn instanceof Socket);\n        $iov = [];\n        for ($i = 0; $i < 10; $i++) {\n            $iov[] = 1024 * 128;\n        }\n\n        Assert::eq($conn->readVectorAll($iov), $iovector);\n        Assert::eq(implode('', $conn->readVectorAll($iov)), substr($packedStr, 0, $totalLength2));\n\n        // has close\n        Assert::eq($conn->readVectorAll($iov), []);\n    });\n\n    go(function () use ($server, $port) {\n        global $packedStr, $totalLength, $totalLength2;\n\n        $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::assert($conn->connect('127.0.0.1', $port));\n\n        // Let readVectorAll trigger EAGAIN (verify the correctness of the error returned by readVectorAll)\n        Coroutine::sleep(0.5);\n\n        $ret = $conn->sendAll($packedStr);\n        Assert::eq($ret, $totalLength);\n\n        $ret = $conn->sendAll(substr($packedStr, 0, $totalLength2));\n        Assert::eq($ret, $totalLength2);\n\n        $server->close();\n    });\n});\n\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/recvAll.phpt",
    "content": "--TEST--\nswoole_socket_coro: recvAll\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->initRandomDataArray(4, 512 * 1024);\n\nconst CASE_LIST = '4';\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $conn = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::assert($conn->connect('127.0.0.1', $pm->getFreePort()));\n        /**\n         * Case1，第一次接收到少量內容，随后分多次触发可读事件接收更多数据片\n         */\n        if (swoole_string(CASE_LIST)->split(',')->contains(1, false)) {\n            $conn->send(\"case-1\");\n            Co::sleep(0.001);\n            $header = $conn->recvAll(4);\n            $len = unpack('Nlen', $header)['len'];\n            $body = $conn->recvAll($len);\n            Assert::eq($body, $pm->getRandomDataElement(0));\n        }\n        /**\n         * Case2，第一次未接收到任何内容，返回 EAGAIN，监听可读，随后分多次触发可读事件接收更多数据片\n         */\n        if (swoole_string(CASE_LIST)->split(',')->contains(2, false)) {\n            $conn->send(\"case-2\");\n            $header = $conn->recvAll(4);\n            $len = unpack('Nlen', $header)['len'];\n            $body = $conn->recvAll($len);\n            Assert::eq($body, $pm->getRandomDataElement(1));\n        }\n        /**\n         * Case3，第一次收到少量内容，第二次服务端关闭连接\n         */\n        if (swoole_string(CASE_LIST)->split(',')->contains(3, false)) {\n            $conn->send(\"case-3\");\n            Co::sleep(0.001);\n            $header = $conn->recvAll(4);\n            $len = unpack('Nlen', $header)['len'];\n            $body = $conn->recvAll($len);\n            Assert::eq($body, substr($pm->getRandomDataElement(2), 0,1024));\n        }\n        /**\n         * Case4，接收4次，第5次服务端关闭连接\n         */\n        if (swoole_string(CASE_LIST)->split(',')->contains(4, false)) {\n            $conn->send(\"case-4\");\n            Co::sleep(0.001);\n            $header = $conn->recvAll(4);\n            $len = unpack('Nlen', $header)['len'];\n            $body = $conn->recvAll($len);\n            Assert::eq($body, substr($pm->getRandomDataElement(3), 0, 65536 * 3));\n        }\n    });\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->on('WorkerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('Receive', function (Swoole\\Server $server, $fd, $rid, $data) use ($pm) {\n        if ($data == 'case-1') {\n            $body = $pm->getRandomDataElement(0);\n            $data = pack('N', strlen($body)) . $body;\n            $server->send($fd, substr($data, 0, 1024));\n            usleep(40000);\n            $server->send($fd, substr($data, 1024));\n        } elseif ($data == 'case-2') {\n            $body = $pm->getRandomDataElement(1);\n            $server->send($fd, pack('N', strlen($body)));\n            usleep(40000);\n            $server->send($fd, $body);\n        } elseif ($data == 'case-3') {\n            $body = $pm->getRandomDataElement(2);\n            $server->send($fd, pack('N', strlen($body)));\n            $server->send($fd, substr($body, 0, 1024));\n            usleep(40000);\n            $server->close($fd);\n        } elseif ($data == 'case-4') {\n            $body = $pm->getRandomDataElement(3);\n            $server->send($fd, pack('N', strlen($body)));\n            for ($i = 0; $i < 3; $i++) {\n                $server->send($fd, substr($body, $i * 65536, 65536));\n                usleep(40000);\n            }\n            $server->close($fd);\n        }\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/recvAll_timeout.phpt",
    "content": "--TEST--\nswoole_socket_coro: recvAll timeout (slow network)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->initRandomDataEx(MAX_CONCURRENCY_MID, 1, 1024);\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($c = MAX_CONCURRENCY_MID; $c--;) {\n        go(function () use ($pm, $c) {\n            $conn = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n            Assert::assert($conn->connect('127.0.0.1', $pm->getFreePort()));\n            $conn->send($c);\n            $timeout = ms_random(0.1, 1);\n            $s = microtime(true);\n            $data = $conn->recvAll(1024, $timeout);\n            time_approximate($timeout, microtime(true) - $s);\n            Assert::assert(strlen($data) > 0);\n            Assert::assert(strlen($data) != 1024);\n            Assert::assert(strpos($pm->getRandomDataEx($c), $data) === 0);\n            Assert::assert($conn->errCode == SOCKET_ETIMEDOUT);\n            Assert::assert($conn->errMsg == swoole_strerror(SOCKET_ETIMEDOUT));\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->on('WorkerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('Receive', function (Swoole\\Server $server, int $fd, int $rid, string $data) use ($pm) {\n        $s = $pm->getRandomDataEx($data);\n        while ($server->exists($fd)) {\n            $server->send($fd, string_pop_front($s, 1));\n            Co::sleep(0.005);\n        }\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/recv_bad_packet.phpt",
    "content": "--TEST--\nswoole_socket_coro: recv bad packet\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst SIZE = 2 * 1024 * 1024;\n\n$pm = new SwooleTest\\ProcessManager;\n\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) {\n        go(function () use ($pm, $i) {\n            $cli = new Co\\Socket(AF_INET, SOCK_STREAM, 0);\n            $cli->setProtocol([\n                'open_length_check' => true,\n                'package_max_length' => 4 * 1024 * 1024,\n                'package_length_type' => 'N',\n                'package_length_offset' => 0,\n                'package_body_offset' => 4,\n            ]);\n            if ($cli->connect('127.0.0.1', $pm->getFreePort()) == false) {\n                echo \"ERROR\\n\";\n                return;\n            }\n            for ($i = 0; $i < 3; $i++) {\n                $sid = strval(rand(100000, 999999));\n                $send_data = str_repeat('A', 1000) . $sid;\n                $cli->send(pack('N', strlen($send_data)) . $send_data);\n                $data = $cli->recvPacket(0.2);\n                Assert::isEmpty($data);\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        \"worker_num\" => 4,\n        'log_level' => SWOOLE_LOG_ERROR,\n        'open_length_check' => true,\n        'package_max_length' => 4 * 1024 * 1024,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n    ));\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n        $len = rand(1024, 8192);\n        $send_data = str_repeat('A', $len);\n        //bad packet\n        $serv->send($fd, pack('N', SIZE) . $send_data);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_socket_coro/recv_line.phpt",
    "content": "--TEST--\nswoole_socket_coro: recv line\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Server;\nuse Swoole\\Coroutine\\Server\\Connection;\n\nCo\\run(function () {\n    $port = get_one_free_port();\n    go(function() use($port) {\n        $server = new Server('0.0.0.0', $port, false);\n\n        $server->handle(function (Connection $conn) use ($server) {\n            $conn->send(\"hello world\\n\");\n            $conn->send(\"\\r\");\n            $conn->send(str_repeat('A', 128) . str_repeat('B', 125));\n            $server->shutdown();\n        });\n\n        $server->start();\n    });\n\n    $cli = new Co\\Socket(AF_INET, SOCK_STREAM, 0);\n    if ($cli->connect('127.0.0.1', $port) == false) {\n        echo \"ERROR\\n\";\n        return;\n    }\n    Assert::eq($cli->recvLine(128), \"hello world\\n\");\n    Assert::eq($cli->recvLine(128), \"\\r\");\n    Assert::eq($cli->recvLine(128), str_repeat('A', 128));\n    Assert::eq($cli->recvLine(128), str_repeat('B', 125));\n    Assert::eq($cli->recvLine(128), \"\");\n});\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_socket_coro/recv_timeout.phpt",
    "content": "--TEST--\nswoole_socket_coro: recv timeout\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    for ($c = MAX_CONCURRENCY_MID; $c--;) {\n        go(function () use ($pm) {\n            $conn = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n            Assert::assert($conn->connect('127.0.0.1', $pm->getFreePort()));\n            $conn->send(json_encode(['data' => 'hello']));\n            $timeout = ms_random(0.1, 1);\n            $s = microtime(true);\n            $case = mt_rand(0, 2);\n            switch ($case) {\n                case 0:\n                    $ret = $conn->recv(1024, $timeout);\n                    break;\n                case 1:\n                    $ret = $conn->recvAll(1024, $timeout);\n                    break;\n                case 2:\n                    $ret = $conn->recvfrom($peer, $timeout);\n                    break;\n            }\n            time_approximate($timeout, microtime(true) - $s);\n            Assert::assert($ret === false, \"[case=$case]\");\n            Assert::assert($conn->errCode == SOCKET_ETIMEDOUT);\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->on('WorkerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('Receive', function () { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/recv_with_buffer.phpt",
    "content": "--TEST--\nswoole_socket_coro: recv with buffer\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Server;\nuse Swoole\\Coroutine\\Server\\Connection;\n\nconst DATA = \"hello world\\n\";\n\nCo\\run(function () {\n    $port = get_one_free_port();\n    go(function () use ($port) {\n        $server = new Server('0.0.0.0', $port, false);\n\n        $server->handle(function (Connection $conn) use ($server) {\n            $conn->send(DATA);\n            $server->shutdown();\n        });\n\n        $server->start();\n    });\n\n    $cli = new Co\\Socket(AF_INET, SOCK_STREAM, 0);\n    if ($cli->connect('127.0.0.1', $port) == false) {\n        echo \"ERROR\\n\";\n        return;\n    }\n\n    $data = '';\n    while (1) {\n        $char = $cli->recvWithBuffer(1);\n        if (strlen($char) == 1) {\n            $data .= $char;\n        } else {\n            break;\n        }\n    }\n    Assert::eq($data, DATA);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_socket_coro/reuse.phpt",
    "content": "--TEST--\nswoole_socket_coro: reuse socket object\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst SEND_STR = \"hello world\\n\";\n\nuse SwooleTest\\ProcessManager;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n\n    echo \"Co [1]\\n\";\n\n    $map = [];\n    Co\\Run(function () use ($pm, &$map) {\n        $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, 0);\n        Assert::assert($socket->connect('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($socket->send(SEND_STR));\n        echo $socket->recv();\n        $map['sock'] = $socket;\n    });\n\n    echo \"Co [2]\\n\";\n\n    Co\\Run(function () use ($pm, &$map) {\n        $socket = $map['sock'];\n        Assert::assert($socket->send(SEND_STR));\n        echo $socket->recv();\n        unset($map['sock']);\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, 0);\n    Assert::assert($socket->bind('127.0.0.1', $pm->getFreePort()));\n    Assert::assert($socket->listen(128));\n    $pm->wakeup();\n    go(function () use ($socket, $pm) {\n        $client = $socket->accept();\n        Assert::isInstanceOf($client, Swoole\\Coroutine\\Socket::class);\n        while (true) {\n            $client_data = $client->recv(1024, -1);\n            if (empty($client_data)) {\n                echo \"closed\\n\";\n                break;\n            }\n            if ($client->errCode > 0) {\n                Assert::same($client->errCode, SOCKET_ETIMEDOUT);\n                break;\n            } else {\n                Assert::same($client_data, SEND_STR);\n                $client->send('swoole '.SEND_STR);\n            }\n        }\n        $client->close();\n        $socket->close();\n        echo \"server exit\\n\";\n    });\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nCo [1]\nswoole hello world\nCo [2]\nswoole hello world\nclosed\nserver exit\n"
  },
  {
    "path": "tests/swoole_socket_coro/reuse_2.phpt",
    "content": "--TEST--\nswoole_socket_coro: reuse socket object [2]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst SEND_STR = \"hello world\\n\";\n\nuse SwooleTest\\ProcessManager;\nuse Swoole\\Constant;\nuse Swoole\\Server;\n\n$pm = new ProcessManager;\n\n$pm->initFreePorts(2);\n\n$pm->parentFunc = function ($pid) use ($pm) {\n\n    $map = [];\n\n    $serv = new Server('127.0.0.1', $pm->getFreePort(1), SWOOLE_BASE);\n\n    $serv->on(Constant::EVENT_WORKER_START, function (Server $server)  use ($pm, &$map) {\n        $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, 0);\n        Assert::assert($socket->connect('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($socket->send(SEND_STR));\n        echo $socket->recv();\n        $map['sock'] = $socket;\n\n        $server->shutdown();\n    });\n\n    $serv->on(Constant::EVENT_RECEIVE, function () {\n\n    });\n\n    echo \"Server start [1]\\n\";\n    $serv->start();\n    echo \"Server stop [1]\\n\";\n\n    echo \"Co [2]\\n\";\n\n    Co\\Run(function () use ($pm, &$map) {\n        $socket = $map['sock'];\n        Assert::assert($socket->send(SEND_STR));\n        echo $socket->recv();\n        unset($map['sock']);\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, 0);\n    Assert::assert($socket->bind('127.0.0.1', $pm->getFreePort()));\n    Assert::assert($socket->listen(128));\n    $pm->wakeup();\n    go(function () use ($socket, $pm) {\n        $client = null;\n        while (true) {\n            $client = $socket->accept();\n            if ($client) {\n                break;\n            }\n        }\n        Assert::isInstanceOf($client, Swoole\\Coroutine\\Socket::class);\n        while (true) {\n            $client_data = $client->recv(1024, -1);\n            if (empty($client_data)) {\n                echo \"closed\\n\";\n                break;\n            }\n            if ($client->errCode > 0) {\n                Assert::same($client->errCode, SOCKET_ETIMEDOUT);\n                break;\n            } else {\n                Assert::same($client_data, SEND_STR);\n                $client->send('swoole '.SEND_STR);\n            }\n        }\n        $client->close();\n        $socket->close();\n        echo \"server exit\\n\";\n    });\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nServer start [1]\nswoole hello world\nServer stop [1]\nCo [2]\nswoole hello world\nclosed\nserver exit\n"
  },
  {
    "path": "tests/swoole_socket_coro/sendfile.phpt",
    "content": "--TEST--\nswoole_socket_coro: sendfile\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\n\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    Co\\Run(function ()  use ($pm) {\n        $conn = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::assert($conn->connect('127.0.0.1', $pm->getFreePort()));\n\n        $conn->send(pack('N', filesize(TEST_IMAGE)));\n        $ret = $conn->sendfile(TEST_IMAGE);\n        Assert::assert($ret);\n\n        $data = $conn->recv();\n        $conn->send(pack('N', 8) . 'shutdown');\n        $conn->close();\n        Assert::same($data, md5_file(TEST_IMAGE));\n    });\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP);\n    $serv->set([\n        \"worker_num\" => 1,\n        'log_file' => '/dev/null',\n        'open_length_check' => true,\n        'dispatch_mode' => 1,\n        'package_length_type' => 'N',\n        'package_length_offset' => 0,\n        'package_body_offset' => 4,\n        'package_max_length' => 2000000,\n    ]);\n    $serv->on(\"WorkerStart\", function (Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on(\"Receive\", function (Server $serv, $fd, $rid, $data)\n    {\n        if (substr($data, 4, 8) == 'shutdown')\n        {\n            $serv->shutdown();\n            return;\n        }\n        $serv->send($fd, md5(substr($data, 4)));\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_socket_coro/sendto.phpt",
    "content": "--TEST--\nswoole_socket_coro: sendto\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst N = 5;\n//Server\ngo(function () {\n    $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_DGRAM, 0);\n    $socket->bind('127.0.0.1', 9601);\n    for ($i = 0; $i < N; $i++)\n    {\n        $peer = null;\n        $data = $socket->recvfrom($peer);\n        echo \"[Server] recvfrom[{$peer['address']}:{$peer['port']}] : $data\\n\";\n        $socket->sendto($peer['address'], $peer['port'], \"Swoole: $data\");\n    }\n});\n\n//Client\ngo(function () {\n    $socket = new  Swoole\\Coroutine\\Socket(AF_INET, SOCK_DGRAM, 0);\n    for ($i = 0; $i < N; $i++)\n    {\n        $socket->sendto('127.0.0.1', 9601, \"hello-\".$i);\n        $peer = null;\n        $data = $socket->recvfrom($peer);\n        echo \"[Client] recvfrom[{$peer['address']}:{$peer['port']}] : $data\\n\";\n    }\n});\nSwoole\\Event::wait();\n?>\n--EXPECTF--\n[Server] recvfrom[127.0.0.1:%d] : hello-0\n[Client] recvfrom[127.0.0.1:9601] : Swoole: hello-0\n[Server] recvfrom[127.0.0.1:%d] : hello-1\n[Client] recvfrom[127.0.0.1:9601] : Swoole: hello-1\n[Server] recvfrom[127.0.0.1:%d] : hello-2\n[Client] recvfrom[127.0.0.1:9601] : Swoole: hello-2\n[Server] recvfrom[127.0.0.1:%d] : hello-3\n[Client] recvfrom[127.0.0.1:9601] : Swoole: hello-3\n[Server] recvfrom[127.0.0.1:%d] : hello-4\n[Client] recvfrom[127.0.0.1:9601] : Swoole: hello-4\n"
  },
  {
    "path": "tests/swoole_socket_coro/sendto_big.phpt",
    "content": "--TEST--\nswoole_socket_coro: sendto\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$randomData = '';\nCo\\run(function () {\n    go(function () {\n        $socket = new Swoole\\Coroutine\\Socket(AF_UNIX, SOCK_DGRAM, IPPROTO_IP);\n        $socket->bind('/tmp/test-server.sock', 0);\n        $data = $socket->recvfrom($peer);\n        if (!$data) {\n            return;\n        }\n        Assert::same($data, 'hello');\n        Assert::same($peer['address'], '/tmp/test-client.sock');\n        Assert::same($peer['port'], 0);\n        global $randomData;\n        for ($n = 0; $n < MAX_CONCURRENCY * MAX_REQUESTS; $n++) {\n            $chunk = get_safe_random(1024);\n            $randomData .= $chunk;\n            Assert::same($socket->sendto($peer['address'], $peer['port'], $chunk), strlen($chunk));\n        }\n        // close\n        Assert::same($socket->sendto($peer['address'], $peer['port'], ''), 0);\n    });\n    go(function () {\n        $socket = new  Swoole\\Coroutine\\Socket(AF_UNIX, SOCK_DGRAM, IPPROTO_IP);\n        $socket->bind('/tmp/test-client.sock', 0);\n        $socket->sendto('/tmp/test-server.sock', 0, 'hello');\n        $data = '';\n        while (true) {\n            $tmp = $socket->recvfrom($peer);\n            if (empty($tmp)) {\n                break;\n            }\n            $data .= $tmp;\n        }\n        global $randomData;\n        if (Assert::same($data, $randomData)) {\n            echo \"OK\\n\";\n        }\n    });\n});\n?>\n--EXPECT--\nOK\n"
  },
  {
    "path": "tests/swoole_socket_coro/sendto_large_packet.phpt",
    "content": "--TEST--\nswoole_socket_coro: send large packet\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Socket;\nuse Swoole\\Event;\n\nconst N = 5;\nconst MIN_PACKET_SIZE = IS_MAC_OS ? 4000 : 30000;\nconst MAX_PACKET_SIZE = IS_MAC_OS ? 8000 : 65000;\n\n// Server\ngo(function () {\n    $socket = new Socket(AF_INET, SOCK_DGRAM, 0);\n    $socket->bind('127.0.0.1', 9601);\n    for ($i = 0; $i < N; $i++) {\n        $peer = null;\n        $data = $socket->recvfrom($peer);\n        Assert::assert($data);\n        Assert::assert($socket->sendto($peer['address'], $peer['port'], \"Swoole: {$data}\"));\n        Assert::assert(strlen($data) >= MIN_PACKET_SIZE);\n        Assert::assert(is_array($peer));\n    }\n});\n\n// Client\ngo(function () {\n    $socket = new Socket(AF_INET, SOCK_DGRAM, 0);\n    for ($i = 0; $i < N; $i++) {\n        Assert::assert($socket->sendto('127.0.0.1', 9601, str_repeat('A', rand(MIN_PACKET_SIZE, MAX_PACKET_SIZE))), 'error: ' . swoole_strerror($socket->errCode));\n        $peer = null;\n        $data = $socket->recvfrom($peer);\n        Assert::assert($data);\n        Assert::assert(is_array($peer));\n        Assert::assert(strlen($data) >= MIN_PACKET_SIZE);\n    }\n});\nEvent::wait();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_socket_coro/server_accept.phpt",
    "content": "--TEST--\nswoole_socket_coro: server accept\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Socket;\n\nclass Protocol\n{\n    const HEAD_LENGTH = 4 + 2 + 4;\n    const LENGTH_OFFSET = 6;\n    const HEAD_PACK_FORMAT = 'nNN';\n    const HEAD_UNPACK_FORMAT = 'ntype/Nid/Nlength';\n\n    public static function pack(int $type, int $id, string $data): string\n    {\n        return pack(static::HEAD_PACK_FORMAT, $type, $id, strlen($data)) . $data;\n    }\n\n    public static function unpack(string $data): array\n    {\n        return unpack(static::HEAD_UNPACK_FORMAT, $data);\n    }\n}\n\nCo\\run(function () {\n    $server = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    $server->setProtocol([\n        'open_length_check' => true,\n        'package_length_type' => 'N',\n        'package_length_offset' => Protocol::LENGTH_OFFSET,\n        'package_body_offset' => Protocol::HEAD_LENGTH\n    ]);\n    if (!$server->bind('127.0.0.1')) {\n        throw new Exception('Bind failed: ' . $server->errMsg);\n    }\n    if (!$server->listen()) {\n        throw new Exception('Listen failed: ' . $server->errMsg);\n    }\n    $port = $server->getsockname()['port'] ?? 0;\n    if (!$port) {\n        throw new Exception('No port');\n    }\n    for ($n = MAX_CONCURRENCY; $n--;) {\n        Coroutine::create(function () use ($port) {\n            Co::sleep(0.01);\n            $client = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n            $connected = $client->connect('127.0.0.1', $port, 3);\n            if (!$connected) {\n                throw new Exception('Connect failed: ' . $client->errMsg);\n            }\n            for ($n = MAX_REQUESTS; $n--;) {\n                $bytes = mt_rand(2, 1024);\n                $random = $bytes ? get_safe_random($bytes - 1) . 'S' : '';\n                $data = Protocol::pack(mt_rand(0, 127), mt_rand(0, 4294967295), $random);\n                if ($client->sendAll($data) !== strlen($data)) {\n                    throw new Exception('Send failed: ' . $client->errMsg);\n                }\n                $head = $client->recvAll(Protocol::HEAD_LENGTH);\n                if (strlen($head) !== Protocol::HEAD_LENGTH) {\n                    throw new Exception('Recv head failed: ' . $client->errMsg);\n                }\n                $head = Protocol::unpack($head);\n                $length = $head['length'];\n                if ($length !== 0) {\n                    $body = $client->recvAll($length, -1);\n                    if (strlen($body) !== $length) {\n                        throw new Exception('Recv body failed: ' . $client->errMsg);\n                    }\n                    if (Assert::same($body[strlen($body) - 1], 'W')) {\n                        $body[strlen($body) - 1] = 'S';\n                    }\n                    Assert::same($body, $random);\n                }\n            }\n            $client->close();\n        });\n    }\n    Coroutine::create(function () use ($server) {\n        while (true) {\n            Coroutine::sleep(0.1);\n            if (Coroutine::stats()['coroutine_num'] === 2) {\n                $server->close();\n                break;\n            }\n        }\n    });\n    while (true) {\n        /* @var $client Socket */\n        $client = $server->accept(-1);\n        if (!$client) {\n            break;\n        }\n        go(function () use ($client) {\n            while (true) {\n                $packet = $client->recvPacket(-1);\n                if (!$packet) {\n                    /* Connection closed */\n                    break;\n                }\n                if (strlen($packet) > Protocol::HEAD_LENGTH) {\n                    Assert::same($packet[strlen($packet) - 1], 'S');\n                    $packet[strlen($packet) - 1] = 'W';\n                }\n                $client->sendAll($packet);\n            }\n        });\n    }\n});\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/setopt/bindtodevice.phpt",
    "content": "--TEST--\nswoole_socket_coro/setopt:SO_BINDTODEVICE\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nif (!extension_loaded('sockets')) {\n    die('SKIP sockets extension not available.');\n}\nif (!defined(\"SO_BINDTODEVICE\")) {\n    die('SKIP SO_BINDTODEVICE not supported on this platform.');\n}\nif (!function_exists(\"posix_getuid\") || posix_getuid() != 0) {\n    die('SKIP SO_BINDTODEVICE requires root permissions.');\n}\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$socket = new Co\\Socket(AF_INET, SOCK_STREAM, SOL_TCP);\n\n$retval_1 = $socket->setOption(SOL_SOCKET, SO_BINDTODEVICE, \"lo\");\nAssert::assert($retval_1 === true);\n\n$retval_2 = $socket->setOption(SOL_SOCKET, SO_BINDTODEVICE, \"ethIDONOTEXIST\");\nAssert::assert($retval_2 === false);\n?>\n--EXPECTF--\nWarning: Swoole\\Coroutine\\Socket::setOption(): setsockopt(%d) failed, Error: No such device[%d] in %s on line %d\n"
  },
  {
    "path": "tests/swoole_socket_coro/setopt/ipv6_pktinfo.phpt",
    "content": "--TEST--\nswoole_socket_coro/setopt: setOption IPV6_PKTINFO\n--SKIPIF--\n<?php \nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_not_linux();\n ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$socket = new Co\\Socket(AF_INET6, SOCK_DGRAM, SOL_UDP);\n\nvar_dump(@$socket->setOption(IPPROTO_IPV6, IPV6_PKTINFO, []));\nvar_dump($socket->setOption(IPPROTO_IPV6, IPV6_PKTINFO, [\n    \"addr\" => '::1',\n    \"ifindex\" => 0\n]));\n\n?>\n--EXPECTF--\nbool(false)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_socket_coro/setopt/multicast.phpt",
    "content": "--TEST--\nswoole_socket_coro/setopt: multicast\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\nskip_if_darwin();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$socket = new Co\\Socket(AF_INET, SOCK_DGRAM, SOL_UDP);\n$socket->bind('0.0.0.0', 9905);\n\n$ret = $socket->setOption(IPPROTO_IP, MCAST_JOIN_GROUP, array(\n    'group' => '224.10.20.30',\n    'interface' => 0\n));\n\nif ($ret === false)\n{\n    throw new RuntimeException('Unable to join multicast group');\n}\n\ngo(function () use ($socket) {\n    $n = 10;\n    while($n--) {\n        $addr = [];\n        $data = $socket->recvfrom($addr);\n        Assert::assert(strlen($data) > 10);\n        Assert::assert(!empty($addr['port']));\n        Assert::assert(!empty($addr['address']));\n    }\n});\n\ngo(function () use ($socket) {\n    $client = new Co\\Client(SWOOLE_SOCK_UDP);\n    $client->connect('224.10.20.30', 9905);\n    $n = 10;\n    while($n--) {\n        $client->send(\"hello world [$n]\\n\");\n        co::sleep(.03);\n    }\n});\n\nSwoole\\Event::wait();\n\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_socket_coro/setopt/recvtimeo.phpt",
    "content": "--TEST--\nswoole_socket_coro/setopt: setOption SO_RCVTIMEO\n--DESCRIPTION--\n-wrong params\n-set/get params comparison\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$socket = new Co\\Socket(AF_INET, SOCK_STREAM, SOL_TCP);\n\n//wrong params\n$retval_1 = $socket->setOption(SOL_SOCKET, SO_RCVTIMEO, array());\nAssert::assert($retval_1 === false);\n$options = array(\"sec\" => 1, \"usec\" => 0);\n$retval_2 = $socket->setOption(SOL_SOCKET, SO_RCVTIMEO, $options);\nAssert::assert($retval_2 === true);\n\n?>\n--EXPECTF--\nWarning: Swoole\\Coroutine\\Socket::setOption(): no key \"sec\" passed in optval in %s on line %d\n"
  },
  {
    "path": "tests/swoole_socket_coro/setopt/reuse.phpt",
    "content": "--TEST--\nswoole_socket_coro/setopt: setOption SO_RCVTIMEO\n--DESCRIPTION--\n-wrong params\n-set/get params comparison\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$socket = new Co\\Socket(AF_INET, SOCK_STREAM, SOL_TCP);\n$socket->bind(\"127.0.0.1\", 9501);\n\nAssert::assert($socket->setOption(SOL_SOCKET, SO_REUSEADDR, true));\nAssert::assert($socket->setOption(SOL_SOCKET, SO_REUSEPORT, true));\n\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_socket_coro/shutdown.phpt",
    "content": "--TEST--\nswoole_socket_coro: shutdown\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nfor ($n = 2; $n--;) {\n    $randoms[] = get_safe_random();\n}\ngo(function () use ($randoms) {\n    $server = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    Assert::true($server->bind('127.0.0.1', 9601));\n    Assert::true($server->listen(512));\n    $conn = $server->accept();\n    Assert::isInstanceOf($conn, Swoole\\Coroutine\\Socket::class);\n    Assert::same($conn->recv(), array_shift($randoms));\n    Assert::greaterThan($conn->send(array_shift($randoms)), 0);\n    $conn->close();\n    $server->close();\n});\ngo(function () use ($randoms) {\n    $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    Assert::true($socket->connect('127.0.0.1', 9601));\n    Assert::greaterThan($socket->send(array_shift($randoms)), 0);\n    Assert::same($socket->recv(), array_shift($randoms));\n    Assert::true($socket->shutdown(STREAM_SHUT_WR));\n    for ($n = MAX_REQUESTS; $n--;) {\n        Assert::false($socket->send(array_shift($randoms)));\n        Assert::same($socket->errCode, SOCKET_EPIPE);\n    }\n    Assert::assert($socket->shutdown(STREAM_SHUT_RD));\n    for ($n = MAX_REQUESTS; $n--;) {\n        Assert::assert(!$socket->recv());\n    }\n    Assert::false($socket->shutdown());\n    Assert::same($socket->errCode, SOCKET_ENOTCONN);\n    Assert::true($socket->close());\n    Assert::false($socket->send(''));\n    Assert::false($socket->recv());\n    Assert::same($socket->errCode, SOCKET_EBADF);\n});\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/ssl.phpt",
    "content": "--TEST--\nswoole_socket_coro: ssl client\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_no_ssl();\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nrequire __DIR__ . '/../include/api/http_test_cases.php';\n\nuse function Swoole\\Coroutine\\run;\n\nrun(function () {\n    $content = http_get_with_co_socket('www.baidu.com');\n    Assert::assert(strpos($content, 'map.baidu.com') !== false);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_socket_coro/ssl_bad_server.phpt",
    "content": "--TEST--\nswoole_socket_coro: ssl connect with bad server\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_no_ssl();\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\n\nCo::set(['log_level' => SWOOLE_LOG_WARNING]);\n\nrun(function () {\n    $cli = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, 0);\n    $cli->setProtocol(['open_ssl' => true,]);\n\n    Assert::false ($cli->connect('www.baidu.com', 80));\n    Assert::eq($cli->errCode, SWOOLE_ERROR_SSL_HANDSHAKE_FAILED);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_socket_coro/tcp-c10k.phpt",
    "content": "--TEST--\nswoole_socket_coro: server and client concurrency\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nSwoole\\Runtime::enableCoroutine();\n\n$port = get_one_free_port();\ngo(function () use ($port) {\n    $socket = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    Assert::assert($socket->bind('127.0.0.1', $port));\n    Assert::assert($socket->listen(MAX_CONCURRENCY_MID));\n    $i = 0;\n    while ($conn = $socket->accept()) {\n        go(function () use ($socket, $conn, &$i) {\n            for ($n = MAX_REQUESTS; $n--;) {\n                $data = $conn->recv(tcp_length($conn->recv(tcp_type_length())));\n                Assert::same($data, \"Hello Swoole Server #{$n}!\");\n                $conn->send(tcp_pack(\"Hello Swoole Client #{$n}!\"));\n            }\n            $conn->close();\n            if (++$i === MAX_CONCURRENCY_MID) {\n                $socket->close();\n                echo \"DONE\\n\";\n            }\n        });\n    }\n});\nfor ($c = MAX_CONCURRENCY_MID; $c--;) {\n    go(function () use ($port) {\n        $client = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, 0);\n        Assert::assert($client->connect('127.0.0.1', $port));\n        for ($n = MAX_REQUESTS; $n--;) {\n            $client->send(tcp_pack(\"Hello Swoole Server #{$n}!\"));\n            $data = $client->recv(tcp_length($client->recv(tcp_type_length())));\n            Assert::same($data, \"Hello Swoole Client #{$n}!\");\n        }\n        $client->close();\n    });\n}\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/ulimit.phpt",
    "content": "--TEST--\nswoole_socket_coro: new socket failed\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nif ($argv[1] ?? '' === 'ulimit') {\n    try {\n        for ($n = MAX_CONCURRENCY + 1; $n--;) {\n            $sockets[] = new Co\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        }\n        echo 'never here' . PHP_EOL;\n    } catch (Co\\Socket\\Exception $e) {\n        Assert::assert($e->getCode() === SOCKET_EMFILE);\n        echo \"DONE\\n\";\n    }\n} else {\n    $n = MAX_CONCURRENCY;\n    $_SERVER['TEST_PHP_EXECUTABLE'] = $_SERVER['TEST_PHP_EXECUTABLE'] ?? 'php';\n    $dir = __DIR__;\n    file_put_contents(\n        '/tmp/ulimit.sh',\n        \"ulimit -n {$n} && {$_SERVER['TEST_PHP_EXECUTABLE']} {$_SERVER['PHP_SELF']} ulimit\"\n    );\n    echo shell_exec('/bin/sh /tmp/ulimit.sh');\n    @unlink('/tmp/ulimit.sh');\n}\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/unix_dgram.phpt",
    "content": "--TEST--\nswoole_socket_coro: unix dgram\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\ngo(function () {\n    @unlink('/tmp/test-server.sock');\n    $server = new Swoole\\Coroutine\\Socket(AF_UNIX, SOCK_DGRAM, IPPROTO_IP);\n    $server->bind('/tmp/test-server.sock');\n    go(function () use ($server) {\n        while ($data = $server->recvfrom($peer)) {\n            Assert::same($data, 'hello');\n            $server->sendto($peer['address'], 0, 'world');\n        }\n        var_dump($peer);\n    });\n    go(function () use ($server) {\n        @unlink('/tmp/test-client.sock');\n        $client = new Swoole\\Coroutine\\Socket(AF_UNIX, SOCK_DGRAM, IPPROTO_IP);\n        $client->bind('/tmp/test-client.sock');\n        $peer = [];\n        for ($n = MAX_REQUESTS; $n--;) {\n            $client->sendto('/tmp/test-server.sock', 0, 'hello');\n            $data = $client->recvfrom($peer);\n            Assert::notEmpty($data);\n            if (empty($data)) {\n                break;\n            }\n            Assert::same($data, 'world');\n        }\n        var_dump($peer);\n        $client->close();\n        $server->close();\n    });\n});\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\narray(2) {\n  [\"address\"]=>\n  string(21) \"/tmp/test-server.sock\"\n  [\"port\"]=>\n  int(0)\n}\narray(2) {\n  [\"address\"]=>\n  string(21) \"/tmp/test-client.sock\"\n  [\"port\"]=>\n  int(0)\n}\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/unix_stream.phpt",
    "content": "--TEST--\nswoole_socket_coro: unix stream\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst SOCK_FILE = '/tmp/test-server.sock';\n\nCo\\run(function () {\n    @unlink(SOCK_FILE);\n    $server = new Swoole\\Coroutine\\Socket(AF_UNIX, SOCK_STREAM, IPPROTO_IP);\n    $server->bind(SOCK_FILE);\n    $server->listen();\n\n    go(function () use ($server) {\n        while (!$server->isClosed()) {\n            $conn = $server->accept();\n            while ($data = $conn->recv()) {\n                Assert::same($data, 'hello');\n                $conn->send('world');\n            }\n        }\n    });\n\n    go(function () use ($server) {\n        $client = new Swoole\\Coroutine\\Socket(AF_UNIX, SOCK_STREAM, IPPROTO_IP);\n        $client->connect(SOCK_FILE);\n        for ($n = MAX_REQUESTS; $n--;) {\n            $client->send('hello');\n            $data = $client->recv();\n            Assert::notEmpty($data);\n            if (empty($data)) {\n                break;\n            }\n            Assert::same($data, 'world');\n        }\n        $client->close();\n        $server->close();\n    });\n});\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/writeVectorAll.phpt",
    "content": "--TEST--\nswoole_socket_coro: writeVector all\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nuse Swoole\\Coroutine\\Socket;\n\nuse function Swoole\\Coroutine\\run;\n\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$totalLength = 0;\n$iovector = [];\n$packedStr = '';\n\nfor ($i = 0; $i < 10; $i++) {\n    $iovector[$i] = str_repeat(get_safe_random(1024), 128);\n    $totalLength += strlen($iovector[$i]);\n    $packedStr .= $iovector[$i];\n}\n\nrun(function () {\n    $server = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    $port = get_one_free_port();\n\n    go(function () use ($server, $port) {\n        Assert::assert($server->bind('127.0.0.1', $port));\n        Assert::assert($server->listen(512));\n        $conn = $server->accept();\n        Assert::assert($conn instanceof  Socket);\n        Assert::assert($conn->fd > 0);\n\n        global $totalLength, $packedStr;\n        Assert::assert($conn instanceof Socket);\n        Assert::eq($conn->recvAll($totalLength), $packedStr);\n    });\n\n    go(function () use ($server, $port) {\n        global $iovector, $totalLength;\n\n        $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::assert($conn->connect('127.0.0.1', $port));\n        $ret = $conn->writeVectorAll($iovector);\n        Assert::eq($ret, $totalLength);\n\n        $conn->close();\n    });\n});\n\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/writeVectorAll_ssl.phpt",
    "content": "--TEST--\nswoole_socket_coro: writeVector with ssl\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Socket;\n\nuse function Swoole\\Coroutine\\run;\n\n$totalLength = 0;\n$iovector = [];\n$packedStr = '';\n\nfor ($i = 0; $i < 10; $i++) {\n    $iovector[$i] = str_repeat(get_safe_random(1024), 128);\n    $totalLength += strlen($iovector[$i]);\n    $packedStr .= $iovector[$i];\n}\n$totalLength2 = rand(strlen($packedStr) / 2, strlen($packedStr) - 1024 * 128);\n\n$pm = new ProcessManager();\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        global $totalLength, $iovector;\n        $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        $conn->setProtocol([\n            'open_ssl' => true,\n        ]);\n        $conn->connect('127.0.0.1', $pm->getFreePort());\n\n        $ret = $conn->writeVectorAll($iovector);\n        Assert::eq($ret, $totalLength);\n        $conn->recv();\n        echo \"DONE\\n\";\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        global $totalLength, $packedStr;\n        $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        $socket->setProtocol([\n            'open_ssl' => true,\n            'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n            'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n        ]);\n        Assert::assert($socket->bind('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($socket->listen(MAX_CONCURRENCY));\n\n        $pm->wakeup();\n        /** @var Socket */\n        $conn = $socket->accept();\n        Assert::assert($conn, 'error: ' . swoole_last_error());\n        $conn->sslHandshake();\n\n        Assert::eq($conn->recvAll($totalLength), $packedStr, -1);\n        $conn->send('close');\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/writeVector_ssl.phpt",
    "content": "--TEST--\nswoole_socket_coro: writeVector with ssl\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Socket;\nuse Swoole\\Server;\n\nuse function Swoole\\Coroutine\\run;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n\n        $conn->setProtocol([\n            'open_ssl' => true,\n        ]);\n        $conn->connect('127.0.0.1', $pm->getFreePort());\n\n        $iov = ['hello', 'world'];\n        $ret = $conn->writeVector($iov);\n        Assert::eq($ret, strlen('helloworld'));\n        $pm->kill();\n        echo \"DONE\\n\";\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);\n    $serv->set([\n        'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n        'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n    ]);\n    $serv->on(\"workerStart\", function ($serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function ($serv, $fd, $tid, $data) {\n        Assert::eq($data, 'helloworld');\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/writeVector_ssl_eagain.phpt",
    "content": "--TEST--\nswoole_socket_coro: writeVector_ssl eagain\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Socket;\n\nuse function Swoole\\Coroutine\\run;\n\n$totalLength = 0;\n$iovector = [];\n$packedStr = '';\n\nfor ($i = 0; $i < 10; $i++) {\n    $iovector[$i] = str_repeat(get_safe_random(1024), 128);\n    $totalLength += strlen($iovector[$i]);\n    $packedStr .= $iovector[$i];\n}\n$totalLength2 = rand(strlen($packedStr) / 2, strlen($packedStr) - 1024 * 128);\n\n$pm = new ProcessManager();\n$pm->parentFunc = function ($pid) use ($pm) {\n    run(function () use ($pm) {\n        global $totalLength, $iovector;\n        $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        $conn->setProtocol([\n            'open_ssl' => true,\n        ]);\n        $conn->connect('127.0.0.1', $pm->getFreePort());\n\n        $ret = $conn->writeVectorAll($iovector);\n        Assert::eq($ret, $totalLength);\n        $conn->recv();\n        echo \"DONE\\n\";\n    });\n};\n\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        global $totalLength, $packedStr;\n        $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        $socket->setProtocol([\n            'open_ssl' => true,\n            'ssl_cert_file' => SSL_FILE_DIR . '/server.crt',\n            'ssl_key_file' => SSL_FILE_DIR . '/server.key',\n        ]);\n        Assert::assert($socket->bind('127.0.0.1', $pm->getFreePort()));\n        Assert::assert($socket->listen(MAX_CONCURRENCY));\n\n        $pm->wakeup();\n\n        /** @var Socket */\n        $conn = $socket->accept();\n        Assert::assert($conn, 'error: ' . swoole_last_error());\n        $conn->sslHandshake();\n\n        Coroutine::sleep(0.5);\n\n        Assert::eq($conn->recvAll($totalLength), $packedStr, -1);\n        $conn->send('close');\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/writev.phpt",
    "content": "--TEST--\nswoole_socket_coro: writev test\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nuse Swoole\\Coroutine\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Coroutine\\Socket;\n\nuse function Swoole\\Coroutine\\run;\n\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $requestLine = \"POST / HTTP/1.1\\r\\n\";\n        $header = \"Host: 127.0.0.1\\r\\n\";\n        $header .= \"Connection: keep-alive\\r\\n\";\n        $header .= \"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\\r\\n\";\n        $header .= \"Content-Length: 5\\r\\n\";\n        $header .= \"User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36\\r\\n\";\n        $header .= \"\\r\\n\";\n\n        $body = 'hello';\n\n        $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        $conn->connect('127.0.0.1', $pm->getFreePort());\n        $ret = $conn->writeVector([$requestLine, $header, $body]);\n        Assert::same($ret, strlen($requestLine) + strlen($header) + strlen($body));\n        $ret = $conn->recv();\n        Assert::contains($ret, 'world');\n        $pm->kill();\n        echo \"DONE\\n\";\n    });\n};\n$pm->childFunc = function () use ($pm) {\n    run(function () use ($pm) {\n        $server = new Server(\"127.0.0.1\", $pm->getFreePort(), false);\n        $server->handle('/', function (Request $request, Response $response) {\n            Assert::same($request->getContent(), 'hello');\n            $response->end('world');\n        });\n\n        $server->start();\n    });\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_socket_coro/writev_eagain.phpt",
    "content": "--TEST--\nswoole_socket_coro: writev with eagain\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Socket;\n\nuse function Swoole\\Coroutine\\run;\n\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$totalLength = 0;\n$iovector = [];\n$packedStr = '';\n\nfor ($i = 0; $i < 10; $i++) {\n    $iovector[$i] = str_repeat(get_safe_random(1024), 128);\n    $totalLength += strlen($iovector[$i]);\n    $packedStr .= $iovector[$i];\n}\n\nrun(function () {\n    $server = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    $port = get_one_free_port();\n\n    go(function () use ($server, $port) {\n        Assert::assert($server->bind('127.0.0.1', $port));\n        Assert::assert($server->listen(512));\n        $conn = $server->accept();\n        Assert::assert($conn instanceof  Socket);\n        Assert::assert($conn->fd > 0);\n\n        global $totalLength, $packedStr;\n        Assert::assert($conn instanceof Socket);\n\n        // Let writeVectorAll trigger EAGAIN (verify the correctness of the error returned by writeVectorAll)\n        Coroutine::sleep(0.5);\n        Assert::eq($conn->recvAll($totalLength), $packedStr);\n    });\n\n    go(function () use ($server, $port) {\n        global $iovector, $totalLength;\n\n        $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n        Assert::assert($conn->connect('127.0.0.1', $port));\n        $ret = $conn->writeVectorAll($iovector);\n        Assert::eq($ret, $totalLength);\n        $conn->close();\n    });\n});\n\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_ssh2/bug63480.phpt",
    "content": "--TEST--\nBug #63480 (Warning on using the SSH2 Session resource in the uri)\n--SKIPIF--\n<?php\nrequire_once 'ssh2_skip.inc';\nssh2t_needs_auth();\nssh2t_writes_remote();\n?>\n--FILE--\n<?php\nrequire_once 'ssh2_test.inc';\nCo\\run(function () {\n    $ssh = ssh2_connect(TEST_SSH2_HOSTNAME, TEST_SSH2_PORT);\n    ssh2t_auth($ssh);\n\n    $filename = ssh2t_tempnam();\n    file_put_contents(\"ssh2.sftp://{$ssh}/{$filename}\", 'yada yada');\n    readfile(\"ssh2.sftp://{$ssh}/{$filename}\");\n    unlink(\"ssh2.sftp://{$ssh}/{$filename}\");\n});\n?>\n--EXPECT--\nyada yada\n"
  },
  {
    "path": "tests/swoole_ssh2/bug79631.phpt",
    "content": "--TEST--\nBug 79631 (SSH disconnect segfault with SFTP (assertion failed))\n--SKIPIF--\n<?php\nrequire_once 'ssh2_skip.inc';\nssh2t_needs_auth();\nssh2t_writes_remote();\n?>\n--FILE--\n<?php\nrequire_once 'ssh2_test.inc';\nCo\\run(function () {\n    $ssh = ssh2_connect(TEST_SSH2_HOSTNAME, TEST_SSH2_PORT);\n    ssh2t_auth($ssh);\n    $sftp = ssh2_sftp($ssh);\n    ssh2_disconnect($ssh);\n    echo \"done\\n\";\n});\n?>\n--EXPECT--\ndone\n"
  },
  {
    "path": "tests/swoole_ssh2/ssh2_auth.phpt",
    "content": "--TEST--\nssh2_auth_FOO() - Attempt to authenticate to a remote host\n--SKIPIF--\n<?php require_once 'ssh2_skip.inc';\nssh2t_needs_auth(); ?>\n--FILE--\n<?php\nrequire_once 'ssh2_test.inc';\nCo\\run(function () {\n    $ssh = ssh2_connect(TEST_SSH2_HOSTNAME, TEST_SSH2_PORT);\n    var_dump(ssh2t_auth($ssh));\n});\n?>\n--EXPECT--\nbool(true)\n"
  },
  {
    "path": "tests/swoole_ssh2/ssh2_auth_pubkey.phpt",
    "content": "--TEST--\nssh2_auth_pubkey() - Tests authentication with a key\n--SKIPIF--\n<?php\nrequire_once __DIR__ . '/../include/skipif.inc';\nrequire_once 'ssh2_skip.inc';\nskip_if_in_ci();\n?>\n--FILE--\n<?php\nrequire_once 'ssh2_test.inc';\nCo\\run(function () {\n    $ssh = ssh2_connect(TEST_SSH2_HOSTNAME, TEST_SSH2_PORT);\n    var_dump(ssh2_auth_pubkey($ssh, TEST_SSH2_USER, file_get_contents(TEST_SSH2_PUB_KEY), file_get_contents(TEST_SSH2_PRIV_KEY)));\n\n    $cmd = ssh2_exec($ssh, 'echo \"testing echo with key auth\"' . PHP_EOL);\n\n    var_dump($cmd);\n\n    stream_set_blocking($cmd, true);\n    $response = stream_get_contents($cmd);\n    echo $response . PHP_EOL;\n});\n?>\n--EXPECTF--\nbool(true)\nresource(%d) of type (stream)\ntesting echo with key auth\n\n"
  },
  {
    "path": "tests/swoole_ssh2/ssh2_auth_pubkey_file.phpt",
    "content": "--TEST--\nssh2_auth_pubkey_file() - Tests authentication with a key\n--SKIPIF--\n<?php\nrequire_once __DIR__ . '/../include/skipif.inc';\nrequire_once 'ssh2_skip.inc';\nskip_if_in_ci();\n?>\n--FILE--\n<?php require_once 'ssh2_test.inc';\nCo\\run(function () {\n    $ssh = ssh2_connect(TEST_SSH2_HOSTNAME, TEST_SSH2_PORT);\n\n    var_dump(ssh2_auth_pubkey_file($ssh, TEST_SSH2_USER, TEST_SSH2_PUB_KEY, TEST_SSH2_PRIV_KEY));\n\n    $cmd = ssh2_exec($ssh, 'echo \"testing echo with key auth\"' . PHP_EOL);\n\n    var_dump($cmd);\n\n    stream_set_blocking($cmd, true);\n    $response = stream_get_contents($cmd);\n    echo $response . PHP_EOL;\n});\n?>\n--EXPECTF--\nbool(true)\nresource(%d) of type (stream)\ntesting echo with key auth\n\n"
  },
  {
    "path": "tests/swoole_ssh2/ssh2_connect.phpt",
    "content": "--TEST--\nssh2_connect() Basic connection and pre-authentication\n--SKIPIF--\n<?php require_once 'ssh2_skip.inc'; ?>\n--FILE--\n<?php\nrequire_once 'ssh2_test.inc';\nrequire_once __DIR__ . '/../include/bootstrap.php';\n\nCo\\run(function () {\n    echo \"**Connect\\n\";\n    $ssh = ssh2_connect(TEST_SSH2_HOSTNAME, TEST_SSH2_PORT);\n    var_dump(is_resource($ssh));\n    var_dump(get_resource_type($ssh));\n\n    echo \"**Fingerprint MD5\\n\";\n    $md5 = ssh2_fingerprint($ssh);\n    var_dump(is_string($md5));\n    var_dump(strlen($md5));\n    var_dump(ctype_xdigit($md5));\n\n    echo \"**Fingerprint SHA1\\n\";\n    $sha1 = ssh2_fingerprint($ssh, SSH2_FINGERPRINT_SHA1 | SSH2_FINGERPRINT_HEX);\n    var_dump(is_string($sha1));\n    var_dump(strlen($sha1));\n    var_dump(ctype_xdigit($sha1));\n\n    function ssh2t_strset($v)\n    {\n        return is_string($v) && (strlen($v) > 0);\n    }\n\n    echo \"**Negotiation\\n\";\n    $mn = ssh2_methods_negotiated($ssh);\n    var_dump(ssh2t_strset($mn['kex']));\n    var_dump(ssh2t_strset($mn['hostkey']));\n    foreach (['client_to_server', 'server_to_client'] as $direction) {\n        $mnd = $mn[$direction];\n        var_dump(ssh2t_strset($mnd['crypt']));\n        var_dump(ssh2t_strset($mnd['comp']));\n        var_dump(ssh2t_strset($mnd['mac']));\n    }\n\n    Assert::true(ssh2_disconnect($ssh));\n});\n?>\n--EXPECT--\n**Connect\nbool(true)\nstring(12) \"SSH2 Session\"\n**Fingerprint MD5\nbool(true)\nint(32)\nbool(true)\n**Fingerprint SHA1\nbool(true)\nint(40)\nbool(true)\n**Negotiation\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\n\n"
  },
  {
    "path": "tests/swoole_ssh2/ssh2_exec.phpt",
    "content": "--TEST--\nssh2_shell_test() - Tests opening a shell\n--SKIPIF--\n<?php require_once 'ssh2_skip.inc';\nssh2t_needs_auth(); ?>\n--FILE--\n<?php require_once 'ssh2_test.inc';\nCo\\run(function () {\n    $ssh = ssh2_connect(TEST_SSH2_HOSTNAME, TEST_SSH2_PORT);\n    var_dump(ssh2t_auth($ssh));\n\n    $cmd = ssh2_exec($ssh, 'echo \"testing echo\"' . PHP_EOL);\n\n    var_dump($cmd);\n\n    stream_set_blocking($cmd, true);\n    $response = stream_get_contents($cmd);\n    echo $response . PHP_EOL;\n});\n?>\n--EXPECTF--\nbool(true)\nresource(%d) of type (stream)\ntesting echo\n\n"
  },
  {
    "path": "tests/swoole_ssh2/ssh2_scp_recv.phpt",
    "content": "--TEST--\nssh2_scp_recv() - Tests receiving a file via SCP\n--CREDITS--\nChris MacPherson\n--SKIPIF--\n<?php require_once 'ssh2_skip.inc';\nssh2t_needs_auth();\nssh2t_writes_remote();\n?>\n--FILE--\n<?php require_once 'ssh2_test.inc';\n\nCo\\run(function () {\n    $ssh = ssh2_connect(TEST_SSH2_HOSTNAME, TEST_SSH2_PORT);\n    var_dump(ssh2t_auth($ssh));\n\n    // Create a test file on the remote server\n    $remote_file = ssh2t_tempnam();\n    $remote_content = 'This is test content for SCP receive';\n\n    // Write test content to remote file\n    $sftp = ssh2_sftp($ssh);\n    $fp = fopen(\"ssh2.sftp://{$sftp}/{$remote_file}\", 'w');\n    fwrite($fp, $remote_content);\n    fclose($fp);\n\n    // Local file path\n    $local_file = sys_get_temp_dir() . '/ssh2_scp_recv_test_' . uniqid();\n\n    // Receive file via SCP\n    var_dump(ssh2_scp_recv($ssh, $remote_file, $local_file));\n\n    // Verify the file was received correctly\n    var_dump(file_exists($local_file));\n    var_dump(file_get_contents($local_file) === $remote_content);\n\n    // Clean up\n    unlink($local_file);\n    ssh2_sftp_unlink($sftp, $remote_file);\n});\n?>\n--EXPECTF--\nbool(true)\nbool(true)\nbool(true)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_ssh2/ssh2_scp_send.phpt",
    "content": "--TEST--\nssh2_scp_send() - Tests sending a file via SCP\n--CREDITS--\nChris MacPherson\n--SKIPIF--\n<?php require_once 'ssh2_skip.inc';\nssh2t_needs_auth();\nssh2t_writes_remote();\n?>\n--FILE--\n<?php require_once 'ssh2_test.inc';\n\nCo\\run(function () {\n    $ssh = ssh2_connect(TEST_SSH2_HOSTNAME, TEST_SSH2_PORT);\n    var_dump(ssh2t_auth($ssh));\n\n    // Create a test file locally\n    $local_file = sys_get_temp_dir() . '/ssh2_scp_send_test_' . uniqid();\n    $test_content = 'This is test content for SCP send';\n    file_put_contents($local_file, $test_content);\n\n    // Remote file path\n    $remote_file = ssh2t_tempnam();\n\n    // Send file via SCP\n    var_dump(ssh2_scp_send($ssh, $local_file, $remote_file, 0644));\n\n    // Verify the file was sent correctly\n    $sftp = ssh2_sftp($ssh);\n    $remote_content = file_get_contents(\"ssh2.sftp://{$sftp}/{$remote_file}\");\n    var_dump($remote_content === $test_content);\n\n    // Clean up\n    unlink($local_file);\n    ssh2_sftp_unlink($sftp, $remote_file);\n});\n?>\n--EXPECTF--\nbool(true)\nbool(true)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_ssh2/ssh2_send_eof.phpt",
    "content": "--TEST--\nssh2_send_eof() - Tests closing standard input\n--SKIPIF--\n<?php require_once 'ssh2_skip.inc';\nssh2t_needs_auth(); ?>\n--FILE--\n<?php require_once 'ssh2_test.inc';\nCo\\run(function () {\n    $ssh = ssh2_connect(TEST_SSH2_HOSTNAME, TEST_SSH2_PORT);\n    var_dump(ssh2t_auth($ssh));\n\n    $cmd = ssh2_exec($ssh, \"cat\\n\");\n\n    var_dump($cmd);\n\n    stream_set_blocking($cmd, true);\n\n    $content = 'foo';\n\n    fwrite($cmd, $content);\n    fflush($cmd);\n    ssh2_send_eof($cmd);\n\n    $response = stream_get_contents($cmd);\n    var_dump($response === $content);\n    echo $response . PHP_EOL;\n});\n?>\n--EXPECTF--\nbool(true)\nresource(%d) of type (stream)\nbool(true)\nfoo\n\n"
  },
  {
    "path": "tests/swoole_ssh2/ssh2_sftp_001.phpt",
    "content": "--TEST--\nssh2_sftp - SFTP tests\n--SKIPIF--\n<?php\nrequire_once 'ssh2_skip.inc';\nssh2t_needs_auth();\nssh2t_writes_remote();\n?>\n--FILE--\n<?php\nrequire_once 'ssh2_test.inc';\nCo\\run(function () {\n    $ssh = ssh2_connect(TEST_SSH2_HOSTNAME, TEST_SSH2_PORT);\n    ssh2t_auth($ssh);\n    $sftp = ssh2_sftp($ssh);\n\n    $filename = ssh2t_tempnam();\n    $linkname = ssh2t_tempnam();\n\n    var_dump(ssh2_sftp_mkdir($sftp, $filename, 0644, true));\n    var_dump(ssh2_sftp_symlink($sftp, $filename, $linkname));\n    var_dump(ssh2_sftp_readlink($sftp, $linkname) == $filename);\n    $stat = ssh2_sftp_stat($sftp, $filename);\n    var_dump(ssh2_sftp_rmdir($sftp, $filename));\n    var_dump(ssh2_sftp_unlink($sftp, $linkname));\n    var_dump(($stat['mode'] & 040000) == 040000); // is_dir()\n});\n?>\n--EXPECT--\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\n"
  },
  {
    "path": "tests/swoole_ssh2/ssh2_sftp_002.phpt",
    "content": "--TEST--\nssh2_sftp - SFTP tests\n--SKIPIF--\n<?php\n  require_once 'ssh2_skip.inc';\nssh2t_needs_auth();\nssh2t_writes_remote();\n?>\n--FILE--\n<?php require_once 'ssh2_test.inc';\nCo\\run(function () {\n    $ssh = ssh2_connect(TEST_SSH2_HOSTNAME, TEST_SSH2_PORT);\n    ssh2t_auth($ssh);\n    $sftp = ssh2_sftp($ssh);\n\n    $filename = ssh2t_tempnam();\n\n    $fp = fopen(\"ssh2.sftp://{$sftp}/{$filename}\", 'w');\n    fwrite($fp, \"Hello World\\n\");\n    fwrite($fp, \"Goodbye Planet\\n\");\n    fclose($fp);\n\n    readfile(\"ssh2.sftp://{$sftp}/{$filename}\");\n\n    var_dump(ssh2_sftp_unlink($sftp, $filename));\n});\n?>\n--EXPECT--\nHello World\nGoodbye Planet\nbool(true)\n"
  },
  {
    "path": "tests/swoole_ssh2/ssh2_shell.phpt",
    "content": "--TEST--\nssh2_shell_test() - Tests opening a shell\n--SKIPIF--\n<?php require_once 'ssh2_skip.inc';\nssh2t_needs_auth(); ?>\n--FILE--\n<?php require_once 'ssh2_test.inc';\n\nuse Swoole\\Timer;\nCo\\run(function () {\n    $ssh = ssh2_connect(TEST_SSH2_HOSTNAME, TEST_SSH2_PORT);\n    var_dump(ssh2t_auth($ssh));\n    $shell = ssh2_shell($ssh);\n    var_dump($shell);\n    $greet = fread($shell, 8192);\n    sleep(1);\n    fwrite($shell, 'echo \"foo bar\"' . PHP_EOL);\n    $cid = Co::getCid();\n    Timer::after(1000, function () use ($cid) {\n        Co::cancel($cid, true);\n    });\n    try {\n        while ($line = fgets($shell)) {\n            echo $line;\n        }\n    } catch (Swoole\\Coroutine\\CanceledException $e) {\n        echo \"DONE\\n\";\n    }\n});\n?>\n--EXPECTF--\nbool(true)\nresource(%d) of type (stream)\n%a\n%a\n"
  },
  {
    "path": "tests/swoole_ssh2/ssh2_skip.inc",
    "content": "<?php\n/**\n * This file is part of Swoole.\n *\n * @link     https://www.swoole.com\n * @contact  team@swoole.com\n * @license  https://github.com/swoole/library/blob/master/LICENSE\n */\n\ndeclare(strict_types=1);\nrequire_once 'ssh2_test.inc';\n\nif (!defined('SSH2_POLLIN')) {\n    echo 'skip extension not loaded';\n}\nif (TEST_SSH2_HOSTNAME === false) {\n    echo 'skip TEST_SSH2_HOSTNAME not set';\n}\n\nfunction ssh2t_needs_auth()\n{\n    if (TEST_SSH2_AUTH == 'none') {\n        echo \"skip TEST_SSH2_AUTH == 'none'\";\n    }\n}\n\nfunction ssh2t_writes_remote()\n{\n    if (!TEST_SSH2_TEMPDIR) {\n        echo 'skip TEST_SSH2_TEMPDIR is empty';\n    }\n}\n"
  },
  {
    "path": "tests/swoole_ssh2/ssh2_stream_select.phpt",
    "content": "--TEST--\nssh2_stream_select() - Tests opening a shell and using stream_select\n--SKIPIF--\n<?php require_once('ssh2_skip.inc'); ssh2t_needs_auth(); ?>\n--FILE--\n<?php require_once('ssh2_test.inc');\nCo\\run(function () {\n    $ssh = ssh2_connect(TEST_SSH2_HOSTNAME, TEST_SSH2_PORT);\n    var_dump(ssh2t_auth($ssh));\n    $shell = ssh2_shell($ssh);\n    var_dump($shell);\n\n    $greet = fread($shell, 8192);\n\n    fwrite($shell, \"echo \\\"howdy\\\"\\n\");\n    sleep(1);\n\n    $read = [$shell];\n    $write = null;\n    $except = null;\n    $timeout = 5;\n    $start = time();\n    if (stream_select($read, $write, $except, $timeout) !== false && count($read) > 0) {\n        while($line = fgets($shell)) {\n            echo $line;\n            if (str_ends_with($line, \"howdy\\r\\n\")) {\n                break;\n            }\n        }\n    }\n    $elapsed = time() - $start;\n    var_dump(($elapsed < $timeout));\n});\n?>\n--EXPECTF--\nbool(true)\nresource(%d) of type (stream)\n%a\n%a\n"
  },
  {
    "path": "tests/swoole_ssh2/ssh2_test.inc",
    "content": "<?php\n/**\n * This file is part of Swoole.\n *\n * @link     https://www.swoole.com\n * @contact  team@swoole.com\n * @license  https://github.com/swoole/library/blob/master/LICENSE\n */\n\nssh2t_defenv('TEST_SSH2_HOSTNAME', 'localhost');\nssh2t_defenv('TEST_SSH2_PORT', 22);\nssh2t_defenv('TEST_SSH2_USER', 'swoole_ssh2_test_user');\nssh2t_defenv('TEST_SSH2_PASS', 'swoole_ssh2_test_pass');\nssh2t_defenv('TEST_SSH2_TEMPDIR', '/tmp');\nssh2t_defenv('TEST_SSH2_AUTH', TEST_SSH2_PASS ? 'password' : 'none');\nssh2t_defenv('TEST_SSH2_PUB_KEY', __DIR__ . '/testkey_ed25519.pub');\nssh2t_defenv('TEST_SSH2_PRIV_KEY', __DIR__ . '/testkey_ed25519');\n\nfunction ssh2t_getenv($env, $def = false)\n{\n    $v = getenv($env);\n    return ($v === false) ? $def : $v;\n}\n\nfunction ssh2t_defenv($env, $def = false, $name = null)\n{\n    define(($name === null) ? $env : $name, ssh2t_getenv($env, $def));\n}\n\nfunction ssh2t_auth($ssh)\n{\n    if (!TEST_SSH2_USER) {\n        return false;\n    }\n\n    switch (TEST_SSH2_AUTH) {\n        case 'none':     return (ssh2_auth_none($ssh, TEST_SSH2_USER) === true) ? true : false;\n        case 'agent':    return ssh2_auth_agent($ssh, TEST_SSH2_USER);\n        case 'password': return ssh2_auth_password($ssh, TEST_SSH2_USER, TEST_SSH2_PASS);\n    }\n\n    return false;\n}\n\nfunction ssh2t_tempnam($escape = false)\n{\n    $fn = TEST_SSH2_TEMPDIR . '/php-ssh2-test-' . uniqid();\n    return $escape ? escapeshellarg($fn) : $fn;\n}\n"
  },
  {
    "path": "tests/swoole_ssh2/testkey_ed25519",
    "content": "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\nQyNTUxOQAAACB0GwDRjBT3NEK1LE48T+ElF6oFvHmJntQ5cZQHBVlDAgAAAJhU66/nVOuv\n5wAAAAtzc2gtZWQyNTUxOQAAACB0GwDRjBT3NEK1LE48T+ElF6oFvHmJntQ5cZQHBVlDAg\nAAAEC9tmUV4e++pQWWQm3ffFvGZZYPiZkD+UqrZYfghuLAeXQbANGMFPc0QrUsTjxP4SUX\nqgW8eYme1DlxlAcFWUMCAAAAEHJhbmdvQHN3b29sZS5jb20BAgMEBQ==\n-----END OPENSSH PRIVATE KEY-----\n"
  },
  {
    "path": "tests/swoole_ssh2/testkey_ed25519.pub",
    "content": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHQbANGMFPc0QrUsTjxP4SUXqgW8eYme1DlxlAcFWUMC rango@swoole.com\n"
  },
  {
    "path": "tests/swoole_stdext/array_method/0.phpt",
    "content": "--TEST--\nswoole_stdext/array_method: 0\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$array = typed_array('<int>');\n$array[0] = 1;\n$array[1] = 2;\n$array[2] = 3;\n$array[3] = 999;\nAssert::false($array->isEmpty());\nAssert::eq($array->count(), count($array));\nAssert::eq($array->slice(0, 2), [1, 2]);\nAssert::true($array->contains(999));\nAssert::eq($array->search(999), 3);\nAssert::true([]->isEmpty());\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_stdext/array_method/1.phpt",
    "content": "--TEST--\nswoole_stdext/array_method: 1\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$array = typed_array('<string>', [\"lemon\", \"orange\", \"banana\", \"apple\"]);\n$sorted_array = array(\"apple\", \"banana\", \"lemon\", \"orange\",);\n\n$ref = &$array;\n$ref->sort(SORT_NATURAL | SORT_FLAG_CASE);\n\nAssert::same($array, $sorted_array);\nAssert::same($ref, $sorted_array);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_stdext/array_method/2.phpt",
    "content": "--TEST--\nswoole_stdext/array_method: 2\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$array = array(\"orange\", \"banana\", \"apple\", \"raspberry\");\n\n$stack = $array;\nAssert::eq($stack->count(), 4);\n$ref = &$stack;\n$fruit = $ref->shift();\nAssert::eq($fruit, \"orange\");\nAssert::eq($stack->count(), 3);\nAssert::eq($array->count(), 4);\n\n$ref->unshift(\"mango\");\nAssert::eq($stack->count(), 4);\n\n$stack2 = array(\"orange\", \"banana\", \"apple\", \"raspberry\");\nAssert::eq($stack2->count(), 4);\n$stack2->shift();\n?>\n--EXPECTF--\nWarning: array_shift(): Argument #1 ($array) must be passed by reference, value given in %s on line %d\n"
  },
  {
    "path": "tests/swoole_stdext/array_method/method.phpt",
    "content": "--TEST--\nswoole_stdext/array_method: all array methods test\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nif (PHP_VERSION_ID >= 80400) {\n    $array = [\n        'a' => 'dog',\n        'b' => 'cat',\n        'c' => 'cow',\n        'd' => 'duck',\n        'e' => 'goose',\n        'f' => 'elephant'\n    ];\n    Assert::eq(\n        $array->all(fn(string $value) => $value->length() > 12),\n        array_all($array, fn(string $value) => $value->length() > 12)\n    );\n\n    Assert::eq(\n        $array->any(fn(string $value) => $value->length() > 5),\n        array_any($array, fn(string $value) => $value->length() > 5)\n    );\n}\n\n$array = array(\"FirSt\" => 1, \"SecOnd\" => 4);\nAssert::eq($array->changeKeyCase(CASE_UPPER), array_change_key_case($array, CASE_UPPER));\n\n$array = array('a', 'b', 'c', 'd', 'e');\nAssert::eq($array->chunk(2), array_chunk($array, 2));\n\n$records = [\n    [\n        'id' => 2135,\n        'first_name' => 'John',\n        'last_name' => 'Doe',\n    ],\n    [\n        'id' => 3245,\n        'first_name' => 'Sally',\n        'last_name' => 'Smith',\n    ],\n    [\n        'id' => 5342,\n        'first_name' => 'Jane',\n        'last_name' => 'Jones',\n    ],\n    [\n        'id' => 5623,\n        'first_name' => 'Peter',\n        'last_name' => 'Doe',\n    ]\n];\nAssert::eq($records->column('id'), array_column($records, 'id'));\n\n$array = array(1, \"hello\", 1, \"world\", \"hello\");\nAssert::eq($array->countValues(), array_count_values($array));\n\n$array1 = array(\"a\" => \"green\", \"red\", \"blue\", \"red\");\n$array2 = array(\"b\" => \"green\", \"yellow\", \"red\");\nAssert::eq($array1->diff($array2), array_diff($array1, $array2));\n\n$array1 = array(\"a\" => \"green\", \"b\" => \"brown\", \"c\" => \"blue\", \"red\");\n$array2 = array(\"a\" => \"green\", \"yellow\", \"red\");\nAssert::eq($array1->diffAssoc($array2), array_diff_assoc($array1, $array2));\n\n$array1 = array('blue' => 1, 'red' => 2, 'green' => 3, 'purple' => 4);\n$array2 = array('green' => 5, 'yellow' => 7, 'cyan' => 8);\nAssert::eq($array1->diffKey($array2), array_diff_key($array1, $array2));\n\nfunction odd($var)\n{\n    return $var & 1;\n}\n$array = [6, 7, 8, 9, 10, 11, 12];\nAssert::eq($array->filter('odd'), array_filter($array, 'odd'));\n\nif (PHP_VERSION_ID >= 80400) {\n    $array = [\n        'a' => 'dog',\n        'b' => 'cat',\n        'c' => 'cow',\n        'd' => 'duck',\n        'e' => 'goose',\n        'f' => 'elephant'\n    ];\n    function compare(string $value) {\n        return strlen($value) > 4;\n    }\n    Assert::eq($array->find('compare'), array_find($array, 'compare'));\n}\n\n$input = array(\"oranges\", \"apples\", \"pears\");\nAssert::eq($input->flip(), array_flip($input));\n\n$array1 = array(\"a\" => \"green\", \"red\", \"blue\");\n$array2 = array(\"b\" => \"green\", \"yellow\", \"red\");\nAssert::eq($array1->intersect($array2), array_intersect($array1, $array2));\nAssert::eq($array1->intersectAssoc($array2), array_intersect_assoc($array1, $array2));\n\n$array = ['apple', 2, 3];\nAssert::eq($array->isList(), array_is_list($array));\n\n$array = ['first' => 1, 'second' => 4];\nAssert::eq($array->keyExists('first'), array_key_exists('first', $array));\n\n$array = ['a' => 1, 'b' => 2, 'c' => 3];\nAssert::eq($array->keyFirst(), array_key_first($array));\nAssert::eq($array->keyLast(), array_key_last($array));\nAssert::eq($array->keys(), array_keys($array));\nAssert::eq($array->values(), array_values($array));\n\nfunction cube($n)\n{\n    return ($n * $n * $n);\n}\n\n$a = [1, 2, 3, 4, 5];\nAssert::eq($a->map('cube'), array_map('cube', $a));\n\n$array = array(12, 10, 9);\nAssert::eq($array->pad(5, 0), array_pad($array, 5, 0));\n\n$a = array(2, 4, 6, 8);\nAssert::eq($a->product(), array_product($a));\n\n$array = array(\"Neo\", \"Morpheus\", \"Trinity\", \"Cypher\", \"Tank\");\nAssert::notEq($array->rand(2), array_rand($array, 2));\n\nfunction sum($carry, $item)\n{\n    $carry += $item;\n    return $carry;\n}\n$array = array(1, 2, 3, 4, 5);\nAssert::eq($array->reduce('sum'), array_reduce($array, 'sum'));\n\n$base = array(\"orange\", \"banana\", \"apple\", \"raspberry\");\n$replacements = array(0 => \"pineapple\", 4 => \"cherry\");\n$replacements2 = array(0 => \"grape\");\nAssert::eq($base->replace($replacements, $replacements2), array_replace($base, $replacements, $replacements2));\n\n$array  = array(\"php\", 4.0, array(\"green\", \"red\"));\nAssert::eq($array->reverse()->reverse(), array_reverse(array_reverse($array)));\n\n$array = array(0 => 'blue', 1 => 'red', 2 => 'green', 3 => 'red');\nAssert::eq($array->search('green'), array_search('green', $array));\n\n$array = array(\"a\", \"b\", \"c\", \"d\", \"e\");\nAssert::eq($array->slice(-2, 1), array_slice($array, -2, 1));\n\n$a = array(2, 4, 6, 8);\nAssert::eq($a->sum(), array_sum($a));\n\n$array = [\"a\" => \"green\", \"red\", \"b\" => \"green\", \"blue\", \"red\"];\nAssert::eq($array->unique(), array_unique($array));\nAssert::eq($array->count(), count($array));\n\n$array = array(\"Mac\", \"NT\", \"Irix\", \"Linux\");\nAssert::eq($array->contains(\"Mac\"), in_array(\"Mac\", $array));\nAssert::eq($array->join(\",\"), implode(\",\", $array));\n\n$array = [];\nAssert::true($array->isEmpty());\n\n$array = typed_array('<string>', [\"lemon\", \"orange\", \"banana\", \"apple\"]);\nAssert::true($array->isTyped());\n\n$fruits1 = array(\"lemon\", \"orange\", \"banana\", \"apple\");\n$fruits2 = array(\"lemon\", \"orange\", \"banana\", \"apple\");\n$result = &$fruits1;\nsort($fruits2);\nAssert::eq($result->sort(), $fruits2);\nAssert::eq($fruits1, $fruits2);\n\n$stack = array(\"orange\", \"banana\", \"apple\", \"raspberry\");\n$result = &$stack;\nAssert::eq($result->pop(), 'raspberry');\nAssert::eq(array_pop($stack), 'apple');\n\n$array = array(\"red\",\"green\");\n$result = &$array;\n$result->push(\"blue\");\nAssert::eq($array, [\"red\",\"green\", \"blue\"]);\narray_push($array, \"yellow\");\nAssert::eq($array, [\"red\",\"green\", \"blue\", \"yellow\"]);\n\n$stack = array(\"orange\", \"banana\", \"apple\", \"raspberry\");\n$result = &$stack;\nAssert::eq($result->shift(), 'orange');\nAssert::eq(array_shift($stack), 'banana');\n\n$queue = [\"orange\", \"banana\"];\n$result = &$queue;\n$result->unshift(\"orange\");\nAssert::eq($queue, [\"orange\", \"orange\", \"banana\"]);\narray_unshift($queue, \"orange\");\nAssert::eq($queue, [\"orange\", \"orange\", \"orange\", \"banana\"]);\n\n$array1 = array(\"red\", \"green\", \"blue\", \"yellow\");\n$array2 = array(\"red\", \"green\", \"blue\", \"yellow\");\n\n$result = &$array1;\nAssert::eq($result->splice(2), array_splice($array2, 2));\nAssert::eq($array1, $array2);\n\n$find = array(\"Hello\",\"world\");\n$replace = array(\"B\");\n$arr = array(\"Hello\",\"world\",\"!\");\nAssert::eq($arr->replaceStr($find, $replace), str_replace($find, $replace, $arr));\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_stdext/stream_method/0.phpt",
    "content": "--TEST--\nswoole_stdext/stream_method: 0\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$filepath = \"/tmp/test.txt\";\n$fp = fopen($filepath, \"w+\");\n$rdata = random_bytes(1024);\nAssert::greaterThan($fp->write($rdata->base64Encode()), $rdata->length());\n$fp->seek(0);\nAssert::eq($rdata, $fp->read(8192)->base64Decode());\nAssert::eq($fp->stat(), fstat($fp));\nAssert::true($fp->sync());\nAssert::true($fp->dataSync());\n$fp->seek(100);\nAssert::true($fp->tell() == 100);\nAssert::true($fp->lock(LOCK_SH) == true);\nAssert::true($fp->lock(LOCK_UN) == true);\nAssert::true($fp->eof() == feof($fp));\n\n$fp->seek(0);\n$char = $fp->getChar();\n$fp->seek(0);\nAssert::eq($char, fgetc($fp));\n$fp->seek(0);\n$line = $fp->getLine();\n$fp->seek(0);\nAssert::eq($line, fgets($fp));\nAssert::true($fp->truncate(1000));\n\nAssert::true($fp->close());\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_stdext/string_method/0.phpt",
    "content": "--TEST--\nswoole_stdext/string_method: 0\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$string = 'hello world, this is a test string';\nAssert::false($string->isEmpty());\nAssert::eq($string->length(), strlen($string));\nAssert::eq($string->substr(0, 5), 'hello');\nAssert::eq($string->contains('world'), true);\nAssert::eq($string->indexOf('test'), strpos($string, 'test'));\nAssert::eq($string->split(' '), explode(' ', $string));\nAssert::true(''->isEmpty());\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_stdext/string_method/1.phpt",
    "content": "--TEST--\nswoole_stdext/string_method: 1\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$str = \"first=value&arr[]=foo+bar&arr[]=baz\";\n\n$output = $str->parseStr();\nAssert::eq($output['first'], 'value');\nAssert::eq($output['arr'][0], 'foo bar');\nAssert::eq($output['arr'][1], 'baz');\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_stdext/string_method/json.phpt",
    "content": "--TEST--\nswoole_stdext/string_method: json\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$array = ['a' => random_bytes(128)->base64Encode(), 'b' => random_int(1, PHP_INT_MAX), 'c' => php_uname()];\n\n$str = $array->jsonEncode();\nAssert::notEmpty($str);\nAssert::eq($str->jsonDecode(), $array);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_stdext/string_method/marshal.phpt",
    "content": "--TEST--\nswoole_stdext/string_method: marshal\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$array = ['a' => random_bytes(128)->base64Encode(), 'b' => random_int(1, PHP_INT_MAX), 'c' => php_uname()];\n\n$str = $array->marshal();\nAssert::notEmpty($str);\nAssert::eq($str->unmarshal(), $array);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_stdext/string_method/match.phpt",
    "content": "--TEST--\nswoole_stdext/string_method: match\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$str = 'foobarbaz';\n$regex1 = '/(foo)(bar)(baz)/';\n$matches = $str->match($regex1, PREG_OFFSET_CAPTURE);\n\npreg_match($regex1, $str, $matches2, PREG_OFFSET_CAPTURE);\nAssert::eq($matches, $matches2);\n\n\n$html = \"<b>bold text</b><a href=howdy.html>click me</a>\";\n$regex2 = \"/(<([\\w]+)[^>]*>)(.*?)(<\\/\\\\2>)/\";\npreg_match_all($regex2, $html, $matches2, PREG_SET_ORDER);\n\n$matches = $html->matchAll($regex2, PREG_SET_ORDER);\nAssert::eq($matches, $matches2);\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_stdext/string_method/mbstring.phpt",
    "content": "--TEST--\nswoole_stdext/string_method: all mbstring methods test\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nif (!extension_loaded(\"mbstring\")) exit(\"skip mbstring extension not loaded\");\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n$text = 'bbbb大声向宇宙呐喊bbbb';\nAssert::eq($text->mbUpperFirst(), mb_ucfirst($text));\n\n$text = 'Bbbb大声向宇宙呐喊bbbb';\nAssert::eq($text->mbLowerFirst(), mb_lcfirst($text));\n\n$text = \"\\t\\t大声向宇宙呐喊 :) ...  \";\nAssert::eq($text->mbTrim(), mb_trim($text));\nAssert::eq($text->mbLTrim(), mb_ltrim($text));\nAssert::eq($text->mbRTrim(), mb_rtrim($text));\n\n$text = '大声大声大声向宇宙呐喊';\nAssert::eq($text->mbSubstrCount('大声'), mb_substr_count($text, '大声'));\n\n$text = \"大声大声大声向宇宙呐喊\";\nAssert::eq($text->mbSubstr(0, -1), mb_substr($text, 0, -1));\n\n$text = 'b大声大声大声向宇宙呐喊';\nAssert::eq($text->mbUpper(), mb_strtoupper($text));\n\n$text = 'BBBBBB大声大声大声向宇宙呐喊';\nAssert::eq($text->mbLower(), mb_strtolower($text));\n\n$email  = 'name我哦知道@example.com';\nAssert::eq($email->mbFind('哦'), mb_strstr($email, '哦'));\nAssert::eq($email->mbIFind('道'), mb_stristr($email, '道'));\n\n$mystring1 = '当地特产';\n$mystring2 = '现在只是谈谈';\nAssert::true($mystring1->mbIndexOf('地') === mb_strpos($mystring1, '地'));\nAssert::true($mystring2->mbIIndexOf('A') === mb_stripos($mystring2, 'a'));\n\n$mystring = '啦啦啦啦啦啦啦';\nAssert::true($mystring->mbLastIndexOf('好') === false);\nAssert::true(mb_strrpos($mystring, '好') === false);\nAssert::eq($mystring->mbILastIndexOf('啦'), mb_strripos($mystring, '啦'));\nAssert::eq($mystring->mbLastCharIndexOf('啦'), mb_strrchr($mystring, '啦'));\nAssert::eq($mystring->mbILastCharIndex('啦'), mb_strrichr($mystring, '啦'));\n\n$text = '啦啦啦啦啦啦啦';\nAssert::eq($text->mbLength(), mb_strlen($text));\nAssert::eq($text->mbCut(0, 2), mb_strcut($text, 0, 2));\nAssert::eq($text->mbDetectEncoding(), mb_detect_encoding($text));\nAssert::eq($text->mbConvertEncoding('GBK'), mb_convert_encoding($text, 'GBK'));\n\n$text = 'bbbbba啦啦啦啦啦啦啦';\nAssert::eq(\n    $text->mbConvertCase(MB_CASE_UPPER)->mbConvertCase(MB_CASE_LOWER),\n    mb_convert_case(mb_convert_case($text, MB_CASE_UPPER), MB_CASE_LOWER)\n    );\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_stdext/string_method/method.phpt",
    "content": "--TEST--\nswoole_stdext/string_method: all string methods test\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$text = 'aaaaaa';\nAssert::eq($text->length(), strlen($text));\n\n$text = '';\nAssert::eq($text->isEmpty(), true);\n\n$text = 'A';\nAssert::eq($text->lower(), strtolower($text));\nAssert::eq($text->lowerFirst(), lcfirst($text));\n\n$text = 'b';\nAssert::eq($text->upper(), strtoupper($text));\nAssert::eq($text->upperFirst(), ucfirst($text));\n\n$text = 'hello world!';\nAssert::eq($text->upperWords(), ucwords($text));\n\n$text = \"PHP isThirty\\nYears Old!\\tYay to the Elephant!\\n\";\n$characters = \"\\0..\\37!@\\177..\\377\";\nAssert::eq($text->addCSlashes($characters), addCSlashes($text, $characters));\n\n$text = \"O'Reilly?\";\nAssert::eq($text->addSlashes(), addslashes($text));\n\n$text = 'This is quite a long string, which will get broken up because the line is going to be too long after base64 encoding it.';\nAssert::eq($text->base64Encode()->chunkSplit(), chunk_split(base64_encode($text)));\n\n$text = \"Two Ts and one F.\";\nAssert::eq($text->countChars(1), count_chars($text, 1));\n\n$text = \"I'll \\\"walk\\\" the <b>dog</b> now\";\nAssert::eq($text->htmlEntityEncode()->htmlEntityDecode(), html_entity_decode(htmlentities($text)));\n\n$text = \"<a href='test'>Test</a>\";\nAssert::eq($text->htmlSpecialCharsEncode(ENT_QUOTES)->htmlSpecialCharsDecode(), htmlspecialchars_decode(htmlspecialchars($text, ENT_QUOTES)));\n\n$text = \"\\t\\tThese are a few words :) ...  \";\nAssert::eq($text->trim(), trim($text));\nAssert::eq($text->lTrim(), ltrim($text));\nAssert::eq($text->rTrim(), rtrim($text));\n\n$text = \"first=value&arr[]=foo+bar&arr[]=baz\";\nparse_str($text, $result);\nAssert::eq($text->parseStr(), $result);\n\n$url = 'http://username:password@hostname:9090/path?arg=value#anchor';\nAssert::eq($url->parseUrl(), parse_url($url));\n\n$text = 'The lazy fox jumped over the fence';\nAssert::eq($text->contains('lazy'), str_contains($text, 'lazy'));\n\nif (PHP_VERSION_ID >= 80300) {\n    $text = 'ABC';\n    Assert::eq($text->incr(), str_increment($text));\n    Assert::eq($text->decr(), str_decrement($text));\n}\n\n$text = \"<body text=%BODY%>\";\nAssert::eq($text->replace(\"%BODY%\", \"black\"), str_replace(\"%BODY%\", \"black\", $text));\nAssert::eq($text->iReplace(\"%body%\", \"black\"), str_ireplace(\"%body%\", \"black\", $text));\n\n$text = \"Alien\";\nAssert::eq($text->pad(10), str_pad($text, 10));\n\n$text = \"-=\";\nAssert::eq($text->repeat(10), str_repeat($text, 10));\n\n$text = 'abcdef';\nAssert::notEq($text->shuffle(), str_shuffle($text));\n\n$text = \"piece1 piece2 piece3 piece4 piece5 piece6\";\nAssert::eq($text->split(' '),  explode(' ', $text));\n\n$text = 'The lazy fox jumped over the fence';\nAssert::eq($text->startsWith('The'),  str_starts_with($text, 'The'));\nAssert::eq($text->endsWith('fence'),  str_ends_with($text, 'fence'));\n\n$text = \"Hello fri3nd, you're\n               looking          good today!\";\nAssert::eq($text->wordCount(0),  str_word_count($text, 0));\nAssert::eq($text->wordCount(1),  str_word_count($text, 1));\nAssert::eq($text->wordCount(2),  str_word_count($text, 2));\n\n$var1 = \"Hello\";\n$var2 = \"hello\";\nAssert::eq($var1->iCompare($var2), strcasecmp($var1, $var2));\nAssert::eq($var1->compare($var2), strcmp($var1, $var2));\n\n$email  = 'name@example.com';\nAssert::eq($email->find('@'), strstr($email, '@'));\nAssert::eq($email->iFind('N'), stristr($email, 'N'));\n\n$text = '<p>Test paragraph.</p><!-- Comment --> <a href=\"#fragment\">Other text</a>';\nAssert::eq($text->stripTags(), strip_tags($text));\n\n$text = 'I\\'d have a coffee.\\nNot a problem.';\nAssert::eq($text->stripCSlashes(), stripcslashes($text));\n\n$str = \"Is your name O\\'reilly?\";\nAssert::eq($str->stripSlashes(), stripslashes($str));\n\n$mystring1 = 'xyz';\n$mystring2 = 'ABC';\nAssert::true($mystring1->iIndexOf('A') === stripos($mystring1, 'a'));\nAssert::true($mystring2->iIndexOf('A') === stripos($mystring2, 'a'));\nAssert::eq($mystring2->indexOf('a'), strpos($mystring2, 'a'));\n\n$mystring = 'Elephpant';\nAssert::true($mystring->lastIndexOf('b') === false);\nAssert::true(strrpos($mystring, 'b') === false);\nAssert::eq($mystring->iLastIndexOf('E'), strripos($mystring, 'E'));\nAssert::eq($mystring->lastCharIndexOf('E'), strrchr($mystring, 'E'));\n\n$text = \"abcdef\";\nAssert::eq($text->substr(0, -1), substr($text, 0, -1));\nAssert::eq($text->substrCompare(\"bc\", 1, 2), substr_compare($text, \"bc\", 1, 2));\n\n$text = 'This is a test';\nAssert::eq($text->substrCount('is'), substr_count($text, 'is'));\n\n$var = 'ABCDEFGH:/MNRPQR/';\nAssert::eq($var->substrReplace('bob', 0), substr_replace($var, 'bob', 0));\n\n$var = 'abcdefghijklmn';\nAssert::eq($var->reverse(), strrev($var));\nAssert::eq($var->md5(), md5($var));\nAssert::eq($var->sha1(), sha1($var));\nAssert::eq($var->crc32(), crc32($var));\nAssert::eq($var->hash('sha256'), hash('sha256', $var));\n\n$str = 'This is an encoded string';\nAssert::eq($str->base64Encode()->base64Decode(), base64_decode(base64_encode($str)));\n\n$text = 'Data123!@-_ +';\nAssert::eq($text->urlEncode()->urlDecode(), urldecode(urlencode($text)));\n\n$text = 'foo @+%/';\nAssert::eq($text->rawUrlEncode()->rawUrlDecode(), rawurldecode(rawurlencode($text)));\n\n$pattern = \"/php/i\";\n$text = \"PHP is the web scripting language of choice.\";\n$result1 = $text->match(\"/php/i\");\npreg_match(\"/php/i\", $text, $result2);\nAssert::eq($result1, $result2);\n\n$pattern = \"/\\(?  (\\d{3})?  \\)?  (?(1)  [\\-\\s] ) \\d{3}-\\d{4}/x\";\n$subject = \"Call 555-1212 or 1-800-555-1212\";\n$result1 = $subject->matchAll($pattern);\npreg_match_all($pattern, $subject, $result2);\nAssert::eq($result1, $result2);\n\n$text = '112';\nAssert::eq($text->isNumeric(), is_numeric($text));\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/0.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: 0\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$arr = typed_array(' <int>');\n\ntry {\n    $array = typed_array('<int');\n} catch (Throwable $e) {\n    Assert::true($e->getMessage()->contains(\"must start with '<' and end with '>'\"));\n    echo \"DONE\\n\";\n}\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/1.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: 1\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$array = typed_array('<int>');\n$array[] = 123;\n\ntry {\n    $array[] = '456';\n} catch (TypeError $e) {\n    Assert::true($e->getMessage()->contains('Array value type mismatch'));\n}\n\n$array = typed_array('<bool>');\n$array[] = true;\n$array[] = false;\ntry {\n    $array[] = '456';\n} catch (TypeError $e) {\n    Assert::true($e->getMessage()->contains('Array value type mismatch'));\n}\n\n$array = typed_array('<string>');\n$array[] = '456';\ntry {\n    $array[] = true;\n} catch (TypeError $e) {\n    Assert::true($e->getMessage()->contains('Array value type mismatch'));\n}\n\n$array = typed_array('<float>');\n$array[] = 4556.56;\ntry {\n    $array[] = 456;\n} catch (TypeError $e) {\n    Assert::true($e->getMessage()->contains('Array value type mismatch'));\n}\n\n$array = typed_array('<stdClass>');\n$array[] = new stdClass();\ntry {\n    $array[] = new ArrayObject();\n} catch (TypeError $e) {\n    Assert::true($e->getMessage()->contains('Array value type mismatch'));\n}\n\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/10.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: 10\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$bytes = random_bytes(128);\n$array = typed_array('<int, string>');\n$array[999] = $bytes;\n$copy = typed_array('<int, string>', $array);\nAssert::eq($copy[999], $bytes);\n\ntry {\n    $copy2 = typed_array('<string, string>', $array);\n} catch (Throwable $e) {\n    Assert::true($e->getMessage()->contains('not match the initial values'));\n    echo \"DONE\\n\";\n}\n\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/11.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: 11\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$num = random_int(1, 1000);\n$array = typed_array('<int>', [1, $num, 3]);\nforeach($array as &$v) {\n    var_dump($v);\n    echo \"each\\n\";\n}\necho \"end\\n\";\n?>\n--EXPECTF--\nFatal error: Uncaught Error: The type array do not support using references for element value during iteration in %s:%d\nStack trace:\n#0 {main}\n  thrown in %s on line %d\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/2.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: 2\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$array = typed_array('<int, <string, string>>');\n$array[999] = typed_array('<string, string>');\n\ntry {\n    $array[888] = typed_array('<int, string>');\n} catch (TypeError $e) {\n    Assert::true($e->getMessage()->contains('Array value type mismatch'));\n    echo \"DONE\\n\";\n}\n\ntry {\n    $array[777] = [];\n} catch (TypeError $e) {\n    Assert::true($e->getMessage()->contains('Array value type mismatch'));\n    echo \"DONE\\n\";\n}\n\n?>\n--EXPECT--\nDONE\nDONE\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/3.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: 3\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$map = typed_array('<string, int>');\n$map['a'] = 1;\n$map['b'] = 2;\n$map['c'] = 3;\nunset($map['b']);\n\nAssert::true(isset($map['a']));\nAssert::true(isset($map['c']));\nAssert::false(isset($map['b']));\n\n$array = typed_array('<int>');\n$array[] = 3;\n$array[] = 5;\n$array[] = 7;\n\ntry {\n    unset($array[0]);\n} catch (Throwable $e) {\n    Assert::true($e->getMessage()->contains('not support'));\n    echo \"DONE\\n\";\n}\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/4.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: 4\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$array = typed_array('<int, string>');\n$array[999] = 'test';\n\ntry {\n    $array['hello'] = 'world';\n} catch (TypeError $e) {\n    Assert::true($e->getMessage()->contains('Array key type mismatch'));\n    echo \"DONE\\n\";\n}\n\n$key = str_repeat('a', 1000);\ntry {\n    $array[$key] = 'world';\n} catch (TypeError $e) {\n    Assert::true($e->getMessage()->contains('Array key type mismatch'));\n    echo \"DONE\\n\";\n}\n?>\n--EXPECTF--\nDONE\nDONE\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/5.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: 5\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$array = typed_array('<int>');\n$array[] = 3;\n$array[] = 5;\n$array[] = 7;\n$array[3] = random_int(1, 100);\n\ntry {\n    $array[5] = 999;\n} catch (Throwable $e) {\n    Assert::true($e->getMessage()->contains('out of the permitted range'));\n    echo \"DONE\\n\";\n}\n\ntry {\n    $array[\"hello\"] = 999;\n} catch (Throwable $e) {\n    Assert::true($e->getMessage()->contains('must be undef or int'));\n    echo \"DONE\\n\";\n}\n\n?>\n--EXPECT--\nDONE\nDONE\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/6.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: 6\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$num = random_int(1, 1000);\n$array = typed_array('<int>', [1, $num, 3]);\nAssert::eq($array->count(), 3);\nAssert::eq($array[1], $num);\n\ntry {\n    $array = typed_array('<int>', [1, \"hello\", 3]);\n} catch (Throwable $e) {\n    Assert::true($e->getMessage()->contains('Array value type mismatch'));\n    echo \"DONE\\n\";\n}\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/7.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: 7\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$num = random_int(1, 1000);\n$array = typed_array('<int>', [1, $num, 3]);\n\n$copy = [];\nforeach($array as $k => $v) {\n    $copy[] = $v;\n    $array[$k] = $v * 2;\n}\n\nAssert::eq($copy[1], $num);\nAssert::eq($array[1], $num * 2);\nAssert::true($array->isTyped());\n\ntry {\n    $array[] = 'hello';\n} catch (Throwable $e) {\n    Assert::true($e->getMessage()->contains('Array value type mismatch'));\n    echo \"DONE\\n\";\n}\n\nforeach($copy as &$v) {\n    $v = 0;\n}\nAssert::eq($copy->sum(), 0);\n\ntry {\n    foreach ($array as &$v) {\n        var_dump($v);\n    }\n} catch (Throwable $e) {\n    Assert::true($e->getMessage()->contains('not support using references for element value'));\n    echo \"DONE\\n\";\n}\n?>\n--EXPECT--\nDONE\nDONE\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/8.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: 8\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$arr = typed_array('<int>');\n\n$arr[] = 1;\n$arr[0] += 10;\nAssert::eq($arr[0], 11);\n\ntry {\n    $arr[0] .= \"hello world\";\n} catch (TypeError $e) {\n    Assert::true($e->getMessage()->contains('Array value type mismatch'));\n    echo \"DONE\\n\";\n}\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/9.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: 9\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$array = typed_array('<int, string>');\n$array[999] = random_bytes(128);\n\ntry {\n    $a = typed_array('<int, string>');\n    $array[888] = $a;\n} catch (TypeError $e) {\n    Assert::true($e->getMessage()->contains('Array value type mismatch'));\n    echo \"DONE\\n\";\n}\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/array_pop.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: array_pop\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$arr = typed_array('<int>', [1, 3, 5, 7]);\n$v = array_pop($arr);\nAssert::eq($v, 7);\nAssert::eq($arr[2], 5);\nAssert::eq($arr->count(), 3);\nAssert::true($arr->isList());\n\ntry {\n    array_push($arr, 9, 'hello world', true);\n} catch (TypeError $e) {\n    Assert::true($e->getMessage()->contains('Array value type mismatch'));\n    echo \"DONE\\n\";\n}\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/array_push.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: 9\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$arr = typed_array('<int>', [1, 3, 5]);\narray_push($arr, 7);\nAssert::eq($arr[3], 7);\nAssert::true($arr->isList());\n\ntry {\n    array_push($arr, 9, 'hello world', true);\n} catch (TypeError $e) {\n    Assert::true($e->getMessage()->contains('Array value type mismatch'));\n    echo \"DONE\\n\";\n}\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/array_shift.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: array_shift\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$arr = typed_array('<int>', [1, 3, 5, 7]);\n$v = array_shift($arr);\nAssert::eq($v, 1);\nAssert::eq($arr[1], 5);\nAssert::eq($arr->count(), 3);\nAssert::true($arr->isList());\n\ntry {\n    array_unshift($arr, 9, 'hello world', true);\n} catch (TypeError $e) {\n    Assert::true($e->getMessage()->contains('Array value type mismatch'));\n    echo \"DONE\\n\";\n}\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/array_splice.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: array_splice\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$arr = typed_array('<string>', array(\"red\", \"green\", \"blue\", \"yellow\"));\narray_splice($arr, 2);\nAssert::eq($arr->count(), 2);\narray_push($arr, \"purple\", \"orange\");\nAssert::eq($arr->count(), 4);\nAssert::true($arr->isList());\n\narray_splice($arr, 1, count($arr), \"black\");\nAssert::true($arr->contains(\"black\"));\nAssert::eq($arr->count(), 2);\nAssert::true($arr->isList());\nAssert::true($arr->isTyped());\n\narray_splice($arr, -1, 1, array(\"gray\", \"maroon\"));\nAssert::eq($arr->count(), 3);\nAssert::true($arr->contains(\"maroon\"));\nAssert::true($arr->isTyped());\nAssert::true($arr->isList());\n\ntry {\n    array_splice($arr, -1, 1, array(9999, false));\n} catch (TypeError $e) {\n    Assert::true($e->getMessage()->contains('Array value type mismatch'));\n    echo \"DONE\\n\";\n}\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/array_unshift.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: 10\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$arr = typed_array('<int>', [1, 3, 5]);\narray_unshift($arr, 999);\nAssert::eq($arr[0], 999);\nAssert::true($arr->isList());\n\ntry {\n    array_unshift($arr, 9, 'hello world', true);\n} catch (TypeError $e) {\n    Assert::true($e->getMessage()->contains('Array value type mismatch'));\n    echo \"DONE\\n\";\n}\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/resource.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: resource\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$map = typed_array('<string, resource>');\n$map['a'] = fopen(__FILE__, 'r');\nAssert::eq(get_resource_type($map['a']), 'stream');\n\ntry {\n    $map['b'] = 1;\n} catch (Throwable $e) {\n    Assert::true($e->getMessage()->contains('Array value type mismatch'));\n    echo \"DONE\\n\";\n}\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_stdext/typed_array/sort.phpt",
    "content": "--TEST--\nswoole_stdext/typed_array: sort\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\n$arr = typed_array('<int>', [1243, 3434, 5453, 4532, 2, 3454, 5233, 655, 234, 6, 2356, 4554]);\nAssert::true($arr->isList());\nAssert::true($arr->isTyped());\n\nsort($arr);\nAssert::true($arr->isTyped());\n\nshuffle($arr);\nAssert::true($arr->isTyped());\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_table/big_size.phpt",
    "content": "--TEST--\nswoole_table: big size\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$table = new \\Swoole\\Table(1);\n$table->column('string', \\Swoole\\Table::TYPE_STRING, $string_size = mt_rand(2, 6) * 0xffff);\n$table->column('int', \\Swoole\\Table::TYPE_INT, 8);\n$table->create();\n$data = [\n    'string' => str_repeat('S', $string_size),\n    'int' => PHP_INT_MAX\n];\n$table->set('test', $data);\nAssert::same($table->get('test'), $data);\nvar_dump($table->get('test'));\n?>\n--EXPECTF--\narray(2) {\n  [\"string\"]=>\n  string(%d) \"%s\"\n  [\"int\"]=>\n  int(%d)\n}\n"
  },
  {
    "path": "tests/swoole_table/bug_2263.phpt",
    "content": "--TEST--\nswoole_table: bug_2263\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Table;\n\n$table = new Table(1024);\n$table->column('data', Table::TYPE_STRING, 1);\n$table->create();\n\n$table->set(\"1234567890\", ['data' => '1']);\n$table->set(\"44984\", ['data' => '2']);\n\n$table->del(\"1234567890\");\n\nforeach($table as $ip => $row) {\n\techo $ip.\"\\n\";\n}\n\n?>\n--EXPECT--\n44984\n"
  },
  {
    "path": "tests/swoole_table/bug_2290.phpt",
    "content": "--TEST--\nswoole_table: bug_2290\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$table = new \\Swoole\\Table(1024);\n$table->column('h', \\Swoole\\Table::TYPE_STRING, 128);\n$table->column('b', \\Swoole\\Table::TYPE_STRING, 1024 * 512);\n$table->column('_e', \\Swoole\\Table::TYPE_INT);\n$table->create();\n\n$headers = ['Content-Type' => 'text/html; charset=utf-8'];\n$body = <<<EOS\n<!DOCTYPE html><html lang=\"en\"></html>\nEOS;\n\n$value = ['h' => json_encode($headers, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'b' => $body];\n$value['_e'] = time(); // Remove this line and the result is correct\n$table->set('test', $value);\n\necho $table->get('test', 'b');\n?>\n--EXPECT--\n<!DOCTYPE html><html lang=\"en\"></html>\n"
  },
  {
    "path": "tests/swoole_table/create_10k_object.phpt",
    "content": "--TEST--\nswoole_table: create 10,000 objects\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nfor ($i = 0; $i < 10000; $i++) {\n    $main = new Swoole\\Table(1);\n}\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_table/del.phpt",
    "content": "--TEST--\nswoole_table: clear all columns\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$table = new Swoole\\Table(1024, 0.25);\n$table->column('state', Swoole\\Table::TYPE_INT);\n$table->column('remainLen', Swoole\\Table::TYPE_INT);\n$table->column('data', Swoole\\Table::TYPE_STRING, 64);\n$table->create();\n\nfunction data($table) {\n    $table_key = 'table';\n    $table->incr($table_key, 'state');\n    $state = $table->get($table_key, 'state');\n    var_dump($state);\n    $data = $table->get($table_key, 'data');\n    $data .= 'abc';\n    $table->set($table_key, ['data' => $data]);\n    var_dump($table->get($table_key));\n\n    if ($state === 1) {\n        $table->incr($table_key, 'remainLen', 3);\n    } else {\n        $remainLen = $table->get($table_key, 'remainLen');\n        if ($remainLen === 3) {\n            $res = $table->del($table_key);\n            var_dump($res);\n            var_dump($table->get($table_key));\n        }\n    }\n}\ndata($table);\ndata($table);\ndata($table);\n?>\n--EXPECT--\nint(1)\narray(3) {\n  [\"state\"]=>\n  int(1)\n  [\"remainLen\"]=>\n  int(0)\n  [\"data\"]=>\n  string(3) \"abc\"\n}\nint(2)\narray(3) {\n  [\"state\"]=>\n  int(2)\n  [\"remainLen\"]=>\n  int(3)\n  [\"data\"]=>\n  string(6) \"abcabc\"\n}\nbool(true)\nbool(false)\nint(1)\narray(3) {\n  [\"state\"]=>\n  int(1)\n  [\"remainLen\"]=>\n  int(0)\n  [\"data\"]=>\n  string(3) \"abc\"\n}\n"
  },
  {
    "path": "tests/swoole_table/force_unlock.phpt",
    "content": "--TEST--\nswoole_table: force unlock\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process;\n\nini_set('memory_limit', '16M');\n\n$table = new \\Swoole\\Table(1);\n$table->column('string', \\Swoole\\Table::TYPE_STRING, 4 * 1024 * 1024);\n$table->column('int', \\Swoole\\Table::TYPE_INT, 8);\n$table->create();\n$str_size = 4 * 1024 * 1024;\n$str_value = random_bytes($str_size);\n$data = [\n    'string' => $str_value,\n    'int' => PHP_INT_MAX\n];\n$table->set('test', $data);\n\n$proc = new Process(function () use ($table) {\n    $str = str_repeat('A', 5 * 1024 * 1024);\n    // Fatal error: memory exhausted\n    $data = $table->get('test');\n    var_dump(strlen($data['string']));\n    var_dump(strlen($str));\n    var_dump(memory_get_usage());\n}, true, SOCK_STREAM);\n\n$proc->start();\n\n$exit_status = Process::wait();\nAssert::eq($exit_status['code'], 255);\nAssert::contains($proc->read(), 'Fatal error: Allowed memory');\n\n$data = $table->get('test');\nAssert::eq(strlen($data['string']), $str_size);\nAssert::eq($data['string'], $str_value);\necho \"Done\\n\";\n?>\n--EXPECTF--\n[%s]\tWARNING\tTableRow::lock(): lock process[%d] not exists, force unlock\nDone\n"
  },
  {
    "path": "tests/swoole_table/foreach.phpt",
    "content": "--TEST--\nswoole_table: iterator\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$table = new Swoole\\Table(65536);\n\n$table->column('id', Swoole\\Table::TYPE_INT);\n$table->column('name', Swoole\\Table::TYPE_STRING, 128);\n$table->column('num', Swoole\\Table::TYPE_FLOAT);\n\nif (!$table->create())\n{\n    echo __LINE__.\" error\";\n}\n$table->set('test_key', array('id' => 1, 'name' => 'rango', 'num' => 3.1415926));\n$table->set('hello_world', array('id' => 100, 'name' => 'xinhua', 'num' => 399.66));\n\n$_key = array();\n$_num = array();\nforeach ($table as $key => $value)\n{\n    $_key [] = $key;\n    $_num [] = $value['num'];\n}\nsort($_key);\nsort($_num);\nif (implode('', $_key) == 'hello_worldtest_key' and  array_sum($_num) == 399.66 + 3.1415926)\n{\n    echo 'SUCCESS';\n}\n\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_table/getMemorySize_1.phpt",
    "content": "--TEST--\nswoole_table: getMemorySize\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Table;\n\n$table = new Table(2);\nAssert::eq($table->getMemorySize(), 0);\n$table->column('name', Table::TYPE_STRING, 32);\n$table->create();\nAssert::greaterThan($table->getMemorySize(), 0);\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_table/get_after_destroy.phpt",
    "content": "--TEST--\nswoole_table: get after destroy\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process;\nuse Swoole\\Table;\n\n$table = new Table(1024);\n$table->column('id', Table::TYPE_INT);\n$table->column('name', Table::TYPE_STRING, 10);\n$table->create();\n\n$table->set('1', ['id' => 1, 'name' => 'rango']);\n\nAssert::eq($table->get('1')['id'], 1);\nAssert::eq($table->get('1')['name'], 'rango');\n\n$proc = new Process(function () use ($table) {\n    usleep(10000);\n    Assert::eq($table->get('1')['id'], 2);\n    Assert::eq($table->get('1')['name'], '');\n}, true, SOCK_STREAM);\n\n$proc->start();\n\n$table->destroy();\n\n$output = $proc->read();\nAssert::contains($output, 'table is not created or has been destroyed');\n$retval = Process::wait();\nAssert::eq($retval['code'], 255);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_table/get_before_create.phpt",
    "content": "--TEST--\nswoole_table: get before create\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process;\nuse Swoole\\Table;\n\n$proc = new Process(function ()  {\n    $table = new Table(1024);\n    $table->column('id', Table::TYPE_INT);\n    $table->column('name', Table::TYPE_STRING, 10);\n\n    Assert::eq($table->get('1')['id'], 1);\n    Assert::eq($table->get('1')['name'], 'rango');\n}, true, SOCK_STREAM);\n\n$proc->start();\n\n$output = $proc->read();\nAssert::contains($output, 'table is not created or has been destroyed');\n$retval = Process::wait();\nAssert::eq($retval['code'], 255);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_table/gh-5789.phpt",
    "content": "--TEST--\nswoole_table: bug_5789\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Table;\n\n$table = new Table(128, 1);\n$table->column('test', Table::TYPE_INT);\n$table->create();\n\n$value = random_int(1, PHP_INT_MAX);\n$table->set('firstrow', ['test' => $value]);\n\nAssert::eq($table->get('firstrow', 'test'), $value);\nAssert::eq($table->get('firstrow', null), ['test' => $value]);\nAssert::same($table->get('not-exists', null), false);\nAssert::same($table->get('not-exists', 'test'), false);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_table/incr_after_del.phpt",
    "content": "--TEST--\nswoole_table: incr after del\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$table = new \\Swoole\\Table(1024);\n$table->column('id', \\Swoole\\Table::TYPE_INT);\n$table->create();\n\n$table->set('1', ['id' => 1,]);\n\nfor($i=0;$i<10;$i++) {\n  $table->incr('1', 'id', 1);\n}\n\nAssert::eq($table->get('1')['id'], 11);\nAssert::true($table->del('1'));\nAssert::false($table->get('1'));\n\nfor($i = 0; $i < 10; $i++) {\n  $table->incr('1', 'id', 1);\n}\nAssert::eq($table->get('1')['id'], 10);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_table/int.phpt",
    "content": "--TEST--\nswoole_table: int\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$table = new Swoole\\Table(65536);\n\n$table->column('i8', Swoole\\Table::TYPE_INT, 1);\n$table->column('i16', Swoole\\Table::TYPE_INT, 2);\n$table->column('i32', Swoole\\Table::TYPE_INT, 4);\n$table->column('i64', Swoole\\Table::TYPE_INT, 8);\n\nif (!$table->create())\n{\n    echo __LINE__.\" error\";\n}\n\n$ret = $table->set('test_key', array(\n    'i8' => -120,\n    'i16' => -30000,\n    'i32' => -1247483648,\n    'i64' => -9023372036854775808,\n));\nif (!$ret)\n{\n    echo __LINE__.\" error\";\n}\n\n$ret = $table->get('test_key');\nif (!$ret)\n{\n    echo __LINE__.\" error\";\n}\n\nAssert::same($ret['i8'], -120);\nAssert::same($ret['i16'], -30000);\nAssert::same($ret['i32'], -1247483648);\nAssert::same($ret['i64'], -9023372036854775808);\n\n$ret = $table->incr('test_key', 'i8', 8);\nif (!$ret)\n{\n    echo __LINE__.\" error\";\n}\nAssert::same($table->get('test_key', 'i8'), -120 + 8);\n\n$ret = $table->decr('test_key', 'i32', 8);\nif (!$ret)\n{\n    echo __LINE__.\" error\";\n}\nAssert::same($table->get('test_key', 'i32'), -1247483648 - 8);\n\n$ret = $table->set('test_key', array(\n    'i8' => 120,\n    'i16' => 30000,\n    'i32' => 1247483648,\n    'i64' => 9023372036854775808,\n));\nif (!$ret)\n{\n    echo __LINE__.\" error\";\n}\n\n$ret = $table->get('test_key');\nif (!$ret)\n{\n    echo __LINE__.\" error\";\n}\n\nAssert::same($ret['i8'], 120);\nAssert::same($ret['i16'], 30000);\nAssert::same($ret['i32'], 1247483648);\nAssert::same($ret['i64'], 9023372036854775808);\n\n$ret = $table->incr('test_key', 'i8', 4);\nif (!$ret)\n{\n    echo __LINE__.\" error\";\n}\nAssert::same($table->get('test_key', 'i8'), 120 + 4);\n\n$ret = $table->decr('test_key', 'i32', 8);\nif (!$ret)\n{\n    echo __LINE__.\" error\";\n}\nAssert::same($table->get('test_key', 'i32'), 1247483648 - 8);\n\necho \"SUCCESS\";\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_table/key_value.phpt",
    "content": "--TEST--\nswoole_table: key-value operate\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst PI =  3.1415926;\nconst NAME = 'rango';\n\n$table = new Swoole\\Table(65536);\n\n$table->column('id', Swoole\\Table::TYPE_INT);\n$table->column('name', Swoole\\Table::TYPE_STRING, 128);\n$table->column('num', Swoole\\Table::TYPE_FLOAT);\n\nif (!$table->create())\n{\n    echo __LINE__.\" error\";\n}\nif (!$table->set('test_key', array('id' => 1, 'name' => NAME, 'num' =>PI)))\n{\n    echo __LINE__.\" error\";\n}\n\n$ret = $table->get('test_key');\nif (!($ret and is_array($ret) and $ret['id'] == 1))\n{\n    echo __LINE__.\" error\";\n}\n\n$ret = $table->get('test_key', 'id');\nif (!($ret and $ret == 1))\n{\n    echo __LINE__.\" error\";\n}\n\nAssert::eq($table->get('test_key', 'name'), NAME);\nAssert::eq($table->get('test_key', 'num'), PI);\n\n// field not exists\nAssert::false($table->get('test_key', 'id_no_exists'));\nAssert::false($table->get('test_key_no_exists', 'id_no_exists'));\n\n$ret = $table->exist('test_key');\nif (!($ret))\n{\n    echo __LINE__.\" error\";\n}\n\n$ret = $table->exist('test_key_not_exists');\nif ($ret)\n{\n    echo __LINE__.\" error\";\n}\n\n$ret = $table->incr('test_key','id', 2);\nif (!$ret)\n{\n    echo __LINE__.\" error\";\n}\n$_value = $table->get('test_key', 'id');\nif ($_value != 3)\n{\n    echo __LINE__.\" error\";\n}\n\n$ret = $table->decr('test_key', 'id', 2);\nif (!$ret)\n{\n    echo __LINE__ . \" error\";\n}\n$_value = $table->get('test_key', 'id');\nif ($_value != 1)\n{\n    echo __LINE__ . \" error\";\n}\n\n$table->set('hello_world', array('id' => 100, 'name' => 'xinhua', 'num' => 399.66));\nif (count($table) != 2)\n{\n    echo __LINE__.\" error\";\n}\n\n$ret =  $table->del('test_key');\nif (!$ret)\n{\n    echo __LINE__.\" error\";\n}\nif ($table->exist('test_key'))\n{\n    echo __LINE__.\" error\";\n}\necho \"SUCCESS\\n\";\n?>\n--EXPECT--\nSUCCESS\n"
  },
  {
    "path": "tests/swoole_table/negative.phpt",
    "content": "--TEST--\nswoole_table: negative\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$table = new Swoole\\Table(65536);\n\n$table->column('v1', Swoole\\Table::TYPE_INT);\n$table->column('v2', Swoole\\Table::TYPE_FLOAT);\n\nif (!$table->create())\n{\n    echo __LINE__.\" error\";\n}\n$table->set('test1', ['v1' => 0, 'v2' => 0]);\n\nAssert::same($table->decr('test1', 'v1', 1), -1);\nAssert::same($table->decr('test1', 'v2', 1.5), -1.5);\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_table/random_bytes.phpt",
    "content": "--TEST--\nswoole_table: read/write random data\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$table = new \\Swoole\\Table(IS_IN_CI ? 1024 : 2048);\n$table->column('string', \\Swoole\\Table::TYPE_STRING, 256 * 1024);\n$table->create();\n\n$n = IS_IN_CI ? 100 : 1000;\n// $n = 100;\n\n$map = [];\n\nwhile($n--) {\n    $key = \"key-\".rand(1000000, 9999999);\n    $value = RandStr::getBytes(rand(100*1024, 250*1024));\n    $map[$key] = $value;\n    $table->set($key, ['string' => $value]);\n}\n\nforeach($map as $k => $v) {\n    Assert::same($table->get($k)['string'], $v);\n}\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_table/set_after_del.phpt",
    "content": "--TEST--\nswoole_table: set after del\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$table = new \\Swoole\\Table(1024);\n$table->column('id', \\Swoole\\Table::TYPE_INT);\n$table->column('name', \\Swoole\\Table::TYPE_STRING, 10);\n$table->create();\n\n$table->set('1', ['id' => 1, 'name' => 'rango']);\n\nAssert::eq($table->get('1')['id'], 1);\nAssert::eq($table->get('1')['name'], 'rango');\nAssert::true($table->del('1'));\nAssert::false($table->get('1'));\n$table->set('1', ['id' => 2, ]);\n\nAssert::eq($table->get('1')['id'], 2);\nAssert::eq($table->get('1')['name'], '');\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_table/stats.phpt",
    "content": "--TEST--\nswoole_table: stats\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Table;\n\ndefine('N',  IS_IN_CI ? 10000 : 100000);\n\n$table = new Table(N);\n$table->column('string', Table::TYPE_STRING, 256);\n$table->create();\n\n$map = [];\n$keys = [];\n\n$n = N;\nwhile ($n--) {\n    $key = base64_decode(RandStr::getBytes(rand(10, 30)));\n    $value = RandStr::getBytes(rand(100, 250));\n    if ($table->set($key, ['string' => $value])) {\n        $map[$key] = $value;\n        $keys[] = $key;\n    }\n}\n\n$stats1 = $table->stats();\n\nAssert::eq(count($keys), N);\nAssert::eq(count(array_unique($keys)), $stats1['insert_count']);\n\nphpt_var_dump(\"insert\\n\".str_repeat('-', 64), $stats1);\n\ndefine('UPDATE_N', rand(100, 1000));\n\n$_n = UPDATE_N;\nwhile ($_n--) {\n    $key = array_rand($map);\n    $value = RandStr::getBytes(rand(100, 250));\n    Assert::true($table->set($key, ['string' => $value]));\n    $map[$key] = $value;\n}\n\n$stats2 = $table->stats();\nAssert::eq($stats1['update_count'] + UPDATE_N, $stats2['update_count']);\nphpt_var_dump(\"update\\n\" . str_repeat('-', 64), $stats2);\n\nforeach($map as $k => $v) {\n    Assert::same($table->get($k)['string'], $v);\n    $table->del($k);\n}\n\n$stats3 = $table->stats();\nAssert::eq($stats3['num'], 0);\nAssert::eq($stats3['available_slice_num'], $stats3['total_slice_num']);\nphpt_var_dump(\"delete\\n\" . str_repeat('-', 64), $stats3);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_table/type_conv.phpt",
    "content": "--TEST--\nswoole_table: type convert\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst PI =  3.1415926;\nconst NAME = 'rango';\n\n$table = new Swoole\\Table(65536);\n\n$table->column('id', Swoole\\Table::TYPE_INT);\n$table->column('name', Swoole\\Table::TYPE_STRING, 128);\n$table->column('num', Swoole\\Table::TYPE_FLOAT);\n\nif (!$table->create()) {\n    echo __LINE__.\" error\";\n}\n\n$table->set('test_key', array('id' => 1, 'name' => NAME, 'num' => PI));\n$table->set(1002, array('id' => '2', 'name' => 'hello', 'num' => PI + 9));\n\n$r1 = ($table->get('test_key'));\n$r2 = ($table->get(1002));\n\nAssert::same($r1['id'], 1);\nAssert::same($r2['id'], 2);\n\n$table->set('test_key', array('id' => '2348', 'name' => 1024, 'num' => '3.231'));\n$r1 = ($table->get('test_key'));\n\nAssert::same($r1['id'], 2348);\nAssert::same($r1['num'], 3.231);\nAssert::same($r1['name'], '1024');\n\n$table->set('test_key', array('id' => 'abc', 'name' => 1024, 'num' => '3.231'));\n$r1 = ($table->get('test_key'));\nAssert::same($r1['id'], 0);\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_thread/add_update.phpt",
    "content": "--TEST--\nswoole_thread: add/update\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread\\Map;\n\nconst KEY_EXISTS = 'exists';\nconst KEY_NOT_EXISTS = 'not_exists';\n\nconst INDEX_EXISTS = 1000;\nconst INDEX_NOT_EXISTS = 9999;\n\n$value = random_bytes(32);\n\n$m = new Map();\n$m[KEY_EXISTS] = $value;\n\nAssert::false($m->update(KEY_NOT_EXISTS, $value));\nAssert::true($m->add(KEY_NOT_EXISTS, $value));\nAssert::eq($m[KEY_NOT_EXISTS], $value);\n\nunset($m[KEY_NOT_EXISTS]);\nAssert::eq($m[KEY_NOT_EXISTS], null);\n\nAssert::false($m->add(KEY_EXISTS, $value));\nAssert::true($m->update(KEY_EXISTS, $value));\nAssert::eq($m[KEY_EXISTS], $value);\n\n$m2 = new Map();\n$m2[INDEX_EXISTS] = $value;\n\nAssert::false($m2->update(INDEX_NOT_EXISTS, $value));\nAssert::true($m2->add(INDEX_NOT_EXISTS, $value));\nAssert::eq($m2[INDEX_NOT_EXISTS], $value);\n\nunset($m2[INDEX_NOT_EXISTS]);\nAssert::eq($m2[INDEX_NOT_EXISTS], null);\n\nAssert::false($m2->add(INDEX_EXISTS, $value));\nAssert::true($m2->update(INDEX_EXISTS, $value));\nAssert::eq($m2[INDEX_EXISTS], $value);\n\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_thread/affinity.phpt",
    "content": "--TEST--\nswoole_thread: Affinity\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\n$tm = new \\SwooleTest\\ThreadManager();\n\nAssert::eq(Thread::API_NAME, 'POSIX Threads');\n\n$tm->parentFunc = function () {\n    $thread = new Thread(__FILE__, 'child');\n    $r = Thread::getAffinity();\n    Assert::eq(count($r), swoole_cpu_num());\n    Assert::assert(Thread::setAffinity([1]));\n    Assert::eq(Thread::getAffinity(), [1]);\n    $thread->join();\n};\n\n$tm->childFunc = function () {\n    $r = Thread::getAffinity();\n    Assert::eq(count($r), swoole_cpu_num());\n    Assert::assert(Thread::setAffinity([0]));\n    Assert::eq(Thread::getAffinity(), [0]);\n};\n\n$tm->run();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_thread/arraylist.phpt",
    "content": "--TEST--\nswoole_thread: arraylist\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread\\ArrayList;\n\n$uuid = uniqid();\n$array = [\n    random_int(1, 999999999999999999),\n    random_bytes(128),\n    $uuid,\n    time(),\n];\n\n$l = new ArrayList($array);\nAssert::eq(count($l), count($array));\nAssert::eq($l->toArray(), $array);\nAssert::eq($l->find($uuid), 2);\n\nfor ($i = 0; $i < count($array); $i++) {\n    Assert::eq($l[$i], $array[$i]);\n}\n\n$array2 = [\n    'key' => 'value',\n    'hello' => 'world',\n];\n$l[] = $array2;\n\nAssert::eq(count($l), 5);\nAssert::eq($l[4]->toArray(), $array2);\n\ntry {\n    $l2 = new ArrayList($array2);\n    echo \"never here\\n\";\n} catch (Throwable $e) {\n    Assert::contains($e->getMessage(), 'must be an array of type list');\n}\n\n$uuid2 = uniqid();\n$l[] = $uuid2;\n$count = count($l);\n\nunset($l[1]);\nAssert::eq(count($l), $count - 1);\nAssert::eq($l[1], $uuid);\nAssert::eq($l->find($uuid), 1);\nAssert::eq($l->find($uuid2), $count - 2);\n\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_thread/async-io.phpt",
    "content": "--TEST--\nswoole_thread: async-io\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\nconst C = 4;\nconst N = 256;\nconst M = 9999;\n\n$args = Thread::getArguments();\n$running = true;\n$md5 = md5_file(__FILE__);\n\nif (empty($args)) {\n    $threads = [];\n    $atomic = new Swoole\\Thread\\Atomic();\n    $atomicLong = new Swoole\\Thread\\Atomic\\Long();\n    for ($i = 0; $i < C; $i++) {\n        $threads[] = new Thread(__FILE__, $i, $atomic, $atomicLong);\n    }\n    for ($i = 0; $i < C; $i++) {\n        $threads[$i]->join();\n    }\n    Assert::eq($atomic->get(), C * N);\n    Assert::eq($atomicLong->get(), C * N * M);\n} else {\n    $id = $args[0];\n    $atomic = $args[1];\n    $atomicLong = $args[2];\n    Co\\run(function () use ($atomic, $atomicLong, $md5) {\n        $n = N;\n        while ($n--) {\n            $atomic->add();\n            $atomicLong->add(M);\n            $rs = \\Swoole\\Coroutine\\System::readFile(__FILE__);\n            Assert::eq(md5($rs), $md5);\n        }\n    });\n    exit(0);\n}\necho \"DONE\\n\";\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_thread/atomic_ctor.phpt",
    "content": "--TEST--\nswoole_thread: atomic ctor\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Lock;\n\n\n$tm = new \\SwooleTest\\ThreadManager();\n\n$tm->parentFunc = function () {\n    $lock = new Lock;\n    $lock->lock();\n    $num1 = random_int(1, 1 << 31);\n    $num2 = random_int(1 << 31, PHP_INT_MAX);\n    $atomic1 = new Swoole\\Thread\\Atomic($num1);\n    $atomic2 = new Swoole\\Thread\\Atomic\\Long($num2);\n    $thread = new Thread(__FILE__, $lock, $atomic1, $atomic2, $num1, $num2);\n    $lock->lock();\n    echo \"main thread\\n\";\n    $thread->join();\n};\n\n$tm->childFunc = function ($lock, $atomic1, $atomic2, $num1, $num2) {\n    echo \"child thread\\n\";\n    usleep(200_000);\n    $lock->unlock();\n    Assert::eq($atomic1->get(), $num1);\n    Assert::eq($atomic2->get(), $num2);\n    exit(0);\n};\n\n$tm->run();\n?>\n--EXPECTF--\nchild thread\nmain thread\n"
  },
  {
    "path": "tests/swoole_thread/barrier.phpt",
    "content": "--TEST--\nswoole_thread: barrier\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Barrier;\n\n$tm = new \\SwooleTest\\ThreadManager();\n\n$tm->parentFunc = function () {\n    $barrier = new Barrier(2);\n    $s = microtime(true);\n    $thread = new Thread(__FILE__, $barrier);\n    $barrier->wait();\n    Assert::greaterThanEq(microtime(true) - $s, 0.2);\n    echo \"main thread\\n\";\n    $thread->join();\n};\n\n$tm->childFunc = function ($barrier) {\n    echo \"child thread\\n\";\n    usleep(200_000);\n    $barrier->wait();\n    exit(0);\n};\n\n$tm->run();\n?>\n--EXPECTF--\nchild thread\nmain thread\n"
  },
  {
    "path": "tests/swoole_thread/co-stream.phpt",
    "content": "--TEST--\nswoole_thread: co stream\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Runtime;\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Queue;\n\n$tm = new \\SwooleTest\\ThreadManager();\n$tm->initFreePorts(increment: crc32(__FILE__) % 1000);\n\n$tm->parentFunc = function () use ($tm) {\n    Runtime::enableCoroutine(SWOOLE_HOOK_ALL);\n    Co\\run(function () use ($tm) {\n        $queue = new Queue();\n        $fp = stream_socket_server('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr);\n        $queue->push($fp);\n        $thread = new Thread(__FILE__, $queue);\n        var_dump('main thread');\n        $thread->join();\n    });\n};\n\n$tm->childFunc = function ($queue) use ($tm) {\n    var_dump('child thread');\n    $fp = $queue->pop();\n    Co\\run(function () use ($fp, $tm) {\n        var_dump('child thread, co 0');\n        Co\\go(function () use ($tm) {\n            var_dump('child thread, co 1');\n            $client = stream_socket_client('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr);\n            Assert::notEmpty($client);\n            $data = fread($client, 8192);\n            Assert::eq($data, \"hello world\\n\");\n            fclose($client);\n        });\n        $conn = stream_socket_accept($fp, -1);\n        fwrite($conn, \"hello world\\n\");\n        fclose($conn);\n        fclose($fp);\n    });\n};\n\n$tm->run();\n?>\n--EXPECT--\nstring(11) \"main thread\"\nstring(12) \"child thread\"\nstring(18) \"child thread, co 0\"\nstring(18) \"child thread, co 1\"\n"
  },
  {
    "path": "tests/swoole_thread/co-user-yield.phpt",
    "content": "--TEST--\nswoole_thread: co user yield\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Timer;\n\nconst C = 4;\nconst N = 32;\n\n$args = Thread::getArguments();\n$running = true;\n\nswoole_async_set(['enable_coroutine' => false]);\n\nif (empty($args)) {\n    $threads = [];\n    $atomic = new Swoole\\Thread\\Atomic();\n    for ($i = 0; $i < C; $i++) {\n        $threads[] = new Thread(__FILE__, $i, $atomic);\n    }\n    for ($i = 0; $i < C; $i++) {\n        $threads[$i]->join();\n    }\n    Assert::eq($atomic->get(), C * N);\n} else {\n    $id = $args[0];\n    $atomic = $args[1];\n    Co\\run(function () use ($atomic) {\n        $n = N;\n        $cid = Co::getCid();\n        while ($n--) {\n            Timer::after(10, function () use ($cid) {\n                Co::resume($cid);\n            });\n            Co::yield();\n            $atomic->add();\n        }\n    });\n    exit(0);\n}\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_thread/empty_args.phpt",
    "content": "--TEST--\nswoole_thread: info\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\n$args = Thread::getArguments();\nAssert::assert($args === null);\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_thread/exit.phpt",
    "content": "--TEST--\nswoole_thread: lock\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Lock;\nuse SwooleTest\\ThreadManager;\n\nconst CODE = 234;\n\n$tm = new ThreadManager();\n\n$tm->parentFunc = function () {\n    $lock = new Lock;\n    $lock->lock();\n    $thread = new Thread(__FILE__, $lock);\n    $lock->unlock();\n    $thread->join();\n    Assert::eq($thread->getExitStatus(), CODE);\n    echo 'DONE' . PHP_EOL;\n};\n\n$tm->childFunc = function ($lock) {\n    $lock->lock();\n    usleep(100_000);\n    exit(CODE);\n};\n\n$tm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_thread/fatal_error_1.inc",
    "content": "<?php\n\nuse Swoole\\Thread;\n\n$args = Thread::getArguments();\nif (empty($args)) {\n    echo \"start child thread\\n\";\n    $threads[] = new Thread(__FILE__, 'error');\n    $threads[0]->join();\n    echo \"stop child thread\\n\";\n} else {\n    Co\\run(function () {\n        (function () {\n            swoole_implicit_fn('fatal_error');\n        })();\n    });\n}\necho \"DONE\\n\";\n"
  },
  {
    "path": "tests/swoole_thread/fatal_error_1.phpt",
    "content": "--TEST--\nswoole_thread: fatal error\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = ProcessManager::exec(function () {\n    include __DIR__ . '/fatal_error_1.inc';\n});\n$output = $pm->getChildOutput();\nAssert::contains($output, \"start child thread\\n\");\nAssert::contains($output, \"stop child thread\\n\");\nAssert::contains($output, \"Fatal error: Uncaught Swoole\\Error: test\");\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_thread/fatal_error_2.inc",
    "content": "<?php\n\nuse Swoole\\Thread;\n\n$args = Thread::getArguments();\nif (empty($args)) {\n    echo \"start child thread\\n\";\n    $threads[] = new Thread(__FILE__, 'error');\n    $threads[0]->join();\n    echo \"stop child thread\\n\";\n} else {\n    (function () {\n        swoole_implicit_fn('fatal_error');\n    })();\n}\necho \"DONE\\n\";\n"
  },
  {
    "path": "tests/swoole_thread/fatal_error_2.phpt",
    "content": "--TEST--\nswoole_thread: fatal error\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Thread;\n\n$pm = ProcessManager::exec(function () {\n    include __DIR__ . '/fatal_error_2.inc';\n});\n$output = $pm->getChildOutput();\nAssert::contains($output, \"start child thread\\n\");\nAssert::contains($output, \"stop child thread\\n\");\nAssert::contains($output, \"Fatal error: Uncaught Swoole\\Error: test\");\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_thread/fatal_error_3.phpt",
    "content": "--TEST--\nswoole_thread: fatal error 3\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse SwooleTest\\ThreadManager;\n\n$tm = new ThreadManager();\n\n$tm->parentFunc = function () {\n    register_shutdown_function(function () {\n        echo \"shutdown\\n\";\n    });\n    Assert::eq(Thread::getInfo()['thread_num'], 1);\n    $thread = new Thread(__FILE__, 'child');\n    usleep(100000);\n    echo \"main thread\\n\";\n    Assert::eq(Thread::getInfo()['thread_num'], 2);\n    $thread->detach();\n};\n\n$tm->childFunc = function () {\n    echo \"child thread\\n\";\n    sleep(1000);\n    exit(0);\n};\n\n$tm->run();\n?>\n--EXPECTF--\nchild thread\nmain thread\nshutdown\n[%s]\tWARNING\tPHPCoroutine::enable_hook(): The runtime hook can only set on the main thread and no child threads have been created\n[%s]\tWARNING\tphp_swoole_thread_rshutdown(): Fatal Error: 2 active threads are running, cannot exit safely.\n"
  },
  {
    "path": "tests/swoole_thread/incr.phpt",
    "content": "--TEST--\nswoole_thread: incr/decr\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread\\Map;\nuse Swoole\\Thread\\ArrayList;\n\nconst KEY_EXISTS_LVAL = 'exists_lval';\nconst KEY_EXISTS_DVAL = 'exists_dval';\nconst KEY_NOT_EXISTS_LVAL = 'not_exists_lval';\nconst KEY_NOT_EXISTS_DVAL = 'not_exists_dval';\n\n$init_lval = random_int(1, 999999999999999999);\n$init_dval = random_int(1, 999999999999999999) / 13.03;\n\n$add_lval = random_int(1, 88888888);\n$add_dval = random_int(1, 88888888) / 17.07;\n\n$m = new Map();\n$m[KEY_EXISTS_LVAL] = $init_lval;\n$m[KEY_EXISTS_DVAL] = $init_dval;\n$l = new ArrayList();\n\nAssert::eq($m->incr(KEY_NOT_EXISTS_LVAL), 1);\nAssert::eq($m[KEY_NOT_EXISTS_LVAL], 1);\n\nAssert::eq($m->incr(KEY_NOT_EXISTS_DVAL, $add_dval), $add_dval);\nAssert::eq($m[KEY_NOT_EXISTS_DVAL], $add_dval);\n\nAssert::eq($m->incr(KEY_EXISTS_LVAL), $init_lval + 1);\nAssert::eq($m[KEY_EXISTS_LVAL], $init_lval + 1);\n\nAssert::eq($m->incr(KEY_EXISTS_DVAL), $init_dval + 1);\nAssert::eq($m[KEY_EXISTS_DVAL], $init_dval + 1);\n\n// clean\n$m[KEY_EXISTS_LVAL] = $init_lval;\n$m[KEY_EXISTS_DVAL] = $init_dval;\nunset($m[KEY_NOT_EXISTS_DVAL], $m[KEY_NOT_EXISTS_LVAL]);\n\nAssert::eq($m->incr(KEY_EXISTS_LVAL, $add_lval), $init_lval + $add_lval);\nAssert::eq($m[KEY_EXISTS_LVAL], $init_lval + $add_lval);\n\nAssert::eq($m->incr(KEY_EXISTS_DVAL, $add_lval), $init_dval + $add_lval);\nAssert::eq($m[KEY_EXISTS_DVAL], $init_dval + $add_lval);\n\nAssert::eq($m->decr(KEY_NOT_EXISTS_LVAL), -1);\nAssert::eq($m[KEY_NOT_EXISTS_LVAL], -1);\n\n$m[KEY_EXISTS_LVAL] = $init_lval;\n$m[KEY_EXISTS_DVAL] = $init_dval;\n\nAssert::eq($m->decr(KEY_EXISTS_LVAL, $add_lval), $init_lval - $add_lval);\nAssert::eq($m[KEY_EXISTS_LVAL], $init_lval - $add_lval);\n\nAssert::eq($m->decr(KEY_EXISTS_DVAL, $add_lval), $init_dval - $add_lval);\nAssert::eq($m[KEY_EXISTS_DVAL], $init_dval - $add_lval);\n\nAssert::eq($l->incr(0), 1);\nAssert::eq($l[0], 1);\n\nAssert::eq($l->incr(1, $add_lval), $add_lval);\nAssert::eq($l[1], $add_lval);\n\n$l[0] = 0;\n$l[1] = 0;\n\nAssert::eq($l->incr(0, $add_dval), intval($add_dval));\n\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_thread/info.phpt",
    "content": "--TEST--\nswoole_thread: info\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\n$tm = new \\SwooleTest\\ThreadManager();\n\nAssert::eq(Thread::API_NAME, 'POSIX Threads');\n\n$tm->parentFunc = function () {\n    $thread = new Thread(__FILE__, 'child');\n    $info = Thread::getInfo();\n    Assert::true($info['is_main_thread']);\n    $thread->join();\n};\n\n$tm->childFunc = function () {\n    $info = Thread::getInfo();\n    Assert::false($info['is_main_thread']);\n};\n\n$tm->run();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_thread/lock.phpt",
    "content": "--TEST--\nswoole_thread: lock\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Lock;\n\n$tm = new \\SwooleTest\\ThreadManager();\n\n$tm->parentFunc = function () {\n    $lock = new Lock;\n    $lock->lock();\n    $thread = new Thread(__FILE__, $lock);\n    $lock->lock();\n    echo \"main thread\\n\";\n    $thread->join();\n};\n\n$tm->childFunc = function ($lock) {\n    echo \"child thread\\n\";\n    usleep(200_000);\n    $lock->unlock();\n    exit(0);\n};\n\n$tm->run();\n?>\n--EXPECTF--\nchild thread\nmain thread\n"
  },
  {
    "path": "tests/swoole_thread/map.phpt",
    "content": "--TEST--\nswoole_thread: map\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread\\Map;\n\n$uuid = uniqid();\n\n$array = [\n    'a' => random_int(1, 999999999999999999),\n    'b' => random_bytes(128),\n    'c' => $uuid,\n    'd' => time(),\n];\n\n$m = new Map($array);\nAssert::eq($m->toArray(), $array);\nAssert::eq(count($m), count($array));\nAssert::eq($m->find($uuid), 'c');\n\nforeach ($array as $k => $v) {\n    Assert::eq($m[$k], $array[$k]);\n}\n\n$array2 = [\n    'key' => 'value',\n    'hello' => 'world',\n];\n$m['map'] = $array2;\nAssert::eq(count($m), 5);\nAssert::eq($m['map']->toArray(), $array2);\nAssert::eq(count($m['map']), count($array2));\nAssert::eq($m['map']->values(), array_values($array2));\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_thread/map2array.phpt",
    "content": "--TEST--\nswoole_thread: map to array\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\n\nuse Swoole\\Thread\\Map;\n\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$LURDATE = new Map;\n$time = 'abc';\n$LURDATE[$time] = new Map([\"saaa\" => 1111]);\n$ls = $LURDATE[$time]->toArray();\nforeach ($ls as $k => $v) {\n    unset($LURDATE[$time][$k]);\n}\nunset($LURDATE[$time]);\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_thread/name.phpt",
    "content": "--TEST--\nswoole_thread: name\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\nskip_if_not_linux();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\n$tm = new \\SwooleTest\\ThreadManager();\n\nAssert::eq(Thread::API_NAME, 'POSIX Threads');\n\n$tm->parentFunc = function () {\n    $thread = new Thread(__FILE__, 'child');\n    Thread::setName('master thread');\n    Assert::eq(get_thread_name(), 'master thread');\n    $thread->join();\n};\n\n$tm->childFunc = function () {\n    Thread::setName('child thread');\n    Assert::eq(get_thread_name(), 'child thread');\n};\n\n$tm->run();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_thread/numeric_strkey.phpt",
    "content": "--TEST--\nswoole_thread: numeric key\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\n\nuse Swoole\\Thread\\Map;\n\nrequire __DIR__ . '/../include/bootstrap.php';\n$uuid = uniqid();\n\nconst I_KEY = 6666;\nconst S_KEY = '6666';\n\n$arr = new Map([S_KEY => 2222, 'test' => $uuid]);\nAssert::eq($arr[S_KEY], 2222);\nAssert::eq($arr[6666], 2222);\nAssert::eq($arr['test'], $uuid);\n\nunset($arr[S_KEY]);\nAssert::false(isset($arr[S_KEY]));\nAssert::keyNotExists($arr->toArray(), I_KEY);\n\n$uuid2 = uniqid();\n$arr[6666.66] = $uuid2;\n$arr['6666.66'] = $uuid2;\nAssert::eq($arr[6666], $uuid2);\n\n$arr[true] = $uuid2;\n$arr[false] = $uuid2;\n$arr[null] = $uuid2;\n\n$stream = fopen('php://stdin', 'r+');\n@$arr[$stream] = $uuid2;\n\nAssert::eq($arr[true], $uuid2);\nAssert::eq($arr[false], $uuid2);\nAssert::eq($arr[null], $uuid2);\nAssert::eq(@$arr[$stream], $uuid2);\n\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_thread/php_socket.phpt",
    "content": "--TEST--\nswoole_thread: php_socket\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Queue;\nuse SwooleTest\\ThreadManager;\n\n$tm = new ThreadManager();\n$tm->initFreePorts(increment: crc32(__FILE__) % 1000);\n\n$tm->parentFunc = function () use ($tm) {\n    $queue = new Queue();\n    $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n    socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);\n    socket_bind($sock, '127.0.0.1', $tm->getFreePort());\n    $queue->push($sock);\n    $thread = new Thread(__FILE__, $queue, 0);\n    var_dump('main thread');\n    $thread->join();\n};\n\n$tm->childFunc = function ($queue, $id) use ($tm) {\n    if ($id === 0) {\n        var_dump('child thread 0');\n        $svr_sock = $queue->pop();\n        socket_listen($svr_sock, 128);\n        $thread = new Thread(__FILE__, $queue, 1);\n        $conn = socket_accept($svr_sock);\n        socket_write($conn, \"Swoole: hello world\\n\");\n        socket_close($conn);\n        socket_close($svr_sock);\n        $thread->join();\n    } else {\n        var_dump('child thread 1');\n        $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\n        socket_connect($sock, '127.0.0.1', $tm->getFreePort());\n        socket_send($sock, \"hello world\", 0, 0);\n        socket_recv($sock, $buf, 1024, 0);\n        Assert::eq($buf, \"Swoole: hello world\\n\");\n        socket_close($sock);\n    }\n    exit(0);\n};\n\n$tm->run();\necho \"Done\\n\";\n?>\n--EXPECT--\nstring(11) \"main thread\"\nstring(14) \"child thread 0\"\nstring(14) \"child thread 1\"\nDone\n"
  },
  {
    "path": "tests/swoole_thread/pipe.phpt",
    "content": "--TEST--\nswoole_thread: pipe\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\n$args = Thread::getArguments();\n\nif (empty($args)) {\n    $rdata = random_bytes(random_int(1024, 2048));\n    Co\\run(function () use ($rdata) {\n        $sockets = swoole_coroutine_socketpair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);\n        $thread = new Thread(__FILE__, $sockets[1], $rdata);\n        Assert::eq($sockets[0]->recv(8192), $rdata);\n        $thread->join();\n        echo \"DONE\\n\";\n    });\n} else {\n    $socket = $args[0];\n    $rdata = $args[1];\n    Co\\run(function () use ($socket, $rdata, $argv) {\n        usleep(100);\n        shell_exec('sleep 0.01');\n        $socket->send($rdata);\n    });\n    exit(0);\n}\n?>\n--EXPECTF--\nDONE\n"
  },
  {
    "path": "tests/swoole_thread/priority.phpt",
    "content": "--TEST--\nswoole_thread: priority\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\nskip_if_not_root();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\n$tm = new \\SwooleTest\\ThreadManager();\n\nAssert::eq(Thread::API_NAME, 'POSIX Threads');\n\nfunction test_thread_priority($priority, $policy)\n{\n    $r = Thread::getPriority();\n    Assert::eq($r['policy'], 0);\n    Assert::eq($r['priority'], 0);\n    Assert::assert(Thread::setPriority($priority, $policy));\n\n    $r = Thread::getPriority();\n    Assert::eq($r['policy'], $policy);\n    Assert::eq($r['priority'], $priority);\n}\n\n$tm->parentFunc = function () {\n    $thread = new Thread(__FILE__, 'child');\n    test_thread_priority(10, Thread::SCHED_FIFO);\n    $thread->join();\n};\n\n$tm->childFunc = function () {\n    test_thread_priority(5, Thread::SCHED_RR);\n};\n\n$tm->run();\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_thread/putenv.phpt",
    "content": "--TEST--\nswoole_thread: putenv\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$c = 8;\n$threads = [];\n\nfor ($i = 0; $i < $c; $i++) {\n    $threads[] = new Swoole\\Thread(TESTS_API_PATH . '/swoole_thread/putenv.php', $i);\n}\n\nfor ($i = 0; $i < $c; $i++) {\n    $threads[$i]->join();\n}\n\nfor ($i = 0; $i < $c; $i++) {\n    $env = getenv('TEST_THREAD_' . $i);\n    Assert::notEmpty($env);\n}\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_thread/queue.phpt",
    "content": "--TEST--\nswoole_thread: queue\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Queue;\n\nconst C = 4;\nconst N = 1024;\n\n$args = Thread::getArguments();\n$total_parent = 0;\n$total_child = 0;\n\nif (empty($args)) {\n    $threads = [];\n    $queue = new Queue;\n    $map = new Thread\\Map();\n    for ($i = 0; $i < C; $i++) {\n        $threads[] = new Thread(__FILE__, $i, $queue, $map);\n    }\n    $n = N;\n    while ($n--) {\n        $rdata = base64_encode(random_bytes(random_int(16, 128)));\n        $total_parent += strlen($rdata);\n        $queue->push($rdata, Queue::NOTIFY_ONE);\n        usleep(random_int(100, 1000));\n    }\n    $n = 4;\n    while ($n--) {\n        $queue->push('', Queue::NOTIFY_ONE);\n    }\n    for ($i = 0; $i < C; $i++) {\n        $threads[$i]->join();\n        $total_child += $map[$i];\n    }\n    Assert::eq($total_parent, $total_child);\n} else {\n    $i = $args[0];\n    $queue = $args[1];\n    $map = $args[2];\n    $map[$i] = 0;\n    while (1) {\n        $job = $queue->pop(-1);\n        if (!$job) {\n            break;\n        }\n        $map[$i] += strlen($job);\n        Assert::assert(strlen($job), 16);\n    }\n    exit(0);\n}\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_thread/queue_notify_all.phpt",
    "content": "--TEST--\nswoole_thread: queue notify all\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Queue;\nuse Swoole\\Thread\\Barrier;\n\nconst C = 4;\n\n$args = Thread::getArguments();\n\nif (empty($args)) {\n    $threads = [];\n    $queue = new Queue;\n    $barrier = new Barrier(C + 1);\n    $uuid = uniqid();\n    for ($i = 0; $i < C; $i++) {\n        $threads[] = new Thread(__FILE__, $i, $queue, $uuid, $barrier);\n    }\n    $barrier->wait();\n    usleep(10000);\n    for ($i = 0; $i < C; $i++) {\n        $queue->push($uuid, Queue::NOTIFY_ALL);\n    }\n    for ($i = 0; $i < C; $i++) {\n        $threads[$i]->join();\n    }\n    Assert::eq($queue->count(), 0);\n} else {\n    $i = $args[0];\n    $queue = $args[1];\n    $uuid = $args[2];\n    $barrier = $args[3];\n    $barrier->wait();\n    $job = $queue->pop(-1);\n    if ($job !== null) {\n        Assert::eq($job, $uuid);\n    } else {\n        Assert::eq(swoole_last_error(), SWOOLE_ERROR_NO_PAYLOAD);\n    }\n    exit(0);\n}\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_thread/server/base.phpt",
    "content": "--TEST--\nswoole_thread/server: base\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire __DIR__ . '/functions.inc';\n\nuse Swoole\\Thread;\n\nconst SIZE = 2 * 1024 * 1024;\n$port = get_constant_port(__FILE__);\n\n$serv = new Swoole\\Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => 2,\n    'log_level' => SWOOLE_LOG_ERROR,\n    'open_eof_check' => true,\n    'package_eof' => \"\\r\\n\",\n    'init_arguments' => function () {\n        global $queue, $atomic;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic = new Swoole\\Thread\\Atomic(1);\n        return [$queue, $atomic];\n    }\n));\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic] = Thread::getArguments();\n    if ($workerId == 0) {\n        $queue->push(\"begin\\n\", Thread\\Queue::NOTIFY_ALL);\n    }\n});\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n    $json = json_decode(rtrim($data));\n    if ($json->type == 'eof') {\n        $serv->send($fd, \"EOF\\r\\n\");\n    }\n});\n$serv->on('shutdown', function () {\n    global $queue, $atomic;\n    echo 'shutdown', PHP_EOL;\n    Assert::eq($atomic->get(), 0);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    global $port;\n    echo $queue->pop(-1);\n    Co\\run(function () use ($port) {\n        thread_server_test_eof_client($port);\n    });\n    $atomic->set(0);\n    echo \"done\\n\";\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECT--\nbegin\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/bug_5662.phpt",
    "content": "--TEST--\nswoole_thread/server: Github #5662\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Queue;\n\n$port = get_constant_port(__FILE__);\n$server = new Swoole\\Http\\Server('127.0.0.1', $port, SWOOLE_THREAD);\n$server->set([\n\t'log_file' => '/dev/null',\n\t'worker_num' => 2,\n\t'max_request' => 5,\n\t'init_arguments' => function () {\n        global $queue;\n        $queue = new Queue();\n        return [$queue];\n    }\n]);\n$server->on('WorkerStart', function (Swoole\\Server $server, $workerId) {\n    [$queue] = Thread::getArguments();\n    $queue->push('start', Queue::NOTIFY_ALL);\n});\n$server->addProcess(new Swoole\\Process(function ($process) use ($server, $port) {\n\t[$queue] = Thread::getArguments();\n\tAssert::true($queue->pop(-1) == 'start');\n\tfor ($i = 0; $i < 20; $i++) {\n        Assert::true(file_get_contents(\"http://127.0.0.1:{$port}/\") == 'OK');\n    }\n    $server->shutdown();\n}));\n$server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n    $response->end('OK');\n});\n$server->start();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_thread/server/bug_5694.phpt",
    "content": "--TEST--\nswoole_thread/server: Github #5694\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Queue;\n\n$port = get_constant_port(__FILE__);\n$server = new Swoole\\Http\\Server('127.0.0.1', $port, SWOOLE_THREAD);\n$server->set([\n\t'log_file' => '/dev/null',\n\t'worker_num' => 1,\n\t'task_worker_num' => 1,\n\t'max_request' => 1,\n\t'heartbeat_check_interval'=> 1,\n    'heartbeat_idle_time'=> 2,\n    'enable_coroutine' => true,\n    'hook_flags' => SWOOLE_HOOK_ALL,\n    'init_arguments' => function () {\n        global $queue;\n        $queue = new Queue();\n        return [$queue];\n    }\n]);\n\n$server->on('WorkerStart', function (Swoole\\Server $server, $workerId) {\n    [$queue] = Thread::getArguments();\n    $queue->push('start', Queue::NOTIFY_ALL);\n});\n\n$server->on('Task', function (Swoole\\Server $server, int $task_id, int $src_worker_id, mixed $data) {\n    var_dump($data);\n});\n\n$server->addProcess(new Swoole\\Process(function ($process) use ($server, $port) {\n\t[$queue] = Thread::getArguments();\n\tAssert::true($queue->pop(-1) == 'start');\n\tAssert::true(file_get_contents(\"http://127.0.0.1:{$port}/\") == 'OK');\n\tsleep(1);\n\tAssert::true(file_get_contents(\"http://127.0.0.1:{$port}/\") == 'OK');\n\tsleep(2);\n\t$server->shutdown();\n}));\n\n$server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) use ($server) {\n    $server->task('12313');\n    $response->end('OK');\n});\n$server->start();\n?>\n--EXPECT--\nstring(5) \"12313\"\nstring(5) \"12313\"\n"
  },
  {
    "path": "tests/swoole_thread/server/create_response.phpt",
    "content": "--TEST--\nswoole_thread/server: create response\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\nconst SIZE = 2 * 1024 * 1024;\n$port = get_constant_port(__FILE__);\n\n$serv = new Swoole\\Http\\Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => 2,\n    'task_worker_num' => 3,\n    'log_level' => SWOOLE_LOG_ERROR,\n    'init_arguments' => function () {\n        global $queue, $atomic1, $atomic2;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic1 = new Swoole\\Thread\\Atomic(0);\n        $atomic2 = new Swoole\\Thread\\Atomic(0);\n        return [$queue, $atomic1, $atomic2];\n    }\n));\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    if ($atomic1->add() == 5) {\n        $queue->push(\"begin\\n\", Thread\\Queue::NOTIFY_ALL);\n    }\n});\n$serv->on('WorkerStop', function (Swoole\\Server $serv, $workerId) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    $atomic2->add();\n});\n$serv->on('Request', function ($req, $resp) use ($serv) {\n    $resp->detach();\n    $serv->task(['fd' => $resp->fd, 'uid' => $req->get['uid']]);\n});\n$serv->on('Task', function ($serv, $task_id, $worker_id, $data) {\n    $response = Swoole\\Http\\Response::create($data['fd']);\n    $response->end($data['uid']);\n    $response->close();\n});\n$serv->on('shutdown', function () {\n    global $queue, $atomic1, $atomic2;\n    Assert::eq($atomic1->get(), 5);\n    Assert::eq($atomic2->get(), 5);\n    echo \"shutdown\\n\";\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    global $port;\n    echo $queue->pop(-1);\n    $reqUid = uniqid();\n    Assert::eq(file_get_contents('http://127.0.0.1:' . $port . '/?uid=' . $reqUid), $reqUid);\n    echo \"done\\n\";\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECTF--\nbegin\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/exit.phpt",
    "content": "--TEST--\nswoole_thread/server: exit\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Http\\Server;\n\nconst CODE = 235;\nconst SIZE = 2 * 1024 * 1024;\n$port = get_constant_port(__FILE__);\n\n$serv = new Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => 2,\n    'log_file' => '/dev/null',\n    'log_level' => SWOOLE_LOG_ERROR,\n    'enable_coroutine' => false,\n    'init_arguments' => function () {\n        global $queue, $atomic1, $atomic2;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic1 = new Swoole\\Thread\\Atomic(0);\n        $atomic2 = new Swoole\\Thread\\Atomic(0);\n        return [$queue, $atomic1, $atomic2];\n    }\n));\n$serv->on('WorkerStart', function (Server $serv, $workerId) use ($port) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    if ($atomic1->add() == 2) {\n        $queue->push(\"begin\\n\", Thread\\Queue::NOTIFY_ALL);\n    }\n    echo 'worker start', PHP_EOL;\n});\n$serv->on('workerError', function (Server $serv, $workerId, $workerPid, $status, $signal) {\n    Assert::eq($status, CODE);\n    echo 'worker error', PHP_EOL;\n});\n$serv->on('WorkerStop', function (Server $serv, $workerId) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n});\n$serv->on('Request', function ($req, $resp) use ($serv) {\n    if ($req->server['request_uri'] == '/exit') {\n        swoole_implicit_fn('bailout', CODE);\n    }\n});\n$serv->on('shutdown', function () {\n    global $queue, $atomic1, $atomic2;\n    echo 'shutdown', PHP_EOL;\n    Assert::eq($atomic1->get(), 3);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    global $port;\n    echo $queue->pop(-1);\n\n    $rs = @file_get_contents('http://127.0.0.1:' . $port . '/exit');\n    Assert::false($rs);\n\n    usleep(200_000);\n    echo \"done\\n\";\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECTF--\nworker start\nworker start\nbegin\nworker error\nworker start\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/fatal_error.phpt",
    "content": "--TEST--\nswoole_thread/server: fatal error\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\nconst SIZE = 2 * 1024 * 1024;\n$port = get_constant_port(__FILE__);\n\n$serv = new Swoole\\Http\\Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => 2,\n    'log_level' => SWOOLE_LOG_ERROR,\n    'log_file' => '/dev/null',\n    'init_arguments' => function () {\n        global $queue, $atomic1, $atomic2;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic1 = new Swoole\\Thread\\Atomic(0);\n        $atomic2 = new Swoole\\Thread\\Atomic(0);\n        return [$queue, $atomic1, $atomic2];\n    }\n));\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    if ($atomic1->add() == 2) {\n        $queue->push(\"begin\\n\", Thread\\Queue::NOTIFY_ALL);\n    }\n});\n$serv->on('WorkerStop', function (Swoole\\Server $serv, $workerId) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    $atomic2->add();\n});\n$serv->on('Request', function ($req, $resp) use ($serv) {\n    if ($req->server['request_uri'] == '/error') {\n        trigger_error('user fatal error', E_USER_ERROR);\n    }\n});\n$serv->on('shutdown', function () {\n    global $queue, $atomic1, $atomic2;\n    echo 'shutdown', PHP_EOL;\n    Assert::eq($atomic1->get(), 3);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    global $port;\n    echo $queue->pop(-1);\n\n    $rs = @file_get_contents('http://127.0.0.1:' . $port . '/error');\n    Assert::false($rs);\n\n    usleep(100_000);\n    echo \"done\\n\";\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECTF--\nbegin\n\nFatal error: user fatal error in %s on line %d\ndone\nshutdown\n\n--EXPECTF_85--\nbegin\n\nFatal error: user fatal error in %s on line %d\nStack trace:\n#0 %s(%d): trigger_error('%s', %d)\n#1 [internal function]: {closure:%s:%d}(Object(Swoole\\Http\\Request), Object(Swoole\\Http\\Response))\n#2 {main}\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/functions.inc",
    "content": "<?php\nfunction thread_server_test_eof_client($port)\n{\n    $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n    $cli->set([\n        'open_eof_check' => true,\n        'package_eof' => \"\\r\\n\",\n    ]);\n    Assert::assert($cli->connect('127.0.0.1', $port, 2));\n    $cli->send(json_encode(['type' => 'eof']) . \"\\r\\n\");\n    Assert::eq($cli->recv(), \"EOF\\r\\n\");\n}\n"
  },
  {
    "path": "tests/swoole_thread/server/heartbeat.phpt",
    "content": "--TEST--\nswoole_thread/server: heartbeat\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\nconst SIZE = 2 * 1024 * 1024;\n$port = get_constant_port(__FILE__);\n\n$serv = new Swoole\\Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => 1,\n    'log_level' => SWOOLE_LOG_ERROR,\n    'heartbeat_check_interval' => 1,\n    'heartbeat_idle_time' => 2,\n    'init_arguments' => function () {\n        global $queue, $atomic;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic = new Swoole\\Thread\\Atomic(1);\n        return [$queue, $atomic];\n    }\n));\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic] = Thread::getArguments();\n    if ($workerId == 0) {\n        $queue->push(\"begin\\n\", Thread\\Queue::NOTIFY_ALL);\n    }\n});\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n});\n$serv->on('shutdown', function () {\n    global $queue, $atomic;\n    echo 'shutdown', PHP_EOL;\n    Assert::eq($atomic->get(), 0);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    global $port;\n    echo $queue->pop(-1);\n\n    $client = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    if (!$client->connect('127.0.0.1', $port, 5, 0)) {\n        echo \"Error: \" . $client->errCode;\n        die(\"\\n\");\n    }\n    $s1 = time();\n    Assert::same(@$client->recv(), '');\n    $s2 = time();\n    Assert::assert($s2 - $s1 > 1);\n\n    $atomic->set(0);\n    echo \"done\\n\";\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECT--\nbegin\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/hook_flags.phpt",
    "content": "--TEST--\nswoole_thread/server: base\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Runtime;\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Queue;\nuse Swoole\\Thread\\Atomic;\n\n$port = get_constant_port(__FILE__);\n\n$serv = new Swoole\\Http\\Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => 4,\n    'log_level' => SWOOLE_LOG_ERROR,\n    'hook_flags' => SWOOLE_HOOK_ALL,\n    'init_arguments' => function () {\n        global $queue, $atomic;\n        $queue = new Queue();\n        $atomic = new Atomic(0);\n        return [$queue, $atomic];\n    }\n));\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic] = Thread::getArguments();\n    Assert::eq(Runtime::getHookFlags(), SWOOLE_HOOK_ALL);\n    $output = file_get_contents(\"http://127.0.0.1:$port/\");\n    $queue->push($output, Queue::NOTIFY_ALL);\n});\n$serv->on('Request', function ($req, $resp) {\n    usleep(100000);\n    $resp->end('DONE');\n});\n$serv->on('shutdown', function ($server) {\n    global $queue, $atomic;\n    echo 'shutdown', PHP_EOL;\n    Assert::eq($atomic->get(), $server->setting['worker_num']);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    for ($i = 0; $i < 4; $i++) {\n        echo $queue->pop(-1), PHP_EOL;\n        $atomic->add(1);\n    }\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECT--\nDONE\nDONE\nDONE\nDONE\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/listen.phpt",
    "content": "--TEST--\nswoole_thread/server: listen\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\nrequire __DIR__ . '/functions.inc';\n\nuse Swoole\\Thread;\n\n$port = get_constant_port(__FILE__);\n\n$serv = new Swoole\\Server('127.0.0.1', $port + 1, SWOOLE_THREAD);\n$port2 = $serv->listen('127.0.0.1', $port, SWOOLE_SOCK_TCP);\n$serv->set(array(\n    'worker_num' => 2,\n    'log_level' => SWOOLE_LOG_ERROR,\n    'open_eof_check' => true,\n    'package_eof' => \"\\r\\n\",\n    'init_arguments' => function () {\n        global $queue, $atomic;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic = new Swoole\\Thread\\Atomic(1);\n        return [$queue, $atomic];\n    }\n));\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic] = Thread::getArguments();\n    if ($workerId == 0) {\n        $queue->push(\"begin\\n\", Thread\\Queue::NOTIFY_ALL);\n    }\n});\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n    $json = json_decode(rtrim($data));\n    if ($json->type == 'eof') {\n        $serv->send($fd, \"EOF\\r\\n\");\n    }\n});\n$serv->on('shutdown', function () {\n    global $queue, $atomic;\n    echo 'shutdown', PHP_EOL;\n    Assert::eq($atomic->get(), 0);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    global $port;\n    echo $queue->pop(-1);\n    Co\\run(function () use ($port) {\n        Co::join([\n            Co\\go(function () use ($port) {\n                thread_server_test_eof_client($port);\n            }),\n            Co\\go(function () use ($port) {\n                thread_server_test_eof_client($port + 1);\n            })\n        ]);\n    });\n    $atomic->set(0);\n    echo \"done\\n\";\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECT--\nbegin\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/manager_timer.phpt",
    "content": "--TEST--\nswoole_thread/server: reload\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Timer;\n\n$port = get_constant_port(__FILE__);\n\n$serv = new Swoole\\Http\\Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => 2,\n    'log_level' => SWOOLE_LOG_ERROR,\n    'log_file' => '/dev/null',\n    'init_arguments' => function () {\n        global $queue, $atomic1, $atomic2;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic1 = new Swoole\\Thread\\Atomic(0);\n        $atomic2 = new Swoole\\Thread\\Atomic(0);\n        return [$queue, $atomic1, $atomic2];\n    }\n));\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    $atomic1->add();\n});\n$serv->on('WorkerStop', function (Swoole\\Server $serv, $workerId) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    $atomic2->add();\n});\n$serv->on('Request', function ($req, $resp) use ($serv) {\n});\n$serv->on('Task', function ($serv, $task_id, $worker_id, $data) {\n});\n$serv->on('managerStart', function ($serv) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    $queue->push(\"begin 1\\n\", Thread\\Queue::NOTIFY_ALL);\n    $count = 0;\n    Timer::tick(100, function () use ($queue, &$count) {\n        $count++;\n        if ($count == 5) {\n            $queue->push(\"begin 2\\n\", Thread\\Queue::NOTIFY_ALL);\n        }\n    });\n});\n$serv->on('shutdown', function () {\n    global $queue, $atomic1, $atomic2;\n    echo 'shutdown', PHP_EOL;\n    Assert::eq($atomic1->get(), 2);\n    Assert::eq($atomic2->get(), 2);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    echo $queue->pop(-1);\n    echo $queue->pop(-1);\n    echo \"done\\n\";\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECT--\nbegin 1\nbegin 2\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/reload.phpt",
    "content": "--TEST--\nswoole_thread/server: reload\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\n$port = get_constant_port(__FILE__);\n\n$serv = new Swoole\\Http\\Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => 3,\n    'task_worker_num' => 4,\n    'log_level' => SWOOLE_LOG_ERROR,\n    'log_file' => '/dev/null',\n    'init_arguments' => function () {\n        global $queue, $atomic1, $atomic2;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic1 = new Swoole\\Thread\\Atomic(0);\n        $atomic2 = new Swoole\\Thread\\Atomic(0);\n        return [$queue, $atomic1, $atomic2];\n    }\n));\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    if ($atomic1->add() == 14) {\n        $queue->push(\"begin 2\\n\", Thread\\Queue::NOTIFY_ALL);\n    }\n});\n$serv->on('WorkerStop', function (Swoole\\Server $serv, $workerId) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    $atomic2->add();\n});\n$serv->on('Request', function ($req, $resp) use ($serv) {\n});\n$serv->on('Task', function ($serv, $task_id, $worker_id, $data) {\n});\n$serv->on('managerStart', function ($serv) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    $queue->push(\"begin 1\\n\", Thread\\Queue::NOTIFY_ALL);\n});\n$serv->on('beforeReload', function ($serv) {\n    echo 'beforeReload', PHP_EOL;\n});\n$serv->on('afterReload', function ($serv) {\n    echo 'afterReload', PHP_EOL;\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n});\n$serv->on('shutdown', function () {\n    global $queue, $atomic1, $atomic2;\n    echo 'shutdown', PHP_EOL;\n    Assert::eq($atomic1->get(), 14);\n    Assert::eq($atomic2->get(), 14);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    echo $queue->pop(-1);\n    $serv->reload();\n    echo $queue->pop(-1);\n    echo \"done\\n\";\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECT--\nbegin 1\nbeforeReload\nafterReload\nbegin 2\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/reload_task_workers.phpt",
    "content": "--TEST--\nswoole_thread/server: reload task workers\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\n$port = get_constant_port(__FILE__);\n\n$serv = new Swoole\\Http\\Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => 3,\n    'task_worker_num' => 4,\n    'log_level' => SWOOLE_LOG_ERROR,\n    'log_file' => '/dev/null',\n    'init_arguments' => function () {\n        global $queue, $atomic1, $atomic2;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic1 = new Swoole\\Thread\\Atomic(0);\n        $atomic2 = new Swoole\\Thread\\Atomic(0);\n        return [$queue, $atomic1, $atomic2];\n    }\n));\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    $c = $atomic1->add();\n    if ($c == 11) {\n        $queue->push(\"begin 2\\n\", Thread\\Queue::NOTIFY_ALL);\n    }\n});\n$serv->on('WorkerStop', function (Swoole\\Server $serv, $workerId) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    $atomic2->add();\n});\n$serv->on('Request', function ($req, $resp) use ($serv) {\n});\n$serv->on('Task', function ($serv, $task_id, $worker_id, $data) {\n});\n$serv->on('managerStart', function ($serv) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    $queue->push(\"begin 1\\n\", Thread\\Queue::NOTIFY_ALL);\n});\n$serv->on('beforeReload', function ($serv) {\n    echo 'beforeReload', PHP_EOL;\n});\n$serv->on('afterReload', function ($serv) {\n    echo 'afterReload', PHP_EOL;\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n});\n$serv->on('shutdown', function () {\n    global $queue, $atomic1, $atomic2;\n    echo 'shutdown', PHP_EOL;\n    Assert::eq($atomic1->get(), 11);\n    Assert::eq($atomic2->get(), 11);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    echo $queue->pop(-1);\n    $serv->reload(true);\n    echo $queue->pop(-1);\n    echo \"done\\n\";\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECT--\nbegin 1\nbeforeReload\nafterReload\nbegin 2\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/reload_task_workers_async.phpt",
    "content": "--TEST--\nswoole_thread/server: reload task workers [async]\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\n$port = get_constant_port(__FILE__);\n\n$serv = new Swoole\\Http\\Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => 3,\n    'task_worker_num' => 4,\n    'log_level' => SWOOLE_LOG_ERROR,\n    'task_enable_coroutine' => true,\n    'log_file' => '/dev/null',\n    'init_arguments' => function () {\n        global $queue, $atomic1, $atomic2;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic1 = new Swoole\\Thread\\Atomic(0);\n        $atomic2 = new Swoole\\Thread\\Atomic(0);\n        return [$queue, $atomic1, $atomic2];\n    }\n));\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    $c = $atomic1->add();\n    if ($c == 11) {\n        $queue->push(\"begin 2\\n\", Thread\\Queue::NOTIFY_ALL);\n    }\n});\n$serv->on('WorkerStop', function (Swoole\\Server $serv, $workerId) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    $atomic2->add();\n});\n$serv->on('Request', function ($req, $resp) use ($serv) {\n});\n$serv->on('Task', function ($serv, $task_id, $worker_id, $data) {\n});\n$serv->on('managerStart', function ($serv) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    $queue->push(\"begin 1\\n\", Thread\\Queue::NOTIFY_ALL);\n});\n$serv->on('beforeReload', function ($serv) {\n    echo 'beforeReload', PHP_EOL;\n});\n$serv->on('afterReload', function ($serv) {\n    echo 'afterReload', PHP_EOL;\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n});\n$serv->on('shutdown', function () {\n    global $queue, $atomic1, $atomic2;\n    echo 'shutdown', PHP_EOL;\n    Assert::eq($atomic1->get(), 11);\n    Assert::eq($atomic2->get(), 11);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    echo $queue->pop(-1);\n    $serv->reload(true);\n    echo $queue->pop(-1);\n    echo \"done\\n\";\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECT--\nbegin 1\nbeforeReload\nafterReload\nbegin 2\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/reset_concurrency.phpt",
    "content": "--TEST--\nswoole_thread/server: reset concurrency [SWOOLE_THREAD]\n--SKIPIF--\n<?php require __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\Thread\\Atomic;\nuse Swoole\\Thread;\nuse function Swoole\\Coroutine\\run;\n\nconst N = 64;\nconst WORKER_NUM = 4;\n\n$port = get_constant_port(__FILE__);\n\n$serv = new Swoole\\Http\\Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => WORKER_NUM,\n    'max_concurrency' => 160,\n    'log_level' => SWOOLE_LOG_ERROR,\n    'log_file' => '/dev/null',\n    'init_arguments' => function () {\n        global $queue, $atomic1, $atomic2;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic1 = new Swoole\\Thread\\Atomic(0);\n        $atomic2 = new Swoole\\Thread\\Atomic(0);\n        return [$queue, $atomic1, $atomic2];\n    }\n));\n$serv->on('WorkerStart', function (Server $serv, $workerId) use ($port) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    if ($atomic1->add() == WORKER_NUM) {\n        $queue->push(\"begin\\n\", Thread\\Queue::NOTIFY_ALL);\n    }\n});\n$serv->on('WorkerStop', function (Server $serv, $workerId) {\n    echo 'WORKER STOP', PHP_EOL;\n});\n$serv->on('pipeMessage', function (Server $serv, $wid, $msg) {\n    swoole_implicit_fn('bailout');\n});\n$serv->on('Request', function (Request $req, Response $resp) use ($serv) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    $c = $atomic2->add();\n    if ($c < N) {\n        Co::sleep(100);\n    } elseif ($c == N) {\n        $stats = $serv->stats();\n        Assert::eq($stats['concurrency'], N);\n        $wid = $serv->getWorkerId();\n        for ($i = 0; $i < WORKER_NUM; $i++) {\n            if ($i !== $wid) {\n                $serv->sendMessage('error', $i);\n            }\n        }\n        swoole_implicit_fn('bailout');\n    } else {\n        $stats = $serv->stats();\n        Assert::eq($stats['concurrency'], 1);\n        $resp->end(json_encode($stats));\n    }\n});\n$serv->on('shutdown', function () {\n    global $queue, $atomic1, $atomic2;\n    echo 'SHUTDOWN', PHP_EOL;\n    Assert::eq($atomic1->get(), WORKER_NUM * 2);\n    Assert::eq($atomic2->get(), N + 1);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv, $port) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    $queue->pop(-1);\n    run(function () use ($port, $serv, $atomic1, $queue) {\n        $n = N;\n        $coroutines = [];\n        while ($n--) {\n            $coroutines[] = go(function () use ($port) {\n                $client = new Client('127.0.0.1', $port);\n                $client->set(['timeout' => 10]);\n                Assert::eq($client->get('/'), false);\n                Assert::eq($client->getStatusCode(), SWOOLE_HTTP_CLIENT_ESTATUS_SERVER_RESET);\n            });\n        }\n\n        Co::join($coroutines);\n\n        while (1) {\n            if ($atomic1->get() == WORKER_NUM * 2) {\n                break;\n            }\n            Co::sleep(0.1);\n        }\n\n        $client = new Client('127.0.0.1', $port);\n        Assert::assert($client->get('/'));\n        $stats = json_decode($client->getBody());\n        Assert::eq($stats->concurrency, 1);\n        $serv->shutdown();\n\n        echo \"DONE\\n\";\n    });\n}));\n$serv->start();\n?>\n--EXPECT--\nDONE\nWORKER STOP\nWORKER STOP\nWORKER STOP\nWORKER STOP\nSHUTDOWN\n"
  },
  {
    "path": "tests/swoole_thread/server/send_in_user_process.phpt",
    "content": "--TEST--\nswoole_thread/server: send in user process\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\nconst SIZE = 2 * 1024 * 1024;\n$port = get_constant_port(__FILE__);\n\n$serv = new Swoole\\Http\\Server('127.0.0.1', $port, SWOOLE_THREAD);\n\n$proc = new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    global $port;\n    echo $queue->pop(-1);\n    $reqUid = uniqid();\n    Assert::eq(file_get_contents('http://127.0.0.1:' . $port . '/?uid=' . $reqUid), $reqUid);\n    echo \"done\\n\";\n    $serv->shutdown();\n});\n$serv->addProcess($proc);\n\n$proc2 = new Swoole\\Process(function ($process) use ($serv) {\n    $json = $process->read();\n    $data = json_decode($json, true);\n    $response = Swoole\\Http\\Response::create($data['fd']);\n    $response->end($data['uid']);\n    $response->close();\n});\n$serv->addProcess($proc2);\n\n$serv->set(array(\n    'worker_num' => 1,\n    'log_level' => SWOOLE_LOG_ERROR,\n    'init_arguments' => function () {\n        global $queue, $atomic;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic = new Swoole\\Thread\\Atomic(0);\n        return [$queue, $atomic];\n    }\n));\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic] = Thread::getArguments();\n    $atomic->add();\n    $queue->push(\"begin\\n\", Thread\\Queue::NOTIFY_ALL);\n});\n$serv->on('WorkerStop', function (Swoole\\Server $serv, $workerId) {\n    [$queue, $atomic] = Thread::getArguments();\n    $atomic->add();\n});\n$serv->on('Request', function ($req, $resp) use ($serv, $proc2) {\n    $resp->detach();\n    $proc2->write(json_encode(['fd' => $resp->fd, 'uid' => $req->get['uid']]));\n});\n$serv->on('shutdown', function () {\n    global $queue, $atomic;\n    Assert::eq($atomic->get(), 2);\n    echo \"shutdown\\n\";\n});\n\n$serv->start();\n?>\n--EXPECT--\nbegin\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/send_large_packet.phpt",
    "content": "--TEST--\nswoole_thread/server: send large packet\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\nconst SIZE = 2 * 1024 * 1024;\n$port = get_constant_port(__FILE__);\n\n$serv = new Swoole\\Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => 2,\n    'log_level' => SWOOLE_LOG_ERROR,\n    'open_length_check' => true,\n    'package_max_length' => 4 * 1024 * 1024,\n    'package_length_type' => 'N',\n    'package_length_offset' => 0,\n    'package_body_offset' => 4,\n    'init_arguments' => function () {\n        global $queue, $atomic;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic = new Swoole\\Thread\\Atomic(1);\n        return [$queue, $atomic];\n    }\n));\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic] = Thread::getArguments();\n    if ($workerId == 0) {\n        $queue->push(\"begin\\n\", Thread\\Queue::NOTIFY_ALL);\n    }\n});\n$serv->on(\"WorkerStop\", function (Swoole\\Server $serv, $workerId) {\n});\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n    $send_data = str_repeat('A', SIZE - 12) . substr($data, -8, 8);\n    $serv->send($fd, pack('N', strlen($send_data)) . $send_data);\n});\n$serv->on('shutdown', function () {\n    global $queue, $atomic;\n    echo 'shutdown', PHP_EOL;\n    Assert::eq($atomic->get(), 0);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    global $port;\n    echo $queue->pop(-1);\n\n    $c = MAX_CONCURRENCY_LOW;\n    $n = MAX_REQUESTS_LOW;\n\n    for ($i = 0; $i < $c; $i++) {\n        go(function () use ($i, $n, $atomic, $port) {\n            $cli = new Co\\Client(SWOOLE_SOCK_TCP);\n            $cli->set([\n                'open_length_check' => true,\n                'package_max_length' => 4 * 1024 * 1024,\n                'package_length_type' => 'N',\n                'package_length_offset' => 0,\n                'package_body_offset' => 4,\n            ]);\n            if ($cli->connect('127.0.0.1', $port, 2) == false) {\n                echo \"ERROR\\n\";\n                return;\n            }\n            for ($i = 0; $i < $n; $i++) {\n                $sid = strval(rand(10000000, 99999999));\n                $send_data = str_repeat('A', 1000) . $sid;\n                $cli->send(pack('N', strlen($send_data)) . $send_data);\n                $data = $cli->recv();\n                Assert::same(strlen($data), SIZE);\n                Assert::same($sid, substr($data, -8, 8));\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    $atomic->set(0);\n    echo \"done\\n\";\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECT--\nbegin\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/setting.phpt",
    "content": "--TEST--\nswoole_thread/server: setting\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\n$port = get_constant_port(__FILE__);\nconst N = 4;\n\n$serv = new Swoole\\Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => N,\n    'log_level' => SWOOLE_LOG_ERROR,\n    'init_arguments' => function () {\n        global $queue, $atomic;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic = new Swoole\\Thread\\Atomic(0);\n        return [$queue, $atomic];\n    }\n));\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic] = Thread::getArguments();\n    Assert::isArray($serv->setting);\n    if ($atomic->add(1) == N) {\n        $queue->push(\"begin\\n\", Thread\\Queue::NOTIFY_ALL);\n    }\n});\n$serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n});\n$serv->on('shutdown', function () {\n    global $queue, $atomic;\n    echo 'shutdown', PHP_EOL;\n    Assert::eq($atomic->get(), N);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    echo $queue->pop(-1);\n    echo \"done\\n\";\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECT--\nbegin\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/stop_worker.phpt",
    "content": "--TEST--\nswoole_thread/server: stop worker\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\n$port = get_constant_port(__FILE__);\n\n$serv = new Swoole\\Http\\Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => 2,\n    'task_worker_num' => 3,\n    'log_level' => SWOOLE_LOG_ERROR,\n    'init_arguments' => function () {\n        global $queue, $atomic1, $atomic2;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic1 = new Swoole\\Thread\\Atomic(0);\n        $atomic2 = new Swoole\\Thread\\Atomic(0);\n        return [$queue, $atomic1, $atomic2];\n    }\n));\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    if ($atomic1->add() == 5) {\n        $queue->push(\"begin\\n\", Thread\\Queue::NOTIFY_ALL);\n    }\n});\n$serv->on('WorkerStop', function (Swoole\\Server $serv, $workerId) {\n    [$queue, $atomic1, $atomic2] = Thread::getArguments();\n    $atomic2->add();\n});\n$serv->on('Request', function ($req, $resp) use ($serv) {\n    if ($req->server['request_uri'] == '/stop') {\n        $serv->stop($req->get['worker'] ?? 0);\n        $resp->end(\"OK\\n\");\n    }\n});\n$serv->on('Task', function ($serv, $task_id, $worker_id, $data) {\n\n});\n$serv->on('shutdown', function () {\n    global $queue, $atomic1, $atomic2;\n    echo 'shutdown', PHP_EOL;\n    Assert::eq($atomic1->get(), 7);\n    Assert::eq($atomic2->get(), 7);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    global $port;\n    echo $queue->pop(-1);\n\n    echo file_get_contents('http://127.0.0.1:' . $port . '/stop?worker=' . random_int(0, 1));\n    echo file_get_contents('http://127.0.0.1:' . $port . '/stop?worker=' . random_int(2, 4));\n\n    sleep(1);\n    echo \"done\\n\";\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECTF--\nbegin\nOK\nOK\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/taskCo.phpt",
    "content": "--TEST--\nswoole_thread/server: taskCo\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$port = get_constant_port(__FILE__);\n\n$serv = new Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => 2,\n    'task_worker_num' => 2,\n    'enable_coroutine' => true,\n    'init_arguments' => function () {\n        global $queue, $atomic;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic = new Swoole\\Thread\\Atomic(1);\n        return [$queue, $atomic];\n    }\n));\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic] = Thread::getArguments();\n    if ($workerId == 0) {\n        $queue->push(\"begin\\n\", Thread\\Queue::NOTIFY_ALL);\n    }\n});\n$serv->on('task',\n    function (Server $server, int $task_id, int $src_worker_id, mixed $data) {\n        $server->finish($data * 7);\n    }\n);\n$serv->on('Request',\n    function (Request $request, Response $response) use ($serv) {\n        $tasks = range(1, 16);\n        $result = $serv->taskCo($tasks);\n        $response->header('Content-Type', 'application/json');\n        $response->end(json_encode([\"count\" => count($result)]));\n    }\n);\n$serv->on('shutdown', function () {\n    global $queue, $atomic;\n    echo 'shutdown', PHP_EOL;\n    Assert::eq($atomic->get(), 0);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    global $port;\n    echo $queue->pop(-1);\n    Co\\run(function () use ($port) {\n        echo httpGetBody(\"http://127.0.0.1:{$port}/\") . PHP_EOL;\n    });\n    $atomic->set(0);\n    echo \"done\\n\";\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECT--\nbegin\n{\"count\":16}\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/taskWaitMulti.phpt",
    "content": "--TEST--\nswoole_thread/server: taskWaitMulti\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Http\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$port = get_constant_port(__FILE__);\n\n$serv = new Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => 2,\n    'task_worker_num' => 2,\n    'enable_coroutine' => false,\n    'init_arguments' => function () {\n        global $queue, $atomic;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic = new Swoole\\Thread\\Atomic(1);\n        return [$queue, $atomic];\n    }\n));\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic] = Thread::getArguments();\n    if ($workerId == 0) {\n        $queue->push(\"begin\\n\", Thread\\Queue::NOTIFY_ALL);\n    }\n});\n\n\n$serv->on('task',\n    function (Server $server, int $task_id, int $src_worker_id, mixed $data) {\n        $server->finish($data * 7);\n    }\n);\n\n$serv->on('Request',\n    function (Request $request, Response $response) use ($serv) {\n        $tasks = [9999, 8888];\n        $result = $serv->taskWaitMulti($tasks);\n        $response->header('Content-Type', 'application/json');\n        $_rs = array_values($result);\n        sort($_rs);\n        $response->end(json_encode([\"data\" => $_rs]));\n    }\n);\n$serv->on('shutdown', function () {\n    global $queue, $atomic;\n    echo 'shutdown', PHP_EOL;\n    Assert::eq($atomic->get(), 0);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    global $port;\n    echo $queue->pop(-1);\n    Co\\run(function () use ($port) {\n        echo httpGetBody(\"http://127.0.0.1:{$port}/\") . PHP_EOL;\n    });\n    $atomic->set(0);\n    echo \"done\\n\";\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECT--\nbegin\n{\"data\":[62216,69993]}\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/udp_port.phpt",
    "content": "--TEST--\nswoole_thread/server: listen udp port\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Timer;\nuse Swoole\\WebSocket\\Server;\n\n$port = get_constant_port(__FILE__);\n\n$serv = new Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => 2,\n    'log_level' => SWOOLE_LOG_ERROR,\n    'reload_async' => true,\n    'init_arguments' => function () {\n        global $queue, $atomic;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic = new Swoole\\Thread\\Atomic(0);\n        return [$queue, $atomic];\n    }\n));\n$udp = $serv->addListener('127.0.0.1', $port + 1, SWOOLE_SOCK_UDP);\n$udp->on('packet', function ($serv, $data, $addr) {\n    echo \"udp packet\\n\";\n    $serv->sendto($addr['address'], $addr['port'], $data);\n});\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic] = Thread::getArguments();\n    if ($atomic->add() == 1) {\n        $queue->push(\"begin\\n\", Thread\\Queue::NOTIFY_ALL);\n    }\n    echo \"worker start\\n\";\n});\n$serv->on('message', function (Server $server, $frame) {\n    echo \"message\\n\";\n});\n$serv->on('workerExit', function (Server $server, $wid) {\n    var_dump('worker exit: ' . $wid);\n    Timer::clearAll();\n});\n$serv->on('shutdown', function (Server $server) {\n    global $queue, $atomic;\n    echo 'shutdown', PHP_EOL;\n    Assert::eq($atomic->get(), $server->setting['worker_num']);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    global $port;\n    echo $queue->pop(-1);\n    Co\\run(function () use ($port) {\n        $udp_sock = stream_socket_client('udp://127.0.0.1:' . ($port + 1), $errno, $errstr);\n        $pkt = random_bytes(1024);\n        fwrite($udp_sock, $pkt);\n        $data = fread($udp_sock, 1024);\n        Assert::eq($pkt, $data);\n    });\n    echo \"done\\n\";\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECT--\nworker start\nworker start\nbegin\nudp packet\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/server/websocket.phpt",
    "content": "--TEST--\nswoole_thread/server: websocket\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\WebSocket\\Server;\n\n$port = get_constant_port(__FILE__);\n\n$serv = new Server('127.0.0.1', $port, SWOOLE_THREAD);\n$serv->set(array(\n    'worker_num' => 2,\n    'log_level' => SWOOLE_LOG_ERROR,\n    'init_arguments' => function () {\n        global $queue, $atomic;\n        $queue = new Swoole\\Thread\\Queue();\n        $atomic = new Swoole\\Thread\\Atomic(1);\n        return [$queue, $atomic];\n    }\n));\n$serv->on('WorkerStart', function (Swoole\\Server $serv, $workerId) use ($port) {\n    [$queue, $atomic] = Thread::getArguments();\n    if ($workerId == 0) {\n        $queue->push(\"begin\\n\", Thread\\Queue::NOTIFY_ALL);\n    }\n});\n$serv->on('message', function (Server $server, $frame) {\n    $server->push($frame->fd, $frame->data);\n});\n$serv->on('shutdown', function () {\n    global $queue, $atomic;\n    echo 'shutdown', PHP_EOL;\n    Assert::eq($atomic->get(), 0);\n});\n$serv->addProcess(new Swoole\\Process(function ($process) use ($serv) {\n    [$queue, $atomic] = Thread::getArguments();\n    global $port;\n    echo $queue->pop(-1);\n    Co\\run(function () use ($port) {\n        $cli = new Co\\Http\\Client('127.0.0.1', $port);\n        $data = base64_decode(random_bytes(2048));\n        Assert::assert($cli->upgrade('/'));\n        $cli->push($data);\n        $frame = $cli->recv();\n        Assert::eq($frame->data, $data);\n    });\n    $atomic->set(0);\n    echo \"done\\n\";\n    $serv->shutdown();\n}));\n$serv->start();\n?>\n--EXPECT--\nbegin\ndone\nshutdown\n"
  },
  {
    "path": "tests/swoole_thread/shell_exec.phpt",
    "content": "--TEST--\nswoole_thread: lock\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Lock;\nuse Swoole\\Runtime;\nuse SwooleTest\\ThreadManager;\n\n$tm = new ThreadManager();\n\n$tm->parentFunc = function () {\n    $lock = new Lock;\n    $lock->lock();\n    $thread = new Thread(__FILE__, $lock);\n    $lock->unlock();\n    $thread->join();\n    Assert::eq($thread->getExitStatus(), 0);\n    echo 'DONE' . PHP_EOL;\n};\n\n$tm->childFunc = function ($lock) {\n    $lock->lock();\n    usleep(100_000);\n    Co\\run(function () {\n        shell_exec('ls /tmp');\n        sleep(1);\n        gethostbyname('www.baidu.com');\n    });\n    exit(0);\n};\n\n$tm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_thread/signal.phpt",
    "content": "--TEST--\nswoole_thread: signal\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Process;\nuse Swoole\\Thread;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Timer;\n\n$args = Thread::getArguments();\n\nif (empty($args)) {\n    Co\\run(function () {\n        $sockets = swoole_coroutine_socketpair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);\n        $thread = new Thread(__FILE__, $sockets[0]);\n        $parent_pipe = $sockets[1];\n        Timer::after(500, function () {\n            echo \"timer\\n\";\n            Process::kill(posix_getpid(), SIGTERM);\n        });\n        // 收到信号之后向子线程发送指令让子线程退出\n        if (System::waitSignal(SIGTERM)) {\n            echo \"signal term\\n\";\n            $parent_pipe->send('exit');\n        }\n        Co\\go(function () use ($parent_pipe, $thread) {\n            // 从管道中读取子线程退出的信息\n            echo $parent_pipe->recv(8192), PHP_EOL;\n            // 回收子线程\n            $thread->join();\n        });\n    });\n} else {\n    $child_pipe = $args[0];\n    Co\\run(function () use ($child_pipe) {\n        // 收到父线程的指令，开始退出\n        echo $child_pipe->recv(8192), PHP_EOL;\n        // 通知父线程已退出\n        $child_pipe->send('child exit');\n    });\n    exit(0);\n}\n?>\n--EXPECTF--\ntimer\nsignal term\nexit\nchild exit\n"
  },
  {
    "path": "tests/swoole_thread/sort.phpt",
    "content": "--TEST--\nswoole_thread: sort\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread\\Map;\nuse Swoole\\Thread\\ArrayList;\n\n$original_map = array(\n    \"l\" => \"lemon\",\n    \"o\" => \"orange\",\n    \"O\" => \"Orange\",\n    \"O1\" => \"Orange1\",\n    \"o2\" => \"orange2\",\n    \"O3\" => \"Orange3\",\n    \"o20\" => \"orange20\",\n    \"b\" => \"banana\",\n);\n\n$unsorted_map = new Map($original_map);\n$unsorted_map->sort();\n\n$copied_map = $original_map;\nasort($copied_map);\nAssert::eq($unsorted_map->toArray(), $copied_map);\n\n$original_list =  array( 100, 33, 555, 22 );\n$copied_list = $original_list;\n\n$unsorted_list = new ArrayList($original_list);\n$unsorted_list->sort();\nsort($copied_list);\nAssert::eq($unsorted_list->toArray(), $copied_list);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_thread/stdio.phpt",
    "content": "--TEST--\nswoole_thread: stdio\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Lock;\n\n$tm = new \\SwooleTest\\ThreadManager();\n\n$tm->parentFunc = function () {\n    $lock = new Lock;\n    $lock->lock();\n    $thread = new Thread(__FILE__, $lock);\n    $lock->lock();\n    $thread->join();\n    echo \"main thread\\n\";\n};\n\n$tm->childFunc = function ($lock) {\n    echo \"child thread\\n\";\n    usleep(200_000);\n    $lock->unlock();\n    fwrite(STDOUT, \"hello swoole\\n\");\n    Assert::notEmpty(STDIN);\n    exit(0);\n};\n\n$tm->run();\necho \"DONE\\n\";\n?>\n--EXPECTF--\nchild thread\nhello swoole\nmain thread\nDONE\n"
  },
  {
    "path": "tests/swoole_thread/stream.phpt",
    "content": "--TEST--\nswoole_thread: stream\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Queue;\n\n$tm = new \\SwooleTest\\ThreadManager();\n$tm->initFreePorts(increment: crc32(__FILE__) % 1000);\n\n$tm->parentFunc = function () use ($tm) {\n    $queue = new Queue();\n    $fp = stream_socket_server('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr);\n    $queue->push($fp);\n    $thread = new Thread(__FILE__, $queue, 0);\n    var_dump('main thread');\n    $thread->join();\n};\n\n$tm->childFunc = function ($queue, $id) use ($tm) {\n    if ($id === 0) {\n        var_dump('child thread 0');\n        $fp = $queue->pop();\n        $thread = new Thread(__FILE__, $queue, 1);\n        $conn = stream_socket_accept($fp, -1);\n        fwrite($conn, \"hello world\\n\");\n        fclose($conn);\n        fclose($fp);\n        $thread->join();\n    } else {\n        var_dump('child thread 1');\n        $client = stream_socket_client('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr);\n        Assert::notEmpty($client);\n        $data = fread($client, 8192);\n        Assert::eq($data, \"hello world\\n\");\n        fclose($client);\n    }\n};\n\n$tm->run();\n?>\n--EXPECT--\nstring(11) \"main thread\"\nstring(14) \"child thread 0\"\nstring(14) \"child thread 1\"\n"
  },
  {
    "path": "tests/swoole_thread/stream_arg.phpt",
    "content": "--TEST--\nswoole_thread: stream as a thread argument\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\nuse Swoole\\Thread\\Queue;\n\n$tm = new \\SwooleTest\\ThreadManager();\n$tm->initFreePorts(increment: crc32(__FILE__) % 1000);\n\n$tm->parentFunc = function () use ($tm) {\n    $fp = stream_socket_server('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr);\n    $thread = new Thread(__FILE__, $fp, 0);\n    var_dump('main thread');\n    $thread->join();\n};\n\n$tm->childFunc = function ($fp, $id) use ($tm) {\n    if ($id === 0) {\n        var_dump('child thread 0');\n        $thread = new Thread(__FILE__, $fp, 1);\n        $conn = stream_socket_accept($fp, -1);\n        fwrite($conn, \"hello world\\n\");\n        fclose($conn);\n        fclose($fp);\n        $thread->join();\n    } else {\n        var_dump('child thread 1');\n        $client = stream_socket_client('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr);\n        Assert::notEmpty($client);\n        $data = fread($client, 8192);\n        Assert::eq($data, \"hello world\\n\");\n        fclose($client);\n    }\n};\n\n$tm->run();\n?>\n--EXPECT--\nstring(11) \"main thread\"\nstring(14) \"child thread 0\"\nstring(14) \"child thread 1\"\n"
  },
  {
    "path": "tests/swoole_thread/thread_status.phpt",
    "content": "--TEST--\nswoole_thread: thread status\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\n$t1 = new Thread(TESTS_API_PATH . '/swoole_thread/sleep.php');\nusleep(10);\nAssert::true($t1->joinable());\nAssert::true($t1->isAlive());\nAssert::true($t1->join());\nAssert::false($t1->joinable());\nAssert::false($t1->isAlive());\n\n$t2 = new Thread(TESTS_API_PATH . '/swoole_thread/sleep.php');\n$t2->detach();\nusleep(10);\nAssert::false($t2->joinable());\nAssert::true($t2->isAlive());\nwhile (Thread::activeCount() > 1) {\n    usleep(10);\n}\nAssert::false($t2->isAlive());\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_thread/yield.phpt",
    "content": "--TEST--\nswoole_thread: thread status\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_nts();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Thread;\n\n$t1 = new Thread(TESTS_API_PATH . '/swoole_thread/sleep.php');\nAssert::false($t1->isAlive());\n$t1->detach();\nThread::yield();\nusleep(10);\nAssert::true($t1->isAlive());\nwhile (Thread::activeCount() > 1) {\n    usleep(10);\n}\nAssert::false($t1->isAlive());\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_timer/after.phpt",
    "content": "--TEST--\nswoole_timer: swoole_timer_after,swoole_timer_exists,swoole_timer_clear\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nclass TimerTest {\n    public static $count = 0;\n    private $timer_id = null;\n\n    protected function resetTimer($ms) {\n        if ($this->timer_id && Swoole\\Timer::exists($this->timer_id)) {\n            Swoole\\Timer::clear($this->timer_id);\n            $this->timer_id = null;\n        }\n        if (self::$count == 10) {\n            return;\n        }\n        $this->timer_id = Swoole\\Timer::after($ms, array($this, 'onTimerTick'));\n        Assert::assert($this->timer_id > 0);\n    }\n\n    public function onTimerTick() {\n        self::$count++;\n        echo \"onTimerTick:\" . self::$count . \"\\n\";\n        $this->resetTimer(10);\n    }\n}\n\n$timer_test = new TimerTest();\n$timer_test->onTimerTick();\n\n?>\n--EXPECT--\nonTimerTick:1\nonTimerTick:2\nonTimerTick:3\nonTimerTick:4\nonTimerTick:5\nonTimerTick:6\nonTimerTick:7\nonTimerTick:8\nonTimerTick:9\nonTimerTick:10\n"
  },
  {
    "path": "tests/swoole_timer/after_fork.phpt",
    "content": "--TEST--\nswoole_timer: after fork\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$id = Swoole\\Timer::after(1, function () { echo 'never here' . PHP_EOL; });\nif (Assert::greaterThan($id, 0)) {\n    $process = new Swoole\\Process(function () use ($id) {\n        // timer will be removed before fork\n        Assert::false(Swoole\\Timer::exists($id));\n        echo \"DONE\\n\";\n    });\n    $process->start();\n    $process::wait();\n    Assert::true(Swoole\\Timer::clear($id));\n}\nSwoole\\Event::wait();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_timer/bug_2342.phpt",
    "content": "--TEST--\nswoole_timer: bug Github#2342\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nclass workerInfo\n{\n    public $data;\n    public function __construct() {\n        $this->data = str_repeat('A', 1024 * 1024 * 1);\n    }\n}\n\nfunction worker($timerId, $info)\n{\n    Swoole\\Timer::clear($timerId);\n}\nfunction manager($timerID)\n{\n    Swoole\\Timer::tick( 10, 'worker', new workerInfo());\n}\n$mem = memory_get_usage();\n$timerId = Swoole\\Timer::tick(50, 'manager');\nSwoole\\Timer::after(500, function()use($timerId){\n    Swoole\\Timer::clear($timerId);\n});\nSwoole\\Event::wait();\nAssert::assert($mem + 1024 * 1024 * 1 > memory_get_usage());\necho \"DONE\\n\";\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_timer/bug_4794.phpt",
    "content": "--TEST--\nswoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\Channel;\nuse function Swoole\\Coroutine\\run;\nuse function Swoole\\Coroutine\\go;\n\nrun(function(){\n    $channel = new Channel(1);\n    go(function () use ($channel) {\n        $channel->push(['rand' => 9999]);\n    });\n    go(function () use ($channel) {\n        $data = $channel->pop(0.00001);\n        var_dump($data);\n    });\n});\n?>\n--EXPECT--\narray(1) {\n  [\"rand\"]=>\n  int(9999)\n}\n"
  },
  {
    "path": "tests/swoole_timer/bug_4794_2.phpt",
    "content": "--TEST--\nswoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Process;\n\n$atomic = new Atomic;\n\n$processFast = new Process(function () {\n    usleep(1000);\n});\n$processFast->start();\n\n$processSlow = new Process(function () use ($atomic) {\n    $atomic->wait(10);\n    usleep(10 * 1000);\n});\n$processSlow->start();\n\nCoroutine\\run(function () use ($processFast, $processSlow, $atomic) {\n    for ($n = MAX_REQUESTS; $n--;) {\n        $status = System::waitPid($processSlow->pid, 0.0001);\n        Assert::false($status);\n        Assert::same(swoole_last_error(), SOCKET_ETIMEDOUT);\n    }\n    $atomic->wakeup();\n    $status = System::waitPid($processSlow->pid, 1);\n    Assert::same($status['pid'], $processSlow->pid);\n    var_dump($status);\n    $status = System::waitPid($processFast->pid);\n    Assert::same($status['pid'], $processFast->pid);\n    var_dump($status);\n});\n\n?>\n--EXPECTF--\narray(3) {\n  [\"pid\"]=>\n  int(%d)\n  [\"code\"]=>\n  int(0)\n  [\"signal\"]=>\n  int(0)\n}\narray(3) {\n  [\"pid\"]=>\n  int(%d)\n  [\"code\"]=>\n  int(0)\n  [\"signal\"]=>\n  int(0)\n}\n"
  },
  {
    "path": "tests/swoole_timer/bug_4794_3.phpt",
    "content": "--TEST--\nswoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc';\nskip_if_offline();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine;\n\nCoroutine\\run(function () {\n    $client = stream_socket_client('tcp://www.qq.com:80', $errno, $errstr, 30);\n    $events = Coroutine::waitEvent($client, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE, 0.0001);\n    Assert::eq($events, SWOOLE_EVENT_WRITE);\n    fwrite($client, \"GET / HTTP/1.1\\r\\nHost: www.qq.com\\r\\n\\r\\n\");\n    $events = Coroutine::waitEvent($client, SWOOLE_EVENT_READ);\n    Assert::eq($events, SWOOLE_EVENT_READ);\n    $response = fread($client, 8192);\n    Assert::contains($response, 'www.qq.com');\n});\n\necho \"DONE\\n\";\n\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_timer/bug_4794_4.phpt",
    "content": "--TEST--\nswoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Atomic;\nuse Swoole\\Coroutine;\nuse Swoole\\Coroutine\\System;\nuse Swoole\\Process;\n\n$atomic = new Atomic;\n\n$pid = getmypid();\n$killer = new Process(function () use ($pid, $atomic) {\n    $atomic->wait();\n    echo \"2\\n\";\n    switch_process();\n    Process::kill($pid, SIGUSR1);\n    $atomic->wait();\n    echo \"6\\n\";\n    switch_process();\n    Process::kill($pid, SIGUSR2);\n    echo \"8\\n\";\n});\n$killer->start();\n\nCoroutine\\run(function () use ($atomic) {\n    Coroutine::sleep(0.001);\n    switch_process();\n    $atomic->wakeup();\n    echo \"1\\n\";\n    Assert::eq(System::waitSignal(SIGUSR1), SIGUSR1);\n    echo \"3\\n\";\n    Assert::false(System::waitSignal(SIGUSR2, 0.0001));\n    echo \"4\\n\";\n    $atomic->wakeup();\n    echo \"5\\n\";\n    Assert::eq(System::waitSignal(SIGUSR2), SIGUSR2);\n    echo \"7\\n\";\n    System::wait(0.0001);\n    echo \"9\\n\";\n});\n\n?>\n--EXPECT--\n1\n2\n3\n4\n5\n6\n8\n7\n9\n"
  },
  {
    "path": "tests/swoole_timer/bug_4794_5.phpt",
    "content": "--TEST--\nswoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\nuse function Swoole\\Coroutine\\run;\n\nrun(function () {\n    $host = Swoole\\Coroutine::dnsLookup('www.' . uniqid() . '.' . uniqid(), 0.005);\n    Assert::eq($host, false);\n    Assert::eq(swoole_last_error(), SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED);\n});\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_timer/bug_4794_6.phpt",
    "content": "--TEST--\nswoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\System;\n\nCo\\run(\n    function () {\n        $conn = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $conn->connect('www.baidu.com', 80);\n        $info = $conn->getpeername();\n        Assert::eq($info['host'], System::gethostbyname('www.baidu.com'), 'AF_INET', 0.0001);\n        Assert::eq($info['port'], 80);\n    }\n);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_timer/bug_4794_7.phpt",
    "content": "--TEST--\nswoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid\n--SKIPIF--\n<?php require  __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nini_set('swoole.display_errors', 'off');\n\n$cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n$r = $cli->connect(\"11.11.11.11\", 80, 0.0005);\nAssert::false($r);\nAssert::eq($cli->errCode, SOCKET_ETIMEDOUT);\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_timer/bug_4794_8.phpt",
    "content": "--TEST--\nswoole_timer: #4794 Timer::add() (ERRNO 505): msec value[0] is invalid\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ngo(function () {\n    $sock = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    Assert::assert($sock->bind('127.0.0.1', 9601));\n    Assert::assert($sock->listen(512));\n    $conn = $sock->accept(0.0001);\n    Assert::assert($conn);\n    Assert::isInstanceOf($conn, Swoole\\Coroutine\\Socket::class);\n\n    $data = $conn->recv(0.0001);\n    $json = json_decode($data, true);\n    Assert::same($json['data'] ?? '', 'hello');\n    $conn->send(\"world\\n\");\n    $conn->close();\n});\n\ngo(function ()  {\n    $conn = new Swoole\\Coroutine\\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    Assert::assert($conn->connect('127.0.0.1', 9601));\n    $conn->send(json_encode(['data' => 'hello']));\n    echo $conn->recv();\n});\n?>\n--EXPECT--\nworld\n"
  },
  {
    "path": "tests/swoole_timer/call_private.phpt",
    "content": "--TEST--\nswoole_timer: call private method\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nclass Test\n{\n    private static function foo() { }\n\n    private function bar() { }\n}\n\n// method not exists\n//------------------------------------------------------------------------------------------------------------------\n$pm = ProcessManager::exec(function () {\n    Swoole\\Timer::After(1, [Test::class, 'not_exist']);\n});\n$pm->expectExitCode(255);\n$output = $pm->getChildOutput();\nif (PHP_VERSION_ID < 80000) {\n    Assert::contains($output, 'Uncaught TypeError: Argument 2 passed to Swoole\\Timer::after() must be callable, array given');\n} else {\n    Assert::contains($output, 'Uncaught TypeError: Swoole\\Timer::after(): Argument #2 ($callback) must be a valid callback, class Test does not have a method \"not_exist\"');\n}\n\n// private method\n//------------------------------------------------------------------------------------------------------------------\n$pm = ProcessManager::exec(function () {\n    Swoole\\Timer::After(1, [Test::class, 'foo']);\n});\n$pm->expectExitCode(255);\n$output = $pm->getChildOutput();\nif (PHP_VERSION_ID < 80000) {\n    Assert::contains($output, 'Uncaught TypeError: Argument 2 passed to Swoole\\Timer::after() must be callable, array given');\n} else {\n    Assert::contains($output, 'Swoole\\Timer::after(): Argument #2 ($callback) must be a valid callback, cannot access private method Test::foo()');\n}\n\n// private method\n//------------------------------------------------------------------------------------------------------------------\n$pm = ProcessManager::exec(function () {\n    Swoole\\Timer::After(1, [new Test, 'bar']);\n});\n$pm->expectExitCode(255);\n$output = $pm->getChildOutput();\nif (PHP_VERSION_ID < 80000) {\n    Assert::contains($output, 'Uncaught TypeError: Argument 2 passed to Swoole\\Timer::after() must be callable, array given');\n} else {\n    Assert::contains($output, 'Swoole\\Timer::after(): Argument #2 ($callback) must be a valid callback, cannot access private method Test::bar()');\n}\n\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_timer/callback_bug_with_array.phpt",
    "content": "--TEST--\nswoole_timer: Timer callback\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nclass TestCo\n{\n    public function foo()\n    {\n        co::sleep(0.001);\n        $cid = go(function () {\n            co::yield();\n        });\n        co::resume($cid);\n        echo @$this->test;\n    }\n}\n\nfor ($c = MAX_CONCURRENCY; $c--;) {\n    Swoole\\Timer::after($c + 1, [new TestCo, 'foo']);\n}\n\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_timer/clearAll.phpt",
    "content": "--TEST--\nswoole_timer: #2697\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$server = new Swoole\\Server('127.0.0.1', get_one_free_port(), SWOOLE_PROCESS);\n$server->set(['log_file' => '/dev/null']);\n$server->on('workerStart', function (Swoole\\Server $server, int $worker_id) {\n    Swoole\\Timer::after(1000, function () {\n        var_dump('never here');\n    });\n    Swoole\\Timer::tick(1000, function () {\n        var_dump('never here');\n    });\n    Swoole\\Timer::clearAll();\n    if ($worker_id === 0) {\n        Swoole\\Timer::after(200, function () use ($server) {\n            $server->shutdown();\n        });\n    }\n});\n$server->on('receive', function () { });\n$server->start();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_timer/enable_coroutine.phpt",
    "content": "--TEST--\nswoole_timer: enable_coroutine setting\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nswoole_async_set([\n    'enable_coroutine' => false\n]);\nSwoole\\Timer::after(1, function () {\n    $uid = Co::getuid();\n    echo \"#{$uid}\\n\";\n});\n?>\n--EXPECT--\n#-1\n"
  },
  {
    "path": "tests/swoole_timer/enable_coroutine2.phpt",
    "content": "--TEST--\nswoole_timer: enable_coroutine setting\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nswoole_async_set([\n    'enable_coroutine' => false\n]);\nSwoole\\Timer::after(1, function () {\n    $uid = Co::getuid();\n    echo \"#{$uid}\\n\";\n});\nSwoole\\Event::wait();\n\nswoole_async_set([\n    'enable_coroutine' => true\n]);\nSwoole\\Timer::after(1, function () {\n    $uid = Co::getuid();\n    echo \"#{$uid}\\n\";\n});\nSwoole\\Event::wait();\n?>\n--EXPECT--\n#-1\n#1\n"
  },
  {
    "path": "tests/swoole_timer/function_alias.phpt",
    "content": "--TEST--\nswoole_timer: function alias\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nvar_dump(\n    function_exists('swoole_timer_after') &&\n    function_exists('swoole_timer_tick') &&\n    function_exists('swoole_timer_exists') &&\n    function_exists('swoole_timer_info') &&\n    function_exists('swoole_timer_stats') &&\n    function_exists('swoole_timer_list') &&\n    function_exists('swoole_timer_clear') &&\n    function_exists('swoole_timer_clear_all')\n);\n\n?>\n--EXPECTF--\nbool(true)\n"
  },
  {
    "path": "tests/swoole_timer/greater_than_0.phpt",
    "content": "--TEST--\nswoole_timer: Timer must be greater than 0\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nAssert::false(@Swoole\\Timer::after(0, function() {}));\nAssert::false(@Swoole\\Timer::after(-1, function() {}));\nAssert::false(@Swoole\\Timer::tick(0, function() {}));\nAssert::false(@Swoole\\Timer::tick(-1, function() {}));\n?>\n--EXPECTF--\n"
  },
  {
    "path": "tests/swoole_timer/info.phpt",
    "content": "--TEST--\nswoole_timer: list\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$timers = [];\nfor ($c = 1000; $c--;) {\n    $msec = mt_rand(100, 500);\n    $timers[Swoole\\Timer::after($msec, function () { })] = $msec;\n}\nforeach (Swoole\\Timer::list() as $timer_id) {\n    $info = Swoole\\Timer::info($timer_id);\n    time_approximate($timers[$timer_id], $info['exec_msec']);\n    Assert::same($info['interval'], 0);\n    Assert::same($info['round'], 0);\n    Assert::false($info['removed']);\n}\nSwoole\\Timer::tick(1, function (int $timer_id) {\n    // tick\n    $info = Swoole\\Timer::info($timer_id);\n    Assert::greaterThan($info['interval'], 0);\n    Assert::same($info['round'], 0);\n    Assert::false($info['removed']);\n    // remove\n    Swoole\\Timer::clear($timer_id);\n    $info = Swoole\\Timer::info($timer_id);\n    Assert::true($info['removed']);\n    // after\n    $info = Swoole\\Timer::info(Swoole\\Timer::after(1, function () { }));\n    Assert::greaterThan($info['exec_msec'], 0);\n    Assert::same($info['interval'], 0);\n    Assert::same($info['round'], 1); // next round\n    Assert::false($info['removed']);\n});\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_timer/list.phpt",
    "content": "--TEST--\nswoole_timer: list\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$timers = [];\nfor ($c = MAX_CONCURRENCY; $c--;) {\n    $timers[] = Swoole\\Timer::after(mt_rand(1, 100), function () { });\n}\n$iterator = Swoole\\Timer::list();\nAssert::isInstanceOf($iterator, ArrayIterator::class);\n$timers_2 = iterator_to_array($iterator);\nsort($timers_2);\nsort($timers);\nAssert::same($timers_2, $timers);\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_timer/manager.phpt",
    "content": "--TEST--\nswoole_timer: timer in manager\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\nconst RES_FILE = __DIR__.'/result.txt';\nfile_put_contents(RES_FILE, \"\");\n\n//设置等待10秒\n$pm->setWaitTimeout(10);\n\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    $fp = fopen(RES_FILE, \"rw\");\n    while(!feof($fp)) {\n        $line = fgets($fp);\n        if ($line) {\n            echo $line;\n        }\n    }\n    unlink(RES_FILE);\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    ini_set('swoole.display_errors', 'Off');\n    $serv = new Swoole\\Server(\"0.0.0.0\", $pm->getFreePort(), SWOOLE_PROCESS);\n\n    $serv->set(array(\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n    ));\n\n    $serv->on('managerStart', function ($serv) use ($pm) {\n\n        file_put_contents(RES_FILE, \"start\\n\", FILE_APPEND);\n\n        $id = Swoole\\Timer::tick(300, function () {\n            file_put_contents(RES_FILE, \"timer 1\\n\", FILE_APPEND);\n        });\n\n        Swoole\\Timer::after(900, function () use ($id, $serv, $pm) {\n            file_put_contents(RES_FILE, \"timer 2\\n\", FILE_APPEND);\n            Swoole\\Timer::clear($id);\n\n            Swoole\\Timer::tick(200, function ($id) use ($serv, $pm) {\n                static $i = 0;\n                file_put_contents(RES_FILE, \"timer 3\\n\", FILE_APPEND);\n                $i ++;\n                if ($i > 4) {\n                    file_put_contents(RES_FILE, \"end\\n\", FILE_APPEND);\n                    Swoole\\Timer::clear($id);\n                    $pm->wakeup();\n                    $serv->shutdown();\n                }\n            });\n        });\n    });\n\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $reactor_id, $data) {\n\n    });\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nstart\ntimer 1\ntimer 1\ntimer 1\ntimer 2\ntimer 3\ntimer 3\ntimer 3\ntimer 3\ntimer 3\nend\n"
  },
  {
    "path": "tests/swoole_timer/master.phpt",
    "content": "--TEST--\nswoole_timer: timer in master\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Server;\nuse Swoole\\Timer;\n\nconst RES_FILE = __DIR__ . '/result.txt';\nfile_put_contents(RES_FILE, '');\n\n$pm = new ProcessManager();\n$pm->setWaitTimeout(-1);\n$pm->parentFunc = function ($pid) use ($pm) {\n    $fp = fopen(RES_FILE, 'rw');\n    while (!feof($fp)) {\n        $line = fgets($fp);\n        if ($line) {\n            echo $line;\n        }\n    }\n    if (IS_MAC_OS) {\n        $pm->kill();\n    }\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('0.0.0.0', $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $server->on('start', function (Server $server) use ($pm) {\n        file_put_contents(RES_FILE, \"start\\n\", FILE_APPEND);\n        $id = Timer::tick(30, function () {\n            file_put_contents(RES_FILE, \"timer 1\\n\", FILE_APPEND);\n        });\n        Timer::after(90, function () use ($id, $server, $pm) {\n            file_put_contents(RES_FILE, \"timer 2\\n\", FILE_APPEND);\n            Timer::clear($id);\n            Timer::tick(10, function ($id) use ($server, $pm) {\n                static $i = 0;\n                file_put_contents(RES_FILE, \"timer 3\\n\", FILE_APPEND);\n                $i++;\n                if ($i > 4) {\n                    file_put_contents(RES_FILE, \"end\\n\", FILE_APPEND);\n                    Timer::clear($id);\n                    $pm->wakeup();\n                    $server->shutdown();\n                }\n            });\n        });\n    });\n    $server->on('receive', function () {});\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n@unlink(RES_FILE);\n?>\n--EXPECT--\nstart\ntimer 1\ntimer 1\ntimer 1\ntimer 2\ntimer 3\ntimer 3\ntimer 3\ntimer 3\ntimer 3\nend\n"
  },
  {
    "path": "tests/swoole_timer/master_base.phpt",
    "content": "--TEST--\nswoole_timer: timer in master (base)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->setWaitTimeout(-1);\n$pm->parentFunc = function () use ($pm) {\n    if (IS_MAC_OS) {\n        $pm->kill();\n    }\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $server->on('start', function (Swoole\\Server $server) use ($pm) {\n        echo \"start\\n\";\n        $id = Swoole\\Timer::tick(100, function () {\n            echo \"timer 1\\n\";\n        });\n        Swoole\\Timer::after(300, function () use ($id, $server, $pm) {\n            echo \"timer 2\\n\";\n            Swoole\\Timer::clear($id);\n            Swoole\\Timer::tick(50, function ($id) use ($server, $pm) {\n                static $i = 0;\n                echo \"timer 3\\n\";\n                $i++;\n                if ($i > 4) {\n                    echo \"end\\n\";\n                    Swoole\\Timer::clear($id);\n                    $pm->wakeup();\n                    $server->shutdown();\n                }\n            });\n        });\n    });\n    $server->on('receive', function () { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nstart\ntimer 1\ntimer 1\ntimer 1\ntimer 2\ntimer 3\ntimer 3\ntimer 3\ntimer 3\ntimer 3\nend\n"
  },
  {
    "path": "tests/swoole_timer/memory.phpt",
    "content": "--TEST--\nswoole_timer: memory leak test\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$stat = new stdClass();\n$stat->count = 0;\n$stat->m0 = memory_get_usage();\n$stat->data = [];\n\nSwoole\\Timer::tick(1, function ( $id ) use ( &$stat ) {\n    $obj = new stdClass();\n    $obj->name = random_bytes(8192);\n    $stat->data = $obj->name;\n    $stat->count++;\n    if ($stat->count == 1) {\n        $stat->m1 = memory_get_usage();\n        echo 'diff[0] ' . ($stat->m1 - $stat->m0) . \"\\n\";\n    } elseif ($stat->count == 99) {\n        $stat->m2 = memory_get_usage();\n        Assert::lessThan($stat->m2 - $stat->m1, 128);\n        echo 'diff[1] ' . ($stat->m2 - $stat->m1) . \"\\n\";\n        Swoole\\Timer::clear($id);\n    }\n});\n\n\\Swoole\\Event::wait();\n$stat->m3 = memory_get_usage();\n$stat->data = null;\necho 'diff[2] ' . ($stat->m3 - $stat->m0) . \"\\n\";\n\n?>\n--EXPECTF--\ndiff[0] %d\ndiff[1] %d\ndiff[2] %d\n"
  },
  {
    "path": "tests/swoole_timer/next_round.phpt",
    "content": "--TEST--\nswoole_timer: timer round control\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Timer;\nuse Swoole\\Event;\n\nTimer::after(10, function () {\n    Assert::eq(timer::stats()['round'], 1);\n    Timer::after(10, function () {\n        Assert::eq(timer::stats()['round'], 2);\n    });\n    usleep(100000);\n});\n\nEvent::wait();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_timer/not_exist.phpt",
    "content": "--TEST--\nswoole_timer: clear timer not exist\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nSwoole\\Timer::after(10, function () {\n    Assert::assert(0); // never here\n});\nfor ($n = MAX_REQUESTS; $n--;) {\n    Assert::assert(Swoole\\Timer::clear($n) === ($n === 1 ? true : false));\n}\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_timer/reinit_1.phpt",
    "content": "--TEST--\nswoole_timer: reinit [1]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Timer;\n\nconst RES_FILE = __DIR__ . '/result.txt';\nfile_put_contents(RES_FILE, \"\");\n\n$pm = new ProcessManager;\n$pm->setWaitTimeout(-1);\n$pm->parentFunc = function ($pid) use ($pm) {\n    $fp = fopen(RES_FILE, \"rw\");\n    while (!feof($fp)) {\n        $line = fgets($fp);\n        if ($line) {\n            echo $line;\n        }\n    }\n    if (IS_MAC_OS) {\n        $pm->kill();\n    }\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server(\"0.0.0.0\", $pm->getFreePort(), SWOOLE_PROCESS);\n    $server->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $server->on('start', function (Swoole\\Server $server) use ($pm) {\n        file_put_contents(RES_FILE, \"start\\n\", FILE_APPEND);\n        static $i = 0;\n        $id = Timer::tick(100, function ($timerId) use (&$i, $server, $pm) {\n            file_put_contents(RES_FILE, \"timer 1\\n\", FILE_APPEND);\n            if (($i++) == 4) {\n                Timer::clear($timerId);\n                $server->shutdown();\n                $pm->wakeup();\n            }\n        });\n    });\n\n    static $j = 0;\n    Timer::tick(50, function ($timerId) use (&$j) {\n        file_put_contents(RES_FILE, \"timer 2\\n\", FILE_APPEND);\n        if (($j++) == 5) {\n            Timer::clear($timerId);\n        }\n     });\n\n    $server->on('receive', function () { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\nunlink(RES_FILE);\n?>\n--EXPECT--\nstart\ntimer 2\ntimer 2\ntimer 1\ntimer 2\ntimer 2\ntimer 1\ntimer 2\ntimer 2\ntimer 1\ntimer 1\ntimer 1\n"
  },
  {
    "path": "tests/swoole_timer/reinit_2.phpt",
    "content": "--TEST--\nswoole_timer: reinit [2]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Timer;\n\nconst RES_FILE = __DIR__ . '/result.txt';\nfile_put_contents(RES_FILE, \"\");\n\n$pm = new ProcessManager;\n$pm->setWaitTimeout(-1);\n$pm->parentFunc = function ($pid) use ($pm) {\n    $fp = fopen(RES_FILE, \"rw\");\n    while (!feof($fp)) {\n        $line = fgets($fp);\n        if ($line) {\n            echo $line;\n        }\n    }\n    if (IS_MAC_OS) {\n        $pm->kill();\n    }\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Server(\"0.0.0.0\", $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $server->on('start', function (Swoole\\Server $server) use ($pm) {\n        file_put_contents(RES_FILE, \"start\\n\", FILE_APPEND);\n        static $i = 0;\n        $id = Timer::tick(100, function ($timerId) use (&$i, $server, $pm) {\n            file_put_contents(RES_FILE, \"timer 1\\n\", FILE_APPEND);\n            if (($i++) == 4) {\n                Timer::clear($timerId);\n                $server->shutdown();\n                $pm->wakeup();\n            }\n        });\n    });\n\n    static $j = 0;\n    Timer::tick(50, function ($timerId) use (&$j) {\n        file_put_contents(RES_FILE, \"timer 2\\n\", FILE_APPEND);\n        if (($j++) == 5) {\n            Timer::clear($timerId);\n        }\n     });\n\n    $server->on('receive', function () { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\nunlink(RES_FILE);\n?>\n--EXPECT--\nstart\ntimer 2\ntimer 2\ntimer 1\ntimer 2\ntimer 2\ntimer 1\ntimer 2\ntimer 2\ntimer 1\ntimer 1\ntimer 1\n"
  },
  {
    "path": "tests/swoole_timer/stats.phpt",
    "content": "--TEST--\nswoole_timer: list\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$s = microtime(true);\nvar_dump(Swoole\\Timer::stats());\nfor ($c = 1000; $c--;) {\n    Swoole\\Timer::after(mt_rand(1, 1000), function () { });\n}\nvar_dump(Swoole\\Timer::stats());\nforeach (Swoole\\Timer::list() as $timer_id) {\n    Assert::true(Swoole\\Timer::clear($timer_id));\n}\nSwoole\\Timer::after(100, function () {\n    var_dump(Swoole\\Timer::stats());\n});\nSwoole\\Event::wait();\ntime_approximate(0.1, microtime(true) - $s, 0.2);\n?>\n--EXPECTF--\narray(3) {\n  [\"initialized\"]=>\n  bool(false)\n  [\"num\"]=>\n  int(0)\n  [\"round\"]=>\n  int(0)\n}\narray(3) {\n  [\"initialized\"]=>\n  bool(true)\n  [\"num\"]=>\n  int(1000)\n  [\"round\"]=>\n  int(0)\n}\narray(3) {\n  [\"initialized\"]=>\n  bool(true)\n  [\"num\"]=>\n  int(1)\n  [\"round\"]=>\n  int(1)\n}\n"
  },
  {
    "path": "tests/swoole_timer/swoole_timer_list_alias.phpt",
    "content": "--TEST--\nswoole_timer: function alias about swoole_timer_list\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n$extension = new ReflectionExtension('swoole');\necho substr_count($extension, \"Swoole\\\\Timer\\\\Iterator\");\n?>\n--EXPECT--\n3\n"
  },
  {
    "path": "tests/swoole_timer/task_worker.phpt",
    "content": "--TEST--\nswoole_timer: call after in Task-Worker\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Client;\nuse Swoole\\Server;\nuse Swoole\\Timer;\n\n$pm = new ProcessManager();\n$pm->parentFunc = function ($pid) use ($pm) {\n    $cli = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $cli->set(['open_eof_check' => true, 'package_eof' => \"\\r\\n\\r\\n\"]);\n    $cli->connect('127.0.0.1', $pm->getFreePort(), 5) or exit('ERROR');\n\n    $cli->send('task-01') or exit('ERROR');\n    for ($i = 0; $i < 5; $i++) {\n        echo trim($cli->recv()) . \"\\n\";\n    }\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    ini_set('swoole.display_errors', 'Off');\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set([\n        'worker_num' => 1,\n        'task_worker_num' => 1,\n        'log_file' => '/dev/null',\n    ]);\n    $serv->on('WorkerStart', function (Server $serv) use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Server $serv, $fd, $rid, $data) {\n        $serv->task([$fd, 'timer']);\n    });\n\n    $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) {\n        [$fd] = $data;\n        Timer::after(500, function () use ($serv, $fd) {\n            $serv->send($fd, \"500\\r\\n\\r\\n\");\n            Timer::after(300, function () use ($serv, $fd) {\n                $serv->send($fd, \"800\\r\\n\\r\\n\");\n            });\n        });\n        Timer::after(1000, function () use ($serv, $fd) {\n            $serv->send($fd, \"1000[1]\\r\\n\\r\\n\");\n        });\n        Timer::after(1000, function () use ($serv, $fd) {\n            $serv->send($fd, \"1000[2]\\r\\n\\r\\n\");\n        });\n        Timer::after(500, function () use ($serv, $fd) {\n            $serv->send($fd, \"500[2]\\r\\n\\r\\n\");\n        });\n        Timer::after(2000, function () use ($serv, $fd) {\n            $serv->send($fd, \"2000\\r\\n\\r\\n\");\n        });\n    });\n\n    $serv->on('finish', function (Server $serv, $fd, $rid, $data) {});\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n500\n500[2]\n800\n1000[1]\n1000[2]\n"
  },
  {
    "path": "tests/swoole_timer/task_worker_tick_1k.phpt",
    "content": "--TEST--\nswoole_timer: call tick timer in Task-Worker\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function ($pid) use ($pm)\n{\n    $cli = new Swoole\\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);\n    $cli->set(['open_eof_check' => true, \"package_eof\" => \"\\r\\n\\r\\n\"]);\n    $cli->connect('127.0.0.1', $pm->getFreePort(), 5) or die(\"ERROR\");\n\n    $cli->send(\"task-01\") or die(\"ERROR\");\n    for ($i = 0; $i < 3; $i++)\n    {\n        echo trim($cli->recv()).\"\\n\";\n    }\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm)\n{\n    ini_set('swoole.display_errors', 'Off');\n    $serv = new Swoole\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS);\n    $serv->set(array(\n        \"worker_num\" => 1,\n        'task_worker_num' => 1,\n        'log_file' => '/dev/null',\n    ));\n    $serv->on(\"WorkerStart\", function (Swoole\\Server $serv)  use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on('receive', function (Swoole\\Server $serv, $fd, $rid, $data) {\n        $serv->task([$fd, 'timer']);\n    });\n\n    $serv->on('task', function (Swoole\\Server $serv, $task_id, $worker_id, $data) {\n        static $i = 0;\n        Swoole\\Timer::tick(1, function () use(&$i, $serv) {\n            $i++;\n            if ($i % 500 == 499) {\n                $serv->send(1, \"timer-$i\\r\\n\\r\\n\");\n            }\n        });\n    });\n\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\ntimer-499\ntimer-999\ntimer-1499\n"
  },
  {
    "path": "tests/swoole_timer/verify.phpt",
    "content": "--TEST--\nswoole_timer: verify timer\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nfor ($c = MAX_CONCURRENCY; $c--;) {\n    go(function () {\n        $seconds = ms_random(0.1, 0.5);\n        $start = microtime(true);\n        Co::sleep($seconds);\n        time_approximate($seconds, microtime(true) - $start, 0.25);\n    });\n}\nSwoole\\Event::wait();\necho \"DONE\\n\";\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_websocket_server/bug_1.phpt",
    "content": "--TEST--\nswoole_websocket_server: bug 1\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\n/**\n * @link https://wenda.swoole.com/detail/108914\n */\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        global $count;\n        $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->set(['timeout' => 5]);\n        Assert::assert($cli->upgrade('/'));\n        $data1 = get_safe_random(random_int(1024, 8192));\n        $cli->push($data1);\n        $frame1 = $cli->recv();\n        Assert::eq($frame1->data, md5($data1));\n\n        $data2 = get_safe_random(random_int(65536, 65536 * 2));\n        $pkt2 = Swoole\\WebSocket\\Server::pack($data2, WEBSOCKET_OPCODE_TEXT);\n\n        $cli->socket->sendAll(substr($pkt2, 0, 4));\n        usleep(1000);\n        $cli->socket->sendAll(substr($pkt2, 4));\n\n        $frame2 = $cli->recv();\n        Assert::eq($frame2->data, md5($data2));\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort());\n    $serv->set([\n        'open_http2_protocol' => true,\n//        'log_file' => '/dev/null',\n    ]);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('message', function (Swoole\\WebSocket\\Server $server, Swoole\\WebSocket\\Frame $frame) {\n        $server->push($frame->fd, md5($frame->data));\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_websocket_server/close_frame_flag.phpt",
    "content": "--TEST--\nswoole_websocket_server: websocket server active close with close frame flag false\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    $cli = new SwooleTest\\Samtleben\\WebsocketClient;\n    $connected = $cli->connect('127.0.0.1', $pm->getFreePort(), '/');\n    Assert::assert($connected);\n    $cli->sendRecv('shutdown');\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        // 'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'open_websocket_close_frame' => false\n    ]);\n    $serv->on('WorkerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('Message', function ($serv, $frame) {\n        if ($frame->opcode == WEBSOCKET_OPCODE_CLOSE) {\n            echo \"{$frame->code}\\n\";\n            echo \"{$frame->reason}\\n\";\n            Assert::true(false, 'never here'); // Should never reach here\n        } else {\n            if ($frame->data == 'shutdown') {\n                echo \"{$frame->data}\";\n                $serv->disconnect($frame->fd, 4000, 'shutdown received');\n            }\n        }\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nshutdown\n"
  },
  {
    "path": "tests/swoole_websocket_server/close_frame_full.phpt",
    "content": "--TEST--\nswoole_websocket_server: websocket server send and recv close frame full test\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    for ($c = MAX_CONCURRENCY_LOW; $c--;) {\n        go(function () use ($pm) {\n            $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            $cli->set([\n                'timeout' => 5,\n                'open_websocket_ping_frame' => true,\n                'open_websocket_pong_frame' => true,\n                'open_websocket_close_frame' => true,\n            ]);\n            for ($n = MAX_REQUESTS; $n--;) {\n                Assert::true($cli->upgrade('/'));\n                $code = mt_rand(0, 5000);\n                $reason = md5($code);\n                $close_frame = new Swoole\\WebSocket\\CloseFrame;\n                $close_frame->code = $code;\n                $close_frame->reason = $reason;\n                Assert::true($cli->push($close_frame));\n                // recv the last close frame\n                $frame = $cli->recv();\n                Assert::isInstanceOf($frame, Swoole\\WebSocket\\CloseFrame::class);\n                Assert::same($frame->opcode, WEBSOCKET_OPCODE_CLOSE);\n                Assert::same(md5($frame->code), $frame->reason);\n                // connection closed\n                Assert::true($cli->disconnect($frame->code, $frame->reason));\n                Assert::false($cli->recv());\n                Assert::false($cli->connected);\n                Assert::same($cli->errCode, 5001); // connection close normally\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        // 'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $serv->on('WorkerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('Message', function (Swoole\\WebSocket\\Server  $serv, Swoole\\WebSocket\\Frame $frame) {\n        Assert::isInstanceOf($frame, Swoole\\WebSocket\\CloseFrame::class);\n        Assert::same($frame->opcode, WEBSOCKET_OPCODE_CLOSE);\n        if (mt_rand(0, 1)) {\n            $serv->push($frame->fd, $frame);\n        } else {\n            $serv->disconnect($frame->fd, $frame->code, $frame->reason);\n        }\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_websocket_server/compression.phpt",
    "content": "--TEST--\nswoole_websocket_server: compression\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse Swoole\\WebSocket\\Server;\nuse SwooleTest\\ProcessManager;\n\nphpt_var_dump(defined('SWOOLE_HAVE_ZLIB'));\n\n$pm = new ProcessManager();\n$pm->initRandomData(MAX_REQUESTS);\n$pm->parentFunc = function (int $pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $cli = new Client('127.0.0.1', $pm->getFreePort());\n        $cli->set([\n            'timeout' => 5,\n            'websocket_compression' => true,\n        ]);\n        $ret = $cli->upgrade('/');\n        if (!Assert::assert($ret)) {\n            return;\n        }\n        for ($n = MAX_REQUESTS; $n--;) {\n            $data = $pm->getRandomData();\n            $cli->push(\n                $data,\n                SWOOLE_WEBSOCKET_OPCODE_TEXT,\n                SWOOLE_WEBSOCKET_FLAG_FIN | SWOOLE_WEBSOCKET_FLAG_COMPRESS\n            );\n            $frame = $cli->recv();\n            if (!Assert::same($frame->data, $data)) {\n                return;\n            }\n            if (!Assert::eq($frame->flags & SWOOLE_WEBSOCKET_FLAG_COMPRESS, defined('SWOOLE_HAVE_ZLIB'))) {\n                return;\n            }\n        }\n        echo \"DONE\\n\";\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $server->set([\n        'log_file' => '/dev/null',\n        'websocket_compression' => true,\n    ]);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('message', function (Server $server, Frame $frame) use ($pm) {\n        if (!Assert::same($frame->data, $pm->getRandomData())) {\n            $server->close($frame->fd);\n            return;\n        }\n        if (!Assert::eq($frame->flags & SWOOLE_WEBSOCKET_FLAG_COMPRESS, defined('SWOOLE_HAVE_ZLIB'))) {\n            $server->close($frame->fd);\n            return;\n        }\n        $server->push($frame->fd, $frame);\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_websocket_server/disconnect.phpt",
    "content": "--TEST--\nswoole_websocket_server: websocket server disconnect\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    $cli = new SwooleTest\\Samtleben\\WebsocketClient;\n    $connected = $cli->connect('127.0.0.1', $pm->getFreePort(), '/');\n    Assert::assert($connected);\n    $response = $cli->sendRecv(\"shutdown\");\n    echo unpack('n', substr($response, 0, 2))[1] . \"\\n\";\n    echo substr($response, 2) . \"\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        // 'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $serv->on('WorkerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('Message', function (Swoole\\WebSocket\\Server  $serv, Swoole\\WebSocket\\Frame $frame) {\n        if ($frame->data == 'shutdown') {\n            $serv->disconnect($frame->fd, 4000, 'shutdown received');\n        }\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n4000\nshutdown received\n"
  },
  {
    "path": "tests/swoole_websocket_server/disconnect_with_code.phpt",
    "content": "--TEST--\nswoole_websocket_server: websocket server disconnect with one param code\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    $cli = new SwooleTest\\Samtleben\\WebsocketClient;\n    $connected = $cli->connect('127.0.0.1', $pm->getFreePort(), '/');\n    Assert::assert($connected);\n    $response = $cli->sendRecv(\"shutdown\");\n    $byteArray = unpack('n', $response);\n    echo $byteArray[1] . \"\\n\";\n    echo substr($response, 2);\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        // 'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $serv->on('WorkerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('Message', function (Swoole\\WebSocket\\Server  $serv, Swoole\\WebSocket\\Frame $frame) {\n        if ($frame->data == 'shutdown') {\n            $serv->disconnect($frame->fd, 4001);\n        }\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n4001\n"
  },
  {
    "path": "tests/swoole_websocket_server/dynamic_property.phpt",
    "content": "--TEST--\nswoole_websocket_server: Creation of dynamic property is deprecated.\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\n$pm = new ProcessManager;\n$pm->initFreePorts(10);\n$websocket = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort());\n$port1 = $websocket->listen(\"127.0.0.1\", $pm->getFreePort(1), SWOOLE_SOCK_TCP);\n$port2 = $websocket->listen(\"127.0.0.1\", $pm->getFreePort(2), SWOOLE_SOCK_TCP);\n$port3 = $websocket->listen(\"127.0.0.1\", $pm->getFreePort(3), SWOOLE_SOCK_TCP);\n$port1->on('handshake', function($request, $response) {});\n$port1->on('beforehandshakeresponse', function($request, $response) {});\nvar_dump($port1->getCallback('handshake') != null);\nvar_dump($port1->getCallback('BeforeHandshakeResponse') != null);\n\n$port2->on('HANDSHAKE', function($request, $response) {});\n$port2->on('BEFOREHANDSHAKERESPONSE', function($request, $response) {});\nvar_dump($port1->getCallback('HANDSHAKE') != null);\nvar_dump($port1->getCallback('BEFOREHANDSHAKERESPONSE') != null);\n\n$port3->on('handShake', function($request, $response) {});\n$port3->on('beforehandShakeResponse', function($request, $response) {});\nvar_dump($port1->getCallback('handShake') != null);\nvar_dump($port1->getCallback('beforehandShakeResponse') != null);\necho \"DONE\\n\";\n?>\n--EXPECT--\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nbool(true)\nDONE\n"
  },
  {
    "path": "tests/swoole_websocket_server/empty_message.phpt",
    "content": "--TEST--\nswoole_websocket_server: empty message\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->useConstantPorts = true;\n$pm->initFreePorts();\n\n$pm->parentFunc = function (int $pid) use ($pm) {\n    go(function() use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $connected = $cli->upgrade('/?test=a&b=hello');\n        Assert::assert($connected);\n        $cli->push('');\n        $response = $cli->recv();\n        Assert::assert($response);\n        $json = json_decode($response->data);\n        Assert::isEmpty($json->data);\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        // 'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $serv->on('WorkerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('open', function (Swoole\\WebSocket\\Server  $serv, Swoole\\Http\\Request $req) {\n    });\n    $serv->on('Message', function (Swoole\\WebSocket\\Server  $serv, Swoole\\WebSocket\\Frame $frame) {\n        $serv->push($frame->fd, json_encode($frame));\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_websocket_server/exists_and_isEstablished.phpt",
    "content": "--TEST--\nswoole_websocket_server: exists & isEstablished\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$connections = [];\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    $ready = new Chan;\n    for ($c = 0; $c < MAX_CONCURRENCY; $c++) {\n        go(function () use ($pm, $ready, $c) {\n            /* @var $connections Co\\Http\\Client[][] */\n            global $connections;\n            $connections[$c] = [\n                'cli' => new Co\\Http\\Client('127.0.0.1', $pm->getFreePort()),\n                'type' => array_random(['null', 'http', 'websocket']),\n                'fd' => -1\n            ];\n            if ($connections[$c]['type'] !== 'null') {\n                if (!Assert::assert($connections[$c]['cli']->get('/'))) {\n                    exit;\n                }\n                $connections[$c]['fd'] = (int)$connections[$c]['cli']->body;\n                if ($connections[$c]['type'] === 'websocket') {\n                    if (!Assert::assert($connections[$c]['cli']->upgrade('/'))) {\n                        exit;\n                    }\n                }\n            }\n            $ready->push(true);\n        });\n    }\n    go(function () use ($pm, $ready) {\n        global $connections;\n        for ($c = 0; $c < MAX_CONCURRENCY; $c++) {\n            Assert::true($ready->pop());\n        }\n        $cli = new Co\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        if (Assert::assert($cli->upgrade('/'))) {\n            for ($c = 0; $c < MAX_CONCURRENCY; $c++) {\n                if (!Assert::assert($cli->push($connections[$c]['fd']))) {\n                    exit;\n                }\n                $frame = $cli->recv();\n                if (!Assert::assert($frame instanceof Swoole\\WebSocket\\Frame)) {\n                    exit;\n                }\n                // var_dump($connections[$c], $frame->data);\n                if (!Assert::assert($frame->data === ($connections[$c]['type'] ?? 'null'))) {\n                    exit;\n                }\n            }\n        }\n        $connections = null;\n        echo \"DONE\\n\";\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\WebSocket\\Server ('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $server->set(['log_file' => '/dev/null']);\n    $server->on('start', function () use ($pm) {\n        switch_process();\n        $pm->wakeup();\n    });\n    $server->on('request', function (Swoole\\Http\\Request $request, Swoole\\Http\\Response $response) {\n        $response->end($request->fd);\n    });\n    $server->on('message', function (Swoole\\WebSocket\\Server $server, Swoole\\WebSocket\\Frame $frame) {\n        $fd = (int)$frame->data;\n        $server->push(\n            $frame->fd,\n            $server->isEstablished($fd) ? 'websocket' : ($server->exists($fd) ? 'http' : 'null')\n        );\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_websocket_server/fin.phpt",
    "content": "--TEST--\nswoole_websocket_server: websocket server recv and merge fin packages\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$count = 0;\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, &$count) {\n    for ($c = MAX_CONCURRENCY; $c--;) {\n        go(function () use ($pm, &$count) {\n            $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            $cli->set(['timeout' => 5]);\n            $ret = $cli->upgrade('/');\n            Assert::assert($ret);\n            $rand_list = [];\n            $times = MAX_REQUESTS;\n            for ($n = $times; $n--;) {\n                $rand = get_safe_random(mt_rand(1, 1280));\n                $rand_list[] = $rand;\n                $opcode = $n === $times - 1 ? WEBSOCKET_OPCODE_TEXT : WEBSOCKET_OPCODE_CONTINUATION;\n                $finish = $n === 0;\n                if (mt_rand(0, 1)) {\n                    $frame = new Swoole\\WebSocket\\Frame;\n                    $frame->opcode = $opcode;\n                    $frame->data = $rand;\n                    $frame->finish = $finish;\n                    $ret = $cli->push($frame);\n                } else {\n                    $ret = $cli->push($rand, $opcode, $finish);\n                }\n                Assert::assert($ret);\n            }\n            $frame = $cli->recv();\n            if (Assert::assert($frame->data === implode('', $rand_list))) {\n                $count++;\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    Assert::same($count, MAX_CONCURRENCY);\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        // 'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $serv->on('WorkerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('Message', function (Swoole\\WebSocket\\Server  $serv, Swoole\\WebSocket\\Frame $frame) {\n        if (mt_rand(0, 1)) {\n            $serv->push($frame->fd, $frame);\n        } else {\n            $serv->push($frame->fd, $frame->data, $frame->opcode, true);\n        }\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_websocket_server/fin2.phpt",
    "content": "--TEST--\nswoole_websocket_server: fin [2]\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->set(['timeout' => 5]);\n        $ret = $cli->upgrade('/');\n        Assert::assert($ret);\n        $rand_list = [];\n        $rand = get_safe_random(mt_rand(1, 1280));\n        $rand_list[] = $rand;\n        $times = rand(8, 32);\n        for ($n = $times; $n--;) {\n            $opcode = $n === $times - 1 ? WEBSOCKET_OPCODE_TEXT : WEBSOCKET_OPCODE_CONTINUATION;\n            $finish = $n === 0;\n            if (mt_rand(0, 1)) {\n                $frame = new Swoole\\WebSocket\\Frame;\n                $frame->opcode = $opcode;\n                $frame->data = $rand;\n                $frame->finish = $finish;\n                $ret = $cli->push($frame);\n            } else {\n                $ret = $cli->push($rand, $opcode, $finish);\n            }\n        }\n        Assert::assert($ret);\n        $frame = $cli->recv();\n        Assert::assert($frame instanceof Swoole\\WebSocket\\Frame);\n        $data = json_decode($frame->data);\n        Assert::assert($data->finish);\n        Assert::assert($data->data, implode('', $rand_list));\n    });\n\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n         'worker_num' => 1,\n         'log_file' => '/dev/null'\n    ]);\n    $serv->on('WorkerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('Message', function (Swoole\\WebSocket\\Server  $serv, Swoole\\WebSocket\\Frame $frame) {\n        if (mt_rand(0, 1)) {\n            $frame->data = json_encode($frame);\n            $serv->push($frame->fd, $frame);\n        } else {\n            $serv->push($frame->fd, json_encode($frame), $frame->opcode, true);\n        }\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_websocket_server/get_large_requests.phpt",
    "content": "--TEST--\nswoole_websocket_server: websocket with large data concurrency\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\n\n$count = 0;\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    for ($c = MAX_CONCURRENCY_MID; $c--;) {\n        go(function () use ($pm) {\n            global $count;\n            $cli = new Client('127.0.0.1', $pm->getFreePort());\n            $cli->set(['timeout' => -1]);\n            $ret = $cli->upgrade('/');\n            Assert::assert($ret);\n            $len = mt_rand(35000, 40000);\n            $data = get_safe_random($len);\n            for ($n = MAX_REQUESTS; $n--;) {\n                $cli->push($data);\n                $ret = $cli->recv();\n                if (Assert::eq($ret->data, $data)) {\n                    $count++;\n                }\n            }\n            if (co::stats()['coroutine_num'] === 1) {\n                Assert::same($count, (MAX_CONCURRENCY_MID * MAX_REQUESTS));\n                $cli->push('max');\n                Assert::assert((int)$cli->recv()->data > 1);\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        // 'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('message', function (Server $server, Frame $frame) {\n        co::sleep(0.001);\n        if ($frame->data === 'max') {\n            $server->push($frame->fd, co::stats()['coroutine_peak_num']);\n        } else {\n            Assert::assert(strlen($frame->data) >= 35000);\n            $server->push($frame->fd, $frame->data);\n        }\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_websocket_server/get_small_requests.phpt",
    "content": "--TEST--\nswoole_websocket_server: websocket with small data concurrency\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$count = 0;\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    for ($c = MAX_CONCURRENCY; $c--;) {\n        go(function () use ($pm) {\n            global $count;\n            $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            $cli->set(['timeout' => 5]);\n            $ret = $cli->upgrade('/');\n            Assert::assert($ret);\n            $len = mt_rand(1, 100);\n            $data = get_safe_random($len);\n            for ($n = MAX_REQUESTS; $n--;) {\n                $cli->push($data);\n                $ret = $cli->recv();\n                if (Assert::assert($ret->data == $len)) {\n                    $count++;\n                }\n            }\n            if (co::stats()['coroutine_num'] === 1) {\n                Assert::same($count, (MAX_CONCURRENCY * MAX_REQUESTS));\n                $cli->push('max');\n                Assert::assert((int)$cli->recv()->data > 1);\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        // 'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('message', function (Swoole\\WebSocket\\Server  $server, Swoole\\WebSocket\\Frame $frame) {\n        if (mt_rand(0, 1)) {\n            co::sleep(0.001); // 50% block\n        }\n        if ($frame->data === 'max') {\n            $server->push($frame->fd, co::stats()['coroutine_peak_num']);\n        } else {\n            $server->push($frame->fd, strlen($frame->data));\n        }\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_websocket_server/greeter.phpt",
    "content": "--TEST--\nswoole_websocket_server: websocket greeter and reply twice\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$count = 0;\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, &$count) {\n    for ($c = MAX_CONCURRENCY; $c--;) {\n        go(function () use ($pm, &$count) {\n            global $count;\n            $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            $cli->set(['timeout' => 5]);\n            $ret = $cli->upgrade('/');\n            Assert::assert($ret);\n            $data = sha1(get_safe_random(mt_rand(1, 1024)));\n            for ($n = MAX_REQUESTS; $n--;) {\n                $cli->push($data);\n                $ret = $cli->recv();\n                Assert::same($ret->data, \"Hello {$data}!\");\n                $ret = $cli->recv();\n                Assert::same($ret->data, \"How are you, {$data}?\");\n                $count++;\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    Assert::same($count, (MAX_CONCURRENCY * MAX_REQUESTS));\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        // 'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('message', function (Swoole\\WebSocket\\Server  $server, Swoole\\WebSocket\\Frame $frame) {\n        $server->push($frame->fd, \"Hello {$frame->data}!\");\n        $server->push($frame->fd, \"How are you, {$frame->data}?\");\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_websocket_server/header_token.phpt",
    "content": "--TEST--\nswoole_websocket_server: header token\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nconst LOG_FILE = __DIR__ . '/swoole.log';\n$count = 0;\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, &$count) {\n    Co\\run(function () use ($pm) {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $connected = $cli->connect('127.0.0.1', $pm->getFreePort());\n        Assert::assert($connected);\n        $cli->send(\"GET /chat HTTP/1.1\\r\\n\" .\n            \"Host: localhost\\r\\n\" .\n            \"Upgrade: websocket, abc\\r\\n\" .\n            \"Connection: Upgrade, abc\\r\\n\" .\n            \"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\\r\\n\" .\n            \"Sec-WebSocket-Protocol: chat\\r\\n\" .\n            \"Sec-WebSocket-Version: 13\\r\\n\\r\\n\");\n        $res = $cli->recv();\n        Assert::contains($res, 'HTTP/1.1 101 Switching Protocols');\n\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $connected = $cli->connect('127.0.0.1', $pm->getFreePort());\n        Assert::assert($connected);\n        $cli->send(\"GET /chat HTTP/1.1\\r\\n\" .\n            \"Host: localhost\\r\\n\" .\n            \"Upgrade: abc, websocket\\r\\n\" .\n            \"Connection: abc, Upgrade\\r\\n\" .\n            \"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\\r\\n\" .\n            \"Sec-WebSocket-Protocol: chat\\r\\n\" .\n            \"Sec-WebSocket-Version: 13\\r\\n\\r\\n\");\n        $res = $cli->recv();\n        Assert::contains($res, 'HTTP/1.1 101 Switching Protocols');\n\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $connected = $cli->connect('127.0.0.1', $pm->getFreePort());\n        Assert::assert($connected);\n        $cli->send(\"GET /chat HTTP/1.1\\r\\n\" .\n            \"Host: localhost\\r\\n\" .\n            \"Upgrade: abc, websocket, def\\r\\n\" .\n            \"Connection: abc, Upgrade, def\\r\\n\" .\n            \"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\\r\\n\" .\n            \"Sec-WebSocket-Protocol: chat\\r\\n\" .\n            \"Sec-WebSocket-Version: 13\\r\\n\\r\\n\");\n        $res = $cli->recv();\n        Assert::contains($res, 'HTTP/1.1 101 Switching Protocols');\n    });\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'log_file' => LOG_FILE,\n    ]);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('message', function (Swoole\\WebSocket\\Server $server, Swoole\\WebSocket\\Frame $frame) {\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\nunlink(LOG_FILE);\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_websocket_server/listener.phpt",
    "content": "--TEST--\nswoole_websocket_server: addlistener\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    go(function () use ($pm) {\n        $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', 9506);\n        $cli->set(['timeout' => 5]);\n        $ret = $cli->upgrade('/');\n        Assert::assert($ret);\n        foreach (range(1, 100) as $i)\n        {\n            $ret = $cli->push(\"hello\");\n            Assert::assert($ret);\n            $frame = $cli->recv();\n            Assert::same($frame->data, \"Swoole: hello\");\n        }\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file'   => '/dev/null'\n    ]);\n\n    $serv->listen('127.0.0.1', 9506, SWOOLE_SOCK_TCP);\n\n    $serv->on('workerStart', function () use ($pm)\n    {\n        $pm->wakeup();\n    });\n    $serv->on('open', function ($swoole_server, $req)\n    {\n    });\n    $serv->on('message', function ($swoole_server, $frame)\n    {\n        $swoole_server->push($frame->fd, \"Swoole: {$frame->data}\");\n    });\n    $serv->on('close', function ($swoole_server, $fd)\n    {\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_websocket_server/malformed_data.phpt",
    "content": "--TEST--\nswoole_websocket_server: malformed data\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nconst LOG_FILE = __DIR__ . '/swoole.log';\n$count = 0;\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, &$count) {\n    $bytes = [chr(25)];\n    swoole_loop_n(255, function () use (&$bytes) {\n        $bytes[] = chr(255);\n    });\n\n    Co\\run(function () use ($bytes, $pm) {\n        $cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);\n        $connected = $cli->connect('127.0.0.1', $pm->getFreePort());\n        Assert::assert($connected);\n        $cli->send(\"GET /chat HTTP/1.1\\r\\n\" .\n            \"Host: localhost\\r\\n\" .\n            \"Upgrade: websocket\\r\\n\" .\n            \"Connection: Upgrade\\r\\n\" .\n            \"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\\r\\n\" .\n            \"Sec-WebSocket-Protocol: chat\\r\\n\" .\n            \"Sec-WebSocket-Version: 13\\r\\n\\r\\n\");\n        $r1 = $cli->recv();\n        Assert::contains($r1, 'HTTP/1.1 101 Switching Protocols');\n        $cli->send(implode('', $bytes));\n        $r2 = $cli->recv();\n        Assert::eq($r2, false);\n        Assert::eq($cli->errCode, SOCKET_ECONNRESET);\n    });\n    $pm->kill();\n    $log = file_get_contents(LOG_FILE);\n    Assert::contains($log, 'malformed data');\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $serv->set([\n        'log_file' => LOG_FILE,\n    ]);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('message', function (Swoole\\WebSocket\\Server $server, Swoole\\WebSocket\\Frame $frame) {\n\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\nunlink(LOG_FILE);\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_websocket_server/memory.phpt",
    "content": "--TEST--\nswoole_websocket_server: memory trace\n--SKIPIF--\n<?php\nrequire __DIR__ . '/../include/skipif.inc';\nskip_if_no_top();\n?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\ndefine('FRAME_DATA_SIZE', 100 * 1024);\n\n$pm = new ProcessManager();\n$pm->parentFunc = function () use ($pm) {\n    phpt_echo(\"start to benchmark \" . MAX_REQUESTS_MID . \" times...\\n\");\n    $concurrency = PRESSURE_LEVEL === PRESSURE_NORMAL ? MAX_CONCURRENCY * 4 : MAX_CONCURRENCY;\n    Co::set(['max_coroutine' => $concurrency + 1]);\n    Co\\run(function () use ($pm, $concurrency) {\n        phpt_echo(\"Concurrency: {$concurrency}\\n\");\n        for ($c = $concurrency; $c--;) {\n            go(function () use ($pm, $c) {\n                $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n                $cli->set(['timeout' => -1]);\n                while (!@$cli->upgrade('/')) {\n                    Co::sleep(0.1);\n                }\n                while ($cli->recv(-1)) {\n                    continue;\n                }\n            });\n        }\n    });\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\Websocket\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set(['worker_num' => 1, 'log_file' => '/dev/null']);\n    $server->on('workerStart', function (Swoole\\Websocket\\Server $server, int $worker_id) use ($pm) {\n        global $mem_records;\n        phpt_echo(\"init\\n\");\n        co::sleep(1);\n        $pm->wakeup();\n        phpt_echo(\"start\\n\");\n        while (true) {\n            $master_top = top($server->master_pid);\n            $worker_top = top($server->worker_pid);\n            if (empty($master_top) || empty($worker_top)) {\n                phpt_echo(\"shutdown\\n\");\n                foreach ($server->connections as $fd) {\n                    @$server->close($fd);\n                }\n                $server->shutdown();\n                return;\n            }\n            $mem_records[] = [\n                'master_virtual' => $master_top['VIRT'],\n                'master_real' => $master_top['RES'],\n                'worker_virtual' => $worker_top['VIRT'],\n                'worker_real' => $worker_top['RES']\n            ];\n            phpt_var_dump(end($mem_records));\n            if (($records_count = count($mem_records)) === MAX_REQUESTS_MID) {\n                phpt_echo(\"=== master virtual ===\\n\");\n                phpt_var_dump($master_virtual = array_column($mem_records, 'master_virtual'));\n                phpt_echo(\"=== master real ===\\n\");\n                phpt_var_dump($master_real = array_column($mem_records, 'master_real'));\n                phpt_echo(\"=== worker virtual ===\\n\");\n                phpt_var_dump($worker_virtual = array_column($mem_records, 'worker_virtual'));\n                phpt_echo(\"=== worker real ===\\n\");\n                phpt_var_dump($worker_real = array_column($mem_records, 'worker_real'));\n                for ($i = $records_count / 2; $i < $records_count; $i++) {\n                    approximate($master_virtual[$i], $master_virtual[$records_count / 2]);\n                    approximate($worker_virtual[$i], $worker_virtual[$records_count / 2]);\n                    approximate($worker_real[$i], $worker_real[$records_count / 2]);\n                }\n                $server->shutdown();\n                return;\n            }\n            $fd = 0;\n            $success = 0;\n            foreach ($server->connections as $fd) {\n                if (@$server->push($fd, str_repeat('S', FRAME_DATA_SIZE))) {\n                    $success++;\n                }\n            }\n            phpt_echo(\"#{$records_count}: push \" . (FRAME_DATA_SIZE / 1024) . \"k data to {$fd} client success {$success}!\\n\");\n            co::sleep(REQUESTS_WAIT_TIME);\n            switch_process();\n        }\n    });\n    $server->on('message', function (Swoole\\Websocket\\Server $server, Swoole\\WebSocket\\Frame $frame) { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_websocket_server/message_size.phpt",
    "content": "--TEST--\nswoole_websocket_server: message size\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\WebSocket\\Frame;\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\n\nconst N = 8;\n$count = 0;\n$pm = new SwooleTest\\ProcessManager;\n\nfunction test(Client $cli, $min, $max)\n{\n    global $count;\n    $len = mt_rand($min, $max);\n    $data = get_safe_random($len);\n    for ($n = N; $n--;) {\n        $cli->push($data);\n        $ret = $cli->recv();\n        if (Assert::eq($ret->data, $data)) {\n            $count++;\n        }\n    }\n}\n\n$pm->parentFunc = function (int $pid) use ($pm) {\n    for ($c = N; $c--;) {\n        go(function () use ($pm) {\n            $cli = new Client('127.0.0.1', $pm->getFreePort());\n            $cli->set(['timeout' => -1]);\n            $ret = $cli->upgrade('/');\n            Assert::assert($ret);\n            test($cli, 64, 200);\n            test($cli, 25600, 70000);\n            test($cli, 70000, 400000);\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        // 'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('message', function (Server $server, Frame $frame) {\n        $server->push($frame->fd, $frame->data);\n    });\n    $serv->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_websocket_server/onDisconnct.phpt",
    "content": "--TEST--\nswoole_websocket_server: onDisconnect\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse function Swoole\\Coroutine\\run;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\WebSocket\\Frame;\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function (int $pid) use ($pm) {\n    run(function () use ($pm) {\n        $data = httpGetBody('http://127.0.0.1:' . $pm->getFreePort() . '/');\n        Assert::contains($data, 'HTTP 400 Bad Request');\n\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        Assert::assert($client->upgrade('/websocket'));\n        Assert::eq($client->getStatusCode(), 101);\n        $client->push('hello world');\n        $client->close();\n    });\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $serv->on('WorkerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('Message', function (Server $serv, Frame $frame) {\n        if ($frame->data == 'shutdown') {\n            $serv->disconnect($frame->fd, 4000, 'shutdown received');\n        }\n    });\n    $serv->on('connect', function ($s, $id) use ($pm) {\n        puts(\"connect \" . $id);\n    });\n    $serv->on('disconnect', function ($s, $id) use ($pm) {\n        puts(\"disconnect \" . $id);\n    });\n    $serv->on('open', function ($s, $req) use ($pm) {\n        puts(\"open \" . $req->fd);\n    });\n    $serv->on('close', function ($s, $id) use ($pm) {\n        puts(\"close \" . $id);\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\nputs('done!');\n?>\n--EXPECT--\nconnect 1\ndisconnect 1\nconnect 2\nopen 2\nclose 2\ndone!\n"
  },
  {
    "path": "tests/swoole_websocket_server/pack.phpt",
    "content": "--TEST--\nswoole_websocket_server: websocket frame pack/unpack\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nerror_reporting(error_reporting() & ~(E_NOTICE));\n\nuse Swoole\\WebSocket\\Frame;\nuse Swoole\\WebSocket\\CloseFrame;\n\nfor ($i = 1000; $i--;) {\n    // generate some rand frames\n    $opcode = mt_rand(WEBSOCKET_OPCODE_CONTINUATION, WEBSOCKET_OPCODE_PONG);\n    $data = base64_encode(get_safe_random(mt_rand(0, 128000))) . 'EOL';\n    if ($opcode === WEBSOCKET_OPCODE_CLOSE) {\n        $code = mt_rand(0, 5000);\n        $data = substr($data, -mt_rand(3, 125), 125);\n    }\n    $finish = !!mt_rand(0, 1);\n\n    // pack them\n    if (mt_rand(0, 1) || $opcode === WEBSOCKET_OPCODE_CLOSE) {\n        if ($opcode === WEBSOCKET_OPCODE_CLOSE) {\n            $frame = new CloseFrame;\n            $frame->code = $code;\n            $frame->reason = $data;\n        } else {\n            $frame = new Frame;\n            $frame->data = $data;\n        }\n        $frame->opcode = $opcode;\n        $frame->finish = $finish;\n        if (!mt_rand(0, 4)) {\n            unset($frame->data);\n            unset($frame->reason);\n            $data = '';\n        }\n        if (mt_rand(0, 1)) {\n            $packed = (string)$frame;\n        } else {\n            $packed = Frame::pack($frame);\n        }\n    } else {\n        $packed = Frame::pack($data, $opcode, $finish);\n    }\n\n    // unpack\n    $unpacked = Frame::unpack($packed);\n\n    // verify\n    if ($opcode === WEBSOCKET_OPCODE_CLOSE) {\n        Assert::same($unpacked->code, $code);\n        Assert::same($unpacked->reason, $data);\n        Assert::true($unpacked->finish == SWOOLE_WEBSOCKET_FLAG_FIN);\n    } else {\n        Assert::same($unpacked->data, $data);\n        Assert::same($unpacked->opcode, $opcode);\n        if ($opcode === WEBSOCKET_OPCODE_PING || $opcode === WEBSOCKET_OPCODE_PONG) {\n            Assert::true($unpacked->finish == SWOOLE_WEBSOCKET_FLAG_FIN);\n        } else {\n            Assert::same($unpacked->finish, $finish);\n        }\n\n    }\n}\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_websocket_server/ping.phpt",
    "content": "--TEST--\nswoole_websocket_server: test ping function\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Frame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $client->set(['open_websocket_ping_frame' => true]);\n        Assert::true($client->upgrade('/'));\n        $client->push('123456');\n        $frame = $client->recv();\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_PING);\n        Assert::true($frame->data == 'Hello World');\n        $frame = $client->recv();\n        Assert::true($frame->opcode == SWOOLE_WEBSOCKET_OPCODE_PING);\n        Assert::true($frame->data == '');\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n    ]);\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('message', function (Server $server, Frame $frame) {\n        $server->ping($frame->fd, 'Hello World');\n        $server->ping($frame->fd);\n    });\n\n    $server->start();\n};\n\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_websocket_server/pingloop.phpt",
    "content": "--TEST--\nswoole_websocket_server: ping loop\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst PING_INTERVAL = 100; // (ms), just for test, don't need to be so fast!\nconst PING_LOOP = 5;\n\n$count = 0;\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    for ($i = MAX_CONCURRENCY_MID; $i--;) {\n        go(function () use ($pm) {\n            $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            $cli->set([\n                'open_websocket_ping_frame' => true,\n                'open_websocket_pong_frame' => true,\n                'open_websocket_close_frame' => true,\n            ]);\n            $ret = $cli->upgrade('/');\n            Assert::assert($ret);\n            $loop = 0;\n            while ($response = $cli->recv(-1)) {\n                switch ($response->opcode) {\n                    case WEBSOCKET_OPCODE_PING:\n                        global $count;\n                        $count++;\n                        $loop++;\n                        if (mt_rand(0, 1)) {\n                            $pong = new Swoole\\WebSocket\\Frame;\n                            $pong->opcode = WEBSOCKET_OPCODE_PONG;\n                            $ret = $cli->push($pong);\n                        } else {\n                            $ret = $cli->push('', WEBSOCKET_OPCODE_PONG);\n                        }\n                        Assert::assert($ret);\n                        break;\n                    case WEBSOCKET_OPCODE_CLOSE:\n                        break 2;\n                    default:\n                        Assert::assert(0, 'never hear.');\n                }\n            }\n            Assert::same($loop, PING_LOOP);\n        });\n    }\n    Swoole\\Event::wait();\n    global $count;\n    Assert::same($count, PING_LOOP * MAX_CONCURRENCY_MID);\n    $pm->kill();\n    echo \"DONE\";\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $serv->on('workerStart', function (Swoole\\WebSocket\\Server  $server) use ($pm) {\n        $timer_id = Swoole\\Timer::tick(PING_INTERVAL, function () use ($server) {\n            foreach ($server->connections as $fd) {\n                if (mt_rand(0, 1)) {\n                    $ping = new Swoole\\WebSocket\\Frame;\n                    $ping->opcode = WEBSOCKET_OPCODE_PING;\n                    $server->push($fd, $ping);\n                } else {\n                    $server->push($fd, '', WEBSOCKET_OPCODE_PING);\n                }\n            }\n        });\n        Swoole\\Timer::after(PING_LOOP * PING_INTERVAL, function () use ($pm, $server, $timer_id) {\n            Swoole\\Timer::clear($timer_id);\n            foreach ($server->connections as $fd) {\n                $server->push($fd, new Swoole\\WebSocket\\CloseFrame);\n            }\n        });\n        $pm->wakeup();\n    });\n    $serv->on('open', function ($server, $req) { });\n    $serv->on('message', function ($server, Swoole\\WebSocket\\Frame $frame) {\n        Assert::same($frame->opcode, WEBSOCKET_OPCODE_PONG);\n    });\n    $serv->on('close', function ($server, $fd) { });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_websocket_server/pingloop_open_ping_pong_frame.phpt",
    "content": "--TEST--\nswoole_websocket_server: ping loop\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nconst PING_INTERVAL = 100; // (ms), just for test, don't need to be so fast!\nconst PING_LOOP = 5;\n\n$count = 0;\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    for ($i = MAX_CONCURRENCY_MID; $i--;) {\n        go(function () use ($pm) {\n            $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            $cli->set([\n                'open_websocket_ping_frame' => true,\n                'open_websocket_pong_frame' => true,\n                'open_websocket_close_frame' => true,\n            ]);\n            $ret = $cli->upgrade('/');\n            Assert::assert($ret);\n            $loop = 0;\n            while ($response = $cli->recv(-1)) {\n                switch ($response->opcode) {\n                    case WEBSOCKET_OPCODE_PING:\n                        Assert::same($response->data, 'ping');\n                        global $count;\n                        $count++;\n                        $loop++;\n                        if (mt_rand(0, 1)) {\n                            $pong = new Swoole\\WebSocket\\Frame;\n                            $pong->data = 'pong';\n                            $pong->opcode = WEBSOCKET_OPCODE_PONG;\n                            $ret = $cli->push($pong);\n                        } else {\n                            $ret = $cli->push('pong', WEBSOCKET_OPCODE_PONG);\n                        }\n                        Assert::assert($ret);\n                        break;\n                    case WEBSOCKET_OPCODE_CLOSE:\n                        break 2;\n                    default:\n                        Assert::assert(0, 'never hear.');\n                }\n            }\n            Assert::same($loop, PING_LOOP);\n        });\n    }\n    Swoole\\Event::wait();\n    global $count;\n    Assert::same($count, PING_LOOP * MAX_CONCURRENCY_MID);\n    $pm->kill();\n    echo \"DONE\";\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'open_websocket_ping_frame' => true,\n        'open_websocket_pong_frame' => true,\n    ]);\n    $serv->on('workerStart', function (Swoole\\WebSocket\\Server  $server) use ($pm) {\n        $timer_id = Swoole\\Timer::tick(PING_INTERVAL, function () use ($server) {\n            foreach ($server->connections as $fd) {\n                if (mt_rand(0, 1)) {\n                    $ping = new Swoole\\WebSocket\\Frame;\n                    $ping->data = 'ping';\n                    $ping->opcode = WEBSOCKET_OPCODE_PING;\n                    $server->push($fd, $ping);\n                } else {\n                    $server->push($fd, 'ping', WEBSOCKET_OPCODE_PING);\n                }\n            }\n        });\n        Swoole\\Timer::after(PING_LOOP * PING_INTERVAL, function () use ($pm, $server, $timer_id) {\n            Swoole\\Timer::clear($timer_id);\n            Swoole\\Coroutine::sleep(0.1); // wait pong\n            foreach ($server->connections as $fd) {\n                $server->push($fd, new Swoole\\WebSocket\\CloseFrame);\n            }\n        });\n        $pm->wakeup();\n    });\n    $serv->on('open', function ($server, $req) {\n    });\n    $atomic = new Swoole\\Atomic;\n    $serv->on('message', function ($server, Swoole\\WebSocket\\Frame $frame) use ($atomic) {\n        if ($frame->opcode === WEBSOCKET_OPCODE_PONG) {\n            Assert::same($frame->data, 'pong');\n            $atomic->add();\n        }\n    });\n    $serv->on('close', function ($server, $fd) {\n    });\n    $serv->start();\n    Assert::same($atomic->get(), PING_LOOP * MAX_CONCURRENCY_MID);\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_websocket_server/pingpong.phpt",
    "content": "--TEST--\nswoole_websocket_server: websocket ping pong (auto)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    go(function () use ($pm) {\n        $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->set([\n            'timeout' => 5,\n            'open_websocket_ping_frame' => true,\n            'open_websocket_pong_frame' => true,\n            'open_websocket_close_frame' => true,\n        ]);\n        $ret = $cli->upgrade('/');\n        Assert::assert($ret);\n        for ($i = 100; $i--;) {\n            $ping = new Swoole\\WebSocket\\Frame;\n            $ping->opcode = WEBSOCKET_OPCODE_PING;\n            $ret = $cli->push($ping);\n            Assert::assert($ret);\n            $pong = $cli->recv();\n            Assert::same($pong->opcode, WEBSOCKET_OPCODE_PONG);\n        }\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        // 'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('open', function ($swoole_server, $req) { });\n    $serv->on('message', function ($swoole_server, $frame) { });\n    $serv->on('close', function ($swoole_server, $fd) { });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_websocket_server/pingpong_open_ping_pong_frame.phpt",
    "content": "--TEST--\nswoole_websocket_server: websocket ping pong (auto)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    go(function () use ($pm) {\n        $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->set([\n            'timeout' => 5,\n            'open_websocket_ping_frame' => true,\n            'open_websocket_pong_frame' => true,\n            'open_websocket_close_frame' => true,\n        ]);\n        $ret = $cli->upgrade('/');\n        Assert::assert($ret);\n        for ($i = 100; $i--;) {\n            $ping = new Swoole\\WebSocket\\Frame;\n            $ping->opcode = WEBSOCKET_OPCODE_PING;\n            $ping->data = 'ping';\n            $ret = $cli->push($ping);\n            Assert::assert($ret);\n            $pong = $cli->recv();\n            Assert::same($pong->opcode, WEBSOCKET_OPCODE_PONG);\n            Assert::same($pong->data, 'pong');\n        }\n        $pm->kill();\n    });\n    Swoole\\Event::wait();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        // 'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'open_websocket_ping_frame' => true,\n        'open_websocket_pong_frame' => true,\n    ]);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('open', function ($swoole_server, $req) {\n    });\n    $atomic = new Swoole\\Atomic;\n    $serv->on('message', function (Swoole\\WebSocket\\Server  $server, Swoole\\WebSocket\\Frame $frame) use ($atomic) {\n        if ($frame->opcode === WEBSOCKET_OPCODE_PING) {\n            Assert::same($frame->data, 'ping');\n            $atomic->add();\n            $pongFrame = new Swoole\\WebSocket\\Frame;\n            $pongFrame->opcode = WEBSOCKET_OPCODE_PONG;\n            $pongFrame->data = 'pong';\n            $server->push($frame->fd, $pongFrame);\n        }\n    });\n    $serv->on('close', function ($swoole_server, $fd) {\n    });\n    $serv->start();\n    Assert::same($atomic->get(), 100);\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_websocket_server/query.phpt",
    "content": "--TEST--\nswoole_websocket_server: query\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->useConstantPorts = true;\n$pm->initFreePorts();\n\n$pm->parentFunc = function (int $pid) use ($pm) {\n    go(function() use ($pm) {\n        $cli = new Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $connected = $cli->upgrade('/?test=a&b=hello');\n        Assert::assert($connected);\n        $response = $cli->recv();\n        Assert::assert($response);\n        $json = json_decode($response->data, true);\n        Assert::assert(is_array($json));\n        Assert::same($json['test'], 'a');\n        Assert::same($json['b'], 'hello');\n    });\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        // 'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $serv->on('WorkerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('open', function (Swoole\\WebSocket\\Server  $serv, Swoole\\Http\\Request $req) {\n        $serv->push($req->fd, json_encode($req->get));\n    });\n    $serv->on('Message', function (Swoole\\WebSocket\\Server  $serv, Swoole\\WebSocket\\Frame $frame) {\n\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_websocket_server/recv_decode.phpt",
    "content": "--TEST--\nswoole_websocket_server: websocket server full test\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$data_list = [];\nfor ($i = MAX_REQUESTS; $i--;) {\n    $rand = get_safe_random(mt_rand(1, 128000));\n    if (mt_rand(0, 1)) {\n        $data_list[$i] = $i . '|' . WEBSOCKET_OPCODE_BINARY . '|' . $rand;\n    } else {\n        $data_list[$i] = $i . '|' . WEBSOCKET_OPCODE_TEXT . '|' . base64_encode($rand);\n    }\n}\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, $data_list) {\n    for ($c = MAX_CONCURRENCY_LOW; $c--;) {\n        go(function () use ($pm, $data_list) {\n            $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            $cli->set(['timeout' => 5]);\n            $ret = $cli->upgrade('/');\n            Assert::assert($ret);\n            foreach ($data_list as $data) {\n                if (mt_rand(0, 1)) {\n                    $frame = new Swoole\\WebSocket\\Frame;\n                    $frame->opcode = (int)explode('|', $data, 3)[1];\n                    $frame->data = $data;\n                    $ret = $cli->push($frame);\n                } else {\n                    $ret = $cli->push($data, (int)explode('|', $data, 3)[1]);\n                }\n                if (!Assert::assert($ret)) {\n                    var_dump(swoole_strerror(swoole_last_error()));\n                } else {\n                    $ret = $cli->recv();\n                    unset($data_list[$ret->data]);\n                }\n            }\n            Assert::assert(empty($data_list));\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        // 'worker_num' => 1,\n        'log_file' => '/dev/null'\n    ]);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('message', function (Swoole\\WebSocket\\Server  $serv, Swoole\\WebSocket\\Frame $recv_frame) {\n        global $data_list;\n        list($id, $opcode) = explode('|', $recv_frame->data, 3);\n        if (!Assert::assert($recv_frame->finish)) {\n            return;\n        }\n        if (!Assert::assert($recv_frame->opcode === (int)$opcode)) {\n            return;\n        }\n        if (!Assert::assert($recv_frame->data === $data_list[$id])) {\n            var_dump($recv_frame->data);\n            var_dump($data_list[$id]);\n            return;\n        }\n        if (mt_rand(0, 1)) {\n            $send_frame = new Swoole\\WebSocket\\Frame;\n            $send_frame->data = $id;\n            $serv->push($recv_frame->fd, $send_frame);\n        } else {\n            $serv->push($recv_frame->fd, $id);\n        }\n    });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_websocket_server/send_close_frame.phpt",
    "content": "--TEST--\nswoole_websocket_server: send close frame will not close connection\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\nuse Swoole\\Coroutine\\Http\\Client;\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\WebSocket\\Frame;\nuse Swoole\\WebSocket\\CloseFrame;\nuse SwooleTest\\ProcessManager as ProcessManager;\n\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm) {\n    Co\\run(function () use ($pm) {\n        $client = new Client('127.0.0.1', $pm->getFreePort());\n        $ret = $client->upgrade('/');\n        $client->push('aaa');\n        $client->push('lalalala');\n    });\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set([\n        'worker_num' => 1,\n        'package_max_length' => 100 * 1024 * 1024,\n    ]);\n\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n\n    $server->on('message', function (Server $server, Frame $frame) use ($pm) {\n        if ($frame->data == 'aaa') {\n            $close = new CloseFrame();\n            $close->opcode = SWOOLE_WEBSOCKET_OPCODE_CLOSE;\n            $close->code = SWOOLE_WEBSOCKET_CLOSE_NORMAL;\n            $close->reason = 'hahahhah';\n            Assert::true($server->push($frame->fd, $close));\n        } else {\n            var_dump($frame->data);\n        }\n    });\n\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nstring(8) \"lalalala\"\n"
  },
  {
    "path": "tests/swoole_websocket_server/send_encode.phpt",
    "content": "--TEST--\nswoole_websocket_server: websocket server full test\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$count = MAX_CONCURRENCY_MID;\n$data_list = [];\nfor ($i = MAX_REQUESTS; $i--;) {\n    $rand = get_safe_random(mt_rand(1, 128000));\n    if (mt_rand(0, 1)) {\n        $data_list[$i] = $i . '|' . WEBSOCKET_OPCODE_BINARY . '|' . $rand;\n    } else {\n        $data_list[$i] = $i . '|' . WEBSOCKET_OPCODE_TEXT . '|' . base64_encode($rand);\n    }\n}\n$pm = new ProcessManager;\n$pm->parentFunc = function (int $pid) use ($pm, &$count, $data_list) {\n    for ($c = $count; $c--;) {\n        go(function () use ($pm, &$count, $data_list) {\n            $cli = new \\Swoole\\Coroutine\\Http\\Client('127.0.0.1', $pm->getFreePort());\n            $cli->set(['timeout' => 5]);\n            $ret = $cli->upgrade('/');\n            Assert::assert($ret);\n            while (($frame = $cli->recv())) {\n                /**@var $frame Swoole\\WebSocket\\Frame */\n                list($id, $opcode) = explode('|', $frame->data, 3);\n                Assert::assert($frame->finish);\n                Assert::same($frame->opcode, (int)$opcode);\n                Assert::same($frame->data, $data_list[$id]);\n                if (Assert::true(isset($data_list[$id]))) {\n                    unset($data_list[$id]);\n                }\n                if (empty($data_list)) {\n                    break;\n                }\n            }\n            if (Assert::assert(empty($data_list))) {\n                $count--;\n            }\n        });\n    }\n    Swoole\\Event::wait();\n    Assert::same($count, 0);\n    echo \"complete\\n\";\n    $pm->kill();\n};\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        // 'worker_num' => 1,\n        'log_file' => '/dev/null',\n        'send_yield' => true,\n        'send_timeout' => 10\n    ]);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('open', function (Swoole\\WebSocket\\Server  $serv, Swoole\\Http\\Request $req) {\n        global $data_list;\n        foreach ($data_list as $data) {\n            $opcode = (int)explode('|', $data, 3)[1];\n            if (mt_rand(0, 1)) {\n                $frame = new Swoole\\WebSocket\\Frame;\n                $frame->opcode = $opcode;\n                $frame->data = $data;\n                $ret = $serv->push($req->fd, $frame);\n            } else {\n                $ret = $serv->push($req->fd, $data, $opcode);\n            }\n            if (!Assert::assert($ret)) {\n                var_dump($serv->getLastError());\n            }\n        }\n    });\n    $serv->on('message', function (Swoole\\WebSocket\\Server  $serv, Swoole\\WebSocket\\Frame $frame) { });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\ncomplete\n"
  },
  {
    "path": "tests/swoole_websocket_server/send_encode_async.phpt",
    "content": "--TEST--\nswoole_websocket_server: websocket server full test\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$data_list = [];\nfor ($i = MAX_REQUESTS; $i--;) {\n    $rand = get_safe_random(mt_rand(1, 128000));\n    if (mt_rand(0, 1)) {\n        $data_list[$i] = $i . '|' . WEBSOCKET_OPCODE_BINARY . '|' . $rand;\n    } else {\n        $data_list[$i] = $i . '|' . WEBSOCKET_OPCODE_TEXT . '|' . base64_encode($rand);\n    }\n}\n\n$pm = new ProcessManager;\n\n$pm->parentFunc = function (int $pid) use ($pm, $data_list) {\n    for ($c = MAX_CONCURRENCY_LOW; $c--;) {\n        go(function () use ($pm) {\n            $cli = new Co\\http\\client('127.0.0.1', $pm->getFreePort());\n            $cli->set(['timeout' => -1]);\n            $ret = $cli->upgrade('/');\n            if ($ret == false) {\n                die(\"error=\" . $cli->errCode);\n            }\n            global $data_list;\n            $cli_data_list = $data_list;\n\n            while (true) {\n                $frame = $cli->recv();\n                list($id, $opcode) = explode('|', $frame->data, 3);\n                Assert::assert($frame->finish);\n                Assert::same($frame->opcode, (int)$opcode);\n                Assert::same($frame->data, $cli_data_list[$id]);\n                unset($cli_data_list[$id]);\n                if (empty($cli_data_list)) {\n                    break;\n                }\n            }\n            $cli->close();\n        });\n    }\n    Swoole\\Event::wait();\n    $pm->kill();\n};\n\n$pm->childFunc = function () use ($pm) {\n    $serv = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SERVER_MODE_RANDOM);\n    $serv->set([\n        'worker_num' => 1,\n        'log_file' => TEST_LOG_FILE,\n        'send_yield' => true,\n        'send_timeout' => 2,\n    ]);\n    $serv->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $serv->on('open', function (Swoole\\WebSocket\\Server  $serv, Swoole\\Http\\Request $req) {\n        global $data_list;\n        foreach ($data_list as $data) {\n            $opcode = (int)explode('|', $data, 3)[1];\n            if (mt_rand(0, 1)) {\n                $frame = new Swoole\\WebSocket\\Frame;\n                $frame->opcode = $opcode;\n                $frame->data = $data;\n                $ret = $serv->push($req->fd, $frame);\n            } else {\n                $ret = $serv->push($req->fd, $data, $opcode);\n            }\n            if (!Assert::assert($ret)) {\n                var_dump($serv->getLastError());\n            }\n        }\n    });\n    $serv->on('message', function (Swoole\\WebSocket\\Server  $serv, Swoole\\WebSocket\\Frame $frame) { });\n    $serv->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/swoole_websocket_server/set_cookie_on_before_handshake_response.phpt",
    "content": "--TEST--\nswoole_websocket_server: websocket server set cookie on beforeHandshakeResponse (#3270)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$pm = new ProcessManager;\n$pm->initFreePorts();\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $cli = new Co\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        if (Assert::true($cli->upgrade('/'))) {\n            Assert::same($cli->set_cookie_headers, [\n                'abc=def'\n            ]);\n        }\n    });\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('beforeHandShakeResponse', function (Server $server, Request $request, Response $response) {\n        $response->cookie('abc', 'def');\n    });\n    $server->on('message', function () { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_websocket_server/set_cookie_on_handshake.phpt",
    "content": "--TEST--\nswoole_websocket_server: websocket server set cookie on handshake (#3270)\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n$pm = new ProcessManager;\n$pm->initFreePorts();\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $cli = new Co\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        if (Assert::true($cli->upgrade('/'))) {\n            Assert::same($cli->headers['x-asdf'], 'asdf');\n            Assert::same($cli->set_cookie_headers, [\n                'foo=bar',\n                'abc=def'\n            ]);\n        }\n    });\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Swoole\\WebSocket\\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    $server->on('handshake', function (\\Swoole\\Http\\Request $request, \\Swoole\\Http\\Response $response) {\n        $secWebSocketKey = $request->header['sec-websocket-key'];\n        $patten = '#^[+/0-9A-Za-z]{21}[AQgw]==$#';\n        if (0 === preg_match($patten, $secWebSocketKey) || 16 !== strlen(base64_decode($secWebSocketKey))) {\n            $response->end();\n            return false;\n        }\n        $key = base64_encode(sha1(\n            $request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',\n            true\n        ));\n        $headers = [\n            'Upgrade' => 'websocket',\n            'Connection' => 'Upgrade',\n            'Sec-WebSocket-Accept' => $key,\n            'Sec-WebSocket-Version' => '13',\n            'Set-Cookie' => 'foo=bar',\n            'X-asdf' => 'asdf'\n        ];\n        // WebSocket connection to 'ws://127.0.0.1:9502/'\n        // failed: Error during WebSocket handshake:\n        // Response must not include 'Sec-WebSocket-Protocol' header if not present in request: websocket\n        if (isset($request->header['sec-websocket-protocol'])) {\n            $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol'];\n        }\n        foreach ($headers as $key => $val) {\n            $response->header($key, $val);\n        }\n        $response->cookie('abc', 'def');\n        $response->status(101);\n        $response->end();\n        return true;\n    });\n    $server->on('message', function () { });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/swoole_websocket_server/websocket_compress_on_handshake.phpt",
    "content": "--TEST--\nswoole_websocket_server: websocket compression with handshake\n--SKIPIF--\n<?php require __DIR__ . '/../include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '/../include/bootstrap.php';\n\nuse Swoole\\WebSocket\\Server;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\n\n$pm = new ProcessManager;\n$pm->initFreePorts();\n$pm->parentFunc = function () use ($pm) {\n    Co\\run(function () use ($pm) {\n        $cli = new Co\\Http\\Client('127.0.0.1', $pm->getFreePort());\n        $cli->setHeaders(['Sec-WebSocket-Extensions' => 'permessage-deflate; client_max_window_bits']);\n        if (Assert::true($cli->upgrade('/'))) {\n            Assert::contains($cli->headers['sec-websocket-extensions'], 'permessage-deflate');\n        }\n    });\n    $pm->kill();\n    echo \"DONE\\n\";\n};\n$pm->childFunc = function () use ($pm) {\n    $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);\n    $server->set(['websocket_compression' => true]);\n    $server->on('workerStart', function () use ($pm) {\n        $pm->wakeup();\n    });\n    // test with Chrome\n    $server->on('request', function (Request $request, Response $response) use ($pm) {\n        $port = $pm->getFreePort();\n        $response->end(<<<HTML\n<script>\nvar wsServer = 'ws://127.0.0.1:{$port}';\nvar websocket = new WebSocket(wsServer);\nwebsocket.onopen = function (evt) {\n\tconsole.log(\"Connected to WebSocket server.\");\n};\n\nwebsocket.onclose = function (evt) {\n\tconsole.log(\"Disconnected\");\n};\n\nwebsocket.onmessage = function (evt) {\n\tconsole.log('Retrieved data from server: ' + evt.data);\n};\n\nwebsocket.onerror = function (evt, e) {\n\tconsole.log('Error occured: ' + evt.data);\n};\n</script>\nHTML);\n    });\n    $server->on('handshake', function (Request $request, Response $response) {\n        $secWebSocketKey = $request->header['sec-websocket-key'];\n        $patten = '#^[+/0-9A-Za-z]{21}[AQgw]==$#';\n        if (0 === preg_match($patten, $secWebSocketKey) || 16 !== strlen(base64_decode($secWebSocketKey))) {\n            $response->end();\n            return false;\n        }\n        $key = base64_encode(sha1(\n            $request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',\n            true\n        ));\n        $headers = [\n            'Upgrade' => 'websocket',\n            'Connection' => 'Upgrade',\n            'Sec-WebSocket-Accept' => $key,\n            'Sec-WebSocket-Version' => '13',\n        ];\n        if (isset($request->header['sec-websocket-protocol'])) {\n            $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol'];\n        }\n        foreach ($headers as $key => $val) {\n            $response->header($key, $val);\n        }\n        $response->status(101);\n        $response->end();\n        return true;\n    });\n    $server->on('message', function ($serv, $frame) {\n        $serv->push($frame->fd, \"hello world\");\n    });\n    $server->start();\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\nDONE\n"
  },
  {
    "path": "tests/template",
    "content": "--TEST--\n{{test_name}}: {{test_intro}}\n--SKIPIF--\n<?php require __DIR__ . '{{dir_deep}}/include/skipif.inc'; ?>\n--FILE--\n<?php\nrequire __DIR__ . '{{dir_deep}}/include/bootstrap.php';\n$pm = new SwooleTest\\ProcessManager;\n$pm->parentFunc = function () use ($pm) {\n\n};\n$pm->childFunc = function () use ($pm) {\n\n};\n$pm->childFirst();\n$pm->run();\n?>\n--EXPECT--\n"
  },
  {
    "path": "tests/test.sql",
    "content": "/*\n Navicat Premium Data Transfer\n\n Source Server         : localhost\n Source Server Type    : MySQL\n Source Server Version : 80011\n Source Host           : localhost:3306\n Source Schema         : test\n\n Target Server Type    : MySQL\n Target Server Version : 80011\n File Encoding         : 65001\n\n Date: 14/09/2018 16:13:17\n*/\n\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\n-- ----------------------------\n-- Table structure for ckl\n-- ----------------------------\nDROP TABLE IF EXISTS `ckl`;\nCREATE TABLE `ckl` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `domain` varchar(128) NOT NULL,\n  `path` varchar(128) NOT NULL,\n  `name` varchar(32) NOT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;\n\n-- ----------------------------\n-- Records of ckl\n-- ----------------------------\nBEGIN;\nINSERT INTO `ckl` VALUES (1, 'www.baidu.com', '/search', 'baidu');\nINSERT INTO `ckl` VALUES (2, 'www.taobao.com', '/search', 'taobao');\nINSERT INTO `ckl` VALUES (3, 'www.qq.com', '/search', 'qq');\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for custom\n-- ----------------------------\nDROP TABLE IF EXISTS `custom`;\nCREATE TABLE `custom` (\n  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,\n  `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;\n\n-- ----------------------------\n-- Table structure for numbers\n-- ----------------------------\nDROP TABLE IF EXISTS `numbers`;\nCREATE TABLE `numbers` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `tinyint` tinyint(4) NOT NULL,\n  `utinyint` tinyint(255) unsigned NOT NULL,\n  `smallint` smallint(6) NOT NULL,\n  `usmallint` smallint(5) unsigned NOT NULL,\n  `mediumint` mediumint(9) NOT NULL,\n  `umediumint` mediumint(8) unsigned NOT NULL,\n  `int` int(11) NOT NULL,\n  `uint` int(10) unsigned NOT NULL,\n  `bigint` bigint(20) NOT NULL,\n  `ubigint` bigint(20) unsigned NOT NULL,\n  `float` float NOT NULL,\n  `double` double NOT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;\n\n-- ----------------------------\n-- Records of numbers\n-- ----------------------------\nBEGIN;\nINSERT INTO `numbers` VALUES (1, 127, 255, 32767, 65535, 8388607, 16777215, 2147483647, 4294967294, 9223372036854775807, 18446744073709551615, 1.23457, 1.2345678901234567);\nINSERT INTO `numbers` VALUES (2, -128, 123, -32768, 12345, -8388608, 123456, -2147483648, 123456, -9223372036854775808, 123456, -1.23457, -1.2345678901234567);\nINSERT INTO `numbers` VALUES (3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.23, 1.23);\nCOMMIT;\n\n-- ----------------------------\n-- Table structure for userinfo\n-- ----------------------------\nDROP TABLE IF EXISTS `userinfo`;\nCREATE TABLE `userinfo` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `name` varchar(40) NOT NULL,\n  `level` int(11) NOT NULL,\n  `passwd` varchar(40),\n  `regtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `big_n` bigint(20) NOT NULL,\n  `data` json NOT NULL,\n  `lastlogin_ip` int(11) NOT NULL,\n  `price` double NOT NULL,\n  `mdate` date NOT NULL,\n  `mtime` time NOT NULL,\n  `mdatetime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  `year` year(4) NOT NULL DEFAULT '1970',\n  `int8_t` tinyint(11) NOT NULL,\n  `mshort` smallint(6) NOT NULL,\n  `mtext` text,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=144 DEFAULT CHARSET=utf8;\n\n-- ----------------------------\n-- Records of userinfo\n-- ----------------------------\nBEGIN;\nINSERT INTO `userinfo` VALUES (1, 'jack', 199, 'xuyou', '2015-01-02 02:00:00', 999000, 'null', 1270, 0.22, '1970-01-01', '21:52:33', '2018-04-17 04:16:20', 1989, 127, 32767, '');\nINSERT INTO `userinfo` VALUES (2, 'jack', 0, 'xuyou', '2016-05-20 00:00:00', 0, '{\\\"a\\\": 123}', 0, 0, '1970-01-01', '00:00:00', '1970-01-01 01:03:00', 1999, 0, 0, NULL);\nINSERT INTO `userinfo` VALUES (3, '韩天峰', 0, 'xuyou', '2016-05-20 19:08:47', 0, 'null', 0, 0, '1970-01-01', '00:00:00', '1970-01-01 00:00:00', 0000, 0, 0, '');\nINSERT INTO `userinfo` VALUES (4, 'jack', 11, 'xuyou', '2016-05-20 19:17:33', 0, 'null', 0, 0, '1970-01-01', '00:00:00', '1970-01-01 00:00:00', 0000, 0, 0, NULL);\nINSERT INTO `userinfo` VALUES (5, 'rango22', 0, '123456', '2016-07-19 13:31:37', 0, 'null', 0, 0, '1970-01-01', '00:00:00', '1970-01-01 00:00:00', 0000, 0, 0, '');\nINSERT INTO `userinfo` VALUES (6, 'hello', 99, NULL, '2017-07-03 19:37:37', 19999991, 'null', 7775533, 256.33, '2017-12-13', '09:51:29', '1970-01-01 00:00:00', 2015, 127, 32321, '我们都是中国人，你很好吗？');\nINSERT INTO `userinfo` VALUES (7, 'twosee', 0, NULL, '2017-07-03 19:37:49', 99999999, '{}', 0, 0, '1997-06-04', '01:02:03', '1997-06-04 04:05:06.0708', 0000, 0, 0, '');\nINSERT INTO `userinfo` VALUES (8, 'hello', 99, '123456', '2018-04-09 15:48:00', 99999999, 'null', 0, 0, '1970-01-01', '00:00:00', '1970-01-01 00:00:00', 0000, 0, 0, NULL);\nCOMMIT;\n\nSET FOREIGN_KEY_CHECKS = 1;\n"
  },
  {
    "path": "thirdparty/boost/asm/LICENSE",
    "content": "Boost Software License - Version 1.0 - August 17th, 2003\n\nPermission is hereby granted, free of charge, to any person or organization\nobtaining a copy of the software and accompanying documentation covered by\nthis license (the \"Software\") to use, reproduce, display, distribute,\nexecute, and transmit the Software, and to prepare derivative works of the\nSoftware, and to permit third-parties to whom the Software is furnished to\ndo so, all subject to the following:\n\nThe copyright notices in the Software and this entire statement, including\nthe above license grant, this restriction and the following disclaimer,\nmust be included in all copies of the Software, in whole or in part, and\nall derivative works of the Software, unless such copies or derivative\nworks are solely in the form of machine-executable object code generated by\na source language processor.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT\nSHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE\nFOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "thirdparty/boost/asm/combined.S",
    "content": "#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)\n    #if defined(__x86_64__)\n        #include \"make_x86_64_sysv_elf_gas.S\"\n        #include \"jump_x86_64_sysv_elf_gas.S\"\n    #elif defined(__ppc64__)\n        #include \"make_ppc64_sysv_elf_gas.S\"\n        #include \"jump_ppc64_sysv_elf_gas.S\"\n    #elif defined(__arm64__) || defined(__aarch64__)\n        #include \"make_arm64_aapcs_elf_gas.S\"\n        #include \"jump_arm64_aapcs_elf_gas.S\"\n    #elif defined(__loongarch64)\n        #include \"make_loongarch64_sysv_elf_gas.S\"\n        #include \"jump_loongarch64_sysv_elf_gas.S\"\n    #else\n        #error \"No arch's\"\n    #endif\n#elif defined(__MACH__)\n    #include \"make_combined_sysv_macho_gas.S\"\n    #include \"jump_combined_sysv_macho_gas.S\"\n#else\n    #error \"not supports\"\n#endif\n"
  },
  {
    "path": "thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S",
    "content": "/*\n       Copyright Edward Nevill + Oliver Kowalke 2015\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n/*******************************************************\n *                                                     *\n *  -------------------------------------------------  *\n *  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  *\n *  -------------------------------------------------  *\n *  | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c|  *\n *  -------------------------------------------------  *\n *  |    d8     |    d9     |    d10    |    d11    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  8  |  9  |  10 |  11 |  12 |  13 |  14 |  15 |  *\n *  -------------------------------------------------  *\n *  | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c|  *\n *  -------------------------------------------------  *\n *  |    d12    |    d13    |    d14    |    d15    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  16 |  17 |  18 |  19 |  20 |  21 |  22 |  23 |  *\n *  -------------------------------------------------  *\n *  | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c|  *\n *  -------------------------------------------------  *\n *  |    x19    |    x20    |    x21    |    x22    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  24 |  25 |  26 |  27 |  28 |  29 |  30 |  31 |  *\n *  -------------------------------------------------  *\n *  | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c|  *\n *  -------------------------------------------------  *\n *  |    x23    |    x24    |    x25    |    x26    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  32 |  33 |  34 |  35 |  36 |  37 |  38 |  39 |  *\n *  -------------------------------------------------  *\n *  | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c|  *\n *  -------------------------------------------------  *\n *  |    x27    |    x28    |    FP     |     LR    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  40 |  41 |  42 | 43  |           |           |  *\n *  -------------------------------------------------  *\n *  | 0xa0| 0xa4| 0xa8| 0xac|           |           |  *\n *  -------------------------------------------------  *\n *  |     PC    |   align   |           |           |  *\n *  -------------------------------------------------  *\n *                                                     *\n *******************************************************/\n\n.file \"jump_arm64_aapcs_elf_gas.S\"\n.text\n.align  2\n.global swoole_jump_fcontext\n.type   swoole_jump_fcontext, %function\nswoole_jump_fcontext:\n    # prepare stack for GP + FPU\n    sub  sp, sp, #0xb0\n\n    # save d8 - d15\n    stp  d8,  d9,  [sp, #0x00]\n    stp  d10, d11, [sp, #0x10]\n    stp  d12, d13, [sp, #0x20]\n    stp  d14, d15, [sp, #0x30]\n\n    # save x19-x30\n    stp  x19, x20, [sp, #0x40]\n    stp  x21, x22, [sp, #0x50]\n    stp  x23, x24, [sp, #0x60]\n    stp  x25, x26, [sp, #0x70]\n    stp  x27, x28, [sp, #0x80]\n    stp  x29, x30, [sp, #0x90]\n\n    # save LR as PC\n    str  x30, [sp, #0xa0]\n\n    # store RSP (pointing to context-data) in X0\n    mov  x4, sp\n\n    # restore RSP (pointing to context-data) from X1\n    mov  sp, x0\n\n    # load d8 - d15\n    ldp  d8,  d9,  [sp, #0x00]\n    ldp  d10, d11, [sp, #0x10]\n    ldp  d12, d13, [sp, #0x20]\n    ldp  d14, d15, [sp, #0x30]\n\n    # load x19-x30\n    ldp  x19, x20, [sp, #0x40]\n    ldp  x21, x22, [sp, #0x50]\n    ldp  x23, x24, [sp, #0x60]\n    ldp  x25, x26, [sp, #0x70]\n    ldp  x27, x28, [sp, #0x80]\n    ldp  x29, x30, [sp, #0x90]\n\n    # return transfer_t from jump\n    # pass transfer_t as first arg in context function\n    # X0 == FCTX, X1 == DATA\n    mov x0, x4\n\n    # load pc\n    ldr  x4, [sp, #0xa0]\n\n    # restore stack from GP + FPU\n    add  sp, sp, #0xb0\n\n    ret x4\n.size   swoole_jump_fcontext,.-swoole_jump_fcontext\n# Mark that we don't need executable stack.\n.section .note.GNU-stack,\"\",%progbits\n"
  },
  {
    "path": "thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S",
    "content": "/*\n         Copyright Edward Nevill + Oliver Kowalke 2015\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n/*******************************************************\n *                                                     *\n *  -------------------------------------------------  *\n *  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  *\n *  -------------------------------------------------  *\n *  | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c|  *\n *  -------------------------------------------------  *\n *  |    d8     |    d9     |    d10    |    d11    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  8  |  9  |  10 |  11 |  12 |  13 |  14 |  15 |  *\n *  -------------------------------------------------  *\n *  | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c|  *\n *  -------------------------------------------------  *\n *  |    d12    |    d13    |    d14    |    d15    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  16 |  17 |  18 |  19 |  20 |  21 |  22 |  23 |  *\n *  -------------------------------------------------  *\n *  | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c|  *\n *  -------------------------------------------------  *\n *  |    x19    |    x20    |    x21    |    x22    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  24 |  25 |  26 |  27 |  28 |  29 |  30 |  31 |  *\n *  -------------------------------------------------  *\n *  | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c|  *\n *  -------------------------------------------------  *\n *  |    x23    |    x24    |    x25    |    x26    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  32 |  33 |  34 |  35 |  36 |  37 |  38 |  39 |  *\n *  -------------------------------------------------  *\n *  | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c|  *\n *  -------------------------------------------------  *\n *  |    x27    |    x28    |    FP     |     LR    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  40 |  41 |  42 | 43  |           |           |  *\n *  -------------------------------------------------  *\n *  | 0xa0| 0xa4| 0xa8| 0xac|           |           |  *\n *  -------------------------------------------------  *\n *  |     PC    |   align   |           |           |  *\n *  -------------------------------------------------  *\n *                                                     *\n *******************************************************/\n\n.text\n.globl _swoole_jump_fcontext\n.balign 16\n_swoole_jump_fcontext:\n    ; prepare stack for GP + FPU\n    sub  sp, sp, #0xb0\n\n    ; save d8 - d15\n    stp  d8,  d9,  [sp, #0x00]\n    stp  d10, d11, [sp, #0x10]\n    stp  d12, d13, [sp, #0x20]\n    stp  d14, d15, [sp, #0x30]\n\n    ; save x19-x30\n    stp  x19, x20, [sp, #0x40]\n    stp  x21, x22, [sp, #0x50]\n    stp  x23, x24, [sp, #0x60]\n    stp  x25, x26, [sp, #0x70]\n    stp  x27, x28, [sp, #0x80]\n    stp  fp,  lr,  [sp, #0x90]\n\n    ; save LR as PC\n    str  lr, [sp, #0xa0]\n\n    ; store RSP (pointing to context-data) in X0\n    mov  x4, sp\n\n    ; restore RSP (pointing to context-data) from X1\n    mov  sp, x0\n\n    ; load d8 - d15\n    ldp  d8,  d9,  [sp, #0x00]\n    ldp  d10, d11, [sp, #0x10]\n    ldp  d12, d13, [sp, #0x20]\n    ldp  d14, d15, [sp, #0x30]\n\n    ; load x19-x30\n    ldp  x19, x20, [sp, #0x40]\n    ldp  x21, x22, [sp, #0x50]\n    ldp  x23, x24, [sp, #0x60]\n    ldp  x25, x26, [sp, #0x70]\n    ldp  x27, x28, [sp, #0x80]\n    ldp  fp,  lr,  [sp, #0x90]\n\n    ; return transfer_t from jump\n    ; pass transfer_t as first arg in context function\n    ; X0 == FCTX, X1 == DATA\n    mov x0, x4\n\n    ; load pc\n    ldr  x4, [sp, #0xa0]\n\n    ; restore stack from GP + FPU\n    add  sp, sp, #0xb0\n\n    ret x4\n"
  },
  {
    "path": "thirdparty/boost/asm/jump_combined_sysv_macho_gas.S",
    "content": "/*\n            Copyright Sergue E. Leontiev 2013.\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n\n// Stub file for universal binary\n\n#if defined(__x86_64__)\n    #include \"jump_x86_64_sysv_macho_gas.S\"\n#elif defined(__ppc64__)\n    #include \"jump_ppc64_sysv_macho_gas.S\"\n#elif defined(__arm64__)\n    #include \"jump_arm64_aapcs_macho_gas.S\"\n#else\n    #error \"No arch's\"\n#endif\n"
  },
  {
    "path": "thirdparty/boost/asm/jump_loongarch64_sysv_elf_gas.S",
    "content": "/*******************************************************\n *                                                     *\n *  -------------------------------------------------  *\n *  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  *\n *  -------------------------------------------------  *\n *  |     0     |     8     |    16     |     24    |  *\n *  -------------------------------------------------  *\n *  |    FS0    |    FS1    |    FS2    |    FS3    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  8  |  9  |  10 |  11 |  12 |  13 |  14 |  15 |  *\n *  -------------------------------------------------  *\n *  |     32    |    40     |     48    |     56    |  *\n *  -------------------------------------------------  *\n *  |    FS4    |    FS5    |    FS6    |    FS7    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  16 |  17 |  18 |  19 |  20 |  21 |  22 |  23 |  *\n *  -------------------------------------------------  *\n *  |     64    |    72     |     80    |     88    |  *\n *  -------------------------------------------------  *\n *  |    S0     |    S1     |     S2    |     S3    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  24 |  25 |  26 |  27 |  28 |  29 |  30 |  31 |  *\n *  -------------------------------------------------  *\n *  |  96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 |  *\n *  -------------------------------------------------  *\n *  |    S4     |    S5     |     S6    |     S7    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  32 |  33 |  34 |  35 |  36 |  37 |  38 |  39 |  *\n *  -------------------------------------------------  *\n *  | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 |  *\n *  -------------------------------------------------  *\n *  |    S8     |    FP     |     RA    |     PC    |  *\n *  -------------------------------------------------  *\n *                                                     *\n * *****************************************************/\n\n.file \"jump_loongarch64_sysv_elf_gas.S\"\n.text\n.globl swoole_jump_fcontext\n.align 2\n.type swoole_jump_fcontext,@function\nswoole_jump_fcontext:\n    # reserve space on stack\n    addi.d  $sp, $sp, -160\n\n    # save fs0 - fs7\n    fst.d  $fs0, $sp, 0\n    fst.d  $fs1, $sp, 8\n    fst.d  $fs2, $sp, 16\n    fst.d  $fs3, $sp, 24\n    fst.d  $fs4, $sp, 32\n    fst.d  $fs5, $sp, 40\n    fst.d  $fs6, $sp, 48\n    fst.d  $fs7, $sp, 56\n\n    # save s0 - s8, fp, ra\n    st.d  $s0, $sp, 64\n    st.d  $s1, $sp, 72\n    st.d  $s2, $sp, 80\n    st.d  $s3, $sp, 88\n    st.d  $s4, $sp, 96\n    st.d  $s5, $sp, 104\n    st.d  $s6, $sp, 112\n    st.d  $s7, $sp, 120\n    st.d  $s8, $sp, 128\n    st.d  $fp, $sp, 136\n    st.d  $ra, $sp, 144\n\n    # save RA as PC\n    st.d  $ra, $sp, 152\n\n    # store SP (pointing to context-data) in A2\n    move  $a2, $sp\n\n    # restore SP (pointing to context-data) from A0\n    move  $sp, $a0\n\n    # load fs0 - fs7\n    fld.d  $fs0, $sp, 0\n    fld.d  $fs1, $sp, 8\n    fld.d  $fs2, $sp, 16\n    fld.d  $fs3, $sp, 24\n    fld.d  $fs4, $sp, 32\n    fld.d  $fs5, $sp, 40\n    fld.d  $fs6, $sp, 48\n    fld.d  $fs7, $sp, 56\n\n    #load s0 - s7\n    ld.d  $s0, $sp, 64\n    ld.d  $s1, $sp, 72\n    ld.d  $s2, $sp, 80\n    ld.d  $s3, $sp, 88\n    ld.d  $s4, $sp, 96\n    ld.d  $s5, $sp, 104\n    ld.d  $s6, $sp, 112\n    ld.d  $s7, $sp, 120\n    ld.d  $s8, $sp, 128\n    ld.d  $fp, $sp, 136\n    ld.d  $ra, $sp, 144\n\n    # return transfer_t from jump\n    # pass transfer_t as first arg in context function\n    # a0 == FCTX, a1 == DATA\n    move  $a0, $a2\n\n    # load PC\n    ld.d  $a2, $sp, 152\n\n    # restore stack\n    addi.d  $sp, $sp, 160\n\n    # jump to context\n    jr  $a2\n.size swoole_jump_fcontext, .-swoole_jump_fcontext\n\n/* Mark that we don't need executable stack.  */\n.section .note.GNU-stack,\"\",%progbits\n"
  },
  {
    "path": "thirdparty/boost/asm/jump_mips64_n64_elf_gas.S",
    "content": "/*\n            Copyright Jiaxun Yang 2018.\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n\n/*******************************************************\n *                                                     *\n *  -------------------------------------------------  *\n *  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  *\n *  -------------------------------------------------  *\n *  |     0     |     8     |    16     |     24    |  *\n *  -------------------------------------------------  *\n *  |    F24    |    F25    |    F26    |    F27    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  8  |  9  |  10 |  11 |  12 |  13 |  14 |  15 |  *\n *  -------------------------------------------------  *\n *  |     32    |    40     |     48    |     56    |  *\n *  -------------------------------------------------  *\n *  |    F28    |    F29    |    F30    |    F31    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  16 |  17 |  18 |  19 |  20 |  21 |  22 |  23 |  *\n *  -------------------------------------------------  *\n *  |     64    |    72     |     80    |     88    |  *\n *  -------------------------------------------------  *\n *  |    S0     |    S1     |     S2    |     S3    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  24 |  25 |  26 |  27 |  28 |  29 |  30 |  31 |  *\n *  -------------------------------------------------  *\n *  |  96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 |  *\n *  -------------------------------------------------  *\n *  |    S4     |    S5     |     S6    |     S7    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  32 |  33 |  34 |  35 |  36 |  37 |  38 |  39 |  *\n *  -------------------------------------------------  *\n *  | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 |  *\n *  -------------------------------------------------  *\n *  |    FP     |    GP     |     RA    |     PC    |  *\n *  -------------------------------------------------  *\n *                                                     *\n * *****************************************************/\n\n.file \"jump_mips64_n64_elf_gas.S\"\n.text\n.globl swoole_jump_fcontext\n.align 3\n.type swoole_jump_fcontext,@function\n.ent swoole_jump_fcontext\nswoole_jump_fcontext:\n    # reserve space on stack\n    daddiu $sp, $sp, -160\n\n    sd  $s0, 64($sp)  # save S0\n    sd  $s1, 72($sp)  # save S1\n    sd  $s2, 80($sp)  # save S2\n    sd  $s3, 88($sp)  # save S3\n    sd  $s4, 96($sp)  # save S4\n    sd  $s5, 104($sp) # save S5\n    sd  $s6, 112($sp) # save S6\n    sd  $s7, 120($sp) # save S7\n    sd  $fp, 128($sp) # save FP\n    sd  $ra, 144($sp) # save RA\n    sd  $ra, 152($sp) # save RA as PC\n\n#if defined(__mips_hard_float)\n    s.d  $f24, 0($sp)   # save F24\n    s.d  $f25, 8($sp)   # save F25\n    s.d  $f26, 16($sp)  # save F26\n    s.d  $f27, 24($sp)  # save F27\n    s.d  $f28, 32($sp)  # save F28\n    s.d  $f29, 40($sp)  # save F29\n    s.d  $f30, 48($sp)  # save F30\n    s.d  $f31, 56($sp)  # save F31\n#endif\n\n    # store SP (pointing to old context-data) in v0 as return\n    move  $v0, $sp\n\n    # get SP (pointing to new context-data) from a0 param\n    move  $sp, $a0\n\n#if defined(__mips_hard_float)\n    l.d  $f24, 0($sp)   # restore F24\n    l.d  $f25, 8($sp)   # restore F25\n    l.d  $f26, 16($sp)  # restore F26\n    l.d  $f27, 24($sp)  # restore F27\n    l.d  $f28, 32($sp)  # restore F28\n    l.d  $f29, 40($sp)  # restore F29\n    l.d  $f30, 48($sp)  # restore F30\n    l.d  $f31, 56($sp)  # restore F31\n#endif\n\n    ld  $s0, 64($sp)  # restore S0\n    ld  $s1, 72($sp)  # restore S1\n    ld  $s2, 80($sp)  # restore S2\n    ld  $s3, 88($sp)  # restore S3\n    ld  $s4, 96($sp)  # restore S4\n    ld  $s5, 104($sp) # restore S5\n    ld  $s6, 112($sp) # restore S6\n    ld  $s7, 120($sp) # restore S7\n    ld  $fp, 128($sp) # restore FP\n    ld  $ra, 144($sp) # restore RAa\n\n    # load PC\n    ld  $t9, 152($sp)\n\n    # adjust stack\n    daddiu $sp, $sp, 160\n\n    move  $a0, $v0 # move old sp from v0 to a0 as param\n    move  $v1, $a1 # move *data from a1 to v1 as return\n\n    # jump to context\n    jr  $t9\n.end swoole_jump_fcontext\n.size swoole_jump_fcontext, .-swoole_jump_fcontext\n\n/* Mark that we don't need executable stack.  */\n.section .note.GNU-stack,\"\",%progbits\n"
  },
  {
    "path": "thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S",
    "content": "/*\n            Copyright Oliver Kowalke 2009.\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n\n/*******************************************************\n *                                                     *\n *  -------------------------------------------------  *\n *  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  *\n *  -------------------------------------------------  *\n *  |  0  |  4  |  8  |  12 |  16 |  20 |  24 |  28 |  *\n *  -------------------------------------------------  *\n *  |    TOC    |    R14    |    R15    |    R16    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  8  |  9  |  10 |  11 |  12 |  13 |  14 |  15 |  *\n *  -------------------------------------------------  *\n *  |  32 |  36 |  40 |  44 |  48 |  52 |  56 |  60 |  *\n *  -------------------------------------------------  *\n *  |    R17    |    R18    |     R19   |    R20    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  16 |  17 |  18 |  19 |  20 |  21 |  22 |  23 |  *\n *  -------------------------------------------------  *\n *  |  64 |  68 |  72 |  76 |  80 |  84 |  88 |  92 |  *\n *  -------------------------------------------------  *\n *  |    R21    |    R22    |    R23    |    R24    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  24 |  25 |  26 |  27 |  28 |  29 |  30 |  31 |  *\n *  -------------------------------------------------  *\n *  |  96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 |  *\n *  -------------------------------------------------  *\n *  |    R25    |    R26    |    R27    |    R28    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  32 |  33 |  34 |  35 |  36 |  37 |  38 |  39 |  *\n *  -------------------------------------------------  *\n *  | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 |  *\n *  -------------------------------------------------  *\n *  |    R29    |    R30    |    R31    |   hidden  |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  40 |  41 |  42 |  43 |  44 |  45 |  46 |  47 |  *\n *  -------------------------------------------------  *\n *  | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 |  *\n *  -------------------------------------------------  *\n *  |     CR    |     LR    |     PC    | back-chain|  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  48 |  49 |  50 |  51 |  52 |  53 |  54 |  55 |  *\n *  -------------------------------------------------  *\n *  | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 |  *\n *  -------------------------------------------------  *\n *  |  cr saved |  lr saved |  compiler |   linker  |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  56 |  57 |  58 |  59 |  60 |  61 |  62 |  63 |  *\n *  -------------------------------------------------  *\n *  | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 |  *\n *  -------------------------------------------------  *\n *  | TOC saved |    FCTX   |    DATA   |           |  *\n *  -------------------------------------------------  *\n *                                                     *\n *******************************************************/\n\n.file \"jump_ppc64_sysv_elf_gas.S\"\n.globl swoole_jump_fcontext\n#if _CALL_ELF == 2\n\t.text\n\t.align 2\nswoole_jump_fcontext:\n        addis   %r2, %r12, .TOC.-swoole_jump_fcontext@ha\n        addi    %r2, %r2, .TOC.-swoole_jump_fcontext@l\n        .localentry swoole_jump_fcontext, . - swoole_jump_fcontext\n#else\n\t.section \".opd\",\"aw\"\n\t.align 3\nswoole_jump_fcontext:\n# ifdef _CALL_LINUX\n        .quad   .L.swoole_jump_fcontext,.TOC.@tocbase,0\n        .type   swoole_jump_fcontext,@function\n        .text\n        .align 2\n.L.swoole_jump_fcontext:\n# else\n        .hidden .swoole_jump_fcontext\n        .globl  .swoole_jump_fcontext\n        .quad   .swoole_jump_fcontext,.TOC.@tocbase,0\n        .size   swoole_jump_fcontext,24\n        .type   .swoole_jump_fcontext,@function\n        .text\n        .align 2\n.swoole_jump_fcontext:\n# endif\n#endif\n    # reserve space on stack\n    subi  %r1, %r1, 184\n\n#if _CALL_ELF != 2\n    std  %r2,  0(%r1)  # save TOC\n#endif\n    std  %r14, 8(%r1)  # save R14\n    std  %r15, 16(%r1)  # save R15\n    std  %r16, 24(%r1)  # save R16\n    std  %r17, 32(%r1)  # save R17\n    std  %r18, 40(%r1)  # save R18\n    std  %r19, 48(%r1)  # save R19\n    std  %r20, 56(%r1)  # save R20\n    std  %r21, 64(%r1)  # save R21\n    std  %r22, 72(%r1)  # save R22\n    std  %r23, 80(%r1)  # save R23\n    std  %r24, 88(%r1)  # save R24\n    std  %r25, 96(%r1)  # save R25\n    std  %r26, 104(%r1)  # save R26\n    std  %r27, 112(%r1)  # save R27\n    std  %r28, 120(%r1)  # save R28\n    std  %r29, 128(%r1)  # save R29\n    std  %r30, 136(%r1)  # save R30\n    std  %r31, 144(%r1)  # save R31\n#if _CALL_ELF != 2\n    std  %r3,  152(%r1)  # save hidden\n#endif\n\n    # save CR\n    mfcr  %r0\n    std   %r0, 160(%r1)\n    # save LR\n    mflr  %r0\n    std   %r0, 168(%r1)\n    # save LR as PC\n    std   %r0, 176(%r1)\n\n    # store RSP (pointing to context-data) in R6\n    mr  %r6, %r1\n\n#if _CALL_ELF == 2\n    # restore RSP (pointing to context-data) from R3\n    mr  %r1, %r3\n#else\n    # restore RSP (pointing to context-data) from R4\n    mr  %r1, %r4\n\n    ld  %r2,  0(%r1)  # restore TOC\n#endif\n    ld  %r14, 8(%r1)  # restore R14\n    ld  %r15, 16(%r1)  # restore R15\n    ld  %r16, 24(%r1)  # restore R16\n    ld  %r17, 32(%r1)  # restore R17\n    ld  %r18, 40(%r1)  # restore R18\n    ld  %r19, 48(%r1)  # restore R19\n    ld  %r20, 56(%r1)  # restore R20\n    ld  %r21, 64(%r1)  # restore R21\n    ld  %r22, 72(%r1)  # restore R22\n    ld  %r23, 80(%r1)  # restore R23\n    ld  %r24, 88(%r1)  # restore R24\n    ld  %r25, 96(%r1)  # restore R25\n    ld  %r26, 104(%r1)  # restore R26\n    ld  %r27, 112(%r1)  # restore R27\n    ld  %r28, 120(%r1)  # restore R28\n    ld  %r29, 128(%r1)  # restore R29\n    ld  %r30, 136(%r1)  # restore R30\n    ld  %r31, 144(%r1)  # restore R31\n#if _CALL_ELF != 2\n    ld  %r3,  152(%r1)  # restore hidden\n#endif\n\n    # restore CR\n    ld  %r0, 160(%r1)\n    mtcr  %r0\n    # restore LR\n    ld  %r0, 168(%r1)\n    mtlr  %r0\n\n    # load PC\n    ld  %r12, 176(%r1)\n    # restore CTR\n    mtctr  %r12\n\n    # adjust stack\n    addi  %r1, %r1, 184\n\n#if _CALL_ELF == 2\n    # copy transfer_t into transfer_fn arg registers\n    mr  %r3, %r6\n    # arg pointer already in %r4\n\n    # jump to context\n    bctr\n\t.size swoole_jump_fcontext, .-swoole_jump_fcontext\n#else\n    # zero in r3 indicates first jump to context-function\n    cmpdi %r3, 0\n    beq use_entry_arg\n\n    # return transfer_t\n    std  %r6, 0(%r3)\n    std  %r5, 8(%r3)\n\n    # jump to context\n    bctr\n\nuse_entry_arg:\n    # copy transfer_t into transfer_fn arg registers\n    mr  %r3, %r6\n    mr  %r4, %r5\n\n    # jump to context\n    bctr\n# ifdef _CALL_LINUX\n\t.size .swoole_jump_fcontext, .-.L.swoole_jump_fcontext\n# else\n\t.size .swoole_jump_fcontext, .-.swoole_jump_fcontext\n# endif\n#endif\n\n\n/* Mark that we don't need executable stack.  */\n.section .note.GNU-stack,\"\",%progbits\n"
  },
  {
    "path": "thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S",
    "content": "/*\n            Copyright Oliver Kowalke 2009.\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n\n/*******************************************************\n *                                                     *\n *  -------------------------------------------------  *\n *  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  *\n *  -------------------------------------------------  *\n *  |  0  |  4  |  8  |  12 |  16 |  20 |  24 |  28 |  *\n *  -------------------------------------------------  *\n *  |    R13    |    R14    |    R15    |    R16    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  8  |  9  |  10 |  11 |  12 |  13 |  14 |  15 |  *\n *  -------------------------------------------------  *\n *  |  32 |  36 |  40 |  44 |  48 |  52 |  56 |  60 |  *\n *  -------------------------------------------------  *\n *  |    R17    |    R18    |     R19   |    R20    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  16 |  17 |  18 |  19 |  20 |  21 |  22 |  23 |  *\n *  -------------------------------------------------  *\n *  |  64 |  68 |  72 |  76 |  80 |  84 |  88 |  92 |  *\n *  -------------------------------------------------  *\n *  |    R21    |    R22    |    R23    |    R24    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  24 |  25 |  26 |  27 |  28 |  29 |  30 |  31 |  *\n *  -------------------------------------------------  *\n *  |  96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 |  *\n *  -------------------------------------------------  *\n *  |    R25    |    R26    |    R27    |    R28    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  32 |  33 |  34 |  35 |  36 |  37 |  38 |  39 |  *\n *  -------------------------------------------------  *\n *  | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 |  *\n *  -------------------------------------------------  *\n *  |    R29    |    R30    |    R31    |   hidden  |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  40 |  41 |  42 |  43 |  44 |  45 |  46 |  47 |  *\n *  -------------------------------------------------  *\n *  | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 |  *\n *  -------------------------------------------------  *\n *  |     CR    |     LR    |     PC    | back-chain|  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  48 |  49 |  50 |  51 |  52 |  53 |  54 |  55 |  *\n *  -------------------------------------------------  *\n *  | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 |  *\n *  -------------------------------------------------  *\n *  |  cr saved |  lr saved |  compiler |   linker  |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  56 |  57 |  58 |  59 |  60 |  61 |  62 |  63 |  *\n *  -------------------------------------------------  *\n *  | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 |  *\n *  -------------------------------------------------  *\n *  |    FCTX   |    DATA   |           |           |  *\n *  -------------------------------------------------  *\n *                                                     *\n *******************************************************/\n\n.text\n.align 2\n.globl _swoole_jump_fcontext\n\n_swoole_jump_fcontext:\n    ; reserve space on stack\n    subi  r1, r1, 184\n\n    std  r14, 8(r1)  ; save R14\n    std  r15, 16(r1)  ; save R15\n    std  r16, 24(r1)  ; save R16\n    std  r17, 32(r1)  ; save R17\n    std  r18, 40(r1)  ; save R18\n    std  r19, 48(r1)  ; save R19\n    std  r20, 56(r1)  ; save R20\n    std  r21, 64(r1)  ; save R21\n    std  r22, 72(r1)  ; save R22\n    std  r23, 80(r1)  ; save R23\n    std  r24, 88(r1)  ; save R24\n    std  r25, 96(r1)  ; save R25\n    std  r26, 104(r1)  ; save R26\n    std  r27, 112(r1)  ; save R27\n    std  r28, 120(r1)  ; save R28\n    std  r29, 128(r1)  ; save R29\n    std  r30, 136(r1)  ; save R30\n    std  r31, 144(r1)  ; save R31\n    std  r3,  152(r1)  ; save hidden\n\n    ; save CR\n    mfcr  r0\n    std   r0, 160(r1)\n    ; save LR\n    mflr  r0\n    std   r0, 168(r1)\n    ; save LR as PC\n    std   r0, 176(r1)\n\n    ; store RSP (pointing to context-data) in R6\n    mr  r6, r1\n\n    ; restore RSP (pointing to context-data) from R4\n    mr  r1, r4\n\n    ld  r14, 8(r1)  ; restore R14\n    ld  r15, 16(r1)  ; restore R15\n    ld  r16, 24(r1)  ; restore R16\n    ld  r17, 32(r1)  ; restore R17\n    ld  r18, 40(r1)  ; restore R18\n    ld  r19, 48(r1)  ; restore R19\n    ld  r20, 56(r1)  ; restore R20\n    ld  r21, 64(r1)  ; restore R21\n    ld  r22, 72(r1)  ; restore R22\n    ld  r23, 80(r1)  ; restore R23\n    ld  r24, 88(r1)  ; restore R24\n    ld  r25, 96(r1)  ; restore R25\n    ld  r26, 104(r1)  ; restore R26\n    ld  r27, 112(r1)  ; restore R27\n    ld  r28, 120(r1)  ; restore R28\n    ld  r29, 128(r1)  ; restore R29\n    ld  r30, 136(r1)  ; restore R30\n    ld  r31, 144(r1)  ; restore R31\n    ld  r3,  152(r1)  ; restore hidden\n\n    ; restore CR\n    ld  r0, 160(r1)\n    mtcr  r0\n    ; restore LR\n    ld  r0, 168(r1)\n    mtlr  r0\n\n    ; load PC\n    ld  r12, 176(r1)\n    ; restore CTR\n    mtctr  r12\n\n    ; adjust stack\n    addi  r1, r1, 184\n\n    ; zero in r3 indicates first jump to context-function\n    cmpdi r3, 0\n    beq use_entry_arg\n\n    ; return transfer_t\n    std  r6, 0(r3)\n    std  r5, 8(r3)\n\n    ; jump to context\n    bctr\n\nuse_entry_arg:\n    ; copy transfer_t into transfer_fn arg registers\n    mr  r3, r6\n    mr  r4, r5\n\n    ; jump to context\n    bctr\n"
  },
  {
    "path": "thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S",
    "content": "\n/*\n            Copyright Oliver Kowalke 2009.\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n\n/*******************************************************\n *                                                     *\n *  -------------------------------------------------  *\n *  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  *\n *  -------------------------------------------------  *\n *  |  0  |  4  |  8  |  12 |  16 |  20 |  24 |  28 |  *\n *  -------------------------------------------------  *\n *  |    TOC    |    R14    |    R15    |    R16    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  8  |  9  |  10 |  11 |  12 |  13 |  14 |  15 |  *\n *  -------------------------------------------------  *\n *  |  32 |  36 |  40 |  44 |  48 |  52 |  56 |  60 |  *\n *  -------------------------------------------------  *\n *  |    R17    |    R18    |     R19   |    R20    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  16 |  17 |  18 |  19 |  20 |  21 |  22 |  23 |  *\n *  -------------------------------------------------  *\n *  |  64 |  68 |  72 |  76 |  80 |  84 |  88 |  92 |  *\n *  -------------------------------------------------  *\n *  |    R21    |    R22    |    R23    |    R24    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  24 |  25 |  26 |  27 |  28 |  29 |  30 |  31 |  *\n *  -------------------------------------------------  *\n *  |  96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 |  *\n *  -------------------------------------------------  *\n *  |    R25    |    R26    |    R27    |    R28    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  32 |  33 |  34 |  35 |  36 |  37 |  38 |  39 |  *\n *  -------------------------------------------------  *\n *  | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 |  *\n *  -------------------------------------------------  *\n *  |    R29    |    R30    |    R31    |   hidden  |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  40 |  41 |  42 |  43 |  44 |  45 |  46 |  47 |  *\n *  -------------------------------------------------  *\n *  | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 |  *\n *  -------------------------------------------------  *\n *  |     CR    |     LR    |     PC    | back-chain|  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  48 |  49 |  50 |  51 |  52 |  53 |  54 |  55 |  *\n *  -------------------------------------------------  *\n *  | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 |  *\n *  -------------------------------------------------  *\n *  |  cr saved |  lr saved |  compiler |   linker  |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  56 |  57 |  58 |  59 |  60 |  61 |  62 |  63 |  *\n *  -------------------------------------------------  *\n *  | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 |  *\n *  -------------------------------------------------  *\n *  | TOC saved |    FCTX   |    DATA   |           |  *\n *  -------------------------------------------------  *\n *                                                     *\n *******************************************************/\n\n    .file \"jump_ppc64_sysv_xcoff_gas.S\"\n    .toc\n    .csect .text[PR], 5\n    .align 2\n    .globl  swoole_jump_fcontext[DS]\n    .globl .swoole_jump_fcontext\n    .csect  swoole_jump_fcontext[DS], 3\nswoole_jump_fcontext:\n    .llong .swoole_jump_fcontext[PR], TOC[tc0], 0\n    .csect .text[PR], 5\n.swoole_jump_fcontext:\n    # reserve space on stack\n    subi  1, 1, 184\n\n    std  2, 0(1)  # save TOC\n    std  14, 8(1)  # save R14\n    std  15, 16(1)  # save R15\n    std  16, 24(1)  # save R16\n    std  17, 32(1)  # save R17\n    std  18, 40(1)  # save R18\n    std  19, 48(1)  # save R19\n    std  20, 56(1)  # save R20\n    std  21, 64(1)  # save R21\n    std  22, 72(1)  # save R22\n    std  23, 80(1)  # save R23\n    std  24, 88(1)  # save R24\n    std  25, 96(1)  # save R25\n    std  26, 104(1)  # save R26\n    std  27, 112(1)  # save R27\n    std  28, 120(1)  # save R28\n    std  29, 128(1)  # save R29\n    std  30, 136(1)  # save R30\n    std  31, 144(1)  # save R31\n    std  3,  152(1)  # save hidden\n\n    # save CR\n    mfcr  0\n    std  0, 160(1)\n    # save LR\n    mflr  0\n    std  0, 168(1)\n    # save LR as PC\n    std  0, 176(1)\n\n    # store RSP (pointing to context-data) in R6\n    mr  6, 1\n\n    # restore RSP (pointing to context-data) from R4\n    mr  1, 4\n\n    ld  2, 0(1)  # restore TOC\n    ld  14, 8(1)  # restore R14\n    ld  15, 16(1)  # restore R15\n    ld  16, 24(1)  # restore R16\n    ld  17, 32(1)  # restore R17\n    ld  18, 40(1)  # restore R18\n    ld  19, 48(1)  # restore R19\n    ld  20, 56(1)  # restore R20\n    ld  21, 64(1)  # restore R21\n    ld  22, 72(1)  # restore R22\n    ld  23, 80(1)  # restore R23\n    ld  24, 88(1)  # restore R24\n    ld  25, 96(1)  # restore R25\n    ld  26, 104(1)  # restore R26\n    ld  27, 112(1)  # restore R27\n    ld  28, 120(1)  # restore R28\n    ld  29, 128(1)  # restore R29\n    ld  30, 136(1)  # restore R30\n    ld  31, 144(1)  # restore R31\n    ld  3,  152(1)  # restore hidden\n\n    # restore CR\n    ld  0, 160(1)\n    mtcr  0\n    # restore LR\n    ld  0, 168(1)\n    mtlr  0\n\n    # load PC\n    ld  0, 176(1)\n    # restore CTR\n    mtctr  0\n\n    # adjust stack\n    addi  1, 1, 184\n\n    # zero in r3 indicates first jump to context-function\n    cmpdi 3, 0\n    beq use_entry_arg\n\n    # return transfer_t\n    std  6, 0(3)\n    std  5, 8(3)\n\n    # jump to context\n    bctr\n\nuse_entry_arg:\n    # copy transfer_t into transfer_fn arg registers\n    mr  3, 6\n    mr  4, 5\n\n    # jump to context\n    bctr\n"
  },
  {
    "path": "thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S",
    "content": "/*\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n/*******************************************************\n *                                                     *\n *  -------------------------------------------------  *\n *  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  *\n *  -------------------------------------------------  *\n *  | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c|  *\n *  -------------------------------------------------  *\n *  |    fs0    |    fs1    |    fs2    |    fs3    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  8  |  9  |  10 |  11 |  12 |  13 |  14 |  15 |  *\n *  -------------------------------------------------  *\n *  | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c|  *\n *  -------------------------------------------------  *\n *  |    fs4    |    fs5    |    fs6    |    fs7    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  16 |  17 |  18 |  19 |  20 |  21 |  22 |  23 |  *\n *  -------------------------------------------------  *\n *  | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c|  *\n *  -------------------------------------------------  *\n *  |    fs8    |    fs9    |    fs10   |    fs11   |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  24 |  25 |  26 |  27 |  28 |  29 |  30 |  31 |  *\n *  -------------------------------------------------  *\n *  | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c|  *\n *  -------------------------------------------------  *\n *  |    s0     |    s1     |    s2     |    s3     |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  32 |  33 |  34 |  35 |  36 |  37 |  38 |  39 |  *\n *  -------------------------------------------------  *\n *  | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c|  *\n *  -------------------------------------------------  *\n *  |    s4     |    s5     |    s6     |     s7    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  40 |  41 |  42 | 43  |  44 | 45  |  46 | 47  |  *\n *  -------------------------------------------------  *\n *  | 0xa0| 0xa4| 0xa8| 0xac| 0xb0| 0xb4| 0xb8| 0xbc|  *\n *  -------------------------------------------------  *\n *  |     s8    |     s9    |    s10    |    s11    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  48 |  49 |  50 | 51  |     |     |     |     |  *\n *  -------------------------------------------------  *\n *  | 0xc0| 0xc4| 0xc8| 0xcc|     |     |     |     |  *\n *  -------------------------------------------------  *\n *  |     ra    |     pc    |           |           |  *\n *  -------------------------------------------------  *\n *                                                     *\n *******************************************************/\n\n.file \"jump_riscv64_sysv_elf_gas.S\"\n.text\n.align  1\n.global swoole_jump_fcontext\n.type   swoole_jump_fcontext, %function\nswoole_jump_fcontext:\n    # prepare stack for GP + FPU\n    addi  sp, sp, -0xd0\n\n    # save fs0 - fs11\n    fsd  fs0, 0x00(sp)\n    fsd  fs1, 0x08(sp)\n    fsd  fs2, 0x10(sp)\n    fsd  fs3, 0x18(sp)\n    fsd  fs4, 0x20(sp)\n    fsd  fs5, 0x28(sp)\n    fsd  fs6, 0x30(sp)\n    fsd  fs7, 0x38(sp)\n    fsd  fs8, 0x40(sp)\n    fsd  fs9, 0x48(sp)\n    fsd  fs10, 0x50(sp)\n    fsd  fs11, 0x58(sp)\n\n    # save s0-s11, ra\n    sd  s0, 0x60(sp)\n    sd  s1, 0x68(sp)\n    sd  s2, 0x70(sp)\n    sd  s3, 0x78(sp)\n    sd  s4, 0x80(sp)\n    sd  s5, 0x88(sp)\n    sd  s6, 0x90(sp)\n    sd  s7, 0x98(sp)\n    sd  s8, 0xa0(sp)\n    sd  s9, 0xa8(sp)\n    sd  s10, 0xb0(sp)\n    sd  s11, 0xb8(sp)\n    sd  ra, 0xc0(sp)\n\n    # save RA as PC\n    sd  ra, 0xc8(sp)\n\n    # store SP (pointing to context-data) in A2\n    mv  a2, sp\n\n    # restore SP (pointing to context-data) from A0\n    mv  sp, a0\n\n    # load fs0 - fs11\n    fld  fs0, 0x00(sp)\n    fld  fs1, 0x08(sp)\n    fld  fs2, 0x10(sp)\n    fld  fs3, 0x18(sp)\n    fld  fs4, 0x20(sp)\n    fld  fs5, 0x28(sp)\n    fld  fs6, 0x30(sp)\n    fld  fs7, 0x38(sp)\n    fld  fs8, 0x40(sp)\n    fld  fs9, 0x48(sp)\n    fld  fs10, 0x50(sp)\n    fld  fs11, 0x58(sp)\n\n    # load s0-s11,ra\n    ld  s0, 0x60(sp)\n    ld  s1, 0x68(sp)\n    ld  s2, 0x70(sp)\n    ld  s3, 0x78(sp)\n    ld  s4, 0x80(sp)\n    ld  s5, 0x88(sp)\n    ld  s6, 0x90(sp)\n    ld  s7, 0x98(sp)\n    ld  s8, 0xa0(sp)\n    ld  s9, 0xa8(sp)\n    ld  s10, 0xb0(sp)\n    ld  s11, 0xb8(sp)\n    ld  ra, 0xc0(sp)\n\n    # return transfer_t from jump\n    # pass transfer_t as first arg in context function\n    # a0 == FCTX, a1 == DATA\n    mv a0, a2\n\n    # load pc\n    ld  a2, 0xc8(sp)\n\n    # restore stack from GP + FPU\n    addi  sp, sp, 0xd0\n\n    jr a2\n.size   swoole_jump_fcontext,.-swoole_jump_fcontext\n# Mark that we don't need executable stack.\n.section .note.GNU-stack,\"\",%progbits\n"
  },
  {
    "path": "thirdparty/boost/asm/jump_sparc64_sysv_elf_gas.S",
    "content": "/*\n            Copyright Martin Husemann 2013.\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n\n/*******************************************************************\n *                                                                 *\n *  -------------------------------------------------------------  *\n *  |  Offset (in 4 or 8 byte units) | Content                  |  *\n *  -------------------------------------------------------------  *\n *  | 0                              | %sp                      |  *\n *  -------------------------------------------------------------  *\n *  | 1                              | %pc                      |  *\n *  -------------------------------------------------------------  *\n *  | 2                              | %i7 (return address)     |  *\n *  -------------------------------------------------------------  *\n *  | 3                              | %g1                      |  *\n *  -------------------------------------------------------------  *\n *  | 4                              | %g2                      |  *\n *  -------------------------------------------------------------  *\n *  | 5                              | %g3                      |  *\n *  -------------------------------------------------------------  *\n *  | 6                              | %g6                      |  *\n *  -------------------------------------------------------------  *\n *  | 7                              | %g7                      |  *\n *  -------------------------------------------------------------  *\n *    The local and in registers are stored on the stack.          *\n *******************************************************************/\n\n#define OFF(N)\t(8*(N))\n#define CCFSZ   176\t\t// C Compiler Frame Size\n#define BIAS    (2048-1)\t// Stack offset for 64 bit programs\n#define FC_SZ   448\t\t// sizeof(fcontext_t)\n#define FC_STK  384\t\t// offsetof(fcontext_t, fc_stack)\n#define FC_FPU  0\t\t// offsetof(fcontext_t, fc_fp)\n#define FC_FSR  264\t\t// offsetof(fcontext_t, fc_fp.fp_fsr)\n#define FC_FPRS 256\t\t// offsetof(fcontext_t, fc_fp.fp_fprs)\n#define FC_GREG 320\t\t// offsetof(fcontext_t, fc_greg)\n#define BLOCK_SIZE  64\n\n    .register %g2,#ignore\n    .register %g3,#ignore\n    .register %g6,#ignore\n\n.text\n.globl swoole_jump_fcontext\n.align 4\n.type swoole_jump_fcontext,@function\n// intptr_t\n// swoole_jump_fcontext( fcontext_t * ofc, fcontext_t const* nfc, intptr_t vp,\n//                bool preserve_fpu = true);\nswoole_jump_fcontext:\n    // %o0 = pointer to old fcontext, save current state here\n    // %o1 = new context to jump to\n    // %o2 = new return value in context %o0\n    // %o3 = preserve fpu registers\n    // Save current state in %o0 fcontext, then activate %o1.\n    // If %o3, include fpu registers.\n\n    flushw // make sure all shadow registers are up to date in the current stack\n\n    // save current state to fcontext_t at %o0\n    stx %sp, [%o0 + FC_GREG + OFF(0)]    // current stack pointer\n    add %o7, 8, %o4         // calculate next instruction past call\n    stx %o4, [%o0 + FC_GREG + OFF(1)]    // and store it as %pc in save context\n    stx %o7, [%o0 + FC_GREG + OFF(2)]\n    stx %g1, [%o0 + FC_GREG + OFF(3)]\n    stx %g2, [%o0 + FC_GREG + OFF(4)]\n    stx %g3, [%o0 + FC_GREG + OFF(5)]\n    stx %g6, [%o0 + FC_GREG + OFF(6)]\n    stx %g7, [%o0 + FC_GREG + OFF(7)]\n\n    // do we need to handle fpu?\n    brz %o3, Lno_fpu\n    nop\n\n    add %o0, FC_FPU, %o5\n    stda %f0, [%o5] 0xf0 /* ASI_BLOCK_PRIMARY */\n    add %o5, BLOCK_SIZE, %o5\n    stda %f16, [%o5] 0xf0\n    add %o5, BLOCK_SIZE, %o5\n    stda %f32, [%o5] 0xf0\n    add %o5, BLOCK_SIZE, %o5\n    stda %f48, [%o5] 0xf0\n    stx %fsr, [%o0+FC_FSR]\n    rd %fprs, %o4\n    stx %o4, [%o0+FC_FPRS]\n\n    add %o1, FC_FPU, %o5\n    ldda [%o5] 0xf0 /* ASI_BLOCK_PRIMARY */, %f0\n    add %o5, BLOCK_SIZE, %o5\n    ldda [%o5] 0xf0, %f16\n    add %o5, BLOCK_SIZE, %o5\n    ldda [%o5] 0xf0, %f32\n    add %o5, BLOCK_SIZE, %o5\n    ldda [%o5] 0xf0, %f48\n    ldx [%o1+FC_FSR], %fsr\n    ldx [%o1+FC_FPRS], %o4\n    wr %o4,0,%fprs\n\nLno_fpu:\n    // load new state from %o1\n    ldx [%o1 + FC_GREG + OFF(1)], %o4\n    ldx [%o1 + FC_GREG + OFF(2)], %o7\n    ldx [%o1 + FC_GREG + OFF(3)], %g1\n    ldx [%o1 + FC_GREG + OFF(4)], %g2\n    ldx [%o1 + FC_GREG + OFF(5)], %g3\n    ldx [%o1 + FC_GREG + OFF(6)], %g6\n    ldx [%o1 + FC_GREG + OFF(7)], %g7\n    // switch to new stack\n    ldx [%o1 + FC_GREG + OFF(0)], %sp\n    // and now reload from this stack the shadow regist bank contents\n    ldx [%sp + BIAS + OFF(0)], %l0\n    ldx [%sp + BIAS + OFF(1)], %l1\n    ldx [%sp + BIAS + OFF(2)], %l2\n    ldx [%sp + BIAS + OFF(3)], %l3\n    ldx [%sp + BIAS + OFF(4)], %l4\n    ldx [%sp + BIAS + OFF(5)], %l5\n    ldx [%sp + BIAS + OFF(6)], %l6\n    ldx [%sp + BIAS + OFF(7)], %l7\n    ldx [%sp + BIAS + OFF(8)], %i0\n    ldx [%sp + BIAS + OFF(9)], %i1\n    ldx [%sp + BIAS + OFF(10)], %i2\n    ldx [%sp + BIAS + OFF(11)], %i3\n    ldx [%sp + BIAS + OFF(12)], %i4\n    ldx [%sp + BIAS + OFF(13)], %i5\n    ldx [%sp + BIAS + OFF(14)], %i6\n    ldx [%sp + BIAS + OFF(15)], %i7\n\n    // finally continue execution in new context\n    jmp %o4\n    mov %o2, %o0    // return arg as result\n\n.size swoole_jump_fcontext,.-swoole_jump_fcontext\n\n/* Mark that we don't need executable stack.  */\n.section .note.GNU-stack,\"\",%progbits\n"
  },
  {
    "path": "thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S",
    "content": "/*\n            Copyright Oliver Kowalke 2009.\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n            http://www.boost.org/LICENSE_1_0.txt)\n*/\n\n/****************************************************************************************\n *                                                                                      *\n *  ----------------------------------------------------------------------------------  *\n *  |    0    |    1    |    2    |    3    |    4     |    5    |    6    |    7    |  *\n *  ----------------------------------------------------------------------------------  *\n *  |   0x0   |   0x4   |   0x8   |   0xc   |   0x10   |   0x14  |   0x18  |   0x1c  |  *\n *  ----------------------------------------------------------------------------------  *\n *  | fc_mxcsr|fc_x87_cw|       guard       |         R12        |        R13        |  *\n *  ----------------------------------------------------------------------------------  *\n *  ----------------------------------------------------------------------------------  *\n *  |    8    |    9    |   10    |   11    |    12    |    13   |    14   |    15   |  *\n *  ----------------------------------------------------------------------------------  *\n *  |   0x20  |   0x24  |   0x28  |  0x2c   |   0x30   |   0x34  |   0x38  |   0x3c  |  *\n *  ----------------------------------------------------------------------------------  *\n *  |        R14        |        R15        |         RBX        |        RBP        |  *\n *  ----------------------------------------------------------------------------------  *\n *  ----------------------------------------------------------------------------------  *\n *  |   16    |   17    |   18    |   19    |    20    |    21   |    22   |    23   |  *\n *  ----------------------------------------------------------------------------------  *\n *  |   0x40  |   0x44  |                                                            |  *\n *  ----------------------------------------------------------------------------------  *\n *  |        RIP        |                                                            |  *\n *  ----------------------------------------------------------------------------------  *\n *                                                                                      *\n ****************************************************************************************/\n\n# if defined __CET__\n#  include <cet.h>\n#  define SWOOLE_SHSTK_ENABLED (__CET__ & 0x2)\n#  define SWOOLE_CONTEXT_SHADOW_STACK (SWOOLE_SHSTK_ENABLED && SHADOW_STACK_SYSCALL)\n# else\n#  define _CET_ENDBR\n# endif\n.file \"jump_x86_64_sysv_elf_gas.S\"\n.text\n.globl swoole_jump_fcontext\n.type swoole_jump_fcontext,@function\n.align 16\nswoole_jump_fcontext:\n    _CET_ENDBR\n    leaq  -0x40(%rsp), %rsp /* prepare stack */\n\n#if !defined(SWOOLE_USE_TSX)\n    stmxcsr  (%rsp)     /* save MMX control- and status-word */\n    fnstcw   0x4(%rsp)  /* save x87 control-word */\n#endif\n\n#if defined(SWOOLE_CONTEXT_TLS_STACK_PROTECTOR)\n    movq  %fs:0x28, %rcx    /* read stack guard from TLS record */\n    movq  %rcx, 0x8(%rsp)   /* save stack guard */\n#endif\n\n    movq  %r12, 0x10(%rsp)  /* save R12 */\n    movq  %r13, 0x18(%rsp)  /* save R13 */\n    movq  %r14, 0x20(%rsp)  /* save R14 */\n    movq  %r15, 0x28(%rsp)  /* save R15 */\n    movq  %rbx, 0x30(%rsp)  /* save RBX */\n    movq  %rbp, 0x38(%rsp)  /* save RBP */\n\n#if SWOOLE_CONTEXT_SHADOW_STACK\n    /* grow the stack to reserve space for shadow stack pointer(SSP) */\n    leaq  -0x8(%rsp), %rsp\n    /* read the current SSP and store it */\n    rdsspq  %rcx\n    movq  %rcx, (%rsp)\n#endif\n\n    /* store RSP (pointing to context-data) in RAX */\n    movq  %rsp, %rax\n\n    /* restore RSP (pointing to context-data) from RDI */\n    movq  %rdi, %rsp\n\n#if SWOOLE_CONTEXT_SHADOW_STACK\n    /* first 8 bytes are SSP */\n    movq  (%rsp), %rcx\n    leaq  0x8(%rsp), %rsp\n\n    /* Restore target(new) shadow stack */\n    rstorssp  -8(%rcx)\n    /* restore token for previous shadow stack is pushed */\n    /* on previous shadow stack after saveprevssp */\n    saveprevssp\n\n    /* when return, swoole_jump_fcontext jump to restored return address */\n    /* (r8) instead of RET. This miss of RET implies us to unwind */\n    /* shadow stack accordingly. Otherwise mismatch occur */\n    movq  $1, %rcx\n    incsspq  %rcx\n#endif\n\n    movq  0x40(%rsp), %r8  /* restore return-address */\n\n#if !defined(SWOOLE_USE_TSX)\n    ldmxcsr  (%rsp)     /* restore MMX control- and status-word */\n    fldcw    0x4(%rsp)  /* restore x87 control-word */\n#endif\n\n#if defined(SWOOLE_CONTEXT_TLS_STACK_PROTECTOR)\n    movq  0x8(%rsp), %rdx  /* load stack guard */\n    movq  %rdx, %fs:0x28   /* restore stack guard to TLS record */\n#endif\n\n    movq  0x10(%rsp), %r12  /* restore R12 */\n    movq  0x18(%rsp), %r13  /* restore R13 */\n    movq  0x20(%rsp), %r14  /* restore R14 */\n    movq  0x28(%rsp), %r15  /* restore R15 */\n    movq  0x30(%rsp), %rbx  /* restore RBX */\n    movq  0x38(%rsp), %rbp  /* restore RBP */\n\n    leaq  0x48(%rsp), %rsp /* prepare stack */\n\n    /* return transfer_t from jump */\n#if !defined(_ILP32)\n    /* RAX == fctx, RDX == data */\n    movq  %rsi, %rdx\n#else\n    /* RAX == data:fctx */\n    salq  $32, %rsi\n    orq   %rsi, %rax\n#endif\n    /* pass transfer_t as first arg in context function */\n#if !defined(_ILP32)\n    /* RDI == fctx, RSI == data */\n#else\n    /* RDI == data:fctx */\n#endif\n    movq  %rax, %rdi\n\n    /* indirect jump to context */\n    jmp  *%r8\n.size swoole_jump_fcontext,.-swoole_jump_fcontext\n\n/* Mark that we don't need executable stack.  */\n.section .note.GNU-stack,\"\",%progbits\n"
  },
  {
    "path": "thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S",
    "content": "/*\n            Copyright Oliver Kowalke 2009.\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n            http://www.boost.org/LICENSE_1_0.txt)\n*/\n\n/****************************************************************************************\n *                                                                                      *\n *  ----------------------------------------------------------------------------------  *\n *  |    0    |    1    |    2    |    3    |    4     |    5    |    6    |    7    |  *\n *  ----------------------------------------------------------------------------------  *\n *  |   0x0   |   0x4   |   0x8   |   0xc   |   0x10   |   0x14  |   0x18  |   0x1c  |  *\n *  ----------------------------------------------------------------------------------  *\n *  | fc_mxcsr|fc_x87_cw|        R12        |         R13        |        R14        |  *\n *  ----------------------------------------------------------------------------------  *\n *  ----------------------------------------------------------------------------------  *\n *  |    8    |    9    |   10    |   11    |    12    |    13   |    14   |    15   |  *\n *  ----------------------------------------------------------------------------------  *\n *  |   0x20  |   0x24  |   0x28  |  0x2c   |   0x30   |   0x34  |   0x38  |   0x3c  |  *\n *  ----------------------------------------------------------------------------------  *\n *  |        R15        |        RBX        |         RBP        |        RIP        |  *\n *  ----------------------------------------------------------------------------------  *\n *                                                                                      *\n ****************************************************************************************/\n\n.text\n.globl _swoole_jump_fcontext\n.align 8\n_swoole_jump_fcontext:\n    leaq  -0x38(%rsp), %rsp /* prepare stack */\n\n#if !defined(SWOOLE_USE_TSX)\n    stmxcsr  (%rsp)     /* save MMX control- and status-word */\n    fnstcw   0x4(%rsp)  /* save x87 control-word */\n#endif\n\n    movq  %r12, 0x8(%rsp)  /* save R12 */\n    movq  %r13, 0x10(%rsp)  /* save R13 */\n    movq  %r14, 0x18(%rsp)  /* save R14 */\n    movq  %r15, 0x20(%rsp)  /* save R15 */\n    movq  %rbx, 0x28(%rsp)  /* save RBX */\n    movq  %rbp, 0x30(%rsp)  /* save RBP */\n\n    /* store RSP (pointing to context-data) in RAX */\n    movq  %rsp, %rax\n\n    /* restore RSP (pointing to context-data) from RDI */\n    movq  %rdi, %rsp\n\n    movq  0x38(%rsp), %r8  /* restore return-address */\n\n#if !defined(SWOOLE_USE_TSX)\n    ldmxcsr  (%rsp)     /* restore MMX control- and status-word */\n    fldcw    0x4(%rsp)  /* restore x87 control-word */\n#endif\n\n    movq  0x8(%rsp), %r12  /* restore R12 */\n    movq  0x10(%rsp), %r13  /* restore R13 */\n    movq  0x18(%rsp), %r14  /* restore R14 */\n    movq  0x20(%rsp), %r15  /* restore R15 */\n    movq  0x28(%rsp), %rbx  /* restore RBX */\n    movq  0x30(%rsp), %rbp  /* restore RBP */\n\n    leaq  0x40(%rsp), %rsp /* prepare stack */\n\n    /* return transfer_t from jump */\n    /* RAX == fctx, RDX == data */\n    movq  %rsi, %rdx\n    /* pass transfer_t as first arg in context function */\n    /* RDI == fctx, RSI == data */\n    movq  %rax, %rdi\n\n    /* indirect jump to context */\n    jmp  *%r8\n"
  },
  {
    "path": "thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S",
    "content": "/*\n            Copyright Edward Nevill + Oliver Kowalke 2015\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n/*******************************************************\n *                                                     *\n *  -------------------------------------------------  *\n *  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  *\n *  -------------------------------------------------  *\n *  | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c|  *\n *  -------------------------------------------------  *\n *  |    d8     |    d9     |    d10    |    d11    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  8  |  9  |  10 |  11 |  12 |  13 |  14 |  15 |  *\n *  -------------------------------------------------  *\n *  | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c|  *\n *  -------------------------------------------------  *\n *  |    d12    |    d13    |    d14    |    d15    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  16 |  17 |  18 |  19 |  20 |  21 |  22 |  23 |  *\n *  -------------------------------------------------  *\n *  | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c|  *\n *  -------------------------------------------------  *\n *  |    x19    |    x20    |    x21    |    x22    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  24 |  25 |  26 |  27 |  28 |  29 |  30 |  31 |  *\n *  -------------------------------------------------  *\n *  | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c|  *\n *  -------------------------------------------------  *\n *  |    x23    |    x24    |    x25    |    x26    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  32 |  33 |  34 |  35 |  36 |  37 |  38 |  39 |  *\n *  -------------------------------------------------  *\n *  | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c|  *\n *  -------------------------------------------------  *\n *  |    x27    |    x28    |    FP     |     LR    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  40 |  41 |  42 | 43  |           |           |  *\n *  -------------------------------------------------  *\n *  | 0xa0| 0xa4| 0xa8| 0xac|           |           |  *\n *  -------------------------------------------------  *\n *  |     PC    |   align   |           |           |  *\n *  -------------------------------------------------  *\n *                                                     *\n *******************************************************/\n\n.file \"make_arm64_aapcs_elf_gas.S\"\n.text\n.align  2\n.global swoole_make_fcontext\n.type   swoole_make_fcontext, %function\nswoole_make_fcontext:\n    # shift address in x0 (allocated stack) to lower 16 byte boundary\n    and x0, x0, ~0xF\n\n    # reserve space for context-data on context-stack\n    sub  x0, x0, #0xb0\n\n    # third arg of swoole_make_fcontext() == address of context-function\n    # store address as a PC to jump in\n    str  x2, [x0, #0xa0]\n\n    # save address of finish as return-address for context-function\n    # will be entered after context-function returns (LR register)\n    adr  x1, finish\n    str  x1, [x0, #0x98]\n\n    ret  x30 // return pointer to context-data (x0)\n\nfinish:\n    # exit code is zero\n    mov  x0, #0\n    # exit application\n    bl  _exit\n\n.size   swoole_make_fcontext,.-swoole_make_fcontext\n# Mark that we don't need executable stack.\n.section .note.GNU-stack,\"\",%progbits\n"
  },
  {
    "path": "thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S",
    "content": "/*\n            Copyright Edward Nevill + Oliver Kowalke 2015\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n/*******************************************************\n *                                                     *\n *  -------------------------------------------------  *\n *  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  *\n *  -------------------------------------------------  *\n *  | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c|  *\n *  -------------------------------------------------  *\n *  |    d8     |    d9     |    d10    |    d11    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  8  |  9  |  10 |  11 |  12 |  13 |  14 |  15 |  *\n *  -------------------------------------------------  *\n *  | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c|  *\n *  -------------------------------------------------  *\n *  |    d12    |    d13    |    d14    |    d15    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  16 |  17 |  18 |  19 |  20 |  21 |  22 |  23 |  *\n *  -------------------------------------------------  *\n *  | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c|  *\n *  -------------------------------------------------  *\n *  |    x19    |    x20    |    x21    |    x22    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  24 |  25 |  26 |  27 |  28 |  29 |  30 |  31 |  *\n *  -------------------------------------------------  *\n *  | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c|  *\n *  -------------------------------------------------  *\n *  |    x23    |    x24    |    x25    |    x26    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  32 |  33 |  34 |  35 |  36 |  37 |  38 |  39 |  *\n *  -------------------------------------------------  *\n *  | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c|  *\n *  -------------------------------------------------  *\n *  |    x27    |    x28    |    FP     |     LR    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  40 |  41 |  42 | 43  |           |           |  *\n *  -------------------------------------------------  *\n *  | 0xa0| 0xa4| 0xa8| 0xac|           |           |  *\n *  -------------------------------------------------  *\n *  |     PC    |   align   |           |           |  *\n *  -------------------------------------------------  *\n *                                                     *\n *******************************************************/\n\n.text\n.globl _swoole_make_fcontext\n.balign 16\n\n_swoole_make_fcontext:\n    ; shift address in x0 (allocated stack) to lower 16 byte boundary\n    and x0, x0, ~0xF\n\n    ; reserve space for context-data on context-stack\n    sub  x0, x0, #0xb0\n\n    ; third arg of swoole_make_fcontext() == address of context-function\n    ; store address as a PC to jump in\n    str  x2, [x0, #0xa0]\n\n    adr  x1, finish\n\n    ; save address of finish as return-address for context-function\n    ; will be entered after context-function returns (LR register)\n    str  x1, [x0, #0x98]\n\n    ret  lr ; return pointer to context-data (x0)\n\nfinish:\n    ; exit code is zero\n    mov  x0, #0\n    ; exit application\n    bl  __exit\n\n\n"
  },
  {
    "path": "thirdparty/boost/asm/make_combined_sysv_macho_gas.S",
    "content": "/*\n            Copyright Sergue E. Leontiev 2013.\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n\n// Stub file for universal binary\n\n#if defined(__x86_64__)\n    #include \"make_x86_64_sysv_macho_gas.S\"\n#elif defined(__ppc64__)\n    #include \"make_ppc64_sysv_macho_gas.S\"\n#elif defined(__arm64__)\n    #include \"make_arm64_aapcs_macho_gas.S\"\n#else\n    #error \"No arch's\"\n#endif\n"
  },
  {
    "path": "thirdparty/boost/asm/make_loongarch64_sysv_elf_gas.S",
    "content": "/*******************************************************\n *                                                     *\n *  -------------------------------------------------  *\n *  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  *\n *  -------------------------------------------------  *\n *  |     0     |     8     |    16     |     24    |  *\n *  -------------------------------------------------  *\n *  |    FS0    |    FS1    |    FS2    |    FS3    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  8  |  9  |  10 |  11 |  12 |  13 |  14 |  15 |  *\n *  -------------------------------------------------  *\n *  |     32    |    40     |     48    |     56    |  *\n *  -------------------------------------------------  *\n *  |    FS4    |    FS5    |    FS6    |    FS7    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  16 |  17 |  18 |  19 |  20 |  21 |  22 |  23 |  *\n *  -------------------------------------------------  *\n *  |     64    |    72     |     80    |     88    |  *\n *  -------------------------------------------------  *\n *  |    S0     |    S1     |     S2    |     S3    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  24 |  25 |  26 |  27 |  28 |  29 |  30 |  31 |  *\n *  -------------------------------------------------  *\n *  |  96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 |  *\n *  -------------------------------------------------  *\n *  |    S4     |    S5     |     S6    |     S7    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  32 |  33 |  34 |  35 |  36 |  37 |  38 |  39 |  *\n *  -------------------------------------------------  *\n *  | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 |  *\n *  -------------------------------------------------  *\n *  |    S8     |    FP     |     RA    |     PC    |  *\n *  -------------------------------------------------  *\n *                                                     *\n * *****************************************************/\n\n.file \"make_loongarch64_sysv_elf_gas.S\"\n.text\n.globl swoole_make_fcontext\n.align 2\n.type swoole_make_fcontext,@function\nswoole_make_fcontext:\n    # shift address in A0 to lower 16 byte boundary\n    bstrins.d $a0, $zero, 3, 0\n\n    # reserve space for context-data on context-stack\n    addi.d  $a0, $a0, -160\n\n    # third arg of swoole_make_fcontext() == address of context-function\n    st.d  $a2, $a0, 152\n\n    # save address of finish as return-address for context-function\n    # will be entered after context-function returns\n    la.local  $a4, finish\n    st.d  $a4, $a0, 144\n\n    # return pointer to context-data\n    jr  $ra\n\nfinish:\n    # exit code is zero\n    li.d  $a0, 0\n    # call _exit(0)\n    b  %plt(_exit)\n\n.size swoole_make_fcontext, .-swoole_make_fcontext\n/* Mark that we don't need executable stack.  */\n.section .note.GNU-stack,\"\",%progbits\n"
  },
  {
    "path": "thirdparty/boost/asm/make_mips64_n64_elf_gas.S",
    "content": "/*\n            Copyright Jiaxun Yang 2018.\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n\n/*******************************************************\n *                                                     *\n *  -------------------------------------------------  *\n *  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  *\n *  -------------------------------------------------  *\n *  |     0     |     8     |    16     |     24    |  *\n *  -------------------------------------------------  *\n *  |    F24    |    F25    |    F26    |    F27    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  8  |  9  |  10 |  11 |  12 |  13 |  14 |  15 |  *\n *  -------------------------------------------------  *\n *  |     32    |    40     |     48    |     56    |  *\n *  -------------------------------------------------  *\n *  |    F28    |    F29    |    F30    |    F31    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  16 |  17 |  18 |  19 |  20 |  21 |  22 |  23 |  *\n *  -------------------------------------------------  *\n *  |     64    |    72     |     80    |     88    |  *\n *  -------------------------------------------------  *\n *  |    S0     |    S1     |     S2    |     S3    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  24 |  25 |  26 |  27 |  28 |  29 |  30 |  31 |  *\n *  -------------------------------------------------  *\n *  |  96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 |  *\n *  -------------------------------------------------  *\n *  |    S4     |    S5     |     S6    |     S7    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  32 |  33 |  34 |  35 |  36 |  37 |  38 |  39 |  *\n *  -------------------------------------------------  *\n *  | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 |  *\n *  -------------------------------------------------  *\n *  |    FP     |    GP     |     RA    |     PC    |  *\n *  -------------------------------------------------  *\n *                                                     *\n * *****************************************************/\n\n.file \"make_mips64_n64_elf_gas.S\"\n.text\n.globl swoole_make_fcontext\n.align 3\n.type swoole_make_fcontext,@function\n.ent swoole_make_fcontext\nswoole_make_fcontext:\n#ifdef __PIC__\n.set    noreorder\n.cpload $t9\n.set    reorder\n#endif\n    # shift address in A0 to lower 16 byte boundary\n    li $v1, 0xfffffffffffffff0\n    and $v0, $v1, $a0\n\n    # reserve space for context-data on context-stack\n    daddiu $v0, $v0, -160\n\n    # third arg of swoole_make_fcontext() == address of context-function\n    sd  $a2, 152($v0)\n    # save global pointer in context-data\n    sd  $gp, 136($v0)\n\n    # psudo instruction compute abs address of label finish based on GP\n    dla  $t9, finish\n\n    # save address of finish as return-address for context-function\n    # will be entered after context-function returns\n    sd  $t9, 144($v0)\n\n    jr  $ra # return pointer to context-data\n\nfinish:\n    # reload our gp register (needed for la)\n    daddiu $t0, $sp, -160\n    ld $gp, 136($t0)\n\n    # call _exit(0)\n    #  the previous function should have left the 16 bytes incoming argument\n    #  area on the stack which we reuse for calling _exit\n    dla $t9, _exit\n    move $a0, $zero\n    jr $t9\n.end swoole_make_fcontext\n.size swoole_make_fcontext, .-swoole_make_fcontext\n\n/* Mark that we don't need executable stack.  */\n.section .note.GNU-stack,\"\",%progbits\n"
  },
  {
    "path": "thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S",
    "content": "/*\n            Copyright Oliver Kowalke 2009.\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n\n/*******************************************************\n *                                                     *\n *  -------------------------------------------------  *\n *  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  *\n *  -------------------------------------------------  *\n *  |  0  |  4  |  8  |  12 |  16 |  20 |  24 |  28 |  *\n *  -------------------------------------------------  *\n *  |    TOC    |    R14    |    R15    |    R16    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  8  |  9  |  10 |  11 |  12 |  13 |  14 |  15 |  *\n *  -------------------------------------------------  *\n *  |  32 |  36 |  40 |  44 |  48 |  52 |  56 |  60 |  *\n *  -------------------------------------------------  *\n *  |    R17    |    R18    |     R19   |    R20    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  16 |  17 |  18 |  19 |  20 |  21 |  22 |  23 |  *\n *  -------------------------------------------------  *\n *  |  64 |  68 |  72 |  76 |  80 |  84 |  88 |  92 |  *\n *  -------------------------------------------------  *\n *  |    R21    |    R22    |    R23    |    R24    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  24 |  25 |  26 |  27 |  28 |  29 |  30 |  31 |  *\n *  -------------------------------------------------  *\n *  |  96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 |  *\n *  -------------------------------------------------  *\n *  |    R25    |    R26    |    R27    |    R28    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  32 |  33 |  34 |  35 |  36 |  37 |  38 |  39 |  *\n *  -------------------------------------------------  *\n *  | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 |  *\n *  -------------------------------------------------  *\n *  |    R29    |    R30    |    R31    |   hidden  |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  40 |  41 |  42 |  43 |  44 |  45 |  46 |  47 |  *\n *  -------------------------------------------------  *\n *  | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 |  *\n *  -------------------------------------------------  *\n *  |     CR    |     LR    |     PC    | back-chain|  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  48 |  49 |  50 |  51 |  52 |  53 |  54 |  55 |  *\n *  -------------------------------------------------  *\n *  | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 |  *\n *  -------------------------------------------------  *\n *  |  cr saved |  lr saved |  compiler |   linker  |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  56 |  57 |  58 |  59 |  60 |  61 |  62 |  63 |  *\n *  -------------------------------------------------  *\n *  | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 |  *\n *  -------------------------------------------------  *\n *  | TOC saved |    FCTX   |    DATA   |           |  *\n *  -------------------------------------------------  *\n *                                                     *\n *******************************************************/\n\n.file \"make_ppc64_sysv_elf_gas.S\"\n.globl swoole_make_fcontext\n#if _CALL_ELF == 2\n\t.text\n\t.align 2\nswoole_make_fcontext:\n\taddis\t%r2, %r12, .TOC.-swoole_make_fcontext@ha\n\taddi\t%r2, %r2, .TOC.-swoole_make_fcontext@l\n\t.localentry swoole_make_fcontext, . - swoole_make_fcontext\n#else\n\t.section \".opd\",\"aw\"\n\t.align 3\nswoole_make_fcontext:\n# ifdef _CALL_LINUX\n\t.quad\t.L.swoole_make_fcontext,.TOC.@tocbase,0\n\t.type\tswoole_make_fcontext,@function\n\t.text\n\t.align 2\n.L.swoole_make_fcontext:\n# else\n\t.hidden\t.swoole_make_fcontext\n\t.globl\t.swoole_make_fcontext\n\t.quad\t.swoole_make_fcontext,.TOC.@tocbase,0\n\t.size\tswoole_make_fcontext,24\n\t.type\t.swoole_make_fcontext,@function\n\t.text\n\t.align 2\n.swoole_make_fcontext:\n# endif\n#endif\n    # save return address into R6\n    mflr  %r6\n\n    # first arg of swoole_make_fcontext() == top address of context-stack\n    # shift address in R3 to lower 16 byte boundary\n    clrrdi  %r3, %r3, 4\n\n    # reserve space for context-data on context-stack\n    # including 64 byte of linkage + parameter area (R1 % 16 == 0)\n    subi  %r3, %r3, 248\n\n    # third arg of swoole_make_fcontext() == address of context-function\n    # entry point (ELFv2) or descriptor (ELFv1)\n#if _CALL_ELF == 2\n    # save address of context-function entry point\n    std  %r5, 176(%r3)\n#else\n    # save address of context-function entry point\n    ld   %r4, 0(%r5)\n    std  %r4, 176(%r3)\n    # save TOC of context-function\n    ld   %r4, 8(%r5)\n    std  %r4, 0(%r3)\n#endif\n\n    # set back-chain to zero\n    li   %r0, 0\n    std  %r0, 184(%r3)\n\n#if _CALL_ELF != 2\n    # zero in r3 indicates first jump to context-function\n    std  %r0, 152(%r3)\n#endif\n\n    # load LR\n    mflr  %r0\n    # jump to label 1\n    bl  1f\n1:\n    # load LR into R4\n    mflr  %r4\n    # compute abs address of label finish\n    addi  %r4, %r4, finish - 1b\n    # restore LR\n    mtlr  %r0\n    # save address of finish as return-address for context-function\n    # will be entered after context-function returns\n    std  %r4, 168(%r3)\n\n    # restore return address from R6\n    mtlr  %r6\n\n    blr  # return pointer to context-data\n\nfinish:\n    # save return address into R0\n    mflr  %r0\n    # save return address on stack, set up stack frame\n    std  %r0, 8(%r1)\n    # allocate stack space, R1 % 16 == 0\n    stdu  %r1, -32(%r1)\n\n    # exit code is zero\n    li  %r3, 0\n    # exit application\n    bl  _exit\n    nop\n#if _CALL_ELF == 2\n\t.size swoole_make_fcontext, .-swoole_make_fcontext\n#else\n# ifdef _CALL_LINUX\n\t.size .swoole_make_fcontext, .-.L.swoole_make_fcontext\n# else\n\t.size .swoole_make_fcontext, .-.swoole_make_fcontext\n# endif\n#endif\n\n/* Mark that we don't need executable stack.  */\n.section .note.GNU-stack,\"\",%progbits\n"
  },
  {
    "path": "thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S",
    "content": "/*\n            Copyright Oliver Kowalke 2009.\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n\n/*******************************************************\n *                                                     *\n *  -------------------------------------------------  *\n *  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  *\n *  -------------------------------------------------  *\n *  |  0  |  4  |  8  |  12 |  16 |  20 |  24 |  28 |  *\n *  -------------------------------------------------  *\n *  |    R13    |    R14    |    R15    |    R16    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  8  |  9  |  10 |  11 |  12 |  13 |  14 |  15 |  *\n *  -------------------------------------------------  *\n *  |  32 |  36 |  40 |  44 |  48 |  52 |  56 |  60 |  *\n *  -------------------------------------------------  *\n *  |    R17    |    R18    |     R19   |    R20    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  16 |  17 |  18 |  19 |  20 |  21 |  22 |  23 |  *\n *  -------------------------------------------------  *\n *  |  64 |  68 |  72 |  76 |  80 |  84 |  88 |  92 |  *\n *  -------------------------------------------------  *\n *  |    R21    |    R22    |    R23    |    R24    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  24 |  25 |  26 |  27 |  28 |  29 |  30 |  31 |  *\n *  -------------------------------------------------  *\n *  |  96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 |  *\n *  -------------------------------------------------  *\n *  |    R25    |    R26    |    R27    |    R28    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  32 |  33 |  34 |  35 |  36 |  37 |  38 |  39 |  *\n *  -------------------------------------------------  *\n *  | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 |  *\n *  -------------------------------------------------  *\n *  |    R29    |    R30    |    R31    |   hidden  |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  40 |  41 |  42 |  43 |  44 |  45 |  46 |  47 |  *\n *  -------------------------------------------------  *\n *  | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 |  *\n *  -------------------------------------------------  *\n *  |     CR    |     LR    |     PC    | back-chain|  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  48 |  49 |  50 |  51 |  52 |  53 |  54 |  55 |  *\n *  -------------------------------------------------  *\n *  | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 |  *\n *  -------------------------------------------------  *\n *  |  cr saved |  lr saved |  compiler |   linker  |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  56 |  57 |  58 |  59 |  60 |  61 |  62 |  63 |  *\n *  -------------------------------------------------  *\n *  | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 |  *\n *  -------------------------------------------------  *\n *  |    FCTX   |    DATA   |           |           |  *\n *  -------------------------------------------------  *\n *                                                     *\n *******************************************************/\n\n.text\n.globl _swoole_make_fcontext\n_swoole_make_fcontext:\n    ; save return address into R6\n    mflr  r6\n\n    ; first arg of swoole_make_fcontext() == top address of context-function\n    ; shift address in R3 to lower 16 byte boundary\n    clrrwi  r3, r3, 4\n\n    ; reserve space for context-data on context-stack\n    ; including 64 byte of linkage + parameter area (R1  16 == 0)\n    subi  r3, r3, 240\n\n    ; third arg of swoole_make_fcontext() == address of context-function\n    stw  r5, 176(r3)\n\n    ; set back-chain to zero\n    li   r0, 0\n    std  r0, 184(r3)\n\n    ; compute address of returned transfer_t\n    addi r0, r3, 224\n    mr   r4, r0\n    std  r4, 152(r3)\n\n    ; load LR\n    mflr  r0\n    ; jump to label 1\n    bl  l1\nl1:\n    ; load LR into R4\n    mflr  r4\n    ; compute abs address of label finish\n    addi  r4, r4, lo16((finish - .) + 4)\n    ; restore LR\n    mtlr  r0\n    ; save address of finish as return-address for context-function\n    ; will be entered after context-function returns\n    std  r4, 168(r3)\n\n    ; restore return address from R6\n    mtlr  r6\n\n    blr  ; return pointer to context-data\n\nfinish:\n    ; save return address into R0\n    mflr  r0\n    ; save return address on stack, set up stack frame\n    stw  r0, 8(r1)\n    ; allocate stack space, R1  16 == 0\n    stwu  r1, -32(r1)\n\n    ; set return value to zero\n    li  r3, 0\n    ; exit application\n    bl  __exit\n    nop\n"
  },
  {
    "path": "thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S",
    "content": "/*\n            Copyright Oliver Kowalke 2009.\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n\n/*******************************************************\n *                                                     *\n *  -------------------------------------------------  *\n *  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  *\n *  -------------------------------------------------  *\n *  |  0  |  4  |  8  |  12 |  16 |  20 |  24 |  28 |  *\n *  -------------------------------------------------  *\n *  |    TOC    |    R14    |    R15    |    R16    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  8  |  9  |  10 |  11 |  12 |  13 |  14 |  15 |  *\n *  -------------------------------------------------  *\n *  |  32 |  36 |  40 |  44 |  48 |  52 |  56 |  60 |  *\n *  -------------------------------------------------  *\n *  |    R17    |    R18    |     R19   |    R20    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  16 |  17 |  18 |  19 |  20 |  21 |  22 |  23 |  *\n *  -------------------------------------------------  *\n *  |  64 |  68 |  72 |  76 |  80 |  84 |  88 |  92 |  *\n *  -------------------------------------------------  *\n *  |    R21    |    R22    |    R23    |    R24    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  24 |  25 |  26 |  27 |  28 |  29 |  30 |  31 |  *\n *  -------------------------------------------------  *\n *  |  96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 |  *\n *  -------------------------------------------------  *\n *  |    R25    |    R26    |    R27    |    R28    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  32 |  33 |  34 |  35 |  36 |  37 |  38 |  39 |  *\n *  -------------------------------------------------  *\n *  | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 |  *\n *  -------------------------------------------------  *\n *  |    R29    |    R30    |    R31    |   hidden  |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  40 |  41 |  42 |  43 |  44 |  45 |  46 |  47 |  *\n *  -------------------------------------------------  *\n *  | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 |  *\n *  -------------------------------------------------  *\n *  |     CR    |     LR    |     PC    | back-chain|  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  48 |  49 |  50 |  51 |  52 |  53 |  54 |  55 |  *\n *  -------------------------------------------------  *\n *  | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 |  *\n *  -------------------------------------------------  *\n *  |  cr saved |  lr saved |  compiler |   linker  |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  56 |  57 |  58 |  59 |  60 |  61 |  62 |  63 |  *\n *  -------------------------------------------------  *\n *  | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 |  *\n *  -------------------------------------------------  *\n *  | TOC saved |    FCTX   |    DATA   |           |  *\n *  -------------------------------------------------  *\n *                                                     *\n *******************************************************/\n\n    .file \"make_ppc64_sysv_xcoff_gas.S\"\n    .toc\n    .csect .text[PR], 5\n    .align 2\n    .globl  swoole_make_fcontext[DS]\n    .globl .swoole_make_fcontext\n    .csect  swoole_make_fcontext[DS], 3\nswoole_make_fcontext:\n    .llong .swoole_make_fcontext[PR], TOC[tc0], 0\n    .csect .text[PR], 5\n.swoole_make_fcontext:\n    # save return address into R6\n    mflr  6\n\n    # first arg of swoole_make_fcontext() == top address of context-function\n    # shift address in R3 to lower 16 byte boundary\n    clrrdi  3, 3, 4\n\n    # reserve space for context-data on context-stack\n    # including 64 byte of linkage + parameter area (R1 % 16 == 0)\n    subi  3, 3, 248\n\n    # third arg of swoole_make_fcontext() == address of context-function descriptor\n    ld   4, 0(5)\n    std  4, 176(3)\n    # save TOC of context-function\n    ld   4, 8(5)\n    std  4, 0(3)\n\n    # set back-chain to zero\n    li   0, 0\n    std  0, 184(3)\n\n    # zero in r3 indicates first jump to context-function\n    std  0, 152(3)\n\n    # load LR\n    mflr  0\n    # jump to label 1\n    bl  .Label\n.Label:\n    # load LR into R4\n    mflr  4\n    # compute abs address of label .L_finish\n    addi  4, 4, .L_finish - .Label\n    # restore LR\n    mtlr  0\n    # save address of finish as return-address for context-function\n    # will be entered after context-function returns\n    std  4, 168(3)\n\n    # restore return address from R6\n    mtlr  6\n\n    blr  # return pointer to context-data\n\n.L_finish:\n    # save return address into R0\n    mflr  0\n    # save return address on stack, set up stack frame\n    std  0, 8(1)\n    # allocate stack space, R1 % 16 == 0\n    stdu  1, -32(1)\n\n    # exit code is zero\n    li  3, 0\n    # exit application\n    bl  ._exit\n    nop\n"
  },
  {
    "path": "thirdparty/boost/asm/make_riscv64_sysv_elf_gas.S",
    "content": "/*\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n/*******************************************************\n *                                                     *\n *  -------------------------------------------------  *\n *  |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  *\n *  -------------------------------------------------  *\n *  | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c|  *\n *  -------------------------------------------------  *\n *  |    fs0    |    fs1    |    fs2    |    fs3    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  8  |  9  |  10 |  11 |  12 |  13 |  14 |  15 |  *\n *  -------------------------------------------------  *\n *  | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c|  *\n *  -------------------------------------------------  *\n *  |    fs4    |    fs5    |    fs6    |    fs7    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  16 |  17 |  18 |  19 |  20 |  21 |  22 |  23 |  *\n *  -------------------------------------------------  *\n *  | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c|  *\n *  -------------------------------------------------  *\n *  |    fs8    |    fs9    |    fs10   |    fs11   |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  24 |  25 |  26 |  27 |  28 |  29 |  30 |  31 |  *\n *  -------------------------------------------------  *\n *  | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c|  *\n *  -------------------------------------------------  *\n *  |    s0     |    s1     |    s2     |    s3     |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  32 |  33 |  34 |  35 |  36 |  37 |  38 |  39 |  *\n *  -------------------------------------------------  *\n *  | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c|  *\n *  -------------------------------------------------  *\n *  |    s4     |    s5     |    s6     |     s7    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  40 |  41 |  42 | 43  |  44 | 45  |  46 | 47  |  *\n *  -------------------------------------------------  *\n *  | 0xa0| 0xa4| 0xa8| 0xac| 0xb0| 0xb4| 0xb8| 0xbc|  *\n *  -------------------------------------------------  *\n *  |     s8    |     s9    |    s10    |    s11    |  *\n *  -------------------------------------------------  *\n *  -------------------------------------------------  *\n *  |  48 |  49 |  50 | 51  |     |     |     |     |  *\n *  -------------------------------------------------  *\n *  | 0xc0| 0xc4| 0xc8| 0xcc|     |     |     |     |  *\n *  -------------------------------------------------  *\n *  |     ra    |     pc    |           |           |  *\n *  -------------------------------------------------  *\n *                                                     *\n *******************************************************/\n\n.file \"make_riscv64_sysv_elf_gas.S\"\n.text\n.align  1\n.global swoole_make_fcontext\n.type   swoole_make_fcontext, %function\nswoole_make_fcontext:\n    # shift address in a0 (allocated stack) to lower 16 byte boundary\n    andi a0, a0, ~0xF\n\n    # reserve space for context-data on context-stack\n    addi  a0, a0, -0xd0\n\n    # third arg of swoole_make_fcontext() == address of context-function\n    # store address as a PC to jump in\n    sd  a2, 0xc8(a0)\n\n    # save address of finish as return-address for context-function\n    # will be entered after context-function returns (RA register)\n    lla  a4, finish\n    sd  a4, 0xc0(a0)\n\n    ret // return pointer to context-data (a0)\n\nfinish:\n    # exit code is zero\n    li  a0, 0\n    # exit application\n    tail  _exit@plt\n\n.size   swoole_make_fcontext,.-swoole_make_fcontext\n# Mark that we don't need executable stack.\n.section .note.GNU-stack,\"\",%progbits\n"
  },
  {
    "path": "thirdparty/boost/asm/make_sparc64_sysv_elf_gas.S",
    "content": "/*\n            Copyright Martin Husemann 2013.\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n          http://www.boost.org/LICENSE_1_0.txt)\n*/\n\n/*******************************************************************\n *                                                                 *\n *  -------------------------------------------------------------  *\n *  |  Offset (in 4 or 8 byte units) | Content                  |  *\n *  -------------------------------------------------------------  *\n *  | 0                              | %sp                      |  *\n *  -------------------------------------------------------------  *\n *  | 1                              | %pc                      |  *\n *  -------------------------------------------------------------  *\n *  | 2                              | %i7 (return address)     |  *\n *  -------------------------------------------------------------  *\n *  | 3                              | %g1                      |  *\n *  -------------------------------------------------------------  *\n *  | 4                              | %g2                      |  *\n *  -------------------------------------------------------------  *\n *  | 5                              | %g3                      |  *\n *  -------------------------------------------------------------  *\n *  | 6                              | %g6                      |  *\n *  -------------------------------------------------------------  *\n *  | 7                              | %g7                      |  *\n *  -------------------------------------------------------------  *\n *    The local and in registers are stored on the stack.          *\n *******************************************************************/\n\n#define\tOFF(N)\t(8*(N))\n#define\tCCFSZ\t176\t\t// C Compiler Frame Size\n#define\tBIAS\t(2048-1)\t// Stack offset for 64 bit programs\n#define\tFC_SZ\t448\t\t// sizeof(fcontext_t)\n#define\tFC_STK\t384\t\t// offsetof(fcontext_t, fc_stack)\n#define\tFC_FPU\t0\t\t// offsetof(fcontext_t, fc_fp)\n#define\tFC_FSR\t264\t\t// offsetof(fcontext_t, fc_fp.fp_fsr)\n#define\tFC_FPRS\t256\t\t// offsetof(fcontext_t, fc_fp.fp_fprs)\n#define\tFC_GREG\t320\t\t// offsetof(fcontext_t, fc_greg)\n#define\tBLOCK_SIZE\t64\n\n\t.register %g2,#ignore\n\t.register %g3,#ignore\n\t.register %g6,#ignore\n\n.text\n.globl swoole_make_fcontext\n.align 4\n.type swoole_make_fcontext,@function\n// fcontext_t *\n// swoole_make_fcontext( void * sp, std::size_t size, void (* fn)( intptr_t) )\nswoole_make_fcontext:\n    save %sp, -CCFSZ, %sp\n    // %i0 initial stack pointer\n    // %i1 stack size limit\n    // %i2 function pointer for context start function\n\n    sub %i0, FC_SZ, %i4\t\t// allocate fcontext_t at on the new stack and keep pointer as return value\n    andn %i4, BLOCK_SIZE-1, %i5\t// force block ops usable alignement and keep pointer to fcontext in %i5\n\n    stx %i0, [%i5+FC_STK+OFF(0)]\t// save fs_stack.sp\n    stx %i1, [%i5+FC_STK+OFF(1)]\t// save fs_stack.size\n    sub %i5, CCFSZ+BIAS, %o1\t   \t// leave space for one register window (and offset stack for 64bit)\n    stx %o1, [%i5+FC_GREG+OFF(0)]\t// save new stack pointer\n    stx %i2, [%i5+FC_GREG+OFF(1)]\t// save new %pc (function pointer)\n    stx %g1, [%i5+FC_GREG+OFF(3)]\n    stx %g2, [%i5+FC_GREG+OFF(4)]\n    stx %g3, [%i5+FC_GREG+OFF(5)]\n    stx %g6, [%i5+FC_GREG+OFF(6)]\n    stx %g7, [%i5+FC_GREG+OFF(7)]\n\n    // synthesize \"return address\": jump to finish\n1:  rd %pc, %i4\n    add %i4, finish-1b-8, %i4\n    stx %i4, [%i5+FC_GREG+OFF(2)]\n\n    ret\n    restore %g0, %i5, %o0\t// return fcontext_t\n\nfinish:\n    mov %g0, %o0\n    call _exit\n     nop\n\n.size swoole_make_fcontext,.-swoole_make_fcontext\n\n/* Mark that we don't need executable stack.  */\n.section .note.GNU-stack,\"\",%progbits\n"
  },
  {
    "path": "thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S",
    "content": "/*\n            Copyright Oliver Kowalke 2009.\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n            http://www.boost.org/LICENSE_1_0.txt)\n*/\n\n/****************************************************************************************\n *                                                                                      *\n *  ----------------------------------------------------------------------------------  *\n *  |    0    |    1    |    2    |    3    |    4     |    5    |    6    |    7    |  *\n *  ----------------------------------------------------------------------------------  *\n *  |   0x0   |   0x4   |   0x8   |   0xc   |   0x10   |   0x14  |   0x18  |   0x1c  |  *\n *  ----------------------------------------------------------------------------------  *\n *  | fc_mxcsr|fc_x87_cw|       guard       |         R12        |        R13        |  *\n *  ----------------------------------------------------------------------------------  *\n *  ----------------------------------------------------------------------------------  *\n *  |    8    |    9    |   10    |   11    |    12    |    13   |    14   |    15   |  *\n *  ----------------------------------------------------------------------------------  *\n *  |   0x20  |   0x24  |   0x28  |  0x2c   |   0x30   |   0x34  |   0x38  |   0x3c  |  *\n *  ----------------------------------------------------------------------------------  *\n *  |        R14        |        R15        |         RBX        |        RBP        |  *\n *  ----------------------------------------------------------------------------------  *\n *  ----------------------------------------------------------------------------------  *\n *  |   16    |   17    |   18    |   19    |    20    |    21   |    22   |    23   |  *\n *  ----------------------------------------------------------------------------------  *\n *  |   0x40  |   0x44  |                                                            |  *\n *  ----------------------------------------------------------------------------------  *\n *  |        RIP        |                                                            |  *\n *  ----------------------------------------------------------------------------------  *\n *                                                                                      *\n ****************************************************************************************/\n\n# if defined __CET__\n#  include <cet.h>\n#  define SWOOLE_SHSTK_ENABLED (__CET__ & 0x2)\n#  define SWOOLE_CONTEXT_SHADOW_STACK (SWOOLE_SHSTK_ENABLED && SHADOW_STACK_SYSCALL)\n# else\n#  define _CET_ENDBR\n# endif\n.file \"make_x86_64_sysv_elf_gas.S\"\n.text\n.globl swoole_make_fcontext\n.type swoole_make_fcontext,@function\n.align 16\nswoole_make_fcontext:\n    _CET_ENDBR\n#if SWOOLE_CONTEXT_SHADOW_STACK\n    /* the new shadow stack pointer (SSP) */\n    movq  -0x8(%rdi), %r9\n#endif\n\n    /* first arg of swoole_make_fcontext() == top of context-stack */\n    movq  %rdi, %rax\n\n    /* shift address in RAX to lower 16 byte boundary */\n    andq  $-16, %rax\n\n    /* reserve space for context-data on context-stack */\n    /* on context-function entry: (RSP -0x8) % 16 == 0 */\n    leaq  -0x48(%rax), %rax\n\n    /* third arg of swoole_make_fcontext() == address of context-function */\n    /* stored in RBX */\n    movq  %rdx, 0x30(%rax)\n\n    /* save MMX control- and status-word */\n    stmxcsr  (%rax)\n    /* save x87 control-word */\n    fnstcw   0x4(%rax)\n\n#if defined(SWOOLE_CONTEXT_TLS_STACK_PROTECTOR)\n    /* save stack guard */\n    movq  %fs:0x28, %rcx    /* read stack guard from TLS record */\n    movq  %rcx, 0x8(%rsp)   /* save stack guard */\n#endif\n\n    /* compute abs address of label trampoline */\n    leaq  trampoline(%rip), %rcx\n    /* save address of trampoline as return-address for context-function */\n    /* will be entered after calling jump_fcontext() first time */\n    movq  %rcx, 0x40(%rax)\n\n    /* compute abs address of label finish */\n    leaq  finish(%rip), %rcx\n    /* save address of finish as return-address for context-function */\n    /* will be entered after context-function returns */\n    movq  %rcx, 0x38(%rax)\n\n#if SWOOLE_CONTEXT_SHADOW_STACK\n    /* Populate the shadow stack and normal stack */\n    /* get original SSP */\n    rdsspq  %r8\n    /* restore new shadow stack */\n    rstorssp  -0x8(%r9)\n    /* save the restore token on the original shadow stack */\n    saveprevssp\n    /* push the address of \"jmp trampoline\" to the new shadow stack */\n    /* as well as the stack */\n    call  1f\n    jmp  trampoline\n1:\n    /* save address of \"jmp trampoline\" as return-address */\n    /* for context-function */\n    pop 0x38(%rax)\n    /* Get the new SSP.  */\n    rdsspq  %r9\n    /* restore original shadow stack */\n    rstorssp  -0x8(%r8)\n    /* save the restore token on the new shadow stack.  */\n    saveprevssp\n\n    /* reserve space for the new SSP */\n    leaq  -0x8(%rax), %rax\n    /* save the new SSP to this fcontext */\n    movq  %r9, (%rax)\n#endif\n\n    ret /* return pointer to context-data */\n\ntrampoline:\n    _CET_ENDBR\n    /* store return address on stack */\n    /* fix stack alignment */\n#if SWOOLE_CONTEXT_SHADOW_STACK\n    /* save address of \"jmp *%rbp\" as return-address */\n    /* on stack and shadow stack */\n    call  2f\n    jmp  *%rbp\n2:\n#else\n    push %rbp\n#endif\n    /* jump to context-function */\n    jmp *%rbx\n\nfinish:\n    _CET_ENDBR\n    /* exit code is zero */\n    xorq  %rdi, %rdi\n    /* exit application */\n    call  _exit@PLT\n    hlt\n.size swoole_make_fcontext,.-swoole_make_fcontext\n\n/* Mark that we don't need executable stack. */\n.section .note.GNU-stack,\"\",%progbits\n"
  },
  {
    "path": "thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S",
    "content": "/*\n            Copyright Oliver Kowalke 2009.\n   Distributed under the Boost Software License, Version 1.0.\n      (See accompanying file LICENSE_1_0.txt or copy at\n            http://www.boost.org/LICENSE_1_0.txt)\n*/\n\n/****************************************************************************************\n *                                                                                      *\n *  ----------------------------------------------------------------------------------  *\n *  |    0    |    1    |    2    |    3    |    4     |    5    |    6    |    7    |  *\n *  ----------------------------------------------------------------------------------  *\n *  |   0x0   |   0x4   |   0x8   |   0xc   |   0x10   |   0x14  |   0x18  |   0x1c  |  *\n *  ----------------------------------------------------------------------------------  *\n *  | fc_mxcsr|fc_x87_cw|        R12        |         R13        |        R14        |  *\n *  ----------------------------------------------------------------------------------  *\n *  ----------------------------------------------------------------------------------  *\n *  |    8    |    9    |   10    |   11    |    12    |    13   |    14   |    15   |  *\n *  ----------------------------------------------------------------------------------  *\n *  |   0x20  |   0x24  |   0x28  |  0x2c   |   0x30   |   0x34  |   0x38  |   0x3c  |  *\n *  ----------------------------------------------------------------------------------  *\n *  |        R15        |        RBX        |         RBP        |        RIP        |  *\n *  ----------------------------------------------------------------------------------  *\n *                                                                                      *\n ****************************************************************************************/\n\n.text\n.globl _swoole_make_fcontext\n.align 8\n_swoole_make_fcontext:\n    /* first arg of swoole_make_fcontext() == top of context-stack */\n    movq  %rdi, %rax\n\n    /* shift address in RAX to lower 16 byte boundary */\n    andq  $-16, %rax\n\n    /* reserve space for context-data on context-stack */\n    /* on context-function entry: (RSP -0x8) % 16 == 0 */\n    leaq  -0x40(%rax), %rax\n\n    /* third arg of swoole_make_fcontext() == address of context-function */\n    /* stored in RBX */\n    movq  %rdx, 0x28(%rax)\n\n    /* save MMX control- and status-word */\n    stmxcsr  (%rax)\n    /* save x87 control-word */\n    fnstcw   0x4(%rax)\n\n    /* compute abs address of label trampoline */\n    leaq  trampoline(%rip), %rcx\n    /* save address of trampoline as return-address for context-function */\n    /* will be entered after calling jump_fcontext() first time */\n    movq  %rcx, 0x38(%rax)\n\n    /* compute abs address of label finish */\n    leaq  finish(%rip), %rcx\n    /* save address of finish as return-address for context-function */\n    /* will be entered after context-function returns */\n    movq  %rcx, 0x30(%rax)\n\n    ret /* return pointer to context-data */\n\ntrampoline:\n    /* store return address on stack */\n    /* fix stack alignment */\n    push %rbp\n    /* jump to context-function */\n    jmp *%rbx\n\nfinish:\n    /* exit code is zero */\n    xorq  %rdi, %rdi\n    /* exit application */\n    call  __exit\n    hlt\n"
  },
  {
    "path": "thirdparty/hiredis/CHANGELOG.md",
    "content": "### 1.0.0 (unreleased)\n\n**BREAKING CHANGES**:\n\n* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now\n  protocol errors. This is consistent with the RESP specification. On 32-bit\n  platforms, the upper bound is lowered to `SIZE_MAX`.\n\n* Change `redisReply.len` to `size_t`, as it denotes the the size of a string\n\n  User code should compare this to `size_t` values as well.  If it was used to\n  compare to other values, casting might be necessary or can be removed, if\n  casting was applied before.\n\n### 0.14.0 (2018-09-25)\n\n* Make string2ll static to fix conflict with Redis (Tom Lee [c3188b])\n* Use -dynamiclib instead of -shared for OSX (Ryan Schmidt [a65537])\n* Use string2ll from Redis w/added tests (Michael Grunder [7bef04, 60f622])\n* Makefile - OSX compilation fixes (Ryan Schmidt [881fcb, 0e9af8])\n* Remove redundant NULL checks (Justin Brewer [54acc8, 58e6b8])\n* Fix bulk and multi-bulk length truncation (Justin Brewer [109197])\n* Fix SIGSEGV in OpenBSD by checking for NULL before calling freeaddrinfo (Justin Brewer [546d94])\n* Several POSIX compatibility fixes (Justin Brewer [bbeab8, 49bbaa, d1c1b6])\n* Makefile - Compatibility fixes (Dimitri Vorobiev [3238cf, 12a9d1])\n* Makefile - Fix make install on FreeBSD (Zach Shipko [a2ef2b])\n* Makefile - don't assume $(INSTALL) is cp (Igor Gnatenko [725a96])\n* Separate side-effect causing function from assert and small cleanup (amallia [b46413, 3c3234])\n* Don't send negative values to `__redisAsyncCommand` (Frederik Deweerdt [706129])\n* Fix leak if setsockopt fails (Frederik Deweerdt [e21c9c])\n* Fix libevent leak (zfz [515228])\n* Clean up GCC warning (Ichito Nagata [2ec774])\n* Keep track of errno in `__redisSetErrorFromErrno()` as snprintf may use it (Jin Qing [25cd88])\n* Solaris compilation fix (Donald Whyte [41b07d])\n* Reorder linker arguments when building examples (Tustfarm-heart [06eedd])\n* Keep track of subscriptions in case of rapid subscribe/unsubscribe (Hyungjin Kim [073dc8, be76c5, d46999])\n* libuv use after free fix (Paul Scott [cbb956])\n* Properly close socket fd on reconnect attempt (WSL [64d1ec])\n* Skip valgrind in OSX tests (Jan-Erik Rediger [9deb78])\n* Various updates for Travis testing OSX (Ted Nyman [fa3774, 16a459, bc0ea5])\n* Update libevent (Chris Xin [386802])\n* Change sds.h for building in C++ projects (Ali Volkan ATLI [f5b32e])\n* Use proper format specifier in redisFormatSdsCommandArgv (Paulino Huerta, Jan-Erik Rediger [360a06, 8655a6])\n* Better handling of NULL reply in example code (Jan-Erik Rediger [1b8ed3])\n* Prevent overflow when formatting an error (Jan-Erik Rediger [0335cb])\n* Compatibility fix for strerror_r (Tom Lee [bb1747])\n* Properly detect integer parse/overflow errors (Justin Brewer [93421f])\n* Adds CI for Windows and cygwin fixes (owent, [6c53d6, 6c3e40])\n* Catch a buffer overflow when formatting the error message\n* Import latest upstream sds. This breaks applications that are linked against the old hiredis v0.13\n* Fix warnings, when compiled with -Wshadow\n* Make hiredis compile in Cygwin on Windows, now CI-tested\n\n**BREAKING CHANGES**:\n\n* Remove backwards compatibility macro's\n\nThis removes the following old function aliases, use the new name now:\n\n| Old                         | New                    |\n| --------------------------- | ---------------------- |\n| redisReplyReaderCreate      | redisReaderCreate      |\n| redisReplyReaderCreate      | redisReaderCreate      |\n| redisReplyReaderFree        | redisReaderFree        |\n| redisReplyReaderFeed        | redisReaderFeed        |\n| redisReplyReaderGetReply    | redisReaderGetReply    |\n| redisReplyReaderSetPrivdata | redisReaderSetPrivdata |\n| redisReplyReaderGetObject   | redisReaderGetObject   |\n| redisReplyReaderGetError    | redisReaderGetError    |\n\n* The `DEBUG` variable in the Makefile was renamed to `DEBUG_FLAGS`\n\nPreviously it broke some builds for people that had `DEBUG` set to some arbitrary value,\ndue to debugging other software.\nBy renaming we avoid unintentional name clashes.\n\nSimply rename `DEBUG` to `DEBUG_FLAGS` in your environment to make it working again.\n\n### 0.13.3 (2015-09-16)\n\n* Revert \"Clear `REDIS_CONNECTED` flag when connection is closed\".\n* Make tests pass on FreeBSD (Thanks, Giacomo Olgeni)\n\n\nIf the `REDIS_CONNECTED` flag is cleared,\nthe async onDisconnect callback function will never be called.\nThis causes problems as the disconnect is never reported back to the user.\n\n### 0.13.2 (2015-08-25)\n\n* Prevent crash on pending replies in async code (Thanks, @switch-st)\n* Clear `REDIS_CONNECTED` flag when connection is closed (Thanks, Jerry Jacobs)\n* Add MacOS X addapter (Thanks, @dizzus)\n* Add Qt adapter (Thanks, Pietro Cerutti)\n* Add Ivykis adapter (Thanks, Gergely Nagy)\n\nAll adapters are provided as is and are only tested where possible.\n\n### 0.13.1 (2015-05-03)\n\nThis is a bug fix release.\nThe new `reconnect` method introduced new struct members, which clashed with pre-defined names in pre-C99 code.\nAnother commit forced C99 compilation just to make it work, but of course this is not desirable for outside projects.\nOther non-C99 code can now use hiredis as usual again.\nSorry for the inconvenience.\n\n* Fix memory leak in async reply handling (Salvatore Sanfilippo)\n* Rename struct member to avoid name clash with pre-c99 code (Alex Balashov, ncopa)\n\n### 0.13.0 (2015-04-16)\n\nThis release adds a minimal Windows compatibility layer.\nThe parser, standalone since v0.12.0, can now be compiled on Windows\n(and thus used in other client libraries as well)\n\n* Windows compatibility layer for parser code (tzickel)\n* Properly escape data printed to PKGCONF file (Dan Skorupski)\n* Fix tests when assert() undefined (Keith Bennett, Matt Stancliff)\n* Implement a reconnect method for the client context, this changes the structure of `redisContext` (Aaron Bedra)\n\n### 0.12.1 (2015-01-26)\n\n* Fix `make install`: DESTDIR support, install all required files, install PKGCONF in proper location\n* Fix `make test` as 32 bit build on 64 bit platform\n\n### 0.12.0 (2015-01-22)\n\n* Add optional KeepAlive support\n\n* Try again on EINTR errors\n\n* Add libuv adapter\n\n* Add IPv6 support\n\n* Remove possibility of multiple close on same fd\n\n* Add ability to bind source address on connect\n\n* Add redisConnectFd() and redisFreeKeepFd()\n\n* Fix getaddrinfo() memory leak\n\n* Free string if it is unused (fixes memory leak)\n\n* Improve redisAppendCommandArgv performance 2.5x\n\n* Add support for SO_REUSEADDR\n\n* Fix redisvFormatCommand format parsing\n\n* Add GLib 2.0 adapter\n\n* Refactor reading code into read.c\n\n* Fix errno error buffers to not clobber errors\n\n* Generate pkgconf during build\n\n* Silence _BSD_SOURCE warnings\n\n* Improve digit counting for multibulk creation\n\n\n### 0.11.0\n\n* Increase the maximum multi-bulk reply depth to 7.\n\n* Increase the read buffer size from 2k to 16k.\n\n* Use poll(2) instead of select(2) to support large fds (>= 1024).\n\n### 0.10.1\n\n* Makefile overhaul. Important to check out if you override one or more\n  variables using environment variables or via arguments to the \"make\" tool.\n\n* Issue #45: Fix potential memory leak for a multi bulk reply with 0 elements\n  being created by the default reply object functions.\n\n* Issue #43: Don't crash in an asynchronous context when Redis returns an error\n  reply after the connection has been made (this happens when the maximum\n  number of connections is reached).\n\n### 0.10.0\n\n* See commit log.\n\n"
  },
  {
    "path": "thirdparty/hiredis/COPYING",
    "content": "Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\nCopyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice,\n  this list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\n* Neither the name of Redis nor the names of its contributors may be used\n  to endorse or promote products derived from this software without specific\n  prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "thirdparty/hiredis/README.md",
    "content": "[![Build Status](https://travis-ci.org/redis/hiredis.png)](https://travis-ci.org/redis/hiredis)\n\n**This Readme reflects the latest changed in the master branch. See [v0.13.3](https://github.com/redis/hiredis/tree/v0.13.3) for the Readme and documentation for the latest release.**\n\n# HIREDIS\n\nHiredis is a minimalistic C client library for the [Redis](http://redis.io/) database.\n\nIt is minimalistic because it just adds minimal support for the protocol, but\nat the same time it uses a high level printf-alike API in order to make it\nmuch higher level than otherwise suggested by its minimal code base and the\nlack of explicit bindings for every Redis command.\n\nApart from supporting sending commands and receiving replies, it comes with\na reply parser that is decoupled from the I/O layer. It\nis a stream parser designed for easy reusability, which can for instance be used\nin higher level language bindings for efficient reply parsing.\n\nHiredis only supports the binary-safe Redis protocol, so you can use it with any\nRedis version >= 1.2.0.\n\nThe library comes with multiple APIs. There is the\n*synchronous API*, the *asynchronous API* and the *reply parsing API*.\n\n## Upgrading to `1.0.0`\n\nVersion 1.0.0 marks a stable release of hiredis.\nIt includes some minor breaking changes, mostly to make the exposed API more uniform and self-explanatory.\nIt also bundles the updated `sds` library, to sync up with upstream and Redis.\nFor most applications a recompile against the new hiredis should be enough.\nFor code changes see the [Changelog](CHANGELOG.md).\n\n## Upgrading from `<0.9.0`\n\nVersion 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing\ncode using hiredis should not be a big pain. The key thing to keep in mind when\nupgrading is that hiredis >= 0.9.0 uses a `redisContext*` to keep state, in contrast to\nthe stateless 0.0.1 that only has a file descriptor to work with.\n\n## Synchronous API\n\nTo consume the synchronous API, there are only a few function calls that need to be introduced:\n\n```c\nredisContext *redisConnect(const char *ip, int port);\nvoid *redisCommand(redisContext *c, const char *format, ...);\nvoid freeReplyObject(void *reply);\n```\n\n### Connecting\n\nThe function `redisConnect` is used to create a so-called `redisContext`. The\ncontext is where Hiredis holds state for a connection. The `redisContext`\nstruct has an integer `err` field that is non-zero when the connection is in\nan error state. The field `errstr` will contain a string with a description of\nthe error. More information on errors can be found in the **Errors** section.\nAfter trying to connect to Redis using `redisConnect` you should\ncheck the `err` field to see if establishing the connection was successful:\n```c\nredisContext *c = redisConnect(\"127.0.0.1\", 6379);\nif (c == NULL || c->err) {\n    if (c) {\n        printf(\"Error: %s\\n\", c->errstr);\n        // handle error\n    } else {\n        printf(\"Can't allocate redis context\\n\");\n    }\n}\n```\n\n*Note: A `redisContext` is not thread-safe.*\n\n### Sending commands\n\nThere are several ways to issue commands to Redis. The first that will be introduced is\n`redisCommand`. This function takes a format similar to printf. In the simplest form,\nit is used like this:\n```c\nreply = redisCommand(context, \"SET foo bar\");\n```\n\nThe specifier `%s` interpolates a string in the command, and uses `strlen` to\ndetermine the length of the string:\n```c\nreply = redisCommand(context, \"SET foo %s\", value);\n```\nWhen you need to pass binary safe strings in a command, the `%b` specifier can be\nused. Together with a pointer to the string, it requires a `size_t` length argument\nof the string:\n```c\nreply = redisCommand(context, \"SET foo %b\", value, (size_t) valuelen);\n```\nInternally, Hiredis splits the command in different arguments and will\nconvert it to the protocol used to communicate with Redis.\nOne or more spaces separates arguments, so you can use the specifiers\nanywhere in an argument:\n```c\nreply = redisCommand(context, \"SET key:%s %s\", myid, value);\n```\n\n### Using replies\n\nThe return value of `redisCommand` holds a reply when the command was\nsuccessfully executed. When an error occurs, the return value is `NULL` and\nthe `err` field in the context will be set (see section on **Errors**).\nOnce an error is returned the context cannot be reused and you should set up\na new connection.\n\nThe standard replies that `redisCommand` are of the type `redisReply`. The\n`type` field in the `redisReply` should be used to test what kind of reply\nwas received:\n\n* **`REDIS_REPLY_STATUS`**:\n    * The command replied with a status reply. The status string can be accessed using `reply->str`.\n      The length of this string can be accessed using `reply->len`.\n\n* **`REDIS_REPLY_ERROR`**:\n    *  The command replied with an error. The error string can be accessed identical to `REDIS_REPLY_STATUS`.\n\n* **`REDIS_REPLY_INTEGER`**:\n    * The command replied with an integer. The integer value can be accessed using the\n      `reply->integer` field of type `long long`.\n\n* **`REDIS_REPLY_NIL`**:\n    * The command replied with a **nil** object. There is no data to access.\n\n* **`REDIS_REPLY_STRING`**:\n    * A bulk (string) reply. The value of the reply can be accessed using `reply->str`.\n      The length of this string can be accessed using `reply->len`.\n\n* **`REDIS_REPLY_ARRAY`**:\n    * A multi bulk reply. The number of elements in the multi bulk reply is stored in\n      `reply->elements`. Every element in the multi bulk reply is a `redisReply` object as well\n      and can be accessed via `reply->element[..index..]`.\n      Redis may reply with nested arrays but this is fully supported.\n\nReplies should be freed using the `freeReplyObject()` function.\nNote that this function will take care of freeing sub-reply objects\ncontained in arrays and nested arrays, so there is no need for the user to\nfree the sub replies (it is actually harmful and will corrupt the memory).\n\n**Important:** the current version of hiredis (0.10.0) frees replies when the\nasynchronous API is used. This means you should not call `freeReplyObject` when\nyou use this API. The reply is cleaned up by hiredis _after_ the callback\nreturns. This behavior will probably change in future releases, so make sure to\nkeep an eye on the changelog when upgrading (see issue #39).\n\n### Cleaning up\n\nTo disconnect and free the context the following function can be used:\n```c\nvoid redisFree(redisContext *c);\n```\nThis function immediately closes the socket and then frees the allocations done in\ncreating the context.\n\n### Sending commands (cont'd)\n\nTogether with `redisCommand`, the function `redisCommandArgv` can be used to issue commands.\nIt has the following prototype:\n```c\nvoid *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);\n```\nIt takes the number of arguments `argc`, an array of strings `argv` and the lengths of the\narguments `argvlen`. For convenience, `argvlen` may be set to `NULL` and the function will\nuse `strlen(3)` on every argument to determine its length. Obviously, when any of the arguments\nneed to be binary safe, the entire array of lengths `argvlen` should be provided.\n\nThe return value has the same semantic as `redisCommand`.\n\n### Pipelining\n\nTo explain how Hiredis supports pipelining in a blocking connection, there needs to be\nunderstanding of the internal execution flow.\n\nWhen any of the functions in the `redisCommand` family is called, Hiredis first formats the\ncommand according to the Redis protocol. The formatted command is then put in the output buffer\nof the context. This output buffer is dynamic, so it can hold any number of commands.\nAfter the command is put in the output buffer, `redisGetReply` is called. This function has the\nfollowing two execution paths:\n\n1. The input buffer is non-empty:\n    * Try to parse a single reply from the input buffer and return it\n    * If no reply could be parsed, continue at *2*\n2. The input buffer is empty:\n    * Write the **entire** output buffer to the socket\n    * Read from the socket until a single reply could be parsed\n\nThe function `redisGetReply` is exported as part of the Hiredis API and can be used when a reply\nis expected on the socket. To pipeline commands, the only things that needs to be done is\nfilling up the output buffer. For this cause, two commands can be used that are identical\nto the `redisCommand` family, apart from not returning a reply:\n```c\nvoid redisAppendCommand(redisContext *c, const char *format, ...);\nvoid redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);\n```\nAfter calling either function one or more times, `redisGetReply` can be used to receive the\nsubsequent replies. The return value for this function is either `REDIS_OK` or `REDIS_ERR`, where\nthe latter means an error occurred while reading a reply. Just as with the other commands,\nthe `err` field in the context can be used to find out what the cause of this error is.\n\nThe following examples shows a simple pipeline (resulting in only a single call to `write(2)` and\na single call to `read(2)`):\n```c\nredisReply *reply;\nredisAppendCommand(context,\"SET foo bar\");\nredisAppendCommand(context,\"GET foo\");\nredisGetReply(context,&reply); // reply for SET\nfreeReplyObject(reply);\nredisGetReply(context,&reply); // reply for GET\nfreeReplyObject(reply);\n```\nThis API can also be used to implement a blocking subscriber:\n```c\nreply = redisCommand(context,\"SUBSCRIBE foo\");\nfreeReplyObject(reply);\nwhile(redisGetReply(context,&reply) == REDIS_OK) {\n    // consume message\n    freeReplyObject(reply);\n}\n```\n### Errors\n\nWhen a function call is not successful, depending on the function either `NULL` or `REDIS_ERR` is\nreturned. The `err` field inside the context will be non-zero and set to one of the\nfollowing constants:\n\n* **`REDIS_ERR_IO`**:\n    There was an I/O error while creating the connection, trying to write\n    to the socket or read from the socket. If you included `errno.h` in your\n    application, you can use the global `errno` variable to find out what is\n    wrong.\n\n* **`REDIS_ERR_EOF`**:\n    The server closed the connection which resulted in an empty read.\n\n* **`REDIS_ERR_PROTOCOL`**:\n    There was an error while parsing the protocol.\n\n* **`REDIS_ERR_OTHER`**:\n    Any other error. Currently, it is only used when a specified hostname to connect\n    to cannot be resolved.\n\nIn every case, the `errstr` field in the context will be set to hold a string representation\nof the error.\n\n## Asynchronous API\n\nHiredis comes with an asynchronous API that works easily with any event library.\nExamples are bundled that show using Hiredis with [libev](http://software.schmorp.de/pkg/libev.html)\nand [libevent](http://monkey.org/~provos/libevent/).\n\n### Connecting\n\nThe function `redisAsyncConnect` can be used to establish a non-blocking connection to\nRedis. It returns a pointer to the newly created `redisAsyncContext` struct. The `err` field\nshould be checked after creation to see if there were errors creating the connection.\nBecause the connection that will be created is non-blocking, the kernel is not able to\ninstantly return if the specified host and port is able to accept a connection.\n\n*Note: A `redisAsyncContext` is not thread-safe.*\n\n```c\nredisAsyncContext *c = redisAsyncConnect(\"127.0.0.1\", 6379);\nif (c->err) {\n    printf(\"Error: %s\\n\", c->errstr);\n    // handle error\n}\n```\n\nThe asynchronous context can hold a disconnect callback function that is called when the\nconnection is disconnected (either because of an error or per user request). This function should\nhave the following prototype:\n```c\nvoid(const redisAsyncContext *c, int status);\n```\nOn a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the\nuser, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `err`\nfield in the context can be accessed to find out the cause of the error.\n\nThe context object is always freed after the disconnect callback fired. When a reconnect is needed,\nthe disconnect callback is a good point to do so.\n\nSetting the disconnect callback can only be done once per context. For subsequent calls it will\nreturn `REDIS_ERR`. The function to set the disconnect callback has the following prototype:\n```c\nint redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);\n```\n### Sending commands and their callbacks\n\nIn an asynchronous context, commands are automatically pipelined due to the nature of an event loop.\nTherefore, unlike the synchronous API, there is only a single way to send commands.\nBecause commands are sent to Redis asynchronously, issuing a command requires a callback function\nthat is called when the reply is received. Reply callbacks should have the following prototype:\n```c\nvoid(redisAsyncContext *c, void *reply, void *privdata);\n```\nThe `privdata` argument can be used to curry arbitrary data to the callback from the point where\nthe command is initially queued for execution.\n\nThe functions that can be used to issue commands in an asynchronous context are:\n```c\nint redisAsyncCommand(\n  redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,\n  const char *format, ...);\nint redisAsyncCommandArgv(\n  redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,\n  int argc, const char **argv, const size_t *argvlen);\n```\nBoth functions work like their blocking counterparts. The return value is `REDIS_OK` when the command\nwas successfully added to the output buffer and `REDIS_ERR` otherwise. Example: when the connection\nis being disconnected per user-request, no new commands may be added to the output buffer and `REDIS_ERR` is\nreturned on calls to the `redisAsyncCommand` family.\n\nIf the reply for a command with a `NULL` callback is read, it is immediately freed. When the callback\nfor a command is non-`NULL`, the memory is freed immediately following the callback: the reply is only\nvalid for the duration of the callback.\n\nAll pending callbacks are called with a `NULL` reply when the context encountered an error.\n\n### Disconnecting\n\nAn asynchronous connection can be terminated using:\n```c\nvoid redisAsyncDisconnect(redisAsyncContext *ac);\n```\nWhen this function is called, the connection is **not** immediately terminated. Instead, new\ncommands are no longer accepted and the connection is only terminated when all pending commands\nhave been written to the socket, their respective replies have been read and their respective\ncallbacks have been executed. After this, the disconnection callback is executed with the\n`REDIS_OK` status and the context object is freed.\n\n### Hooking it up to event library *X*\n\nThere are a few hooks that need to be set on the context object after it is created.\nSee the `adapters/` directory for bindings to *libev* and *libevent*.\n\n## Reply parsing API\n\nHiredis comes with a reply parsing API that makes it easy for writing higher\nlevel language bindings.\n\nThe reply parsing API consists of the following functions:\n```c\nredisReader *redisReaderCreate(void);\nvoid redisReaderFree(redisReader *reader);\nint redisReaderFeed(redisReader *reader, const char *buf, size_t len);\nint redisReaderGetReply(redisReader *reader, void **reply);\n```\nThe same set of functions are used internally by hiredis when creating a\nnormal Redis context, the above API just exposes it to the user for a direct\nusage.\n\n### Usage\n\nThe function `redisReaderCreate` creates a `redisReader` structure that holds a\nbuffer with unparsed data and state for the protocol parser.\n\nIncoming data -- most likely from a socket -- can be placed in the internal\nbuffer of the `redisReader` using `redisReaderFeed`. This function will make a\ncopy of the buffer pointed to by `buf` for `len` bytes. This data is parsed\nwhen `redisReaderGetReply` is called. This function returns an integer status\nand a reply object (as described above) via `void **reply`. The returned status\ncan be either `REDIS_OK` or `REDIS_ERR`, where the latter means something went\nwrong (either a protocol error, or an out of memory error).\n\nThe parser limits the level of nesting for multi bulk payloads to 7. If the\nmulti bulk nesting level is higher than this, the parser returns an error.\n\n### Customizing replies\n\nThe function `redisReaderGetReply` creates `redisReply` and makes the function\nargument `reply` point to the created `redisReply` variable. For instance, if\nthe response of type `REDIS_REPLY_STATUS` then the `str` field of `redisReply`\nwill hold the status as a vanilla C string. However, the functions that are\nresponsible for creating instances of the `redisReply` can be customized by\nsetting the `fn` field on the `redisReader` struct. This should be done\nimmediately after creating the `redisReader`.\n\nFor example, [hiredis-rb](https://github.com/pietern/hiredis-rb/blob/master/ext/hiredis_ext/reader.c)\nuses customized reply object functions to create Ruby objects.\n\n### Reader max buffer\n\nBoth when using the Reader API directly or when using it indirectly via a\nnormal Redis context, the redisReader structure uses a buffer in order to\naccumulate data from the server.\nUsually this buffer is destroyed when it is empty and is larger than 16\nKiB in order to avoid wasting memory in unused buffers\n\nHowever when working with very big payloads destroying the buffer may slow\ndown performances considerably, so it is possible to modify the max size of\nan idle buffer changing the value of the `maxbuf` field of the reader structure\nto the desired value. The special value of 0 means that there is no maximum\nvalue for an idle buffer, so the buffer will never get freed.\n\nFor instance if you have a normal Redis context you can set the maximum idle\nbuffer to zero (unlimited) just with:\n```c\ncontext->reader->maxbuf = 0;\n```\nThis should be done only in order to maximize performances when working with\nlarge payloads. The context should be set back to `REDIS_READER_MAX_BUF` again\nas soon as possible in order to prevent allocation of useless memory.\n\n## AUTHORS\n\nHiredis was written by Salvatore Sanfilippo (antirez at gmail) and\nPieter Noordhuis (pcnoordhuis at gmail) and is released under the BSD license.  \nHiredis is currently maintained by Matt Stancliff (matt at genges dot com) and\nJan-Erik Rediger (janerik at fnordig dot com)\n"
  },
  {
    "path": "thirdparty/hiredis/alloc.c",
    "content": "/*\n * Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>\n *\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 are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * 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 *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND 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 COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"fmacros.h\"\n#include \"alloc.h\"\n#include <string.h>\n#include <stdlib.h>\n\nhiredisAllocFuncs hiredisAllocFns = {\n    .mallocFn = malloc,\n    .callocFn = calloc,\n    .reallocFn = realloc,\n    .strdupFn = strdup,\n    .freeFn = free,\n};\n\n/* Override hiredis' allocators with ones supplied by the user */\nhiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *override) {\n    hiredisAllocFuncs orig = hiredisAllocFns;\n\n    hiredisAllocFns = *override;\n\n    return orig;\n}\n\n/* Reset allocators to use libc defaults */\nvoid hiredisResetAllocators(void) {\n    hiredisAllocFns = (hiredisAllocFuncs) {\n        .mallocFn = malloc,\n        .callocFn = calloc,\n        .reallocFn = realloc,\n        .strdupFn = strdup,\n        .freeFn = free,\n    };\n}\n\n#ifdef _WIN32\n\nvoid *hi_malloc(size_t size) {\n    return hiredisAllocFns.mallocFn(size);\n}\n\nvoid *hi_calloc(size_t nmemb, size_t size) {\n    return hiredisAllocFns.callocFn(nmemb, size);\n}\n\nvoid *hi_realloc(void *ptr, size_t size) {\n    return hiredisAllocFns.reallocFn(ptr, size);\n}\n\nchar *hi_strdup(const char *str) {\n    return hiredisAllocFns.strdupFn(str);\n}\n\nvoid hi_free(void *ptr) {\n    hiredisAllocFns.freeFn(ptr);\n}\n\n#endif\n"
  },
  {
    "path": "thirdparty/hiredis/alloc.h",
    "content": "/*\n * Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>\n *\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 are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * 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 *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND 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 COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef HIREDIS_ALLOC_H\n#define HIREDIS_ALLOC_H\n\n#include <stddef.h> /* for size_t */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* Structure pointing to our actually configured allocators */\ntypedef struct hiredisAllocFuncs {\n    void *(*mallocFn)(size_t);\n    void *(*callocFn)(size_t,size_t);\n    void *(*reallocFn)(void*,size_t);\n    char *(*strdupFn)(const char*);\n    void (*freeFn)(void*);\n} hiredisAllocFuncs;\n\nhiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *ha);\nvoid hiredisResetAllocators(void);\n\n#ifndef _WIN32\n\n/* Hiredis' configured allocator function pointer struct */\nextern hiredisAllocFuncs hiredisAllocFns;\n\nstatic inline void *hi_malloc(size_t size) {\n    return hiredisAllocFns.mallocFn(size);\n}\n\nstatic inline void *hi_calloc(size_t nmemb, size_t size) {\n    return hiredisAllocFns.callocFn(nmemb, size);\n}\n\nstatic inline void *hi_realloc(void *ptr, size_t size) {\n    return hiredisAllocFns.reallocFn(ptr, size);\n}\n\nstatic inline char *hi_strdup(const char *str) {\n    return hiredisAllocFns.strdupFn(str);\n}\n\nstatic inline void hi_free(void *ptr) {\n    hiredisAllocFns.freeFn(ptr);\n}\n\n#else\n\nvoid *hi_malloc(size_t size);\nvoid *hi_calloc(size_t nmemb, size_t size);\nvoid *hi_realloc(void *ptr, size_t size);\nchar *hi_strdup(const char *str);\nvoid hi_free(void *ptr);\n\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* HIREDIS_ALLOC_H */\n"
  },
  {
    "path": "thirdparty/hiredis/fmacros.h",
    "content": "#ifndef __HIREDIS_FMACRO_H\n#define __HIREDIS_FMACRO_H\n\n#define _XOPEN_SOURCE 600\n#define _POSIX_C_SOURCE 200112L\n\n#if defined(__APPLE__) && defined(__MACH__)\n/* Enable TCP_KEEPALIVE */\n#define _DARWIN_C_SOURCE\n#endif\n\n#endif\n"
  },
  {
    "path": "thirdparty/hiredis/hiredis.c",
    "content": "/*\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,\n *                     Jan-Erik Rediger <janerik at fnordig dot com>\n *\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 are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * 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 *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND 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 COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"fmacros.h\"\n#include <string.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <errno.h>\n#include <ctype.h>\n\n#include \"hiredis.h\"\n#include \"net.h\"\n#include \"sds.h\"\n\n#define SW_HOOK_POLL_FAKE\n#include \"swoole_socket_hook.h\"\n\nextern int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout);\nextern int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout);\n\nstatic redisContextFuncs redisContextDefaultFuncs = {\n    .free_privctx = NULL,\n    .read_ = redisNetRead,\n    .write_ = redisNetWrite\n};\n\nstatic redisReply *createReplyObject(int type);\nstatic void *createStringObject(const redisReadTask *task, char *str, size_t len);\nstatic void *createArrayObject(const redisReadTask *task, size_t elements);\nstatic void *createIntegerObject(const redisReadTask *task, long long value);\nstatic void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len);\nstatic void *createNilObject(const redisReadTask *task);\nstatic void *createBoolObject(const redisReadTask *task, int bval);\n\n/* Default set of functions to build the reply. Keep in mind that such a\n * function returning NULL is interpreted as OOM. */\nstatic redisReplyObjectFunctions defaultFunctions = {\n    createStringObject,\n    createArrayObject,\n    createIntegerObject,\n    createDoubleObject,\n    createNilObject,\n    createBoolObject,\n    freeReplyObject\n};\n\n/* Create a reply object */\nstatic redisReply *createReplyObject(int type) {\n    redisReply *r = hi_calloc(1,sizeof(*r));\n\n    if (r == NULL)\n        return NULL;\n\n    r->type = type;\n    return r;\n}\n\n/* Free a reply object */\nvoid freeReplyObject(void *reply) {\n    redisReply *r = reply;\n    size_t j;\n\n    if (r == NULL)\n        return;\n\n    switch(r->type) {\n    case REDIS_REPLY_INTEGER:\n    case REDIS_REPLY_NIL:\n    case REDIS_REPLY_BOOL:\n        break; /* Nothing to free */\n    case REDIS_REPLY_ARRAY:\n    case REDIS_REPLY_MAP:\n    case REDIS_REPLY_SET:\n    case REDIS_REPLY_PUSH:\n        if (r->element != NULL) {\n            for (j = 0; j < r->elements; j++)\n                freeReplyObject(r->element[j]);\n            hi_free(r->element);\n        }\n        break;\n    case REDIS_REPLY_ERROR:\n    case REDIS_REPLY_STATUS:\n    case REDIS_REPLY_STRING:\n    case REDIS_REPLY_DOUBLE:\n    case REDIS_REPLY_VERB:\n    case REDIS_REPLY_BIGNUM:\n        hi_free(r->str);\n        break;\n    }\n    hi_free(r);\n}\n\nstatic void *createStringObject(const redisReadTask *task, char *str, size_t len) {\n    redisReply *r, *parent;\n    char *buf;\n\n    r = createReplyObject(task->type);\n    if (r == NULL)\n        return NULL;\n\n    assert(task->type == REDIS_REPLY_ERROR  ||\n           task->type == REDIS_REPLY_STATUS ||\n           task->type == REDIS_REPLY_STRING ||\n           task->type == REDIS_REPLY_VERB   ||\n           task->type == REDIS_REPLY_BIGNUM);\n\n    /* Copy string value */\n    if (task->type == REDIS_REPLY_VERB) {\n        buf = hi_malloc(len-4+1); /* Skip 4 bytes of verbatim type header. */\n        if (buf == NULL) goto oom;\n\n        memcpy(r->vtype,str,3);\n        r->vtype[3] = '\\0';\n        memcpy(buf,str+4,len-4);\n        buf[len-4] = '\\0';\n        r->len = len - 4;\n    } else {\n        buf = hi_malloc(len+1);\n        if (buf == NULL) goto oom;\n\n        memcpy(buf,str,len);\n        buf[len] = '\\0';\n        r->len = len;\n    }\n    r->str = buf;\n\n    if (task->parent) {\n        parent = task->parent->obj;\n        assert(parent->type == REDIS_REPLY_ARRAY ||\n               parent->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET ||\n               parent->type == REDIS_REPLY_PUSH);\n        parent->element[task->idx] = r;\n    }\n    return r;\n\noom:\n    freeReplyObject(r);\n    return NULL;\n}\n\nstatic void *createArrayObject(const redisReadTask *task, size_t elements) {\n    redisReply *r, *parent;\n\n    r = createReplyObject(task->type);\n    if (r == NULL)\n        return NULL;\n\n    if (elements > 0) {\n        if (SIZE_MAX / sizeof(redisReply*) < elements) return NULL;  /* Don't overflow */\n        r->element = hi_calloc(elements,sizeof(redisReply*));\n        if (r->element == NULL) {\n            freeReplyObject(r);\n            return NULL;\n        }\n    }\n\n    r->elements = elements;\n\n    if (task->parent) {\n        parent = task->parent->obj;\n        assert(parent->type == REDIS_REPLY_ARRAY ||\n               parent->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET ||\n               parent->type == REDIS_REPLY_PUSH);\n        parent->element[task->idx] = r;\n    }\n    return r;\n}\n\nstatic void *createIntegerObject(const redisReadTask *task, long long value) {\n    redisReply *r, *parent;\n\n    r = createReplyObject(REDIS_REPLY_INTEGER);\n    if (r == NULL)\n        return NULL;\n\n    r->integer = value;\n\n    if (task->parent) {\n        parent = task->parent->obj;\n        assert(parent->type == REDIS_REPLY_ARRAY ||\n               parent->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET ||\n               parent->type == REDIS_REPLY_PUSH);\n        parent->element[task->idx] = r;\n    }\n    return r;\n}\n\nstatic void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len) {\n    redisReply *r, *parent;\n\n    r = createReplyObject(REDIS_REPLY_DOUBLE);\n    if (r == NULL)\n        return NULL;\n\n    r->dval = value;\n    r->str = hi_malloc(len+1);\n    if (r->str == NULL) {\n        freeReplyObject(r);\n        return NULL;\n    }\n\n    /* The double reply also has the original protocol string representing a\n     * double as a null terminated string. This way the caller does not need\n     * to format back for string conversion, especially since Redis does efforts\n     * to make the string more human readable avoiding the calssical double\n     * decimal string conversion artifacts. */\n    memcpy(r->str, str, len);\n    r->str[len] = '\\0';\n    r->len = len;\n\n    if (task->parent) {\n        parent = task->parent->obj;\n        assert(parent->type == REDIS_REPLY_ARRAY ||\n               parent->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET ||\n               parent->type == REDIS_REPLY_PUSH);\n        parent->element[task->idx] = r;\n    }\n    return r;\n}\n\nstatic void *createNilObject(const redisReadTask *task) {\n    redisReply *r, *parent;\n\n    r = createReplyObject(REDIS_REPLY_NIL);\n    if (r == NULL)\n        return NULL;\n\n    if (task->parent) {\n        parent = task->parent->obj;\n        assert(parent->type == REDIS_REPLY_ARRAY ||\n               parent->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET ||\n               parent->type == REDIS_REPLY_PUSH);\n        parent->element[task->idx] = r;\n    }\n    return r;\n}\n\nstatic void *createBoolObject(const redisReadTask *task, int bval) {\n    redisReply *r, *parent;\n\n    r = createReplyObject(REDIS_REPLY_BOOL);\n    if (r == NULL)\n        return NULL;\n\n    r->integer = bval != 0;\n\n    if (task->parent) {\n        parent = task->parent->obj;\n        assert(parent->type == REDIS_REPLY_ARRAY ||\n               parent->type == REDIS_REPLY_MAP ||\n               parent->type == REDIS_REPLY_SET ||\n               parent->type == REDIS_REPLY_PUSH);\n        parent->element[task->idx] = r;\n    }\n    return r;\n}\n\n/* Return the number of digits of 'v' when converted to string in radix 10.\n * Implementation borrowed from link in redis/src/util.c:string2ll(). */\nstatic uint32_t countDigits(uint64_t v) {\n  uint32_t result = 1;\n  for (;;) {\n    if (v < 10) return result;\n    if (v < 100) return result + 1;\n    if (v < 1000) return result + 2;\n    if (v < 10000) return result + 3;\n    v /= 10000U;\n    result += 4;\n  }\n  return result;\n}\n\n/* Helper that calculates the bulk length given a certain string length. */\nstatic size_t bulklen(size_t len) {\n    return 1+countDigits(len)+2+len+2;\n}\n\nint redisvFormatCommand(char **target, const char *format, va_list ap) {\n    const char *c = format;\n    char *cmd = NULL; /* final command */\n    int pos; /* position in final command */\n    sds curarg, newarg; /* current argument */\n    int touched = 0; /* was the current argument touched? */\n    char **curargv = NULL, **newargv = NULL;\n    int argc = 0;\n    int totlen = 0;\n    int error_type = 0; /* 0 = no error; -1 = memory error; -2 = format error */\n    int j;\n\n    /* Abort if there is not target to set */\n    if (target == NULL)\n        return -1;\n\n    /* Build the command string accordingly to protocol */\n    curarg = sdsempty();\n    if (curarg == NULL)\n        return -1;\n\n    while(*c != '\\0') {\n        if (*c != '%' || c[1] == '\\0') {\n            if (*c == ' ') {\n                if (touched) {\n                    newargv = hi_realloc(curargv,sizeof(char*)*(argc+1));\n                    if (newargv == NULL) goto memory_err;\n                    curargv = newargv;\n                    curargv[argc++] = curarg;\n                    totlen += bulklen(sdslen(curarg));\n\n                    /* curarg is put in argv so it can be overwritten. */\n                    curarg = sdsempty();\n                    if (curarg == NULL) goto memory_err;\n                    touched = 0;\n                }\n            } else {\n                newarg = sdscatlen(curarg,c,1);\n                if (newarg == NULL) goto memory_err;\n                curarg = newarg;\n                touched = 1;\n            }\n        } else {\n            char *arg;\n            size_t size;\n\n            /* Set newarg so it can be checked even if it is not touched. */\n            newarg = curarg;\n\n            switch(c[1]) {\n            case 's':\n                arg = va_arg(ap,char*);\n                size = strlen(arg);\n                if (size > 0)\n                    newarg = sdscatlen(curarg,arg,size);\n                break;\n            case 'b':\n                arg = va_arg(ap,char*);\n                size = va_arg(ap,size_t);\n                if (size > 0)\n                    newarg = sdscatlen(curarg,arg,size);\n                break;\n            case '%':\n                newarg = sdscat(curarg,\"%\");\n                break;\n            default:\n                /* Try to detect printf format */\n                {\n                    static const char intfmts[] = \"diouxX\";\n                    static const char flags[] = \"#0-+ \";\n                    char _format[16];\n                    const char *_p = c+1;\n                    size_t _l = 0;\n                    va_list _cpy;\n\n                    /* Flags */\n                    while (*_p != '\\0' && strchr(flags,*_p) != NULL) _p++;\n\n                    /* Field width */\n                    while (*_p != '\\0' && isdigit(*_p)) _p++;\n\n                    /* Precision */\n                    if (*_p == '.') {\n                        _p++;\n                        while (*_p != '\\0' && isdigit(*_p)) _p++;\n                    }\n\n                    /* Copy va_list before consuming with va_arg */\n                    va_copy(_cpy,ap);\n\n                    /* Integer conversion (without modifiers) */\n                    if (strchr(intfmts,*_p) != NULL) {\n                        va_arg(ap,int);\n                        goto fmt_valid;\n                    }\n\n                    /* Double conversion (without modifiers) */\n                    if (strchr(\"eEfFgGaA\",*_p) != NULL) {\n                        va_arg(ap,double);\n                        goto fmt_valid;\n                    }\n\n                    /* Size: char */\n                    if (_p[0] == 'h' && _p[1] == 'h') {\n                        _p += 2;\n                        if (*_p != '\\0' && strchr(intfmts,*_p) != NULL) {\n                            va_arg(ap,int); /* char gets promoted to int */\n                            goto fmt_valid;\n                        }\n                        goto fmt_invalid;\n                    }\n\n                    /* Size: short */\n                    if (_p[0] == 'h') {\n                        _p += 1;\n                        if (*_p != '\\0' && strchr(intfmts,*_p) != NULL) {\n                            va_arg(ap,int); /* short gets promoted to int */\n                            goto fmt_valid;\n                        }\n                        goto fmt_invalid;\n                    }\n\n                    /* Size: long long */\n                    if (_p[0] == 'l' && _p[1] == 'l') {\n                        _p += 2;\n                        if (*_p != '\\0' && strchr(intfmts,*_p) != NULL) {\n                            va_arg(ap,long long);\n                            goto fmt_valid;\n                        }\n                        goto fmt_invalid;\n                    }\n\n                    /* Size: long */\n                    if (_p[0] == 'l') {\n                        _p += 1;\n                        if (*_p != '\\0' && strchr(intfmts,*_p) != NULL) {\n                            va_arg(ap,long);\n                            goto fmt_valid;\n                        }\n                        goto fmt_invalid;\n                    }\n\n                fmt_invalid:\n                    va_end(_cpy);\n                    goto format_err;\n\n                fmt_valid:\n                    _l = (_p+1)-c;\n                    if (_l < sizeof(_format)-2) {\n                        memcpy(_format,c,_l);\n                        _format[_l] = '\\0';\n                        newarg = sdscatvprintf(curarg,_format,_cpy);\n\n                        /* Update current position (note: outer blocks\n                         * increment c twice so compensate here) */\n                        c = _p-1;\n                    }\n\n                    va_end(_cpy);\n                    break;\n                }\n            }\n\n            if (newarg == NULL) goto memory_err;\n            curarg = newarg;\n\n            touched = 1;\n            c++;\n        }\n        c++;\n    }\n\n    /* Add the last argument if needed */\n    if (touched) {\n        newargv = hi_realloc(curargv,sizeof(char*)*(argc+1));\n        if (newargv == NULL) goto memory_err;\n        curargv = newargv;\n        curargv[argc++] = curarg;\n        totlen += bulklen(sdslen(curarg));\n    } else {\n        sdsfree(curarg);\n    }\n\n    /* Clear curarg because it was put in curargv or was free'd. */\n    curarg = NULL;\n\n    /* Add bytes needed to hold multi bulk count */\n    totlen += 1+countDigits(argc)+2;\n\n    /* Build the command at protocol level */\n    cmd = hi_malloc(totlen+1);\n    if (cmd == NULL) goto memory_err;\n\n    pos = sprintf(cmd,\"*%d\\r\\n\",argc);\n    for (j = 0; j < argc; j++) {\n        pos += sprintf(cmd+pos,\"$%zu\\r\\n\",sdslen(curargv[j]));\n        memcpy(cmd+pos,curargv[j],sdslen(curargv[j]));\n        pos += sdslen(curargv[j]);\n        sdsfree(curargv[j]);\n        cmd[pos++] = '\\r';\n        cmd[pos++] = '\\n';\n    }\n    assert(pos == totlen);\n    cmd[pos] = '\\0';\n\n    hi_free(curargv);\n    *target = cmd;\n    return totlen;\n\nformat_err:\n    error_type = -2;\n    goto cleanup;\n\nmemory_err:\n    error_type = -1;\n    goto cleanup;\n\ncleanup:\n    if (curargv) {\n        while(argc--)\n            sdsfree(curargv[argc]);\n        hi_free(curargv);\n    }\n\n    sdsfree(curarg);\n    hi_free(cmd);\n\n    return error_type;\n}\n\n/* Format a command according to the Redis protocol. This function\n * takes a format similar to printf:\n *\n * %s represents a C null terminated string you want to interpolate\n * %b represents a binary safe string\n *\n * When using %b you need to provide both the pointer to the string\n * and the length in bytes as a size_t. Examples:\n *\n * len = redisFormatCommand(target, \"GET %s\", mykey);\n * len = redisFormatCommand(target, \"SET %s %b\", mykey, myval, myvallen);\n */\nint redisFormatCommand(char **target, const char *format, ...) {\n    va_list ap;\n    int len;\n    va_start(ap,format);\n    len = redisvFormatCommand(target,format,ap);\n    va_end(ap);\n\n    /* The API says \"-1\" means bad result, but we now also return \"-2\" in some\n     * cases.  Force the return value to always be -1. */\n    if (len < 0)\n        len = -1;\n\n    return len;\n}\n\n/* Format a command according to the Redis protocol using an sds string and\n * sdscatfmt for the processing of arguments. This function takes the\n * number of arguments, an array with arguments and an array with their\n * lengths. If the latter is set to NULL, strlen will be used to compute the\n * argument lengths.\n */\nint redisFormatSdsCommandArgv(sds *target, int argc, const char **argv,\n                              const size_t *argvlen)\n{\n    sds cmd, aux;\n    unsigned long long totlen;\n    int j;\n    size_t len;\n\n    /* Abort on a NULL target */\n    if (target == NULL)\n        return -1;\n\n    /* Calculate our total size */\n    totlen = 1+countDigits(argc)+2;\n    for (j = 0; j < argc; j++) {\n        len = argvlen ? argvlen[j] : strlen(argv[j]);\n        totlen += bulklen(len);\n    }\n\n    /* Use an SDS string for command construction */\n    cmd = sdsempty();\n    if (cmd == NULL)\n        return -1;\n\n    /* We already know how much storage we need */\n    aux = sdsMakeRoomFor(cmd, totlen);\n    if (aux == NULL) {\n        sdsfree(cmd);\n        return -1;\n    }\n\n    cmd = aux;\n\n    /* Construct command */\n    cmd = sdscatfmt(cmd, \"*%i\\r\\n\", argc);\n    for (j=0; j < argc; j++) {\n        len = argvlen ? argvlen[j] : strlen(argv[j]);\n        cmd = sdscatfmt(cmd, \"$%u\\r\\n\", len);\n        cmd = sdscatlen(cmd, argv[j], len);\n        cmd = sdscatlen(cmd, \"\\r\\n\", sizeof(\"\\r\\n\")-1);\n    }\n\n    assert(sdslen(cmd)==totlen);\n\n    *target = cmd;\n    return totlen;\n}\n\nvoid redisFreeSdsCommand(sds cmd) {\n    sdsfree(cmd);\n}\n\n/* Format a command according to the Redis protocol. This function takes the\n * number of arguments, an array with arguments and an array with their\n * lengths. If the latter is set to NULL, strlen will be used to compute the\n * argument lengths.\n */\nint redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) {\n    char *cmd = NULL; /* final command */\n    int pos; /* position in final command */\n    size_t len;\n    int totlen, j;\n\n    /* Abort on a NULL target */\n    if (target == NULL)\n        return -1;\n\n    /* Calculate number of bytes needed for the command */\n    totlen = 1+countDigits(argc)+2;\n    for (j = 0; j < argc; j++) {\n        len = argvlen ? argvlen[j] : strlen(argv[j]);\n        totlen += bulklen(len);\n    }\n\n    /* Build the command at protocol level */\n    cmd = hi_malloc(totlen+1);\n    if (cmd == NULL)\n        return -1;\n\n    pos = sprintf(cmd,\"*%d\\r\\n\",argc);\n    for (j = 0; j < argc; j++) {\n        len = argvlen ? argvlen[j] : strlen(argv[j]);\n        pos += sprintf(cmd+pos,\"$%zu\\r\\n\",len);\n        memcpy(cmd+pos,argv[j],len);\n        pos += len;\n        cmd[pos++] = '\\r';\n        cmd[pos++] = '\\n';\n    }\n    assert(pos == totlen);\n    cmd[pos] = '\\0';\n\n    *target = cmd;\n    return totlen;\n}\n\nvoid redisFreeCommand(char *cmd) {\n    hi_free(cmd);\n}\n\nvoid __redisSetError(redisContext *c, int type, const char *str) {\n    size_t len;\n\n    c->err = type;\n    if (str != NULL) {\n        len = strlen(str);\n        len = len < (sizeof(c->errstr)-1) ? len : (sizeof(c->errstr)-1);\n        memcpy(c->errstr,str,len);\n        c->errstr[len] = '\\0';\n    } else {\n        /* Only REDIS_ERR_IO may lack a description! */\n        assert(type == REDIS_ERR_IO);\n        strerror_r(errno, c->errstr, sizeof(c->errstr));\n    }\n}\n\nredisReader *redisReaderCreate(void) {\n    return redisReaderCreateWithFunctions(&defaultFunctions);\n}\n\nstatic void redisPushAutoFree(void *privdata, void *reply) {\n    (void)privdata;\n    freeReplyObject(reply);\n}\n\nstatic redisContext *redisContextInit(void) {\n    redisContext *c;\n\n    c = hi_calloc(1, sizeof(*c));\n    if (c == NULL)\n        return NULL;\n\n    c->funcs = &redisContextDefaultFuncs;\n\n    c->obuf = sdsempty();\n    c->reader = redisReaderCreate();\n    c->fd = REDIS_INVALID_FD;\n\n    if (c->obuf == NULL || c->reader == NULL) {\n        redisFree(c);\n        return NULL;\n    }\n\n    return c;\n}\n\nvoid redisFree(redisContext *c) {\n    if (c == NULL)\n        return;\n    redisNetClose(c);\n\n    sdsfree(c->obuf);\n    redisReaderFree(c->reader);\n    hi_free(c->tcp.host);\n    hi_free(c->tcp.source_addr);\n    hi_free(c->unix_sock.path);\n    hi_free(c->connect_timeout);\n    hi_free(c->command_timeout);\n    hi_free(c->saddr);\n\n    if (c->privdata && c->free_privdata)\n        c->free_privdata(c->privdata);\n\n    if (c->funcs->free_privctx)\n        c->funcs->free_privctx(c->privctx);\n\n    memset(c, 0xff, sizeof(*c));\n    hi_free(c);\n}\n\nredisFD redisFreeKeepFd(redisContext *c) {\n    redisFD fd = c->fd;\n    c->fd = REDIS_INVALID_FD;\n    redisFree(c);\n    return fd;\n}\n\nint redisReconnect(redisContext *c) {\n    c->err = 0;\n    memset(c->errstr, '\\0', strlen(c->errstr));\n\n    if (c->privctx && c->funcs->free_privctx) {\n        c->funcs->free_privctx(c->privctx);\n        c->privctx = NULL;\n    }\n\n    redisNetClose(c);\n\n    sdsfree(c->obuf);\n    redisReaderFree(c->reader);\n\n    c->obuf = sdsempty();\n    c->reader = redisReaderCreate();\n\n    if (c->obuf == NULL || c->reader == NULL) {\n        __redisSetError(c, REDIS_ERR_OOM, \"Out of memory\");\n        return REDIS_ERR;\n    }\n\n    int ret = REDIS_ERR;\n    if (c->connection_type == REDIS_CONN_TCP) {\n        ret = redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port,\n               c->connect_timeout, c->tcp.source_addr);\n    } else if (c->connection_type == REDIS_CONN_UNIX) {\n        ret = redisContextConnectUnix(c, c->unix_sock.path, c->connect_timeout);\n    } else {\n        /* Something bad happened here and shouldn't have. There isn't\n           enough information in the context to reconnect. */\n        __redisSetError(c,REDIS_ERR_OTHER,\"Not enough information to reconnect\");\n        ret = REDIS_ERR;\n    }\n\n    if (c->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {\n        redisContextSetTimeout(c, *c->command_timeout);\n    }\n\n    return ret;\n}\n\nredisContext *redisConnectWithOptions(const redisOptions *options) {\n    redisContext *c = redisContextInit();\n    if (c == NULL) {\n        return NULL;\n    }\n    if (!(options->options & REDIS_OPT_NONBLOCK)) {\n        c->flags |= REDIS_BLOCK;\n    }\n    if (options->options & REDIS_OPT_REUSEADDR) {\n        c->flags |= REDIS_REUSEADDR;\n    }\n    if (options->options & REDIS_OPT_NOAUTOFREE) {\n        c->flags |= REDIS_NO_AUTO_FREE;\n    }\n\n    /* Set any user supplied RESP3 PUSH handler or use freeReplyObject\n     * as a default unless specifically flagged that we don't want one. */\n    if (options->push_cb != NULL)\n        redisSetPushCallback(c, options->push_cb);\n    else if (!(options->options & REDIS_OPT_NO_PUSH_AUTOFREE))\n        redisSetPushCallback(c, redisPushAutoFree);\n\n    c->privdata = options->privdata;\n    c->free_privdata = options->free_privdata;\n\n    if (redisContextUpdateConnectTimeout(c, options->connect_timeout) != REDIS_OK ||\n        redisContextUpdateCommandTimeout(c, options->command_timeout) != REDIS_OK) {\n        __redisSetError(c, REDIS_ERR_OOM, \"Out of memory\");\n        return c;\n    }\n\n    if (options->type == REDIS_CONN_TCP) {\n        redisContextConnectBindTcp(c, options->endpoint.tcp.ip,\n                                   options->endpoint.tcp.port, options->connect_timeout,\n                                   options->endpoint.tcp.source_addr);\n    } else if (options->type == REDIS_CONN_UNIX) {\n        redisContextConnectUnix(c, options->endpoint.unix_socket,\n                                options->connect_timeout);\n    } else if (options->type == REDIS_CONN_USERFD) {\n        c->fd = options->endpoint.fd;\n        c->flags |= REDIS_CONNECTED;\n    } else {\n        // Unknown type - FIXME - FREE\n        return NULL;\n    }\n\n    if (options->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {\n        redisContextSetTimeout(c, *options->command_timeout);\n    }\n\n    return c;\n}\n\n/* Connect to a Redis instance. On error the field error in the returned\n * context will be set to the return value of the error function.\n * When no set of reply functions is given, the default set will be used. */\nredisContext *redisConnect(const char *ip, int port) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    options.connect_timeout = &tv;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectNonBlock(const char *ip, int port) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    options.options |= REDIS_OPT_NONBLOCK;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectBindNonBlock(const char *ip, int port,\n                                       const char *source_addr) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    options.endpoint.tcp.source_addr = source_addr;\n    options.options |= REDIS_OPT_NONBLOCK;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,\n                                                const char *source_addr) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_TCP(&options, ip, port);\n    options.endpoint.tcp.source_addr = source_addr;\n    options.options |= REDIS_OPT_NONBLOCK|REDIS_OPT_REUSEADDR;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectUnix(const char *path) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_UNIX(&options, path);\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_UNIX(&options, path);\n    options.connect_timeout = &tv;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectUnixNonBlock(const char *path) {\n    redisOptions options = {0};\n    REDIS_OPTIONS_SET_UNIX(&options, path);\n    options.options |= REDIS_OPT_NONBLOCK;\n    return redisConnectWithOptions(&options);\n}\n\nredisContext *redisConnectFd(redisFD fd) {\n    redisOptions options = {0};\n    options.type = REDIS_CONN_USERFD;\n    options.endpoint.fd = fd;\n    return redisConnectWithOptions(&options);\n}\n\n/* Set read/write timeout on a blocking socket. */\nint redisSetTimeout(redisContext *c, const struct timeval tv) {\n    if (c->flags & REDIS_BLOCK)\n        return redisContextSetTimeout(c,tv);\n    return REDIS_ERR;\n}\n\n/* Enable connection KeepAlive. */\nint redisEnableKeepAlive(redisContext *c) {\n    if (redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL) != REDIS_OK)\n        return REDIS_ERR;\n    return REDIS_OK;\n}\n\n/* Set a user provided RESP3 PUSH handler and return any old one set. */\nredisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn) {\n    redisPushFn *old = c->push_cb;\n    c->push_cb = fn;\n    return old;\n}\n\n/* Use this function to handle a read event on the descriptor. It will try\n * and read some bytes from the socket and feed them to the reply parser.\n *\n * After this function is called, you may use redisGetReplyFromReader to\n * see if there is a reply available. */\nint redisBufferRead(redisContext *c) {\n    char buf[1024*16];\n    int nread;\n\n    /* Return early when the context has seen an error. */\n    if (c->err)\n        return REDIS_ERR;\n\n    nread = c->funcs->read_(c, buf, sizeof(buf));\n    if (nread < 0) {\n        return REDIS_ERR;\n    }\n    if (nread > 0 && redisReaderFeed(c->reader, buf, nread) != REDIS_OK) {\n        __redisSetError(c, c->reader->err, c->reader->errstr);\n        return REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\n/* Write the output buffer to the socket.\n *\n * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was\n * successfully written to the socket. When the buffer is empty after the\n * write operation, \"done\" is set to 1 (if given).\n *\n * Returns REDIS_ERR if an error occurred trying to write and sets\n * c->errstr to hold the appropriate error string.\n */\nint redisBufferWrite(redisContext *c, int *done) {\n\n    /* Return early when the context has seen an error. */\n    if (c->err)\n        return REDIS_ERR;\n\n    if (sdslen(c->obuf) > 0) {\n        ssize_t nwritten = c->funcs->write_(c);\n        if (nwritten < 0) {\n            return REDIS_ERR;\n        } else if (nwritten > 0) {\n            if (nwritten == (ssize_t)sdslen(c->obuf)) {\n                sdsfree(c->obuf);\n                c->obuf = sdsempty();\n                if (c->obuf == NULL)\n                    goto oom;\n            } else {\n                if (sdsrange(c->obuf,nwritten,-1) < 0) goto oom;\n            }\n        }\n    }\n    if (done != NULL) *done = (sdslen(c->obuf) == 0);\n    return REDIS_OK;\n\noom:\n    __redisSetError(c, REDIS_ERR_OOM, \"Out of memory\");\n    return REDIS_ERR;\n}\n\n/* Internal helper that returns 1 if the reply was a RESP3 PUSH\n * message and we handled it with a user-provided callback. */\nstatic int redisHandledPushReply(redisContext *c, void *reply) {\n    if (reply && c->push_cb && redisIsPushReply(reply)) {\n        c->push_cb(c->privdata, reply);\n        return 1;\n    }\n\n    return 0;\n}\n\n/* Get a reply from our reader or set an error in the context. */\nint redisGetReplyFromReader(redisContext *c, void **reply) {\n    if (redisReaderGetReply(c->reader, reply) == REDIS_ERR) {\n        __redisSetError(c,c->reader->err,c->reader->errstr);\n        return REDIS_ERR;\n    }\n\n    return REDIS_OK;\n}\n\n/* Internal helper to get the next reply from our reader while handling\n * any PUSH messages we encounter along the way.  This is separate from\n * redisGetReplyFromReader so as to not change its behavior. */\nstatic int redisNextInBandReplyFromReader(redisContext *c, void **reply) {\n    do {\n        if (redisGetReplyFromReader(c, reply) == REDIS_ERR)\n            return REDIS_ERR;\n    } while (redisHandledPushReply(c, *reply));\n\n    return REDIS_OK;\n}\n\nint redisGetReply(redisContext *c, void **reply) {\n    int wdone = 0;\n    void *aux = NULL;\n\n    /* Try to read pending replies */\n    if (redisNextInBandReplyFromReader(c,&aux) == REDIS_ERR)\n        return REDIS_ERR;\n\n    /* For the blocking context, flush output buffer and read reply */\n    if (aux == NULL && (c->flags & REDIS_BLOCK)) {\n        /* Write until done */\n        do {\n            if (redisBufferWrite(c,&wdone) == REDIS_ERR)\n                return REDIS_ERR;\n        } while (!wdone);\n\n        /* Read until there is a reply */\n        do {\n            if (redisBufferRead(c) == REDIS_ERR)\n                return REDIS_ERR;\n\n            if (redisNextInBandReplyFromReader(c,&aux) == REDIS_ERR)\n                return REDIS_ERR;\n        } while (aux == NULL);\n    }\n\n    /* Set reply or free it if we were passed NULL */\n    if (reply != NULL) {\n        *reply = aux;\n    } else {\n        freeReplyObject(aux);\n    }\n\n    return REDIS_OK;\n}\n\n\n/* Helper function for the redisAppendCommand* family of functions.\n *\n * Write a formatted command to the output buffer. When this family\n * is used, you need to call redisGetReply yourself to retrieve\n * the reply (or replies in pub/sub).\n */\nint __redisAppendCommand(redisContext *c, const char *cmd, size_t len) {\n    sds newbuf;\n\n    newbuf = sdscatlen(c->obuf,cmd,len);\n    if (newbuf == NULL) {\n        __redisSetError(c,REDIS_ERR_OOM,\"Out of memory\");\n        return REDIS_ERR;\n    }\n\n    c->obuf = newbuf;\n    return REDIS_OK;\n}\n\nint redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len) {\n\n    if (__redisAppendCommand(c, cmd, len) != REDIS_OK) {\n        return REDIS_ERR;\n    }\n\n    return REDIS_OK;\n}\n\nint redisvAppendCommand(redisContext *c, const char *format, va_list ap) {\n    char *cmd;\n    int len;\n\n    len = redisvFormatCommand(&cmd,format,ap);\n    if (len == -1) {\n        __redisSetError(c,REDIS_ERR_OOM,\"Out of memory\");\n        return REDIS_ERR;\n    } else if (len == -2) {\n        __redisSetError(c,REDIS_ERR_OTHER,\"Invalid format string\");\n        return REDIS_ERR;\n    }\n\n    if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {\n        hi_free(cmd);\n        return REDIS_ERR;\n    }\n\n    hi_free(cmd);\n    return REDIS_OK;\n}\n\nint redisAppendCommand(redisContext *c, const char *format, ...) {\n    va_list ap;\n    int ret;\n\n    va_start(ap,format);\n    ret = redisvAppendCommand(c,format,ap);\n    va_end(ap);\n    return ret;\n}\n\nint redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {\n    sds cmd;\n    int len;\n\n    len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);\n    if (len == -1) {\n        __redisSetError(c,REDIS_ERR_OOM,\"Out of memory\");\n        return REDIS_ERR;\n    }\n\n    if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {\n        sdsfree(cmd);\n        return REDIS_ERR;\n    }\n\n    sdsfree(cmd);\n    return REDIS_OK;\n}\n\n/* Helper function for the redisCommand* family of functions.\n *\n * Write a formatted command to the output buffer. If the given context is\n * blocking, immediately read the reply into the \"reply\" pointer. When the\n * context is non-blocking, the \"reply\" pointer will not be used and the\n * command is simply appended to the write buffer.\n *\n * Returns the reply when a reply was successfully retrieved. Returns NULL\n * otherwise. When NULL is returned in a blocking context, the error field\n * in the context will be set.\n */\nstatic void *__redisBlockForReply(redisContext *c) {\n    void *reply;\n\n    if (c->flags & REDIS_BLOCK) {\n        if (redisGetReply(c,&reply) != REDIS_OK)\n            return NULL;\n        return reply;\n    }\n    return NULL;\n}\n\nvoid *redisvCommand(redisContext *c, const char *format, va_list ap) {\n    if (redisvAppendCommand(c,format,ap) != REDIS_OK)\n        return NULL;\n    return __redisBlockForReply(c);\n}\n\nvoid *redisCommand(redisContext *c, const char *format, ...) {\n    va_list ap;\n    va_start(ap,format);\n    void *reply = redisvCommand(c,format,ap);\n    va_end(ap);\n    return reply;\n}\n\nvoid *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {\n    if (redisAppendCommandArgv(c,argc,argv,argvlen) != REDIS_OK)\n        return NULL;\n    return __redisBlockForReply(c);\n}\n"
  },
  {
    "path": "thirdparty/hiredis/hiredis.h",
    "content": "/*\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,\n *                     Jan-Erik Rediger <janerik at fnordig dot com>\n *\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 are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * 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 *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND 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 COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef __HIREDIS_H\n#define __HIREDIS_H\n#include \"read.h\"\n#include <stdarg.h> /* for va_list */\n#ifndef _MSC_VER\n#include <sys/time.h> /* for struct timeval */\n#else\nstruct timeval; /* forward declaration */\ntypedef long long ssize_t;\n#endif\n#include <stdint.h> /* uintXX_t, etc */\n#include \"sds.h\" /* for sds */\n#include \"alloc.h\" /* for allocation wrappers */\n\n#define HIREDIS_MAJOR 1\n#define HIREDIS_MINOR 0\n#define HIREDIS_PATCH 1\n#define HIREDIS_SONAME 1.0.1-dev\n\n/* Connection type can be blocking or non-blocking and is set in the\n * least significant bit of the flags field in redisContext. */\n#define REDIS_BLOCK 0x1\n\n/* Connection may be disconnected before being free'd. The second bit\n * in the flags field is set when the context is connected. */\n#define REDIS_CONNECTED 0x2\n\n/* The async API might try to disconnect cleanly and flush the output\n * buffer and read all subsequent replies before disconnecting.\n * This flag means no new commands can come in and the connection\n * should be terminated once all replies have been read. */\n#define REDIS_DISCONNECTING 0x4\n\n/* Flag specific to the async API which means that the context should be clean\n * up as soon as possible. */\n#define REDIS_FREEING 0x8\n\n/* Flag that is set when an async callback is executed. */\n#define REDIS_IN_CALLBACK 0x10\n\n/* Flag that is set when the async context has one or more subscriptions. */\n#define REDIS_SUBSCRIBED 0x20\n\n/* Flag that is set when monitor mode is active */\n#define REDIS_MONITORING 0x40\n\n/* Flag that is set when we should set SO_REUSEADDR before calling bind() */\n#define REDIS_REUSEADDR 0x80\n\n/**\n * Flag that indicates the user does not want the context to\n * be automatically freed upon error\n */\n#define REDIS_NO_AUTO_FREE 0x200\n\n#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */\n\n/* number of times we retry to connect in the case of EADDRNOTAVAIL and\n * SO_REUSEADDR is being used. */\n#define REDIS_CONNECT_RETRIES  10\n\n/* Forward declarations for structs defined elsewhere */\nstruct redisAsyncContext;\nstruct redisContext;\n\n/* RESP3 push helpers and callback prototypes */\n#define redisIsPushReply(r) (((redisReply*)(r))->type == REDIS_REPLY_PUSH)\ntypedef void (redisPushFn)(void *, void *);\ntypedef void (redisAsyncPushFn)(struct redisAsyncContext *, void *);\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* This is the reply object returned by redisCommand() */\ntypedef struct redisReply {\n    int type; /* REDIS_REPLY_* */\n    long long integer; /* The integer when type is REDIS_REPLY_INTEGER */\n    double dval; /* The double when type is REDIS_REPLY_DOUBLE */\n    size_t len; /* Length of string */\n    char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING\n                  REDIS_REPLY_VERB, REDIS_REPLY_DOUBLE (in additional to dval),\n                  and REDIS_REPLY_BIGNUM. */\n    char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null\n                      terminated 3 character content type, such as \"txt\". */\n    size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */\n    struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */\n} redisReply;\n\nredisReader *redisReaderCreate(void);\n\n/* Function to free the reply objects hiredis returns by default. */\nvoid freeReplyObject(void *reply);\n\n/* Functions to format a command according to the protocol. */\nint redisvFormatCommand(char **target, const char *format, va_list ap);\nint redisFormatCommand(char **target, const char *format, ...);\nint redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);\nint redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);\nvoid redisFreeCommand(char *cmd);\nvoid redisFreeSdsCommand(sds cmd);\n\nenum redisConnectionType {\n    REDIS_CONN_TCP,\n    REDIS_CONN_UNIX,\n    REDIS_CONN_USERFD\n};\n\nstruct redisSsl;\n\n#define REDIS_OPT_NONBLOCK 0x01\n#define REDIS_OPT_REUSEADDR 0x02\n\n/**\n * Don't automatically free the async object on a connection failure,\n * or other implicit conditions. Only free on an explicit call to disconnect() or free()\n */\n#define REDIS_OPT_NOAUTOFREE 0x04\n\n/* Don't automatically intercept and free RESP3 PUSH replies. */\n#define REDIS_OPT_NO_PUSH_AUTOFREE 0x08\n\n/* In Unix systems a file descriptor is a regular signed int, with -1\n * representing an invalid descriptor. In Windows it is a SOCKET\n * (32- or 64-bit unsigned integer depending on the architecture), where\n * all bits set (~0) is INVALID_SOCKET.  */\n#ifndef _WIN32\ntypedef int redisFD;\n#define REDIS_INVALID_FD -1\n#else\n#ifdef _WIN64\ntypedef unsigned long long redisFD; /* SOCKET = 64-bit UINT_PTR */\n#else\ntypedef unsigned long redisFD;      /* SOCKET = 32-bit UINT_PTR */\n#endif\n#define REDIS_INVALID_FD ((redisFD)(~0)) /* INVALID_SOCKET */\n#endif\n\ntypedef struct {\n    /*\n     * the type of connection to use. This also indicates which\n     * `endpoint` member field to use\n     */\n    int type;\n    /* bit field of REDIS_OPT_xxx */\n    int options;\n    /* timeout value for connect operation. If NULL, no timeout is used */\n    const struct timeval *connect_timeout;\n    /* timeout value for commands. If NULL, no timeout is used.  This can be\n     * updated at runtime with redisSetTimeout/redisAsyncSetTimeout. */\n    const struct timeval *command_timeout;\n    union {\n        /** use this field for tcp/ip connections */\n        struct {\n            const char *source_addr;\n            const char *ip;\n            int port;\n        } tcp;\n        /** use this field for unix domain sockets */\n        const char *unix_socket;\n        /**\n         * use this field to have hiredis operate an already-open\n         * file descriptor */\n        redisFD fd;\n    } endpoint;\n\n    /* Optional user defined data/destructor */\n    void *privdata;\n    void (*free_privdata)(void *);\n\n    /* A user defined PUSH message callback */\n    redisPushFn *push_cb;\n    redisAsyncPushFn *async_push_cb;\n} redisOptions;\n\n/**\n * Helper macros to initialize options to their specified fields.\n */\n#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) \\\n    (opts)->type = REDIS_CONN_TCP; \\\n    (opts)->endpoint.tcp.ip = ip_; \\\n    (opts)->endpoint.tcp.port = port_;\n\n#define REDIS_OPTIONS_SET_UNIX(opts, path) \\\n    (opts)->type = REDIS_CONN_UNIX;        \\\n    (opts)->endpoint.unix_socket = path;\n\n#define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) \\\n    (opts)->privdata = data;                         \\\n    (opts)->free_privdata = dtor;                    \\\n\ntypedef struct redisContextFuncs {\n    void (*free_privctx)(void *);\n    ssize_t (*read_)(struct redisContext *, char *, size_t);\n    ssize_t (*write_)(struct redisContext *);\n} redisContextFuncs;\n\n/* Context for a connection to Redis */\ntypedef struct redisContext {\n    const redisContextFuncs *funcs;   /* Function table */\n\n    int err; /* Error flags, 0 when there is no error */\n    char errstr[128]; /* String representation of error when applicable */\n    redisFD fd;\n    int flags;\n    char *obuf; /* Write buffer */\n    redisReader *reader; /* Protocol reader */\n\n    enum redisConnectionType connection_type;\n    struct timeval *connect_timeout;\n    struct timeval *command_timeout;\n\n    struct {\n        char *host;\n        char *source_addr;\n        int port;\n    } tcp;\n\n    struct {\n        char *path;\n    } unix_sock;\n\n    /* For non-blocking connect */\n    struct sockaddr *saddr;\n    size_t addrlen;\n\n    /* Optional data and corresponding destructor users can use to provide\n     * context to a given redisContext.  Not used by hiredis. */\n    void *privdata;\n    void (*free_privdata)(void *);\n\n    /* Internal context pointer presently used by hiredis to manage\n     * SSL connections. */\n    void *privctx;\n\n    /* An optional RESP3 PUSH handler */\n    redisPushFn *push_cb;\n} redisContext;\n\nredisContext *redisConnectWithOptions(const redisOptions *options);\nredisContext *redisConnect(const char *ip, int port);\nredisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);\nredisContext *redisConnectNonBlock(const char *ip, int port);\nredisContext *redisConnectBindNonBlock(const char *ip, int port,\n                                       const char *source_addr);\nredisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,\n                                                const char *source_addr);\nredisContext *redisConnectUnix(const char *path);\nredisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);\nredisContext *redisConnectUnixNonBlock(const char *path);\nredisContext *redisConnectFd(redisFD fd);\n\n/**\n * Reconnect the given context using the saved information.\n *\n * This re-uses the exact same connect options as in the initial connection.\n * host, ip (or path), timeout and bind address are reused,\n * flags are used unmodified from the existing context.\n *\n * Returns REDIS_OK on successful connect or REDIS_ERR otherwise.\n */\nint redisReconnect(redisContext *c);\n\nredisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn);\nint redisSetTimeout(redisContext *c, const struct timeval tv);\nint redisEnableKeepAlive(redisContext *c);\nvoid redisFree(redisContext *c);\nredisFD redisFreeKeepFd(redisContext *c);\nint redisBufferRead(redisContext *c);\nint redisBufferWrite(redisContext *c, int *done);\n\n/* In a blocking context, this function first checks if there are unconsumed\n * replies to return and returns one if so. Otherwise, it flushes the output\n * buffer to the socket and reads until it has a reply. In a non-blocking\n * context, it will return unconsumed replies until there are no more. */\nint redisGetReply(redisContext *c, void **reply);\nint redisGetReplyFromReader(redisContext *c, void **reply);\n\n/* Write a formatted command to the output buffer. Use these functions in blocking mode\n * to get a pipeline of commands. */\nint redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len);\n\n/* Write a command to the output buffer. Use these functions in blocking mode\n * to get a pipeline of commands. */\nint redisvAppendCommand(redisContext *c, const char *format, va_list ap);\nint redisAppendCommand(redisContext *c, const char *format, ...);\nint redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);\n\n/* Issue a command to Redis. In a blocking context, it is identical to calling\n * redisAppendCommand, followed by redisGetReply. The function will return\n * NULL if there was an error in performing the request, otherwise it will\n * return the reply. In a non-blocking context, it is identical to calling\n * only redisAppendCommand and will always return NULL. */\nvoid *redisvCommand(redisContext *c, const char *format, va_list ap);\nvoid *redisCommand(redisContext *c, const char *format, ...);\nvoid *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "thirdparty/hiredis/net.c",
    "content": "/* Extracted from anet.c to work properly with Hiredis error reporting.\n *\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,\n *                     Jan-Erik Rediger <janerik at fnordig dot com>\n *\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 are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * 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 *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND 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 COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"fmacros.h\"\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <sys/select.h>\n#include <sys/un.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n#include <arpa/inet.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <string.h>\n#include <netdb.h>\n#include <errno.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <poll.h>\n#include <limits.h>\n#include <stdlib.h>\n\n#include \"net.h\"\n#include \"sds.h\"\n\n#include \"swoole_socket_hook.h\"\n\n/* Defined in hiredis.c */\nvoid __redisSetError(redisContext *c, int type, const char *str);\n\nvoid redisNetClose(redisContext *c) {\n    if (c && c->fd != REDIS_INVALID_FD) {\n        close(c->fd);\n        c->fd = REDIS_INVALID_FD;\n    }\n}\n\nssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) {\n    ssize_t nread = recv(c->fd, buf, bufcap, 0);\n    if (nread == -1) {\n        if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {\n            /* Try again later */\n            return 0;\n        }\n#if 0\n        else if(errno == ETIMEDOUT && (c->flags & REDIS_BLOCK)) {\n            /* especially in windows */\n            __redisSetError(c, REDIS_ERR_TIMEOUT, \"recv timeout\");\n            return -1;\n        }\n#endif\n        else {\n            __redisSetError(c, REDIS_ERR_IO, NULL);\n            return -1;\n        }\n    } else if (nread == 0) {\n        __redisSetError(c, REDIS_ERR_EOF, \"Server closed the connection\");\n        return -1;\n    } else {\n        return nread;\n    }\n}\n\nssize_t redisNetWrite(redisContext *c) {\n    ssize_t nwritten = send(c->fd, c->obuf, sdslen(c->obuf), 0);\n    if (nwritten < 0) {\n        if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {\n            /* Try again later */\n        } else {\n            __redisSetError(c, REDIS_ERR_IO, NULL);\n            return -1;\n        }\n    }\n    return nwritten;\n}\n\nstatic void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {\n    int errorno = errno;  /* snprintf() may change errno */\n    char buf[128] = { 0 };\n    size_t len = 0;\n\n    if (prefix != NULL)\n        len = snprintf(buf,sizeof(buf),\"%s: \",prefix);\n    strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len);\n    __redisSetError(c,type,buf);\n}\n\nstatic int redisSetReuseAddr(redisContext *c) {\n    int on = 1;\n    if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n        redisNetClose(c);\n        return REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\nstatic int redisCreateSocket(redisContext *c, int type) {\n    redisFD s;\n    if ((s = socket(type, SOCK_STREAM, 0)) == REDIS_INVALID_FD) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n        return REDIS_ERR;\n    }\n    c->fd = s;\n    if (type == AF_INET) {\n        if (redisSetReuseAddr(c) == REDIS_ERR) {\n            return REDIS_ERR;\n        }\n    }\n    return REDIS_OK;\n}\n\nstatic int redisSetBlocking(redisContext *c, int blocking) {\n#if 0\n#ifndef _WIN32\n    int flags;\n\n    /* Set the socket nonblocking.\n     * Note that fcntl(2) for F_GETFL and F_SETFL can't be\n     * interrupted by a signal. */\n    if ((flags = fcntl(c->fd, F_GETFL)) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"fcntl(F_GETFL)\");\n        redisNetClose(c);\n        return REDIS_ERR;\n    }\n\n    if (blocking)\n        flags &= ~O_NONBLOCK;\n    else\n        flags |= O_NONBLOCK;\n\n    if (fcntl(c->fd, F_SETFL, flags) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"fcntl(F_SETFL)\");\n        redisNetClose(c);\n        return REDIS_ERR;\n    }\n#else\n    u_long mode = blocking ? 0 : 1;\n    if (ioctl(c->fd, FIONBIO, &mode) == -1) {\n        __redisSetErrorFromErrno(c, REDIS_ERR_IO, \"ioctl(FIONBIO)\");\n        redisNetClose(c);\n        return REDIS_ERR;\n    }\n#endif /* _WIN32 */\n#endif\n    return REDIS_OK;\n}\n\nint redisKeepAlive(redisContext *c, int interval) {\n    int val = 1;\n    redisFD fd = c->fd;\n\n    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){\n        __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));\n        return REDIS_ERR;\n    }\n\n    val = interval;\n\n#if defined(__APPLE__) && defined(__MACH__)\n    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {\n        __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));\n        return REDIS_ERR;\n    }\n#else\n#if defined(__GLIBC__) && !defined(__FreeBSD_kernel__)\n    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {\n        __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));\n        return REDIS_ERR;\n    }\n\n    val = interval/3;\n    if (val == 0) val = 1;\n    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {\n        __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));\n        return REDIS_ERR;\n    }\n\n    val = 3;\n    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {\n        __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));\n        return REDIS_ERR;\n    }\n#endif\n#endif\n\n    return REDIS_OK;\n}\n\nint redisSetTcpNoDelay(redisContext *c) {\n    int yes = 1;\n    if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"setsockopt(TCP_NODELAY)\");\n        redisNetClose(c);\n        return REDIS_ERR;\n    }\n    return REDIS_OK;\n}\n\n#define __MAX_MSEC (((LONG_MAX) - 999) / 1000)\n\nstatic int redisContextTimeoutMsec(redisContext *c, long *result)\n{\n    const struct timeval *timeout = c->connect_timeout;\n    long msec = -1;\n\n    /* Only use timeout when not NULL. */\n    if (timeout != NULL) {\n        if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {\n            *result = msec;\n            return REDIS_ERR;\n        }\n\n        msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000);\n\n        if (msec < 0 || msec > INT_MAX) {\n            msec = INT_MAX;\n        }\n    }\n\n    *result = msec;\n    return REDIS_OK;\n}\n\nstatic int redisContextWaitReady(redisContext *c, long msec) {\n    struct pollfd   wfd[1];\n\n    wfd[0].fd     = c->fd;\n    wfd[0].events = POLLOUT;\n\n    if (errno == EINPROGRESS) {\n        int res;\n\n        if ((res = poll(wfd, 1, msec)) == -1) {\n            __redisSetErrorFromErrno(c, REDIS_ERR_IO, \"poll(2)\");\n            redisNetClose(c);\n            return REDIS_ERR;\n        } else if (res == 0) {\n            errno = ETIMEDOUT;\n            __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n            redisNetClose(c);\n            return REDIS_ERR;\n        }\n\n        if (redisCheckConnectDone(c, &res) != REDIS_OK || res == 0) {\n            redisCheckSocketError(c);\n            return REDIS_ERR;\n        }\n\n        return REDIS_OK;\n    }\n\n    __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n    redisNetClose(c);\n    return REDIS_ERR;\n}\n\nint redisCheckConnectDone(redisContext *c, int *completed) {\n    int rc = connect(c->fd, (const struct sockaddr *)c->saddr, c->addrlen);\n    if (rc == 0) {\n        *completed = 1;\n        return REDIS_OK;\n    }\n    switch (errno) {\n    case EISCONN:\n        *completed = 1;\n        return REDIS_OK;\n    case EALREADY:\n    case EINPROGRESS:\n    case EWOULDBLOCK:\n        *completed = 0;\n        return REDIS_OK;\n    default:\n        return REDIS_ERR;\n    }\n}\n\nint redisCheckSocketError(redisContext *c) {\n    int err = 0, errno_saved = errno;\n    socklen_t errlen = sizeof(err);\n\n    if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"getsockopt(SO_ERROR)\");\n        return REDIS_ERR;\n    }\n\n    if (err == 0) {\n        err = errno_saved;\n    }\n\n    if (err) {\n        errno = err;\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);\n        return REDIS_ERR;\n    }\n\n    return REDIS_OK;\n}\n\nint redisContextSetTimeout(redisContext *c, const struct timeval tv) {\n#if 0\n    const void *to_ptr = &tv;\n    size_t to_sz = sizeof(tv);\n\n    if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"setsockopt(SO_RCVTIMEO)\");\n        return REDIS_ERR;\n    }\n    if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,to_ptr,to_sz) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"setsockopt(SO_SNDTIMEO)\");\n        return REDIS_ERR;\n    }\n#else\n    double timeout = tv.tv_sec ;\n    timeout += (double) tv.tv_usec / 1000 / 1000;\n    if (swoole_coroutine_socket_set_timeout(c->fd, SO_RCVTIMEO, timeout) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"setsockopt(SO_RCVTIMEO)\");\n        return REDIS_ERR;\n    }\n    if (swoole_coroutine_socket_set_timeout(c->fd, SO_SNDTIMEO, timeout) == -1) {\n        __redisSetErrorFromErrno(c,REDIS_ERR_IO,\"setsockopt(SO_RCVTIMEO)\");\n        return REDIS_ERR;\n    }\n#endif\n    return REDIS_OK;\n}\n\nint redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout) {\n    /* Same timeval struct, short circuit */\n    if (c->connect_timeout == timeout)\n        return REDIS_OK;\n\n    /* Allocate context timeval if we need to */\n    if (c->connect_timeout == NULL) {\n        c->connect_timeout = hi_malloc(sizeof(*c->connect_timeout));\n        if (c->connect_timeout == NULL)\n            return REDIS_ERR;\n    }\n\n    memcpy(c->connect_timeout, timeout, sizeof(*c->connect_timeout));\n    return REDIS_OK;\n}\n\nint redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout) {\n    /* Same timeval struct, short circuit */\n    if (c->command_timeout == timeout)\n        return REDIS_OK;\n\n    /* Allocate context timeval if we need to */\n    if (c->command_timeout == NULL) {\n        c->command_timeout = hi_malloc(sizeof(*c->command_timeout));\n        if (c->command_timeout == NULL)\n            return REDIS_ERR;\n    }\n\n    memcpy(c->command_timeout, timeout, sizeof(*c->command_timeout));\n    return REDIS_OK;\n}\n\nstatic int _redisContextConnectTcp(redisContext *c, const char *addr, int port,\n                                   const struct timeval *timeout,\n                                   const char *source_addr) {\n    redisFD s;\n    int rv, n;\n    char _port[6];  /* strlen(\"65535\"); */\n    struct addrinfo hints, *servinfo, *bservinfo, *p, *b;\n    int blocking = (c->flags & REDIS_BLOCK);\n    int reuseaddr = (c->flags & REDIS_REUSEADDR);\n    int reuses = 0;\n    long timeout_msec = -1;\n\n    servinfo = NULL;\n    c->connection_type = REDIS_CONN_TCP;\n    c->tcp.port = port;\n\n    /* We need to take possession of the passed parameters\n     * to make them reusable for a reconnect.\n     * We also carefully check we don't free data we already own,\n     * as in the case of the reconnect method.\n     *\n     * This is a bit ugly, but atleast it works and doesn't leak memory.\n     **/\n    if (c->tcp.host != addr) {\n        hi_free(c->tcp.host);\n\n        c->tcp.host = hi_strdup(addr);\n        if (c->tcp.host == NULL)\n            goto oom;\n    }\n\n    if (timeout) {\n        if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR)\n            goto oom;\n    } else {\n        hi_free(c->connect_timeout);\n        c->connect_timeout = NULL;\n    }\n\n    if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {\n        __redisSetError(c, REDIS_ERR_IO, \"Invalid timeout specified\");\n        goto error;\n    }\n\n    if (source_addr == NULL) {\n        hi_free(c->tcp.source_addr);\n        c->tcp.source_addr = NULL;\n    } else if (c->tcp.source_addr != source_addr) {\n        hi_free(c->tcp.source_addr);\n        c->tcp.source_addr = hi_strdup(source_addr);\n    }\n\n    snprintf(_port, 6, \"%d\", port);\n    memset(&hints,0,sizeof(hints));\n    hints.ai_family = AF_INET;\n    hints.ai_socktype = SOCK_STREAM;\n\n    /* Try with IPv6 if no IPv4 address was found. We do it in this order since\n     * in a Redis client you can't afford to test if you have IPv6 connectivity\n     * as this would add latency to every connect. Otherwise a more sensible\n     * route could be: Use IPv6 if both addresses are available and there is IPv6\n     * connectivity. */\n    if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) {\n         hints.ai_family = AF_INET6;\n         if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {\n            __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));\n            return REDIS_ERR;\n        }\n    }\n    for (p = servinfo; p != NULL; p = p->ai_next) {\naddrretry:\n        if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == REDIS_INVALID_FD)\n            continue;\n\n        c->fd = s;\n        if (redisSetBlocking(c,0) != REDIS_OK)\n            goto error;\n        if (c->tcp.source_addr) {\n            int bound = 0;\n            /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */\n            if ((rv = getaddrinfo(c->tcp.source_addr, NULL, &hints, &bservinfo)) != 0) {\n                char buf[128];\n                snprintf(buf,sizeof(buf),\"Can't get addr: %s\",gai_strerror(rv));\n                __redisSetError(c,REDIS_ERR_OTHER,buf);\n                goto error;\n            }\n\n            if (reuseaddr) {\n                n = 1;\n                if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,\n                               sizeof(n)) < 0) {\n                    freeaddrinfo(bservinfo);\n                    goto error;\n                }\n            }\n\n            for (b = bservinfo; b != NULL; b = b->ai_next) {\n                if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {\n                    bound = 1;\n                    break;\n                }\n            }\n            freeaddrinfo(bservinfo);\n            if (!bound) {\n                char buf[128];\n                snprintf(buf,sizeof(buf),\"Can't bind socket: %s\",strerror(errno));\n                __redisSetError(c,REDIS_ERR_OTHER,buf);\n                goto error;\n            }\n        }\n\n        /* For repeat connection */\n        hi_free(c->saddr);\n        c->saddr = hi_malloc(p->ai_addrlen);\n        if (c->saddr == NULL)\n            goto oom;\n\n        memcpy(c->saddr, p->ai_addr, p->ai_addrlen);\n        c->addrlen = p->ai_addrlen;\n\n        if (c->connect_timeout) {\n            double timeout = c->connect_timeout->tv_sec ;\n            timeout += (double) c->connect_timeout->tv_usec / 1000 / 1000;\n            swoole_coroutine_socket_set_connect_timeout(c->fd, timeout);\n        }\n\n        if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {\n            if (errno == EHOSTUNREACH) {\n                redisNetClose(c);\n                continue;\n            } else if (errno == EINPROGRESS) {\n                if (blocking) {\n                    goto wait_for_ready;\n                }\n                /* This is ok.\n                 * Note that even when it's in blocking mode, we unset blocking\n                 * for `connect()`\n                 */\n            } else if (errno == EADDRNOTAVAIL && reuseaddr) {\n                if (++reuses >= REDIS_CONNECT_RETRIES) {\n                    goto error;\n                } else {\n                    redisNetClose(c);\n                    goto addrretry;\n                }\n            } else {\n                wait_for_ready:\n                if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)\n                    goto error;\n                if (redisSetTcpNoDelay(c) != REDIS_OK)\n                    goto error;\n            }\n        }\n        if (blocking && redisSetBlocking(c,1) != REDIS_OK)\n            goto error;\n\n        c->flags |= REDIS_CONNECTED;\n        rv = REDIS_OK;\n        goto end;\n    }\n    if (p == NULL) {\n        char buf[128];\n        snprintf(buf,sizeof(buf),\"Can't create socket: %s\",strerror(errno));\n        __redisSetError(c,REDIS_ERR_OTHER,buf);\n        goto error;\n    }\n\noom:\n    __redisSetError(c, REDIS_ERR_OOM, \"Out of memory\");\nerror:\n    rv = REDIS_ERR;\nend:\n    if(servinfo) {\n        freeaddrinfo(servinfo);\n    }\n\n    return rv;  // Need to return REDIS_OK if alright\n}\n\nint redisContextConnectTcp(redisContext *c, const char *addr, int port,\n                           const struct timeval *timeout) {\n    return _redisContextConnectTcp(c, addr, port, timeout, NULL);\n}\n\nint redisContextConnectBindTcp(redisContext *c, const char *addr, int port,\n                               const struct timeval *timeout,\n                               const char *source_addr) {\n    return _redisContextConnectTcp(c, addr, port, timeout, source_addr);\n}\n\nint redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {\n#ifndef _WIN32\n    int blocking = (c->flags & REDIS_BLOCK);\n    struct sockaddr_un *sa;\n    long timeout_msec = -1;\n\n    if (redisCreateSocket(c,AF_UNIX) < 0)\n        return REDIS_ERR;\n    if (redisSetBlocking(c,0) != REDIS_OK)\n        return REDIS_ERR;\n\n    c->connection_type = REDIS_CONN_UNIX;\n    if (c->unix_sock.path != path) {\n        hi_free(c->unix_sock.path);\n\n        c->unix_sock.path = hi_strdup(path);\n        if (c->unix_sock.path == NULL)\n            goto oom;\n    }\n\n    if (timeout) {\n        if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR)\n            goto oom;\n    } else {\n        hi_free(c->connect_timeout);\n        c->connect_timeout = NULL;\n    }\n\n    if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)\n        return REDIS_ERR;\n\n    /* Don't leak sockaddr if we're reconnecting */\n    if (c->saddr) hi_free(c->saddr);\n\n    sa = (struct sockaddr_un*)(c->saddr = hi_malloc(sizeof(struct sockaddr_un)));\n    if (sa == NULL)\n        goto oom;\n\n    c->addrlen = sizeof(struct sockaddr_un);\n    sa->sun_family = AF_UNIX;\n    strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1);\n    if (connect(c->fd, (struct sockaddr*)sa, sizeof(*sa)) == -1) {\n        if (errno == EINPROGRESS && !blocking) {\n            /* This is ok. */\n        } else {\n            if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)\n                return REDIS_ERR;\n        }\n    }\n\n    /* Reset socket to be blocking after connect(2). */\n    if (blocking && redisSetBlocking(c,1) != REDIS_OK)\n        return REDIS_ERR;\n\n    c->flags |= REDIS_CONNECTED;\n    return REDIS_OK;\n#else\n    /* We currently do not support Unix sockets for Windows. */\n    /* TODO(m): https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ */\n    errno = EPROTONOSUPPORT;\n    return REDIS_ERR;\n#endif /* _WIN32 */\noom:\n    __redisSetError(c, REDIS_ERR_OOM, \"Out of memory\");\n    return REDIS_ERR;\n}\n"
  },
  {
    "path": "thirdparty/hiredis/net.h",
    "content": "/* Extracted from anet.c to work properly with Hiredis error reporting.\n *\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,\n *                     Jan-Erik Rediger <janerik at fnordig dot com>\n *\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 are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * 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 *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND 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 COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef __NET_H\n#define __NET_H\n\n#include \"hiredis.h\"\n\nvoid redisNetClose(redisContext *c);\nssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap);\nssize_t redisNetWrite(redisContext *c);\n\nint redisCheckSocketError(redisContext *c);\nint redisContextSetTimeout(redisContext *c, const struct timeval tv);\nint redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);\nint redisContextConnectBindTcp(redisContext *c, const char *addr, int port,\n                               const struct timeval *timeout,\n                               const char *source_addr);\nint redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);\nint redisKeepAlive(redisContext *c, int interval);\nint redisCheckConnectDone(redisContext *c, int *completed);\n\nint redisSetTcpNoDelay(redisContext *c);\n\n#endif\n"
  },
  {
    "path": "thirdparty/hiredis/read.c",
    "content": "/*\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n *\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 are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * 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 *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND 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 COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"fmacros.h\"\n#include <string.h>\n#include <stdlib.h>\n#ifndef _MSC_VER\n#include <unistd.h>\n#include <strings.h>\n#endif\n#include <assert.h>\n#include <errno.h>\n#include <ctype.h>\n#include <limits.h>\n#include <math.h>\n\n#include \"alloc.h\"\n#include \"read.h\"\n#include \"sds.h\"\n\n/* Initial size of our nested reply stack and how much we grow it when needd */\n#define REDIS_READER_STACK_SIZE 9\n\nstatic void __redisReaderSetError(redisReader *r, int type, const char *str) {\n    size_t len;\n\n    if (r->reply != NULL && r->fn && r->fn->freeObject) {\n        r->fn->freeObject(r->reply);\n        r->reply = NULL;\n    }\n\n    /* Clear input buffer on errors. */\n    sdsfree(r->buf);\n    r->buf = NULL;\n    r->pos = r->len = 0;\n\n    /* Reset task stack. */\n    r->ridx = -1;\n\n    /* Set error. */\n    r->err = type;\n    len = strlen(str);\n    len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1);\n    memcpy(r->errstr,str,len);\n    r->errstr[len] = '\\0';\n}\n\nstatic size_t chrtos(char *buf, size_t size, char byte) {\n    size_t len = 0;\n\n    switch(byte) {\n    case '\\\\':\n    case '\"':\n        len = snprintf(buf,size,\"\\\"\\\\%c\\\"\",byte);\n        break;\n    case '\\n': len = snprintf(buf,size,\"\\\"\\\\n\\\"\"); break;\n    case '\\r': len = snprintf(buf,size,\"\\\"\\\\r\\\"\"); break;\n    case '\\t': len = snprintf(buf,size,\"\\\"\\\\t\\\"\"); break;\n    case '\\a': len = snprintf(buf,size,\"\\\"\\\\a\\\"\"); break;\n    case '\\b': len = snprintf(buf,size,\"\\\"\\\\b\\\"\"); break;\n    default:\n        if (isprint(byte))\n            len = snprintf(buf,size,\"\\\"%c\\\"\",byte);\n        else\n            len = snprintf(buf,size,\"\\\"\\\\x%02x\\\"\",(unsigned char)byte);\n        break;\n    }\n\n    return len;\n}\n\nstatic void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) {\n    char cbuf[8], sbuf[128];\n\n    chrtos(cbuf,sizeof(cbuf),byte);\n    snprintf(sbuf,sizeof(sbuf),\n        \"Protocol error, got %s as reply type byte\", cbuf);\n    __redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf);\n}\n\nstatic void __redisReaderSetErrorOOM(redisReader *r) {\n    __redisReaderSetError(r,REDIS_ERR_OOM,\"Out of memory\");\n}\n\nstatic char *readBytes(redisReader *r, unsigned int bytes) {\n    char *p;\n    if (r->len-r->pos >= bytes) {\n        p = r->buf+r->pos;\n        r->pos += bytes;\n        return p;\n    }\n    return NULL;\n}\n\n/* Find pointer to \\r\\n. */\nstatic char *seekNewline(char *s, size_t len) {\n    char *ret;\n\n    /* We cannot match with fewer than 2 bytes */\n    if (len < 2)\n        return NULL;\n\n    /* Search up to len - 1 characters */\n    len--;\n\n    /* Look for the \\r */\n    while ((ret = memchr(s, '\\r', len)) != NULL) {\n        if (ret[1] == '\\n') {\n            /* Found. */\n            break;\n        }\n        /* Continue searching. */\n        ret++;\n        len -= ret - s;\n        s = ret;\n    }\n\n    return ret;\n}\n\n/* Convert a string into a long long. Returns REDIS_OK if the string could be\n * parsed into a (non-overflowing) long long, REDIS_ERR otherwise. The value\n * will be set to the parsed value when appropriate.\n *\n * Note that this function demands that the string strictly represents\n * a long long: no spaces or other characters before or after the string\n * representing the number are accepted, nor zeroes at the start if not\n * for the string \"0\" representing the zero number.\n *\n * Because of its strictness, it is safe to use this function to check if\n * you can convert a string into a long long, and obtain back the string\n * from the number without any loss in the string representation. */\nstatic int string2ll(const char *s, size_t slen, long long *value) {\n    const char *p = s;\n    size_t plen = 0;\n    int negative = 0;\n    unsigned long long v;\n\n    if (plen == slen)\n        return REDIS_ERR;\n\n    /* Special case: first and only digit is 0. */\n    if (slen == 1 && p[0] == '0') {\n        if (value != NULL) *value = 0;\n        return REDIS_OK;\n    }\n\n    if (p[0] == '-') {\n        negative = 1;\n        p++; plen++;\n\n        /* Abort on only a negative sign. */\n        if (plen == slen)\n            return REDIS_ERR;\n    }\n\n    /* First digit should be 1-9, otherwise the string should just be 0. */\n    if (p[0] >= '1' && p[0] <= '9') {\n        v = p[0]-'0';\n        p++; plen++;\n    } else if (p[0] == '0' && slen == 1) {\n        *value = 0;\n        return REDIS_OK;\n    } else {\n        return REDIS_ERR;\n    }\n\n    while (plen < slen && p[0] >= '0' && p[0] <= '9') {\n        if (v > (ULLONG_MAX / 10)) /* Overflow. */\n            return REDIS_ERR;\n        v *= 10;\n\n        if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */\n            return REDIS_ERR;\n        v += p[0]-'0';\n\n        p++; plen++;\n    }\n\n    /* Return if not all bytes were used. */\n    if (plen < slen)\n        return REDIS_ERR;\n\n    if (negative) {\n        if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */\n            return REDIS_ERR;\n        if (value != NULL) *value = -v;\n    } else {\n        if (v > LLONG_MAX) /* Overflow. */\n            return REDIS_ERR;\n        if (value != NULL) *value = v;\n    }\n    return REDIS_OK;\n}\n\nstatic char *readLine(redisReader *r, int *_len) {\n    char *p, *s;\n    int len;\n\n    p = r->buf+r->pos;\n    s = seekNewline(p,(r->len-r->pos));\n    if (s != NULL) {\n        len = s-(r->buf+r->pos);\n        r->pos += len+2; /* skip \\r\\n */\n        if (_len) *_len = len;\n        return p;\n    }\n    return NULL;\n}\n\nstatic void moveToNextTask(redisReader *r) {\n    redisReadTask *cur, *prv;\n    while (r->ridx >= 0) {\n        /* Return a.s.a.p. when the stack is now empty. */\n        if (r->ridx == 0) {\n            r->ridx--;\n            return;\n        }\n\n        cur = r->task[r->ridx];\n        prv = r->task[r->ridx-1];\n        assert(prv->type == REDIS_REPLY_ARRAY ||\n               prv->type == REDIS_REPLY_MAP ||\n               prv->type == REDIS_REPLY_SET ||\n               prv->type == REDIS_REPLY_PUSH);\n        if (cur->idx == prv->elements-1) {\n            r->ridx--;\n        } else {\n            /* Reset the type because the next item can be anything */\n            assert(cur->idx < prv->elements);\n            cur->type = -1;\n            cur->elements = -1;\n            cur->idx++;\n            return;\n        }\n    }\n}\n\nstatic int processLineItem(redisReader *r) {\n    redisReadTask *cur = r->task[r->ridx];\n    void *obj;\n    char *p;\n    int len;\n\n    if ((p = readLine(r,&len)) != NULL) {\n        if (cur->type == REDIS_REPLY_INTEGER) {\n            long long v;\n\n            if (string2ll(p, len, &v) == REDIS_ERR) {\n                __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                        \"Bad integer value\");\n                return REDIS_ERR;\n            }\n\n            if (r->fn && r->fn->createInteger) {\n                obj = r->fn->createInteger(cur,v);\n            } else {\n                obj = (void*)REDIS_REPLY_INTEGER;\n            }\n        } else if (cur->type == REDIS_REPLY_DOUBLE) {\n            char buf[326], *eptr;\n            double d;\n\n            if ((size_t)len >= sizeof(buf)) {\n                __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                        \"Double value is too large\");\n                return REDIS_ERR;\n            }\n\n            memcpy(buf,p,len);\n            buf[len] = '\\0';\n\n            if (len == 3 && strcasecmp(buf,\"inf\") == 0) {\n                d = INFINITY; /* Positive infinite. */\n            } else if (len == 4 && strcasecmp(buf,\"-inf\") == 0) {\n                d = -INFINITY; /* Negative infinite. */\n            } else {\n                d = strtod((char*)buf,&eptr);\n                /* RESP3 only allows \"inf\", \"-inf\", and finite values, while\n                 * strtod() allows other variations on infinity, NaN,\n                 * etc. We explicity handle our two allowed infinite cases\n                 * above, so strtod() should only result in finite values. */\n                if (buf[0] == '\\0' || eptr != &buf[len] || !isfinite(d)) {\n                    __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                            \"Bad double value\");\n                    return REDIS_ERR;\n                }\n            }\n\n            if (r->fn && r->fn->createDouble) {\n                obj = r->fn->createDouble(cur,d,buf,len);\n            } else {\n                obj = (void*)REDIS_REPLY_DOUBLE;\n            }\n        } else if (cur->type == REDIS_REPLY_NIL) {\n            if (len != 0) {\n                __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                        \"Bad nil value\");\n                return REDIS_ERR;\n            }\n\n            if (r->fn && r->fn->createNil)\n                obj = r->fn->createNil(cur);\n            else\n                obj = (void*)REDIS_REPLY_NIL;\n        } else if (cur->type == REDIS_REPLY_BOOL) {\n            int bval;\n\n            if (len != 1 || !strchr(\"tTfF\", p[0])) {\n                __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                        \"Bad bool value\");\n                return REDIS_ERR;\n            }\n\n            bval = p[0] == 't' || p[0] == 'T';\n            if (r->fn && r->fn->createBool)\n                obj = r->fn->createBool(cur,bval);\n            else\n                obj = (void*)REDIS_REPLY_BOOL;\n        } else if (cur->type == REDIS_REPLY_BIGNUM) {\n            /* Ensure all characters are decimal digits (with possible leading\n             * minus sign). */\n            int i;\n            for (i = 0; i < len; i++) {\n                /* XXX Consider: Allow leading '+'? Error on leading '0's? */\n                if (i == 0 && p[0] == '-') continue;\n                if (p[i] < '0' || p[i] > '9') {\n                    __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                            \"Bad bignum value\");\n                    return REDIS_ERR;\n                }\n            }\n            if (r->fn && r->fn->createString)\n                obj = r->fn->createString(cur,p,len);\n            else\n                obj = (void*)REDIS_REPLY_BIGNUM;\n        } else {\n            /* Type will be error or status. */\n            int i;\n            for (i = 0; i < len; i++) {\n                if (p[i] == '\\r' || p[i] == '\\n') {\n                    __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                            \"Bad simple string value\");\n                    return REDIS_ERR;\n                }\n            }\n            if (r->fn && r->fn->createString)\n                obj = r->fn->createString(cur,p,len);\n            else\n                obj = (void*)(size_t)(cur->type);\n        }\n\n        if (obj == NULL) {\n            __redisReaderSetErrorOOM(r);\n            return REDIS_ERR;\n        }\n\n        /* Set reply if this is the root object. */\n        if (r->ridx == 0) r->reply = obj;\n        moveToNextTask(r);\n        return REDIS_OK;\n    }\n\n    return REDIS_ERR;\n}\n\nstatic int processBulkItem(redisReader *r) {\n    redisReadTask *cur = r->task[r->ridx];\n    void *obj = NULL;\n    char *p, *s;\n    long long len;\n    unsigned long bytelen;\n    int success = 0;\n\n    p = r->buf+r->pos;\n    s = seekNewline(p,r->len-r->pos);\n    if (s != NULL) {\n        p = r->buf+r->pos;\n        bytelen = s-(r->buf+r->pos)+2; /* include \\r\\n */\n\n        if (string2ll(p, bytelen - 2, &len) == REDIS_ERR) {\n            __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                    \"Bad bulk string length\");\n            return REDIS_ERR;\n        }\n\n        if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) {\n            __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                    \"Bulk string length out of range\");\n            return REDIS_ERR;\n        }\n\n        if (len == -1) {\n            /* The nil object can always be created. */\n            if (r->fn && r->fn->createNil)\n                obj = r->fn->createNil(cur);\n            else\n                obj = (void*)REDIS_REPLY_NIL;\n            success = 1;\n        } else {\n            /* Only continue when the buffer contains the entire bulk item. */\n            bytelen += len+2; /* include \\r\\n */\n            if (r->pos+bytelen <= r->len) {\n                if ((cur->type == REDIS_REPLY_VERB && len < 4) ||\n                    (cur->type == REDIS_REPLY_VERB && s[5] != ':'))\n                {\n                    __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                            \"Verbatim string 4 bytes of content type are \"\n                            \"missing or incorrectly encoded.\");\n                    return REDIS_ERR;\n                }\n                if (r->fn && r->fn->createString)\n                    obj = r->fn->createString(cur,s+2,len);\n                else\n                    obj = (void*)(long)cur->type;\n                success = 1;\n            }\n        }\n\n        /* Proceed when obj was created. */\n        if (success) {\n            if (obj == NULL) {\n                __redisReaderSetErrorOOM(r);\n                return REDIS_ERR;\n            }\n\n            r->pos += bytelen;\n\n            /* Set reply if this is the root object. */\n            if (r->ridx == 0) r->reply = obj;\n            moveToNextTask(r);\n            return REDIS_OK;\n        }\n    }\n\n    return REDIS_ERR;\n}\n\nstatic int redisReaderGrow(redisReader *r) {\n    redisReadTask **aux;\n    int newlen;\n\n    /* Grow our stack size */\n    newlen = r->tasks + REDIS_READER_STACK_SIZE;\n    aux = hi_realloc(r->task, sizeof(*r->task) * newlen);\n    if (aux == NULL)\n        goto oom;\n\n    r->task = aux;\n\n    /* Allocate new tasks */\n    for (; r->tasks < newlen; r->tasks++) {\n        r->task[r->tasks] = hi_calloc(1, sizeof(**r->task));\n        if (r->task[r->tasks] == NULL)\n            goto oom;\n    }\n\n    return REDIS_OK;\noom:\n    __redisReaderSetErrorOOM(r);\n    return REDIS_ERR;\n}\n\n/* Process the array, map and set types. */\nstatic int processAggregateItem(redisReader *r) {\n    redisReadTask *cur = r->task[r->ridx];\n    void *obj;\n    char *p;\n    long long elements;\n    int root = 0, len;\n\n    if (r->ridx == r->tasks - 1) {\n        if (redisReaderGrow(r) == REDIS_ERR)\n            return REDIS_ERR;\n    }\n\n    if ((p = readLine(r,&len)) != NULL) {\n        if (string2ll(p, len, &elements) == REDIS_ERR) {\n            __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                    \"Bad multi-bulk length\");\n            return REDIS_ERR;\n        }\n\n        root = (r->ridx == 0);\n\n        if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX) ||\n            (r->maxelements > 0 && elements > r->maxelements))\n        {\n            __redisReaderSetError(r,REDIS_ERR_PROTOCOL,\n                    \"Multi-bulk length out of range\");\n            return REDIS_ERR;\n        }\n\n        if (elements == -1) {\n            if (r->fn && r->fn->createNil)\n                obj = r->fn->createNil(cur);\n            else\n                obj = (void*)REDIS_REPLY_NIL;\n\n            if (obj == NULL) {\n                __redisReaderSetErrorOOM(r);\n                return REDIS_ERR;\n            }\n\n            moveToNextTask(r);\n        } else {\n            if (cur->type == REDIS_REPLY_MAP) elements *= 2;\n\n            if (r->fn && r->fn->createArray)\n                obj = r->fn->createArray(cur,elements);\n            else\n                obj = (void*)(long)cur->type;\n\n            if (obj == NULL) {\n                __redisReaderSetErrorOOM(r);\n                return REDIS_ERR;\n            }\n\n            /* Modify task stack when there are more than 0 elements. */\n            if (elements > 0) {\n                cur->elements = elements;\n                cur->obj = obj;\n                r->ridx++;\n                r->task[r->ridx]->type = -1;\n                r->task[r->ridx]->elements = -1;\n                r->task[r->ridx]->idx = 0;\n                r->task[r->ridx]->obj = NULL;\n                r->task[r->ridx]->parent = cur;\n                r->task[r->ridx]->privdata = r->privdata;\n            } else {\n                moveToNextTask(r);\n            }\n        }\n\n        /* Set reply if this is the root object. */\n        if (root) r->reply = obj;\n        return REDIS_OK;\n    }\n\n    return REDIS_ERR;\n}\n\nstatic int processItem(redisReader *r) {\n    redisReadTask *cur = r->task[r->ridx];\n    char *p;\n\n    /* check if we need to read type */\n    if (cur->type < 0) {\n        if ((p = readBytes(r,1)) != NULL) {\n            switch (p[0]) {\n            case '-':\n                cur->type = REDIS_REPLY_ERROR;\n                break;\n            case '+':\n                cur->type = REDIS_REPLY_STATUS;\n                break;\n            case ':':\n                cur->type = REDIS_REPLY_INTEGER;\n                break;\n            case ',':\n                cur->type = REDIS_REPLY_DOUBLE;\n                break;\n            case '_':\n                cur->type = REDIS_REPLY_NIL;\n                break;\n            case '$':\n                cur->type = REDIS_REPLY_STRING;\n                break;\n            case '*':\n                cur->type = REDIS_REPLY_ARRAY;\n                break;\n            case '%':\n                cur->type = REDIS_REPLY_MAP;\n                break;\n            case '~':\n                cur->type = REDIS_REPLY_SET;\n                break;\n            case '#':\n                cur->type = REDIS_REPLY_BOOL;\n                break;\n            case '=':\n                cur->type = REDIS_REPLY_VERB;\n                break;\n            case '>':\n                cur->type = REDIS_REPLY_PUSH;\n                break;\n            case '(':\n                cur->type = REDIS_REPLY_BIGNUM;\n                break;\n            default:\n                __redisReaderSetErrorProtocolByte(r,*p);\n                return REDIS_ERR;\n            }\n        } else {\n            /* could not consume 1 byte */\n            return REDIS_ERR;\n        }\n    }\n\n    /* process typed item */\n    switch(cur->type) {\n    case REDIS_REPLY_ERROR:\n    case REDIS_REPLY_STATUS:\n    case REDIS_REPLY_INTEGER:\n    case REDIS_REPLY_DOUBLE:\n    case REDIS_REPLY_NIL:\n    case REDIS_REPLY_BOOL:\n    case REDIS_REPLY_BIGNUM:\n        return processLineItem(r);\n    case REDIS_REPLY_STRING:\n    case REDIS_REPLY_VERB:\n        return processBulkItem(r);\n    case REDIS_REPLY_ARRAY:\n    case REDIS_REPLY_MAP:\n    case REDIS_REPLY_SET:\n    case REDIS_REPLY_PUSH:\n        return processAggregateItem(r);\n    default:\n        assert(NULL);\n        return REDIS_ERR; /* Avoid warning. */\n    }\n}\n\nredisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {\n    redisReader *r;\n\n    r = hi_calloc(1,sizeof(redisReader));\n    if (r == NULL)\n        return NULL;\n\n    r->buf = sdsempty();\n    if (r->buf == NULL)\n        goto oom;\n\n    r->task = hi_calloc(REDIS_READER_STACK_SIZE, sizeof(*r->task));\n    if (r->task == NULL)\n        goto oom;\n\n    for (; r->tasks < REDIS_READER_STACK_SIZE; r->tasks++) {\n        r->task[r->tasks] = hi_calloc(1, sizeof(**r->task));\n        if (r->task[r->tasks] == NULL)\n            goto oom;\n    }\n\n    r->fn = fn;\n    r->maxbuf = REDIS_READER_MAX_BUF;\n    r->maxelements = REDIS_READER_MAX_ARRAY_ELEMENTS;\n    r->ridx = -1;\n\n    return r;\noom:\n    redisReaderFree(r);\n    return NULL;\n}\n\nvoid redisReaderFree(redisReader *r) {\n    if (r == NULL)\n        return;\n\n    if (r->reply != NULL && r->fn && r->fn->freeObject)\n        r->fn->freeObject(r->reply);\n\n    if (r->task) {\n        /* We know r->task[i] is allocated if i < r->tasks */\n        for (int i = 0; i < r->tasks; i++) {\n            hi_free(r->task[i]);\n        }\n\n        hi_free(r->task);\n    }\n\n    sdsfree(r->buf);\n    hi_free(r);\n}\n\nint redisReaderFeed(redisReader *r, const char *buf, size_t len) {\n    sds newbuf;\n\n    /* Return early when this reader is in an erroneous state. */\n    if (r->err)\n        return REDIS_ERR;\n\n    /* Copy the provided buffer. */\n    if (buf != NULL && len >= 1) {\n        /* Destroy internal buffer when it is empty and is quite large. */\n        if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) {\n            sdsfree(r->buf);\n            r->buf = sdsempty();\n            if (r->buf == 0) goto oom;\n\n            r->pos = 0;\n        }\n\n        newbuf = sdscatlen(r->buf,buf,len);\n        if (newbuf == NULL) goto oom;\n\n        r->buf = newbuf;\n        r->len = sdslen(r->buf);\n    }\n\n    return REDIS_OK;\noom:\n    __redisReaderSetErrorOOM(r);\n    return REDIS_ERR;\n}\n\nint redisReaderGetReply(redisReader *r, void **reply) {\n    /* Default target pointer to NULL. */\n    if (reply != NULL)\n        *reply = NULL;\n\n    /* Return early when this reader is in an erroneous state. */\n    if (r->err)\n        return REDIS_ERR;\n\n    /* When the buffer is empty, there will never be a reply. */\n    if (r->len == 0)\n        return REDIS_OK;\n\n    /* Set first item to process when the stack is empty. */\n    if (r->ridx == -1) {\n        r->task[0]->type = -1;\n        r->task[0]->elements = -1;\n        r->task[0]->idx = -1;\n        r->task[0]->obj = NULL;\n        r->task[0]->parent = NULL;\n        r->task[0]->privdata = r->privdata;\n        r->ridx = 0;\n    }\n\n    /* Process items in reply. */\n    while (r->ridx >= 0)\n        if (processItem(r) != REDIS_OK)\n            break;\n\n    /* Return ASAP when an error occurred. */\n    if (r->err)\n        return REDIS_ERR;\n\n    /* Discard part of the buffer when we've consumed at least 1k, to avoid\n     * doing unnecessary calls to memmove() in sds.c. */\n    if (r->pos >= 1024) {\n        if (sdsrange(r->buf,r->pos,-1) < 0) return REDIS_ERR;\n        r->pos = 0;\n        r->len = sdslen(r->buf);\n    }\n\n    /* Emit a reply when there is one. */\n    if (r->ridx == -1) {\n        if (reply != NULL) {\n            *reply = r->reply;\n        } else if (r->reply != NULL && r->fn && r->fn->freeObject) {\n            r->fn->freeObject(r->reply);\n        }\n        r->reply = NULL;\n    }\n    return REDIS_OK;\n}\n"
  },
  {
    "path": "thirdparty/hiredis/read.h",
    "content": "/*\n * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>\n *\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 are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * 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 *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND 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 COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n#ifndef __HIREDIS_READ_H\n#define __HIREDIS_READ_H\n#include <stdio.h> /* for size_t */\n\n#define REDIS_ERR -1\n#define REDIS_OK 0\n\n/* When an error occurs, the err flag in a context is set to hold the type of\n * error that occurred. REDIS_ERR_IO means there was an I/O error and you\n * should use the \"errno\" variable to find out what is wrong.\n * For other values, the \"errstr\" field will hold a description. */\n#define REDIS_ERR_IO 1 /* Error in read or write */\n#define REDIS_ERR_EOF 3 /* End of file */\n#define REDIS_ERR_PROTOCOL 4 /* Protocol error */\n#define REDIS_ERR_OOM 5 /* Out of memory */\n#define REDIS_ERR_TIMEOUT 6 /* Timed out */\n#define REDIS_ERR_OTHER 2 /* Everything else... */\n\n#define REDIS_REPLY_STRING 1\n#define REDIS_REPLY_ARRAY 2\n#define REDIS_REPLY_INTEGER 3\n#define REDIS_REPLY_NIL 4\n#define REDIS_REPLY_STATUS 5\n#define REDIS_REPLY_ERROR 6\n#define REDIS_REPLY_DOUBLE 7\n#define REDIS_REPLY_BOOL 8\n#define REDIS_REPLY_MAP 9\n#define REDIS_REPLY_SET 10\n#define REDIS_REPLY_ATTR 11\n#define REDIS_REPLY_PUSH 12\n#define REDIS_REPLY_BIGNUM 13\n#define REDIS_REPLY_VERB 14\n\n/* Default max unused reader buffer. */\n#define REDIS_READER_MAX_BUF (1024*16)\n\n/* Default multi-bulk element limit */\n#define REDIS_READER_MAX_ARRAY_ELEMENTS ((1LL<<32) - 1)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct redisReadTask {\n    int type;\n    long long elements; /* number of elements in multibulk container */\n    int idx; /* index in parent (array) object */\n    void *obj; /* holds user-generated value for a read task */\n    struct redisReadTask *parent; /* parent task */\n    void *privdata; /* user-settable arbitrary field */\n} redisReadTask;\n\ntypedef struct redisReplyObjectFunctions {\n    void *(*createString)(const redisReadTask*, char*, size_t);\n    void *(*createArray)(const redisReadTask*, size_t);\n    void *(*createInteger)(const redisReadTask*, long long);\n    void *(*createDouble)(const redisReadTask*, double, char*, size_t);\n    void *(*createNil)(const redisReadTask*);\n    void *(*createBool)(const redisReadTask*, int);\n    void (*freeObject)(void*);\n} redisReplyObjectFunctions;\n\ntypedef struct redisReader {\n    int err; /* Error flags, 0 when there is no error */\n    char errstr[128]; /* String representation of error when applicable */\n\n    char *buf; /* Read buffer */\n    size_t pos; /* Buffer cursor */\n    size_t len; /* Buffer length */\n    size_t maxbuf; /* Max length of unused buffer */\n    long long maxelements; /* Max multi-bulk elements */\n\n    redisReadTask **task;\n    int tasks;\n\n    int ridx; /* Index of current read task */\n    void *reply; /* Temporary reply pointer */\n\n    redisReplyObjectFunctions *fn;\n    void *privdata;\n} redisReader;\n\n/* Public API for the protocol parser. */\nredisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn);\nvoid redisReaderFree(redisReader *r);\nint redisReaderFeed(redisReader *r, const char *buf, size_t len);\nint redisReaderGetReply(redisReader *r, void **reply);\n\n#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))\n#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply)\n#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr)\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "thirdparty/hiredis/sds.c",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Oran Agra\n * Copyright (c) 2015, Redis Labs, 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 are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * 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 *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND 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 COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include \"fmacros.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#include <assert.h>\n#include <limits.h>\n#include \"sds.h\"\n#include \"sdsalloc.h\"\n\nstatic inline int sdsHdrSize(char type) {\n    switch(type&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return sizeof(struct sdshdr5);\n        case SDS_TYPE_8:\n            return sizeof(struct sdshdr8);\n        case SDS_TYPE_16:\n            return sizeof(struct sdshdr16);\n        case SDS_TYPE_32:\n            return sizeof(struct sdshdr32);\n        case SDS_TYPE_64:\n            return sizeof(struct sdshdr64);\n    }\n    return 0;\n}\n\nstatic inline char sdsReqType(size_t string_size) {\n    if (string_size < 32)\n        return SDS_TYPE_5;\n    if (string_size < 0xff)\n        return SDS_TYPE_8;\n    if (string_size < 0xffff)\n        return SDS_TYPE_16;\n    if (string_size < 0xffffffff)\n        return SDS_TYPE_32;\n    return SDS_TYPE_64;\n}\n\nstatic inline size_t sdsTypeMaxSize(char type) {\n    if (type == SDS_TYPE_5)\n        return (1<<5) - 1;\n    if (type == SDS_TYPE_8)\n        return (1<<8) - 1;\n    if (type == SDS_TYPE_16)\n        return (1<<16) - 1;\n#if (LONG_MAX == LLONG_MAX)\n    if (type == SDS_TYPE_32)\n        return (1ll<<32) - 1;\n#endif\n    return -1; /* this is equivalent to the max SDS_TYPE_64 or SDS_TYPE_32 */\n}\n\n/* Create a new sds string with the content specified by the 'init' pointer\n * and 'initlen'.\n * If NULL is used for 'init' the string is initialized with zero bytes.\n *\n * The string is always null-terminated (all the sds strings are, always) so\n * even if you create an sds string with:\n *\n * mystring = sdsnewlen(\"abc\",3);\n *\n * You can print the string with printf() as there is an implicit \\0 at the\n * end of the string. However the string is binary safe and can contain\n * \\0 characters in the middle, as the length is stored in the sds header. */\nsds sdsnewlen(const void *init, size_t initlen) {\n    void *sh;\n    sds s;\n    char type = sdsReqType(initlen);\n    /* Empty strings are usually created in order to append. Use type 8\n     * since type 5 is not good at this. */\n    if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;\n    int hdrlen = sdsHdrSize(type);\n    unsigned char *fp; /* flags pointer. */\n\n    if (initlen > SIZE_MAX - hdrlen - 1) return NULL; /* Catch size_t overflow */\n    sh = s_malloc(hdrlen+initlen+1);\n    if (sh == NULL) return NULL;\n    if (!init)\n        memset(sh, 0, hdrlen+initlen+1);\n    s = (char*)sh+hdrlen;\n    fp = ((unsigned char*)s)-1;\n    switch(type) {\n        case SDS_TYPE_5: {\n            *fp = type | (initlen << SDS_TYPE_BITS);\n            break;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            sh->len = initlen;\n            sh->alloc = initlen;\n            *fp = type;\n            break;\n        }\n    }\n    if (initlen && init)\n        memcpy(s, init, initlen);\n    s[initlen] = '\\0';\n    return s;\n}\n\n/* Create an empty (zero length) sds string. Even in this case the string\n * always has an implicit null term. */\nsds sdsempty(void) {\n    return sdsnewlen(\"\",0);\n}\n\n/* Create a new sds string starting from a null terminated C string. */\nsds sdsnew(const char *init) {\n    size_t initlen = (init == NULL) ? 0 : strlen(init);\n    return sdsnewlen(init, initlen);\n}\n\n/* Duplicate an sds string. */\nsds sdsdup(const sds s) {\n    return sdsnewlen(s, sdslen(s));\n}\n\n/* Free an sds string. No operation is performed if 's' is NULL. */\nvoid sdsfree(sds s) {\n    if (s == NULL) return;\n    s_free((char*)s-sdsHdrSize(s[-1]));\n}\n\n/* Set the sds string length to the length as obtained with strlen(), so\n * considering as content only up to the first null term character.\n *\n * This function is useful when the sds string is hacked manually in some\n * way, like in the following example:\n *\n * s = sdsnew(\"foobar\");\n * s[2] = '\\0';\n * sdsupdatelen(s);\n * printf(\"%d\\n\", sdslen(s));\n *\n * The output will be \"2\", but if we comment out the call to sdsupdatelen()\n * the output will be \"6\" as the string was modified but the logical length\n * remains 6 bytes. */\nvoid sdsupdatelen(sds s) {\n    int reallen = strlen(s);\n    sdssetlen(s, reallen);\n}\n\n/* Modify an sds string in-place to make it empty (zero length).\n * However all the existing buffer is not discarded but set as free space\n * so that next append operations will not require allocations up to the\n * number of bytes previously available. */\nvoid sdsclear(sds s) {\n    sdssetlen(s, 0);\n    s[0] = '\\0';\n}\n\n/* Enlarge the free space at the end of the sds string so that the caller\n * is sure that after calling this function can overwrite up to addlen\n * bytes after the end of the string, plus one more byte for nul term.\n *\n * Note: this does not change the *length* of the sds string as returned\n * by sdslen(), but only the free buffer space we have. */\n sds sdsMakeRoomFor(sds s, size_t addlen) {\n    void *sh, *newsh;\n    size_t avail = sdsavail(s);\n    size_t len, newlen;\n    char type, oldtype = s[-1] & SDS_TYPE_MASK;\n    int hdrlen;\n\n    /* Return ASAP if there is enough space left. */\n    if (avail >= addlen) return s;\n\n    len = sdslen(s);\n    sh = (char*)s - sdsHdrSize(oldtype);\n\n    /* Fix: Prevent Integer Overflow */\n    if (addlen > SIZE_MAX - len) return NULL;  /* Prevent overflow */\n    newlen = len + addlen;\n    \n    if (newlen < SDS_MAX_PREALLOC)\n        newlen *= 2;\n    else\n        newlen += SDS_MAX_PREALLOC;\n\n    type = sdsReqType(newlen);\n\n    /* Don't use type 5: the user is appending to the string and type 5 is\n     * not able to remember empty space, so sdsMakeRoomFor() must be called\n     * at every appending operation. */\n    if (type == SDS_TYPE_5) type = SDS_TYPE_8;\n\n    hdrlen = sdsHdrSize(type);\n\n    /* Fix: Ensure safe memory allocation */\n    if (hdrlen + newlen + 1 < newlen) return NULL;  /* Prevent overflow */\n\n    if (oldtype == type) {\n        newsh = s_realloc(sh, hdrlen + newlen + 1);\n        if (newsh == NULL) return NULL;\n        s = (char*)newsh + hdrlen;\n    } else {\n        /* Since the header size changes, need to move the string forward,\n         * and can't use realloc */\n        newsh = s_malloc(hdrlen + newlen + 1);\n        if (newsh == NULL) return NULL;\n        memcpy((char*)newsh + hdrlen, s, len + 1);\n        s_free(sh);\n        s = (char*)newsh + hdrlen;\n        s[-1] = type;\n        sdssetlen(s, len);\n    }\n\n    /* Fix: Prevent setting a too-large allocation */\n    if (newlen > sdsTypeMaxSize(type)) newlen = sdsTypeMaxSize(type);\n    sdssetalloc(s, newlen);\n\n    return s;\n}\n\n\n/* Reallocate the sds string so that it has no free space at the end. The\n * contained string remains not altered, but next concatenation operations\n * will require a reallocation.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdsRemoveFreeSpace(sds s) {\n    void *sh, *newsh;\n    char type, oldtype = s[-1] & SDS_TYPE_MASK;\n    int hdrlen;\n    size_t len = sdslen(s);\n    sh = (char*)s-sdsHdrSize(oldtype);\n\n    type = sdsReqType(len);\n    hdrlen = sdsHdrSize(type);\n    if (oldtype==type) {\n        newsh = s_realloc(sh, hdrlen+len+1);\n        if (newsh == NULL) return NULL;\n        s = (char*)newsh+hdrlen;\n    } else {\n        newsh = s_malloc(hdrlen+len+1);\n        if (newsh == NULL) return NULL;\n        memcpy((char*)newsh+hdrlen, s, len+1);\n        s_free(sh);\n        s = (char*)newsh+hdrlen;\n        s[-1] = type;\n        sdssetlen(s, len);\n    }\n    sdssetalloc(s, len);\n    return s;\n}\n\n/* Return the total size of the allocation of the specifed sds string,\n * including:\n * 1) The sds header before the pointer.\n * 2) The string.\n * 3) The free buffer at the end if any.\n * 4) The implicit null term.\n */\nsize_t sdsAllocSize(sds s) {\n    size_t alloc = sdsalloc(s);\n    return sdsHdrSize(s[-1])+alloc+1;\n}\n\n/* Return the pointer of the actual SDS allocation (normally SDS strings\n * are referenced by the start of the string buffer). */\nvoid *sdsAllocPtr(sds s) {\n    return (void*) (s-sdsHdrSize(s[-1]));\n}\n\n/* Increment the sds length and decrements the left free space at the\n * end of the string according to 'incr'. Also set the null term\n * in the new end of the string.\n *\n * This function is used in order to fix the string length after the\n * user calls sdsMakeRoomFor(), writes something after the end of\n * the current string, and finally needs to set the new length.\n *\n * Note: it is possible to use a negative increment in order to\n * right-trim the string.\n *\n * Usage example:\n *\n * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the\n * following schema, to cat bytes coming from the kernel to the end of an\n * sds string without copying into an intermediate buffer:\n *\n * oldlen = sdslen(s);\n * s = sdsMakeRoomFor(s, BUFFER_SIZE);\n * nread = read(fd, s+oldlen, BUFFER_SIZE);\n * ... check for nread <= 0 and handle it ...\n * sdsIncrLen(s, nread);\n */\nvoid sdsIncrLen(sds s, int incr) {\n    unsigned char flags = s[-1];\n    size_t len;\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5: {\n            unsigned char *fp = ((unsigned char*)s)-1;\n            unsigned char oldlen = SDS_TYPE_5_LEN(flags);\n            assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr)));\n            *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS);\n            len = oldlen+incr;\n            break;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr)));\n            len = (sh->len += incr);\n            break;\n        }\n        default: len = 0; /* Just to avoid compilation warnings. */\n    }\n    s[len] = '\\0';\n}\n\n/* Grow the sds to have the specified length. Bytes that were not part of\n * the original length of the sds will be set to zero.\n *\n * if the specified length is smaller than the current length, no operation\n * is performed. */\nsds sdsgrowzero(sds s, size_t len) {\n    size_t curlen = sdslen(s);\n\n    if (len <= curlen) return s;\n    s = sdsMakeRoomFor(s,len-curlen);\n    if (s == NULL) return NULL;\n\n    /* Make sure added region doesn't contain garbage */\n    memset(s+curlen,0,(len-curlen+1)); /* also set trailing \\0 byte */\n    sdssetlen(s, len);\n    return s;\n}\n\n/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the\n * end of the specified sds string 's'.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatlen(sds s, const void *t, size_t len) {\n    size_t curlen = sdslen(s);\n\n    s = sdsMakeRoomFor(s,len);\n    if (s == NULL) return NULL;\n    memcpy(s+curlen, t, len);\n    sdssetlen(s, curlen+len);\n    s[curlen+len] = '\\0';\n    return s;\n}\n\n/* Append the specified null termianted C string to the sds string 's'.\n *\n * After the call, the passed sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscat(sds s, const char *t) {\n    return sdscatlen(s, t, strlen(t));\n}\n\n/* Append the specified sds 't' to the existing sds 's'.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatsds(sds s, const sds t) {\n    return sdscatlen(s, t, sdslen(t));\n}\n\n/* Destructively modify the sds string 's' to hold the specified binary\n * safe string pointed by 't' of length 'len' bytes. */\nsds sdscpylen(sds s, const char *t, size_t len) {\n    if (sdsalloc(s) < len) {\n        s = sdsMakeRoomFor(s,len-sdslen(s));\n        if (s == NULL) return NULL;\n    }\n    memcpy(s, t, len);\n    s[len] = '\\0';\n    sdssetlen(s, len);\n    return s;\n}\n\n/* Like sdscpylen() but 't' must be a null-terminated string so that the length\n * of the string is obtained with strlen(). */\nsds sdscpy(sds s, const char *t) {\n    return sdscpylen(s, t, strlen(t));\n}\n\n/* Helper for sdscatlonglong() doing the actual number -> string\n * conversion. 's' must point to a string with room for at least\n * SDS_LLSTR_SIZE bytes.\n *\n * The function returns the length of the null-terminated string\n * representation stored at 's'. */\n#define SDS_LLSTR_SIZE 21\nint sdsll2str(char *s, long long value) {\n    char *p, aux;\n    unsigned long long v;\n    size_t l;\n\n    /* Generate the string representation, this method produces\n     * an reversed string. */\n    v = (value < 0) ? -value : value;\n    p = s;\n    do {\n        *p++ = '0'+(v%10);\n        v /= 10;\n    } while(v);\n    if (value < 0) *p++ = '-';\n\n    /* Compute length and add null term. */\n    l = p-s;\n    *p = '\\0';\n\n    /* Reverse the string. */\n    p--;\n    while(s < p) {\n        aux = *s;\n        *s = *p;\n        *p = aux;\n        s++;\n        p--;\n    }\n    return l;\n}\n\n/* Identical sdsll2str(), but for unsigned long long type. */\nint sdsull2str(char *s, unsigned long long v) {\n    char *p, aux;\n    size_t l;\n\n    /* Generate the string representation, this method produces\n     * an reversed string. */\n    p = s;\n    do {\n        *p++ = '0'+(v%10);\n        v /= 10;\n    } while(v);\n\n    /* Compute length and add null term. */\n    l = p-s;\n    *p = '\\0';\n\n    /* Reverse the string. */\n    p--;\n    while(s < p) {\n        aux = *s;\n        *s = *p;\n        *p = aux;\n        s++;\n        p--;\n    }\n    return l;\n}\n\n/* Create an sds string from a long long value. It is much faster than:\n *\n * sdscatprintf(sdsempty(),\"%lld\\n\", value);\n */\nsds sdsfromlonglong(long long value) {\n    char buf[SDS_LLSTR_SIZE];\n    int len = sdsll2str(buf,value);\n\n    return sdsnewlen(buf,len);\n}\n\n/* Like sdscatprintf() but gets va_list instead of being variadic. */\nsds sdscatvprintf(sds s, const char *fmt, va_list ap) {\n    va_list cpy;\n    char staticbuf[1024], *buf = staticbuf, *t;\n    size_t buflen = strlen(fmt)*2;\n\n    /* We try to start using a static buffer for speed.\n     * If not possible we revert to heap allocation. */\n    if (buflen > sizeof(staticbuf)) {\n        buf = s_malloc(buflen);\n        if (buf == NULL) return NULL;\n    } else {\n        buflen = sizeof(staticbuf);\n    }\n\n    /* Try with buffers two times bigger every time we fail to\n     * fit the string in the current buffer size. */\n    while(1) {\n        buf[buflen-2] = '\\0';\n        va_copy(cpy,ap);\n        vsnprintf(buf, buflen, fmt, cpy);\n        va_end(cpy);\n        if (buf[buflen-2] != '\\0') {\n            if (buf != staticbuf) s_free(buf);\n            buflen *= 2;\n            buf = s_malloc(buflen);\n            if (buf == NULL) return NULL;\n            continue;\n        }\n        break;\n    }\n\n    /* Finally concat the obtained string to the SDS string and return it. */\n    t = sdscat(s, buf);\n    if (buf != staticbuf) s_free(buf);\n    return t;\n}\n\n/* Append to the sds string 's' a string obtained using printf-alike format\n * specifier.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call.\n *\n * Example:\n *\n * s = sdsnew(\"Sum is: \");\n * s = sdscatprintf(s,\"%d+%d = %d\",a,b,a+b).\n *\n * Often you need to create a string from scratch with the printf-alike\n * format. When this is the need, just use sdsempty() as the target string:\n *\n * s = sdscatprintf(sdsempty(), \"... your format ...\", args);\n */\nsds sdscatprintf(sds s, const char *fmt, ...) {\n    va_list ap;\n    char *t;\n    va_start(ap, fmt);\n    t = sdscatvprintf(s,fmt,ap);\n    va_end(ap);\n    return t;\n}\n\n/* This function is similar to sdscatprintf, but much faster as it does\n * not rely on sprintf() family functions implemented by the libc that\n * are often very slow. Moreover directly handling the sds string as\n * new data is concatenated provides a performance improvement.\n *\n * However this function only handles an incompatible subset of printf-alike\n * format specifiers:\n *\n * %s - C String\n * %S - SDS string\n * %i - signed int\n * %I - 64 bit signed integer (long long, int64_t)\n * %u - unsigned int\n * %U - 64 bit unsigned integer (unsigned long long, uint64_t)\n * %% - Verbatim \"%\" character.\n */\nsds sdscatfmt(sds s, char const *fmt, ...) {\n    const char *f = fmt;\n    int i;\n    va_list ap;\n\n    va_start(ap,fmt);\n    i = sdslen(s); /* Position of the next byte to write to dest str. */\n    while(*f) {\n        char next, *str;\n        size_t l;\n        long long num;\n        unsigned long long unum;\n\n        /* Make sure there is always space for at least 1 char. */\n        if (sdsavail(s)==0) {\n            s = sdsMakeRoomFor(s,1);\n            if (s == NULL) goto fmt_error;\n        }\n\n        switch(*f) {\n        case '%':\n            next = *(f+1);\n            f++;\n            switch(next) {\n            case 's':\n            case 'S':\n                str = va_arg(ap,char*);\n                l = (next == 's') ? strlen(str) : sdslen(str);\n                if (sdsavail(s) < l) {\n                    s = sdsMakeRoomFor(s,l);\n                    if (s == NULL) goto fmt_error;\n                }\n                memcpy(s+i,str,l);\n                sdsinclen(s,l);\n                i += l;\n                break;\n            case 'i':\n            case 'I':\n                if (next == 'i')\n                    num = va_arg(ap,int);\n                else\n                    num = va_arg(ap,long long);\n                {\n                    char buf[SDS_LLSTR_SIZE];\n                    l = sdsll2str(buf,num);\n                    if (sdsavail(s) < l) {\n                        s = sdsMakeRoomFor(s,l);\n                        if (s == NULL) goto fmt_error;\n                    }\n                    memcpy(s+i,buf,l);\n                    sdsinclen(s,l);\n                    i += l;\n                }\n                break;\n            case 'u':\n            case 'U':\n                if (next == 'u')\n                    unum = va_arg(ap,unsigned int);\n                else\n                    unum = va_arg(ap,unsigned long long);\n                {\n                    char buf[SDS_LLSTR_SIZE];\n                    l = sdsull2str(buf,unum);\n                    if (sdsavail(s) < l) {\n                        s = sdsMakeRoomFor(s,l);\n                        if (s == NULL) goto fmt_error;\n                    }\n                    memcpy(s+i,buf,l);\n                    sdsinclen(s,l);\n                    i += l;\n                }\n                break;\n            default: /* Handle %% and generally %<unknown>. */\n                s[i++] = next;\n                sdsinclen(s,1);\n                break;\n            }\n            break;\n        default:\n            s[i++] = *f;\n            sdsinclen(s,1);\n            break;\n        }\n        f++;\n    }\n    va_end(ap);\n\n    /* Add null-term */\n    s[i] = '\\0';\n    return s;\n\nfmt_error:\n    va_end(ap);\n    return NULL;\n}\n\n/* Remove the part of the string from left and from right composed just of\n * contiguous characters found in 'cset', that is a null terminted C string.\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call.\n *\n * Example:\n *\n * s = sdsnew(\"AA...AA.a.aa.aHelloWorld     :::\");\n * s = sdstrim(s,\"Aa. :\");\n * printf(\"%s\\n\", s);\n *\n * Output will be just \"Hello World\".\n */\nsds sdstrim(sds s, const char *cset) {\n    char *start, *end, *sp, *ep;\n    size_t len;\n\n    sp = start = s;\n    ep = end = s+sdslen(s)-1;\n    while(sp <= end && strchr(cset, *sp)) sp++;\n    while(ep > sp && strchr(cset, *ep)) ep--;\n    len = (sp > ep) ? 0 : ((ep-sp)+1);\n    if (s != sp) memmove(s, sp, len);\n    s[len] = '\\0';\n    sdssetlen(s,len);\n    return s;\n}\n\n/* Turn the string into a smaller (or equal) string containing only the\n * substring specified by the 'start' and 'end' indexes.\n *\n * start and end can be negative, where -1 means the last character of the\n * string, -2 the penultimate character, and so forth.\n *\n * The interval is inclusive, so the start and end characters will be part\n * of the resulting string.\n *\n * The string is modified in-place.\n *\n * Return value:\n * -1 (error) if sdslen(s) is larger than maximum positive ssize_t value.\n *  0 on success.\n *\n * Example:\n *\n * s = sdsnew(\"Hello World\");\n * sdsrange(s,1,-1); => \"ello World\"\n */\nint sdsrange(sds s, ssize_t start, ssize_t end) {\n    size_t newlen, len = sdslen(s);\n    if (len > SSIZE_MAX) return -1;\n\n    if (len == 0) return 0;\n    if (start < 0) {\n        start = len+start;\n        if (start < 0) start = 0;\n    }\n    if (end < 0) {\n        end = len+end;\n        if (end < 0) end = 0;\n    }\n    newlen = (start > end) ? 0 : (end-start)+1;\n    if (newlen != 0) {\n        if (start >= (ssize_t)len) {\n            newlen = 0;\n        } else if (end >= (ssize_t)len) {\n            end = len-1;\n            newlen = (start > end) ? 0 : (end-start)+1;\n        }\n    } else {\n        start = 0;\n    }\n    if (start && newlen) memmove(s, s+start, newlen);\n    s[newlen] = 0;\n    sdssetlen(s,newlen);\n    return 0;\n}\n\n/* Apply tolower() to every character of the sds string 's'. */\nvoid sdstolower(sds s) {\n    int len = sdslen(s), j;\n\n    for (j = 0; j < len; j++) s[j] = tolower(s[j]);\n}\n\n/* Apply toupper() to every character of the sds string 's'. */\nvoid sdstoupper(sds s) {\n    int len = sdslen(s), j;\n\n    for (j = 0; j < len; j++) s[j] = toupper(s[j]);\n}\n\n/* Compare two sds strings s1 and s2 with memcmp().\n *\n * Return value:\n *\n *     positive if s1 > s2.\n *     negative if s1 < s2.\n *     0 if s1 and s2 are exactly the same binary string.\n *\n * If two strings share exactly the same prefix, but one of the two has\n * additional characters, the longer string is considered to be greater than\n * the smaller one. */\nint sdscmp(const sds s1, const sds s2) {\n    size_t l1, l2, minlen;\n    int cmp;\n\n    l1 = sdslen(s1);\n    l2 = sdslen(s2);\n    minlen = (l1 < l2) ? l1 : l2;\n    cmp = memcmp(s1,s2,minlen);\n    if (cmp == 0) return l1-l2;\n    return cmp;\n}\n\n/* Split 's' with separator in 'sep'. An array\n * of sds strings is returned. *count will be set\n * by reference to the number of tokens returned.\n *\n * On out of memory, zero length string, zero length\n * separator, NULL is returned.\n *\n * Note that 'sep' is able to split a string using\n * a multi-character separator. For example\n * sdssplit(\"foo_-_bar\",\"_-_\"); will return two\n * elements \"foo\" and \"bar\".\n *\n * This version of the function is binary-safe but\n * requires length arguments. sdssplit() is just the\n * same function but for zero-terminated strings.\n */\nsds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) {\n    int elements = 0, slots = 5, start = 0, j;\n    sds *tokens;\n\n    if (seplen < 1 || len < 0) return NULL;\n\n    tokens = s_malloc(sizeof(sds)*slots);\n    if (tokens == NULL) return NULL;\n\n    if (len == 0) {\n        *count = 0;\n        return tokens;\n    }\n    for (j = 0; j < (len-(seplen-1)); j++) {\n        /* make sure there is room for the next element and the final one */\n        if (slots < elements+2) {\n            sds *newtokens;\n\n            slots *= 2;\n            newtokens = s_realloc(tokens,sizeof(sds)*slots);\n            if (newtokens == NULL) goto cleanup;\n            tokens = newtokens;\n        }\n        /* search the separator */\n        if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {\n            tokens[elements] = sdsnewlen(s+start,j-start);\n            if (tokens[elements] == NULL) goto cleanup;\n            elements++;\n            start = j+seplen;\n            j = j+seplen-1; /* skip the separator */\n        }\n    }\n    /* Add the final element. We are sure there is room in the tokens array. */\n    tokens[elements] = sdsnewlen(s+start,len-start);\n    if (tokens[elements] == NULL) goto cleanup;\n    elements++;\n    *count = elements;\n    return tokens;\n\ncleanup:\n    {\n        int i;\n        for (i = 0; i < elements; i++) sdsfree(tokens[i]);\n        s_free(tokens);\n        *count = 0;\n        return NULL;\n    }\n}\n\n/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */\nvoid sdsfreesplitres(sds *tokens, int count) {\n    if (!tokens) return;\n    while(count--)\n        sdsfree(tokens[count]);\n    s_free(tokens);\n}\n\n/* Append to the sds string \"s\" an escaped string representation where\n * all the non-printable characters (tested with isprint()) are turned into\n * escapes in the form \"\\n\\r\\a....\" or \"\\x<hex-number>\".\n *\n * After the call, the modified sds string is no longer valid and all the\n * references must be substituted with the new pointer returned by the call. */\nsds sdscatrepr(sds s, const char *p, size_t len) {\n    s = sdscatlen(s,\"\\\"\",1);\n    while(len--) {\n        switch(*p) {\n        case '\\\\':\n        case '\"':\n            s = sdscatprintf(s,\"\\\\%c\",*p);\n            break;\n        case '\\n': s = sdscatlen(s,\"\\\\n\",2); break;\n        case '\\r': s = sdscatlen(s,\"\\\\r\",2); break;\n        case '\\t': s = sdscatlen(s,\"\\\\t\",2); break;\n        case '\\a': s = sdscatlen(s,\"\\\\a\",2); break;\n        case '\\b': s = sdscatlen(s,\"\\\\b\",2); break;\n        default:\n            if (isprint(*p))\n                s = sdscatprintf(s,\"%c\",*p);\n            else\n                s = sdscatprintf(s,\"\\\\x%02x\",(unsigned char)*p);\n            break;\n        }\n        p++;\n    }\n    return sdscatlen(s,\"\\\"\",1);\n}\n\n/* Helper function for sdssplitargs() that converts a hex digit into an\n * integer from 0 to 15 */\nint hex_digit_to_int(char c) {\n    switch(c) {\n    case '0': return 0;\n    case '1': return 1;\n    case '2': return 2;\n    case '3': return 3;\n    case '4': return 4;\n    case '5': return 5;\n    case '6': return 6;\n    case '7': return 7;\n    case '8': return 8;\n    case '9': return 9;\n    case 'a': case 'A': return 10;\n    case 'b': case 'B': return 11;\n    case 'c': case 'C': return 12;\n    case 'd': case 'D': return 13;\n    case 'e': case 'E': return 14;\n    case 'f': case 'F': return 15;\n    default: return 0;\n    }\n}\n\n/* Split a line into arguments, where every argument can be in the\n * following programming-language REPL-alike form:\n *\n * foo bar \"newline are supported\\n\" and \"\\xff\\x00otherstuff\"\n *\n * The number of arguments is stored into *argc, and an array\n * of sds is returned.\n *\n * The caller should free the resulting array of sds strings with\n * sdsfreesplitres().\n *\n * Note that sdscatrepr() is able to convert back a string into\n * a quoted string in the same format sdssplitargs() is able to parse.\n *\n * The function returns the allocated tokens on success, even when the\n * input string is empty, or NULL if the input contains unbalanced\n * quotes or closed quotes followed by non space characters\n * as in: \"foo\"bar or \"foo'\n */\nsds *sdssplitargs(const char *line, int *argc) {\n    const char *p = line;\n    char *current = NULL;\n    char **vector = NULL;\n\n    *argc = 0;\n    while(1) {\n        /* skip blanks */\n        while(*p && isspace(*p)) p++;\n        if (*p) {\n            /* get a token */\n            int inq=0;  /* set to 1 if we are in \"quotes\" */\n            int insq=0; /* set to 1 if we are in 'single quotes' */\n            int done=0;\n\n            if (current == NULL) current = sdsempty();\n            while(!done) {\n                if (inq) {\n                    if (*p == '\\\\' && *(p+1) == 'x' &&\n                                             isxdigit(*(p+2)) &&\n                                             isxdigit(*(p+3)))\n                    {\n                        unsigned char byte;\n\n                        byte = (hex_digit_to_int(*(p+2))*16)+\n                                hex_digit_to_int(*(p+3));\n                        current = sdscatlen(current,(char*)&byte,1);\n                        p += 3;\n                    } else if (*p == '\\\\' && *(p+1)) {\n                        char c;\n\n                        p++;\n                        switch(*p) {\n                        case 'n': c = '\\n'; break;\n                        case 'r': c = '\\r'; break;\n                        case 't': c = '\\t'; break;\n                        case 'b': c = '\\b'; break;\n                        case 'a': c = '\\a'; break;\n                        default: c = *p; break;\n                        }\n                        current = sdscatlen(current,&c,1);\n                    } else if (*p == '\"') {\n                        /* closing quote must be followed by a space or\n                         * nothing at all. */\n                        if (*(p+1) && !isspace(*(p+1))) goto err;\n                        done=1;\n                    } else if (!*p) {\n                        /* unterminated quotes */\n                        goto err;\n                    } else {\n                        current = sdscatlen(current,p,1);\n                    }\n                } else if (insq) {\n                    if (*p == '\\\\' && *(p+1) == '\\'') {\n                        p++;\n                        current = sdscatlen(current,\"'\",1);\n                    } else if (*p == '\\'') {\n                        /* closing quote must be followed by a space or\n                         * nothing at all. */\n                        if (*(p+1) && !isspace(*(p+1))) goto err;\n                        done=1;\n                    } else if (!*p) {\n                        /* unterminated quotes */\n                        goto err;\n                    } else {\n                        current = sdscatlen(current,p,1);\n                    }\n                } else {\n                    switch(*p) {\n                    case ' ':\n                    case '\\n':\n                    case '\\r':\n                    case '\\t':\n                    case '\\0':\n                        done=1;\n                        break;\n                    case '\"':\n                        inq=1;\n                        break;\n                    case '\\'':\n                        insq=1;\n                        break;\n                    default:\n                        current = sdscatlen(current,p,1);\n                        break;\n                    }\n                }\n                if (*p) p++;\n            }\n            /* add the token to the vector */\n            {\n                char **new_vector = s_realloc(vector,((*argc)+1)*sizeof(char*));\n                if (new_vector == NULL) {\n                    s_free(vector);\n                    return NULL;\n                }\n\n                vector = new_vector;\n                vector[*argc] = current;\n                (*argc)++;\n                current = NULL;\n            }\n        } else {\n            /* Even on empty input string return something not NULL. */\n            if (vector == NULL) vector = s_malloc(sizeof(void*));\n            return vector;\n        }\n    }\n\nerr:\n    while((*argc)--)\n        sdsfree(vector[*argc]);\n    s_free(vector);\n    if (current) sdsfree(current);\n    *argc = 0;\n    return NULL;\n}\n\n/* Modify the string substituting all the occurrences of the set of\n * characters specified in the 'from' string to the corresponding character\n * in the 'to' array.\n *\n * For instance: sdsmapchars(mystring, \"ho\", \"01\", 2)\n * will have the effect of turning the string \"hello\" into \"0ell1\".\n *\n * The function returns the sds string pointer, that is always the same\n * as the input pointer since no resize is needed. */\nsds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {\n    size_t j, i, l = sdslen(s);\n\n    for (j = 0; j < l; j++) {\n        for (i = 0; i < setlen; i++) {\n            if (s[j] == from[i]) {\n                s[j] = to[i];\n                break;\n            }\n        }\n    }\n    return s;\n}\n\n/* Join an array of C strings using the specified separator (also a C string).\n * Returns the result as an sds string. */\nsds sdsjoin(char **argv, int argc, char *sep) {\n    sds join = sdsempty();\n    int j;\n\n    for (j = 0; j < argc; j++) {\n        join = sdscat(join, argv[j]);\n        if (j != argc-1) join = sdscat(join,sep);\n    }\n    return join;\n}\n\n/* Like sdsjoin, but joins an array of SDS strings. */\nsds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {\n    sds join = sdsempty();\n    int j;\n\n    for (j = 0; j < argc; j++) {\n        join = sdscatsds(join, argv[j]);\n        if (j != argc-1) join = sdscatlen(join,sep,seplen);\n    }\n    return join;\n}\n\n/* Wrappers to the allocators used by SDS. Note that SDS will actually\n * just use the macros defined into sdsalloc.h in order to avoid to pay\n * the overhead of function calls. Here we define these wrappers only for\n * the programs SDS is linked to, if they want to touch the SDS internals\n * even if they use a different allocator. */\nvoid *sds_malloc(size_t size) { return s_malloc(size); }\nvoid *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); }\nvoid sds_free(void *ptr) { s_free(ptr); }\n\n#if defined(SDS_TEST_MAIN)\n#include <stdio.h>\n#include \"testhelp.h\"\n#include \"limits.h\"\n\n#define UNUSED(x) (void)(x)\nint sdsTest(void) {\n    {\n        sds x = sdsnew(\"foo\"), y;\n\n        test_cond(\"Create a string and obtain the length\",\n            sdslen(x) == 3 && memcmp(x,\"foo\\0\",4) == 0)\n\n        sdsfree(x);\n        x = sdsnewlen(\"foo\",2);\n        test_cond(\"Create a string with specified length\",\n            sdslen(x) == 2 && memcmp(x,\"fo\\0\",3) == 0)\n\n        x = sdscat(x,\"bar\");\n        test_cond(\"Strings concatenation\",\n            sdslen(x) == 5 && memcmp(x,\"fobar\\0\",6) == 0);\n\n        x = sdscpy(x,\"a\");\n        test_cond(\"sdscpy() against an originally longer string\",\n            sdslen(x) == 1 && memcmp(x,\"a\\0\",2) == 0)\n\n        x = sdscpy(x,\"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\");\n        test_cond(\"sdscpy() against an originally shorter string\",\n            sdslen(x) == 33 &&\n            memcmp(x,\"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\\0\",33) == 0)\n\n        sdsfree(x);\n        x = sdscatprintf(sdsempty(),\"%d\",123);\n        test_cond(\"sdscatprintf() seems working in the base case\",\n            sdslen(x) == 3 && memcmp(x,\"123\\0\",4) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\"--\");\n        x = sdscatfmt(x, \"Hello %s World %I,%I--\", \"Hi!\", LLONG_MIN,LLONG_MAX);\n        test_cond(\"sdscatfmt() seems working in the base case\",\n            sdslen(x) == 60 &&\n            memcmp(x,\"--Hello Hi! World -9223372036854775808,\"\n                     \"9223372036854775807--\",60) == 0)\n        printf(\"[%s]\\n\",x);\n\n        sdsfree(x);\n        x = sdsnew(\"--\");\n        x = sdscatfmt(x, \"%u,%U--\", UINT_MAX, ULLONG_MAX);\n        test_cond(\"sdscatfmt() seems working with unsigned numbers\",\n            sdslen(x) == 35 &&\n            memcmp(x,\"--4294967295,18446744073709551615--\",35) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\" x \");\n        sdstrim(x,\" x\");\n        test_cond(\"sdstrim() works when all chars match\",\n            sdslen(x) == 0)\n\n        sdsfree(x);\n        x = sdsnew(\" x \");\n        sdstrim(x,\" \");\n        test_cond(\"sdstrim() works when a single char remains\",\n            sdslen(x) == 1 && x[0] == 'x')\n\n        sdsfree(x);\n        x = sdsnew(\"xxciaoyyy\");\n        sdstrim(x,\"xy\");\n        test_cond(\"sdstrim() correctly trims characters\",\n            sdslen(x) == 4 && memcmp(x,\"ciao\\0\",5) == 0)\n\n        y = sdsdup(x);\n        sdsrange(y,1,1);\n        test_cond(\"sdsrange(...,1,1)\",\n            sdslen(y) == 1 && memcmp(y,\"i\\0\",2) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,1,-1);\n        test_cond(\"sdsrange(...,1,-1)\",\n            sdslen(y) == 3 && memcmp(y,\"iao\\0\",4) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,-2,-1);\n        test_cond(\"sdsrange(...,-2,-1)\",\n            sdslen(y) == 2 && memcmp(y,\"ao\\0\",3) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,2,1);\n        test_cond(\"sdsrange(...,2,1)\",\n            sdslen(y) == 0 && memcmp(y,\"\\0\",1) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,1,100);\n        test_cond(\"sdsrange(...,1,100)\",\n            sdslen(y) == 3 && memcmp(y,\"iao\\0\",4) == 0)\n\n        sdsfree(y);\n        y = sdsdup(x);\n        sdsrange(y,100,100);\n        test_cond(\"sdsrange(...,100,100)\",\n            sdslen(y) == 0 && memcmp(y,\"\\0\",1) == 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"foo\");\n        y = sdsnew(\"foa\");\n        test_cond(\"sdscmp(foo,foa)\", sdscmp(x,y) > 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"bar\");\n        y = sdsnew(\"bar\");\n        test_cond(\"sdscmp(bar,bar)\", sdscmp(x,y) == 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnew(\"aar\");\n        y = sdsnew(\"bar\");\n        test_cond(\"sdscmp(bar,bar)\", sdscmp(x,y) < 0)\n\n        sdsfree(y);\n        sdsfree(x);\n        x = sdsnewlen(\"\\a\\n\\0foo\\r\",7);\n        y = sdscatrepr(sdsempty(),x,sdslen(x));\n        test_cond(\"sdscatrepr(...data...)\",\n            memcmp(y,\"\\\"\\\\a\\\\n\\\\x00foo\\\\r\\\"\",15) == 0)\n\n        {\n            unsigned int oldfree;\n            char *p;\n            int step = 10, j, i;\n\n            sdsfree(x);\n            sdsfree(y);\n            x = sdsnew(\"0\");\n            test_cond(\"sdsnew() free/len buffers\", sdslen(x) == 1 && sdsavail(x) == 0);\n\n            /* Run the test a few times in order to hit the first two\n             * SDS header types. */\n            for (i = 0; i < 10; i++) {\n                int oldlen = sdslen(x);\n                x = sdsMakeRoomFor(x,step);\n                int type = x[-1]&SDS_TYPE_MASK;\n\n                test_cond(\"sdsMakeRoomFor() len\", sdslen(x) == oldlen);\n                if (type != SDS_TYPE_5) {\n                    test_cond(\"sdsMakeRoomFor() free\", sdsavail(x) >= step);\n                    oldfree = sdsavail(x);\n                }\n                p = x+oldlen;\n                for (j = 0; j < step; j++) {\n                    p[j] = 'A'+j;\n                }\n                sdsIncrLen(x,step);\n            }\n            test_cond(\"sdsMakeRoomFor() content\",\n                memcmp(\"0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ\",x,101) == 0);\n            test_cond(\"sdsMakeRoomFor() final length\",sdslen(x)==101);\n\n            sdsfree(x);\n        }\n    }\n    test_report()\n    return 0;\n}\n#endif\n\n#ifdef SDS_TEST_MAIN\nint main(void) {\n    return sdsTest();\n}\n#endif\n"
  },
  {
    "path": "thirdparty/hiredis/sds.h",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Oran Agra\n * Copyright (c) 2015, Redis Labs, 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 are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * 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 *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND 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 COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef __SDS_H\n#define __SDS_H\n\n#define SDS_MAX_PREALLOC (1024*1024)\n#ifdef _MSC_VER\n#define __attribute__(x)\ntypedef long long ssize_t;\n#define SSIZE_MAX (LLONG_MAX >> 1)\n#endif\n\n#include <sys/types.h>\n#include <stdarg.h>\n#include <stdint.h>\n\ntypedef char *sds;\n\n/* Note: sdshdr5 is never used, we just access the flags byte directly.\n * However is here to document the layout of type 5 SDS strings. */\nstruct __attribute__ ((__packed__)) sdshdr5 {\n    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr8 {\n    uint8_t len; /* used */\n    uint8_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr16 {\n    uint16_t len; /* used */\n    uint16_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr32 {\n    uint32_t len; /* used */\n    uint32_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\nstruct __attribute__ ((__packed__)) sdshdr64 {\n    uint64_t len; /* used */\n    uint64_t alloc; /* excluding the header and null terminator */\n    unsigned char flags; /* 3 lsb of type, 5 unused bits */\n    char buf[];\n};\n\n#define SDS_TYPE_5  0\n#define SDS_TYPE_8  1\n#define SDS_TYPE_16 2\n#define SDS_TYPE_32 3\n#define SDS_TYPE_64 4\n#define SDS_TYPE_MASK 7\n#define SDS_TYPE_BITS 3\n#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)));\n#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))\n#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)\n\nstatic inline size_t sdslen(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return SDS_TYPE_5_LEN(flags);\n        case SDS_TYPE_8:\n            return SDS_HDR(8,s)->len;\n        case SDS_TYPE_16:\n            return SDS_HDR(16,s)->len;\n        case SDS_TYPE_32:\n            return SDS_HDR(32,s)->len;\n        case SDS_TYPE_64:\n            return SDS_HDR(64,s)->len;\n    }\n    return 0;\n}\n\nstatic inline size_t sdsavail(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5: {\n            return 0;\n        }\n        case SDS_TYPE_8: {\n            SDS_HDR_VAR(8,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_16: {\n            SDS_HDR_VAR(16,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_32: {\n            SDS_HDR_VAR(32,s);\n            return sh->alloc - sh->len;\n        }\n        case SDS_TYPE_64: {\n            SDS_HDR_VAR(64,s);\n            return sh->alloc - sh->len;\n        }\n    }\n    return 0;\n}\n\nstatic inline void sdssetlen(sds s, size_t newlen) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            {\n                unsigned char *fp = ((unsigned char*)s)-1;\n                *fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS));\n            }\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->len = (uint8_t)newlen;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->len = (uint16_t)newlen;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->len = (uint32_t)newlen;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->len = (uint64_t)newlen;\n            break;\n    }\n}\n\nstatic inline void sdsinclen(sds s, size_t inc) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            {\n                unsigned char *fp = ((unsigned char*)s)-1;\n                unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc;\n                *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);\n            }\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->len += (uint8_t)inc;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->len += (uint16_t)inc;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->len += (uint32_t)inc;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->len += (uint64_t)inc;\n            break;\n    }\n}\n\n/* sdsalloc() = sdsavail() + sdslen() */\nstatic inline size_t sdsalloc(const sds s) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            return SDS_TYPE_5_LEN(flags);\n        case SDS_TYPE_8:\n            return SDS_HDR(8,s)->alloc;\n        case SDS_TYPE_16:\n            return SDS_HDR(16,s)->alloc;\n        case SDS_TYPE_32:\n            return SDS_HDR(32,s)->alloc;\n        case SDS_TYPE_64:\n            return SDS_HDR(64,s)->alloc;\n    }\n    return 0;\n}\n\nstatic inline void sdssetalloc(sds s, size_t newlen) {\n    unsigned char flags = s[-1];\n    switch(flags&SDS_TYPE_MASK) {\n        case SDS_TYPE_5:\n            /* Nothing to do, this type has no total allocation info. */\n            break;\n        case SDS_TYPE_8:\n            SDS_HDR(8,s)->alloc = (uint8_t)newlen;\n            break;\n        case SDS_TYPE_16:\n            SDS_HDR(16,s)->alloc = (uint16_t)newlen;\n            break;\n        case SDS_TYPE_32:\n            SDS_HDR(32,s)->alloc = (uint32_t)newlen;\n            break;\n        case SDS_TYPE_64:\n            SDS_HDR(64,s)->alloc = (uint64_t)newlen;\n            break;\n    }\n}\n\nsds sdsnewlen(const void *init, size_t initlen);\nsds sdsnew(const char *init);\nsds sdsempty(void);\nsds sdsdup(const sds s);\nvoid sdsfree(sds s);\nsds sdsgrowzero(sds s, size_t len);\nsds sdscatlen(sds s, const void *t, size_t len);\nsds sdscat(sds s, const char *t);\nsds sdscatsds(sds s, const sds t);\nsds sdscpylen(sds s, const char *t, size_t len);\nsds sdscpy(sds s, const char *t);\n\nsds sdscatvprintf(sds s, const char *fmt, va_list ap);\n#ifdef __GNUC__\nsds sdscatprintf(sds s, const char *fmt, ...)\n    __attribute__((format(printf, 2, 3)));\n#else\nsds sdscatprintf(sds s, const char *fmt, ...);\n#endif\n\nsds sdscatfmt(sds s, char const *fmt, ...);\nsds sdstrim(sds s, const char *cset);\nint sdsrange(sds s, ssize_t start, ssize_t end);\nvoid sdsupdatelen(sds s);\nvoid sdsclear(sds s);\nint sdscmp(const sds s1, const sds s2);\nsds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);\nvoid sdsfreesplitres(sds *tokens, int count);\nvoid sdstolower(sds s);\nvoid sdstoupper(sds s);\nsds sdsfromlonglong(long long value);\nsds sdscatrepr(sds s, const char *p, size_t len);\nsds *sdssplitargs(const char *line, int *argc);\nsds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);\nsds sdsjoin(char **argv, int argc, char *sep);\nsds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);\n\n/* Low level functions exposed to the user API */\nsds sdsMakeRoomFor(sds s, size_t addlen);\nvoid sdsIncrLen(sds s, int incr);\nsds sdsRemoveFreeSpace(sds s);\nsize_t sdsAllocSize(sds s);\nvoid *sdsAllocPtr(sds s);\n\n/* Export the allocator used by SDS to the program using SDS.\n * Sometimes the program SDS is linked to, may use a different set of\n * allocators, but may want to allocate or free things that SDS will\n * respectively free or allocate. */\nvoid *sds_malloc(size_t size);\nvoid *sds_realloc(void *ptr, size_t size);\nvoid sds_free(void *ptr);\n\n#ifdef REDIS_TEST\nint sdsTest(int argc, char *argv[]);\n#endif\n\n#endif\n"
  },
  {
    "path": "thirdparty/hiredis/sdsalloc.h",
    "content": "/* SDSLib 2.0 -- A C dynamic strings library\n *\n * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>\n * Copyright (c) 2015, Oran Agra\n * Copyright (c) 2015, Redis Labs, 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 are met:\n *\n *   * Redistributions of source code must retain the above copyright notice,\n *     this list of conditions and the following disclaimer.\n *   * 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 *   * Neither the name of Redis nor the names of its contributors may be used\n *     to endorse or promote products derived from this software without\n *     specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND 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 COPYRIGHT OWNER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* SDS allocator selection.\n *\n * This file is used in order to change the SDS allocator at compile time.\n * Just define the following defines to what you want to use. Also add\n * the include of your alternate allocator if needed (not needed in order\n * to use the default libc allocator). */\n\n#include \"alloc.h\"\n\n#define s_malloc hi_malloc\n#define s_realloc hi_realloc\n#define s_free hi_free\n"
  },
  {
    "path": "thirdparty/llhttp/LICENSE",
    "content": "This software is licensed under the MIT License.\n\nCopyright Fedor Indutny, 2018.\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to permit\npersons to whom the Software is furnished to do so, subject to the\nfollowing conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\nNO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\nDAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\nUSE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "thirdparty/llhttp/LICENSE-MIT",
    "content": "This software is licensed under the MIT License.\n\nCopyright Fedor Indutny, 2018.\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to permit\npersons to whom the Software is furnished to do so, subject to the\nfollowing conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\nNO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\nDAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\nUSE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "thirdparty/llhttp/api.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"llhttp.h\"\n\n#define CALLBACK_MAYBE(PARSER, NAME)                                          \\\n  do {                                                                        \\\n    const llhttp_settings_t* settings;                                        \\\n    settings = (const llhttp_settings_t*) (PARSER)->settings;                 \\\n    if (settings == NULL || settings->NAME == NULL) {                         \\\n      err = 0;                                                                \\\n      break;                                                                  \\\n    }                                                                         \\\n    err = settings->NAME((PARSER));                                           \\\n  } while (0)\n\n#define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN)                         \\\n  do {                                                                        \\\n    const llhttp_settings_t* settings;                                        \\\n    settings = (const llhttp_settings_t*) (PARSER)->settings;                 \\\n    if (settings == NULL || settings->NAME == NULL) {                         \\\n      err = 0;                                                                \\\n      break;                                                                  \\\n    }                                                                         \\\n    err = settings->NAME((PARSER), (START), (LEN));                           \\\n    if (err == -1) {                                                          \\\n      err = HPE_USER;                                                         \\\n      llhttp_set_error_reason((PARSER), \"Span callback error in \" #NAME);     \\\n    }                                                                         \\\n  } while (0)\n\nvoid llhttp_init(llhttp_t* parser, llhttp_type_t type,\n                 const llhttp_settings_t* settings) {\n  llhttp__internal_init(parser);\n\n  parser->type = type;\n  parser->settings = (void*) settings;\n}\n\n\n#if defined(__wasm__)\n\nextern int wasm_on_message_begin(llhttp_t * p);\nextern int wasm_on_url(llhttp_t* p, const char* at, size_t length);\nextern int wasm_on_status(llhttp_t* p, const char* at, size_t length);\nextern int wasm_on_header_field(llhttp_t* p, const char* at, size_t length);\nextern int wasm_on_header_value(llhttp_t* p, const char* at, size_t length);\nextern int wasm_on_headers_complete(llhttp_t * p, int status_code,\n                                    uint8_t upgrade, int should_keep_alive);\nextern int wasm_on_body(llhttp_t* p, const char* at, size_t length);\nextern int wasm_on_message_complete(llhttp_t * p);\n\nstatic int wasm_on_headers_complete_wrap(llhttp_t* p) {\n  return wasm_on_headers_complete(p, p->status_code, p->upgrade,\n                                  llhttp_should_keep_alive(p));\n}\n\nconst llhttp_settings_t wasm_settings = {\n  .on_message_begin = wasm_on_message_begin,\n  .on_url = wasm_on_url,\n  .on_status = wasm_on_status,\n  .on_header_field = wasm_on_header_field,\n  .on_header_value = wasm_on_header_value,\n  .on_headers_complete = wasm_on_headers_complete_wrap,\n  .on_body = wasm_on_body,\n  .on_message_complete = wasm_on_message_complete,\n};\n\n\nllhttp_t* llhttp_alloc(llhttp_type_t type) {\n  llhttp_t* parser = malloc(sizeof(llhttp_t));\n  llhttp_init(parser, type, &wasm_settings);\n  return parser;\n}\n\nvoid llhttp_free(llhttp_t* parser) {\n  free(parser);\n}\n\n#endif  // defined(__wasm__)\n\n/* Some getters required to get stuff from the parser */\n\nuint8_t llhttp_get_type(llhttp_t* parser) {\n  return parser->type;\n}\n\nuint8_t llhttp_get_http_major(llhttp_t* parser) {\n  return parser->http_major;\n}\n\nuint8_t llhttp_get_http_minor(llhttp_t* parser) {\n  return parser->http_minor;\n}\n\nuint8_t llhttp_get_method(llhttp_t* parser) {\n  return parser->method;\n}\n\nint llhttp_get_status_code(llhttp_t* parser) {\n  return parser->status_code;\n}\n\nuint8_t llhttp_get_upgrade(llhttp_t* parser) {\n  return parser->upgrade;\n}\n\n\nvoid llhttp_reset(llhttp_t* parser) {\n  llhttp_type_t type = parser->type;\n  const llhttp_settings_t* settings = parser->settings;\n  void* data = parser->data;\n  uint16_t lenient_flags = parser->lenient_flags;\n\n  llhttp__internal_init(parser);\n\n  parser->type = type;\n  parser->settings = (void*) settings;\n  parser->data = data;\n  parser->lenient_flags = lenient_flags;\n}\n\n\nllhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) {\n  return llhttp__internal_execute(parser, data, data + len);\n}\n\n\nvoid llhttp_settings_init(llhttp_settings_t* settings) {\n  memset(settings, 0, sizeof(*settings));\n}\n\n\nllhttp_errno_t llhttp_finish(llhttp_t* parser) {\n  int err;\n\n  /* We're in an error state. Don't bother doing anything. */\n  if (parser->error != 0) {\n    return 0;\n  }\n\n  switch (parser->finish) {\n    case HTTP_FINISH_SAFE_WITH_CB:\n      CALLBACK_MAYBE(parser, on_message_complete);\n      if (err != HPE_OK) return err;\n\n    /* FALLTHROUGH */\n    case HTTP_FINISH_SAFE:\n      return HPE_OK;\n    case HTTP_FINISH_UNSAFE:\n      parser->reason = \"Invalid EOF state\";\n      return HPE_INVALID_EOF_STATE;\n    default:\n      abort();\n  }\n}\n\n\nvoid llhttp_pause(llhttp_t* parser) {\n  if (parser->error != HPE_OK) {\n    return;\n  }\n\n  parser->error = HPE_PAUSED;\n  parser->reason = \"Paused\";\n}\n\n\nvoid llhttp_resume(llhttp_t* parser) {\n  if (parser->error != HPE_PAUSED) {\n    return;\n  }\n\n  parser->error = 0;\n}\n\n\nvoid llhttp_resume_after_upgrade(llhttp_t* parser) {\n  if (parser->error != HPE_PAUSED_UPGRADE) {\n    return;\n  }\n\n  parser->error = 0;\n}\n\n\nllhttp_errno_t llhttp_get_errno(const llhttp_t* parser) {\n  return parser->error;\n}\n\n\nconst char* llhttp_get_error_reason(const llhttp_t* parser) {\n  return parser->reason;\n}\n\n\nvoid llhttp_set_error_reason(llhttp_t* parser, const char* reason) {\n  parser->reason = reason;\n}\n\n\nconst char* llhttp_get_error_pos(const llhttp_t* parser) {\n  return parser->error_pos;\n}\n\n\nconst char* llhttp_errno_name(llhttp_errno_t err) {\n#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return \"HPE_\" #NAME;\n  switch (err) {\n    HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)\n    default: abort();\n  }\n#undef HTTP_ERRNO_GEN\n}\n\n\nconst char* llhttp_method_name(llhttp_method_t method) {\n#define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING;\n  switch (method) {\n    HTTP_ALL_METHOD_MAP(HTTP_METHOD_GEN)\n    default: abort();\n  }\n#undef HTTP_METHOD_GEN\n}\n\nconst char* llhttp_status_name(llhttp_status_t status) {\n#define HTTP_STATUS_GEN(NUM, NAME, STRING) case HTTP_STATUS_##NAME: return #STRING;\n  switch (status) {\n    HTTP_STATUS_MAP(HTTP_STATUS_GEN)\n    default: abort();\n  }\n#undef HTTP_STATUS_GEN\n}\n\n\nvoid llhttp_set_lenient_headers(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_HEADERS;\n  } else {\n    parser->lenient_flags &= ~LENIENT_HEADERS;\n  }\n}\n\n\nvoid llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_CHUNKED_LENGTH;\n  } else {\n    parser->lenient_flags &= ~LENIENT_CHUNKED_LENGTH;\n  }\n}\n\n\nvoid llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_KEEP_ALIVE;\n  } else {\n    parser->lenient_flags &= ~LENIENT_KEEP_ALIVE;\n  }\n}\n\nvoid llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_TRANSFER_ENCODING;\n  } else {\n    parser->lenient_flags &= ~LENIENT_TRANSFER_ENCODING;\n  }\n}\n\nvoid llhttp_set_lenient_version(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_VERSION;\n  } else {\n    parser->lenient_flags &= ~LENIENT_VERSION;\n  }\n}\n\nvoid llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_DATA_AFTER_CLOSE;\n  } else {\n    parser->lenient_flags &= ~LENIENT_DATA_AFTER_CLOSE;\n  }\n}\n\nvoid llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_OPTIONAL_LF_AFTER_CR;\n  } else {\n    parser->lenient_flags &= ~LENIENT_OPTIONAL_LF_AFTER_CR;\n  }\n}\n\nvoid llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;\n  } else {\n    parser->lenient_flags &= ~LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;\n  }\n}\n\nvoid llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_OPTIONAL_CR_BEFORE_LF;\n  } else {\n    parser->lenient_flags &= ~LENIENT_OPTIONAL_CR_BEFORE_LF;\n  }\n}\n\nvoid llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled) {\n  if (enabled) {\n    parser->lenient_flags |= LENIENT_SPACES_AFTER_CHUNK_SIZE;\n  } else {\n    parser->lenient_flags &= ~LENIENT_SPACES_AFTER_CHUNK_SIZE;\n  }\n}\n\n/* Callbacks */\n\n\nint llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_message_begin);\n  return err;\n}\n\n\nint llhttp__on_protocol(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_protocol, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_protocol_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_protocol_complete);\n  return err;\n}\n\n\nint llhttp__on_url(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_url, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_url_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_url_complete);\n  return err;\n}\n\n\nint llhttp__on_status(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_status, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_status_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_status_complete);\n  return err;\n}\n\n\nint llhttp__on_method(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_method, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_method_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_method_complete);\n  return err;\n}\n\n\nint llhttp__on_version(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_version, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_version_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_version_complete);\n  return err;\n}\n\n\nint llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_header_field, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_header_field_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_header_field_complete);\n  return err;\n}\n\n\nint llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_header_value, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_header_value_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_header_value_complete);\n  return err;\n}\n\n\nint llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_headers_complete);\n  return err;\n}\n\n\nint llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_message_complete);\n  return err;\n}\n\n\nint llhttp__on_body(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_body, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_chunk_header);\n  return err;\n}\n\n\nint llhttp__on_chunk_extension_name(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_chunk_extension_name, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_chunk_extension_name_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_chunk_extension_name_complete);\n  return err;\n}\n\n\nint llhttp__on_chunk_extension_value(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  SPAN_CALLBACK_MAYBE(s, on_chunk_extension_value, p, endp - p);\n  return err;\n}\n\n\nint llhttp__on_chunk_extension_value_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_chunk_extension_value_complete);\n  return err;\n}\n\n\nint llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_chunk_complete);\n  return err;\n}\n\n\nint llhttp__on_reset(llhttp_t* s, const char* p, const char* endp) {\n  int err;\n  CALLBACK_MAYBE(s, on_reset);\n  return err;\n}\n\n\n/* Private */\n\n\nvoid llhttp__debug(llhttp_t* s, const char* p, const char* endp,\n                   const char* msg) {\n  if (p == endp) {\n    fprintf(stderr, \"p=%p type=%d flags=%02x next=null debug=%s\\n\", s, s->type,\n            s->flags, msg);\n  } else {\n    fprintf(stderr, \"p=%p type=%d flags=%02x next=%02x   debug=%s\\n\", s,\n            s->type, s->flags, *p, msg);\n  }\n}\n"
  },
  {
    "path": "thirdparty/llhttp/http.c",
    "content": "#include <stdio.h>\n#ifndef LLHTTP__TEST\n# include \"llhttp.h\"\n#else\n# define llhttp_t llparse_t\n#endif  /* */\n\nint llhttp_message_needs_eof(const llhttp_t* parser);\nint llhttp_should_keep_alive(const llhttp_t* parser);\n\nint llhttp__before_headers_complete(llhttp_t* parser, const char* p,\n                                    const char* endp) {\n  /* Set this here so that on_headers_complete() callbacks can see it */\n  if ((parser->flags & F_UPGRADE) &&\n      (parser->flags & F_CONNECTION_UPGRADE)) {\n    /* For responses, \"Upgrade: foo\" and \"Connection: upgrade\" are\n     * mandatory only when it is a 101 Switching Protocols response,\n     * otherwise it is purely informational, to announce support.\n     */\n    parser->upgrade =\n        (parser->type == HTTP_REQUEST || parser->status_code == 101);\n  } else {\n    parser->upgrade = (parser->method == HTTP_CONNECT);\n  }\n  return 0;\n}\n\n\n/* Return values:\n * 0 - No body, `restart`, message_complete\n * 1 - CONNECT request, `restart`, message_complete, and pause\n * 2 - chunk_size_start\n * 3 - body_identity\n * 4 - body_identity_eof\n * 5 - invalid transfer-encoding for request\n */\nint llhttp__after_headers_complete(llhttp_t* parser, const char* p,\n                                   const char* endp) {\n  int hasBody;\n\n  hasBody = parser->flags & F_CHUNKED || parser->content_length > 0;\n  if (\n      (parser->upgrade && (parser->method == HTTP_CONNECT ||\n                          (parser->flags & F_SKIPBODY) || !hasBody)) ||\n      /* See RFC 2616 section 4.4 - 1xx e.g. Continue */\n      (parser->type == HTTP_RESPONSE && parser->status_code == 101)\n  ) {\n    /* Exit, the rest of the message is in a different protocol. */\n    return 1;\n  }\n\n  if (parser->type == HTTP_RESPONSE && parser->status_code == 100) {\n    /* No body, restart as the message is complete */\n    return 0;\n  }\n\n  /* See RFC 2616 section 4.4 */\n  if (\n    parser->flags & F_SKIPBODY ||         /* response to a HEAD request */\n    (\n      parser->type == HTTP_RESPONSE && (\n        parser->status_code == 102 ||     /* Processing */\n        parser->status_code == 103 ||     /* Early Hints */\n        parser->status_code == 204 ||     /* No Content */\n        parser->status_code == 304        /* Not Modified */\n      )\n    )\n  ) {\n    return 0;\n  } else if (parser->flags & F_CHUNKED) {\n    /* chunked encoding - ignore Content-Length header, prepare for a chunk */\n    return 2;\n  } else if (parser->flags & F_TRANSFER_ENCODING) {\n    if (parser->type == HTTP_REQUEST &&\n        (parser->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0 &&\n        (parser->lenient_flags & LENIENT_TRANSFER_ENCODING) == 0) {\n      /* RFC 7230 3.3.3 */\n\n      /* If a Transfer-Encoding header field\n       * is present in a request and the chunked transfer coding is not\n       * the final encoding, the message body length cannot be determined\n       * reliably; the server MUST respond with the 400 (Bad Request)\n       * status code and then close the connection.\n       */\n      return 5;\n    } else {\n      /* RFC 7230 3.3.3 */\n\n      /* If a Transfer-Encoding header field is present in a response and\n       * the chunked transfer coding is not the final encoding, the\n       * message body length is determined by reading the connection until\n       * it is closed by the server.\n       */\n      return 4;\n    }\n  } else {\n    if (!(parser->flags & F_CONTENT_LENGTH)) {\n      if (!llhttp_message_needs_eof(parser)) {\n        /* Assume content-length 0 - read the next */\n        return 0;\n      } else {\n        /* Read body until EOF */\n        return 4;\n      }\n    } else if (parser->content_length == 0) {\n      /* Content-Length header given but zero: Content-Length: 0\\r\\n */\n      return 0;\n    } else {\n      /* Content-Length header given and non-zero */\n      return 3;\n    }\n  }\n}\n\n\nint llhttp__after_message_complete(llhttp_t* parser, const char* p,\n                                   const char* endp) {\n  int should_keep_alive;\n\n  should_keep_alive = llhttp_should_keep_alive(parser);\n  parser->finish = HTTP_FINISH_SAFE;\n  parser->flags = 0;\n\n  /* NOTE: this is ignored in loose parsing mode */\n  return should_keep_alive;\n}\n\n\nint llhttp_message_needs_eof(const llhttp_t* parser) {\n  if (parser->type == HTTP_REQUEST) {\n    return 0;\n  }\n\n  /* See RFC 2616 section 4.4 */\n  if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */\n      parser->status_code == 204 ||     /* No Content */\n      parser->status_code == 304 ||     /* Not Modified */\n      (parser->flags & F_SKIPBODY)) {     /* response to a HEAD request */\n    return 0;\n  }\n\n  /* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */\n  if ((parser->flags & F_TRANSFER_ENCODING) &&\n      (parser->flags & F_CHUNKED) == 0) {\n    return 1;\n  }\n\n  if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) {\n    return 0;\n  }\n\n  return 1;\n}\n\n\nint llhttp_should_keep_alive(const llhttp_t* parser) {\n  if (parser->http_major > 0 && parser->http_minor > 0) {\n    /* HTTP/1.1 */\n    if (parser->flags & F_CONNECTION_CLOSE) {\n      return 0;\n    }\n  } else {\n    /* HTTP/1.0 or earlier */\n    if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {\n      return 0;\n    }\n  }\n\n  return !llhttp_message_needs_eof(parser);\n}\n"
  },
  {
    "path": "thirdparty/llhttp/llhttp.c",
    "content": "#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n\n#ifdef __SSE4_2__\n #ifdef _MSC_VER\n  #include <nmmintrin.h>\n #else  /* !_MSC_VER */\n  #include <x86intrin.h>\n #endif  /* _MSC_VER */\n#endif  /* __SSE4_2__ */\n\n#ifdef __ARM_NEON__\n #include <arm_neon.h>\n#endif  /* __ARM_NEON__ */\n\n#ifdef __wasm__\n #include <wasm_simd128.h>\n#endif  /* __wasm__ */\n\n#ifdef _MSC_VER\n #define ALIGN(n) _declspec(align(n))\n #define UNREACHABLE __assume(0)\n#else  /* !_MSC_VER */\n #define ALIGN(n) __attribute__((aligned(n)))\n #define UNREACHABLE __builtin_unreachable()\n#endif  /* _MSC_VER */\n\n#include \"llhttp.h\"\n\ntypedef int (*llhttp__internal__span_cb)(\n             llhttp__internal_t*, const char*, const char*);\n\nstatic const unsigned char llparse_blob0[] = {\n  'o', 'n'\n};\nstatic const unsigned char llparse_blob1[] = {\n  'e', 'c', 't', 'i', 'o', 'n'\n};\nstatic const unsigned char llparse_blob2[] = {\n  'l', 'o', 's', 'e'\n};\nstatic const unsigned char llparse_blob3[] = {\n  'e', 'e', 'p', '-', 'a', 'l', 'i', 'v', 'e'\n};\nstatic const unsigned char llparse_blob4[] = {\n  'p', 'g', 'r', 'a', 'd', 'e'\n};\nstatic const unsigned char llparse_blob5[] = {\n  'c', 'h', 'u', 'n', 'k', 'e', 'd'\n};\n#ifdef __SSE4_2__\nstatic const unsigned char ALIGN(16) llparse_blob6[] = {\n  0x9, 0x9, ' ', '~', 0x80, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,\n  0x0, 0x0, 0x0, 0x0, 0x0\n};\n#endif  /* __SSE4_2__ */\n#ifdef __SSE4_2__\nstatic const unsigned char ALIGN(16) llparse_blob7[] = {\n  '!', '!', '#', '\\'', '*', '+', '-', '.', '0', '9', 'A',\n  'Z', '^', 'z', '|', '|'\n};\n#endif  /* __SSE4_2__ */\n#ifdef __SSE4_2__\nstatic const unsigned char ALIGN(16) llparse_blob8[] = {\n  '~', '~', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,\n  0x0, 0x0, 0x0, 0x0, 0x0\n};\n#endif  /* __SSE4_2__ */\nstatic const unsigned char llparse_blob9[] = {\n  'e', 'n', 't', '-', 'l', 'e', 'n', 'g', 't', 'h'\n};\nstatic const unsigned char llparse_blob10[] = {\n  'r', 'o', 'x', 'y', '-', 'c', 'o', 'n', 'n', 'e', 'c',\n  't', 'i', 'o', 'n'\n};\nstatic const unsigned char llparse_blob11[] = {\n  'r', 'a', 'n', 's', 'f', 'e', 'r', '-', 'e', 'n', 'c',\n  'o', 'd', 'i', 'n', 'g'\n};\nstatic const unsigned char llparse_blob12[] = {\n  'p', 'g', 'r', 'a', 'd', 'e'\n};\nstatic const unsigned char llparse_blob13[] = {\n  'T', 'T', 'P'\n};\nstatic const unsigned char llparse_blob14[] = {\n  0xd, 0xa, 0xd, 0xa, 'S', 'M', 0xd, 0xa, 0xd, 0xa\n};\nstatic const unsigned char llparse_blob15[] = {\n  'C', 'E'\n};\nstatic const unsigned char llparse_blob16[] = {\n  'T', 'S', 'P'\n};\nstatic const unsigned char llparse_blob17[] = {\n  'N', 'O', 'U', 'N', 'C', 'E'\n};\nstatic const unsigned char llparse_blob18[] = {\n  'I', 'N', 'D'\n};\nstatic const unsigned char llparse_blob19[] = {\n  'E', 'C', 'K', 'O', 'U', 'T'\n};\nstatic const unsigned char llparse_blob20[] = {\n  'N', 'E', 'C', 'T'\n};\nstatic const unsigned char llparse_blob21[] = {\n  'E', 'T', 'E'\n};\nstatic const unsigned char llparse_blob22[] = {\n  'C', 'R', 'I', 'B', 'E'\n};\nstatic const unsigned char llparse_blob23[] = {\n  'L', 'U', 'S', 'H'\n};\nstatic const unsigned char llparse_blob24[] = {\n  'E', 'T'\n};\nstatic const unsigned char llparse_blob25[] = {\n  'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R'\n};\nstatic const unsigned char llparse_blob26[] = {\n  'E', 'A', 'D'\n};\nstatic const unsigned char llparse_blob27[] = {\n  'N', 'K'\n};\nstatic const unsigned char llparse_blob28[] = {\n  'C', 'K'\n};\nstatic const unsigned char llparse_blob29[] = {\n  'S', 'E', 'A', 'R', 'C', 'H'\n};\nstatic const unsigned char llparse_blob30[] = {\n  'R', 'G', 'E'\n};\nstatic const unsigned char llparse_blob31[] = {\n  'C', 'T', 'I', 'V', 'I', 'T', 'Y'\n};\nstatic const unsigned char llparse_blob32[] = {\n  'L', 'E', 'N', 'D', 'A', 'R'\n};\nstatic const unsigned char llparse_blob33[] = {\n  'V', 'E'\n};\nstatic const unsigned char llparse_blob34[] = {\n  'O', 'T', 'I', 'F', 'Y'\n};\nstatic const unsigned char llparse_blob35[] = {\n  'P', 'T', 'I', 'O', 'N', 'S'\n};\nstatic const unsigned char llparse_blob36[] = {\n  'C', 'H'\n};\nstatic const unsigned char llparse_blob37[] = {\n  'S', 'E'\n};\nstatic const unsigned char llparse_blob38[] = {\n  'A', 'Y'\n};\nstatic const unsigned char llparse_blob39[] = {\n  'S', 'T'\n};\nstatic const unsigned char llparse_blob40[] = {\n  'I', 'N', 'D'\n};\nstatic const unsigned char llparse_blob41[] = {\n  'A', 'T', 'C', 'H'\n};\nstatic const unsigned char llparse_blob42[] = {\n  'G', 'E'\n};\nstatic const unsigned char llparse_blob43[] = {\n  'U', 'E', 'R', 'Y'\n};\nstatic const unsigned char llparse_blob44[] = {\n  'I', 'N', 'D'\n};\nstatic const unsigned char llparse_blob45[] = {\n  'O', 'R', 'D'\n};\nstatic const unsigned char llparse_blob46[] = {\n  'I', 'R', 'E', 'C', 'T'\n};\nstatic const unsigned char llparse_blob47[] = {\n  'O', 'R', 'T'\n};\nstatic const unsigned char llparse_blob48[] = {\n  'R', 'C', 'H'\n};\nstatic const unsigned char llparse_blob49[] = {\n  'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R'\n};\nstatic const unsigned char llparse_blob50[] = {\n  'U', 'R', 'C', 'E'\n};\nstatic const unsigned char llparse_blob51[] = {\n  'B', 'S', 'C', 'R', 'I', 'B', 'E'\n};\nstatic const unsigned char llparse_blob52[] = {\n  'A', 'R', 'D', 'O', 'W', 'N'\n};\nstatic const unsigned char llparse_blob53[] = {\n  'A', 'C', 'E'\n};\nstatic const unsigned char llparse_blob54[] = {\n  'I', 'N', 'D'\n};\nstatic const unsigned char llparse_blob55[] = {\n  'N', 'K'\n};\nstatic const unsigned char llparse_blob56[] = {\n  'C', 'K'\n};\nstatic const unsigned char llparse_blob57[] = {\n  'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E'\n};\nstatic const unsigned char llparse_blob58[] = {\n  'T', 'T', 'P'\n};\nstatic const unsigned char llparse_blob59[] = {\n  'C', 'E'\n};\nstatic const unsigned char llparse_blob60[] = {\n  'T', 'S', 'P'\n};\nstatic const unsigned char llparse_blob61[] = {\n  'A', 'D'\n};\nstatic const unsigned char llparse_blob62[] = {\n  'T', 'P', '/'\n};\n\nenum llparse_match_status_e {\n  kMatchComplete,\n  kMatchPause,\n  kMatchMismatch\n};\ntypedef enum llparse_match_status_e llparse_match_status_t;\n\nstruct llparse_match_s {\n  llparse_match_status_t status;\n  const unsigned char* current;\n};\ntypedef struct llparse_match_s llparse_match_t;\n\nstatic llparse_match_t llparse__match_sequence_to_lower(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp,\n    const unsigned char* seq, uint32_t seq_len) {\n  uint32_t index;\n  llparse_match_t res;\n\n  index = s->_index;\n  for (; p != endp; p++) {\n    unsigned char current;\n\n    current = ((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p));\n    if (current == seq[index]) {\n      if (++index == seq_len) {\n        res.status = kMatchComplete;\n        goto reset;\n      }\n    } else {\n      res.status = kMatchMismatch;\n      goto reset;\n    }\n  }\n  s->_index = index;\n  res.status = kMatchPause;\n  res.current = p;\n  return res;\nreset:\n  s->_index = 0;\n  res.current = p;\n  return res;\n}\n\nstatic llparse_match_t llparse__match_sequence_to_lower_unsafe(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp,\n    const unsigned char* seq, uint32_t seq_len) {\n  uint32_t index;\n  llparse_match_t res;\n\n  index = s->_index;\n  for (; p != endp; p++) {\n    unsigned char current;\n\n    current = ((*p) | 0x20);\n    if (current == seq[index]) {\n      if (++index == seq_len) {\n        res.status = kMatchComplete;\n        goto reset;\n      }\n    } else {\n      res.status = kMatchMismatch;\n      goto reset;\n    }\n  }\n  s->_index = index;\n  res.status = kMatchPause;\n  res.current = p;\n  return res;\nreset:\n  s->_index = 0;\n  res.current = p;\n  return res;\n}\n\nstatic llparse_match_t llparse__match_sequence_id(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp,\n    const unsigned char* seq, uint32_t seq_len) {\n  uint32_t index;\n  llparse_match_t res;\n\n  index = s->_index;\n  for (; p != endp; p++) {\n    unsigned char current;\n\n    current = *p;\n    if (current == seq[index]) {\n      if (++index == seq_len) {\n        res.status = kMatchComplete;\n        goto reset;\n      }\n    } else {\n      res.status = kMatchMismatch;\n      goto reset;\n    }\n  }\n  s->_index = index;\n  res.status = kMatchPause;\n  res.current = p;\n  return res;\nreset:\n  s->_index = 0;\n  res.current = p;\n  return res;\n}\n\nenum llparse_state_e {\n  s_error,\n  s_n_llhttp__internal__n_closed,\n  s_n_llhttp__internal__n_invoke_llhttp__after_message_complete,\n  s_n_llhttp__internal__n_pause_1,\n  s_n_llhttp__internal__n_invoke_is_equal_upgrade,\n  s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2,\n  s_n_llhttp__internal__n_chunk_data_almost_done_1,\n  s_n_llhttp__internal__n_chunk_data_almost_done,\n  s_n_llhttp__internal__n_consume_content_length,\n  s_n_llhttp__internal__n_span_start_llhttp__on_body,\n  s_n_llhttp__internal__n_invoke_is_equal_content_length,\n  s_n_llhttp__internal__n_chunk_size_almost_done,\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_9,\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete,\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1,\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2,\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_10,\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete,\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1,\n  s_n_llhttp__internal__n_chunk_extension_quoted_value_done,\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2,\n  s_n_llhttp__internal__n_error_30,\n  s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair,\n  s_n_llhttp__internal__n_error_31,\n  s_n_llhttp__internal__n_chunk_extension_quoted_value,\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3,\n  s_n_llhttp__internal__n_error_33,\n  s_n_llhttp__internal__n_chunk_extension_value,\n  s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value,\n  s_n_llhttp__internal__n_error_34,\n  s_n_llhttp__internal__n_chunk_extension_name,\n  s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name,\n  s_n_llhttp__internal__n_chunk_extensions,\n  s_n_llhttp__internal__n_chunk_size_otherwise,\n  s_n_llhttp__internal__n_chunk_size,\n  s_n_llhttp__internal__n_chunk_size_digit,\n  s_n_llhttp__internal__n_invoke_update_content_length_1,\n  s_n_llhttp__internal__n_consume_content_length_1,\n  s_n_llhttp__internal__n_span_start_llhttp__on_body_1,\n  s_n_llhttp__internal__n_eof,\n  s_n_llhttp__internal__n_span_start_llhttp__on_body_2,\n  s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete,\n  s_n_llhttp__internal__n_error_5,\n  s_n_llhttp__internal__n_headers_almost_done,\n  s_n_llhttp__internal__n_header_field_colon_discard_ws,\n  s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete,\n  s_n_llhttp__internal__n_span_start_llhttp__on_header_value,\n  s_n_llhttp__internal__n_header_value_discard_lws,\n  s_n_llhttp__internal__n_header_value_discard_ws_almost_done,\n  s_n_llhttp__internal__n_header_value_lws,\n  s_n_llhttp__internal__n_header_value_almost_done,\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_17,\n  s_n_llhttp__internal__n_header_value_lenient,\n  s_n_llhttp__internal__n_error_54,\n  s_n_llhttp__internal__n_header_value_otherwise,\n  s_n_llhttp__internal__n_header_value_connection_token,\n  s_n_llhttp__internal__n_header_value_connection_ws,\n  s_n_llhttp__internal__n_header_value_connection_1,\n  s_n_llhttp__internal__n_header_value_connection_2,\n  s_n_llhttp__internal__n_header_value_connection_3,\n  s_n_llhttp__internal__n_header_value_connection,\n  s_n_llhttp__internal__n_error_56,\n  s_n_llhttp__internal__n_error_57,\n  s_n_llhttp__internal__n_header_value_content_length_ws,\n  s_n_llhttp__internal__n_header_value_content_length,\n  s_n_llhttp__internal__n_error_59,\n  s_n_llhttp__internal__n_error_58,\n  s_n_llhttp__internal__n_header_value_te_token_ows,\n  s_n_llhttp__internal__n_header_value,\n  s_n_llhttp__internal__n_header_value_te_token,\n  s_n_llhttp__internal__n_header_value_te_chunked_last,\n  s_n_llhttp__internal__n_header_value_te_chunked,\n  s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1,\n  s_n_llhttp__internal__n_header_value_discard_ws,\n  s_n_llhttp__internal__n_invoke_load_header_state,\n  s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete,\n  s_n_llhttp__internal__n_header_field_general_otherwise,\n  s_n_llhttp__internal__n_header_field_general,\n  s_n_llhttp__internal__n_header_field_colon,\n  s_n_llhttp__internal__n_header_field_3,\n  s_n_llhttp__internal__n_header_field_4,\n  s_n_llhttp__internal__n_header_field_2,\n  s_n_llhttp__internal__n_header_field_1,\n  s_n_llhttp__internal__n_header_field_5,\n  s_n_llhttp__internal__n_header_field_6,\n  s_n_llhttp__internal__n_header_field_7,\n  s_n_llhttp__internal__n_header_field,\n  s_n_llhttp__internal__n_span_start_llhttp__on_header_field,\n  s_n_llhttp__internal__n_header_field_start,\n  s_n_llhttp__internal__n_headers_start,\n  s_n_llhttp__internal__n_url_to_http_09,\n  s_n_llhttp__internal__n_url_skip_to_http09,\n  s_n_llhttp__internal__n_url_skip_lf_to_http09_1,\n  s_n_llhttp__internal__n_url_skip_lf_to_http09,\n  s_n_llhttp__internal__n_req_pri_upgrade,\n  s_n_llhttp__internal__n_req_http_complete_crlf,\n  s_n_llhttp__internal__n_req_http_complete,\n  s_n_llhttp__internal__n_invoke_load_method_1,\n  s_n_llhttp__internal__n_invoke_llhttp__on_version_complete,\n  s_n_llhttp__internal__n_error_67,\n  s_n_llhttp__internal__n_error_74,\n  s_n_llhttp__internal__n_req_http_minor,\n  s_n_llhttp__internal__n_error_75,\n  s_n_llhttp__internal__n_req_http_dot,\n  s_n_llhttp__internal__n_error_76,\n  s_n_llhttp__internal__n_req_http_major,\n  s_n_llhttp__internal__n_span_start_llhttp__on_version,\n  s_n_llhttp__internal__n_req_after_protocol,\n  s_n_llhttp__internal__n_invoke_load_method,\n  s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete,\n  s_n_llhttp__internal__n_error_82,\n  s_n_llhttp__internal__n_req_after_http_start_1,\n  s_n_llhttp__internal__n_invoke_load_method_2,\n  s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1,\n  s_n_llhttp__internal__n_req_after_http_start_2,\n  s_n_llhttp__internal__n_invoke_load_method_3,\n  s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2,\n  s_n_llhttp__internal__n_req_after_http_start_3,\n  s_n_llhttp__internal__n_req_after_http_start,\n  s_n_llhttp__internal__n_span_start_llhttp__on_protocol,\n  s_n_llhttp__internal__n_req_http_start,\n  s_n_llhttp__internal__n_url_to_http,\n  s_n_llhttp__internal__n_url_skip_to_http,\n  s_n_llhttp__internal__n_url_fragment,\n  s_n_llhttp__internal__n_span_end_stub_query_3,\n  s_n_llhttp__internal__n_url_query,\n  s_n_llhttp__internal__n_url_query_or_fragment,\n  s_n_llhttp__internal__n_url_path,\n  s_n_llhttp__internal__n_span_start_stub_path_2,\n  s_n_llhttp__internal__n_span_start_stub_path,\n  s_n_llhttp__internal__n_span_start_stub_path_1,\n  s_n_llhttp__internal__n_url_server_with_at,\n  s_n_llhttp__internal__n_url_server,\n  s_n_llhttp__internal__n_url_schema_delim_1,\n  s_n_llhttp__internal__n_url_schema_delim,\n  s_n_llhttp__internal__n_span_end_stub_schema,\n  s_n_llhttp__internal__n_url_schema,\n  s_n_llhttp__internal__n_url_start,\n  s_n_llhttp__internal__n_span_start_llhttp__on_url_1,\n  s_n_llhttp__internal__n_url_entry_normal,\n  s_n_llhttp__internal__n_span_start_llhttp__on_url,\n  s_n_llhttp__internal__n_url_entry_connect,\n  s_n_llhttp__internal__n_req_spaces_before_url,\n  s_n_llhttp__internal__n_req_first_space_before_url,\n  s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1,\n  s_n_llhttp__internal__n_after_start_req_2,\n  s_n_llhttp__internal__n_after_start_req_3,\n  s_n_llhttp__internal__n_after_start_req_1,\n  s_n_llhttp__internal__n_after_start_req_4,\n  s_n_llhttp__internal__n_after_start_req_6,\n  s_n_llhttp__internal__n_after_start_req_8,\n  s_n_llhttp__internal__n_after_start_req_9,\n  s_n_llhttp__internal__n_after_start_req_7,\n  s_n_llhttp__internal__n_after_start_req_5,\n  s_n_llhttp__internal__n_after_start_req_12,\n  s_n_llhttp__internal__n_after_start_req_13,\n  s_n_llhttp__internal__n_after_start_req_11,\n  s_n_llhttp__internal__n_after_start_req_10,\n  s_n_llhttp__internal__n_after_start_req_14,\n  s_n_llhttp__internal__n_after_start_req_17,\n  s_n_llhttp__internal__n_after_start_req_16,\n  s_n_llhttp__internal__n_after_start_req_15,\n  s_n_llhttp__internal__n_after_start_req_18,\n  s_n_llhttp__internal__n_after_start_req_20,\n  s_n_llhttp__internal__n_after_start_req_21,\n  s_n_llhttp__internal__n_after_start_req_19,\n  s_n_llhttp__internal__n_after_start_req_23,\n  s_n_llhttp__internal__n_after_start_req_24,\n  s_n_llhttp__internal__n_after_start_req_26,\n  s_n_llhttp__internal__n_after_start_req_28,\n  s_n_llhttp__internal__n_after_start_req_29,\n  s_n_llhttp__internal__n_after_start_req_27,\n  s_n_llhttp__internal__n_after_start_req_25,\n  s_n_llhttp__internal__n_after_start_req_30,\n  s_n_llhttp__internal__n_after_start_req_22,\n  s_n_llhttp__internal__n_after_start_req_31,\n  s_n_llhttp__internal__n_after_start_req_32,\n  s_n_llhttp__internal__n_after_start_req_35,\n  s_n_llhttp__internal__n_after_start_req_36,\n  s_n_llhttp__internal__n_after_start_req_34,\n  s_n_llhttp__internal__n_after_start_req_37,\n  s_n_llhttp__internal__n_after_start_req_38,\n  s_n_llhttp__internal__n_after_start_req_42,\n  s_n_llhttp__internal__n_after_start_req_43,\n  s_n_llhttp__internal__n_after_start_req_41,\n  s_n_llhttp__internal__n_after_start_req_40,\n  s_n_llhttp__internal__n_after_start_req_39,\n  s_n_llhttp__internal__n_after_start_req_45,\n  s_n_llhttp__internal__n_after_start_req_44,\n  s_n_llhttp__internal__n_after_start_req_33,\n  s_n_llhttp__internal__n_after_start_req_46,\n  s_n_llhttp__internal__n_after_start_req_49,\n  s_n_llhttp__internal__n_after_start_req_50,\n  s_n_llhttp__internal__n_after_start_req_51,\n  s_n_llhttp__internal__n_after_start_req_52,\n  s_n_llhttp__internal__n_after_start_req_48,\n  s_n_llhttp__internal__n_after_start_req_47,\n  s_n_llhttp__internal__n_after_start_req_55,\n  s_n_llhttp__internal__n_after_start_req_57,\n  s_n_llhttp__internal__n_after_start_req_58,\n  s_n_llhttp__internal__n_after_start_req_56,\n  s_n_llhttp__internal__n_after_start_req_54,\n  s_n_llhttp__internal__n_after_start_req_59,\n  s_n_llhttp__internal__n_after_start_req_60,\n  s_n_llhttp__internal__n_after_start_req_53,\n  s_n_llhttp__internal__n_after_start_req_62,\n  s_n_llhttp__internal__n_after_start_req_63,\n  s_n_llhttp__internal__n_after_start_req_61,\n  s_n_llhttp__internal__n_after_start_req_66,\n  s_n_llhttp__internal__n_after_start_req_68,\n  s_n_llhttp__internal__n_after_start_req_69,\n  s_n_llhttp__internal__n_after_start_req_67,\n  s_n_llhttp__internal__n_after_start_req_70,\n  s_n_llhttp__internal__n_after_start_req_65,\n  s_n_llhttp__internal__n_after_start_req_64,\n  s_n_llhttp__internal__n_after_start_req,\n  s_n_llhttp__internal__n_span_start_llhttp__on_method_1,\n  s_n_llhttp__internal__n_res_line_almost_done,\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_30,\n  s_n_llhttp__internal__n_res_status,\n  s_n_llhttp__internal__n_span_start_llhttp__on_status,\n  s_n_llhttp__internal__n_res_status_code_otherwise,\n  s_n_llhttp__internal__n_res_status_code_digit_3,\n  s_n_llhttp__internal__n_res_status_code_digit_2,\n  s_n_llhttp__internal__n_res_status_code_digit_1,\n  s_n_llhttp__internal__n_res_after_version,\n  s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1,\n  s_n_llhttp__internal__n_error_93,\n  s_n_llhttp__internal__n_error_107,\n  s_n_llhttp__internal__n_res_http_minor,\n  s_n_llhttp__internal__n_error_108,\n  s_n_llhttp__internal__n_res_http_dot,\n  s_n_llhttp__internal__n_error_109,\n  s_n_llhttp__internal__n_res_http_major,\n  s_n_llhttp__internal__n_span_start_llhttp__on_version_1,\n  s_n_llhttp__internal__n_res_after_protocol,\n  s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3,\n  s_n_llhttp__internal__n_error_115,\n  s_n_llhttp__internal__n_res_after_start_1,\n  s_n_llhttp__internal__n_res_after_start_2,\n  s_n_llhttp__internal__n_res_after_start_3,\n  s_n_llhttp__internal__n_res_after_start,\n  s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1,\n  s_n_llhttp__internal__n_invoke_llhttp__on_method_complete,\n  s_n_llhttp__internal__n_req_or_res_method_2,\n  s_n_llhttp__internal__n_invoke_update_type_1,\n  s_n_llhttp__internal__n_req_or_res_method_3,\n  s_n_llhttp__internal__n_req_or_res_method_1,\n  s_n_llhttp__internal__n_req_or_res_method,\n  s_n_llhttp__internal__n_span_start_llhttp__on_method,\n  s_n_llhttp__internal__n_start_req_or_res,\n  s_n_llhttp__internal__n_invoke_load_type,\n  s_n_llhttp__internal__n_invoke_update_finish,\n  s_n_llhttp__internal__n_start,\n};\ntypedef enum llparse_state_e llparse_state_t;\n\nint llhttp__on_method(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_url(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_protocol(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_version(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_header_field(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_header_value(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_body(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_chunk_extension_name(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_chunk_extension_value(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_status(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_load_initial_message_completed(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->initial_message_completed;\n}\n\nint llhttp__on_reset(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_update_finish(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->finish = 2;\n  return 0;\n}\n\nint llhttp__on_message_begin(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_load_type(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->type;\n}\n\nint llhttp__internal__c_store_method(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp,\n    int match) {\n  state->method = match;\n  return 0;\n}\n\nint llhttp__on_method_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_is_equal_method(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->method == 5;\n}\n\nint llhttp__internal__c_update_http_major(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->http_major = 0;\n  return 0;\n}\n\nint llhttp__internal__c_update_http_minor(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->http_minor = 9;\n  return 0;\n}\n\nint llhttp__on_url_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_test_lenient_flags(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 1) == 1;\n}\n\nint llhttp__internal__c_test_lenient_flags_1(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 256) == 256;\n}\n\nint llhttp__internal__c_test_flags(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->flags & 128) == 128;\n}\n\nint llhttp__on_chunk_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_message_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_is_equal_upgrade(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->upgrade == 1;\n}\n\nint llhttp__after_message_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_update_content_length(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->content_length = 0;\n  return 0;\n}\n\nint llhttp__internal__c_update_initial_message_completed(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->initial_message_completed = 1;\n  return 0;\n}\n\nint llhttp__internal__c_update_finish_1(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->finish = 0;\n  return 0;\n}\n\nint llhttp__internal__c_test_lenient_flags_2(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 4) == 4;\n}\n\nint llhttp__internal__c_test_lenient_flags_3(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 32) == 32;\n}\n\nint llhttp__before_headers_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_headers_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__after_headers_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_mul_add_content_length(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp,\n    int match) {\n  /* Multiplication overflow */\n  if (state->content_length > 0xffffffffffffffffULL / 16) {\n    return 1;\n  }\n  \n  state->content_length *= 16;\n  \n  /* Addition overflow */\n  if (match >= 0) {\n    if (state->content_length > 0xffffffffffffffffULL - match) {\n      return 1;\n    }\n  } else {\n    if (state->content_length < 0ULL - match) {\n      return 1;\n    }\n  }\n  state->content_length += match;\n  return 0;\n}\n\nint llhttp__internal__c_test_lenient_flags_4(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 512) == 512;\n}\n\nint llhttp__on_chunk_header(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_is_equal_content_length(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->content_length == 0;\n}\n\nint llhttp__internal__c_test_lenient_flags_7(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 128) == 128;\n}\n\nint llhttp__internal__c_or_flags(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 128;\n  return 0;\n}\n\nint llhttp__internal__c_test_lenient_flags_8(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 64) == 64;\n}\n\nint llhttp__on_chunk_extension_name_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__on_chunk_extension_value_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_update_finish_3(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->finish = 1;\n  return 0;\n}\n\nint llhttp__internal__c_or_flags_1(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 64;\n  return 0;\n}\n\nint llhttp__internal__c_update_upgrade(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->upgrade = 1;\n  return 0;\n}\n\nint llhttp__internal__c_store_header_state(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp,\n    int match) {\n  state->header_state = match;\n  return 0;\n}\n\nint llhttp__on_header_field_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_load_header_state(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->header_state;\n}\n\nint llhttp__internal__c_test_flags_4(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->flags & 512) == 512;\n}\n\nint llhttp__internal__c_test_lenient_flags_22(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 2) == 2;\n}\n\nint llhttp__internal__c_or_flags_5(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 1;\n  return 0;\n}\n\nint llhttp__internal__c_update_header_state(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->header_state = 1;\n  return 0;\n}\n\nint llhttp__on_header_value_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_or_flags_6(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 2;\n  return 0;\n}\n\nint llhttp__internal__c_or_flags_7(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 4;\n  return 0;\n}\n\nint llhttp__internal__c_or_flags_8(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 8;\n  return 0;\n}\n\nint llhttp__internal__c_update_header_state_3(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->header_state = 6;\n  return 0;\n}\n\nint llhttp__internal__c_update_header_state_1(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->header_state = 0;\n  return 0;\n}\n\nint llhttp__internal__c_update_header_state_6(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->header_state = 5;\n  return 0;\n}\n\nint llhttp__internal__c_update_header_state_7(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->header_state = 7;\n  return 0;\n}\n\nint llhttp__internal__c_test_flags_2(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->flags & 32) == 32;\n}\n\nint llhttp__internal__c_mul_add_content_length_1(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp,\n    int match) {\n  /* Multiplication overflow */\n  if (state->content_length > 0xffffffffffffffffULL / 10) {\n    return 1;\n  }\n  \n  state->content_length *= 10;\n  \n  /* Addition overflow */\n  if (match >= 0) {\n    if (state->content_length > 0xffffffffffffffffULL - match) {\n      return 1;\n    }\n  } else {\n    if (state->content_length < 0ULL - match) {\n      return 1;\n    }\n  }\n  state->content_length += match;\n  return 0;\n}\n\nint llhttp__internal__c_or_flags_17(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 32;\n  return 0;\n}\n\nint llhttp__internal__c_test_flags_3(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->flags & 8) == 8;\n}\n\nint llhttp__internal__c_test_lenient_flags_20(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 8) == 8;\n}\n\nint llhttp__internal__c_or_flags_18(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 512;\n  return 0;\n}\n\nint llhttp__internal__c_and_flags(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags &= -9;\n  return 0;\n}\n\nint llhttp__internal__c_update_header_state_8(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->header_state = 8;\n  return 0;\n}\n\nint llhttp__internal__c_or_flags_20(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->flags |= 16;\n  return 0;\n}\n\nint llhttp__on_protocol_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_load_method(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->method;\n}\n\nint llhttp__internal__c_store_http_major(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp,\n    int match) {\n  state->http_major = match;\n  return 0;\n}\n\nint llhttp__internal__c_store_http_minor(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp,\n    int match) {\n  state->http_minor = match;\n  return 0;\n}\n\nint llhttp__internal__c_test_lenient_flags_24(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return (state->lenient_flags & 16) == 16;\n}\n\nint llhttp__on_version_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_load_http_major(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->http_major;\n}\n\nint llhttp__internal__c_load_http_minor(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  return state->http_minor;\n}\n\nint llhttp__internal__c_update_status_code(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->status_code = 0;\n  return 0;\n}\n\nint llhttp__internal__c_mul_add_status_code(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp,\n    int match) {\n  /* Multiplication overflow */\n  if (state->status_code > 0xffff / 10) {\n    return 1;\n  }\n  \n  state->status_code *= 10;\n  \n  /* Addition overflow */\n  if (match >= 0) {\n    if (state->status_code > 0xffff - match) {\n      return 1;\n    }\n  } else {\n    if (state->status_code < 0 - match) {\n      return 1;\n    }\n  }\n  state->status_code += match;\n  return 0;\n}\n\nint llhttp__on_status_complete(\n    llhttp__internal_t* s, const unsigned char* p,\n    const unsigned char* endp);\n\nint llhttp__internal__c_update_type(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->type = 1;\n  return 0;\n}\n\nint llhttp__internal__c_update_type_1(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  state->type = 2;\n  return 0;\n}\n\nint llhttp__internal_init(llhttp__internal_t* state) {\n  memset(state, 0, sizeof(*state));\n  state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_start;\n  return 0;\n}\n\nstatic llparse_state_t llhttp__internal__run(\n    llhttp__internal_t* state,\n    const unsigned char* p,\n    const unsigned char* endp) {\n  int match;\n  switch ((llparse_state_t) (intptr_t) state->_current) {\n    case s_n_llhttp__internal__n_closed:\n    s_n_llhttp__internal__n_closed: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_closed;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_closed;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_closed;\n        }\n        default: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_3;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__after_message_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: {\n      switch (llhttp__after_message_complete(state, p, endp)) {\n        case 1:\n          goto s_n_llhttp__internal__n_invoke_update_content_length;\n        default:\n          goto s_n_llhttp__internal__n_invoke_update_finish_1;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_pause_1:\n    s_n_llhttp__internal__n_pause_1: {\n      state->error = 0x16;\n      state->reason = \"Pause on CONNECT/Upgrade\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_is_equal_upgrade:\n    s_n_llhttp__internal__n_invoke_is_equal_upgrade: {\n      switch (llhttp__internal__c_is_equal_upgrade(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;\n        default:\n          goto s_n_llhttp__internal__n_pause_1;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2:\n    s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: {\n      switch (llhttp__on_message_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_is_equal_upgrade;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_13;\n        default:\n          goto s_n_llhttp__internal__n_error_38;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_data_almost_done_1:\n    s_n_llhttp__internal__n_chunk_data_almost_done_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_data_almost_done_1;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_7;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_data_almost_done:\n    s_n_llhttp__internal__n_chunk_data_almost_done: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_data_almost_done;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_6;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_data_almost_done_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_7;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_consume_content_length:\n    s_n_llhttp__internal__n_consume_content_length: {\n      size_t avail;\n      uint64_t need;\n      \n      avail = endp - p;\n      need = state->content_length;\n      if (avail >= need) {\n        p += need;\n        state->content_length = 0;\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_body;\n      }\n      \n      state->content_length -= avail;\n      return s_n_llhttp__internal__n_consume_content_length;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_body:\n    s_n_llhttp__internal__n_span_start_llhttp__on_body: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_body;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_body;\n      goto s_n_llhttp__internal__n_consume_content_length;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_is_equal_content_length:\n    s_n_llhttp__internal__n_invoke_is_equal_content_length: {\n      switch (llhttp__internal__c_is_equal_content_length(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_body;\n        default:\n          goto s_n_llhttp__internal__n_invoke_or_flags;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_size_almost_done:\n    s_n_llhttp__internal__n_chunk_size_almost_done: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_size_almost_done;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_8;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_test_lenient_flags_9:\n    s_n_llhttp__internal__n_invoke_test_lenient_flags_9: {\n      switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n        case 1:\n          goto s_n_llhttp__internal__n_chunk_size_almost_done;\n        default:\n          goto s_n_llhttp__internal__n_error_20;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete: {\n      switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_9;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_5;\n        default:\n          goto s_n_llhttp__internal__n_error_19;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1:\n    s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1: {\n      switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_chunk_size_almost_done;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_6;\n        default:\n          goto s_n_llhttp__internal__n_error_21;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2:\n    s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2: {\n      switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_chunk_extensions;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_7;\n        default:\n          goto s_n_llhttp__internal__n_error_22;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_test_lenient_flags_10:\n    s_n_llhttp__internal__n_invoke_test_lenient_flags_10: {\n      switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n        case 1:\n          goto s_n_llhttp__internal__n_chunk_size_almost_done;\n        default:\n          goto s_n_llhttp__internal__n_error_25;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete: {\n      switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_10;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_8;\n        default:\n          goto s_n_llhttp__internal__n_error_24;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1:\n    s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1: {\n      switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_chunk_size_almost_done;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_9;\n        default:\n          goto s_n_llhttp__internal__n_error_26;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_extension_quoted_value_done:\n    s_n_llhttp__internal__n_chunk_extension_quoted_value_done: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_extension_quoted_value_done;\n      }\n      switch (*p) {\n        case 10: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_11;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_size_almost_done;\n        }\n        case ';': {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_extensions;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_29;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2:\n    s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2: {\n      switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_chunk_extension_quoted_value_done;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_10;\n        default:\n          goto s_n_llhttp__internal__n_error_27;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_30:\n    s_n_llhttp__internal__n_error_30: {\n      state->error = 0x2;\n      state->reason = \"Invalid quoted-pair in chunk extensions quoted value\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair:\n    s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_extension_quoted_value;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_3;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_31:\n    s_n_llhttp__internal__n_error_31: {\n      state->error = 0x2;\n      state->reason = \"Invalid character in chunk extensions quoted value\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_extension_quoted_value:\n    s_n_llhttp__internal__n_chunk_extension_quoted_value: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_extension_quoted_value;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_extension_quoted_value;\n        }\n        case 2: {\n          p++;\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_2;\n        }\n        case 3: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_4;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3:\n    s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3: {\n      switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_chunk_extensions;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_11;\n        default:\n          goto s_n_llhttp__internal__n_error_32;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_33:\n    s_n_llhttp__internal__n_error_33: {\n      state->error = 0x2;\n      state->reason = \"Invalid character in chunk extensions value\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_extension_value:\n    s_n_llhttp__internal__n_chunk_extension_value: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 3, 4, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 5, 0, 0, 0, 0,\n        0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 0,\n        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,\n        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,\n        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,\n        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\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_extension_value;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value;\n        }\n        case 2: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_1;\n        }\n        case 3: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_extension_value;\n        }\n        case 4: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_extension_quoted_value;\n        }\n        case 5: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_5;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_6;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value:\n    s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_chunk_extension_value;\n      goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_3;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_34:\n    s_n_llhttp__internal__n_error_34: {\n      state->error = 0x2;\n      state->reason = \"Invalid character in chunk extensions name\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_extension_name:\n    s_n_llhttp__internal__n_chunk_extension_name: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 3, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 0, 5, 0, 0,\n        0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 0,\n        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,\n        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,\n        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,\n        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\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_extension_name;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name;\n        }\n        case 2: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_1;\n        }\n        case 3: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_extension_name;\n        }\n        case 4: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_2;\n        }\n        case 5: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_3;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_4;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name:\n    s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_chunk_extension_name;\n      goto s_n_llhttp__internal__n_chunk_extension_name;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_extensions:\n    s_n_llhttp__internal__n_chunk_extensions: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_extensions;\n      }\n      switch (*p) {\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_error_17;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_error_18;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_size_otherwise:\n    s_n_llhttp__internal__n_chunk_size_otherwise: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_size_otherwise;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_4;\n        }\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_5;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_size_almost_done;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_4;\n        }\n        case ';': {\n          p++;\n          goto s_n_llhttp__internal__n_chunk_extensions;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_35;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_size:\n    s_n_llhttp__internal__n_chunk_size: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_size;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'A': {\n          p++;\n          match = 10;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'B': {\n          p++;\n          match = 11;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'C': {\n          p++;\n          match = 12;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'D': {\n          p++;\n          match = 13;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'E': {\n          p++;\n          match = 14;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'F': {\n          p++;\n          match = 15;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'a': {\n          p++;\n          match = 10;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'b': {\n          p++;\n          match = 11;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'c': {\n          p++;\n          match = 12;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'd': {\n          p++;\n          match = 13;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'e': {\n          p++;\n          match = 14;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'f': {\n          p++;\n          match = 15;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_chunk_size_otherwise;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_chunk_size_digit:\n    s_n_llhttp__internal__n_chunk_size_digit: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_chunk_size_digit;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'A': {\n          p++;\n          match = 10;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'B': {\n          p++;\n          match = 11;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'C': {\n          p++;\n          match = 12;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'D': {\n          p++;\n          match = 13;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'E': {\n          p++;\n          match = 14;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'F': {\n          p++;\n          match = 15;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'a': {\n          p++;\n          match = 10;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'b': {\n          p++;\n          match = 11;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'c': {\n          p++;\n          match = 12;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'd': {\n          p++;\n          match = 13;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'e': {\n          p++;\n          match = 14;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        case 'f': {\n          p++;\n          match = 15;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_37;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_update_content_length_1:\n    s_n_llhttp__internal__n_invoke_update_content_length_1: {\n      switch (llhttp__internal__c_update_content_length(state, p, endp)) {\n        default:\n          goto s_n_llhttp__internal__n_chunk_size_digit;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_consume_content_length_1:\n    s_n_llhttp__internal__n_consume_content_length_1: {\n      size_t avail;\n      uint64_t need;\n      \n      avail = endp - p;\n      need = state->content_length;\n      if (avail >= need) {\n        p += need;\n        state->content_length = 0;\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_body_1;\n      }\n      \n      state->content_length -= avail;\n      return s_n_llhttp__internal__n_consume_content_length_1;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_body_1:\n    s_n_llhttp__internal__n_span_start_llhttp__on_body_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_body_1;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_body;\n      goto s_n_llhttp__internal__n_consume_content_length_1;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_eof:\n    s_n_llhttp__internal__n_eof: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_eof;\n      }\n      p++;\n      goto s_n_llhttp__internal__n_eof;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_body_2:\n    s_n_llhttp__internal__n_span_start_llhttp__on_body_2: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_body_2;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_body;\n      goto s_n_llhttp__internal__n_eof;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: {\n      switch (llhttp__after_headers_complete(state, p, endp)) {\n        case 1:\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1;\n        case 2:\n          goto s_n_llhttp__internal__n_invoke_update_content_length_1;\n        case 3:\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_body_1;\n        case 4:\n          goto s_n_llhttp__internal__n_invoke_update_finish_3;\n        case 5:\n          goto s_n_llhttp__internal__n_error_39;\n        default:\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_5:\n    s_n_llhttp__internal__n_error_5: {\n      state->error = 0xa;\n      state->reason = \"Invalid header field char\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_headers_almost_done:\n    s_n_llhttp__internal__n_headers_almost_done: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_headers_almost_done;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_flags_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_12;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_colon_discard_ws:\n    s_n_llhttp__internal__n_header_field_colon_discard_ws: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_colon_discard_ws;\n      }\n      switch (*p) {\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_colon_discard_ws;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_field_colon;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete: {\n      switch (llhttp__on_header_value_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_header_field_start;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_18;\n        default:\n          goto s_n_llhttp__internal__n_error_48;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_header_value:\n    s_n_llhttp__internal__n_span_start_llhttp__on_header_value: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_header_value;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_header_value;\n      goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_discard_lws:\n    s_n_llhttp__internal__n_header_value_discard_lws: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_discard_lws;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_15;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_15;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_load_header_state_1;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_discard_ws_almost_done:\n    s_n_llhttp__internal__n_header_value_discard_ws_almost_done: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_discard_ws_almost_done;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_discard_lws;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_16;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_lws:\n    s_n_llhttp__internal__n_header_value_lws: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_lws;\n      }\n      switch (*p) {\n        case 9: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_18;\n        }\n        case ' ': {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_18;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_load_header_state_5;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_almost_done:\n    s_n_llhttp__internal__n_header_value_almost_done: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_almost_done;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_lws;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_53;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_test_lenient_flags_17:\n    s_n_llhttp__internal__n_invoke_test_lenient_flags_17: {\n      switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n        case 1:\n          goto s_n_llhttp__internal__n_header_value_almost_done;\n        default:\n          goto s_n_llhttp__internal__n_error_51;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_lenient:\n    s_n_llhttp__internal__n_header_value_lenient: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_lenient;\n      }\n      switch (*p) {\n        case 10: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4;\n        }\n        case 13: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5;\n        }\n        default: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_lenient;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_54:\n    s_n_llhttp__internal__n_error_54: {\n      state->error = 0xa;\n      state->reason = \"Invalid header value char\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_otherwise:\n    s_n_llhttp__internal__n_header_value_otherwise: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_otherwise;\n      }\n      switch (*p) {\n        case 10: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1;\n        }\n        case 13: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_19;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_connection_token:\n    s_n_llhttp__internal__n_header_value_connection_token: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_connection_token;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_connection_token;\n        }\n        case 2: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_connection;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_value_otherwise;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_connection_ws:\n    s_n_llhttp__internal__n_header_value_connection_ws: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_connection_ws;\n      }\n      switch (*p) {\n        case 10: {\n          goto s_n_llhttp__internal__n_header_value_otherwise;\n        }\n        case 13: {\n          goto s_n_llhttp__internal__n_header_value_otherwise;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_connection_ws;\n        }\n        case ',': {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_load_header_state_6;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_5;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_connection_1:\n    s_n_llhttp__internal__n_header_value_connection_1: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_connection_1;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob2, 4);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_update_header_state_3;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_value_connection_1;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_header_value_connection_token;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_connection_2:\n    s_n_llhttp__internal__n_header_value_connection_2: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_connection_2;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob3, 9);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_update_header_state_6;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_value_connection_2;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_header_value_connection_token;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_connection_3:\n    s_n_llhttp__internal__n_header_value_connection_3: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_connection_3;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob4, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_update_header_state_7;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_value_connection_3;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_header_value_connection_token;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_connection:\n    s_n_llhttp__internal__n_header_value_connection: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_connection;\n      }\n      switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_connection;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_connection;\n        }\n        case 'c': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_connection_1;\n        }\n        case 'k': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_connection_2;\n        }\n        case 'u': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_connection_3;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_value_connection_token;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_56:\n    s_n_llhttp__internal__n_error_56: {\n      state->error = 0xb;\n      state->reason = \"Content-Length overflow\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_57:\n    s_n_llhttp__internal__n_error_57: {\n      state->error = 0xb;\n      state->reason = \"Invalid character in Content-Length\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_content_length_ws:\n    s_n_llhttp__internal__n_header_value_content_length_ws: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_content_length_ws;\n      }\n      switch (*p) {\n        case 10: {\n          goto s_n_llhttp__internal__n_invoke_or_flags_17;\n        }\n        case 13: {\n          goto s_n_llhttp__internal__n_invoke_or_flags_17;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_content_length_ws;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_7;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_content_length:\n    s_n_llhttp__internal__n_header_value_content_length: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_content_length;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_value_content_length_ws;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_59:\n    s_n_llhttp__internal__n_error_59: {\n      state->error = 0xf;\n      state->reason = \"Invalid `Transfer-Encoding` header value\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_58:\n    s_n_llhttp__internal__n_error_58: {\n      state->error = 0xf;\n      state->reason = \"Invalid `Transfer-Encoding` header value\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_te_token_ows:\n    s_n_llhttp__internal__n_header_value_te_token_ows: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_te_token_ows;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_te_token_ows;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_te_token_ows;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_value_te_chunked;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value:\n    s_n_llhttp__internal__n_header_value: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value;\n      }\n      #ifdef __SSE4_2__\n      if (endp - p >= 16) {\n        __m128i ranges;\n        __m128i input;\n        int match_len;\n      \n        /* Load input */\n        input = _mm_loadu_si128((__m128i const*) p);\n        ranges = _mm_loadu_si128((__m128i const*) llparse_blob6);\n      \n        /* Find first character that does not match `ranges` */\n        match_len = _mm_cmpestri(ranges, 6,\n            input, 16,\n            _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |\n              _SIDD_NEGATIVE_POLARITY);\n      \n        if (match_len != 0) {\n          p += match_len;\n          goto s_n_llhttp__internal__n_header_value;\n        }\n        goto s_n_llhttp__internal__n_header_value_otherwise;\n      }\n      #endif  /* __SSE4_2__ */\n      #ifdef __ARM_NEON__\n      while (endp - p >= 16) {\n        uint8x16_t input;\n        uint8x16_t single;\n        uint8x16_t mask;\n        uint8x8_t narrow;\n        uint64_t match_mask;\n        int match_len;\n      \n        /* Load input */\n        input = vld1q_u8(p);\n        /* Find first character that does not match `ranges` */\n        single = vceqq_u8(input, vdupq_n_u8(0x9));\n        mask = single;\n        single = vandq_u16(\n          vcgeq_u8(input, vdupq_n_u8(' ')),\n          vcleq_u8(input, vdupq_n_u8('~'))\n        );\n        mask = vorrq_u16(mask, single);\n        single = vandq_u16(\n          vcgeq_u8(input, vdupq_n_u8(0x80)),\n          vcleq_u8(input, vdupq_n_u8(0xff))\n        );\n        mask = vorrq_u16(mask, single);\n        narrow = vshrn_n_u16(mask, 4);\n        match_mask = ~vget_lane_u64(vreinterpret_u64_u8(narrow), 0);\n        match_len = __builtin_ctzll(match_mask) >> 2;\n        if (match_len != 16) {\n          p += match_len;\n          goto s_n_llhttp__internal__n_header_value_otherwise;\n        }\n        p += 16;\n      }\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value;\n      }\n      #endif  /* __ARM_NEON__ */\n      #ifdef __wasm_simd128__\n      while (endp - p >= 16) {\n        v128_t input;\n        v128_t mask;\n        v128_t single;\n        int match_len;\n      \n        /* Load input */\n        input = wasm_v128_load(p);\n        /* Find first character that does not match `ranges` */\n        single = wasm_i8x16_eq(input, wasm_u8x16_const_splat(0x9));\n        mask = single;\n        single = wasm_v128_and(\n          wasm_i8x16_ge(input, wasm_u8x16_const_splat(' ')),\n          wasm_i8x16_le(input, wasm_u8x16_const_splat('~'))\n        );\n        mask = wasm_v128_or(mask, single);\n        single = wasm_v128_and(\n          wasm_i8x16_ge(input, wasm_u8x16_const_splat(0x80)),\n          wasm_i8x16_le(input, wasm_u8x16_const_splat(0xff))\n        );\n        mask = wasm_v128_or(mask, single);\n        match_len = __builtin_ctz(\n          ~wasm_i8x16_bitmask(mask)\n        );\n        if (match_len != 16) {\n          p += match_len;\n          goto s_n_llhttp__internal__n_header_value_otherwise;\n        }\n        p += 16;\n      }\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value;\n      }\n      #endif  /* __wasm_simd128__ */\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_value_otherwise;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_te_token:\n    s_n_llhttp__internal__n_header_value_te_token: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_te_token;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_te_token;\n        }\n        case 2: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_te_token_ows;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_9;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_te_chunked_last:\n    s_n_llhttp__internal__n_header_value_te_chunked_last: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_te_chunked_last;\n      }\n      switch (*p) {\n        case 10: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_8;\n        }\n        case 13: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_8;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_te_chunked_last;\n        }\n        case ',': {\n          goto s_n_llhttp__internal__n_invoke_load_type_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_value_te_token;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_te_chunked:\n    s_n_llhttp__internal__n_header_value_te_chunked: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_te_chunked;\n      }\n      match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob5, 7);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_te_chunked_last;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_value_te_chunked;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_header_value_te_token;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1:\n    s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_header_value;\n      goto s_n_llhttp__internal__n_invoke_load_header_state_3;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_value_discard_ws:\n    s_n_llhttp__internal__n_header_value_discard_ws: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_value_discard_ws;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_discard_ws;\n        }\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_14;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_discard_ws_almost_done;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_header_value_discard_ws;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_load_header_state:\n    s_n_llhttp__internal__n_invoke_load_header_state: {\n      switch (llhttp__internal__c_load_header_state(state, p, endp)) {\n        case 2:\n          goto s_n_llhttp__internal__n_invoke_test_flags_4;\n        case 3:\n          goto s_n_llhttp__internal__n_invoke_test_flags_5;\n        default:\n          goto s_n_llhttp__internal__n_header_value_discard_ws;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete: {\n      switch (llhttp__on_header_field_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_load_header_state;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_19;\n        default:\n          goto s_n_llhttp__internal__n_error_45;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_general_otherwise:\n    s_n_llhttp__internal__n_header_field_general_otherwise: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_general_otherwise;\n      }\n      switch (*p) {\n        case ':': {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_62;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_general:\n    s_n_llhttp__internal__n_header_field_general: {\n      static uint8_t lookup_table[] = {\n        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,\n        0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,\n        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,\n        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,\n        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,\n        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,\n        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\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_general;\n      }\n      #ifdef __SSE4_2__\n      if (endp - p >= 16) {\n        __m128i ranges;\n        __m128i input;\n        int match_len;\n      \n        /* Load input */\n        input = _mm_loadu_si128((__m128i const*) p);\n        ranges = _mm_loadu_si128((__m128i const*) llparse_blob7);\n      \n        /* Find first character that does not match `ranges` */\n        match_len = _mm_cmpestri(ranges, 16,\n            input, 16,\n            _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |\n              _SIDD_NEGATIVE_POLARITY);\n      \n        if (match_len != 0) {\n          p += match_len;\n          goto s_n_llhttp__internal__n_header_field_general;\n        }\n        ranges = _mm_loadu_si128((__m128i const*) llparse_blob8);\n      \n        /* Find first character that does not match `ranges` */\n        match_len = _mm_cmpestri(ranges, 2,\n            input, 16,\n            _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |\n              _SIDD_NEGATIVE_POLARITY);\n      \n        if (match_len != 0) {\n          p += match_len;\n          goto s_n_llhttp__internal__n_header_field_general;\n        }\n        goto s_n_llhttp__internal__n_header_field_general_otherwise;\n      }\n      #endif  /* __SSE4_2__ */\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_general;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_field_general_otherwise;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_colon:\n    s_n_llhttp__internal__n_header_field_colon: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_colon;\n      }\n      switch (*p) {\n        case ' ': {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_13;\n        }\n        case ':': {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_10;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_3:\n    s_n_llhttp__internal__n_header_field_3: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_3;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob1, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_store_header_state;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_field_3;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_11;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_4:\n    s_n_llhttp__internal__n_header_field_4: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_4;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob9, 10);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_store_header_state;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_field_4;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_11;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_2:\n    s_n_llhttp__internal__n_header_field_2: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_2;\n      }\n      switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) {\n        case 'n': {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_3;\n        }\n        case 't': {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_4;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_11;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_1:\n    s_n_llhttp__internal__n_header_field_1: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_1;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob0, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_2;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_field_1;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_11;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_5:\n    s_n_llhttp__internal__n_header_field_5: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_5;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob10, 15);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_store_header_state;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_field_5;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_11;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_6:\n    s_n_llhttp__internal__n_header_field_6: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_6;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob11, 16);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_store_header_state;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_field_6;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_11;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_7:\n    s_n_llhttp__internal__n_header_field_7: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_7;\n      }\n      match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob12, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_store_header_state;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_header_field_7;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_11;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field:\n    s_n_llhttp__internal__n_header_field: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field;\n      }\n      switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) {\n        case 'c': {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_1;\n        }\n        case 'p': {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_5;\n        }\n        case 't': {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_6;\n        }\n        case 'u': {\n          p++;\n          goto s_n_llhttp__internal__n_header_field_7;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_update_header_state_11;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_header_field:\n    s_n_llhttp__internal__n_span_start_llhttp__on_header_field: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_header_field;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_header_field;\n      goto s_n_llhttp__internal__n_header_field;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_header_field_start:\n    s_n_llhttp__internal__n_header_field_start: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_header_field_start;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_1;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_headers_almost_done;\n        }\n        case ':': {\n          goto s_n_llhttp__internal__n_error_44;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_header_field;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_headers_start:\n    s_n_llhttp__internal__n_headers_start: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_headers_start;\n      }\n      switch (*p) {\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_header_field_start;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_to_http_09:\n    s_n_llhttp__internal__n_url_to_http_09: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_to_http_09;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_update_http_major;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_skip_to_http09:\n    s_n_llhttp__internal__n_url_skip_to_http09: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_skip_to_http09;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        default: {\n          p++;\n          goto s_n_llhttp__internal__n_url_to_http_09;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_skip_lf_to_http09_1:\n    s_n_llhttp__internal__n_url_skip_lf_to_http09_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_skip_lf_to_http09_1;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_url_to_http_09;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_63;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_skip_lf_to_http09:\n    s_n_llhttp__internal__n_url_skip_lf_to_http09: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_skip_lf_to_http09;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_url_skip_lf_to_http09_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_63;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_pri_upgrade:\n    s_n_llhttp__internal__n_req_pri_upgrade: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_pri_upgrade;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob14, 10);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_error_72;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_req_pri_upgrade;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_73;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_http_complete_crlf:\n    s_n_llhttp__internal__n_req_http_complete_crlf: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_http_complete_crlf;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_headers_start;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_26;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_http_complete:\n    s_n_llhttp__internal__n_req_http_complete: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_http_complete;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_25;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_req_http_complete_crlf;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_71;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_load_method_1:\n    s_n_llhttp__internal__n_invoke_load_method_1: {\n      switch (llhttp__internal__c_load_method(state, p, endp)) {\n        case 34:\n          goto s_n_llhttp__internal__n_req_pri_upgrade;\n        default:\n          goto s_n_llhttp__internal__n_req_http_complete;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_version_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__on_version_complete: {\n      switch (llhttp__on_version_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_load_method_1;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_21;\n        default:\n          goto s_n_llhttp__internal__n_error_68;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_67:\n    s_n_llhttp__internal__n_error_67: {\n      state->error = 0x9;\n      state->reason = \"Invalid HTTP version\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_74:\n    s_n_llhttp__internal__n_error_74: {\n      state->error = 0x9;\n      state->reason = \"Invalid minor version\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_http_minor:\n    s_n_llhttp__internal__n_req_http_minor: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_http_minor;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_version_2;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_75:\n    s_n_llhttp__internal__n_error_75: {\n      state->error = 0x9;\n      state->reason = \"Expected dot\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_http_dot:\n    s_n_llhttp__internal__n_req_http_dot: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_http_dot;\n      }\n      switch (*p) {\n        case '.': {\n          p++;\n          goto s_n_llhttp__internal__n_req_http_minor;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_version_3;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_76:\n    s_n_llhttp__internal__n_error_76: {\n      state->error = 0x9;\n      state->reason = \"Invalid major version\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_http_major:\n    s_n_llhttp__internal__n_req_http_major: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_http_major;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_store_http_major;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_version_4;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_version:\n    s_n_llhttp__internal__n_span_start_llhttp__on_version: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_version;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_version;\n      goto s_n_llhttp__internal__n_req_http_major;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_after_protocol:\n    s_n_llhttp__internal__n_req_after_protocol: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_after_protocol;\n      }\n      switch (*p) {\n        case '/': {\n          p++;\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_version;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_77;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_load_method:\n    s_n_llhttp__internal__n_invoke_load_method: {\n      switch (llhttp__internal__c_load_method(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 1:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 2:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 3:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 4:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 5:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 6:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 7:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 8:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 9:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 10:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 11:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 12:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 13:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 14:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 15:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 16:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 17:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 18:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 19:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 20:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 21:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 22:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 23:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 24:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 25:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 26:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 27:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 28:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 29:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 30:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 31:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 32:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 33:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 34:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 46:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        default:\n          goto s_n_llhttp__internal__n_error_66;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete: {\n      switch (llhttp__on_protocol_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_load_method;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_22;\n        default:\n          goto s_n_llhttp__internal__n_error_65;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_82:\n    s_n_llhttp__internal__n_error_82: {\n      state->error = 0x8;\n      state->reason = \"Expected HTTP/, RTSP/ or ICE/\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_after_http_start_1:\n    s_n_llhttp__internal__n_req_after_http_start_1: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_after_http_start_1;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob13, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_req_after_http_start_1;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_load_method_2:\n    s_n_llhttp__internal__n_invoke_load_method_2: {\n      switch (llhttp__internal__c_load_method(state, p, endp)) {\n        case 33:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        default:\n          goto s_n_llhttp__internal__n_error_79;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1:\n    s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1: {\n      switch (llhttp__on_protocol_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_load_method_2;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_23;\n        default:\n          goto s_n_llhttp__internal__n_error_78;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_after_http_start_2:\n    s_n_llhttp__internal__n_req_after_http_start_2: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_after_http_start_2;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_req_after_http_start_2;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_load_method_3:\n    s_n_llhttp__internal__n_invoke_load_method_3: {\n      switch (llhttp__internal__c_load_method(state, p, endp)) {\n        case 1:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 3:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 6:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 35:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 36:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 37:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 38:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 39:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 40:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 41:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 42:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 43:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 44:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        case 45:\n          goto s_n_llhttp__internal__n_req_after_protocol;\n        default:\n          goto s_n_llhttp__internal__n_error_81;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2:\n    s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2: {\n      switch (llhttp__on_protocol_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_invoke_load_method_3;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_24;\n        default:\n          goto s_n_llhttp__internal__n_error_80;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_after_http_start_3:\n    s_n_llhttp__internal__n_req_after_http_start_3: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_after_http_start_3;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_2;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_req_after_http_start_3;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_after_http_start:\n    s_n_llhttp__internal__n_req_after_http_start: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_after_http_start;\n      }\n      switch (*p) {\n        case 'H': {\n          p++;\n          goto s_n_llhttp__internal__n_req_after_http_start_1;\n        }\n        case 'I': {\n          p++;\n          goto s_n_llhttp__internal__n_req_after_http_start_2;\n        }\n        case 'R': {\n          p++;\n          goto s_n_llhttp__internal__n_req_after_http_start_3;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_protocol:\n    s_n_llhttp__internal__n_span_start_llhttp__on_protocol: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_protocol;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_protocol;\n      goto s_n_llhttp__internal__n_req_after_http_start;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_http_start:\n    s_n_llhttp__internal__n_req_http_start: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_http_start;\n      }\n      switch (*p) {\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_req_http_start;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_protocol;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_to_http:\n    s_n_llhttp__internal__n_url_to_http: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_to_http;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_skip_to_http:\n    s_n_llhttp__internal__n_url_skip_to_http: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_skip_to_http;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        default: {\n          p++;\n          goto s_n_llhttp__internal__n_url_to_http;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_fragment:\n    s_n_llhttp__internal__n_url_fragment: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0,\n        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,\n        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,\n        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,\n        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\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_fragment;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 2: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_6;\n        }\n        case 3: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_7;\n        }\n        case 4: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_8;\n        }\n        case 5: {\n          p++;\n          goto s_n_llhttp__internal__n_url_fragment;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_83;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_end_stub_query_3:\n    s_n_llhttp__internal__n_span_end_stub_query_3: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_end_stub_query_3;\n      }\n      p++;\n      goto s_n_llhttp__internal__n_url_fragment;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_query:\n    s_n_llhttp__internal__n_url_query: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        4, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0,\n        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,\n        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,\n        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,\n        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\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_query;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 2: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_9;\n        }\n        case 3: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_10;\n        }\n        case 4: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_11;\n        }\n        case 5: {\n          p++;\n          goto s_n_llhttp__internal__n_url_query;\n        }\n        case 6: {\n          goto s_n_llhttp__internal__n_span_end_stub_query_3;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_84;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_query_or_fragment:\n    s_n_llhttp__internal__n_url_query_or_fragment: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_query_or_fragment;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 10: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_3;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 13: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_4;\n        }\n        case ' ': {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_5;\n        }\n        case '#': {\n          p++;\n          goto s_n_llhttp__internal__n_url_fragment;\n        }\n        case '?': {\n          p++;\n          goto s_n_llhttp__internal__n_url_query;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_85;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_path:\n    s_n_llhttp__internal__n_url_path: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,\n        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\n        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,\n        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,\n        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,\n        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,\n        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\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_path;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 2: {\n          p++;\n          goto s_n_llhttp__internal__n_url_path;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_url_query_or_fragment;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_stub_path_2:\n    s_n_llhttp__internal__n_span_start_stub_path_2: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_stub_path_2;\n      }\n      p++;\n      goto s_n_llhttp__internal__n_url_path;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_stub_path:\n    s_n_llhttp__internal__n_span_start_stub_path: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_stub_path;\n      }\n      p++;\n      goto s_n_llhttp__internal__n_url_path;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_stub_path_1:\n    s_n_llhttp__internal__n_span_start_stub_path_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_stub_path_1;\n      }\n      p++;\n      goto s_n_llhttp__internal__n_url_path;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_server_with_at:\n    s_n_llhttp__internal__n_url_server_with_at: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7,\n        8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5,\n        0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0,\n        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,\n        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,\n        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,\n        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\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_server_with_at;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 2: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_12;\n        }\n        case 3: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_13;\n        }\n        case 4: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_14;\n        }\n        case 5: {\n          p++;\n          goto s_n_llhttp__internal__n_url_server;\n        }\n        case 6: {\n          goto s_n_llhttp__internal__n_span_start_stub_path_1;\n        }\n        case 7: {\n          p++;\n          goto s_n_llhttp__internal__n_url_query;\n        }\n        case 8: {\n          p++;\n          goto s_n_llhttp__internal__n_error_86;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_87;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_server:\n    s_n_llhttp__internal__n_url_server: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7,\n        8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5,\n        0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0,\n        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,\n        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,\n        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,\n        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\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_server;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 2: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url;\n        }\n        case 3: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_1;\n        }\n        case 4: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_url_2;\n        }\n        case 5: {\n          p++;\n          goto s_n_llhttp__internal__n_url_server;\n        }\n        case 6: {\n          goto s_n_llhttp__internal__n_span_start_stub_path;\n        }\n        case 7: {\n          p++;\n          goto s_n_llhttp__internal__n_url_query;\n        }\n        case 8: {\n          p++;\n          goto s_n_llhttp__internal__n_url_server_with_at;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_88;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_schema_delim_1:\n    s_n_llhttp__internal__n_url_schema_delim_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_schema_delim_1;\n      }\n      switch (*p) {\n        case '/': {\n          p++;\n          goto s_n_llhttp__internal__n_url_server;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_89;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_schema_delim:\n    s_n_llhttp__internal__n_url_schema_delim: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_schema_delim;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case '/': {\n          p++;\n          goto s_n_llhttp__internal__n_url_schema_delim_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_89;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_end_stub_schema:\n    s_n_llhttp__internal__n_span_end_stub_schema: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_end_stub_schema;\n      }\n      p++;\n      goto s_n_llhttp__internal__n_url_schema_delim;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_schema:\n    s_n_llhttp__internal__n_url_schema: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        1, 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, 2, 0, 0, 0, 0, 0,\n        0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,\n        0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,\n        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,\n        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,\n        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,\n        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\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_schema;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 2: {\n          goto s_n_llhttp__internal__n_span_end_stub_schema;\n        }\n        case 3: {\n          p++;\n          goto s_n_llhttp__internal__n_url_schema;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_90;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_start:\n    s_n_llhttp__internal__n_url_start: {\n      static uint8_t lookup_table[] = {\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,\n        0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,\n        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,\n        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,\n        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,\n        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\n      };\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_start;\n      }\n      switch (lookup_table[(uint8_t) *p]) {\n        case 1: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 2: {\n          goto s_n_llhttp__internal__n_span_start_stub_path_2;\n        }\n        case 3: {\n          goto s_n_llhttp__internal__n_url_schema;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_91;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_url_1:\n    s_n_llhttp__internal__n_span_start_llhttp__on_url_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_url_1;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_url;\n      goto s_n_llhttp__internal__n_url_start;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_entry_normal:\n    s_n_llhttp__internal__n_url_entry_normal: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_entry_normal;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_url_1;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_url:\n    s_n_llhttp__internal__n_span_start_llhttp__on_url: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_url;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_url;\n      goto s_n_llhttp__internal__n_url_server;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_url_entry_connect:\n    s_n_llhttp__internal__n_url_entry_connect: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_url_entry_connect;\n      }\n      switch (*p) {\n        case 9: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        case 12: {\n          p++;\n          goto s_n_llhttp__internal__n_error_2;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_url;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_spaces_before_url:\n    s_n_llhttp__internal__n_req_spaces_before_url: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_spaces_before_url;\n      }\n      switch (*p) {\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_req_spaces_before_url;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_is_equal_method;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_first_space_before_url:\n    s_n_llhttp__internal__n_req_first_space_before_url: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_first_space_before_url;\n      }\n      switch (*p) {\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_req_spaces_before_url;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_92;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1:\n    s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1: {\n      switch (llhttp__on_method_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_req_first_space_before_url;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_29;\n        default:\n          goto s_n_llhttp__internal__n_error_111;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_2:\n    s_n_llhttp__internal__n_after_start_req_2: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_2;\n      }\n      switch (*p) {\n        case 'L': {\n          p++;\n          match = 19;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_3:\n    s_n_llhttp__internal__n_after_start_req_3: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_3;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob17, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 36;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_3;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_1:\n    s_n_llhttp__internal__n_after_start_req_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_1;\n      }\n      switch (*p) {\n        case 'C': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_2;\n        }\n        case 'N': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_3;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_4:\n    s_n_llhttp__internal__n_after_start_req_4: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_4;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob18, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 16;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_4;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_6:\n    s_n_llhttp__internal__n_after_start_req_6: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_6;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob19, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 22;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_6;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_8:\n    s_n_llhttp__internal__n_after_start_req_8: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_8;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob20, 4);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_8;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_9:\n    s_n_llhttp__internal__n_after_start_req_9: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_9;\n      }\n      switch (*p) {\n        case 'Y': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_7:\n    s_n_llhttp__internal__n_after_start_req_7: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_7;\n      }\n      switch (*p) {\n        case 'N': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_8;\n        }\n        case 'P': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_9;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_5:\n    s_n_llhttp__internal__n_after_start_req_5: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_5;\n      }\n      switch (*p) {\n        case 'H': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_6;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_7;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_12:\n    s_n_llhttp__internal__n_after_start_req_12: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_12;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob21, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_12;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_13:\n    s_n_llhttp__internal__n_after_start_req_13: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_13;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob22, 5);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 35;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_13;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_11:\n    s_n_llhttp__internal__n_after_start_req_11: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_11;\n      }\n      switch (*p) {\n        case 'L': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_12;\n        }\n        case 'S': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_13;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_10:\n    s_n_llhttp__internal__n_after_start_req_10: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_10;\n      }\n      switch (*p) {\n        case 'E': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_11;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_14:\n    s_n_llhttp__internal__n_after_start_req_14: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_14;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob23, 4);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 45;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_14;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_17:\n    s_n_llhttp__internal__n_after_start_req_17: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_17;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob25, 9);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 41;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_17;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_16:\n    s_n_llhttp__internal__n_after_start_req_16: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_16;\n      }\n      switch (*p) {\n        case '_': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_17;\n        }\n        default: {\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_15:\n    s_n_llhttp__internal__n_after_start_req_15: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_15;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob24, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_16;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_15;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_18:\n    s_n_llhttp__internal__n_after_start_req_18: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_18;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob26, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_18;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_20:\n    s_n_llhttp__internal__n_after_start_req_20: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_20;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob27, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 31;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_20;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_21:\n    s_n_llhttp__internal__n_after_start_req_21: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_21;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob28, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_21;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_19:\n    s_n_llhttp__internal__n_after_start_req_19: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_19;\n      }\n      switch (*p) {\n        case 'I': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_20;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_21;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_23:\n    s_n_llhttp__internal__n_after_start_req_23: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_23;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob29, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 24;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_23;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_24:\n    s_n_llhttp__internal__n_after_start_req_24: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_24;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob30, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 23;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_24;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_26:\n    s_n_llhttp__internal__n_after_start_req_26: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_26;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob31, 7);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 21;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_26;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_28:\n    s_n_llhttp__internal__n_after_start_req_28: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_28;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob32, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 30;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_28;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_29:\n    s_n_llhttp__internal__n_after_start_req_29: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_29;\n      }\n      switch (*p) {\n        case 'L': {\n          p++;\n          match = 10;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_27:\n    s_n_llhttp__internal__n_after_start_req_27: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_27;\n      }\n      switch (*p) {\n        case 'A': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_28;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_29;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_25:\n    s_n_llhttp__internal__n_after_start_req_25: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_25;\n      }\n      switch (*p) {\n        case 'A': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_26;\n        }\n        case 'C': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_27;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_30:\n    s_n_llhttp__internal__n_after_start_req_30: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_30;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob33, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 11;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_30;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_22:\n    s_n_llhttp__internal__n_after_start_req_22: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_22;\n      }\n      switch (*p) {\n        case '-': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_23;\n        }\n        case 'E': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_24;\n        }\n        case 'K': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_25;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_30;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_31:\n    s_n_llhttp__internal__n_after_start_req_31: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_31;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob34, 5);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 25;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_31;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_32:\n    s_n_llhttp__internal__n_after_start_req_32: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_32;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob35, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_32;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_35:\n    s_n_llhttp__internal__n_after_start_req_35: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_35;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob36, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 28;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_35;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_36:\n    s_n_llhttp__internal__n_after_start_req_36: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_36;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob37, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 39;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_36;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_34:\n    s_n_llhttp__internal__n_after_start_req_34: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_34;\n      }\n      switch (*p) {\n        case 'T': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_35;\n        }\n        case 'U': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_36;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_37:\n    s_n_llhttp__internal__n_after_start_req_37: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_37;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob38, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 38;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_37;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_38:\n    s_n_llhttp__internal__n_after_start_req_38: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_38;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob39, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_38;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_42:\n    s_n_llhttp__internal__n_after_start_req_42: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_42;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob40, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 12;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_42;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_43:\n    s_n_llhttp__internal__n_after_start_req_43: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_43;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob41, 4);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 13;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_43;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_41:\n    s_n_llhttp__internal__n_after_start_req_41: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_41;\n      }\n      switch (*p) {\n        case 'F': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_42;\n        }\n        case 'P': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_43;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_40:\n    s_n_llhttp__internal__n_after_start_req_40: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_40;\n      }\n      switch (*p) {\n        case 'P': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_41;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_39:\n    s_n_llhttp__internal__n_after_start_req_39: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_39;\n      }\n      switch (*p) {\n        case 'I': {\n          p++;\n          match = 34;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_40;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_45:\n    s_n_llhttp__internal__n_after_start_req_45: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_45;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob42, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 29;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_45;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_44:\n    s_n_llhttp__internal__n_after_start_req_44: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_44;\n      }\n      switch (*p) {\n        case 'R': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_45;\n        }\n        case 'T': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_33:\n    s_n_llhttp__internal__n_after_start_req_33: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_33;\n      }\n      switch (*p) {\n        case 'A': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_34;\n        }\n        case 'L': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_37;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_38;\n        }\n        case 'R': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_39;\n        }\n        case 'U': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_44;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_46:\n    s_n_llhttp__internal__n_after_start_req_46: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_46;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob43, 4);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 46;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_46;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_49:\n    s_n_llhttp__internal__n_after_start_req_49: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_49;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob44, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 17;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_49;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_50:\n    s_n_llhttp__internal__n_after_start_req_50: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_50;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob45, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 44;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_50;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_51:\n    s_n_llhttp__internal__n_after_start_req_51: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_51;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob46, 5);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 43;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_51;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_52:\n    s_n_llhttp__internal__n_after_start_req_52: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_52;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob47, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 20;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_52;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_48:\n    s_n_llhttp__internal__n_after_start_req_48: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_48;\n      }\n      switch (*p) {\n        case 'B': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_49;\n        }\n        case 'C': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_50;\n        }\n        case 'D': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_51;\n        }\n        case 'P': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_52;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_47:\n    s_n_llhttp__internal__n_after_start_req_47: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_47;\n      }\n      switch (*p) {\n        case 'E': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_48;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_55:\n    s_n_llhttp__internal__n_after_start_req_55: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_55;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob48, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 14;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_55;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_57:\n    s_n_llhttp__internal__n_after_start_req_57: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_57;\n      }\n      switch (*p) {\n        case 'P': {\n          p++;\n          match = 37;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_58:\n    s_n_llhttp__internal__n_after_start_req_58: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_58;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob49, 9);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 42;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_58;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_56:\n    s_n_llhttp__internal__n_after_start_req_56: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_56;\n      }\n      switch (*p) {\n        case 'U': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_57;\n        }\n        case '_': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_58;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_54:\n    s_n_llhttp__internal__n_after_start_req_54: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_54;\n      }\n      switch (*p) {\n        case 'A': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_55;\n        }\n        case 'T': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_56;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_59:\n    s_n_llhttp__internal__n_after_start_req_59: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_59;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob50, 4);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 33;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_59;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_60:\n    s_n_llhttp__internal__n_after_start_req_60: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_60;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob51, 7);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 26;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_60;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_53:\n    s_n_llhttp__internal__n_after_start_req_53: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_53;\n      }\n      switch (*p) {\n        case 'E': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_54;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_59;\n        }\n        case 'U': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_60;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_62:\n    s_n_llhttp__internal__n_after_start_req_62: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_62;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob52, 6);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 40;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_62;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_63:\n    s_n_llhttp__internal__n_after_start_req_63: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_63;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob53, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_63;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_61:\n    s_n_llhttp__internal__n_after_start_req_61: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_61;\n      }\n      switch (*p) {\n        case 'E': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_62;\n        }\n        case 'R': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_63;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_66:\n    s_n_llhttp__internal__n_after_start_req_66: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_66;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob54, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 18;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_66;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_68:\n    s_n_llhttp__internal__n_after_start_req_68: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_68;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob55, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 32;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_68;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_69:\n    s_n_llhttp__internal__n_after_start_req_69: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_69;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob56, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 15;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_69;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_67:\n    s_n_llhttp__internal__n_after_start_req_67: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_67;\n      }\n      switch (*p) {\n        case 'I': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_68;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_69;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_70:\n    s_n_llhttp__internal__n_after_start_req_70: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_70;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob57, 8);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 27;\n          goto s_n_llhttp__internal__n_invoke_store_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_after_start_req_70;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_65:\n    s_n_llhttp__internal__n_after_start_req_65: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_65;\n      }\n      switch (*p) {\n        case 'B': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_66;\n        }\n        case 'L': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_67;\n        }\n        case 'S': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_70;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req_64:\n    s_n_llhttp__internal__n_after_start_req_64: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req_64;\n      }\n      switch (*p) {\n        case 'N': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_65;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_after_start_req:\n    s_n_llhttp__internal__n_after_start_req: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_after_start_req;\n      }\n      switch (*p) {\n        case 'A': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_1;\n        }\n        case 'B': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_4;\n        }\n        case 'C': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_5;\n        }\n        case 'D': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_10;\n        }\n        case 'F': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_14;\n        }\n        case 'G': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_15;\n        }\n        case 'H': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_18;\n        }\n        case 'L': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_19;\n        }\n        case 'M': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_22;\n        }\n        case 'N': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_31;\n        }\n        case 'O': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_32;\n        }\n        case 'P': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_33;\n        }\n        case 'Q': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_46;\n        }\n        case 'R': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_47;\n        }\n        case 'S': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_53;\n        }\n        case 'T': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_61;\n        }\n        case 'U': {\n          p++;\n          goto s_n_llhttp__internal__n_after_start_req_64;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_112;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_method_1:\n    s_n_llhttp__internal__n_span_start_llhttp__on_method_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_method_1;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_method;\n      goto s_n_llhttp__internal__n_after_start_req;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_line_almost_done:\n    s_n_llhttp__internal__n_res_line_almost_done: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_line_almost_done;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_29;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_test_lenient_flags_30:\n    s_n_llhttp__internal__n_invoke_test_lenient_flags_30: {\n      switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n        case 1:\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;\n        default:\n          goto s_n_llhttp__internal__n_error_98;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_status:\n    s_n_llhttp__internal__n_res_status: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_status;\n      }\n      switch (*p) {\n        case 10: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_status;\n        }\n        case 13: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_status_1;\n        }\n        default: {\n          p++;\n          goto s_n_llhttp__internal__n_res_status;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_status:\n    s_n_llhttp__internal__n_span_start_llhttp__on_status: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_status;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_status;\n      goto s_n_llhttp__internal__n_res_status;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_status_code_otherwise:\n    s_n_llhttp__internal__n_res_status_code_otherwise: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_status_code_otherwise;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_test_lenient_flags_28;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_res_line_almost_done;\n        }\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_status;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_99;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_status_code_digit_3:\n    s_n_llhttp__internal__n_res_status_code_digit_3: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_status_code_digit_3;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_101;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_status_code_digit_2:\n    s_n_llhttp__internal__n_res_status_code_digit_2: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_status_code_digit_2;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_103;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_status_code_digit_1:\n    s_n_llhttp__internal__n_res_status_code_digit_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_status_code_digit_1;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_mul_add_status_code;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_105;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_after_version:\n    s_n_llhttp__internal__n_res_after_version: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_after_version;\n      }\n      switch (*p) {\n        case ' ': {\n          p++;\n          goto s_n_llhttp__internal__n_invoke_update_status_code;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_106;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1:\n    s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1: {\n      switch (llhttp__on_version_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_res_after_version;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_28;\n        default:\n          goto s_n_llhttp__internal__n_error_94;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_93:\n    s_n_llhttp__internal__n_error_93: {\n      state->error = 0x9;\n      state->reason = \"Invalid HTTP version\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_107:\n    s_n_llhttp__internal__n_error_107: {\n      state->error = 0x9;\n      state->reason = \"Invalid minor version\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_http_minor:\n    s_n_llhttp__internal__n_res_http_minor: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_http_minor;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_store_http_minor_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_version_7;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_108:\n    s_n_llhttp__internal__n_error_108: {\n      state->error = 0x9;\n      state->reason = \"Expected dot\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_http_dot:\n    s_n_llhttp__internal__n_res_http_dot: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_http_dot;\n      }\n      switch (*p) {\n        case '.': {\n          p++;\n          goto s_n_llhttp__internal__n_res_http_minor;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_version_8;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_109:\n    s_n_llhttp__internal__n_error_109: {\n      state->error = 0x9;\n      state->reason = \"Invalid major version\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_http_major:\n    s_n_llhttp__internal__n_res_http_major: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_http_major;\n      }\n      switch (*p) {\n        case '0': {\n          p++;\n          match = 0;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '1': {\n          p++;\n          match = 1;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '2': {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '3': {\n          p++;\n          match = 3;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '4': {\n          p++;\n          match = 4;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '5': {\n          p++;\n          match = 5;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '6': {\n          p++;\n          match = 6;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '7': {\n          p++;\n          match = 7;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '8': {\n          p++;\n          match = 8;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        case '9': {\n          p++;\n          match = 9;\n          goto s_n_llhttp__internal__n_invoke_store_http_major_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_version_9;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_version_1:\n    s_n_llhttp__internal__n_span_start_llhttp__on_version_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_version_1;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_version;\n      goto s_n_llhttp__internal__n_res_http_major;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_after_protocol:\n    s_n_llhttp__internal__n_res_after_protocol: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_after_protocol;\n      }\n      switch (*p) {\n        case '/': {\n          p++;\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_114;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3:\n    s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3: {\n      switch (llhttp__on_protocol_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_res_after_protocol;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_30;\n        default:\n          goto s_n_llhttp__internal__n_error_113;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_error_115:\n    s_n_llhttp__internal__n_error_115: {\n      state->error = 0x8;\n      state->reason = \"Expected HTTP/, RTSP/ or ICE/\";\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_error;\n      return s_error;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_after_start_1:\n    s_n_llhttp__internal__n_res_after_start_1: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_after_start_1;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob58, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_res_after_start_1;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_after_start_2:\n    s_n_llhttp__internal__n_res_after_start_2: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_after_start_2;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob59, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_res_after_start_2;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_after_start_3:\n    s_n_llhttp__internal__n_res_after_start_3: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_after_start_3;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob60, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_res_after_start_3;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_res_after_start:\n    s_n_llhttp__internal__n_res_after_start: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_res_after_start;\n      }\n      switch (*p) {\n        case 'H': {\n          p++;\n          goto s_n_llhttp__internal__n_res_after_start_1;\n        }\n        case 'I': {\n          p++;\n          goto s_n_llhttp__internal__n_res_after_start_2;\n        }\n        case 'R': {\n          p++;\n          goto s_n_llhttp__internal__n_res_after_start_3;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1:\n    s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_protocol;\n      goto s_n_llhttp__internal__n_res_after_start;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_llhttp__on_method_complete:\n    s_n_llhttp__internal__n_invoke_llhttp__on_method_complete: {\n      switch (llhttp__on_method_complete(state, p, endp)) {\n        case 0:\n          goto s_n_llhttp__internal__n_req_first_space_before_url;\n        case 21:\n          goto s_n_llhttp__internal__n_pause_26;\n        default:\n          goto s_n_llhttp__internal__n_error_1;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_or_res_method_2:\n    s_n_llhttp__internal__n_req_or_res_method_2: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_or_res_method_2;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob61, 2);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          match = 2;\n          goto s_n_llhttp__internal__n_invoke_store_method;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_req_or_res_method_2;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_110;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_update_type_1:\n    s_n_llhttp__internal__n_invoke_update_type_1: {\n      switch (llhttp__internal__c_update_type_1(state, p, endp)) {\n        default:\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_or_res_method_3:\n    s_n_llhttp__internal__n_req_or_res_method_3: {\n      llparse_match_t match_seq;\n      \n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_or_res_method_3;\n      }\n      match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob62, 3);\n      p = match_seq.current;\n      switch (match_seq.status) {\n        case kMatchComplete: {\n          p++;\n          goto s_n_llhttp__internal__n_span_end_llhttp__on_method_1;\n        }\n        case kMatchPause: {\n          return s_n_llhttp__internal__n_req_or_res_method_3;\n        }\n        case kMatchMismatch: {\n          goto s_n_llhttp__internal__n_error_110;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_or_res_method_1:\n    s_n_llhttp__internal__n_req_or_res_method_1: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_or_res_method_1;\n      }\n      switch (*p) {\n        case 'E': {\n          p++;\n          goto s_n_llhttp__internal__n_req_or_res_method_2;\n        }\n        case 'T': {\n          p++;\n          goto s_n_llhttp__internal__n_req_or_res_method_3;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_110;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_req_or_res_method:\n    s_n_llhttp__internal__n_req_or_res_method: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_req_or_res_method;\n      }\n      switch (*p) {\n        case 'H': {\n          p++;\n          goto s_n_llhttp__internal__n_req_or_res_method_1;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_error_110;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_span_start_llhttp__on_method:\n    s_n_llhttp__internal__n_span_start_llhttp__on_method: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_span_start_llhttp__on_method;\n      }\n      state->_span_pos0 = (void*) p;\n      state->_span_cb0 = llhttp__on_method;\n      goto s_n_llhttp__internal__n_req_or_res_method;\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_start_req_or_res:\n    s_n_llhttp__internal__n_start_req_or_res: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_start_req_or_res;\n      }\n      switch (*p) {\n        case 'H': {\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_method;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_update_type_2;\n        }\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_load_type:\n    s_n_llhttp__internal__n_invoke_load_type: {\n      switch (llhttp__internal__c_load_type(state, p, endp)) {\n        case 1:\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_method_1;\n        case 2:\n          goto s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1;\n        default:\n          goto s_n_llhttp__internal__n_start_req_or_res;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_invoke_update_finish:\n    s_n_llhttp__internal__n_invoke_update_finish: {\n      switch (llhttp__internal__c_update_finish(state, p, endp)) {\n        default:\n          goto s_n_llhttp__internal__n_invoke_llhttp__on_message_begin;\n      }\n      UNREACHABLE;\n    }\n    case s_n_llhttp__internal__n_start:\n    s_n_llhttp__internal__n_start: {\n      if (p == endp) {\n        return s_n_llhttp__internal__n_start;\n      }\n      switch (*p) {\n        case 10: {\n          p++;\n          goto s_n_llhttp__internal__n_start;\n        }\n        case 13: {\n          p++;\n          goto s_n_llhttp__internal__n_start;\n        }\n        default: {\n          goto s_n_llhttp__internal__n_invoke_load_initial_message_completed;\n        }\n      }\n      UNREACHABLE;\n    }\n    default:\n      UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_2: {\n    state->error = 0x7;\n    state->reason = \"Invalid characters in url\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_finish_2: {\n    switch (llhttp__internal__c_update_finish_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_start;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_initial_message_completed: {\n    switch (llhttp__internal__c_update_initial_message_completed(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_finish_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_content_length: {\n    switch (llhttp__internal__c_update_content_length(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_initial_message_completed;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_8: {\n    state->error = 0x5;\n    state->reason = \"Data after `Connection: close`\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_3: {\n    switch (llhttp__internal__c_test_lenient_flags_3(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_closed;\n      default:\n        goto s_n_llhttp__internal__n_error_8;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_2: {\n    switch (llhttp__internal__c_test_lenient_flags_2(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_update_initial_message_completed;\n      default:\n        goto s_n_llhttp__internal__n_closed;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_finish_1: {\n    switch (llhttp__internal__c_update_finish_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_test_lenient_flags_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_13: {\n    state->error = 0x15;\n    state->reason = \"on_message_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_upgrade;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_38: {\n    state->error = 0x12;\n    state->reason = \"`on_message_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_15: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_40: {\n    state->error = 0x14;\n    state->reason = \"`on_chunk_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1: {\n    switch (llhttp__on_chunk_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_15;\n      default:\n        goto s_n_llhttp__internal__n_error_40;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_2: {\n    state->error = 0x15;\n    state->reason = \"on_message_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_pause_1;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_9: {\n    state->error = 0x12;\n    state->reason = \"`on_message_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1: {\n    switch (llhttp__on_message_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_pause_1;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_2;\n      default:\n        goto s_n_llhttp__internal__n_error_9;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_36: {\n    state->error = 0xc;\n    state->reason = \"Chunk size overflow\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_10: {\n    state->error = 0xc;\n    state->reason = \"Invalid character in chunk size\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_4: {\n    switch (llhttp__internal__c_test_lenient_flags_4(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_chunk_size_otherwise;\n      default:\n        goto s_n_llhttp__internal__n_error_10;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_3: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_content_length_1;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_14: {\n    state->error = 0x14;\n    state->reason = \"`on_chunk_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete: {\n    switch (llhttp__on_chunk_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_update_content_length_1;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_3;\n      default:\n        goto s_n_llhttp__internal__n_error_14;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_13: {\n    state->error = 0x19;\n    state->reason = \"Missing expected CR after chunk data\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_6: {\n    switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete;\n      default:\n        goto s_n_llhttp__internal__n_error_13;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_15: {\n    state->error = 0x2;\n    state->reason = \"Expected LF after chunk data\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_7: {\n    switch (llhttp__internal__c_test_lenient_flags_7(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete;\n      default:\n        goto s_n_llhttp__internal__n_error_15;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_body: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_body(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_data_almost_done;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_chunk_data_almost_done;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags: {\n    switch (llhttp__internal__c_or_flags(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_field_start;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_4: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_header pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_content_length;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_12: {\n    state->error = 0x13;\n    state->reason = \"`on_chunk_header` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header: {\n    switch (llhttp__on_chunk_header(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_is_equal_content_length;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_4;\n      default:\n        goto s_n_llhttp__internal__n_error_12;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_16: {\n    state->error = 0x2;\n    state->reason = \"Expected LF after chunk size\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_8: {\n    switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header;\n      default:\n        goto s_n_llhttp__internal__n_error_16;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_11: {\n    state->error = 0x19;\n    state->reason = \"Missing expected CR after chunk size\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_5: {\n    switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_chunk_size_almost_done;\n      default:\n        goto s_n_llhttp__internal__n_error_11;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_17: {\n    state->error = 0x2;\n    state->reason = \"Invalid character in chunk extensions\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_18: {\n    state->error = 0x2;\n    state->reason = \"Invalid character in chunk extensions\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_20: {\n    state->error = 0x19;\n    state->reason = \"Missing expected CR after chunk extension name\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_5: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_extension_name pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_9;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_19: {\n    state->error = 0x22;\n    state->reason = \"`on_chunk_extension_name` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_name(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_6: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_extension_name pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_21: {\n    state->error = 0x22;\n    state->reason = \"`on_chunk_extension_name` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_name(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_7: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_extension_name pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extensions;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_22: {\n    state->error = 0x22;\n    state->reason = \"`on_chunk_extension_name` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_2: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_name(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_25: {\n    state->error = 0x19;\n    state->reason = \"Missing expected CR after chunk extension value\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_8: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_extension_value pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_10;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_24: {\n    state->error = 0x23;\n    state->reason = \"`on_chunk_extension_value` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_9: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_extension_value pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_26: {\n    state->error = 0x23;\n    state->reason = \"`on_chunk_extension_value` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_28: {\n    state->error = 0x19;\n    state->reason = \"Missing expected CR after chunk extension value\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_11: {\n    switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_chunk_size_almost_done;\n      default:\n        goto s_n_llhttp__internal__n_error_28;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_29: {\n    state->error = 0x2;\n    state->reason = \"Invalid character in chunk extensions quote value\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_10: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_extension_value pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extension_quoted_value_done;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_27: {\n    state->error = 0x23;\n    state->reason = \"`on_chunk_extension_value` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_2: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_3: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_30;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_error_30;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_4: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_31;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_error_31;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_11: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_extension_value pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extensions;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_32: {\n    state->error = 0x23;\n    state->reason = \"`on_chunk_extension_value` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_5: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_6: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_33;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_error_33;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_12: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_extension_name pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extension_value;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_23: {\n    state->error = 0x22;\n    state->reason = \"`on_chunk_extension_name` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_3: {\n    switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_chunk_extension_value;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_12;\n      default:\n        goto s_n_llhttp__internal__n_error_23;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_3: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_name(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_4: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_chunk_extension_name(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_34;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_error_34;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_35: {\n    state->error = 0xc;\n    state->reason = \"Invalid character in chunk size\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_mul_add_content_length: {\n    switch (llhttp__internal__c_mul_add_content_length(state, p, endp, match)) {\n      case 1:\n        goto s_n_llhttp__internal__n_error_36;\n      default:\n        goto s_n_llhttp__internal__n_chunk_size;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_37: {\n    state->error = 0xc;\n    state->reason = \"Invalid character in chunk size\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_body_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_body(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_finish_3: {\n    switch (llhttp__internal__c_update_finish_3(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_span_start_llhttp__on_body_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_39: {\n    state->error = 0xf;\n    state->reason = \"Request has invalid `Transfer-Encoding`\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause: {\n    state->error = 0x15;\n    state->reason = \"on_message_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_7: {\n    state->error = 0x12;\n    state->reason = \"`on_message_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_message_complete: {\n    switch (llhttp__on_message_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;\n      case 21:\n        goto s_n_llhttp__internal__n_pause;\n      default:\n        goto s_n_llhttp__internal__n_error_7;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_1: {\n    switch (llhttp__internal__c_or_flags_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_2: {\n    switch (llhttp__internal__c_or_flags_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_upgrade: {\n    switch (llhttp__internal__c_update_upgrade(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_or_flags_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_14: {\n    state->error = 0x15;\n    state->reason = \"Paused by on_headers_complete\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_6: {\n    state->error = 0x11;\n    state->reason = \"User callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete: {\n    switch (llhttp__on_headers_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_or_flags_1;\n      case 2:\n        goto s_n_llhttp__internal__n_invoke_update_upgrade;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_14;\n      default:\n        goto s_n_llhttp__internal__n_error_6;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete: {\n    switch (llhttp__before_headers_complete(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_flags: {\n    switch (llhttp__internal__c_test_flags(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1;\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_1: {\n    switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_test_flags;\n      default:\n        goto s_n_llhttp__internal__n_error_5;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_17: {\n    state->error = 0x15;\n    state->reason = \"on_chunk_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_42: {\n    state->error = 0x14;\n    state->reason = \"`on_chunk_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_2: {\n    switch (llhttp__on_chunk_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_17;\n      default:\n        goto s_n_llhttp__internal__n_error_42;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_3: {\n    switch (llhttp__internal__c_or_flags_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_4: {\n    switch (llhttp__internal__c_or_flags_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_upgrade_1: {\n    switch (llhttp__internal__c_update_upgrade(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_or_flags_4;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_16: {\n    state->error = 0x15;\n    state->reason = \"Paused by on_headers_complete\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_41: {\n    state->error = 0x11;\n    state->reason = \"User callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete_1: {\n    switch (llhttp__on_headers_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_or_flags_3;\n      case 2:\n        goto s_n_llhttp__internal__n_invoke_update_upgrade_1;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_16;\n      default:\n        goto s_n_llhttp__internal__n_error_41;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete_1: {\n    switch (llhttp__before_headers_complete(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete_1;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_flags_1: {\n    switch (llhttp__internal__c_test_flags(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_2;\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete_1;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_43: {\n    state->error = 0x2;\n    state->reason = \"Expected LF after headers\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_12: {\n    switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_test_flags_1;\n      default:\n        goto s_n_llhttp__internal__n_error_43;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_44: {\n    state->error = 0xa;\n    state->reason = \"Invalid header token\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_field: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_field(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_5;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_error_5;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_13: {\n    switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_header_field_colon_discard_ws;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_60: {\n    state->error = 0xb;\n    state->reason = \"Content-Length can't be present with Transfer-Encoding\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_47: {\n    state->error = 0xa;\n    state->reason = \"Invalid header value char\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_15: {\n    switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_header_value_discard_ws;\n      default:\n        goto s_n_llhttp__internal__n_error_47;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_49: {\n    state->error = 0xb;\n    state->reason = \"Empty Content-Length\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_18: {\n    state->error = 0x15;\n    state->reason = \"on_header_value_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_field_start;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_48: {\n    state->error = 0x1d;\n    state->reason = \"`on_header_value_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state: {\n    switch (llhttp__internal__c_update_header_state(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_5: {\n    switch (llhttp__internal__c_or_flags_5(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_6: {\n    switch (llhttp__internal__c_or_flags_6(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_7: {\n    switch (llhttp__internal__c_or_flags_7(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_8: {\n    switch (llhttp__internal__c_or_flags_8(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_header_state_2: {\n    switch (llhttp__internal__c_load_header_state(state, p, endp)) {\n      case 5:\n        goto s_n_llhttp__internal__n_invoke_or_flags_5;\n      case 6:\n        goto s_n_llhttp__internal__n_invoke_or_flags_6;\n      case 7:\n        goto s_n_llhttp__internal__n_invoke_or_flags_7;\n      case 8:\n        goto s_n_llhttp__internal__n_invoke_or_flags_8;\n      default:\n        goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_header_state_1: {\n    switch (llhttp__internal__c_load_header_state(state, p, endp)) {\n      case 2:\n        goto s_n_llhttp__internal__n_error_49;\n      default:\n        goto s_n_llhttp__internal__n_invoke_load_header_state_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_46: {\n    state->error = 0xa;\n    state->reason = \"Invalid header value char\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_14: {\n    switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_header_value_discard_lws;\n      default:\n        goto s_n_llhttp__internal__n_error_46;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_50: {\n    state->error = 0x2;\n    state->reason = \"Expected LF after CR\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_16: {\n    switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_header_value_discard_lws;\n      default:\n        goto s_n_llhttp__internal__n_error_50;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_1: {\n    switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_header_state_4: {\n    switch (llhttp__internal__c_load_header_state(state, p, endp)) {\n      case 8:\n        goto s_n_llhttp__internal__n_invoke_update_header_state_1;\n      default:\n        goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_52: {\n    state->error = 0xa;\n    state->reason = \"Unexpected whitespace after header value\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_18: {\n    switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_load_header_state_4;\n      default:\n        goto s_n_llhttp__internal__n_error_52;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_2: {\n    switch (llhttp__internal__c_update_header_state(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_9: {\n    switch (llhttp__internal__c_or_flags_5(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_10: {\n    switch (llhttp__internal__c_or_flags_6(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_11: {\n    switch (llhttp__internal__c_or_flags_7(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_12: {\n    switch (llhttp__internal__c_or_flags_8(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_header_state_5: {\n    switch (llhttp__internal__c_load_header_state(state, p, endp)) {\n      case 5:\n        goto s_n_llhttp__internal__n_invoke_or_flags_9;\n      case 6:\n        goto s_n_llhttp__internal__n_invoke_or_flags_10;\n      case 7:\n        goto s_n_llhttp__internal__n_invoke_or_flags_11;\n      case 8:\n        goto s_n_llhttp__internal__n_invoke_or_flags_12;\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_53: {\n    state->error = 0x3;\n    state->reason = \"Missing expected LF after header value\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_51: {\n    state->error = 0x19;\n    state->reason = \"Missing expected CR after header value\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_17;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_test_lenient_flags_17;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_header_value_almost_done;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_header_value_almost_done;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_header_value_almost_done;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_54;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_54;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_19: {\n    switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_header_value_lenient;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_4: {\n    switch (llhttp__internal__c_update_header_state(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_connection;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_13: {\n    switch (llhttp__internal__c_or_flags_5(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state_4;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_14: {\n    switch (llhttp__internal__c_or_flags_6(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state_4;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_15: {\n    switch (llhttp__internal__c_or_flags_7(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state_4;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_16: {\n    switch (llhttp__internal__c_or_flags_8(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_connection;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_header_state_6: {\n    switch (llhttp__internal__c_load_header_state(state, p, endp)) {\n      case 5:\n        goto s_n_llhttp__internal__n_invoke_or_flags_13;\n      case 6:\n        goto s_n_llhttp__internal__n_invoke_or_flags_14;\n      case 7:\n        goto s_n_llhttp__internal__n_invoke_or_flags_15;\n      case 8:\n        goto s_n_llhttp__internal__n_invoke_or_flags_16;\n      default:\n        goto s_n_llhttp__internal__n_header_value_connection;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_5: {\n    switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_connection_token;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_3: {\n    switch (llhttp__internal__c_update_header_state_3(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_connection_ws;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_6: {\n    switch (llhttp__internal__c_update_header_state_6(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_connection_ws;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_7: {\n    switch (llhttp__internal__c_update_header_state_7(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_connection_ws;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_6: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_56;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_56;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_mul_add_content_length_1: {\n    switch (llhttp__internal__c_mul_add_content_length_1(state, p, endp, match)) {\n      case 1:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_6;\n      default:\n        goto s_n_llhttp__internal__n_header_value_content_length;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_17: {\n    switch (llhttp__internal__c_or_flags_17(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_otherwise;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_7: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_57;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_57;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_55: {\n    state->error = 0x4;\n    state->reason = \"Duplicate Content-Length\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_flags_2: {\n    switch (llhttp__internal__c_test_flags_2(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_header_value_content_length;\n      default:\n        goto s_n_llhttp__internal__n_error_55;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_9: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_59;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_error_59;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_8: {\n    switch (llhttp__internal__c_update_header_state_8(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_otherwise;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_value_8: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_value(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_58;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_error_58;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_20: {\n    switch (llhttp__internal__c_test_lenient_flags_20(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_8;\n      default:\n        goto s_n_llhttp__internal__n_header_value_te_chunked;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_type_1: {\n    switch (llhttp__internal__c_load_type(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_test_lenient_flags_20;\n      default:\n        goto s_n_llhttp__internal__n_header_value_te_chunked;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_9: {\n    switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_and_flags: {\n    switch (llhttp__internal__c_and_flags(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_value_te_chunked;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_19: {\n    switch (llhttp__internal__c_or_flags_18(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_and_flags;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_21: {\n    switch (llhttp__internal__c_test_lenient_flags_20(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_9;\n      default:\n        goto s_n_llhttp__internal__n_invoke_or_flags_19;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_type_2: {\n    switch (llhttp__internal__c_load_type(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_test_lenient_flags_21;\n      default:\n        goto s_n_llhttp__internal__n_invoke_or_flags_19;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_18: {\n    switch (llhttp__internal__c_or_flags_18(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_and_flags;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_flags_3: {\n    switch (llhttp__internal__c_test_flags_3(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_load_type_2;\n      default:\n        goto s_n_llhttp__internal__n_invoke_or_flags_18;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_or_flags_20: {\n    switch (llhttp__internal__c_or_flags_20(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_header_state_9;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_header_state_3: {\n    switch (llhttp__internal__c_load_header_state(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_header_value_connection;\n      case 2:\n        goto s_n_llhttp__internal__n_invoke_test_flags_2;\n      case 3:\n        goto s_n_llhttp__internal__n_invoke_test_flags_3;\n      case 4:\n        goto s_n_llhttp__internal__n_invoke_or_flags_20;\n      default:\n        goto s_n_llhttp__internal__n_header_value;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_22: {\n    switch (llhttp__internal__c_test_lenient_flags_22(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_error_60;\n      default:\n        goto s_n_llhttp__internal__n_header_value_discard_ws;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_flags_4: {\n    switch (llhttp__internal__c_test_flags_4(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_test_lenient_flags_22;\n      default:\n        goto s_n_llhttp__internal__n_header_value_discard_ws;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_61: {\n    state->error = 0xf;\n    state->reason = \"Transfer-Encoding can't be present with Content-Length\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_23: {\n    switch (llhttp__internal__c_test_lenient_flags_22(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_error_61;\n      default:\n        goto s_n_llhttp__internal__n_header_value_discard_ws;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_flags_5: {\n    switch (llhttp__internal__c_test_flags_2(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_test_lenient_flags_23;\n      default:\n        goto s_n_llhttp__internal__n_header_value_discard_ws;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_19: {\n    state->error = 0x15;\n    state->reason = \"on_header_field_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_header_state;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_45: {\n    state->error = 0x1c;\n    state->reason = \"`on_header_field_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_field(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_header_field(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_62: {\n    state->error = 0xa;\n    state->reason = \"Invalid header token\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_10: {\n    switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_field_general;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_store_header_state: {\n    switch (llhttp__internal__c_store_header_state(state, p, endp, match)) {\n      default:\n        goto s_n_llhttp__internal__n_header_field_colon;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_header_state_11: {\n    switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_header_field_general;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_4: {\n    state->error = 0x1e;\n    state->reason = \"Unexpected space after start line\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags: {\n    switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_header_field_start;\n      default:\n        goto s_n_llhttp__internal__n_error_4;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_20: {\n    state->error = 0x15;\n    state->reason = \"on_url_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_headers_start;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_3: {\n    state->error = 0x1a;\n    state->reason = \"`on_url_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_url_complete: {\n    switch (llhttp__on_url_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_headers_start;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_20;\n      default:\n        goto s_n_llhttp__internal__n_error_3;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_http_minor: {\n    switch (llhttp__internal__c_update_http_minor(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_http_major: {\n    switch (llhttp__internal__c_update_http_major(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_http_minor;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_3: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_63: {\n    state->error = 0x7;\n    state->reason = \"Expected CRLF\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_4: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_lf_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_72: {\n    state->error = 0x17;\n    state->reason = \"Pause on PRI/Upgrade\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_73: {\n    state->error = 0x9;\n    state->reason = \"Expected HTTP/2 Connection Preface\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_70: {\n    state->error = 0x2;\n    state->reason = \"Expected CRLF after version\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_26: {\n    switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_headers_start;\n      default:\n        goto s_n_llhttp__internal__n_error_70;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_69: {\n    state->error = 0x9;\n    state->reason = \"Expected CRLF after version\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_25: {\n    switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_req_http_complete_crlf;\n      default:\n        goto s_n_llhttp__internal__n_error_69;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_71: {\n    state->error = 0x9;\n    state->reason = \"Expected CRLF after version\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_21: {\n    state->error = 0x15;\n    state->reason = \"on_version_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_1;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_68: {\n    state->error = 0x21;\n    state->reason = \"`on_version_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_version_complete;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_version_complete;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_67;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_67;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_http_minor: {\n    switch (llhttp__internal__c_load_http_minor(state, p, endp)) {\n      case 9:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_http_minor_1: {\n    switch (llhttp__internal__c_load_http_minor(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;\n      case 1:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_http_minor_2: {\n    switch (llhttp__internal__c_load_http_minor(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_http_major: {\n    switch (llhttp__internal__c_load_http_major(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_load_http_minor;\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_load_http_minor_1;\n      case 2:\n        goto s_n_llhttp__internal__n_invoke_load_http_minor_2;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_24: {\n    switch (llhttp__internal__c_test_lenient_flags_24(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;\n      default:\n        goto s_n_llhttp__internal__n_invoke_load_http_major;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_store_http_minor: {\n    switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_test_lenient_flags_24;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_2: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_74;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_74;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_3: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_75;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_75;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_store_http_major: {\n    switch (llhttp__internal__c_store_http_major(state, p, endp, match)) {\n      default:\n        goto s_n_llhttp__internal__n_req_http_dot;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_4: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_76;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_76;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_77: {\n    state->error = 0x8;\n    state->reason = \"Expected HTTP/, RTSP/ or ICE/\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_66: {\n    state->error = 0x8;\n    state->reason = \"Invalid method for HTTP/x.x request\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_22: {\n    state->error = 0x15;\n    state->reason = \"on_protocol_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_65: {\n    state->error = 0x26;\n    state->reason = \"`on_protocol_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_protocol: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_protocol(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_protocol(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_82;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_82;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_79: {\n    state->error = 0x8;\n    state->reason = \"Expected SOURCE method for ICE/x.x request\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_23: {\n    state->error = 0x15;\n    state->reason = \"on_protocol_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_2;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_78: {\n    state->error = 0x26;\n    state->reason = \"`on_protocol_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_protocol_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_protocol(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_81: {\n    state->error = 0x8;\n    state->reason = \"Invalid method for RTSP/x.x request\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_24: {\n    state->error = 0x15;\n    state->reason = \"on_protocol_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_3;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_80: {\n    state->error = 0x26;\n    state->reason = \"`on_protocol_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_protocol_2: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_protocol(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_25: {\n    state->error = 0x15;\n    state->reason = \"on_url_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_http_start;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_64: {\n    state->error = 0x1a;\n    state->reason = \"`on_url_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1: {\n    switch (llhttp__on_url_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_req_http_start;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_25;\n      default:\n        goto s_n_llhttp__internal__n_error_64;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_5: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_6: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_7: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_lf_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_8: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_83: {\n    state->error = 0x7;\n    state->reason = \"Invalid char in url fragment start\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_9: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_10: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_lf_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_11: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_84: {\n    state->error = 0x7;\n    state->reason = \"Invalid char in url query\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_85: {\n    state->error = 0x7;\n    state->reason = \"Invalid char in url path\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_lf_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_2: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_12: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_13: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_lf_to_http09;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_url_14: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_url(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_url_skip_to_http;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_86: {\n    state->error = 0x7;\n    state->reason = \"Double @ in url\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_87: {\n    state->error = 0x7;\n    state->reason = \"Unexpected char in url server\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_88: {\n    state->error = 0x7;\n    state->reason = \"Unexpected char in url server\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_89: {\n    state->error = 0x7;\n    state->reason = \"Unexpected char in url schema\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_90: {\n    state->error = 0x7;\n    state->reason = \"Unexpected char in url schema\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_91: {\n    state->error = 0x7;\n    state->reason = \"Unexpected start char in url\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_is_equal_method: {\n    switch (llhttp__internal__c_is_equal_method(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_url_entry_normal;\n      default:\n        goto s_n_llhttp__internal__n_url_entry_connect;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_92: {\n    state->error = 0x6;\n    state->reason = \"Expected space after method\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_29: {\n    state->error = 0x15;\n    state->reason = \"on_method_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_first_space_before_url;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_111: {\n    state->error = 0x20;\n    state->reason = \"`on_method_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_method_2: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_method(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_store_method_1: {\n    switch (llhttp__internal__c_store_method(state, p, endp, match)) {\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_method_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_112: {\n    state->error = 0x6;\n    state->reason = \"Invalid method encountered\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_104: {\n    state->error = 0xd;\n    state->reason = \"Invalid status code\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_102: {\n    state->error = 0xd;\n    state->reason = \"Invalid status code\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_100: {\n    state->error = 0xd;\n    state->reason = \"Invalid status code\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_27: {\n    state->error = 0x15;\n    state->reason = \"on_status_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_headers_start;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_96: {\n    state->error = 0x1b;\n    state->reason = \"`on_status_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_status_complete: {\n    switch (llhttp__on_status_complete(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_headers_start;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_27;\n      default:\n        goto s_n_llhttp__internal__n_error_96;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_95: {\n    state->error = 0xd;\n    state->reason = \"Invalid response status\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_28: {\n    switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;\n      default:\n        goto s_n_llhttp__internal__n_error_95;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_97: {\n    state->error = 0x2;\n    state->reason = \"Expected LF after CR\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_29: {\n    switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;\n      default:\n        goto s_n_llhttp__internal__n_error_97;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_98: {\n    state->error = 0x19;\n    state->reason = \"Missing expected CR after response line\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_status: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_status(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_30;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_invoke_test_lenient_flags_30;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_status_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_status(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) (p + 1);\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_line_almost_done;\n      return s_error;\n    }\n    p++;\n    goto s_n_llhttp__internal__n_res_line_almost_done;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_99: {\n    state->error = 0xd;\n    state->reason = \"Invalid response status\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_mul_add_status_code_2: {\n    switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) {\n      case 1:\n        goto s_n_llhttp__internal__n_error_100;\n      default:\n        goto s_n_llhttp__internal__n_res_status_code_otherwise;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_101: {\n    state->error = 0xd;\n    state->reason = \"Invalid status code\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_mul_add_status_code_1: {\n    switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) {\n      case 1:\n        goto s_n_llhttp__internal__n_error_102;\n      default:\n        goto s_n_llhttp__internal__n_res_status_code_digit_3;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_103: {\n    state->error = 0xd;\n    state->reason = \"Invalid status code\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_mul_add_status_code: {\n    switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) {\n      case 1:\n        goto s_n_llhttp__internal__n_error_104;\n      default:\n        goto s_n_llhttp__internal__n_res_status_code_digit_2;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_105: {\n    state->error = 0xd;\n    state->reason = \"Invalid status code\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_status_code: {\n    switch (llhttp__internal__c_update_status_code(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_res_status_code_digit_1;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_106: {\n    state->error = 0x9;\n    state->reason = \"Expected space after version\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_28: {\n    state->error = 0x15;\n    state->reason = \"on_version_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_after_version;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_94: {\n    state->error = 0x21;\n    state->reason = \"`on_version_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_6: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_5: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_93;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_93;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_http_minor_3: {\n    switch (llhttp__internal__c_load_http_minor(state, p, endp)) {\n      case 9:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_http_minor_4: {\n    switch (llhttp__internal__c_load_http_minor(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;\n      case 1:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_http_minor_5: {\n    switch (llhttp__internal__c_load_http_minor(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_http_major_1: {\n    switch (llhttp__internal__c_load_http_major(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_load_http_minor_3;\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_load_http_minor_4;\n      case 2:\n        goto s_n_llhttp__internal__n_invoke_load_http_minor_5;\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_test_lenient_flags_27: {\n    switch (llhttp__internal__c_test_lenient_flags_24(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;\n      default:\n        goto s_n_llhttp__internal__n_invoke_load_http_major_1;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_store_http_minor_1: {\n    switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_test_lenient_flags_27;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_7: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_107;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_107;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_8: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_108;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_108;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_store_http_major_1: {\n    switch (llhttp__internal__c_store_http_major(state, p, endp, match)) {\n      default:\n        goto s_n_llhttp__internal__n_res_http_dot;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_version_9: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_version(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_109;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_109;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_114: {\n    state->error = 0x8;\n    state->reason = \"Expected HTTP/, RTSP/ or ICE/\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_30: {\n    state->error = 0x15;\n    state->reason = \"on_protocol_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_after_protocol;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_113: {\n    state->error = 0x26;\n    state->reason = \"`on_protocol_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_protocol(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_protocol(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_115;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_error_115;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_26: {\n    state->error = 0x15;\n    state->reason = \"on_method_complete pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_first_space_before_url;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_1: {\n    state->error = 0x20;\n    state->reason = \"`on_method_complete` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_method: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_method(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_method_complete;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_llhttp__on_method_complete;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_type: {\n    switch (llhttp__internal__c_update_type(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_span_end_llhttp__on_method;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_store_method: {\n    switch (llhttp__internal__c_store_method(state, p, endp, match)) {\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_type;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_110: {\n    state->error = 0x8;\n    state->reason = \"Invalid word encountered\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_span_end_llhttp__on_method_1: {\n    const unsigned char* start;\n    int err;\n    \n    start = state->_span_pos0;\n    state->_span_pos0 = NULL;\n    err = llhttp__on_method(state, start, p);\n    if (err != 0) {\n      state->error = err;\n      state->error_pos = (const char*) p;\n      state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_type_1;\n      return s_error;\n    }\n    goto s_n_llhttp__internal__n_invoke_update_type_1;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_update_type_2: {\n    switch (llhttp__internal__c_update_type(state, p, endp)) {\n      default:\n        goto s_n_llhttp__internal__n_span_start_llhttp__on_method_1;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_31: {\n    state->error = 0x15;\n    state->reason = \"on_message_begin pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_type;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error: {\n    state->error = 0x10;\n    state->reason = \"`on_message_begin` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_message_begin: {\n    switch (llhttp__on_message_begin(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_load_type;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_31;\n      default:\n        goto s_n_llhttp__internal__n_error;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_pause_32: {\n    state->error = 0x15;\n    state->reason = \"on_reset pause\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_finish;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_error_116: {\n    state->error = 0x1f;\n    state->reason = \"`on_reset` callback error\";\n    state->error_pos = (const char*) p;\n    state->_current = (void*) (intptr_t) s_error;\n    return s_error;\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_llhttp__on_reset: {\n    switch (llhttp__on_reset(state, p, endp)) {\n      case 0:\n        goto s_n_llhttp__internal__n_invoke_update_finish;\n      case 21:\n        goto s_n_llhttp__internal__n_pause_32;\n      default:\n        goto s_n_llhttp__internal__n_error_116;\n    }\n    UNREACHABLE;\n  }\n  s_n_llhttp__internal__n_invoke_load_initial_message_completed: {\n    switch (llhttp__internal__c_load_initial_message_completed(state, p, endp)) {\n      case 1:\n        goto s_n_llhttp__internal__n_invoke_llhttp__on_reset;\n      default:\n        goto s_n_llhttp__internal__n_invoke_update_finish;\n    }\n    UNREACHABLE;\n  }\n}\n\nint llhttp__internal_execute(llhttp__internal_t* state, const char* p, const char* endp) {\n  llparse_state_t next;\n\n  /* check lingering errors */\n  if (state->error != 0) {\n    return state->error;\n  }\n\n  /* restart spans */\n  if (state->_span_pos0 != NULL) {\n    state->_span_pos0 = (void*) p;\n  }\n  \n  next = llhttp__internal__run(state, (const unsigned char*) p, (const unsigned char*) endp);\n  if (next == s_error) {\n    return state->error;\n  }\n  state->_current = (void*) (intptr_t) next;\n\n  /* execute spans */\n  if (state->_span_pos0 != NULL) {\n    int error;\n  \n    error = ((llhttp__internal__span_cb) state->_span_cb0)(state, state->_span_pos0, (const char*) endp);\n    if (error != 0) {\n      state->error = error;\n      state->error_pos = endp;\n      return error;\n    }\n  }\n  \n  return 0;\n}"
  },
  {
    "path": "thirdparty/llhttp/llhttp.h",
    "content": "\n#ifndef INCLUDE_LLHTTP_H_\n#define INCLUDE_LLHTTP_H_\n\n#define LLHTTP_VERSION_MAJOR 9\n#define LLHTTP_VERSION_MINOR 3\n#define LLHTTP_VERSION_PATCH 0\n\n#ifndef INCLUDE_LLHTTP_ITSELF_H_\n#define INCLUDE_LLHTTP_ITSELF_H_\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\ntypedef struct llhttp__internal_s llhttp__internal_t;\nstruct llhttp__internal_s {\n  int32_t _index;\n  void* _span_pos0;\n  void* _span_cb0;\n  int32_t error;\n  const char* reason;\n  const char* error_pos;\n  void* data;\n  void* _current;\n  uint64_t content_length;\n  uint8_t type;\n  uint8_t method;\n  uint8_t http_major;\n  uint8_t http_minor;\n  uint8_t header_state;\n  uint16_t lenient_flags;\n  uint8_t upgrade;\n  uint8_t finish;\n  uint16_t flags;\n  uint16_t status_code;\n  uint8_t initial_message_completed;\n  void* settings;\n};\n\nint llhttp__internal_init(llhttp__internal_t* s);\nint llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp);\n\n#ifdef __cplusplus\n}  /* extern \"C\" */\n#endif\n#endif  /* INCLUDE_LLHTTP_ITSELF_H_ */\n\n\n#ifndef LLLLHTTP_C_HEADERS_\n#define LLLLHTTP_C_HEADERS_\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nenum llhttp_errno {\n  HPE_OK = 0,\n  HPE_INTERNAL = 1,\n  HPE_STRICT = 2,\n  HPE_CR_EXPECTED = 25,\n  HPE_LF_EXPECTED = 3,\n  HPE_UNEXPECTED_CONTENT_LENGTH = 4,\n  HPE_UNEXPECTED_SPACE = 30,\n  HPE_CLOSED_CONNECTION = 5,\n  HPE_INVALID_METHOD = 6,\n  HPE_INVALID_URL = 7,\n  HPE_INVALID_CONSTANT = 8,\n  HPE_INVALID_VERSION = 9,\n  HPE_INVALID_HEADER_TOKEN = 10,\n  HPE_INVALID_CONTENT_LENGTH = 11,\n  HPE_INVALID_CHUNK_SIZE = 12,\n  HPE_INVALID_STATUS = 13,\n  HPE_INVALID_EOF_STATE = 14,\n  HPE_INVALID_TRANSFER_ENCODING = 15,\n  HPE_CB_MESSAGE_BEGIN = 16,\n  HPE_CB_HEADERS_COMPLETE = 17,\n  HPE_CB_MESSAGE_COMPLETE = 18,\n  HPE_CB_CHUNK_HEADER = 19,\n  HPE_CB_CHUNK_COMPLETE = 20,\n  HPE_PAUSED = 21,\n  HPE_PAUSED_UPGRADE = 22,\n  HPE_PAUSED_H2_UPGRADE = 23,\n  HPE_USER = 24,\n  HPE_CB_URL_COMPLETE = 26,\n  HPE_CB_STATUS_COMPLETE = 27,\n  HPE_CB_METHOD_COMPLETE = 32,\n  HPE_CB_VERSION_COMPLETE = 33,\n  HPE_CB_HEADER_FIELD_COMPLETE = 28,\n  HPE_CB_HEADER_VALUE_COMPLETE = 29,\n  HPE_CB_CHUNK_EXTENSION_NAME_COMPLETE = 34,\n  HPE_CB_CHUNK_EXTENSION_VALUE_COMPLETE = 35,\n  HPE_CB_RESET = 31,\n  HPE_CB_PROTOCOL_COMPLETE = 38\n};\ntypedef enum llhttp_errno llhttp_errno_t;\n\nenum llhttp_flags {\n  F_CONNECTION_KEEP_ALIVE = 0x1,\n  F_CONNECTION_CLOSE = 0x2,\n  F_CONNECTION_UPGRADE = 0x4,\n  F_CHUNKED = 0x8,\n  F_UPGRADE = 0x10,\n  F_CONTENT_LENGTH = 0x20,\n  F_SKIPBODY = 0x40,\n  F_TRAILING = 0x80,\n  F_TRANSFER_ENCODING = 0x200\n};\ntypedef enum llhttp_flags llhttp_flags_t;\n\nenum llhttp_lenient_flags {\n  LENIENT_HEADERS = 0x1,\n  LENIENT_CHUNKED_LENGTH = 0x2,\n  LENIENT_KEEP_ALIVE = 0x4,\n  LENIENT_TRANSFER_ENCODING = 0x8,\n  LENIENT_VERSION = 0x10,\n  LENIENT_DATA_AFTER_CLOSE = 0x20,\n  LENIENT_OPTIONAL_LF_AFTER_CR = 0x40,\n  LENIENT_OPTIONAL_CRLF_AFTER_CHUNK = 0x80,\n  LENIENT_OPTIONAL_CR_BEFORE_LF = 0x100,\n  LENIENT_SPACES_AFTER_CHUNK_SIZE = 0x200\n};\ntypedef enum llhttp_lenient_flags llhttp_lenient_flags_t;\n\nenum llhttp_type {\n  HTTP_BOTH = 0,\n  HTTP_REQUEST = 1,\n  HTTP_RESPONSE = 2\n};\ntypedef enum llhttp_type llhttp_type_t;\n\nenum llhttp_finish {\n  HTTP_FINISH_SAFE = 0,\n  HTTP_FINISH_SAFE_WITH_CB = 1,\n  HTTP_FINISH_UNSAFE = 2\n};\ntypedef enum llhttp_finish llhttp_finish_t;\n\nenum llhttp_method {\n  HTTP_DELETE = 0,\n  HTTP_GET = 1,\n  HTTP_HEAD = 2,\n  HTTP_POST = 3,\n  HTTP_PUT = 4,\n  HTTP_CONNECT = 5,\n  HTTP_OPTIONS = 6,\n  HTTP_TRACE = 7,\n  HTTP_COPY = 8,\n  HTTP_LOCK = 9,\n  HTTP_MKCOL = 10,\n  HTTP_MOVE = 11,\n  HTTP_PROPFIND = 12,\n  HTTP_PROPPATCH = 13,\n  HTTP_SEARCH = 14,\n  HTTP_UNLOCK = 15,\n  HTTP_BIND = 16,\n  HTTP_REBIND = 17,\n  HTTP_UNBIND = 18,\n  HTTP_ACL = 19,\n  HTTP_REPORT = 20,\n  HTTP_MKACTIVITY = 21,\n  HTTP_CHECKOUT = 22,\n  HTTP_MERGE = 23,\n  HTTP_MSEARCH = 24,\n  HTTP_NOTIFY = 25,\n  HTTP_SUBSCRIBE = 26,\n  HTTP_UNSUBSCRIBE = 27,\n  HTTP_PATCH = 28,\n  HTTP_PURGE = 29,\n  HTTP_MKCALENDAR = 30,\n  HTTP_LINK = 31,\n  HTTP_UNLINK = 32,\n  HTTP_SOURCE = 33,\n  HTTP_PRI = 34,\n  HTTP_DESCRIBE = 35,\n  HTTP_ANNOUNCE = 36,\n  HTTP_SETUP = 37,\n  HTTP_PLAY = 38,\n  HTTP_PAUSE = 39,\n  HTTP_TEARDOWN = 40,\n  HTTP_GET_PARAMETER = 41,\n  HTTP_SET_PARAMETER = 42,\n  HTTP_REDIRECT = 43,\n  HTTP_RECORD = 44,\n  HTTP_FLUSH = 45,\n  HTTP_QUERY = 46\n};\ntypedef enum llhttp_method llhttp_method_t;\n\nenum llhttp_status {\n  HTTP_STATUS_CONTINUE = 100,\n  HTTP_STATUS_SWITCHING_PROTOCOLS = 101,\n  HTTP_STATUS_PROCESSING = 102,\n  HTTP_STATUS_EARLY_HINTS = 103,\n  HTTP_STATUS_RESPONSE_IS_STALE = 110,\n  HTTP_STATUS_REVALIDATION_FAILED = 111,\n  HTTP_STATUS_DISCONNECTED_OPERATION = 112,\n  HTTP_STATUS_HEURISTIC_EXPIRATION = 113,\n  HTTP_STATUS_MISCELLANEOUS_WARNING = 199,\n  HTTP_STATUS_OK = 200,\n  HTTP_STATUS_CREATED = 201,\n  HTTP_STATUS_ACCEPTED = 202,\n  HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203,\n  HTTP_STATUS_NO_CONTENT = 204,\n  HTTP_STATUS_RESET_CONTENT = 205,\n  HTTP_STATUS_PARTIAL_CONTENT = 206,\n  HTTP_STATUS_MULTI_STATUS = 207,\n  HTTP_STATUS_ALREADY_REPORTED = 208,\n  HTTP_STATUS_TRANSFORMATION_APPLIED = 214,\n  HTTP_STATUS_IM_USED = 226,\n  HTTP_STATUS_MISCELLANEOUS_PERSISTENT_WARNING = 299,\n  HTTP_STATUS_MULTIPLE_CHOICES = 300,\n  HTTP_STATUS_MOVED_PERMANENTLY = 301,\n  HTTP_STATUS_FOUND = 302,\n  HTTP_STATUS_SEE_OTHER = 303,\n  HTTP_STATUS_NOT_MODIFIED = 304,\n  HTTP_STATUS_USE_PROXY = 305,\n  HTTP_STATUS_SWITCH_PROXY = 306,\n  HTTP_STATUS_TEMPORARY_REDIRECT = 307,\n  HTTP_STATUS_PERMANENT_REDIRECT = 308,\n  HTTP_STATUS_BAD_REQUEST = 400,\n  HTTP_STATUS_UNAUTHORIZED = 401,\n  HTTP_STATUS_PAYMENT_REQUIRED = 402,\n  HTTP_STATUS_FORBIDDEN = 403,\n  HTTP_STATUS_NOT_FOUND = 404,\n  HTTP_STATUS_METHOD_NOT_ALLOWED = 405,\n  HTTP_STATUS_NOT_ACCEPTABLE = 406,\n  HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407,\n  HTTP_STATUS_REQUEST_TIMEOUT = 408,\n  HTTP_STATUS_CONFLICT = 409,\n  HTTP_STATUS_GONE = 410,\n  HTTP_STATUS_LENGTH_REQUIRED = 411,\n  HTTP_STATUS_PRECONDITION_FAILED = 412,\n  HTTP_STATUS_PAYLOAD_TOO_LARGE = 413,\n  HTTP_STATUS_URI_TOO_LONG = 414,\n  HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415,\n  HTTP_STATUS_RANGE_NOT_SATISFIABLE = 416,\n  HTTP_STATUS_EXPECTATION_FAILED = 417,\n  HTTP_STATUS_IM_A_TEAPOT = 418,\n  HTTP_STATUS_PAGE_EXPIRED = 419,\n  HTTP_STATUS_ENHANCE_YOUR_CALM = 420,\n  HTTP_STATUS_MISDIRECTED_REQUEST = 421,\n  HTTP_STATUS_UNPROCESSABLE_ENTITY = 422,\n  HTTP_STATUS_LOCKED = 423,\n  HTTP_STATUS_FAILED_DEPENDENCY = 424,\n  HTTP_STATUS_TOO_EARLY = 425,\n  HTTP_STATUS_UPGRADE_REQUIRED = 426,\n  HTTP_STATUS_PRECONDITION_REQUIRED = 428,\n  HTTP_STATUS_TOO_MANY_REQUESTS = 429,\n  HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL = 430,\n  HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,\n  HTTP_STATUS_LOGIN_TIMEOUT = 440,\n  HTTP_STATUS_NO_RESPONSE = 444,\n  HTTP_STATUS_RETRY_WITH = 449,\n  HTTP_STATUS_BLOCKED_BY_PARENTAL_CONTROL = 450,\n  HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451,\n  HTTP_STATUS_CLIENT_CLOSED_LOAD_BALANCED_REQUEST = 460,\n  HTTP_STATUS_INVALID_X_FORWARDED_FOR = 463,\n  HTTP_STATUS_REQUEST_HEADER_TOO_LARGE = 494,\n  HTTP_STATUS_SSL_CERTIFICATE_ERROR = 495,\n  HTTP_STATUS_SSL_CERTIFICATE_REQUIRED = 496,\n  HTTP_STATUS_HTTP_REQUEST_SENT_TO_HTTPS_PORT = 497,\n  HTTP_STATUS_INVALID_TOKEN = 498,\n  HTTP_STATUS_CLIENT_CLOSED_REQUEST = 499,\n  HTTP_STATUS_INTERNAL_SERVER_ERROR = 500,\n  HTTP_STATUS_NOT_IMPLEMENTED = 501,\n  HTTP_STATUS_BAD_GATEWAY = 502,\n  HTTP_STATUS_SERVICE_UNAVAILABLE = 503,\n  HTTP_STATUS_GATEWAY_TIMEOUT = 504,\n  HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505,\n  HTTP_STATUS_VARIANT_ALSO_NEGOTIATES = 506,\n  HTTP_STATUS_INSUFFICIENT_STORAGE = 507,\n  HTTP_STATUS_LOOP_DETECTED = 508,\n  HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509,\n  HTTP_STATUS_NOT_EXTENDED = 510,\n  HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511,\n  HTTP_STATUS_WEB_SERVER_UNKNOWN_ERROR = 520,\n  HTTP_STATUS_WEB_SERVER_IS_DOWN = 521,\n  HTTP_STATUS_CONNECTION_TIMEOUT = 522,\n  HTTP_STATUS_ORIGIN_IS_UNREACHABLE = 523,\n  HTTP_STATUS_TIMEOUT_OCCURED = 524,\n  HTTP_STATUS_SSL_HANDSHAKE_FAILED = 525,\n  HTTP_STATUS_INVALID_SSL_CERTIFICATE = 526,\n  HTTP_STATUS_RAILGUN_ERROR = 527,\n  HTTP_STATUS_SITE_IS_OVERLOADED = 529,\n  HTTP_STATUS_SITE_IS_FROZEN = 530,\n  HTTP_STATUS_IDENTITY_PROVIDER_AUTHENTICATION_ERROR = 561,\n  HTTP_STATUS_NETWORK_READ_TIMEOUT = 598,\n  HTTP_STATUS_NETWORK_CONNECT_TIMEOUT = 599\n};\ntypedef enum llhttp_status llhttp_status_t;\n\n#define HTTP_ERRNO_MAP(XX) \\\n  XX(0, OK, OK) \\\n  XX(1, INTERNAL, INTERNAL) \\\n  XX(2, STRICT, STRICT) \\\n  XX(25, CR_EXPECTED, CR_EXPECTED) \\\n  XX(3, LF_EXPECTED, LF_EXPECTED) \\\n  XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \\\n  XX(30, UNEXPECTED_SPACE, UNEXPECTED_SPACE) \\\n  XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \\\n  XX(6, INVALID_METHOD, INVALID_METHOD) \\\n  XX(7, INVALID_URL, INVALID_URL) \\\n  XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \\\n  XX(9, INVALID_VERSION, INVALID_VERSION) \\\n  XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \\\n  XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \\\n  XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \\\n  XX(13, INVALID_STATUS, INVALID_STATUS) \\\n  XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \\\n  XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \\\n  XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \\\n  XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \\\n  XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \\\n  XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \\\n  XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \\\n  XX(21, PAUSED, PAUSED) \\\n  XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \\\n  XX(23, PAUSED_H2_UPGRADE, PAUSED_H2_UPGRADE) \\\n  XX(24, USER, USER) \\\n  XX(26, CB_URL_COMPLETE, CB_URL_COMPLETE) \\\n  XX(27, CB_STATUS_COMPLETE, CB_STATUS_COMPLETE) \\\n  XX(32, CB_METHOD_COMPLETE, CB_METHOD_COMPLETE) \\\n  XX(33, CB_VERSION_COMPLETE, CB_VERSION_COMPLETE) \\\n  XX(28, CB_HEADER_FIELD_COMPLETE, CB_HEADER_FIELD_COMPLETE) \\\n  XX(29, CB_HEADER_VALUE_COMPLETE, CB_HEADER_VALUE_COMPLETE) \\\n  XX(34, CB_CHUNK_EXTENSION_NAME_COMPLETE, CB_CHUNK_EXTENSION_NAME_COMPLETE) \\\n  XX(35, CB_CHUNK_EXTENSION_VALUE_COMPLETE, CB_CHUNK_EXTENSION_VALUE_COMPLETE) \\\n  XX(31, CB_RESET, CB_RESET) \\\n  XX(38, CB_PROTOCOL_COMPLETE, CB_PROTOCOL_COMPLETE) \\\n\n\n#define HTTP_METHOD_MAP(XX) \\\n  XX(0, DELETE, DELETE) \\\n  XX(1, GET, GET) \\\n  XX(2, HEAD, HEAD) \\\n  XX(3, POST, POST) \\\n  XX(4, PUT, PUT) \\\n  XX(5, CONNECT, CONNECT) \\\n  XX(6, OPTIONS, OPTIONS) \\\n  XX(7, TRACE, TRACE) \\\n  XX(8, COPY, COPY) \\\n  XX(9, LOCK, LOCK) \\\n  XX(10, MKCOL, MKCOL) \\\n  XX(11, MOVE, MOVE) \\\n  XX(12, PROPFIND, PROPFIND) \\\n  XX(13, PROPPATCH, PROPPATCH) \\\n  XX(14, SEARCH, SEARCH) \\\n  XX(15, UNLOCK, UNLOCK) \\\n  XX(16, BIND, BIND) \\\n  XX(17, REBIND, REBIND) \\\n  XX(18, UNBIND, UNBIND) \\\n  XX(19, ACL, ACL) \\\n  XX(20, REPORT, REPORT) \\\n  XX(21, MKACTIVITY, MKACTIVITY) \\\n  XX(22, CHECKOUT, CHECKOUT) \\\n  XX(23, MERGE, MERGE) \\\n  XX(24, MSEARCH, M-SEARCH) \\\n  XX(25, NOTIFY, NOTIFY) \\\n  XX(26, SUBSCRIBE, SUBSCRIBE) \\\n  XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \\\n  XX(28, PATCH, PATCH) \\\n  XX(29, PURGE, PURGE) \\\n  XX(30, MKCALENDAR, MKCALENDAR) \\\n  XX(31, LINK, LINK) \\\n  XX(32, UNLINK, UNLINK) \\\n  XX(33, SOURCE, SOURCE) \\\n  XX(46, QUERY, QUERY) \\\n\n\n#define RTSP_METHOD_MAP(XX) \\\n  XX(1, GET, GET) \\\n  XX(3, POST, POST) \\\n  XX(6, OPTIONS, OPTIONS) \\\n  XX(35, DESCRIBE, DESCRIBE) \\\n  XX(36, ANNOUNCE, ANNOUNCE) \\\n  XX(37, SETUP, SETUP) \\\n  XX(38, PLAY, PLAY) \\\n  XX(39, PAUSE, PAUSE) \\\n  XX(40, TEARDOWN, TEARDOWN) \\\n  XX(41, GET_PARAMETER, GET_PARAMETER) \\\n  XX(42, SET_PARAMETER, SET_PARAMETER) \\\n  XX(43, REDIRECT, REDIRECT) \\\n  XX(44, RECORD, RECORD) \\\n  XX(45, FLUSH, FLUSH) \\\n\n\n#define HTTP_ALL_METHOD_MAP(XX) \\\n  XX(0, DELETE, DELETE) \\\n  XX(1, GET, GET) \\\n  XX(2, HEAD, HEAD) \\\n  XX(3, POST, POST) \\\n  XX(4, PUT, PUT) \\\n  XX(5, CONNECT, CONNECT) \\\n  XX(6, OPTIONS, OPTIONS) \\\n  XX(7, TRACE, TRACE) \\\n  XX(8, COPY, COPY) \\\n  XX(9, LOCK, LOCK) \\\n  XX(10, MKCOL, MKCOL) \\\n  XX(11, MOVE, MOVE) \\\n  XX(12, PROPFIND, PROPFIND) \\\n  XX(13, PROPPATCH, PROPPATCH) \\\n  XX(14, SEARCH, SEARCH) \\\n  XX(15, UNLOCK, UNLOCK) \\\n  XX(16, BIND, BIND) \\\n  XX(17, REBIND, REBIND) \\\n  XX(18, UNBIND, UNBIND) \\\n  XX(19, ACL, ACL) \\\n  XX(20, REPORT, REPORT) \\\n  XX(21, MKACTIVITY, MKACTIVITY) \\\n  XX(22, CHECKOUT, CHECKOUT) \\\n  XX(23, MERGE, MERGE) \\\n  XX(24, MSEARCH, M-SEARCH) \\\n  XX(25, NOTIFY, NOTIFY) \\\n  XX(26, SUBSCRIBE, SUBSCRIBE) \\\n  XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \\\n  XX(28, PATCH, PATCH) \\\n  XX(29, PURGE, PURGE) \\\n  XX(30, MKCALENDAR, MKCALENDAR) \\\n  XX(31, LINK, LINK) \\\n  XX(32, UNLINK, UNLINK) \\\n  XX(33, SOURCE, SOURCE) \\\n  XX(34, PRI, PRI) \\\n  XX(35, DESCRIBE, DESCRIBE) \\\n  XX(36, ANNOUNCE, ANNOUNCE) \\\n  XX(37, SETUP, SETUP) \\\n  XX(38, PLAY, PLAY) \\\n  XX(39, PAUSE, PAUSE) \\\n  XX(40, TEARDOWN, TEARDOWN) \\\n  XX(41, GET_PARAMETER, GET_PARAMETER) \\\n  XX(42, SET_PARAMETER, SET_PARAMETER) \\\n  XX(43, REDIRECT, REDIRECT) \\\n  XX(44, RECORD, RECORD) \\\n  XX(45, FLUSH, FLUSH) \\\n  XX(46, QUERY, QUERY) \\\n\n\n#define HTTP_STATUS_MAP(XX) \\\n  XX(100, CONTINUE, CONTINUE) \\\n  XX(101, SWITCHING_PROTOCOLS, SWITCHING_PROTOCOLS) \\\n  XX(102, PROCESSING, PROCESSING) \\\n  XX(103, EARLY_HINTS, EARLY_HINTS) \\\n  XX(110, RESPONSE_IS_STALE, RESPONSE_IS_STALE) \\\n  XX(111, REVALIDATION_FAILED, REVALIDATION_FAILED) \\\n  XX(112, DISCONNECTED_OPERATION, DISCONNECTED_OPERATION) \\\n  XX(113, HEURISTIC_EXPIRATION, HEURISTIC_EXPIRATION) \\\n  XX(199, MISCELLANEOUS_WARNING, MISCELLANEOUS_WARNING) \\\n  XX(200, OK, OK) \\\n  XX(201, CREATED, CREATED) \\\n  XX(202, ACCEPTED, ACCEPTED) \\\n  XX(203, NON_AUTHORITATIVE_INFORMATION, NON_AUTHORITATIVE_INFORMATION) \\\n  XX(204, NO_CONTENT, NO_CONTENT) \\\n  XX(205, RESET_CONTENT, RESET_CONTENT) \\\n  XX(206, PARTIAL_CONTENT, PARTIAL_CONTENT) \\\n  XX(207, MULTI_STATUS, MULTI_STATUS) \\\n  XX(208, ALREADY_REPORTED, ALREADY_REPORTED) \\\n  XX(214, TRANSFORMATION_APPLIED, TRANSFORMATION_APPLIED) \\\n  XX(226, IM_USED, IM_USED) \\\n  XX(299, MISCELLANEOUS_PERSISTENT_WARNING, MISCELLANEOUS_PERSISTENT_WARNING) \\\n  XX(300, MULTIPLE_CHOICES, MULTIPLE_CHOICES) \\\n  XX(301, MOVED_PERMANENTLY, MOVED_PERMANENTLY) \\\n  XX(302, FOUND, FOUND) \\\n  XX(303, SEE_OTHER, SEE_OTHER) \\\n  XX(304, NOT_MODIFIED, NOT_MODIFIED) \\\n  XX(305, USE_PROXY, USE_PROXY) \\\n  XX(306, SWITCH_PROXY, SWITCH_PROXY) \\\n  XX(307, TEMPORARY_REDIRECT, TEMPORARY_REDIRECT) \\\n  XX(308, PERMANENT_REDIRECT, PERMANENT_REDIRECT) \\\n  XX(400, BAD_REQUEST, BAD_REQUEST) \\\n  XX(401, UNAUTHORIZED, UNAUTHORIZED) \\\n  XX(402, PAYMENT_REQUIRED, PAYMENT_REQUIRED) \\\n  XX(403, FORBIDDEN, FORBIDDEN) \\\n  XX(404, NOT_FOUND, NOT_FOUND) \\\n  XX(405, METHOD_NOT_ALLOWED, METHOD_NOT_ALLOWED) \\\n  XX(406, NOT_ACCEPTABLE, NOT_ACCEPTABLE) \\\n  XX(407, PROXY_AUTHENTICATION_REQUIRED, PROXY_AUTHENTICATION_REQUIRED) \\\n  XX(408, REQUEST_TIMEOUT, REQUEST_TIMEOUT) \\\n  XX(409, CONFLICT, CONFLICT) \\\n  XX(410, GONE, GONE) \\\n  XX(411, LENGTH_REQUIRED, LENGTH_REQUIRED) \\\n  XX(412, PRECONDITION_FAILED, PRECONDITION_FAILED) \\\n  XX(413, PAYLOAD_TOO_LARGE, PAYLOAD_TOO_LARGE) \\\n  XX(414, URI_TOO_LONG, URI_TOO_LONG) \\\n  XX(415, UNSUPPORTED_MEDIA_TYPE, UNSUPPORTED_MEDIA_TYPE) \\\n  XX(416, RANGE_NOT_SATISFIABLE, RANGE_NOT_SATISFIABLE) \\\n  XX(417, EXPECTATION_FAILED, EXPECTATION_FAILED) \\\n  XX(418, IM_A_TEAPOT, IM_A_TEAPOT) \\\n  XX(419, PAGE_EXPIRED, PAGE_EXPIRED) \\\n  XX(420, ENHANCE_YOUR_CALM, ENHANCE_YOUR_CALM) \\\n  XX(421, MISDIRECTED_REQUEST, MISDIRECTED_REQUEST) \\\n  XX(422, UNPROCESSABLE_ENTITY, UNPROCESSABLE_ENTITY) \\\n  XX(423, LOCKED, LOCKED) \\\n  XX(424, FAILED_DEPENDENCY, FAILED_DEPENDENCY) \\\n  XX(425, TOO_EARLY, TOO_EARLY) \\\n  XX(426, UPGRADE_REQUIRED, UPGRADE_REQUIRED) \\\n  XX(428, PRECONDITION_REQUIRED, PRECONDITION_REQUIRED) \\\n  XX(429, TOO_MANY_REQUESTS, TOO_MANY_REQUESTS) \\\n  XX(430, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL) \\\n  XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, REQUEST_HEADER_FIELDS_TOO_LARGE) \\\n  XX(440, LOGIN_TIMEOUT, LOGIN_TIMEOUT) \\\n  XX(444, NO_RESPONSE, NO_RESPONSE) \\\n  XX(449, RETRY_WITH, RETRY_WITH) \\\n  XX(450, BLOCKED_BY_PARENTAL_CONTROL, BLOCKED_BY_PARENTAL_CONTROL) \\\n  XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, UNAVAILABLE_FOR_LEGAL_REASONS) \\\n  XX(460, CLIENT_CLOSED_LOAD_BALANCED_REQUEST, CLIENT_CLOSED_LOAD_BALANCED_REQUEST) \\\n  XX(463, INVALID_X_FORWARDED_FOR, INVALID_X_FORWARDED_FOR) \\\n  XX(494, REQUEST_HEADER_TOO_LARGE, REQUEST_HEADER_TOO_LARGE) \\\n  XX(495, SSL_CERTIFICATE_ERROR, SSL_CERTIFICATE_ERROR) \\\n  XX(496, SSL_CERTIFICATE_REQUIRED, SSL_CERTIFICATE_REQUIRED) \\\n  XX(497, HTTP_REQUEST_SENT_TO_HTTPS_PORT, HTTP_REQUEST_SENT_TO_HTTPS_PORT) \\\n  XX(498, INVALID_TOKEN, INVALID_TOKEN) \\\n  XX(499, CLIENT_CLOSED_REQUEST, CLIENT_CLOSED_REQUEST) \\\n  XX(500, INTERNAL_SERVER_ERROR, INTERNAL_SERVER_ERROR) \\\n  XX(501, NOT_IMPLEMENTED, NOT_IMPLEMENTED) \\\n  XX(502, BAD_GATEWAY, BAD_GATEWAY) \\\n  XX(503, SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE) \\\n  XX(504, GATEWAY_TIMEOUT, GATEWAY_TIMEOUT) \\\n  XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP_VERSION_NOT_SUPPORTED) \\\n  XX(506, VARIANT_ALSO_NEGOTIATES, VARIANT_ALSO_NEGOTIATES) \\\n  XX(507, INSUFFICIENT_STORAGE, INSUFFICIENT_STORAGE) \\\n  XX(508, LOOP_DETECTED, LOOP_DETECTED) \\\n  XX(509, BANDWIDTH_LIMIT_EXCEEDED, BANDWIDTH_LIMIT_EXCEEDED) \\\n  XX(510, NOT_EXTENDED, NOT_EXTENDED) \\\n  XX(511, NETWORK_AUTHENTICATION_REQUIRED, NETWORK_AUTHENTICATION_REQUIRED) \\\n  XX(520, WEB_SERVER_UNKNOWN_ERROR, WEB_SERVER_UNKNOWN_ERROR) \\\n  XX(521, WEB_SERVER_IS_DOWN, WEB_SERVER_IS_DOWN) \\\n  XX(522, CONNECTION_TIMEOUT, CONNECTION_TIMEOUT) \\\n  XX(523, ORIGIN_IS_UNREACHABLE, ORIGIN_IS_UNREACHABLE) \\\n  XX(524, TIMEOUT_OCCURED, TIMEOUT_OCCURED) \\\n  XX(525, SSL_HANDSHAKE_FAILED, SSL_HANDSHAKE_FAILED) \\\n  XX(526, INVALID_SSL_CERTIFICATE, INVALID_SSL_CERTIFICATE) \\\n  XX(527, RAILGUN_ERROR, RAILGUN_ERROR) \\\n  XX(529, SITE_IS_OVERLOADED, SITE_IS_OVERLOADED) \\\n  XX(530, SITE_IS_FROZEN, SITE_IS_FROZEN) \\\n  XX(561, IDENTITY_PROVIDER_AUTHENTICATION_ERROR, IDENTITY_PROVIDER_AUTHENTICATION_ERROR) \\\n  XX(598, NETWORK_READ_TIMEOUT, NETWORK_READ_TIMEOUT) \\\n  XX(599, NETWORK_CONNECT_TIMEOUT, NETWORK_CONNECT_TIMEOUT) \\\n\n\n#ifdef __cplusplus\n}  /* extern \"C\" */\n#endif\n#endif  /* LLLLHTTP_C_HEADERS_ */\n\n\n#ifndef INCLUDE_LLHTTP_API_H_\n#define INCLUDE_LLHTTP_API_H_\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n#include <stddef.h>\n\n#if defined(__wasm__)\n#define LLHTTP_EXPORT __attribute__((visibility(\"default\")))\n#elif defined(_WIN32)\n#define LLHTTP_EXPORT __declspec(dllexport)\n#else\n#define LLHTTP_EXPORT\n#endif\n\ntypedef llhttp__internal_t llhttp_t;\ntypedef struct llhttp_settings_s llhttp_settings_t;\n\ntypedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length);\ntypedef int (*llhttp_cb)(llhttp_t*);\n\nstruct llhttp_settings_s {\n  /* Possible return values 0, -1, `HPE_PAUSED` */\n  llhttp_cb      on_message_begin;\n\n  /* Possible return values 0, -1, HPE_USER */\n  llhttp_data_cb on_protocol;\n  llhttp_data_cb on_url;\n  llhttp_data_cb on_status;\n  llhttp_data_cb on_method;\n  llhttp_data_cb on_version;\n  llhttp_data_cb on_header_field;\n  llhttp_data_cb on_header_value;\n  llhttp_data_cb      on_chunk_extension_name;\n  llhttp_data_cb      on_chunk_extension_value;\n\n  /* Possible return values:\n   * 0  - Proceed normally\n   * 1  - Assume that request/response has no body, and proceed to parsing the\n   *      next message\n   * 2  - Assume absence of body (as above) and make `llhttp_execute()` return\n   *      `HPE_PAUSED_UPGRADE`\n   * -1 - Error\n   * `HPE_PAUSED`\n   */\n  llhttp_cb      on_headers_complete;\n\n  /* Possible return values 0, -1, HPE_USER */\n  llhttp_data_cb on_body;\n\n  /* Possible return values 0, -1, `HPE_PAUSED` */\n  llhttp_cb      on_message_complete;\n  llhttp_cb      on_protocol_complete;\n  llhttp_cb      on_url_complete;\n  llhttp_cb      on_status_complete;\n  llhttp_cb      on_method_complete;\n  llhttp_cb      on_version_complete;\n  llhttp_cb      on_header_field_complete;\n  llhttp_cb      on_header_value_complete;\n  llhttp_cb      on_chunk_extension_name_complete;\n  llhttp_cb      on_chunk_extension_value_complete;\n\n  /* When on_chunk_header is called, the current chunk length is stored\n   * in parser->content_length.\n   * Possible return values 0, -1, `HPE_PAUSED`\n   */\n  llhttp_cb      on_chunk_header;\n  llhttp_cb      on_chunk_complete;\n  llhttp_cb      on_reset;\n};\n\n/* Initialize the parser with specific type and user settings.\n *\n * NOTE: lifetime of `settings` has to be at least the same as the lifetime of\n * the `parser` here. In practice, `settings` has to be either a static\n * variable or be allocated with `malloc`, `new`, etc.\n */\nLLHTTP_EXPORT\nvoid llhttp_init(llhttp_t* parser, llhttp_type_t type,\n                 const llhttp_settings_t* settings);\n\nLLHTTP_EXPORT\nllhttp_t* llhttp_alloc(llhttp_type_t type);\n\nLLHTTP_EXPORT\nvoid llhttp_free(llhttp_t* parser);\n\nLLHTTP_EXPORT\nuint8_t llhttp_get_type(llhttp_t* parser);\n\nLLHTTP_EXPORT\nuint8_t llhttp_get_http_major(llhttp_t* parser);\n\nLLHTTP_EXPORT\nuint8_t llhttp_get_http_minor(llhttp_t* parser);\n\nLLHTTP_EXPORT\nuint8_t llhttp_get_method(llhttp_t* parser);\n\nLLHTTP_EXPORT\nint llhttp_get_status_code(llhttp_t* parser);\n\nLLHTTP_EXPORT\nuint8_t llhttp_get_upgrade(llhttp_t* parser);\n\n/* Reset an already initialized parser back to the start state, preserving the\n * existing parser type, callback settings, user data, and lenient flags.\n */\nLLHTTP_EXPORT\nvoid llhttp_reset(llhttp_t* parser);\n\n/* Initialize the settings object */\nLLHTTP_EXPORT\nvoid llhttp_settings_init(llhttp_settings_t* settings);\n\n/* Parse full or partial request/response, invoking user callbacks along the\n * way.\n *\n * If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing\n * interrupts, and such errno is returned from `llhttp_execute()`. If\n * `HPE_PAUSED` was used as a errno, the execution can be resumed with\n * `llhttp_resume()` call.\n *\n * In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE`\n * is returned after fully parsing the request/response. If the user wishes to\n * continue parsing, they need to invoke `llhttp_resume_after_upgrade()`.\n *\n * NOTE: if this function ever returns a non-pause type error, it will continue\n * to return the same error upon each successive call up until `llhttp_init()`\n * is called.\n */\nLLHTTP_EXPORT\nllhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len);\n\n/* This method should be called when the other side has no further bytes to\n * send (e.g. shutdown of readable side of the TCP connection.)\n *\n * Requests without `Content-Length` and other messages might require treating\n * all incoming bytes as the part of the body, up to the last byte of the\n * connection. This method will invoke `on_message_complete()` callback if the\n * request was terminated safely. Otherwise a error code would be returned.\n */\nLLHTTP_EXPORT\nllhttp_errno_t llhttp_finish(llhttp_t* parser);\n\n/* Returns `1` if the incoming message is parsed until the last byte, and has\n * to be completed by calling `llhttp_finish()` on EOF\n */\nLLHTTP_EXPORT\nint llhttp_message_needs_eof(const llhttp_t* parser);\n\n/* Returns `1` if there might be any other messages following the last that was\n * successfully parsed.\n */\nLLHTTP_EXPORT\nint llhttp_should_keep_alive(const llhttp_t* parser);\n\n/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set\n * appropriate error reason.\n *\n * Important: do not call this from user callbacks! User callbacks must return\n * `HPE_PAUSED` if pausing is required.\n */\nLLHTTP_EXPORT\nvoid llhttp_pause(llhttp_t* parser);\n\n/* Might be called to resume the execution after the pause in user's callback.\n * See `llhttp_execute()` above for details.\n *\n * Call this only if `llhttp_execute()` returns `HPE_PAUSED`.\n */\nLLHTTP_EXPORT\nvoid llhttp_resume(llhttp_t* parser);\n\n/* Might be called to resume the execution after the pause in user's callback.\n * See `llhttp_execute()` above for details.\n *\n * Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE`\n */\nLLHTTP_EXPORT\nvoid llhttp_resume_after_upgrade(llhttp_t* parser);\n\n/* Returns the latest return error */\nLLHTTP_EXPORT\nllhttp_errno_t llhttp_get_errno(const llhttp_t* parser);\n\n/* Returns the verbal explanation of the latest returned error.\n *\n * Note: User callback should set error reason when returning the error. See\n * `llhttp_set_error_reason()` for details.\n */\nLLHTTP_EXPORT\nconst char* llhttp_get_error_reason(const llhttp_t* parser);\n\n/* Assign verbal description to the returned error. Must be called in user\n * callbacks right before returning the errno.\n *\n * Note: `HPE_USER` error code might be useful in user callbacks.\n */\nLLHTTP_EXPORT\nvoid llhttp_set_error_reason(llhttp_t* parser, const char* reason);\n\n/* Returns the pointer to the last parsed byte before the returned error. The\n * pointer is relative to the `data` argument of `llhttp_execute()`.\n *\n * Note: this method might be useful for counting the number of parsed bytes.\n */\nLLHTTP_EXPORT\nconst char* llhttp_get_error_pos(const llhttp_t* parser);\n\n/* Returns textual name of error code */\nLLHTTP_EXPORT\nconst char* llhttp_errno_name(llhttp_errno_t err);\n\n/* Returns textual name of HTTP method */\nLLHTTP_EXPORT\nconst char* llhttp_method_name(llhttp_method_t method);\n\n/* Returns textual name of HTTP status */\nLLHTTP_EXPORT\nconst char* llhttp_status_name(llhttp_status_t status);\n\n/* Enables/disables lenient header value parsing (disabled by default).\n *\n * Lenient parsing disables header value token checks, extending llhttp's\n * protocol support to highly non-compliant clients/server. No\n * `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when\n * lenient parsing is \"on\".\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * request smuggling attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_headers(llhttp_t* parser, int enabled);\n\n\n/* Enables/disables lenient handling of conflicting `Transfer-Encoding` and\n * `Content-Length` headers (disabled by default).\n *\n * Normally `llhttp` would error when `Transfer-Encoding` is present in\n * conjunction with `Content-Length`. This error is important to prevent HTTP\n * request smuggling, but may be less desirable for small number of cases\n * involving legacy servers.\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * request smuggling attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled);\n\n\n/* Enables/disables lenient handling of `Connection: close` and HTTP/1.0\n * requests responses.\n *\n * Normally `llhttp` would error on (in strict mode) or discard (in loose mode)\n * the HTTP request/response after the request/response with `Connection: close`\n * and `Content-Length`. This is important to prevent cache poisoning attacks,\n * but might interact badly with outdated and insecure clients. With this flag\n * the extra request/response will be parsed normally.\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * poisoning attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled);\n\n/* Enables/disables lenient handling of `Transfer-Encoding` header.\n *\n * Normally `llhttp` would error when a `Transfer-Encoding` has `chunked` value\n * and another value after it (either in a single header or in multiple\n * headers whose value are internally joined using `, `).\n * This is mandated by the spec to reliably determine request body size and thus\n * avoid request smuggling.\n * With this flag the extra value will be parsed normally.\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * request smuggling attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled);\n\n/* Enables/disables lenient handling of HTTP version.\n *\n * Normally `llhttp` would error when the HTTP version in the request or status line\n * is not `0.9`, `1.0`, `1.1` or `2.0`.\n * With this flag the invalid value will be parsed normally.\n *\n * **Enabling this flag can pose a security issue since you will allow unsupported\n * HTTP versions. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_version(llhttp_t* parser, int enabled);\n\n/* Enables/disables lenient handling of additional data received after a message ends\n * and keep-alive is disabled.\n *\n * Normally `llhttp` would error when additional unexpected data is received if the message\n * contains the `Connection` header with `close` value.\n * With this flag the extra data will discarded without throwing an error.\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * poisoning attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled);\n\n/* Enables/disables lenient handling of incomplete CRLF sequences.\n *\n * Normally `llhttp` would error when a CR is not followed by LF when terminating the\n * request line, the status line, the headers or a chunk header.\n * With this flag only a CR is required to terminate such sections.\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * request smuggling attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled);\n\n/*\n * Enables/disables lenient handling of line separators.\n *\n * Normally `llhttp` would error when a LF is not preceded by CR when terminating the\n * request line, the status line, the headers, a chunk header or a chunk data.\n * With this flag only a LF is required to terminate such sections.\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * request smuggling attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled);\n\n/* Enables/disables lenient handling of chunks not separated via CRLF.\n *\n * Normally `llhttp` would error when after a chunk data a CRLF is missing before\n * starting a new chunk.\n * With this flag the new chunk can start immediately after the previous one.\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * request smuggling attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled);\n\n/* Enables/disables lenient handling of spaces after chunk size.\n *\n * Normally `llhttp` would error when after a chunk size is followed by one or more\n * spaces are present instead of a CRLF or `;`.\n * With this flag this check is disabled.\n *\n * **Enabling this flag can pose a security issue since you will be exposed to\n * request smuggling attacks. USE WITH CAUTION!**\n */\nLLHTTP_EXPORT\nvoid llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled);\n\n#ifdef __cplusplus\n}  /* extern \"C\" */\n#endif\n#endif  /* INCLUDE_LLHTTP_API_H_ */\n\n\n#endif  /* INCLUDE_LLHTTP_H_ */\n"
  },
  {
    "path": "thirdparty/multipart_parser.c",
    "content": "/* Based on node-formidable by Felix Geisendörfer\n * Igor Afonov - afonov@gmail.com - 2012\n * MIT License - http://www.opensource.org/licenses/mit-license.php\n */\n\n#include \"multipart_parser.h\"\n\n#include <stdio.h>\n#include <stdarg.h>\n#include <string.h>\n\n#ifdef DEBUG_MULTIPART\n#include <ctype.h>\n#define multipart_log(format, ...)                                                                                     \\\n    do {                                                                                                               \\\n        fprintf(stderr, \"[MULTIPART_PARSER] line %d: \" format \"\\n\", __LINE__, __VA_ARGS__);                            \\\n    } while (0)\n#define multipart_log_c(format)                                                                                        \\\n    do {                                                                                                               \\\n        if (isprint(c)) {                                                                                              \\\n            multipart_log(\"parsing '%c' \" format, c);                                                                  \\\n        } else {                                                                                                       \\\n            multipart_log(\"parsing '\\\\x%0.2x' \" format, c);                                                            \\\n        }                                                                                                              \\\n    } while (0)\n#else\n#define multipart_log(format, ...)\n#define multipart_log_c(format, ...)\n#endif\n\n#define NOTIFY_CB(FOR, r)                                                                                              \\\n    do {                                                                                                               \\\n        if (p->settings->on_##FOR) {                                                                                   \\\n            if ((ret = p->settings->on_##FOR(p)) == MPPE_PAUSED) {                                                     \\\n                return r;                                                                                              \\\n            } else if (ret != MPPE_OK) {                                                                               \\\n                return MPPE_ERROR;                                                                                     \\\n            }                                                                                                          \\\n        }                                                                                                              \\\n    } while (0)\n\n#define EMIT_DATA_CB(FOR, r, ptr, len)                                                                                 \\\n    do {                                                                                                               \\\n        if (p->settings->on_##FOR) {                                                                                   \\\n            if ((ret = p->settings->on_##FOR(p, ptr, len)) == MPPE_PAUSED) {                                           \\\n                return r;                                                                                              \\\n            } else if (ret != MPPE_OK) {                                                                               \\\n                return MPPE_ERROR;                                                                                     \\\n            }                                                                                                          \\\n        }                                                                                                              \\\n    } while (0)\n\n#define ERROR_OUT(reason)                                                                                              \\\n    do {                                                                                                               \\\n        p->error_unexpected = c;                                                                                       \\\n        p->error_i = i;                                                                                                \\\n        p->error_reason = reason;                                                                                      \\\n        return MPPE_ERROR;                                                                                             \\\n    } while (0)\n\n#define ERROR_EXPECT(reason, ch)                                                                                       \\\n    do {                                                                                                               \\\n        p->error_expected = ch;                                                                                        \\\n        if (ch == LF) {                                                                                                \\\n            multipart_log(\"expecting LF at %zu, but it's \\\\x%.2x\", i, c);                                              \\\n        } else if (ch == CR) {                                                                                         \\\n            multipart_log(\"expecting CR at %zu, but it's \\\\x%.2x\", i, c);                                              \\\n        } else {                                                                                                       \\\n            multipart_log(\"expecting '%c' at %zu, but it's \\\\x%.2x\", ch, i, c);                                        \\\n        }                                                                                                              \\\n        ERROR_OUT(reason);                                                                                             \\\n    } while (0)\n\n#define LF 10\n#define CR 13\n\nenum state {\n    s_uninitialized = 0,\n    s_start,\n    s_start_boundary,\n    s_header_field_start,\n    s_header_field,\n    s_headers_almost_done,\n    s_header_value_start,\n    s_header_value,\n    s_header_value_almost_done,\n    s_part_data_start,\n    s_part_data,\n    s_part_data_almost_boundary,\n    s_part_data_boundary,\n    s_part_data_almost_end,\n    s_part_data_end,\n    s_part_data_final_hyphen,\n    s_end\n};\n\nmultipart_parser *multipart_parser_init(const char *boundary,\n                                        size_t boundary_length,\n                                        const multipart_parser_settings *settings) {\n    multipart_parser *p = calloc(sizeof(multipart_parser) + boundary_length + boundary_length + 9 + 4, sizeof(char));\n    memcpy(p->boundary, \"--\", 2);\n    memcpy(p->boundary + 2, boundary, boundary_length);\n    p->boundary[2 + boundary_length] = 0;\n\n    p->boundary_length = boundary_length + 2;\n    p->index = 0;\n    p->state = s_start;\n    p->error_i = 0;\n    p->error_unexpected = 0;\n    p->error_expected = 0;\n    p->error_reason = MPPE_OK;\n    p->state = s_start;\n    p->settings = settings;\n\n    return p;\n}\n\nvoid multipart_parser_free(multipart_parser *p) {\n    free(p);\n}\n\nint multipart_parser_error_msg(multipart_parser *p, char *buf, size_t len) {\n    int ret;\n    switch (p->error_reason) {\n    case MPPE_OK:\n        return 0;\n    case MPPE_PAUSED:\n        return snprintf(buf, len, \"parser paused\");\n    case MPPE_UNKNOWN:\n        return snprintf(buf, len, \"parser unknown\");\n    default:\n        return snprintf(buf, len, \"parser abort\");\n    case MPPE_BOUNDARY_END_NO_CRLF:\n        ret = snprintf(buf, len, \"no CRLF at first boundary end: \");\n        break;\n    case MPPE_BAD_START_BOUNDARY:\n        ret = snprintf(buf, len, \"first boundary mismatching: \");\n        break;\n    case MPPE_INVALID_HEADER_FIELD_CHAR:\n        ret = snprintf(buf, len, \"invalid char in header field: \");\n        break;\n    case MPPE_INVALID_HEADER_VALUE_CHAR:\n        ret = snprintf(buf, len, \"invalid char in header value: \");\n        break;\n    case MPPE_BAD_PART_END:\n        ret = snprintf(buf, len, \"no next part or final hyphen: expecting CR or '-' \");\n        break;\n    case MPPE_END_BOUNDARY_NO_DASH:\n        ret = snprintf(buf, len, \"bad final hyphen: \");\n        break;\n    }\n    if (ret < 0) {\n        return 0;\n    }\n    if ((size_t) ret >= len) {\n        return ret;\n    }\n    switch (p->error_expected) {\n    case '\\0':\n        break;\n    case CR:\n        ret += snprintf(buf + ret, len - ret, \"expecting CR \");\n        break;\n    case LF:\n        ret += snprintf(buf + ret, len - ret, \"expecting LF \");\n        break;\n    default:\n        ret += snprintf(buf + ret, len - ret, \"expecting '%c' \", p->error_expected);\n        break;\n    }\n    if (ret < 0) {\n        return 0;\n    }\n    if ((size_t) ret >= len) {\n        return ret;\n    }\n    if (isprint(p->error_unexpected)) {\n        ret += snprintf(buf + ret, len - ret, \"at %zu, but it is '%c'\", p->error_i, p->error_unexpected);\n    } else {\n        ret += snprintf(buf + ret, len - ret, \"at %zu, but it is '\\\\x%.2x'\", p->error_i, p->error_unexpected);\n    }\n    return ret;\n}\n\nssize_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t len) {\n    size_t i = 0;\n    size_t mark = 0;\n    size_t mark_end = 0;\n    char c, cl;\n    int is_last = 0;\n    int ret;\n\n    while (i < len) {\n        c = buf[i];\n        is_last = (i == (len - 1));\n        switch (p->state) {\n        case s_start:\n            multipart_log_c(\"s_start\");\n            p->index = 0;\n            p->state = s_start_boundary;\n            /* fallthrough */\n            /* no break */\n        case s_start_boundary:\n            multipart_log_c(\"s_start_boundary\");\n            if (p->index == p->boundary_length) {\n                // https://github.com/swoole/swoole-src/issues/5168\n                if (c == '-') {\n                    p->state = s_part_data_final_hyphen;\n                } else if (c != CR) {\n                    ERROR_EXPECT(MPPE_BOUNDARY_END_NO_CRLF, CR);\n                }\n                p->index++;\n                break;\n            } else if (p->index == (size_t)(p->boundary_length + 1)) {\n                if (c != LF) {\n                    ERROR_EXPECT(MPPE_BOUNDARY_END_NO_CRLF, LF);\n                }\n                p->index = 0;\n                p->state = s_header_field_start;\n                NOTIFY_CB(part_data_begin, i + 1);\n                break;\n            }\n            if (c != p->boundary[p->index]) {\n                ERROR_EXPECT(MPPE_BAD_START_BOUNDARY, p->boundary[p->index]);\n            }\n            p->index++;\n            break;\n        case s_header_field_start:\n            multipart_log_c(\"s_header_field_start\");\n            mark = i;\n            p->state = s_header_field;\n            /* fallthrough */\n            /* no break */\n        case s_header_field:\n            multipart_log_c(\"s_header_field\");\n            if (c == CR) {\n                p->state = s_headers_almost_done;\n                break;\n            }\n            if (c == '-') {\n                if (is_last) {\n                    EMIT_DATA_CB(header_field, i + 1, buf + mark, i - mark + 1);\n                }\n                break;\n            }\n            if (c == ':') {\n                p->state = s_header_value_start;\n                EMIT_DATA_CB(header_field, i + 1, buf + mark, i - mark);\n                break;\n            }\n            cl = c | 0x20;\n            if (cl < 'a' || cl > 'z') {\n                multipart_log_c(\"invalid character in header field\");\n                p->error_unexpected = c;\n                ERROR_OUT(MPPE_INVALID_HEADER_FIELD_CHAR);\n            }\n            if (is_last) {\n                EMIT_DATA_CB(header_field, i + 1, buf + mark, i - mark + 1);\n            }\n            break;\n        case s_headers_almost_done:\n            multipart_log_c(\"s_headers_almost_done\");\n            if (c != LF) {\n                ERROR_EXPECT(MPPE_INVALID_HEADER_VALUE_CHAR, LF);\n            }\n            p->state = s_part_data_start;\n            break;\n        case s_header_value_start:\n            multipart_log_c(\"s_header_value_start\");\n            if (c == ' ') {\n                break;\n            }\n            mark = i;\n            p->state = s_header_value;\n            /* fallthrough */\n            /* no break */\n        case s_header_value:\n            multipart_log_c(\"s_header_value\");\n            if (c == CR) {\n                p->state = s_header_value_almost_done;\n                EMIT_DATA_CB(header_value, i + 1, buf + mark, i - mark);\n            } else if (is_last) {\n                ERROR_EXPECT(MPPE_HEADER_VALUE_INCOMPLETE, CR);\n            }\n            break;\n        case s_header_value_almost_done:\n            multipart_log_c(\"s_header_value_almost_done\");\n            if (c != LF) {\n                ERROR_EXPECT(MPPE_INVALID_HEADER_VALUE_CHAR, LF);\n            }\n            p->state = s_header_field_start;\n            break;\n        case s_part_data_start:\n            multipart_log_c(\"s_part_data_start\");\n            mark = i;\n            p->state = s_part_data;\n            NOTIFY_CB(headers_complete, i);\n            /* fallthrough */\n            /* no break */\n        case s_part_data:\n        data_rollback:\n            multipart_log_c(\"s_part_data\");\n            mark_end = i + 1;\n            if (c == CR) {\n                if (mark_end - mark - 1 > 0) {\n                    EMIT_DATA_CB(part_data, i + 1, buf + mark, mark_end - mark - 1);\n                }\n                mark = i;\n                p->state = s_part_data_almost_boundary;\n                break;\n            }\n            if (is_last) {\n                EMIT_DATA_CB(part_data, i + 1, buf + mark, mark_end - mark);\n            }\n            break;\n        case s_part_data_almost_boundary:\n            multipart_log_c(\"s_part_data_almost_boundary\");\n            if (c != LF) {\n                EMIT_DATA_CB(part_data, i + 1, \"\\r\", 1);\n                p->state = s_part_data;\n                mark = i;\n                goto data_rollback;\n            } else {\n                p->state = s_part_data_boundary;\n                p->index = 0;\n                break;\n            }\n        case s_part_data_boundary:\n            multipart_log_c(\"s_part_data_boundary\");\n            if (p->boundary[p->index] != c) {\n                EMIT_DATA_CB(part_data, i + 1, \"\\r\\n\", 2);\n                if (p->index > 0) {\n                    EMIT_DATA_CB(part_data, i + 1, p->boundary, p->index);\n                }\n                mark = i;\n                p->state = s_part_data;\n                goto data_rollback;\n            } else {\n                p->index++;\n                if (p->index == p->boundary_length) {\n                    p->state = s_part_data_almost_end;\n                }\n                break;\n            }\n        case s_part_data_almost_end:\n            multipart_log_c(\"s_part_data_almost_end\");\n            if (c == '-') {\n                p->state = s_part_data_final_hyphen;\n                NOTIFY_CB(part_data_end, i + 1);\n                break;\n            }\n            if (c == CR) {\n                p->state = s_part_data_end;\n                NOTIFY_CB(part_data_end, i + 1);\n                break;\n            }\n            // should be end or another part\n            multipart_log(\"expecting '-' or CR at %zu but it's \\\\x%0.2x\", i, c);\n            ERROR_OUT(MPPE_BAD_PART_END);\n        case s_part_data_final_hyphen:\n            multipart_log_c(\"s_part_data_final_hyphen\");\n            if (c == '-') {\n                p->state = s_end;\n                NOTIFY_CB(body_end, i);\n                break;\n            }\n            // should be -\n            ERROR_EXPECT(MPPE_END_BOUNDARY_NO_DASH, '-');\n        case s_part_data_end:\n            multipart_log_c(\"s_part_data_end\");\n            if (c == LF) {\n                p->state = s_header_field_start;\n                NOTIFY_CB(part_data_begin, i + 1);\n                break;\n            }\n            // should be -\n            ERROR_EXPECT(MPPE_END_BOUNDARY_NO_DASH, '-');\n        case s_end:\n            multipart_log_c(\"s_end\");\n            break;\n        default:\n            multipart_log_c(\"Multipart parser unrecoverable error\");\n            ERROR_OUT(MPPE_UNKNOWN);\n        }\n        ++i;\n    }\n    return i;\n}\n"
  },
  {
    "path": "thirdparty/multipart_parser.h",
    "content": "/* Based on node-formidable by Felix Geisendörfer\n * Igor Afonov - afonov@gmail.com - 2012\n * MIT License - http://www.opensource.org/licenses/mit-license.php\n * @link https://github.com/libcat/libcat/blob/develop/deps/multipart_parser\n */\n\n#ifndef _multipart_parser_h\n#define _multipart_parser_h\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n\ntypedef struct multipart_parser multipart_parser;\ntypedef struct multipart_parser_settings multipart_parser_settings;\ntypedef struct multipart_parser_state multipart_parser_state;\n\ntypedef int (*multipart_data_cb)(multipart_parser *, const char *at, size_t length);\ntypedef int (*multipart_notify_cb)(multipart_parser *);\n\nenum multipart_error {\n    MPPE_OK = 0,\n    MPPE_PAUSED,\n    MPPE_UNKNOWN,\n    MPPE_BOUNDARY_END_NO_CRLF,\n    MPPE_BAD_START_BOUNDARY,\n    MPPE_INVALID_HEADER_FIELD_CHAR,\n    MPPE_INVALID_HEADER_VALUE_CHAR,\n    MPPE_BAD_PART_END,\n    MPPE_END_BOUNDARY_NO_DASH,\n    MPPE_HEADER_VALUE_INCOMPLETE,\n};\n\n#define MPPE_ERROR -1\n\n// from RFC2046\n#define BOUNDARY_MAX_LEN 70\n\nstruct multipart_parser {\n    /* private holder for callbacks */\n    const multipart_parser_settings *settings;\n    /* private internal index for matching boundary */\n    size_t index;\n    /* public error unexpected char index */\n    size_t error_i;\n    /* private boundary length + 2 (\"--\") */\n    unsigned char boundary_length;\n    FILE *fp;\n    void *data;\n    /* private FSM state */\n    unsigned char state;\n    /* public error reason */\n    unsigned char error_reason;\n    /* private boundary storage: \"--\" + boundary */\n    char boundary[(2 + BOUNDARY_MAX_LEN) * 2 + 9];\n    /* public error expected char */\n    char error_expected;\n    /* public error unexpected char */\n    char error_unexpected;\n};\n\nstruct multipart_parser_settings {\n    /*\n     * data callback called on header field coming\n     * for example data is \"Content-Type\" with length 12\n     */\n    multipart_data_cb on_header_field;\n    /*\n     * data callback called on header value coming\n     * for example data is \"plain/text\" with length 10\n     */\n    multipart_data_cb on_header_value;\n    /*\n     * data callback called on body data coming,\n     * will be called repeatedly until data end\n     */\n    multipart_data_cb on_part_data;\n    /*\n     * before \"--\" boundary\n     */\n    multipart_notify_cb on_part_data_begin;\n    /*\n     * after all headers line \"\\r\\n\", before body\n     */\n    multipart_notify_cb on_headers_complete;\n    /*\n     * after body, before next \"--\" boundary\n     */\n    multipart_notify_cb on_part_data_end;\n    /*\n     * after last \"--\" boundary \"--\"\n     */\n    multipart_notify_cb on_body_end;\n};\n\nmultipart_parser *multipart_parser_init(const char *boundary,\n                                        size_t boundary_length,\n                                        const multipart_parser_settings *settings);\nvoid multipart_parser_free(multipart_parser *p);\n\n/**\n * The multipart header must be complete, otherwise it will be parsed incorrectly\n */\nssize_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t len);\nint multipart_parser_error_msg(multipart_parser *p, char *buf, size_t len);\n\n#ifdef __cplusplus\n} /* extern \"C\" */\n#endif\n\n#endif\n"
  },
  {
    "path": "thirdparty/nghttp2/COPYING",
    "content": "The MIT License\n\nCopyright (c) 2012, 2014, 2015, 2016 Tatsuhiro Tsujikawa\nCopyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "thirdparty/nghttp2/LICENSE",
    "content": "See COPYING\r\n"
  },
  {
    "path": "thirdparty/nghttp2/nghttp2.h",
    "content": "/*\n * nghttp2 - HTTP/2 C Library\n *\n * Copyright (c) 2013, 2014 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef NGHTTP2_H\n#define NGHTTP2_H\n\n/* Define WIN32 when build target is Win32 API (borrowed from\n   libcurl) */\n#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)\n#  define WIN32\n#endif\n\n/* Compatibility for non-Clang compilers */\n#ifndef __has_declspec_attribute\n#  define __has_declspec_attribute(x) 0\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdlib.h>\n#if defined(_MSC_VER) && (_MSC_VER < 1800)\n/* MSVC < 2013 does not have inttypes.h because it is not C99\n   compliant.  See compiler macros and version number in\n   https://sourceforge.net/p/predef/wiki/Compilers/ */\n#  include <stdint.h>\n#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */\n#  include <inttypes.h>\n#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */\n#include <sys/types.h>\n#include <stdarg.h>\n#include <string.h>\n#include <stddef.h>\n\nconst char *nghttp2_strerror(int error_code);\nstatic inline uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len) {\n    if (len == 0) {\n        return dest;\n    }\n\n    memcpy(dest, src, len);\n\n    return dest + len;\n}\n\n#define DEBUGF(s, ...)\n#define nghttp2_min_size(A, B) ((A) < (B) ? (A) : (B))\n#define nghttp2_max_size(A, B) ((A) > (B) ? (A) : (B))\n\n#ifdef NGHTTP2_STATICLIB\n#  define NGHTTP2_EXTERN\n#elif defined(WIN32) ||                                                        \\\n  (__has_declspec_attribute(dllexport) && __has_declspec_attribute(dllimport))\n#  ifdef BUILDING_NGHTTP2\n#    define NGHTTP2_EXTERN __declspec(dllexport)\n#  else /* !BUILDING_NGHTTP2 */\n#    define NGHTTP2_EXTERN __declspec(dllimport)\n#  endif /* !BUILDING_NGHTTP2 */\n#else    /* !defined(WIN32) */\n#  ifdef BUILDING_NGHTTP2\n#    define NGHTTP2_EXTERN __attribute__((visibility(\"default\")))\n#  else /* !BUILDING_NGHTTP2 */\n#    define NGHTTP2_EXTERN\n#  endif /* !BUILDING_NGHTTP2 */\n#endif   /* !defined(WIN32) */\n\n#ifdef BUILDING_NGHTTP2\n#  undef NGHTTP2_NO_SSIZE_T\n#endif /* BUILDING_NGHTTP2 */\n\n/**\n * @typedef\n *\n * :type:`nghttp2_ssize` is a signed counterpart of size_t.\n */\ntypedef ptrdiff_t nghttp2_ssize;\n\n/**\n * @macro\n *\n * The protocol version identification string of this library\n * supports.  This identifier is used if HTTP/2 is used over TLS.\n */\n#define NGHTTP2_PROTO_VERSION_ID \"h2\"\n/**\n * @macro\n *\n * The length of :macro:`NGHTTP2_PROTO_VERSION_ID`.\n */\n#define NGHTTP2_PROTO_VERSION_ID_LEN 2\n\n/**\n * @macro\n *\n * The serialized form of ALPN protocol identifier this library\n * supports.  Notice that first byte is the length of following\n * protocol identifier.  This is the same wire format of `TLS ALPN\n * extension <https://tools.ietf.org/html/rfc7301>`_.  This is useful\n * to process incoming ALPN tokens in wire format.\n */\n#define NGHTTP2_PROTO_ALPN \"\\x2h2\"\n\n/**\n * @macro\n *\n * The length of :macro:`NGHTTP2_PROTO_ALPN`.\n */\n#define NGHTTP2_PROTO_ALPN_LEN (sizeof(NGHTTP2_PROTO_ALPN) - 1)\n\n/**\n * @macro\n *\n * The protocol version identification string of this library\n * supports.  This identifier is used if HTTP/2 is used over cleartext\n * TCP.\n */\n#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID \"h2c\"\n\n/**\n * @macro\n *\n * The length of :macro:`NGHTTP2_CLEARTEXT_PROTO_VERSION_ID`.\n */\n#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN 3\n\nstruct nghttp2_session;\n/**\n * @struct\n *\n * The primary structure to hold the resources needed for a HTTP/2\n * session.  The details of this structure are intentionally hidden\n * from the public API.\n */\ntypedef struct nghttp2_session nghttp2_session;\n\n/**\n * @macro\n *\n * The age of :type:`nghttp2_info`\n */\n#define NGHTTP2_VERSION_AGE 1\n\n/**\n * @struct\n *\n * This struct is what `nghttp2_version()` returns.  It holds\n * information about the particular nghttp2 version.\n */\ntypedef struct {\n  /**\n   * Age of this struct.  This instance of nghttp2 sets it to\n   * :macro:`NGHTTP2_VERSION_AGE` but a future version may bump it and\n   * add more struct fields at the bottom\n   */\n  int age;\n  /**\n   * the :macro:`NGHTTP2_VERSION_NUM` number (since age ==1)\n   */\n  int version_num;\n  /**\n   * points to the :macro:`NGHTTP2_VERSION` string (since age ==1)\n   */\n  const char *version_str;\n  /**\n   * points to the :macro:`NGHTTP2_PROTO_VERSION_ID` string this\n   * instance implements (since age ==1)\n   */\n  const char *proto_str;\n  /* -------- the above fields all exist when age == 1 */\n} nghttp2_info;\n\n/**\n * @macro\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.\n *\n * The default weight of stream dependency.\n */\n#define NGHTTP2_DEFAULT_WEIGHT 16\n\n/**\n * @macro\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.\n *\n * The maximum weight of stream dependency.\n */\n#define NGHTTP2_MAX_WEIGHT 256\n\n/**\n * @macro\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.\n *\n * The minimum weight of stream dependency.\n */\n#define NGHTTP2_MIN_WEIGHT 1\n\n/**\n * @macro\n *\n * The maximum window size\n */\n#define NGHTTP2_MAX_WINDOW_SIZE ((int32_t)((1U << 31) - 1))\n\n/**\n * @macro\n *\n * The initial window size for stream level flow control.\n */\n#define NGHTTP2_INITIAL_WINDOW_SIZE ((1 << 16) - 1)\n/**\n * @macro\n *\n * The initial window size for connection level flow control.\n */\n#define NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE ((1 << 16) - 1)\n\n/**\n * @macro\n *\n * The default header table size.\n */\n#define NGHTTP2_DEFAULT_HEADER_TABLE_SIZE (1 << 12)\n\n/**\n * @macro\n *\n * The client magic string, which is the first 24 bytes byte string of\n * client connection preface.\n */\n#define NGHTTP2_CLIENT_MAGIC \"PRI * HTTP/2.0\\r\\n\\r\\nSM\\r\\n\\r\\n\"\n\n/**\n * @macro\n *\n * The length of :macro:`NGHTTP2_CLIENT_MAGIC`.\n */\n#define NGHTTP2_CLIENT_MAGIC_LEN 24\n\n/**\n * @macro\n *\n * The default max number of settings per SETTINGS frame\n */\n#define NGHTTP2_DEFAULT_MAX_SETTINGS 32\n\n/**\n * @enum\n *\n * Error codes used in this library.  The code range is [-999, -500],\n * inclusive. The following values are defined:\n */\ntypedef enum {\n  /**\n   * Invalid argument passed.\n   */\n  NGHTTP2_ERR_INVALID_ARGUMENT = -501,\n  /**\n   * Out of buffer space.\n   */\n  NGHTTP2_ERR_BUFFER_ERROR = -502,\n  /**\n   * The specified protocol version is not supported.\n   */\n  NGHTTP2_ERR_UNSUPPORTED_VERSION = -503,\n  /**\n   * Used as a return value from :type:`nghttp2_send_callback2`,\n   * :type:`nghttp2_recv_callback` and\n   * :type:`nghttp2_send_data_callback` to indicate that the operation\n   * would block.\n   */\n  NGHTTP2_ERR_WOULDBLOCK = -504,\n  /**\n   * General protocol error\n   */\n  NGHTTP2_ERR_PROTO = -505,\n  /**\n   * The frame is invalid.\n   */\n  NGHTTP2_ERR_INVALID_FRAME = -506,\n  /**\n   * The peer performed a shutdown on the connection.\n   */\n  NGHTTP2_ERR_EOF = -507,\n  /**\n   * Used as a return value from\n   * :func:`nghttp2_data_source_read_callback2` to indicate that data\n   * transfer is postponed.  See\n   * :func:`nghttp2_data_source_read_callback2` for details.\n   */\n  NGHTTP2_ERR_DEFERRED = -508,\n  /**\n   * Stream ID has reached the maximum value.  Therefore no stream ID\n   * is available.\n   */\n  NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE = -509,\n  /**\n   * The stream is already closed; or the stream ID is invalid.\n   */\n  NGHTTP2_ERR_STREAM_CLOSED = -510,\n  /**\n   * RST_STREAM has been added to the outbound queue.  The stream is\n   * in closing state.\n   */\n  NGHTTP2_ERR_STREAM_CLOSING = -511,\n  /**\n   * The transmission is not allowed for this stream (e.g., a frame\n   * with END_STREAM flag set has already sent).\n   */\n  NGHTTP2_ERR_STREAM_SHUT_WR = -512,\n  /**\n   * The stream ID is invalid.\n   */\n  NGHTTP2_ERR_INVALID_STREAM_ID = -513,\n  /**\n   * The state of the stream is not valid (e.g., DATA cannot be sent\n   * to the stream if response HEADERS has not been sent).\n   */\n  NGHTTP2_ERR_INVALID_STREAM_STATE = -514,\n  /**\n   * Another DATA frame has already been deferred.\n   */\n  NGHTTP2_ERR_DEFERRED_DATA_EXIST = -515,\n  /**\n   * Starting new stream is not allowed (e.g., GOAWAY has been sent\n   * and/or received).\n   */\n  NGHTTP2_ERR_START_STREAM_NOT_ALLOWED = -516,\n  /**\n   * GOAWAY has already been sent.\n   */\n  NGHTTP2_ERR_GOAWAY_ALREADY_SENT = -517,\n  /**\n   * The received frame contains the invalid header block (e.g., There\n   * are duplicate header names; or the header names are not encoded\n   * in US-ASCII character set and not lower cased; or the header name\n   * is zero-length string; or the header value contains multiple\n   * in-sequence NUL bytes).\n   */\n  NGHTTP2_ERR_INVALID_HEADER_BLOCK = -518,\n  /**\n   * Indicates that the context is not suitable to perform the\n   * requested operation.\n   */\n  NGHTTP2_ERR_INVALID_STATE = -519,\n  /**\n   * The user callback function failed due to the temporal error.\n   */\n  NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE = -521,\n  /**\n   * The length of the frame is invalid, either too large or too small.\n   */\n  NGHTTP2_ERR_FRAME_SIZE_ERROR = -522,\n  /**\n   * Header block inflate/deflate error.\n   */\n  NGHTTP2_ERR_HEADER_COMP = -523,\n  /**\n   * Flow control error\n   */\n  NGHTTP2_ERR_FLOW_CONTROL = -524,\n  /**\n   * Insufficient buffer size given to function.\n   */\n  NGHTTP2_ERR_INSUFF_BUFSIZE = -525,\n  /**\n   * Callback was paused by the application\n   */\n  NGHTTP2_ERR_PAUSE = -526,\n  /**\n   * There are too many in-flight SETTING frame and no more\n   * transmission of SETTINGS is allowed.\n   */\n  NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS = -527,\n  /**\n   * The server push is disabled.\n   */\n  NGHTTP2_ERR_PUSH_DISABLED = -528,\n  /**\n   * DATA or HEADERS frame for a given stream has been already\n   * submitted and has not been fully processed yet.  Application\n   * should wait for the transmission of the previously submitted\n   * frame before submitting another.\n   */\n  NGHTTP2_ERR_DATA_EXIST = -529,\n  /**\n   * The current session is closing due to a connection error or\n   * `nghttp2_session_terminate_session()` is called.\n   */\n  NGHTTP2_ERR_SESSION_CLOSING = -530,\n  /**\n   * Invalid HTTP header field was received and stream is going to be\n   * closed.\n   */\n  NGHTTP2_ERR_HTTP_HEADER = -531,\n  /**\n   * Violation in HTTP messaging rule.\n   */\n  NGHTTP2_ERR_HTTP_MESSAGING = -532,\n  /**\n   * Stream was refused.\n   */\n  NGHTTP2_ERR_REFUSED_STREAM = -533,\n  /**\n   * Unexpected internal error, but recovered.\n   */\n  NGHTTP2_ERR_INTERNAL = -534,\n  /**\n   * Indicates that a processing was canceled.\n   */\n  NGHTTP2_ERR_CANCEL = -535,\n  /**\n   * When a local endpoint expects to receive SETTINGS frame, it\n   * receives an other type of frame.\n   */\n  NGHTTP2_ERR_SETTINGS_EXPECTED = -536,\n  /**\n   * When a local endpoint receives too many settings entries\n   * in a single SETTINGS frame.\n   */\n  NGHTTP2_ERR_TOO_MANY_SETTINGS = -537,\n  /**\n   * The errors < :enum:`nghttp2_error.NGHTTP2_ERR_FATAL` mean that\n   * the library is under unexpected condition and processing was\n   * terminated (e.g., out of memory).  If application receives this\n   * error code, it must stop using that :type:`nghttp2_session`\n   * object and only allowed operation for that object is deallocate\n   * it using `nghttp2_session_del()`.\n   */\n  NGHTTP2_ERR_FATAL = -900,\n  /**\n   * Out of memory.  This is a fatal error.\n   */\n  NGHTTP2_ERR_NOMEM = -901,\n  /**\n   * The user callback function failed.  This is a fatal error.\n   */\n  NGHTTP2_ERR_CALLBACK_FAILURE = -902,\n  /**\n   * Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was\n   * received and further processing is not possible.\n   */\n  NGHTTP2_ERR_BAD_CLIENT_MAGIC = -903,\n  /**\n   * Possible flooding by peer was detected in this HTTP/2 session.\n   * Flooding is measured by how many PING and SETTINGS frames with\n   * ACK flag set are queued for transmission.  These frames are\n   * response for the peer initiated frames, and peer can cause memory\n   * exhaustion on server side to send these frames forever and does\n   * not read network.\n   */\n  NGHTTP2_ERR_FLOODED = -904,\n  /**\n   * When a local endpoint receives too many CONTINUATION frames\n   * following a HEADER frame.\n   */\n  NGHTTP2_ERR_TOO_MANY_CONTINUATIONS = -905,\n} nghttp2_error;\n\n/**\n * @struct\n *\n * The object representing single contiguous buffer.\n */\ntypedef struct {\n  /**\n   * The pointer to the buffer.\n   */\n  uint8_t *base;\n  /**\n   * The length of the buffer.\n   */\n  size_t len;\n} nghttp2_vec;\n\nstruct nghttp2_rcbuf;\n\n/**\n * @struct\n *\n * The object representing reference counted buffer.  The details of\n * this structure are intentionally hidden from the public API.\n */\ntypedef struct nghttp2_rcbuf nghttp2_rcbuf;\n\n/**\n * @function\n *\n * Increments the reference count of |rcbuf| by 1.\n */\nNGHTTP2_EXTERN void nghttp2_rcbuf_incref(nghttp2_rcbuf *rcbuf);\n\n/**\n * @function\n *\n * Decrements the reference count of |rcbuf| by 1.  If the reference\n * count becomes zero, the object pointed by |rcbuf| will be freed.\n * In this case, application must not use |rcbuf| again.\n */\nNGHTTP2_EXTERN void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf);\n\n/**\n * @function\n *\n * Returns the underlying buffer managed by |rcbuf|.\n */\nNGHTTP2_EXTERN nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf);\n\n/**\n * @function\n *\n * Returns nonzero if the underlying buffer is statically allocated,\n * and 0 otherwise. This can be useful for language bindings that wish\n * to avoid creating duplicate strings for these buffers.\n */\nNGHTTP2_EXTERN int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf);\n\n/**\n * @enum\n *\n * The flags for header field name/value pair.\n */\ntypedef enum {\n  /**\n   * No flag set.\n   */\n  NGHTTP2_NV_FLAG_NONE = 0,\n  /**\n   * Indicates that this name/value pair must not be indexed (\"Literal\n   * Header Field never Indexed\" representation must be used in HPACK\n   * encoding).  Other implementation calls this bit as \"sensitive\".\n   */\n  NGHTTP2_NV_FLAG_NO_INDEX = 0x01,\n  /**\n   * This flag is set solely by application.  If this flag is set, the\n   * library does not make a copy of header field name.  This could\n   * improve performance.\n   */\n  NGHTTP2_NV_FLAG_NO_COPY_NAME = 0x02,\n  /**\n   * This flag is set solely by application.  If this flag is set, the\n   * library does not make a copy of header field value.  This could\n   * improve performance.\n   */\n  NGHTTP2_NV_FLAG_NO_COPY_VALUE = 0x04\n} nghttp2_nv_flag;\n\n/**\n * @struct\n *\n * The name/value pair, which mainly used to represent header fields.\n */\ntypedef struct {\n  /**\n   * The |name| byte string.  If this struct is presented from library\n   * (e.g., :type:`nghttp2_on_frame_recv_callback`), |name| is\n   * guaranteed to be NULL-terminated.  For some callbacks\n   * (:type:`nghttp2_before_frame_send_callback`,\n   * :type:`nghttp2_on_frame_send_callback`, and\n   * :type:`nghttp2_on_frame_not_send_callback`), it may not be\n   * NULL-terminated if header field is passed from application with\n   * the flag :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`).\n   * When application is constructing this struct, |name| is not\n   * required to be NULL-terminated.\n   */\n  uint8_t *name;\n  /**\n   * The |value| byte string.  If this struct is presented from\n   * library (e.g., :type:`nghttp2_on_frame_recv_callback`), |value|\n   * is guaranteed to be NULL-terminated.  For some callbacks\n   * (:type:`nghttp2_before_frame_send_callback`,\n   * :type:`nghttp2_on_frame_send_callback`, and\n   * :type:`nghttp2_on_frame_not_send_callback`), it may not be\n   * NULL-terminated if header field is passed from application with\n   * the flag :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE`).\n   * When application is constructing this struct, |value| is not\n   * required to be NULL-terminated.\n   */\n  uint8_t *value;\n  /**\n   * The length of the |name|, excluding terminating NULL.\n   */\n  size_t namelen;\n  /**\n   * The length of the |value|, excluding terminating NULL.\n   */\n  size_t valuelen;\n  /**\n   * Bitwise OR of one or more of :type:`nghttp2_nv_flag`.\n   */\n  uint8_t flags;\n} nghttp2_nv;\n\n/**\n * @enum\n *\n * The frame types in HTTP/2 specification.\n */\ntypedef enum {\n  /**\n   * The DATA frame.\n   */\n  NGHTTP2_DATA = 0,\n  /**\n   * The HEADERS frame.\n   */\n  NGHTTP2_HEADERS = 0x01,\n  /**\n   * The PRIORITY frame.\n   */\n  NGHTTP2_PRIORITY = 0x02,\n  /**\n   * The RST_STREAM frame.\n   */\n  NGHTTP2_RST_STREAM = 0x03,\n  /**\n   * The SETTINGS frame.\n   */\n  NGHTTP2_SETTINGS = 0x04,\n  /**\n   * The PUSH_PROMISE frame.\n   */\n  NGHTTP2_PUSH_PROMISE = 0x05,\n  /**\n   * The PING frame.\n   */\n  NGHTTP2_PING = 0x06,\n  /**\n   * The GOAWAY frame.\n   */\n  NGHTTP2_GOAWAY = 0x07,\n  /**\n   * The WINDOW_UPDATE frame.\n   */\n  NGHTTP2_WINDOW_UPDATE = 0x08,\n  /**\n   * The CONTINUATION frame.  This frame type won't be passed to any\n   * callbacks because the library processes this frame type and its\n   * preceding HEADERS/PUSH_PROMISE as a single frame.\n   */\n  NGHTTP2_CONTINUATION = 0x09,\n  /**\n   * The ALTSVC frame, which is defined in `RFC 7383\n   * <https://tools.ietf.org/html/rfc7838#section-4>`_.\n   */\n  NGHTTP2_ALTSVC = 0x0a,\n  /**\n   * The ORIGIN frame, which is defined by `RFC 8336\n   * <https://tools.ietf.org/html/rfc8336>`_.\n   */\n  NGHTTP2_ORIGIN = 0x0c,\n  /**\n   * The PRIORITY_UPDATE frame, which is defined by :rfc:`9218`.\n   */\n  NGHTTP2_PRIORITY_UPDATE = 0x10\n} nghttp2_frame_type;\n\n/**\n * @enum\n *\n * The flags for HTTP/2 frames.  This enum defines all flags for all\n * frames.\n */\ntypedef enum {\n  /**\n   * No flag set.\n   */\n  NGHTTP2_FLAG_NONE = 0,\n  /**\n   * The END_STREAM flag.\n   */\n  NGHTTP2_FLAG_END_STREAM = 0x01,\n  /**\n   * The END_HEADERS flag.\n   */\n  NGHTTP2_FLAG_END_HEADERS = 0x04,\n  /**\n   * The ACK flag.\n   */\n  NGHTTP2_FLAG_ACK = 0x01,\n  /**\n   * The PADDED flag.\n   */\n  NGHTTP2_FLAG_PADDED = 0x08,\n  /**\n   * The PRIORITY flag.\n   */\n  NGHTTP2_FLAG_PRIORITY = 0x20\n} nghttp2_flag;\n\n/**\n * @enum\n * The SETTINGS ID.\n */\ntypedef enum {\n  /**\n   * SETTINGS_HEADER_TABLE_SIZE\n   */\n  NGHTTP2_SETTINGS_HEADER_TABLE_SIZE = 0x01,\n  /**\n   * SETTINGS_ENABLE_PUSH\n   */\n  NGHTTP2_SETTINGS_ENABLE_PUSH = 0x02,\n  /**\n   * SETTINGS_MAX_CONCURRENT_STREAMS\n   */\n  NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x03,\n  /**\n   * SETTINGS_INITIAL_WINDOW_SIZE\n   */\n  NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x04,\n  /**\n   * SETTINGS_MAX_FRAME_SIZE\n   */\n  NGHTTP2_SETTINGS_MAX_FRAME_SIZE = 0x05,\n  /**\n   * SETTINGS_MAX_HEADER_LIST_SIZE\n   */\n  NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06,\n  /**\n   * SETTINGS_ENABLE_CONNECT_PROTOCOL\n   * (`RFC 8441 <https://tools.ietf.org/html/rfc8441>`_)\n   */\n  NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08,\n  /**\n   * SETTINGS_NO_RFC7540_PRIORITIES (:rfc:`9218`)\n   */\n  NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES = 0x09\n} nghttp2_settings_id;\n/* Note: If we add SETTINGS, update the capacity of\n   NGHTTP2_INBOUND_NUM_IV as well */\n\n/**\n * @macro\n *\n * .. warning::\n *\n *   Deprecated.  The initial max concurrent streams is 0xffffffffu.\n *\n * Default maximum number of incoming concurrent streams.  Use\n * `nghttp2_submit_settings()` with\n * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS`\n * to change the maximum number of incoming concurrent streams.\n *\n * .. note::\n *\n *   The maximum number of outgoing concurrent streams is 100 by\n *   default.\n */\n#define NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1)\n\n/**\n * @enum\n * The status codes for the RST_STREAM and GOAWAY frames.\n */\ntypedef enum {\n  /**\n   * No errors.\n   */\n  NGHTTP2_NO_ERROR = 0x00,\n  /**\n   * PROTOCOL_ERROR\n   */\n  NGHTTP2_PROTOCOL_ERROR = 0x01,\n  /**\n   * INTERNAL_ERROR\n   */\n  NGHTTP2_INTERNAL_ERROR = 0x02,\n  /**\n   * FLOW_CONTROL_ERROR\n   */\n  NGHTTP2_FLOW_CONTROL_ERROR = 0x03,\n  /**\n   * SETTINGS_TIMEOUT\n   */\n  NGHTTP2_SETTINGS_TIMEOUT = 0x04,\n  /**\n   * STREAM_CLOSED\n   */\n  NGHTTP2_STREAM_CLOSED = 0x05,\n  /**\n   * FRAME_SIZE_ERROR\n   */\n  NGHTTP2_FRAME_SIZE_ERROR = 0x06,\n  /**\n   * REFUSED_STREAM\n   */\n  NGHTTP2_REFUSED_STREAM = 0x07,\n  /**\n   * CANCEL\n   */\n  NGHTTP2_CANCEL = 0x08,\n  /**\n   * COMPRESSION_ERROR\n   */\n  NGHTTP2_COMPRESSION_ERROR = 0x09,\n  /**\n   * CONNECT_ERROR\n   */\n  NGHTTP2_CONNECT_ERROR = 0x0a,\n  /**\n   * ENHANCE_YOUR_CALM\n   */\n  NGHTTP2_ENHANCE_YOUR_CALM = 0x0b,\n  /**\n   * INADEQUATE_SECURITY\n   */\n  NGHTTP2_INADEQUATE_SECURITY = 0x0c,\n  /**\n   * HTTP_1_1_REQUIRED\n   */\n  NGHTTP2_HTTP_1_1_REQUIRED = 0x0d\n} nghttp2_error_code;\n\n/**\n * @struct\n * The frame header.\n */\ntypedef struct {\n  /**\n   * The length field of this frame, excluding frame header.\n   */\n  size_t length;\n  /**\n   * The stream identifier (aka, stream ID)\n   */\n  int32_t stream_id;\n  /**\n   * The type of this frame.  See `nghttp2_frame_type`.\n   */\n  uint8_t type;\n  /**\n   * The flags.\n   */\n  uint8_t flags;\n  /**\n   * Reserved bit in frame header.  Currently, this is always set to 0\n   * and application should not expect something useful in here.\n   */\n  uint8_t reserved;\n} nghttp2_frame_hd;\n\n/**\n * @union\n *\n * This union represents the some kind of data source passed to\n * :type:`nghttp2_data_source_read_callback2`.\n */\ntypedef union {\n  /**\n   * The integer field, suitable for a file descriptor.\n   */\n  int fd;\n  /**\n   * The pointer to an arbitrary object.\n   */\n  void *ptr;\n} nghttp2_data_source;\n\n/**\n * @enum\n *\n * The flags used to set in |data_flags| output parameter in\n * :type:`nghttp2_data_source_read_callback2`.\n */\ntypedef enum {\n  /**\n   * No flag set.\n   */\n  NGHTTP2_DATA_FLAG_NONE = 0,\n  /**\n   * Indicates EOF was sensed.\n   */\n  NGHTTP2_DATA_FLAG_EOF = 0x01,\n  /**\n   * Indicates that END_STREAM flag must not be set even if\n   * NGHTTP2_DATA_FLAG_EOF is set.  Usually this flag is used to send\n   * trailer fields with `nghttp2_submit_request2()` or\n   * `nghttp2_submit_response2()`.\n   */\n  NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02,\n  /**\n   * Indicates that application will send complete DATA frame in\n   * :type:`nghttp2_send_data_callback`.\n   */\n  NGHTTP2_DATA_FLAG_NO_COPY = 0x04\n} nghttp2_data_flag;\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @functypedef\n *\n * .. warning::\n *\n *   Deprecated.  Use :type:`nghttp2_data_source_read_callback2`\n *   instead.\n *\n * Callback function invoked when the library wants to read data from\n * the |source|.  The read data is sent in the stream |stream_id|.\n * The implementation of this function must read at most |length|\n * bytes of data from |source| (or possibly other places) and store\n * them in |buf| and return number of data stored in |buf|.  If EOF is\n * reached, set :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag\n * in |*data_flags|.\n *\n * Sometime it is desirable to avoid copying data into |buf| and let\n * application to send data directly.  To achieve this, set\n * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` to\n * |*data_flags| (and possibly other flags, just like when we do\n * copy), and return the number of bytes to send without copying data\n * into |buf|.  The library, seeing\n * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY`, will invoke\n * :type:`nghttp2_send_data_callback`.  The application must send\n * complete DATA frame in that callback.\n *\n * If this callback is set by `nghttp2_submit_request()`,\n * `nghttp2_submit_response()` or `nghttp2_submit_headers()` and\n * `nghttp2_submit_data()` with flag parameter\n * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` set, and\n * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag is set to\n * |*data_flags|, DATA frame will have END_STREAM flag set.  Usually,\n * this is expected behaviour and all are fine.  One exception is send\n * trailer fields.  You cannot send trailer fields after sending frame\n * with END_STREAM set.  To avoid this problem, one can set\n * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_END_STREAM` along\n * with :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` to signal the\n * library not to set END_STREAM in DATA frame.  Then application can\n * use `nghttp2_submit_trailer()` to send trailer fields.\n * `nghttp2_submit_trailer()` can be called inside this callback.\n *\n * If the application wants to postpone DATA frames (e.g.,\n * asynchronous I/O, or reading data blocks for long time), it is\n * achieved by returning :enum:`nghttp2_error.NGHTTP2_ERR_DEFERRED`\n * without reading any data in this invocation.  The library removes\n * DATA frame from the outgoing queue temporarily.  To move back\n * deferred DATA frame to outgoing queue, call\n * `nghttp2_session_resume_data()`.\n *\n * By default, |length| is limited to 16KiB at maximum.  If peer\n * allows larger frames, application can enlarge transmission buffer\n * size.  See :type:`nghttp2_data_source_read_length_callback` for\n * more details.\n *\n * If the application just wants to return from\n * `nghttp2_session_send()` or `nghttp2_session_mem_send()` without\n * sending anything, return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE`.\n *\n * In case of error, there are 2 choices. Returning\n * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will\n * close the stream by issuing RST_STREAM with\n * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`.  If a different\n * error code is desirable, use `nghttp2_submit_rst_stream()` with a\n * desired error code and then return\n * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.\n * Returning :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will\n * signal the entire session failure.\n */\ntypedef ssize_t (*nghttp2_data_source_read_callback)(\n  nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length,\n  uint32_t *data_flags, nghttp2_data_source *source, void *user_data);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @functypedef\n *\n * Callback function invoked when the library wants to read data from\n * the |source|.  The read data is sent in the stream |stream_id|.\n * The implementation of this function must read at most |length|\n * bytes of data from |source| (or possibly other places) and store\n * them in |buf| and return number of data stored in |buf|.  If EOF is\n * reached, set :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag\n * in |*data_flags|.\n *\n * Sometime it is desirable to avoid copying data into |buf| and let\n * application to send data directly.  To achieve this, set\n * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` to\n * |*data_flags| (and possibly other flags, just like when we do\n * copy), and return the number of bytes to send without copying data\n * into |buf|.  The library, seeing\n * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY`, will invoke\n * :type:`nghttp2_send_data_callback`.  The application must send\n * complete DATA frame in that callback.\n *\n * If this callback is set by `nghttp2_submit_request2()`,\n * `nghttp2_submit_response2()` or `nghttp2_submit_headers()` and\n * `nghttp2_submit_data2()` with flag parameter\n * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` set, and\n * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag is set to\n * |*data_flags|, DATA frame will have END_STREAM flag set.  Usually,\n * this is expected behaviour and all are fine.  One exception is send\n * trailer fields.  You cannot send trailer fields after sending frame\n * with END_STREAM set.  To avoid this problem, one can set\n * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_END_STREAM` along\n * with :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` to signal the\n * library not to set END_STREAM in DATA frame.  Then application can\n * use `nghttp2_submit_trailer()` to send trailer fields.\n * `nghttp2_submit_trailer()` can be called inside this callback.\n *\n * If the application wants to postpone DATA frames (e.g.,\n * asynchronous I/O, or reading data blocks for long time), it is\n * achieved by returning :enum:`nghttp2_error.NGHTTP2_ERR_DEFERRED`\n * without reading any data in this invocation.  The library removes\n * DATA frame from the outgoing queue temporarily.  To move back\n * deferred DATA frame to outgoing queue, call\n * `nghttp2_session_resume_data()`.\n *\n * By default, |length| is limited to 16KiB at maximum.  If peer\n * allows larger frames, application can enlarge transmission buffer\n * size.  See :type:`nghttp2_data_source_read_length_callback` for\n * more details.\n *\n * If the application just wants to return from\n * `nghttp2_session_send()` or `nghttp2_session_mem_send2()` without\n * sending anything, return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE`.\n *\n * In case of error, there are 2 choices. Returning\n * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will\n * close the stream by issuing RST_STREAM with\n * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`.  If a different\n * error code is desirable, use `nghttp2_submit_rst_stream()` with a\n * desired error code and then return\n * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.\n * Returning :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will\n * signal the entire session failure.\n */\ntypedef nghttp2_ssize (*nghttp2_data_source_read_callback2)(\n  nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length,\n  uint32_t *data_flags, nghttp2_data_source *source, void *user_data);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @struct\n *\n * .. warning::\n *\n *   Deprecated.  Use :type:`nghttp2_data_provider2` instead.\n *\n * This struct represents the data source and the way to read a chunk\n * of data from it.\n */\ntypedef struct {\n  /**\n   * The data source.\n   */\n  nghttp2_data_source source;\n  /**\n   * The callback function to read a chunk of data from the |source|.\n   */\n  nghttp2_data_source_read_callback read_callback;\n} nghttp2_data_provider;\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @struct\n *\n * This struct represents the data source and the way to read a chunk\n * of data from it.\n */\ntypedef struct {\n  /**\n   * The data source.\n   */\n  nghttp2_data_source source;\n  /**\n   * The callback function to read a chunk of data from the |source|.\n   */\n  nghttp2_data_source_read_callback2 read_callback;\n} nghttp2_data_provider2;\n\n/**\n * @struct\n *\n * The DATA frame.  The received data is delivered via\n * :type:`nghttp2_on_data_chunk_recv_callback`.\n */\ntypedef struct {\n  nghttp2_frame_hd hd;\n  /**\n   * The length of the padding in this frame.  This includes PAD_HIGH\n   * and PAD_LOW.\n   */\n  size_t padlen;\n} nghttp2_data;\n\n/**\n * @enum\n *\n * The category of HEADERS, which indicates the role of the frame.  In\n * HTTP/2 spec, request, response, push response and other arbitrary\n * headers (e.g., trailer fields) are all called just HEADERS.  To\n * give the application the role of incoming HEADERS frame, we define\n * several categories.\n */\ntypedef enum {\n  /**\n   * The HEADERS frame is opening new stream, which is analogous to\n   * SYN_STREAM in SPDY.\n   */\n  NGHTTP2_HCAT_REQUEST = 0,\n  /**\n   * The HEADERS frame is the first response headers, which is\n   * analogous to SYN_REPLY in SPDY.\n   */\n  NGHTTP2_HCAT_RESPONSE = 1,\n  /**\n   * The HEADERS frame is the first headers sent against reserved\n   * stream.\n   */\n  NGHTTP2_HCAT_PUSH_RESPONSE = 2,\n  /**\n   * The HEADERS frame which does not apply for the above categories,\n   * which is analogous to HEADERS in SPDY.  If non-final response\n   * (e.g., status 1xx) is used, final response HEADERS frame will be\n   * categorized here.\n   */\n  NGHTTP2_HCAT_HEADERS = 3\n} nghttp2_headers_category;\n\n/**\n * @struct\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.\n *\n * The structure to specify stream dependency.\n */\ntypedef struct {\n  /**\n   * The stream ID of the stream to depend on.  Specifying 0 makes\n   * stream not depend any other stream.\n   */\n  int32_t stream_id;\n  /**\n   * The weight of this dependency.\n   */\n  int32_t weight;\n  /**\n   * nonzero means exclusive dependency\n   */\n  uint8_t exclusive;\n} nghttp2_priority_spec;\n\n/**\n * @struct\n *\n * The HEADERS frame.  It has the following members:\n */\ntypedef struct {\n  /**\n   * The frame header.\n   */\n  nghttp2_frame_hd hd;\n  /**\n   * The length of the padding in this frame.  This includes PAD_HIGH\n   * and PAD_LOW.\n   */\n  size_t padlen;\n  /**\n   * .. warning::\n   *\n   *   Deprecated.  :rfc:`7540` priorities are deprecated by\n   *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n   *   prioritization scheme.\n   *\n   * The priority specification\n   */\n  nghttp2_priority_spec pri_spec;\n  /**\n   * The name/value pairs.\n   */\n  nghttp2_nv *nva;\n  /**\n   * The number of name/value pairs in |nva|.\n   */\n  size_t nvlen;\n  /**\n   * The category of this HEADERS frame.\n   */\n  nghttp2_headers_category cat;\n} nghttp2_headers;\n\n/**\n * @struct\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.\n *\n * The PRIORITY frame.  It has the following members:\n */\ntypedef struct {\n  /**\n   * The frame header.\n   */\n  nghttp2_frame_hd hd;\n  /**\n   * The priority specification.\n   */\n  nghttp2_priority_spec pri_spec;\n} nghttp2_priority;\n\n/**\n * @struct\n *\n * The RST_STREAM frame.  It has the following members:\n */\ntypedef struct {\n  /**\n   * The frame header.\n   */\n  nghttp2_frame_hd hd;\n  /**\n   * The error code.  See :type:`nghttp2_error_code`.\n   */\n  uint32_t error_code;\n} nghttp2_rst_stream;\n\n/**\n * @struct\n *\n * The SETTINGS ID/Value pair.  It has the following members:\n */\ntypedef struct {\n  /**\n   * The SETTINGS ID.  See :type:`nghttp2_settings_id`.\n   */\n  int32_t settings_id;\n  /**\n   * The value of this entry.\n   */\n  uint32_t value;\n} nghttp2_settings_entry;\n\n/**\n * @struct\n *\n * The SETTINGS frame.  It has the following members:\n */\ntypedef struct {\n  /**\n   * The frame header.\n   */\n  nghttp2_frame_hd hd;\n  /**\n   * The number of SETTINGS ID/Value pairs in |iv|.\n   */\n  size_t niv;\n  /**\n   * The pointer to the array of SETTINGS ID/Value pair.\n   */\n  nghttp2_settings_entry *iv;\n} nghttp2_settings;\n\n/**\n * @struct\n *\n * The PUSH_PROMISE frame.  It has the following members:\n */\ntypedef struct {\n  /**\n   * The frame header.\n   */\n  nghttp2_frame_hd hd;\n  /**\n   * The length of the padding in this frame.  This includes PAD_HIGH\n   * and PAD_LOW.\n   */\n  size_t padlen;\n  /**\n   * The name/value pairs.\n   */\n  nghttp2_nv *nva;\n  /**\n   * The number of name/value pairs in |nva|.\n   */\n  size_t nvlen;\n  /**\n   * The promised stream ID\n   */\n  int32_t promised_stream_id;\n  /**\n   * Reserved bit.  Currently this is always set to 0 and application\n   * should not expect something useful in here.\n   */\n  uint8_t reserved;\n} nghttp2_push_promise;\n\n/**\n * @struct\n *\n * The PING frame.  It has the following members:\n */\ntypedef struct {\n  /**\n   * The frame header.\n   */\n  nghttp2_frame_hd hd;\n  /**\n   * The opaque data\n   */\n  uint8_t opaque_data[8];\n} nghttp2_ping;\n\n/**\n * @struct\n *\n * The GOAWAY frame.  It has the following members:\n */\ntypedef struct {\n  /**\n   * The frame header.\n   */\n  nghttp2_frame_hd hd;\n  /**\n   * The last stream stream ID.\n   */\n  int32_t last_stream_id;\n  /**\n   * The error code.  See :type:`nghttp2_error_code`.\n   */\n  uint32_t error_code;\n  /**\n   * The additional debug data\n   */\n  uint8_t *opaque_data;\n  /**\n   * The length of |opaque_data| member.\n   */\n  size_t opaque_data_len;\n  /**\n   * Reserved bit.  Currently this is always set to 0 and application\n   * should not expect something useful in here.\n   */\n  uint8_t reserved;\n} nghttp2_goaway;\n\n/**\n * @struct\n *\n * The WINDOW_UPDATE frame.  It has the following members:\n */\ntypedef struct {\n  /**\n   * The frame header.\n   */\n  nghttp2_frame_hd hd;\n  /**\n   * The window size increment.\n   */\n  int32_t window_size_increment;\n  /**\n   * Reserved bit.  Currently this is always set to 0 and application\n   * should not expect something useful in here.\n   */\n  uint8_t reserved;\n} nghttp2_window_update;\n\n/**\n * @struct\n *\n * The extension frame.  It has following members:\n */\ntypedef struct {\n  /**\n   * The frame header.\n   */\n  nghttp2_frame_hd hd;\n  /**\n   * The pointer to extension payload.  The exact pointer type is\n   * determined by hd.type.\n   *\n   * Currently, no extension is supported.  This is a place holder for\n   * the future extensions.\n   */\n  void *payload;\n} nghttp2_extension;\n\n/**\n * @union\n *\n * This union includes all frames to pass them to various function\n * calls as nghttp2_frame type.  The CONTINUATION frame is omitted\n * from here because the library deals with it internally.\n */\ntypedef union {\n  /**\n   * The frame header, which is convenient to inspect frame header.\n   */\n  nghttp2_frame_hd hd;\n  /**\n   * The DATA frame.\n   */\n  nghttp2_data data;\n  /**\n   * The HEADERS frame.\n   */\n  nghttp2_headers headers;\n  /**\n   * The PRIORITY frame.\n   */\n  nghttp2_priority priority;\n  /**\n   * The RST_STREAM frame.\n   */\n  nghttp2_rst_stream rst_stream;\n  /**\n   * The SETTINGS frame.\n   */\n  nghttp2_settings settings;\n  /**\n   * The PUSH_PROMISE frame.\n   */\n  nghttp2_push_promise push_promise;\n  /**\n   * The PING frame.\n   */\n  nghttp2_ping ping;\n  /**\n   * The GOAWAY frame.\n   */\n  nghttp2_goaway goaway;\n  /**\n   * The WINDOW_UPDATE frame.\n   */\n  nghttp2_window_update window_update;\n  /**\n   * The extension frame.\n   */\n  nghttp2_extension ext;\n} nghttp2_frame;\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @functypedef\n *\n * .. warning::\n *\n *   Deprecated.  Use :type:`nghttp2_send_callback2` instead.\n *\n * Callback function invoked when |session| wants to send data to the\n * remote peer.  The implementation of this function must send at most\n * |length| bytes of data stored in |data|.  The |flags| is currently\n * not used and always 0. It must return the number of bytes sent if\n * it succeeds.  If it cannot send any single byte without blocking,\n * it must return :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`.  For\n * other errors, it must return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  The\n * |user_data| pointer is the third argument passed in to the call to\n * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.\n *\n * This callback is required if the application uses\n * `nghttp2_session_send()` to send data to the remote endpoint.  If\n * the application uses solely `nghttp2_session_mem_send()` instead,\n * this callback function is unnecessary.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_send_callback()`.\n *\n * .. note::\n *\n *   The |length| may be very small.  If that is the case, and\n *   application disables Nagle algorithm (``TCP_NODELAY``), then just\n *   writing |data| to the network stack leads to very small packet,\n *   and it is very inefficient.  An application should be responsible\n *   to buffer up small chunks of data as necessary to avoid this\n *   situation.\n */\ntypedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session,\n                                         const uint8_t *data, size_t length,\n                                         int flags, void *user_data);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @functypedef\n *\n * Callback function invoked when |session| wants to send data to the\n * remote peer.  The implementation of this function must send at most\n * |length| bytes of data stored in |data|.  The |flags| is currently\n * not used and always 0. It must return the number of bytes sent if\n * it succeeds.  If it cannot send any single byte without blocking,\n * it must return :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`.  For\n * other errors, it must return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  The\n * |user_data| pointer is the third argument passed in to the call to\n * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.\n *\n * This callback is required if the application uses\n * `nghttp2_session_send()` to send data to the remote endpoint.  If\n * the application uses solely `nghttp2_session_mem_send2()` instead,\n * this callback function is unnecessary.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_send_callback2()`.\n *\n * .. note::\n *\n *   The |length| may be very small.  If that is the case, and\n *   application disables Nagle algorithm (``TCP_NODELAY``), then just\n *   writing |data| to the network stack leads to very small packet,\n *   and it is very inefficient.  An application should be responsible\n *   to buffer up small chunks of data as necessary to avoid this\n *   situation.\n */\ntypedef nghttp2_ssize (*nghttp2_send_callback2)(nghttp2_session *session,\n                                                const uint8_t *data,\n                                                size_t length, int flags,\n                                                void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when\n * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` is used in\n * :type:`nghttp2_data_source_read_callback` to send complete DATA\n * frame.\n *\n * The |frame| is a DATA frame to send.  The |framehd| is the\n * serialized frame header (9 bytes). The |length| is the length of\n * application data to send (this does not include padding).  The\n * |source| is the same pointer passed to\n * :type:`nghttp2_data_source_read_callback`.\n *\n * The application first must send frame header |framehd| of length 9\n * bytes.  If ``frame->data.padlen > 0``, send 1 byte of value\n * ``frame->data.padlen - 1``.  Then send exactly |length| bytes of\n * application data.  Finally, if ``frame->data.padlen > 1``, send\n * ``frame->data.padlen - 1`` bytes of zero as padding.\n *\n * The application has to send complete DATA frame in this callback.\n * If all data were written successfully, return 0.\n *\n * If it cannot send any data at all, just return\n * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`; the library will call\n * this callback with the same parameters later (It is recommended to\n * send complete DATA frame at once in this function to deal with\n * error; if partial frame data has already sent, it is impossible to\n * send another data in that state, and all we can do is tear down\n * connection).  When data is fully processed, but application wants\n * to make `nghttp2_session_mem_send2()` or `nghttp2_session_send()`\n * return immediately without processing next frames, return\n * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE`.  If application decided to\n * reset this stream, return\n * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`, then\n * the library will send RST_STREAM with INTERNAL_ERROR as error code.\n * The application can also return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, which will\n * result in connection closure.  Returning any other value is treated\n * as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned.\n */\ntypedef int (*nghttp2_send_data_callback)(nghttp2_session *session,\n                                          nghttp2_frame *frame,\n                                          const uint8_t *framehd, size_t length,\n                                          nghttp2_data_source *source,\n                                          void *user_data);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @functypedef\n *\n * .. warning::\n *\n *   Deprecated.  Use :type:`nghttp2_recv_callback2` instead.\n *\n * Callback function invoked when |session| wants to receive data from\n * the remote peer.  The implementation of this function must read at\n * most |length| bytes of data and store it in |buf|.  The |flags| is\n * currently not used and always 0.  It must return the number of\n * bytes written in |buf| if it succeeds.  If it cannot read any\n * single byte without blocking, it must return\n * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`.  If it gets EOF\n * before it reads any single byte, it must return\n * :enum:`nghttp2_error.NGHTTP2_ERR_EOF`.  For other errors, it must\n * return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n * Returning 0 is treated as\n * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`.  The |user_data|\n * pointer is the third argument passed in to the call to\n * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.\n *\n * This callback is required if the application uses\n * `nghttp2_session_recv()` to receive data from the remote endpoint.\n * If the application uses solely `nghttp2_session_mem_recv()`\n * instead, this callback function is unnecessary.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_recv_callback()`.\n */\ntypedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf,\n                                         size_t length, int flags,\n                                         void *user_data);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @functypedef\n *\n * Callback function invoked when |session| wants to receive data from\n * the remote peer.  The implementation of this function must read at\n * most |length| bytes of data and store it in |buf|.  The |flags| is\n * currently not used and always 0.  It must return the number of\n * bytes written in |buf| if it succeeds.  If it cannot read any\n * single byte without blocking, it must return\n * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`.  If it gets EOF\n * before it reads any single byte, it must return\n * :enum:`nghttp2_error.NGHTTP2_ERR_EOF`.  For other errors, it must\n * return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n * Returning 0 is treated as\n * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`.  The |user_data|\n * pointer is the third argument passed in to the call to\n * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.\n *\n * This callback is required if the application uses\n * `nghttp2_session_recv()` to receive data from the remote endpoint.\n * If the application uses solely `nghttp2_session_mem_recv2()`\n * instead, this callback function is unnecessary.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_recv_callback2()`.\n */\ntypedef nghttp2_ssize (*nghttp2_recv_callback2)(nghttp2_session *session,\n                                                uint8_t *buf, size_t length,\n                                                int flags, void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked by `nghttp2_session_recv()` and\n * `nghttp2_session_mem_recv2()` when a frame is received.  The\n * |user_data| pointer is the third argument passed in to the call to\n * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.\n *\n * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen``\n * member of their data structure are always ``NULL`` and 0\n * respectively.  The header name/value pairs are emitted via\n * :type:`nghttp2_on_header_callback`.\n *\n * Only HEADERS and DATA frame can signal the end of incoming data.\n * If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the\n * |frame| is the last frame from the remote peer in this stream.\n *\n * This callback won't be called for CONTINUATION frames.\n * HEADERS/PUSH_PROMISE + CONTINUATIONs are treated as single frame.\n *\n * The implementation of this function must return 0 if it succeeds.\n * If nonzero value is returned, it is treated as fatal error and\n * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()`\n * functions immediately return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_on_frame_recv_callback()`.\n */\ntypedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session,\n                                              const nghttp2_frame *frame,\n                                              void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked by `nghttp2_session_recv()` and\n * `nghttp2_session_mem_recv2()` when an invalid non-DATA frame is\n * received.  The error is indicated by the |lib_error_code|, which is\n * one of the values defined in :type:`nghttp2_error`.  When this\n * callback function is invoked, the library automatically submits\n * either RST_STREAM or GOAWAY frame.  The |user_data| pointer is the\n * third argument passed in to the call to\n * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.\n *\n * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen``\n * member of their data structure are always ``NULL`` and 0\n * respectively.\n *\n * The implementation of this function must return 0 if it succeeds.\n * If nonzero is returned, it is treated as fatal error and\n * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()`\n * functions immediately return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`.\n */\ntypedef int (*nghttp2_on_invalid_frame_recv_callback)(\n  nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code,\n  void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when a chunk of data in DATA frame is\n * received.  The |stream_id| is the stream ID this DATA frame belongs\n * to.  The |flags| is the flags of DATA frame which this data chunk\n * is contained.  ``(flags & NGHTTP2_FLAG_END_STREAM) != 0`` does not\n * necessarily mean this chunk of data is the last one in the stream.\n * You should use :type:`nghttp2_on_frame_recv_callback` to know all\n * data frames are received.  The |user_data| pointer is the third\n * argument passed in to the call to `nghttp2_session_client_new()` or\n * `nghttp2_session_server_new()`.\n *\n * If the application uses `nghttp2_session_mem_recv2()`, it can\n * return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` to make\n * `nghttp2_session_mem_recv2()` return without processing further\n * input bytes.  The memory by pointed by the |data| is retained until\n * `nghttp2_session_mem_recv2()` or `nghttp2_session_recv()` is\n * called.  The application must retain the input bytes which was used\n * to produce the |data| parameter, because it may refer to the memory\n * region included in the input bytes.\n *\n * The implementation of this function must return 0 if it succeeds.\n * If nonzero is returned, it is treated as fatal error, and\n * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()`\n * functions immediately return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_on_data_chunk_recv_callback()`.\n */\ntypedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session,\n                                                   uint8_t flags,\n                                                   int32_t stream_id,\n                                                   const uint8_t *data,\n                                                   size_t len, void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked just before the non-DATA frame |frame| is\n * sent.  The |user_data| pointer is the third argument passed in to\n * the call to `nghttp2_session_client_new()` or\n * `nghttp2_session_server_new()`.\n *\n * The implementation of this function must return 0 if it succeeds.\n * It can also return :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL` to\n * cancel the transmission of the given frame.\n *\n * If there is a fatal error while executing this callback, the\n * implementation should return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, which makes\n * `nghttp2_session_send()` and `nghttp2_session_mem_send2()`\n * functions immediately return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n *\n * If the other value is returned, it is treated as if\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned.\n * But the implementation should not rely on this since the library\n * may define new return value to extend its capability.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_before_frame_send_callback()`.\n */\ntypedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session,\n                                                  const nghttp2_frame *frame,\n                                                  void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked after the frame |frame| is sent.  The\n * |user_data| pointer is the third argument passed in to the call to\n * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`.\n *\n * The implementation of this function must return 0 if it succeeds.\n * If nonzero is returned, it is treated as fatal error and\n * `nghttp2_session_send()` and `nghttp2_session_mem_send2()`\n * functions immediately return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_on_frame_send_callback()`.\n */\ntypedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session,\n                                              const nghttp2_frame *frame,\n                                              void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked after the non-DATA frame |frame| is not\n * sent because of the error.  The error is indicated by the\n * |lib_error_code|, which is one of the values defined in\n * :type:`nghttp2_error`.  The |user_data| pointer is the third\n * argument passed in to the call to `nghttp2_session_client_new()` or\n * `nghttp2_session_server_new()`.\n *\n * The implementation of this function must return 0 if it succeeds.\n * If nonzero is returned, it is treated as fatal error and\n * `nghttp2_session_send()` and `nghttp2_session_mem_send2()`\n * functions immediately return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n *\n * `nghttp2_session_get_stream_user_data()` can be used to get\n * associated data.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_on_frame_not_send_callback()`.\n */\ntypedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session,\n                                                  const nghttp2_frame *frame,\n                                                  int lib_error_code,\n                                                  void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when the stream |stream_id| is closed.\n * The reason of closure is indicated by the |error_code|.  The\n * |error_code| is usually one of :enum:`nghttp2_error_code`, but that\n * is not guaranteed.  The stream_user_data, which was specified in\n * `nghttp2_submit_request2()` or `nghttp2_submit_headers()`, is still\n * available in this function.  The |user_data| pointer is the third\n * argument passed in to the call to `nghttp2_session_client_new()` or\n * `nghttp2_session_server_new()`.\n *\n * This function is also called for a stream in reserved state.\n *\n * The implementation of this function must return 0 if it succeeds.\n * If nonzero is returned, it is treated as fatal error and\n * `nghttp2_session_recv()`, `nghttp2_session_mem_recv2()`,\n * `nghttp2_session_send()`, and `nghttp2_session_mem_send2()`\n * functions immediately return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_on_stream_close_callback()`.\n */\ntypedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session,\n                                                int32_t stream_id,\n                                                uint32_t error_code,\n                                                void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when the reception of header block in\n * HEADERS or PUSH_PROMISE is started.  Each header name/value pair\n * will be emitted by :type:`nghttp2_on_header_callback`.\n *\n * The ``frame->hd.flags`` may not have\n * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_HEADERS` flag set, which\n * indicates that one or more CONTINUATION frames are involved.  But\n * the application does not need to care about that because the header\n * name/value pairs are emitted transparently regardless of\n * CONTINUATION frames.\n *\n * The server applications probably create an object to store\n * information about new stream if ``frame->hd.type ==\n * NGHTTP2_HEADERS`` and ``frame->headers.cat ==\n * NGHTTP2_HCAT_REQUEST``.  If |session| is configured as server side,\n * ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST``\n * containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing\n * trailer fields and never get PUSH_PROMISE in this callback.\n *\n * For the client applications, ``frame->hd.type`` is either\n * ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``.  In case of\n * ``NGHTTP2_HEADERS``, ``frame->headers.cat ==\n * NGHTTP2_HCAT_RESPONSE`` means that it is the first response\n * headers, but it may be non-final response which is indicated by 1xx\n * status code.  In this case, there may be zero or more HEADERS frame\n * with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has\n * non-final response code and finally client gets exactly one HEADERS\n * frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS``\n * containing final response headers (non-1xx status code).  The\n * trailer fields also has ``frame->headers.cat ==\n * NGHTTP2_HCAT_HEADERS`` which does not contain any status code.\n *\n * Returning\n * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will\n * close the stream (promised stream if frame is PUSH_PROMISE) by\n * issuing RST_STREAM with\n * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`.  In this case,\n * :type:`nghttp2_on_header_callback` and\n * :type:`nghttp2_on_frame_recv_callback` will not be invoked.  If a\n * different error code is desirable, use\n * `nghttp2_submit_rst_stream()` with a desired error code and then\n * return :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.\n * Again, use ``frame->push_promise.promised_stream_id`` as stream_id\n * parameter in `nghttp2_submit_rst_stream()` if frame is\n * PUSH_PROMISE.\n *\n * The implementation of this function must return 0 if it succeeds.\n * It can return\n * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to\n * reset the stream (promised stream if frame is PUSH_PROMISE).  For\n * critical errors, it must return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  If the other\n * value is returned, it is treated as if\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned.  If\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned,\n * `nghttp2_session_mem_recv2()` function will immediately return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_on_begin_headers_callback()`.\n */\ntypedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session,\n                                                 const nghttp2_frame *frame,\n                                                 void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when a header name/value pair is received\n * for the |frame|.  The |name| of length |namelen| is header name.\n * The |value| of length |valuelen| is header value.  The |flags| is\n * bitwise OR of one or more of :type:`nghttp2_nv_flag`.\n *\n * If :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_INDEX` is set in\n * |flags|, the receiver must not index this name/value pair when\n * forwarding it to the next hop.  More specifically, \"Literal Header\n * Field never Indexed\" representation must be used in HPACK encoding.\n *\n * When this callback is invoked, ``frame->hd.type`` is either\n * :enum:`nghttp2_frame_type.NGHTTP2_HEADERS` or\n * :enum:`nghttp2_frame_type.NGHTTP2_PUSH_PROMISE`.  After all header\n * name/value pairs are processed with this callback, and no error has\n * been detected, :type:`nghttp2_on_frame_recv_callback` will be\n * invoked.  If there is an error in decompression,\n * :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be\n * invoked.\n *\n * Both |name| and |value| are guaranteed to be NULL-terminated.  The\n * |namelen| and |valuelen| do not include terminal NULL.  If\n * `nghttp2_option_set_no_http_messaging()` is used with nonzero\n * value, NULL character may be included in |name| or |value| before\n * terminating NULL.\n *\n * Please note that unless `nghttp2_option_set_no_http_messaging()` is\n * used, nghttp2 library does perform validation against the |name|\n * and the |value| using `nghttp2_check_header_name()` and\n * `nghttp2_check_header_value()`.  In addition to this, nghttp2\n * performs validation based on HTTP Messaging rule, which is briefly\n * explained in :ref:`http-messaging` section.\n *\n * If the application uses `nghttp2_session_mem_recv2()`, it can\n * return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` to make\n * `nghttp2_session_mem_recv2()` return without processing further\n * input bytes.  The memory pointed by |frame|, |name| and |value|\n * parameters are retained until `nghttp2_session_mem_recv2()` or\n * `nghttp2_session_recv()` is called.  The application must retain\n * the input bytes which was used to produce these parameters, because\n * it may refer to the memory region included in the input bytes.\n *\n * Returning\n * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will\n * close the stream (promised stream if frame is PUSH_PROMISE) by\n * issuing RST_STREAM with\n * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`.  In this case,\n * :type:`nghttp2_on_header_callback` and\n * :type:`nghttp2_on_frame_recv_callback` will not be invoked.  If a\n * different error code is desirable, use\n * `nghttp2_submit_rst_stream()` with a desired error code and then\n * return :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.\n * Again, use ``frame->push_promise.promised_stream_id`` as stream_id\n * parameter in `nghttp2_submit_rst_stream()` if frame is\n * PUSH_PROMISE.\n *\n * The implementation of this function must return 0 if it succeeds.\n * It may return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` or\n * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  For\n * other critical failures, it must return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  If the other\n * nonzero value is returned, it is treated as\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  If\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned,\n * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()`\n * functions immediately return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_on_header_callback()`.\n *\n * .. warning::\n *\n *   Application should properly limit the total buffer size to store\n *   incoming header fields.  Without it, peer may send large number\n *   of header fields or large header fields to cause out of memory in\n *   local endpoint.  Due to how HPACK works, peer can do this\n *   effectively without using much memory on their own.\n */\ntypedef int (*nghttp2_on_header_callback)(nghttp2_session *session,\n                                          const nghttp2_frame *frame,\n                                          const uint8_t *name, size_t namelen,\n                                          const uint8_t *value, size_t valuelen,\n                                          uint8_t flags, void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when a header name/value pair is received\n * for the |frame|.  The |name| is header name.  The |value| is header\n * value.  The |flags| is bitwise OR of one or more of\n * :type:`nghttp2_nv_flag`.\n *\n * This callback behaves like :type:`nghttp2_on_header_callback`,\n * except that |name| and |value| are stored in reference counted\n * buffer.  If application wishes to keep these references without\n * copying them, use `nghttp2_rcbuf_incref()` to increment their\n * reference count.  It is the application's responsibility to call\n * `nghttp2_rcbuf_decref()` if they called `nghttp2_rcbuf_incref()` so\n * as not to leak memory.  If the |session| is created by\n * `nghttp2_session_server_new3()` or `nghttp2_session_client_new3()`,\n * the function to free memory is the one belongs to the mem\n * parameter.  As long as this free function alives, |name| and\n * |value| can live after |session| was destroyed.\n */\ntypedef int (*nghttp2_on_header_callback2)(nghttp2_session *session,\n                                           const nghttp2_frame *frame,\n                                           nghttp2_rcbuf *name,\n                                           nghttp2_rcbuf *value, uint8_t flags,\n                                           void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when a invalid header name/value pair is\n * received for the |frame|.\n *\n * The parameter and behaviour are similar to\n * :type:`nghttp2_on_header_callback`.  The difference is that this\n * callback is only invoked when a invalid header name/value pair is\n * received which is treated as stream error if this callback is not\n * set.  Only invalid regular header field are passed to this\n * callback.  In other words, invalid pseudo header field is not\n * passed to this callback.  Also header fields which includes upper\n * cased latter are also treated as error without passing them to this\n * callback.\n *\n * This callback is only considered if HTTP messaging validation is\n * turned on (which is on by default, see\n * `nghttp2_option_set_no_http_messaging()`).\n *\n * With this callback, application inspects the incoming invalid\n * field, and it also can reset stream from this callback by returning\n * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  By\n * default, the error code is\n * :enum:`nghttp2_error_code.NGHTTP2_PROTOCOL_ERROR`.  To change the\n * error code, call `nghttp2_submit_rst_stream()` with the error code\n * of choice in addition to returning\n * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.\n *\n * If 0 is returned, the header field is ignored, and the stream is\n * not reset.\n */\ntypedef int (*nghttp2_on_invalid_header_callback)(\n  nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name,\n  size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags,\n  void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when a invalid header name/value pair is\n * received for the |frame|.\n *\n * The parameter and behaviour are similar to\n * :type:`nghttp2_on_header_callback2`.  The difference is that this\n * callback is only invoked when a invalid header name/value pair is\n * received which is silently ignored if this callback is not set.\n * Only invalid regular header field are passed to this callback.  In\n * other words, invalid pseudo header field is not passed to this\n * callback.  Also header fields which includes upper cased latter are\n * also treated as error without passing them to this callback.\n *\n * This callback is only considered if HTTP messaging validation is\n * turned on (which is on by default, see\n * `nghttp2_option_set_no_http_messaging()`).\n *\n * With this callback, application inspects the incoming invalid\n * field, and it also can reset stream from this callback by returning\n * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  By\n * default, the error code is\n * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`.  To change the\n * error code, call `nghttp2_submit_rst_stream()` with the error code\n * of choice in addition to returning\n * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.\n */\ntypedef int (*nghttp2_on_invalid_header_callback2)(\n  nghttp2_session *session, const nghttp2_frame *frame, nghttp2_rcbuf *name,\n  nghttp2_rcbuf *value, uint8_t flags, void *user_data);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @functypedef\n *\n * .. warning::\n *\n *   Deprecated.  Use :type:`nghttp2_select_padding_callback2`\n *   instead.\n *\n * Callback function invoked when the library asks application how\n * many padding bytes are required for the transmission of the\n * |frame|.  The application must choose the total length of payload\n * including padded bytes in range [frame->hd.length, max_payloadlen],\n * inclusive.  Choosing number not in this range will be treated as\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  Returning\n * ``frame->hd.length`` means no padding is added.  Returning\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will make\n * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions\n * immediately return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_select_padding_callback()`.\n */\ntypedef ssize_t (*nghttp2_select_padding_callback)(nghttp2_session *session,\n                                                   const nghttp2_frame *frame,\n                                                   size_t max_payloadlen,\n                                                   void *user_data);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @functypedef\n *\n * Callback function invoked when the library asks application how\n * many padding bytes are required for the transmission of the\n * |frame|.  The application must choose the total length of payload\n * including padded bytes in range [frame->hd.length, max_payloadlen],\n * inclusive.  Choosing number not in this range will be treated as\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  Returning\n * ``frame->hd.length`` means no padding is added.  Returning\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will make\n * `nghttp2_session_send()` and `nghttp2_session_mem_send2()`\n * functions immediately return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_select_padding_callback2()`.\n */\ntypedef nghttp2_ssize (*nghttp2_select_padding_callback2)(\n  nghttp2_session *session, const nghttp2_frame *frame, size_t max_payloadlen,\n  void *user_data);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @functypedef\n *\n * .. warning::\n *\n *   Deprecated.  Use\n *   :type:`nghttp2_data_source_read_length_callback2` instead.\n *\n * Callback function invoked when library wants to get max length of\n * data to send data to the remote peer.  The implementation of this\n * function should return a value in the following range.  [1,\n * min(|session_remote_window_size|, |stream_remote_window_size|,\n * |remote_max_frame_size|)].  If a value greater than this range is\n * returned than the max allow value will be used.  Returning a value\n * smaller than this range is treated as\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  The\n * |frame_type| is provided for future extensibility and identifies\n * the type of frame (see :type:`nghttp2_frame_type`) for which to get\n * the length for.  Currently supported frame types are:\n * :enum:`nghttp2_frame_type.NGHTTP2_DATA`.\n *\n * This callback can be used to control the length in bytes for which\n * :type:`nghttp2_data_source_read_callback` is allowed to send to the\n * remote endpoint.  This callback is optional.  Returning\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will signal the\n * entire session failure.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_data_source_read_length_callback()`.\n */\ntypedef ssize_t (*nghttp2_data_source_read_length_callback)(\n  nghttp2_session *session, uint8_t frame_type, int32_t stream_id,\n  int32_t session_remote_window_size, int32_t stream_remote_window_size,\n  uint32_t remote_max_frame_size, void *user_data);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @functypedef\n *\n * Callback function invoked when library wants to get max length of\n * data to send data to the remote peer.  The implementation of this\n * function should return a value in the following range.  [1,\n * min(|session_remote_window_size|, |stream_remote_window_size|,\n * |remote_max_frame_size|)].  If a value greater than this range is\n * returned than the max allow value will be used.  Returning a value\n * smaller than this range is treated as\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  The\n * |frame_type| is provided for future extensibility and identifies\n * the type of frame (see :type:`nghttp2_frame_type`) for which to get\n * the length for.  Currently supported frame types are:\n * :enum:`nghttp2_frame_type.NGHTTP2_DATA`.\n *\n * This callback can be used to control the length in bytes for which\n * :type:`nghttp2_data_source_read_callback` is allowed to send to the\n * remote endpoint.  This callback is optional.  Returning\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will signal the\n * entire session failure.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_data_source_read_length_callback2()`.\n */\ntypedef nghttp2_ssize (*nghttp2_data_source_read_length_callback2)(\n  nghttp2_session *session, uint8_t frame_type, int32_t stream_id,\n  int32_t session_remote_window_size, int32_t stream_remote_window_size,\n  uint32_t remote_max_frame_size, void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when a frame header is received.  The\n * |hd| points to received frame header.\n *\n * Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will\n * also be called when frame header of CONTINUATION frame is received.\n *\n * If both :type:`nghttp2_on_begin_frame_callback` and\n * :type:`nghttp2_on_begin_headers_callback` are set and HEADERS or\n * PUSH_PROMISE is received, :type:`nghttp2_on_begin_frame_callback`\n * will be called first.\n *\n * The implementation of this function must return 0 if it succeeds.\n * If nonzero value is returned, it is treated as fatal error and\n * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()`\n * functions immediately return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n *\n * To set this callback to :type:`nghttp2_session_callbacks`, use\n * `nghttp2_session_callbacks_set_on_begin_frame_callback()`.\n */\ntypedef int (*nghttp2_on_begin_frame_callback)(nghttp2_session *session,\n                                               const nghttp2_frame_hd *hd,\n                                               void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when chunk of extension frame payload is\n * received.  The |hd| points to frame header.  The received\n * chunk is |data| of length |len|.\n *\n * The implementation of this function must return 0 if it succeeds.\n *\n * To abort processing this extension frame, return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`.\n *\n * If fatal error occurred, application should return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  In this case,\n * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()`\n * functions immediately return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  If the other\n * values are returned, currently they are treated as\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n */\ntypedef int (*nghttp2_on_extension_chunk_recv_callback)(\n  nghttp2_session *session, const nghttp2_frame_hd *hd, const uint8_t *data,\n  size_t len, void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when library asks the application to\n * unpack extension payload from its wire format.  The extension\n * payload has been passed to the application using\n * :type:`nghttp2_on_extension_chunk_recv_callback`.  The frame header\n * is already unpacked by the library and provided as |hd|.\n *\n * To receive extension frames, the application must tell desired\n * extension frame type to the library using\n * `nghttp2_option_set_user_recv_extension_type()`.\n *\n * The implementation of this function may store the pointer to the\n * created object as a result of unpacking in |*payload|, and returns\n * 0.  The pointer stored in |*payload| is opaque to the library, and\n * the library does not own its pointer.  |*payload| is initialized as\n * ``NULL``.  The |*payload| is available as ``frame->ext.payload`` in\n * :type:`nghttp2_on_frame_recv_callback`.  Therefore if application\n * can free that memory inside :type:`nghttp2_on_frame_recv_callback`\n * callback.  Of course, application has a liberty not to use\n * |*payload|, and do its own mechanism to process extension frames.\n *\n * To abort processing this extension frame, return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`.\n *\n * If fatal error occurred, application should return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  In this case,\n * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()`\n * functions immediately return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  If the other\n * values are returned, currently they are treated as\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n */\ntypedef int (*nghttp2_unpack_extension_callback)(nghttp2_session *session,\n                                                 void **payload,\n                                                 const nghttp2_frame_hd *hd,\n                                                 void *user_data);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @functypedef\n *\n * .. warning::\n *\n *   Deprecated.  Use :type:`nghttp2_pack_extension_callback2`\n *   instead.\n *\n * Callback function invoked when library asks the application to pack\n * extension payload in its wire format.  The frame header will be\n * packed by library.  Application must pack payload only.\n * ``frame->ext.payload`` is the object passed to\n * `nghttp2_submit_extension()` as payload parameter.  Application\n * must pack extension payload to the |buf| of its capacity |len|\n * bytes.  The |len| is at least 16KiB.\n *\n * The implementation of this function should return the number of\n * bytes written into |buf| when it succeeds.\n *\n * To abort processing this extension frame, return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`, and\n * :type:`nghttp2_on_frame_not_send_callback` will be invoked.\n *\n * If fatal error occurred, application should return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  In this case,\n * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions\n * immediately return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  If the other\n * values are returned, currently they are treated as\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  If the return\n * value is strictly larger than |len|, it is treated as\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n */\ntypedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session,\n                                                   uint8_t *buf, size_t len,\n                                                   const nghttp2_frame *frame,\n                                                   void *user_data);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @functypedef\n *\n * Callback function invoked when library asks the application to pack\n * extension payload in its wire format.  The frame header will be\n * packed by library.  Application must pack payload only.\n * ``frame->ext.payload`` is the object passed to\n * `nghttp2_submit_extension()` as payload parameter.  Application\n * must pack extension payload to the |buf| of its capacity |len|\n * bytes.  The |len| is at least 16KiB.\n *\n * The implementation of this function should return the number of\n * bytes written into |buf| when it succeeds.\n *\n * To abort processing this extension frame, return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`, and\n * :type:`nghttp2_on_frame_not_send_callback` will be invoked.\n *\n * If fatal error occurred, application should return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  In this case,\n * `nghttp2_session_send()` and `nghttp2_session_mem_send2()`\n * functions immediately return\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  If the other\n * values are returned, currently they are treated as\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  If the return\n * value is strictly larger than |len|, it is treated as\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n */\ntypedef nghttp2_ssize (*nghttp2_pack_extension_callback2)(\n  nghttp2_session *session, uint8_t *buf, size_t len,\n  const nghttp2_frame *frame, void *user_data);\n\n/**\n * @functypedef\n *\n * .. warning::\n *\n *   Deprecated.  Use :type:`nghttp2_error_callback2` instead.\n *\n * Callback function invoked when library provides the error message\n * intended for human consumption.  This callback is solely for\n * debugging purpose.  The |msg| is typically NULL-terminated string\n * of length |len|.  |len| does not include the sentinel NULL\n * character.\n *\n * The format of error message may change between nghttp2 library\n * versions.  The application should not depend on the particular\n * format.\n *\n * Normally, application should return 0 from this callback.  If fatal\n * error occurred while doing something in this callback, application\n * should return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n * In this case, library will return immediately with return value\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  Currently, if\n * nonzero value is returned from this callback, they are treated as\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, but application\n * should not rely on this details.\n */\ntypedef int (*nghttp2_error_callback)(nghttp2_session *session, const char *msg,\n                                      size_t len, void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when library provides the error code, and\n * message.  This callback is solely for debugging purpose.\n * |lib_error_code| is one of error code defined in\n * :enum:`nghttp2_error`.  The |msg| is typically NULL-terminated\n * string of length |len|, and intended for human consumption.  |len|\n * does not include the sentinel NULL character.\n *\n * The format of error message may change between nghttp2 library\n * versions.  The application should not depend on the particular\n * format.\n *\n * Normally, application should return 0 from this callback.  If fatal\n * error occurred while doing something in this callback, application\n * should return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.\n * In this case, library will return immediately with return value\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`.  Currently, if\n * nonzero value is returned from this callback, they are treated as\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, but application\n * should not rely on this details.\n */\ntypedef int (*nghttp2_error_callback2)(nghttp2_session *session,\n                                       int lib_error_code, const char *msg,\n                                       size_t len, void *user_data);\n\nstruct nghttp2_session_callbacks;\n\n/**\n * @struct\n *\n * Callback functions for :type:`nghttp2_session`.  The details of\n * this structure are intentionally hidden from the public API.\n */\ntypedef struct nghttp2_session_callbacks nghttp2_session_callbacks;\n\n/**\n * @function\n *\n * Initializes |*callbacks_ptr| with NULL values.\n *\n * The initialized object can be used when initializing multiple\n * :type:`nghttp2_session` objects.\n *\n * When the application finished using this object, it can use\n * `nghttp2_session_callbacks_del()` to free its memory.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nNGHTTP2_EXTERN int\nnghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr);\n\n/**\n * @function\n *\n * Frees any resources allocated for |callbacks|.  If |callbacks| is\n * ``NULL``, this function does nothing.\n */\nNGHTTP2_EXTERN void\nnghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  Use `nghttp2_session_callbacks_set_send_callback2()`\n *   with :type:`nghttp2_send_callback2` instead.\n *\n * Sets callback function invoked when a session wants to send data to\n * the remote peer.  This callback is not necessary if the application\n * uses solely `nghttp2_session_mem_send()` to serialize data to\n * transmit.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_callback(\n  nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @function\n *\n * Sets callback function invoked when a session wants to send data to\n * the remote peer.  This callback is not necessary if the application\n * uses solely `nghttp2_session_mem_send2()` to serialize data to\n * transmit.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_callback2(\n  nghttp2_session_callbacks *cbs, nghttp2_send_callback2 send_callback);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  Use `nghttp2_session_callbacks_set_recv_callback2()`\n *   with :type:`nghttp2_recv_callback2` instead.\n *\n * Sets callback function invoked when the a session wants to receive\n * data from the remote peer.  This callback is not necessary if the\n * application uses solely `nghttp2_session_mem_recv()` to process\n * received data.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback(\n  nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @function\n *\n * Sets callback function invoked when the a session wants to receive\n * data from the remote peer.  This callback is not necessary if the\n * application uses solely `nghttp2_session_mem_recv2()` to process\n * received data.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback2(\n  nghttp2_session_callbacks *cbs, nghttp2_recv_callback2 recv_callback);\n\n/**\n * @function\n *\n * Sets callback function invoked by `nghttp2_session_recv()` and\n * `nghttp2_session_mem_recv2()` when a frame is received.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_on_frame_recv_callback on_frame_recv_callback);\n\n/**\n * @function\n *\n * Sets callback function invoked by `nghttp2_session_recv()` and\n * `nghttp2_session_mem_recv2()` when an invalid non-DATA frame is\n * received.\n */\nNGHTTP2_EXTERN void\nnghttp2_session_callbacks_set_on_invalid_frame_recv_callback(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback);\n\n/**\n * @function\n *\n * Sets callback function invoked when a chunk of data in DATA frame\n * is received.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_data_chunk_recv_callback(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback);\n\n/**\n * @function\n *\n * Sets callback function invoked before a non-DATA frame is sent.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_before_frame_send_callback(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_before_frame_send_callback before_frame_send_callback);\n\n/**\n * @function\n *\n * Sets callback function invoked after a frame is sent.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_send_callback(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_on_frame_send_callback on_frame_send_callback);\n\n/**\n * @function\n *\n * Sets callback function invoked when a non-DATA frame is not sent\n * because of an error.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_not_send_callback(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_on_frame_not_send_callback on_frame_not_send_callback);\n\n/**\n * @function\n *\n * Sets callback function invoked when the stream is closed.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_stream_close_callback(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_on_stream_close_callback on_stream_close_callback);\n\n/**\n * @function\n *\n * Sets callback function invoked when the reception of header block\n * in HEADERS or PUSH_PROMISE is started.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_headers_callback(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_on_begin_headers_callback on_begin_headers_callback);\n\n/**\n * @function\n *\n * Sets callback function invoked when a header name/value pair is\n * received.  If both\n * `nghttp2_session_callbacks_set_on_header_callback()` and\n * `nghttp2_session_callbacks_set_on_header_callback2()` are used to\n * set callbacks, the latter has the precedence.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_on_header_callback on_header_callback);\n\n/**\n * @function\n *\n * Sets callback function invoked when a header name/value pair is\n * received.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback2(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_on_header_callback2 on_header_callback2);\n\n/**\n * @function\n *\n * Sets callback function invoked when a invalid header name/value\n * pair is received.  If both\n * `nghttp2_session_callbacks_set_on_invalid_header_callback()` and\n * `nghttp2_session_callbacks_set_on_invalid_header_callback2()` are\n * used to set callbacks, the latter takes the precedence.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_on_invalid_header_callback on_invalid_header_callback);\n\n/**\n * @function\n *\n * Sets callback function invoked when a invalid header name/value\n * pair is received.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback2(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_on_invalid_header_callback2 on_invalid_header_callback2);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  Use\n *   `nghttp2_session_callbacks_set_select_padding_callback2()` with\n *   :type:`nghttp2_select_padding_callback2` instead.\n *\n * Sets callback function invoked when the library asks application\n * how many padding bytes are required for the transmission of the\n * given frame.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_select_padding_callback(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_select_padding_callback select_padding_callback);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @function\n *\n * Sets callback function invoked when the library asks application\n * how many padding bytes are required for the transmission of the\n * given frame.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_select_padding_callback2(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_select_padding_callback2 select_padding_callback);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  Use\n *   `nghttp2_session_callbacks_set_data_source_read_length_callback2()`\n *   with :type:`nghttp2_data_source_read_length_callback2` instead.\n *\n * Sets callback function determine the length allowed in\n * :type:`nghttp2_data_source_read_callback`.\n */\nNGHTTP2_EXTERN void\nnghttp2_session_callbacks_set_data_source_read_length_callback(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_data_source_read_length_callback data_source_read_length_callback);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @function\n *\n * Sets callback function determine the length allowed in\n * :type:`nghttp2_data_source_read_callback2`.\n */\nNGHTTP2_EXTERN void\nnghttp2_session_callbacks_set_data_source_read_length_callback2(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_data_source_read_length_callback2 data_source_read_length_callback);\n\n/**\n * @function\n *\n * Sets callback function invoked when a frame header is received.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_frame_callback(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_on_begin_frame_callback on_begin_frame_callback);\n\n/**\n * @function\n *\n * Sets callback function invoked when\n * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` is used in\n * :type:`nghttp2_data_source_read_callback2` to avoid data copy.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_data_callback(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_send_data_callback send_data_callback);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  Use\n *   `nghttp2_session_callbacks_set_pack_extension_callback2()` with\n *   :type:`nghttp2_pack_extension_callback2` instead.\n *\n * Sets callback function invoked when the library asks the\n * application to pack extension frame payload in wire format.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_pack_extension_callback(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_pack_extension_callback pack_extension_callback);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @function\n *\n * Sets callback function invoked when the library asks the\n * application to pack extension frame payload in wire format.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_pack_extension_callback2(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_pack_extension_callback2 pack_extension_callback);\n\n/**\n * @function\n *\n * Sets callback function invoked when the library asks the\n * application to unpack extension frame payload from wire format.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_unpack_extension_callback(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_unpack_extension_callback unpack_extension_callback);\n\n/**\n * @function\n *\n * Sets callback function invoked when chunk of extension frame\n * payload is received.\n */\nNGHTTP2_EXTERN void\nnghttp2_session_callbacks_set_on_extension_chunk_recv_callback(\n  nghttp2_session_callbacks *cbs,\n  nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback);\n\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  Use\n *   `nghttp2_session_callbacks_set_error_callback2()` with\n *   :type:`nghttp2_error_callback2` instead.\n *\n * Sets callback function invoked when library tells error message to\n * the application.\n *\n * If both :type:`nghttp2_error_callback` and\n * :type:`nghttp2_error_callback2` are set, the latter takes\n * precedence.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback(\n  nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback);\n\n/**\n * @function\n *\n * Sets callback function invoked when library tells error code, and\n * message to the application.\n *\n * If both :type:`nghttp2_error_callback` and\n * :type:`nghttp2_error_callback2` are set, the latter takes\n * precedence.\n */\nNGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback2(\n  nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2);\n\n/**\n * @functypedef\n *\n * Custom memory allocator to replace malloc().  The |mem_user_data|\n * is the mem_user_data member of :type:`nghttp2_mem` structure.\n */\ntypedef void *(*nghttp2_malloc)(size_t size, void *mem_user_data);\n\n/**\n * @functypedef\n *\n * Custom memory allocator to replace free().  The |mem_user_data| is\n * the mem_user_data member of :type:`nghttp2_mem` structure.\n */\ntypedef void (*nghttp2_free)(void *ptr, void *mem_user_data);\n\n/**\n * @functypedef\n *\n * Custom memory allocator to replace calloc().  The |mem_user_data|\n * is the mem_user_data member of :type:`nghttp2_mem` structure.\n */\ntypedef void *(*nghttp2_calloc)(size_t nmemb, size_t size, void *mem_user_data);\n\n/**\n * @functypedef\n *\n * Custom memory allocator to replace realloc().  The |mem_user_data|\n * is the mem_user_data member of :type:`nghttp2_mem` structure.\n */\ntypedef void *(*nghttp2_realloc)(void *ptr, size_t size, void *mem_user_data);\n\n/**\n * @struct\n *\n * Custom memory allocator functions and user defined pointer.  The\n * |mem_user_data| member is passed to each allocator function.  This\n * can be used, for example, to achieve per-session memory pool.\n *\n * In the following example code, ``my_malloc``, ``my_free``,\n * ``my_calloc`` and ``my_realloc`` are the replacement of the\n * standard allocators ``malloc``, ``free``, ``calloc`` and\n * ``realloc`` respectively::\n *\n *     void *my_malloc_cb(size_t size, void *mem_user_data) {\n *       return my_malloc(size);\n *     }\n *\n *     void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); }\n *\n *     void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) {\n *       return my_calloc(nmemb, size);\n *     }\n *\n *     void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) {\n *       return my_realloc(ptr, size);\n *     }\n *\n *     void session_new() {\n *       nghttp2_session *session;\n *       nghttp2_session_callbacks *callbacks;\n *       nghttp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb,\n *                          my_realloc_cb};\n *\n *       ...\n *\n *       nghttp2_session_client_new3(&session, callbacks, NULL, NULL, &mem);\n *\n *       ...\n *     }\n */\ntypedef struct {\n  /**\n   * An arbitrary user supplied data.  This is passed to each\n   * allocator function.\n   */\n  void *mem_user_data;\n  /**\n   * Custom allocator function to replace malloc().\n   */\n  nghttp2_malloc malloc;\n  /**\n   * Custom allocator function to replace free().\n   */\n  nghttp2_free free;\n  /**\n   * Custom allocator function to replace calloc().\n   */\n  nghttp2_calloc calloc;\n  /**\n   * Custom allocator function to replace realloc().\n   */\n  nghttp2_realloc realloc;\n} nghttp2_mem;\n\nstruct nghttp2_option;\n\n/**\n * @struct\n *\n * Configuration options for :type:`nghttp2_session`.  The details of\n * this structure are intentionally hidden from the public API.\n */\ntypedef struct nghttp2_option nghttp2_option;\n\n/**\n * @function\n *\n * Initializes |*option_ptr| with default values.\n *\n * When the application finished using this object, it can use\n * `nghttp2_option_del()` to free its memory.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nNGHTTP2_EXTERN int nghttp2_option_new(nghttp2_option **option_ptr);\n\n/**\n * @function\n *\n * Frees any resources allocated for |option|.  If |option| is\n * ``NULL``, this function does nothing.\n */\nNGHTTP2_EXTERN void nghttp2_option_del(nghttp2_option *option);\n\n/**\n * @function\n *\n * This option prevents the library from sending WINDOW_UPDATE for a\n * connection automatically.  If this option is set to nonzero, the\n * library won't send WINDOW_UPDATE for DATA until application calls\n * `nghttp2_session_consume()` to indicate the consumed amount of\n * data.  Don't use `nghttp2_submit_window_update()` for this purpose.\n * By default, this option is set to zero.\n */\nNGHTTP2_EXTERN void\nnghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val);\n\n/**\n * @function\n *\n * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of\n * remote endpoint as if it is received in SETTINGS frame.  Without\n * specifying this option, the maximum number of outgoing concurrent\n * streams is initially limited to 100 to avoid issues when the local\n * endpoint submits lots of requests before receiving initial SETTINGS\n * frame from the remote endpoint, since sending them at once to the\n * remote endpoint could lead to rejection of some of the requests.\n * This value will be overwritten when the local endpoint receives\n * initial SETTINGS frame from the remote endpoint, either to the\n * value advertised in SETTINGS_MAX_CONCURRENT_STREAMS or to the\n * default value (unlimited) if none was advertised.\n */\nNGHTTP2_EXTERN void\nnghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option,\n                                               uint32_t val);\n\n/**\n * @function\n *\n * By default, nghttp2 library, if configured as server, requires\n * first 24 bytes of client magic byte string (MAGIC).  In most cases,\n * this will simplify the implementation of server.  But sometimes\n * server may want to detect the application protocol based on first\n * few bytes on clear text communication.\n *\n * If this option is used with nonzero |val|, nghttp2 library does not\n * handle MAGIC.  It still checks following SETTINGS frame.  This\n * means that applications should deal with MAGIC by themselves.\n *\n * If this option is not used or used with zero value, if MAGIC does\n * not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()`\n * and `nghttp2_session_mem_recv2()` will return error\n * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal\n * error.\n */\nNGHTTP2_EXTERN void\nnghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val);\n\n/**\n * @function\n *\n * By default, nghttp2 library enforces subset of HTTP Messaging rules\n * described in `HTTP/2 specification, section 8\n * <https://tools.ietf.org/html/rfc7540#section-8>`_.  See\n * :ref:`http-messaging` section for details.  For those applications\n * who use nghttp2 library as non-HTTP use, give nonzero to |val| to\n * disable this enforcement.  Please note that disabling this feature\n * does not change the fundamental client and server model of HTTP.\n * That is, even if the validation is disabled, only client can send\n * requests.\n */\nNGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option,\n                                                         int val);\n\n/**\n * @function\n *\n * RFC 7540 does not enforce any limit on the number of incoming\n * reserved streams (in RFC 7540 terms, streams in reserved (remote)\n * state).  This only affects client side, since only server can push\n * streams.  Malicious server can push arbitrary number of streams,\n * and make client's memory exhausted.  This option can set the\n * maximum number of such incoming streams to avoid possible memory\n * exhaustion.  If this option is set, and pushed streams are\n * automatically closed on reception, without calling user provided\n * callback, if they exceed the given limit.  The default value is\n * 200.  If session is configured as server side, this option has no\n * effect.  Server can control the number of streams to push.\n */\nNGHTTP2_EXTERN void\nnghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option,\n                                               uint32_t val);\n\n/**\n * @function\n *\n * Sets extension frame type the application is willing to handle with\n * user defined callbacks (see\n * :type:`nghttp2_on_extension_chunk_recv_callback` and\n * :type:`nghttp2_unpack_extension_callback`).  The |type| is\n * extension frame type, and must be strictly greater than 0x9.\n * Otherwise, this function does nothing.  The application can call\n * this function multiple times to set more than one frame type to\n * receive.  The application does not have to call this function if it\n * just sends extension frames.\n */\nNGHTTP2_EXTERN void\nnghttp2_option_set_user_recv_extension_type(nghttp2_option *option,\n                                            uint8_t type);\n\n/**\n * @function\n *\n * Sets extension frame type the application is willing to receive\n * using builtin handler.  The |type| is the extension frame type to\n * receive, and must be strictly greater than 0x9.  Otherwise, this\n * function does nothing.  The application can call this function\n * multiple times to set more than one frame type to receive.  The\n * application does not have to call this function if it just sends\n * extension frames.\n *\n * If same frame type is passed to both\n * `nghttp2_option_set_builtin_recv_extension_type()` and\n * `nghttp2_option_set_user_recv_extension_type()`, the latter takes\n * precedence.\n */\nNGHTTP2_EXTERN void\nnghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option,\n                                               uint8_t type);\n\n/**\n * @function\n *\n * This option prevents the library from sending PING frame with ACK\n * flag set automatically when PING frame without ACK flag set is\n * received.  If this option is set to nonzero, the library won't send\n * PING frame with ACK flag set in the response for incoming PING\n * frame.  The application can send PING frame with ACK flag set using\n * `nghttp2_submit_ping()` with :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK`\n * as flags parameter.\n */\nNGHTTP2_EXTERN void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option,\n                                                        int val);\n\n/**\n * @function\n *\n * This option sets the maximum length of header block (a set of\n * header fields per one HEADERS frame) to send.  The length of a\n * given set of header fields is calculated using\n * `nghttp2_hd_deflate_bound()`.  The default value is 64KiB.  If\n * application attempts to send header fields larger than this limit,\n * the transmission of the frame fails with error code\n * :enum:`nghttp2_error.NGHTTP2_ERR_FRAME_SIZE_ERROR`.\n */\nNGHTTP2_EXTERN void\nnghttp2_option_set_max_send_header_block_length(nghttp2_option *option,\n                                                size_t val);\n\n/**\n * @function\n *\n * This option sets the maximum dynamic table size for deflating\n * header fields.  The default value is 4KiB.  In HTTP/2, receiver of\n * deflated header block can specify maximum dynamic table size.  The\n * actual maximum size is the minimum of the size receiver specified\n * and this option value.\n */\nNGHTTP2_EXTERN void\nnghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option,\n                                                  size_t val);\n\n/**\n * @function\n *\n * This option prevents the library from retaining closed streams to\n * maintain the priority tree.  If this option is set to nonzero,\n * applications can discard closed stream completely to save memory.\n *\n * If\n * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`\n * of value of 1 is submitted via `nghttp2_submit_settings()`, any\n * closed streams are not retained regardless of this option.\n */\nNGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option,\n                                                         int val);\n\n/**\n * @function\n *\n * This function sets the maximum number of outgoing SETTINGS ACK and\n * PING ACK frames retained in :type:`nghttp2_session` object.  If\n * more than those frames are retained, the peer is considered to be\n * misbehaving and session will be closed.  The default value is 1000.\n */\nNGHTTP2_EXTERN void nghttp2_option_set_max_outbound_ack(nghttp2_option *option,\n                                                        size_t val);\n\n/**\n * @function\n *\n * This function sets the maximum number of SETTINGS entries per\n * SETTINGS frame that will be accepted. If more than those entries\n * are received, the peer is considered to be misbehaving and session\n * will be closed. The default value is 32.\n */\nNGHTTP2_EXTERN void nghttp2_option_set_max_settings(nghttp2_option *option,\n                                                    size_t val);\n\n/**\n * @function\n *\n * This option, if set to nonzero, allows server to fallback to\n * :rfc:`7540` priorities if SETTINGS_NO_RFC7540_PRIORITIES was not\n * received from client, and server submitted\n * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`\n * = 1 via `nghttp2_submit_settings()`.  Most of the advanced\n * functionality for RFC 7540 priorities are still disabled.  This\n * fallback only enables the minimal feature set of RFC 7540\n * priorities to deal with priority signaling from client.\n *\n * Client session ignores this option.\n */\nNGHTTP2_EXTERN void\nnghttp2_option_set_server_fallback_rfc7540_priorities(nghttp2_option *option,\n                                                      int val);\n\n/**\n * @function\n *\n * This option, if set to nonzero, turns off RFC 9113 leading and\n * trailing white spaces validation against HTTP field value.  Some\n * important fields, such as HTTP/2 pseudo header fields, are\n * validated more strictly and this option does not apply to them.\n */\nNGHTTP2_EXTERN void\nnghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(\n  nghttp2_option *option, int val);\n\n/**\n * @function\n *\n * This function sets the rate limit for the incoming stream reset\n * (RST_STREAM frame).  It is server use only.  It is a token-bucket\n * based rate limiter.  |burst| specifies the number of tokens that is\n * initially available.  The maximum number of tokens is capped to\n * this value.  |rate| specifies the number of tokens that are\n * regenerated per second.  An incoming RST_STREAM consumes one token.\n * If there is no token available, GOAWAY is sent to tear down the\n * connection.  |burst| and |rate| default to 1000 and 33\n * respectively.\n */\nNGHTTP2_EXTERN void\nnghttp2_option_set_stream_reset_rate_limit(nghttp2_option *option,\n                                           uint64_t burst, uint64_t rate);\n\n/**\n * @function\n *\n * This function sets the maximum number of CONTINUATION frames\n * following an incoming HEADER frame.  If more than those frames are\n * received, the remote endpoint is considered to be misbehaving and\n * session will be closed.  The default value is 8.\n */\nNGHTTP2_EXTERN void nghttp2_option_set_max_continuations(nghttp2_option *option,\n                                                         size_t val);\n\n/**\n * @function\n *\n * Initializes |*session_ptr| for client use.  The all members of\n * |callbacks| are copied to |*session_ptr|.  Therefore |*session_ptr|\n * does not store |callbacks|.  The |user_data| is an arbitrary user\n * supplied data, which will be passed to the callback functions.\n *\n * The :type:`nghttp2_send_callback2` must be specified.  If the\n * application code uses `nghttp2_session_recv()`, the\n * :type:`nghttp2_recv_callback` must be specified.  The other members\n * of |callbacks| can be ``NULL``.\n *\n * If this function fails, |*session_ptr| is left untouched.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nNGHTTP2_EXTERN int\nnghttp2_session_client_new(nghttp2_session **session_ptr,\n                           const nghttp2_session_callbacks *callbacks,\n                           void *user_data);\n\n/**\n * @function\n *\n * Initializes |*session_ptr| for server use.  The all members of\n * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr|\n * does not store |callbacks|.  The |user_data| is an arbitrary user\n * supplied data, which will be passed to the callback functions.\n *\n * The :type:`nghttp2_send_callback2` must be specified.  If the\n * application code uses `nghttp2_session_recv()`, the\n * :type:`nghttp2_recv_callback` must be specified.  The other members\n * of |callbacks| can be ``NULL``.\n *\n * If this function fails, |*session_ptr| is left untouched.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nNGHTTP2_EXTERN int\nnghttp2_session_server_new(nghttp2_session **session_ptr,\n                           const nghttp2_session_callbacks *callbacks,\n                           void *user_data);\n\n/**\n * @function\n *\n * Like `nghttp2_session_client_new()`, but with additional options\n * specified in the |option|.\n *\n * The |option| can be ``NULL`` and the call is equivalent to\n * `nghttp2_session_client_new()`.\n *\n * This function does not take ownership |option|.  The application is\n * responsible for freeing |option| if it finishes using the object.\n *\n * The library code does not refer to |option| after this function\n * returns.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nNGHTTP2_EXTERN int\nnghttp2_session_client_new2(nghttp2_session **session_ptr,\n                            const nghttp2_session_callbacks *callbacks,\n                            void *user_data, const nghttp2_option *option);\n\n/**\n * @function\n *\n * Like `nghttp2_session_server_new()`, but with additional options\n * specified in the |option|.\n *\n * The |option| can be ``NULL`` and the call is equivalent to\n * `nghttp2_session_server_new()`.\n *\n * This function does not take ownership |option|.  The application is\n * responsible for freeing |option| if it finishes using the object.\n *\n * The library code does not refer to |option| after this function\n * returns.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nNGHTTP2_EXTERN int\nnghttp2_session_server_new2(nghttp2_session **session_ptr,\n                            const nghttp2_session_callbacks *callbacks,\n                            void *user_data, const nghttp2_option *option);\n\n/**\n * @function\n *\n * Like `nghttp2_session_client_new2()`, but with additional custom\n * memory allocator specified in the |mem|.\n *\n * The |mem| can be ``NULL`` and the call is equivalent to\n * `nghttp2_session_client_new2()`.\n *\n * This function does not take ownership |mem|.  The application is\n * responsible for freeing |mem|.\n *\n * The library code does not refer to |mem| pointer after this\n * function returns, so the application can safely free it.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nNGHTTP2_EXTERN int nghttp2_session_client_new3(\n  nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks,\n  void *user_data, const nghttp2_option *option, nghttp2_mem *mem);\n\n/**\n * @function\n *\n * Like `nghttp2_session_server_new2()`, but with additional custom\n * memory allocator specified in the |mem|.\n *\n * The |mem| can be ``NULL`` and the call is equivalent to\n * `nghttp2_session_server_new2()`.\n *\n * This function does not take ownership |mem|.  The application is\n * responsible for freeing |mem|.\n *\n * The library code does not refer to |mem| pointer after this\n * function returns, so the application can safely free it.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nNGHTTP2_EXTERN int nghttp2_session_server_new3(\n  nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks,\n  void *user_data, const nghttp2_option *option, nghttp2_mem *mem);\n\n/**\n * @function\n *\n * Frees any resources allocated for |session|.  If |session| is\n * ``NULL``, this function does nothing.\n */\nNGHTTP2_EXTERN void nghttp2_session_del(nghttp2_session *session);\n\n/**\n * @function\n *\n * Sends pending frames to the remote peer.\n *\n * This function retrieves the highest prioritized frame from the\n * outbound queue and sends it to the remote peer.  It does this as\n * many times as possible until the user callback\n * :type:`nghttp2_send_callback2` returns\n * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`, the outbound queue\n * becomes empty or flow control is triggered (remote window size\n * becomes depleted or maximum number of concurrent streams is\n * reached).  This function calls several callback functions which are\n * passed when initializing the |session|.  Here is the simple time\n * chart which tells when each callback is invoked:\n *\n * 1. Get the next frame to send from outbound queue.\n *\n * 2. Prepare transmission of the frame.\n *\n * 3. If the control frame cannot be sent because some preconditions\n *    are not met (e.g., request HEADERS cannot be sent after GOAWAY),\n *    :type:`nghttp2_on_frame_not_send_callback` is invoked.  Abort\n *    the following steps.\n *\n * 4. If the frame is HEADERS, PUSH_PROMISE or DATA,\n *    :type:`nghttp2_select_padding_callback` is invoked.\n *\n * 5. If the frame is request HEADERS, the stream is opened here.\n *\n * 6. :type:`nghttp2_before_frame_send_callback` is invoked.\n *\n * 7. If :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL` is returned from\n *    :type:`nghttp2_before_frame_send_callback`, the current frame\n *    transmission is canceled, and\n *    :type:`nghttp2_on_frame_not_send_callback` is invoked.  Abort\n *    the following steps.\n *\n * 8. :type:`nghttp2_send_callback2` is invoked one or more times to\n *    send the frame.\n *\n * 9. :type:`nghttp2_on_frame_send_callback` is invoked.\n *\n * 10. If the transmission of the frame triggers closure of the\n *     stream, the stream is closed and\n *     :type:`nghttp2_on_stream_close_callback` is invoked.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`\n *     The callback function failed.\n */\nNGHTTP2_EXTERN int nghttp2_session_send(nghttp2_session *session);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  Use `nghttp2_session_mem_send2()` instead.\n *\n * Returns the serialized data to send.\n *\n * This function behaves like `nghttp2_session_send()` except that it\n * does not use :type:`nghttp2_send_callback` to transmit data.\n * Instead, it assigns the pointer to the serialized data to the\n * |*data_ptr| and returns its length.  The other callbacks are called\n * in the same way as they are in `nghttp2_session_send()`.\n *\n * If no data is available to send, this function returns 0.\n *\n * This function may not return all serialized data in one invocation.\n * To get all data, call this function repeatedly until it returns 0\n * or one of negative error codes.\n *\n * The assigned |*data_ptr| is valid until the next call of\n * `nghttp2_session_mem_send()` or `nghttp2_session_send()`.\n *\n * The caller must send all data before sending the next chunk of\n * data.\n *\n * This function returns the length of the data pointed by the\n * |*data_ptr| if it succeeds, or one of the following negative error\n * codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n *\n * .. note::\n *\n *   This function may produce very small byte string.  If that is the\n *   case, and application disables Nagle algorithm (``TCP_NODELAY``),\n *   then writing this small chunk leads to very small packet, and it\n *   is very inefficient.  An application should be responsible to\n *   buffer up small chunks of data as necessary to avoid this\n *   situation.\n */\nNGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session,\n                                                const uint8_t **data_ptr);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @function\n *\n * Returns the serialized data to send.\n *\n * This function behaves like `nghttp2_session_send()` except that it\n * does not use :type:`nghttp2_send_callback2` to transmit data.\n * Instead, it assigns the pointer to the serialized data to the\n * |*data_ptr| and returns its length.  The other callbacks are called\n * in the same way as they are in `nghttp2_session_send()`.\n *\n * If no data is available to send, this function returns 0.\n *\n * This function may not return all serialized data in one invocation.\n * To get all data, call this function repeatedly until it returns 0\n * or one of negative error codes.\n *\n * The assigned |*data_ptr| is valid until the next call of\n * `nghttp2_session_mem_send2()` or `nghttp2_session_send()`.\n *\n * The caller must send all data before sending the next chunk of\n * data.\n *\n * This function returns the length of the data pointed by the\n * |*data_ptr| if it succeeds, or one of the following negative error\n * codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n *\n * .. note::\n *\n *   This function may produce very small byte string.  If that is the\n *   case, and application disables Nagle algorithm (``TCP_NODELAY``),\n *   then writing this small chunk leads to very small packet, and it\n *   is very inefficient.  An application should be responsible to\n *   buffer up small chunks of data as necessary to avoid this\n *   situation.\n */\nNGHTTP2_EXTERN nghttp2_ssize\nnghttp2_session_mem_send2(nghttp2_session *session, const uint8_t **data_ptr);\n\n/**\n * @function\n *\n * Receives frames from the remote peer.\n *\n * This function receives as many frames as possible until the user\n * callback :type:`nghttp2_recv_callback` returns\n * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`.  This function calls\n * several callback functions which are passed when initializing the\n * |session|.  Here is the simple time chart which tells when each\n * callback is invoked:\n *\n * 1. :type:`nghttp2_recv_callback` is invoked one or more times to\n *    receive frame header.\n *\n * 2. When frame header is received,\n *    :type:`nghttp2_on_begin_frame_callback` is invoked.\n *\n * 3. If the frame is DATA frame:\n *\n *    1. :type:`nghttp2_recv_callback` is invoked to receive DATA\n *       payload. For each chunk of data,\n *       :type:`nghttp2_on_data_chunk_recv_callback` is invoked.\n *\n *    2. If one DATA frame is completely received,\n *       :type:`nghttp2_on_frame_recv_callback` is invoked.  If the\n *       reception of the frame triggers the closure of the stream,\n *       :type:`nghttp2_on_stream_close_callback` is invoked.\n *\n * 4. If the frame is the control frame:\n *\n *    1. :type:`nghttp2_recv_callback` is invoked one or more times to\n *       receive whole frame.\n *\n *    2. If the received frame is valid, then following actions are\n *       taken.  If the frame is either HEADERS or PUSH_PROMISE,\n *       :type:`nghttp2_on_begin_headers_callback` is invoked.  Then\n *       :type:`nghttp2_on_header_callback` is invoked for each header\n *       name/value pair.  For invalid header field,\n *       :type:`nghttp2_on_invalid_header_callback` is called.  After\n *       all name/value pairs are emitted successfully,\n *       :type:`nghttp2_on_frame_recv_callback` is invoked.  For other\n *       frames, :type:`nghttp2_on_frame_recv_callback` is invoked.\n *       If the reception of the frame triggers the closure of the\n *       stream, :type:`nghttp2_on_stream_close_callback` is invoked.\n *\n *    3. If the received frame is unpacked but is interpreted as\n *       invalid, :type:`nghttp2_on_invalid_frame_recv_callback` is\n *       invoked.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_EOF`\n *     The remote peer did shutdown on the connection.\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`\n *     The callback function failed.\n * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC`\n *     Invalid client magic was detected.  This error only returns\n *     when |session| was configured as server and\n *     `nghttp2_option_set_no_recv_client_magic()` is not used with\n *     nonzero value.\n * :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED`\n *     Flooding was detected in this HTTP/2 session, and it must be\n *     closed.  This is most likely caused by misbehaviour of peer.\n */\nNGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  Use `nghttp2_session_mem_recv2()` instead.\n *\n * Processes data |in| as an input from the remote endpoint.  The\n * |inlen| indicates the number of bytes to receive in the |in|.\n *\n * This function behaves like `nghttp2_session_recv()` except that it\n * does not use :type:`nghttp2_recv_callback` to receive data; the\n * |in| is the only data for the invocation of this function.  If all\n * bytes are processed, this function returns.  The other callbacks\n * are called in the same way as they are in `nghttp2_session_recv()`.\n *\n * In the current implementation, this function always tries to\n * processes |inlen| bytes of input data unless either an error occurs or\n * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is returned from\n * :type:`nghttp2_on_header_callback` or\n * :type:`nghttp2_on_data_chunk_recv_callback`.  If\n * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is used, the return value\n * includes the number of bytes which was used to produce the data or\n * frame for the callback.\n *\n * This function returns the number of processed bytes, or one of the\n * following negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`\n *     The callback function failed.\n * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC`\n *     Invalid client magic was detected.  This error only returns\n *     when |session| was configured as server and\n *     `nghttp2_option_set_no_recv_client_magic()` is not used with\n *     nonzero value.\n * :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED`\n *     Flooding was detected in this HTTP/2 session, and it must be\n *     closed.  This is most likely caused by misbehaviour of peer.\n */\nNGHTTP2_EXTERN ssize_t nghttp2_session_mem_recv(nghttp2_session *session,\n                                                const uint8_t *in,\n                                                size_t inlen);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @function\n *\n * Processes data |in| as an input from the remote endpoint.  The\n * |inlen| indicates the number of bytes to receive in the |in|.\n *\n * This function behaves like `nghttp2_session_recv()` except that it\n * does not use :type:`nghttp2_recv_callback` to receive data; the\n * |in| is the only data for the invocation of this function.  If all\n * bytes are processed, this function returns.  The other callbacks\n * are called in the same way as they are in `nghttp2_session_recv()`.\n *\n * In the current implementation, this function always tries to\n * processes |inlen| bytes of input data unless either an error occurs or\n * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is returned from\n * :type:`nghttp2_on_header_callback` or\n * :type:`nghttp2_on_data_chunk_recv_callback`.  If\n * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is used, the return value\n * includes the number of bytes which was used to produce the data or\n * frame for the callback.\n *\n * This function returns the number of processed bytes, or one of the\n * following negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`\n *     The callback function failed.\n * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC`\n *     Invalid client magic was detected.  This error only returns\n *     when |session| was configured as server and\n *     `nghttp2_option_set_no_recv_client_magic()` is not used with\n *     nonzero value.\n * :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED`\n *     Flooding was detected in this HTTP/2 session, and it must be\n *     closed.  This is most likely caused by misbehaviour of peer.\n */\nNGHTTP2_EXTERN nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session,\n                                                       const uint8_t *in,\n                                                       size_t inlen);\n\n/**\n * @function\n *\n * Puts back previously deferred DATA frame in the stream |stream_id|\n * to the outbound queue.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The stream does not exist; or no deferred data exist.\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nNGHTTP2_EXTERN int nghttp2_session_resume_data(nghttp2_session *session,\n                                               int32_t stream_id);\n\n/**\n * @function\n *\n * Returns nonzero value if |session| wants to receive data from the\n * remote peer.\n *\n * If both `nghttp2_session_want_read()` and\n * `nghttp2_session_want_write()` return 0, the application should\n * drop the connection.\n */\nNGHTTP2_EXTERN int nghttp2_session_want_read(nghttp2_session *session);\n\n/**\n * @function\n *\n * Returns nonzero value if |session| wants to send data to the remote\n * peer.\n *\n * If both `nghttp2_session_want_read()` and\n * `nghttp2_session_want_write()` return 0, the application should\n * drop the connection.\n */\nNGHTTP2_EXTERN int nghttp2_session_want_write(nghttp2_session *session);\n\n/**\n * @function\n *\n * Returns stream_user_data for the stream |stream_id|.  The\n * stream_user_data is provided by `nghttp2_submit_request2()`,\n * `nghttp2_submit_headers()` or\n * `nghttp2_session_set_stream_user_data()`.  Unless it is set using\n * `nghttp2_session_set_stream_user_data()`, if the stream is\n * initiated by the remote endpoint, stream_user_data is always\n * ``NULL``.  If the stream does not exist, this function returns\n * ``NULL``.\n */\nNGHTTP2_EXTERN void *\nnghttp2_session_get_stream_user_data(nghttp2_session *session,\n                                     int32_t stream_id);\n\n/**\n * @function\n *\n * Sets the |stream_user_data| to the stream denoted by the\n * |stream_id|.  If a stream user data is already set to the stream,\n * it is replaced with the |stream_user_data|.  It is valid to specify\n * ``NULL`` in the |stream_user_data|, which nullifies the associated\n * data pointer.\n *\n * It is valid to set the |stream_user_data| to the stream reserved by\n * PUSH_PROMISE frame.\n *\n * This function returns 0 if it succeeds, or one of following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The stream does not exist\n */\nNGHTTP2_EXTERN int\nnghttp2_session_set_stream_user_data(nghttp2_session *session,\n                                     int32_t stream_id, void *stream_user_data);\n\n/**\n * @function\n *\n * Sets |user_data| to |session|, overwriting the existing user data\n * specified in `nghttp2_session_client_new()`, or\n * `nghttp2_session_server_new()`.\n */\nNGHTTP2_EXTERN void nghttp2_session_set_user_data(nghttp2_session *session,\n                                                  void *user_data);\n\n/**\n * @function\n *\n * Returns the number of frames in the outbound queue.  This does not\n * include the deferred DATA frames.\n */\nNGHTTP2_EXTERN size_t\nnghttp2_session_get_outbound_queue_size(nghttp2_session *session);\n\n/**\n * @function\n *\n * Returns the number of DATA payload in bytes received without\n * WINDOW_UPDATE transmission for the stream |stream_id|.  The local\n * (receive) window size can be adjusted by\n * `nghttp2_submit_window_update()`.  This function takes into account\n * that and returns effective data length.  In particular, if the\n * local window size is reduced by submitting negative\n * window_size_increment with `nghttp2_submit_window_update()`, this\n * function returns the number of bytes less than actually received.\n *\n * This function returns -1 if it fails.\n */\nNGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_recv_data_length(\n  nghttp2_session *session, int32_t stream_id);\n\n/**\n * @function\n *\n * Returns the local (receive) window size for the stream |stream_id|.\n * The local window size can be adjusted by\n * `nghttp2_submit_window_update()`.  This function takes into account\n * that and returns effective window size.\n *\n * This function does not take into account the amount of received\n * data from the remote endpoint.  Use\n * `nghttp2_session_get_stream_local_window_size()` to know the amount\n * of data the remote endpoint can send without receiving stream level\n * WINDOW_UPDATE frame.  Note that each stream is still subject to the\n * connection level flow control.\n *\n * This function returns -1 if it fails.\n */\nNGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_local_window_size(\n  nghttp2_session *session, int32_t stream_id);\n\n/**\n * @function\n *\n * Returns the amount of flow-controlled payload (e.g., DATA) that the\n * remote endpoint can send without receiving stream level\n * WINDOW_UPDATE frame.  It is also subject to the connection level\n * flow control.  So the actual amount of data to send is\n * min(`nghttp2_session_get_stream_local_window_size()`,\n * `nghttp2_session_get_local_window_size()`).\n *\n * This function returns -1 if it fails.\n */\nNGHTTP2_EXTERN int32_t nghttp2_session_get_stream_local_window_size(\n  nghttp2_session *session, int32_t stream_id);\n\n/**\n * @function\n *\n * Returns the number of DATA payload in bytes received without\n * WINDOW_UPDATE transmission for a connection.  The local (receive)\n * window size can be adjusted by `nghttp2_submit_window_update()`.\n * This function takes into account that and returns effective data\n * length.  In particular, if the local window size is reduced by\n * submitting negative window_size_increment with\n * `nghttp2_submit_window_update()`, this function returns the number\n * of bytes less than actually received.\n *\n * This function returns -1 if it fails.\n */\nNGHTTP2_EXTERN int32_t\nnghttp2_session_get_effective_recv_data_length(nghttp2_session *session);\n\n/**\n * @function\n *\n * Returns the local (receive) window size for a connection.  The\n * local window size can be adjusted by\n * `nghttp2_submit_window_update()`.  This function takes into account\n * that and returns effective window size.\n *\n * This function does not take into account the amount of received\n * data from the remote endpoint.  Use\n * `nghttp2_session_get_local_window_size()` to know the amount of\n * data the remote endpoint can send without receiving\n * connection-level WINDOW_UPDATE frame.  Note that each stream is\n * still subject to the stream level flow control.\n *\n * This function returns -1 if it fails.\n */\nNGHTTP2_EXTERN int32_t\nnghttp2_session_get_effective_local_window_size(nghttp2_session *session);\n\n/**\n * @function\n *\n * Returns the amount of flow-controlled payload (e.g., DATA) that the\n * remote endpoint can send without receiving connection level\n * WINDOW_UPDATE frame.  Note that each stream is still subject to the\n * stream level flow control (see\n * `nghttp2_session_get_stream_local_window_size()`).\n *\n * This function returns -1 if it fails.\n */\nNGHTTP2_EXTERN int32_t\nnghttp2_session_get_local_window_size(nghttp2_session *session);\n\n/**\n * @function\n *\n * Returns the remote window size for a given stream |stream_id|.\n *\n * This is the amount of flow-controlled payload (e.g., DATA) that the\n * local endpoint can send without stream level WINDOW_UPDATE.  There\n * is also connection level flow control, so the effective size of\n * payload that the local endpoint can actually send is\n * min(`nghttp2_session_get_stream_remote_window_size()`,\n * `nghttp2_session_get_remote_window_size()`).\n *\n * This function returns -1 if it fails.\n */\nNGHTTP2_EXTERN int32_t nghttp2_session_get_stream_remote_window_size(\n  nghttp2_session *session, int32_t stream_id);\n\n/**\n * @function\n *\n * Returns the remote window size for a connection.\n *\n * This function always succeeds.\n */\nNGHTTP2_EXTERN int32_t\nnghttp2_session_get_remote_window_size(nghttp2_session *session);\n\n/**\n * @function\n *\n * Returns 1 if local peer half closed the given stream |stream_id|.\n * Returns 0 if it did not.  Returns -1 if no such stream exists.\n */\nNGHTTP2_EXTERN int\nnghttp2_session_get_stream_local_close(nghttp2_session *session,\n                                       int32_t stream_id);\n\n/**\n * @function\n *\n * Returns 1 if remote peer half closed the given stream |stream_id|.\n * Returns 0 if it did not.  Returns -1 if no such stream exists.\n */\nNGHTTP2_EXTERN int\nnghttp2_session_get_stream_remote_close(nghttp2_session *session,\n                                        int32_t stream_id);\n\n/**\n * @function\n *\n * Returns the current dynamic table size of HPACK inflater, including\n * the overhead 32 bytes per entry described in RFC 7541.\n */\nNGHTTP2_EXTERN size_t\nnghttp2_session_get_hd_inflate_dynamic_table_size(nghttp2_session *session);\n\n/**\n * @function\n *\n * Returns the current dynamic table size of HPACK deflater including\n * the overhead 32 bytes per entry described in RFC 7541.\n */\nNGHTTP2_EXTERN size_t\nnghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session);\n\n/**\n * @function\n *\n * Signals the session so that the connection should be terminated.\n *\n * The last stream ID is the minimum value between the stream ID of a\n * stream for which :type:`nghttp2_on_frame_recv_callback` was called\n * most recently and the last stream ID we have sent to the peer\n * previously.\n *\n * The |error_code| is the error code of this GOAWAY frame.  The\n * pre-defined error code is one of :enum:`nghttp2_error_code`.\n *\n * After the transmission, both `nghttp2_session_want_read()` and\n * `nghttp2_session_want_write()` return 0.\n *\n * This function should be called when the connection should be\n * terminated after sending GOAWAY.  If the remaining streams should\n * be processed after GOAWAY, use `nghttp2_submit_goaway()` instead.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nNGHTTP2_EXTERN int nghttp2_session_terminate_session(nghttp2_session *session,\n                                                     uint32_t error_code);\n\n/**\n * @function\n *\n * Signals the session so that the connection should be terminated.\n *\n * This function behaves like `nghttp2_session_terminate_session()`,\n * but the last stream ID can be specified by the application for fine\n * grained control of stream.  The HTTP/2 specification does not allow\n * last_stream_id to be increased.  So the actual value sent as\n * last_stream_id is the minimum value between the given\n * |last_stream_id| and the last_stream_id we have previously sent to\n * the peer.\n *\n * The |last_stream_id| is peer's stream ID or 0.  So if |session| is\n * initialized as client, |last_stream_id| must be even or 0.  If\n * |session| is initialized as server, |last_stream_id| must be odd or\n * 0.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |last_stream_id| is invalid.\n */\nNGHTTP2_EXTERN int nghttp2_session_terminate_session2(nghttp2_session *session,\n                                                      int32_t last_stream_id,\n                                                      uint32_t error_code);\n\n/**\n * @function\n *\n * Signals to the client that the server started graceful shutdown\n * procedure.\n *\n * This function is only usable for server.  If this function is\n * called with client side session, this function returns\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`.\n *\n * To gracefully shutdown HTTP/2 session, server should call this\n * function to send GOAWAY with last_stream_id (1u << 31) - 1.  And\n * after some delay (e.g., 1 RTT), send another GOAWAY with the stream\n * ID that the server has some processing using\n * `nghttp2_submit_goaway()`.  See also\n * `nghttp2_session_get_last_proc_stream_id()`.\n *\n * Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY\n * and does nothing more.  This is a mere indication to the client\n * that session shutdown is imminent.  The application should call\n * `nghttp2_submit_goaway()` with appropriate last_stream_id after\n * this call.\n *\n * If one or more GOAWAY frame have been already sent by either\n * `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`,\n * this function has no effect.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`\n *     The |session| is initialized as client.\n */\nNGHTTP2_EXTERN int nghttp2_submit_shutdown_notice(nghttp2_session *session);\n\n/**\n * @function\n *\n * Returns the value of SETTINGS |id| notified by a remote endpoint.\n * The |id| must be one of values defined in\n * :enum:`nghttp2_settings_id`.\n */\nNGHTTP2_EXTERN uint32_t nghttp2_session_get_remote_settings(\n  nghttp2_session *session, nghttp2_settings_id id);\n\n/**\n * @function\n *\n * Returns the value of SETTINGS |id| of local endpoint acknowledged\n * by the remote endpoint.  The |id| must be one of the values defined\n * in :enum:`nghttp2_settings_id`.\n */\nNGHTTP2_EXTERN uint32_t nghttp2_session_get_local_settings(\n  nghttp2_session *session, nghttp2_settings_id id);\n\n/**\n * @function\n *\n * Tells the |session| that next stream ID is |next_stream_id|.  The\n * |next_stream_id| must be equal or greater than the value returned\n * by `nghttp2_session_get_next_stream_id()`.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |next_stream_id| is strictly less than the value\n *     `nghttp2_session_get_next_stream_id()` returns; or\n *     |next_stream_id| is invalid (e.g., even integer for client, or\n *     odd integer for server).\n */\nNGHTTP2_EXTERN int nghttp2_session_set_next_stream_id(nghttp2_session *session,\n                                                      int32_t next_stream_id);\n\n/**\n * @function\n *\n * Returns the next outgoing stream ID.  Notice that return type is\n * uint32_t.  If we run out of stream ID for this session, this\n * function returns 1 << 31.\n */\nNGHTTP2_EXTERN uint32_t\nnghttp2_session_get_next_stream_id(nghttp2_session *session);\n\n/**\n * @function\n *\n * Tells the |session| that |size| bytes for a stream denoted by\n * |stream_id| were consumed by application and are ready to\n * WINDOW_UPDATE.  The consumed bytes are counted towards both\n * connection and stream level WINDOW_UPDATE (see\n * `nghttp2_session_consume_connection()` and\n * `nghttp2_session_consume_stream()` to update consumption\n * independently).  This function is intended to be used without\n * automatic window update (see\n * `nghttp2_option_set_no_auto_window_update()`).\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |stream_id| is 0.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`\n *     Automatic WINDOW_UPDATE is not disabled.\n */\nNGHTTP2_EXTERN int nghttp2_session_consume(nghttp2_session *session,\n                                           int32_t stream_id, size_t size);\n\n/**\n * @function\n *\n * Like `nghttp2_session_consume()`, but this only tells library that\n * |size| bytes were consumed only for connection level.  Note that\n * HTTP/2 maintains connection and stream level flow control windows\n * independently.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`\n *     Automatic WINDOW_UPDATE is not disabled.\n */\nNGHTTP2_EXTERN int nghttp2_session_consume_connection(nghttp2_session *session,\n                                                      size_t size);\n\n/**\n * @function\n *\n * Like `nghttp2_session_consume()`, but this only tells library that\n * |size| bytes were consumed only for stream denoted by |stream_id|.\n * Note that HTTP/2 maintains connection and stream level flow control\n * windows independently.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |stream_id| is 0.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`\n *     Automatic WINDOW_UPDATE is not disabled.\n */\nNGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session,\n                                                  int32_t stream_id,\n                                                  size_t size);\n\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.  In the future release after the end of\n *   2024, this function will always return 0 without doing anything.\n *\n * Changes priority of existing stream denoted by |stream_id|.  The\n * new priority specification is |pri_spec|.\n *\n * The priority is changed silently and instantly, and no PRIORITY\n * frame will be sent to notify the peer of this change.  This\n * function may be useful for server to change the priority of pushed\n * stream.\n *\n * If |session| is initialized as server, and ``pri_spec->stream_id``\n * points to the idle stream, the idle stream is created if it does\n * not exist.  The created idle stream will depend on root stream\n * (stream 0) with weight 16.\n *\n * Otherwise, if stream denoted by ``pri_spec->stream_id`` is not\n * found, we use default priority instead of given |pri_spec|.  That\n * is make stream depend on root stream with weight 16.\n *\n * If\n * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`\n * of value of 1 is submitted via `nghttp2_submit_settings()`, this\n * function does nothing and returns 0.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     Attempted to depend on itself; or no stream exist for the given\n *     |stream_id|; or |stream_id| is 0\n */\nNGHTTP2_EXTERN int\nnghttp2_session_change_stream_priority(nghttp2_session *session,\n                                       int32_t stream_id,\n                                       const nghttp2_priority_spec *pri_spec);\n\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.  In the future release after the end of\n *   2024, this function will always return 0 without doing anything.\n *\n * Creates idle stream with the given |stream_id|, and priority\n * |pri_spec|.\n *\n * The stream creation is done without sending PRIORITY frame, which\n * means that peer does not know about the existence of this idle\n * stream in the local endpoint.\n *\n * RFC 7540 does not disallow the use of creation of idle stream with\n * odd or even stream ID regardless of client or server.  So this\n * function can create odd or even stream ID regardless of client or\n * server.  But probably it is a bit safer to use the stream ID the\n * local endpoint can initiate (in other words, use odd stream ID for\n * client, and even stream ID for server), to avoid potential\n * collision from peer's instruction.  Also we can use\n * `nghttp2_session_set_next_stream_id()` to avoid to open created\n * idle streams accidentally if we follow this recommendation.\n *\n * If |session| is initialized as server, and ``pri_spec->stream_id``\n * points to the idle stream, the idle stream is created if it does\n * not exist.  The created idle stream will depend on root stream\n * (stream 0) with weight 16.\n *\n * Otherwise, if stream denoted by ``pri_spec->stream_id`` is not\n * found, we use default priority instead of given |pri_spec|.  That\n * is make stream depend on root stream with weight 16.\n *\n * If\n * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`\n * of value of 1 is submitted via `nghttp2_submit_settings()`, this\n * function does nothing and returns 0.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     Attempted to depend on itself; or stream denoted by |stream_id|\n *     already exists; or |stream_id| cannot be used to create idle\n *     stream (in other words, local endpoint has already opened\n *     stream ID greater than or equal to the given stream ID; or\n *     |stream_id| is 0\n */\nNGHTTP2_EXTERN int\nnghttp2_session_create_idle_stream(nghttp2_session *session, int32_t stream_id,\n                                   const nghttp2_priority_spec *pri_spec);\n\n/**\n * @function\n *\n * .. warning::\n *\n *   This function is deprecated in favor of\n *   `nghttp2_session_upgrade2()`, because this function lacks the\n *   parameter to tell the library the request method used in the\n *   original HTTP request.  This information is required for client\n *   to validate actual response body length against content-length\n *   header field (see `nghttp2_option_set_no_http_messaging()`).  If\n *   HEAD is used in request, the length of response body must be 0\n *   regardless of value included in content-length header field.\n *\n * Performs post-process of HTTP Upgrade request.  This function can\n * be called from both client and server, but the behavior is very\n * different in each other.\n *\n * If called from client side, the |settings_payload| must be the\n * value sent in ``HTTP2-Settings`` header field and must be decoded\n * by base64url decoder.  The |settings_payloadlen| is the length of\n * |settings_payload|.  The |settings_payload| is unpacked and its\n * setting values will be submitted using `nghttp2_submit_settings()`.\n * This means that the client application code does not need to submit\n * SETTINGS by itself.  The stream with stream ID=1 is opened and the\n * |stream_user_data| is used for its stream_user_data.  The opened\n * stream becomes half-closed (local) state.\n *\n * If called from server side, the |settings_payload| must be the\n * value received in ``HTTP2-Settings`` header field and must be\n * decoded by base64url decoder.  The |settings_payloadlen| is the\n * length of |settings_payload|.  It is treated as if the SETTINGS\n * frame with that payload is received.  Thus, callback functions for\n * the reception of SETTINGS frame will be invoked.  The stream with\n * stream ID=1 is opened.  The |stream_user_data| is ignored.  The\n * opened stream becomes half-closed (remote).\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |settings_payload| is badly formed.\n * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO`\n *     The stream ID 1 is already used or closed; or is not available.\n */\nNGHTTP2_EXTERN int nghttp2_session_upgrade(nghttp2_session *session,\n                                           const uint8_t *settings_payload,\n                                           size_t settings_payloadlen,\n                                           void *stream_user_data);\n\n/**\n * @function\n *\n * Performs post-process of HTTP Upgrade request.  This function can\n * be called from both client and server, but the behavior is very\n * different in each other.\n *\n * If called from client side, the |settings_payload| must be the\n * value sent in ``HTTP2-Settings`` header field and must be decoded\n * by base64url decoder.  The |settings_payloadlen| is the length of\n * |settings_payload|.  The |settings_payload| is unpacked and its\n * setting values will be submitted using `nghttp2_submit_settings()`.\n * This means that the client application code does not need to submit\n * SETTINGS by itself.  The stream with stream ID=1 is opened and the\n * |stream_user_data| is used for its stream_user_data.  The opened\n * stream becomes half-closed (local) state.\n *\n * If called from server side, the |settings_payload| must be the\n * value received in ``HTTP2-Settings`` header field and must be\n * decoded by base64url decoder.  The |settings_payloadlen| is the\n * length of |settings_payload|.  It is treated as if the SETTINGS\n * frame with that payload is received.  Thus, callback functions for\n * the reception of SETTINGS frame will be invoked.  The stream with\n * stream ID=1 is opened.  The |stream_user_data| is ignored.  The\n * opened stream becomes half-closed (remote).\n *\n * If the request method is HEAD, pass nonzero value to\n * |head_request|.  Otherwise, pass 0.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |settings_payload| is badly formed.\n * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO`\n *     The stream ID 1 is already used or closed; or is not available.\n */\nNGHTTP2_EXTERN int nghttp2_session_upgrade2(nghttp2_session *session,\n                                            const uint8_t *settings_payload,\n                                            size_t settings_payloadlen,\n                                            int head_request,\n                                            void *stream_user_data);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  Use `nghttp2_pack_settings_payload2()` instead.\n *\n * Serializes the SETTINGS values |iv| in the |buf|.  The size of the\n * |buf| is specified by |buflen|.  The number of entries in the |iv|\n * array is given by |niv|.  The required space in |buf| for the |niv|\n * entries is ``6*niv`` bytes and if the given buffer is too small, an\n * error is returned.  This function is used mainly for creating a\n * SETTINGS payload to be sent with the ``HTTP2-Settings`` header\n * field in an HTTP Upgrade request.  The data written in |buf| is NOT\n * base64url encoded and the application is responsible for encoding.\n *\n * This function returns the number of bytes written in |buf|, or one\n * of the following negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |iv| contains duplicate settings ID or invalid value.\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`\n *     The provided |buflen| size is too small to hold the output.\n */\nNGHTTP2_EXTERN ssize_t nghttp2_pack_settings_payload(\n  uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @function\n *\n * Serializes the SETTINGS values |iv| in the |buf|.  The size of the\n * |buf| is specified by |buflen|.  The number of entries in the |iv|\n * array is given by |niv|.  The required space in |buf| for the |niv|\n * entries is ``6*niv`` bytes and if the given buffer is too small, an\n * error is returned.  This function is used mainly for creating a\n * SETTINGS payload to be sent with the ``HTTP2-Settings`` header\n * field in an HTTP Upgrade request.  The data written in |buf| is NOT\n * base64url encoded and the application is responsible for encoding.\n *\n * This function returns the number of bytes written in |buf|, or one\n * of the following negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |iv| contains duplicate settings ID or invalid value.\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`\n *     The provided |buflen| size is too small to hold the output.\n */\nNGHTTP2_EXTERN nghttp2_ssize nghttp2_pack_settings_payload2(\n  uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv);\n\n/**\n * @function\n *\n * Returns string describing the |lib_error_code|.  The\n * |lib_error_code| must be one of the :enum:`nghttp2_error`.\n */\nNGHTTP2_EXTERN const char *nghttp2_strerror(int lib_error_code);\n\n/**\n * @function\n *\n * Returns string representation of HTTP/2 error code |error_code|\n * (e.g., ``PROTOCOL_ERROR`` is returned if ``error_code ==\n * NGHTTP2_PROTOCOL_ERROR``).  If string representation is unknown for\n * given |error_code|, this function returns string ``unknown``.\n */\nNGHTTP2_EXTERN const char *nghttp2_http2_strerror(uint32_t error_code);\n\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.\n *\n * Initializes |pri_spec| with the |stream_id| of the stream to depend\n * on with |weight| and its exclusive flag.  If |exclusive| is\n * nonzero, exclusive flag is set.\n *\n * The |weight| must be in [:macro:`NGHTTP2_MIN_WEIGHT`,\n * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive.\n */\nNGHTTP2_EXTERN void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec,\n                                               int32_t stream_id,\n                                               int32_t weight, int exclusive);\n\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.\n *\n * Initializes |pri_spec| with the default values.  The default values\n * are: stream_id = 0, weight = :macro:`NGHTTP2_DEFAULT_WEIGHT` and\n * exclusive = 0.\n */\nNGHTTP2_EXTERN void\nnghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec);\n\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.\n *\n * Returns nonzero if the |pri_spec| is filled with default values.\n */\nNGHTTP2_EXTERN int\nnghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  Use `nghttp2_submit_request2()` instead.\n *\n * Submits HEADERS frame and optionally one or more DATA frames.\n *\n * The |pri_spec| is a deprecated priority specification of this\n * request.  ``NULL`` means the default priority (see\n * `nghttp2_priority_spec_default_init()`).  To specify the priority,\n * use `nghttp2_priority_spec_init()`.  If |pri_spec| is not ``NULL``,\n * this function will copy its data members.\n *\n * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`,\n * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive.  If ``pri_spec->weight``\n * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes\n * :macro:`NGHTTP2_MIN_WEIGHT`.  If it is strictly greater than\n * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes\n * :macro:`NGHTTP2_MAX_WEIGHT`.\n *\n * If\n * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`\n * of value of 1 is received by a remote endpoint, |pri_spec| is\n * ignored, and treated as if ``NULL`` is specified.\n *\n * The |nva| is an array of name/value pair :type:`nghttp2_nv` with\n * |nvlen| elements.  The application is responsible to include\n * required pseudo-header fields (header field whose name starts with\n * \":\") in |nva| and must place pseudo-headers before regular header\n * fields.\n *\n * This function creates copies of all name/value pairs in |nva|.  It\n * also lower-cases all names in |nva|.  The order of elements in\n * |nva| is preserved.  For header fields with\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set,\n * header field name and value are not copied respectively.  With\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application\n * is responsible to pass header field name in lowercase.  The\n * application should maintain the references to them until\n * :type:`nghttp2_on_frame_send_callback` or\n * :type:`nghttp2_on_frame_not_send_callback` is called.\n *\n * HTTP/2 specification has requirement about header fields in the\n * request HEADERS.  See the specification for more details.\n *\n * If |data_prd| is not ``NULL``, it provides data which will be sent\n * in subsequent DATA frames.  In this case, a method that allows\n * request message bodies\n * (https://tools.ietf.org/html/rfc7231#section-4) must be specified\n * with ``:method`` key in |nva| (e.g. ``POST``).  This function does\n * not take ownership of the |data_prd|.  The function copies the\n * members of the |data_prd|.  If |data_prd| is ``NULL``, HEADERS have\n * END_STREAM set.  The |stream_user_data| is data associated to the\n * stream opened by this request and can be an arbitrary pointer,\n * which can be retrieved later by\n * `nghttp2_session_get_stream_user_data()`.\n *\n * This function returns assigned stream ID if it succeeds, or one of\n * the following negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE`\n *     No stream ID is available because maximum stream ID was\n *     reached.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     Trying to depend on itself (new stream ID equals\n *     ``pri_spec->stream_id``).\n * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO`\n *     The |session| is server session.\n *\n * .. warning::\n *\n *   This function returns assigned stream ID if it succeeds.  But\n *   that stream is not created yet.  The application must not submit\n *   frame to that stream ID before\n *   :type:`nghttp2_before_frame_send_callback` is called for this\n *   frame.  This means `nghttp2_session_get_stream_user_data()` does\n *   not work before the callback.  But\n *   `nghttp2_session_set_stream_user_data()` handles this situation\n *   specially, and it can set data to a stream during this period.\n *\n */\nNGHTTP2_EXTERN int32_t nghttp2_submit_request(\n  nghttp2_session *session, const nghttp2_priority_spec *pri_spec,\n  const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd,\n  void *stream_user_data);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @function\n *\n * Submits HEADERS frame and optionally one or more DATA frames.\n *\n * The |pri_spec| is a deprecated priority specification of this\n * request.  ``NULL`` means the default priority (see\n * `nghttp2_priority_spec_default_init()`).  To specify the priority,\n * use `nghttp2_priority_spec_init()`.  If |pri_spec| is not ``NULL``,\n * this function will copy its data members.  In the future release\n * after the end of 2024, this function will ignore |pri_spec| and\n * behave as if ``NULL`` is given.\n *\n * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`,\n * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive.  If ``pri_spec->weight``\n * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes\n * :macro:`NGHTTP2_MIN_WEIGHT`.  If it is strictly greater than\n * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes\n * :macro:`NGHTTP2_MAX_WEIGHT`.\n *\n * If\n * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`\n * of value of 1 is received by a remote endpoint, |pri_spec| is\n * ignored, and treated as if ``NULL`` is specified.\n *\n * The |nva| is an array of name/value pair :type:`nghttp2_nv` with\n * |nvlen| elements.  The application is responsible to include\n * required pseudo-header fields (header field whose name starts with\n * \":\") in |nva| and must place pseudo-headers before regular header\n * fields.\n *\n * This function creates copies of all name/value pairs in |nva|.  It\n * also lower-cases all names in |nva|.  The order of elements in\n * |nva| is preserved.  For header fields with\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set,\n * header field name and value are not copied respectively.  With\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application\n * is responsible to pass header field name in lowercase.  The\n * application should maintain the references to them until\n * :type:`nghttp2_on_frame_send_callback` or\n * :type:`nghttp2_on_frame_not_send_callback` is called.\n *\n * HTTP/2 specification has requirement about header fields in the\n * request HEADERS.  See the specification for more details.\n *\n * If |data_prd| is not ``NULL``, it provides data which will be sent\n * in subsequent DATA frames.  In this case, a method that allows\n * request message bodies\n * (https://tools.ietf.org/html/rfc7231#section-4) must be specified\n * with ``:method`` key in |nva| (e.g. ``POST``).  This function does\n * not take ownership of the |data_prd|.  The function copies the\n * members of the |data_prd|.  If |data_prd| is ``NULL``, HEADERS have\n * END_STREAM set.  The |stream_user_data| is data associated to the\n * stream opened by this request and can be an arbitrary pointer,\n * which can be retrieved later by\n * `nghttp2_session_get_stream_user_data()`.\n *\n * This function returns assigned stream ID if it succeeds, or one of\n * the following negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE`\n *     No stream ID is available because maximum stream ID was\n *     reached.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     Trying to depend on itself (new stream ID equals\n *     ``pri_spec->stream_id``).\n * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO`\n *     The |session| is server session.\n *\n * .. warning::\n *\n *   This function returns assigned stream ID if it succeeds.  But\n *   that stream is not created yet.  The application must not submit\n *   frame to that stream ID before\n *   :type:`nghttp2_before_frame_send_callback` is called for this\n *   frame.  This means `nghttp2_session_get_stream_user_data()` does\n *   not work before the callback.  But\n *   `nghttp2_session_set_stream_user_data()` handles this situation\n *   specially, and it can set data to a stream during this period.\n *\n */\nNGHTTP2_EXTERN int32_t nghttp2_submit_request2(\n  nghttp2_session *session, const nghttp2_priority_spec *pri_spec,\n  const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider2 *data_prd,\n  void *stream_user_data);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  Use `nghttp2_submit_response2()` instead.\n *\n * Submits response HEADERS frame and optionally one or more DATA\n * frames against the stream |stream_id|.\n *\n * The |nva| is an array of name/value pair :type:`nghttp2_nv` with\n * |nvlen| elements.  The application is responsible to include\n * required pseudo-header fields (header field whose name starts with\n * \":\") in |nva| and must place pseudo-headers before regular header\n * fields.\n *\n * This function creates copies of all name/value pairs in |nva|.  It\n * also lower-cases all names in |nva|.  The order of elements in\n * |nva| is preserved.  For header fields with\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set,\n * header field name and value are not copied respectively.  With\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application\n * is responsible to pass header field name in lowercase.  The\n * application should maintain the references to them until\n * :type:`nghttp2_on_frame_send_callback` or\n * :type:`nghttp2_on_frame_not_send_callback` is called.\n *\n * HTTP/2 specification has requirement about header fields in the\n * response HEADERS.  See the specification for more details.\n *\n * If |data_prd| is not ``NULL``, it provides data which will be sent\n * in subsequent DATA frames.  This function does not take ownership\n * of the |data_prd|.  The function copies the members of the\n * |data_prd|.  If |data_prd| is ``NULL``, HEADERS will have\n * END_STREAM flag set.\n *\n * This method can be used as normal HTTP response and push response.\n * When pushing a resource using this function, the |session| must be\n * configured using `nghttp2_session_server_new()` or its variants and\n * the target stream denoted by the |stream_id| must be reserved using\n * `nghttp2_submit_push_promise()`.\n *\n * To send non-final response headers (e.g., HTTP status 101), don't\n * use this function because this function half-closes the outbound\n * stream.  Instead, use `nghttp2_submit_headers()` for this purpose.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |stream_id| is 0.\n * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST`\n *     DATA or HEADERS has been already submitted and not fully\n *     processed yet.  Normally, this does not happen, but when\n *     application wrongly calls `nghttp2_submit_response()` twice,\n *     this may happen.\n * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO`\n *     The |session| is client session.\n *\n * .. warning::\n *\n *   Calling this function twice for the same stream ID may lead to\n *   program crash.  It is generally considered to a programming error\n *   to commit response twice.\n */\nNGHTTP2_EXTERN int\nnghttp2_submit_response(nghttp2_session *session, int32_t stream_id,\n                        const nghttp2_nv *nva, size_t nvlen,\n                        const nghttp2_data_provider *data_prd);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @function\n *\n * Submits response HEADERS frame and optionally one or more DATA\n * frames against the stream |stream_id|.\n *\n * The |nva| is an array of name/value pair :type:`nghttp2_nv` with\n * |nvlen| elements.  The application is responsible to include\n * required pseudo-header fields (header field whose name starts with\n * \":\") in |nva| and must place pseudo-headers before regular header\n * fields.\n *\n * This function creates copies of all name/value pairs in |nva|.  It\n * also lower-cases all names in |nva|.  The order of elements in\n * |nva| is preserved.  For header fields with\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set,\n * header field name and value are not copied respectively.  With\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application\n * is responsible to pass header field name in lowercase.  The\n * application should maintain the references to them until\n * :type:`nghttp2_on_frame_send_callback` or\n * :type:`nghttp2_on_frame_not_send_callback` is called.\n *\n * HTTP/2 specification has requirement about header fields in the\n * response HEADERS.  See the specification for more details.\n *\n * If |data_prd| is not ``NULL``, it provides data which will be sent\n * in subsequent DATA frames.  This function does not take ownership\n * of the |data_prd|.  The function copies the members of the\n * |data_prd|.  If |data_prd| is ``NULL``, HEADERS will have\n * END_STREAM flag set.\n *\n * This method can be used as normal HTTP response and push response.\n * When pushing a resource using this function, the |session| must be\n * configured using `nghttp2_session_server_new()` or its variants and\n * the target stream denoted by the |stream_id| must be reserved using\n * `nghttp2_submit_push_promise()`.\n *\n * To send non-final response headers (e.g., HTTP status 101), don't\n * use this function because this function half-closes the outbound\n * stream.  Instead, use `nghttp2_submit_headers()` for this purpose.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |stream_id| is 0.\n * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST`\n *     DATA or HEADERS has been already submitted and not fully\n *     processed yet.  Normally, this does not happen, but when\n *     application wrongly calls `nghttp2_submit_response2()` twice,\n *     this may happen.\n * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO`\n *     The |session| is client session.\n *\n * .. warning::\n *\n *   Calling this function twice for the same stream ID may lead to\n *   program crash.  It is generally considered to a programming error\n *   to commit response twice.\n */\nNGHTTP2_EXTERN int\nnghttp2_submit_response2(nghttp2_session *session, int32_t stream_id,\n                         const nghttp2_nv *nva, size_t nvlen,\n                         const nghttp2_data_provider2 *data_prd);\n\n/**\n * @function\n *\n * Submits trailer fields HEADERS against the stream |stream_id|.\n *\n * The |nva| is an array of name/value pair :type:`nghttp2_nv` with\n * |nvlen| elements.  The application must not include pseudo-header\n * fields (headers whose names starts with \":\") in |nva|.\n *\n * This function creates copies of all name/value pairs in |nva|.  It\n * also lower-cases all names in |nva|.  The order of elements in\n * |nva| is preserved.  For header fields with\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set,\n * header field name and value are not copied respectively.  With\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application\n * is responsible to pass header field name in lowercase.  The\n * application should maintain the references to them until\n * :type:`nghttp2_on_frame_send_callback` or\n * :type:`nghttp2_on_frame_not_send_callback` is called.\n *\n * For server, trailer fields must follow response HEADERS or response\n * DATA without END_STREAM flat set.  The library does not enforce\n * this requirement, and applications should do this for themselves.\n * If `nghttp2_submit_trailer()` is called before any response HEADERS\n * submission (usually by `nghttp2_submit_response2()`), the content\n * of |nva| will be sent as response headers, which will result in\n * error.\n *\n * This function has the same effect with `nghttp2_submit_headers()`,\n * with flags = :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` and both\n * pri_spec and stream_user_data to NULL.\n *\n * To submit trailer fields after `nghttp2_submit_response2()` is\n * called, the application has to specify\n * :type:`nghttp2_data_provider2` to `nghttp2_submit_response2()`.\n * Inside of :type:`nghttp2_data_source_read_callback2`, when setting\n * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF`, also set\n * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_END_STREAM`.  After\n * that, the application can send trailer fields using\n * `nghttp2_submit_trailer()`.  `nghttp2_submit_trailer()` can be used\n * inside :type:`nghttp2_data_source_read_callback2`.\n *\n * This function returns 0 if it succeeds and |stream_id| is -1.\n * Otherwise, this function returns 0 if it succeeds, or one of the\n * following negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |stream_id| is 0.\n */\nNGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session,\n                                          int32_t stream_id,\n                                          const nghttp2_nv *nva, size_t nvlen);\n\n/**\n * @function\n *\n * Submits HEADERS frame. The |flags| is bitwise OR of the\n * following values:\n *\n * * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`\n *\n * If |flags| includes :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`,\n * this frame has END_STREAM flag set.\n *\n * The library handles the CONTINUATION frame internally and it\n * correctly sets END_HEADERS to the last sequence of the PUSH_PROMISE\n * or CONTINUATION frame.\n *\n * If the |stream_id| is -1, this frame is assumed as request (i.e.,\n * request HEADERS frame which opens new stream).  In this case, the\n * assigned stream ID will be returned.  Otherwise, specify stream ID\n * in |stream_id|.\n *\n * The |pri_spec| is a deprecated priority specification of this\n * request.  ``NULL`` means the default priority (see\n * `nghttp2_priority_spec_default_init()`).  To specify the priority,\n * use `nghttp2_priority_spec_init()`.  If |pri_spec| is not ``NULL``,\n * this function will copy its data members.  In the future release\n * after the end of 2024, this function will ignore |pri_spec| and\n * behave as if ``NULL`` is given.\n *\n * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`,\n * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive.  If ``pri_spec->weight``\n * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes\n * :macro:`NGHTTP2_MIN_WEIGHT`.  If it is strictly greater than\n * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`.\n *\n * If\n * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`\n * of value of 1 is received by a remote endpoint, |pri_spec| is\n * ignored, and treated as if ``NULL`` is specified.\n *\n * The |nva| is an array of name/value pair :type:`nghttp2_nv` with\n * |nvlen| elements.  The application is responsible to include\n * required pseudo-header fields (header field whose name starts with\n * \":\") in |nva| and must place pseudo-headers before regular header\n * fields.\n *\n * This function creates copies of all name/value pairs in |nva|.  It\n * also lower-cases all names in |nva|.  The order of elements in\n * |nva| is preserved.  For header fields with\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set,\n * header field name and value are not copied respectively.  With\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application\n * is responsible to pass header field name in lowercase.  The\n * application should maintain the references to them until\n * :type:`nghttp2_on_frame_send_callback` or\n * :type:`nghttp2_on_frame_not_send_callback` is called.\n *\n * The |stream_user_data| is a pointer to an arbitrary data which is\n * associated to the stream this frame will open.  Therefore it is\n * only used if this frame opens streams, in other words, it changes\n * stream state from idle or reserved to open.\n *\n * This function is low-level in a sense that the application code can\n * specify flags directly.  For usual HTTP request,\n * `nghttp2_submit_request2()` is useful.  Likewise, for HTTP\n * response, prefer `nghttp2_submit_response2()`.\n *\n * This function returns newly assigned stream ID if it succeeds and\n * |stream_id| is -1.  Otherwise, this function returns 0 if it\n * succeeds, or one of the following negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE`\n *     No stream ID is available because maximum stream ID was\n *     reached.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |stream_id| is 0; or trying to depend on itself (stream ID\n *     equals ``pri_spec->stream_id``).\n * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST`\n *     DATA or HEADERS has been already submitted and not fully\n *     processed yet.  This happens if stream denoted by |stream_id|\n *     is in reserved state.\n * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO`\n *     The |stream_id| is -1, and |session| is server session.\n *\n * .. warning::\n *\n *   This function returns assigned stream ID if it succeeds and\n *   |stream_id| is -1.  But that stream is not opened yet.  The\n *   application must not submit frame to that stream ID before\n *   :type:`nghttp2_before_frame_send_callback` is called for this\n *   frame.\n *\n */\nNGHTTP2_EXTERN int32_t nghttp2_submit_headers(\n  nghttp2_session *session, uint8_t flags, int32_t stream_id,\n  const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen,\n  void *stream_user_data);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  Use `nghttp2_submit_data2()` instead.\n *\n * Submits one or more DATA frames to the stream |stream_id|.  The\n * data to be sent are provided by |data_prd|.  If |flags| contains\n * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`, the last DATA frame\n * has END_STREAM flag set.\n *\n * This function does not take ownership of the |data_prd|.  The\n * function copies the members of the |data_prd|.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST`\n *     DATA or HEADERS has been already submitted and not fully\n *     processed yet.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |stream_id| is 0.\n * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED`\n *     The stream was already closed; or the |stream_id| is invalid.\n *\n * .. note::\n *\n *   Currently, only one DATA or HEADERS is allowed for a stream at a\n *   time.  Submitting these frames more than once before first DATA\n *   or HEADERS is finished results in\n *   :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` error code.  The\n *   earliest callback which tells that previous frame is done is\n *   :type:`nghttp2_on_frame_send_callback`.  In side that callback,\n *   new data can be submitted using `nghttp2_submit_data()`.  Of\n *   course, all data except for last one must not have\n *   :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` flag set in |flags|.\n *   This sounds a bit complicated, and we recommend to use\n *   `nghttp2_submit_request()` and `nghttp2_submit_response()` to\n *   avoid this cascading issue.  The experience shows that for HTTP\n *   use, these two functions are enough to implement both client and\n *   server.\n */\nNGHTTP2_EXTERN int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,\n                                       int32_t stream_id,\n                                       const nghttp2_data_provider *data_prd);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @function\n *\n * Submits one or more DATA frames to the stream |stream_id|.  The\n * data to be sent are provided by |data_prd|.  If |flags| contains\n * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`, the last DATA frame\n * has END_STREAM flag set.\n *\n * This function does not take ownership of the |data_prd|.  The\n * function copies the members of the |data_prd|.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST`\n *     DATA or HEADERS has been already submitted and not fully\n *     processed yet.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |stream_id| is 0.\n * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED`\n *     The stream was already closed; or the |stream_id| is invalid.\n *\n * .. note::\n *\n *   Currently, only one DATA or HEADERS is allowed for a stream at a\n *   time.  Submitting these frames more than once before first DATA\n *   or HEADERS is finished results in\n *   :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` error code.  The\n *   earliest callback which tells that previous frame is done is\n *   :type:`nghttp2_on_frame_send_callback`.  In side that callback,\n *   new data can be submitted using `nghttp2_submit_data2()`.  Of\n *   course, all data except for last one must not have\n *   :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` flag set in |flags|.\n *   This sounds a bit complicated, and we recommend to use\n *   `nghttp2_submit_request2()` and `nghttp2_submit_response2()` to\n *   avoid this cascading issue.  The experience shows that for HTTP\n *   use, these two functions are enough to implement both client and\n *   server.\n */\nNGHTTP2_EXTERN int nghttp2_submit_data2(nghttp2_session *session, uint8_t flags,\n                                        int32_t stream_id,\n                                        const nghttp2_data_provider2 *data_prd);\n\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.  In the future release after the end of\n *   2024, this function will always return 0 without doing anything.\n *\n * Submits PRIORITY frame to change the priority of stream |stream_id|\n * to the priority specification |pri_spec|.\n *\n * The |flags| is currently ignored and should be\n * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`.\n *\n * The |pri_spec| is a deprecated priority specification of this\n * request.  ``NULL`` is not allowed for this function. To specify the\n * priority, use `nghttp2_priority_spec_init()`.  This function will\n * copy its data members.\n *\n * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`,\n * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive.  If ``pri_spec->weight``\n * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes\n * :macro:`NGHTTP2_MIN_WEIGHT`.  If it is strictly greater than\n * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes\n * :macro:`NGHTTP2_MAX_WEIGHT`.\n *\n * If\n * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`\n * of value of 1 is received by a remote endpoint, this function does\n * nothing and returns 0.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |stream_id| is 0; or the |pri_spec| is NULL; or trying to\n *     depend on itself.\n */\nNGHTTP2_EXTERN int\nnghttp2_submit_priority(nghttp2_session *session, uint8_t flags,\n                        int32_t stream_id,\n                        const nghttp2_priority_spec *pri_spec);\n\n/**\n * @macro\n *\n * :macro:`NGHTTP2_EXTPRI_DEFAULT_URGENCY` is the default urgency\n * level for :rfc:`9218` extensible priorities.\n */\n#define NGHTTP2_EXTPRI_DEFAULT_URGENCY 3\n\n/**\n * @macro\n *\n * :macro:`NGHTTP2_EXTPRI_URGENCY_HIGH` is the highest urgency level\n * for :rfc:`9218` extensible priorities.\n */\n#define NGHTTP2_EXTPRI_URGENCY_HIGH 0\n\n/**\n * @macro\n *\n * :macro:`NGHTTP2_EXTPRI_URGENCY_LOW` is the lowest urgency level for\n * :rfc:`9218` extensible priorities.\n */\n#define NGHTTP2_EXTPRI_URGENCY_LOW 7\n\n/**\n * @macro\n *\n * :macro:`NGHTTP2_EXTPRI_URGENCY_LEVELS` is the number of urgency\n * levels for :rfc:`9218` extensible priorities.\n */\n#define NGHTTP2_EXTPRI_URGENCY_LEVELS (NGHTTP2_EXTPRI_URGENCY_LOW + 1)\n\n/**\n * @struct\n *\n * :type:`nghttp2_extpri` is :rfc:`9218` extensible priorities\n * specification for a stream.\n */\ntypedef struct nghttp2_extpri {\n  /**\n   * :member:`urgency` is the urgency of a stream, it must be in\n   * [:macro:`NGHTTP2_EXTPRI_URGENCY_HIGH`,\n   * :macro:`NGHTTP2_EXTPRI_URGENCY_LOW`], inclusive, and 0 is the\n   * highest urgency.\n   */\n  uint32_t urgency;\n  /**\n   * :member:`inc` indicates that a content can be processed\n   * incrementally or not.  If inc is 0, it cannot be processed\n   * incrementally.  If inc is 1, it can be processed incrementally.\n   * Other value is not permitted.\n   */\n  int inc;\n} nghttp2_extpri;\n\n/**\n * @function\n *\n * Submits RST_STREAM frame to cancel/reject the stream |stream_id|\n * with the error code |error_code|.\n *\n * The pre-defined error code is one of :enum:`nghttp2_error_code`.\n *\n * The |flags| is currently ignored and should be\n * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |stream_id| is 0.\n */\nNGHTTP2_EXTERN int nghttp2_submit_rst_stream(nghttp2_session *session,\n                                             uint8_t flags, int32_t stream_id,\n                                             uint32_t error_code);\n\n/**\n * @function\n *\n * Stores local settings and submits SETTINGS frame.  The |iv| is the\n * pointer to the array of :type:`nghttp2_settings_entry`.  The |niv|\n * indicates the number of :type:`nghttp2_settings_entry`.\n *\n * The |flags| is currently ignored and should be\n * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`.\n *\n * This function does not take ownership of the |iv|.  This function\n * copies all the elements in the |iv|.\n *\n * While updating individual stream's local window size, if the window\n * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE,\n * RST_STREAM is issued against such a stream.\n *\n * SETTINGS with :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` is\n * automatically submitted by the library and application could not\n * send it at its will.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |iv| contains invalid value (e.g., initial window size\n *     strictly greater than (1 << 31) - 1.\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nNGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session,\n                                           uint8_t flags,\n                                           const nghttp2_settings_entry *iv,\n                                           size_t niv);\n\n/**\n * @function\n *\n * Submits PUSH_PROMISE frame.\n *\n * The |flags| is currently ignored.  The library handles the\n * CONTINUATION frame internally and it correctly sets END_HEADERS to\n * the last sequence of the PUSH_PROMISE or CONTINUATION frame.\n *\n * The |stream_id| must be client initiated stream ID.\n *\n * The |nva| is an array of name/value pair :type:`nghttp2_nv` with\n * |nvlen| elements.  The application is responsible to include\n * required pseudo-header fields (header field whose name starts with\n * \":\") in |nva| and must place pseudo-headers before regular header\n * fields.\n *\n * This function creates copies of all name/value pairs in |nva|.  It\n * also lower-cases all names in |nva|.  The order of elements in\n * |nva| is preserved.  For header fields with\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set,\n * header field name and value are not copied respectively.  With\n * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application\n * is responsible to pass header field name in lowercase.  The\n * application should maintain the references to them until\n * :type:`nghttp2_on_frame_send_callback` or\n * :type:`nghttp2_on_frame_not_send_callback` is called.\n *\n * The |promised_stream_user_data| is a pointer to an arbitrary data\n * which is associated to the promised stream this frame will open and\n * make it in reserved state.  It is available using\n * `nghttp2_session_get_stream_user_data()`.  The application can\n * access it in :type:`nghttp2_before_frame_send_callback` and\n * :type:`nghttp2_on_frame_send_callback` of this frame.\n *\n * The client side is not allowed to use this function.\n *\n * To submit response headers and data, use\n * `nghttp2_submit_response2()`.\n *\n * This function returns assigned promised stream ID if it succeeds,\n * or one of the following negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO`\n *     This function was invoked when |session| is initialized as\n *     client.\n * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE`\n *     No stream ID is available because maximum stream ID was\n *     reached.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |stream_id| is 0; The |stream_id| does not designate stream\n *     that peer initiated.\n * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED`\n *     The stream was already closed; or the |stream_id| is invalid.\n *\n * .. warning::\n *\n *   This function returns assigned promised stream ID if it succeeds.\n *   As of 1.16.0, stream object for pushed resource is created when\n *   this function succeeds.  In that case, the application can submit\n *   push response for the promised frame.\n *\n *   In 1.15.0 or prior versions, pushed stream is not opened yet when\n *   this function succeeds.  The application must not submit frame to\n *   that stream ID before :type:`nghttp2_before_frame_send_callback`\n *   is called for this frame.\n *\n */\nNGHTTP2_EXTERN int32_t nghttp2_submit_push_promise(\n  nghttp2_session *session, uint8_t flags, int32_t stream_id,\n  const nghttp2_nv *nva, size_t nvlen, void *promised_stream_user_data);\n\n/**\n * @function\n *\n * Submits PING frame.  You don't have to send PING back when you\n * received PING frame.  The library automatically submits PING frame\n * in this case.\n *\n * The |flags| is bitwise OR of 0 or more of the following value.\n *\n * * :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK`\n *\n * Unless `nghttp2_option_set_no_auto_ping_ack()` is used, the |flags|\n * should be :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`.\n *\n * If the |opaque_data| is non ``NULL``, then it should point to the 8\n * bytes array of memory to specify opaque data to send with PING\n * frame.  If the |opaque_data| is ``NULL``, zero-cleared 8 bytes will\n * be sent as opaque data.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nNGHTTP2_EXTERN int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,\n                                       const uint8_t *opaque_data);\n\n/**\n * @function\n *\n * Submits GOAWAY frame with the last stream ID |last_stream_id| and\n * the error code |error_code|.\n *\n * The pre-defined error code is one of :enum:`nghttp2_error_code`.\n *\n * The |flags| is currently ignored and should be\n * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`.\n *\n * The |last_stream_id| is peer's stream ID or 0.  So if |session| is\n * initialized as client, |last_stream_id| must be even or 0.  If\n * |session| is initialized as server, |last_stream_id| must be odd or\n * 0.\n *\n * The HTTP/2 specification says last_stream_id must not be increased\n * from the value previously sent.  So the actual value sent as\n * last_stream_id is the minimum value between the given\n * |last_stream_id| and the last_stream_id previously sent to the\n * peer.\n *\n * If the |opaque_data| is not ``NULL`` and |opaque_data_len| is not\n * zero, those data will be sent as additional debug data.  The\n * library makes a copy of the memory region pointed by |opaque_data|\n * with the length |opaque_data_len|, so the caller does not need to\n * keep this memory after the return of this function.  If the\n * |opaque_data_len| is 0, the |opaque_data| could be ``NULL``.\n *\n * After successful transmission of GOAWAY, following things happen.\n * All incoming streams having strictly more than |last_stream_id| are\n * closed.  All incoming HEADERS which starts new stream are simply\n * ignored.  After all active streams are handled, both\n * `nghttp2_session_want_read()` and `nghttp2_session_want_write()`\n * return 0 and the application can close session.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |opaque_data_len| is too large; the |last_stream_id| is\n *     invalid.\n */\nNGHTTP2_EXTERN int nghttp2_submit_goaway(nghttp2_session *session,\n                                         uint8_t flags, int32_t last_stream_id,\n                                         uint32_t error_code,\n                                         const uint8_t *opaque_data,\n                                         size_t opaque_data_len);\n\n/**\n * @function\n *\n * Returns the last stream ID of a stream for which\n * :type:`nghttp2_on_frame_recv_callback` was invoked most recently.\n * The returned value can be used as last_stream_id parameter for\n * `nghttp2_submit_goaway()` and\n * `nghttp2_session_terminate_session2()`.\n *\n * This function always succeeds.\n */\nNGHTTP2_EXTERN int32_t\nnghttp2_session_get_last_proc_stream_id(nghttp2_session *session);\n\n/**\n * @function\n *\n * Returns nonzero if new request can be sent from local endpoint.\n *\n * This function return 0 if request is not allowed for this session.\n * There are several reasons why request is not allowed.  Some of the\n * reasons are: session is server; stream ID has been spent; GOAWAY\n * has been sent or received.\n *\n * The application can call `nghttp2_submit_request2()` without\n * consulting this function.  In that case,\n * `nghttp2_submit_request2()` may return error.  Or, request is\n * failed to sent, and :type:`nghttp2_on_stream_close_callback` is\n * called.\n */\nNGHTTP2_EXTERN int\nnghttp2_session_check_request_allowed(nghttp2_session *session);\n\n/**\n * @function\n *\n * Returns nonzero if |session| is initialized as server side session.\n */\nNGHTTP2_EXTERN int\nnghttp2_session_check_server_session(nghttp2_session *session);\n\n/**\n * @function\n *\n * Submits WINDOW_UPDATE frame.\n *\n * The |flags| is currently ignored and should be\n * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`.\n *\n * The |stream_id| is the stream ID to send this WINDOW_UPDATE.  To\n * send connection level WINDOW_UPDATE, specify 0 to |stream_id|.\n *\n * If the |window_size_increment| is positive, the WINDOW_UPDATE with\n * that value as window_size_increment is queued.  If the\n * |window_size_increment| is larger than the received bytes from the\n * remote endpoint, the local window size is increased by that\n * difference.  If the sole purpose is to increase the local window\n * size, consider to use `nghttp2_session_set_local_window_size()`.\n *\n * If the |window_size_increment| is negative, the local window size\n * is decreased by -|window_size_increment|.  If automatic\n * WINDOW_UPDATE is enabled\n * (`nghttp2_option_set_no_auto_window_update()`), and the library\n * decided that the WINDOW_UPDATE should be submitted, then\n * WINDOW_UPDATE is queued with the current received bytes count.  If\n * the sole purpose is to decrease the local window size, consider to\n * use `nghttp2_session_set_local_window_size()`.\n *\n * If the |window_size_increment| is 0, the function does nothing and\n * returns 0.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_FLOW_CONTROL`\n *     The local window size overflow or gets negative.\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nNGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session,\n                                                uint8_t flags,\n                                                int32_t stream_id,\n                                                int32_t window_size_increment);\n\n/**\n * @function\n *\n * Set local window size (local endpoints's window size) to the given\n * |window_size| for the given stream denoted by |stream_id|.  To\n * change connection level window size, specify 0 to |stream_id|.  To\n * increase window size, this function may submit WINDOW_UPDATE frame\n * to transmission queue.\n *\n * The |flags| is currently ignored and should be\n * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`.\n *\n * This sounds similar to `nghttp2_submit_window_update()`, but there\n * are 2 differences.  The first difference is that this function\n * takes the absolute value of window size to set, rather than the\n * delta.  To change the window size, this may be easier to use since\n * the application just declares the intended window size, rather than\n * calculating delta.  The second difference is that\n * `nghttp2_submit_window_update()` affects the received bytes count\n * which has not acked yet.  By the specification of\n * `nghttp2_submit_window_update()`, to strictly increase the local\n * window size, we have to submit delta including all received bytes\n * count, which might not be desirable in some cases.  On the other\n * hand, this function does not affect the received bytes count.  It\n * just sets the local window size to the given value.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |stream_id| is negative.\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nNGHTTP2_EXTERN int\nnghttp2_session_set_local_window_size(nghttp2_session *session, uint8_t flags,\n                                      int32_t stream_id, int32_t window_size);\n\n/**\n * @function\n *\n * Submits extension frame.\n *\n * Application can pass arbitrary frame flags and stream ID in |flags|\n * and |stream_id| respectively.  The |payload| is opaque pointer, and\n * it can be accessible though ``frame->ext.payload`` in\n * :type:`nghttp2_pack_extension_callback2`.  The library will not own\n * passed |payload| pointer.\n *\n * The application must set :type:`nghttp2_pack_extension_callback2`\n * using `nghttp2_session_callbacks_set_pack_extension_callback2()`.\n *\n * The application should retain the memory pointed by |payload| until\n * the transmission of extension frame is done (which is indicated by\n * :type:`nghttp2_on_frame_send_callback`), or transmission fails\n * (which is indicated by :type:`nghttp2_on_frame_not_send_callback`).\n * If application does not touch this memory region after packing it\n * into a wire format, application can free it inside\n * :type:`nghttp2_pack_extension_callback2`.\n *\n * The standard HTTP/2 frame cannot be sent with this function, so\n * |type| must be strictly grater than 0x9.  Otherwise, this function\n * will fail with error code\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`\n *     If :type:`nghttp2_pack_extension_callback2` is not set.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     If  |type| specifies  standard  HTTP/2 frame  type.  The  frame\n *     types  in the  rage [0x0,  0x9], both  inclusive, are  standard\n *     HTTP/2 frame type, and cannot be sent using this function.\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory\n */\nNGHTTP2_EXTERN int nghttp2_submit_extension(nghttp2_session *session,\n                                            uint8_t type, uint8_t flags,\n                                            int32_t stream_id, void *payload);\n\n/**\n * @struct\n *\n * The payload of ALTSVC frame.  ALTSVC frame is a non-critical\n * extension to HTTP/2.  If this frame is received, and\n * `nghttp2_option_set_user_recv_extension_type()` is not set, and\n * `nghttp2_option_set_builtin_recv_extension_type()` is set for\n * :enum:`nghttp2_frame_type.NGHTTP2_ALTSVC`,\n * ``nghttp2_extension.payload`` will point to this struct.\n *\n * It has the following members:\n */\ntypedef struct {\n  /**\n   * The pointer to origin which this alternative service is\n   * associated with.  This is not necessarily NULL-terminated.\n   */\n  uint8_t *origin;\n  /**\n   * The length of the |origin|.\n   */\n  size_t origin_len;\n  /**\n   * The pointer to Alt-Svc field value contained in ALTSVC frame.\n   * This is not necessarily NULL-terminated.\n   */\n  uint8_t *field_value;\n  /**\n   * The length of the |field_value|.\n   */\n  size_t field_value_len;\n} nghttp2_ext_altsvc;\n\n/**\n * @function\n *\n * Submits ALTSVC frame.\n *\n * ALTSVC frame is a non-critical extension to HTTP/2, and defined in\n * `RFC 7383 <https://tools.ietf.org/html/rfc7838#section-4>`_.\n *\n * The |flags| is currently ignored and should be\n * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`.\n *\n * The |origin| points to the origin this alternative service is\n * associated with.  The |origin_len| is the length of the origin.  If\n * |stream_id| is 0, the origin must be specified.  If |stream_id| is\n * not zero, the origin must be empty (in other words, |origin_len|\n * must be 0).\n *\n * The ALTSVC frame is only usable from server side.  If this function\n * is invoked with client side session, this function returns\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`\n *     The function is called from client side session\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The sum of |origin_len| and |field_value_len| is larger than\n *     16382; or |origin_len| is 0 while |stream_id| is 0; or\n *     |origin_len| is not 0 while |stream_id| is not 0.\n */\nNGHTTP2_EXTERN int nghttp2_submit_altsvc(nghttp2_session *session,\n                                         uint8_t flags, int32_t stream_id,\n                                         const uint8_t *origin,\n                                         size_t origin_len,\n                                         const uint8_t *field_value,\n                                         size_t field_value_len);\n\n/**\n * @struct\n *\n * The single entry of an origin.\n */\ntypedef struct {\n  /**\n   * The pointer to origin.  No validation is made against this field\n   * by the library.  This is not necessarily NULL-terminated.\n   */\n  uint8_t *origin;\n  /**\n   * The length of the |origin|.\n   */\n  size_t origin_len;\n} nghttp2_origin_entry;\n\n/**\n * @struct\n *\n * The payload of ORIGIN frame.  ORIGIN frame is a non-critical\n * extension to HTTP/2 and defined by `RFC 8336\n * <https://tools.ietf.org/html/rfc8336>`_.\n *\n * If this frame is received, and\n * `nghttp2_option_set_user_recv_extension_type()` is not set, and\n * `nghttp2_option_set_builtin_recv_extension_type()` is set for\n * :enum:`nghttp2_frame_type.NGHTTP2_ORIGIN`,\n * ``nghttp2_extension.payload`` will point to this struct.\n *\n * It has the following members:\n */\ntypedef struct {\n  /**\n   * The number of origins contained in |ov|.\n   */\n  size_t nov;\n  /**\n   * The pointer to the array of origins contained in ORIGIN frame.\n   */\n  nghttp2_origin_entry *ov;\n} nghttp2_ext_origin;\n\n/**\n * @function\n *\n * Submits ORIGIN frame.\n *\n * ORIGIN frame is a non-critical extension to HTTP/2 and defined by\n * `RFC 8336 <https://tools.ietf.org/html/rfc8336>`_.\n *\n * The |flags| is currently ignored and should be\n * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`.\n *\n * The |ov| points to the array of origins.  The |nov| specifies the\n * number of origins included in |ov|.  This function creates copies\n * of all elements in |ov|.\n *\n * The ORIGIN frame is only usable by a server.  If this function is\n * invoked with client side session, this function returns\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`.\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`\n *     The function is called from client side session.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     There are too many origins, or an origin is too large to fit\n *     into a default frame payload.\n */\nNGHTTP2_EXTERN int nghttp2_submit_origin(nghttp2_session *session,\n                                         uint8_t flags,\n                                         const nghttp2_origin_entry *ov,\n                                         size_t nov);\n\n/**\n * @struct\n *\n * The payload of PRIORITY_UPDATE frame.  PRIORITY_UPDATE frame is a\n * non-critical extension to HTTP/2.  If this frame is received, and\n * `nghttp2_option_set_user_recv_extension_type()` is not set, and\n * `nghttp2_option_set_builtin_recv_extension_type()` is set for\n * :enum:`nghttp2_frame_type.NGHTTP2_PRIORITY_UPDATE`,\n * ``nghttp2_extension.payload`` will point to this struct.\n *\n * It has the following members:\n */\ntypedef struct {\n  /**\n   * The stream ID of the stream whose priority is updated.\n   */\n  int32_t stream_id;\n  /**\n   * The pointer to Priority field value.  It is not necessarily\n   * NULL-terminated.\n   */\n  uint8_t *field_value;\n  /**\n   * The length of the :member:`field_value`.\n   */\n  size_t field_value_len;\n} nghttp2_ext_priority_update;\n\n/**\n * @function\n *\n * Submits PRIORITY_UPDATE frame.\n *\n * PRIORITY_UPDATE frame is a non-critical extension to HTTP/2, and\n * defined in :rfc:`9218#section-7.1`.\n *\n * The |flags| is currently ignored and should be\n * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`.\n *\n * The |stream_id| is the ID of stream which is prioritized.  The\n * |field_value| points to the Priority field value.  The\n * |field_value_len| is the length of the Priority field value.\n *\n * If this function is called by server,\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` is returned.\n *\n * If\n * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`\n * of value of 0 is received by a remote endpoint (or it is omitted),\n * this function does nothing and returns 0.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`\n *     The function is called from server side session\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     The |field_value_len| is larger than 16380; or |stream_id| is\n *     0.\n */\nNGHTTP2_EXTERN int nghttp2_submit_priority_update(nghttp2_session *session,\n                                                  uint8_t flags,\n                                                  int32_t stream_id,\n                                                  const uint8_t *field_value,\n                                                  size_t field_value_len);\n\n/**\n * @function\n *\n * Changes the priority of the existing stream denoted by |stream_id|.\n * The new priority is |extpri|.  This function is meant to be used by\n * server for :rfc:`9218` extensible prioritization scheme.\n *\n * If |session| is initialized as client, this function returns\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`.  For client, use\n * `nghttp2_submit_priority_update()` instead.\n *\n * If :member:`extpri->urgency <nghttp2_extpri.urgency>` is out of\n * bound, it is set to :macro:`NGHTTP2_EXTPRI_URGENCY_LOW`.\n *\n * If |ignore_client_signal| is nonzero, server starts to ignore\n * client priority signals for this stream.\n *\n * If\n * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`\n * of value of 1 is not submitted via `nghttp2_submit_settings()`,\n * this function does nothing and returns 0.\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`\n *     The |session| is initialized as client.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     |stream_id| is zero; or a stream denoted by |stream_id| is not\n *     found.\n */\nNGHTTP2_EXTERN int nghttp2_session_change_extpri_stream_priority(\n  nghttp2_session *session, int32_t stream_id, const nghttp2_extpri *extpri,\n  int ignore_client_signal);\n\n/**\n * @function\n *\n * Stores the stream priority of the existing stream denoted by\n * |stream_id| in the object pointed by |extpri|.  This function is\n * meant to be used by server for :rfc:`9218` extensible\n * prioritization scheme.\n *\n * If |session| is initialized as client, this function returns\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`.\n *\n * If\n * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`\n * of value of 1 is not submitted via `nghttp2_submit_settings()`,\n * this function does nothing and returns 0.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`\n *     The |session| is initialized as client.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     |stream_id| is zero; or a stream denoted by |stream_id| is not\n *     found.\n */\nNGHTTP2_EXTERN int nghttp2_session_get_extpri_stream_priority(\n  nghttp2_session *session, nghttp2_extpri *extpri, int32_t stream_id);\n\n/**\n * @function\n *\n * Parses Priority header field value pointed by |value| of length\n * |len|, and stores the result in the object pointed by |extpri|.\n * Priority header field is defined in :rfc:`9218`.\n *\n * This function does not initialize the object pointed by |extpri|\n * before storing the result.  It only assigns the values that the\n * parser correctly extracted to fields.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`\n *     Failed to parse the header field value.\n */\nNGHTTP2_EXTERN int nghttp2_extpri_parse_priority(nghttp2_extpri *extpri,\n                                                 const uint8_t *value,\n                                                 size_t len);\n\n/**\n * @function\n *\n * Compares ``lhs->name`` of length ``lhs->namelen`` bytes and\n * ``rhs->name`` of length ``rhs->namelen`` bytes.  Returns negative\n * integer if ``lhs->name`` is found to be less than ``rhs->name``; or\n * returns positive integer if ``lhs->name`` is found to be greater\n * than ``rhs->name``; or returns 0 otherwise.\n */\nNGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs,\n                                           const nghttp2_nv *rhs);\n\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  Use `nghttp2_select_alpn` instead.\n *\n * A helper function for dealing with ALPN in server side.  The |in|\n * contains peer's protocol list in preferable order.  The format of\n * |in| is length-prefixed and not null-terminated.  For example,\n * ``h2`` and ``http/1.1`` stored in |in| like this::\n *\n *     in[0] = 2\n *     in[1..2] = \"h2\"\n *     in[3] = 8\n *     in[4..11] = \"http/1.1\"\n *     inlen = 12\n *\n * The selection algorithm is as follows:\n *\n * 1. If peer's list contains HTTP/2 protocol the library supports,\n *    it is selected and returns 1. The following step is not taken.\n *\n * 2. If peer's list contains ``http/1.1``, this function selects\n *    ``http/1.1`` and returns 0.  The following step is not taken.\n *\n * 3. This function selects nothing and returns -1 (So called\n *    non-overlap case).  In this case, |out| and |outlen| are left\n *    untouched.\n *\n * Selecting ``h2`` means that ``h2`` is written into |*out| and its\n * length (which is 2) is assigned to |*outlen|.\n *\n * For ALPN, refer to https://tools.ietf.org/html/rfc7301\n *\n * To use this method you should do something like::\n *\n *     static int alpn_select_proto_cb(SSL* ssl,\n *                                     const unsigned char **out,\n *                                     unsigned char *outlen,\n *                                     const unsigned char *in,\n *                                     unsigned int inlen,\n *                                     void *arg)\n *     {\n *         int rv;\n *         rv = nghttp2_select_next_protocol((unsigned char**)out, outlen,\n *                                           in, inlen);\n *         if (rv == -1) {\n *             return SSL_TLSEXT_ERR_NOACK;\n *         }\n *         if (rv == 1) {\n *             ((MyType*)arg)->http2_selected = 1;\n *         }\n *         return SSL_TLSEXT_ERR_OK;\n *     }\n *     ...\n *     SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, my_obj);\n *\n */\nNGHTTP2_EXTERN int nghttp2_select_next_protocol(unsigned char **out,\n                                                unsigned char *outlen,\n                                                const unsigned char *in,\n                                                unsigned int inlen);\n\n/**\n * @function\n *\n * A helper function for dealing with ALPN in server side.  The |in|\n * contains peer's protocol list in preferable order.  The format of\n * |in| is length-prefixed and not null-terminated.  For example,\n * ``h2`` and ``http/1.1`` stored in |in| like this::\n *\n *     in[0] = 2\n *     in[1..2] = \"h2\"\n *     in[3] = 8\n *     in[4..11] = \"http/1.1\"\n *     inlen = 12\n *\n * The selection algorithm is as follows:\n *\n * 1. If peer's list contains HTTP/2 protocol the library supports,\n *    it is selected and returns 1. The following step is not taken.\n *\n * 2. If peer's list contains ``http/1.1``, this function selects\n *    ``http/1.1`` and returns 0.  The following step is not taken.\n *\n * 3. This function selects nothing and returns -1 (So called\n *    non-overlap case).  In this case, |out| and |outlen| are left\n *    untouched.\n *\n * Selecting ``h2`` means that ``h2`` is written into |*out| and its\n * length (which is 2) is assigned to |*outlen|.\n *\n * For ALPN, refer to https://tools.ietf.org/html/rfc7301\n *\n * To use this method you should do something like::\n *\n *     static int alpn_select_proto_cb(SSL* ssl,\n *                                     const unsigned char **out,\n *                                     unsigned char *outlen,\n *                                     const unsigned char *in,\n *                                     unsigned int inlen,\n *                                     void *arg)\n *     {\n *         int rv;\n *         rv = nghttp2_select_alpn(out, outlen, in, inlen);\n *         if (rv == -1) {\n *             return SSL_TLSEXT_ERR_NOACK;\n *         }\n *         if (rv == 1) {\n *             ((MyType*)arg)->http2_selected = 1;\n *         }\n *         return SSL_TLSEXT_ERR_OK;\n *     }\n *     ...\n *     SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, my_obj);\n *\n */\nNGHTTP2_EXTERN int nghttp2_select_alpn(const unsigned char **out,\n                                       unsigned char *outlen,\n                                       const unsigned char *in,\n                                       unsigned int inlen);\n\n/**\n * @function\n *\n * Returns a pointer to a nghttp2_info struct with version information\n * about the run-time library in use.  The |least_version| argument\n * can be set to a 24 bit numerical value for the least accepted\n * version number and if the condition is not met, this function will\n * return a ``NULL``.  Pass in 0 to skip the version checking.\n */\nNGHTTP2_EXTERN nghttp2_info *nghttp2_version(int least_version);\n\n/**\n * @function\n *\n * Returns nonzero if the :type:`nghttp2_error` library error code\n * |lib_error| is fatal.\n */\nNGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error_code);\n\n/**\n * @function\n *\n * Returns nonzero if HTTP header field name |name| of length |len| is\n * valid according to http://tools.ietf.org/html/rfc7230#section-3.2\n *\n * Because this is a header field name in HTTP2, the upper cased alphabet\n * is treated as error.\n */\nNGHTTP2_EXTERN int nghttp2_check_header_name(const uint8_t *name, size_t len);\n\n/**\n * @function\n *\n * Returns nonzero if HTTP header field value |value| of length |len|\n * is valid according to\n * http://tools.ietf.org/html/rfc7230#section-3.2\n *\n * This function is considered obsolete, and application should\n * consider to use `nghttp2_check_header_value_rfc9113()` instead.\n */\nNGHTTP2_EXTERN int nghttp2_check_header_value(const uint8_t *value, size_t len);\n\n/**\n * @function\n *\n * Returns nonzero if HTTP header field value |value| of length |len|\n * is valid according to\n * http://tools.ietf.org/html/rfc7230#section-3.2, plus\n * https://datatracker.ietf.org/doc/html/rfc9113#section-8.2.1\n */\nNGHTTP2_EXTERN int nghttp2_check_header_value_rfc9113(const uint8_t *value,\n                                                      size_t len);\n\n/**\n * @function\n *\n * Returns nonzero if the |value| which is supposed to be the value of\n * the :method header field is valid according to\n * https://datatracker.ietf.org/doc/html/rfc7231#section-4 and\n * https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6\n */\nNGHTTP2_EXTERN int nghttp2_check_method(const uint8_t *value, size_t len);\n\n/**\n * @function\n *\n * Returns nonzero if the |value| which is supposed to be the value of\n * the :path header field is valid according to\n * https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.3\n *\n * |value| is valid if it merely consists of the allowed characters.\n * In particular, it does not check whether |value| follows the syntax\n * of path.  The allowed characters are all characters valid by\n * `nghttp2_check_header_value` minus SPC and HT.\n */\nNGHTTP2_EXTERN int nghttp2_check_path(const uint8_t *value, size_t len);\n\n/**\n * @function\n *\n * Returns nonzero if the |value| which is supposed to be the value of the\n * :authority or host header field is valid according to\n * https://tools.ietf.org/html/rfc3986#section-3.2\n *\n * Note that :authority and host field values are not authority.  They\n * do not include userinfo in RFC 3986, see\n * https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2, that\n * is, it does not include '@'.  This function treats '@' as a valid\n * character.\n *\n * |value| is valid if it merely consists of the allowed characters.\n * In particular, it does not check whether |value| follows the syntax\n * of authority.\n */\nNGHTTP2_EXTERN int nghttp2_check_authority(const uint8_t *value, size_t len);\n\n/* HPACK API */\n\nstruct nghttp2_hd_deflater;\n\n/**\n * @struct\n *\n * HPACK deflater object.\n */\ntypedef struct nghttp2_hd_deflater nghttp2_hd_deflater;\n\n/**\n * @function\n *\n * Initializes |*deflater_ptr| for deflating name/values pairs.\n *\n * The |max_deflate_dynamic_table_size| is the upper bound of header\n * table size the deflater will use.\n *\n * If this function fails, |*deflater_ptr| is left untouched.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nNGHTTP2_EXTERN int\nnghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr,\n                       size_t max_deflate_dynamic_table_size);\n\n/**\n * @function\n *\n * Like `nghttp2_hd_deflate_new()`, but with additional custom memory\n * allocator specified in the |mem|.\n *\n * The |mem| can be ``NULL`` and the call is equivalent to\n * `nghttp2_hd_deflate_new()`.\n *\n * This function does not take ownership |mem|.  The application is\n * responsible for freeing |mem|.\n *\n * The library code does not refer to |mem| pointer after this\n * function returns, so the application can safely free it.\n */\nNGHTTP2_EXTERN int\nnghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr,\n                        size_t max_deflate_dynamic_table_size,\n                        nghttp2_mem *mem);\n\n/**\n * @function\n *\n * Deallocates any resources allocated for |deflater|.\n */\nNGHTTP2_EXTERN void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater);\n\n/**\n * @function\n *\n * Changes header table size of the |deflater| to\n * |settings_max_dynamic_table_size| bytes.  This may trigger eviction\n * in the dynamic table.\n *\n * The |settings_max_dynamic_table_size| should be the value received\n * in SETTINGS_HEADER_TABLE_SIZE.\n *\n * The deflater never uses more memory than\n * ``max_deflate_dynamic_table_size`` bytes specified in\n * `nghttp2_hd_deflate_new()`.  Therefore, if\n * |settings_max_dynamic_table_size| >\n * ``max_deflate_dynamic_table_size``, resulting maximum table size\n * becomes ``max_deflate_dynamic_table_size``.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nNGHTTP2_EXTERN int\nnghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater,\n                                     size_t settings_max_dynamic_table_size);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  Use `nghttp2_hd_deflate_hd2()` instead.\n *\n * Deflates the |nva|, which has the |nvlen| name/value pairs, into\n * the |buf| of length |buflen|.\n *\n * If |buf| is not large enough to store the deflated header block,\n * this function fails with\n * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`.  The caller\n * should use `nghttp2_hd_deflate_bound()` to know the upper bound of\n * buffer size required to deflate given header name/value pairs.\n *\n * Once this function fails, subsequent call of this function always\n * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`.\n *\n * After this function returns, it is safe to delete the |nva|.\n *\n * This function returns the number of bytes written to |buf| if it\n * succeeds, or one of the following negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`\n *     Deflation process has failed.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`\n *     The provided |buflen| size is too small to hold the output.\n */\nNGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater,\n                                             uint8_t *buf, size_t buflen,\n                                             const nghttp2_nv *nva,\n                                             size_t nvlen);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @function\n *\n * Deflates the |nva|, which has the |nvlen| name/value pairs, into\n * the |buf| of length |buflen|.\n *\n * If |buf| is not large enough to store the deflated header block,\n * this function fails with\n * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`.  The caller\n * should use `nghttp2_hd_deflate_bound()` to know the upper bound of\n * buffer size required to deflate given header name/value pairs.\n *\n * Once this function fails, subsequent call of this function always\n * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`.\n *\n * After this function returns, it is safe to delete the |nva|.\n *\n * This function returns the number of bytes written to |buf| if it\n * succeeds, or one of the following negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`\n *     Deflation process has failed.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`\n *     The provided |buflen| size is too small to hold the output.\n */\nNGHTTP2_EXTERN nghttp2_ssize\nnghttp2_hd_deflate_hd2(nghttp2_hd_deflater *deflater, uint8_t *buf,\n                       size_t buflen, const nghttp2_nv *nva, size_t nvlen);\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  Use `nghttp2_hd_deflate_hd_vec2()` instead.\n *\n * Deflates the |nva|, which has the |nvlen| name/value pairs, into\n * the |veclen| size of buf vector |vec|.  The each size of buffer\n * must be set in len field of :type:`nghttp2_vec`.  If and only if\n * one chunk is filled up completely, next chunk will be used.  If\n * |vec| is not large enough to store the deflated header block, this\n * function fails with\n * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`.  The caller\n * should use `nghttp2_hd_deflate_bound()` to know the upper bound of\n * buffer size required to deflate given header name/value pairs.\n *\n * Once this function fails, subsequent call of this function always\n * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`.\n *\n * After this function returns, it is safe to delete the |nva|.\n *\n * This function returns the number of bytes written to |vec| if it\n * succeeds, or one of the following negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`\n *     Deflation process has failed.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`\n *     The provided |buflen| size is too small to hold the output.\n */\nNGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater,\n                                                 const nghttp2_vec *vec,\n                                                 size_t veclen,\n                                                 const nghttp2_nv *nva,\n                                                 size_t nvlen);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @function\n *\n * Deflates the |nva|, which has the |nvlen| name/value pairs, into\n * the |veclen| size of buf vector |vec|.  The each size of buffer\n * must be set in len field of :type:`nghttp2_vec`.  If and only if\n * one chunk is filled up completely, next chunk will be used.  If\n * |vec| is not large enough to store the deflated header block, this\n * function fails with\n * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`.  The caller\n * should use `nghttp2_hd_deflate_bound()` to know the upper bound of\n * buffer size required to deflate given header name/value pairs.\n *\n * Once this function fails, subsequent call of this function always\n * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`.\n *\n * After this function returns, it is safe to delete the |nva|.\n *\n * This function returns the number of bytes written to |vec| if it\n * succeeds, or one of the following negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`\n *     Deflation process has failed.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`\n *     The provided |buflen| size is too small to hold the output.\n */\nNGHTTP2_EXTERN nghttp2_ssize nghttp2_hd_deflate_hd_vec2(\n  nghttp2_hd_deflater *deflater, const nghttp2_vec *vec, size_t veclen,\n  const nghttp2_nv *nva, size_t nvlen);\n\n/**\n * @function\n *\n * Returns an upper bound on the compressed size after deflation of\n * |nva| of length |nvlen|.\n */\nNGHTTP2_EXTERN size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,\n                                               const nghttp2_nv *nva,\n                                               size_t nvlen);\n\n/**\n * @function\n *\n * Returns the number of entries that header table of |deflater|\n * contains.  This is the sum of the number of static table and\n * dynamic table, so the return value is at least 61.\n */\nNGHTTP2_EXTERN\nsize_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater);\n\n/**\n * @function\n *\n * Returns the table entry denoted by |idx| from header table of\n * |deflater|.  The |idx| is 1-based, and idx=1 returns first entry of\n * static table.  idx=62 returns first entry of dynamic table if it\n * exists.  Specifying idx=0 is error, and this function returns NULL.\n * If |idx| is strictly greater than the number of entries the tables\n * contain, this function returns NULL.\n */\nNGHTTP2_EXTERN\nconst nghttp2_nv *\nnghttp2_hd_deflate_get_table_entry(nghttp2_hd_deflater *deflater, size_t idx);\n\n/**\n * @function\n *\n * Returns the used dynamic table size, including the overhead 32\n * bytes per entry described in RFC 7541.\n */\nNGHTTP2_EXTERN\nsize_t nghttp2_hd_deflate_get_dynamic_table_size(nghttp2_hd_deflater *deflater);\n\n/**\n * @function\n *\n * Returns the maximum dynamic table size.\n */\nNGHTTP2_EXTERN\nsize_t\nnghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater);\n\nstruct nghttp2_hd_inflater;\n\n/**\n * @struct\n *\n * HPACK inflater object.\n */\ntypedef struct nghttp2_hd_inflater nghttp2_hd_inflater;\n\n/**\n * @function\n *\n * Initializes |*inflater_ptr| for inflating name/values pairs.\n *\n * If this function fails, |*inflater_ptr| is left untouched.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nNGHTTP2_EXTERN int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr);\n\n/**\n * @function\n *\n * Like `nghttp2_hd_inflate_new()`, but with additional custom memory\n * allocator specified in the |mem|.\n *\n * The |mem| can be ``NULL`` and the call is equivalent to\n * `nghttp2_hd_inflate_new()`.\n *\n * This function does not take ownership |mem|.  The application is\n * responsible for freeing |mem|.\n *\n * The library code does not refer to |mem| pointer after this\n * function returns, so the application can safely free it.\n */\nNGHTTP2_EXTERN int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr,\n                                           nghttp2_mem *mem);\n\n/**\n * @function\n *\n * Deallocates any resources allocated for |inflater|.\n */\nNGHTTP2_EXTERN void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater);\n\n/**\n * @function\n *\n * Changes header table size in the |inflater|.  This may trigger\n * eviction in the dynamic table.\n *\n * The |settings_max_dynamic_table_size| should be the value\n * transmitted in SETTINGS_HEADER_TABLE_SIZE.\n *\n * This function must not be called while header block is being\n * inflated.  In other words, this function must be called after\n * initialization of |inflater|, but before calling\n * `nghttp2_hd_inflate_hd3()`, or after\n * `nghttp2_hd_inflate_end_headers()`.  Otherwise,\n * `NGHTTP2_ERR_INVALID_STATE` was returned.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`\n *     The function is called while header block is being inflated.\n *     Probably, application missed to call\n *     `nghttp2_hd_inflate_end_headers()`.\n */\nNGHTTP2_EXTERN int\nnghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater,\n                                     size_t settings_max_dynamic_table_size);\n\n/**\n * @enum\n *\n * The flags for header inflation.\n */\ntypedef enum {\n  /**\n   * No flag set.\n   */\n  NGHTTP2_HD_INFLATE_NONE = 0,\n  /**\n   * Indicates all headers were inflated.\n   */\n  NGHTTP2_HD_INFLATE_FINAL = 0x01,\n  /**\n   * Indicates a header was emitted.\n   */\n  NGHTTP2_HD_INFLATE_EMIT = 0x02\n} nghttp2_hd_inflate_flag;\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  Use `nghttp2_hd_inflate_hd2()` instead.\n *\n * Inflates name/value block stored in |in| with length |inlen|.  This\n * function performs decompression.  For each successful emission of\n * header name/value pair,\n * :enum:`nghttp2_hd_inflate_flag.NGHTTP2_HD_INFLATE_EMIT` is set in\n * |*inflate_flags| and name/value pair is assigned to the |nv_out|\n * and the function returns.  The caller must not free the members of\n * |nv_out|.\n *\n * The |nv_out| may include pointers to the memory region in the |in|.\n * The caller must retain the |in| while the |nv_out| is used.\n *\n * The application should call this function repeatedly until the\n * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and\n * return value is non-negative.  This means the all input values are\n * processed successfully.  Then the application must call\n * `nghttp2_hd_inflate_end_headers()` to prepare for the next header\n * block input.\n *\n * The caller can feed complete compressed header block.  It also can\n * feed it in several chunks.  The caller must set |in_final| to\n * nonzero if the given input is the last block of the compressed\n * header.\n *\n * This function returns the number of bytes processed if it succeeds,\n * or one of the following negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`\n *     Inflation process has failed.\n * :enum:`nghttp2_error.NGHTTP2_ERR_BUFFER_ERROR`\n *     The header field name or value is too large.\n *\n * Example follows::\n *\n *     int inflate_header_block(nghttp2_hd_inflater *hd_inflater,\n *                              uint8_t *in, size_t inlen, int final)\n *     {\n *         ssize_t rv;\n *\n *         for(;;) {\n *             nghttp2_nv nv;\n *             int inflate_flags = 0;\n *\n *             rv = nghttp2_hd_inflate_hd(hd_inflater, &nv, &inflate_flags,\n *                                        in, inlen, final);\n *\n *             if(rv < 0) {\n *                 fprintf(stderr, \"inflate failed with error code %zd\", rv);\n *                 return -1;\n *             }\n *\n *             in += rv;\n *             inlen -= rv;\n *\n *             if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {\n *                 fwrite(nv.name, nv.namelen, 1, stderr);\n *                 fprintf(stderr, \": \");\n *                 fwrite(nv.value, nv.valuelen, 1, stderr);\n *                 fprintf(stderr, \"\\n\");\n *             }\n *             if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {\n *                 nghttp2_hd_inflate_end_headers(hd_inflater);\n *                 break;\n *             }\n *             if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 &&\n *                inlen == 0) {\n *                break;\n *             }\n *         }\n *\n *         return 0;\n *     }\n *\n */\nNGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,\n                                             nghttp2_nv *nv_out,\n                                             int *inflate_flags, uint8_t *in,\n                                             size_t inlen, int in_final);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n#ifndef NGHTTP2_NO_SSIZE_T\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  Use `nghttp2_hd_inflate_hd3()` instead.\n *\n * Inflates name/value block stored in |in| with length |inlen|.  This\n * function performs decompression.  For each successful emission of\n * header name/value pair,\n * :enum:`nghttp2_hd_inflate_flag.NGHTTP2_HD_INFLATE_EMIT` is set in\n * |*inflate_flags| and name/value pair is assigned to the |nv_out|\n * and the function returns.  The caller must not free the members of\n * |nv_out|.\n *\n * The |nv_out| may include pointers to the memory region in the |in|.\n * The caller must retain the |in| while the |nv_out| is used.\n *\n * The application should call this function repeatedly until the\n * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and\n * return value is non-negative.  If that happens, all given input\n * data (|inlen| bytes) are processed successfully.  Then the\n * application must call `nghttp2_hd_inflate_end_headers()` to prepare\n * for the next header block input.\n *\n * In other words, if |in_final| is nonzero, and this function returns\n * |inlen|, you can assert that\n * :enum:`nghttp2_hd_inflate_final.NGHTTP2_HD_INFLATE_FINAL` is set in\n * |*inflate_flags|.\n *\n * The caller can feed complete compressed header block.  It also can\n * feed it in several chunks.  The caller must set |in_final| to\n * nonzero if the given input is the last block of the compressed\n * header.\n *\n * This function returns the number of bytes processed if it succeeds,\n * or one of the following negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`\n *     Inflation process has failed.\n * :enum:`nghttp2_error.NGHTTP2_ERR_BUFFER_ERROR`\n *     The header field name or value is too large.\n *\n * Example follows::\n *\n *     int inflate_header_block(nghttp2_hd_inflater *hd_inflater,\n *                              uint8_t *in, size_t inlen, int final)\n *     {\n *         ssize_t rv;\n *\n *         for(;;) {\n *             nghttp2_nv nv;\n *             int inflate_flags = 0;\n *\n *             rv = nghttp2_hd_inflate_hd2(hd_inflater, &nv, &inflate_flags,\n *                                         in, inlen, final);\n *\n *             if(rv < 0) {\n *                 fprintf(stderr, \"inflate failed with error code %zd\", rv);\n *                 return -1;\n *             }\n *\n *             in += rv;\n *             inlen -= rv;\n *\n *             if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {\n *                 fwrite(nv.name, nv.namelen, 1, stderr);\n *                 fprintf(stderr, \": \");\n *                 fwrite(nv.value, nv.valuelen, 1, stderr);\n *                 fprintf(stderr, \"\\n\");\n *             }\n *             if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {\n *                 nghttp2_hd_inflate_end_headers(hd_inflater);\n *                 break;\n *             }\n *             if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 &&\n *                inlen == 0) {\n *                break;\n *             }\n *         }\n *\n *         return 0;\n *     }\n *\n */\nNGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,\n                                              nghttp2_nv *nv_out,\n                                              int *inflate_flags,\n                                              const uint8_t *in, size_t inlen,\n                                              int in_final);\n\n#endif /* NGHTTP2_NO_SSIZE_T */\n\n/**\n * @function\n *\n * Inflates name/value block stored in |in| with length |inlen|.  This\n * function performs decompression.  For each successful emission of\n * header name/value pair,\n * :enum:`nghttp2_hd_inflate_flag.NGHTTP2_HD_INFLATE_EMIT` is set in\n * |*inflate_flags| and name/value pair is assigned to the |nv_out|\n * and the function returns.  The caller must not free the members of\n * |nv_out|.\n *\n * The |nv_out| may include pointers to the memory region in the |in|.\n * The caller must retain the |in| while the |nv_out| is used.\n *\n * The application should call this function repeatedly until the\n * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and\n * return value is non-negative.  If that happens, all given input\n * data (|inlen| bytes) are processed successfully.  Then the\n * application must call `nghttp2_hd_inflate_end_headers()` to prepare\n * for the next header block input.\n *\n * In other words, if |in_final| is nonzero, and this function returns\n * |inlen|, you can assert that\n * :enum:`nghttp2_hd_inflate_final.NGHTTP2_HD_INFLATE_FINAL` is set in\n * |*inflate_flags|.\n *\n * The caller can feed complete compressed header block.  It also can\n * feed it in several chunks.  The caller must set |in_final| to\n * nonzero if the given input is the last block of the compressed\n * header.\n *\n * This function returns the number of bytes processed if it succeeds,\n * or one of the following negative error codes:\n *\n * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`\n *     Inflation process has failed.\n * :enum:`nghttp2_error.NGHTTP2_ERR_BUFFER_ERROR`\n *     The header field name or value is too large.\n *\n * Example follows::\n *\n *     int inflate_header_block(nghttp2_hd_inflater *hd_inflater,\n *                              uint8_t *in, size_t inlen, int final)\n *     {\n *         nghttp2_ssize rv;\n *\n *         for(;;) {\n *             nghttp2_nv nv;\n *             int inflate_flags = 0;\n *\n *             rv = nghttp2_hd_inflate_hd3(hd_inflater, &nv, &inflate_flags,\n *                                         in, inlen, final);\n *\n *             if(rv < 0) {\n *                 fprintf(stderr, \"inflate failed with error code %td\", rv);\n *                 return -1;\n *             }\n *\n *             in += rv;\n *             inlen -= rv;\n *\n *             if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {\n *                 fwrite(nv.name, nv.namelen, 1, stderr);\n *                 fprintf(stderr, \": \");\n *                 fwrite(nv.value, nv.valuelen, 1, stderr);\n *                 fprintf(stderr, \"\\n\");\n *             }\n *             if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {\n *                 nghttp2_hd_inflate_end_headers(hd_inflater);\n *                 break;\n *             }\n *             if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 &&\n *                inlen == 0) {\n *                break;\n *             }\n *         }\n *\n *         return 0;\n *     }\n *\n */\nNGHTTP2_EXTERN nghttp2_ssize nghttp2_hd_inflate_hd3(\n  nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags,\n  const uint8_t *in, size_t inlen, int in_final);\n\n/**\n * @function\n *\n * Signals the end of decompression for one header block.\n *\n * This function returns 0 if it succeeds. Currently this function\n * always succeeds.\n */\nNGHTTP2_EXTERN int\nnghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater);\n\n/**\n * @function\n *\n * Returns the number of entries that header table of |inflater|\n * contains.  This is the sum of the number of static table and\n * dynamic table, so the return value is at least 61.\n */\nNGHTTP2_EXTERN\nsize_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater);\n\n/**\n * @function\n *\n * Returns the table entry denoted by |idx| from header table of\n * |inflater|.  The |idx| is 1-based, and idx=1 returns first entry of\n * static table.  idx=62 returns first entry of dynamic table if it\n * exists.  Specifying idx=0 is error, and this function returns NULL.\n * If |idx| is strictly greater than the number of entries the tables\n * contain, this function returns NULL.\n */\nNGHTTP2_EXTERN\nconst nghttp2_nv *\nnghttp2_hd_inflate_get_table_entry(nghttp2_hd_inflater *inflater, size_t idx);\n\n/**\n * @function\n *\n * Returns the used dynamic table size, including the overhead 32\n * bytes per entry described in RFC 7541.\n */\nNGHTTP2_EXTERN\nsize_t nghttp2_hd_inflate_get_dynamic_table_size(nghttp2_hd_inflater *inflater);\n\n/**\n * @function\n *\n * Returns the maximum dynamic table size.\n */\nNGHTTP2_EXTERN\nsize_t\nnghttp2_hd_inflate_get_max_dynamic_table_size(nghttp2_hd_inflater *inflater);\n\nstruct nghttp2_stream;\n\n/**\n * @struct\n *\n * The structure to represent HTTP/2 stream.  The details of this\n * structure are intentionally hidden from the public API.\n */\ntypedef struct nghttp2_stream nghttp2_stream;\n\n/**\n * @function\n *\n * Returns pointer to :type:`nghttp2_stream` object denoted by\n * |stream_id|.  If stream was not found, returns NULL.\n *\n * Returns imaginary root stream (see\n * `nghttp2_session_get_root_stream()`) if 0 is given in |stream_id|.\n *\n * Unless |stream_id| == 0, the returned pointer is valid until next\n * call of `nghttp2_session_send()`, `nghttp2_session_mem_send2()`,\n * `nghttp2_session_recv()`, and `nghttp2_session_mem_recv2()`.\n */\nNGHTTP2_EXTERN nghttp2_stream *\nnghttp2_session_find_stream(nghttp2_session *session, int32_t stream_id);\n\n/**\n * @enum\n *\n * State of stream as described in RFC 7540.\n */\ntypedef enum {\n  /**\n   * idle state.\n   */\n  NGHTTP2_STREAM_STATE_IDLE = 1,\n  /**\n   * open state.\n   */\n  NGHTTP2_STREAM_STATE_OPEN,\n  /**\n   * reserved (local) state.\n   */\n  NGHTTP2_STREAM_STATE_RESERVED_LOCAL,\n  /**\n   * reserved (remote) state.\n   */\n  NGHTTP2_STREAM_STATE_RESERVED_REMOTE,\n  /**\n   * half closed (local) state.\n   */\n  NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL,\n  /**\n   * half closed (remote) state.\n   */\n  NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE,\n  /**\n   * closed state.\n   */\n  NGHTTP2_STREAM_STATE_CLOSED\n} nghttp2_stream_proto_state;\n\n/**\n * @function\n *\n * Returns state of |stream|.  The root stream retrieved by\n * `nghttp2_session_get_root_stream()` will have stream state\n * :enum:`nghttp2_stream_proto_state.NGHTTP2_STREAM_STATE_IDLE`.\n */\nNGHTTP2_EXTERN nghttp2_stream_proto_state\nnghttp2_stream_get_state(nghttp2_stream *stream);\n\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.\n *\n * Returns root of dependency tree, which is imaginary stream with\n * stream ID 0.  The returned pointer is valid until |session| is\n * freed by `nghttp2_session_del()`.\n */\nNGHTTP2_EXTERN nghttp2_stream *\nnghttp2_session_get_root_stream(nghttp2_session *session);\n\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.  In the future release after the end of\n *   2024, this function will always return NULL.\n *\n * Returns the parent stream of |stream| in dependency tree.  Returns\n * NULL if there is no such stream.\n */\nNGHTTP2_EXTERN nghttp2_stream *\nnghttp2_stream_get_parent(nghttp2_stream *stream);\n\nNGHTTP2_EXTERN int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream);\n\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.  In the future release after the end of\n *   2024, this function will always return NULL.\n *\n * Returns the next sibling stream of |stream| in dependency tree.\n * Returns NULL if there is no such stream.\n */\nNGHTTP2_EXTERN nghttp2_stream *\nnghttp2_stream_get_next_sibling(nghttp2_stream *stream);\n\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.  In the future release after the end of\n *   2024, this function will always return NULL.\n *\n * Returns the previous sibling stream of |stream| in dependency tree.\n * Returns NULL if there is no such stream.\n */\nNGHTTP2_EXTERN nghttp2_stream *\nnghttp2_stream_get_previous_sibling(nghttp2_stream *stream);\n\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.  In the future release after the end of\n *   2024, this function will always return NULL.\n *\n * Returns the first child stream of |stream| in dependency tree.\n * Returns NULL if there is no such stream.\n */\nNGHTTP2_EXTERN nghttp2_stream *\nnghttp2_stream_get_first_child(nghttp2_stream *stream);\n\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.  In the future release after the end of\n *   2024, this function will always return\n *   :macro:`NGHTTP2_DEFAULT_WEIGHT`.\n *\n * Returns dependency weight to the parent stream of |stream|.\n */\nNGHTTP2_EXTERN int32_t nghttp2_stream_get_weight(nghttp2_stream *stream);\n\n/**\n * @function\n *\n * .. warning::\n *\n *   Deprecated.  :rfc:`7540` priorities are deprecated by\n *   :rfc:`9113`.  Consider migrating to :rfc:`9218` extensible\n *   prioritization scheme.  In the future release after the end of\n *   2024, this function will always return 0.\n *\n * Returns the sum of the weight for |stream|'s children.\n */\nNGHTTP2_EXTERN int32_t\nnghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream);\n\n/**\n * @functypedef\n *\n * Callback function invoked when the library outputs debug logging.\n * The function is called with arguments suitable for ``vfprintf(3)``\n *\n * The debug output is only enabled if the library is built with\n * ``DEBUGBUILD`` macro defined.\n */\ntypedef void (*nghttp2_debug_vprintf_callback)(const char *format,\n                                               va_list args);\n\n/**\n * @function\n *\n * Sets a debug output callback called by the library when built with\n * ``DEBUGBUILD`` macro defined.  If this option is not used, debug\n * log is written into standard error output.\n *\n * For builds without ``DEBUGBUILD`` macro defined, this function is\n * noop.\n *\n * Note that building with ``DEBUGBUILD`` may cause significant\n * performance penalty to libnghttp2 because of extra processing.  It\n * should be used for debugging purpose only.\n *\n * .. Warning::\n *\n *   Building with ``DEBUGBUILD`` may cause significant performance\n *   penalty to libnghttp2 because of extra processing.  It should be\n *   used for debugging purpose only.  We write this two times because\n *   this is important.\n */\nNGHTTP2_EXTERN void nghttp2_set_debug_vprintf_callback(\n  nghttp2_debug_vprintf_callback debug_vprintf_callback);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* NGHTTP2_H */\n"
  },
  {
    "path": "thirdparty/nghttp2/nghttp2_buf.c",
    "content": "/*\n * nghttp2 - HTTP/2 C Library\n *\n * Copyright (c) 2014 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"nghttp2_buf.h\"\n\n#include <stdio.h>\n\nvoid nghttp2_buf_init(nghttp2_buf *buf) {\n  buf->begin = NULL;\n  buf->end = NULL;\n  buf->pos = NULL;\n  buf->last = NULL;\n  buf->mark = NULL;\n}\n\nint nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem) {\n  nghttp2_buf_init(buf);\n  return nghttp2_buf_reserve(buf, initial, mem);\n}\n\nvoid nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem) {\n  if (buf == NULL) {\n    return;\n  }\n\n  nghttp2_mem_free(mem, buf->begin);\n  buf->begin = NULL;\n}\n\nint nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem) {\n  uint8_t *ptr;\n  size_t cap;\n\n  cap = nghttp2_buf_cap(buf);\n\n  if (cap >= new_cap) {\n    return 0;\n  }\n\n  new_cap = nghttp2_max_size(new_cap, cap * 2);\n\n  ptr = nghttp2_mem_realloc(mem, buf->begin, new_cap);\n  if (ptr == NULL) {\n    return NGHTTP2_ERR_NOMEM;\n  }\n\n  buf->pos = ptr + (buf->pos - buf->begin);\n  buf->last = ptr + (buf->last - buf->begin);\n  buf->mark = ptr + (buf->mark - buf->begin);\n  buf->begin = ptr;\n  buf->end = ptr + new_cap;\n\n  return 0;\n}\n\nvoid nghttp2_buf_reset(nghttp2_buf *buf) {\n  buf->pos = buf->last = buf->mark = buf->begin;\n}\n\nvoid nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len) {\n  buf->begin = buf->pos = buf->last = buf->mark = buf->end = begin;\n  if (len) {\n    buf->end += len;\n  }\n}\n\nstatic int buf_chain_new(nghttp2_buf_chain **chain, size_t chunk_length,\n                         nghttp2_mem *mem) {\n  int rv;\n\n  *chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain));\n  if (*chain == NULL) {\n    return NGHTTP2_ERR_NOMEM;\n  }\n\n  (*chain)->next = NULL;\n\n  rv = nghttp2_buf_init2(&(*chain)->buf, chunk_length, mem);\n  if (rv != 0) {\n    nghttp2_mem_free(mem, *chain);\n    return NGHTTP2_ERR_NOMEM;\n  }\n\n  return 0;\n}\n\nstatic void buf_chain_del(nghttp2_buf_chain *chain, nghttp2_mem *mem) {\n  nghttp2_buf_free(&chain->buf, mem);\n  nghttp2_mem_free(mem, chain);\n}\n\nint nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk,\n                      nghttp2_mem *mem) {\n  return nghttp2_bufs_init2(bufs, chunk_length, max_chunk, 0, mem);\n}\n\nint nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length,\n                       size_t max_chunk, size_t offset, nghttp2_mem *mem) {\n  return nghttp2_bufs_init3(bufs, chunk_length, max_chunk, max_chunk, offset,\n                            mem);\n}\n\nint nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length,\n                       size_t max_chunk, size_t chunk_keep, size_t offset,\n                       nghttp2_mem *mem) {\n  int rv;\n  nghttp2_buf_chain *chain;\n\n  if (chunk_keep == 0 || max_chunk < chunk_keep || chunk_length < offset) {\n    return NGHTTP2_ERR_INVALID_ARGUMENT;\n  }\n\n  rv = buf_chain_new(&chain, chunk_length, mem);\n  if (rv != 0) {\n    return rv;\n  }\n\n  bufs->mem = mem;\n  bufs->offset = offset;\n\n  bufs->head = chain;\n  bufs->cur = bufs->head;\n\n  nghttp2_buf_shift_right(&bufs->cur->buf, offset);\n\n  bufs->chunk_length = chunk_length;\n  bufs->chunk_used = 1;\n  bufs->max_chunk = max_chunk;\n  bufs->chunk_keep = chunk_keep;\n\n  return 0;\n}\n\nint nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length) {\n  int rv;\n  nghttp2_buf_chain *chain;\n\n  if (chunk_length < bufs->offset) {\n    return NGHTTP2_ERR_INVALID_ARGUMENT;\n  }\n\n  rv = buf_chain_new(&chain, chunk_length, bufs->mem);\n  if (rv != 0) {\n    return rv;\n  }\n\n  nghttp2_bufs_free(bufs);\n\n  bufs->head = chain;\n  bufs->cur = bufs->head;\n\n  nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset);\n\n  bufs->chunk_length = chunk_length;\n  bufs->chunk_used = 1;\n\n  return 0;\n}\n\nvoid nghttp2_bufs_free(nghttp2_bufs *bufs) {\n  nghttp2_buf_chain *chain, *next_chain;\n\n  if (bufs == NULL) {\n    return;\n  }\n\n  for (chain = bufs->head; chain;) {\n    next_chain = chain->next;\n\n    buf_chain_del(chain, bufs->mem);\n\n    chain = next_chain;\n  }\n\n  bufs->head = NULL;\n}\n\nint nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len,\n                           nghttp2_mem *mem) {\n  nghttp2_buf_chain *chain;\n\n  chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain));\n  if (chain == NULL) {\n    return NGHTTP2_ERR_NOMEM;\n  }\n\n  chain->next = NULL;\n\n  nghttp2_buf_wrap_init(&chain->buf, begin, len);\n\n  bufs->mem = mem;\n  bufs->offset = 0;\n\n  bufs->head = chain;\n  bufs->cur = bufs->head;\n\n  bufs->chunk_length = len;\n  bufs->chunk_used = 1;\n  bufs->max_chunk = 1;\n  bufs->chunk_keep = 1;\n\n  return 0;\n}\n\nint nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, const nghttp2_vec *vec,\n                            size_t veclen, nghttp2_mem *mem) {\n  size_t i = 0;\n  nghttp2_buf_chain *cur_chain;\n  nghttp2_buf_chain *head_chain;\n  nghttp2_buf_chain **dst_chain = &head_chain;\n\n  if (veclen == 0) {\n    return nghttp2_bufs_wrap_init(bufs, NULL, 0, mem);\n  }\n\n  head_chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain) * veclen);\n  if (head_chain == NULL) {\n    return NGHTTP2_ERR_NOMEM;\n  }\n\n  for (i = 0; i < veclen; ++i) {\n    cur_chain = &head_chain[i];\n    cur_chain->next = NULL;\n    nghttp2_buf_wrap_init(&cur_chain->buf, vec[i].base, vec[i].len);\n\n    *dst_chain = cur_chain;\n    dst_chain = &cur_chain->next;\n  }\n\n  bufs->mem = mem;\n  bufs->offset = 0;\n\n  bufs->head = head_chain;\n  bufs->cur = bufs->head;\n\n  /* We don't use chunk_length since no allocation is expected. */\n  bufs->chunk_length = 0;\n  bufs->chunk_used = veclen;\n  bufs->max_chunk = veclen;\n  bufs->chunk_keep = veclen;\n\n  return 0;\n}\n\nvoid nghttp2_bufs_wrap_free(nghttp2_bufs *bufs) {\n  if (bufs == NULL) {\n    return;\n  }\n\n  if (bufs->head) {\n    nghttp2_mem_free(bufs->mem, bufs->head);\n  }\n}\n\nvoid nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs) {\n  nghttp2_buf_chain *ci;\n\n  for (ci = bufs->cur; ci; ci = ci->next) {\n    if (nghttp2_buf_len(&ci->buf) == 0) {\n      return;\n    } else {\n      bufs->cur = ci;\n    }\n  }\n}\n\nsize_t nghttp2_bufs_len(nghttp2_bufs *bufs) {\n  nghttp2_buf_chain *ci;\n  size_t len;\n\n  len = 0;\n  for (ci = bufs->head; ci; ci = ci->next) {\n    len += nghttp2_buf_len(&ci->buf);\n  }\n\n  return len;\n}\n\nstatic int bufs_alloc_chain(nghttp2_bufs *bufs) {\n  int rv;\n  nghttp2_buf_chain *chain;\n\n  if (bufs->cur->next) {\n    bufs->cur = bufs->cur->next;\n\n    return 0;\n  }\n\n  if (bufs->max_chunk == bufs->chunk_used) {\n    return NGHTTP2_ERR_BUFFER_ERROR;\n  }\n\n  rv = buf_chain_new(&chain, bufs->chunk_length, bufs->mem);\n  if (rv != 0) {\n    return rv;\n  }\n\n  DEBUGF(\"new buffer %zu bytes allocated for bufs %p, used %zu\\n\",\n         bufs->chunk_length, bufs, bufs->chunk_used);\n\n  ++bufs->chunk_used;\n\n  bufs->cur->next = chain;\n  bufs->cur = chain;\n\n  nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset);\n\n  return 0;\n}\n\nint nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) {\n  int rv;\n  size_t nwrite;\n  nghttp2_buf *buf;\n  const uint8_t *p;\n\n  p = data;\n\n  while (len) {\n    buf = &bufs->cur->buf;\n\n    nwrite = nghttp2_min_size(nghttp2_buf_avail(buf), len);\n    if (nwrite == 0) {\n      rv = bufs_alloc_chain(bufs);\n      if (rv != 0) {\n        return rv;\n      }\n      continue;\n    }\n\n    buf->last = nghttp2_cpymem(buf->last, p, nwrite);\n    p += nwrite;\n    len -= nwrite;\n  }\n\n  return 0;\n}\n\nstatic int bufs_ensure_addb(nghttp2_bufs *bufs) {\n  int rv;\n  nghttp2_buf *buf;\n\n  buf = &bufs->cur->buf;\n\n  if (nghttp2_buf_avail(buf) > 0) {\n    return 0;\n  }\n\n  rv = bufs_alloc_chain(bufs);\n  if (rv != 0) {\n    return rv;\n  }\n\n  return 0;\n}\n\nint nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b) {\n  int rv;\n\n  rv = bufs_ensure_addb(bufs);\n  if (rv != 0) {\n    return rv;\n  }\n\n  *bufs->cur->buf.last++ = b;\n\n  return 0;\n}\n\nint nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b) {\n  int rv;\n\n  rv = bufs_ensure_addb(bufs);\n  if (rv != 0) {\n    return rv;\n  }\n\n  *bufs->cur->buf.last = b;\n\n  return 0;\n}\n\nint nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b) {\n  int rv;\n\n  rv = bufs_ensure_addb(bufs);\n  if (rv != 0) {\n    return rv;\n  }\n\n  *bufs->cur->buf.last++ |= b;\n\n  return 0;\n}\n\nint nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b) {\n  int rv;\n\n  rv = bufs_ensure_addb(bufs);\n  if (rv != 0) {\n    return rv;\n  }\n\n  *bufs->cur->buf.last |= b;\n\n  return 0;\n}\n\nnghttp2_ssize nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) {\n  size_t len;\n  nghttp2_buf_chain *chain;\n  nghttp2_buf *buf;\n  uint8_t *res;\n  nghttp2_buf resbuf;\n\n  len = 0;\n\n  for (chain = bufs->head; chain; chain = chain->next) {\n    len += nghttp2_buf_len(&chain->buf);\n  }\n\n  if (len == 0) {\n    res = NULL;\n    return 0;\n  }\n\n  res = nghttp2_mem_malloc(bufs->mem, len);\n  if (res == NULL) {\n    return NGHTTP2_ERR_NOMEM;\n  }\n\n  nghttp2_buf_wrap_init(&resbuf, res, len);\n\n  for (chain = bufs->head; chain; chain = chain->next) {\n    buf = &chain->buf;\n    resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));\n  }\n\n  *out = res;\n\n  return (nghttp2_ssize)len;\n}\n\nsize_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out) {\n  size_t len;\n  nghttp2_buf_chain *chain;\n  nghttp2_buf *buf;\n  nghttp2_buf resbuf;\n\n  len = nghttp2_bufs_len(bufs);\n\n  nghttp2_buf_wrap_init(&resbuf, out, len);\n\n  for (chain = bufs->head; chain; chain = chain->next) {\n    buf = &chain->buf;\n    resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf));\n  }\n\n  return len;\n}\n\nvoid nghttp2_bufs_reset(nghttp2_bufs *bufs) {\n  nghttp2_buf_chain *chain, *ci;\n  size_t k;\n\n  k = bufs->chunk_keep;\n\n  for (ci = bufs->head; ci; ci = ci->next) {\n    nghttp2_buf_reset(&ci->buf);\n    nghttp2_buf_shift_right(&ci->buf, bufs->offset);\n\n    if (--k == 0) {\n      break;\n    }\n  }\n\n  if (ci) {\n    chain = ci->next;\n    ci->next = NULL;\n\n    for (ci = chain; ci;) {\n      chain = ci->next;\n\n      buf_chain_del(ci, bufs->mem);\n\n      ci = chain;\n    }\n\n    bufs->chunk_used = bufs->chunk_keep;\n  }\n\n  bufs->cur = bufs->head;\n}\n\nint nghttp2_bufs_advance(nghttp2_bufs *bufs) { return bufs_alloc_chain(bufs); }\n\nint nghttp2_bufs_next_present(nghttp2_bufs *bufs) {\n  nghttp2_buf_chain *chain;\n\n  chain = bufs->cur->next;\n\n  return chain && nghttp2_buf_len(&chain->buf);\n}\n"
  },
  {
    "path": "thirdparty/nghttp2/nghttp2_buf.h",
    "content": "/*\n * nghttp2 - HTTP/2 C Library\n *\n * Copyright (c) 2014 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef NGHTTP2_BUF_H\n#define NGHTTP2_BUF_H\n\n#include \"nghttp2.h\"\n\n#include \"nghttp2_mem.h\"\n\ntypedef struct {\n  /* This points to the beginning of the buffer. The effective range\n     of buffer is [begin, end). */\n  uint8_t *begin;\n  /* This points to the memory one byte beyond the end of the\n     buffer. */\n  uint8_t *end;\n  /* The position indicator for effective start of the buffer. pos <=\n     last must be hold. */\n  uint8_t *pos;\n  /* The position indicator for effective one beyond of the end of the\n     buffer. last <= end must be hold. */\n  uint8_t *last;\n  /* Mark arbitrary position in buffer [begin, end) */\n  uint8_t *mark;\n} nghttp2_buf;\n\n#define nghttp2_buf_len(BUF) ((size_t)((BUF)->last - (BUF)->pos))\n#define nghttp2_buf_avail(BUF) ((size_t)((BUF)->end - (BUF)->last))\n#define nghttp2_buf_mark_avail(BUF) ((size_t)((BUF)->mark - (BUF)->last))\n#define nghttp2_buf_cap(BUF) ((size_t)((BUF)->end - (BUF)->begin))\n\n#define nghttp2_buf_pos_offset(BUF) ((size_t)((BUF)->pos - (BUF)->begin))\n#define nghttp2_buf_last_offset(BUF) ((size_t)((BUF)->last - (BUF)->begin))\n\n#define nghttp2_buf_shift_right(BUF, AMT)                                      \\\n  do {                                                                         \\\n    (BUF)->pos += AMT;                                                         \\\n    (BUF)->last += AMT;                                                        \\\n  } while (0)\n\n#define nghttp2_buf_shift_left(BUF, AMT)                                       \\\n  do {                                                                         \\\n    (BUF)->pos -= AMT;                                                         \\\n    (BUF)->last -= AMT;                                                        \\\n  } while (0)\n\n/*\n * Initializes the |buf|. No memory is allocated in this function. Use\n * nghttp2_buf_reserve() to allocate memory.\n */\nvoid nghttp2_buf_init(nghttp2_buf *buf);\n\n/*\n * Initializes the |buf| and allocates at least |initial| bytes of\n * memory.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *     Out of memory\n */\nint nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem);\n\n/*\n * Frees buffer in |buf|.\n */\nvoid nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem);\n\n/*\n * Extends buffer so that nghttp2_buf_cap() returns at least\n * |new_cap|. If extensions took place, buffer pointers in |buf| will\n * change.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *     Out of memory\n */\nint nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem);\n\n/*\n * Resets pos, last, mark member of |buf| to buf->begin.\n */\nvoid nghttp2_buf_reset(nghttp2_buf *buf);\n\n/*\n * Initializes |buf| using supplied buffer |begin| of length\n * |len|. Semantically, the application should not call *_reserve() or\n * nghttp2_free() functions for |buf|.\n */\nvoid nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len);\n\nstruct nghttp2_buf_chain;\n\ntypedef struct nghttp2_buf_chain nghttp2_buf_chain;\n\n/* Chains 2 buffers */\nstruct nghttp2_buf_chain {\n  /* Points to the subsequent buffer. NULL if there is no such\n     buffer. */\n  nghttp2_buf_chain *next;\n  nghttp2_buf buf;\n};\n\ntypedef struct {\n  /* Points to the first buffer */\n  nghttp2_buf_chain *head;\n  /* Buffer pointer where write occurs. */\n  nghttp2_buf_chain *cur;\n  /* Memory allocator */\n  nghttp2_mem *mem;\n  /* The buffer capacity of each buf.  This field may be 0 if\n     nghttp2_bufs is initialized by nghttp2_bufs_wrap_init* family\n     functions. */\n  size_t chunk_length;\n  /* The maximum number of nghttp2_buf_chain */\n  size_t max_chunk;\n  /* The number of nghttp2_buf_chain allocated */\n  size_t chunk_used;\n  /* The number of nghttp2_buf_chain to keep on reset */\n  size_t chunk_keep;\n  /* pos offset from begin in each buffers. On initialization and\n     reset, buf->pos and buf->last are positioned at buf->begin +\n     offset. */\n  size_t offset;\n} nghttp2_bufs;\n\n/*\n * This is the same as calling nghttp2_bufs_init2 with the given\n * arguments and offset = 0.\n */\nint nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk,\n                      nghttp2_mem *mem);\n\n/*\n * This is the same as calling nghttp2_bufs_init3 with the given\n * arguments and chunk_keep = max_chunk.\n */\nint nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length,\n                       size_t max_chunk, size_t offset, nghttp2_mem *mem);\n\n/*\n * Initializes |bufs|. Each buffer size is given in the\n * |chunk_length|.  The maximum number of buffers is given in the\n * |max_chunk|.  On reset, first |chunk_keep| buffers are kept and\n * remaining buffers are deleted.  Each buffer will have bufs->pos and\n * bufs->last shifted to left by |offset| bytes on creation and reset.\n *\n * This function allocates first buffer.  bufs->head and bufs->cur\n * will point to the first buffer after this call.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *     Out of memory.\n * NGHTTP2_ERR_INVALID_ARGUMENT\n *     chunk_keep is 0; or max_chunk < chunk_keep; or offset is too\n *     long.\n */\nint nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length,\n                       size_t max_chunk, size_t chunk_keep, size_t offset,\n                       nghttp2_mem *mem);\n\n/*\n * Frees any related resources to the |bufs|.\n */\nvoid nghttp2_bufs_free(nghttp2_bufs *bufs);\n\n/*\n * Initializes |bufs| using supplied buffer |begin| of length |len|.\n * The first buffer bufs->head uses buffer |begin|.  The buffer size\n * is fixed and no extra chunk buffer is allocated.  In other\n * words, max_chunk = chunk_keep = 1.  To free the resource allocated\n * for |bufs|, use nghttp2_bufs_wrap_free().\n *\n * Don't use the function which performs allocation, such as\n * nghttp2_bufs_realloc().\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *     Out of memory.\n */\nint nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len,\n                           nghttp2_mem *mem);\n\n/*\n * Initializes |bufs| using supplied |veclen| size of buf vector\n * |vec|.  The number of buffers is fixed and no extra chunk buffer is\n * allocated.  In other words, max_chunk = chunk_keep = |in_len|.  To\n * free the resource allocated for |bufs|, use\n * nghttp2_bufs_wrap_free().\n *\n * Don't use the function which performs allocation, such as\n * nghttp2_bufs_realloc().\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *     Out of memory.\n */\nint nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, const nghttp2_vec *vec,\n                            size_t veclen, nghttp2_mem *mem);\n\n/*\n * Frees any related resource to the |bufs|.  This function does not\n * free supplied buffer provided in nghttp2_bufs_wrap_init().\n */\nvoid nghttp2_bufs_wrap_free(nghttp2_bufs *bufs);\n\n/*\n * Reallocates internal buffer using |chunk_length|.  The max_chunk,\n * chunk_keep and offset do not change.  After successful allocation\n * of new buffer, previous buffers are deallocated without copying\n * anything into new buffers.  chunk_used is reset to 1.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *     Out of memory.\n * NGHTTP2_ERR_INVALID_ARGUMENT\n *     chunk_length < offset\n */\nint nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length);\n\n/*\n * Appends the |data| of length |len| to the |bufs|. The write starts\n * at bufs->cur->buf.last. A new buffers will be allocated to store\n * all data.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *     Out of memory.\n * NGHTTP2_ERR_BUFFER_ERROR\n *     Out of buffer space.\n */\nint nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len);\n\n/*\n * Appends a single byte |b| to the |bufs|. The write starts at\n * bufs->cur->buf.last. A new buffers will be allocated to store all\n * data.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *     Out of memory.\n * NGHTTP2_ERR_BUFFER_ERROR\n *     Out of buffer space.\n */\nint nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b);\n\n/*\n * Behaves like nghttp2_bufs_addb(), but this does not update\n * buf->last pointer.\n */\nint nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b);\n\n#define nghttp2_bufs_fast_addb(BUFS, B)                                        \\\n  do {                                                                         \\\n    *(BUFS)->cur->buf.last++ = B;                                              \\\n  } while (0)\n\n#define nghttp2_bufs_fast_addb_hold(BUFS, B)                                   \\\n  do {                                                                         \\\n    *(BUFS)->cur->buf.last = B;                                                \\\n  } while (0)\n\n/*\n * Performs bitwise-OR of |b| at bufs->cur->buf.last. A new buffers\n * will be allocated if necessary.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *     Out of memory.\n * NGHTTP2_ERR_BUFFER_ERROR\n *     Out of buffer space.\n */\nint nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b);\n\n/*\n * Behaves like nghttp2_bufs_orb(), but does not update buf->last\n * pointer.\n */\nint nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b);\n\n#define nghttp2_bufs_fast_orb(BUFS, B)                                         \\\n  do {                                                                         \\\n    uint8_t **p = &(BUFS)->cur->buf.last;                                      \\\n    **p = (uint8_t)(**p | (B));                                                \\\n    ++(*p);                                                                    \\\n  } while (0)\n\n#define nghttp2_bufs_fast_orb_hold(BUFS, B)                                    \\\n  do {                                                                         \\\n    uint8_t *p = (BUFS)->cur->buf.last;                                        \\\n    *p = (uint8_t)(*p | (B));                                                  \\\n  } while (0)\n\n/*\n * Copies all data stored in |bufs| to the contiguous buffer.  This\n * function allocates the contiguous memory to store all data in\n * |bufs| and assigns it to |*out|.\n *\n * The contents of |bufs| is left unchanged.\n *\n * This function returns the length of copied data and assigns the\n * pointer to copied data to |*out| if it succeeds, or one of the\n * following negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *     Out of memory\n */\nnghttp2_ssize nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out);\n\n/*\n * Copies all data stored in |bufs| to |out|.  This function assumes\n * that the buffer space pointed by |out| has at least\n * nghttp2_bufs(bufs) bytes.\n *\n * The contents of |bufs| is left unchanged.\n *\n * This function returns the length of copied data.\n */\nsize_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out);\n\n/*\n * Resets |bufs| and makes the buffers empty.\n */\nvoid nghttp2_bufs_reset(nghttp2_bufs *bufs);\n\n/*\n * Moves bufs->cur to bufs->cur->next.  If resulting bufs->cur is\n * NULL, this function allocates new buffers and bufs->cur points to\n * it.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *     Out of memory\n * NGHTTP2_ERR_BUFFER_ERROR\n *     Out of buffer space.\n */\nint nghttp2_bufs_advance(nghttp2_bufs *bufs);\n\n/* Sets bufs->cur to bufs->head */\n#define nghttp2_bufs_rewind(BUFS)                                              \\\n  do {                                                                         \\\n    (BUFS)->cur = (BUFS)->head;                                                \\\n  } while (0)\n\n/*\n * Move bufs->cur, from the current position, using next member, to\n * the last buf which has nghttp2_buf_len(buf) > 0 without seeing buf\n * which satisfies nghttp2_buf_len(buf) == 0.  If\n * nghttp2_buf_len(&bufs->cur->buf) == 0 or bufs->cur->next is NULL,\n * bufs->cur is unchanged.\n */\nvoid nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs);\n\n/*\n * Returns nonzero if bufs->cur->next is not empty.\n */\nint nghttp2_bufs_next_present(nghttp2_bufs *bufs);\n\n#define nghttp2_bufs_cur_avail(BUFS) nghttp2_buf_avail(&(BUFS)->cur->buf)\n\n/*\n * Returns the total buffer length of |bufs|.\n */\nsize_t nghttp2_bufs_len(nghttp2_bufs *bufs);\n\n#endif /* NGHTTP2_BUF_H */\n"
  },
  {
    "path": "thirdparty/nghttp2/nghttp2_hd.c",
    "content": "/*\n * nghttp2 - HTTP/2 C Library\n *\n * Copyright (c) 2013 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"nghttp2_hd.h\"\n\n#include <string.h>\n#include <assert.h>\n#include <stdio.h>\n\n/* Make scalar initialization form of nghttp2_hd_entry */\n#define MAKE_STATIC_ENT(N, V, T, H)                                            \\\n  {                                                                            \\\n    {NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1},                         \\\n    {NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1},                         \\\n    {(uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0},     \\\n    T,                                                                         \\\n    H,                                                                         \\\n  }\n\n/* Generated by mkstatictbl.py */\n/* 3rd parameter is nghttp2_token value for header field name.  We use\n   first enum value if same header names are repeated (e.g.,\n   :status). */\nstatic const nghttp2_hd_static_entry static_table[] = {\n  MAKE_STATIC_ENT(\":authority\", \"\", 0, 3153725150u),\n  MAKE_STATIC_ENT(\":method\", \"GET\", 1, 695666056u),\n  MAKE_STATIC_ENT(\":method\", \"POST\", 1, 695666056u),\n  MAKE_STATIC_ENT(\":path\", \"/\", 3, 3292848686u),\n  MAKE_STATIC_ENT(\":path\", \"/index.html\", 3, 3292848686u),\n  MAKE_STATIC_ENT(\":scheme\", \"http\", 5, 2510477674u),\n  MAKE_STATIC_ENT(\":scheme\", \"https\", 5, 2510477674u),\n  MAKE_STATIC_ENT(\":status\", \"200\", 7, 4000288983u),\n  MAKE_STATIC_ENT(\":status\", \"204\", 7, 4000288983u),\n  MAKE_STATIC_ENT(\":status\", \"206\", 7, 4000288983u),\n  MAKE_STATIC_ENT(\":status\", \"304\", 7, 4000288983u),\n  MAKE_STATIC_ENT(\":status\", \"400\", 7, 4000288983u),\n  MAKE_STATIC_ENT(\":status\", \"404\", 7, 4000288983u),\n  MAKE_STATIC_ENT(\":status\", \"500\", 7, 4000288983u),\n  MAKE_STATIC_ENT(\"accept-charset\", \"\", 14, 3664010344u),\n  MAKE_STATIC_ENT(\"accept-encoding\", \"gzip, deflate\", 15, 3379649177u),\n  MAKE_STATIC_ENT(\"accept-language\", \"\", 16, 1979086614u),\n  MAKE_STATIC_ENT(\"accept-ranges\", \"\", 17, 1713753958u),\n  MAKE_STATIC_ENT(\"accept\", \"\", 18, 136609321u),\n  MAKE_STATIC_ENT(\"access-control-allow-origin\", \"\", 19, 2710797292u),\n  MAKE_STATIC_ENT(\"age\", \"\", 20, 742476188u),\n  MAKE_STATIC_ENT(\"allow\", \"\", 21, 2930878514u),\n  MAKE_STATIC_ENT(\"authorization\", \"\", 22, 2436257726u),\n  MAKE_STATIC_ENT(\"cache-control\", \"\", 23, 1355326669u),\n  MAKE_STATIC_ENT(\"content-disposition\", \"\", 24, 3889184348u),\n  MAKE_STATIC_ENT(\"content-encoding\", \"\", 25, 65203592u),\n  MAKE_STATIC_ENT(\"content-language\", \"\", 26, 24973587u),\n  MAKE_STATIC_ENT(\"content-length\", \"\", 27, 1308181789u),\n  MAKE_STATIC_ENT(\"content-location\", \"\", 28, 2302364718u),\n  MAKE_STATIC_ENT(\"content-range\", \"\", 29, 3555523146u),\n  MAKE_STATIC_ENT(\"content-type\", \"\", 30, 4244048277u),\n  MAKE_STATIC_ENT(\"cookie\", \"\", 31, 2007449791u),\n  MAKE_STATIC_ENT(\"date\", \"\", 32, 3564297305u),\n  MAKE_STATIC_ENT(\"etag\", \"\", 33, 113792960u),\n  MAKE_STATIC_ENT(\"expect\", \"\", 34, 2530896728u),\n  MAKE_STATIC_ENT(\"expires\", \"\", 35, 1049544579u),\n  MAKE_STATIC_ENT(\"from\", \"\", 36, 2513272949u),\n  MAKE_STATIC_ENT(\"host\", \"\", 37, 2952701295u),\n  MAKE_STATIC_ENT(\"if-match\", \"\", 38, 3597694698u),\n  MAKE_STATIC_ENT(\"if-modified-since\", \"\", 39, 2213050793u),\n  MAKE_STATIC_ENT(\"if-none-match\", \"\", 40, 2536202615u),\n  MAKE_STATIC_ENT(\"if-range\", \"\", 41, 2340978238u),\n  MAKE_STATIC_ENT(\"if-unmodified-since\", \"\", 42, 3794814858u),\n  MAKE_STATIC_ENT(\"last-modified\", \"\", 43, 3226950251u),\n  MAKE_STATIC_ENT(\"link\", \"\", 44, 232457833u),\n  MAKE_STATIC_ENT(\"location\", \"\", 45, 200649126u),\n  MAKE_STATIC_ENT(\"max-forwards\", \"\", 46, 1826162134u),\n  MAKE_STATIC_ENT(\"proxy-authenticate\", \"\", 47, 2709445359u),\n  MAKE_STATIC_ENT(\"proxy-authorization\", \"\", 48, 2686392507u),\n  MAKE_STATIC_ENT(\"range\", \"\", 49, 4208725202u),\n  MAKE_STATIC_ENT(\"referer\", \"\", 50, 3969579366u),\n  MAKE_STATIC_ENT(\"refresh\", \"\", 51, 3572655668u),\n  MAKE_STATIC_ENT(\"retry-after\", \"\", 52, 3336180598u),\n  MAKE_STATIC_ENT(\"server\", \"\", 53, 1085029842u),\n  MAKE_STATIC_ENT(\"set-cookie\", \"\", 54, 1848371000u),\n  MAKE_STATIC_ENT(\"strict-transport-security\", \"\", 55, 4138147361u),\n  MAKE_STATIC_ENT(\"transfer-encoding\", \"\", 56, 3719590988u),\n  MAKE_STATIC_ENT(\"user-agent\", \"\", 57, 606444526u),\n  MAKE_STATIC_ENT(\"vary\", \"\", 58, 1085005381u),\n  MAKE_STATIC_ENT(\"via\", \"\", 59, 1762798611u),\n  MAKE_STATIC_ENT(\"www-authenticate\", \"\", 60, 779865858u),\n};\n\nstatic int memeq(const void *s1, const void *s2, size_t n) {\n  return memcmp(s1, s2, n) == 0;\n}\n\n/*\n * This function was generated by genlibtokenlookup.py.  Inspired by\n * h2o header lookup.  https://github.com/h2o/h2o\n */\nstatic int32_t lookup_token(const uint8_t *name, size_t namelen) {\n  switch (namelen) {\n  case 2:\n    switch (name[1]) {\n    case 'e':\n      if (memeq(\"t\", name, 1)) {\n        return NGHTTP2_TOKEN_TE;\n      }\n      break;\n    }\n    break;\n  case 3:\n    switch (name[2]) {\n    case 'a':\n      if (memeq(\"vi\", name, 2)) {\n        return NGHTTP2_TOKEN_VIA;\n      }\n      break;\n    case 'e':\n      if (memeq(\"ag\", name, 2)) {\n        return NGHTTP2_TOKEN_AGE;\n      }\n      break;\n    }\n    break;\n  case 4:\n    switch (name[3]) {\n    case 'e':\n      if (memeq(\"dat\", name, 3)) {\n        return NGHTTP2_TOKEN_DATE;\n      }\n      break;\n    case 'g':\n      if (memeq(\"eta\", name, 3)) {\n        return NGHTTP2_TOKEN_ETAG;\n      }\n      break;\n    case 'k':\n      if (memeq(\"lin\", name, 3)) {\n        return NGHTTP2_TOKEN_LINK;\n      }\n      break;\n    case 'm':\n      if (memeq(\"fro\", name, 3)) {\n        return NGHTTP2_TOKEN_FROM;\n      }\n      break;\n    case 't':\n      if (memeq(\"hos\", name, 3)) {\n        return NGHTTP2_TOKEN_HOST;\n      }\n      break;\n    case 'y':\n      if (memeq(\"var\", name, 3)) {\n        return NGHTTP2_TOKEN_VARY;\n      }\n      break;\n    }\n    break;\n  case 5:\n    switch (name[4]) {\n    case 'e':\n      if (memeq(\"rang\", name, 4)) {\n        return NGHTTP2_TOKEN_RANGE;\n      }\n      break;\n    case 'h':\n      if (memeq(\":pat\", name, 4)) {\n        return NGHTTP2_TOKEN__PATH;\n      }\n      break;\n    case 'w':\n      if (memeq(\"allo\", name, 4)) {\n        return NGHTTP2_TOKEN_ALLOW;\n      }\n      break;\n    }\n    break;\n  case 6:\n    switch (name[5]) {\n    case 'e':\n      if (memeq(\"cooki\", name, 5)) {\n        return NGHTTP2_TOKEN_COOKIE;\n      }\n      break;\n    case 'r':\n      if (memeq(\"serve\", name, 5)) {\n        return NGHTTP2_TOKEN_SERVER;\n      }\n      break;\n    case 't':\n      if (memeq(\"accep\", name, 5)) {\n        return NGHTTP2_TOKEN_ACCEPT;\n      }\n      if (memeq(\"expec\", name, 5)) {\n        return NGHTTP2_TOKEN_EXPECT;\n      }\n      break;\n    }\n    break;\n  case 7:\n    switch (name[6]) {\n    case 'd':\n      if (memeq(\":metho\", name, 6)) {\n        return NGHTTP2_TOKEN__METHOD;\n      }\n      break;\n    case 'e':\n      if (memeq(\":schem\", name, 6)) {\n        return NGHTTP2_TOKEN__SCHEME;\n      }\n      if (memeq(\"upgrad\", name, 6)) {\n        return NGHTTP2_TOKEN_UPGRADE;\n      }\n      break;\n    case 'h':\n      if (memeq(\"refres\", name, 6)) {\n        return NGHTTP2_TOKEN_REFRESH;\n      }\n      break;\n    case 'r':\n      if (memeq(\"refere\", name, 6)) {\n        return NGHTTP2_TOKEN_REFERER;\n      }\n      break;\n    case 's':\n      if (memeq(\":statu\", name, 6)) {\n        return NGHTTP2_TOKEN__STATUS;\n      }\n      if (memeq(\"expire\", name, 6)) {\n        return NGHTTP2_TOKEN_EXPIRES;\n      }\n      break;\n    }\n    break;\n  case 8:\n    switch (name[7]) {\n    case 'e':\n      if (memeq(\"if-rang\", name, 7)) {\n        return NGHTTP2_TOKEN_IF_RANGE;\n      }\n      break;\n    case 'h':\n      if (memeq(\"if-matc\", name, 7)) {\n        return NGHTTP2_TOKEN_IF_MATCH;\n      }\n      break;\n    case 'n':\n      if (memeq(\"locatio\", name, 7)) {\n        return NGHTTP2_TOKEN_LOCATION;\n      }\n      break;\n    case 'y':\n      if (memeq(\"priorit\", name, 7)) {\n        return NGHTTP2_TOKEN_PRIORITY;\n      }\n      break;\n    }\n    break;\n  case 9:\n    switch (name[8]) {\n    case 'l':\n      if (memeq(\":protoco\", name, 8)) {\n        return NGHTTP2_TOKEN__PROTOCOL;\n      }\n      break;\n    }\n    break;\n  case 10:\n    switch (name[9]) {\n    case 'e':\n      if (memeq(\"keep-aliv\", name, 9)) {\n        return NGHTTP2_TOKEN_KEEP_ALIVE;\n      }\n      if (memeq(\"set-cooki\", name, 9)) {\n        return NGHTTP2_TOKEN_SET_COOKIE;\n      }\n      break;\n    case 'n':\n      if (memeq(\"connectio\", name, 9)) {\n        return NGHTTP2_TOKEN_CONNECTION;\n      }\n      break;\n    case 't':\n      if (memeq(\"user-agen\", name, 9)) {\n        return NGHTTP2_TOKEN_USER_AGENT;\n      }\n      break;\n    case 'y':\n      if (memeq(\":authorit\", name, 9)) {\n        return NGHTTP2_TOKEN__AUTHORITY;\n      }\n      break;\n    }\n    break;\n  case 11:\n    switch (name[10]) {\n    case 'r':\n      if (memeq(\"retry-afte\", name, 10)) {\n        return NGHTTP2_TOKEN_RETRY_AFTER;\n      }\n      break;\n    }\n    break;\n  case 12:\n    switch (name[11]) {\n    case 'e':\n      if (memeq(\"content-typ\", name, 11)) {\n        return NGHTTP2_TOKEN_CONTENT_TYPE;\n      }\n      break;\n    case 's':\n      if (memeq(\"max-forward\", name, 11)) {\n        return NGHTTP2_TOKEN_MAX_FORWARDS;\n      }\n      break;\n    }\n    break;\n  case 13:\n    switch (name[12]) {\n    case 'd':\n      if (memeq(\"last-modifie\", name, 12)) {\n        return NGHTTP2_TOKEN_LAST_MODIFIED;\n      }\n      break;\n    case 'e':\n      if (memeq(\"content-rang\", name, 12)) {\n        return NGHTTP2_TOKEN_CONTENT_RANGE;\n      }\n      break;\n    case 'h':\n      if (memeq(\"if-none-matc\", name, 12)) {\n        return NGHTTP2_TOKEN_IF_NONE_MATCH;\n      }\n      break;\n    case 'l':\n      if (memeq(\"cache-contro\", name, 12)) {\n        return NGHTTP2_TOKEN_CACHE_CONTROL;\n      }\n      break;\n    case 'n':\n      if (memeq(\"authorizatio\", name, 12)) {\n        return NGHTTP2_TOKEN_AUTHORIZATION;\n      }\n      break;\n    case 's':\n      if (memeq(\"accept-range\", name, 12)) {\n        return NGHTTP2_TOKEN_ACCEPT_RANGES;\n      }\n      break;\n    }\n    break;\n  case 14:\n    switch (name[13]) {\n    case 'h':\n      if (memeq(\"content-lengt\", name, 13)) {\n        return NGHTTP2_TOKEN_CONTENT_LENGTH;\n      }\n      break;\n    case 't':\n      if (memeq(\"accept-charse\", name, 13)) {\n        return NGHTTP2_TOKEN_ACCEPT_CHARSET;\n      }\n      break;\n    }\n    break;\n  case 15:\n    switch (name[14]) {\n    case 'e':\n      if (memeq(\"accept-languag\", name, 14)) {\n        return NGHTTP2_TOKEN_ACCEPT_LANGUAGE;\n      }\n      break;\n    case 'g':\n      if (memeq(\"accept-encodin\", name, 14)) {\n        return NGHTTP2_TOKEN_ACCEPT_ENCODING;\n      }\n      break;\n    }\n    break;\n  case 16:\n    switch (name[15]) {\n    case 'e':\n      if (memeq(\"content-languag\", name, 15)) {\n        return NGHTTP2_TOKEN_CONTENT_LANGUAGE;\n      }\n      if (memeq(\"www-authenticat\", name, 15)) {\n        return NGHTTP2_TOKEN_WWW_AUTHENTICATE;\n      }\n      break;\n    case 'g':\n      if (memeq(\"content-encodin\", name, 15)) {\n        return NGHTTP2_TOKEN_CONTENT_ENCODING;\n      }\n      break;\n    case 'n':\n      if (memeq(\"content-locatio\", name, 15)) {\n        return NGHTTP2_TOKEN_CONTENT_LOCATION;\n      }\n      if (memeq(\"proxy-connectio\", name, 15)) {\n        return NGHTTP2_TOKEN_PROXY_CONNECTION;\n      }\n      break;\n    }\n    break;\n  case 17:\n    switch (name[16]) {\n    case 'e':\n      if (memeq(\"if-modified-sinc\", name, 16)) {\n        return NGHTTP2_TOKEN_IF_MODIFIED_SINCE;\n      }\n      break;\n    case 'g':\n      if (memeq(\"transfer-encodin\", name, 16)) {\n        return NGHTTP2_TOKEN_TRANSFER_ENCODING;\n      }\n      break;\n    }\n    break;\n  case 18:\n    switch (name[17]) {\n    case 'e':\n      if (memeq(\"proxy-authenticat\", name, 17)) {\n        return NGHTTP2_TOKEN_PROXY_AUTHENTICATE;\n      }\n      break;\n    }\n    break;\n  case 19:\n    switch (name[18]) {\n    case 'e':\n      if (memeq(\"if-unmodified-sinc\", name, 18)) {\n        return NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE;\n      }\n      break;\n    case 'n':\n      if (memeq(\"content-dispositio\", name, 18)) {\n        return NGHTTP2_TOKEN_CONTENT_DISPOSITION;\n      }\n      if (memeq(\"proxy-authorizatio\", name, 18)) {\n        return NGHTTP2_TOKEN_PROXY_AUTHORIZATION;\n      }\n      break;\n    }\n    break;\n  case 25:\n    switch (name[24]) {\n    case 'y':\n      if (memeq(\"strict-transport-securit\", name, 24)) {\n        return NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY;\n      }\n      break;\n    }\n    break;\n  case 27:\n    switch (name[26]) {\n    case 'n':\n      if (memeq(\"access-control-allow-origi\", name, 26)) {\n        return NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN;\n      }\n      break;\n    }\n    break;\n  }\n  return -1;\n}\n\nvoid nghttp2_hd_entry_init(nghttp2_hd_entry *ent, nghttp2_hd_nv *nv) {\n  ent->nv = *nv;\n  ent->cnv.name = nv->name->base;\n  ent->cnv.namelen = nv->name->len;\n  ent->cnv.value = nv->value->base;\n  ent->cnv.valuelen = nv->value->len;\n  ent->cnv.flags = nv->flags;\n  ent->next = NULL;\n  ent->hash = 0;\n\n  nghttp2_rcbuf_incref(ent->nv.name);\n  nghttp2_rcbuf_incref(ent->nv.value);\n}\n\nvoid nghttp2_hd_entry_free(nghttp2_hd_entry *ent) {\n  nghttp2_rcbuf_decref(ent->nv.value);\n  nghttp2_rcbuf_decref(ent->nv.name);\n}\n\nstatic int name_eq(const nghttp2_hd_nv *a, const nghttp2_nv *b) {\n  return a->name->len == b->namelen &&\n         memeq(a->name->base, b->name, b->namelen);\n}\n\nstatic int value_eq(const nghttp2_hd_nv *a, const nghttp2_nv *b) {\n  return a->value->len == b->valuelen &&\n         memeq(a->value->base, b->value, b->valuelen);\n}\n\nstatic uint32_t name_hash(const nghttp2_nv *nv) {\n  /* 32 bit FNV-1a: http://isthe.com/chongo/tech/comp/fnv/ */\n  uint32_t h = 2166136261u;\n  size_t i;\n\n  for (i = 0; i < nv->namelen; ++i) {\n    h ^= nv->name[i];\n    h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);\n  }\n\n  return h;\n}\n\nstatic void hd_map_init(nghttp2_hd_map *map) {\n  memset(map, 0, sizeof(nghttp2_hd_map));\n}\n\nstatic void hd_map_insert(nghttp2_hd_map *map, nghttp2_hd_entry *ent) {\n  nghttp2_hd_entry **bucket;\n\n  bucket = &map->table[ent->hash & (HD_MAP_SIZE - 1)];\n\n  if (*bucket == NULL) {\n    *bucket = ent;\n    return;\n  }\n\n  /* lower index is linked near the root */\n  ent->next = *bucket;\n  *bucket = ent;\n}\n\nstatic nghttp2_hd_entry *hd_map_find(nghttp2_hd_map *map, int *exact_match,\n                                     const nghttp2_nv *nv, int32_t token,\n                                     uint32_t hash, int name_only) {\n  nghttp2_hd_entry *p;\n  nghttp2_hd_entry *res = NULL;\n\n  *exact_match = 0;\n\n  for (p = map->table[hash & (HD_MAP_SIZE - 1)]; p; p = p->next) {\n    if (token != p->nv.token ||\n        (token == -1 && (hash != p->hash || !name_eq(&p->nv, nv)))) {\n      continue;\n    }\n    if (!res) {\n      res = p;\n      if (name_only) {\n        break;\n      }\n    }\n    if (value_eq(&p->nv, nv)) {\n      res = p;\n      *exact_match = 1;\n      break;\n    }\n  }\n\n  return res;\n}\n\nstatic void hd_map_remove(nghttp2_hd_map *map, nghttp2_hd_entry *ent) {\n  nghttp2_hd_entry **dst;\n\n  dst = &map->table[ent->hash & (HD_MAP_SIZE - 1)];\n\n  for (; *dst; dst = &(*dst)->next) {\n    if (*dst != ent) {\n      continue;\n    }\n\n    *dst = ent->next;\n    ent->next = NULL;\n    return;\n  }\n}\n\nstatic int hd_ringbuf_init(nghttp2_hd_ringbuf *ringbuf, size_t bufsize,\n                           nghttp2_mem *mem) {\n  size_t size;\n  for (size = 1; size < bufsize; size <<= 1)\n    ;\n  ringbuf->buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size);\n  if (ringbuf->buffer == NULL) {\n    return NGHTTP2_ERR_NOMEM;\n  }\n  ringbuf->mask = size - 1;\n  ringbuf->first = 0;\n  ringbuf->len = 0;\n  return 0;\n}\n\nstatic nghttp2_hd_entry *hd_ringbuf_get(nghttp2_hd_ringbuf *ringbuf,\n                                        size_t idx) {\n  assert(idx < ringbuf->len);\n  return ringbuf->buffer[(ringbuf->first + idx) & ringbuf->mask];\n}\n\nstatic int hd_ringbuf_reserve(nghttp2_hd_ringbuf *ringbuf, size_t bufsize,\n                              nghttp2_mem *mem) {\n  size_t i;\n  size_t size;\n  nghttp2_hd_entry **buffer;\n\n  if (ringbuf->mask + 1 >= bufsize) {\n    return 0;\n  }\n  for (size = 1; size < bufsize; size <<= 1)\n    ;\n  buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size);\n  if (buffer == NULL) {\n    return NGHTTP2_ERR_NOMEM;\n  }\n  for (i = 0; i < ringbuf->len; ++i) {\n    buffer[i] = hd_ringbuf_get(ringbuf, i);\n  }\n  nghttp2_mem_free(mem, ringbuf->buffer);\n  ringbuf->buffer = buffer;\n  ringbuf->mask = size - 1;\n  ringbuf->first = 0;\n  return 0;\n}\n\nstatic void hd_ringbuf_free(nghttp2_hd_ringbuf *ringbuf, nghttp2_mem *mem) {\n  size_t i;\n  if (ringbuf == NULL) {\n    return;\n  }\n  for (i = 0; i < ringbuf->len; ++i) {\n    nghttp2_hd_entry *ent = hd_ringbuf_get(ringbuf, i);\n\n    nghttp2_hd_entry_free(ent);\n    nghttp2_mem_free(mem, ent);\n  }\n  nghttp2_mem_free(mem, ringbuf->buffer);\n}\n\nstatic int hd_ringbuf_push_front(nghttp2_hd_ringbuf *ringbuf,\n                                 nghttp2_hd_entry *ent, nghttp2_mem *mem) {\n  int rv;\n\n  rv = hd_ringbuf_reserve(ringbuf, ringbuf->len + 1, mem);\n\n  if (rv != 0) {\n    return rv;\n  }\n\n  ringbuf->buffer[--ringbuf->first & ringbuf->mask] = ent;\n  ++ringbuf->len;\n\n  return 0;\n}\n\nstatic void hd_ringbuf_pop_back(nghttp2_hd_ringbuf *ringbuf) {\n  assert(ringbuf->len > 0);\n  --ringbuf->len;\n}\n\nstatic int hd_context_init(nghttp2_hd_context *context, nghttp2_mem *mem) {\n  int rv;\n  context->mem = mem;\n  context->bad = 0;\n  context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;\n  rv = hd_ringbuf_init(\n    &context->hd_table,\n    context->hd_table_bufsize_max / NGHTTP2_HD_ENTRY_OVERHEAD, mem);\n  if (rv != 0) {\n    return rv;\n  }\n\n  context->hd_table_bufsize = 0;\n  context->next_seq = 0;\n\n  return 0;\n}\n\nstatic void hd_context_free(nghttp2_hd_context *context) {\n  hd_ringbuf_free(&context->hd_table, context->mem);\n}\n\nint nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem) {\n  return nghttp2_hd_deflate_init2(\n    deflater, NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE, mem);\n}\n\nint nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater,\n                             size_t max_deflate_dynamic_table_size,\n                             nghttp2_mem *mem) {\n  int rv;\n  rv = hd_context_init(&deflater->ctx, mem);\n  if (rv != 0) {\n    return rv;\n  }\n\n  hd_map_init(&deflater->map);\n\n  if (max_deflate_dynamic_table_size < NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE) {\n    deflater->notify_table_size_change = 1;\n    deflater->ctx.hd_table_bufsize_max = max_deflate_dynamic_table_size;\n  } else {\n    deflater->notify_table_size_change = 0;\n  }\n\n  deflater->deflate_hd_table_bufsize_max = max_deflate_dynamic_table_size;\n  deflater->min_hd_table_bufsize_max = UINT32_MAX;\n\n  return 0;\n}\n\nint nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem) {\n  int rv;\n\n  rv = hd_context_init(&inflater->ctx, mem);\n  if (rv != 0) {\n    goto fail;\n  }\n\n  inflater->settings_hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;\n  inflater->min_hd_table_bufsize_max = UINT32_MAX;\n\n  inflater->nv_name_keep = NULL;\n  inflater->nv_value_keep = NULL;\n\n  inflater->opcode = NGHTTP2_HD_OPCODE_NONE;\n  inflater->state = NGHTTP2_HD_STATE_INFLATE_START;\n\n  nghttp2_buf_init(&inflater->namebuf);\n  nghttp2_buf_init(&inflater->valuebuf);\n\n  inflater->namercbuf = NULL;\n  inflater->valuercbuf = NULL;\n\n  inflater->huffman_encoded = 0;\n  inflater->index = 0;\n  inflater->left = 0;\n  inflater->shift = 0;\n  inflater->index_required = 0;\n  inflater->no_index = 0;\n\n  return 0;\n\nfail:\n  return rv;\n}\n\nstatic void hd_inflate_keep_free(nghttp2_hd_inflater *inflater) {\n  nghttp2_rcbuf_decref(inflater->nv_value_keep);\n  nghttp2_rcbuf_decref(inflater->nv_name_keep);\n\n  inflater->nv_value_keep = NULL;\n  inflater->nv_name_keep = NULL;\n}\n\nvoid nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater) {\n  hd_context_free(&deflater->ctx);\n}\n\nvoid nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater) {\n  hd_inflate_keep_free(inflater);\n\n  nghttp2_rcbuf_decref(inflater->valuercbuf);\n  nghttp2_rcbuf_decref(inflater->namercbuf);\n\n  hd_context_free(&inflater->ctx);\n}\n\nstatic size_t entry_room(size_t namelen, size_t valuelen) {\n  return NGHTTP2_HD_ENTRY_OVERHEAD + namelen + valuelen;\n}\n\nstatic void emit_header(nghttp2_hd_nv *nv_out, nghttp2_hd_nv *nv) {\n  DEBUGF(\"inflatehd: header emission: %s: %s\\n\", nv->name->base,\n         nv->value->base);\n  /* ent->ref may be 0. This happens if the encoder emits literal\n     block larger than header table capacity with indexing. */\n  *nv_out = *nv;\n}\n\nstatic size_t count_encoded_length(size_t n, size_t prefix) {\n  size_t k = (size_t)((1 << prefix) - 1);\n  size_t len = 0;\n\n  if (n < k) {\n    return 1;\n  }\n\n  n -= k;\n  ++len;\n\n  for (; n >= 128; n >>= 7, ++len)\n    ;\n\n  return len + 1;\n}\n\nstatic size_t encode_length(uint8_t *buf, size_t n, size_t prefix) {\n  size_t k = (size_t)((1 << prefix) - 1);\n  uint8_t *begin = buf;\n\n  *buf = (uint8_t)(*buf & ~k);\n\n  if (n < k) {\n    *buf = (uint8_t)(*buf | n);\n    return 1;\n  }\n\n  *buf = (uint8_t)(*buf | k);\n  ++buf;\n\n  n -= k;\n\n  for (; n >= 128; n >>= 7) {\n    *buf++ = (uint8_t)((1 << 7) | (n & 0x7f));\n  }\n\n  *buf++ = (uint8_t)n;\n\n  return (size_t)(buf - begin);\n}\n\n/*\n * Decodes |prefix| prefixed integer stored from |in|.  The |last|\n * represents the 1 beyond the last of the valid contiguous memory\n * region from |in|.  The decoded integer must be less than or equal\n * to UINT32_MAX.\n *\n * If the |initial| is nonzero, it is used as a initial value, this\n * function assumes the |in| starts with intermediate data.\n *\n * An entire integer is decoded successfully, decoded, the |*fin| is\n * set to nonzero.\n *\n * This function stores the decoded integer in |*res| if it succeed,\n * including partial decoding (in this case, number of shift to make\n * in the next call will be stored in |*shift_ptr|) and returns number\n * of bytes processed, or returns -1, indicating decoding error.\n */\nstatic nghttp2_ssize decode_length(uint32_t *res, size_t *shift_ptr, int *fin,\n                                   uint32_t initial, size_t shift,\n                                   const uint8_t *in, const uint8_t *last,\n                                   size_t prefix) {\n  uint32_t k = (uint8_t)((1 << prefix) - 1);\n  uint32_t n = initial;\n  const uint8_t *start = in;\n\n  *shift_ptr = 0;\n  *fin = 0;\n\n  if (n == 0) {\n    if ((*in & k) != k) {\n      *res = (*in) & k;\n      *fin = 1;\n      return 1;\n    }\n\n    n = k;\n\n    if (++in == last) {\n      *res = n;\n      return (nghttp2_ssize)(in - start);\n    }\n  }\n\n  for (; in != last; ++in, shift += 7) {\n    uint32_t add = *in & 0x7f;\n\n    if (shift >= 32) {\n      DEBUGF(\"inflate: shift exponent overflow\\n\");\n      return -1;\n    }\n\n    if ((UINT32_MAX >> shift) < add) {\n      DEBUGF(\"inflate: integer overflow on shift\\n\");\n      return -1;\n    }\n\n    add <<= shift;\n\n    if (UINT32_MAX - add < n) {\n      DEBUGF(\"inflate: integer overflow on addition\\n\");\n      return -1;\n    }\n\n    n += add;\n\n    if ((*in & (1 << 7)) == 0) {\n      break;\n    }\n  }\n\n  *shift_ptr = shift;\n\n  if (in == last) {\n    *res = n;\n    return (nghttp2_ssize)(in - start);\n  }\n\n  *res = n;\n  *fin = 1;\n  return (nghttp2_ssize)(in + 1 - start);\n}\n\nstatic int emit_table_size(nghttp2_bufs *bufs, size_t table_size) {\n  int rv;\n  uint8_t *bufp;\n  size_t blocklen;\n  uint8_t sb[16];\n\n  DEBUGF(\"deflatehd: emit table_size=%zu\\n\", table_size);\n\n  blocklen = count_encoded_length(table_size, 5);\n\n  if (sizeof(sb) < blocklen) {\n    return NGHTTP2_ERR_HEADER_COMP;\n  }\n\n  bufp = sb;\n\n  *bufp = 0x20u;\n\n  encode_length(bufp, table_size, 5);\n\n  rv = nghttp2_bufs_add(bufs, sb, blocklen);\n  if (rv != 0) {\n    return rv;\n  }\n\n  return 0;\n}\n\nstatic int emit_indexed_block(nghttp2_bufs *bufs, size_t idx) {\n  int rv;\n  size_t blocklen;\n  uint8_t sb[16];\n  uint8_t *bufp;\n\n  blocklen = count_encoded_length(idx + 1, 7);\n\n  DEBUGF(\"deflatehd: emit indexed index=%zu, %zu bytes\\n\", idx, blocklen);\n\n  if (sizeof(sb) < blocklen) {\n    return NGHTTP2_ERR_HEADER_COMP;\n  }\n\n  bufp = sb;\n  *bufp = 0x80u;\n  encode_length(bufp, idx + 1, 7);\n\n  rv = nghttp2_bufs_add(bufs, sb, blocklen);\n  if (rv != 0) {\n    return rv;\n  }\n\n  return 0;\n}\n\nstatic int emit_string(nghttp2_bufs *bufs, const uint8_t *str, size_t len) {\n  int rv;\n  uint8_t sb[16];\n  uint8_t *bufp;\n  size_t blocklen;\n  size_t enclen;\n  int huffman = 0;\n\n  enclen = nghttp2_hd_huff_encode_count(str, len);\n\n  if (enclen < len) {\n    huffman = 1;\n  } else {\n    enclen = len;\n  }\n\n  blocklen = count_encoded_length(enclen, 7);\n\n  DEBUGF(\"deflatehd: emit string str=%.*s, length=%zu, huffman=%d, \"\n         \"encoded_length=%zu\\n\",\n         (int)len, (const char *)str, len, huffman, enclen);\n\n  if (sizeof(sb) < blocklen) {\n    return NGHTTP2_ERR_HEADER_COMP;\n  }\n\n  bufp = sb;\n  *bufp = huffman ? 1 << 7 : 0;\n  encode_length(bufp, enclen, 7);\n\n  rv = nghttp2_bufs_add(bufs, sb, blocklen);\n  if (rv != 0) {\n    return rv;\n  }\n\n  if (huffman) {\n    rv = nghttp2_hd_huff_encode(bufs, str, len);\n  } else {\n    assert(enclen == len);\n    rv = nghttp2_bufs_add(bufs, str, len);\n  }\n\n  return rv;\n}\n\nstatic uint8_t pack_first_byte(int indexing_mode) {\n  switch (indexing_mode) {\n  case NGHTTP2_HD_WITH_INDEXING:\n    return 0x40u;\n  case NGHTTP2_HD_WITHOUT_INDEXING:\n    return 0;\n  case NGHTTP2_HD_NEVER_INDEXING:\n    return 0x10u;\n  default:\n    assert(0);\n  }\n  /* This is required to compile with android NDK r10d +\n     --enable-werror */\n  return 0;\n}\n\nstatic int emit_indname_block(nghttp2_bufs *bufs, size_t idx,\n                              const nghttp2_nv *nv, int indexing_mode) {\n  int rv;\n  uint8_t *bufp;\n  size_t blocklen;\n  uint8_t sb[16];\n  size_t prefixlen;\n\n  if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) {\n    prefixlen = 6;\n  } else {\n    prefixlen = 4;\n  }\n\n  DEBUGF(\"deflatehd: emit indname index=%zu, valuelen=%zu, indexing_mode=%d\\n\",\n         idx, nv->valuelen, indexing_mode);\n\n  blocklen = count_encoded_length(idx + 1, prefixlen);\n\n  if (sizeof(sb) < blocklen) {\n    return NGHTTP2_ERR_HEADER_COMP;\n  }\n\n  bufp = sb;\n\n  *bufp = pack_first_byte(indexing_mode);\n\n  encode_length(bufp, idx + 1, prefixlen);\n\n  rv = nghttp2_bufs_add(bufs, sb, blocklen);\n  if (rv != 0) {\n    return rv;\n  }\n\n  rv = emit_string(bufs, nv->value, nv->valuelen);\n  if (rv != 0) {\n    return rv;\n  }\n\n  return 0;\n}\n\nstatic int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv,\n                              int indexing_mode) {\n  int rv;\n\n  DEBUGF(\n    \"deflatehd: emit newname namelen=%zu, valuelen=%zu, indexing_mode=%d\\n\",\n    nv->namelen, nv->valuelen, indexing_mode);\n\n  rv = nghttp2_bufs_addb(bufs, pack_first_byte(indexing_mode));\n  if (rv != 0) {\n    return rv;\n  }\n\n  rv = emit_string(bufs, nv->name, nv->namelen);\n  if (rv != 0) {\n    return rv;\n  }\n\n  rv = emit_string(bufs, nv->value, nv->valuelen);\n  if (rv != 0) {\n    return rv;\n  }\n\n  return 0;\n}\n\nstatic int add_hd_table_incremental(nghttp2_hd_context *context,\n                                    nghttp2_hd_nv *nv, nghttp2_hd_map *map,\n                                    uint32_t hash) {\n  int rv;\n  nghttp2_hd_entry *new_ent;\n  size_t room;\n  nghttp2_mem *mem;\n\n  mem = context->mem;\n  room = entry_room(nv->name->len, nv->value->len);\n\n  while (context->hd_table_bufsize + room > context->hd_table_bufsize_max &&\n         context->hd_table.len > 0) {\n    size_t idx = context->hd_table.len - 1;\n    nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx);\n\n    context->hd_table_bufsize -=\n      entry_room(ent->nv.name->len, ent->nv.value->len);\n\n    DEBUGF(\"hpack: remove item from header table: %s: %s\\n\",\n           (char *)ent->nv.name->base, (char *)ent->nv.value->base);\n\n    hd_ringbuf_pop_back(&context->hd_table);\n    if (map) {\n      hd_map_remove(map, ent);\n    }\n\n    nghttp2_hd_entry_free(ent);\n    nghttp2_mem_free(mem, ent);\n  }\n\n  if (room > context->hd_table_bufsize_max) {\n    /* The entry taking more than NGHTTP2_HD_MAX_BUFFER_SIZE is\n       immediately evicted.  So we don't allocate memory for it. */\n    return 0;\n  }\n\n  new_ent = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry));\n  if (new_ent == NULL) {\n    return NGHTTP2_ERR_NOMEM;\n  }\n\n  nghttp2_hd_entry_init(new_ent, nv);\n\n  rv = hd_ringbuf_push_front(&context->hd_table, new_ent, mem);\n\n  if (rv != 0) {\n    nghttp2_hd_entry_free(new_ent);\n    nghttp2_mem_free(mem, new_ent);\n\n    return rv;\n  }\n\n  new_ent->seq = context->next_seq++;\n  new_ent->hash = hash;\n\n  if (map) {\n    hd_map_insert(map, new_ent);\n  }\n\n  context->hd_table_bufsize += room;\n\n  return 0;\n}\n\ntypedef struct {\n  nghttp2_ssize index;\n  /* Nonzero if both name and value are matched. */\n  int name_value_match;\n} search_result;\n\nstatic search_result search_static_table(const nghttp2_nv *nv, int32_t token,\n                                         int name_only) {\n  search_result res = {token, 0};\n  int i;\n  const nghttp2_hd_static_entry *ent;\n\n  if (name_only) {\n    return res;\n  }\n\n  for (i = token;\n       i <= NGHTTP2_TOKEN_WWW_AUTHENTICATE && static_table[i].token == token;\n       ++i) {\n    ent = &static_table[i];\n    if (ent->value.len == nv->valuelen &&\n        memcmp(ent->value.base, nv->value, nv->valuelen) == 0) {\n      res.index = i;\n      res.name_value_match = 1;\n      return res;\n    }\n  }\n  return res;\n}\n\nstatic search_result search_hd_table(nghttp2_hd_context *context,\n                                     const nghttp2_nv *nv, int32_t token,\n                                     int indexing_mode, nghttp2_hd_map *map,\n                                     uint32_t hash) {\n  search_result res = {-1, 0};\n  const nghttp2_hd_entry *ent;\n  int exact_match;\n  int name_only = indexing_mode == NGHTTP2_HD_NEVER_INDEXING;\n\n  exact_match = 0;\n  ent = hd_map_find(map, &exact_match, nv, token, hash, name_only);\n\n  if (!exact_match && token >= 0 && token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) {\n    return search_static_table(nv, token, name_only);\n  }\n\n  if (ent == NULL) {\n    return res;\n  }\n\n  res.index = (nghttp2_ssize)(context->next_seq - 1 - ent->seq +\n                              NGHTTP2_STATIC_TABLE_LENGTH);\n  res.name_value_match = exact_match;\n\n  return res;\n}\n\nstatic void hd_context_shrink_table_size(nghttp2_hd_context *context,\n                                         nghttp2_hd_map *map) {\n  nghttp2_mem *mem;\n\n  mem = context->mem;\n\n  while (context->hd_table_bufsize > context->hd_table_bufsize_max &&\n         context->hd_table.len > 0) {\n    size_t idx = context->hd_table.len - 1;\n    nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx);\n    context->hd_table_bufsize -=\n      entry_room(ent->nv.name->len, ent->nv.value->len);\n    hd_ringbuf_pop_back(&context->hd_table);\n    if (map) {\n      hd_map_remove(map, ent);\n    }\n\n    nghttp2_hd_entry_free(ent);\n    nghttp2_mem_free(mem, ent);\n  }\n}\n\nint nghttp2_hd_deflate_change_table_size(\n  nghttp2_hd_deflater *deflater, size_t settings_max_dynamic_table_size) {\n  size_t next_bufsize = nghttp2_min_size(\n    settings_max_dynamic_table_size, deflater->deflate_hd_table_bufsize_max);\n\n  deflater->ctx.hd_table_bufsize_max = next_bufsize;\n\n  deflater->min_hd_table_bufsize_max =\n    nghttp2_min_size(deflater->min_hd_table_bufsize_max, next_bufsize);\n\n  deflater->notify_table_size_change = 1;\n\n  hd_context_shrink_table_size(&deflater->ctx, &deflater->map);\n  return 0;\n}\n\nint nghttp2_hd_inflate_change_table_size(\n  nghttp2_hd_inflater *inflater, size_t settings_max_dynamic_table_size) {\n  switch (inflater->state) {\n  case NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE:\n  case NGHTTP2_HD_STATE_INFLATE_START:\n    break;\n  default:\n    return NGHTTP2_ERR_INVALID_STATE;\n  }\n\n  inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size;\n\n  /* It seems that encoder is not required to send dynamic table size\n     update if the table size is not changed after applying\n     SETTINGS_HEADER_TABLE_SIZE.  RFC 7541 is ambiguous here, but this\n     is the intention of the editor.  If new maximum table size is\n     strictly smaller than the current negotiated maximum size,\n     encoder must send dynamic table size update.  In other cases, we\n     cannot expect it to do so. */\n  if (inflater->ctx.hd_table_bufsize_max > settings_max_dynamic_table_size) {\n    inflater->state = NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE;\n    /* Remember minimum value, and validate that encoder sends the\n       value less than or equal to this. */\n    inflater->min_hd_table_bufsize_max = settings_max_dynamic_table_size;\n\n    inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size;\n\n    hd_context_shrink_table_size(&inflater->ctx, NULL);\n  }\n\n  return 0;\n}\n\n#define INDEX_RANGE_VALID(context, idx)                                        \\\n  ((idx) < (context)->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH)\n\nstatic size_t get_max_index(nghttp2_hd_context *context) {\n  return context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH;\n}\n\nnghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t idx) {\n  assert(INDEX_RANGE_VALID(context, idx));\n  if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) {\n    return hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH)\n      ->nv;\n  } else {\n    const nghttp2_hd_static_entry *ent = &static_table[idx];\n    nghttp2_hd_nv nv = {(nghttp2_rcbuf *)&ent->name,\n                        (nghttp2_rcbuf *)&ent->value, ent->token,\n                        NGHTTP2_NV_FLAG_NONE};\n    return nv;\n  }\n}\n\nstatic const nghttp2_nv *nghttp2_hd_table_get2(nghttp2_hd_context *context,\n                                               size_t idx) {\n  assert(INDEX_RANGE_VALID(context, idx));\n  if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) {\n    return &hd_ringbuf_get(&context->hd_table,\n                           idx - NGHTTP2_STATIC_TABLE_LENGTH)\n              ->cnv;\n  }\n\n  return &static_table[idx].cnv;\n}\n\nstatic int hd_deflate_decide_indexing(nghttp2_hd_deflater *deflater,\n                                      const nghttp2_nv *nv, int32_t token) {\n  if (token == NGHTTP2_TOKEN__PATH || token == NGHTTP2_TOKEN_AGE ||\n      token == NGHTTP2_TOKEN_CONTENT_LENGTH || token == NGHTTP2_TOKEN_ETAG ||\n      token == NGHTTP2_TOKEN_IF_MODIFIED_SINCE ||\n      token == NGHTTP2_TOKEN_IF_NONE_MATCH || token == NGHTTP2_TOKEN_LOCATION ||\n      token == NGHTTP2_TOKEN_SET_COOKIE ||\n      entry_room(nv->namelen, nv->valuelen) >\n        deflater->ctx.hd_table_bufsize_max * 3 / 4) {\n    return NGHTTP2_HD_WITHOUT_INDEXING;\n  }\n\n  return NGHTTP2_HD_WITH_INDEXING;\n}\n\nstatic int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs,\n                      const nghttp2_nv *nv) {\n  int rv;\n  search_result res;\n  nghttp2_ssize idx;\n  int indexing_mode;\n  int32_t token;\n  nghttp2_mem *mem;\n  uint32_t hash = 0;\n\n  DEBUGF(\"deflatehd: deflating %.*s: %.*s\\n\", (int)nv->namelen, nv->name,\n         (int)nv->valuelen, nv->value);\n\n  mem = deflater->ctx.mem;\n\n  token = lookup_token(nv->name, nv->namelen);\n  if (token == -1) {\n    hash = name_hash(nv);\n  } else if (token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) {\n    hash = static_table[token].hash;\n  }\n\n  /* Don't index authorization header field since it may contain low\n     entropy secret data (e.g., id/password).  Also cookie header\n     field with less than 20 bytes value is also never indexed.  This\n     is the same criteria used in Firefox codebase. */\n  indexing_mode = token == NGHTTP2_TOKEN_AUTHORIZATION ||\n                      (token == NGHTTP2_TOKEN_COOKIE && nv->valuelen < 20) ||\n                      (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX)\n                    ? NGHTTP2_HD_NEVER_INDEXING\n                    : hd_deflate_decide_indexing(deflater, nv, token);\n\n  res = search_hd_table(&deflater->ctx, nv, token, indexing_mode,\n                        &deflater->map, hash);\n\n  idx = res.index;\n\n  if (res.name_value_match) {\n    DEBUGF(\"deflatehd: name/value match index=%td\\n\", idx);\n\n    rv = emit_indexed_block(bufs, (size_t)idx);\n    if (rv != 0) {\n      return rv;\n    }\n\n    return 0;\n  }\n\n  if (res.index != -1) {\n    DEBUGF(\"deflatehd: name match index=%td\\n\", res.index);\n  }\n\n  if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) {\n    nghttp2_hd_nv hd_nv;\n\n    if (idx != -1) {\n      hd_nv.name = nghttp2_hd_table_get(&deflater->ctx, (size_t)idx).name;\n      nghttp2_rcbuf_incref(hd_nv.name);\n    } else {\n      rv = nghttp2_rcbuf_new2(&hd_nv.name, nv->name, nv->namelen, mem);\n      if (rv != 0) {\n        return rv;\n      }\n    }\n\n    rv = nghttp2_rcbuf_new2(&hd_nv.value, nv->value, nv->valuelen, mem);\n\n    if (rv != 0) {\n      nghttp2_rcbuf_decref(hd_nv.name);\n      return rv;\n    }\n\n    hd_nv.token = token;\n    hd_nv.flags = NGHTTP2_NV_FLAG_NONE;\n\n    rv = add_hd_table_incremental(&deflater->ctx, &hd_nv, &deflater->map, hash);\n\n    nghttp2_rcbuf_decref(hd_nv.value);\n    nghttp2_rcbuf_decref(hd_nv.name);\n\n    if (rv != 0) {\n      return NGHTTP2_ERR_HEADER_COMP;\n    }\n  }\n  if (idx == -1) {\n    rv = emit_newname_block(bufs, nv, indexing_mode);\n  } else {\n    rv = emit_indname_block(bufs, (size_t)idx, nv, indexing_mode);\n  }\n  if (rv != 0) {\n    return rv;\n  }\n\n  return 0;\n}\n\nint nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater,\n                               nghttp2_bufs *bufs, const nghttp2_nv *nv,\n                               size_t nvlen) {\n  size_t i;\n  int rv = 0;\n\n  if (deflater->ctx.bad) {\n    return NGHTTP2_ERR_HEADER_COMP;\n  }\n\n  if (deflater->notify_table_size_change) {\n    size_t min_hd_table_bufsize_max;\n\n    min_hd_table_bufsize_max = deflater->min_hd_table_bufsize_max;\n\n    deflater->notify_table_size_change = 0;\n    deflater->min_hd_table_bufsize_max = UINT32_MAX;\n\n    if (deflater->ctx.hd_table_bufsize_max > min_hd_table_bufsize_max) {\n      rv = emit_table_size(bufs, min_hd_table_bufsize_max);\n\n      if (rv != 0) {\n        goto fail;\n      }\n    }\n\n    rv = emit_table_size(bufs, deflater->ctx.hd_table_bufsize_max);\n\n    if (rv != 0) {\n      goto fail;\n    }\n  }\n\n  for (i = 0; i < nvlen; ++i) {\n    rv = deflate_nv(deflater, bufs, &nv[i]);\n    if (rv != 0) {\n      goto fail;\n    }\n  }\n\n  DEBUGF(\"deflatehd: all input name/value pairs were deflated\\n\");\n\n  return 0;\nfail:\n  DEBUGF(\"deflatehd: error return %d\\n\", rv);\n\n  deflater->ctx.bad = 1;\n  return rv;\n}\n\nssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf,\n                              size_t buflen, const nghttp2_nv *nv,\n                              size_t nvlen) {\n  return (ssize_t)nghttp2_hd_deflate_hd2(deflater, buf, buflen, nv, nvlen);\n}\n\nnghttp2_ssize nghttp2_hd_deflate_hd2(nghttp2_hd_deflater *deflater,\n                                     uint8_t *buf, size_t buflen,\n                                     const nghttp2_nv *nv, size_t nvlen) {\n  nghttp2_bufs bufs;\n  int rv;\n  nghttp2_mem *mem;\n\n  mem = deflater->ctx.mem;\n\n  rv = nghttp2_bufs_wrap_init(&bufs, buf, buflen, mem);\n\n  if (rv != 0) {\n    return rv;\n  }\n\n  rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nv, nvlen);\n\n  buflen = nghttp2_bufs_len(&bufs);\n\n  nghttp2_bufs_wrap_free(&bufs);\n\n  if (rv == NGHTTP2_ERR_BUFFER_ERROR) {\n    return NGHTTP2_ERR_INSUFF_BUFSIZE;\n  }\n\n  if (rv != 0) {\n    return rv;\n  }\n\n  return (nghttp2_ssize)buflen;\n}\n\nssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater,\n                                  const nghttp2_vec *vec, size_t veclen,\n                                  const nghttp2_nv *nv, size_t nvlen) {\n  return (ssize_t)nghttp2_hd_deflate_hd_vec2(deflater, vec, veclen, nv, nvlen);\n}\n\nnghttp2_ssize nghttp2_hd_deflate_hd_vec2(nghttp2_hd_deflater *deflater,\n                                         const nghttp2_vec *vec, size_t veclen,\n                                         const nghttp2_nv *nv, size_t nvlen) {\n  nghttp2_bufs bufs;\n  int rv;\n  nghttp2_mem *mem;\n  size_t buflen;\n\n  mem = deflater->ctx.mem;\n\n  rv = nghttp2_bufs_wrap_init2(&bufs, vec, veclen, mem);\n\n  if (rv != 0) {\n    return rv;\n  }\n\n  rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nv, nvlen);\n\n  buflen = nghttp2_bufs_len(&bufs);\n\n  nghttp2_bufs_wrap_free(&bufs);\n\n  if (rv == NGHTTP2_ERR_BUFFER_ERROR) {\n    return NGHTTP2_ERR_INSUFF_BUFSIZE;\n  }\n\n  if (rv != 0) {\n    return rv;\n  }\n\n  return (nghttp2_ssize)buflen;\n}\n\nsize_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,\n                                const nghttp2_nv *nva, size_t nvlen) {\n  size_t n = 0;\n  size_t i;\n  (void)deflater;\n\n  /* Possible Maximum Header Table Size Change.  Encoding (1u << 31) -\n     1 using 4 bit prefix requires 6 bytes.  We may emit this at most\n     twice. */\n  n += 12;\n\n  /* Use Literal Header Field without indexing - New Name, since it is\n     most space consuming format.  Also we choose the less one between\n     non-huffman and huffman, so using literal byte count is\n     sufficient for upper bound.\n\n     Encoding (1u << 31) - 1 using 7 bit prefix requires 6 bytes.  We\n     need 2 of this for |nvlen| header fields. */\n  n += 6 * 2 * nvlen;\n\n  for (i = 0; i < nvlen; ++i) {\n    n += nva[i].namelen + nva[i].valuelen;\n  }\n\n  return n;\n}\n\nint nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr,\n                           size_t deflate_hd_table_bufsize_max) {\n  return nghttp2_hd_deflate_new2(deflater_ptr, deflate_hd_table_bufsize_max,\n                                 NULL);\n}\n\nint nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr,\n                            size_t deflate_hd_table_bufsize_max,\n                            nghttp2_mem *mem) {\n  int rv;\n  nghttp2_hd_deflater *deflater;\n\n  if (mem == NULL) {\n    mem = nghttp2_mem_default();\n  }\n\n  deflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_deflater));\n\n  if (deflater == NULL) {\n    return NGHTTP2_ERR_NOMEM;\n  }\n\n  rv = nghttp2_hd_deflate_init2(deflater, deflate_hd_table_bufsize_max, mem);\n\n  if (rv != 0) {\n    nghttp2_mem_free(mem, deflater);\n\n    return rv;\n  }\n\n  *deflater_ptr = deflater;\n\n  return 0;\n}\n\nvoid nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater) {\n  nghttp2_mem *mem;\n\n  mem = deflater->ctx.mem;\n\n  nghttp2_hd_deflate_free(deflater);\n\n  nghttp2_mem_free(mem, deflater);\n}\n\nstatic void hd_inflate_set_huffman_encoded(nghttp2_hd_inflater *inflater,\n                                           const uint8_t *in) {\n  inflater->huffman_encoded = (*in & (1 << 7)) != 0;\n}\n\n/*\n * Decodes the integer from the range [in, last).  The result is\n * assigned to |inflater->left|.  If the |inflater->left| is 0, then\n * it performs variable integer decoding from scratch. Otherwise, it\n * uses the |inflater->left| as the initial value and continues to\n * decode assuming that [in, last) begins with intermediary sequence.\n *\n * This function returns the number of bytes read if it succeeds, or\n * one of the following negative error codes:\n *\n * NGHTTP2_ERR_HEADER_COMP\n *   Integer decoding failed\n */\nstatic nghttp2_ssize hd_inflate_read_len(nghttp2_hd_inflater *inflater,\n                                         int *rfin, const uint8_t *in,\n                                         const uint8_t *last, size_t prefix,\n                                         size_t maxlen) {\n  nghttp2_ssize rv;\n  uint32_t out;\n\n  *rfin = 0;\n\n  rv = decode_length(&out, &inflater->shift, rfin, (uint32_t)inflater->left,\n                     inflater->shift, in, last, prefix);\n\n  if (rv == -1) {\n    DEBUGF(\"inflatehd: integer decoding failed\\n\");\n    return NGHTTP2_ERR_HEADER_COMP;\n  }\n\n  if (out > maxlen) {\n    DEBUGF(\"inflatehd: integer exceeded the maximum value %zu\\n\", maxlen);\n    return NGHTTP2_ERR_HEADER_COMP;\n  }\n\n  inflater->left = out;\n\n  DEBUGF(\"inflatehd: decoded integer is %u\\n\", out);\n\n  return rv;\n}\n\n/*\n * Reads |inflater->left| bytes from the range [in, last) and performs\n * huffman decoding against them and pushes the result into the\n * |buffer|.\n *\n * This function returns the number of bytes read if it succeeds, or\n * one of the following negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *   Out of memory\n * NGHTTP2_ERR_HEADER_COMP\n *   Huffman decoding failed\n */\nstatic nghttp2_ssize hd_inflate_read_huff(nghttp2_hd_inflater *inflater,\n                                          nghttp2_buf *buf, const uint8_t *in,\n                                          const uint8_t *last) {\n  nghttp2_ssize readlen;\n  int fin = 0;\n  if ((size_t)(last - in) >= inflater->left) {\n    last = in + inflater->left;\n    fin = 1;\n  }\n  readlen = nghttp2_hd_huff_decode(&inflater->huff_decode_ctx, buf, in,\n                                   (size_t)(last - in), fin);\n\n  if (readlen < 0) {\n    DEBUGF(\"inflatehd: huffman decoding failed\\n\");\n    return readlen;\n  }\n  if (nghttp2_hd_huff_decode_failure_state(&inflater->huff_decode_ctx)) {\n    DEBUGF(\"inflatehd: huffman decoding failed\\n\");\n    return NGHTTP2_ERR_HEADER_COMP;\n  }\n\n  inflater->left -= (size_t)readlen;\n  return readlen;\n}\n\n/*\n * Reads |inflater->left| bytes from the range [in, last) and copies\n * them into the |buffer|.\n *\n * This function returns the number of bytes read if it succeeds, or\n * one of the following negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *   Out of memory\n * NGHTTP2_ERR_HEADER_COMP\n *   Header decompression failed\n */\nstatic nghttp2_ssize hd_inflate_read(nghttp2_hd_inflater *inflater,\n                                     nghttp2_buf *buf, const uint8_t *in,\n                                     const uint8_t *last) {\n  size_t len = nghttp2_min_size((size_t)(last - in), inflater->left);\n\n  buf->last = nghttp2_cpymem(buf->last, in, len);\n\n  inflater->left -= len;\n  return (nghttp2_ssize)len;\n}\n\n/*\n * Finalize indexed header representation reception.  The referenced\n * header is always emitted, and |*nv_out| is filled with that value.\n */\nstatic void hd_inflate_commit_indexed(nghttp2_hd_inflater *inflater,\n                                      nghttp2_hd_nv *nv_out) {\n  nghttp2_hd_nv nv = nghttp2_hd_table_get(&inflater->ctx, inflater->index);\n\n  emit_header(nv_out, &nv);\n}\n\n/*\n * Finalize literal header representation - new name- reception. If\n * header is emitted, |*nv_out| is filled with that value and 0 is\n * returned.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *   Out of memory\n */\nstatic int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater,\n                                     nghttp2_hd_nv *nv_out) {\n  nghttp2_hd_nv nv;\n  int rv;\n\n  if (inflater->no_index) {\n    nv.flags = NGHTTP2_NV_FLAG_NO_INDEX;\n  } else {\n    nv.flags = NGHTTP2_NV_FLAG_NONE;\n  }\n\n  nv.name = inflater->namercbuf;\n  nv.value = inflater->valuercbuf;\n  nv.token = lookup_token(inflater->namercbuf->base, inflater->namercbuf->len);\n\n  if (inflater->index_required) {\n    rv = add_hd_table_incremental(&inflater->ctx, &nv, NULL, 0);\n\n    if (rv != 0) {\n      return rv;\n    }\n  }\n\n  emit_header(nv_out, &nv);\n\n  inflater->nv_name_keep = nv.name;\n  inflater->nv_value_keep = nv.value;\n\n  inflater->namercbuf = NULL;\n  inflater->valuercbuf = NULL;\n\n  return 0;\n}\n\n/*\n * Finalize literal header representation - indexed name-\n * reception. If header is emitted, |*nv_out| is filled with that\n * value and 0 is returned.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *   Out of memory\n */\nstatic int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater,\n                                     nghttp2_hd_nv *nv_out) {\n  nghttp2_hd_nv nv;\n  int rv;\n\n  nv = nghttp2_hd_table_get(&inflater->ctx, inflater->index);\n\n  if (inflater->no_index) {\n    nv.flags = NGHTTP2_NV_FLAG_NO_INDEX;\n  } else {\n    nv.flags = NGHTTP2_NV_FLAG_NONE;\n  }\n\n  nghttp2_rcbuf_incref(nv.name);\n\n  nv.value = inflater->valuercbuf;\n\n  if (inflater->index_required) {\n    rv = add_hd_table_incremental(&inflater->ctx, &nv, NULL, 0);\n    if (rv != 0) {\n      nghttp2_rcbuf_decref(nv.name);\n      return NGHTTP2_ERR_NOMEM;\n    }\n  }\n\n  emit_header(nv_out, &nv);\n\n  inflater->nv_name_keep = nv.name;\n  inflater->nv_value_keep = nv.value;\n\n  inflater->valuercbuf = NULL;\n\n  return 0;\n}\n\nssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,\n                              int *inflate_flags, uint8_t *in, size_t inlen,\n                              int in_final) {\n  return nghttp2_hd_inflate_hd2(inflater, nv_out, inflate_flags, in, inlen,\n                                in_final);\n}\n\nssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,\n                               nghttp2_nv *nv_out, int *inflate_flags,\n                               const uint8_t *in, size_t inlen, int in_final) {\n  return (nghttp2_ssize)nghttp2_hd_inflate_hd3(inflater, nv_out, inflate_flags,\n                                               in, inlen, in_final);\n}\n\nnghttp2_ssize nghttp2_hd_inflate_hd3(nghttp2_hd_inflater *inflater,\n                                     nghttp2_nv *nv_out, int *inflate_flags,\n                                     const uint8_t *in, size_t inlen,\n                                     int in_final) {\n  nghttp2_ssize rv;\n  nghttp2_hd_nv hd_nv;\n\n  rv = nghttp2_hd_inflate_hd_nv(inflater, &hd_nv, inflate_flags, in, inlen,\n                                in_final);\n\n  if (rv < 0) {\n    return rv;\n  }\n\n  if (*inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {\n    nv_out->name = hd_nv.name->base;\n    nv_out->namelen = hd_nv.name->len;\n\n    nv_out->value = hd_nv.value->base;\n    nv_out->valuelen = hd_nv.value->len;\n\n    nv_out->flags = hd_nv.flags;\n  }\n\n  return rv;\n}\n\nnghttp2_ssize nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater,\n                                       nghttp2_hd_nv *nv_out,\n                                       int *inflate_flags, const uint8_t *in,\n                                       size_t inlen, int in_final) {\n  nghttp2_ssize rv = 0;\n  const uint8_t *first = in;\n  const uint8_t *last = in + inlen;\n  int rfin = 0;\n  int busy = 0;\n  nghttp2_mem *mem;\n\n  mem = inflater->ctx.mem;\n\n  if (inflater->ctx.bad) {\n    return NGHTTP2_ERR_HEADER_COMP;\n  }\n\n  DEBUGF(\"inflatehd: start state=%d\\n\", inflater->state);\n  hd_inflate_keep_free(inflater);\n  *inflate_flags = NGHTTP2_HD_INFLATE_NONE;\n  for (; in != last || busy;) {\n    busy = 0;\n    switch (inflater->state) {\n    case NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE:\n      if ((*in & 0xe0u) != 0x20u) {\n        DEBUGF(\"inflatehd: header table size change was expected, but saw \"\n               \"0x%02x as first byte\",\n               *in);\n        rv = NGHTTP2_ERR_HEADER_COMP;\n        goto fail;\n      }\n    /* fall through */\n    case NGHTTP2_HD_STATE_INFLATE_START:\n    case NGHTTP2_HD_STATE_OPCODE:\n      if ((*in & 0xe0u) == 0x20u) {\n        DEBUGF(\"inflatehd: header table size change\\n\");\n        if (inflater->state == NGHTTP2_HD_STATE_OPCODE) {\n          DEBUGF(\"inflatehd: header table size change must appear at the head \"\n                 \"of header block\\n\");\n          rv = NGHTTP2_ERR_HEADER_COMP;\n          goto fail;\n        }\n        inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED;\n        inflater->state = NGHTTP2_HD_STATE_READ_TABLE_SIZE;\n      } else if (*in & 0x80u) {\n        DEBUGF(\"inflatehd: indexed repr\\n\");\n        inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED;\n        inflater->state = NGHTTP2_HD_STATE_READ_INDEX;\n      } else {\n        if (*in == 0x40u || *in == 0 || *in == 0x10u) {\n          DEBUGF(\"inflatehd: literal header repr - new name\\n\");\n          inflater->opcode = NGHTTP2_HD_OPCODE_NEWNAME;\n          inflater->state = NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN;\n        } else {\n          DEBUGF(\"inflatehd: literal header repr - indexed name\\n\");\n          inflater->opcode = NGHTTP2_HD_OPCODE_INDNAME;\n          inflater->state = NGHTTP2_HD_STATE_READ_INDEX;\n        }\n        inflater->index_required = (*in & 0x40) != 0;\n        inflater->no_index = (*in & 0xf0u) == 0x10u;\n        DEBUGF(\"inflatehd: indexing required=%d, no_index=%d\\n\",\n               inflater->index_required, inflater->no_index);\n        if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {\n          ++in;\n        }\n      }\n      inflater->left = 0;\n      inflater->shift = 0;\n      break;\n    case NGHTTP2_HD_STATE_READ_TABLE_SIZE:\n      rfin = 0;\n      rv = hd_inflate_read_len(\n        inflater, &rfin, in, last, 5,\n        nghttp2_min_size(inflater->min_hd_table_bufsize_max,\n                         inflater->settings_hd_table_bufsize_max));\n      if (rv < 0) {\n        goto fail;\n      }\n      in += rv;\n      if (!rfin) {\n        goto almost_ok;\n      }\n      DEBUGF(\"inflatehd: table_size=%zu\\n\", inflater->left);\n      inflater->min_hd_table_bufsize_max = UINT32_MAX;\n      inflater->ctx.hd_table_bufsize_max = inflater->left;\n      hd_context_shrink_table_size(&inflater->ctx, NULL);\n      inflater->state = NGHTTP2_HD_STATE_INFLATE_START;\n      break;\n    case NGHTTP2_HD_STATE_READ_INDEX: {\n      size_t prefixlen;\n\n      if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) {\n        prefixlen = 7;\n      } else if (inflater->index_required) {\n        prefixlen = 6;\n      } else {\n        prefixlen = 4;\n      }\n\n      rfin = 0;\n      rv = hd_inflate_read_len(inflater, &rfin, in, last, prefixlen,\n                               get_max_index(&inflater->ctx));\n      if (rv < 0) {\n        goto fail;\n      }\n\n      in += rv;\n\n      if (!rfin) {\n        goto almost_ok;\n      }\n\n      if (inflater->left == 0) {\n        rv = NGHTTP2_ERR_HEADER_COMP;\n        goto fail;\n      }\n\n      DEBUGF(\"inflatehd: index=%zu\\n\", inflater->left);\n      if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) {\n        inflater->index = inflater->left;\n        --inflater->index;\n\n        hd_inflate_commit_indexed(inflater, nv_out);\n\n        inflater->state = NGHTTP2_HD_STATE_OPCODE;\n        *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT;\n        return (nghttp2_ssize)(in - first);\n      } else {\n        inflater->index = inflater->left;\n        --inflater->index;\n\n        inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;\n      }\n      break;\n    }\n    case NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN:\n      hd_inflate_set_huffman_encoded(inflater, in);\n      inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN;\n      inflater->left = 0;\n      inflater->shift = 0;\n      DEBUGF(\"inflatehd: huffman encoded=%d\\n\", inflater->huffman_encoded != 0);\n    /* Fall through */\n    case NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN:\n      rfin = 0;\n      rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV);\n      if (rv < 0) {\n        goto fail;\n      }\n      in += rv;\n      if (!rfin) {\n        DEBUGF(\"inflatehd: integer not fully decoded. current=%zu\\n\",\n               inflater->left);\n\n        goto almost_ok;\n      }\n\n      if (inflater->huffman_encoded) {\n        nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx);\n\n        inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF;\n\n        rv =\n          nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left * 2 + 1, mem);\n      } else {\n        inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAME;\n        rv = nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left + 1, mem);\n      }\n\n      if (rv != 0) {\n        goto fail;\n      }\n\n      nghttp2_buf_wrap_init(&inflater->namebuf, inflater->namercbuf->base,\n                            inflater->namercbuf->len);\n\n      break;\n    case NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF:\n      rv = hd_inflate_read_huff(inflater, &inflater->namebuf, in, last);\n      if (rv < 0) {\n        goto fail;\n      }\n\n      in += rv;\n\n      DEBUGF(\"inflatehd: %td bytes read\\n\", rv);\n\n      if (inflater->left) {\n        DEBUGF(\"inflatehd: still %zu bytes to go\\n\", inflater->left);\n\n        goto almost_ok;\n      }\n\n      *inflater->namebuf.last = '\\0';\n      inflater->namercbuf->len = nghttp2_buf_len(&inflater->namebuf);\n\n      inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;\n\n      break;\n    case NGHTTP2_HD_STATE_NEWNAME_READ_NAME:\n      rv = hd_inflate_read(inflater, &inflater->namebuf, in, last);\n      if (rv < 0) {\n        goto fail;\n      }\n\n      in += rv;\n\n      DEBUGF(\"inflatehd: %td bytes read\\n\", rv);\n      if (inflater->left) {\n        DEBUGF(\"inflatehd: still %zu bytes to go\\n\", inflater->left);\n\n        goto almost_ok;\n      }\n\n      *inflater->namebuf.last = '\\0';\n      inflater->namercbuf->len = nghttp2_buf_len(&inflater->namebuf);\n\n      inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;\n\n      break;\n    case NGHTTP2_HD_STATE_CHECK_VALUELEN:\n      hd_inflate_set_huffman_encoded(inflater, in);\n      inflater->state = NGHTTP2_HD_STATE_READ_VALUELEN;\n      inflater->left = 0;\n      inflater->shift = 0;\n      DEBUGF(\"inflatehd: huffman encoded=%d\\n\", inflater->huffman_encoded != 0);\n    /* Fall through */\n    case NGHTTP2_HD_STATE_READ_VALUELEN:\n      rfin = 0;\n      rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV);\n      if (rv < 0) {\n        goto fail;\n      }\n\n      in += rv;\n\n      if (!rfin) {\n        goto almost_ok;\n      }\n\n      DEBUGF(\"inflatehd: valuelen=%zu\\n\", inflater->left);\n\n      if (inflater->huffman_encoded) {\n        nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx);\n\n        inflater->state = NGHTTP2_HD_STATE_READ_VALUEHUFF;\n\n        rv =\n          nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left * 2 + 1, mem);\n      } else {\n        inflater->state = NGHTTP2_HD_STATE_READ_VALUE;\n\n        rv = nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left + 1, mem);\n      }\n\n      if (rv != 0) {\n        goto fail;\n      }\n\n      nghttp2_buf_wrap_init(&inflater->valuebuf, inflater->valuercbuf->base,\n                            inflater->valuercbuf->len);\n\n      busy = 1;\n\n      break;\n    case NGHTTP2_HD_STATE_READ_VALUEHUFF:\n      rv = hd_inflate_read_huff(inflater, &inflater->valuebuf, in, last);\n      if (rv < 0) {\n        goto fail;\n      }\n\n      in += rv;\n\n      DEBUGF(\"inflatehd: %td bytes read\\n\", rv);\n\n      if (inflater->left) {\n        DEBUGF(\"inflatehd: still %zu bytes to go\\n\", inflater->left);\n\n        goto almost_ok;\n      }\n\n      *inflater->valuebuf.last = '\\0';\n      inflater->valuercbuf->len = nghttp2_buf_len(&inflater->valuebuf);\n\n      if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {\n        rv = hd_inflate_commit_newname(inflater, nv_out);\n      } else {\n        rv = hd_inflate_commit_indname(inflater, nv_out);\n      }\n\n      if (rv != 0) {\n        goto fail;\n      }\n\n      inflater->state = NGHTTP2_HD_STATE_OPCODE;\n      *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT;\n\n      return (nghttp2_ssize)(in - first);\n    case NGHTTP2_HD_STATE_READ_VALUE:\n      rv = hd_inflate_read(inflater, &inflater->valuebuf, in, last);\n      if (rv < 0) {\n        DEBUGF(\"inflatehd: value read failure %td: %s\\n\", rv,\n               nghttp2_strerror((int)rv));\n        goto fail;\n      }\n\n      in += rv;\n\n      DEBUGF(\"inflatehd: %td bytes read\\n\", rv);\n\n      if (inflater->left) {\n        DEBUGF(\"inflatehd: still %zu bytes to go\\n\", inflater->left);\n        goto almost_ok;\n      }\n\n      *inflater->valuebuf.last = '\\0';\n      inflater->valuercbuf->len = nghttp2_buf_len(&inflater->valuebuf);\n\n      if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {\n        rv = hd_inflate_commit_newname(inflater, nv_out);\n      } else {\n        rv = hd_inflate_commit_indname(inflater, nv_out);\n      }\n\n      if (rv != 0) {\n        goto fail;\n      }\n\n      inflater->state = NGHTTP2_HD_STATE_OPCODE;\n      *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT;\n\n      return (nghttp2_ssize)(in - first);\n    }\n  }\n\n  assert(in == last);\n\n  DEBUGF(\"inflatehd: all input bytes were processed\\n\");\n\n  if (in_final) {\n    DEBUGF(\"inflatehd: in_final set\\n\");\n\n    if (inflater->state != NGHTTP2_HD_STATE_OPCODE &&\n        inflater->state != NGHTTP2_HD_STATE_INFLATE_START) {\n      DEBUGF(\"inflatehd: unacceptable state=%d\\n\", inflater->state);\n      rv = NGHTTP2_ERR_HEADER_COMP;\n\n      goto fail;\n    }\n    *inflate_flags |= NGHTTP2_HD_INFLATE_FINAL;\n  }\n  return (nghttp2_ssize)(in - first);\n\nalmost_ok:\n  if (in_final) {\n    DEBUGF(\"inflatehd: input ended prematurely\\n\");\n\n    rv = NGHTTP2_ERR_HEADER_COMP;\n\n    goto fail;\n  }\n  return (nghttp2_ssize)(in - first);\n\nfail:\n  DEBUGF(\"inflatehd: error return %td\\n\", rv);\n\n  inflater->ctx.bad = 1;\n  return rv;\n}\n\nint nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) {\n  hd_inflate_keep_free(inflater);\n  inflater->state = NGHTTP2_HD_STATE_INFLATE_START;\n  return 0;\n}\n\nint nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr) {\n  return nghttp2_hd_inflate_new2(inflater_ptr, NULL);\n}\n\nint nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr,\n                            nghttp2_mem *mem) {\n  int rv;\n  nghttp2_hd_inflater *inflater;\n\n  if (mem == NULL) {\n    mem = nghttp2_mem_default();\n  }\n\n  inflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_inflater));\n\n  if (inflater == NULL) {\n    return NGHTTP2_ERR_NOMEM;\n  }\n\n  rv = nghttp2_hd_inflate_init(inflater, mem);\n\n  if (rv != 0) {\n    nghttp2_mem_free(mem, inflater);\n\n    return rv;\n  }\n\n  *inflater_ptr = inflater;\n\n  return 0;\n}\n\nvoid nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) {\n  nghttp2_mem *mem;\n\n  mem = inflater->ctx.mem;\n  nghttp2_hd_inflate_free(inflater);\n\n  nghttp2_mem_free(mem, inflater);\n}\n\nint nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t idx,\n                                  nghttp2_nv *nv, int indexing_mode) {\n  return emit_indname_block(bufs, idx, nv, indexing_mode);\n}\n\nint nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv,\n                                  int indexing_mode) {\n  return emit_newname_block(bufs, nv, indexing_mode);\n}\n\nint nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size) {\n  return emit_table_size(bufs, table_size);\n}\n\nnghttp2_ssize nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr,\n                                       int *fin, uint32_t initial, size_t shift,\n                                       uint8_t *in, uint8_t *last,\n                                       size_t prefix) {\n  return decode_length(res, shift_ptr, fin, initial, shift, in, last, prefix);\n}\n\nstatic const nghttp2_nv *hd_get_table_entry(nghttp2_hd_context *context,\n                                            size_t idx) {\n  if (idx == 0) {\n    return NULL;\n  }\n\n  --idx;\n\n  if (!INDEX_RANGE_VALID(context, idx)) {\n    return NULL;\n  }\n\n  return nghttp2_hd_table_get2(context, idx);\n}\n\nsize_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater) {\n  return get_max_index(&deflater->ctx);\n}\n\nconst nghttp2_nv *\nnghttp2_hd_deflate_get_table_entry(nghttp2_hd_deflater *deflater, size_t idx) {\n  return hd_get_table_entry(&deflater->ctx, idx);\n}\n\nsize_t\nnghttp2_hd_deflate_get_dynamic_table_size(nghttp2_hd_deflater *deflater) {\n  return deflater->ctx.hd_table_bufsize;\n}\n\nsize_t\nnghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater) {\n  return deflater->ctx.hd_table_bufsize_max;\n}\n\nsize_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater) {\n  return get_max_index(&inflater->ctx);\n}\n\nconst nghttp2_nv *\nnghttp2_hd_inflate_get_table_entry(nghttp2_hd_inflater *inflater, size_t idx) {\n  return hd_get_table_entry(&inflater->ctx, idx);\n}\n\nsize_t\nnghttp2_hd_inflate_get_dynamic_table_size(nghttp2_hd_inflater *inflater) {\n  return inflater->ctx.hd_table_bufsize;\n}\n\nsize_t\nnghttp2_hd_inflate_get_max_dynamic_table_size(nghttp2_hd_inflater *inflater) {\n  return inflater->ctx.hd_table_bufsize_max;\n}\n"
  },
  {
    "path": "thirdparty/nghttp2/nghttp2_hd.h",
    "content": "/*\n * nghttp2 - HTTP/2 C Library\n *\n * Copyright (c) 2013 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef NGHTTP2_HD_H\n#define NGHTTP2_HD_H\n\n#include \"nghttp2.h\"\n\n#include \"nghttp2_hd_huffman.h\"\n#include \"nghttp2_buf.h\"\n#include \"nghttp2_rcbuf.h\"\n\n#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE\n#define NGHTTP2_HD_ENTRY_OVERHEAD 32\n\n/* The maximum length of one name/value pair.  This is the sum of the\n   length of name and value.  This is not specified by the spec. We\n   just chose the arbitrary size */\n#define NGHTTP2_HD_MAX_NV 65536\n\n/* Default size of maximum table buffer size for encoder. Even if\n   remote decoder notifies larger buffer size for its decoding,\n   encoder only uses the memory up to this value. */\n#define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12)\n\n/* Exported for unit test */\n#define NGHTTP2_STATIC_TABLE_LENGTH 61\n\n/* Generated by genlibtokenlookup.py */\ntypedef enum {\n  NGHTTP2_TOKEN__AUTHORITY = 0,\n  NGHTTP2_TOKEN__METHOD = 1,\n  NGHTTP2_TOKEN__PATH = 3,\n  NGHTTP2_TOKEN__SCHEME = 5,\n  NGHTTP2_TOKEN__STATUS = 7,\n  NGHTTP2_TOKEN_ACCEPT_CHARSET = 14,\n  NGHTTP2_TOKEN_ACCEPT_ENCODING = 15,\n  NGHTTP2_TOKEN_ACCEPT_LANGUAGE = 16,\n  NGHTTP2_TOKEN_ACCEPT_RANGES = 17,\n  NGHTTP2_TOKEN_ACCEPT = 18,\n  NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 19,\n  NGHTTP2_TOKEN_AGE = 20,\n  NGHTTP2_TOKEN_ALLOW = 21,\n  NGHTTP2_TOKEN_AUTHORIZATION = 22,\n  NGHTTP2_TOKEN_CACHE_CONTROL = 23,\n  NGHTTP2_TOKEN_CONTENT_DISPOSITION = 24,\n  NGHTTP2_TOKEN_CONTENT_ENCODING = 25,\n  NGHTTP2_TOKEN_CONTENT_LANGUAGE = 26,\n  NGHTTP2_TOKEN_CONTENT_LENGTH = 27,\n  NGHTTP2_TOKEN_CONTENT_LOCATION = 28,\n  NGHTTP2_TOKEN_CONTENT_RANGE = 29,\n  NGHTTP2_TOKEN_CONTENT_TYPE = 30,\n  NGHTTP2_TOKEN_COOKIE = 31,\n  NGHTTP2_TOKEN_DATE = 32,\n  NGHTTP2_TOKEN_ETAG = 33,\n  NGHTTP2_TOKEN_EXPECT = 34,\n  NGHTTP2_TOKEN_EXPIRES = 35,\n  NGHTTP2_TOKEN_FROM = 36,\n  NGHTTP2_TOKEN_HOST = 37,\n  NGHTTP2_TOKEN_IF_MATCH = 38,\n  NGHTTP2_TOKEN_IF_MODIFIED_SINCE = 39,\n  NGHTTP2_TOKEN_IF_NONE_MATCH = 40,\n  NGHTTP2_TOKEN_IF_RANGE = 41,\n  NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE = 42,\n  NGHTTP2_TOKEN_LAST_MODIFIED = 43,\n  NGHTTP2_TOKEN_LINK = 44,\n  NGHTTP2_TOKEN_LOCATION = 45,\n  NGHTTP2_TOKEN_MAX_FORWARDS = 46,\n  NGHTTP2_TOKEN_PROXY_AUTHENTICATE = 47,\n  NGHTTP2_TOKEN_PROXY_AUTHORIZATION = 48,\n  NGHTTP2_TOKEN_RANGE = 49,\n  NGHTTP2_TOKEN_REFERER = 50,\n  NGHTTP2_TOKEN_REFRESH = 51,\n  NGHTTP2_TOKEN_RETRY_AFTER = 52,\n  NGHTTP2_TOKEN_SERVER = 53,\n  NGHTTP2_TOKEN_SET_COOKIE = 54,\n  NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY = 55,\n  NGHTTP2_TOKEN_TRANSFER_ENCODING = 56,\n  NGHTTP2_TOKEN_USER_AGENT = 57,\n  NGHTTP2_TOKEN_VARY = 58,\n  NGHTTP2_TOKEN_VIA = 59,\n  NGHTTP2_TOKEN_WWW_AUTHENTICATE = 60,\n  NGHTTP2_TOKEN_TE,\n  NGHTTP2_TOKEN_CONNECTION,\n  NGHTTP2_TOKEN_KEEP_ALIVE,\n  NGHTTP2_TOKEN_PROXY_CONNECTION,\n  NGHTTP2_TOKEN_UPGRADE,\n  NGHTTP2_TOKEN__PROTOCOL,\n  NGHTTP2_TOKEN_PRIORITY,\n} nghttp2_token;\n\nstruct nghttp2_hd_entry;\ntypedef struct nghttp2_hd_entry nghttp2_hd_entry;\n\ntypedef struct {\n  /* The buffer containing header field name.  NULL-termination is\n     guaranteed. */\n  nghttp2_rcbuf *name;\n  /* The buffer containing header field value.  NULL-termination is\n     guaranteed. */\n  nghttp2_rcbuf *value;\n  /* nghttp2_token value for name.  It could be -1 if we have no token\n     for that header field name. */\n  int32_t token;\n  /* Bitwise OR of one or more of nghttp2_nv_flag. */\n  uint8_t flags;\n} nghttp2_hd_nv;\n\nstruct nghttp2_hd_entry {\n  /* The header field name/value pair */\n  nghttp2_hd_nv nv;\n  /* This is solely for nghttp2_hd_{deflate,inflate}_get_table_entry\n     APIs to keep backward compatibility. */\n  nghttp2_nv cnv;\n  /* The next entry which shares same bucket in hash table. */\n  nghttp2_hd_entry *next;\n  /* The sequence number.  We will increment it by one whenever we\n     store nghttp2_hd_entry to dynamic header table. */\n  uint32_t seq;\n  /* The hash value for header name (nv.name). */\n  uint32_t hash;\n};\n\n/* The entry used for static header table. */\ntypedef struct {\n  nghttp2_rcbuf name;\n  nghttp2_rcbuf value;\n  nghttp2_nv cnv;\n  int32_t token;\n  uint32_t hash;\n} nghttp2_hd_static_entry;\n\ntypedef struct {\n  nghttp2_hd_entry **buffer;\n  size_t mask;\n  size_t first;\n  size_t len;\n} nghttp2_hd_ringbuf;\n\ntypedef enum {\n  NGHTTP2_HD_OPCODE_NONE,\n  NGHTTP2_HD_OPCODE_INDEXED,\n  NGHTTP2_HD_OPCODE_NEWNAME,\n  NGHTTP2_HD_OPCODE_INDNAME\n} nghttp2_hd_opcode;\n\ntypedef enum {\n  NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE,\n  NGHTTP2_HD_STATE_INFLATE_START,\n  NGHTTP2_HD_STATE_OPCODE,\n  NGHTTP2_HD_STATE_READ_TABLE_SIZE,\n  NGHTTP2_HD_STATE_READ_INDEX,\n  NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN,\n  NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN,\n  NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF,\n  NGHTTP2_HD_STATE_NEWNAME_READ_NAME,\n  NGHTTP2_HD_STATE_CHECK_VALUELEN,\n  NGHTTP2_HD_STATE_READ_VALUELEN,\n  NGHTTP2_HD_STATE_READ_VALUEHUFF,\n  NGHTTP2_HD_STATE_READ_VALUE\n} nghttp2_hd_inflate_state;\n\ntypedef enum {\n  NGHTTP2_HD_WITH_INDEXING,\n  NGHTTP2_HD_WITHOUT_INDEXING,\n  NGHTTP2_HD_NEVER_INDEXING\n} nghttp2_hd_indexing_mode;\n\ntypedef struct {\n  /* dynamic header table */\n  nghttp2_hd_ringbuf hd_table;\n  /* Memory allocator */\n  nghttp2_mem *mem;\n  /* Abstract buffer size of hd_table as described in the spec. This\n     is the sum of length of name/value in hd_table +\n     NGHTTP2_HD_ENTRY_OVERHEAD bytes overhead per each entry. */\n  size_t hd_table_bufsize;\n  /* The effective header table size. */\n  size_t hd_table_bufsize_max;\n  /* Next sequence number for nghttp2_hd_entry */\n  uint32_t next_seq;\n  /* If inflate/deflate error occurred, this value is set to 1 and\n     further invocation of inflate/deflate will fail with\n     NGHTTP2_ERR_HEADER_COMP. */\n  uint8_t bad;\n} nghttp2_hd_context;\n\n#define HD_MAP_SIZE 128\n\ntypedef struct {\n  nghttp2_hd_entry *table[HD_MAP_SIZE];\n} nghttp2_hd_map;\n\nstruct nghttp2_hd_deflater {\n  nghttp2_hd_context ctx;\n  nghttp2_hd_map map;\n  /* The upper limit of the header table size the deflater accepts. */\n  size_t deflate_hd_table_bufsize_max;\n  /* Minimum header table size notified in the next context update */\n  size_t min_hd_table_bufsize_max;\n  /* If nonzero, send header table size using encoding context update\n     in the next deflate process */\n  uint8_t notify_table_size_change;\n};\n\nstruct nghttp2_hd_inflater {\n  nghttp2_hd_context ctx;\n  /* Stores current state of huffman decoding */\n  nghttp2_hd_huff_decode_context huff_decode_ctx;\n  /* header buffer */\n  nghttp2_buf namebuf, valuebuf;\n  nghttp2_rcbuf *namercbuf, *valuercbuf;\n  /* Pointer to the name/value pair which are used in the current\n     header emission. */\n  nghttp2_rcbuf *nv_name_keep, *nv_value_keep;\n  /* The number of bytes to read */\n  size_t left;\n  /* The index in indexed repr or indexed name */\n  size_t index;\n  /* The maximum header table size the inflater supports. This is the\n     same value transmitted in SETTINGS_HEADER_TABLE_SIZE */\n  size_t settings_hd_table_bufsize_max;\n  /* Minimum header table size set by nghttp2_hd_inflate_change_table_size */\n  size_t min_hd_table_bufsize_max;\n  /* The number of next shift to decode integer */\n  size_t shift;\n  nghttp2_hd_opcode opcode;\n  nghttp2_hd_inflate_state state;\n  /* nonzero if string is huffman encoded */\n  uint8_t huffman_encoded;\n  /* nonzero if deflater requires that current entry is indexed */\n  uint8_t index_required;\n  /* nonzero if deflater requires that current entry must not be\n     indexed */\n  uint8_t no_index;\n};\n\n/*\n * Initializes the |ent| members.  The reference counts of nv->name\n * and nv->value are increased by one for each.\n */\nvoid nghttp2_hd_entry_init(nghttp2_hd_entry *ent, nghttp2_hd_nv *nv);\n\n/*\n * This function decreases the reference counts of nv->name and\n * nv->value.\n */\nvoid nghttp2_hd_entry_free(nghttp2_hd_entry *ent);\n\n/*\n * Initializes |deflater| for deflating name/values pairs.\n *\n * The encoder only uses up to\n * NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE bytes for header table\n * even if the larger value is specified later in\n * nghttp2_hd_change_table_size().\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *     Out of memory.\n */\nint nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem);\n\n/*\n * Initializes |deflater| for deflating name/values pairs.\n *\n * The encoder only uses up to |max_deflate_dynamic_table_size| bytes\n * for header table even if the larger value is specified later in\n * nghttp2_hd_change_table_size().\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *     Out of memory.\n */\nint nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater,\n                             size_t max_deflate_dynamic_table_size,\n                             nghttp2_mem *mem);\n\n/*\n * Deallocates any resources allocated for |deflater|.\n */\nvoid nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater);\n\n/*\n * Deflates the |nva|, which has the |nvlen| name/value pairs, into\n * the |bufs|.\n *\n * This function expands |bufs| as necessary to store the result. If\n * buffers is full and the process still requires more space, this\n * function fails and returns NGHTTP2_ERR_HEADER_COMP.\n *\n * After this function returns, it is safe to delete the |nva|.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *     Out of memory.\n * NGHTTP2_ERR_HEADER_COMP\n *     Deflation process has failed.\n * NGHTTP2_ERR_BUFFER_ERROR\n *     Out of buffer space.\n */\nint nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater,\n                               nghttp2_bufs *bufs, const nghttp2_nv *nva,\n                               size_t nvlen);\n\n/*\n * Initializes |inflater| for inflating name/values pairs.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`NGHTTP2_ERR_NOMEM`\n *     Out of memory.\n */\nint nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem);\n\n/*\n * Deallocates any resources allocated for |inflater|.\n */\nvoid nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater);\n\n/*\n * Similar to nghttp2_hd_inflate_hd(), but this takes nghttp2_hd_nv\n * instead of nghttp2_nv as output parameter |nv_out|.  Other than\n * that return values and semantics are the same as\n * nghttp2_hd_inflate_hd().\n */\nnghttp2_ssize nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater,\n                                       nghttp2_hd_nv *nv_out,\n                                       int *inflate_flags, const uint8_t *in,\n                                       size_t inlen, int in_final);\n\n/* For unittesting purpose */\nint nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index,\n                                  nghttp2_nv *nv, int indexing_mode);\n\n/* For unittesting purpose */\nint nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv,\n                                  int indexing_mode);\n\n/* For unittesting purpose */\nint nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size);\n\n/* For unittesting purpose */\nnghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t index);\n\n/* For unittesting purpose */\nnghttp2_ssize nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr,\n                                       int *fin, uint32_t initial, size_t shift,\n                                       uint8_t *in, uint8_t *last,\n                                       size_t prefix);\n\n/* Huffman encoding/decoding functions */\n\n/*\n * Counts the required bytes to encode |src| with length |len|.\n *\n * This function returns the number of required bytes to encode given\n * data, including padding of prefix of terminal symbol code. This\n * function always succeeds.\n */\nsize_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len);\n\n/*\n * Encodes the given data |src| with length |srclen| to the |bufs|.\n * This function expands extra buffers in |bufs| if necessary.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM\n *     Out of memory.\n * NGHTTP2_ERR_BUFFER_ERROR\n *     Out of buffer space.\n */\nint nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src,\n                           size_t srclen);\n\nvoid nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx);\n\n/*\n * Decodes the given data |src| with length |srclen|.  The |ctx| must\n * be initialized by nghttp2_hd_huff_decode_context_init(). The result\n * will be written to |buf|.  This function assumes that |buf| has the\n * enough room to store the decoded byte string.\n *\n * The caller must set the |fin| to nonzero if the given input is the\n * final block.\n *\n * This function returns the number of read bytes from the |in|.\n *\n * If this function fails, it returns one of the following negative\n * return codes:\n *\n * NGHTTP2_ERR_NOMEM\n *     Out of memory.\n * NGHTTP2_ERR_HEADER_COMP\n *     Decoding process has failed.\n */\nnghttp2_ssize nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,\n                                     nghttp2_buf *buf, const uint8_t *src,\n                                     size_t srclen, int fin);\n\n/*\n * nghttp2_hd_huff_decode_failure_state returns nonzero if |ctx|\n * indicates that huffman decoding context is in failure state.\n */\nint nghttp2_hd_huff_decode_failure_state(nghttp2_hd_huff_decode_context *ctx);\n\n#endif /* NGHTTP2_HD_H */\n"
  },
  {
    "path": "thirdparty/nghttp2/nghttp2_hd_huffman.c",
    "content": "/*\n * nghttp2 - HTTP/2 C Library\n *\n * Copyright (c) 2013 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"nghttp2_hd_huffman.h\"\n\n#include <string.h>\n#include <assert.h>\n#include <stdio.h>\n\n#include \"nghttp2_hd.h\"\n\nsize_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) {\n  size_t i;\n  size_t nbits = 0;\n\n  for (i = 0; i < len; ++i) {\n    nbits += huff_sym_table[src[i]].nbits;\n  }\n  /* pad the prefix of EOS (256) */\n  return (nbits + 7) / 8;\n}\n\nint nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src,\n                           size_t srclen) {\n  const nghttp2_huff_sym *sym;\n  const uint8_t *end = src + srclen;\n  uint64_t code = 0;\n  uint32_t x;\n  size_t nbits = 0;\n  size_t avail;\n  int rv;\n\n  avail = nghttp2_bufs_cur_avail(bufs);\n\n  for (; src != end;) {\n    sym = &huff_sym_table[*src++];\n    code |= (uint64_t)sym->code << (32 - nbits);\n    nbits += sym->nbits;\n    if (nbits < 32) {\n      continue;\n    }\n    if (avail >= 4) {\n      x = htonl((uint32_t)(code >> 32));\n      memcpy(bufs->cur->buf.last, &x, 4);\n      bufs->cur->buf.last += 4;\n      avail -= 4;\n      code <<= 32;\n      nbits -= 32;\n      continue;\n    }\n\n    for (; nbits >= 8;) {\n      rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 56));\n      if (rv != 0) {\n        return rv;\n      }\n      code <<= 8;\n      nbits -= 8;\n    }\n\n    avail = nghttp2_bufs_cur_avail(bufs);\n  }\n\n  for (; nbits >= 8;) {\n    rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 56));\n    if (rv != 0) {\n      return rv;\n    }\n    code <<= 8;\n    nbits -= 8;\n  }\n\n  if (nbits) {\n    rv = nghttp2_bufs_addb(\n      bufs, (uint8_t)((uint8_t)(code >> 56) | ((1 << (8 - nbits)) - 1)));\n    if (rv != 0) {\n      return rv;\n    }\n  }\n\n  return 0;\n}\n\nvoid nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) {\n  ctx->fstate = NGHTTP2_HUFF_ACCEPTED;\n}\n\nnghttp2_ssize nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,\n                                     nghttp2_buf *buf, const uint8_t *src,\n                                     size_t srclen, int final) {\n  const uint8_t *end = src + srclen;\n  nghttp2_huff_decode node = {ctx->fstate, 0};\n  const nghttp2_huff_decode *t = &node;\n  uint8_t c;\n\n  /* We use the decoding algorithm described in\n      - http://graphics.ics.uci.edu/pub/Prefix.pdf [!!! NO LONGER VALID !!!]\n      - https://ics.uci.edu/~dan/pubs/Prefix.pdf\n      - https://github.com/nghttp2/nghttp2/files/15141264/Prefix.pdf */\n  for (; src != end;) {\n    c = *src++;\n    t = &huff_decode_table[t->fstate & 0x1ff][c >> 4];\n    if (t->fstate & NGHTTP2_HUFF_SYM) {\n      *buf->last++ = t->sym;\n    }\n\n    t = &huff_decode_table[t->fstate & 0x1ff][c & 0xf];\n    if (t->fstate & NGHTTP2_HUFF_SYM) {\n      *buf->last++ = t->sym;\n    }\n  }\n\n  ctx->fstate = t->fstate;\n\n  if (final && !(ctx->fstate & NGHTTP2_HUFF_ACCEPTED)) {\n    return NGHTTP2_ERR_HEADER_COMP;\n  }\n\n  return (nghttp2_ssize)srclen;\n}\n\nint nghttp2_hd_huff_decode_failure_state(nghttp2_hd_huff_decode_context *ctx) {\n  return ctx->fstate == 0x100;\n}\n"
  },
  {
    "path": "thirdparty/nghttp2/nghttp2_hd_huffman.h",
    "content": "/*\n * nghttp2 - HTTP/2 C Library\n *\n * Copyright (c) 2013 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef NGHTTP2_HD_HUFFMAN_H\n#define NGHTTP2_HD_HUFFMAN_H\n\n#include <stdint.h>\n#include <arpa/inet.h>\n\ntypedef enum {\n  /* FSA accepts this state as the end of huffman encoding\n     sequence. */\n  NGHTTP2_HUFF_ACCEPTED = 1 << 14,\n  /* This state emits symbol */\n  NGHTTP2_HUFF_SYM = 1 << 15,\n} nghttp2_huff_decode_flag;\n\ntypedef struct {\n  /* fstate is the current huffman decoding state, which is actually\n     the node ID of internal huffman tree with\n     nghttp2_huff_decode_flag OR-ed.  We have 257 leaf nodes, but they\n     are identical to root node other than emitting a symbol, so we\n     have 256 internal nodes [1..255], inclusive.  The node ID 256 is\n     a special node and it is a terminal state that means decoding\n     failed. */\n  uint16_t fstate;\n  /* symbol if NGHTTP2_HUFF_SYM flag set */\n  uint8_t sym;\n} nghttp2_huff_decode;\n\ntypedef nghttp2_huff_decode huff_decode_table_type[16];\n\ntypedef struct {\n  /* fstate is the current huffman decoding state. */\n  uint16_t fstate;\n} nghttp2_hd_huff_decode_context;\n\ntypedef struct {\n  /* The number of bits in this code */\n  uint32_t nbits;\n  /* Huffman code aligned to LSB */\n  uint32_t code;\n} nghttp2_huff_sym;\n\nextern const nghttp2_huff_sym huff_sym_table[];\nextern const nghttp2_huff_decode huff_decode_table[][16];\n\n#endif /* NGHTTP2_HD_HUFFMAN_H */\n"
  },
  {
    "path": "thirdparty/nghttp2/nghttp2_hd_huffman_data.c",
    "content": "/*\n * nghttp2 - HTTP/2 C Library\n *\n * Copyright (c) 2013 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"nghttp2_hd_huffman.h\"\n\n/* Generated by mkhufftbl.py */\n\nconst nghttp2_huff_sym huff_sym_table[] = {\n  {13, 0xffc00000u}, {23, 0xffffb000u}, {28, 0xfffffe20u}, {28, 0xfffffe30u},\n  {28, 0xfffffe40u}, {28, 0xfffffe50u}, {28, 0xfffffe60u}, {28, 0xfffffe70u},\n  {28, 0xfffffe80u}, {24, 0xffffea00u}, {30, 0xfffffff0u}, {28, 0xfffffe90u},\n  {28, 0xfffffea0u}, {30, 0xfffffff4u}, {28, 0xfffffeb0u}, {28, 0xfffffec0u},\n  {28, 0xfffffed0u}, {28, 0xfffffee0u}, {28, 0xfffffef0u}, {28, 0xffffff00u},\n  {28, 0xffffff10u}, {28, 0xffffff20u}, {30, 0xfffffff8u}, {28, 0xffffff30u},\n  {28, 0xffffff40u}, {28, 0xffffff50u}, {28, 0xffffff60u}, {28, 0xffffff70u},\n  {28, 0xffffff80u}, {28, 0xffffff90u}, {28, 0xffffffa0u}, {28, 0xffffffb0u},\n  {6, 0x50000000u},  {10, 0xfe000000u}, {10, 0xfe400000u}, {12, 0xffa00000u},\n  {13, 0xffc80000u}, {6, 0x54000000u},  {8, 0xf8000000u},  {11, 0xff400000u},\n  {10, 0xfe800000u}, {10, 0xfec00000u}, {8, 0xf9000000u},  {11, 0xff600000u},\n  {8, 0xfa000000u},  {6, 0x58000000u},  {6, 0x5c000000u},  {6, 0x60000000u},\n  {5, 0x0u},         {5, 0x8000000u},   {5, 0x10000000u},  {6, 0x64000000u},\n  {6, 0x68000000u},  {6, 0x6c000000u},  {6, 0x70000000u},  {6, 0x74000000u},\n  {6, 0x78000000u},  {6, 0x7c000000u},  {7, 0xb8000000u},  {8, 0xfb000000u},\n  {15, 0xfff80000u}, {6, 0x80000000u},  {12, 0xffb00000u}, {10, 0xff000000u},\n  {13, 0xffd00000u}, {6, 0x84000000u},  {7, 0xba000000u},  {7, 0xbc000000u},\n  {7, 0xbe000000u},  {7, 0xc0000000u},  {7, 0xc2000000u},  {7, 0xc4000000u},\n  {7, 0xc6000000u},  {7, 0xc8000000u},  {7, 0xca000000u},  {7, 0xcc000000u},\n  {7, 0xce000000u},  {7, 0xd0000000u},  {7, 0xd2000000u},  {7, 0xd4000000u},\n  {7, 0xd6000000u},  {7, 0xd8000000u},  {7, 0xda000000u},  {7, 0xdc000000u},\n  {7, 0xde000000u},  {7, 0xe0000000u},  {7, 0xe2000000u},  {7, 0xe4000000u},\n  {8, 0xfc000000u},  {7, 0xe6000000u},  {8, 0xfd000000u},  {13, 0xffd80000u},\n  {19, 0xfffe0000u}, {13, 0xffe00000u}, {14, 0xfff00000u}, {6, 0x88000000u},\n  {15, 0xfffa0000u}, {5, 0x18000000u},  {6, 0x8c000000u},  {5, 0x20000000u},\n  {6, 0x90000000u},  {5, 0x28000000u},  {6, 0x94000000u},  {6, 0x98000000u},\n  {6, 0x9c000000u},  {5, 0x30000000u},  {7, 0xe8000000u},  {7, 0xea000000u},\n  {6, 0xa0000000u},  {6, 0xa4000000u},  {6, 0xa8000000u},  {5, 0x38000000u},\n  {6, 0xac000000u},  {7, 0xec000000u},  {6, 0xb0000000u},  {5, 0x40000000u},\n  {5, 0x48000000u},  {6, 0xb4000000u},  {7, 0xee000000u},  {7, 0xf0000000u},\n  {7, 0xf2000000u},  {7, 0xf4000000u},  {7, 0xf6000000u},  {15, 0xfffc0000u},\n  {11, 0xff800000u}, {14, 0xfff40000u}, {13, 0xffe80000u}, {28, 0xffffffc0u},\n  {20, 0xfffe6000u}, {22, 0xffff4800u}, {20, 0xfffe7000u}, {20, 0xfffe8000u},\n  {22, 0xffff4c00u}, {22, 0xffff5000u}, {22, 0xffff5400u}, {23, 0xffffb200u},\n  {22, 0xffff5800u}, {23, 0xffffb400u}, {23, 0xffffb600u}, {23, 0xffffb800u},\n  {23, 0xffffba00u}, {23, 0xffffbc00u}, {24, 0xffffeb00u}, {23, 0xffffbe00u},\n  {24, 0xffffec00u}, {24, 0xffffed00u}, {22, 0xffff5c00u}, {23, 0xffffc000u},\n  {24, 0xffffee00u}, {23, 0xffffc200u}, {23, 0xffffc400u}, {23, 0xffffc600u},\n  {23, 0xffffc800u}, {21, 0xfffee000u}, {22, 0xffff6000u}, {23, 0xffffca00u},\n  {22, 0xffff6400u}, {23, 0xffffcc00u}, {23, 0xffffce00u}, {24, 0xffffef00u},\n  {22, 0xffff6800u}, {21, 0xfffee800u}, {20, 0xfffe9000u}, {22, 0xffff6c00u},\n  {22, 0xffff7000u}, {23, 0xffffd000u}, {23, 0xffffd200u}, {21, 0xfffef000u},\n  {23, 0xffffd400u}, {22, 0xffff7400u}, {22, 0xffff7800u}, {24, 0xfffff000u},\n  {21, 0xfffef800u}, {22, 0xffff7c00u}, {23, 0xffffd600u}, {23, 0xffffd800u},\n  {21, 0xffff0000u}, {21, 0xffff0800u}, {22, 0xffff8000u}, {21, 0xffff1000u},\n  {23, 0xffffda00u}, {22, 0xffff8400u}, {23, 0xffffdc00u}, {23, 0xffffde00u},\n  {20, 0xfffea000u}, {22, 0xffff8800u}, {22, 0xffff8c00u}, {22, 0xffff9000u},\n  {23, 0xffffe000u}, {22, 0xffff9400u}, {22, 0xffff9800u}, {23, 0xffffe200u},\n  {26, 0xfffff800u}, {26, 0xfffff840u}, {20, 0xfffeb000u}, {19, 0xfffe2000u},\n  {22, 0xffff9c00u}, {23, 0xffffe400u}, {22, 0xffffa000u}, {25, 0xfffff600u},\n  {26, 0xfffff880u}, {26, 0xfffff8c0u}, {26, 0xfffff900u}, {27, 0xfffffbc0u},\n  {27, 0xfffffbe0u}, {26, 0xfffff940u}, {24, 0xfffff100u}, {25, 0xfffff680u},\n  {19, 0xfffe4000u}, {21, 0xffff1800u}, {26, 0xfffff980u}, {27, 0xfffffc00u},\n  {27, 0xfffffc20u}, {26, 0xfffff9c0u}, {27, 0xfffffc40u}, {24, 0xfffff200u},\n  {21, 0xffff2000u}, {21, 0xffff2800u}, {26, 0xfffffa00u}, {26, 0xfffffa40u},\n  {28, 0xffffffd0u}, {27, 0xfffffc60u}, {27, 0xfffffc80u}, {27, 0xfffffca0u},\n  {20, 0xfffec000u}, {24, 0xfffff300u}, {20, 0xfffed000u}, {21, 0xffff3000u},\n  {22, 0xffffa400u}, {21, 0xffff3800u}, {21, 0xffff4000u}, {23, 0xffffe600u},\n  {22, 0xffffa800u}, {22, 0xffffac00u}, {25, 0xfffff700u}, {25, 0xfffff780u},\n  {24, 0xfffff400u}, {24, 0xfffff500u}, {26, 0xfffffa80u}, {23, 0xffffe800u},\n  {26, 0xfffffac0u}, {27, 0xfffffcc0u}, {26, 0xfffffb00u}, {26, 0xfffffb40u},\n  {27, 0xfffffce0u}, {27, 0xfffffd00u}, {27, 0xfffffd20u}, {27, 0xfffffd40u},\n  {27, 0xfffffd60u}, {28, 0xffffffe0u}, {27, 0xfffffd80u}, {27, 0xfffffda0u},\n  {27, 0xfffffdc0u}, {27, 0xfffffde0u}, {27, 0xfffffe00u}, {26, 0xfffffb80u},\n  {30, 0xfffffffcu}};\n\nconst nghttp2_huff_decode huff_decode_table[][16] = {\n  /* 0 */\n  {\n    {0x04, 0},\n    {0x05, 0},\n    {0x07, 0},\n    {0x08, 0},\n    {0x0b, 0},\n    {0x0c, 0},\n    {0x10, 0},\n    {0x13, 0},\n    {0x19, 0},\n    {0x1c, 0},\n    {0x20, 0},\n    {0x23, 0},\n    {0x2a, 0},\n    {0x31, 0},\n    {0x39, 0},\n    {0x4040, 0},\n  },\n  /* 1 */\n  {\n    {0xc000, 48},\n    {0xc000, 49},\n    {0xc000, 50},\n    {0xc000, 97},\n    {0xc000, 99},\n    {0xc000, 101},\n    {0xc000, 105},\n    {0xc000, 111},\n    {0xc000, 115},\n    {0xc000, 116},\n    {0x0d, 0},\n    {0x0e, 0},\n    {0x11, 0},\n    {0x12, 0},\n    {0x14, 0},\n    {0x15, 0},\n  },\n  /* 2 */\n  {\n    {0x8001, 48},\n    {0xc016, 48},\n    {0x8001, 49},\n    {0xc016, 49},\n    {0x8001, 50},\n    {0xc016, 50},\n    {0x8001, 97},\n    {0xc016, 97},\n    {0x8001, 99},\n    {0xc016, 99},\n    {0x8001, 101},\n    {0xc016, 101},\n    {0x8001, 105},\n    {0xc016, 105},\n    {0x8001, 111},\n    {0xc016, 111},\n  },\n  /* 3 */\n  {\n    {0x8002, 48},\n    {0x8009, 48},\n    {0x8017, 48},\n    {0xc028, 48},\n    {0x8002, 49},\n    {0x8009, 49},\n    {0x8017, 49},\n    {0xc028, 49},\n    {0x8002, 50},\n    {0x8009, 50},\n    {0x8017, 50},\n    {0xc028, 50},\n    {0x8002, 97},\n    {0x8009, 97},\n    {0x8017, 97},\n    {0xc028, 97},\n  },\n  /* 4 */\n  {\n    {0x8003, 48},\n    {0x8006, 48},\n    {0x800a, 48},\n    {0x800f, 48},\n    {0x8018, 48},\n    {0x801f, 48},\n    {0x8029, 48},\n    {0xc038, 48},\n    {0x8003, 49},\n    {0x8006, 49},\n    {0x800a, 49},\n    {0x800f, 49},\n    {0x8018, 49},\n    {0x801f, 49},\n    {0x8029, 49},\n    {0xc038, 49},\n  },\n  /* 5 */\n  {\n    {0x8003, 50},\n    {0x8006, 50},\n    {0x800a, 50},\n    {0x800f, 50},\n    {0x8018, 50},\n    {0x801f, 50},\n    {0x8029, 50},\n    {0xc038, 50},\n    {0x8003, 97},\n    {0x8006, 97},\n    {0x800a, 97},\n    {0x800f, 97},\n    {0x8018, 97},\n    {0x801f, 97},\n    {0x8029, 97},\n    {0xc038, 97},\n  },\n  /* 6 */\n  {\n    {0x8002, 99},\n    {0x8009, 99},\n    {0x8017, 99},\n    {0xc028, 99},\n    {0x8002, 101},\n    {0x8009, 101},\n    {0x8017, 101},\n    {0xc028, 101},\n    {0x8002, 105},\n    {0x8009, 105},\n    {0x8017, 105},\n    {0xc028, 105},\n    {0x8002, 111},\n    {0x8009, 111},\n    {0x8017, 111},\n    {0xc028, 111},\n  },\n  /* 7 */\n  {\n    {0x8003, 99},\n    {0x8006, 99},\n    {0x800a, 99},\n    {0x800f, 99},\n    {0x8018, 99},\n    {0x801f, 99},\n    {0x8029, 99},\n    {0xc038, 99},\n    {0x8003, 101},\n    {0x8006, 101},\n    {0x800a, 101},\n    {0x800f, 101},\n    {0x8018, 101},\n    {0x801f, 101},\n    {0x8029, 101},\n    {0xc038, 101},\n  },\n  /* 8 */\n  {\n    {0x8003, 105},\n    {0x8006, 105},\n    {0x800a, 105},\n    {0x800f, 105},\n    {0x8018, 105},\n    {0x801f, 105},\n    {0x8029, 105},\n    {0xc038, 105},\n    {0x8003, 111},\n    {0x8006, 111},\n    {0x800a, 111},\n    {0x800f, 111},\n    {0x8018, 111},\n    {0x801f, 111},\n    {0x8029, 111},\n    {0xc038, 111},\n  },\n  /* 9 */\n  {\n    {0x8001, 115},\n    {0xc016, 115},\n    {0x8001, 116},\n    {0xc016, 116},\n    {0xc000, 32},\n    {0xc000, 37},\n    {0xc000, 45},\n    {0xc000, 46},\n    {0xc000, 47},\n    {0xc000, 51},\n    {0xc000, 52},\n    {0xc000, 53},\n    {0xc000, 54},\n    {0xc000, 55},\n    {0xc000, 56},\n    {0xc000, 57},\n  },\n  /* 10 */\n  {\n    {0x8002, 115},\n    {0x8009, 115},\n    {0x8017, 115},\n    {0xc028, 115},\n    {0x8002, 116},\n    {0x8009, 116},\n    {0x8017, 116},\n    {0xc028, 116},\n    {0x8001, 32},\n    {0xc016, 32},\n    {0x8001, 37},\n    {0xc016, 37},\n    {0x8001, 45},\n    {0xc016, 45},\n    {0x8001, 46},\n    {0xc016, 46},\n  },\n  /* 11 */\n  {\n    {0x8003, 115},\n    {0x8006, 115},\n    {0x800a, 115},\n    {0x800f, 115},\n    {0x8018, 115},\n    {0x801f, 115},\n    {0x8029, 115},\n    {0xc038, 115},\n    {0x8003, 116},\n    {0x8006, 116},\n    {0x800a, 116},\n    {0x800f, 116},\n    {0x8018, 116},\n    {0x801f, 116},\n    {0x8029, 116},\n    {0xc038, 116},\n  },\n  /* 12 */\n  {\n    {0x8002, 32},\n    {0x8009, 32},\n    {0x8017, 32},\n    {0xc028, 32},\n    {0x8002, 37},\n    {0x8009, 37},\n    {0x8017, 37},\n    {0xc028, 37},\n    {0x8002, 45},\n    {0x8009, 45},\n    {0x8017, 45},\n    {0xc028, 45},\n    {0x8002, 46},\n    {0x8009, 46},\n    {0x8017, 46},\n    {0xc028, 46},\n  },\n  /* 13 */\n  {\n    {0x8003, 32},\n    {0x8006, 32},\n    {0x800a, 32},\n    {0x800f, 32},\n    {0x8018, 32},\n    {0x801f, 32},\n    {0x8029, 32},\n    {0xc038, 32},\n    {0x8003, 37},\n    {0x8006, 37},\n    {0x800a, 37},\n    {0x800f, 37},\n    {0x8018, 37},\n    {0x801f, 37},\n    {0x8029, 37},\n    {0xc038, 37},\n  },\n  /* 14 */\n  {\n    {0x8003, 45},\n    {0x8006, 45},\n    {0x800a, 45},\n    {0x800f, 45},\n    {0x8018, 45},\n    {0x801f, 45},\n    {0x8029, 45},\n    {0xc038, 45},\n    {0x8003, 46},\n    {0x8006, 46},\n    {0x800a, 46},\n    {0x800f, 46},\n    {0x8018, 46},\n    {0x801f, 46},\n    {0x8029, 46},\n    {0xc038, 46},\n  },\n  /* 15 */\n  {\n    {0x8001, 47},\n    {0xc016, 47},\n    {0x8001, 51},\n    {0xc016, 51},\n    {0x8001, 52},\n    {0xc016, 52},\n    {0x8001, 53},\n    {0xc016, 53},\n    {0x8001, 54},\n    {0xc016, 54},\n    {0x8001, 55},\n    {0xc016, 55},\n    {0x8001, 56},\n    {0xc016, 56},\n    {0x8001, 57},\n    {0xc016, 57},\n  },\n  /* 16 */\n  {\n    {0x8002, 47},\n    {0x8009, 47},\n    {0x8017, 47},\n    {0xc028, 47},\n    {0x8002, 51},\n    {0x8009, 51},\n    {0x8017, 51},\n    {0xc028, 51},\n    {0x8002, 52},\n    {0x8009, 52},\n    {0x8017, 52},\n    {0xc028, 52},\n    {0x8002, 53},\n    {0x8009, 53},\n    {0x8017, 53},\n    {0xc028, 53},\n  },\n  /* 17 */\n  {\n    {0x8003, 47},\n    {0x8006, 47},\n    {0x800a, 47},\n    {0x800f, 47},\n    {0x8018, 47},\n    {0x801f, 47},\n    {0x8029, 47},\n    {0xc038, 47},\n    {0x8003, 51},\n    {0x8006, 51},\n    {0x800a, 51},\n    {0x800f, 51},\n    {0x8018, 51},\n    {0x801f, 51},\n    {0x8029, 51},\n    {0xc038, 51},\n  },\n  /* 18 */\n  {\n    {0x8003, 52},\n    {0x8006, 52},\n    {0x800a, 52},\n    {0x800f, 52},\n    {0x8018, 52},\n    {0x801f, 52},\n    {0x8029, 52},\n    {0xc038, 52},\n    {0x8003, 53},\n    {0x8006, 53},\n    {0x800a, 53},\n    {0x800f, 53},\n    {0x8018, 53},\n    {0x801f, 53},\n    {0x8029, 53},\n    {0xc038, 53},\n  },\n  /* 19 */\n  {\n    {0x8002, 54},\n    {0x8009, 54},\n    {0x8017, 54},\n    {0xc028, 54},\n    {0x8002, 55},\n    {0x8009, 55},\n    {0x8017, 55},\n    {0xc028, 55},\n    {0x8002, 56},\n    {0x8009, 56},\n    {0x8017, 56},\n    {0xc028, 56},\n    {0x8002, 57},\n    {0x8009, 57},\n    {0x8017, 57},\n    {0xc028, 57},\n  },\n  /* 20 */\n  {\n    {0x8003, 54},\n    {0x8006, 54},\n    {0x800a, 54},\n    {0x800f, 54},\n    {0x8018, 54},\n    {0x801f, 54},\n    {0x8029, 54},\n    {0xc038, 54},\n    {0x8003, 55},\n    {0x8006, 55},\n    {0x800a, 55},\n    {0x800f, 55},\n    {0x8018, 55},\n    {0x801f, 55},\n    {0x8029, 55},\n    {0xc038, 55},\n  },\n  /* 21 */\n  {\n    {0x8003, 56},\n    {0x8006, 56},\n    {0x800a, 56},\n    {0x800f, 56},\n    {0x8018, 56},\n    {0x801f, 56},\n    {0x8029, 56},\n    {0xc038, 56},\n    {0x8003, 57},\n    {0x8006, 57},\n    {0x800a, 57},\n    {0x800f, 57},\n    {0x8018, 57},\n    {0x801f, 57},\n    {0x8029, 57},\n    {0xc038, 57},\n  },\n  /* 22 */\n  {\n    {0x1a, 0},\n    {0x1b, 0},\n    {0x1d, 0},\n    {0x1e, 0},\n    {0x21, 0},\n    {0x22, 0},\n    {0x24, 0},\n    {0x25, 0},\n    {0x2b, 0},\n    {0x2e, 0},\n    {0x32, 0},\n    {0x35, 0},\n    {0x3a, 0},\n    {0x3d, 0},\n    {0x41, 0},\n    {0x4044, 0},\n  },\n  /* 23 */\n  {\n    {0xc000, 61},\n    {0xc000, 65},\n    {0xc000, 95},\n    {0xc000, 98},\n    {0xc000, 100},\n    {0xc000, 102},\n    {0xc000, 103},\n    {0xc000, 104},\n    {0xc000, 108},\n    {0xc000, 109},\n    {0xc000, 110},\n    {0xc000, 112},\n    {0xc000, 114},\n    {0xc000, 117},\n    {0x26, 0},\n    {0x27, 0},\n  },\n  /* 24 */\n  {\n    {0x8001, 61},\n    {0xc016, 61},\n    {0x8001, 65},\n    {0xc016, 65},\n    {0x8001, 95},\n    {0xc016, 95},\n    {0x8001, 98},\n    {0xc016, 98},\n    {0x8001, 100},\n    {0xc016, 100},\n    {0x8001, 102},\n    {0xc016, 102},\n    {0x8001, 103},\n    {0xc016, 103},\n    {0x8001, 104},\n    {0xc016, 104},\n  },\n  /* 25 */\n  {\n    {0x8002, 61},\n    {0x8009, 61},\n    {0x8017, 61},\n    {0xc028, 61},\n    {0x8002, 65},\n    {0x8009, 65},\n    {0x8017, 65},\n    {0xc028, 65},\n    {0x8002, 95},\n    {0x8009, 95},\n    {0x8017, 95},\n    {0xc028, 95},\n    {0x8002, 98},\n    {0x8009, 98},\n    {0x8017, 98},\n    {0xc028, 98},\n  },\n  /* 26 */\n  {\n    {0x8003, 61},\n    {0x8006, 61},\n    {0x800a, 61},\n    {0x800f, 61},\n    {0x8018, 61},\n    {0x801f, 61},\n    {0x8029, 61},\n    {0xc038, 61},\n    {0x8003, 65},\n    {0x8006, 65},\n    {0x800a, 65},\n    {0x800f, 65},\n    {0x8018, 65},\n    {0x801f, 65},\n    {0x8029, 65},\n    {0xc038, 65},\n  },\n  /* 27 */\n  {\n    {0x8003, 95},\n    {0x8006, 95},\n    {0x800a, 95},\n    {0x800f, 95},\n    {0x8018, 95},\n    {0x801f, 95},\n    {0x8029, 95},\n    {0xc038, 95},\n    {0x8003, 98},\n    {0x8006, 98},\n    {0x800a, 98},\n    {0x800f, 98},\n    {0x8018, 98},\n    {0x801f, 98},\n    {0x8029, 98},\n    {0xc038, 98},\n  },\n  /* 28 */\n  {\n    {0x8002, 100},\n    {0x8009, 100},\n    {0x8017, 100},\n    {0xc028, 100},\n    {0x8002, 102},\n    {0x8009, 102},\n    {0x8017, 102},\n    {0xc028, 102},\n    {0x8002, 103},\n    {0x8009, 103},\n    {0x8017, 103},\n    {0xc028, 103},\n    {0x8002, 104},\n    {0x8009, 104},\n    {0x8017, 104},\n    {0xc028, 104},\n  },\n  /* 29 */\n  {\n    {0x8003, 100},\n    {0x8006, 100},\n    {0x800a, 100},\n    {0x800f, 100},\n    {0x8018, 100},\n    {0x801f, 100},\n    {0x8029, 100},\n    {0xc038, 100},\n    {0x8003, 102},\n    {0x8006, 102},\n    {0x800a, 102},\n    {0x800f, 102},\n    {0x8018, 102},\n    {0x801f, 102},\n    {0x8029, 102},\n    {0xc038, 102},\n  },\n  /* 30 */\n  {\n    {0x8003, 103},\n    {0x8006, 103},\n    {0x800a, 103},\n    {0x800f, 103},\n    {0x8018, 103},\n    {0x801f, 103},\n    {0x8029, 103},\n    {0xc038, 103},\n    {0x8003, 104},\n    {0x8006, 104},\n    {0x800a, 104},\n    {0x800f, 104},\n    {0x8018, 104},\n    {0x801f, 104},\n    {0x8029, 104},\n    {0xc038, 104},\n  },\n  /* 31 */\n  {\n    {0x8001, 108},\n    {0xc016, 108},\n    {0x8001, 109},\n    {0xc016, 109},\n    {0x8001, 110},\n    {0xc016, 110},\n    {0x8001, 112},\n    {0xc016, 112},\n    {0x8001, 114},\n    {0xc016, 114},\n    {0x8001, 117},\n    {0xc016, 117},\n    {0xc000, 58},\n    {0xc000, 66},\n    {0xc000, 67},\n    {0xc000, 68},\n  },\n  /* 32 */\n  {\n    {0x8002, 108},\n    {0x8009, 108},\n    {0x8017, 108},\n    {0xc028, 108},\n    {0x8002, 109},\n    {0x8009, 109},\n    {0x8017, 109},\n    {0xc028, 109},\n    {0x8002, 110},\n    {0x8009, 110},\n    {0x8017, 110},\n    {0xc028, 110},\n    {0x8002, 112},\n    {0x8009, 112},\n    {0x8017, 112},\n    {0xc028, 112},\n  },\n  /* 33 */\n  {\n    {0x8003, 108},\n    {0x8006, 108},\n    {0x800a, 108},\n    {0x800f, 108},\n    {0x8018, 108},\n    {0x801f, 108},\n    {0x8029, 108},\n    {0xc038, 108},\n    {0x8003, 109},\n    {0x8006, 109},\n    {0x800a, 109},\n    {0x800f, 109},\n    {0x8018, 109},\n    {0x801f, 109},\n    {0x8029, 109},\n    {0xc038, 109},\n  },\n  /* 34 */\n  {\n    {0x8003, 110},\n    {0x8006, 110},\n    {0x800a, 110},\n    {0x800f, 110},\n    {0x8018, 110},\n    {0x801f, 110},\n    {0x8029, 110},\n    {0xc038, 110},\n    {0x8003, 112},\n    {0x8006, 112},\n    {0x800a, 112},\n    {0x800f, 112},\n    {0x8018, 112},\n    {0x801f, 112},\n    {0x8029, 112},\n    {0xc038, 112},\n  },\n  /* 35 */\n  {\n    {0x8002, 114},\n    {0x8009, 114},\n    {0x8017, 114},\n    {0xc028, 114},\n    {0x8002, 117},\n    {0x8009, 117},\n    {0x8017, 117},\n    {0xc028, 117},\n    {0x8001, 58},\n    {0xc016, 58},\n    {0x8001, 66},\n    {0xc016, 66},\n    {0x8001, 67},\n    {0xc016, 67},\n    {0x8001, 68},\n    {0xc016, 68},\n  },\n  /* 36 */\n  {\n    {0x8003, 114},\n    {0x8006, 114},\n    {0x800a, 114},\n    {0x800f, 114},\n    {0x8018, 114},\n    {0x801f, 114},\n    {0x8029, 114},\n    {0xc038, 114},\n    {0x8003, 117},\n    {0x8006, 117},\n    {0x800a, 117},\n    {0x800f, 117},\n    {0x8018, 117},\n    {0x801f, 117},\n    {0x8029, 117},\n    {0xc038, 117},\n  },\n  /* 37 */\n  {\n    {0x8002, 58},\n    {0x8009, 58},\n    {0x8017, 58},\n    {0xc028, 58},\n    {0x8002, 66},\n    {0x8009, 66},\n    {0x8017, 66},\n    {0xc028, 66},\n    {0x8002, 67},\n    {0x8009, 67},\n    {0x8017, 67},\n    {0xc028, 67},\n    {0x8002, 68},\n    {0x8009, 68},\n    {0x8017, 68},\n    {0xc028, 68},\n  },\n  /* 38 */\n  {\n    {0x8003, 58},\n    {0x8006, 58},\n    {0x800a, 58},\n    {0x800f, 58},\n    {0x8018, 58},\n    {0x801f, 58},\n    {0x8029, 58},\n    {0xc038, 58},\n    {0x8003, 66},\n    {0x8006, 66},\n    {0x800a, 66},\n    {0x800f, 66},\n    {0x8018, 66},\n    {0x801f, 66},\n    {0x8029, 66},\n    {0xc038, 66},\n  },\n  /* 39 */\n  {\n    {0x8003, 67},\n    {0x8006, 67},\n    {0x800a, 67},\n    {0x800f, 67},\n    {0x8018, 67},\n    {0x801f, 67},\n    {0x8029, 67},\n    {0xc038, 67},\n    {0x8003, 68},\n    {0x8006, 68},\n    {0x800a, 68},\n    {0x800f, 68},\n    {0x8018, 68},\n    {0x801f, 68},\n    {0x8029, 68},\n    {0xc038, 68},\n  },\n  /* 40 */\n  {\n    {0x2c, 0},\n    {0x2d, 0},\n    {0x2f, 0},\n    {0x30, 0},\n    {0x33, 0},\n    {0x34, 0},\n    {0x36, 0},\n    {0x37, 0},\n    {0x3b, 0},\n    {0x3c, 0},\n    {0x3e, 0},\n    {0x3f, 0},\n    {0x42, 0},\n    {0x43, 0},\n    {0x45, 0},\n    {0x4048, 0},\n  },\n  /* 41 */\n  {\n    {0xc000, 69},\n    {0xc000, 70},\n    {0xc000, 71},\n    {0xc000, 72},\n    {0xc000, 73},\n    {0xc000, 74},\n    {0xc000, 75},\n    {0xc000, 76},\n    {0xc000, 77},\n    {0xc000, 78},\n    {0xc000, 79},\n    {0xc000, 80},\n    {0xc000, 81},\n    {0xc000, 82},\n    {0xc000, 83},\n    {0xc000, 84},\n  },\n  /* 42 */\n  {\n    {0x8001, 69},\n    {0xc016, 69},\n    {0x8001, 70},\n    {0xc016, 70},\n    {0x8001, 71},\n    {0xc016, 71},\n    {0x8001, 72},\n    {0xc016, 72},\n    {0x8001, 73},\n    {0xc016, 73},\n    {0x8001, 74},\n    {0xc016, 74},\n    {0x8001, 75},\n    {0xc016, 75},\n    {0x8001, 76},\n    {0xc016, 76},\n  },\n  /* 43 */\n  {\n    {0x8002, 69},\n    {0x8009, 69},\n    {0x8017, 69},\n    {0xc028, 69},\n    {0x8002, 70},\n    {0x8009, 70},\n    {0x8017, 70},\n    {0xc028, 70},\n    {0x8002, 71},\n    {0x8009, 71},\n    {0x8017, 71},\n    {0xc028, 71},\n    {0x8002, 72},\n    {0x8009, 72},\n    {0x8017, 72},\n    {0xc028, 72},\n  },\n  /* 44 */\n  {\n    {0x8003, 69},\n    {0x8006, 69},\n    {0x800a, 69},\n    {0x800f, 69},\n    {0x8018, 69},\n    {0x801f, 69},\n    {0x8029, 69},\n    {0xc038, 69},\n    {0x8003, 70},\n    {0x8006, 70},\n    {0x800a, 70},\n    {0x800f, 70},\n    {0x8018, 70},\n    {0x801f, 70},\n    {0x8029, 70},\n    {0xc038, 70},\n  },\n  /* 45 */\n  {\n    {0x8003, 71},\n    {0x8006, 71},\n    {0x800a, 71},\n    {0x800f, 71},\n    {0x8018, 71},\n    {0x801f, 71},\n    {0x8029, 71},\n    {0xc038, 71},\n    {0x8003, 72},\n    {0x8006, 72},\n    {0x800a, 72},\n    {0x800f, 72},\n    {0x8018, 72},\n    {0x801f, 72},\n    {0x8029, 72},\n    {0xc038, 72},\n  },\n  /* 46 */\n  {\n    {0x8002, 73},\n    {0x8009, 73},\n    {0x8017, 73},\n    {0xc028, 73},\n    {0x8002, 74},\n    {0x8009, 74},\n    {0x8017, 74},\n    {0xc028, 74},\n    {0x8002, 75},\n    {0x8009, 75},\n    {0x8017, 75},\n    {0xc028, 75},\n    {0x8002, 76},\n    {0x8009, 76},\n    {0x8017, 76},\n    {0xc028, 76},\n  },\n  /* 47 */\n  {\n    {0x8003, 73},\n    {0x8006, 73},\n    {0x800a, 73},\n    {0x800f, 73},\n    {0x8018, 73},\n    {0x801f, 73},\n    {0x8029, 73},\n    {0xc038, 73},\n    {0x8003, 74},\n    {0x8006, 74},\n    {0x800a, 74},\n    {0x800f, 74},\n    {0x8018, 74},\n    {0x801f, 74},\n    {0x8029, 74},\n    {0xc038, 74},\n  },\n  /* 48 */\n  {\n    {0x8003, 75},\n    {0x8006, 75},\n    {0x800a, 75},\n    {0x800f, 75},\n    {0x8018, 75},\n    {0x801f, 75},\n    {0x8029, 75},\n    {0xc038, 75},\n    {0x8003, 76},\n    {0x8006, 76},\n    {0x800a, 76},\n    {0x800f, 76},\n    {0x8018, 76},\n    {0x801f, 76},\n    {0x8029, 76},\n    {0xc038, 76},\n  },\n  /* 49 */\n  {\n    {0x8001, 77},\n    {0xc016, 77},\n    {0x8001, 78},\n    {0xc016, 78},\n    {0x8001, 79},\n    {0xc016, 79},\n    {0x8001, 80},\n    {0xc016, 80},\n    {0x8001, 81},\n    {0xc016, 81},\n    {0x8001, 82},\n    {0xc016, 82},\n    {0x8001, 83},\n    {0xc016, 83},\n    {0x8001, 84},\n    {0xc016, 84},\n  },\n  /* 50 */\n  {\n    {0x8002, 77},\n    {0x8009, 77},\n    {0x8017, 77},\n    {0xc028, 77},\n    {0x8002, 78},\n    {0x8009, 78},\n    {0x8017, 78},\n    {0xc028, 78},\n    {0x8002, 79},\n    {0x8009, 79},\n    {0x8017, 79},\n    {0xc028, 79},\n    {0x8002, 80},\n    {0x8009, 80},\n    {0x8017, 80},\n    {0xc028, 80},\n  },\n  /* 51 */\n  {\n    {0x8003, 77},\n    {0x8006, 77},\n    {0x800a, 77},\n    {0x800f, 77},\n    {0x8018, 77},\n    {0x801f, 77},\n    {0x8029, 77},\n    {0xc038, 77},\n    {0x8003, 78},\n    {0x8006, 78},\n    {0x800a, 78},\n    {0x800f, 78},\n    {0x8018, 78},\n    {0x801f, 78},\n    {0x8029, 78},\n    {0xc038, 78},\n  },\n  /* 52 */\n  {\n    {0x8003, 79},\n    {0x8006, 79},\n    {0x800a, 79},\n    {0x800f, 79},\n    {0x8018, 79},\n    {0x801f, 79},\n    {0x8029, 79},\n    {0xc038, 79},\n    {0x8003, 80},\n    {0x8006, 80},\n    {0x800a, 80},\n    {0x800f, 80},\n    {0x8018, 80},\n    {0x801f, 80},\n    {0x8029, 80},\n    {0xc038, 80},\n  },\n  /* 53 */\n  {\n    {0x8002, 81},\n    {0x8009, 81},\n    {0x8017, 81},\n    {0xc028, 81},\n    {0x8002, 82},\n    {0x8009, 82},\n    {0x8017, 82},\n    {0xc028, 82},\n    {0x8002, 83},\n    {0x8009, 83},\n    {0x8017, 83},\n    {0xc028, 83},\n    {0x8002, 84},\n    {0x8009, 84},\n    {0x8017, 84},\n    {0xc028, 84},\n  },\n  /* 54 */\n  {\n    {0x8003, 81},\n    {0x8006, 81},\n    {0x800a, 81},\n    {0x800f, 81},\n    {0x8018, 81},\n    {0x801f, 81},\n    {0x8029, 81},\n    {0xc038, 81},\n    {0x8003, 82},\n    {0x8006, 82},\n    {0x800a, 82},\n    {0x800f, 82},\n    {0x8018, 82},\n    {0x801f, 82},\n    {0x8029, 82},\n    {0xc038, 82},\n  },\n  /* 55 */\n  {\n    {0x8003, 83},\n    {0x8006, 83},\n    {0x800a, 83},\n    {0x800f, 83},\n    {0x8018, 83},\n    {0x801f, 83},\n    {0x8029, 83},\n    {0xc038, 83},\n    {0x8003, 84},\n    {0x8006, 84},\n    {0x800a, 84},\n    {0x800f, 84},\n    {0x8018, 84},\n    {0x801f, 84},\n    {0x8029, 84},\n    {0xc038, 84},\n  },\n  /* 56 */\n  {\n    {0xc000, 85},\n    {0xc000, 86},\n    {0xc000, 87},\n    {0xc000, 89},\n    {0xc000, 106},\n    {0xc000, 107},\n    {0xc000, 113},\n    {0xc000, 118},\n    {0xc000, 119},\n    {0xc000, 120},\n    {0xc000, 121},\n    {0xc000, 122},\n    {0x46, 0},\n    {0x47, 0},\n    {0x49, 0},\n    {0x404a, 0},\n  },\n  /* 57 */\n  {\n    {0x8001, 85},\n    {0xc016, 85},\n    {0x8001, 86},\n    {0xc016, 86},\n    {0x8001, 87},\n    {0xc016, 87},\n    {0x8001, 89},\n    {0xc016, 89},\n    {0x8001, 106},\n    {0xc016, 106},\n    {0x8001, 107},\n    {0xc016, 107},\n    {0x8001, 113},\n    {0xc016, 113},\n    {0x8001, 118},\n    {0xc016, 118},\n  },\n  /* 58 */\n  {\n    {0x8002, 85},\n    {0x8009, 85},\n    {0x8017, 85},\n    {0xc028, 85},\n    {0x8002, 86},\n    {0x8009, 86},\n    {0x8017, 86},\n    {0xc028, 86},\n    {0x8002, 87},\n    {0x8009, 87},\n    {0x8017, 87},\n    {0xc028, 87},\n    {0x8002, 89},\n    {0x8009, 89},\n    {0x8017, 89},\n    {0xc028, 89},\n  },\n  /* 59 */\n  {\n    {0x8003, 85},\n    {0x8006, 85},\n    {0x800a, 85},\n    {0x800f, 85},\n    {0x8018, 85},\n    {0x801f, 85},\n    {0x8029, 85},\n    {0xc038, 85},\n    {0x8003, 86},\n    {0x8006, 86},\n    {0x800a, 86},\n    {0x800f, 86},\n    {0x8018, 86},\n    {0x801f, 86},\n    {0x8029, 86},\n    {0xc038, 86},\n  },\n  /* 60 */\n  {\n    {0x8003, 87},\n    {0x8006, 87},\n    {0x800a, 87},\n    {0x800f, 87},\n    {0x8018, 87},\n    {0x801f, 87},\n    {0x8029, 87},\n    {0xc038, 87},\n    {0x8003, 89},\n    {0x8006, 89},\n    {0x800a, 89},\n    {0x800f, 89},\n    {0x8018, 89},\n    {0x801f, 89},\n    {0x8029, 89},\n    {0xc038, 89},\n  },\n  /* 61 */\n  {\n    {0x8002, 106},\n    {0x8009, 106},\n    {0x8017, 106},\n    {0xc028, 106},\n    {0x8002, 107},\n    {0x8009, 107},\n    {0x8017, 107},\n    {0xc028, 107},\n    {0x8002, 113},\n    {0x8009, 113},\n    {0x8017, 113},\n    {0xc028, 113},\n    {0x8002, 118},\n    {0x8009, 118},\n    {0x8017, 118},\n    {0xc028, 118},\n  },\n  /* 62 */\n  {\n    {0x8003, 106},\n    {0x8006, 106},\n    {0x800a, 106},\n    {0x800f, 106},\n    {0x8018, 106},\n    {0x801f, 106},\n    {0x8029, 106},\n    {0xc038, 106},\n    {0x8003, 107},\n    {0x8006, 107},\n    {0x800a, 107},\n    {0x800f, 107},\n    {0x8018, 107},\n    {0x801f, 107},\n    {0x8029, 107},\n    {0xc038, 107},\n  },\n  /* 63 */\n  {\n    {0x8003, 113},\n    {0x8006, 113},\n    {0x800a, 113},\n    {0x800f, 113},\n    {0x8018, 113},\n    {0x801f, 113},\n    {0x8029, 113},\n    {0xc038, 113},\n    {0x8003, 118},\n    {0x8006, 118},\n    {0x800a, 118},\n    {0x800f, 118},\n    {0x8018, 118},\n    {0x801f, 118},\n    {0x8029, 118},\n    {0xc038, 118},\n  },\n  /* 64 */\n  {\n    {0x8001, 119},\n    {0xc016, 119},\n    {0x8001, 120},\n    {0xc016, 120},\n    {0x8001, 121},\n    {0xc016, 121},\n    {0x8001, 122},\n    {0xc016, 122},\n    {0xc000, 38},\n    {0xc000, 42},\n    {0xc000, 44},\n    {0xc000, 59},\n    {0xc000, 88},\n    {0xc000, 90},\n    {0x4b, 0},\n    {0x4e, 0},\n  },\n  /* 65 */\n  {\n    {0x8002, 119},\n    {0x8009, 119},\n    {0x8017, 119},\n    {0xc028, 119},\n    {0x8002, 120},\n    {0x8009, 120},\n    {0x8017, 120},\n    {0xc028, 120},\n    {0x8002, 121},\n    {0x8009, 121},\n    {0x8017, 121},\n    {0xc028, 121},\n    {0x8002, 122},\n    {0x8009, 122},\n    {0x8017, 122},\n    {0xc028, 122},\n  },\n  /* 66 */\n  {\n    {0x8003, 119},\n    {0x8006, 119},\n    {0x800a, 119},\n    {0x800f, 119},\n    {0x8018, 119},\n    {0x801f, 119},\n    {0x8029, 119},\n    {0xc038, 119},\n    {0x8003, 120},\n    {0x8006, 120},\n    {0x800a, 120},\n    {0x800f, 120},\n    {0x8018, 120},\n    {0x801f, 120},\n    {0x8029, 120},\n    {0xc038, 120},\n  },\n  /* 67 */\n  {\n    {0x8003, 121},\n    {0x8006, 121},\n    {0x800a, 121},\n    {0x800f, 121},\n    {0x8018, 121},\n    {0x801f, 121},\n    {0x8029, 121},\n    {0xc038, 121},\n    {0x8003, 122},\n    {0x8006, 122},\n    {0x800a, 122},\n    {0x800f, 122},\n    {0x8018, 122},\n    {0x801f, 122},\n    {0x8029, 122},\n    {0xc038, 122},\n  },\n  /* 68 */\n  {\n    {0x8001, 38},\n    {0xc016, 38},\n    {0x8001, 42},\n    {0xc016, 42},\n    {0x8001, 44},\n    {0xc016, 44},\n    {0x8001, 59},\n    {0xc016, 59},\n    {0x8001, 88},\n    {0xc016, 88},\n    {0x8001, 90},\n    {0xc016, 90},\n    {0x4c, 0},\n    {0x4d, 0},\n    {0x4f, 0},\n    {0x51, 0},\n  },\n  /* 69 */\n  {\n    {0x8002, 38},\n    {0x8009, 38},\n    {0x8017, 38},\n    {0xc028, 38},\n    {0x8002, 42},\n    {0x8009, 42},\n    {0x8017, 42},\n    {0xc028, 42},\n    {0x8002, 44},\n    {0x8009, 44},\n    {0x8017, 44},\n    {0xc028, 44},\n    {0x8002, 59},\n    {0x8009, 59},\n    {0x8017, 59},\n    {0xc028, 59},\n  },\n  /* 70 */\n  {\n    {0x8003, 38},\n    {0x8006, 38},\n    {0x800a, 38},\n    {0x800f, 38},\n    {0x8018, 38},\n    {0x801f, 38},\n    {0x8029, 38},\n    {0xc038, 38},\n    {0x8003, 42},\n    {0x8006, 42},\n    {0x800a, 42},\n    {0x800f, 42},\n    {0x8018, 42},\n    {0x801f, 42},\n    {0x8029, 42},\n    {0xc038, 42},\n  },\n  /* 71 */\n  {\n    {0x8003, 44},\n    {0x8006, 44},\n    {0x800a, 44},\n    {0x800f, 44},\n    {0x8018, 44},\n    {0x801f, 44},\n    {0x8029, 44},\n    {0xc038, 44},\n    {0x8003, 59},\n    {0x8006, 59},\n    {0x800a, 59},\n    {0x800f, 59},\n    {0x8018, 59},\n    {0x801f, 59},\n    {0x8029, 59},\n    {0xc038, 59},\n  },\n  /* 72 */\n  {\n    {0x8002, 88},\n    {0x8009, 88},\n    {0x8017, 88},\n    {0xc028, 88},\n    {0x8002, 90},\n    {0x8009, 90},\n    {0x8017, 90},\n    {0xc028, 90},\n    {0xc000, 33},\n    {0xc000, 34},\n    {0xc000, 40},\n    {0xc000, 41},\n    {0xc000, 63},\n    {0x50, 0},\n    {0x52, 0},\n    {0x54, 0},\n  },\n  /* 73 */\n  {\n    {0x8003, 88},\n    {0x8006, 88},\n    {0x800a, 88},\n    {0x800f, 88},\n    {0x8018, 88},\n    {0x801f, 88},\n    {0x8029, 88},\n    {0xc038, 88},\n    {0x8003, 90},\n    {0x8006, 90},\n    {0x800a, 90},\n    {0x800f, 90},\n    {0x8018, 90},\n    {0x801f, 90},\n    {0x8029, 90},\n    {0xc038, 90},\n  },\n  /* 74 */\n  {\n    {0x8001, 33},\n    {0xc016, 33},\n    {0x8001, 34},\n    {0xc016, 34},\n    {0x8001, 40},\n    {0xc016, 40},\n    {0x8001, 41},\n    {0xc016, 41},\n    {0x8001, 63},\n    {0xc016, 63},\n    {0xc000, 39},\n    {0xc000, 43},\n    {0xc000, 124},\n    {0x53, 0},\n    {0x55, 0},\n    {0x58, 0},\n  },\n  /* 75 */\n  {\n    {0x8002, 33},\n    {0x8009, 33},\n    {0x8017, 33},\n    {0xc028, 33},\n    {0x8002, 34},\n    {0x8009, 34},\n    {0x8017, 34},\n    {0xc028, 34},\n    {0x8002, 40},\n    {0x8009, 40},\n    {0x8017, 40},\n    {0xc028, 40},\n    {0x8002, 41},\n    {0x8009, 41},\n    {0x8017, 41},\n    {0xc028, 41},\n  },\n  /* 76 */\n  {\n    {0x8003, 33},\n    {0x8006, 33},\n    {0x800a, 33},\n    {0x800f, 33},\n    {0x8018, 33},\n    {0x801f, 33},\n    {0x8029, 33},\n    {0xc038, 33},\n    {0x8003, 34},\n    {0x8006, 34},\n    {0x800a, 34},\n    {0x800f, 34},\n    {0x8018, 34},\n    {0x801f, 34},\n    {0x8029, 34},\n    {0xc038, 34},\n  },\n  /* 77 */\n  {\n    {0x8003, 40},\n    {0x8006, 40},\n    {0x800a, 40},\n    {0x800f, 40},\n    {0x8018, 40},\n    {0x801f, 40},\n    {0x8029, 40},\n    {0xc038, 40},\n    {0x8003, 41},\n    {0x8006, 41},\n    {0x800a, 41},\n    {0x800f, 41},\n    {0x8018, 41},\n    {0x801f, 41},\n    {0x8029, 41},\n    {0xc038, 41},\n  },\n  /* 78 */\n  {\n    {0x8002, 63},\n    {0x8009, 63},\n    {0x8017, 63},\n    {0xc028, 63},\n    {0x8001, 39},\n    {0xc016, 39},\n    {0x8001, 43},\n    {0xc016, 43},\n    {0x8001, 124},\n    {0xc016, 124},\n    {0xc000, 35},\n    {0xc000, 62},\n    {0x56, 0},\n    {0x57, 0},\n    {0x59, 0},\n    {0x5a, 0},\n  },\n  /* 79 */\n  {\n    {0x8003, 63},\n    {0x8006, 63},\n    {0x800a, 63},\n    {0x800f, 63},\n    {0x8018, 63},\n    {0x801f, 63},\n    {0x8029, 63},\n    {0xc038, 63},\n    {0x8002, 39},\n    {0x8009, 39},\n    {0x8017, 39},\n    {0xc028, 39},\n    {0x8002, 43},\n    {0x8009, 43},\n    {0x8017, 43},\n    {0xc028, 43},\n  },\n  /* 80 */\n  {\n    {0x8003, 39},\n    {0x8006, 39},\n    {0x800a, 39},\n    {0x800f, 39},\n    {0x8018, 39},\n    {0x801f, 39},\n    {0x8029, 39},\n    {0xc038, 39},\n    {0x8003, 43},\n    {0x8006, 43},\n    {0x800a, 43},\n    {0x800f, 43},\n    {0x8018, 43},\n    {0x801f, 43},\n    {0x8029, 43},\n    {0xc038, 43},\n  },\n  /* 81 */\n  {\n    {0x8002, 124},\n    {0x8009, 124},\n    {0x8017, 124},\n    {0xc028, 124},\n    {0x8001, 35},\n    {0xc016, 35},\n    {0x8001, 62},\n    {0xc016, 62},\n    {0xc000, 0},\n    {0xc000, 36},\n    {0xc000, 64},\n    {0xc000, 91},\n    {0xc000, 93},\n    {0xc000, 126},\n    {0x5b, 0},\n    {0x5c, 0},\n  },\n  /* 82 */\n  {\n    {0x8003, 124},\n    {0x8006, 124},\n    {0x800a, 124},\n    {0x800f, 124},\n    {0x8018, 124},\n    {0x801f, 124},\n    {0x8029, 124},\n    {0xc038, 124},\n    {0x8002, 35},\n    {0x8009, 35},\n    {0x8017, 35},\n    {0xc028, 35},\n    {0x8002, 62},\n    {0x8009, 62},\n    {0x8017, 62},\n    {0xc028, 62},\n  },\n  /* 83 */\n  {\n    {0x8003, 35},\n    {0x8006, 35},\n    {0x800a, 35},\n    {0x800f, 35},\n    {0x8018, 35},\n    {0x801f, 35},\n    {0x8029, 35},\n    {0xc038, 35},\n    {0x8003, 62},\n    {0x8006, 62},\n    {0x800a, 62},\n    {0x800f, 62},\n    {0x8018, 62},\n    {0x801f, 62},\n    {0x8029, 62},\n    {0xc038, 62},\n  },\n  /* 84 */\n  {\n    {0x8001, 0},\n    {0xc016, 0},\n    {0x8001, 36},\n    {0xc016, 36},\n    {0x8001, 64},\n    {0xc016, 64},\n    {0x8001, 91},\n    {0xc016, 91},\n    {0x8001, 93},\n    {0xc016, 93},\n    {0x8001, 126},\n    {0xc016, 126},\n    {0xc000, 94},\n    {0xc000, 125},\n    {0x5d, 0},\n    {0x5e, 0},\n  },\n  /* 85 */\n  {\n    {0x8002, 0},\n    {0x8009, 0},\n    {0x8017, 0},\n    {0xc028, 0},\n    {0x8002, 36},\n    {0x8009, 36},\n    {0x8017, 36},\n    {0xc028, 36},\n    {0x8002, 64},\n    {0x8009, 64},\n    {0x8017, 64},\n    {0xc028, 64},\n    {0x8002, 91},\n    {0x8009, 91},\n    {0x8017, 91},\n    {0xc028, 91},\n  },\n  /* 86 */\n  {\n    {0x8003, 0},\n    {0x8006, 0},\n    {0x800a, 0},\n    {0x800f, 0},\n    {0x8018, 0},\n    {0x801f, 0},\n    {0x8029, 0},\n    {0xc038, 0},\n    {0x8003, 36},\n    {0x8006, 36},\n    {0x800a, 36},\n    {0x800f, 36},\n    {0x8018, 36},\n    {0x801f, 36},\n    {0x8029, 36},\n    {0xc038, 36},\n  },\n  /* 87 */\n  {\n    {0x8003, 64},\n    {0x8006, 64},\n    {0x800a, 64},\n    {0x800f, 64},\n    {0x8018, 64},\n    {0x801f, 64},\n    {0x8029, 64},\n    {0xc038, 64},\n    {0x8003, 91},\n    {0x8006, 91},\n    {0x800a, 91},\n    {0x800f, 91},\n    {0x8018, 91},\n    {0x801f, 91},\n    {0x8029, 91},\n    {0xc038, 91},\n  },\n  /* 88 */\n  {\n    {0x8002, 93},\n    {0x8009, 93},\n    {0x8017, 93},\n    {0xc028, 93},\n    {0x8002, 126},\n    {0x8009, 126},\n    {0x8017, 126},\n    {0xc028, 126},\n    {0x8001, 94},\n    {0xc016, 94},\n    {0x8001, 125},\n    {0xc016, 125},\n    {0xc000, 60},\n    {0xc000, 96},\n    {0xc000, 123},\n    {0x5f, 0},\n  },\n  /* 89 */\n  {\n    {0x8003, 93},\n    {0x8006, 93},\n    {0x800a, 93},\n    {0x800f, 93},\n    {0x8018, 93},\n    {0x801f, 93},\n    {0x8029, 93},\n    {0xc038, 93},\n    {0x8003, 126},\n    {0x8006, 126},\n    {0x800a, 126},\n    {0x800f, 126},\n    {0x8018, 126},\n    {0x801f, 126},\n    {0x8029, 126},\n    {0xc038, 126},\n  },\n  /* 90 */\n  {\n    {0x8002, 94},\n    {0x8009, 94},\n    {0x8017, 94},\n    {0xc028, 94},\n    {0x8002, 125},\n    {0x8009, 125},\n    {0x8017, 125},\n    {0xc028, 125},\n    {0x8001, 60},\n    {0xc016, 60},\n    {0x8001, 96},\n    {0xc016, 96},\n    {0x8001, 123},\n    {0xc016, 123},\n    {0x60, 0},\n    {0x6e, 0},\n  },\n  /* 91 */\n  {\n    {0x8003, 94},\n    {0x8006, 94},\n    {0x800a, 94},\n    {0x800f, 94},\n    {0x8018, 94},\n    {0x801f, 94},\n    {0x8029, 94},\n    {0xc038, 94},\n    {0x8003, 125},\n    {0x8006, 125},\n    {0x800a, 125},\n    {0x800f, 125},\n    {0x8018, 125},\n    {0x801f, 125},\n    {0x8029, 125},\n    {0xc038, 125},\n  },\n  /* 92 */\n  {\n    {0x8002, 60},\n    {0x8009, 60},\n    {0x8017, 60},\n    {0xc028, 60},\n    {0x8002, 96},\n    {0x8009, 96},\n    {0x8017, 96},\n    {0xc028, 96},\n    {0x8002, 123},\n    {0x8009, 123},\n    {0x8017, 123},\n    {0xc028, 123},\n    {0x61, 0},\n    {0x65, 0},\n    {0x6f, 0},\n    {0x85, 0},\n  },\n  /* 93 */\n  {\n    {0x8003, 60},\n    {0x8006, 60},\n    {0x800a, 60},\n    {0x800f, 60},\n    {0x8018, 60},\n    {0x801f, 60},\n    {0x8029, 60},\n    {0xc038, 60},\n    {0x8003, 96},\n    {0x8006, 96},\n    {0x800a, 96},\n    {0x800f, 96},\n    {0x8018, 96},\n    {0x801f, 96},\n    {0x8029, 96},\n    {0xc038, 96},\n  },\n  /* 94 */\n  {\n    {0x8003, 123},\n    {0x8006, 123},\n    {0x800a, 123},\n    {0x800f, 123},\n    {0x8018, 123},\n    {0x801f, 123},\n    {0x8029, 123},\n    {0xc038, 123},\n    {0x62, 0},\n    {0x63, 0},\n    {0x66, 0},\n    {0x69, 0},\n    {0x70, 0},\n    {0x77, 0},\n    {0x86, 0},\n    {0x99, 0},\n  },\n  /* 95 */\n  {\n    {0xc000, 92},\n    {0xc000, 195},\n    {0xc000, 208},\n    {0x64, 0},\n    {0x67, 0},\n    {0x68, 0},\n    {0x6a, 0},\n    {0x6b, 0},\n    {0x71, 0},\n    {0x74, 0},\n    {0x78, 0},\n    {0x7e, 0},\n    {0x87, 0},\n    {0x8e, 0},\n    {0x9a, 0},\n    {0xa9, 0},\n  },\n  /* 96 */\n  {\n    {0x8001, 92},\n    {0xc016, 92},\n    {0x8001, 195},\n    {0xc016, 195},\n    {0x8001, 208},\n    {0xc016, 208},\n    {0xc000, 128},\n    {0xc000, 130},\n    {0xc000, 131},\n    {0xc000, 162},\n    {0xc000, 184},\n    {0xc000, 194},\n    {0xc000, 224},\n    {0xc000, 226},\n    {0x6c, 0},\n    {0x6d, 0},\n  },\n  /* 97 */\n  {\n    {0x8002, 92},\n    {0x8009, 92},\n    {0x8017, 92},\n    {0xc028, 92},\n    {0x8002, 195},\n    {0x8009, 195},\n    {0x8017, 195},\n    {0xc028, 195},\n    {0x8002, 208},\n    {0x8009, 208},\n    {0x8017, 208},\n    {0xc028, 208},\n    {0x8001, 128},\n    {0xc016, 128},\n    {0x8001, 130},\n    {0xc016, 130},\n  },\n  /* 98 */\n  {\n    {0x8003, 92},\n    {0x8006, 92},\n    {0x800a, 92},\n    {0x800f, 92},\n    {0x8018, 92},\n    {0x801f, 92},\n    {0x8029, 92},\n    {0xc038, 92},\n    {0x8003, 195},\n    {0x8006, 195},\n    {0x800a, 195},\n    {0x800f, 195},\n    {0x8018, 195},\n    {0x801f, 195},\n    {0x8029, 195},\n    {0xc038, 195},\n  },\n  /* 99 */\n  {\n    {0x8003, 208},\n    {0x8006, 208},\n    {0x800a, 208},\n    {0x800f, 208},\n    {0x8018, 208},\n    {0x801f, 208},\n    {0x8029, 208},\n    {0xc038, 208},\n    {0x8002, 128},\n    {0x8009, 128},\n    {0x8017, 128},\n    {0xc028, 128},\n    {0x8002, 130},\n    {0x8009, 130},\n    {0x8017, 130},\n    {0xc028, 130},\n  },\n  /* 100 */\n  {\n    {0x8003, 128},\n    {0x8006, 128},\n    {0x800a, 128},\n    {0x800f, 128},\n    {0x8018, 128},\n    {0x801f, 128},\n    {0x8029, 128},\n    {0xc038, 128},\n    {0x8003, 130},\n    {0x8006, 130},\n    {0x800a, 130},\n    {0x800f, 130},\n    {0x8018, 130},\n    {0x801f, 130},\n    {0x8029, 130},\n    {0xc038, 130},\n  },\n  /* 101 */\n  {\n    {0x8001, 131},\n    {0xc016, 131},\n    {0x8001, 162},\n    {0xc016, 162},\n    {0x8001, 184},\n    {0xc016, 184},\n    {0x8001, 194},\n    {0xc016, 194},\n    {0x8001, 224},\n    {0xc016, 224},\n    {0x8001, 226},\n    {0xc016, 226},\n    {0xc000, 153},\n    {0xc000, 161},\n    {0xc000, 167},\n    {0xc000, 172},\n  },\n  /* 102 */\n  {\n    {0x8002, 131},\n    {0x8009, 131},\n    {0x8017, 131},\n    {0xc028, 131},\n    {0x8002, 162},\n    {0x8009, 162},\n    {0x8017, 162},\n    {0xc028, 162},\n    {0x8002, 184},\n    {0x8009, 184},\n    {0x8017, 184},\n    {0xc028, 184},\n    {0x8002, 194},\n    {0x8009, 194},\n    {0x8017, 194},\n    {0xc028, 194},\n  },\n  /* 103 */\n  {\n    {0x8003, 131},\n    {0x8006, 131},\n    {0x800a, 131},\n    {0x800f, 131},\n    {0x8018, 131},\n    {0x801f, 131},\n    {0x8029, 131},\n    {0xc038, 131},\n    {0x8003, 162},\n    {0x8006, 162},\n    {0x800a, 162},\n    {0x800f, 162},\n    {0x8018, 162},\n    {0x801f, 162},\n    {0x8029, 162},\n    {0xc038, 162},\n  },\n  /* 104 */\n  {\n    {0x8003, 184},\n    {0x8006, 184},\n    {0x800a, 184},\n    {0x800f, 184},\n    {0x8018, 184},\n    {0x801f, 184},\n    {0x8029, 184},\n    {0xc038, 184},\n    {0x8003, 194},\n    {0x8006, 194},\n    {0x800a, 194},\n    {0x800f, 194},\n    {0x8018, 194},\n    {0x801f, 194},\n    {0x8029, 194},\n    {0xc038, 194},\n  },\n  /* 105 */\n  {\n    {0x8002, 224},\n    {0x8009, 224},\n    {0x8017, 224},\n    {0xc028, 224},\n    {0x8002, 226},\n    {0x8009, 226},\n    {0x8017, 226},\n    {0xc028, 226},\n    {0x8001, 153},\n    {0xc016, 153},\n    {0x8001, 161},\n    {0xc016, 161},\n    {0x8001, 167},\n    {0xc016, 167},\n    {0x8001, 172},\n    {0xc016, 172},\n  },\n  /* 106 */\n  {\n    {0x8003, 224},\n    {0x8006, 224},\n    {0x800a, 224},\n    {0x800f, 224},\n    {0x8018, 224},\n    {0x801f, 224},\n    {0x8029, 224},\n    {0xc038, 224},\n    {0x8003, 226},\n    {0x8006, 226},\n    {0x800a, 226},\n    {0x800f, 226},\n    {0x8018, 226},\n    {0x801f, 226},\n    {0x8029, 226},\n    {0xc038, 226},\n  },\n  /* 107 */\n  {\n    {0x8002, 153},\n    {0x8009, 153},\n    {0x8017, 153},\n    {0xc028, 153},\n    {0x8002, 161},\n    {0x8009, 161},\n    {0x8017, 161},\n    {0xc028, 161},\n    {0x8002, 167},\n    {0x8009, 167},\n    {0x8017, 167},\n    {0xc028, 167},\n    {0x8002, 172},\n    {0x8009, 172},\n    {0x8017, 172},\n    {0xc028, 172},\n  },\n  /* 108 */\n  {\n    {0x8003, 153},\n    {0x8006, 153},\n    {0x800a, 153},\n    {0x800f, 153},\n    {0x8018, 153},\n    {0x801f, 153},\n    {0x8029, 153},\n    {0xc038, 153},\n    {0x8003, 161},\n    {0x8006, 161},\n    {0x800a, 161},\n    {0x800f, 161},\n    {0x8018, 161},\n    {0x801f, 161},\n    {0x8029, 161},\n    {0xc038, 161},\n  },\n  /* 109 */\n  {\n    {0x8003, 167},\n    {0x8006, 167},\n    {0x800a, 167},\n    {0x800f, 167},\n    {0x8018, 167},\n    {0x801f, 167},\n    {0x8029, 167},\n    {0xc038, 167},\n    {0x8003, 172},\n    {0x8006, 172},\n    {0x800a, 172},\n    {0x800f, 172},\n    {0x8018, 172},\n    {0x801f, 172},\n    {0x8029, 172},\n    {0xc038, 172},\n  },\n  /* 110 */\n  {\n    {0x72, 0},\n    {0x73, 0},\n    {0x75, 0},\n    {0x76, 0},\n    {0x79, 0},\n    {0x7b, 0},\n    {0x7f, 0},\n    {0x82, 0},\n    {0x88, 0},\n    {0x8b, 0},\n    {0x8f, 0},\n    {0x92, 0},\n    {0x9b, 0},\n    {0xa2, 0},\n    {0xaa, 0},\n    {0xb4, 0},\n  },\n  /* 111 */\n  {\n    {0xc000, 176},\n    {0xc000, 177},\n    {0xc000, 179},\n    {0xc000, 209},\n    {0xc000, 216},\n    {0xc000, 217},\n    {0xc000, 227},\n    {0xc000, 229},\n    {0xc000, 230},\n    {0x7a, 0},\n    {0x7c, 0},\n    {0x7d, 0},\n    {0x80, 0},\n    {0x81, 0},\n    {0x83, 0},\n    {0x84, 0},\n  },\n  /* 112 */\n  {\n    {0x8001, 176},\n    {0xc016, 176},\n    {0x8001, 177},\n    {0xc016, 177},\n    {0x8001, 179},\n    {0xc016, 179},\n    {0x8001, 209},\n    {0xc016, 209},\n    {0x8001, 216},\n    {0xc016, 216},\n    {0x8001, 217},\n    {0xc016, 217},\n    {0x8001, 227},\n    {0xc016, 227},\n    {0x8001, 229},\n    {0xc016, 229},\n  },\n  /* 113 */\n  {\n    {0x8002, 176},\n    {0x8009, 176},\n    {0x8017, 176},\n    {0xc028, 176},\n    {0x8002, 177},\n    {0x8009, 177},\n    {0x8017, 177},\n    {0xc028, 177},\n    {0x8002, 179},\n    {0x8009, 179},\n    {0x8017, 179},\n    {0xc028, 179},\n    {0x8002, 209},\n    {0x8009, 209},\n    {0x8017, 209},\n    {0xc028, 209},\n  },\n  /* 114 */\n  {\n    {0x8003, 176},\n    {0x8006, 176},\n    {0x800a, 176},\n    {0x800f, 176},\n    {0x8018, 176},\n    {0x801f, 176},\n    {0x8029, 176},\n    {0xc038, 176},\n    {0x8003, 177},\n    {0x8006, 177},\n    {0x800a, 177},\n    {0x800f, 177},\n    {0x8018, 177},\n    {0x801f, 177},\n    {0x8029, 177},\n    {0xc038, 177},\n  },\n  /* 115 */\n  {\n    {0x8003, 179},\n    {0x8006, 179},\n    {0x800a, 179},\n    {0x800f, 179},\n    {0x8018, 179},\n    {0x801f, 179},\n    {0x8029, 179},\n    {0xc038, 179},\n    {0x8003, 209},\n    {0x8006, 209},\n    {0x800a, 209},\n    {0x800f, 209},\n    {0x8018, 209},\n    {0x801f, 209},\n    {0x8029, 209},\n    {0xc038, 209},\n  },\n  /* 116 */\n  {\n    {0x8002, 216},\n    {0x8009, 216},\n    {0x8017, 216},\n    {0xc028, 216},\n    {0x8002, 217},\n    {0x8009, 217},\n    {0x8017, 217},\n    {0xc028, 217},\n    {0x8002, 227},\n    {0x8009, 227},\n    {0x8017, 227},\n    {0xc028, 227},\n    {0x8002, 229},\n    {0x8009, 229},\n    {0x8017, 229},\n    {0xc028, 229},\n  },\n  /* 117 */\n  {\n    {0x8003, 216},\n    {0x8006, 216},\n    {0x800a, 216},\n    {0x800f, 216},\n    {0x8018, 216},\n    {0x801f, 216},\n    {0x8029, 216},\n    {0xc038, 216},\n    {0x8003, 217},\n    {0x8006, 217},\n    {0x800a, 217},\n    {0x800f, 217},\n    {0x8018, 217},\n    {0x801f, 217},\n    {0x8029, 217},\n    {0xc038, 217},\n  },\n  /* 118 */\n  {\n    {0x8003, 227},\n    {0x8006, 227},\n    {0x800a, 227},\n    {0x800f, 227},\n    {0x8018, 227},\n    {0x801f, 227},\n    {0x8029, 227},\n    {0xc038, 227},\n    {0x8003, 229},\n    {0x8006, 229},\n    {0x800a, 229},\n    {0x800f, 229},\n    {0x8018, 229},\n    {0x801f, 229},\n    {0x8029, 229},\n    {0xc038, 229},\n  },\n  /* 119 */\n  {\n    {0x8001, 230},\n    {0xc016, 230},\n    {0xc000, 129},\n    {0xc000, 132},\n    {0xc000, 133},\n    {0xc000, 134},\n    {0xc000, 136},\n    {0xc000, 146},\n    {0xc000, 154},\n    {0xc000, 156},\n    {0xc000, 160},\n    {0xc000, 163},\n    {0xc000, 164},\n    {0xc000, 169},\n    {0xc000, 170},\n    {0xc000, 173},\n  },\n  /* 120 */\n  {\n    {0x8002, 230},\n    {0x8009, 230},\n    {0x8017, 230},\n    {0xc028, 230},\n    {0x8001, 129},\n    {0xc016, 129},\n    {0x8001, 132},\n    {0xc016, 132},\n    {0x8001, 133},\n    {0xc016, 133},\n    {0x8001, 134},\n    {0xc016, 134},\n    {0x8001, 136},\n    {0xc016, 136},\n    {0x8001, 146},\n    {0xc016, 146},\n  },\n  /* 121 */\n  {\n    {0x8003, 230},\n    {0x8006, 230},\n    {0x800a, 230},\n    {0x800f, 230},\n    {0x8018, 230},\n    {0x801f, 230},\n    {0x8029, 230},\n    {0xc038, 230},\n    {0x8002, 129},\n    {0x8009, 129},\n    {0x8017, 129},\n    {0xc028, 129},\n    {0x8002, 132},\n    {0x8009, 132},\n    {0x8017, 132},\n    {0xc028, 132},\n  },\n  /* 122 */\n  {\n    {0x8003, 129},\n    {0x8006, 129},\n    {0x800a, 129},\n    {0x800f, 129},\n    {0x8018, 129},\n    {0x801f, 129},\n    {0x8029, 129},\n    {0xc038, 129},\n    {0x8003, 132},\n    {0x8006, 132},\n    {0x800a, 132},\n    {0x800f, 132},\n    {0x8018, 132},\n    {0x801f, 132},\n    {0x8029, 132},\n    {0xc038, 132},\n  },\n  /* 123 */\n  {\n    {0x8002, 133},\n    {0x8009, 133},\n    {0x8017, 133},\n    {0xc028, 133},\n    {0x8002, 134},\n    {0x8009, 134},\n    {0x8017, 134},\n    {0xc028, 134},\n    {0x8002, 136},\n    {0x8009, 136},\n    {0x8017, 136},\n    {0xc028, 136},\n    {0x8002, 146},\n    {0x8009, 146},\n    {0x8017, 146},\n    {0xc028, 146},\n  },\n  /* 124 */\n  {\n    {0x8003, 133},\n    {0x8006, 133},\n    {0x800a, 133},\n    {0x800f, 133},\n    {0x8018, 133},\n    {0x801f, 133},\n    {0x8029, 133},\n    {0xc038, 133},\n    {0x8003, 134},\n    {0x8006, 134},\n    {0x800a, 134},\n    {0x800f, 134},\n    {0x8018, 134},\n    {0x801f, 134},\n    {0x8029, 134},\n    {0xc038, 134},\n  },\n  /* 125 */\n  {\n    {0x8003, 136},\n    {0x8006, 136},\n    {0x800a, 136},\n    {0x800f, 136},\n    {0x8018, 136},\n    {0x801f, 136},\n    {0x8029, 136},\n    {0xc038, 136},\n    {0x8003, 146},\n    {0x8006, 146},\n    {0x800a, 146},\n    {0x800f, 146},\n    {0x8018, 146},\n    {0x801f, 146},\n    {0x8029, 146},\n    {0xc038, 146},\n  },\n  /* 126 */\n  {\n    {0x8001, 154},\n    {0xc016, 154},\n    {0x8001, 156},\n    {0xc016, 156},\n    {0x8001, 160},\n    {0xc016, 160},\n    {0x8001, 163},\n    {0xc016, 163},\n    {0x8001, 164},\n    {0xc016, 164},\n    {0x8001, 169},\n    {0xc016, 169},\n    {0x8001, 170},\n    {0xc016, 170},\n    {0x8001, 173},\n    {0xc016, 173},\n  },\n  /* 127 */\n  {\n    {0x8002, 154},\n    {0x8009, 154},\n    {0x8017, 154},\n    {0xc028, 154},\n    {0x8002, 156},\n    {0x8009, 156},\n    {0x8017, 156},\n    {0xc028, 156},\n    {0x8002, 160},\n    {0x8009, 160},\n    {0x8017, 160},\n    {0xc028, 160},\n    {0x8002, 163},\n    {0x8009, 163},\n    {0x8017, 163},\n    {0xc028, 163},\n  },\n  /* 128 */\n  {\n    {0x8003, 154},\n    {0x8006, 154},\n    {0x800a, 154},\n    {0x800f, 154},\n    {0x8018, 154},\n    {0x801f, 154},\n    {0x8029, 154},\n    {0xc038, 154},\n    {0x8003, 156},\n    {0x8006, 156},\n    {0x800a, 156},\n    {0x800f, 156},\n    {0x8018, 156},\n    {0x801f, 156},\n    {0x8029, 156},\n    {0xc038, 156},\n  },\n  /* 129 */\n  {\n    {0x8003, 160},\n    {0x8006, 160},\n    {0x800a, 160},\n    {0x800f, 160},\n    {0x8018, 160},\n    {0x801f, 160},\n    {0x8029, 160},\n    {0xc038, 160},\n    {0x8003, 163},\n    {0x8006, 163},\n    {0x800a, 163},\n    {0x800f, 163},\n    {0x8018, 163},\n    {0x801f, 163},\n    {0x8029, 163},\n    {0xc038, 163},\n  },\n  /* 130 */\n  {\n    {0x8002, 164},\n    {0x8009, 164},\n    {0x8017, 164},\n    {0xc028, 164},\n    {0x8002, 169},\n    {0x8009, 169},\n    {0x8017, 169},\n    {0xc028, 169},\n    {0x8002, 170},\n    {0x8009, 170},\n    {0x8017, 170},\n    {0xc028, 170},\n    {0x8002, 173},\n    {0x8009, 173},\n    {0x8017, 173},\n    {0xc028, 173},\n  },\n  /* 131 */\n  {\n    {0x8003, 164},\n    {0x8006, 164},\n    {0x800a, 164},\n    {0x800f, 164},\n    {0x8018, 164},\n    {0x801f, 164},\n    {0x8029, 164},\n    {0xc038, 164},\n    {0x8003, 169},\n    {0x8006, 169},\n    {0x800a, 169},\n    {0x800f, 169},\n    {0x8018, 169},\n    {0x801f, 169},\n    {0x8029, 169},\n    {0xc038, 169},\n  },\n  /* 132 */\n  {\n    {0x8003, 170},\n    {0x8006, 170},\n    {0x800a, 170},\n    {0x800f, 170},\n    {0x8018, 170},\n    {0x801f, 170},\n    {0x8029, 170},\n    {0xc038, 170},\n    {0x8003, 173},\n    {0x8006, 173},\n    {0x800a, 173},\n    {0x800f, 173},\n    {0x8018, 173},\n    {0x801f, 173},\n    {0x8029, 173},\n    {0xc038, 173},\n  },\n  /* 133 */\n  {\n    {0x89, 0},\n    {0x8a, 0},\n    {0x8c, 0},\n    {0x8d, 0},\n    {0x90, 0},\n    {0x91, 0},\n    {0x93, 0},\n    {0x96, 0},\n    {0x9c, 0},\n    {0x9f, 0},\n    {0xa3, 0},\n    {0xa6, 0},\n    {0xab, 0},\n    {0xae, 0},\n    {0xb5, 0},\n    {0xbe, 0},\n  },\n  /* 134 */\n  {\n    {0xc000, 178},\n    {0xc000, 181},\n    {0xc000, 185},\n    {0xc000, 186},\n    {0xc000, 187},\n    {0xc000, 189},\n    {0xc000, 190},\n    {0xc000, 196},\n    {0xc000, 198},\n    {0xc000, 228},\n    {0xc000, 232},\n    {0xc000, 233},\n    {0x94, 0},\n    {0x95, 0},\n    {0x97, 0},\n    {0x98, 0},\n  },\n  /* 135 */\n  {\n    {0x8001, 178},\n    {0xc016, 178},\n    {0x8001, 181},\n    {0xc016, 181},\n    {0x8001, 185},\n    {0xc016, 185},\n    {0x8001, 186},\n    {0xc016, 186},\n    {0x8001, 187},\n    {0xc016, 187},\n    {0x8001, 189},\n    {0xc016, 189},\n    {0x8001, 190},\n    {0xc016, 190},\n    {0x8001, 196},\n    {0xc016, 196},\n  },\n  /* 136 */\n  {\n    {0x8002, 178},\n    {0x8009, 178},\n    {0x8017, 178},\n    {0xc028, 178},\n    {0x8002, 181},\n    {0x8009, 181},\n    {0x8017, 181},\n    {0xc028, 181},\n    {0x8002, 185},\n    {0x8009, 185},\n    {0x8017, 185},\n    {0xc028, 185},\n    {0x8002, 186},\n    {0x8009, 186},\n    {0x8017, 186},\n    {0xc028, 186},\n  },\n  /* 137 */\n  {\n    {0x8003, 178},\n    {0x8006, 178},\n    {0x800a, 178},\n    {0x800f, 178},\n    {0x8018, 178},\n    {0x801f, 178},\n    {0x8029, 178},\n    {0xc038, 178},\n    {0x8003, 181},\n    {0x8006, 181},\n    {0x800a, 181},\n    {0x800f, 181},\n    {0x8018, 181},\n    {0x801f, 181},\n    {0x8029, 181},\n    {0xc038, 181},\n  },\n  /* 138 */\n  {\n    {0x8003, 185},\n    {0x8006, 185},\n    {0x800a, 185},\n    {0x800f, 185},\n    {0x8018, 185},\n    {0x801f, 185},\n    {0x8029, 185},\n    {0xc038, 185},\n    {0x8003, 186},\n    {0x8006, 186},\n    {0x800a, 186},\n    {0x800f, 186},\n    {0x8018, 186},\n    {0x801f, 186},\n    {0x8029, 186},\n    {0xc038, 186},\n  },\n  /* 139 */\n  {\n    {0x8002, 187},\n    {0x8009, 187},\n    {0x8017, 187},\n    {0xc028, 187},\n    {0x8002, 189},\n    {0x8009, 189},\n    {0x8017, 189},\n    {0xc028, 189},\n    {0x8002, 190},\n    {0x8009, 190},\n    {0x8017, 190},\n    {0xc028, 190},\n    {0x8002, 196},\n    {0x8009, 196},\n    {0x8017, 196},\n    {0xc028, 196},\n  },\n  /* 140 */\n  {\n    {0x8003, 187},\n    {0x8006, 187},\n    {0x800a, 187},\n    {0x800f, 187},\n    {0x8018, 187},\n    {0x801f, 187},\n    {0x8029, 187},\n    {0xc038, 187},\n    {0x8003, 189},\n    {0x8006, 189},\n    {0x800a, 189},\n    {0x800f, 189},\n    {0x8018, 189},\n    {0x801f, 189},\n    {0x8029, 189},\n    {0xc038, 189},\n  },\n  /* 141 */\n  {\n    {0x8003, 190},\n    {0x8006, 190},\n    {0x800a, 190},\n    {0x800f, 190},\n    {0x8018, 190},\n    {0x801f, 190},\n    {0x8029, 190},\n    {0xc038, 190},\n    {0x8003, 196},\n    {0x8006, 196},\n    {0x800a, 196},\n    {0x800f, 196},\n    {0x8018, 196},\n    {0x801f, 196},\n    {0x8029, 196},\n    {0xc038, 196},\n  },\n  /* 142 */\n  {\n    {0x8001, 198},\n    {0xc016, 198},\n    {0x8001, 228},\n    {0xc016, 228},\n    {0x8001, 232},\n    {0xc016, 232},\n    {0x8001, 233},\n    {0xc016, 233},\n    {0xc000, 1},\n    {0xc000, 135},\n    {0xc000, 137},\n    {0xc000, 138},\n    {0xc000, 139},\n    {0xc000, 140},\n    {0xc000, 141},\n    {0xc000, 143},\n  },\n  /* 143 */\n  {\n    {0x8002, 198},\n    {0x8009, 198},\n    {0x8017, 198},\n    {0xc028, 198},\n    {0x8002, 228},\n    {0x8009, 228},\n    {0x8017, 228},\n    {0xc028, 228},\n    {0x8002, 232},\n    {0x8009, 232},\n    {0x8017, 232},\n    {0xc028, 232},\n    {0x8002, 233},\n    {0x8009, 233},\n    {0x8017, 233},\n    {0xc028, 233},\n  },\n  /* 144 */\n  {\n    {0x8003, 198},\n    {0x8006, 198},\n    {0x800a, 198},\n    {0x800f, 198},\n    {0x8018, 198},\n    {0x801f, 198},\n    {0x8029, 198},\n    {0xc038, 198},\n    {0x8003, 228},\n    {0x8006, 228},\n    {0x800a, 228},\n    {0x800f, 228},\n    {0x8018, 228},\n    {0x801f, 228},\n    {0x8029, 228},\n    {0xc038, 228},\n  },\n  /* 145 */\n  {\n    {0x8003, 232},\n    {0x8006, 232},\n    {0x800a, 232},\n    {0x800f, 232},\n    {0x8018, 232},\n    {0x801f, 232},\n    {0x8029, 232},\n    {0xc038, 232},\n    {0x8003, 233},\n    {0x8006, 233},\n    {0x800a, 233},\n    {0x800f, 233},\n    {0x8018, 233},\n    {0x801f, 233},\n    {0x8029, 233},\n    {0xc038, 233},\n  },\n  /* 146 */\n  {\n    {0x8001, 1},\n    {0xc016, 1},\n    {0x8001, 135},\n    {0xc016, 135},\n    {0x8001, 137},\n    {0xc016, 137},\n    {0x8001, 138},\n    {0xc016, 138},\n    {0x8001, 139},\n    {0xc016, 139},\n    {0x8001, 140},\n    {0xc016, 140},\n    {0x8001, 141},\n    {0xc016, 141},\n    {0x8001, 143},\n    {0xc016, 143},\n  },\n  /* 147 */\n  {\n    {0x8002, 1},\n    {0x8009, 1},\n    {0x8017, 1},\n    {0xc028, 1},\n    {0x8002, 135},\n    {0x8009, 135},\n    {0x8017, 135},\n    {0xc028, 135},\n    {0x8002, 137},\n    {0x8009, 137},\n    {0x8017, 137},\n    {0xc028, 137},\n    {0x8002, 138},\n    {0x8009, 138},\n    {0x8017, 138},\n    {0xc028, 138},\n  },\n  /* 148 */\n  {\n    {0x8003, 1},\n    {0x8006, 1},\n    {0x800a, 1},\n    {0x800f, 1},\n    {0x8018, 1},\n    {0x801f, 1},\n    {0x8029, 1},\n    {0xc038, 1},\n    {0x8003, 135},\n    {0x8006, 135},\n    {0x800a, 135},\n    {0x800f, 135},\n    {0x8018, 135},\n    {0x801f, 135},\n    {0x8029, 135},\n    {0xc038, 135},\n  },\n  /* 149 */\n  {\n    {0x8003, 137},\n    {0x8006, 137},\n    {0x800a, 137},\n    {0x800f, 137},\n    {0x8018, 137},\n    {0x801f, 137},\n    {0x8029, 137},\n    {0xc038, 137},\n    {0x8003, 138},\n    {0x8006, 138},\n    {0x800a, 138},\n    {0x800f, 138},\n    {0x8018, 138},\n    {0x801f, 138},\n    {0x8029, 138},\n    {0xc038, 138},\n  },\n  /* 150 */\n  {\n    {0x8002, 139},\n    {0x8009, 139},\n    {0x8017, 139},\n    {0xc028, 139},\n    {0x8002, 140},\n    {0x8009, 140},\n    {0x8017, 140},\n    {0xc028, 140},\n    {0x8002, 141},\n    {0x8009, 141},\n    {0x8017, 141},\n    {0xc028, 141},\n    {0x8002, 143},\n    {0x8009, 143},\n    {0x8017, 143},\n    {0xc028, 143},\n  },\n  /* 151 */\n  {\n    {0x8003, 139},\n    {0x8006, 139},\n    {0x800a, 139},\n    {0x800f, 139},\n    {0x8018, 139},\n    {0x801f, 139},\n    {0x8029, 139},\n    {0xc038, 139},\n    {0x8003, 140},\n    {0x8006, 140},\n    {0x800a, 140},\n    {0x800f, 140},\n    {0x8018, 140},\n    {0x801f, 140},\n    {0x8029, 140},\n    {0xc038, 140},\n  },\n  /* 152 */\n  {\n    {0x8003, 141},\n    {0x8006, 141},\n    {0x800a, 141},\n    {0x800f, 141},\n    {0x8018, 141},\n    {0x801f, 141},\n    {0x8029, 141},\n    {0xc038, 141},\n    {0x8003, 143},\n    {0x8006, 143},\n    {0x800a, 143},\n    {0x800f, 143},\n    {0x8018, 143},\n    {0x801f, 143},\n    {0x8029, 143},\n    {0xc038, 143},\n  },\n  /* 153 */\n  {\n    {0x9d, 0},\n    {0x9e, 0},\n    {0xa0, 0},\n    {0xa1, 0},\n    {0xa4, 0},\n    {0xa5, 0},\n    {0xa7, 0},\n    {0xa8, 0},\n    {0xac, 0},\n    {0xad, 0},\n    {0xaf, 0},\n    {0xb1, 0},\n    {0xb6, 0},\n    {0xb9, 0},\n    {0xbf, 0},\n    {0xcf, 0},\n  },\n  /* 154 */\n  {\n    {0xc000, 147},\n    {0xc000, 149},\n    {0xc000, 150},\n    {0xc000, 151},\n    {0xc000, 152},\n    {0xc000, 155},\n    {0xc000, 157},\n    {0xc000, 158},\n    {0xc000, 165},\n    {0xc000, 166},\n    {0xc000, 168},\n    {0xc000, 174},\n    {0xc000, 175},\n    {0xc000, 180},\n    {0xc000, 182},\n    {0xc000, 183},\n  },\n  /* 155 */\n  {\n    {0x8001, 147},\n    {0xc016, 147},\n    {0x8001, 149},\n    {0xc016, 149},\n    {0x8001, 150},\n    {0xc016, 150},\n    {0x8001, 151},\n    {0xc016, 151},\n    {0x8001, 152},\n    {0xc016, 152},\n    {0x8001, 155},\n    {0xc016, 155},\n    {0x8001, 157},\n    {0xc016, 157},\n    {0x8001, 158},\n    {0xc016, 158},\n  },\n  /* 156 */\n  {\n    {0x8002, 147},\n    {0x8009, 147},\n    {0x8017, 147},\n    {0xc028, 147},\n    {0x8002, 149},\n    {0x8009, 149},\n    {0x8017, 149},\n    {0xc028, 149},\n    {0x8002, 150},\n    {0x8009, 150},\n    {0x8017, 150},\n    {0xc028, 150},\n    {0x8002, 151},\n    {0x8009, 151},\n    {0x8017, 151},\n    {0xc028, 151},\n  },\n  /* 157 */\n  {\n    {0x8003, 147},\n    {0x8006, 147},\n    {0x800a, 147},\n    {0x800f, 147},\n    {0x8018, 147},\n    {0x801f, 147},\n    {0x8029, 147},\n    {0xc038, 147},\n    {0x8003, 149},\n    {0x8006, 149},\n    {0x800a, 149},\n    {0x800f, 149},\n    {0x8018, 149},\n    {0x801f, 149},\n    {0x8029, 149},\n    {0xc038, 149},\n  },\n  /* 158 */\n  {\n    {0x8003, 150},\n    {0x8006, 150},\n    {0x800a, 150},\n    {0x800f, 150},\n    {0x8018, 150},\n    {0x801f, 150},\n    {0x8029, 150},\n    {0xc038, 150},\n    {0x8003, 151},\n    {0x8006, 151},\n    {0x800a, 151},\n    {0x800f, 151},\n    {0x8018, 151},\n    {0x801f, 151},\n    {0x8029, 151},\n    {0xc038, 151},\n  },\n  /* 159 */\n  {\n    {0x8002, 152},\n    {0x8009, 152},\n    {0x8017, 152},\n    {0xc028, 152},\n    {0x8002, 155},\n    {0x8009, 155},\n    {0x8017, 155},\n    {0xc028, 155},\n    {0x8002, 157},\n    {0x8009, 157},\n    {0x8017, 157},\n    {0xc028, 157},\n    {0x8002, 158},\n    {0x8009, 158},\n    {0x8017, 158},\n    {0xc028, 158},\n  },\n  /* 160 */\n  {\n    {0x8003, 152},\n    {0x8006, 152},\n    {0x800a, 152},\n    {0x800f, 152},\n    {0x8018, 152},\n    {0x801f, 152},\n    {0x8029, 152},\n    {0xc038, 152},\n    {0x8003, 155},\n    {0x8006, 155},\n    {0x800a, 155},\n    {0x800f, 155},\n    {0x8018, 155},\n    {0x801f, 155},\n    {0x8029, 155},\n    {0xc038, 155},\n  },\n  /* 161 */\n  {\n    {0x8003, 157},\n    {0x8006, 157},\n    {0x800a, 157},\n    {0x800f, 157},\n    {0x8018, 157},\n    {0x801f, 157},\n    {0x8029, 157},\n    {0xc038, 157},\n    {0x8003, 158},\n    {0x8006, 158},\n    {0x800a, 158},\n    {0x800f, 158},\n    {0x8018, 158},\n    {0x801f, 158},\n    {0x8029, 158},\n    {0xc038, 158},\n  },\n  /* 162 */\n  {\n    {0x8001, 165},\n    {0xc016, 165},\n    {0x8001, 166},\n    {0xc016, 166},\n    {0x8001, 168},\n    {0xc016, 168},\n    {0x8001, 174},\n    {0xc016, 174},\n    {0x8001, 175},\n    {0xc016, 175},\n    {0x8001, 180},\n    {0xc016, 180},\n    {0x8001, 182},\n    {0xc016, 182},\n    {0x8001, 183},\n    {0xc016, 183},\n  },\n  /* 163 */\n  {\n    {0x8002, 165},\n    {0x8009, 165},\n    {0x8017, 165},\n    {0xc028, 165},\n    {0x8002, 166},\n    {0x8009, 166},\n    {0x8017, 166},\n    {0xc028, 166},\n    {0x8002, 168},\n    {0x8009, 168},\n    {0x8017, 168},\n    {0xc028, 168},\n    {0x8002, 174},\n    {0x8009, 174},\n    {0x8017, 174},\n    {0xc028, 174},\n  },\n  /* 164 */\n  {\n    {0x8003, 165},\n    {0x8006, 165},\n    {0x800a, 165},\n    {0x800f, 165},\n    {0x8018, 165},\n    {0x801f, 165},\n    {0x8029, 165},\n    {0xc038, 165},\n    {0x8003, 166},\n    {0x8006, 166},\n    {0x800a, 166},\n    {0x800f, 166},\n    {0x8018, 166},\n    {0x801f, 166},\n    {0x8029, 166},\n    {0xc038, 166},\n  },\n  /* 165 */\n  {\n    {0x8003, 168},\n    {0x8006, 168},\n    {0x800a, 168},\n    {0x800f, 168},\n    {0x8018, 168},\n    {0x801f, 168},\n    {0x8029, 168},\n    {0xc038, 168},\n    {0x8003, 174},\n    {0x8006, 174},\n    {0x800a, 174},\n    {0x800f, 174},\n    {0x8018, 174},\n    {0x801f, 174},\n    {0x8029, 174},\n    {0xc038, 174},\n  },\n  /* 166 */\n  {\n    {0x8002, 175},\n    {0x8009, 175},\n    {0x8017, 175},\n    {0xc028, 175},\n    {0x8002, 180},\n    {0x8009, 180},\n    {0x8017, 180},\n    {0xc028, 180},\n    {0x8002, 182},\n    {0x8009, 182},\n    {0x8017, 182},\n    {0xc028, 182},\n    {0x8002, 183},\n    {0x8009, 183},\n    {0x8017, 183},\n    {0xc028, 183},\n  },\n  /* 167 */\n  {\n    {0x8003, 175},\n    {0x8006, 175},\n    {0x800a, 175},\n    {0x800f, 175},\n    {0x8018, 175},\n    {0x801f, 175},\n    {0x8029, 175},\n    {0xc038, 175},\n    {0x8003, 180},\n    {0x8006, 180},\n    {0x800a, 180},\n    {0x800f, 180},\n    {0x8018, 180},\n    {0x801f, 180},\n    {0x8029, 180},\n    {0xc038, 180},\n  },\n  /* 168 */\n  {\n    {0x8003, 182},\n    {0x8006, 182},\n    {0x800a, 182},\n    {0x800f, 182},\n    {0x8018, 182},\n    {0x801f, 182},\n    {0x8029, 182},\n    {0xc038, 182},\n    {0x8003, 183},\n    {0x8006, 183},\n    {0x800a, 183},\n    {0x800f, 183},\n    {0x8018, 183},\n    {0x801f, 183},\n    {0x8029, 183},\n    {0xc038, 183},\n  },\n  /* 169 */\n  {\n    {0xc000, 188},\n    {0xc000, 191},\n    {0xc000, 197},\n    {0xc000, 231},\n    {0xc000, 239},\n    {0xb0, 0},\n    {0xb2, 0},\n    {0xb3, 0},\n    {0xb7, 0},\n    {0xb8, 0},\n    {0xba, 0},\n    {0xbb, 0},\n    {0xc0, 0},\n    {0xc7, 0},\n    {0xd0, 0},\n    {0xdf, 0},\n  },\n  /* 170 */\n  {\n    {0x8001, 188},\n    {0xc016, 188},\n    {0x8001, 191},\n    {0xc016, 191},\n    {0x8001, 197},\n    {0xc016, 197},\n    {0x8001, 231},\n    {0xc016, 231},\n    {0x8001, 239},\n    {0xc016, 239},\n    {0xc000, 9},\n    {0xc000, 142},\n    {0xc000, 144},\n    {0xc000, 145},\n    {0xc000, 148},\n    {0xc000, 159},\n  },\n  /* 171 */\n  {\n    {0x8002, 188},\n    {0x8009, 188},\n    {0x8017, 188},\n    {0xc028, 188},\n    {0x8002, 191},\n    {0x8009, 191},\n    {0x8017, 191},\n    {0xc028, 191},\n    {0x8002, 197},\n    {0x8009, 197},\n    {0x8017, 197},\n    {0xc028, 197},\n    {0x8002, 231},\n    {0x8009, 231},\n    {0x8017, 231},\n    {0xc028, 231},\n  },\n  /* 172 */\n  {\n    {0x8003, 188},\n    {0x8006, 188},\n    {0x800a, 188},\n    {0x800f, 188},\n    {0x8018, 188},\n    {0x801f, 188},\n    {0x8029, 188},\n    {0xc038, 188},\n    {0x8003, 191},\n    {0x8006, 191},\n    {0x800a, 191},\n    {0x800f, 191},\n    {0x8018, 191},\n    {0x801f, 191},\n    {0x8029, 191},\n    {0xc038, 191},\n  },\n  /* 173 */\n  {\n    {0x8003, 197},\n    {0x8006, 197},\n    {0x800a, 197},\n    {0x800f, 197},\n    {0x8018, 197},\n    {0x801f, 197},\n    {0x8029, 197},\n    {0xc038, 197},\n    {0x8003, 231},\n    {0x8006, 231},\n    {0x800a, 231},\n    {0x800f, 231},\n    {0x8018, 231},\n    {0x801f, 231},\n    {0x8029, 231},\n    {0xc038, 231},\n  },\n  /* 174 */\n  {\n    {0x8002, 239},\n    {0x8009, 239},\n    {0x8017, 239},\n    {0xc028, 239},\n    {0x8001, 9},\n    {0xc016, 9},\n    {0x8001, 142},\n    {0xc016, 142},\n    {0x8001, 144},\n    {0xc016, 144},\n    {0x8001, 145},\n    {0xc016, 145},\n    {0x8001, 148},\n    {0xc016, 148},\n    {0x8001, 159},\n    {0xc016, 159},\n  },\n  /* 175 */\n  {\n    {0x8003, 239},\n    {0x8006, 239},\n    {0x800a, 239},\n    {0x800f, 239},\n    {0x8018, 239},\n    {0x801f, 239},\n    {0x8029, 239},\n    {0xc038, 239},\n    {0x8002, 9},\n    {0x8009, 9},\n    {0x8017, 9},\n    {0xc028, 9},\n    {0x8002, 142},\n    {0x8009, 142},\n    {0x8017, 142},\n    {0xc028, 142},\n  },\n  /* 176 */\n  {\n    {0x8003, 9},\n    {0x8006, 9},\n    {0x800a, 9},\n    {0x800f, 9},\n    {0x8018, 9},\n    {0x801f, 9},\n    {0x8029, 9},\n    {0xc038, 9},\n    {0x8003, 142},\n    {0x8006, 142},\n    {0x800a, 142},\n    {0x800f, 142},\n    {0x8018, 142},\n    {0x801f, 142},\n    {0x8029, 142},\n    {0xc038, 142},\n  },\n  /* 177 */\n  {\n    {0x8002, 144},\n    {0x8009, 144},\n    {0x8017, 144},\n    {0xc028, 144},\n    {0x8002, 145},\n    {0x8009, 145},\n    {0x8017, 145},\n    {0xc028, 145},\n    {0x8002, 148},\n    {0x8009, 148},\n    {0x8017, 148},\n    {0xc028, 148},\n    {0x8002, 159},\n    {0x8009, 159},\n    {0x8017, 159},\n    {0xc028, 159},\n  },\n  /* 178 */\n  {\n    {0x8003, 144},\n    {0x8006, 144},\n    {0x800a, 144},\n    {0x800f, 144},\n    {0x8018, 144},\n    {0x801f, 144},\n    {0x8029, 144},\n    {0xc038, 144},\n    {0x8003, 145},\n    {0x8006, 145},\n    {0x800a, 145},\n    {0x800f, 145},\n    {0x8018, 145},\n    {0x801f, 145},\n    {0x8029, 145},\n    {0xc038, 145},\n  },\n  /* 179 */\n  {\n    {0x8003, 148},\n    {0x8006, 148},\n    {0x800a, 148},\n    {0x800f, 148},\n    {0x8018, 148},\n    {0x801f, 148},\n    {0x8029, 148},\n    {0xc038, 148},\n    {0x8003, 159},\n    {0x8006, 159},\n    {0x800a, 159},\n    {0x800f, 159},\n    {0x8018, 159},\n    {0x801f, 159},\n    {0x8029, 159},\n    {0xc038, 159},\n  },\n  /* 180 */\n  {\n    {0xc000, 171},\n    {0xc000, 206},\n    {0xc000, 215},\n    {0xc000, 225},\n    {0xc000, 236},\n    {0xc000, 237},\n    {0xbc, 0},\n    {0xbd, 0},\n    {0xc1, 0},\n    {0xc4, 0},\n    {0xc8, 0},\n    {0xcb, 0},\n    {0xd1, 0},\n    {0xd8, 0},\n    {0xe0, 0},\n    {0xee, 0},\n  },\n  /* 181 */\n  {\n    {0x8001, 171},\n    {0xc016, 171},\n    {0x8001, 206},\n    {0xc016, 206},\n    {0x8001, 215},\n    {0xc016, 215},\n    {0x8001, 225},\n    {0xc016, 225},\n    {0x8001, 236},\n    {0xc016, 236},\n    {0x8001, 237},\n    {0xc016, 237},\n    {0xc000, 199},\n    {0xc000, 207},\n    {0xc000, 234},\n    {0xc000, 235},\n  },\n  /* 182 */\n  {\n    {0x8002, 171},\n    {0x8009, 171},\n    {0x8017, 171},\n    {0xc028, 171},\n    {0x8002, 206},\n    {0x8009, 206},\n    {0x8017, 206},\n    {0xc028, 206},\n    {0x8002, 215},\n    {0x8009, 215},\n    {0x8017, 215},\n    {0xc028, 215},\n    {0x8002, 225},\n    {0x8009, 225},\n    {0x8017, 225},\n    {0xc028, 225},\n  },\n  /* 183 */\n  {\n    {0x8003, 171},\n    {0x8006, 171},\n    {0x800a, 171},\n    {0x800f, 171},\n    {0x8018, 171},\n    {0x801f, 171},\n    {0x8029, 171},\n    {0xc038, 171},\n    {0x8003, 206},\n    {0x8006, 206},\n    {0x800a, 206},\n    {0x800f, 206},\n    {0x8018, 206},\n    {0x801f, 206},\n    {0x8029, 206},\n    {0xc038, 206},\n  },\n  /* 184 */\n  {\n    {0x8003, 215},\n    {0x8006, 215},\n    {0x800a, 215},\n    {0x800f, 215},\n    {0x8018, 215},\n    {0x801f, 215},\n    {0x8029, 215},\n    {0xc038, 215},\n    {0x8003, 225},\n    {0x8006, 225},\n    {0x800a, 225},\n    {0x800f, 225},\n    {0x8018, 225},\n    {0x801f, 225},\n    {0x8029, 225},\n    {0xc038, 225},\n  },\n  /* 185 */\n  {\n    {0x8002, 236},\n    {0x8009, 236},\n    {0x8017, 236},\n    {0xc028, 236},\n    {0x8002, 237},\n    {0x8009, 237},\n    {0x8017, 237},\n    {0xc028, 237},\n    {0x8001, 199},\n    {0xc016, 199},\n    {0x8001, 207},\n    {0xc016, 207},\n    {0x8001, 234},\n    {0xc016, 234},\n    {0x8001, 235},\n    {0xc016, 235},\n  },\n  /* 186 */\n  {\n    {0x8003, 236},\n    {0x8006, 236},\n    {0x800a, 236},\n    {0x800f, 236},\n    {0x8018, 236},\n    {0x801f, 236},\n    {0x8029, 236},\n    {0xc038, 236},\n    {0x8003, 237},\n    {0x8006, 237},\n    {0x800a, 237},\n    {0x800f, 237},\n    {0x8018, 237},\n    {0x801f, 237},\n    {0x8029, 237},\n    {0xc038, 237},\n  },\n  /* 187 */\n  {\n    {0x8002, 199},\n    {0x8009, 199},\n    {0x8017, 199},\n    {0xc028, 199},\n    {0x8002, 207},\n    {0x8009, 207},\n    {0x8017, 207},\n    {0xc028, 207},\n    {0x8002, 234},\n    {0x8009, 234},\n    {0x8017, 234},\n    {0xc028, 234},\n    {0x8002, 235},\n    {0x8009, 235},\n    {0x8017, 235},\n    {0xc028, 235},\n  },\n  /* 188 */\n  {\n    {0x8003, 199},\n    {0x8006, 199},\n    {0x800a, 199},\n    {0x800f, 199},\n    {0x8018, 199},\n    {0x801f, 199},\n    {0x8029, 199},\n    {0xc038, 199},\n    {0x8003, 207},\n    {0x8006, 207},\n    {0x800a, 207},\n    {0x800f, 207},\n    {0x8018, 207},\n    {0x801f, 207},\n    {0x8029, 207},\n    {0xc038, 207},\n  },\n  /* 189 */\n  {\n    {0x8003, 234},\n    {0x8006, 234},\n    {0x800a, 234},\n    {0x800f, 234},\n    {0x8018, 234},\n    {0x801f, 234},\n    {0x8029, 234},\n    {0xc038, 234},\n    {0x8003, 235},\n    {0x8006, 235},\n    {0x800a, 235},\n    {0x800f, 235},\n    {0x8018, 235},\n    {0x801f, 235},\n    {0x8029, 235},\n    {0xc038, 235},\n  },\n  /* 190 */\n  {\n    {0xc2, 0},\n    {0xc3, 0},\n    {0xc5, 0},\n    {0xc6, 0},\n    {0xc9, 0},\n    {0xca, 0},\n    {0xcc, 0},\n    {0xcd, 0},\n    {0xd2, 0},\n    {0xd5, 0},\n    {0xd9, 0},\n    {0xdc, 0},\n    {0xe1, 0},\n    {0xe7, 0},\n    {0xef, 0},\n    {0xf6, 0},\n  },\n  /* 191 */\n  {\n    {0xc000, 192},\n    {0xc000, 193},\n    {0xc000, 200},\n    {0xc000, 201},\n    {0xc000, 202},\n    {0xc000, 205},\n    {0xc000, 210},\n    {0xc000, 213},\n    {0xc000, 218},\n    {0xc000, 219},\n    {0xc000, 238},\n    {0xc000, 240},\n    {0xc000, 242},\n    {0xc000, 243},\n    {0xc000, 255},\n    {0xce, 0},\n  },\n  /* 192 */\n  {\n    {0x8001, 192},\n    {0xc016, 192},\n    {0x8001, 193},\n    {0xc016, 193},\n    {0x8001, 200},\n    {0xc016, 200},\n    {0x8001, 201},\n    {0xc016, 201},\n    {0x8001, 202},\n    {0xc016, 202},\n    {0x8001, 205},\n    {0xc016, 205},\n    {0x8001, 210},\n    {0xc016, 210},\n    {0x8001, 213},\n    {0xc016, 213},\n  },\n  /* 193 */\n  {\n    {0x8002, 192},\n    {0x8009, 192},\n    {0x8017, 192},\n    {0xc028, 192},\n    {0x8002, 193},\n    {0x8009, 193},\n    {0x8017, 193},\n    {0xc028, 193},\n    {0x8002, 200},\n    {0x8009, 200},\n    {0x8017, 200},\n    {0xc028, 200},\n    {0x8002, 201},\n    {0x8009, 201},\n    {0x8017, 201},\n    {0xc028, 201},\n  },\n  /* 194 */\n  {\n    {0x8003, 192},\n    {0x8006, 192},\n    {0x800a, 192},\n    {0x800f, 192},\n    {0x8018, 192},\n    {0x801f, 192},\n    {0x8029, 192},\n    {0xc038, 192},\n    {0x8003, 193},\n    {0x8006, 193},\n    {0x800a, 193},\n    {0x800f, 193},\n    {0x8018, 193},\n    {0x801f, 193},\n    {0x8029, 193},\n    {0xc038, 193},\n  },\n  /* 195 */\n  {\n    {0x8003, 200},\n    {0x8006, 200},\n    {0x800a, 200},\n    {0x800f, 200},\n    {0x8018, 200},\n    {0x801f, 200},\n    {0x8029, 200},\n    {0xc038, 200},\n    {0x8003, 201},\n    {0x8006, 201},\n    {0x800a, 201},\n    {0x800f, 201},\n    {0x8018, 201},\n    {0x801f, 201},\n    {0x8029, 201},\n    {0xc038, 201},\n  },\n  /* 196 */\n  {\n    {0x8002, 202},\n    {0x8009, 202},\n    {0x8017, 202},\n    {0xc028, 202},\n    {0x8002, 205},\n    {0x8009, 205},\n    {0x8017, 205},\n    {0xc028, 205},\n    {0x8002, 210},\n    {0x8009, 210},\n    {0x8017, 210},\n    {0xc028, 210},\n    {0x8002, 213},\n    {0x8009, 213},\n    {0x8017, 213},\n    {0xc028, 213},\n  },\n  /* 197 */\n  {\n    {0x8003, 202},\n    {0x8006, 202},\n    {0x800a, 202},\n    {0x800f, 202},\n    {0x8018, 202},\n    {0x801f, 202},\n    {0x8029, 202},\n    {0xc038, 202},\n    {0x8003, 205},\n    {0x8006, 205},\n    {0x800a, 205},\n    {0x800f, 205},\n    {0x8018, 205},\n    {0x801f, 205},\n    {0x8029, 205},\n    {0xc038, 205},\n  },\n  /* 198 */\n  {\n    {0x8003, 210},\n    {0x8006, 210},\n    {0x800a, 210},\n    {0x800f, 210},\n    {0x8018, 210},\n    {0x801f, 210},\n    {0x8029, 210},\n    {0xc038, 210},\n    {0x8003, 213},\n    {0x8006, 213},\n    {0x800a, 213},\n    {0x800f, 213},\n    {0x8018, 213},\n    {0x801f, 213},\n    {0x8029, 213},\n    {0xc038, 213},\n  },\n  /* 199 */\n  {\n    {0x8001, 218},\n    {0xc016, 218},\n    {0x8001, 219},\n    {0xc016, 219},\n    {0x8001, 238},\n    {0xc016, 238},\n    {0x8001, 240},\n    {0xc016, 240},\n    {0x8001, 242},\n    {0xc016, 242},\n    {0x8001, 243},\n    {0xc016, 243},\n    {0x8001, 255},\n    {0xc016, 255},\n    {0xc000, 203},\n    {0xc000, 204},\n  },\n  /* 200 */\n  {\n    {0x8002, 218},\n    {0x8009, 218},\n    {0x8017, 218},\n    {0xc028, 218},\n    {0x8002, 219},\n    {0x8009, 219},\n    {0x8017, 219},\n    {0xc028, 219},\n    {0x8002, 238},\n    {0x8009, 238},\n    {0x8017, 238},\n    {0xc028, 238},\n    {0x8002, 240},\n    {0x8009, 240},\n    {0x8017, 240},\n    {0xc028, 240},\n  },\n  /* 201 */\n  {\n    {0x8003, 218},\n    {0x8006, 218},\n    {0x800a, 218},\n    {0x800f, 218},\n    {0x8018, 218},\n    {0x801f, 218},\n    {0x8029, 218},\n    {0xc038, 218},\n    {0x8003, 219},\n    {0x8006, 219},\n    {0x800a, 219},\n    {0x800f, 219},\n    {0x8018, 219},\n    {0x801f, 219},\n    {0x8029, 219},\n    {0xc038, 219},\n  },\n  /* 202 */\n  {\n    {0x8003, 238},\n    {0x8006, 238},\n    {0x800a, 238},\n    {0x800f, 238},\n    {0x8018, 238},\n    {0x801f, 238},\n    {0x8029, 238},\n    {0xc038, 238},\n    {0x8003, 240},\n    {0x8006, 240},\n    {0x800a, 240},\n    {0x800f, 240},\n    {0x8018, 240},\n    {0x801f, 240},\n    {0x8029, 240},\n    {0xc038, 240},\n  },\n  /* 203 */\n  {\n    {0x8002, 242},\n    {0x8009, 242},\n    {0x8017, 242},\n    {0xc028, 242},\n    {0x8002, 243},\n    {0x8009, 243},\n    {0x8017, 243},\n    {0xc028, 243},\n    {0x8002, 255},\n    {0x8009, 255},\n    {0x8017, 255},\n    {0xc028, 255},\n    {0x8001, 203},\n    {0xc016, 203},\n    {0x8001, 204},\n    {0xc016, 204},\n  },\n  /* 204 */\n  {\n    {0x8003, 242},\n    {0x8006, 242},\n    {0x800a, 242},\n    {0x800f, 242},\n    {0x8018, 242},\n    {0x801f, 242},\n    {0x8029, 242},\n    {0xc038, 242},\n    {0x8003, 243},\n    {0x8006, 243},\n    {0x800a, 243},\n    {0x800f, 243},\n    {0x8018, 243},\n    {0x801f, 243},\n    {0x8029, 243},\n    {0xc038, 243},\n  },\n  /* 205 */\n  {\n    {0x8003, 255},\n    {0x8006, 255},\n    {0x800a, 255},\n    {0x800f, 255},\n    {0x8018, 255},\n    {0x801f, 255},\n    {0x8029, 255},\n    {0xc038, 255},\n    {0x8002, 203},\n    {0x8009, 203},\n    {0x8017, 203},\n    {0xc028, 203},\n    {0x8002, 204},\n    {0x8009, 204},\n    {0x8017, 204},\n    {0xc028, 204},\n  },\n  /* 206 */\n  {\n    {0x8003, 203},\n    {0x8006, 203},\n    {0x800a, 203},\n    {0x800f, 203},\n    {0x8018, 203},\n    {0x801f, 203},\n    {0x8029, 203},\n    {0xc038, 203},\n    {0x8003, 204},\n    {0x8006, 204},\n    {0x800a, 204},\n    {0x800f, 204},\n    {0x8018, 204},\n    {0x801f, 204},\n    {0x8029, 204},\n    {0xc038, 204},\n  },\n  /* 207 */\n  {\n    {0xd3, 0},\n    {0xd4, 0},\n    {0xd6, 0},\n    {0xd7, 0},\n    {0xda, 0},\n    {0xdb, 0},\n    {0xdd, 0},\n    {0xde, 0},\n    {0xe2, 0},\n    {0xe4, 0},\n    {0xe8, 0},\n    {0xeb, 0},\n    {0xf0, 0},\n    {0xf3, 0},\n    {0xf7, 0},\n    {0xfa, 0},\n  },\n  /* 208 */\n  {\n    {0xc000, 211},\n    {0xc000, 212},\n    {0xc000, 214},\n    {0xc000, 221},\n    {0xc000, 222},\n    {0xc000, 223},\n    {0xc000, 241},\n    {0xc000, 244},\n    {0xc000, 245},\n    {0xc000, 246},\n    {0xc000, 247},\n    {0xc000, 248},\n    {0xc000, 250},\n    {0xc000, 251},\n    {0xc000, 252},\n    {0xc000, 253},\n  },\n  /* 209 */\n  {\n    {0x8001, 211},\n    {0xc016, 211},\n    {0x8001, 212},\n    {0xc016, 212},\n    {0x8001, 214},\n    {0xc016, 214},\n    {0x8001, 221},\n    {0xc016, 221},\n    {0x8001, 222},\n    {0xc016, 222},\n    {0x8001, 223},\n    {0xc016, 223},\n    {0x8001, 241},\n    {0xc016, 241},\n    {0x8001, 244},\n    {0xc016, 244},\n  },\n  /* 210 */\n  {\n    {0x8002, 211},\n    {0x8009, 211},\n    {0x8017, 211},\n    {0xc028, 211},\n    {0x8002, 212},\n    {0x8009, 212},\n    {0x8017, 212},\n    {0xc028, 212},\n    {0x8002, 214},\n    {0x8009, 214},\n    {0x8017, 214},\n    {0xc028, 214},\n    {0x8002, 221},\n    {0x8009, 221},\n    {0x8017, 221},\n    {0xc028, 221},\n  },\n  /* 211 */\n  {\n    {0x8003, 211},\n    {0x8006, 211},\n    {0x800a, 211},\n    {0x800f, 211},\n    {0x8018, 211},\n    {0x801f, 211},\n    {0x8029, 211},\n    {0xc038, 211},\n    {0x8003, 212},\n    {0x8006, 212},\n    {0x800a, 212},\n    {0x800f, 212},\n    {0x8018, 212},\n    {0x801f, 212},\n    {0x8029, 212},\n    {0xc038, 212},\n  },\n  /* 212 */\n  {\n    {0x8003, 214},\n    {0x8006, 214},\n    {0x800a, 214},\n    {0x800f, 214},\n    {0x8018, 214},\n    {0x801f, 214},\n    {0x8029, 214},\n    {0xc038, 214},\n    {0x8003, 221},\n    {0x8006, 221},\n    {0x800a, 221},\n    {0x800f, 221},\n    {0x8018, 221},\n    {0x801f, 221},\n    {0x8029, 221},\n    {0xc038, 221},\n  },\n  /* 213 */\n  {\n    {0x8002, 222},\n    {0x8009, 222},\n    {0x8017, 222},\n    {0xc028, 222},\n    {0x8002, 223},\n    {0x8009, 223},\n    {0x8017, 223},\n    {0xc028, 223},\n    {0x8002, 241},\n    {0x8009, 241},\n    {0x8017, 241},\n    {0xc028, 241},\n    {0x8002, 244},\n    {0x8009, 244},\n    {0x8017, 244},\n    {0xc028, 244},\n  },\n  /* 214 */\n  {\n    {0x8003, 222},\n    {0x8006, 222},\n    {0x800a, 222},\n    {0x800f, 222},\n    {0x8018, 222},\n    {0x801f, 222},\n    {0x8029, 222},\n    {0xc038, 222},\n    {0x8003, 223},\n    {0x8006, 223},\n    {0x800a, 223},\n    {0x800f, 223},\n    {0x8018, 223},\n    {0x801f, 223},\n    {0x8029, 223},\n    {0xc038, 223},\n  },\n  /* 215 */\n  {\n    {0x8003, 241},\n    {0x8006, 241},\n    {0x800a, 241},\n    {0x800f, 241},\n    {0x8018, 241},\n    {0x801f, 241},\n    {0x8029, 241},\n    {0xc038, 241},\n    {0x8003, 244},\n    {0x8006, 244},\n    {0x800a, 244},\n    {0x800f, 244},\n    {0x8018, 244},\n    {0x801f, 244},\n    {0x8029, 244},\n    {0xc038, 244},\n  },\n  /* 216 */\n  {\n    {0x8001, 245},\n    {0xc016, 245},\n    {0x8001, 246},\n    {0xc016, 246},\n    {0x8001, 247},\n    {0xc016, 247},\n    {0x8001, 248},\n    {0xc016, 248},\n    {0x8001, 250},\n    {0xc016, 250},\n    {0x8001, 251},\n    {0xc016, 251},\n    {0x8001, 252},\n    {0xc016, 252},\n    {0x8001, 253},\n    {0xc016, 253},\n  },\n  /* 217 */\n  {\n    {0x8002, 245},\n    {0x8009, 245},\n    {0x8017, 245},\n    {0xc028, 245},\n    {0x8002, 246},\n    {0x8009, 246},\n    {0x8017, 246},\n    {0xc028, 246},\n    {0x8002, 247},\n    {0x8009, 247},\n    {0x8017, 247},\n    {0xc028, 247},\n    {0x8002, 248},\n    {0x8009, 248},\n    {0x8017, 248},\n    {0xc028, 248},\n  },\n  /* 218 */\n  {\n    {0x8003, 245},\n    {0x8006, 245},\n    {0x800a, 245},\n    {0x800f, 245},\n    {0x8018, 245},\n    {0x801f, 245},\n    {0x8029, 245},\n    {0xc038, 245},\n    {0x8003, 246},\n    {0x8006, 246},\n    {0x800a, 246},\n    {0x800f, 246},\n    {0x8018, 246},\n    {0x801f, 246},\n    {0x8029, 246},\n    {0xc038, 246},\n  },\n  /* 219 */\n  {\n    {0x8003, 247},\n    {0x8006, 247},\n    {0x800a, 247},\n    {0x800f, 247},\n    {0x8018, 247},\n    {0x801f, 247},\n    {0x8029, 247},\n    {0xc038, 247},\n    {0x8003, 248},\n    {0x8006, 248},\n    {0x800a, 248},\n    {0x800f, 248},\n    {0x8018, 248},\n    {0x801f, 248},\n    {0x8029, 248},\n    {0xc038, 248},\n  },\n  /* 220 */\n  {\n    {0x8002, 250},\n    {0x8009, 250},\n    {0x8017, 250},\n    {0xc028, 250},\n    {0x8002, 251},\n    {0x8009, 251},\n    {0x8017, 251},\n    {0xc028, 251},\n    {0x8002, 252},\n    {0x8009, 252},\n    {0x8017, 252},\n    {0xc028, 252},\n    {0x8002, 253},\n    {0x8009, 253},\n    {0x8017, 253},\n    {0xc028, 253},\n  },\n  /* 221 */\n  {\n    {0x8003, 250},\n    {0x8006, 250},\n    {0x800a, 250},\n    {0x800f, 250},\n    {0x8018, 250},\n    {0x801f, 250},\n    {0x8029, 250},\n    {0xc038, 250},\n    {0x8003, 251},\n    {0x8006, 251},\n    {0x800a, 251},\n    {0x800f, 251},\n    {0x8018, 251},\n    {0x801f, 251},\n    {0x8029, 251},\n    {0xc038, 251},\n  },\n  /* 222 */\n  {\n    {0x8003, 252},\n    {0x8006, 252},\n    {0x800a, 252},\n    {0x800f, 252},\n    {0x8018, 252},\n    {0x801f, 252},\n    {0x8029, 252},\n    {0xc038, 252},\n    {0x8003, 253},\n    {0x8006, 253},\n    {0x800a, 253},\n    {0x800f, 253},\n    {0x8018, 253},\n    {0x801f, 253},\n    {0x8029, 253},\n    {0xc038, 253},\n  },\n  /* 223 */\n  {\n    {0xc000, 254},\n    {0xe3, 0},\n    {0xe5, 0},\n    {0xe6, 0},\n    {0xe9, 0},\n    {0xea, 0},\n    {0xec, 0},\n    {0xed, 0},\n    {0xf1, 0},\n    {0xf2, 0},\n    {0xf4, 0},\n    {0xf5, 0},\n    {0xf8, 0},\n    {0xf9, 0},\n    {0xfb, 0},\n    {0xfc, 0},\n  },\n  /* 224 */\n  {\n    {0x8001, 254},\n    {0xc016, 254},\n    {0xc000, 2},\n    {0xc000, 3},\n    {0xc000, 4},\n    {0xc000, 5},\n    {0xc000, 6},\n    {0xc000, 7},\n    {0xc000, 8},\n    {0xc000, 11},\n    {0xc000, 12},\n    {0xc000, 14},\n    {0xc000, 15},\n    {0xc000, 16},\n    {0xc000, 17},\n    {0xc000, 18},\n  },\n  /* 225 */\n  {\n    {0x8002, 254},\n    {0x8009, 254},\n    {0x8017, 254},\n    {0xc028, 254},\n    {0x8001, 2},\n    {0xc016, 2},\n    {0x8001, 3},\n    {0xc016, 3},\n    {0x8001, 4},\n    {0xc016, 4},\n    {0x8001, 5},\n    {0xc016, 5},\n    {0x8001, 6},\n    {0xc016, 6},\n    {0x8001, 7},\n    {0xc016, 7},\n  },\n  /* 226 */\n  {\n    {0x8003, 254},\n    {0x8006, 254},\n    {0x800a, 254},\n    {0x800f, 254},\n    {0x8018, 254},\n    {0x801f, 254},\n    {0x8029, 254},\n    {0xc038, 254},\n    {0x8002, 2},\n    {0x8009, 2},\n    {0x8017, 2},\n    {0xc028, 2},\n    {0x8002, 3},\n    {0x8009, 3},\n    {0x8017, 3},\n    {0xc028, 3},\n  },\n  /* 227 */\n  {\n    {0x8003, 2},\n    {0x8006, 2},\n    {0x800a, 2},\n    {0x800f, 2},\n    {0x8018, 2},\n    {0x801f, 2},\n    {0x8029, 2},\n    {0xc038, 2},\n    {0x8003, 3},\n    {0x8006, 3},\n    {0x800a, 3},\n    {0x800f, 3},\n    {0x8018, 3},\n    {0x801f, 3},\n    {0x8029, 3},\n    {0xc038, 3},\n  },\n  /* 228 */\n  {\n    {0x8002, 4},\n    {0x8009, 4},\n    {0x8017, 4},\n    {0xc028, 4},\n    {0x8002, 5},\n    {0x8009, 5},\n    {0x8017, 5},\n    {0xc028, 5},\n    {0x8002, 6},\n    {0x8009, 6},\n    {0x8017, 6},\n    {0xc028, 6},\n    {0x8002, 7},\n    {0x8009, 7},\n    {0x8017, 7},\n    {0xc028, 7},\n  },\n  /* 229 */\n  {\n    {0x8003, 4},\n    {0x8006, 4},\n    {0x800a, 4},\n    {0x800f, 4},\n    {0x8018, 4},\n    {0x801f, 4},\n    {0x8029, 4},\n    {0xc038, 4},\n    {0x8003, 5},\n    {0x8006, 5},\n    {0x800a, 5},\n    {0x800f, 5},\n    {0x8018, 5},\n    {0x801f, 5},\n    {0x8029, 5},\n    {0xc038, 5},\n  },\n  /* 230 */\n  {\n    {0x8003, 6},\n    {0x8006, 6},\n    {0x800a, 6},\n    {0x800f, 6},\n    {0x8018, 6},\n    {0x801f, 6},\n    {0x8029, 6},\n    {0xc038, 6},\n    {0x8003, 7},\n    {0x8006, 7},\n    {0x800a, 7},\n    {0x800f, 7},\n    {0x8018, 7},\n    {0x801f, 7},\n    {0x8029, 7},\n    {0xc038, 7},\n  },\n  /* 231 */\n  {\n    {0x8001, 8},\n    {0xc016, 8},\n    {0x8001, 11},\n    {0xc016, 11},\n    {0x8001, 12},\n    {0xc016, 12},\n    {0x8001, 14},\n    {0xc016, 14},\n    {0x8001, 15},\n    {0xc016, 15},\n    {0x8001, 16},\n    {0xc016, 16},\n    {0x8001, 17},\n    {0xc016, 17},\n    {0x8001, 18},\n    {0xc016, 18},\n  },\n  /* 232 */\n  {\n    {0x8002, 8},\n    {0x8009, 8},\n    {0x8017, 8},\n    {0xc028, 8},\n    {0x8002, 11},\n    {0x8009, 11},\n    {0x8017, 11},\n    {0xc028, 11},\n    {0x8002, 12},\n    {0x8009, 12},\n    {0x8017, 12},\n    {0xc028, 12},\n    {0x8002, 14},\n    {0x8009, 14},\n    {0x8017, 14},\n    {0xc028, 14},\n  },\n  /* 233 */\n  {\n    {0x8003, 8},\n    {0x8006, 8},\n    {0x800a, 8},\n    {0x800f, 8},\n    {0x8018, 8},\n    {0x801f, 8},\n    {0x8029, 8},\n    {0xc038, 8},\n    {0x8003, 11},\n    {0x8006, 11},\n    {0x800a, 11},\n    {0x800f, 11},\n    {0x8018, 11},\n    {0x801f, 11},\n    {0x8029, 11},\n    {0xc038, 11},\n  },\n  /* 234 */\n  {\n    {0x8003, 12},\n    {0x8006, 12},\n    {0x800a, 12},\n    {0x800f, 12},\n    {0x8018, 12},\n    {0x801f, 12},\n    {0x8029, 12},\n    {0xc038, 12},\n    {0x8003, 14},\n    {0x8006, 14},\n    {0x800a, 14},\n    {0x800f, 14},\n    {0x8018, 14},\n    {0x801f, 14},\n    {0x8029, 14},\n    {0xc038, 14},\n  },\n  /* 235 */\n  {\n    {0x8002, 15},\n    {0x8009, 15},\n    {0x8017, 15},\n    {0xc028, 15},\n    {0x8002, 16},\n    {0x8009, 16},\n    {0x8017, 16},\n    {0xc028, 16},\n    {0x8002, 17},\n    {0x8009, 17},\n    {0x8017, 17},\n    {0xc028, 17},\n    {0x8002, 18},\n    {0x8009, 18},\n    {0x8017, 18},\n    {0xc028, 18},\n  },\n  /* 236 */\n  {\n    {0x8003, 15},\n    {0x8006, 15},\n    {0x800a, 15},\n    {0x800f, 15},\n    {0x8018, 15},\n    {0x801f, 15},\n    {0x8029, 15},\n    {0xc038, 15},\n    {0x8003, 16},\n    {0x8006, 16},\n    {0x800a, 16},\n    {0x800f, 16},\n    {0x8018, 16},\n    {0x801f, 16},\n    {0x8029, 16},\n    {0xc038, 16},\n  },\n  /* 237 */\n  {\n    {0x8003, 17},\n    {0x8006, 17},\n    {0x800a, 17},\n    {0x800f, 17},\n    {0x8018, 17},\n    {0x801f, 17},\n    {0x8029, 17},\n    {0xc038, 17},\n    {0x8003, 18},\n    {0x8006, 18},\n    {0x800a, 18},\n    {0x800f, 18},\n    {0x8018, 18},\n    {0x801f, 18},\n    {0x8029, 18},\n    {0xc038, 18},\n  },\n  /* 238 */\n  {\n    {0xc000, 19},\n    {0xc000, 20},\n    {0xc000, 21},\n    {0xc000, 23},\n    {0xc000, 24},\n    {0xc000, 25},\n    {0xc000, 26},\n    {0xc000, 27},\n    {0xc000, 28},\n    {0xc000, 29},\n    {0xc000, 30},\n    {0xc000, 31},\n    {0xc000, 127},\n    {0xc000, 220},\n    {0xc000, 249},\n    {0xfd, 0},\n  },\n  /* 239 */\n  {\n    {0x8001, 19},\n    {0xc016, 19},\n    {0x8001, 20},\n    {0xc016, 20},\n    {0x8001, 21},\n    {0xc016, 21},\n    {0x8001, 23},\n    {0xc016, 23},\n    {0x8001, 24},\n    {0xc016, 24},\n    {0x8001, 25},\n    {0xc016, 25},\n    {0x8001, 26},\n    {0xc016, 26},\n    {0x8001, 27},\n    {0xc016, 27},\n  },\n  /* 240 */\n  {\n    {0x8002, 19},\n    {0x8009, 19},\n    {0x8017, 19},\n    {0xc028, 19},\n    {0x8002, 20},\n    {0x8009, 20},\n    {0x8017, 20},\n    {0xc028, 20},\n    {0x8002, 21},\n    {0x8009, 21},\n    {0x8017, 21},\n    {0xc028, 21},\n    {0x8002, 23},\n    {0x8009, 23},\n    {0x8017, 23},\n    {0xc028, 23},\n  },\n  /* 241 */\n  {\n    {0x8003, 19},\n    {0x8006, 19},\n    {0x800a, 19},\n    {0x800f, 19},\n    {0x8018, 19},\n    {0x801f, 19},\n    {0x8029, 19},\n    {0xc038, 19},\n    {0x8003, 20},\n    {0x8006, 20},\n    {0x800a, 20},\n    {0x800f, 20},\n    {0x8018, 20},\n    {0x801f, 20},\n    {0x8029, 20},\n    {0xc038, 20},\n  },\n  /* 242 */\n  {\n    {0x8003, 21},\n    {0x8006, 21},\n    {0x800a, 21},\n    {0x800f, 21},\n    {0x8018, 21},\n    {0x801f, 21},\n    {0x8029, 21},\n    {0xc038, 21},\n    {0x8003, 23},\n    {0x8006, 23},\n    {0x800a, 23},\n    {0x800f, 23},\n    {0x8018, 23},\n    {0x801f, 23},\n    {0x8029, 23},\n    {0xc038, 23},\n  },\n  /* 243 */\n  {\n    {0x8002, 24},\n    {0x8009, 24},\n    {0x8017, 24},\n    {0xc028, 24},\n    {0x8002, 25},\n    {0x8009, 25},\n    {0x8017, 25},\n    {0xc028, 25},\n    {0x8002, 26},\n    {0x8009, 26},\n    {0x8017, 26},\n    {0xc028, 26},\n    {0x8002, 27},\n    {0x8009, 27},\n    {0x8017, 27},\n    {0xc028, 27},\n  },\n  /* 244 */\n  {\n    {0x8003, 24},\n    {0x8006, 24},\n    {0x800a, 24},\n    {0x800f, 24},\n    {0x8018, 24},\n    {0x801f, 24},\n    {0x8029, 24},\n    {0xc038, 24},\n    {0x8003, 25},\n    {0x8006, 25},\n    {0x800a, 25},\n    {0x800f, 25},\n    {0x8018, 25},\n    {0x801f, 25},\n    {0x8029, 25},\n    {0xc038, 25},\n  },\n  /* 245 */\n  {\n    {0x8003, 26},\n    {0x8006, 26},\n    {0x800a, 26},\n    {0x800f, 26},\n    {0x8018, 26},\n    {0x801f, 26},\n    {0x8029, 26},\n    {0xc038, 26},\n    {0x8003, 27},\n    {0x8006, 27},\n    {0x800a, 27},\n    {0x800f, 27},\n    {0x8018, 27},\n    {0x801f, 27},\n    {0x8029, 27},\n    {0xc038, 27},\n  },\n  /* 246 */\n  {\n    {0x8001, 28},\n    {0xc016, 28},\n    {0x8001, 29},\n    {0xc016, 29},\n    {0x8001, 30},\n    {0xc016, 30},\n    {0x8001, 31},\n    {0xc016, 31},\n    {0x8001, 127},\n    {0xc016, 127},\n    {0x8001, 220},\n    {0xc016, 220},\n    {0x8001, 249},\n    {0xc016, 249},\n    {0xfe, 0},\n    {0xff, 0},\n  },\n  /* 247 */\n  {\n    {0x8002, 28},\n    {0x8009, 28},\n    {0x8017, 28},\n    {0xc028, 28},\n    {0x8002, 29},\n    {0x8009, 29},\n    {0x8017, 29},\n    {0xc028, 29},\n    {0x8002, 30},\n    {0x8009, 30},\n    {0x8017, 30},\n    {0xc028, 30},\n    {0x8002, 31},\n    {0x8009, 31},\n    {0x8017, 31},\n    {0xc028, 31},\n  },\n  /* 248 */\n  {\n    {0x8003, 28},\n    {0x8006, 28},\n    {0x800a, 28},\n    {0x800f, 28},\n    {0x8018, 28},\n    {0x801f, 28},\n    {0x8029, 28},\n    {0xc038, 28},\n    {0x8003, 29},\n    {0x8006, 29},\n    {0x800a, 29},\n    {0x800f, 29},\n    {0x8018, 29},\n    {0x801f, 29},\n    {0x8029, 29},\n    {0xc038, 29},\n  },\n  /* 249 */\n  {\n    {0x8003, 30},\n    {0x8006, 30},\n    {0x800a, 30},\n    {0x800f, 30},\n    {0x8018, 30},\n    {0x801f, 30},\n    {0x8029, 30},\n    {0xc038, 30},\n    {0x8003, 31},\n    {0x8006, 31},\n    {0x800a, 31},\n    {0x800f, 31},\n    {0x8018, 31},\n    {0x801f, 31},\n    {0x8029, 31},\n    {0xc038, 31},\n  },\n  /* 250 */\n  {\n    {0x8002, 127},\n    {0x8009, 127},\n    {0x8017, 127},\n    {0xc028, 127},\n    {0x8002, 220},\n    {0x8009, 220},\n    {0x8017, 220},\n    {0xc028, 220},\n    {0x8002, 249},\n    {0x8009, 249},\n    {0x8017, 249},\n    {0xc028, 249},\n    {0xc000, 10},\n    {0xc000, 13},\n    {0xc000, 22},\n    {0x100, 0},\n  },\n  /* 251 */\n  {\n    {0x8003, 127},\n    {0x8006, 127},\n    {0x800a, 127},\n    {0x800f, 127},\n    {0x8018, 127},\n    {0x801f, 127},\n    {0x8029, 127},\n    {0xc038, 127},\n    {0x8003, 220},\n    {0x8006, 220},\n    {0x800a, 220},\n    {0x800f, 220},\n    {0x8018, 220},\n    {0x801f, 220},\n    {0x8029, 220},\n    {0xc038, 220},\n  },\n  /* 252 */\n  {\n    {0x8003, 249},\n    {0x8006, 249},\n    {0x800a, 249},\n    {0x800f, 249},\n    {0x8018, 249},\n    {0x801f, 249},\n    {0x8029, 249},\n    {0xc038, 249},\n    {0x8001, 10},\n    {0xc016, 10},\n    {0x8001, 13},\n    {0xc016, 13},\n    {0x8001, 22},\n    {0xc016, 22},\n    {0x100, 0},\n    {0x100, 0},\n  },\n  /* 253 */\n  {\n    {0x8002, 10},\n    {0x8009, 10},\n    {0x8017, 10},\n    {0xc028, 10},\n    {0x8002, 13},\n    {0x8009, 13},\n    {0x8017, 13},\n    {0xc028, 13},\n    {0x8002, 22},\n    {0x8009, 22},\n    {0x8017, 22},\n    {0xc028, 22},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n  },\n  /* 254 */\n  {\n    {0x8003, 10},\n    {0x8006, 10},\n    {0x800a, 10},\n    {0x800f, 10},\n    {0x8018, 10},\n    {0x801f, 10},\n    {0x8029, 10},\n    {0xc038, 10},\n    {0x8003, 13},\n    {0x8006, 13},\n    {0x800a, 13},\n    {0x800f, 13},\n    {0x8018, 13},\n    {0x801f, 13},\n    {0x8029, 13},\n    {0xc038, 13},\n  },\n  /* 255 */\n  {\n    {0x8003, 22},\n    {0x8006, 22},\n    {0x800a, 22},\n    {0x800f, 22},\n    {0x8018, 22},\n    {0x801f, 22},\n    {0x8029, 22},\n    {0xc038, 22},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n  },\n  /* 256 */\n  {\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n    {0x100, 0},\n  },\n};\n"
  },
  {
    "path": "thirdparty/nghttp2/nghttp2_helper.c",
    "content": "/*\n * nghttp2 - HTTP/2 C Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include \"nghttp2.h\"\n#include \"nghttp2_mem.h\"\n\n#include <netinet/in.h>\n#include <assert.h>\n#include <string.h>\n\n\nconst char *nghttp2_strerror(int error_code) {\n  switch (error_code) {\n  case 0:\n    return \"Success\";\n  case NGHTTP2_ERR_INVALID_ARGUMENT:\n    return \"Invalid argument\";\n  case NGHTTP2_ERR_BUFFER_ERROR:\n    return \"Out of buffer space\";\n  case NGHTTP2_ERR_UNSUPPORTED_VERSION:\n    return \"Unsupported SPDY version\";\n  case NGHTTP2_ERR_WOULDBLOCK:\n    return \"Operation would block\";\n  case NGHTTP2_ERR_PROTO:\n    return \"Protocol error\";\n  case NGHTTP2_ERR_INVALID_FRAME:\n    return \"Invalid frame octets\";\n  case NGHTTP2_ERR_EOF:\n    return \"EOF\";\n  case NGHTTP2_ERR_DEFERRED:\n    return \"Data transfer deferred\";\n  case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE:\n    return \"No more Stream ID available\";\n  case NGHTTP2_ERR_STREAM_CLOSED:\n    return \"Stream was already closed or invalid\";\n  case NGHTTP2_ERR_STREAM_CLOSING:\n    return \"Stream is closing\";\n  case NGHTTP2_ERR_STREAM_SHUT_WR:\n    return \"The transmission is not allowed for this stream\";\n  case NGHTTP2_ERR_INVALID_STREAM_ID:\n    return \"Stream ID is invalid\";\n  case NGHTTP2_ERR_INVALID_STREAM_STATE:\n    return \"Invalid stream state\";\n  case NGHTTP2_ERR_DEFERRED_DATA_EXIST:\n    return \"Another DATA frame has already been deferred\";\n  case NGHTTP2_ERR_START_STREAM_NOT_ALLOWED:\n    return \"request HEADERS is not allowed\";\n  case NGHTTP2_ERR_GOAWAY_ALREADY_SENT:\n    return \"GOAWAY has already been sent\";\n  case NGHTTP2_ERR_INVALID_HEADER_BLOCK:\n    return \"Invalid header block\";\n  case NGHTTP2_ERR_INVALID_STATE:\n    return \"Invalid state\";\n  case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE:\n    return \"The user callback function failed due to the temporal error\";\n  case NGHTTP2_ERR_FRAME_SIZE_ERROR:\n    return \"The length of the frame is invalid\";\n  case NGHTTP2_ERR_HEADER_COMP:\n    return \"Header compression/decompression error\";\n  case NGHTTP2_ERR_FLOW_CONTROL:\n    return \"Flow control error\";\n  case NGHTTP2_ERR_INSUFF_BUFSIZE:\n    return \"Insufficient buffer size given to function\";\n  case NGHTTP2_ERR_PAUSE:\n    return \"Callback was paused by the application\";\n  case NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS:\n    return \"Too many inflight SETTINGS\";\n  case NGHTTP2_ERR_PUSH_DISABLED:\n    return \"Server push is disabled by peer\";\n  case NGHTTP2_ERR_DATA_EXIST:\n    return \"DATA or HEADERS frame has already been submitted for the stream\";\n  case NGHTTP2_ERR_SESSION_CLOSING:\n    return \"The current session is closing\";\n  case NGHTTP2_ERR_HTTP_HEADER:\n    return \"Invalid HTTP header field was received\";\n  case NGHTTP2_ERR_HTTP_MESSAGING:\n    return \"Violation in HTTP messaging rule\";\n  case NGHTTP2_ERR_REFUSED_STREAM:\n    return \"Stream was refused\";\n  case NGHTTP2_ERR_INTERNAL:\n    return \"Internal error\";\n  case NGHTTP2_ERR_CANCEL:\n    return \"Cancel\";\n  case NGHTTP2_ERR_SETTINGS_EXPECTED:\n    return \"When a local endpoint expects to receive SETTINGS frame, it \"\n           \"receives an other type of frame\";\n  case NGHTTP2_ERR_NOMEM:\n    return \"Out of memory\";\n  case NGHTTP2_ERR_CALLBACK_FAILURE:\n    return \"The user callback function failed\";\n  case NGHTTP2_ERR_BAD_CLIENT_MAGIC:\n    return \"Received bad client magic byte string\";\n  case NGHTTP2_ERR_FLOODED:\n    return \"Flooding was detected in this HTTP/2 session, and it must be \"\n           \"closed\";\n  case NGHTTP2_ERR_TOO_MANY_SETTINGS:\n    return \"SETTINGS frame contained more than the maximum allowed entries\";\n  case NGHTTP2_ERR_TOO_MANY_CONTINUATIONS:\n    return \"Too many CONTINUATION frames following a HEADER frame\";\n  default:\n    return \"Unknown error code\";\n  }\n}\n"
  },
  {
    "path": "thirdparty/nghttp2/nghttp2_mem.c",
    "content": "/*\n * nghttp2 - HTTP/2 C Library\n *\n * Copyright (c) 2014 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"nghttp2_mem.h\"\n\nstatic void *default_malloc(size_t size, void *mem_user_data) {\n  (void)mem_user_data;\n\n  return malloc(size);\n}\n\nstatic void default_free(void *ptr, void *mem_user_data) {\n  (void)mem_user_data;\n\n  free(ptr);\n}\n\nstatic void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) {\n  (void)mem_user_data;\n\n  return calloc(nmemb, size);\n}\n\nstatic void *default_realloc(void *ptr, size_t size, void *mem_user_data) {\n  (void)mem_user_data;\n\n  return realloc(ptr, size);\n}\n\nstatic nghttp2_mem mem_default = {NULL, default_malloc, default_free,\n                                  default_calloc, default_realloc};\n\nnghttp2_mem *nghttp2_mem_default(void) { return &mem_default; }\n\nvoid *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size) {\n  return mem->malloc(size, mem->mem_user_data);\n}\n\nvoid nghttp2_mem_free(nghttp2_mem *mem, void *ptr) {\n  mem->free(ptr, mem->mem_user_data);\n}\n\nvoid nghttp2_mem_free2(nghttp2_free free_func, void *ptr, void *mem_user_data) {\n  free_func(ptr, mem_user_data);\n}\n\nvoid *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size) {\n  return mem->calloc(nmemb, size, mem->mem_user_data);\n}\n\nvoid *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size) {\n  return mem->realloc(ptr, size, mem->mem_user_data);\n}\n"
  },
  {
    "path": "thirdparty/nghttp2/nghttp2_mem.h",
    "content": "/*\n * nghttp2 - HTTP/2 C Library\n *\n * Copyright (c) 2014 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef NGHTTP2_MEM_H\n#define NGHTTP2_MEM_H\n\n#include \"nghttp2.h\"\n\n/* The default, system standard memory allocator */\nnghttp2_mem *nghttp2_mem_default(void);\n\n/* Convenient wrapper functions to call allocator function in\n   |mem|. */\nvoid *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size);\nvoid nghttp2_mem_free(nghttp2_mem *mem, void *ptr);\nvoid nghttp2_mem_free2(nghttp2_free free_func, void *ptr, void *mem_user_data);\nvoid *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size);\nvoid *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size);\n\n#endif /* NGHTTP2_MEM_H */\n"
  },
  {
    "path": "thirdparty/nghttp2/nghttp2_rcbuf.c",
    "content": "/*\n * nghttp2 - HTTP/2 C Library\n *\n * Copyright (c) 2016 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"nghttp2_rcbuf.h\"\n\n#include <string.h>\n#include <assert.h>\n\n#include \"nghttp2_mem.h\"\n\nint nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size,\n                      nghttp2_mem *mem) {\n  uint8_t *p;\n\n  p = nghttp2_mem_malloc(mem, sizeof(nghttp2_rcbuf) + size);\n  if (p == NULL) {\n    return NGHTTP2_ERR_NOMEM;\n  }\n\n  *rcbuf_ptr = (void *)p;\n\n  (*rcbuf_ptr)->mem_user_data = mem->mem_user_data;\n  (*rcbuf_ptr)->free = mem->free;\n  (*rcbuf_ptr)->base = p + sizeof(nghttp2_rcbuf);\n  (*rcbuf_ptr)->len = size;\n  (*rcbuf_ptr)->ref = 1;\n\n  return 0;\n}\n\nint nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src,\n                       size_t srclen, nghttp2_mem *mem) {\n  int rv;\n\n  rv = nghttp2_rcbuf_new(rcbuf_ptr, srclen + 1, mem);\n  if (rv != 0) {\n    return rv;\n  }\n\n  (*rcbuf_ptr)->len = srclen;\n  *nghttp2_cpymem((*rcbuf_ptr)->base, src, srclen) = '\\0';\n\n  return 0;\n}\n\n/*\n * Frees |rcbuf| itself, regardless of its reference cout.\n */\nvoid nghttp2_rcbuf_del(nghttp2_rcbuf *rcbuf) {\n  nghttp2_mem_free2(rcbuf->free, rcbuf, rcbuf->mem_user_data);\n}\n\nvoid nghttp2_rcbuf_incref(nghttp2_rcbuf *rcbuf) {\n  if (rcbuf->ref == -1) {\n    return;\n  }\n\n  ++rcbuf->ref;\n}\n\nvoid nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf) {\n  if (rcbuf == NULL || rcbuf->ref == -1) {\n    return;\n  }\n\n  assert(rcbuf->ref > 0);\n\n  if (--rcbuf->ref == 0) {\n    nghttp2_rcbuf_del(rcbuf);\n  }\n}\n\nnghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf) {\n  nghttp2_vec res = {rcbuf->base, rcbuf->len};\n  return res;\n}\n"
  },
  {
    "path": "thirdparty/nghttp2/nghttp2_rcbuf.h",
    "content": "/*\n * nghttp2 - HTTP/2 C Library\n *\n * Copyright (c) 2016 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef NGHTTP2_RCBUF_H\n#define NGHTTP2_RCBUF_H\n\n#include \"nghttp2.h\"\n\nstruct nghttp2_rcbuf {\n  /* custom memory allocator belongs to the mem parameter when\n     creating this object. */\n  void *mem_user_data;\n  nghttp2_free free;\n  /* The pointer to the underlying buffer */\n  uint8_t *base;\n  /* Size of buffer pointed by |base|. */\n  size_t len;\n  /* Reference count */\n  int32_t ref;\n};\n\n/*\n * Allocates nghttp2_rcbuf object with |size| as initial buffer size.\n * When the function succeeds, the reference count becomes 1.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM:\n *     Out of memory.\n */\nint nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size, nghttp2_mem *mem);\n\n/*\n * Like nghttp2_rcbuf_new(), but initializes the buffer with |src| of\n * length |srclen|.  This function allocates additional byte at the\n * end and puts '\\0' into it, so that the resulting buffer could be\n * used as NULL-terminated string.  Still (*rcbuf_ptr)->len equals to\n * |srclen|.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * NGHTTP2_ERR_NOMEM:\n *     Out of memory.\n */\nint nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src,\n                       size_t srclen, nghttp2_mem *mem);\n\n/*\n * Frees |rcbuf| itself, regardless of its reference cout.\n */\nvoid nghttp2_rcbuf_del(nghttp2_rcbuf *rcbuf);\n\n#endif /* NGHTTP2_RCBUF_H */\n"
  },
  {
    "path": "thirdparty/nghttp2/nghttp2ver.h",
    "content": "/*\n * nghttp2 - HTTP/2 C Library\n *\n * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef NGHTTP2VER_H\n#define NGHTTP2VER_H\n\n/**\n * @macro\n * Version number of the nghttp2 library release\n */\n#define NGHTTP2_VERSION \"1.64.0\"\n\n/**\n * @macro\n * Numerical representation of the version number of the nghttp2 library\n * release. This is a 24 bit number with 8 bits for major number, 8 bits\n * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.\n */\n#define NGHTTP2_VERSION_NUM 0x014000\n\n#endif /* NGHTTP2VER_H */\n"
  },
  {
    "path": "thirdparty/nlohmann/LICENSE.MIT",
    "content": "MIT License \n\nCopyright (c) 2013-2021 Niels Lohmann\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "thirdparty/nlohmann/adl_serializer.hpp",
    "content": "#pragma once\n\n#include <utility>\n\n#include <nlohmann/detail/conversions/from_json.hpp>\n#include <nlohmann/detail/conversions/to_json.hpp>\n\nnamespace nlohmann\n{\n\ntemplate<typename, typename>\nstruct adl_serializer\n{\n    /*!\n    @brief convert a JSON value to any value type\n\n    This function is usually called by the `get()` function of the\n    @ref basic_json class (either explicit or via conversion operators).\n\n    @param[in] j        JSON value to read from\n    @param[in,out] val  value to write to\n    */\n    template<typename BasicJsonType, typename ValueType>\n    static auto from_json(BasicJsonType&& j, ValueType& val) noexcept(\n        noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))\n    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())\n    {\n        ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);\n    }\n\n    /*!\n    @brief convert any value type to a JSON value\n\n    This function is usually called by the constructors of the @ref basic_json\n    class.\n\n    @param[in,out] j  JSON value to write to\n    @param[in] val    value to read from\n    */\n    template <typename BasicJsonType, typename ValueType>\n    static auto to_json(BasicJsonType& j, ValueType&& val) noexcept(\n        noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val))))\n    -> decltype(::nlohmann::to_json(j, std::forward<ValueType>(val)), void())\n    {\n        ::nlohmann::to_json(j, std::forward<ValueType>(val));\n    }\n};\n\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/conversions/from_json.hpp",
    "content": "#pragma once\n\n#include <algorithm> // transform\n#include <array> // array\n#include <ciso646> // and, not\n#include <forward_list> // forward_list\n#include <iterator> // inserter, front_inserter, end\n#include <map> // map\n#include <string> // string\n#include <tuple> // tuple, make_tuple\n#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible\n#include <unordered_map> // unordered_map\n#include <utility> // pair, declval\n#include <valarray> // valarray\n\n#include <nlohmann/detail/exceptions.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/meta/cpp_future.hpp>\n#include <nlohmann/detail/meta/type_traits.hpp>\n#include <nlohmann/detail/value_t.hpp>\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename std::nullptr_t& n)\n{\n    if (JSON_HEDLEY_UNLIKELY(not j.is_null()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be null, but is \" + std::string(j.type_name())));\n    }\n    n = nullptr;\n}\n\n// overloads for basic_json template parameters\ntemplate<typename BasicJsonType, typename ArithmeticType,\n         enable_if_t<std::is_arithmetic<ArithmeticType>::value and\n                     not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,\n                     int> = 0>\nvoid get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)\n{\n    switch (static_cast<value_t>(j))\n    {\n        case value_t::number_unsigned:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());\n            break;\n        }\n        case value_t::number_integer:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());\n            break;\n        }\n        case value_t::number_float:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());\n            break;\n        }\n\n        default:\n            JSON_THROW(type_error::create(302, \"type must be number, but is \" + std::string(j.type_name())));\n    }\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)\n{\n    if (JSON_HEDLEY_UNLIKELY(not j.is_boolean()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be boolean, but is \" + std::string(j.type_name())));\n    }\n    b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)\n{\n    if (JSON_HEDLEY_UNLIKELY(not j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be string, but is \" + std::string(j.type_name())));\n    }\n    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n\ntemplate <\n    typename BasicJsonType, typename ConstructibleStringType,\n    enable_if_t <\n        is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value and\n        not std::is_same<typename BasicJsonType::string_t,\n                         ConstructibleStringType>::value,\n        int > = 0 >\nvoid from_json(const BasicJsonType& j, ConstructibleStringType& s)\n{\n    if (JSON_HEDLEY_UNLIKELY(not j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be string, but is \" + std::string(j.type_name())));\n    }\n\n    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType, typename EnumType,\n         enable_if_t<std::is_enum<EnumType>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, EnumType& e)\n{\n    typename std::underlying_type<EnumType>::type val;\n    get_arithmetic_value(j, val);\n    e = static_cast<EnumType>(val);\n}\n\n// forward_list doesn't have an insert method\ntemplate<typename BasicJsonType, typename T, typename Allocator,\n         enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)\n{\n    if (JSON_HEDLEY_UNLIKELY(not j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name())));\n    }\n    l.clear();\n    std::transform(j.rbegin(), j.rend(),\n                   std::front_inserter(l), [](const BasicJsonType & i)\n    {\n        return i.template get<T>();\n    });\n}\n\n// valarray doesn't have an insert method\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, std::valarray<T>& l)\n{\n    if (JSON_HEDLEY_UNLIKELY(not j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name())));\n    }\n    l.resize(j.size());\n    std::copy(j.begin(), j.end(), std::begin(l));\n}\n\ntemplate <typename BasicJsonType, typename T, std::size_t N>\nauto from_json(const BasicJsonType& j, T (&arr)[N])\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i = 0; i < N; ++i)\n    {\n        arr[i] = j.at(i).template get<T>();\n    }\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)\n{\n    arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();\n}\n\ntemplate <typename BasicJsonType, typename T, std::size_t N>\nauto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,\n                          priority_tag<2> /*unused*/)\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i = 0; i < N; ++i)\n    {\n        arr[i] = j.at(i).template get<T>();\n    }\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nauto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)\n-> decltype(\n    arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),\n    j.template get<typename ConstructibleArrayType::value_type>(),\n    void())\n{\n    using std::end;\n\n    ConstructibleArrayType ret;\n    ret.reserve(j.size());\n    std::transform(j.begin(), j.end(),\n                   std::inserter(ret, end(ret)), [](const BasicJsonType & i)\n    {\n        // get<BasicJsonType>() returns *this, this won't call a from_json\n        // method when value_type is BasicJsonType\n        return i.template get<typename ConstructibleArrayType::value_type>();\n    });\n    arr = std::move(ret);\n}\n\ntemplate <typename BasicJsonType, typename ConstructibleArrayType>\nvoid from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,\n                          priority_tag<0> /*unused*/)\n{\n    using std::end;\n\n    ConstructibleArrayType ret;\n    std::transform(\n        j.begin(), j.end(), std::inserter(ret, end(ret)),\n        [](const BasicJsonType & i)\n    {\n        // get<BasicJsonType>() returns *this, this won't call a from_json\n        // method when value_type is BasicJsonType\n        return i.template get<typename ConstructibleArrayType::value_type>();\n    });\n    arr = std::move(ret);\n}\n\ntemplate <typename BasicJsonType, typename ConstructibleArrayType,\n          enable_if_t <\n              is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value and\n              not is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value and\n              not is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value and\n              not is_basic_json<ConstructibleArrayType>::value,\n              int > = 0 >\n\nauto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)\n-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),\nj.template get<typename ConstructibleArrayType::value_type>(),\nvoid())\n{\n    if (JSON_HEDLEY_UNLIKELY(not j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" +\n                                      std::string(j.type_name())));\n    }\n\n    from_json_array_impl(j, arr, priority_tag<3> {});\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType,\n         enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, ConstructibleObjectType& obj)\n{\n    if (JSON_HEDLEY_UNLIKELY(not j.is_object()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be object, but is \" + std::string(j.type_name())));\n    }\n\n    ConstructibleObjectType ret;\n    auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();\n    using value_type = typename ConstructibleObjectType::value_type;\n    std::transform(\n        inner_object->begin(), inner_object->end(),\n        std::inserter(ret, ret.begin()),\n        [](typename BasicJsonType::object_t::value_type const & p)\n    {\n        return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());\n    });\n    obj = std::move(ret);\n}\n\n// overload for arithmetic types, not chosen for basic_json template arguments\n// (BooleanType, etc..); note: Is it really necessary to provide explicit\n// overloads for boolean_t etc. in case of a custom BooleanType which is not\n// an arithmetic type?\ntemplate<typename BasicJsonType, typename ArithmeticType,\n         enable_if_t <\n             std::is_arithmetic<ArithmeticType>::value and\n             not std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value and\n             not std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value and\n             not std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value and\n             not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,\n             int> = 0>\nvoid from_json(const BasicJsonType& j, ArithmeticType& val)\n{\n    switch (static_cast<value_t>(j))\n    {\n        case value_t::number_unsigned:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());\n            break;\n        }\n        case value_t::number_integer:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());\n            break;\n        }\n        case value_t::number_float:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());\n            break;\n        }\n        case value_t::boolean:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());\n            break;\n        }\n\n        default:\n            JSON_THROW(type_error::create(302, \"type must be number, but is \" + std::string(j.type_name())));\n    }\n}\n\ntemplate<typename BasicJsonType, typename A1, typename A2>\nvoid from_json(const BasicJsonType& j, std::pair<A1, A2>& p)\n{\n    p = {j.at(0).template get<A1>(), j.at(1).template get<A2>()};\n}\n\ntemplate<typename BasicJsonType, typename Tuple, std::size_t... Idx>\nvoid from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence<Idx...> /*unused*/)\n{\n    t = std::make_tuple(j.at(Idx).template get<typename std::tuple_element<Idx, Tuple>::type>()...);\n}\n\ntemplate<typename BasicJsonType, typename... Args>\nvoid from_json(const BasicJsonType& j, std::tuple<Args...>& t)\n{\n    from_json_tuple_impl(j, t, index_sequence_for<Args...> {});\n}\n\ntemplate <typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,\n          typename = enable_if_t<not std::is_constructible<\n                                     typename BasicJsonType::string_t, Key>::value>>\nvoid from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)\n{\n    if (JSON_HEDLEY_UNLIKELY(not j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name())));\n    }\n    m.clear();\n    for (const auto& p : j)\n    {\n        if (JSON_HEDLEY_UNLIKELY(not p.is_array()))\n        {\n            JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(p.type_name())));\n        }\n        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());\n    }\n}\n\ntemplate <typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,\n          typename = enable_if_t<not std::is_constructible<\n                                     typename BasicJsonType::string_t, Key>::value>>\nvoid from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)\n{\n    if (JSON_HEDLEY_UNLIKELY(not j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name())));\n    }\n    m.clear();\n    for (const auto& p : j)\n    {\n        if (JSON_HEDLEY_UNLIKELY(not p.is_array()))\n        {\n            JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(p.type_name())));\n        }\n        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());\n    }\n}\n\nstruct from_json_fn\n{\n    template<typename BasicJsonType, typename T>\n    auto operator()(const BasicJsonType& j, T& val) const\n    noexcept(noexcept(from_json(j, val)))\n    -> decltype(from_json(j, val), void())\n    {\n        return from_json(j, val);\n    }\n};\n}  // namespace detail\n\n/// namespace to hold default `from_json` function\n/// to see why this is required:\n/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html\nnamespace\n{\nconstexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value;\n} // namespace\n} // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/conversions/to_chars.hpp",
    "content": "#pragma once\n\n#include <array> // array\n#include <cassert> // assert\n#include <ciso646> // or, and, not\n#include <cmath>   // signbit, isfinite\n#include <cstdint> // intN_t, uintN_t\n#include <cstring> // memcpy, memmove\n#include <limits> // numeric_limits\n#include <type_traits> // conditional\n#include <nlohmann/detail/macro_scope.hpp>\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\n/*!\n@brief implements the Grisu2 algorithm for binary to decimal floating-point\nconversion.\n\nThis implementation is a slightly modified version of the reference\nimplementation which may be obtained from\nhttp://florian.loitsch.com/publications (bench.tar.gz).\n\nThe code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.\n\nFor a detailed description of the algorithm see:\n\n[1] Loitsch, \"Printing Floating-Point Numbers Quickly and Accurately with\n    Integers\", Proceedings of the ACM SIGPLAN 2010 Conference on Programming\n    Language Design and Implementation, PLDI 2010\n[2] Burger, Dybvig, \"Printing Floating-Point Numbers Quickly and Accurately\",\n    Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language\n    Design and Implementation, PLDI 1996\n*/\nnamespace dtoa_impl\n{\n\ntemplate <typename Target, typename Source>\nTarget reinterpret_bits(const Source source)\n{\n    static_assert(sizeof(Target) == sizeof(Source), \"size mismatch\");\n\n    Target target;\n    std::memcpy(&target, &source, sizeof(Source));\n    return target;\n}\n\nstruct diyfp // f * 2^e\n{\n    static constexpr int kPrecision = 64; // = q\n\n    std::uint64_t f = 0;\n    int e = 0;\n\n    constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {}\n\n    /*!\n    @brief returns x - y\n    @pre x.e == y.e and x.f >= y.f\n    */\n    static diyfp sub(const diyfp& x, const diyfp& y) noexcept\n    {\n        assert(x.e == y.e);\n        assert(x.f >= y.f);\n\n        return {x.f - y.f, x.e};\n    }\n\n    /*!\n    @brief returns x * y\n    @note The result is rounded. (Only the upper q bits are returned.)\n    */\n    static diyfp mul(const diyfp& x, const diyfp& y) noexcept\n    {\n        static_assert(kPrecision == 64, \"internal error\");\n\n        // Computes:\n        //  f = round((x.f * y.f) / 2^q)\n        //  e = x.e + y.e + q\n\n        // Emulate the 64-bit * 64-bit multiplication:\n        //\n        // p = u * v\n        //   = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi)\n        //   = (u_lo v_lo         ) + 2^32 ((u_lo v_hi         ) + (u_hi v_lo         )) + 2^64 (u_hi v_hi         )\n        //   = (p0                ) + 2^32 ((p1                ) + (p2                )) + 2^64 (p3                )\n        //   = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3                )\n        //   = (p0_lo             ) + 2^32 (p0_hi + p1_lo + p2_lo                      ) + 2^64 (p1_hi + p2_hi + p3)\n        //   = (p0_lo             ) + 2^32 (Q                                          ) + 2^64 (H                 )\n        //   = (p0_lo             ) + 2^32 (Q_lo + 2^32 Q_hi                           ) + 2^64 (H                 )\n        //\n        // (Since Q might be larger than 2^32 - 1)\n        //\n        //   = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H)\n        //\n        // (Q_hi + H does not overflow a 64-bit int)\n        //\n        //   = p_lo + 2^64 p_hi\n\n        const std::uint64_t u_lo = x.f & 0xFFFFFFFFu;\n        const std::uint64_t u_hi = x.f >> 32u;\n        const std::uint64_t v_lo = y.f & 0xFFFFFFFFu;\n        const std::uint64_t v_hi = y.f >> 32u;\n\n        const std::uint64_t p0 = u_lo * v_lo;\n        const std::uint64_t p1 = u_lo * v_hi;\n        const std::uint64_t p2 = u_hi * v_lo;\n        const std::uint64_t p3 = u_hi * v_hi;\n\n        const std::uint64_t p0_hi = p0 >> 32u;\n        const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu;\n        const std::uint64_t p1_hi = p1 >> 32u;\n        const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu;\n        const std::uint64_t p2_hi = p2 >> 32u;\n\n        std::uint64_t Q = p0_hi + p1_lo + p2_lo;\n\n        // The full product might now be computed as\n        //\n        // p_hi = p3 + p2_hi + p1_hi + (Q >> 32)\n        // p_lo = p0_lo + (Q << 32)\n        //\n        // But in this particular case here, the full p_lo is not required.\n        // Effectively we only need to add the highest bit in p_lo to p_hi (and\n        // Q_hi + 1 does not overflow).\n\n        Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up\n\n        const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u);\n\n        return {h, x.e + y.e + 64};\n    }\n\n    /*!\n    @brief normalize x such that the significand is >= 2^(q-1)\n    @pre x.f != 0\n    */\n    static diyfp normalize(diyfp x) noexcept\n    {\n        assert(x.f != 0);\n\n        while ((x.f >> 63u) == 0)\n        {\n            x.f <<= 1u;\n            x.e--;\n        }\n\n        return x;\n    }\n\n    /*!\n    @brief normalize x such that the result has the exponent E\n    @pre e >= x.e and the upper e - x.e bits of x.f must be zero.\n    */\n    static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept\n    {\n        const int delta = x.e - target_exponent;\n\n        assert(delta >= 0);\n        assert(((x.f << delta) >> delta) == x.f);\n\n        return {x.f << delta, target_exponent};\n    }\n};\n\nstruct boundaries\n{\n    diyfp w;\n    diyfp minus;\n    diyfp plus;\n};\n\n/*!\nCompute the (normalized) diyfp representing the input number 'value' and its\nboundaries.\n\n@pre value must be finite and positive\n*/\ntemplate <typename FloatType>\nboundaries compute_boundaries(FloatType value)\n{\n    assert(std::isfinite(value));\n    assert(value > 0);\n\n    // Convert the IEEE representation into a diyfp.\n    //\n    // If v is denormal:\n    //      value = 0.F * 2^(1 - bias) = (          F) * 2^(1 - bias - (p-1))\n    // If v is normalized:\n    //      value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1))\n\n    static_assert(std::numeric_limits<FloatType>::is_iec559,\n                  \"internal error: dtoa_short requires an IEEE-754 floating-point implementation\");\n\n    constexpr int      kPrecision = std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit)\n    constexpr int      kBias      = std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1);\n    constexpr int      kMinExp    = 1 - kBias;\n    constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1)\n\n    using bits_type = typename std::conditional<kPrecision == 24, std::uint32_t, std::uint64_t >::type;\n\n    const std::uint64_t bits = reinterpret_bits<bits_type>(value);\n    const std::uint64_t E = bits >> (kPrecision - 1);\n    const std::uint64_t F = bits & (kHiddenBit - 1);\n\n    const bool is_denormal = E == 0;\n    const diyfp v = is_denormal\n                    ? diyfp(F, kMinExp)\n                    : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);\n\n    // Compute the boundaries m- and m+ of the floating-point value\n    // v = f * 2^e.\n    //\n    // Determine v- and v+, the floating-point predecessor and successor if v,\n    // respectively.\n    //\n    //      v- = v - 2^e        if f != 2^(p-1) or e == e_min                (A)\n    //         = v - 2^(e-1)    if f == 2^(p-1) and e > e_min                (B)\n    //\n    //      v+ = v + 2^e\n    //\n    // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_\n    // between m- and m+ round to v, regardless of how the input rounding\n    // algorithm breaks ties.\n    //\n    //      ---+-------------+-------------+-------------+-------------+---  (A)\n    //         v-            m-            v             m+            v+\n    //\n    //      -----------------+------+------+-------------+-------------+---  (B)\n    //                       v-     m-     v             m+            v+\n\n    const bool lower_boundary_is_closer = F == 0 and E > 1;\n    const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);\n    const diyfp m_minus = lower_boundary_is_closer\n                          ? diyfp(4 * v.f - 1, v.e - 2)  // (B)\n                          : diyfp(2 * v.f - 1, v.e - 1); // (A)\n\n    // Determine the normalized w+ = m+.\n    const diyfp w_plus = diyfp::normalize(m_plus);\n\n    // Determine w- = m- such that e_(w-) = e_(w+).\n    const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e);\n\n    return {diyfp::normalize(v), w_minus, w_plus};\n}\n\n// Given normalized diyfp w, Grisu needs to find a (normalized) cached\n// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies\n// within a certain range [alpha, gamma] (Definition 3.2 from [1])\n//\n//      alpha <= e = e_c + e_w + q <= gamma\n//\n// or\n//\n//      f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q\n//                          <= f_c * f_w * 2^gamma\n//\n// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies\n//\n//      2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma\n//\n// or\n//\n//      2^(q - 2 + alpha) <= c * w < 2^(q + gamma)\n//\n// The choice of (alpha,gamma) determines the size of the table and the form of\n// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well\n// in practice:\n//\n// The idea is to cut the number c * w = f * 2^e into two parts, which can be\n// processed independently: An integral part p1, and a fractional part p2:\n//\n//      f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e\n//              = (f div 2^-e) + (f mod 2^-e) * 2^e\n//              = p1 + p2 * 2^e\n//\n// The conversion of p1 into decimal form requires a series of divisions and\n// modulos by (a power of) 10. These operations are faster for 32-bit than for\n// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be\n// achieved by choosing\n//\n//      -e >= 32   or   e <= -32 := gamma\n//\n// In order to convert the fractional part\n//\n//      p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ...\n//\n// into decimal form, the fraction is repeatedly multiplied by 10 and the digits\n// d[-i] are extracted in order:\n//\n//      (10 * p2) div 2^-e = d[-1]\n//      (10 * p2) mod 2^-e = d[-2] / 10^1 + ...\n//\n// The multiplication by 10 must not overflow. It is sufficient to choose\n//\n//      10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64.\n//\n// Since p2 = f mod 2^-e < 2^-e,\n//\n//      -e <= 60   or   e >= -60 := alpha\n\nconstexpr int kAlpha = -60;\nconstexpr int kGamma = -32;\n\nstruct cached_power // c = f * 2^e ~= 10^k\n{\n    std::uint64_t f;\n    int e;\n    int k;\n};\n\n/*!\nFor a normalized diyfp w = f * 2^e, this function returns a (normalized) cached\npower-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c\nsatisfies (Definition 3.2 from [1])\n\n     alpha <= e_c + e + q <= gamma.\n*/\ninline cached_power get_cached_power_for_binary_exponent(int e)\n{\n    // Now\n    //\n    //      alpha <= e_c + e + q <= gamma                                    (1)\n    //      ==> f_c * 2^alpha <= c * 2^e * 2^q\n    //\n    // and since the c's are normalized, 2^(q-1) <= f_c,\n    //\n    //      ==> 2^(q - 1 + alpha) <= c * 2^(e + q)\n    //      ==> 2^(alpha - e - 1) <= c\n    //\n    // If c were an exact power of ten, i.e. c = 10^k, one may determine k as\n    //\n    //      k = ceil( log_10( 2^(alpha - e - 1) ) )\n    //        = ceil( (alpha - e - 1) * log_10(2) )\n    //\n    // From the paper:\n    // \"In theory the result of the procedure could be wrong since c is rounded,\n    //  and the computation itself is approximated [...]. In practice, however,\n    //  this simple function is sufficient.\"\n    //\n    // For IEEE double precision floating-point numbers converted into\n    // normalized diyfp's w = f * 2^e, with q = 64,\n    //\n    //      e >= -1022      (min IEEE exponent)\n    //           -52        (p - 1)\n    //           -52        (p - 1, possibly normalize denormal IEEE numbers)\n    //           -11        (normalize the diyfp)\n    //         = -1137\n    //\n    // and\n    //\n    //      e <= +1023      (max IEEE exponent)\n    //           -52        (p - 1)\n    //           -11        (normalize the diyfp)\n    //         = 960\n    //\n    // This binary exponent range [-1137,960] results in a decimal exponent\n    // range [-307,324]. One does not need to store a cached power for each\n    // k in this range. For each such k it suffices to find a cached power\n    // such that the exponent of the product lies in [alpha,gamma].\n    // This implies that the difference of the decimal exponents of adjacent\n    // table entries must be less than or equal to\n    //\n    //      floor( (gamma - alpha) * log_10(2) ) = 8.\n    //\n    // (A smaller distance gamma-alpha would require a larger table.)\n\n    // NB:\n    // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34.\n\n    constexpr int kCachedPowersMinDecExp = -300;\n    constexpr int kCachedPowersDecStep = 8;\n\n    static constexpr std::array<cached_power, 79> kCachedPowers =\n    {\n        {\n            { 0xAB70FE17C79AC6CA, -1060, -300 },\n            { 0xFF77B1FCBEBCDC4F, -1034, -292 },\n            { 0xBE5691EF416BD60C, -1007, -284 },\n            { 0x8DD01FAD907FFC3C,  -980, -276 },\n            { 0xD3515C2831559A83,  -954, -268 },\n            { 0x9D71AC8FADA6C9B5,  -927, -260 },\n            { 0xEA9C227723EE8BCB,  -901, -252 },\n            { 0xAECC49914078536D,  -874, -244 },\n            { 0x823C12795DB6CE57,  -847, -236 },\n            { 0xC21094364DFB5637,  -821, -228 },\n            { 0x9096EA6F3848984F,  -794, -220 },\n            { 0xD77485CB25823AC7,  -768, -212 },\n            { 0xA086CFCD97BF97F4,  -741, -204 },\n            { 0xEF340A98172AACE5,  -715, -196 },\n            { 0xB23867FB2A35B28E,  -688, -188 },\n            { 0x84C8D4DFD2C63F3B,  -661, -180 },\n            { 0xC5DD44271AD3CDBA,  -635, -172 },\n            { 0x936B9FCEBB25C996,  -608, -164 },\n            { 0xDBAC6C247D62A584,  -582, -156 },\n            { 0xA3AB66580D5FDAF6,  -555, -148 },\n            { 0xF3E2F893DEC3F126,  -529, -140 },\n            { 0xB5B5ADA8AAFF80B8,  -502, -132 },\n            { 0x87625F056C7C4A8B,  -475, -124 },\n            { 0xC9BCFF6034C13053,  -449, -116 },\n            { 0x964E858C91BA2655,  -422, -108 },\n            { 0xDFF9772470297EBD,  -396, -100 },\n            { 0xA6DFBD9FB8E5B88F,  -369,  -92 },\n            { 0xF8A95FCF88747D94,  -343,  -84 },\n            { 0xB94470938FA89BCF,  -316,  -76 },\n            { 0x8A08F0F8BF0F156B,  -289,  -68 },\n            { 0xCDB02555653131B6,  -263,  -60 },\n            { 0x993FE2C6D07B7FAC,  -236,  -52 },\n            { 0xE45C10C42A2B3B06,  -210,  -44 },\n            { 0xAA242499697392D3,  -183,  -36 },\n            { 0xFD87B5F28300CA0E,  -157,  -28 },\n            { 0xBCE5086492111AEB,  -130,  -20 },\n            { 0x8CBCCC096F5088CC,  -103,  -12 },\n            { 0xD1B71758E219652C,   -77,   -4 },\n            { 0x9C40000000000000,   -50,    4 },\n            { 0xE8D4A51000000000,   -24,   12 },\n            { 0xAD78EBC5AC620000,     3,   20 },\n            { 0x813F3978F8940984,    30,   28 },\n            { 0xC097CE7BC90715B3,    56,   36 },\n            { 0x8F7E32CE7BEA5C70,    83,   44 },\n            { 0xD5D238A4ABE98068,   109,   52 },\n            { 0x9F4F2726179A2245,   136,   60 },\n            { 0xED63A231D4C4FB27,   162,   68 },\n            { 0xB0DE65388CC8ADA8,   189,   76 },\n            { 0x83C7088E1AAB65DB,   216,   84 },\n            { 0xC45D1DF942711D9A,   242,   92 },\n            { 0x924D692CA61BE758,   269,  100 },\n            { 0xDA01EE641A708DEA,   295,  108 },\n            { 0xA26DA3999AEF774A,   322,  116 },\n            { 0xF209787BB47D6B85,   348,  124 },\n            { 0xB454E4A179DD1877,   375,  132 },\n            { 0x865B86925B9BC5C2,   402,  140 },\n            { 0xC83553C5C8965D3D,   428,  148 },\n            { 0x952AB45CFA97A0B3,   455,  156 },\n            { 0xDE469FBD99A05FE3,   481,  164 },\n            { 0xA59BC234DB398C25,   508,  172 },\n            { 0xF6C69A72A3989F5C,   534,  180 },\n            { 0xB7DCBF5354E9BECE,   561,  188 },\n            { 0x88FCF317F22241E2,   588,  196 },\n            { 0xCC20CE9BD35C78A5,   614,  204 },\n            { 0x98165AF37B2153DF,   641,  212 },\n            { 0xE2A0B5DC971F303A,   667,  220 },\n            { 0xA8D9D1535CE3B396,   694,  228 },\n            { 0xFB9B7CD9A4A7443C,   720,  236 },\n            { 0xBB764C4CA7A44410,   747,  244 },\n            { 0x8BAB8EEFB6409C1A,   774,  252 },\n            { 0xD01FEF10A657842C,   800,  260 },\n            { 0x9B10A4E5E9913129,   827,  268 },\n            { 0xE7109BFBA19C0C9D,   853,  276 },\n            { 0xAC2820D9623BF429,   880,  284 },\n            { 0x80444B5E7AA7CF85,   907,  292 },\n            { 0xBF21E44003ACDD2D,   933,  300 },\n            { 0x8E679C2F5E44FF8F,   960,  308 },\n            { 0xD433179D9C8CB841,   986,  316 },\n            { 0x9E19DB92B4E31BA9,  1013,  324 },\n        }\n    };\n\n    // This computation gives exactly the same results for k as\n    //      k = ceil((kAlpha - e - 1) * 0.30102999566398114)\n    // for |e| <= 1500, but doesn't require floating-point operations.\n    // NB: log_10(2) ~= 78913 / 2^18\n    assert(e >= -1500);\n    assert(e <=  1500);\n    const int f = kAlpha - e - 1;\n    const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0);\n\n    const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;\n    assert(index >= 0);\n    assert(static_cast<std::size_t>(index) < kCachedPowers.size());\n\n    const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)];\n    assert(kAlpha <= cached.e + e + 64);\n    assert(kGamma >= cached.e + e + 64);\n\n    return cached;\n}\n\n/*!\nFor n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.\nFor n == 0, returns 1 and sets pow10 := 1.\n*/\ninline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10)\n{\n    // LCOV_EXCL_START\n    if (n >= 1000000000)\n    {\n        pow10 = 1000000000;\n        return 10;\n    }\n    // LCOV_EXCL_STOP\n    else if (n >= 100000000)\n    {\n        pow10 = 100000000;\n        return  9;\n    }\n    else if (n >= 10000000)\n    {\n        pow10 = 10000000;\n        return  8;\n    }\n    else if (n >= 1000000)\n    {\n        pow10 = 1000000;\n        return  7;\n    }\n    else if (n >= 100000)\n    {\n        pow10 = 100000;\n        return  6;\n    }\n    else if (n >= 10000)\n    {\n        pow10 = 10000;\n        return  5;\n    }\n    else if (n >= 1000)\n    {\n        pow10 = 1000;\n        return  4;\n    }\n    else if (n >= 100)\n    {\n        pow10 = 100;\n        return  3;\n    }\n    else if (n >= 10)\n    {\n        pow10 = 10;\n        return  2;\n    }\n    else\n    {\n        pow10 = 1;\n        return 1;\n    }\n}\n\ninline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta,\n                         std::uint64_t rest, std::uint64_t ten_k)\n{\n    assert(len >= 1);\n    assert(dist <= delta);\n    assert(rest <= delta);\n    assert(ten_k > 0);\n\n    //               <--------------------------- delta ---->\n    //                                  <---- dist --------->\n    // --------------[------------------+-------------------]--------------\n    //               M-                 w                   M+\n    //\n    //                                  ten_k\n    //                                <------>\n    //                                       <---- rest ---->\n    // --------------[------------------+----+--------------]--------------\n    //                                  w    V\n    //                                       = buf * 10^k\n    //\n    // ten_k represents a unit-in-the-last-place in the decimal representation\n    // stored in buf.\n    // Decrement buf by ten_k while this takes buf closer to w.\n\n    // The tests are written in this order to avoid overflow in unsigned\n    // integer arithmetic.\n\n    while (rest < dist\n            and delta - rest >= ten_k\n            and (rest + ten_k < dist or dist - rest > rest + ten_k - dist))\n    {\n        assert(buf[len - 1] != '0');\n        buf[len - 1]--;\n        rest += ten_k;\n    }\n}\n\n/*!\nGenerates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.\nM- and M+ must be normalized and share the same exponent -60 <= e <= -32.\n*/\ninline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,\n                             diyfp M_minus, diyfp w, diyfp M_plus)\n{\n    static_assert(kAlpha >= -60, \"internal error\");\n    static_assert(kGamma <= -32, \"internal error\");\n\n    // Generates the digits (and the exponent) of a decimal floating-point\n    // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's\n    // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma.\n    //\n    //               <--------------------------- delta ---->\n    //                                  <---- dist --------->\n    // --------------[------------------+-------------------]--------------\n    //               M-                 w                   M+\n    //\n    // Grisu2 generates the digits of M+ from left to right and stops as soon as\n    // V is in [M-,M+].\n\n    assert(M_plus.e >= kAlpha);\n    assert(M_plus.e <= kGamma);\n\n    std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)\n    std::uint64_t dist  = diyfp::sub(M_plus, w      ).f; // (significand of (M+ - w ), implicit exponent is e)\n\n    // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0):\n    //\n    //      M+ = f * 2^e\n    //         = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e\n    //         = ((p1        ) * 2^-e + (p2        )) * 2^e\n    //         = p1 + p2 * 2^e\n\n    const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e);\n\n    auto p1 = static_cast<std::uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)\n    std::uint64_t p2 = M_plus.f & (one.f - 1);                    // p2 = f mod 2^-e\n\n    // 1)\n    //\n    // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]\n\n    assert(p1 > 0);\n\n    std::uint32_t pow10;\n    const int k = find_largest_pow10(p1, pow10);\n\n    //      10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)\n    //\n    //      p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1))\n    //         = (d[k-1]         ) * 10^(k-1) + (p1 mod 10^(k-1))\n    //\n    //      M+ = p1                                             + p2 * 2^e\n    //         = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1))          + p2 * 2^e\n    //         = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e\n    //         = d[k-1] * 10^(k-1) + (                         rest) * 2^e\n    //\n    // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0)\n    //\n    //      p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0]\n    //\n    // but stop as soon as\n    //\n    //      rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e\n\n    int n = k;\n    while (n > 0)\n    {\n        // Invariants:\n        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)    (buffer = 0 for n = k)\n        //      pow10 = 10^(n-1) <= p1 < 10^n\n        //\n        const std::uint32_t d = p1 / pow10;  // d = p1 div 10^(n-1)\n        const std::uint32_t r = p1 % pow10;  // r = p1 mod 10^(n-1)\n        //\n        //      M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e\n        //         = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)\n        //\n        assert(d <= 9);\n        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d\n        //\n        //      M+ = buffer * 10^(n-1) + (r + p2 * 2^e)\n        //\n        p1 = r;\n        n--;\n        //\n        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)\n        //      pow10 = 10^n\n        //\n\n        // Now check if enough digits have been generated.\n        // Compute\n        //\n        //      p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e\n        //\n        // Note:\n        // Since rest and delta share the same exponent e, it suffices to\n        // compare the significands.\n        const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2;\n        if (rest <= delta)\n        {\n            // V = buffer * 10^n, with M- <= V <= M+.\n\n            decimal_exponent += n;\n\n            // We may now just stop. But instead look if the buffer could be\n            // decremented to bring V closer to w.\n            //\n            // pow10 = 10^n is now 1 ulp in the decimal representation V.\n            // The rounding procedure works with diyfp's with an implicit\n            // exponent of e.\n            //\n            //      10^n = (10^n * 2^-e) * 2^e = ulp * 2^e\n            //\n            const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e;\n            grisu2_round(buffer, length, dist, delta, rest, ten_n);\n\n            return;\n        }\n\n        pow10 /= 10;\n        //\n        //      pow10 = 10^(n-1) <= p1 < 10^n\n        // Invariants restored.\n    }\n\n    // 2)\n    //\n    // The digits of the integral part have been generated:\n    //\n    //      M+ = d[k-1]...d[1]d[0] + p2 * 2^e\n    //         = buffer            + p2 * 2^e\n    //\n    // Now generate the digits of the fractional part p2 * 2^e.\n    //\n    // Note:\n    // No decimal point is generated: the exponent is adjusted instead.\n    //\n    // p2 actually represents the fraction\n    //\n    //      p2 * 2^e\n    //          = p2 / 2^-e\n    //          = d[-1] / 10^1 + d[-2] / 10^2 + ...\n    //\n    // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...)\n    //\n    //      p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m\n    //                      + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...)\n    //\n    // using\n    //\n    //      10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e)\n    //                = (                   d) * 2^-e + (                   r)\n    //\n    // or\n    //      10^m * p2 * 2^e = d + r * 2^e\n    //\n    // i.e.\n    //\n    //      M+ = buffer + p2 * 2^e\n    //         = buffer + 10^-m * (d + r * 2^e)\n    //         = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e\n    //\n    // and stop as soon as 10^-m * r * 2^e <= delta * 2^e\n\n    assert(p2 > delta);\n\n    int m = 0;\n    for (;;)\n    {\n        // Invariant:\n        //      M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e\n        //         = buffer * 10^-m + 10^-m * (p2                                 ) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * (10 * p2)                   ) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e\n        //\n        assert(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);\n        p2 *= 10;\n        const std::uint64_t d = p2 >> -one.e;     // d = (10 * p2) div 2^-e\n        const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e\n        //\n        //      M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))\n        //         = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e\n        //\n        assert(d <= 9);\n        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d\n        //\n        //      M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e\n        //\n        p2 = r;\n        m++;\n        //\n        //      M+ = buffer * 10^-m + 10^-m * p2 * 2^e\n        // Invariant restored.\n\n        // Check if enough digits have been generated.\n        //\n        //      10^-m * p2 * 2^e <= delta * 2^e\n        //              p2 * 2^e <= 10^m * delta * 2^e\n        //                    p2 <= 10^m * delta\n        delta *= 10;\n        dist  *= 10;\n        if (p2 <= delta)\n        {\n            break;\n        }\n    }\n\n    // V = buffer * 10^-m, with M- <= V <= M+.\n\n    decimal_exponent -= m;\n\n    // 1 ulp in the decimal representation is now 10^-m.\n    // Since delta and dist are now scaled by 10^m, we need to do the\n    // same with ulp in order to keep the units in sync.\n    //\n    //      10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e\n    //\n    const std::uint64_t ten_m = one.f;\n    grisu2_round(buffer, length, dist, delta, p2, ten_m);\n\n    // By construction this algorithm generates the shortest possible decimal\n    // number (Loitsch, Theorem 6.2) which rounds back to w.\n    // For an input number of precision p, at least\n    //\n    //      N = 1 + ceil(p * log_10(2))\n    //\n    // decimal digits are sufficient to identify all binary floating-point\n    // numbers (Matula, \"In-and-Out conversions\").\n    // This implies that the algorithm does not produce more than N decimal\n    // digits.\n    //\n    //      N = 17 for p = 53 (IEEE double precision)\n    //      N = 9  for p = 24 (IEEE single precision)\n}\n\n/*!\nv = buf * 10^decimal_exponent\nlen is the length of the buffer (number of decimal digits)\nThe buffer must be large enough, i.e. >= max_digits10.\n*/\nJSON_HEDLEY_NON_NULL(1)\ninline void grisu2(char* buf, int& len, int& decimal_exponent,\n                   diyfp m_minus, diyfp v, diyfp m_plus)\n{\n    assert(m_plus.e == m_minus.e);\n    assert(m_plus.e == v.e);\n\n    //  --------(-----------------------+-----------------------)--------    (A)\n    //          m-                      v                       m+\n    //\n    //  --------------------(-----------+-----------------------)--------    (B)\n    //                      m-          v                       m+\n    //\n    // First scale v (and m- and m+) such that the exponent is in the range\n    // [alpha, gamma].\n\n    const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e);\n\n    const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k\n\n    // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma]\n    const diyfp w       = diyfp::mul(v,       c_minus_k);\n    const diyfp w_minus = diyfp::mul(m_minus, c_minus_k);\n    const diyfp w_plus  = diyfp::mul(m_plus,  c_minus_k);\n\n    //  ----(---+---)---------------(---+---)---------------(---+---)----\n    //          w-                      w                       w+\n    //          = c*m-                  = c*v                   = c*m+\n    //\n    // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and\n    // w+ are now off by a small amount.\n    // In fact:\n    //\n    //      w - v * 10^k < 1 ulp\n    //\n    // To account for this inaccuracy, add resp. subtract 1 ulp.\n    //\n    //  --------+---[---------------(---+---)---------------]---+--------\n    //          w-  M-                  w                   M+  w+\n    //\n    // Now any number in [M-, M+] (bounds included) will round to w when input,\n    // regardless of how the input rounding algorithm breaks ties.\n    //\n    // And digit_gen generates the shortest possible such number in [M-, M+].\n    // Note that this does not mean that Grisu2 always generates the shortest\n    // possible number in the interval (m-, m+).\n    const diyfp M_minus(w_minus.f + 1, w_minus.e);\n    const diyfp M_plus (w_plus.f  - 1, w_plus.e );\n\n    decimal_exponent = -cached.k; // = -(-k) = k\n\n    grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);\n}\n\n/*!\nv = buf * 10^decimal_exponent\nlen is the length of the buffer (number of decimal digits)\nThe buffer must be large enough, i.e. >= max_digits10.\n*/\ntemplate <typename FloatType>\nJSON_HEDLEY_NON_NULL(1)\nvoid grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)\n{\n    static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,\n                  \"internal error: not enough precision\");\n\n    assert(std::isfinite(value));\n    assert(value > 0);\n\n    // If the neighbors (and boundaries) of 'value' are always computed for double-precision\n    // numbers, all float's can be recovered using strtod (and strtof). However, the resulting\n    // decimal representations are not exactly \"short\".\n    //\n    // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars)\n    // says \"value is converted to a string as if by std::sprintf in the default (\"C\") locale\"\n    // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars'\n    // does.\n    // On the other hand, the documentation for 'std::to_chars' requires that \"parsing the\n    // representation using the corresponding std::from_chars function recovers value exactly\". That\n    // indicates that single precision floating-point numbers should be recovered using\n    // 'std::strtof'.\n    //\n    // NB: If the neighbors are computed for single-precision numbers, there is a single float\n    //     (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision\n    //     value is off by 1 ulp.\n#if 0\n    const boundaries w = compute_boundaries(static_cast<double>(value));\n#else\n    const boundaries w = compute_boundaries(value);\n#endif\n\n    grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);\n}\n\n/*!\n@brief appends a decimal representation of e to buf\n@return a pointer to the element following the exponent.\n@pre -1000 < e < 1000\n*/\nJSON_HEDLEY_NON_NULL(1)\nJSON_HEDLEY_RETURNS_NON_NULL\ninline char* append_exponent(char* buf, int e)\n{\n    assert(e > -1000);\n    assert(e <  1000);\n\n    if (e < 0)\n    {\n        e = -e;\n        *buf++ = '-';\n    }\n    else\n    {\n        *buf++ = '+';\n    }\n\n    auto k = static_cast<std::uint32_t>(e);\n    if (k < 10)\n    {\n        // Always print at least two digits in the exponent.\n        // This is for compatibility with printf(\"%g\").\n        *buf++ = '0';\n        *buf++ = static_cast<char>('0' + k);\n    }\n    else if (k < 100)\n    {\n        *buf++ = static_cast<char>('0' + k / 10);\n        k %= 10;\n        *buf++ = static_cast<char>('0' + k);\n    }\n    else\n    {\n        *buf++ = static_cast<char>('0' + k / 100);\n        k %= 100;\n        *buf++ = static_cast<char>('0' + k / 10);\n        k %= 10;\n        *buf++ = static_cast<char>('0' + k);\n    }\n\n    return buf;\n}\n\n/*!\n@brief prettify v = buf * 10^decimal_exponent\n\nIf v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point\nnotation. Otherwise it will be printed in exponential notation.\n\n@pre min_exp < 0\n@pre max_exp > 0\n*/\nJSON_HEDLEY_NON_NULL(1)\nJSON_HEDLEY_RETURNS_NON_NULL\ninline char* format_buffer(char* buf, int len, int decimal_exponent,\n                           int min_exp, int max_exp)\n{\n    assert(min_exp < 0);\n    assert(max_exp > 0);\n\n    const int k = len;\n    const int n = len + decimal_exponent;\n\n    // v = buf * 10^(n-k)\n    // k is the length of the buffer (number of decimal digits)\n    // n is the position of the decimal point relative to the start of the buffer.\n\n    if (k <= n and n <= max_exp)\n    {\n        // digits[000]\n        // len <= max_exp + 2\n\n        std::memset(buf + k, '0', static_cast<size_t>(n - k));\n        // Make it look like a floating-point number (#362, #378)\n        buf[n + 0] = '.';\n        buf[n + 1] = '0';\n        return buf + (n + 2);\n    }\n\n    if (0 < n and n <= max_exp)\n    {\n        // dig.its\n        // len <= max_digits10 + 1\n\n        assert(k > n);\n\n        std::memmove(buf + (n + 1), buf + n, static_cast<size_t>(k - n));\n        buf[n] = '.';\n        return buf + (k + 1);\n    }\n\n    if (min_exp < n and n <= 0)\n    {\n        // 0.[000]digits\n        // len <= 2 + (-min_exp - 1) + max_digits10\n\n        std::memmove(buf + (2 + -n), buf, static_cast<size_t>(k));\n        buf[0] = '0';\n        buf[1] = '.';\n        std::memset(buf + 2, '0', static_cast<size_t>(-n));\n        return buf + (2 + (-n) + k);\n    }\n\n    if (k == 1)\n    {\n        // dE+123\n        // len <= 1 + 5\n\n        buf += 1;\n    }\n    else\n    {\n        // d.igitsE+123\n        // len <= max_digits10 + 1 + 5\n\n        std::memmove(buf + 2, buf + 1, static_cast<size_t>(k - 1));\n        buf[1] = '.';\n        buf += 1 + k;\n    }\n\n    *buf++ = 'e';\n    return append_exponent(buf, n - 1);\n}\n\n} // namespace dtoa_impl\n\n/*!\n@brief generates a decimal representation of the floating-point number value in [first, last).\n\nThe format of the resulting decimal representation is similar to printf's %g\nformat. Returns an iterator pointing past-the-end of the decimal representation.\n\n@note The input number must be finite, i.e. NaN's and Inf's are not supported.\n@note The buffer must be large enough.\n@note The result is NOT null-terminated.\n*/\ntemplate <typename FloatType>\nJSON_HEDLEY_NON_NULL(1, 2)\nJSON_HEDLEY_RETURNS_NON_NULL\nchar* to_chars(char* first, const char* last, FloatType value)\n{\n    static_cast<void>(last); // maybe unused - fix warning\n    assert(std::isfinite(value));\n\n    // Use signbit(value) instead of (value < 0) since signbit works for -0.\n    if (std::signbit(value))\n    {\n        value = -value;\n        *first++ = '-';\n    }\n\n    if (value == 0) // +-0\n    {\n        *first++ = '0';\n        // Make it look like a floating-point number (#362, #378)\n        *first++ = '.';\n        *first++ = '0';\n        return first;\n    }\n\n    assert(last - first >= std::numeric_limits<FloatType>::max_digits10);\n\n    // Compute v = buffer * 10^decimal_exponent.\n    // The decimal digits are stored in the buffer, which needs to be interpreted\n    // as an unsigned decimal integer.\n    // len is the length of the buffer, i.e. the number of decimal digits.\n    int len = 0;\n    int decimal_exponent = 0;\n    dtoa_impl::grisu2(first, len, decimal_exponent, value);\n\n    assert(len <= std::numeric_limits<FloatType>::max_digits10);\n\n    // Format the buffer like printf(\"%.*g\", prec, value)\n    constexpr int kMinExp = -4;\n    // Use digits10 here to increase compatibility with version 2.\n    constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;\n\n    assert(last - first >= kMaxExp + 2);\n    assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);\n    assert(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);\n\n    return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);\n}\n\n} // namespace detail\n} // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/conversions/to_json.hpp",
    "content": "#pragma once\n\n#include <algorithm> // copy\n#include <ciso646> // or, and, not\n#include <iterator> // begin, end\n#include <string> // string\n#include <tuple> // tuple, get\n#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type\n#include <utility> // move, forward, declval, pair\n#include <valarray> // valarray\n#include <vector> // vector\n\n#include <nlohmann/detail/iterators/iteration_proxy.hpp>\n#include <nlohmann/detail/meta/cpp_future.hpp>\n#include <nlohmann/detail/meta/type_traits.hpp>\n#include <nlohmann/detail/value_t.hpp>\n\nnamespace nlohmann\n{\nnamespace detail\n{\n//////////////////\n// constructors //\n//////////////////\n\ntemplate<value_t> struct external_constructor;\n\ntemplate<>\nstruct external_constructor<value_t::boolean>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept\n    {\n        j.m_type = value_t::boolean;\n        j.m_value = b;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::string>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)\n    {\n        j.m_type = value_t::string;\n        j.m_value = s;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)\n    {\n        j.m_type = value_t::string;\n        j.m_value = std::move(s);\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType, typename CompatibleStringType,\n             enable_if_t<not std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,\n                         int> = 0>\n    static void construct(BasicJsonType& j, const CompatibleStringType& str)\n    {\n        j.m_type = value_t::string;\n        j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_float>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept\n    {\n        j.m_type = value_t::number_float;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_unsigned>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept\n    {\n        j.m_type = value_t::number_unsigned;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_integer>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept\n    {\n        j.m_type = value_t::number_integer;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::array>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)\n    {\n        j.m_type = value_t::array;\n        j.m_value = arr;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)\n    {\n        j.m_type = value_t::array;\n        j.m_value = std::move(arr);\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType, typename CompatibleArrayType,\n             enable_if_t<not std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,\n                         int> = 0>\n    static void construct(BasicJsonType& j, const CompatibleArrayType& arr)\n    {\n        using std::begin;\n        using std::end;\n        j.m_type = value_t::array;\n        j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const std::vector<bool>& arr)\n    {\n        j.m_type = value_t::array;\n        j.m_value = value_t::array;\n        j.m_value.array->reserve(arr.size());\n        for (const bool x : arr)\n        {\n            j.m_value.array->push_back(x);\n        }\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType, typename T,\n             enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>\n    static void construct(BasicJsonType& j, const std::valarray<T>& arr)\n    {\n        j.m_type = value_t::array;\n        j.m_value = value_t::array;\n        j.m_value.array->resize(arr.size());\n        if (arr.size() > 0)\n        {\n            std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());\n        }\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::object>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)\n    {\n        j.m_type = value_t::object;\n        j.m_value = obj;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)\n    {\n        j.m_type = value_t::object;\n        j.m_value = std::move(obj);\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType, typename CompatibleObjectType,\n             enable_if_t<not std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int> = 0>\n    static void construct(BasicJsonType& j, const CompatibleObjectType& obj)\n    {\n        using std::begin;\n        using std::end;\n\n        j.m_type = value_t::object;\n        j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));\n        j.assert_invariant();\n    }\n};\n\n/////////////\n// to_json //\n/////////////\n\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>\nvoid to_json(BasicJsonType& j, T b) noexcept\n{\n    external_constructor<value_t::boolean>::construct(j, b);\n}\n\ntemplate<typename BasicJsonType, typename CompatibleString,\n         enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const CompatibleString& s)\n{\n    external_constructor<value_t::string>::construct(j, s);\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)\n{\n    external_constructor<value_t::string>::construct(j, std::move(s));\n}\n\ntemplate<typename BasicJsonType, typename FloatType,\n         enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, FloatType val) noexcept\n{\n    external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleNumberUnsignedType,\n         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept\n{\n    external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleNumberIntegerType,\n         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept\n{\n    external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename EnumType,\n         enable_if_t<std::is_enum<EnumType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, EnumType e) noexcept\n{\n    using underlying_type = typename std::underlying_type<EnumType>::type;\n    external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, const std::vector<bool>& e)\n{\n    external_constructor<value_t::array>::construct(j, e);\n}\n\ntemplate <typename BasicJsonType, typename CompatibleArrayType,\n          enable_if_t<is_compatible_array_type<BasicJsonType,\n                      CompatibleArrayType>::value and\n                      not is_compatible_object_type<\n                          BasicJsonType, CompatibleArrayType>::value and\n                      not is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value and\n                      not is_basic_json<CompatibleArrayType>::value,\n                      int> = 0>\nvoid to_json(BasicJsonType& j, const CompatibleArrayType& arr)\n{\n    external_constructor<value_t::array>::construct(j, arr);\n}\n\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const std::valarray<T>& arr)\n{\n    external_constructor<value_t::array>::construct(j, std::move(arr));\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)\n{\n    external_constructor<value_t::array>::construct(j, std::move(arr));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType,\n         enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value and not is_basic_json<CompatibleObjectType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const CompatibleObjectType& obj)\n{\n    external_constructor<value_t::object>::construct(j, obj);\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)\n{\n    external_constructor<value_t::object>::construct(j, std::move(obj));\n}\n\ntemplate <\n    typename BasicJsonType, typename T, std::size_t N,\n    enable_if_t<not std::is_constructible<typename BasicJsonType::string_t,\n                const T(&)[N]>::value,\n                int> = 0 >\nvoid to_json(BasicJsonType& j, const T(&arr)[N])\n{\n    external_constructor<value_t::array>::construct(j, arr);\n}\n\ntemplate < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >\nvoid to_json(BasicJsonType& j, const std::pair<T1, T2>& p)\n{\n    j = { p.first, p.second };\n}\n\n// for https://github.com/nlohmann/json/pull/1134\ntemplate < typename BasicJsonType, typename T,\n           enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const T& b)\n{\n    j = { {b.key(), b.value()} };\n}\n\ntemplate<typename BasicJsonType, typename Tuple, std::size_t... Idx>\nvoid to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)\n{\n    j = { std::get<Idx>(t)... };\n}\n\ntemplate<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>\nvoid to_json(BasicJsonType& j, const T& t)\n{\n    to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});\n}\n\nstruct to_json_fn\n{\n    template<typename BasicJsonType, typename T>\n    auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))\n    -> decltype(to_json(j, std::forward<T>(val)), void())\n    {\n        return to_json(j, std::forward<T>(val));\n    }\n};\n}  // namespace detail\n\n/// namespace to hold default `to_json` function\nnamespace\n{\nconstexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value;\n} // namespace\n} // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/exceptions.hpp",
    "content": "#pragma once\n\n#include <exception> // exception\n#include <stdexcept> // runtime_error\n#include <string> // to_string\n\n#include <nlohmann/detail/input/position_t.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n\nnamespace nlohmann\n{\nnamespace detail\n{\n////////////////\n// exceptions //\n////////////////\n\n/*!\n@brief general exception of the @ref basic_json class\n\nThis class is an extension of `std::exception` objects with a member @a id for\nexception ids. It is used as the base class for all exceptions thrown by the\n@ref basic_json class. This class can hence be used as \"wildcard\" to catch\nexceptions.\n\nSubclasses:\n- @ref parse_error for exceptions indicating a parse error\n- @ref invalid_iterator for exceptions indicating errors with iterators\n- @ref type_error for exceptions indicating executing a member function with\n                  a wrong type\n- @ref out_of_range for exceptions indicating access out of the defined range\n- @ref other_error for exceptions indicating other library errors\n\n@internal\n@note To have nothrow-copy-constructible exceptions, we internally use\n      `std::runtime_error` which can cope with arbitrary-length error messages.\n      Intermediate strings are built with static functions and then passed to\n      the actual constructor.\n@endinternal\n\n@liveexample{The following code shows how arbitrary library exceptions can be\ncaught.,exception}\n\n@since version 3.0.0\n*/\nclass exception : public std::exception\n{\n  public:\n    /// returns the explanatory string\n    JSON_HEDLEY_RETURNS_NON_NULL\n    const char* what() const noexcept override\n    {\n        return m.what();\n    }\n\n    /// the id of the exception\n    const int id;\n\n  protected:\n    JSON_HEDLEY_NON_NULL(3)\n    exception(int id_, const char* what_arg) : id(id_), m(what_arg) {}\n\n    static std::string name(const std::string& ename, int id_)\n    {\n        return \"[json.exception.\" + ename + \".\" + std::to_string(id_) + \"] \";\n    }\n\n  private:\n    /// an exception object as storage for error messages\n    std::runtime_error m;\n};\n\n/*!\n@brief exception indicating a parse error\n\nThis exception is thrown by the library when a parse error occurs. Parse errors\ncan occur during the deserialization of JSON text, CBOR, MessagePack, as well\nas when using JSON Patch.\n\nMember @a byte holds the byte index of the last read character in the input\nfile.\n\nExceptions have ids 1xx.\n\nname / id                      | example message | description\n------------------------------ | --------------- | -------------------------\njson.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position.\njson.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\\uxxxx` entries (\"surrogate pairs\"). This error indicates that the surrogate pair is incomplete or contains an invalid code point.\njson.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid.\njson.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects.\njson.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one \"op\" member, whose value indicates the operation to perform. Its value must be one of \"add\", \"remove\", \"replace\", \"move\", \"copy\", or \"test\"; other values are errors.\njson.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`.\njson.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character.\njson.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences.\njson.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number.\njson.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read.\njson.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read.\njson.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read.\njson.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet).\n\n@note For an input with n bytes, 1 is the index of the first character and n+1\n      is the index of the terminating null byte or the end of file. This also\n      holds true when reading a byte vector (CBOR or MessagePack).\n\n@liveexample{The following code shows how a `parse_error` exception can be\ncaught.,parse_error}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass parse_error : public exception\n{\n  public:\n    /*!\n    @brief create a parse error exception\n    @param[in] id_       the id of the exception\n    @param[in] pos       the position where the error occurred (or with\n                         chars_read_total=0 if the position cannot be\n                         determined)\n    @param[in] what_arg  the explanatory string\n    @return parse_error object\n    */\n    static parse_error create(int id_, const position_t& pos, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"parse_error\", id_) + \"parse error\" +\n                        position_string(pos) + \": \" + what_arg;\n        return parse_error(id_, pos.chars_read_total, w.c_str());\n    }\n\n    static parse_error create(int id_, std::size_t byte_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"parse_error\", id_) + \"parse error\" +\n                        (byte_ != 0 ? (\" at byte \" + std::to_string(byte_)) : \"\") +\n                        \": \" + what_arg;\n        return parse_error(id_, byte_, w.c_str());\n    }\n\n    /*!\n    @brief byte index of the parse error\n\n    The byte index of the last read character in the input file.\n\n    @note For an input with n bytes, 1 is the index of the first character and\n          n+1 is the index of the terminating null byte or the end of file.\n          This also holds true when reading a byte vector (CBOR or MessagePack).\n    */\n    const std::size_t byte;\n\n  private:\n    parse_error(int id_, std::size_t byte_, const char* what_arg)\n        : exception(id_, what_arg), byte(byte_) {}\n\n    static std::string position_string(const position_t& pos)\n    {\n        return \" at line \" + std::to_string(pos.lines_read + 1) +\n               \", column \" + std::to_string(pos.chars_read_current_line);\n    }\n};\n\n/*!\n@brief exception indicating errors with iterators\n\nThis exception is thrown if iterators passed to a library function do not match\nthe expected semantics.\n\nExceptions have ids 2xx.\n\nname / id                           | example message | description\n----------------------------------- | --------------- | -------------------------\njson.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.\njson.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion.\njson.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from.\njson.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid.\njson.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid.\njson.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range.\njson.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key.\njson.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.\njson.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.\njson.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.\njson.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to.\njson.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container.\njson.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered.\njson.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin().\n\n@liveexample{The following code shows how an `invalid_iterator` exception can be\ncaught.,invalid_iterator}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass invalid_iterator : public exception\n{\n  public:\n    static invalid_iterator create(int id_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"invalid_iterator\", id_) + what_arg;\n        return invalid_iterator(id_, w.c_str());\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    invalid_iterator(int id_, const char* what_arg)\n        : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating executing a member function with a wrong type\n\nThis exception is thrown in case of a type error; that is, a library function is\nexecuted on a JSON value whose type does not match the expected semantics.\n\nExceptions have ids 3xx.\n\nname / id                     | example message | description\n----------------------------- | --------------- | -------------------------\njson.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead.\njson.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types.\njson.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &.\njson.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types.\njson.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types.\njson.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types.\njson.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types.\njson.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types.\njson.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types.\njson.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types.\njson.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types.\njson.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types.\njson.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined.\njson.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.\njson.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.\njson.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |\njson.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) |\n\n@liveexample{The following code shows how a `type_error` exception can be\ncaught.,type_error}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass type_error : public exception\n{\n  public:\n    static type_error create(int id_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"type_error\", id_) + what_arg;\n        return type_error(id_, w.c_str());\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating access out of the defined range\n\nThis exception is thrown in case a library function is called on an input\nparameter that exceeds the expected range, for instance in case of array\nindices or nonexisting object keys.\n\nExceptions have ids 4xx.\n\nname / id                       | example message | description\n------------------------------- | --------------- | -------------------------\njson.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1.\njson.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it.\njson.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object.\njson.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.\njson.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.\njson.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.\njson.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. |\njson.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. |\njson.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string |\n\n@liveexample{The following code shows how an `out_of_range` exception can be\ncaught.,out_of_range}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass out_of_range : public exception\n{\n  public:\n    static out_of_range create(int id_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"out_of_range\", id_) + what_arg;\n        return out_of_range(id_, w.c_str());\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating other library errors\n\nThis exception is thrown in case of errors that cannot be classified with the\nother exception types.\n\nExceptions have ids 5xx.\n\nname / id                      | example message | description\n------------------------------ | --------------- | -------------------------\njson.exception.other_error.501 | unsuccessful: {\"op\":\"test\",\"path\":\"/baz\", \"value\":\"bar\"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed.\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n\n@liveexample{The following code shows how an `other_error` exception can be\ncaught.,other_error}\n\n@since version 3.0.0\n*/\nclass other_error : public exception\n{\n  public:\n    static other_error create(int id_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"other_error\", id_) + what_arg;\n        return other_error(id_, w.c_str());\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n}  // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/input/binary_reader.hpp",
    "content": "#pragma once\n\n#include <algorithm> // generate_n\n#include <array> // array\n#include <cassert> // assert\n#include <cmath> // ldexp\n#include <cstddef> // size_t\n#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t\n#include <cstdio> // snprintf\n#include <cstring> // memcpy\n#include <iterator> // back_inserter\n#include <limits> // numeric_limits\n#include <string> // char_traits, string\n#include <utility> // make_pair, move\n\n#include <nlohmann/detail/exceptions.hpp>\n#include <nlohmann/detail/input/input_adapters.hpp>\n#include <nlohmann/detail/input/json_sax.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/meta/is_sax.hpp>\n#include <nlohmann/detail/value_t.hpp>\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////\n// binary reader //\n///////////////////\n\n/*!\n@brief deserialization of CBOR, MessagePack, and UBJSON values\n*/\ntemplate<typename BasicJsonType, typename SAX = json_sax_dom_parser<BasicJsonType>>\nclass binary_reader\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using json_sax_t = SAX;\n\n  public:\n    /*!\n    @brief create a binary reader\n\n    @param[in] adapter  input adapter to read from\n    */\n    explicit binary_reader(input_adapter_t adapter) : ia(std::move(adapter))\n    {\n        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};\n        assert(ia);\n    }\n\n    // make class move-only\n    binary_reader(const binary_reader&) = delete;\n    binary_reader(binary_reader&&) = default;\n    binary_reader& operator=(const binary_reader&) = delete;\n    binary_reader& operator=(binary_reader&&) = default;\n    ~binary_reader() = default;\n\n    /*!\n    @param[in] format  the binary format to parse\n    @param[in] sax_    a SAX event processor\n    @param[in] strict  whether to expect the input to be consumed completed\n\n    @return\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    bool sax_parse(const input_format_t format,\n                   json_sax_t* sax_,\n                   const bool strict = true)\n    {\n        sax = sax_;\n        bool result = false;\n\n        switch (format)\n        {\n            case input_format_t::bson:\n                result = parse_bson_internal();\n                break;\n\n            case input_format_t::cbor:\n                result = parse_cbor_internal();\n                break;\n\n            case input_format_t::msgpack:\n                result = parse_msgpack_internal();\n                break;\n\n            case input_format_t::ubjson:\n                result = parse_ubjson_internal();\n                break;\n\n            default:            // LCOV_EXCL_LINE\n                assert(false);  // LCOV_EXCL_LINE\n        }\n\n        // strict mode: next byte must be EOF\n        if (result and strict)\n        {\n            if (format == input_format_t::ubjson)\n            {\n                get_ignore_noop();\n            }\n            else\n            {\n                get();\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(current != std::char_traits<char>::eof()))\n            {\n                return sax->parse_error(chars_read, get_token_string(),\n                                        parse_error::create(110, chars_read, exception_message(format, \"expected end of input; last byte: 0x\" + get_token_string(), \"value\")));\n            }\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief determine system byte order\n\n    @return true if and only if system's byte order is little endian\n\n    @note from http://stackoverflow.com/a/1001328/266378\n    */\n    static constexpr bool little_endianess(int num = 1) noexcept\n    {\n        return *reinterpret_cast<char*>(&num) == 1;\n    }\n\n  private:\n    //////////\n    // BSON //\n    //////////\n\n    /*!\n    @brief Reads in a BSON-object and passes it to the SAX-parser.\n    @return whether a valid BSON-value was passed to the SAX parser\n    */\n    bool parse_bson_internal()\n    {\n        std::int32_t document_size;\n        get_number<std::int32_t, true>(input_format_t::bson, document_size);\n\n        if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1))))\n        {\n            return false;\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_list(/*is_array*/false)))\n        {\n            return false;\n        }\n\n        return sax->end_object();\n    }\n\n    /*!\n    @brief Parses a C-style string from the BSON input.\n    @param[in, out] result  A reference to the string variable where the read\n                            string is to be stored.\n    @return `true` if the \\x00-byte indicating the end of the string was\n             encountered before the EOF; false` indicates an unexpected EOF.\n    */\n    bool get_bson_cstr(string_t& result)\n    {\n        auto out = std::back_inserter(result);\n        while (true)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::bson, \"cstring\")))\n            {\n                return false;\n            }\n            if (current == 0x00)\n            {\n                return true;\n            }\n            *out++ = static_cast<char>(current);\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief Parses a zero-terminated string of length @a len from the BSON\n           input.\n    @param[in] len  The length (including the zero-byte at the end) of the\n                    string to be read.\n    @param[in, out] result  A reference to the string variable where the read\n                            string is to be stored.\n    @tparam NumberType The type of the length @a len\n    @pre len >= 1\n    @return `true` if the string was successfully parsed\n    */\n    template<typename NumberType>\n    bool get_bson_string(const NumberType len, string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(len < 1))\n        {\n            auto last_token = get_token_string();\n            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, \"string length must be at least 1, is \" + std::to_string(len), \"string\")));\n        }\n\n        return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) and get() != std::char_traits<char>::eof();\n    }\n\n    /*!\n    @brief Read a BSON document element of the given @a element_type.\n    @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html\n    @param[in] element_type_parse_position The position in the input stream,\n               where the `element_type` was read.\n    @warning Not all BSON element types are supported yet. An unsupported\n             @a element_type will give rise to a parse_error.114:\n             Unsupported BSON record type 0x...\n    @return whether a valid BSON-object/array was passed to the SAX parser\n    */\n    bool parse_bson_element_internal(const int element_type,\n                                     const std::size_t element_type_parse_position)\n    {\n        switch (element_type)\n        {\n            case 0x01: // double\n            {\n                double number;\n                return get_number<double, true>(input_format_t::bson, number) and sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0x02: // string\n            {\n                std::int32_t len;\n                string_t value;\n                return get_number<std::int32_t, true>(input_format_t::bson, len) and get_bson_string(len, value) and sax->string(value);\n            }\n\n            case 0x03: // object\n            {\n                return parse_bson_internal();\n            }\n\n            case 0x04: // array\n            {\n                return parse_bson_array();\n            }\n\n            case 0x08: // boolean\n            {\n                return sax->boolean(get() != 0);\n            }\n\n            case 0x0A: // null\n            {\n                return sax->null();\n            }\n\n            case 0x10: // int32\n            {\n                std::int32_t value;\n                return get_number<std::int32_t, true>(input_format_t::bson, value) and sax->number_integer(value);\n            }\n\n            case 0x12: // int64\n            {\n                std::int64_t value;\n                return get_number<std::int64_t, true>(input_format_t::bson, value) and sax->number_integer(value);\n            }\n\n            default: // anything else not supported (yet)\n            {\n                std::array<char, 3> cr{{}};\n                (std::snprintf)(cr.data(), cr.size(), \"%.2hhX\", static_cast<unsigned char>(element_type));\n                return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, \"Unsupported BSON record type 0x\" + std::string(cr.data())));\n            }\n        }\n    }\n\n    /*!\n    @brief Read a BSON element list (as specified in the BSON-spec)\n\n    The same binary layout is used for objects and arrays, hence it must be\n    indicated with the argument @a is_array which one is expected\n    (true --> array, false --> object).\n\n    @param[in] is_array Determines if the element list being read is to be\n                        treated as an object (@a is_array == false), or as an\n                        array (@a is_array == true).\n    @return whether a valid BSON-object/array was passed to the SAX parser\n    */\n    bool parse_bson_element_list(const bool is_array)\n    {\n        string_t key;\n        while (int element_type = get())\n        {\n            if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::bson, \"element list\")))\n            {\n                return false;\n            }\n\n            const std::size_t element_type_parse_position = chars_read;\n            if (JSON_HEDLEY_UNLIKELY(not get_bson_cstr(key)))\n            {\n                return false;\n            }\n\n            if (not is_array and not sax->key(key))\n            {\n                return false;\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_internal(element_type, element_type_parse_position)))\n            {\n                return false;\n            }\n\n            // get_bson_cstr only appends\n            key.clear();\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief Reads an array from the BSON input and passes it to the SAX-parser.\n    @return whether a valid BSON-array was passed to the SAX parser\n    */\n    bool parse_bson_array()\n    {\n        std::int32_t document_size;\n        get_number<std::int32_t, true>(input_format_t::bson, document_size);\n\n        if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1))))\n        {\n            return false;\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_list(/*is_array*/true)))\n        {\n            return false;\n        }\n\n        return sax->end_array();\n    }\n\n    //////////\n    // CBOR //\n    //////////\n\n    /*!\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether a valid CBOR value was passed to the SAX parser\n    */\n    bool parse_cbor_internal(const bool get_char = true)\n    {\n        switch (get_char ? get() : current)\n        {\n            // EOF\n            case std::char_traits<char>::eof():\n                return unexpect_eof(input_format_t::cbor, \"value\");\n\n            // Integer 0x00..0x17 (0..23)\n            case 0x00:\n            case 0x01:\n            case 0x02:\n            case 0x03:\n            case 0x04:\n            case 0x05:\n            case 0x06:\n            case 0x07:\n            case 0x08:\n            case 0x09:\n            case 0x0A:\n            case 0x0B:\n            case 0x0C:\n            case 0x0D:\n            case 0x0E:\n            case 0x0F:\n            case 0x10:\n            case 0x11:\n            case 0x12:\n            case 0x13:\n            case 0x14:\n            case 0x15:\n            case 0x16:\n            case 0x17:\n                return sax->number_unsigned(static_cast<number_unsigned_t>(current));\n\n            case 0x18: // Unsigned integer (one-byte uint8_t follows)\n            {\n                std::uint8_t number;\n                return get_number(input_format_t::cbor, number) and sax->number_unsigned(number);\n            }\n\n            case 0x19: // Unsigned integer (two-byte uint16_t follows)\n            {\n                std::uint16_t number;\n                return get_number(input_format_t::cbor, number) and sax->number_unsigned(number);\n            }\n\n            case 0x1A: // Unsigned integer (four-byte uint32_t follows)\n            {\n                std::uint32_t number;\n                return get_number(input_format_t::cbor, number) and sax->number_unsigned(number);\n            }\n\n            case 0x1B: // Unsigned integer (eight-byte uint64_t follows)\n            {\n                std::uint64_t number;\n                return get_number(input_format_t::cbor, number) and sax->number_unsigned(number);\n            }\n\n            // Negative integer -1-0x00..-1-0x17 (-1..-24)\n            case 0x20:\n            case 0x21:\n            case 0x22:\n            case 0x23:\n            case 0x24:\n            case 0x25:\n            case 0x26:\n            case 0x27:\n            case 0x28:\n            case 0x29:\n            case 0x2A:\n            case 0x2B:\n            case 0x2C:\n            case 0x2D:\n            case 0x2E:\n            case 0x2F:\n            case 0x30:\n            case 0x31:\n            case 0x32:\n            case 0x33:\n            case 0x34:\n            case 0x35:\n            case 0x36:\n            case 0x37:\n                return sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - current));\n\n            case 0x38: // Negative integer (one-byte uint8_t follows)\n            {\n                std::uint8_t number;\n                return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x39: // Negative integer -1-n (two-byte uint16_t follows)\n            {\n                std::uint16_t number;\n                return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)\n            {\n                std::uint32_t number;\n                return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)\n            {\n                std::uint64_t number;\n                return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast<number_integer_t>(-1)\n                        - static_cast<number_integer_t>(number));\n            }\n\n            // UTF-8 string (0x00..0x17 bytes follow)\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)\n            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)\n            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)\n            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)\n            case 0x7F: // UTF-8 string (indefinite length)\n            {\n                string_t s;\n                return get_cbor_string(s) and sax->string(s);\n            }\n\n            // array (0x00..0x17 data items follow)\n            case 0x80:\n            case 0x81:\n            case 0x82:\n            case 0x83:\n            case 0x84:\n            case 0x85:\n            case 0x86:\n            case 0x87:\n            case 0x88:\n            case 0x89:\n            case 0x8A:\n            case 0x8B:\n            case 0x8C:\n            case 0x8D:\n            case 0x8E:\n            case 0x8F:\n            case 0x90:\n            case 0x91:\n            case 0x92:\n            case 0x93:\n            case 0x94:\n            case 0x95:\n            case 0x96:\n            case 0x97:\n                return get_cbor_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));\n\n            case 0x98: // array (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len;\n                return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast<std::size_t>(len));\n            }\n\n            case 0x99: // array (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len;\n                return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast<std::size_t>(len));\n            }\n\n            case 0x9A: // array (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len;\n                return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast<std::size_t>(len));\n            }\n\n            case 0x9B: // array (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len;\n                return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast<std::size_t>(len));\n            }\n\n            case 0x9F: // array (indefinite length)\n                return get_cbor_array(std::size_t(-1));\n\n            // map (0x00..0x17 pairs of data items follow)\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n                return get_cbor_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));\n\n            case 0xB8: // map (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len;\n                return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast<std::size_t>(len));\n            }\n\n            case 0xB9: // map (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len;\n                return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast<std::size_t>(len));\n            }\n\n            case 0xBA: // map (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len;\n                return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast<std::size_t>(len));\n            }\n\n            case 0xBB: // map (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len;\n                return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast<std::size_t>(len));\n            }\n\n            case 0xBF: // map (indefinite length)\n                return get_cbor_object(std::size_t(-1));\n\n            case 0xF4: // false\n                return sax->boolean(false);\n\n            case 0xF5: // true\n                return sax->boolean(true);\n\n            case 0xF6: // null\n                return sax->null();\n\n            case 0xF9: // Half-Precision Float (two-byte IEEE 754)\n            {\n                const int byte1_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, \"number\")))\n                {\n                    return false;\n                }\n                const int byte2_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, \"number\")))\n                {\n                    return false;\n                }\n\n                const auto byte1 = static_cast<unsigned char>(byte1_raw);\n                const auto byte2 = static_cast<unsigned char>(byte2_raw);\n\n                // code from RFC 7049, Appendix D, Figure 3:\n                // As half-precision floating-point numbers were only added\n                // to IEEE 754 in 2008, today's programming platforms often\n                // still only have limited support for them. It is very\n                // easy to include at least decoding support for them even\n                // without such support. An example of a small decoder for\n                // half-precision floating-point numbers in the C language\n                // is shown in Fig. 3.\n                const auto half = static_cast<unsigned int>((byte1 << 8u) + byte2);\n                const double val = [&half]\n                {\n                    const int exp = (half >> 10u) & 0x1Fu;\n                    const unsigned int mant = half & 0x3FFu;\n                    assert(0 <= exp and exp <= 32);\n                    assert(mant <= 1024);\n                    switch (exp)\n                    {\n                        case 0:\n                            return std::ldexp(mant, -24);\n                        case 31:\n                            return (mant == 0)\n                            ? std::numeric_limits<double>::infinity()\n                            : std::numeric_limits<double>::quiet_NaN();\n                        default:\n                            return std::ldexp(mant + 1024, exp - 25);\n                    }\n                }();\n                return sax->number_float((half & 0x8000u) != 0\n                                         ? static_cast<number_float_t>(-val)\n                                         : static_cast<number_float_t>(val), \"\");\n            }\n\n            case 0xFA: // Single-Precision Float (four-byte IEEE 754)\n            {\n                float number;\n                return get_number(input_format_t::cbor, number) and sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xFB: // Double-Precision Float (eight-byte IEEE 754)\n            {\n                double number;\n                return get_number(input_format_t::cbor, number) and sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            default: // anything else (0xFF is handled inside the other types)\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, \"invalid byte: 0x\" + last_token, \"value\")));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a CBOR string\n\n    This function first reads starting bytes to determine the expected\n    string length and then copies this number of bytes into a string.\n    Additionally, CBOR's strings with indefinite lengths are supported.\n\n    @param[out] result  created string\n\n    @return whether string creation completed\n    */\n    bool get_cbor_string(string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, \"string\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // UTF-8 string (0x00..0x17 bytes follow)\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            {\n                return get_string(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len;\n                return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len;\n                return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len;\n                return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len;\n                return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7F: // UTF-8 string (indefinite length)\n            {\n                while (get() != 0xFF)\n                {\n                    string_t chunk;\n                    if (not get_cbor_string(chunk))\n                    {\n                        return false;\n                    }\n                    result.append(chunk);\n                }\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, \"expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x\" + last_token, \"string\")));\n            }\n        }\n    }\n\n    /*!\n    @param[in] len  the length of the array or std::size_t(-1) for an\n                    array of indefinite size\n    @return whether array creation completed\n    */\n    bool get_cbor_array(const std::size_t len)\n    {\n        if (JSON_HEDLEY_UNLIKELY(not sax->start_array(len)))\n        {\n            return false;\n        }\n\n        if (len != std::size_t(-1))\n        {\n            for (std::size_t i = 0; i < len; ++i)\n            {\n                if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal()))\n                {\n                    return false;\n                }\n            }\n        }\n        else\n        {\n            while (get() != 0xFF)\n            {\n                if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal(false)))\n                {\n                    return false;\n                }\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @param[in] len  the length of the object or std::size_t(-1) for an\n                    object of indefinite size\n    @return whether object creation completed\n    */\n    bool get_cbor_object(const std::size_t len)\n    {\n        if (JSON_HEDLEY_UNLIKELY(not sax->start_object(len)))\n        {\n            return false;\n        }\n\n        string_t key;\n        if (len != std::size_t(-1))\n        {\n            for (std::size_t i = 0; i < len; ++i)\n            {\n                get();\n                if (JSON_HEDLEY_UNLIKELY(not get_cbor_string(key) or not sax->key(key)))\n                {\n                    return false;\n                }\n\n                if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal()))\n                {\n                    return false;\n                }\n                key.clear();\n            }\n        }\n        else\n        {\n            while (get() != 0xFF)\n            {\n                if (JSON_HEDLEY_UNLIKELY(not get_cbor_string(key) or not sax->key(key)))\n                {\n                    return false;\n                }\n\n                if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal()))\n                {\n                    return false;\n                }\n                key.clear();\n            }\n        }\n\n        return sax->end_object();\n    }\n\n    /////////////\n    // MsgPack //\n    /////////////\n\n    /*!\n    @return whether a valid MessagePack value was passed to the SAX parser\n    */\n    bool parse_msgpack_internal()\n    {\n        switch (get())\n        {\n            // EOF\n            case std::char_traits<char>::eof():\n                return unexpect_eof(input_format_t::msgpack, \"value\");\n\n            // positive fixint\n            case 0x00:\n            case 0x01:\n            case 0x02:\n            case 0x03:\n            case 0x04:\n            case 0x05:\n            case 0x06:\n            case 0x07:\n            case 0x08:\n            case 0x09:\n            case 0x0A:\n            case 0x0B:\n            case 0x0C:\n            case 0x0D:\n            case 0x0E:\n            case 0x0F:\n            case 0x10:\n            case 0x11:\n            case 0x12:\n            case 0x13:\n            case 0x14:\n            case 0x15:\n            case 0x16:\n            case 0x17:\n            case 0x18:\n            case 0x19:\n            case 0x1A:\n            case 0x1B:\n            case 0x1C:\n            case 0x1D:\n            case 0x1E:\n            case 0x1F:\n            case 0x20:\n            case 0x21:\n            case 0x22:\n            case 0x23:\n            case 0x24:\n            case 0x25:\n            case 0x26:\n            case 0x27:\n            case 0x28:\n            case 0x29:\n            case 0x2A:\n            case 0x2B:\n            case 0x2C:\n            case 0x2D:\n            case 0x2E:\n            case 0x2F:\n            case 0x30:\n            case 0x31:\n            case 0x32:\n            case 0x33:\n            case 0x34:\n            case 0x35:\n            case 0x36:\n            case 0x37:\n            case 0x38:\n            case 0x39:\n            case 0x3A:\n            case 0x3B:\n            case 0x3C:\n            case 0x3D:\n            case 0x3E:\n            case 0x3F:\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            case 0x58:\n            case 0x59:\n            case 0x5A:\n            case 0x5B:\n            case 0x5C:\n            case 0x5D:\n            case 0x5E:\n            case 0x5F:\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            case 0x78:\n            case 0x79:\n            case 0x7A:\n            case 0x7B:\n            case 0x7C:\n            case 0x7D:\n            case 0x7E:\n            case 0x7F:\n                return sax->number_unsigned(static_cast<number_unsigned_t>(current));\n\n            // fixmap\n            case 0x80:\n            case 0x81:\n            case 0x82:\n            case 0x83:\n            case 0x84:\n            case 0x85:\n            case 0x86:\n            case 0x87:\n            case 0x88:\n            case 0x89:\n            case 0x8A:\n            case 0x8B:\n            case 0x8C:\n            case 0x8D:\n            case 0x8E:\n            case 0x8F:\n                return get_msgpack_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));\n\n            // fixarray\n            case 0x90:\n            case 0x91:\n            case 0x92:\n            case 0x93:\n            case 0x94:\n            case 0x95:\n            case 0x96:\n            case 0x97:\n            case 0x98:\n            case 0x99:\n            case 0x9A:\n            case 0x9B:\n            case 0x9C:\n            case 0x9D:\n            case 0x9E:\n            case 0x9F:\n                return get_msgpack_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));\n\n            // fixstr\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n            case 0xB8:\n            case 0xB9:\n            case 0xBA:\n            case 0xBB:\n            case 0xBC:\n            case 0xBD:\n            case 0xBE:\n            case 0xBF:\n            case 0xD9: // str 8\n            case 0xDA: // str 16\n            case 0xDB: // str 32\n            {\n                string_t s;\n                return get_msgpack_string(s) and sax->string(s);\n            }\n\n            case 0xC0: // nil\n                return sax->null();\n\n            case 0xC2: // false\n                return sax->boolean(false);\n\n            case 0xC3: // true\n                return sax->boolean(true);\n\n            case 0xCA: // float 32\n            {\n                float number;\n                return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xCB: // float 64\n            {\n                double number;\n                return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xCC: // uint 8\n            {\n                std::uint8_t number;\n                return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number);\n            }\n\n            case 0xCD: // uint 16\n            {\n                std::uint16_t number;\n                return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number);\n            }\n\n            case 0xCE: // uint 32\n            {\n                std::uint32_t number;\n                return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number);\n            }\n\n            case 0xCF: // uint 64\n            {\n                std::uint64_t number;\n                return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number);\n            }\n\n            case 0xD0: // int 8\n            {\n                std::int8_t number;\n                return get_number(input_format_t::msgpack, number) and sax->number_integer(number);\n            }\n\n            case 0xD1: // int 16\n            {\n                std::int16_t number;\n                return get_number(input_format_t::msgpack, number) and sax->number_integer(number);\n            }\n\n            case 0xD2: // int 32\n            {\n                std::int32_t number;\n                return get_number(input_format_t::msgpack, number) and sax->number_integer(number);\n            }\n\n            case 0xD3: // int 64\n            {\n                std::int64_t number;\n                return get_number(input_format_t::msgpack, number) and sax->number_integer(number);\n            }\n\n            case 0xDC: // array 16\n            {\n                std::uint16_t len;\n                return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast<std::size_t>(len));\n            }\n\n            case 0xDD: // array 32\n            {\n                std::uint32_t len;\n                return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast<std::size_t>(len));\n            }\n\n            case 0xDE: // map 16\n            {\n                std::uint16_t len;\n                return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast<std::size_t>(len));\n            }\n\n            case 0xDF: // map 32\n            {\n                std::uint32_t len;\n                return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast<std::size_t>(len));\n            }\n\n            // negative fixint\n            case 0xE0:\n            case 0xE1:\n            case 0xE2:\n            case 0xE3:\n            case 0xE4:\n            case 0xE5:\n            case 0xE6:\n            case 0xE7:\n            case 0xE8:\n            case 0xE9:\n            case 0xEA:\n            case 0xEB:\n            case 0xEC:\n            case 0xED:\n            case 0xEE:\n            case 0xEF:\n            case 0xF0:\n            case 0xF1:\n            case 0xF2:\n            case 0xF3:\n            case 0xF4:\n            case 0xF5:\n            case 0xF6:\n            case 0xF7:\n            case 0xF8:\n            case 0xF9:\n            case 0xFA:\n            case 0xFB:\n            case 0xFC:\n            case 0xFD:\n            case 0xFE:\n            case 0xFF:\n                return sax->number_integer(static_cast<std::int8_t>(current));\n\n            default: // anything else\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, \"invalid byte: 0x\" + last_token, \"value\")));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a MessagePack string\n\n    This function first reads starting bytes to determine the expected\n    string length and then copies this number of bytes into a string.\n\n    @param[out] result  created string\n\n    @return whether string creation completed\n    */\n    bool get_msgpack_string(string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::msgpack, \"string\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // fixstr\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n            case 0xB8:\n            case 0xB9:\n            case 0xBA:\n            case 0xBB:\n            case 0xBC:\n            case 0xBD:\n            case 0xBE:\n            case 0xBF:\n            {\n                return get_string(input_format_t::msgpack, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0xD9: // str 8\n            {\n                std::uint8_t len;\n                return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result);\n            }\n\n            case 0xDA: // str 16\n            {\n                std::uint16_t len;\n                return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result);\n            }\n\n            case 0xDB: // str 32\n            {\n                std::uint32_t len;\n                return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result);\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, \"expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x\" + last_token, \"string\")));\n            }\n        }\n    }\n\n    /*!\n    @param[in] len  the length of the array\n    @return whether array creation completed\n    */\n    bool get_msgpack_array(const std::size_t len)\n    {\n        if (JSON_HEDLEY_UNLIKELY(not sax->start_array(len)))\n        {\n            return false;\n        }\n\n        for (std::size_t i = 0; i < len; ++i)\n        {\n            if (JSON_HEDLEY_UNLIKELY(not parse_msgpack_internal()))\n            {\n                return false;\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @param[in] len  the length of the object\n    @return whether object creation completed\n    */\n    bool get_msgpack_object(const std::size_t len)\n    {\n        if (JSON_HEDLEY_UNLIKELY(not sax->start_object(len)))\n        {\n            return false;\n        }\n\n        string_t key;\n        for (std::size_t i = 0; i < len; ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(not get_msgpack_string(key) or not sax->key(key)))\n            {\n                return false;\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(not parse_msgpack_internal()))\n            {\n                return false;\n            }\n            key.clear();\n        }\n\n        return sax->end_object();\n    }\n\n    ////////////\n    // UBJSON //\n    ////////////\n\n    /*!\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether a valid UBJSON value was passed to the SAX parser\n    */\n    bool parse_ubjson_internal(const bool get_char = true)\n    {\n        return get_ubjson_value(get_char ? get_ignore_noop() : current);\n    }\n\n    /*!\n    @brief reads a UBJSON string\n\n    This function is either called after reading the 'S' byte explicitly\n    indicating a string, or in case of an object key where the 'S' byte can be\n    left out.\n\n    @param[out] result   created string\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether string creation completed\n    */\n    bool get_ubjson_string(string_t& result, const bool get_char = true)\n    {\n        if (get_char)\n        {\n            get();  // TODO(niels): may we ignore N here?\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, \"value\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            case 'U':\n            {\n                std::uint8_t len;\n                return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'i':\n            {\n                std::int8_t len;\n                return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'I':\n            {\n                std::int16_t len;\n                return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'l':\n            {\n                std::int32_t len;\n                return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'L':\n            {\n                std::int64_t len;\n                return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);\n            }\n\n            default:\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"expected length type specification (U, i, I, l, L); last byte: 0x\" + last_token, \"string\")));\n        }\n    }\n\n    /*!\n    @param[out] result  determined size\n    @return whether size determination completed\n    */\n    bool get_ubjson_size_value(std::size_t& result)\n    {\n        switch (get_ignore_noop())\n        {\n            case 'U':\n            {\n                std::uint8_t number;\n                if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'i':\n            {\n                std::int8_t number;\n                if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'I':\n            {\n                std::int16_t number;\n                if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'l':\n            {\n                std::int32_t number;\n                if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'L':\n            {\n                std::int64_t number;\n                if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"expected length type specification (U, i, I, l, L) after '#'; last byte: 0x\" + last_token, \"size\")));\n            }\n        }\n    }\n\n    /*!\n    @brief determine the type and size for a container\n\n    In the optimized UBJSON format, a type and a size can be provided to allow\n    for a more compact representation.\n\n    @param[out] result  pair of the size and the type\n\n    @return whether pair creation completed\n    */\n    bool get_ubjson_size_type(std::pair<std::size_t, int>& result)\n    {\n        result.first = string_t::npos; // size\n        result.second = 0; // type\n\n        get_ignore_noop();\n\n        if (current == '$')\n        {\n            result.second = get();  // must not ignore 'N', because 'N' maybe the type\n            if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, \"type\")))\n            {\n                return false;\n            }\n\n            get_ignore_noop();\n            if (JSON_HEDLEY_UNLIKELY(current != '#'))\n            {\n                if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, \"value\")))\n                {\n                    return false;\n                }\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, \"expected '#' after type information; last byte: 0x\" + last_token, \"size\")));\n            }\n\n            return get_ubjson_size_value(result.first);\n        }\n\n        if (current == '#')\n        {\n            return get_ubjson_size_value(result.first);\n        }\n\n        return true;\n    }\n\n    /*!\n    @param prefix  the previously read or set type prefix\n    @return whether value creation completed\n    */\n    bool get_ubjson_value(const int prefix)\n    {\n        switch (prefix)\n        {\n            case std::char_traits<char>::eof():  // EOF\n                return unexpect_eof(input_format_t::ubjson, \"value\");\n\n            case 'T':  // true\n                return sax->boolean(true);\n            case 'F':  // false\n                return sax->boolean(false);\n\n            case 'Z':  // null\n                return sax->null();\n\n            case 'U':\n            {\n                std::uint8_t number;\n                return get_number(input_format_t::ubjson, number) and sax->number_unsigned(number);\n            }\n\n            case 'i':\n            {\n                std::int8_t number;\n                return get_number(input_format_t::ubjson, number) and sax->number_integer(number);\n            }\n\n            case 'I':\n            {\n                std::int16_t number;\n                return get_number(input_format_t::ubjson, number) and sax->number_integer(number);\n            }\n\n            case 'l':\n            {\n                std::int32_t number;\n                return get_number(input_format_t::ubjson, number) and sax->number_integer(number);\n            }\n\n            case 'L':\n            {\n                std::int64_t number;\n                return get_number(input_format_t::ubjson, number) and sax->number_integer(number);\n            }\n\n            case 'd':\n            {\n                float number;\n                return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 'D':\n            {\n                double number;\n                return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 'C':  // char\n            {\n                get();\n                if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, \"char\")))\n                {\n                    return false;\n                }\n                if (JSON_HEDLEY_UNLIKELY(current > 127))\n                {\n                    auto last_token = get_token_string();\n                    return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"byte after 'C' must be in range 0x00..0x7F; last byte: 0x\" + last_token, \"char\")));\n                }\n                string_t s(1, static_cast<char>(current));\n                return sax->string(s);\n            }\n\n            case 'S':  // string\n            {\n                string_t s;\n                return get_ubjson_string(s) and sax->string(s);\n            }\n\n            case '[':  // array\n                return get_ubjson_array();\n\n            case '{':  // object\n                return get_ubjson_object();\n\n            default: // anything else\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, \"invalid byte: 0x\" + last_token, \"value\")));\n            }\n        }\n    }\n\n    /*!\n    @return whether array creation completed\n    */\n    bool get_ubjson_array()\n    {\n        std::pair<std::size_t, int> size_and_type;\n        if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type)))\n        {\n            return false;\n        }\n\n        if (size_and_type.first != string_t::npos)\n        {\n            if (JSON_HEDLEY_UNLIKELY(not sax->start_array(size_and_type.first)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second != 0)\n            {\n                if (size_and_type.second != 'N')\n                {\n                    for (std::size_t i = 0; i < size_and_type.first; ++i)\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(not get_ubjson_value(size_and_type.second)))\n                        {\n                            return false;\n                        }\n                    }\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal()))\n                    {\n                        return false;\n                    }\n                }\n            }\n        }\n        else\n        {\n            if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1))))\n            {\n                return false;\n            }\n\n            while (current != ']')\n            {\n                if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal(false)))\n                {\n                    return false;\n                }\n                get_ignore_noop();\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @return whether object creation completed\n    */\n    bool get_ubjson_object()\n    {\n        std::pair<std::size_t, int> size_and_type;\n        if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type)))\n        {\n            return false;\n        }\n\n        string_t key;\n        if (size_and_type.first != string_t::npos)\n        {\n            if (JSON_HEDLEY_UNLIKELY(not sax->start_object(size_and_type.first)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second != 0)\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key) or not sax->key(key)))\n                    {\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(not get_ubjson_value(size_and_type.second)))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key) or not sax->key(key)))\n                    {\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal()))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n        }\n        else\n        {\n            if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1))))\n            {\n                return false;\n            }\n\n            while (current != '}')\n            {\n                if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key, false) or not sax->key(key)))\n                {\n                    return false;\n                }\n                if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal()))\n                {\n                    return false;\n                }\n                get_ignore_noop();\n                key.clear();\n            }\n        }\n\n        return sax->end_object();\n    }\n\n    ///////////////////////\n    // Utility functions //\n    ///////////////////////\n\n    /*!\n    @brief get next character from the input\n\n    This function provides the interface to the used input adapter. It does\n    not throw in case the input reached EOF, but returns a -'ve valued\n    `std::char_traits<char>::eof()` in that case.\n\n    @return character read from the input\n    */\n    int get()\n    {\n        ++chars_read;\n        return current = ia->get_character();\n    }\n\n    /*!\n    @return character read from the input after ignoring all 'N' entries\n    */\n    int get_ignore_noop()\n    {\n        do\n        {\n            get();\n        }\n        while (current == 'N');\n\n        return current;\n    }\n\n    /*\n    @brief read a number from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format   the current format (for diagnostics)\n    @param[out] result  number of type @a NumberType\n\n    @return whether conversion completed\n\n    @note This function needs to respect the system's endianess, because\n          bytes in CBOR, MessagePack, and UBJSON are stored in network order\n          (big endian) and therefore need reordering on little endian systems.\n    */\n    template<typename NumberType, bool InputIsLittleEndian = false>\n    bool get_number(const input_format_t format, NumberType& result)\n    {\n        // step 1: read input into array with system's byte order\n        std::array<std::uint8_t, sizeof(NumberType)> vec;\n        for (std::size_t i = 0; i < sizeof(NumberType); ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, \"number\")))\n            {\n                return false;\n            }\n\n            // reverse byte order prior to conversion if necessary\n            if (is_little_endian != InputIsLittleEndian)\n            {\n                vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current);\n            }\n            else\n            {\n                vec[i] = static_cast<std::uint8_t>(current); // LCOV_EXCL_LINE\n            }\n        }\n\n        // step 2: convert array into number of type T and return\n        std::memcpy(&result, vec.data(), sizeof(NumberType));\n        return true;\n    }\n\n    /*!\n    @brief create a string by reading characters from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format the current format (for diagnostics)\n    @param[in] len number of characters to read\n    @param[out] result string created by reading @a len bytes\n\n    @return whether string creation completed\n\n    @note We can not reserve @a len bytes for the result, because @a len\n          may be too large. Usually, @ref unexpect_eof() detects the end of\n          the input before we run out of string memory.\n    */\n    template<typename NumberType>\n    bool get_string(const input_format_t format,\n                    const NumberType len,\n                    string_t& result)\n    {\n        bool success = true;\n        std::generate_n(std::back_inserter(result), len, [this, &success, &format]()\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, \"string\")))\n            {\n                success = false;\n            }\n            return static_cast<char>(current);\n        });\n        return success;\n    }\n\n    /*!\n    @param[in] format   the current format (for diagnostics)\n    @param[in] context  further context information (for diagnostics)\n    @return whether the last read character is not EOF\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    bool unexpect_eof(const input_format_t format, const char* context) const\n    {\n        if (JSON_HEDLEY_UNLIKELY(current == std::char_traits<char>::eof()))\n        {\n            return sax->parse_error(chars_read, \"<end of file>\",\n                                    parse_error::create(110, chars_read, exception_message(format, \"unexpected end of input\", context)));\n        }\n        return true;\n    }\n\n    /*!\n    @return a string representation of the last read byte\n    */\n    std::string get_token_string() const\n    {\n        std::array<char, 3> cr{{}};\n        (std::snprintf)(cr.data(), cr.size(), \"%.2hhX\", static_cast<unsigned char>(current));\n        return std::string{cr.data()};\n    }\n\n    /*!\n    @param[in] format   the current format\n    @param[in] detail   a detailed error message\n    @param[in] context  further context information\n    @return a message string to use in the parse_error exceptions\n    */\n    std::string exception_message(const input_format_t format,\n                                  const std::string& detail,\n                                  const std::string& context) const\n    {\n        std::string error_msg = \"syntax error while parsing \";\n\n        switch (format)\n        {\n            case input_format_t::cbor:\n                error_msg += \"CBOR\";\n                break;\n\n            case input_format_t::msgpack:\n                error_msg += \"MessagePack\";\n                break;\n\n            case input_format_t::ubjson:\n                error_msg += \"UBJSON\";\n                break;\n\n            case input_format_t::bson:\n                error_msg += \"BSON\";\n                break;\n\n            default:            // LCOV_EXCL_LINE\n                assert(false);  // LCOV_EXCL_LINE\n        }\n\n        return error_msg + \" \" + context + \": \" + detail;\n    }\n\n  private:\n    /// input adapter\n    input_adapter_t ia = nullptr;\n\n    /// the current character\n    int current = std::char_traits<char>::eof();\n\n    /// the number of characters read\n    std::size_t chars_read = 0;\n\n    /// whether we can assume little endianess\n    const bool is_little_endian = little_endianess();\n\n    /// the SAX parser\n    json_sax_t* sax = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/input/input_adapters.hpp",
    "content": "#pragma once\n\n#include <array> // array\n#include <cassert> // assert\n#include <cstddef> // size_t\n#include <cstdio> //FILE *\n#include <cstring> // strlen\n#include <istream> // istream\n#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next\n#include <memory> // shared_ptr, make_shared, addressof\n#include <numeric> // accumulate\n#include <string> // string, char_traits\n#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer\n#include <utility> // pair, declval\n\n#include <nlohmann/detail/iterators/iterator_traits.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// the supported input formats\nenum class input_format_t { json, cbor, msgpack, ubjson, bson };\n\n////////////////////\n// input adapters //\n////////////////////\n\n/*!\n@brief abstract input adapter interface\n\nProduces a stream of std::char_traits<char>::int_type characters from a\nstd::istream, a buffer, or some other input type. Accepts the return of\nexactly one non-EOF character for future input. The int_type characters\nreturned consist of all valid char values as positive values (typically\nunsigned char), plus an EOF value outside that range, specified by the value\nof the function std::char_traits<char>::eof(). This value is typically -1, but\ncould be any arbitrary value which is not a valid char value.\n*/\nstruct input_adapter_protocol\n{\n    /// get a character [0,255] or std::char_traits<char>::eof().\n    virtual std::char_traits<char>::int_type get_character() = 0;\n    virtual ~input_adapter_protocol() = default;\n};\n\n/// a type to simplify interfaces\nusing input_adapter_t = std::shared_ptr<input_adapter_protocol>;\n\n/*!\nInput adapter for stdio file access. This adapter read only 1 byte and do not use any\n buffer. This adapter is a very low level adapter.\n*/\nclass file_input_adapter : public input_adapter_protocol\n{\n  public:\n    JSON_HEDLEY_NON_NULL(2)\n    explicit file_input_adapter(std::FILE* f)  noexcept\n        : m_file(f)\n    {}\n\n    // make class move-only\n    file_input_adapter(const file_input_adapter&) = delete;\n    file_input_adapter(file_input_adapter&&) = default;\n    file_input_adapter& operator=(const file_input_adapter&) = delete;\n    file_input_adapter& operator=(file_input_adapter&&) = default;\n    ~file_input_adapter() override = default;\n\n    std::char_traits<char>::int_type get_character() noexcept override\n    {\n        return std::fgetc(m_file);\n    }\n\n  private:\n    /// the file pointer to read from\n    std::FILE* m_file;\n};\n\n\n/*!\nInput adapter for a (caching) istream. Ignores a UFT Byte Order Mark at\nbeginning of input. Does not support changing the underlying std::streambuf\nin mid-input. Maintains underlying std::istream and std::streambuf to support\nsubsequent use of standard std::istream operations to process any input\ncharacters following those used in parsing the JSON input.  Clears the\nstd::istream flags; any input errors (e.g., EOF) will be detected by the first\nsubsequent call for input from the std::istream.\n*/\nclass input_stream_adapter : public input_adapter_protocol\n{\n  public:\n    ~input_stream_adapter() override\n    {\n        // clear stream flags; we use underlying streambuf I/O, do not\n        // maintain ifstream flags, except eof\n        is.clear(is.rdstate() & std::ios::eofbit);\n    }\n\n    explicit input_stream_adapter(std::istream& i)\n        : is(i), sb(*i.rdbuf())\n    {}\n\n    // delete because of pointer members\n    input_stream_adapter(const input_stream_adapter&) = delete;\n    input_stream_adapter& operator=(input_stream_adapter&) = delete;\n    input_stream_adapter(input_stream_adapter&&) = delete;\n    input_stream_adapter& operator=(input_stream_adapter&&) = delete;\n\n    // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to\n    // ensure that std::char_traits<char>::eof() and the character 0xFF do not\n    // end up as the same value, eg. 0xFFFFFFFF.\n    std::char_traits<char>::int_type get_character() override\n    {\n        auto res = sb.sbumpc();\n        // set eof manually, as we don't use the istream interface.\n        if (res == EOF)\n        {\n            is.clear(is.rdstate() | std::ios::eofbit);\n        }\n        return res;\n    }\n\n  private:\n    /// the associated input stream\n    std::istream& is;\n    std::streambuf& sb;\n};\n\n/// input adapter for buffer input\nclass input_buffer_adapter : public input_adapter_protocol\n{\n  public:\n    input_buffer_adapter(const char* b, const std::size_t l) noexcept\n        : cursor(b), limit(b == nullptr ? nullptr : (b + l))\n    {}\n\n    // delete because of pointer members\n    input_buffer_adapter(const input_buffer_adapter&) = delete;\n    input_buffer_adapter& operator=(input_buffer_adapter&) = delete;\n    input_buffer_adapter(input_buffer_adapter&&) = delete;\n    input_buffer_adapter& operator=(input_buffer_adapter&&) = delete;\n    ~input_buffer_adapter() override = default;\n\n    std::char_traits<char>::int_type get_character() noexcept override\n    {\n        if (JSON_HEDLEY_LIKELY(cursor < limit))\n        {\n            assert(cursor != nullptr and limit != nullptr);\n            return std::char_traits<char>::to_int_type(*(cursor++));\n        }\n\n        return std::char_traits<char>::eof();\n    }\n\n  private:\n    /// pointer to the current character\n    const char* cursor;\n    /// pointer past the last character\n    const char* const limit;\n};\n\ntemplate<typename WideStringType, size_t T>\nstruct wide_string_input_helper\n{\n    // UTF-32\n    static void fill_buffer(const WideStringType& str,\n                            size_t& current_wchar,\n                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,\n                            size_t& utf8_bytes_index,\n                            size_t& utf8_bytes_filled)\n    {\n        utf8_bytes_index = 0;\n\n        if (current_wchar == str.size())\n        {\n            utf8_bytes[0] = std::char_traits<char>::eof();\n            utf8_bytes_filled = 1;\n        }\n        else\n        {\n            // get the current character\n            const auto wc = static_cast<unsigned int>(str[current_wchar++]);\n\n            // UTF-32 to UTF-8 encoding\n            if (wc < 0x80)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n            else if (wc <= 0x7FF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((wc >> 6u) & 0x1Fu));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (wc & 0x3Fu));\n                utf8_bytes_filled = 2;\n            }\n            else if (wc <= 0xFFFF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((wc >> 12u) & 0x0Fu));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((wc >> 6u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (wc & 0x3Fu));\n                utf8_bytes_filled = 3;\n            }\n            else if (wc <= 0x10FFFF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((wc >> 18u) & 0x07u));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((wc >> 12u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((wc >> 6u) & 0x3Fu));\n                utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (wc & 0x3Fu));\n                utf8_bytes_filled = 4;\n            }\n            else\n            {\n                // unknown character\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n        }\n    }\n};\n\ntemplate<typename WideStringType>\nstruct wide_string_input_helper<WideStringType, 2>\n{\n    // UTF-16\n    static void fill_buffer(const WideStringType& str,\n                            size_t& current_wchar,\n                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,\n                            size_t& utf8_bytes_index,\n                            size_t& utf8_bytes_filled)\n    {\n        utf8_bytes_index = 0;\n\n        if (current_wchar == str.size())\n        {\n            utf8_bytes[0] = std::char_traits<char>::eof();\n            utf8_bytes_filled = 1;\n        }\n        else\n        {\n            // get the current character\n            const auto wc = static_cast<unsigned int>(str[current_wchar++]);\n\n            // UTF-16 to UTF-8 encoding\n            if (wc < 0x80)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n            else if (wc <= 0x7FF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((wc >> 6u)));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (wc & 0x3Fu));\n                utf8_bytes_filled = 2;\n            }\n            else if (0xD800 > wc or wc >= 0xE000)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((wc >> 12u)));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((wc >> 6u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (wc & 0x3Fu));\n                utf8_bytes_filled = 3;\n            }\n            else\n            {\n                if (current_wchar < str.size())\n                {\n                    const auto wc2 = static_cast<unsigned int>(str[current_wchar++]);\n                    const auto charcode = 0x10000u + (((wc & 0x3FFu) << 10u) | (wc2 & 0x3FFu));\n                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));\n                    utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));\n                    utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));\n                    utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));\n                    utf8_bytes_filled = 4;\n                }\n                else\n                {\n                    // unknown character\n                    ++current_wchar;\n                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                    utf8_bytes_filled = 1;\n                }\n            }\n        }\n    }\n};\n\ntemplate<typename WideStringType>\nclass wide_string_input_adapter : public input_adapter_protocol\n{\n  public:\n    explicit wide_string_input_adapter(const WideStringType& w) noexcept\n        : str(w)\n    {}\n\n    std::char_traits<char>::int_type get_character() noexcept override\n    {\n        // check if buffer needs to be filled\n        if (utf8_bytes_index == utf8_bytes_filled)\n        {\n            fill_buffer<sizeof(typename WideStringType::value_type)>();\n\n            assert(utf8_bytes_filled > 0);\n            assert(utf8_bytes_index == 0);\n        }\n\n        // use buffer\n        assert(utf8_bytes_filled > 0);\n        assert(utf8_bytes_index < utf8_bytes_filled);\n        return utf8_bytes[utf8_bytes_index++];\n    }\n\n  private:\n    template<size_t T>\n    void fill_buffer()\n    {\n        wide_string_input_helper<WideStringType, T>::fill_buffer(str, current_wchar, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);\n    }\n\n    /// the wstring to process\n    const WideStringType& str;\n\n    /// index of the current wchar in str\n    std::size_t current_wchar = 0;\n\n    /// a buffer for UTF-8 bytes\n    std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};\n\n    /// index to the utf8_codes array for the next valid byte\n    std::size_t utf8_bytes_index = 0;\n    /// number of valid bytes in the utf8_codes array\n    std::size_t utf8_bytes_filled = 0;\n};\n\nclass input_adapter\n{\n  public:\n    // native support\n    JSON_HEDLEY_NON_NULL(2)\n    input_adapter(std::FILE* file)\n        : ia(std::make_shared<file_input_adapter>(file)) {}\n    /// input adapter for input stream\n    input_adapter(std::istream& i)\n        : ia(std::make_shared<input_stream_adapter>(i)) {}\n\n    /// input adapter for input stream\n    input_adapter(std::istream&& i)\n        : ia(std::make_shared<input_stream_adapter>(i)) {}\n\n    input_adapter(const std::wstring& ws)\n        : ia(std::make_shared<wide_string_input_adapter<std::wstring>>(ws)) {}\n\n    input_adapter(const std::u16string& ws)\n        : ia(std::make_shared<wide_string_input_adapter<std::u16string>>(ws)) {}\n\n    input_adapter(const std::u32string& ws)\n        : ia(std::make_shared<wide_string_input_adapter<std::u32string>>(ws)) {}\n\n    /// input adapter for buffer\n    template<typename CharT,\n             typename std::enable_if<\n                 std::is_pointer<CharT>::value and\n                 std::is_integral<typename std::remove_pointer<CharT>::type>::value and\n                 sizeof(typename std::remove_pointer<CharT>::type) == 1,\n                 int>::type = 0>\n    input_adapter(CharT b, std::size_t l)\n        : ia(std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(b), l)) {}\n\n    // derived support\n\n    /// input adapter for string literal\n    template<typename CharT,\n             typename std::enable_if<\n                 std::is_pointer<CharT>::value and\n                 std::is_integral<typename std::remove_pointer<CharT>::type>::value and\n                 sizeof(typename std::remove_pointer<CharT>::type) == 1,\n                 int>::type = 0>\n    input_adapter(CharT b)\n        : input_adapter(reinterpret_cast<const char*>(b),\n                        std::strlen(reinterpret_cast<const char*>(b))) {}\n\n    /// input adapter for iterator range with contiguous storage\n    template<class IteratorType,\n             typename std::enable_if<\n                 std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,\n                 int>::type = 0>\n    input_adapter(IteratorType first, IteratorType last)\n    {\n#ifndef NDEBUG\n        // assertion to check that the iterator range is indeed contiguous,\n        // see http://stackoverflow.com/a/35008842/266378 for more discussion\n        const auto is_contiguous = std::accumulate(\n                                       first, last, std::pair<bool, int>(true, 0),\n                                       [&first](std::pair<bool, int> res, decltype(*first) val)\n        {\n            res.first &= (val == *(std::next(std::addressof(*first), res.second++)));\n            return res;\n        }).first;\n        assert(is_contiguous);\n#endif\n\n        // assertion to check that each element is 1 byte long\n        static_assert(\n            sizeof(typename iterator_traits<IteratorType>::value_type) == 1,\n            \"each element in the iterator range must have the size of 1 byte\");\n\n        const auto len = static_cast<size_t>(std::distance(first, last));\n        if (JSON_HEDLEY_LIKELY(len > 0))\n        {\n            // there is at least one element: use the address of first\n            ia = std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(&(*first)), len);\n        }\n        else\n        {\n            // the address of first cannot be used: use nullptr\n            ia = std::make_shared<input_buffer_adapter>(nullptr, len);\n        }\n    }\n\n    /// input adapter for array\n    template<class T, std::size_t N>\n    input_adapter(T (&array)[N])\n        : input_adapter(std::begin(array), std::end(array)) {}\n\n    /// input adapter for contiguous container\n    template<class ContiguousContainer, typename\n             std::enable_if<not std::is_pointer<ContiguousContainer>::value and\n                            std::is_base_of<std::random_access_iterator_tag, typename iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value,\n                            int>::type = 0>\n    input_adapter(const ContiguousContainer& c)\n        : input_adapter(std::begin(c), std::end(c)) {}\n\n    operator input_adapter_t()\n    {\n        return ia;\n    }\n\n  private:\n    /// the actual adapter\n    input_adapter_t ia = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/input/json_sax.hpp",
    "content": "#pragma once\n\n#include <cassert> // assert\n#include <cstddef>\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n#include <nlohmann/detail/exceptions.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n\nnamespace nlohmann\n{\n\n/*!\n@brief SAX interface\n\nThis class describes the SAX interface used by @ref nlohmann::json::sax_parse.\nEach function is called in different situations while the input is parsed. The\nboolean return value informs the parser whether to continue processing the\ninput.\n*/\ntemplate<typename BasicJsonType>\nstruct json_sax\n{\n    /// type for (signed) integers\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    /// type for unsigned integers\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    /// type for floating-point numbers\n    using number_float_t = typename BasicJsonType::number_float_t;\n    /// type for strings\n    using string_t = typename BasicJsonType::string_t;\n\n    /*!\n    @brief a null value was read\n    @return whether parsing should proceed\n    */\n    virtual bool null() = 0;\n\n    /*!\n    @brief a boolean value was read\n    @param[in] val  boolean value\n    @return whether parsing should proceed\n    */\n    virtual bool boolean(bool val) = 0;\n\n    /*!\n    @brief an integer number was read\n    @param[in] val  integer value\n    @return whether parsing should proceed\n    */\n    virtual bool number_integer(number_integer_t val) = 0;\n\n    /*!\n    @brief an unsigned integer number was read\n    @param[in] val  unsigned integer value\n    @return whether parsing should proceed\n    */\n    virtual bool number_unsigned(number_unsigned_t val) = 0;\n\n    /*!\n    @brief an floating-point number was read\n    @param[in] val  floating-point value\n    @param[in] s    raw token value\n    @return whether parsing should proceed\n    */\n    virtual bool number_float(number_float_t val, const string_t& s) = 0;\n\n    /*!\n    @brief a string was read\n    @param[in] val  string value\n    @return whether parsing should proceed\n    @note It is safe to move the passed string.\n    */\n    virtual bool string(string_t& val) = 0;\n\n    /*!\n    @brief the beginning of an object was read\n    @param[in] elements  number of object elements or -1 if unknown\n    @return whether parsing should proceed\n    @note binary formats may report the number of elements\n    */\n    virtual bool start_object(std::size_t elements) = 0;\n\n    /*!\n    @brief an object key was read\n    @param[in] val  object key\n    @return whether parsing should proceed\n    @note It is safe to move the passed string.\n    */\n    virtual bool key(string_t& val) = 0;\n\n    /*!\n    @brief the end of an object was read\n    @return whether parsing should proceed\n    */\n    virtual bool end_object() = 0;\n\n    /*!\n    @brief the beginning of an array was read\n    @param[in] elements  number of array elements or -1 if unknown\n    @return whether parsing should proceed\n    @note binary formats may report the number of elements\n    */\n    virtual bool start_array(std::size_t elements) = 0;\n\n    /*!\n    @brief the end of an array was read\n    @return whether parsing should proceed\n    */\n    virtual bool end_array() = 0;\n\n    /*!\n    @brief a parse error occurred\n    @param[in] position    the position in the input where the error occurs\n    @param[in] last_token  the last read token\n    @param[in] ex          an exception object describing the error\n    @return whether parsing should proceed (must return false)\n    */\n    virtual bool parse_error(std::size_t position,\n                             const std::string& last_token,\n                             const detail::exception& ex) = 0;\n\n    virtual ~json_sax() = default;\n};\n\n\nnamespace detail\n{\n/*!\n@brief SAX implementation to create a JSON value from SAX events\n\nThis class implements the @ref json_sax interface and processes the SAX events\nto create a JSON value which makes it basically a DOM parser. The structure or\nhierarchy of the JSON value is managed by the stack `ref_stack` which contains\na pointer to the respective array or object for each recursion depth.\n\nAfter successful parsing, the value that is passed by reference to the\nconstructor contains the parsed value.\n\n@tparam BasicJsonType  the JSON type\n*/\ntemplate<typename BasicJsonType>\nclass json_sax_dom_parser\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n\n    /*!\n    @param[in, out] r  reference to a JSON value that is manipulated while\n                       parsing\n    @param[in] allow_exceptions_  whether parse errors yield exceptions\n    */\n    explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)\n        : root(r), allow_exceptions(allow_exceptions_)\n    {}\n\n    // make class move-only\n    json_sax_dom_parser(const json_sax_dom_parser&) = delete;\n    json_sax_dom_parser(json_sax_dom_parser&&) = default;\n    json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete;\n    json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default;\n    ~json_sax_dom_parser() = default;\n\n    bool null()\n    {\n        handle_value(nullptr);\n        return true;\n    }\n\n    bool boolean(bool val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_integer(number_integer_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_float(number_float_t val, const string_t& /*unused*/)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool string(string_t& val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool start_object(std::size_t len)\n    {\n        ref_stack.push_back(handle_value(BasicJsonType::value_t::object));\n\n        if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408,\n                                            \"excessive object size: \" + std::to_string(len)));\n        }\n\n        return true;\n    }\n\n    bool key(string_t& val)\n    {\n        // add null at given key and store the reference for later\n        object_element = &(ref_stack.back()->m_value.object->operator[](val));\n        return true;\n    }\n\n    bool end_object()\n    {\n        ref_stack.pop_back();\n        return true;\n    }\n\n    bool start_array(std::size_t len)\n    {\n        ref_stack.push_back(handle_value(BasicJsonType::value_t::array));\n\n        if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408,\n                                            \"excessive array size: \" + std::to_string(len)));\n        }\n\n        return true;\n    }\n\n    bool end_array()\n    {\n        ref_stack.pop_back();\n        return true;\n    }\n\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,\n                     const detail::exception& ex)\n    {\n        errored = true;\n        if (allow_exceptions)\n        {\n            // determine the proper exception type from the id\n            switch ((ex.id / 100) % 100)\n            {\n                case 1:\n                    JSON_THROW(*static_cast<const detail::parse_error*>(&ex));\n                case 4:\n                    JSON_THROW(*static_cast<const detail::out_of_range*>(&ex));\n                // LCOV_EXCL_START\n                case 2:\n                    JSON_THROW(*static_cast<const detail::invalid_iterator*>(&ex));\n                case 3:\n                    JSON_THROW(*static_cast<const detail::type_error*>(&ex));\n                case 5:\n                    JSON_THROW(*static_cast<const detail::other_error*>(&ex));\n                default:\n                    assert(false);\n                    // LCOV_EXCL_STOP\n            }\n        }\n        return false;\n    }\n\n    constexpr bool is_errored() const\n    {\n        return errored;\n    }\n\n  private:\n    /*!\n    @invariant If the ref stack is empty, then the passed value will be the new\n               root.\n    @invariant If the ref stack contains a value, then it is an array or an\n               object to which we can add elements\n    */\n    template<typename Value>\n    JSON_HEDLEY_RETURNS_NON_NULL\n    BasicJsonType* handle_value(Value&& v)\n    {\n        if (ref_stack.empty())\n        {\n            root = BasicJsonType(std::forward<Value>(v));\n            return &root;\n        }\n\n        assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());\n\n        if (ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v));\n            return &(ref_stack.back()->m_value.array->back());\n        }\n\n        assert(ref_stack.back()->is_object());\n        assert(object_element);\n        *object_element = BasicJsonType(std::forward<Value>(v));\n        return object_element;\n    }\n\n    /// the parsed JSON value\n    BasicJsonType& root;\n    /// stack to model hierarchy of values\n    std::vector<BasicJsonType*> ref_stack {};\n    /// helper to hold the reference for the next object element\n    BasicJsonType* object_element = nullptr;\n    /// whether a syntax error occurred\n    bool errored = false;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n};\n\ntemplate<typename BasicJsonType>\nclass json_sax_dom_callback_parser\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using parser_callback_t = typename BasicJsonType::parser_callback_t;\n    using parse_event_t = typename BasicJsonType::parse_event_t;\n\n    json_sax_dom_callback_parser(BasicJsonType& r,\n                                 const parser_callback_t cb,\n                                 const bool allow_exceptions_ = true)\n        : root(r), callback(cb), allow_exceptions(allow_exceptions_)\n    {\n        keep_stack.push_back(true);\n    }\n\n    // make class move-only\n    json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete;\n    json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default;\n    json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete;\n    json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default;\n    ~json_sax_dom_callback_parser() = default;\n\n    bool null()\n    {\n        handle_value(nullptr);\n        return true;\n    }\n\n    bool boolean(bool val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_integer(number_integer_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_float(number_float_t val, const string_t& /*unused*/)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool string(string_t& val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool start_object(std::size_t len)\n    {\n        // check callback for object start\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);\n        keep_stack.push_back(keep);\n\n        auto val = handle_value(BasicJsonType::value_t::object, true);\n        ref_stack.push_back(val.second);\n\n        // check object limit\n        if (ref_stack.back() and JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, \"excessive object size: \" + std::to_string(len)));\n        }\n\n        return true;\n    }\n\n    bool key(string_t& val)\n    {\n        BasicJsonType k = BasicJsonType(val);\n\n        // check callback for key\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);\n        key_keep_stack.push_back(keep);\n\n        // add discarded value at given key and store the reference for later\n        if (keep and ref_stack.back())\n        {\n            object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);\n        }\n\n        return true;\n    }\n\n    bool end_object()\n    {\n        if (ref_stack.back() and not callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))\n        {\n            // discard object\n            *ref_stack.back() = discarded;\n        }\n\n        assert(not ref_stack.empty());\n        assert(not keep_stack.empty());\n        ref_stack.pop_back();\n        keep_stack.pop_back();\n\n        if (not ref_stack.empty() and ref_stack.back() and ref_stack.back()->is_object())\n        {\n            // remove discarded value\n            for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)\n            {\n                if (it->is_discarded())\n                {\n                    ref_stack.back()->erase(it);\n                    break;\n                }\n            }\n        }\n\n        return true;\n    }\n\n    bool start_array(std::size_t len)\n    {\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);\n        keep_stack.push_back(keep);\n\n        auto val = handle_value(BasicJsonType::value_t::array, true);\n        ref_stack.push_back(val.second);\n\n        // check array limit\n        if (ref_stack.back() and JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, \"excessive array size: \" + std::to_string(len)));\n        }\n\n        return true;\n    }\n\n    bool end_array()\n    {\n        bool keep = true;\n\n        if (ref_stack.back())\n        {\n            keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());\n            if (not keep)\n            {\n                // discard array\n                *ref_stack.back() = discarded;\n            }\n        }\n\n        assert(not ref_stack.empty());\n        assert(not keep_stack.empty());\n        ref_stack.pop_back();\n        keep_stack.pop_back();\n\n        // remove discarded value\n        if (not keep and not ref_stack.empty() and ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->pop_back();\n        }\n\n        return true;\n    }\n\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,\n                     const detail::exception& ex)\n    {\n        errored = true;\n        if (allow_exceptions)\n        {\n            // determine the proper exception type from the id\n            switch ((ex.id / 100) % 100)\n            {\n                case 1:\n                    JSON_THROW(*static_cast<const detail::parse_error*>(&ex));\n                case 4:\n                    JSON_THROW(*static_cast<const detail::out_of_range*>(&ex));\n                // LCOV_EXCL_START\n                case 2:\n                    JSON_THROW(*static_cast<const detail::invalid_iterator*>(&ex));\n                case 3:\n                    JSON_THROW(*static_cast<const detail::type_error*>(&ex));\n                case 5:\n                    JSON_THROW(*static_cast<const detail::other_error*>(&ex));\n                default:\n                    assert(false);\n                    // LCOV_EXCL_STOP\n            }\n        }\n        return false;\n    }\n\n    constexpr bool is_errored() const\n    {\n        return errored;\n    }\n\n  private:\n    /*!\n    @param[in] v  value to add to the JSON value we build during parsing\n    @param[in] skip_callback  whether we should skip calling the callback\n               function; this is required after start_array() and\n               start_object() SAX events, because otherwise we would call the\n               callback function with an empty array or object, respectively.\n\n    @invariant If the ref stack is empty, then the passed value will be the new\n               root.\n    @invariant If the ref stack contains a value, then it is an array or an\n               object to which we can add elements\n\n    @return pair of boolean (whether value should be kept) and pointer (to the\n            passed value in the ref_stack hierarchy; nullptr if not kept)\n    */\n    template<typename Value>\n    std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)\n    {\n        assert(not keep_stack.empty());\n\n        // do not handle this value if we know it would be added to a discarded\n        // container\n        if (not keep_stack.back())\n        {\n            return {false, nullptr};\n        }\n\n        // create value\n        auto value = BasicJsonType(std::forward<Value>(v));\n\n        // check callback\n        const bool keep = skip_callback or callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);\n\n        // do not handle this value if we just learnt it shall be discarded\n        if (not keep)\n        {\n            return {false, nullptr};\n        }\n\n        if (ref_stack.empty())\n        {\n            root = std::move(value);\n            return {true, &root};\n        }\n\n        // skip this value if we already decided to skip the parent\n        // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)\n        if (not ref_stack.back())\n        {\n            return {false, nullptr};\n        }\n\n        // we now only expect arrays and objects\n        assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());\n\n        // array\n        if (ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->push_back(std::move(value));\n            return {true, &(ref_stack.back()->m_value.array->back())};\n        }\n\n        // object\n        assert(ref_stack.back()->is_object());\n        // check if we should store an element for the current key\n        assert(not key_keep_stack.empty());\n        const bool store_element = key_keep_stack.back();\n        key_keep_stack.pop_back();\n\n        if (not store_element)\n        {\n            return {false, nullptr};\n        }\n\n        assert(object_element);\n        *object_element = std::move(value);\n        return {true, object_element};\n    }\n\n    /// the parsed JSON value\n    BasicJsonType& root;\n    /// stack to model hierarchy of values\n    std::vector<BasicJsonType*> ref_stack {};\n    /// stack to manage which values to keep\n    std::vector<bool> keep_stack {};\n    /// stack to manage which object keys to keep\n    std::vector<bool> key_keep_stack {};\n    /// helper to hold the reference for the next object element\n    BasicJsonType* object_element = nullptr;\n    /// whether a syntax error occurred\n    bool errored = false;\n    /// callback function\n    const parser_callback_t callback = nullptr;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n    /// a discarded value for the callback\n    BasicJsonType discarded = BasicJsonType::value_t::discarded;\n};\n\ntemplate<typename BasicJsonType>\nclass json_sax_acceptor\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n\n    bool null()\n    {\n        return true;\n    }\n\n    bool boolean(bool /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_integer(number_integer_t /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool string(string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool start_object(std::size_t  /*unused*/ = std::size_t(-1))\n    {\n        return true;\n    }\n\n    bool key(string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool end_object()\n    {\n        return true;\n    }\n\n    bool start_array(std::size_t  /*unused*/ = std::size_t(-1))\n    {\n        return true;\n    }\n\n    bool end_array()\n    {\n        return true;\n    }\n\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)\n    {\n        return false;\n    }\n};\n}  // namespace detail\n\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/input/lexer.hpp",
    "content": "#pragma once\n\n#include <array> // array\n#include <clocale> // localeconv\n#include <cstddef> // size_t\n#include <cstdio> // snprintf\n#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull\n#include <initializer_list> // initializer_list\n#include <string> // char_traits, string\n#include <utility> // move\n#include <vector> // vector\n\n#include <nlohmann/detail/input/input_adapters.hpp>\n#include <nlohmann/detail/input/position_t.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////\n// lexer //\n///////////\n\n/*!\n@brief lexical analysis\n\nThis class organizes the lexical analysis during JSON deserialization.\n*/\ntemplate<typename BasicJsonType>\nclass lexer\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n\n  public:\n    /// token types for the parser\n    enum class token_type\n    {\n        uninitialized,    ///< indicating the scanner is uninitialized\n        literal_true,     ///< the `true` literal\n        literal_false,    ///< the `false` literal\n        literal_null,     ///< the `null` literal\n        value_string,     ///< a string -- use get_string() for actual value\n        value_unsigned,   ///< an unsigned integer -- use get_number_unsigned() for actual value\n        value_integer,    ///< a signed integer -- use get_number_integer() for actual value\n        value_float,      ///< an floating point number -- use get_number_float() for actual value\n        begin_array,      ///< the character for array begin `[`\n        begin_object,     ///< the character for object begin `{`\n        end_array,        ///< the character for array end `]`\n        end_object,       ///< the character for object end `}`\n        name_separator,   ///< the name separator `:`\n        value_separator,  ///< the value separator `,`\n        parse_error,      ///< indicating a parse error\n        end_of_input,     ///< indicating the end of the input buffer\n        literal_or_value  ///< a literal or the begin of a value (only for diagnostics)\n    };\n\n    /// return name of values of type token_type (only used for errors)\n    JSON_HEDLEY_RETURNS_NON_NULL\n    JSON_HEDLEY_CONST\n    static const char* token_type_name(const token_type t) noexcept\n    {\n        switch (t)\n        {\n            case token_type::uninitialized:\n                return \"<uninitialized>\";\n            case token_type::literal_true:\n                return \"true literal\";\n            case token_type::literal_false:\n                return \"false literal\";\n            case token_type::literal_null:\n                return \"null literal\";\n            case token_type::value_string:\n                return \"string literal\";\n            case lexer::token_type::value_unsigned:\n            case lexer::token_type::value_integer:\n            case lexer::token_type::value_float:\n                return \"number literal\";\n            case token_type::begin_array:\n                return \"'['\";\n            case token_type::begin_object:\n                return \"'{'\";\n            case token_type::end_array:\n                return \"']'\";\n            case token_type::end_object:\n                return \"'}'\";\n            case token_type::name_separator:\n                return \"':'\";\n            case token_type::value_separator:\n                return \"','\";\n            case token_type::parse_error:\n                return \"<parse error>\";\n            case token_type::end_of_input:\n                return \"end of input\";\n            case token_type::literal_or_value:\n                return \"'[', '{', or a literal\";\n            // LCOV_EXCL_START\n            default: // catch non-enum values\n                return \"unknown token\";\n                // LCOV_EXCL_STOP\n        }\n    }\n\n    explicit lexer(detail::input_adapter_t&& adapter)\n        : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {}\n\n    // delete because of pointer members\n    lexer(const lexer&) = delete;\n    lexer(lexer&&) = delete;\n    lexer& operator=(lexer&) = delete;\n    lexer& operator=(lexer&&) = delete;\n    ~lexer() = default;\n\n  private:\n    /////////////////////\n    // locales\n    /////////////////////\n\n    /// return the locale-dependent decimal point\n    JSON_HEDLEY_PURE\n    static char get_decimal_point() noexcept\n    {\n        const auto loc = localeconv();\n        assert(loc != nullptr);\n        return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);\n    }\n\n    /////////////////////\n    // scan functions\n    /////////////////////\n\n    /*!\n    @brief get codepoint from 4 hex characters following `\\u`\n\n    For input \"\\u c1 c2 c3 c4\" the codepoint is:\n      (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4\n    = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)\n\n    Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'\n    must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The\n    conversion is done by subtracting the offset (0x30, 0x37, and 0x57)\n    between the ASCII value of the character and the desired integer value.\n\n    @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or\n            non-hex character)\n    */\n    int get_codepoint()\n    {\n        // this function only makes sense after reading `\\u`\n        assert(current == 'u');\n        int codepoint = 0;\n\n        const auto factors = { 12u, 8u, 4u, 0u };\n        for (const auto factor : factors)\n        {\n            get();\n\n            if (current >= '0' and current <= '9')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x30u) << factor);\n            }\n            else if (current >= 'A' and current <= 'F')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x37u) << factor);\n            }\n            else if (current >= 'a' and current <= 'f')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x57u) << factor);\n            }\n            else\n            {\n                return -1;\n            }\n        }\n\n        assert(0x0000 <= codepoint and codepoint <= 0xFFFF);\n        return codepoint;\n    }\n\n    /*!\n    @brief check if the next byte(s) are inside a given range\n\n    Adds the current byte and, for each passed range, reads a new byte and\n    checks if it is inside the range. If a violation was detected, set up an\n    error message and return false. Otherwise, return true.\n\n    @param[in] ranges  list of integers; interpreted as list of pairs of\n                       inclusive lower and upper bound, respectively\n\n    @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,\n         1, 2, or 3 pairs. This precondition is enforced by an assertion.\n\n    @return true if and only if no range violation was detected\n    */\n    bool next_byte_in_range(std::initializer_list<int> ranges)\n    {\n        assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6);\n        add(current);\n\n        for (auto range = ranges.begin(); range != ranges.end(); ++range)\n        {\n            get();\n            if (JSON_HEDLEY_LIKELY(*range <= current and current <= *(++range)))\n            {\n                add(current);\n            }\n            else\n            {\n                error_message = \"invalid string: ill-formed UTF-8 byte\";\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief scan a string literal\n\n    This function scans a string according to Sect. 7 of RFC 7159. While\n    scanning, bytes are escaped and copied into buffer token_buffer. Then the\n    function returns successfully, token_buffer is *not* null-terminated (as it\n    may contain \\0 bytes), and token_buffer.size() is the number of bytes in the\n    string.\n\n    @return token_type::value_string if string could be successfully scanned,\n            token_type::parse_error otherwise\n\n    @note In case of errors, variable error_message contains a textual\n          description.\n    */\n    token_type scan_string()\n    {\n        // reset token_buffer (ignore opening quote)\n        reset();\n\n        // we entered the function by reading an open quote\n        assert(current == '\\\"');\n\n        while (true)\n        {\n            // get next character\n            switch (get())\n            {\n                // end of file while parsing string\n                case std::char_traits<char>::eof():\n                {\n                    error_message = \"invalid string: missing closing quote\";\n                    return token_type::parse_error;\n                }\n\n                // closing quote\n                case '\\\"':\n                {\n                    return token_type::value_string;\n                }\n\n                // escapes\n                case '\\\\':\n                {\n                    switch (get())\n                    {\n                        // quotation mark\n                        case '\\\"':\n                            add('\\\"');\n                            break;\n                        // reverse solidus\n                        case '\\\\':\n                            add('\\\\');\n                            break;\n                        // solidus\n                        case '/':\n                            add('/');\n                            break;\n                        // backspace\n                        case 'b':\n                            add('\\b');\n                            break;\n                        // form feed\n                        case 'f':\n                            add('\\f');\n                            break;\n                        // line feed\n                        case 'n':\n                            add('\\n');\n                            break;\n                        // carriage return\n                        case 'r':\n                            add('\\r');\n                            break;\n                        // tab\n                        case 't':\n                            add('\\t');\n                            break;\n\n                        // unicode escapes\n                        case 'u':\n                        {\n                            const int codepoint1 = get_codepoint();\n                            int codepoint = codepoint1; // start with codepoint1\n\n                            if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1))\n                            {\n                                error_message = \"invalid string: '\\\\u' must be followed by 4 hex digits\";\n                                return token_type::parse_error;\n                            }\n\n                            // check if code point is a high surrogate\n                            if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF)\n                            {\n                                // expect next \\uxxxx entry\n                                if (JSON_HEDLEY_LIKELY(get() == '\\\\' and get() == 'u'))\n                                {\n                                    const int codepoint2 = get_codepoint();\n\n                                    if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1))\n                                    {\n                                        error_message = \"invalid string: '\\\\u' must be followed by 4 hex digits\";\n                                        return token_type::parse_error;\n                                    }\n\n                                    // check if codepoint2 is a low surrogate\n                                    if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF))\n                                    {\n                                        // overwrite codepoint\n                                        codepoint = static_cast<int>(\n                                                        // high surrogate occupies the most significant 22 bits\n                                                        (static_cast<unsigned int>(codepoint1) << 10u)\n                                                        // low surrogate occupies the least significant 15 bits\n                                                        + static_cast<unsigned int>(codepoint2)\n                                                        // there is still the 0xD800, 0xDC00 and 0x10000 noise\n                                                        // in the result so we have to subtract with:\n                                                        // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00\n                                                        - 0x35FDC00u);\n                                    }\n                                    else\n                                    {\n                                        error_message = \"invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF\";\n                                        return token_type::parse_error;\n                                    }\n                                }\n                                else\n                                {\n                                    error_message = \"invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF\";\n                                    return token_type::parse_error;\n                                }\n                            }\n                            else\n                            {\n                                if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF))\n                                {\n                                    error_message = \"invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF\";\n                                    return token_type::parse_error;\n                                }\n                            }\n\n                            // result of the above calculation yields a proper codepoint\n                            assert(0x00 <= codepoint and codepoint <= 0x10FFFF);\n\n                            // translate codepoint into bytes\n                            if (codepoint < 0x80)\n                            {\n                                // 1-byte characters: 0xxxxxxx (ASCII)\n                                add(codepoint);\n                            }\n                            else if (codepoint <= 0x7FF)\n                            {\n                                // 2-byte characters: 110xxxxx 10xxxxxx\n                                add(static_cast<int>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u)));\n                                add(static_cast<int>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n                            else if (codepoint <= 0xFFFF)\n                            {\n                                // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx\n                                add(static_cast<int>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u)));\n                                add(static_cast<int>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));\n                                add(static_cast<int>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n                            else\n                            {\n                                // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx\n                                add(static_cast<int>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u)));\n                                add(static_cast<int>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu)));\n                                add(static_cast<int>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));\n                                add(static_cast<int>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n\n                            break;\n                        }\n\n                        // other characters after escape\n                        default:\n                            error_message = \"invalid string: forbidden character after backslash\";\n                            return token_type::parse_error;\n                    }\n\n                    break;\n                }\n\n                // invalid control characters\n                case 0x00:\n                {\n                    error_message = \"invalid string: control character U+0000 (NUL) must be escaped to \\\\u0000\";\n                    return token_type::parse_error;\n                }\n\n                case 0x01:\n                {\n                    error_message = \"invalid string: control character U+0001 (SOH) must be escaped to \\\\u0001\";\n                    return token_type::parse_error;\n                }\n\n                case 0x02:\n                {\n                    error_message = \"invalid string: control character U+0002 (STX) must be escaped to \\\\u0002\";\n                    return token_type::parse_error;\n                }\n\n                case 0x03:\n                {\n                    error_message = \"invalid string: control character U+0003 (ETX) must be escaped to \\\\u0003\";\n                    return token_type::parse_error;\n                }\n\n                case 0x04:\n                {\n                    error_message = \"invalid string: control character U+0004 (EOT) must be escaped to \\\\u0004\";\n                    return token_type::parse_error;\n                }\n\n                case 0x05:\n                {\n                    error_message = \"invalid string: control character U+0005 (ENQ) must be escaped to \\\\u0005\";\n                    return token_type::parse_error;\n                }\n\n                case 0x06:\n                {\n                    error_message = \"invalid string: control character U+0006 (ACK) must be escaped to \\\\u0006\";\n                    return token_type::parse_error;\n                }\n\n                case 0x07:\n                {\n                    error_message = \"invalid string: control character U+0007 (BEL) must be escaped to \\\\u0007\";\n                    return token_type::parse_error;\n                }\n\n                case 0x08:\n                {\n                    error_message = \"invalid string: control character U+0008 (BS) must be escaped to \\\\u0008 or \\\\b\";\n                    return token_type::parse_error;\n                }\n\n                case 0x09:\n                {\n                    error_message = \"invalid string: control character U+0009 (HT) must be escaped to \\\\u0009 or \\\\t\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0A:\n                {\n                    error_message = \"invalid string: control character U+000A (LF) must be escaped to \\\\u000A or \\\\n\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0B:\n                {\n                    error_message = \"invalid string: control character U+000B (VT) must be escaped to \\\\u000B\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0C:\n                {\n                    error_message = \"invalid string: control character U+000C (FF) must be escaped to \\\\u000C or \\\\f\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0D:\n                {\n                    error_message = \"invalid string: control character U+000D (CR) must be escaped to \\\\u000D or \\\\r\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0E:\n                {\n                    error_message = \"invalid string: control character U+000E (SO) must be escaped to \\\\u000E\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0F:\n                {\n                    error_message = \"invalid string: control character U+000F (SI) must be escaped to \\\\u000F\";\n                    return token_type::parse_error;\n                }\n\n                case 0x10:\n                {\n                    error_message = \"invalid string: control character U+0010 (DLE) must be escaped to \\\\u0010\";\n                    return token_type::parse_error;\n                }\n\n                case 0x11:\n                {\n                    error_message = \"invalid string: control character U+0011 (DC1) must be escaped to \\\\u0011\";\n                    return token_type::parse_error;\n                }\n\n                case 0x12:\n                {\n                    error_message = \"invalid string: control character U+0012 (DC2) must be escaped to \\\\u0012\";\n                    return token_type::parse_error;\n                }\n\n                case 0x13:\n                {\n                    error_message = \"invalid string: control character U+0013 (DC3) must be escaped to \\\\u0013\";\n                    return token_type::parse_error;\n                }\n\n                case 0x14:\n                {\n                    error_message = \"invalid string: control character U+0014 (DC4) must be escaped to \\\\u0014\";\n                    return token_type::parse_error;\n                }\n\n                case 0x15:\n                {\n                    error_message = \"invalid string: control character U+0015 (NAK) must be escaped to \\\\u0015\";\n                    return token_type::parse_error;\n                }\n\n                case 0x16:\n                {\n                    error_message = \"invalid string: control character U+0016 (SYN) must be escaped to \\\\u0016\";\n                    return token_type::parse_error;\n                }\n\n                case 0x17:\n                {\n                    error_message = \"invalid string: control character U+0017 (ETB) must be escaped to \\\\u0017\";\n                    return token_type::parse_error;\n                }\n\n                case 0x18:\n                {\n                    error_message = \"invalid string: control character U+0018 (CAN) must be escaped to \\\\u0018\";\n                    return token_type::parse_error;\n                }\n\n                case 0x19:\n                {\n                    error_message = \"invalid string: control character U+0019 (EM) must be escaped to \\\\u0019\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1A:\n                {\n                    error_message = \"invalid string: control character U+001A (SUB) must be escaped to \\\\u001A\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1B:\n                {\n                    error_message = \"invalid string: control character U+001B (ESC) must be escaped to \\\\u001B\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1C:\n                {\n                    error_message = \"invalid string: control character U+001C (FS) must be escaped to \\\\u001C\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1D:\n                {\n                    error_message = \"invalid string: control character U+001D (GS) must be escaped to \\\\u001D\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1E:\n                {\n                    error_message = \"invalid string: control character U+001E (RS) must be escaped to \\\\u001E\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1F:\n                {\n                    error_message = \"invalid string: control character U+001F (US) must be escaped to \\\\u001F\";\n                    return token_type::parse_error;\n                }\n\n                // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))\n                case 0x20:\n                case 0x21:\n                case 0x23:\n                case 0x24:\n                case 0x25:\n                case 0x26:\n                case 0x27:\n                case 0x28:\n                case 0x29:\n                case 0x2A:\n                case 0x2B:\n                case 0x2C:\n                case 0x2D:\n                case 0x2E:\n                case 0x2F:\n                case 0x30:\n                case 0x31:\n                case 0x32:\n                case 0x33:\n                case 0x34:\n                case 0x35:\n                case 0x36:\n                case 0x37:\n                case 0x38:\n                case 0x39:\n                case 0x3A:\n                case 0x3B:\n                case 0x3C:\n                case 0x3D:\n                case 0x3E:\n                case 0x3F:\n                case 0x40:\n                case 0x41:\n                case 0x42:\n                case 0x43:\n                case 0x44:\n                case 0x45:\n                case 0x46:\n                case 0x47:\n                case 0x48:\n                case 0x49:\n                case 0x4A:\n                case 0x4B:\n                case 0x4C:\n                case 0x4D:\n                case 0x4E:\n                case 0x4F:\n                case 0x50:\n                case 0x51:\n                case 0x52:\n                case 0x53:\n                case 0x54:\n                case 0x55:\n                case 0x56:\n                case 0x57:\n                case 0x58:\n                case 0x59:\n                case 0x5A:\n                case 0x5B:\n                case 0x5D:\n                case 0x5E:\n                case 0x5F:\n                case 0x60:\n                case 0x61:\n                case 0x62:\n                case 0x63:\n                case 0x64:\n                case 0x65:\n                case 0x66:\n                case 0x67:\n                case 0x68:\n                case 0x69:\n                case 0x6A:\n                case 0x6B:\n                case 0x6C:\n                case 0x6D:\n                case 0x6E:\n                case 0x6F:\n                case 0x70:\n                case 0x71:\n                case 0x72:\n                case 0x73:\n                case 0x74:\n                case 0x75:\n                case 0x76:\n                case 0x77:\n                case 0x78:\n                case 0x79:\n                case 0x7A:\n                case 0x7B:\n                case 0x7C:\n                case 0x7D:\n                case 0x7E:\n                case 0x7F:\n                {\n                    add(current);\n                    break;\n                }\n\n                // U+0080..U+07FF: bytes C2..DF 80..BF\n                case 0xC2:\n                case 0xC3:\n                case 0xC4:\n                case 0xC5:\n                case 0xC6:\n                case 0xC7:\n                case 0xC8:\n                case 0xC9:\n                case 0xCA:\n                case 0xCB:\n                case 0xCC:\n                case 0xCD:\n                case 0xCE:\n                case 0xCF:\n                case 0xD0:\n                case 0xD1:\n                case 0xD2:\n                case 0xD3:\n                case 0xD4:\n                case 0xD5:\n                case 0xD6:\n                case 0xD7:\n                case 0xD8:\n                case 0xD9:\n                case 0xDA:\n                case 0xDB:\n                case 0xDC:\n                case 0xDD:\n                case 0xDE:\n                case 0xDF:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(not next_byte_in_range({0x80, 0xBF})))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+0800..U+0FFF: bytes E0 A0..BF 80..BF\n                case 0xE0:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF\n                // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF\n                case 0xE1:\n                case 0xE2:\n                case 0xE3:\n                case 0xE4:\n                case 0xE5:\n                case 0xE6:\n                case 0xE7:\n                case 0xE8:\n                case 0xE9:\n                case 0xEA:\n                case 0xEB:\n                case 0xEC:\n                case 0xEE:\n                case 0xEF:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+D000..U+D7FF: bytes ED 80..9F 80..BF\n                case 0xED:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF\n                case 0xF0:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF\n                case 0xF1:\n                case 0xF2:\n                case 0xF3:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF\n                case 0xF4:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // remaining bytes (80..C1 and F5..FF) are ill-formed\n                default:\n                {\n                    error_message = \"invalid string: ill-formed UTF-8 byte\";\n                    return token_type::parse_error;\n                }\n            }\n        }\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(float& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtof(str, endptr);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(double& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtod(str, endptr);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(long double& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtold(str, endptr);\n    }\n\n    /*!\n    @brief scan a number literal\n\n    This function scans a string according to Sect. 6 of RFC 7159.\n\n    The function is realized with a deterministic finite state machine derived\n    from the grammar described in RFC 7159. Starting in state \"init\", the\n    input is read and used to determined the next state. Only state \"done\"\n    accepts the number. State \"error\" is a trap state to model errors. In the\n    table below, \"anything\" means any character but the ones listed before.\n\n    state    | 0        | 1-9      | e E      | +       | -       | .        | anything\n    ---------|----------|----------|----------|---------|---------|----------|-----------\n    init     | zero     | any1     | [error]  | [error] | minus   | [error]  | [error]\n    minus    | zero     | any1     | [error]  | [error] | [error] | [error]  | [error]\n    zero     | done     | done     | exponent | done    | done    | decimal1 | done\n    any1     | any1     | any1     | exponent | done    | done    | decimal1 | done\n    decimal1 | decimal2 | [error]  | [error]  | [error] | [error] | [error]  | [error]\n    decimal2 | decimal2 | decimal2 | exponent | done    | done    | done     | done\n    exponent | any2     | any2     | [error]  | sign    | sign    | [error]  | [error]\n    sign     | any2     | any2     | [error]  | [error] | [error] | [error]  | [error]\n    any2     | any2     | any2     | done     | done    | done    | done     | done\n\n    The state machine is realized with one label per state (prefixed with\n    \"scan_number_\") and `goto` statements between them. The state machine\n    contains cycles, but any cycle can be left when EOF is read. Therefore,\n    the function is guaranteed to terminate.\n\n    During scanning, the read bytes are stored in token_buffer. This string is\n    then converted to a signed integer, an unsigned integer, or a\n    floating-point number.\n\n    @return token_type::value_unsigned, token_type::value_integer, or\n            token_type::value_float if number could be successfully scanned,\n            token_type::parse_error otherwise\n\n    @note The scanner is independent of the current locale. Internally, the\n          locale's decimal point is used instead of `.` to work with the\n          locale-dependent converters.\n    */\n    token_type scan_number()  // lgtm [cpp/use-of-goto]\n    {\n        // reset token_buffer to store the number's bytes\n        reset();\n\n        // the type of the parsed number; initially set to unsigned; will be\n        // changed if minus sign, decimal point or exponent is read\n        token_type number_type = token_type::value_unsigned;\n\n        // state (init): we just found out we need to scan a number\n        switch (current)\n        {\n            case '-':\n            {\n                add(current);\n                goto scan_number_minus;\n            }\n\n            case '0':\n            {\n                add(current);\n                goto scan_number_zero;\n            }\n\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            // all other characters are rejected outside scan_number()\n            default:            // LCOV_EXCL_LINE\n                assert(false);  // LCOV_EXCL_LINE\n        }\n\nscan_number_minus:\n        // state: we just parsed a leading minus sign\n        number_type = token_type::value_integer;\n        switch (get())\n        {\n            case '0':\n            {\n                add(current);\n                goto scan_number_zero;\n            }\n\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after '-'\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_zero:\n        // state: we just parse a zero (maybe with a leading minus sign)\n        switch (get())\n        {\n            case '.':\n            {\n                add(decimal_point_char);\n                goto scan_number_decimal1;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_any1:\n        // state: we just parsed a number 0-9 (maybe with a leading minus sign)\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            case '.':\n            {\n                add(decimal_point_char);\n                goto scan_number_decimal1;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_decimal1:\n        // state: we just parsed a decimal point\n        number_type = token_type::value_float;\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_decimal2;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after '.'\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_decimal2:\n        // we just parsed at least one number after a decimal point\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_decimal2;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_exponent:\n        // we just parsed an exponent\n        number_type = token_type::value_float;\n        switch (get())\n        {\n            case '+':\n            case '-':\n            {\n                add(current);\n                goto scan_number_sign;\n            }\n\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n            {\n                error_message =\n                    \"invalid number; expected '+', '-', or digit after exponent\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_sign:\n        // we just parsed an exponent sign\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after exponent sign\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_any2:\n        // we just parsed a number after the exponent or exponent sign\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_done:\n        // unget the character after the number (we only read it to know that\n        // we are done scanning a number)\n        unget();\n\n        char* endptr = nullptr;\n        errno = 0;\n\n        // try to parse integers first and fall back to floats\n        if (number_type == token_type::value_unsigned)\n        {\n            const auto x = std::strtoull(token_buffer.data(), &endptr, 10);\n\n            // we checked the number format before\n            assert(endptr == token_buffer.data() + token_buffer.size());\n\n            if (errno == 0)\n            {\n                value_unsigned = static_cast<number_unsigned_t>(x);\n                if (value_unsigned == x)\n                {\n                    return token_type::value_unsigned;\n                }\n            }\n        }\n        else if (number_type == token_type::value_integer)\n        {\n            const auto x = std::strtoll(token_buffer.data(), &endptr, 10);\n\n            // we checked the number format before\n            assert(endptr == token_buffer.data() + token_buffer.size());\n\n            if (errno == 0)\n            {\n                value_integer = static_cast<number_integer_t>(x);\n                if (value_integer == x)\n                {\n                    return token_type::value_integer;\n                }\n            }\n        }\n\n        // this code is reached if we parse a floating-point number or if an\n        // integer conversion above failed\n        strtof(value_float, token_buffer.data(), &endptr);\n\n        // we checked the number format before\n        assert(endptr == token_buffer.data() + token_buffer.size());\n\n        return token_type::value_float;\n    }\n\n    /*!\n    @param[in] literal_text  the literal text to expect\n    @param[in] length        the length of the passed literal text\n    @param[in] return_type   the token type to return on success\n    */\n    JSON_HEDLEY_NON_NULL(2)\n    token_type scan_literal(const char* literal_text, const std::size_t length,\n                            token_type return_type)\n    {\n        assert(current == literal_text[0]);\n        for (std::size_t i = 1; i < length; ++i)\n        {\n            if (JSON_HEDLEY_UNLIKELY(get() != literal_text[i]))\n            {\n                error_message = \"invalid literal\";\n                return token_type::parse_error;\n            }\n        }\n        return return_type;\n    }\n\n    /////////////////////\n    // input management\n    /////////////////////\n\n    /// reset token_buffer; current character is beginning of token\n    void reset() noexcept\n    {\n        token_buffer.clear();\n        token_string.clear();\n        token_string.push_back(std::char_traits<char>::to_char_type(current));\n    }\n\n    /*\n    @brief get next character from the input\n\n    This function provides the interface to the used input adapter. It does\n    not throw in case the input reached EOF, but returns a\n    `std::char_traits<char>::eof()` in that case.  Stores the scanned characters\n    for use in error messages.\n\n    @return character read from the input\n    */\n    std::char_traits<char>::int_type get()\n    {\n        ++position.chars_read_total;\n        ++position.chars_read_current_line;\n\n        if (next_unget)\n        {\n            // just reset the next_unget variable and work with current\n            next_unget = false;\n        }\n        else\n        {\n            current = ia->get_character();\n        }\n\n        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char>::eof()))\n        {\n            token_string.push_back(std::char_traits<char>::to_char_type(current));\n        }\n\n        if (current == '\\n')\n        {\n            ++position.lines_read;\n            position.chars_read_current_line = 0;\n        }\n\n        return current;\n    }\n\n    /*!\n    @brief unget current character (read it again on next get)\n\n    We implement unget by setting variable next_unget to true. The input is not\n    changed - we just simulate ungetting by modifying chars_read_total,\n    chars_read_current_line, and token_string. The next call to get() will\n    behave as if the unget character is read again.\n    */\n    void unget()\n    {\n        next_unget = true;\n\n        --position.chars_read_total;\n\n        // in case we \"unget\" a newline, we have to also decrement the lines_read\n        if (position.chars_read_current_line == 0)\n        {\n            if (position.lines_read > 0)\n            {\n                --position.lines_read;\n            }\n        }\n        else\n        {\n            --position.chars_read_current_line;\n        }\n\n        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char>::eof()))\n        {\n            assert(not token_string.empty());\n            token_string.pop_back();\n        }\n    }\n\n    /// add a character to token_buffer\n    void add(int c)\n    {\n        token_buffer.push_back(std::char_traits<char>::to_char_type(c));\n    }\n\n  public:\n    /////////////////////\n    // value getters\n    /////////////////////\n\n    /// return integer value\n    constexpr number_integer_t get_number_integer() const noexcept\n    {\n        return value_integer;\n    }\n\n    /// return unsigned integer value\n    constexpr number_unsigned_t get_number_unsigned() const noexcept\n    {\n        return value_unsigned;\n    }\n\n    /// return floating-point value\n    constexpr number_float_t get_number_float() const noexcept\n    {\n        return value_float;\n    }\n\n    /// return current string value (implicitly resets the token; useful only once)\n    string_t& get_string()\n    {\n        return token_buffer;\n    }\n\n    /////////////////////\n    // diagnostics\n    /////////////////////\n\n    /// return position of last read token\n    constexpr position_t get_position() const noexcept\n    {\n        return position;\n    }\n\n    /// return the last read token (for errors only).  Will never contain EOF\n    /// (an arbitrary value that is not a valid char value, often -1), because\n    /// 255 may legitimately occur.  May contain NUL, which should be escaped.\n    std::string get_token_string() const\n    {\n        // escape control characters\n        std::string result;\n        for (const auto c : token_string)\n        {\n            if ('\\x00' <= c and c <= '\\x1F')\n            {\n                // escape control characters\n                std::array<char, 9> cs{{}};\n                (std::snprintf)(cs.data(), cs.size(), \"<U+%.4X>\", static_cast<unsigned char>(c));\n                result += cs.data();\n            }\n            else\n            {\n                // add character as is\n                result.push_back(c);\n            }\n        }\n\n        return result;\n    }\n\n    /// return syntax error message\n    JSON_HEDLEY_RETURNS_NON_NULL\n    constexpr const char* get_error_message() const noexcept\n    {\n        return error_message;\n    }\n\n    /////////////////////\n    // actual scanner\n    /////////////////////\n\n    /*!\n    @brief skip the UTF-8 byte order mark\n    @return true iff there is no BOM or the correct BOM has been skipped\n    */\n    bool skip_bom()\n    {\n        if (get() == 0xEF)\n        {\n            // check if we completely parse the BOM\n            return get() == 0xBB and get() == 0xBF;\n        }\n\n        // the first character is not the beginning of the BOM; unget it to\n        // process is later\n        unget();\n        return true;\n    }\n\n    token_type scan()\n    {\n        // initially, skip the BOM\n        if (position.chars_read_total == 0 and not skip_bom())\n        {\n            error_message = \"invalid BOM; must be 0xEF 0xBB 0xBF if given\";\n            return token_type::parse_error;\n        }\n\n        // read next character and ignore whitespace\n        do\n        {\n            get();\n        }\n        while (current == ' ' or current == '\\t' or current == '\\n' or current == '\\r');\n\n        switch (current)\n        {\n            // structural characters\n            case '[':\n                return token_type::begin_array;\n            case ']':\n                return token_type::end_array;\n            case '{':\n                return token_type::begin_object;\n            case '}':\n                return token_type::end_object;\n            case ':':\n                return token_type::name_separator;\n            case ',':\n                return token_type::value_separator;\n\n            // literals\n            case 't':\n                return scan_literal(\"true\", 4, token_type::literal_true);\n            case 'f':\n                return scan_literal(\"false\", 5, token_type::literal_false);\n            case 'n':\n                return scan_literal(\"null\", 4, token_type::literal_null);\n\n            // string\n            case '\\\"':\n                return scan_string();\n\n            // number\n            case '-':\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n                return scan_number();\n\n            // end of input (the null byte is needed when parsing from\n            // string literals)\n            case '\\0':\n            case std::char_traits<char>::eof():\n                return token_type::end_of_input;\n\n            // error\n            default:\n                error_message = \"invalid literal\";\n                return token_type::parse_error;\n        }\n    }\n\n  private:\n    /// input adapter\n    detail::input_adapter_t ia = nullptr;\n\n    /// the current character\n    std::char_traits<char>::int_type current = std::char_traits<char>::eof();\n\n    /// whether the next get() call should just return current\n    bool next_unget = false;\n\n    /// the start position of the current token\n    position_t position {};\n\n    /// raw input token string (for error messages)\n    std::vector<char> token_string {};\n\n    /// buffer for variable-length tokens (numbers, strings)\n    string_t token_buffer {};\n\n    /// a description of occurred lexer errors\n    const char* error_message = \"\";\n\n    // number values\n    number_integer_t value_integer = 0;\n    number_unsigned_t value_unsigned = 0;\n    number_float_t value_float = 0;\n\n    /// the decimal point\n    const char decimal_point_char = '.';\n};\n}  // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/input/parser.hpp",
    "content": "#pragma once\n\n#include <cassert> // assert\n#include <cmath> // isfinite\n#include <cstdint> // uint8_t\n#include <functional> // function\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n#include <nlohmann/detail/exceptions.hpp>\n#include <nlohmann/detail/input/input_adapters.hpp>\n#include <nlohmann/detail/input/json_sax.hpp>\n#include <nlohmann/detail/input/lexer.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/meta/is_sax.hpp>\n#include <nlohmann/detail/value_t.hpp>\n\nnamespace nlohmann\n{\nnamespace detail\n{\n////////////\n// parser //\n////////////\n\n/*!\n@brief syntax analysis\n\nThis class implements a recursive decent parser.\n*/\ntemplate<typename BasicJsonType>\nclass parser\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using lexer_t = lexer<BasicJsonType>;\n    using token_type = typename lexer_t::token_type;\n\n  public:\n    enum class parse_event_t : uint8_t\n    {\n        /// the parser read `{` and started to process a JSON object\n        object_start,\n        /// the parser read `}` and finished processing a JSON object\n        object_end,\n        /// the parser read `[` and started to process a JSON array\n        array_start,\n        /// the parser read `]` and finished processing a JSON array\n        array_end,\n        /// the parser read a key of a value in an object\n        key,\n        /// the parser finished reading a JSON value\n        value\n    };\n\n    using parser_callback_t =\n        std::function<bool(int depth, parse_event_t event, BasicJsonType& parsed)>;\n\n    /// a parser reading from an input adapter\n    explicit parser(detail::input_adapter_t&& adapter,\n                    const parser_callback_t cb = nullptr,\n                    const bool allow_exceptions_ = true)\n        : callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_)\n    {\n        // read first token\n        get_token();\n    }\n\n    /*!\n    @brief public parser interface\n\n    @param[in] strict      whether to expect the last token to be EOF\n    @param[in,out] result  parsed JSON value\n\n    @throw parse_error.101 in case of an unexpected token\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n    */\n    void parse(const bool strict, BasicJsonType& result)\n    {\n        if (callback)\n        {\n            json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);\n            sax_parse_internal(&sdp);\n            result.assert_invariant();\n\n            // in strict mode, input must be completely read\n            if (strict and (get_token() != token_type::end_of_input))\n            {\n                sdp.parse_error(m_lexer.get_position(),\n                                m_lexer.get_token_string(),\n                                parse_error::create(101, m_lexer.get_position(),\n                                                    exception_message(token_type::end_of_input, \"value\")));\n            }\n\n            // in case of an error, return discarded value\n            if (sdp.is_errored())\n            {\n                result = value_t::discarded;\n                return;\n            }\n\n            // set top-level value to null if it was discarded by the callback\n            // function\n            if (result.is_discarded())\n            {\n                result = nullptr;\n            }\n        }\n        else\n        {\n            json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);\n            sax_parse_internal(&sdp);\n            result.assert_invariant();\n\n            // in strict mode, input must be completely read\n            if (strict and (get_token() != token_type::end_of_input))\n            {\n                sdp.parse_error(m_lexer.get_position(),\n                                m_lexer.get_token_string(),\n                                parse_error::create(101, m_lexer.get_position(),\n                                                    exception_message(token_type::end_of_input, \"value\")));\n            }\n\n            // in case of an error, return discarded value\n            if (sdp.is_errored())\n            {\n                result = value_t::discarded;\n                return;\n            }\n        }\n    }\n\n    /*!\n    @brief public accept interface\n\n    @param[in] strict  whether to expect the last token to be EOF\n    @return whether the input is a proper JSON text\n    */\n    bool accept(const bool strict = true)\n    {\n        json_sax_acceptor<BasicJsonType> sax_acceptor;\n        return sax_parse(&sax_acceptor, strict);\n    }\n\n    template <typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    bool sax_parse(SAX* sax, const bool strict = true)\n    {\n        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};\n        const bool result = sax_parse_internal(sax);\n\n        // strict mode: next byte must be EOF\n        if (result and strict and (get_token() != token_type::end_of_input))\n        {\n            return sax->parse_error(m_lexer.get_position(),\n                                    m_lexer.get_token_string(),\n                                    parse_error::create(101, m_lexer.get_position(),\n                                            exception_message(token_type::end_of_input, \"value\")));\n        }\n\n        return result;\n    }\n\n  private:\n    template <typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    bool sax_parse_internal(SAX* sax)\n    {\n        // stack to remember the hierarchy of structured values we are parsing\n        // true = array; false = object\n        std::vector<bool> states;\n        // value to avoid a goto (see comment where set to true)\n        bool skip_to_state_evaluation = false;\n\n        while (true)\n        {\n            if (not skip_to_state_evaluation)\n            {\n                // invariant: get_token() was called before each iteration\n                switch (last_token)\n                {\n                    case token_type::begin_object:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1))))\n                        {\n                            return false;\n                        }\n\n                        // closing } -> we are done\n                        if (get_token() == token_type::end_object)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(not sax->end_object()))\n                            {\n                                return false;\n                            }\n                            break;\n                        }\n\n                        // parse key\n                        if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    parse_error::create(101, m_lexer.get_position(),\n                                                            exception_message(token_type::value_string, \"object key\")));\n                        }\n                        if (JSON_HEDLEY_UNLIKELY(not sax->key(m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n\n                        // parse separator (:)\n                        if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    parse_error::create(101, m_lexer.get_position(),\n                                                            exception_message(token_type::name_separator, \"object separator\")));\n                        }\n\n                        // remember we are now inside an object\n                        states.push_back(false);\n\n                        // parse values\n                        get_token();\n                        continue;\n                    }\n\n                    case token_type::begin_array:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1))))\n                        {\n                            return false;\n                        }\n\n                        // closing ] -> we are done\n                        if (get_token() == token_type::end_array)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(not sax->end_array()))\n                            {\n                                return false;\n                            }\n                            break;\n                        }\n\n                        // remember we are now inside an array\n                        states.push_back(true);\n\n                        // parse values (no need to call get_token)\n                        continue;\n                    }\n\n                    case token_type::value_float:\n                    {\n                        const auto res = m_lexer.get_number_float();\n\n                        if (JSON_HEDLEY_UNLIKELY(not std::isfinite(res)))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    out_of_range::create(406, \"number overflow parsing '\" + m_lexer.get_token_string() + \"'\"));\n                        }\n\n                        if (JSON_HEDLEY_UNLIKELY(not sax->number_float(res, m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n\n                        break;\n                    }\n\n                    case token_type::literal_false:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(not sax->boolean(false)))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::literal_null:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(not sax->null()))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::literal_true:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(not sax->boolean(true)))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_integer:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(not sax->number_integer(m_lexer.get_number_integer())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_string:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(not sax->string(m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_unsigned:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(not sax->number_unsigned(m_lexer.get_number_unsigned())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::parse_error:\n                    {\n                        // using \"uninitialized\" to avoid \"expected\" message\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(),\n                                                        exception_message(token_type::uninitialized, \"value\")));\n                    }\n\n                    default: // the last token was unexpected\n                    {\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(),\n                                                        exception_message(token_type::literal_or_value, \"value\")));\n                    }\n                }\n            }\n            else\n            {\n                skip_to_state_evaluation = false;\n            }\n\n            // we reached this line after we successfully parsed a value\n            if (states.empty())\n            {\n                // empty stack: we reached the end of the hierarchy: done\n                return true;\n            }\n\n            if (states.back())  // array\n            {\n                // comma -> next value\n                if (get_token() == token_type::value_separator)\n                {\n                    // parse a new value\n                    get_token();\n                    continue;\n                }\n\n                // closing ]\n                if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))\n                {\n                    if (JSON_HEDLEY_UNLIKELY(not sax->end_array()))\n                    {\n                        return false;\n                    }\n\n                    // We are done with this array. Before we can parse a\n                    // new value, we need to evaluate the new state first.\n                    // By setting skip_to_state_evaluation to false, we\n                    // are effectively jumping to the beginning of this if.\n                    assert(not states.empty());\n                    states.pop_back();\n                    skip_to_state_evaluation = true;\n                    continue;\n                }\n\n                return sax->parse_error(m_lexer.get_position(),\n                                        m_lexer.get_token_string(),\n                                        parse_error::create(101, m_lexer.get_position(),\n                                                exception_message(token_type::end_array, \"array\")));\n            }\n            else  // object\n            {\n                // comma -> next value\n                if (get_token() == token_type::value_separator)\n                {\n                    // parse key\n                    if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))\n                    {\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(),\n                                                        exception_message(token_type::value_string, \"object key\")));\n                    }\n\n                    if (JSON_HEDLEY_UNLIKELY(not sax->key(m_lexer.get_string())))\n                    {\n                        return false;\n                    }\n\n                    // parse separator (:)\n                    if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))\n                    {\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(),\n                                                        exception_message(token_type::name_separator, \"object separator\")));\n                    }\n\n                    // parse values\n                    get_token();\n                    continue;\n                }\n\n                // closing }\n                if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))\n                {\n                    if (JSON_HEDLEY_UNLIKELY(not sax->end_object()))\n                    {\n                        return false;\n                    }\n\n                    // We are done with this object. Before we can parse a\n                    // new value, we need to evaluate the new state first.\n                    // By setting skip_to_state_evaluation to false, we\n                    // are effectively jumping to the beginning of this if.\n                    assert(not states.empty());\n                    states.pop_back();\n                    skip_to_state_evaluation = true;\n                    continue;\n                }\n\n                return sax->parse_error(m_lexer.get_position(),\n                                        m_lexer.get_token_string(),\n                                        parse_error::create(101, m_lexer.get_position(),\n                                                exception_message(token_type::end_object, \"object\")));\n            }\n        }\n    }\n\n    /// get next token from lexer\n    token_type get_token()\n    {\n        return last_token = m_lexer.scan();\n    }\n\n    std::string exception_message(const token_type expected, const std::string& context)\n    {\n        std::string error_msg = \"syntax error \";\n\n        if (not context.empty())\n        {\n            error_msg += \"while parsing \" + context + \" \";\n        }\n\n        error_msg += \"- \";\n\n        if (last_token == token_type::parse_error)\n        {\n            error_msg += std::string(m_lexer.get_error_message()) + \"; last read: '\" +\n                         m_lexer.get_token_string() + \"'\";\n        }\n        else\n        {\n            error_msg += \"unexpected \" + std::string(lexer_t::token_type_name(last_token));\n        }\n\n        if (expected != token_type::uninitialized)\n        {\n            error_msg += \"; expected \" + std::string(lexer_t::token_type_name(expected));\n        }\n\n        return error_msg;\n    }\n\n  private:\n    /// callback function\n    const parser_callback_t callback = nullptr;\n    /// the type of the last read token\n    token_type last_token = token_type::uninitialized;\n    /// the lexer\n    lexer_t m_lexer;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n};\n}  // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/input/position_t.hpp",
    "content": "#pragma once\n\n#include <cstddef> // size_t\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// struct to capture the start position of the current token\nstruct position_t\n{\n    /// the total number of characters read\n    std::size_t chars_read_total = 0;\n    /// the number of characters read in the current line\n    std::size_t chars_read_current_line = 0;\n    /// the number of lines read\n    std::size_t lines_read = 0;\n\n    /// conversion to size_t to preserve SAX interface\n    constexpr operator size_t() const\n    {\n        return chars_read_total;\n    }\n};\n\n} // namespace detail\n} // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/iterators/internal_iterator.hpp",
    "content": "#pragma once\n\n#include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/*!\n@brief an iterator value\n\n@note This structure could easily be a union, but MSVC currently does not allow\nunions members with complex constructors, see https://github.com/nlohmann/json/pull/105.\n*/\ntemplate<typename BasicJsonType> struct internal_iterator\n{\n    /// iterator for JSON objects\n    typename BasicJsonType::object_t::iterator object_iterator {};\n    /// iterator for JSON arrays\n    typename BasicJsonType::array_t::iterator array_iterator {};\n    /// generic iterator for all other types\n    primitive_iterator_t primitive_iterator {};\n};\n}  // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/iterators/iter_impl.hpp",
    "content": "#pragma once\n\n#include <ciso646> // not\n#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next\n#include <type_traits> // conditional, is_const, remove_const\n\n#include <nlohmann/detail/exceptions.hpp>\n#include <nlohmann/detail/iterators/internal_iterator.hpp>\n#include <nlohmann/detail/iterators/primitive_iterator.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/meta/cpp_future.hpp>\n#include <nlohmann/detail/meta/type_traits.hpp>\n#include <nlohmann/detail/value_t.hpp>\n\nnamespace nlohmann\n{\nnamespace detail\n{\n// forward declare, to be able to friend it later on\ntemplate<typename IteratorType> class iteration_proxy;\ntemplate<typename IteratorType> class iteration_proxy_value;\n\n/*!\n@brief a template for a bidirectional iterator for the @ref basic_json class\nThis class implements a both iterators (iterator and const_iterator) for the\n@ref basic_json class.\n@note An iterator is called *initialized* when a pointer to a JSON value has\n      been set (e.g., by a constructor or a copy assignment). If the iterator is\n      default-constructed, it is *uninitialized* and most methods are undefined.\n      **The library uses assertions to detect calls on uninitialized iterators.**\n@requirement The class satisfies the following concept requirements:\n-\n[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):\n  The iterator that can be moved can be moved in both directions (i.e.\n  incremented and decremented).\n@since version 1.0.0, simplified in version 2.0.9, change to bidirectional\n       iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)\n*/\ntemplate<typename BasicJsonType>\nclass iter_impl\n{\n    /// allow basic_json to access private members\n    friend iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;\n    friend BasicJsonType;\n    friend iteration_proxy<iter_impl>;\n    friend iteration_proxy_value<iter_impl>;\n\n    using object_t = typename BasicJsonType::object_t;\n    using array_t = typename BasicJsonType::array_t;\n    // make sure BasicJsonType is basic_json or const basic_json\n    static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,\n                  \"iter_impl only accepts (const) basic_json\");\n\n  public:\n\n    /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.\n    /// The C++ Standard has never required user-defined iterators to derive from std::iterator.\n    /// A user-defined iterator should provide publicly accessible typedefs named\n    /// iterator_category, value_type, difference_type, pointer, and reference.\n    /// Note that value_type is required to be non-const, even for constant iterators.\n    using iterator_category = std::bidirectional_iterator_tag;\n\n    /// the type of the values when the iterator is dereferenced\n    using value_type = typename BasicJsonType::value_type;\n    /// a type to represent differences between iterators\n    using difference_type = typename BasicJsonType::difference_type;\n    /// defines a pointer to the type iterated over (value_type)\n    using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,\n          typename BasicJsonType::const_pointer,\n          typename BasicJsonType::pointer>::type;\n    /// defines a reference to the type iterated over (value_type)\n    using reference =\n        typename std::conditional<std::is_const<BasicJsonType>::value,\n        typename BasicJsonType::const_reference,\n        typename BasicJsonType::reference>::type;\n\n    /// default constructor\n    iter_impl() = default;\n\n    /*!\n    @brief constructor for a given JSON instance\n    @param[in] object  pointer to a JSON object for this iterator\n    @pre object != nullptr\n    @post The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    explicit iter_impl(pointer object) noexcept : m_object(object)\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = typename object_t::iterator();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = typename array_t::iterator();\n                break;\n            }\n\n            default:\n            {\n                m_it.primitive_iterator = primitive_iterator_t();\n                break;\n            }\n        }\n    }\n\n    /*!\n    @note The conventional copy constructor and copy assignment are implicitly\n          defined. Combined with the following converting constructor and\n          assignment, they support: (1) copy from iterator to iterator, (2)\n          copy from const iterator to const iterator, and (3) conversion from\n          iterator to const iterator. However conversion from const iterator\n          to iterator is not defined.\n    */\n\n    /*!\n    @brief const copy constructor\n    @param[in] other const iterator to copy from\n    @note This copy constructor had to be defined explicitly to circumvent a bug\n          occurring on msvc v19.0 compiler (VS 2015) debug build. For more\n          information refer to: https://github.com/nlohmann/json/issues/1608\n    */\n    iter_impl(const iter_impl<const BasicJsonType>& other) noexcept\n        : m_object(other.m_object), m_it(other.m_it)\n    {}\n\n    /*!\n    @brief converting assignment\n    @param[in] other const iterator to copy from\n    @return const/non-const iterator\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept\n    {\n        m_object = other.m_object;\n        m_it = other.m_it;\n        return *this;\n    }\n\n    /*!\n    @brief converting constructor\n    @param[in] other  non-const iterator to copy from\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept\n        : m_object(other.m_object), m_it(other.m_it)\n    {}\n\n    /*!\n    @brief converting assignment\n    @param[in] other  non-const iterator to copy from\n    @return const/non-const iterator\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept\n    {\n        m_object = other.m_object;\n        m_it = other.m_it;\n        return *this;\n    }\n\n  private:\n    /*!\n    @brief set the iterator to the first value\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    void set_begin() noexcept\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = m_object->m_value.object->begin();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = m_object->m_value.array->begin();\n                break;\n            }\n\n            case value_t::null:\n            {\n                // set to end so begin()==end() is true: null is empty\n                m_it.primitive_iterator.set_end();\n                break;\n            }\n\n            default:\n            {\n                m_it.primitive_iterator.set_begin();\n                break;\n            }\n        }\n    }\n\n    /*!\n    @brief set the iterator past the last value\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    void set_end() noexcept\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = m_object->m_value.object->end();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = m_object->m_value.array->end();\n                break;\n            }\n\n            default:\n            {\n                m_it.primitive_iterator.set_end();\n                break;\n            }\n        }\n    }\n\n  public:\n    /*!\n    @brief return a reference to the value pointed to by the iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference operator*() const\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                assert(m_it.object_iterator != m_object->m_value.object->end());\n                return m_it.object_iterator->second;\n            }\n\n            case value_t::array:\n            {\n                assert(m_it.array_iterator != m_object->m_value.array->end());\n                return *m_it.array_iterator;\n            }\n\n            case value_t::null:\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))\n                {\n                    return *m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n            }\n        }\n    }\n\n    /*!\n    @brief dereference the iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    pointer operator->() const\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                assert(m_it.object_iterator != m_object->m_value.object->end());\n                return &(m_it.object_iterator->second);\n            }\n\n            case value_t::array:\n            {\n                assert(m_it.array_iterator != m_object->m_value.array->end());\n                return &*m_it.array_iterator;\n            }\n\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))\n                {\n                    return m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n            }\n        }\n    }\n\n    /*!\n    @brief post-increment (it++)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl const operator++(int)\n    {\n        auto result = *this;\n        ++(*this);\n        return result;\n    }\n\n    /*!\n    @brief pre-increment (++it)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator++()\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                std::advance(m_it.object_iterator, 1);\n                break;\n            }\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, 1);\n                break;\n            }\n\n            default:\n            {\n                ++m_it.primitive_iterator;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief post-decrement (it--)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl const operator--(int)\n    {\n        auto result = *this;\n        --(*this);\n        return result;\n    }\n\n    /*!\n    @brief pre-decrement (--it)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator--()\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                std::advance(m_it.object_iterator, -1);\n                break;\n            }\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, -1);\n                break;\n            }\n\n            default:\n            {\n                --m_it.primitive_iterator;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief  comparison: equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator==(const iter_impl& other) const\n    {\n        // if objects are not the same, the comparison is undefined\n        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(212, \"cannot compare iterators of different containers\"));\n        }\n\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                return (m_it.object_iterator == other.m_it.object_iterator);\n\n            case value_t::array:\n                return (m_it.array_iterator == other.m_it.array_iterator);\n\n            default:\n                return (m_it.primitive_iterator == other.m_it.primitive_iterator);\n        }\n    }\n\n    /*!\n    @brief  comparison: not equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator!=(const iter_impl& other) const\n    {\n        return not operator==(other);\n    }\n\n    /*!\n    @brief  comparison: smaller\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator<(const iter_impl& other) const\n    {\n        // if objects are not the same, the comparison is undefined\n        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(212, \"cannot compare iterators of different containers\"));\n        }\n\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(213, \"cannot compare order of object iterators\"));\n\n            case value_t::array:\n                return (m_it.array_iterator < other.m_it.array_iterator);\n\n            default:\n                return (m_it.primitive_iterator < other.m_it.primitive_iterator);\n        }\n    }\n\n    /*!\n    @brief  comparison: less than or equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator<=(const iter_impl& other) const\n    {\n        return not other.operator < (*this);\n    }\n\n    /*!\n    @brief  comparison: greater than\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator>(const iter_impl& other) const\n    {\n        return not operator<=(other);\n    }\n\n    /*!\n    @brief  comparison: greater than or equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator>=(const iter_impl& other) const\n    {\n        return not operator<(other);\n    }\n\n    /*!\n    @brief  add to iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator+=(difference_type i)\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(209, \"cannot use offsets with object iterators\"));\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, i);\n                break;\n            }\n\n            default:\n            {\n                m_it.primitive_iterator += i;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief  subtract from iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator-=(difference_type i)\n    {\n        return operator+=(-i);\n    }\n\n    /*!\n    @brief  add to iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator+(difference_type i) const\n    {\n        auto result = *this;\n        result += i;\n        return result;\n    }\n\n    /*!\n    @brief  addition of distance and iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    friend iter_impl operator+(difference_type i, const iter_impl& it)\n    {\n        auto result = it;\n        result += i;\n        return result;\n    }\n\n    /*!\n    @brief  subtract from iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator-(difference_type i) const\n    {\n        auto result = *this;\n        result -= i;\n        return result;\n    }\n\n    /*!\n    @brief  return difference\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    difference_type operator-(const iter_impl& other) const\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(209, \"cannot use offsets with object iterators\"));\n\n            case value_t::array:\n                return m_it.array_iterator - other.m_it.array_iterator;\n\n            default:\n                return m_it.primitive_iterator - other.m_it.primitive_iterator;\n        }\n    }\n\n    /*!\n    @brief  access to successor\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference operator[](difference_type n) const\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(208, \"cannot use operator[] for object iterators\"));\n\n            case value_t::array:\n                return *std::next(m_it.array_iterator, n);\n\n            case value_t::null:\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n))\n                {\n                    return *m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n            }\n        }\n    }\n\n    /*!\n    @brief  return the key of an object iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    const typename object_t::key_type& key() const\n    {\n        assert(m_object != nullptr);\n\n        if (JSON_HEDLEY_LIKELY(m_object->is_object()))\n        {\n            return m_it.object_iterator->first;\n        }\n\n        JSON_THROW(invalid_iterator::create(207, \"cannot use key() for non-object iterators\"));\n    }\n\n    /*!\n    @brief  return the value of an iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference value() const\n    {\n        return operator*();\n    }\n\n  private:\n    /// associated JSON instance\n    pointer m_object = nullptr;\n    /// the actual iterator of the associated instance\n    internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {};\n};\n} // namespace detail\n} // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/iterators/iteration_proxy.hpp",
    "content": "#pragma once\n\n#include <cstddef> // size_t\n#include <iterator> // input_iterator_tag\n#include <string> // string, to_string\n#include <tuple> // tuple_size, get, tuple_element\n\n#include <nlohmann/detail/meta/type_traits.hpp>\n#include <nlohmann/detail/value_t.hpp>\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename string_type>\nvoid int_to_string( string_type& target, std::size_t value )\n{\n    target = std::to_string(value);\n}\ntemplate <typename IteratorType> class iteration_proxy_value\n{\n  public:\n    using difference_type = std::ptrdiff_t;\n    using value_type = iteration_proxy_value;\n    using pointer = value_type * ;\n    using reference = value_type & ;\n    using iterator_category = std::input_iterator_tag;\n    using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;\n\n  private:\n    /// the iterator\n    IteratorType anchor;\n    /// an index for arrays (used to create key names)\n    std::size_t array_index = 0;\n    /// last stringified array index\n    mutable std::size_t array_index_last = 0;\n    /// a string representation of the array index\n    mutable string_type array_index_str = \"0\";\n    /// an empty string (to return a reference for primitive values)\n    const string_type empty_str = \"\";\n\n  public:\n    explicit iteration_proxy_value(IteratorType it) noexcept : anchor(it) {}\n\n    /// dereference operator (needed for range-based for)\n    iteration_proxy_value& operator*()\n    {\n        return *this;\n    }\n\n    /// increment operator (needed for range-based for)\n    iteration_proxy_value& operator++()\n    {\n        ++anchor;\n        ++array_index;\n\n        return *this;\n    }\n\n    /// equality operator (needed for InputIterator)\n    bool operator==(const iteration_proxy_value& o) const\n    {\n        return anchor == o.anchor;\n    }\n\n    /// inequality operator (needed for range-based for)\n    bool operator!=(const iteration_proxy_value& o) const\n    {\n        return anchor != o.anchor;\n    }\n\n    /// return key of the iterator\n    const string_type& key() const\n    {\n        assert(anchor.m_object != nullptr);\n\n        switch (anchor.m_object->type())\n        {\n            // use integer array index as key\n            case value_t::array:\n            {\n                if (array_index != array_index_last)\n                {\n                    int_to_string( array_index_str, array_index );\n                    array_index_last = array_index;\n                }\n                return array_index_str;\n            }\n\n            // use key from the object\n            case value_t::object:\n                return anchor.key();\n\n            // use an empty key for all primitive types\n            default:\n                return empty_str;\n        }\n    }\n\n    /// return value of the iterator\n    typename IteratorType::reference value() const\n    {\n        return anchor.value();\n    }\n};\n\n/// proxy class for the items() function\ntemplate<typename IteratorType> class iteration_proxy\n{\n  private:\n    /// the container to iterate\n    typename IteratorType::reference container;\n\n  public:\n    /// construct iteration proxy from a container\n    explicit iteration_proxy(typename IteratorType::reference cont) noexcept\n        : container(cont) {}\n\n    /// return iterator begin (needed for range-based for)\n    iteration_proxy_value<IteratorType> begin() noexcept\n    {\n        return iteration_proxy_value<IteratorType>(container.begin());\n    }\n\n    /// return iterator end (needed for range-based for)\n    iteration_proxy_value<IteratorType> end() noexcept\n    {\n        return iteration_proxy_value<IteratorType>(container.end());\n    }\n};\n// Structured Bindings Support\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\ntemplate <std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>\nauto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())\n{\n    return i.key();\n}\n// Structured Bindings Support\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\ntemplate <std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>\nauto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())\n{\n    return i.value();\n}\n}  // namespace detail\n}  // namespace nlohmann\n\n// The Addition to the STD Namespace is required to add\n// Structured Bindings Support to the iteration_proxy_value class\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\nnamespace std\n{\n#if defined(__clang__)\n    // Fix: https://github.com/nlohmann/json/issues/1401\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wmismatched-tags\"\n#endif\ntemplate <typename IteratorType>\nclass tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>>\n            : public std::integral_constant<std::size_t, 2> {};\n\ntemplate <std::size_t N, typename IteratorType>\nclass tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>\n{\n  public:\n    using type = decltype(\n                     get<N>(std::declval <\n                            ::nlohmann::detail::iteration_proxy_value<IteratorType >> ()));\n};\n#if defined(__clang__)\n    #pragma clang diagnostic pop\n#endif\n} // namespace std\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/iterators/iterator_traits.hpp",
    "content": "#pragma once\n\n#include <iterator> // random_access_iterator_tag\n\n#include <nlohmann/detail/meta/void_t.hpp>\n#include <nlohmann/detail/meta/cpp_future.hpp>\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate <typename It, typename = void>\nstruct iterator_types {};\n\ntemplate <typename It>\nstruct iterator_types <\n    It,\n    void_t<typename It::difference_type, typename It::value_type, typename It::pointer,\n    typename It::reference, typename It::iterator_category >>\n{\n    using difference_type = typename It::difference_type;\n    using value_type = typename It::value_type;\n    using pointer = typename It::pointer;\n    using reference = typename It::reference;\n    using iterator_category = typename It::iterator_category;\n};\n\n// This is required as some compilers implement std::iterator_traits in a way that\n// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.\ntemplate <typename T, typename = void>\nstruct iterator_traits\n{\n};\n\ntemplate <typename T>\nstruct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>\n            : iterator_types<T>\n{\n};\n\ntemplate <typename T>\nstruct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>\n{\n    using iterator_category = std::random_access_iterator_tag;\n    using value_type = T;\n    using difference_type = ptrdiff_t;\n    using pointer = T*;\n    using reference = T&;\n};\n} // namespace detail\n} // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/iterators/json_reverse_iterator.hpp",
    "content": "#pragma once\n\n#include <cstddef> // ptrdiff_t\n#include <iterator> // reverse_iterator\n#include <utility> // declval\n\nnamespace nlohmann\n{\nnamespace detail\n{\n//////////////////////\n// reverse_iterator //\n//////////////////////\n\n/*!\n@brief a template for a reverse iterator class\n\n@tparam Base the base iterator type to reverse. Valid types are @ref\niterator (to create @ref reverse_iterator) and @ref const_iterator (to\ncreate @ref const_reverse_iterator).\n\n@requirement The class satisfies the following concept requirements:\n-\n[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):\n  The iterator that can be moved can be moved in both directions (i.e.\n  incremented and decremented).\n- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):\n  It is possible to write to the pointed-to element (only if @a Base is\n  @ref iterator).\n\n@since version 1.0.0\n*/\ntemplate<typename Base>\nclass json_reverse_iterator : public std::reverse_iterator<Base>\n{\n  public:\n    using difference_type = std::ptrdiff_t;\n    /// shortcut to the reverse iterator adapter\n    using base_iterator = std::reverse_iterator<Base>;\n    /// the reference type for the pointed-to element\n    using reference = typename Base::reference;\n\n    /// create reverse iterator from iterator\n    explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept\n        : base_iterator(it) {}\n\n    /// create reverse iterator from base class\n    explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}\n\n    /// post-increment (it++)\n    json_reverse_iterator const operator++(int)\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator++(1));\n    }\n\n    /// pre-increment (++it)\n    json_reverse_iterator& operator++()\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator++());\n    }\n\n    /// post-decrement (it--)\n    json_reverse_iterator const operator--(int)\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator--(1));\n    }\n\n    /// pre-decrement (--it)\n    json_reverse_iterator& operator--()\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator--());\n    }\n\n    /// add to iterator\n    json_reverse_iterator& operator+=(difference_type i)\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));\n    }\n\n    /// add to iterator\n    json_reverse_iterator operator+(difference_type i) const\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator+(i));\n    }\n\n    /// subtract from iterator\n    json_reverse_iterator operator-(difference_type i) const\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator-(i));\n    }\n\n    /// return difference\n    difference_type operator-(const json_reverse_iterator& other) const\n    {\n        return base_iterator(*this) - base_iterator(other);\n    }\n\n    /// access to successor\n    reference operator[](difference_type n) const\n    {\n        return *(this->operator+(n));\n    }\n\n    /// return the key of an object iterator\n    auto key() const -> decltype(std::declval<Base>().key())\n    {\n        auto it = --this->base();\n        return it.key();\n    }\n\n    /// return the value of an iterator\n    reference value() const\n    {\n        auto it = --this->base();\n        return it.operator * ();\n    }\n};\n}  // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/iterators/primitive_iterator.hpp",
    "content": "#pragma once\n\n#include <cstddef> // ptrdiff_t\n#include <limits>  // numeric_limits\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/*\n@brief an iterator for primitive JSON types\n\nThis class models an iterator for primitive JSON types (boolean, number,\nstring). It's only purpose is to allow the iterator/const_iterator classes\nto \"iterate\" over primitive values. Internally, the iterator is modeled by\na `difference_type` variable. Value begin_value (`0`) models the begin,\nend_value (`1`) models past the end.\n*/\nclass primitive_iterator_t\n{\n  private:\n    using difference_type = std::ptrdiff_t;\n    static constexpr difference_type begin_value = 0;\n    static constexpr difference_type end_value = begin_value + 1;\n\n    /// iterator as signed integer type\n    difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();\n\n  public:\n    constexpr difference_type get_value() const noexcept\n    {\n        return m_it;\n    }\n\n    /// set iterator to a defined beginning\n    void set_begin() noexcept\n    {\n        m_it = begin_value;\n    }\n\n    /// set iterator to a defined past the end\n    void set_end() noexcept\n    {\n        m_it = end_value;\n    }\n\n    /// return whether the iterator can be dereferenced\n    constexpr bool is_begin() const noexcept\n    {\n        return m_it == begin_value;\n    }\n\n    /// return whether the iterator is at end\n    constexpr bool is_end() const noexcept\n    {\n        return m_it == end_value;\n    }\n\n    friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it == rhs.m_it;\n    }\n\n    friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it < rhs.m_it;\n    }\n\n    primitive_iterator_t operator+(difference_type n) noexcept\n    {\n        auto result = *this;\n        result += n;\n        return result;\n    }\n\n    friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it - rhs.m_it;\n    }\n\n    primitive_iterator_t& operator++() noexcept\n    {\n        ++m_it;\n        return *this;\n    }\n\n    primitive_iterator_t const operator++(int) noexcept\n    {\n        auto result = *this;\n        ++m_it;\n        return result;\n    }\n\n    primitive_iterator_t& operator--() noexcept\n    {\n        --m_it;\n        return *this;\n    }\n\n    primitive_iterator_t const operator--(int) noexcept\n    {\n        auto result = *this;\n        --m_it;\n        return result;\n    }\n\n    primitive_iterator_t& operator+=(difference_type n) noexcept\n    {\n        m_it += n;\n        return *this;\n    }\n\n    primitive_iterator_t& operator-=(difference_type n) noexcept\n    {\n        m_it -= n;\n        return *this;\n    }\n};\n}  // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/json_pointer.hpp",
    "content": "#pragma once\n\n#include <algorithm> // all_of\n#include <cassert> // assert\n#include <cctype> // isdigit\n#include <numeric> // accumulate\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n#include <nlohmann/detail/exceptions.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/value_t.hpp>\n\nnamespace nlohmann\n{\ntemplate<typename BasicJsonType>\nclass json_pointer\n{\n    // allow basic_json to access private members\n    NLOHMANN_BASIC_JSON_TPL_DECLARATION\n    friend class basic_json;\n\n  public:\n    /*!\n    @brief create JSON pointer\n\n    Create a JSON pointer according to the syntax described in\n    [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).\n\n    @param[in] s  string representing the JSON pointer; if omitted, the empty\n                  string is assumed which references the whole JSON value\n\n    @throw parse_error.107 if the given JSON pointer @a s is nonempty and does\n                           not begin with a slash (`/`); see example below\n\n    @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is\n    not followed by `0` (representing `~`) or `1` (representing `/`); see\n    example below\n\n    @liveexample{The example shows the construction several valid JSON pointers\n    as well as the exceptional behavior.,json_pointer}\n\n    @since version 2.0.0\n    */\n    explicit json_pointer(const std::string& s = \"\")\n        : reference_tokens(split(s))\n    {}\n\n    /*!\n    @brief return a string representation of the JSON pointer\n\n    @invariant For each JSON pointer `ptr`, it holds:\n    @code {.cpp}\n    ptr == json_pointer(ptr.to_string());\n    @endcode\n\n    @return a string representation of the JSON pointer\n\n    @liveexample{The example shows the result of `to_string`.,json_pointer__to_string}\n\n    @since version 2.0.0\n    */\n    std::string to_string() const\n    {\n        return std::accumulate(reference_tokens.begin(), reference_tokens.end(),\n                               std::string{},\n                               [](const std::string & a, const std::string & b)\n        {\n            return a + \"/\" + escape(b);\n        });\n    }\n\n    /// @copydoc to_string()\n    operator std::string() const\n    {\n        return to_string();\n    }\n\n    /*!\n    @brief append another JSON pointer at the end of this JSON pointer\n\n    @param[in] ptr  JSON pointer to append\n    @return JSON pointer with @a ptr appended\n\n    @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}\n\n    @complexity Linear in the length of @a ptr.\n\n    @sa @ref operator/=(std::string) to append a reference token\n    @sa @ref operator/=(std::size_t) to append an array index\n    @sa @ref operator/(const json_pointer&, const json_pointer&) for a binary operator\n\n    @since version 3.6.0\n    */\n    json_pointer& operator/=(const json_pointer& ptr)\n    {\n        reference_tokens.insert(reference_tokens.end(),\n                                ptr.reference_tokens.begin(),\n                                ptr.reference_tokens.end());\n        return *this;\n    }\n\n    /*!\n    @brief append an unescaped reference token at the end of this JSON pointer\n\n    @param[in] token  reference token to append\n    @return JSON pointer with @a token appended without escaping @a token\n\n    @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}\n\n    @complexity Amortized constant.\n\n    @sa @ref operator/=(const json_pointer&) to append a JSON pointer\n    @sa @ref operator/=(std::size_t) to append an array index\n    @sa @ref operator/(const json_pointer&, std::size_t) for a binary operator\n\n    @since version 3.6.0\n    */\n    json_pointer& operator/=(std::string token)\n    {\n        push_back(std::move(token));\n        return *this;\n    }\n\n    /*!\n    @brief append an array index at the end of this JSON pointer\n\n    @param[in] array_index  array index to append\n    @return JSON pointer with @a array_index appended\n\n    @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}\n\n    @complexity Amortized constant.\n\n    @sa @ref operator/=(const json_pointer&) to append a JSON pointer\n    @sa @ref operator/=(std::string) to append a reference token\n    @sa @ref operator/(const json_pointer&, std::string) for a binary operator\n\n    @since version 3.6.0\n    */\n    json_pointer& operator/=(std::size_t array_index)\n    {\n        return *this /= std::to_string(array_index);\n    }\n\n    /*!\n    @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer\n\n    @param[in] lhs  JSON pointer\n    @param[in] rhs  JSON pointer\n    @return a new JSON pointer with @a rhs appended to @a lhs\n\n    @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}\n\n    @complexity Linear in the length of @a lhs and @a rhs.\n\n    @sa @ref operator/=(const json_pointer&) to append a JSON pointer\n\n    @since version 3.6.0\n    */\n    friend json_pointer operator/(const json_pointer& lhs,\n                                  const json_pointer& rhs)\n    {\n        return json_pointer(lhs) /= rhs;\n    }\n\n    /*!\n    @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer\n\n    @param[in] ptr  JSON pointer\n    @param[in] token  reference token\n    @return a new JSON pointer with unescaped @a token appended to @a ptr\n\n    @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}\n\n    @complexity Linear in the length of @a ptr.\n\n    @sa @ref operator/=(std::string) to append a reference token\n\n    @since version 3.6.0\n    */\n    friend json_pointer operator/(const json_pointer& ptr, std::string token)\n    {\n        return json_pointer(ptr) /= std::move(token);\n    }\n\n    /*!\n    @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer\n\n    @param[in] ptr  JSON pointer\n    @param[in] array_index  array index\n    @return a new JSON pointer with @a array_index appended to @a ptr\n\n    @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}\n\n    @complexity Linear in the length of @a ptr.\n\n    @sa @ref operator/=(std::size_t) to append an array index\n\n    @since version 3.6.0\n    */\n    friend json_pointer operator/(const json_pointer& ptr, std::size_t array_index)\n    {\n        return json_pointer(ptr) /= array_index;\n    }\n\n    /*!\n    @brief returns the parent of this JSON pointer\n\n    @return parent of this JSON pointer; in case this JSON pointer is the root,\n            the root itself is returned\n\n    @complexity Linear in the length of the JSON pointer.\n\n    @liveexample{The example shows the result of `parent_pointer` for different\n    JSON Pointers.,json_pointer__parent_pointer}\n\n    @since version 3.6.0\n    */\n    json_pointer parent_pointer() const\n    {\n        if (empty())\n        {\n            return *this;\n        }\n\n        json_pointer res = *this;\n        res.pop_back();\n        return res;\n    }\n\n    /*!\n    @brief remove last reference token\n\n    @pre not `empty()`\n\n    @liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back}\n\n    @complexity Constant.\n\n    @throw out_of_range.405 if JSON pointer has no parent\n\n    @since version 3.6.0\n    */\n    void pop_back()\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\"));\n        }\n\n        reference_tokens.pop_back();\n    }\n\n    /*!\n    @brief return last reference token\n\n    @pre not `empty()`\n    @return last reference token\n\n    @liveexample{The example shows the usage of `back`.,json_pointer__back}\n\n    @complexity Constant.\n\n    @throw out_of_range.405 if JSON pointer has no parent\n\n    @since version 3.6.0\n    */\n    const std::string& back() const\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\"));\n        }\n\n        return reference_tokens.back();\n    }\n\n    /*!\n    @brief append an unescaped token at the end of the reference pointer\n\n    @param[in] token  token to add\n\n    @complexity Amortized constant.\n\n    @liveexample{The example shows the result of `push_back` for different\n    JSON Pointers.,json_pointer__push_back}\n\n    @since version 3.6.0\n    */\n    void push_back(const std::string& token)\n    {\n        reference_tokens.push_back(token);\n    }\n\n    /// @copydoc push_back(const std::string&)\n    void push_back(std::string&& token)\n    {\n        reference_tokens.push_back(std::move(token));\n    }\n\n    /*!\n    @brief return whether pointer points to the root document\n\n    @return true iff the JSON pointer points to the root document\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example shows the result of `empty` for different JSON\n    Pointers.,json_pointer__empty}\n\n    @since version 3.6.0\n    */\n    bool empty() const noexcept\n    {\n        return reference_tokens.empty();\n    }\n\n  private:\n    /*!\n    @param[in] s  reference token to be converted into an array index\n\n    @return integer representation of @a s\n\n    @throw out_of_range.404 if string @a s could not be converted to an integer\n    */\n    static int array_index(const std::string& s)\n    {\n        std::size_t processed_chars = 0;\n        const int res = std::stoi(s, &processed_chars);\n\n        // check if the string was completely read\n        if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size()))\n        {\n            JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + s + \"'\"));\n        }\n\n        return res;\n    }\n\n    json_pointer top() const\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\"));\n        }\n\n        json_pointer result = *this;\n        result.reference_tokens = {reference_tokens[0]};\n        return result;\n    }\n\n    /*!\n    @brief create and return a reference to the pointed to value\n\n    @complexity Linear in the number of reference tokens.\n\n    @throw parse_error.109 if array index is not a number\n    @throw type_error.313 if value cannot be unflattened\n    */\n    BasicJsonType& get_and_create(BasicJsonType& j) const\n    {\n        using size_type = typename BasicJsonType::size_type;\n        auto result = &j;\n\n        // in case no reference tokens exist, return a reference to the JSON value\n        // j which will be overwritten by a primitive value\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (result->type())\n            {\n                case detail::value_t::null:\n                {\n                    if (reference_token == \"0\")\n                    {\n                        // start a new array if reference token is 0\n                        result = &result->operator[](0);\n                    }\n                    else\n                    {\n                        // start a new object otherwise\n                        result = &result->operator[](reference_token);\n                    }\n                    break;\n                }\n\n                case detail::value_t::object:\n                {\n                    // create an entry in the object\n                    result = &result->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    // create an entry in the array\n                    JSON_TRY\n                    {\n                        result = &result->operator[](static_cast<size_type>(array_index(reference_token)));\n                    }\n                    JSON_CATCH(std::invalid_argument&)\n                    {\n                        JSON_THROW(detail::parse_error::create(109, 0, \"array index '\" + reference_token + \"' is not a number\"));\n                    }\n                    break;\n                }\n\n                /*\n                The following code is only reached if there exists a reference\n                token _and_ the current value is primitive. In this case, we have\n                an error situation, because primitive values may only occur as\n                single value; that is, with an empty list of reference tokens.\n                */\n                default:\n                    JSON_THROW(detail::type_error::create(313, \"invalid value to unflatten\"));\n            }\n        }\n\n        return *result;\n    }\n\n    /*!\n    @brief return a reference to the pointed to value\n\n    @note This version does not throw if a value is not present, but tries to\n          create nested values instead. For instance, calling this function\n          with pointer `\"/this/that\"` on a null value is equivalent to calling\n          `operator[](\"this\").operator[](\"that\")` on that value, effectively\n          changing the null value to an object.\n\n    @param[in] ptr  a JSON value\n\n    @return reference to the JSON value pointed to by the JSON pointer\n\n    @complexity Linear in the length of the JSON pointer.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    BasicJsonType& get_unchecked(BasicJsonType* ptr) const\n    {\n        using size_type = typename BasicJsonType::size_type;\n        for (const auto& reference_token : reference_tokens)\n        {\n            // convert null values to arrays or objects before continuing\n            if (ptr->is_null())\n            {\n                // check if reference token is a number\n                const bool nums =\n                    std::all_of(reference_token.begin(), reference_token.end(),\n                                [](const unsigned char x)\n                {\n                    return std::isdigit(x);\n                });\n\n                // change value to array for numbers or \"-\" or to object otherwise\n                *ptr = (nums or reference_token == \"-\")\n                       ? detail::value_t::array\n                       : detail::value_t::object;\n            }\n\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // use unchecked object access\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    // error condition (cf. RFC 6901, Sect. 4)\n                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))\n                    {\n                        JSON_THROW(detail::parse_error::create(106, 0,\n                                                               \"array index '\" + reference_token +\n                                                               \"' must not begin with '0'\"));\n                    }\n\n                    if (reference_token == \"-\")\n                    {\n                        // explicitly treat \"-\" as index beyond the end\n                        ptr = &ptr->operator[](ptr->m_value.array->size());\n                    }\n                    else\n                    {\n                        // convert array index to number; unchecked access\n                        JSON_TRY\n                        {\n                            ptr = &ptr->operator[](\n                                static_cast<size_type>(array_index(reference_token)));\n                        }\n                        JSON_CATCH(std::invalid_argument&)\n                        {\n                            JSON_THROW(detail::parse_error::create(109, 0, \"array index '\" + reference_token + \"' is not a number\"));\n                        }\n                    }\n                    break;\n                }\n\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\"));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    BasicJsonType& get_checked(BasicJsonType* ptr) const\n    {\n        using size_type = typename BasicJsonType::size_type;\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // note: at performs range check\n                    ptr = &ptr->at(reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        JSON_THROW(detail::out_of_range::create(402,\n                                                                \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) +\n                                                                \") is out of range\"));\n                    }\n\n                    // error condition (cf. RFC 6901, Sect. 4)\n                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))\n                    {\n                        JSON_THROW(detail::parse_error::create(106, 0,\n                                                               \"array index '\" + reference_token +\n                                                               \"' must not begin with '0'\"));\n                    }\n\n                    // note: at performs range check\n                    JSON_TRY\n                    {\n                        ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));\n                    }\n                    JSON_CATCH(std::invalid_argument&)\n                    {\n                        JSON_THROW(detail::parse_error::create(109, 0, \"array index '\" + reference_token + \"' is not a number\"));\n                    }\n                    break;\n                }\n\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\"));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @brief return a const reference to the pointed to value\n\n    @param[in] ptr  a JSON value\n\n    @return const reference to the JSON value pointed to by the JSON\n    pointer\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const\n    {\n        using size_type = typename BasicJsonType::size_type;\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // use unchecked object access\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" cannot be used for const access\n                        JSON_THROW(detail::out_of_range::create(402,\n                                                                \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) +\n                                                                \") is out of range\"));\n                    }\n\n                    // error condition (cf. RFC 6901, Sect. 4)\n                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))\n                    {\n                        JSON_THROW(detail::parse_error::create(106, 0,\n                                                               \"array index '\" + reference_token +\n                                                               \"' must not begin with '0'\"));\n                    }\n\n                    // use unchecked array access\n                    JSON_TRY\n                    {\n                        ptr = &ptr->operator[](\n                            static_cast<size_type>(array_index(reference_token)));\n                    }\n                    JSON_CATCH(std::invalid_argument&)\n                    {\n                        JSON_THROW(detail::parse_error::create(109, 0, \"array index '\" + reference_token + \"' is not a number\"));\n                    }\n                    break;\n                }\n\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\"));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    const BasicJsonType& get_checked(const BasicJsonType* ptr) const\n    {\n        using size_type = typename BasicJsonType::size_type;\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // note: at performs range check\n                    ptr = &ptr->at(reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        JSON_THROW(detail::out_of_range::create(402,\n                                                                \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) +\n                                                                \") is out of range\"));\n                    }\n\n                    // error condition (cf. RFC 6901, Sect. 4)\n                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))\n                    {\n                        JSON_THROW(detail::parse_error::create(106, 0,\n                                                               \"array index '\" + reference_token +\n                                                               \"' must not begin with '0'\"));\n                    }\n\n                    // note: at performs range check\n                    JSON_TRY\n                    {\n                        ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));\n                    }\n                    JSON_CATCH(std::invalid_argument&)\n                    {\n                        JSON_THROW(detail::parse_error::create(109, 0, \"array index '\" + reference_token + \"' is not a number\"));\n                    }\n                    break;\n                }\n\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\"));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    */\n    bool contains(const BasicJsonType* ptr) const\n    {\n        using size_type = typename BasicJsonType::size_type;\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    if (not ptr->contains(reference_token))\n                    {\n                        // we did not find the key in the object\n                        return false;\n                    }\n\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        return false;\n                    }\n\n                    // error condition (cf. RFC 6901, Sect. 4)\n                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))\n                    {\n                        JSON_THROW(detail::parse_error::create(106, 0,\n                                                               \"array index '\" + reference_token +\n                                                               \"' must not begin with '0'\"));\n                    }\n\n                    JSON_TRY\n                    {\n                        const auto idx = static_cast<size_type>(array_index(reference_token));\n                        if (idx >= ptr->size())\n                        {\n                            // index out of range\n                            return false;\n                        }\n\n                        ptr = &ptr->operator[](idx);\n                        break;\n                    }\n                    JSON_CATCH(std::invalid_argument&)\n                    {\n                        JSON_THROW(detail::parse_error::create(109, 0, \"array index '\" + reference_token + \"' is not a number\"));\n                    }\n                    break;\n                }\n\n                default:\n                {\n                    // we do not expect primitive values if there is still a\n                    // reference token to process\n                    return false;\n                }\n            }\n        }\n\n        // no reference token left means we found a primitive value\n        return true;\n    }\n\n    /*!\n    @brief split the string input to reference tokens\n\n    @note This function is only called by the json_pointer constructor.\n          All exceptions below are documented there.\n\n    @throw parse_error.107  if the pointer is not empty or begins with '/'\n    @throw parse_error.108  if character '~' is not followed by '0' or '1'\n    */\n    static std::vector<std::string> split(const std::string& reference_string)\n    {\n        std::vector<std::string> result;\n\n        // special case: empty reference string -> no reference tokens\n        if (reference_string.empty())\n        {\n            return result;\n        }\n\n        // check if nonempty reference string begins with slash\n        if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))\n        {\n            JSON_THROW(detail::parse_error::create(107, 1,\n                                                   \"JSON pointer must be empty or begin with '/' - was: '\" +\n                                                   reference_string + \"'\"));\n        }\n\n        // extract the reference tokens:\n        // - slash: position of the last read slash (or end of string)\n        // - start: position after the previous slash\n        for (\n            // search for the first slash after the first character\n            std::size_t slash = reference_string.find_first_of('/', 1),\n            // set the beginning of the first reference token\n            start = 1;\n            // we can stop if start == 0 (if slash == std::string::npos)\n            start != 0;\n            // set the beginning of the next reference token\n            // (will eventually be 0 if slash == std::string::npos)\n            start = (slash == std::string::npos) ? 0 : slash + 1,\n            // find next slash\n            slash = reference_string.find_first_of('/', start))\n        {\n            // use the text between the beginning of the reference token\n            // (start) and the last slash (slash).\n            auto reference_token = reference_string.substr(start, slash - start);\n\n            // check reference tokens are properly escaped\n            for (std::size_t pos = reference_token.find_first_of('~');\n                    pos != std::string::npos;\n                    pos = reference_token.find_first_of('~', pos + 1))\n            {\n                assert(reference_token[pos] == '~');\n\n                // ~ must be followed by 0 or 1\n                if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 or\n                                         (reference_token[pos + 1] != '0' and\n                                          reference_token[pos + 1] != '1')))\n                {\n                    JSON_THROW(detail::parse_error::create(108, 0, \"escape character '~' must be followed with '0' or '1'\"));\n                }\n            }\n\n            // finally, store the reference token\n            unescape(reference_token);\n            result.push_back(reference_token);\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief replace all occurrences of a substring by another string\n\n    @param[in,out] s  the string to manipulate; changed so that all\n                   occurrences of @a f are replaced with @a t\n    @param[in]     f  the substring to replace with @a t\n    @param[in]     t  the string to replace @a f\n\n    @pre The search string @a f must not be empty. **This precondition is\n    enforced with an assertion.**\n\n    @since version 2.0.0\n    */\n    static void replace_substring(std::string& s, const std::string& f,\n                                  const std::string& t)\n    {\n        assert(not f.empty());\n        for (auto pos = s.find(f);                // find first occurrence of f\n                pos != std::string::npos;         // make sure f was found\n                s.replace(pos, f.size(), t),      // replace with t, and\n                pos = s.find(f, pos + t.size()))  // find next occurrence of f\n        {}\n    }\n\n    /// escape \"~\" to \"~0\" and \"/\" to \"~1\"\n    static std::string escape(std::string s)\n    {\n        replace_substring(s, \"~\", \"~0\");\n        replace_substring(s, \"/\", \"~1\");\n        return s;\n    }\n\n    /// unescape \"~1\" to tilde and \"~0\" to slash (order is important!)\n    static void unescape(std::string& s)\n    {\n        replace_substring(s, \"~1\", \"/\");\n        replace_substring(s, \"~0\", \"~\");\n    }\n\n    /*!\n    @param[in] reference_string  the reference string to the current value\n    @param[in] value             the value to consider\n    @param[in,out] result        the result object to insert values to\n\n    @note Empty objects or arrays are flattened to `null`.\n    */\n    static void flatten(const std::string& reference_string,\n                        const BasicJsonType& value,\n                        BasicJsonType& result)\n    {\n        switch (value.type())\n        {\n            case detail::value_t::array:\n            {\n                if (value.m_value.array->empty())\n                {\n                    // flatten empty array as null\n                    result[reference_string] = nullptr;\n                }\n                else\n                {\n                    // iterate array and use index as reference string\n                    for (std::size_t i = 0; i < value.m_value.array->size(); ++i)\n                    {\n                        flatten(reference_string + \"/\" + std::to_string(i),\n                                value.m_value.array->operator[](i), result);\n                    }\n                }\n                break;\n            }\n\n            case detail::value_t::object:\n            {\n                if (value.m_value.object->empty())\n                {\n                    // flatten empty object as null\n                    result[reference_string] = nullptr;\n                }\n                else\n                {\n                    // iterate object and use keys as reference string\n                    for (const auto& element : *value.m_value.object)\n                    {\n                        flatten(reference_string + \"/\" + escape(element.first), element.second, result);\n                    }\n                }\n                break;\n            }\n\n            default:\n            {\n                // add primitive value with its reference string\n                result[reference_string] = value;\n                break;\n            }\n        }\n    }\n\n    /*!\n    @param[in] value  flattened JSON\n\n    @return unflattened JSON\n\n    @throw parse_error.109 if array index is not a number\n    @throw type_error.314  if value is not an object\n    @throw type_error.315  if object values are not primitive\n    @throw type_error.313  if value cannot be unflattened\n    */\n    static BasicJsonType\n    unflatten(const BasicJsonType& value)\n    {\n        if (JSON_HEDLEY_UNLIKELY(not value.is_object()))\n        {\n            JSON_THROW(detail::type_error::create(314, \"only objects can be unflattened\"));\n        }\n\n        BasicJsonType result;\n\n        // iterate the JSON object values\n        for (const auto& element : *value.m_value.object)\n        {\n            if (JSON_HEDLEY_UNLIKELY(not element.second.is_primitive()))\n            {\n                JSON_THROW(detail::type_error::create(315, \"values in object must be primitive\"));\n            }\n\n            // assign value to reference pointed to by JSON pointer; Note that if\n            // the JSON pointer is \"\" (i.e., points to the whole value), function\n            // get_and_create returns a reference to result itself. An assignment\n            // will then create a primitive value.\n            json_pointer(element.first).get_and_create(result) = element.second;\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief compares two JSON pointers for equality\n\n    @param[in] lhs  JSON pointer to compare\n    @param[in] rhs  JSON pointer to compare\n    @return whether @a lhs is equal to @a rhs\n\n    @complexity Linear in the length of the JSON pointer\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n    */\n    friend bool operator==(json_pointer const& lhs,\n                           json_pointer const& rhs) noexcept\n    {\n        return lhs.reference_tokens == rhs.reference_tokens;\n    }\n\n    /*!\n    @brief compares two JSON pointers for inequality\n\n    @param[in] lhs  JSON pointer to compare\n    @param[in] rhs  JSON pointer to compare\n    @return whether @a lhs is not equal @a rhs\n\n    @complexity Linear in the length of the JSON pointer\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n    */\n    friend bool operator!=(json_pointer const& lhs,\n                           json_pointer const& rhs) noexcept\n    {\n        return not (lhs == rhs);\n    }\n\n    /// the reference tokens\n    std::vector<std::string> reference_tokens;\n};\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/json_ref.hpp",
    "content": "#pragma once\n\n#include <initializer_list>\n#include <utility>\n\n#include <nlohmann/detail/meta/type_traits.hpp>\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename BasicJsonType>\nclass json_ref\n{\n  public:\n    using value_type = BasicJsonType;\n\n    json_ref(value_type&& value)\n        : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true)\n    {}\n\n    json_ref(const value_type& value)\n        : value_ref(const_cast<value_type*>(&value)), is_rvalue(false)\n    {}\n\n    json_ref(std::initializer_list<json_ref> init)\n        : owned_value(init), value_ref(&owned_value), is_rvalue(true)\n    {}\n\n    template <\n        class... Args,\n        enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 >\n    json_ref(Args && ... args)\n        : owned_value(std::forward<Args>(args)...), value_ref(&owned_value),\n          is_rvalue(true) {}\n\n    // class should be movable only\n    json_ref(json_ref&&) = default;\n    json_ref(const json_ref&) = delete;\n    json_ref& operator=(const json_ref&) = delete;\n    json_ref& operator=(json_ref&&) = delete;\n    ~json_ref() = default;\n\n    value_type moved_or_copied() const\n    {\n        if (is_rvalue)\n        {\n            return std::move(*value_ref);\n        }\n        return *value_ref;\n    }\n\n    value_type const& operator*() const\n    {\n        return *static_cast<value_type const*>(value_ref);\n    }\n\n    value_type const* operator->() const\n    {\n        return static_cast<value_type const*>(value_ref);\n    }\n\n  private:\n    mutable value_type owned_value = nullptr;\n    value_type* value_ref = nullptr;\n    const bool is_rvalue;\n};\n}  // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/macro_scope.hpp",
    "content": "#pragma once\n\n#include <utility> // pair\n#include <nlohmann/thirdparty/hedley/hedley.hpp>\n\n// This file contains all internal macro definitions\n// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them\n\n// exclude unsupported compilers\n#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)\n    #if defined(__clang__)\n        #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400\n            #error \"unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers\"\n        #endif\n    #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))\n        #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800\n            #error \"unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers\"\n        #endif\n    #endif\n#endif\n\n// C++ language standard detection\n#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464\n    #define JSON_HAS_CPP_17\n    #define JSON_HAS_CPP_14\n#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)\n    #define JSON_HAS_CPP_14\n#endif\n\n// disable float-equal warnings on GCC/clang\n#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#endif\n\n// disable documentation warnings on clang\n#if defined(__clang__)\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Wdocumentation\"\n#endif\n\n// allow to disable exceptions\n#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)\n    #define JSON_THROW(exception) throw exception\n    #define JSON_TRY try\n    #define JSON_CATCH(exception) catch(exception)\n    #define JSON_INTERNAL_CATCH(exception) catch(exception)\n#else\n    #include <cstdlib>\n    #define JSON_THROW(exception) std::abort()\n    #define JSON_TRY if(true)\n    #define JSON_CATCH(exception) if(false)\n    #define JSON_INTERNAL_CATCH(exception) if(false)\n#endif\n\n// override exception macros\n#if defined(JSON_THROW_USER)\n    #undef JSON_THROW\n    #define JSON_THROW JSON_THROW_USER\n#endif\n#if defined(JSON_TRY_USER)\n    #undef JSON_TRY\n    #define JSON_TRY JSON_TRY_USER\n#endif\n#if defined(JSON_CATCH_USER)\n    #undef JSON_CATCH\n    #define JSON_CATCH JSON_CATCH_USER\n    #undef JSON_INTERNAL_CATCH\n    #define JSON_INTERNAL_CATCH JSON_CATCH_USER\n#endif\n#if defined(JSON_INTERNAL_CATCH_USER)\n    #undef JSON_INTERNAL_CATCH\n    #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER\n#endif\n\n/*!\n@brief macro to briefly define a mapping between an enum and JSON\n@def NLOHMANN_JSON_SERIALIZE_ENUM\n@since version 3.4.0\n*/\n#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...)                                            \\\n    template<typename BasicJsonType>                                                            \\\n    inline void to_json(BasicJsonType& j, const ENUM_TYPE& e)                                   \\\n    {                                                                                           \\\n        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE \" must be an enum!\");          \\\n        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \\\n        auto it = std::find_if(std::begin(m), std::end(m),                                      \\\n                               [e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool  \\\n        {                                                                                       \\\n            return ej_pair.first == e;                                                          \\\n        });                                                                                     \\\n        j = ((it != std::end(m)) ? it : std::begin(m))->second;                                 \\\n    }                                                                                           \\\n    template<typename BasicJsonType>                                                            \\\n    inline void from_json(const BasicJsonType& j, ENUM_TYPE& e)                                 \\\n    {                                                                                           \\\n        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE \" must be an enum!\");          \\\n        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \\\n        auto it = std::find_if(std::begin(m), std::end(m),                                      \\\n                               [&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \\\n        {                                                                                       \\\n            return ej_pair.second == j;                                                         \\\n        });                                                                                     \\\n        e = ((it != std::end(m)) ? it : std::begin(m))->first;                                  \\\n    }\n\n// Ugly macros to avoid uglier copy-paste when specializing basic_json. They\n// may be removed in the future once the class is split.\n\n#define NLOHMANN_BASIC_JSON_TPL_DECLARATION                                \\\n    template<template<typename, typename, typename...> class ObjectType,   \\\n             template<typename, typename...> class ArrayType,              \\\n             class StringType, class BooleanType, class NumberIntegerType, \\\n             class NumberUnsignedType, class NumberFloatType,              \\\n             template<typename> class AllocatorType,                       \\\n             template<typename, typename = void> class JSONSerializer>\n\n#define NLOHMANN_BASIC_JSON_TPL                                            \\\n    basic_json<ObjectType, ArrayType, StringType, BooleanType,             \\\n    NumberIntegerType, NumberUnsignedType, NumberFloatType,                \\\n    AllocatorType, JSONSerializer>\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/macro_unscope.hpp",
    "content": "#pragma once\n\n// restore GCC/clang diagnostic settings\n#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)\n    #pragma GCC diagnostic pop\n#endif\n#if defined(__clang__)\n    #pragma GCC diagnostic pop\n#endif\n\n// clean up\n#undef JSON_INTERNAL_CATCH\n#undef JSON_CATCH\n#undef JSON_THROW\n#undef JSON_TRY\n#undef JSON_HAS_CPP_14\n#undef JSON_HAS_CPP_17\n#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION\n#undef NLOHMANN_BASIC_JSON_TPL\n\n#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/meta/cpp_future.hpp",
    "content": "#pragma once\n\n#include <ciso646> // not\n#include <cstddef> // size_t\n#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type\n\nnamespace nlohmann\n{\nnamespace detail\n{\n// alias templates to reduce boilerplate\ntemplate<bool B, typename T = void>\nusing enable_if_t = typename std::enable_if<B, T>::type;\n\ntemplate<typename T>\nusing uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;\n\n// implementation of C++14 index_sequence and affiliates\n// source: https://stackoverflow.com/a/32223343\ntemplate<std::size_t... Ints>\nstruct index_sequence\n{\n    using type = index_sequence;\n    using value_type = std::size_t;\n    static constexpr std::size_t size() noexcept\n    {\n        return sizeof...(Ints);\n    }\n};\n\ntemplate<class Sequence1, class Sequence2>\nstruct merge_and_renumber;\n\ntemplate<std::size_t... I1, std::size_t... I2>\nstruct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>\n        : index_sequence < I1..., (sizeof...(I1) + I2)... > {};\n\ntemplate<std::size_t N>\nstruct make_index_sequence\n    : merge_and_renumber < typename make_index_sequence < N / 2 >::type,\n      typename make_index_sequence < N - N / 2 >::type > {};\n\ntemplate<> struct make_index_sequence<0> : index_sequence<> {};\ntemplate<> struct make_index_sequence<1> : index_sequence<0> {};\n\ntemplate<typename... Ts>\nusing index_sequence_for = make_index_sequence<sizeof...(Ts)>;\n\n// dispatch utility (taken from ranges-v3)\ntemplate<unsigned N> struct priority_tag : priority_tag < N - 1 > {};\ntemplate<> struct priority_tag<0> {};\n\n// taken from ranges-v3\ntemplate<typename T>\nstruct static_const\n{\n    static constexpr T value{};\n};\n\ntemplate<typename T>\nconstexpr T static_const<T>::value;\n}  // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/meta/detected.hpp",
    "content": "#pragma once\n\n#include <type_traits>\n\n#include <nlohmann/detail/meta/void_t.hpp>\n\n// http://en.cppreference.com/w/cpp/experimental/is_detected\nnamespace nlohmann\n{\nnamespace detail\n{\nstruct nonesuch\n{\n    nonesuch() = delete;\n    ~nonesuch() = delete;\n    nonesuch(nonesuch const&) = delete;\n    nonesuch(nonesuch const&&) = delete;\n    void operator=(nonesuch const&) = delete;\n    void operator=(nonesuch&&) = delete;\n};\n\ntemplate <class Default,\n          class AlwaysVoid,\n          template <class...> class Op,\n          class... Args>\nstruct detector\n{\n    using value_t = std::false_type;\n    using type = Default;\n};\n\ntemplate <class Default, template <class...> class Op, class... Args>\nstruct detector<Default, void_t<Op<Args...>>, Op, Args...>\n{\n    using value_t = std::true_type;\n    using type = Op<Args...>;\n};\n\ntemplate <template <class...> class Op, class... Args>\nusing is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;\n\ntemplate <template <class...> class Op, class... Args>\nusing detected_t = typename detector<nonesuch, void, Op, Args...>::type;\n\ntemplate <class Default, template <class...> class Op, class... Args>\nusing detected_or = detector<Default, void, Op, Args...>;\n\ntemplate <class Default, template <class...> class Op, class... Args>\nusing detected_or_t = typename detected_or<Default, Op, Args...>::type;\n\ntemplate <class Expected, template <class...> class Op, class... Args>\nusing is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;\n\ntemplate <class To, template <class...> class Op, class... Args>\nusing is_detected_convertible =\n    std::is_convertible<detected_t<Op, Args...>, To>;\n}  // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/meta/is_sax.hpp",
    "content": "#pragma once\n\n#include <cstdint> // size_t\n#include <utility> // declval\n#include <string> // string\n\n#include <nlohmann/detail/meta/detected.hpp>\n#include <nlohmann/detail/meta/type_traits.hpp>\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate <typename T>\nusing null_function_t = decltype(std::declval<T&>().null());\n\ntemplate <typename T>\nusing boolean_function_t =\n    decltype(std::declval<T&>().boolean(std::declval<bool>()));\n\ntemplate <typename T, typename Integer>\nusing number_integer_function_t =\n    decltype(std::declval<T&>().number_integer(std::declval<Integer>()));\n\ntemplate <typename T, typename Unsigned>\nusing number_unsigned_function_t =\n    decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>()));\n\ntemplate <typename T, typename Float, typename String>\nusing number_float_function_t = decltype(std::declval<T&>().number_float(\n                                    std::declval<Float>(), std::declval<const String&>()));\n\ntemplate <typename T, typename String>\nusing string_function_t =\n    decltype(std::declval<T&>().string(std::declval<String&>()));\n\ntemplate <typename T>\nusing start_object_function_t =\n    decltype(std::declval<T&>().start_object(std::declval<std::size_t>()));\n\ntemplate <typename T, typename String>\nusing key_function_t =\n    decltype(std::declval<T&>().key(std::declval<String&>()));\n\ntemplate <typename T>\nusing end_object_function_t = decltype(std::declval<T&>().end_object());\n\ntemplate <typename T>\nusing start_array_function_t =\n    decltype(std::declval<T&>().start_array(std::declval<std::size_t>()));\n\ntemplate <typename T>\nusing end_array_function_t = decltype(std::declval<T&>().end_array());\n\ntemplate <typename T, typename Exception>\nusing parse_error_function_t = decltype(std::declval<T&>().parse_error(\n        std::declval<std::size_t>(), std::declval<const std::string&>(),\n        std::declval<const Exception&>()));\n\ntemplate <typename SAX, typename BasicJsonType>\nstruct is_sax\n{\n  private:\n    static_assert(is_basic_json<BasicJsonType>::value,\n                  \"BasicJsonType must be of type basic_json<...>\");\n\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using exception_t = typename BasicJsonType::exception;\n\n  public:\n    static constexpr bool value =\n        is_detected_exact<bool, null_function_t, SAX>::value &&\n        is_detected_exact<bool, boolean_function_t, SAX>::value &&\n        is_detected_exact<bool, number_integer_function_t, SAX,\n        number_integer_t>::value &&\n        is_detected_exact<bool, number_unsigned_function_t, SAX,\n        number_unsigned_t>::value &&\n        is_detected_exact<bool, number_float_function_t, SAX, number_float_t,\n        string_t>::value &&\n        is_detected_exact<bool, string_function_t, SAX, string_t>::value &&\n        is_detected_exact<bool, start_object_function_t, SAX>::value &&\n        is_detected_exact<bool, key_function_t, SAX, string_t>::value &&\n        is_detected_exact<bool, end_object_function_t, SAX>::value &&\n        is_detected_exact<bool, start_array_function_t, SAX>::value &&\n        is_detected_exact<bool, end_array_function_t, SAX>::value &&\n        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;\n};\n\ntemplate <typename SAX, typename BasicJsonType>\nstruct is_sax_static_asserts\n{\n  private:\n    static_assert(is_basic_json<BasicJsonType>::value,\n                  \"BasicJsonType must be of type basic_json<...>\");\n\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using exception_t = typename BasicJsonType::exception;\n\n  public:\n    static_assert(is_detected_exact<bool, null_function_t, SAX>::value,\n                  \"Missing/invalid function: bool null()\");\n    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,\n                  \"Missing/invalid function: bool boolean(bool)\");\n    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,\n                  \"Missing/invalid function: bool boolean(bool)\");\n    static_assert(\n        is_detected_exact<bool, number_integer_function_t, SAX,\n        number_integer_t>::value,\n        \"Missing/invalid function: bool number_integer(number_integer_t)\");\n    static_assert(\n        is_detected_exact<bool, number_unsigned_function_t, SAX,\n        number_unsigned_t>::value,\n        \"Missing/invalid function: bool number_unsigned(number_unsigned_t)\");\n    static_assert(is_detected_exact<bool, number_float_function_t, SAX,\n                  number_float_t, string_t>::value,\n                  \"Missing/invalid function: bool number_float(number_float_t, const string_t&)\");\n    static_assert(\n        is_detected_exact<bool, string_function_t, SAX, string_t>::value,\n        \"Missing/invalid function: bool string(string_t&)\");\n    static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value,\n                  \"Missing/invalid function: bool start_object(std::size_t)\");\n    static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value,\n                  \"Missing/invalid function: bool key(string_t&)\");\n    static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value,\n                  \"Missing/invalid function: bool end_object()\");\n    static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value,\n                  \"Missing/invalid function: bool start_array(std::size_t)\");\n    static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value,\n                  \"Missing/invalid function: bool end_array()\");\n    static_assert(\n        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value,\n        \"Missing/invalid function: bool parse_error(std::size_t, const \"\n        \"std::string&, const exception&)\");\n};\n}  // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/meta/type_traits.hpp",
    "content": "#pragma once\n\n#include <ciso646> // not\n#include <limits> // numeric_limits\n#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type\n#include <utility> // declval\n\n#include <nlohmann/detail/iterators/iterator_traits.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/meta/cpp_future.hpp>\n#include <nlohmann/detail/meta/detected.hpp>\n#include <nlohmann/json_fwd.hpp>\n\nnamespace nlohmann\n{\n/*!\n@brief detail namespace with internal helper functions\n\nThis namespace collects functions that should not be exposed,\nimplementations of some @ref basic_json methods, and meta-programming helpers.\n\n@since version 2.1.0\n*/\nnamespace detail\n{\n/////////////\n// helpers //\n/////////////\n\n// Note to maintainers:\n//\n// Every trait in this file expects a non CV-qualified type.\n// The only exceptions are in the 'aliases for detected' section\n// (i.e. those of the form: decltype(T::member_function(std::declval<T>())))\n//\n// In this case, T has to be properly CV-qualified to constraint the function arguments\n// (e.g. to_json(BasicJsonType&, const T&))\n\ntemplate<typename> struct is_basic_json : std::false_type {};\n\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nstruct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};\n\n//////////////////////////\n// aliases for detected //\n//////////////////////////\n\ntemplate <typename T>\nusing mapped_type_t = typename T::mapped_type;\n\ntemplate <typename T>\nusing key_type_t = typename T::key_type;\n\ntemplate <typename T>\nusing value_type_t = typename T::value_type;\n\ntemplate <typename T>\nusing difference_type_t = typename T::difference_type;\n\ntemplate <typename T>\nusing pointer_t = typename T::pointer;\n\ntemplate <typename T>\nusing reference_t = typename T::reference;\n\ntemplate <typename T>\nusing iterator_category_t = typename T::iterator_category;\n\ntemplate <typename T>\nusing iterator_t = typename T::iterator;\n\ntemplate <typename T, typename... Args>\nusing to_json_function = decltype(T::to_json(std::declval<Args>()...));\n\ntemplate <typename T, typename... Args>\nusing from_json_function = decltype(T::from_json(std::declval<Args>()...));\n\ntemplate <typename T, typename U>\nusing get_template_function = decltype(std::declval<T>().template get<U>());\n\n// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists\ntemplate <typename BasicJsonType, typename T, typename = void>\nstruct has_from_json : std::false_type {};\n\ntemplate <typename BasicJsonType, typename T>\nstruct has_from_json<BasicJsonType, T,\n           enable_if_t<not is_basic_json<T>::value>>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<void, from_json_function, serializer,\n        const BasicJsonType&, T&>::value;\n};\n\n// This trait checks if JSONSerializer<T>::from_json(json const&) exists\n// this overload is used for non-default-constructible user-defined-types\ntemplate <typename BasicJsonType, typename T, typename = void>\nstruct has_non_default_from_json : std::false_type {};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_non_default_from_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<T, from_json_function, serializer,\n        const BasicJsonType&>::value;\n};\n\n// This trait checks if BasicJsonType::json_serializer<T>::to_json exists\n// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.\ntemplate <typename BasicJsonType, typename T, typename = void>\nstruct has_to_json : std::false_type {};\n\ntemplate <typename BasicJsonType, typename T>\nstruct has_to_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<void, to_json_function, serializer, BasicJsonType&,\n        T>::value;\n};\n\n\n///////////////////\n// is_ functions //\n///////////////////\n\ntemplate <typename T, typename = void>\nstruct is_iterator_traits : std::false_type {};\n\ntemplate <typename T>\nstruct is_iterator_traits<iterator_traits<T>>\n{\n  private:\n    using traits = iterator_traits<T>;\n\n  public:\n    static constexpr auto value =\n        is_detected<value_type_t, traits>::value &&\n        is_detected<difference_type_t, traits>::value &&\n        is_detected<pointer_t, traits>::value &&\n        is_detected<iterator_category_t, traits>::value &&\n        is_detected<reference_t, traits>::value;\n};\n\n// source: https://stackoverflow.com/a/37193089/4116453\n\ntemplate <typename T, typename = void>\nstruct is_complete_type : std::false_type {};\n\ntemplate <typename T>\nstruct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};\n\ntemplate <typename BasicJsonType, typename CompatibleObjectType,\n          typename = void>\nstruct is_compatible_object_type_impl : std::false_type {};\n\ntemplate <typename BasicJsonType, typename CompatibleObjectType>\nstruct is_compatible_object_type_impl <\n    BasicJsonType, CompatibleObjectType,\n    enable_if_t<is_detected<mapped_type_t, CompatibleObjectType>::value and\n    is_detected<key_type_t, CompatibleObjectType>::value >>\n{\n\n    using object_t = typename BasicJsonType::object_t;\n\n    // macOS's is_constructible does not play well with nonesuch...\n    static constexpr bool value =\n        std::is_constructible<typename object_t::key_type,\n        typename CompatibleObjectType::key_type>::value and\n        std::is_constructible<typename object_t::mapped_type,\n        typename CompatibleObjectType::mapped_type>::value;\n};\n\ntemplate <typename BasicJsonType, typename CompatibleObjectType>\nstruct is_compatible_object_type\n    : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};\n\ntemplate <typename BasicJsonType, typename ConstructibleObjectType,\n          typename = void>\nstruct is_constructible_object_type_impl : std::false_type {};\n\ntemplate <typename BasicJsonType, typename ConstructibleObjectType>\nstruct is_constructible_object_type_impl <\n    BasicJsonType, ConstructibleObjectType,\n    enable_if_t<is_detected<mapped_type_t, ConstructibleObjectType>::value and\n    is_detected<key_type_t, ConstructibleObjectType>::value >>\n{\n    using object_t = typename BasicJsonType::object_t;\n\n    static constexpr bool value =\n        (std::is_default_constructible<ConstructibleObjectType>::value and\n         (std::is_move_assignable<ConstructibleObjectType>::value or\n          std::is_copy_assignable<ConstructibleObjectType>::value) and\n         (std::is_constructible<typename ConstructibleObjectType::key_type,\n          typename object_t::key_type>::value and\n          std::is_same <\n          typename object_t::mapped_type,\n          typename ConstructibleObjectType::mapped_type >::value)) or\n        (has_from_json<BasicJsonType,\n         typename ConstructibleObjectType::mapped_type>::value or\n         has_non_default_from_json <\n         BasicJsonType,\n         typename ConstructibleObjectType::mapped_type >::value);\n};\n\ntemplate <typename BasicJsonType, typename ConstructibleObjectType>\nstruct is_constructible_object_type\n    : is_constructible_object_type_impl<BasicJsonType,\n      ConstructibleObjectType> {};\n\ntemplate <typename BasicJsonType, typename CompatibleStringType,\n          typename = void>\nstruct is_compatible_string_type_impl : std::false_type {};\n\ntemplate <typename BasicJsonType, typename CompatibleStringType>\nstruct is_compatible_string_type_impl <\n    BasicJsonType, CompatibleStringType,\n    enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type,\n    value_type_t, CompatibleStringType>::value >>\n{\n    static constexpr auto value =\n        std::is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;\n};\n\ntemplate <typename BasicJsonType, typename ConstructibleStringType>\nstruct is_compatible_string_type\n    : is_compatible_string_type_impl<BasicJsonType, ConstructibleStringType> {};\n\ntemplate <typename BasicJsonType, typename ConstructibleStringType,\n          typename = void>\nstruct is_constructible_string_type_impl : std::false_type {};\n\ntemplate <typename BasicJsonType, typename ConstructibleStringType>\nstruct is_constructible_string_type_impl <\n    BasicJsonType, ConstructibleStringType,\n    enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type,\n    value_type_t, ConstructibleStringType>::value >>\n{\n    static constexpr auto value =\n        std::is_constructible<ConstructibleStringType,\n        typename BasicJsonType::string_t>::value;\n};\n\ntemplate <typename BasicJsonType, typename ConstructibleStringType>\nstruct is_constructible_string_type\n    : is_constructible_string_type_impl<BasicJsonType, ConstructibleStringType> {};\n\ntemplate <typename BasicJsonType, typename CompatibleArrayType, typename = void>\nstruct is_compatible_array_type_impl : std::false_type {};\n\ntemplate <typename BasicJsonType, typename CompatibleArrayType>\nstruct is_compatible_array_type_impl <\n    BasicJsonType, CompatibleArrayType,\n    enable_if_t<is_detected<value_type_t, CompatibleArrayType>::value and\n    is_detected<iterator_t, CompatibleArrayType>::value and\n// This is needed because json_reverse_iterator has a ::iterator type...\n// Therefore it is detected as a CompatibleArrayType.\n// The real fix would be to have an Iterable concept.\n    not is_iterator_traits<\n    iterator_traits<CompatibleArrayType>>::value >>\n{\n    static constexpr bool value =\n        std::is_constructible<BasicJsonType,\n        typename CompatibleArrayType::value_type>::value;\n};\n\ntemplate <typename BasicJsonType, typename CompatibleArrayType>\nstruct is_compatible_array_type\n    : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};\n\ntemplate <typename BasicJsonType, typename ConstructibleArrayType, typename = void>\nstruct is_constructible_array_type_impl : std::false_type {};\n\ntemplate <typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type_impl <\n    BasicJsonType, ConstructibleArrayType,\n    enable_if_t<std::is_same<ConstructibleArrayType,\n    typename BasicJsonType::value_type>::value >>\n            : std::true_type {};\n\ntemplate <typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type_impl <\n    BasicJsonType, ConstructibleArrayType,\n    enable_if_t<not std::is_same<ConstructibleArrayType,\n    typename BasicJsonType::value_type>::value and\n    std::is_default_constructible<ConstructibleArrayType>::value and\n(std::is_move_assignable<ConstructibleArrayType>::value or\n std::is_copy_assignable<ConstructibleArrayType>::value) and\nis_detected<value_type_t, ConstructibleArrayType>::value and\nis_detected<iterator_t, ConstructibleArrayType>::value and\nis_complete_type<\ndetected_t<value_type_t, ConstructibleArrayType>>::value >>\n{\n    static constexpr bool value =\n        // This is needed because json_reverse_iterator has a ::iterator type,\n        // furthermore, std::back_insert_iterator (and other iterators) have a\n        // base class `iterator`... Therefore it is detected as a\n        // ConstructibleArrayType. The real fix would be to have an Iterable\n        // concept.\n        not is_iterator_traits<iterator_traits<ConstructibleArrayType>>::value and\n\n        (std::is_same<typename ConstructibleArrayType::value_type,\n         typename BasicJsonType::array_t::value_type>::value or\n         has_from_json<BasicJsonType,\n         typename ConstructibleArrayType::value_type>::value or\n         has_non_default_from_json <\n         BasicJsonType, typename ConstructibleArrayType::value_type >::value);\n};\n\ntemplate <typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type\n    : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};\n\ntemplate <typename RealIntegerType, typename CompatibleNumberIntegerType,\n          typename = void>\nstruct is_compatible_integer_type_impl : std::false_type {};\n\ntemplate <typename RealIntegerType, typename CompatibleNumberIntegerType>\nstruct is_compatible_integer_type_impl <\n    RealIntegerType, CompatibleNumberIntegerType,\n    enable_if_t<std::is_integral<RealIntegerType>::value and\n    std::is_integral<CompatibleNumberIntegerType>::value and\n    not std::is_same<bool, CompatibleNumberIntegerType>::value >>\n{\n    // is there an assert somewhere on overflows?\n    using RealLimits = std::numeric_limits<RealIntegerType>;\n    using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;\n\n    static constexpr auto value =\n        std::is_constructible<RealIntegerType,\n        CompatibleNumberIntegerType>::value and\n        CompatibleLimits::is_integer and\n        RealLimits::is_signed == CompatibleLimits::is_signed;\n};\n\ntemplate <typename RealIntegerType, typename CompatibleNumberIntegerType>\nstruct is_compatible_integer_type\n    : is_compatible_integer_type_impl<RealIntegerType,\n      CompatibleNumberIntegerType> {};\n\ntemplate <typename BasicJsonType, typename CompatibleType, typename = void>\nstruct is_compatible_type_impl: std::false_type {};\n\ntemplate <typename BasicJsonType, typename CompatibleType>\nstruct is_compatible_type_impl <\n    BasicJsonType, CompatibleType,\n    enable_if_t<is_complete_type<CompatibleType>::value >>\n{\n    static constexpr bool value =\n        has_to_json<BasicJsonType, CompatibleType>::value;\n};\n\ntemplate <typename BasicJsonType, typename CompatibleType>\nstruct is_compatible_type\n    : is_compatible_type_impl<BasicJsonType, CompatibleType> {};\n\n// https://en.cppreference.com/w/cpp/types/conjunction\ntemplate<class...> struct conjunction : std::true_type { };\ntemplate<class B1> struct conjunction<B1> : B1 { };\ntemplate<class B1, class... Bn>\nstruct conjunction<B1, Bn...>\n: std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};\n\ntemplate <typename T1, typename T2>\nstruct is_constructible_tuple : std::false_type {};\n\ntemplate <typename T1, typename... Args>\nstruct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<std::is_constructible<T1, Args>...> {};\n}  // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/meta/void_t.hpp",
    "content": "#pragma once\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate <typename ...Ts> struct make_void\n{\n    using type = void;\n};\ntemplate <typename ...Ts> using void_t = typename make_void<Ts...>::type;\n} // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/output/binary_writer.hpp",
    "content": "#pragma once\n\n#include <algorithm> // reverse\n#include <array> // array\n#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t\n#include <cstring> // memcpy\n#include <limits> // numeric_limits\n#include <string> // string\n\n#include <nlohmann/detail/input/binary_reader.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/output/output_adapters.hpp>\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////\n// binary writer //\n///////////////////\n\n/*!\n@brief serialization to CBOR and MessagePack values\n*/\ntemplate<typename BasicJsonType, typename CharType>\nclass binary_writer\n{\n    using string_t = typename BasicJsonType::string_t;\n\n  public:\n    /*!\n    @brief create a binary writer\n\n    @param[in] adapter  output adapter to write to\n    */\n    explicit binary_writer(output_adapter_t<CharType> adapter) : oa(adapter)\n    {\n        assert(oa);\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @pre       j.type() == value_t::object\n    */\n    void write_bson(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::object:\n            {\n                write_bson_object(*j.m_value.object);\n                break;\n            }\n\n            default:\n            {\n                JSON_THROW(type_error::create(317, \"to serialize to BSON, top-level type must be object, but is \" + std::string(j.type_name())));\n            }\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    */\n    void write_cbor(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n            {\n                oa->write_character(to_char_type(0xF6));\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                oa->write_character(j.m_value.boolean\n                                    ? to_char_type(0xF5)\n                                    : to_char_type(0xF4));\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                if (j.m_value.number_integer >= 0)\n                {\n                    // CBOR does not differentiate between positive signed\n                    // integers and unsigned integers. Therefore, we used the\n                    // code from the value_t::number_unsigned case here.\n                    if (j.m_value.number_integer <= 0x17)\n                    {\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x18));\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x19));\n                        write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x1A));\n                        write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                    }\n                    else\n                    {\n                        oa->write_character(to_char_type(0x1B));\n                        write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                    }\n                }\n                else\n                {\n                    // The conversions below encode the sign in the first\n                    // byte, and the value is converted to a positive number.\n                    const auto positive_number = -1 - j.m_value.number_integer;\n                    if (j.m_value.number_integer >= -24)\n                    {\n                        write_number(static_cast<std::uint8_t>(0x20 + positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x38));\n                        write_number(static_cast<std::uint8_t>(positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x39));\n                        write_number(static_cast<std::uint16_t>(positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x3A));\n                        write_number(static_cast<std::uint32_t>(positive_number));\n                    }\n                    else\n                    {\n                        oa->write_character(to_char_type(0x3B));\n                        write_number(static_cast<std::uint64_t>(positive_number));\n                    }\n                }\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x18));\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x19));\n                    write_number(static_cast<std::uint16_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x1A));\n                    write_number(static_cast<std::uint32_t>(j.m_value.number_unsigned));\n                }\n                else\n                {\n                    oa->write_character(to_char_type(0x1B));\n                    write_number(static_cast<std::uint64_t>(j.m_value.number_unsigned));\n                }\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                oa->write_character(get_cbor_float_prefix(j.m_value.number_float));\n                write_number(j.m_value.number_float);\n                break;\n            }\n\n            case value_t::string:\n            {\n                // step 1: write control byte and the string length\n                const auto N = j.m_value.string->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0x60 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x78));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x79));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x7A));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x7B));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write the string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                // step 1: write control byte and the array size\n                const auto N = j.m_value.array->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0x80 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x98));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x99));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x9A));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x9B));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_cbor(el);\n                }\n                break;\n            }\n\n            case value_t::object:\n            {\n                // step 1: write control byte and the object size\n                const auto N = j.m_value.object->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0xA0 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xB8));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xB9));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xBA));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xBB));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_cbor(el.first);\n                    write_cbor(el.second);\n                }\n                break;\n            }\n\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    */\n    void write_msgpack(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::null: // nil\n            {\n                oa->write_character(to_char_type(0xC0));\n                break;\n            }\n\n            case value_t::boolean: // true and false\n            {\n                oa->write_character(j.m_value.boolean\n                                    ? to_char_type(0xC3)\n                                    : to_char_type(0xC2));\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                if (j.m_value.number_integer >= 0)\n                {\n                    // MessagePack does not differentiate between positive\n                    // signed integers and unsigned integers. Therefore, we used\n                    // the code from the value_t::number_unsigned case here.\n                    if (j.m_value.number_unsigned < 128)\n                    {\n                        // positive fixnum\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        // uint 8\n                        oa->write_character(to_char_type(0xCC));\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        // uint 16\n                        oa->write_character(to_char_type(0xCD));\n                        write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        // uint 32\n                        oa->write_character(to_char_type(0xCE));\n                        write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())\n                    {\n                        // uint 64\n                        oa->write_character(to_char_type(0xCF));\n                        write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                    }\n                }\n                else\n                {\n                    if (j.m_value.number_integer >= -32)\n                    {\n                        // negative fixnum\n                        write_number(static_cast<std::int8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() and\n                             j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())\n                    {\n                        // int 8\n                        oa->write_character(to_char_type(0xD0));\n                        write_number(static_cast<std::int8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() and\n                             j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())\n                    {\n                        // int 16\n                        oa->write_character(to_char_type(0xD1));\n                        write_number(static_cast<std::int16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() and\n                             j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())\n                    {\n                        // int 32\n                        oa->write_character(to_char_type(0xD2));\n                        write_number(static_cast<std::int32_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() and\n                             j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())\n                    {\n                        // int 64\n                        oa->write_character(to_char_type(0xD3));\n                        write_number(static_cast<std::int64_t>(j.m_value.number_integer));\n                    }\n                }\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned < 128)\n                {\n                    // positive fixnum\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    // uint 8\n                    oa->write_character(to_char_type(0xCC));\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // uint 16\n                    oa->write_character(to_char_type(0xCD));\n                    write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // uint 32\n                    oa->write_character(to_char_type(0xCE));\n                    write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    // uint 64\n                    oa->write_character(to_char_type(0xCF));\n                    write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                }\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                oa->write_character(get_msgpack_float_prefix(j.m_value.number_float));\n                write_number(j.m_value.number_float);\n                break;\n            }\n\n            case value_t::string:\n            {\n                // step 1: write control byte and the string length\n                const auto N = j.m_value.string->size();\n                if (N <= 31)\n                {\n                    // fixstr\n                    write_number(static_cast<std::uint8_t>(0xA0 | N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    // str 8\n                    oa->write_character(to_char_type(0xD9));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // str 16\n                    oa->write_character(to_char_type(0xDA));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // str 32\n                    oa->write_character(to_char_type(0xDB));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write the string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                // step 1: write control byte and the array size\n                const auto N = j.m_value.array->size();\n                if (N <= 15)\n                {\n                    // fixarray\n                    write_number(static_cast<std::uint8_t>(0x90 | N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // array 16\n                    oa->write_character(to_char_type(0xDC));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // array 32\n                    oa->write_character(to_char_type(0xDD));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_msgpack(el);\n                }\n                break;\n            }\n\n            case value_t::object:\n            {\n                // step 1: write control byte and the object size\n                const auto N = j.m_value.object->size();\n                if (N <= 15)\n                {\n                    // fixmap\n                    write_number(static_cast<std::uint8_t>(0x80 | (N & 0xF)));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // map 16\n                    oa->write_character(to_char_type(0xDE));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // map 32\n                    oa->write_character(to_char_type(0xDF));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_msgpack(el.first);\n                    write_msgpack(el.second);\n                }\n                break;\n            }\n\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @param[in] use_count   whether to use '#' prefixes (optimized format)\n    @param[in] use_type    whether to use '$' prefixes (optimized format)\n    @param[in] add_prefix  whether prefixes need to be used for this value\n    */\n    void write_ubjson(const BasicJsonType& j, const bool use_count,\n                      const bool use_type, const bool add_prefix = true)\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('Z'));\n                }\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(j.m_value.boolean\n                                        ? to_char_type('T')\n                                        : to_char_type('F'));\n                }\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix);\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix);\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix);\n                break;\n            }\n\n            case value_t::string:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('S'));\n                }\n                write_number_with_ubjson_prefix(j.m_value.string->size(), true);\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('['));\n                }\n\n                bool prefix_required = true;\n                if (use_type and not j.m_value.array->empty())\n                {\n                    assert(use_count);\n                    const CharType first_prefix = ubjson_prefix(j.front());\n                    const bool same_prefix = std::all_of(j.begin() + 1, j.end(),\n                                                         [this, first_prefix](const BasicJsonType & v)\n                    {\n                        return ubjson_prefix(v) == first_prefix;\n                    });\n\n                    if (same_prefix)\n                    {\n                        prefix_required = false;\n                        oa->write_character(to_char_type('$'));\n                        oa->write_character(first_prefix);\n                    }\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.array->size(), true);\n                }\n\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_ubjson(el, use_count, use_type, prefix_required);\n                }\n\n                if (not use_count)\n                {\n                    oa->write_character(to_char_type(']'));\n                }\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('{'));\n                }\n\n                bool prefix_required = true;\n                if (use_type and not j.m_value.object->empty())\n                {\n                    assert(use_count);\n                    const CharType first_prefix = ubjson_prefix(j.front());\n                    const bool same_prefix = std::all_of(j.begin(), j.end(),\n                                                         [this, first_prefix](const BasicJsonType & v)\n                    {\n                        return ubjson_prefix(v) == first_prefix;\n                    });\n\n                    if (same_prefix)\n                    {\n                        prefix_required = false;\n                        oa->write_character(to_char_type('$'));\n                        oa->write_character(first_prefix);\n                    }\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.object->size(), true);\n                }\n\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_number_with_ubjson_prefix(el.first.size(), true);\n                    oa->write_characters(\n                        reinterpret_cast<const CharType*>(el.first.c_str()),\n                        el.first.size());\n                    write_ubjson(el.second, use_count, use_type, prefix_required);\n                }\n\n                if (not use_count)\n                {\n                    oa->write_character(to_char_type('}'));\n                }\n\n                break;\n            }\n\n            default:\n                break;\n        }\n    }\n\n  private:\n    //////////\n    // BSON //\n    //////////\n\n    /*!\n    @return The size of a BSON document entry header, including the id marker\n            and the entry name size (and its null-terminator).\n    */\n    static std::size_t calc_bson_entry_header_size(const string_t& name)\n    {\n        const auto it = name.find(static_cast<typename string_t::value_type>(0));\n        if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))\n        {\n            JSON_THROW(out_of_range::create(409,\n                                            \"BSON key cannot contain code point U+0000 (at byte \" + std::to_string(it) + \")\"));\n        }\n\n        return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;\n    }\n\n    /*!\n    @brief Writes the given @a element_type and @a name to the output adapter\n    */\n    void write_bson_entry_header(const string_t& name,\n                                 const std::uint8_t element_type)\n    {\n        oa->write_character(to_char_type(element_type)); // boolean\n        oa->write_characters(\n            reinterpret_cast<const CharType*>(name.c_str()),\n            name.size() + 1u);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and boolean value @a value\n    */\n    void write_bson_boolean(const string_t& name,\n                            const bool value)\n    {\n        write_bson_entry_header(name, 0x08);\n        oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00));\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and double value @a value\n    */\n    void write_bson_double(const string_t& name,\n                           const double value)\n    {\n        write_bson_entry_header(name, 0x01);\n        write_number<double, true>(value);\n    }\n\n    /*!\n    @return The size of the BSON-encoded string in @a value\n    */\n    static std::size_t calc_bson_string_size(const string_t& value)\n    {\n        return sizeof(std::int32_t) + value.size() + 1ul;\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and string value @a value\n    */\n    void write_bson_string(const string_t& name,\n                           const string_t& value)\n    {\n        write_bson_entry_header(name, 0x02);\n\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size() + 1ul));\n        oa->write_characters(\n            reinterpret_cast<const CharType*>(value.c_str()),\n            value.size() + 1);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and null value\n    */\n    void write_bson_null(const string_t& name)\n    {\n        write_bson_entry_header(name, 0x0A);\n    }\n\n    /*!\n    @return The size of the BSON-encoded integer @a value\n    */\n    static std::size_t calc_bson_integer_size(const std::int64_t value)\n    {\n        return (std::numeric_limits<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)()\n               ? sizeof(std::int32_t)\n               : sizeof(std::int64_t);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and integer @a value\n    */\n    void write_bson_integer(const string_t& name,\n                            const std::int64_t value)\n    {\n        if ((std::numeric_limits<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)())\n        {\n            write_bson_entry_header(name, 0x10); // int32\n            write_number<std::int32_t, true>(static_cast<std::int32_t>(value));\n        }\n        else\n        {\n            write_bson_entry_header(name, 0x12); // int64\n            write_number<std::int64_t, true>(static_cast<std::int64_t>(value));\n        }\n    }\n\n    /*!\n    @return The size of the BSON-encoded unsigned integer in @a j\n    */\n    static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept\n    {\n        return (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n               ? sizeof(std::int32_t)\n               : sizeof(std::int64_t);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and unsigned @a value\n    */\n    void write_bson_unsigned(const string_t& name,\n                             const std::uint64_t value)\n    {\n        if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n        {\n            write_bson_entry_header(name, 0x10 /* int32 */);\n            write_number<std::int32_t, true>(static_cast<std::int32_t>(value));\n        }\n        else if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n        {\n            write_bson_entry_header(name, 0x12 /* int64 */);\n            write_number<std::int64_t, true>(static_cast<std::int64_t>(value));\n        }\n        else\n        {\n            JSON_THROW(out_of_range::create(407, \"integer number \" + std::to_string(value) + \" cannot be represented by BSON as it does not fit int64\"));\n        }\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and object @a value\n    */\n    void write_bson_object_entry(const string_t& name,\n                                 const typename BasicJsonType::object_t& value)\n    {\n        write_bson_entry_header(name, 0x03); // object\n        write_bson_object(value);\n    }\n\n    /*!\n    @return The size of the BSON-encoded array @a value\n    */\n    static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value)\n    {\n        std::size_t array_index = 0ul;\n\n        const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), 0ul, [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el)\n        {\n            return result + calc_bson_element_size(std::to_string(array_index++), el);\n        });\n\n        return sizeof(std::int32_t) + embedded_document_size + 1ul;\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and array @a value\n    */\n    void write_bson_array(const string_t& name,\n                          const typename BasicJsonType::array_t& value)\n    {\n        write_bson_entry_header(name, 0x04); // array\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_array_size(value)));\n\n        std::size_t array_index = 0ul;\n\n        for (const auto& el : value)\n        {\n            write_bson_element(std::to_string(array_index++), el);\n        }\n\n        oa->write_character(to_char_type(0x00));\n    }\n\n    /*!\n    @brief Calculates the size necessary to serialize the JSON value @a j with its @a name\n    @return The calculated size for the BSON document entry for @a j with the given @a name.\n    */\n    static std::size_t calc_bson_element_size(const string_t& name,\n            const BasicJsonType& j)\n    {\n        const auto header_size = calc_bson_entry_header_size(name);\n        switch (j.type())\n        {\n            case value_t::object:\n                return header_size + calc_bson_object_size(*j.m_value.object);\n\n            case value_t::array:\n                return header_size + calc_bson_array_size(*j.m_value.array);\n\n            case value_t::boolean:\n                return header_size + 1ul;\n\n            case value_t::number_float:\n                return header_size + 8ul;\n\n            case value_t::number_integer:\n                return header_size + calc_bson_integer_size(j.m_value.number_integer);\n\n            case value_t::number_unsigned:\n                return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned);\n\n            case value_t::string:\n                return header_size + calc_bson_string_size(*j.m_value.string);\n\n            case value_t::null:\n                return header_size + 0ul;\n\n            // LCOV_EXCL_START\n            default:\n                assert(false);\n                return 0ul;\n                // LCOV_EXCL_STOP\n        }\n    }\n\n    /*!\n    @brief Serializes the JSON value @a j to BSON and associates it with the\n           key @a name.\n    @param name The name to associate with the JSON entity @a j within the\n                current BSON document\n    @return The size of the BSON entry\n    */\n    void write_bson_element(const string_t& name,\n                            const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::object:\n                return write_bson_object_entry(name, *j.m_value.object);\n\n            case value_t::array:\n                return write_bson_array(name, *j.m_value.array);\n\n            case value_t::boolean:\n                return write_bson_boolean(name, j.m_value.boolean);\n\n            case value_t::number_float:\n                return write_bson_double(name, j.m_value.number_float);\n\n            case value_t::number_integer:\n                return write_bson_integer(name, j.m_value.number_integer);\n\n            case value_t::number_unsigned:\n                return write_bson_unsigned(name, j.m_value.number_unsigned);\n\n            case value_t::string:\n                return write_bson_string(name, *j.m_value.string);\n\n            case value_t::null:\n                return write_bson_null(name);\n\n            // LCOV_EXCL_START\n            default:\n                assert(false);\n                return;\n                // LCOV_EXCL_STOP\n        }\n    }\n\n    /*!\n    @brief Calculates the size of the BSON serialization of the given\n           JSON-object @a j.\n    @param[in] j  JSON value to serialize\n    @pre       j.type() == value_t::object\n    */\n    static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)\n    {\n        std::size_t document_size = std::accumulate(value.begin(), value.end(), 0ul,\n                                    [](size_t result, const typename BasicJsonType::object_t::value_type & el)\n        {\n            return result += calc_bson_element_size(el.first, el.second);\n        });\n\n        return sizeof(std::int32_t) + document_size + 1ul;\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @pre       j.type() == value_t::object\n    */\n    void write_bson_object(const typename BasicJsonType::object_t& value)\n    {\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_object_size(value)));\n\n        for (const auto& el : value)\n        {\n            write_bson_element(el.first, el.second);\n        }\n\n        oa->write_character(to_char_type(0x00));\n    }\n\n    //////////\n    // CBOR //\n    //////////\n\n    static constexpr CharType get_cbor_float_prefix(float /*unused*/)\n    {\n        return to_char_type(0xFA);  // Single-Precision Float\n    }\n\n    static constexpr CharType get_cbor_float_prefix(double /*unused*/)\n    {\n        return to_char_type(0xFB);  // Double-Precision Float\n    }\n\n    /////////////\n    // MsgPack //\n    /////////////\n\n    static constexpr CharType get_msgpack_float_prefix(float /*unused*/)\n    {\n        return to_char_type(0xCA);  // float 32\n    }\n\n    static constexpr CharType get_msgpack_float_prefix(double /*unused*/)\n    {\n        return to_char_type(0xCB);  // float 64\n    }\n\n    ////////////\n    // UBJSON //\n    ////////////\n\n    // UBJSON: write number (floating point)\n    template<typename NumberType, typename std::enable_if<\n                 std::is_floating_point<NumberType>::value, int>::type = 0>\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if (add_prefix)\n        {\n            oa->write_character(get_ubjson_float_prefix(n));\n        }\n        write_number(n);\n    }\n\n    // UBJSON: write number (unsigned integer)\n    template<typename NumberType, typename std::enable_if<\n                 std::is_unsigned<NumberType>::value, int>::type = 0>\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('i'));  // int8\n            }\n            write_number(static_cast<std::uint8_t>(n));\n        }\n        else if (n <= (std::numeric_limits<std::uint8_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('U'));  // uint8\n            }\n            write_number(static_cast<std::uint8_t>(n));\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('I'));  // int16\n            }\n            write_number(static_cast<std::int16_t>(n));\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('l'));  // int32\n            }\n            write_number(static_cast<std::int32_t>(n));\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('L'));  // int64\n            }\n            write_number(static_cast<std::int64_t>(n));\n        }\n        else\n        {\n            JSON_THROW(out_of_range::create(407, \"integer number \" + std::to_string(n) + \" cannot be represented by UBJSON as it does not fit int64\"));\n        }\n    }\n\n    // UBJSON: write number (signed integer)\n    template<typename NumberType, typename std::enable_if<\n                 std::is_signed<NumberType>::value and\n                 not std::is_floating_point<NumberType>::value, int>::type = 0>\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if ((std::numeric_limits<std::int8_t>::min)() <= n and n <= (std::numeric_limits<std::int8_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('i'));  // int8\n            }\n            write_number(static_cast<std::int8_t>(n));\n        }\n        else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n and n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('U'));  // uint8\n            }\n            write_number(static_cast<std::uint8_t>(n));\n        }\n        else if ((std::numeric_limits<std::int16_t>::min)() <= n and n <= (std::numeric_limits<std::int16_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('I'));  // int16\n            }\n            write_number(static_cast<std::int16_t>(n));\n        }\n        else if ((std::numeric_limits<std::int32_t>::min)() <= n and n <= (std::numeric_limits<std::int32_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('l'));  // int32\n            }\n            write_number(static_cast<std::int32_t>(n));\n        }\n        else if ((std::numeric_limits<std::int64_t>::min)() <= n and n <= (std::numeric_limits<std::int64_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('L'));  // int64\n            }\n            write_number(static_cast<std::int64_t>(n));\n        }\n        // LCOV_EXCL_START\n        else\n        {\n            JSON_THROW(out_of_range::create(407, \"integer number \" + std::to_string(n) + \" cannot be represented by UBJSON as it does not fit int64\"));\n        }\n        // LCOV_EXCL_STOP\n    }\n\n    /*!\n    @brief determine the type prefix of container values\n\n    @note This function does not need to be 100% accurate when it comes to\n          integer limits. In case a number exceeds the limits of int64_t,\n          this will be detected by a later call to function\n          write_number_with_ubjson_prefix. Therefore, we return 'L' for any\n          value that does not fit the previous limits.\n    */\n    CharType ubjson_prefix(const BasicJsonType& j) const noexcept\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n                return 'Z';\n\n            case value_t::boolean:\n                return j.m_value.boolean ? 'T' : 'F';\n\n            case value_t::number_integer:\n            {\n                if ((std::numeric_limits<std::int8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())\n                {\n                    return 'i';\n                }\n                if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    return 'U';\n                }\n                if ((std::numeric_limits<std::int16_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())\n                {\n                    return 'I';\n                }\n                if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())\n                {\n                    return 'l';\n                }\n                // no check and assume int64_t (see note above)\n                return 'L';\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))\n                {\n                    return 'i';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint8_t>::max)()))\n                {\n                    return 'U';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))\n                {\n                    return 'I';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n                {\n                    return 'l';\n                }\n                // no check and assume int64_t (see note above)\n                return 'L';\n            }\n\n            case value_t::number_float:\n                return get_ubjson_float_prefix(j.m_value.number_float);\n\n            case value_t::string:\n                return 'S';\n\n            case value_t::array:\n                return '[';\n\n            case value_t::object:\n                return '{';\n\n            default:  // discarded values\n                return 'N';\n        }\n    }\n\n    static constexpr CharType get_ubjson_float_prefix(float /*unused*/)\n    {\n        return 'd';  // float 32\n    }\n\n    static constexpr CharType get_ubjson_float_prefix(double /*unused*/)\n    {\n        return 'D';  // float 64\n    }\n\n    ///////////////////////\n    // Utility functions //\n    ///////////////////////\n\n    /*\n    @brief write a number to output input\n    @param[in] n number of type @a NumberType\n    @tparam NumberType the type of the number\n    @tparam OutputIsLittleEndian Set to true if output data is\n                                 required to be little endian\n\n    @note This function needs to respect the system's endianess, because bytes\n          in CBOR, MessagePack, and UBJSON are stored in network order (big\n          endian) and therefore need reordering on little endian systems.\n    */\n    template<typename NumberType, bool OutputIsLittleEndian = false>\n    void write_number(const NumberType n)\n    {\n        // step 1: write number to array of length NumberType\n        std::array<CharType, sizeof(NumberType)> vec;\n        std::memcpy(vec.data(), &n, sizeof(NumberType));\n\n        // step 2: write array to output (with possible reordering)\n        if (is_little_endian != OutputIsLittleEndian)\n        {\n            // reverse byte order prior to conversion if necessary\n            std::reverse(vec.begin(), vec.end());\n        }\n\n        oa->write_characters(vec.data(), sizeof(NumberType));\n    }\n\n  public:\n    // The following to_char_type functions are implement the conversion\n    // between uint8_t and CharType. In case CharType is not unsigned,\n    // such a conversion is required to allow values greater than 128.\n    // See <https://github.com/nlohmann/json/issues/1286> for a discussion.\n    template < typename C = CharType,\n               enable_if_t < std::is_signed<C>::value and std::is_signed<char>::value > * = nullptr >\n    static constexpr CharType to_char_type(std::uint8_t x) noexcept\n    {\n        return *reinterpret_cast<char*>(&x);\n    }\n\n    template < typename C = CharType,\n               enable_if_t < std::is_signed<C>::value and std::is_unsigned<char>::value > * = nullptr >\n    static CharType to_char_type(std::uint8_t x) noexcept\n    {\n        static_assert(sizeof(std::uint8_t) == sizeof(CharType), \"size of CharType must be equal to std::uint8_t\");\n        static_assert(std::is_pod<CharType>::value, \"CharType must be POD\");\n        CharType result;\n        std::memcpy(&result, &x, sizeof(x));\n        return result;\n    }\n\n    template<typename C = CharType,\n             enable_if_t<std::is_unsigned<C>::value>* = nullptr>\n    static constexpr CharType to_char_type(std::uint8_t x) noexcept\n    {\n        return x;\n    }\n\n    template < typename InputCharType, typename C = CharType,\n               enable_if_t <\n                   std::is_signed<C>::value and\n                   std::is_signed<char>::value and\n                   std::is_same<char, typename std::remove_cv<InputCharType>::type>::value\n                   > * = nullptr >\n    static constexpr CharType to_char_type(InputCharType x) noexcept\n    {\n        return x;\n    }\n\n  private:\n    /// whether we can assume little endianess\n    const bool is_little_endian = binary_reader<BasicJsonType>::little_endianess();\n\n    /// the output\n    output_adapter_t<CharType> oa = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/output/output_adapters.hpp",
    "content": "#pragma once\n\n#include <algorithm> // copy\n#include <cstddef> // size_t\n#include <ios> // streamsize\n#include <iterator> // back_inserter\n#include <memory> // shared_ptr, make_shared\n#include <ostream> // basic_ostream\n#include <string> // basic_string\n#include <vector> // vector\n#include <nlohmann/detail/macro_scope.hpp>\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// abstract output adapter interface\ntemplate<typename CharType> struct output_adapter_protocol\n{\n    virtual void write_character(CharType c) = 0;\n    virtual void write_characters(const CharType* s, std::size_t length) = 0;\n    virtual ~output_adapter_protocol() = default;\n};\n\n/// a type to simplify interfaces\ntemplate<typename CharType>\nusing output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;\n\n/// output adapter for byte vectors\ntemplate<typename CharType>\nclass output_vector_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_vector_adapter(std::vector<CharType>& vec) noexcept\n        : v(vec)\n    {}\n\n    void write_character(CharType c) override\n    {\n        v.push_back(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        std::copy(s, s + length, std::back_inserter(v));\n    }\n\n  private:\n    std::vector<CharType>& v;\n};\n\n/// output adapter for output streams\ntemplate<typename CharType>\nclass output_stream_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept\n        : stream(s)\n    {}\n\n    void write_character(CharType c) override\n    {\n        stream.put(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        stream.write(s, static_cast<std::streamsize>(length));\n    }\n\n  private:\n    std::basic_ostream<CharType>& stream;\n};\n\n/// output adapter for basic_string\ntemplate<typename CharType, typename StringType = std::basic_string<CharType>>\nclass output_string_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_string_adapter(StringType& s) noexcept\n        : str(s)\n    {}\n\n    void write_character(CharType c) override\n    {\n        str.push_back(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        str.append(s, length);\n    }\n\n  private:\n    StringType& str;\n};\n\ntemplate<typename CharType, typename StringType = std::basic_string<CharType>>\nclass output_adapter\n{\n  public:\n    output_adapter(std::vector<CharType>& vec)\n        : oa(std::make_shared<output_vector_adapter<CharType>>(vec)) {}\n\n    output_adapter(std::basic_ostream<CharType>& s)\n        : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}\n\n    output_adapter(StringType& s)\n        : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}\n\n    operator output_adapter_t<CharType>()\n    {\n        return oa;\n    }\n\n  private:\n    output_adapter_t<CharType> oa = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/output/serializer.hpp",
    "content": "#pragma once\n\n#include <algorithm> // reverse, remove, fill, find, none_of\n#include <array> // array\n#include <cassert> // assert\n#include <ciso646> // and, or\n#include <clocale> // localeconv, lconv\n#include <cmath> // labs, isfinite, isnan, signbit\n#include <cstddef> // size_t, ptrdiff_t\n#include <cstdint> // uint8_t\n#include <cstdio> // snprintf\n#include <limits> // numeric_limits\n#include <string> // string\n#include <type_traits> // is_same\n#include <utility> // move\n\n#include <nlohmann/detail/conversions/to_chars.hpp>\n#include <nlohmann/detail/exceptions.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/meta/cpp_future.hpp>\n#include <nlohmann/detail/output/binary_writer.hpp>\n#include <nlohmann/detail/output/output_adapters.hpp>\n#include <nlohmann/detail/value_t.hpp>\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////\n// serialization //\n///////////////////\n\n/// how to treat decoding errors\nenum class error_handler_t\n{\n    strict,  ///< throw a type_error exception in case of invalid UTF-8\n    replace, ///< replace invalid UTF-8 sequences with U+FFFD\n    ignore   ///< ignore invalid UTF-8 sequences\n};\n\ntemplate<typename BasicJsonType>\nclass serializer\n{\n    using string_t = typename BasicJsonType::string_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    static constexpr std::uint8_t UTF8_ACCEPT = 0;\n    static constexpr std::uint8_t UTF8_REJECT = 1;\n\n  public:\n    /*!\n    @param[in] s  output stream to serialize to\n    @param[in] ichar  indentation character to use\n    @param[in] error_handler_  how to react on decoding errors\n    */\n    serializer(output_adapter_t<char> s, const char ichar,\n               error_handler_t error_handler_ = error_handler_t::strict)\n        : o(std::move(s))\n        , loc(std::localeconv())\n        , thousands_sep(loc->thousands_sep == nullptr ? '\\0' : * (loc->thousands_sep))\n        , decimal_point(loc->decimal_point == nullptr ? '\\0' : * (loc->decimal_point))\n        , indent_char(ichar)\n        , indent_string(512, indent_char)\n        , error_handler(error_handler_)\n    {}\n\n    // delete because of pointer members\n    serializer(const serializer&) = delete;\n    serializer& operator=(const serializer&) = delete;\n    serializer(serializer&&) = delete;\n    serializer& operator=(serializer&&) = delete;\n    ~serializer() = default;\n\n    /*!\n    @brief internal implementation of the serialization function\n\n    This function is called by the public member function dump and organizes\n    the serialization internally. The indentation level is propagated as\n    additional parameter. In case of arrays and objects, the function is\n    called recursively.\n\n    - strings and object keys are escaped using `escape_string()`\n    - integer numbers are converted implicitly via `operator<<`\n    - floating-point numbers are converted to a string using `\"%g\"` format\n\n    @param[in] val             value to serialize\n    @param[in] pretty_print    whether the output shall be pretty-printed\n    @param[in] indent_step     the indent level\n    @param[in] current_indent  the current indent level (only used internally)\n    */\n    void dump(const BasicJsonType& val, const bool pretty_print,\n              const bool ensure_ascii,\n              const unsigned int indent_step,\n              const unsigned int current_indent = 0)\n    {\n        switch (val.m_type)\n        {\n            case value_t::object:\n            {\n                if (val.m_value.object->empty())\n                {\n                    o->write_characters(\"{}\", 2);\n                    return;\n                }\n\n                if (pretty_print)\n                {\n                    o->write_characters(\"{\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    // first n-1 elements\n                    auto i = val.m_value.object->cbegin();\n                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)\n                    {\n                        o->write_characters(indent_string.c_str(), new_indent);\n                        o->write_character('\\\"');\n                        dump_escaped(i->first, ensure_ascii);\n                        o->write_characters(\"\\\": \", 3);\n                        dump(i->second, true, ensure_ascii, indent_step, new_indent);\n                        o->write_characters(\",\\n\", 2);\n                    }\n\n                    // last element\n                    assert(i != val.m_value.object->cend());\n                    assert(std::next(i) == val.m_value.object->cend());\n                    o->write_characters(indent_string.c_str(), new_indent);\n                    o->write_character('\\\"');\n                    dump_escaped(i->first, ensure_ascii);\n                    o->write_characters(\"\\\": \", 3);\n                    dump(i->second, true, ensure_ascii, indent_step, new_indent);\n\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character('}');\n                }\n                else\n                {\n                    o->write_character('{');\n\n                    // first n-1 elements\n                    auto i = val.m_value.object->cbegin();\n                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)\n                    {\n                        o->write_character('\\\"');\n                        dump_escaped(i->first, ensure_ascii);\n                        o->write_characters(\"\\\":\", 2);\n                        dump(i->second, false, ensure_ascii, indent_step, current_indent);\n                        o->write_character(',');\n                    }\n\n                    // last element\n                    assert(i != val.m_value.object->cend());\n                    assert(std::next(i) == val.m_value.object->cend());\n                    o->write_character('\\\"');\n                    dump_escaped(i->first, ensure_ascii);\n                    o->write_characters(\"\\\":\", 2);\n                    dump(i->second, false, ensure_ascii, indent_step, current_indent);\n\n                    o->write_character('}');\n                }\n\n                return;\n            }\n\n            case value_t::array:\n            {\n                if (val.m_value.array->empty())\n                {\n                    o->write_characters(\"[]\", 2);\n                    return;\n                }\n\n                if (pretty_print)\n                {\n                    o->write_characters(\"[\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    // first n-1 elements\n                    for (auto i = val.m_value.array->cbegin();\n                            i != val.m_value.array->cend() - 1; ++i)\n                    {\n                        o->write_characters(indent_string.c_str(), new_indent);\n                        dump(*i, true, ensure_ascii, indent_step, new_indent);\n                        o->write_characters(\",\\n\", 2);\n                    }\n\n                    // last element\n                    assert(not val.m_value.array->empty());\n                    o->write_characters(indent_string.c_str(), new_indent);\n                    dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);\n\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character(']');\n                }\n                else\n                {\n                    o->write_character('[');\n\n                    // first n-1 elements\n                    for (auto i = val.m_value.array->cbegin();\n                            i != val.m_value.array->cend() - 1; ++i)\n                    {\n                        dump(*i, false, ensure_ascii, indent_step, current_indent);\n                        o->write_character(',');\n                    }\n\n                    // last element\n                    assert(not val.m_value.array->empty());\n                    dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);\n\n                    o->write_character(']');\n                }\n\n                return;\n            }\n\n            case value_t::string:\n            {\n                o->write_character('\\\"');\n                dump_escaped(*val.m_value.string, ensure_ascii);\n                o->write_character('\\\"');\n                return;\n            }\n\n            case value_t::boolean:\n            {\n                if (val.m_value.boolean)\n                {\n                    o->write_characters(\"true\", 4);\n                }\n                else\n                {\n                    o->write_characters(\"false\", 5);\n                }\n                return;\n            }\n\n            case value_t::number_integer:\n            {\n                dump_integer(val.m_value.number_integer);\n                return;\n            }\n\n            case value_t::number_unsigned:\n            {\n                dump_integer(val.m_value.number_unsigned);\n                return;\n            }\n\n            case value_t::number_float:\n            {\n                dump_float(val.m_value.number_float);\n                return;\n            }\n\n            case value_t::discarded:\n            {\n                o->write_characters(\"<discarded>\", 11);\n                return;\n            }\n\n            case value_t::null:\n            {\n                o->write_characters(\"null\", 4);\n                return;\n            }\n\n            default:            // LCOV_EXCL_LINE\n                assert(false);  // LCOV_EXCL_LINE\n        }\n    }\n\n  private:\n    /*!\n    @brief dump escaped string\n\n    Escape a string by replacing certain special characters by a sequence of an\n    escape character (backslash) and another character and other control\n    characters by a sequence of \"\\u\" followed by a four-digit hex\n    representation. The escaped string is written to output stream @a o.\n\n    @param[in] s  the string to escape\n    @param[in] ensure_ascii  whether to escape non-ASCII characters with\n                             \\uXXXX sequences\n\n    @complexity Linear in the length of string @a s.\n    */\n    void dump_escaped(const string_t& s, const bool ensure_ascii)\n    {\n        std::uint32_t codepoint;\n        std::uint8_t state = UTF8_ACCEPT;\n        std::size_t bytes = 0;  // number of bytes written to string_buffer\n\n        // number of bytes written at the point of the last valid byte\n        std::size_t bytes_after_last_accept = 0;\n        std::size_t undumped_chars = 0;\n\n        for (std::size_t i = 0; i < s.size(); ++i)\n        {\n            const auto byte = static_cast<uint8_t>(s[i]);\n\n            switch (decode(state, codepoint, byte))\n            {\n                case UTF8_ACCEPT:  // decode found a new code point\n                {\n                    switch (codepoint)\n                    {\n                        case 0x08: // backspace\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'b';\n                            break;\n                        }\n\n                        case 0x09: // horizontal tab\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 't';\n                            break;\n                        }\n\n                        case 0x0A: // newline\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'n';\n                            break;\n                        }\n\n                        case 0x0C: // formfeed\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'f';\n                            break;\n                        }\n\n                        case 0x0D: // carriage return\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'r';\n                            break;\n                        }\n\n                        case 0x22: // quotation mark\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = '\\\"';\n                            break;\n                        }\n\n                        case 0x5C: // reverse solidus\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = '\\\\';\n                            break;\n                        }\n\n                        default:\n                        {\n                            // escape control characters (0x00..0x1F) or, if\n                            // ensure_ascii parameter is used, non-ASCII characters\n                            if ((codepoint <= 0x1F) or (ensure_ascii and (codepoint >= 0x7F)))\n                            {\n                                if (codepoint <= 0xFFFF)\n                                {\n                                    (std::snprintf)(string_buffer.data() + bytes, 7, \"\\\\u%04x\",\n                                                    static_cast<std::uint16_t>(codepoint));\n                                    bytes += 6;\n                                }\n                                else\n                                {\n                                    (std::snprintf)(string_buffer.data() + bytes, 13, \"\\\\u%04x\\\\u%04x\",\n                                                    static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),\n                                                    static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));\n                                    bytes += 12;\n                                }\n                            }\n                            else\n                            {\n                                // copy byte to buffer (all previous bytes\n                                // been copied have in default case above)\n                                string_buffer[bytes++] = s[i];\n                            }\n                            break;\n                        }\n                    }\n\n                    // write buffer and reset index; there must be 13 bytes\n                    // left, as this is the maximal number of bytes to be\n                    // written (\"\\uxxxx\\uxxxx\\0\") for one code point\n                    if (string_buffer.size() - bytes < 13)\n                    {\n                        o->write_characters(string_buffer.data(), bytes);\n                        bytes = 0;\n                    }\n\n                    // remember the byte position of this accept\n                    bytes_after_last_accept = bytes;\n                    undumped_chars = 0;\n                    break;\n                }\n\n                case UTF8_REJECT:  // decode found invalid UTF-8 byte\n                {\n                    switch (error_handler)\n                    {\n                        case error_handler_t::strict:\n                        {\n                            std::string sn(3, '\\0');\n                            (std::snprintf)(&sn[0], sn.size(), \"%.2X\", byte);\n                            JSON_THROW(type_error::create(316, \"invalid UTF-8 byte at index \" + std::to_string(i) + \": 0x\" + sn));\n                        }\n\n                        case error_handler_t::ignore:\n                        case error_handler_t::replace:\n                        {\n                            // in case we saw this character the first time, we\n                            // would like to read it again, because the byte\n                            // may be OK for itself, but just not OK for the\n                            // previous sequence\n                            if (undumped_chars > 0)\n                            {\n                                --i;\n                            }\n\n                            // reset length buffer to the last accepted index;\n                            // thus removing/ignoring the invalid characters\n                            bytes = bytes_after_last_accept;\n\n                            if (error_handler == error_handler_t::replace)\n                            {\n                                // add a replacement character\n                                if (ensure_ascii)\n                                {\n                                    string_buffer[bytes++] = '\\\\';\n                                    string_buffer[bytes++] = 'u';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'd';\n                                }\n                                else\n                                {\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xEF');\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xBF');\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xBD');\n                                }\n\n                                // write buffer and reset index; there must be 13 bytes\n                                // left, as this is the maximal number of bytes to be\n                                // written (\"\\uxxxx\\uxxxx\\0\") for one code point\n                                if (string_buffer.size() - bytes < 13)\n                                {\n                                    o->write_characters(string_buffer.data(), bytes);\n                                    bytes = 0;\n                                }\n\n                                bytes_after_last_accept = bytes;\n                            }\n\n                            undumped_chars = 0;\n\n                            // continue processing the string\n                            state = UTF8_ACCEPT;\n                            break;\n                        }\n\n                        default:            // LCOV_EXCL_LINE\n                            assert(false);  // LCOV_EXCL_LINE\n                    }\n                    break;\n                }\n\n                default:  // decode found yet incomplete multi-byte code point\n                {\n                    if (not ensure_ascii)\n                    {\n                        // code point will not be escaped - copy byte to buffer\n                        string_buffer[bytes++] = s[i];\n                    }\n                    ++undumped_chars;\n                    break;\n                }\n            }\n        }\n\n        // we finished processing the string\n        if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))\n        {\n            // write buffer\n            if (bytes > 0)\n            {\n                o->write_characters(string_buffer.data(), bytes);\n            }\n        }\n        else\n        {\n            // we finish reading, but do not accept: string was incomplete\n            switch (error_handler)\n            {\n                case error_handler_t::strict:\n                {\n                    std::string sn(3, '\\0');\n                    (std::snprintf)(&sn[0], sn.size(), \"%.2X\", static_cast<std::uint8_t>(s.back()));\n                    JSON_THROW(type_error::create(316, \"incomplete UTF-8 string; last byte: 0x\" + sn));\n                }\n\n                case error_handler_t::ignore:\n                {\n                    // write all accepted bytes\n                    o->write_characters(string_buffer.data(), bytes_after_last_accept);\n                    break;\n                }\n\n                case error_handler_t::replace:\n                {\n                    // write all accepted bytes\n                    o->write_characters(string_buffer.data(), bytes_after_last_accept);\n                    // add a replacement character\n                    if (ensure_ascii)\n                    {\n                        o->write_characters(\"\\\\ufffd\", 6);\n                    }\n                    else\n                    {\n                        o->write_characters(\"\\xEF\\xBF\\xBD\", 3);\n                    }\n                    break;\n                }\n\n                default:            // LCOV_EXCL_LINE\n                    assert(false);  // LCOV_EXCL_LINE\n            }\n        }\n    }\n\n    /*!\n    @brief count digits\n\n    Count the number of decimal (base 10) digits for an input unsigned integer.\n\n    @param[in] x  unsigned integer number to count its digits\n    @return    number of decimal digits\n    */\n    inline unsigned int count_digits(number_unsigned_t x) noexcept\n    {\n        unsigned int n_digits = 1;\n        for (;;)\n        {\n            if (x < 10)\n            {\n                return n_digits;\n            }\n            if (x < 100)\n            {\n                return n_digits + 1;\n            }\n            if (x < 1000)\n            {\n                return n_digits + 2;\n            }\n            if (x < 10000)\n            {\n                return n_digits + 3;\n            }\n            x = x / 10000u;\n            n_digits += 4;\n        }\n    }\n\n    /*!\n    @brief dump an integer\n\n    Dump a given integer to output stream @a o. Works internally with\n    @a number_buffer.\n\n    @param[in] x  integer number (signed or unsigned) to dump\n    @tparam NumberType either @a number_integer_t or @a number_unsigned_t\n    */\n    template<typename NumberType, detail::enable_if_t<\n                 std::is_same<NumberType, number_unsigned_t>::value or\n                 std::is_same<NumberType, number_integer_t>::value,\n                 int> = 0>\n    void dump_integer(NumberType x)\n    {\n        static constexpr std::array<std::array<char, 2>, 100> digits_to_99\n        {\n            {\n                {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}},\n                {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}},\n                {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}},\n                {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}},\n                {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}},\n                {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}},\n                {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}},\n                {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}},\n                {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}},\n                {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}},\n            }\n        };\n\n        // special case for \"0\"\n        if (x == 0)\n        {\n            o->write_character('0');\n            return;\n        }\n\n        // use a pointer to fill the buffer\n        auto buffer_ptr = number_buffer.begin();\n\n        const bool is_negative = std::is_same<NumberType, number_integer_t>::value and not(x >= 0); // see issue #755\n        number_unsigned_t abs_value;\n\n        unsigned int n_chars;\n\n        if (is_negative)\n        {\n            *buffer_ptr = '-';\n            abs_value = remove_sign(x);\n\n            // account one more byte for the minus sign\n            n_chars = 1 + count_digits(abs_value);\n        }\n        else\n        {\n            abs_value = static_cast<number_unsigned_t>(x);\n            n_chars = count_digits(abs_value);\n        }\n\n        // spare 1 byte for '\\0'\n        assert(n_chars < number_buffer.size() - 1);\n\n        // jump to the end to generate the string from backward\n        // so we later avoid reversing the result\n        buffer_ptr += n_chars;\n\n        // Fast int2ascii implementation inspired by \"Fastware\" talk by Andrei Alexandrescu\n        // See: https://www.youtube.com/watch?v=o4-CwDo2zpg\n        while (abs_value >= 100)\n        {\n            const auto digits_index = static_cast<unsigned>((abs_value % 100));\n            abs_value /= 100;\n            *(--buffer_ptr) = digits_to_99[digits_index][1];\n            *(--buffer_ptr) = digits_to_99[digits_index][0];\n        }\n\n        if (abs_value >= 10)\n        {\n            const auto digits_index = static_cast<unsigned>(abs_value);\n            *(--buffer_ptr) = digits_to_99[digits_index][1];\n            *(--buffer_ptr) = digits_to_99[digits_index][0];\n        }\n        else\n        {\n            *(--buffer_ptr) = static_cast<char>('0' + abs_value);\n        }\n\n        o->write_characters(number_buffer.data(), n_chars);\n    }\n\n    /*!\n    @brief dump a floating-point number\n\n    Dump a given floating-point number to output stream @a o. Works internally\n    with @a number_buffer.\n\n    @param[in] x  floating-point number to dump\n    */\n    void dump_float(number_float_t x)\n    {\n        // NaN / inf\n        if (not std::isfinite(x))\n        {\n            o->write_characters(\"null\", 4);\n            return;\n        }\n\n        // If number_float_t is an IEEE-754 single or double precision number,\n        // use the Grisu2 algorithm to produce short numbers which are\n        // guaranteed to round-trip, using strtof and strtod, resp.\n        //\n        // NB: The test below works if <long double> == <double>.\n        static constexpr bool is_ieee_single_or_double\n            = (std::numeric_limits<number_float_t>::is_iec559 and std::numeric_limits<number_float_t>::digits == 24 and std::numeric_limits<number_float_t>::max_exponent == 128) or\n              (std::numeric_limits<number_float_t>::is_iec559 and std::numeric_limits<number_float_t>::digits == 53 and std::numeric_limits<number_float_t>::max_exponent == 1024);\n\n        dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());\n    }\n\n    void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)\n    {\n        char* begin = number_buffer.data();\n        char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);\n\n        o->write_characters(begin, static_cast<size_t>(end - begin));\n    }\n\n    void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)\n    {\n        // get number of digits for a float -> text -> float round-trip\n        static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;\n\n        // the actual conversion\n        std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), \"%.*g\", d, x);\n\n        // negative value indicates an error\n        assert(len > 0);\n        // check if buffer was large enough\n        assert(static_cast<std::size_t>(len) < number_buffer.size());\n\n        // erase thousands separator\n        if (thousands_sep != '\\0')\n        {\n            const auto end = std::remove(number_buffer.begin(),\n                                         number_buffer.begin() + len, thousands_sep);\n            std::fill(end, number_buffer.end(), '\\0');\n            assert((end - number_buffer.begin()) <= len);\n            len = (end - number_buffer.begin());\n        }\n\n        // convert decimal point to '.'\n        if (decimal_point != '\\0' and decimal_point != '.')\n        {\n            const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);\n            if (dec_pos != number_buffer.end())\n            {\n                *dec_pos = '.';\n            }\n        }\n\n        o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));\n\n        // determine if need to append \".0\"\n        const bool value_is_int_like =\n            std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,\n                         [](char c)\n        {\n            return c == '.' or c == 'e';\n        });\n\n        if (value_is_int_like)\n        {\n            o->write_characters(\".0\", 2);\n        }\n    }\n\n    /*!\n    @brief check whether a string is UTF-8 encoded\n\n    The function checks each byte of a string whether it is UTF-8 encoded. The\n    result of the check is stored in the @a state parameter. The function must\n    be called initially with state 0 (accept). State 1 means the string must\n    be rejected, because the current byte is not allowed. If the string is\n    completely processed, but the state is non-zero, the string ended\n    prematurely; that is, the last byte indicated more bytes should have\n    followed.\n\n    @param[in,out] state  the state of the decoding\n    @param[in,out] codep  codepoint (valid only if resulting state is UTF8_ACCEPT)\n    @param[in] byte       next byte to decode\n    @return               new state\n\n    @note The function has been edited: a std::array is used.\n\n    @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>\n    @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/\n    */\n    static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept\n    {\n        static const std::array<std::uint8_t, 400> utf8d =\n        {\n            {\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, // 00..1F\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, // 20..3F\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, // 40..5F\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, // 60..7F\n                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F\n                7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF\n                8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF\n                0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF\n                0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF\n                0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0\n                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2\n                1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4\n                1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6\n                1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8\n            }\n        };\n\n        const std::uint8_t type = utf8d[byte];\n\n        codep = (state != UTF8_ACCEPT)\n                ? (byte & 0x3fu) | (codep << 6u)\n                : (0xFFu >> type) & (byte);\n\n        state = utf8d[256u + state * 16u + type];\n        return state;\n    }\n\n    /*\n     * Overload to make the compiler happy while it is instantiating\n     * dump_integer for number_unsigned_t.\n     * Must never be called.\n     */\n    number_unsigned_t remove_sign(number_unsigned_t x)\n    {\n        assert(false); // LCOV_EXCL_LINE\n        return x; // LCOV_EXCL_LINE\n    }\n\n    /*\n     * Helper function for dump_integer\n     *\n     * This function takes a negative signed integer and returns its absolute\n     * value as unsigned integer. The plus/minus shuffling is necessary as we can\n     * not directly remove the sign of an arbitrary signed integer as the\n     * absolute values of INT_MIN and INT_MAX are usually not the same. See\n     * #1708 for details.\n     */\n    inline number_unsigned_t remove_sign(number_integer_t x) noexcept\n    {\n        assert(x < 0 and x < (std::numeric_limits<number_integer_t>::max)());\n        return static_cast<number_unsigned_t>(-(x + 1)) + 1;\n    }\n\n  private:\n    /// the output of the serializer\n    output_adapter_t<char> o = nullptr;\n\n    /// a (hopefully) large enough character buffer\n    std::array<char, 64> number_buffer{{}};\n\n    /// the locale\n    const std::lconv* loc = nullptr;\n    /// the locale's thousand separator character\n    const char thousands_sep = '\\0';\n    /// the locale's decimal point character\n    const char decimal_point = '\\0';\n\n    /// string buffer\n    std::array<char, 512> string_buffer{{}};\n\n    /// the indentation character\n    const char indent_char;\n    /// the indentation string\n    string_t indent_string;\n\n    /// error_handler how to react on decoding errors\n    const error_handler_t error_handler;\n};\n}  // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/detail/value_t.hpp",
    "content": "#pragma once\n\n#include <array> // array\n#include <ciso646> // and\n#include <cstddef> // size_t\n#include <cstdint> // uint8_t\n#include <string> // string\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////////////\n// JSON type enumeration //\n///////////////////////////\n\n/*!\n@brief the JSON type enumeration\n\nThis enumeration collects the different JSON types. It is internally used to\ndistinguish the stored values, and the functions @ref basic_json::is_null(),\n@ref basic_json::is_object(), @ref basic_json::is_array(),\n@ref basic_json::is_string(), @ref basic_json::is_boolean(),\n@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),\n@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),\n@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and\n@ref basic_json::is_structured() rely on it.\n\n@note There are three enumeration entries (number_integer, number_unsigned, and\nnumber_float), because the library distinguishes these three types for numbers:\n@ref basic_json::number_unsigned_t is used for unsigned integers,\n@ref basic_json::number_integer_t is used for signed integers, and\n@ref basic_json::number_float_t is used for floating-point numbers or to\napproximate integers which do not fit in the limits of their respective type.\n\n@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON\nvalue with the default value for a given type\n\n@since version 1.0.0\n*/\nenum class value_t : std::uint8_t\n{\n    null,             ///< null value\n    object,           ///< object (unordered set of name/value pairs)\n    array,            ///< array (ordered collection of values)\n    string,           ///< string value\n    boolean,          ///< boolean value\n    number_integer,   ///< number value (signed integer)\n    number_unsigned,  ///< number value (unsigned integer)\n    number_float,     ///< number value (floating-point)\n    discarded         ///< discarded by the the parser callback function\n};\n\n/*!\n@brief comparison operator for JSON types\n\nReturns an ordering that is similar to Python:\n- order: null < boolean < number < object < array < string\n- furthermore, each type is not smaller than itself\n- discarded values are not comparable\n\n@since version 1.0.0\n*/\ninline bool operator<(const value_t lhs, const value_t rhs) noexcept\n{\n    static constexpr std::array<std::uint8_t, 8> order = {{\n            0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,\n            1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */\n        }\n    };\n\n    const auto l_index = static_cast<std::size_t>(lhs);\n    const auto r_index = static_cast<std::size_t>(rhs);\n    return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index];\n}\n}  // namespace detail\n}  // namespace nlohmann\n"
  },
  {
    "path": "thirdparty/nlohmann/json.hpp",
    "content": "/*\n    __ _____ _____ _____\n __|  |   __|     |   | |  JSON for Modern C++\n|  |  |__   |  |  | | | |  version 3.7.3\n|_____|_____|_____|_|___|  https://github.com/nlohmann/json\n\nLicensed under the MIT License <http://opensource.org/licenses/MIT>.\nSPDX-License-Identifier: MIT\nCopyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.\n\nPermission is hereby  granted, free of charge, to any  person obtaining a copy\nof this software and associated  documentation files (the \"Software\"), to deal\nin the Software  without restriction, including without  limitation the rights\nto  use, copy,  modify, merge,  publish, distribute,  sublicense, and/or  sell\ncopies  of  the Software,  and  to  permit persons  to  whom  the Software  is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE  IS PROVIDED \"AS  IS\", WITHOUT WARRANTY  OF ANY KIND,  EXPRESS OR\nIMPLIED,  INCLUDING BUT  NOT  LIMITED TO  THE  WARRANTIES OF  MERCHANTABILITY,\nFITNESS FOR  A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT  SHALL THE\nAUTHORS  OR COPYRIGHT  HOLDERS  BE  LIABLE FOR  ANY  CLAIM,  DAMAGES OR  OTHER\nLIABILITY, WHETHER IN AN ACTION OF  CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE  OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\n#ifndef INCLUDE_NLOHMANN_JSON_HPP_\n#define INCLUDE_NLOHMANN_JSON_HPP_\n\n#define NLOHMANN_JSON_VERSION_MAJOR 3\n#define NLOHMANN_JSON_VERSION_MINOR 7\n#define NLOHMANN_JSON_VERSION_PATCH 3\n\n#include <algorithm> // all_of, find, for_each\n#include <cassert> // assert\n#include <ciso646> // and, not, or\n#include <cstddef> // nullptr_t, ptrdiff_t, size_t\n#include <functional> // hash, less\n#include <initializer_list> // initializer_list\n#include <iosfwd> // istream, ostream\n#include <iterator> // random_access_iterator_tag\n#include <memory> // unique_ptr\n#include <numeric> // accumulate\n#include <string> // string, stoi, to_string\n#include <utility> // declval, forward, move, pair, swap\n#include <vector> // vector\n\n#include <nlohmann/adl_serializer.hpp>\n#include <nlohmann/detail/conversions/from_json.hpp>\n#include <nlohmann/detail/conversions/to_json.hpp>\n#include <nlohmann/detail/exceptions.hpp>\n#include <nlohmann/detail/input/binary_reader.hpp>\n#include <nlohmann/detail/input/input_adapters.hpp>\n#include <nlohmann/detail/input/lexer.hpp>\n#include <nlohmann/detail/input/parser.hpp>\n#include <nlohmann/detail/iterators/internal_iterator.hpp>\n#include <nlohmann/detail/iterators/iter_impl.hpp>\n#include <nlohmann/detail/iterators/iteration_proxy.hpp>\n#include <nlohmann/detail/iterators/json_reverse_iterator.hpp>\n#include <nlohmann/detail/iterators/primitive_iterator.hpp>\n#include <nlohmann/detail/json_pointer.hpp>\n#include <nlohmann/detail/json_ref.hpp>\n#include <nlohmann/detail/macro_scope.hpp>\n#include <nlohmann/detail/meta/cpp_future.hpp>\n#include <nlohmann/detail/meta/type_traits.hpp>\n#include <nlohmann/detail/output/binary_writer.hpp>\n#include <nlohmann/detail/output/output_adapters.hpp>\n#include <nlohmann/detail/output/serializer.hpp>\n#include <nlohmann/detail/value_t.hpp>\n#include <nlohmann/json_fwd.hpp>\n\n/*!\n@brief namespace for Niels Lohmann\n@see https://github.com/nlohmann\n@since version 1.0.0\n*/\nnamespace nlohmann\n{\n\n/*!\n@brief a class to store JSON values\n\n@tparam ObjectType type for JSON objects (`std::map` by default; will be used\nin @ref object_t)\n@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used\nin @ref array_t)\n@tparam StringType type for JSON strings and object keys (`std::string` by\ndefault; will be used in @ref string_t)\n@tparam BooleanType type for JSON booleans (`bool` by default; will be used\nin @ref boolean_t)\n@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by\ndefault; will be used in @ref number_integer_t)\n@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c\n`uint64_t` by default; will be used in @ref number_unsigned_t)\n@tparam NumberFloatType type for JSON floating-point numbers (`double` by\ndefault; will be used in @ref number_float_t)\n@tparam AllocatorType type of the allocator to use (`std::allocator` by\ndefault)\n@tparam JSONSerializer the serializer to resolve internal calls to `to_json()`\nand `from_json()` (@ref adl_serializer by default)\n\n@requirement The class satisfies the following concept requirements:\n- Basic\n - [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible):\n   JSON values can be default constructed. The result will be a JSON null\n   value.\n - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible):\n   A JSON value can be constructed from an rvalue argument.\n - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible):\n   A JSON value can be copy-constructed from an lvalue expression.\n - [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable):\n   A JSON value van be assigned from an rvalue argument.\n - [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable):\n   A JSON value can be copy-assigned from an lvalue expression.\n - [Destructible](https://en.cppreference.com/w/cpp/named_req/Destructible):\n   JSON values can be destructed.\n- Layout\n - [StandardLayoutType](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType):\n   JSON values have\n   [standard layout](https://en.cppreference.com/w/cpp/language/data_members#Standard_layout):\n   All non-static data members are private and standard layout types, the\n   class has no virtual functions or (virtual) base classes.\n- Library-wide\n - [EqualityComparable](https://en.cppreference.com/w/cpp/named_req/EqualityComparable):\n   JSON values can be compared with `==`, see @ref\n   operator==(const_reference,const_reference).\n - [LessThanComparable](https://en.cppreference.com/w/cpp/named_req/LessThanComparable):\n   JSON values can be compared with `<`, see @ref\n   operator<(const_reference,const_reference).\n - [Swappable](https://en.cppreference.com/w/cpp/named_req/Swappable):\n   Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of\n   other compatible types, using unqualified function call @ref swap().\n - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer):\n   JSON values can be compared against `std::nullptr_t` objects which are used\n   to model the `null` value.\n- Container\n - [Container](https://en.cppreference.com/w/cpp/named_req/Container):\n   JSON values can be used like STL containers and provide iterator access.\n - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer);\n   JSON values can be used like STL containers and provide reverse iterator\n   access.\n\n@invariant The member variables @a m_value and @a m_type have the following\nrelationship:\n- If `m_type == value_t::object`, then `m_value.object != nullptr`.\n- If `m_type == value_t::array`, then `m_value.array != nullptr`.\n- If `m_type == value_t::string`, then `m_value.string != nullptr`.\nThe invariants are checked by member function assert_invariant().\n\n@internal\n@note ObjectType trick from http://stackoverflow.com/a/9860911\n@endinternal\n\n@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange\nFormat](http://rfc7159.net/rfc7159)\n\n@since version 1.0.0\n\n@nosubgrouping\n*/\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nclass basic_json\n{\n  private:\n    template<detail::value_t> friend struct detail::external_constructor;\n    friend ::nlohmann::json_pointer<basic_json>;\n    friend ::nlohmann::detail::parser<basic_json>;\n    friend ::nlohmann::detail::serializer<basic_json>;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::iter_impl;\n    template<typename BasicJsonType, typename CharType>\n    friend class ::nlohmann::detail::binary_writer;\n    template<typename BasicJsonType, typename SAX>\n    friend class ::nlohmann::detail::binary_reader;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::json_sax_dom_parser;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::json_sax_dom_callback_parser;\n\n    /// workaround type for MSVC\n    using basic_json_t = NLOHMANN_BASIC_JSON_TPL;\n\n    // convenience aliases for types residing in namespace detail;\n    using lexer = ::nlohmann::detail::lexer<basic_json>;\n    using parser = ::nlohmann::detail::parser<basic_json>;\n\n    using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;\n    template<typename BasicJsonType>\n    using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;\n    template<typename BasicJsonType>\n    using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>;\n    template<typename Iterator>\n    using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>;\n    template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>;\n\n    template<typename CharType>\n    using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>;\n\n    using binary_reader = ::nlohmann::detail::binary_reader<basic_json>;\n    template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;\n\n    using serializer = ::nlohmann::detail::serializer<basic_json>;\n\n  public:\n    using value_t = detail::value_t;\n    /// JSON Pointer, see @ref nlohmann::json_pointer\n    using json_pointer = ::nlohmann::json_pointer<basic_json>;\n    template<typename T, typename SFINAE>\n    using json_serializer = JSONSerializer<T, SFINAE>;\n    /// how to treat decoding errors\n    using error_handler_t = detail::error_handler_t;\n    /// helper type for initializer lists of basic_json values\n    using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;\n\n    using input_format_t = detail::input_format_t;\n    /// SAX interface type, see @ref nlohmann::json_sax\n    using json_sax_t = json_sax<basic_json>;\n\n    ////////////////\n    // exceptions //\n    ////////////////\n\n    /// @name exceptions\n    /// Classes to implement user-defined exceptions.\n    /// @{\n\n    /// @copydoc detail::exception\n    using exception = detail::exception;\n    /// @copydoc detail::parse_error\n    using parse_error = detail::parse_error;\n    /// @copydoc detail::invalid_iterator\n    using invalid_iterator = detail::invalid_iterator;\n    /// @copydoc detail::type_error\n    using type_error = detail::type_error;\n    /// @copydoc detail::out_of_range\n    using out_of_range = detail::out_of_range;\n    /// @copydoc detail::other_error\n    using other_error = detail::other_error;\n\n    /// @}\n\n\n    /////////////////////\n    // container types //\n    /////////////////////\n\n    /// @name container types\n    /// The canonic container types to use @ref basic_json like any other STL\n    /// container.\n    /// @{\n\n    /// the type of elements in a basic_json container\n    using value_type = basic_json;\n\n    /// the type of an element reference\n    using reference = value_type&;\n    /// the type of an element const reference\n    using const_reference = const value_type&;\n\n    /// a type to represent differences between iterators\n    using difference_type = std::ptrdiff_t;\n    /// a type to represent container sizes\n    using size_type = std::size_t;\n\n    /// the allocator type\n    using allocator_type = AllocatorType<basic_json>;\n\n    /// the type of an element pointer\n    using pointer = typename std::allocator_traits<allocator_type>::pointer;\n    /// the type of an element const pointer\n    using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;\n\n    /// an iterator for a basic_json container\n    using iterator = iter_impl<basic_json>;\n    /// a const iterator for a basic_json container\n    using const_iterator = iter_impl<const basic_json>;\n    /// a reverse iterator for a basic_json container\n    using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>;\n    /// a const reverse iterator for a basic_json container\n    using const_reverse_iterator = json_reverse_iterator<typename basic_json::const_iterator>;\n\n    /// @}\n\n\n    /*!\n    @brief returns the allocator associated with the container\n    */\n    static allocator_type get_allocator()\n    {\n        return allocator_type();\n    }\n\n    /*!\n    @brief returns version information on the library\n\n    This function returns a JSON object with information about the library,\n    including the version number and information on the platform and compiler.\n\n    @return JSON object holding version information\n    key         | description\n    ----------- | ---------------\n    `compiler`  | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version).\n    `copyright` | The copyright line for the library as string.\n    `name`      | The name of the library as string.\n    `platform`  | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`.\n    `url`       | The URL of the project as string.\n    `version`   | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string).\n\n    @liveexample{The following code shows an example output of the `meta()`\n    function.,meta}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @complexity Constant.\n\n    @since 2.1.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json meta()\n    {\n        basic_json result;\n\n        result[\"copyright\"] = \"(C) 2013-2017 Niels Lohmann\";\n        result[\"name\"] = \"JSON for Modern C++\";\n        result[\"url\"] = \"https://github.com/nlohmann/json\";\n        result[\"version\"][\"string\"] =\n            std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + \".\" +\n            std::to_string(NLOHMANN_JSON_VERSION_MINOR) + \".\" +\n            std::to_string(NLOHMANN_JSON_VERSION_PATCH);\n        result[\"version\"][\"major\"] = NLOHMANN_JSON_VERSION_MAJOR;\n        result[\"version\"][\"minor\"] = NLOHMANN_JSON_VERSION_MINOR;\n        result[\"version\"][\"patch\"] = NLOHMANN_JSON_VERSION_PATCH;\n\n#ifdef _WIN32\n        result[\"platform\"] = \"win32\";\n#elif defined __linux__\n        result[\"platform\"] = \"linux\";\n#elif defined __APPLE__\n        result[\"platform\"] = \"apple\";\n#elif defined __unix__\n        result[\"platform\"] = \"unix\";\n#else\n        result[\"platform\"] = \"unknown\";\n#endif\n\n#if defined(__ICC) || defined(__INTEL_COMPILER)\n        result[\"compiler\"] = {{\"family\", \"icc\"}, {\"version\", __INTEL_COMPILER}};\n#elif defined(__clang__)\n        result[\"compiler\"] = {{\"family\", \"clang\"}, {\"version\", __clang_version__}};\n#elif defined(__GNUC__) || defined(__GNUG__)\n        result[\"compiler\"] = {{\"family\", \"gcc\"}, {\"version\", std::to_string(__GNUC__) + \".\" + std::to_string(__GNUC_MINOR__) + \".\" + std::to_string(__GNUC_PATCHLEVEL__)}};\n#elif defined(__HP_cc) || defined(__HP_aCC)\n        result[\"compiler\"] = \"hp\"\n#elif defined(__IBMCPP__)\n        result[\"compiler\"] = {{\"family\", \"ilecpp\"}, {\"version\", __IBMCPP__}};\n#elif defined(_MSC_VER)\n        result[\"compiler\"] = {{\"family\", \"msvc\"}, {\"version\", _MSC_VER}};\n#elif defined(__PGI)\n        result[\"compiler\"] = {{\"family\", \"pgcpp\"}, {\"version\", __PGI}};\n#elif defined(__SUNPRO_CC)\n        result[\"compiler\"] = {{\"family\", \"sunpro\"}, {\"version\", __SUNPRO_CC}};\n#else\n        result[\"compiler\"] = {{\"family\", \"unknown\"}, {\"version\", \"unknown\"}};\n#endif\n\n#ifdef __cplusplus\n        result[\"compiler\"][\"c++\"] = std::to_string(__cplusplus);\n#else\n        result[\"compiler\"][\"c++\"] = \"unknown\";\n#endif\n        return result;\n    }\n\n\n    ///////////////////////////\n    // JSON value data types //\n    ///////////////////////////\n\n    /// @name JSON value data types\n    /// The data types to store a JSON value. These types are derived from\n    /// the template arguments passed to class @ref basic_json.\n    /// @{\n\n#if defined(JSON_HAS_CPP_14)\n    // Use transparent comparator if possible, combined with perfect forwarding\n    // on find() and count() calls prevents unnecessary string construction.\n    using object_comparator_t = std::less<>;\n#else\n    using object_comparator_t = std::less<StringType>;\n#endif\n\n    /*!\n    @brief a type for an object\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows:\n    > An object is an unordered collection of zero or more name/value pairs,\n    > where a name is a string and a value is a string, number, boolean, null,\n    > object, or array.\n\n    To store objects in C++, a type is defined by the template parameters\n    described below.\n\n    @tparam ObjectType  the container to store objects (e.g., `std::map` or\n    `std::unordered_map`)\n    @tparam StringType the type of the keys or names (e.g., `std::string`).\n    The comparison function `std::less<StringType>` is used to order elements\n    inside the container.\n    @tparam AllocatorType the allocator to use for objects (e.g.,\n    `std::allocator`)\n\n    #### Default type\n\n    With the default values for @a ObjectType (`std::map`), @a StringType\n    (`std::string`), and @a AllocatorType (`std::allocator`), the default\n    value for @a object_t is:\n\n    @code {.cpp}\n    std::map<\n      std::string, // key_type\n      basic_json, // value_type\n      std::less<std::string>, // key_compare\n      std::allocator<std::pair<const std::string, basic_json>> // allocator_type\n    >\n    @endcode\n\n    #### Behavior\n\n    The choice of @a object_t influences the behavior of the JSON class. With\n    the default type, objects have the following behavior:\n\n    - When all names are unique, objects will be interoperable in the sense\n      that all software implementations receiving that object will agree on\n      the name-value mappings.\n    - When the names within an object are not unique, it is unspecified which\n      one of the values for a given key will be chosen. For instance,\n      `{\"key\": 2, \"key\": 1}` could be equal to either `{\"key\": 1}` or\n      `{\"key\": 2}`.\n    - Internally, name/value pairs are stored in lexicographical order of the\n      names. Objects will also be serialized (see @ref dump) in this order.\n      For instance, `{\"b\": 1, \"a\": 2}` and `{\"a\": 2, \"b\": 1}` will be stored\n      and serialized as `{\"a\": 2, \"b\": 1}`.\n    - When comparing objects, the order of the name/value pairs is irrelevant.\n      This makes objects interoperable in the sense that they will not be\n      affected by these differences. For instance, `{\"b\": 1, \"a\": 2}` and\n      `{\"a\": 2, \"b\": 1}` will be treated as equal.\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) specifies:\n    > An implementation may set limits on the maximum depth of nesting.\n\n    In this class, the object's limit of nesting is not explicitly constrained.\n    However, a maximum depth of nesting may be introduced by the compiler or\n    runtime environment. A theoretical limit can be queried by calling the\n    @ref max_size function of a JSON object.\n\n    #### Storage\n\n    Objects are stored as pointers in a @ref basic_json type. That is, for any\n    access to object values, a pointer of type `object_t*` must be\n    dereferenced.\n\n    @sa @ref array_t -- type for an array value\n\n    @since version 1.0.0\n\n    @note The order name/value pairs are added to the object is *not*\n    preserved by the library. Therefore, iterating an object may return\n    name/value pairs in a different order than they were originally stored. In\n    fact, keys will be traversed in alphabetical order as `std::map` with\n    `std::less` is used by default. Please note this behavior conforms to [RFC\n    7159](http://rfc7159.net/rfc7159), because any order implements the\n    specified \"unordered\" nature of JSON objects.\n    */\n    using object_t = ObjectType<StringType,\n          basic_json,\n          object_comparator_t,\n          AllocatorType<std::pair<const StringType,\n          basic_json>>>;\n\n    /*!\n    @brief a type for an array\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows:\n    > An array is an ordered sequence of zero or more values.\n\n    To store objects in C++, a type is defined by the template parameters\n    explained below.\n\n    @tparam ArrayType  container type to store arrays (e.g., `std::vector` or\n    `std::list`)\n    @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`)\n\n    #### Default type\n\n    With the default values for @a ArrayType (`std::vector`) and @a\n    AllocatorType (`std::allocator`), the default value for @a array_t is:\n\n    @code {.cpp}\n    std::vector<\n      basic_json, // value_type\n      std::allocator<basic_json> // allocator_type\n    >\n    @endcode\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) specifies:\n    > An implementation may set limits on the maximum depth of nesting.\n\n    In this class, the array's limit of nesting is not explicitly constrained.\n    However, a maximum depth of nesting may be introduced by the compiler or\n    runtime environment. A theoretical limit can be queried by calling the\n    @ref max_size function of a JSON array.\n\n    #### Storage\n\n    Arrays are stored as pointers in a @ref basic_json type. That is, for any\n    access to array values, a pointer of type `array_t*` must be dereferenced.\n\n    @sa @ref object_t -- type for an object value\n\n    @since version 1.0.0\n    */\n    using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;\n\n    /*!\n    @brief a type for a string\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows:\n    > A string is a sequence of zero or more Unicode characters.\n\n    To store objects in C++, a type is defined by the template parameter\n    described below. Unicode values are split by the JSON class into\n    byte-sized characters during deserialization.\n\n    @tparam StringType  the container to store strings (e.g., `std::string`).\n    Note this container is used for keys/names in objects, see @ref object_t.\n\n    #### Default type\n\n    With the default values for @a StringType (`std::string`), the default\n    value for @a string_t is:\n\n    @code {.cpp}\n    std::string\n    @endcode\n\n    #### Encoding\n\n    Strings are stored in UTF-8 encoding. Therefore, functions like\n    `std::string::size()` or `std::string::length()` return the number of\n    bytes in the string rather than the number of characters or glyphs.\n\n    #### String comparison\n\n    [RFC 7159](http://rfc7159.net/rfc7159) states:\n    > Software implementations are typically required to test names of object\n    > members for equality. Implementations that transform the textual\n    > representation into sequences of Unicode code units and then perform the\n    > comparison numerically, code unit by code unit, are interoperable in the\n    > sense that implementations will agree in all cases on equality or\n    > inequality of two strings. For example, implementations that compare\n    > strings with escaped characters unconverted may incorrectly find that\n    > `\"a\\\\b\"` and `\"a\\u005Cb\"` are not equal.\n\n    This implementation is interoperable as it does compare strings code unit\n    by code unit.\n\n    #### Storage\n\n    String values are stored as pointers in a @ref basic_json type. That is,\n    for any access to string values, a pointer of type `string_t*` must be\n    dereferenced.\n\n    @since version 1.0.0\n    */\n    using string_t = StringType;\n\n    /*!\n    @brief a type for a boolean\n\n    [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a\n    type which differentiates the two literals `true` and `false`.\n\n    To store objects in C++, a type is defined by the template parameter @a\n    BooleanType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a BooleanType (`bool`), the default value for\n    @a boolean_t is:\n\n    @code {.cpp}\n    bool\n    @endcode\n\n    #### Storage\n\n    Boolean values are stored directly inside a @ref basic_json type.\n\n    @since version 1.0.0\n    */\n    using boolean_t = BooleanType;\n\n    /*!\n    @brief a type for a number (integer)\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store integer numbers in C++, a type is defined by the template\n    parameter @a NumberIntegerType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberIntegerType (`int64_t`), the default\n    value for @a number_integer_t is:\n\n    @code {.cpp}\n    int64_t\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in integer literals lead to an interpretation as octal\n      number. Internally, the value will be stored as decimal number. For\n      instance, the C++ integer literal `010` will be serialized to `8`.\n      During deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) specifies:\n    > An implementation may set limits on the range and precision of numbers.\n\n    When the default type is used, the maximal integer number that can be\n    stored is `9223372036854775807` (INT64_MAX) and the minimal integer number\n    that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers\n    that are out of range will yield over/underflow when used in a\n    constructor. During deserialization, too large or small integer numbers\n    will be automatically be stored as @ref number_unsigned_t or @ref\n    number_float_t.\n\n    [RFC 7159](http://rfc7159.net/rfc7159) further states:\n    > Note that when such software is used, numbers that are integers and are\n    > in the range \\f$[-2^{53}+1, 2^{53}-1]\\f$ are interoperable in the sense\n    > that implementations will agree exactly on their numeric values.\n\n    As this range is a subrange of the exactly supported range [INT64_MIN,\n    INT64_MAX], this class's integer type is interoperable.\n\n    #### Storage\n\n    Integer number values are stored directly inside a @ref basic_json type.\n\n    @sa @ref number_float_t -- type for number values (floating-point)\n\n    @sa @ref number_unsigned_t -- type for number values (unsigned integer)\n\n    @since version 1.0.0\n    */\n    using number_integer_t = NumberIntegerType;\n\n    /*!\n    @brief a type for a number (unsigned)\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store unsigned integer numbers in C++, a type is defined by the\n    template parameter @a NumberUnsignedType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberUnsignedType (`uint64_t`), the\n    default value for @a number_unsigned_t is:\n\n    @code {.cpp}\n    uint64_t\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in integer literals lead to an interpretation as octal\n      number. Internally, the value will be stored as decimal number. For\n      instance, the C++ integer literal `010` will be serialized to `8`.\n      During deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) specifies:\n    > An implementation may set limits on the range and precision of numbers.\n\n    When the default type is used, the maximal integer number that can be\n    stored is `18446744073709551615` (UINT64_MAX) and the minimal integer\n    number that can be stored is `0`. Integer numbers that are out of range\n    will yield over/underflow when used in a constructor. During\n    deserialization, too large or small integer numbers will be automatically\n    be stored as @ref number_integer_t or @ref number_float_t.\n\n    [RFC 7159](http://rfc7159.net/rfc7159) further states:\n    > Note that when such software is used, numbers that are integers and are\n    > in the range \\f$[-2^{53}+1, 2^{53}-1]\\f$ are interoperable in the sense\n    > that implementations will agree exactly on their numeric values.\n\n    As this range is a subrange (when considered in conjunction with the\n    number_integer_t type) of the exactly supported range [0, UINT64_MAX],\n    this class's integer type is interoperable.\n\n    #### Storage\n\n    Integer number values are stored directly inside a @ref basic_json type.\n\n    @sa @ref number_float_t -- type for number values (floating-point)\n    @sa @ref number_integer_t -- type for number values (integer)\n\n    @since version 2.0.0\n    */\n    using number_unsigned_t = NumberUnsignedType;\n\n    /*!\n    @brief a type for a number (floating-point)\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store floating-point numbers in C++, a type is defined by the template\n    parameter @a NumberFloatType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberFloatType (`double`), the default\n    value for @a number_float_t is:\n\n    @code {.cpp}\n    double\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in floating-point literals will be ignored. Internally,\n      the value will be stored as decimal number. For instance, the C++\n      floating-point literal `01.2` will be serialized to `1.2`. During\n      deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) states:\n    > This specification allows implementations to set limits on the range and\n    > precision of numbers accepted. Since software that implements IEEE\n    > 754-2008 binary64 (double precision) numbers is generally available and\n    > widely used, good interoperability can be achieved by implementations\n    > that expect no more precision or range than these provide, in the sense\n    > that implementations will approximate JSON numbers within the expected\n    > precision.\n\n    This implementation does exactly follow this approach, as it uses double\n    precision floating-point numbers. Note values smaller than\n    `-1.79769313486232e+308` and values greater than `1.79769313486232e+308`\n    will be stored as NaN internally and be serialized to `null`.\n\n    #### Storage\n\n    Floating-point number values are stored directly inside a @ref basic_json\n    type.\n\n    @sa @ref number_integer_t -- type for number values (integer)\n\n    @sa @ref number_unsigned_t -- type for number values (unsigned integer)\n\n    @since version 1.0.0\n    */\n    using number_float_t = NumberFloatType;\n\n    /// @}\n\n  private:\n\n    /// helper for exception-safe object creation\n    template<typename T, typename... Args>\n    JSON_HEDLEY_RETURNS_NON_NULL\n    static T* create(Args&& ... args)\n    {\n        AllocatorType<T> alloc;\n        using AllocatorTraits = std::allocator_traits<AllocatorType<T>>;\n\n        auto deleter = [&](T * object)\n        {\n            AllocatorTraits::deallocate(alloc, object, 1);\n        };\n        std::unique_ptr<T, decltype(deleter)> object(AllocatorTraits::allocate(alloc, 1), deleter);\n        AllocatorTraits::construct(alloc, object.get(), std::forward<Args>(args)...);\n        assert(object != nullptr);\n        return object.release();\n    }\n\n    ////////////////////////\n    // JSON value storage //\n    ////////////////////////\n\n    /*!\n    @brief a JSON value\n\n    The actual storage for a JSON value of the @ref basic_json class. This\n    union combines the different storage types for the JSON value types\n    defined in @ref value_t.\n\n    JSON type | value_t type    | used type\n    --------- | --------------- | ------------------------\n    object    | object          | pointer to @ref object_t\n    array     | array           | pointer to @ref array_t\n    string    | string          | pointer to @ref string_t\n    boolean   | boolean         | @ref boolean_t\n    number    | number_integer  | @ref number_integer_t\n    number    | number_unsigned | @ref number_unsigned_t\n    number    | number_float    | @ref number_float_t\n    null      | null            | *no value is stored*\n\n    @note Variable-length types (objects, arrays, and strings) are stored as\n    pointers. The size of the union should not exceed 64 bits if the default\n    value types are used.\n\n    @since version 1.0.0\n    */\n    union json_value\n    {\n        /// object (stored with pointer to save storage)\n        object_t* object;\n        /// array (stored with pointer to save storage)\n        array_t* array;\n        /// string (stored with pointer to save storage)\n        string_t* string;\n        /// boolean\n        boolean_t boolean;\n        /// number (integer)\n        number_integer_t number_integer;\n        /// number (unsigned integer)\n        number_unsigned_t number_unsigned;\n        /// number (floating-point)\n        number_float_t number_float;\n\n        /// default constructor (for null values)\n        json_value() = default;\n        /// constructor for booleans\n        json_value(boolean_t v) noexcept : boolean(v) {}\n        /// constructor for numbers (integer)\n        json_value(number_integer_t v) noexcept : number_integer(v) {}\n        /// constructor for numbers (unsigned)\n        json_value(number_unsigned_t v) noexcept : number_unsigned(v) {}\n        /// constructor for numbers (floating-point)\n        json_value(number_float_t v) noexcept : number_float(v) {}\n        /// constructor for empty values of a given type\n        json_value(value_t t)\n        {\n            switch (t)\n            {\n                case value_t::object:\n                {\n                    object = create<object_t>();\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    array = create<array_t>();\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    string = create<string_t>(\"\");\n                    break;\n                }\n\n                case value_t::boolean:\n                {\n                    boolean = boolean_t(false);\n                    break;\n                }\n\n                case value_t::number_integer:\n                {\n                    number_integer = number_integer_t(0);\n                    break;\n                }\n\n                case value_t::number_unsigned:\n                {\n                    number_unsigned = number_unsigned_t(0);\n                    break;\n                }\n\n                case value_t::number_float:\n                {\n                    number_float = number_float_t(0.0);\n                    break;\n                }\n\n                case value_t::null:\n                {\n                    object = nullptr;  // silence warning, see #821\n                    break;\n                }\n\n                default:\n                {\n                    object = nullptr;  // silence warning, see #821\n                    if (JSON_HEDLEY_UNLIKELY(t == value_t::null))\n                    {\n                        JSON_THROW(other_error::create(500, \"961c151d2e87f2686a955a9be24d316f1362bf21 3.7.3\")); // LCOV_EXCL_LINE\n                    }\n                    break;\n                }\n            }\n        }\n\n        /// constructor for strings\n        json_value(const string_t& value)\n        {\n            string = create<string_t>(value);\n        }\n\n        /// constructor for rvalue strings\n        json_value(string_t&& value)\n        {\n            string = create<string_t>(std::move(value));\n        }\n\n        /// constructor for objects\n        json_value(const object_t& value)\n        {\n            object = create<object_t>(value);\n        }\n\n        /// constructor for rvalue objects\n        json_value(object_t&& value)\n        {\n            object = create<object_t>(std::move(value));\n        }\n\n        /// constructor for arrays\n        json_value(const array_t& value)\n        {\n            array = create<array_t>(value);\n        }\n\n        /// constructor for rvalue arrays\n        json_value(array_t&& value)\n        {\n            array = create<array_t>(std::move(value));\n        }\n\n        void destroy(value_t t) noexcept\n        {\n            // flatten the current json_value to a heap-allocated stack\n            std::vector<basic_json> stack;\n\n            // move the top-level items to stack\n            if (t == value_t::array)\n            {\n                stack.reserve(array->size());\n                std::move(array->begin(), array->end(), std::back_inserter(stack));\n            }\n            else if (t == value_t::object)\n            {\n                stack.reserve(object->size());\n                for (auto&& it : *object)\n                {\n                    stack.push_back(std::move(it.second));\n                }\n            }\n\n            while (not stack.empty())\n            {\n                // move the last item to local variable to be processed\n                basic_json current_item(std::move(stack.back()));\n                stack.pop_back();\n\n                // if current_item is array/object, move\n                // its children to the stack to be processed later\n                if (current_item.is_array())\n                {\n                    std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(),\n                              std::back_inserter(stack));\n\n                    current_item.m_value.array->clear();\n                }\n                else if (current_item.is_object())\n                {\n                    for (auto&& it : *current_item.m_value.object)\n                    {\n                        stack.push_back(std::move(it.second));\n                    }\n\n                    current_item.m_value.object->clear();\n                }\n\n                // it's now safe that current_item get destructed\n                // since it doesn't have any children\n            }\n\n            switch (t)\n            {\n                case value_t::object:\n                {\n                    AllocatorType<object_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, object);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    AllocatorType<array_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, array);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);\n                    break;\n                }\n\n                default:\n                {\n                    break;\n                }\n            }\n        }\n    };\n\n    /*!\n    @brief checks the class invariants\n\n    This function asserts the class invariants. It needs to be called at the\n    end of every constructor to make sure that created objects respect the\n    invariant. Furthermore, it has to be called each time the type of a JSON\n    value is changed, because the invariant expresses a relationship between\n    @a m_type and @a m_value.\n    */\n    void assert_invariant() const noexcept\n    {\n        assert(m_type != value_t::object or m_value.object != nullptr);\n        assert(m_type != value_t::array or m_value.array != nullptr);\n        assert(m_type != value_t::string or m_value.string != nullptr);\n    }\n\n  public:\n    //////////////////////////\n    // JSON parser callback //\n    //////////////////////////\n\n    /*!\n    @brief parser event types\n\n    The parser callback distinguishes the following events:\n    - `object_start`: the parser read `{` and started to process a JSON object\n    - `key`: the parser read a key of a value in an object\n    - `object_end`: the parser read `}` and finished processing a JSON object\n    - `array_start`: the parser read `[` and started to process a JSON array\n    - `array_end`: the parser read `]` and finished processing a JSON array\n    - `value`: the parser finished reading a JSON value\n\n    @image html callback_events.png \"Example when certain parse events are triggered\"\n\n    @sa @ref parser_callback_t for more information and examples\n    */\n    using parse_event_t = typename parser::parse_event_t;\n\n    /*!\n    @brief per-element parser callback type\n\n    With a parser callback function, the result of parsing a JSON text can be\n    influenced. When passed to @ref parse, it is called on certain events\n    (passed as @ref parse_event_t via parameter @a event) with a set recursion\n    depth @a depth and context JSON value @a parsed. The return value of the\n    callback function is a boolean indicating whether the element that emitted\n    the callback shall be kept or not.\n\n    We distinguish six scenarios (determined by the event type) in which the\n    callback function can be called. The following table describes the values\n    of the parameters @a depth, @a event, and @a parsed.\n\n    parameter @a event | description | parameter @a depth | parameter @a parsed\n    ------------------ | ----------- | ------------------ | -------------------\n    parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded\n    parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key\n    parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object\n    parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded\n    parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array\n    parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value\n\n    @image html callback_events.png \"Example when certain parse events are triggered\"\n\n    Discarding a value (i.e., returning `false`) has different effects\n    depending on the context in which function was called:\n\n    - Discarded values in structured types are skipped. That is, the parser\n      will behave as if the discarded value was never read.\n    - In case a value outside a structured type is skipped, it is replaced\n      with `null`. This case happens if the top-level element is skipped.\n\n    @param[in] depth  the depth of the recursion during parsing\n\n    @param[in] event  an event of type parse_event_t indicating the context in\n    the callback function has been called\n\n    @param[in,out] parsed  the current intermediate parse result; note that\n    writing to this value has no effect for parse_event_t::key events\n\n    @return Whether the JSON value which called the function during parsing\n    should be kept (`true`) or not (`false`). In the latter case, it is either\n    skipped completely or replaced by an empty discarded object.\n\n    @sa @ref parse for examples\n\n    @since version 1.0.0\n    */\n    using parser_callback_t = typename parser::parser_callback_t;\n\n    //////////////////\n    // constructors //\n    //////////////////\n\n    /// @name constructors and destructors\n    /// Constructors of class @ref basic_json, copy/move constructor, copy\n    /// assignment, static functions creating objects, and the destructor.\n    /// @{\n\n    /*!\n    @brief create an empty value with a given type\n\n    Create an empty JSON value with a given type. The value will be default\n    initialized with an empty value which depends on the type:\n\n    Value type  | initial value\n    ----------- | -------------\n    null        | `null`\n    boolean     | `false`\n    string      | `\"\"`\n    number      | `0`\n    object      | `{}`\n    array       | `[]`\n\n    @param[in] v  the type of the value to create\n\n    @complexity Constant.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows the constructor for different @ref\n    value_t values,basic_json__value_t}\n\n    @sa @ref clear() -- restores the postcondition of this constructor\n\n    @since version 1.0.0\n    */\n    basic_json(const value_t v)\n        : m_type(v), m_value(v)\n    {\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a null object\n\n    Create a `null` JSON value. It either takes a null pointer as parameter\n    (explicitly creating `null`) or no parameter (implicitly creating `null`).\n    The passed null pointer itself is not read -- it is only used to choose\n    the right constructor.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this constructor never throws\n    exceptions.\n\n    @liveexample{The following code shows the constructor with and without a\n    null pointer parameter.,basic_json__nullptr_t}\n\n    @since version 1.0.0\n    */\n    basic_json(std::nullptr_t = nullptr) noexcept\n        : basic_json(value_t::null)\n    {\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a JSON value\n\n    This is a \"catch all\" constructor for all compatible JSON types; that is,\n    types for which a `to_json()` method exists. The constructor forwards the\n    parameter @a val to that method (to `json_serializer<U>::to_json` method\n    with `U = uncvref_t<CompatibleType>`, to be exact).\n\n    Template type @a CompatibleType includes, but is not limited to, the\n    following types:\n    - **arrays**: @ref array_t and all kinds of compatible containers such as\n      `std::vector`, `std::deque`, `std::list`, `std::forward_list`,\n      `std::array`, `std::valarray`, `std::set`, `std::unordered_set`,\n      `std::multiset`, and `std::unordered_multiset` with a `value_type` from\n      which a @ref basic_json value can be constructed.\n    - **objects**: @ref object_t and all kinds of compatible associative\n      containers such as `std::map`, `std::unordered_map`, `std::multimap`,\n      and `std::unordered_multimap` with a `key_type` compatible to\n      @ref string_t and a `value_type` from which a @ref basic_json value can\n      be constructed.\n    - **strings**: @ref string_t, string literals, and all compatible string\n      containers can be used.\n    - **numbers**: @ref number_integer_t, @ref number_unsigned_t,\n      @ref number_float_t, and all convertible number types such as `int`,\n      `size_t`, `int64_t`, `float` or `double` can be used.\n    - **boolean**: @ref boolean_t / `bool` can be used.\n\n    See the examples below.\n\n    @tparam CompatibleType a type such that:\n    - @a CompatibleType is not derived from `std::istream`,\n    - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move\n         constructors),\n    - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments)\n    - @a CompatibleType is not a @ref basic_json nested type (e.g.,\n         @ref json_pointer, @ref iterator, etc ...)\n    - @ref @ref json_serializer<U> has a\n         `to_json(basic_json_t&, CompatibleType&&)` method\n\n    @tparam U = `uncvref_t<CompatibleType>`\n\n    @param[in] val the value to be forwarded to the respective constructor\n\n    @complexity Usually linear in the size of the passed @a val, also\n                depending on the implementation of the called `to_json()`\n                method.\n\n    @exceptionsafety Depends on the called constructor. For types directly\n    supported by the library (i.e., all types for which no `to_json()` function\n    was provided), strong guarantee holds: if an exception is thrown, there are\n    no changes to any JSON value.\n\n    @liveexample{The following code shows the constructor with several\n    compatible types.,basic_json__CompatibleType}\n\n    @since version 2.1.0\n    */\n    template <typename CompatibleType,\n              typename U = detail::uncvref_t<CompatibleType>,\n              detail::enable_if_t<\n                  not detail::is_basic_json<U>::value and detail::is_compatible_type<basic_json_t, U>::value, int> = 0>\n    basic_json(CompatibleType && val) noexcept(noexcept(\n                JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),\n                                           std::forward<CompatibleType>(val))))\n    {\n        JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a JSON value from an existing one\n\n    This is a constructor for existing @ref basic_json types.\n    It does not hijack copy/move constructors, since the parameter has different\n    template arguments than the current ones.\n\n    The constructor tries to convert the internal @ref m_value of the parameter.\n\n    @tparam BasicJsonType a type such that:\n    - @a BasicJsonType is a @ref basic_json type.\n    - @a BasicJsonType has different template arguments than @ref basic_json_t.\n\n    @param[in] val the @ref basic_json value to be converted.\n\n    @complexity Usually linear in the size of the passed @a val, also\n                depending on the implementation of the called `to_json()`\n                method.\n\n    @exceptionsafety Depends on the called constructor. For types directly\n    supported by the library (i.e., all types for which no `to_json()` function\n    was provided), strong guarantee holds: if an exception is thrown, there are\n    no changes to any JSON value.\n\n    @since version 3.2.0\n    */\n    template <typename BasicJsonType,\n              detail::enable_if_t<\n                  detail::is_basic_json<BasicJsonType>::value and not std::is_same<basic_json, BasicJsonType>::value, int> = 0>\n    basic_json(const BasicJsonType& val)\n    {\n        using other_boolean_t = typename BasicJsonType::boolean_t;\n        using other_number_float_t = typename BasicJsonType::number_float_t;\n        using other_number_integer_t = typename BasicJsonType::number_integer_t;\n        using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n        using other_string_t = typename BasicJsonType::string_t;\n        using other_object_t = typename BasicJsonType::object_t;\n        using other_array_t = typename BasicJsonType::array_t;\n\n        switch (val.type())\n        {\n            case value_t::boolean:\n                JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());\n                break;\n            case value_t::number_float:\n                JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());\n                break;\n            case value_t::number_integer:\n                JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());\n                break;\n            case value_t::number_unsigned:\n                JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());\n                break;\n            case value_t::string:\n                JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());\n                break;\n            case value_t::object:\n                JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());\n                break;\n            case value_t::array:\n                JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());\n                break;\n            case value_t::null:\n                *this = nullptr;\n                break;\n            case value_t::discarded:\n                m_type = value_t::discarded;\n                break;\n            default:            // LCOV_EXCL_LINE\n                assert(false);  // LCOV_EXCL_LINE\n        }\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a container (array or object) from an initializer list\n\n    Creates a JSON value of type array or object from the passed initializer\n    list @a init. In case @a type_deduction is `true` (default), the type of\n    the JSON value to be created is deducted from the initializer list @a init\n    according to the following rules:\n\n    1. If the list is empty, an empty JSON object value `{}` is created.\n    2. If the list consists of pairs whose first element is a string, a JSON\n       object value is created where the first elements of the pairs are\n       treated as keys and the second elements are as values.\n    3. In all other cases, an array is created.\n\n    The rules aim to create the best fit between a C++ initializer list and\n    JSON values. The rationale is as follows:\n\n    1. The empty initializer list is written as `{}` which is exactly an empty\n       JSON object.\n    2. C++ has no way of describing mapped types other than to list a list of\n       pairs. As JSON requires that keys must be of type string, rule 2 is the\n       weakest constraint one can pose on initializer lists to interpret them\n       as an object.\n    3. In all other cases, the initializer list could not be interpreted as\n       JSON object type, so interpreting it as JSON array type is safe.\n\n    With the rules described above, the following JSON values cannot be\n    expressed by an initializer list:\n\n    - the empty array (`[]`): use @ref array(initializer_list_t)\n      with an empty initializer list in this case\n    - arrays whose elements satisfy rule 2: use @ref\n      array(initializer_list_t) with the same initializer list\n      in this case\n\n    @note When used without parentheses around an empty initializer list, @ref\n    basic_json() is called instead of this function, yielding the JSON null\n    value.\n\n    @param[in] init  initializer list with JSON values\n\n    @param[in] type_deduction internal parameter; when set to `true`, the type\n    of the JSON value is deducted from the initializer list @a init; when set\n    to `false`, the type provided via @a manual_type is forced. This mode is\n    used by the functions @ref array(initializer_list_t) and\n    @ref object(initializer_list_t).\n\n    @param[in] manual_type internal parameter; when @a type_deduction is set\n    to `false`, the created JSON value will use the provided type (only @ref\n    value_t::array and @ref value_t::object are valid); when @a type_deduction\n    is set to `true`, this parameter has no effect\n\n    @throw type_error.301 if @a type_deduction is `false`, @a manual_type is\n    `value_t::object`, but @a init contains an element which is not a pair\n    whose first element is a string. In this case, the constructor could not\n    create an object. If @a type_deduction would have be `true`, an array\n    would have been created. See @ref object(initializer_list_t)\n    for an example.\n\n    @complexity Linear in the size of the initializer list @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The example below shows how JSON values are created from\n    initializer lists.,basic_json__list_init_t}\n\n    @sa @ref array(initializer_list_t) -- create a JSON array\n    value from an initializer list\n    @sa @ref object(initializer_list_t) -- create a JSON object\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    basic_json(initializer_list_t init,\n               bool type_deduction = true,\n               value_t manual_type = value_t::array)\n    {\n        // check if each element is an array with two elements whose first\n        // element is a string\n        bool is_an_object = std::all_of(init.begin(), init.end(),\n                                        [](const detail::json_ref<basic_json>& element_ref)\n        {\n            return element_ref->is_array() and element_ref->size() == 2 and (*element_ref)[0].is_string();\n        });\n\n        // adjust type if type deduction is not wanted\n        if (not type_deduction)\n        {\n            // if array is wanted, do not create an object though possible\n            if (manual_type == value_t::array)\n            {\n                is_an_object = false;\n            }\n\n            // if object is wanted but impossible, throw an exception\n            if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object and not is_an_object))\n            {\n                JSON_THROW(type_error::create(301, \"cannot create object from initializer list\"));\n            }\n        }\n\n        if (is_an_object)\n        {\n            // the initializer list is a list of pairs -> create object\n            m_type = value_t::object;\n            m_value = value_t::object;\n\n            std::for_each(init.begin(), init.end(), [this](const detail::json_ref<basic_json>& element_ref)\n            {\n                auto element = element_ref.moved_or_copied();\n                m_value.object->emplace(\n                    std::move(*((*element.m_value.array)[0].m_value.string)),\n                    std::move((*element.m_value.array)[1]));\n            });\n        }\n        else\n        {\n            // the initializer list describes an array -> create array\n            m_type = value_t::array;\n            m_value.array = create<array_t>(init.begin(), init.end());\n        }\n\n        assert_invariant();\n    }\n\n    /*!\n    @brief explicitly create an array from an initializer list\n\n    Creates a JSON array value from a given initializer list. That is, given a\n    list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the\n    initializer list is empty, the empty array `[]` is created.\n\n    @note This function is only needed to express two edge cases that cannot\n    be realized with the initializer list constructor (@ref\n    basic_json(initializer_list_t, bool, value_t)). These cases\n    are:\n    1. creating an array whose elements are all pairs whose first element is a\n    string -- in this case, the initializer list constructor would create an\n    object, taking the first elements as keys\n    2. creating an empty array -- passing the empty initializer list to the\n    initializer list constructor yields an empty object\n\n    @param[in] init  initializer list with JSON values to create an array from\n    (optional)\n\n    @return JSON array value\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows an example for the `array`\n    function.,array}\n\n    @sa @ref basic_json(initializer_list_t, bool, value_t) --\n    create a JSON value from an initializer list\n    @sa @ref object(initializer_list_t) -- create a JSON object\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json array(initializer_list_t init = {})\n    {\n        return basic_json(init, false, value_t::array);\n    }\n\n    /*!\n    @brief explicitly create an object from an initializer list\n\n    Creates a JSON object value from a given initializer list. The initializer\n    lists elements must be pairs, and their first elements must be strings. If\n    the initializer list is empty, the empty object `{}` is created.\n\n    @note This function is only added for symmetry reasons. In contrast to the\n    related function @ref array(initializer_list_t), there are\n    no cases which can only be expressed by this function. That is, any\n    initializer list @a init can also be passed to the initializer list\n    constructor @ref basic_json(initializer_list_t, bool, value_t).\n\n    @param[in] init  initializer list to create an object from (optional)\n\n    @return JSON object value\n\n    @throw type_error.301 if @a init is not a list of pairs whose first\n    elements are strings. In this case, no object can be created. When such a\n    value is passed to @ref basic_json(initializer_list_t, bool, value_t),\n    an array would have been created from the passed initializer list @a init.\n    See example below.\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows an example for the `object`\n    function.,object}\n\n    @sa @ref basic_json(initializer_list_t, bool, value_t) --\n    create a JSON value from an initializer list\n    @sa @ref array(initializer_list_t) -- create a JSON array\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json object(initializer_list_t init = {})\n    {\n        return basic_json(init, false, value_t::object);\n    }\n\n    /*!\n    @brief construct an array with count copies of given value\n\n    Constructs a JSON array value by creating @a cnt copies of a passed value.\n    In case @a cnt is `0`, an empty array is created.\n\n    @param[in] cnt  the number of JSON copies of @a val to create\n    @param[in] val  the JSON value to copy\n\n    @post `std::distance(begin(),end()) == cnt` holds.\n\n    @complexity Linear in @a cnt.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows examples for the @ref\n    basic_json(size_type\\, const basic_json&)\n    constructor.,basic_json__size_type_basic_json}\n\n    @since version 1.0.0\n    */\n    basic_json(size_type cnt, const basic_json& val)\n        : m_type(value_t::array)\n    {\n        m_value.array = create<array_t>(cnt, val);\n        assert_invariant();\n    }\n\n    /*!\n    @brief construct a JSON container given an iterator range\n\n    Constructs the JSON value with the contents of the range `[first, last)`.\n    The semantics depends on the different types a JSON value can have:\n    - In case of a null type, invalid_iterator.206 is thrown.\n    - In case of other primitive types (number, boolean, or string), @a first\n      must be `begin()` and @a last must be `end()`. In this case, the value is\n      copied. Otherwise, invalid_iterator.204 is thrown.\n    - In case of structured types (array, object), the constructor behaves as\n      similar versions for `std::vector` or `std::map`; that is, a JSON array\n      or object is constructed from the values in the range.\n\n    @tparam InputIT an input iterator type (@ref iterator or @ref\n    const_iterator)\n\n    @param[in] first begin of the range to copy from (included)\n    @param[in] last end of the range to copy from (excluded)\n\n    @pre Iterators @a first and @a last must be initialized. **This\n         precondition is enforced with an assertion (see warning).** If\n         assertions are switched off, a violation of this precondition yields\n         undefined behavior.\n\n    @pre Range `[first, last)` is valid. Usually, this precondition cannot be\n         checked efficiently. Only certain edge cases are detected; see the\n         description of the exceptions below. A violation of this precondition\n         yields undefined behavior.\n\n    @warning A precondition is enforced with a runtime assertion that will\n             result in calling `std::abort` if this precondition is not met.\n             Assertions can be disabled by defining `NDEBUG` at compile time.\n             See https://en.cppreference.com/w/cpp/error/assert for more\n             information.\n\n    @throw invalid_iterator.201 if iterators @a first and @a last are not\n    compatible (i.e., do not belong to the same JSON value). In this case,\n    the range `[first, last)` is undefined.\n    @throw invalid_iterator.204 if iterators @a first and @a last belong to a\n    primitive type (number, boolean, or string), but @a first does not point\n    to the first element any more. In this case, the range `[first, last)` is\n    undefined. See example code below.\n    @throw invalid_iterator.206 if iterators @a first and @a last belong to a\n    null value. In this case, the range `[first, last)` is undefined.\n\n    @complexity Linear in distance between @a first and @a last.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The example below shows several ways to create JSON values by\n    specifying a subrange with iterators.,basic_json__InputIt_InputIt}\n\n    @since version 1.0.0\n    */\n    template<class InputIT, typename std::enable_if<\n                 std::is_same<InputIT, typename basic_json_t::iterator>::value or\n                 std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int>::type = 0>\n    basic_json(InputIT first, InputIT last)\n    {\n        assert(first.m_object != nullptr);\n        assert(last.m_object != nullptr);\n\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(201, \"iterators are not compatible\"));\n        }\n\n        // copy type from first iterator\n        m_type = first.m_object->m_type;\n\n        // check if iterator range is complete for primitive values\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            {\n                if (JSON_HEDLEY_UNLIKELY(not first.m_it.primitive_iterator.is_begin()\n                                         or not last.m_it.primitive_iterator.is_end()))\n                {\n                    JSON_THROW(invalid_iterator::create(204, \"iterators out of range\"));\n                }\n                break;\n            }\n\n            default:\n                break;\n        }\n\n        switch (m_type)\n        {\n            case value_t::number_integer:\n            {\n                m_value.number_integer = first.m_object->m_value.number_integer;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value.number_unsigned = first.m_object->m_value.number_unsigned;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value.number_float = first.m_object->m_value.number_float;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value.boolean = first.m_object->m_value.boolean;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value = *first.m_object->m_value.string;\n                break;\n            }\n\n            case value_t::object:\n            {\n                m_value.object = create<object_t>(first.m_it.object_iterator,\n                                                  last.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value.array = create<array_t>(first.m_it.array_iterator,\n                                                last.m_it.array_iterator);\n                break;\n            }\n\n            default:\n                JSON_THROW(invalid_iterator::create(206, \"cannot construct with iterators from \" +\n                                                    std::string(first.m_object->type_name())));\n        }\n\n        assert_invariant();\n    }\n\n\n    ///////////////////////////////////////\n    // other constructors and destructor //\n    ///////////////////////////////////////\n\n    /// @private\n    basic_json(const detail::json_ref<basic_json>& ref)\n        : basic_json(ref.moved_or_copied())\n    {}\n\n    /*!\n    @brief copy constructor\n\n    Creates a copy of a given JSON value.\n\n    @param[in] other  the JSON value to copy\n\n    @post `*this == other`\n\n    @complexity Linear in the size of @a other.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n    - As postcondition, it holds: `other == basic_json(other)`.\n\n    @liveexample{The following code shows an example for the copy\n    constructor.,basic_json__basic_json}\n\n    @since version 1.0.0\n    */\n    basic_json(const basic_json& other)\n        : m_type(other.m_type)\n    {\n        // check of passed value is valid\n        other.assert_invariant();\n\n        switch (m_type)\n        {\n            case value_t::object:\n            {\n                m_value = *other.m_value.object;\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value = *other.m_value.array;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value = *other.m_value.string;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value = other.m_value.boolean;\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                m_value = other.m_value.number_integer;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value = other.m_value.number_unsigned;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value = other.m_value.number_float;\n                break;\n            }\n\n            default:\n                break;\n        }\n\n        assert_invariant();\n    }\n\n    /*!\n    @brief move constructor\n\n    Move constructor. Constructs a JSON value with the contents of the given\n    value @a other using move semantics. It \"steals\" the resources from @a\n    other and leaves it as JSON null value.\n\n    @param[in,out] other  value to move to this object\n\n    @post `*this` has the same value as @a other before the call.\n    @post @a other is a JSON null value.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this constructor never throws\n    exceptions.\n\n    @requirement This function helps `basic_json` satisfying the\n    [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible)\n    requirements.\n\n    @liveexample{The code below shows the move constructor explicitly called\n    via std::move.,basic_json__moveconstructor}\n\n    @since version 1.0.0\n    */\n    basic_json(basic_json&& other) noexcept\n        : m_type(std::move(other.m_type)),\n          m_value(std::move(other.m_value))\n    {\n        // check that passed value is valid\n        other.assert_invariant();\n\n        // invalidate payload\n        other.m_type = value_t::null;\n        other.m_value = {};\n\n        assert_invariant();\n    }\n\n    /*!\n    @brief copy assignment\n\n    Copy assignment operator. Copies a JSON value via the \"copy and swap\"\n    strategy: It is expressed in terms of the copy constructor, destructor,\n    and the `swap()` member function.\n\n    @param[in] other  value to copy from\n\n    @complexity Linear.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n\n    @liveexample{The code below shows and example for the copy assignment. It\n    creates a copy of value `a` which is then swapped with `b`. Finally\\, the\n    copy of `a` (which is the null value after the swap) is\n    destroyed.,basic_json__copyassignment}\n\n    @since version 1.0.0\n    */\n    basic_json& operator=(basic_json other) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value and\n        std::is_nothrow_move_assignable<value_t>::value and\n        std::is_nothrow_move_constructible<json_value>::value and\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        // check that passed value is valid\n        other.assert_invariant();\n\n        using std::swap;\n        swap(m_type, other.m_type);\n        swap(m_value, other.m_value);\n\n        assert_invariant();\n        return *this;\n    }\n\n    /*!\n    @brief destructor\n\n    Destroys the JSON value and frees all allocated memory.\n\n    @complexity Linear.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n    - All stored elements are destroyed and all memory is freed.\n\n    @since version 1.0.0\n    */\n    ~basic_json() noexcept\n    {\n        assert_invariant();\n        m_value.destroy(m_type);\n    }\n\n    /// @}\n\n  public:\n    ///////////////////////\n    // object inspection //\n    ///////////////////////\n\n    /// @name object inspection\n    /// Functions to inspect the type of a JSON value.\n    /// @{\n\n    /*!\n    @brief serialization\n\n    Serialization function for JSON values. The function tries to mimic\n    Python's `json.dumps()` function, and currently supports its @a indent\n    and @a ensure_ascii parameters.\n\n    @param[in] indent If indent is nonnegative, then array elements and object\n    members will be pretty-printed with that indent level. An indent level of\n    `0` will only insert newlines. `-1` (the default) selects the most compact\n    representation.\n    @param[in] indent_char The character to use for indentation if @a indent is\n    greater than `0`. The default is ` ` (space).\n    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters\n    in the output are escaped with `\\uXXXX` sequences, and the result consists\n    of ASCII characters only.\n    @param[in] error_handler  how to react on decoding errors; there are three\n    possible values: `strict` (throws and exception in case a decoding error\n    occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD),\n    and `ignore` (ignore invalid UTF-8 sequences during serialization).\n\n    @return string containing the serialization of the JSON value\n\n    @throw type_error.316 if a string stored inside the JSON value is not\n                          UTF-8 encoded\n\n    @complexity Linear.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @liveexample{The following example shows the effect of different @a indent\\,\n    @a indent_char\\, and @a ensure_ascii parameters to the result of the\n    serialization.,dump}\n\n    @see https://docs.python.org/2/library/json.html#json.dump\n\n    @since version 1.0.0; indentation character @a indent_char, option\n           @a ensure_ascii and exceptions added in version 3.0.0; error\n           handlers added in version 3.4.0.\n    */\n    string_t dump(const int indent = -1,\n                  const char indent_char = ' ',\n                  const bool ensure_ascii = false,\n                  const error_handler_t error_handler = error_handler_t::strict) const\n    {\n        string_t result;\n        serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler);\n\n        if (indent >= 0)\n        {\n            s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));\n        }\n        else\n        {\n            s.dump(*this, false, ensure_ascii, 0);\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief return the type of the JSON value (explicit)\n\n    Return the type of the JSON value as a value from the @ref value_t\n    enumeration.\n\n    @return the type of the JSON value\n            Value type                | return value\n            ------------------------- | -------------------------\n            null                      | value_t::null\n            boolean                   | value_t::boolean\n            string                    | value_t::string\n            number (integer)          | value_t::number_integer\n            number (unsigned integer) | value_t::number_unsigned\n            number (floating-point)   | value_t::number_float\n            object                    | value_t::object\n            array                     | value_t::array\n            discarded                 | value_t::discarded\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `type()` for all JSON\n    types.,type}\n\n    @sa @ref operator value_t() -- return the type of the JSON value (implicit)\n    @sa @ref type_name() -- return the type as string\n\n    @since version 1.0.0\n    */\n    constexpr value_t type() const noexcept\n    {\n        return m_type;\n    }\n\n    /*!\n    @brief return whether type is primitive\n\n    This function returns true if and only if the JSON type is primitive\n    (string, number, boolean, or null).\n\n    @return `true` if type is primitive (string, number, boolean, or null),\n    `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_primitive()` for all JSON\n    types.,is_primitive}\n\n    @sa @ref is_structured() -- returns whether JSON value is structured\n    @sa @ref is_null() -- returns whether JSON value is `null`\n    @sa @ref is_string() -- returns whether JSON value is a string\n    @sa @ref is_boolean() -- returns whether JSON value is a boolean\n    @sa @ref is_number() -- returns whether JSON value is a number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_primitive() const noexcept\n    {\n        return is_null() or is_string() or is_boolean() or is_number();\n    }\n\n    /*!\n    @brief return whether type is structured\n\n    This function returns true if and only if the JSON type is structured\n    (array or object).\n\n    @return `true` if type is structured (array or object), `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_structured()` for all JSON\n    types.,is_structured}\n\n    @sa @ref is_primitive() -- returns whether value is primitive\n    @sa @ref is_array() -- returns whether value is an array\n    @sa @ref is_object() -- returns whether value is an object\n\n    @since version 1.0.0\n    */\n    constexpr bool is_structured() const noexcept\n    {\n        return is_array() or is_object();\n    }\n\n    /*!\n    @brief return whether value is null\n\n    This function returns true if and only if the JSON value is null.\n\n    @return `true` if type is null, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_null()` for all JSON\n    types.,is_null}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_null() const noexcept\n    {\n        return m_type == value_t::null;\n    }\n\n    /*!\n    @brief return whether value is a boolean\n\n    This function returns true if and only if the JSON value is a boolean.\n\n    @return `true` if type is boolean, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_boolean()` for all JSON\n    types.,is_boolean}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_boolean() const noexcept\n    {\n        return m_type == value_t::boolean;\n    }\n\n    /*!\n    @brief return whether value is a number\n\n    This function returns true if and only if the JSON value is a number. This\n    includes both integer (signed and unsigned) and floating-point values.\n\n    @return `true` if type is number (regardless whether integer, unsigned\n    integer or floating-type), `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number()` for all JSON\n    types.,is_number}\n\n    @sa @ref is_number_integer() -- check if value is an integer or unsigned\n    integer number\n    @sa @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n    @sa @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number() const noexcept\n    {\n        return is_number_integer() or is_number_float();\n    }\n\n    /*!\n    @brief return whether value is an integer number\n\n    This function returns true if and only if the JSON value is a signed or\n    unsigned integer number. This excludes floating-point values.\n\n    @return `true` if type is an integer or unsigned integer number, `false`\n    otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_integer()` for all\n    JSON types.,is_number_integer}\n\n    @sa @ref is_number() -- check if value is a number\n    @sa @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n    @sa @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number_integer() const noexcept\n    {\n        return m_type == value_t::number_integer or m_type == value_t::number_unsigned;\n    }\n\n    /*!\n    @brief return whether value is an unsigned integer number\n\n    This function returns true if and only if the JSON value is an unsigned\n    integer number. This excludes floating-point and signed integer values.\n\n    @return `true` if type is an unsigned integer number, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_unsigned()` for all\n    JSON types.,is_number_unsigned}\n\n    @sa @ref is_number() -- check if value is a number\n    @sa @ref is_number_integer() -- check if value is an integer or unsigned\n    integer number\n    @sa @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 2.0.0\n    */\n    constexpr bool is_number_unsigned() const noexcept\n    {\n        return m_type == value_t::number_unsigned;\n    }\n\n    /*!\n    @brief return whether value is a floating-point number\n\n    This function returns true if and only if the JSON value is a\n    floating-point number. This excludes signed and unsigned integer values.\n\n    @return `true` if type is a floating-point number, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_float()` for all\n    JSON types.,is_number_float}\n\n    @sa @ref is_number() -- check if value is number\n    @sa @ref is_number_integer() -- check if value is an integer number\n    @sa @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number_float() const noexcept\n    {\n        return m_type == value_t::number_float;\n    }\n\n    /*!\n    @brief return whether value is an object\n\n    This function returns true if and only if the JSON value is an object.\n\n    @return `true` if type is object, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_object()` for all JSON\n    types.,is_object}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_object() const noexcept\n    {\n        return m_type == value_t::object;\n    }\n\n    /*!\n    @brief return whether value is an array\n\n    This function returns true if and only if the JSON value is an array.\n\n    @return `true` if type is array, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_array()` for all JSON\n    types.,is_array}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_array() const noexcept\n    {\n        return m_type == value_t::array;\n    }\n\n    /*!\n    @brief return whether value is a string\n\n    This function returns true if and only if the JSON value is a string.\n\n    @return `true` if type is string, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_string()` for all JSON\n    types.,is_string}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_string() const noexcept\n    {\n        return m_type == value_t::string;\n    }\n\n    /*!\n    @brief return whether value is discarded\n\n    This function returns true if and only if the JSON value was discarded\n    during parsing with a callback function (see @ref parser_callback_t).\n\n    @note This function will always be `false` for JSON values after parsing.\n    That is, discarded values can only occur during parsing, but will be\n    removed when inside a structured value or replaced by null in other cases.\n\n    @return `true` if type is discarded, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_discarded()` for all JSON\n    types.,is_discarded}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_discarded() const noexcept\n    {\n        return m_type == value_t::discarded;\n    }\n\n    /*!\n    @brief return the type of the JSON value (implicit)\n\n    Implicitly return the type of the JSON value as a value from the @ref\n    value_t enumeration.\n\n    @return the type of the JSON value\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies the @ref value_t operator for\n    all JSON types.,operator__value_t}\n\n    @sa @ref type() -- return the type of the JSON value (explicit)\n    @sa @ref type_name() -- return the type as string\n\n    @since version 1.0.0\n    */\n    constexpr operator value_t() const noexcept\n    {\n        return m_type;\n    }\n\n    /// @}\n\n  private:\n    //////////////////\n    // value access //\n    //////////////////\n\n    /// get a boolean (explicit)\n    boolean_t get_impl(boolean_t* /*unused*/) const\n    {\n        if (JSON_HEDLEY_LIKELY(is_boolean()))\n        {\n            return m_value.boolean;\n        }\n\n        JSON_THROW(type_error::create(302, \"type must be boolean, but is \" + std::string(type_name())));\n    }\n\n    /// get a pointer to the value (object)\n    object_t* get_impl_ptr(object_t* /*unused*/) noexcept\n    {\n        return is_object() ? m_value.object : nullptr;\n    }\n\n    /// get a pointer to the value (object)\n    constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept\n    {\n        return is_object() ? m_value.object : nullptr;\n    }\n\n    /// get a pointer to the value (array)\n    array_t* get_impl_ptr(array_t* /*unused*/) noexcept\n    {\n        return is_array() ? m_value.array : nullptr;\n    }\n\n    /// get a pointer to the value (array)\n    constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept\n    {\n        return is_array() ? m_value.array : nullptr;\n    }\n\n    /// get a pointer to the value (string)\n    string_t* get_impl_ptr(string_t* /*unused*/) noexcept\n    {\n        return is_string() ? m_value.string : nullptr;\n    }\n\n    /// get a pointer to the value (string)\n    constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept\n    {\n        return is_string() ? m_value.string : nullptr;\n    }\n\n    /// get a pointer to the value (boolean)\n    boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept\n    {\n        return is_boolean() ? &m_value.boolean : nullptr;\n    }\n\n    /// get a pointer to the value (boolean)\n    constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept\n    {\n        return is_boolean() ? &m_value.boolean : nullptr;\n    }\n\n    /// get a pointer to the value (integer number)\n    number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept\n    {\n        return is_number_integer() ? &m_value.number_integer : nullptr;\n    }\n\n    /// get a pointer to the value (integer number)\n    constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept\n    {\n        return is_number_integer() ? &m_value.number_integer : nullptr;\n    }\n\n    /// get a pointer to the value (unsigned number)\n    number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept\n    {\n        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;\n    }\n\n    /// get a pointer to the value (unsigned number)\n    constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept\n    {\n        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;\n    }\n\n    /// get a pointer to the value (floating-point number)\n    number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept\n    {\n        return is_number_float() ? &m_value.number_float : nullptr;\n    }\n\n    /// get a pointer to the value (floating-point number)\n    constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept\n    {\n        return is_number_float() ? &m_value.number_float : nullptr;\n    }\n\n    /*!\n    @brief helper function to implement get_ref()\n\n    This function helps to implement get_ref() without code duplication for\n    const and non-const overloads\n\n    @tparam ThisType will be deduced as `basic_json` or `const basic_json`\n\n    @throw type_error.303 if ReferenceType does not match underlying value\n    type of the current JSON\n    */\n    template<typename ReferenceType, typename ThisType>\n    static ReferenceType get_ref_impl(ThisType& obj)\n    {\n        // delegate the call to get_ptr<>()\n        auto ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();\n\n        if (JSON_HEDLEY_LIKELY(ptr != nullptr))\n        {\n            return *ptr;\n        }\n\n        JSON_THROW(type_error::create(303, \"incompatible ReferenceType for get_ref, actual type is \" + std::string(obj.type_name())));\n    }\n\n  public:\n    /// @name value access\n    /// Direct access to the stored value of a JSON value.\n    /// @{\n\n    /*!\n    @brief get special-case overload\n\n    This overloads avoids a lot of template boilerplate, it can be seen as the\n    identity method\n\n    @tparam BasicJsonType == @ref basic_json\n\n    @return a copy of *this\n\n    @complexity Constant.\n\n    @since version 2.1.0\n    */\n    template<typename BasicJsonType, detail::enable_if_t<\n                 std::is_same<typename std::remove_const<BasicJsonType>::type, basic_json_t>::value,\n                 int> = 0>\n    basic_json get() const\n    {\n        return *this;\n    }\n\n    /*!\n    @brief get special-case overload\n\n    This overloads converts the current @ref basic_json in a different\n    @ref basic_json type\n\n    @tparam BasicJsonType == @ref basic_json\n\n    @return a copy of *this, converted into @tparam BasicJsonType\n\n    @complexity Depending on the implementation of the called `from_json()`\n                method.\n\n    @since version 3.2.0\n    */\n    template<typename BasicJsonType, detail::enable_if_t<\n                 not std::is_same<BasicJsonType, basic_json>::value and\n                 detail::is_basic_json<BasicJsonType>::value, int> = 0>\n    BasicJsonType get() const\n    {\n        return *this;\n    }\n\n    /*!\n    @brief get a value (explicit)\n\n    Explicit type conversion between the JSON value and a compatible value\n    which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)\n    and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).\n    The value is converted by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    ValueType ret;\n    JSONSerializer<ValueType>::from_json(*this, ret);\n    return ret;\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json,\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `void from_json(const basic_json&, ValueType&)`, and\n    - @ref json_serializer<ValueType> does not have a `from_json()` method of\n      the form `ValueType from_json(const basic_json&)`\n\n    @tparam ValueTypeCV the provided value type\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @a ValueType\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,get__ValueType_const}\n\n    @since version 2.1.0\n    */\n    template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,\n             detail::enable_if_t <\n                 not detail::is_basic_json<ValueType>::value and\n                 detail::has_from_json<basic_json_t, ValueType>::value and\n                 not detail::has_non_default_from_json<basic_json_t, ValueType>::value,\n                 int> = 0>\n    ValueType get() const noexcept(noexcept(\n                                       JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))\n    {\n        // we cannot static_assert on ValueTypeCV being non-const, because\n        // there is support for get<const basic_json_t>(), which is why we\n        // still need the uncvref\n        static_assert(not std::is_reference<ValueTypeCV>::value,\n                      \"get() cannot be used with reference types, you might want to use get_ref()\");\n        static_assert(std::is_default_constructible<ValueType>::value,\n                      \"types must be DefaultConstructible when used with get()\");\n\n        ValueType ret;\n        JSONSerializer<ValueType>::from_json(*this, ret);\n        return ret;\n    }\n\n    /*!\n    @brief get a value (explicit); special case\n\n    Explicit type conversion between the JSON value and a compatible value\n    which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)\n    and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).\n    The value is converted by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    return JSONSerializer<ValueTypeCV>::from_json(*this);\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json and\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `ValueType from_json(const basic_json&)`\n\n    @note If @ref json_serializer<ValueType> has both overloads of\n    `from_json()`, this one is chosen.\n\n    @tparam ValueTypeCV the provided value type\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @a ValueType\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @since version 2.1.0\n    */\n    template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,\n             detail::enable_if_t<not std::is_same<basic_json_t, ValueType>::value and\n                                 detail::has_non_default_from_json<basic_json_t, ValueType>::value,\n                                 int> = 0>\n    ValueType get() const noexcept(noexcept(\n                                       JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>())))\n    {\n        static_assert(not std::is_reference<ValueTypeCV>::value,\n                      \"get() cannot be used with reference types, you might want to use get_ref()\");\n        return JSONSerializer<ValueType>::from_json(*this);\n    }\n\n    /*!\n    @brief get a value (explicit)\n\n    Explicit type conversion between the JSON value and a compatible value.\n    The value is filled into the input parameter by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    ValueType v;\n    JSONSerializer<ValueType>::from_json(*this, v);\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json,\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `void from_json(const basic_json&, ValueType&)`, and\n\n    @tparam ValueType the input parameter type.\n\n    @return the input parameter, allowing chaining calls.\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,get_to}\n\n    @since version 3.3.0\n    */\n    template<typename ValueType,\n             detail::enable_if_t <\n                 not detail::is_basic_json<ValueType>::value and\n                 detail::has_from_json<basic_json_t, ValueType>::value,\n                 int> = 0>\n    ValueType & get_to(ValueType& v) const noexcept(noexcept(\n                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))\n    {\n        JSONSerializer<ValueType>::from_json(*this, v);\n        return v;\n    }\n\n    template <\n        typename T, std::size_t N,\n        typename Array = T (&)[N],\n        detail::enable_if_t <\n            detail::has_from_json<basic_json_t, Array>::value, int > = 0 >\n    Array get_to(T (&v)[N]) const\n    noexcept(noexcept(JSONSerializer<Array>::from_json(\n                          std::declval<const basic_json_t&>(), v)))\n    {\n        JSONSerializer<Array>::from_json(*this, v);\n        return v;\n    }\n\n\n    /*!\n    @brief get a pointer value (implicit)\n\n    Implicit pointer access to the internally stored JSON value. No copies are\n    made.\n\n    @warning Writing data to the pointee of the result yields an undefined\n    state.\n\n    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref\n    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,\n    @ref number_unsigned_t, or @ref number_float_t. Enforced by a static\n    assertion.\n\n    @return pointer to the internally stored JSON value if the requested\n    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how pointers to internal values of a\n    JSON value can be requested. Note that no type conversions are made and a\n    `nullptr` is returned if the value and the requested pointer type does not\n    match.,get_ptr}\n\n    @since version 1.0.0\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))\n    {\n        // delegate the call to get_impl_ptr<>()\n        return get_impl_ptr(static_cast<PointerType>(nullptr));\n    }\n\n    /*!\n    @brief get a pointer value (implicit)\n    @copydoc get_ptr()\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value and\n                 std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0>\n    constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))\n    {\n        // delegate the call to get_impl_ptr<>() const\n        return get_impl_ptr(static_cast<PointerType>(nullptr));\n    }\n\n    /*!\n    @brief get a pointer value (explicit)\n\n    Explicit pointer access to the internally stored JSON value. No copies are\n    made.\n\n    @warning The pointer becomes invalid if the underlying JSON object\n    changes.\n\n    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref\n    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,\n    @ref number_unsigned_t, or @ref number_float_t.\n\n    @return pointer to the internally stored JSON value if the requested\n    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how pointers to internal values of a\n    JSON value can be requested. Note that no type conversions are made and a\n    `nullptr` is returned if the value and the requested pointer type does not\n    match.,get__PointerType}\n\n    @sa @ref get_ptr() for explicit pointer-member access\n\n    @since version 1.0.0\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    auto get() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())\n    {\n        // delegate the call to get_ptr\n        return get_ptr<PointerType>();\n    }\n\n    /*!\n    @brief get a pointer value (explicit)\n    @copydoc get()\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    constexpr auto get() const noexcept -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())\n    {\n        // delegate the call to get_ptr\n        return get_ptr<PointerType>();\n    }\n\n    /*!\n    @brief get a reference value (implicit)\n\n    Implicit reference access to the internally stored JSON value. No copies\n    are made.\n\n    @warning Writing data to the referee of the result yields an undefined\n    state.\n\n    @tparam ReferenceType reference type; must be a reference to @ref array_t,\n    @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or\n    @ref number_float_t. Enforced by static assertion.\n\n    @return reference to the internally stored JSON value if the requested\n    reference type @a ReferenceType fits to the JSON value; throws\n    type_error.303 otherwise\n\n    @throw type_error.303 in case passed type @a ReferenceType is incompatible\n    with the stored JSON value; see example below\n\n    @complexity Constant.\n\n    @liveexample{The example shows several calls to `get_ref()`.,get_ref}\n\n    @since version 1.1.0\n    */\n    template<typename ReferenceType, typename std::enable_if<\n                 std::is_reference<ReferenceType>::value, int>::type = 0>\n    ReferenceType get_ref()\n    {\n        // delegate call to get_ref_impl\n        return get_ref_impl<ReferenceType>(*this);\n    }\n\n    /*!\n    @brief get a reference value (implicit)\n    @copydoc get_ref()\n    */\n    template<typename ReferenceType, typename std::enable_if<\n                 std::is_reference<ReferenceType>::value and\n                 std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int>::type = 0>\n    ReferenceType get_ref() const\n    {\n        // delegate call to get_ref_impl\n        return get_ref_impl<ReferenceType>(*this);\n    }\n\n    /*!\n    @brief get a value (implicit)\n\n    Implicit type conversion between the JSON value and a compatible value.\n    The call is realized by calling @ref get() const.\n\n    @tparam ValueType non-pointer type compatible to the JSON value, for\n    instance `int` for JSON integer numbers, `bool` for JSON booleans, or\n    `std::vector` types for JSON arrays. The character type of @ref string_t\n    as well as an initializer list of this type is excluded to avoid\n    ambiguities as these types implicitly convert to `std::string`.\n\n    @return copy of the JSON value, converted to type @a ValueType\n\n    @throw type_error.302 in case passed type @a ValueType is incompatible\n    to the JSON value type (e.g., the JSON value is of type boolean, but a\n    string is requested); see example below\n\n    @complexity Linear in the size of the JSON value.\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,operator__ValueType}\n\n    @since version 1.0.0\n    */\n    template < typename ValueType, typename std::enable_if <\n                   not std::is_pointer<ValueType>::value and\n                   not std::is_same<ValueType, detail::json_ref<basic_json>>::value and\n                   not std::is_same<ValueType, typename string_t::value_type>::value and\n                   not detail::is_basic_json<ValueType>::value\n\n#ifndef _MSC_VER  // fix for issue #167 operator<< ambiguity under VS2015\n                   and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value\n#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) and _MSC_VER <= 1914))\n                   and not std::is_same<ValueType, typename std::string_view>::value\n#endif\n#endif\n                   and detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value\n                   , int >::type = 0 >\n    operator ValueType() const\n    {\n        // delegate the call to get<>() const\n        return get<ValueType>();\n    }\n\n    /// @}\n\n\n    ////////////////////\n    // element access //\n    ////////////////////\n\n    /// @name element access\n    /// Access to the JSON value.\n    /// @{\n\n    /*!\n    @brief access specified array element with bounds checking\n\n    Returns a reference to the element at specified location @a idx, with\n    bounds checking.\n\n    @param[in] idx  index of the element to access\n\n    @return reference to the element at index @a idx\n\n    @throw type_error.304 if the JSON value is not an array; in this case,\n    calling `at` with an index makes no sense. See example below.\n    @throw out_of_range.401 if the index @a idx is out of range of the array;\n    that is, `idx >= size()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how array elements can be read and\n    written using `at()`. It also demonstrates the different exceptions that\n    can be thrown.,at__size_type}\n    */\n    reference at(size_type idx)\n    {\n        // at only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            JSON_TRY\n            {\n                return m_value.array->at(idx);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\"));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief access specified array element with bounds checking\n\n    Returns a const reference to the element at specified location @a idx,\n    with bounds checking.\n\n    @param[in] idx  index of the element to access\n\n    @return const reference to the element at index @a idx\n\n    @throw type_error.304 if the JSON value is not an array; in this case,\n    calling `at` with an index makes no sense. See example below.\n    @throw out_of_range.401 if the index @a idx is out of range of the array;\n    that is, `idx >= size()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how array elements can be read using\n    `at()`. It also demonstrates the different exceptions that can be thrown.,\n    at__size_type_const}\n    */\n    const_reference at(size_type idx) const\n    {\n        // at only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            JSON_TRY\n            {\n                return m_value.array->at(idx);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\"));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief access specified object element with bounds checking\n\n    Returns a reference to the element at with specified key @a key, with\n    bounds checking.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.304 if the JSON value is not an object; in this case,\n    calling `at` with a key makes no sense. See example below.\n    @throw out_of_range.403 if the key @a key is is not stored in the object;\n    that is, `find(key) == end()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Logarithmic in the size of the container.\n\n    @sa @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how object elements can be read and\n    written using `at()`. It also demonstrates the different exceptions that\n    can be thrown.,at__object_t_key_type}\n    */\n    reference at(const typename object_t::key_type& key)\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_TRY\n            {\n                return m_value.object->at(key);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(403, \"key '\" + key + \"' not found\"));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief access specified object element with bounds checking\n\n    Returns a const reference to the element at with specified key @a key,\n    with bounds checking.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @throw type_error.304 if the JSON value is not an object; in this case,\n    calling `at` with a key makes no sense. See example below.\n    @throw out_of_range.403 if the key @a key is is not stored in the object;\n    that is, `find(key) == end()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Logarithmic in the size of the container.\n\n    @sa @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how object elements can be read using\n    `at()`. It also demonstrates the different exceptions that can be thrown.,\n    at__object_t_key_type_const}\n    */\n    const_reference at(const typename object_t::key_type& key) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_TRY\n            {\n                return m_value.object->at(key);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(403, \"key '\" + key + \"' not found\"));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief access specified array element\n\n    Returns a reference to the element at specified location @a idx.\n\n    @note If @a idx is beyond the range of the array (i.e., `idx >= size()`),\n    then the array is silently filled up with `null` values to make `idx` a\n    valid reference to the last stored element.\n\n    @param[in] idx  index of the element to access\n\n    @return reference to the element at index @a idx\n\n    @throw type_error.305 if the JSON value is not an array or null; in that\n    cases, using the [] operator with an index makes no sense.\n\n    @complexity Constant if @a idx is in the range of the array. Otherwise\n    linear in `idx - size()`.\n\n    @liveexample{The example below shows how array elements can be read and\n    written using `[]` operator. Note the addition of `null`\n    values.,operatorarray__size_type}\n\n    @since version 1.0.0\n    */\n    reference operator[](size_type idx)\n    {\n        // implicitly convert null value to an empty array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value.array = create<array_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // fill up array with null values if given idx is outside range\n            if (idx >= m_value.array->size())\n            {\n                m_value.array->insert(m_value.array->end(),\n                                      idx - m_value.array->size() + 1,\n                                      basic_json());\n            }\n\n            return m_value.array->operator[](idx);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a numeric argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief access specified array element\n\n    Returns a const reference to the element at specified location @a idx.\n\n    @param[in] idx  index of the element to access\n\n    @return const reference to the element at index @a idx\n\n    @throw type_error.305 if the JSON value is not an array; in that case,\n    using the [] operator with an index makes no sense.\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how array elements can be read using\n    the `[]` operator.,operatorarray__size_type_const}\n\n    @since version 1.0.0\n    */\n    const_reference operator[](size_type idx) const\n    {\n        // const operator[] only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            return m_value.array->operator[](idx);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a numeric argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief access specified object element\n\n    Returns a reference to the element at with specified key @a key.\n\n    @note If @a key is not found in the object, then it is silently added to\n    the object and filled with a `null` value to make `key` a valid reference.\n    In case the value was `null` before, it is converted to an object.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.305 if the JSON value is not an object or null; in that\n    cases, using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read and\n    written using the `[]` operator.,operatorarray__key_type}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.0.0\n    */\n    reference operator[](const typename object_t::key_type& key)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            return m_value.object->operator[](key);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief read-only access specified object element\n\n    Returns a const reference to the element at with specified key @a key. No\n    bounds checking is performed.\n\n    @warning If the element with key @a key does not exist, the behavior is\n    undefined.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @pre The element with key @a key must exist. **This precondition is\n         enforced with an assertion.**\n\n    @throw type_error.305 if the JSON value is not an object; in that case,\n    using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read using\n    the `[]` operator.,operatorarray__key_type_const}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.0.0\n    */\n    const_reference operator[](const typename object_t::key_type& key) const\n    {\n        // const operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            assert(m_value.object->find(key) != m_value.object->end());\n            return m_value.object->find(key)->second;\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief access specified object element\n\n    Returns a reference to the element at with specified key @a key.\n\n    @note If @a key is not found in the object, then it is silently added to\n    the object and filled with a `null` value to make `key` a valid reference.\n    In case the value was `null` before, it is converted to an object.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.305 if the JSON value is not an object or null; in that\n    cases, using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read and\n    written using the `[]` operator.,operatorarray__key_type}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.1.0\n    */\n    template<typename T>\n    JSON_HEDLEY_NON_NULL(2)\n    reference operator[](T* key)\n    {\n        // implicitly convert null to object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            return m_value.object->operator[](key);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief read-only access specified object element\n\n    Returns a const reference to the element at with specified key @a key. No\n    bounds checking is performed.\n\n    @warning If the element with key @a key does not exist, the behavior is\n    undefined.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @pre The element with key @a key must exist. **This precondition is\n         enforced with an assertion.**\n\n    @throw type_error.305 if the JSON value is not an object; in that case,\n    using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read using\n    the `[]` operator.,operatorarray__key_type_const}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.1.0\n    */\n    template<typename T>\n    JSON_HEDLEY_NON_NULL(2)\n    const_reference operator[](T* key) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            assert(m_value.object->find(key) != m_value.object->end());\n            return m_value.object->find(key)->second;\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief access specified object element with default value\n\n    Returns either a copy of an object's element at the specified key @a key\n    or a given default value if no element with key @a key exists.\n\n    The function is basically equivalent to executing\n    @code {.cpp}\n    try {\n        return at(key);\n    } catch(out_of_range) {\n        return default_value;\n    }\n    @endcode\n\n    @note Unlike @ref at(const typename object_t::key_type&), this function\n    does not throw if the given key @a key was not found.\n\n    @note Unlike @ref operator[](const typename object_t::key_type& key), this\n    function does not implicitly add an element to the position defined by @a\n    key. This function is furthermore also applicable to const objects.\n\n    @param[in] key  key of the element to access\n    @param[in] default_value  the value to return if @a key is not found\n\n    @tparam ValueType type compatible to JSON values, for instance `int` for\n    JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for\n    JSON arrays. Note the type of the expected value at @a key and the default\n    value @a default_value must be compatible.\n\n    @return copy of the element at key @a key or @a default_value if @a key\n    is not found\n\n    @throw type_error.302 if @a default_value does not match the type of the\n    value at @a key\n    @throw type_error.306 if the JSON value is not an object; in that case,\n    using `value()` with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be queried\n    with a default value.,basic_json__value}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n\n    @since version 1.0.0\n    */\n    template<class ValueType, typename std::enable_if<\n                 std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>\n    ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // if key is found, return value and given default value otherwise\n            const auto it = find(key);\n            if (it != end())\n            {\n                return *it;\n            }\n\n            return default_value;\n        }\n\n        JSON_THROW(type_error::create(306, \"cannot use value() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief overload for a default value of type const char*\n    @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const\n    */\n    string_t value(const typename object_t::key_type& key, const char* default_value) const\n    {\n        return value(key, string_t(default_value));\n    }\n\n    /*!\n    @brief access specified object element via JSON Pointer with default value\n\n    Returns either a copy of an object's element at the specified key @a key\n    or a given default value if no element with key @a key exists.\n\n    The function is basically equivalent to executing\n    @code {.cpp}\n    try {\n        return at(ptr);\n    } catch(out_of_range) {\n        return default_value;\n    }\n    @endcode\n\n    @note Unlike @ref at(const json_pointer&), this function does not throw\n    if the given key @a key was not found.\n\n    @param[in] ptr  a JSON pointer to the element to access\n    @param[in] default_value  the value to return if @a ptr found no value\n\n    @tparam ValueType type compatible to JSON values, for instance `int` for\n    JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for\n    JSON arrays. Note the type of the expected value at @a key and the default\n    value @a default_value must be compatible.\n\n    @return copy of the element at key @a key or @a default_value if @a key\n    is not found\n\n    @throw type_error.302 if @a default_value does not match the type of the\n    value at @a ptr\n    @throw type_error.306 if the JSON value is not an object; in that case,\n    using `value()` with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be queried\n    with a default value.,basic_json__value_ptr}\n\n    @sa @ref operator[](const json_pointer&) for unchecked access by reference\n\n    @since version 2.0.2\n    */\n    template<class ValueType, typename std::enable_if<\n                 std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>\n    ValueType value(const json_pointer& ptr, const ValueType& default_value) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // if pointer resolves a value, return it or use default value\n            JSON_TRY\n            {\n                return ptr.get_checked(this);\n            }\n            JSON_INTERNAL_CATCH (out_of_range&)\n            {\n                return default_value;\n            }\n        }\n\n        JSON_THROW(type_error::create(306, \"cannot use value() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief overload for a default value of type const char*\n    @copydoc basic_json::value(const json_pointer&, ValueType) const\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    string_t value(const json_pointer& ptr, const char* default_value) const\n    {\n        return value(ptr, string_t(default_value));\n    }\n\n    /*!\n    @brief access the first element\n\n    Returns a reference to the first element in the container. For a JSON\n    container `c`, the expression `c.front()` is equivalent to `*c.begin()`.\n\n    @return In case of a structured type (array or object), a reference to the\n    first element is returned. In case of number, string, or boolean values, a\n    reference to the value is returned.\n\n    @complexity Constant.\n\n    @pre The JSON value must not be `null` (would throw `std::out_of_range`)\n    or an empty array or object (undefined behavior, **guarded by\n    assertions**).\n    @post The JSON value remains unchanged.\n\n    @throw invalid_iterator.214 when called on `null` value\n\n    @liveexample{The following code shows an example for `front()`.,front}\n\n    @sa @ref back() -- access the last element\n\n    @since version 1.0.0\n    */\n    reference front()\n    {\n        return *begin();\n    }\n\n    /*!\n    @copydoc basic_json::front()\n    */\n    const_reference front() const\n    {\n        return *cbegin();\n    }\n\n    /*!\n    @brief access the last element\n\n    Returns a reference to the last element in the container. For a JSON\n    container `c`, the expression `c.back()` is equivalent to\n    @code {.cpp}\n    auto tmp = c.end();\n    --tmp;\n    return *tmp;\n    @endcode\n\n    @return In case of a structured type (array or object), a reference to the\n    last element is returned. In case of number, string, or boolean values, a\n    reference to the value is returned.\n\n    @complexity Constant.\n\n    @pre The JSON value must not be `null` (would throw `std::out_of_range`)\n    or an empty array or object (undefined behavior, **guarded by\n    assertions**).\n    @post The JSON value remains unchanged.\n\n    @throw invalid_iterator.214 when called on a `null` value. See example\n    below.\n\n    @liveexample{The following code shows an example for `back()`.,back}\n\n    @sa @ref front() -- access the first element\n\n    @since version 1.0.0\n    */\n    reference back()\n    {\n        auto tmp = end();\n        --tmp;\n        return *tmp;\n    }\n\n    /*!\n    @copydoc basic_json::back()\n    */\n    const_reference back() const\n    {\n        auto tmp = cend();\n        --tmp;\n        return *tmp;\n    }\n\n    /*!\n    @brief remove element given an iterator\n\n    Removes the element specified by iterator @a pos. The iterator @a pos must\n    be valid and dereferenceable. Thus the `end()` iterator (which is valid,\n    but is not dereferenceable) cannot be used as a value for @a pos.\n\n    If called on a primitive type other than `null`, the resulting JSON value\n    will be `null`.\n\n    @param[in] pos iterator to the element to remove\n    @return Iterator following the last removed element. If the iterator @a\n    pos refers to the last element, the `end()` iterator is returned.\n\n    @tparam IteratorType an @ref iterator or @ref const_iterator\n\n    @post Invalidates iterators and references at or after the point of the\n    erase, including the `end()` iterator.\n\n    @throw type_error.307 if called on a `null` value; example: `\"cannot use\n    erase() with null\"`\n    @throw invalid_iterator.202 if called on an iterator which does not belong\n    to the current JSON value; example: `\"iterator does not fit current\n    value\"`\n    @throw invalid_iterator.205 if called on a primitive type with invalid\n    iterator (i.e., any iterator which is not `begin()`); example: `\"iterator\n    out of range\"`\n\n    @complexity The complexity depends on the type:\n    - objects: amortized constant\n    - arrays: linear in distance between @a pos and the end of the container\n    - strings: linear in the length of the string\n    - other types: constant\n\n    @liveexample{The example shows the result of `erase()` for different JSON\n    types.,erase__IteratorType}\n\n    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n    @sa @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    template<class IteratorType, typename std::enable_if<\n                 std::is_same<IteratorType, typename basic_json_t::iterator>::value or\n                 std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type\n             = 0>\n    IteratorType erase(IteratorType pos)\n    {\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n        }\n\n        IteratorType result = end();\n\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            {\n                if (JSON_HEDLEY_UNLIKELY(not pos.m_it.primitive_iterator.is_begin()))\n                {\n                    JSON_THROW(invalid_iterator::create(205, \"iterator out of range\"));\n                }\n\n                if (is_string())\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);\n                    m_value.string = nullptr;\n                }\n\n                m_type = value_t::null;\n                assert_invariant();\n                break;\n            }\n\n            case value_t::object:\n            {\n                result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);\n                break;\n            }\n\n            default:\n                JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name())));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief remove elements given an iterator range\n\n    Removes the element specified by the range `[first; last)`. The iterator\n    @a first does not need to be dereferenceable if `first == last`: erasing\n    an empty range is a no-op.\n\n    If called on a primitive type other than `null`, the resulting JSON value\n    will be `null`.\n\n    @param[in] first iterator to the beginning of the range to remove\n    @param[in] last iterator past the end of the range to remove\n    @return Iterator following the last removed element. If the iterator @a\n    second refers to the last element, the `end()` iterator is returned.\n\n    @tparam IteratorType an @ref iterator or @ref const_iterator\n\n    @post Invalidates iterators and references at or after the point of the\n    erase, including the `end()` iterator.\n\n    @throw type_error.307 if called on a `null` value; example: `\"cannot use\n    erase() with null\"`\n    @throw invalid_iterator.203 if called on iterators which does not belong\n    to the current JSON value; example: `\"iterators do not fit current value\"`\n    @throw invalid_iterator.204 if called on a primitive type with invalid\n    iterators (i.e., if `first != begin()` and `last != end()`); example:\n    `\"iterators out of range\"`\n\n    @complexity The complexity depends on the type:\n    - objects: `log(size()) + std::distance(first, last)`\n    - arrays: linear in the distance between @a first and @a last, plus linear\n      in the distance between @a last and end of the container\n    - strings: linear in the length of the string\n    - other types: constant\n\n    @liveexample{The example shows the result of `erase()` for different JSON\n    types.,erase__IteratorType_IteratorType}\n\n    @sa @ref erase(IteratorType) -- removes the element at a given position\n    @sa @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n    @sa @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    template<class IteratorType, typename std::enable_if<\n                 std::is_same<IteratorType, typename basic_json_t::iterator>::value or\n                 std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type\n             = 0>\n    IteratorType erase(IteratorType first, IteratorType last)\n    {\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(this != first.m_object or this != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(203, \"iterators do not fit current value\"));\n        }\n\n        IteratorType result = end();\n\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            {\n                if (JSON_HEDLEY_LIKELY(not first.m_it.primitive_iterator.is_begin()\n                                       or not last.m_it.primitive_iterator.is_end()))\n                {\n                    JSON_THROW(invalid_iterator::create(204, \"iterators out of range\"));\n                }\n\n                if (is_string())\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);\n                    m_value.string = nullptr;\n                }\n\n                m_type = value_t::null;\n                assert_invariant();\n                break;\n            }\n\n            case value_t::object:\n            {\n                result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,\n                                              last.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,\n                                             last.m_it.array_iterator);\n                break;\n            }\n\n            default:\n                JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name())));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief remove element from a JSON object given a key\n\n    Removes elements from a JSON object with the key value @a key.\n\n    @param[in] key value of the elements to remove\n\n    @return Number of elements removed. If @a ObjectType is the default\n    `std::map` type, the return value will always be `0` (@a key was not\n    found) or `1` (@a key was found).\n\n    @post References and iterators to the erased elements are invalidated.\n    Other references and iterators are not affected.\n\n    @throw type_error.307 when called on a type other than JSON object;\n    example: `\"cannot use erase() with null\"`\n\n    @complexity `log(size()) + count(key)`\n\n    @liveexample{The example shows the effect of `erase()`.,erase__key_type}\n\n    @sa @ref erase(IteratorType) -- removes the element at a given position\n    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    size_type erase(const typename object_t::key_type& key)\n    {\n        // this erase only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            return m_value.object->erase(key);\n        }\n\n        JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief remove element from a JSON array given an index\n\n    Removes element from a JSON array at the index @a idx.\n\n    @param[in] idx index of the element to remove\n\n    @throw type_error.307 when called on a type other than JSON object;\n    example: `\"cannot use erase() with null\"`\n    @throw out_of_range.401 when `idx >= size()`; example: `\"array index 17\n    is out of range\"`\n\n    @complexity Linear in distance between @a idx and the end of the container.\n\n    @liveexample{The example shows the effect of `erase()`.,erase__size_type}\n\n    @sa @ref erase(IteratorType) -- removes the element at a given position\n    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n\n    @since version 1.0.0\n    */\n    void erase(const size_type idx)\n    {\n        // this erase only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            if (JSON_HEDLEY_UNLIKELY(idx >= size()))\n            {\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\"));\n            }\n\n            m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));\n        }\n        else\n        {\n            JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name())));\n        }\n    }\n\n    /// @}\n\n\n    ////////////\n    // lookup //\n    ////////////\n\n    /// @name lookup\n    /// @{\n\n    /*!\n    @brief find an element in a JSON object\n\n    Finds an element in a JSON object with key equivalent to @a key. If the\n    element is not found or the JSON value is not an object, end() is\n    returned.\n\n    @note This method always returns @ref end() when executed on a JSON type\n          that is not an object.\n\n    @param[in] key key value of the element to search for.\n\n    @return Iterator to an element with key equivalent to @a key. If no such\n    element is found or the JSON value is not an object, past-the-end (see\n    @ref end()) iterator is returned.\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The example shows how `find()` is used.,find__key_type}\n\n    @sa @ref contains(KeyT&&) const -- checks whether a key exists\n\n    @since version 1.0.0\n    */\n    template<typename KeyT>\n    iterator find(KeyT&& key)\n    {\n        auto result = end();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief find an element in a JSON object\n    @copydoc find(KeyT&&)\n    */\n    template<typename KeyT>\n    const_iterator find(KeyT&& key) const\n    {\n        auto result = cend();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief returns the number of occurrences of a key in a JSON object\n\n    Returns the number of elements with key @a key. If ObjectType is the\n    default `std::map` type, the return value will always be `0` (@a key was\n    not found) or `1` (@a key was found).\n\n    @note This method always returns `0` when executed on a JSON type that is\n          not an object.\n\n    @param[in] key key value of the element to count\n\n    @return Number of elements with key @a key. If the JSON value is not an\n    object, the return value will be `0`.\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The example shows how `count()` is used.,count}\n\n    @since version 1.0.0\n    */\n    template<typename KeyT>\n    size_type count(KeyT&& key) const\n    {\n        // return 0 for all nonobject types\n        return is_object() ? m_value.object->count(std::forward<KeyT>(key)) : 0;\n    }\n\n    /*!\n    @brief check the existence of an element in a JSON object\n\n    Check whether an element exists in a JSON object with key equivalent to\n    @a key. If the element is not found or the JSON value is not an object,\n    false is returned.\n\n    @note This method always returns false when executed on a JSON type\n          that is not an object.\n\n    @param[in] key key value to check its existence.\n\n    @return true if an element with specified @a key exists. If no such\n    element with such key is found or the JSON value is not an object,\n    false is returned.\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The following code shows an example for `contains()`.,contains}\n\n    @sa @ref find(KeyT&&) -- returns an iterator to an object element\n    @sa @ref contains(const json_pointer&) const -- checks the existence for a JSON pointer\n\n    @since version 3.6.0\n    */\n    template<typename KeyT, typename std::enable_if<\n                 not std::is_same<typename std::decay<KeyT>::type, json_pointer>::value, int>::type = 0>\n    bool contains(KeyT && key) const\n    {\n        return is_object() and m_value.object->find(std::forward<KeyT>(key)) != m_value.object->end();\n    }\n\n    /*!\n    @brief check the existence of an element in a JSON object given a JSON pointer\n\n    Check whether the given JSON pointer @a ptr can be resolved in the current\n    JSON value.\n\n    @note This method can be executed on any JSON value type.\n\n    @param[in] ptr JSON pointer to check its existence.\n\n    @return true if the JSON pointer can be resolved to a stored value, false\n    otherwise.\n\n    @post If `j.contains(ptr)` returns true, it is safe to call `j[ptr]`.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The following code shows an example for `contains()`.,contains_json_pointer}\n\n    @sa @ref contains(KeyT &&) const -- checks the existence of a key\n\n    @since version 3.7.0\n    */\n    bool contains(const json_pointer& ptr) const\n    {\n        return ptr.contains(this);\n    }\n\n    /// @}\n\n\n    ///////////////\n    // iterators //\n    ///////////////\n\n    /// @name iterators\n    /// @{\n\n    /*!\n    @brief returns an iterator to the first element\n\n    Returns an iterator to the first element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return iterator to the first element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n\n    @liveexample{The following code shows an example for `begin()`.,begin}\n\n    @sa @ref cbegin() -- returns a const iterator to the beginning\n    @sa @ref end() -- returns an iterator to the end\n    @sa @ref cend() -- returns a const iterator to the end\n\n    @since version 1.0.0\n    */\n    iterator begin() noexcept\n    {\n        iterator result(this);\n        result.set_begin();\n        return result;\n    }\n\n    /*!\n    @copydoc basic_json::cbegin()\n    */\n    const_iterator begin() const noexcept\n    {\n        return cbegin();\n    }\n\n    /*!\n    @brief returns a const iterator to the first element\n\n    Returns a const iterator to the first element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return const iterator to the first element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).begin()`.\n\n    @liveexample{The following code shows an example for `cbegin()`.,cbegin}\n\n    @sa @ref begin() -- returns an iterator to the beginning\n    @sa @ref end() -- returns an iterator to the end\n    @sa @ref cend() -- returns a const iterator to the end\n\n    @since version 1.0.0\n    */\n    const_iterator cbegin() const noexcept\n    {\n        const_iterator result(this);\n        result.set_begin();\n        return result;\n    }\n\n    /*!\n    @brief returns an iterator to one past the last element\n\n    Returns an iterator to one past the last element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return iterator one past the last element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n\n    @liveexample{The following code shows an example for `end()`.,end}\n\n    @sa @ref cend() -- returns a const iterator to the end\n    @sa @ref begin() -- returns an iterator to the beginning\n    @sa @ref cbegin() -- returns a const iterator to the beginning\n\n    @since version 1.0.0\n    */\n    iterator end() noexcept\n    {\n        iterator result(this);\n        result.set_end();\n        return result;\n    }\n\n    /*!\n    @copydoc basic_json::cend()\n    */\n    const_iterator end() const noexcept\n    {\n        return cend();\n    }\n\n    /*!\n    @brief returns a const iterator to one past the last element\n\n    Returns a const iterator to one past the last element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return const iterator one past the last element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).end()`.\n\n    @liveexample{The following code shows an example for `cend()`.,cend}\n\n    @sa @ref end() -- returns an iterator to the end\n    @sa @ref begin() -- returns an iterator to the beginning\n    @sa @ref cbegin() -- returns a const iterator to the beginning\n\n    @since version 1.0.0\n    */\n    const_iterator cend() const noexcept\n    {\n        const_iterator result(this);\n        result.set_end();\n        return result;\n    }\n\n    /*!\n    @brief returns an iterator to the reverse-beginning\n\n    Returns an iterator to the reverse-beginning; that is, the last element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `reverse_iterator(end())`.\n\n    @liveexample{The following code shows an example for `rbegin()`.,rbegin}\n\n    @sa @ref crbegin() -- returns a const reverse iterator to the beginning\n    @sa @ref rend() -- returns a reverse iterator to the end\n    @sa @ref crend() -- returns a const reverse iterator to the end\n\n    @since version 1.0.0\n    */\n    reverse_iterator rbegin() noexcept\n    {\n        return reverse_iterator(end());\n    }\n\n    /*!\n    @copydoc basic_json::crbegin()\n    */\n    const_reverse_iterator rbegin() const noexcept\n    {\n        return crbegin();\n    }\n\n    /*!\n    @brief returns an iterator to the reverse-end\n\n    Returns an iterator to the reverse-end; that is, one before the first\n    element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `reverse_iterator(begin())`.\n\n    @liveexample{The following code shows an example for `rend()`.,rend}\n\n    @sa @ref crend() -- returns a const reverse iterator to the end\n    @sa @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa @ref crbegin() -- returns a const reverse iterator to the beginning\n\n    @since version 1.0.0\n    */\n    reverse_iterator rend() noexcept\n    {\n        return reverse_iterator(begin());\n    }\n\n    /*!\n    @copydoc basic_json::crend()\n    */\n    const_reverse_iterator rend() const noexcept\n    {\n        return crend();\n    }\n\n    /*!\n    @brief returns a const reverse iterator to the last element\n\n    Returns a const iterator to the reverse-beginning; that is, the last\n    element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).rbegin()`.\n\n    @liveexample{The following code shows an example for `crbegin()`.,crbegin}\n\n    @sa @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa @ref rend() -- returns a reverse iterator to the end\n    @sa @ref crend() -- returns a const reverse iterator to the end\n\n    @since version 1.0.0\n    */\n    const_reverse_iterator crbegin() const noexcept\n    {\n        return const_reverse_iterator(cend());\n    }\n\n    /*!\n    @brief returns a const reverse iterator to one before the first\n\n    Returns a const reverse iterator to the reverse-end; that is, one before\n    the first element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).rend()`.\n\n    @liveexample{The following code shows an example for `crend()`.,crend}\n\n    @sa @ref rend() -- returns a reverse iterator to the end\n    @sa @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa @ref crbegin() -- returns a const reverse iterator to the beginning\n\n    @since version 1.0.0\n    */\n    const_reverse_iterator crend() const noexcept\n    {\n        return const_reverse_iterator(cbegin());\n    }\n\n  public:\n    /*!\n    @brief wrapper to access iterator member functions in range-based for\n\n    This function allows to access @ref iterator::key() and @ref\n    iterator::value() during range-based for loops. In these loops, a\n    reference to the JSON values is returned, so there is no access to the\n    underlying iterator.\n\n    For loop without iterator_wrapper:\n\n    @code{cpp}\n    for (auto it = j_object.begin(); it != j_object.end(); ++it)\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    Range-based for loop without iterator proxy:\n\n    @code{cpp}\n    for (auto it : j_object)\n    {\n        // \"it\" is of type json::reference and has no key() member\n        std::cout << \"value: \" << it << '\\n';\n    }\n    @endcode\n\n    Range-based for loop with iterator proxy:\n\n    @code{cpp}\n    for (auto it : json::iterator_wrapper(j_object))\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    @note When iterating over an array, `key()` will return the index of the\n          element as string (see example).\n\n    @param[in] ref  reference to a JSON value\n    @return iteration proxy object wrapping @a ref with an interface to use in\n            range-based for loops\n\n    @liveexample{The following code shows how the wrapper is used,iterator_wrapper}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @note The name of this function is not yet final and may change in the\n    future.\n\n    @deprecated This stream operator is deprecated and will be removed in\n                future 4.0.0 of the library. Please use @ref items() instead;\n                that is, replace `json::iterator_wrapper(j)` with `j.items()`.\n    */\n    JSON_HEDLEY_DEPRECATED(3.1.0)\n    static iteration_proxy<iterator> iterator_wrapper(reference ref) noexcept\n    {\n        return ref.items();\n    }\n\n    /*!\n    @copydoc iterator_wrapper(reference)\n    */\n    JSON_HEDLEY_DEPRECATED(3.1.0)\n    static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref) noexcept\n    {\n        return ref.items();\n    }\n\n    /*!\n    @brief helper to access iterator member functions in range-based for\n\n    This function allows to access @ref iterator::key() and @ref\n    iterator::value() during range-based for loops. In these loops, a\n    reference to the JSON values is returned, so there is no access to the\n    underlying iterator.\n\n    For loop without `items()` function:\n\n    @code{cpp}\n    for (auto it = j_object.begin(); it != j_object.end(); ++it)\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    Range-based for loop without `items()` function:\n\n    @code{cpp}\n    for (auto it : j_object)\n    {\n        // \"it\" is of type json::reference and has no key() member\n        std::cout << \"value: \" << it << '\\n';\n    }\n    @endcode\n\n    Range-based for loop with `items()` function:\n\n    @code{cpp}\n    for (auto& el : j_object.items())\n    {\n        std::cout << \"key: \" << el.key() << \", value:\" << el.value() << '\\n';\n    }\n    @endcode\n\n    The `items()` function also allows to use\n    [structured bindings](https://en.cppreference.com/w/cpp/language/structured_binding)\n    (C++17):\n\n    @code{cpp}\n    for (auto& [key, val] : j_object.items())\n    {\n        std::cout << \"key: \" << key << \", value:\" << val << '\\n';\n    }\n    @endcode\n\n    @note When iterating over an array, `key()` will return the index of the\n          element as string (see example). For primitive types (e.g., numbers),\n          `key()` returns an empty string.\n\n    @return iteration proxy object wrapping @a ref with an interface to use in\n            range-based for loops\n\n    @liveexample{The following code shows how the function is used.,items}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 3.1.0, structured bindings support since 3.5.0.\n    */\n    iteration_proxy<iterator> items() noexcept\n    {\n        return iteration_proxy<iterator>(*this);\n    }\n\n    /*!\n    @copydoc items()\n    */\n    iteration_proxy<const_iterator> items() const noexcept\n    {\n        return iteration_proxy<const_iterator>(*this);\n    }\n\n    /// @}\n\n\n    //////////////\n    // capacity //\n    //////////////\n\n    /// @name capacity\n    /// @{\n\n    /*!\n    @brief checks whether the container is empty.\n\n    Checks if a JSON value has no elements (i.e. whether its @ref size is `0`).\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `true`\n            boolean     | `false`\n            string      | `false`\n            number      | `false`\n            object      | result of function `object_t::empty()`\n            array       | result of function `array_t::empty()`\n\n    @liveexample{The following code uses `empty()` to check if a JSON\n    object contains any elements.,empty}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their `empty()` functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @note This function does not return whether a string stored as JSON value\n    is empty - it returns whether the JSON container itself is empty which is\n    false in the case of a string.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `begin() == end()`.\n\n    @sa @ref size() -- returns the number of elements\n\n    @since version 1.0.0\n    */\n    bool empty() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::null:\n            {\n                // null values are empty\n                return true;\n            }\n\n            case value_t::array:\n            {\n                // delegate call to array_t::empty()\n                return m_value.array->empty();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::empty()\n                return m_value.object->empty();\n            }\n\n            default:\n            {\n                // all other types are nonempty\n                return false;\n            }\n        }\n    }\n\n    /*!\n    @brief returns the number of elements\n\n    Returns the number of elements in a JSON value.\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `0`\n            boolean     | `1`\n            string      | `1`\n            number      | `1`\n            object      | result of function object_t::size()\n            array       | result of function array_t::size()\n\n    @liveexample{The following code calls `size()` on the different value\n    types.,size}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their size() functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @note This function does not return the length of a string stored as JSON\n    value - it returns the number of elements in the JSON value which is 1 in\n    the case of a string.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `std::distance(begin(), end())`.\n\n    @sa @ref empty() -- checks whether the container is empty\n    @sa @ref max_size() -- returns the maximal number of elements\n\n    @since version 1.0.0\n    */\n    size_type size() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::null:\n            {\n                // null values are empty\n                return 0;\n            }\n\n            case value_t::array:\n            {\n                // delegate call to array_t::size()\n                return m_value.array->size();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::size()\n                return m_value.object->size();\n            }\n\n            default:\n            {\n                // all other types have size 1\n                return 1;\n            }\n        }\n    }\n\n    /*!\n    @brief returns the maximum possible number of elements\n\n    Returns the maximum number of elements a JSON value is able to hold due to\n    system or library implementation limitations, i.e. `std::distance(begin(),\n    end())` for the JSON value.\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `0` (same as `size()`)\n            boolean     | `1` (same as `size()`)\n            string      | `1` (same as `size()`)\n            number      | `1` (same as `size()`)\n            object      | result of function `object_t::max_size()`\n            array       | result of function `array_t::max_size()`\n\n    @liveexample{The following code calls `max_size()` on the different value\n    types. Note the output is implementation specific.,max_size}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their `max_size()` functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of returning `b.size()` where `b` is the largest\n      possible JSON value.\n\n    @sa @ref size() -- returns the number of elements\n\n    @since version 1.0.0\n    */\n    size_type max_size() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::array:\n            {\n                // delegate call to array_t::max_size()\n                return m_value.array->max_size();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::max_size()\n                return m_value.object->max_size();\n            }\n\n            default:\n            {\n                // all other types have max_size() == size()\n                return size();\n            }\n        }\n    }\n\n    /// @}\n\n\n    ///////////////\n    // modifiers //\n    ///////////////\n\n    /// @name modifiers\n    /// @{\n\n    /*!\n    @brief clears the contents\n\n    Clears the content of a JSON value and resets it to the default value as\n    if @ref basic_json(value_t) would have been called with the current value\n    type from @ref type():\n\n    Value type  | initial value\n    ----------- | -------------\n    null        | `null`\n    boolean     | `false`\n    string      | `\"\"`\n    number      | `0`\n    object      | `{}`\n    array       | `[]`\n\n    @post Has the same effect as calling\n    @code {.cpp}\n    *this = basic_json(type());\n    @endcode\n\n    @liveexample{The example below shows the effect of `clear()` to different\n    JSON types.,clear}\n\n    @complexity Linear in the size of the JSON value.\n\n    @iterators All iterators, pointers and references related to this container\n               are invalidated.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @sa @ref basic_json(value_t) -- constructor that creates an object with the\n        same value than calling `clear()`\n\n    @since version 1.0.0\n    */\n    void clear() noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::number_integer:\n            {\n                m_value.number_integer = 0;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value.number_unsigned = 0;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value.number_float = 0.0;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value.boolean = false;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value.string->clear();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value.array->clear();\n                break;\n            }\n\n            case value_t::object:\n            {\n                m_value.object->clear();\n                break;\n            }\n\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @brief add an object to an array\n\n    Appends the given element @a val to the end of the JSON value. If the\n    function is called on a JSON null value, an empty array is created before\n    appending @a val.\n\n    @param[in] val the value to add to the JSON array\n\n    @throw type_error.308 when called on a type other than JSON array or\n    null; example: `\"cannot use push_back() with number\"`\n\n    @complexity Amortized constant.\n\n    @liveexample{The example shows how `push_back()` and `+=` can be used to\n    add elements to a JSON array. Note how the `null` value was silently\n    converted to a JSON array.,push_back}\n\n    @since version 1.0.0\n    */\n    void push_back(basic_json&& val)\n    {\n        // push_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(not(is_null() or is_array())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array (move semantics)\n        m_value.array->push_back(std::move(val));\n        // invalidate object: mark it null so we do not call the destructor\n        // cppcheck-suppress accessMoved\n        val.m_type = value_t::null;\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    reference operator+=(basic_json&& val)\n    {\n        push_back(std::move(val));\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    void push_back(const basic_json& val)\n    {\n        // push_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(not(is_null() or is_array())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array\n        m_value.array->push_back(val);\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    reference operator+=(const basic_json& val)\n    {\n        push_back(val);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an object\n\n    Inserts the given element @a val to the JSON object. If the function is\n    called on a JSON null value, an empty object is created before inserting\n    @a val.\n\n    @param[in] val the value to add to the JSON object\n\n    @throw type_error.308 when called on a type other than JSON object or\n    null; example: `\"cannot use push_back() with number\"`\n\n    @complexity Logarithmic in the size of the container, O(log(`size()`)).\n\n    @liveexample{The example shows how `push_back()` and `+=` can be used to\n    add elements to a JSON object. Note how the `null` value was silently\n    converted to a JSON object.,push_back__object_t__value}\n\n    @since version 1.0.0\n    */\n    void push_back(const typename object_t::value_type& val)\n    {\n        // push_back only works for null objects or objects\n        if (JSON_HEDLEY_UNLIKELY(not(is_null() or is_object())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // add element to array\n        m_value.object->insert(val);\n    }\n\n    /*!\n    @brief add an object to an object\n    @copydoc push_back(const typename object_t::value_type&)\n    */\n    reference operator+=(const typename object_t::value_type& val)\n    {\n        push_back(val);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an object\n\n    This function allows to use `push_back` with an initializer list. In case\n\n    1. the current value is an object,\n    2. the initializer list @a init contains only two elements, and\n    3. the first element of @a init is a string,\n\n    @a init is converted into an object element and added using\n    @ref push_back(const typename object_t::value_type&). Otherwise, @a init\n    is converted to a JSON value and added using @ref push_back(basic_json&&).\n\n    @param[in] init  an initializer list\n\n    @complexity Linear in the size of the initializer list @a init.\n\n    @note This function is required to resolve an ambiguous overload error,\n          because pairs like `{\"key\", \"value\"}` can be both interpreted as\n          `object_t::value_type` or `std::initializer_list<basic_json>`, see\n          https://github.com/nlohmann/json/issues/235 for more information.\n\n    @liveexample{The example shows how initializer lists are treated as\n    objects when possible.,push_back__initializer_list}\n    */\n    void push_back(initializer_list_t init)\n    {\n        if (is_object() and init.size() == 2 and (*init.begin())->is_string())\n        {\n            basic_json&& key = init.begin()->moved_or_copied();\n            push_back(typename object_t::value_type(\n                          std::move(key.get_ref<string_t&>()), (init.begin() + 1)->moved_or_copied()));\n        }\n        else\n        {\n            push_back(basic_json(init));\n        }\n    }\n\n    /*!\n    @brief add an object to an object\n    @copydoc push_back(initializer_list_t)\n    */\n    reference operator+=(initializer_list_t init)\n    {\n        push_back(init);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an array\n\n    Creates a JSON value from the passed parameters @a args to the end of the\n    JSON value. If the function is called on a JSON null value, an empty array\n    is created before appending the value created from @a args.\n\n    @param[in] args arguments to forward to a constructor of @ref basic_json\n    @tparam Args compatible types to create a @ref basic_json object\n\n    @return reference to the inserted element\n\n    @throw type_error.311 when called on a type other than JSON array or\n    null; example: `\"cannot use emplace_back() with number\"`\n\n    @complexity Amortized constant.\n\n    @liveexample{The example shows how `push_back()` can be used to add\n    elements to a JSON array. Note how the `null` value was silently converted\n    to a JSON array.,emplace_back}\n\n    @since version 2.0.8, returns reference since 3.7.0\n    */\n    template<class... Args>\n    reference emplace_back(Args&& ... args)\n    {\n        // emplace_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(not(is_null() or is_array())))\n        {\n            JSON_THROW(type_error::create(311, \"cannot use emplace_back() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array (perfect forwarding)\n#ifdef JSON_HAS_CPP_17\n        return m_value.array->emplace_back(std::forward<Args>(args)...);\n#else\n        m_value.array->emplace_back(std::forward<Args>(args)...);\n        return m_value.array->back();\n#endif\n    }\n\n    /*!\n    @brief add an object to an object if key does not exist\n\n    Inserts a new element into a JSON object constructed in-place with the\n    given @a args if there is no element with the key in the container. If the\n    function is called on a JSON null value, an empty object is created before\n    appending the value created from @a args.\n\n    @param[in] args arguments to forward to a constructor of @ref basic_json\n    @tparam Args compatible types to create a @ref basic_json object\n\n    @return a pair consisting of an iterator to the inserted element, or the\n            already-existing element if no insertion happened, and a bool\n            denoting whether the insertion took place.\n\n    @throw type_error.311 when called on a type other than JSON object or\n    null; example: `\"cannot use emplace() with number\"`\n\n    @complexity Logarithmic in the size of the container, O(log(`size()`)).\n\n    @liveexample{The example shows how `emplace()` can be used to add elements\n    to a JSON object. Note how the `null` value was silently converted to a\n    JSON object. Further note how no value is added if there was already one\n    value stored with the same key.,emplace}\n\n    @since version 2.0.8\n    */\n    template<class... Args>\n    std::pair<iterator, bool> emplace(Args&& ... args)\n    {\n        // emplace only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(not(is_null() or is_object())))\n        {\n            JSON_THROW(type_error::create(311, \"cannot use emplace() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // add element to array (perfect forwarding)\n        auto res = m_value.object->emplace(std::forward<Args>(args)...);\n        // create result iterator and set iterator to the result of emplace\n        auto it = begin();\n        it.m_it.object_iterator = res.first;\n\n        // return pair of iterator and boolean\n        return {it, res.second};\n    }\n\n    /// Helper for insertion of an iterator\n    /// @note: This uses std::distance to support GCC 4.8,\n    ///        see https://github.com/nlohmann/json/pull/1257\n    template<typename... Args>\n    iterator insert_iterator(const_iterator pos, Args&& ... args)\n    {\n        iterator result(this);\n        assert(m_value.array != nullptr);\n\n        auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator);\n        m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...);\n        result.m_it.array_iterator = m_value.array->begin() + insert_pos;\n\n        // This could have been written as:\n        // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);\n        // but the return value of insert is missing in GCC 4.8, so it is written this way instead.\n\n        return result;\n    }\n\n    /*!\n    @brief inserts element\n\n    Inserts element @a val before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] val element to insert\n    @return iterator pointing to the inserted @a val.\n\n    @throw type_error.309 if called on JSON values other than arrays;\n    example: `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @complexity Constant plus linear in the distance between @a pos and end of\n    the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, const basic_json& val)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // check if iterator pos fits to this JSON value\n            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n            {\n                JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n            }\n\n            // insert to array and return iterator\n            return insert_iterator(pos, val);\n        }\n\n        JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief inserts element\n    @copydoc insert(const_iterator, const basic_json&)\n    */\n    iterator insert(const_iterator pos, basic_json&& val)\n    {\n        return insert(pos, val);\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts @a cnt copies of @a val before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] cnt number of copies of @a val to insert\n    @param[in] val element to insert\n    @return iterator pointing to the first element inserted, or @a pos if\n    `cnt==0`\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @complexity Linear in @a cnt plus linear in the distance between @a pos\n    and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__count}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, size_type cnt, const basic_json& val)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // check if iterator pos fits to this JSON value\n            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n            {\n                JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n            }\n\n            // insert to array and return iterator\n            return insert_iterator(pos, cnt, val);\n        }\n\n        JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from range `[first, last)` before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n    @throw invalid_iterator.211 if @a first or @a last are iterators into\n    container for which insert is called; example: `\"passed iterators may not\n    belong to container\"`\n\n    @return iterator pointing to the first element inserted, or @a pos if\n    `first==last`\n\n    @complexity Linear in `std::distance(first, last)` plus linear in the\n    distance between @a pos and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__range}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, const_iterator first, const_iterator last)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_UNLIKELY(not is_array()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n        }\n\n        // check if iterator pos fits to this JSON value\n        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\"));\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(first.m_object == this))\n        {\n            JSON_THROW(invalid_iterator::create(211, \"passed iterators may not belong to container\"));\n        }\n\n        // insert to array and return iterator\n        return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from initializer list @a ilist before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] ilist initializer list to insert the values from\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @return iterator pointing to the first element inserted, or @a pos if\n    `ilist` is empty\n\n    @complexity Linear in `ilist.size()` plus linear in the distance between\n    @a pos and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__ilist}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, initializer_list_t ilist)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_UNLIKELY(not is_array()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n        }\n\n        // check if iterator pos fits to this JSON value\n        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n        }\n\n        // insert to array and return iterator\n        return insert_iterator(pos, ilist.begin(), ilist.end());\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from range `[first, last)`.\n\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n\n    @throw type_error.309 if called on JSON values other than objects; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if iterator @a first or @a last does does not\n    point to an object; example: `\"iterators first and last must point to\n    objects\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n\n    @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number\n    of elements to insert.\n\n    @liveexample{The example shows how `insert()` is used.,insert__range_object}\n\n    @since version 3.0.0\n    */\n    void insert(const_iterator first, const_iterator last)\n    {\n        // insert only works for objects\n        if (JSON_HEDLEY_UNLIKELY(not is_object()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\"));\n        }\n\n        // passed iterators must belong to objects\n        if (JSON_HEDLEY_UNLIKELY(not first.m_object->is_object()))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterators first and last must point to objects\"));\n        }\n\n        m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);\n    }\n\n    /*!\n    @brief updates a JSON object from another object, overwriting existing keys\n\n    Inserts all values from JSON object @a j and overwrites existing keys.\n\n    @param[in] j  JSON object to read values from\n\n    @throw type_error.312 if called on JSON values other than objects; example:\n    `\"cannot use update() with string\"`\n\n    @complexity O(N*log(size() + N)), where N is the number of elements to\n                insert.\n\n    @liveexample{The example shows how `update()` is used.,update}\n\n    @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update\n\n    @since version 3.0.0\n    */\n    void update(const_reference j)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(not is_object()))\n        {\n            JSON_THROW(type_error::create(312, \"cannot use update() with \" + std::string(type_name())));\n        }\n        if (JSON_HEDLEY_UNLIKELY(not j.is_object()))\n        {\n            JSON_THROW(type_error::create(312, \"cannot use update() with \" + std::string(j.type_name())));\n        }\n\n        for (auto it = j.cbegin(); it != j.cend(); ++it)\n        {\n            m_value.object->operator[](it.key()) = it.value();\n        }\n    }\n\n    /*!\n    @brief updates a JSON object from another object, overwriting existing keys\n\n    Inserts all values from from range `[first, last)` and overwrites existing\n    keys.\n\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n\n    @throw type_error.312 if called on JSON values other than objects; example:\n    `\"cannot use update() with string\"`\n    @throw invalid_iterator.202 if iterator @a first or @a last does does not\n    point to an object; example: `\"iterators first and last must point to\n    objects\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n\n    @complexity O(N*log(size() + N)), where N is the number of elements to\n                insert.\n\n    @liveexample{The example shows how `update()` is used__range.,update}\n\n    @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update\n\n    @since version 3.0.0\n    */\n    void update(const_iterator first, const_iterator last)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(not is_object()))\n        {\n            JSON_THROW(type_error::create(312, \"cannot use update() with \" + std::string(type_name())));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\"));\n        }\n\n        // passed iterators must belong to objects\n        if (JSON_HEDLEY_UNLIKELY(not first.m_object->is_object()\n                                 or not last.m_object->is_object()))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterators first and last must point to objects\"));\n        }\n\n        for (auto it = first; it != last; ++it)\n        {\n            m_value.object->operator[](it.key()) = it.value();\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of the JSON value with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other JSON value to exchange the contents with\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how JSON values can be swapped with\n    `swap()`.,swap__reference}\n\n    @since version 1.0.0\n    */\n    void swap(reference other) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value and\n        std::is_nothrow_move_assignable<value_t>::value and\n        std::is_nothrow_move_constructible<json_value>::value and\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        std::swap(m_type, other.m_type);\n        std::swap(m_value, other.m_value);\n        assert_invariant();\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON array with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other array to exchange the contents with\n\n    @throw type_error.310 when JSON value is not an array; example: `\"cannot\n    use swap() with string\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how arrays can be swapped with\n    `swap()`.,swap__array_t}\n\n    @since version 1.0.0\n    */\n    void swap(array_t& other)\n    {\n        // swap only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            std::swap(*(m_value.array), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON object with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other object to exchange the contents with\n\n    @throw type_error.310 when JSON value is not an object; example:\n    `\"cannot use swap() with string\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how objects can be swapped with\n    `swap()`.,swap__object_t}\n\n    @since version 1.0.0\n    */\n    void swap(object_t& other)\n    {\n        // swap only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            std::swap(*(m_value.object), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON string with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other string to exchange the contents with\n\n    @throw type_error.310 when JSON value is not a string; example: `\"cannot\n    use swap() with boolean\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how strings can be swapped with\n    `swap()`.,swap__string_t}\n\n    @since version 1.0.0\n    */\n    void swap(string_t& other)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_string()))\n        {\n            std::swap(*(m_value.string), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name())));\n        }\n    }\n\n    /// @}\n\n  public:\n    //////////////////////////////////////////\n    // lexicographical comparison operators //\n    //////////////////////////////////////////\n\n    /// @name lexicographical comparison operators\n    /// @{\n\n    /*!\n    @brief comparison: equal\n\n    Compares two JSON values for equality according to the following rules:\n    - Two JSON values are equal if (1) they are from the same type and (2)\n      their stored values are the same according to their respective\n      `operator==`.\n    - Integer and floating-point numbers are automatically converted before\n      comparison. Note than two NaN values are always treated as unequal.\n    - Two JSON null values are equal.\n\n    @note Floating-point inside JSON values numbers are compared with\n    `json::number_float_t::operator==` which is `double::operator==` by\n    default. To compare floating-point while respecting an epsilon, an alternative\n    [comparison function](https://github.com/mariokonrad/marnav/blob/master/src/marnav/math/floatingpoint.hpp#L34-#L39)\n    could be used, for instance\n    @code {.cpp}\n    template<typename T, typename = typename std::enable_if<std::is_floating_point<T>::value, T>::type>\n    inline bool is_same(T a, T b, T epsilon = std::numeric_limits<T>::epsilon()) noexcept\n    {\n        return std::abs(a - b) <= epsilon;\n    }\n    @endcode\n\n    @note NaN values never compare equal to themselves or to other NaN values.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether the values @a lhs and @a rhs are equal\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @complexity Linear.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__equal}\n\n    @since version 1.0.0\n    */\n    friend bool operator==(const_reference lhs, const_reference rhs) noexcept\n    {\n        const auto lhs_type = lhs.type();\n        const auto rhs_type = rhs.type();\n\n        if (lhs_type == rhs_type)\n        {\n            switch (lhs_type)\n            {\n                case value_t::array:\n                    return *lhs.m_value.array == *rhs.m_value.array;\n\n                case value_t::object:\n                    return *lhs.m_value.object == *rhs.m_value.object;\n\n                case value_t::null:\n                    return true;\n\n                case value_t::string:\n                    return *lhs.m_value.string == *rhs.m_value.string;\n\n                case value_t::boolean:\n                    return lhs.m_value.boolean == rhs.m_value.boolean;\n\n                case value_t::number_integer:\n                    return lhs.m_value.number_integer == rhs.m_value.number_integer;\n\n                case value_t::number_unsigned:\n                    return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned;\n\n                case value_t::number_float:\n                    return lhs.m_value.number_float == rhs.m_value.number_float;\n\n                default:\n                    return false;\n            }\n        }\n        else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer)\n        {\n            return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer);\n        }\n        else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned);\n        }\n        else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer)\n        {\n            return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer;\n        }\n        else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned);\n        }\n\n        return false;\n    }\n\n    /*!\n    @brief comparison: equal\n    @copydoc operator==(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs == basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: equal\n    @copydoc operator==(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) == rhs;\n    }\n\n    /*!\n    @brief comparison: not equal\n\n    Compares two JSON values for inequality by calculating `not (lhs == rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether the values @a lhs and @a rhs are not equal\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__notequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator!=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return not (lhs == rhs);\n    }\n\n    /*!\n    @brief comparison: not equal\n    @copydoc operator!=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs != basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: not equal\n    @copydoc operator!=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) != rhs;\n    }\n\n    /*!\n    @brief comparison: less than\n\n    Compares whether one JSON value @a lhs is less than another JSON value @a\n    rhs according to the following rules:\n    - If @a lhs and @a rhs have the same type, the values are compared using\n      the default `<` operator.\n    - Integer and floating-point numbers are automatically converted before\n      comparison\n    - In case @a lhs and @a rhs have different types, the values are ignored\n      and the order of the types is considered, see\n      @ref operator<(const value_t, const value_t).\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is less than @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__less}\n\n    @since version 1.0.0\n    */\n    friend bool operator<(const_reference lhs, const_reference rhs) noexcept\n    {\n        const auto lhs_type = lhs.type();\n        const auto rhs_type = rhs.type();\n\n        if (lhs_type == rhs_type)\n        {\n            switch (lhs_type)\n            {\n                case value_t::array:\n                    // note parentheses are necessary, see\n                    // https://github.com/nlohmann/json/issues/1530\n                    return (*lhs.m_value.array) < (*rhs.m_value.array);\n\n                case value_t::object:\n                    return (*lhs.m_value.object) < (*rhs.m_value.object);\n\n                case value_t::null:\n                    return false;\n\n                case value_t::string:\n                    return (*lhs.m_value.string) < (*rhs.m_value.string);\n\n                case value_t::boolean:\n                    return (lhs.m_value.boolean) < (rhs.m_value.boolean);\n\n                case value_t::number_integer:\n                    return (lhs.m_value.number_integer) < (rhs.m_value.number_integer);\n\n                case value_t::number_unsigned:\n                    return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned);\n\n                case value_t::number_float:\n                    return (lhs.m_value.number_float) < (rhs.m_value.number_float);\n\n                default:\n                    return false;\n            }\n        }\n        else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_integer) < rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer)\n        {\n            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_integer);\n        }\n        else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_unsigned);\n        }\n        else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_integer < static_cast<number_integer_t>(rhs.m_value.number_unsigned);\n        }\n        else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer)\n        {\n            return static_cast<number_integer_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer;\n        }\n\n        // We only reach this line if we cannot compare values. In that case,\n        // we compare types. Note we have to call the operator explicitly,\n        // because MSVC has problems otherwise.\n        return operator<(lhs_type, rhs_type);\n    }\n\n    /*!\n    @brief comparison: less than\n    @copydoc operator<(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs < basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: less than\n    @copydoc operator<(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) < rhs;\n    }\n\n    /*!\n    @brief comparison: less than or equal\n\n    Compares whether one JSON value @a lhs is less than or equal to another\n    JSON value by calculating `not (rhs < lhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is less than or equal to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__greater}\n\n    @since version 1.0.0\n    */\n    friend bool operator<=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return not (rhs < lhs);\n    }\n\n    /*!\n    @brief comparison: less than or equal\n    @copydoc operator<=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs <= basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: less than or equal\n    @copydoc operator<=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) <= rhs;\n    }\n\n    /*!\n    @brief comparison: greater than\n\n    Compares whether one JSON value @a lhs is greater than another\n    JSON value by calculating `not (lhs <= rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is greater than to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__lessequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator>(const_reference lhs, const_reference rhs) noexcept\n    {\n        return not (lhs <= rhs);\n    }\n\n    /*!\n    @brief comparison: greater than\n    @copydoc operator>(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs > basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: greater than\n    @copydoc operator>(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) > rhs;\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n\n    Compares whether one JSON value @a lhs is greater than or equal to another\n    JSON value by calculating `not (lhs < rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is greater than or equal to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__greaterequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator>=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return not (lhs < rhs);\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n    @copydoc operator>=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs >= basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n    @copydoc operator>=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) >= rhs;\n    }\n\n    /// @}\n\n    ///////////////////\n    // serialization //\n    ///////////////////\n\n    /// @name serialization\n    /// @{\n\n    /*!\n    @brief serialize to stream\n\n    Serialize the given JSON value @a j to the output stream @a o. The JSON\n    value will be serialized using the @ref dump member function.\n\n    - The indentation of the output can be controlled with the member variable\n      `width` of the output stream @a o. For instance, using the manipulator\n      `std::setw(4)` on @a o sets the indentation level to `4` and the\n      serialization result is the same as calling `dump(4)`.\n\n    - The indentation character can be controlled with the member variable\n      `fill` of the output stream @a o. For instance, the manipulator\n      `std::setfill('\\\\t')` sets indentation to use a tab character rather than\n      the default space character.\n\n    @param[in,out] o  stream to serialize to\n    @param[in] j  JSON value to serialize\n\n    @return the stream @a o\n\n    @throw type_error.316 if a string stored inside the JSON value is not\n                          UTF-8 encoded\n\n    @complexity Linear.\n\n    @liveexample{The example below shows the serialization with different\n    parameters to `width` to adjust the indentation level.,operator_serialize}\n\n    @since version 1.0.0; indentation character added in version 3.0.0\n    */\n    friend std::ostream& operator<<(std::ostream& o, const basic_json& j)\n    {\n        // read width member and use it as indentation parameter if nonzero\n        const bool pretty_print = o.width() > 0;\n        const auto indentation = pretty_print ? o.width() : 0;\n\n        // reset width to 0 for subsequent calls to this stream\n        o.width(0);\n\n        // do the actual serialization\n        serializer s(detail::output_adapter<char>(o), o.fill());\n        s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation));\n        return o;\n    }\n\n    /*!\n    @brief serialize to stream\n    @deprecated This stream operator is deprecated and will be removed in\n                future 4.0.0 of the library. Please use\n                @ref operator<<(std::ostream&, const basic_json&)\n                instead; that is, replace calls like `j >> o;` with `o << j;`.\n    @since version 1.0.0; deprecated since version 3.0.0\n    */\n    JSON_HEDLEY_DEPRECATED(3.0.0)\n    friend std::ostream& operator>>(const basic_json& j, std::ostream& o)\n    {\n        return o << j;\n    }\n\n    /// @}\n\n\n    /////////////////////\n    // deserialization //\n    /////////////////////\n\n    /// @name deserialization\n    /// @{\n\n    /*!\n    @brief deserialize from a compatible input\n\n    This function reads from a compatible input. Examples are:\n    - an array of 1-byte values\n    - strings with character/literal type with size of 1 byte\n    - input streams\n    - container with contiguous storage of 1-byte values. Compatible container\n      types include `std::vector`, `std::string`, `std::array`,\n      `std::valarray`, and `std::initializer_list`. Furthermore, C-style\n      arrays can be used with `std::begin()`/`std::end()`. User-defined\n      containers can be used as long as they implement random-access iterators\n      and a contiguous storage.\n\n    @pre Each element of the container has a size of 1 byte. Violating this\n    precondition yields undefined behavior. **This precondition is enforced\n    with a static assertion.**\n\n    @pre The container storage is contiguous. Violating this precondition\n    yields undefined behavior. **This precondition is enforced with an\n    assertion.**\n\n    @warning There is no way to enforce all preconditions at compile-time. If\n             the function is called with a noncompliant container and with\n             assertions switched off, the behavior is undefined and will most\n             likely yield segmentation violation.\n\n    @param[in] i  input to read from\n    @param[in] cb  a parser callback function of type @ref parser_callback_t\n    which is used to control the deserialization by filtering unwanted values\n    (optional)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.101 if a parse error occurs; example: `\"\"unexpected end\n    of input; expected string literal\"\"`\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser. The complexity can be higher if the parser callback function\n    @a cb has a super-linear complexity.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `parse()` function reading\n    from an array.,parse__array__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function with\n    and without callback function.,parse__string__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function with\n    and without callback function.,parse__istream__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function reading\n    from a contiguous container.,parse__contiguouscontainer__parser_callback_t}\n\n    @since version 2.0.3 (contiguous containers)\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json parse(detail::input_adapter&& i,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true)\n    {\n        basic_json result;\n        parser(i, cb, allow_exceptions).parse(true, result);\n        return result;\n    }\n\n    static bool accept(detail::input_adapter&& i)\n    {\n        return parser(i).accept(true);\n    }\n\n    /*!\n    @brief generate SAX events\n\n    The SAX event lister must follow the interface of @ref json_sax.\n\n    This function reads from a compatible input. Examples are:\n    - an array of 1-byte values\n    - strings with character/literal type with size of 1 byte\n    - input streams\n    - container with contiguous storage of 1-byte values. Compatible container\n      types include `std::vector`, `std::string`, `std::array`,\n      `std::valarray`, and `std::initializer_list`. Furthermore, C-style\n      arrays can be used with `std::begin()`/`std::end()`. User-defined\n      containers can be used as long as they implement random-access iterators\n      and a contiguous storage.\n\n    @pre Each element of the container has a size of 1 byte. Violating this\n    precondition yields undefined behavior. **This precondition is enforced\n    with a static assertion.**\n\n    @pre The container storage is contiguous. Violating this precondition\n    yields undefined behavior. **This precondition is enforced with an\n    assertion.**\n\n    @warning There is no way to enforce all preconditions at compile-time. If\n             the function is called with a noncompliant container and with\n             assertions switched off, the behavior is undefined and will most\n             likely yield segmentation violation.\n\n    @param[in] i  input to read from\n    @param[in,out] sax  SAX event listener\n    @param[in] format  the format to parse (JSON, CBOR, MessagePack, or UBJSON)\n    @param[in] strict  whether the input has to be consumed completely\n\n    @return return value of the last processed SAX event\n\n    @throw parse_error.101 if a parse error occurs; example: `\"\"unexpected end\n    of input; expected string literal\"\"`\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser. The complexity can be higher if the SAX consumer @a sax has\n    a super-linear complexity.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `sax_parse()` function\n    reading from string and processing the events with a user-defined SAX\n    event consumer.,sax_parse}\n\n    @since version 3.2.0\n    */\n    template <typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    static bool sax_parse(detail::input_adapter&& i, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true)\n    {\n        assert(sax);\n        return format == input_format_t::json\n               ? parser(std::move(i)).sax_parse(sax, strict)\n               : detail::binary_reader<basic_json, SAX>(std::move(i)).sax_parse(format, sax, strict);\n    }\n\n    /*!\n    @brief deserialize from an iterator range with contiguous storage\n\n    This function reads from an iterator range of a container with contiguous\n    storage of 1-byte values. Compatible container types include\n    `std::vector`, `std::string`, `std::array`, `std::valarray`, and\n    `std::initializer_list`. Furthermore, C-style arrays can be used with\n    `std::begin()`/`std::end()`. User-defined containers can be used as long\n    as they implement random-access iterators and a contiguous storage.\n\n    @pre The iterator range is contiguous. Violating this precondition yields\n    undefined behavior. **This precondition is enforced with an assertion.**\n    @pre Each element in the range has a size of 1 byte. Violating this\n    precondition yields undefined behavior. **This precondition is enforced\n    with a static assertion.**\n\n    @warning There is no way to enforce all preconditions at compile-time. If\n             the function is called with noncompliant iterators and with\n             assertions switched off, the behavior is undefined and will most\n             likely yield segmentation violation.\n\n    @tparam IteratorType iterator of container with contiguous storage\n    @param[in] first  begin of the range to parse (included)\n    @param[in] last  end of the range to parse (excluded)\n    @param[in] cb  a parser callback function of type @ref parser_callback_t\n    which is used to control the deserialization by filtering unwanted values\n    (optional)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.101 in case of an unexpected token\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser. The complexity can be higher if the parser callback function\n    @a cb has a super-linear complexity.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `parse()` function reading\n    from an iterator range.,parse__iteratortype__parser_callback_t}\n\n    @since version 2.0.3\n    */\n    template<class IteratorType, typename std::enable_if<\n                 std::is_base_of<\n                     std::random_access_iterator_tag,\n                     typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>\n    static basic_json parse(IteratorType first, IteratorType last,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true)\n    {\n        basic_json result;\n        parser(detail::input_adapter(first, last), cb, allow_exceptions).parse(true, result);\n        return result;\n    }\n\n    template<class IteratorType, typename std::enable_if<\n                 std::is_base_of<\n                     std::random_access_iterator_tag,\n                     typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>\n    static bool accept(IteratorType first, IteratorType last)\n    {\n        return parser(detail::input_adapter(first, last)).accept(true);\n    }\n\n    template<class IteratorType, class SAX, typename std::enable_if<\n                 std::is_base_of<\n                     std::random_access_iterator_tag,\n                     typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>\n    JSON_HEDLEY_NON_NULL(3)\n    static bool sax_parse(IteratorType first, IteratorType last, SAX* sax)\n    {\n        return parser(detail::input_adapter(first, last)).sax_parse(sax);\n    }\n\n    /*!\n    @brief deserialize from stream\n    @deprecated This stream operator is deprecated and will be removed in\n                version 4.0.0 of the library. Please use\n                @ref operator>>(std::istream&, basic_json&)\n                instead; that is, replace calls like `j << i;` with `i >> j;`.\n    @since version 1.0.0; deprecated since version 3.0.0\n    */\n    JSON_HEDLEY_DEPRECATED(3.0.0)\n    friend std::istream& operator<<(basic_json& j, std::istream& i)\n    {\n        return operator>>(i, j);\n    }\n\n    /*!\n    @brief deserialize from stream\n\n    Deserializes an input stream to a JSON value.\n\n    @param[in,out] i  input stream to read a serialized JSON value from\n    @param[in,out] j  JSON value to write the deserialized input to\n\n    @throw parse_error.101 in case of an unexpected token\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below shows how a JSON value is constructed by\n    reading a serialization from a stream.,operator_deserialize}\n\n    @sa parse(std::istream&, const parser_callback_t) for a variant with a\n    parser callback function to filter values while parsing\n\n    @since version 1.0.0\n    */\n    friend std::istream& operator>>(std::istream& i, basic_json& j)\n    {\n        parser(detail::input_adapter(i)).parse(false, j);\n        return i;\n    }\n\n    /// @}\n\n    ///////////////////////////\n    // convenience functions //\n    ///////////////////////////\n\n    /*!\n    @brief return the type as string\n\n    Returns the type name as string to be used in error messages - usually to\n    indicate that a function was called on a wrong JSON type.\n\n    @return a string representation of a the @a m_type member:\n            Value type  | return value\n            ----------- | -------------\n            null        | `\"null\"`\n            boolean     | `\"boolean\"`\n            string      | `\"string\"`\n            number      | `\"number\"` (for all number types)\n            object      | `\"object\"`\n            array       | `\"array\"`\n            discarded   | `\"discarded\"`\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @complexity Constant.\n\n    @liveexample{The following code exemplifies `type_name()` for all JSON\n    types.,type_name}\n\n    @sa @ref type() -- return the type of the JSON value\n    @sa @ref operator value_t() -- return the type of the JSON value (implicit)\n\n    @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept`\n    since 3.0.0\n    */\n    JSON_HEDLEY_RETURNS_NON_NULL\n    const char* type_name() const noexcept\n    {\n        {\n            switch (m_type)\n            {\n                case value_t::null:\n                    return \"null\";\n                case value_t::object:\n                    return \"object\";\n                case value_t::array:\n                    return \"array\";\n                case value_t::string:\n                    return \"string\";\n                case value_t::boolean:\n                    return \"boolean\";\n                case value_t::discarded:\n                    return \"discarded\";\n                default:\n                    return \"number\";\n            }\n        }\n    }\n\n\n  private:\n    //////////////////////\n    // member variables //\n    //////////////////////\n\n    /// the type of the current element\n    value_t m_type = value_t::null;\n\n    /// the value of the current element\n    json_value m_value = {};\n\n    //////////////////////////////////////////\n    // binary serialization/deserialization //\n    //////////////////////////////////////////\n\n    /// @name binary serialization/deserialization support\n    /// @{\n\n  public:\n    /*!\n    @brief create a CBOR serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the CBOR (Concise\n    Binary Object Representation) serialization format. CBOR is a binary\n    serialization format which aims to be more compact than JSON itself, yet\n    more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    CBOR types according to the CBOR specification (RFC 7049):\n\n    JSON value type | value/range                                | CBOR type                          | first byte\n    --------------- | ------------------------------------------ | ---------------------------------- | ---------------\n    null            | `null`                                     | Null                               | 0xF6\n    boolean         | `true`                                     | True                               | 0xF5\n    boolean         | `false`                                    | False                              | 0xF4\n    number_integer  | -9223372036854775808..-2147483649          | Negative integer (8 bytes follow)  | 0x3B\n    number_integer  | -2147483648..-32769                        | Negative integer (4 bytes follow)  | 0x3A\n    number_integer  | -32768..-129                               | Negative integer (2 bytes follow)  | 0x39\n    number_integer  | -128..-25                                  | Negative integer (1 byte follow)   | 0x38\n    number_integer  | -24..-1                                    | Negative integer                   | 0x20..0x37\n    number_integer  | 0..23                                      | Integer                            | 0x00..0x17\n    number_integer  | 24..255                                    | Unsigned integer (1 byte follow)   | 0x18\n    number_integer  | 256..65535                                 | Unsigned integer (2 bytes follow)  | 0x19\n    number_integer  | 65536..4294967295                          | Unsigned integer (4 bytes follow)  | 0x1A\n    number_integer  | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow)  | 0x1B\n    number_unsigned | 0..23                                      | Integer                            | 0x00..0x17\n    number_unsigned | 24..255                                    | Unsigned integer (1 byte follow)   | 0x18\n    number_unsigned | 256..65535                                 | Unsigned integer (2 bytes follow)  | 0x19\n    number_unsigned | 65536..4294967295                          | Unsigned integer (4 bytes follow)  | 0x1A\n    number_unsigned | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow)  | 0x1B\n    number_float    | *any value*                                | Double-Precision Float             | 0xFB\n    string          | *length*: 0..23                            | UTF-8 string                       | 0x60..0x77\n    string          | *length*: 23..255                          | UTF-8 string (1 byte follow)       | 0x78\n    string          | *length*: 256..65535                       | UTF-8 string (2 bytes follow)      | 0x79\n    string          | *length*: 65536..4294967295                | UTF-8 string (4 bytes follow)      | 0x7A\n    string          | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow)      | 0x7B\n    array           | *size*: 0..23                              | array                              | 0x80..0x97\n    array           | *size*: 23..255                            | array (1 byte follow)              | 0x98\n    array           | *size*: 256..65535                         | array (2 bytes follow)             | 0x99\n    array           | *size*: 65536..4294967295                  | array (4 bytes follow)             | 0x9A\n    array           | *size*: 4294967296..18446744073709551615   | array (8 bytes follow)             | 0x9B\n    object          | *size*: 0..23                              | map                                | 0xA0..0xB7\n    object          | *size*: 23..255                            | map (1 byte follow)                | 0xB8\n    object          | *size*: 256..65535                         | map (2 bytes follow)               | 0xB9\n    object          | *size*: 65536..4294967295                  | map (4 bytes follow)               | 0xBA\n    object          | *size*: 4294967296..18446744073709551615   | map (8 bytes follow)               | 0xBB\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a CBOR value.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @note The following CBOR types are not used in the conversion:\n          - byte strings (0x40..0x5F)\n          - UTF-8 strings terminated by \"break\" (0x7F)\n          - arrays terminated by \"break\" (0x9F)\n          - maps terminated by \"break\" (0xBF)\n          - date/time (0xC0..0xC1)\n          - bignum (0xC2..0xC3)\n          - decimal fraction (0xC4)\n          - bigfloat (0xC5)\n          - tagged items (0xC6..0xD4, 0xD8..0xDB)\n          - expected conversions (0xD5..0xD7)\n          - simple values (0xE0..0xF3, 0xF8)\n          - undefined (0xF7)\n          - half and single-precision floats (0xF9-0xFA)\n          - break (0xFF)\n\n    @param[in] j  JSON value to serialize\n    @return MessagePack serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in CBOR format.,to_cbor}\n\n    @sa http://cbor.io\n    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the\n        analogous deserialization\n    @sa @ref to_msgpack(const basic_json&) for the related MessagePack format\n    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n\n    @since version 2.0.9\n    */\n    static std::vector<uint8_t> to_cbor(const basic_json& j)\n    {\n        std::vector<uint8_t> result;\n        to_cbor(j, result);\n        return result;\n    }\n\n    static void to_cbor(const basic_json& j, detail::output_adapter<uint8_t> o)\n    {\n        binary_writer<uint8_t>(o).write_cbor(j);\n    }\n\n    static void to_cbor(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_cbor(j);\n    }\n\n    /*!\n    @brief create a MessagePack serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the MessagePack\n    serialization format. MessagePack is a binary serialization format which\n    aims to be more compact than JSON itself, yet more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    MessagePack types according to the MessagePack specification:\n\n    JSON value type | value/range                       | MessagePack type | first byte\n    --------------- | --------------------------------- | ---------------- | ----------\n    null            | `null`                            | nil              | 0xC0\n    boolean         | `true`                            | true             | 0xC3\n    boolean         | `false`                           | false            | 0xC2\n    number_integer  | -9223372036854775808..-2147483649 | int64            | 0xD3\n    number_integer  | -2147483648..-32769               | int32            | 0xD2\n    number_integer  | -32768..-129                      | int16            | 0xD1\n    number_integer  | -128..-33                         | int8             | 0xD0\n    number_integer  | -32..-1                           | negative fixint  | 0xE0..0xFF\n    number_integer  | 0..127                            | positive fixint  | 0x00..0x7F\n    number_integer  | 128..255                          | uint 8           | 0xCC\n    number_integer  | 256..65535                        | uint 16          | 0xCD\n    number_integer  | 65536..4294967295                 | uint 32          | 0xCE\n    number_integer  | 4294967296..18446744073709551615  | uint 64          | 0xCF\n    number_unsigned | 0..127                            | positive fixint  | 0x00..0x7F\n    number_unsigned | 128..255                          | uint 8           | 0xCC\n    number_unsigned | 256..65535                        | uint 16          | 0xCD\n    number_unsigned | 65536..4294967295                 | uint 32          | 0xCE\n    number_unsigned | 4294967296..18446744073709551615  | uint 64          | 0xCF\n    number_float    | *any value*                       | float 64         | 0xCB\n    string          | *length*: 0..31                   | fixstr           | 0xA0..0xBF\n    string          | *length*: 32..255                 | str 8            | 0xD9\n    string          | *length*: 256..65535              | str 16           | 0xDA\n    string          | *length*: 65536..4294967295       | str 32           | 0xDB\n    array           | *size*: 0..15                     | fixarray         | 0x90..0x9F\n    array           | *size*: 16..65535                 | array 16         | 0xDC\n    array           | *size*: 65536..4294967295         | array 32         | 0xDD\n    object          | *size*: 0..15                     | fix map          | 0x80..0x8F\n    object          | *size*: 16..65535                 | map 16           | 0xDE\n    object          | *size*: 65536..4294967295         | map 32           | 0xDF\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a MessagePack value.\n\n    @note The following values can **not** be converted to a MessagePack value:\n          - strings with more than 4294967295 bytes\n          - arrays with more than 4294967295 elements\n          - objects with more than 4294967295 elements\n\n    @note The following MessagePack types are not used in the conversion:\n          - bin 8 - bin 32 (0xC4..0xC6)\n          - ext 8 - ext 32 (0xC7..0xC9)\n          - float 32 (0xCA)\n          - fixext 1 - fixext 16 (0xD4..0xD8)\n\n    @note Any MessagePack output created @ref to_msgpack can be successfully\n          parsed by @ref from_msgpack.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @param[in] j  JSON value to serialize\n    @return MessagePack serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in MessagePack format.,to_msgpack}\n\n    @sa http://msgpack.org\n    @sa @ref from_msgpack for the analogous deserialization\n    @sa @ref to_cbor(const basic_json& for the related CBOR format\n    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n\n    @since version 2.0.9\n    */\n    static std::vector<uint8_t> to_msgpack(const basic_json& j)\n    {\n        std::vector<uint8_t> result;\n        to_msgpack(j, result);\n        return result;\n    }\n\n    static void to_msgpack(const basic_json& j, detail::output_adapter<uint8_t> o)\n    {\n        binary_writer<uint8_t>(o).write_msgpack(j);\n    }\n\n    static void to_msgpack(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_msgpack(j);\n    }\n\n    /*!\n    @brief create a UBJSON serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the UBJSON\n    (Universal Binary JSON) serialization format. UBJSON aims to be more compact\n    than JSON itself, yet more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    UBJSON types according to the UBJSON specification:\n\n    JSON value type | value/range                       | UBJSON type | marker\n    --------------- | --------------------------------- | ----------- | ------\n    null            | `null`                            | null        | `Z`\n    boolean         | `true`                            | true        | `T`\n    boolean         | `false`                           | false       | `F`\n    number_integer  | -9223372036854775808..-2147483649 | int64       | `L`\n    number_integer  | -2147483648..-32769               | int32       | `l`\n    number_integer  | -32768..-129                      | int16       | `I`\n    number_integer  | -128..127                         | int8        | `i`\n    number_integer  | 128..255                          | uint8       | `U`\n    number_integer  | 256..32767                        | int16       | `I`\n    number_integer  | 32768..2147483647                 | int32       | `l`\n    number_integer  | 2147483648..9223372036854775807   | int64       | `L`\n    number_unsigned | 0..127                            | int8        | `i`\n    number_unsigned | 128..255                          | uint8       | `U`\n    number_unsigned | 256..32767                        | int16       | `I`\n    number_unsigned | 32768..2147483647                 | int32       | `l`\n    number_unsigned | 2147483648..9223372036854775807   | int64       | `L`\n    number_float    | *any value*                       | float64     | `D`\n    string          | *with shortest length indicator*  | string      | `S`\n    array           | *see notes on optimized format*   | array       | `[`\n    object          | *see notes on optimized format*   | map         | `{`\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a UBJSON value.\n\n    @note The following values can **not** be converted to a UBJSON value:\n          - strings with more than 9223372036854775807 bytes (theoretical)\n          - unsigned integer numbers above 9223372036854775807\n\n    @note The following markers are not used in the conversion:\n          - `Z`: no-op values are not created.\n          - `C`: single-byte strings are serialized with `S` markers.\n\n    @note Any UBJSON output created @ref to_ubjson can be successfully parsed\n          by @ref from_ubjson.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @note The optimized formats for containers are supported: Parameter\n          @a use_size adds size information to the beginning of a container and\n          removes the closing marker. Parameter @a use_type further checks\n          whether all elements of a container have the same type and adds the\n          type marker to the beginning of the container. The @a use_type\n          parameter must only be used together with @a use_size = true. Note\n          that @a use_size = true alone may result in larger representations -\n          the benefit of this parameter is that the receiving side is\n          immediately informed on the number of elements of the container.\n\n    @param[in] j  JSON value to serialize\n    @param[in] use_size  whether to add size annotations to container types\n    @param[in] use_type  whether to add type annotations to container types\n                         (must be combined with @a use_size = true)\n    @return UBJSON serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in UBJSON format.,to_ubjson}\n\n    @sa http://ubjson.org\n    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the\n        analogous deserialization\n    @sa @ref to_cbor(const basic_json& for the related CBOR format\n    @sa @ref to_msgpack(const basic_json&) for the related MessagePack format\n\n    @since version 3.1.0\n    */\n    static std::vector<uint8_t> to_ubjson(const basic_json& j,\n                                          const bool use_size = false,\n                                          const bool use_type = false)\n    {\n        std::vector<uint8_t> result;\n        to_ubjson(j, result, use_size, use_type);\n        return result;\n    }\n\n    static void to_ubjson(const basic_json& j, detail::output_adapter<uint8_t> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<uint8_t>(o).write_ubjson(j, use_size, use_type);\n    }\n\n    static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<char>(o).write_ubjson(j, use_size, use_type);\n    }\n\n\n    /*!\n    @brief Serializes the given JSON object `j` to BSON and returns a vector\n           containing the corresponding BSON-representation.\n\n    BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are\n    stored as a single entity (a so-called document).\n\n    The library uses the following mapping from JSON values types to BSON types:\n\n    JSON value type | value/range                       | BSON type   | marker\n    --------------- | --------------------------------- | ----------- | ------\n    null            | `null`                            | null        | 0x0A\n    boolean         | `true`, `false`                   | boolean     | 0x08\n    number_integer  | -9223372036854775808..-2147483649 | int64       | 0x12\n    number_integer  | -2147483648..2147483647           | int32       | 0x10\n    number_integer  | 2147483648..9223372036854775807   | int64       | 0x12\n    number_unsigned | 0..2147483647                     | int32       | 0x10\n    number_unsigned | 2147483648..9223372036854775807   | int64       | 0x12\n    number_unsigned | 9223372036854775808..18446744073709551615| --   | --\n    number_float    | *any value*                       | double      | 0x01\n    string          | *any value*                       | string      | 0x02\n    array           | *any value*                       | document    | 0x04\n    object          | *any value*                       | document    | 0x03\n\n    @warning The mapping is **incomplete**, since only JSON-objects (and things\n    contained therein) can be serialized to BSON.\n    Also, integers larger than 9223372036854775807 cannot be serialized to BSON,\n    and the keys may not contain U+0000, since they are serialized a\n    zero-terminated c-strings.\n\n    @throw out_of_range.407  if `j.is_number_unsigned() && j.get<std::uint64_t>() > 9223372036854775807`\n    @throw out_of_range.409  if a key in `j` contains a NULL (U+0000)\n    @throw type_error.317    if `!j.is_object()`\n\n    @pre The input `j` is required to be an object: `j.is_object() == true`.\n\n    @note Any BSON output created via @ref to_bson can be successfully parsed\n          by @ref from_bson.\n\n    @param[in] j  JSON value to serialize\n    @return BSON serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in BSON format.,to_bson}\n\n    @sa http://bsonspec.org/spec.html\n    @sa @ref from_bson(detail::input_adapter&&, const bool strict) for the\n        analogous deserialization\n    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n    @sa @ref to_cbor(const basic_json&) for the related CBOR format\n    @sa @ref to_msgpack(const basic_json&) for the related MessagePack format\n    */\n    static std::vector<uint8_t> to_bson(const basic_json& j)\n    {\n        std::vector<uint8_t> result;\n        to_bson(j, result);\n        return result;\n    }\n\n    /*!\n    @brief Serializes the given JSON object `j` to BSON and forwards the\n           corresponding BSON-representation to the given output_adapter `o`.\n    @param j The JSON object to convert to BSON.\n    @param o The output adapter that receives the binary BSON representation.\n    @pre The input `j` shall be an object: `j.is_object() == true`\n    @sa @ref to_bson(const basic_json&)\n    */\n    static void to_bson(const basic_json& j, detail::output_adapter<uint8_t> o)\n    {\n        binary_writer<uint8_t>(o).write_bson(j);\n    }\n\n    /*!\n    @copydoc to_bson(const basic_json&, detail::output_adapter<uint8_t>)\n    */\n    static void to_bson(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_bson(j);\n    }\n\n\n    /*!\n    @brief create a JSON value from an input in CBOR format\n\n    Deserializes a given input @a i to a JSON value using the CBOR (Concise\n    Binary Object Representation) serialization format.\n\n    The library maps CBOR types to JSON value types as follows:\n\n    CBOR type              | JSON value type | first byte\n    ---------------------- | --------------- | ----------\n    Integer                | number_unsigned | 0x00..0x17\n    Unsigned integer       | number_unsigned | 0x18\n    Unsigned integer       | number_unsigned | 0x19\n    Unsigned integer       | number_unsigned | 0x1A\n    Unsigned integer       | number_unsigned | 0x1B\n    Negative integer       | number_integer  | 0x20..0x37\n    Negative integer       | number_integer  | 0x38\n    Negative integer       | number_integer  | 0x39\n    Negative integer       | number_integer  | 0x3A\n    Negative integer       | number_integer  | 0x3B\n    Negative integer       | number_integer  | 0x40..0x57\n    UTF-8 string           | string          | 0x60..0x77\n    UTF-8 string           | string          | 0x78\n    UTF-8 string           | string          | 0x79\n    UTF-8 string           | string          | 0x7A\n    UTF-8 string           | string          | 0x7B\n    UTF-8 string           | string          | 0x7F\n    array                  | array           | 0x80..0x97\n    array                  | array           | 0x98\n    array                  | array           | 0x99\n    array                  | array           | 0x9A\n    array                  | array           | 0x9B\n    array                  | array           | 0x9F\n    map                    | object          | 0xA0..0xB7\n    map                    | object          | 0xB8\n    map                    | object          | 0xB9\n    map                    | object          | 0xBA\n    map                    | object          | 0xBB\n    map                    | object          | 0xBF\n    False                  | `false`         | 0xF4\n    True                   | `true`          | 0xF5\n    Null                   | `null`          | 0xF6\n    Half-Precision Float   | number_float    | 0xF9\n    Single-Precision Float | number_float    | 0xFA\n    Double-Precision Float | number_float    | 0xFB\n\n    @warning The mapping is **incomplete** in the sense that not all CBOR\n             types can be converted to a JSON value. The following CBOR types\n             are not supported and will yield parse errors (parse_error.112):\n             - byte strings (0x40..0x5F)\n             - date/time (0xC0..0xC1)\n             - bignum (0xC2..0xC3)\n             - decimal fraction (0xC4)\n             - bigfloat (0xC5)\n             - tagged items (0xC6..0xD4, 0xD8..0xDB)\n             - expected conversions (0xD5..0xD7)\n             - simple values (0xE0..0xF3, 0xF8)\n             - undefined (0xF7)\n\n    @warning CBOR allows map keys of any type, whereas JSON only allows\n             strings as keys in object values. Therefore, CBOR maps with keys\n             other than UTF-8 strings are rejected (parse_error.113).\n\n    @note Any CBOR output created @ref to_cbor can be successfully parsed by\n          @ref from_cbor.\n\n    @param[in] i  an input in CBOR format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if unsupported features from CBOR were\n    used in the given input @a v or if the input is not valid CBOR\n    @throw parse_error.113 if a string was expected as map key, but not found\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in CBOR\n    format to a JSON value.,from_cbor}\n\n    @sa http://cbor.io\n    @sa @ref to_cbor(const basic_json&) for the analogous serialization\n    @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for the\n        related MessagePack format\n    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the\n        related UBJSON format\n\n    @since version 2.0.9; parameter @a start_index since 2.1.1; changed to\n           consume input adapters, removed start_index parameter, and added\n           @a strict parameter since 3.0.0; added @a allow_exceptions parameter\n           since 3.2.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_cbor(detail::input_adapter&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::cbor, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_cbor(detail::input_adapter&&, const bool, const bool)\n    */\n    template<typename A1, typename A2,\n             detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_cbor(A1 && a1, A2 && a2,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::cbor, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @brief create a JSON value from an input in MessagePack format\n\n    Deserializes a given input @a i to a JSON value using the MessagePack\n    serialization format.\n\n    The library maps MessagePack types to JSON value types as follows:\n\n    MessagePack type | JSON value type | first byte\n    ---------------- | --------------- | ----------\n    positive fixint  | number_unsigned | 0x00..0x7F\n    fixmap           | object          | 0x80..0x8F\n    fixarray         | array           | 0x90..0x9F\n    fixstr           | string          | 0xA0..0xBF\n    nil              | `null`          | 0xC0\n    false            | `false`         | 0xC2\n    true             | `true`          | 0xC3\n    float 32         | number_float    | 0xCA\n    float 64         | number_float    | 0xCB\n    uint 8           | number_unsigned | 0xCC\n    uint 16          | number_unsigned | 0xCD\n    uint 32          | number_unsigned | 0xCE\n    uint 64          | number_unsigned | 0xCF\n    int 8            | number_integer  | 0xD0\n    int 16           | number_integer  | 0xD1\n    int 32           | number_integer  | 0xD2\n    int 64           | number_integer  | 0xD3\n    str 8            | string          | 0xD9\n    str 16           | string          | 0xDA\n    str 32           | string          | 0xDB\n    array 16         | array           | 0xDC\n    array 32         | array           | 0xDD\n    map 16           | object          | 0xDE\n    map 32           | object          | 0xDF\n    negative fixint  | number_integer  | 0xE0-0xFF\n\n    @warning The mapping is **incomplete** in the sense that not all\n             MessagePack types can be converted to a JSON value. The following\n             MessagePack types are not supported and will yield parse errors:\n              - bin 8 - bin 32 (0xC4..0xC6)\n              - ext 8 - ext 32 (0xC7..0xC9)\n              - fixext 1 - fixext 16 (0xD4..0xD8)\n\n    @note Any MessagePack output created @ref to_msgpack can be successfully\n          parsed by @ref from_msgpack.\n\n    @param[in] i  an input in MessagePack format convertible to an input\n                  adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if unsupported features from MessagePack were\n    used in the given input @a i or if the input is not valid MessagePack\n    @throw parse_error.113 if a string was expected as map key, but not found\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    MessagePack format to a JSON value.,from_msgpack}\n\n    @sa http://msgpack.org\n    @sa @ref to_msgpack(const basic_json&) for the analogous serialization\n    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the\n        related CBOR format\n    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for\n        the related UBJSON format\n    @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for\n        the related BSON format\n\n    @since version 2.0.9; parameter @a start_index since 2.1.1; changed to\n           consume input adapters, removed start_index parameter, and added\n           @a strict parameter since 3.0.0; added @a allow_exceptions parameter\n           since 3.2.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_msgpack(detail::input_adapter&& i,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_msgpack(detail::input_adapter&&, const bool, const bool)\n    */\n    template<typename A1, typename A2,\n             detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_msgpack(A1 && a1, A2 && a2,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @brief create a JSON value from an input in UBJSON format\n\n    Deserializes a given input @a i to a JSON value using the UBJSON (Universal\n    Binary JSON) serialization format.\n\n    The library maps UBJSON types to JSON value types as follows:\n\n    UBJSON type | JSON value type                         | marker\n    ----------- | --------------------------------------- | ------\n    no-op       | *no value, next value is read*          | `N`\n    null        | `null`                                  | `Z`\n    false       | `false`                                 | `F`\n    true        | `true`                                  | `T`\n    float32     | number_float                            | `d`\n    float64     | number_float                            | `D`\n    uint8       | number_unsigned                         | `U`\n    int8        | number_integer                          | `i`\n    int16       | number_integer                          | `I`\n    int32       | number_integer                          | `l`\n    int64       | number_integer                          | `L`\n    string      | string                                  | `S`\n    char        | string                                  | `C`\n    array       | array (optimized values are supported)  | `[`\n    object      | object (optimized values are supported) | `{`\n\n    @note The mapping is **complete** in the sense that any UBJSON value can\n          be converted to a JSON value.\n\n    @param[in] i  an input in UBJSON format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if a parse error occurs\n    @throw parse_error.113 if a string could not be parsed successfully\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    UBJSON format to a JSON value.,from_ubjson}\n\n    @sa http://ubjson.org\n    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             analogous serialization\n    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the\n        related CBOR format\n    @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for\n        the related MessagePack format\n    @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for\n        the related BSON format\n\n    @since version 3.1.0; added @a allow_exceptions parameter since 3.2.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_ubjson(detail::input_adapter&& i,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_ubjson(detail::input_adapter&&, const bool, const bool)\n    */\n    template<typename A1, typename A2,\n             detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_ubjson(A1 && a1, A2 && a2,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @brief Create a JSON value from an input in BSON format\n\n    Deserializes a given input @a i to a JSON value using the BSON (Binary JSON)\n    serialization format.\n\n    The library maps BSON record types to JSON value types as follows:\n\n    BSON type       | BSON marker byte | JSON value type\n    --------------- | ---------------- | ---------------------------\n    double          | 0x01             | number_float\n    string          | 0x02             | string\n    document        | 0x03             | object\n    array           | 0x04             | array\n    binary          | 0x05             | still unsupported\n    undefined       | 0x06             | still unsupported\n    ObjectId        | 0x07             | still unsupported\n    boolean         | 0x08             | boolean\n    UTC Date-Time   | 0x09             | still unsupported\n    null            | 0x0A             | null\n    Regular Expr.   | 0x0B             | still unsupported\n    DB Pointer      | 0x0C             | still unsupported\n    JavaScript Code | 0x0D             | still unsupported\n    Symbol          | 0x0E             | still unsupported\n    JavaScript Code | 0x0F             | still unsupported\n    int32           | 0x10             | number_integer\n    Timestamp       | 0x11             | still unsupported\n    128-bit decimal float | 0x13       | still unsupported\n    Max Key         | 0x7F             | still unsupported\n    Min Key         | 0xFF             | still unsupported\n\n    @warning The mapping is **incomplete**. The unsupported mappings\n             are indicated in the table above.\n\n    @param[in] i  an input in BSON format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.114 if an unsupported BSON record type is encountered\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    BSON format to a JSON value.,from_bson}\n\n    @sa http://bsonspec.org/spec.html\n    @sa @ref to_bson(const basic_json&) for the analogous serialization\n    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the\n        related CBOR format\n    @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for\n        the related MessagePack format\n    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the\n        related UBJSON format\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bson(detail::input_adapter&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_bson(detail::input_adapter&&, const bool, const bool)\n    */\n    template<typename A1, typename A2,\n             detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bson(A1 && a1, A2 && a2,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n\n\n    /// @}\n\n    //////////////////////////\n    // JSON Pointer support //\n    //////////////////////////\n\n    /// @name JSON Pointer functions\n    /// @{\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Uses a JSON pointer to retrieve a reference to the respective JSON value.\n    No bound checking is performed. Similar to @ref operator[](const typename\n    object_t::key_type&), `null` values are created in arrays and objects if\n    necessary.\n\n    In particular:\n    - If the JSON pointer points to an object key that does not exist, it\n      is created an filled with a `null` value before a reference to it\n      is returned.\n    - If the JSON pointer points to an array index that does not exist, it\n      is created an filled with a `null` value before a reference to it\n      is returned. All indices between the current maximum and the given\n      index are also filled with `null`.\n    - The special value `-` is treated as a synonym for the index past the\n      end.\n\n    @param[in] ptr  a JSON pointer\n\n    @return reference to the element pointed to by @a ptr\n\n    @complexity Constant.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n\n    @liveexample{The behavior is shown in the example.,operatorjson_pointer}\n\n    @since version 2.0.0\n    */\n    reference operator[](const json_pointer& ptr)\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Uses a JSON pointer to retrieve a reference to the respective JSON value.\n    No bound checking is performed. The function does not change the JSON\n    value; no `null` values are created. In particular, the the special value\n    `-` yields an exception.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return const reference to the element pointed to by @a ptr\n\n    @complexity Constant.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n\n    @liveexample{The behavior is shown in the example.,operatorjson_pointer_const}\n\n    @since version 2.0.0\n    */\n    const_reference operator[](const json_pointer& ptr) const\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Returns a reference to the element at with specified JSON pointer @a ptr,\n    with bounds checking.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return reference to the element pointed to by @a ptr\n\n    @throw parse_error.106 if an array index in the passed JSON pointer @a ptr\n    begins with '0'. See example below.\n\n    @throw parse_error.109 if an array index in the passed JSON pointer @a ptr\n    is not a number. See example below.\n\n    @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr\n    is out of range. See example below.\n\n    @throw out_of_range.402 if the array index '-' is used in the passed JSON\n    pointer @a ptr. As `at` provides checked access (and no elements are\n    implicitly inserted), the index '-' is always invalid. See example below.\n\n    @throw out_of_range.403 if the JSON pointer describes a key of an object\n    which cannot be found. See example below.\n\n    @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.\n    See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 2.0.0\n\n    @liveexample{The behavior is shown in the example.,at_json_pointer}\n    */\n    reference at(const json_pointer& ptr)\n    {\n        return ptr.get_checked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Returns a const reference to the element at with specified JSON pointer @a\n    ptr, with bounds checking.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return reference to the element pointed to by @a ptr\n\n    @throw parse_error.106 if an array index in the passed JSON pointer @a ptr\n    begins with '0'. See example below.\n\n    @throw parse_error.109 if an array index in the passed JSON pointer @a ptr\n    is not a number. See example below.\n\n    @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr\n    is out of range. See example below.\n\n    @throw out_of_range.402 if the array index '-' is used in the passed JSON\n    pointer @a ptr. As `at` provides checked access (and no elements are\n    implicitly inserted), the index '-' is always invalid. See example below.\n\n    @throw out_of_range.403 if the JSON pointer describes a key of an object\n    which cannot be found. See example below.\n\n    @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.\n    See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 2.0.0\n\n    @liveexample{The behavior is shown in the example.,at_json_pointer_const}\n    */\n    const_reference at(const json_pointer& ptr) const\n    {\n        return ptr.get_checked(this);\n    }\n\n    /*!\n    @brief return flattened JSON value\n\n    The function creates a JSON object whose keys are JSON pointers (see [RFC\n    6901](https://tools.ietf.org/html/rfc6901)) and whose values are all\n    primitive. The original JSON value can be restored using the @ref\n    unflatten() function.\n\n    @return an object that maps JSON pointers to primitive values\n\n    @note Empty objects and arrays are flattened to `null` and will not be\n          reconstructed correctly by the @ref unflatten() function.\n\n    @complexity Linear in the size the JSON value.\n\n    @liveexample{The following code shows how a JSON object is flattened to an\n    object whose keys consist of JSON pointers.,flatten}\n\n    @sa @ref unflatten() for the reverse function\n\n    @since version 2.0.0\n    */\n    basic_json flatten() const\n    {\n        basic_json result(value_t::object);\n        json_pointer::flatten(\"\", *this, result);\n        return result;\n    }\n\n    /*!\n    @brief unflatten a previously flattened JSON value\n\n    The function restores the arbitrary nesting of a JSON value that has been\n    flattened before using the @ref flatten() function. The JSON value must\n    meet certain constraints:\n    1. The value must be an object.\n    2. The keys must be JSON pointers (see\n       [RFC 6901](https://tools.ietf.org/html/rfc6901))\n    3. The mapped values must be primitive JSON types.\n\n    @return the original JSON from a flattened version\n\n    @note Empty objects and arrays are flattened by @ref flatten() to `null`\n          values and can not unflattened to their original type. Apart from\n          this example, for a JSON value `j`, the following is always true:\n          `j == j.flatten().unflatten()`.\n\n    @complexity Linear in the size the JSON value.\n\n    @throw type_error.314  if value is not an object\n    @throw type_error.315  if object values are not primitive\n\n    @liveexample{The following code shows how a flattened JSON object is\n    unflattened into the original nested JSON object.,unflatten}\n\n    @sa @ref flatten() for the reverse function\n\n    @since version 2.0.0\n    */\n    basic_json unflatten() const\n    {\n        return json_pointer::unflatten(*this);\n    }\n\n    /// @}\n\n    //////////////////////////\n    // JSON Patch functions //\n    //////////////////////////\n\n    /// @name JSON Patch functions\n    /// @{\n\n    /*!\n    @brief applies a JSON patch\n\n    [JSON Patch](http://jsonpatch.com) defines a JSON document structure for\n    expressing a sequence of operations to apply to a JSON) document. With\n    this function, a JSON Patch is applied to the current JSON value by\n    executing all operations from the patch.\n\n    @param[in] json_patch  JSON patch document\n    @return patched document\n\n    @note The application of a patch is atomic: Either all operations succeed\n          and the patched document is returned or an exception is thrown. In\n          any case, the original value is not changed: the patch is applied\n          to a copy of the value.\n\n    @throw parse_error.104 if the JSON patch does not consist of an array of\n    objects\n\n    @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory\n    attributes are missing); example: `\"operation add must have member path\"`\n\n    @throw out_of_range.401 if an array index is out of range.\n\n    @throw out_of_range.403 if a JSON pointer inside the patch could not be\n    resolved successfully in the current JSON value; example: `\"key baz not\n    found\"`\n\n    @throw out_of_range.405 if JSON pointer has no parent (\"add\", \"remove\",\n    \"move\")\n\n    @throw other_error.501 if \"test\" operation was unsuccessful\n\n    @complexity Linear in the size of the JSON value and the length of the\n    JSON patch. As usually only a fraction of the JSON value is affected by\n    the patch, the complexity can usually be neglected.\n\n    @liveexample{The following code shows how a JSON patch is applied to a\n    value.,patch}\n\n    @sa @ref diff -- create a JSON patch by comparing two JSON values\n\n    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)\n    @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901)\n\n    @since version 2.0.0\n    */\n    basic_json patch(const basic_json& json_patch) const\n    {\n        // make a working copy to apply the patch to\n        basic_json result = *this;\n\n        // the valid JSON Patch operations\n        enum class patch_operations {add, remove, replace, move, copy, test, invalid};\n\n        const auto get_op = [](const std::string & op)\n        {\n            if (op == \"add\")\n            {\n                return patch_operations::add;\n            }\n            if (op == \"remove\")\n            {\n                return patch_operations::remove;\n            }\n            if (op == \"replace\")\n            {\n                return patch_operations::replace;\n            }\n            if (op == \"move\")\n            {\n                return patch_operations::move;\n            }\n            if (op == \"copy\")\n            {\n                return patch_operations::copy;\n            }\n            if (op == \"test\")\n            {\n                return patch_operations::test;\n            }\n\n            return patch_operations::invalid;\n        };\n\n        // wrapper for \"add\" operation; add value at ptr\n        const auto operation_add = [&result](json_pointer & ptr, basic_json val)\n        {\n            // adding to the root of the target document means replacing it\n            if (ptr.empty())\n            {\n                result = val;\n                return;\n            }\n\n            // make sure the top element of the pointer exists\n            json_pointer top_pointer = ptr.top();\n            if (top_pointer != ptr)\n            {\n                result.at(top_pointer);\n            }\n\n            // get reference to parent of JSON pointer ptr\n            const auto last_path = ptr.back();\n            ptr.pop_back();\n            basic_json& parent = result[ptr];\n\n            switch (parent.m_type)\n            {\n                case value_t::null:\n                case value_t::object:\n                {\n                    // use operator[] to add value\n                    parent[last_path] = val;\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    if (last_path == \"-\")\n                    {\n                        // special case: append to back\n                        parent.push_back(val);\n                    }\n                    else\n                    {\n                        const auto idx = json_pointer::array_index(last_path);\n                        if (JSON_HEDLEY_UNLIKELY(static_cast<size_type>(idx) > parent.size()))\n                        {\n                            // avoid undefined behavior\n                            JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\"));\n                        }\n\n                        // default case: insert add offset\n                        parent.insert(parent.begin() + static_cast<difference_type>(idx), val);\n                    }\n                    break;\n                }\n\n                // if there exists a parent it cannot be primitive\n                default:            // LCOV_EXCL_LINE\n                    assert(false);  // LCOV_EXCL_LINE\n            }\n        };\n\n        // wrapper for \"remove\" operation; remove value at ptr\n        const auto operation_remove = [&result](json_pointer & ptr)\n        {\n            // get reference to parent of JSON pointer ptr\n            const auto last_path = ptr.back();\n            ptr.pop_back();\n            basic_json& parent = result.at(ptr);\n\n            // remove child\n            if (parent.is_object())\n            {\n                // perform range check\n                auto it = parent.find(last_path);\n                if (JSON_HEDLEY_LIKELY(it != parent.end()))\n                {\n                    parent.erase(it);\n                }\n                else\n                {\n                    JSON_THROW(out_of_range::create(403, \"key '\" + last_path + \"' not found\"));\n                }\n            }\n            else if (parent.is_array())\n            {\n                // note erase performs range check\n                parent.erase(static_cast<size_type>(json_pointer::array_index(last_path)));\n            }\n        };\n\n        // type check: top level value must be an array\n        if (JSON_HEDLEY_UNLIKELY(not json_patch.is_array()))\n        {\n            JSON_THROW(parse_error::create(104, 0, \"JSON patch must be an array of objects\"));\n        }\n\n        // iterate and apply the operations\n        for (const auto& val : json_patch)\n        {\n            // wrapper to get a value for an operation\n            const auto get_value = [&val](const std::string & op,\n                                          const std::string & member,\n                                          bool string_type) -> basic_json &\n            {\n                // find value\n                auto it = val.m_value.object->find(member);\n\n                // context-sensitive error message\n                const auto error_msg = (op == \"op\") ? \"operation\" : \"operation '\" + op + \"'\";\n\n                // check if desired value is present\n                if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end()))\n                {\n                    JSON_THROW(parse_error::create(105, 0, error_msg + \" must have member '\" + member + \"'\"));\n                }\n\n                // check if result is of type string\n                if (JSON_HEDLEY_UNLIKELY(string_type and not it->second.is_string()))\n                {\n                    JSON_THROW(parse_error::create(105, 0, error_msg + \" must have string member '\" + member + \"'\"));\n                }\n\n                // no error: return value\n                return it->second;\n            };\n\n            // type check: every element of the array must be an object\n            if (JSON_HEDLEY_UNLIKELY(not val.is_object()))\n            {\n                JSON_THROW(parse_error::create(104, 0, \"JSON patch must be an array of objects\"));\n            }\n\n            // collect mandatory members\n            const std::string op = get_value(\"op\", \"op\", true);\n            const std::string path = get_value(op, \"path\", true);\n            json_pointer ptr(path);\n\n            switch (get_op(op))\n            {\n                case patch_operations::add:\n                {\n                    operation_add(ptr, get_value(\"add\", \"value\", false));\n                    break;\n                }\n\n                case patch_operations::remove:\n                {\n                    operation_remove(ptr);\n                    break;\n                }\n\n                case patch_operations::replace:\n                {\n                    // the \"path\" location must exist - use at()\n                    result.at(ptr) = get_value(\"replace\", \"value\", false);\n                    break;\n                }\n\n                case patch_operations::move:\n                {\n                    const std::string from_path = get_value(\"move\", \"from\", true);\n                    json_pointer from_ptr(from_path);\n\n                    // the \"from\" location must exist - use at()\n                    basic_json v = result.at(from_ptr);\n\n                    // The move operation is functionally identical to a\n                    // \"remove\" operation on the \"from\" location, followed\n                    // immediately by an \"add\" operation at the target\n                    // location with the value that was just removed.\n                    operation_remove(from_ptr);\n                    operation_add(ptr, v);\n                    break;\n                }\n\n                case patch_operations::copy:\n                {\n                    const std::string from_path = get_value(\"copy\", \"from\", true);\n                    const json_pointer from_ptr(from_path);\n\n                    // the \"from\" location must exist - use at()\n                    basic_json v = result.at(from_ptr);\n\n                    // The copy is functionally identical to an \"add\"\n                    // operation at the target location using the value\n                    // specified in the \"from\" member.\n                    operation_add(ptr, v);\n                    break;\n                }\n\n                case patch_operations::test:\n                {\n                    bool success = false;\n                    JSON_TRY\n                    {\n                        // check if \"value\" matches the one at \"path\"\n                        // the \"path\" location must exist - use at()\n                        success = (result.at(ptr) == get_value(\"test\", \"value\", false));\n                    }\n                    JSON_INTERNAL_CATCH (out_of_range&)\n                    {\n                        // ignore out of range errors: success remains false\n                    }\n\n                    // throw an exception if test fails\n                    if (JSON_HEDLEY_UNLIKELY(not success))\n                    {\n                        JSON_THROW(other_error::create(501, \"unsuccessful: \" + val.dump()));\n                    }\n\n                    break;\n                }\n\n                default:\n                {\n                    // op must be \"add\", \"remove\", \"replace\", \"move\", \"copy\", or\n                    // \"test\"\n                    JSON_THROW(parse_error::create(105, 0, \"operation value '\" + op + \"' is invalid\"));\n                }\n            }\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief creates a diff as a JSON patch\n\n    Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can\n    be changed into the value @a target by calling @ref patch function.\n\n    @invariant For two JSON values @a source and @a target, the following code\n    yields always `true`:\n    @code {.cpp}\n    source.patch(diff(source, target)) == target;\n    @endcode\n\n    @note Currently, only `remove`, `add`, and `replace` operations are\n          generated.\n\n    @param[in] source  JSON value to compare from\n    @param[in] target  JSON value to compare against\n    @param[in] path    helper value to create JSON pointers\n\n    @return a JSON patch to convert the @a source to @a target\n\n    @complexity Linear in the lengths of @a source and @a target.\n\n    @liveexample{The following code shows how a JSON patch is created as a\n    diff for two JSON values.,diff}\n\n    @sa @ref patch -- apply a JSON patch\n    @sa @ref merge_patch -- apply a JSON Merge Patch\n\n    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)\n\n    @since version 2.0.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json diff(const basic_json& source, const basic_json& target,\n                           const std::string& path = \"\")\n    {\n        // the patch\n        basic_json result(value_t::array);\n\n        // if the values are the same, return empty patch\n        if (source == target)\n        {\n            return result;\n        }\n\n        if (source.type() != target.type())\n        {\n            // different types: replace value\n            result.push_back(\n            {\n                {\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}\n            });\n            return result;\n        }\n\n        switch (source.type())\n        {\n            case value_t::array:\n            {\n                // first pass: traverse common elements\n                std::size_t i = 0;\n                while (i < source.size() and i < target.size())\n                {\n                    // recursive call to compare array values at index i\n                    auto temp_diff = diff(source[i], target[i], path + \"/\" + std::to_string(i));\n                    result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                    ++i;\n                }\n\n                // i now reached the end of at least one array\n                // in a second pass, traverse the remaining elements\n\n                // remove my remaining elements\n                const auto end_index = static_cast<difference_type>(result.size());\n                while (i < source.size())\n                {\n                    // add operations in reverse order to avoid invalid\n                    // indices\n                    result.insert(result.begin() + end_index, object(\n                    {\n                        {\"op\", \"remove\"},\n                        {\"path\", path + \"/\" + std::to_string(i)}\n                    }));\n                    ++i;\n                }\n\n                // add other remaining elements\n                while (i < target.size())\n                {\n                    result.push_back(\n                    {\n                        {\"op\", \"add\"},\n                        {\"path\", path + \"/\" + std::to_string(i)},\n                        {\"value\", target[i]}\n                    });\n                    ++i;\n                }\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                // first pass: traverse this object's elements\n                for (auto it = source.cbegin(); it != source.cend(); ++it)\n                {\n                    // escape the key name to be used in a JSON patch\n                    const auto key = json_pointer::escape(it.key());\n\n                    if (target.find(it.key()) != target.end())\n                    {\n                        // recursive call to compare object values at key it\n                        auto temp_diff = diff(it.value(), target[it.key()], path + \"/\" + key);\n                        result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                    }\n                    else\n                    {\n                        // found a key that is not in o -> remove it\n                        result.push_back(object(\n                        {\n                            {\"op\", \"remove\"}, {\"path\", path + \"/\" + key}\n                        }));\n                    }\n                }\n\n                // second pass: traverse other object's elements\n                for (auto it = target.cbegin(); it != target.cend(); ++it)\n                {\n                    if (source.find(it.key()) == source.end())\n                    {\n                        // found a key that is not in this -> add it\n                        const auto key = json_pointer::escape(it.key());\n                        result.push_back(\n                        {\n                            {\"op\", \"add\"}, {\"path\", path + \"/\" + key},\n                            {\"value\", it.value()}\n                        });\n                    }\n                }\n\n                break;\n            }\n\n            default:\n            {\n                // both primitive type: replace value\n                result.push_back(\n                {\n                    {\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}\n                });\n                break;\n            }\n        }\n\n        return result;\n    }\n\n    /// @}\n\n    ////////////////////////////////\n    // JSON Merge Patch functions //\n    ////////////////////////////////\n\n    /// @name JSON Merge Patch functions\n    /// @{\n\n    /*!\n    @brief applies a JSON Merge Patch\n\n    The merge patch format is primarily intended for use with the HTTP PATCH\n    method as a means of describing a set of modifications to a target\n    resource's content. This function applies a merge patch to the current\n    JSON value.\n\n    The function implements the following algorithm from Section 2 of\n    [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396):\n\n    ```\n    define MergePatch(Target, Patch):\n      if Patch is an Object:\n        if Target is not an Object:\n          Target = {} // Ignore the contents and set it to an empty Object\n        for each Name/Value pair in Patch:\n          if Value is null:\n            if Name exists in Target:\n              remove the Name/Value pair from Target\n          else:\n            Target[Name] = MergePatch(Target[Name], Value)\n        return Target\n      else:\n        return Patch\n    ```\n\n    Thereby, `Target` is the current object; that is, the patch is applied to\n    the current value.\n\n    @param[in] apply_patch  the patch to apply\n\n    @complexity Linear in the lengths of @a patch.\n\n    @liveexample{The following code shows how a JSON Merge Patch is applied to\n    a JSON document.,merge_patch}\n\n    @sa @ref patch -- apply a JSON patch\n    @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396)\n\n    @since version 3.0.0\n    */\n    void merge_patch(const basic_json& apply_patch)\n    {\n        if (apply_patch.is_object())\n        {\n            if (not is_object())\n            {\n                *this = object();\n            }\n            for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it)\n            {\n                if (it.value().is_null())\n                {\n                    erase(it.key());\n                }\n                else\n                {\n                    operator[](it.key()).merge_patch(it.value());\n                }\n            }\n        }\n        else\n        {\n            *this = apply_patch;\n        }\n    }\n\n    /// @}\n};\n\n/*!\n@brief user-defined to_string function for JSON values\n\nThis function implements a user-defined to_string  for JSON objects.\n\n@param[in] j  a JSON object\n@return a std::string object\n*/\n\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nstd::string to_string(const NLOHMANN_BASIC_JSON_TPL& j)\n{\n    return j.dump();\n}\n} // namespace nlohmann\n\n///////////////////////\n// nonmember support //\n///////////////////////\n\n// specialization of std::swap, and std::hash\nnamespace std\n{\n\n/// hash value for JSON objects\ntemplate<>\nstruct hash<nlohmann::json>\n{\n    /*!\n    @brief return a hash value for a JSON object\n\n    @since version 1.0.0\n    */\n    std::size_t operator()(const nlohmann::json& j) const\n    {\n        // a naive hashing via the string representation\n        const auto& h = hash<nlohmann::json::string_t>();\n        return h(j.dump());\n    }\n};\n\n/// specialization for std::less<value_t>\n/// @note: do not remove the space after '<',\n///        see https://github.com/nlohmann/json/pull/679\ntemplate<>\nstruct less<::nlohmann::detail::value_t>\n{\n    /*!\n    @brief compare two value_t enum values\n    @since version 3.0.0\n    */\n    bool operator()(nlohmann::detail::value_t lhs,\n                    nlohmann::detail::value_t rhs) const noexcept\n    {\n        return nlohmann::detail::operator<(lhs, rhs);\n    }\n};\n\n/*!\n@brief exchanges the values of two JSON objects\n\n@since version 1.0.0\n*/\ntemplate<>\ninline void swap<nlohmann::json>(nlohmann::json& j1, nlohmann::json& j2) noexcept(\n    is_nothrow_move_constructible<nlohmann::json>::value and\n    is_nothrow_move_assignable<nlohmann::json>::value\n)\n{\n    j1.swap(j2);\n}\n\n} // namespace std\n\n/*!\n@brief user-defined string literal for JSON values\n\nThis operator implements a user-defined string literal for JSON objects. It\ncan be used by adding `\"_json\"` to a string literal and returns a JSON object\nif no parse error occurred.\n\n@param[in] s  a string representation of a JSON object\n@param[in] n  the length of string @a s\n@return a JSON object\n\n@since version 1.0.0\n*/\nJSON_HEDLEY_NON_NULL(1)\ninline nlohmann::json operator \"\" _json(const char* s, std::size_t n)\n{\n    return nlohmann::json::parse(s, s + n);\n}\n\n/*!\n@brief user-defined string literal for JSON pointer\n\nThis operator implements a user-defined string literal for JSON Pointers. It\ncan be used by adding `\"_json_pointer\"` to a string literal and returns a JSON pointer\nobject if no parse error occurred.\n\n@param[in] s  a string representation of a JSON Pointer\n@param[in] n  the length of string @a s\n@return a JSON pointer object\n\n@since version 2.0.0\n*/\nJSON_HEDLEY_NON_NULL(1)\ninline nlohmann::json::json_pointer operator \"\" _json_pointer(const char* s, std::size_t n)\n{\n    return nlohmann::json::json_pointer(std::string(s, n));\n}\n\n#include <nlohmann/detail/macro_unscope.hpp>\n\n#endif  // INCLUDE_NLOHMANN_JSON_HPP_\n"
  },
  {
    "path": "thirdparty/nlohmann/json_fwd.hpp",
    "content": "#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_\n#define INCLUDE_NLOHMANN_JSON_FWD_HPP_\n\n#include <cstdint> // int64_t, uint64_t\n#include <map> // map\n#include <memory> // allocator\n#include <string> // string\n#include <vector> // vector\n\n/*!\n@brief namespace for Niels Lohmann\n@see https://github.com/nlohmann\n@since version 1.0.0\n*/\nnamespace nlohmann\n{\n/*!\n@brief default JSONSerializer template argument\n\nThis serializer ignores the template arguments and uses ADL\n([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))\nfor serialization.\n*/\ntemplate<typename T = void, typename SFINAE = void>\nstruct adl_serializer;\n\ntemplate<template<typename U, typename V, typename... Args> class ObjectType =\n         std::map,\n         template<typename U, typename... Args> class ArrayType = std::vector,\n         class StringType = std::string, class BooleanType = bool,\n         class NumberIntegerType = std::int64_t,\n         class NumberUnsignedType = std::uint64_t,\n         class NumberFloatType = double,\n         template<typename U> class AllocatorType = std::allocator,\n         template<typename T, typename SFINAE = void> class JSONSerializer =\n         adl_serializer>\nclass basic_json;\n\n/*!\n@brief JSON Pointer\n\nA JSON pointer defines a string syntax for identifying a specific value\nwithin a JSON document. It can be used with functions `at` and\n`operator[]`. Furthermore, JSON pointers are the base for JSON patches.\n\n@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)\n\n@since version 2.0.0\n*/\ntemplate<typename BasicJsonType>\nclass json_pointer;\n\n/*!\n@brief default JSON class\n\nThis type is the default specialization of the @ref basic_json class which\nuses the standard template types.\n\n@since version 1.0.0\n*/\nusing json = basic_json<>;\n}  // namespace nlohmann\n\n#endif  // INCLUDE_NLOHMANN_JSON_FWD_HPP_\n"
  },
  {
    "path": "thirdparty/nlohmann/thirdparty/hedley/hedley.hpp",
    "content": "/* Hedley - https://nemequ.github.io/hedley\n * Created by Evan Nemerson <evan@nemerson.com>\n *\n * To the extent possible under law, the author(s) have dedicated all\n * copyright and related and neighboring rights to this software to\n * the public domain worldwide. This software is distributed without\n * any warranty.\n *\n * For details, see <http://creativecommons.org/publicdomain/zero/1.0/>.\n * SPDX-License-Identifier: CC0-1.0\n */\n\n#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 11)\n#if defined(JSON_HEDLEY_VERSION)\n    #undef JSON_HEDLEY_VERSION\n#endif\n#define JSON_HEDLEY_VERSION 11\n\n#if defined(JSON_HEDLEY_STRINGIFY_EX)\n    #undef JSON_HEDLEY_STRINGIFY_EX\n#endif\n#define JSON_HEDLEY_STRINGIFY_EX(x) #x\n\n#if defined(JSON_HEDLEY_STRINGIFY)\n    #undef JSON_HEDLEY_STRINGIFY\n#endif\n#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x)\n\n#if defined(JSON_HEDLEY_CONCAT_EX)\n    #undef JSON_HEDLEY_CONCAT_EX\n#endif\n#define JSON_HEDLEY_CONCAT_EX(a,b) a##b\n\n#if defined(JSON_HEDLEY_CONCAT)\n    #undef JSON_HEDLEY_CONCAT\n#endif\n#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b)\n\n#if defined(JSON_HEDLEY_VERSION_ENCODE)\n    #undef JSON_HEDLEY_VERSION_ENCODE\n#endif\n#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision))\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR)\n    #undef JSON_HEDLEY_VERSION_DECODE_MAJOR\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000)\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR)\n    #undef JSON_HEDLEY_VERSION_DECODE_MINOR\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000)\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION)\n    #undef JSON_HEDLEY_VERSION_DECODE_REVISION\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000)\n\n#if defined(JSON_HEDLEY_GNUC_VERSION)\n    #undef JSON_HEDLEY_GNUC_VERSION\n#endif\n#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__)\n    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)\n#elif defined(__GNUC__)\n    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK)\n    #undef JSON_HEDLEY_GNUC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_GNUC_VERSION)\n    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_MSVC_VERSION)\n    #undef JSON_HEDLEY_MSVC_VERSION\n#endif\n#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100)\n#elif defined(_MSC_FULL_VER)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10)\n#elif defined(_MSC_VER)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK)\n    #undef JSON_HEDLEY_MSVC_VERSION_CHECK\n#endif\n#if !defined(_MSC_VER)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0)\n#elif defined(_MSC_VER) && (_MSC_VER >= 1400)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch)))\n#elif defined(_MSC_VER) && (_MSC_VER >= 1200)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch)))\n#else\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor)))\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_VERSION)\n    #undef JSON_HEDLEY_INTEL_VERSION\n#endif\n#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE)\n    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE)\n#elif defined(__INTEL_COMPILER)\n    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK)\n    #undef JSON_HEDLEY_INTEL_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_INTEL_VERSION)\n    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_PGI_VERSION)\n    #undef JSON_HEDLEY_PGI_VERSION\n#endif\n#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__)\n    #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__)\n#endif\n\n#if defined(JSON_HEDLEY_PGI_VERSION_CHECK)\n    #undef JSON_HEDLEY_PGI_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_PGI_VERSION)\n    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_SUNPRO_VERSION)\n    #undef JSON_HEDLEY_SUNPRO_VERSION\n#endif\n#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10)\n#elif defined(__SUNPRO_C)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf)\n#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10)\n#elif defined(__SUNPRO_CC)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf)\n#endif\n\n#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK)\n    #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_SUNPRO_VERSION)\n    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)\n    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION\n#endif\n#if defined(__EMSCRIPTEN__)\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__)\n#endif\n\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK)\n    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_ARM_VERSION)\n    #undef JSON_HEDLEY_ARM_VERSION\n#endif\n#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100)\n#elif defined(__CC_ARM) && defined(__ARMCC_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100)\n#endif\n\n#if defined(JSON_HEDLEY_ARM_VERSION_CHECK)\n    #undef JSON_HEDLEY_ARM_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_ARM_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_IBM_VERSION)\n    #undef JSON_HEDLEY_IBM_VERSION\n#endif\n#if defined(__ibmxl__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__)\n#elif defined(__xlC__) && defined(__xlC_ver__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff)\n#elif defined(__xlC__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0)\n#endif\n\n#if defined(JSON_HEDLEY_IBM_VERSION_CHECK)\n    #undef JSON_HEDLEY_IBM_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_IBM_VERSION)\n    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_VERSION)\n    #undef JSON_HEDLEY_TI_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__)\n    #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_VERSION)\n    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_CRAY_VERSION)\n    #undef JSON_HEDLEY_CRAY_VERSION\n#endif\n#if defined(_CRAYC)\n    #if defined(_RELEASE_PATCHLEVEL)\n        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL)\n    #else\n        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0)\n    #endif\n#endif\n\n#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK)\n    #undef JSON_HEDLEY_CRAY_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_CRAY_VERSION)\n    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_IAR_VERSION)\n    #undef JSON_HEDLEY_IAR_VERSION\n#endif\n#if defined(__IAR_SYSTEMS_ICC__)\n    #if __VER__ > 1000\n        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000))\n    #else\n        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(VER / 100, __VER__ % 100, 0)\n    #endif\n#endif\n\n#if defined(JSON_HEDLEY_IAR_VERSION_CHECK)\n    #undef JSON_HEDLEY_IAR_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_IAR_VERSION)\n    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TINYC_VERSION)\n    #undef JSON_HEDLEY_TINYC_VERSION\n#endif\n#if defined(__TINYC__)\n    #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100)\n#endif\n\n#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK)\n    #undef JSON_HEDLEY_TINYC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TINYC_VERSION)\n    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_DMC_VERSION)\n    #undef JSON_HEDLEY_DMC_VERSION\n#endif\n#if defined(__DMC__)\n    #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf)\n#endif\n\n#if defined(JSON_HEDLEY_DMC_VERSION_CHECK)\n    #undef JSON_HEDLEY_DMC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_DMC_VERSION)\n    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_COMPCERT_VERSION)\n    #undef JSON_HEDLEY_COMPCERT_VERSION\n#endif\n#if defined(__COMPCERT_VERSION__)\n    #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100)\n#endif\n\n#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK)\n    #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_COMPCERT_VERSION)\n    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_PELLES_VERSION)\n    #undef JSON_HEDLEY_PELLES_VERSION\n#endif\n#if defined(__POCC__)\n    #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK)\n    #undef JSON_HEDLEY_PELLES_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_PELLES_VERSION)\n    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_VERSION)\n    #undef JSON_HEDLEY_GCC_VERSION\n#endif\n#if \\\n    defined(JSON_HEDLEY_GNUC_VERSION) && \\\n    !defined(__clang__) && \\\n    !defined(JSON_HEDLEY_INTEL_VERSION) && \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_ARM_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_VERSION) && \\\n    !defined(__COMPCERT__)\n    #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION\n#endif\n\n#if defined(JSON_HEDLEY_GCC_VERSION_CHECK)\n    #undef JSON_HEDLEY_GCC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_GCC_VERSION)\n    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_ATTRIBUTE\n#endif\n#if defined(__has_attribute)\n    #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute)\n#else\n    #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE\n#endif\n#if defined(__has_attribute)\n    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE\n#endif\n#if defined(__has_attribute)\n    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE\n#endif\n#if \\\n    defined(__has_cpp_attribute) && \\\n    defined(__cplusplus) && \\\n    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0))\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS)\n    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS\n#endif\n#if !defined(__cplusplus) || !defined(__has_cpp_attribute)\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)\n#elif \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \\\n    (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0))\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute)\n#else\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE\n#endif\n#if defined(__has_cpp_attribute) && defined(__cplusplus)\n    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE\n#endif\n#if defined(__has_cpp_attribute) && defined(__cplusplus)\n    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_BUILTIN)\n    #undef JSON_HEDLEY_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN)\n    #undef JSON_HEDLEY_GNUC_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN)\n    #undef JSON_HEDLEY_GCC_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_FEATURE)\n    #undef JSON_HEDLEY_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_HAS_FEATURE(feature) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE)\n    #undef JSON_HEDLEY_GNUC_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_FEATURE)\n    #undef JSON_HEDLEY_GCC_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_EXTENSION)\n    #undef JSON_HEDLEY_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_HAS_EXTENSION(extension) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION)\n    #undef JSON_HEDLEY_GNUC_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION)\n    #undef JSON_HEDLEY_GCC_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_WARNING)\n    #undef JSON_HEDLEY_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_HAS_WARNING(warning) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_WARNING)\n    #undef JSON_HEDLEY_GNUC_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_WARNING)\n    #undef JSON_HEDLEY_GCC_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for\n   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_\n#endif\n#if defined(__cplusplus) && JSON_HEDLEY_HAS_WARNING(\"-Wc++98-compat\")\n#  define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++98-compat\\\"\") \\\n    xpr \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#else\n#  define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x\n#endif\n\n#if \\\n    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \\\n    defined(__clang__) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \\\n    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \\\n    (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR))\n    #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value)\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_PRAGMA(value) __pragma(value)\n#else\n    #define JSON_HEDLEY_PRAGMA(value)\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH)\n    #undef JSON_HEDLEY_DIAGNOSTIC_PUSH\n#endif\n#if defined(JSON_HEDLEY_DIAGNOSTIC_POP)\n    #undef JSON_HEDLEY_DIAGNOSTIC_POP\n#endif\n#if defined(__clang__)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"clang diagnostic push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"clang diagnostic pop\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"warning(push)\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"warning(pop)\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"GCC diagnostic push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"GCC diagnostic pop\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push))\n    #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop))\n#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"pop\")\n#elif JSON_HEDLEY_TI_VERSION_CHECK(8,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"diag_push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"diag_pop\")\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"warning(push)\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"warning(pop)\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH\n    #define JSON_HEDLEY_DIAGNOSTIC_POP\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wdeprecated-declarations\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"clang diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"warning(disable:1478 1786)\")\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1215,1444\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"GCC diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996))\n#elif JSON_HEDLEY_TI_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1291,1718\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"error_messages(off,symdeprecated,symdeprecated2)\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress=Pe1444,Pe1215\")\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"warn(disable:2241)\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"clang diagnostic ignored \\\"-Wunknown-pragmas\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"warning(disable:161)\")\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 1675\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"GCC diagnostic ignored \\\"-Wunknown-pragmas\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068))\n#elif JSON_HEDLEY_TI_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 163\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress=Pe161\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-attributes\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"clang diagnostic ignored \\\"-Wunknown-attributes\\\"\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"GCC diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"warning(disable:1292)\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030))\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1097\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"error_messages(off,attrskipunsup)\")\n#elif JSON_HEDLEY_TI_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1173\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wcast-qual\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"clang diagnostic ignored \\\"-Wcast-qual\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"warning(disable:2203 2331)\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"GCC diagnostic ignored \\\"-Wcast-qual\\\"\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#endif\n\n#if defined(JSON_HEDLEY_DEPRECATED)\n    #undef JSON_HEDLEY_DEPRECATED\n#endif\n#if defined(JSON_HEDLEY_DEPRECATED_FOR)\n    #undef JSON_HEDLEY_DEPRECATED_FOR\n#endif\n#if defined(__cplusplus) && (__cplusplus >= 201402L)\n    #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated(\"Since \" #since)]])\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated(\"Since \" #since \"; use \" #replacement)]])\n#elif \\\n    JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(8,3,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__(\"Since \" #since)))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__(\"Since \" #since \"; use \" #replacement)))\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \\\n    (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))\n    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__))\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated(\"Since \" # since))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated(\"Since \" #since \"; use \" #replacement))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated)\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated)\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DEPRECATED(since) _Pragma(\"deprecated\")\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma(\"deprecated\")\n#else\n    #define JSON_HEDLEY_DEPRECATED(since)\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement)\n#endif\n\n#if defined(JSON_HEDLEY_UNAVAILABLE)\n    #undef JSON_HEDLEY_UNAVAILABLE\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__(\"Not available until \" #available_since)))\n#else\n    #define JSON_HEDLEY_UNAVAILABLE(available_since)\n#endif\n\n#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT)\n    #undef JSON_HEDLEY_WARN_UNUSED_RESULT\n#endif\n#if defined(__cplusplus) && (__cplusplus >= 201703L)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \\\n    (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))\n#elif defined(_Check_return_) /* SAL */\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_\n#else\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT\n#endif\n\n#if defined(JSON_HEDLEY_SENTINEL)\n    #undef JSON_HEDLEY_SENTINEL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0)\n    #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position)))\n#else\n    #define JSON_HEDLEY_SENTINEL(position)\n#endif\n\n#if defined(JSON_HEDLEY_NO_RETURN)\n    #undef JSON_HEDLEY_NO_RETURN\n#endif\n#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_NO_RETURN __noreturn\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))\n#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L\n    #define JSON_HEDLEY_NO_RETURN _Noreturn\n#elif defined(__cplusplus) && (__cplusplus >= 201103L)\n    #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]])\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(18,0,0) || \\\n    (JSON_HEDLEY_TI_VERSION_CHECK(17,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))\n    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_NO_RETURN _Pragma(\"does_not_return\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0)\n    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)\n#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_NO_RETURN _Pragma(\"FUNC_NEVER_RETURNS;\")\n#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)\n    #define JSON_HEDLEY_NO_RETURN __attribute((noreturn))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)\n    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)\n#else\n    #define JSON_HEDLEY_NO_RETURN\n#endif\n\n#if defined(JSON_HEDLEY_NO_ESCAPE)\n    #undef JSON_HEDLEY_NO_ESCAPE\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape)\n    #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__))\n#else\n    #define JSON_HEDLEY_NO_ESCAPE\n#endif\n\n#if defined(JSON_HEDLEY_UNREACHABLE)\n    #undef JSON_HEDLEY_UNREACHABLE\n#endif\n#if defined(JSON_HEDLEY_UNREACHABLE_RETURN)\n    #undef JSON_HEDLEY_UNREACHABLE_RETURN\n#endif\n#if \\\n    (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5)\n    #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable()\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0)\n    #define JSON_HEDLEY_UNREACHABLE() __assume(0)\n#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0)\n    #if defined(__cplusplus)\n        #define JSON_HEDLEY_UNREACHABLE() std::_nassert(0)\n    #else\n        #define JSON_HEDLEY_UNREACHABLE() _nassert(0)\n    #endif\n    #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return value\n#elif defined(EXIT_FAILURE)\n    #define JSON_HEDLEY_UNREACHABLE() abort()\n#else\n    #define JSON_HEDLEY_UNREACHABLE()\n    #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return value\n#endif\n#if !defined(JSON_HEDLEY_UNREACHABLE_RETURN)\n    #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE()\n#endif\n\n#if defined(JSON_HEDLEY_ASSUME)\n    #undef JSON_HEDLEY_ASSUME\n#endif\n#if \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_ASSUME(expr) __assume(expr)\n#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume)\n    #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr)\n#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0)\n    #if defined(__cplusplus)\n        #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr)\n    #else\n        #define JSON_HEDLEY_ASSUME(expr) _nassert(expr)\n    #endif\n#elif \\\n    (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && !defined(JSON_HEDLEY_ARM_VERSION)) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5)\n    #define JSON_HEDLEY_ASSUME(expr) ((void) ((expr) ? 1 : (__builtin_unreachable(), 1)))\n#else\n    #define JSON_HEDLEY_ASSUME(expr) ((void) (expr))\n#endif\n\nJSON_HEDLEY_DIAGNOSTIC_PUSH\n#if JSON_HEDLEY_HAS_WARNING(\"-Wpedantic\")\n    #pragma clang diagnostic ignored \"-Wpedantic\"\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wc++98-compat-pedantic\") && defined(__cplusplus)\n    #pragma clang diagnostic ignored \"-Wc++98-compat-pedantic\"\n#endif\n#if JSON_HEDLEY_GCC_HAS_WARNING(\"-Wvariadic-macros\",4,0,0)\n    #if defined(__clang__)\n        #pragma clang diagnostic ignored \"-Wvariadic-macros\"\n    #elif defined(JSON_HEDLEY_GCC_VERSION)\n        #pragma GCC diagnostic ignored \"-Wvariadic-macros\"\n    #endif\n#endif\n#if defined(JSON_HEDLEY_NON_NULL)\n    #undef JSON_HEDLEY_NON_NULL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)\n    #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__)))\n#else\n    #define JSON_HEDLEY_NON_NULL(...)\n#endif\nJSON_HEDLEY_DIAGNOSTIC_POP\n\n#if defined(JSON_HEDLEY_PRINTF_FORMAT)\n    #undef JSON_HEDLEY_PRINTF_FORMAT\n#endif\n#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check)))\n#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check)))\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(format) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \\\n    (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check)))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check))\n#else\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check)\n#endif\n\n#if defined(JSON_HEDLEY_CONSTEXPR)\n    #undef JSON_HEDLEY_CONSTEXPR\n#endif\n#if defined(__cplusplus)\n    #if __cplusplus >= 201103L\n        #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr)\n    #endif\n#endif\n#if !defined(JSON_HEDLEY_CONSTEXPR)\n    #define JSON_HEDLEY_CONSTEXPR\n#endif\n\n#if defined(JSON_HEDLEY_PREDICT)\n    #undef JSON_HEDLEY_PREDICT\n#endif\n#if defined(JSON_HEDLEY_LIKELY)\n    #undef JSON_HEDLEY_LIKELY\n#endif\n#if defined(JSON_HEDLEY_UNLIKELY)\n    #undef JSON_HEDLEY_UNLIKELY\n#endif\n#if defined(JSON_HEDLEY_UNPREDICTABLE)\n    #undef JSON_HEDLEY_UNPREDICTABLE\n#endif\n#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable)\n    #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable(!!(expr))\n#endif\n#if \\\n  JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0)\n#  define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(expr, value, probability)\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1, probability)\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0, probability)\n#  define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1)\n#  define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0)\n#if !defined(JSON_HEDLEY_BUILTIN_UNPREDICTABLE)\n    #define JSON_HEDLEY_BUILTIN_UNPREDICTABLE(expr) __builtin_expect_with_probability(!!(expr), 1, 0.5)\n#endif\n#elif \\\n  JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(6,1,0) || \\\n  JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27)\n#  define JSON_HEDLEY_PREDICT(expr, expected, probability) \\\n    (((probability) >= 0.9) ? __builtin_expect(!!(expr), (expected)) : (((void) (expected)), !!(expr)))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \\\n    (__extension__ ({ \\\n        JSON_HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \\\n        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \\\n    }))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \\\n    (__extension__ ({ \\\n        JSON_HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \\\n        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \\\n    }))\n#  define JSON_HEDLEY_LIKELY(expr)   __builtin_expect(!!(expr), 1)\n#  define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0)\n#else\n#  define JSON_HEDLEY_PREDICT(expr, expected, probability) (((void) (expected)), !!(expr))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr))\n#  define JSON_HEDLEY_LIKELY(expr) (!!(expr))\n#  define JSON_HEDLEY_UNLIKELY(expr) (!!(expr))\n#endif\n#if !defined(JSON_HEDLEY_UNPREDICTABLE)\n    #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5)\n#endif\n\n#if defined(JSON_HEDLEY_MALLOC)\n    #undef JSON_HEDLEY_MALLOC\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \\\n    (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))\n    #define JSON_HEDLEY_MALLOC __attribute__((__malloc__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_MALLOC _Pragma(\"returns_new_memory\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0)\n    #define JSON_HEDLEY_MALLOC __declspec(restrict)\n#else\n    #define JSON_HEDLEY_MALLOC\n#endif\n\n#if defined(JSON_HEDLEY_PURE)\n    #undef JSON_HEDLEY_PURE\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \\\n    (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_PURE __attribute__((__pure__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_PURE _Pragma(\"does_not_write_global_data\")\n#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_PURE _Pragma(\"FUNC_IS_PURE;\")\n#else\n    #define JSON_HEDLEY_PURE\n#endif\n\n#if defined(JSON_HEDLEY_CONST)\n    #undef JSON_HEDLEY_CONST\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(const) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \\\n    (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_CONST __attribute__((__const__))\n#elif \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_CONST _Pragma(\"no_side_effect\")\n#else\n    #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE\n#endif\n\n#if defined(JSON_HEDLEY_RESTRICT)\n    #undef JSON_HEDLEY_RESTRICT\n#endif\n#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus)\n    #define JSON_HEDLEY_RESTRICT restrict\n#elif \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \\\n    defined(__clang__)\n    #define JSON_HEDLEY_RESTRICT __restrict\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus)\n    #define JSON_HEDLEY_RESTRICT _Restrict\n#else\n    #define JSON_HEDLEY_RESTRICT\n#endif\n\n#if defined(JSON_HEDLEY_INLINE)\n    #undef JSON_HEDLEY_INLINE\n#endif\n#if \\\n    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \\\n    (defined(__cplusplus) && (__cplusplus >= 199711L))\n    #define JSON_HEDLEY_INLINE inline\n#elif \\\n    defined(JSON_HEDLEY_GCC_VERSION) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0)\n    #define JSON_HEDLEY_INLINE __inline__\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_INLINE __inline\n#else\n    #define JSON_HEDLEY_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_ALWAYS_INLINE)\n    #undef JSON_HEDLEY_ALWAYS_INLINE\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \\\n    (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))\n    #define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0)\n    #define JSON_HEDLEY_ALWAYS_INLINE __forceinline\n#elif JSON_HEDLEY_TI_VERSION_CHECK(7,0,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_ALWAYS_INLINE _Pragma(\"FUNC_ALWAYS_INLINE;\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_ALWAYS_INLINE _Pragma(\"inline=forced\")\n#else\n    #define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_NEVER_INLINE)\n    #undef JSON_HEDLEY_NEVER_INLINE\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \\\n    (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))\n    #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__))\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0)\n    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"noinline\")\n#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"FUNC_CANNOT_INLINE;\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"inline=never\")\n#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)\n    #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)\n    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)\n#else\n    #define JSON_HEDLEY_NEVER_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_PRIVATE)\n    #undef JSON_HEDLEY_PRIVATE\n#endif\n#if defined(JSON_HEDLEY_PUBLIC)\n    #undef JSON_HEDLEY_PUBLIC\n#endif\n#if defined(JSON_HEDLEY_IMPORT)\n    #undef JSON_HEDLEY_IMPORT\n#endif\n#if defined(_WIN32) || defined(__CYGWIN__)\n    #define JSON_HEDLEY_PRIVATE\n    #define JSON_HEDLEY_PUBLIC   __declspec(dllexport)\n    #define JSON_HEDLEY_IMPORT   __declspec(dllimport)\n#else\n    #if \\\n        JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \\\n        JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n        JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n        JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n        JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n        JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n        JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \\\n        (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_EABI__) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))\n        #define JSON_HEDLEY_PRIVATE __attribute__((__visibility__(\"hidden\")))\n        #define JSON_HEDLEY_PUBLIC  __attribute__((__visibility__(\"default\")))\n    #else\n        #define JSON_HEDLEY_PRIVATE\n        #define JSON_HEDLEY_PUBLIC\n    #endif\n    #define JSON_HEDLEY_IMPORT    extern\n#endif\n\n#if defined(JSON_HEDLEY_NO_THROW)\n    #undef JSON_HEDLEY_NO_THROW\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)\n    #define JSON_HEDLEY_NO_THROW __declspec(nothrow)\n#else\n    #define JSON_HEDLEY_NO_THROW\n#endif\n\n#if defined(JSON_HEDLEY_FALL_THROUGH)\n    #undef JSON_HEDLEY_FALL_THROUGH\n#endif\n#if JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(fallthrough,7,0,0) && !defined(JSON_HEDLEY_PGI_VERSION)\n    #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__))\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough)\n    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]])\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough)\n    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]])\n#elif defined(__fallthrough) /* SAL */\n    #define JSON_HEDLEY_FALL_THROUGH __fallthrough\n#else\n    #define JSON_HEDLEY_FALL_THROUGH\n#endif\n\n#if defined(JSON_HEDLEY_RETURNS_NON_NULL)\n    #undef JSON_HEDLEY_RETURNS_NON_NULL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)\n    #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__))\n#elif defined(_Ret_notnull_) /* SAL */\n    #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_\n#else\n    #define JSON_HEDLEY_RETURNS_NON_NULL\n#endif\n\n#if defined(JSON_HEDLEY_ARRAY_PARAM)\n    #undef JSON_HEDLEY_ARRAY_PARAM\n#endif\n#if \\\n    defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \\\n    !defined(__STDC_NO_VLA__) && \\\n    !defined(__cplusplus) && \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_TINYC_VERSION)\n    #define JSON_HEDLEY_ARRAY_PARAM(name) (name)\n#else\n    #define JSON_HEDLEY_ARRAY_PARAM(name)\n#endif\n\n#if defined(JSON_HEDLEY_IS_CONSTANT)\n    #undef JSON_HEDLEY_IS_CONSTANT\n#endif\n#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR)\n    #undef JSON_HEDLEY_REQUIRE_CONSTEXPR\n#endif\n/* JSON_HEDLEY_IS_CONSTEXPR_ is for\n   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */\n#if defined(JSON_HEDLEY_IS_CONSTEXPR_)\n    #undef JSON_HEDLEY_IS_CONSTEXPR_\n#endif\n#if \\\n    JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(6,1,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0)\n    #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr)\n#endif\n#if !defined(__cplusplus)\n#  if \\\n       JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \\\n       JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n       JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n       JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n       JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \\\n       JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \\\n       JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24)\n#if defined(__INTPTR_TYPE__)\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*)\n#else\n    #include <stdint.h>\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*)\n#endif\n#  elif \\\n       (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && !defined(JSON_HEDLEY_SUNPRO_VERSION) && !defined(JSON_HEDLEY_PGI_VERSION)) || \\\n       JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) || \\\n       JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \\\n       JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \\\n       JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \\\n       JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0)\n#if defined(__INTPTR_TYPE__)\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0)\n#else\n    #include <stdint.h>\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0)\n#endif\n#  elif \\\n       defined(JSON_HEDLEY_GCC_VERSION) || \\\n       defined(JSON_HEDLEY_INTEL_VERSION) || \\\n       defined(JSON_HEDLEY_TINYC_VERSION) || \\\n       defined(JSON_HEDLEY_TI_VERSION) || \\\n       defined(__clang__)\n#    define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \\\n        sizeof(void) != \\\n        sizeof(*( \\\n                  1 ? \\\n                  ((void*) ((expr) * 0L) ) : \\\n((struct { char v[sizeof(void) * 2]; } *) 1) \\\n                ) \\\n              ) \\\n                                            )\n#  endif\n#endif\n#if defined(JSON_HEDLEY_IS_CONSTEXPR_)\n    #if !defined(JSON_HEDLEY_IS_CONSTANT)\n        #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr)\n    #endif\n    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1))\n#else\n    #if !defined(JSON_HEDLEY_IS_CONSTANT)\n        #define JSON_HEDLEY_IS_CONSTANT(expr) (0)\n    #endif\n    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr)\n#endif\n\n#if defined(JSON_HEDLEY_BEGIN_C_DECLS)\n    #undef JSON_HEDLEY_BEGIN_C_DECLS\n#endif\n#if defined(JSON_HEDLEY_END_C_DECLS)\n    #undef JSON_HEDLEY_END_C_DECLS\n#endif\n#if defined(JSON_HEDLEY_C_DECL)\n    #undef JSON_HEDLEY_C_DECL\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_BEGIN_C_DECLS extern \"C\" {\n    #define JSON_HEDLEY_END_C_DECLS }\n    #define JSON_HEDLEY_C_DECL extern \"C\"\n#else\n    #define JSON_HEDLEY_BEGIN_C_DECLS\n    #define JSON_HEDLEY_END_C_DECLS\n    #define JSON_HEDLEY_C_DECL\n#endif\n\n#if defined(JSON_HEDLEY_STATIC_ASSERT)\n    #undef JSON_HEDLEY_STATIC_ASSERT\n#endif\n#if \\\n  !defined(__cplusplus) && ( \\\n      (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \\\n      JSON_HEDLEY_HAS_FEATURE(c_static_assert) || \\\n      JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \\\n      JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n      defined(_Static_assert) \\\n    )\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message)\n#elif \\\n  (defined(__cplusplus) && (__cplusplus >= 201103L)) || \\\n  JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \\\n  (defined(__cplusplus) && JSON_HEDLEY_TI_VERSION_CHECK(8,3,0))\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message))\n#else\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message)\n#endif\n\n#if defined(JSON_HEDLEY_CONST_CAST)\n    #undef JSON_HEDLEY_CONST_CAST\n#endif\n#if defined(__cplusplus)\n#  define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast<T>(expr))\n#elif \\\n  JSON_HEDLEY_HAS_WARNING(\"-Wcast-qual\") || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \\\n        JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n        JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \\\n        ((T) (expr)); \\\n        JSON_HEDLEY_DIAGNOSTIC_POP \\\n    }))\n#else\n#  define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_REINTERPRET_CAST)\n    #undef JSON_HEDLEY_REINTERPRET_CAST\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast<T>(expr))\n#else\n    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (*((T*) &(expr)))\n#endif\n\n#if defined(JSON_HEDLEY_STATIC_CAST)\n    #undef JSON_HEDLEY_STATIC_CAST\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast<T>(expr))\n#else\n    #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_CPP_CAST)\n    #undef JSON_HEDLEY_CPP_CAST\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_CPP_CAST(T, expr) static_cast<T>(expr)\n#else\n    #define JSON_HEDLEY_CPP_CAST(T, expr) (expr)\n#endif\n\n#if defined(JSON_HEDLEY_NULL)\n    #undef JSON_HEDLEY_NULL\n#endif\n#if defined(__cplusplus)\n    #if __cplusplus >= 201103L\n        #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr)\n    #elif defined(NULL)\n        #define JSON_HEDLEY_NULL NULL\n    #else\n        #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0)\n    #endif\n#elif defined(NULL)\n    #define JSON_HEDLEY_NULL NULL\n#else\n    #define JSON_HEDLEY_NULL ((void*) 0)\n#endif\n\n#if defined(JSON_HEDLEY_MESSAGE)\n    #undef JSON_HEDLEY_MESSAGE\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n#  define JSON_HEDLEY_MESSAGE(msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \\\n    JSON_HEDLEY_PRAGMA(message msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#elif \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg)\n#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg)\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#else\n#  define JSON_HEDLEY_MESSAGE(msg)\n#endif\n\n#if defined(JSON_HEDLEY_WARNING)\n    #undef JSON_HEDLEY_WARNING\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n#  define JSON_HEDLEY_WARNING(msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \\\n    JSON_HEDLEY_PRAGMA(clang warning msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#elif \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \\\n  JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0)\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg)\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#else\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg)\n#endif\n\n#if defined(JSON_HEDLEY_REQUIRE)\n    #undef JSON_HEDLEY_REQUIRE\n#endif\n#if defined(JSON_HEDLEY_REQUIRE_MSG)\n    #undef JSON_HEDLEY_REQUIRE_MSG\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wgcc-compat\")\n#    define JSON_HEDLEY_REQUIRE(expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wgcc-compat\\\"\") \\\n    __attribute__((diagnose_if(!(expr), #expr, \"error\"))) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wgcc-compat\\\"\") \\\n    __attribute__((diagnose_if(!(expr), msg, \"error\"))) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#  else\n#    define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, \"error\")))\n#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, \"error\")))\n#  endif\n#else\n#  define JSON_HEDLEY_REQUIRE(expr)\n#  define JSON_HEDLEY_REQUIRE_MSG(expr,msg)\n#endif\n\n#if defined(JSON_HEDLEY_FLAGS)\n    #undef JSON_HEDLEY_FLAGS\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum)\n    #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__))\n#endif\n\n#if defined(JSON_HEDLEY_FLAGS_CAST)\n    #undef JSON_HEDLEY_FLAGS_CAST\n#endif\n#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0)\n#  define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \\\n        JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n        _Pragma(\"warning(disable:188)\") \\\n        ((T) (expr)); \\\n        JSON_HEDLEY_DIAGNOSTIC_POP \\\n    }))\n#else\n#  define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr)\n#endif\n\n#if defined(JSON_HEDLEY_EMPTY_BASES)\n    #undef JSON_HEDLEY_EMPTY_BASES\n#endif\n#if JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)\n    #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases)\n#else\n    #define JSON_HEDLEY_EMPTY_BASES\n#endif\n\n/* Remaining macros are deprecated. */\n\n#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK)\n    #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK\n#endif\n#if defined(__clang__)\n    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0)\n#else\n    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN)\n    #undef JSON_HEDLEY_CLANG_HAS_BUILTIN\n#endif\n#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE)\n    #undef JSON_HEDLEY_CLANG_HAS_FEATURE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION)\n    #undef JSON_HEDLEY_CLANG_HAS_EXTENSION\n#endif\n#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_WARNING)\n    #undef JSON_HEDLEY_CLANG_HAS_WARNING\n#endif\n#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning)\n\n#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */\n"
  },
  {
    "path": "thirdparty/nlohmann/thirdparty/hedley/hedley_undef.hpp",
    "content": "#undef JSON_HEDLEY_ALWAYS_INLINE\n#undef JSON_HEDLEY_ARM_VERSION\n#undef JSON_HEDLEY_ARM_VERSION_CHECK\n#undef JSON_HEDLEY_ARRAY_PARAM\n#undef JSON_HEDLEY_ASSUME\n#undef JSON_HEDLEY_BEGIN_C_DECLS\n#undef JSON_HEDLEY_C_DECL\n#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_BUILTIN\n#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_EXTENSION\n#undef JSON_HEDLEY_CLANG_HAS_FEATURE\n#undef JSON_HEDLEY_CLANG_HAS_WARNING\n#undef JSON_HEDLEY_COMPCERT_VERSION\n#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK\n#undef JSON_HEDLEY_CONCAT\n#undef JSON_HEDLEY_CONCAT_EX\n#undef JSON_HEDLEY_CONST\n#undef JSON_HEDLEY_CONST_CAST\n#undef JSON_HEDLEY_CONSTEXPR\n#undef JSON_HEDLEY_CPP_CAST\n#undef JSON_HEDLEY_CRAY_VERSION\n#undef JSON_HEDLEY_CRAY_VERSION_CHECK\n#undef JSON_HEDLEY_DEPRECATED\n#undef JSON_HEDLEY_DEPRECATED_FOR\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#undef JSON_HEDLEY_DIAGNOSTIC_POP\n#undef JSON_HEDLEY_DIAGNOSTIC_PUSH\n#undef JSON_HEDLEY_DMC_VERSION\n#undef JSON_HEDLEY_DMC_VERSION_CHECK\n#undef JSON_HEDLEY_EMPTY_BASES\n#undef JSON_HEDLEY_EMSCRIPTEN_VERSION\n#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK\n#undef JSON_HEDLEY_END_C_DECLS\n#undef JSON_HEDLEY_FALL_THROUGH\n#undef JSON_HEDLEY_FLAGS\n#undef JSON_HEDLEY_FLAGS_CAST\n#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_BUILTIN\n#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_EXTENSION\n#undef JSON_HEDLEY_GCC_HAS_FEATURE\n#undef JSON_HEDLEY_GCC_HAS_WARNING\n#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK\n#undef JSON_HEDLEY_GCC_VERSION\n#undef JSON_HEDLEY_GCC_VERSION_CHECK\n#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_BUILTIN\n#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_EXTENSION\n#undef JSON_HEDLEY_GNUC_HAS_FEATURE\n#undef JSON_HEDLEY_GNUC_HAS_WARNING\n#undef JSON_HEDLEY_GNUC_VERSION\n#undef JSON_HEDLEY_GNUC_VERSION_CHECK\n#undef JSON_HEDLEY_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_BUILTIN\n#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS\n#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_EXTENSION\n#undef JSON_HEDLEY_HAS_FEATURE\n#undef JSON_HEDLEY_HAS_WARNING\n#undef JSON_HEDLEY_IAR_VERSION\n#undef JSON_HEDLEY_IAR_VERSION_CHECK\n#undef JSON_HEDLEY_IBM_VERSION\n#undef JSON_HEDLEY_IBM_VERSION_CHECK\n#undef JSON_HEDLEY_IMPORT\n#undef JSON_HEDLEY_INLINE\n#undef JSON_HEDLEY_INTEL_VERSION\n#undef JSON_HEDLEY_INTEL_VERSION_CHECK\n#undef JSON_HEDLEY_IS_CONSTANT\n#undef JSON_HEDLEY_IS_CONSTEXPR_\n#undef JSON_HEDLEY_LIKELY\n#undef JSON_HEDLEY_MALLOC\n#undef JSON_HEDLEY_MESSAGE\n#undef JSON_HEDLEY_MSVC_VERSION\n#undef JSON_HEDLEY_MSVC_VERSION_CHECK\n#undef JSON_HEDLEY_NEVER_INLINE\n#undef JSON_HEDLEY_NO_ESCAPE\n#undef JSON_HEDLEY_NON_NULL\n#undef JSON_HEDLEY_NO_RETURN\n#undef JSON_HEDLEY_NO_THROW\n#undef JSON_HEDLEY_NULL\n#undef JSON_HEDLEY_PELLES_VERSION\n#undef JSON_HEDLEY_PELLES_VERSION_CHECK\n#undef JSON_HEDLEY_PGI_VERSION\n#undef JSON_HEDLEY_PGI_VERSION_CHECK\n#undef JSON_HEDLEY_PREDICT\n#undef JSON_HEDLEY_PRINTF_FORMAT\n#undef JSON_HEDLEY_PRIVATE\n#undef JSON_HEDLEY_PUBLIC\n#undef JSON_HEDLEY_PURE\n#undef JSON_HEDLEY_REINTERPRET_CAST\n#undef JSON_HEDLEY_REQUIRE\n#undef JSON_HEDLEY_REQUIRE_CONSTEXPR\n#undef JSON_HEDLEY_REQUIRE_MSG\n#undef JSON_HEDLEY_RESTRICT\n#undef JSON_HEDLEY_RETURNS_NON_NULL\n#undef JSON_HEDLEY_SENTINEL\n#undef JSON_HEDLEY_STATIC_ASSERT\n#undef JSON_HEDLEY_STATIC_CAST\n#undef JSON_HEDLEY_STRINGIFY\n#undef JSON_HEDLEY_STRINGIFY_EX\n#undef JSON_HEDLEY_SUNPRO_VERSION\n#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK\n#undef JSON_HEDLEY_TINYC_VERSION\n#undef JSON_HEDLEY_TINYC_VERSION_CHECK\n#undef JSON_HEDLEY_TI_VERSION\n#undef JSON_HEDLEY_TI_VERSION_CHECK\n#undef JSON_HEDLEY_UNAVAILABLE\n#undef JSON_HEDLEY_UNLIKELY\n#undef JSON_HEDLEY_UNPREDICTABLE\n#undef JSON_HEDLEY_UNREACHABLE\n#undef JSON_HEDLEY_UNREACHABLE_RETURN\n#undef JSON_HEDLEY_VERSION\n#undef JSON_HEDLEY_VERSION_DECODE_MAJOR\n#undef JSON_HEDLEY_VERSION_DECODE_MINOR\n#undef JSON_HEDLEY_VERSION_DECODE_REVISION\n#undef JSON_HEDLEY_VERSION_ENCODE\n#undef JSON_HEDLEY_WARNING\n#undef JSON_HEDLEY_WARN_UNUSED_RESULT\n"
  },
  {
    "path": "thirdparty/pdo_oci/CREDITS",
    "content": "Oracle (OCI) driver for PDO\nWez Furlong\n"
  },
  {
    "path": "thirdparty/pdo_oci/README.md",
    "content": "This code was copied from php-8.3.28. Starting from PHP 8.4,\nthis extension has been removed and migrated to https://pecl.php.net/package/pdo_oci.\n\nHowever, the latest version available on PECL is 1.1.0,\nwhich was released on August 21, 2024, whereas on May 6, 2025,\nphp-8.3.28 made modifications to pdo_oci\n(see commit: https://github.com/php/php-src/commit/dcf9d8f812abb3854c802e4b831d82f9d7e5c26f).\n\n- PECL Package: https://pecl.php.net/package/pdo_oci\n- GitHub Repository: https://github.com/php/pecl-database-pdo_oci\n\n\n## Merge pdo_oci into Swoole\n\n```shell\ngit clone https://github.com/php/pecl-database-pdo_oci.git\nmeld soft/php/pecl-database-pdo_oci swoole-src/thirdparty/pdo_oci\n```\n"
  },
  {
    "path": "thirdparty/pdo_oci/oci_driver.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_ORACLE_HOOK\n#include \"php_swoole_oracle.h\"\n\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"pdo/php_pdo.h\"\n#include \"Zend/zend_exceptions.h\"\n\nstatic inline ub4 pdo_oci_sanitize_prefetch(long prefetch);\n\nstatic void pdo_oci_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */\n{\n    pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data;\n    pdo_oci_error_info *einfo;\n\n    einfo = &H->einfo;\n\n    if (stmt) {\n        pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data;\n\n        if (S->einfo.errmsg) {\n            einfo = &S->einfo;\n        }\n    }\n\n    if (einfo->errcode) {\n        add_next_index_long(info, einfo->errcode);\n        add_next_index_string(info, einfo->errmsg);\n    }\n}\n/* }}} */\n\nub4 _oci_error(OCIError *err,\n               pdo_dbh_t *dbh,\n               pdo_stmt_t *stmt,\n               char *what,\n               sword status,\n               int isinit,\n               const char *file,\n               int line) /* {{{ */\n{\n    text errbuf[1024] = \"<<Unknown>>\";\n    char tmp_buf[2048];\n    pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data;\n    pdo_oci_error_info *einfo;\n    pdo_oci_stmt *S = NULL;\n    pdo_error_type *pdo_err = &dbh->error_code;\n\n    if (stmt) {\n        S = (pdo_oci_stmt *) stmt->driver_data;\n        einfo = &S->einfo;\n        pdo_err = &stmt->error_code;\n    } else {\n        einfo = &H->einfo;\n    }\n\n    if (einfo->errmsg) {\n        pefree(einfo->errmsg, dbh->is_persistent);\n    }\n\n    einfo->errmsg = NULL;\n    einfo->errcode = 0;\n    einfo->file = file;\n    einfo->line = line;\n\n    if (isinit) { /* Initialization error */\n        strcpy(*pdo_err, \"HY000\");\n        slprintf(tmp_buf, sizeof(tmp_buf), \"%s (%s:%d)\", what, file, line);\n        einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);\n    } else {\n        switch (status) {\n        case OCI_SUCCESS:\n            strcpy(*pdo_err, \"00000\");\n            break;\n        case OCI_ERROR:\n            OCIErrorGet(err, (ub4) 1, NULL, &einfo->errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR);\n            slprintf(tmp_buf, sizeof(tmp_buf), \"%s: %s (%s:%d)\", what, errbuf, file, line);\n            einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);\n            break;\n        case OCI_SUCCESS_WITH_INFO:\n            OCIErrorGet(err, (ub4) 1, NULL, &einfo->errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR);\n            slprintf(tmp_buf, sizeof(tmp_buf), \"%s: OCI_SUCCESS_WITH_INFO: %s (%s:%d)\", what, errbuf, file, line);\n            einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);\n            break;\n        case OCI_NEED_DATA:\n            slprintf(tmp_buf, sizeof(tmp_buf), \"%s: OCI_NEED_DATA (%s:%d)\", what, file, line);\n            einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);\n            break;\n        case OCI_NO_DATA:\n            slprintf(tmp_buf, sizeof(tmp_buf), \"%s: OCI_NO_DATA (%s:%d)\", what, file, line);\n            einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);\n            break;\n        case OCI_INVALID_HANDLE:\n            slprintf(tmp_buf, sizeof(tmp_buf), \"%s: OCI_INVALID_HANDLE (%s:%d)\", what, file, line);\n            einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);\n            break;\n        case OCI_STILL_EXECUTING:\n            slprintf(tmp_buf, sizeof(tmp_buf), \"%s: OCI_STILL_EXECUTING (%s:%d)\", what, file, line);\n            einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);\n            break;\n        case OCI_CONTINUE:\n            slprintf(tmp_buf, sizeof(tmp_buf), \"%s: OCI_CONTINUE (%s:%d)\", what, file, line);\n            einfo->errmsg = pestrdup(tmp_buf, dbh->is_persistent);\n            break;\n        }\n\n        if (einfo->errcode) {\n            switch (einfo->errcode) {\n            case 1013: /* user requested cancel of current operation */\n                zend_bailout();\n                break;\n\n            case 12154: /* ORA-12154: TNS:could not resolve service name */\n                strcpy(*pdo_err, \"42S02\");\n                break;\n\n            case 22: /* ORA-00022: invalid session id */\n            case 378:\n            case 602:\n            case 603:\n            case 604:\n            case 609:\n            case 1012: /* ORA-01012: */\n            case 1033:\n            case 1041:\n            case 1043:\n            case 1089:\n            case 1090:\n            case 1092:\n            case 3113: /* ORA-03133: end of file on communication channel */\n            case 3114:\n            case 3122:\n            case 3135:\n            case 12153:\n            case 27146:\n            case 28511:\n                /* consider the connection closed */\n                dbh->is_closed = 1;\n                H->attached = 0;\n                strcpy(*pdo_err, \"01002\"); /* FIXME */\n                break;\n\n            default:\n                strcpy(*pdo_err, \"HY000\");\n            }\n        }\n\n        if (stmt) {\n            /* always propagate the error code back up to the dbh,\n             * so that we can catch the error information when execute\n             * is called via query.  See Bug #33707 */\n            if (H->einfo.errmsg) {\n                pefree(H->einfo.errmsg, dbh->is_persistent);\n            }\n            H->einfo = *einfo;\n            H->einfo.errmsg = einfo->errmsg ? pestrdup(einfo->errmsg, dbh->is_persistent) : NULL;\n            strcpy(dbh->error_code, stmt->error_code);\n        }\n    }\n\n    /* little mini hack so that we can use this code from the dbh ctor */\n    if (!dbh->methods && status != OCI_SUCCESS_WITH_INFO) {\n        zend_throw_exception_ex(php_pdo_get_exception(), einfo->errcode, \"SQLSTATE[%s]: %s\", *pdo_err, einfo->errmsg);\n    }\n\n    return einfo->errcode;\n}\n/* }}} */\n\nstatic void oci_handle_closer(pdo_dbh_t *dbh) /* {{{ */\n{\n    pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data;\n\n    if (H->svc) {\n        /* rollback any outstanding work */\n        OCITransRollback(H->svc, H->err, 0);\n    }\n\n    if (H->session) {\n        OCIHandleFree(H->session, OCI_HTYPE_SESSION);\n        H->session = NULL;\n    }\n\n    if (H->svc) {\n        OCIHandleFree(H->svc, OCI_HTYPE_SVCCTX);\n        H->svc = NULL;\n    }\n\n    if (H->server && H->attached) {\n        H->last_err = OCIServerDetach(H->server, H->err, OCI_DEFAULT);\n        if (H->last_err) {\n            oci_drv_error(\"OCIServerDetach\");\n        }\n        H->attached = 0;\n    }\n\n    if (H->server) {\n        OCIHandleFree(H->server, OCI_HTYPE_SERVER);\n        H->server = NULL;\n    }\n\n    if (H->err) {\n        OCIHandleFree(H->err, OCI_HTYPE_ERROR);\n        H->err = NULL;\n    }\n\n    if (H->charset && H->env) {\n        OCIHandleFree(H->env, OCI_HTYPE_ENV);\n        H->env = NULL;\n    }\n\n    if (H->einfo.errmsg) {\n        pefree(H->einfo.errmsg, dbh->is_persistent);\n        H->einfo.errmsg = NULL;\n    }\n\n    pefree(H, dbh->is_persistent);\n}\n/* }}} */\n\nstatic bool oci_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) /* {{{ */\n{\n    pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data;\n    pdo_oci_stmt *S = ecalloc(1, sizeof(*S));\n    ub4 prefetch;\n    zend_string *nsql = NULL;\n    int ret;\n\n#ifdef HAVE_OCISTMTFETCH2\n    S->exec_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL\n                       ? OCI_STMT_SCROLLABLE_READONLY\n                       : OCI_DEFAULT;\n#else\n    S->exec_type = OCI_DEFAULT;\n#endif\n\n    S->H = H;\n    stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED;\n    ret = pdo_parse_params(stmt, sql, &nsql);\n\n    if (ret == 1) {\n        /* query was re-written */\n        sql = nsql;\n    } else if (ret == -1) {\n        /* couldn't grok it */\n        strcpy(dbh->error_code, stmt->error_code);\n        efree(S);\n        return false;\n    }\n\n    /* create an OCI statement handle */\n    OCIHandleAlloc(H->env, (dvoid *) &S->stmt, OCI_HTYPE_STMT, 0, NULL);\n\n    /* and our own private error handle */\n    OCIHandleAlloc(H->env, (dvoid *) &S->err, OCI_HTYPE_ERROR, 0, NULL);\n\n    if (ZSTR_LEN(sql) != 0) {\n        H->last_err =\n            OCIStmtPrepare(S->stmt, H->err, (text *) ZSTR_VAL(sql), (ub4) ZSTR_LEN(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);\n        if (nsql) {\n            zend_string_release(nsql);\n            nsql = NULL;\n        }\n        if (H->last_err) {\n            H->last_err = oci_drv_error(\"OCIStmtPrepare\");\n            OCIHandleFree(S->stmt, OCI_HTYPE_STMT);\n            OCIHandleFree(S->err, OCI_HTYPE_ERROR);\n            efree(S);\n            return false;\n        }\n    }\n\n    prefetch = H->prefetch; /* Note 0 is allowed so in future REF CURSORs can be used & then passed with no row loss*/\n    H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_ROWS, H->err);\n    if (!H->last_err) {\n        prefetch *= PDO_OCI_PREFETCH_ROWSIZE;\n        H->last_err = OCIAttrSet(S->stmt, OCI_HTYPE_STMT, &prefetch, 0, OCI_ATTR_PREFETCH_MEMORY, H->err);\n    }\n\n    stmt->driver_data = S;\n    stmt->methods = &swoole_oci_stmt_methods;\n    if (nsql) {\n        zend_string_release(nsql);\n        nsql = NULL;\n    }\n\n    return true;\n}\n/* }}} */\n\nstatic zend_long oci_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) /* {{{ */\n{\n    pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data;\n    OCIStmt *stmt;\n    ub2 stmt_type;\n    ub4 rowcount;\n    int ret = -1;\n\n    OCIHandleAlloc(H->env, (dvoid *) &stmt, OCI_HTYPE_STMT, 0, NULL);\n\n    H->last_err =\n        OCIStmtPrepare(stmt, H->err, (text *) ZSTR_VAL(sql), (ub4) ZSTR_LEN(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);\n    if (H->last_err) {\n        H->last_err = oci_drv_error(\"OCIStmtPrepare\");\n        OCIHandleFree(stmt, OCI_HTYPE_STMT);\n        return -1;\n    }\n\n    H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &stmt_type, 0, OCI_ATTR_STMT_TYPE, H->err);\n\n    if (stmt_type == OCI_STMT_SELECT) {\n        /* invalid usage; cancel it */\n        OCIHandleFree(stmt, OCI_HTYPE_STMT);\n        php_error_docref(NULL, E_WARNING, \"issuing a SELECT query here is invalid\");\n        return -1;\n    }\n\n    /* now we are good to go */\n    H->last_err = OCIStmtExecute(H->svc,\n                                 stmt,\n                                 H->err,\n                                 1,\n                                 0,\n                                 NULL,\n                                 NULL,\n                                 (dbh->auto_commit && !dbh->in_txn) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT);\n\n    sword last_err = H->last_err;\n\n    if (last_err) {\n        H->last_err = oci_drv_error(\"OCIStmtExecute\");\n    }\n\n    if (!last_err || last_err == OCI_SUCCESS_WITH_INFO) {\n        /* return the number of affected rows */\n        H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, H->err);\n        ret = rowcount;\n    }\n\n    OCIHandleFree(stmt, OCI_HTYPE_STMT);\n\n    return ret;\n}\n/* }}} */\n\nstatic zend_string *oci_handle_quoter(pdo_dbh_t *dbh,\n                                      const zend_string *unquoted,\n                                      enum pdo_param_type paramtype) /* {{{ */\n{\n    int qcount = 0;\n    char const *cu, *l, *r;\n    char *c, *quoted;\n    size_t quotedlen;\n    zend_string *quoted_str;\n\n    if (ZSTR_LEN(unquoted) == 0) {\n        return ZSTR_INIT_LITERAL(\"''\", 0);\n    }\n\n    /* count single quotes */\n    for (cu = ZSTR_VAL(unquoted); (cu = strchr(cu, '\\'')); qcount++, cu++)\n        ; /* empty loop */\n\n    quotedlen = ZSTR_LEN(unquoted) + qcount + 2;\n    quoted = c = emalloc(quotedlen + 1);\n    *c++ = '\\'';\n\n    /* foreach (chunk that ends in a quote) */\n    for (l = ZSTR_VAL(unquoted); (r = strchr(l, '\\'')); l = r + 1) {\n        strncpy(c, l, r - l + 1);\n        c += (r - l + 1);\n        *c++ = '\\''; /* add second quote */\n    }\n\n    /* Copy remainder and add enclosing quote */\n    strncpy(c, l, quotedlen - (c - quoted) - 1);\n    quoted[quotedlen - 1] = '\\'';\n    quoted[quotedlen] = '\\0';\n\n    quoted_str = zend_string_init(quoted, quotedlen, 0);\n    efree(quoted);\n    return quoted_str;\n}\n/* }}} */\n\nstatic bool oci_handle_begin(pdo_dbh_t *dbh) /* {{{ */\n{\n    /* with Oracle, there is nothing special to be done */\n    return true;\n}\n/* }}} */\n\nstatic bool oci_handle_commit(pdo_dbh_t *dbh) /* {{{ */\n{\n    pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data;\n\n    H->last_err = OCITransCommit(H->svc, H->err, 0);\n\n    if (H->last_err) {\n        H->last_err = oci_drv_error(\"OCITransCommit\");\n        return false;\n    }\n    return true;\n}\n/* }}} */\n\nstatic bool oci_handle_rollback(pdo_dbh_t *dbh) /* {{{ */\n{\n    pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data;\n\n    H->last_err = OCITransRollback(H->svc, H->err, 0);\n\n    if (H->last_err) {\n        H->last_err = oci_drv_error(\"OCITransRollback\");\n        return false;\n    }\n    return true;\n}\n/* }}} */\n\nstatic bool oci_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) /* {{{ */\n{\n    zend_long lval;\n    pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data;\n\n    switch (attr) {\n    case PDO_ATTR_AUTOCOMMIT: {\n        bool bval;\n        if (!pdo_get_bool_param(&bval, val)) {\n            return false;\n        }\n\n        if (dbh->in_txn) {\n            /* Assume they want to commit whatever is outstanding */\n            H->last_err = OCITransCommit(H->svc, H->err, 0);\n\n            if (H->last_err) {\n                H->last_err = oci_drv_error(\"OCITransCommit\");\n                return false;\n            }\n            dbh->in_txn = false;\n        }\n\n        dbh->auto_commit = (unsigned int) bval;\n        return true;\n    }\n    case PDO_ATTR_PREFETCH: {\n        if (!pdo_get_long_param(&lval, val)) {\n            return false;\n        }\n\n        H->prefetch = pdo_oci_sanitize_prefetch(lval);\n        return true;\n    }\n    case PDO_OCI_ATTR_ACTION: {\n#if (OCI_MAJOR_VERSION >= 10)\n        zend_string *action = zval_try_get_string(val);\n        if (UNEXPECTED(!action)) {\n            return false;\n        }\n\n        H->last_err = OCIAttrSet(\n            H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(action), (ub4) ZSTR_LEN(action), OCI_ATTR_ACTION, H->err);\n        if (H->last_err) {\n            oci_drv_error(\"OCIAttrSet: OCI_ATTR_ACTION\");\n            return false;\n        }\n        return true;\n#else\n        oci_drv_error(\"Unsupported attribute type\");\n        return false;\n#endif\n    }\n    case PDO_OCI_ATTR_CLIENT_INFO: {\n#if (OCI_MAJOR_VERSION >= 10)\n        zend_string *client_info = zval_try_get_string(val);\n        if (UNEXPECTED(!client_info)) {\n            return false;\n        }\n\n        H->last_err = OCIAttrSet(H->session,\n                                 OCI_HTYPE_SESSION,\n                                 (dvoid *) ZSTR_VAL(client_info),\n                                 (ub4) ZSTR_LEN(client_info),\n                                 OCI_ATTR_CLIENT_INFO,\n                                 H->err);\n        if (H->last_err) {\n            oci_drv_error(\"OCIAttrSet: OCI_ATTR_CLIENT_INFO\");\n            return false;\n        }\n        return true;\n#else\n        oci_drv_error(\"Unsupported attribute type\");\n        return false;\n#endif\n    }\n    case PDO_OCI_ATTR_CLIENT_IDENTIFIER: {\n#if (OCI_MAJOR_VERSION >= 10)\n        zend_string *identifier = zval_try_get_string(val);\n        if (UNEXPECTED(!identifier)) {\n            return false;\n        }\n\n        H->last_err = OCIAttrSet(H->session,\n                                 OCI_HTYPE_SESSION,\n                                 (dvoid *) ZSTR_VAL(identifier),\n                                 (ub4) ZSTR_LEN(identifier),\n                                 OCI_ATTR_CLIENT_IDENTIFIER,\n                                 H->err);\n        if (H->last_err) {\n            oci_drv_error(\"OCIAttrSet: OCI_ATTR_CLIENT_IDENTIFIER\");\n            return false;\n        }\n        return true;\n#else\n        oci_drv_error(\"Unsupported attribute type\");\n        return false;\n#endif\n    }\n    case PDO_OCI_ATTR_MODULE: {\n#if (OCI_MAJOR_VERSION >= 10)\n        zend_string *module = zval_try_get_string(val);\n        if (UNEXPECTED(!module)) {\n            return false;\n        }\n\n        H->last_err = OCIAttrSet(\n            H->session, OCI_HTYPE_SESSION, (dvoid *) ZSTR_VAL(module), (ub4) ZSTR_LEN(module), OCI_ATTR_MODULE, H->err);\n        if (H->last_err) {\n            oci_drv_error(\"OCIAttrSet: OCI_ATTR_MODULE\");\n            return false;\n        }\n        return true;\n#else\n        oci_drv_error(\"Unsupported attribute type\");\n        return false;\n#endif\n    }\n    case PDO_OCI_ATTR_CALL_TIMEOUT: {\n#if (OCI_MAJOR_VERSION >= 18)\n        if (!pdo_get_long_param(&lval, val)) {\n            return false;\n        }\n        ub4 timeout = (ub4) lval;\n\n        H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, (dvoid *) &timeout, (ub4) 0, OCI_ATTR_CALL_TIMEOUT, H->err);\n        if (H->last_err) {\n            oci_drv_error(\"OCIAttrSet: OCI_ATTR_CALL_TIMEOUT\");\n            return false;\n        }\n        return true;\n#else\n        oci_drv_error(\"Unsupported attribute type\");\n        return false;\n#endif\n    }\n    default:\n        return false;\n    }\n}\n/* }}} */\n\nstatic int oci_handle_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) /* {{{ */\n{\n    pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data;\n\n    switch (attr) {\n    case PDO_ATTR_SERVER_VERSION:\n    case PDO_ATTR_SERVER_INFO: {\n        text infostr[512];\n        char verstr[15];\n        ub4 vernum;\n\n        if (OCIServerRelease(H->svc, H->err, infostr, (ub4) sizeof(infostr), (ub1) OCI_HTYPE_SVCCTX, &vernum)) {\n            ZVAL_STRING(return_value, \"<<Unknown>>\");\n        } else {\n            if (attr == PDO_ATTR_SERVER_INFO) {\n                ZVAL_STRING(return_value, (char *) infostr);\n            } else {\n                slprintf(verstr,\n                         sizeof(verstr),\n                         \"%d.%d.%d.%d.%d\",\n                         (int) ((vernum >> 24) & 0xFF), /* version number */\n                         (int) ((vernum >> 20) & 0x0F), /* release number*/\n                         (int) ((vernum >> 12) & 0xFF), /* update number */\n                         (int) ((vernum >> 8) & 0x0F),  /* port release number */\n                         (int) ((vernum >> 0) & 0xFF)); /* port update number */\n\n                ZVAL_STRING(return_value, verstr);\n            }\n        }\n        return TRUE;\n    }\n\n    case PDO_ATTR_CLIENT_VERSION: {\n#if OCI_MAJOR_VERSION > 10 || (OCI_MAJOR_VERSION == 10 && OCI_MINOR_VERSION >= 2)\n        /* Run time client version */\n        sword major, minor, update, patch, port_update;\n        char verstr[15];\n\n        OCIClientVersion(&major, &minor, &update, &patch, &port_update);\n        slprintf(verstr, sizeof(verstr), \"%d.%d.%d.%d.%d\", major, minor, update, patch, port_update);\n        ZVAL_STRING(return_value, verstr);\n#elif defined(SWOOLE_PDO_OCI_CLIENT_VERSION)\n        /* Compile time client version */\n        ZVAL_STRING(return_value, SWOOLE_PDO_OCI_CLIENT_VERSION);\n#else\n        return FALSE;\n\n#endif /* Check for OCIClientVersion() support */\n\n        return TRUE;\n    }\n\n    case PDO_ATTR_AUTOCOMMIT:\n        ZVAL_BOOL(return_value, dbh->auto_commit);\n        return TRUE;\n\n    case PDO_ATTR_PREFETCH:\n        ZVAL_LONG(return_value, H->prefetch);\n        return TRUE;\n    case PDO_OCI_ATTR_CALL_TIMEOUT: {\n#if (OCI_MAJOR_VERSION >= 18)\n        ub4 timeout;\n\n        H->last_err = OCIAttrGet(H->svc, OCI_HTYPE_SVCCTX, (dvoid *) &timeout, NULL, OCI_ATTR_CALL_TIMEOUT, H->err);\n        if (H->last_err) {\n            oci_drv_error(\"OCIAttrGet: OCI_ATTR_CALL_TIMEOUT\");\n            return FALSE;\n        }\n\n        ZVAL_LONG(return_value, (zend_long) timeout);\n        return TRUE;\n#else\n        oci_drv_error(\"Unsupported attribute type\");\n        return FALSE;\n#endif\n    }\n    default:\n        return FALSE;\n    }\n    return FALSE;\n}\n/* }}} */\n\nstatic zend_result pdo_oci_check_liveness(pdo_dbh_t *dbh) /* {{{ */\n{\n    pdo_oci_db_handle *H = (pdo_oci_db_handle *) dbh->driver_data;\n    sb4 error_code = 0;\n#if (!((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))))\n    char version[256];\n#endif\n\n    /* TODO move attached check to PDO level */\n    if (H->attached == 0) {\n        return FAILURE;\n    }\n    /* TODO add persistent_timeout check at PDO level */\n\n    /* Use OCIPing instead of OCIServerVersion. If OCIPing returns ORA-1010 (invalid OCI operation)\n     * such as from Pre-10.1 servers, the error is still from the server and we would have\n     * successfully performed a roundtrip and validated the connection. Use OCIServerVersion for\n     * Pre-10.2 clients\n     */\n#if ((OCI_MAJOR_VERSION > 10) ||                                                                                       \\\n     ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) /* OCIPing available 10.2 onwards */\n    H->last_err = OCIPing(H->svc, H->err, OCI_DEFAULT);\n#else\n    /* use good old OCIServerVersion() */\n    H->last_err = OCIServerVersion(H->svc, H->err, (text *) version, sizeof(version), OCI_HTYPE_SVCCTX);\n#endif\n    if (H->last_err == OCI_SUCCESS) {\n        return SUCCESS;\n    }\n\n    OCIErrorGet(H->err, (ub4) 1, NULL, &error_code, NULL, 0, OCI_HTYPE_ERROR);\n\n    if (error_code == 1010) {\n        return SUCCESS;\n    }\n    return FAILURE;\n}\n/* }}} */\n\nstatic const struct pdo_dbh_methods oci_methods = {\n    oci_handle_closer,\n    oci_handle_preparer,\n    oci_handle_doer,\n    oci_handle_quoter,\n    oci_handle_begin,\n    oci_handle_commit,\n    oci_handle_rollback,\n    oci_handle_set_attribute,\n    NULL, /* last_id not supported */\n    pdo_oci_fetch_error_func,\n    oci_handle_get_attribute,\n    pdo_oci_check_liveness, /* check_liveness */\n    NULL,                   /* get_driver_methods */\n    NULL,                   /* request_shutdown */\n    NULL,                   /* in transaction, use PDO's internal tracking mechanism */\n    NULL                    /* get_gc */\n};\n\nstatic int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */\n{\n    pdo_oci_db_handle *H;\n    int i, ret = 0;\n    struct pdo_data_src_parser vars[] = {\n        {\"charset\", NULL, 0}, {\"dbname\", \"\", 0}, {\"user\", NULL, 0}, {\"password\", NULL, 0}};\n\n    php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 4);\n\n    H = pecalloc(1, sizeof(*H), dbh->is_persistent);\n    dbh->driver_data = H;\n\n    dbh->skip_param_evt = 1 << PDO_PARAM_EVT_FETCH_PRE | 1 << PDO_PARAM_EVT_FETCH_POST | 1 << PDO_PARAM_EVT_NORMALIZE;\n\n    H->prefetch = PDO_OCI_PREFETCH_DEFAULT;\n\n    /* allocate an environment */\n#ifdef HAVE_OCIENVNLSCREATE\n    if (vars[0].optval) {\n        H->charset = OCINlsCharSetNameToId(swoole_pdo_oci_Env, (const oratext *) vars[0].optval);\n        if (!H->charset) {\n            oci_init_error(\"OCINlsCharSetNameToId: unknown character set name\");\n            goto cleanup;\n        } else {\n            if (OCIEnvNlsCreate(&H->env, SWOOLE_PDO_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, H->charset, H->charset) !=\n                OCI_SUCCESS) {\n                oci_init_error(\"OCIEnvNlsCreate: Check the character set is valid and that PHP has access to Oracle \"\n                               \"libraries and NLS data\");\n                goto cleanup;\n            }\n        }\n    }\n#endif\n    if (H->env == NULL) {\n        /* use the global environment */\n        H->env = swoole_pdo_oci_Env;\n    }\n\n    /* something to hold errors */\n    OCIHandleAlloc(H->env, (dvoid **) &H->err, OCI_HTYPE_ERROR, 0, NULL);\n\n    /* handle for the server */\n    OCIHandleAlloc(H->env, (dvoid **) &H->server, OCI_HTYPE_SERVER, 0, NULL);\n\n    H->last_err =\n        OCIServerAttach(H->server, H->err, (text *) vars[1].optval, (sb4) strlen(vars[1].optval), OCI_DEFAULT);\n\n    if (H->last_err) {\n        oci_drv_error(\"pdo_oci_handle_factory\");\n        goto cleanup;\n    }\n\n    H->attached = 1;\n\n    /* create a service context */\n    H->last_err = OCIHandleAlloc(H->env, (dvoid **) &H->svc, OCI_HTYPE_SVCCTX, 0, NULL);\n    if (H->last_err) {\n        oci_drv_error(\"OCIHandleAlloc: OCI_HTYPE_SVCCTX\");\n        goto cleanup;\n    }\n\n    H->last_err = OCIHandleAlloc(H->env, (dvoid **) &H->session, OCI_HTYPE_SESSION, 0, NULL);\n    if (H->last_err) {\n        oci_drv_error(\"OCIHandleAlloc: OCI_HTYPE_SESSION\");\n        goto cleanup;\n    }\n\n    /* set server handle into service handle */\n    H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->server, 0, OCI_ATTR_SERVER, H->err);\n    if (H->last_err) {\n        oci_drv_error(\"OCIAttrSet: OCI_ATTR_SERVER\");\n        goto cleanup;\n    }\n\n    /* username */\n    if (!dbh->username && vars[2].optval) {\n        dbh->username = pestrdup(vars[2].optval, dbh->is_persistent);\n    }\n\n    if (dbh->username) {\n        H->last_err = OCIAttrSet(\n            H->session, OCI_HTYPE_SESSION, dbh->username, (ub4) strlen(dbh->username), OCI_ATTR_USERNAME, H->err);\n        if (H->last_err) {\n            oci_drv_error(\"OCIAttrSet: OCI_ATTR_USERNAME\");\n            goto cleanup;\n        }\n    }\n\n    /* password */\n    if (!dbh->password && vars[3].optval) {\n        dbh->password = pestrdup(vars[3].optval, dbh->is_persistent);\n    }\n\n    if (dbh->password) {\n        H->last_err = OCIAttrSet(\n            H->session, OCI_HTYPE_SESSION, dbh->password, (ub4) strlen(dbh->password), OCI_ATTR_PASSWORD, H->err);\n        if (H->last_err) {\n            oci_drv_error(\"OCIAttrSet: OCI_ATTR_PASSWORD\");\n            goto cleanup;\n        }\n    }\n\n    /* Now fire up the session */\n    H->last_err = OCISessionBegin(H->svc, H->err, H->session, OCI_CRED_RDBMS, OCI_DEFAULT);\n    if (H->last_err) {\n        oci_drv_error(\"OCISessionBegin\");\n        /* OCISessionBegin returns OCI_SUCCESS_WITH_INFO when\n         * user's password has expired, but is still usable.\n         */\n        if (H->last_err != OCI_SUCCESS_WITH_INFO) {\n            goto cleanup;\n        }\n    }\n\n    /* set the server handle into service handle */\n    H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->session, 0, OCI_ATTR_SESSION, H->err);\n    if (H->last_err) {\n        oci_drv_error(\"OCIAttrSet: OCI_ATTR_SESSION\");\n        goto cleanup;\n    }\n\n    /* Get max character width */\n    H->last_err = OCINlsNumericInfoGet(H->env, H->err, &H->max_char_width, OCI_NLS_CHARSET_MAXBYTESZ);\n    if (H->last_err) {\n        oci_drv_error(\"OCINlsNumericInfoGet: OCI_NLS_CHARSET_MAXBYTESZ\");\n        goto cleanup;\n    }\n\n    dbh->methods = &oci_methods;\n    dbh->alloc_own_columns = 1;\n    dbh->native_case = PDO_CASE_UPPER;\n\n    ret = 1;\n\ncleanup:\n    for (i = 0; i < sizeof(vars) / sizeof(vars[0]); i++) {\n        if (vars[i].freeme) {\n            efree(vars[i].optval);\n        }\n    }\n\n    if (!ret) {\n        oci_handle_closer(dbh);\n    }\n\n    return ret;\n}\n/* }}} */\n\nconst pdo_driver_t swoole_pdo_oci_driver = {PDO_DRIVER_HEADER(oci), pdo_oci_handle_factory};\n\nstatic inline ub4 pdo_oci_sanitize_prefetch(long prefetch) /* {{{ */\n{\n    if (prefetch < 0) {\n        prefetch = 0;\n    } else if (prefetch > UB4MAXVAL / PDO_OCI_PREFETCH_ROWSIZE) {\n        prefetch = PDO_OCI_PREFETCH_DEFAULT;\n    }\n    return ((ub4) prefetch);\n}\n/* }}} */\n"
  },
  {
    "path": "thirdparty/pdo_oci/oci_statement.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_ORACLE_HOOK\n#include \"php_swoole_oracle.h\"\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"pdo/php_pdo.h\"\n#include \"Zend/zend_extensions.h\"\n\n#define PDO_OCI_LOBMAXSIZE (4294967295UL) /* OCI_LOBMAXSIZE */\n\n#define STMT_CALL(name, params)                                                                                        \\\n    do {                                                                                                               \\\n        S->last_err = name params;                                                                                     \\\n        S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name, S->last_err, FALSE, __FILE__, __LINE__);              \\\n        if (S->last_err) {                                                                                             \\\n            return 0;                                                                                                  \\\n        }                                                                                                              \\\n    } while (0)\n\n#define STMT_CALL_MSG(name, msg, params)                                                                               \\\n    do {                                                                                                               \\\n        S->last_err = name params;                                                                                     \\\n        S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name \": \" #msg, S->last_err, FALSE, __FILE__, __LINE__);    \\\n        if (S->last_err) {                                                                                             \\\n            return 0;                                                                                                  \\\n        }                                                                                                              \\\n    } while (0)\n\nstatic php_stream *oci_create_lob_stream(pdo_stmt_t *stmt, OCILobLocator *lob);\n\n#define OCI_TEMPLOB_CLOSE(envhp, svchp, errhp, lob)                                                                    \\\n    do {                                                                                                               \\\n        boolean isTempLOB;                                                                                             \\\n        OCILobIsTemporary(envhp, errhp, lob, &isTempLOB);                                                              \\\n        if (isTempLOB) OCILobFreeTemporary(svchp, errhp, lob);                                                         \\\n    } while (0)\n\nstatic int oci_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */\n{\n    pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data;\n    HashTable *BC = stmt->bound_columns;\n    HashTable *BP = stmt->bound_params;\n\n    int i;\n\n    if (S->stmt) {\n        /* cancel server side resources for the statement if we didn't\n         * fetch it all */\n        OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT);\n\n        /* free the handle */\n        OCIHandleFree(S->stmt, OCI_HTYPE_STMT);\n        S->stmt = NULL;\n    }\n    if (S->err) {\n        OCIHandleFree(S->err, OCI_HTYPE_ERROR);\n        S->err = NULL;\n    }\n\n    /* need to ensure these go away now */\n    if (BC) {\n        zend_hash_destroy(BC);\n        FREE_HASHTABLE(stmt->bound_columns);\n        stmt->bound_columns = NULL;\n    }\n\n    if (BP) {\n        zend_hash_destroy(BP);\n        FREE_HASHTABLE(stmt->bound_params);\n        stmt->bound_params = NULL;\n    }\n\n    if (S->einfo.errmsg) {\n        pefree(S->einfo.errmsg, stmt->dbh->is_persistent);\n        S->einfo.errmsg = NULL;\n    }\n\n#if PHP_API_VERSION < 20240925\n    bool server_obj_usable =\n        !Z_ISUNDEF(stmt->database_object_handle) &&\n        IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)]) &&\n        !(OBJ_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED);\n#else\n    bool server_obj_usable = php_pdo_stmt_valid_db_obj_handle(stmt);\n#endif\n\n    if (S->cols) {\n        for (i = 0; i < stmt->column_count; i++) {\n            if (S->cols[i].data) {\n                switch (S->cols[i].dtype) {\n                case SQLT_BLOB:\n                case SQLT_CLOB:\n                    if (server_obj_usable) {\n                        OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, (OCILobLocator *) S->cols[i].data);\n                    }\n                    OCIDescriptorFree(S->cols[i].data, OCI_DTYPE_LOB);\n                    break;\n                default:\n                    efree(S->cols[i].data);\n                }\n            }\n        }\n        efree(S->cols);\n        S->cols = NULL;\n    }\n    efree(S);\n\n    stmt->driver_data = NULL;\n\n    return 1;\n} /* }}} */\n\nstatic int oci_stmt_execute(pdo_stmt_t *stmt) /* {{{ */\n{\n    pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data;\n    ub4 rowcount;\n    b4 mode;\n\n    if (!S->stmt_type) {\n        STMT_CALL_MSG(\n            OCIAttrGet, \"OCI_ATTR_STMT_TYPE\", (S->stmt, OCI_HTYPE_STMT, &S->stmt_type, 0, OCI_ATTR_STMT_TYPE, S->err));\n    }\n\n    if (stmt->executed) {\n        /* ensure that we cancel the cursor from a previous fetch */\n        OCIStmtFetch(S->stmt, S->err, 0, OCI_FETCH_NEXT, OCI_DEFAULT);\n    }\n\n#ifdef OCI_STMT_SCROLLABLE_READONLY /* needed for oci8 ? */\n    if (S->exec_type == OCI_STMT_SCROLLABLE_READONLY) {\n        mode = OCI_STMT_SCROLLABLE_READONLY;\n    } else\n#endif\n        if (stmt->dbh->auto_commit && !stmt->dbh->in_txn) {\n        mode = OCI_COMMIT_ON_SUCCESS;\n    } else {\n        mode = OCI_DEFAULT;\n    }\n\n    STMT_CALL(\n        OCIStmtExecute,\n        (S->H->svc, S->stmt, S->err, (S->stmt_type == OCI_STMT_SELECT && !S->have_blobs) ? 0 : 1, 0, NULL, NULL, mode));\n\n    if (!stmt->executed) {\n        ub4 colcount;\n        /* do first-time-only definition of bind/mapping stuff */\n\n        /* how many columns do we have ? */\n        STMT_CALL_MSG(\n            OCIAttrGet, \"ATTR_PARAM_COUNT\", (S->stmt, OCI_HTYPE_STMT, &colcount, 0, OCI_ATTR_PARAM_COUNT, S->err));\n\n        stmt->column_count = (int) colcount;\n\n        if (S->cols) {\n            int i;\n            for (i = 0; i < stmt->column_count; i++) {\n                if (S->cols[i].data) {\n                    switch (S->cols[i].dtype) {\n                    case SQLT_BLOB:\n                    case SQLT_CLOB:\n                        /* do nothing */\n                        break;\n                    default:\n                        efree(S->cols[i].data);\n                    }\n                }\n            }\n            efree(S->cols);\n        }\n\n        S->cols = ecalloc(colcount, sizeof(pdo_oci_column));\n    }\n\n    STMT_CALL_MSG(OCIAttrGet, \"ATTR_ROW_COUNT\", (S->stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, S->err));\n    stmt->row_count = (long) rowcount;\n\n    return 1;\n} /* }}} */\n\nstatic sb4 oci_bind_input_cb(\n    dvoid *ctx, OCIBind *bindp, ub4 iter, ub4 index, dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp) /* {{{ */\n{\n    struct pdo_bound_param_data *param = (struct pdo_bound_param_data *) ctx;\n    pdo_oci_bound_param *P = (pdo_oci_bound_param *) param->driver_data;\n    zval *parameter;\n\n    ZEND_ASSERT(param);\n\n    *indpp = &P->indicator;\n\n    // TODO Duplicate dereferencing operation\n    if (Z_ISREF(param->parameter))\n        parameter = Z_REFVAL(param->parameter);\n    else\n        parameter = &param->parameter;\n\n    if (P->thing) {\n        *bufpp = P->thing;\n        *alenp = sizeof(void *);\n    } else if (ZVAL_IS_NULL(parameter)) {\n        /* insert a NULL value into the column */\n        P->indicator = -1; /* NULL */\n        *bufpp = 0;\n        *alenp = -1;\n    } else if (!P->thing) {\n        /* regular string bind */\n    \t// safe: use preconverted C buffer (no Zend API here `try_convert_to_string`)\n        if (!P->preconv_buf) {\n            return OCI_ERROR;\n        }\n        *bufpp = P->preconv_buf;\n        *alenp = P->preconv_len;\n    }\n\n    *piecep = OCI_ONE_PIECE;\n    return OCI_CONTINUE;\n} /* }}} */\n\n/**\n * This function will be called during the execution of OCIStmtExecute,\n * while the OCIStmtExecute function is executed in an AIO asynchronous thread within asynchronous IO.\n *\n */\nstatic sb4 oci_bind_output_cb(dvoid *ctx,\n                              OCIBind *bindp,\n                              ub4 iter,\n                              ub4 index,\n                              dvoid **bufpp,\n                              ub4 **alenpp,\n                              ub1 *piecep,\n                              dvoid **indpp,\n                              ub2 **rcodepp) /* {{{ */\n{\n    struct pdo_bound_param_data *param = (struct pdo_bound_param_data *) ctx;\n    pdo_oci_bound_param *P = (pdo_oci_bound_param *) param->driver_data;\n    zval *parameter;\n\n    ZEND_ASSERT(param);\n\n    if (Z_ISREF(param->parameter)) {\n        parameter = Z_REFVAL(param->parameter);\n    } else {\n        parameter = &param->parameter;\n    }\n\n    if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {\n        P->actual_len = sizeof(OCILobLocator *);\n        *bufpp = P->thing;\n        *alenpp = &P->actual_len;\n        *piecep = OCI_ONE_PIECE;\n        *rcodepp = &P->retcode;\n        *indpp = &P->indicator;\n        return OCI_CONTINUE;\n    }\n\n    if (Z_TYPE_P(parameter) == IS_OBJECT || Z_TYPE_P(parameter) == IS_RESOURCE) {\n        return OCI_CONTINUE;\n    }\n\n    // It can be safely released in an aio thread, this is a persistent string,\n    // allocated with malloc instead of ZendMM emalloc.\n    zval_ptr_dtor(parameter);\n\n    // Must use malloc for persistent strings\n    Z_STR_P(parameter) = zend_string_alloc(param->max_value_len, 1);\n    P->used_for_output = 1;\n\n    P->actual_len = (ub4) Z_STRLEN_P(parameter);\n    *alenpp = &P->actual_len;\n    *bufpp = (Z_STR_P(parameter))->val;\n    *piecep = OCI_ONE_PIECE;\n    *rcodepp = &P->retcode;\n    *indpp = &P->indicator;\n\n    return OCI_CONTINUE;\n} /* }}} */\n\nstatic int oci_stmt_param_hook(pdo_stmt_t *stmt,\n                               struct pdo_bound_param_data *param,\n                               enum pdo_param_event event_type) /* {{{ */\n{\n    pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data;\n\n    /* we're only interested in parameters for prepared SQL right now */\n    if (param->is_param) {\n        pdo_oci_bound_param *P;\n        sb4 value_sz = -1;\n        zval *parameter;\n\n        if (Z_ISREF(param->parameter))\n            parameter = Z_REFVAL(param->parameter);\n        else\n            parameter = &param->parameter;\n\n        P = (pdo_oci_bound_param *) param->driver_data;\n\n        switch (event_type) {\n        case PDO_PARAM_EVT_FETCH_PRE:\n        case PDO_PARAM_EVT_FETCH_POST:\n        case PDO_PARAM_EVT_NORMALIZE:\n            /* Do nothing */\n            break;\n\n        case PDO_PARAM_EVT_FREE:\n            P = param->driver_data;\n            if (P && P->preconv_buf) {\n\t\t\t\tefree(P->preconv_buf);\n\t\t\t\tP->preconv_buf = NULL;\n\t\t\t\tP->preconv_len = 0;\n\t\t\t}\n            if (P && P->thing) {\n                OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing);\n                OCIDescriptorFree(P->thing, OCI_DTYPE_LOB);\n                P->thing = NULL;\n                efree(P);\n            } else if (P) {\n                efree(P);\n            }\n            break;\n\n        case PDO_PARAM_EVT_ALLOC:\n            P = (pdo_oci_bound_param *) ecalloc(1, sizeof(pdo_oci_bound_param));\n            param->driver_data = P;\n\n            /* figure out what we're doing */\n            switch (PDO_PARAM_TYPE(param->param_type)) {\n            case PDO_PARAM_STMT:\n                return 0;\n\n            case PDO_PARAM_LOB:\n                /* P->thing is now an OCILobLocator * */\n                P->oci_type = SQLT_BLOB;\n                value_sz = (sb4) sizeof(OCILobLocator *);\n                break;\n\n            case PDO_PARAM_STR:\n            default:\n                P->oci_type = SQLT_CHR;\n                value_sz = (sb4) param->max_value_len;\n                if (param->max_value_len == 0) {\n                    value_sz = (sb4) 1332; /* maximum size before value is interpreted as a LONG value */\n                }\n            }\n\n            if (param->name) {\n                STMT_CALL(OCIBindByName,\n                          (S->stmt,\n                           &P->bind,\n                           S->err,\n                           (text *) param->name->val,\n                           (sb4) param->name->len,\n                           0,\n                           value_sz,\n                           P->oci_type,\n                           &P->indicator,\n                           0,\n                           &P->retcode,\n                           0,\n                           0,\n                           OCI_DATA_AT_EXEC));\n            } else {\n                STMT_CALL(OCIBindByPos,\n                          (S->stmt,\n                           &P->bind,\n                           S->err,\n                           ((ub4) param->paramno) + 1,\n                           0,\n                           value_sz,\n                           P->oci_type,\n                           &P->indicator,\n                           0,\n                           &P->retcode,\n                           0,\n                           0,\n                           OCI_DATA_AT_EXEC));\n            }\n\n\n            STMT_CALL(OCIBindDynamic, (P->bind, S->err, param, oci_bind_input_cb, param, oci_bind_output_cb));\n\n            return 1;\n\n        case PDO_PARAM_EVT_EXEC_PRE:\n            P->indicator = 0;\n            P->used_for_output = 0;\n            if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {\n                ub4 empty = 0;\n                STMT_CALL(OCIDescriptorAlloc, (S->H->env, &P->thing, OCI_DTYPE_LOB, 0, NULL));\n                STMT_CALL(OCIAttrSet, (P->thing, OCI_DTYPE_LOB, &empty, 0, OCI_ATTR_LOBEMPTY, S->err));\n                S->have_blobs = 1;\n            }\n\n            /* in PDO_PARAM_EVT_EXEC_PRE, executed in PHP-context (main thread) */\n            if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_LOB && Z_TYPE_P(parameter) != IS_NULL) {\n                convert_to_string(parameter);\n                /* allocate persistent or temporary buffer that outlives dispatch to async thread */\n                P->preconv_len = Z_STRLEN_P(parameter);\n                P->preconv_buf = emalloc(P->preconv_len + 1);\n                memcpy(P->preconv_buf, Z_STRVAL_P(parameter), P->preconv_len);\n                P->preconv_buf[P->preconv_len] = '\\0';\n            }\n\n            return 1;\n\n        case PDO_PARAM_EVT_EXEC_POST:\n            /* fixup stuff set in motion in oci_bind_output_cb */\n            if (P->used_for_output) {\n                if (P->indicator == -1) {\n                    /* set up a NULL value */\n                    if (Z_TYPE_P(parameter) == IS_STRING) {\n                        /* OCI likes to stick non-terminated strings in things */\n                        *Z_STRVAL_P(parameter) = '\\0';\n                    }\n                    zval_ptr_dtor_str(parameter);\n                    ZVAL_UNDEF(parameter);\n                } else if (Z_TYPE_P(parameter) == IS_STRING) {\n                    Z_STR_P(parameter) = zend_string_init(Z_STRVAL_P(parameter), P->actual_len, 1);\n                }\n            } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->thing) {\n                php_stream *stm;\n\n                if (Z_TYPE_P(parameter) == IS_NULL) {\n                    /* if the param is NULL, then we assume that they\n                     * wanted to bind a lob locator into it from the query\n                     * */\n\n                    stm = oci_create_lob_stream(stmt, (OCILobLocator *) P->thing);\n                    if (stm) {\n                        OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE);\n                        php_stream_to_zval(stm, parameter);\n                    }\n                } else {\n                    /* we're a LOB being used for insert; transfer the data now */\n                    size_t n;\n                    ub4 amt, offset = 1;\n                    char *consume;\n\n                    php_stream_from_zval_no_verify(stm, parameter);\n                    if (stm) {\n                        OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE);\n                        do {\n                            char buf[8192];\n                            n = php_stream_read(stm, buf, sizeof(buf));\n                            if ((int) n <= 0) {\n                                break;\n                            }\n                            consume = buf;\n                            do {\n                                amt = (ub4) n;\n                                OCILobWrite(S->H->svc,\n                                            S->err,\n                                            (OCILobLocator *) P->thing,\n                                            &amt,\n                                            offset,\n                                            consume,\n                                            (ub4) n,\n                                            OCI_ONE_PIECE,\n                                            NULL,\n                                            NULL,\n                                            0,\n                                            SQLCS_IMPLICIT);\n                                offset += amt;\n                                n -= amt;\n                                consume += amt;\n                            } while (n);\n                        } while (1);\n                        OCILobClose(S->H->svc, S->err, (OCILobLocator *) P->thing);\n                        OCILobFlushBuffer(S->H->svc, S->err, (OCILobLocator *) P->thing, 0);\n                    } else if (Z_TYPE_P(parameter) == IS_STRING) {\n                        /* stick the string into the LOB */\n                        consume = Z_STRVAL_P(parameter);\n                        n = Z_STRLEN_P(parameter);\n                        if (n) {\n                            OCILobOpen(S->H->svc, S->err, (OCILobLocator *) P->thing, OCI_LOB_READWRITE);\n                            while (n) {\n                                amt = (ub4) n;\n                                OCILobWrite(S->H->svc,\n                                            S->err,\n                                            (OCILobLocator *) P->thing,\n                                            &amt,\n                                            offset,\n                                            consume,\n                                            (ub4) n,\n                                            OCI_ONE_PIECE,\n                                            NULL,\n                                            NULL,\n                                            0,\n                                            SQLCS_IMPLICIT);\n                                consume += amt;\n                                n -= amt;\n                            }\n                            OCILobClose(S->H->svc, S->err, (OCILobLocator *) P->thing);\n                        }\n                    }\n                    OCI_TEMPLOB_CLOSE(S->H->env, S->H->svc, S->H->err, P->thing);\n                    OCIDescriptorFree(P->thing, OCI_DTYPE_LOB);\n                    P->thing = NULL;\n                }\n            }\n\n            if (P->preconv_buf) {\n\t\t\t\tefree(P->preconv_buf);\n\t\t\t\tP->preconv_buf = NULL;\n\t\t\t\tP->preconv_len = 0;\n\t\t\t}\n\n            return 1;\n        }\n    }\n\n    return 1;\n} /* }}} */\n\nstatic int oci_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) /* {{{ */\n{\n#ifdef HAVE_OCISTMTFETCH2\n    ub4 ociori = OCI_FETCH_NEXT;\n#endif\n    pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data;\n\n#ifdef HAVE_OCISTMTFETCH2\n    switch (ori) {\n    case PDO_FETCH_ORI_NEXT:\n        ociori = OCI_FETCH_NEXT;\n        break;\n    case PDO_FETCH_ORI_PRIOR:\n        ociori = OCI_FETCH_PRIOR;\n        break;\n    case PDO_FETCH_ORI_FIRST:\n        ociori = OCI_FETCH_FIRST;\n        break;\n    case PDO_FETCH_ORI_LAST:\n        ociori = OCI_FETCH_LAST;\n        break;\n    case PDO_FETCH_ORI_ABS:\n        ociori = OCI_FETCH_ABSOLUTE;\n        break;\n    case PDO_FETCH_ORI_REL:\n        ociori = OCI_FETCH_RELATIVE;\n        break;\n    }\n    S->last_err = OCIStmtFetch2(S->stmt, S->err, 1, ociori, (sb4) offset, OCI_DEFAULT);\n#else\n    S->last_err = OCIStmtFetch(S->stmt, S->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT);\n#endif\n\n    if (S->last_err == OCI_NO_DATA) {\n        /* no (more) data */\n        return 0;\n    }\n\n    if (S->last_err == OCI_NEED_DATA) {\n        oci_stmt_error(\"OCI_NEED_DATA\");\n        return 0;\n    }\n\n    if (S->last_err == OCI_SUCCESS_WITH_INFO || S->last_err == OCI_SUCCESS) {\n        return 1;\n    }\n\n    oci_stmt_error(\"OCIStmtFetch\");\n\n    return 0;\n} /* }}} */\n\n/**\n * This function is thread-safe as it does not call any ZendAPI.\n */\nstatic sb4 oci_define_callback(\n    dvoid *octxp, OCIDefine *define, ub4 iter, dvoid **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) {\n    pdo_oci_column *col = (pdo_oci_column *) octxp;\n\n    switch (col->dtype) {\n    case SQLT_BLOB:\n    case SQLT_CLOB:\n        *piecep = OCI_ONE_PIECE;\n        *bufpp = col->data;\n        *alenpp = &col->datalen;\n        *indpp = (dvoid *) &col->indicator;\n        break;\n        EMPTY_SWITCH_DEFAULT_CASE();\n    }\n\n    return OCI_CONTINUE;\n}\n\nstatic int oci_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */\n{\n    pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data;\n    OCIParam *param = NULL;\n    text *colname;\n    ub2 dtype, data_size, precis;\n    ub4 namelen;\n    struct pdo_column_data *col = &stmt->columns[colno];\n    bool dyn = FALSE;\n\n    /* describe the column */\n    STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid *) &param, colno + 1));\n\n    /* what type ? */\n    STMT_CALL_MSG(OCIAttrGet, \"OCI_ATTR_DATA_TYPE\", (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err));\n\n    /* how big ? */\n    STMT_CALL_MSG(\n        OCIAttrGet, \"OCI_ATTR_DATA_SIZE\", (param, OCI_DTYPE_PARAM, &data_size, 0, OCI_ATTR_DATA_SIZE, S->err));\n\n    /* precision ? */\n    STMT_CALL_MSG(OCIAttrGet, \"OCI_ATTR_PRECISION\", (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err));\n\n    /* name ? */\n    STMT_CALL_MSG(OCIAttrGet, \"OCI_ATTR_NAME\", (param, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, S->err));\n\n    col->precision = precis;\n    col->maxlen = data_size;\n    col->name = zend_string_init((char *) colname, namelen, 0);\n\n    S->cols[colno].dtype = dtype;\n\n    /* how much room do we need to store the field */\n    switch (dtype) {\n    case SQLT_LBI:\n    case SQLT_LNG:\n        if (dtype == SQLT_LBI) {\n            dtype = SQLT_BIN;\n        } else {\n            dtype = SQLT_CHR;\n        }\n        S->cols[colno].datalen = 512; /* XXX should be INT_MAX and fetched by pieces */\n        S->cols[colno].data = emalloc(S->cols[colno].datalen + 1);\n        break;\n\n    case SQLT_BLOB:\n    case SQLT_CLOB:\n        STMT_CALL(OCIDescriptorAlloc, (S->H->env, (dvoid **) &S->cols[colno].data, OCI_DTYPE_LOB, 0, NULL));\n        S->cols[colno].datalen = sizeof(OCILobLocator *);\n        dyn = TRUE;\n        break;\n\n    case SQLT_BIN:\n    default:\n        if (dtype == SQLT_DAT || dtype == SQLT_NUM || dtype == SQLT_RDD\n#ifdef SQLT_TIMESTAMP\n            || dtype == SQLT_TIMESTAMP\n#endif\n#ifdef SQLT_TIMESTAMP_TZ\n            || dtype == SQLT_TIMESTAMP_TZ\n#endif\n        ) {\n            /* should be big enough for most date formats and numbers */\n            S->cols[colno].datalen = 512;\n#if defined(SQLT_IBFLOAT) && defined(SQLT_IBDOUBLE)\n        } else if (dtype == SQLT_IBFLOAT || dtype == SQLT_IBDOUBLE) {\n            S->cols[colno].datalen = 1024;\n#endif\n        } else if (dtype == SQLT_BIN) {\n            S->cols[colno].datalen = (ub4) col->maxlen * 2; /* raw characters to hex digits */\n        } else {\n            S->cols[colno].datalen = (ub4) (col->maxlen * S->H->max_char_width);\n        }\n\n        S->cols[colno].data = emalloc(S->cols[colno].datalen + 1);\n        dtype = SQLT_CHR;\n    }\n\n    STMT_CALL(OCIDefineByPos,\n              (S->stmt,\n               &S->cols[colno].def,\n               S->err,\n               colno + 1,\n               S->cols[colno].data,\n               S->cols[colno].datalen,\n               dtype,\n               &S->cols[colno].indicator,\n               &S->cols[colno].fetched_len,\n               &S->cols[colno].retcode,\n               dyn ? OCI_DYNAMIC_FETCH : OCI_DEFAULT));\n\n    if (dyn) {\n        STMT_CALL(OCIDefineDynamic, (S->cols[colno].def, S->err, &S->cols[colno], oci_define_callback));\n    }\n\n    return 1;\n} /* }}} */\n\nstruct _oci_lob_env {\n    OCISvcCtx *svc;\n    OCIError *err;\n};\ntypedef struct _oci_lob_env oci_lob_env;\n\nstruct oci_lob_self {\n    zval dbh;\n    pdo_stmt_t *stmt;\n    pdo_oci_stmt *S;\n    OCILobLocator *lob;\n    oci_lob_env *E;\n    ub4 offset;\n    ub1 csfrm;\n};\n\nstatic ssize_t oci_blob_write(php_stream *stream, const char *buf, size_t count) {\n    struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract;\n    ub4 amt;\n    sword r;\n\n    amt = (ub4) count;\n    r = OCILobWrite(self->E->svc,\n                    self->E->err,\n                    self->lob,\n                    &amt,\n                    self->offset,\n                    (char *) buf,\n                    (ub4) count,\n                    OCI_ONE_PIECE,\n                    NULL,\n                    NULL,\n                    0,\n                    SQLCS_IMPLICIT);\n\n    if (r != OCI_SUCCESS) {\n        return (ssize_t) -1;\n    }\n\n    self->offset += amt;\n    return amt;\n}\n\nstatic ssize_t oci_blob_read(php_stream *stream, char *buf, size_t count) {\n    struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract;\n#if HAVE_OCILOBREAD2\n    oraub8 byte_amt = (oraub8) count;\n    oraub8 char_amt = 0;\n\n    sword r = OCILobRead2(self->E->svc,\n                          self->E->err,\n                          self->lob,\n                          &byte_amt,\n                          &char_amt,\n                          (oraub8) self->offset,\n                          buf,\n                          (oraub8) count,\n                          OCI_ONE_PIECE,\n                          NULL,\n                          NULL,\n                          0,\n                          self->csfrm);\n#else\n    ub4 byte_amt = (ub4) count;\n\n    sword r = OCILobRead(self->E->svc,\n                         self->E->err,\n                         self->lob,\n                         &byte_amt,\n                         self->offset,\n                         buf,\n                         (ub4) count,\n                         NULL,\n                         NULL,\n                         0,\n                         SQLCS_IMPLICIT);\n#endif\n\n    if (r != OCI_SUCCESS && r != OCI_NEED_DATA) {\n        return (ssize_t) -1;\n    }\n\n#if HAVE_OCILOBREAD2\n    self->offset += self->csfrm == 0 ? byte_amt : char_amt;\n#else\n    self->offset += byte_amt;\n#endif\n    if (byte_amt < count) {\n        stream->eof = 1;\n    }\n    return byte_amt;\n}\n\nstatic int oci_blob_close(php_stream *stream, int close_handle) {\n    struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract;\n    pdo_stmt_t *stmt = self->stmt;\n\n    if (close_handle) {\n        zend_object *obj = &stmt->std;\n\n        OCILobClose(self->E->svc, self->E->err, self->lob);\n        zval_ptr_dtor(&self->dbh);\n        GC_DELREF(obj);\n        efree(self->E);\n        efree(self);\n    }\n\n    /* php_pdo_free_statement(stmt); */\n    return 0;\n}\n\nstatic int oci_blob_flush(php_stream *stream) {\n    struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract;\n    OCILobFlushBuffer(self->E->svc, self->E->err, self->lob, 0);\n    return 0;\n}\n\nstatic int oci_blob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) {\n    struct oci_lob_self *self = (struct oci_lob_self *) stream->abstract;\n\n    if (offset >= PDO_OCI_LOBMAXSIZE) {\n        return -1;\n    } else {\n        self->offset = (ub4) offset + 1; /* Oracle LOBS are 1-based, but PHP is 0-based */\n        return 0;\n    }\n}\n\nstatic const php_stream_ops oci_blob_stream_ops = {oci_blob_write,\n                                                   oci_blob_read,\n                                                   oci_blob_close,\n                                                   oci_blob_flush,\n                                                   \"pdo_oci blob stream\",\n                                                   oci_blob_seek,\n                                                   NULL,\n                                                   NULL,\n                                                   NULL};\n\nstatic php_stream *oci_create_lob_stream(pdo_stmt_t *stmt, OCILobLocator *lob) {\n    php_stream *stm;\n    struct oci_lob_self *self = ecalloc(1, sizeof(*self));\n\n#if PHP_API_VERSION < 20240925\n    ZVAL_COPY_VALUE(&self->dbh, &stmt->database_object_handle);\n#else\n    ZVAL_OBJ(&self->dbh, stmt->database_object_handle);\n#endif\n    self->lob = lob;\n    self->offset = 1; /* 1-based */\n    self->stmt = stmt;\n    self->S = (pdo_oci_stmt *) stmt->driver_data;\n    self->E = ecalloc(1, sizeof(oci_lob_env));\n    self->E->svc = self->S->H->svc;\n    self->E->err = self->S->err;\n\n    OCILobCharSetForm(self->S->H->env, self->S->err, self->lob, &self->csfrm);\n\n    stm = php_stream_alloc(&oci_blob_stream_ops, self, 0, \"r+b\");\n\n    if (stm) {\n        zend_object *obj;\n        obj = &stmt->std;\n        Z_ADDREF(self->dbh);\n        GC_ADDREF(obj);\n        return stm;\n    }\n\n    efree(self);\n    return NULL;\n}\n\nstatic int oci_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) /* {{{ */\n{\n    pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data;\n    pdo_oci_column *C = &S->cols[colno];\n\n    /* check the indicator to ensure that the data is intact */\n    if (C->indicator == -1) {\n        /* A NULL value */\n        ZVAL_NULL(result);\n        return 1;\n    } else if (C->indicator == 0) {\n        /* it was stored perfectly */\n\n        if (C->dtype == SQLT_BLOB || C->dtype == SQLT_CLOB) {\n            if (C->data) {\n                php_stream *stream = oci_create_lob_stream(stmt, (OCILobLocator *) C->data);\n                OCILobOpen(S->H->svc, S->err, (OCILobLocator *) C->data, OCI_LOB_READONLY);\n                php_stream_to_zval(stream, result);\n                return 1;\n            }\n            return 0;\n        }\n\n        ZVAL_STRINGL_FAST(result, C->data, C->fetched_len);\n        return 1;\n    } else {\n        /* it was truncated */\n        php_error_docref(NULL, E_WARNING, \"Column %d data was too large for buffer and was truncated to fit it\", colno);\n        ZVAL_STRINGL(result, C->data, C->fetched_len);\n        return 1;\n    }\n} /* }}} */\n\nstatic int oci_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) /* {{{ */\n{\n    pdo_oci_stmt *S = (pdo_oci_stmt *) stmt->driver_data;\n    OCIParam *param = NULL;\n    ub2 dtype, precis;\n    sb1 scale;\n    zval flags;\n    ub1 isnull, charset_form;\n    if (!S->stmt) {\n        return FAILURE;\n    }\n    if (colno >= stmt->column_count) {\n        /* error invalid column */\n        return FAILURE;\n    }\n\n    array_init(return_value);\n    array_init(&flags);\n\n    /* describe the column */\n    STMT_CALL(OCIParamGet, (S->stmt, OCI_HTYPE_STMT, S->err, (dvoid *) &param, colno + 1));\n\n    /* column data type */\n    STMT_CALL_MSG(OCIAttrGet, \"OCI_ATTR_DATA_TYPE\", (param, OCI_DTYPE_PARAM, &dtype, 0, OCI_ATTR_DATA_TYPE, S->err));\n\n    /* column precision */\n    STMT_CALL_MSG(OCIAttrGet, \"OCI_ATTR_PRECISION\", (param, OCI_DTYPE_PARAM, &precis, 0, OCI_ATTR_PRECISION, S->err));\n\n    /* column scale */\n    STMT_CALL_MSG(OCIAttrGet, \"OCI_ATTR_SCALE\", (param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, S->err));\n\n    /* string column charset form */\n    if (dtype == SQLT_CHR || dtype == SQLT_VCS || dtype == SQLT_AFC || dtype == SQLT_CLOB) {\n        STMT_CALL_MSG(OCIAttrGet,\n                      \"OCI_ATTR_CHARSET_FORM\",\n                      (param, OCI_DTYPE_PARAM, &charset_form, 0, OCI_ATTR_CHARSET_FORM, S->err));\n    }\n\n    if (dtype) {\n        /* if there is a declared type */\n        switch (dtype) {\n#ifdef SQLT_TIMESTAMP\n        case SQLT_TIMESTAMP:\n            add_assoc_string(return_value, \"oci:decl_type\", \"TIMESTAMP\");\n            add_assoc_string(return_value, \"native_type\", \"TIMESTAMP\");\n            break;\n#endif\n#ifdef SQLT_TIMESTAMP_TZ\n        case SQLT_TIMESTAMP_TZ:\n            add_assoc_string(return_value, \"oci:decl_type\", \"TIMESTAMP WITH TIMEZONE\");\n            add_assoc_string(return_value, \"native_type\", \"TIMESTAMP WITH TIMEZONE\");\n            break;\n#endif\n#ifdef SQLT_TIMESTAMP_LTZ\n        case SQLT_TIMESTAMP_LTZ:\n            add_assoc_string(return_value, \"oci:decl_type\", \"TIMESTAMP WITH LOCAL TIMEZONE\");\n            add_assoc_string(return_value, \"native_type\", \"TIMESTAMP WITH LOCAL TIMEZONE\");\n            break;\n#endif\n#ifdef SQLT_INTERVAL_YM\n        case SQLT_INTERVAL_YM:\n            add_assoc_string(return_value, \"oci:decl_type\", \"INTERVAL YEAR TO MONTH\");\n            add_assoc_string(return_value, \"native_type\", \"INTERVAL YEAR TO MONTH\");\n            break;\n#endif\n#ifdef SQLT_INTERVAL_DS\n        case SQLT_INTERVAL_DS:\n            add_assoc_string(return_value, \"oci:decl_type\", \"INTERVAL DAY TO SECOND\");\n            add_assoc_string(return_value, \"native_type\", \"INTERVAL DAY TO SECOND\");\n            break;\n#endif\n        case SQLT_DAT:\n            add_assoc_string(return_value, \"oci:decl_type\", \"DATE\");\n            add_assoc_string(return_value, \"native_type\", \"DATE\");\n            break;\n        case SQLT_FLT:\n        case SQLT_NUM:\n            /* if the precision is nonzero and scale is -127 then it is a FLOAT */\n            if (scale == -127 && precis != 0) {\n                add_assoc_string(return_value, \"oci:decl_type\", \"FLOAT\");\n                add_assoc_string(return_value, \"native_type\", \"FLOAT\");\n            } else {\n                add_assoc_string(return_value, \"oci:decl_type\", \"NUMBER\");\n                add_assoc_string(return_value, \"native_type\", \"NUMBER\");\n            }\n            break;\n        case SQLT_LNG:\n            add_assoc_string(return_value, \"oci:decl_type\", \"LONG\");\n            add_assoc_string(return_value, \"native_type\", \"LONG\");\n            break;\n        case SQLT_BIN:\n            add_assoc_string(return_value, \"oci:decl_type\", \"RAW\");\n            add_assoc_string(return_value, \"native_type\", \"RAW\");\n            break;\n        case SQLT_LBI:\n            add_assoc_string(return_value, \"oci:decl_type\", \"LONG RAW\");\n            add_assoc_string(return_value, \"native_type\", \"LONG RAW\");\n            break;\n        case SQLT_CHR:\n        case SQLT_VCS:\n            if (charset_form == SQLCS_NCHAR) {\n                add_assoc_string(return_value, \"oci:decl_type\", \"NVARCHAR2\");\n                add_assoc_string(return_value, \"native_type\", \"NVARCHAR2\");\n            } else {\n                add_assoc_string(return_value, \"oci:decl_type\", \"VARCHAR2\");\n                add_assoc_string(return_value, \"native_type\", \"VARCHAR2\");\n            }\n            break;\n        case SQLT_AFC:\n            if (charset_form == SQLCS_NCHAR) {\n                add_assoc_string(return_value, \"oci:decl_type\", \"NCHAR\");\n                add_assoc_string(return_value, \"native_type\", \"NCHAR\");\n            } else {\n                add_assoc_string(return_value, \"oci:decl_type\", \"CHAR\");\n                add_assoc_string(return_value, \"native_type\", \"CHAR\");\n            }\n            break;\n        case SQLT_BLOB:\n            add_assoc_string(return_value, \"oci:decl_type\", \"BLOB\");\n            add_next_index_string(&flags, \"blob\");\n            add_assoc_string(return_value, \"native_type\", \"BLOB\");\n            break;\n        case SQLT_CLOB:\n            if (charset_form == SQLCS_NCHAR) {\n                add_assoc_string(return_value, \"oci:decl_type\", \"NCLOB\");\n                add_assoc_string(return_value, \"native_type\", \"NCLOB\");\n            } else {\n                add_assoc_string(return_value, \"oci:decl_type\", \"CLOB\");\n                add_assoc_string(return_value, \"native_type\", \"CLOB\");\n            }\n            add_next_index_string(&flags, \"blob\");\n            break;\n        case SQLT_BFILE:\n            add_assoc_string(return_value, \"oci:decl_type\", \"BFILE\");\n            add_next_index_string(&flags, \"blob\");\n            add_assoc_string(return_value, \"native_type\", \"BFILE\");\n            break;\n        case SQLT_RDD:\n            add_assoc_string(return_value, \"oci:decl_type\", \"ROWID\");\n            add_assoc_string(return_value, \"native_type\", \"ROWID\");\n            break;\n        case SQLT_BFLOAT:\n        case SQLT_IBFLOAT:\n            add_assoc_string(return_value, \"oci:decl_type\", \"BINARY_FLOAT\");\n            add_assoc_string(return_value, \"native_type\", \"BINARY_FLOAT\");\n            break;\n        case SQLT_BDOUBLE:\n        case SQLT_IBDOUBLE:\n            add_assoc_string(return_value, \"oci:decl_type\", \"BINARY_DOUBLE\");\n            add_assoc_string(return_value, \"native_type\", \"BINARY_DOUBLE\");\n            break;\n        default:\n            add_assoc_long(return_value, \"oci:decl_type\", dtype);\n            add_assoc_string(return_value, \"native_type\", \"UNKNOWN\");\n        }\n    } else {\n        /* if the column is NULL */\n        add_assoc_long(return_value, \"oci:decl_type\", 0);\n        add_assoc_string(return_value, \"native_type\", \"NULL\");\n    }\n\n    switch (dtype) {\n    case SQLT_BLOB:\n    case SQLT_CLOB:\n        add_assoc_long(return_value, \"pdo_type\", PDO_PARAM_LOB);\n        break;\n    default:\n        add_assoc_long(return_value, \"pdo_type\", PDO_PARAM_STR);\n        break;\n    }\n\n    /* column can be null */\n    STMT_CALL_MSG(OCIAttrGet, \"OCI_ATTR_IS_NULL\", (param, OCI_DTYPE_PARAM, &isnull, 0, OCI_ATTR_IS_NULL, S->err));\n\n    if (isnull) {\n        add_next_index_string(&flags, \"nullable\");\n    } else {\n        add_next_index_string(&flags, \"not_null\");\n    }\n\n    /* PDO type */\n    switch (dtype) {\n    case SQLT_BFILE:\n    case SQLT_BLOB:\n    case SQLT_CLOB:\n        add_assoc_long(return_value, \"pdo_type\", PDO_PARAM_LOB);\n        break;\n    default:\n        add_assoc_long(return_value, \"pdo_type\", PDO_PARAM_STR);\n    }\n\n    add_assoc_long(return_value, \"scale\", scale);\n    add_assoc_zval(return_value, \"flags\", &flags);\n\n    OCIDescriptorFree(param, OCI_DTYPE_PARAM);\n    return SUCCESS;\n} /* }}} */\n\nconst struct pdo_stmt_methods swoole_oci_stmt_methods = {oci_stmt_dtor,\n                                                         oci_stmt_execute,\n                                                         oci_stmt_fetch,\n                                                         oci_stmt_describe,\n                                                         oci_stmt_get_col,\n                                                         oci_stmt_param_hook,\n                                                         NULL, /* set_attr */\n                                                         NULL, /* get_attr */\n                                                         oci_stmt_col_meta,\n                                                         NULL,\n                                                         NULL};\n\n"
  },
  {
    "path": "thirdparty/pdo_oci/pdo_oci.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"pdo/php_pdo.h\"\n#include \"pdo/php_pdo_driver.h\"\n#include \"php_pdo_oci.h\"\n#include \"php_pdo_oci_int.h\"\n#ifdef ZTS\n#include <TSRM/TSRM.h>\n#endif\n\n/* {{{ pdo_oci_module_entry */\n\nstatic const zend_module_dep pdo_oci_deps[] = {ZEND_MOD_REQUIRED(\"pdo\") ZEND_MOD_END};\n\nzend_module_entry pdo_oci_module_entry = {STANDARD_MODULE_HEADER_EX,\n                                          NULL,\n                                          pdo_oci_deps,\n                                          \"PDO_OCI\",\n                                          NULL,\n                                          PHP_MINIT(pdo_oci),\n                                          PHP_MSHUTDOWN(pdo_oci),\n                                          PHP_RINIT(pdo_oci),\n                                          NULL,\n                                          PHP_MINFO(pdo_oci),\n                                          PHP_PDO_OCI_VERSION,\n                                          STANDARD_MODULE_PROPERTIES};\n/* }}} */\n\n#ifdef COMPILE_DL_PDO_OCI\nZEND_GET_MODULE(pdo_oci)\n#endif\n\nconst ub4 PDO_OCI_INIT_MODE =\n#if 0 && defined(OCI_SHARED)\n\t\t\t/* shared mode is known to be bad for PHP */\n\t\t\tOCI_SHARED\n#else\n    OCI_DEFAULT\n#endif\n#ifdef OCI_OBJECT\n    | OCI_OBJECT\n#endif\n\t// Whether PHP is ZTS or not, when used in Swoole coroutines, this option must be enabled.\n    | OCI_THREADED\n    ;\n\n/* true global environment */\nOCIEnv *pdo_oci_Env = NULL;\n\n#ifdef ZTS\n/* lock for pdo_oci_Env initialization */\nstatic MUTEX_T pdo_oci_env_mutex;\n#endif\n\n/* {{{ PHP_MINIT_FUNCTION */\nPHP_MINIT_FUNCTION(pdo_oci) {\n    REGISTER_PDO_CLASS_CONST_LONG(\"OCI_ATTR_ACTION\", (zend_long) PDO_OCI_ATTR_ACTION);\n    REGISTER_PDO_CLASS_CONST_LONG(\"OCI_ATTR_CLIENT_INFO\", (zend_long) PDO_OCI_ATTR_CLIENT_INFO);\n    REGISTER_PDO_CLASS_CONST_LONG(\"OCI_ATTR_CLIENT_IDENTIFIER\", (zend_long) PDO_OCI_ATTR_CLIENT_IDENTIFIER);\n    REGISTER_PDO_CLASS_CONST_LONG(\"OCI_ATTR_MODULE\", (zend_long) PDO_OCI_ATTR_MODULE);\n    REGISTER_PDO_CLASS_CONST_LONG(\"OCI_ATTR_CALL_TIMEOUT\", (zend_long) PDO_OCI_ATTR_CALL_TIMEOUT);\n\n    if (FAILURE == php_pdo_register_driver(&pdo_oci_driver)) {\n        return FAILURE;\n    }\n\n    // Defer OCI init to PHP_RINIT_FUNCTION because with php-fpm,\n    // NLS_LANG is not yet available here.\n\n#ifdef ZTS\n    pdo_oci_env_mutex = tsrm_mutex_alloc();\n#endif\n\n    return SUCCESS;\n}\n/* }}} */\n\n/* {{{ PHP_RINIT_FUNCTION */\nPHP_RINIT_FUNCTION(pdo_oci) {\n    if (!pdo_oci_Env) {\n#ifdef ZTS\n        tsrm_mutex_lock(pdo_oci_env_mutex);\n        if (!pdo_oci_Env) {  // double-checked locking idiom\n#endif\n#ifdef HAVE_OCIENVCREATE\n            OCIEnvCreate(&pdo_oci_Env, PDO_OCI_INIT_MODE, NULL, NULL, NULL, NULL, 0, NULL);\n#else\n        OCIInitialize(PDO_OCI_INIT_MODE, NULL, NULL, NULL, NULL);\n        OCIEnvInit(&pdo_oci_Env, OCI_DEFAULT, 0, NULL);\n#endif\n#ifdef ZTS\n        }\n        tsrm_mutex_unlock(pdo_oci_env_mutex);\n#endif\n    }\n\n    return SUCCESS;\n}\n/* }}} */\n\n/* {{{ PHP_MSHUTDOWN_FUNCTION */\nPHP_MSHUTDOWN_FUNCTION(pdo_oci) {\n    php_pdo_unregister_driver(&pdo_oci_driver);\n\n    if (pdo_oci_Env) {\n        OCIHandleFree((dvoid *) pdo_oci_Env, OCI_HTYPE_ENV);\n    }\n\n#ifdef ZTS\n    tsrm_mutex_free(pdo_oci_env_mutex);\n#endif\n\n    return SUCCESS;\n}\n/* }}} */\n\n/* {{{ PHP_MINFO_FUNCTION */\nPHP_MINFO_FUNCTION(pdo_oci) {\n    php_info_print_table_start();\n    php_info_print_table_row(2, \"PDO Driver for OCI 8 and later\", \"enabled\");\n    php_info_print_table_end();\n}\n/* }}} */\n"
  },
  {
    "path": "thirdparty/pdo_oci/php_pdo_oci_int.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef PHP_PDO_OCI_INT_H\n#define PHP_PDO_OCI_INT_H\n\n#include \"zend_portability.h\"\n\n#include <oci.h>\n\ntypedef struct {\n    const char *file;\n    int line;\n    sb4 errcode;\n    char *errmsg;\n} pdo_oci_error_info;\n\n/* stuff we use in an OCI database handle */\ntypedef struct {\n    OCIServer *server;\n    OCISession *session;\n    OCIEnv *env;\n    OCIError *err;\n    OCISvcCtx *svc;\n    /* OCI9; 0 == use NLS_LANG */\n    ub4 prefetch;\n    ub2 charset;\n    sword last_err;\n    sb4 max_char_width;\n\n    unsigned attached : 1;\n    unsigned _reserved : 31;\n\n    pdo_oci_error_info einfo;\n} pdo_oci_db_handle;\n\ntypedef struct {\n    OCIDefine *def;\n    ub2 fetched_len;\n    ub2 retcode;\n    sb2 indicator;\n\n    char *data;\n    ub4 datalen;\n\n    ub2 dtype;\n\n} pdo_oci_column;\n\ntypedef struct {\n    pdo_oci_db_handle *H;\n    OCIStmt *stmt;\n    OCIError *err;\n    sword last_err;\n    ub2 stmt_type;\n    ub4 exec_type;\n    pdo_oci_column *cols;\n    pdo_oci_error_info einfo;\n    unsigned int have_blobs : 1;\n} pdo_oci_stmt;\n\ntypedef struct {\n    OCIBind *bind; /* allocated by OCI */\n    sb2 oci_type;\n    sb2 indicator;\n    ub2 retcode;\n\n    ub4 actual_len;\n\n    dvoid *thing; /* for LOBS, REFCURSORS etc. */\n\n    unsigned used_for_output;\n\n    /* --- NEW: preconverted buffer for async-safe binds --- */\n     char *preconv_buf;      /* pointer to a NUL-terminated C buffer (owned by param) */\n     ub4   preconv_len;      /* length of the buffer in bytes */\n} pdo_oci_bound_param;\n\nextern const ub4 SWOOLE_PDO_OCI_INIT_MODE;\nextern const pdo_driver_t swoole_pdo_oci_driver;\nextern OCIEnv *swoole_pdo_oci_Env;\n\nub4 _oci_error(\n    OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, int isinit, const char *file, int line);\n#define oci_init_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, TRUE, __FILE__, __LINE__)\n#define oci_drv_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, FALSE, __FILE__, __LINE__)\n#define oci_stmt_error(w) _oci_error(S->err, stmt->dbh, stmt, w, S->last_err, FALSE, __FILE__, __LINE__)\n\nextern const struct pdo_stmt_methods swoole_oci_stmt_methods;\nextern const ub4 SWOOLE_PDO_OCI_INIT_MODE;\nextern OCIEnv *swoole_pdo_oci_Env;\n\n/* Default prefetch size in number of rows */\n#define PDO_OCI_PREFETCH_DEFAULT 100\n\n/* Arbitrary assumed row length for prefetch memory limit calcuation */\n#define PDO_OCI_PREFETCH_ROWSIZE 1024\n\nenum {\n    PDO_OCI_ATTR_ACTION = PDO_ATTR_DRIVER_SPECIFIC,\n    PDO_OCI_ATTR_CLIENT_INFO,\n    PDO_OCI_ATTR_CLIENT_IDENTIFIER,\n    PDO_OCI_ATTR_MODULE,\n    PDO_OCI_ATTR_CALL_TIMEOUT\n};\n\n#endif /* PHP_PDO_OCI_INT_H */\n"
  },
  {
    "path": "thirdparty/pdo_sqlite/php_pdo_sqlite.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef PHP_PDO_SQLITE_H\n#define PHP_PDO_SQLITE_H\n\nextern zend_module_entry pdo_sqlite_module_entry;\n#define phpext_pdo_sqlite_ptr &pdo_sqlite_module_entry\n\n#include \"php_version.h\"\n#define PHP_PDO_SQLITE_VERSION PHP_VERSION\n\n#ifdef ZTS\n#include \"TSRM.h\"\n#endif\n\nenum pdo_sqlite_transaction_mode {\n\tPDO_SQLITE_TRANSACTION_MODE_DEFERRED = 0,\n\tPDO_SQLITE_TRANSACTION_MODE_IMMEDIATE = 1,\n\tPDO_SQLITE_TRANSACTION_MODE_EXCLUSIVE = 2\n};\n\nPHP_MINIT_FUNCTION(pdo_sqlite);\nPHP_MSHUTDOWN_FUNCTION(pdo_sqlite);\nPHP_RINIT_FUNCTION(pdo_sqlite);\nPHP_RSHUTDOWN_FUNCTION(pdo_sqlite);\nPHP_MINFO_FUNCTION(pdo_sqlite);\n\n#endif\t/* PHP_PDO_SQLITE_H */\n"
  },
  {
    "path": "thirdparty/pdo_sqlite/php_pdo_sqlite_int.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef PHP_PDO_SQLITE_INT_H\n#define PHP_PDO_SQLITE_INT_H\n\n#include \"php_pdo_sqlite.h\"\n#include <sqlite3.h>\n\ntypedef struct {\n\tconst char *file;\n\tint line;\n\tunsigned int errcode;\n\tchar *errmsg;\n} pdo_sqlite_error_info;\n\ntypedef struct {\n\tsqlite3 *db;\n\tpdo_sqlite_error_info einfo;\n\tenum pdo_sqlite_transaction_mode transaction_mode;\n} pdo_sqlite_db_handle;\n\ntypedef struct {\n\tpdo_sqlite_db_handle \t*H;\n\tsqlite3_stmt *stmt;\n\tunsigned pre_fetched:1;\n\tunsigned done:1;\n} pdo_sqlite_stmt;\n\nextern const pdo_driver_t pdo_sqlite_driver;\n\n#if PHP_VERSION_ID >= 80400\nextern int pdo_sqlite_scanner(pdo_scanner_t *s);\n#endif\n\nextern int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line);\n#define pdo_sqlite_error(s) _pdo_sqlite_error(s, NULL, __FILE__, __LINE__)\n#define pdo_sqlite_error_stmt(s) _pdo_sqlite_error(stmt->dbh, stmt, __FILE__, __LINE__)\n\nextern const struct pdo_stmt_methods swoole_sqlite_stmt_methods;\n\nenum {\n\tPDO_SQLITE_ATTR_OPEN_FLAGS = PDO_ATTR_DRIVER_SPECIFIC,\n\tPDO_SQLITE_ATTR_READONLY_STATEMENT,\n\tPDO_SQLITE_ATTR_EXTENDED_RESULT_CODES,\n\tPDO_SQLITE_ATTR_BUSY_STATEMENT,\n\tPDO_SQLITE_ATTR_EXPLAIN_STATEMENT,\n\tPDO_SQLITE_ATTR_TRANSACTION_MODE\n};\n\ntypedef int pdo_sqlite_create_collation_callback(void*, int, const void*, int, const void*);\n\nvoid pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAMETERS, pdo_sqlite_create_collation_callback callback);\n\n#endif\n"
  },
  {
    "path": "thirdparty/pdo_sqlite/sqlite_driver.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_SQLITE_HOOK\n#include \"php_swoole_sqlite.h\"\n#include \"php_swoole_api.h\"\n\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"ext/pdo/php_pdo.h\"\n#include \"ext/pdo/php_pdo_driver.h\"\n#include \"php_pdo_sqlite.h\"\n#include \"php_pdo_sqlite_int.h\"\n#include \"zend_exceptions.h\"\n\nint _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line) /* {{{ */\n{\n\tpdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;\n\tpdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code;\n\tpdo_sqlite_error_info *einfo = &H->einfo;\n\n\teinfo->errcode = sqlite3_errcode(H->db);\n\teinfo->file = file;\n\teinfo->line = line;\n\n\tif (einfo->errcode != SQLITE_OK) {\n\t\tif (einfo->errmsg) {\n\t\t\tpefree(einfo->errmsg, dbh->is_persistent);\n\t\t}\n\t\teinfo->errmsg = pestrdup((char*)sqlite3_errmsg(H->db), dbh->is_persistent);\n\t} else { /* no error */\n\t\tstrncpy(*pdo_err, PDO_ERR_NONE, sizeof(*pdo_err));\n\t\treturn 0;\n\t}\n\tswitch (einfo->errcode) {\n\t\tcase SQLITE_NOTFOUND:\n\t\t\tstrncpy(*pdo_err, \"42S02\", sizeof(*pdo_err));\n\t\t\tbreak;\n\n\t\tcase SQLITE_INTERRUPT:\n\t\t\tstrncpy(*pdo_err, \"01002\", sizeof(*pdo_err));\n\t\t\tbreak;\n\n\t\tcase SQLITE_NOLFS:\n\t\t\tstrncpy(*pdo_err, \"HYC00\", sizeof(*pdo_err));\n\t\t\tbreak;\n\n\t\tcase SQLITE_TOOBIG:\n\t\t\tstrncpy(*pdo_err, \"22001\", sizeof(*pdo_err));\n\t\t\tbreak;\n\n\t\tcase SQLITE_CONSTRAINT:\n\t\t\tstrncpy(*pdo_err, \"23000\", sizeof(*pdo_err));\n\t\t\tbreak;\n\n\t\tcase SQLITE_ERROR:\n\t\tdefault:\n\t\t\tstrncpy(*pdo_err, \"HY000\", sizeof(*pdo_err));\n\t\t\tbreak;\n\t}\n\n\tif (!dbh->methods) {\n\t\tpdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err);\n\t}\n\n\treturn einfo->errcode;\n}\n/* }}} */\n\nstatic void pdo_sqlite_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info)\n{\n\tpdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;\n\tpdo_sqlite_error_info *einfo = &H->einfo;\n\n\tif (einfo->errcode) {\n\t\tadd_next_index_long(info, einfo->errcode);\n\t\tadd_next_index_string(info, einfo->errmsg);\n\t}\n}\n\nstatic void sqlite_handle_closer(pdo_dbh_t *dbh) /* {{{ */\n{\n\tpdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;\n\n\tif (H) {\n\t\tpdo_sqlite_error_info *einfo = &H->einfo;\n\n\t\tif (H->db) {\n#ifdef HAVE_SQLITE3_CLOSE_V2\n\t\t\tsqlite3_close_v2(H->db);\n#else\n\t\t\tsqlite3_close(H->db);\n#endif\n\t\t\tH->db = NULL;\n\t\t}\n\t\tif (einfo->errmsg) {\n\t\t\tpefree(einfo->errmsg, dbh->is_persistent);\n\t\t\teinfo->errmsg = NULL;\n\t\t}\n\t\tpefree(H, dbh->is_persistent);\n\t\tdbh->driver_data = NULL;\n\t}\n}\n/* }}} */\n\nstatic bool sqlite_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options)\n{\n\tpdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;\n\tpdo_sqlite_stmt *S = ecalloc(1, sizeof(pdo_sqlite_stmt));\n\tint i;\n\tconst char *tail;\n\n\tS->H = H;\n\tstmt->driver_data = S;\n\tstmt->methods = &swoole_sqlite_stmt_methods;\n\tstmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL|PDO_PLACEHOLDER_NAMED;\n\n\tif (PDO_CURSOR_FWDONLY != pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY)) {\n\t\tH->einfo.errcode = SQLITE_ERROR;\n\t\tpdo_sqlite_error(dbh);\n\t\treturn false;\n\t}\n\n\ti = sqlite3_prepare_v2(H->db, ZSTR_VAL(sql), ZSTR_LEN(sql), &S->stmt, &tail);\n\tif (i == SQLITE_OK) {\n\t\treturn true;\n\t}\n\n\tpdo_sqlite_error(dbh);\n\n\treturn false;\n}\n\nstatic zend_long sqlite_handle_doer(pdo_dbh_t *dbh, const zend_string *sql)\n{\n\tpdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;\n\n\tif (sqlite3_exec(H->db, ZSTR_VAL(sql), NULL, NULL, NULL) != SQLITE_OK) {\n\t\tpdo_sqlite_error(dbh);\n\t\treturn -1;\n\t} else {\n\t\treturn sqlite3_changes(H->db);\n\t}\n}\n\nstatic zend_string *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const zend_string *name)\n{\n\tpdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;\n\n\treturn zend_i64_to_str(sqlite3_last_insert_rowid(H->db));\n}\n\n/* NB: doesn't handle binary strings... use prepared stmts for that */\nstatic zend_string* sqlite_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype)\n{\n\tchar *quoted;\n\tif (ZSTR_LEN(unquoted) > (INT_MAX - 3) / 2) {\n\t\treturn NULL;\n\t}\n#if PHP_VERSION_ID >= 80500\n\tif (UNEXPECTED(zend_str_has_nul_byte(unquoted))) {\n\t\tif (dbh->error_mode == PDO_ERRMODE_EXCEPTION) {\n\t\t\tzend_throw_exception_ex(\n\t\t\t\tphp_pdo_get_exception(), 0,\n\t\t\t\t\"SQLite PDO::quote does not support null bytes\");\n\t\t} else if (dbh->error_mode == PDO_ERRMODE_WARNING) {\n\t\t\tphp_error_docref(NULL, E_WARNING,\n\t\t\t\t\"SQLite PDO::quote does not support null bytes\");\n\t\t}\n\t\treturn NULL;\n\t}\n#endif\n\n\tquoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3);\n\t/* TODO use %Q format? */\n\tsqlite3_snprintf(2*ZSTR_LEN(unquoted) + 3, quoted, \"'%q'\", ZSTR_VAL(unquoted));\n\tzend_string *quoted_str = zend_string_init(quoted, strlen(quoted), 0);\n\tefree(quoted);\n\treturn quoted_str;\n}\n\nstatic bool sqlite_handle_begin(pdo_dbh_t *dbh)\n{\n\tpdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;\n\n\tconst char *begin_statement = \"BEGIN\";\n\tswitch (H->transaction_mode) {\n\t\tcase PDO_SQLITE_TRANSACTION_MODE_DEFERRED:\n\t\t\tbegin_statement = \"BEGIN DEFERRED TRANSACTION\";\n\t\t\tbreak;\n\t\tcase PDO_SQLITE_TRANSACTION_MODE_IMMEDIATE:\n\t\t\tbegin_statement = \"BEGIN IMMEDIATE TRANSACTION\";\n\t\t\tbreak;\n\t\tcase PDO_SQLITE_TRANSACTION_MODE_EXCLUSIVE:\n\t\t\tbegin_statement = \"BEGIN EXCLUSIVE TRANSACTION\";\n\t\t\tbreak;\n\t}\n\n\tif (sqlite3_exec(H->db, begin_statement, NULL, NULL, NULL) != SQLITE_OK) {\n\t\tpdo_sqlite_error(dbh);\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic bool sqlite_handle_commit(pdo_dbh_t *dbh)\n{\n\tpdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;\n\n\tif (sqlite3_exec(H->db, \"COMMIT\", NULL, NULL, NULL) != SQLITE_OK) {\n\t\tpdo_sqlite_error(dbh);\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic bool sqlite_handle_rollback(pdo_dbh_t *dbh)\n{\n\tpdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;\n\n\tif (sqlite3_exec(H->db, \"ROLLBACK\", NULL, NULL, NULL) != SQLITE_OK) {\n\t\tpdo_sqlite_error(dbh);\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic int pdo_sqlite_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value)\n{\n\tpdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;\n\n\tswitch (attr) {\n\t\tcase PDO_ATTR_CLIENT_VERSION:\n\t\tcase PDO_ATTR_SERVER_VERSION:\n\t\t\tZVAL_STRING(return_value, (char *)sqlite3_libversion());\n\t\t\tbreak;\n\t\tcase PDO_SQLITE_ATTR_TRANSACTION_MODE:\n\t\t\tZVAL_LONG(return_value, H->transaction_mode);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\nstatic bool pdo_sqlite_in_transaction(pdo_dbh_t *dbh)\n{\n\tpdo_sqlite_db_handle* H = (pdo_sqlite_db_handle*) dbh->driver_data;\n\t/* It's not possible in sqlite3 to explicitly turn autocommit off other\n\t * than manually starting a transaction. Manual transactions always are\n\t * the mode of operation when autocommit is off. */\n\treturn H->db && sqlite3_get_autocommit(H->db) == 0;\n}\n\nstatic bool pdo_sqlite_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)\n{\n\tpdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data;\n\tzend_long lval;\n\n\tswitch (attr) {\n\t\tcase PDO_ATTR_TIMEOUT:\n\t\t\tif (!pdo_get_long_param(&lval, val)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tsqlite3_busy_timeout(H->db, lval * 1000);\n\t\t\treturn true;\n\t\tcase PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES:\n\t\t\tif (!pdo_get_long_param(&lval, val)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tsqlite3_extended_result_codes(H->db, lval);\n\t\t\treturn true;\n\t\tcase PDO_SQLITE_ATTR_TRANSACTION_MODE:\n\t\t\tif (!pdo_get_long_param(&lval, val)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tswitch (lval) {\n\t\t\t\tcase PDO_SQLITE_TRANSACTION_MODE_DEFERRED:\n\t\t\t\tcase PDO_SQLITE_TRANSACTION_MODE_IMMEDIATE:\n\t\t\t\tcase PDO_SQLITE_TRANSACTION_MODE_EXCLUSIVE:\n\t\t\t\t\tH->transaction_mode = lval;\n\t\t\t\t\treturn true;\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\t}\n\treturn false;\n}\n\nstatic void pdo_sqlite_request_shutdown(pdo_dbh_t *dbh)\n{\n}\n\nstatic const struct pdo_dbh_methods sqlite_methods = {\n\tsqlite_handle_closer,\n\tsqlite_handle_preparer,\n\tsqlite_handle_doer,\n\tsqlite_handle_quoter,\n\tsqlite_handle_begin,\n\tsqlite_handle_commit,\n\tsqlite_handle_rollback,\n\tpdo_sqlite_set_attr,\n\tpdo_sqlite_last_insert_id,\n\tpdo_sqlite_fetch_error_func,\n\tpdo_sqlite_get_attribute,\n\tNULL,\t/* check_liveness: not needed */\n\tNULL,\n\tpdo_sqlite_request_shutdown,\n\tpdo_sqlite_in_transaction,\n\tNULL,\n#if PHP_VERSION_ID >= 80400\n\tpdo_sqlite_scanner\n#endif\n};\n\nstatic char *make_filename_safe(const char *filename)\n{\n\tif (!filename) {\n\t\treturn NULL;\n\t}\n\tif (*filename && strncasecmp(filename, \"file:\", 5) == 0) {\n\t\tif (PG(open_basedir) && *PG(open_basedir)) {\n\t\t\treturn NULL;\n\t\t}\n\t\treturn estrdup(filename);\n\t}\n\tif (*filename && strcmp(filename, \":memory:\")) {\n\t\tchar *fullpath = expand_filepath(filename, NULL);\n\n\t\tif (!fullpath) {\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif (php_check_open_basedir(fullpath)) {\n\t\t\tefree(fullpath);\n\t\t\treturn NULL;\n\t\t}\n\t\treturn fullpath;\n\t}\n\treturn estrdup(filename);\n}\n\n#define ZVAL_NULLABLE_STRING(zv, str) do { \\\n\tzval *zv_ = zv; \\\n\tconst char *str_ = str; \\\n\tif (str_) { \\\n\t\tZVAL_STRING(zv_, str_); \\\n\t} else { \\\n\t\tZVAL_NULL(zv_); \\\n\t} \\\n} while (0)\n\nstatic int pdo_sqlite_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */\n{\n\tpdo_sqlite_db_handle *H;\n\tint i, ret = 0;\n\tzend_long timeout = 60, flags;\n\tchar *filename;\n\n\tH = pecalloc(1, sizeof(pdo_sqlite_db_handle), dbh->is_persistent);\n\n\tH->einfo.errcode = 0;\n\tH->einfo.errmsg = NULL;\n\tdbh->driver_data = H;\n\n\t/* skip all but this one param event */\n\tdbh->skip_param_evt = 0x7F ^ (1 << PDO_PARAM_EVT_EXEC_PRE);\n\n\tfilename = make_filename_safe(dbh->data_source);\n\n\tif (!filename) {\n\t\tzend_throw_exception_ex(php_pdo_get_exception(), 0,\n\t\t\t\"open_basedir prohibits opening %s\",\n\t\t\tdbh->data_source);\n\t\tgoto cleanup;\n\t}\n\n\tflags = pdo_attr_lval(driver_options, PDO_SQLITE_ATTR_OPEN_FLAGS, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);\n\n\tif (!(PG(open_basedir) && *PG(open_basedir))) {\n\t\tflags |= SQLITE_OPEN_URI;\n\t}\n\ti = sqlite3_open_v2(filename, &H->db, flags, NULL);\n\n\tefree(filename);\n\n\tif (i != SQLITE_OK) {\n\t\tpdo_sqlite_error(dbh);\n\t\tgoto cleanup;\n\t}\n\n\tif (driver_options) {\n\t\ttimeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, timeout);\n\t}\n\tsqlite3_busy_timeout(H->db, timeout * 1000);\n\n\tdbh->alloc_own_columns = 1;\n\tdbh->max_escaped_char_length = 2;\n\n\tret = 1;\n\ncleanup:\n\tdbh->methods = &sqlite_methods;\n\n\treturn ret;\n}\n/* }}} */\n\nconst pdo_driver_t swoole_pdo_sqlite_driver = {\n\tPDO_DRIVER_HEADER(sqlite),\n\tpdo_sqlite_handle_factory\n};\n"
  },
  {
    "path": "thirdparty/pdo_sqlite/sqlite_sql_parser.c",
    "content": "/* Generated by re2c 4.3 */\n#line 1 \"ext/pdo_sqlite/sqlite_sql_parser.re\"\n/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Matteo Beccati <mbeccati@php.net>                            |\n  +----------------------------------------------------------------------+\n*/\n\n\n#include \"php.h\"\n#include \"ext/pdo/php_pdo_driver.h\"\n#include \"ext/pdo/pdo_sql_parser.h\"\n\nint pdo_sqlite_scanner(pdo_scanner_t *s)\n{\n\tconst char *cursor = s->cur;\n\n\ts->tok = cursor;\n\t#line 34 \"ext/pdo_sqlite/sqlite_sql_parser.re\"\n\n\n\t\n#line 34 \"ext/pdo_sqlite/sqlite_sql_parser.c\"\n{\n\tYYCTYPE yych;\n\tunsigned int yyaccept = 0;\n\tif ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase 0x00: goto yy1;\n\t\tcase '\"': goto yy4;\n\t\tcase '\\'': goto yy6;\n\t\tcase '-': goto yy7;\n\t\tcase '/': goto yy8;\n\t\tcase ':': goto yy9;\n\t\tcase '?': goto yy10;\n\t\tcase '[': goto yy12;\n\t\tcase '`': goto yy13;\n\t\tdefault: goto yy2;\n\t}\nyy1:\n\tYYCURSOR = YYMARKER;\n\tswitch (yyaccept) {\n\t\tcase 0: goto yy5;\n\t\tcase 1: goto yy17;\n\t\tcase 2: goto yy21;\n\t\tcase 3: goto yy33;\n\t\tdefault: goto yy37;\n\t}\nyy2:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase 0x00:\n\t\tcase '\"':\n\t\tcase '\\'':\n\t\tcase '-':\n\t\tcase '/':\n\t\tcase ':':\n\t\tcase '?':\n\t\tcase '[':\n\t\tcase '`': goto yy3;\n\t\tdefault: goto yy2;\n\t}\nyy3:\n#line 46 \"ext/pdo_sqlite/sqlite_sql_parser.re\"\n\t{ RET(PDO_PARSER_TEXT); }\n#line 80 \"ext/pdo_sqlite/sqlite_sql_parser.c\"\nyy4:\n\tyyaccept = 0;\n\tyych = *(YYMARKER = ++YYCURSOR);\n\tif (yych >= 0x01) goto yy15;\nyy5:\n#line 44 \"ext/pdo_sqlite/sqlite_sql_parser.re\"\n\t{ SKIP_ONE(PDO_PARSER_TEXT); }\n#line 88 \"ext/pdo_sqlite/sqlite_sql_parser.c\"\nyy6:\n\tyyaccept = 0;\n\tyych = *(YYMARKER = ++YYCURSOR);\n\tif (yych <= 0x00) goto yy5;\n\tgoto yy19;\nyy7:\n\tyych = *++YYCURSOR;\n\tswitch (yych) {\n\t\tcase '-': goto yy22;\n\t\tdefault: goto yy5;\n\t}\nyy8:\n\tyych = *++YYCURSOR;\n\tswitch (yych) {\n\t\tcase '*': goto yy24;\n\t\tdefault: goto yy5;\n\t}\nyy9:\n\tyych = *++YYCURSOR;\n\tswitch (yych) {\n\t\tcase '0':\n\t\tcase '1':\n\t\tcase '2':\n\t\tcase '3':\n\t\tcase '4':\n\t\tcase '5':\n\t\tcase '6':\n\t\tcase '7':\n\t\tcase '8':\n\t\tcase '9':\n\t\tcase 'A':\n\t\tcase 'B':\n\t\tcase 'C':\n\t\tcase 'D':\n\t\tcase 'E':\n\t\tcase 'F':\n\t\tcase 'G':\n\t\tcase 'H':\n\t\tcase 'I':\n\t\tcase 'J':\n\t\tcase 'K':\n\t\tcase 'L':\n\t\tcase 'M':\n\t\tcase 'N':\n\t\tcase 'O':\n\t\tcase 'P':\n\t\tcase 'Q':\n\t\tcase 'R':\n\t\tcase 'S':\n\t\tcase 'T':\n\t\tcase 'U':\n\t\tcase 'V':\n\t\tcase 'W':\n\t\tcase 'X':\n\t\tcase 'Y':\n\t\tcase 'Z':\n\t\tcase '_':\n\t\tcase 'a':\n\t\tcase 'b':\n\t\tcase 'c':\n\t\tcase 'd':\n\t\tcase 'e':\n\t\tcase 'f':\n\t\tcase 'g':\n\t\tcase 'h':\n\t\tcase 'i':\n\t\tcase 'j':\n\t\tcase 'k':\n\t\tcase 'l':\n\t\tcase 'm':\n\t\tcase 'n':\n\t\tcase 'o':\n\t\tcase 'p':\n\t\tcase 'q':\n\t\tcase 'r':\n\t\tcase 's':\n\t\tcase 't':\n\t\tcase 'u':\n\t\tcase 'v':\n\t\tcase 'w':\n\t\tcase 'x':\n\t\tcase 'y':\n\t\tcase 'z': goto yy25;\n\t\tcase ':': goto yy27;\n\t\tdefault: goto yy5;\n\t}\nyy10:\n\tyych = *++YYCURSOR;\n\tswitch (yych) {\n\t\tcase '?': goto yy29;\n\t\tdefault: goto yy11;\n\t}\nyy11:\n#line 43 \"ext/pdo_sqlite/sqlite_sql_parser.re\"\n\t{ RET(PDO_PARSER_BIND_POS); }\n#line 184 \"ext/pdo_sqlite/sqlite_sql_parser.c\"\nyy12:\n\tyyaccept = 0;\n\tyych = *(YYMARKER = ++YYCURSOR);\n\tif (yych <= 0x00) goto yy5;\n\tgoto yy31;\nyy13:\n\tyyaccept = 0;\n\tyych = *(YYMARKER = ++YYCURSOR);\n\tif (yych <= 0x00) goto yy5;\n\tgoto yy35;\nyy14:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\nyy15:\n\tswitch (yych) {\n\t\tcase 0x00: goto yy1;\n\t\tcase '\"': goto yy16;\n\t\tdefault: goto yy14;\n\t}\nyy16:\n\tyyaccept = 1;\n\tYYMARKER = ++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase 0x00: goto yy17;\n\t\tcase '\"': goto yy16;\n\t\tdefault: goto yy14;\n\t}\nyy17:\n#line 37 \"ext/pdo_sqlite/sqlite_sql_parser.re\"\n\t{ RET(PDO_PARSER_TEXT); }\n#line 218 \"ext/pdo_sqlite/sqlite_sql_parser.c\"\nyy18:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\nyy19:\n\tswitch (yych) {\n\t\tcase 0x00: goto yy1;\n\t\tcase '\\'': goto yy20;\n\t\tdefault: goto yy18;\n\t}\nyy20:\n\tyyaccept = 2;\n\tYYMARKER = ++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase 0x00: goto yy21;\n\t\tcase '\\'': goto yy20;\n\t\tdefault: goto yy18;\n\t}\nyy21:\n#line 38 \"ext/pdo_sqlite/sqlite_sql_parser.re\"\n\t{ RET(PDO_PARSER_TEXT); }\n#line 242 \"ext/pdo_sqlite/sqlite_sql_parser.c\"\nyy22:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase '\\n': goto yy23;\n\t\tdefault: goto yy22;\n\t}\nyy23:\n#line 45 \"ext/pdo_sqlite/sqlite_sql_parser.re\"\n\t{ RET(PDO_PARSER_TEXT); }\n#line 254 \"ext/pdo_sqlite/sqlite_sql_parser.c\"\nyy24:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase '*': goto yy38;\n\t\tdefault: goto yy24;\n\t}\nyy25:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase '0':\n\t\tcase '1':\n\t\tcase '2':\n\t\tcase '3':\n\t\tcase '4':\n\t\tcase '5':\n\t\tcase '6':\n\t\tcase '7':\n\t\tcase '8':\n\t\tcase '9':\n\t\tcase 'A':\n\t\tcase 'B':\n\t\tcase 'C':\n\t\tcase 'D':\n\t\tcase 'E':\n\t\tcase 'F':\n\t\tcase 'G':\n\t\tcase 'H':\n\t\tcase 'I':\n\t\tcase 'J':\n\t\tcase 'K':\n\t\tcase 'L':\n\t\tcase 'M':\n\t\tcase 'N':\n\t\tcase 'O':\n\t\tcase 'P':\n\t\tcase 'Q':\n\t\tcase 'R':\n\t\tcase 'S':\n\t\tcase 'T':\n\t\tcase 'U':\n\t\tcase 'V':\n\t\tcase 'W':\n\t\tcase 'X':\n\t\tcase 'Y':\n\t\tcase 'Z':\n\t\tcase '_':\n\t\tcase 'a':\n\t\tcase 'b':\n\t\tcase 'c':\n\t\tcase 'd':\n\t\tcase 'e':\n\t\tcase 'f':\n\t\tcase 'g':\n\t\tcase 'h':\n\t\tcase 'i':\n\t\tcase 'j':\n\t\tcase 'k':\n\t\tcase 'l':\n\t\tcase 'm':\n\t\tcase 'n':\n\t\tcase 'o':\n\t\tcase 'p':\n\t\tcase 'q':\n\t\tcase 'r':\n\t\tcase 's':\n\t\tcase 't':\n\t\tcase 'u':\n\t\tcase 'v':\n\t\tcase 'w':\n\t\tcase 'x':\n\t\tcase 'y':\n\t\tcase 'z': goto yy25;\n\t\tdefault: goto yy26;\n\t}\nyy26:\n#line 42 \"ext/pdo_sqlite/sqlite_sql_parser.re\"\n\t{ RET(PDO_PARSER_BIND); }\n#line 336 \"ext/pdo_sqlite/sqlite_sql_parser.c\"\nyy27:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase ':': goto yy27;\n\t\tdefault: goto yy28;\n\t}\nyy28:\n#line 41 \"ext/pdo_sqlite/sqlite_sql_parser.re\"\n\t{ RET(PDO_PARSER_TEXT); }\n#line 348 \"ext/pdo_sqlite/sqlite_sql_parser.c\"\nyy29:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase '?': goto yy29;\n\t\tdefault: goto yy28;\n\t}\nyy30:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\nyy31:\n\tswitch (yych) {\n\t\tcase 0x00: goto yy1;\n\t\tcase ']': goto yy32;\n\t\tdefault: goto yy30;\n\t}\nyy32:\n\tyyaccept = 3;\n\tYYMARKER = ++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase 0x00: goto yy33;\n\t\tcase ']': goto yy32;\n\t\tdefault: goto yy30;\n\t}\nyy33:\n#line 40 \"ext/pdo_sqlite/sqlite_sql_parser.re\"\n\t{ RET(PDO_PARSER_TEXT); }\n#line 380 \"ext/pdo_sqlite/sqlite_sql_parser.c\"\nyy34:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\nyy35:\n\tswitch (yych) {\n\t\tcase 0x00: goto yy1;\n\t\tcase '`': goto yy36;\n\t\tdefault: goto yy34;\n\t}\nyy36:\n\tyyaccept = 4;\n\tYYMARKER = ++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase 0x00: goto yy37;\n\t\tcase '`': goto yy36;\n\t\tdefault: goto yy34;\n\t}\nyy37:\n#line 39 \"ext/pdo_sqlite/sqlite_sql_parser.re\"\n\t{ RET(PDO_PARSER_TEXT); }\n#line 404 \"ext/pdo_sqlite/sqlite_sql_parser.c\"\nyy38:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase '*': goto yy38;\n\t\tcase '/': goto yy39;\n\t\tdefault: goto yy24;\n\t}\nyy39:\n\t++YYCURSOR;\n\tgoto yy23;\n}\n#line 47 \"ext/pdo_sqlite/sqlite_sql_parser.re\"\n\n}\n"
  },
  {
    "path": "thirdparty/pdo_sqlite/sqlite_statement.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_SQLITE_HOOK\n#include \"php_swoole_sqlite.h\"\n#include \"php_swoole_api.h\"\n\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"ext/pdo/php_pdo.h\"\n#include \"ext/pdo/php_pdo_driver.h\"\n#include \"php_pdo_sqlite.h\"\n#include \"php_pdo_sqlite_int.h\"\n\n#if defined(__APPLE__)\n// If more than one usage, a Zend macro could be created\n// around this runtime check\n#include <Availability.h>\n#endif\n\nstatic int pdo_sqlite_stmt_dtor(pdo_stmt_t *stmt)\n{\n\tpdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;\n\n\tif (S->stmt) {\n\t\tsqlite3_finalize(S->stmt);\n\t\tS->stmt = NULL;\n\t}\n\tefree(S);\n\treturn 1;\n}\n\nstatic int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt)\n{\n\tpdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;\n\n\tif (stmt->executed && !S->done) {\n\t\tsqlite3_reset(S->stmt);\n\t}\n\n\tS->done = 0;\n\tswitch (sqlite3_step(S->stmt)) {\n\t\tcase SQLITE_ROW:\n\t\t\tS->pre_fetched = 1;\n\t\t\tphp_pdo_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt));\n\t\t\treturn 1;\n\n\t\tcase SQLITE_DONE:\n\t\t\tphp_pdo_stmt_set_column_count(stmt, sqlite3_column_count(S->stmt));\n\t\t\tstmt->row_count = sqlite3_changes(S->H->db);\n\t\t\tsqlite3_reset(S->stmt);\n\t\t\tS->done = 1;\n\t\t\treturn 1;\n\n\t\tcase SQLITE_ERROR:\n\t\t\tsqlite3_reset(S->stmt);\n\t\t\tZEND_FALLTHROUGH;\n\t\tcase SQLITE_MISUSE:\n\t\tcase SQLITE_BUSY:\n\t\tdefault:\n\t\t\tpdo_sqlite_error_stmt(stmt);\n\t\t\treturn 0;\n\t}\n}\n\nstatic int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,\n\t\tenum pdo_param_event event_type)\n{\n\tpdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;\n\tzval *parameter;\n\n\tswitch (event_type) {\n\t\tcase PDO_PARAM_EVT_EXEC_PRE:\n\t\t\tif (stmt->executed && !S->done) {\n\t\t\t\tsqlite3_reset(S->stmt);\n\t\t\t\tS->done = 1;\n\t\t\t}\n\n\t\t\tif (param->is_param) {\n\n\t\t\t\tif (param->paramno == -1) {\n\t\t\t\t\tparam->paramno = sqlite3_bind_parameter_index(S->stmt, ZSTR_VAL(param->name)) - 1;\n\t\t\t\t}\n\n\t\t\t\tswitch (PDO_PARAM_TYPE(param->param_type)) {\n\t\t\t\t\tcase PDO_PARAM_STMT:\n\t\t\t\t\t\treturn 0;\n\n\t\t\t\t\tcase PDO_PARAM_NULL:\n\t\t\t\t\t\tif (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) {\n\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpdo_sqlite_error_stmt(stmt);\n\t\t\t\t\t\treturn 0;\n\n\t\t\t\t\tcase PDO_PARAM_INT:\n\t\t\t\t\tcase PDO_PARAM_BOOL:\n\t\t\t\t\t\tif (Z_ISREF(param->parameter)) {\n\t\t\t\t\t\t\tparameter = Z_REFVAL(param->parameter);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tparameter = &param->parameter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (Z_TYPE_P(parameter) == IS_NULL) {\n\t\t\t\t\t\t\tif (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) {\n\t\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconvert_to_long(parameter);\n#if ZEND_LONG_MAX > 2147483647\n\t\t\t\t\t\t\tif (SQLITE_OK == sqlite3_bind_int64(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) {\n\t\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t\t}\n#else\n\t\t\t\t\t\t\tif (SQLITE_OK == sqlite3_bind_int(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) {\n\t\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpdo_sqlite_error_stmt(stmt);\n\t\t\t\t\t\treturn 0;\n\n\t\t\t\t\tcase PDO_PARAM_LOB:\n\t\t\t\t\t\tif (Z_ISREF(param->parameter)) {\n\t\t\t\t\t\t\tparameter = Z_REFVAL(param->parameter);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tparameter = &param->parameter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (Z_TYPE_P(parameter) == IS_RESOURCE) {\n\t\t\t\t\t\t\tphp_stream *stm = NULL;\n\t\t\t\t\t\t\tphp_stream_from_zval_no_verify(stm, parameter);\n\t\t\t\t\t\t\tif (stm) {\n\t\t\t\t\t\t\t\tzend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0);\n\t\t\t\t\t\t\t\tzval_ptr_dtor(parameter);\n\t\t\t\t\t\t\t\tZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC());\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tpdo_raise_impl_error(stmt->dbh, stmt, \"HY105\", \"Expected a stream resource\");\n\t\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (Z_TYPE_P(parameter) == IS_NULL) {\n\t\t\t\t\t\t\tif (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) {\n\t\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tpdo_sqlite_error_stmt(stmt);\n\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (!try_convert_to_string(parameter)) {\n\t\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (SQLITE_OK == sqlite3_bind_blob(S->stmt, param->paramno + 1,\n\t\t\t\t\t\t\t\tZ_STRVAL_P(parameter),\n\t\t\t\t\t\t\t\tZ_STRLEN_P(parameter),\n\t\t\t\t\t\t\t\tSQLITE_STATIC)) {\n\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn 0;\n\n\t\t\t\t\tcase PDO_PARAM_STR:\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif (Z_ISREF(param->parameter)) {\n\t\t\t\t\t\t\tparameter = Z_REFVAL(param->parameter);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tparameter = &param->parameter;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (Z_TYPE_P(parameter) == IS_NULL) {\n\t\t\t\t\t\t\tif (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) {\n\t\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (!try_convert_to_string(parameter)) {\n\t\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (SQLITE_OK == sqlite3_bind_text(S->stmt, param->paramno + 1,\n\t\t\t\t\t\t\t\t\tZ_STRVAL_P(parameter),\n\t\t\t\t\t\t\t\t\tZ_STRLEN_P(parameter),\n\t\t\t\t\t\t\t\t\tSQLITE_STATIC)) {\n\t\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpdo_sqlite_error_stmt(stmt);\n\t\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\t;\n\t}\n\treturn 1;\n}\n\nstatic int pdo_sqlite_stmt_fetch(pdo_stmt_t *stmt,\n\tenum pdo_fetch_orientation ori, zend_long offset)\n{\n\tpdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;\n\tint i;\n\tif (!S->stmt) {\n\t\treturn 0;\n\t}\n\tif (S->pre_fetched) {\n\t\tS->pre_fetched = 0;\n\t\treturn 1;\n\t}\n\tif (S->done) {\n\t\treturn 0;\n\t}\n\ti = sqlite3_step(S->stmt);\n\tswitch (i) {\n\t\tcase SQLITE_ROW:\n\t\t\treturn 1;\n\n\t\tcase SQLITE_DONE:\n\t\t\tS->done = 1;\n\t\t\tsqlite3_reset(S->stmt);\n\t\t\treturn 0;\n\n\t\tcase SQLITE_ERROR:\n\t\t\tsqlite3_reset(S->stmt);\n\t\t\tZEND_FALLTHROUGH;\n\t\tdefault:\n\t\t\tpdo_sqlite_error_stmt(stmt);\n\t\t\treturn 0;\n\t}\n}\n\nstatic int pdo_sqlite_stmt_describe(pdo_stmt_t *stmt, int colno)\n{\n\tpdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;\n\tconst char *str;\n\n\tif(colno >= sqlite3_column_count(S->stmt)) {\n\t\t/* error invalid column */\n\t\tpdo_sqlite_error_stmt(stmt);\n\t\treturn 0;\n\t}\n\n\tstr = sqlite3_column_name(S->stmt, colno);\n\tstmt->columns[colno].name = zend_string_init(str, strlen(str), 0);\n\tstmt->columns[colno].maxlen = SIZE_MAX;\n\tstmt->columns[colno].precision = 0;\n\n\treturn 1;\n}\n\nstatic int pdo_sqlite_stmt_get_col(\n\t\tpdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type)\n{\n\tpdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;\n\tif (!S->stmt) {\n\t\treturn 0;\n\t}\n\tif(colno >= sqlite3_data_count(S->stmt)) {\n\t\t/* error invalid column */\n\t\tpdo_sqlite_error_stmt(stmt);\n\t\treturn 0;\n\t}\n\tswitch (sqlite3_column_type(S->stmt, colno)) {\n\t\tcase SQLITE_NULL:\n\t\t\tZVAL_NULL(result);\n\t\t\treturn 1;\n\n\t\tcase SQLITE_INTEGER: {\n\t\t\tint64_t i = sqlite3_column_int64(S->stmt, colno);\n#if SIZEOF_ZEND_LONG < 8\n\t\t\tif (i > ZEND_LONG_MAX || i < ZEND_LONG_MIN) {\n\t\t\t\tZVAL_STRINGL(result,\n\t\t\t\t\t(char *) sqlite3_column_text(S->stmt, colno),\n\t\t\t\t\tsqlite3_column_bytes(S->stmt, colno));\n\t\t\t\treturn 1;\n\t\t\t}\n#endif\n\t\t\tZVAL_LONG(result, i);\n\t\t\treturn 1;\n\t\t}\n\n\t\tcase SQLITE_FLOAT:\n\t\t\tZVAL_DOUBLE(result, sqlite3_column_double(S->stmt, colno));\n\t\t\treturn 1;\n\n\t\tcase SQLITE_BLOB:\n\t\t\tZVAL_STRINGL_FAST(result,\n\t\t\t\tsqlite3_column_blob(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno));\n\t\t\treturn 1;\n\n\t\tdefault:\n\t\t\tZVAL_STRINGL_FAST(result,\n\t\t\t\t(char *) sqlite3_column_text(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno));\n\t\t\treturn 1;\n\t}\n}\n\nstatic int pdo_sqlite_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)\n{\n\tpdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;\n\tconst char *str;\n\tzval flags;\n\n\tif (!S->stmt || !stmt->executed) {\n\t\treturn FAILURE;\n\t}\n\tif(colno >= sqlite3_column_count(S->stmt)) {\n\t\t/* error invalid column */\n\t\tpdo_sqlite_error_stmt(stmt);\n\t\treturn FAILURE;\n\t}\n\n\tarray_init(return_value);\n\tarray_init(&flags);\n\n\tswitch (sqlite3_column_type(S->stmt, colno)) {\n\t\tcase SQLITE_NULL:\n\t\t\tadd_assoc_str(return_value, \"native_type\", ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE));\n\t\t\tadd_assoc_long(return_value, \"pdo_type\", PDO_PARAM_NULL);\n\t\t\tbreak;\n\n\t\tcase SQLITE_FLOAT:\n\t\t\tadd_assoc_str(return_value, \"native_type\", ZSTR_KNOWN(ZEND_STR_DOUBLE));\n\t\t\tadd_assoc_long(return_value, \"pdo_type\", PDO_PARAM_STR);\n\t\t\tbreak;\n\n\t\tcase SQLITE_BLOB:\n\t\t\tadd_next_index_string(&flags, \"blob\");\n\t\t\tZEND_FALLTHROUGH;\n\t\tcase SQLITE_TEXT:\n\t\t\tadd_assoc_str(return_value, \"native_type\", ZSTR_KNOWN(ZEND_STR_STRING));\n\t\t\tadd_assoc_long(return_value, \"pdo_type\", PDO_PARAM_STR);\n\t\t\tbreak;\n\n\t\tcase SQLITE_INTEGER:\n\t\t\tadd_assoc_str(return_value, \"native_type\", ZSTR_KNOWN(ZEND_STR_INTEGER));\n\t\t\tadd_assoc_long(return_value, \"pdo_type\", PDO_PARAM_INT);\n\t\t\tbreak;\n\t}\n\n\tstr = sqlite3_column_decltype(S->stmt, colno);\n\tif (str) {\n\t\tadd_assoc_string(return_value, \"sqlite:decl_type\", (char *)str);\n\t}\n\n#ifdef HAVE_SQLITE3_COLUMN_TABLE_NAME\n\tstr = sqlite3_column_table_name(S->stmt, colno);\n\tif (str) {\n\t\tadd_assoc_string(return_value, \"table\", (char *)str);\n\t}\n#endif\n\n\tadd_assoc_zval(return_value, \"flags\", &flags);\n\n\treturn SUCCESS;\n}\n\nstatic int pdo_sqlite_stmt_cursor_closer(pdo_stmt_t *stmt)\n{\n\tpdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;\n\tsqlite3_reset(S->stmt);\n\treturn 1;\n}\n\nstatic int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val)\n{\n\tpdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;\n\n\tswitch (attr) {\n\t\tcase PDO_SQLITE_ATTR_READONLY_STATEMENT:\n\t\t\tZVAL_FALSE(val);\n\n\t\t\tif (sqlite3_stmt_readonly(S->stmt)) {\n\t\t\t\tZVAL_TRUE(val);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase PDO_SQLITE_ATTR_BUSY_STATEMENT:\n\t\t\tZVAL_FALSE(val);\n\n\t\t\tif (sqlite3_stmt_busy(S->stmt)) {\n\t\t\t\tZVAL_TRUE(val);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase PDO_SQLITE_ATTR_EXPLAIN_STATEMENT:\n#if SQLITE_VERSION_NUMBER >= 3043000\n#if defined(__APPLE__)\n\t\t\tif (__builtin_available(macOS 14.2, *)) {\n#endif\n\t\t\t\tZVAL_LONG(val, (zend_long)sqlite3_stmt_isexplain(S->stmt));\n\t\t\t\treturn 1;\n#if defined(__APPLE__)\n\t\t\t} else {\n\t\t\t\tzend_value_error(\"explain statement unsupported\");\n\t\t\t\treturn 0;\n\t\t\t}\n#endif\n#else\n\t\t\tzend_value_error(\"explain statement unsupported\");\n\t\t\treturn 0;\n#endif\n\t\tdefault:\n\t\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\nstatic int pdo_sqlite_stmt_set_attribute(pdo_stmt_t *stmt, zend_long attr, zval *zval)\n{\n\tswitch (attr) {\n\t\tcase PDO_SQLITE_ATTR_EXPLAIN_STATEMENT:\n#if SQLITE_VERSION_NUMBER >= 3043000\n#if defined(__APPLE__)\n\t\t\tif (__builtin_available(macOS 14.2, *)) {\n#endif\n\t\t\t\tif (Z_TYPE_P(zval) != IS_LONG) {\n\t\t\t\t\tzend_type_error(\"explain mode must be of type int, %s given\", zend_zval_value_name(zval));\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tif (Z_LVAL_P(zval) < 0 || Z_LVAL_P(zval) > 2) {\n\t\t\t\t\tzend_value_error(\"explain mode must be one of the Pdo\\\\Sqlite::EXPLAIN_MODE_* constants\");\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\n\t\t\t\tpdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data;\n\t\t\t\tif (sqlite3_stmt_explain(S->stmt, (int)Z_LVAL_P(zval)) != SQLITE_OK) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\n\t\t\t\treturn 1;\n#if defined(__APPLE__)\n\t\t\t} else {\n\t\t\t\tzend_value_error(\"explain statement unsupported\");\n\t\t\t\treturn 0;\n\t\t\t}\n#endif\n#else\n\t\t\tzend_value_error(\"explain statement unsupported\");\n\t\t\treturn 0;\n#endif\n\t\tdefault:\n\t\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\nconst struct pdo_stmt_methods swoole_sqlite_stmt_methods = {\n\tpdo_sqlite_stmt_dtor,\n\tpdo_sqlite_stmt_execute,\n\tpdo_sqlite_stmt_fetch,\n\tpdo_sqlite_stmt_describe,\n\tpdo_sqlite_stmt_get_col,\n\tpdo_sqlite_stmt_param_hook,\n\tpdo_sqlite_stmt_set_attribute, /* set_attr */\n\tpdo_sqlite_stmt_get_attribute, /* get_attr */\n\tpdo_sqlite_stmt_col_meta,\n\tNULL, /* next_rowset */\n\tpdo_sqlite_stmt_cursor_closer\n};\n"
  },
  {
    "path": "thirdparty/php/LICENSE",
    "content": "--------------------------------------------------------------------\n                  The PHP License, version 3.01\nCopyright (c) 1999 - 2022 The PHP Group. All rights reserved.\n--------------------------------------------------------------------\n\nRedistribution and use in source and binary forms, with or without\nmodification, is permitted provided that the following conditions\nare met:\n\n  1. Redistributions of source code must retain the above copyright\n     notice, this list of conditions and the following disclaimer.\n\n  2. Redistributions in binary form must reproduce the above copyright\n     notice, this list of conditions and the following disclaimer in\n     the documentation and/or other materials provided with the\n     distribution.\n\n  3. The name \"PHP\" must not be used to endorse or promote products\n     derived from this software without prior written permission. For\n     written permission, please contact group@php.net.\n\n  4. Products derived from this software may not be called \"PHP\", nor\n     may \"PHP\" appear in their name, without prior written permission\n     from group@php.net.  You may indicate that your software works in\n     conjunction with PHP by saying \"Foo for PHP\" instead of calling\n     it \"PHP Foo\" or \"phpfoo\"\n\n  5. The PHP Group may publish revised and/or new versions of the\n     license from time to time. Each version will be given a\n     distinguishing version number.\n     Once covered code has been published under a particular version\n     of the license, you may always continue to use it under the terms\n     of that version. You may also choose to use such covered code\n     under the terms of any subsequent version of the license\n     published by the PHP Group. No one other than the PHP Group has\n     the right to modify the terms applicable to covered code created\n     under this License.\n\n  6. Redistributions of any form whatsoever must retain the following\n     acknowledgment:\n     \"This product includes PHP software, freely available from\n     <http://www.php.net/software/>\".\n\nTHIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND\nANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\nPARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE PHP\nDEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\nINDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\nHOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\nSTRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\nOF THE POSSIBILITY OF SUCH DAMAGE.\n\n--------------------------------------------------------------------\n\nThis software consists of voluntary contributions made by many\nindividuals on behalf of the PHP Group.\n\nThe PHP Group can be contacted via Email at group@php.net.\n\nFor more information on the PHP Group and the PHP project,\nplease see <http://www.php.net>.\n\nPHP includes the Zend Engine, freely available at\n<http://www.zend.com>.\n"
  },
  {
    "path": "thirdparty/php/curl/curl_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: f1d616c644ad366405816cde0384f6f391773ebf */\n\n#include \"swoole_curl_interface.h\"\n\n#if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_close, 0, 1, IS_VOID, 0)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_swoole_native_curl_copy_handle, 0, 1, CurlHandle, MAY_BE_FALSE)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_errno, 0, 1, IS_LONG, 0)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_error, 0, 1, IS_STRING, 0)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_escape, 0, 2, MAY_BE_STRING|MAY_BE_FALSE)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\n\tZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_native_curl_unescape arginfo_swoole_native_curl_escape\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_exec, 0, 1, MAY_BE_STRING|MAY_BE_BOOL)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_swoole_native_curl_file_create, 0, 1, CURLFile, 0)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mime_type, IS_STRING, 1, \"null\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, posted_filename, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_getinfo, 0, 1, IS_MIXED, 0)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, option, IS_LONG, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_swoole_native_curl_init, 0, 0, CurlHandle, MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, url, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\n#if LIBCURL_VERSION_NUM >= 0x073E00 && PHP_VERSION_ID >= 80200 /* Available since 7.62.0 */\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_upkeep, 0, 1, _IS_BOOL, 0)\n        ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\nZEND_END_ARG_INFO()\n#endif\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_add_handle, 0, 2, IS_LONG, 0)\n\tZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_close, 0, 1, IS_VOID, 0)\n\tZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_errno, 0, 1, IS_LONG, 0)\n\tZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_exec, 0, 2, IS_LONG, 0)\n\tZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)\n\tZEND_ARG_INFO(1, still_running)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_getcontent, 0, 1, IS_STRING, 1)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_multi_info_read, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)\n\tZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, queued_messages, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_swoole_native_curl_multi_init, 0, 0, CurlMultiHandle, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_native_curl_multi_remove_handle arginfo_swoole_native_curl_multi_add_handle\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_select, 0, 1, IS_LONG, 0)\n    ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)\n    ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"1.0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_setopt, 0, 3, _IS_BOOL, 0)\n    ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)\n    ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)\n    ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_pause, 0, 2, IS_LONG, 0)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\n\tZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_native_curl_reset arginfo_swoole_native_curl_close\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_setopt_array, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\n\tZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_setopt, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\n\tZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nstatic const zend_function_entry swoole_native_curl_functions[] = {\n    PHP_FE(swoole_native_curl_close, arginfo_swoole_native_curl_close)\n    PHP_FE(swoole_native_curl_copy_handle, arginfo_swoole_native_curl_copy_handle)\n    PHP_FE(swoole_native_curl_errno, arginfo_swoole_native_curl_errno)\n    PHP_FE(swoole_native_curl_error, arginfo_swoole_native_curl_error)\n    PHP_FE(swoole_native_curl_exec, arginfo_swoole_native_curl_exec)\n    PHP_FE(swoole_native_curl_getinfo, arginfo_swoole_native_curl_getinfo)\n    PHP_FE(swoole_native_curl_init, arginfo_swoole_native_curl_init)\n#if LIBCURL_VERSION_NUM >= 0x073E00 && PHP_VERSION_ID >= 80200\n    PHP_FE(swoole_native_curl_upkeep, arginfo_swoole_native_curl_upkeep)\n#endif\n    PHP_FE(swoole_native_curl_setopt, arginfo_swoole_native_curl_setopt)\n    PHP_FE(swoole_native_curl_setopt_array, arginfo_swoole_native_curl_setopt_array)\n    PHP_FE(swoole_native_curl_reset, arginfo_swoole_native_curl_reset)\n    PHP_FE(swoole_native_curl_escape, arginfo_swoole_native_curl_escape)\n    PHP_FE(swoole_native_curl_unescape, arginfo_swoole_native_curl_unescape)\n    PHP_FE(swoole_native_curl_pause, arginfo_swoole_native_curl_pause)\n    PHP_FE(swoole_native_curl_multi_add_handle, arginfo_swoole_native_curl_multi_add_handle)\n    PHP_FE(swoole_native_curl_multi_close, arginfo_swoole_native_curl_multi_close)\n    PHP_FE(swoole_native_curl_multi_errno, arginfo_swoole_native_curl_multi_errno)\n    PHP_FE(swoole_native_curl_multi_exec, arginfo_swoole_native_curl_multi_exec)\n    PHP_FE(swoole_native_curl_multi_select, arginfo_swoole_native_curl_multi_select)\n    PHP_FE(swoole_native_curl_multi_setopt, arginfo_swoole_native_curl_multi_setopt)\n    PHP_FE(swoole_native_curl_multi_getcontent, arginfo_swoole_native_curl_multi_getcontent)\n    PHP_FE(swoole_native_curl_multi_info_read, arginfo_swoole_native_curl_multi_info_read)\n    PHP_FE(swoole_native_curl_multi_init, arginfo_swoole_native_curl_multi_init)\n    PHP_FE(swoole_native_curl_multi_remove_handle, arginfo_swoole_native_curl_multi_remove_handle)\n    PHP_FE_END\n};\n#endif\n"
  },
  {
    "path": "thirdparty/php/curl/curl_private.h",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | Copyright (c) The PHP Group                                          |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | https://www.php.net/license/3_01.txt                                 |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Author: Sterling Hughes <sterling@php.net>                           |\n   |         Wez Furlong <wez@thebrainroom.com>                           |\n   +----------------------------------------------------------------------+\n*/\n\n#if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400\n\n#ifndef _PHP_CURL_PRIVATE_H\n#define _PHP_CURL_PRIVATE_H\n\n#include \"php_curl.h\"\n\n#define PHP_CURL_DEBUG 0\n\n#include \"php_version.h\"\n#define PHP_CURL_VERSION PHP_VERSION\n\n#include <curl/curl.h>\n#include <curl/multi.h>\n\n#define CURLOPT_RETURNTRANSFER 19913\n#define CURLOPT_BINARYTRANSFER 19914 /* For Backward compatibility */\n#define PHP_CURL_STDOUT 0\n#define PHP_CURL_FILE 1\n#define PHP_CURL_USER 2\n#define PHP_CURL_DIRECT 3\n#define PHP_CURL_RETURN 4\n#define PHP_CURL_IGNORE 7\n\n#define SAVE_CURL_ERROR(__handle, __err)                                                                               \\\n    do {                                                                                                               \\\n        (__handle)->err.no = (int) __err;                                                                              \\\n    } while (0)\n\ntypedef struct {\n    zval func_name;\n    zend_fcall_info_cache fci_cache;\n    FILE *fp;\n    smart_str buf;\n    int method;\n    zval stream;\n} php_curl_write;\n\ntypedef struct {\n    zval func_name;\n    zend_fcall_info_cache fci_cache;\n    FILE *fp;\n    zend_resource *res;\n    int method;\n    zval stream;\n} php_curl_read;\n\ntypedef struct {\n    zval func_name;\n    zend_fcall_info_cache fci_cache;\n} php_curl_callback;\n\ntypedef struct {\n    php_curl_write *write;\n    php_curl_write *write_header;\n    php_curl_read *read;\n    zval std_err;\n    php_curl_callback *progress;\n#if PHP_VERSION_ID >= 80200\n    php_curl_callback *xferinfo;\n#endif\n    php_curl_callback *fnmatch;\n#if LIBCURL_VERSION_NUM >= 0x075400 && PHP_VERSION_ID >= 80300\n    php_curl_callback *sshhostkey;\n#endif\n} php_curl_handlers;\n\nstruct _php_curl_error {\n    char str[CURL_ERROR_SIZE + 1];\n    int no;\n};\n\nstruct _php_curl_send_headers {\n    zend_string *str;\n};\n\nstruct _php_curl_free {\n    zend_llist post;\n    zend_llist stream;\n    HashTable *slist;\n};\n\ntypedef struct {\n    CURL *cp;\n    php_curl_handlers handlers;\n    struct _php_curl_free *to_free;\n    struct _php_curl_send_headers header;\n    struct _php_curl_error err;\n    bool in_callback;\n    uint32_t *clone;\n    zval postfields;\n    /* For CURLOPT_PRIVATE */\n    zval private_data;\n    /* CurlShareHandle object set using CURLOPT_SHARE. */\n    struct _php_curlsh *share;\n    zend_object std;\n} php_curl;\n\n#define CURLOPT_SAFE_UPLOAD -1\n\ntypedef struct {\n    php_curl_callback *server_push;\n} php_curlm_handlers;\n\nnamespace swoole {\nnamespace curl {\nclass Multi;\n}\n}  // namespace swoole\n\nusing swoole::curl::Multi;\n\ntypedef struct {\n    Multi *multi;\n    zend_llist easyh;\n    php_curlm_handlers handlers;\n    struct {\n        int no;\n    } err;\n    zend_object std;\n} php_curlm;\n\ntypedef struct _php_curlsh {\n    CURLSH *share;\n    struct {\n        int no;\n    } err;\n    zend_object std;\n} php_curlsh;\n\nphp_curl *swoole_curl_init_handle_into_zval(zval *curl);\nvoid swoole_curl_init_handle(php_curl *ch);\nvoid swoole_curl_cleanup_handle(php_curl *);\nvoid swoole_curl_multi_cleanup_list(void *data);\nvoid swoole_curl_verify_handlers(php_curl *ch, bool reporterror);\nvoid swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source);\n\nstatic inline php_curl_handlers *curl_handlers(php_curl *ch) {\n    return &ch->handlers;\n}\n\n#if PHP_VERSION_ID >= 80300\n/* Consumes `zv` */\nzend_long swoole_curl_get_long(zval *zv);\n#endif\n\nstatic inline php_curl *curl_from_obj(zend_object *obj) {\n    return (php_curl *) ((char *) (obj) -XtOffsetOf(php_curl, std));\n}\n\n#define Z_CURL_P(zv) curl_from_obj(Z_OBJ_P(zv))\n\nstatic inline php_curlsh *curl_share_from_obj(zend_object *obj) {\n    return (php_curlsh *) ((char *) (obj) -XtOffsetOf(php_curlsh, std));\n}\n\n#define Z_CURL_SHARE_P(zv) curl_share_from_obj(Z_OBJ_P(zv))\nvoid curl_multi_register_class(const zend_function_entry *method_entries);\n\n#if PHP_VERSION_ID >= 80200\nzend_result swoole_curl_cast_object(zend_object *obj, zval *result, int type);\n#else\nint swoole_curl_cast_object(zend_object *obj, zval *result, int type);\n#endif\n\n#endif /* _PHP_CURL_PRIVATE_H */\n#endif\n"
  },
  {
    "path": "thirdparty/php/curl/interface.cc",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | Copyright (c) The PHP Group                                          |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | https://www.php.net/license/3_01.txt                                 |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Author: Sterling Hughes <sterling@php.net>                           |\n   |         Tianfeng Han  <rango@swoole.com>                             |\n   +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_cxx.h\"\n\n#if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400\n#include \"php_swoole_curl.h\"\n\nusing namespace swoole;\n\nSW_EXTERN_C_BEGIN\n#include \"swoole_curl_interface.h\"\n#include \"curl_arginfo.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include <curl/curl.h>\n#include <curl/easy.h>\n\n/* As of curl 7.11.1 this is no longer defined inside curl.h */\n#ifndef HttpPost\n#define HttpPost curl_httppost\n#endif\n\n/* {{{ cruft for thread safe SSL crypto locks */\n#if defined(ZTS) && defined(HAVE_CURL_OLD_OPENSSL)\n#if defined(HAVE_OPENSSL_CRYPTO_H)\n#define PHP_CURL_NEED_OPENSSL_TSL\n#include <openssl/crypto.h>\n#else\n#warning \"libcurl was compiled with OpenSSL support, but configure could not find \" \\\n    \"openssl/crypto.h; thus no SSL crypto locking callbacks will be set, which may \" \\\n    \"cause random crashes on SSL requests\"\n#endif\n#endif /* ZTS && HAVE_CURL_OLD_OPENSSL */\n/* }}} */\n\n#include \"zend_smart_str.h\"\n#include \"ext/standard/info.h\"\n#include \"ext/standard/file.h\"\n#include \"ext/standard/url.h\"\n#include \"curl_private.h\"\n\n#ifdef __GNUC__\n/* don't complain about deprecated CURLOPT_* we're exposing to PHP; we\n   need to keep using those to avoid breaking PHP API compatibiltiy */\n#pragma GCC diagnostic ignored \"-Wdeprecated-declarations\"\n#endif\n\n#ifdef PHP_CURL_NEED_OPENSSL_TSL /* {{{ */\nstatic MUTEX_T *php_curl_openssl_tsl = NULL;\n\n/* Locking callbacks are no longer used since OpenSSL 1.1. Mark the functions as unused to\n * avoid warnings due to this. */\nstatic ZEND_ATTRIBUTE_UNUSED void php_curl_ssl_lock(int mode, int n, const char *file, int line) {\n    if (mode & CRYPTO_LOCK) {\n        tsrm_mutex_lock(php_curl_openssl_tsl[n]);\n    } else {\n        tsrm_mutex_unlock(php_curl_openssl_tsl[n]);\n    }\n}\n\nstatic ZEND_ATTRIBUTE_UNUSED unsigned long php_curl_ssl_id(void) {\n    return (unsigned long) tsrm_thread_id();\n}\n#endif\n\nstatic zend_class_entry *swoole_native_curl_exception_ce;\nstatic zend_object_handlers swoole_native_curl_exception_handlers;\n\n#define CAAL(s, v) add_assoc_long_ex(return_value, s, sizeof(s) - 1, (zend_long) v);\n#define CAAD(s, v) add_assoc_double_ex(return_value, s, sizeof(s) - 1, (double) v);\n#define CAAS(s, v) add_assoc_string_ex(return_value, s, sizeof(s) - 1, (char *) (v ? v : \"\"));\n#define CAASTR(s, v) add_assoc_str_ex(return_value, s, sizeof(s) - 1, v ? zend_string_copy(v) : ZSTR_EMPTY_ALLOC());\n#define CAAZ(s, v) add_assoc_zval_ex(return_value, s, sizeof(s) - 1, (zval *) v);\n\n#if defined(PHP_WIN32) || defined(__GNUC__)\n#define php_curl_ret(__ret)                                                                                            \\\n    RETVAL_FALSE;                                                                                                      \\\n    return __ret;\n#else\n#define php_curl_ret(__ret)                                                                                            \\\n    RETVAL_FALSE;                                                                                                      \\\n    return;\n#endif\n\nstatic zend_result php_curl_option_str(php_curl *ch, zend_long option, const char *str, const size_t len) {\n#if PHP_VERSION_ID >= 80300\n    if (zend_char_has_nul_byte(str, len)) {\n#else\n    if (strlen(str) != len) {\n#endif\n        zend_value_error(\"%s(): cURL option must not contain any null bytes\", get_active_function_name());\n        return FAILURE;\n    }\n\n    CURLcode error = curl_easy_setopt(ch->cp, (CURLoption) option, str);\n    SAVE_CURL_ERROR(ch, error);\n\n    return error == CURLE_OK ? SUCCESS : FAILURE;\n}\n#if PHP_VERSION_ID >= 80300\nstatic zend_result php_curl_option_url(php_curl *ch, const zend_string *url) /* {{{ */\n#else\nstatic zend_result php_curl_option_url(php_curl *ch, const char *url, const size_t len) /* {{{ */\n#endif\n{\n    /* Disable file:// if open_basedir are used */\n    if (PG(open_basedir) && *PG(open_basedir)) {\n        curl_easy_setopt(ch->cp, CURLOPT_PROTOCOLS, CURLPROTO_ALL & ~CURLPROTO_FILE);\n    }\n\n#if PHP_VERSION_ID >= 80300\n    return php_curl_option_str(ch, CURLOPT_URL, ZSTR_VAL(url), ZSTR_LEN(url));\n#else\n    return php_curl_option_str(ch, CURLOPT_URL, url, len);\n#endif\n}\n/* }}} */\n\nvoid swoole_curl_verify_handlers(php_curl *ch, bool reporterror) /* {{{ */\n{\n    php_stream *stream;\n\n    ZEND_ASSERT(ch);\n\n    if (!Z_ISUNDEF(ch->handlers.std_err)) {\n        stream = (php_stream *) zend_fetch_resource2_ex(\n            &ch->handlers.std_err, NULL, php_file_le_stream(), php_file_le_pstream());\n        if (stream == NULL) {\n            if (reporterror) {\n                php_error_docref(NULL, E_WARNING, \"CURLOPT_STDERR resource has gone away, resetting to stderr\");\n            }\n            zval_ptr_dtor(&ch->handlers.std_err);\n            ZVAL_UNDEF(&ch->handlers.std_err);\n\n            curl_easy_setopt(ch->cp, CURLOPT_STDERR, stderr);\n        }\n    }\n    if (ch->handlers.read && !Z_ISUNDEF(ch->handlers.read->stream)) {\n        stream = (php_stream *) zend_fetch_resource2_ex(\n            &ch->handlers.read->stream, NULL, php_file_le_stream(), php_file_le_pstream());\n        if (stream == NULL) {\n            if (reporterror) {\n                php_error_docref(NULL, E_WARNING, \"CURLOPT_INFILE resource has gone away, resetting to default\");\n            }\n            zval_ptr_dtor(&ch->handlers.read->stream);\n            ZVAL_UNDEF(&ch->handlers.read->stream);\n            ch->handlers.read->res = NULL;\n            ch->handlers.read->fp = 0;\n\n            curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch);\n        }\n    }\n    if (ch->handlers.write_header && !Z_ISUNDEF(ch->handlers.write_header->stream)) {\n        stream = (php_stream *) zend_fetch_resource2_ex(\n            &ch->handlers.write_header->stream, NULL, php_file_le_stream(), php_file_le_pstream());\n        if (stream == NULL) {\n            if (reporterror) {\n                php_error_docref(NULL, E_WARNING, \"CURLOPT_WRITEHEADER resource has gone away, resetting to default\");\n            }\n            zval_ptr_dtor(&ch->handlers.write_header->stream);\n            ZVAL_UNDEF(&ch->handlers.write_header->stream);\n            ch->handlers.write_header->fp = 0;\n\n            ch->handlers.write_header->method = PHP_CURL_IGNORE;\n            curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch);\n        }\n    }\n    if (ch->handlers.write && !Z_ISUNDEF(ch->handlers.write->stream)) {\n        stream = (php_stream *) zend_fetch_resource2_ex(\n            &ch->handlers.write->stream, NULL, php_file_le_stream(), php_file_le_pstream());\n        if (stream == NULL) {\n            if (reporterror) {\n                php_error_docref(NULL, E_WARNING, \"CURLOPT_FILE resource has gone away, resetting to default\");\n            }\n            zval_ptr_dtor(&ch->handlers.write->stream);\n            ZVAL_UNDEF(&ch->handlers.write->stream);\n            ch->handlers.write->fp = 0;\n\n            ch->handlers.write->method = PHP_CURL_STDOUT;\n            curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch);\n        }\n    }\n    return;\n}\n/* }}} */\n\n/* CurlHandle class */\nstatic const zend_function_entry swoole_coroutine_curl_handle_methods[] = {ZEND_FE_END};\n\nzend_class_entry *swoole_coroutine_curl_handle_ce;\nstatic zend_object_handlers swoole_coroutine_curl_handle_handlers;\n\nstatic zend_object *swoole_curl_create_object(zend_class_entry *class_type);\nstatic void swoole_curl_free_obj(zend_object *object);\nstatic HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n);\nstatic zend_function *swoole_curl_get_constructor(zend_object *object);\nstatic zend_object *swoole_curl_clone_obj(zend_object *object);\nphp_curl *swoole_curl_init_handle_into_zval(zval *curl);\nstatic inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields);\n\nSW_EXTERN_C_END\n\nvoid swoole_native_curl_minit(int module_number) {\n    if (!SWOOLE_G(cli)) {\n        return;\n    }\n\n#ifdef PHP_CURL_NEED_OPENSSL_TSL\n    if (!CRYPTO_get_id_callback()) {\n        int i, c = CRYPTO_num_locks();\n\n        php_curl_openssl_tsl = (MUTEX_T *) malloc(c * sizeof(MUTEX_T));\n        if (!php_curl_openssl_tsl) {\n            return;\n        }\n\n        for (i = 0; i < c; ++i) {\n            php_curl_openssl_tsl[i] = tsrm_mutex_alloc();\n        }\n\n        CRYPTO_set_id_callback(php_curl_ssl_id);\n        CRYPTO_set_locking_callback(php_curl_ssl_lock);\n    }\n#endif\n\n    swoole_coroutine_curl_handle_ce = curl_ce;\n    swoole_coroutine_curl_handle_ce->create_object = swoole_curl_create_object;\n#if PHP_VERSION_ID >= 80300\n    swoole_coroutine_curl_handle_ce->default_object_handlers = &swoole_coroutine_curl_handle_handlers;\n#endif\n    memcpy(&swoole_coroutine_curl_handle_handlers, &std_object_handlers, sizeof(zend_object_handlers));\n    swoole_coroutine_curl_handle_handlers.offset = XtOffsetOf(php_curl, std);\n    swoole_coroutine_curl_handle_handlers.free_obj = swoole_curl_free_obj;\n    swoole_coroutine_curl_handle_handlers.get_gc = swoole_curl_get_gc;\n    swoole_coroutine_curl_handle_handlers.get_constructor = swoole_curl_get_constructor;\n    swoole_coroutine_curl_handle_handlers.clone_obj = swoole_curl_clone_obj;\n    swoole_coroutine_curl_handle_handlers.cast_object = swoole_curl_cast_object;\n    swoole_coroutine_curl_handle_handlers.compare = [](zval *o1, zval *o2) { return ZEND_UNCOMPARABLE; };\n\n    swoole_coroutine_curl_handle_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES;\n\n    zend_declare_property_null(swoole_coroutine_curl_handle_ce, ZEND_STRL(\"private_data\"), ZEND_ACC_PUBLIC);\n\n    curl_multi_register_class(nullptr);\n\n    zend_unregister_functions(swoole_native_curl_functions, -1, CG(function_table));\n    zend_register_functions(NULL, swoole_native_curl_functions, NULL, MODULE_PERSISTENT);\n\n    SW_INIT_CLASS_ENTRY_EX(swoole_native_curl_exception,\n                           \"Swoole\\\\Coroutine\\\\Curl\\\\Exception\",\n                           \"Co\\\\Coroutine\\\\Curl\\\\Exception\",\n                           nullptr,\n                           swoole_exception);\n}\n\n/* CurlHandle class */\n\nstatic zend_object *swoole_curl_create_object(zend_class_entry *class_type) {\n    php_curl *intern = (php_curl *) zend_object_alloc(sizeof(php_curl), class_type);\n\n    zend_object_std_init(&intern->std, class_type);\n    object_properties_init(&intern->std, class_type);\n#if PHP_VERSION_ID < 80300\n    intern->std.handlers = &swoole_coroutine_curl_handle_handlers;\n#endif\n\n    return &intern->std;\n}\n\nstatic zend_function *swoole_curl_get_constructor(zend_object *object) {\n    zend_throw_error(NULL, \"Cannot directly construct CurlHandle, use curl_init() instead\");\n    return NULL;\n}\n\nstatic zend_object *swoole_curl_clone_obj(zend_object *object) {\n    php_curl *ch;\n    CURL *cp;\n    zval *postfields;\n    zend_object *clone_object;\n    php_curl *clone_ch;\n\n    clone_object = swoole_curl_create_object(curl_ce);\n    clone_ch = curl_from_obj(clone_object);\n\n    ch = curl_from_obj(object);\n    cp = curl_easy_duphandle(ch->cp);\n    if (!cp) {\n        zend_throw_exception(NULL, \"Failed to clone CurlHandle\", 0);\n        return &clone_ch->std;\n    }\n\n    swoole_curl_init_handle(clone_ch);\n\n    clone_ch->cp = cp;\n    swoole_setup_easy_copy_handlers(clone_ch, ch);\n    swoole::curl::create_handle(clone_ch->cp);\n\n    postfields = &ch->postfields;\n    if (Z_TYPE_P(postfields) != IS_UNDEF) {\n        if (build_mime_structure_from_hash(clone_ch, postfields) == FAILURE) {\n            zend_throw_exception(NULL, \"Failed to clone CurlHandle\", 0);\n            return &clone_ch->std;\n        }\n    }\n\n    return &clone_ch->std;\n}\n\nstatic HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n) {\n    php_curl *curl = curl_from_obj(object);\n\n    zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();\n\n    zend_get_gc_buffer_add_zval(gc_buffer, &curl->postfields);\n    if (curl->handlers.read) {\n        zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.read->func_name);\n        zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.read->stream);\n    }\n\n    if (curl->handlers.write) {\n        zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write->func_name);\n        zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write->stream);\n    }\n\n    if (curl->handlers.write_header) {\n        zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write_header->func_name);\n        zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write_header->stream);\n    }\n\n    if (curl->handlers.progress) {\n        zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.progress->func_name);\n    }\n\n#if PHP_VERSION_ID >= 80200\n    if (curl->handlers.xferinfo) {\n        zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.xferinfo->func_name);\n    }\n#endif\n\n    if (curl->handlers.fnmatch) {\n        zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.fnmatch->func_name);\n    }\n\n#if LIBCURL_VERSION_NUM >= 0x075400 && PHP_VERSION_ID >= 80300\n    if (curl->handlers.sshhostkey) {\n        zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.sshhostkey->func_name);\n    }\n#endif\n\n    zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.std_err);\n    zend_get_gc_buffer_add_zval(gc_buffer, &curl->private_data);\n\n    zend_get_gc_buffer_use(gc_buffer, table, n);\n\n    return zend_std_get_properties(object);\n}\n\n#if PHP_VERSION_ID >= 80200\nzend_result swoole_curl_cast_object(zend_object *obj, zval *result, int type) {\n#else\nint swoole_curl_cast_object(zend_object *obj, zval *result, int type) {\n#endif\n    if (type == IS_LONG) {\n        /* For better backward compatibility, make (int) $curl_handle return the object ID,\n         * similar to how it previously returned the resource ID. */\n        ZVAL_LONG(result, obj->handle);\n        return SUCCESS;\n    }\n\n    return zend_std_cast_object_tostring(obj, result, type);\n}\n\nvoid swoole_native_curl_mshutdown() {}\n\n/* {{{ curl_write_nothing\n * Used as a work around. See _php_curl_close_ex\n */\nstatic size_t fn_write_nothing(char *data, size_t size, size_t nmemb, void *ctx) {\n    return size * nmemb;\n}\n/* }}} */\n\n/* {{{ curl_write\n */\nstatic size_t fn_write(char *data, size_t size, size_t nmemb, void *ctx) {\n    php_curl *ch = (php_curl *) ctx;\n    php_curl_write *t = ch->handlers.write;\n    size_t length = size * nmemb;\n\n#if PHP_CURL_DEBUG\n    fprintf(stderr, \"curl_write() called\\n\");\n    fprintf(stderr, \"data = %s, size = %d, nmemb = %d, ctx = %x\\n\", data, size, nmemb, ctx);\n#endif\n\n    switch (t->method) {\n    case PHP_CURL_STDOUT:\n        PHPWRITE(data, length);\n        break;\n    case PHP_CURL_FILE:\n        return fwrite(data, size, nmemb, t->fp);\n    case PHP_CURL_RETURN:\n        if (length > 0) {\n            smart_str_appendl(&t->buf, data, (int) length);\n        }\n        break;\n    case PHP_CURL_USER: {\n        zval argv[2];\n        zval retval;\n        int error;\n        zend_fcall_info fci;\n\n        GC_ADDREF(&ch->std);\n        ZVAL_OBJ(&argv[0], &ch->std);\n        ZVAL_STRINGL(&argv[1], data, length);\n\n        fci.size = sizeof(fci);\n        fci.object = NULL;\n        ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);\n        fci.retval = &retval;\n        fci.param_count = 2;\n        fci.params = argv;\n        fci.named_params = NULL;\n        ch->in_callback = 1;\n        error = zend_call_function(&fci, &t->fci_cache);\n        ch->in_callback = 0;\n        if (error == FAILURE) {\n            php_error_docref(NULL, E_WARNING, \"Could not call the CURLOPT_WRITEFUNCTION\");\n            length = -1;\n        } else if (!Z_ISUNDEF(retval)) {\n            swoole_curl_verify_handlers(ch, /* reporterror */ true);\n#if PHP_VERSION_ID >= 80300\n            length = swoole_curl_get_long(&retval);\n#else\n            length = zval_get_long(&retval);\n#endif\n        }\n\n        zval_ptr_dtor(&argv[0]);\n        zval_ptr_dtor(&argv[1]);\n        break;\n    }\n    }\n\n    return length;\n}\n/* }}} */\n\n/* {{{ curl_fnmatch\n */\nstatic int fn_fnmatch(void *ctx, const char *pattern, const char *string) {\n    php_curl *ch = (php_curl *) ctx;\n    php_curl_callback *t = ch->handlers.fnmatch;\n    int rval = CURL_FNMATCHFUNC_FAIL;\n    zval argv[3];\n    zval retval;\n    zend_result error;\n    zend_fcall_info fci;\n\n    GC_ADDREF(&ch->std);\n    ZVAL_OBJ(&argv[0], &ch->std);\n    ZVAL_STRING(&argv[1], pattern);\n    ZVAL_STRING(&argv[2], string);\n\n    fci.size = sizeof(fci);\n    ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);\n    fci.object = NULL;\n    fci.retval = &retval;\n    fci.param_count = 3;\n    fci.params = argv;\n    fci.named_params = NULL;\n\n    ch->in_callback = 1;\n    error = zend_call_function(&fci, &t->fci_cache);\n    ch->in_callback = 0;\n    if (error == FAILURE) {\n        php_error_docref(NULL, E_WARNING, \"Cannot call the CURLOPT_FNMATCH_FUNCTION\");\n    } else if (!Z_ISUNDEF(retval)) {\n        swoole_curl_verify_handlers(ch, /* reporterror */ true);\n#if PHP_VERSION_ID >= 80300\n        rval = swoole_curl_get_long(&retval);\n#else\n        rval = zval_get_long(&retval);\n#endif\n    }\n    zval_ptr_dtor(&argv[0]);\n    zval_ptr_dtor(&argv[1]);\n    zval_ptr_dtor(&argv[2]);\n    return rval;\n}\n/* }}} */\n\n/* {{{ curl_progress\n */\nstatic int fn_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) {\n    php_curl *ch = (php_curl *) clientp;\n    php_curl_callback *t = ch->handlers.progress;\n    int rval = 0;\n\n#if PHP_CURL_DEBUG\n    fprintf(stderr, \"curl_progress() called\\n\");\n    fprintf(stderr,\n            \"clientp = %x, dltotal = %f, dlnow = %f, ultotal = %f, ulnow = %f\\n\",\n            clientp,\n            dltotal,\n            dlnow,\n            ultotal,\n            ulnow);\n#endif\n\n    zval argv[5];\n    zval retval;\n    zend_result error;\n    zend_fcall_info fci;\n\n    GC_ADDREF(&ch->std);\n    ZVAL_OBJ(&argv[0], &ch->std);\n    ZVAL_LONG(&argv[1], (zend_long) dltotal);\n    ZVAL_LONG(&argv[2], (zend_long) dlnow);\n    ZVAL_LONG(&argv[3], (zend_long) ultotal);\n    ZVAL_LONG(&argv[4], (zend_long) ulnow);\n\n    fci.size = sizeof(fci);\n    ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);\n    fci.object = NULL;\n    fci.retval = &retval;\n    fci.param_count = 5;\n    fci.params = argv;\n    fci.named_params = NULL;\n\n    ch->in_callback = 1;\n    error = zend_call_function(&fci, &t->fci_cache);\n    ch->in_callback = 0;\n    if (error == FAILURE) {\n        php_error_docref(NULL, E_WARNING, \"Cannot call the CURLOPT_PROGRESSFUNCTION\");\n    } else if (!Z_ISUNDEF(retval)) {\n        swoole_curl_verify_handlers(ch, /* reporterror */ true);\n#if PHP_VERSION_ID >= 80300\n        if (0 != swoole_curl_get_long(&retval)) {\n#else\n        if (0 != zval_get_long(&retval)) {\n#endif\n            rval = 1;\n        }\n    }\n    zval_ptr_dtor(&argv[0]);\n    return rval;\n}\n/* }}} */\n\n#if LIBCURL_VERSION_NUM >= 0x072000 && PHP_VERSION_ID >= 80200\n/* {{{ curl_xferinfo */\nstatic size_t fn_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {\n    php_curl *ch = (php_curl *) clientp;\n    php_curl_callback *t = ch->handlers.xferinfo;\n    int rval = 0;\n\n#if PHP_CURL_DEBUG\n    fprintf(stderr, \"curl_xferinfo() called\\n\");\n    fprintf(stderr,\n            \"clientp = %x, dltotal = %ld, dlnow = %ld, ultotal = %ld, ulnow = %ld\\n\",\n            clientp,\n            dltotal,\n            dlnow,\n            ultotal,\n            ulnow);\n#endif\n\n    zval argv[5];\n    zval retval;\n    zend_result error;\n    zend_fcall_info fci;\n\n    GC_ADDREF(&ch->std);\n    ZVAL_OBJ(&argv[0], &ch->std);\n    ZVAL_LONG(&argv[1], dltotal);\n    ZVAL_LONG(&argv[2], dlnow);\n    ZVAL_LONG(&argv[3], ultotal);\n    ZVAL_LONG(&argv[4], ulnow);\n\n    fci.size = sizeof(fci);\n    ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);\n    fci.object = NULL;\n    fci.retval = &retval;\n    fci.param_count = 5;\n    fci.params = argv;\n    fci.named_params = NULL;\n\n    ch->in_callback = 1;\n    error = zend_call_function(&fci, &t->fci_cache);\n    ch->in_callback = 0;\n    if (error == FAILURE) {\n        php_error_docref(NULL, E_WARNING, \"Cannot call the CURLOPT_XFERINFOFUNCTION\");\n    } else if (!Z_ISUNDEF(retval)) {\n        swoole_curl_verify_handlers(ch, /* reporterror */ true);\n#if PHP_VERSION_ID >= 80300\n        if (0 != swoole_curl_get_long(&retval)) {\n#else\n        if (0 != zval_get_long(&retval)) {\n#endif\n            rval = 1;\n        }\n    }\n    zval_ptr_dtor(&argv[0]);\n    return rval;\n}\n/* }}} */\n#endif\n\n#if LIBCURL_VERSION_NUM >= 0x075400 && PHP_VERSION_ID >= 80300\nstatic int fn_ssh_hostkeyfunction(void *clientp, int keytype, const char *key, size_t keylen) {\n    php_curl *ch = (php_curl *) clientp;\n    php_curl_callback *t = ch->handlers.sshhostkey;\n    int rval = CURLKHMATCH_MISMATCH; /* cancel connection in case of an exception */\n\n#if PHP_CURL_DEBUG\n    fprintf(stderr, \"curl_ssh_hostkeyfunction() called\\n\");\n    fprintf(stderr, \"clientp = %x, keytype = %d, key = %s, keylen = %zu\\n\", clientp, keytype, key, keylen);\n#endif\n\n    zval argv[4];\n    zval retval;\n    zend_result error;\n    zend_fcall_info fci;\n\n    GC_ADDREF(&ch->std);\n    ZVAL_OBJ(&argv[0], &ch->std);\n    ZVAL_LONG(&argv[1], keytype);\n    ZVAL_STRINGL(&argv[2], key, keylen);\n    ZVAL_LONG(&argv[3], keylen);\n\n    fci.size = sizeof(fci);\n    ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);\n    fci.object = NULL;\n    fci.retval = &retval;\n    fci.param_count = 4;\n    fci.params = argv;\n    fci.named_params = NULL;\n\n    ch->in_callback = 1;\n    error = zend_call_function(&fci, &t->fci_cache);\n    ch->in_callback = 0;\n    if (error == FAILURE) {\n        php_error_docref(NULL, E_WARNING, \"Cannot call the CURLOPT_SSH_HOSTKEYFUNCTION\");\n    } else if (!Z_ISUNDEF(retval)) {\n        swoole_curl_verify_handlers(ch, /* reporterror */ true);\n        if (Z_TYPE(retval) == IS_LONG) {\n            zend_long retval_long = Z_LVAL(retval);\n            if (retval_long == CURLKHMATCH_OK || retval_long == CURLKHMATCH_MISMATCH) {\n                rval = retval_long;\n            } else {\n                zend_throw_error(NULL,\n                                 \"The CURLOPT_SSH_HOSTKEYFUNCTION callback must return either CURLKHMATCH_OK or \"\n                                 \"CURLKHMATCH_MISMATCH\");\n            }\n        } else {\n            zend_throw_error(\n                NULL,\n                \"The CURLOPT_SSH_HOSTKEYFUNCTION callback must return either CURLKHMATCH_OK or CURLKHMATCH_MISMATCH\");\n#if PHP_VERSION_ID >= 80300\n            zval_ptr_dtor(&retval);\n#endif\n        }\n    }\n    zval_ptr_dtor(&argv[0]);\n    zval_ptr_dtor(&argv[2]);\n    return rval;\n}\n#endif\n\n/* {{{ curl_read\n */\nstatic size_t fn_read(char *data, size_t size, size_t nmemb, void *ctx) {\n    php_curl *ch = (php_curl *) ctx;\n    php_curl_read *t = ch->handlers.read;\n    int length = 0;\n\n    switch (t->method) {\n    case PHP_CURL_DIRECT:\n        if (t->fp) {\n            length = fread(data, size, nmemb, t->fp);\n        }\n        break;\n    case PHP_CURL_USER: {\n        zval argv[3];\n        zval retval;\n        zend_result error;\n        zend_fcall_info fci;\n\n        GC_ADDREF(&ch->std);\n        ZVAL_OBJ(&argv[0], &ch->std);\n        if (t->res) {\n            GC_ADDREF(t->res);\n            ZVAL_RES(&argv[1], t->res);\n        } else {\n            ZVAL_NULL(&argv[1]);\n        }\n        ZVAL_LONG(&argv[2], (int) size * nmemb);\n\n        fci.size = sizeof(fci);\n        ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);\n        fci.object = NULL;\n        fci.retval = &retval;\n        fci.param_count = 3;\n        fci.params = argv;\n        fci.named_params = NULL;\n\n        ch->in_callback = 1;\n        error = zend_call_function(&fci, &t->fci_cache);\n        ch->in_callback = 0;\n        if (error == FAILURE) {\n            php_error_docref(NULL, E_WARNING, \"Cannot call the CURLOPT_READFUNCTION\");\n            length = CURL_READFUNC_ABORT;\n        } else if (!Z_ISUNDEF(retval)) {\n            swoole_curl_verify_handlers(ch, /* reporterror */ true);\n            if (Z_TYPE(retval) == IS_STRING) {\n                length = MIN((int) (size * nmemb), Z_STRLEN(retval));\n                memcpy(data, Z_STRVAL(retval), length);\n            } else if (Z_TYPE(retval) == IS_LONG) {\n                length = Z_LVAL_P(&retval);\n            }\n            zval_ptr_dtor(&retval);\n        }\n\n        zval_ptr_dtor(&argv[0]);\n        zval_ptr_dtor(&argv[1]);\n        break;\n    }\n    }\n\n    return length;\n}\n/* }}} */\n\n/* {{{ curl_write_header\n */\nstatic size_t fn_write_header(char *data, size_t size, size_t nmemb, void *ctx) {\n    php_curl *ch = (php_curl *) ctx;\n    php_curl_write *t = ch->handlers.write_header;\n    size_t length = size * nmemb;\n\n    switch (t->method) {\n    case PHP_CURL_STDOUT:\n        /* Handle special case write when we're returning the entire transfer\n         */\n        if (ch->handlers.write->method == PHP_CURL_RETURN && length > 0) {\n            smart_str_appendl(&ch->handlers.write->buf, data, (int) length);\n        } else {\n            PHPWRITE(data, length);\n        }\n        break;\n    case PHP_CURL_FILE:\n        return fwrite(data, size, nmemb, t->fp);\n    case PHP_CURL_USER: {\n        zval argv[2];\n        zval retval;\n        zend_result error;\n        zend_fcall_info fci;\n\n        GC_ADDREF(&ch->std);\n        ZVAL_OBJ(&argv[0], &ch->std);\n\n        ZVAL_STRINGL(&argv[1], data, length);\n\n        fci.size = sizeof(fci);\n        ZVAL_COPY_VALUE(&fci.function_name, &t->func_name);\n        fci.object = NULL;\n        fci.retval = &retval;\n        fci.param_count = 2;\n        fci.params = argv;\n        fci.named_params = NULL;\n\n        ch->in_callback = 1;\n        error = zend_call_function(&fci, &t->fci_cache);\n        ch->in_callback = 0;\n        if (error == FAILURE) {\n            php_error_docref(NULL, E_WARNING, \"Could not call the CURLOPT_HEADERFUNCTION\");\n            length = -1;\n        } else if (!Z_ISUNDEF(retval)) {\n            swoole_curl_verify_handlers(ch, /* reporterror */ true);\n#if PHP_VERSION_ID >= 80300\n            length = swoole_curl_get_long(&retval);\n#else\n            length = zval_get_long(&retval);\n#endif\n        }\n        zval_ptr_dtor(&argv[0]);\n        zval_ptr_dtor(&argv[1]);\n        break;\n    }\n\n    case PHP_CURL_IGNORE:\n        return length;\n\n    default:\n        return -1;\n    }\n\n    return length;\n}\n/* }}} */\n\nstatic int curl_debug(CURL *cp, curl_infotype type, char *buf, size_t buf_len, void *ctx) /* {{{ */\n{\n    php_curl *ch = (php_curl *) ctx;\n\n    if (type == CURLINFO_HEADER_OUT) {\n        if (ch->header.str) {\n            zend_string_release_ex(ch->header.str, 0);\n        }\n        ch->header.str = zend_string_init(buf, buf_len, 0);\n    }\n\n    return 0;\n}\n/* }}} */\n\n/* {{{ curl_free_post\n */\nstatic void curl_free_post(void **post) {\n    curl_mime_free((curl_mime *) *post);\n}\n/* }}} */\n\nstruct mime_data_cb_arg {\n    zend_string *filename;\n    php_stream *stream;\n};\n\n/* {{{ curl_free_cb_arg\n */\nstatic void curl_free_cb_arg(void **cb_arg_p) {\n    struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) *cb_arg_p;\n\n    ZEND_ASSERT(cb_arg->stream == NULL);\n    zend_string_release(cb_arg->filename);\n    efree(cb_arg);\n}\n/* }}} */\n\n/* {{{ curl_free_slist\n */\nstatic void curl_free_slist(zval *el) {\n    curl_slist_free_all(((struct curl_slist *) Z_PTR_P(el)));\n}\n/* }}} */\n\nphp_curl *swoole_curl_init_handle_into_zval(zval *curl) {\n    php_curl *ch;\n\n    object_init_ex(curl, swoole_coroutine_curl_handle_ce);\n    ch = Z_CURL_P(curl);\n\n    swoole_curl_init_handle(ch);\n\n    return ch;\n}\n\n/* {{{ alloc_curl_handle\n */\nvoid swoole_curl_init_handle(php_curl *ch) {\n    ch->to_free = (struct _php_curl_free *) ecalloc(1, sizeof(struct _php_curl_free));\n    ch->handlers.write = (php_curl_write *) ecalloc(1, sizeof(php_curl_write));\n    ch->handlers.write_header = (php_curl_write *) ecalloc(1, sizeof(php_curl_write));\n    ch->handlers.read = (php_curl_read *) ecalloc(1, sizeof(php_curl_read));\n    ch->handlers.progress = NULL;\n#if PHP_VERSION_ID >= 80200\n    ch->handlers.xferinfo = NULL;\n#endif\n    ch->handlers.fnmatch = NULL;\n#if LIBCURL_VERSION_NUM >= 0x075400 && PHP_VERSION_ID >= 80300\n    ch->handlers.sshhostkey = NULL;\n#endif\n    ch->clone = (uint32_t *) emalloc(sizeof(uint32_t));\n    *ch->clone = 1;\n\n    memset(&ch->err, 0, sizeof(struct _php_curl_error));\n\n    zend_llist_init(&ch->to_free->post, sizeof(struct HttpPost *), (llist_dtor_func_t) curl_free_post, 0);\n    zend_llist_init(&ch->to_free->stream, sizeof(struct mime_data_cb_arg *), (llist_dtor_func_t) curl_free_cb_arg, 0);\n\n    ch->to_free->slist = (HashTable *) emalloc(sizeof(HashTable));\n    zend_hash_init(ch->to_free->slist, 4, NULL, curl_free_slist, 0);\n    ZVAL_UNDEF(&ch->postfields);\n}\n/* }}} */\n\n/* {{{ create_certinfo\n */\nstatic void create_certinfo(struct curl_certinfo *ci, zval *listcode) {\n    int i;\n\n    if (ci) {\n        zval certhash;\n\n        for (i = 0; i < ci->num_of_certs; i++) {\n            struct curl_slist *slist;\n\n            array_init(&certhash);\n            for (slist = ci->certinfo[i]; slist; slist = slist->next) {\n                int len;\n                char s[64];\n                char *tmp;\n                strncpy(s, slist->data, sizeof(s));\n                s[sizeof(s) - 1] = '\\0';\n                tmp = (char *) memchr(s, ':', sizeof(s));\n                if (tmp) {\n                    *tmp = '\\0';\n                    len = strlen(s);\n                    add_assoc_string(&certhash, s, &slist->data[len + 1]);\n                } else {\n                    php_error_docref(NULL, E_WARNING, \"Could not extract hash key from certificate info\");\n                }\n            }\n            add_next_index_zval(listcode, &certhash);\n        }\n    }\n}\n/* }}} */\n\n/* {{{ _php_curl_set_default_options()\n   Set default options for a handle */\nstatic void _php_curl_set_default_options(php_curl *ch) {\n    const char *cainfo;\n\n    curl_easy_setopt(ch->cp, CURLOPT_NOPROGRESS, 1L);\n    curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0L);\n    curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str);\n    curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, fn_write);\n    curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch);\n    curl_easy_setopt(ch->cp, CURLOPT_READFUNCTION, fn_read);\n    curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch);\n    curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, fn_write_header);\n    curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch);\n#ifndef ZTS\n    curl_easy_setopt(ch->cp, CURLOPT_DNS_USE_GLOBAL_CACHE, 1L);\n#endif\n    curl_easy_setopt(ch->cp, CURLOPT_DNS_CACHE_TIMEOUT, 120L);\n    curl_easy_setopt(ch->cp, CURLOPT_MAXREDIRS, 20L); /* prevent infinite redirects */\n\n    cainfo = INI_STR(\"openssl.cafile\");\n    if (!(cainfo && cainfo[0] != '\\0')) {\n        cainfo = INI_STR(\"curl.cainfo\");\n    }\n    if (cainfo && cainfo[0] != '\\0') {\n        curl_easy_setopt(ch->cp, CURLOPT_CAINFO, cainfo);\n    }\n\n#ifdef ZTS\n    curl_easy_setopt(ch->cp, CURLOPT_NOSIGNAL, 1L);\n#endif\n}\n/* }}} */\n\n/* {{{ proto resource curl_init([string url])\n   Initialize a cURL session */\nPHP_FUNCTION(swoole_native_curl_init) {\n    php_curl *ch;\n    CURL *cp;\n    zend_string *url = NULL;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_STR_OR_NULL(url)\n    ZEND_PARSE_PARAMETERS_END();\n\n    cp = curl_easy_init();\n    if (!cp) {\n        php_error_docref(NULL, E_WARNING, \"Could not initialize a new cURL handle\");\n        RETURN_FALSE;\n    }\n\n    ch = swoole_curl_init_handle_into_zval(return_value);\n    ch->cp = cp;\n\n    ch->handlers.write->method = PHP_CURL_STDOUT;\n    ch->handlers.read->method = PHP_CURL_DIRECT;\n    ch->handlers.write_header->method = PHP_CURL_IGNORE;\n\n    _php_curl_set_default_options(ch);\n    swoole::curl::create_handle(cp);\n\n    if (url) {\n#if PHP_VERSION_ID >= 80300\n        if (php_curl_option_url(ch, url) == FAILURE) {\n#else\n        if (php_curl_option_url(ch, ZSTR_VAL(url), ZSTR_LEN(url)) == FAILURE) {\n#endif\n            zval_ptr_dtor(return_value);\n            RETURN_FALSE;\n        }\n    }\n}\n/* }}} */\n\n#if PHP_VERSION_ID >= 80300\nstatic void _php_copy_callback(php_curl *ch,\n                               php_curl_callback **new_callback,\n                               php_curl_callback *source_callback,\n                               CURLoption option) {\n    if (source_callback) {\n        *new_callback = (php_curl_callback *) ecalloc(1, sizeof(php_curl_callback));\n        if (!Z_ISUNDEF(source_callback->func_name)) {\n            ZVAL_COPY(&(*new_callback)->func_name, &source_callback->func_name);\n        }\n        curl_easy_setopt(ch->cp, (CURLoption) option, (void *) ch);\n    }\n}\n#endif\n\nvoid swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source) {\n    if (!Z_ISUNDEF(source->handlers.write->stream)) {\n        Z_ADDREF(source->handlers.write->stream);\n    }\n    ch->handlers.write->stream = source->handlers.write->stream;\n    ch->handlers.write->method = source->handlers.write->method;\n    if (!Z_ISUNDEF(source->handlers.read->stream)) {\n        Z_ADDREF(source->handlers.read->stream);\n    }\n    ch->handlers.read->stream = source->handlers.read->stream;\n    ch->handlers.read->method = source->handlers.read->method;\n    ch->handlers.write_header->method = source->handlers.write_header->method;\n    if (!Z_ISUNDEF(source->handlers.write_header->stream)) {\n        Z_ADDREF(source->handlers.write_header->stream);\n    }\n    ch->handlers.write_header->stream = source->handlers.write_header->stream;\n\n    ch->handlers.write->fp = source->handlers.write->fp;\n    ch->handlers.write_header->fp = source->handlers.write_header->fp;\n    ch->handlers.read->fp = source->handlers.read->fp;\n    ch->handlers.read->res = source->handlers.read->res;\n\n    if (!Z_ISUNDEF(source->handlers.write->func_name)) {\n        ZVAL_COPY(&ch->handlers.write->func_name, &source->handlers.write->func_name);\n    }\n    if (!Z_ISUNDEF(source->handlers.read->func_name)) {\n        ZVAL_COPY(&ch->handlers.read->func_name, &source->handlers.read->func_name);\n    }\n    if (!Z_ISUNDEF(source->handlers.write_header->func_name)) {\n        ZVAL_COPY(&ch->handlers.write_header->func_name, &source->handlers.write_header->func_name);\n    }\n\n    curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str);\n    curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch);\n    curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch);\n    curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch);\n    curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *) ch);\n\n#if PHP_VERSION_ID >= 80300\n    _php_copy_callback(ch, &ch->handlers.progress, source->handlers.progress, CURLOPT_PROGRESSDATA);\n    _php_copy_callback(ch, &ch->handlers.xferinfo, source->handlers.xferinfo, CURLOPT_XFERINFODATA);\n    _php_copy_callback(ch, &ch->handlers.fnmatch, source->handlers.fnmatch, CURLOPT_FNMATCH_DATA);\n#if LIBCURL_VERSION_NUM >= 0x075400\n    _php_copy_callback(ch, &ch->handlers.sshhostkey, source->handlers.sshhostkey, CURLOPT_SSH_HOSTKEYDATA);\n#endif\n#else\n    if (source->handlers.progress) {\n        ch->handlers.progress = (php_curl_callback *) ecalloc(1, sizeof(php_curl_callback));\n        if (!Z_ISUNDEF(source->handlers.progress->func_name)) {\n            ZVAL_COPY(&ch->handlers.progress->func_name, &source->handlers.progress->func_name);\n        }\n        curl_easy_setopt(ch->cp, CURLOPT_PROGRESSDATA, (void *) ch);\n    }\n\n#if PHP_VERSION_ID >= 80200\n    if (source->handlers.xferinfo) {\n        ch->handlers.xferinfo = (php_curl_callback *) ecalloc(1, sizeof(php_curl_callback));\n        if (!Z_ISUNDEF(source->handlers.xferinfo->func_name)) {\n            ZVAL_COPY(&ch->handlers.xferinfo->func_name, &source->handlers.xferinfo->func_name);\n        }\n        curl_easy_setopt(ch->cp, CURLOPT_XFERINFODATA, (void *) ch);\n    }\n#endif\n\n    if (source->handlers.fnmatch) {\n        ch->handlers.fnmatch = (php_curl_callback *) ecalloc(1, sizeof(php_curl_callback));\n        if (!Z_ISUNDEF(source->handlers.fnmatch->func_name)) {\n            ZVAL_COPY(&ch->handlers.fnmatch->func_name, &source->handlers.fnmatch->func_name);\n        }\n        curl_easy_setopt(ch->cp, CURLOPT_FNMATCH_DATA, (void *) ch);\n    }\n#endif\n\n    ZVAL_COPY(&ch->private_data, &source->private_data);\n\n    efree(ch->to_free->slist);\n    efree(ch->to_free);\n    ch->to_free = source->to_free;\n    efree(ch->clone);\n    ch->clone = source->clone;\n\n    /* Keep track of cloned copies to avoid invoking curl destructors for every clone */\n    (*source->clone)++;\n}\n\n#if PHP_VERSION_ID >= 80300\nzend_long swoole_curl_get_long(zval *zv) {\n    if (EXPECTED(Z_TYPE_P(zv) == IS_LONG)) {\n        return Z_LVAL_P(zv);\n    } else {\n        zend_long ret = zval_get_long(zv);\n        zval_ptr_dtor(zv);\n        return ret;\n    }\n}\n#endif\n\nstatic size_t read_cb(char *buffer, size_t size, size_t nitems, void *arg) /* {{{ */\n{\n    struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg;\n    ssize_t numread;\n\n    if (cb_arg->stream == NULL) {\n        if (!(cb_arg->stream = php_stream_open_wrapper(ZSTR_VAL(cb_arg->filename), \"rb\", IGNORE_PATH, NULL))) {\n            return CURL_READFUNC_ABORT;\n        }\n    }\n    numread = php_stream_read(cb_arg->stream, buffer, nitems * size);\n    if (numread < 0) {\n        php_stream_close(cb_arg->stream);\n        cb_arg->stream = NULL;\n        return CURL_READFUNC_ABORT;\n    }\n    return numread;\n}\n/* }}} */\n\nstatic int seek_cb(void *arg, curl_off_t offset, int origin) /* {{{ */\n{\n    struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg;\n    int res;\n\n    if (cb_arg->stream == NULL) {\n        return CURL_SEEKFUNC_CANTSEEK;\n    }\n    res = php_stream_seek(cb_arg->stream, offset, origin);\n    return res == SUCCESS ? CURL_SEEKFUNC_OK : CURL_SEEKFUNC_CANTSEEK;\n}\n/* }}} */\n\nstatic void free_cb(void *arg) /* {{{ */\n{\n    struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg;\n\n    if (cb_arg->stream != NULL) {\n        php_stream_close(cb_arg->stream);\n        cb_arg->stream = NULL;\n    }\n}\n/* }}} */\n\n#if PHP_VERSION_ID >= 80200\nstatic inline CURLcode add_simple_field(curl_mime *mime, zend_string *string_key, zval *current) {\n    CURLcode error = CURLE_OK;\n    curl_mimepart *part;\n    CURLcode form_error;\n\n    zend_string *postval, *tmp_postval;\n\n    postval = zval_get_tmp_string(current, &tmp_postval);\n\n    part = curl_mime_addpart(mime);\n    if (part == NULL) {\n        zend_tmp_string_release(tmp_postval);\n#if PHP_VERSION_ID < 80300\n        zend_string_release_ex(string_key, 0);\n#endif\n        return CURLE_OUT_OF_MEMORY;\n    }\n    if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK ||\n        (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK) {\n        error = form_error;\n    }\n    zend_tmp_string_release(tmp_postval);\n\n    return error;\n}\n\nstatic inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields) /* {{{ */\n{\n    HashTable *postfields = Z_ARRVAL_P(zpostfields);\n    CURLcode error = CURLE_OK;\n    zval *current;\n    zend_string *string_key;\n    zend_ulong num_key;\n    curl_mime *mime = NULL;\n    curl_mimepart *part;\n    CURLcode form_error;\n\n    if (zend_hash_num_elements(postfields) > 0) {\n        mime = curl_mime_init(ch->cp);\n        if (mime == NULL) {\n            return FAILURE;\n        }\n    }\n\n    ZEND_HASH_FOREACH_KEY_VAL(postfields, num_key, string_key, current) {\n        zend_string *postval;\n        /* Pretend we have a string_key here */\n        if (!string_key) {\n            string_key = zend_long_to_str(num_key);\n        } else {\n            zend_string_addref(string_key);\n        }\n\n        ZVAL_DEREF(current);\n        if (Z_TYPE_P(current) == IS_OBJECT && instanceof_function(Z_OBJCE_P(current), curl_CURLFile_class)) {\n            /* new-style file upload */\n            zval *prop, rv;\n            char *type = NULL, *filename = NULL;\n            struct mime_data_cb_arg *cb_arg;\n            php_stream *stream;\n            php_stream_statbuf ssb;\n            size_t filesize = -1;\n            curl_seek_callback seekfunc = seek_cb;\n#if PHP_VERSION_ID >= 80300\n            prop = zend_read_property_ex(\n                curl_CURLFile_class, Z_OBJ_P(current), ZSTR_KNOWN(ZEND_STR_NAME), /* silent */ false, &rv);\n#else\n            prop = zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), \"name\", sizeof(\"name\") - 1, 0, &rv);\n#endif\n            ZVAL_DEREF(prop);\n            if (Z_TYPE_P(prop) != IS_STRING) {\n                php_error_docref(NULL, E_WARNING, \"Invalid filename for key %s\", ZSTR_VAL(string_key));\n            } else {\n                postval = Z_STR_P(prop);\n\n                if (php_check_open_basedir(ZSTR_VAL(postval))) {\n                    goto out_string;\n                }\n\n                prop = zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), \"mime\", sizeof(\"mime\") - 1, 0, &rv);\n                ZVAL_DEREF(prop);\n                if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) {\n                    type = Z_STRVAL_P(prop);\n                }\n                prop = zend_read_property(\n                    curl_CURLFile_class, Z_OBJ_P(current), \"postname\", sizeof(\"postname\") - 1, 0, &rv);\n                ZVAL_DEREF(prop);\n                if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) {\n                    filename = Z_STRVAL_P(prop);\n                }\n\n                zval_ptr_dtor(&ch->postfields);\n                ZVAL_COPY(&ch->postfields, zpostfields);\n\n                if ((stream = php_stream_open_wrapper(ZSTR_VAL(postval), \"rb\", STREAM_MUST_SEEK, NULL))) {\n                    if (!stream->readfilters.head && !php_stream_stat(stream, &ssb)) {\n                        filesize = ssb.sb.st_size;\n                    }\n                } else {\n                    seekfunc = NULL;\n                }\n\n                part = curl_mime_addpart(mime);\n                if (part == NULL) {\n                    if (stream) {\n                        php_stream_close(stream);\n                    }\n                    goto out_string;\n                }\n\n                cb_arg = (struct mime_data_cb_arg *) emalloc(sizeof *cb_arg);\n                cb_arg->filename = zend_string_copy(postval);\n                cb_arg->stream = stream;\n\n                if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK ||\n                    (form_error = curl_mime_data_cb(part, filesize, read_cb, seekfunc, free_cb, cb_arg)) != CURLE_OK ||\n                    (form_error = curl_mime_filename(part, filename ? filename : ZSTR_VAL(postval))) != CURLE_OK ||\n                    (form_error = curl_mime_type(part, type ? type : \"application/octet-stream\")) != CURLE_OK) {\n                    error = form_error;\n                }\n                zend_llist_add_element(&ch->to_free->stream, &cb_arg);\n            }\n\n            zend_string_release_ex(string_key, 0);\n            continue;\n        }\n\n        if (Z_TYPE_P(current) == IS_OBJECT && instanceof_function(Z_OBJCE_P(current), curl_CURLStringFile_class)) {\n            /* new-style file upload from string */\n            zval *prop, rv;\n            char *type = NULL, *filename = NULL;\n\n            prop = zend_read_property(\n                curl_CURLStringFile_class, Z_OBJ_P(current), \"postname\", sizeof(\"postname\") - 1, 0, &rv);\n            if (EG(exception)) {\n                goto out_string;\n            }\n            ZVAL_DEREF(prop);\n            ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);\n\n            filename = Z_STRVAL_P(prop);\n\n            prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), \"mime\", sizeof(\"mime\") - 1, 0, &rv);\n            if (EG(exception)) {\n                goto out_string;\n            }\n            ZVAL_DEREF(prop);\n            ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);\n\n            type = Z_STRVAL_P(prop);\n\n            prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), \"data\", sizeof(\"data\") - 1, 0, &rv);\n            if (EG(exception)) {\n                goto out_string;\n            }\n            ZVAL_DEREF(prop);\n            ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);\n\n            postval = Z_STR_P(prop);\n\n            zval_ptr_dtor(&ch->postfields);\n            ZVAL_COPY(&ch->postfields, zpostfields);\n\n            part = curl_mime_addpart(mime);\n            if (part == NULL) {\n                goto out_string;\n            }\n            if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK ||\n                (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK ||\n                (form_error = curl_mime_filename(part, filename)) != CURLE_OK ||\n                (form_error = curl_mime_type(part, type)) != CURLE_OK) {\n                error = form_error;\n            }\n\n            zend_string_release_ex(string_key, 0);\n            continue;\n        }\n\n        if (Z_TYPE_P(current) == IS_ARRAY) {\n            zval *current_element;\n\n            ZEND_HASH_FOREACH_VAL(HASH_OF(current), current_element) {\n                add_simple_field(mime, string_key, current_element);\n            }\n            ZEND_HASH_FOREACH_END();\n\n            zend_string_release_ex(string_key, 0);\n            continue;\n        }\n\n        add_simple_field(mime, string_key, current);\n        zend_string_release_ex(string_key, 0);\n    }\n    ZEND_HASH_FOREACH_END();\n\n    SAVE_CURL_ERROR(ch, error);\n    if (error != CURLE_OK) {\n        goto out_mime;\n    }\n\n    if ((*ch->clone) == 1) {\n        zend_llist_clean(&ch->to_free->post);\n    }\n\n    zend_llist_add_element(&ch->to_free->post, &mime);\n    error = curl_easy_setopt(ch->cp, CURLOPT_MIMEPOST, mime);\n    SAVE_CURL_ERROR(ch, error);\n    return error == CURLE_OK ? SUCCESS : FAILURE;\n\nout_string:\n    zend_string_release_ex(string_key, false);\nout_mime:\n\n    curl_mime_free(mime);\n    return FAILURE;\n}\n/* }}} */\n#else\nstatic inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields) /* {{{ */\n{\n    HashTable *postfields = Z_ARRVAL_P(zpostfields);\n    CURLcode error = CURLE_OK;\n    zval *current;\n    zend_string *string_key;\n    zend_ulong num_key;\n    curl_mime *mime = NULL;\n    curl_mimepart *part;\n    CURLcode form_error;\n\n    if (zend_hash_num_elements(postfields) > 0) {\n        mime = curl_mime_init(ch->cp);\n        if (mime == NULL) {\n            return FAILURE;\n        }\n    }\n\n    ZEND_HASH_FOREACH_KEY_VAL(postfields, num_key, string_key, current) {\n        zend_string *postval, *tmp_postval;\n        /* Pretend we have a string_key here */\n        if (!string_key) {\n            string_key = zend_long_to_str(num_key);\n        } else {\n            zend_string_addref(string_key);\n        }\n\n        ZVAL_DEREF(current);\n        if (Z_TYPE_P(current) == IS_OBJECT && instanceof_function(Z_OBJCE_P(current), curl_CURLFile_class)) {\n            /* new-style file upload */\n            zval *prop, rv;\n            char *type = NULL, *filename = NULL;\n            struct mime_data_cb_arg *cb_arg;\n            php_stream *stream;\n            php_stream_statbuf ssb;\n            size_t filesize = -1;\n            curl_seek_callback seekfunc = seek_cb;\n\n            prop = zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), \"name\", sizeof(\"name\") - 1, 0, &rv);\n            ZVAL_DEREF(prop);\n            if (Z_TYPE_P(prop) != IS_STRING) {\n                php_error_docref(NULL, E_WARNING, \"Invalid filename for key %s\", ZSTR_VAL(string_key));\n            } else {\n                postval = Z_STR_P(prop);\n\n                if (php_check_open_basedir(ZSTR_VAL(postval))) {\n                    return FAILURE;\n                }\n\n                prop = zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), \"mime\", sizeof(\"mime\") - 1, 0, &rv);\n                ZVAL_DEREF(prop);\n                if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) {\n                    type = Z_STRVAL_P(prop);\n                }\n                prop = zend_read_property(\n                    curl_CURLFile_class, Z_OBJ_P(current), \"postname\", sizeof(\"postname\") - 1, 0, &rv);\n                ZVAL_DEREF(prop);\n                if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) {\n                    filename = Z_STRVAL_P(prop);\n                }\n\n                zval_ptr_dtor(&ch->postfields);\n                ZVAL_COPY(&ch->postfields, zpostfields);\n\n                if ((stream = php_stream_open_wrapper(ZSTR_VAL(postval), \"rb\", STREAM_MUST_SEEK, NULL))) {\n                    if (!stream->readfilters.head && !php_stream_stat(stream, &ssb)) {\n                        filesize = ssb.sb.st_size;\n                    }\n                } else {\n                    seekfunc = NULL;\n                }\n\n                cb_arg = (struct mime_data_cb_arg *) emalloc(sizeof *cb_arg);\n                cb_arg->filename = zend_string_copy(postval);\n                cb_arg->stream = stream;\n\n                part = curl_mime_addpart(mime);\n                if (part == NULL) {\n                    zend_string_release_ex(string_key, 0);\n                    return FAILURE;\n                }\n                if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK ||\n                    (form_error = curl_mime_data_cb(part, filesize, read_cb, seekfunc, free_cb, cb_arg)) != CURLE_OK ||\n                    (form_error = curl_mime_filename(part, filename ? filename : ZSTR_VAL(postval))) != CURLE_OK ||\n                    (form_error = curl_mime_type(part, type ? type : \"application/octet-stream\")) != CURLE_OK) {\n                    error = form_error;\n                }\n                zend_llist_add_element(&ch->to_free->stream, &cb_arg);\n            }\n\n            zend_string_release_ex(string_key, 0);\n            continue;\n        }\n\n        if (Z_TYPE_P(current) == IS_OBJECT && instanceof_function(Z_OBJCE_P(current), curl_CURLStringFile_class)) {\n            /* new-style file upload from string */\n            zval *prop, rv;\n            char *type = NULL, *filename = NULL;\n\n            prop = zend_read_property(\n                curl_CURLStringFile_class, Z_OBJ_P(current), \"postname\", sizeof(\"postname\") - 1, 0, &rv);\n            if (EG(exception)) {\n                zend_string_release_ex(string_key, 0);\n                return FAILURE;\n            }\n            ZVAL_DEREF(prop);\n            ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);\n\n            filename = Z_STRVAL_P(prop);\n\n            prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), \"mime\", sizeof(\"mime\") - 1, 0, &rv);\n            if (EG(exception)) {\n                zend_string_release_ex(string_key, 0);\n                return FAILURE;\n            }\n            ZVAL_DEREF(prop);\n            ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);\n\n            type = Z_STRVAL_P(prop);\n\n            prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), \"data\", sizeof(\"data\") - 1, 0, &rv);\n            if (EG(exception)) {\n                zend_string_release_ex(string_key, 0);\n                return FAILURE;\n            }\n            ZVAL_DEREF(prop);\n            ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);\n\n            postval = Z_STR_P(prop);\n\n            zval_ptr_dtor(&ch->postfields);\n            ZVAL_COPY(&ch->postfields, zpostfields);\n\n            part = curl_mime_addpart(mime);\n            if (part == NULL) {\n                zend_string_release_ex(string_key, 0);\n                return FAILURE;\n            }\n            if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK ||\n                (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK ||\n                (form_error = curl_mime_filename(part, filename)) != CURLE_OK ||\n                (form_error = curl_mime_type(part, type)) != CURLE_OK) {\n                error = form_error;\n            }\n\n            zend_string_release_ex(string_key, 0);\n            continue;\n        }\n\n        postval = zval_get_tmp_string(current, &tmp_postval);\n\n        part = curl_mime_addpart(mime);\n        if (part == NULL) {\n            zend_tmp_string_release(tmp_postval);\n            zend_string_release_ex(string_key, 0);\n            return FAILURE;\n        }\n        if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK ||\n            (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK) {\n            error = form_error;\n        }\n        zend_tmp_string_release(tmp_postval);\n        zend_string_release_ex(string_key, 0);\n    }\n    ZEND_HASH_FOREACH_END();\n\n    SAVE_CURL_ERROR(ch, error);\n    if (error != CURLE_OK) {\n        return FAILURE;\n    }\n\n    if ((*ch->clone) == 1) {\n        zend_llist_clean(&ch->to_free->post);\n    }\n    zend_llist_add_element(&ch->to_free->post, &mime);\n    error = curl_easy_setopt(ch->cp, CURLOPT_MIMEPOST, mime);\n\n    SAVE_CURL_ERROR(ch, error);\n    return error == CURLE_OK ? SUCCESS : FAILURE;\n}\n/* }}} */\n#endif\n\n/* {{{ proto resource curl_copy_handle(resource ch)\n   Copy a cURL handle along with all of it's preferences */\nPHP_FUNCTION(swoole_native_curl_copy_handle) {\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    RETURN_OBJ(swoole_curl_clone_obj(Z_OBJ_P(zid)));\n}\n/* }}} */\n\nstatic zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool is_array_config) {\n    CURLcode error = CURLE_OK;\n    zend_long lval;\n\n    switch (option) {\n    /* Long options */\n    case CURLOPT_SSL_VERIFYHOST:\n        lval = zval_get_long(zvalue);\n        if (lval == 1) {\n            php_error_docref(\n                NULL, E_NOTICE, \"CURLOPT_SSL_VERIFYHOST no longer accepts the value 1, value 2 will be used instead\");\n            error = curl_easy_setopt(ch->cp, (CURLoption) option, 2L);\n            break;\n        }\n        ZEND_FALLTHROUGH;\n    case CURLOPT_AUTOREFERER:\n    case CURLOPT_BUFFERSIZE:\n    case CURLOPT_CONNECTTIMEOUT:\n    case CURLOPT_COOKIESESSION:\n    case CURLOPT_CRLF:\n    case CURLOPT_DNS_CACHE_TIMEOUT:\n    case CURLOPT_DNS_USE_GLOBAL_CACHE:\n    case CURLOPT_FAILONERROR:\n    case CURLOPT_FILETIME:\n    case CURLOPT_FORBID_REUSE:\n    case CURLOPT_FRESH_CONNECT:\n    case CURLOPT_FTP_USE_EPRT:\n    case CURLOPT_FTP_USE_EPSV:\n    case CURLOPT_HEADER:\n    case CURLOPT_HTTPGET:\n    case CURLOPT_HTTPPROXYTUNNEL:\n    case CURLOPT_HTTP_VERSION:\n    case CURLOPT_INFILESIZE:\n    case CURLOPT_LOW_SPEED_LIMIT:\n    case CURLOPT_LOW_SPEED_TIME:\n    case CURLOPT_MAXCONNECTS:\n    case CURLOPT_MAXREDIRS:\n    case CURLOPT_NETRC:\n    case CURLOPT_NOBODY:\n    case CURLOPT_NOPROGRESS:\n    case CURLOPT_NOSIGNAL:\n    case CURLOPT_PORT:\n    case CURLOPT_POST:\n    case CURLOPT_PROXYPORT:\n    case CURLOPT_PROXYTYPE:\n    case CURLOPT_PUT:\n    case CURLOPT_RESUME_FROM:\n    case CURLOPT_SSLVERSION:\n    case CURLOPT_SSL_VERIFYPEER:\n    case CURLOPT_TIMECONDITION:\n    case CURLOPT_TIMEOUT:\n    case CURLOPT_TIMEVALUE:\n    case CURLOPT_TRANSFERTEXT:\n    case CURLOPT_UNRESTRICTED_AUTH:\n    case CURLOPT_UPLOAD:\n    case CURLOPT_VERBOSE:\n    case CURLOPT_HTTPAUTH:\n    case CURLOPT_FTP_CREATE_MISSING_DIRS:\n    case CURLOPT_PROXYAUTH:\n    case CURLOPT_FTP_RESPONSE_TIMEOUT:\n    case CURLOPT_IPRESOLVE:\n    case CURLOPT_MAXFILESIZE:\n    case CURLOPT_TCP_NODELAY:\n    case CURLOPT_FTPSSLAUTH:\n    case CURLOPT_IGNORE_CONTENT_LENGTH:\n    case CURLOPT_FTP_SKIP_PASV_IP:\n    case CURLOPT_FTP_FILEMETHOD:\n    case CURLOPT_CONNECT_ONLY:\n    case CURLOPT_LOCALPORT:\n    case CURLOPT_LOCALPORTRANGE:\n    case CURLOPT_SSL_SESSIONID_CACHE:\n    case CURLOPT_FTP_SSL_CCC:\n    case CURLOPT_SSH_AUTH_TYPES:\n    case CURLOPT_CONNECTTIMEOUT_MS:\n    case CURLOPT_HTTP_CONTENT_DECODING:\n    case CURLOPT_HTTP_TRANSFER_DECODING:\n    case CURLOPT_TIMEOUT_MS:\n    case CURLOPT_NEW_DIRECTORY_PERMS:\n    case CURLOPT_NEW_FILE_PERMS:\n    case CURLOPT_USE_SSL:\n    case CURLOPT_APPEND:\n    case CURLOPT_DIRLISTONLY:\n    case CURLOPT_PROXY_TRANSFER_MODE:\n    case CURLOPT_ADDRESS_SCOPE:\n    case CURLOPT_CERTINFO:\n    case CURLOPT_PROTOCOLS:\n    case CURLOPT_REDIR_PROTOCOLS:\n    case CURLOPT_SOCKS5_GSSAPI_NEC:\n    case CURLOPT_TFTP_BLKSIZE:\n    case CURLOPT_FTP_USE_PRET:\n    case CURLOPT_RTSP_CLIENT_CSEQ:\n    case CURLOPT_RTSP_REQUEST:\n    case CURLOPT_RTSP_SERVER_CSEQ:\n    case CURLOPT_WILDCARDMATCH:\n    case CURLOPT_GSSAPI_DELEGATION:\n    case CURLOPT_ACCEPTTIMEOUT_MS:\n    case CURLOPT_SSL_OPTIONS:\n    case CURLOPT_TCP_KEEPALIVE:\n    case CURLOPT_TCP_KEEPIDLE:\n    case CURLOPT_TCP_KEEPINTVL:\n    case CURLOPT_SASL_IR:\n    case CURLOPT_EXPECT_100_TIMEOUT_MS:\n    case CURLOPT_SSL_ENABLE_ALPN:\n    case CURLOPT_SSL_ENABLE_NPN:\n    case CURLOPT_HEADEROPT:\n    case CURLOPT_SSL_VERIFYSTATUS:\n    case CURLOPT_PATH_AS_IS:\n    case CURLOPT_SSL_FALSESTART:\n    case CURLOPT_PIPEWAIT:\n    case CURLOPT_STREAM_WEIGHT:\n    case CURLOPT_TFTP_NO_OPTIONS:\n    case CURLOPT_TCP_FASTOPEN:\n    case CURLOPT_KEEP_SENDING_ON_ERROR:\n    case CURLOPT_PROXY_SSL_OPTIONS:\n    case CURLOPT_PROXY_SSL_VERIFYHOST:\n    case CURLOPT_PROXY_SSL_VERIFYPEER:\n    case CURLOPT_PROXY_SSLVERSION:\n    case CURLOPT_SUPPRESS_CONNECT_HEADERS:\n    case CURLOPT_SOCKS5_AUTH:\n    case CURLOPT_SSH_COMPRESSION:\n#if LIBCURL_VERSION_NUM >= 0x073b00 /* Available since 7.59.0 */\n    case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x073c00 /* Available since 7.60.0 */\n    case CURLOPT_DNS_SHUFFLE_ADDRESSES:\n    case CURLOPT_HAPROXYPROTOCOL:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x073d00 /* Available since 7.61.0 */\n    case CURLOPT_DISALLOW_USERNAME_IN_URL:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x073E00 && PHP_VERSION_ID >= 80200 /* Available since 7.62.0 */\n    case CURLOPT_UPKEEP_INTERVAL_MS:\n    case CURLOPT_UPLOAD_BUFFERSIZE:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074000 /* Available since 7.64.0 */\n    case CURLOPT_HTTP09_ALLOWED:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074001 && PHP_VERSION_ID >= 80200 /* Available since 7.64.1 */\n    case CURLOPT_ALTSVC_CTRL:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074100 && PHP_VERSION_ID >= 80200 /* Available since 7.65.0 */\n    case CURLOPT_MAXAGE_CONN:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074500 && PHP_VERSION_ID >= 80200 /* Available since 7.69.0 */\n    case CURLOPT_MAIL_RCPT_ALLLOWFAILS:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074a00 && PHP_VERSION_ID >= 80200 /* Available since 7.74.0 */\n    case CURLOPT_HSTS_CTRL:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074c00 && PHP_VERSION_ID >= 80200 /* Available since 7.76.0 */\n    case CURLOPT_DOH_SSL_VERIFYHOST:\n    case CURLOPT_DOH_SSL_VERIFYPEER:\n    case CURLOPT_DOH_SSL_VERIFYSTATUS:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075000 && PHP_VERSION_ID >= 80200 /* Available since 7.80.0 */\n    case CURLOPT_MAXLIFETIME_CONN:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075100 && PHP_VERSION_ID >= 80300 /* Available since 7.81.0 */\n    case CURLOPT_MIME_OPTIONS:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075600 && PHP_VERSION_ID >= 80300 /* Available since 7.86.0 */\n    case CURLOPT_WS_OPTIONS:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075700 && PHP_VERSION_ID >= 80300 /* Available since 7.87.0 */\n    case CURLOPT_CA_CACHE_TIMEOUT:\n    case CURLOPT_QUICK_EXIT:\n#endif\n        lval = zval_get_long(zvalue);\n        if ((option == CURLOPT_PROTOCOLS || option == CURLOPT_REDIR_PROTOCOLS) &&\n            (PG(open_basedir) && *PG(open_basedir)) && (lval & CURLPROTO_FILE)) {\n            php_error_docref(NULL, E_WARNING, \"CURLPROTO_FILE cannot be activated when an open_basedir is set\");\n            return FAILURE;\n        }\n#if defined(ZTS)\n        if (option == CURLOPT_DNS_USE_GLOBAL_CACHE && lval) {\n            php_error_docref(\n                NULL, E_WARNING, \"CURLOPT_DNS_USE_GLOBAL_CACHE cannot be activated when thread safety is enabled\");\n            return FAILURE;\n        }\n#endif\n        error = curl_easy_setopt(ch->cp, (CURLoption) option, lval);\n        break;\n    case CURLOPT_SAFE_UPLOAD:\n        if (!zend_is_true(zvalue)) {\n            zend_value_error(\"%s(): Disabling safe uploads is no longer supported\", get_active_function_name());\n            return FAILURE;\n        }\n        break;\n\n    /* String options */\n    case CURLOPT_CAINFO:\n    case CURLOPT_CAPATH:\n    case CURLOPT_COOKIE:\n    case CURLOPT_EGDSOCKET:\n    case CURLOPT_INTERFACE:\n    case CURLOPT_PROXY:\n    case CURLOPT_PROXYUSERPWD:\n    case CURLOPT_REFERER:\n    case CURLOPT_SSLCERTTYPE:\n    case CURLOPT_SSLENGINE:\n    case CURLOPT_SSLENGINE_DEFAULT:\n    case CURLOPT_SSLKEY:\n    case CURLOPT_SSLKEYPASSWD:\n    case CURLOPT_SSLKEYTYPE:\n    case CURLOPT_SSL_CIPHER_LIST:\n    case CURLOPT_USERAGENT:\n#if PHP_VERSION_ID < 80300\n    case CURLOPT_USERPWD:\n#endif\n    case CURLOPT_COOKIELIST:\n    case CURLOPT_FTP_ALTERNATIVE_TO_USER:\n    case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:\n#if PHP_VERSION_ID < 80300\n    case CURLOPT_PASSWORD:\n#endif\n    case CURLOPT_PROXYPASSWORD:\n    case CURLOPT_PROXYUSERNAME:\n#if PHP_VERSION_ID < 80300\n    case CURLOPT_USERNAME:\n#endif\n    case CURLOPT_NOPROXY:\n    case CURLOPT_SOCKS5_GSSAPI_SERVICE:\n    case CURLOPT_MAIL_FROM:\n    case CURLOPT_RTSP_STREAM_URI:\n    case CURLOPT_RTSP_TRANSPORT:\n    case CURLOPT_TLSAUTH_TYPE:\n    case CURLOPT_TLSAUTH_PASSWORD:\n    case CURLOPT_TLSAUTH_USERNAME:\n    case CURLOPT_TRANSFER_ENCODING:\n    case CURLOPT_DNS_SERVERS:\n    case CURLOPT_MAIL_AUTH:\n    case CURLOPT_LOGIN_OPTIONS:\n    case CURLOPT_PINNEDPUBLICKEY:\n    case CURLOPT_PROXY_SERVICE_NAME:\n    case CURLOPT_SERVICE_NAME:\n    case CURLOPT_DEFAULT_PROTOCOL:\n    case CURLOPT_PRE_PROXY:\n    case CURLOPT_PROXY_CAINFO:\n    case CURLOPT_PROXY_CAPATH:\n    case CURLOPT_PROXY_CRLFILE:\n    case CURLOPT_PROXY_KEYPASSWD:\n    case CURLOPT_PROXY_PINNEDPUBLICKEY:\n    case CURLOPT_PROXY_SSL_CIPHER_LIST:\n    case CURLOPT_PROXY_SSLCERT:\n    case CURLOPT_PROXY_SSLCERTTYPE:\n    case CURLOPT_PROXY_SSLKEY:\n    case CURLOPT_PROXY_SSLKEYTYPE:\n    case CURLOPT_PROXY_TLSAUTH_PASSWORD:\n    case CURLOPT_PROXY_TLSAUTH_TYPE:\n    case CURLOPT_PROXY_TLSAUTH_USERNAME:\n    case CURLOPT_ABSTRACT_UNIX_SOCKET:\n    case CURLOPT_REQUEST_TARGET:\n#if LIBCURL_VERSION_NUM >= 0x073d00 /* Available since 7.61.0 */\n    case CURLOPT_PROXY_TLS13_CIPHERS:\n    case CURLOPT_TLS13_CIPHERS:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074001 && PHP_VERSION_ID >= 80200 /* Available since 7.64.1 */\n    case CURLOPT_ALTSVC:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074200 && PHP_VERSION_ID >= 80200 /* Available since 7.66.0 */\n    case CURLOPT_SASL_AUTHZID:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */\n    case CURLOPT_PROXY_ISSUERCERT:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074900 && PHP_VERSION_ID >= 80200 /* Available since 7.73.0 */\n    case CURLOPT_SSL_EC_CURVES:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074b00 && PHP_VERSION_ID >= 80200 /* Available since 7.75.0 */\n    case CURLOPT_AWS_SIGV4:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075000 && PHP_VERSION_ID >= 80200 /* Available since 7.80.0 */\n    case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075500 && PHP_VERSION_ID >= 80300 /* Available since 7.85.0 */\n    case CURLOPT_PROTOCOLS_STR:\n    case CURLOPT_REDIR_PROTOCOLS_STR:\n#endif\n    {\n        zend_string *tmp_str;\n        zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);\n#if LIBCURL_VERSION_NUM >= 0x075500 && PHP_VERSION_ID >= 80300 /* Available since 7.85.0 */\n        if ((option == CURLOPT_PROTOCOLS_STR || option == CURLOPT_REDIR_PROTOCOLS_STR) &&\n            (PG(open_basedir) && *PG(open_basedir)) &&\n            (php_memnistr(ZSTR_VAL(str), \"file\", sizeof(\"file\") - 1, ZSTR_VAL(str) + ZSTR_LEN(str)) != NULL ||\n             php_memnistr(ZSTR_VAL(str), \"all\", sizeof(\"all\") - 1, ZSTR_VAL(str) + ZSTR_LEN(str)) != NULL)) {\n            zend_tmp_string_release(tmp_str);\n            php_error_docref(NULL, E_WARNING, \"The FILE protocol cannot be activated when an open_basedir is set\");\n            return FAILURE;\n        }\n#endif\n        zend_result ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str));\n        zend_tmp_string_release(tmp_str);\n        return ret;\n    }\n\n    /* Curl nullable string options */\n    case CURLOPT_CUSTOMREQUEST:\n    case CURLOPT_FTPPORT:\n    case CURLOPT_RANGE:\n    case CURLOPT_FTP_ACCOUNT:\n    case CURLOPT_RTSP_SESSION_ID:\n    case CURLOPT_ACCEPT_ENCODING:\n    case CURLOPT_DNS_INTERFACE:\n    case CURLOPT_DNS_LOCAL_IP4:\n    case CURLOPT_DNS_LOCAL_IP6:\n    case CURLOPT_XOAUTH2_BEARER:\n    case CURLOPT_UNIX_SOCKET_PATH:\n#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */\n    case CURLOPT_DOH_URL:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074a00 && PHP_VERSION_ID >= 80200 /* Available since 7.74.0 */\n    case CURLOPT_HSTS:\n#endif\n    case CURLOPT_KRBLEVEL:\n#if PHP_VERSION_ID >= 80300\n        // Authorization header would be implictly set\n        // with an empty string thus we explictly set the option\n        // to null to avoid this unwarranted side effect\n    case CURLOPT_USERPWD:\n    case CURLOPT_USERNAME:\n    case CURLOPT_PASSWORD:\n#endif\n    {\n        if (Z_ISNULL_P(zvalue)) {\n            error = curl_easy_setopt(ch->cp, (CURLoption) option, NULL);\n        } else {\n            zend_string *tmp_str;\n            zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);\n            zend_result ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str));\n            zend_tmp_string_release(tmp_str);\n            return ret;\n        }\n        break;\n    }\n\n    /* Curl private option */\n    case CURLOPT_PRIVATE: {\n        zval_ptr_dtor(&ch->private_data);\n        ZVAL_COPY(&ch->private_data, zvalue);\n        return SUCCESS;\n    }\n\n        /* Curl url option */\n    case CURLOPT_URL: {\n        zend_string *tmp_str;\n        zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);\n#if PHP_VERSION_ID >= 80300\n        zend_result ret = php_curl_option_url(ch, str);\n#else\n        zend_result ret = php_curl_option_url(ch, ZSTR_VAL(str), ZSTR_LEN(str));\n#endif\n        zend_tmp_string_release(tmp_str);\n        return ret;\n    }\n\n    /* Curl file handle options */\n    case CURLOPT_FILE:\n    case CURLOPT_INFILE:\n    case CURLOPT_STDERR:\n    case CURLOPT_WRITEHEADER: {\n        FILE *fp = NULL;\n        php_stream *what = NULL;\n\n        if (Z_TYPE_P(zvalue) != IS_NULL) {\n            what = (php_stream *) zend_fetch_resource2_ex(\n                zvalue, \"File-Handle\", php_file_le_stream(), php_file_le_pstream());\n            if (!what) {\n                return FAILURE;\n            }\n\n            if (FAILURE == php_stream_cast(what, PHP_STREAM_AS_STDIO, (void **) &fp, REPORT_ERRORS)) {\n                return FAILURE;\n            }\n\n            if (!fp) {\n                return FAILURE;\n            }\n        }\n\n        error = CURLE_OK;\n        switch (option) {\n        case CURLOPT_FILE:\n            if (!what) {\n                if (!Z_ISUNDEF(ch->handlers.write->stream)) {\n                    zval_ptr_dtor(&ch->handlers.write->stream);\n                    ZVAL_UNDEF(&ch->handlers.write->stream);\n                }\n                ch->handlers.write->fp = NULL;\n                ch->handlers.write->method = PHP_CURL_STDOUT;\n            } else if (what->mode[0] != 'r' || what->mode[1] == '+') {\n                zval_ptr_dtor(&ch->handlers.write->stream);\n                ch->handlers.write->fp = fp;\n                ch->handlers.write->method = PHP_CURL_FILE;\n                ZVAL_COPY(&ch->handlers.write->stream, zvalue);\n            } else {\n                zend_value_error(\"%s(): The provided file handle must be writable\", get_active_function_name());\n                return FAILURE;\n            }\n            break;\n        case CURLOPT_WRITEHEADER:\n            if (!what) {\n                if (!Z_ISUNDEF(ch->handlers.write_header->stream)) {\n                    zval_ptr_dtor(&ch->handlers.write_header->stream);\n                    ZVAL_UNDEF(&ch->handlers.write_header->stream);\n                }\n                ch->handlers.write_header->fp = NULL;\n                ch->handlers.write_header->method = PHP_CURL_IGNORE;\n            } else if (what->mode[0] != 'r' || what->mode[1] == '+') {\n                zval_ptr_dtor(&ch->handlers.write_header->stream);\n                ch->handlers.write_header->fp = fp;\n                ch->handlers.write_header->method = PHP_CURL_FILE;\n                ZVAL_COPY(&ch->handlers.write_header->stream, zvalue);\n            } else {\n                zend_value_error(\"%s(): The provided file handle must be writable\", get_active_function_name());\n                return FAILURE;\n            }\n            break;\n        case CURLOPT_INFILE:\n            if (!what) {\n                if (!Z_ISUNDEF(ch->handlers.read->stream)) {\n                    zval_ptr_dtor(&ch->handlers.read->stream);\n                    ZVAL_UNDEF(&ch->handlers.read->stream);\n                }\n                ch->handlers.read->fp = NULL;\n                ch->handlers.read->res = NULL;\n            } else {\n                zval_ptr_dtor(&ch->handlers.read->stream);\n                ch->handlers.read->fp = fp;\n                ch->handlers.read->res = Z_RES_P(zvalue);\n                ZVAL_COPY(&ch->handlers.read->stream, zvalue);\n            }\n            break;\n        case CURLOPT_STDERR:\n            if (!what) {\n                if (!Z_ISUNDEF(ch->handlers.std_err)) {\n                    zval_ptr_dtor(&ch->handlers.std_err);\n                    ZVAL_UNDEF(&ch->handlers.std_err);\n                }\n            } else if (what->mode[0] != 'r' || what->mode[1] == '+') {\n                zval_ptr_dtor(&ch->handlers.std_err);\n                ZVAL_COPY(&ch->handlers.std_err, zvalue);\n            } else {\n                zend_value_error(\"%s(): The provided file handle must be writable\", get_active_function_name());\n                return FAILURE;\n            }\n            ZEND_FALLTHROUGH;\n        default:\n            error = curl_easy_setopt(ch->cp, (CURLoption) option, fp);\n            break;\n        }\n        break;\n    }\n\n    /* Curl linked list options */\n    case CURLOPT_HTTP200ALIASES:\n    case CURLOPT_HTTPHEADER:\n    case CURLOPT_POSTQUOTE:\n    case CURLOPT_PREQUOTE:\n    case CURLOPT_QUOTE:\n    case CURLOPT_TELNETOPTIONS:\n    case CURLOPT_MAIL_RCPT:\n    case CURLOPT_RESOLVE:\n    case CURLOPT_PROXYHEADER:\n    case CURLOPT_CONNECT_TO: {\n        zval *current;\n        HashTable *ph;\n        zend_string *val, *tmp_val;\n        struct curl_slist *slist = NULL;\n\n        if (Z_TYPE_P(zvalue) != IS_ARRAY) {\n            const char *name = NULL;\n            switch (option) {\n            case CURLOPT_HTTPHEADER:\n                name = \"CURLOPT_HTTPHEADER\";\n                break;\n            case CURLOPT_QUOTE:\n                name = \"CURLOPT_QUOTE\";\n                break;\n            case CURLOPT_HTTP200ALIASES:\n                name = \"CURLOPT_HTTP200ALIASES\";\n                break;\n            case CURLOPT_POSTQUOTE:\n                name = \"CURLOPT_POSTQUOTE\";\n                break;\n            case CURLOPT_PREQUOTE:\n                name = \"CURLOPT_PREQUOTE\";\n                break;\n            case CURLOPT_TELNETOPTIONS:\n                name = \"CURLOPT_TELNETOPTIONS\";\n                break;\n            case CURLOPT_MAIL_RCPT:\n                name = \"CURLOPT_MAIL_RCPT\";\n                break;\n            case CURLOPT_RESOLVE:\n                name = \"CURLOPT_RESOLVE\";\n                break;\n            case CURLOPT_PROXYHEADER:\n                name = \"CURLOPT_PROXYHEADER\";\n                break;\n            case CURLOPT_CONNECT_TO:\n                name = \"CURLOPT_CONNECT_TO\";\n                break;\n            }\n            zend_type_error(\"%s(): The %s option must have an array value\", get_active_function_name(), name);\n            return FAILURE;\n        }\n\n        ph = Z_ARRVAL_P(zvalue);\n        ZEND_HASH_FOREACH_VAL(ph, current) {\n            ZVAL_DEREF(current);\n            val = zval_get_tmp_string(current, &tmp_val);\n#if PHP_VERSION_ID >= 80300\n            struct curl_slist *new_slist = curl_slist_append(slist, ZSTR_VAL(val));\n            zend_tmp_string_release(tmp_val);\n            if (!new_slist) {\n                curl_slist_free_all(slist);\n                php_error_docref(NULL, E_WARNING, \"Could not build curl_slist\");\n                return FAILURE;\n            }\n            slist = new_slist;\n#else\n            slist = curl_slist_append(slist, ZSTR_VAL(val));\n            zend_tmp_string_release(tmp_val);\n            if (!slist) {\n                php_error_docref(NULL, E_WARNING, \"Could not build curl_slist\");\n                return FAILURE;\n            }\n#endif\n        }\n        ZEND_HASH_FOREACH_END();\n\n        if (slist) {\n            if ((*ch->clone) == 1) {\n                zend_hash_index_update_ptr(ch->to_free->slist, option, slist);\n            } else {\n                zend_hash_next_index_insert_ptr(ch->to_free->slist, slist);\n            }\n        }\n\n        error = curl_easy_setopt(ch->cp, (CURLoption) option, slist);\n\n        break;\n    }\n\n    case CURLOPT_BINARYTRANSFER:\n        /* Do nothing, just backward compatibility */\n        break;\n\n    case CURLOPT_FOLLOWLOCATION:\n        lval = zend_is_true(zvalue);\n        error = curl_easy_setopt(ch->cp, (CURLoption) option, (long) lval);\n        break;\n\n    case CURLOPT_HEADERFUNCTION:\n        if (!Z_ISUNDEF(ch->handlers.write_header->func_name)) {\n            zval_ptr_dtor(&ch->handlers.write_header->func_name);\n            ch->handlers.write_header->fci_cache = empty_fcall_info_cache;\n        }\n        ZVAL_COPY(&ch->handlers.write_header->func_name, zvalue);\n        ch->handlers.write_header->method = PHP_CURL_USER;\n        break;\n\n    case CURLOPT_POSTFIELDS:\n        if (Z_TYPE_P(zvalue) == IS_ARRAY) {\n            if (zend_hash_num_elements(HASH_OF(zvalue)) == 0) {\n                /* no need to build the mime structure for empty hashtables;\n                   also works around https://github.com/curl/curl/issues/6455 */\n                curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDS, \"\");\n                error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, 0L);\n            } else {\n                return build_mime_structure_from_hash(ch, zvalue);\n            }\n        } else {\n            zend_string *tmp_str;\n            zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);\n            /* with curl 7.17.0 and later, we can use COPYPOSTFIELDS, but we have to provide size before */\n            error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, ZSTR_LEN(str));\n            error = curl_easy_setopt(ch->cp, CURLOPT_COPYPOSTFIELDS, ZSTR_VAL(str));\n            zend_tmp_string_release(tmp_str);\n        }\n        break;\n\n    case CURLOPT_PROGRESSFUNCTION:\n        curl_easy_setopt(ch->cp, CURLOPT_PROGRESSFUNCTION, fn_progress);\n        curl_easy_setopt(ch->cp, CURLOPT_PROGRESSDATA, ch);\n        if (ch->handlers.progress == NULL) {\n            ch->handlers.progress = (php_curl_callback *) ecalloc(1, sizeof(php_curl_callback));\n        } else if (!Z_ISUNDEF(ch->handlers.progress->func_name)) {\n            zval_ptr_dtor(&ch->handlers.progress->func_name);\n            ch->handlers.progress->fci_cache = empty_fcall_info_cache;\n        }\n        ZVAL_COPY(&ch->handlers.progress->func_name, zvalue);\n        break;\n\n#if LIBCURL_VERSION_NUM >= 0x075400 && PHP_VERSION_ID >= 80300\n    case CURLOPT_SSH_HOSTKEYFUNCTION:\n        curl_easy_setopt(ch->cp, CURLOPT_SSH_HOSTKEYFUNCTION, fn_ssh_hostkeyfunction);\n        curl_easy_setopt(ch->cp, CURLOPT_SSH_HOSTKEYDATA, ch);\n        if (ch->handlers.sshhostkey == NULL) {\n            ch->handlers.sshhostkey = (php_curl_callback *) ecalloc(1, sizeof(php_curl_callback));\n        } else if (!Z_ISUNDEF(ch->handlers.sshhostkey->func_name)) {\n            zval_ptr_dtor(&ch->handlers.sshhostkey->func_name);\n            ch->handlers.sshhostkey->fci_cache = empty_fcall_info_cache;\n        }\n        ZVAL_COPY(&ch->handlers.sshhostkey->func_name, zvalue);\n        break;\n#endif\n\n    case CURLOPT_READFUNCTION:\n        if (!Z_ISUNDEF(ch->handlers.read->func_name)) {\n            zval_ptr_dtor(&ch->handlers.read->func_name);\n            ch->handlers.read->fci_cache = empty_fcall_info_cache;\n        }\n        ZVAL_COPY(&ch->handlers.read->func_name, zvalue);\n        ch->handlers.read->method = PHP_CURL_USER;\n        break;\n\n    case CURLOPT_RETURNTRANSFER:\n        if (zend_is_true(zvalue)) {\n            ch->handlers.write->method = PHP_CURL_RETURN;\n        } else {\n            ch->handlers.write->method = PHP_CURL_STDOUT;\n        }\n        break;\n\n    case CURLOPT_WRITEFUNCTION:\n        if (!Z_ISUNDEF(ch->handlers.write->func_name)) {\n            zval_ptr_dtor(&ch->handlers.write->func_name);\n            ch->handlers.write->fci_cache = empty_fcall_info_cache;\n        }\n        ZVAL_COPY(&ch->handlers.write->func_name, zvalue);\n        ch->handlers.write->method = PHP_CURL_USER;\n        break;\n\n#if PHP_VERSION_ID >= 80200\n    case CURLOPT_XFERINFOFUNCTION:\n        curl_easy_setopt(ch->cp, CURLOPT_XFERINFOFUNCTION, fn_xferinfo);\n        curl_easy_setopt(ch->cp, CURLOPT_XFERINFODATA, ch);\n        if (ch->handlers.xferinfo == NULL) {\n            ch->handlers.xferinfo = (php_curl_callback *) ecalloc(1, sizeof(php_curl_callback));\n        } else if (!Z_ISUNDEF(ch->handlers.xferinfo->func_name)) {\n            zval_ptr_dtor(&ch->handlers.xferinfo->func_name);\n            ch->handlers.xferinfo->fci_cache = empty_fcall_info_cache;\n        }\n        ZVAL_COPY(&ch->handlers.xferinfo->func_name, zvalue);\n        break;\n#endif\n\n    /* Curl off_t options */\n    case CURLOPT_MAX_RECV_SPEED_LARGE:\n    case CURLOPT_MAX_SEND_SPEED_LARGE:\n#if PHP_VERSION_ID >= 80200\n    case CURLOPT_MAXFILESIZE_LARGE:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x073b00 /* Available since 7.59.0 */\n    case CURLOPT_TIMEVALUE_LARGE:\n#endif\n        lval = zval_get_long(zvalue);\n        error = curl_easy_setopt(ch->cp, (CURLoption) option, (curl_off_t) lval);\n        break;\n\n    case CURLOPT_POSTREDIR:\n        lval = zval_get_long(zvalue);\n        error = curl_easy_setopt(ch->cp, CURLOPT_POSTREDIR, (long) (lval & CURL_REDIR_POST_ALL));\n        break;\n\n    /* the following options deal with files, therefore the open_basedir check\n     * is required.\n     */\n    case CURLOPT_COOKIEFILE:\n    case CURLOPT_COOKIEJAR:\n    case CURLOPT_RANDOM_FILE:\n    case CURLOPT_SSLCERT:\n    case CURLOPT_NETRC_FILE:\n    case CURLOPT_SSH_PRIVATE_KEYFILE:\n    case CURLOPT_SSH_PUBLIC_KEYFILE:\n    case CURLOPT_CRLFILE:\n    case CURLOPT_ISSUERCERT:\n    case CURLOPT_SSH_KNOWNHOSTS: {\n        zend_string *tmp_str;\n        zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);\n        zend_result ret;\n\n        if (ZSTR_LEN(str) && php_check_open_basedir(ZSTR_VAL(str))) {\n            zend_tmp_string_release(tmp_str);\n            return FAILURE;\n        }\n\n        ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str));\n        zend_tmp_string_release(tmp_str);\n        return ret;\n    }\n\n    case CURLINFO_HEADER_OUT:\n        if (zend_is_true(zvalue)) {\n            curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, curl_debug);\n            curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *) ch);\n            curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 1L);\n        } else {\n            curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, NULL);\n            curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, NULL);\n            curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0L);\n        }\n        break;\n\n    case CURLOPT_SHARE: {\n        if (Z_TYPE_P(zvalue) == IS_OBJECT && Z_OBJCE_P(zvalue) == curl_share_ce) {\n            php_curlsh *sh = Z_CURL_SHARE_P(zvalue);\n            curl_easy_setopt(ch->cp, CURLOPT_SHARE, sh->share);\n\n            if (ch->share) {\n                OBJ_RELEASE(&ch->share->std);\n            }\n            GC_ADDREF(&sh->std);\n            ch->share = sh;\n        }\n    } break;\n\n    case CURLOPT_FNMATCH_FUNCTION:\n        curl_easy_setopt(ch->cp, CURLOPT_FNMATCH_FUNCTION, fn_fnmatch);\n        curl_easy_setopt(ch->cp, CURLOPT_FNMATCH_DATA, ch);\n        if (ch->handlers.fnmatch == NULL) {\n            ch->handlers.fnmatch = (php_curl_callback *) ecalloc(1, sizeof(php_curl_callback));\n        } else if (!Z_ISUNDEF(ch->handlers.fnmatch->func_name)) {\n            zval_ptr_dtor(&ch->handlers.fnmatch->func_name);\n            ch->handlers.fnmatch->fci_cache = empty_fcall_info_cache;\n        }\n        ZVAL_COPY(&ch->handlers.fnmatch->func_name, zvalue);\n        break;\n\n        /* Curl blob options */\n#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */\n    case CURLOPT_ISSUERCERT_BLOB:\n    case CURLOPT_PROXY_ISSUERCERT_BLOB:\n    case CURLOPT_PROXY_SSLCERT_BLOB:\n    case CURLOPT_PROXY_SSLKEY_BLOB:\n    case CURLOPT_SSLCERT_BLOB:\n    case CURLOPT_SSLKEY_BLOB:\n#if LIBCURL_VERSION_NUM >= 0x074d00 && PHP_VERSION_ID >= 80200 /* Available since 7.77.0 */\n    case CURLOPT_CAINFO_BLOB:\n    case CURLOPT_PROXY_CAINFO_BLOB:\n#endif\n    {\n        zend_string *tmp_str;\n        zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);\n\n        struct curl_blob stblob;\n        stblob.data = ZSTR_VAL(str);\n        stblob.len = ZSTR_LEN(str);\n        stblob.flags = CURL_BLOB_COPY;\n        error = curl_easy_setopt(ch->cp, (CURLoption) option, &stblob);\n\n        zend_tmp_string_release(tmp_str);\n    } break;\n#endif\n\n    default:\n        if (is_array_config) {\n            zend_argument_value_error(2, \"must contain only valid cURL options\");\n        } else {\n            zend_argument_value_error(2, \"is not a valid cURL option\");\n        }\n        error = CURLE_UNKNOWN_OPTION;\n        break;\n    }\n\n    SAVE_CURL_ERROR(ch, error);\n    if (error != CURLE_OK) {\n        return FAILURE;\n    } else {\n        return SUCCESS;\n    }\n}\n/* }}} */\n\n/* {{{ proto bool curl_setopt(resource ch, int option, mixed value)\n   Set an option for a cURL transfer */\nPHP_FUNCTION(swoole_native_curl_setopt) {\n    zval *zid, *zvalue;\n    zend_long options;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(3, 3)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    Z_PARAM_LONG(options)\n    Z_PARAM_ZVAL(zvalue)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid, false)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    RETURN_BOOL(_php_curl_setopt(ch, options, zvalue, 0) == SUCCESS);\n}\n/* }}} */\n\n/* {{{ proto bool curl_setopt_array(resource ch, array options)\n   Set an array of option for a cURL transfer */\nPHP_FUNCTION(swoole_native_curl_setopt_array) {\n    zval *zid, *arr, *entry;\n    php_curl *ch;\n    zend_ulong option;\n    zend_string *string_key;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    Z_PARAM_ARRAY(arr)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid, false)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(arr), option, string_key, entry) {\n        if (string_key) {\n            zend_argument_value_error(2, \"contains an invalid cURL option\");\n            RETURN_THROWS();\n        }\n        ZVAL_DEREF(entry);\n        if (_php_curl_setopt(ch, (zend_long) option, entry, 1) == FAILURE) {\n            RETURN_FALSE;\n        }\n    }\n    ZEND_HASH_FOREACH_END();\n\n    RETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ _php_curl_cleanup_handle(ch)\n   Cleanup an execution phase */\nvoid swoole_curl_cleanup_handle(php_curl *ch) {\n    smart_str_free(&ch->handlers.write->buf);\n    if (ch->header.str) {\n        zend_string_release_ex(ch->header.str, 0);\n        ch->header.str = NULL;\n    }\n\n    memset(ch->err.str, 0, CURL_ERROR_SIZE + 1);\n    ch->err.no = 0;\n}\n/* }}} */\n\n/* {{{ proto bool curl_exec(resource ch)\n   Perform a cURL session */\nPHP_FUNCTION(swoole_native_curl_exec) {\n    CURLcode error;\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    swoole_curl_verify_handlers(ch, /* reporterror */ true);\n    swoole_curl_cleanup_handle(ch);\n    error = swoole_curl_easy_perform(ch->cp);\n    SAVE_CURL_ERROR(ch, error);\n\n    if (error != CURLE_OK) {\n        smart_str_free(&ch->handlers.write->buf);\n        RETURN_FALSE;\n    }\n\n    if (!Z_ISUNDEF(ch->handlers.std_err)) {\n        php_stream *stream;\n        stream = (php_stream *) zend_fetch_resource2_ex(\n            &ch->handlers.std_err, NULL, php_file_le_stream(), php_file_le_pstream());\n        if (stream) {\n            php_stream_flush(stream);\n        }\n    }\n\n    if (ch->handlers.write->method == PHP_CURL_RETURN && ch->handlers.write->buf.s) {\n        smart_str_0(&ch->handlers.write->buf);\n        RETURN_STR_COPY(ch->handlers.write->buf.s);\n    }\n\n    /* flush the file handle, so any remaining data is synched to disk */\n    if (ch->handlers.write->method == PHP_CURL_FILE && ch->handlers.write->fp) {\n        fflush(ch->handlers.write->fp);\n    }\n    if (ch->handlers.write_header->method == PHP_CURL_FILE && ch->handlers.write_header->fp) {\n        fflush(ch->handlers.write_header->fp);\n    }\n\n    if (ch->handlers.write->method == PHP_CURL_RETURN) {\n        RETURN_EMPTY_STRING();\n    } else {\n        RETURN_TRUE;\n    }\n}\n/* }}} */\n\n/* {{{ proto mixed curl_getinfo(resource ch [, int option])\n   Get information regarding a specific transfer */\nPHP_FUNCTION(swoole_native_curl_getinfo) {\n    zval *zid;\n    php_curl *ch;\n    zend_long option;\n    bool option_is_null = 1;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG_OR_NULL(option, option_is_null)\n\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid, false)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    if (option_is_null) {\n        char *s_code;\n        /* libcurl expects long datatype. So far no cases are known where\n           it would be an issue. Using zend_long would truncate a 64-bit\n           var on Win64, so the exact long datatype fits everywhere, as\n           long as there's no 32-bit int overflow. */\n        long l_code;\n        double d_code;\n        struct curl_certinfo *ci = NULL;\n        zval listcode;\n#if LIBCURL_VERSION_NUM >= 0x073d00 /* 7.61.0 */\n        curl_off_t co;\n#endif\n\n        array_init(return_value);\n\n        if (curl_easy_getinfo(ch->cp, CURLINFO_EFFECTIVE_URL, &s_code) == CURLE_OK) {\n            CAAS(\"url\", s_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_TYPE, &s_code) == CURLE_OK) {\n            if (s_code != NULL) {\n                CAAS(\"content_type\", s_code);\n            } else {\n                zval retnull;\n                ZVAL_NULL(&retnull);\n                CAAZ(\"content_type\", &retnull);\n            }\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_HTTP_CODE, &l_code) == CURLE_OK) {\n            CAAL(\"http_code\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_HEADER_SIZE, &l_code) == CURLE_OK) {\n            CAAL(\"header_size\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_REQUEST_SIZE, &l_code) == CURLE_OK) {\n            CAAL(\"request_size\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_FILETIME, &l_code) == CURLE_OK) {\n            CAAL(\"filetime\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_SSL_VERIFYRESULT, &l_code) == CURLE_OK) {\n            CAAL(\"ssl_verify_result\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_COUNT, &l_code) == CURLE_OK) {\n            CAAL(\"redirect_count\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_TOTAL_TIME, &d_code) == CURLE_OK) {\n            CAAD(\"total_time\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_NAMELOOKUP_TIME, &d_code) == CURLE_OK) {\n            CAAD(\"namelookup_time\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_CONNECT_TIME, &d_code) == CURLE_OK) {\n            CAAD(\"connect_time\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_PRETRANSFER_TIME, &d_code) == CURLE_OK) {\n            CAAD(\"pretransfer_time\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_SIZE_UPLOAD, &d_code) == CURLE_OK) {\n            CAAD(\"size_upload\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_SIZE_DOWNLOAD, &d_code) == CURLE_OK) {\n            CAAD(\"size_download\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_SPEED_DOWNLOAD, &d_code) == CURLE_OK) {\n            CAAD(\"speed_download\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_SPEED_UPLOAD, &d_code) == CURLE_OK) {\n            CAAD(\"speed_upload\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d_code) == CURLE_OK) {\n            CAAD(\"download_content_length\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_LENGTH_UPLOAD, &d_code) == CURLE_OK) {\n            CAAD(\"upload_content_length\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_STARTTRANSFER_TIME, &d_code) == CURLE_OK) {\n            CAAD(\"starttransfer_time\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_TIME, &d_code) == CURLE_OK) {\n            CAAD(\"redirect_time\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_URL, &s_code) == CURLE_OK) {\n            CAAS(\"redirect_url\", s_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_PRIMARY_IP, &s_code) == CURLE_OK) {\n            CAAS(\"primary_ip\", s_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_CERTINFO, &ci) == CURLE_OK) {\n            array_init(&listcode);\n            create_certinfo(ci, &listcode);\n            CAAZ(\"certinfo\", &listcode);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_PRIMARY_PORT, &l_code) == CURLE_OK) {\n            CAAL(\"primary_port\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_LOCAL_IP, &s_code) == CURLE_OK) {\n            CAAS(\"local_ip\", s_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_LOCAL_PORT, &l_code) == CURLE_OK) {\n            CAAL(\"local_port\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_HTTP_VERSION, &l_code) == CURLE_OK) {\n            CAAL(\"http_version\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_PROTOCOL, &l_code) == CURLE_OK) {\n            CAAL(\"protocol\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_PROXY_SSL_VERIFYRESULT, &l_code) == CURLE_OK) {\n            CAAL(\"ssl_verifyresult\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_SCHEME, &s_code) == CURLE_OK) {\n            CAAS(\"scheme\", s_code);\n        }\n#if LIBCURL_VERSION_NUM >= 0x073d00 /* Available since 7.61.0 */\n        if (curl_easy_getinfo(ch->cp, CURLINFO_APPCONNECT_TIME_T, &co) == CURLE_OK) {\n            CAAL(\"appconnect_time_us\", co);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_CONNECT_TIME_T, &co) == CURLE_OK) {\n            CAAL(\"connect_time_us\", co);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_NAMELOOKUP_TIME_T, &co) == CURLE_OK) {\n            CAAL(\"namelookup_time_us\", co);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_PRETRANSFER_TIME_T, &co) == CURLE_OK) {\n            CAAL(\"pretransfer_time_us\", co);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_TIME_T, &co) == CURLE_OK) {\n            CAAL(\"redirect_time_us\", co);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_STARTTRANSFER_TIME_T, &co) == CURLE_OK) {\n            CAAL(\"starttransfer_time_us\", co);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_TOTAL_TIME_T, &co) == CURLE_OK) {\n            CAAL(\"total_time_us\", co);\n        }\n#endif\n        if (ch->header.str) {\n            CAASTR(\"request_header\", ch->header.str);\n        }\n#if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */\n        if (curl_easy_getinfo(ch->cp, CURLINFO_EFFECTIVE_METHOD, &s_code) == CURLE_OK) {\n            CAAS(\"effective_method\", s_code);\n        }\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075400 && PHP_VERSION_ID >= 80300\n        if (curl_easy_getinfo(ch->cp, CURLINFO_CAPATH, &s_code) == CURLE_OK) {\n            CAAS(\"capath\", s_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_CAINFO, &s_code) == CURLE_OK) {\n            CAAS(\"cainfo\", s_code);\n        }\n#endif\n    } else {\n        switch (option) {\n        case CURLINFO_HEADER_OUT:\n            if (ch->header.str) {\n                RETURN_STR_COPY(ch->header.str);\n            } else {\n                RETURN_FALSE;\n            }\n        case CURLINFO_CERTINFO: {\n            struct curl_certinfo *ci = NULL;\n\n            array_init(return_value);\n\n            if (curl_easy_getinfo(ch->cp, CURLINFO_CERTINFO, &ci) == CURLE_OK) {\n                create_certinfo(ci, return_value);\n            } else {\n                RETURN_FALSE;\n            }\n            break;\n        }\n        case CURLINFO_PRIVATE: {\n            if (!Z_ISUNDEF(ch->private_data)) {\n                RETURN_COPY(&ch->private_data);\n            } else {\n                RETURN_FALSE;\n            }\n            break;\n        }\n        default: {\n            int type = CURLINFO_TYPEMASK & option;\n            switch (type) {\n            case CURLINFO_STRING: {\n                char *s_code = NULL;\n\n                if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &s_code) == CURLE_OK && s_code) {\n                    RETURN_STRING(s_code);\n                } else {\n                    RETURN_FALSE;\n                }\n                break;\n            }\n            case CURLINFO_LONG: {\n                zend_long code = 0;\n\n                if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &code) == CURLE_OK) {\n                    RETURN_LONG(code);\n                } else {\n                    RETURN_FALSE;\n                }\n                break;\n            }\n            case CURLINFO_DOUBLE: {\n                double code = 0.0;\n\n                if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &code) == CURLE_OK) {\n                    RETURN_DOUBLE(code);\n                } else {\n                    RETURN_FALSE;\n                }\n                break;\n            }\n            case CURLINFO_SLIST: {\n                struct curl_slist *slist;\n                if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &slist) == CURLE_OK) {\n                    struct curl_slist *current = slist;\n                    array_init(return_value);\n                    while (current) {\n                        add_next_index_string(return_value, current->data);\n                        current = current->next;\n                    }\n                    curl_slist_free_all(slist);\n                } else {\n                    RETURN_FALSE;\n                }\n                break;\n            }\n            case CURLINFO_OFF_T: {\n                curl_off_t c_off;\n                if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &c_off) == CURLE_OK) {\n                    RETURN_LONG((long) c_off);\n                } else {\n                    RETURN_FALSE;\n                }\n                break;\n            }\n            default:\n                RETURN_FALSE;\n            }\n        }\n        }\n    }\n}\n/* }}} */\n\n/* {{{ proto string curl_error(resource ch)\n   Return a string contain the last error for the current session */\nPHP_FUNCTION(swoole_native_curl_error) {\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid, false)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    if (ch->err.no) {\n        ch->err.str[CURL_ERROR_SIZE] = 0;\n        if (strlen(ch->err.str) > 0) {\n            RETURN_STRING(ch->err.str);\n        } else {\n            RETURN_STRING(curl_easy_strerror((CURLcode) ch->err.no));\n        }\n    } else {\n        RETURN_EMPTY_STRING();\n    }\n}\n/* }}} */\n\n/* {{{ proto int curl_errno(resource ch)\n   Return an integer containing the last error number */\nPHP_FUNCTION(swoole_native_curl_errno) {\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid, false)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    RETURN_LONG(ch->err.no);\n}\n/* }}} */\n\n/* {{{ proto void curl_close(resource ch)\n   Close a cURL session */\nPHP_FUNCTION(swoole_native_curl_close) {\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        return;\n    }\n\n    if (ch->in_callback) {\n        zend_throw_error(NULL, \"%s(): Attempt to close cURL handle from a callback\", get_active_function_name());\n        RETURN_THROWS();\n    }\n}\n/* }}} */\n\n#if PHP_VERSION_ID >= 80300\nstatic void _php_curl_free_callback(php_curl_callback *callback) {\n    if (callback) {\n        zval_ptr_dtor(&callback->func_name);\n        efree(callback);\n    }\n}\n#endif\n\nstatic void swoole_curl_free_obj(zend_object *object) {\n    php_curl *ch = curl_from_obj(object);\n\n#if PHP_CURL_DEBUG\n    fprintf(stderr, \"DTOR CALLED, ch = %x\\n\", ch);\n#endif\n\n    if (!ch->cp) {\n        /* Can happen if constructor throws. */\n        zend_object_std_dtor(&ch->std);\n        return;\n    }\n\n    swoole_curl_verify_handlers(ch, /* reporterror */ false);\n\n    /*\n     * Libcurl is doing connection caching. When easy handle is cleaned up,\n     * if the handle was previously used by the curl_multi_api, the connection\n     * remains open un the curl multi handle is cleaned up. Some protocols are\n     * sending content like the FTP one, and libcurl try to use the\n     * WRITEFUNCTION or the HEADERFUNCTION. Since structures used in those\n     * callback are freed, we need to use an other callback to which avoid\n     * segfaults.\n     *\n     * Libcurl commit d021f2e8a00 fix this issue and should be part of 7.28.2\n     */\n    curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, fn_write_nothing);\n    curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, fn_write_nothing);\n\n    swoole::curl::Handle *handle = swoole::curl::get_handle(ch->cp);\n    if (handle && handle->multi) {\n        handle->multi->remove_handle(handle);\n    }\n\n    /* cURL destructors should be invoked only by last curl handle */\n    if (--(*ch->clone) == 0) {\n        zend_llist_clean(&ch->to_free->post);\n        zend_llist_clean(&ch->to_free->stream);\n        zend_hash_destroy(ch->to_free->slist);\n        efree(ch->to_free->slist);\n        efree(ch->to_free);\n        efree(ch->clone);\n\n        swoole::curl::destroy_handle(ch->cp);\n    }\n\n    if (ch->cp) {\n        curl_easy_cleanup(ch->cp);\n    }\n\n    smart_str_free(&ch->handlers.write->buf);\n    zval_ptr_dtor(&ch->handlers.write->func_name);\n    zval_ptr_dtor(&ch->handlers.read->func_name);\n    zval_ptr_dtor(&ch->handlers.write_header->func_name);\n    zval_ptr_dtor(&ch->handlers.std_err);\n    if (ch->header.str) {\n        zend_string_release_ex(ch->header.str, 0);\n    }\n\n    zval_ptr_dtor(&ch->handlers.write_header->stream);\n    zval_ptr_dtor(&ch->handlers.write->stream);\n    zval_ptr_dtor(&ch->handlers.read->stream);\n\n    efree(ch->handlers.write);\n    efree(ch->handlers.write_header);\n    efree(ch->handlers.read);\n\n#if PHP_VERSION_ID >= 80300\n    _php_curl_free_callback(ch->handlers.progress);\n    _php_curl_free_callback(ch->handlers.xferinfo);\n    _php_curl_free_callback(ch->handlers.fnmatch);\n#if LIBCURL_VERSION_NUM >= 0x075400\n    _php_curl_free_callback(ch->handlers.sshhostkey);\n#endif\n#else\n    if (ch->handlers.progress) {\n        zval_ptr_dtor(&ch->handlers.progress->func_name);\n        efree(ch->handlers.progress);\n    }\n\n#if PHP_VERSION_ID >= 80200\n    if (ch->handlers.xferinfo) {\n        zval_ptr_dtor(&ch->handlers.xferinfo->func_name);\n        efree(ch->handlers.xferinfo);\n    }\n#endif\n\n    if (ch->handlers.fnmatch) {\n        zval_ptr_dtor(&ch->handlers.fnmatch->func_name);\n        efree(ch->handlers.fnmatch);\n    }\n\n#if LIBCURL_VERSION_NUM >= 0x075400 && php_version_id >= 80300\n    if (ch->handlers.sshhostkey) {\n        zval_ptr_dtor(&ch->handlers.sshhostkey->func_name);\n        efree(ch->handlers.sshhostkey);\n    }\n#endif\n#endif\n\n    zval_ptr_dtor(&ch->postfields);\n    zval_ptr_dtor(&ch->private_data);\n\n    if (ch->share) {\n        OBJ_RELEASE(&ch->share->std);\n    }\n\n    zend_object_std_dtor(&ch->std);\n}\n\n/* {{{ _php_curl_reset_handlers()\n   Reset all handlers of a given php_curl */\nstatic void _php_curl_reset_handlers(php_curl *ch) {\n    if (!Z_ISUNDEF(ch->handlers.write->stream)) {\n        zval_ptr_dtor(&ch->handlers.write->stream);\n        ZVAL_UNDEF(&ch->handlers.write->stream);\n    }\n\n    ch->handlers.write->fp = NULL;\n    ch->handlers.write->method = PHP_CURL_STDOUT;\n\n    if (!Z_ISUNDEF(ch->handlers.write_header->stream)) {\n        zval_ptr_dtor(&ch->handlers.write_header->stream);\n        ZVAL_UNDEF(&ch->handlers.write_header->stream);\n    }\n    ch->handlers.write_header->fp = NULL;\n    ch->handlers.write_header->method = PHP_CURL_IGNORE;\n\n    if (!Z_ISUNDEF(ch->handlers.read->stream)) {\n        zval_ptr_dtor(&ch->handlers.read->stream);\n        ZVAL_UNDEF(&ch->handlers.read->stream);\n    }\n    ch->handlers.read->fp = NULL;\n    ch->handlers.read->res = NULL;\n    ch->handlers.read->method = PHP_CURL_DIRECT;\n\n    if (!Z_ISUNDEF(ch->handlers.std_err)) {\n        zval_ptr_dtor(&ch->handlers.std_err);\n        ZVAL_UNDEF(&ch->handlers.std_err);\n    }\n\n    if (ch->handlers.progress) {\n        zval_ptr_dtor(&ch->handlers.progress->func_name);\n        efree(ch->handlers.progress);\n        ch->handlers.progress = NULL;\n    }\n\n#if LIBCURL_VERSION_NUM >= 0x072000 && PHP_VERSION_ID >= 80200\n    if (ch->handlers.xferinfo) {\n        zval_ptr_dtor(&ch->handlers.xferinfo->func_name);\n        efree(ch->handlers.xferinfo);\n        ch->handlers.xferinfo = NULL;\n    }\n#endif\n\n    if (ch->handlers.fnmatch) {\n        zval_ptr_dtor(&ch->handlers.fnmatch->func_name);\n        efree(ch->handlers.fnmatch);\n        ch->handlers.fnmatch = NULL;\n    }\n\n#if LIBCURL_VERSION_NUM >= 0x075400 && PHP_VERSION_ID >= 80300\n    if (ch->handlers.sshhostkey) {\n        zval_ptr_dtor(&ch->handlers.sshhostkey->func_name);\n        efree(ch->handlers.sshhostkey);\n        ch->handlers.sshhostkey = NULL;\n    }\n#endif\n}\n/* }}} */\n\n/* {{{ proto void curl_reset(resource ch)\n   Reset all options of a libcurl session handle */\nPHP_FUNCTION(swoole_native_curl_reset) {\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    if (ch->in_callback) {\n        zend_throw_error(NULL, \"%s(): Attempt to reset cURL handle from a callback\", get_active_function_name());\n        RETURN_THROWS();\n    }\n\n    curl_easy_reset(ch->cp);\n    _php_curl_reset_handlers(ch);\n    _php_curl_set_default_options(ch);\n}\n/* }}} */\n\n/* {{{ proto void curl_escape(resource ch, string str)\n   URL encodes the given string */\nPHP_FUNCTION(swoole_native_curl_escape) {\n    zend_string *str;\n    char *res;\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    Z_PARAM_STR(str)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(str))) {\n        RETURN_FALSE;\n    }\n\n    if ((res = curl_easy_escape(ch->cp, ZSTR_VAL(str), ZSTR_LEN(str)))) {\n        RETVAL_STRING(res);\n        curl_free(res);\n    } else {\n        RETURN_FALSE;\n    }\n}\n/* }}} */\n\n/* {{{ proto void curl_unescape(resource ch, string str)\n   URL decodes the given string */\nPHP_FUNCTION(swoole_native_curl_unescape) {\n    char *out = NULL;\n    int out_len;\n    zval *zid;\n    zend_string *str;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    Z_PARAM_STR(str)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(str))) {\n        RETURN_FALSE;\n    }\n\n    if ((out = curl_easy_unescape(ch->cp, ZSTR_VAL(str), ZSTR_LEN(str), &out_len))) {\n        RETVAL_STRINGL(out, out_len);\n        curl_free(out);\n    } else {\n        RETURN_FALSE;\n    }\n}\n/* }}} */\n\n/* {{{ proto void curl_pause(resource ch, int bitmask)\n       pause and unpause a connection */\nPHP_FUNCTION(swoole_native_curl_pause) {\n    zend_long bitmask;\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    Z_PARAM_LONG(bitmask)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    RETURN_LONG(curl_easy_pause(ch->cp, bitmask));\n}\n/* }}} */\n\n#if LIBCURL_VERSION_NUM >= 0x073E00 && PHP_VERSION_ID >= 80200 /* Available since 7.62.0 */\n/* {{{ perform connection upkeep checks */\nPHP_FUNCTION(swoole_native_curl_upkeep) {\n    CURLcode error;\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    error = curl_easy_upkeep(ch->cp);\n    SAVE_CURL_ERROR(ch, error);\n\n    RETURN_BOOL(error == CURLE_OK);\n}\n/*}}} */\n#endif\n#endif\n"
  },
  {
    "path": "thirdparty/php/curl/multi.cc",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | Copyright (c) The PHP Group                                          |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | https://www.php.net/license/3_01.txt                                 |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Author: Sterling Hughes <sterling@php.net>                           |\n   +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_cxx.h\"\n#include \"zend_object_handlers.h\"\n\n#if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400\n#include \"php_swoole_curl.h\"\n\nusing swoole::curl::Multi;\nusing swoole::curl::Selector;\n\nSW_EXTERN_C_BEGIN\n#include \"swoole_curl_interface.h\"\n#include \"curl_arginfo.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include <curl/curl.h>\n#include <curl/easy.h>\n\n#define SAVE_CURLM_ERROR(__handle, __err) (__handle)->err.no = (int) __err;\n\nvoid swoole_curl_multi_set_in_coroutine(php_curlm *mh, bool value) {\n    zend_update_property_bool(nullptr, &mh->std, ZEND_STRL(\"in_coroutine\"), value);\n}\n\nbool swoole_curl_multi_is_in_coroutine(php_curlm *mh) {\n    zval rv;\n    zval *zv = zend_read_property_ex(nullptr, &mh->std, SW_ZSTR_KNOWN(SW_ZEND_STR_IN_COROUTINE), 1, &rv);\n    return zval_is_true(zv);\n}\n\n/* CurlMultiHandle class */\n\nzend_class_entry *swoole_coroutine_curl_multi_handle_ce;\n\nstatic inline php_curlm *curl_multi_from_obj(zend_object *obj) {\n    return (php_curlm *) ((char *) (obj) -XtOffsetOf(php_curlm, std));\n}\n#define Z_CURL_MULTI_P(zv) curl_multi_from_obj(Z_OBJ_P(zv))\n\nstatic void _php_curl_multi_free(php_curlm *mh);\n\nSW_EXTERN_C_END\n\n/* {{{ Returns a new cURL multi handle */\nPHP_FUNCTION(swoole_native_curl_multi_init) {\n    php_curlm *mh;\n\n    ZEND_PARSE_PARAMETERS_NONE();\n\n    object_init_ex(return_value, swoole_coroutine_curl_multi_handle_ce);\n    mh = Z_CURL_MULTI_P(return_value);\n    mh->multi = new Multi();\n\n    swoole_curl_multi_set_in_coroutine(mh, true);\n    zend_llist_init(&mh->easyh, sizeof(zval), swoole_curl_multi_cleanup_list, 0);\n}\n/* }}} */\n\n/* {{{ Add a normal cURL handle to a cURL multi handle */\nPHP_FUNCTION(swoole_native_curl_multi_add_handle) {\n    zval *z_mh;\n    zval *z_ch;\n    php_curlm *mh;\n    php_curl *ch;\n    CURLMcode error = CURLM_OK;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce)\n    Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    mh = Z_CURL_MULTI_P(z_mh);\n    ch = swoole_curl_get_handle(z_ch);\n\n    if (!(swoole_curl_multi_is_in_coroutine(mh))) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION,\n                           \"The given object is not a valid coroutine CurlMultiHandle object\");\n        RETURN_FALSE;\n    }\n\n    swoole_curl_verify_handlers(ch, /* reporterror */ true);\n    swoole_curl_cleanup_handle(ch);\n\n    auto handle = swoole::curl::get_handle(ch->cp);\n    error = mh->multi->add_handle(handle);\n    SAVE_CURLM_ERROR(mh, error);\n\n#if PHP_VERSION_ID >= 80200\n    if (error == CURLM_OK) {\n        Z_ADDREF_P(z_ch);\n        zend_llist_add_element(&mh->easyh, z_ch);\n    }\n#else\n    Z_ADDREF_P(z_ch);\n    zend_llist_add_element(&mh->easyh, z_ch);\n#endif\n\n    swoole_trace_log(SW_TRACE_CO_CURL, \"multi=%p, cp=%p, handle=%p, error=%d\", mh->multi, ch->cp, handle, error);\n    RETURN_LONG((zend_long) error);\n}\n/* }}} */\n\nvoid swoole_curl_multi_cleanup_list(void *data) /* {{{ */\n{\n    zval *z_ch = (zval *) data;\n\n    zval_ptr_dtor(z_ch);\n}\n/* }}} */\n\n/* Used internally as comparison routine passed to zend_list_del_element */\nstatic int curl_compare_objects(zval *z1, zval *z2) /* {{{ */\n{\n    return (Z_TYPE_P(z1) == Z_TYPE_P(z2) && Z_TYPE_P(z1) == IS_OBJECT && Z_OBJ_P(z1) == Z_OBJ_P(z2));\n}\n/* }}} */\n\n/* Used to find the php_curl resource for a given curl easy handle */\nstatic zval *_php_curl_multi_find_easy_handle(php_curlm *mh, CURL *easy) /* {{{ */\n{\n    php_curl *tmp_ch;\n    zend_llist_position pos;\n    zval *pz_ch_temp;\n\n    for (pz_ch_temp = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch_temp;\n         pz_ch_temp = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) {\n        tmp_ch = swoole_curl_get_handle(pz_ch_temp, false, false);\n        if (tmp_ch && tmp_ch->cp == easy) {\n            return pz_ch_temp;\n        }\n    }\n\n    return NULL;\n}\n/* }}} */\n\n/* {{{ Remove a multi handle from a set of cURL handles */\nPHP_FUNCTION(swoole_native_curl_multi_remove_handle) {\n    zval *z_mh;\n    zval *z_ch;\n    php_curlm *mh;\n    php_curl *ch;\n    CURLMcode error = CURLM_OK;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce)\n    Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    mh = Z_CURL_MULTI_P(z_mh);\n    if (!(swoole_curl_multi_is_in_coroutine(mh))) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION,\n                           \"The given object is not a valid coroutine CurlMultiHandle object\");\n        RETURN_FALSE;\n    }\n    ch = Z_CURL_P(z_ch);\n    auto handle = swoole::curl::get_handle(ch->cp);\n    if (handle && handle->multi) {\n        error = mh->multi->remove_handle(handle);\n    } else {\n        error = curl_multi_remove_handle(mh->multi, ch->cp);\n    }\n\n    swoole_trace_log(SW_TRACE_CO_CURL, \"multi=%p, cp=%p, handle=%p, error=%d\", mh->multi, ch->cp, handle, error);\n    SAVE_CURLM_ERROR(mh, error);\n#if PHP_VERSION_ID >= 80200\n    if (error == CURLM_OK) {\n        zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *)) curl_compare_objects);\n    }\n#else\n    zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *)) curl_compare_objects);\n#endif\n    RETVAL_LONG((zend_long) error);\n}\n/* }}} */\n\n/* {{{ Get all the sockets associated with the cURL extension, which can then be \"selected\" */\nPHP_FUNCTION(swoole_native_curl_multi_select) {\n    zval *z_mh;\n    php_curlm *mh;\n    double timeout = 1.0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END();\n\n    mh = Z_CURL_MULTI_P(z_mh);\n    if (!(swoole_curl_multi_is_in_coroutine(mh))) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION,\n                           \"The given object is not a valid coroutine CurlMultiHandle object\");\n        RETURN_FALSE;\n    }\n\n#if PHP_VERSION_ID >= 80200\n    if (!(timeout >= 0.0 && timeout <= ((double) INT_MAX / 1000.0))) {\n        swoole_fatal_error(\n            SW_ERROR_WRONG_OPERATION, \"timeout must be between 0 and %d\", (int) ceilf((double) INT_MAX / 1000));\n#ifdef CURLM_BAD_FUNCTION_ARGUMENT\n        SAVE_CURLM_ERROR(mh, CURLM_BAD_FUNCTION_ARGUMENT);\n#endif\n        RETURN_FALSE;\n    }\n#endif\n\n    RETURN_LONG(mh->multi->select(mh, timeout));\n}\n/* }}} */\n\n/* {{{ Run the sub-connections of the current cURL handle */\nPHP_FUNCTION(swoole_native_curl_multi_exec) {\n    zval *z_mh;\n    zval *z_still_running;\n    php_curlm *mh;\n    int still_running;\n    CURLMcode error = CURLM_OK;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce)\n    Z_PARAM_ZVAL(z_still_running)\n    ZEND_PARSE_PARAMETERS_END();\n\n    mh = Z_CURL_MULTI_P(z_mh);\n    if (!(swoole_curl_multi_is_in_coroutine(mh))) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION,\n                           \"The given object is not a valid coroutine CurlMultiHandle object\");\n        RETURN_FALSE;\n    }\n\n    {\n        zend_llist_position pos;\n        php_curl *ch;\n        zval *pz_ch;\n\n        for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;\n             pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) {\n            ch = Z_CURL_P(pz_ch);\n\n            swoole_curl_verify_handlers(ch, /* reporterror */ true);\n        }\n    }\n\n    error = mh->multi->perform();\n    still_running = mh->multi->get_running_handles();\n    ZEND_TRY_ASSIGN_REF_LONG(z_still_running, still_running);\n\n    SAVE_CURLM_ERROR(mh, error);\n    RETURN_LONG((zend_long) error);\n}\n/* }}} */\n\n/* {{{ Return the content of a cURL handle if CURLOPT_RETURNTRANSFER is set */\nPHP_FUNCTION(swoole_native_curl_multi_getcontent) {\n    zval *z_ch;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    ch = Z_CURL_P(z_ch);\n\n    if (ch->handlers.write->method == PHP_CURL_RETURN) {\n        if (!ch->handlers.write->buf.s) {\n            RETURN_EMPTY_STRING();\n        }\n        smart_str_0(&ch->handlers.write->buf);\n        RETURN_STR_COPY(ch->handlers.write->buf.s);\n    }\n\n    RETURN_NULL();\n}\n/* }}} */\n\n/* {{{ Get information about the current transfers */\nPHP_FUNCTION(swoole_native_curl_multi_info_read) {\n    zval *z_mh;\n    php_curlm *mh;\n    CURLMsg *tmp_msg;\n    int queued_msgs;\n    zval *zmsgs_in_queue = NULL;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_ZVAL(zmsgs_in_queue)\n    ZEND_PARSE_PARAMETERS_END();\n\n    mh = Z_CURL_MULTI_P(z_mh);\n    if (!(swoole_curl_multi_is_in_coroutine(mh))) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION,\n                           \"The given object is not a valid coroutine CurlMultiHandle object\");\n        RETURN_FALSE;\n    }\n\n    tmp_msg = curl_multi_info_read(mh->multi->get_multi_handle(), &queued_msgs);\n    if (tmp_msg == NULL) {\n        RETURN_FALSE;\n    }\n\n    if (zmsgs_in_queue) {\n        ZEND_TRY_ASSIGN_REF_LONG(zmsgs_in_queue, queued_msgs);\n    }\n\n    array_init(return_value);\n    add_assoc_long(return_value, \"msg\", tmp_msg->msg);\n    add_assoc_long(return_value, \"result\", tmp_msg->data.result);\n\n    /* find the original easy curl handle */\n    {\n        zval *pz_ch = _php_curl_multi_find_easy_handle(mh, tmp_msg->easy_handle);\n        if (pz_ch != NULL) {\n            /* we must save result to be able to read error message */\n            ch = swoole_curl_get_handle(pz_ch, false, false);\n            if (ch) {\n                SAVE_CURL_ERROR(ch, tmp_msg->data.result);\n            }\n            Z_ADDREF_P(pz_ch);\n            add_assoc_zval(return_value, \"handle\", pz_ch);\n        }\n    }\n}\n/* }}} */\n\n/* {{{ Close a set of cURL handles */\nPHP_FUNCTION(swoole_native_curl_multi_close) {\n    php_curlm *mh;\n    zval *z_mh;\n\n    zend_llist_position pos;\n    zval *pz_ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    mh = Z_CURL_MULTI_P(z_mh);\n\n    for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;\n         pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) {\n        php_curl *ch = Z_CURL_P(pz_ch);\n        if (!ch) {\n            continue;\n        }\n        swoole_curl_verify_handlers(ch, /* reporterror */ false);\n        auto handle = swoole::curl::get_handle(ch->cp);\n        if (handle) {\n            mh->multi->remove_handle(handle);\n        } else {\n            curl_multi_remove_handle(mh->multi, ch->cp);\n        }\n    }\n    zend_llist_clean(&mh->easyh);\n}\n/* }}} */\n\n/* {{{ Return an integer containing the last multi curl error number */\nPHP_FUNCTION(swoole_native_curl_multi_errno) {\n    zval *z_mh;\n    php_curlm *mh;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    mh = Z_CURL_MULTI_P(z_mh);\n\n    RETURN_LONG(mh->err.no);\n}\n/* }}} */\n\n/* {{{ return string describing error code */\nPHP_FUNCTION(swoole_native_curl_multi_strerror) {\n    zend_long code;\n    const char *str;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_LONG(code)\n    ZEND_PARSE_PARAMETERS_END();\n\n    str = curl_multi_strerror((CURLMcode) code);\n    if (str) {\n        RETURN_STRING(str);\n    } else {\n        RETURN_NULL();\n    }\n}\n/* }}} */\n\nstatic int _php_server_push_callback(\n    CURL *parent_ch, CURL *easy, size_t num_headers, struct curl_pushheaders *push_headers, void *userp) /* {{{ */\n{\n    php_curl *ch;\n    php_curl *parent;\n    php_curlm *mh = (php_curlm *) userp;\n    size_t rval = CURL_PUSH_DENY;\n    php_curl_callback *t = mh->handlers.server_push;\n    zval *pz_parent_ch = NULL;\n    zval pz_ch;\n    zval headers;\n    zval retval;\n    char *header;\n    zend_result error;\n\n    zend_fcall_info fci = empty_fcall_info;\n\n    pz_parent_ch = _php_curl_multi_find_easy_handle(mh, parent_ch);\n    if (pz_parent_ch == NULL) {\n        return rval;\n    }\n\n    if (UNEXPECTED(zend_fcall_info_init(&t->func_name, 0, &fci, &t->fci_cache, NULL, NULL) == FAILURE)) {\n        php_error_docref(NULL, E_WARNING, \"Cannot call the CURLMOPT_PUSHFUNCTION\");\n        return rval;\n    }\n\n    parent = Z_CURL_P(pz_parent_ch);\n\n    ch = swoole_curl_init_handle_into_zval(&pz_ch);\n    ch->cp = easy;\n    swoole_setup_easy_copy_handlers(ch, parent);\n\n    auto parent_handle = swoole::curl::get_handle(parent->cp);\n    if (parent_handle) {\n        auto handle = swoole::curl::create_handle(easy);\n        handle->multi = parent_handle->multi;\n    }\n\n    size_t i;\n    array_init(&headers);\n    for (i = 0; i < num_headers; i++) {\n        header = curl_pushheader_bynum(push_headers, i);\n        add_next_index_string(&headers, header);\n    }\n\n#if PHP_VERSION_ID >= 80300\n    ZEND_ASSERT(pz_parent_ch);\n    zval call_args[3] = {*pz_parent_ch, pz_ch, headers};\n\n    fci.param_count = 3;\n    fci.params = call_args;\n    fci.retval = &retval;\n#else\n    zend_fcall_info_argn(&fci, 3, pz_parent_ch, &pz_ch, &headers);\n\n    fci.retval = &retval;\n#endif\n\n    error = zend_call_function(&fci, &t->fci_cache);\n#if PHP_VERSION_ID < 80300\n    zend_fcall_info_args_clear(&fci, 1);\n#endif\n    zval_ptr_dtor_nogc(&headers);\n\n    if (error == FAILURE) {\n        php_error_docref(NULL, E_WARNING, \"Cannot call the CURLMOPT_PUSHFUNCTION\");\n    } else if (!Z_ISUNDEF(retval)) {\n#if PHP_VERSION_ID >= 80300\n        if (CURL_PUSH_DENY != swoole_curl_get_long(&retval)) {\n#else\n        if (CURL_PUSH_DENY != zval_get_long(&retval)) {\n#endif\n            rval = CURL_PUSH_OK;\n            zend_llist_add_element(&mh->easyh, &pz_ch);\n        } else {\n            /* libcurl will free this easy handle, avoid double free */\n            ch->cp = NULL;\n        }\n    }\n\n    return rval;\n}\n/* }}} */\n\nstatic bool _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, zval *return_value) /* {{{ */\n{\n    CURLMcode error = CURLM_OK;\n\n    switch (option) {\n    case CURLMOPT_PIPELINING:\n    case CURLMOPT_MAXCONNECTS:\n    case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE:\n    case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE:\n    case CURLMOPT_MAX_HOST_CONNECTIONS:\n    case CURLMOPT_MAX_PIPELINE_LENGTH:\n    case CURLMOPT_MAX_TOTAL_CONNECTIONS:\n#if LIBCURL_VERSION_NUM >= 0x074300 && PHP_VERSION_ID >= 80200\n    case CURLMOPT_MAX_CONCURRENT_STREAMS:\n#endif\n    {\n        zend_long lval = zval_get_long(zvalue);\n\n        if (option == CURLMOPT_PIPELINING && (lval & 1)) {\n#if LIBCURL_VERSION_NUM >= 0x073e00 /* 7.62.0 */\n            php_error_docref(NULL, E_WARNING, \"CURLPIPE_HTTP1 is no longer supported\");\n#else\n            php_error_docref(NULL, E_DEPRECATED, \"CURLPIPE_HTTP1 is deprecated\");\n#endif\n        }\n        error = curl_multi_setopt(mh->multi->get_multi_handle(), (CURLMoption) option, lval);\n        break;\n    }\n    case CURLMOPT_PUSHFUNCTION: {\n        if (mh->handlers.server_push == NULL) {\n            mh->handlers.server_push = (php_curl_callback *) ecalloc(1, sizeof(php_curl_callback));\n        } else if (!Z_ISUNDEF(mh->handlers.server_push->func_name)) {\n            zval_ptr_dtor(&mh->handlers.server_push->func_name);\n            mh->handlers.server_push->fci_cache = empty_fcall_info_cache;\n        }\n\n        ZVAL_COPY(&mh->handlers.server_push->func_name, zvalue);\n#if PHP_VERSION_ID >= 80200\n        error = curl_multi_setopt(mh->multi->get_multi_handle(), CURLMOPT_PUSHFUNCTION, _php_server_push_callback);\n#else\n        error = curl_multi_setopt(mh->multi->get_multi_handle(), (CURLMoption) option, _php_server_push_callback);\n#endif\n        if (error != CURLM_OK) {\n            return false;\n        }\n        error = curl_multi_setopt(mh->multi->get_multi_handle(), CURLMOPT_PUSHDATA, mh);\n        break;\n    }\n    default:\n        zend_argument_value_error(2, \"is not a valid cURL multi option\");\n        error = CURLM_UNKNOWN_OPTION;\n        break;\n    }\n\n    SAVE_CURLM_ERROR(mh, error);\n\n    return error == CURLM_OK;\n}\n/* }}} */\n\n/* {{{ Set an option for the curl multi handle */\nPHP_FUNCTION(swoole_native_curl_multi_setopt) {\n    zval *z_mh, *zvalue;\n    zend_long options;\n    php_curlm *mh;\n\n    ZEND_PARSE_PARAMETERS_START(3, 3)\n    Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce)\n    Z_PARAM_LONG(options)\n    Z_PARAM_ZVAL(zvalue)\n    ZEND_PARSE_PARAMETERS_END();\n\n    mh = Z_CURL_MULTI_P(z_mh);\n    if (!(swoole_curl_multi_is_in_coroutine(mh))) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION,\n                           \"The given object is not a valid coroutine CurlMultiHandle object\");\n        RETURN_FALSE;\n    }\n    if (_php_curl_multi_setopt(mh, (CURLMoption) options, zvalue, return_value)) {\n        RETURN_TRUE;\n    } else {\n        RETURN_FALSE;\n    }\n}\n/* }}} */\n\n/* CurlMultiHandle class */\n\nstatic zend_object_handlers swoole_coroutine_curl_multi_handle_handlers;\n\nstatic zend_object *swoole_curl_multi_create_object(zend_class_entry *class_type) {\n    php_curlm *intern = (php_curlm *) zend_object_alloc(sizeof(php_curlm), class_type);\n\n    zend_object_std_init(&intern->std, class_type);\n    object_properties_init(&intern->std, class_type);\n#if PHP_VERSION_ID < 80300\n    intern->std.handlers = &swoole_coroutine_curl_multi_handle_handlers;\n#endif\n\n    return &intern->std;\n}\n\nstatic zend_function *swoole_curl_multi_get_constructor(zend_object *object) {\n    zend_throw_error(NULL, \"Cannot directly construct CurlMultiHandle, use curl_multi_init() instead\");\n    return NULL;\n}\n\nstatic void swoole_curl_multi_free_obj(zend_object *object) {\n    php_curlm *mh = (php_curlm *) curl_multi_from_obj(object);\n    if (!mh->multi) {\n        /* Can happen if constructor throws. */\n        zend_object_std_dtor(&mh->std);\n        return;\n    }\n    _php_curl_multi_free(mh);\n    zend_object_std_dtor(&mh->std);\n}\n\nstatic HashTable *swoole_curl_multi_get_gc(zend_object *object, zval **table, int *n) {\n    php_curlm *curl_multi = curl_multi_from_obj(object);\n\n    zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();\n\n    if (curl_multi->handlers.server_push) {\n        zend_get_gc_buffer_add_zval(gc_buffer, &curl_multi->handlers.server_push->func_name);\n    }\n\n    zend_llist_position pos;\n    for (zval *pz_ch = (zval *) zend_llist_get_first_ex(&curl_multi->easyh, &pos); pz_ch;\n         pz_ch = (zval *) zend_llist_get_next_ex(&curl_multi->easyh, &pos)) {\n        zend_get_gc_buffer_add_zval(gc_buffer, pz_ch);\n    }\n\n    zend_get_gc_buffer_use(gc_buffer, table, n);\n\n    return zend_std_get_properties(object);\n}\n\nvoid curl_multi_register_class(const zend_function_entry *method_entries) {\n    swoole_coroutine_curl_multi_handle_ce = curl_multi_ce;\n    swoole_coroutine_curl_multi_handle_ce->create_object = swoole_curl_multi_create_object;\n\n    memcpy(&swoole_coroutine_curl_multi_handle_handlers, &std_object_handlers, sizeof(zend_object_handlers));\n    swoole_coroutine_curl_multi_handle_handlers.offset = XtOffsetOf(php_curlm, std);\n    swoole_coroutine_curl_multi_handle_handlers.free_obj = swoole_curl_multi_free_obj;\n    swoole_coroutine_curl_multi_handle_handlers.get_gc = swoole_curl_multi_get_gc;\n    swoole_coroutine_curl_multi_handle_handlers.get_constructor = swoole_curl_multi_get_constructor;\n    swoole_coroutine_curl_multi_handle_handlers.clone_obj = NULL;\n    swoole_coroutine_curl_multi_handle_handlers.cast_object = swoole_curl_cast_object;\n    swoole_coroutine_curl_multi_handle_handlers.compare = [](zval *o1, zval *o2) { return ZEND_UNCOMPARABLE; };\n\n    zend_declare_property_bool(swoole_coroutine_curl_multi_handle_ce, ZEND_STRL(\"in_coroutine\"), 0, ZEND_ACC_PUBLIC);\n}\n\nstatic void _php_curl_multi_free(php_curlm *mh) {\n    bool is_in_coroutine = swoole_curl_multi_is_in_coroutine(mh);\n    for (zend_llist_element *element = mh->easyh.head; element; element = element->next) {\n        zval *z_ch = (zval *) element->data;\n        php_curl *ch;\n        if (OBJ_FLAGS(Z_OBJ_P(z_ch)) & IS_OBJ_FREE_CALLED) {\n            continue;\n        }\n        if ((ch = swoole_curl_get_handle(z_ch, true, false))) {\n            swoole_curl_verify_handlers(ch, /* reporterror */ false);\n            auto handle = swoole::curl::get_handle(ch->cp);\n            if (is_in_coroutine && handle) {\n                mh->multi->remove_handle(handle);\n            } else {\n                curl_multi_remove_handle(mh->multi, ch->cp);\n            }\n        }\n    }\n    if (mh->multi) {\n        if (is_in_coroutine) {\n            delete mh->multi;\n        } else {\n            curl_multi_cleanup(mh->multi);\n        }\n        mh->multi = nullptr;\n    }\n    zend_llist_clean(&mh->easyh);\n    if (mh->handlers.server_push) {\n        zval_ptr_dtor(&mh->handlers.server_push->func_name);\n        efree(mh->handlers.server_push);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "thirdparty/php/curl/php_curl.h",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | Copyright (c) The PHP Group                                          |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | https://www.php.net/license/3_01.txt                                 |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Author: Sterling Hughes <sterling@php.net>                           |\n   |         Wez Furlong <wez@thebrainroom.com>                           |\n   +----------------------------------------------------------------------+\n*/\n#include \"php_swoole_cxx.h\"\n#if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400\n\n#ifndef _PHP_CURL_H\n#define _PHP_CURL_H\n\n#include \"php.h\"\n\n#ifdef PHP_WIN32\n# ifdef PHP_CURL_EXPORTS\n# define PHP_CURL_API __declspec(dllexport)\n# else\n#  define PHP_CURL_API __declspec(dllimport)\n# endif\n#elif defined(__GNUC__) && __GNUC__ >= 4\n# define PHP_CURL_API __attribute__ ((visibility(\"default\")))\n#else\n# define PHP_CURL_API\n#endif\n\nPHP_CURL_API extern zend_class_entry *curl_ce;\nPHP_CURL_API extern zend_class_entry *curl_share_ce;\nPHP_CURL_API extern zend_class_entry *curl_multi_ce;\nPHP_CURL_API extern zend_class_entry *swoole_coroutine_curl_handle_ce;\nPHP_CURL_API extern zend_class_entry *swoole_coroutine_curl_multi_handle_ce;\nPHP_CURL_API extern zend_class_entry *curl_CURLFile_class;\nPHP_CURL_API extern zend_class_entry *curl_CURLStringFile_class;\n\n#endif  /* _PHP_CURL_H */\n#endif\n"
  },
  {
    "path": "thirdparty/php/main/SAPI.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author:  Zeev Suraski <zeev@php.net>                                 |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"main/php_variables.h\"\n\n/**\n * This only handles the cases of PARSE_STRING and PARSE_COOKIE\n */\nstatic void swoole_php_treat_data(int arg, char *str, zval *destArray) {\n    char *res = NULL, *var, *val, *separator = NULL;\n    zval array;\n    int free_buffer = 0;\n    char *strtok_buf = NULL;\n    zend_long count = 0;\n\n    ZVAL_UNDEF(&array);\n    ZVAL_COPY_VALUE(&array, destArray);\n\n    res = str;\n    free_buffer = 1;\n\n    if (!res) {\n        return;\n    }\n\n    switch (arg) {\n    case PARSE_STRING:\n#if PHP_VERSION_ID >= 80500\n        separator = ZSTR_VAL(PG(arg_separator).input);\n#else\n        separator = PG(arg_separator).input;\n#endif\n        break;\n    case PARSE_COOKIE:\n        separator = (char *) \";\\0\";\n        break;\n    }\n\n    var = php_strtok_r(res, separator, &strtok_buf);\n\n    while (var) {\n        size_t val_len;\n        size_t new_val_len;\n\n        val = strchr(var, '=');\n\n        if (arg == PARSE_COOKIE) {\n            /* Remove leading spaces from cookie names, needed for multi-cookie header where ; can be followed by a\n             * space */\n            while (isspace(*var)) {\n                var++;\n            }\n            if (var == val || *var == '\\0') {\n                goto next_cookie;\n            }\n        }\n\n        if (++count > PG(max_input_vars)) {\n            swoole_warning(\"Input variables exceeded \" ZEND_LONG_FMT\n                           \". To increase the limit change max_input_vars in php.ini.\",\n                           PG(max_input_vars));\n            break;\n        }\n\n        if (val) { /* have a value */\n            *val++ = '\\0';\n            if (arg == PARSE_COOKIE) {\n                val_len = php_raw_url_decode(val, strlen(val));\n            } else {\n                val_len = php_url_decode(val, strlen(val));\n            }\n        } else {\n            val = (char *) \"\";\n            val_len = 0;\n        }\n\n        val = estrndup(val, val_len);\n        if (arg != PARSE_COOKIE) {\n            php_url_decode(var, strlen(var));\n        }\n\n        if (sapi_module.input_filter(PARSE_STRING, var, &val, val_len, &new_val_len)) {\n            if (arg == PARSE_STRING ||\n                (arg == PARSE_COOKIE && !zend_symtable_str_exists(Z_ARRVAL_P(&array), var, strlen(var)))) {\n                php_register_variable_safe(var, val, new_val_len, &array);\n            }\n        }\n        efree(val);\n    next_cookie:\n        var = php_strtok_r(NULL, separator, &strtok_buf);\n    }\n\n    if (free_buffer) {\n        efree(res);\n    }\n}\n"
  },
  {
    "path": "thirdparty/php/sockets/conversions.cc",
    "content": "#include \"php_sockets_cxx.h\"\n\n#include <Zend/zend_llist.h>\n#include <zend_smart_str.h>\n\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <arpa/inet.h>\n#include <netinet/in.h>\n#include <sys/un.h>\n#include <sys/ioctl.h>\n#include <net/if.h>\n\n#include <limits.h>\n#include <stdarg.h>\n#include <stddef.h>\n\n#define MAX_USER_BUFF_SIZE ((size_t) (100 * 1024 * 1024))\n#define DEFAULT_BUFF_SIZE 8192\n\nstruct _ser_context {\n    HashTable params; /* stores pointers; has to be first */\n    struct err_s err;\n    zend_llist keys,\n        /* common part to res_context ends here */\n        allocations;\n    SocketImpl *sock;\n};\n\nstruct _res_context {\n    HashTable params; /* stores pointers; has to be first */\n    struct err_s err;\n    zend_llist keys;\n};\n\ntypedef struct {\n    /* zval info */\n    const char *name;\n    unsigned name_size;\n    int required;\n\n    /* structure info */\n    size_t field_offset; /* 0 to pass full structure, e.g. when more than\n                                                    one field is to be changed; in that case the\n                                                    callbacks need to know the name of the fields */\n\n    /* callbacks */\n    from_zval_write_field *from_zval;\n    to_zval_read_field *to_zval;\n} field_descriptor;\n\nconst struct key_value sw_empty_key_value_list[] = {{0}};\n\n/* ERRORS */\nstatic void do_from_to_zval_err(\n    struct err_s *err, zend_llist *keys, const char *what_conv, const char *fmt, va_list ap) {\n    smart_str path = {0};\n    const char **node;\n    char *user_msg;\n    int user_msg_size;\n    zend_llist_position pos;\n\n    if (err->has_error) {\n        return;\n    }\n\n    for (node = (const char **) zend_llist_get_first_ex(keys, &pos); node != NULL;\n         node = (const char **) zend_llist_get_next_ex(keys, &pos)) {\n        smart_str_appends(&path, *node);\n        smart_str_appends(&path, \" > \");\n    }\n\n    if (path.s && ZSTR_LEN(path.s) > 3) {\n        ZSTR_LEN(path.s) -= 3;\n    }\n    smart_str_0(&path);\n\n    user_msg_size = vspprintf(&user_msg, 0, fmt, ap);\n\n    err->has_error = 1;\n    err->level = E_WARNING;\n    spprintf(&err->msg,\n             0,\n             \"error converting %s data (path: %s): %.*s\",\n             what_conv,\n             path.s && *ZSTR_VAL(path.s) != '\\0' ? ZSTR_VAL(path.s) : \"unavailable\",\n             user_msg_size,\n             user_msg);\n    err->should_free = 1;\n\n    efree(user_msg);\n    smart_str_free(&path);\n}\n\nZEND_ATTRIBUTE_FORMAT(printf, 2, 3)\n\nstatic void do_from_zval_err(ser_context *ctx, const char *fmt, ...) {\n    va_list ap;\n\n    va_start(ap, fmt);\n    do_from_to_zval_err(&ctx->err, &ctx->keys, \"user\", fmt, ap);\n    va_end(ap);\n}\nZEND_ATTRIBUTE_FORMAT(printf, 2, 3)\n\nstatic void do_to_zval_err(res_context *ctx, const char *fmt, ...) {\n    va_list ap;\n\n    va_start(ap, fmt);\n    do_from_to_zval_err(&ctx->err, &ctx->keys, \"native\", fmt, ap);\n    va_end(ap);\n}\n\nvoid err_msg_dispose(struct err_s *err) {\n    if (err->msg != NULL) {\n        php_error_docref(NULL, err->level, \"%s\", err->msg);\n        if (err->should_free) {\n            efree(err->msg);\n        }\n    }\n}\n\nvoid allocations_dispose(zend_llist **allocations) {\n    zend_llist_destroy(*allocations);\n    efree(*allocations);\n    *allocations = NULL;\n}\n\n/* Generic Aggregated conversions */\nstatic void from_zval_write_aggregation(const zval *container,\n                                        char *structure,\n                                        const field_descriptor *descriptors,\n                                        ser_context *ctx) {\n    const field_descriptor *descr;\n    zval *elem;\n\n    if (Z_TYPE_P(container) != IS_ARRAY) {\n        do_from_zval_err(ctx, \"%s\", \"expected an array here\");\n    }\n\n    for (descr = descriptors; descr->name != NULL && !ctx->err.has_error; descr++) {\n        if ((elem = zend_hash_str_find(Z_ARRVAL_P(container), descr->name, descr->name_size - 1)) != NULL) {\n            if (descr->from_zval == NULL) {\n                do_from_zval_err(ctx,\n                                 \"No information on how to convert value \"\n                                 \"of key '%s'\",\n                                 descr->name);\n                break;\n            }\n\n            zend_llist_add_element(&ctx->keys, (void *) &descr->name);\n            descr->from_zval(elem, ((char *) structure) + descr->field_offset, ctx);\n            zend_llist_remove_tail(&ctx->keys);\n\n        } else if (descr->required) {\n            do_from_zval_err(ctx, \"The key '%s' is required\", descr->name);\n            break;\n        }\n    }\n}\nstatic void to_zval_read_aggregation(const char *structure,\n                                     zval *zarr, /* initialized array */\n                                     const field_descriptor *descriptors,\n                                     res_context *ctx) {\n    const field_descriptor *descr;\n\n    assert(Z_TYPE_P(zarr) == IS_ARRAY);\n    assert(Z_ARRVAL_P(zarr) != NULL);\n\n    for (descr = descriptors; descr->name != NULL && !ctx->err.has_error; descr++) {\n        zval *new_zv, tmp;\n\n        if (descr->to_zval == NULL) {\n            do_to_zval_err(ctx,\n                           \"No information on how to convert native \"\n                           \"field into value for key '%s'\",\n                           descr->name);\n            break;\n        }\n\n        ZVAL_NULL(&tmp);\n        new_zv = zend_symtable_str_update(Z_ARRVAL_P(zarr), descr->name, descr->name_size - 1, &tmp);\n\n        zend_llist_add_element(&ctx->keys, (void *) &descr->name);\n        descr->to_zval(structure + descr->field_offset, new_zv, ctx);\n        zend_llist_remove_tail(&ctx->keys);\n    }\n}\n\nstatic void to_zval_read_unsigned(const char *data, zval *zv, res_context *ctx) {\n    unsigned ival;\n    memcpy(&ival, data, sizeof(ival));\n\n    ZVAL_LONG(zv, (zend_long) ival);\n}\n\nstatic void from_zval_write_sin6_addr(const zval *zaddr_str, char *addr6, ser_context *ctx) {\n    int res;\n    struct sockaddr_in6 saddr6 = {0};\n    zend_string *addr_str;\n\n    addr_str = zval_get_string((zval *) zaddr_str);\n    res = php_set_inet6_addr(&saddr6, ZSTR_VAL(addr_str), ctx->sock);\n    if (res) {\n        memcpy(addr6, &saddr6.sin6_addr, sizeof saddr6.sin6_addr);\n    } else {\n        /* error already emitted, but let's emit another more relevant */\n        do_from_zval_err(ctx,\n                         \"could not resolve address '%s' to get an AF_INET6 \"\n                         \"address\",\n                         Z_STRVAL_P(zaddr_str));\n    }\n\n    zend_string_release(addr_str);\n}\n\nstatic void to_zval_read_sin6_addr(const char *data, zval *zv, res_context *ctx) {\n    const struct in6_addr *addr = (const struct in6_addr *) data;\n    socklen_t size = INET6_ADDRSTRLEN;\n    zend_string *str = zend_string_alloc(size - 1, 0);\n\n    memset(ZSTR_VAL(str), '\\0', size);\n\n    ZVAL_NEW_STR(zv, str);\n\n    if (inet_ntop(AF_INET6, addr, Z_STRVAL_P(zv), size) == NULL) {\n        do_to_zval_err(ctx,\n                       \"could not convert IPv6 address to string \"\n                       \"(errno %d)\",\n                       errno);\n        return;\n    }\n\n    Z_STRLEN_P(zv) = strlen(Z_STRVAL_P(zv));\n}\n\n/* CONVERSIONS for if_index */\nstatic void from_zval_write_ifindex(const zval *zv, char *uinteger, ser_context *ctx) {\n    unsigned ret = 0;\n\n    if (Z_TYPE_P(zv) == IS_LONG) {\n        if (Z_LVAL_P(zv) < 0 || (zend_ulong) Z_LVAL_P(zv) > UINT_MAX) { /* allow 0 (unspecified interface) */\n            do_from_zval_err(ctx,\n                             \"the interface index cannot be negative or \"\n                             \"larger than %u; given \" ZEND_LONG_FMT,\n                             UINT_MAX,\n                             Z_LVAL_P(zv));\n        } else {\n            ret = (unsigned) Z_LVAL_P(zv);\n        }\n    } else {\n        zend_string *str;\n\n        str = zval_get_string((zval *) zv);\n\n#if HAVE_IF_NAMETOINDEX\n        ret = if_nametoindex(ZSTR_VAL(str));\n        if (ret == 0) {\n            do_from_zval_err(ctx, \"no interface with name \\\"%s\\\" could be found\", ZSTR_VAL(str));\n        }\n#elif defined(SIOCGIFINDEX)\n        {\n            struct ifreq ifr;\n            if (strlcpy(ifr.ifr_name, ZSTR_VAL(str), sizeof(ifr.ifr_name)) >= sizeof(ifr.ifr_name)) {\n                do_from_zval_err(ctx, \"the interface name \\\"%s\\\" is too large \", ZSTR_VAL(str));\n            } else if (ioctl(ctx->sock->get_fd(), SIOCGIFINDEX, &ifr) < 0) {\n                if (errno == ENODEV) {\n                    do_from_zval_err(ctx,\n                                     \"no interface with name \\\"%s\\\" could be \"\n                                     \"found\",\n                                     ZSTR_VAL(str));\n                } else {\n                    do_from_zval_err(ctx,\n                                     \"error fetching interface index for \"\n                                     \"interface with name \\\"%s\\\" (errno %d)\",\n                                     ZSTR_VAL(str),\n                                     errno);\n                }\n            } else {\n                ret = (unsigned) ifr.ifr_ifindex;\n            }\n        }\n#else\n        do_from_zval_err(ctx,\n                         \"this platform does not support looking up an interface by \"\n                         \"name, an integer interface index must be supplied instead\");\n#endif\n\n        zend_string_release(str);\n    }\n\n    if (!ctx->err.has_error) {\n        memcpy(uinteger, &ret, sizeof(ret));\n    }\n}\n\n/* CONVERSIONS for struct in6_pktinfo */\n#if defined(IPV6_PKTINFO)\nstatic const field_descriptor descriptors_in6_pktinfo[] = {{\"addr\",\n                                                            sizeof(\"addr\"),\n                                                            1,\n                                                            offsetof(struct in6_pktinfo, ipi6_addr),\n                                                            from_zval_write_sin6_addr,\n                                                            to_zval_read_sin6_addr},\n                                                           {\"ifindex\",\n                                                            sizeof(\"ifindex\"),\n                                                            1,\n                                                            offsetof(struct in6_pktinfo, ipi6_ifindex),\n                                                            from_zval_write_ifindex,\n                                                            to_zval_read_unsigned},\n                                                           {0}};\n\nvoid from_zval_write_in6_pktinfo(const zval *container, char *in6_pktinfo_c, ser_context *ctx) {\n    from_zval_write_aggregation(container, in6_pktinfo_c, descriptors_in6_pktinfo, ctx);\n}\n\nvoid to_zval_read_in6_pktinfo(const char *data, zval *zv, res_context *ctx) {\n    array_init_size(zv, 2);\n\n    to_zval_read_aggregation(data, zv, descriptors_in6_pktinfo, ctx);\n}\n\n#endif\n\n/* ENTRY POINT for conversions */\nstatic void free_from_zval_allocation(void *alloc_ptr_ptr) {\n    efree(*(void **) alloc_ptr_ptr);\n}\n\nvoid *from_zval_run_conversions(const zval *container,\n                                SocketImpl *sock,\n                                from_zval_write_field *writer,\n                                size_t struct_size,\n                                const char *top_name,\n                                zend_llist **allocations /* out */,\n                                struct err_s *err /* in/out */) {\n    ser_context ctx;\n    char *structure;\n\n    *allocations = NULL;\n\n    if (err->has_error) {\n        return NULL;\n    }\n\n    memset(&ctx, 0, sizeof(ctx));\n    zend_hash_init(&ctx.params, 8, NULL, NULL, 0);\n    zend_llist_init(&ctx.keys, sizeof(const char *), NULL, 0);\n    zend_llist_init(&ctx.allocations, sizeof(void *), &free_from_zval_allocation, 0);\n    ctx.sock = sock;\n\n    structure = (char *) ecalloc(1, struct_size);\n\n    zend_llist_add_element(&ctx.keys, &top_name);\n    zend_llist_add_element(&ctx.allocations, &structure);\n\n    /* main call */\n    writer(container, structure, &ctx);\n\n    if (ctx.err.has_error) {\n        zend_llist_destroy(&ctx.allocations); /* deallocates structure as well */\n        structure = NULL;\n        *err = ctx.err;\n    } else {\n        *allocations = (zend_llist *) emalloc(sizeof **allocations);\n        **allocations = ctx.allocations;\n    }\n\n    zend_llist_destroy(&ctx.keys);\n    zend_hash_destroy(&ctx.params);\n\n    return structure;\n}\n\nzval *to_zval_run_conversions(const char *structure,\n                              to_zval_read_field *reader,\n                              const char *top_name,\n                              const struct key_value *key_value_pairs,\n                              struct err_s *err,\n                              zval *zv) {\n    res_context ctx;\n    const struct key_value *kv;\n\n    if (err->has_error) {\n        return NULL;\n    }\n\n    memset(&ctx, 0, sizeof(ctx));\n    zend_llist_init(&ctx.keys, sizeof(const char *), NULL, 0);\n    zend_llist_add_element(&ctx.keys, &top_name);\n\n    zend_hash_init(&ctx.params, 8, NULL, NULL, 0);\n    for (kv = key_value_pairs; kv->key != NULL; kv++) {\n        zend_hash_str_update_ptr(&ctx.params, kv->key, kv->key_size - 1, kv->value);\n    }\n\n    ZVAL_NULL(zv);\n    /* main call */\n    reader(structure, zv, &ctx);\n\n    if (ctx.err.has_error) {\n        zval_ptr_dtor(zv);\n        ZVAL_UNDEF(zv);\n        *err = ctx.err;\n    }\n\n    zend_llist_destroy(&ctx.keys);\n    zend_hash_destroy(&ctx.params);\n\n    return Z_ISUNDEF_P(zv) ? NULL : zv;\n}\n"
  },
  {
    "path": "thirdparty/php/sockets/conversions.h",
    "content": "#pragma once\n\n#include \"php_sockets_cxx.h\"\n\n#include <netinet/in.h>\n#include <sys/socket.h>\n\n/* TYPE DEFINITIONS */\nstruct err_s {\n    int has_error;\n    char *msg;\n    int level;\n    int should_free;\n};\n\nstruct key_value {\n    const char *key;\n    unsigned key_size;\n    void *value;\n};\n\n/* the complete types of these two are not relevant to the outside */\ntypedef struct _ser_context ser_context;\ntypedef struct _res_context res_context;\n\n#define KEY_RECVMSG_RET \"recvmsg_ret\"\n\ntypedef void(from_zval_write_field)(const zval *arr_value, char *field, ser_context *ctx);\ntypedef void(to_zval_read_field)(const char *data, zval *zv, res_context *ctx);\n\n/* VARIABLE DECLARATIONS */\nextern const struct key_value sw_empty_key_value_list[];\n\n/* AUX FUNCTIONS */\nvoid err_msg_dispose(struct err_s *err);\nvoid allocations_dispose(zend_llist **allocations);\n\n/* CONVERSION FUNCTIONS */\nvoid from_zval_write_int(const zval *arr_value, char *field, ser_context *ctx);\nvoid to_zval_read_int(const char *data, zval *zv, res_context *ctx);\n\n#ifdef IPV6_PKTINFO\nvoid from_zval_write_in6_pktinfo(const zval *container, char *in6_pktinfo_c, ser_context *ctx);\nvoid to_zval_read_in6_pktinfo(const char *data, zval *zv, res_context *ctx);\n#endif\n\n#ifdef SO_PASSCRED\nvoid from_zval_write_ucred(const zval *container, char *ucred_c, ser_context *ctx);\nvoid to_zval_read_ucred(const char *data, zval *zv, res_context *ctx);\n#endif\n\nvoid from_zval_write_msghdr_recv(const zval *container, char *msghdr_c, ser_context *ctx);\n\n/* ENTRY POINTS FOR CONVERSIONS */\nvoid *from_zval_run_conversions(const zval *container,\n                                SocketImpl *sock,\n                                from_zval_write_field *writer,\n                                size_t struct_size,\n                                const char *top_name,\n                                zend_llist **allocations /* out */,\n                                struct err_s *err /* in/out */);\n\nzval *to_zval_run_conversions(const char *structure,\n                              to_zval_read_field *reader,\n                              const char *top_name,\n                              const struct key_value *key_value_pairs,\n                              struct err_s *err,\n                              zval *zv);\n"
  },
  {
    "path": "thirdparty/php/sockets/multicast.cc",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | PHP Version 7                                                        |\n   +----------------------------------------------------------------------+\n   | Copyright (c) 1997-2018 The PHP Group                                |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | http://www.php.net/license/3_01.txt                                  |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Authors: Gustavo Lopes    <cataphract@php.net>                       |\n   +----------------------------------------------------------------------+\n */\n\n#include \"php_sockets_cxx.h\"\n\n#include \"php_network.h\"\n#include <sys/socket.h>\n#include <sys/ioctl.h>\n#include <net/if.h>\n#ifdef HAVE_SYS_SOCKIO_H\n#include <sys/sockio.h>\n#endif\n#include <netinet/in.h>\n#include <arpa/inet.h>\n\n#include \"main/php_network.h\"\n\nenum source_op { JOIN_SOURCE, LEAVE_SOURCE, BLOCK_SOURCE, UNBLOCK_SOURCE };\n\nstatic int _php_mcast_join_leave(\n    SocketImpl *sock, int level, struct sockaddr *group, socklen_t group_len, unsigned int if_index, int join);\n#ifdef HAS_MCAST_EXT\nstatic int _php_mcast_source_op(SocketImpl *sock,\n                                int level,\n                                struct sockaddr *group,\n                                socklen_t group_len,\n                                struct sockaddr *source,\n                                socklen_t source_len,\n                                unsigned int if_index,\n                                enum source_op sop);\n#endif\n\n#ifdef RFC3678_API\nstatic int _php_source_op_to_rfc3678_op(enum source_op sop);\n#elif HAS_MCAST_EXT\nstatic const char *_php_source_op_to_string(enum source_op sop);\nstatic int _php_source_op_to_ipv4_op(enum source_op sop);\n#endif\n\nint php_string_to_if_index(const char *val, unsigned *out) {\n#if HAVE_IF_NAMETOINDEX\n    unsigned int ind;\n\n    ind = if_nametoindex(val);\n    if (ind == 0) {\n        php_error_docref(NULL, E_WARNING, \"no interface with name \\\"%s\\\" could be found\", val);\n        return FAILURE;\n    } else {\n        *out = ind;\n        return SUCCESS;\n    }\n#else\n    php_error_docref(NULL,\n                     E_WARNING,\n                     \"this platform does not support looking up an interface by \"\n                     \"name, an integer interface index must be supplied instead\");\n    return FAILURE;\n#endif\n}\n\nstatic int php_get_if_index_from_zval(zval *val, unsigned *out) {\n    int ret;\n\n    if (Z_TYPE_P(val) == IS_LONG) {\n        if (Z_LVAL_P(val) < 0 || (zend_ulong) Z_LVAL_P(val) > UINT_MAX) {\n            php_error_docref(NULL,\n                             E_WARNING,\n                             \"the interface index cannot be negative or larger than %u;\"\n                             \" given \" ZEND_LONG_FMT,\n                             UINT_MAX,\n                             Z_LVAL_P(val));\n            ret = FAILURE;\n        } else {\n            *out = Z_LVAL_P(val);\n            ret = SUCCESS;\n        }\n    } else {\n        zend_string *str = zval_get_string(val);\n        ret = php_string_to_if_index(ZSTR_VAL(str), out);\n        zend_string_release(str);\n    }\n\n    return ret;\n}\n\nstatic int php_get_if_index_from_array(const HashTable *ht, const char *key, SocketImpl *sock, unsigned int *if_index) {\n    zval *val;\n\n    if ((val = zend_hash_str_find(ht, key, strlen(key))) == NULL) {\n        *if_index = 0; /* default: 0 */\n        return SUCCESS;\n    }\n\n    return php_get_if_index_from_zval(val, if_index);\n}\n\nstatic int php_get_address_from_array(\n    const HashTable *ht, const char *key, SocketImpl *sock, php_sockaddr_storage *ss, socklen_t *ss_len) {\n    zval *val;\n    zend_string *str;\n\n    if ((val = zend_hash_str_find(ht, key, strlen(key))) == NULL) {\n        php_error_docref(NULL, E_WARNING, \"no key \\\"%s\\\" passed in optval\", key);\n        return FAILURE;\n    }\n    str = zval_get_string(val);\n    if (!php_set_inet46_addr(ss, ss_len, ZSTR_VAL(str), sock)) {\n        zend_string_release(str);\n        return FAILURE;\n    }\n    zend_string_release(str);\n    return SUCCESS;\n}\n\nstatic int php_do_mcast_opt(SocketImpl *php_sock, int level, int optname, zval *arg4) {\n    HashTable *opt_ht;\n    unsigned int if_index;\n    int retval;\n    int (*mcast_req_fun)(SocketImpl *, int, struct sockaddr *, socklen_t, unsigned);\n#ifdef HAS_MCAST_EXT\n    int (*mcast_sreq_fun)(SocketImpl *, int, struct sockaddr *, socklen_t, struct sockaddr *, socklen_t, unsigned);\n#endif\n\n    php_sockaddr_storage group = {0};\n    socklen_t glen = 0;\n#ifdef HAS_MCAST_EXT\n    php_sockaddr_storage source = {0};\n    socklen_t slen = 0;\n#endif\n\n    switch (optname) {\n    case PHP_MCAST_JOIN_GROUP:\n        mcast_req_fun = &php_mcast_join;\n        goto mcast_req_fun;\n    case PHP_MCAST_LEAVE_GROUP: {\n        mcast_req_fun = &php_mcast_leave;\n    mcast_req_fun:\n        convert_to_array_ex(arg4);\n        opt_ht = Z_ARRVAL_P(arg4);\n\n        if (php_get_address_from_array(opt_ht, \"group\", php_sock, &group, &glen) == FAILURE) {\n            return FAILURE;\n        }\n        if (php_get_if_index_from_array(opt_ht, \"interface\", php_sock, &if_index) == FAILURE) {\n            return FAILURE;\n        }\n\n        retval = mcast_req_fun(php_sock, level, (struct sockaddr *) &group, glen, if_index);\n        break;\n    }\n\n#ifdef HAS_MCAST_EXT\n    case PHP_MCAST_BLOCK_SOURCE:\n        mcast_sreq_fun = &php_mcast_block_source;\n        goto mcast_sreq_fun;\n    case PHP_MCAST_UNBLOCK_SOURCE:\n        mcast_sreq_fun = &php_mcast_unblock_source;\n        goto mcast_sreq_fun;\n    case PHP_MCAST_JOIN_SOURCE_GROUP:\n        mcast_sreq_fun = &php_mcast_join_source;\n        goto mcast_sreq_fun;\n    case PHP_MCAST_LEAVE_SOURCE_GROUP: {\n        mcast_sreq_fun = &php_mcast_leave_source;\n    mcast_sreq_fun:\n        convert_to_array_ex(arg4);\n        opt_ht = Z_ARRVAL_P(arg4);\n\n        if (php_get_address_from_array(opt_ht, \"group\", php_sock, &group, &glen) == FAILURE) {\n            return FAILURE;\n        }\n        if (php_get_address_from_array(opt_ht, \"source\", php_sock, &source, &slen) == FAILURE) {\n            return FAILURE;\n        }\n        if (php_get_if_index_from_array(opt_ht, \"interface\", php_sock, &if_index) == FAILURE) {\n            return FAILURE;\n        }\n\n        retval = mcast_sreq_fun(\n            php_sock, level, (struct sockaddr *) &group, glen, (struct sockaddr *) &source, slen, if_index);\n        break;\n    }\n#endif\n    default:\n        php_error_docref(NULL,\n                         E_WARNING,\n                         \"unexpected option in php_do_mcast_opt (level %d, option %d). \"\n                         \"This is a bug.\",\n                         level,\n                         optname);\n        return FAILURE;\n    }\n\n    if (retval != 0) {\n        if (retval != -2) { /* error, but message already emitted */\n            PHP_SWOOLE_SOCKET_ERROR(php_sock, \"unable to set socket option\", errno);\n        }\n        return FAILURE;\n    }\n    return SUCCESS;\n}\n\nint php_do_setsockopt_ip_mcast(SocketImpl *php_sock, int level, int optname, zval *arg4) {\n    unsigned int if_index;\n    struct in_addr if_addr;\n    void *opt_ptr;\n    socklen_t optlen;\n    unsigned char ipv4_mcast_ttl_lback;\n    int retval;\n\n    switch (optname) {\n    case PHP_MCAST_JOIN_GROUP:\n    case PHP_MCAST_LEAVE_GROUP:\n#ifdef HAS_MCAST_EXT\n    case PHP_MCAST_BLOCK_SOURCE:\n    case PHP_MCAST_UNBLOCK_SOURCE:\n    case PHP_MCAST_JOIN_SOURCE_GROUP:\n    case PHP_MCAST_LEAVE_SOURCE_GROUP:\n#endif\n        if (php_do_mcast_opt(php_sock, level, optname, arg4) == FAILURE) {\n            return FAILURE;\n        } else {\n            return SUCCESS;\n        }\n\n    case IP_MULTICAST_IF:\n        if (php_get_if_index_from_zval(arg4, &if_index) == FAILURE) {\n            return FAILURE;\n        }\n\n        if (php_if_index_to_addr4(if_index, php_sock, &if_addr) == FAILURE) {\n            return FAILURE;\n        }\n        opt_ptr = &if_addr;\n        optlen = sizeof(if_addr);\n        goto dosockopt;\n\n    case IP_MULTICAST_LOOP:\n        convert_to_boolean_ex(arg4);\n        ipv4_mcast_ttl_lback = (unsigned char) (Z_TYPE_P(arg4) == IS_TRUE);\n        goto ipv4_loop_ttl;\n\n    case IP_MULTICAST_TTL:\n        convert_to_long_ex(arg4);\n        if (Z_LVAL_P(arg4) < 0L || Z_LVAL_P(arg4) > 255L) {\n            php_error_docref(NULL, E_WARNING, \"Expected a value between 0 and 255\");\n            return FAILURE;\n        }\n        ipv4_mcast_ttl_lback = (unsigned char) Z_LVAL_P(arg4);\n    ipv4_loop_ttl:\n        opt_ptr = &ipv4_mcast_ttl_lback;\n        optlen = sizeof(ipv4_mcast_ttl_lback);\n        goto dosockopt;\n    }\n\n    return 1;\n\ndosockopt:\n    retval = setsockopt(php_sock->get_fd(), level, optname, opt_ptr, optlen);\n    if (retval != 0) {\n        PHP_SWOOLE_SOCKET_ERROR(php_sock, \"unable to set socket option\", errno);\n        return FAILURE;\n    }\n\n    return SUCCESS;\n}\n\nint php_do_setsockopt_ipv6_mcast(SocketImpl *php_sock, int level, int optname, zval *arg4) {\n    unsigned int if_index;\n    void *opt_ptr;\n    socklen_t optlen;\n    int ov;\n    int retval;\n\n    switch (optname) {\n    case PHP_MCAST_JOIN_GROUP:\n    case PHP_MCAST_LEAVE_GROUP:\n#ifdef HAS_MCAST_EXT\n    case PHP_MCAST_BLOCK_SOURCE:\n    case PHP_MCAST_UNBLOCK_SOURCE:\n    case PHP_MCAST_JOIN_SOURCE_GROUP:\n    case PHP_MCAST_LEAVE_SOURCE_GROUP:\n#endif\n        if (php_do_mcast_opt(php_sock, level, optname, arg4) == FAILURE) {\n            return FAILURE;\n        } else {\n            return SUCCESS;\n        }\n\n    case IPV6_MULTICAST_IF:\n        if (php_get_if_index_from_zval(arg4, &if_index) == FAILURE) {\n            return FAILURE;\n        }\n\n        opt_ptr = &if_index;\n        optlen = sizeof(if_index);\n        goto dosockopt;\n\n    case IPV6_MULTICAST_LOOP:\n        convert_to_boolean_ex(arg4);\n        ov = (int) Z_TYPE_P(arg4) == IS_TRUE;\n        goto ipv6_loop_hops;\n    case IPV6_MULTICAST_HOPS:\n        convert_to_long_ex(arg4);\n        if (Z_LVAL_P(arg4) < -1L || Z_LVAL_P(arg4) > 255L) {\n            php_error_docref(NULL, E_WARNING, \"Expected a value between -1 and 255\");\n            return FAILURE;\n        }\n        ov = (int) Z_LVAL_P(arg4);\n    ipv6_loop_hops:\n        opt_ptr = &ov;\n        optlen = sizeof(ov);\n        goto dosockopt;\n    }\n\n    return 1; /* not handled */\n\ndosockopt:\n    retval = setsockopt(php_sock->get_fd(), level, optname, opt_ptr, optlen);\n    if (retval != 0) {\n        PHP_SWOOLE_SOCKET_ERROR(php_sock, \"unable to set socket option\", errno);\n        return FAILURE;\n    }\n\n    return SUCCESS;\n}\n\nint php_mcast_join(SocketImpl *sock, int level, struct sockaddr *group, socklen_t group_len, unsigned int if_index) {\n    return _php_mcast_join_leave(sock, level, group, group_len, if_index, 1);\n}\n\nint php_mcast_leave(SocketImpl *sock, int level, struct sockaddr *group, socklen_t group_len, unsigned int if_index) {\n    return _php_mcast_join_leave(sock, level, group, group_len, if_index, 0);\n}\n\n#ifdef HAS_MCAST_EXT\nint php_mcast_join_source(SocketImpl *sock,\n                          int level,\n                          struct sockaddr *group,\n                          socklen_t group_len,\n                          struct sockaddr *source,\n                          socklen_t source_len,\n                          unsigned int if_index) {\n    return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, JOIN_SOURCE);\n}\n\nint php_mcast_leave_source(SocketImpl *sock,\n                           int level,\n                           struct sockaddr *group,\n                           socklen_t group_len,\n                           struct sockaddr *source,\n                           socklen_t source_len,\n                           unsigned int if_index) {\n    return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, LEAVE_SOURCE);\n}\n\nint php_mcast_block_source(SocketImpl *sock,\n                           int level,\n                           struct sockaddr *group,\n                           socklen_t group_len,\n                           struct sockaddr *source,\n                           socklen_t source_len,\n                           unsigned int if_index) {\n    return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, BLOCK_SOURCE);\n}\n\nint php_mcast_unblock_source(SocketImpl *sock,\n                             int level,\n                             struct sockaddr *group,\n                             socklen_t group_len,\n                             struct sockaddr *source,\n                             socklen_t source_len,\n                             unsigned int if_index) {\n    return _php_mcast_source_op(sock, level, group, group_len, source, source_len, if_index, UNBLOCK_SOURCE);\n}\n#endif /* HAS_MCAST_EXT */\n\nstatic int _php_mcast_join_leave(SocketImpl *sock,\n                                 int level,\n                                 struct sockaddr *group, /* struct sockaddr_in/sockaddr_in6 */\n                                 socklen_t group_len,\n                                 unsigned int if_index,\n                                 int join) {\n#ifdef RFC3678_API\n    struct group_req greq = {0};\n\n    memcpy(&greq.gr_group, group, group_len);\n    assert(greq.gr_group.ss_family != 0); /* the caller has set this */\n    greq.gr_interface = if_index;\n\n    return setsockopt(sock->get_fd(), level, join ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, (char *) &greq, sizeof(greq));\n#else\n    if (sock->get_sock_type() == AF_INET) {\n        struct ip_mreq mreq = {{0}};\n        struct in_addr addr;\n\n        assert(group_len == sizeof(struct sockaddr_in));\n\n        if (if_index != 0) {\n            if (php_if_index_to_addr4(if_index, sock, &addr) == FAILURE)\n                return -2; /* failure, but notice already emitted */\n            mreq.imr_interface = addr;\n        } else {\n            mreq.imr_interface.s_addr = htonl(INADDR_ANY);\n        }\n        mreq.imr_multiaddr = ((struct sockaddr_in *) group)->sin_addr;\n        return setsockopt(\n            sock->get_fd(), level, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, (char *) &mreq, sizeof(mreq));\n    } else if (sock->get_sock_type() == AF_INET6) {\n        struct ipv6_mreq mreq = {{{{0}}}};\n\n        assert(group_len == sizeof(struct sockaddr_in6));\n\n        mreq.ipv6mr_multiaddr = ((struct sockaddr_in6 *) group)->sin6_addr;\n        mreq.ipv6mr_interface = if_index;\n\n        return setsockopt(\n            sock->get_fd(), level, join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP, (char *) &mreq, sizeof(mreq));\n    } else {\n        php_error_docref(NULL,\n                         E_WARNING,\n                         \"Option %s is inapplicable to this socket type\",\n                         join ? \"MCAST_JOIN_GROUP\" : \"MCAST_LEAVE_GROUP\");\n        return -2;\n    }\n#endif\n}\n\n#ifdef HAS_MCAST_EXT\nstatic int _php_mcast_source_op(SocketImpl *sock,\n                                int level,\n                                struct sockaddr *group,\n                                socklen_t group_len,\n                                struct sockaddr *source,\n                                socklen_t source_len,\n                                unsigned int if_index,\n                                enum source_op sop) {\n#ifdef RFC3678_API\n    struct group_source_req gsreq = {0};\n\n    memcpy(&gsreq.gsr_group, group, group_len);\n    assert(gsreq.gsr_group.ss_family != 0);\n    memcpy(&gsreq.gsr_source, source, source_len);\n    assert(gsreq.gsr_source.ss_family != 0);\n    gsreq.gsr_interface = if_index;\n\n    return setsockopt(sock->get_fd(), level, _php_source_op_to_rfc3678_op(sop), (char *) &gsreq, sizeof(gsreq));\n#else\n    if (sock->type == AF_INET) {\n        struct ip_mreq_source mreqs = {0};\n        struct in_addr addr;\n\n        mreqs.imr_multiaddr = ((struct sockaddr_in *) group)->sin_addr;\n        mreqs.imr_sourceaddr = ((struct sockaddr_in *) source)->sin_addr;\n\n        assert(group_len == sizeof(struct sockaddr_in));\n        assert(source_len == sizeof(struct sockaddr_in));\n\n        if (if_index != 0) {\n            if (php_if_index_to_addr4(if_index, sock, &addr) == FAILURE)\n                return -2; /* failure, but notice already emitted */\n            mreqs.imr_interface = addr;\n        } else {\n            mreqs.imr_interface.s_addr = htonl(INADDR_ANY);\n        }\n\n        return setsockopt(sock->get_fd(), level, _php_source_op_to_ipv4_op(sop), (char *) &mreqs, sizeof(mreqs));\n    } else if (sock->type == AF_INET6) {\n        php_error_docref(\n            NULL, E_WARNING, \"This platform does not support %s for IPv6 sockets\", _php_source_op_to_string(sop));\n        return -2;\n    } else {\n        php_error_docref(\n            NULL, E_WARNING, \"Option %s is inapplicable to this socket type\", _php_source_op_to_string(sop));\n        return -2;\n    }\n#endif\n}\n\n#if RFC3678_API\nstatic int _php_source_op_to_rfc3678_op(enum source_op sop) {\n    switch (sop) {\n    case JOIN_SOURCE:\n        return MCAST_JOIN_SOURCE_GROUP;\n    case LEAVE_SOURCE:\n        return MCAST_LEAVE_SOURCE_GROUP;\n    case BLOCK_SOURCE:\n        return MCAST_BLOCK_SOURCE;\n    case UNBLOCK_SOURCE:\n        return MCAST_UNBLOCK_SOURCE;\n    }\n\n    abort();\n    return 0;\n}\n#else\nstatic const char *_php_source_op_to_string(enum source_op sop) {\n    switch (sop) {\n    case JOIN_SOURCE:\n        return \"MCAST_JOIN_SOURCE_GROUP\";\n    case LEAVE_SOURCE:\n        return \"MCAST_LEAVE_SOURCE_GROUP\";\n    case BLOCK_SOURCE:\n        return \"MCAST_BLOCK_SOURCE\";\n    case UNBLOCK_SOURCE:\n        return \"MCAST_UNBLOCK_SOURCE\";\n    }\n\n    abort();\n    return \"\";\n}\n\nstatic int _php_source_op_to_ipv4_op(enum source_op sop) {\n    switch (sop) {\n    case JOIN_SOURCE:\n        return IP_ADD_SOURCE_MEMBERSHIP;\n    case LEAVE_SOURCE:\n        return IP_DROP_SOURCE_MEMBERSHIP;\n    case BLOCK_SOURCE:\n        return IP_BLOCK_SOURCE;\n    case UNBLOCK_SOURCE:\n        return IP_UNBLOCK_SOURCE;\n    }\n\n    abort();\n    return 0;\n}\n#endif\n\n#endif /* HAS_MCAST_EXT */\n\n#ifdef PHP_WIN32\nint php_if_index_to_addr4(unsigned if_index, SocketImpl *php_sock, struct in_addr *out_addr) {\n    MIB_IPADDRTABLE *addr_table;\n    ULONG size;\n    DWORD retval;\n    DWORD i;\n\n    (void) php_sock; /* not necessary */\n\n    if (if_index == 0) {\n        out_addr->s_addr = INADDR_ANY;\n        return SUCCESS;\n    }\n\n    size = 4 * (sizeof *addr_table);\n    addr_table = emalloc(size);\nretry:\n    retval = GetIpAddrTable(addr_table, &size, 0);\n    if (retval == ERROR_INSUFFICIENT_BUFFER) {\n        efree(addr_table);\n        addr_table = emalloc(size);\n        goto retry;\n    }\n    if (retval != NO_ERROR) {\n        php_error_docref(NULL, E_WARNING, \"GetIpAddrTable failed with error %lu\", retval);\n        return FAILURE;\n    }\n    for (i = 0; i < addr_table->dwNumEntries; i++) {\n        MIB_IPADDRROW r = addr_table->table[i];\n        if (r.dwIndex == if_index) {\n            out_addr->s_addr = r.dwAddr;\n            return SUCCESS;\n        }\n    }\n    php_error_docref(NULL, E_WARNING, \"No interface with index %u was found\", if_index);\n    return FAILURE;\n}\n\nint php_add4_to_if_index(struct in_addr *addr, SocketImpl *php_sock, unsigned *if_index) {\n    MIB_IPADDRTABLE *addr_table;\n    ULONG size;\n    DWORD retval;\n    DWORD i;\n\n    (void) php_sock; /* not necessary */\n\n    if (addr->s_addr == INADDR_ANY) {\n        *if_index = 0;\n        return SUCCESS;\n    }\n\n    size = 4 * (sizeof *addr_table);\n    addr_table = emalloc(size);\nretry:\n    retval = GetIpAddrTable(addr_table, &size, 0);\n    if (retval == ERROR_INSUFFICIENT_BUFFER) {\n        efree(addr_table);\n        addr_table = emalloc(size);\n        goto retry;\n    }\n    if (retval != NO_ERROR) {\n        php_error_docref(NULL, E_WARNING, \"GetIpAddrTable failed with error %lu\", retval);\n        return FAILURE;\n    }\n    for (i = 0; i < addr_table->dwNumEntries; i++) {\n        MIB_IPADDRROW r = addr_table->table[i];\n        if (r.dwAddr == addr->s_addr) {\n            *if_index = r.dwIndex;\n            return SUCCESS;\n        }\n    }\n\n    {\n        char addr_str[17] = {0};\n        inet_ntop(AF_INET, addr, addr_str, sizeof(addr_str));\n        php_error_docref(NULL, E_WARNING, \"The interface with IP address %s was not found\", addr_str);\n    }\n    return FAILURE;\n}\n\n#else\n\nint php_if_index_to_addr4(unsigned if_index, SocketImpl *php_sock, struct in_addr *out_addr) {\n    struct ifreq if_req;\n\n    if (if_index == 0) {\n        out_addr->s_addr = INADDR_ANY;\n        return SUCCESS;\n    }\n\n#if !defined(ifr_ifindex) && defined(ifr_index)\n#define ifr_ifindex ifr_index\n#endif\n\n#if defined(SIOCGIFNAME)\n    if_req.ifr_ifindex = if_index;\n    if (ioctl(php_sock->get_fd(), SIOCGIFNAME, &if_req) == -1) {\n#elif defined(HAVE_IF_INDEXTONAME)\n    if (if_indextoname(if_index, if_req.ifr_name) == NULL) {\n#else\n#error Neither SIOCGIFNAME nor if_indextoname are available\n#endif\n        php_error_docref(NULL, E_WARNING, \"Failed obtaining address for interface %u: error %d\", if_index, errno);\n        return FAILURE;\n    }\n\n    if (ioctl(php_sock->get_fd(), SIOCGIFADDR, &if_req) == -1) {\n        php_error_docref(NULL, E_WARNING, \"Failed obtaining address for interface %u: error %d\", if_index, errno);\n        return FAILURE;\n    }\n\n    memcpy(out_addr, &((struct sockaddr_in *) &if_req.ifr_addr)->sin_addr, sizeof *out_addr);\n    return SUCCESS;\n}\n\nint php_add4_to_if_index(struct in_addr *addr, SocketImpl *php_sock, unsigned *if_index) {\n    struct ifconf if_conf = {0};\n    char *buf = NULL, *p;\n    int size = 0, lastsize = 0;\n    size_t entry_len;\n\n    if (addr->s_addr == INADDR_ANY) {\n        *if_index = 0;\n        return SUCCESS;\n    }\n\n    for (;;) {\n        size += 5 * sizeof(struct ifreq);\n        buf = (char *) ecalloc(size, 1);\n        if_conf.ifc_len = size;\n        if_conf.ifc_buf = (caddr_t) buf;\n\n        if (ioctl(php_sock->get_fd(), SIOCGIFCONF, (char *) &if_conf) == -1 && (errno != EINVAL || lastsize != 0)) {\n            php_error_docref(NULL, E_WARNING, \"Failed obtaining interfaces list: error %d\", errno);\n            goto err;\n        }\n\n        if (if_conf.ifc_len == lastsize) /* not increasing anymore */\n            break;\n        else {\n            lastsize = if_conf.ifc_len;\n            efree(buf);\n            buf = NULL;\n        }\n    }\n\n    for (p = (char *) if_conf.ifc_buf; p < ((char *) if_conf.ifc_buf + if_conf.ifc_len); p += entry_len) {\n        struct ifreq *cur_req;\n\n        /* let's hope the pointer is aligned */\n        cur_req = (struct ifreq *) p;\n\n#ifdef HAVE_SOCKADDR_SA_LEN\n        entry_len = cur_req->ifr_addr.sa_len + sizeof(cur_req->ifr_name);\n#else\n        /* if there's no sa_len, assume the ifr_addr field is a sockaddr */\n        entry_len = sizeof(struct sockaddr) + sizeof(cur_req->ifr_name);\n#endif\n        entry_len = MAX(entry_len, sizeof(*cur_req));\n\n        if ((((struct sockaddr *) &cur_req->ifr_addr)->sa_family == AF_INET) &&\n            (((struct sockaddr_in *) &cur_req->ifr_addr)->sin_addr.s_addr == addr->s_addr)) {\n#if defined(SIOCGIFINDEX)\n            if (ioctl(php_sock->get_fd(), SIOCGIFINDEX, (char *) cur_req) == -1) {\n#elif defined(HAVE_IF_NAMETOINDEX)\n            unsigned index_tmp;\n            if ((index_tmp = if_nametoindex(cur_req->ifr_name)) == 0) {\n#else\n#error Neither SIOCGIFINDEX nor if_nametoindex are available\n#endif\n                php_error_docref(NULL, E_WARNING, \"Error converting interface name to index: error %d\", errno);\n                goto err;\n            } else {\n#if defined(SIOCGIFINDEX)\n                *if_index = cur_req->ifr_ifindex;\n#else\n                *if_index = index_tmp;\n#endif\n                efree(buf);\n                return SUCCESS;\n            }\n        }\n    }\n\n    {\n        char addr_str[17] = {0};\n        inet_ntop(AF_INET, addr, addr_str, sizeof(addr_str));\n        php_error_docref(NULL, E_WARNING, \"The interface with IP address %s was not found\", addr_str);\n    }\n\nerr:\n    if (buf != NULL) efree(buf);\n    return FAILURE;\n}\n#endif\n"
  },
  {
    "path": "thirdparty/php/sockets/multicast.h",
    "content": "/*\n +----------------------------------------------------------------------+\n | PHP Version 7                                                        |\n +----------------------------------------------------------------------+\n | Copyright (c) 1997-2018 The PHP Group                                |\n +----------------------------------------------------------------------+\n | This source file is subject to version 3.01 of the PHP license,      |\n | that is bundled with this package in the file LICENSE, and is        |\n | available through the world-wide-web at the following url:           |\n | http://www.php.net/license/3_01.txt                                  |\n | If you did not receive a copy of the PHP license and are unable to   |\n | obtain it through the world-wide-web, please send a note to          |\n | license@php.net so we can mail you a copy immediately.               |\n +----------------------------------------------------------------------+\n | Authors: Gustavo Lopes    <cataphract@php.net>                       |\n +----------------------------------------------------------------------+\n */\n\n#pragma once\n\n#include \"php_swoole_cxx.h\"\n\n#if defined(MCAST_JOIN_GROUP) && !defined(__APPLE__)\n#define RFC3678_API 1\n/* has block/unblock and source membership, in this case for both IPv4 and IPv6 */\n#define HAS_MCAST_EXT 1\n#elif defined(IP_ADD_SOURCE_MEMBERSHIP) && !defined(__APPLE__)\n/* has block/unblock and source membership, but only for IPv4 */\n#define HAS_MCAST_EXT 1\n#endif\n\n#ifndef RFC3678_API\n#define PHP_MCAST_JOIN_GROUP IP_ADD_MEMBERSHIP\n#define PHP_MCAST_LEAVE_GROUP IP_DROP_MEMBERSHIP\n#ifdef HAS_MCAST_EXT\n#define PHP_MCAST_BLOCK_SOURCE IP_BLOCK_SOURCE\n#define PHP_MCAST_UNBLOCK_SOURCE IP_UNBLOCK_SOURCE\n#define PHP_MCAST_JOIN_SOURCE_GROUP IP_ADD_SOURCE_MEMBERSHIP\n#define PHP_MCAST_LEAVE_SOURCE_GROUP IP_DROP_SOURCE_MEMBERSHIP\n#endif\n#else\n#define PHP_MCAST_JOIN_GROUP MCAST_JOIN_GROUP\n#define PHP_MCAST_LEAVE_GROUP MCAST_LEAVE_GROUP\n#define PHP_MCAST_BLOCK_SOURCE MCAST_BLOCK_SOURCE\n#define PHP_MCAST_UNBLOCK_SOURCE MCAST_UNBLOCK_SOURCE\n#define PHP_MCAST_JOIN_SOURCE_GROUP MCAST_JOIN_SOURCE_GROUP\n#define PHP_MCAST_LEAVE_SOURCE_GROUP MCAST_LEAVE_SOURCE_GROUP\n#endif\n\nint php_do_setsockopt_ip_mcast(SocketImpl *php_sock, int level, int optname, zval *arg4);\nint php_do_setsockopt_ipv6_mcast(SocketImpl *php_sock, int level, int optname, zval *arg4);\nint php_if_index_to_addr4(unsigned if_index, SocketImpl *php_sock, struct in_addr *out_addr);\nint php_add4_to_if_index(struct in_addr *addr, SocketImpl *php_sock, unsigned *if_index);\nint php_string_to_if_index(const char *val, unsigned *out);\nint php_mcast_join(SocketImpl *sock, int level, struct sockaddr *group, socklen_t group_len, unsigned int if_index);\n\nint php_mcast_leave(SocketImpl *sock, int level, struct sockaddr *group, socklen_t group_len, unsigned int if_index);\n\n#ifdef HAS_MCAST_EXT\nint php_mcast_join_source(SocketImpl *sock,\n                          int level,\n                          struct sockaddr *group,\n                          socklen_t group_len,\n                          struct sockaddr *source,\n                          socklen_t source_len,\n                          unsigned int if_index);\n\nint php_mcast_leave_source(SocketImpl *sock,\n                           int level,\n                           struct sockaddr *group,\n                           socklen_t group_len,\n                           struct sockaddr *source,\n                           socklen_t source_len,\n                           unsigned int if_index);\n\nint php_mcast_block_source(SocketImpl *sock,\n                           int level,\n                           struct sockaddr *group,\n                           socklen_t group_len,\n                           struct sockaddr *source,\n                           socklen_t source_len,\n                           unsigned int if_index);\n\nint php_mcast_unblock_source(SocketImpl *sock,\n                             int level,\n                             struct sockaddr *group,\n                             socklen_t group_len,\n                             struct sockaddr *source,\n                             socklen_t source_len,\n                             unsigned int if_index);\n#endif\n"
  },
  {
    "path": "thirdparty/php/sockets/php_sockets_cxx.h",
    "content": "#pragma once\n\n#include \"php_swoole_cxx.h\"\n\n#include <php_network.h>\n#include <netinet/in.h>\n\n#include \"thirdparty/php/sockets/multicast.h\"\n#include \"thirdparty/php/sockets/conversions.h\"\n\n#define PHP_SWOOLE_SOCKET_ERROR(socket, msg, errn)                                                                     \\\n    do {                                                                                                               \\\n        int _err = (errn); /* save value to avoid repeated calls to WSAGetLastError() on Windows */                    \\\n        (socket)->errCode = _err;                                                                                      \\\n        if (_err != EAGAIN && _err != EWOULDBLOCK && _err != EINPROGRESS) {                                            \\\n            php_error_docref(NULL, E_WARNING, \"%s [%d]: %s\", msg, _err, strerror(_err));                               \\\n        }                                                                                                              \\\n    } while (0)\n\nint php_do_setsockopt_ipv6_rfc3542(SocketImpl *php_sock, int level, int optname, zval *arg4);\nint php_do_getsockopt_ipv6_rfc3542(SocketImpl *php_sock, int level, int optname, zval *result);\n\nint php_string_to_if_index(const char *val, unsigned *out);\n\n/*\n * Convert an IPv6 literal or a hostname info a sockaddr_in6.\n * The IPv6 literal can be a IPv4 mapped address (like ::ffff:127.0.0.1).\n * If the hostname yields no IPv6 addresses, a mapped IPv4 address may be returned (AI_V4MAPPED)\n */\nint php_set_inet6_addr(struct sockaddr_in6 *sin6, char *string, SocketImpl *php_sock);\n\n/*\n * Convert an IPv4 literal or a hostname into a sockaddr_in.\n */\nint php_set_inet_addr(struct sockaddr_in *sin, char *string, SocketImpl *php_sock);\n\n/*\n * Calls either php_set_inet6_addr() or php_set_inet_addr(), depending on the type of the socket.\n */\nint php_set_inet46_addr(php_sockaddr_storage *ss, socklen_t *ss_len, char *string, SocketImpl *php_sock);\n"
  },
  {
    "path": "thirdparty/php/sockets/sendrecvmsg.cc",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | PHP Version 7                                                        |\n   +----------------------------------------------------------------------+\n   | Copyright (c) 1997-2018 The PHP Group                                |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | http://www.php.net/license/3_01.txt                                  |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Authors: Gustavo Lopes    <cataphract@php.net>                       |\n   +----------------------------------------------------------------------+\n */\n\n#include \"php_sockets_cxx.h\"\n\n#include <limits.h>\n#include <Zend/zend_llist.h>\n#ifdef ZTS\n#include <TSRM/TSRM.h>\n#endif\n\n#define MAX_USER_BUFF_SIZE ((size_t) (100 * 1024 * 1024))\n#define DEFAULT_BUFF_SIZE 8192\n#define MAX_ARRAY_KEY_SIZE 128\n\n#define LONG_CHECK_VALID_INT(l)                                                                                        \\\n    do {                                                                                                               \\\n        if ((l) < INT_MIN && (l) > INT_MAX) {                                                                          \\\n            php_error_docref(NULL,                                                                                     \\\n                             E_WARNING,                                                                                \\\n                             \"The value \" ZEND_LONG_FMT \" does not fit inside \"                                        \\\n                             \"the boundaries of a native integer\",                                                     \\\n                             (l));                                                                                     \\\n            return;                                                                                                    \\\n        }                                                                                                              \\\n    } while (0)\n\nint php_do_setsockopt_ipv6_rfc3542(SocketImpl *php_sock, int level, int optname, zval *arg4) {\n#ifdef IPV6_PKTINFO\n    struct err_s err = {0};\n#endif\n    zend_llist *allocations = NULL;\n    void *opt_ptr;\n    socklen_t optlen;\n    int retval;\n\n    assert(level == IPPROTO_IPV6);\n\n    switch (optname) {\n#ifdef IPV6_PKTINFO\n    case IPV6_PKTINFO:\n        opt_ptr = from_zval_run_conversions(\n            arg4, php_sock, from_zval_write_in6_pktinfo, sizeof(struct in6_pktinfo), \"in6_pktinfo\", &allocations, &err);\n        if (err.has_error) {\n            err_msg_dispose(&err);\n            return FAILURE;\n        }\n\n        optlen = sizeof(struct in6_pktinfo);\n        goto dosockopt;\n#endif\n    }\n\n    /* we also support IPV6_TCLASS, but that can be handled by the default\n     * integer optval handling in the caller */\n    return 1;\n\n#ifdef IPV6_PKTINFO\ndosockopt:\n#endif\n    retval = setsockopt(php_sock->get_fd(), level, optname, opt_ptr, optlen);\n    if (retval != 0) {\n        PHP_SWOOLE_SOCKET_ERROR(php_sock, \"unable to set socket option\", errno);\n    }\n    allocations_dispose(&allocations);\n\n    return retval != 0 ? FAILURE : SUCCESS;\n}\n\nint php_do_getsockopt_ipv6_rfc3542(SocketImpl *php_sock, int level, int optname, zval *result) {\n    struct err_s err = {0};\n    char *buffer;\n    socklen_t size;\n    int res;\n    to_zval_read_field *reader;\n\n    assert(level == IPPROTO_IPV6);\n\n    switch (optname) {\n#ifdef IPV6_PKTINFO\n    case IPV6_PKTINFO:\n        size = sizeof(struct in6_pktinfo);\n        reader = &to_zval_read_in6_pktinfo;\n        break;\n#endif\n    default:\n        return 1;\n    }\n\n    buffer = (char *) ecalloc(1, size);\n    res = getsockopt(php_sock->get_fd(), level, optname, buffer, &size);\n    if (res != 0) {\n        PHP_SWOOLE_SOCKET_ERROR(php_sock, \"unable to get socket option\", errno);\n    } else {\n        zval tmp;\n        zval *zv = to_zval_run_conversions(buffer, reader, \"in6_pktinfo\", sw_empty_key_value_list, &err, &tmp);\n        if (err.has_error) {\n            err_msg_dispose(&err);\n            res = -1;\n        } else {\n            ZVAL_COPY_VALUE(result, zv);\n        }\n    }\n    efree(buffer);\n\n    return res == 0 ? SUCCESS : FAILURE;\n}\n"
  },
  {
    "path": "thirdparty/php/sockets/sockaddr_conv.cc",
    "content": "#include \"php_sockets_cxx.h\"\n\n#include <netdb.h>\n#include <arpa/inet.h>\n\n/* Sets addr by hostname, or by ip in string form (AF_INET6) */\nint php_set_inet6_addr(struct sockaddr_in6 *sin6, char *string, SocketImpl *php_sock) /* {{{ */\n{\n    struct in6_addr tmp;\n#if HAVE_GETADDRINFO\n    struct addrinfo hints;\n    struct addrinfo *addrinfo = NULL;\n#endif\n    char *scope = strchr(string, '%');\n\n    if (inet_pton(AF_INET6, string, &tmp)) {\n        memcpy(&(sin6->sin6_addr.s6_addr), &(tmp.s6_addr), sizeof(struct in6_addr));\n    } else {\n#if HAVE_GETADDRINFO\n\n        memset(&hints, 0, sizeof(struct addrinfo));\n        hints.ai_family = AF_INET6;\n#if HAVE_AI_V4MAPPED\n        hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;\n#else\n        hints.ai_flags = AI_ADDRCONFIG;\n#endif\n        getaddrinfo(string, NULL, &hints, &addrinfo);\n        if (!addrinfo) {\n#ifdef PHP_WIN32\n            PHP_SWOOLE_SOCKET_ERROR(php_sock, \"Host lookup failed\", WSAGetLastError());\n#else\n            PHP_SWOOLE_SOCKET_ERROR(php_sock, \"Host lookup failed\", (-10000 - h_errno));\n#endif\n            return 0;\n        }\n        if (addrinfo->ai_family != PF_INET6 || addrinfo->ai_addrlen != sizeof(struct sockaddr_in6)) {\n            php_error_docref(NULL, E_WARNING, \"Host lookup failed: Non AF_INET6 domain returned on AF_INET6 socket\");\n            freeaddrinfo(addrinfo);\n            return 0;\n        }\n\n        memcpy(&(sin6->sin6_addr.s6_addr),\n               ((struct sockaddr_in6 *) (addrinfo->ai_addr))->sin6_addr.s6_addr,\n               sizeof(struct in6_addr));\n        freeaddrinfo(addrinfo);\n\n#else\n        /* No IPv6 specific hostname resolution is available on this system? */\n        php_error_docref(NULL, E_WARNING, \"Host lookup failed: getaddrinfo() not available on this system\");\n        return 0;\n#endif\n    }\n\n    if (scope++) {\n        zend_long lval = 0;\n        double dval = 0;\n        unsigned scope_id = 0;\n\n        if (IS_LONG == is_numeric_string(scope, strlen(scope), &lval, &dval, 0)) {\n            if (lval > 0 && (zend_ulong) lval <= UINT_MAX) {\n                scope_id = lval;\n            }\n        } else {\n            php_string_to_if_index(scope, &scope_id);\n        }\n\n        sin6->sin6_scope_id = scope_id;\n    }\n\n    return 1;\n}\n/* }}} */\n\n/* Sets addr by hostname, or by ip in string form (AF_INET)  */\nint php_set_inet_addr(struct sockaddr_in *sin, char *string, SocketImpl *php_sock) /* {{{ */\n{\n    struct in_addr tmp;\n    struct hostent *host_entry;\n\n    if (inet_pton(AF_INET, string, &tmp)) {\n        sin->sin_addr.s_addr = tmp.s_addr;\n    } else {\n        if (strlen(string) > MAXFQDNLEN || !(host_entry = php_network_gethostbyname(string))) {\n            /* Note: < -10000 indicates a host lookup error */\n            PHP_SWOOLE_SOCKET_ERROR(php_sock, \"Host lookup failed\", (-10000 - h_errno));\n            return 0;\n        }\n        if (host_entry->h_addrtype != AF_INET) {\n            php_error_docref(NULL, E_WARNING, \"Host lookup failed: Non AF_INET domain returned on AF_INET socket\");\n            return 0;\n        }\n        memcpy(&(sin->sin_addr.s_addr), host_entry->h_addr_list[0], host_entry->h_length);\n    }\n\n    return 1;\n}\n/* }}} */\n\n/* Sets addr by hostname or by ip in string form (AF_INET or AF_INET6,\n * depending on the socket) */\nint php_set_inet46_addr(php_sockaddr_storage *ss, socklen_t *ss_len, char *string, SocketImpl *php_sock) /* {{{ */\n{\n    if (php_sock->get_sock_domain() == AF_INET) {\n        struct sockaddr_in t = {0};\n        if (php_set_inet_addr(&t, string, php_sock)) {\n            memcpy(ss, &t, sizeof t);\n            ss->ss_family = AF_INET;\n            *ss_len = sizeof(t);\n            return 1;\n        }\n    } else if (php_sock->get_sock_domain() == AF_INET6) {\n        struct sockaddr_in6 t = {0};\n        if (php_set_inet6_addr(&t, string, php_sock)) {\n            memcpy(ss, &t, sizeof t);\n            ss->ss_family = AF_INET6;\n            *ss_len = sizeof(t);\n            return 1;\n        }\n    } else {\n        php_error_docref(NULL, E_WARNING, \"IP address used in the context of an unexpected type of socket\");\n    }\n    return 0;\n}\n"
  },
  {
    "path": "thirdparty/php/ssh2/LICENSE",
    "content": "-------------------------------------------------------------------- \n                  The PHP License, version 3.01\nCopyright (c) 1999 - 2012 The PHP Group. All rights reserved.\n-------------------------------------------------------------------- \n\nRedistribution and use in source and binary forms, with or without\nmodification, is permitted provided that the following conditions\nare met:\n\n  1. Redistributions of source code must retain the above copyright\n     notice, this list of conditions and the following disclaimer.\n \n  2. Redistributions in binary form must reproduce the above copyright\n     notice, this list of conditions and the following disclaimer in\n     the documentation and/or other materials provided with the\n     distribution.\n \n  3. The name \"PHP\" must not be used to endorse or promote products\n     derived from this software without prior written permission. For\n     written permission, please contact group@php.net.\n  \n  4. Products derived from this software may not be called \"PHP\", nor\n     may \"PHP\" appear in their name, without prior written permission\n     from group@php.net.  You may indicate that your software works in\n     conjunction with PHP by saying \"Foo for PHP\" instead of calling\n     it \"PHP Foo\" or \"phpfoo\"\n \n  5. The PHP Group may publish revised and/or new versions of the\n     license from time to time. Each version will be given a\n     distinguishing version number.\n     Once covered code has been published under a particular version\n     of the license, you may always continue to use it under the terms\n     of that version. You may also choose to use such covered code\n     under the terms of any subsequent version of the license\n     published by the PHP Group. No one other than the PHP Group has\n     the right to modify the terms applicable to covered code created\n     under this License.\n\n  6. Redistributions of any form whatsoever must retain the following\n     acknowledgment:\n     \"This product includes PHP software, freely available from\n     <http://www.php.net/software/>\".\n\nTHIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND \nANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A \nPARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE PHP\nDEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, \nINDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES \n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR \nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\nHOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\nSTRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\nOF THE POSSIBILITY OF SUCH DAMAGE.\n\n-------------------------------------------------------------------- \n\nThis software consists of voluntary contributions made by many\nindividuals on behalf of the PHP Group.\n\nThe PHP Group can be contacted via Email at group@php.net.\n\nFor more information on the PHP Group and the PHP project, \nplease see <http://www.php.net>.\n\nPHP includes the Zend Engine, freely available at\n<http://www.zend.com>."
  },
  {
    "path": "thirdparty/php/ssh2/php_ssh2.h",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | PHP Version 7                                                        |\n   +----------------------------------------------------------------------+\n   | Copyright (c) 1997-2016 The PHP Group                                |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | http://www.php.net/license/3_01.txt                                  |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Author: Sara Golemon <pollita@php.net>                               |\n   +----------------------------------------------------------------------+\n*/\n\n#ifndef PHP_SSH2_H\n#define PHP_SSH2_H\n\n#include \"php_swoole_ssh2.h\"\n\n#include \"ext/standard/url.h\"\n#include \"main/php_network.h\"\n\n#define PHP_SSH2_DEFAULT_PORT 22\n\n/* Exported Constants */\n#define PHP_SSH2_FINGERPRINT_MD5 0x0000\n#define PHP_SSH2_FINGERPRINT_SHA1 0x0001\n#define PHP_SSH2_FINGERPRINT_HEX 0x0000\n#define PHP_SSH2_FINGERPRINT_RAW 0x0002\n\n#define PHP_SSH2_TERM_UNIT_CHARS 0x0000\n#define PHP_SSH2_TERM_UNIT_PIXELS 0x0001\n\n#define PHP_SSH2_DEFAULT_TERMINAL \"vanilla\"\n#define PHP_SSH2_DEFAULT_TERM_WIDTH 80\n#define PHP_SSH2_DEFAULT_TERM_HEIGHT 25\n#define PHP_SSH2_DEFAULT_TERM_UNIT PHP_SSH2_TERM_UNIT_CHARS\n\n#define PHP_SSH2_SESSION_RES_NAME \"SSH2 Session\"\n#define PHP_SSH2_CHANNEL_STREAM_NAME \"SSH2 Channel\"\n#define PHP_SSH2_LISTENER_RES_NAME \"SSH2 Listener\"\n#define PHP_SSH2_SFTP_RES_NAME \"SSH2 SFTP\"\n#define PHP_SSH2_PKEY_SUBSYS_RES_NAME \"SSH2 Publickey Subsystem\"\n\n#define PHP_SSH2_SFTP_STREAM_NAME \"SSH2 SFTP File\"\n#define PHP_SSH2_SFTP_DIRSTREAM_NAME \"SSH2 SFTP Directory\"\n#define PHP_SSH2_SFTP_WRAPPER_NAME \"SSH2 SFTP\"\n\n#define PHP_SSH2_LISTEN_MAX_QUEUED 128\n\ntypedef struct _php_ssh2_sftp_data {\n    LIBSSH2_SESSION *session;\n    LIBSSH2_SFTP *sftp;\n\n    zend_resource *session_rsrc;\n} php_ssh2_sftp_data;\n\ntypedef struct _php_ssh2_listener_data {\n    LIBSSH2_SESSION *session;\n    LIBSSH2_LISTENER *listener;\n\n    zend_resource *session_rsrc;\n} php_ssh2_listener_data;\n\ntypedef struct _php_ssh2_pkey_subsys_data {\n    LIBSSH2_SESSION *session;\n    LIBSSH2_PUBLICKEY *pkey;\n\n    zend_resource *session_rsrc;\n} php_ssh2_pkey_subsys_data;\n\n#define SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession)                                                         \\\n    if ((session = (LIBSSH2_SESSION *) zend_fetch_resource(                                                            \\\n             Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) {                                \\\n        RETURN_FALSE;                                                                                                  \\\n    }                                                                                                                  \\\n    if (libssh2_userauth_authenticated(session)) {                                                                     \\\n        php_error_docref(NULL, E_WARNING, \"Connection already authenticated\");                                         \\\n        RETURN_FALSE;                                                                                                  \\\n    }\n\n#define SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession)                                                            \\\n    if ((session = (LIBSSH2_SESSION *) zend_fetch_resource(                                                            \\\n             Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) {                                \\\n        RETURN_FALSE;                                                                                                  \\\n    }                                                                                                                  \\\n    if (!libssh2_userauth_authenticated(session)) {                                                                    \\\n        php_error_docref(NULL, E_WARNING, \"Connection not authenticated\");                                             \\\n        RETURN_FALSE;                                                                                                  \\\n    }\n\ntypedef struct _php_ssh2_channel_data {\n    LIBSSH2_CHANNEL *channel;\n\n    /* Distinguish which stream we should read/write from/to */\n    unsigned int streamid;\n    char is_blocking;\n    long timeout;\n\n    /* Resource */\n    zend_resource *session_rsrc;\n\n    /* Allow one stream to be closed while the other is kept open */\n    unsigned char *refcount;\n\n} php_ssh2_channel_data;\n\nLIBSSH2_SESSION *php_ssh2_session_connect(const char *host, int port, zval *methods, zval *callbacks);\nvoid php_ssh2_sftp_dtor(zend_resource *rsrc);\nphp_url *php_ssh2_fopen_wraper_parse_path(const char *path,\n                                          const char *type,\n                                          php_stream_context *context,\n                                          LIBSSH2_SESSION **psession,\n                                          zend_resource **presource,\n                                          LIBSSH2_SFTP **psftp,\n                                          zend_resource **psftp_rsrc);\n\nextern php_stream_ops php_ssh2_channel_stream_ops;\n\nextern php_stream_wrapper php_ssh2_stream_wrapper_shell;\nextern php_stream_wrapper php_ssh2_stream_wrapper_exec;\nextern php_stream_wrapper php_ssh2_stream_wrapper_tunnel;\nextern php_stream_wrapper php_ssh2_stream_wrapper_scp;\nextern php_stream_wrapper php_ssh2_sftp_wrapper;\n\n/* Resource list entries */\nextern int le_ssh2_session;\nextern int le_ssh2_sftp;\n\nstatic inline LIBSSH2_SESSION *ssh2_get_session(php_ssh2_channel_data *abstract) {\n    return (LIBSSH2_SESSION *) zend_fetch_resource(abstract->session_rsrc, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);\n}\n\n#endif /* PHP_SSH2_H */\n\n/*\n * Local variables:\n * tab-width: 4\n * c-basic-offset: 4\n * indent-tabs-mode: t\n * End:\n */\n"
  },
  {
    "path": "thirdparty/php/ssh2/ssh2.cc",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | PHP Version 7                                                        |\n   +----------------------------------------------------------------------+\n   | Copyright (c) 1997-2016 The PHP Group                                |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | http://www.php.net/license/3_01.txt                                  |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Author: Sara Golemon <pollita@php.net>                               |\n   +----------------------------------------------------------------------+\n*/\n\n#include \"php_ssh2.h\"\n#include \"php_swoole_ssh2_hook.h\"\n\n#include \"ext/standard/info.h\"\n#include \"ext/standard/file.h\"\n\n#include \"main/php_network.h\"\n\n/* Internal Constants */\n#ifndef SHA_DIGEST_LENGTH\n#define SHA_DIGEST_LENGTH 20\n#endif\n\n#ifndef MD5_DIGEST_LENGTH\n#define MD5_DIGEST_LENGTH 16\n#endif\n\n/* True global resources - no need for thread safety here */\nint le_ssh2_session;\nint le_ssh2_listener;\nint le_ssh2_sftp;\nint le_ssh2_pkey_subsys;\n\n/* *************\n * Callbacks *\n ************* */\n\n/* {{{ php_ssh2_alloc_cb\n * Wrap emalloc()\n */\nstatic LIBSSH2_ALLOC_FUNC(php_ssh2_alloc_cb) {\n    return emalloc(count);\n}\n/* }}} */\n\n/* {{{ php_ssh2_free_cb\n * Wrap efree()\n */\nstatic LIBSSH2_FREE_FUNC(php_ssh2_free_cb) {\n    efree(ptr);\n}\n/* }}} */\n\n/* {{{ php_ssh2_realloc_cb\n * Wrap erealloc()\n */\nstatic LIBSSH2_REALLOC_FUNC(php_ssh2_realloc_cb) {\n    return erealloc(ptr, count);\n}\n/* }}} */\n\n/* {{{ php_ssh2_debug_cb\n * Debug packets\n */\nLIBSSH2_DEBUG_FUNC(php_ssh2_debug_cb) {\n    php_ssh2_session_data *data;\n    zval args[3];\n\n    if (!abstract || !*abstract) {\n        return;\n    }\n    data = (php_ssh2_session_data *) *abstract;\n    if (!data->debug_cb) {\n        return;\n    }\n\n    ZVAL_STRINGL(&args[0], message, message_len);\n    ZVAL_STRINGL(&args[1], language, language_len);\n    ZVAL_LONG(&args[2], always_display);\n\n    zval retval;\n    if (FAILURE == call_user_function(NULL, NULL, data->debug_cb, &retval, 3, args)) {\n        php_error_docref(NULL, E_WARNING, \"Failure calling debug callback\");\n    }\n\n    if (!Z_ISUNDEF(retval)) {\n        zval_ptr_dtor(&retval);\n    }\n}\n/* }}} */\n\n/* {{{ php_ssh2_ignore_cb\n * Ignore packets\n */\nLIBSSH2_IGNORE_FUNC(php_ssh2_ignore_cb) {\n    php_ssh2_session_data *data;\n    zval zretval;\n    zval args[1];\n\n    if (!abstract || !*abstract) {\n        return;\n    }\n    data = (php_ssh2_session_data *) *abstract;\n    if (!data->ignore_cb) {\n        return;\n    }\n\n    ZVAL_STRINGL(&args[0], message, message_len);\n\n    if (FAILURE == call_user_function(NULL, NULL, data->ignore_cb, &zretval, 1, args)) {\n        php_error_docref(NULL, E_WARNING, \"Failure calling ignore callback\");\n    }\n    if (Z_TYPE_P(&zretval) != IS_UNDEF) {\n        zval_ptr_dtor(&zretval);\n    }\n}\n/* }}} */\n\n/* {{{ php_ssh2_macerror_cb\n * Called when a MAC error occurs, offers the chance to ignore\n * WHY ARE YOU IGNORING MAC ERRORS??????\n */\nLIBSSH2_MACERROR_FUNC(php_ssh2_macerror_cb) {\n    php_ssh2_session_data *data;\n    zval zretval;\n    zval args[1];\n    int retval = -1;\n\n    if (!abstract || !*abstract) {\n        return -1;\n    }\n    data = (php_ssh2_session_data *) *abstract;\n    if (!data->macerror_cb) {\n        return -1;\n    }\n\n    ZVAL_STRINGL(&args[0], packet, packet_len);\n\n    if (FAILURE == call_user_function(NULL, NULL, data->macerror_cb, &zretval, 1, args)) {\n        php_error_docref(NULL, E_WARNING, \"Failure calling macerror callback\");\n    } else {\n        retval = zval_is_true(&zretval) ? 0 : -1;\n    }\n    if (Z_TYPE_P(&zretval) != IS_UNDEF) {\n        zval_ptr_dtor(&zretval);\n    }\n\n    return retval;\n}\n/* }}} */\n\n/* {{{ php_ssh2_disconnect_cb\n * Connection closed by foreign host\n */\nLIBSSH2_DISCONNECT_FUNC(php_ssh2_disconnect_cb) {\n    php_ssh2_session_data *data;\n    zval args[3];\n\n    if (!abstract || !*abstract) {\n        return;\n    }\n    data = (php_ssh2_session_data *) *abstract;\n    if (!data->disconnect_cb) {\n        return;\n    }\n\n    ZVAL_LONG(&args[0], reason);\n    ZVAL_STRINGL(&args[1], message, message_len);\n    ZVAL_STRINGL(&args[2], language, language_len);\n\n    zval retval;\n    if (FAILURE == call_user_function(NULL, NULL, data->disconnect_cb, &retval, 3, args)) {\n        php_error_docref(NULL, E_WARNING, \"Failure calling disconnect callback\");\n    }\n\n    if (!Z_ISUNDEF(retval)) {\n        zval_ptr_dtor(&retval);\n    }\n}\n/* }}} */\n\n/* *****************\n * Userspace API *\n ***************** */\n\n/* {{{ php_ssh2_set_callback\n * Try to set a method if it's passed in with the hash table\n */\nstatic int php_ssh2_set_callback(LIBSSH2_SESSION *session,\n                                 HashTable *ht,\n                                 const char *callback,\n                                 int callback_len,\n                                 int callback_type,\n                                 php_ssh2_session_data *data) {\n    zval *handler, *copyval;\n    void *internal_handler;\n    zend_string *callback_zstring;\n\n    callback_zstring = zend_string_init(callback, callback_len, 0);\n    if ((handler = zend_hash_find(ht, callback_zstring)) == NULL) {\n        zend_string_release(callback_zstring);\n        return 0;\n    }\n    zend_string_release(callback_zstring);\n\n    if (!zend_is_callable(handler, 0, NULL)) {\n        return -1;\n    }\n\n    copyval = (zval *) emalloc(sizeof(zval));\n    ZVAL_COPY(copyval, handler);\n\n    switch (callback_type) {\n    case LIBSSH2_CALLBACK_IGNORE:\n        internal_handler = (void *) php_ssh2_ignore_cb;\n        if (data->ignore_cb) {\n            zval_ptr_dtor(data->ignore_cb);\n        }\n        data->ignore_cb = copyval;\n        break;\n    case LIBSSH2_CALLBACK_DEBUG:\n        internal_handler = (void *) php_ssh2_debug_cb;\n        if (data->debug_cb) {\n            zval_ptr_dtor(data->debug_cb);\n        }\n        data->debug_cb = copyval;\n        break;\n    case LIBSSH2_CALLBACK_MACERROR:\n        internal_handler = (void *) php_ssh2_macerror_cb;\n        if (data->macerror_cb) {\n            zval_ptr_dtor(data->macerror_cb);\n        }\n        data->macerror_cb = copyval;\n        break;\n    case LIBSSH2_CALLBACK_DISCONNECT:\n        internal_handler = (void *) php_ssh2_disconnect_cb;\n        if (data->disconnect_cb) {\n            zval_ptr_dtor(data->disconnect_cb);\n        }\n        data->disconnect_cb = copyval;\n        break;\n    default:\n        zval_ptr_dtor(copyval);\n        return -1;\n    }\n\n    libssh2_session_callback_set(session, callback_type, internal_handler);\n\n    return 0;\n}\n/* }}} */\n\n/* {{{ php_ssh2_set_method\n * Try to set a method if it's passed in with the hash table\n */\nstatic int php_ssh2_set_method(\n    LIBSSH2_SESSION *session, HashTable *ht, const char *method, int method_len, int method_type) {\n    zval *value;\n    zend_string *method_zstring;\n\n    method_zstring = zend_string_init(method, method_len, 0);\n    if ((value = zend_hash_find(ht, method_zstring)) == NULL) {\n        zend_string_release(method_zstring);\n        return 0;\n    }\n    zend_string_release(method_zstring);\n\n    if ((Z_TYPE_P(value) != IS_STRING)) {\n        return -1;\n    }\n\n    return libssh2_session_method_pref(session, method_type, Z_STRVAL_P(value));\n}\n/* }}} */\n\n/* {{{ php_ssh2_session_connect\n * Connect to an SSH server with requested methods\n */\nLIBSSH2_SESSION *php_ssh2_session_connect(const char *host, int port, zval *methods, zval *callbacks) {\n    LIBSSH2_SESSION *session;\n    php_ssh2_session_data *data;\n    zend_string *hash_lookup_zstring;\n\n    int domain = swoole::network::Address::verify_ip(AF_INET6, host) ? AF_INET6 : AF_INET;\n\n    auto sock = new SocketImpl(domain, SOCK_STREAM, 0);\n    if (sock->get_fd() < 0 || !sock->connect(host, port)) {\n        php_error_docref(NULL, E_WARNING, \"Unable to connect to %s on port %d\", host, port);\n        delete sock;\n        return NULL;\n    }\n\n    data = (php_ssh2_session_data *) ecalloc(1, sizeof(php_ssh2_session_data));\n    data->socket = sock;\n\n    session = libssh2_session_init_ex(php_ssh2_alloc_cb, php_ssh2_free_cb, php_ssh2_realloc_cb, data);\n    if (!session) {\n        php_error_docref(NULL, E_WARNING, \"Unable to initialize SSH2 session\");\n        efree(data);\n        delete sock;\n        return NULL;\n    }\n\n    libssh2_banner_set(session, LIBSSH2_SSH_DEFAULT_BANNER \" swoole-\" SWOOLE_VERSION);\n    libssh2_session_set_blocking(session, 0);\n\n    /* Override method preferences */\n    if (methods) {\n        zval *container;\n\n        if (php_ssh2_set_method(session, HASH_OF(methods), \"kex\", sizeof(\"kex\") - 1, LIBSSH2_METHOD_KEX)) {\n            php_error_docref(NULL, E_WARNING, \"Failed overriding KEX method\");\n        }\n        if (php_ssh2_set_method(session, HASH_OF(methods), \"hostkey\", sizeof(\"hostkey\") - 1, LIBSSH2_METHOD_HOSTKEY)) {\n            php_error_docref(NULL, E_WARNING, \"Failed overriding HOSTKEY method\");\n        }\n\n        hash_lookup_zstring = zend_string_init(\"client_to_server\", sizeof(\"client_to_server\") - 1, 0);\n        if ((container = zend_hash_find(HASH_OF(methods), hash_lookup_zstring)) != NULL &&\n            Z_TYPE_P(container) == IS_ARRAY) {\n            if (php_ssh2_set_method(\n                    session, HASH_OF(container), \"crypt\", sizeof(\"crypt\") - 1, LIBSSH2_METHOD_CRYPT_CS)) {\n                php_error_docref(NULL, E_WARNING, \"Failed overriding client to server CRYPT method\");\n            }\n            if (php_ssh2_set_method(session, HASH_OF(container), \"mac\", sizeof(\"mac\") - 1, LIBSSH2_METHOD_MAC_CS)) {\n                php_error_docref(NULL, E_WARNING, \"Failed overriding client to server MAC method\");\n            }\n            if (php_ssh2_set_method(session, HASH_OF(container), \"comp\", sizeof(\"comp\") - 1, LIBSSH2_METHOD_COMP_CS)) {\n                php_error_docref(NULL, E_WARNING, \"Failed overriding client to server COMP method\");\n            }\n            if (php_ssh2_set_method(session, HASH_OF(container), \"lang\", sizeof(\"lang\") - 1, LIBSSH2_METHOD_LANG_CS)) {\n                php_error_docref(NULL, E_WARNING, \"Failed overriding client to server LANG method\");\n            }\n        }\n        zend_string_release(hash_lookup_zstring);\n\n        hash_lookup_zstring = zend_string_init(\"server_to_client\", sizeof(\"server_to_client\") - 1, 0);\n        if ((container = zend_hash_find(HASH_OF(methods), hash_lookup_zstring)) != NULL &&\n            Z_TYPE_P(container) == IS_ARRAY) {\n            if (php_ssh2_set_method(\n                    session, HASH_OF(container), \"crypt\", sizeof(\"crypt\") - 1, LIBSSH2_METHOD_CRYPT_SC)) {\n                php_error_docref(NULL, E_WARNING, \"Failed overriding server to client CRYPT method\");\n            }\n            if (php_ssh2_set_method(session, HASH_OF(container), \"mac\", sizeof(\"mac\") - 1, LIBSSH2_METHOD_MAC_SC)) {\n                php_error_docref(NULL, E_WARNING, \"Failed overriding server to client MAC method\");\n            }\n            if (php_ssh2_set_method(session, HASH_OF(container), \"comp\", sizeof(\"comp\") - 1, LIBSSH2_METHOD_COMP_SC)) {\n                php_error_docref(NULL, E_WARNING, \"Failed overriding server to client COMP method\");\n            }\n            if (php_ssh2_set_method(session, HASH_OF(container), \"lang\", sizeof(\"lang\") - 1, LIBSSH2_METHOD_LANG_SC)) {\n                php_error_docref(NULL, E_WARNING, \"Failed overriding server to client LANG method\");\n            }\n        }\n        zend_string_release(hash_lookup_zstring);\n    }\n\n    /* Register Callbacks */\n    if (callbacks) {\n        /* ignore debug disconnect macerror */\n\n        if (php_ssh2_set_callback(\n                session, HASH_OF(callbacks), \"ignore\", sizeof(\"ignore\") - 1, LIBSSH2_CALLBACK_IGNORE, data)) {\n            php_error_docref(NULL, E_WARNING, \"Failed setting IGNORE callback\");\n        }\n\n        if (php_ssh2_set_callback(\n                session, HASH_OF(callbacks), \"debug\", sizeof(\"debug\") - 1, LIBSSH2_CALLBACK_DEBUG, data)) {\n            php_error_docref(NULL, E_WARNING, \"Failed setting DEBUG callback\");\n        }\n\n        if (php_ssh2_set_callback(\n                session, HASH_OF(callbacks), \"macerror\", sizeof(\"macerror\") - 1, LIBSSH2_CALLBACK_MACERROR, data)) {\n            php_error_docref(NULL, E_WARNING, \"Failed setting MACERROR callback\");\n        }\n\n        if (php_ssh2_set_callback(session,\n                                  HASH_OF(callbacks),\n                                  \"disconnect\",\n                                  sizeof(\"disconnect\") - 1,\n                                  LIBSSH2_CALLBACK_DISCONNECT,\n                                  data)) {\n            php_error_docref(NULL, E_WARNING, \"Failed setting DISCONNECT callback\");\n        }\n    }\n\n    if (libssh2_session_handshake(session, sock->get_fd())) {\n        int last_error = 0;\n        char *error_msg = NULL;\n\n        last_error = libssh2_session_last_error(session, &error_msg, NULL, 0);\n        php_error_docref(NULL, E_WARNING, \"Error starting up SSH connection(%d): %s\", last_error, error_msg);\n        libssh2_session_free(session);\n        efree(data);\n        delete sock;\n        return NULL;\n    }\n\n    return session;\n}\n/* }}} */\n\n/* {{{ proto resource ssh2_connect(string host[, int port[, array methods[, array callbacks]]])\n * Establish a connection to a remote SSH server and return a resource on success, false on error\n */\nPHP_FUNCTION(ssh2_connect) {\n    LIBSSH2_SESSION *session;\n    zval *methods = NULL, *callbacks = NULL;\n    char *host;\n    zend_long port = PHP_SSH2_DEFAULT_PORT;\n    size_t host_len;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"s|la!a!\", &host, &host_len, &port, &methods, &callbacks) == FAILURE) {\n        return;\n    }\n\n    session = php_ssh2_session_connect(host, port, methods, callbacks);\n    if (!session) {\n        php_error_docref(NULL, E_WARNING, \"Unable to connect to %s\", host);\n        RETURN_FALSE;\n    }\n\n    RETURN_RES(zend_register_resource(session, le_ssh2_session));\n}\n/* }}} */\n\n/* {{{ proto resource ssh2_disconnect(resource session)\n * close a connection to a remote SSH server and return a true on success, false on error.\n */\nPHP_FUNCTION(ssh2_disconnect) {\n    zval *zsession;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"r\", &zsession) == FAILURE) {\n        RETURN_FALSE;\n    }\n\n    zend_list_close(Z_RES_P(zsession));\n\n    RETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ proto array ssh2_methods_negotiated(resource session)\n * Return list of negotiaed methods\n */\nPHP_FUNCTION(ssh2_methods_negotiated) {\n    LIBSSH2_SESSION *session;\n    zval *zsession, endpoint;\n    char *kex, *hostkey, *crypt_cs, *crypt_sc, *mac_cs, *mac_sc, *comp_cs, *comp_sc, *lang_cs, *lang_sc;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"r\", &zsession) == FAILURE) {\n        return;\n    }\n\n    if ((session = (LIBSSH2_SESSION *) zend_fetch_resource(\n             Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    kex = (char *) libssh2_session_methods(session, LIBSSH2_METHOD_KEX);\n    hostkey = (char *) libssh2_session_methods(session, LIBSSH2_METHOD_HOSTKEY);\n    crypt_cs = (char *) libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_CS);\n    crypt_sc = (char *) libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_SC);\n    mac_cs = (char *) libssh2_session_methods(session, LIBSSH2_METHOD_MAC_CS);\n    mac_sc = (char *) libssh2_session_methods(session, LIBSSH2_METHOD_MAC_SC);\n    comp_cs = (char *) libssh2_session_methods(session, LIBSSH2_METHOD_COMP_CS);\n    comp_sc = (char *) libssh2_session_methods(session, LIBSSH2_METHOD_COMP_SC);\n    lang_cs = (char *) libssh2_session_methods(session, LIBSSH2_METHOD_LANG_CS);\n    lang_sc = (char *) libssh2_session_methods(session, LIBSSH2_METHOD_LANG_SC);\n\n    array_init(return_value);\n    add_assoc_string(return_value, \"kex\", kex);\n    add_assoc_string(return_value, \"hostkey\", hostkey);\n\n    array_init(&endpoint);\n    add_assoc_string(&endpoint, \"crypt\", crypt_cs);\n    add_assoc_string(&endpoint, \"mac\", mac_cs);\n    add_assoc_string(&endpoint, \"comp\", comp_cs);\n    add_assoc_string(&endpoint, \"lang\", lang_cs);\n    add_assoc_zval(return_value, \"client_to_server\", &endpoint);\n\n    array_init(&endpoint);\n    add_assoc_string(&endpoint, \"crypt\", crypt_sc);\n    add_assoc_string(&endpoint, \"mac\", mac_sc);\n    add_assoc_string(&endpoint, \"comp\", comp_sc);\n    add_assoc_string(&endpoint, \"lang\", lang_sc);\n    add_assoc_zval(return_value, \"server_to_client\", &endpoint);\n}\n/* }}} */\n\n/* {{{ proto string ssh2_fingerprint(resource session[, int flags])\n * Returns a server hostkey hash from an active session\n * Defaults to MD5 fingerprint encoded as ASCII hex values\n */\nPHP_FUNCTION(ssh2_fingerprint) {\n    LIBSSH2_SESSION *session;\n    zval *zsession;\n    const char *fingerprint;\n    zend_long flags = 0;\n    size_t i, fingerprint_len;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"r|l\", &zsession, &flags) == FAILURE) {\n        return;\n    }\n    fingerprint_len = (flags & PHP_SSH2_FINGERPRINT_SHA1) ? SHA_DIGEST_LENGTH : MD5_DIGEST_LENGTH;\n\n    if ((session = (LIBSSH2_SESSION *) zend_fetch_resource(\n             Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    fingerprint = (char *) libssh2_hostkey_hash(\n        session, (flags & PHP_SSH2_FINGERPRINT_SHA1) ? LIBSSH2_HOSTKEY_HASH_SHA1 : LIBSSH2_HOSTKEY_HASH_MD5);\n    if (!fingerprint) {\n        php_error_docref(NULL, E_WARNING, \"Unable to retrieve fingerprint from specified session\");\n        RETURN_FALSE;\n    }\n\n    for (i = 0; i < fingerprint_len; i++) {\n        if (fingerprint[i] != '\\0') {\n            goto fingerprint_good;\n        }\n    }\n    php_error_docref(NULL, E_WARNING, \"No fingerprint available using specified hash\");\n    RETURN_NULL();\nfingerprint_good:\n    if (flags & PHP_SSH2_FINGERPRINT_RAW) {\n        RETURN_STRINGL(fingerprint, fingerprint_len);\n    } else {\n        char *hexchars;\n\n        hexchars = (char *) emalloc((fingerprint_len * 2) + 1);\n        for (i = 0; i < fingerprint_len; i++) {\n            snprintf(hexchars + (2 * i), 3, \"%02X\", (unsigned char) fingerprint[i]);\n        }\n        ZVAL_STRINGL(return_value, hexchars, 2 * fingerprint_len);\n        efree(hexchars);\n    }\n}\n/* }}} */\n\n/* {{{ proto array ssh2_auth_none(resource session, string username)\n * Attempt \"none\" authentication, returns a list of allowed methods on failed authentication,\n * false on utter failure, or true on success\n */\nPHP_FUNCTION(ssh2_auth_none) {\n    LIBSSH2_SESSION *session;\n    zval *zsession;\n    char *username, *methods, *s, *p;\n    size_t username_len;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rs\", &zsession, &username, &username_len) == FAILURE) {\n        return;\n    }\n\n    if ((session = (LIBSSH2_SESSION *) zend_fetch_resource(\n             Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    s = methods = libssh2_userauth_list(session, username, username_len);\n    if (!methods) {\n        /* Either bad failure, or unexpected success */\n        RETURN_BOOL(libssh2_userauth_authenticated(session));\n    }\n\n    array_init(return_value);\n    while ((p = strchr(s, ','))) {\n        if ((p - s) > 0) {\n            add_next_index_stringl(return_value, s, p - s);\n        }\n        s = p + 1;\n    }\n    if (strlen(s)) {\n        add_next_index_string(return_value, s);\n    }\n}\n/* }}} */\n\nchar *password_for_kbd_callback;\n\nstatic void kbd_callback(const char *name,\n                         int name_len,\n                         const char *instruction,\n                         int instruction_len,\n                         int num_prompts,\n                         const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,\n                         LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,\n                         void **abstract) {\n    (void) name;\n    (void) name_len;\n    (void) instruction;\n    (void) instruction_len;\n    if (num_prompts == 1) {\n        responses[0].text = estrdup(password_for_kbd_callback);\n        responses[0].length = strlen(password_for_kbd_callback);\n    }\n    (void) prompts;\n    (void) abstract;\n}\n\n/* {{{ proto bool ssh2_auth_password(resource session, string username, string password)\n * Authenticate over SSH using a plain password\n */\nPHP_FUNCTION(ssh2_auth_password) {\n    LIBSSH2_SESSION *session;\n    zval *zsession;\n    zend_string *username, *password;\n    char *userauthlist;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rSS\", &zsession, &username, &password) == FAILURE) {\n        return;\n    }\n\n    SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession);\n\n    userauthlist = libssh2_userauth_list(session, username->val, username->len);\n\n    if (userauthlist != NULL) {\n        password_for_kbd_callback = password->val;\n        if (strstr(userauthlist, \"keyboard-interactive\") != NULL) {\n            if (libssh2_userauth_keyboard_interactive(session, username->val, &kbd_callback) == 0) {\n                RETURN_TRUE;\n            }\n        }\n\n        /* TODO: Support password change callback */\n        if (libssh2_userauth_password_ex(session, username->val, username->len, password->val, password->len, NULL)) {\n            php_error_docref(NULL, E_WARNING, \"Authentication failed for %s using password\", username->val);\n            RETURN_FALSE;\n        }\n    }\n\n    RETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ proto bool ssh2_auth_pubkey_file(resource session, string username, string pubkeyfile, string privkeyfile[,\n * string passphrase]) Authenticate using a public key\n */\nPHP_FUNCTION(ssh2_auth_pubkey_file) {\n    LIBSSH2_SESSION *session;\n    zval *zsession;\n    zend_string *username, *pubkey, *privkey, *passphrase = nullptr;\n#ifndef PHP_WIN32\n    zend_string *newpath;\n    struct passwd *pws;\n#endif\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rSSS|S\", &zsession, &username, &pubkey, &privkey, &passphrase) ==\n        FAILURE) {\n        return;\n    }\n\n    if (php_check_open_basedir(ZSTR_VAL(pubkey)) || php_check_open_basedir(ZSTR_VAL(privkey))) {\n        RETURN_FALSE;\n    }\n\n    SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession);\n#ifndef PHP_WIN32\n    /* Explode '~/paths' stopgap fix because libssh2 does not accept tilde for homedir\n      This should be ifdef'ed when a fix is available to support older libssh2 versions*/\n    pws = getpwuid(geteuid());\n    if (ZSTR_LEN(pubkey) >= 2 && *(ZSTR_VAL(pubkey)) == '~' && *(ZSTR_VAL(pubkey) + 1) == '/') {\n        newpath = zend_string_alloc(strlen(pws->pw_dir) + ZSTR_LEN(pubkey), 0);\n        strcpy(ZSTR_VAL(newpath), pws->pw_dir);\n        strcat(ZSTR_VAL(newpath), ZSTR_VAL(pubkey) + 1);\n        zend_string_release(pubkey);\n        pubkey = newpath;\n    }\n    if (ZSTR_LEN(privkey) >= 2 && *(ZSTR_VAL(privkey)) == '~' && *(ZSTR_VAL(privkey) + 1) == '/') {\n        newpath = zend_string_alloc(strlen(pws->pw_dir) + ZSTR_LEN(privkey), 0);\n        strcpy(ZSTR_VAL(newpath), pws->pw_dir);\n        strcat(ZSTR_VAL(newpath), ZSTR_VAL(privkey) + 1);\n        zend_string_release(privkey);\n        privkey = newpath;\n    }\n#endif\n\n    auto passphrase_ptr = passphrase ? ZSTR_VAL(passphrase) : nullptr;\n\n    /* TODO: Support passphrase callback */\n    if (libssh2_userauth_publickey_fromfile_ex(\n            session, ZSTR_VAL(username), ZSTR_LEN(username), ZSTR_VAL(pubkey), ZSTR_VAL(privkey), passphrase_ptr)) {\n        char *buf;\n        int len;\n        libssh2_session_last_error(session, &buf, &len, 0);\n        php_error_docref(NULL, E_WARNING, \"Authentication failed for %s using public key: %s\", ZSTR_VAL(username), buf);\n        RETURN_FALSE;\n    }\n\n    RETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ proto bool ssh2_auth_pubkey(resource session, string username, string pubkey, string privkey[, string\n * passphrase]) Authenticate using a public key\n */\nPHP_FUNCTION(ssh2_auth_pubkey) {\n    LIBSSH2_SESSION *session;\n    zval *zsession;\n    zend_string *username, *pubkey, *privkey, *passphrase = nullptr;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rSSS|S\", &zsession, &username, &pubkey, &privkey, &passphrase) ==\n        FAILURE) {\n        return;\n    }\n\n    auto passphrase_ptr = passphrase ? ZSTR_VAL(passphrase) : nullptr;\n\n    SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession);\n\n    if (libssh2_userauth_publickey_frommemory(session,\n                                              ZSTR_VAL(username),\n                                              ZSTR_LEN(username),\n                                              ZSTR_VAL(pubkey),\n                                              ZSTR_LEN(pubkey),\n                                              ZSTR_VAL(privkey),\n                                              ZSTR_LEN(privkey),\n                                              passphrase_ptr)) {\n        char *buf;\n        int len;\n        libssh2_session_last_error(session, &buf, &len, 0);\n        php_error_docref(NULL, E_WARNING, \"Authentication failed for %s using public key: %s\", ZSTR_VAL(username), buf);\n        RETURN_FALSE;\n    }\n\n    RETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ proto bool ssh2_auth_hostbased_file(resource session, string username, string hostname, string pubkeyfile, string\n * privkeyfile[, string passphrase[, string local_username]]) Authenticate using a hostkey\n */\nPHP_FUNCTION(ssh2_auth_hostbased_file) {\n    LIBSSH2_SESSION *session;\n    zval *zsession;\n    char *username, *hostname, *pubkey, *privkey, *passphrase = NULL, *local_username = NULL;\n    size_t username_len, hostname_len, pubkey_len, privkey_len, passphrase_len, local_username_len;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(),\n                              \"rssss|s!s!\",\n                              &zsession,\n                              &username,\n                              &username_len,\n                              &hostname,\n                              &hostname_len,\n                              &pubkey,\n                              &pubkey_len,\n                              &privkey,\n                              &privkey_len,\n                              &passphrase,\n                              &passphrase_len,\n                              &local_username,\n                              &local_username_len) == FAILURE) {\n        return;\n    }\n\n    if (php_check_open_basedir(pubkey) || php_check_open_basedir(privkey)) {\n        RETURN_FALSE;\n    }\n\n    SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession);\n\n    if (!local_username) {\n        local_username = username;\n        local_username_len = username_len;\n    }\n\n    /* TODO: Support passphrase callback */\n    if (libssh2_userauth_hostbased_fromfile_ex(session,\n                                               username,\n                                               username_len,\n                                               pubkey,\n                                               privkey,\n                                               passphrase,\n                                               hostname,\n                                               hostname_len,\n                                               local_username,\n                                               local_username_len)) {\n        php_error_docref(NULL, E_WARNING, \"Authentication failed for %s using hostbased public key\", username);\n        RETURN_FALSE;\n    }\n\n    RETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ proto resource ssh2_forward_listen(resource session, int port[, string host[, long max_connections]])\n * Bind a port on the remote server and listen for connections\n */\nPHP_FUNCTION(ssh2_forward_listen) {\n    zval *zsession;\n    LIBSSH2_SESSION *session;\n    LIBSSH2_LISTENER *listener;\n    php_ssh2_listener_data *data;\n    zend_long port;\n    char *host = NULL;\n    size_t host_len;\n    zend_long max_connections = PHP_SSH2_LISTEN_MAX_QUEUED;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rl|sl\", &zsession, &port, &host, &host_len, &max_connections) ==\n        FAILURE) {\n        return;\n    }\n\n    SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);\n\n    listener = libssh2_channel_forward_listen_ex(session, host, port, NULL, max_connections);\n\n    if (!listener) {\n        php_error_docref(NULL, E_WARNING, \"Failure listening on remote port\");\n        RETURN_FALSE;\n    }\n\n    data = (php_ssh2_listener_data *) emalloc(sizeof(php_ssh2_listener_data));\n    data->session = session;\n    data->session_rsrc = Z_RES_P(zsession);\n    Z_ADDREF_P(zsession);\n    data->listener = listener;\n\n    RETURN_RES(zend_register_resource(data, le_ssh2_listener));\n}\n/* }}} */\n\n/* {{{ proto stream ssh2_forward_accept(resource listener[, string &shost[, long &sport]])\n * Accept a connection created by a listener\n */\nPHP_FUNCTION(ssh2_forward_accept) {\n    zval *zlistener;\n    php_ssh2_listener_data *data;\n    LIBSSH2_CHANNEL *channel;\n    php_ssh2_channel_data *channel_data;\n    php_stream *stream;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"r\", &zlistener) == FAILURE) {\n        return;\n    }\n\n    if ((data = (php_ssh2_listener_data *) zend_fetch_resource(\n             Z_RES_P(zlistener), PHP_SSH2_LISTENER_RES_NAME, le_ssh2_listener)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    auto session = data->session;\n    channel = libssh2_channel_forward_accept(data->listener);\n\n    if (!channel) {\n        RETURN_FALSE;\n    }\n\n    channel_data = (php_ssh2_channel_data *) emalloc(sizeof(php_ssh2_channel_data));\n    channel_data->channel = channel;\n    channel_data->streamid = 0;\n    channel_data->is_blocking = 0;\n    channel_data->session_rsrc = data->session_rsrc;\n    channel_data->refcount = NULL;\n\n    stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, \"r+\");\n    if (!stream) {\n        php_error_docref(NULL, E_WARNING, \"Failure allocating stream\");\n        efree(channel_data);\n        libssh2_channel_free(channel);\n        RETURN_FALSE;\n    }\n\n    GC_ADDREF(channel_data->session_rsrc);\n\n    php_stream_to_zval(stream, return_value);\n}\n/* }}} */\n\n/* ***********************\n * Publickey Subsystem *\n *********************** */\n\n/* {{{ proto resource ssh2_publickey_init(resource connection)\nInitialize the publickey subsystem */\nPHP_FUNCTION(ssh2_publickey_init) {\n    zval *zsession;\n    LIBSSH2_SESSION *session;\n    LIBSSH2_PUBLICKEY *pkey;\n    php_ssh2_pkey_subsys_data *data;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"r\", &zsession) == FAILURE) {\n        return;\n    }\n\n    SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);\n\n    pkey = libssh2_publickey_init(session);\n\n    if (!pkey) {\n        int last_error = 0;\n        char *error_msg = NULL;\n\n        last_error = libssh2_session_last_error(session, &error_msg, NULL, 0);\n        php_error_docref(NULL, E_WARNING, \"Unable to initialize publickey subsystem(%d) %s\", last_error, error_msg);\n        RETURN_FALSE;\n    }\n\n    data = (php_ssh2_pkey_subsys_data *) emalloc(sizeof(php_ssh2_pkey_subsys_data));\n    data->session = session;\n    data->session_rsrc = Z_RES_P(zsession);\n    Z_ADDREF_P(zsession);\n    data->pkey = pkey;\n\n    RETURN_RES(zend_register_resource(data, le_ssh2_pkey_subsys));\n}\n/* }}} */\n\n/* {{{ proto bool ssh2_publickey_add(resource pkey, string algoname, string blob[, bool overwrite=FALSE [,array\nattributes=NULL]]) Add an additional publickey */\nPHP_FUNCTION(ssh2_publickey_add) {\n    zval *zpkey_data, *zattrs = NULL;\n    php_ssh2_pkey_subsys_data *data;\n    char *algo, *blob;\n    size_t algo_len, blob_len;\n    unsigned long num_attrs = 0;\n    libssh2_publickey_attribute *attrs = NULL;\n    zend_bool overwrite = 0;\n\n    if (zend_parse_parameters(\n            ZEND_NUM_ARGS(), \"rss|ba\", &zpkey_data, &algo, &algo_len, &blob, &blob_len, &overwrite, &zattrs) ==\n        FAILURE) {\n        return;\n    }\n\n    if ((data = (php_ssh2_pkey_subsys_data *) zend_fetch_resource(\n             Z_RES_P(zpkey_data), PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    if (zattrs) {\n        HashPosition pos;\n        zval *attr_val;\n        unsigned long current_attr = 0;\n\n        num_attrs = zend_hash_num_elements(Z_ARRVAL_P(zattrs));\n        attrs = (libssh2_publickey_attribute *) safe_emalloc(num_attrs, sizeof(libssh2_publickey_attribute), 0);\n\n        for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(zattrs), &pos);\n             (attr_val = zend_hash_get_current_data_ex(Z_ARRVAL_P(zattrs), &pos)) != NULL;\n             zend_hash_move_forward_ex(Z_ARRVAL_P(zattrs), &pos)) {\n            zend_string *key;\n            int type;\n            zend_ulong idx;\n            zval copyval = *attr_val;\n\n            type = zend_hash_get_current_key_ex(Z_ARRVAL_P(zattrs), &key, &idx, &pos);\n            if (type == HASH_KEY_NON_EXISTENT) {\n                /* All but impossible */\n                break;\n            }\n            if (type == HASH_KEY_IS_LONG) {\n                /* Malformed, ignore */\n                php_error_docref(NULL, E_WARNING, \"Malformed attirbute array, contains numeric index\");\n                num_attrs--;\n                continue;\n            }\n\n            if (!key || (key->len == 1 && key->val[0] == '*')) {\n                /* Empty key, ignore */\n                php_error_docref(NULL, E_WARNING, \"Empty attribute key\");\n                num_attrs--;\n                continue;\n            }\n\n            zval_copy_ctor(&copyval);\n            // TODO Sean-Der\n            // Z_UNSET_ISREF_P(&copyval);\n            // Z_SET_REFCOUNT_P(&copyval, 1);\n            convert_to_string(&copyval);\n\n            if (key->val[0] == '*') {\n                attrs[current_attr].mandatory = 1;\n                attrs[current_attr].name = key->val + 1;\n                attrs[current_attr].name_len = key->len - 2;\n            } else {\n                attrs[current_attr].mandatory = 0;\n                attrs[current_attr].name = key->val;\n                attrs[current_attr].name_len = key->len - 1;\n            }\n            attrs[current_attr].value_len = Z_STRLEN(copyval);\n            attrs[current_attr].value = Z_STRVAL(copyval);\n\n            /* copyval deliberately not dtor'd, we're stealing the string */\n            current_attr++;\n        }\n    }\n\n    if (libssh2_publickey_add_ex(data->pkey,\n                                 (unsigned char *) algo,\n                                 algo_len,\n                                 (unsigned char *) blob,\n                                 blob_len,\n                                 overwrite,\n                                 num_attrs,\n                                 attrs)) {\n        php_error_docref(NULL, E_WARNING, \"Unable to add %s key\", algo);\n        RETVAL_FALSE;\n    } else {\n        RETVAL_TRUE;\n    }\n\n    if (attrs) {\n        unsigned long i;\n\n        for (i = 0; i < num_attrs; i++) {\n            /* name doesn't need freeing */\n            // TODO Sean-Der\n            // efree(attrs[i].value);\n        }\n        efree(attrs);\n    }\n}\n/* }}} */\n\n/* {{{ proto bool ssh2_publickey_remove(resource pkey, string algoname, string blob)\nRemove a publickey entry */\nPHP_FUNCTION(ssh2_publickey_remove) {\n    zval *zpkey_data;\n    php_ssh2_pkey_subsys_data *data;\n    char *algo, *blob;\n    size_t algo_len, blob_len;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rss\", &zpkey_data, &algo, &algo_len, &blob, &blob_len) == FAILURE) {\n        return;\n    }\n\n    if ((data = (php_ssh2_pkey_subsys_data *) zend_fetch_resource(\n             Z_RES_P(zpkey_data), PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    if (libssh2_publickey_remove_ex(data->pkey, (unsigned char *) algo, algo_len, (unsigned char *) blob, blob_len)) {\n        php_error_docref(NULL, E_WARNING, \"Unable to remove %s key\", algo);\n        RETURN_FALSE;\n    }\n\n    RETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ proto array ssh2_publickey_list(resource pkey)\nList currently installed publickey entries */\nPHP_FUNCTION(ssh2_publickey_list) {\n    zval *zpkey_data;\n    php_ssh2_pkey_subsys_data *data;\n    unsigned long num_keys, i;\n    libssh2_publickey_list *keys;\n    zend_string *hash_key_zstring;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"r\", &zpkey_data) == FAILURE) {\n        return;\n    }\n\n    if ((data = (php_ssh2_pkey_subsys_data *) zend_fetch_resource(\n             Z_RES_P(zpkey_data), PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    if (libssh2_publickey_list_fetch(data->pkey, &num_keys, &keys)) {\n        php_error_docref(NULL, E_WARNING, \"Unable to list keys on remote server\");\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n    for (i = 0; i < num_keys; i++) {\n        zval key, attrs;\n        unsigned long j;\n\n        array_init(&key);\n\n        add_assoc_stringl(&key, \"name\", (char *) keys[i].name, keys[i].name_len);\n        add_assoc_stringl(&key, \"blob\", (char *) keys[i].blob, keys[i].blob_len);\n\n        array_init(&attrs);\n        for (j = 0; j < keys[i].num_attrs; j++) {\n            zval attr;\n\n            ZVAL_STRINGL(&attr, keys[i].attrs[j].value, keys[i].attrs[j].value_len);\n            hash_key_zstring = zend_string_init(keys[i].attrs[j].name, keys[i].attrs[j].name_len, 0);\n            zend_hash_add(Z_ARRVAL_P(&attrs), hash_key_zstring, &attr);\n            zend_string_release(hash_key_zstring);\n        }\n        add_assoc_zval(&key, \"attrs\", &attrs);\n\n        add_next_index_zval(return_value, &key);\n    }\n\n    libssh2_publickey_list_free(data->pkey, keys);\n}\n/* }}} */\n\n/* {{{ proto array ssh2_auth_agent(resource session, string username)\nAuthenticate using the ssh agent */\nPHP_FUNCTION(ssh2_auth_agent) {\n#ifdef PHP_SSH2_AGENT_AUTH\n    zval *zsession;\n    char *username;\n    size_t username_len;\n\n    LIBSSH2_SESSION *session;\n    char *userauthlist;\n    LIBSSH2_AGENT *agent = NULL;\n    int rc;\n    struct libssh2_agent_publickey *identity, *prev_identity = NULL;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rs\", &zsession, &username, &username_len) == FAILURE) {\n        return;\n    }\n\n    SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession);\n\n    /* check what authentication methods are available */\n    userauthlist = libssh2_userauth_list(session, username, username_len);\n\n    if (userauthlist != NULL && strstr(userauthlist, \"publickey\") == NULL) {\n        php_error_docref(NULL, E_WARNING, \"\\\"publickey\\\" authentication is not supported\");\n        RETURN_FALSE;\n    }\n\n    /* Connect to the ssh-agent */\n    agent = libssh2_agent_init(session);\n\n    if (!agent) {\n        php_error_docref(NULL, E_WARNING, \"Failure initializing ssh-agent support\");\n        RETURN_FALSE;\n    }\n\n    if (libssh2_agent_connect(agent)) {\n        php_error_docref(NULL, E_WARNING, \"Failure connecting to ssh-agent\");\n        libssh2_agent_free(agent);\n        RETURN_FALSE;\n    }\n\n    if (libssh2_agent_list_identities(agent)) {\n        php_error_docref(NULL, E_WARNING, \"Failure requesting identities to ssh-agent\");\n        libssh2_agent_disconnect(agent);\n        libssh2_agent_free(agent);\n        RETURN_FALSE;\n    }\n\n    while (1) {\n        rc = libssh2_agent_get_identity(agent, &identity, prev_identity);\n\n        if (rc == 1) {\n            php_error_docref(NULL, E_WARNING, \"Couldn't continue authentication\");\n            libssh2_agent_disconnect(agent);\n            libssh2_agent_free(agent);\n            RETURN_FALSE;\n        }\n\n        if (rc < 0) {\n            php_error_docref(NULL, E_WARNING, \"Failure obtaining identity from ssh-agent support\");\n            libssh2_agent_disconnect(agent);\n            libssh2_agent_free(agent);\n            RETURN_FALSE;\n        }\n\n        if (!libssh2_agent_userauth(agent, username, identity)) {\n            libssh2_agent_disconnect(agent);\n            libssh2_agent_free(agent);\n            RETURN_TRUE;\n        }\n        prev_identity = identity;\n    }\n#else\n    php_error_docref(\n        NULL,\n        E_WARNING,\n        \"Upgrade the libssh2 library (needs 1.2.3 or higher) and reinstall the ssh2 extension for ssh2 agent support\");\n    RETURN_FALSE;\n#endif /* PHP_SSH2_AGENT_AUTH */\n}\n/* }}} */\n\n/* ***********************\n * Module Housekeeping *\n *********************** */\n\nstatic void php_ssh2_session_dtor(zend_resource *rsrc) {\n    LIBSSH2_SESSION *session = (LIBSSH2_SESSION *) rsrc->ptr;\n    php_ssh2_session_data **data = (php_ssh2_session_data **) libssh2_session_abstract(session);\n\n    libssh2_session_disconnect(session, \"swoole_ssh2 (https://github.com/swoole/swoole-src)\");\n\n    if (*data) {\n        if ((*data)->ignore_cb) {\n            zval_ptr_dtor((*data)->ignore_cb);\n        }\n        if ((*data)->debug_cb) {\n            zval_ptr_dtor((*data)->debug_cb);\n        }\n        if ((*data)->macerror_cb) {\n            zval_ptr_dtor((*data)->macerror_cb);\n        }\n        if ((*data)->disconnect_cb) {\n            zval_ptr_dtor((*data)->disconnect_cb);\n        }\n\n        delete (*data)->socket;\n        efree(*data);\n        *data = NULL;\n    }\n\n    libssh2_session_free(session);\n}\n\nstatic void php_ssh2_listener_dtor(zend_resource *rsrc) {\n    php_ssh2_listener_data *data = (php_ssh2_listener_data *) rsrc->ptr;\n    LIBSSH2_LISTENER *listener = data->listener;\n    auto session = data->session;\n\n    libssh2_channel_forward_cancel(listener);\n    zend_list_delete(data->session_rsrc);\n    efree(data);\n}\n\nstatic void php_ssh2_pkey_subsys_dtor(zend_resource *rsrc) {\n    php_ssh2_pkey_subsys_data *data = (php_ssh2_pkey_subsys_data *) rsrc->ptr;\n    LIBSSH2_PUBLICKEY *pkey = data->pkey;\n\n    libssh2_publickey_shutdown(pkey);\n    zend_list_delete(data->session_rsrc);\n    efree(data);\n}\n\n/* {{{ PHP_MINIT_FUNCTION\n */\nint php_swoole_ssh2_minit(int module_number) {\n    le_ssh2_session =\n        zend_register_list_destructors_ex(php_ssh2_session_dtor, NULL, PHP_SSH2_SESSION_RES_NAME, module_number);\n    le_ssh2_listener =\n        zend_register_list_destructors_ex(php_ssh2_listener_dtor, NULL, PHP_SSH2_LISTENER_RES_NAME, module_number);\n    le_ssh2_sftp = zend_register_list_destructors_ex(php_ssh2_sftp_dtor, NULL, PHP_SSH2_SFTP_RES_NAME, module_number);\n    le_ssh2_pkey_subsys = zend_register_list_destructors_ex(\n        php_ssh2_pkey_subsys_dtor, NULL, PHP_SSH2_PKEY_SUBSYS_RES_NAME, module_number);\n\n    REGISTER_LONG_CONSTANT(\"SSH2_FINGERPRINT_MD5\", PHP_SSH2_FINGERPRINT_MD5, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SSH2_FINGERPRINT_SHA1\", PHP_SSH2_FINGERPRINT_SHA1, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SSH2_FINGERPRINT_HEX\", PHP_SSH2_FINGERPRINT_HEX, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SSH2_FINGERPRINT_RAW\", PHP_SSH2_FINGERPRINT_RAW, CONST_CS | CONST_PERSISTENT);\n\n    REGISTER_LONG_CONSTANT(\"SSH2_TERM_UNIT_CHARS\", PHP_SSH2_TERM_UNIT_CHARS, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SSH2_TERM_UNIT_PIXELS\", PHP_SSH2_TERM_UNIT_PIXELS, CONST_CS | CONST_PERSISTENT);\n\n    REGISTER_STRING_CONSTANT(\"SSH2_DEFAULT_TERMINAL\", PHP_SSH2_DEFAULT_TERMINAL, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SSH2_DEFAULT_TERM_WIDTH\", PHP_SSH2_DEFAULT_TERM_WIDTH, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SSH2_DEFAULT_TERM_HEIGHT\", PHP_SSH2_DEFAULT_TERM_HEIGHT, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SSH2_DEFAULT_TERM_UNIT\", PHP_SSH2_DEFAULT_TERM_UNIT, CONST_CS | CONST_PERSISTENT);\n\n    REGISTER_LONG_CONSTANT(\"SSH2_STREAM_STDIO\", 0, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SSH2_STREAM_STDERR\", SSH_EXTENDED_DATA_STDERR, CONST_CS | CONST_PERSISTENT);\n\n    /* events/revents */\n    REGISTER_LONG_CONSTANT(\"SSH2_POLLIN\", LIBSSH2_POLLFD_POLLIN, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SSH2_POLLEXT\", LIBSSH2_POLLFD_POLLEXT, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SSH2_POLLOUT\", LIBSSH2_POLLFD_POLLOUT, CONST_CS | CONST_PERSISTENT);\n\n    /* revents only */\n    REGISTER_LONG_CONSTANT(\"SSH2_POLLERR\", LIBSSH2_POLLFD_POLLERR, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SSH2_POLLHUP\", LIBSSH2_POLLFD_POLLHUP, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SSH2_POLLNVAL\", LIBSSH2_POLLFD_POLLNVAL, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SSH2_POLL_SESSION_CLOSED\", LIBSSH2_POLLFD_SESSION_CLOSED, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SSH2_POLL_CHANNEL_CLOSED\", LIBSSH2_POLLFD_CHANNEL_CLOSED, CONST_CS | CONST_PERSISTENT);\n    REGISTER_LONG_CONSTANT(\"SSH2_POLL_LISTENER_CLOSED\", LIBSSH2_POLLFD_LISTENER_CLOSED, CONST_CS | CONST_PERSISTENT);\n\n    return (php_register_url_stream_wrapper(\"ssh2.shell\", &php_ssh2_stream_wrapper_shell) == SUCCESS &&\n            php_register_url_stream_wrapper(\"ssh2.exec\", &php_ssh2_stream_wrapper_exec) == SUCCESS &&\n            php_register_url_stream_wrapper(\"ssh2.tunnel\", &php_ssh2_stream_wrapper_tunnel) == SUCCESS &&\n            php_register_url_stream_wrapper(\"ssh2.scp\", &php_ssh2_stream_wrapper_scp) == SUCCESS &&\n            php_register_url_stream_wrapper(\"ssh2.sftp\", &php_ssh2_sftp_wrapper) == SUCCESS)\n               ? SUCCESS\n               : FAILURE;\n}\n/* }}} */\n\nint php_swoole_ssh2_mshutdown() {\n    return (php_unregister_url_stream_wrapper(\"ssh2.shell\") == SUCCESS &&\n            php_unregister_url_stream_wrapper(\"ssh2.exec\") == SUCCESS &&\n            php_unregister_url_stream_wrapper(\"ssh2.tunnel\") == SUCCESS &&\n            php_unregister_url_stream_wrapper(\"ssh2.scp\") == SUCCESS &&\n            php_unregister_url_stream_wrapper(\"ssh2.sftp\") == SUCCESS)\n               ? SUCCESS\n               : FAILURE;\n}\n\nvoid php_swoole_ssh2_minfo() {\n    php_info_print_table_row(2, \"SSH2 support\", \"enabled\");\n    php_info_print_table_row(2, \"libssh2 banner\", LIBSSH2_SSH_BANNER);\n}\n"
  },
  {
    "path": "thirdparty/php/ssh2/ssh2_fopen_wrappers.cc",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | PHP Version 7                                                        |\n   +----------------------------------------------------------------------+\n   | Copyright (c) 1997-2016 The PHP Group                                |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | http://www.php.net/license/3_01.txt                                  |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Author: Sara Golemon <pollita@php.net>                               |\n   +----------------------------------------------------------------------+\n*/\n\n#include \"php_ssh2.h\"\n#include \"php_swoole_ssh2_hook.h\"\n\nvoid *php_ssh2_zval_from_resource_handle(int handle) {\n    zval *val;\n    zend_resource *zr;\n    ZEND_HASH_FOREACH_VAL(&EG(regular_list), val) {\n        zr = Z_RES_P(val);\n        if (zr->handle == handle) {\n            return val;\n        }\n    }\n    ZEND_HASH_FOREACH_END();\n    return NULL;\n}\n\n/* **********************\n * channel_stream_ops *\n ********************** */\n\nstatic ssize_t php_ssh2_channel_stream_write(php_stream *stream, const char *buf, size_t count) {\n    php_ssh2_channel_data *abstract = (php_ssh2_channel_data *) stream->abstract;\n    ssize_t writestate;\n    LIBSSH2_SESSION *session;\n\n    session =\n        (LIBSSH2_SESSION *) zend_fetch_resource(abstract->session_rsrc, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);\n\n#ifdef PHP_SSH2_SESSION_TIMEOUT\n    if (abstract->is_blocking) {\n        ssh2_set_socket_timeout(session, abstract->timeout);\n    }\n#endif\n\n    writestate = libssh2_channel_write_ex(abstract->channel, abstract->streamid, buf, count);\n\n#ifdef PHP_SSH2_SESSION_TIMEOUT\n    if (abstract->is_blocking) {\n        ssh2_set_socket_timeout(session, -1);\n    }\n#endif\n\n    if (writestate < 0) {\n        char *error_msg = NULL;\n        if (libssh2_session_last_error(session, &error_msg, NULL, 0) == writestate) {\n            php_error_docref(NULL, E_WARNING, \"Failure '%s' (%ld)\", error_msg, writestate);\n        }\n\n        stream->eof = 1;\n    }\n\n    return writestate;\n}\n\nstatic ssize_t php_ssh2_channel_stream_read(php_stream *stream, char *buf, size_t count) {\n    php_ssh2_channel_data *abstract = (php_ssh2_channel_data *) stream->abstract;\n    ssize_t readstate;\n    auto session = ssh2_get_session(abstract);\n\n    stream->eof = libssh2_channel_eof(abstract->channel);\n\n#ifdef PHP_SSH2_SESSION_TIMEOUT\n    if (abstract->is_blocking) {\n        ssh2_set_socket_timeout(session, abstract->timeout);\n    }\n#endif\n\n    readstate = libssh2_channel_read_ex(abstract->channel, abstract->streamid, buf, count);\n\n#ifdef PHP_SSH2_SESSION_TIMEOUT\n    if (abstract->is_blocking) {\n        ssh2_set_socket_timeout(session, -1);\n    }\n#endif\n\n    if (readstate < 0) {\n        char *error_msg = NULL;\n        if (libssh2_session_last_error(session, &error_msg, NULL, 0) == readstate) {\n            php_error_docref(NULL, E_WARNING, \"Failure '%s' (%ld)\", error_msg, readstate);\n        }\n\n        stream->eof = 1;\n        readstate = 0;\n    }\n    return readstate;\n}\n\nstatic int php_ssh2_channel_stream_close(php_stream *stream, int close_handle) {\n    php_ssh2_channel_data *abstract = (php_ssh2_channel_data *) stream->abstract;\n\n    if (!abstract->refcount || (--(*(abstract->refcount)) == 0)) {\n        /* Last one out, turn off the lights */\n        if (abstract->refcount) {\n            efree(abstract->refcount);\n        }\n        auto session = ssh2_get_session(abstract);\n        libssh2_channel_eof(abstract->channel);\n        libssh2_channel_free(abstract->channel);\n        zend_list_delete(abstract->session_rsrc);\n    }\n    efree(abstract);\n\n    return 0;\n}\n\nstatic int php_ssh2_channel_stream_flush(php_stream *stream) {\n    php_ssh2_channel_data *abstract = (php_ssh2_channel_data *) stream->abstract;\n    auto session = ssh2_get_session(abstract);\n\n    return libssh2_channel_flush_ex(abstract->channel, abstract->streamid);\n}\n\nstatic int php_ssh2_channel_stream_cast(php_stream *stream, int castas, void **ret) {\n    php_ssh2_channel_data *abstract = (php_ssh2_channel_data *) stream->abstract;\n    LIBSSH2_SESSION *session;\n    php_ssh2_session_data **session_data;\n\n    session =\n        (LIBSSH2_SESSION *) zend_fetch_resource(abstract->session_rsrc, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);\n    session_data = (php_ssh2_session_data **) libssh2_session_abstract(session);\n\n    switch (castas) {\n    case PHP_STREAM_AS_FD:\n    case PHP_STREAM_AS_FD_FOR_SELECT:\n    case PHP_STREAM_AS_SOCKETD:\n        if (ret) {\n            *(php_socket_t *) ret = (*session_data)->socket->get_fd();\n        }\n        return SUCCESS;\n    default:\n        return FAILURE;\n    }\n}\n\nstatic int php_ssh2_channel_stream_set_option(php_stream *stream, int option, int value, void *ptrparam) {\n    php_ssh2_channel_data *abstract = (php_ssh2_channel_data *) stream->abstract;\n    auto session = ssh2_get_session(abstract);\n    int ret;\n\n    switch (option) {\n    case PHP_STREAM_OPTION_BLOCKING: {\n        ret = abstract->is_blocking;\n        abstract->is_blocking = value;\n        return ret;\n    }\n    case PHP_STREAM_OPTION_META_DATA_API: {\n        add_assoc_long((zval *) ptrparam, \"exit_status\", libssh2_channel_get_exit_status(abstract->channel));\n        break;\n    }\n    case PHP_STREAM_OPTION_READ_TIMEOUT: {\n        ret = abstract->timeout;\n#ifdef PHP_SSH2_SESSION_TIMEOUT\n        struct timeval tv = *(struct timeval *) ptrparam;\n        abstract->timeout = tv.tv_sec * 1000 + (tv.tv_usec / 1000);\n#else\n        php_error_docref(NULL, E_WARNING, \"No support for ssh2 stream timeout. Please recompile with libssh2 >= 1.2.9\");\n#endif\n        return ret;\n    }\n    case PHP_STREAM_OPTION_CHECK_LIVENESS: {\n        return stream->eof = libssh2_channel_eof(abstract->channel);\n    }\n    }\n\n    return -1;\n}\n\nphp_stream_ops php_ssh2_channel_stream_ops = {\n    php_ssh2_channel_stream_write,\n    php_ssh2_channel_stream_read,\n    php_ssh2_channel_stream_close,\n    php_ssh2_channel_stream_flush,\n    PHP_SSH2_CHANNEL_STREAM_NAME,\n    NULL, /* seek */\n    php_ssh2_channel_stream_cast,\n    NULL, /* stat */\n    php_ssh2_channel_stream_set_option,\n};\n\n/* *********************\n * Magic Path Helper *\n ********************* */\n\n/* {{{ php_ssh2_fopen_wraper_parse_path\n * Parse an ssh2.*:// path\n */\nphp_url *php_ssh2_fopen_wraper_parse_path(const char *path,\n                                          const char *type,\n                                          php_stream_context *context,\n                                          LIBSSH2_SESSION **psession,\n                                          zend_resource **presource,\n                                          LIBSSH2_SFTP **psftp,\n                                          zend_resource **psftp_rsrc) {\n    php_ssh2_sftp_data *sftp_data = NULL;\n    LIBSSH2_SESSION *session;\n    php_url *resource;\n    zval *methods = NULL, *callbacks = NULL, zsession, *tmpzval;\n    zend_long resource_id;\n    const char *h;\n    char *username = NULL, *password = NULL, *pubkey_file = NULL, *privkey_file = NULL;\n    int username_len = 0, password_len = 0;\n\n    h = strstr(path, \"Resource id #\");\n    if (h) {\n        /* Starting with 5.6.28, 7.0.13 need to be clean, else php_url_parse will fail */\n        char *tmp = estrdup(path);\n\n        strncpy(tmp + (h - path), h + sizeof(\"Resource id #\") - 1, strlen(tmp) - sizeof(\"Resource id #\"));\n        resource = php_url_parse(tmp);\n        efree(tmp);\n    } else {\n        resource = php_url_parse(path);\n    }\n    if (!resource || !resource->path) {\n        return NULL;\n    }\n\n    if (strncmp(ZSTR_VAL(resource->scheme), \"ssh2.\", sizeof(\"ssh2.\") - 1)) {\n        /* Not an ssh wrapper */\n        php_url_free(resource);\n        return NULL;\n    }\n\n    if (strcmp(ZSTR_VAL(resource->scheme) + sizeof(\"ssh2.\") - 1, type)) {\n        /* Wrong ssh2. wrapper type */\n        php_url_free(resource);\n        return NULL;\n    }\n\n    if (!resource->host) {\n        return NULL;\n    }\n\n    /*\n     * Find resource->path in the path string, then copy the entire string from the original path.\n     * This includes ?query#fragment in the path string\n     */\n\n    /* Look for a resource ID to reuse a session */\n    if (is_numeric_string(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), &resource_id, NULL, 0) == IS_LONG) {\n        php_ssh2_sftp_data *sftp_data;\n        zval *zresource;\n\n        if ((zresource = (zval *) php_ssh2_zval_from_resource_handle(resource_id)) == NULL) {\n            php_url_free(resource);\n            return NULL;\n        }\n\n        if (psftp) {\n            /* suppress potential warning by passing NULL as resource_type_name */\n            sftp_data = (php_ssh2_sftp_data *) zend_fetch_resource(Z_RES_P(zresource), NULL, le_ssh2_sftp);\n            if (sftp_data) {\n                /* Want the sftp layer */\n                Z_ADDREF_P(zresource);\n                *psftp_rsrc = Z_RES_P(zresource);\n                *psftp = sftp_data->sftp;\n                *presource = sftp_data->session_rsrc;\n                *psession = sftp_data->session;\n                return resource;\n            }\n        }\n        session =\n            (LIBSSH2_SESSION *) zend_fetch_resource(Z_RES_P(zresource), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);\n        if (session) {\n            if (psftp) {\n                /* We need an sftp layer too */\n                LIBSSH2_SFTP *sftp = libssh2_sftp_init(session);\n\n                if (!sftp) {\n                    php_url_free(resource);\n                    return NULL;\n                }\n                sftp_data = (php_ssh2_sftp_data *) emalloc(sizeof(php_ssh2_sftp_data));\n                sftp_data->sftp = sftp;\n                sftp_data->session = session;\n                sftp_data->session_rsrc = Z_RES_P(zresource);\n                Z_ADDREF_P(zresource);\n                *psftp_rsrc = zend_register_resource(sftp_data, le_ssh2_sftp);\n                *psftp = sftp;\n                *presource = Z_RES_P(zresource);\n                *psession = session;\n                return resource;\n            }\n            Z_ADDREF_P(zresource);\n            *presource = Z_RES_P(zresource);\n            *psession = session;\n            return resource;\n        }\n    }\n\n    /* Fallback on finding it in the context */\n    if (ZSTR_VAL(resource->host)[0] == 0 && context && psftp &&\n        (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"sftp\")) != NULL &&\n        Z_TYPE_P(tmpzval) == IS_RESOURCE) {\n        php_ssh2_sftp_data *sftp_data;\n        sftp_data = (php_ssh2_sftp_data *) zend_fetch_resource(Z_RES_P(tmpzval), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp);\n        if (sftp_data) {\n            Z_ADDREF_P(tmpzval);\n            *psftp_rsrc = Z_RES_P(tmpzval);\n            *psftp = sftp_data->sftp;\n            *presource = sftp_data->session_rsrc;\n            *psession = sftp_data->session;\n            return resource;\n        }\n    }\n    if (ZSTR_VAL(resource->host)[0] == 0 && context &&\n        (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"session\")) != NULL &&\n        Z_TYPE_P(tmpzval) == IS_RESOURCE) {\n        session = (LIBSSH2_SESSION *) zend_fetch_resource(Z_RES_P(tmpzval), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);\n        if (session) {\n            if (psftp) {\n                /* We need an SFTP layer too! */\n                LIBSSH2_SFTP *sftp = libssh2_sftp_init(session);\n                php_ssh2_sftp_data *sftp_data;\n\n                if (!sftp) {\n                    php_url_free(resource);\n                    return NULL;\n                }\n                sftp_data = (php_ssh2_sftp_data *) emalloc(sizeof(php_ssh2_sftp_data));\n                sftp_data->sftp = sftp;\n                sftp_data->session = session;\n                sftp_data->session_rsrc = Z_RES_P(tmpzval);\n                Z_ADDREF_P(tmpzval);\n                *psftp_rsrc = zend_register_resource(sftp_data, le_ssh2_sftp);\n                *psftp = sftp;\n                *presource = Z_RES_P(tmpzval);\n                *psession = session;\n                return resource;\n            }\n            Z_ADDREF_P(tmpzval);\n            *psession = session;\n            *presource = Z_RES_P(tmpzval);\n            return resource;\n        }\n    }\n\n    /* Make our own connection then */\n    if (!resource->port) {\n        resource->port = 22;\n    }\n\n    if (context && (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"methods\")) != NULL &&\n        Z_TYPE_P(tmpzval) == IS_ARRAY) {\n        methods = tmpzval;\n    }\n\n    if (context && (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"callbacks\")) != NULL &&\n        Z_TYPE_P(tmpzval) == IS_ARRAY) {\n        callbacks = tmpzval;\n    }\n\n    if (context && (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"username\")) != NULL &&\n        Z_TYPE_P(tmpzval) == IS_STRING) {\n        username = Z_STRVAL_P(tmpzval);\n        username_len = Z_STRLEN_P(tmpzval);\n    }\n\n    if (context && (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"password\")) != NULL &&\n        Z_TYPE_P(tmpzval) == IS_STRING) {\n        password = Z_STRVAL_P(tmpzval);\n        password_len = Z_STRLEN_P(tmpzval);\n    }\n\n    if (context && (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"pubkey_file\")) != NULL &&\n        Z_TYPE_P(tmpzval) == IS_STRING) {\n        pubkey_file = Z_STRVAL_P(tmpzval);\n    }\n\n    if (context && (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"privkey_file\")) != NULL &&\n        Z_TYPE_P(tmpzval) == IS_STRING) {\n        privkey_file = Z_STRVAL_P(tmpzval);\n    }\n\n    if (resource->user) {\n        int len = ZSTR_LEN(resource->user);\n\n        if (len) {\n            username = ZSTR_VAL(resource->user);\n            username_len = len;\n        }\n    }\n\n    if (resource->pass) {\n        int len = ZSTR_LEN(resource->pass);\n\n        if (len) {\n            password = ZSTR_VAL(resource->pass);\n            password_len = len;\n        }\n    }\n\n    if (!username) {\n        /* username is a minimum */\n        php_url_free(resource);\n        return NULL;\n    }\n\n    session = php_ssh2_session_connect(ZSTR_VAL(resource->host), resource->port, methods, callbacks);\n    if (!session) {\n        /* Unable to connect! */\n        php_url_free(resource);\n        return NULL;\n    }\n\n    /* Authenticate */\n    if (pubkey_file && privkey_file) {\n        if (php_check_open_basedir(pubkey_file) || php_check_open_basedir(privkey_file)) {\n            php_url_free(resource);\n            return NULL;\n        }\n\n        /* Attempt pubkey authentication */\n        if (!libssh2_userauth_publickey_fromfile(session, username, pubkey_file, privkey_file, password)) {\n            goto session_authed;\n        }\n    }\n\n    if (password) {\n        /* Attempt password authentication */\n        if (libssh2_userauth_password_ex(session, username, username_len, password, password_len, NULL) == 0) {\n            goto session_authed;\n        }\n    }\n\n    /* Auth failure */\n    php_url_free(resource);\n    if (Z_RES(zsession)) {\n        zend_list_delete(Z_RES(zsession));\n    }\n    return NULL;\n\nsession_authed:\n    ZVAL_RES(&zsession, zend_register_resource(session, le_ssh2_session));\n\n    if (psftp) {\n        LIBSSH2_SFTP *sftp;\n        zval zsftp{};\n\n        sftp = libssh2_sftp_init(session);\n        if (!sftp) {\n            php_url_free(resource);\n            zend_list_delete(Z_RES(zsession));\n            return NULL;\n        }\n\n        sftp_data = (php_ssh2_sftp_data *) emalloc(sizeof(php_ssh2_sftp_data));\n        sftp_data->session = session;\n        sftp_data->sftp = sftp;\n        sftp_data->session_rsrc = Z_RES(zsession);\n\n        // TODO Sean-Der\n        // ZEND_REGISTER_RESOURCE(sftp_data, le_ssh2_sftp);\n        *psftp_rsrc = Z_RES(zsftp);\n        *psftp = sftp;\n    }\n\n    *presource = Z_RES(zsession);\n    *psession = session;\n\n    return resource;\n}\n/* }}} */\n\n/* *****************\n * Shell Wrapper *\n ***************** */\n\n/* {{{ php_ssh2_shell_open\n * Make a stream from a session\n */\nstatic php_stream *php_ssh2_shell_open(LIBSSH2_SESSION *session,\n                                       zend_resource *resource,\n                                       const char *term,\n                                       int term_len,\n                                       zval *environment,\n                                       long width,\n                                       long height,\n                                       long type) {\n    LIBSSH2_CHANNEL *channel;\n    php_ssh2_channel_data *channel_data;\n    php_stream *stream;\n\n    channel = libssh2_channel_open_session(session);\n    if (!channel) {\n        php_error_docref(NULL, E_WARNING, \"Unable to request a channel from remote host\");\n        return NULL;\n    }\n\n    if (environment) {\n        zend_string *key;\n        int key_type;\n        zend_ulong idx;\n\n        for (zend_hash_internal_pointer_reset(HASH_OF(environment));\n             (key_type = zend_hash_get_current_key(HASH_OF(environment), &key, &idx)) != HASH_KEY_NON_EXISTENT;\n             zend_hash_move_forward(HASH_OF(environment))) {\n            if (key_type == HASH_KEY_IS_STRING) {\n                zval *value;\n\n                if ((value = zend_hash_get_current_data(HASH_OF(environment))) != NULL) {\n                    zval copyval = *value;\n\n                    zval_copy_ctor(&copyval);\n                    convert_to_string(&copyval);\n\n                    if (libssh2_channel_setenv_ex(channel, key->val, key->len, Z_STRVAL(copyval), Z_STRLEN(copyval))) {\n                        php_error_docref(\n                            NULL, E_WARNING, \"Failed setting %s=%s on remote end\", ZSTR_VAL(key), Z_STRVAL(copyval));\n                    }\n                    zval_dtor(&copyval);\n                }\n            } else {\n                php_error_docref(NULL, E_NOTICE, \"Skipping numeric index in environment array\");\n            }\n        }\n    }\n\n    if (type == PHP_SSH2_TERM_UNIT_CHARS) {\n        if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, width, height, 0, 0)) {\n            php_error_docref(NULL, E_WARNING, \"Failed allocating %s pty at %ldx%ld characters\", term, width, height);\n            libssh2_channel_free(channel);\n            return NULL;\n        }\n    } else {\n        if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, 0, 0, width, height)) {\n            php_error_docref(NULL, E_WARNING, \"Failed allocating %s pty at %ldx%ld pixels\", term, width, height);\n            libssh2_channel_free(channel);\n            return NULL;\n        }\n    }\n\n    if (libssh2_channel_shell(channel)) {\n        php_error_docref(NULL, E_WARNING, \"Unable to request shell from remote host\");\n        libssh2_channel_free(channel);\n        return NULL;\n    }\n\n    /* Turn it into a stream */\n    channel_data = (php_ssh2_channel_data *) emalloc(sizeof(php_ssh2_channel_data));\n    channel_data->channel = channel;\n    channel_data->streamid = 0;\n    channel_data->is_blocking = 0;\n    channel_data->timeout = 0;\n    channel_data->session_rsrc = resource;\n    channel_data->refcount = NULL;\n\n    stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, \"r+\");\n\n    return stream;\n}\n/* }}} */\n\n/* {{{ php_ssh2_fopen_wrapper_shell\n * ssh2.shell:// fopen wrapper\n */\nstatic php_stream *php_ssh2_fopen_wrapper_shell(php_stream_wrapper *wrapper,\n                                                const char *path,\n                                                const char *mode,\n                                                int options,\n                                                zend_string **opened_path,\n                                                php_stream_context *context STREAMS_DC) {\n    LIBSSH2_SESSION *session = NULL;\n    php_stream *stream;\n    zval *tmpzval, *environment = NULL;\n    const char *terminal = PHP_SSH2_DEFAULT_TERMINAL;\n    zend_long width = PHP_SSH2_DEFAULT_TERM_WIDTH;\n    zend_long height = PHP_SSH2_DEFAULT_TERM_HEIGHT;\n    zend_long type = PHP_SSH2_DEFAULT_TERM_UNIT;\n    zend_resource *rsrc = NULL;\n    int terminal_len = sizeof(PHP_SSH2_DEFAULT_TERMINAL) - 1;\n    php_url *resource;\n    char *s;\n\n    resource = php_ssh2_fopen_wraper_parse_path(path, \"shell\", context, &session, &rsrc, NULL, NULL);\n    if (!resource || !session) {\n        return NULL;\n    }\n\n    if (context && (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"env\")) != NULL &&\n        Z_TYPE_P(tmpzval) == IS_ARRAY) {\n        environment = tmpzval;\n    }\n\n    if (context && (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"term\")) != NULL &&\n        Z_TYPE_P(tmpzval) == IS_STRING) {\n        terminal = Z_STRVAL_P(tmpzval);\n        terminal_len = Z_STRLEN_P(tmpzval);\n    }\n\n    if (context && (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"term_width\")) != NULL) {\n        zval copyval;\n        copyval = *tmpzval;\n        convert_to_long(&copyval);\n        width = Z_LVAL_P(&copyval);\n        zval_ptr_dtor(&copyval);\n    }\n\n    if (context && (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"term_height\")) != NULL) {\n        zval copyval;\n        copyval = *tmpzval;\n        convert_to_long(&copyval);\n        height = Z_LVAL_P(&copyval);\n        zval_ptr_dtor(&copyval);\n    }\n\n    if (context && (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"term_units\")) != NULL) {\n        zval copyval;\n        copyval = *tmpzval;\n        convert_to_long(&copyval);\n        type = Z_LVAL_P(&copyval);\n        zval_ptr_dtor(&copyval);\n    }\n\n    s = resource->path ? ZSTR_VAL(resource->path) : NULL;\n\n    if (s && s[0] == '/') {\n        /* Terminal type encoded into URL overrides context terminal type */\n        char *p;\n\n        s++;\n        p = strchr(s, '/');\n        if (p) {\n            if (p - s) {\n                terminal = s;\n                terminal_len = p - terminal;\n                s += terminal_len + 1;\n            } else {\n                /* \"null\" terminal given, skip it */\n                s++;\n            }\n        } else {\n            int len;\n\n            if ((len = strlen(path + 1))) {\n                terminal = s;\n                terminal_len = len;\n                s += len;\n            }\n        }\n    }\n\n    /* TODO: Accept resolution and environment vars as URL style parameters\n     * ssh2.shell://hostorresource/terminal/99x99c?envvar=envval&envvar=envval....\n     */\n    stream = php_ssh2_shell_open(session, rsrc, terminal, terminal_len, environment, width, height, type);\n    if (!stream) {\n        zend_list_delete(rsrc);\n    }\n    php_url_free(resource);\n\n    return stream;\n}\n/* }}} */\n\nstatic php_stream_wrapper_ops php_ssh2_shell_stream_wops = {php_ssh2_fopen_wrapper_shell,\n                                                            NULL, /* stream_close */\n                                                            NULL, /* stat */\n                                                            NULL, /* stat_url */\n                                                            NULL, /* opendir */\n                                                            \"ssh2.shell\"};\n\nphp_stream_wrapper php_ssh2_stream_wrapper_shell = {&php_ssh2_shell_stream_wops, NULL, 0};\n\n/* {{{ proto stream ssh2_shell(resource session[, string term_type[, array env[, int width, int height[, int\n * width_height_type]]]]) Open a shell at the remote end and allocate a channel for it\n */\nPHP_FUNCTION(ssh2_shell) {\n    LIBSSH2_SESSION *session;\n    php_stream *stream;\n    zval *zsession;\n    zval *environment = NULL;\n    const char *term = PHP_SSH2_DEFAULT_TERMINAL;\n    size_t term_len = sizeof(PHP_SSH2_DEFAULT_TERMINAL) - 1;\n    zend_long width = PHP_SSH2_DEFAULT_TERM_WIDTH;\n    zend_long height = PHP_SSH2_DEFAULT_TERM_HEIGHT;\n    zend_long type = PHP_SSH2_DEFAULT_TERM_UNIT;\n    int argc = ZEND_NUM_ARGS();\n\n    if (argc == 5) {\n        php_error_docref(NULL, E_ERROR, \"width specified without height parameter\");\n        RETURN_FALSE;\n    }\n\n    if (zend_parse_parameters(argc, \"r|sa!lll\", &zsession, &term, &term_len, &environment, &width, &height, &type) ==\n        FAILURE) {\n        return;\n    }\n\n    SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);\n\n    stream = php_ssh2_shell_open(session, Z_RES_P(zsession), term, term_len, environment, width, height, type);\n    if (!stream) {\n        RETURN_FALSE;\n    }\n\n    /* Ensure that channels are freed BEFORE the sessions they belong to */\n    Z_ADDREF_P(zsession);\n\n    php_stream_to_zval(stream, return_value);\n}\n/* }}} */\n\nPHP_FUNCTION(ssh2_shell_resize) {\n    zend_long width;\n    zend_long height;\n    zend_long width_px = 0;\n    zend_long height_px = 0;\n    zval *zparent;\n    php_stream *parent;\n    php_ssh2_channel_data *data;\n\n    int argc = ZEND_NUM_ARGS();\n\n    if (zend_parse_parameters(argc, \"rll|ll\", &zparent, &width, &height, &width_px, &height_px) == FAILURE) {\n        return;\n    }\n\n    php_stream_from_zval(parent, zparent);\n\n    if (parent->ops != &php_ssh2_channel_stream_ops) {\n        php_error_docref(NULL, E_WARNING, \"Provided stream is not of type \" PHP_SSH2_CHANNEL_STREAM_NAME);\n        RETURN_FALSE;\n    }\n\n    data = (php_ssh2_channel_data *) parent->abstract;\n    auto session = ssh2_get_session(data);\n\n    libssh2_channel_request_pty_size_ex(data->channel, width, height, width_px, height_px);\n\n    RETURN_TRUE;\n}\n\n/* ****************\n * Exec Wrapper *\n **************** */\n\n/* {{{ php_ssh2_exec_command\n * Make a stream from a session\n */\nstatic php_stream *php_ssh2_exec_command(LIBSSH2_SESSION *session,\n                                         zend_resource *rsrc,\n                                         char *command,\n                                         char *term,\n                                         int term_len,\n                                         zval *environment,\n                                         long width,\n                                         long height,\n                                         long type) {\n    LIBSSH2_CHANNEL *channel;\n    php_ssh2_channel_data *channel_data;\n    php_stream *stream;\n\n    channel = libssh2_channel_open_session(session);\n    if (!channel) {\n        php_error_docref(NULL, E_WARNING, \"Unable to request a channel from remote host\");\n        return NULL;\n    }\n\n    if (environment) {\n        zend_string *key = NULL;\n        int key_type;\n        zend_ulong idx = 0;\n        HashPosition pos;\n\n        for (zend_hash_internal_pointer_reset_ex(HASH_OF(environment), &pos);\n             (key_type = zend_hash_get_current_key_ex(HASH_OF(environment), &key, &idx, &pos)) != HASH_KEY_NON_EXISTENT;\n             zend_hash_move_forward_ex(HASH_OF(environment), &pos)) {\n            if (key_type == HASH_KEY_IS_STRING) {\n                zval *value;\n\n                if ((value = zend_hash_get_current_data(HASH_OF(environment))) != NULL) {\n                    zval copyval = *value;\n\n                    zval_copy_ctor(&copyval);\n                    convert_to_string(&copyval);\n                    if (libssh2_channel_setenv_ex(channel, key->val, key->len, Z_STRVAL(copyval), Z_STRLEN(copyval))) {\n                        php_error_docref(\n                            NULL, E_WARNING, \"Failed setting %s=%s on remote end\", ZSTR_VAL(key), Z_STRVAL(copyval));\n                    }\n                    zval_dtor(&copyval);\n                }\n            } else {\n                php_error_docref(NULL, E_NOTICE, \"Skipping numeric index in environment array\");\n            }\n        }\n    }\n\n    if (term) {\n        if (type == PHP_SSH2_TERM_UNIT_CHARS) {\n            if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, width, height, 0, 0)) {\n                php_error_docref(\n                    NULL, E_WARNING, \"Failed allocating %s pty at %ldx%ld characters\", term, width, height);\n                libssh2_channel_free(channel);\n                return NULL;\n            }\n        } else {\n            if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, 0, 0, width, height)) {\n                php_error_docref(NULL, E_WARNING, \"Failed allocating %s pty at %ldx%ld pixels\", term, width, height);\n                libssh2_channel_free(channel);\n                return NULL;\n            }\n        }\n    }\n\n    if (libssh2_channel_exec(channel, command)) {\n        php_error_docref(NULL, E_WARNING, \"Unable to request command execution on remote host\");\n        libssh2_channel_free(channel);\n        return NULL;\n    }\n\n    /* Turn it into a stream */\n    channel_data = (php_ssh2_channel_data *) emalloc(sizeof(php_ssh2_channel_data));\n    channel_data->channel = channel;\n    channel_data->streamid = 0;\n    channel_data->is_blocking = 0;\n    channel_data->timeout = 0;\n    channel_data->session_rsrc = rsrc;\n    channel_data->refcount = NULL;\n\n    stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, \"r+\");\n\n    return stream;\n}\n/* }}} */\n\n/* {{{ php_ssh2_fopen_wrapper_exec\n * ssh2.exec:// fopen wrapper\n */\nstatic php_stream *php_ssh2_fopen_wrapper_exec(php_stream_wrapper *wrapper,\n                                               const char *path,\n                                               const char *mode,\n                                               int options,\n                                               zend_string **opened_path,\n                                               php_stream_context *context STREAMS_DC) {\n    LIBSSH2_SESSION *session = NULL;\n    php_stream *stream;\n    zval *tmpzval, *environment = NULL;\n    zend_resource *rsrc = NULL;\n    php_url *resource;\n    char *terminal = NULL;\n    int terminal_len = 0;\n    long width = PHP_SSH2_DEFAULT_TERM_WIDTH;\n    long height = PHP_SSH2_DEFAULT_TERM_HEIGHT;\n    long type = PHP_SSH2_DEFAULT_TERM_UNIT;\n\n    resource = php_ssh2_fopen_wraper_parse_path(path, \"exec\", context, &session, &rsrc, NULL, NULL);\n    if (!resource || !session) {\n        return NULL;\n    }\n    if (!resource->path) {\n        php_url_free(resource);\n        zend_list_delete(rsrc);\n        return NULL;\n    }\n\n    if (context && (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"env\")) != NULL &&\n        Z_TYPE_P(tmpzval) == IS_ARRAY) {\n        environment = tmpzval;\n    }\n\n    if (context && (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"term\")) != NULL &&\n        Z_TYPE_P(tmpzval) == IS_STRING) {\n        terminal = Z_STRVAL_P(tmpzval);\n        terminal_len = Z_STRLEN_P(tmpzval);\n    }\n\n    if (context && (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"term_width\")) != NULL) {\n        zval copyval;\n        copyval = *tmpzval;\n        convert_to_long(&copyval);\n        width = Z_LVAL_P(&copyval);\n        zval_ptr_dtor(&copyval);\n    }\n\n    if (context && (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"term_height\")) != NULL) {\n        zval copyval;\n        copyval = *tmpzval;\n        convert_to_long(&copyval);\n        height = Z_LVAL_P(&copyval);\n        zval_ptr_dtor(&copyval);\n    }\n\n    if (context && (tmpzval = php_stream_context_get_option(context, \"ssh2\", \"term_units\")) != NULL) {\n        zval *copyval;\n        copyval = tmpzval;\n        convert_to_long(copyval);\n        type = Z_LVAL_P(copyval);\n        zval_ptr_dtor(copyval);\n    }\n\n    stream = php_ssh2_exec_command(\n        session, rsrc, ZSTR_VAL(resource->path) + 1, terminal, terminal_len, environment, width, height, type);\n    if (!stream) {\n        zend_list_delete(rsrc);\n    }\n    php_url_free(resource);\n\n    return stream;\n}\n/* }}} */\n\nstatic php_stream_wrapper_ops php_ssh2_exec_stream_wops = {php_ssh2_fopen_wrapper_exec,\n                                                           NULL, /* stream_close */\n                                                           NULL, /* stat */\n                                                           NULL, /* stat_url */\n                                                           NULL, /* opendir */\n                                                           \"ssh2.exec\"};\n\nphp_stream_wrapper php_ssh2_stream_wrapper_exec = {&php_ssh2_exec_stream_wops, NULL, 0};\n\n/* {{{ proto stream ssh2_exec(resource session, string command[, string pty[, array env[, int width[, int height[, int\n * width_height_type]]]]]) Execute a command at the remote end and allocate a channel for it\n *\n * This function has a dirty little secret.... pty and env can be in either order.... shhhh... don't tell anyone\n */\nPHP_FUNCTION(ssh2_exec) {\n    LIBSSH2_SESSION *session;\n    php_stream *stream;\n    zval *zsession;\n    zval *environment = NULL;\n    zval *zpty = NULL;\n    char *command;\n    size_t command_len;\n    zend_long width = PHP_SSH2_DEFAULT_TERM_WIDTH;\n    zend_long height = PHP_SSH2_DEFAULT_TERM_HEIGHT;\n    zend_long type = PHP_SSH2_DEFAULT_TERM_UNIT;\n    char *term = NULL;\n    int term_len = 0;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(),\n                              \"rs|z!z!lll\",\n                              &zsession,\n                              &command,\n                              &command_len,\n                              &zpty,\n                              &environment,\n                              &width,\n                              &height,\n                              &type) == FAILURE) {\n        return;\n    }\n\n    if (zpty && Z_TYPE_P(zpty) == IS_ARRAY) {\n        /* Swap pty and environment -- old call style */\n        zval *tmp = zpty;\n        zpty = environment;\n        environment = tmp;\n    }\n\n    if (environment && Z_TYPE_P(environment) != IS_ARRAY) {\n        php_error_docref(NULL, E_WARNING, \"ssh2_exec() expects arg 4 to be of type array\");\n        RETURN_FALSE;\n    }\n\n    if (zpty) {\n        convert_to_string(zpty);\n        term = Z_STRVAL_P(zpty);\n        term_len = Z_STRLEN_P(zpty);\n    }\n\n    SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);\n\n    stream =\n        php_ssh2_exec_command(session, Z_RES_P(zsession), command, term, term_len, environment, width, height, type);\n    if (!stream) {\n        RETURN_FALSE;\n    }\n\n    /* Ensure that channels are freed BEFORE the sessions they belong to */\n    Z_ADDREF_P(zsession);\n\n    php_stream_to_zval(stream, return_value);\n}\n/* }}} */\n\n/* ***************\n * SCP Wrapper *\n *************** */\n\n/* {{{ php_ssh2_scp_xfer\n * Make a stream from a session\n */\nstatic php_stream *php_ssh2_scp_xfer(LIBSSH2_SESSION *session, zend_resource *rsrc, char *filename) {\n    LIBSSH2_CHANNEL *channel;\n    php_ssh2_channel_data *channel_data;\n    php_stream *stream;\n\n#ifdef SW_USE_SSH2_ASYNC_HOOK\n    php_ssh2_session_data *session_res = libssh2_session_abstract(session);\n#endif\n    channel = libssh2_scp_recv(session, filename, NULL);\n    if (!channel) {\n        char *error;\n        libssh2_session_last_error(session, &error, NULL, 0);\n        php_error_docref(NULL, E_WARNING, \"Unable to request a channel from remote host: %s\", error);\n        return NULL;\n    }\n\n    /* Turn it into a stream */\n    channel_data = (php_ssh2_channel_data *) emalloc(sizeof(php_ssh2_channel_data));\n    channel_data->channel = channel;\n    channel_data->streamid = 0;\n    channel_data->is_blocking = 0;\n    channel_data->timeout = 0;\n    channel_data->session_rsrc = rsrc;\n    channel_data->refcount = NULL;\n\n    stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, \"r\");\n\n    return stream;\n}\n/* }}} */\n\n/* {{{ php_ssh2_fopen_wrapper_scp\n * ssh2.scp:// fopen wrapper (Read mode only, if you want to know why write mode isn't supported as a stream, take a\n * look at the SCP protocol)\n */\nstatic php_stream *php_ssh2_fopen_wrapper_scp(php_stream_wrapper *wrapper,\n                                              const char *path,\n                                              const char *mode,\n                                              int options,\n                                              zend_string **opened_path,\n                                              php_stream_context *context STREAMS_DC) {\n    LIBSSH2_SESSION *session = NULL;\n    php_stream *stream;\n    zend_resource *rsrc = NULL;\n    php_url *resource;\n\n    if (strchr(mode, '+') || strchr(mode, 'a') || strchr(mode, 'w')) {\n        return NULL;\n    }\n\n    resource = php_ssh2_fopen_wraper_parse_path(path, \"scp\", context, &session, &rsrc, NULL, NULL);\n    if (!resource || !session) {\n        return NULL;\n    }\n    if (!resource->path) {\n        php_url_free(resource);\n        zend_list_delete(rsrc);\n        return NULL;\n    }\n\n    stream = php_ssh2_scp_xfer(session, rsrc, ZSTR_VAL(resource->path));\n    if (!stream) {\n        zend_list_delete(rsrc);\n    }\n    php_url_free(resource);\n\n    return stream;\n}\n/* }}} */\n\nstatic php_stream_wrapper_ops php_ssh2_scp_stream_wops = {php_ssh2_fopen_wrapper_scp,\n                                                          NULL, /* stream_close */\n                                                          NULL, /* stat */\n                                                          NULL, /* stat_url */\n                                                          NULL, /* opendir */\n                                                          \"ssh2.scp\"};\n\nphp_stream_wrapper php_ssh2_stream_wrapper_scp = {&php_ssh2_scp_stream_wops, NULL, 0};\n\n/* {{{ proto bool ssh2_scp_recv(resource session, string remote_file, string local_file)\n * Request a file via SCP\n */\nPHP_FUNCTION(ssh2_scp_recv) {\n    LIBSSH2_SESSION *session;\n    LIBSSH2_CHANNEL *remote_file;\n    struct stat sb;\n    php_stream *local_file;\n    zval *zsession;\n    char *remote_filename, *local_filename;\n    size_t remote_filename_len, local_filename_len;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(),\n                              \"rss\",\n                              &zsession,\n                              &remote_filename,\n                              &remote_filename_len,\n                              &local_filename,\n                              &local_filename_len) == FAILURE) {\n        return;\n    }\n\n    SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);\n\n    remote_file = libssh2_scp_recv(session, remote_filename, &sb);\n    if (!remote_file) {\n        php_error_docref(NULL, E_WARNING, \"Unable to receive remote file\");\n        RETURN_FALSE;\n    }\n\n    local_file = php_stream_open_wrapper(local_filename, \"wb\", REPORT_ERRORS, NULL);\n    if (!local_file) {\n        php_error_docref(NULL, E_WARNING, \"Unable to write to local file\");\n        libssh2_channel_free(remote_file);\n        RETURN_FALSE;\n    }\n\n    while (sb.st_size) {\n        char buffer[8192];\n        int bytes_read;\n\n        bytes_read = libssh2_channel_read(remote_file, buffer, sb.st_size > 8192 ? 8192 : sb.st_size);\n        if (bytes_read < 0) {\n            php_error_docref(NULL, E_WARNING, \"Error reading from remote file\");\n            libssh2_channel_free(remote_file);\n            php_stream_close(local_file);\n            RETURN_FALSE;\n        }\n        php_stream_write(local_file, buffer, bytes_read);\n        sb.st_size -= bytes_read;\n    }\n\n    libssh2_channel_free(remote_file);\n    php_stream_close(local_file);\n\n    RETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ proto stream ssh2_scp_send(resource session, string local_file, string remote_file[, int create_mode = 0644])\n * Send a file via SCP\n */\nPHP_FUNCTION(ssh2_scp_send) {\n    LIBSSH2_SESSION *session;\n    LIBSSH2_CHANNEL *remote_file;\n    php_stream *local_file;\n    zval *zsession;\n    char *local_filename, *remote_filename;\n    size_t local_filename_len, remote_filename_len;\n    zend_long create_mode = 0644;\n    php_stream_statbuf ssb;\n    int argc = ZEND_NUM_ARGS();\n\n    if (zend_parse_parameters(argc,\n                              \"rss|l\",\n                              &zsession,\n                              &local_filename,\n                              &local_filename_len,\n                              &remote_filename,\n                              &remote_filename_len,\n                              &create_mode) == FAILURE) {\n        return;\n    }\n\n    SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);\n\n    local_file = php_stream_open_wrapper(local_filename, \"rb\", REPORT_ERRORS, NULL);\n    if (!local_file) {\n        php_error_docref(NULL, E_WARNING, \"Unable to read source file\");\n        RETURN_FALSE;\n    }\n\n    if (php_stream_stat(local_file, &ssb)) {\n        php_error_docref(NULL, E_WARNING, \"Failed statting local file\");\n        php_stream_close(local_file);\n        RETURN_FALSE;\n    }\n\n    if (argc < 4) {\n        create_mode = ssb.sb.st_mode & 0777;\n    }\n\n    remote_file =\n        libssh2_scp_send_ex(session, remote_filename, create_mode, ssb.sb.st_size, ssb.sb.st_atime, ssb.sb.st_mtime);\n    if (!remote_file) {\n        int last_error = 0;\n        char *error_msg = NULL;\n\n        last_error = libssh2_session_last_error(session, &error_msg, NULL, 0);\n        php_error_docref(NULL, E_WARNING, \"Failure creating remote file: %s (%d)\", error_msg, last_error);\n        php_stream_close(local_file);\n        RETURN_FALSE;\n    }\n\n    while (ssb.sb.st_size) {\n        char buffer[8192];\n        ssize_t toread = MIN(8192, ssb.sb.st_size);\n        ssize_t bytesread = php_stream_read(local_file, buffer, toread);\n        ssize_t sent = 0;\n        ssize_t justsent = 0;\n\n        if (bytesread <= 0 || bytesread > toread) {\n            php_error_docref(NULL, E_WARNING, \"Failed copying file 2\");\n            php_stream_close(local_file);\n            libssh2_channel_free(remote_file);\n            RETURN_FALSE;\n        }\n\n        while (bytesread - sent > 0) {\n            if ((justsent = libssh2_channel_write(remote_file, (buffer + sent), bytesread - sent)) < 0) {\n                switch (justsent) {\n                case LIBSSH2_ERROR_EAGAIN:\n                    php_error_docref(NULL, E_WARNING, \"Operation would block\");\n                    break;\n\n                case LIBSSH2_ERROR_ALLOC:\n                    php_error_docref(NULL, E_WARNING, \"An internal memory allocation call failed\");\n                    break;\n\n                case LIBSSH2_ERROR_SOCKET_SEND:\n                    php_error_docref(NULL, E_WARNING, \"Unable to send data on socket\");\n                    break;\n\n                case LIBSSH2_ERROR_CHANNEL_CLOSED:\n                    php_error_docref(NULL, E_WARNING, \"The channel has been closed\");\n                    break;\n\n                case LIBSSH2_ERROR_CHANNEL_EOF_SENT:\n                    php_error_docref(NULL, E_WARNING, \"The channel has been requested to be closed\");\n                    break;\n                }\n\n                php_stream_close(local_file);\n                libssh2_channel_free(remote_file);\n                RETURN_FALSE;\n            }\n            sent = sent + justsent;\n        }\n        ssb.sb.st_size -= bytesread;\n    }\n\n    libssh2_channel_flush_ex(remote_file, LIBSSH2_CHANNEL_FLUSH_ALL);\n    php_stream_close(local_file);\n    libssh2_channel_free(remote_file);\n    RETURN_TRUE;\n}\n/* }}} */\n\n/* ***************************\n * Direct TCP/IP Transport *\n *************************** */\n\n/* {{{ php_ssh2_direct_tcpip\n * Make a stream from a session\n */\nstatic php_stream *php_ssh2_direct_tcpip(LIBSSH2_SESSION *session, zend_resource *rsrc, char *host, int port) {\n    LIBSSH2_CHANNEL *channel;\n    php_ssh2_channel_data *channel_data;\n    php_stream *stream;\n\n    channel = libssh2_channel_direct_tcpip(session, host, port);\n    if (!channel) {\n        php_error_docref(NULL, E_WARNING, \"Unable to request a channel from remote host\");\n        return NULL;\n    }\n\n    /* Turn it into a stream */\n    channel_data = (php_ssh2_channel_data *) emalloc(sizeof(php_ssh2_channel_data));\n    channel_data->channel = channel;\n    channel_data->streamid = 0;\n    channel_data->is_blocking = 0;\n    channel_data->timeout = 0;\n    channel_data->session_rsrc = rsrc;\n    channel_data->refcount = NULL;\n\n    stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, \"r+\");\n\n    return stream;\n}\n/* }}} */\n\n/* {{{ php_ssh2_fopen_wrapper_tunnel\n * ssh2.tunnel:// fopen wrapper\n */\nstatic php_stream *php_ssh2_fopen_wrapper_tunnel(php_stream_wrapper *wrapper,\n                                                 const char *path,\n                                                 const char *mode,\n                                                 int options,\n                                                 zend_string **opened_path,\n                                                 php_stream_context *context STREAMS_DC) {\n    LIBSSH2_SESSION *session = NULL;\n    php_stream *stream = NULL;\n    php_url *resource;\n    char *host = NULL;\n    int port = 0;\n    zend_resource *rsrc;\n\n    resource = php_ssh2_fopen_wraper_parse_path(path, \"tunnel\", context, &session, &rsrc, NULL, NULL);\n    if (!resource || !session) {\n        return NULL;\n    }\n\n    if (resource->path && ZSTR_VAL(resource->path)[0] == '/') {\n        char *colon;\n\n        host = ZSTR_VAL(resource->path) + 1;\n        if (*host == '[') {\n            /* IPv6 Encapsulated Format */\n            host++;\n            colon = strstr(host, \"]:\");\n            if (colon) {\n                *colon = 0;\n                colon += 2;\n            }\n        } else {\n            colon = strchr(host, ':');\n            if (colon) {\n                *(colon++) = 0;\n            }\n        }\n        if (colon) {\n            port = atoi(colon);\n        }\n    }\n\n    if ((port <= 0) || (port > 65535) || !host || (strlen(host) == 0)) {\n        /* Invalid connection criteria */\n        php_url_free(resource);\n        zend_list_delete(rsrc);\n        return NULL;\n    }\n\n    stream = php_ssh2_direct_tcpip(session, rsrc, host, port);\n    if (!stream) {\n        zend_list_delete(rsrc);\n    }\n    php_url_free(resource);\n\n    return stream;\n}\n/* }}} */\n\nstatic php_stream_wrapper_ops php_ssh2_tunnel_stream_wops = {php_ssh2_fopen_wrapper_tunnel,\n                                                             NULL, /* stream_close */\n                                                             NULL, /* stat */\n                                                             NULL, /* stat_url */\n                                                             NULL, /* opendir */\n                                                             \"ssh2.tunnel\"};\n\nphp_stream_wrapper php_ssh2_stream_wrapper_tunnel = {&php_ssh2_tunnel_stream_wops, NULL, 0};\n\n/* {{{ proto stream ssh2_tunnel(resource session, string host, int port)\n * Tunnel to remote TCP/IP host/port\n */\nPHP_FUNCTION(ssh2_tunnel) {\n    LIBSSH2_SESSION *session;\n    php_stream *stream;\n    zval *zsession;\n    char *host;\n    size_t host_len;\n    zend_long port;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rsl\", &zsession, &host, &host_len, &port) == FAILURE) {\n        return;\n    }\n\n    SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);\n\n    stream = php_ssh2_direct_tcpip(session, Z_RES_P(zsession), host, port);\n    if (!stream) {\n        RETURN_FALSE;\n    }\n\n    /* Ensure that channels are freed BEFORE the sessions they belong to */\n    Z_ADDREF_P(zsession);\n\n    php_stream_to_zval(stream, return_value);\n}\n/* }}} */\n\n/* ******************\n * Generic Helper *\n ****************** */\n\n/* {{{ proto stream ssh2_fetch_stream(stream channel, int streamid)\n * Fetch an extended data stream\n */\nPHP_FUNCTION(ssh2_fetch_stream) {\n    php_ssh2_channel_data *data, *stream_data;\n    php_stream *parent, *stream;\n    zval *zparent;\n    zend_long streamid;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rl\", &zparent, &streamid) == FAILURE) {\n        return;\n    }\n\n    if (streamid < 0) {\n        php_error_docref(NULL, E_WARNING, \"Invalid stream ID requested\");\n        RETURN_FALSE;\n    }\n\n    php_stream_from_zval(parent, zparent);\n\n    if (parent->ops != &php_ssh2_channel_stream_ops) {\n        php_error_docref(NULL, E_WARNING, \"Provided stream is not of type \" PHP_SSH2_CHANNEL_STREAM_NAME);\n        RETURN_FALSE;\n    }\n\n    data = (php_ssh2_channel_data *) parent->abstract;\n\n    if (!data->refcount) {\n        data->refcount = (uchar *) emalloc(sizeof(uchar));\n        *(data->refcount) = 1;\n    }\n\n    if (*(data->refcount) == 255) {\n        php_error_docref(NULL, E_WARNING, \"Too many streams associated to a single channel\");\n        RETURN_FALSE;\n    }\n\n    (*(data->refcount))++;\n\n    stream_data = (php_ssh2_channel_data *) emalloc(sizeof(php_ssh2_channel_data));\n    memcpy(stream_data, data, sizeof(php_ssh2_channel_data));\n    stream_data->streamid = streamid;\n\n    stream = php_stream_alloc(&php_ssh2_channel_stream_ops, stream_data, 0, \"r+\");\n    if (!stream) {\n        php_error_docref(NULL, E_WARNING, \"Error opening substream\");\n        efree(stream_data);\n        (data->refcount)--;\n        RETURN_FALSE;\n    }\n\n    php_stream_to_zval(stream, return_value);\n}\n/* }}} */\n\n/* {{{ proto stream ssh2_send_eof(stream channel)\n * Sends EOF to a stream. Primary use is to close stdin of an stdio stream.\n */\nPHP_FUNCTION(ssh2_send_eof) {\n    php_ssh2_channel_data *data;\n    php_stream *parent;\n    zval *zparent;\n    int ssh2_ret;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"r\", &zparent) == FAILURE) {\n        return;\n    }\n\n    php_stream_from_zval(parent, zparent);\n    if (parent->ops != &php_ssh2_channel_stream_ops) {\n        php_error_docref(NULL, E_WARNING, \"Provided stream is not of type \" PHP_SSH2_CHANNEL_STREAM_NAME);\n        RETURN_FALSE;\n    }\n\n    data = (php_ssh2_channel_data *) parent->abstract;\n    if (!data) {\n        php_error_docref(NULL, E_WARNING, \"Abstract in stream is null\");\n        RETURN_FALSE;\n    }\n\n    auto session = ssh2_get_session(data);\n\n    ssh2_ret = libssh2_channel_send_eof(data->channel);\n    if (ssh2_ret < 0) {\n        php_error_docref(NULL, E_WARNING, \"Couldn't send EOF to channel (Return code %d)\", ssh2_ret);\n        RETURN_FALSE;\n    }\n\n    RETURN_TRUE;\n}\n/* }}} */\n\n/*\n * Local variables:\n * tab-width: 4\n * c-basic-offset: 4\n * End:\n * vim600: noet sw=4 ts=4 fdm=marker\n * vim<600: noet sw=4 ts=4\n */\n"
  },
  {
    "path": "thirdparty/php/ssh2/ssh2_sftp.cc",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | PHP Version 7                                                        |\n   +----------------------------------------------------------------------+\n   | Copyright (c) 1997-2016 The PHP Group                                |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | http://www.php.net/license/3_01.txt                                  |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Author: Sara Golemon <pollita@php.net>                               |\n   +----------------------------------------------------------------------+\n*/\n\n#include \"php_ssh2.h\"\n\n#include \"php_swoole_ssh2_hook.h\"\n\nBEGIN_EXTERN_C()\n#include \"ext/standard/php_string.h\"\nEND_EXTERN_C()\n\n/* *************************\n * Resource Housekeeping *\n ************************* */\n\nvoid php_ssh2_sftp_dtor(zend_resource *rsrc) {\n    php_ssh2_sftp_data *data = (php_ssh2_sftp_data *) rsrc->ptr;\n\n    if (!data) {\n        return;\n    }\n\n    if (data->session_rsrc->ptr != NULL) {\n        libssh2_sftp_shutdown(data->sftp);\n    }\n\n    zend_list_delete(data->session_rsrc);\n\n    efree(data);\n}\n\n/* *****************\n * SFTP File Ops *\n ***************** */\n\nunsigned long php_ssh2_parse_fopen_modes(char *openmode) {\n    unsigned long flags = 0;\n\n    if (strchr(openmode, 'a')) {\n        flags |= LIBSSH2_FXF_APPEND;\n    }\n\n    if (strchr(openmode, 'w')) {\n        flags |= LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC | LIBSSH2_FXF_CREAT;\n    }\n\n    if (strchr(openmode, 'r')) {\n        flags |= LIBSSH2_FXF_READ;\n    }\n\n    if (strchr(openmode, '+')) {\n        flags |= LIBSSH2_FXF_READ | LIBSSH2_FXF_WRITE;\n    }\n\n    if (strchr(openmode, 'x')) {\n        flags |= LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC | LIBSSH2_FXF_EXCL | LIBSSH2_FXF_CREAT;\n    }\n\n    return flags;\n}\n\nstatic inline int php_ssh2_sftp_attr2ssb(php_stream_statbuf *ssb, LIBSSH2_SFTP_ATTRIBUTES *attrs) {\n    memset(ssb, 0, sizeof(php_stream_statbuf));\n    if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) {\n        ssb->sb.st_size = attrs->filesize;\n    }\n\n    if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) {\n        ssb->sb.st_uid = attrs->uid;\n        ssb->sb.st_gid = attrs->gid;\n    }\n    if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {\n        ssb->sb.st_mode = attrs->permissions;\n    }\n    if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) {\n        ssb->sb.st_atime = attrs->atime;\n        ssb->sb.st_mtime = attrs->mtime;\n    }\n\n    return 0;\n}\n\ntypedef struct _php_ssh2_sftp_handle_data {\n    LIBSSH2_SFTP_HANDLE *handle;\n    // The sftp_rsrc resource holds a reference to session_rsrc,\n    // so you can store the pointer directly without worrying about its lifetime.\n    // In the destructor, the session reference is dropped only after the sftp_rsrc resource has been freed.\n    LIBSSH2_SESSION *session;\n    LIBSSH2_SFTP *sftp;\n    zend_resource *sftp_rsrc;\n} php_ssh2_sftp_handle_data;\n\n/* {{{ php_ssh2_sftp_stream_write\n */\nstatic ssize_t php_ssh2_sftp_stream_write(php_stream *stream, const char *buf, size_t count) {\n    php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data *) stream->abstract;\n    ssize_t bytes_written;\n\n    auto session = data->session;\n    bytes_written = libssh2_sftp_write(data->handle, buf, count);\n\n    return bytes_written;\n}\n/* }}} */\n\n/* {{{ php_ssh2_sftp_stream_read\n */\nstatic ssize_t php_ssh2_sftp_stream_read(php_stream *stream, char *buf, size_t count) {\n    php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data *) stream->abstract;\n    ssize_t bytes_read;\n\n    auto session = data->session;\n    bytes_read = libssh2_sftp_read(data->handle, buf, count);\n\n    stream->eof = (bytes_read <= 0 && bytes_read != LIBSSH2_ERROR_EAGAIN);\n\n    return bytes_read;\n}\n/* }}} */\n\n/* {{{ php_ssh2_sftp_stream_close\n */\nstatic int php_ssh2_sftp_stream_close(php_stream *stream, int close_handle) {\n    php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data *) stream->abstract;\n    auto session = data->session;\n\n    libssh2_sftp_close(data->handle);\n    zend_list_delete(data->sftp_rsrc);\n    efree(data);\n\n    return 0;\n}\n/* }}} */\n\n/* {{{ php_ssh2_sftp_stream_seek\n */\nstatic int php_ssh2_sftp_stream_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) {\n    php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data *) stream->abstract;\n    auto session = data->session;\n\n    switch (whence) {\n    case SEEK_END: {\n        LIBSSH2_SFTP_ATTRIBUTES attrs;\n        if (libssh2_sftp_fstat(data->handle, &attrs)) {\n            return -1;\n        }\n        if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) == 0) {\n            return -1;\n        }\n        offset += attrs.filesize;\n        break;\n    }\n    case SEEK_CUR: {\n        zend_off_t current_offset = libssh2_sftp_tell(data->handle);\n\n        if (current_offset < 0) {\n            return -1;\n        }\n\n        offset += current_offset;\n        break;\n    }\n    default:\n        return -1;\n    }\n\n    libssh2_sftp_seek(data->handle, offset);\n\n    if (newoffset) {\n        *newoffset = offset;\n    }\n\n    return 0;\n}\n/* }}} */\n\n/* {{{ php_ssh2_sftp_stream_fstat\n */\nstatic int php_ssh2_sftp_stream_fstat(php_stream *stream, php_stream_statbuf *ssb) {\n    php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data *) stream->abstract;\n    LIBSSH2_SFTP_ATTRIBUTES attrs;\n    auto session = data->session;\n\n    if (libssh2_sftp_fstat(data->handle, &attrs)) {\n        return -1;\n    }\n\n    return php_ssh2_sftp_attr2ssb(ssb, &attrs);\n}\n/* }}} */\n\nstatic php_stream_ops php_ssh2_sftp_stream_ops = {\n    php_ssh2_sftp_stream_write,\n    php_ssh2_sftp_stream_read,\n    php_ssh2_sftp_stream_close,\n    NULL, /* flush */\n    PHP_SSH2_SFTP_STREAM_NAME,\n    php_ssh2_sftp_stream_seek,\n    NULL, /* cast */\n    php_ssh2_sftp_stream_fstat,\n    NULL, /* set_option */\n};\n\n/* {{{ php_ssh2_sftp_stream_opener\n */\n\nstatic php_stream *php_ssh2_sftp_stream_opener(php_stream_wrapper *wrapper,\n                                               const char *filename,\n                                               const char *mode,\n                                               int options,\n                                               zend_string **opened_path,\n                                               php_stream_context *context STREAMS_DC) {\n    php_ssh2_sftp_handle_data *data;\n    LIBSSH2_SESSION *session = NULL;\n    LIBSSH2_SFTP *sftp = NULL;\n    LIBSSH2_SFTP_HANDLE *handle;\n    php_stream *stream;\n    zend_resource *rsrc = NULL, *sftp_rsrc = NULL;\n    php_url *resource;\n    unsigned long flags;\n    long perms = 0644;\n\n    resource = php_ssh2_fopen_wraper_parse_path(filename, \"sftp\", context, &session, &rsrc, &sftp, &sftp_rsrc);\n    if (!resource || !session || !sftp || !sftp_rsrc) {\n        return NULL;\n    }\n\n    flags = php_ssh2_parse_fopen_modes((char *) mode);\n\n    handle = libssh2_sftp_open(sftp, ZSTR_VAL(resource->path), flags, perms);\n    if (!handle) {\n        php_error_docref(NULL, E_WARNING, \"Unable to open %s on remote host\", filename);\n        php_url_free(resource);\n        zend_list_delete(sftp_rsrc);\n        return NULL;\n    }\n\n    data = (php_ssh2_sftp_handle_data *) emalloc(sizeof(php_ssh2_sftp_handle_data));\n    data->handle = handle;\n    data->session = session;\n    data->sftp = sftp;\n    data->sftp_rsrc = sftp_rsrc;\n\n    stream = php_stream_alloc(&php_ssh2_sftp_stream_ops, data, 0, mode);\n    if (!stream) {\n        libssh2_sftp_close(handle);\n        zend_list_delete(sftp_rsrc);\n        efree(data);\n    }\n    php_url_free(resource);\n\n    return stream;\n}\n/* }}} */\n\n/* **********************\n * SFTP Directory Ops *\n ********************** */\n\n/* {{{ php_ssh2_sftp_dirstream_read\n */\nstatic ssize_t php_ssh2_sftp_dirstream_read(php_stream *stream, char *buf, size_t count) {\n    php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data *) stream->abstract;\n    php_stream_dirent *ent = (php_stream_dirent *) buf;\n    auto session = data->session;\n    int bytesread = libssh2_sftp_readdir(data->handle, ent->d_name, sizeof(ent->d_name) - 1, NULL);\n    zend_string *basename;\n\n    if (bytesread <= 0) {\n        return 0;\n    }\n    ent->d_name[bytesread] = 0;\n\n    basename = php_basename(ent->d_name, bytesread, NULL, 0);\n    if (!basename) {\n        return 0;\n    }\n\n    bytesread = MIN(sizeof(ent->d_name) - 1, basename->len);\n    memcpy(ent->d_name, basename->val, bytesread);\n    ent->d_name[bytesread] = 0;\n    zend_string_release(basename);\n\n    return sizeof(php_stream_dirent);\n}\n/* }}} */\n\n/* {{{ php_ssh2_sftp_dirstream_close\n */\nstatic int php_ssh2_sftp_dirstream_close(php_stream *stream, int close_handle) {\n    php_ssh2_sftp_handle_data *data = (php_ssh2_sftp_handle_data *) stream->abstract;\n    auto session = data->session;\n\n    libssh2_sftp_close(data->handle);\n    zend_list_delete(data->sftp_rsrc);\n    efree(data);\n\n    return 0;\n}\n/* }}} */\n\nstatic php_stream_ops php_ssh2_sftp_dirstream_ops = {\n    NULL, /* write */\n    php_ssh2_sftp_dirstream_read,\n    php_ssh2_sftp_dirstream_close,\n    NULL, /* flush */\n    PHP_SSH2_SFTP_DIRSTREAM_NAME,\n    NULL, /* seek */\n    NULL, /* cast */\n    NULL, /* fstat */\n    NULL, /* set_option */\n};\n\n/* {{{ php_ssh2_sftp_dirstream_opener\n */\nstatic php_stream *php_ssh2_sftp_dirstream_opener(php_stream_wrapper *wrapper,\n                                                  const char *filename,\n                                                  const char *mode,\n                                                  int options,\n                                                  zend_string **opened_path,\n                                                  php_stream_context *context STREAMS_DC) {\n    php_ssh2_sftp_handle_data *data;\n    LIBSSH2_SESSION *session = NULL;\n    LIBSSH2_SFTP *sftp = NULL;\n    LIBSSH2_SFTP_HANDLE *handle;\n    php_stream *stream;\n    zend_resource *rsrc = NULL, *sftp_rsrc = NULL;\n    php_url *resource;\n\n    resource = php_ssh2_fopen_wraper_parse_path(filename, \"sftp\", context, &session, &rsrc, &sftp, &sftp_rsrc);\n    if (!resource || !session || !sftp) {\n        return NULL;\n    }\n\n    handle = libssh2_sftp_opendir(sftp, ZSTR_VAL(resource->path));\n    if (!handle) {\n        php_error_docref(NULL, E_WARNING, \"Unable to open %s on remote host\", filename);\n        php_url_free(resource);\n        zend_list_delete(sftp_rsrc);\n        return NULL;\n    }\n\n    data = (php_ssh2_sftp_handle_data *) emalloc(sizeof(php_ssh2_sftp_handle_data));\n    data->handle = handle;\n    data->sftp_rsrc = sftp_rsrc;\n\n    stream = php_stream_alloc(&php_ssh2_sftp_dirstream_ops, data, 0, mode);\n    if (!stream) {\n        libssh2_sftp_close(handle);\n        zend_list_delete(sftp_rsrc);\n        efree(data);\n    }\n    php_url_free(resource);\n\n    return stream;\n}\n/* }}} */\n\n/* ****************\n * SFTP Wrapper *\n **************** */\n\n/* {{{ php_ssh2_sftp_urlstat\n */\nstatic int php_ssh2_sftp_urlstat(\n    php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context) {\n    LIBSSH2_SFTP_ATTRIBUTES attrs;\n    LIBSSH2_SESSION *session = NULL;\n    LIBSSH2_SFTP *sftp = NULL;\n    zend_resource *rsrc = NULL, *sftp_rsrc = NULL;\n    php_url *resource;\n\n    resource = php_ssh2_fopen_wraper_parse_path(url, \"sftp\", context, &session, &rsrc, &sftp, &sftp_rsrc);\n    if (!resource || !session || !sftp || !resource->path) {\n        return -1;\n    }\n\n    if (libssh2_sftp_stat_ex(sftp,\n                             ZSTR_VAL(resource->path),\n                             ZSTR_LEN(resource->path),\n                             (flags & PHP_STREAM_URL_STAT_LINK) ? LIBSSH2_SFTP_LSTAT : LIBSSH2_SFTP_STAT,\n                             &attrs)) {\n        php_url_free(resource);\n        // zend_list_delete(sftp_rsrcid);\n        return -1;\n    }\n\n    php_url_free(resource);\n\n    /* parse_path addrefs the resource, but we're not holding on to it so we have to delref it before we leave */\n    // zend_list_delete(sftp_rsrcid);\n\n    return php_ssh2_sftp_attr2ssb(ssb, &attrs);\n}\n/* }}} */\n\n/* {{{ php_ssh2_sftp_unlink\n */\nstatic int php_ssh2_sftp_unlink(php_stream_wrapper *wrapper,\n                                const char *url,\n                                int options,\n                                php_stream_context *context) {\n    LIBSSH2_SESSION *session = NULL;\n    LIBSSH2_SFTP *sftp = NULL;\n    zend_resource *rsrc = NULL, *sftp_rsrc = NULL;\n    php_url *resource;\n    int result;\n\n    resource = php_ssh2_fopen_wraper_parse_path(url, \"sftp\", context, &session, &rsrc, &sftp, &sftp_rsrc);\n    if (!resource || !session || !sftp || !resource->path) {\n        if (resource) {\n            php_url_free(resource);\n        }\n        return 0;\n    }\n\n    result = libssh2_sftp_unlink(sftp, ZSTR_VAL(resource->path));\n    php_url_free(resource);\n\n    // zend_list_delete(sftp_rsrcid);\n\n    /* libssh2 uses 0 for success and the streams API uses 0 for failure, so invert */\n    return (result == 0) ? -1 : 0;\n}\n/* }}} */\n\n/* {{{ php_ssh2_sftp_rename\n */\nstatic int php_ssh2_sftp_rename(\n    php_stream_wrapper *wrapper, const char *url_from, const char *url_to, int options, php_stream_context *context) {\n    LIBSSH2_SESSION *session = NULL;\n    LIBSSH2_SFTP *sftp = NULL;\n    zend_resource *rsrc = NULL, *sftp_rsrc = NULL;\n    php_url *resource, *resource_to;\n    int result;\n\n    if (strncmp(url_from, \"ssh2.sftp://\", sizeof(\"ssh2.sftp://\") - 1) ||\n        strncmp(url_to, \"ssh2.sftp://\", sizeof(\"ssh2.sftp://\") - 1)) {\n        return 0;\n    }\n\n    resource_to = php_url_parse(url_to);\n    if (!resource_to || !resource_to->path) {\n        if (resource_to) {\n            php_url_free(resource_to);\n        }\n        return 0;\n    }\n\n    resource = php_ssh2_fopen_wraper_parse_path(url_from, \"sftp\", context, &session, &rsrc, &sftp, &sftp_rsrc);\n    if (!resource || !session || !sftp || !resource->path) {\n        if (resource) {\n            php_url_free(resource);\n        }\n        php_url_free(resource_to);\n        return 0;\n    }\n\n    result = libssh2_sftp_rename(sftp, ZSTR_VAL(resource->path), ZSTR_VAL(resource_to->path));\n    php_url_free(resource);\n    php_url_free(resource_to);\n\n    // zend_list_delete(sftp_rsrcid);\n\n    /* libssh2 uses 0 for success and the streams API uses 0 for failure, so invert */\n    return (result == 0) ? -1 : 0;\n}\n/* }}} */\n\n/* {{{ php_ssh2_sftp_mkdir\n */\nstatic int php_ssh2_sftp_mkdir(\n    php_stream_wrapper *wrapper, const char *url, int mode, int options, php_stream_context *context) {\n    LIBSSH2_SESSION *session = NULL;\n    LIBSSH2_SFTP *sftp = NULL;\n    zend_resource *rsrc = NULL, *sftp_rsrc = NULL;\n    php_url *resource;\n    int result;\n\n    resource = php_ssh2_fopen_wraper_parse_path(url, \"sftp\", context, &session, &rsrc, &sftp, &sftp_rsrc);\n    if (!resource || !session || !sftp || !resource->path) {\n        if (resource) {\n            php_url_free(resource);\n        }\n        return 0;\n    }\n\n    if (options & PHP_STREAM_MKDIR_RECURSIVE) {\n        /* Just attempt to make every directory, some will fail, but we only care about the last success/failure */\n        char *p = ZSTR_VAL(resource->path);\n        while ((p = strchr(p + 1, '/'))) {\n            libssh2_sftp_mkdir_ex(sftp, ZSTR_VAL(resource->path), p - ZSTR_VAL(resource->path), mode);\n        }\n    }\n\n    result = libssh2_sftp_mkdir(sftp, ZSTR_VAL(resource->path), mode);\n    php_url_free(resource);\n\n    // zend_list_delete(sftp_rsrcid);\n\n    /* libssh2 uses 0 for success and the streams API uses 0 for failure, so invert */\n    return (result == 0) ? -1 : 0;\n}\n/* }}} */\n\n/* {{{ php_ssh2_sftp_rmdir\n */\nstatic int php_ssh2_sftp_rmdir(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context) {\n    LIBSSH2_SESSION *session = NULL;\n    LIBSSH2_SFTP *sftp = NULL;\n    zend_resource *rsrc = NULL, *sftp_rsrc = NULL;\n    php_url *resource;\n    int result;\n\n    resource = php_ssh2_fopen_wraper_parse_path(url, \"sftp\", context, &session, &rsrc, &sftp, &sftp_rsrc);\n    if (!resource || !session || !sftp || !resource->path) {\n        if (resource) {\n            php_url_free(resource);\n        }\n        return 0;\n    }\n\n    result = libssh2_sftp_rmdir(sftp, ZSTR_VAL(resource->path));\n    php_url_free(resource);\n\n    // zend_list_delete(sftp_rsrcid);\n\n    /* libssh2 uses 0 for success and the streams API uses 0 for failure, so invert */\n    return (result == 0) ? -1 : 0;\n}\n/* }}} */\n\nstatic php_stream_wrapper_ops php_ssh2_sftp_wrapper_ops = {\n    php_ssh2_sftp_stream_opener,\n    NULL, /* close */\n    NULL, /* stat */\n    php_ssh2_sftp_urlstat,\n    php_ssh2_sftp_dirstream_opener,\n    PHP_SSH2_SFTP_WRAPPER_NAME,\n    php_ssh2_sftp_unlink,\n    php_ssh2_sftp_rename,\n    php_ssh2_sftp_mkdir,\n    php_ssh2_sftp_rmdir,\n};\n\nphp_stream_wrapper php_ssh2_sftp_wrapper = {\n    &php_ssh2_sftp_wrapper_ops,\n    NULL,\n    1,\n};\n\n/* *****************\n * Userspace API *\n ***************** */\n\n/* {{{ proto resource ssh2_sftp(resource session)\n * Request the SFTP subsystem from an already connected SSH2 server\n */\nPHP_FUNCTION(ssh2_sftp) {\n    LIBSSH2_SESSION *session;\n    LIBSSH2_SFTP *sftp;\n    php_ssh2_sftp_data *data;\n    zval *zsession;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"r\", &zsession) == FAILURE) {\n        return;\n    }\n\n    if ((session = (LIBSSH2_SESSION *) zend_fetch_resource(\n             Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    sftp = libssh2_sftp_init(session);\n    if (!sftp) {\n        char *sess_err;\n        libssh2_session_last_error(session, &sess_err, NULL, 0);\n        php_error_docref(NULL, E_WARNING, \"Unable to startup SFTP subsystem: %s\", sess_err);\n        RETURN_FALSE;\n    }\n\n    data = (php_ssh2_sftp_data *) emalloc(sizeof(php_ssh2_sftp_data));\n    data->session = session;\n    data->sftp = sftp;\n    data->session_rsrc = Z_RES_P(zsession);\n    Z_ADDREF_P(zsession);\n\n    RETURN_RES(zend_register_resource(data, le_ssh2_sftp));\n}\n/* }}} */\n\n/* Much of the stuff below can be done via wrapper ops as of PHP5, but is included here for PHP 4.3 users */\n\n/* {{{ proto bool ssh2_sftp_rename(resource sftp, string from, string to)\n */\nPHP_FUNCTION(ssh2_sftp_rename) {\n    php_ssh2_sftp_data *data;\n    zval *zsftp;\n    zend_string *src, *dst;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rSS\", &zsftp, &src, &dst) == FAILURE) {\n        return;\n    }\n\n    if ((data = (php_ssh2_sftp_data *) zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) ==\n        NULL) {\n        RETURN_FALSE;\n    }\n\n    auto session = data->session;\n\n    RETURN_BOOL(!libssh2_sftp_rename_ex(\n        data->sftp,\n        src->val,\n        src->len,\n        dst->val,\n        dst->len,\n        LIBSSH2_SFTP_RENAME_OVERWRITE | LIBSSH2_SFTP_RENAME_ATOMIC | LIBSSH2_SFTP_RENAME_NATIVE));\n}\n/* }}} */\n\n/* {{{ proto bool ssh2_sftp_unlink(resource sftp, string filename)\n */\nPHP_FUNCTION(ssh2_sftp_unlink) {\n    php_ssh2_sftp_data *data;\n    zval *zsftp;\n    zend_string *filename;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rS\", &zsftp, &filename) == FAILURE) {\n        return;\n    }\n\n    if ((data = (php_ssh2_sftp_data *) zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) ==\n        NULL) {\n        RETURN_FALSE;\n    }\n\n    auto session = data->session;\n    RETURN_BOOL(!libssh2_sftp_unlink_ex(data->sftp, filename->val, filename->len));\n}\n/* }}} */\n\n/* {{{ proto bool ssh2_sftp_mkdir(resource sftp, string dirname[, int mode[, int recursive]])\n */\nPHP_FUNCTION(ssh2_sftp_mkdir) {\n    php_ssh2_sftp_data *data;\n    zval *zsftp;\n    zend_string *dirname;\n    zend_long mode = 0777;\n    zend_bool recursive = 0;\n    const char *p;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rS|lb\", &zsftp, &dirname, &mode, &recursive) == FAILURE) {\n        return;\n    }\n\n    if (!dirname) {\n        RETURN_FALSE;\n    }\n\n    if ((data = (php_ssh2_sftp_data *) zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) ==\n        NULL) {\n        RETURN_FALSE;\n    }\n\n    auto session = data->session;\n\n    if (recursive) {\n        /* Just attempt to make every directory, some will fail, but we only care about the last success/failure */\n        p = dirname->val;\n        while ((p = strchr(p + 1, '/'))) {\n            if ((p - dirname->val) + 1 == (long) dirname->len) {\n                break;\n            }\n            libssh2_sftp_mkdir_ex(data->sftp, dirname->val, p - dirname->val, mode);\n        }\n    }\n\n    RETURN_BOOL(!libssh2_sftp_mkdir_ex(data->sftp, dirname->val, dirname->len, mode));\n}\n/* }}} */\n\n/* {{{ proto bool ssh2_sftp_rmdir(resource sftp, string dirname)\n */\nPHP_FUNCTION(ssh2_sftp_rmdir) {\n    php_ssh2_sftp_data *data;\n    zval *zsftp;\n    zend_string *dirname;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rS\", &zsftp, &dirname) == FAILURE) {\n        return;\n    }\n\n    if ((data = (php_ssh2_sftp_data *) zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) ==\n        NULL) {\n        RETURN_FALSE;\n    }\n\n    auto session = data->session;\n    RETURN_BOOL(!libssh2_sftp_rmdir_ex(data->sftp, dirname->val, dirname->len));\n}\n/* }}} */\n\n/* {{{ proto bool ssh2_sftp_chmod(resource sftp, string filename, int mode)\n */\nPHP_FUNCTION(ssh2_sftp_chmod) {\n    php_ssh2_sftp_data *data;\n    zval *zsftp;\n    zend_string *filename;\n    zend_long mode;\n    LIBSSH2_SFTP_ATTRIBUTES attrs;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rSl\", &zsftp, &filename, &mode) == FAILURE) {\n        return;\n    }\n\n    if (ZSTR_LEN(filename) < 1) {\n        RETURN_FALSE;\n    }\n\n    if ((data = (php_ssh2_sftp_data *) zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) ==\n        NULL) {\n        RETURN_FALSE;\n    }\n\n    attrs.permissions = mode;\n    attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;\n\n    auto session = data->session;\n\n    RETURN_BOOL(!libssh2_sftp_stat_ex(data->sftp, filename->val, filename->len, LIBSSH2_SFTP_SETSTAT, &attrs));\n}\n/* }}} */\n\n/* {{{ php_ssh2_sftp_stat_func\n * In PHP4.3 this is the only way to request stat into, in PHP >= 5 you can use the fopen wrapper approach\n * Both methods will return identical structures\n * (well, the other one will include other values set to 0 but they don't count)\n */\nstatic void php_ssh2_sftp_stat_func(INTERNAL_FUNCTION_PARAMETERS, int stat_type) {\n    php_ssh2_sftp_data *data;\n    LIBSSH2_SFTP_ATTRIBUTES attrs;\n    zval *zsftp;\n    zend_string *path;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rS\", &zsftp, &path) == FAILURE) {\n        return;\n    }\n\n    if ((data = (php_ssh2_sftp_data *) zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) ==\n        NULL) {\n        RETURN_FALSE;\n    }\n\n    auto session = data->session;\n\n    if (libssh2_sftp_stat_ex(data->sftp, path->val, path->len, stat_type, &attrs)) {\n        php_error_docref(NULL, E_WARNING, \"Failed to stat remote file\");\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n\n    if (attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) {\n        add_index_long(return_value, 7, attrs.filesize);\n        add_assoc_long(return_value, \"size\", attrs.filesize);\n    }\n    if (attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID) {\n        add_index_long(return_value, 4, attrs.uid);\n        add_assoc_long(return_value, \"uid\", attrs.uid);\n\n        add_index_long(return_value, 5, attrs.gid);\n        add_assoc_long(return_value, \"gid\", attrs.gid);\n    }\n    if (attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {\n        add_index_long(return_value, 2, attrs.permissions);\n        add_assoc_long(return_value, \"mode\", attrs.permissions);\n    }\n    if (attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) {\n        add_index_long(return_value, 8, attrs.atime);\n        add_assoc_long(return_value, \"atime\", attrs.atime);\n\n        add_index_long(return_value, 9, attrs.mtime);\n        add_assoc_long(return_value, \"mtime\", attrs.mtime);\n    }\n}\n/* }}} */\n\n/* {{{ proto array ssh2_sftp_stat(resource sftp, string path)\n */\nPHP_FUNCTION(ssh2_sftp_stat) {\n    php_ssh2_sftp_stat_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, LIBSSH2_SFTP_STAT);\n}\n/* }}} */\n\n/* {{{ proto array ssh2_sftp_lstat(resource sftp, string path)\n */\nPHP_FUNCTION(ssh2_sftp_lstat) {\n    php_ssh2_sftp_stat_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, LIBSSH2_SFTP_LSTAT);\n}\n/* }}} */\n\n/* {{{ proto bool ssh2_sftp_symlink(resource sftp, string target, string link)\n */\nPHP_FUNCTION(ssh2_sftp_symlink) {\n    php_ssh2_sftp_data *data;\n    zval *zsftp;\n    zend_string *targ, *link;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rSS\", &zsftp, &targ, &link) == FAILURE) {\n        return;\n    }\n\n    if ((data = (php_ssh2_sftp_data *) zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) ==\n        NULL) {\n        RETURN_FALSE;\n    }\n\n    auto session = data->session;\n    RETURN_BOOL(!libssh2_sftp_symlink_ex(data->sftp, targ->val, targ->len, link->val, link->len, LIBSSH2_SFTP_SYMLINK));\n}\n/* }}} */\n\n/* {{{ proto string ssh2_sftp_readlink(resource sftp, string link)\n */\nPHP_FUNCTION(ssh2_sftp_readlink) {\n    php_ssh2_sftp_data *data;\n    zval *zsftp;\n    zend_string *link;\n    int targ_len = 0;\n    char targ[8192];\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rS\", &zsftp, &link) == FAILURE) {\n        return;\n    }\n\n    if ((data = (php_ssh2_sftp_data *) zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) ==\n        NULL) {\n        RETURN_FALSE;\n    }\n\n    auto session = data->session;\n\n    if ((targ_len = libssh2_sftp_symlink_ex(data->sftp, link->val, link->len, targ, 8192, LIBSSH2_SFTP_READLINK)) < 0) {\n        php_error_docref(NULL, E_WARNING, \"Unable to read link '%s'\", ZSTR_VAL(link));\n        RETURN_FALSE;\n    }\n\n    RETURN_STRINGL(targ, targ_len);\n}\n/* }}} */\n\n/* {{{ proto string ssh2_sftp_realpath(resource sftp, string filename)\n */\nPHP_FUNCTION(ssh2_sftp_realpath) {\n    php_ssh2_sftp_data *data;\n    zval *zsftp;\n    zend_string *link;\n    int targ_len = 0;\n    char targ[8192];\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(), \"rS\", &zsftp, &link) == FAILURE) {\n        return;\n    }\n\n    if ((data = (php_ssh2_sftp_data *) zend_fetch_resource(Z_RES_P(zsftp), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp)) ==\n        NULL) {\n        RETURN_FALSE;\n    }\n\n    if (data->session_rsrc->ptr == NULL) {\n        RETURN_FALSE;\n    }\n\n    auto session = data->session;\n\n    if ((targ_len = libssh2_sftp_symlink_ex(data->sftp, link->val, link->len, targ, 8192, LIBSSH2_SFTP_REALPATH)) < 0) {\n        php_error_docref(NULL, E_WARNING, \"Unable to resolve realpath for '%s'\", link->val);\n        RETURN_FALSE;\n    }\n\n    RETURN_STRINGL(targ, targ_len);\n}\n/* }}} */\n\n/*\n * Local variables:\n * tab-width: 4\n * c-basic-offset: 4\n * indent-tabs-mode: t\n * End:\n */\n"
  },
  {
    "path": "thirdparty/php/standard/proc_open.cc",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | Copyright (c) The PHP Group                                          |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | https://www.php.net/license/3_01.txt                                 |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Author: Wez Furlong <wez@thebrainroom.com>                           |\n   +----------------------------------------------------------------------+\n */\n\n#include \"thirdparty/php/standard/proc_open.h\"\n\nusing swoole::Coroutine;\nusing swoole::PHPCoroutine;\nusing swoole::coroutine::System;\n\n#ifdef HAVE_SYS_WAIT_H\n#include <sys/wait.h>\n#endif\n\n#ifdef HAVE_FCNTL_H\n#include <fcntl.h>\n#endif\n\n#ifdef HAVE_PTY_H\n#include <pty.h>\n#elif defined(__FreeBSD__)\n/* FreeBSD defines `openpty` in <libutil.h> */\n#include <libutil.h>\n#elif defined(__NetBSD__) || defined(__DragonFly__)\n/* On recent NetBSD/DragonFlyBSD releases the emalloc, estrdup ... calls had been introduced in libutil */\n#if defined(__NetBSD__)\n#include <sys/termios.h>\n#else\n#include <termios.h>\n#endif\nextern int openpty(int *, int *, char *, struct termios *, struct winsize *);\n#elif defined(__sun)\n#include <termios.h>\n#else\n/* Mac OS X (and some BSDs) define `openpty` in <util.h> */\n#include <util.h>\n#endif\n\nstatic int le_proc_open;\nstatic const char *le_proc_name = \"process/coroutine\";\n\nstatic pid_t _co_waitpid(pid_t __pid, int *__stat_loc, int __options) {\n#ifdef SW_THREAD\n    return System::waitpid_safe(__pid, __stat_loc, __options);\n#else\n    return System::waitpid(__pid, __stat_loc, __options);\n#endif\n}\n\n/* {{{ _php_array_to_envp\n * Process the `environment` argument to `proc_open`\n * Convert into data structures which can be passed to underlying OS APIs like `exec` on POSIX or\n * `CreateProcessW` on Win32 */\nstatic sw_php_process_env _php_array_to_envp(zval *environment) {\n    zval *element;\n    sw_php_process_env env;\n#ifndef PHP_WIN32\n    char **ep;\n#endif\n    char *p;\n    size_t sizeenv = 0;\n    HashTable *env_hash; /* temporary PHP array used as helper */\n\n    memset(&env, 0, sizeof(env));\n\n    if (!environment) {\n        return env;\n    }\n\n    uint32_t cnt = zend_hash_num_elements(Z_ARRVAL_P(environment));\n\n    if (cnt < 1) {\n#ifndef PHP_WIN32\n        env.envarray = (char **) ecalloc(1, sizeof(char *));\n#endif\n        env.envp = (char *) ecalloc(4, 1);\n        return env;\n    }\n\n    ALLOC_HASHTABLE(env_hash);\n    zend_hash_init(env_hash, cnt, NULL, NULL, 0);\n\n    void *_key, *_str;\n    zend_string *key, *str;\n    /* first, we have to get the size of all the elements in the hash */\n    ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(environment), _key, element) {\n        key = (zend_string *) _key;\n        str = zval_get_string(element);\n\n        if (ZSTR_LEN(str) == 0) {\n            zend_string_release_ex(str, 0);\n            continue;\n        }\n\n        sizeenv += ZSTR_LEN(str) + 1;\n\n        if (key && ZSTR_LEN(key)) {\n            sizeenv += ZSTR_LEN(key) + 1;\n            zend_hash_add_ptr(env_hash, key, str);\n        } else {\n            zend_hash_next_index_insert_ptr(env_hash, str);\n        }\n    }\n    ZEND_HASH_FOREACH_END();\n\n#ifndef PHP_WIN32\n    ep = env.envarray = (char **) ecalloc(cnt + 1, sizeof(char *));\n#endif\n    p = env.envp = (char *) ecalloc(sizeenv + 4, 1);\n\n    ZEND_HASH_FOREACH_STR_KEY_PTR(env_hash, _key, _str) {\n        key = (zend_string *) _key;\n        str = (zend_string *) _str;\n#ifndef PHP_WIN32\n        *ep = p;\n        ++ep;\n#endif\n\n        if (key) {\n            memcpy(p, ZSTR_VAL(key), ZSTR_LEN(key));\n            p += ZSTR_LEN(key);\n            *p++ = '=';\n        }\n\n        memcpy(p, ZSTR_VAL(str), ZSTR_LEN(str));\n        p += ZSTR_LEN(str);\n        *p++ = '\\0';\n        zend_string_release_ex(str, 0);\n    }\n    ZEND_HASH_FOREACH_END();\n\n    assert((uint32_t) (p - env.envp) <= sizeenv);\n\n    zend_hash_destroy(env_hash);\n    FREE_HASHTABLE(env_hash);\n\n    return env;\n}\n/* }}} */\n\n/* {{{ _php_free_envp\n * Free the structures allocated by `_php_array_to_envp` */\nstatic void _php_free_envp(sw_php_process_env env) {\n    if (env.envarray) {\n        efree(env.envarray);\n    }\n    if (env.envp) {\n        efree(env.envp);\n    }\n}\n/* }}} */\n\nstatic void proc_co_rsrc_dtor(zend_resource *rsrc) {\n    sw_php_process_handle *proc = (sw_php_process_handle *) rsrc->ptr;\n    int wstatus = 0;\n\n    /* Close all handles to avoid a deadlock */\n    for (int i = 0; i < proc->npipes; i++) {\n        if (proc->pipes[i] != NULL) {\n            GC_DELREF(proc->pipes[i]);\n            zend_list_close(proc->pipes[i]);\n            proc->pipes[i] = NULL;\n        }\n    }\n\n    if (proc->running) {\n        _co_waitpid(proc->child, &wstatus, 0);\n    }\n    if (proc->wstatus) {\n        *proc->wstatus = wstatus;\n    }\n\n    _php_free_envp(proc->env);\n    efree(proc->pipes);\n    zend_string_release_ex(proc->command, false);\n    efree(proc);\n}\n/* }}} */\n\n/* {{{ PHP_MINIT_FUNCTION(proc_open) */\nvoid swoole_proc_open_init(int module_number) {\n    le_proc_open = zend_register_list_destructors_ex(proc_co_rsrc_dtor, NULL, le_proc_name, module_number);\n}\n/* }}} */\n\n/* {{{ Kill a process opened by `proc_open` */\nPHP_FUNCTION(swoole_proc_terminate) {\n    zval *zproc;\n    sw_php_process_handle *proc;\n    zend_long sig_no = SIGTERM;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_RESOURCE(zproc)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG(sig_no)\n    ZEND_PARSE_PARAMETERS_END();\n\n    proc = (sw_php_process_handle *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open);\n    if (proc == NULL) {\n        RETURN_THROWS();\n    }\n\n#ifdef PHP_WIN32\n    RETURN_BOOL(TerminateProcess(proc->childHandle, 255));\n#else\n    RETURN_BOOL(kill(proc->child, sig_no) == 0);\n#endif\n}\n/* }}} */\n\n/* {{{ Close a process opened by `proc_open` */\nPHP_FUNCTION(swoole_proc_close) {\n    zval *zproc;\n    int wstatus = 0;\n    sw_php_process_handle *proc;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_RESOURCE(zproc)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((proc = (sw_php_process_handle *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open)) == NULL) {\n        RETURN_THROWS();\n    }\n    proc->wstatus = &wstatus;\n    zend_list_close(Z_RES_P(zproc));\n    RETURN_LONG(wstatus);\n}\n/* }}} */\n\n/* {{{ Get information about a process opened by `proc_open` */\nPHP_FUNCTION(swoole_proc_get_status) {\n    zval *zproc;\n    sw_php_process_handle *proc;\n    int wstatus;\n    pid_t wait_pid;\n    bool running = 1, signaled = 0, stopped = 0;\n    int exitcode = -1, termsig = 0, stopsig = 0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_RESOURCE(zproc)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((proc = (sw_php_process_handle *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open)) == NULL) {\n        RETURN_THROWS();\n    }\n\n    array_init(return_value);\n    add_assoc_str(return_value, \"command\", zend_string_copy(proc->command));\n    add_assoc_long(return_value, \"pid\", (zend_long) proc->child);\n\n    errno = 0;\n    wait_pid = _co_waitpid(proc->child, &wstatus, WNOHANG | WUNTRACED);\n\n    if (wait_pid == proc->child) {\n        if (WIFEXITED(wstatus)) {\n            running = 0;\n            exitcode = WEXITSTATUS(wstatus);\n        }\n        if (WIFSIGNALED(wstatus)) {\n            running = 0;\n            signaled = 1;\n            termsig = WTERMSIG(wstatus);\n        }\n        if (WIFSTOPPED(wstatus)) {\n            stopped = 1;\n            stopsig = WSTOPSIG(wstatus);\n        }\n    } else if (wait_pid == -1) {\n        /* The only error which could occur here is ECHILD, which means that the PID we were\n         * looking for either does not exist or is not a child of this process */\n        running = 0;\n    }\n\n    proc->running = running;\n\n    add_assoc_bool(return_value, \"running\", running);\n    add_assoc_bool(return_value, \"signaled\", signaled);\n    add_assoc_bool(return_value, \"stopped\", stopped);\n    add_assoc_long(return_value, \"exitcode\", exitcode);\n    add_assoc_long(return_value, \"termsig\", termsig);\n    add_assoc_long(return_value, \"stopsig\", stopsig);\n}\n/* }}} */\n\n#ifdef PHP_WIN32\n\n/* We use this to allow child processes to inherit handles\n * One static instance can be shared and used for all calls to `proc_open`, since the values are\n * never changed */\nSECURITY_ATTRIBUTES php_proc_open_security = {\n    .nLength = sizeof(SECURITY_ATTRIBUTES), .lpSecurityDescriptor = NULL, .bInheritHandle = TRUE};\n\n#define pipe(pair) (CreatePipe(&pair[0], &pair[1], &php_proc_open_security, 0) ? 0 : -1)\n\n#define COMSPEC_NT \"cmd.exe\"\n\nstatic inline HANDLE dup_handle(HANDLE src, BOOL inherit, BOOL closeorig) {\n    HANDLE copy, self = GetCurrentProcess();\n\n    if (!DuplicateHandle(\n            self, src, self, &copy, 0, inherit, DUPLICATE_SAME_ACCESS | (closeorig ? DUPLICATE_CLOSE_SOURCE : 0)))\n        return NULL;\n    return copy;\n}\n\nstatic inline HANDLE dup_fd_as_handle(int fd) {\n    return dup_handle((HANDLE) _get_osfhandle(fd), TRUE, FALSE);\n}\n\n#define close_descriptor(fd) CloseHandle(fd)\n#else /* !PHP_WIN32 */\n#define close_descriptor(fd) close(fd)\n#endif\n\n/* Determines the type of a descriptor item. */\ntypedef enum _descriptor_type { DESCRIPTOR_TYPE_STD, DESCRIPTOR_TYPE_PIPE, DESCRIPTOR_TYPE_SOCKET } descriptor_type;\n\n/* One instance of this struct is created for each item in `$descriptorspec` argument to `proc_open`\n * They are used within `proc_open` and freed before it returns */\ntypedef struct _descriptorspec_item {\n    int index; /* desired FD # in child process */\n    descriptor_type type;\n    php_file_descriptor_t childend;  /* FD # opened for use in child\n                                      * (will be copied to `index` in child) */\n    php_file_descriptor_t parentend; /* FD # opened for use in parent\n                                      * (for pipes only; will be 0 otherwise) */\n    int mode_flags;                  /* mode for opening FDs: r/o, r/w, binary (on Win32), etc */\n} descriptorspec_item;\n\nstatic zend_string *get_valid_arg_string(zval *zv, int elem_num) {\n    zend_string *str = zval_get_string(zv);\n    if (!str) {\n        return NULL;\n    }\n\n    if (elem_num == 1 && ZSTR_LEN(str) == 0) {\n        zend_value_error(\"First element must contain a non-empty program name\");\n        zend_string_release(str);\n        return NULL;\n    }\n\n    if (strlen(ZSTR_VAL(str)) != ZSTR_LEN(str)) {\n        zend_value_error(\"Command array element %d contains a null byte\", elem_num);\n        zend_string_release(str);\n        return NULL;\n    }\n\n    return str;\n}\n\n#ifdef PHP_WIN32\nstatic void append_backslashes(smart_str *str, size_t num_bs) {\n    for (size_t i = 0; i < num_bs; i++) {\n        smart_str_appendc(str, '\\\\');\n    }\n}\n\n/* See https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments */\nstatic void append_win_escaped_arg(smart_str *str, zend_string *arg) {\n    size_t num_bs = 0;\n\n    smart_str_appendc(str, '\"');\n    for (size_t i = 0; i < ZSTR_LEN(arg); ++i) {\n        char c = ZSTR_VAL(arg)[i];\n        if (c == '\\\\') {\n            num_bs++;\n            continue;\n        }\n\n        if (c == '\"') {\n            /* Backslashes before \" need to be doubled. */\n            num_bs = num_bs * 2 + 1;\n        }\n        append_backslashes(str, num_bs);\n        smart_str_appendc(str, c);\n        num_bs = 0;\n    }\n    append_backslashes(str, num_bs * 2);\n    smart_str_appendc(str, '\"');\n}\n\nstatic zend_string *create_win_command_from_args(HashTable *args) {\n    smart_str str = {0};\n    zval *arg_zv;\n    bool is_prog_name = 1;\n    int elem_num = 0;\n\n    ZEND_HASH_FOREACH_VAL(args, arg_zv) {\n        zend_string *arg_str = get_valid_arg_string(arg_zv, ++elem_num);\n        if (!arg_str) {\n            smart_str_free(&str);\n            return NULL;\n        }\n\n        if (!is_prog_name) {\n            smart_str_appendc(&str, ' ');\n        }\n\n        append_win_escaped_arg(&str, arg_str);\n\n        is_prog_name = 0;\n        zend_string_release(arg_str);\n    }\n    ZEND_HASH_FOREACH_END();\n    smart_str_0(&str);\n    return str.s;\n}\n\n/* Get a boolean option from the `other_options` array which can be passed to `proc_open`.\n * (Currently, all options apply on Windows only.) */\nstatic bool get_option(zval *other_options, char *opt_name, size_t opt_name_len) {\n    HashTable *opt_ary = Z_ARRVAL_P(other_options);\n    zval *item = zend_hash_str_find_deref(opt_ary, opt_name, opt_name_len);\n    return item != NULL && (Z_TYPE_P(item) == IS_TRUE || ((Z_TYPE_P(item) == IS_LONG) && Z_LVAL_P(item)));\n}\n\n/* Initialize STARTUPINFOW struct, used on Windows when spawning a process.\n * Ref: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfow */\nstatic void init_startup_info(STARTUPINFOW *si, descriptorspec_item *descriptors, int ndesc) {\n    memset(si, 0, sizeof(STARTUPINFOW));\n    si->cb = sizeof(STARTUPINFOW);\n    si->dwFlags = STARTF_USESTDHANDLES;\n\n    si->hStdInput = GetStdHandle(STD_INPUT_HANDLE);\n    si->hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);\n    si->hStdError = GetStdHandle(STD_ERROR_HANDLE);\n\n    /* redirect stdin/stdout/stderr if requested */\n    for (int i = 0; i < ndesc; i++) {\n        switch (descriptors[i].index) {\n        case 0:\n            si->hStdInput = descriptors[i].childend;\n            break;\n        case 1:\n            si->hStdOutput = descriptors[i].childend;\n            break;\n        case 2:\n            si->hStdError = descriptors[i].childend;\n            break;\n        }\n    }\n}\n\nstatic void init_process_info(PROCESS_INFORMATION *pi) {\n    memset(&pi, 0, sizeof(pi));\n}\n\nstatic zend_result convert_command_to_use_shell(wchar_t **cmdw, size_t cmdw_len) {\n    size_t len = sizeof(COMSPEC_NT) + sizeof(\" /s /c \") + cmdw_len + 3;\n    wchar_t *cmdw_shell = (wchar_t *) malloc(len * sizeof(wchar_t));\n\n    if (cmdw_shell == NULL) {\n        php_error_docref(NULL, E_WARNING, \"Command conversion failed\");\n        return FAILURE;\n    }\n\n    if (_snwprintf(cmdw_shell, len, L\"%hs /s /c \\\"%s\\\"\", COMSPEC_NT, *cmdw) == -1) {\n        free(cmdw_shell);\n        php_error_docref(NULL, E_WARNING, \"Command conversion failed\");\n        return FAILURE;\n    }\n\n    free(*cmdw);\n    *cmdw = cmdw_shell;\n\n    return SUCCESS;\n}\n#endif\n\n/* Convert command parameter array passed as first argument to `proc_open` into command string */\nstatic zend_string *get_command_from_array(HashTable *array, char ***argv, int num_elems) {\n    zval *arg_zv;\n    zend_string *command = NULL;\n    int i = 0;\n\n    *argv = (char **) safe_emalloc(sizeof(char *), num_elems + 1, 0);\n\n    ZEND_HASH_FOREACH_VAL(array, arg_zv) {\n        zend_string *arg_str = get_valid_arg_string(arg_zv, i + 1);\n        if (!arg_str) {\n            /* Terminate with NULL so exit_fail code knows how many entries to free */\n            (*argv)[i] = NULL;\n            if (command != NULL) {\n                zend_string_release_ex(command, false);\n            }\n            return NULL;\n        }\n\n        if (i == 0) {\n            command = zend_string_copy(arg_str);\n        }\n\n        (*argv)[i++] = (char *) estrdup(ZSTR_VAL(arg_str));\n        zend_string_release(arg_str);\n    }\n    ZEND_HASH_FOREACH_END();\n\n    (*argv)[i] = NULL;\n    return command;\n}\n\nstatic descriptorspec_item *alloc_descriptor_array(HashTable *descriptorspec) {\n    uint32_t ndescriptors = zend_hash_num_elements(descriptorspec);\n    return (descriptorspec_item *) ecalloc(sizeof(descriptorspec_item), ndescriptors);\n}\n\nstatic zend_string *get_string_parameter(zval *array, int index, const char *param_name) {\n    zval *array_item;\n    if ((array_item = zend_hash_index_find(Z_ARRVAL_P(array), index)) == NULL) {\n        zend_value_error(\"Missing %s\", param_name);\n        return NULL;\n    }\n    return zval_try_get_string(array_item);\n}\n\nstatic zend_result set_proc_descriptor_to_blackhole(descriptorspec_item *desc) {\n#ifdef PHP_WIN32\n    desc->childend = CreateFileA(\n        \"nul\", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);\n    if (desc->childend == NULL) {\n        php_error_docref(NULL, E_WARNING, \"Failed to open nul\");\n        return FAILURE;\n    }\n#else\n    desc->childend = open(\"/dev/null\", O_RDWR);\n    if (desc->childend < 0) {\n        php_error_docref(NULL, E_WARNING, \"Failed to open /dev/null: %s\", strerror(errno));\n        return FAILURE;\n    }\n#endif\n    return SUCCESS;\n}\n\nstatic zend_result set_proc_descriptor_to_pty(descriptorspec_item *desc, int *master_fd, int *slave_fd) {\n#ifdef HAVE_OPENPTY\n    /* All FDs set to PTY in the child process will go to the slave end of the same PTY.\n     * Likewise, all the corresponding entries in `$pipes` in the parent will all go to the master\n     * end of the same PTY.\n     * If this is the first descriptorspec set to 'pty', find an available PTY and get master and\n     * slave FDs. */\n    if (*master_fd == -1) {\n        if (openpty(master_fd, slave_fd, NULL, NULL, NULL)) {\n            php_error_docref(NULL, E_WARNING, \"Could not open PTY (pseudoterminal): %s\", strerror(errno));\n            return FAILURE;\n        }\n    }\n\n    desc->type = DESCRIPTOR_TYPE_PIPE;\n    desc->childend = dup(*slave_fd);\n    desc->parentend = dup(*master_fd);\n    desc->mode_flags = O_RDWR;\n    return SUCCESS;\n#else\n    php_error_docref(NULL, E_WARNING, \"PTY (pseudoterminal) not supported on this system\");\n    return FAILURE;\n#endif\n}\n\n/* Mark the descriptor close-on-exec, so it won't be inherited by children */\nstatic php_file_descriptor_t make_descriptor_cloexec(php_file_descriptor_t fd) {\n#ifdef PHP_WIN32\n    return dup_handle(fd, FALSE, TRUE);\n#else\n#if defined(F_SETFD) && defined(FD_CLOEXEC)\n    fcntl(fd, F_SETFD, FD_CLOEXEC);\n#endif\n    return fd;\n#endif\n}\n\nstatic zend_result set_proc_descriptor_to_pipe(descriptorspec_item *desc, zend_string *zmode) {\n    php_file_descriptor_t newpipe[2];\n\n    if (pipe(newpipe) != 0) {\n        php_error_docref(NULL, E_WARNING, \"Unable to create pipe %s\", strerror(errno));\n        return FAILURE;\n    }\n\n    desc->type = DESCRIPTOR_TYPE_PIPE;\n\n    if (strncmp(ZSTR_VAL(zmode), \"w\", 1) != 0) {\n        desc->parentend = newpipe[1];\n        desc->childend = newpipe[0];\n        desc->mode_flags = O_WRONLY;\n    } else {\n        desc->parentend = newpipe[0];\n        desc->childend = newpipe[1];\n        desc->mode_flags = O_RDONLY;\n    }\n\n    desc->parentend = make_descriptor_cloexec(desc->parentend);\n\n#ifdef PHP_WIN32\n    if (ZSTR_LEN(zmode) >= 2 && ZSTR_VAL(zmode)[1] == 'b') desc->mode_flags |= O_BINARY;\n#endif\n\n    return SUCCESS;\n}\n\n#ifdef PHP_WIN32\n#define create_socketpair(socks) socketpair_win32(AF_INET, SOCK_STREAM, 0, (socks), 0)\n#else\n#define create_socketpair(socks) socketpair(AF_UNIX, SOCK_STREAM, 0, (socks))\n#endif\n\nstatic zend_result set_proc_descriptor_to_socket(descriptorspec_item *desc) {\n    php_socket_t sock[2];\n\n    if (create_socketpair(sock)) {\n        zend_string *err = php_socket_error_str(php_socket_errno());\n        php_error_docref(NULL, E_WARNING, \"Unable to create socket pair: %s\", ZSTR_VAL(err));\n        zend_string_release(err);\n        return FAILURE;\n    }\n\n    desc->type = DESCRIPTOR_TYPE_SOCKET;\n    desc->parentend = make_descriptor_cloexec((php_file_descriptor_t) sock[0]);\n\n    /* Pass sock[1] to child because it will never use overlapped IO on Windows. */\n    desc->childend = (php_file_descriptor_t) sock[1];\n\n    return SUCCESS;\n}\n\nstatic zend_result set_proc_descriptor_to_file(descriptorspec_item *desc,\n                                               zend_string *file_path,\n                                               zend_string *file_mode) {\n    php_socket_t fd;\n\n    /* try a wrapper */\n    php_stream *stream =\n        php_stream_open_wrapper(ZSTR_VAL(file_path), ZSTR_VAL(file_mode), REPORT_ERRORS | STREAM_WILL_CAST, NULL);\n    if (stream == NULL) {\n        return FAILURE;\n    }\n\n    /* force into an fd */\n    if (php_stream_cast(stream, PHP_STREAM_CAST_RELEASE | PHP_STREAM_AS_FD, (void **) &fd, REPORT_ERRORS) == FAILURE) {\n        return FAILURE;\n    }\n\n#ifdef PHP_WIN32\n    desc->childend = dup_fd_as_handle((int) fd);\n    _close((int) fd);\n\n    /* Simulate the append mode by fseeking to the end of the file\n     * This introduces a potential race condition, but it is the best we can do */\n    if (strchr(ZSTR_VAL(file_mode), 'a')) {\n        SetFilePointer(desc->childend, 0, NULL, FILE_END);\n    }\n#else\n    desc->childend = fd;\n#endif\n    return SUCCESS;\n}\n\nstatic zend_result dup_proc_descriptor(php_file_descriptor_t from, php_file_descriptor_t *to, zend_ulong nindex) {\n#ifdef PHP_WIN32\n    *to = dup_handle(from, TRUE, FALSE);\n    if (*to == NULL) {\n        php_error_docref(NULL, E_WARNING, \"Failed to dup() for descriptor \" ZEND_LONG_FMT, nindex);\n        return FAILURE;\n    }\n#else\n    *to = dup(from);\n    if (*to < 0) {\n        php_error_docref(\n            NULL, E_WARNING, \"Failed to dup() for descriptor \" ZEND_LONG_FMT \": %s\", nindex, strerror(errno));\n        return FAILURE;\n    }\n#endif\n    return SUCCESS;\n}\n\nstatic zend_result redirect_proc_descriptor(\n    descriptorspec_item *desc, int target, descriptorspec_item *descriptors, int ndesc, int nindex) {\n    php_file_descriptor_t redirect_to = PHP_INVALID_FD;\n\n    for (int i = 0; i < ndesc; i++) {\n        if (descriptors[i].index == target) {\n            redirect_to = descriptors[i].childend;\n            break;\n        }\n    }\n\n    if (redirect_to == PHP_INVALID_FD) { /* Didn't find the index we wanted */\n        if (target < 0 || target > 2) {\n            php_error_docref(NULL, E_WARNING, \"Redirection target %d not found\", target);\n            return FAILURE;\n        }\n\n        /* Support referring to a stdin/stdout/stderr pipe adopted from the parent,\n         * which happens whenever an explicit override is not provided. */\n#ifndef PHP_WIN32\n        redirect_to = target;\n#else\n        switch (target) {\n        case 0:\n            redirect_to = GetStdHandle(STD_INPUT_HANDLE);\n            break;\n        case 1:\n            redirect_to = GetStdHandle(STD_OUTPUT_HANDLE);\n            break;\n        case 2:\n            redirect_to = GetStdHandle(STD_ERROR_HANDLE);\n            break;\n            EMPTY_SWITCH_DEFAULT_CASE()\n        }\n#endif\n    }\n\n    return dup_proc_descriptor(redirect_to, &desc->childend, nindex);\n}\n\n/* Process one item from `$descriptorspec` argument to `proc_open` */\nstatic zend_result set_proc_descriptor_from_array(\n    zval *descitem, descriptorspec_item *descriptors, int ndesc, int nindex, int *pty_master_fd, int *pty_slave_fd) {\n    zend_string *ztype = get_string_parameter(descitem, 0, \"handle qualifier\");\n    if (!ztype) {\n        return FAILURE;\n    }\n\n    zend_string *zmode = NULL, *zfile = NULL;\n    zend_result retval = FAILURE;\n\n#if 0\n    if (zend_string_equals_literal(ztype, \"pipe\")) {\n        /* Set descriptor to pipe */\n        zmode = get_string_parameter(descitem, 1, \"mode parameter for 'pipe'\");\n        if (zmode == NULL) {\n            goto finish;\n        }\n        retval = set_proc_descriptor_to_pipe(&descriptors[ndesc], zmode);\n    } else\n#endif\n    if (zend_string_equals_literal(ztype, \"socket\") || zend_string_equals_literal(ztype, \"pipe\")) {\n        /* Set descriptor to socketpair */\n        retval = set_proc_descriptor_to_socket(&descriptors[ndesc]);\n    } else if (zend_string_equals(ztype, ZSTR_KNOWN(ZEND_STR_FILE))) {\n        /* Set descriptor to file */\n        if ((zfile = get_string_parameter(descitem, 1, \"file name parameter for 'file'\")) == NULL) {\n            goto finish;\n        }\n        if ((zmode = get_string_parameter(descitem, 2, \"mode parameter for 'file'\")) == NULL) {\n            goto finish;\n        }\n        retval = set_proc_descriptor_to_file(&descriptors[ndesc], zfile, zmode);\n    } else if (zend_string_equals_literal(ztype, \"redirect\")) {\n        /* Redirect descriptor to whatever another descriptor is set to */\n        zval *ztarget = zend_hash_index_find_deref(Z_ARRVAL_P(descitem), 1);\n        if (!ztarget) {\n            zend_value_error(\"Missing redirection target\");\n            goto finish;\n        }\n        if (Z_TYPE_P(ztarget) != IS_LONG) {\n            zend_value_error(\"Redirection target must be of type int, %s given\", zend_zval_type_name(ztarget));\n            goto finish;\n        }\n\n        retval = redirect_proc_descriptor(&descriptors[ndesc], (int) Z_LVAL_P(ztarget), descriptors, ndesc, nindex);\n    } else if (zend_string_equals(ztype, ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE))) {\n        /* Set descriptor to blackhole (discard all data written) */\n        retval = set_proc_descriptor_to_blackhole(&descriptors[ndesc]);\n    } else if (zend_string_equals_literal(ztype, \"pty\")) {\n        /* Set descriptor to slave end of PTY */\n        retval = set_proc_descriptor_to_pty(&descriptors[ndesc], pty_master_fd, pty_slave_fd);\n    } else {\n        php_error_docref(NULL, E_WARNING, \"%s is not a valid descriptor spec/mode\", ZSTR_VAL(ztype));\n        goto finish;\n    }\n\nfinish:\n    if (zmode) zend_string_release(zmode);\n    if (zfile) zend_string_release(zfile);\n    zend_string_release(ztype);\n    return retval;\n}\n\nstatic zend_result set_proc_descriptor_from_resource(zval *resource, descriptorspec_item *desc, int nindex) {\n    /* Should be a stream - try and dup the descriptor */\n    php_stream *stream = (php_stream *) zend_fetch_resource(Z_RES_P(resource), \"stream\", php_file_le_stream());\n    if (stream == NULL) {\n        return FAILURE;\n    }\n\n    php_socket_t fd;\n    zend_result status = (zend_result) php_stream_cast(stream, PHP_STREAM_AS_FD, (void **) &fd, REPORT_ERRORS);\n    if (status == FAILURE) {\n        return FAILURE;\n    }\n\n#ifdef PHP_WIN32\n    php_file_descriptor_t fd_t = (php_file_descriptor_t) _get_osfhandle(fd);\n#else\n    php_file_descriptor_t fd_t = fd;\n#endif\n    return dup_proc_descriptor(fd_t, &desc->childend, nindex);\n}\n\n#ifndef PHP_WIN32\n#if defined(USE_POSIX_SPAWN)\nstatic zend_result close_parentends_of_pipes(posix_spawn_file_actions_t *actions,\n                                             descriptorspec_item *descriptors,\n                                             int ndesc) {\n    int r;\n    for (int i = 0; i < ndesc; i++) {\n        if (descriptors[i].type != DESCRIPTOR_TYPE_STD) {\n            r = posix_spawn_file_actions_addclose(actions, descriptors[i].parentend);\n            if (r != 0) {\n                php_error_docref(\n                    NULL, E_WARNING, \"Cannot close file descriptor %d: %s\", descriptors[i].parentend, strerror(r));\n                return FAILURE;\n            }\n        }\n        if (descriptors[i].childend != descriptors[i].index) {\n            r = posix_spawn_file_actions_adddup2(actions, descriptors[i].childend, descriptors[i].index);\n            if (r != 0) {\n                php_error_docref(NULL,\n                                 E_WARNING,\n                                 \"Unable to copy file descriptor %d (for pipe) into \"\n                                 \"file descriptor %d: %s\",\n                                 descriptors[i].childend,\n                                 descriptors[i].index,\n                                 strerror(r));\n                return FAILURE;\n            }\n            r = posix_spawn_file_actions_addclose(actions, descriptors[i].childend);\n            if (r != 0) {\n                php_error_docref(\n                    NULL, E_WARNING, \"Cannot close file descriptor %d: %s\", descriptors[i].childend, strerror(r));\n                return FAILURE;\n            }\n        }\n    }\n\n    return SUCCESS;\n}\n#else\nstatic zend_result close_parentends_of_pipes(descriptorspec_item *descriptors, int ndesc) {\n    /* We are running in child process\n     * Close the 'parent end' of pipes which were opened before forking/spawning\n     * Also, dup() the child end of all pipes as necessary so they will use the FD\n     * number which the user requested */\n    for (int i = 0; i < ndesc; i++) {\n        if (descriptors[i].type != DESCRIPTOR_TYPE_STD) {\n            close(descriptors[i].parentend);\n        }\n        if (descriptors[i].childend != descriptors[i].index) {\n            if (dup2(descriptors[i].childend, descriptors[i].index) < 0) {\n                php_error_docref(NULL,\n                                 E_WARNING,\n                                 \"Unable to copy file descriptor %d (for pipe) into \"\n                                 \"file descriptor %d: %s\",\n                                 descriptors[i].childend,\n                                 descriptors[i].index,\n                                 strerror(errno));\n                return FAILURE;\n            }\n            close(descriptors[i].childend);\n        }\n    }\n\n    return SUCCESS;\n}\n#endif\n#endif\n\nstatic void close_all_descriptors(descriptorspec_item *descriptors, int ndesc) {\n    for (int i = 0; i < ndesc; i++) {\n        close_descriptor(descriptors[i].childend);\n        if (descriptors[i].parentend) close_descriptor(descriptors[i].parentend);\n    }\n}\n\nstatic void efree_argv(char **argv) {\n    if (argv) {\n        char **arg = argv;\n        while (*arg != NULL) {\n            efree(*arg);\n            arg++;\n        }\n        efree(argv);\n    }\n}\n\n/* {{{ Execute a command, with specified files used for input/output */\nPHP_FUNCTION(swoole_proc_open) {\n    zend_string *command_str;\n    HashTable *command_ht;\n    HashTable *descriptorspec;                       /* Mandatory argument */\n    zval *pipes;                                     /* Mandatory argument */\n    char *cwd = NULL;                                /* Optional argument */\n    size_t cwd_len = 0;                              /* Optional argument */\n    zval *environment = NULL, *other_options = NULL; /* Optional arguments */\n\n    sw_php_process_env env;\n    int ndesc = 0;\n    int i;\n    zval *descitem = NULL;\n    zend_string *str_index;\n    zend_ulong nindex;\n    descriptorspec_item *descriptors = NULL;\n#ifdef PHP_WIN32\n    PROCESS_INFORMATION pi;\n    HANDLE childHandle;\n    STARTUPINFOW si;\n    BOOL newprocok;\n    DWORD dwCreateFlags = 0;\n    UINT old_error_mode;\n    char cur_cwd[MAXPATHLEN];\n    wchar_t *cmdw = NULL, *cwdw = NULL, *envpw = NULL;\n    size_t cmdw_len;\n    bool suppress_errors = 0;\n    bool bypass_shell = 0;\n    bool blocking_pipes = 0;\n    bool create_process_group = 0;\n    bool create_new_console = 0;\n#else\n    char **argv = NULL;\n#endif\n    int pty_master_fd = -1, pty_slave_fd = -1;\n    php_process_id_t child;\n    sw_php_process_handle *proc;\n\n    ZEND_PARSE_PARAMETERS_START(3, 6)\n    Z_PARAM_ARRAY_HT_OR_STR(command_ht, command_str)\n    Z_PARAM_ARRAY_HT(descriptorspec)\n    Z_PARAM_ZVAL(pipes)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_STRING_OR_NULL(cwd, cwd_len)\n    Z_PARAM_ARRAY_OR_NULL(environment)\n    Z_PARAM_ARRAY_OR_NULL(other_options)\n    ZEND_PARSE_PARAMETERS_END();\n\n    memset(&env, 0, sizeof(env));\n\n    if (command_ht) {\n        uint32_t num_elems = zend_hash_num_elements(command_ht);\n        if (num_elems == 0) {\n            zend_argument_value_error(1, \"must have at least one element\");\n            RETURN_THROWS();\n        }\n\n#ifdef PHP_WIN32\n        /* Automatically bypass shell if command is given as an array */\n        bypass_shell = 1;\n        command_str = create_win_command_from_args(command_ht);\n#else\n        command_str = get_command_from_array(command_ht, &argv, num_elems);\n#endif\n\n        if (!command_str) {\n#ifndef PHP_WIN32\n            efree_argv(argv);\n#endif\n            RETURN_FALSE;\n        }\n    } else {\n        zend_string_addref(command_str);\n    }\n\n#ifdef PHP_WIN32\n    if (other_options) {\n        suppress_errors = get_option(other_options, \"suppress_errors\", strlen(\"suppress_errors\"));\n        /* TODO: Deprecate in favor of array command? */\n        bypass_shell = bypass_shell || get_option(other_options, \"bypass_shell\", strlen(\"bypass_shell\"));\n        blocking_pipes = get_option(other_options, \"blocking_pipes\", strlen(\"blocking_pipes\"));\n        create_process_group = get_option(other_options, \"create_process_group\", strlen(\"create_process_group\"));\n        create_new_console = get_option(other_options, \"create_new_console\", strlen(\"create_new_console\"));\n    }\n#endif\n\n    php_swoole_check_reactor();\n    if (php_swoole_signal_isset_handler(SIGCHLD)) {\n        php_swoole_error(E_WARNING, \"The signal [SIGCHLD] is registered, cannot execute swoole_proc_open\");\n        RETURN_FALSE;\n    }\n\n    swoole::Coroutine::get_current_safe();\n\n    if (environment) {\n        env = _php_array_to_envp(environment);\n    }\n\n    descriptors = alloc_descriptor_array(descriptorspec);\n\n    /* Walk the descriptor spec and set up files/pipes */\n    ZEND_HASH_FOREACH_KEY_VAL(descriptorspec, nindex, str_index, descitem) {\n        if (str_index) {\n            zend_argument_value_error(2, \"must be an integer indexed array\");\n            goto exit_fail;\n        }\n\n        descriptors[ndesc].index = (int) nindex;\n\n        ZVAL_DEREF(descitem);\n        if (Z_TYPE_P(descitem) == IS_RESOURCE) {\n            if (set_proc_descriptor_from_resource(descitem, &descriptors[ndesc], ndesc) == FAILURE) {\n                goto exit_fail;\n            }\n        } else if (Z_TYPE_P(descitem) == IS_ARRAY) {\n            if (set_proc_descriptor_from_array(\n                    descitem, descriptors, ndesc, (int) nindex, &pty_master_fd, &pty_slave_fd) == FAILURE) {\n                goto exit_fail;\n            }\n        } else {\n            php_swoole_fatal_error(E_WARNING, \"Descriptor item must be either an array or a File-Handle\");\n            goto exit_fail;\n        }\n        ndesc++;\n    }\n    ZEND_HASH_FOREACH_END();\n\n#ifdef PHP_WIN32\n    if (cwd == NULL) {\n        char *getcwd_result = VCWD_GETCWD(cur_cwd, MAXPATHLEN);\n        if (!getcwd_result) {\n            php_error_docref(NULL, E_WARNING, \"Cannot get current directory\");\n            goto exit_fail;\n        }\n        cwd = cur_cwd;\n    }\n    cwdw = php_win32_cp_any_to_w(cwd);\n    if (!cwdw) {\n        php_error_docref(NULL, E_WARNING, \"CWD conversion failed\");\n        goto exit_fail;\n    }\n\n    init_startup_info(&si, descriptors, ndesc);\n    init_process_info(&pi);\n\n    if (suppress_errors) {\n        old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);\n    }\n\n    dwCreateFlags = NORMAL_PRIORITY_CLASS;\n    if (strcmp(sapi_module.name, \"cli\") != 0) {\n        dwCreateFlags |= CREATE_NO_WINDOW;\n    }\n    if (create_process_group) {\n        dwCreateFlags |= CREATE_NEW_PROCESS_GROUP;\n    }\n    if (create_new_console) {\n        dwCreateFlags |= CREATE_NEW_CONSOLE;\n    }\n    envpw = php_win32_cp_env_any_to_w(env.envp);\n    if (envpw) {\n        dwCreateFlags |= CREATE_UNICODE_ENVIRONMENT;\n    } else {\n        if (env.envp) {\n            php_error_docref(NULL, E_WARNING, \"ENV conversion failed\");\n            goto exit_fail;\n        }\n    }\n\n    cmdw = php_win32_cp_conv_any_to_w(ZSTR_VAL(command_str), ZSTR_LEN(command_str), &cmdw_len);\n    if (!cmdw) {\n        php_error_docref(NULL, E_WARNING, \"Command conversion failed\");\n        goto exit_fail;\n    }\n\n    if (!bypass_shell) {\n        if (convert_command_to_use_shell(&cmdw, cmdw_len) == FAILURE) {\n            goto exit_fail;\n        }\n    }\n    newprocok = CreateProcessW(\n        NULL, cmdw, &php_proc_open_security, &php_proc_open_security, TRUE, dwCreateFlags, envpw, cwdw, &si, &pi);\n\n    if (suppress_errors) {\n        SetErrorMode(old_error_mode);\n    }\n\n    if (newprocok == FALSE) {\n        DWORD dw = GetLastError();\n        close_all_descriptors(descriptors, ndesc);\n        php_error_docref(NULL, E_WARNING, \"CreateProcess failed, error code: %u\", dw);\n        goto exit_fail;\n    }\n\n    childHandle = pi.hProcess;\n    child = pi.dwProcessId;\n    CloseHandle(pi.hThread);\n#elif defined(USE_POSIX_SPAWN)\n    posix_spawn_file_actions_t factions;\n    int r;\n    posix_spawn_file_actions_init(&factions);\n\n    if (close_parentends_of_pipes(&factions, descriptors, ndesc) == FAILURE) {\n        posix_spawn_file_actions_destroy(&factions);\n        close_all_descriptors(descriptors, ndesc);\n        goto exit_fail;\n    }\n\n    if (cwd) {\n        r = posix_spawn_file_actions_addchdir_np(&factions, cwd);\n        if (r != 0) {\n            php_error_docref(NULL, E_WARNING, \"posix_spawn_file_actions_addchdir_np() failed: %s\", strerror(r));\n        }\n    }\n\n    if (argv) {\n        r = posix_spawnp(&child, ZSTR_VAL(command_str), &factions, NULL, argv, (env.envarray ? env.envarray : environ));\n    } else {\n        r = posix_spawn(&child,\n                        \"/bin/sh\",\n                        &factions,\n                        NULL,\n                        (char *const[]){\"sh\", \"-c\", ZSTR_VAL(command_str), NULL},\n                        env.envarray ? env.envarray : environ);\n    }\n    posix_spawn_file_actions_destroy(&factions);\n    if (r != 0) {\n        close_all_descriptors(descriptors, ndesc);\n        php_error_docref(NULL, E_WARNING, \"posix_spawn() failed: %s\", strerror(r));\n        goto exit_fail;\n    }\n#elif HAVE_FORK\n    /* the Unix way */\n    child = swoole_fork(SW_FORK_EXEC);\n\n    if (child == 0) {\n        /* This is the child process */\n\n        if (close_parentends_of_pipes(descriptors, ndesc) == FAILURE) {\n            /* We are already in child process and can't do anything to make\n             * `proc_open` return an error in the parent\n             * All we can do is exit with a non-zero (error) exit code */\n            _exit(127);\n        }\n\n        if (cwd) {\n            php_ignore_value(chdir(cwd));\n        }\n\n        if (argv) {\n            /* execvpe() is non-portable, use environ instead. */\n            if (env.envarray) {\n                environ = env.envarray;\n            }\n            execvp(ZSTR_VAL(command_str), argv);\n        } else {\n            if (env.envarray) {\n                execle(\"/bin/sh\", \"sh\", \"-c\", ZSTR_VAL(command_str), NULL, env.envarray);\n            } else {\n                execl(\"/bin/sh\", \"sh\", \"-c\", ZSTR_VAL(command_str), NULL);\n            }\n        }\n\n        /* If execvp/execle/execl are successful, we will never reach here\n         * Display error and exit with non-zero (error) status code */\n        php_error_docref(NULL, E_WARNING, \"Exec failed: %s\", strerror(errno));\n        _exit(127);\n    } else if (child < 0) {\n        /* Failed to fork() */\n        close_all_descriptors(descriptors, ndesc);\n        php_error_docref(NULL, E_WARNING, \"Fork failed: %s\", strerror(errno));\n        goto exit_fail;\n    }\n#else\n#error You lose (configure should not have let you get here)\n#endif\n\n    /* We forked/spawned and this is the parent */\n\n    pipes = zend_try_array_init(pipes);\n    if (!pipes) {\n        goto exit_fail;\n    }\n\n    proc = (sw_php_process_handle *) emalloc(sizeof(sw_php_process_handle));\n    proc->command = zend_string_copy(command_str);\n    proc->wstatus = nullptr;\n    proc->running = true;\n    proc->pipes = (zend_resource **) emalloc(sizeof(zend_resource *) * ndesc);\n    proc->npipes = ndesc;\n    proc->child = child;\n    proc->env = env;\n\n    /* Clean up all the child ends and then open streams on the parent\n     *   ends, where appropriate */\n    for (i = 0; i < ndesc; i++) {\n        php_stream *stream = NULL;\n\n        close_descriptor(descriptors[i].childend);\n\n        if (descriptors[i].type == DESCRIPTOR_TYPE_PIPE) {\n            const char *mode_string = NULL;\n\n            switch (descriptors[i].mode_flags) {\n#ifdef PHP_WIN32\n            case O_WRONLY | O_BINARY:\n                mode_string = \"wb\";\n                break;\n            case O_RDONLY | O_BINARY:\n                mode_string = \"rb\";\n                break;\n#endif\n            case O_WRONLY:\n                mode_string = \"w\";\n                break;\n            case O_RDONLY:\n                mode_string = \"r\";\n                break;\n            case O_RDWR:\n                mode_string = \"r+\";\n                break;\n            }\n\n#ifdef PHP_WIN32\n            stream = php_stream_fopen_from_fd(\n                _open_osfhandle((intptr_t) descriptors[i].parentend, descriptors[i].mode_flags), mode_string, NULL);\n            php_stream_set_option(stream, PHP_STREAM_OPTION_PIPE_BLOCKING, blocking_pipes, NULL);\n#else\n            stream = php_swoole_create_stream_from_pipe(descriptors[i].parentend, mode_string, NULL STREAMS_CC);\n#endif\n        } else if (descriptors[i].type == DESCRIPTOR_TYPE_SOCKET) {\n            stream = php_swoole_create_stream_from_socket(\n                (php_socket_t) descriptors[i].parentend, AF_UNIX, SOCK_STREAM, 0 STREAMS_CC);\n        } else {\n            proc->pipes[i] = NULL;\n        }\n\n        if (stream) {\n            zval retfp;\n\n            /* nasty hack; don't copy it */\n            stream->flags |= PHP_STREAM_FLAG_NO_SEEK;\n\n            php_stream_to_zval(stream, &retfp);\n            add_index_zval(pipes, descriptors[i].index, &retfp);\n\n            proc->pipes[i] = Z_RES(retfp);\n            Z_ADDREF(retfp);\n        }\n    }\n\n    if (1) {\n        RETVAL_RES(zend_register_resource(proc, le_proc_open));\n    } else {\n    exit_fail:\n        _php_free_envp(env);\n        RETVAL_FALSE;\n    }\n\n    zend_string_release_ex(command_str, false);\n#ifdef PHP_WIN32\n    free(cwdw);\n    free(cmdw);\n    free(envpw);\n#else\n    efree_argv(argv);\n#endif\n#ifdef HAVE_OPENPTY\n    if (pty_master_fd != -1) {\n        close(pty_master_fd);\n    }\n    if (pty_slave_fd != -1) {\n        close(pty_slave_fd);\n    }\n#endif\n    if (descriptors) {\n        efree(descriptors);\n    }\n}\n/* }}} */\n"
  },
  {
    "path": "thirdparty/php/standard/proc_open.h",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | Copyright (c) The PHP Group                                          |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | https://www.php.net/license/3_01.txt                                 |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Author: Wez Furlong <wez@thebrainroom.com>                           |\n   +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_cxx.h\"\n\nSW_EXTERN_C_BEGIN\nvoid swoole_proc_open_init(int module_number);\nPHP_FUNCTION(swoole_proc_open);\nPHP_FUNCTION(swoole_proc_close);\nPHP_FUNCTION(swoole_proc_get_status);\nPHP_FUNCTION(swoole_proc_terminate);\nSW_EXTERN_C_END\n\n#ifdef PHP_WIN32\ntypedef HANDLE php_file_descriptor_t;\ntypedef DWORD php_process_id_t;\n#define PHP_INVALID_FD INVALID_HANDLE_VALUE\n#else\ntypedef int php_file_descriptor_t;\ntypedef pid_t php_process_id_t;\n#define PHP_INVALID_FD (-1)\n#endif\n\n/* Environment block under Win32 is a NUL terminated sequence of NUL terminated\n *   name=value strings.\n * Under Unix, it is an argv style array. */\ntypedef struct {\n    char *envp;\n#ifndef PHP_WIN32\n    char **envarray;\n#endif\n} sw_php_process_env;\n\ntypedef struct {\n    bool running;\n    int *wstatus;\n    php_process_id_t child;\n#ifdef PHP_WIN32\n    HANDLE childHandle;\n#endif\n    int npipes;\n    zend_resource **pipes;\n    zend_string *command;\n    sw_php_process_env env;\n#if HAVE_SYS_WAIT_H\n    /* We can only request the status once before it becomes unavailable.\n     * Cache the result so we can request it multiple times. */\n    int cached_exit_wait_status_value;\n    bool has_cached_exit_wait_status;\n#endif\n} sw_php_process_handle;\n"
  },
  {
    "path": "thirdparty/php/standard/var_decoder.cc",
    "content": "#include \"php_swoole.h\"\n\nBEGIN_EXTERN_C()\n#include \"ext/standard/php_var.h\"\n#include \"ext/json/php_json.h\"\nextern PHP_JSON_API zend_class_entry *php_json_exception_ce;\nEND_EXTERN_C()\n\nnamespace zend {\nvoid unserialize(zval *return_value, const char *buf, size_t buf_len, HashTable *options) {\n    php_unserialize_with_options(return_value, buf, buf_len, options, \"swoole_ext_unserialize\");\n}\n\nstatic const char *php_json_get_error_msg(php_json_error_code error_code) /* {{{ */\n{\n    switch (error_code) {\n    case PHP_JSON_ERROR_NONE:\n        return \"No error\";\n    case PHP_JSON_ERROR_DEPTH:\n        return \"Maximum stack depth exceeded\";\n    case PHP_JSON_ERROR_STATE_MISMATCH:\n        return \"State mismatch (invalid or malformed JSON)\";\n    case PHP_JSON_ERROR_CTRL_CHAR:\n        return \"Control character error, possibly incorrectly encoded\";\n    case PHP_JSON_ERROR_SYNTAX:\n        return \"Syntax error\";\n    case PHP_JSON_ERROR_UTF8:\n        return \"Malformed UTF-8 characters, possibly incorrectly encoded\";\n    case PHP_JSON_ERROR_RECURSION:\n        return \"Recursion detected\";\n    case PHP_JSON_ERROR_INF_OR_NAN:\n        return \"Inf and NaN cannot be JSON encoded\";\n    case PHP_JSON_ERROR_UNSUPPORTED_TYPE:\n        return \"Type is not supported\";\n    case PHP_JSON_ERROR_INVALID_PROPERTY_NAME:\n        return \"The decoded property name is invalid\";\n    case PHP_JSON_ERROR_UTF16:\n        return \"Single unpaired UTF-16 surrogate in unicode escape\";\n    case PHP_JSON_ERROR_NON_BACKED_ENUM:\n        return \"Non-backed enums have no default serialization\";\n    default:\n        return \"Unknown error\";\n    }\n}\n/* }}} */\n\nvoid json_decode(zval *return_value, const char *str, size_t str_len, zend_long options, zend_long depth) {\n    if (!(options & PHP_JSON_THROW_ON_ERROR)) {\n        JSON_G(error_code) = PHP_JSON_ERROR_NONE;\n    }\n\n    if (!str_len) {\n        if (!(options & PHP_JSON_THROW_ON_ERROR)) {\n            JSON_G(error_code) = PHP_JSON_ERROR_SYNTAX;\n        } else {\n            zend_throw_exception(\n                php_json_exception_ce, php_json_get_error_msg(PHP_JSON_ERROR_SYNTAX), PHP_JSON_ERROR_SYNTAX);\n        }\n        RETURN_NULL();\n    }\n\n    if (depth <= 0) {\n        php_error_docref(NULL, E_WARNING, \"Depth must be greater than zero\");\n        RETURN_NULL();\n    }\n\n    if (depth > INT_MAX) {\n        php_error_docref(NULL, E_WARNING, \"Depth must be lower than %d\", INT_MAX);\n        RETURN_NULL();\n    }\n    php_json_decode_ex(return_value, (char *) str, str_len, options, depth);\n}\n}  // namespace zend\n"
  },
  {
    "path": "thirdparty/php/streams/php_streams_int.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | PHP Version 7                                                        |\n  +----------------------------------------------------------------------+\n  | Copyright (c) 1997-2017 The PHP Group                                |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.php.net/license/3_01.txt                                  |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@thebrainroom.com>                           |\n  +----------------------------------------------------------------------+\n*/\n\n/* $Id$ */\n\n\n#if ZEND_DEBUG\n\n#define emalloc_rel_orig(size)\t\\\n\t\t( __php_stream_call_depth == 0 \\\n\t\t? _emalloc((size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_RELAY_CC) \\\n\t\t: _emalloc((size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC) )\n\n#define erealloc_rel_orig(ptr, size)\t\\\n\t\t( __php_stream_call_depth == 0 \\\n\t\t? _erealloc((ptr), (size), 0 ZEND_FILE_LINE_CC ZEND_FILE_LINE_RELAY_CC) \\\n\t\t: _erealloc((ptr), (size), 0 ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC) )\n\n#define pemalloc_rel_orig(size, persistent)\t((persistent) ? malloc((size)) : emalloc_rel_orig((size)))\n#define perealloc_rel_orig(ptr, size, persistent)\t((persistent) ? realloc((ptr), (size)) : erealloc_rel_orig((ptr), (size)))\n#else\n# define pemalloc_rel_orig(size, persistent)\t\t\t\tpemalloc((size), (persistent))\n# define perealloc_rel_orig(ptr, size, persistent)\t\t\tperealloc((ptr), (size), (persistent))\n# define emalloc_rel_orig(size)\t\t\t\t\t\t\t\temalloc((size))\n#endif\n\n#define STREAM_DEBUG 0\n#define STREAM_WRAPPER_PLAIN_FILES\t((php_stream_wrapper*)-1)\n\n#ifndef MAP_FAILED\n#define MAP_FAILED ((void *) -1)\n#endif\n\n#define CHUNK_SIZE\t8192\n\n#ifdef PHP_WIN32\n# ifdef EWOULDBLOCK\n#  undef EWOULDBLOCK\n# endif\n# define EWOULDBLOCK WSAEWOULDBLOCK\n# ifdef EMSGSIZE\n#  undef EMSGSIZE\n# endif\n# define EMSGSIZE WSAEMSGSIZE\n#endif\n"
  },
  {
    "path": "thirdparty/php/streams/plain_wrapper.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Authors: Wez Furlong <wez@thebrainroom.com>                          |\n  +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole.h\"\n#include \"php_open_temporary_file.h\"\n#include \"ext/standard/file.h\"\n#include \"ext/standard/flock_compat.h\"\n#include \"ext/standard/php_filestat.h\"\n\n#include <fcntl.h>\n\n#if PHP_VERSION_ID >= 80400\n#ifdef HAVE_SYS_WAIT_H\n#include <sys/wait.h>\n#endif\n#ifdef HAVE_SYS_FILE_H\n#include <sys/file.h>\n#endif\n#else\n#if HAVE_SYS_WAIT_H\n#include <sys/wait.h>\n#endif\n#if HAVE_SYS_FILE_H\n#include <sys/file.h>\n#endif\n#endif\n\n#ifdef HAVE_SYS_MMAN_H\n#include <sys/mman.h>\n#endif\n\n#include \"thirdparty/php/streams/php_streams_int.h\"\n\n#include \"swoole_file_hook.h\"\n\n#define sw_php_stream_fopen_from_fd_rel(fd, mode, persistent_id, zero_position)                                         \\\n    _sw_php_stream_fopen_from_fd((fd), (mode), (persistent_id), (zero_position) STREAMS_REL_CC)\n\n#define sw_php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id)                                                    \\\n    _sw_php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_REL_CC)\n\n#define sw_php_stream_fopen_rel(path, mode, opened_path, options)                                                       \\\n    _sw_php_stream_fopen(path, mode, opened_path, options STREAMS_REL_CC)\n\n#ifndef PHP_WIN32\nextern int php_get_uid_by_name(const char *name, uid_t *uid);\nextern int php_get_gid_by_name(const char *name, gid_t *gid);\n#endif\n\n#if defined(PHP_WIN32)\n#define PLAIN_WRAP_BUF_SIZE(st) (((st) > UINT_MAX) ? UINT_MAX : (unsigned int) (st))\n\n#if PHP_VERSION_ID >= 80100\n#define fsync _commit\n#define fdatasync fsync\n#endif\n\n#else\n#define PLAIN_WRAP_BUF_SIZE(st) (st)\n\n#if PHP_VERSION_ID >= 80100\n#if !defined(HAVE_FDATASYNC)\n#define fdatasync fsync\n#elif defined(__APPLE__)\n// The symbol is present, however not in the headers\nextern int fdatasync(int);\n#endif\n#endif\n\n#endif\n\nstatic php_stream_size_t sw_php_stdiop_write(php_stream *stream, const char *buf, size_t count);\nstatic php_stream_size_t sw_php_stdiop_read(php_stream *stream, char *buf, size_t count);\nstatic int sw_php_stdiop_close(php_stream *stream, int close_handle);\nstatic int sw_php_stdiop_stat(php_stream *stream, php_stream_statbuf *ssb);\nstatic int sw_php_stdiop_flush(php_stream *stream);\nstatic int sw_php_stdiop_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset);\nstatic int sw_php_stdiop_set_option(php_stream *stream, int option, int value, void *ptrparam);\nstatic int sw_php_stdiop_cast(php_stream *stream, int castas, void **ret);\nstatic void php_stream_mode_sanitize_fdopen_fopencookie(php_stream *stream, char *result);\nstatic php_stream *_sw_php_stream_fopen_from_fd_int(int fd, const char *mode, const char *persistent_id STREAMS_DC);\n#if PHP_VERSION_ID >= 80200\nstatic php_stream *_sw_php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id, bool zero_position STREAMS_DC);\n#else\nstatic php_stream *_sw_php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id STREAMS_DC);\n#endif\nstatic int sw_php_mkdir(const char *dir, zend_long mode);\n\nstatic int sw_php_stream_parse_fopen_modes(const char *mode, int *open_flags) {\n    int flags;\n\n    switch (mode[0]) {\n    case 'r':\n        flags = 0;\n        break;\n    case 'w':\n        flags = O_TRUNC | O_CREAT;\n        break;\n    case 'a':\n        flags = O_CREAT | O_APPEND;\n        break;\n    case 'x':\n        flags = O_CREAT | O_EXCL;\n        break;\n    case 'c':\n        flags = O_CREAT;\n        break;\n    default:\n        /* unknown mode */\n        return FAILURE;\n    }\n\n    if (strchr(mode, '+')) {\n        flags |= O_RDWR;\n    } else if (flags) {\n        flags |= O_WRONLY;\n    } else {\n        flags |= O_RDONLY;\n    }\n\n#if defined(O_CLOEXEC)\n    if (strchr(mode, 'e')) {\n        flags |= O_CLOEXEC;\n    }\n#endif\n\n#if defined(O_NONBLOCK)\n    if (strchr(mode, 'n')) {\n        flags |= O_NONBLOCK;\n    }\n#endif\n\n#if defined(_O_TEXT) && defined(O_BINARY)\n    if (strchr(mode, 't')) {\n        flags |= _O_TEXT;\n    } else {\n        flags |= O_BINARY;\n    }\n#endif\n\n    *open_flags = flags;\n    return SUCCESS;\n}\n\n\n/* {{{ ------- STDIO stream implementation -------*/\n\ntypedef struct {\n    FILE *file;\n    int fd;\t\t\t\t\t/* underlying file descriptor */\n    unsigned is_process_pipe:1;\t                /* use pclose instead of fclose */\n    unsigned is_pipe:1;\t\t                /* stream is an actual pipe, currently Windows only*/\n    unsigned cached_fstat:1;\t                /* sb is valid */\n    unsigned is_pipe_blocking:1;                /* allow blocking read() on pipes, currently Windows only */\n    unsigned no_forced_fstat:1;                 /* Use fstat cache even if forced */\n    unsigned is_seekable:1;\t\t        /* don't try and seek, if not set */\n    unsigned can_poll:1;\n    unsigned _reserved:25;\n\n    int lock_flag;\t\t\t        /* stores the lock state */\n    zend_string *temp_name;\t                /* if non-null, this is the path to a temporary file that\n\t\t\t\t\t\t * is to be deleted when the stream is closed */\n#ifdef HAVE_FLUSHIO\n    char last_op;\n#endif\n\n#ifdef HAVE_MMAP\n    char *last_mapped_addr;\n    size_t last_mapped_len;\n#endif\n#ifdef PHP_WIN32\n    char *last_mapped_addr;\n    HANDLE file_mapping;\n#endif\n\n    zend_stat_t sb;\n} php_stdio_stream_data;\n#define PHP_STDIOP_GET_FD(anfd, data)\tanfd = (data)->file ? fileno((data)->file) : (data)->fd\n\nstatic php_stream_ops sw_php_stream_stdio_ops = {\n    sw_php_stdiop_write,\n    sw_php_stdiop_read,\n    sw_php_stdiop_close,\n    sw_php_stdiop_flush,\n    \"STDIO/coroutine\",\n    sw_php_stdiop_seek,\n    sw_php_stdiop_cast,\n    sw_php_stdiop_stat,\n    sw_php_stdiop_set_option,\n};\n\nstatic int do_fstat(php_stdio_stream_data *d, int force) {\n    if (!d->cached_fstat || force) {\n        int fd;\n        int r;\n\n        PHP_STDIOP_GET_FD(fd, d);\n        r = fstat(fd, &d->sb);\n        d->cached_fstat = r == 0;\n\n        return r;\n    }\n    return 0;\n}\n\nstatic php_stream *_sw_php_stream_fopen_from_fd_int(int fd, const char *mode, const char *persistent_id STREAMS_DC) {\n    php_stdio_stream_data *self = (php_stdio_stream_data *) pemalloc_rel_orig(sizeof(*self), persistent_id);\n    memset(self, 0, sizeof(*self));\n    self->file = NULL;\n    self->is_seekable = 1;\n    self->is_pipe = 0;\n    self->lock_flag = LOCK_UN;\n    self->is_process_pipe = 0;\n    self->temp_name = NULL;\n    self->fd = fd;\n#ifdef PHP_WIN32\n    self->is_pipe_blocking = 0;\n#endif\n    return php_stream_alloc_rel(&sw_php_stream_stdio_ops, self, persistent_id, mode);\n}\n\nstatic void _sw_detect_is_seekable(php_stdio_stream_data *self) {\n#if defined(S_ISFIFO) && defined(S_ISCHR)\n    if (self->fd >= 0 && do_fstat(self, 0) == 0) {\n        self->is_seekable = !(S_ISFIFO(self->sb.st_mode) || S_ISCHR(self->sb.st_mode));\n        self->is_pipe = S_ISFIFO(self->sb.st_mode);\n        self->can_poll = S_ISFIFO(self->sb.st_mode) || S_ISSOCK(self->sb.st_mode) || S_ISCHR(self->sb.st_mode);\n        if (self->can_poll) {\n            swoole_coroutine_socket_create(self->fd);\n        }\n    }\n#elif defined(PHP_WIN32)\n#if PHP_VERSION_ID >= 80300\n    uintptr_t handle = _get_osfhandle(self->fd);\n\n    if (handle != (uintptr_t)INVALID_HANDLE_VALUE) {\n#else\n    zend_uintptr_t handle = _get_osfhandle(self->fd);\n\n    if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) {\n#endif\n        DWORD file_type = GetFileType((HANDLE)handle);\n\n        self->is_seekable = !(file_type == FILE_TYPE_PIPE || file_type == FILE_TYPE_CHAR);\n        self->is_pipe = file_type == FILE_TYPE_PIPE;\n\n        /* Additional check needed to distinguish between pipes and sockets. */\n        if (self->is_pipe && !GetNamedPipeInfo((HANDLE) handle, NULL, NULL, NULL, NULL)) {\n            self->is_pipe = 0;\n        }\n    }\n#endif\n}\n\nstatic php_stream *_sw_php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id, bool zero_position STREAMS_DC) {\n    php_stream *stream = sw_php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id);\n\n    if (stream) {\n        php_stdio_stream_data *self = (php_stdio_stream_data *) stream->abstract;\n\n        _sw_detect_is_seekable(self);\n        if (!self->is_seekable) {\n            stream->flags |= PHP_STREAM_FLAG_NO_SEEK;\n            stream->position = -1;\n        } else if (zero_position) {\n            ZEND_ASSERT(lseek(self->fd, 0, SEEK_CUR) == 0);\n            stream->position = 0;\n        }\n        else {\n            stream->position = lseek(self->fd, 0, SEEK_CUR);\n#ifdef ESPIPE\n            /* FIXME: Is this code still needed? */\n            if (stream->position == (zend_off_t)-1 && errno == ESPIPE) {\n                stream->flags |= PHP_STREAM_FLAG_NO_SEEK;\n                self->is_seekable = 0;\n            }\n#endif\n        }\n    }\n\n    return stream;\n}\n\nstatic php_stream_size_t sw_php_stdiop_write(php_stream *stream, const char *buf, size_t count) {\n    php_stdio_stream_data *data = (php_stdio_stream_data *) stream->abstract;\n\n    assert(data != NULL);\n\n    if (data->fd >= 0) {\n#ifdef PHP_WIN32\n        ssize_t bytes_written;\n        if (ZEND_SIZE_T_UINT_OVFL(count)) {\n            count = UINT_MAX;\n        }\n        bytes_written = _write(data->fd, buf, (unsigned int)count);\n#else\n        ssize_t bytes_written = write(data->fd, buf, count);\n#endif\n        if (bytes_written < 0) {\n            if (PHP_IS_TRANSIENT_ERROR(errno)) {\n                return 0;\n            }\n            if (errno == EINTR) {\n                /* TODO: Should this be treated as a proper error or not? */\n                return bytes_written;\n            }\n            if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) {\n                php_error_docref(NULL, E_NOTICE, \"Write of %zu bytes failed with errno=%d %s\", count, errno, strerror(errno));\n            }\n        }\n        return bytes_written;\n    } else {\n#ifdef HAVE_FLUSHIO\n        if (data->is_seekable && data->last_op == 'r') {\n            fseek(data->file, 0, SEEK_CUR);\n        }\n        data->last_op = 'w';\n#endif\n\n        return (ssize_t) fwrite(buf, 1, count, data->file);\n    }\n}\n\nstatic php_stream_size_t sw_php_stdiop_read(php_stream *stream, char *buf, size_t count) {\n    php_stdio_stream_data *data = (php_stdio_stream_data *) stream->abstract;\n    ssize_t ret;\n\n    assert(data != NULL);\n\n    if (data->fd >= 0) {\n#ifdef PHP_WIN32\n        php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;\n\n        if ((self->is_pipe || self->is_process_pipe) && !self->is_pipe_blocking) {\n            HANDLE ph = (HANDLE)_get_osfhandle(data->fd);\n            int retry = 0;\n            DWORD avail_read = 0;\n\n            do {\n                /* Look ahead to get the available data amount to read. Do the same\n                        as read() does, however not blocking forever. In case it failed,\n                        no data will be read (better than block). */\n                if (!PeekNamedPipe(ph, NULL, 0, NULL, &avail_read, NULL)) {\n                    break;\n                }\n                /* If there's nothing to read, wait in 10us periods. */\n                if (0 == avail_read) {\n                    usleep(10);\n                }\n            } while (0 == avail_read && retry++ < 3200000);\n\n            /* Reduce the required data amount to what is available, otherwise read()\n                    will block.*/\n            if (avail_read < count) {\n                count = avail_read;\n            }\n        }\n#endif\n        ret = read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count));\n\n        if (ret == (ssize_t) -1 && errno == EINTR) {\n            /* Read was interrupted, retry once,\n             If read still fails, giveup with feof==0\n             so script can retry if desired */\n            ret = read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count));\n        }\n\n        if (ret < 0) {\n            if (PHP_IS_TRANSIENT_ERROR(errno)) {\n                /* Not an error. */\n                ret = 0;\n            } else if (errno == EINTR) {\n                /* TODO: Should this be treated as a proper error or not? */\n            } else {\n                if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) {\n                    php_error_docref(NULL, E_NOTICE, \"Read of %zu bytes failed with errno=%d %s\", count, errno, strerror(errno));\n                }\n\n                /* TODO: Remove this special-case? */\n                if (errno != EBADF) {\n                    stream->eof = 1;\n                }\n            }\n        } else if (ret == 0) {\n            stream->eof = 1;\n        }\n\n    } else {\n#ifdef HAVE_FLUSHIO\n        if (data->is_seekable && data->last_op == 'w')\n            fseek(data->file, 0, SEEK_CUR);\n        data->last_op = 'r';\n#endif\n        ret = fread(buf, 1, count, data->file);\n\n        stream->eof = feof(data->file);\n    }\n    return ret;\n}\n\nstatic int sw_php_stdiop_close(php_stream *stream, int close_handle) {\n    int ret;\n    php_stdio_stream_data *data = (php_stdio_stream_data *) stream->abstract;\n\n    assert(data != NULL);\n\n#ifdef HAVE_MMAP\n    if (data->last_mapped_addr) {\n        munmap(data->last_mapped_addr, data->last_mapped_len);\n        data->last_mapped_addr = NULL;\n    }\n#elif defined(PHP_WIN32)\n    if (data->last_mapped_addr) {\n        UnmapViewOfFile(data->last_mapped_addr);\n        data->last_mapped_addr = NULL;\n    }\n    if (data->file_mapping) {\n        CloseHandle(data->file_mapping);\n        data->file_mapping = NULL;\n    }\n#endif\n\n    if (close_handle) {\n        if (data->file) {\n            if (data->is_process_pipe) {\n                errno = 0;\n                ret = pclose(data->file);\n\n#if HAVE_SYS_WAIT_H\n                if (WIFEXITED(ret)) {\n                    ret = WEXITSTATUS(ret);\n                }\n#endif\n            } else {\n                ret = fclose(data->file);\n                data->file = NULL;\n            }\n        } else if (data->fd != -1) {\n            if ((data->lock_flag & LOCK_EX) || (data->lock_flag & LOCK_SH)) {\n                swoole_coroutine_flock(data->fd, LOCK_UN);\n            }\n            ret = close(data->fd);\n            data->fd = -1;\n        } else {\n            return 0; /* everything should be closed already -> success */\n        }\n        if (data->temp_name) {\n#ifdef PHP_WIN32\n            php_win32_ioutil_unlink(ZSTR_VAL(data->temp_name));\n#else\n            unlink(ZSTR_VAL(data->temp_name));\n#endif\n            /* temporary streams are never persistent */\n            zend_string_release_ex(data->temp_name, 0);\n            data->temp_name = NULL;\n        }\n    } else {\n        ret = 0;\n        data->file = NULL;\n        data->fd = -1;\n    }\n\n    pefree(data, stream->is_persistent);\n\n    return ret;\n}\n\nstatic int sw_php_stdiop_flush(php_stream *stream) {\n    php_stdio_stream_data *data = (php_stdio_stream_data *) stream->abstract;\n\n    assert(data != NULL);\n\n    /*\n     * stdio buffers data in user land. By calling fflush(3), this\n     * data is send to the kernel using write(2). fsync'ing is\n     * something completely different.\n     */\n    if (data->file) {\n        return fflush(data->file);\n    }\n    return 0;\n}\n\n#if PHP_VERSION_ID >= 80100\nstatic int php_stdiop_sync(php_stream *stream, bool dataonly)\n{\n    php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;\n    FILE *fp;\n    int fd;\n\n    if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void**)&fp, REPORT_ERRORS) == FAILURE) {\n        return -1;\n    }\n\n    if (sw_php_stdiop_flush(stream) == 0) {\n        PHP_STDIOP_GET_FD(fd, data);\n        if (dataonly) {\n            return fdatasync(fd);\n        } else {\n            return fsync(fd);\n        }\n    }\n    return -1;\n}\n#endif\n\nstatic int sw_php_stdiop_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) {\n    php_stdio_stream_data *data = (php_stdio_stream_data *) stream->abstract;\n    int ret;\n\n    assert(data != NULL);\n\n    if (!data->is_seekable) {\n        php_error_docref(NULL, E_WARNING, \"Cannot seek on this stream\");\n        return -1;\n    }\n\n    if (data->fd >= 0) {\n        zend_off_t result;\n\n        result = lseek(data->fd, offset, whence);\n        if (result == (zend_off_t) -1) return -1;\n\n        *newoffset = result;\n        return 0;\n\n    } else {\n        ret = fseek(data->file, offset, whence);\n        *newoffset = ftell(data->file);\n        return ret;\n    }\n}\n\nstatic int sw_php_stdiop_cast(php_stream *stream, int castas, void **ret) {\n    php_socket_t fd;\n    php_stdio_stream_data *data = (php_stdio_stream_data *) stream->abstract;\n\n    assert(data != NULL);\n\n    /* as soon as someone touches the stdio layer, buffering may ensue,\n     * so we need to stop using the fd directly in that case */\n\n    switch (castas) {\n    case PHP_STREAM_AS_STDIO:\n        if (ret) {\n            if (data->file == NULL) {\n                /* we were opened as a plain file descriptor, so we\n                 * need fdopen now */\n                char fixed_mode[5];\n                php_stream_mode_sanitize_fdopen_fopencookie(stream, fixed_mode);\n                data->file = fdopen(data->fd, fixed_mode);\n                if (data->file == NULL) {\n                    return FAILURE;\n                }\n            }\n\n            *(FILE **) ret = data->file;\n            data->fd = SOCK_ERR;\n        }\n        return SUCCESS;\n\n    case PHP_STREAM_AS_FD_FOR_SELECT:\n        PHP_STDIOP_GET_FD(fd, data);\n        if (SOCK_ERR == fd) {\n            return FAILURE;\n        }\n        if (ret) {\n            *(php_socket_t *) ret = fd;\n        }\n        return SUCCESS;\n\n    case PHP_STREAM_AS_FD:\n        PHP_STDIOP_GET_FD(fd, data);\n\n        if (SOCK_ERR == fd) {\n            return FAILURE;\n        }\n        if (data->file) {\n            fflush(data->file);\n        }\n        if (ret) {\n            *(php_socket_t *) ret = fd;\n        }\n        return SUCCESS;\n    default:\n        return FAILURE;\n    }\n}\n\nstatic int sw_php_stdiop_stat(php_stream *stream, php_stream_statbuf *ssb) {\n    int ret;\n    php_stdio_stream_data *data = (php_stdio_stream_data *) stream->abstract;\n\n    assert(data != NULL);\n    if ((ret = do_fstat(data, 1)) == 0) {\n        memcpy(&ssb->sb, &data->sb, sizeof(ssb->sb));\n    }\n\n    return ret;\n}\n\nstatic int sw_php_stdiop_set_option(php_stream *stream, int option, int value, void *ptrparam) {\n    php_stdio_stream_data *data = (php_stdio_stream_data *) stream->abstract;\n    size_t size;\n    int fd;\n#ifdef O_NONBLOCK\n    /* FIXME: make this work for win32 */\n    int flags;\n    int oldval;\n#endif\n\n    PHP_STDIOP_GET_FD(fd, data);\n\n    switch (option) {\n    case PHP_STREAM_OPTION_BLOCKING:\n        if (fd == -1) return -1;\n#ifdef O_NONBLOCK\n        flags = fcntl(fd, F_GETFL, 0);\n        oldval = (flags & O_NONBLOCK) ? 0 : 1;\n        if (value)\n            flags &= ~O_NONBLOCK;\n        else\n            flags |= O_NONBLOCK;\n\n        if (-1 == fcntl(fd, F_SETFL, flags)) return -1;\n        return oldval;\n#else\n        return -1; /* not yet implemented */\n#endif\n\n    case PHP_STREAM_OPTION_WRITE_BUFFER:\n\n        if (data->file == NULL) {\n            return -1;\n        }\n\n        if (ptrparam)\n            size = *(size_t *) ptrparam;\n        else\n            size = BUFSIZ;\n\n        switch (value) {\n        case PHP_STREAM_BUFFER_NONE:\n            return setvbuf(data->file, NULL, _IONBF, 0);\n\n        case PHP_STREAM_BUFFER_LINE:\n            return setvbuf(data->file, NULL, _IOLBF, size);\n\n        case PHP_STREAM_BUFFER_FULL:\n            return setvbuf(data->file, NULL, _IOFBF, size);\n\n        default:\n            return -1;\n        }\n        break;\n\n    case PHP_STREAM_OPTION_LOCKING:\n        if (fd == -1) {\n            return -1;\n        }\n#if PHP_VERSION_ID >= 80300\n        if ((uintptr_t) ptrparam == PHP_STREAM_LOCK_SUPPORTED) {\n            return 0;\n        }\n#else\n        if ((zend_uintptr_t) ptrparam == PHP_STREAM_LOCK_SUPPORTED) {\n            return 0;\n        }\n#endif\n\n        if (!swoole_coroutine_flock(fd, value)) {\n            data->lock_flag = value;\n            return 0;\n        } else {\n            return -1;\n        }\n        break;\n\n    case PHP_STREAM_OPTION_MMAP_API:\n#if HAVE_MMAP\n    {\n        php_stream_mmap_range *range = (php_stream_mmap_range*)ptrparam;\n        int prot, flags;\n\n        switch (value) {\n        case PHP_STREAM_MMAP_SUPPORTED:\n            return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;\n\n        case PHP_STREAM_MMAP_MAP_RANGE:\n            if (do_fstat(data, 1) != 0) {\n                return PHP_STREAM_OPTION_RETURN_ERR;\n            }\n            if (range->offset > (size_t) data->sb.st_size) {\n                range->offset = data->sb.st_size;\n            }\n            if (range->length == 0 ||\n                range->length > data->sb.st_size - range->offset) {\n                range->length = data->sb.st_size - range->offset;\n            }\n            switch (range->mode) {\n            case PHP_STREAM_MAP_MODE_READONLY:\n                prot = PROT_READ;\n                flags = MAP_PRIVATE;\n                break;\n            case PHP_STREAM_MAP_MODE_READWRITE:\n                prot = PROT_READ | PROT_WRITE;\n                flags = MAP_PRIVATE;\n                break;\n            case PHP_STREAM_MAP_MODE_SHARED_READONLY:\n                prot = PROT_READ;\n                flags = MAP_SHARED;\n                break;\n            case PHP_STREAM_MAP_MODE_SHARED_READWRITE:\n                prot = PROT_READ | PROT_WRITE;\n                flags = MAP_SHARED;\n                break;\n            default:\n                return PHP_STREAM_OPTION_RETURN_ERR;\n            }\n            range->mapped = (char*)mmap(NULL, range->length, prot, flags, fd, range->offset);\n            if (range->mapped == (char*)MAP_FAILED) {\n                range->mapped = NULL;\n                return PHP_STREAM_OPTION_RETURN_ERR;\n            }\n            /* remember the mapping */\n            data->last_mapped_addr = range->mapped;\n            data->last_mapped_len = range->length;\n            return PHP_STREAM_OPTION_RETURN_OK;\n\n        case PHP_STREAM_MMAP_UNMAP:\n            if (data->last_mapped_addr) {\n                munmap(data->last_mapped_addr, data->last_mapped_len);\n                data->last_mapped_addr = NULL;\n\n                return PHP_STREAM_OPTION_RETURN_OK;\n            }\n            return PHP_STREAM_OPTION_RETURN_ERR;\n        }\n    }\n#elif defined(PHP_WIN32)\n    {\n        php_stream_mmap_range *range = (php_stream_mmap_range*)ptrparam;\n        HANDLE hfile = (HANDLE)_get_osfhandle(fd);\n        DWORD prot, acc, loffs = 0, hoffs = 0, delta = 0;\n        LARGE_INTEGER file_size;\n\n        switch (value) {\n        case PHP_STREAM_MMAP_SUPPORTED:\n            return hfile == INVALID_HANDLE_VALUE ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;\n\n        case PHP_STREAM_MMAP_MAP_RANGE:\n            switch (range->mode) {\n            case PHP_STREAM_MAP_MODE_READONLY:\n                prot = PAGE_READONLY;\n                acc = FILE_MAP_READ;\n                break;\n            case PHP_STREAM_MAP_MODE_READWRITE:\n                prot = PAGE_READWRITE;\n                acc = FILE_MAP_READ | FILE_MAP_WRITE;\n                break;\n            case PHP_STREAM_MAP_MODE_SHARED_READONLY:\n                prot = PAGE_READONLY;\n                acc = FILE_MAP_READ;\n                /* TODO: we should assign a name for the mapping */\n                break;\n            case PHP_STREAM_MAP_MODE_SHARED_READWRITE:\n                prot = PAGE_READWRITE;\n                acc = FILE_MAP_READ | FILE_MAP_WRITE;\n                /* TODO: we should assign a name for the mapping */\n                break;\n            default:\n                return PHP_STREAM_OPTION_RETURN_ERR;\n            }\n\n            /* create a mapping capable of viewing the whole file (this costs no real resources) */\n            data->file_mapping = CreateFileMapping(hfile, NULL, prot, 0, 0, NULL);\n\n            if (data->file_mapping == NULL) {\n                return PHP_STREAM_OPTION_RETURN_ERR;\n            }\n\n            if (!GetFileSizeEx(hfile, &file_size)) {\n                CloseHandle(data->file_mapping);\n                data->file_mapping = NULL;\n                return PHP_STREAM_OPTION_RETURN_ERR;\n            }\n# if defined(_WIN64)\n            size = file_size.QuadPart;\n# else\n            if (file_size.HighPart) {\n                CloseHandle(data->file_mapping);\n                data->file_mapping = NULL;\n                return PHP_STREAM_OPTION_RETURN_ERR;\n            } else {\n                size = file_size.LowPart;\n            }\n# endif\n            if (range->offset > size) {\n                range->offset = size;\n            }\n            if (range->length == 0 || range->length > size - range->offset) {\n                range->length = size - range->offset;\n            }\n\n            /* figure out how big a chunk to map to be able to view the part that we need */\n            if (range->offset != 0) {\n                SYSTEM_INFO info;\n                DWORD gran;\n\n                GetSystemInfo(&info);\n                gran = info.dwAllocationGranularity;\n                ZEND_ASSERT(gran != 0 && (gran & (gran - 1)) == 0);\n                size_t rounded_offset = (range->offset / gran) * gran;\n                delta = range->offset - rounded_offset;\n                loffs = (DWORD)rounded_offset;\n                hoffs = (DWORD)(rounded_offset >> 32);\n            }\n\n            /* MapViewOfFile()ing zero bytes would map to the end of the file; match *nix behavior instead */\n            if (range->length + delta == 0) {\n                return PHP_STREAM_OPTION_RETURN_ERR;\n            }\n\n            data->last_mapped_addr = MapViewOfFile(data->file_mapping, acc, hoffs, loffs, range->length + delta);\n\n            if (data->last_mapped_addr) {\n                /* give them back the address of the start offset they requested */\n                range->mapped = data->last_mapped_addr + delta;\n                return PHP_STREAM_OPTION_RETURN_OK;\n            }\n\n            CloseHandle(data->file_mapping);\n            data->file_mapping = NULL;\n\n            return PHP_STREAM_OPTION_RETURN_ERR;\n\n        case PHP_STREAM_MMAP_UNMAP:\n            if (data->last_mapped_addr) {\n                UnmapViewOfFile(data->last_mapped_addr);\n                data->last_mapped_addr = NULL;\n                CloseHandle(data->file_mapping);\n                data->file_mapping = NULL;\n                return PHP_STREAM_OPTION_RETURN_OK;\n            }\n            return PHP_STREAM_OPTION_RETURN_ERR;\n\n        default:\n            return PHP_STREAM_OPTION_RETURN_ERR;\n        }\n    }\n#endif\n        return PHP_STREAM_OPTION_RETURN_NOTIMPL;\n\n#if PHP_VERSION_ID >= 80100\n    case PHP_STREAM_OPTION_SYNC_API:\n        switch (value) {\n        case PHP_STREAM_SYNC_SUPPORTED:\n            return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;\n        case PHP_STREAM_SYNC_FSYNC:\n            return php_stdiop_sync(stream, 0) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;\n        case PHP_STREAM_SYNC_FDSYNC:\n            return php_stdiop_sync(stream, 1) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;\n        }\n        /* Invalid option passed */\n        return PHP_STREAM_OPTION_RETURN_ERR;\n#endif\n\n    case PHP_STREAM_OPTION_TRUNCATE_API:\n        switch (value) {\n        case PHP_STREAM_TRUNCATE_SUPPORTED:\n            return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;\n\n        case PHP_STREAM_TRUNCATE_SET_SIZE: {\n            ptrdiff_t new_size = *(ptrdiff_t*)ptrparam;\n            if (new_size < 0) {\n                return PHP_STREAM_OPTION_RETURN_ERR;\n            }\n#ifdef PHP_WIN32\n            HANDLE h = (HANDLE) _get_osfhandle(fd);\n            if (INVALID_HANDLE_VALUE == h) {\n                return PHP_STREAM_OPTION_RETURN_ERR;\n            }\n\n            LARGE_INTEGER sz, old_sz;\n            sz.QuadPart = 0;\n\n            if (!SetFilePointerEx(h, sz, &old_sz, FILE_CURRENT)) {\n                return PHP_STREAM_OPTION_RETURN_ERR;\n            }\n\n#ifdef _WIN64\n            sz.QuadPart = new_size;\n#else\n            sz.HighPart = 0;\n            sz.LowPart = new_size;\n#endif\n            if (!SetFilePointerEx(h, sz, NULL, FILE_BEGIN)) {\n                return PHP_STREAM_OPTION_RETURN_ERR;\n            }\n            if (0 == SetEndOfFile(h)) {\n                return PHP_STREAM_OPTION_RETURN_ERR;\n            }\n            if (!SetFilePointerEx(h, old_sz, NULL, FILE_BEGIN)) {\n                return PHP_STREAM_OPTION_RETURN_ERR;\n            }\n            return PHP_STREAM_OPTION_RETURN_OK;\n#else\n            return ftruncate(fd, new_size) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;\n#endif\n        }\n        }\n        return PHP_STREAM_OPTION_RETURN_NOTIMPL;\n\n#ifdef PHP_WIN32\n    case PHP_STREAM_OPTION_PIPE_BLOCKING:\n        data->is_pipe_blocking = value;\n        return PHP_STREAM_OPTION_RETURN_OK;\n#endif\n    case PHP_STREAM_OPTION_META_DATA_API:\n        if (fd == -1) return -1;\n#ifdef O_NONBLOCK\n        flags = fcntl(fd, F_GETFL, 0);\n\n        add_assoc_bool((zval *) ptrparam, \"timed_out\", 0);\n        add_assoc_bool((zval *) ptrparam, \"blocked\", (flags & O_NONBLOCK) ? 0 : 1);\n        add_assoc_bool((zval *) ptrparam, \"eof\", stream->eof);\n\n        return PHP_STREAM_OPTION_RETURN_OK;\n#endif\n        return -1;\n    default:\n        return PHP_STREAM_OPTION_RETURN_NOTIMPL;\n    }\n}\n/* }}} */\n\n/* {{{ plain files opendir/readdir implementation */\nstatic php_stream_size_t php_plain_files_dirstream_read(php_stream *stream, char *buf, size_t count) {\n    DIR *dir = (DIR *) stream->abstract;\n    struct dirent *result;\n    php_stream_dirent *ent = (php_stream_dirent *) buf;\n\n    /* avoid problems if someone mis-uses the stream */\n    if (count != sizeof(php_stream_dirent)) {\n        return -1;\n    }\n\n    result = readdir(dir);\n    if (result) {\n        PHP_STRLCPY(ent->d_name, result->d_name, sizeof(ent->d_name), strlen(result->d_name));\n#if PHP_VERSION_ID >= 80300\n#ifdef _DIRENT_HAVE_D_TYPE\n        ent->d_type = result->d_type;\n#else\n        ent->d_type = DT_UNKNOWN;\n#endif\n#endif\n        return sizeof(php_stream_dirent);\n    }\n    return 0;\n}\n\nstatic int php_plain_files_dirstream_close(php_stream *stream, int close_handle) {\n    return closedir((DIR *) stream->abstract);\n}\n\nstatic int php_plain_files_dirstream_rewind(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs) {\n    rewinddir((DIR *) stream->abstract);\n    return 0;\n}\n\nstatic php_stream_ops php_plain_files_dirstream_ops = {\n    NULL,\n    php_plain_files_dirstream_read,\n    php_plain_files_dirstream_close,\n    NULL,\n    \"dir\",\n    php_plain_files_dirstream_rewind,\n    NULL, /* cast */\n    NULL, /* stat */\n    NULL  /* set_option */\n};\n\nstatic php_stream *php_plain_files_dir_opener(php_stream_wrapper *wrapper,\n                                              const char *path,\n                                              const char *mode,\n                                              int options,\n                                              zend_string **opened_path,\n                                              php_stream_context *context STREAMS_DC) {\n    DIR *dir = NULL;\n    php_stream *stream = NULL;\n\n#ifdef HAVE_GLOB\n    if (options & STREAM_USE_GLOB_DIR_OPEN) {\n        return php_glob_stream_wrapper.wops->dir_opener(\n            (php_stream_wrapper *) &php_glob_stream_wrapper, path, mode, options, opened_path, context STREAMS_REL_CC);\n    }\n#endif\n\n    if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(path)) {\n        return NULL;\n    }\n\n    dir = opendir(path);\n\n#ifdef PHP_WIN32\n    if (!dir) {\n        php_win32_docref2_from_error(GetLastError(), path, path);\n    }\n\n    if (dir && dir->finished) {\n        closedir(dir);\n        dir = NULL;\n    }\n#endif\n    if (dir) {\n        stream = php_stream_alloc(&php_plain_files_dirstream_ops, dir, 0, mode);\n        if (stream == NULL) closedir(dir);\n    }\n\n    return stream;\n}\n/* }}} */\n\nstatic php_stream *_sw_php_stream_fopen(const char *filename,\n                                    const char *mode,\n                                    zend_string **opened_path,\n                                    int options STREAMS_DC) {\n    char _realpath[MAXPATHLEN];\n    int open_flags;\n    int fd;\n    php_stream *ret;\n    int persistent = options & STREAM_OPEN_PERSISTENT;\n    char *persistent_id = NULL;\n\n    if (FAILURE == sw_php_stream_parse_fopen_modes(mode, &open_flags)) {\n        php_stream_wrapper_log_error(&php_plain_files_wrapper, options, \"`%s' is not a valid mode for fopen\", mode);\n        return NULL;\n    }\n\n    if (options & STREAM_ASSUME_REALPATH) {\n        strlcpy(_realpath, filename, sizeof(_realpath));\n    } else {\n        if (expand_filepath(filename, _realpath) == NULL) {\n            return NULL;\n        }\n    }\n\n    if (persistent) {\n        spprintf(&persistent_id, 0, \"streams_stdio_%d_%s\", open_flags, _realpath);\n        switch (php_stream_from_persistent_id(persistent_id, &ret)) {\n        case PHP_STREAM_PERSISTENT_SUCCESS:\n            if (opened_path) {\n                // TODO: avoid reallocation???\n                *opened_path = zend_string_init(_realpath, strlen(_realpath), 0);\n            }\n            /* fall through */\n#if PHP_VERSION_ID >= 80100\n            ZEND_FALLTHROUGH;\n#endif\n\n        case PHP_STREAM_PERSISTENT_FAILURE:\n            efree(persistent_id);\n            return ret;\n        }\n    }\n#ifdef PHP_WIN32\n    fd = php_win32_ioutil_open(_realpath, open_flags, 0666);\n#else\n    fd = open(_realpath, open_flags, 0666);\n#endif\n    if (fd != -1) {\n        ret = sw_php_stream_fopen_from_fd_rel(fd, mode, persistent_id, (open_flags & O_APPEND) == 0);\n        if (ret) {\n            if (opened_path) {\n                *opened_path = zend_string_init(_realpath, strlen(_realpath), 0);\n            }\n            if (persistent_id) {\n                efree(persistent_id);\n            }\n\n            /* WIN32 always set ISREG flag */\n#ifndef PHP_WIN32\n            /* sanity checks for include/require.\n             * We check these after opening the stream, so that we save\n             * on fstat() syscalls */\n            if (options & STREAM_OPEN_FOR_INCLUDE) {\n                php_stdio_stream_data *self = (php_stdio_stream_data *) ret->abstract;\n                int r;\n\n                r = do_fstat(self, 0);\n                if ((r == 0 && !S_ISREG(self->sb.st_mode))) {\n                    if (opened_path) {\n                        zend_string_release_ex(*opened_path, 0);\n                        *opened_path = NULL;\n                    }\n                    php_stream_close(ret);\n                    return NULL;\n                }\n\n                /* Make sure the fstat result is reused when we later try to get the\n                 * file size. */\n                self->no_forced_fstat = 1;\n            }\n\n            if (options & STREAM_USE_BLOCKING_PIPE) {\n                php_stdio_stream_data *self = (php_stdio_stream_data *) ret->abstract;\n                self->is_pipe_blocking = 1;\n            }\n#endif\n\n            return ret;\n        }\n        close(fd);\n    }\n    if (persistent_id) {\n        efree(persistent_id);\n    }\n    return NULL;\n}\n\nstatic php_stream *php_plain_files_stream_opener(php_stream_wrapper *wrapper,\n                                 const char *path,\n                                 const char *mode,\n                                 int options,\n                                 zend_string **opened_path,\n                                 php_stream_context *context STREAMS_DC) {\n    if (SW_STR_ISTARTS_WITH(path, strlen(path), SW_ASYNC_FILE_PROTOCOL \"://\")) {\n        path += sizeof(SW_ASYNC_FILE_PROTOCOL \"://\") - 1;\n    }\n    if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(path)) {\n        return NULL;\n    }\n\n    php_stream *stream;\n    /** phar_open_archive_fp, cannot use async-io */\n    if (EG(current_execute_data) && EG(current_execute_data)->func &&\n        ZEND_USER_CODE(EG(current_execute_data)->func->type)) {\n        const zend_op *opline = EG(current_execute_data)->opline;\n        if (opline && opline->opcode == ZEND_INCLUDE_OR_EVAL &&\n            (opline->extended_value & (ZEND_INCLUDE | ZEND_INCLUDE_ONCE | ZEND_REQUIRE | ZEND_REQUIRE_ONCE))) {\n            goto _open_for_include;\n        }\n    }\n    /** include file, cannot use async-io */\n    if (options & STREAM_OPEN_FOR_INCLUDE) {\n    _open_for_include:\n        stream = php_stream_fopen_rel(path, mode, opened_path, options);\n        if (stream == NULL) {\n            return NULL;\n        }\n        stream->ops = php_swoole_get_ori_php_stream_stdio_ops();\n        return stream;\n    }\n\n    return sw_php_stream_fopen_rel(path, mode, opened_path, options);\n}\n\nstatic int php_plain_files_url_stater(\n    php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context) {\n#if PHP_VERSION_ID >= 80100\n    if (!(flags & PHP_STREAM_URL_STAT_IGNORE_OPEN_BASEDIR)) {\n#endif\n        if (strncasecmp(url, \"file://\", sizeof(\"file://\") - 1) == 0) {\n            url += sizeof(\"file://\") - 1;\n        }\n\n        if (php_check_open_basedir_ex(url, (flags & PHP_STREAM_URL_STAT_QUIET) ? 0 : 1)) {\n            return -1;\n        }\n#if PHP_VERSION_ID >= 80100\n    }\n#endif\n\n#ifdef PHP_WIN32\n    if (flags & PHP_STREAM_URL_STAT_LINK) {\n        return lstat(url, &ssb->sb);\n    }\n#else\n#ifdef HAVE_SYMLINK\n    if (flags & PHP_STREAM_URL_STAT_LINK) {\n        return lstat(url, &ssb->sb);\n    } else\n#endif\n#endif\n    return stat(url, &ssb->sb);\n}\n\nstatic int php_plain_files_unlink(php_stream_wrapper *wrapper,\n                                  const char *url,\n                                  int options,\n                                  php_stream_context *context) {\n    int ret;\n\n    if (strncasecmp(url, \"file://\", sizeof(\"file://\") - 1) == 0) {\n        url += sizeof(\"file://\") - 1;\n    }\n\n    if (php_check_open_basedir(url)) {\n        return 0;\n    }\n\n    ret = unlink(url);\n    if (ret == -1) {\n        if (options & REPORT_ERRORS) {\n            php_error_docref1(NULL, url, E_WARNING, \"%s\", strerror(errno));\n        }\n        return 0;\n    }\n\n    /* Clear stat cache (and realpath cache) */\n    php_clear_stat_cache(1, NULL, 0);\n\n    return 1;\n}\n\nstatic int php_plain_files_rename(\n    php_stream_wrapper *wrapper, const char *url_from, const char *url_to, int options, php_stream_context *context) {\n    int ret;\n\n    if (!url_from || !url_to) {\n        return 0;\n    }\n\n#ifdef PHP_WIN32\n    if (!php_win32_check_trailing_space(url_from, (int) strlen(url_from))) {\n        php_win32_docref2_from_error(ERROR_INVALID_NAME, url_from, url_to);\n        return 0;\n    }\n    if (!php_win32_check_trailing_space(url_to, (int) strlen(url_to))) {\n        php_win32_docref2_from_error(ERROR_INVALID_NAME, url_from, url_to);\n        return 0;\n    }\n#endif\n\n    if (strncasecmp(url_from, \"file://\", sizeof(\"file://\") - 1) == 0) {\n        url_from += sizeof(\"file://\") - 1;\n    }\n\n    if (strncasecmp(url_to, \"file://\", sizeof(\"file://\") - 1) == 0) {\n        url_to += sizeof(\"file://\") - 1;\n    }\n\n    if (php_check_open_basedir(url_from) || php_check_open_basedir(url_to)) {\n        return 0;\n    }\n\n    ret = rename(url_from, url_to);\n\n    if (ret == -1) {\n#ifndef PHP_WIN32\n#ifdef EXDEV\n        if (errno == EXDEV) {\n            zend_stat_t sb;\n#if !defined(ZTS) && !defined(TSRM_WIN32)\n            /* not sure what to do in ZTS case, umask is not thread-safe */\n            int oldmask = umask(077);\n#endif\n            int success = 0;\n            if (php_copy_file(url_from, url_to) == SUCCESS) {\n                if (stat(url_from, &sb) == 0) {\n                    success = 1;\n#ifndef TSRM_WIN32\n                    /*\n                     * Try to set user and permission info on the target.\n                     * If we're not root, then some of these may fail.\n                     * We try chown first, to set proper group info, relying\n                     * on the system environment to have proper umask to not allow\n                     * access to the file in the meantime.\n                     */\n                    if (chown(url_to, sb.st_uid, sb.st_gid)) {\n                        php_error_docref2(NULL, url_from, url_to, E_WARNING, \"%s\", strerror(errno));\n                        if (errno != EPERM) {\n                            success = 0;\n                        }\n                    }\n\n                    if (success) {\n                        if (chmod(url_to, sb.st_mode)) {\n                            php_error_docref2(NULL, url_from, url_to, E_WARNING, \"%s\", strerror(errno));\n                            if (errno != EPERM) {\n                                success = 0;\n                            }\n                        }\n                    }\n#endif\n                    if (success) {\n                        unlink(url_from);\n                    }\n                } else {\n                    php_error_docref2(NULL, url_from, url_to, E_WARNING, \"%s\", strerror(errno));\n                }\n            } else {\n                php_error_docref2(NULL, url_from, url_to, E_WARNING, \"%s\", strerror(errno));\n            }\n#if !defined(ZTS) && !defined(TSRM_WIN32)\n            umask(oldmask);\n#endif\n            return success;\n        }\n#endif\n#endif\n\n#ifdef PHP_WIN32\n        php_win32_docref2_from_error(GetLastError(), url_from, url_to);\n#else\n        php_error_docref2(NULL, url_from, url_to, E_WARNING, \"%s\", strerror(errno));\n#endif\n        return 0;\n    }\n\n    /* Clear stat cache (and realpath cache) */\n    php_clear_stat_cache(1, NULL, 0);\n\n    return 1;\n}\n\nstatic int php_plain_files_mkdir(\n    php_stream_wrapper *wrapper, const char *dir, int mode, int options, php_stream_context *context) {\n    if (strncasecmp(dir, \"file://\", sizeof(\"file://\") - 1) == 0) {\n        dir += sizeof(\"file://\") - 1;\n    }\n\n    if (!(options & PHP_STREAM_MKDIR_RECURSIVE)) {\n        return sw_php_mkdir(dir, mode) == 0;\n    }\n\n    char buf[MAXPATHLEN];\n    if (!expand_filepath_with_mode(dir, buf, NULL, 0, CWD_EXPAND)) {\n        php_error_docref(NULL, E_WARNING, \"Invalid path\");\n        return 0;\n    }\n\n    if (php_check_open_basedir(buf)) {\n        return 0;\n    }\n\n    /* we look for directory separator from the end of string, thus hopefully reducing our work load */\n    char *p;\n    zend_stat_t sb;\n    size_t dir_len = strlen(dir), offset = 0;\n    char *e = buf + strlen(buf);\n\n    if ((p = (char *) memchr(buf, DEFAULT_SLASH, dir_len))) {\n        offset = p - buf + 1;\n    }\n\n    if (p && dir_len == 1) {\n        /* buf == \"DEFAULT_SLASH\" */\n    } else {\n        /* find a top level directory we need to create */\n        while ((p = strrchr(buf + offset, DEFAULT_SLASH)) || (offset != 1 && (p = strrchr(buf, DEFAULT_SLASH)))) {\n            int n = 0;\n\n            *p = '\\0';\n            while (p > buf && *(p - 1) == DEFAULT_SLASH) {\n                ++n;\n                --p;\n                *p = '\\0';\n            }\n            if (stat(buf, &sb) == 0) {\n                while (1) {\n                    *p = DEFAULT_SLASH;\n                    if (!n) break;\n                    --n;\n                    ++p;\n                }\n                break;\n            }\n        }\n    }\n\n    if (!p) {\n        p = buf;\n    }\n    while (true) {\n        int ret = mkdir(buf, (mode_t) mode);\n        if (ret < 0 && errno != EEXIST) {\n            if (options & REPORT_ERRORS) {\n                php_error_docref(NULL, E_WARNING, \"%s\", strerror(errno));\n            }\n            return 0;\n        }\n\n        bool replaced_slash = false;\n        while (++p != e) {\n            if (*p == '\\0') {\n                replaced_slash = true;\n                *p = DEFAULT_SLASH;\n                if (*(p + 1) != '\\0') {\n                    break;\n                }\n            }\n        }\n        if (p == e || !replaced_slash) {\n            /* No more directories to create */\n            /* issue a warning to client when the last directory was created failed */\n            if (ret < 0) {\n                if (options & REPORT_ERRORS) {\n                    php_error_docref(NULL, E_WARNING, \"%s\", strerror(errno));\n                }\n                return 0;\n            }\n            return 1;\n        }\n    }\n}\n\nstatic int php_plain_files_rmdir(php_stream_wrapper *wrapper,\n                                 const char *url,\n                                 int options,\n                                 php_stream_context *context) {\n    if (strncasecmp(url, \"file://\", sizeof(\"file://\") - 1) == 0) {\n        url += sizeof(\"file://\") - 1;\n    }\n\n    if (php_check_open_basedir(url)) {\n        return 0;\n    }\n\n#ifdef PHP_WIN32\n    if (!php_win32_check_trailing_space(url, (int) strlen(url))) {\n        php_error_docref1(NULL, url, E_WARNING, \"%s\", strerror(ENOENT));\n        return 0;\n    }\n#endif\n\n    if (rmdir(url) < 0) {\n        php_error_docref1(NULL, url, E_WARNING, \"%s\", strerror(errno));\n        return 0;\n    }\n\n    /* Clear stat cache (and realpath cache) */\n    php_clear_stat_cache(1, NULL, 0);\n\n    return 1;\n}\n\nstatic int php_plain_files_metadata(\n    php_stream_wrapper *wrapper, const char *url, int option, void *value, php_stream_context *context) {\n    struct utimbuf *newtime;\n#ifndef PHP_WIN32\n    uid_t uid;\n    gid_t gid;\n#endif\n    mode_t mode;\n    int ret = 0;\n\n#ifdef PHP_WIN32\n    if (!php_win32_check_trailing_space(url, strlen(url))) {\n        php_error_docref1(NULL, url, E_WARNING, \"%s\", strerror(ENOENT));\n        return 0;\n    }\n#endif\n\n    if (strncasecmp(url, \"file://\", sizeof(\"file://\") - 1) == 0) {\n        url += sizeof(\"file://\") - 1;\n    }\n\n    if (php_check_open_basedir(url)) {\n        return 0;\n    }\n\n    switch (option) {\n    case PHP_STREAM_META_TOUCH:\n        newtime = (struct utimbuf *) value;\n        if (access(url, F_OK) != 0) {\n            int file = open(url, O_CREAT | O_WRONLY | O_TRUNC, 0666);\n            if (file == -1) {\n                php_error_docref1(NULL, url, E_WARNING, \"Unable to create file %s because %s\", url, strerror(errno));\n                return 0;\n            }\n            close(file);\n        }\n\n        ret = utime(url, newtime);\n        break;\n#ifndef PHP_WIN32\n    case PHP_STREAM_META_OWNER_NAME:\n    case PHP_STREAM_META_OWNER:\n        if (option == PHP_STREAM_META_OWNER_NAME) {\n            if (php_get_uid_by_name((char *) value, &uid) != SUCCESS) {\n                php_error_docref1(NULL, url, E_WARNING, \"Unable to find uid for %s\", (char *) value);\n                return 0;\n            }\n        } else {\n            uid = (uid_t) * (long *) value;\n        }\n        ret = chown(url, uid, -1);\n        break;\n    case PHP_STREAM_META_GROUP:\n    case PHP_STREAM_META_GROUP_NAME:\n        if (option == PHP_STREAM_META_GROUP_NAME) {\n            if (php_get_gid_by_name((char *) value, &gid) != SUCCESS) {\n                php_error_docref1(NULL, url, E_WARNING, \"Unable to find gid for %s\", (char *) value);\n                return 0;\n            }\n        } else {\n            gid = (gid_t) * (long *) value;\n        }\n        ret = chown(url, -1, gid);\n        break;\n#endif\n    case PHP_STREAM_META_ACCESS:\n        mode = (mode_t) * (zend_long *) value;\n        ret = chmod(url, mode);\n        break;\n    default:\n        zend_value_error(\"Unknown option %d for stream_metadata\", option);\n        return 0;\n    }\n    if (ret == -1) {\n        php_error_docref1(NULL, url, E_WARNING, \"Operation failed: %s\", strerror(errno));\n        return 0;\n    }\n    php_clear_stat_cache(0, NULL, 0);\n    return 1;\n}\n\nstatic php_stream_wrapper_ops sw_php_plain_files_wrapper_ops = {\n    php_plain_files_stream_opener,\n    NULL,\n    NULL,\n    php_plain_files_url_stater,\n    php_plain_files_dir_opener,\n    \"plainfile/coroutine\",\n    php_plain_files_unlink,\n    php_plain_files_rename,\n    php_plain_files_mkdir,\n    php_plain_files_rmdir,\n    php_plain_files_metadata\n};\n\nPHPAPI php_stream_wrapper sw_php_plain_files_wrapper = {\n    &sw_php_plain_files_wrapper_ops,\n    NULL,\n    0\n};\n\nstatic void php_stream_mode_sanitize_fdopen_fopencookie(php_stream *stream, char *result) {\n    /* replace modes not supported by fdopen and fopencookie, but supported\n     * by PHP's fread(), so that their calls won't fail */\n    const char *cur_mode = stream->mode;\n    int has_plus = 0, has_bin = 0, i, res_curs = 0;\n\n    if (cur_mode[0] == 'r' || cur_mode[0] == 'w' || cur_mode[0] == 'a') {\n        result[res_curs++] = cur_mode[0];\n    } else {\n        /* assume cur_mode[0] is 'c' or 'x'; substitute by 'w', which should not\n         * truncate anything in fdopen/fopencookie */\n        result[res_curs++] = 'w';\n\n        /* x is allowed (at least by glibc & compat), but not as the 1st mode\n         * as in PHP and in any case is (at best) ignored by fdopen and fopencookie */\n    }\n\n    /* assume current mode has at most length 4 (e.g. wbn+) */\n    for (i = 1; i < 4 && cur_mode[i] != '\\0'; i++) {\n        if (cur_mode[i] == 'b') {\n            has_bin = 1;\n        } else if (cur_mode[i] == '+') {\n            has_plus = 1;\n        }\n        /* ignore 'n', 't' or other stuff */\n    }\n\n    if (has_bin) {\n        result[res_curs++] = 'b';\n    }\n    if (has_plus) {\n        result[res_curs++] = '+';\n    }\n    result[res_curs] = '\\0';\n}\n\nstatic int sw_php_mkdir(const char *dir, zend_long mode) {\n    int ret;\n\n    if (php_check_open_basedir(dir)) {\n        return -1;\n    }\n\n    if ((ret = mkdir(dir, (mode_t) mode)) < 0) {\n        php_error_docref(NULL, E_WARNING, \"%s\", strerror(errno));\n    }\n\n    return ret;\n}\n"
  },
  {
    "path": "thirdparty/php/zend/zend_execute.c",
    "content": "\n#include \"zend.h\"\n#include \"zend_compile.h\"\n\n#if defined(ZEND_VM_FP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID))\n#define EXECUTE_DATA_D void\n#define EXECUTE_DATA_C\n#define EXECUTE_DATA_DC\n#define EXECUTE_DATA_CC\n#define NO_EXECUTE_DATA_CC\n#else\n#define EXECUTE_DATA_D zend_execute_data *execute_data\n#define EXECUTE_DATA_C execute_data\n#define EXECUTE_DATA_DC , EXECUTE_DATA_D\n#define EXECUTE_DATA_CC , EXECUTE_DATA_C\n#define NO_EXECUTE_DATA_CC , NULL\n#endif\n\n#if defined(ZEND_VM_FP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID))\n#define OPLINE_D void\n#define OPLINE_C\n#define OPLINE_DC\n#define OPLINE_CC\n#else\n#define OPLINE_D const zend_op *opline\n#define OPLINE_C opline\n#define OPLINE_DC , OPLINE_D\n#define OPLINE_CC , OPLINE_C\n#endif\n\n#define FREE_OP(type, var)                                                                                             \\\n    if ((type) & (IS_TMP_VAR | IS_VAR)) {                                                                              \\\n        zval_ptr_dtor_nogc(EX_VAR(var));                                                                               \\\n    }\n\n#define RETURN_VALUE_USED(opline) ((opline)->result_type != IS_UNUSED)\n\n#define CV_DEF_OF(i) (EX(func)->op_array.vars[i])\n\n#define get_zval_ptr(op_type, node, type) _get_zval_ptr(op_type, node, type EXECUTE_DATA_CC OPLINE_CC)\n#define get_zval_ptr_deref(op_type, node, type) _get_zval_ptr_deref(op_type, node, type EXECUTE_DATA_CC OPLINE_CC)\n#define get_zval_ptr_undef(op_type, node, type) _get_zval_ptr_undef(op_type, node, type EXECUTE_DATA_CC OPLINE_CC)\n#define get_op_data_zval_ptr_r(op_type, node) _get_op_data_zval_ptr_r(op_type, node EXECUTE_DATA_CC OPLINE_CC)\n#define get_op_data_zval_ptr_deref_r(op_type, node)                                                                    \\\n    _get_op_data_zval_ptr_deref_r(op_type, node EXECUTE_DATA_CC OPLINE_CC)\n#define get_zval_ptr_ptr(op_type, node, type) _get_zval_ptr_ptr(op_type, node, type EXECUTE_DATA_CC)\n#define get_zval_ptr_ptr_undef(op_type, node, type) _get_zval_ptr_ptr(op_type, node, type EXECUTE_DATA_CC)\n#define get_obj_zval_ptr(op_type, node, type) _get_obj_zval_ptr(op_type, node, type EXECUTE_DATA_CC OPLINE_CC)\n#define get_obj_zval_ptr_undef(op_type, node, type)                                                                    \\\n    _get_obj_zval_ptr_undef(op_type, node, type EXECUTE_DATA_CC OPLINE_CC)\n#define get_obj_zval_ptr_ptr(op_type, node, type) _get_obj_zval_ptr_ptr(op_type, node, type EXECUTE_DATA_CC)\n\n#if PHP_VERSION_ID < 80300\nstatic ZEND_COLD void zend_illegal_container_offset(zend_string *container, const zval *offset, int type) {\n    switch (type) {\n    case BP_VAR_IS:\n        zend_type_error(\"Cannot access offset of type %s in isset or empty\",\n                zend_zval_type_name(offset));\n        return;\n    case BP_VAR_UNSET:\n        /* Consistent error for when trying to unset a string offset */\n        if (zend_string_equals(container, ZSTR_KNOWN(ZEND_STR_STRING))) {\n            zend_throw_error(NULL, \"Cannot unset string offsets\");\n        } else {\n            zend_type_error(\"Cannot unset offset of type %s on %s\", zend_zval_type_name(offset), ZSTR_VAL(container));\n        }\n        return;\n    default:\n        zend_type_error(\"Cannot access offset of type %s on %s\",\n                zend_zval_type_name(offset), ZSTR_VAL(container));\n        return;\n    }\n}\n#endif\n\nstatic zend_always_inline zval *_get_zval_ptr_tmp(uint32_t var EXECUTE_DATA_DC) {\n    zval *ret = EX_VAR(var);\n\n    ZEND_ASSERT(Z_TYPE_P(ret) != IS_REFERENCE);\n\n    return ret;\n}\n\nstatic zend_always_inline zval *_get_zval_ptr_var(uint32_t var EXECUTE_DATA_DC) {\n    zval *ret = EX_VAR(var);\n\n    return ret;\n}\n\nstatic zend_always_inline zval *_get_zval_ptr_var_deref(uint32_t var EXECUTE_DATA_DC) {\n    zval *ret = EX_VAR(var);\n\n    ZVAL_DEREF(ret);\n    return ret;\n}\n\nstatic zend_never_inline ZEND_COLD zval *zval_undefined_cv(uint32_t var EXECUTE_DATA_DC) {\n    if (EXPECTED(EG(exception) == NULL)) {\n        zend_string *cv = CV_DEF_OF(EX_VAR_TO_NUM(var));\n        zend_error(E_WARNING, \"Undefined variable $%s\", ZSTR_VAL(cv));\n    }\n    return &EG(uninitialized_zval);\n}\n\nstatic zend_never_inline ZEND_COLD zval *ZEND_FASTCALL _zval_undefined_op1(EXECUTE_DATA_D) {\n    return zval_undefined_cv(EX(opline)->op1.var EXECUTE_DATA_CC);\n}\n\nstatic zend_never_inline ZEND_COLD zval *ZEND_FASTCALL _zval_undefined_op2(EXECUTE_DATA_D) {\n    return zval_undefined_cv(EX(opline)->op2.var EXECUTE_DATA_CC);\n}\n\n#define ZVAL_UNDEFINED_OP1() _zval_undefined_op1(EXECUTE_DATA_C)\n#define ZVAL_UNDEFINED_OP2() _zval_undefined_op2(EXECUTE_DATA_C)\n\nstatic zend_never_inline ZEND_COLD zval *_get_zval_cv_lookup(zval *ptr, uint32_t var, int type EXECUTE_DATA_DC) {\n    switch (type) {\n    case BP_VAR_R:\n    case BP_VAR_UNSET:\n        ptr = zval_undefined_cv(var EXECUTE_DATA_CC);\n        break;\n    case BP_VAR_IS:\n        ptr = &EG(uninitialized_zval);\n        break;\n    case BP_VAR_RW:\n        zval_undefined_cv(var EXECUTE_DATA_CC);\n        ZEND_FALLTHROUGH;\n    case BP_VAR_W:\n        ZVAL_NULL(ptr);\n        break;\n    }\n    return ptr;\n}\n\nstatic zend_always_inline zval *_get_zval_ptr_cv(uint32_t var, int type EXECUTE_DATA_DC) {\n    zval *ret = EX_VAR(var);\n\n    if (UNEXPECTED(Z_TYPE_P(ret) == IS_UNDEF)) {\n        if (type == BP_VAR_W) {\n            ZVAL_NULL(ret);\n        } else {\n            return _get_zval_cv_lookup(ret, var, type EXECUTE_DATA_CC);\n        }\n    }\n    return ret;\n}\n\nstatic zend_always_inline zval *_get_zval_ptr_cv_deref(uint32_t var, int type EXECUTE_DATA_DC) {\n    zval *ret = EX_VAR(var);\n\n    if (UNEXPECTED(Z_TYPE_P(ret) == IS_UNDEF)) {\n        if (type == BP_VAR_W) {\n            ZVAL_NULL(ret);\n            return ret;\n        } else {\n            return _get_zval_cv_lookup(ret, var, type EXECUTE_DATA_CC);\n        }\n    }\n    ZVAL_DEREF(ret);\n    return ret;\n}\n\nstatic zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_R(uint32_t var EXECUTE_DATA_DC) {\n    zval *ret = EX_VAR(var);\n\n    if (UNEXPECTED(Z_TYPE_P(ret) == IS_UNDEF)) {\n        return zval_undefined_cv(var EXECUTE_DATA_CC);\n    }\n    return ret;\n}\n\nstatic zend_always_inline zval *_get_zval_ptr_cv_deref_BP_VAR_R(uint32_t var EXECUTE_DATA_DC) {\n    zval *ret = EX_VAR(var);\n\n    if (UNEXPECTED(Z_TYPE_P(ret) == IS_UNDEF)) {\n        return zval_undefined_cv(var EXECUTE_DATA_CC);\n    }\n    ZVAL_DEREF(ret);\n    return ret;\n}\n\nstatic zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_IS(uint32_t var EXECUTE_DATA_DC) {\n    zval *ret = EX_VAR(var);\n\n    return ret;\n}\n\nstatic zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_RW(uint32_t var EXECUTE_DATA_DC) {\n    zval *ret = EX_VAR(var);\n\n    if (UNEXPECTED(Z_TYPE_P(ret) == IS_UNDEF)) {\n        zval_undefined_cv(var EXECUTE_DATA_CC);\n        ZVAL_NULL(ret);\n        return ret;\n    }\n    return ret;\n}\n\nstatic zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_W(uint32_t var EXECUTE_DATA_DC) {\n    zval *ret = EX_VAR(var);\n\n    if (Z_TYPE_P(ret) == IS_UNDEF) {\n        ZVAL_NULL(ret);\n    }\n    return ret;\n}\n\nstatic zend_always_inline zval *_get_zval_ptr(int op_type, znode_op node, int type EXECUTE_DATA_DC OPLINE_DC) {\n    if (op_type & (IS_TMP_VAR | IS_VAR)) {\n        if (!ZEND_DEBUG || op_type == IS_VAR) {\n            return _get_zval_ptr_var(node.var EXECUTE_DATA_CC);\n        } else {\n            ZEND_ASSERT(op_type == IS_TMP_VAR);\n            return _get_zval_ptr_tmp(node.var EXECUTE_DATA_CC);\n        }\n    } else {\n        if (op_type == IS_CONST) {\n            return RT_CONSTANT(opline, node);\n        } else if (op_type == IS_CV) {\n            return _get_zval_ptr_cv(node.var, type EXECUTE_DATA_CC);\n        } else {\n            return NULL;\n        }\n    }\n}\n\nstatic zend_always_inline zval *_get_op_data_zval_ptr_r(int op_type, znode_op node EXECUTE_DATA_DC OPLINE_DC) {\n    if (op_type & (IS_TMP_VAR | IS_VAR)) {\n        if (!ZEND_DEBUG || op_type == IS_VAR) {\n            return _get_zval_ptr_var(node.var EXECUTE_DATA_CC);\n        } else {\n            ZEND_ASSERT(op_type == IS_TMP_VAR);\n            return _get_zval_ptr_tmp(node.var EXECUTE_DATA_CC);\n        }\n    } else {\n        if (op_type == IS_CONST) {\n            return RT_CONSTANT(opline + 1, node);\n        } else if (op_type == IS_CV) {\n            return _get_zval_ptr_cv_BP_VAR_R(node.var EXECUTE_DATA_CC);\n        } else {\n            return NULL;\n        }\n    }\n}\n\nstatic zend_always_inline ZEND_ATTRIBUTE_UNUSED zval *_get_zval_ptr_deref(int op_type,\n                                                                          znode_op node,\n                                                                          int type EXECUTE_DATA_DC OPLINE_DC) {\n    if (op_type & (IS_TMP_VAR | IS_VAR)) {\n        if (op_type == IS_TMP_VAR) {\n            return _get_zval_ptr_tmp(node.var EXECUTE_DATA_CC);\n        } else {\n            ZEND_ASSERT(op_type == IS_VAR);\n            return _get_zval_ptr_var_deref(node.var EXECUTE_DATA_CC);\n        }\n    } else {\n        if (op_type == IS_CONST) {\n            return RT_CONSTANT(opline, node);\n        } else if (op_type == IS_CV) {\n            return _get_zval_ptr_cv_deref(node.var, type EXECUTE_DATA_CC);\n        } else {\n            return NULL;\n        }\n    }\n}\n\nstatic zend_always_inline ZEND_ATTRIBUTE_UNUSED zval *_get_op_data_zval_ptr_deref_r(\n    int op_type, znode_op node EXECUTE_DATA_DC OPLINE_DC) {\n    if (op_type & (IS_TMP_VAR | IS_VAR)) {\n        if (op_type == IS_TMP_VAR) {\n            return _get_zval_ptr_tmp(node.var EXECUTE_DATA_CC);\n        } else {\n            ZEND_ASSERT(op_type == IS_VAR);\n            return _get_zval_ptr_var_deref(node.var EXECUTE_DATA_CC);\n        }\n    } else {\n        if (op_type == IS_CONST) {\n            return RT_CONSTANT(opline + 1, node);\n        } else if (op_type == IS_CV) {\n            return _get_zval_ptr_cv_deref_BP_VAR_R(node.var EXECUTE_DATA_CC);\n        } else {\n            return NULL;\n        }\n    }\n}\n\nstatic zend_always_inline zval *_get_zval_ptr_undef(int op_type, znode_op node, int type EXECUTE_DATA_DC OPLINE_DC) {\n    if (op_type & (IS_TMP_VAR | IS_VAR)) {\n        if (!ZEND_DEBUG || op_type == IS_VAR) {\n            return _get_zval_ptr_var(node.var EXECUTE_DATA_CC);\n        } else {\n            ZEND_ASSERT(op_type == IS_TMP_VAR);\n            return _get_zval_ptr_tmp(node.var EXECUTE_DATA_CC);\n        }\n    } else {\n        if (op_type == IS_CONST) {\n            return RT_CONSTANT(opline, node);\n        } else if (op_type == IS_CV) {\n            return EX_VAR(node.var);\n        } else {\n            return NULL;\n        }\n    }\n}\n\nstatic zend_always_inline zval *_get_zval_ptr_ptr_var(uint32_t var EXECUTE_DATA_DC) {\n    zval *ret = EX_VAR(var);\n\n    if (EXPECTED(Z_TYPE_P(ret) == IS_INDIRECT)) {\n        ret = Z_INDIRECT_P(ret);\n    }\n    return ret;\n}\n\nstatic inline zval *_get_zval_ptr_ptr(int op_type, znode_op node, int type EXECUTE_DATA_DC) {\n    if (op_type == IS_CV) {\n        return _get_zval_ptr_cv(node.var, type EXECUTE_DATA_CC);\n    } else /* if (op_type == IS_VAR) */ {\n        ZEND_ASSERT(op_type == IS_VAR);\n        return _get_zval_ptr_ptr_var(node.var EXECUTE_DATA_CC);\n    }\n}\n\nstatic inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr(int op_type,\n                                                            znode_op op,\n                                                            int type EXECUTE_DATA_DC OPLINE_DC) {\n    if (op_type == IS_UNUSED) {\n        return &EX(This);\n    }\n    return get_zval_ptr(op_type, op, type);\n}\n\nstatic inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr_undef(int op_type,\n                                                                  znode_op op,\n                                                                  int type EXECUTE_DATA_DC OPLINE_DC) {\n    if (op_type == IS_UNUSED) {\n        return &EX(This);\n    }\n    return get_zval_ptr_undef(op_type, op, type);\n}\n\nstatic inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr_ptr(int op_type, znode_op node, int type EXECUTE_DATA_DC) {\n    if (op_type == IS_UNUSED) {\n        return &EX(This);\n    }\n    return get_zval_ptr_ptr(op_type, node, type);\n}\n\nstatic zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_index(const zend_string *offset) {\n    zend_error(E_WARNING, \"Undefined array key \\\"%s\\\"\", ZSTR_VAL(offset));\n}\n\nstatic zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_offset(zend_long lval) {\n    zend_error(E_WARNING, \"Undefined array key \" ZEND_LONG_FMT, lval);\n}\n\nstatic zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_illegal_array_offset_access(const zval *offset) {\n    zend_illegal_container_offset(ZSTR_KNOWN(ZEND_STR_ARRAY), offset, BP_VAR_RW);\n}\n\nstatic zend_never_inline uint8_t slow_index_convert(HashTable *ht, const zval *dim, zend_value *value EXECUTE_DATA_DC) {\n    switch (Z_TYPE_P(dim)) {\n    case IS_UNDEF: {\n        /* The array may be destroyed while throwing the notice.\n         * Temporarily increase the refcount to detect this situation. */\n        if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {\n            GC_ADDREF(ht);\n        }\n        ZVAL_UNDEFINED_OP2();\n        if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {\n            zend_array_destroy(ht);\n            return IS_NULL;\n        }\n        if (EG(exception)) {\n            return IS_NULL;\n        }\n        ZEND_FALLTHROUGH;\n    }\n    case IS_NULL:\n        value->str = ZSTR_EMPTY_ALLOC();\n        return IS_STRING;\n    case IS_DOUBLE:\n        value->lval = zend_dval_to_lval(Z_DVAL_P(dim));\n        if (!zend_is_long_compatible(Z_DVAL_P(dim), value->lval)) {\n            /* The array may be destroyed while throwing the notice.\n             * Temporarily increase the refcount to detect this situation. */\n            if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {\n                GC_ADDREF(ht);\n            }\n            zend_incompatible_double_to_long_error(Z_DVAL_P(dim));\n            if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {\n                zend_array_destroy(ht);\n                return IS_NULL;\n            }\n            if (EG(exception)) {\n                return IS_NULL;\n            }\n        }\n        return IS_LONG;\n    case IS_RESOURCE:\n        /* The array may be destroyed while throwing the notice.\n         * Temporarily increase the refcount to detect this situation. */\n        if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {\n            GC_ADDREF(ht);\n        }\n        zend_use_resource_as_offset(dim);\n        if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {\n            zend_array_destroy(ht);\n            return IS_NULL;\n        }\n        if (EG(exception)) {\n            return IS_NULL;\n        }\n        value->lval = Z_RES_HANDLE_P(dim);\n        return IS_LONG;\n    case IS_FALSE:\n        value->lval = 0;\n        return IS_LONG;\n    case IS_TRUE:\n        value->lval = 1;\n        return IS_LONG;\n    default:\n        zend_illegal_array_offset_access(dim);\n        return IS_NULL;\n    }\n}\n\nstatic zend_never_inline uint8_t slow_index_convert_w(HashTable *ht,\n                                                      const zval *dim,\n                                                      zend_value *value EXECUTE_DATA_DC) {\n    switch (Z_TYPE_P(dim)) {\n    case IS_UNDEF: {\n        /* The array may be destroyed while throwing the notice.\n         * Temporarily increase the refcount to detect this situation. */\n        if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {\n            GC_ADDREF(ht);\n        }\n        ZVAL_UNDEFINED_OP2();\n        if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {\n            if (!GC_REFCOUNT(ht)) {\n                zend_array_destroy(ht);\n            }\n            return IS_NULL;\n        }\n        if (EG(exception)) {\n            return IS_NULL;\n        }\n        ZEND_FALLTHROUGH;\n    }\n    case IS_NULL:\n        value->str = ZSTR_EMPTY_ALLOC();\n        return IS_STRING;\n    case IS_DOUBLE:\n        value->lval = zend_dval_to_lval(Z_DVAL_P(dim));\n        if (!zend_is_long_compatible(Z_DVAL_P(dim), value->lval)) {\n            /* The array may be destroyed while throwing the notice.\n             * Temporarily increase the refcount to detect this situation. */\n            if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {\n                GC_ADDREF(ht);\n            }\n            zend_incompatible_double_to_long_error(Z_DVAL_P(dim));\n            if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {\n                if (!GC_REFCOUNT(ht)) {\n                    zend_array_destroy(ht);\n                }\n                return IS_NULL;\n            }\n            if (EG(exception)) {\n                return IS_NULL;\n            }\n        }\n        return IS_LONG;\n    case IS_RESOURCE:\n        /* The array may be destroyed while throwing the notice.\n         * Temporarily increase the refcount to detect this situation. */\n        if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {\n            GC_ADDREF(ht);\n        }\n        zend_use_resource_as_offset(dim);\n        if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {\n            if (!GC_REFCOUNT(ht)) {\n                zend_array_destroy(ht);\n            }\n            return IS_NULL;\n        }\n        if (EG(exception)) {\n            return IS_NULL;\n        }\n        value->lval = Z_RES_HANDLE_P(dim);\n        return IS_LONG;\n    case IS_FALSE:\n        value->lval = 0;\n        return IS_LONG;\n    case IS_TRUE:\n        value->lval = 1;\n        return IS_LONG;\n    default:\n        zend_illegal_array_offset_access(dim);\n        return IS_NULL;\n    }\n}\n\nstatic zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht,\n                                                                   const zval *dim,\n                                                                   int dim_type,\n                                                                   int type EXECUTE_DATA_DC) {\n    zval *retval = NULL;\n    zend_string *offset_key;\n    zend_ulong hval;\n\ntry_again:\n    if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) {\n        hval = Z_LVAL_P(dim);\n    num_index:\n        if (type != BP_VAR_W) {\n            ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef);\n            return retval;\n        num_undef:\n            switch (type) {\n            case BP_VAR_R:\n                zend_undefined_offset(hval);\n                ZEND_FALLTHROUGH;\n            case BP_VAR_UNSET:\n            case BP_VAR_IS:\n                retval = &EG(uninitialized_zval);\n                break;\n            case BP_VAR_RW:\n                retval = zend_undefined_offset_write(ht, hval);\n                break;\n            }\n        } else {\n            ZEND_HASH_INDEX_LOOKUP(ht, hval, retval);\n        }\n    } else if (EXPECTED(Z_TYPE_P(dim) == IS_STRING)) {\n        offset_key = Z_STR_P(dim);\n        if (ZEND_CONST_COND(dim_type != IS_CONST, 1)) {\n            if (ZEND_HANDLE_NUMERIC(offset_key, hval)) {\n                goto num_index;\n            }\n        }\n    str_index:\n        if (type != BP_VAR_W) {\n            retval = zend_hash_find_ex(ht, offset_key, ZEND_CONST_COND(dim_type == IS_CONST, 0));\n            if (!retval) {\n                switch (type) {\n                case BP_VAR_R:\n                    zend_undefined_index(offset_key);\n                    ZEND_FALLTHROUGH;\n                case BP_VAR_UNSET:\n                case BP_VAR_IS:\n                    retval = &EG(uninitialized_zval);\n                    break;\n                case BP_VAR_RW:\n                    retval = zend_undefined_index_write(ht, offset_key);\n                    break;\n                }\n            }\n        } else {\n            retval = zend_hash_lookup(ht, offset_key);\n        }\n    } else if (EXPECTED(Z_TYPE_P(dim) == IS_REFERENCE)) {\n        dim = Z_REFVAL_P(dim);\n        goto try_again;\n    } else {\n        zend_value val;\n        uint8_t t;\n\n        if (type != BP_VAR_W && type != BP_VAR_RW) {\n            t = slow_index_convert(ht, dim, &val EXECUTE_DATA_CC);\n        } else {\n            t = slow_index_convert_w(ht, dim, &val EXECUTE_DATA_CC);\n        }\n        if (t == IS_STRING) {\n            offset_key = val.str;\n            goto str_index;\n        } else if (t == IS_LONG) {\n            hval = val.lval;\n            goto num_index;\n        } else {\n            retval = (type == BP_VAR_W || type == BP_VAR_RW) ? NULL : &EG(uninitialized_zval);\n        }\n    }\n    return retval;\n}\n\nstatic zend_never_inline zval *ZEND_FASTCALL\nzend_fetch_dimension_address_inner_RW_CONST(HashTable *ht, const zval *dim EXECUTE_DATA_DC) {\n    return zend_fetch_dimension_address_inner(ht, dim, IS_CONST, BP_VAR_RW EXECUTE_DATA_CC);\n}\n\nstatic zend_never_inline zval *ZEND_FASTCALL zend_fetch_dimension_address_inner_RW(HashTable *ht,\n                                                                                   const zval *dim EXECUTE_DATA_DC) {\n    return zend_fetch_dimension_address_inner(ht, dim, IS_TMP_VAR, BP_VAR_RW EXECUTE_DATA_CC);\n}\n\nstatic zend_never_inline zval *ZEND_FASTCALL zend_fetch_dimension_address_inner_W(HashTable *ht,\n                                                                                  const zval *dim EXECUTE_DATA_DC) {\n    return zend_fetch_dimension_address_inner(ht, dim, IS_TMP_VAR, BP_VAR_W EXECUTE_DATA_CC);\n}\n\nstatic zend_never_inline zval *ZEND_FASTCALL\nzend_fetch_dimension_address_inner_W_CONST(HashTable *ht, const zval *dim EXECUTE_DATA_DC) {\n    return zend_fetch_dimension_address_inner(ht, dim, IS_CONST, BP_VAR_W EXECUTE_DATA_CC);\n}\n\nstatic zend_always_inline int zend_binary_op(zval *ret, zval *op1, zval *op2 OPLINE_DC) {\n    static const binary_op_type zend_binary_ops[] = {add_function,\n                                                     sub_function,\n                                                     mul_function,\n                                                     div_function,\n                                                     mod_function,\n                                                     shift_left_function,\n                                                     shift_right_function,\n                                                     concat_function,\n                                                     bitwise_or_function,\n                                                     bitwise_and_function,\n                                                     bitwise_xor_function,\n                                                     pow_function};\n    /* size_t cast makes GCC to better optimize 64-bit PIC code */\n    size_t opcode = (size_t) opline->extended_value;\n\n    return zend_binary_ops[opcode - ZEND_ADD](ret, op1, op2);\n}\n\nstatic zend_never_inline void zend_binary_assign_op_typed_ref(zend_reference *ref,\n                                                              zval *value OPLINE_DC EXECUTE_DATA_DC) {\n    zval z_copy;\n\n    /* Make sure that in-place concatenation is used if the LHS is a string. */\n    if (opline->extended_value == ZEND_CONCAT && Z_TYPE(ref->val) == IS_STRING) {\n        concat_function(&ref->val, &ref->val, value);\n        ZEND_ASSERT(Z_TYPE(ref->val) == IS_STRING && \"Concat should return string\");\n        return;\n    }\n\n    zend_binary_op(&z_copy, &ref->val, value OPLINE_CC);\n    if (EXPECTED(zend_verify_ref_assignable_zval(ref, &z_copy, EX_USES_STRICT_TYPES()))) {\n        zval_ptr_dtor(&ref->val);\n        ZVAL_COPY_VALUE(&ref->val, &z_copy);\n    } else {\n        zval_ptr_dtor(&z_copy);\n    }\n}\n"
  },
  {
    "path": "thirdparty/php82/pdo_odbc/odbc_driver.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_ODBC_HOOK\n#include \"php_swoole_odbc.h\"\n\n#if PHP_VERSION_ID >= 80200 && PHP_VERSION_ID < 80300\n\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"pdo/php_pdo.h\"\n#include \"pdo/php_pdo_driver.h\"\n#include \"zend_exceptions.h\"\n#include <php_odbc_utils.h>\n\nstatic void pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    pdo_odbc_errinfo *einfo = &H->einfo;\n    pdo_odbc_stmt *S = NULL;\n    zend_string *message = NULL;\n\n    if (stmt) {\n        S = (pdo_odbc_stmt *) stmt->driver_data;\n        einfo = &S->einfo;\n    }\n\n    message = strpprintf(0,\n                         \"%s (%s[%ld] at %s:%d)\",\n                         einfo->last_err_msg,\n                         einfo->what,\n                         (long) einfo->last_error,\n                         einfo->file,\n                         einfo->line);\n\n    add_next_index_long(info, einfo->last_error);\n    add_next_index_str(info, message);\n    add_next_index_string(info, einfo->last_state);\n}\n\nvoid pdo_odbc_error(\n    pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line) /* {{{ */\n{\n    SQLRETURN rc;\n    SQLSMALLINT errmsgsize = 0;\n    SQLHANDLE eh;\n    SQLSMALLINT htype, recno = 1;\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    pdo_odbc_errinfo *einfo = &H->einfo;\n    pdo_odbc_stmt *S = NULL;\n    pdo_error_type *pdo_err = &dbh->error_code;\n\n    if (stmt) {\n        S = (pdo_odbc_stmt *) stmt->driver_data;\n\n        einfo = &S->einfo;\n        pdo_err = &stmt->error_code;\n    }\n\n    if (statement == SQL_NULL_HSTMT && S) {\n        statement = S->stmt;\n    }\n\n    if (statement) {\n        htype = SQL_HANDLE_STMT;\n        eh = statement;\n    } else if (H->dbc) {\n        htype = SQL_HANDLE_DBC;\n        eh = H->dbc;\n    } else {\n        htype = SQL_HANDLE_ENV;\n        eh = H->env;\n    }\n\n    rc = SQLGetDiagRec(htype,\n                       eh,\n                       recno++,\n                       (SQLCHAR *) einfo->last_state,\n                       &einfo->last_error,\n                       (SQLCHAR *) einfo->last_err_msg,\n                       sizeof(einfo->last_err_msg) - 1,\n                       &errmsgsize);\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        errmsgsize = 0;\n    }\n\n    einfo->last_err_msg[errmsgsize] = '\\0';\n    einfo->file = file;\n    einfo->line = line;\n    einfo->what = what;\n\n    strcpy(*pdo_err, einfo->last_state);\n    /* printf(\"@@ SQLSTATE[%s] %s\\n\", *pdo_err, einfo->last_err_msg); */\n    if (!dbh->methods) {\n        zend_throw_exception_ex(php_pdo_get_exception(),\n                                einfo->last_error,\n                                \"SQLSTATE[%s] %s: %d %s\",\n                                *pdo_err,\n                                what,\n                                einfo->last_error,\n                                einfo->last_err_msg);\n    }\n\n    /* just like a cursor, once you start pulling, you need to keep\n     * going until the end; SQL Server (at least) will mess with the\n     * actual cursor state if you don't finish retrieving all the\n     * diagnostic records (which can be generated by PRINT statements\n     * in the query, for instance). */\n    while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {\n        SQLCHAR discard_state[6];\n        SQLCHAR discard_buf[1024];\n        SQLINTEGER code;\n        rc = SQLGetDiagRec(htype, eh, recno++, discard_state, &code, discard_buf, sizeof(discard_buf) - 1, &errmsgsize);\n    }\n}\n/* }}} */\n\nstatic void odbc_handle_closer(pdo_dbh_t *dbh) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n\n    if (H->dbc != SQL_NULL_HANDLE) {\n        SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);\n        SQLDisconnect(H->dbc);\n        SQLFreeHandle(SQL_HANDLE_DBC, H->dbc);\n        H->dbc = NULL;\n    }\n    SQLFreeHandle(SQL_HANDLE_ENV, H->env);\n    H->env = NULL;\n    pefree(H, dbh->is_persistent);\n    dbh->driver_data = NULL;\n}\n\nstatic bool odbc_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) {\n    RETCODE rc;\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    pdo_odbc_stmt *S = ecalloc(1, sizeof(*S));\n    enum pdo_cursor_type cursor_type = PDO_CURSOR_FWDONLY;\n    int ret;\n    zend_string *nsql = NULL;\n\n    S->H = H;\n    S->assume_utf8 = H->assume_utf8;\n\n    /* before we prepare, we need to peek at the query; if it uses named parameters,\n     * we want PDO to rewrite them for us */\n    stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL;\n    ret = pdo_parse_params(stmt, sql, &nsql);\n\n    if (ret == 1) {\n        /* query was re-written */\n        sql = nsql;\n    } else if (ret == -1) {\n        /* couldn't grok it */\n        strcpy(dbh->error_code, stmt->error_code);\n        efree(S);\n        return false;\n    }\n\n    rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &S->stmt);\n\n    if (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) {\n        efree(S);\n        if (nsql) {\n            zend_string_release(nsql);\n        }\n        pdo_odbc_drv_error(\"SQLAllocStmt\");\n        return false;\n    }\n\n    stmt->driver_data = S;\n\n    cursor_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY);\n    if (cursor_type != PDO_CURSOR_FWDONLY) {\n        rc = SQLSetStmtAttr(S->stmt, SQL_ATTR_CURSOR_SCROLLABLE, (void *) SQL_SCROLLABLE, 0);\n        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n            pdo_odbc_stmt_error(\"SQLSetStmtAttr: SQL_ATTR_CURSOR_SCROLLABLE\");\n            SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);\n            if (nsql) {\n                zend_string_release(nsql);\n            }\n            return false;\n        }\n    }\n\n    rc = SQLPrepare(S->stmt, (SQLCHAR *) ZSTR_VAL(sql), SQL_NTS);\n    if (nsql) {\n        zend_string_release(nsql);\n    }\n\n    stmt->methods = &odbc_stmt_methods;\n\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_stmt_error(\"SQLPrepare\");\n        if (rc != SQL_SUCCESS_WITH_INFO) {\n            /* clone error information into the db handle */\n            strcpy(H->einfo.last_err_msg, S->einfo.last_err_msg);\n            H->einfo.file = S->einfo.file;\n            H->einfo.line = S->einfo.line;\n            H->einfo.what = S->einfo.what;\n            strcpy(dbh->error_code, stmt->error_code);\n        }\n    }\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        return false;\n    }\n    return true;\n}\n\nstatic zend_long odbc_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    RETCODE rc;\n    SQLLEN row_count = -1;\n    PDO_ODBC_HSTMT stmt;\n\n    rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt);\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_drv_error(\"SQLAllocHandle: STMT\");\n        return -1;\n    }\n\n    rc = SQLExecDirect(stmt, (SQLCHAR *) ZSTR_VAL(sql), ZSTR_LEN(sql));\n\n    if (rc == SQL_NO_DATA) {\n        /* If SQLExecDirect executes a searched update or delete statement that\n         * does not affect any rows at the data source, the call to\n         * SQLExecDirect returns SQL_NO_DATA. */\n        row_count = 0;\n        goto out;\n    }\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_doer_error(\"SQLExecDirect\");\n        goto out;\n    }\n\n    rc = SQLRowCount(stmt, &row_count);\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_doer_error(\"SQLRowCount\");\n        goto out;\n    }\n    if (row_count == -1) {\n        row_count = 0;\n    }\nout:\n    SQLFreeHandle(SQL_HANDLE_STMT, stmt);\n    return row_count;\n}\n\n/* TODO: Do ODBC quoter\nstatic int odbc_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t\n*quotedlen, enum pdo_param_type param_type )\n{\n        // pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;\n        // TODO: figure it out\n        return 0;\n}\n*/\n\nstatic bool odbc_handle_begin(pdo_dbh_t *dbh) {\n    if (dbh->auto_commit) {\n        /* we need to disable auto-commit now, to be able to initiate a transaction */\n        RETCODE rc;\n        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n\n        rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER);\n        if (rc != SQL_SUCCESS) {\n            pdo_odbc_drv_error(\"SQLSetConnectAttr AUTOCOMMIT = OFF\");\n            return false;\n        }\n    }\n    return true;\n}\n\nstatic bool odbc_handle_commit(pdo_dbh_t *dbh) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    RETCODE rc;\n\n    rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT);\n\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_drv_error(\"SQLEndTran: Commit\");\n\n        if (rc != SQL_SUCCESS_WITH_INFO) {\n            return false;\n        }\n    }\n\n    if (dbh->auto_commit) {\n        /* turn auto-commit back on again */\n        rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);\n        if (rc != SQL_SUCCESS) {\n            pdo_odbc_drv_error(\"SQLSetConnectAttr AUTOCOMMIT = ON\");\n            return false;\n        }\n    }\n    return true;\n}\n\nstatic bool odbc_handle_rollback(pdo_dbh_t *dbh) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    RETCODE rc;\n\n    rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);\n\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_drv_error(\"SQLEndTran: Rollback\");\n\n        if (rc != SQL_SUCCESS_WITH_INFO) {\n            return false;\n        }\n    }\n    if (dbh->auto_commit && H->dbc) {\n        /* turn auto-commit back on again */\n        rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);\n        if (rc != SQL_SUCCESS) {\n            pdo_odbc_drv_error(\"SQLSetConnectAttr AUTOCOMMIT = ON\");\n            return false;\n        }\n    }\n\n    return true;\n}\n\nstatic bool odbc_handle_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    bool bval;\n\n    switch (attr) {\n    case PDO_ODBC_ATTR_ASSUME_UTF8:\n        if (!pdo_get_bool_param(&bval, val)) {\n            return false;\n        }\n        H->assume_utf8 = bval;\n        return true;\n    case PDO_ATTR_AUTOCOMMIT:\n        if (!pdo_get_bool_param(&bval, val)) {\n            return false;\n        }\n        if (dbh->in_txn) {\n            pdo_raise_impl_error(\n                dbh, NULL, \"HY000\", \"Cannot change autocommit mode while a transaction is already open\");\n            return false;\n        }\n        if (dbh->auto_commit ^ bval) {\n            dbh->auto_commit = bval;\n            RETCODE rc =\n                SQLSetConnectAttr(H->dbc,\n                                  SQL_ATTR_AUTOCOMMIT,\n                                  dbh->auto_commit ? (SQLPOINTER) SQL_AUTOCOMMIT_ON : (SQLPOINTER) SQL_AUTOCOMMIT_OFF,\n                                  SQL_IS_INTEGER);\n            if (rc != SQL_SUCCESS) {\n                pdo_odbc_drv_error(dbh->auto_commit ? \"SQLSetConnectAttr AUTOCOMMIT = ON\"\n                                                    : \"SQLSetConnectAttr AUTOCOMMIT = OFF\");\n                return false;\n            }\n        }\n        return true;\n    default:\n        strcpy(H->einfo.last_err_msg, \"Unknown Attribute\");\n        H->einfo.what = \"setAttribute\";\n        strcpy(H->einfo.last_state, \"IM001\");\n        return false;\n    }\n}\n\nstatic int pdo_odbc_get_info_string(pdo_dbh_t *dbh, SQLUSMALLINT type, zval *val) {\n    RETCODE rc;\n    SQLSMALLINT out_len;\n    char buf[256];\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    rc = SQLGetInfo(H->dbc, type, (SQLPOINTER) buf, sizeof(buf), &out_len);\n    /* returning -1 is treated as an error, not as unsupported */\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        return -1;\n    }\n    ZVAL_STRINGL(val, buf, out_len);\n    return 1;\n}\n\nstatic int odbc_handle_get_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    switch (attr) {\n    case PDO_ATTR_CLIENT_VERSION:\n        ZVAL_STRING(val, \"ODBC-\" PDO_ODBC_TYPE);\n        return 1;\n\n    case PDO_ATTR_SERVER_VERSION:\n        return pdo_odbc_get_info_string(dbh, SQL_DBMS_VER, val);\n    case PDO_ATTR_SERVER_INFO:\n        return pdo_odbc_get_info_string(dbh, SQL_DBMS_NAME, val);\n    case PDO_ATTR_PREFETCH:\n    case PDO_ATTR_TIMEOUT:\n    case PDO_ATTR_CONNECTION_STATUS:\n        break;\n    case PDO_ODBC_ATTR_ASSUME_UTF8:\n        ZVAL_BOOL(val, H->assume_utf8 ? 1 : 0);\n        return 1;\n    case PDO_ATTR_AUTOCOMMIT:\n        ZVAL_BOOL(val, dbh->auto_commit);\n        return 1;\n    }\n    return 0;\n}\n\nstatic zend_result odbc_handle_check_liveness(pdo_dbh_t *dbh) {\n    RETCODE ret;\n    UCHAR d_name[32];\n    SQLSMALLINT len;\n    SQLUINTEGER dead = SQL_CD_FALSE;\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n\n    ret = SQLGetConnectAttr(H->dbc, SQL_ATTR_CONNECTION_DEAD, &dead, 0, NULL);\n    if (ret == SQL_SUCCESS && dead == SQL_CD_TRUE) {\n        /* Bail early here, since we know it's gone */\n        return FAILURE;\n    }\n    /*\n     * If the driver doesn't support SQL_ATTR_CONNECTION_DEAD, or if\n     * it returns false (which could be a false positive), fall back\n     * to using SQL_DATA_SOURCE_READ_ONLY, which isn't semantically\n     * correct, but works with many drivers.\n     */\n    ret = SQLGetInfo(H->dbc, SQL_DATA_SOURCE_READ_ONLY, d_name, sizeof(d_name), &len);\n\n    if (ret != SQL_SUCCESS || len == 0) {\n        return FAILURE;\n    }\n    return SUCCESS;\n}\n\nstatic const struct pdo_dbh_methods odbc_methods = {\n    odbc_handle_closer,\n    odbc_handle_preparer,\n    odbc_handle_doer,\n    NULL, /* quoter */\n    odbc_handle_begin,\n    odbc_handle_commit,\n    odbc_handle_rollback,\n    odbc_handle_set_attr,\n    NULL, /* last id */\n    pdo_odbc_fetch_error_func,\n    odbc_handle_get_attr,       /* get attr */\n    odbc_handle_check_liveness, /* check_liveness */\n    NULL,                       /* get_driver_methods */\n    NULL,                       /* request_shutdown */\n    NULL,                       /* in transaction, use PDO's internal tracking mechanism */\n    NULL                        /* get_gc */\n};\n\nstatic int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */\n{\n    pdo_odbc_db_handle *H;\n    RETCODE rc;\n    int use_direct = 0;\n    zend_ulong cursor_lib;\n\n    H = pecalloc(1, sizeof(*H), dbh->is_persistent);\n\n    dbh->driver_data = H;\n\n    rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H->env);\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_drv_error(\"SQLAllocHandle: ENV\");\n        goto fail;\n    }\n\n    rc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_drv_error(\"SQLSetEnvAttr: ODBC3\");\n        goto fail;\n    }\n\n#ifdef SQL_ATTR_CONNECTION_POOLING\n    if (pdo_odbc_pool_on != SQL_CP_OFF) {\n        rc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void *) pdo_odbc_pool_mode, 0);\n        if (rc != SQL_SUCCESS) {\n            pdo_odbc_drv_error(\"SQLSetEnvAttr: SQL_ATTR_CP_MATCH\");\n            goto fail;\n        }\n    }\n#endif\n\n    rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc);\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_drv_error(\"SQLAllocHandle: DBC\");\n        goto fail;\n    }\n\n    rc = SQLSetConnectAttr(H->dbc,\n                           SQL_ATTR_AUTOCOMMIT,\n                           (SQLPOINTER) (intptr_t) (dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF),\n                           SQL_IS_INTEGER);\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_drv_error(\"SQLSetConnectAttr AUTOCOMMIT\");\n        goto fail;\n    }\n\n    /* set up the cursor library, if needed, or if configured explicitly */\n    cursor_lib = pdo_attr_lval(driver_options, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY, SQL_CUR_USE_IF_NEEDED);\n    rc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void *) cursor_lib, SQL_IS_INTEGER);\n    if (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) {\n        pdo_odbc_drv_error(\"SQLSetConnectAttr SQL_ODBC_CURSORS\");\n        goto fail;\n    }\n\n    /* a connection string may have = but not ; - i.e. \"DSN=PHP\" */\n    if (strchr(dbh->data_source, '=')) {\n        SQLCHAR dsnbuf[1024];\n        SQLSMALLINT dsnbuflen;\n\n        use_direct = 1;\n\n        /* Force UID and PWD to be set in the DSN */\n        bool is_uid_set =\n            dbh->username && *dbh->username && !strstr(dbh->data_source, \"uid=\") && !strstr(dbh->data_source, \"UID=\");\n        bool is_pwd_set =\n            dbh->password && *dbh->password && !strstr(dbh->data_source, \"pwd=\") && !strstr(dbh->data_source, \"PWD=\");\n        if (is_uid_set && is_pwd_set) {\n            char *uid = NULL, *pwd = NULL;\n            bool should_quote_uid =\n                !php_odbc_connstr_is_quoted(dbh->username) && php_odbc_connstr_should_quote(dbh->username);\n            bool should_quote_pwd =\n                !php_odbc_connstr_is_quoted(dbh->password) && php_odbc_connstr_should_quote(dbh->password);\n            if (should_quote_uid) {\n                size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->username);\n                uid = emalloc(estimated_length);\n                php_odbc_connstr_quote(uid, dbh->username, estimated_length);\n            } else {\n                uid = dbh->username;\n            }\n            if (should_quote_pwd) {\n                size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->password);\n                pwd = emalloc(estimated_length);\n                php_odbc_connstr_quote(pwd, dbh->password, estimated_length);\n            } else {\n                pwd = dbh->password;\n            }\n            size_t new_dsn_size = strlen(dbh->data_source) + strlen(uid) + strlen(pwd) + strlen(\";UID=;PWD=\") + 1;\n            char *dsn = pemalloc(new_dsn_size, dbh->is_persistent);\n            snprintf(dsn, new_dsn_size, \"%s;UID=%s;PWD=%s\", dbh->data_source, uid, pwd);\n            pefree((char *) dbh->data_source, dbh->is_persistent);\n            dbh->data_source = dsn;\n            if (uid && should_quote_uid) {\n                efree(uid);\n            }\n            if (pwd && should_quote_pwd) {\n                efree(pwd);\n            }\n        }\n\n        rc = SQLDriverConnect(H->dbc,\n                              NULL,\n                              (SQLCHAR *) dbh->data_source,\n                              strlen(dbh->data_source),\n                              dsnbuf,\n                              sizeof(dsnbuf) - 1,\n                              &dsnbuflen,\n                              SQL_DRIVER_NOPROMPT);\n    }\n    if (!use_direct) {\n        rc = SQLConnect(H->dbc,\n                        (SQLCHAR *) dbh->data_source,\n                        SQL_NTS,\n                        (SQLCHAR *) dbh->username,\n                        SQL_NTS,\n                        (SQLCHAR *) dbh->password,\n                        SQL_NTS);\n    }\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_drv_error(use_direct ? \"SQLDriverConnect\" : \"SQLConnect\");\n        goto fail;\n    }\n\n    /* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */\n\n    dbh->methods = &odbc_methods;\n    dbh->alloc_own_columns = 1;\n\n    return 1;\n\nfail:\n    dbh->methods = &odbc_methods;\n    return 0;\n}\n/* }}} */\n\nconst pdo_driver_t swoole_pdo_odbc_driver = {PDO_DRIVER_HEADER(odbc), pdo_odbc_handle_factory};\n#endif\n"
  },
  {
    "path": "thirdparty/php82/pdo_odbc/odbc_stmt.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_ODBC_HOOK\n#include \"php_swoole_odbc.h\"\n\n#if PHP_VERSION_ID >= 80200 && PHP_VERSION_ID < 80300\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"pdo/php_pdo.h\"\n#include \"pdo/php_pdo_driver.h\"\n\nenum pdo_odbc_conv_result { PDO_ODBC_CONV_NOT_REQUIRED, PDO_ODBC_CONV_OK, PDO_ODBC_CONV_FAIL };\n\nstatic int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SQLSMALLINT sqltype) {\n    if (!S->assume_utf8) return 0;\n    switch (sqltype) {\n#ifdef SQL_WCHAR\n    case SQL_WCHAR:\n        return 1;\n#endif\n#ifdef SQL_WLONGVARCHAR\n    case SQL_WLONGVARCHAR:\n        return 1;\n#endif\n#ifdef SQL_WVARCHAR\n    case SQL_WVARCHAR:\n        return 1;\n#endif\n    default:\n        return 0;\n    }\n}\n\nstatic int pdo_odbc_utf82ucs2(\n    pdo_stmt_t *stmt, int is_unicode, const char *buf, zend_ulong buflen, zend_ulong *outlen) {\n#ifdef PHP_WIN32\n    if (is_unicode && buflen) {\n        pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n        DWORD ret;\n\n        ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, NULL, 0);\n        if (ret == 0) {\n            /*printf(\"%s:%d %d [%d] %.*s\\n\", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/\n            return PDO_ODBC_CONV_FAIL;\n        }\n\n        ret *= sizeof(WCHAR);\n\n        if (S->convbufsize <= ret) {\n            S->convbufsize = ret + sizeof(WCHAR);\n            S->convbuf = erealloc(S->convbuf, S->convbufsize);\n        }\n\n        ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, (LPWSTR) S->convbuf, S->convbufsize / sizeof(WCHAR));\n        if (ret == 0) {\n            /*printf(\"%s:%d %d [%d] %.*s\\n\", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/\n            return PDO_ODBC_CONV_FAIL;\n        }\n\n        ret *= sizeof(WCHAR);\n        *outlen = ret;\n        return PDO_ODBC_CONV_OK;\n    }\n#endif\n    return PDO_ODBC_CONV_NOT_REQUIRED;\n}\n\nstatic int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, zval *result) {\n#ifdef PHP_WIN32\n    ZEND_ASSERT(Z_TYPE_P(result) == IS_STRING);\n    if (is_unicode && Z_STRLEN_P(result) != 0) {\n        pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n        DWORD ret;\n\n        ret = WideCharToMultiByte(\n            CP_UTF8, 0, (LPCWSTR) Z_STRVAL_P(result), Z_STRLEN_P(result) / sizeof(WCHAR), NULL, 0, NULL, NULL);\n        if (ret == 0) {\n            return PDO_ODBC_CONV_FAIL;\n        }\n\n        zend_string *str = zend_string_alloc(ret, 0);\n        ret = WideCharToMultiByte(CP_UTF8,\n                                  0,\n                                  (LPCWSTR) Z_STRVAL_P(result),\n                                  Z_STRLEN_P(result) / sizeof(WCHAR),\n                                  ZSTR_VAL(str),\n                                  ZSTR_LEN(str),\n                                  NULL,\n                                  NULL);\n        if (ret == 0) {\n            return PDO_ODBC_CONV_FAIL;\n        }\n\n        ZSTR_VAL(str)[ret] = '\\0';\n        zval_ptr_dtor_str(result);\n        ZVAL_STR(result, str);\n        return PDO_ODBC_CONV_OK;\n    }\n#endif\n    return PDO_ODBC_CONV_NOT_REQUIRED;\n}\n\nstatic void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S) {\n    if (S->cols) {\n        int i;\n\n        for (i = 0; i < S->col_count; i++) {\n            if (S->cols[i].data) {\n                efree(S->cols[i].data);\n            }\n        }\n        efree(S->cols);\n        S->cols = NULL;\n        S->col_count = 0;\n    }\n}\n\nstatic int odbc_stmt_dtor(pdo_stmt_t *stmt) {\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    if (S->stmt != SQL_NULL_HANDLE) {\n        if (stmt->executed) {\n            SQLCloseCursor(S->stmt);\n        }\n        SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);\n        S->stmt = SQL_NULL_HANDLE;\n    }\n\n    free_cols(stmt, S);\n    if (S->convbuf) {\n        efree(S->convbuf);\n    }\n    efree(S);\n\n    return 1;\n}\n\nstatic int odbc_stmt_execute(pdo_stmt_t *stmt) {\n    RETCODE rc, rc1;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n    char *buf = NULL;\n    SQLLEN row_count = -1;\n\n    if (stmt->executed) {\n        SQLCloseCursor(S->stmt);\n    }\n\n    rc = SQLExecute(S->stmt);\n\n    while (rc == SQL_NEED_DATA) {\n        struct pdo_bound_param_data *param;\n\n        rc = SQLParamData(S->stmt, (SQLPOINTER *) &param);\n        if (rc == SQL_NEED_DATA) {\n            php_stream *stm;\n            int len;\n            pdo_odbc_param *P;\n            zval *parameter;\n\n            P = (pdo_odbc_param *) param->driver_data;\n            if (Z_ISREF(param->parameter)) {\n                parameter = Z_REFVAL(param->parameter);\n            } else {\n                parameter = &param->parameter;\n            }\n            if (Z_TYPE_P(parameter) != IS_RESOURCE) {\n                /* they passed in a string */\n                zend_ulong ulen;\n                convert_to_string(parameter);\n\n                switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), &ulen)) {\n                case PDO_ODBC_CONV_NOT_REQUIRED:\n                    rc1 = SQLPutData(S->stmt, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter));\n                    if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {\n                        rc = rc1;\n                    }\n                    break;\n                case PDO_ODBC_CONV_OK:\n                    rc1 = SQLPutData(S->stmt, S->convbuf, ulen);\n                    if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {\n                        rc = rc1;\n                    }\n                    break;\n                case PDO_ODBC_CONV_FAIL:\n                    pdo_odbc_stmt_error(\"error converting input string\");\n                    SQLCloseCursor(S->stmt);\n                    if (buf) {\n                        efree(buf);\n                    }\n                    return 0;\n                }\n                continue;\n            }\n\n            /* we assume that LOBs are binary and don't need charset\n             * conversion */\n\n            php_stream_from_zval_no_verify(stm, parameter);\n            if (!stm) {\n                /* shouldn't happen either */\n                pdo_odbc_stmt_error(\"input LOB is no longer a stream\");\n                SQLCloseCursor(S->stmt);\n                if (buf) {\n                    efree(buf);\n                }\n                return 0;\n            }\n\n            /* now suck data from the stream and stick it into the database */\n            if (buf == NULL) {\n                buf = emalloc(8192);\n            }\n\n            do {\n                len = php_stream_read(stm, buf, 8192);\n                if (len == 0) {\n                    break;\n                }\n                rc1 = SQLPutData(S->stmt, buf, len);\n                if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {\n                    rc = rc1;\n                }\n            } while (1);\n        }\n    }\n\n    if (buf) {\n        efree(buf);\n    }\n\n    switch (rc) {\n    case SQL_SUCCESS:\n        break;\n    case SQL_NO_DATA_FOUND:\n    case SQL_SUCCESS_WITH_INFO:\n        pdo_odbc_stmt_error(\"SQLExecute\");\n        break;\n\n    default:\n        pdo_odbc_stmt_error(\"SQLExecute\");\n        return 0;\n    }\n\n    SQLRowCount(S->stmt, &row_count);\n    stmt->row_count = row_count;\n\n    if (S->cols == NULL) {\n        /* do first-time-only definition of bind/mapping stuff */\n        SQLSMALLINT colcount;\n\n        /* how many columns do we have ? */\n        SQLNumResultCols(S->stmt, &colcount);\n\n        stmt->column_count = S->col_count = (int) colcount;\n        S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));\n        S->going_long = 0;\n    }\n\n    return 1;\n}\n\nstatic int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, enum pdo_param_event event_type) {\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n    RETCODE rc;\n    SQLSMALLINT sqltype = 0, ctype = 0, scale = 0, nullable = 0;\n    SQLULEN precision = 0;\n    pdo_odbc_param *P;\n    zval *parameter;\n\n    /* we're only interested in parameters for prepared SQL right now */\n    if (param->is_param) {\n        switch (event_type) {\n        case PDO_PARAM_EVT_FETCH_PRE:\n        case PDO_PARAM_EVT_FETCH_POST:\n        case PDO_PARAM_EVT_NORMALIZE:\n            /* Do nothing */\n            break;\n\n        case PDO_PARAM_EVT_FREE:\n            P = param->driver_data;\n            if (P) {\n                efree(P);\n            }\n            break;\n\n        case PDO_PARAM_EVT_ALLOC: {\n            /* figure out what we're doing */\n            switch (PDO_PARAM_TYPE(param->param_type)) {\n            case PDO_PARAM_LOB:\n                break;\n\n            case PDO_PARAM_STMT:\n                return 0;\n\n            default:\n                break;\n            }\n\n            rc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno + 1, &sqltype, &precision, &scale, &nullable);\n            if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n                /* MS Access, for instance, doesn't support SQLDescribeParam,\n                 * so we need to guess */\n                switch (PDO_PARAM_TYPE(param->param_type)) {\n                case PDO_PARAM_INT:\n                    sqltype = SQL_INTEGER;\n                    break;\n                case PDO_PARAM_LOB:\n                    sqltype = SQL_LONGVARBINARY;\n                    break;\n                default:\n                    sqltype = SQL_LONGVARCHAR;\n                }\n                precision = 4000;\n                scale = 5;\n                nullable = 1;\n\n                if (param->max_value_len > 0) {\n                    precision = param->max_value_len;\n                }\n            }\n            if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) {\n                ctype = SQL_C_BINARY;\n            } else {\n                ctype = SQL_C_CHAR;\n            }\n\n            P = emalloc(sizeof(*P));\n            param->driver_data = P;\n\n            P->len = 0; /* is re-populated each EXEC_PRE */\n            P->outbuf = NULL;\n\n            P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype);\n            if (P->is_unicode) {\n                /* avoid driver auto-translation: we'll do it ourselves */\n                ctype = SQL_C_BINARY;\n            }\n\n            if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) {\n                P->paramtype = SQL_PARAM_INPUT_OUTPUT;\n            } else if (param->max_value_len <= 0) {\n                P->paramtype = SQL_PARAM_INPUT;\n            } else {\n                P->paramtype = SQL_PARAM_OUTPUT;\n            }\n\n            if (P->paramtype != SQL_PARAM_INPUT) {\n                if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) {\n                    /* need an explicit buffer to hold result */\n                    P->len = param->max_value_len > 0 ? param->max_value_len : precision;\n                    if (P->is_unicode) {\n                        P->len *= 2;\n                    }\n                    P->outbuf = emalloc(P->len + (P->is_unicode ? 2 : 1));\n                }\n            }\n\n            if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) {\n                pdo_odbc_stmt_error(\"Can't bind a lob for output\");\n                return 0;\n            }\n\n            rc = SQLBindParameter(S->stmt,\n                                  (SQLUSMALLINT) param->paramno + 1,\n                                  P->paramtype,\n                                  ctype,\n                                  sqltype,\n                                  precision,\n                                  scale,\n                                  P->paramtype == SQL_PARAM_INPUT ? (SQLPOINTER) param : P->outbuf,\n                                  P->len,\n                                  &P->len);\n\n            if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {\n                return 1;\n            }\n            pdo_odbc_stmt_error(\"SQLBindParameter\");\n            return 0;\n        }\n\n        case PDO_PARAM_EVT_EXEC_PRE:\n            P = param->driver_data;\n            if (!Z_ISREF(param->parameter)) {\n                parameter = &param->parameter;\n            } else {\n                parameter = Z_REFVAL(param->parameter);\n            }\n\n            if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {\n                if (Z_TYPE_P(parameter) == IS_RESOURCE) {\n                    php_stream *stm;\n                    php_stream_statbuf sb;\n\n                    php_stream_from_zval_no_verify(stm, parameter);\n\n                    if (!stm) {\n                        return 0;\n                    }\n\n                    if (0 == php_stream_stat(stm, &sb)) {\n                        if (P->outbuf) {\n                            int len, amount;\n                            char *ptr = P->outbuf;\n                            char *end = P->outbuf + P->len;\n\n                            P->len = 0;\n                            do {\n                                amount = end - ptr;\n                                if (amount == 0) {\n                                    break;\n                                }\n                                if (amount > 8192) amount = 8192;\n                                len = php_stream_read(stm, ptr, amount);\n                                if (len == 0) {\n                                    break;\n                                }\n                                ptr += len;\n                                P->len += len;\n                            } while (1);\n\n                        } else {\n                            P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size);\n                        }\n                    } else {\n                        if (P->outbuf) {\n                            P->len = 0;\n                        } else {\n                            P->len = SQL_LEN_DATA_AT_EXEC(0);\n                        }\n                    }\n                } else {\n                    convert_to_string(parameter);\n                    if (P->outbuf) {\n                        P->len = Z_STRLEN_P(parameter);\n                        memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len);\n                    } else {\n                        P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));\n                    }\n                }\n            } else if (Z_TYPE_P(parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) {\n                P->len = SQL_NULL_DATA;\n            } else {\n                convert_to_string(parameter);\n                if (P->outbuf) {\n                    zend_ulong ulen;\n                    switch (\n                        pdo_odbc_utf82ucs2(stmt, P->is_unicode, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), &ulen)) {\n                    case PDO_ODBC_CONV_FAIL:\n                    case PDO_ODBC_CONV_NOT_REQUIRED:\n                        P->len = Z_STRLEN_P(parameter);\n                        memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len);\n                        break;\n                    case PDO_ODBC_CONV_OK:\n                        P->len = ulen;\n                        memcpy(P->outbuf, S->convbuf, P->len);\n                        break;\n                    }\n                } else {\n                    P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));\n                }\n            }\n            return 1;\n\n        case PDO_PARAM_EVT_EXEC_POST:\n            P = param->driver_data;\n\n            if (P->outbuf) {\n                if (Z_ISREF(param->parameter)) {\n                    parameter = Z_REFVAL(param->parameter);\n                } else {\n                    parameter = &param->parameter;\n                }\n                zval_ptr_dtor(parameter);\n\n                if (P->len >= 0) {\n                    ZVAL_STRINGL(parameter, P->outbuf, P->len);\n                    switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, parameter)) {\n                    case PDO_ODBC_CONV_FAIL:\n                        /* something fishy, but allow it to come back as binary */\n                    case PDO_ODBC_CONV_NOT_REQUIRED:\n                        break;\n                    case PDO_ODBC_CONV_OK:\n                        break;\n                    }\n                } else {\n                    ZVAL_NULL(parameter);\n                }\n            }\n            return 1;\n        }\n    }\n    return 1;\n}\n\nstatic int odbc_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) {\n    RETCODE rc;\n    SQLSMALLINT odbcori;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    switch (ori) {\n    case PDO_FETCH_ORI_NEXT:\n        odbcori = SQL_FETCH_NEXT;\n        break;\n    case PDO_FETCH_ORI_PRIOR:\n        odbcori = SQL_FETCH_PRIOR;\n        break;\n    case PDO_FETCH_ORI_FIRST:\n        odbcori = SQL_FETCH_FIRST;\n        break;\n    case PDO_FETCH_ORI_LAST:\n        odbcori = SQL_FETCH_LAST;\n        break;\n    case PDO_FETCH_ORI_ABS:\n        odbcori = SQL_FETCH_ABSOLUTE;\n        break;\n    case PDO_FETCH_ORI_REL:\n        odbcori = SQL_FETCH_RELATIVE;\n        break;\n    default:\n        strcpy(stmt->error_code, \"HY106\");\n        return 0;\n    }\n    rc = SQLFetchScroll(S->stmt, odbcori, offset);\n\n    if (rc == SQL_SUCCESS) {\n        return 1;\n    }\n    if (rc == SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_stmt_error(\"SQLFetchScroll\");\n        return 1;\n    }\n\n    if (rc == SQL_NO_DATA) {\n        /* pdo_odbc_stmt_error(\"SQLFetchScroll\"); */\n        return 0;\n    }\n\n    pdo_odbc_stmt_error(\"SQLFetchScroll\");\n\n    return 0;\n}\n\nstatic int odbc_stmt_describe(pdo_stmt_t *stmt, int colno) {\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n    struct pdo_column_data *col = &stmt->columns[colno];\n    RETCODE rc;\n    SQLSMALLINT colnamelen;\n    SQLULEN colsize;\n    SQLLEN displaysize = 0;\n\n    rc = SQLDescribeCol(S->stmt,\n                        colno + 1,\n                        (SQLCHAR *) S->cols[colno].colname,\n                        sizeof(S->cols[colno].colname) - 1,\n                        &colnamelen,\n                        &S->cols[colno].coltype,\n                        &colsize,\n                        NULL,\n                        NULL);\n\n    /* This fixes a known issue with SQL Server and (max) lengths,\n    may affect others as well.  If we are SQL_VARCHAR,\n    SQL_VARBINARY, or SQL_WVARCHAR (or any of the long variations)\n    and zero is returned from colsize then consider it long */\n    if (0 == colsize && (S->cols[colno].coltype == SQL_VARCHAR || S->cols[colno].coltype == SQL_LONGVARCHAR ||\n#ifdef SQL_WVARCHAR\n                         S->cols[colno].coltype == SQL_WVARCHAR ||\n#endif\n#ifdef SQL_WLONGVARCHAR\n                         S->cols[colno].coltype == SQL_WLONGVARCHAR ||\n#endif\n                         S->cols[colno].coltype == SQL_VARBINARY || S->cols[colno].coltype == SQL_LONGVARBINARY)) {\n        S->going_long = 1;\n    }\n\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_stmt_error(\"SQLDescribeCol\");\n        if (rc != SQL_SUCCESS_WITH_INFO) {\n            return 0;\n        }\n    }\n\n    rc = SQLColAttribute(S->stmt, colno + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &displaysize);\n\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_stmt_error(\"SQLColAttribute\");\n        if (rc != SQL_SUCCESS_WITH_INFO) {\n            return 0;\n        }\n    }\n    colsize = displaysize;\n\n    col->maxlen = S->cols[colno].datalen = colsize;\n    col->name = zend_string_init(S->cols[colno].colname, colnamelen, 0);\n    S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype);\n\n    /* tell ODBC to put it straight into our buffer, but only if it\n     * isn't \"long\" data, and only if we haven't already bound a long\n     * column. */\n    if (colsize < 256 && !S->going_long) {\n        S->cols[colno].data = emalloc(colsize + 1);\n        S->cols[colno].is_long = 0;\n\n        rc = SQLBindCol(S->stmt,\n                        colno + 1,\n                        S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR,\n                        S->cols[colno].data,\n                        S->cols[colno].datalen + 1,\n                        &S->cols[colno].fetched_len);\n\n        if (rc != SQL_SUCCESS) {\n            pdo_odbc_stmt_error(\"SQLBindCol\");\n            return 0;\n        }\n    } else {\n        /* allocate a smaller buffer to keep around for smaller\n         * \"long\" columns */\n        S->cols[colno].data = emalloc(256);\n        S->going_long = 1;\n        S->cols[colno].is_long = 1;\n    }\n\n    return 1;\n}\n\nstatic int odbc_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) {\n    array_init(return_value);\n    add_assoc_long(return_value, \"pdo_type\", PDO_PARAM_STR);\n    return 1;\n}\n\nstatic int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) {\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n    pdo_odbc_column *C = &S->cols[colno];\n\n    /* if it is a column containing \"long\" data, perform late binding now */\n    if (C->is_long) {\n        SQLLEN orig_fetched_len = SQL_NULL_DATA;\n        RETCODE rc;\n\n        /* fetch it into C->data, which is allocated with a length\n         * of 256 bytes; if there is more to be had, we then allocate\n         * bigger buffer for the caller to free */\n\n        rc = SQLGetData(S->stmt, colno + 1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data, 256, &C->fetched_len);\n        orig_fetched_len = C->fetched_len;\n\n        if (rc == SQL_SUCCESS && C->fetched_len < 256) {\n            /* all the data fit into our little buffer;\n             * jump down to the generic bound data case */\n            goto in_data;\n        }\n\n        if (rc == SQL_SUCCESS_WITH_INFO || rc == SQL_SUCCESS) {\n            /* this is a 'long column'\n\n             read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks\n             in order into the output buffer; 255 bytes are an optimistic assumption, since the driver may assert\n             more or less NUL bytes at the end; we cater to that later, if actual length information is available\n\n             this loop has to work whether or not SQLGetData() provides the total column length.\n             calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read\n             for that size would be slower except maybe for extremely long columns.*/\n            char *buf2 = emalloc(256);\n            zend_string *str = zend_string_init(C->data, 256, 0);\n            size_t used = 255; /* not 256; the driver NUL terminated the buffer */\n\n            do {\n                C->fetched_len = 0;\n                /* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */\n                rc = SQLGetData(\n                    S->stmt, colno + 1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, buf2, 256, &C->fetched_len);\n\n                /* adjust `used` in case we have proper length info from the driver */\n                if (orig_fetched_len >= 0 && C->fetched_len >= 0) {\n                    SQLLEN fixed_used = orig_fetched_len - C->fetched_len;\n                    if (fixed_used <= used + 1) {\n                        used = fixed_used;\n                    }\n                }\n\n                /* resize output buffer and reassemble block */\n                if (rc == SQL_SUCCESS_WITH_INFO || (rc == SQL_SUCCESS && C->fetched_len > 255)) {\n                    /* point 5, in section \"Retrieving Data with SQLGetData\" in\n                     http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx states that if\n                     SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size) (if a driver fails to\n                     follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */\n                    str = zend_string_realloc(str, used + 256, 0);\n                    memcpy(ZSTR_VAL(str) + used, buf2, 256);\n                    used = used + 255;\n                } else if (rc == SQL_SUCCESS) {\n                    str = zend_string_realloc(str, used + C->fetched_len, 0);\n                    memcpy(ZSTR_VAL(str) + used, buf2, C->fetched_len);\n                    used = used + C->fetched_len;\n                } else {\n                    /* includes SQL_NO_DATA */\n                    break;\n                }\n\n            } while (1);\n\n            efree(buf2);\n\n            /* NULL terminate the buffer once, when finished, for use with the rest of PHP */\n            ZSTR_VAL(str)[used] = '\\0';\n            ZVAL_STR(result, str);\n            if (C->is_unicode) {\n                goto unicode_conv;\n            }\n            return 1;\n        }\n\n        /* something went caca */\n        return 1;\n    }\n\nin_data:\n    /* check the indicator to ensure that the data is intact */\n    if (C->fetched_len == SQL_NULL_DATA) {\n        /* A NULL value */\n        ZVAL_NULL(result);\n        return 1;\n    } else if (C->fetched_len >= 0) {\n        /* it was stored perfectly */\n        ZVAL_STRINGL_FAST(result, C->data, C->fetched_len);\n        if (C->is_unicode) {\n            goto unicode_conv;\n        }\n        return 1;\n    } else {\n        /* no data? */\n        ZVAL_NULL(result);\n        return 1;\n    }\n\nunicode_conv:\n    switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, result)) {\n    case PDO_ODBC_CONV_FAIL:\n        /* oh well.  They can have the binary version of it */\n    case PDO_ODBC_CONV_NOT_REQUIRED:\n        /* shouldn't happen... */\n        return 1;\n    case PDO_ODBC_CONV_OK:\n        return 1;\n    }\n    return 1;\n}\n\nstatic int odbc_stmt_set_param(pdo_stmt_t *stmt, zend_long attr, zval *val) {\n    SQLRETURN rc;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    switch (attr) {\n    case PDO_ATTR_CURSOR_NAME:\n        convert_to_string(val);\n        rc = SQLSetCursorName(S->stmt, (SQLCHAR *) Z_STRVAL_P(val), Z_STRLEN_P(val));\n\n        if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {\n            return 1;\n        }\n        pdo_odbc_stmt_error(\"SQLSetCursorName\");\n        return 0;\n\n    case PDO_ODBC_ATTR_ASSUME_UTF8:\n        S->assume_utf8 = zval_is_true(val);\n        return 0;\n    default:\n        strcpy(S->einfo.last_err_msg, \"Unknown Attribute\");\n        S->einfo.what = \"setAttribute\";\n        strcpy(S->einfo.last_state, \"IM001\");\n        return -1;\n    }\n}\n\nstatic int odbc_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val) {\n    SQLRETURN rc;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    switch (attr) {\n    case PDO_ATTR_CURSOR_NAME: {\n        char buf[256];\n        SQLSMALLINT len = 0;\n        rc = SQLGetCursorName(S->stmt, (SQLCHAR *) buf, sizeof(buf), &len);\n\n        if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {\n            ZVAL_STRINGL(val, buf, len);\n            return 1;\n        }\n        pdo_odbc_stmt_error(\"SQLGetCursorName\");\n        return 0;\n    }\n\n    case PDO_ODBC_ATTR_ASSUME_UTF8:\n        ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0);\n        return 0;\n\n    default:\n        strcpy(S->einfo.last_err_msg, \"Unknown Attribute\");\n        S->einfo.what = \"getAttribute\";\n        strcpy(S->einfo.last_state, \"IM001\");\n        return -1;\n    }\n}\n\nstatic int odbc_stmt_next_rowset(pdo_stmt_t *stmt) {\n    SQLRETURN rc;\n    SQLSMALLINT colcount;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    /* NOTE: can't guarantee that output or input/output parameters\n     * are set until this fella returns SQL_NO_DATA, according to\n     * MSDN ODBC docs */\n    rc = SQLMoreResults(S->stmt);\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        return 0;\n    }\n\n    free_cols(stmt, S);\n    /* how many columns do we have ? */\n    SQLNumResultCols(S->stmt, &colcount);\n    stmt->column_count = S->col_count = (int) colcount;\n    S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));\n    S->going_long = 0;\n\n    return 1;\n}\n\nstatic int odbc_stmt_close_cursor(pdo_stmt_t *stmt) {\n    SQLRETURN rc;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    rc = SQLCloseCursor(S->stmt);\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        return 0;\n    }\n    return 1;\n}\n\nconst struct pdo_stmt_methods odbc_stmt_methods = {odbc_stmt_dtor,\n                                                   odbc_stmt_execute,\n                                                   odbc_stmt_fetch,\n                                                   odbc_stmt_describe,\n                                                   odbc_stmt_get_col,\n                                                   odbc_stmt_param_hook,\n                                                   odbc_stmt_set_param,\n                                                   odbc_stmt_get_attr,\n                                                   odbc_stmt_get_column_meta,\n                                                   odbc_stmt_next_rowset,\n                                                   odbc_stmt_close_cursor};\n#endif\n"
  },
  {
    "path": "thirdparty/php82/pdo_odbc/php_pdo_odbc_int.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#ifdef PHP_WIN32\n#define PDO_ODBC_TYPE \"Win32\"\n#endif\n\n#ifndef PDO_ODBC_TYPE\n#warning Please fix configure to give your ODBC libraries a name\n#define PDO_ODBC_TYPE \"Unknown\"\n#endif\n\n/* {{{ Roll a dice, pick a header at random... */\n#ifdef HAVE_SQLCLI1_H\n#include <sqlcli1.h>\n#if defined(DB268K) && HAVE_LIBRARYMANAGER_H\n#include <LibraryManager.h>\n#endif\n#endif\n\n#ifdef HAVE_ODBC_H\n#include <odbc.h>\n#endif\n\n#ifdef HAVE_IODBC_H\n#include <iodbc.h>\n#endif\n\n#if defined(HAVE_SQLUNIX_H) && !defined(PHP_WIN32)\n#include <sqlunix.h>\n#endif\n\n#ifdef HAVE_SQLTYPES_H\n#include <sqltypes.h>\n#endif\n\n#ifdef HAVE_SQLUCODE_H\n#include <sqlucode.h>\n#endif\n\n#ifdef HAVE_SQL_H\n#include <sql.h>\n#endif\n\n#ifdef HAVE_ISQL_H\n#include <isql.h>\n#endif\n\n#ifdef HAVE_SQLEXT_H\n#include <sqlext.h>\n#endif\n\n#ifdef HAVE_ISQLEXT_H\n#include <isqlext.h>\n#endif\n\n#ifdef HAVE_UDBCEXT_H\n#include <udbcext.h>\n#endif\n\n#ifdef HAVE_CLI0CORE_H\n#include <cli0core.h>\n#endif\n\n#ifdef HAVE_CLI0EXT1_H\n#include <cli0ext.h>\n#endif\n\n#ifdef HAVE_CLI0CLI_H\n#include <cli0cli.h>\n#endif\n\n#ifdef HAVE_CLI0DEFS_H\n#include <cli0defs.h>\n#endif\n\n#ifdef HAVE_CLI0ENV_H\n#include <cli0env.h>\n#endif\n\n#ifdef HAVE_ODBCSDK_H\n#include <odbcsdk.h>\n#endif\n\n/* }}} */\n\n/* {{{ Figure out the type for handles */\n#if !defined(HENV) && !defined(SQLHENV) && defined(SQLHANDLE)\n#define PDO_ODBC_HENV SQLHANDLE\n#define PDO_ODBC_HDBC SQLHANDLE\n#define PDO_ODBC_HSTMT SQLHANDLE\n#elif !defined(HENV) && (defined(SQLHENV) || defined(DB2CLI_VER))\n#define PDO_ODBC_HENV SQLHENV\n#define PDO_ODBC_HDBC SQLHDBC\n#define PDO_ODBC_HSTMT SQLHSTMT\n#else\n#define PDO_ODBC_HENV HENV\n#define PDO_ODBC_HDBC HDBC\n#define PDO_ODBC_HSTMT HSTMT\n#endif\n/* }}} */\n\ntypedef struct {\n    char last_state[6];\n    char last_err_msg[SQL_MAX_MESSAGE_LENGTH];\n    SQLINTEGER last_error;\n    const char *file, *what;\n    int line;\n} pdo_odbc_errinfo;\n\ntypedef struct {\n    PDO_ODBC_HENV env;\n    PDO_ODBC_HDBC dbc;\n    pdo_odbc_errinfo einfo;\n    unsigned assume_utf8 : 1;\n    unsigned _spare : 31;\n} pdo_odbc_db_handle;\n\ntypedef struct {\n    char *data;\n    zend_ulong datalen;\n    SQLLEN fetched_len;\n    SQLSMALLINT coltype;\n    char colname[128];\n    unsigned is_long;\n    unsigned is_unicode : 1;\n    unsigned _spare : 31;\n} pdo_odbc_column;\n\ntypedef struct {\n    PDO_ODBC_HSTMT stmt;\n    pdo_odbc_column *cols;\n    pdo_odbc_db_handle *H;\n    pdo_odbc_errinfo einfo;\n    char *convbuf;\n    zend_ulong convbufsize;\n    unsigned going_long : 1;\n    unsigned assume_utf8 : 1;\n    signed col_count : 16;\n    unsigned _spare : 14;\n} pdo_odbc_stmt;\n\ntypedef struct {\n    SQLLEN len;\n    SQLSMALLINT paramtype;\n    char *outbuf;\n    unsigned is_unicode : 1;\n    unsigned _spare : 31;\n} pdo_odbc_param;\n\nextern const pdo_driver_t pdo_odbc_driver;\nextern const struct pdo_stmt_methods odbc_stmt_methods;\n\nvoid pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line);\n#define pdo_odbc_drv_error(what) pdo_odbc_error(dbh, NULL, SQL_NULL_HSTMT, what, __FILE__, __LINE__)\n#define pdo_odbc_stmt_error(what) pdo_odbc_error(stmt->dbh, stmt, SQL_NULL_HSTMT, what, __FILE__, __LINE__)\n#define pdo_odbc_doer_error(what) pdo_odbc_error(dbh, NULL, stmt, what, __FILE__, __LINE__)\n\nvoid pdo_odbc_init_error_table(void);\nvoid pdo_odbc_fini_error_table(void);\n\n#ifdef SQL_ATTR_CONNECTION_POOLING\nextern zend_ulong pdo_odbc_pool_on;\nextern zend_ulong pdo_odbc_pool_mode;\n#endif\n\nenum {\n    PDO_ODBC_ATTR_USE_CURSOR_LIBRARY = PDO_ATTR_DRIVER_SPECIFIC,\n    PDO_ODBC_ATTR_ASSUME_UTF8 /* assume that input strings are UTF-8 when feeding data to unicode columns */\n};\n"
  },
  {
    "path": "thirdparty/php82/pdo_pgsql/pgsql_driver.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Authors: Edin Kadribasic <edink@emini.dk>                            |\n  |          Ilia Alshanestsky <ilia@prohost.org>                        |\n  |          Wez Furlong <wez@php.net>                                   |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_PGSQL_HOOK\n#include \"php_swoole_pgsql.h\"\n\n#if PHP_VERSION_ID >= 80200 && PHP_VERSION_ID < 80300\n\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"ext/standard/php_string.h\"\n#include \"main/php_network.h\"\n#include \"pdo/php_pdo.h\"\n#include \"pdo/php_pdo_driver.h\"\n#include \"pdo/php_pdo_error.h\"\n#include \"ext/standard/file.h\"\n#include \"php_pdo_pgsql_int.h\"\n#include \"zend_exceptions.h\"\n#include \"pgsql_driver_arginfo.h\"\n\nstatic bool pgsql_handle_in_transaction(pdo_dbh_t *dbh);\n\nstatic char * _pdo_pgsql_trim_message(const char *message, int persistent)\n{\n\tsize_t i = strlen(message)-1;\n\tchar *tmp;\n\n\tif (i>1 && (message[i-1] == '\\r' || message[i-1] == '\\n') && message[i] == '.') {\n\t\t--i;\n\t}\n\twhile (i>0 && (message[i] == '\\r' || message[i] == '\\n')) {\n\t\t--i;\n\t}\n\t++i;\n\ttmp = pemalloc(i + 1, persistent);\n\tmemcpy(tmp, message, i);\n\ttmp[i] = '\\0';\n\n\treturn tmp;\n}\n\nstatic zend_string* _pdo_pgsql_escape_credentials(char *str)\n{\n\tif (str) {\n\t\treturn php_addcslashes_str(str, strlen(str), \"\\\\'\", sizeof(\"\\\\'\"));\n\t}\n\n\treturn NULL;\n}\n\nint _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line) /* {{{ */\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tpdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code;\n\tpdo_pgsql_error_info *einfo = &H->einfo;\n\tchar *errmsg = PQerrorMessage(H->server);\n\n\teinfo->errcode = errcode;\n\teinfo->file = file;\n\teinfo->line = line;\n\n\tif (einfo->errmsg) {\n\t\tpefree(einfo->errmsg, dbh->is_persistent);\n\t\teinfo->errmsg = NULL;\n\t}\n\n\tif (sqlstate == NULL || strlen(sqlstate) >= sizeof(pdo_error_type)) {\n\t\tstrcpy(*pdo_err, \"HY000\");\n\t}\n\telse {\n\t\tstrcpy(*pdo_err, sqlstate);\n\t}\n\n\tif (msg) {\n\t\teinfo->errmsg = pestrdup(msg, dbh->is_persistent);\n\t}\n\telse if (errmsg) {\n\t\teinfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent);\n\t}\n\n\tif (!dbh->methods) {\n\t\tpdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err);\n\t}\n\n\treturn errcode;\n}\n/* }}} */\n\nstatic void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */\n{\n/*\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */\n}\n/* }}} */\n\nstatic void pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tpdo_pgsql_error_info *einfo = &H->einfo;\n\n\tif (einfo->errcode) {\n\t\tadd_next_index_long(info, einfo->errcode);\n\t} else {\n\t\t/* Add null to respect expected info array structure */\n\t\tadd_next_index_null(info);\n\t}\n\tif (einfo->errmsg) {\n\t\tadd_next_index_string(info, einfo->errmsg);\n\t}\n}\n/* }}} */\n\n/* {{{ pdo_pgsql_create_lob_stream */\nstatic ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count)\n{\n\tstruct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;\n\treturn lo_write(self->conn, self->lfd, (char*)buf, count);\n}\n\nstatic ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count)\n{\n\tstruct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;\n\treturn lo_read(self->conn, self->lfd, buf, count);\n}\n\nstatic int pgsql_lob_close(php_stream *stream, int close_handle)\n{\n\tstruct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(&self->dbh))->driver_data;\n\n\tif (close_handle) {\n\t\tlo_close(self->conn, self->lfd);\n\t}\n\tzend_hash_index_del(H->lob_streams, php_stream_get_resource_id(stream));\n\tzval_ptr_dtor(&self->dbh);\n\tefree(self);\n\treturn 0;\n}\n\nstatic int pgsql_lob_flush(php_stream *stream)\n{\n\treturn 0;\n}\n\nstatic int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence,\n\t\tzend_off_t *newoffset)\n{\n\tstruct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;\n#if defined(HAVE_PG_LO64) && defined(ZEND_ENABLE_ZVAL_LONG64)\n\tzend_off_t pos = lo_lseek64(self->conn, self->lfd, offset, whence);\n#else\n\tzend_off_t pos = lo_lseek(self->conn, self->lfd, offset, whence);\n#endif\n\t*newoffset = pos;\n\treturn pos >= 0 ? 0 : -1;\n}\n\nconst php_stream_ops pdo_pgsql_lob_stream_ops = {\n\tpgsql_lob_write,\n\tpgsql_lob_read,\n\tpgsql_lob_close,\n\tpgsql_lob_flush,\n\t\"pdo_pgsql lob stream\",\n\tpgsql_lob_seek,\n\tNULL,\n\tNULL,\n\tNULL\n};\n\nphp_stream *pdo_pgsql_create_lob_stream(zval *dbh, int lfd, Oid oid)\n{\n\tphp_stream *stm;\n\tstruct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self));\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(dbh))->driver_data;\n\n\tZVAL_COPY_VALUE(&self->dbh, dbh);\n\tself->lfd = lfd;\n\tself->oid = oid;\n\tself->conn = H->server;\n\n\tstm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, \"r+b\");\n\n\tif (stm) {\n\t\tZ_ADDREF_P(dbh);\n\t\tzend_hash_index_add_ptr(H->lob_streams, php_stream_get_resource_id(stm), stm->res);\n\t\treturn stm;\n\t}\n\n\tefree(self);\n\treturn NULL;\n}\n/* }}} */\n\nvoid pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh)\n{\n\tzend_resource *res;\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tif (H->lob_streams) {\n\t\tZEND_HASH_REVERSE_FOREACH_PTR(H->lob_streams, res) {\n\t\t\tif (res->type >= 0) {\n\t\t\t\tzend_list_close(res);\n\t\t\t}\n\t\t} ZEND_HASH_FOREACH_END();\n\t}\n}\n\nstatic void pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tif (H) {\n\t\tif (H->lob_streams) {\n\t\t\tpdo_pgsql_close_lob_streams(dbh);\n\t\t\tzend_hash_destroy(H->lob_streams);\n\t\t\tpefree(H->lob_streams, dbh->is_persistent);\n\t\t\tH->lob_streams = NULL;\n\t\t}\n\t\tif (H->server) {\n\t\t\tPQfinish(H->server);\n\t\t\tH->server = NULL;\n\t\t}\n\t\tif (H->einfo.errmsg) {\n\t\t\tpefree(H->einfo.errmsg, dbh->is_persistent);\n\t\t\tH->einfo.errmsg = NULL;\n\t\t}\n\t\tpefree(H, dbh->is_persistent);\n\t\tdbh->driver_data = NULL;\n\t}\n}\n/* }}} */\n\nstatic bool pgsql_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tpdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt));\n\tint scrollable;\n\tint ret;\n\tzend_string *nsql = NULL;\n\tint emulate = 0;\n\tint execute_only = 0;\n\n\tS->H = H;\n\tstmt->driver_data = S;\n\tstmt->methods = &swoole_pgsql_stmt_methods;\n\n\tscrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR,\n\t\tPDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL;\n\n\tif (scrollable) {\n\t\tif (S->cursor_name) {\n\t\t\tefree(S->cursor_name);\n\t\t}\n\t\tspprintf(&S->cursor_name, 0, \"pdo_crsr_%08x\", ++H->stmt_counter);\n\t\temulate = 1;\n\t} else if (driver_options) {\n\t\tif (pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares) == 1) {\n\t\t\temulate = 1;\n\t\t}\n\t\tif (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_PREPARES, H->disable_prepares) == 1) {\n\t\t\texecute_only = 1;\n\t\t}\n\t} else {\n\t\temulate = H->disable_native_prepares || H->emulate_prepares;\n\t\texecute_only = H->disable_prepares;\n\t}\n\n\tif (!emulate && PQprotocolVersion(H->server) <= 2) {\n\t\temulate = 1;\n\t}\n\n\tif (emulate) {\n\t\tstmt->supports_placeholders = PDO_PLACEHOLDER_NONE;\n\t} else {\n\t\tstmt->supports_placeholders = PDO_PLACEHOLDER_NAMED;\n\t\tstmt->named_rewrite_template = \"$%d\";\n\t}\n\n\tret = pdo_parse_params(stmt, sql, &nsql);\n\n\tif (ret == -1) {\n\t\t/* couldn't grok it */\n\t\tstrcpy(dbh->error_code, stmt->error_code);\n\t\treturn false;\n\t} else if (ret == 1) {\n\t\t/* query was re-written */\n\t\tS->query = nsql;\n\t} else {\n\t\tS->query = zend_string_copy(sql);\n\t}\n\n\tif (!emulate && !execute_only) {\n\t\t/* prepared query: set the query name and defer the\n\t\t   actual prepare until the first execute call */\n\t\tspprintf(&S->stmt_name, 0, \"pdo_stmt_%08x\", ++H->stmt_counter);\n\t}\n\n\treturn true;\n}\n\nstatic zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const zend_string *sql)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tPGresult *res;\n\tzend_long ret = 1;\n\tExecStatusType qs;\n\n\tbool in_trans = pgsql_handle_in_transaction(dbh);\n\n\tif (!(res = PQexec(H->server, ZSTR_VAL(sql)))) {\n\t\t/* fatal error */\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\treturn -1;\n\t}\n\tqs = PQresultStatus(res);\n\tif (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) {\n\t\tpdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res));\n\t\tPQclear(res);\n\t\treturn -1;\n\t}\n\tH->pgoid = PQoidValue(res);\n\tif (qs == PGRES_COMMAND_OK) {\n\t\tret = ZEND_ATOL(PQcmdTuples(res));\n\t} else {\n\t\tret = Z_L(0);\n\t}\n\tPQclear(res);\n\tif (in_trans && !pgsql_handle_in_transaction(dbh)) {\n\t\tpdo_pgsql_close_lob_streams(dbh);\n\t}\n\n\treturn ret;\n}\n\nstatic zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype)\n{\n\tunsigned char *escaped;\n\tchar *quoted;\n\tsize_t quotedlen;\n\tzend_string *quoted_str;\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tsize_t tmp_len;\n\tint err;\n\n\tswitch (paramtype) {\n\t\tcase PDO_PARAM_LOB:\n\t\t\t/* escapedlen returned by PQescapeBytea() accounts for trailing 0 */\n\t\t\tescaped = PQescapeByteaConn(H->server, (unsigned char *)ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &tmp_len);\n\t\t\tif (escaped == NULL) {\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tquotedlen = tmp_len + 1;\n\t\t\tquoted = emalloc(quotedlen + 1);\n\t\t\tmemcpy(quoted+1, escaped, quotedlen-2);\n\t\t\tquoted[0] = '\\'';\n\t\t\tquoted[quotedlen-1] = '\\'';\n\t\t\tquoted[quotedlen] = '\\0';\n\t\t\tPQfreemem(escaped);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tquoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3);\n\t\t\tquoted[0] = '\\'';\n\t\t\tquotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &err);\n\t\t\tif (err) {\n\t\t\t\tefree(quoted);\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tquoted[quotedlen + 1] = '\\'';\n\t\t\tquoted[quotedlen + 2] = '\\0';\n\t\t\tquotedlen += 2;\n\t}\n\n\tquoted_str = zend_string_init(quoted, quotedlen, 0);\n\tefree(quoted);\n\treturn quoted_str;\n}\n\nstatic zend_string *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const zend_string *name)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tzend_string *id = NULL;\n\tPGresult *res;\n\tExecStatusType status;\n\n\tif (name == NULL) {\n\t\tres = PQexec(H->server, \"SELECT LASTVAL()\");\n\t} else {\n\t\tconst char *q[1];\n\t\tq[0] = ZSTR_VAL(name);\n\n\t\tres = PQexecParams(H->server, \"SELECT CURRVAL($1)\", 1, NULL, q, NULL, NULL, 0);\n\t}\n\tstatus = PQresultStatus(res);\n\n\tif (res && (status == PGRES_TUPLES_OK)) {\n\t\tid = zend_string_init((char *)PQgetvalue(res, 0, 0), PQgetlength(res, 0, 0), 0);\n\t} else {\n\t\tpdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res));\n\t}\n\n\tif (res) {\n\t\tPQclear(res);\n\t}\n\n\treturn id;\n}\n\nvoid pdo_libpq_version(char *buf, size_t len)\n{\n\tint version = PQlibVersion();\n\tint major = version / 10000;\n\tif (major >= 10) {\n\t\tint minor = version % 10000;\n\t\tsnprintf(buf, len, \"%d.%d\", major, minor);\n\t} else {\n\t\tint minor = version / 100 % 100;\n\t\tint revision = version % 100;\n\t\tsnprintf(buf, len, \"%d.%d.%d\", major, minor, revision);\n\t}\n}\n\nstatic int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tswitch (attr) {\n\t\tcase PDO_ATTR_EMULATE_PREPARES:\n\t\t\tZVAL_BOOL(return_value, H->emulate_prepares);\n\t\t\tbreak;\n\n\t\tcase PDO_PGSQL_ATTR_DISABLE_PREPARES:\n\t\t\tZVAL_BOOL(return_value, H->disable_prepares);\n\t\t\tbreak;\n\n\t\tcase PDO_ATTR_CLIENT_VERSION: {\n\t\t\tchar buf[16];\n\t\t\tpdo_libpq_version(buf, sizeof(buf));\n\t\t\tZVAL_STRING(return_value, buf);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase PDO_ATTR_SERVER_VERSION:\n\t\t\tif (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */\n\t\t\t\tZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, \"server_version\"));\n\t\t\t} else /* emulate above via a query */\n\t\t\t{\n\t\t\t\tPGresult *res = PQexec(H->server, \"SELECT VERSION()\");\n\t\t\t\tif (res && PQresultStatus(res) == PGRES_TUPLES_OK) {\n\t\t\t\t\tZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0));\n\t\t\t\t}\n\n\t\t\t\tif (res) {\n\t\t\t\t\tPQclear(res);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase PDO_ATTR_CONNECTION_STATUS:\n\t\t\tswitch (PQstatus(H->server)) {\n\t\t\t\tcase CONNECTION_STARTED:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Waiting for connection to be made.\", sizeof(\"Waiting for connection to be made.\")-1);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase CONNECTION_MADE:\n\t\t\t\tcase CONNECTION_OK:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Connection OK; waiting to send.\", sizeof(\"Connection OK; waiting to send.\")-1);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase CONNECTION_AWAITING_RESPONSE:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Waiting for a response from the server.\", sizeof(\"Waiting for a response from the server.\")-1);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase CONNECTION_AUTH_OK:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Received authentication; waiting for backend start-up to finish.\", sizeof(\"Received authentication; waiting for backend start-up to finish.\")-1);\n\t\t\t\t\tbreak;\n#ifdef CONNECTION_SSL_STARTUP\n\t\t\t\tcase CONNECTION_SSL_STARTUP:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Negotiating SSL encryption.\", sizeof(\"Negotiating SSL encryption.\")-1);\n\t\t\t\t\tbreak;\n#endif\n\t\t\t\tcase CONNECTION_SETENV:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Negotiating environment-driven parameter settings.\", sizeof(\"Negotiating environment-driven parameter settings.\")-1);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase CONNECTION_BAD:\n\t\t\t\tdefault:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Bad connection.\", sizeof(\"Bad connection.\")-1);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase PDO_ATTR_SERVER_INFO: {\n\t\t\tint spid = PQbackendPID(H->server);\n\n\n\t\t\tzend_string *str_info =\n\t\t\t\tstrpprintf(0,\n\t\t\t\t\t\"PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s\",\n\t\t\t\t\tspid,\n\t\t\t\t\t(char*)PQparameterStatus(H->server, \"client_encoding\"),\n\t\t\t\t\t(char*)PQparameterStatus(H->server, \"is_superuser\"),\n\t\t\t\t\t(char*)PQparameterStatus(H->server, \"session_authorization\"),\n\t\t\t\t\t(char*)PQparameterStatus(H->server, \"DateStyle\"));\n\n\t\t\tZVAL_STR(return_value, str_info);\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\n/* {{{ */\nstatic zend_result pdo_pgsql_check_liveness(pdo_dbh_t *dbh)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tif (!PQconsumeInput(H->server) || PQstatus(H->server) == CONNECTION_BAD) {\n\t\tPQreset(H->server);\n\t}\n\treturn (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE;\n}\n/* }}} */\n\nstatic bool pgsql_handle_in_transaction(pdo_dbh_t *dbh)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\treturn PQtransactionStatus(H->server) > PQTRANS_IDLE;\n}\n\nstatic bool pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tPGresult *res;\n\tbool ret = true;\n\n\tres = PQexec(H->server, cmd);\n\n\tif (PQresultStatus(res) != PGRES_COMMAND_OK) {\n\t\tpdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res));\n\t\tret = false;\n\t}\n\n\tPQclear(res);\n\treturn ret;\n}\n\nstatic bool pgsql_handle_begin(pdo_dbh_t *dbh)\n{\n\treturn pdo_pgsql_transaction_cmd(\"BEGIN\", dbh);\n}\n\nstatic bool pgsql_handle_commit(pdo_dbh_t *dbh)\n{\n\tbool ret = pdo_pgsql_transaction_cmd(\"COMMIT\", dbh);\n\n\t/* When deferred constraints are used the commit could\n\t   fail, and a ROLLBACK implicitly ran. See bug #67462 */\n\tif (ret) {\n\t\tpdo_pgsql_close_lob_streams(dbh);\n\t} else {\n\t\tdbh->in_txn = pgsql_handle_in_transaction(dbh);\n\t}\n\n\treturn ret;\n}\n\nstatic bool pgsql_handle_rollback(pdo_dbh_t *dbh)\n{\n\tint ret = pdo_pgsql_transaction_cmd(\"ROLLBACK\", dbh);\n\n\tif (ret) {\n\t\tpdo_pgsql_close_lob_streams(dbh);\n\t}\n\n\treturn ret;\n}\n\n/* {{{ Returns true if the copy worked fine or false if error */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\n\tzval *pg_rows;\n\n\tchar *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;\n\tsize_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;\n\tchar *query;\n\n\tPGresult *pgsql_result;\n\tExecStatusType status;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"sa|sss!\",\n\t\t\t\t\t&table_name, &table_name_len, &pg_rows,\n\t\t\t\t\t&pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\n\tif (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) {\n\t\tzend_argument_value_error(2, \"cannot be empty\");\n\t\tRETURN_THROWS();\n\t}\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\t/* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */\n\tif (pg_fields) {\n\t\tspprintf(&query, 0, \"COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t} else {\n\t\tspprintf(&query, 0, \"COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t}\n\n\t/* Obtain db Handle */\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\tPQclear(pgsql_result);\n\t}\n\tpgsql_result = PQexec(H->server, query);\n\n\tefree(query);\n\tquery = NULL;\n\n\tif (pgsql_result) {\n\t\tstatus = PQresultStatus(pgsql_result);\n\t} else {\n\t\tstatus = (ExecStatusType) PQstatus(H->server);\n\t}\n\n\tif (status == PGRES_COPY_IN && pgsql_result) {\n\t\tint command_failed = 0;\n\t\tsize_t buffer_len = 0;\n\t\tzval *tmp;\n\n\t\tPQclear(pgsql_result);\n\t\tZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) {\n\t\t\tsize_t query_len;\n\t\t\tif (!try_convert_to_string(tmp)) {\n\t\t\t\tefree(query);\n\t\t\t\tRETURN_THROWS();\n\t\t\t}\n\n\t\t\tif (buffer_len < Z_STRLEN_P(tmp)) {\n\t\t\t\tbuffer_len = Z_STRLEN_P(tmp);\n\t\t\t\tquery = erealloc(query, buffer_len + 2); /* room for \\n\\0 */\n\t\t\t}\n\t\t\tmemcpy(query, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));\n\t\t\tquery_len = Z_STRLEN_P(tmp);\n\t\t\tif (query[query_len - 1] != '\\n') {\n\t\t\t\tquery[query_len++] = '\\n';\n\t\t\t}\n\t\t\tquery[query_len] = '\\0';\n\t\t\tif (PQputCopyData(H->server, query, query_len) != 1) {\n\t\t\t\tefree(query);\n\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\t\tRETURN_FALSE;\n\t\t\t}\n\t\t} ZEND_HASH_FOREACH_END();\n\t\tif (query) {\n\t\t\tefree(query);\n\t\t}\n\n\t\tif (PQputCopyEnd(H->server, NULL) != 1) {\n\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\tRETURN_FALSE;\n\t\t}\n\n\t\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\t\tif (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {\n\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\t\t\tcommand_failed = 1;\n\t\t\t}\n\t\t\tPQclear(pgsql_result);\n\t\t}\n\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_BOOL(!command_failed);\n\t} else {\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\tPQclear(pgsql_result);\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n}\n/* }}} */\n\n/* {{{ Returns true if the copy worked fine or false if error */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\n\tchar *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;\n\tsize_t  table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;\n\tchar *query;\n\tPGresult *pgsql_result;\n\tExecStatusType status;\n\tphp_stream *stream;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"sp|sss!\",\n\t\t\t\t&table_name, &table_name_len, &filename, &filename_len,\n\t\t\t\t&pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\n\t/* Obtain db Handler */\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tstream = php_stream_open_wrapper_ex(filename, \"rb\", 0, NULL, FG(default_context));\n\tif (!stream) {\n\t\tpdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, \"Unable to open the file\");\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n\n\t/* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */\n\tif (pg_fields) {\n\t\tspprintf(&query, 0, \"COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t} else {\n\t\tspprintf(&query, 0, \"COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t}\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\tPQclear(pgsql_result);\n\t}\n\tpgsql_result = PQexec(H->server, query);\n\n\tefree(query);\n\n\tif (pgsql_result) {\n\t\tstatus = PQresultStatus(pgsql_result);\n\t} else {\n\t\tstatus = (ExecStatusType) PQstatus(H->server);\n\t}\n\n\tif (status == PGRES_COPY_IN && pgsql_result) {\n\t\tchar *buf;\n\t\tint command_failed = 0;\n\t\tsize_t line_len = 0;\n\n\t\tPQclear(pgsql_result);\n\t\twhile ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) {\n\t\t\tif (PQputCopyData(H->server, buf, line_len) != 1) {\n\t\t\t\tefree(buf);\n\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\t\tphp_stream_close(stream);\n\t\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\t\tRETURN_FALSE;\n\t\t\t}\n\t\t\tefree(buf);\n\t\t}\n\t\tphp_stream_close(stream);\n\n\t\tif (PQputCopyEnd(H->server, NULL) != 1) {\n\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\tRETURN_FALSE;\n\t\t}\n\n\t\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\t\tif (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {\n\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\t\t\tcommand_failed = 1;\n\t\t\t}\n\t\t\tPQclear(pgsql_result);\n\t\t}\n\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_BOOL(!command_failed);\n\t} else {\n\t\tphp_stream_close(stream);\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\tPQclear(pgsql_result);\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n}\n/* }}} */\n\n\n/* {{{ Returns true if the copy worked fine or false if error */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\n\tchar *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL;\n\tsize_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len;\n\tchar *query;\n\n\tPGresult *pgsql_result;\n\tExecStatusType status;\n\n\tphp_stream *stream;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"sp|sss!\",\n\t\t\t\t\t&table_name, &table_name_len, &filename, &filename_len,\n\t\t\t\t\t&pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tstream = php_stream_open_wrapper_ex(filename, \"wb\", 0, NULL, FG(default_context));\n\tif (!stream) {\n\t\tpdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, \"Unable to open the file for writing\");\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n\n\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\tPQclear(pgsql_result);\n\t}\n\n\t/* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */\n\tif (pg_fields) {\n\t\tspprintf(&query, 0, \"COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t} else {\n\t\tspprintf(&query, 0, \"COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t}\n\tpgsql_result = PQexec(H->server, query);\n\tefree(query);\n\n\tif (pgsql_result) {\n\t\tstatus = PQresultStatus(pgsql_result);\n\t} else {\n\t\tstatus = (ExecStatusType) PQstatus(H->server);\n\t}\n\n\tif (status == PGRES_COPY_OUT && pgsql_result) {\n\t\tPQclear(pgsql_result);\n\t\twhile (1) {\n\t\t\tchar *csv = NULL;\n\t\t\tint ret = PQgetCopyData(H->server, &csv, 0);\n\n\t\t\tif (ret == -1) {\n\t\t\t\tbreak; /* done */\n\t\t\t} else if (ret > 0) {\n\t\t\t\tif (php_stream_write(stream, csv, ret) != (size_t)ret) {\n\t\t\t\t\tpdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, \"Unable to write to file\");\n\t\t\t\t\tPQfreemem(csv);\n\t\t\t\t\tphp_stream_close(stream);\n\t\t\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\t\t\tRETURN_FALSE;\n\t\t\t\t} else {\n\t\t\t\t\tPQfreemem(csv);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\t\tphp_stream_close(stream);\n\t\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\t\tRETURN_FALSE;\n\t\t\t}\n\t\t}\n\t\tphp_stream_close(stream);\n\n\t\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\t\tPQclear(pgsql_result);\n\t\t}\n\t\tRETURN_TRUE;\n\t} else {\n\t\tphp_stream_close(stream);\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\tPQclear(pgsql_result);\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n}\n/* }}} */\n\n/* {{{ Returns true if the copy worked fine or false if error */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\n\tchar *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;\n\tsize_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;\n\tchar *query;\n\n\tPGresult *pgsql_result;\n\tExecStatusType status;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"s|sss!\",\n\t\t&table_name, &table_name_len,\n\t\t&pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\tPQclear(pgsql_result);\n\t}\n\n\t/* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */\n\tif (pg_fields) {\n\t\tspprintf(&query, 0, \"COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t} else {\n\t\tspprintf(&query, 0, \"COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t}\n\tpgsql_result = PQexec(H->server, query);\n\tefree(query);\n\n\tif (pgsql_result) {\n\t\tstatus = PQresultStatus(pgsql_result);\n\t} else {\n\t\tstatus = (ExecStatusType) PQstatus(H->server);\n\t}\n\n\tif (status == PGRES_COPY_OUT && pgsql_result) {\n\t\tPQclear(pgsql_result);\n                array_init(return_value);\n\n\t\twhile (1) {\n\t\t\tchar *csv = NULL;\n\t\t\tint ret = PQgetCopyData(H->server, &csv, 0);\n\t\t\tif (ret == -1) {\n\t\t\t\tbreak; /* copy done */\n\t\t\t} else if (ret > 0) {\n\t\t\t\tadd_next_index_stringl(return_value, csv, ret);\n\t\t\t\tPQfreemem(csv);\n\t\t\t} else {\n\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\t\tRETURN_FALSE;\n\t\t\t}\n\t\t}\n\n\t\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\t\tPQclear(pgsql_result);\n\t\t}\n\t} else {\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\tPQclear(pgsql_result);\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n}\n/* }}} */\n\n\n/* {{{ Creates a new large object, returning its identifier.  Must be called inside a transaction. */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\tOid lfd;\n\n\tZEND_PARSE_PARAMETERS_NONE();\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tlfd = lo_creat(H->server, INV_READ|INV_WRITE);\n\n\tif (lfd != InvalidOid) {\n\t\tzend_string *buf = strpprintf(0, ZEND_ULONG_FMT, (zend_long) lfd);\n\n\t\tRETURN_STR(buf);\n\t}\n\n\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\tPDO_HANDLE_DBH_ERR();\n\tRETURN_FALSE;\n}\n/* }}} */\n\n/* {{{ Opens an existing large object stream.  Must be called inside a transaction. */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\tOid oid;\n\tint lfd;\n\tchar *oidstr;\n\tsize_t oidstrlen;\n\tchar *modestr = \"rb\";\n\tsize_t modestrlen;\n\tint mode = INV_READ;\n\tchar *end_ptr;\n\n\tif (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), \"s|s\",\n\t\t\t\t&oidstr, &oidstrlen, &modestr, &modestrlen)) {\n\t\tRETURN_THROWS();\n\t}\n\n\toid = (Oid)strtoul(oidstr, &end_ptr, 10);\n\tif (oid == 0 && (errno == ERANGE || errno == EINVAL)) {\n\t\tRETURN_FALSE;\n\t}\n\n\tif (strpbrk(modestr, \"+w\")) {\n\t\tmode = INV_READ|INV_WRITE;\n\t}\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tlfd = lo_open(H->server, oid, mode);\n\n\tif (lfd >= 0) {\n\t\tphp_stream *stream = pdo_pgsql_create_lob_stream(ZEND_THIS, lfd, oid);\n\t\tif (stream) {\n\t\t\tphp_stream_to_zval(stream, return_value);\n\t\t\treturn;\n\t\t}\n\t} else {\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t}\n\n\tPDO_HANDLE_DBH_ERR();\n\tRETURN_FALSE;\n}\n/* }}} */\n\n/* {{{ Deletes the large object identified by oid.  Must be called inside a transaction. */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\tOid oid;\n\tchar *oidstr, *end_ptr;\n\tsize_t oidlen;\n\n\tif (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), \"s\",\n\t\t\t\t&oidstr, &oidlen)) {\n\t\tRETURN_THROWS();\n\t}\n\n\toid = (Oid)strtoul(oidstr, &end_ptr, 10);\n\tif (oid == 0 && (errno == ERANGE || errno == EINVAL)) {\n\t\tRETURN_FALSE;\n\t}\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tif (1 == lo_unlink(H->server, oid)) {\n\t\tRETURN_TRUE;\n\t}\n\n\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\tPDO_HANDLE_DBH_ERR();\n\tRETURN_FALSE;\n}\n/* }}} */\n\n/* {{{ Get asynchronous notification */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\tzend_long result_type = PDO_FETCH_USE_DEFAULT;\n\tzend_long ms_timeout = 0;\n\tPGnotify *pgsql_notify;\n\n\tif (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), \"|ll\",\n\t\t\t\t&result_type, &ms_timeout)) {\n\t\tRETURN_THROWS();\n\t}\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\n\tif (result_type == PDO_FETCH_USE_DEFAULT) {\n\t\tresult_type = dbh->default_fetch_type;\n\t}\n\n\tif (result_type != PDO_FETCH_BOTH && result_type != PDO_FETCH_ASSOC && result_type != PDO_FETCH_NUM) {\n\t\tzend_argument_value_error(1, \"must be one of PDO::FETCH_BOTH, PDO::FETCH_ASSOC, or PDO::FETCH_NUM\");\n\t\tRETURN_THROWS();\n\t}\n\n\tif (ms_timeout < 0) {\n\t\tzend_argument_value_error(2, \"must be greater than or equal to 0\");\n\t\tRETURN_THROWS();\n#ifdef ZEND_ENABLE_ZVAL_LONG64\n\t} else if (ms_timeout > INT_MAX) {\n\t\tphp_error_docref(NULL, E_WARNING, \"Timeout was shrunk to %d\", INT_MAX);\n\t\tms_timeout = INT_MAX;\n#endif\n\t}\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tif (!PQconsumeInput(H->server)) {\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n\tpgsql_notify = PQnotifies(H->server);\n\n\tif (ms_timeout && !pgsql_notify) {\n\t\tphp_pollfd_for_ms(PQsocket(H->server), PHP_POLLREADABLE, (int)ms_timeout);\n\n\t\tif (!PQconsumeInput(H->server)) {\n\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\tRETURN_FALSE;\n\t\t}\n\t\tpgsql_notify = PQnotifies(H->server);\n\t}\n\n\tif (!pgsql_notify) {\n\t\tRETURN_FALSE;\n\t}\n\n\tarray_init(return_value);\n\tif (result_type == PDO_FETCH_NUM || result_type == PDO_FETCH_BOTH) {\n\t\tadd_index_string(return_value, 0, pgsql_notify->relname);\n\t\tadd_index_long(return_value, 1, pgsql_notify->be_pid);\n\t\tif (pgsql_notify->extra && pgsql_notify->extra[0]) {\n\t\t\tadd_index_string(return_value, 2, pgsql_notify->extra);\n\t\t}\n\t}\n\tif (result_type == PDO_FETCH_ASSOC || result_type == PDO_FETCH_BOTH) {\n\t\tadd_assoc_string(return_value, \"message\", pgsql_notify->relname);\n\t\tadd_assoc_long(return_value, \"pid\", pgsql_notify->be_pid);\n\t\tif (pgsql_notify->extra && pgsql_notify->extra[0]) {\n\t\t\tadd_assoc_string(return_value, \"payload\", pgsql_notify->extra);\n\t\t}\n\t}\n\n\tPQfreemem(pgsql_notify);\n}\n/* }}} */\n\n/* {{{ Get backend(server) pid */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\n\tZEND_PARSE_PARAMETERS_NONE();\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tRETURN_LONG(PQbackendPID(H->server));\n}\n/* }}} */\n\nstatic const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind)\n{\n\tswitch (kind) {\n\t\tcase PDO_DBH_DRIVER_METHOD_KIND_DBH:\n\t\t\treturn class_PDO_PGSql_Ext_methods;\n\t\tdefault:\n\t\t\treturn NULL;\n\t}\n}\n\nstatic bool pdo_pgsql_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)\n{\n\tbool bval;\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tswitch (attr) {\n\t\tcase PDO_ATTR_EMULATE_PREPARES:\n\t\t\tif (!pdo_get_bool_param(&bval, val)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tH->emulate_prepares = bval;\n\t\t\treturn true;\n\t\tcase PDO_PGSQL_ATTR_DISABLE_PREPARES:\n\t\t\tif (!pdo_get_bool_param(&bval, val)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tH->disable_prepares = bval;\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\nstatic const struct pdo_dbh_methods pgsql_methods = {\n\tpgsql_handle_closer,\n\tpgsql_handle_preparer,\n\tpgsql_handle_doer,\n\tpgsql_handle_quoter,\n\tpgsql_handle_begin,\n\tpgsql_handle_commit,\n\tpgsql_handle_rollback,\n\tpdo_pgsql_set_attr,\n\tpdo_pgsql_last_insert_id,\n\tpdo_pgsql_fetch_error_func,\n\tpdo_pgsql_get_attribute,\n\tpdo_pgsql_check_liveness,\t/* check_liveness */\n\tpdo_pgsql_get_driver_methods,  /* get_driver_methods */\n\tNULL,\n\tpgsql_handle_in_transaction,\n\tNULL /* get_gc */\n};\n\nstatic int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */\n{\n\tpdo_pgsql_db_handle *H;\n\tint ret = 0;\n\tchar *conn_str, *p, *e;\n\tzend_string *tmp_user, *tmp_pass;\n\tzend_long connect_timeout = 30;\n\n\tH = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent);\n\tdbh->driver_data = H;\n\n\tdbh->skip_param_evt =\n\t\t1 << PDO_PARAM_EVT_EXEC_POST |\n\t\t1 << PDO_PARAM_EVT_FETCH_PRE |\n\t\t1 << PDO_PARAM_EVT_FETCH_POST;\n\n\tH->einfo.errcode = 0;\n\tH->einfo.errmsg = NULL;\n\n\t/* PostgreSQL wants params in the connect string to be separated by spaces,\n\t * if the PDO standard semicolons are used, we convert them to spaces\n\t */\n\te = (char *) dbh->data_source + strlen(dbh->data_source);\n\tp = (char *) dbh->data_source;\n\twhile ((p = memchr(p, ';', (e - p)))) {\n\t\t*p = ' ';\n\t}\n\n\tif (driver_options) {\n\t\tconnect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30);\n\t}\n\n\t/* escape username and password, if provided */\n\ttmp_user = _pdo_pgsql_escape_credentials(dbh->username);\n\ttmp_pass = _pdo_pgsql_escape_credentials(dbh->password);\n\n\t/* support both full connection string & connection string + login and/or password */\n\tif (tmp_user && tmp_pass) {\n\t\tspprintf(&conn_str, 0, \"%s user='%s' password='%s' connect_timeout=\" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), ZSTR_VAL(tmp_pass), connect_timeout);\n\t} else if (tmp_user) {\n\t\tspprintf(&conn_str, 0, \"%s user='%s' connect_timeout=\" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), connect_timeout);\n\t} else if (tmp_pass) {\n\t\tspprintf(&conn_str, 0, \"%s password='%s' connect_timeout=\" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_pass), connect_timeout);\n\t} else {\n\t\tspprintf(&conn_str, 0, \"%s connect_timeout=\" ZEND_LONG_FMT, (char *) dbh->data_source, connect_timeout);\n\t}\n\n\tH->server = PQconnectdb(conn_str);\n\tH->lob_streams = (HashTable *) pemalloc(sizeof(HashTable), dbh->is_persistent);\n\tzend_hash_init(H->lob_streams, 0, NULL, NULL, 1);\n\n\tif (tmp_user) {\n\t\tzend_string_release_ex(tmp_user, 0);\n\t}\n\tif (tmp_pass) {\n\t\tzend_string_release_ex(tmp_pass, 0);\n\t}\n\n\tefree(conn_str);\n\n\tif (PQstatus(H->server) != CONNECTION_OK) {\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE);\n\t\tgoto cleanup;\n\t}\n\n\tPQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh);\n\n\tH->attached = 1;\n\tH->pgoid = -1;\n\n\tdbh->methods = &pgsql_methods;\n\tdbh->alloc_own_columns = 1;\n\tdbh->max_escaped_char_length = 2;\n\n\tret = 1;\n\ncleanup:\n\tdbh->methods = &pgsql_methods;\n\tif (!ret) {\n\t\tpgsql_handle_closer(dbh);\n\t}\n\n\treturn ret;\n}\n/* }}} */\n\nconst pdo_driver_t swoole_pdo_pgsql_driver = {\n\tPDO_DRIVER_HEADER(pgsql),\n\tpdo_pgsql_handle_factory\n};\n#endif\n"
  },
  {
    "path": "thirdparty/php82/pdo_pgsql/pgsql_driver_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 9bb79af98dbb7c171fd9533aeabece4937a06cd2 */\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, rows, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, \"\\\"\\\\t\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, \"\\\"\\\\\\\\\\\\\\\\N\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, \"\\\"\\\\t\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, \"\\\"\\\\\\\\\\\\\\\\N\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, \"\\\"\\\\t\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, \"\\\"\\\\\\\\\\\\\\\\N\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, 0, 0, 1)\n\tZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 0, \"\\\"rb\\\"\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fetchMode, IS_LONG, 0, \"PDO::FETCH_DEFAULT\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeoutMilliseconds, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, 0, 0, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\n\nZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlGetNotify);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlGetPid);\n\n\nstatic const zend_function_entry class_PDO_PGSql_Ext_methods[] = {\n\tZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlCopyToArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlCopyToFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlLOBCreate, arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlLOBOpen, arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlLOBUnlink, arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlGetNotify, arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlGetPid, arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, ZEND_ACC_PUBLIC)\n\tZEND_FE_END\n};\n"
  },
  {
    "path": "thirdparty/php82/pdo_pgsql/pgsql_statement.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Authors: Edin Kadribasic <edink@emini.dk>                            |\n  |          Ilia Alshanestsky <ilia@prohost.org>                        |\n  |          Wez Furlong <wez@php.net>                                   |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_PGSQL_HOOK\n#include \"php_swoole_pgsql.h\"\n\n#if PHP_VERSION_ID >= 80200 && PHP_VERSION_ID < 80300\n\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"pdo/php_pdo.h\"\n#include \"pdo/php_pdo_driver.h\"\n#include \"php_pdo_pgsql_int.h\"\n#ifdef HAVE_NETINET_IN_H\n#include <netinet/in.h>\n#endif\n\n/* from postgresql/src/include/catalog/pg_type.h */\n#define BOOLLABEL   \"bool\"\n#define BOOLOID     16\n#define BYTEALABEL  \"bytea\"\n#define BYTEAOID    17\n#define DATELABEL   \"date\"\n#define DATEOID     1082\n#define INT2LABEL   \"int2\"\n#define INT2OID     21\n#define INT4LABEL   \"int4\"\n#define INT4OID     23\n#define INT8LABEL   \"int8\"\n#define INT8OID     20\n#define OIDOID      26\n#define TEXTLABEL   \"text\"\n#define TEXTOID     25\n#define TIMESTAMPLABEL \"timestamp\"\n#define TIMESTAMPOID   1114\n#define VARCHARLABEL \"varchar\"\n#define VARCHAROID   1043\n\n\n\nstatic int pgsql_stmt_dtor(pdo_stmt_t *stmt)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\tbool server_obj_usable = !Z_ISUNDEF(stmt->database_object_handle)\n\t\t&& IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)])\n\t\t&& !(OBJ_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED);\n\n\tif (S->result) {\n\t\t/* free the resource */\n\t\tPQclear(S->result);\n\t\tS->result = NULL;\n\t}\n\n\tif (S->stmt_name) {\n\t\tif (S->is_prepared && server_obj_usable) {\n\t\t\tpdo_pgsql_db_handle *H = S->H;\n\t\t\tchar *q = NULL;\n\t\t\tPGresult *res;\n\n\t\t\tspprintf(&q, 0, \"DEALLOCATE %s\", S->stmt_name);\n\t\t\tres = PQexec(H->server, q);\n\t\t\tefree(q);\n\t\t\tif (res) {\n\t\t\t\tPQclear(res);\n\t\t\t}\n\t\t}\n\t\tefree(S->stmt_name);\n\t\tS->stmt_name = NULL;\n\t}\n\tif (S->param_lengths) {\n\t\tefree(S->param_lengths);\n\t\tS->param_lengths = NULL;\n\t}\n\tif (S->param_values) {\n\t\tefree(S->param_values);\n\t\tS->param_values = NULL;\n\t}\n\tif (S->param_formats) {\n\t\tefree(S->param_formats);\n\t\tS->param_formats = NULL;\n\t}\n\tif (S->param_types) {\n\t\tefree(S->param_types);\n\t\tS->param_types = NULL;\n\t}\n\tif (S->query) {\n\t\tzend_string_release(S->query);\n\t\tS->query = NULL;\n\t}\n\n\tif (S->cursor_name) {\n\t\tif (server_obj_usable) {\n\t\t\tpdo_pgsql_db_handle *H = S->H;\n\t\t\tchar *q = NULL;\n\t\t\tPGresult *res;\n\n\t\t\tspprintf(&q, 0, \"CLOSE %s\", S->cursor_name);\n\t\t\tres = PQexec(H->server, q);\n\t\t\tefree(q);\n\t\t\tif (res) PQclear(res);\n\t\t}\n\t\tefree(S->cursor_name);\n\t\tS->cursor_name = NULL;\n\t}\n\n\tif(S->cols) {\n\t\tefree(S->cols);\n\t\tS->cols = NULL;\n\t}\n\tefree(S);\n\tstmt->driver_data = NULL;\n\treturn 1;\n}\n\nstatic int pgsql_stmt_execute(pdo_stmt_t *stmt)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\tpdo_pgsql_db_handle *H = S->H;\n\tExecStatusType status;\n\n\tbool in_trans = stmt->dbh->methods->in_transaction(stmt->dbh);\n\n\t/* ensure that we free any previous unfetched results */\n\tif(S->result) {\n\t\tPQclear(S->result);\n\t\tS->result = NULL;\n\t}\n\n\tS->current_row = 0;\n\n\tif (S->cursor_name) {\n\t\tchar *q = NULL;\n\n\t\tif (S->is_prepared) {\n\t\t\tspprintf(&q, 0, \"CLOSE %s\", S->cursor_name);\n\t\t\tPQclear(PQexec(H->server, q));\n\t\t\tefree(q);\n\t\t}\n\n\t\tspprintf(&q, 0, \"DECLARE %s SCROLL CURSOR WITH HOLD FOR %s\", S->cursor_name, ZSTR_VAL(stmt->active_query_string));\n\t\tS->result = PQexec(H->server, q);\n\t\tefree(q);\n\n\t\t/* check if declare failed */\n\t\tstatus = PQresultStatus(S->result);\n\t\tif (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {\n\t\t\tpdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));\n\t\t\treturn 0;\n\t\t}\n\t\tPQclear(S->result);\n\n\t\t/* the cursor was declared correctly */\n\t\tS->is_prepared = 1;\n\n\t\t/* fetch to be able to get the number of tuples later, but don't advance the cursor pointer */\n\t\tspprintf(&q, 0, \"FETCH FORWARD 0 FROM %s\", S->cursor_name);\n\t\tS->result = PQexec(H->server, q);\n\t\tefree(q);\n\t} else if (S->stmt_name) {\n\t\t/* using a prepared statement */\n\n\t\tif (!S->is_prepared) {\nstmt_retry:\n\t\t\t/* we deferred the prepare until now, because we didn't\n\t\t\t * know anything about the parameter types; now we do */\n\t\t\tS->result = PQprepare(H->server, S->stmt_name, ZSTR_VAL(S->query),\n\t\t\t\t\t\tstmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0,\n\t\t\t\t\t\tS->param_types);\n\t\t\tstatus = PQresultStatus(S->result);\n\t\t\tswitch (status) {\n\t\t\t\tcase PGRES_COMMAND_OK:\n\t\t\t\tcase PGRES_TUPLES_OK:\n\t\t\t\t\t/* it worked */\n\t\t\t\t\tS->is_prepared = 1;\n\t\t\t\t\tPQclear(S->result);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: {\n\t\t\t\t\tchar *sqlstate = pdo_pgsql_sqlstate(S->result);\n\t\t\t\t\t/* 42P05 means that the prepared statement already existed. this can happen if you use\n\t\t\t\t\t * a connection pooling software line pgpool which doesn't close the db-connection once\n\t\t\t\t\t * php disconnects. if php dies (no chance to run RSHUTDOWN) during execution it has no\n\t\t\t\t\t * chance to DEALLOCATE the prepared statements it has created. so, if we hit a 42P05 we\n\t\t\t\t\t * deallocate it and retry ONCE (thies 2005.12.15)\n\t\t\t\t\t */\n\t\t\t\t\tif (sqlstate && !strcmp(sqlstate, \"42P05\")) {\n\t\t\t\t\t\tchar buf[100]; /* stmt_name == \"pdo_crsr_%08x\" */\n\t\t\t\t\t\tPGresult *res;\n\t\t\t\t\t\tsnprintf(buf, sizeof(buf), \"DEALLOCATE %s\", S->stmt_name);\n\t\t\t\t\t\tres = PQexec(H->server, buf);\n\t\t\t\t\t\tif (res) {\n\t\t\t\t\t\t\tPQclear(res);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tgoto stmt_retry;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpdo_pgsql_error_stmt(stmt, status, sqlstate);\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tS->result = PQexecPrepared(H->server, S->stmt_name,\n\t\t\t\tstmt->bound_params ?\n\t\t\t\t\tzend_hash_num_elements(stmt->bound_params) :\n\t\t\t\t\t0,\n\t\t\t\t(const char**)S->param_values,\n\t\t\t\tS->param_lengths,\n\t\t\t\tS->param_formats,\n\t\t\t\t0);\n\t} else if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED) {\n\t\t/* execute query with parameters */\n\t\tS->result = PQexecParams(H->server, ZSTR_VAL(S->query),\n\t\t\t\tstmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0,\n\t\t\t\tS->param_types,\n\t\t\t\t(const char**)S->param_values,\n\t\t\t\tS->param_lengths,\n\t\t\t\tS->param_formats,\n\t\t\t\t0);\n\t} else {\n\t\t/* execute plain query (with embedded parameters) */\n\t\tS->result = PQexec(H->server, ZSTR_VAL(stmt->active_query_string));\n\t}\n\tstatus = PQresultStatus(S->result);\n\n\tif (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {\n\t\tpdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));\n\t\treturn 0;\n\t}\n\n\tstmt->column_count = (int) PQnfields(S->result);\n\tif (S->cols == NULL) {\n\t\tS->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column));\n\t}\n\n\tif (status == PGRES_COMMAND_OK) {\n\t\tstmt->row_count = ZEND_ATOL(PQcmdTuples(S->result));\n\t\tH->pgoid = PQoidValue(S->result);\n\t} else {\n\t\tstmt->row_count = (zend_long)PQntuples(S->result);\n\t}\n\n\tif (in_trans && !stmt->dbh->methods->in_transaction(stmt->dbh)) {\n\t\tpdo_pgsql_close_lob_streams(stmt->dbh);\n\t}\n\n\treturn 1;\n}\n\nstatic int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,\n\t\tenum pdo_param_event event_type)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\n\tif (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED && param->is_param) {\n\t\tswitch (event_type) {\n\t\t\tcase PDO_PARAM_EVT_FREE:\n\t\t\t\tif (param->driver_data) {\n\t\t\t\t\tefree(param->driver_data);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase PDO_PARAM_EVT_NORMALIZE:\n\t\t\t\t/* decode name from $1, $2 into 0, 1 etc. */\n\t\t\t\tif (param->name) {\n\t\t\t\t\tif (ZSTR_VAL(param->name)[0] == '$') {\n\t\t\t\t\t\tparam->paramno = ZEND_ATOL(ZSTR_VAL(param->name) + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t/* resolve parameter name to rewritten name */\n\t\t\t\t\t\tzend_string *namevar;\n\n\t\t\t\t\t\tif (stmt->bound_param_map && (namevar = zend_hash_find_ptr(stmt->bound_param_map,\n\t\t\t\t\t\t\t\tparam->name)) != NULL) {\n\t\t\t\t\t\t\tparam->paramno = ZEND_ATOL(ZSTR_VAL(namevar) + 1);\n\t\t\t\t\t\t\tparam->paramno--;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tpdo_pgsql_error_stmt_msg(stmt, 0, \"HY093\", ZSTR_VAL(param->name));\n\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase PDO_PARAM_EVT_ALLOC:\n\t\t\t\tif (!stmt->bound_param_map) {\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tif (!zend_hash_index_exists(stmt->bound_param_map, param->paramno)) {\n\t\t\t\t\tpdo_pgsql_error_stmt_msg(stmt, 0, \"HY093\", \"parameter was not defined\");\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tZEND_FALLTHROUGH;\n\t\t\tcase PDO_PARAM_EVT_EXEC_POST:\n\t\t\tcase PDO_PARAM_EVT_FETCH_PRE:\n\t\t\tcase PDO_PARAM_EVT_FETCH_POST:\n\t\t\t\t/* work is handled by EVT_NORMALIZE */\n\t\t\t\treturn 1;\n\n\t\t\tcase PDO_PARAM_EVT_EXEC_PRE:\n\t\t\t\tif (!stmt->bound_param_map) {\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tif (!S->param_values) {\n\t\t\t\t\tS->param_values = ecalloc(\n\t\t\t\t\t\t\tzend_hash_num_elements(stmt->bound_param_map),\n\t\t\t\t\t\t\tsizeof(char*));\n\t\t\t\t\tS->param_lengths = ecalloc(\n\t\t\t\t\t\t\tzend_hash_num_elements(stmt->bound_param_map),\n\t\t\t\t\t\t\tsizeof(int));\n\t\t\t\t\tS->param_formats = ecalloc(\n\t\t\t\t\t\t\tzend_hash_num_elements(stmt->bound_param_map),\n\t\t\t\t\t\t\tsizeof(int));\n\t\t\t\t\tS->param_types = ecalloc(\n\t\t\t\t\t\t\tzend_hash_num_elements(stmt->bound_param_map),\n\t\t\t\t\t\t\tsizeof(Oid));\n\t\t\t\t}\n\t\t\t\tif (param->paramno >= 0) {\n\t\t\t\t\tzval *parameter;\n\n\t\t\t\t\t/*\n\t\t\t\t\tif (param->paramno >= zend_hash_num_elements(stmt->bound_params)) {\n\t\t\t\t\t\tpdo_raise_impl_error(stmt->dbh, stmt, \"HY093\", \"parameter was not defined\");\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\t}\n\t\t\t\t\t*/\n\n\t\t\t\t\tif (Z_ISREF(param->parameter)) {\n\t\t\t\t\t\tparameter = Z_REFVAL(param->parameter);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tparameter = &param->parameter;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB &&\n\t\t\t\t\t\t\tZ_TYPE_P(parameter) == IS_RESOURCE) {\n\t\t\t\t\t\tphp_stream *stm;\n\t\t\t\t\t\tphp_stream_from_zval_no_verify(stm, parameter);\n\t\t\t\t\t\tif (stm) {\n\t\t\t\t\t\t\tif (php_stream_is(stm, &pdo_pgsql_lob_stream_ops)) {\n\t\t\t\t\t\t\t\tstruct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stm->abstract;\n\t\t\t\t\t\t\t\tpdo_pgsql_bound_param *P = param->driver_data;\n\n\t\t\t\t\t\t\t\tif (P == NULL) {\n\t\t\t\t\t\t\t\t\tP = ecalloc(1, sizeof(*P));\n\t\t\t\t\t\t\t\t\tparam->driver_data = P;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tP->oid = htonl(self->oid);\n\t\t\t\t\t\t\t\tS->param_values[param->paramno] = (char*)&P->oid;\n\t\t\t\t\t\t\t\tS->param_lengths[param->paramno] = sizeof(P->oid);\n\t\t\t\t\t\t\t\tS->param_formats[param->paramno] = 1;\n\t\t\t\t\t\t\t\tS->param_types[param->paramno] = OIDOID;\n\t\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tzend_string *str = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0);\n\t\t\t\t\t\t\t\tif (str != NULL) {\n\t\t\t\t\t\t\t\t\tZVAL_STR(parameter, str);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tZVAL_EMPTY_STRING(parameter);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t/* expected a stream resource */\n\t\t\t\t\t\t\tpdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, \"HY105\");\n\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL ||\n\t\t\t\t\t\t\tZ_TYPE_P(parameter) == IS_NULL) {\n\t\t\t\t\t\tS->param_values[param->paramno] = NULL;\n\t\t\t\t\t\tS->param_lengths[param->paramno] = 0;\n\t\t\t\t\t} else if (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE) {\n\t\t\t\t\t\tS->param_values[param->paramno] = Z_TYPE_P(parameter) == IS_TRUE ? \"t\" : \"f\";\n\t\t\t\t\t\tS->param_lengths[param->paramno] = 1;\n\t\t\t\t\t\tS->param_formats[param->paramno] = 0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconvert_to_string(parameter);\n\t\t\t\t\t\tS->param_values[param->paramno] = Z_STRVAL_P(parameter);\n\t\t\t\t\t\tS->param_lengths[param->paramno] = Z_STRLEN_P(parameter);\n\t\t\t\t\t\tS->param_formats[param->paramno] = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {\n\t\t\t\t\t\tS->param_types[param->paramno] = 0;\n\t\t\t\t\t\tS->param_formats[param->paramno] = 1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tS->param_types[param->paramno] = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t} else if (param->is_param && event_type == PDO_PARAM_EVT_NORMALIZE) {\n\t\t/* We need to manually convert to a pg native boolean value */\n\t\tif (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL &&\n\t\t\t((param->param_type & PDO_PARAM_INPUT_OUTPUT) != PDO_PARAM_INPUT_OUTPUT)) {\n\t\t\tconst char *s = zend_is_true(&param->parameter) ? \"t\" : \"f\";\n\t\t\tparam->param_type = PDO_PARAM_STR;\n\t\t\tzval_ptr_dtor(&param->parameter);\n\t\t\tZVAL_STRINGL(&param->parameter, s, 1);\n\t\t}\n\t}\n\treturn 1;\n}\n\nstatic int pgsql_stmt_fetch(pdo_stmt_t *stmt,\n\tenum pdo_fetch_orientation ori, zend_long offset)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\n\tif (S->cursor_name) {\n\t\tchar *ori_str = NULL;\n\t\tchar *q = NULL;\n\t\tExecStatusType status;\n\n\t\tswitch (ori) {\n\t\t\tcase PDO_FETCH_ORI_NEXT: \tspprintf(&ori_str, 0, \"NEXT\"); break;\n\t\t\tcase PDO_FETCH_ORI_PRIOR:\tspprintf(&ori_str, 0, \"BACKWARD\"); break;\n\t\t\tcase PDO_FETCH_ORI_FIRST:\tspprintf(&ori_str, 0, \"FIRST\"); break;\n\t\t\tcase PDO_FETCH_ORI_LAST:\tspprintf(&ori_str, 0, \"LAST\"); break;\n\t\t\tcase PDO_FETCH_ORI_ABS:\t\tspprintf(&ori_str, 0, \"ABSOLUTE \" ZEND_LONG_FMT, offset); break;\n\t\t\tcase PDO_FETCH_ORI_REL:\t\tspprintf(&ori_str, 0, \"RELATIVE \" ZEND_LONG_FMT, offset); break;\n\t\t\tdefault:\n\t\t\t\treturn 0;\n\t\t}\n\n\t\tif(S->result) {\n\t\t\tPQclear(S->result);\n\t\t\tS->result = NULL;\n\t\t}\n\n\t\tspprintf(&q, 0, \"FETCH %s FROM %s\", ori_str, S->cursor_name);\n\t\tefree(ori_str);\n\t\tS->result = PQexec(S->H->server, q);\n\t\tefree(q);\n\t\tstatus = PQresultStatus(S->result);\n\n\t\tif (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {\n\t\t\tpdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));\n\t\t\treturn 0;\n\t\t}\n\n\t\tif (PQntuples(S->result)) {\n\t\t\tS->current_row = 1;\n\t\t\treturn 1;\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t} else {\n\t\tif (S->current_row < stmt->row_count) {\n\t\t\tS->current_row++;\n\t\t\treturn 1;\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t}\n}\n\nstatic int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\tstruct pdo_column_data *cols = stmt->columns;\n\tchar *str;\n\n\tif (!S->result) {\n\t\treturn 0;\n\t}\n\n\tstr = PQfname(S->result, colno);\n\tcols[colno].name = zend_string_init(str, strlen(str), 0);\n\tcols[colno].maxlen = PQfsize(S->result, colno);\n\tcols[colno].precision = PQfmod(S->result, colno);\n\tS->cols[colno].pgsql_type = PQftype(S->result, colno);\n\n\treturn 1;\n}\n\nstatic int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\tif (!S->result) {\n\t\treturn 0;\n\t}\n\n\t/* We have already increased count by 1 in pgsql_stmt_fetch() */\n\tif (PQgetisnull(S->result, S->current_row - 1, colno)) { /* Check if we got NULL */\n\t\tZVAL_NULL(result);\n\t} else {\n\t\tchar *ptr = PQgetvalue(S->result, S->current_row - 1, colno);\n\t\tsize_t len = PQgetlength(S->result, S->current_row - 1, colno);\n\n\t\tswitch (S->cols[colno].pgsql_type) {\n\t\t\tcase BOOLOID:\n\t\t\t\tZVAL_BOOL(result, *ptr == 't');\n\t\t\t\tbreak;\n\n\t\t\tcase INT2OID:\n\t\t\tcase INT4OID:\n#if SIZEOF_ZEND_LONG >= 8\n\t\t\tcase INT8OID:\n#endif\n\t\t\t\tZVAL_LONG(result, ZEND_ATOL(ptr));\n\t\t\t\tbreak;\n\n\t\t\tcase OIDOID: {\n\t\t\t\tchar *end_ptr;\n\t\t\t\tOid oid = (Oid)strtoul(ptr, &end_ptr, 10);\n\t\t\t\tif (type && *type == PDO_PARAM_LOB) {\n\t\t\t\t\t/* If column was bound as LOB, return a stream. */\n\t\t\t\t\tint loid = lo_open(S->H->server, oid, INV_READ);\n\t\t\t\t\tif (loid >= 0) {\n\t\t\t\t\t\tphp_stream *stream = pdo_pgsql_create_lob_stream(&stmt->database_object_handle, loid, oid);\n\t\t\t\t\t\tif (stream) {\n\t\t\t\t\t\t\tphp_stream_to_zval(stream, result);\n\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn 0;\n\t\t\t\t} else {\n\t\t\t\t\t/* Otherwise return OID as integer. */\n\t\t\t\t\tZVAL_LONG(result, oid);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase BYTEAOID: {\n\t\t\t\tsize_t tmp_len;\n\t\t\t\tchar *tmp_ptr = (char *)PQunescapeBytea((unsigned char *) ptr, &tmp_len);\n\t\t\t\tif (!tmp_ptr) {\n\t\t\t\t\t/* PQunescapeBytea returned an error */\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\n\t\t\t\tzend_string *str = zend_string_init(tmp_ptr, tmp_len, 0);\n\t\t\t\tphp_stream *stream = php_stream_memory_open(TEMP_STREAM_READONLY, str);\n\t\t\t\tphp_stream_to_zval(stream, result);\n\t\t\t\tzend_string_release(str);\n\t\t\t\tPQfreemem(tmp_ptr);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tZVAL_STRINGL_FAST(result, ptr, len);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn 1;\n}\n\nstatic zend_always_inline char * pdo_pgsql_translate_oid_to_table(Oid oid, PGconn *conn)\n{\n\tchar *table_name = NULL;\n\tPGresult *tmp_res;\n\tchar *querystr = NULL;\n\n\tspprintf(&querystr, 0, \"SELECT RELNAME FROM PG_CLASS WHERE OID=%d\", oid);\n\n\tif ((tmp_res = PQexec(conn, querystr)) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) {\n\t\tif (tmp_res) {\n\t\t\tPQclear(tmp_res);\n\t\t}\n\t\tefree(querystr);\n\t\treturn 0;\n\t}\n\tefree(querystr);\n\n\tif (1 == PQgetisnull(tmp_res, 0, 0) || (table_name = PQgetvalue(tmp_res, 0, 0)) == NULL) {\n\t\tPQclear(tmp_res);\n\t\treturn 0;\n\t}\n\n\ttable_name = estrdup(table_name);\n\n\tPQclear(tmp_res);\n\treturn table_name;\n}\n\nstatic int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\tPGresult *res;\n\tchar *q=NULL;\n\tExecStatusType status;\n\tOid table_oid;\n\tchar *table_name=NULL;\n\n\tif (!S->result) {\n\t\treturn FAILURE;\n\t}\n\n\tif (colno >= stmt->column_count) {\n\t\treturn FAILURE;\n\t}\n\n\tarray_init(return_value);\n\tadd_assoc_long(return_value, \"pgsql:oid\", S->cols[colno].pgsql_type);\n\n\ttable_oid = PQftable(S->result, colno);\n\tadd_assoc_long(return_value, \"pgsql:table_oid\", table_oid);\n\ttable_name = pdo_pgsql_translate_oid_to_table(table_oid, S->H->server);\n\tif (table_name) {\n\t\tadd_assoc_string(return_value, \"table\", table_name);\n\t\tefree(table_name);\n\t}\n\n\tswitch (S->cols[colno].pgsql_type) {\n\t\tcase BOOLOID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", BOOLLABEL);\n\t\t\tbreak;\n\t\tcase BYTEAOID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", BYTEALABEL);\n\t\t\tbreak;\n\t\tcase INT8OID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", INT8LABEL);\n\t\t\tbreak;\n\t\tcase INT2OID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", INT2LABEL);\n\t\t\tbreak;\n\t\tcase INT4OID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", INT4LABEL);\n\t\t\tbreak;\n\t\tcase TEXTOID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", TEXTLABEL);\n\t\t\tbreak;\n\t\tcase VARCHAROID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", VARCHARLABEL);\n\t\t\tbreak;\n\t\tcase DATEOID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", DATELABEL);\n\t\t\tbreak;\n\t\tcase TIMESTAMPOID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", TIMESTAMPLABEL);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t/* Fetch metadata from Postgres system catalogue */\n\t\t\tspprintf(&q, 0, \"SELECT TYPNAME FROM PG_TYPE WHERE OID=%u\", S->cols[colno].pgsql_type);\n\t\t\tres = PQexec(S->H->server, q);\n\t\t\tefree(q);\n\t\t\tstatus = PQresultStatus(res);\n\t\t\tif (status == PGRES_TUPLES_OK && 1 == PQntuples(res)) {\n\t\t\t\tadd_assoc_string(return_value, \"native_type\", PQgetvalue(res, 0, 0));\n\t\t\t}\n\t\t\tPQclear(res);\n\t}\n\n\tenum pdo_param_type param_type;\n\tswitch (S->cols[colno].pgsql_type) {\n\t\tcase BOOLOID:\n\t\t\tparam_type = PDO_PARAM_BOOL;\n\t\t\tbreak;\n\t\tcase INT2OID:\n\t\tcase INT4OID:\n\t\tcase INT8OID:\n\t\t\tparam_type = PDO_PARAM_INT;\n\t\t\tbreak;\n\t\tcase OIDOID:\n\t\tcase BYTEAOID:\n\t\t\tparam_type = PDO_PARAM_LOB;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tparam_type = PDO_PARAM_STR;\n\t}\n\tadd_assoc_long(return_value, \"pdo_type\", param_type);\n\n\treturn 1;\n}\n\nstatic int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt)\n{\n\treturn 1;\n}\n\nconst struct pdo_stmt_methods swoole_pgsql_stmt_methods = {\n\tpgsql_stmt_dtor,\n\tpgsql_stmt_execute,\n\tpgsql_stmt_fetch,\n\tpgsql_stmt_describe,\n\tpgsql_stmt_get_col,\n\tpgsql_stmt_param_hook,\n\tNULL, /* set_attr */\n\tNULL, /* get_attr */\n\tpgsql_stmt_get_column_meta,\n\tNULL,  /* next_rowset */\n\tpdo_pgsql_stmt_cursor_closer\n};\n#endif\n"
  },
  {
    "path": "thirdparty/php82/pdo_pgsql/php_pdo_pgsql_int.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Authors: Edin Kadribasic <edink@emini.dk>                            |\n  |          Ilia Alshanestsky <ilia@prohost.org>                        |\n  |          Wez Furlong <wez@php.net>                                   |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef PHP_PDO_PGSQL_INT_H\n#define PHP_PDO_PGSQL_INT_H\n\n#include <libpq-fe.h>\n#include <libpq/libpq-fs.h>\n#include <php.h>\n\n#define PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE \"08006\"\n\ntypedef struct {\n\tconst char *file;\n\tint line;\n\tunsigned int errcode;\n\tchar *errmsg;\n} pdo_pgsql_error_info;\n\n/* stuff we use in a pgsql database handle */\ntypedef struct {\n\tPGconn\t\t*server;\n\tunsigned \tattached:1;\n\tunsigned \t_reserved:31;\n\tpdo_pgsql_error_info\teinfo;\n\tOid \t\tpgoid;\n\tunsigned int\tstmt_counter;\n\t/* The following two variables have the same purpose. Unfortunately we need\n\t   to keep track of two different attributes having the same effect. */\n\tbool\t\temulate_prepares;\n\tbool\t\tdisable_native_prepares; /* deprecated since 5.6 */\n\tbool\t\tdisable_prepares;\n\tHashTable       *lob_streams;\n} pdo_pgsql_db_handle;\n\ntypedef struct {\n\tOid          pgsql_type;\n} pdo_pgsql_column;\n\ntypedef struct {\n\tpdo_pgsql_db_handle     *H;\n\tPGresult                *result;\n\tpdo_pgsql_column        *cols;\n\tchar *cursor_name;\n\tchar *stmt_name;\n\tzend_string *query;\n\tchar **param_values;\n\tint *param_lengths;\n\tint *param_formats;\n\tOid *param_types;\n\tint                     current_row;\n\tbool is_prepared;\n} pdo_pgsql_stmt;\n\ntypedef struct {\n\tOid     oid;\n} pdo_pgsql_bound_param;\n\nextern const pdo_driver_t pdo_pgsql_driver;\n\nextern int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line);\n#define pdo_pgsql_error(d,e,z)\t_pdo_pgsql_error(d, NULL, e, z, NULL, __FILE__, __LINE__)\n#define pdo_pgsql_error_msg(d,e,m)\t_pdo_pgsql_error(d, NULL, e, NULL, m, __FILE__, __LINE__)\n#define pdo_pgsql_error_stmt(s,e,z)\t_pdo_pgsql_error(s->dbh, s, e, z, NULL, __FILE__, __LINE__)\n#define pdo_pgsql_error_stmt_msg(stmt, e, sqlstate, msg) \\\n\t_pdo_pgsql_error(stmt->dbh, stmt, e, sqlstate, msg, __FILE__, __LINE__)\n\nextern const struct pdo_stmt_methods swoole_pgsql_stmt_methods;\n\n#define pdo_pgsql_sqlstate(r) PQresultErrorField(r, PG_DIAG_SQLSTATE)\n\nenum {\n\tPDO_PGSQL_ATTR_DISABLE_PREPARES = PDO_ATTR_DRIVER_SPECIFIC,\n};\n\nstruct pdo_pgsql_lob_self {\n\tzval dbh;\n\tPGconn *conn;\n\tint lfd;\n\tOid oid;\n};\n\nenum pdo_pgsql_specific_constants {\n\tPGSQL_TRANSACTION_IDLE = PQTRANS_IDLE,\n\tPGSQL_TRANSACTION_ACTIVE = PQTRANS_ACTIVE,\n\tPGSQL_TRANSACTION_INTRANS = PQTRANS_INTRANS,\n\tPGSQL_TRANSACTION_INERROR = PQTRANS_INERROR,\n\tPGSQL_TRANSACTION_UNKNOWN = PQTRANS_UNKNOWN\n};\n\nphp_stream *pdo_pgsql_create_lob_stream(zval *pdh, int lfd, Oid oid);\nextern const php_stream_ops pdo_pgsql_lob_stream_ops;\n\nvoid pdo_libpq_version(char *buf, size_t len);\nvoid pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh);\n\n#endif /* PHP_PDO_PGSQL_INT_H */\n"
  },
  {
    "path": "thirdparty/php83/pdo_odbc/odbc_driver.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_ODBC_HOOK\n#include \"php_swoole_odbc.h\"\n\n#if PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400\n\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"pdo/php_pdo.h\"\n#include \"pdo/php_pdo_driver.h\"\n#include \"zend_exceptions.h\"\n#include <php_odbc_utils.h>\n\nstatic void pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    pdo_odbc_errinfo *einfo = &H->einfo;\n    pdo_odbc_stmt *S = NULL;\n    zend_string *message = NULL;\n\n    if (stmt) {\n        S = (pdo_odbc_stmt *) stmt->driver_data;\n        einfo = &S->einfo;\n    }\n\n    message = strpprintf(0,\n                         \"%s (%s[%ld] at %s:%d)\",\n                         einfo->last_err_msg,\n                         einfo->what,\n                         (long) einfo->last_error,\n                         einfo->file,\n                         einfo->line);\n\n    add_next_index_long(info, einfo->last_error);\n    add_next_index_str(info, message);\n    add_next_index_string(info, einfo->last_state);\n}\n\nvoid pdo_odbc_error(\n    pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line) /* {{{ */\n{\n    SQLRETURN rc;\n    SQLSMALLINT errmsgsize = 0;\n    SQLHANDLE eh;\n    SQLSMALLINT htype, recno = 1;\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    pdo_odbc_errinfo *einfo = &H->einfo;\n    pdo_odbc_stmt *S = NULL;\n    pdo_error_type *pdo_err = &dbh->error_code;\n\n    if (stmt) {\n        S = (pdo_odbc_stmt *) stmt->driver_data;\n\n        einfo = &S->einfo;\n        pdo_err = &stmt->error_code;\n    }\n\n    if (statement == SQL_NULL_HSTMT && S) {\n        statement = S->stmt;\n    }\n\n    if (statement) {\n        htype = SQL_HANDLE_STMT;\n        eh = statement;\n    } else if (H->dbc) {\n        htype = SQL_HANDLE_DBC;\n        eh = H->dbc;\n    } else {\n        htype = SQL_HANDLE_ENV;\n        eh = H->env;\n    }\n\n    rc = SQLGetDiagRec(htype,\n                       eh,\n                       recno++,\n                       (SQLCHAR *) einfo->last_state,\n                       &einfo->last_error,\n                       (SQLCHAR *) einfo->last_err_msg,\n                       sizeof(einfo->last_err_msg) - 1,\n                       &errmsgsize);\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        errmsgsize = 0;\n    }\n\n    einfo->last_err_msg[errmsgsize] = '\\0';\n    einfo->file = file;\n    einfo->line = line;\n    einfo->what = what;\n\n    strcpy(*pdo_err, einfo->last_state);\n    /* printf(\"@@ SQLSTATE[%s] %s\\n\", *pdo_err, einfo->last_err_msg); */\n    if (!dbh->methods) {\n        zend_throw_exception_ex(php_pdo_get_exception(),\n                                einfo->last_error,\n                                \"SQLSTATE[%s] %s: %d %s\",\n                                *pdo_err,\n                                what,\n                                einfo->last_error,\n                                einfo->last_err_msg);\n    }\n\n    /* just like a cursor, once you start pulling, you need to keep\n     * going until the end; SQL Server (at least) will mess with the\n     * actual cursor state if you don't finish retrieving all the\n     * diagnostic records (which can be generated by PRINT statements\n     * in the query, for instance). */\n    while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {\n        SQLCHAR discard_state[6];\n        SQLCHAR discard_buf[1024];\n        SQLINTEGER code;\n        rc = SQLGetDiagRec(htype, eh, recno++, discard_state, &code, discard_buf, sizeof(discard_buf) - 1, &errmsgsize);\n    }\n}\n/* }}} */\n\nstatic void odbc_handle_closer(pdo_dbh_t *dbh) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n\n    if (H->dbc != SQL_NULL_HANDLE) {\n        SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);\n        SQLDisconnect(H->dbc);\n        SQLFreeHandle(SQL_HANDLE_DBC, H->dbc);\n        H->dbc = NULL;\n    }\n    SQLFreeHandle(SQL_HANDLE_ENV, H->env);\n    H->env = NULL;\n    pefree(H, dbh->is_persistent);\n    dbh->driver_data = NULL;\n}\n\nstatic bool odbc_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) {\n    RETCODE rc;\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    pdo_odbc_stmt *S = ecalloc(1, sizeof(*S));\n    enum pdo_cursor_type cursor_type = PDO_CURSOR_FWDONLY;\n    int ret;\n    zend_string *nsql = NULL;\n\n    S->H = H;\n    S->assume_utf8 = H->assume_utf8;\n\n    /* before we prepare, we need to peek at the query; if it uses named parameters,\n     * we want PDO to rewrite them for us */\n    stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL;\n    ret = pdo_parse_params(stmt, sql, &nsql);\n\n    if (ret == 1) {\n        /* query was re-written */\n        sql = nsql;\n    } else if (ret == -1) {\n        /* couldn't grok it */\n        strcpy(dbh->error_code, stmt->error_code);\n        efree(S);\n        return false;\n    }\n\n    rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &S->stmt);\n\n    if (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) {\n        efree(S);\n        if (nsql) {\n            zend_string_release(nsql);\n        }\n        pdo_odbc_drv_error(\"SQLAllocStmt\");\n        return false;\n    }\n\n    stmt->driver_data = S;\n\n    cursor_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY);\n    if (cursor_type != PDO_CURSOR_FWDONLY) {\n        rc = SQLSetStmtAttr(S->stmt, SQL_ATTR_CURSOR_SCROLLABLE, (void *) SQL_SCROLLABLE, 0);\n        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n            pdo_odbc_stmt_error(\"SQLSetStmtAttr: SQL_ATTR_CURSOR_SCROLLABLE\");\n            SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);\n            if (nsql) {\n                zend_string_release(nsql);\n            }\n            return false;\n        }\n    }\n\n    rc = SQLPrepare(S->stmt, (SQLCHAR *) ZSTR_VAL(sql), SQL_NTS);\n    if (nsql) {\n        zend_string_release(nsql);\n    }\n\n    stmt->methods = &odbc_stmt_methods;\n\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_stmt_error(\"SQLPrepare\");\n        if (rc != SQL_SUCCESS_WITH_INFO) {\n            /* clone error information into the db handle */\n            strcpy(H->einfo.last_err_msg, S->einfo.last_err_msg);\n            H->einfo.file = S->einfo.file;\n            H->einfo.line = S->einfo.line;\n            H->einfo.what = S->einfo.what;\n            strcpy(dbh->error_code, stmt->error_code);\n        }\n    }\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        return false;\n    }\n    return true;\n}\n\nstatic zend_long odbc_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    RETCODE rc;\n    SQLLEN row_count = -1;\n    PDO_ODBC_HSTMT stmt;\n\n    rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt);\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_drv_error(\"SQLAllocHandle: STMT\");\n        return -1;\n    }\n\n    rc = SQLExecDirect(stmt, (SQLCHAR *) ZSTR_VAL(sql), ZSTR_LEN(sql));\n\n    if (rc == SQL_NO_DATA) {\n        /* If SQLExecDirect executes a searched update or delete statement that\n         * does not affect any rows at the data source, the call to\n         * SQLExecDirect returns SQL_NO_DATA. */\n        row_count = 0;\n        goto out;\n    }\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_doer_error(\"SQLExecDirect\");\n        goto out;\n    }\n\n    rc = SQLRowCount(stmt, &row_count);\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_doer_error(\"SQLRowCount\");\n        goto out;\n    }\n    if (row_count == -1) {\n        row_count = 0;\n    }\nout:\n    SQLFreeHandle(SQL_HANDLE_STMT, stmt);\n    return row_count;\n}\n\n/* TODO: Do ODBC quoter\nstatic int odbc_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t\n*quotedlen, enum pdo_param_type param_type )\n{\n        // pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;\n        // TODO: figure it out\n        return 0;\n}\n*/\n\nstatic bool odbc_handle_begin(pdo_dbh_t *dbh) {\n    if (dbh->auto_commit) {\n        /* we need to disable auto-commit now, to be able to initiate a transaction */\n        RETCODE rc;\n        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n\n        rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER);\n        if (rc != SQL_SUCCESS) {\n            pdo_odbc_drv_error(\"SQLSetConnectAttr AUTOCOMMIT = OFF\");\n            return false;\n        }\n    }\n    return true;\n}\n\nstatic bool odbc_handle_commit(pdo_dbh_t *dbh) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    RETCODE rc;\n\n    rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT);\n\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_drv_error(\"SQLEndTran: Commit\");\n\n        if (rc != SQL_SUCCESS_WITH_INFO) {\n            return false;\n        }\n    }\n\n    if (dbh->auto_commit) {\n        /* turn auto-commit back on again */\n        rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);\n        if (rc != SQL_SUCCESS) {\n            pdo_odbc_drv_error(\"SQLSetConnectAttr AUTOCOMMIT = ON\");\n            return false;\n        }\n    }\n    return true;\n}\n\nstatic bool odbc_handle_rollback(pdo_dbh_t *dbh) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    RETCODE rc;\n\n    rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);\n\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_drv_error(\"SQLEndTran: Rollback\");\n\n        if (rc != SQL_SUCCESS_WITH_INFO) {\n            return false;\n        }\n    }\n    if (dbh->auto_commit && H->dbc) {\n        /* turn auto-commit back on again */\n        rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);\n        if (rc != SQL_SUCCESS) {\n            pdo_odbc_drv_error(\"SQLSetConnectAttr AUTOCOMMIT = ON\");\n            return false;\n        }\n    }\n\n    return true;\n}\n\nstatic bool odbc_handle_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    bool bval;\n\n    switch (attr) {\n    case PDO_ODBC_ATTR_ASSUME_UTF8:\n        if (!pdo_get_bool_param(&bval, val)) {\n            return false;\n        }\n        H->assume_utf8 = bval;\n        return true;\n    case PDO_ATTR_AUTOCOMMIT:\n        if (!pdo_get_bool_param(&bval, val)) {\n            return false;\n        }\n        if (dbh->in_txn) {\n            pdo_raise_impl_error(\n                dbh, NULL, \"HY000\", \"Cannot change autocommit mode while a transaction is already open\");\n            return false;\n        }\n        if (dbh->auto_commit ^ bval) {\n            dbh->auto_commit = bval;\n            RETCODE rc =\n                SQLSetConnectAttr(H->dbc,\n                                  SQL_ATTR_AUTOCOMMIT,\n                                  dbh->auto_commit ? (SQLPOINTER) SQL_AUTOCOMMIT_ON : (SQLPOINTER) SQL_AUTOCOMMIT_OFF,\n                                  SQL_IS_INTEGER);\n            if (rc != SQL_SUCCESS) {\n                pdo_odbc_drv_error(dbh->auto_commit ? \"SQLSetConnectAttr AUTOCOMMIT = ON\"\n                                                    : \"SQLSetConnectAttr AUTOCOMMIT = OFF\");\n                return false;\n            }\n        }\n        return true;\n    default:\n        strcpy(H->einfo.last_err_msg, \"Unknown Attribute\");\n        H->einfo.what = \"setAttribute\";\n        strcpy(H->einfo.last_state, \"IM001\");\n        return false;\n    }\n}\n\nstatic int pdo_odbc_get_info_string(pdo_dbh_t *dbh, SQLUSMALLINT type, zval *val) {\n    RETCODE rc;\n    SQLSMALLINT out_len;\n    char buf[256];\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    rc = SQLGetInfo(H->dbc, type, (SQLPOINTER) buf, sizeof(buf), &out_len);\n    /* returning -1 is treated as an error, not as unsupported */\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        return -1;\n    }\n    ZVAL_STRINGL(val, buf, out_len);\n    return 1;\n}\n\nstatic int odbc_handle_get_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    switch (attr) {\n    case PDO_ATTR_CLIENT_VERSION:\n        ZVAL_STRING(val, \"ODBC-\" PDO_ODBC_TYPE);\n        return 1;\n\n    case PDO_ATTR_SERVER_VERSION:\n        return pdo_odbc_get_info_string(dbh, SQL_DBMS_VER, val);\n    case PDO_ATTR_SERVER_INFO:\n        return pdo_odbc_get_info_string(dbh, SQL_DBMS_NAME, val);\n    case PDO_ATTR_PREFETCH:\n    case PDO_ATTR_TIMEOUT:\n    case PDO_ATTR_CONNECTION_STATUS:\n        break;\n    case PDO_ODBC_ATTR_ASSUME_UTF8:\n        ZVAL_BOOL(val, H->assume_utf8 ? 1 : 0);\n        return 1;\n    case PDO_ATTR_AUTOCOMMIT:\n        ZVAL_BOOL(val, dbh->auto_commit);\n        return 1;\n    }\n    return 0;\n}\n\nstatic zend_result odbc_handle_check_liveness(pdo_dbh_t *dbh) {\n    RETCODE ret;\n    UCHAR d_name[32];\n    SQLSMALLINT len;\n    SQLUINTEGER dead = SQL_CD_FALSE;\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n\n    ret = SQLGetConnectAttr(H->dbc, SQL_ATTR_CONNECTION_DEAD, &dead, 0, NULL);\n    if (ret == SQL_SUCCESS && dead == SQL_CD_TRUE) {\n        /* Bail early here, since we know it's gone */\n        return FAILURE;\n    }\n    /*\n     * If the driver doesn't support SQL_ATTR_CONNECTION_DEAD, or if\n     * it returns false (which could be a false positive), fall back\n     * to using SQL_DATA_SOURCE_READ_ONLY, which isn't semantically\n     * correct, but works with many drivers.\n     */\n    ret = SQLGetInfo(H->dbc, SQL_DATA_SOURCE_READ_ONLY, d_name, sizeof(d_name), &len);\n\n    if (ret != SQL_SUCCESS || len == 0) {\n        return FAILURE;\n    }\n    return SUCCESS;\n}\n\nstatic const struct pdo_dbh_methods odbc_methods = {\n    odbc_handle_closer,\n    odbc_handle_preparer,\n    odbc_handle_doer,\n    NULL, /* quoter */\n    odbc_handle_begin,\n    odbc_handle_commit,\n    odbc_handle_rollback,\n    odbc_handle_set_attr,\n    NULL, /* last id */\n    pdo_odbc_fetch_error_func,\n    odbc_handle_get_attr,       /* get attr */\n    odbc_handle_check_liveness, /* check_liveness */\n    NULL,                       /* get_driver_methods */\n    NULL,                       /* request_shutdown */\n    NULL,                       /* in transaction, use PDO's internal tracking mechanism */\n    NULL                        /* get_gc */\n};\n\nstatic int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */\n{\n    pdo_odbc_db_handle *H;\n    RETCODE rc;\n    int use_direct = 0;\n    zend_ulong cursor_lib;\n\n    H = pecalloc(1, sizeof(*H), dbh->is_persistent);\n\n    dbh->driver_data = H;\n\n    rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H->env);\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_drv_error(\"SQLAllocHandle: ENV\");\n        goto fail;\n    }\n\n    rc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_drv_error(\"SQLSetEnvAttr: ODBC3\");\n        goto fail;\n    }\n\n#ifdef SQL_ATTR_CONNECTION_POOLING\n    if (pdo_odbc_pool_on != SQL_CP_OFF) {\n        rc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void *) pdo_odbc_pool_mode, 0);\n        if (rc != SQL_SUCCESS) {\n            pdo_odbc_drv_error(\"SQLSetEnvAttr: SQL_ATTR_CP_MATCH\");\n            goto fail;\n        }\n    }\n#endif\n\n    rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc);\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_drv_error(\"SQLAllocHandle: DBC\");\n        goto fail;\n    }\n\n    rc = SQLSetConnectAttr(H->dbc,\n                           SQL_ATTR_AUTOCOMMIT,\n                           (SQLPOINTER) (intptr_t) (dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF),\n                           SQL_IS_INTEGER);\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_drv_error(\"SQLSetConnectAttr AUTOCOMMIT\");\n        goto fail;\n    }\n\n    /* set up the cursor library, if needed, or if configured explicitly */\n    cursor_lib = pdo_attr_lval(driver_options, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY, SQL_CUR_USE_IF_NEEDED);\n    rc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void *) cursor_lib, SQL_IS_INTEGER);\n    if (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) {\n        pdo_odbc_drv_error(\"SQLSetConnectAttr SQL_ODBC_CURSORS\");\n        goto fail;\n    }\n\n    /* a connection string may have = but not ; - i.e. \"DSN=PHP\" */\n    if (strchr(dbh->data_source, '=')) {\n        SQLCHAR dsnbuf[1024];\n        SQLSMALLINT dsnbuflen;\n\n        use_direct = 1;\n\n        /* Force UID and PWD to be set in the DSN */\n        bool is_uid_set =\n            dbh->username && *dbh->username && !strstr(dbh->data_source, \"uid=\") && !strstr(dbh->data_source, \"UID=\");\n        bool is_pwd_set =\n            dbh->password && *dbh->password && !strstr(dbh->data_source, \"pwd=\") && !strstr(dbh->data_source, \"PWD=\");\n        if (is_uid_set && is_pwd_set) {\n            char *uid = NULL, *pwd = NULL;\n            bool should_quote_uid =\n                !php_odbc_connstr_is_quoted(dbh->username) && php_odbc_connstr_should_quote(dbh->username);\n            bool should_quote_pwd =\n                !php_odbc_connstr_is_quoted(dbh->password) && php_odbc_connstr_should_quote(dbh->password);\n            if (should_quote_uid) {\n                size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->username);\n                uid = emalloc(estimated_length);\n                php_odbc_connstr_quote(uid, dbh->username, estimated_length);\n            } else {\n                uid = dbh->username;\n            }\n            if (should_quote_pwd) {\n                size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->password);\n                pwd = emalloc(estimated_length);\n                php_odbc_connstr_quote(pwd, dbh->password, estimated_length);\n            } else {\n                pwd = dbh->password;\n            }\n            size_t new_dsn_size = strlen(dbh->data_source) + strlen(uid) + strlen(pwd) + strlen(\";UID=;PWD=\") + 1;\n            char *dsn = pemalloc(new_dsn_size, dbh->is_persistent);\n            snprintf(dsn, new_dsn_size, \"%s;UID=%s;PWD=%s\", dbh->data_source, uid, pwd);\n            pefree((char *) dbh->data_source, dbh->is_persistent);\n            dbh->data_source = dsn;\n            if (uid && should_quote_uid) {\n                efree(uid);\n            }\n            if (pwd && should_quote_pwd) {\n                efree(pwd);\n            }\n        }\n\n        rc = SQLDriverConnect(H->dbc,\n                              NULL,\n                              (SQLCHAR *) dbh->data_source,\n                              strlen(dbh->data_source),\n                              dsnbuf,\n                              sizeof(dsnbuf) - 1,\n                              &dsnbuflen,\n                              SQL_DRIVER_NOPROMPT);\n    }\n    if (!use_direct) {\n        rc = SQLConnect(H->dbc,\n                        (SQLCHAR *) dbh->data_source,\n                        SQL_NTS,\n                        (SQLCHAR *) dbh->username,\n                        SQL_NTS,\n                        (SQLCHAR *) dbh->password,\n                        SQL_NTS);\n    }\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_drv_error(use_direct ? \"SQLDriverConnect\" : \"SQLConnect\");\n        goto fail;\n    }\n\n    /* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */\n\n    dbh->methods = &odbc_methods;\n    dbh->alloc_own_columns = 1;\n\n    return 1;\n\nfail:\n    dbh->methods = &odbc_methods;\n    return 0;\n}\n/* }}} */\n\nconst pdo_driver_t swoole_pdo_odbc_driver = {PDO_DRIVER_HEADER(odbc), pdo_odbc_handle_factory};\n#endif\n"
  },
  {
    "path": "thirdparty/php83/pdo_odbc/odbc_stmt.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_ODBC_HOOK\n#include \"php_swoole_odbc.h\"\n\n#if PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"pdo/php_pdo.h\"\n#include \"pdo/php_pdo_driver.h\"\n\nenum pdo_odbc_conv_result { PDO_ODBC_CONV_NOT_REQUIRED, PDO_ODBC_CONV_OK, PDO_ODBC_CONV_FAIL };\n\nstatic int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SWORD sqltype) {\n    if (!S->assume_utf8) return 0;\n    switch (sqltype) {\n#ifdef SQL_WCHAR\n    case SQL_WCHAR:\n        return 1;\n#endif\n#ifdef SQL_WLONGVARCHAR\n    case SQL_WLONGVARCHAR:\n        return 1;\n#endif\n#ifdef SQL_WVARCHAR\n    case SQL_WVARCHAR:\n        return 1;\n#endif\n    default:\n        return 0;\n    }\n}\n\nstatic int pdo_odbc_utf82ucs2(\n    pdo_stmt_t *stmt, int is_unicode, const char *buf, zend_ulong buflen, zend_ulong *outlen) {\n#ifdef PHP_WIN32\n    if (is_unicode && buflen) {\n        pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n        DWORD ret;\n\n        ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, NULL, 0);\n        if (ret == 0) {\n            /*printf(\"%s:%d %d [%d] %.*s\\n\", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/\n            return PDO_ODBC_CONV_FAIL;\n        }\n\n        ret *= sizeof(WCHAR);\n\n        if (S->convbufsize <= ret) {\n            S->convbufsize = ret + sizeof(WCHAR);\n            S->convbuf = erealloc(S->convbuf, S->convbufsize);\n        }\n\n        ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, (LPWSTR) S->convbuf, S->convbufsize / sizeof(WCHAR));\n        if (ret == 0) {\n            /*printf(\"%s:%d %d [%d] %.*s\\n\", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/\n            return PDO_ODBC_CONV_FAIL;\n        }\n\n        ret *= sizeof(WCHAR);\n        *outlen = ret;\n        return PDO_ODBC_CONV_OK;\n    }\n#endif\n    return PDO_ODBC_CONV_NOT_REQUIRED;\n}\n\nstatic int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, zval *result) {\n#ifdef PHP_WIN32\n    ZEND_ASSERT(Z_TYPE_P(result) == IS_STRING);\n    if (is_unicode && Z_STRLEN_P(result) != 0) {\n        pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n        DWORD ret;\n\n        ret = WideCharToMultiByte(\n            CP_UTF8, 0, (LPCWSTR) Z_STRVAL_P(result), Z_STRLEN_P(result) / sizeof(WCHAR), NULL, 0, NULL, NULL);\n        if (ret == 0) {\n            return PDO_ODBC_CONV_FAIL;\n        }\n\n        zend_string *str = zend_string_alloc(ret, 0);\n        ret = WideCharToMultiByte(CP_UTF8,\n                                  0,\n                                  (LPCWSTR) Z_STRVAL_P(result),\n                                  Z_STRLEN_P(result) / sizeof(WCHAR),\n                                  ZSTR_VAL(str),\n                                  ZSTR_LEN(str),\n                                  NULL,\n                                  NULL);\n        if (ret == 0) {\n            return PDO_ODBC_CONV_FAIL;\n        }\n\n        ZSTR_VAL(str)[ret] = '\\0';\n        zval_ptr_dtor_str(result);\n        ZVAL_STR(result, str);\n        return PDO_ODBC_CONV_OK;\n    }\n#endif\n    return PDO_ODBC_CONV_NOT_REQUIRED;\n}\n\nstatic void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S) {\n    if (S->cols) {\n        int i;\n\n        for (i = 0; i < S->col_count; i++) {\n            if (S->cols[i].data) {\n                efree(S->cols[i].data);\n            }\n        }\n        efree(S->cols);\n        S->cols = NULL;\n        S->col_count = 0;\n    }\n}\n\nstatic int odbc_stmt_dtor(pdo_stmt_t *stmt) {\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    if (S->stmt != SQL_NULL_HANDLE) {\n        if (stmt->executed) {\n            SQLCloseCursor(S->stmt);\n        }\n        SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);\n        S->stmt = SQL_NULL_HANDLE;\n    }\n\n    free_cols(stmt, S);\n    if (S->convbuf) {\n        efree(S->convbuf);\n    }\n    efree(S);\n\n    return 1;\n}\n\nstatic int odbc_stmt_execute(pdo_stmt_t *stmt) {\n    RETCODE rc, rc1;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n    char *buf = NULL;\n    SQLLEN row_count = -1;\n\n    if (stmt->executed) {\n        SQLCloseCursor(S->stmt);\n    }\n\n    rc = SQLExecute(S->stmt);\n\n    while (rc == SQL_NEED_DATA) {\n        struct pdo_bound_param_data *param;\n\n        rc = SQLParamData(S->stmt, (SQLPOINTER *) &param);\n        if (rc == SQL_NEED_DATA) {\n            php_stream *stm;\n            int len;\n            pdo_odbc_param *P;\n            zval *parameter;\n\n            P = (pdo_odbc_param *) param->driver_data;\n            if (Z_ISREF(param->parameter)) {\n                parameter = Z_REFVAL(param->parameter);\n            } else {\n                parameter = &param->parameter;\n            }\n            if (Z_TYPE_P(parameter) != IS_RESOURCE) {\n                /* they passed in a string */\n                zend_ulong ulen;\n                convert_to_string(parameter);\n\n                switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), &ulen)) {\n                case PDO_ODBC_CONV_NOT_REQUIRED:\n                    rc1 = SQLPutData(S->stmt, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter));\n                    if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {\n                        rc = rc1;\n                    }\n                    break;\n                case PDO_ODBC_CONV_OK:\n                    rc1 = SQLPutData(S->stmt, S->convbuf, ulen);\n                    if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {\n                        rc = rc1;\n                    }\n                    break;\n                case PDO_ODBC_CONV_FAIL:\n                    pdo_odbc_stmt_error(\"error converting input string\");\n                    SQLCloseCursor(S->stmt);\n                    if (buf) {\n                        efree(buf);\n                    }\n                    return 0;\n                }\n                continue;\n            }\n\n            /* we assume that LOBs are binary and don't need charset\n             * conversion */\n\n            php_stream_from_zval_no_verify(stm, parameter);\n            if (!stm) {\n                /* shouldn't happen either */\n                pdo_odbc_stmt_error(\"input LOB is no longer a stream\");\n                SQLCloseCursor(S->stmt);\n                if (buf) {\n                    efree(buf);\n                }\n                return 0;\n            }\n\n            /* now suck data from the stream and stick it into the database */\n            if (buf == NULL) {\n                buf = emalloc(8192);\n            }\n\n            do {\n                len = php_stream_read(stm, buf, 8192);\n                if (len == 0) {\n                    break;\n                }\n                rc1 = SQLPutData(S->stmt, buf, len);\n                if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {\n                    rc = rc1;\n                }\n            } while (1);\n        }\n    }\n\n    if (buf) {\n        efree(buf);\n    }\n\n    switch (rc) {\n    case SQL_SUCCESS:\n        break;\n    case SQL_NO_DATA_FOUND:\n    case SQL_SUCCESS_WITH_INFO:\n        pdo_odbc_stmt_error(\"SQLExecute\");\n        break;\n\n    default:\n        pdo_odbc_stmt_error(\"SQLExecute\");\n        return 0;\n    }\n\n    SQLRowCount(S->stmt, &row_count);\n    stmt->row_count = row_count;\n\n    if (S->cols == NULL) {\n        /* do first-time-only definition of bind/mapping stuff */\n        SQLSMALLINT colcount;\n\n        /* how many columns do we have ? */\n        SQLNumResultCols(S->stmt, &colcount);\n\n        stmt->column_count = S->col_count = (int) colcount;\n        S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));\n        S->going_long = 0;\n    }\n\n    return 1;\n}\n\nstatic int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, enum pdo_param_event event_type) {\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n    RETCODE rc;\n    SWORD sqltype = 0, ctype = 0, scale = 0, nullable = 0;\n    SQLULEN precision = 0;\n    pdo_odbc_param *P;\n    zval *parameter;\n\n    /* we're only interested in parameters for prepared SQL right now */\n    if (param->is_param) {\n        switch (event_type) {\n        case PDO_PARAM_EVT_FETCH_PRE:\n        case PDO_PARAM_EVT_FETCH_POST:\n        case PDO_PARAM_EVT_NORMALIZE:\n            /* Do nothing */\n            break;\n\n        case PDO_PARAM_EVT_FREE:\n            P = param->driver_data;\n            if (P) {\n                efree(P);\n            }\n            break;\n\n        case PDO_PARAM_EVT_ALLOC: {\n            /* figure out what we're doing */\n            switch (PDO_PARAM_TYPE(param->param_type)) {\n            case PDO_PARAM_LOB:\n                break;\n\n            case PDO_PARAM_STMT:\n                return 0;\n\n            default:\n                break;\n            }\n\n            rc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno + 1, &sqltype, &precision, &scale, &nullable);\n            if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n                /* MS Access, for instance, doesn't support SQLDescribeParam,\n                 * so we need to guess */\n                switch (PDO_PARAM_TYPE(param->param_type)) {\n                case PDO_PARAM_INT:\n                    sqltype = SQL_INTEGER;\n                    break;\n                case PDO_PARAM_LOB:\n                    sqltype = SQL_LONGVARBINARY;\n                    break;\n                default:\n                    sqltype = SQL_LONGVARCHAR;\n                }\n                precision = 4000;\n                scale = 5;\n                nullable = 1;\n\n                if (param->max_value_len > 0) {\n                    precision = param->max_value_len;\n                }\n            }\n            if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) {\n                ctype = SQL_C_BINARY;\n            } else {\n                ctype = SQL_C_CHAR;\n            }\n\n            P = emalloc(sizeof(*P));\n            param->driver_data = P;\n\n            P->len = 0; /* is re-populated each EXEC_PRE */\n            P->outbuf = NULL;\n\n            P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype);\n            if (P->is_unicode) {\n                /* avoid driver auto-translation: we'll do it ourselves */\n                ctype = SQL_C_BINARY;\n            }\n\n            if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) {\n                P->paramtype = SQL_PARAM_INPUT_OUTPUT;\n            } else if (param->max_value_len <= 0) {\n                P->paramtype = SQL_PARAM_INPUT;\n            } else {\n                P->paramtype = SQL_PARAM_OUTPUT;\n            }\n\n            if (P->paramtype != SQL_PARAM_INPUT) {\n                if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) {\n                    /* need an explicit buffer to hold result */\n                    P->len = param->max_value_len > 0 ? param->max_value_len : precision;\n                    if (P->is_unicode) {\n                        P->len *= 2;\n                    }\n                    P->outbuf = emalloc(P->len + (P->is_unicode ? 2 : 1));\n                }\n            }\n\n            if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) {\n                pdo_odbc_stmt_error(\"Can't bind a lob for output\");\n                return 0;\n            }\n\n            rc = SQLBindParameter(S->stmt,\n                                  (SQLUSMALLINT) param->paramno + 1,\n                                  P->paramtype,\n                                  ctype,\n                                  sqltype,\n                                  precision,\n                                  scale,\n                                  P->paramtype == SQL_PARAM_INPUT ? (SQLPOINTER) param : P->outbuf,\n                                  P->len,\n                                  &P->len);\n\n            if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {\n                return 1;\n            }\n            pdo_odbc_stmt_error(\"SQLBindParameter\");\n            return 0;\n        }\n\n        case PDO_PARAM_EVT_EXEC_PRE:\n            P = param->driver_data;\n            if (!Z_ISREF(param->parameter)) {\n                parameter = &param->parameter;\n            } else {\n                parameter = Z_REFVAL(param->parameter);\n            }\n\n            if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {\n                if (Z_TYPE_P(parameter) == IS_RESOURCE) {\n                    php_stream *stm;\n                    php_stream_statbuf sb;\n\n                    php_stream_from_zval_no_verify(stm, parameter);\n\n                    if (!stm) {\n                        return 0;\n                    }\n\n                    if (0 == php_stream_stat(stm, &sb)) {\n                        if (P->outbuf) {\n                            int len, amount;\n                            char *ptr = P->outbuf;\n                            char *end = P->outbuf + P->len;\n\n                            P->len = 0;\n                            do {\n                                amount = end - ptr;\n                                if (amount == 0) {\n                                    break;\n                                }\n                                if (amount > 8192) amount = 8192;\n                                len = php_stream_read(stm, ptr, amount);\n                                if (len == 0) {\n                                    break;\n                                }\n                                ptr += len;\n                                P->len += len;\n                            } while (1);\n\n                        } else {\n                            P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size);\n                        }\n                    } else {\n                        if (P->outbuf) {\n                            P->len = 0;\n                        } else {\n                            P->len = SQL_LEN_DATA_AT_EXEC(0);\n                        }\n                    }\n                } else {\n                    convert_to_string(parameter);\n                    if (P->outbuf) {\n                        P->len = Z_STRLEN_P(parameter);\n                        memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len);\n                    } else {\n                        P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));\n                    }\n                }\n            } else if (Z_TYPE_P(parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) {\n                P->len = SQL_NULL_DATA;\n            } else {\n                convert_to_string(parameter);\n                if (P->outbuf) {\n                    zend_ulong ulen;\n                    switch (\n                        pdo_odbc_utf82ucs2(stmt, P->is_unicode, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), &ulen)) {\n                    case PDO_ODBC_CONV_FAIL:\n                    case PDO_ODBC_CONV_NOT_REQUIRED:\n                        P->len = Z_STRLEN_P(parameter);\n                        memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len);\n                        break;\n                    case PDO_ODBC_CONV_OK:\n                        P->len = ulen;\n                        memcpy(P->outbuf, S->convbuf, P->len);\n                        break;\n                    }\n                } else {\n                    P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));\n                }\n            }\n            return 1;\n\n        case PDO_PARAM_EVT_EXEC_POST:\n            P = param->driver_data;\n\n            if (P->outbuf) {\n                if (Z_ISREF(param->parameter)) {\n                    parameter = Z_REFVAL(param->parameter);\n                } else {\n                    parameter = &param->parameter;\n                }\n                zval_ptr_dtor(parameter);\n\n                if (P->len >= 0) {\n                    ZVAL_STRINGL(parameter, P->outbuf, P->len);\n                    switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, parameter)) {\n                    case PDO_ODBC_CONV_FAIL:\n                        /* something fishy, but allow it to come back as binary */\n                    case PDO_ODBC_CONV_NOT_REQUIRED:\n                        break;\n                    case PDO_ODBC_CONV_OK:\n                        break;\n                    }\n                } else {\n                    ZVAL_NULL(parameter);\n                }\n            }\n            return 1;\n        }\n    }\n    return 1;\n}\n\nstatic int odbc_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) {\n    RETCODE rc;\n    SQLSMALLINT odbcori;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    switch (ori) {\n    case PDO_FETCH_ORI_NEXT:\n        odbcori = SQL_FETCH_NEXT;\n        break;\n    case PDO_FETCH_ORI_PRIOR:\n        odbcori = SQL_FETCH_PRIOR;\n        break;\n    case PDO_FETCH_ORI_FIRST:\n        odbcori = SQL_FETCH_FIRST;\n        break;\n    case PDO_FETCH_ORI_LAST:\n        odbcori = SQL_FETCH_LAST;\n        break;\n    case PDO_FETCH_ORI_ABS:\n        odbcori = SQL_FETCH_ABSOLUTE;\n        break;\n    case PDO_FETCH_ORI_REL:\n        odbcori = SQL_FETCH_RELATIVE;\n        break;\n    default:\n        strcpy(stmt->error_code, \"HY106\");\n        return 0;\n    }\n    rc = SQLFetchScroll(S->stmt, odbcori, offset);\n\n    if (rc == SQL_SUCCESS) {\n        return 1;\n    }\n    if (rc == SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_stmt_error(\"SQLFetchScroll\");\n        return 1;\n    }\n\n    if (rc == SQL_NO_DATA) {\n        /* pdo_odbc_stmt_error(\"SQLFetchScroll\"); */\n        return 0;\n    }\n\n    pdo_odbc_stmt_error(\"SQLFetchScroll\");\n\n    return 0;\n}\n\nstatic int odbc_stmt_describe(pdo_stmt_t *stmt, int colno) {\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n    struct pdo_column_data *col = &stmt->columns[colno];\n    RETCODE rc;\n    SWORD colnamelen;\n    SQLULEN colsize;\n    SQLLEN displaysize = 0;\n\n    rc = SQLDescribeCol(S->stmt,\n                        colno + 1,\n                        (SQLCHAR *) S->cols[colno].colname,\n                        sizeof(S->cols[colno].colname) - 1,\n                        &colnamelen,\n                        &S->cols[colno].coltype,\n                        &colsize,\n                        NULL,\n                        NULL);\n\n    /* This fixes a known issue with SQL Server and (max) lengths,\n    may affect others as well.  If we are SQL_VARCHAR,\n    SQL_VARBINARY, or SQL_WVARCHAR (or any of the long variations)\n    and zero is returned from colsize then consider it long */\n    if (0 == colsize && (S->cols[colno].coltype == SQL_VARCHAR || S->cols[colno].coltype == SQL_LONGVARCHAR ||\n#ifdef SQL_WVARCHAR\n                         S->cols[colno].coltype == SQL_WVARCHAR ||\n#endif\n#ifdef SQL_WLONGVARCHAR\n                         S->cols[colno].coltype == SQL_WLONGVARCHAR ||\n#endif\n                         S->cols[colno].coltype == SQL_VARBINARY || S->cols[colno].coltype == SQL_LONGVARBINARY)) {\n        S->going_long = 1;\n    }\n\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_stmt_error(\"SQLDescribeCol\");\n        if (rc != SQL_SUCCESS_WITH_INFO) {\n            return 0;\n        }\n    }\n\n    rc = SQLColAttribute(S->stmt, colno + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &displaysize);\n\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_stmt_error(\"SQLColAttribute\");\n        if (rc != SQL_SUCCESS_WITH_INFO) {\n            return 0;\n        }\n    }\n    colsize = displaysize;\n\n    col->maxlen = S->cols[colno].datalen = colsize;\n    col->name = zend_string_init(S->cols[colno].colname, colnamelen, 0);\n    S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype);\n\n    /* tell ODBC to put it straight into our buffer, but only if it\n     * isn't \"long\" data, and only if we haven't already bound a long\n     * column. */\n    if (colsize < 256 && !S->going_long) {\n        S->cols[colno].data = emalloc(colsize + 1);\n        S->cols[colno].is_long = 0;\n\n        rc = SQLBindCol(S->stmt,\n                        colno + 1,\n                        S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR,\n                        S->cols[colno].data,\n                        S->cols[colno].datalen + 1,\n                        &S->cols[colno].fetched_len);\n\n        if (rc != SQL_SUCCESS) {\n            pdo_odbc_stmt_error(\"SQLBindCol\");\n            return 0;\n        }\n    } else {\n        /* allocate a smaller buffer to keep around for smaller\n         * \"long\" columns */\n        S->cols[colno].data = emalloc(256);\n        S->going_long = 1;\n        S->cols[colno].is_long = 1;\n    }\n\n    return 1;\n}\n\nstatic int odbc_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) {\n    array_init(return_value);\n    add_assoc_long(return_value, \"pdo_type\", PDO_PARAM_STR);\n    return 1;\n}\n\nstatic int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) {\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n    pdo_odbc_column *C = &S->cols[colno];\n\n    /* if it is a column containing \"long\" data, perform late binding now */\n    if (C->is_long) {\n        SQLLEN orig_fetched_len = SQL_NULL_DATA;\n        RETCODE rc;\n\n        /* fetch it into C->data, which is allocated with a length\n         * of 256 bytes; if there is more to be had, we then allocate\n         * bigger buffer for the caller to free */\n\n        rc = SQLGetData(S->stmt, colno + 1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data, 256, &C->fetched_len);\n        orig_fetched_len = C->fetched_len;\n\n        if (rc == SQL_SUCCESS && C->fetched_len < 256) {\n            /* all the data fit into our little buffer;\n             * jump down to the generic bound data case */\n            goto in_data;\n        }\n\n        if (rc == SQL_SUCCESS_WITH_INFO || rc == SQL_SUCCESS) {\n            /* this is a 'long column'\n\n             read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks\n             in order into the output buffer; 255 bytes are an optimistic assumption, since the driver may assert\n             more or less NUL bytes at the end; we cater to that later, if actual length information is available\n\n             this loop has to work whether or not SQLGetData() provides the total column length.\n             calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read\n             for that size would be slower except maybe for extremely long columns.*/\n            char *buf2 = emalloc(256);\n            zend_string *str = zend_string_init(C->data, 256, 0);\n            size_t used = 255; /* not 256; the driver NUL terminated the buffer */\n\n            do {\n                C->fetched_len = 0;\n                /* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */\n                rc = SQLGetData(\n                    S->stmt, colno + 1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, buf2, 256, &C->fetched_len);\n\n                /* adjust `used` in case we have length info from the driver */\n                if (orig_fetched_len >= 0 && C->fetched_len >= 0) {\n                    SQLLEN fixed_used = orig_fetched_len - C->fetched_len;\n                    ZEND_ASSERT(fixed_used <= used + 1);\n                    used = fixed_used;\n                }\n\n                /* resize output buffer and reassemble block */\n                if (rc == SQL_SUCCESS_WITH_INFO || (rc == SQL_SUCCESS && C->fetched_len > 255)) {\n                    /* point 5, in section \"Retrieving Data with SQLGetData\" in\n                     http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx states that if\n                     SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size) (if a driver fails to\n                     follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */\n                    str = zend_string_realloc(str, used + 256, 0);\n                    memcpy(ZSTR_VAL(str) + used, buf2, 256);\n                    used = used + 255;\n                } else if (rc == SQL_SUCCESS) {\n                    str = zend_string_realloc(str, used + C->fetched_len, 0);\n                    memcpy(ZSTR_VAL(str) + used, buf2, C->fetched_len);\n                    used = used + C->fetched_len;\n                } else {\n                    /* includes SQL_NO_DATA */\n                    break;\n                }\n\n            } while (1);\n\n            efree(buf2);\n\n            /* NULL terminate the buffer once, when finished, for use with the rest of PHP */\n            ZSTR_VAL(str)[used] = '\\0';\n            ZVAL_STR(result, str);\n            if (C->is_unicode) {\n                goto unicode_conv;\n            }\n            return 1;\n        }\n\n        /* something went caca */\n        return 1;\n    }\n\nin_data:\n    /* check the indicator to ensure that the data is intact */\n    if (C->fetched_len == SQL_NULL_DATA) {\n        /* A NULL value */\n        ZVAL_NULL(result);\n        return 1;\n    } else if (C->fetched_len >= 0) {\n        /* it was stored perfectly */\n        ZVAL_STRINGL_FAST(result, C->data, C->fetched_len);\n        if (C->is_unicode) {\n            goto unicode_conv;\n        }\n        return 1;\n    } else {\n        /* no data? */\n        ZVAL_NULL(result);\n        return 1;\n    }\n\nunicode_conv:\n    switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, result)) {\n    case PDO_ODBC_CONV_FAIL:\n        /* oh well.  They can have the binary version of it */\n    case PDO_ODBC_CONV_NOT_REQUIRED:\n        /* shouldn't happen... */\n        return 1;\n    case PDO_ODBC_CONV_OK:\n        return 1;\n    }\n    return 1;\n}\n\nstatic int odbc_stmt_set_param(pdo_stmt_t *stmt, zend_long attr, zval *val) {\n    SQLRETURN rc;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    switch (attr) {\n    case PDO_ATTR_CURSOR_NAME:\n        convert_to_string(val);\n        rc = SQLSetCursorName(S->stmt, (SQLCHAR *) Z_STRVAL_P(val), Z_STRLEN_P(val));\n\n        if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {\n            return 1;\n        }\n        pdo_odbc_stmt_error(\"SQLSetCursorName\");\n        return 0;\n\n    case PDO_ODBC_ATTR_ASSUME_UTF8:\n        S->assume_utf8 = zval_is_true(val);\n        return 0;\n    default:\n        strcpy(S->einfo.last_err_msg, \"Unknown Attribute\");\n        S->einfo.what = \"setAttribute\";\n        strcpy(S->einfo.last_state, \"IM001\");\n        return -1;\n    }\n}\n\nstatic int odbc_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val) {\n    SQLRETURN rc;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    switch (attr) {\n    case PDO_ATTR_CURSOR_NAME: {\n        char buf[256];\n        SQLSMALLINT len = 0;\n        rc = SQLGetCursorName(S->stmt, (SQLCHAR *) buf, sizeof(buf), &len);\n\n        if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {\n            ZVAL_STRINGL(val, buf, len);\n            return 1;\n        }\n        pdo_odbc_stmt_error(\"SQLGetCursorName\");\n        return 0;\n    }\n\n    case PDO_ODBC_ATTR_ASSUME_UTF8:\n        ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0);\n        return 0;\n\n    default:\n        strcpy(S->einfo.last_err_msg, \"Unknown Attribute\");\n        S->einfo.what = \"getAttribute\";\n        strcpy(S->einfo.last_state, \"IM001\");\n        return -1;\n    }\n}\n\nstatic int odbc_stmt_next_rowset(pdo_stmt_t *stmt) {\n    SQLRETURN rc;\n    SQLSMALLINT colcount;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    /* NOTE: can't guarantee that output or input/output parameters\n     * are set until this fella returns SQL_NO_DATA, according to\n     * MSDN ODBC docs */\n    rc = SQLMoreResults(S->stmt);\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        return 0;\n    }\n\n    free_cols(stmt, S);\n    /* how many columns do we have ? */\n    SQLNumResultCols(S->stmt, &colcount);\n    stmt->column_count = S->col_count = (int) colcount;\n    S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));\n    S->going_long = 0;\n\n    return 1;\n}\n\nstatic int odbc_stmt_close_cursor(pdo_stmt_t *stmt) {\n    SQLRETURN rc;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    rc = SQLCloseCursor(S->stmt);\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        return 0;\n    }\n    return 1;\n}\n\nconst struct pdo_stmt_methods odbc_stmt_methods = {odbc_stmt_dtor,\n                                                   odbc_stmt_execute,\n                                                   odbc_stmt_fetch,\n                                                   odbc_stmt_describe,\n                                                   odbc_stmt_get_col,\n                                                   odbc_stmt_param_hook,\n                                                   odbc_stmt_set_param,\n                                                   odbc_stmt_get_attr,\n                                                   odbc_stmt_get_column_meta,\n                                                   odbc_stmt_next_rowset,\n                                                   odbc_stmt_close_cursor};\n#endif\n"
  },
  {
    "path": "thirdparty/php83/pdo_odbc/php_pdo_odbc_int.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#ifdef PHP_WIN32\n# define PDO_ODBC_TYPE\t\"Win32\"\n#endif\n\n#ifndef PDO_ODBC_TYPE\n# warning Please fix configure to give your ODBC libraries a name\n# define PDO_ODBC_TYPE\t\"Unknown\"\n#endif\n\n/* {{{ Roll a dice, pick a header at random... */\n#ifdef HAVE_SQLCLI1_H\n# include <sqlcli1.h>\n# if defined(DB268K) && HAVE_LIBRARYMANAGER_H\n#  include <LibraryManager.h>\n# endif\n#endif\n\n#ifdef HAVE_ODBC_H\n# include <odbc.h>\n#endif\n\n#ifdef HAVE_IODBC_H\n# include <iodbc.h>\n#endif\n\n#if defined(HAVE_SQLUNIX_H) && !defined(PHP_WIN32)\n# include <sqlunix.h>\n#endif\n\n#ifdef HAVE_SQLTYPES_H\n# include <sqltypes.h>\n#endif\n\n#ifdef HAVE_SQLUCODE_H\n# include <sqlucode.h>\n#endif\n\n#ifdef HAVE_SQL_H\n# include <sql.h>\n#endif\n\n#ifdef HAVE_ISQL_H\n# include <isql.h>\n#endif\n\n#ifdef HAVE_SQLEXT_H\n# include <sqlext.h>\n#endif\n\n#ifdef HAVE_ISQLEXT_H\n# include <isqlext.h>\n#endif\n\n#ifdef HAVE_UDBCEXT_H\n# include <udbcext.h>\n#endif\n\n#ifdef HAVE_CLI0CORE_H\n# include <cli0core.h>\n#endif\n\n#ifdef HAVE_CLI0EXT1_H\n# include <cli0ext.h>\n#endif\n\n#ifdef HAVE_CLI0CLI_H\n# include <cli0cli.h>\n#endif\n\n#ifdef HAVE_CLI0DEFS_H\n# include <cli0defs.h>\n#endif\n\n#ifdef HAVE_CLI0ENV_H\n# include <cli0env.h>\n#endif\n\n#ifdef HAVE_ODBCSDK_H\n# include <odbcsdk.h>\n#endif\n\n/* }}} */\n\n/* {{{ Figure out the type for handles */\n#if !defined(HENV) && !defined(SQLHENV) && defined(SQLHANDLE)\n# define PDO_ODBC_HENV\t\tSQLHANDLE\n# define PDO_ODBC_HDBC\t\tSQLHANDLE\n# define PDO_ODBC_HSTMT\t\tSQLHANDLE\n#elif !defined(HENV) && (defined(SQLHENV) || defined(DB2CLI_VER))\n# define PDO_ODBC_HENV\t\tSQLHENV\n# define PDO_ODBC_HDBC\t\tSQLHDBC\n# define PDO_ODBC_HSTMT\t\tSQLHSTMT\n#else\n# define PDO_ODBC_HENV\t\tHENV\n# define PDO_ODBC_HDBC\t\tHDBC\n# define PDO_ODBC_HSTMT\t\tHSTMT\n#endif\n/* }}} */\n\ntypedef struct {\n\tchar last_state[6];\n\tchar last_err_msg[SQL_MAX_MESSAGE_LENGTH];\n\tSDWORD last_error;\n\tconst char *file, *what;\n\tint line;\n} pdo_odbc_errinfo;\n\ntypedef struct {\n\tPDO_ODBC_HENV\tenv;\n\tPDO_ODBC_HDBC\tdbc;\n\tpdo_odbc_errinfo einfo;\n\tunsigned assume_utf8:1;\n\tunsigned _spare:31;\n} pdo_odbc_db_handle;\n\ntypedef struct {\n\tchar *data;\n\tzend_ulong datalen;\n\tSQLLEN fetched_len;\n\tSWORD\tcoltype;\n\tchar colname[128];\n\tunsigned is_long;\n\tunsigned is_unicode:1;\n\tunsigned _spare:31;\n} pdo_odbc_column;\n\ntypedef struct {\n\tPDO_ODBC_HSTMT\tstmt;\n\tpdo_odbc_column *cols;\n\tpdo_odbc_db_handle *H;\n\tpdo_odbc_errinfo einfo;\n\tchar *convbuf;\n\tzend_ulong convbufsize;\n\tunsigned going_long:1;\n\tunsigned assume_utf8:1;\n\tsigned col_count:16;\n\tunsigned _spare:14;\n} pdo_odbc_stmt;\n\ntypedef struct {\n\tSQLLEN len;\n\tSQLSMALLINT paramtype;\n\tchar *outbuf;\n\tunsigned is_unicode:1;\n\tunsigned _spare:31;\n} pdo_odbc_param;\n\nextern const pdo_driver_t pdo_odbc_driver;\nextern const struct pdo_stmt_methods odbc_stmt_methods;\n\nvoid pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line);\n#define pdo_odbc_drv_error(what)\tpdo_odbc_error(dbh, NULL, SQL_NULL_HSTMT, what, __FILE__, __LINE__)\n#define pdo_odbc_stmt_error(what)\tpdo_odbc_error(stmt->dbh, stmt, SQL_NULL_HSTMT, what, __FILE__, __LINE__)\n#define pdo_odbc_doer_error(what)\tpdo_odbc_error(dbh, NULL, stmt, what, __FILE__, __LINE__)\n\nvoid pdo_odbc_init_error_table(void);\nvoid pdo_odbc_fini_error_table(void);\n\n#ifdef SQL_ATTR_CONNECTION_POOLING\nextern zend_ulong pdo_odbc_pool_on;\nextern zend_ulong pdo_odbc_pool_mode;\n#endif\n\nenum {\n\tPDO_ODBC_ATTR_USE_CURSOR_LIBRARY = PDO_ATTR_DRIVER_SPECIFIC,\n\tPDO_ODBC_ATTR_ASSUME_UTF8 /* assume that input strings are UTF-8 when feeding data to unicode columns */\n};\n"
  },
  {
    "path": "thirdparty/php83/pdo_pgsql/pgsql_driver.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Authors: Edin Kadribasic <edink@emini.dk>                            |\n  |          Ilia Alshanestsky <ilia@prohost.org>                        |\n  |          Wez Furlong <wez@php.net>                                   |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_PGSQL_HOOK\n#include \"php_swoole_pgsql.h\"\n\n#if PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400\n\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"ext/standard/php_string.h\"\n#include \"main/php_network.h\"\n#include \"pdo/php_pdo.h\"\n#include \"pdo/php_pdo_driver.h\"\n#include \"pdo/php_pdo_error.h\"\n#include \"ext/standard/file.h\"\n#include \"php_pdo_pgsql_int.h\"\n#include \"zend_exceptions.h\"\n#include \"pgsql_driver_arginfo.h\"\n\nstatic bool pgsql_handle_in_transaction(pdo_dbh_t *dbh);\n\nstatic char * _pdo_pgsql_trim_message(const char *message, int persistent)\n{\n\tsize_t i = strlen(message)-1;\n\tchar *tmp;\n\n\tif (i>1 && (message[i-1] == '\\r' || message[i-1] == '\\n') && message[i] == '.') {\n\t\t--i;\n\t}\n\twhile (i>0 && (message[i] == '\\r' || message[i] == '\\n')) {\n\t\t--i;\n\t}\n\t++i;\n\ttmp = pemalloc(i + 1, persistent);\n\tmemcpy(tmp, message, i);\n\ttmp[i] = '\\0';\n\n\treturn tmp;\n}\n\nstatic zend_string* _pdo_pgsql_escape_credentials(char *str)\n{\n\tif (str) {\n\t\treturn php_addcslashes_str(str, strlen(str), \"\\\\'\", sizeof(\"\\\\'\"));\n\t}\n\n\treturn NULL;\n}\n\nint _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line) /* {{{ */\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tpdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code;\n\tpdo_pgsql_error_info *einfo = &H->einfo;\n\tchar *errmsg = PQerrorMessage(H->server);\n\n\teinfo->errcode = errcode;\n\teinfo->file = file;\n\teinfo->line = line;\n\n\tif (einfo->errmsg) {\n\t\tpefree(einfo->errmsg, dbh->is_persistent);\n\t\teinfo->errmsg = NULL;\n\t}\n\n\tif (sqlstate == NULL || strlen(sqlstate) >= sizeof(pdo_error_type)) {\n\t\tstrcpy(*pdo_err, \"HY000\");\n\t}\n\telse {\n\t\tstrcpy(*pdo_err, sqlstate);\n\t}\n\n\tif (msg) {\n\t\teinfo->errmsg = pestrdup(msg, dbh->is_persistent);\n\t}\n\telse if (errmsg) {\n\t\teinfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent);\n\t}\n\n\tif (!dbh->methods) {\n\t\tpdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err);\n\t}\n\n\treturn errcode;\n}\n/* }}} */\n\nstatic void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */\n{\n/*\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */\n}\n/* }}} */\n\nstatic void pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tpdo_pgsql_error_info *einfo = &H->einfo;\n\n\tif (einfo->errcode) {\n\t\tadd_next_index_long(info, einfo->errcode);\n\t} else {\n\t\t/* Add null to respect expected info array structure */\n\t\tadd_next_index_null(info);\n\t}\n\tif (einfo->errmsg) {\n\t\tadd_next_index_string(info, einfo->errmsg);\n\t}\n}\n/* }}} */\n\n/* {{{ pdo_pgsql_create_lob_stream */\nstatic ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count)\n{\n\tstruct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;\n\treturn lo_write(self->conn, self->lfd, (char*)buf, count);\n}\n\nstatic ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count)\n{\n\tstruct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;\n\treturn lo_read(self->conn, self->lfd, buf, count);\n}\n\nstatic int pgsql_lob_close(php_stream *stream, int close_handle)\n{\n\tstruct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(&self->dbh))->driver_data;\n\n\tif (close_handle) {\n\t\tlo_close(self->conn, self->lfd);\n\t}\n\tzend_hash_index_del(H->lob_streams, php_stream_get_resource_id(stream));\n\tzval_ptr_dtor(&self->dbh);\n\tefree(self);\n\treturn 0;\n}\n\nstatic int pgsql_lob_flush(php_stream *stream)\n{\n\treturn 0;\n}\n\nstatic int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence,\n\t\tzend_off_t *newoffset)\n{\n\tstruct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;\n#if defined(HAVE_PG_LO64) && defined(ZEND_ENABLE_ZVAL_LONG64)\n\tzend_off_t pos = lo_lseek64(self->conn, self->lfd, offset, whence);\n#else\n\tzend_off_t pos = lo_lseek(self->conn, self->lfd, offset, whence);\n#endif\n\t*newoffset = pos;\n\treturn pos >= 0 ? 0 : -1;\n}\n\nconst php_stream_ops pdo_pgsql_lob_stream_ops = {\n\tpgsql_lob_write,\n\tpgsql_lob_read,\n\tpgsql_lob_close,\n\tpgsql_lob_flush,\n\t\"pdo_pgsql lob stream\",\n\tpgsql_lob_seek,\n\tNULL,\n\tNULL,\n\tNULL\n};\n\nphp_stream *pdo_pgsql_create_lob_stream(zval *dbh, int lfd, Oid oid)\n{\n\tphp_stream *stm;\n\tstruct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self));\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(dbh))->driver_data;\n\n\tZVAL_COPY_VALUE(&self->dbh, dbh);\n\tself->lfd = lfd;\n\tself->oid = oid;\n\tself->conn = H->server;\n\n\tstm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, \"r+b\");\n\n\tif (stm) {\n\t\tZ_ADDREF_P(dbh);\n\t\tzend_hash_index_add_ptr(H->lob_streams, php_stream_get_resource_id(stm), stm->res);\n\t\treturn stm;\n\t}\n\n\tefree(self);\n\treturn NULL;\n}\n/* }}} */\n\nvoid pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh)\n{\n\tzend_resource *res;\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tif (H->lob_streams) {\n\t\tZEND_HASH_REVERSE_FOREACH_PTR(H->lob_streams, res) {\n\t\t\tif (res->type >= 0) {\n\t\t\t\tzend_list_close(res);\n\t\t\t}\n\t\t} ZEND_HASH_FOREACH_END();\n\t}\n}\n\nstatic void pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tif (H) {\n\t\tif (H->lob_streams) {\n\t\t\tpdo_pgsql_close_lob_streams(dbh);\n\t\t\tzend_hash_destroy(H->lob_streams);\n\t\t\tpefree(H->lob_streams, dbh->is_persistent);\n\t\t\tH->lob_streams = NULL;\n\t\t}\n\t\tif (H->server) {\n\t\t\tPQfinish(H->server);\n\t\t\tH->server = NULL;\n\t\t}\n\t\tif (H->einfo.errmsg) {\n\t\t\tpefree(H->einfo.errmsg, dbh->is_persistent);\n\t\t\tH->einfo.errmsg = NULL;\n\t\t}\n\t\tpefree(H, dbh->is_persistent);\n\t\tdbh->driver_data = NULL;\n\t}\n}\n/* }}} */\n\nstatic bool pgsql_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tpdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt));\n\tint scrollable;\n\tint ret;\n\tzend_string *nsql = NULL;\n\tint emulate = 0;\n\tint execute_only = 0;\n\n\tS->H = H;\n\tstmt->driver_data = S;\n\tstmt->methods = &swoole_pgsql_stmt_methods;\n\n\tscrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR,\n\t\tPDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL;\n\n\tif (scrollable) {\n\t\tif (S->cursor_name) {\n\t\t\tefree(S->cursor_name);\n\t\t}\n\t\tspprintf(&S->cursor_name, 0, \"pdo_crsr_%08x\", ++H->stmt_counter);\n\t\temulate = 1;\n\t} else if (driver_options) {\n\t\tif (pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares) == 1) {\n\t\t\temulate = 1;\n\t\t}\n\t\tif (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_PREPARES, H->disable_prepares) == 1) {\n\t\t\texecute_only = 1;\n\t\t}\n\t} else {\n\t\temulate = H->disable_native_prepares || H->emulate_prepares;\n\t\texecute_only = H->disable_prepares;\n\t}\n\n\tif (!emulate && PQprotocolVersion(H->server) <= 2) {\n\t\temulate = 1;\n\t}\n\n\tif (emulate) {\n\t\tstmt->supports_placeholders = PDO_PLACEHOLDER_NONE;\n\t} else {\n\t\tstmt->supports_placeholders = PDO_PLACEHOLDER_NAMED;\n\t\tstmt->named_rewrite_template = \"$%d\";\n\t}\n\n\tret = pdo_parse_params(stmt, sql, &nsql);\n\n\tif (ret == -1) {\n\t\t/* couldn't grok it */\n\t\tstrcpy(dbh->error_code, stmt->error_code);\n\t\treturn false;\n\t} else if (ret == 1) {\n\t\t/* query was re-written */\n\t\tS->query = nsql;\n\t} else {\n\t\tS->query = zend_string_copy(sql);\n\t}\n\n\tif (!emulate && !execute_only) {\n\t\t/* prepared query: set the query name and defer the\n\t\t   actual prepare until the first execute call */\n\t\tspprintf(&S->stmt_name, 0, \"pdo_stmt_%08x\", ++H->stmt_counter);\n\t}\n\n\treturn true;\n}\n\nstatic zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const zend_string *sql)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tPGresult *res;\n\tzend_long ret = 1;\n\tExecStatusType qs;\n\n\tbool in_trans = pgsql_handle_in_transaction(dbh);\n\n\tif (!(res = PQexec(H->server, ZSTR_VAL(sql)))) {\n\t\t/* fatal error */\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\treturn -1;\n\t}\n\tqs = PQresultStatus(res);\n\tif (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) {\n\t\tpdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res));\n\t\tPQclear(res);\n\t\treturn -1;\n\t}\n\tH->pgoid = PQoidValue(res);\n\tif (qs == PGRES_COMMAND_OK) {\n\t\tret = ZEND_ATOL(PQcmdTuples(res));\n\t} else {\n\t\tret = Z_L(0);\n\t}\n\tPQclear(res);\n\tif (in_trans && !pgsql_handle_in_transaction(dbh)) {\n\t\tpdo_pgsql_close_lob_streams(dbh);\n\t}\n\n\treturn ret;\n}\n\nstatic zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype)\n{\n\tunsigned char *escaped;\n\tchar *quoted;\n\tsize_t quotedlen;\n\tzend_string *quoted_str;\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tsize_t tmp_len;\n\n\tswitch (paramtype) {\n\t\tcase PDO_PARAM_LOB:\n\t\t\t/* escapedlen returned by PQescapeBytea() accounts for trailing 0 */\n\t\t\tescaped = PQescapeByteaConn(H->server, (unsigned char *)ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &tmp_len);\n\t\t\tquotedlen = tmp_len + 1;\n\t\t\tquoted = emalloc(quotedlen + 1);\n\t\t\tmemcpy(quoted+1, escaped, quotedlen-2);\n\t\t\tquoted[0] = '\\'';\n\t\t\tquoted[quotedlen-1] = '\\'';\n\t\t\tquoted[quotedlen] = '\\0';\n\t\t\tPQfreemem(escaped);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tquoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3);\n\t\t\tquoted[0] = '\\'';\n\t\t\tquotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), NULL);\n\t\t\tquoted[quotedlen + 1] = '\\'';\n\t\t\tquoted[quotedlen + 2] = '\\0';\n\t\t\tquotedlen += 2;\n\t}\n\n\tquoted_str = zend_string_init(quoted, quotedlen, 0);\n\tefree(quoted);\n\treturn quoted_str;\n}\n\nstatic zend_string *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const zend_string *name)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tzend_string *id = NULL;\n\tPGresult *res;\n\tExecStatusType status;\n\n\tif (name == NULL) {\n\t\tres = PQexec(H->server, \"SELECT LASTVAL()\");\n\t} else {\n\t\tconst char *q[1];\n\t\tq[0] = ZSTR_VAL(name);\n\n\t\tres = PQexecParams(H->server, \"SELECT CURRVAL($1)\", 1, NULL, q, NULL, NULL, 0);\n\t}\n\tstatus = PQresultStatus(res);\n\n\tif (res && (status == PGRES_TUPLES_OK)) {\n\t\tid = zend_string_init((char *)PQgetvalue(res, 0, 0), PQgetlength(res, 0, 0), 0);\n\t} else {\n\t\tpdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res));\n\t}\n\n\tif (res) {\n\t\tPQclear(res);\n\t}\n\n\treturn id;\n}\n\nvoid pdo_libpq_version(char *buf, size_t len)\n{\n\tint version = PQlibVersion();\n\tint major = version / 10000;\n\tif (major >= 10) {\n\t\tint minor = version % 10000;\n\t\tsnprintf(buf, len, \"%d.%d\", major, minor);\n\t} else {\n\t\tint minor = version / 100 % 100;\n\t\tint revision = version % 100;\n\t\tsnprintf(buf, len, \"%d.%d.%d\", major, minor, revision);\n\t}\n}\n\nstatic int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tswitch (attr) {\n\t\tcase PDO_ATTR_EMULATE_PREPARES:\n\t\t\tZVAL_BOOL(return_value, H->emulate_prepares);\n\t\t\tbreak;\n\n\t\tcase PDO_PGSQL_ATTR_DISABLE_PREPARES:\n\t\t\tZVAL_BOOL(return_value, H->disable_prepares);\n\t\t\tbreak;\n\n\t\tcase PDO_ATTR_CLIENT_VERSION: {\n\t\t\tchar buf[16];\n\t\t\tpdo_libpq_version(buf, sizeof(buf));\n\t\t\tZVAL_STRING(return_value, buf);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase PDO_ATTR_SERVER_VERSION:\n\t\t\tif (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */\n\t\t\t\tZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, \"server_version\"));\n\t\t\t} else /* emulate above via a query */\n\t\t\t{\n\t\t\t\tPGresult *res = PQexec(H->server, \"SELECT VERSION()\");\n\t\t\t\tif (res && PQresultStatus(res) == PGRES_TUPLES_OK) {\n\t\t\t\t\tZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0));\n\t\t\t\t}\n\n\t\t\t\tif (res) {\n\t\t\t\t\tPQclear(res);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase PDO_ATTR_CONNECTION_STATUS:\n\t\t\tswitch (PQstatus(H->server)) {\n\t\t\t\tcase CONNECTION_STARTED:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Waiting for connection to be made.\", strlen(\"Waiting for connection to be made.\"));\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase CONNECTION_MADE:\n\t\t\t\tcase CONNECTION_OK:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Connection OK; waiting to send.\", strlen(\"Connection OK; waiting to send.\"));\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase CONNECTION_AWAITING_RESPONSE:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Waiting for a response from the server.\", strlen(\"Waiting for a response from the server.\"));\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase CONNECTION_AUTH_OK:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Received authentication; waiting for backend start-up to finish.\", strlen(\"Received authentication; waiting for backend start-up to finish.\"));\n\t\t\t\t\tbreak;\n#ifdef CONNECTION_SSL_STARTUP\n\t\t\t\tcase CONNECTION_SSL_STARTUP:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Negotiating SSL encryption.\", strlen(\"Negotiating SSL encryption.\"));\n\t\t\t\t\tbreak;\n#endif\n\t\t\t\tcase CONNECTION_SETENV:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Negotiating environment-driven parameter settings.\", strlen(\"Negotiating environment-driven parameter settings.\"));\n\t\t\t\t\tbreak;\n\n#ifdef CONNECTION_CONSUME\n\t\t\t\tcase CONNECTION_CONSUME:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Flushing send queue/consuming extra data.\", strlen(\"Flushing send queue/consuming extra data.\"));\n\t\t\t\t\tbreak;\n#endif\n#ifdef CONNECTION_GSS_STARTUP\n\t\t\t\tcase CONNECTION_SSL_STARTUP:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Negotiating GSSAPI.\", strlen(\"Negotiating GSSAPI.\"));\n\t\t\t\t\tbreak;\n#endif\n#ifdef CONNECTION_CHECK_TARGET\n\t\t\t\tcase CONNECTION_CHECK_TARGET:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Connection OK; checking target server properties.\", strlen(\"Connection OK; checking target server properties.\"));\n\t\t\t\t\tbreak;\n#endif\n#ifdef CONNECTION_CHECK_STANDBY\n\t\t\t\tcase CONNECTION_CHECK_STANDBY:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Connection OK; checking if server in standby.\", strlen(\"Connection OK; checking if server in standby.\"));\n\t\t\t\t\tbreak;\n#endif\n\t\t\t\tcase CONNECTION_BAD:\n\t\t\t\tdefault:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Bad connection.\", strlen(\"Bad connection.\"));\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase PDO_ATTR_SERVER_INFO: {\n\t\t\tint spid = PQbackendPID(H->server);\n\n\n\t\t\tzend_string *str_info =\n\t\t\t\tstrpprintf(0,\n\t\t\t\t\t\"PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s\",\n\t\t\t\t\tspid,\n\t\t\t\t\t(char*)PQparameterStatus(H->server, \"client_encoding\"),\n\t\t\t\t\t(char*)PQparameterStatus(H->server, \"is_superuser\"),\n\t\t\t\t\t(char*)PQparameterStatus(H->server, \"session_authorization\"),\n\t\t\t\t\t(char*)PQparameterStatus(H->server, \"DateStyle\"));\n\n\t\t\tZVAL_STR(return_value, str_info);\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\n/* {{{ */\nstatic zend_result pdo_pgsql_check_liveness(pdo_dbh_t *dbh)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tif (!PQconsumeInput(H->server) || PQstatus(H->server) == CONNECTION_BAD) {\n\t\tPQreset(H->server);\n\t}\n\treturn (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE;\n}\n/* }}} */\n\nstatic bool pgsql_handle_in_transaction(pdo_dbh_t *dbh)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\treturn PQtransactionStatus(H->server) > PQTRANS_IDLE;\n}\n\nstatic bool pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tPGresult *res;\n\tbool ret = true;\n\n\tres = PQexec(H->server, cmd);\n\n\tif (PQresultStatus(res) != PGRES_COMMAND_OK) {\n\t\tpdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res));\n\t\tret = false;\n\t}\n\n\tPQclear(res);\n\treturn ret;\n}\n\nstatic bool pgsql_handle_begin(pdo_dbh_t *dbh)\n{\n\treturn pdo_pgsql_transaction_cmd(\"BEGIN\", dbh);\n}\n\nstatic bool pgsql_handle_commit(pdo_dbh_t *dbh)\n{\n\tbool ret = pdo_pgsql_transaction_cmd(\"COMMIT\", dbh);\n\n\t/* When deferred constraints are used the commit could\n\t   fail, and a ROLLBACK implicitly ran. See bug #67462 */\n\tif (ret) {\n\t\tpdo_pgsql_close_lob_streams(dbh);\n\t} else {\n\t\tdbh->in_txn = pgsql_handle_in_transaction(dbh);\n\t}\n\n\treturn ret;\n}\n\nstatic bool pgsql_handle_rollback(pdo_dbh_t *dbh)\n{\n\tint ret = pdo_pgsql_transaction_cmd(\"ROLLBACK\", dbh);\n\n\tif (ret) {\n\t\tpdo_pgsql_close_lob_streams(dbh);\n\t}\n\n\treturn ret;\n}\n\n/* {{{ Returns true if the copy worked fine or false if error */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\n\tzval *pg_rows;\n\n\tchar *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;\n\tsize_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;\n\tchar *query;\n\n\tPGresult *pgsql_result;\n\tExecStatusType status;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"sa|sss!\",\n\t\t\t\t\t&table_name, &table_name_len, &pg_rows,\n\t\t\t\t\t&pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\n\tif (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) {\n\t\tzend_argument_value_error(2, \"cannot be empty\");\n\t\tRETURN_THROWS();\n\t}\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\t/* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */\n\tif (pg_fields) {\n\t\tspprintf(&query, 0, \"COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t} else {\n\t\tspprintf(&query, 0, \"COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t}\n\n\t/* Obtain db Handle */\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\tPQclear(pgsql_result);\n\t}\n\tpgsql_result = PQexec(H->server, query);\n\n\tefree(query);\n\tquery = NULL;\n\n\tif (pgsql_result) {\n\t\tstatus = PQresultStatus(pgsql_result);\n\t} else {\n\t\tstatus = (ExecStatusType) PQstatus(H->server);\n\t}\n\n\tif (status == PGRES_COPY_IN && pgsql_result) {\n\t\tint command_failed = 0;\n\t\tsize_t buffer_len = 0;\n\t\tzval *tmp;\n\n\t\tPQclear(pgsql_result);\n\t\tZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) {\n\t\t\tsize_t query_len;\n\t\t\tif (!try_convert_to_string(tmp)) {\n\t\t\t\tefree(query);\n\t\t\t\tRETURN_THROWS();\n\t\t\t}\n\n\t\t\tif (buffer_len < Z_STRLEN_P(tmp)) {\n\t\t\t\tbuffer_len = Z_STRLEN_P(tmp);\n\t\t\t\tquery = erealloc(query, buffer_len + 2); /* room for \\n\\0 */\n\t\t\t}\n\t\t\tmemcpy(query, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));\n\t\t\tquery_len = Z_STRLEN_P(tmp);\n\t\t\tif (query[query_len - 1] != '\\n') {\n\t\t\t\tquery[query_len++] = '\\n';\n\t\t\t}\n\t\t\tquery[query_len] = '\\0';\n\t\t\tif (PQputCopyData(H->server, query, query_len) != 1) {\n\t\t\t\tefree(query);\n\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\t\tRETURN_FALSE;\n\t\t\t}\n\t\t} ZEND_HASH_FOREACH_END();\n\t\tif (query) {\n\t\t\tefree(query);\n\t\t}\n\n\t\tif (PQputCopyEnd(H->server, NULL) != 1) {\n\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\tRETURN_FALSE;\n\t\t}\n\n\t\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\t\tif (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {\n\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\t\t\tcommand_failed = 1;\n\t\t\t}\n\t\t\tPQclear(pgsql_result);\n\t\t}\n\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_BOOL(!command_failed);\n\t} else {\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\tPQclear(pgsql_result);\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n}\n/* }}} */\n\n/* {{{ Returns true if the copy worked fine or false if error */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\n\tchar *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;\n\tsize_t  table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;\n\tchar *query;\n\tPGresult *pgsql_result;\n\tExecStatusType status;\n\tphp_stream *stream;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"sp|sss!\",\n\t\t\t\t&table_name, &table_name_len, &filename, &filename_len,\n\t\t\t\t&pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\n\t/* Obtain db Handler */\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tstream = php_stream_open_wrapper_ex(filename, \"rb\", 0, NULL, FG(default_context));\n\tif (!stream) {\n\t\tpdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, \"Unable to open the file\");\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n\n\t/* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */\n\tif (pg_fields) {\n\t\tspprintf(&query, 0, \"COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t} else {\n\t\tspprintf(&query, 0, \"COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t}\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\tPQclear(pgsql_result);\n\t}\n\tpgsql_result = PQexec(H->server, query);\n\n\tefree(query);\n\n\tif (pgsql_result) {\n\t\tstatus = PQresultStatus(pgsql_result);\n\t} else {\n\t\tstatus = (ExecStatusType) PQstatus(H->server);\n\t}\n\n\tif (status == PGRES_COPY_IN && pgsql_result) {\n\t\tchar *buf;\n\t\tint command_failed = 0;\n\t\tsize_t line_len = 0;\n\n\t\tPQclear(pgsql_result);\n\t\twhile ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) {\n\t\t\tif (PQputCopyData(H->server, buf, line_len) != 1) {\n\t\t\t\tefree(buf);\n\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\t\tphp_stream_close(stream);\n\t\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\t\tRETURN_FALSE;\n\t\t\t}\n\t\t\tefree(buf);\n\t\t}\n\t\tphp_stream_close(stream);\n\n\t\tif (PQputCopyEnd(H->server, NULL) != 1) {\n\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\tRETURN_FALSE;\n\t\t}\n\n\t\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\t\tif (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {\n\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\t\t\tcommand_failed = 1;\n\t\t\t}\n\t\t\tPQclear(pgsql_result);\n\t\t}\n\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_BOOL(!command_failed);\n\t} else {\n\t\tphp_stream_close(stream);\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\tPQclear(pgsql_result);\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n}\n/* }}} */\n\n\n/* {{{ Returns true if the copy worked fine or false if error */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\n\tchar *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL;\n\tsize_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len;\n\tchar *query;\n\n\tPGresult *pgsql_result;\n\tExecStatusType status;\n\n\tphp_stream *stream;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"sp|sss!\",\n\t\t\t\t\t&table_name, &table_name_len, &filename, &filename_len,\n\t\t\t\t\t&pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tstream = php_stream_open_wrapper_ex(filename, \"wb\", 0, NULL, FG(default_context));\n\tif (!stream) {\n\t\tpdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, \"Unable to open the file for writing\");\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n\n\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\tPQclear(pgsql_result);\n\t}\n\n\t/* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */\n\tif (pg_fields) {\n\t\tspprintf(&query, 0, \"COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t} else {\n\t\tspprintf(&query, 0, \"COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t}\n\tpgsql_result = PQexec(H->server, query);\n\tefree(query);\n\n\tif (pgsql_result) {\n\t\tstatus = PQresultStatus(pgsql_result);\n\t} else {\n\t\tstatus = (ExecStatusType) PQstatus(H->server);\n\t}\n\n\tif (status == PGRES_COPY_OUT && pgsql_result) {\n\t\tPQclear(pgsql_result);\n\t\twhile (1) {\n\t\t\tchar *csv = NULL;\n\t\t\tint ret = PQgetCopyData(H->server, &csv, 0);\n\n\t\t\tif (ret == -1) {\n\t\t\t\tbreak; /* done */\n\t\t\t} else if (ret > 0) {\n\t\t\t\tif (php_stream_write(stream, csv, ret) != (size_t)ret) {\n\t\t\t\t\tpdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, \"Unable to write to file\");\n\t\t\t\t\tPQfreemem(csv);\n\t\t\t\t\tphp_stream_close(stream);\n\t\t\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\t\t\tRETURN_FALSE;\n\t\t\t\t} else {\n\t\t\t\t\tPQfreemem(csv);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\t\tphp_stream_close(stream);\n\t\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\t\tRETURN_FALSE;\n\t\t\t}\n\t\t}\n\t\tphp_stream_close(stream);\n\n\t\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\t\tPQclear(pgsql_result);\n\t\t}\n\t\tRETURN_TRUE;\n\t} else {\n\t\tphp_stream_close(stream);\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\tPQclear(pgsql_result);\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n}\n/* }}} */\n\n/* {{{ Returns true if the copy worked fine or false if error */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\n\tchar *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;\n\tsize_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;\n\tchar *query;\n\n\tPGresult *pgsql_result;\n\tExecStatusType status;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"s|sss!\",\n\t\t&table_name, &table_name_len,\n\t\t&pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\tPQclear(pgsql_result);\n\t}\n\n\t/* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */\n\tif (pg_fields) {\n\t\tspprintf(&query, 0, \"COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t} else {\n\t\tspprintf(&query, 0, \"COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t}\n\tpgsql_result = PQexec(H->server, query);\n\tefree(query);\n\n\tif (pgsql_result) {\n\t\tstatus = PQresultStatus(pgsql_result);\n\t} else {\n\t\tstatus = (ExecStatusType) PQstatus(H->server);\n\t}\n\n\tif (status == PGRES_COPY_OUT && pgsql_result) {\n\t\tPQclear(pgsql_result);\n                array_init(return_value);\n\n\t\twhile (1) {\n\t\t\tchar *csv = NULL;\n\t\t\tint ret = PQgetCopyData(H->server, &csv, 0);\n\t\t\tif (ret == -1) {\n\t\t\t\tbreak; /* copy done */\n\t\t\t} else if (ret > 0) {\n\t\t\t\tadd_next_index_stringl(return_value, csv, ret);\n\t\t\t\tPQfreemem(csv);\n\t\t\t} else {\n\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\t\tRETURN_FALSE;\n\t\t\t}\n\t\t}\n\n\t\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\t\tPQclear(pgsql_result);\n\t\t}\n\t} else {\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\tPQclear(pgsql_result);\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n}\n/* }}} */\n\n\n/* {{{ Creates a new large object, returning its identifier.  Must be called inside a transaction. */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\tOid lfd;\n\n\tZEND_PARSE_PARAMETERS_NONE();\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tlfd = lo_creat(H->server, INV_READ|INV_WRITE);\n\n\tif (lfd != InvalidOid) {\n\t\tzend_string *buf = strpprintf(0, ZEND_ULONG_FMT, (zend_long) lfd);\n\n\t\tRETURN_STR(buf);\n\t}\n\n\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\tPDO_HANDLE_DBH_ERR();\n\tRETURN_FALSE;\n}\n/* }}} */\n\n/* {{{ Opens an existing large object stream.  Must be called inside a transaction. */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\tOid oid;\n\tint lfd;\n\tchar *oidstr;\n\tsize_t oidstrlen;\n\tchar *modestr = \"rb\";\n\tsize_t modestrlen;\n\tint mode = INV_READ;\n\tchar *end_ptr;\n\n\tif (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), \"s|s\",\n\t\t\t\t&oidstr, &oidstrlen, &modestr, &modestrlen)) {\n\t\tRETURN_THROWS();\n\t}\n\n\toid = (Oid)strtoul(oidstr, &end_ptr, 10);\n\tif (oid == 0 && (errno == ERANGE || errno == EINVAL)) {\n\t\tRETURN_FALSE;\n\t}\n\n\tif (strpbrk(modestr, \"+w\")) {\n\t\tmode = INV_READ|INV_WRITE;\n\t}\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tlfd = lo_open(H->server, oid, mode);\n\n\tif (lfd >= 0) {\n\t\tphp_stream *stream = pdo_pgsql_create_lob_stream(ZEND_THIS, lfd, oid);\n\t\tif (stream) {\n\t\t\tphp_stream_to_zval(stream, return_value);\n\t\t\treturn;\n\t\t}\n\t} else {\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t}\n\n\tPDO_HANDLE_DBH_ERR();\n\tRETURN_FALSE;\n}\n/* }}} */\n\n/* {{{ Deletes the large object identified by oid.  Must be called inside a transaction. */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\tOid oid;\n\tchar *oidstr, *end_ptr;\n\tsize_t oidlen;\n\n\tif (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), \"s\",\n\t\t\t\t&oidstr, &oidlen)) {\n\t\tRETURN_THROWS();\n\t}\n\n\toid = (Oid)strtoul(oidstr, &end_ptr, 10);\n\tif (oid == 0 && (errno == ERANGE || errno == EINVAL)) {\n\t\tRETURN_FALSE;\n\t}\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tif (1 == lo_unlink(H->server, oid)) {\n\t\tRETURN_TRUE;\n\t}\n\n\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\tPDO_HANDLE_DBH_ERR();\n\tRETURN_FALSE;\n}\n/* }}} */\n\n/* {{{ Get asynchronous notification */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\tzend_long result_type = PDO_FETCH_USE_DEFAULT;\n\tzend_long ms_timeout = 0;\n\tPGnotify *pgsql_notify;\n\n\tif (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), \"|ll\",\n\t\t\t\t&result_type, &ms_timeout)) {\n\t\tRETURN_THROWS();\n\t}\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\n\tif (result_type == PDO_FETCH_USE_DEFAULT) {\n\t\tresult_type = dbh->default_fetch_type;\n\t}\n\n\tif (result_type != PDO_FETCH_BOTH && result_type != PDO_FETCH_ASSOC && result_type != PDO_FETCH_NUM) {\n\t\tzend_argument_value_error(1, \"must be one of PDO::FETCH_BOTH, PDO::FETCH_ASSOC, or PDO::FETCH_NUM\");\n\t\tRETURN_THROWS();\n\t}\n\n\tif (ms_timeout < 0) {\n\t\tzend_argument_value_error(2, \"must be greater than or equal to 0\");\n\t\tRETURN_THROWS();\n#ifdef ZEND_ENABLE_ZVAL_LONG64\n\t} else if (ms_timeout > INT_MAX) {\n\t\tphp_error_docref(NULL, E_WARNING, \"Timeout was shrunk to %d\", INT_MAX);\n\t\tms_timeout = INT_MAX;\n#endif\n\t}\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tif (!PQconsumeInput(H->server)) {\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n\tpgsql_notify = PQnotifies(H->server);\n\n\tif (ms_timeout && !pgsql_notify) {\n\t\tphp_pollfd_for_ms(PQsocket(H->server), PHP_POLLREADABLE, (int)ms_timeout);\n\n\t\tif (!PQconsumeInput(H->server)) {\n\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\tRETURN_FALSE;\n\t\t}\n\t\tpgsql_notify = PQnotifies(H->server);\n\t}\n\n\tif (!pgsql_notify) {\n\t\tRETURN_FALSE;\n\t}\n\n\tarray_init(return_value);\n\tif (result_type == PDO_FETCH_NUM || result_type == PDO_FETCH_BOTH) {\n\t\tadd_index_string(return_value, 0, pgsql_notify->relname);\n\t\tadd_index_long(return_value, 1, pgsql_notify->be_pid);\n\t\tif (pgsql_notify->extra && pgsql_notify->extra[0]) {\n\t\t\tadd_index_string(return_value, 2, pgsql_notify->extra);\n\t\t}\n\t}\n\tif (result_type == PDO_FETCH_ASSOC || result_type == PDO_FETCH_BOTH) {\n\t\tadd_assoc_string(return_value, \"message\", pgsql_notify->relname);\n\t\tadd_assoc_long(return_value, \"pid\", pgsql_notify->be_pid);\n\t\tif (pgsql_notify->extra && pgsql_notify->extra[0]) {\n\t\t\tadd_assoc_string(return_value, \"payload\", pgsql_notify->extra);\n\t\t}\n\t}\n\n\tPQfreemem(pgsql_notify);\n}\n/* }}} */\n\n/* {{{ Get backend(server) pid */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\n\tZEND_PARSE_PARAMETERS_NONE();\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tRETURN_LONG(PQbackendPID(H->server));\n}\n/* }}} */\n\nstatic const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind)\n{\n\tswitch (kind) {\n\t\tcase PDO_DBH_DRIVER_METHOD_KIND_DBH:\n\t\t\treturn class_PDO_PGSql_Ext_methods;\n\t\tdefault:\n\t\t\treturn NULL;\n\t}\n}\n\nstatic bool pdo_pgsql_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)\n{\n\tbool bval;\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tswitch (attr) {\n\t\tcase PDO_ATTR_EMULATE_PREPARES:\n\t\t\tif (!pdo_get_bool_param(&bval, val)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tH->emulate_prepares = bval;\n\t\t\treturn true;\n\t\tcase PDO_PGSQL_ATTR_DISABLE_PREPARES:\n\t\t\tif (!pdo_get_bool_param(&bval, val)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tH->disable_prepares = bval;\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\nstatic const struct pdo_dbh_methods pgsql_methods = {\n\tpgsql_handle_closer,\n\tpgsql_handle_preparer,\n\tpgsql_handle_doer,\n\tpgsql_handle_quoter,\n\tpgsql_handle_begin,\n\tpgsql_handle_commit,\n\tpgsql_handle_rollback,\n\tpdo_pgsql_set_attr,\n\tpdo_pgsql_last_insert_id,\n\tpdo_pgsql_fetch_error_func,\n\tpdo_pgsql_get_attribute,\n\tpdo_pgsql_check_liveness,\t/* check_liveness */\n\tpdo_pgsql_get_driver_methods,  /* get_driver_methods */\n\tNULL,\n\tpgsql_handle_in_transaction,\n\tNULL /* get_gc */\n};\n\nstatic int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */\n{\n\tpdo_pgsql_db_handle *H;\n\tint ret = 0;\n\tchar *conn_str, *p, *e;\n\tzend_string *tmp_user, *tmp_pass;\n\tzend_long connect_timeout = 30;\n\n\tH = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent);\n\tdbh->driver_data = H;\n\n\tdbh->skip_param_evt =\n\t\t1 << PDO_PARAM_EVT_EXEC_POST |\n\t\t1 << PDO_PARAM_EVT_FETCH_PRE |\n\t\t1 << PDO_PARAM_EVT_FETCH_POST;\n\n\tH->einfo.errcode = 0;\n\tH->einfo.errmsg = NULL;\n\n\t/* PostgreSQL wants params in the connect string to be separated by spaces,\n\t * if the PDO standard semicolons are used, we convert them to spaces\n\t */\n\te = (char *) dbh->data_source + strlen(dbh->data_source);\n\tp = (char *) dbh->data_source;\n\twhile ((p = memchr(p, ';', (e - p)))) {\n\t\t*p = ' ';\n\t}\n\n\tif (driver_options) {\n\t\tconnect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30);\n\t}\n\n\t/* escape username and password, if provided */\n\ttmp_user = _pdo_pgsql_escape_credentials(dbh->username);\n\ttmp_pass = _pdo_pgsql_escape_credentials(dbh->password);\n\n\t/* support both full connection string & connection string + login and/or password */\n\tif (tmp_user && tmp_pass) {\n\t\tspprintf(&conn_str, 0, \"%s user='%s' password='%s' connect_timeout=\" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), ZSTR_VAL(tmp_pass), connect_timeout);\n\t} else if (tmp_user) {\n\t\tspprintf(&conn_str, 0, \"%s user='%s' connect_timeout=\" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_user), connect_timeout);\n\t} else if (tmp_pass) {\n\t\tspprintf(&conn_str, 0, \"%s password='%s' connect_timeout=\" ZEND_LONG_FMT, (char *) dbh->data_source, ZSTR_VAL(tmp_pass), connect_timeout);\n\t} else {\n\t\tspprintf(&conn_str, 0, \"%s connect_timeout=\" ZEND_LONG_FMT, (char *) dbh->data_source, connect_timeout);\n\t}\n\n\tH->server = PQconnectdb(conn_str);\n\tH->lob_streams = (HashTable *) pemalloc(sizeof(HashTable), dbh->is_persistent);\n\tzend_hash_init(H->lob_streams, 0, NULL, NULL, 1);\n\n\tif (tmp_user) {\n\t\tzend_string_release_ex(tmp_user, 0);\n\t}\n\tif (tmp_pass) {\n\t\tzend_string_release_ex(tmp_pass, 0);\n\t}\n\n\tefree(conn_str);\n\n\tif (PQstatus(H->server) != CONNECTION_OK) {\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE);\n\t\tgoto cleanup;\n\t}\n\n\tPQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh);\n\n\tH->attached = 1;\n\tH->pgoid = -1;\n\n\tdbh->methods = &pgsql_methods;\n\tdbh->alloc_own_columns = 1;\n\tdbh->max_escaped_char_length = 2;\n\n\tret = 1;\n\ncleanup:\n\tdbh->methods = &pgsql_methods;\n\tif (!ret) {\n\t\tpgsql_handle_closer(dbh);\n\t}\n\n\treturn ret;\n}\n/* }}} */\n\nconst pdo_driver_t swoole_pdo_pgsql_driver = {\n\tPDO_DRIVER_HEADER(pgsql),\n\tpdo_pgsql_handle_factory\n};\n#endif\n"
  },
  {
    "path": "thirdparty/php83/pdo_pgsql/pgsql_driver_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 9bb79af98dbb7c171fd9533aeabece4937a06cd2 */\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, rows, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, \"\\\"\\\\t\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, \"\\\"\\\\\\\\\\\\\\\\N\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, \"\\\"\\\\t\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, \"\\\"\\\\\\\\\\\\\\\\N\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, \"\\\"\\\\t\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, \"\\\"\\\\\\\\\\\\\\\\N\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, 0, 0, 1)\n\tZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 0, \"\\\"rb\\\"\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fetchMode, IS_LONG, 0, \"PDO::FETCH_DEFAULT\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeoutMilliseconds, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, 0, 0, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\n\nZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlGetNotify);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlGetPid);\n\n\nstatic const zend_function_entry class_PDO_PGSql_Ext_methods[] = {\n\tZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlCopyToArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlCopyToFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlLOBCreate, arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlLOBOpen, arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlLOBUnlink, arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlGetNotify, arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlGetPid, arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, ZEND_ACC_PUBLIC)\n\tZEND_FE_END\n};\n"
  },
  {
    "path": "thirdparty/php83/pdo_pgsql/pgsql_statement.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Authors: Edin Kadribasic <edink@emini.dk>                            |\n  |          Ilia Alshanestsky <ilia@prohost.org>                        |\n  |          Wez Furlong <wez@php.net>                                   |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_PGSQL_HOOK\n#include \"php_swoole_pgsql.h\"\n\n#if PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400\n\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"pdo/php_pdo.h\"\n#include \"pdo/php_pdo_driver.h\"\n#include \"php_pdo_pgsql_int.h\"\n#ifdef HAVE_NETINET_IN_H\n#include <netinet/in.h>\n#endif\n\n/* from postgresql/src/include/catalog/pg_type.h */\n#define BOOLLABEL   \"bool\"\n#define BOOLOID     16\n#define BYTEALABEL  \"bytea\"\n#define BYTEAOID    17\n#define DATELABEL   \"date\"\n#define DATEOID     1082\n#define INT2LABEL   \"int2\"\n#define INT2OID     21\n#define INT4LABEL   \"int4\"\n#define INT4OID     23\n#define INT8LABEL   \"int8\"\n#define INT8OID     20\n#define OIDOID      26\n#define TEXTLABEL   \"text\"\n#define TEXTOID     25\n#define TIMESTAMPLABEL \"timestamp\"\n#define TIMESTAMPOID   1114\n#define VARCHARLABEL \"varchar\"\n#define VARCHAROID   1043\n\n\n\nstatic int pgsql_stmt_dtor(pdo_stmt_t *stmt)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\tbool server_obj_usable = !Z_ISUNDEF(stmt->database_object_handle)\n\t\t&& IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)])\n\t\t&& !(OBJ_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED);\n\n\tif (S->result) {\n\t\t/* free the resource */\n\t\tPQclear(S->result);\n\t\tS->result = NULL;\n\t}\n\n\tif (S->stmt_name) {\n\t\tif (S->is_prepared && server_obj_usable) {\n\t\t\tpdo_pgsql_db_handle *H = S->H;\n\t\t\tchar *q = NULL;\n\t\t\tPGresult *res;\n\n\t\t\tspprintf(&q, 0, \"DEALLOCATE %s\", S->stmt_name);\n\t\t\tres = PQexec(H->server, q);\n\t\t\tefree(q);\n\t\t\tif (res) {\n\t\t\t\tPQclear(res);\n\t\t\t}\n\t\t}\n\t\tefree(S->stmt_name);\n\t\tS->stmt_name = NULL;\n\t}\n\tif (S->param_lengths) {\n\t\tefree(S->param_lengths);\n\t\tS->param_lengths = NULL;\n\t}\n\tif (S->param_values) {\n\t\tefree(S->param_values);\n\t\tS->param_values = NULL;\n\t}\n\tif (S->param_formats) {\n\t\tefree(S->param_formats);\n\t\tS->param_formats = NULL;\n\t}\n\tif (S->param_types) {\n\t\tefree(S->param_types);\n\t\tS->param_types = NULL;\n\t}\n\tif (S->query) {\n\t\tzend_string_release(S->query);\n\t\tS->query = NULL;\n\t}\n\n\tif (S->cursor_name) {\n\t\tif (server_obj_usable) {\n\t\t\tpdo_pgsql_db_handle *H = S->H;\n\t\t\tchar *q = NULL;\n\t\t\tPGresult *res;\n\n\t\t\tspprintf(&q, 0, \"CLOSE %s\", S->cursor_name);\n\t\t\tres = PQexec(H->server, q);\n\t\t\tefree(q);\n\t\t\tif (res) PQclear(res);\n\t\t}\n\t\tefree(S->cursor_name);\n\t\tS->cursor_name = NULL;\n\t}\n\n\tif(S->cols) {\n\t\tefree(S->cols);\n\t\tS->cols = NULL;\n\t}\n\tefree(S);\n\tstmt->driver_data = NULL;\n\treturn 1;\n}\n\nstatic int pgsql_stmt_execute(pdo_stmt_t *stmt)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\tpdo_pgsql_db_handle *H = S->H;\n\tExecStatusType status;\n\n\tbool in_trans = stmt->dbh->methods->in_transaction(stmt->dbh);\n\n\t/* ensure that we free any previous unfetched results */\n\tif(S->result) {\n\t\tPQclear(S->result);\n\t\tS->result = NULL;\n\t}\n\n\tS->current_row = 0;\n\n\tif (S->cursor_name) {\n\t\tchar *q = NULL;\n\n\t\tif (S->is_prepared) {\n\t\t\tspprintf(&q, 0, \"CLOSE %s\", S->cursor_name);\n\t\t\tPQclear(PQexec(H->server, q));\n\t\t\tefree(q);\n\t\t}\n\n\t\tspprintf(&q, 0, \"DECLARE %s SCROLL CURSOR WITH HOLD FOR %s\", S->cursor_name, ZSTR_VAL(stmt->active_query_string));\n\t\tS->result = PQexec(H->server, q);\n\t\tefree(q);\n\n\t\t/* check if declare failed */\n\t\tstatus = PQresultStatus(S->result);\n\t\tif (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {\n\t\t\tpdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));\n\t\t\treturn 0;\n\t\t}\n\t\tPQclear(S->result);\n\n\t\t/* the cursor was declared correctly */\n\t\tS->is_prepared = 1;\n\n\t\t/* fetch to be able to get the number of tuples later, but don't advance the cursor pointer */\n\t\tspprintf(&q, 0, \"FETCH FORWARD 0 FROM %s\", S->cursor_name);\n\t\tS->result = PQexec(H->server, q);\n\t\tefree(q);\n\t} else if (S->stmt_name) {\n\t\t/* using a prepared statement */\n\n\t\tif (!S->is_prepared) {\nstmt_retry:\n\t\t\t/* we deferred the prepare until now, because we didn't\n\t\t\t * know anything about the parameter types; now we do */\n\t\t\tS->result = PQprepare(H->server, S->stmt_name, ZSTR_VAL(S->query),\n\t\t\t\t\t\tstmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0,\n\t\t\t\t\t\tS->param_types);\n\t\t\tstatus = PQresultStatus(S->result);\n\t\t\tswitch (status) {\n\t\t\t\tcase PGRES_COMMAND_OK:\n\t\t\t\tcase PGRES_TUPLES_OK:\n\t\t\t\t\t/* it worked */\n\t\t\t\t\tS->is_prepared = 1;\n\t\t\t\t\tPQclear(S->result);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: {\n\t\t\t\t\tchar *sqlstate = pdo_pgsql_sqlstate(S->result);\n\t\t\t\t\t/* 42P05 means that the prepared statement already existed. this can happen if you use\n\t\t\t\t\t * a connection pooling software line pgpool which doesn't close the db-connection once\n\t\t\t\t\t * php disconnects. if php dies (no chance to run RSHUTDOWN) during execution it has no\n\t\t\t\t\t * chance to DEALLOCATE the prepared statements it has created. so, if we hit a 42P05 we\n\t\t\t\t\t * deallocate it and retry ONCE (thies 2005.12.15)\n\t\t\t\t\t */\n\t\t\t\t\tif (sqlstate && !strcmp(sqlstate, \"42P05\")) {\n\t\t\t\t\t\tchar buf[100]; /* stmt_name == \"pdo_crsr_%08x\" */\n\t\t\t\t\t\tPGresult *res;\n\t\t\t\t\t\tsnprintf(buf, sizeof(buf), \"DEALLOCATE %s\", S->stmt_name);\n\t\t\t\t\t\tres = PQexec(H->server, buf);\n\t\t\t\t\t\tif (res) {\n\t\t\t\t\t\t\tPQclear(res);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tgoto stmt_retry;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpdo_pgsql_error_stmt(stmt, status, sqlstate);\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tS->result = PQexecPrepared(H->server, S->stmt_name,\n\t\t\t\tstmt->bound_params ?\n\t\t\t\t\tzend_hash_num_elements(stmt->bound_params) :\n\t\t\t\t\t0,\n\t\t\t\t(const char**)S->param_values,\n\t\t\t\tS->param_lengths,\n\t\t\t\tS->param_formats,\n\t\t\t\t0);\n\t} else if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED) {\n\t\t/* execute query with parameters */\n\t\tS->result = PQexecParams(H->server, ZSTR_VAL(S->query),\n\t\t\t\tstmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0,\n\t\t\t\tS->param_types,\n\t\t\t\t(const char**)S->param_values,\n\t\t\t\tS->param_lengths,\n\t\t\t\tS->param_formats,\n\t\t\t\t0);\n\t} else {\n\t\t/* execute plain query (with embedded parameters) */\n\t\tS->result = PQexec(H->server, ZSTR_VAL(stmt->active_query_string));\n\t}\n\tstatus = PQresultStatus(S->result);\n\n\tif (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {\n\t\tpdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));\n\t\treturn 0;\n\t}\n\n\tstmt->column_count = (int) PQnfields(S->result);\n\tif (S->cols == NULL) {\n\t\tS->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column));\n\t}\n\n\tif (status == PGRES_COMMAND_OK) {\n\t\tstmt->row_count = ZEND_ATOL(PQcmdTuples(S->result));\n\t\tH->pgoid = PQoidValue(S->result);\n\t} else {\n\t\tstmt->row_count = (zend_long)PQntuples(S->result);\n\t}\n\n\tif (in_trans && !stmt->dbh->methods->in_transaction(stmt->dbh)) {\n\t\tpdo_pgsql_close_lob_streams(stmt->dbh);\n\t}\n\n\treturn 1;\n}\n\nstatic int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,\n\t\tenum pdo_param_event event_type)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\n\tif (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED && param->is_param) {\n\t\tswitch (event_type) {\n\t\t\tcase PDO_PARAM_EVT_FREE:\n\t\t\t\tif (param->driver_data) {\n\t\t\t\t\tefree(param->driver_data);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase PDO_PARAM_EVT_NORMALIZE:\n\t\t\t\t/* decode name from $1, $2 into 0, 1 etc. */\n\t\t\t\tif (param->name) {\n\t\t\t\t\tif (ZSTR_VAL(param->name)[0] == '$') {\n\t\t\t\t\t\tparam->paramno = ZEND_ATOL(ZSTR_VAL(param->name) + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t/* resolve parameter name to rewritten name */\n\t\t\t\t\t\tzend_string *namevar;\n\n\t\t\t\t\t\tif (stmt->bound_param_map && (namevar = zend_hash_find_ptr(stmt->bound_param_map,\n\t\t\t\t\t\t\t\tparam->name)) != NULL) {\n\t\t\t\t\t\t\tparam->paramno = ZEND_ATOL(ZSTR_VAL(namevar) + 1);\n\t\t\t\t\t\t\tparam->paramno--;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tpdo_pgsql_error_stmt_msg(stmt, 0, \"HY093\", ZSTR_VAL(param->name));\n\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase PDO_PARAM_EVT_ALLOC:\n\t\t\t\tif (!stmt->bound_param_map) {\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tif (!zend_hash_index_exists(stmt->bound_param_map, param->paramno)) {\n\t\t\t\t\tpdo_pgsql_error_stmt_msg(stmt, 0, \"HY093\", \"parameter was not defined\");\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tZEND_FALLTHROUGH;\n\t\t\tcase PDO_PARAM_EVT_EXEC_POST:\n\t\t\tcase PDO_PARAM_EVT_FETCH_PRE:\n\t\t\tcase PDO_PARAM_EVT_FETCH_POST:\n\t\t\t\t/* work is handled by EVT_NORMALIZE */\n\t\t\t\treturn 1;\n\n\t\t\tcase PDO_PARAM_EVT_EXEC_PRE:\n\t\t\t\tif (!stmt->bound_param_map) {\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tif (!S->param_values) {\n\t\t\t\t\tS->param_values = ecalloc(\n\t\t\t\t\t\t\tzend_hash_num_elements(stmt->bound_param_map),\n\t\t\t\t\t\t\tsizeof(char*));\n\t\t\t\t\tS->param_lengths = ecalloc(\n\t\t\t\t\t\t\tzend_hash_num_elements(stmt->bound_param_map),\n\t\t\t\t\t\t\tsizeof(int));\n\t\t\t\t\tS->param_formats = ecalloc(\n\t\t\t\t\t\t\tzend_hash_num_elements(stmt->bound_param_map),\n\t\t\t\t\t\t\tsizeof(int));\n\t\t\t\t\tS->param_types = ecalloc(\n\t\t\t\t\t\t\tzend_hash_num_elements(stmt->bound_param_map),\n\t\t\t\t\t\t\tsizeof(Oid));\n\t\t\t\t}\n\t\t\t\tif (param->paramno >= 0) {\n\t\t\t\t\tzval *parameter;\n\n\t\t\t\t\t/*\n\t\t\t\t\tif (param->paramno >= zend_hash_num_elements(stmt->bound_params)) {\n\t\t\t\t\t\tpdo_raise_impl_error(stmt->dbh, stmt, \"HY093\", \"parameter was not defined\");\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\t}\n\t\t\t\t\t*/\n\n\t\t\t\t\tif (Z_ISREF(param->parameter)) {\n\t\t\t\t\t\tparameter = Z_REFVAL(param->parameter);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tparameter = &param->parameter;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB &&\n\t\t\t\t\t\t\tZ_TYPE_P(parameter) == IS_RESOURCE) {\n\t\t\t\t\t\tphp_stream *stm;\n\t\t\t\t\t\tphp_stream_from_zval_no_verify(stm, parameter);\n\t\t\t\t\t\tif (stm) {\n\t\t\t\t\t\t\tif (php_stream_is(stm, &pdo_pgsql_lob_stream_ops)) {\n\t\t\t\t\t\t\t\tstruct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stm->abstract;\n\t\t\t\t\t\t\t\tpdo_pgsql_bound_param *P = param->driver_data;\n\n\t\t\t\t\t\t\t\tif (P == NULL) {\n\t\t\t\t\t\t\t\t\tP = ecalloc(1, sizeof(*P));\n\t\t\t\t\t\t\t\t\tparam->driver_data = P;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tP->oid = htonl(self->oid);\n\t\t\t\t\t\t\t\tS->param_values[param->paramno] = (char*)&P->oid;\n\t\t\t\t\t\t\t\tS->param_lengths[param->paramno] = sizeof(P->oid);\n\t\t\t\t\t\t\t\tS->param_formats[param->paramno] = 1;\n\t\t\t\t\t\t\t\tS->param_types[param->paramno] = OIDOID;\n\t\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tzend_string *str = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0);\n\t\t\t\t\t\t\t\tif (str != NULL) {\n\t\t\t\t\t\t\t\t\tZVAL_STR(parameter, str);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tZVAL_EMPTY_STRING(parameter);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t/* expected a stream resource */\n\t\t\t\t\t\t\tpdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, \"HY105\");\n\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL ||\n\t\t\t\t\t\t\tZ_TYPE_P(parameter) == IS_NULL) {\n\t\t\t\t\t\tS->param_values[param->paramno] = NULL;\n\t\t\t\t\t\tS->param_lengths[param->paramno] = 0;\n\t\t\t\t\t} else if (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE) {\n\t\t\t\t\t\tS->param_values[param->paramno] = Z_TYPE_P(parameter) == IS_TRUE ? \"t\" : \"f\";\n\t\t\t\t\t\tS->param_lengths[param->paramno] = 1;\n\t\t\t\t\t\tS->param_formats[param->paramno] = 0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconvert_to_string(parameter);\n\t\t\t\t\t\tS->param_values[param->paramno] = Z_STRVAL_P(parameter);\n\t\t\t\t\t\tS->param_lengths[param->paramno] = Z_STRLEN_P(parameter);\n\t\t\t\t\t\tS->param_formats[param->paramno] = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {\n\t\t\t\t\t\tS->param_types[param->paramno] = 0;\n\t\t\t\t\t\tS->param_formats[param->paramno] = 1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tS->param_types[param->paramno] = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t} else if (param->is_param && event_type == PDO_PARAM_EVT_NORMALIZE) {\n\t\t/* We need to manually convert to a pg native boolean value */\n\t\tif (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL &&\n\t\t\t((param->param_type & PDO_PARAM_INPUT_OUTPUT) != PDO_PARAM_INPUT_OUTPUT)) {\n\t\t\tconst char *s = zend_is_true(&param->parameter) ? \"t\" : \"f\";\n\t\t\tparam->param_type = PDO_PARAM_STR;\n\t\t\tzval_ptr_dtor(&param->parameter);\n\t\t\tZVAL_STRINGL(&param->parameter, s, 1);\n\t\t}\n\t}\n\treturn 1;\n}\n\nstatic int pgsql_stmt_fetch(pdo_stmt_t *stmt,\n\tenum pdo_fetch_orientation ori, zend_long offset)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\n\tif (S->cursor_name) {\n\t\tchar *ori_str = NULL;\n\t\tchar *q = NULL;\n\t\tExecStatusType status;\n\n\t\tswitch (ori) {\n\t\t\tcase PDO_FETCH_ORI_NEXT: \tspprintf(&ori_str, 0, \"NEXT\"); break;\n\t\t\tcase PDO_FETCH_ORI_PRIOR:\tspprintf(&ori_str, 0, \"BACKWARD\"); break;\n\t\t\tcase PDO_FETCH_ORI_FIRST:\tspprintf(&ori_str, 0, \"FIRST\"); break;\n\t\t\tcase PDO_FETCH_ORI_LAST:\tspprintf(&ori_str, 0, \"LAST\"); break;\n\t\t\tcase PDO_FETCH_ORI_ABS:\t\tspprintf(&ori_str, 0, \"ABSOLUTE \" ZEND_LONG_FMT, offset); break;\n\t\t\tcase PDO_FETCH_ORI_REL:\t\tspprintf(&ori_str, 0, \"RELATIVE \" ZEND_LONG_FMT, offset); break;\n\t\t\tdefault:\n\t\t\t\treturn 0;\n\t\t}\n\n\t\tif(S->result) {\n\t\t\tPQclear(S->result);\n\t\t\tS->result = NULL;\n\t\t}\n\n\t\tspprintf(&q, 0, \"FETCH %s FROM %s\", ori_str, S->cursor_name);\n\t\tefree(ori_str);\n\t\tS->result = PQexec(S->H->server, q);\n\t\tefree(q);\n\t\tstatus = PQresultStatus(S->result);\n\n\t\tif (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {\n\t\t\tpdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));\n\t\t\treturn 0;\n\t\t}\n\n\t\tif (PQntuples(S->result)) {\n\t\t\tS->current_row = 1;\n\t\t\treturn 1;\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t} else {\n\t\tif (S->current_row < stmt->row_count) {\n\t\t\tS->current_row++;\n\t\t\treturn 1;\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t}\n}\n\nstatic int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\tstruct pdo_column_data *cols = stmt->columns;\n\tchar *str;\n\n\tif (!S->result) {\n\t\treturn 0;\n\t}\n\n\tstr = PQfname(S->result, colno);\n\tcols[colno].name = zend_string_init(str, strlen(str), 0);\n\tcols[colno].maxlen = PQfsize(S->result, colno);\n\tcols[colno].precision = PQfmod(S->result, colno);\n\tS->cols[colno].pgsql_type = PQftype(S->result, colno);\n\n\treturn 1;\n}\n\nstatic int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\tif (!S->result) {\n\t\treturn 0;\n\t}\n\n\t/* We have already increased count by 1 in pgsql_stmt_fetch() */\n\tif (PQgetisnull(S->result, S->current_row - 1, colno)) { /* Check if we got NULL */\n\t\tZVAL_NULL(result);\n\t} else {\n\t\tchar *ptr = PQgetvalue(S->result, S->current_row - 1, colno);\n\t\tsize_t len = PQgetlength(S->result, S->current_row - 1, colno);\n\n\t\tswitch (S->cols[colno].pgsql_type) {\n\t\t\tcase BOOLOID:\n\t\t\t\tZVAL_BOOL(result, *ptr == 't');\n\t\t\t\tbreak;\n\n\t\t\tcase INT2OID:\n\t\t\tcase INT4OID:\n#if SIZEOF_ZEND_LONG >= 8\n\t\t\tcase INT8OID:\n#endif\n\t\t\t\tZVAL_LONG(result, ZEND_ATOL(ptr));\n\t\t\t\tbreak;\n\n\t\t\tcase OIDOID: {\n\t\t\t\tchar *end_ptr;\n\t\t\t\tOid oid = (Oid)strtoul(ptr, &end_ptr, 10);\n\t\t\t\tif (type && *type == PDO_PARAM_LOB) {\n\t\t\t\t\t/* If column was bound as LOB, return a stream. */\n\t\t\t\t\tint loid = lo_open(S->H->server, oid, INV_READ);\n\t\t\t\t\tif (loid >= 0) {\n\t\t\t\t\t\tphp_stream *stream = pdo_pgsql_create_lob_stream(&stmt->database_object_handle, loid, oid);\n\t\t\t\t\t\tif (stream) {\n\t\t\t\t\t\t\tphp_stream_to_zval(stream, result);\n\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn 0;\n\t\t\t\t} else {\n\t\t\t\t\t/* Otherwise return OID as integer. */\n\t\t\t\t\tZVAL_LONG(result, oid);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase BYTEAOID: {\n\t\t\t\tsize_t tmp_len;\n\t\t\t\tchar *tmp_ptr = (char *)PQunescapeBytea((unsigned char *) ptr, &tmp_len);\n\t\t\t\tif (!tmp_ptr) {\n\t\t\t\t\t/* PQunescapeBytea returned an error */\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\n\t\t\t\tzend_string *str = zend_string_init(tmp_ptr, tmp_len, 0);\n\t\t\t\tphp_stream *stream = php_stream_memory_open(TEMP_STREAM_READONLY, str);\n\t\t\t\tphp_stream_to_zval(stream, result);\n\t\t\t\tzend_string_release(str);\n\t\t\t\tPQfreemem(tmp_ptr);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tZVAL_STRINGL_FAST(result, ptr, len);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn 1;\n}\n\nstatic zend_always_inline char * pdo_pgsql_translate_oid_to_table(Oid oid, PGconn *conn)\n{\n\tchar *table_name = NULL;\n\tPGresult *tmp_res;\n\tchar *querystr = NULL;\n\n\tspprintf(&querystr, 0, \"SELECT RELNAME FROM PG_CLASS WHERE OID=%d\", oid);\n\n\tif ((tmp_res = PQexec(conn, querystr)) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) {\n\t\tif (tmp_res) {\n\t\t\tPQclear(tmp_res);\n\t\t}\n\t\tefree(querystr);\n\t\treturn 0;\n\t}\n\tefree(querystr);\n\n\tif (1 == PQgetisnull(tmp_res, 0, 0) || (table_name = PQgetvalue(tmp_res, 0, 0)) == NULL) {\n\t\tPQclear(tmp_res);\n\t\treturn 0;\n\t}\n\n\ttable_name = estrdup(table_name);\n\n\tPQclear(tmp_res);\n\treturn table_name;\n}\n\nstatic int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\tPGresult *res;\n\tchar *q=NULL;\n\tExecStatusType status;\n\tOid table_oid;\n\tchar *table_name=NULL;\n\n\tif (!S->result) {\n\t\treturn FAILURE;\n\t}\n\n\tif (colno >= stmt->column_count) {\n\t\treturn FAILURE;\n\t}\n\n\tarray_init(return_value);\n\tadd_assoc_long(return_value, \"pgsql:oid\", S->cols[colno].pgsql_type);\n\n\ttable_oid = PQftable(S->result, colno);\n\tadd_assoc_long(return_value, \"pgsql:table_oid\", table_oid);\n\ttable_name = pdo_pgsql_translate_oid_to_table(table_oid, S->H->server);\n\tif (table_name) {\n\t\tadd_assoc_string(return_value, \"table\", table_name);\n\t\tefree(table_name);\n\t}\n\n\tswitch (S->cols[colno].pgsql_type) {\n\t\tcase BOOLOID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", BOOLLABEL);\n\t\t\tbreak;\n\t\tcase BYTEAOID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", BYTEALABEL);\n\t\t\tbreak;\n\t\tcase INT8OID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", INT8LABEL);\n\t\t\tbreak;\n\t\tcase INT2OID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", INT2LABEL);\n\t\t\tbreak;\n\t\tcase INT4OID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", INT4LABEL);\n\t\t\tbreak;\n\t\tcase TEXTOID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", TEXTLABEL);\n\t\t\tbreak;\n\t\tcase VARCHAROID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", VARCHARLABEL);\n\t\t\tbreak;\n\t\tcase DATEOID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", DATELABEL);\n\t\t\tbreak;\n\t\tcase TIMESTAMPOID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", TIMESTAMPLABEL);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t/* Fetch metadata from Postgres system catalogue */\n\t\t\tspprintf(&q, 0, \"SELECT TYPNAME FROM PG_TYPE WHERE OID=%u\", S->cols[colno].pgsql_type);\n\t\t\tres = PQexec(S->H->server, q);\n\t\t\tefree(q);\n\t\t\tstatus = PQresultStatus(res);\n\t\t\tif (status == PGRES_TUPLES_OK && 1 == PQntuples(res)) {\n\t\t\t\tadd_assoc_string(return_value, \"native_type\", PQgetvalue(res, 0, 0));\n\t\t\t}\n\t\t\tPQclear(res);\n\t}\n\n\tenum pdo_param_type param_type;\n\tswitch (S->cols[colno].pgsql_type) {\n\t\tcase BOOLOID:\n\t\t\tparam_type = PDO_PARAM_BOOL;\n\t\t\tbreak;\n\t\tcase INT2OID:\n\t\tcase INT4OID:\n\t\tcase INT8OID:\n\t\t\tparam_type = PDO_PARAM_INT;\n\t\t\tbreak;\n\t\tcase OIDOID:\n\t\tcase BYTEAOID:\n\t\t\tparam_type = PDO_PARAM_LOB;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tparam_type = PDO_PARAM_STR;\n\t}\n\tadd_assoc_long(return_value, \"pdo_type\", param_type);\n\n\treturn 1;\n}\n\nstatic int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt)\n{\n\treturn 1;\n}\n\nconst struct pdo_stmt_methods swoole_pgsql_stmt_methods = {\n\tpgsql_stmt_dtor,\n\tpgsql_stmt_execute,\n\tpgsql_stmt_fetch,\n\tpgsql_stmt_describe,\n\tpgsql_stmt_get_col,\n\tpgsql_stmt_param_hook,\n\tNULL, /* set_attr */\n\tNULL, /* get_attr */\n\tpgsql_stmt_get_column_meta,\n\tNULL,  /* next_rowset */\n\tpdo_pgsql_stmt_cursor_closer\n};\n#endif\n"
  },
  {
    "path": "thirdparty/php83/pdo_pgsql/php_pdo_pgsql_int.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Authors: Edin Kadribasic <edink@emini.dk>                            |\n  |          Ilia Alshanestsky <ilia@prohost.org>                        |\n  |          Wez Furlong <wez@php.net>                                   |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef PHP_PDO_PGSQL_INT_H\n#define PHP_PDO_PGSQL_INT_H\n\n#include <libpq-fe.h>\n#include <libpq/libpq-fs.h>\n#include <php.h>\n\n#define PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE \"08006\"\n\ntypedef struct {\n\tconst char *file;\n\tint line;\n\tunsigned int errcode;\n\tchar *errmsg;\n} pdo_pgsql_error_info;\n\n/* stuff we use in a pgsql database handle */\ntypedef struct {\n\tPGconn\t\t*server;\n\tunsigned \tattached:1;\n\tunsigned \t_reserved:31;\n\tpdo_pgsql_error_info\teinfo;\n\tOid \t\tpgoid;\n\tunsigned int\tstmt_counter;\n\t/* The following two variables have the same purpose. Unfortunately we need\n\t   to keep track of two different attributes having the same effect. */\n\tbool\t\temulate_prepares;\n\tbool\t\tdisable_native_prepares; /* deprecated since 5.6 */\n\tbool\t\tdisable_prepares;\n\tHashTable       *lob_streams;\n} pdo_pgsql_db_handle;\n\ntypedef struct {\n\tOid          pgsql_type;\n} pdo_pgsql_column;\n\ntypedef struct {\n\tpdo_pgsql_db_handle     *H;\n\tPGresult                *result;\n\tpdo_pgsql_column        *cols;\n\tchar *cursor_name;\n\tchar *stmt_name;\n\tzend_string *query;\n\tchar **param_values;\n\tint *param_lengths;\n\tint *param_formats;\n\tOid *param_types;\n\tint                     current_row;\n\tbool is_prepared;\n} pdo_pgsql_stmt;\n\ntypedef struct {\n\tOid     oid;\n} pdo_pgsql_bound_param;\n\nextern const pdo_driver_t pdo_pgsql_driver;\n\nextern int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line);\n#define pdo_pgsql_error(d,e,z)\t_pdo_pgsql_error(d, NULL, e, z, NULL, __FILE__, __LINE__)\n#define pdo_pgsql_error_msg(d,e,m)\t_pdo_pgsql_error(d, NULL, e, NULL, m, __FILE__, __LINE__)\n#define pdo_pgsql_error_stmt(s,e,z)\t_pdo_pgsql_error(s->dbh, s, e, z, NULL, __FILE__, __LINE__)\n#define pdo_pgsql_error_stmt_msg(stmt, e, sqlstate, msg) \\\n\t_pdo_pgsql_error(stmt->dbh, stmt, e, sqlstate, msg, __FILE__, __LINE__)\n\nextern const struct pdo_stmt_methods swoole_pgsql_stmt_methods;\n\n#define pdo_pgsql_sqlstate(r) PQresultErrorField(r, PG_DIAG_SQLSTATE)\n\nenum {\n\tPDO_PGSQL_ATTR_DISABLE_PREPARES = PDO_ATTR_DRIVER_SPECIFIC,\n};\n\nstruct pdo_pgsql_lob_self {\n\tzval dbh;\n\tPGconn *conn;\n\tint lfd;\n\tOid oid;\n};\n\nenum pdo_pgsql_specific_constants {\n\tPGSQL_TRANSACTION_IDLE = PQTRANS_IDLE,\n\tPGSQL_TRANSACTION_ACTIVE = PQTRANS_ACTIVE,\n\tPGSQL_TRANSACTION_INTRANS = PQTRANS_INTRANS,\n\tPGSQL_TRANSACTION_INERROR = PQTRANS_INERROR,\n\tPGSQL_TRANSACTION_UNKNOWN = PQTRANS_UNKNOWN\n};\n\nphp_stream *pdo_pgsql_create_lob_stream(zval *pdh, int lfd, Oid oid);\nextern const php_stream_ops pdo_pgsql_lob_stream_ops;\n\nvoid pdo_libpq_version(char *buf, size_t len);\nvoid pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh);\n\n#endif /* PHP_PDO_PGSQL_INT_H */\n"
  },
  {
    "path": "thirdparty/php84/curl/curl_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: ddfcdd8a0bf0ee6c338ec1689c6de5d7fd87303d */\n#include \"swoole_curl_interface.h\"\n\n#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_close, 0, 1, IS_VOID, 0)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_swoole_native_curl_copy_handle, 0, 1, CurlHandle, MAY_BE_FALSE)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_errno, 0, 1, IS_LONG, 0)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_error, 0, 1, IS_STRING, 0)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_escape, 0, 2, MAY_BE_STRING|MAY_BE_FALSE)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\n\tZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_native_curl_unescape arginfo_swoole_native_curl_escape\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_setopt, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)\n\tZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_exec, 0, 1, MAY_BE_STRING|MAY_BE_BOOL)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_getinfo, 0, 1, IS_MIXED, 0)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, option, IS_LONG, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_swoole_native_curl_init, 0, 0, CurlHandle, MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, url, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\n#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_upkeep, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\nZEND_END_ARG_INFO()\n#endif\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_add_handle, 0, 2, IS_LONG, 0)\n\tZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_close, 0, 1, IS_VOID, 0)\n\tZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_errno, 0, 1, IS_LONG, 0)\n\tZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_exec, 0, 2, IS_LONG, 0)\n\tZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)\n\tZEND_ARG_INFO(1, still_running)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_getcontent, 0, 1, IS_STRING, 1)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_multi_info_read, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)\n\tZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, queued_messages, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_swoole_native_curl_multi_init, 0, 0, CurlMultiHandle, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_native_curl_multi_remove_handle arginfo_swoole_native_curl_multi_add_handle\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_select, 0, 1, IS_LONG, 0)\n\tZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, \"1.0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_multi_strerror, 0, 1, IS_STRING, 1)\n\tZEND_ARG_TYPE_INFO(0, error_code, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_pause, 0, 2, IS_LONG, 0)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\n\tZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_native_curl_reset arginfo_swoole_native_curl_close\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_setopt_array, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\n\tZEND_ARG_TYPE_INFO(0, options, IS_ARRAY, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_native_curl_setopt, 0, 3, _IS_BOOL, 0)\n\tZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)\n\tZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0)\n\tZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)\nZEND_END_ARG_INFO()\n\n#define arginfo_swoole_native_curl_strerror arginfo_swoole_native_curl_multi_strerror\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_version, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n\nstatic const zend_function_entry swoole_native_curl_functions[] = {\n\tZEND_FE(swoole_native_curl_close, arginfo_swoole_native_curl_close)\n\tZEND_FE(swoole_native_curl_copy_handle, arginfo_swoole_native_curl_copy_handle)\n\tZEND_FE(swoole_native_curl_errno, arginfo_swoole_native_curl_errno)\n\tZEND_FE(swoole_native_curl_error, arginfo_swoole_native_curl_error)\n\tZEND_FE(swoole_native_curl_escape, arginfo_swoole_native_curl_escape)\n\tZEND_FE(swoole_native_curl_unescape, arginfo_swoole_native_curl_unescape)\n\tZEND_FE(swoole_native_curl_multi_setopt, arginfo_swoole_native_curl_multi_setopt)\n\tZEND_FE(swoole_native_curl_exec, arginfo_swoole_native_curl_exec)\n\tZEND_FE(swoole_native_curl_getinfo, arginfo_swoole_native_curl_getinfo)\n\tZEND_FE(swoole_native_curl_init, arginfo_swoole_native_curl_init)\n#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */\n\tZEND_FE(swoole_native_curl_upkeep, arginfo_swoole_native_curl_upkeep)\n#endif\n\tZEND_FE(swoole_native_curl_multi_add_handle, arginfo_swoole_native_curl_multi_add_handle)\n\tZEND_FE(swoole_native_curl_multi_close, arginfo_swoole_native_curl_multi_close)\n\tZEND_FE(swoole_native_curl_multi_errno, arginfo_swoole_native_curl_multi_errno)\n\tZEND_FE(swoole_native_curl_multi_exec, arginfo_swoole_native_curl_multi_exec)\n\tZEND_FE(swoole_native_curl_multi_getcontent, arginfo_swoole_native_curl_multi_getcontent)\n\tZEND_FE(swoole_native_curl_multi_info_read, arginfo_swoole_native_curl_multi_info_read)\n\tZEND_FE(swoole_native_curl_multi_init, arginfo_swoole_native_curl_multi_init)\n\tZEND_FE(swoole_native_curl_multi_remove_handle, arginfo_swoole_native_curl_multi_remove_handle)\n\tZEND_FE(swoole_native_curl_multi_select, arginfo_swoole_native_curl_multi_select)\n\tZEND_FE(swoole_native_curl_pause, arginfo_swoole_native_curl_pause)\n\tZEND_FE(swoole_native_curl_reset, arginfo_swoole_native_curl_reset)\n\tZEND_FE(swoole_native_curl_setopt_array, arginfo_swoole_native_curl_setopt_array)\n\tZEND_FE(swoole_native_curl_setopt, arginfo_swoole_native_curl_setopt)\n\tZEND_FE_END\n};\n\nstatic const zend_function_entry class_CurlHandle_methods[] = {\n\tZEND_FE_END\n};\n\nstatic const zend_function_entry class_CurlMultiHandle_methods[] = {\n\tZEND_FE_END\n};\n\nstatic const zend_function_entry class_CurlShareHandle_methods[] = {\n\tZEND_FE_END\n};\n#endif\n"
  },
  {
    "path": "thirdparty/php84/curl/curl_private.h",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | Copyright (c) The PHP Group                                          |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | https://www.php.net/license/3_01.txt                                 |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Author: Sterling Hughes <sterling@php.net>                           |\n   |         Wez Furlong <wez@thebrainroom.com>                           |\n   +----------------------------------------------------------------------+\n*/\n\n#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400\n\n#ifndef _PHP_CURL_PRIVATE_H\n#define _PHP_CURL_PRIVATE_H\n\n#include \"php_curl.h\"\n\n#define PHP_CURL_DEBUG 0\n\n#include \"php_version.h\"\n#define PHP_CURL_VERSION PHP_VERSION\n\n#include <curl/curl.h>\n#include <curl/multi.h>\n\n#define CURLOPT_RETURNTRANSFER 19913\n#define CURLOPT_BINARYTRANSFER 19914 /* For Backward compatibility */\n#define PHP_CURL_STDOUT 0\n#define PHP_CURL_FILE 1\n#define PHP_CURL_USER 2\n#define PHP_CURL_DIRECT 3\n#define PHP_CURL_RETURN 4\n#define PHP_CURL_IGNORE 7\n\n#define SAVE_CURL_ERROR(__handle, __err)                                                                               \\\n    do {                                                                                                               \\\n        (__handle)->err.no = (int) __err;                                                                              \\\n    } while (0)\n\nPHP_MINIT_FUNCTION(curl);\nPHP_MSHUTDOWN_FUNCTION(curl);\nPHP_MINFO_FUNCTION(curl);\n\ntypedef struct {\n    zend_fcall_info_cache fcc;\n    FILE *fp;\n    smart_str buf;\n    int method;\n    zval stream;\n} php_curl_write;\n\ntypedef struct {\n    zend_fcall_info_cache fcc;\n    FILE *fp;\n    zend_resource *res;\n    int method;\n    zval stream;\n} php_curl_read;\n\ntypedef struct {\n    php_curl_write *write;\n    php_curl_write *write_header;\n    php_curl_read *read;\n    zval std_err;\n    zend_fcall_info_cache progress;\n    zend_fcall_info_cache xferinfo;\n    zend_fcall_info_cache fnmatch;\n    zend_fcall_info_cache debug;\n#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */\n    zend_fcall_info_cache prereq;\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */\n    zend_fcall_info_cache sshhostkey;\n#endif\n} php_curl_handlers;\n\nstruct _php_curl_error {\n    char str[CURL_ERROR_SIZE + 1];\n    int no;\n};\n\nstruct _php_curl_send_headers {\n    zend_string *str;\n};\n\nstruct _php_curl_free {\n    zend_llist post;\n    zend_llist stream;\n#if PHP_VERSION_ID >= 80500\n    HashTable slist;\n#else\n    HashTable *slist;\n#endif\n};\n\ntypedef struct {\n    CURL *cp;\n    php_curl_handlers handlers;\n    struct _php_curl_free *to_free;\n    struct _php_curl_send_headers header;\n    struct _php_curl_error err;\n    bool in_callback;\n    uint32_t *clone;\n    zval postfields;\n    /* For CURLOPT_PRIVATE */\n    zval private_data;\n    /* CurlShareHandle object set using CURLOPT_SHARE. */\n    struct _php_curlsh *share;\n    zend_object std;\n} php_curl;\n\n#define CURLOPT_SAFE_UPLOAD -1\n\ntypedef struct {\n    zend_fcall_info_cache server_push;\n} php_curlm_handlers;\n\nnamespace swoole {\nnamespace curl {\nclass Multi;\n}\n}  // namespace swoole\n\nusing swoole::curl::Multi;\n\ntypedef struct {\n    Multi *multi;\n    zend_llist easyh;\n    php_curlm_handlers handlers;\n    struct {\n        int no;\n    } err;\n    zend_object std;\n} php_curlm;\n\ntypedef struct _php_curlsh {\n    CURLSH *share;\n    struct {\n        int no;\n    } err;\n    zend_object std;\n} php_curlsh;\n\nphp_curl *swoole_curl_init_handle_into_zval(zval *curl);\nvoid swoole_curl_init_handle(php_curl *ch);\nvoid swoole_curl_cleanup_handle(php_curl *);\nvoid swoole_curl_multi_cleanup_list(void *data);\nvoid swoole_curl_verify_handlers(php_curl *ch, bool reporterror);\nvoid swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source);\n\n/* Consumes `zv` */\nzend_long swoole_curl_get_long(zval *zv);\n\nstatic inline php_curl *curl_from_obj(zend_object *obj) {\n    return (php_curl *) ((char *) (obj) -XtOffsetOf(php_curl, std));\n}\n\n#define Z_CURL_P(zv) curl_from_obj(Z_OBJ_P(zv))\n\nstatic inline php_curlsh *curl_share_from_obj(zend_object *obj) {\n    return (php_curlsh *) ((char *) (obj) -XtOffsetOf(php_curlsh, std));\n}\n\n#define Z_CURL_SHARE_P(zv) curl_share_from_obj(Z_OBJ_P(zv))\nvoid curl_multi_register_class(const zend_function_entry *method_entries);\nzend_result swoole_curl_cast_object(zend_object *obj, zval *result, int type);\n\n#endif /* _PHP_CURL_PRIVATE_H */\n#endif\n"
  },
  {
    "path": "thirdparty/php84/curl/interface.cc",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | Copyright (c) The PHP Group                                          |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | https://www.php.net/license/3_01.txt                                 |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Author: Sterling Hughes <sterling@php.net>                           |\n   +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_cxx.h\"\n\n#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400\n#include \"php_swoole_curl.h\"\nusing namespace swoole;\n\nSW_EXTERN_C_BEGIN\n#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS\n#include \"swoole_curl_interface.h\"\n#include \"curl_arginfo.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#ifdef PHP_WIN32\n#include <winsock2.h>\n#include <sys/types.h>\n#endif\n\n#include <curl/curl.h>\n#include <curl/easy.h>\n\n/* As of curl 7.11.1 this is no longer defined inside curl.h */\n#ifndef HttpPost\n#define HttpPost curl_httppost\n#endif\n\n#if PHP_VERSION_ID < 80500\n/* {{{ cruft for thread safe SSL crypto locks */\n#if defined(ZTS) && defined(HAVE_CURL_OLD_OPENSSL)\n#if defined(HAVE_OPENSSL_CRYPTO_H)\n#define PHP_CURL_NEED_OPENSSL_TSL\n#include <openssl/crypto.h>\n#else\n#warning \"libcurl was compiled with OpenSSL support, but configure could not find \" \\\n\t\"openssl/crypto.h; thus no SSL crypto locking callbacks will be set, which may \" \\\n\t\"cause random crashes on SSL requests\"\n#endif\n#endif /* ZTS && HAVE_CURL_OLD_OPENSSL */\n/* }}} */\n#endif\n\n#include \"zend_smart_str.h\"\n#include \"ext/standard/info.h\"\n#include \"ext/standard/file.h\"\n#include \"ext/standard/url.h\"\n#include \"curl_private.h\"\n\n#ifdef __GNUC__\n/* don't complain about deprecated CURLOPT_* we're exposing to PHP; we\n   need to keep using those to avoid breaking PHP API compatibiltiy */\n#pragma GCC diagnostic ignored \"-Wdeprecated-declarations\"\n#endif\n\n#if PHP_VERSION_ID < 80500\n#ifdef PHP_CURL_NEED_OPENSSL_TSL /* {{{ */\nstatic MUTEX_T *php_curl_openssl_tsl = NULL;\n\n/* Locking callbacks are no longer used since OpenSSL 1.1. Mark the functions as unused to\n * avoid warnings due to this. */\nstatic ZEND_ATTRIBUTE_UNUSED void php_curl_ssl_lock(int mode, int n, const char *file, int line) {\n    if (mode & CRYPTO_LOCK) {\n        tsrm_mutex_lock(php_curl_openssl_tsl[n]);\n    } else {\n        tsrm_mutex_unlock(php_curl_openssl_tsl[n]);\n    }\n}\n\nstatic ZEND_ATTRIBUTE_UNUSED unsigned long php_curl_ssl_id(void) {\n    return (unsigned long) tsrm_thread_id();\n}\n#endif\n/* }}} */\n#endif\n\nstatic zend_class_entry *swoole_native_curl_exception_ce;\nstatic zend_object_handlers swoole_native_curl_exception_handlers;\n\n#define CAAL(s, v) add_assoc_long_ex(return_value, s, sizeof(s) - 1, (zend_long) v);\n#define CAAD(s, v) add_assoc_double_ex(return_value, s, sizeof(s) - 1, (double) v);\n#define CAAS(s, v) add_assoc_string_ex(return_value, s, sizeof(s) - 1, (char *) (v ? v : \"\"));\n#define CAASTR(s, v) add_assoc_str_ex(return_value, s, sizeof(s) - 1, v ? zend_string_copy(v) : ZSTR_EMPTY_ALLOC());\n#define CAAZ(s, v) add_assoc_zval_ex(return_value, s, sizeof(s) - 1, (zval *) v);\n\n#if defined(PHP_WIN32) || defined(__GNUC__)\n#define php_curl_ret(__ret)                                                                                            \\\n    RETVAL_FALSE;                                                                                                      \\\n    return __ret;\n#else\n#define php_curl_ret(__ret)                                                                                            \\\n    RETVAL_FALSE;                                                                                                      \\\n    return;\n#endif\n\nstatic zend_result php_curl_option_str(php_curl *ch, zend_long option, const char *str, const size_t len) {\n    if (zend_char_has_nul_byte(str, len)) {\n        zend_value_error(\"%s(): cURL option must not contain any null bytes\", get_active_function_name());\n        return FAILURE;\n    }\n\n    CURLcode error = curl_easy_setopt(ch->cp, (CURLoption) option, str);\n    SAVE_CURL_ERROR(ch, error);\n\n    return error == CURLE_OK ? SUCCESS : FAILURE;\n}\n\nstatic zend_result php_curl_option_url(php_curl *ch, const zend_string *url) /* {{{ */\n{\n    /* Disable file:// if open_basedir are used */\n    if (PG(open_basedir) && *PG(open_basedir)) {\n        curl_easy_setopt(ch->cp, CURLOPT_PROTOCOLS, CURLPROTO_ALL & ~CURLPROTO_FILE);\n    }\n\n#ifdef PHP_WIN32\n    if (zend_string_starts_with_literal_ci(url, \"file://\") && '/' != ZSTR_VAL(url)[sizeof(\"file://\") - 1] &&\n        ZSTR_LEN(url) < MAXPATHLEN - 2) {\n        char _tmp[MAXPATHLEN] = {0};\n\n        memmove(_tmp, \"file:///\", sizeof(\"file:///\") - 1);\n        memmove(_tmp + sizeof(\"file:///\") - 1,\n                ZSTR_VAL(url) + sizeof(\"file://\") - 1,\n                ZSTR_LEN(url) - sizeof(\"file://\") + 1);\n\n        return php_curl_option_str(ch, CURLOPT_URL, _tmp, ZSTR_LEN(url) + 1);\n    }\n#endif\n\n    return php_curl_option_str(ch, CURLOPT_URL, ZSTR_VAL(url), ZSTR_LEN(url));\n}\n/* }}} */\n\nvoid swoole_curl_verify_handlers(php_curl *ch, bool reporterror) /* {{{ */\n{\n    php_stream *stream;\n\n    ZEND_ASSERT(ch);\n\n    if (!Z_ISUNDEF(ch->handlers.std_err)) {\n        stream = (php_stream *) zend_fetch_resource2_ex(\n            &ch->handlers.std_err, NULL, php_file_le_stream(), php_file_le_pstream());\n        if (stream == NULL) {\n            if (reporterror) {\n                php_error_docref(NULL, E_WARNING, \"CURLOPT_STDERR resource has gone away, resetting to stderr\");\n            }\n            zval_ptr_dtor(&ch->handlers.std_err);\n            ZVAL_UNDEF(&ch->handlers.std_err);\n\n            curl_easy_setopt(ch->cp, CURLOPT_STDERR, stderr);\n        }\n    }\n    if (ch->handlers.read && !Z_ISUNDEF(ch->handlers.read->stream)) {\n        stream = (php_stream *) zend_fetch_resource2_ex(\n            &ch->handlers.read->stream, NULL, php_file_le_stream(), php_file_le_pstream());\n        if (stream == NULL) {\n            if (reporterror) {\n                php_error_docref(NULL, E_WARNING, \"CURLOPT_INFILE resource has gone away, resetting to default\");\n            }\n            zval_ptr_dtor(&ch->handlers.read->stream);\n            ZVAL_UNDEF(&ch->handlers.read->stream);\n            ch->handlers.read->res = NULL;\n            ch->handlers.read->fp = 0;\n\n            curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch);\n        }\n    }\n    if (ch->handlers.write_header && !Z_ISUNDEF(ch->handlers.write_header->stream)) {\n        stream = (php_stream *) zend_fetch_resource2_ex(\n            &ch->handlers.write_header->stream, NULL, php_file_le_stream(), php_file_le_pstream());\n        if (stream == NULL) {\n            if (reporterror) {\n                php_error_docref(NULL, E_WARNING, \"CURLOPT_WRITEHEADER resource has gone away, resetting to default\");\n            }\n            zval_ptr_dtor(&ch->handlers.write_header->stream);\n            ZVAL_UNDEF(&ch->handlers.write_header->stream);\n            ch->handlers.write_header->fp = 0;\n\n            ch->handlers.write_header->method = PHP_CURL_IGNORE;\n            curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch);\n        }\n    }\n    if (ch->handlers.write && !Z_ISUNDEF(ch->handlers.write->stream)) {\n        stream = (php_stream *) zend_fetch_resource2_ex(\n            &ch->handlers.write->stream, NULL, php_file_le_stream(), php_file_le_pstream());\n        if (stream == NULL) {\n            if (reporterror) {\n                php_error_docref(NULL, E_WARNING, \"CURLOPT_FILE resource has gone away, resetting to default\");\n            }\n            zval_ptr_dtor(&ch->handlers.write->stream);\n            ZVAL_UNDEF(&ch->handlers.write->stream);\n            ch->handlers.write->fp = 0;\n\n            ch->handlers.write->method = PHP_CURL_STDOUT;\n            curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch);\n        }\n    }\n    return;\n}\n/* }}} */\n\n/* CurlHandle class */\nzend_class_entry *swoole_coroutine_curl_handle_ce;\nstatic zend_object_handlers swoole_coroutine_curl_handle_handlers;\n\nstatic zend_object *swoole_curl_create_object(zend_class_entry *class_type);\nstatic void swoole_curl_free_obj(zend_object *object);\nstatic HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n);\nstatic zend_function *swoole_curl_get_constructor(zend_object *object);\nstatic zend_object *swoole_curl_clone_obj(zend_object *object);\nstatic inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields);\nSW_EXTERN_C_END\n\nvoid swoole_native_curl_minit(int module_number) {\n    if (!SWOOLE_G(cli)) {\n        return;\n    }\n\n#if PHP_VERSION_ID < 80500\n#ifdef PHP_CURL_NEED_OPENSSL_TSL\n    if (!CRYPTO_get_id_callback()) {\n        int i, c = CRYPTO_num_locks();\n\n        php_curl_openssl_tsl = (MUTEX_T *) malloc(c * sizeof(MUTEX_T));\n        if (!php_curl_openssl_tsl) {\n            return;\n        }\n\n        for (i = 0; i < c; ++i) {\n            php_curl_openssl_tsl[i] = tsrm_mutex_alloc();\n        }\n\n        CRYPTO_set_id_callback(php_curl_ssl_id);\n        CRYPTO_set_locking_callback(php_curl_ssl_lock);\n    }\n#endif\n#endif\n\n    swoole_coroutine_curl_handle_ce = curl_ce;\n    swoole_coroutine_curl_handle_ce->create_object = swoole_curl_create_object;\n    swoole_coroutine_curl_handle_ce->default_object_handlers = &swoole_coroutine_curl_handle_handlers;\n\n    memcpy(&swoole_coroutine_curl_handle_handlers, &std_object_handlers, sizeof(zend_object_handlers));\n    swoole_coroutine_curl_handle_handlers.offset = XtOffsetOf(php_curl, std);\n    swoole_coroutine_curl_handle_handlers.free_obj = swoole_curl_free_obj;\n    swoole_coroutine_curl_handle_handlers.get_gc = swoole_curl_get_gc;\n    swoole_coroutine_curl_handle_handlers.get_constructor = swoole_curl_get_constructor;\n    swoole_coroutine_curl_handle_handlers.clone_obj = swoole_curl_clone_obj;\n    swoole_coroutine_curl_handle_handlers.cast_object = swoole_curl_cast_object;\n    swoole_coroutine_curl_handle_handlers.compare = [](zval *o1, zval *o2) { return ZEND_UNCOMPARABLE; };\n\n    swoole_coroutine_curl_handle_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES;\n\n    curl_multi_register_class(nullptr);\n\n    zend_unregister_functions(swoole_native_curl_functions, -1, CG(function_table));\n    zend_register_functions(NULL, swoole_native_curl_functions, NULL, MODULE_PERSISTENT);\n\n    SW_INIT_CLASS_ENTRY_EX(swoole_native_curl_exception,\n                           \"Swoole\\\\Coroutine\\\\Curl\\\\Exception\",\n                           \"Co\\\\Coroutine\\\\Curl\\\\Exception\",\n                           nullptr,\n                           swoole_exception);\n}\n/* }}} */\n\n/* CurlHandle class */\n\nstatic zend_object *swoole_curl_create_object(zend_class_entry *class_type) {\n    php_curl *intern = (php_curl *) zend_object_alloc(sizeof(php_curl), class_type);\n\n    zend_object_std_init(&intern->std, class_type);\n    object_properties_init(&intern->std, class_type);\n\n    return &intern->std;\n}\n\nstatic zend_function *swoole_curl_get_constructor(zend_object *object) {\n    zend_throw_error(NULL, \"Cannot directly construct CurlHandle, use curl_init() instead\");\n    return NULL;\n}\n\nstatic zend_object *swoole_curl_clone_obj(zend_object *object) {\n    php_curl *ch;\n    CURL *cp;\n    zval *postfields;\n    zend_object *clone_object;\n    php_curl *clone_ch;\n\n    clone_object = swoole_curl_create_object(curl_ce);\n    clone_ch = curl_from_obj(clone_object);\n    swoole_curl_init_handle(clone_ch);\n\n    ch = curl_from_obj(object);\n    cp = curl_easy_duphandle(ch->cp);\n    if (!cp) {\n        zend_throw_exception(NULL, \"Failed to clone CurlHandle\", 0);\n        return &clone_ch->std;\n    }\n\n    clone_ch->cp = cp;\n    swoole_setup_easy_copy_handlers(clone_ch, ch);\n    swoole::curl::create_handle(clone_ch->cp);\n\n    postfields = &ch->postfields;\n    if (Z_TYPE_P(postfields) != IS_UNDEF) {\n        if (build_mime_structure_from_hash(clone_ch, postfields) == FAILURE) {\n            zend_throw_exception(NULL, \"Failed to clone CurlHandle\", 0);\n            return &clone_ch->std;\n        }\n    }\n\n    return &clone_ch->std;\n}\n\nstatic HashTable *swoole_curl_get_gc(zend_object *object, zval **table, int *n) {\n    php_curl *curl = curl_from_obj(object);\n\n    zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();\n\n    zend_get_gc_buffer_add_zval(gc_buffer, &curl->postfields);\n    if (curl->handlers.read) {\n        if (ZEND_FCC_INITIALIZED(curl->handlers.read->fcc)) {\n            zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.read->fcc);\n        }\n        zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.read->stream);\n    }\n\n    if (curl->handlers.write) {\n        if (ZEND_FCC_INITIALIZED(curl->handlers.write->fcc)) {\n            zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.write->fcc);\n        }\n        zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write->stream);\n    }\n\n    if (curl->handlers.write_header) {\n        if (ZEND_FCC_INITIALIZED(curl->handlers.write_header->fcc)) {\n            zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.write_header->fcc);\n        }\n        zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.write_header->stream);\n    }\n\n    if (ZEND_FCC_INITIALIZED(curl->handlers.progress)) {\n        zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.progress);\n    }\n\n    if (ZEND_FCC_INITIALIZED(curl->handlers.xferinfo)) {\n        zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.xferinfo);\n    }\n\n    if (ZEND_FCC_INITIALIZED(curl->handlers.fnmatch)) {\n        zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.fnmatch);\n    }\n\n    if (ZEND_FCC_INITIALIZED(curl->handlers.debug)) {\n        zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.debug);\n    }\n\n#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */\n    if (ZEND_FCC_INITIALIZED(curl->handlers.prereq)) {\n        zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.prereq);\n    }\n#endif\n\n#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */\n    if (ZEND_FCC_INITIALIZED(curl->handlers.sshhostkey)) {\n        zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.sshhostkey);\n    }\n#endif\n\n    zend_get_gc_buffer_add_zval(gc_buffer, &curl->handlers.std_err);\n    zend_get_gc_buffer_add_zval(gc_buffer, &curl->private_data);\n\n    zend_get_gc_buffer_use(gc_buffer, table, n);\n\n#if PHP_VERSION_ID >= 80500\n    /* CurlHandle can never have properties as it's final and has strict-properties on.\n     * Avoid building a hash table. */\n    return NULL;\n#else\n    return zend_std_get_properties(object);\n#endif\n}\n\nzend_result swoole_curl_cast_object(zend_object *obj, zval *result, int type) {\n    if (type == IS_LONG) {\n        /* For better backward compatibility, make (int) $curl_handle return the object ID,\n         * similar to how it previously returned the resource ID. */\n        ZVAL_LONG(result, obj->handle);\n        return SUCCESS;\n    }\n\n    return zend_std_cast_object_tostring(obj, result, type);\n}\n\nvoid swoole_native_curl_mshutdown() {}\n\n/* {{{ curl_write */\nstatic size_t fn_write(char *data, size_t size, size_t nmemb, void *ctx) {\n    php_curl *ch = (php_curl *) ctx;\n    php_curl_write *write_handler = ch->handlers.write;\n    size_t length = size * nmemb;\n\n#if PHP_CURL_DEBUG\n    fprintf(stderr, \"curl_write() called\\n\");\n    fprintf(stderr, \"data = %s, size = %d, nmemb = %d, ctx = %x\\n\", data, size, nmemb, ctx);\n#endif\n\n    switch (write_handler->method) {\n    case PHP_CURL_STDOUT:\n        PHPWRITE(data, length);\n        break;\n    case PHP_CURL_FILE:\n        return fwrite(data, size, nmemb, write_handler->fp);\n    case PHP_CURL_RETURN:\n        if (length > 0) {\n            smart_str_appendl(&write_handler->buf, data, (int) length);\n        }\n        break;\n    case PHP_CURL_USER: {\n        zval argv[2];\n        zval retval;\n\n        GC_ADDREF(&ch->std);\n        ZVAL_OBJ(&argv[0], &ch->std);\n        ZVAL_STRINGL(&argv[1], data, length);\n\n        ch->in_callback = true;\n        zend_call_known_fcc(&write_handler->fcc, &retval, /* param_count */ 2, argv, /* named_params */ NULL);\n        ch->in_callback = false;\n        if (!Z_ISUNDEF(retval)) {\n            swoole_curl_verify_handlers(ch, /* reporterror */ true);\n            /* TODO Check callback returns an int or something castable to int */\n            length = swoole_curl_get_long(&retval);\n        }\n\n        zval_ptr_dtor(&argv[0]);\n        zval_ptr_dtor(&argv[1]);\n        break;\n    }\n    }\n\n    return length;\n}\n/* }}} */\n\n/* {{{ curl_fnmatch */\nstatic int fn_fnmatch(void *ctx, const char *pattern, const char *string) {\n    php_curl *ch = (php_curl *) ctx;\n    int rval = CURL_FNMATCHFUNC_FAIL;\n    zval argv[3];\n    zval retval;\n\n    GC_ADDREF(&ch->std);\n    ZVAL_OBJ(&argv[0], &ch->std);\n    ZVAL_STRING(&argv[1], pattern);\n    ZVAL_STRING(&argv[2], string);\n\n    ch->in_callback = true;\n    zend_call_known_fcc(&ch->handlers.fnmatch, &retval, /* param_count */ 3, argv, /* named_params */ NULL);\n    ch->in_callback = false;\n\n    if (!Z_ISUNDEF(retval)) {\n        swoole_curl_verify_handlers(ch, /* reporterror */ true);\n        /* TODO Check callback returns an int or something castable to int */\n        rval = swoole_curl_get_long(&retval);\n    }\n    zval_ptr_dtor(&argv[0]);\n    zval_ptr_dtor(&argv[1]);\n    zval_ptr_dtor(&argv[2]);\n    return rval;\n}\n/* }}} */\n\n/* {{{ curl_progress */\nstatic int fn_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) {\n    php_curl *ch = (php_curl *) clientp;\n    int rval = 0;\n\n#if PHP_CURL_DEBUG\n    fprintf(stderr, \"curl_progress() called\\n\");\n    fprintf(stderr,\n            \"clientp = %x, dltotal = %f, dlnow = %f, ultotal = %f, ulnow = %f\\n\",\n            clientp,\n            dltotal,\n            dlnow,\n            ultotal,\n            ulnow);\n#endif\n\n    zval args[5];\n    zval retval;\n\n    GC_ADDREF(&ch->std);\n    ZVAL_OBJ(&args[0], &ch->std);\n    ZVAL_LONG(&args[1], (zend_long) dltotal);\n    ZVAL_LONG(&args[2], (zend_long) dlnow);\n    ZVAL_LONG(&args[3], (zend_long) ultotal);\n    ZVAL_LONG(&args[4], (zend_long) ulnow);\n\n    ch->in_callback = true;\n    zend_call_known_fcc(&ch->handlers.progress, &retval, /* param_count */ 5, args, /* named_params */ NULL);\n    ch->in_callback = false;\n\n    if (!Z_ISUNDEF(retval)) {\n        swoole_curl_verify_handlers(ch, /* reporterror */ true);\n        /* TODO Check callback returns an int or something castable to int */\n        if (0 != swoole_curl_get_long(&retval)) {\n            rval = 1;\n        }\n    }\n\n    zval_ptr_dtor(&args[0]);\n    return rval;\n}\n/* }}} */\n\n/* {{{ curl_xferinfo */\nstatic int fn_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {\n    php_curl *ch = (php_curl *) clientp;\n    int rval = 0;\n\n#if PHP_CURL_DEBUG\n    fprintf(stderr, \"curl_xferinfo() called\\n\");\n    fprintf(stderr,\n            \"clientp = %x, dltotal = %ld, dlnow = %ld, ultotal = %ld, ulnow = %ld\\n\",\n            clientp,\n            dltotal,\n            dlnow,\n            ultotal,\n            ulnow);\n#endif\n\n    zval argv[5];\n    zval retval;\n\n    GC_ADDREF(&ch->std);\n    ZVAL_OBJ(&argv[0], &ch->std);\n    ZVAL_LONG(&argv[1], dltotal);\n    ZVAL_LONG(&argv[2], dlnow);\n    ZVAL_LONG(&argv[3], ultotal);\n    ZVAL_LONG(&argv[4], ulnow);\n\n    ch->in_callback = true;\n    zend_call_known_fcc(&ch->handlers.xferinfo, &retval, /* param_count */ 5, argv, /* named_params */ NULL);\n    ch->in_callback = false;\n\n    if (!Z_ISUNDEF(retval)) {\n        swoole_curl_verify_handlers(ch, /* reporterror */ true);\n        /* TODO Check callback returns an int or something castable to int */\n        if (0 != swoole_curl_get_long(&retval)) {\n            rval = 1;\n        }\n    }\n\n    zval_ptr_dtor(&argv[0]);\n    return rval;\n}\n/* }}} */\n\n#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */\nstatic int fn_prereqfunction(\n    void *clientp, char *conn_primary_ip, char *conn_local_ip, int conn_primary_port, int conn_local_port) {\n    php_curl *ch = (php_curl *) clientp;\n    int rval = CURL_PREREQFUNC_OK;\n\n    // when CURLOPT_PREREQFUNCTION is set to null, curl_prereqfunction still\n    // gets called. Return CURL_PREREQFUNC_OK immediately in this case to avoid\n    // zend_call_known_fcc() with an uninitialized FCC.\n    if (!ZEND_FCC_INITIALIZED(ch->handlers.prereq)) {\n        return rval;\n    }\n\n#if PHP_CURL_DEBUG\n    fprintf(stderr, \"curl_prereqfunction() called\\n\");\n    fprintf(stderr,\n            \"conn_primary_ip = %s, conn_local_ip = %s, conn_primary_port = %d, conn_local_port = %d\\n\",\n            conn_primary_ip,\n            conn_local_ip,\n            conn_primary_port,\n            conn_local_port);\n#endif\n\n    zval args[5];\n    zval retval;\n\n    GC_ADDREF(&ch->std);\n    ZVAL_OBJ(&args[0], &ch->std);\n    ZVAL_STRING(&args[1], conn_primary_ip);\n    ZVAL_STRING(&args[2], conn_local_ip);\n    ZVAL_LONG(&args[3], conn_primary_port);\n    ZVAL_LONG(&args[4], conn_local_port);\n\n    ch->in_callback = true;\n    zend_call_known_fcc(&ch->handlers.prereq, &retval, /* param_count */ 5, args, /* named_params */ NULL);\n    ch->in_callback = false;\n\n    if (!Z_ISUNDEF(retval)) {\n        swoole_curl_verify_handlers(ch, /* reporterror */ true);\n        if (Z_TYPE(retval) == IS_LONG) {\n            zend_long retval_long = Z_LVAL(retval);\n            if (retval_long == CURL_PREREQFUNC_OK || retval_long == CURL_PREREQFUNC_ABORT) {\n                rval = retval_long;\n            } else {\n                zend_value_error(\"The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or \"\n                                 \"CURL_PREREQFUNC_ABORT\");\n            }\n        } else {\n            zend_type_error(\n                \"The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT\");\n        }\n    }\n\n    zval_ptr_dtor(&args[0]);\n    zval_ptr_dtor(&args[1]);\n    zval_ptr_dtor(&args[2]);\n\n    return rval;\n}\n#endif\n\n#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */\nstatic int fn_ssh_hostkeyfunction(void *clientp, int keytype, const char *key, size_t keylen) {\n    php_curl *ch = (php_curl *) clientp;\n    int rval = CURLKHMATCH_MISMATCH; /* cancel connection in case of an exception */\n\n#if PHP_CURL_DEBUG\n    fprintf(stderr, \"curl_ssh_hostkeyfunction() called\\n\");\n    fprintf(stderr, \"clientp = %x, keytype = %d, key = %s, keylen = %zu\\n\", clientp, keytype, key, keylen);\n#endif\n\n    zval args[4];\n    zval retval;\n\n    GC_ADDREF(&ch->std);\n    ZVAL_OBJ(&args[0], &ch->std);\n    ZVAL_LONG(&args[1], keytype);\n    ZVAL_STRINGL(&args[2], key, keylen);\n    ZVAL_LONG(&args[3], keylen);\n\n    ch->in_callback = true;\n    zend_call_known_fcc(&ch->handlers.sshhostkey, &retval, /* param_count */ 4, args, /* named_params */ NULL);\n    ch->in_callback = false;\n\n    if (!Z_ISUNDEF(retval)) {\n        swoole_curl_verify_handlers(ch, /* reporterror */ true);\n        if (Z_TYPE(retval) == IS_LONG) {\n            zend_long retval_long = Z_LVAL(retval);\n            if (retval_long == CURLKHMATCH_OK || retval_long == CURLKHMATCH_MISMATCH) {\n                rval = retval_long;\n            } else {\n                zend_throw_error(NULL,\n                                 \"The CURLOPT_SSH_HOSTKEYFUNCTION callback must return either CURLKHMATCH_OK or \"\n                                 \"CURLKHMATCH_MISMATCH\");\n            }\n        } else {\n            zend_throw_error(\n                NULL,\n                \"The CURLOPT_SSH_HOSTKEYFUNCTION callback must return either CURLKHMATCH_OK or CURLKHMATCH_MISMATCH\");\n            zval_ptr_dtor(&retval);\n        }\n    }\n\n    zval_ptr_dtor(&args[0]);\n    zval_ptr_dtor(&args[2]);\n    return rval;\n}\n#endif\n\n/* {{{ curl_read */\nstatic size_t fn_read(char *data, size_t size, size_t nmemb, void *ctx) {\n    php_curl *ch = (php_curl *) ctx;\n    php_curl_read *read_handler = ch->handlers.read;\n    size_t length = 0;\n\n    switch (read_handler->method) {\n    case PHP_CURL_DIRECT:\n        if (read_handler->fp) {\n            length = fread(data, size, nmemb, read_handler->fp);\n        }\n        break;\n    case PHP_CURL_USER: {\n        zval argv[3];\n        zval retval;\n\n        GC_ADDREF(&ch->std);\n        ZVAL_OBJ(&argv[0], &ch->std);\n        if (read_handler->res) {\n            GC_ADDREF(read_handler->res);\n            ZVAL_RES(&argv[1], read_handler->res);\n        } else {\n            ZVAL_NULL(&argv[1]);\n        }\n        ZVAL_LONG(&argv[2], (int) size * nmemb);\n\n        ch->in_callback = true;\n        zend_call_known_fcc(&read_handler->fcc, &retval, /* param_count */ 3, argv, /* named_params */ NULL);\n        ch->in_callback = false;\n        if (!Z_ISUNDEF(retval)) {\n            swoole_curl_verify_handlers(ch, /* reporterror */ true);\n            if (Z_TYPE(retval) == IS_STRING) {\n                length = MIN((size * nmemb), Z_STRLEN(retval));\n                memcpy(data, Z_STRVAL(retval), length);\n            } else if (Z_TYPE(retval) == IS_LONG) {\n                length = Z_LVAL_P(&retval);\n            }\n            // TODO Do type error if invalid type?\n            zval_ptr_dtor(&retval);\n        }\n\n        zval_ptr_dtor(&argv[0]);\n        zval_ptr_dtor(&argv[1]);\n        break;\n    }\n    }\n\n    return length;\n}\n/* }}} */\n\n/* {{{ curl_write_header */\nstatic size_t fn_write_header(char *data, size_t size, size_t nmemb, void *ctx) {\n    php_curl *ch = (php_curl *) ctx;\n    php_curl_write *write_handler = ch->handlers.write_header;\n    size_t length = size * nmemb;\n\n    switch (write_handler->method) {\n    case PHP_CURL_STDOUT:\n        /* Handle special case write when we're returning the entire transfer\n         */\n        if (ch->handlers.write->method == PHP_CURL_RETURN && length > 0) {\n            smart_str_appendl(&ch->handlers.write->buf, data, (int) length);\n        } else {\n            PHPWRITE(data, length);\n        }\n        break;\n    case PHP_CURL_FILE:\n        return fwrite(data, size, nmemb, write_handler->fp);\n    case PHP_CURL_USER: {\n        zval argv[2];\n        zval retval;\n\n        GC_ADDREF(&ch->std);\n        ZVAL_OBJ(&argv[0], &ch->std);\n        ZVAL_STRINGL(&argv[1], data, length);\n\n        ch->in_callback = true;\n        zend_call_known_fcc(&write_handler->fcc, &retval, /* param_count */ 2, argv, /* named_params */ NULL);\n        ch->in_callback = false;\n        if (!Z_ISUNDEF(retval)) {\n            // TODO: Check for valid int type for return value\n            swoole_curl_verify_handlers(ch, /* reporterror */ true);\n            length = swoole_curl_get_long(&retval);\n        }\n        zval_ptr_dtor(&argv[0]);\n        zval_ptr_dtor(&argv[1]);\n        break;\n    }\n\n    case PHP_CURL_IGNORE:\n        return length;\n\n    default:\n        return -1;\n    }\n\n    return length;\n}\n/* }}} */\n\nstatic int fn_debug(CURL *handle, curl_infotype type, char *data, size_t size, void *clientp) /* {{{ */\n{\n    php_curl *ch = (php_curl *) clientp;\n\n#if PHP_CURL_DEBUG\n    fprintf(stderr, \"curl_debug() called\\n\");\n    fprintf(stderr, \"type = %d, data = %s\\n\", type, data);\n#endif\n\n    // Implicitly store the headers for compatibility with CURLINFO_HEADER_OUT\n    // used as a Curl option. Previously, setting CURLINFO_HEADER_OUT set curl_debug\n    // as the CURLOPT_DEBUGFUNCTION and stored the debug data when type is set to\n    // CURLINFO_HEADER_OUT. For backward compatibility, we now store the headers\n    // but also call the user-callback function if available.\n    if (type == CURLINFO_HEADER_OUT) {\n        if (ch->header.str) {\n            zend_string_release_ex(ch->header.str, 0);\n        }\n        ch->header.str = zend_string_init(data, size, 0);\n    }\n\n    if (!ZEND_FCC_INITIALIZED(ch->handlers.debug)) {\n        return 0;\n    }\n\n    zval args[3];\n\n    GC_ADDREF(&ch->std);\n    ZVAL_OBJ(&args[0], &ch->std);\n    ZVAL_LONG(&args[1], type);\n    ZVAL_STRINGL(&args[2], data, size);\n\n    ch->in_callback = true;\n    zend_call_known_fcc(&ch->handlers.debug, NULL, /* param_count */ 3, args, /* named_params */ NULL);\n    ch->in_callback = false;\n\n    zval_ptr_dtor(&args[0]);\n    zval_ptr_dtor(&args[2]);\n\n    return 0;\n}\n/* }}} */\n\n/* {{{ curl_free_post */\nstatic void curl_free_post(void **post) {\n    curl_mime_free((curl_mime *) *post);\n}\n/* }}} */\n\nstruct mime_data_cb_arg {\n    zend_string *filename;\n    php_stream *stream;\n};\n\n/* {{{ curl_free_cb_arg */\nstatic void curl_free_cb_arg(void **cb_arg_p) {\n    struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) *cb_arg_p;\n\n    ZEND_ASSERT(cb_arg->stream == NULL);\n    zend_string_release(cb_arg->filename);\n    efree(cb_arg);\n}\n/* }}} */\n\n/* {{{ curl_free_slist */\nstatic void curl_free_slist(zval *el) {\n    curl_slist_free_all(((struct curl_slist *) Z_PTR_P(el)));\n}\n/* }}} */\n\nphp_curl *swoole_curl_init_handle_into_zval(zval *curl) {\n    php_curl *ch;\n\n    object_init_ex(curl, swoole_coroutine_curl_handle_ce);\n    ch = Z_CURL_P(curl);\n\n    swoole_curl_init_handle(ch);\n\n    return ch;\n}\n\nvoid swoole_curl_init_handle(php_curl *ch) {\n    ch->to_free = (struct _php_curl_free *) ecalloc(1, sizeof(struct _php_curl_free));\n    ch->handlers.write = (php_curl_write *) ecalloc(1, sizeof(php_curl_write));\n    ch->handlers.write_header = (php_curl_write *) ecalloc(1, sizeof(php_curl_write));\n    ch->handlers.read = (php_curl_read *) ecalloc(1, sizeof(php_curl_read));\n    ch->handlers.progress = empty_fcall_info_cache;\n    ch->handlers.xferinfo = empty_fcall_info_cache;\n    ch->handlers.fnmatch = empty_fcall_info_cache;\n    ch->handlers.debug = empty_fcall_info_cache;\n#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */\n    ch->handlers.prereq = empty_fcall_info_cache;\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */\n    ch->handlers.sshhostkey = empty_fcall_info_cache;\n#endif\n    ch->clone = (uint32_t *) emalloc(sizeof(uint32_t));\n    *ch->clone = 1;\n\n    memset(&ch->err, 0, sizeof(struct _php_curl_error));\n\n    zend_llist_init(&ch->to_free->post, sizeof(struct HttpPost *), (llist_dtor_func_t) curl_free_post, 0);\n    zend_llist_init(&ch->to_free->stream, sizeof(struct mime_data_cb_arg *), (llist_dtor_func_t) curl_free_cb_arg, 0);\n#if PHP_VERSION_ID >= 80500\n    zend_hash_init(&ch->to_free->slist, 4, NULL, curl_free_slist, 0);\n#else\n    ch->to_free->slist = (HashTable *) emalloc(sizeof(HashTable));\n    zend_hash_init(ch->to_free->slist, 4, NULL, curl_free_slist, 0);\n#endif\n    ZVAL_UNDEF(&ch->postfields);\n}\n\n/* }}} */\n\n/* {{{ create_certinfo */\nstatic void create_certinfo(struct curl_certinfo *ci, zval *listcode) {\n    int i;\n\n    if (ci) {\n        zval certhash;\n\n        for (i = 0; i < ci->num_of_certs; i++) {\n            struct curl_slist *slist;\n\n            array_init(&certhash);\n            for (slist = ci->certinfo[i]; slist; slist = slist->next) {\n                char s[64];\n                char *tmp;\n                strncpy(s, slist->data, sizeof(s));\n                s[sizeof(s) - 1] = '\\0';\n                tmp = (char *) memchr(s, ':', sizeof(s));\n                if (tmp) {\n                    *tmp = '\\0';\n                    size_t len = strlen(s);\n                    add_assoc_string(&certhash, s, &slist->data[len + 1]);\n                } else {\n                    php_error_docref(NULL, E_WARNING, \"Could not extract hash key from certificate info\");\n                }\n            }\n            add_next_index_zval(listcode, &certhash);\n        }\n    }\n}\n/* }}} */\n\n/* {{{ _php_curl_set_default_options()\n   Set default options for a handle */\nstatic void _php_curl_set_default_options(php_curl *ch) {\n    char *cainfo;\n\n    curl_easy_setopt(ch->cp, CURLOPT_NOPROGRESS, 1L);\n    curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0L);\n    curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str);\n    curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, fn_write);\n    curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch);\n    curl_easy_setopt(ch->cp, CURLOPT_READFUNCTION, fn_read);\n    curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch);\n    curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, fn_write_header);\n    curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch);\n    curl_easy_setopt(ch->cp, CURLOPT_DNS_CACHE_TIMEOUT, 120L);\n    curl_easy_setopt(ch->cp, CURLOPT_MAXREDIRS, 20L); /* prevent infinite redirects */\n\n    cainfo = INI_STR(\"openssl.cafile\");\n    if (!(cainfo && cainfo[0] != '\\0')) {\n        cainfo = INI_STR(\"curl.cainfo\");\n    }\n    if (cainfo && cainfo[0] != '\\0') {\n        curl_easy_setopt(ch->cp, CURLOPT_CAINFO, cainfo);\n    }\n\n#ifdef ZTS\n    curl_easy_setopt(ch->cp, CURLOPT_NOSIGNAL, 1L);\n#endif\n}\n/* }}} */\n\n/* {{{ Initialize a cURL session */\nPHP_FUNCTION(swoole_native_curl_init) {\n    php_curl *ch;\n    CURL *cp;\n    zend_string *url = NULL;\n\n    ZEND_PARSE_PARAMETERS_START(0, 1)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_STR_OR_NULL(url)\n    ZEND_PARSE_PARAMETERS_END();\n\n    cp = curl_easy_init();\n    if (!cp) {\n        php_error_docref(NULL, E_WARNING, \"Could not initialize a new cURL handle\");\n        RETURN_FALSE;\n    }\n\n    ch = swoole_curl_init_handle_into_zval(return_value);\n\n    ch->cp = cp;\n\n    ch->handlers.write->method = PHP_CURL_STDOUT;\n    ch->handlers.read->method = PHP_CURL_DIRECT;\n    ch->handlers.write_header->method = PHP_CURL_IGNORE;\n\n    _php_curl_set_default_options(ch);\n    swoole::curl::create_handle(cp);\n\n    if (url) {\n        if (php_curl_option_url(ch, url) == FAILURE) {\n            zval_ptr_dtor(return_value);\n            RETURN_FALSE;\n        }\n    }\n}\n/* }}} */\n\nstatic void php_curl_copy_fcc_with_option(php_curl *ch,\n                                          CURLoption option,\n                                          zend_fcall_info_cache *target_fcc,\n                                          zend_fcall_info_cache *source_fcc) {\n    if (ZEND_FCC_INITIALIZED(*source_fcc)) {\n        zend_fcc_dup(target_fcc, source_fcc);\n        curl_easy_setopt(ch->cp, (CURLoption) option, (void *) ch);\n    }\n}\n\nvoid swoole_setup_easy_copy_handlers(php_curl *ch, php_curl *source) {\n    if (!Z_ISUNDEF(source->handlers.write->stream)) {\n        Z_ADDREF(source->handlers.write->stream);\n    }\n    ch->handlers.write->stream = source->handlers.write->stream;\n    ch->handlers.write->method = source->handlers.write->method;\n    if (!Z_ISUNDEF(source->handlers.read->stream)) {\n        Z_ADDREF(source->handlers.read->stream);\n    }\n    ch->handlers.read->stream = source->handlers.read->stream;\n    ch->handlers.read->method = source->handlers.read->method;\n    ch->handlers.write_header->method = source->handlers.write_header->method;\n    if (!Z_ISUNDEF(source->handlers.write_header->stream)) {\n        Z_ADDREF(source->handlers.write_header->stream);\n    }\n    ch->handlers.write_header->stream = source->handlers.write_header->stream;\n\n    ch->handlers.write->fp = source->handlers.write->fp;\n    ch->handlers.write_header->fp = source->handlers.write_header->fp;\n    ch->handlers.read->fp = source->handlers.read->fp;\n    ch->handlers.read->res = source->handlers.read->res;\n\n    if (ZEND_FCC_INITIALIZED(source->handlers.read->fcc)) {\n        zend_fcc_dup(&source->handlers.read->fcc, &source->handlers.read->fcc);\n    }\n    if (ZEND_FCC_INITIALIZED(source->handlers.write->fcc)) {\n        zend_fcc_dup(&source->handlers.write->fcc, &source->handlers.write->fcc);\n    }\n    if (ZEND_FCC_INITIALIZED(source->handlers.write_header->fcc)) {\n        zend_fcc_dup(&source->handlers.write_header->fcc, &source->handlers.write_header->fcc);\n    }\n\n    curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str);\n    curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch);\n    curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch);\n    curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch);\n    curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *) ch);\n\n    php_curl_copy_fcc_with_option(ch, CURLOPT_PROGRESSDATA, &ch->handlers.progress, &source->handlers.progress);\n    php_curl_copy_fcc_with_option(ch, CURLOPT_XFERINFODATA, &ch->handlers.xferinfo, &source->handlers.xferinfo);\n    php_curl_copy_fcc_with_option(ch, CURLOPT_FNMATCH_DATA, &ch->handlers.fnmatch, &source->handlers.fnmatch);\n    php_curl_copy_fcc_with_option(ch, CURLOPT_DEBUGDATA, &ch->handlers.debug, &source->handlers.debug);\n#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */\n    php_curl_copy_fcc_with_option(ch, CURLOPT_PREREQDATA, &ch->handlers.prereq, &source->handlers.prereq);\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */\n    php_curl_copy_fcc_with_option(ch, CURLOPT_SSH_HOSTKEYDATA, &ch->handlers.sshhostkey, &source->handlers.sshhostkey);\n#endif\n\n    ZVAL_COPY(&ch->private_data, &source->private_data);\n\n#if PHP_VERSION_ID < 80500\n    efree(ch->to_free->slist);\n#endif\n    efree(ch->to_free);\n    ch->to_free = source->to_free;\n    efree(ch->clone);\n    ch->clone = source->clone;\n\n    /* Keep track of cloned copies to avoid invoking curl destructors for every clone */\n    (*source->clone)++;\n}\n\nzend_long swoole_curl_get_long(zval *zv)\n{\n    if (EXPECTED(Z_TYPE_P(zv) == IS_LONG)) {\n        return Z_LVAL_P(zv);\n    } else {\n        zend_long ret = zval_get_long(zv);\n        zval_ptr_dtor(zv);\n        return ret;\n    }\n}\n\nstatic size_t read_cb(char *buffer, size_t size, size_t nitems, void *arg) /* {{{ */\n{\n    struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg;\n    ssize_t numread;\n\n    if (cb_arg->stream == NULL) {\n        if (!(cb_arg->stream = php_stream_open_wrapper(ZSTR_VAL(cb_arg->filename), \"rb\", IGNORE_PATH, NULL))) {\n            return CURL_READFUNC_ABORT;\n        }\n    }\n    numread = php_stream_read(cb_arg->stream, buffer, nitems * size);\n    if (numread < 0) {\n        php_stream_close(cb_arg->stream);\n        cb_arg->stream = NULL;\n        return CURL_READFUNC_ABORT;\n    }\n    return numread;\n}\n/* }}} */\n\nstatic int seek_cb(void *arg, curl_off_t offset, int origin) /* {{{ */\n{\n    struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg;\n    int res;\n\n    if (cb_arg->stream == NULL) {\n        return CURL_SEEKFUNC_CANTSEEK;\n    }\n    res = php_stream_seek(cb_arg->stream, offset, origin);\n    return res == SUCCESS ? CURL_SEEKFUNC_OK : CURL_SEEKFUNC_CANTSEEK;\n}\n/* }}} */\n\nstatic void free_cb(void *arg) /* {{{ */\n{\n    struct mime_data_cb_arg *cb_arg = (struct mime_data_cb_arg *) arg;\n\n    if (cb_arg->stream != NULL) {\n        php_stream_close(cb_arg->stream);\n        cb_arg->stream = NULL;\n    }\n}\n/* }}} */\n\nstatic inline CURLcode add_simple_field(curl_mime *mime, zend_string *string_key, zval *current) {\n    CURLcode error = CURLE_OK;\n    curl_mimepart *part;\n    CURLcode form_error;\n    zend_string *postval, *tmp_postval;\n\n    postval = zval_get_tmp_string(current, &tmp_postval);\n\n    part = curl_mime_addpart(mime);\n    if (part == NULL) {\n        zend_tmp_string_release(tmp_postval);\n        return CURLE_OUT_OF_MEMORY;\n    }\n    if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK ||\n        (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK) {\n        error = form_error;\n    }\n\n    zend_tmp_string_release(tmp_postval);\n\n    return error;\n}\n\nstatic inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpostfields) /* {{{ */\n{\n    HashTable *postfields = Z_ARRVAL_P(zpostfields);\n    CURLcode error = CURLE_OK;\n    zval *current;\n    zend_string *string_key;\n    zend_ulong num_key;\n    curl_mime *mime = NULL;\n    curl_mimepart *part;\n    CURLcode form_error;\n\n    if (zend_hash_num_elements(postfields) > 0) {\n        mime = curl_mime_init(ch->cp);\n        if (mime == NULL) {\n            return FAILURE;\n        }\n    }\n\n    ZEND_HASH_FOREACH_KEY_VAL(postfields, num_key, string_key, current) {\n        zend_string *postval;\n        /* Pretend we have a string_key here */\n        if (!string_key) {\n            string_key = zend_long_to_str(num_key);\n        } else {\n            zend_string_addref(string_key);\n        }\n\n        ZVAL_DEREF(current);\n        if (Z_TYPE_P(current) == IS_OBJECT && instanceof_function(Z_OBJCE_P(current), curl_CURLFile_class)) {\n            /* new-style file upload */\n            zval *prop, rv;\n            char *type = NULL, *filename = NULL;\n            struct mime_data_cb_arg *cb_arg;\n            php_stream_statbuf ssb;\n            size_t filesize = -1;\n            curl_seek_callback seekfunc = seek_cb;\n\n            prop = zend_read_property_ex(\n                curl_CURLFile_class, Z_OBJ_P(current), ZSTR_KNOWN(ZEND_STR_NAME), /* silent */ false, &rv);\n            ZVAL_DEREF(prop);\n            if (Z_TYPE_P(prop) != IS_STRING) {\n                php_error_docref(NULL, E_WARNING, \"Invalid filename for key %s\", ZSTR_VAL(string_key));\n            } else {\n                postval = Z_STR_P(prop);\n\n                if (php_check_open_basedir(ZSTR_VAL(postval))) {\n                    goto out_string;\n                }\n\n                prop = zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), \"mime\", sizeof(\"mime\") - 1, 0, &rv);\n                ZVAL_DEREF(prop);\n                if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) {\n                    type = Z_STRVAL_P(prop);\n                }\n                prop = zend_read_property(\n                    curl_CURLFile_class, Z_OBJ_P(current), \"postname\", sizeof(\"postname\") - 1, 0, &rv);\n                ZVAL_DEREF(prop);\n                if (Z_TYPE_P(prop) == IS_STRING && Z_STRLEN_P(prop) > 0) {\n                    filename = Z_STRVAL_P(prop);\n                }\n\n                zval_ptr_dtor(&ch->postfields);\n                ZVAL_COPY(&ch->postfields, zpostfields);\n\n                php_stream *stream;\n                if ((stream = php_stream_open_wrapper(ZSTR_VAL(postval), \"rb\", STREAM_MUST_SEEK, NULL))) {\n                    if (!stream->readfilters.head && !php_stream_stat(stream, &ssb)) {\n                        filesize = ssb.sb.st_size;\n                    }\n                } else {\n                    seekfunc = NULL;\n                }\n\n                part = curl_mime_addpart(mime);\n                if (part == NULL) {\n                    if (stream) {\n                        php_stream_close(stream);\n                    }\n                    goto out_string;\n                }\n\n                cb_arg = (struct mime_data_cb_arg *) emalloc(sizeof *cb_arg);\n                cb_arg->filename = zend_string_copy(postval);\n                cb_arg->stream = stream;\n\n                if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK ||\n                    (form_error = curl_mime_data_cb(part, filesize, read_cb, seekfunc, free_cb, cb_arg)) != CURLE_OK ||\n                    (form_error = curl_mime_filename(part, filename ? filename : ZSTR_VAL(postval))) != CURLE_OK ||\n                    (form_error = curl_mime_type(part, type ? type : \"application/octet-stream\")) != CURLE_OK) {\n                    error = form_error;\n                }\n                zend_llist_add_element(&ch->to_free->stream, &cb_arg);\n            }\n\n            zend_string_release_ex(string_key, 0);\n            continue;\n        }\n\n        if (Z_TYPE_P(current) == IS_OBJECT && instanceof_function(Z_OBJCE_P(current), curl_CURLStringFile_class)) {\n            /* new-style file upload from string */\n            zval *prop, rv;\n            char *type = NULL, *filename = NULL;\n\n            prop = zend_read_property(\n                curl_CURLStringFile_class, Z_OBJ_P(current), \"postname\", sizeof(\"postname\") - 1, 0, &rv);\n            if (EG(exception)) {\n                goto out_string;\n            }\n            ZVAL_DEREF(prop);\n            ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);\n\n            filename = Z_STRVAL_P(prop);\n\n            prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), \"mime\", sizeof(\"mime\") - 1, 0, &rv);\n            if (EG(exception)) {\n                goto out_string;\n            }\n            ZVAL_DEREF(prop);\n            ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);\n\n            type = Z_STRVAL_P(prop);\n\n            prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), \"data\", sizeof(\"data\") - 1, 0, &rv);\n            if (EG(exception)) {\n                goto out_string;\n            }\n            ZVAL_DEREF(prop);\n            ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING);\n\n            postval = Z_STR_P(prop);\n\n            zval_ptr_dtor(&ch->postfields);\n            ZVAL_COPY(&ch->postfields, zpostfields);\n\n            part = curl_mime_addpart(mime);\n            if (part == NULL) {\n                goto out_string;\n            }\n            if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK ||\n                (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK ||\n                (form_error = curl_mime_filename(part, filename)) != CURLE_OK ||\n                (form_error = curl_mime_type(part, type)) != CURLE_OK) {\n                error = form_error;\n            }\n\n            zend_string_release_ex(string_key, 0);\n            continue;\n        }\n\n        if (Z_TYPE_P(current) == IS_ARRAY) {\n            zval *current_element;\n\n            ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(current), current_element) {\n                add_simple_field(mime, string_key, current_element);\n            }\n            ZEND_HASH_FOREACH_END();\n\n            zend_string_release_ex(string_key, 0);\n            continue;\n        }\n\n        add_simple_field(mime, string_key, current);\n\n        zend_string_release_ex(string_key, 0);\n    }\n    ZEND_HASH_FOREACH_END();\n\n    SAVE_CURL_ERROR(ch, error);\n    if (error != CURLE_OK) {\n        goto out_mime;\n    }\n\n    if ((*ch->clone) == 1) {\n        zend_llist_clean(&ch->to_free->post);\n    }\n    zend_llist_add_element(&ch->to_free->post, &mime);\n    error = curl_easy_setopt(ch->cp, CURLOPT_MIMEPOST, mime);\n\n    SAVE_CURL_ERROR(ch, error);\n    return error == CURLE_OK ? SUCCESS : FAILURE;\n\nout_string:\n    zend_string_release_ex(string_key, false);\nout_mime:\n    curl_mime_free(mime);\n    return FAILURE;\n}\n/* }}} */\n\n/* {{{ Copy a cURL handle along with all of it's preferences */\nPHP_FUNCTION(swoole_native_curl_copy_handle) {\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    zend_object *new_object = swoole_curl_clone_obj(Z_OBJ_P(zid));\n#if PHP_VERSION_ID >= 80500\n    if (EG(exception)) {\n        if (new_object != NULL) {\n            OBJ_RELEASE(new_object);\n        }\n        zend_clear_exception();\n        php_error_docref(NULL, E_WARNING, \"Cannot duplicate cURL handle\");\n        RETURN_FALSE;\n    }\n#endif\n    RETURN_OBJ(new_object);\n}\n/* }}} */\n\nstatic bool php_curl_set_callable_handler(zend_fcall_info_cache *const handler_fcc,\n                                          zval *callable,\n                                          bool is_array_config,\n                                          const char *option_name) {\n    if (ZEND_FCC_INITIALIZED(*handler_fcc)) {\n        zend_fcc_dtor(handler_fcc);\n    }\n\n    if (Z_TYPE_P(callable) == IS_NULL) {\n        return true;\n    }\n\n    char *error = NULL;\n    if (UNEXPECTED(!zend_is_callable_ex(callable,\n                                        /* object */ NULL,\n                                        /* check_flags */ 0,\n                                        /* callable_name */ NULL,\n                                        handler_fcc,\n                                        /* error */ &error))) {\n        if (!EG(exception)) {\n            zend_argument_type_error(\n                2 + !is_array_config, \"must be a valid callback for option %s, %s\", option_name, error);\n        }\n        efree(error);\n        return false;\n    }\n    zend_fcc_addref(handler_fcc);\n    return true;\n}\n\n#define HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(curl_ptr, constant_no_function, handler_type, default_method)        \\\n    case constant_no_function##FUNCTION: {                                                                             \\\n        bool result = php_curl_set_callable_handler(                                                                   \\\n            &curl_ptr->handlers.handler_type->fcc, zvalue, is_array_config, #constant_no_function \"FUNCTION\");         \\\n        if (!result) {                                                                                                 \\\n            curl_ptr->handlers.handler_type->method = default_method;                                                  \\\n            return FAILURE;                                                                                            \\\n        }                                                                                                              \\\n        if (!ZEND_FCC_INITIALIZED(curl_ptr->handlers.handler_type->fcc)) {                                             \\\n            curl_ptr->handlers.handler_type->method = default_method;                                                  \\\n            return SUCCESS;                                                                                            \\\n        }                                                                                                              \\\n        curl_ptr->handlers.handler_type->method = PHP_CURL_USER;                                                       \\\n        break;                                                                                                         \\\n    }\n\n#define HANDLE_CURL_OPTION_CALLABLE(curl_ptr, constant_no_function, handler_fcc, c_callback)                           \\\n    case constant_no_function##FUNCTION: {                                                                             \\\n        bool result = php_curl_set_callable_handler(                                                                   \\\n            &curl_ptr->handler_fcc, zvalue, is_array_config, #constant_no_function \"FUNCTION\");                        \\\n        if (!result) {                                                                                                 \\\n            return FAILURE;                                                                                            \\\n        }                                                                                                              \\\n        curl_easy_setopt(curl_ptr->cp, constant_no_function##FUNCTION, (c_callback));                                  \\\n        curl_easy_setopt(curl_ptr->cp, constant_no_function##DATA, curl_ptr);                                          \\\n        break;                                                                                                         \\\n    }\n\nstatic zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool is_array_config) /* {{{ */\n{\n    CURLcode error = CURLE_OK;\n    zend_long lval;\n\n    switch (option) {\n        /* Callable options */\n        HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_WRITE, write, PHP_CURL_STDOUT);\n        HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_HEADER, write_header, PHP_CURL_IGNORE);\n        HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER(ch, CURLOPT_READ, read, PHP_CURL_DIRECT);\n\n        HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PROGRESS, handlers.progress, fn_progress);\n        HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_XFERINFO, handlers.xferinfo, fn_xferinfo);\n        HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_FNMATCH_, handlers.fnmatch, fn_fnmatch);\n        HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_DEBUG, handlers.debug, fn_debug);\n#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */\n        HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PREREQ, handlers.prereq, fn_prereqfunction);\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */\n        HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_SSH_HOSTKEY, handlers.sshhostkey, fn_ssh_hostkeyfunction);\n#endif\n\n    /* Long options */\n    case CURLOPT_SSL_VERIFYHOST:\n        lval = zval_get_long(zvalue);\n        if (lval == 1) {\n            php_error_docref(\n                NULL, E_NOTICE, \"CURLOPT_SSL_VERIFYHOST no longer accepts the value 1, value 2 will be used instead\");\n            error = curl_easy_setopt(ch->cp, (CURLoption) option, 2L);\n            break;\n        }\n        ZEND_FALLTHROUGH;\n    case CURLOPT_AUTOREFERER:\n    case CURLOPT_BUFFERSIZE:\n    case CURLOPT_CONNECTTIMEOUT:\n    case CURLOPT_COOKIESESSION:\n    case CURLOPT_CRLF:\n    case CURLOPT_DNS_CACHE_TIMEOUT:\n    case CURLOPT_FAILONERROR:\n    case CURLOPT_FILETIME:\n    case CURLOPT_FORBID_REUSE:\n    case CURLOPT_FRESH_CONNECT:\n    case CURLOPT_FTP_USE_EPRT:\n    case CURLOPT_FTP_USE_EPSV:\n    case CURLOPT_HEADER:\n    case CURLOPT_HTTPGET:\n    case CURLOPT_HTTPPROXYTUNNEL:\n    case CURLOPT_HTTP_VERSION:\n    case CURLOPT_INFILESIZE:\n    case CURLOPT_LOW_SPEED_LIMIT:\n    case CURLOPT_LOW_SPEED_TIME:\n    case CURLOPT_MAXCONNECTS:\n    case CURLOPT_MAXREDIRS:\n    case CURLOPT_NETRC:\n    case CURLOPT_NOBODY:\n    case CURLOPT_NOPROGRESS:\n    case CURLOPT_NOSIGNAL:\n    case CURLOPT_PORT:\n    case CURLOPT_POST:\n    case CURLOPT_PROXYPORT:\n    case CURLOPT_PROXYTYPE:\n    case CURLOPT_PUT:\n    case CURLOPT_RESUME_FROM:\n    case CURLOPT_SSLVERSION:\n    case CURLOPT_SSL_VERIFYPEER:\n    case CURLOPT_TIMECONDITION:\n    case CURLOPT_TIMEOUT:\n    case CURLOPT_TIMEVALUE:\n    case CURLOPT_TRANSFERTEXT:\n    case CURLOPT_UNRESTRICTED_AUTH:\n    case CURLOPT_UPLOAD:\n    case CURLOPT_VERBOSE:\n    case CURLOPT_HTTPAUTH:\n    case CURLOPT_FTP_CREATE_MISSING_DIRS:\n    case CURLOPT_PROXYAUTH:\n    case CURLOPT_SERVER_RESPONSE_TIMEOUT:\n    case CURLOPT_IPRESOLVE:\n    case CURLOPT_MAXFILESIZE:\n    case CURLOPT_TCP_NODELAY:\n    case CURLOPT_FTPSSLAUTH:\n    case CURLOPT_IGNORE_CONTENT_LENGTH:\n    case CURLOPT_FTP_SKIP_PASV_IP:\n    case CURLOPT_FTP_FILEMETHOD:\n    case CURLOPT_CONNECT_ONLY:\n    case CURLOPT_LOCALPORT:\n    case CURLOPT_LOCALPORTRANGE:\n    case CURLOPT_SSL_SESSIONID_CACHE:\n    case CURLOPT_FTP_SSL_CCC:\n    case CURLOPT_SSH_AUTH_TYPES:\n    case CURLOPT_CONNECTTIMEOUT_MS:\n    case CURLOPT_HTTP_CONTENT_DECODING:\n    case CURLOPT_HTTP_TRANSFER_DECODING:\n    case CURLOPT_TIMEOUT_MS:\n    case CURLOPT_NEW_DIRECTORY_PERMS:\n    case CURLOPT_NEW_FILE_PERMS:\n    case CURLOPT_USE_SSL:\n    case CURLOPT_APPEND:\n    case CURLOPT_DIRLISTONLY:\n    case CURLOPT_PROXY_TRANSFER_MODE:\n    case CURLOPT_ADDRESS_SCOPE:\n    case CURLOPT_CERTINFO:\n    case CURLOPT_PROTOCOLS:\n    case CURLOPT_REDIR_PROTOCOLS:\n    case CURLOPT_SOCKS5_GSSAPI_NEC:\n    case CURLOPT_TFTP_BLKSIZE:\n    case CURLOPT_FTP_USE_PRET:\n    case CURLOPT_RTSP_CLIENT_CSEQ:\n    case CURLOPT_RTSP_REQUEST:\n    case CURLOPT_RTSP_SERVER_CSEQ:\n    case CURLOPT_WILDCARDMATCH:\n    case CURLOPT_GSSAPI_DELEGATION:\n    case CURLOPT_ACCEPTTIMEOUT_MS:\n    case CURLOPT_SSL_OPTIONS:\n    case CURLOPT_TCP_KEEPALIVE:\n    case CURLOPT_TCP_KEEPIDLE:\n    case CURLOPT_TCP_KEEPINTVL:\n    case CURLOPT_SASL_IR:\n    case CURLOPT_EXPECT_100_TIMEOUT_MS:\n    case CURLOPT_SSL_ENABLE_ALPN:\n    case CURLOPT_SSL_ENABLE_NPN:\n    case CURLOPT_HEADEROPT:\n    case CURLOPT_SSL_VERIFYSTATUS:\n    case CURLOPT_PATH_AS_IS:\n    case CURLOPT_SSL_FALSESTART:\n    case CURLOPT_PIPEWAIT:\n    case CURLOPT_STREAM_WEIGHT:\n    case CURLOPT_TFTP_NO_OPTIONS:\n    case CURLOPT_TCP_FASTOPEN:\n    case CURLOPT_KEEP_SENDING_ON_ERROR:\n    case CURLOPT_PROXY_SSL_OPTIONS:\n    case CURLOPT_PROXY_SSL_VERIFYHOST:\n    case CURLOPT_PROXY_SSL_VERIFYPEER:\n    case CURLOPT_PROXY_SSLVERSION:\n    case CURLOPT_SUPPRESS_CONNECT_HEADERS:\n    case CURLOPT_SOCKS5_AUTH:\n    case CURLOPT_SSH_COMPRESSION:\n    case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS:\n    case CURLOPT_DNS_SHUFFLE_ADDRESSES:\n    case CURLOPT_HAPROXYPROTOCOL:\n    case CURLOPT_DISALLOW_USERNAME_IN_URL:\n#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */\n    case CURLOPT_UPKEEP_INTERVAL_MS:\n    case CURLOPT_UPLOAD_BUFFERSIZE:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074000 /* Available since 7.64.0 */\n    case CURLOPT_HTTP09_ALLOWED:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */\n    case CURLOPT_ALTSVC_CTRL:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074100 /* Available since 7.65.0 */\n    case CURLOPT_MAXAGE_CONN:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074500 /* Available since 7.69.0 */\n    case CURLOPT_MAIL_RCPT_ALLLOWFAILS:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */\n    case CURLOPT_HSTS_CTRL:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074c00 /* Available since 7.76.0 */\n    case CURLOPT_DOH_SSL_VERIFYHOST:\n    case CURLOPT_DOH_SSL_VERIFYPEER:\n    case CURLOPT_DOH_SSL_VERIFYSTATUS:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */\n    case CURLOPT_MAXLIFETIME_CONN:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075100 /* Available since 7.81.0 */\n    case CURLOPT_MIME_OPTIONS:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075600 /* Available since 7.86.0 */\n    case CURLOPT_WS_OPTIONS:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075700 /* Available since 7.87.0 */\n    case CURLOPT_CA_CACHE_TIMEOUT:\n    case CURLOPT_QUICK_EXIT:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x080900 /* Available since 8.9.0 */\n    case CURLOPT_TCP_KEEPCNT:\n#endif\n#if PHP_VERSION_ID >= 80500\n    case CURLOPT_FOLLOWLOCATION:\n#endif\n        lval = zval_get_long(zvalue);\n        if ((option == CURLOPT_PROTOCOLS || option == CURLOPT_REDIR_PROTOCOLS) &&\n            (PG(open_basedir) && *PG(open_basedir)) && (lval & CURLPROTO_FILE)) {\n            php_error_docref(NULL, E_WARNING, \"CURLPROTO_FILE cannot be activated when an open_basedir is set\");\n            return FAILURE;\n        }\n        error = curl_easy_setopt(ch->cp, (CURLoption) option, lval);\n        break;\n    case CURLOPT_SAFE_UPLOAD:\n        if (!zend_is_true(zvalue)) {\n            zend_value_error(\"%s(): Disabling safe uploads is no longer supported\", get_active_function_name());\n            return FAILURE;\n        }\n        break;\n\n    /* String options */\n    case CURLOPT_CAINFO:\n    case CURLOPT_CAPATH:\n    case CURLOPT_COOKIE:\n    case CURLOPT_EGDSOCKET:\n    case CURLOPT_INTERFACE:\n    case CURLOPT_PROXY:\n    case CURLOPT_PROXYUSERPWD:\n    case CURLOPT_REFERER:\n    case CURLOPT_SSLCERTTYPE:\n    case CURLOPT_SSLENGINE:\n    case CURLOPT_SSLENGINE_DEFAULT:\n    case CURLOPT_SSLKEY:\n    case CURLOPT_SSLKEYPASSWD:\n    case CURLOPT_SSLKEYTYPE:\n    case CURLOPT_SSL_CIPHER_LIST:\n    case CURLOPT_USERAGENT:\n    case CURLOPT_COOKIELIST:\n    case CURLOPT_FTP_ALTERNATIVE_TO_USER:\n    case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:\n    case CURLOPT_PROXYPASSWORD:\n    case CURLOPT_PROXYUSERNAME:\n    case CURLOPT_NOPROXY:\n    case CURLOPT_SOCKS5_GSSAPI_SERVICE:\n    case CURLOPT_MAIL_FROM:\n    case CURLOPT_RTSP_STREAM_URI:\n    case CURLOPT_RTSP_TRANSPORT:\n    case CURLOPT_TLSAUTH_TYPE:\n    case CURLOPT_TLSAUTH_PASSWORD:\n    case CURLOPT_TLSAUTH_USERNAME:\n    case CURLOPT_TRANSFER_ENCODING:\n    case CURLOPT_DNS_SERVERS:\n    case CURLOPT_MAIL_AUTH:\n    case CURLOPT_LOGIN_OPTIONS:\n    case CURLOPT_PINNEDPUBLICKEY:\n    case CURLOPT_PROXY_SERVICE_NAME:\n    case CURLOPT_SERVICE_NAME:\n    case CURLOPT_DEFAULT_PROTOCOL:\n    case CURLOPT_PRE_PROXY:\n    case CURLOPT_PROXY_CAINFO:\n    case CURLOPT_PROXY_CAPATH:\n    case CURLOPT_PROXY_CRLFILE:\n    case CURLOPT_PROXY_KEYPASSWD:\n    case CURLOPT_PROXY_PINNEDPUBLICKEY:\n    case CURLOPT_PROXY_SSL_CIPHER_LIST:\n    case CURLOPT_PROXY_SSLCERT:\n    case CURLOPT_PROXY_SSLCERTTYPE:\n    case CURLOPT_PROXY_SSLKEY:\n    case CURLOPT_PROXY_SSLKEYTYPE:\n    case CURLOPT_PROXY_TLSAUTH_PASSWORD:\n    case CURLOPT_PROXY_TLSAUTH_TYPE:\n    case CURLOPT_PROXY_TLSAUTH_USERNAME:\n    case CURLOPT_ABSTRACT_UNIX_SOCKET:\n    case CURLOPT_REQUEST_TARGET:\n    case CURLOPT_PROXY_TLS13_CIPHERS:\n    case CURLOPT_TLS13_CIPHERS:\n#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */\n    case CURLOPT_ALTSVC:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074200 /* Available since 7.66.0 */\n    case CURLOPT_SASL_AUTHZID:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */\n    case CURLOPT_PROXY_ISSUERCERT:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */\n    case CURLOPT_SSL_EC_CURVES:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074b00 /* Available since 7.75.0 */\n    case CURLOPT_AWS_SIGV4:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */\n    case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075500 /* Available since 7.85.0 */\n    case CURLOPT_PROTOCOLS_STR:\n    case CURLOPT_REDIR_PROTOCOLS_STR:\n#endif\n    {\n        zend_string *tmp_str;\n        zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);\n#if LIBCURL_VERSION_NUM >= 0x075500 /* Available since 7.85.0 */\n        if ((option == CURLOPT_PROTOCOLS_STR || option == CURLOPT_REDIR_PROTOCOLS_STR) &&\n            (PG(open_basedir) && *PG(open_basedir)) &&\n            (php_memnistr(ZSTR_VAL(str), \"file\", sizeof(\"file\") - 1, ZSTR_VAL(str) + ZSTR_LEN(str)) != NULL ||\n             php_memnistr(ZSTR_VAL(str), \"all\", sizeof(\"all\") - 1, ZSTR_VAL(str) + ZSTR_LEN(str)) != NULL)) {\n            zend_tmp_string_release(tmp_str);\n            php_error_docref(NULL, E_WARNING, \"The FILE protocol cannot be activated when an open_basedir is set\");\n            return FAILURE;\n        }\n#endif\n        zend_result ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str));\n        zend_tmp_string_release(tmp_str);\n        return ret;\n    }\n\n    /* Curl nullable string options */\n    case CURLOPT_CUSTOMREQUEST:\n    case CURLOPT_FTPPORT:\n    case CURLOPT_RANGE:\n    case CURLOPT_FTP_ACCOUNT:\n    case CURLOPT_RTSP_SESSION_ID:\n    case CURLOPT_ACCEPT_ENCODING:\n    case CURLOPT_DNS_INTERFACE:\n    case CURLOPT_DNS_LOCAL_IP4:\n    case CURLOPT_DNS_LOCAL_IP6:\n    case CURLOPT_XOAUTH2_BEARER:\n    case CURLOPT_UNIX_SOCKET_PATH:\n#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */\n    case CURLOPT_DOH_URL:\n#endif\n#if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */\n    case CURLOPT_HSTS:\n#endif\n    case CURLOPT_KRBLEVEL:\n    // Authorization header would be implictly set\n    // with an empty string thus we explictly set the option\n    // to null to avoid this unwarranted side effect\n    case CURLOPT_USERPWD:\n    case CURLOPT_USERNAME:\n    case CURLOPT_PASSWORD:\n#if LIBCURL_VERSION_NUM >= 0x080e00 && PHP_VERSION_ID >= 80500 /* Available since 8.14.0 */\n    case CURLOPT_SSL_SIGNATURE_ALGORITHMS:\n#endif\n    {\n        if (Z_ISNULL_P(zvalue)) {\n            error = curl_easy_setopt(ch->cp, (CURLoption) option, NULL);\n        } else {\n            zend_string *tmp_str;\n            zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);\n            zend_result ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str));\n            zend_tmp_string_release(tmp_str);\n            return ret;\n        }\n        break;\n    }\n\n    /* Curl private option */\n    case CURLOPT_PRIVATE: {\n        zval_ptr_dtor(&ch->private_data);\n        ZVAL_COPY(&ch->private_data, zvalue);\n        return SUCCESS;\n    }\n\n    /* Curl url option */\n    case CURLOPT_URL: {\n        zend_string *tmp_str;\n        zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);\n        zend_result ret = php_curl_option_url(ch, str);\n        zend_tmp_string_release(tmp_str);\n        return ret;\n    }\n\n    /* Curl file handle options */\n    case CURLOPT_FILE:\n    case CURLOPT_INFILE:\n    case CURLOPT_STDERR:\n    case CURLOPT_WRITEHEADER: {\n        FILE *fp = NULL;\n        php_stream *what = NULL;\n\n        if (Z_TYPE_P(zvalue) != IS_NULL) {\n            what = (php_stream *) zend_fetch_resource2_ex(\n                zvalue, \"File-Handle\", php_file_le_stream(), php_file_le_pstream());\n            if (!what) {\n                return FAILURE;\n            }\n\n            if (FAILURE == php_stream_cast(what, PHP_STREAM_AS_STDIO, (void **) &fp, REPORT_ERRORS)) {\n                return FAILURE;\n            }\n\n            if (!fp) {\n                return FAILURE;\n            }\n        }\n\n        error = CURLE_OK;\n        switch (option) {\n        case CURLOPT_FILE:\n            if (!what) {\n                if (!Z_ISUNDEF(ch->handlers.write->stream)) {\n                    zval_ptr_dtor(&ch->handlers.write->stream);\n                    ZVAL_UNDEF(&ch->handlers.write->stream);\n                }\n                ch->handlers.write->fp = NULL;\n                ch->handlers.write->method = PHP_CURL_STDOUT;\n            } else if (what->mode[0] != 'r' || what->mode[1] == '+') {\n                zval_ptr_dtor(&ch->handlers.write->stream);\n                ch->handlers.write->fp = fp;\n                ch->handlers.write->method = PHP_CURL_FILE;\n                ZVAL_COPY(&ch->handlers.write->stream, zvalue);\n            } else {\n                zend_value_error(\"%s(): The provided file handle must be writable\", get_active_function_name());\n                return FAILURE;\n            }\n            break;\n        case CURLOPT_WRITEHEADER:\n            if (!what) {\n                if (!Z_ISUNDEF(ch->handlers.write_header->stream)) {\n                    zval_ptr_dtor(&ch->handlers.write_header->stream);\n                    ZVAL_UNDEF(&ch->handlers.write_header->stream);\n                }\n                ch->handlers.write_header->fp = NULL;\n                ch->handlers.write_header->method = PHP_CURL_IGNORE;\n            } else if (what->mode[0] != 'r' || what->mode[1] == '+') {\n                zval_ptr_dtor(&ch->handlers.write_header->stream);\n                ch->handlers.write_header->fp = fp;\n                ch->handlers.write_header->method = PHP_CURL_FILE;\n                ZVAL_COPY(&ch->handlers.write_header->stream, zvalue);\n            } else {\n                zend_value_error(\"%s(): The provided file handle must be writable\", get_active_function_name());\n                return FAILURE;\n            }\n            break;\n        case CURLOPT_INFILE:\n            if (!what) {\n                if (!Z_ISUNDEF(ch->handlers.read->stream)) {\n                    zval_ptr_dtor(&ch->handlers.read->stream);\n                    ZVAL_UNDEF(&ch->handlers.read->stream);\n                }\n                ch->handlers.read->fp = NULL;\n                ch->handlers.read->res = NULL;\n            } else {\n                zval_ptr_dtor(&ch->handlers.read->stream);\n                ch->handlers.read->fp = fp;\n                ch->handlers.read->res = Z_RES_P(zvalue);\n                ZVAL_COPY(&ch->handlers.read->stream, zvalue);\n            }\n            break;\n        case CURLOPT_STDERR:\n            if (!what) {\n                if (!Z_ISUNDEF(ch->handlers.std_err)) {\n                    zval_ptr_dtor(&ch->handlers.std_err);\n                    ZVAL_UNDEF(&ch->handlers.std_err);\n                }\n            } else if (what->mode[0] != 'r' || what->mode[1] == '+') {\n                zval_ptr_dtor(&ch->handlers.std_err);\n                ZVAL_COPY(&ch->handlers.std_err, zvalue);\n            } else {\n                zend_value_error(\"%s(): The provided file handle must be writable\", get_active_function_name());\n                return FAILURE;\n            }\n            ZEND_FALLTHROUGH;\n        default:\n            error = curl_easy_setopt(ch->cp, (CURLoption) option, fp);\n            break;\n        }\n        break;\n    }\n\n    /* Curl linked list options */\n    case CURLOPT_HTTP200ALIASES:\n    case CURLOPT_HTTPHEADER:\n    case CURLOPT_POSTQUOTE:\n    case CURLOPT_PREQUOTE:\n    case CURLOPT_QUOTE:\n    case CURLOPT_TELNETOPTIONS:\n    case CURLOPT_MAIL_RCPT:\n    case CURLOPT_RESOLVE:\n    case CURLOPT_PROXYHEADER:\n    case CURLOPT_CONNECT_TO: {\n        zval *current;\n        HashTable *ph;\n        zend_string *val, *tmp_val;\n        struct curl_slist *slist = NULL;\n\n        if (Z_TYPE_P(zvalue) != IS_ARRAY) {\n            const char *name = NULL;\n            switch (option) {\n            case CURLOPT_HTTPHEADER:\n                name = \"CURLOPT_HTTPHEADER\";\n                break;\n            case CURLOPT_QUOTE:\n                name = \"CURLOPT_QUOTE\";\n                break;\n            case CURLOPT_HTTP200ALIASES:\n                name = \"CURLOPT_HTTP200ALIASES\";\n                break;\n            case CURLOPT_POSTQUOTE:\n                name = \"CURLOPT_POSTQUOTE\";\n                break;\n            case CURLOPT_PREQUOTE:\n                name = \"CURLOPT_PREQUOTE\";\n                break;\n            case CURLOPT_TELNETOPTIONS:\n                name = \"CURLOPT_TELNETOPTIONS\";\n                break;\n            case CURLOPT_MAIL_RCPT:\n                name = \"CURLOPT_MAIL_RCPT\";\n                break;\n            case CURLOPT_RESOLVE:\n                name = \"CURLOPT_RESOLVE\";\n                break;\n            case CURLOPT_PROXYHEADER:\n                name = \"CURLOPT_PROXYHEADER\";\n                break;\n            case CURLOPT_CONNECT_TO:\n                name = \"CURLOPT_CONNECT_TO\";\n                break;\n            }\n\n            zend_type_error(\"%s(): The %s option must have an array value\", get_active_function_name(), name);\n            return FAILURE;\n        }\n\n        ph = Z_ARRVAL_P(zvalue);\n        ZEND_HASH_FOREACH_VAL(ph, current) {\n            ZVAL_DEREF(current);\n            val = zval_get_tmp_string(current, &tmp_val);\n            struct curl_slist *new_slist = curl_slist_append(slist, ZSTR_VAL(val));\n            zend_tmp_string_release(tmp_val);\n            if (!new_slist) {\n                curl_slist_free_all(slist);\n                php_error_docref(NULL, E_WARNING, \"Could not build curl_slist\");\n                return FAILURE;\n            }\n            slist = new_slist;\n        }\n        ZEND_HASH_FOREACH_END();\n\n        if (slist) {\n#if PHP_VERSION_ID >= 80500\n            if ((*ch->clone) == 1) {\n                zend_hash_index_update_ptr(&ch->to_free->slist, option, slist);\n            } else {\n                zend_hash_next_index_insert_ptr(&ch->to_free->slist, slist);\n            }\n#else\n            if ((*ch->clone) == 1) {\n                zend_hash_index_update_ptr(ch->to_free->slist, option, slist);\n            } else {\n                zend_hash_next_index_insert_ptr(ch->to_free->slist, slist);\n            }\n#endif\n        }\n\n        error = curl_easy_setopt(ch->cp, (CURLoption) option, slist);\n\n        break;\n    }\n\n    case CURLOPT_BINARYTRANSFER:\n    case CURLOPT_DNS_USE_GLOBAL_CACHE:\n        /* Do nothing, just backward compatibility */\n        break;\n\n#if PHP_VERSION_ID < 80500\n    case CURLOPT_FOLLOWLOCATION:\n        lval = zend_is_true(zvalue);\n        error = curl_easy_setopt(ch->cp, (CURLoption) option, (long) lval);\n        break;\n#endif\n\n    case CURLOPT_POSTFIELDS:\n        if (Z_TYPE_P(zvalue) == IS_ARRAY) {\n            if (zend_hash_num_elements(Z_ARRVAL_P(zvalue)) == 0) {\n                /* no need to build the mime structure for empty hashtables;\n                   also works around https://github.com/curl/curl/issues/6455 */\n                curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDS, \"\");\n                error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, 0L);\n            } else {\n                return build_mime_structure_from_hash(ch, zvalue);\n            }\n        } else {\n            zend_string *tmp_str;\n            zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);\n            /* with curl 7.17.0 and later, we can use COPYPOSTFIELDS, but we have to provide size before */\n            error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, ZSTR_LEN(str));\n            error = curl_easy_setopt(ch->cp, CURLOPT_COPYPOSTFIELDS, ZSTR_VAL(str));\n            zend_tmp_string_release(tmp_str);\n        }\n        break;\n\n    case CURLOPT_RETURNTRANSFER:\n        if (zend_is_true(zvalue)) {\n            ch->handlers.write->method = PHP_CURL_RETURN;\n        } else {\n            ch->handlers.write->method = PHP_CURL_STDOUT;\n        }\n        break;\n\n    /* Curl off_t options */\n#if PHP_VERSION_ID >= 80500\n    case CURLOPT_INFILESIZE_LARGE:\n#endif\n    case CURLOPT_MAX_RECV_SPEED_LARGE:\n    case CURLOPT_MAX_SEND_SPEED_LARGE:\n    case CURLOPT_MAXFILESIZE_LARGE:\n    case CURLOPT_TIMEVALUE_LARGE:\n        lval = zval_get_long(zvalue);\n        error = curl_easy_setopt(ch->cp, (CURLoption) option, (curl_off_t) lval);\n        break;\n\n    case CURLOPT_POSTREDIR:\n        lval = zval_get_long(zvalue);\n        error = curl_easy_setopt(ch->cp, CURLOPT_POSTREDIR, (long) (lval & CURL_REDIR_POST_ALL));\n        break;\n\n    /* the following options deal with files, therefore the open_basedir check\n     * is required.\n     */\n    case CURLOPT_COOKIEFILE:\n    case CURLOPT_COOKIEJAR:\n    case CURLOPT_RANDOM_FILE:\n    case CURLOPT_SSLCERT:\n    case CURLOPT_NETRC_FILE:\n    case CURLOPT_SSH_PRIVATE_KEYFILE:\n    case CURLOPT_SSH_PUBLIC_KEYFILE:\n    case CURLOPT_CRLFILE:\n    case CURLOPT_ISSUERCERT:\n    case CURLOPT_SSH_KNOWNHOSTS: {\n        zend_string *tmp_str;\n        zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);\n        zend_result ret;\n\n        if (ZSTR_LEN(str) && php_check_open_basedir(ZSTR_VAL(str))) {\n            zend_tmp_string_release(tmp_str);\n            return FAILURE;\n        }\n\n        ret = php_curl_option_str(ch, option, ZSTR_VAL(str), ZSTR_LEN(str));\n        zend_tmp_string_release(tmp_str);\n        return ret;\n    }\n\n    case CURLINFO_HEADER_OUT:\n        if (ZEND_FCC_INITIALIZED(ch->handlers.debug)) {\n            zend_value_error(\"CURLINFO_HEADER_OUT option must not be set when the CURLOPT_DEBUGFUNCTION option is set\");\n            return FAILURE;\n        }\n        if (zend_is_true(zvalue)) {\n            curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, fn_debug);\n            curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *) ch);\n            curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 1L);\n        } else {\n            curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, NULL);\n            curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, NULL);\n            curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0L);\n        }\n        break;\n\n    case CURLOPT_SHARE: {\n#if PHP_VERSION_ID >= 80500\n        if (Z_TYPE_P(zvalue) != IS_OBJECT) {\n            break;\n        }\n\n        if (Z_OBJCE_P(zvalue) != curl_share_ce && Z_OBJCE_P(zvalue) != curl_share_persistent_ce) {\n            break;\n        }\n#else\n        if (Z_TYPE_P(zvalue) == IS_OBJECT && Z_OBJCE_P(zvalue) == curl_share_ce) {\n#endif\n        php_curlsh *sh = Z_CURL_SHARE_P(zvalue);\n        curl_easy_setopt(ch->cp, CURLOPT_SHARE, sh->share);\n\n        if (ch->share) {\n            OBJ_RELEASE(&ch->share->std);\n        }\n        GC_ADDREF(&sh->std);\n        ch->share = sh;\n#if PHP_VERSION_ID < 80500\n    }\n#endif\n    } break;\n\n    /* Curl blob options */\n#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */\n    case CURLOPT_ISSUERCERT_BLOB:\n    case CURLOPT_PROXY_ISSUERCERT_BLOB:\n    case CURLOPT_PROXY_SSLCERT_BLOB:\n    case CURLOPT_PROXY_SSLKEY_BLOB:\n    case CURLOPT_SSLCERT_BLOB:\n    case CURLOPT_SSLKEY_BLOB:\n#if LIBCURL_VERSION_NUM >= 0x074d00 /* Available since 7.77.0 */\n    case CURLOPT_CAINFO_BLOB:\n    case CURLOPT_PROXY_CAINFO_BLOB:\n#endif\n    {\n        zend_string *tmp_str;\n        zend_string *str = zval_get_tmp_string(zvalue, &tmp_str);\n\n        struct curl_blob stblob;\n        stblob.data = ZSTR_VAL(str);\n        stblob.len = ZSTR_LEN(str);\n        stblob.flags = CURL_BLOB_COPY;\n        error = curl_easy_setopt(ch->cp, (CURLoption) option, &stblob);\n\n        zend_tmp_string_release(tmp_str);\n    } break;\n#endif\n\n    default:\n        if (is_array_config) {\n            zend_argument_value_error(2, \"must contain only valid cURL options\");\n        } else {\n            zend_argument_value_error(2, \"is not a valid cURL option\");\n        }\n        error = CURLE_UNKNOWN_OPTION;\n        break;\n    }\n\n    SAVE_CURL_ERROR(ch, error);\n    if (error != CURLE_OK) {\n        return FAILURE;\n    } else {\n        return SUCCESS;\n    }\n}\n/* }}} */\n\n/* {{{ Set an option for a cURL transfer */\nPHP_FUNCTION(swoole_native_curl_setopt) {\n    zval *zid, *zvalue;\n    zend_long options;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(3, 3)\n    Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)\n    Z_PARAM_LONG(options)\n    Z_PARAM_ZVAL(zvalue)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid, false)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    if (_php_curl_setopt(ch, options, zvalue, 0) == SUCCESS) {\n        RETURN_TRUE;\n    } else {\n        RETURN_FALSE;\n    }\n}\n/* }}} */\n\n/* {{{ Set an array of option for a cURL transfer */\nPHP_FUNCTION(swoole_native_curl_setopt_array) {\n    zval *zid, *arr, *entry;\n    php_curl *ch;\n    zend_ulong option;\n    zend_string *string_key;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    Z_PARAM_ARRAY(arr)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid, false)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(arr), option, string_key, entry) {\n        if (UNEXPECTED(string_key)) {\n            zend_argument_value_error(2, \"contains an invalid cURL option\");\n            RETURN_THROWS();\n        }\n\n        ZVAL_DEREF(entry);\n        if (_php_curl_setopt(ch, (zend_long) option, entry, 1) == FAILURE) {\n            RETURN_FALSE;\n        }\n    }\n    ZEND_HASH_FOREACH_END();\n\n    RETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ _php_curl_cleanup_handle(ch)\n   Cleanup an execution phase */\nvoid swoole_curl_cleanup_handle(php_curl *ch) {\n    smart_str_free(&ch->handlers.write->buf);\n    if (ch->header.str) {\n        zend_string_release_ex(ch->header.str, 0);\n        ch->header.str = NULL;\n    }\n\n    memset(ch->err.str, 0, CURL_ERROR_SIZE + 1);\n    ch->err.no = 0;\n}\n/* }}} */\n\n/* {{{ Perform a cURL session */\nPHP_FUNCTION(swoole_native_curl_exec) {\n    CURLcode error;\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    swoole_curl_verify_handlers(ch, 1);\n\n    swoole_curl_cleanup_handle(ch);\n\n    error = swoole_curl_easy_perform(ch->cp);\n\n    SAVE_CURL_ERROR(ch, error);\n\n    if (error != CURLE_OK) {\n        smart_str_free(&ch->handlers.write->buf);\n        RETURN_FALSE;\n    }\n\n    if (!Z_ISUNDEF(ch->handlers.std_err)) {\n        php_stream *stream;\n        stream = (php_stream *) zend_fetch_resource2_ex(\n            &ch->handlers.std_err, NULL, php_file_le_stream(), php_file_le_pstream());\n        if (stream) {\n            php_stream_flush(stream);\n        }\n    }\n\n    if (ch->handlers.write->method == PHP_CURL_RETURN && ch->handlers.write->buf.s) {\n        smart_str_0(&ch->handlers.write->buf);\n        RETURN_STR_COPY(ch->handlers.write->buf.s);\n    }\n\n    /* flush the file handle, so any remaining data is synched to disk */\n    if (ch->handlers.write->method == PHP_CURL_FILE && ch->handlers.write->fp) {\n        fflush(ch->handlers.write->fp);\n    }\n    if (ch->handlers.write_header->method == PHP_CURL_FILE && ch->handlers.write_header->fp) {\n        fflush(ch->handlers.write_header->fp);\n    }\n\n    if (ch->handlers.write->method == PHP_CURL_RETURN) {\n        RETURN_EMPTY_STRING();\n    } else {\n        RETURN_TRUE;\n    }\n}\n/* }}} */\n\n/* {{{ Get information regarding a specific transfer */\nPHP_FUNCTION(swoole_native_curl_getinfo) {\n    zval *zid;\n    php_curl *ch;\n    zend_long option;\n    bool option_is_null = 1;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_LONG_OR_NULL(option, option_is_null)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    if (option_is_null) {\n        char *s_code;\n        /* libcurl expects long datatype. So far no cases are known where\n           it would be an issue. Using zend_long would truncate a 64-bit\n           var on Win64, so the exact long datatype fits everywhere, as\n           long as there's no 32-bit int overflow. */\n        long l_code;\n        double d_code;\n        struct curl_certinfo *ci = NULL;\n        zval listcode;\n        curl_off_t co;\n\n        array_init(return_value);\n\n        if (curl_easy_getinfo(ch->cp, CURLINFO_EFFECTIVE_URL, &s_code) == CURLE_OK) {\n            CAAS(\"url\", s_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_TYPE, &s_code) == CURLE_OK) {\n            if (s_code != NULL) {\n                CAAS(\"content_type\", s_code);\n            } else {\n                zval retnull;\n                ZVAL_NULL(&retnull);\n                CAAZ(\"content_type\", &retnull);\n            }\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_HTTP_CODE, &l_code) == CURLE_OK) {\n            CAAL(\"http_code\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_HEADER_SIZE, &l_code) == CURLE_OK) {\n            CAAL(\"header_size\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_REQUEST_SIZE, &l_code) == CURLE_OK) {\n            CAAL(\"request_size\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_FILETIME, &l_code) == CURLE_OK) {\n            CAAL(\"filetime\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_SSL_VERIFYRESULT, &l_code) == CURLE_OK) {\n            CAAL(\"ssl_verify_result\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_COUNT, &l_code) == CURLE_OK) {\n            CAAL(\"redirect_count\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_TOTAL_TIME, &d_code) == CURLE_OK) {\n            CAAD(\"total_time\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_NAMELOOKUP_TIME, &d_code) == CURLE_OK) {\n            CAAD(\"namelookup_time\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_CONNECT_TIME, &d_code) == CURLE_OK) {\n            CAAD(\"connect_time\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_PRETRANSFER_TIME, &d_code) == CURLE_OK) {\n            CAAD(\"pretransfer_time\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_SIZE_UPLOAD, &d_code) == CURLE_OK) {\n            CAAD(\"size_upload\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_SIZE_DOWNLOAD, &d_code) == CURLE_OK) {\n            CAAD(\"size_download\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_SPEED_DOWNLOAD, &d_code) == CURLE_OK) {\n            CAAD(\"speed_download\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_SPEED_UPLOAD, &d_code) == CURLE_OK) {\n            CAAD(\"speed_upload\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d_code) == CURLE_OK) {\n            CAAD(\"download_content_length\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_CONTENT_LENGTH_UPLOAD, &d_code) == CURLE_OK) {\n            CAAD(\"upload_content_length\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_STARTTRANSFER_TIME, &d_code) == CURLE_OK) {\n            CAAD(\"starttransfer_time\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_TIME, &d_code) == CURLE_OK) {\n            CAAD(\"redirect_time\", d_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_URL, &s_code) == CURLE_OK) {\n            CAAS(\"redirect_url\", s_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_PRIMARY_IP, &s_code) == CURLE_OK) {\n            CAAS(\"primary_ip\", s_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_CERTINFO, &ci) == CURLE_OK) {\n            array_init(&listcode);\n            create_certinfo(ci, &listcode);\n            CAAZ(\"certinfo\", &listcode);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_PRIMARY_PORT, &l_code) == CURLE_OK) {\n            CAAL(\"primary_port\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_LOCAL_IP, &s_code) == CURLE_OK) {\n            CAAS(\"local_ip\", s_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_LOCAL_PORT, &l_code) == CURLE_OK) {\n            CAAL(\"local_port\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_HTTP_VERSION, &l_code) == CURLE_OK) {\n            CAAL(\"http_version\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_PROTOCOL, &l_code) == CURLE_OK) {\n            CAAL(\"protocol\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_PROXY_SSL_VERIFYRESULT, &l_code) == CURLE_OK) {\n            CAAL(\"ssl_verifyresult\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_SCHEME, &s_code) == CURLE_OK) {\n            CAAS(\"scheme\", s_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_APPCONNECT_TIME_T, &co) == CURLE_OK) {\n            CAAL(\"appconnect_time_us\", co);\n        }\n#if LIBCURL_VERSION_NUM >= 0x080600 && PHP_VERSION_ID >= 80500 /* Available since 8.6.0 */\n        if (curl_easy_getinfo(ch->cp, CURLINFO_QUEUE_TIME_T, &co) == CURLE_OK) {\n            CAAL(\"queue_time_us\", co);\n        }\n#endif\n        if (curl_easy_getinfo(ch->cp, CURLINFO_CONNECT_TIME_T, &co) == CURLE_OK) {\n            CAAL(\"connect_time_us\", co);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_NAMELOOKUP_TIME_T, &co) == CURLE_OK) {\n            CAAL(\"namelookup_time_us\", co);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_PRETRANSFER_TIME_T, &co) == CURLE_OK) {\n            CAAL(\"pretransfer_time_us\", co);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_TIME_T, &co) == CURLE_OK) {\n            CAAL(\"redirect_time_us\", co);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_STARTTRANSFER_TIME_T, &co) == CURLE_OK) {\n            CAAL(\"starttransfer_time_us\", co);\n        }\n#if LIBCURL_VERSION_NUM >= 0x080a00 /* Available since 8.10.0 */\n        if (curl_easy_getinfo(ch->cp, CURLINFO_POSTTRANSFER_TIME_T, &co) == CURLE_OK) {\n            CAAL(\"posttransfer_time_us\", co);\n        }\n#endif\n        if (curl_easy_getinfo(ch->cp, CURLINFO_TOTAL_TIME_T, &co) == CURLE_OK) {\n            CAAL(\"total_time_us\", co);\n        }\n        if (ch->header.str) {\n            CAASTR(\"request_header\", ch->header.str);\n        }\n#if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */\n        if (curl_easy_getinfo(ch->cp, CURLINFO_EFFECTIVE_METHOD, &s_code) == CURLE_OK) {\n            CAAS(\"effective_method\", s_code);\n        }\n#endif\n#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */\n        if (curl_easy_getinfo(ch->cp, CURLINFO_CAPATH, &s_code) == CURLE_OK) {\n            CAAS(\"capath\", s_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_CAINFO, &s_code) == CURLE_OK) {\n            CAAS(\"cainfo\", s_code);\n        }\n#endif\n#if PHP_VERSION_ID >= 80500\n#if LIBCURL_VERSION_NUM >= 0x080700 /* Available since 8.7.0 */\n        if (curl_easy_getinfo(ch->cp, CURLINFO_USED_PROXY, &l_code) == CURLE_OK) {\n            CAAL(\"used_proxy\", l_code);\n        }\n#endif\n#if LIBCURL_VERSION_NUM >= 0x080c00 /* Available since 8.12.0 */\n        if (curl_easy_getinfo(ch->cp, CURLINFO_HTTPAUTH_USED, &l_code) == CURLE_OK) {\n            CAAL(\"httpauth_used\", l_code);\n        }\n        if (curl_easy_getinfo(ch->cp, CURLINFO_PROXYAUTH_USED, &l_code) == CURLE_OK) {\n            CAAL(\"proxyauth_used\", l_code);\n        }\n#endif\n#if LIBCURL_VERSION_NUM >= 0x080200 /* Available since 8.2.0 */\n        if (curl_easy_getinfo(ch->cp, CURLINFO_CONN_ID, &co) == CURLE_OK) {\n            CAAL(\"conn_id\", co);\n        }\n#endif\n#endif\n    } else {\n        switch (option) {\n        case CURLINFO_HEADER_OUT:\n            if (ch->header.str) {\n                RETURN_STR_COPY(ch->header.str);\n            } else {\n                RETURN_FALSE;\n            }\n        case CURLINFO_CERTINFO: {\n            struct curl_certinfo *ci = NULL;\n\n            array_init(return_value);\n\n            if (curl_easy_getinfo(ch->cp, CURLINFO_CERTINFO, &ci) == CURLE_OK) {\n                create_certinfo(ci, return_value);\n            } else {\n                RETURN_FALSE;\n            }\n            break;\n        }\n        case CURLINFO_PRIVATE:\n            if (!Z_ISUNDEF(ch->private_data)) {\n                RETURN_COPY(&ch->private_data);\n            } else {\n                RETURN_FALSE;\n            }\n            break;\n        default: {\n            int type = CURLINFO_TYPEMASK & option;\n            switch (type) {\n            case CURLINFO_STRING: {\n                char *s_code = NULL;\n\n                if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &s_code) == CURLE_OK && s_code) {\n                    RETURN_STRING(s_code);\n                } else {\n                    RETURN_FALSE;\n                }\n                break;\n            }\n            case CURLINFO_LONG: {\n                zend_long code = 0;\n\n                if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &code) == CURLE_OK) {\n                    RETURN_LONG(code);\n                } else {\n                    RETURN_FALSE;\n                }\n                break;\n            }\n            case CURLINFO_DOUBLE: {\n                double code = 0.0;\n\n                if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &code) == CURLE_OK) {\n                    RETURN_DOUBLE(code);\n                } else {\n                    RETURN_FALSE;\n                }\n                break;\n            }\n            case CURLINFO_SLIST: {\n                struct curl_slist *slist;\n                if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &slist) == CURLE_OK) {\n                    struct curl_slist *current = slist;\n                    array_init(return_value);\n#if PHP_VERSION_ID >= 80500\n\t\t\tzend_hash_real_init_packed(Z_ARRVAL_P(return_value));\n#endif\n                    while (current) {\n                        add_next_index_string(return_value, current->data);\n                        current = current->next;\n                    }\n                    curl_slist_free_all(slist);\n                } else {\n                    RETURN_FALSE;\n                }\n                break;\n            }\n            case CURLINFO_OFF_T: {\n                curl_off_t c_off;\n                if (curl_easy_getinfo(ch->cp, (CURLINFO) option, &c_off) == CURLE_OK) {\n                    RETURN_LONG((long) c_off);\n                } else {\n                    RETURN_FALSE;\n                }\n                break;\n            }\n            default:\n                RETURN_FALSE;\n            }\n        }\n        }\n    }\n}\n/* }}} */\n\n/* {{{ Return a string contain the last error for the current session */\nPHP_FUNCTION(swoole_native_curl_error) {\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    if (ch->err.no) {\n        ch->err.str[CURL_ERROR_SIZE] = 0;\n        if (strlen(ch->err.str) > 0) {\n            RETURN_STRING(ch->err.str);\n        } else {\n            RETURN_STRING(curl_easy_strerror((CURLcode) ch->err.no));\n        }\n    } else {\n        RETURN_EMPTY_STRING();\n    }\n}\n/* }}} */\n\n/* {{{ Return an integer containing the last error number */\nPHP_FUNCTION(swoole_native_curl_errno) {\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    RETURN_LONG(ch->err.no);\n}\n/* }}} */\n\n/* {{{ Close a cURL session */\nPHP_FUNCTION(swoole_native_curl_close) {\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        return;\n    }\n\n    if (ch->in_callback) {\n        zend_throw_error(NULL, \"%s(): Attempt to close cURL handle from a callback\", get_active_function_name());\n        RETURN_THROWS();\n    }\n}\n/* }}} */\n\nstatic void swoole_curl_free_obj(zend_object *object) {\n    php_curl *ch = curl_from_obj(object);\n\n#if PHP_CURL_DEBUG\n    fprintf(stderr, \"DTOR CALLED, ch = %x\\n\", ch);\n#endif\n\n    if (!ch->cp) {\n        /* Can happen if constructor throws. */\n        zend_object_std_dtor(&ch->std);\n        return;\n    }\n\n    swoole_curl_verify_handlers(ch, /* reporterror */ false);\n\n    swoole::curl::Handle *handle = swoole::curl::get_handle(ch->cp);\n    if (handle && handle->multi) {\n        handle->multi->remove_handle(handle);\n    }\n\n    /* cURL destructors should be invoked only by last curl handle */\n    if (--(*ch->clone) == 0) {\n        zend_llist_clean(&ch->to_free->post);\n        zend_llist_clean(&ch->to_free->stream);\n#if PHP_VERSION_ID >= 80500\n\tzend_hash_destroy(&ch->to_free->slist);\n#else\n        zend_hash_destroy(ch->to_free->slist);\n        efree(ch->to_free->slist);\n#endif\n        efree(ch->to_free);\n        efree(ch->clone);\n        swoole::curl::destroy_handle(ch->cp);\n    }\n\n    if (ch->cp) {\n        curl_easy_cleanup(ch->cp);\n    }\n\n    smart_str_free(&ch->handlers.write->buf);\n    if (ZEND_FCC_INITIALIZED(ch->handlers.write->fcc)) {\n        zend_fcc_dtor(&ch->handlers.write->fcc);\n    }\n    if (ZEND_FCC_INITIALIZED(ch->handlers.write_header->fcc)) {\n        zend_fcc_dtor(&ch->handlers.write_header->fcc);\n    }\n    if (ZEND_FCC_INITIALIZED(ch->handlers.read->fcc)) {\n        zend_fcc_dtor(&ch->handlers.read->fcc);\n    }\n    zval_ptr_dtor(&ch->handlers.std_err);\n    if (ch->header.str) {\n        zend_string_release_ex(ch->header.str, 0);\n    }\n\n    zval_ptr_dtor(&ch->handlers.write_header->stream);\n    zval_ptr_dtor(&ch->handlers.write->stream);\n    zval_ptr_dtor(&ch->handlers.read->stream);\n\n    efree(ch->handlers.write);\n    efree(ch->handlers.write_header);\n    efree(ch->handlers.read);\n\n    if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) {\n        zend_fcc_dtor(&ch->handlers.progress);\n    }\n    if (ZEND_FCC_INITIALIZED(ch->handlers.xferinfo)) {\n        zend_fcc_dtor(&ch->handlers.xferinfo);\n    }\n    if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) {\n        zend_fcc_dtor(&ch->handlers.fnmatch);\n    }\n    if (ZEND_FCC_INITIALIZED(ch->handlers.debug)) {\n        zend_fcc_dtor(&ch->handlers.debug);\n    }\n#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */\n    if (ZEND_FCC_INITIALIZED(ch->handlers.prereq)) {\n        zend_fcc_dtor(&ch->handlers.prereq);\n    }\n#endif\n\n#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */\n    if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) {\n        zend_fcc_dtor(&ch->handlers.sshhostkey);\n    }\n#endif\n\n    zval_ptr_dtor(&ch->postfields);\n    zval_ptr_dtor(&ch->private_data);\n\n    if (ch->share) {\n        OBJ_RELEASE(&ch->share->std);\n    }\n\n    zend_object_std_dtor(&ch->std);\n}\n/* }}} */\n\n/* {{{ _php_curl_reset_handlers()\n   Reset all handlers of a given php_curl */\nstatic void _php_curl_reset_handlers(php_curl *ch) {\n    if (!Z_ISUNDEF(ch->handlers.write->stream)) {\n        zval_ptr_dtor(&ch->handlers.write->stream);\n        ZVAL_UNDEF(&ch->handlers.write->stream);\n    }\n    ch->handlers.write->fp = NULL;\n    ch->handlers.write->method = PHP_CURL_STDOUT;\n\n    if (!Z_ISUNDEF(ch->handlers.write_header->stream)) {\n        zval_ptr_dtor(&ch->handlers.write_header->stream);\n        ZVAL_UNDEF(&ch->handlers.write_header->stream);\n    }\n    ch->handlers.write_header->fp = NULL;\n    ch->handlers.write_header->method = PHP_CURL_IGNORE;\n\n    if (!Z_ISUNDEF(ch->handlers.read->stream)) {\n        zval_ptr_dtor(&ch->handlers.read->stream);\n        ZVAL_UNDEF(&ch->handlers.read->stream);\n    }\n    ch->handlers.read->fp = NULL;\n    ch->handlers.read->res = NULL;\n    ch->handlers.read->method = PHP_CURL_DIRECT;\n\n    if (!Z_ISUNDEF(ch->handlers.std_err)) {\n        zval_ptr_dtor(&ch->handlers.std_err);\n        ZVAL_UNDEF(&ch->handlers.std_err);\n    }\n\n    if (ZEND_FCC_INITIALIZED(ch->handlers.progress)) {\n        zend_fcc_dtor(&ch->handlers.progress);\n    }\n\n    if (ZEND_FCC_INITIALIZED(ch->handlers.xferinfo)) {\n        zend_fcc_dtor(&ch->handlers.xferinfo);\n    }\n\n    if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) {\n        zend_fcc_dtor(&ch->handlers.fnmatch);\n    }\n\n    if (ZEND_FCC_INITIALIZED(ch->handlers.debug)) {\n        zend_fcc_dtor(&ch->handlers.debug);\n    }\n#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */\n    if (ZEND_FCC_INITIALIZED(ch->handlers.prereq)) {\n        zend_fcc_dtor(&ch->handlers.prereq);\n    }\n#endif\n\n#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */\n    if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) {\n        zend_fcc_dtor(&ch->handlers.sshhostkey);\n    }\n#endif\n}\n/* }}} */\n\n/* {{{ Reset all options of a libcurl session handle */\nPHP_FUNCTION(swoole_native_curl_reset) {\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    if (ch->in_callback) {\n        zend_throw_error(NULL, \"%s(): Attempt to reset cURL handle from a callback\", get_active_function_name());\n        RETURN_THROWS();\n    }\n\n    curl_easy_reset(ch->cp);\n    _php_curl_reset_handlers(ch);\n    _php_curl_set_default_options(ch);\n}\n/* }}} */\n\n/* {{{ URL encodes the given string */\nPHP_FUNCTION(swoole_native_curl_escape) {\n    zend_string *str;\n    char *res;\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    Z_PARAM_STR(str)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(str))) {\n        RETURN_FALSE;\n    }\n\n    if ((res = curl_easy_escape(ch->cp, ZSTR_VAL(str), ZSTR_LEN(str)))) {\n        RETVAL_STRING(res);\n        curl_free(res);\n    } else {\n        RETURN_FALSE;\n    }\n}\n/* }}} */\n\n/* {{{ URL decodes the given string */\nPHP_FUNCTION(swoole_native_curl_unescape) {\n    char *out = NULL;\n    int out_len;\n    zval *zid;\n    zend_string *str;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_OBJECT_OF_CLASS(zid, swoole_coroutine_curl_handle_ce)\n    Z_PARAM_STR(str)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(str))) {\n        RETURN_FALSE;\n    }\n\n    if ((out = curl_easy_unescape(ch->cp, ZSTR_VAL(str), ZSTR_LEN(str), &out_len))) {\n        RETVAL_STRINGL(out, out_len);\n        curl_free(out);\n    } else {\n        RETURN_FALSE;\n    }\n}\n/* }}} */\n\n/* {{{ pause and unpause a connection */\nPHP_FUNCTION(swoole_native_curl_pause) {\n    zend_long bitmask;\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)\n    Z_PARAM_LONG(bitmask)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    RETURN_LONG(curl_easy_pause(ch->cp, bitmask));\n}\n/* }}} */\n\n#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */\n/* {{{ perform connection upkeep checks */\nPHP_FUNCTION(swoole_native_curl_upkeep) {\n    CURLcode error;\n    zval *zid;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(zid, curl_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if ((ch = swoole_curl_get_handle(zid)) == NULL) {\n        RETURN_FALSE;\n    }\n\n    error = curl_easy_upkeep(ch->cp);\n    SAVE_CURL_ERROR(ch, error);\n\n    RETURN_BOOL(error == CURLE_OK);\n}\n/*}}} */\n#endif\n#endif\n"
  },
  {
    "path": "thirdparty/php84/curl/multi.cc",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | Copyright (c) The PHP Group                                          |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | https://www.php.net/license/3_01.txt                                 |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Author: Sterling Hughes <sterling@php.net>                           |\n   +----------------------------------------------------------------------+\n*/\n\n#include \"php_swoole_cxx.h\"\n#include \"zend_object_handlers.h\"\n\n#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400\n\n#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS\n#include \"php_swoole_curl.h\"\n\nusing swoole::curl::Multi;\nusing swoole::curl::Selector;\n\nSW_EXTERN_C_BEGIN\n#include \"swoole_curl_interface.h\"\n#include \"curl_arginfo.h\"\n\n#include <stdio.h>\n#include <string.h>\n\n#include <curl/curl.h>\n#include <curl/easy.h>\n\n#define SAVE_CURLM_ERROR(__handle, __err) (__handle)->err.no = (int) __err;\n\nvoid swoole_curl_multi_set_in_coroutine(php_curlm *mh, bool value) {\n    zend_update_property_bool(nullptr, &mh->std, ZEND_STRL(\"in_coroutine\"), value);\n}\n\nbool swoole_curl_multi_is_in_coroutine(php_curlm *mh) {\n    zval rv;\n    zval *zv = zend_read_property_ex(nullptr, &mh->std, SW_ZSTR_KNOWN(SW_ZEND_STR_IN_COROUTINE), 1, &rv);\n    return zval_is_true(zv);\n}\n\n/* CurlMultiHandle class */\nzend_class_entry *swoole_coroutine_curl_multi_handle_ce;\n\nstatic inline php_curlm *curl_multi_from_obj(zend_object *obj) {\n    return (php_curlm *) ((char *) (obj) -XtOffsetOf(php_curlm, std));\n}\n\n#define Z_CURL_MULTI_P(zv) curl_multi_from_obj(Z_OBJ_P(zv))\n\nstatic void _php_curl_multi_free(php_curlm *mh);\n\nSW_EXTERN_C_END\n\n/* {{{ Returns a new cURL multi handle */\nPHP_FUNCTION(swoole_native_curl_multi_init) {\n    php_curlm *mh;\n\n    ZEND_PARSE_PARAMETERS_NONE();\n\n    object_init_ex(return_value, swoole_coroutine_curl_multi_handle_ce);\n    mh = Z_CURL_MULTI_P(return_value);\n    mh->multi = new Multi();\n\n    swoole_curl_multi_set_in_coroutine(mh, true);\n    zend_llist_init(&mh->easyh, sizeof(zval), swoole_curl_multi_cleanup_list, 0);\n}\n/* }}} */\n\n/* {{{ Add a normal cURL handle to a cURL multi handle */\nPHP_FUNCTION(swoole_native_curl_multi_add_handle) {\n    zval *z_mh;\n    zval *z_ch;\n    php_curlm *mh;\n    php_curl *ch;\n    CURLMcode error = CURLM_OK;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce)\n    Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    mh = Z_CURL_MULTI_P(z_mh);\n    ch = swoole_curl_get_handle(z_ch);\n\n    if (!(swoole_curl_multi_is_in_coroutine(mh))) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION,\n                           \"The given object is not a valid coroutine CurlMultiHandle object\");\n        RETURN_FALSE;\n    }\n\n    swoole_curl_verify_handlers(ch, /* reporterror */ true);\n    swoole_curl_cleanup_handle(ch);\n\n    auto handle = swoole::curl::get_handle(ch->cp);\n    error = mh->multi->add_handle(handle);\n    SAVE_CURLM_ERROR(mh, error);\n\n    if (error == CURLM_OK) {\n        Z_ADDREF_P(z_ch);\n        zend_llist_add_element(&mh->easyh, z_ch);\n    }\n\n    swoole_trace_log(SW_TRACE_CO_CURL, \"multi=%p, cp=%p, handle=%p, error=%d\", mh->multi, ch->cp, handle, error);\n    RETURN_LONG((zend_long) error);\n}\n/* }}} */\n\nvoid swoole_curl_multi_cleanup_list(void *data) /* {{{ */\n{\n    zval *z_ch = (zval *) data;\n\n    zval_ptr_dtor(z_ch);\n}\n/* }}} */\n\n/* Used internally as comparison routine passed to zend_list_del_element */\nstatic int curl_compare_objects(zval *z1, zval *z2) /* {{{ */\n{\n    return (Z_TYPE_P(z1) == Z_TYPE_P(z2) && Z_TYPE_P(z1) == IS_OBJECT && Z_OBJ_P(z1) == Z_OBJ_P(z2));\n}\n/* }}} */\n\n/* Used to find the php_curl resource for a given curl easy handle */\nstatic zval *_php_curl_multi_find_easy_handle(php_curlm *mh, CURL *easy) /* {{{ */\n{\n    php_curl *tmp_ch;\n    zend_llist_position pos;\n    zval *pz_ch_temp;\n\n    for (pz_ch_temp = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch_temp;\n         pz_ch_temp = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) {\n        tmp_ch = swoole_curl_get_handle(pz_ch_temp, false, false);\n\n        if (tmp_ch && tmp_ch->cp == easy) {\n            return pz_ch_temp;\n        }\n    }\n\n    return NULL;\n}\n/* }}} */\n\n/* {{{ Remove a multi handle from a set of cURL handles */\nPHP_FUNCTION(swoole_native_curl_multi_remove_handle) {\n    zval *z_mh;\n    zval *z_ch;\n    php_curlm *mh;\n    php_curl *ch;\n    CURLMcode error = CURLM_OK;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce)\n    Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    mh = Z_CURL_MULTI_P(z_mh);\n    if (!(swoole_curl_multi_is_in_coroutine(mh))) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION,\n                           \"The given object is not a valid coroutine CurlMultiHandle object\");\n        RETURN_FALSE;\n    }\n\n    ch = Z_CURL_P(z_ch);\n    auto handle = swoole::curl::get_handle(ch->cp);\n    if (handle && handle->multi) {\n        error = mh->multi->remove_handle(handle);\n    } else {\n        error = curl_multi_remove_handle(mh->multi, ch->cp);\n    }\n\n    swoole_trace_log(SW_TRACE_CO_CURL, \"multi=%p, cp=%p, handle=%p, error=%d\", mh->multi, ch->cp, handle, error);\n    SAVE_CURLM_ERROR(mh, error);\n    if (error == CURLM_OK) {\n        zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *)) curl_compare_objects);\n    }\n\n    RETVAL_LONG((zend_long) error);\n}\n/* }}} */\n\n/* {{{ Get all the sockets associated with the cURL extension, which can then be \"selected\" */\nPHP_FUNCTION(swoole_native_curl_multi_select) {\n    zval *z_mh;\n    php_curlm *mh;\n    double timeout = 1.0;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_DOUBLE(timeout)\n    ZEND_PARSE_PARAMETERS_END();\n\n    mh = Z_CURL_MULTI_P(z_mh);\n    if (!(swoole_curl_multi_is_in_coroutine(mh))) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION,\n                           \"The given object is not a valid coroutine CurlMultiHandle object\");\n        RETURN_FALSE;\n    }\n\n#if PHP_VERSION_ID >= 80500\n    if (!(timeout >= 0.0 && timeout <= (INT_MAX / 1000.0))) {\n        zend_argument_value_error(2, \"must be between 0 and %f\", INT_MAX / 1000.0);\n        RETURN_THROWS();\n    }\n#else\n    if (!(timeout >= 0.0 && timeout <= ((double) INT_MAX / 1000.0))) {\n        zend_argument_value_error(2, \"must be between 0 and %d\", (int) ceilf((double) INT_MAX / 1000));\n        RETURN_THROWS();\n    }\n#endif\n\n    RETURN_LONG(mh->multi->select(mh, timeout));\n}\n/* }}} */\n\n/* {{{ Run the sub-connections of the current cURL handle */\nPHP_FUNCTION(swoole_native_curl_multi_exec) {\n    zval *z_mh;\n    zval *z_still_running;\n    php_curlm *mh;\n    int still_running;\n    CURLMcode error = CURLM_OK;\n\n    ZEND_PARSE_PARAMETERS_START(2, 2)\n    Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce)\n    Z_PARAM_ZVAL(z_still_running)\n    ZEND_PARSE_PARAMETERS_END();\n\n    mh = Z_CURL_MULTI_P(z_mh);\n    if (!(swoole_curl_multi_is_in_coroutine(mh))) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION,\n                           \"The given object is not a valid coroutine CurlMultiHandle object\");\n        RETURN_FALSE;\n    }\n\n    {\n        zend_llist_position pos;\n        php_curl *ch;\n        zval *pz_ch;\n\n        for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;\n             pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) {\n            ch = Z_CURL_P(pz_ch);\n            swoole_curl_verify_handlers(ch, /* reporterror */ true);\n        }\n    }\n\n    error = mh->multi->perform();\n    still_running = mh->multi->get_running_handles();\n    ZEND_TRY_ASSIGN_REF_LONG(z_still_running, still_running);\n\n    SAVE_CURLM_ERROR(mh, error);\n    RETURN_LONG((zend_long) error);\n}\n/* }}} */\n\n/* {{{ Return the content of a cURL handle if CURLOPT_RETURNTRANSFER is set */\nPHP_FUNCTION(swoole_native_curl_multi_getcontent) {\n    zval *z_ch;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(z_ch, swoole_coroutine_curl_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    ch = Z_CURL_P(z_ch);\n\n    if (ch->handlers.write->method == PHP_CURL_RETURN) {\n        if (!ch->handlers.write->buf.s) {\n            RETURN_EMPTY_STRING();\n        }\n        smart_str_0(&ch->handlers.write->buf);\n        RETURN_STR_COPY(ch->handlers.write->buf.s);\n    }\n\n    RETURN_NULL();\n}\n/* }}} */\n\n/* {{{ Get information about the current transfers */\nPHP_FUNCTION(swoole_native_curl_multi_info_read) {\n    zval *z_mh;\n    php_curlm *mh;\n    CURLMsg *tmp_msg;\n    int queued_msgs;\n    zval *zmsgs_in_queue = NULL;\n    php_curl *ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 2)\n    Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce)\n    Z_PARAM_OPTIONAL\n    Z_PARAM_ZVAL(zmsgs_in_queue)\n    ZEND_PARSE_PARAMETERS_END();\n\n    mh = Z_CURL_MULTI_P(z_mh);\n    if (!(swoole_curl_multi_is_in_coroutine(mh))) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION,\n                           \"The given object is not a valid coroutine CurlMultiHandle object\");\n        RETURN_FALSE;\n    }\n\n    tmp_msg = curl_multi_info_read(mh->multi->get_multi_handle(), &queued_msgs);\n    if (tmp_msg == NULL) {\n        RETURN_FALSE;\n    }\n\n    if (zmsgs_in_queue) {\n        ZEND_TRY_ASSIGN_REF_LONG(zmsgs_in_queue, queued_msgs);\n    }\n\n    array_init(return_value);\n    add_assoc_long(return_value, \"msg\", tmp_msg->msg);\n    add_assoc_long(return_value, \"result\", tmp_msg->data.result);\n\n    /* find the original easy curl handle */\n    {\n        zval *pz_ch = _php_curl_multi_find_easy_handle(mh, tmp_msg->easy_handle);\n        if (pz_ch != NULL) {\n            /* we must save result to be able to read error message */\n            ch = swoole_curl_get_handle(pz_ch, false, false);\n            SAVE_CURL_ERROR(ch, tmp_msg->data.result);\n\n            Z_ADDREF_P(pz_ch);\n            add_assoc_zval(return_value, \"handle\", pz_ch);\n        }\n    }\n}\n/* }}} */\n\n/* {{{ Close a set of cURL handles */\nPHP_FUNCTION(swoole_native_curl_multi_close) {\n    php_curlm *mh;\n    zval *z_mh;\n\n    zend_llist_position pos;\n    zval *pz_ch;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    mh = Z_CURL_MULTI_P(z_mh);\n\n    for (pz_ch = (zval *) zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;\n         pz_ch = (zval *) zend_llist_get_next_ex(&mh->easyh, &pos)) {\n        php_curl *ch = Z_CURL_P(pz_ch);\n        if (!ch) {\n            continue;\n        }\n        swoole_curl_verify_handlers(ch, 0);\n        auto handle = swoole::curl::get_handle(ch->cp);\n        if (handle) {\n            mh->multi->remove_handle(handle);\n        } else {\n            curl_multi_remove_handle(mh->multi, ch->cp);\n        }\n    }\n    zend_llist_clean(&mh->easyh);\n}\n/* }}} */\n\n/* {{{ Return an integer containing the last multi curl error number */\nPHP_FUNCTION(swoole_native_curl_multi_errno) {\n    zval *z_mh;\n    php_curlm *mh;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n    Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce)\n    ZEND_PARSE_PARAMETERS_END();\n\n    mh = Z_CURL_MULTI_P(z_mh);\n\n    RETURN_LONG(mh->err.no);\n}\n/* }}} */\n\nstatic int _php_server_push_callback(\n    CURL *parent_ch, CURL *easy, size_t num_headers, struct curl_pushheaders *push_headers, void *userp) /* {{{ */\n{\n    php_curl *ch;\n    php_curl *parent;\n    php_curlm *mh = (php_curlm *) userp;\n    int rval = CURL_PUSH_DENY;\n    zval *pz_parent_ch = NULL;\n    zval pz_ch;\n    zval headers;\n    zval retval;\n\n    pz_parent_ch = _php_curl_multi_find_easy_handle(mh, parent_ch);\n    if (pz_parent_ch == NULL) {\n        return rval;\n    }\n\n    parent = Z_CURL_P(pz_parent_ch);\n\n    ch = swoole_curl_init_handle_into_zval(&pz_ch);\n    ch->cp = easy;\n    swoole_setup_easy_copy_handlers(ch, parent);\n\n    auto parent_handle = swoole::curl::get_handle(parent->cp);\n    if (parent_handle) {\n        auto handle = swoole::curl::create_handle(easy);\n        handle->multi = parent_handle->multi;\n    }\n\n#if PHP_VERSION_ID >= 80500\n    array_init_size(&headers, num_headers);\n    zend_hash_real_init_packed(Z_ARRVAL(headers));\n    for (size_t i = 0; i < num_headers; i++) {\n        char *header = curl_pushheader_bynum(push_headers, i);\n        add_index_string(&headers, i, header);\n    }\n#else\n    array_init(&headers);\n    for (size_t i = 0; i < num_headers; i++) {\n        char *header = curl_pushheader_bynum(push_headers, i);\n        add_next_index_string(&headers, header);\n    }\n#endif\n\n    ZEND_ASSERT(pz_parent_ch);\n    zval call_args[3] = {*pz_parent_ch, pz_ch, headers};\n\n    zend_call_known_fcc(&mh->handlers.server_push, &retval, /* param_count */ 3, call_args, /* named_params */ NULL);\n    zval_ptr_dtor_nogc(&headers);\n\n    if (!Z_ISUNDEF(retval)) {\n        if (CURL_PUSH_DENY != swoole_curl_get_long(&retval)) {\n            rval = CURL_PUSH_OK;\n            zend_llist_add_element(&mh->easyh, &pz_ch);\n        } else {\n            /* libcurl will free this easy handle, avoid double free */\n            ch->cp = NULL;\n        }\n    }\n\n    return rval;\n}\n/* }}} */\n\nstatic bool _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, zval *return_value) /* {{{ */\n{\n    CURLMcode error = CURLM_OK;\n\n    switch (option) {\n    case CURLMOPT_PIPELINING:\n    case CURLMOPT_MAXCONNECTS:\n    case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE:\n    case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE:\n    case CURLMOPT_MAX_HOST_CONNECTIONS:\n    case CURLMOPT_MAX_PIPELINE_LENGTH:\n    case CURLMOPT_MAX_TOTAL_CONNECTIONS:\n#if LIBCURL_VERSION_NUM >= 0x074300 /* Available since 7.67.0 */\n    case CURLMOPT_MAX_CONCURRENT_STREAMS:\n#endif\n    {\n        zend_long lval = zval_get_long(zvalue);\n\n        if (option == CURLMOPT_PIPELINING && (lval & 1)) {\n#if LIBCURL_VERSION_NUM >= 0x073e00 /* Available since 7.62.0 */\n            php_error_docref(NULL, E_WARNING, \"CURLPIPE_HTTP1 is no longer supported\");\n#else\n            php_error_docref(NULL, E_DEPRECATED, \"CURLPIPE_HTTP1 is deprecated\");\n#endif\n        }\n        error = curl_multi_setopt(mh->multi->get_multi_handle(), (CURLMoption) option, lval);\n        break;\n    }\n    case CURLMOPT_PUSHFUNCTION: {\n        /* See php_curl_set_callable_handler */\n        if (ZEND_FCC_INITIALIZED(mh->handlers.server_push)) {\n            zend_fcc_dtor(&mh->handlers.server_push);\n        }\n\n        char *error_str = NULL;\n        if (UNEXPECTED(!zend_is_callable_ex(zvalue,\n                                            /* object */ NULL,\n                                            /* check_flags */ 0,\n                                            /* callable_name */ NULL,\n                                            &mh->handlers.server_push,\n                                            /* error */ &error_str))) {\n            if (!EG(exception)) {\n                zend_argument_type_error(2, \"must be a valid callback for option CURLMOPT_PUSHFUNCTION, %s\", error_str);\n            }\n            efree(error_str);\n            return false;\n        }\n        zend_fcc_addref(&mh->handlers.server_push);\n\n        error = curl_multi_setopt(mh->multi->get_multi_handle(), CURLMOPT_PUSHFUNCTION, _php_server_push_callback);\n        if (error != CURLM_OK) {\n            return false;\n        }\n        error = curl_multi_setopt(mh->multi->get_multi_handle(), CURLMOPT_PUSHDATA, mh);\n        break;\n    }\n    default:\n        zend_argument_value_error(2, \"is not a valid cURL multi option\");\n        error = CURLM_UNKNOWN_OPTION;\n        break;\n    }\n\n    SAVE_CURLM_ERROR(mh, error);\n\n    return error == CURLM_OK;\n}\n/* }}} */\n\n/* {{{ Set an option for the curl multi handle */\nPHP_FUNCTION(swoole_native_curl_multi_setopt) {\n    zval *z_mh, *zvalue;\n    zend_long options;\n    php_curlm *mh;\n\n    ZEND_PARSE_PARAMETERS_START(3, 3)\n    Z_PARAM_OBJECT_OF_CLASS(z_mh, swoole_coroutine_curl_multi_handle_ce)\n    Z_PARAM_LONG(options)\n    Z_PARAM_ZVAL(zvalue)\n    ZEND_PARSE_PARAMETERS_END();\n\n    mh = Z_CURL_MULTI_P(z_mh);\n    if (!(swoole_curl_multi_is_in_coroutine(mh))) {\n        swoole_fatal_error(SW_ERROR_WRONG_OPERATION,\n                           \"The given object is not a valid coroutine CurlMultiHandle object\");\n        RETURN_FALSE;\n    }\n    if (_php_curl_multi_setopt(mh, (CURLMoption) options, zvalue, return_value)) {\n        RETURN_TRUE;\n    } else {\n        RETURN_FALSE;\n    }\n}\n/* }}} */\n\n/* CurlMultiHandle class */\n\nstatic zend_object_handlers swoole_coroutine_curl_multi_handle_handlers;\n\nstatic zend_object *swoole_curl_multi_create_object(zend_class_entry *class_type) {\n    php_curlm *intern = (php_curlm *) zend_object_alloc(sizeof(php_curlm), class_type);\n\n    zend_object_std_init(&intern->std, class_type);\n    object_properties_init(&intern->std, class_type);\n    intern->std.handlers = &swoole_coroutine_curl_multi_handle_handlers;\n\n    return &intern->std;\n}\n\nstatic zend_function *swoole_curl_multi_get_constructor(zend_object *object) {\n    zend_throw_error(NULL, \"Cannot directly construct CurlMultiHandle, use curl_multi_init() instead\");\n    return NULL;\n}\n\nstatic void swoole_curl_multi_free_obj(zend_object *object) {\n    php_curlm *mh = curl_multi_from_obj(object);\n\n    if (!mh->multi) {\n        /* Can happen if constructor throws. */\n        zend_object_std_dtor(&mh->std);\n        return;\n    }\n\n    _php_curl_multi_free(mh);\n    zend_object_std_dtor(&mh->std);\n}\n\nstatic void _php_curl_multi_free(php_curlm *mh) {\n    bool is_in_coroutine = swoole_curl_multi_is_in_coroutine(mh);\n    for (zend_llist_element *element = mh->easyh.head; element; element = element->next) {\n        zval *z_ch = (zval *) element->data;\n        php_curl *ch;\n        if (OBJ_FLAGS(Z_OBJ_P(z_ch)) & IS_OBJ_FREE_CALLED) {\n            continue;\n        }\n        if ((ch = swoole_curl_get_handle(z_ch, true, false))) {\n            swoole_curl_verify_handlers(ch, 0);\n            auto handle = swoole::curl::get_handle(ch->cp);\n            if (is_in_coroutine && handle) {\n                mh->multi->remove_handle(handle);\n            } else {\n                curl_multi_remove_handle(mh->multi, ch->cp);\n            }\n        }\n    }\n    if (mh->multi) {\n        if (is_in_coroutine) {\n            delete mh->multi;\n        } else {\n            curl_multi_cleanup(mh->multi);\n        }\n        mh->multi = nullptr;\n    }\n    zend_llist_clean(&mh->easyh);\n\n    if (ZEND_FCC_INITIALIZED(mh->handlers.server_push)) {\n        zend_fcc_dtor(&mh->handlers.server_push);\n    }\n}\n\nstatic HashTable *swoole_curl_multi_get_gc(zend_object *object, zval **table, int *n) {\n    php_curlm *curl_multi = curl_multi_from_obj(object);\n\n    zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();\n\n    if (ZEND_FCC_INITIALIZED(curl_multi->handlers.server_push)) {\n        zend_get_gc_buffer_add_fcc(gc_buffer, &curl_multi->handlers.server_push);\n    }\n\n    zend_llist_position pos;\n    for (zval *pz_ch = (zval *) zend_llist_get_first_ex(&curl_multi->easyh, &pos); pz_ch;\n         pz_ch = (zval *) zend_llist_get_next_ex(&curl_multi->easyh, &pos)) {\n        zend_get_gc_buffer_add_zval(gc_buffer, pz_ch);\n    }\n\n    zend_get_gc_buffer_use(gc_buffer, table, n);\n\n#if PHP_VERSION_ID >= 80500\n    return NULL;\n#else\n    return zend_std_get_properties(object);\n#endif\n}\n\nvoid curl_multi_register_class(const zend_function_entry *method_entries) {\n    swoole_coroutine_curl_multi_handle_ce = curl_multi_ce;\n    swoole_coroutine_curl_multi_handle_ce->create_object = swoole_curl_multi_create_object;\n\n    memcpy(&swoole_coroutine_curl_multi_handle_handlers, &std_object_handlers, sizeof(zend_object_handlers));\n    swoole_coroutine_curl_multi_handle_handlers.offset = XtOffsetOf(php_curlm, std);\n    swoole_coroutine_curl_multi_handle_handlers.free_obj = swoole_curl_multi_free_obj;\n    swoole_coroutine_curl_multi_handle_handlers.get_gc = swoole_curl_multi_get_gc;\n    swoole_coroutine_curl_multi_handle_handlers.get_constructor = swoole_curl_multi_get_constructor;\n    swoole_coroutine_curl_multi_handle_handlers.clone_obj = NULL;\n    swoole_coroutine_curl_multi_handle_handlers.cast_object = swoole_curl_cast_object;\n    swoole_coroutine_curl_multi_handle_handlers.compare = [](zval *o1, zval *o2) { return ZEND_UNCOMPARABLE; };\n\n    zend_declare_property_bool(swoole_coroutine_curl_multi_handle_ce, ZEND_STRL(\"in_coroutine\"), 0, ZEND_ACC_PUBLIC);\n}\n#endif\n"
  },
  {
    "path": "thirdparty/php84/curl/php_curl.h",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | Copyright (c) The PHP Group                                          |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | https://www.php.net/license/3_01.txt                                 |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Author: Sterling Hughes <sterling@php.net>                           |\n   |         Wez Furlong <wez@thebrainroom.com>                           |\n   +----------------------------------------------------------------------+\n*/\n\n#if defined(SW_USE_CURL) && PHP_VERSION_ID >= 80400\n\n#ifndef _PHP_CURL_H\n#define _PHP_CURL_H\n\n#include \"php.h\"\n#include \"zend_smart_str.h\"\n\n#define PHP_CURL_DEBUG 0\n\n#ifdef PHP_WIN32\n#ifdef PHP_CURL_EXPORTS\n#define PHP_CURL_API __declspec(dllexport)\n#else\n#define PHP_CURL_API __declspec(dllimport)\n#endif\n#elif defined(__GNUC__) && __GNUC__ >= 4\n#define PHP_CURL_API __attribute__((visibility(\"default\")))\n#else\n#define PHP_CURL_API\n#endif\n\nPHP_CURL_API extern zend_class_entry *curl_ce;\nPHP_CURL_API extern zend_class_entry *curl_share_ce;\nPHP_CURL_API extern zend_class_entry *curl_multi_ce;\n#if PHP_VERSION_ID >= 80500\nPHP_CURL_API extern zend_class_entry *curl_share_persistent_ce;\n#endif\nPHP_CURL_API extern zend_class_entry *swoole_coroutine_curl_handle_ce;\nPHP_CURL_API extern zend_class_entry *swoole_coroutine_curl_multi_handle_ce;\nPHP_CURL_API extern zend_class_entry *curl_CURLFile_class;\nPHP_CURL_API extern zend_class_entry *curl_CURLStringFile_class;\n\n#endif /* _PHP_CURL_H */\n#endif\n"
  },
  {
    "path": "thirdparty/php84/ftp/CREDITS",
    "content": "FTP\nStefan Esser, Andrew Skalski\n"
  },
  {
    "path": "thirdparty/php84/ftp/ftp.c",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | Copyright (c) The PHP Group                                          |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | https://www.php.net/license/3_01.txt                                 |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Authors: Andrew Skalski <askalski@chek.com>                          |\n   |          Stefan Esser <sesser@php.net> (resume functions)            |\n   +----------------------------------------------------------------------+\n */\n\n#ifdef HAVE_CONFIG_H\n#include <config.h>\n#endif\n\n#include \"php.h\"\n#include \"php_swoole_api.h\"\n#include \"swoole_socket_hook.h\"\n\n#include <stdio.h>\n#include <ctype.h>\n#include <stdlib.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#include <fcntl.h>\n#include <string.h>\n#include <time.h>\n#ifdef PHP_WIN32\n#include <winsock2.h>\n#else\n#ifdef HAVE_SYS_TYPES_H\n#include <sys/types.h>\n#endif\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <netdb.h>\n#endif\n#include <errno.h>\n\n#ifdef HAVE_SYS_TIME_H\n#include <sys/time.h>\n#endif\n\n#ifdef HAVE_SYS_SELECT_H\n#include <sys/select.h>\n#endif\n\n#ifdef SW_HAVE_FTP_SSL\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n#endif\n\n#include \"ftp.h\"\n#include \"ext/standard/fsock.h\"\n\n#ifdef PHP_WIN32\n# undef ETIMEDOUT\n# define ETIMEDOUT WSAETIMEDOUT\n#endif\n\n/* sends an ftp command, returns true on success, false on error.\n * it sends the string \"cmd args\\r\\n\" if args is non-null, or\n * \"cmd\\r\\n\" if args is null\n */\nstatic int\t\tftp_putcmd(\tftpbuf_t *ftp,\n\t\t\t\t\tconst char *cmd,\n\t\t\t\t\tconst size_t cmd_len,\n\t\t\t\t\tconst char *args,\n\t\t\t\t\tconst size_t args_len);\n\n/* wrapper around send/recv to handle timeouts */\nstatic int\t\tmy_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);\nstatic int\t\tmy_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);\nstatic int\t\tmy_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen);\n\n/* reads a line the socket , returns true on success, false on error */\nstatic int\t\tftp_readline(ftpbuf_t *ftp);\n\n/* reads an ftp response, returns true on success, false on error */\nstatic int\t\tftp_getresp(ftpbuf_t *ftp);\n\n/* sets the ftp transfer type */\nstatic int\t\tftp_type(ftpbuf_t *ftp, ftptype_t type);\n\n/* opens up a data stream */\nstatic databuf_t*\tftp_getdata(ftpbuf_t *ftp);\n\n/* accepts the data connection, returns updated data buffer */\nstatic databuf_t*\tdata_accept(databuf_t *data, ftpbuf_t *ftp);\n\n/* closes the data connection, no-op if already closed */\nstatic void\t\tdata_close(ftpbuf_t *ftp);\n\n/* generic file lister */\nstatic char**\t\tftp_genlist(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, const char *path, const size_t path_len);\n\n#ifdef SW_HAVE_FTP_SSL\n/* shuts down a TLS/SSL connection */\nstatic void\t\tftp_ssl_shutdown(ftpbuf_t *ftp, php_socket_t fd, SSL *ssl_handle);\n#endif\n\n/* IP and port conversion box */\nunion ipbox {\n\tstruct in_addr\tia[2];\n\tunsigned short\ts[4];\n\tunsigned char\tc[8];\n};\n\n/* {{{ ftp_open */\nftpbuf_t*\nftp_open(const char *host, short port, zend_long timeout_sec)\n{\n\tftpbuf_t\t\t*ftp;\n\tsocklen_t\t\t size;\n\tstruct timeval tv;\n\n\n\t/* alloc the ftp structure */\n\tftp = ecalloc(1, sizeof(*ftp));\n\n\ttv.tv_sec = timeout_sec;\n\ttv.tv_usec = 0;\n\n\tftp->fd = php_async_socket_connect_to_host(host,\n\t\t\t(unsigned short) (port ? port : 21), SOCK_STREAM,\n\t\t\t0, &tv, NULL, NULL, NULL, 0, STREAM_SOCKOP_NONE);\n\tif (ftp->fd == -1) {\n\t\tgoto bail;\n\t}\n\n\t/* Default Settings */\n\tftp->timeout_sec = timeout_sec;\n\tftp->nb = 0;\n\n\tsize = sizeof(ftp->localaddr);\n\tmemset(&ftp->localaddr, 0, size);\n\tif (getsockname(ftp->fd, (struct sockaddr*) &ftp->localaddr, &size) != 0) {\n\t\tphp_error_docref(NULL, E_WARNING, \"getsockname failed: %s (%d)\", strerror(errno), errno);\n\t\tgoto bail;\n\t}\n\n\tif (!ftp_getresp(ftp) || ftp->resp != 220) {\n\t\tgoto bail;\n\t}\n\n\treturn ftp;\n\nbail:\n\tif (ftp->fd != -1) {\n\t\tclosesocket(ftp->fd);\n\t}\n\tefree(ftp);\n\treturn NULL;\n}\n/* }}} */\n\n/* {{{ ftp_close */\nftpbuf_t*\nftp_close(ftpbuf_t *ftp)\n{\n\tif (ftp == NULL) {\n\t\treturn NULL;\n\t}\n#ifdef SW_HAVE_FTP_SSL\n\tif (ftp->last_ssl_session) {\n\t\tSSL_SESSION_free(ftp->last_ssl_session);\n\t}\n#endif\n\tdata_close(ftp);\n\tif (ftp->stream && ftp->closestream) {\n\t\t\tphp_stream_close(ftp->stream);\n\t}\n\tif (ftp->fd != -1) {\n#ifdef SW_HAVE_FTP_SSL\n\t\tif (ftp->ssl_active) {\n\t\t\tftp_ssl_shutdown(ftp, ftp->fd, ftp->ssl_handle);\n\t\t}\n#endif\n\t\tclosesocket(ftp->fd);\n\t}\n\tftp_gc(ftp);\n\tefree(ftp);\n\treturn NULL;\n}\n/* }}} */\n\n/* {{{ ftp_gc */\nvoid\nftp_gc(ftpbuf_t *ftp)\n{\n\tif (ftp == NULL) {\n\t\treturn;\n\t}\n\tif (ftp->pwd) {\n\t\tefree(ftp->pwd);\n\t\tftp->pwd = NULL;\n\t}\n\tif (ftp->syst) {\n\t\tefree(ftp->syst);\n\t\tftp->syst = NULL;\n\t}\n}\n/* }}} */\n\n/* {{{ ftp_quit */\nint\nftp_quit(ftpbuf_t *ftp)\n{\n\tif (ftp == NULL) {\n\t\treturn 0;\n\t}\n\n\tif (!ftp_putcmd(ftp, \"QUIT\", sizeof(\"QUIT\")-1, NULL, (size_t) 0)) {\n\t\treturn 0;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp != 221) {\n\t\treturn 0;\n\t}\n\n\tif (ftp->pwd) {\n\t\tefree(ftp->pwd);\n\t\tftp->pwd = NULL;\n\t}\n\n\treturn 1;\n}\n/* }}} */\n\n#ifdef SW_HAVE_FTP_SSL\nstatic int ftp_ssl_new_session_cb(SSL *ssl, SSL_SESSION *sess)\n{\n\tftpbuf_t *ftp = SSL_get_app_data(ssl);\n\n\t/* Technically there can be multiple sessions per connection, but we only care about the most recent one. */\n\tif (ftp->last_ssl_session) {\n\t\tSSL_SESSION_free(ftp->last_ssl_session);\n\t}\n\tftp->last_ssl_session = SSL_get1_session(ssl);\n\n\t/* Return 0 as we are not using OpenSSL's session cache. */\n\treturn 0;\n}\n#endif\n\n/* {{{ ftp_login */\nint\nftp_login(ftpbuf_t *ftp, const char *user, const size_t user_len, const char *pass, const size_t pass_len)\n{\n#ifdef SW_HAVE_FTP_SSL\n\tSSL_CTX\t*ctx = NULL;\n\tlong ssl_ctx_options = SSL_OP_ALL;\n\tint err, res;\n\tbool retry;\n#endif\n\tif (ftp == NULL) {\n\t\treturn 0;\n\t}\n\n#ifdef SW_HAVE_FTP_SSL\n\tif (ftp->use_ssl && !ftp->ssl_active) {\n\t\tif (!ftp_putcmd(ftp, \"AUTH\", sizeof(\"AUTH\")-1, \"TLS\", sizeof(\"TLS\")-1)) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (!ftp_getresp(ftp)) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tif (ftp->resp != 234) {\n\t\t\tif (!ftp_putcmd(ftp, \"AUTH\", sizeof(\"AUTH\")-1, \"SSL\", sizeof(\"SSL\")-1)) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tif (!ftp_getresp(ftp)) {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tif (ftp->resp != 334) {\n\t\t\t\treturn 0;\n\t\t\t} else {\n\t\t\t\tftp->old_ssl = 1;\n\t\t\t\tftp->use_ssl_for_data = 1;\n\t\t\t}\n\t\t}\n\n\t\tctx = SSL_CTX_new(SSLv23_client_method());\n\t\tif (ctx == NULL) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"Failed to create the SSL context\");\n\t\t\treturn 0;\n\t\t}\n\n\t\tssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;\n\t\tSSL_CTX_set_options(ctx, ssl_ctx_options);\n\n\t\t/* Allow SSL to re-use sessions.\n\t\t * We're relying on our own session storage as only at most one session will ever be active per FTP connection. */\n\t\tSSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH | SSL_SESS_CACHE_NO_INTERNAL);\n\t\tSSL_CTX_sess_set_new_cb(ctx, ftp_ssl_new_session_cb);\n\n\t\tftp->ssl_handle = SSL_new(ctx);\n\t\tSSL_set_app_data(ftp->ssl_handle, ftp); /* Needed for ftp_ssl_new_session_cb */\n\t\tSSL_CTX_free(ctx);\n\n\t\tif (ftp->ssl_handle == NULL) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"Failed to create the SSL handle\");\n\t\t\treturn 0;\n\t\t}\n\n\t\tSSL_set_fd(ftp->ssl_handle, ftp->fd);\n\n\t\tdo {\n\t\t\tres = SSL_connect(ftp->ssl_handle);\n\t\t\terr = SSL_get_error(ftp->ssl_handle, res);\n\n\t\t\t/* TODO check if handling other error codes would make sense */\n\t\t\tswitch (err) {\n\t\t\t\tcase SSL_ERROR_NONE:\n\t\t\t\t\tretry = 0;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase SSL_ERROR_ZERO_RETURN:\n\t\t\t\t\tretry = 0;\n\t\t\t\t\tSSL_shutdown(ftp->ssl_handle);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase SSL_ERROR_WANT_READ:\n\t\t\t\tcase SSL_ERROR_WANT_WRITE: {\n\t\t\t\t\t\tphp_pollfd p;\n\t\t\t\t\t\tint i;\n\n\t\t\t\t\t\tp.fd = ftp->fd;\n\t\t\t\t\t\tp.events = (err == SSL_ERROR_WANT_READ) ? (POLLIN|POLLPRI) : POLLOUT;\n\t\t\t\t\t\tp.revents = 0;\n\n\t\t\t\t\t\ti = php_poll2(&p, 1, 300);\n\n\t\t\t\t\t\tretry = i > 0;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tphp_error_docref(NULL, E_WARNING, \"SSL/TLS handshake failed\");\n\t\t\t\t\tSSL_shutdown(ftp->ssl_handle);\n\t\t\t\t\tSSL_free(ftp->ssl_handle);\n\t\t\t\t\treturn 0;\n\t\t\t}\n\t\t} while (retry);\n\n\t\tftp->ssl_active = 1;\n\n\t\tif (!ftp->old_ssl) {\n\n\t\t\t/* set protection buffersize to zero */\n\t\t\tif (!ftp_putcmd(ftp, \"PBSZ\", sizeof(\"PBSZ\")-1, \"0\", sizeof(\"0\")-1)) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tif (!ftp_getresp(ftp)) {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t/* enable data conn encryption */\n\t\t\tif (!ftp_putcmd(ftp, \"PROT\", sizeof(\"PROT\")-1, \"P\", sizeof(\"P\")-1)) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tif (!ftp_getresp(ftp)) {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tftp->use_ssl_for_data = (ftp->resp >= 200 && ftp->resp <=299);\n\t\t}\n\t}\n#endif\n\n\tif (!ftp_putcmd(ftp, \"USER\", sizeof(\"USER\")-1, user, user_len)) {\n\t\treturn 0;\n\t}\n\tif (!ftp_getresp(ftp)) {\n\t\treturn 0;\n\t}\n\tif (ftp->resp == 230) {\n\t\treturn 1;\n\t}\n\tif (ftp->resp != 331) {\n\t\treturn 0;\n\t}\n\tif (!ftp_putcmd(ftp, \"PASS\", sizeof(\"PASS\")-1, pass, pass_len)) {\n\t\treturn 0;\n\t}\n\tif (!ftp_getresp(ftp)) {\n\t\treturn 0;\n\t}\n\treturn (ftp->resp == 230);\n}\n/* }}} */\n\n/* {{{ ftp_reinit */\nint\nftp_reinit(ftpbuf_t *ftp)\n{\n\tif (ftp == NULL) {\n\t\treturn 0;\n\t}\n\n\tftp_gc(ftp);\n\n\tftp->nb = 0;\n\n\tif (!ftp_putcmd(ftp, \"REIN\", sizeof(\"REIN\")-1, NULL, (size_t) 0)) {\n\t\treturn 0;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp != 220) {\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n/* }}} */\n\n/* {{{ ftp_syst */\nconst char*\nftp_syst(ftpbuf_t *ftp)\n{\n\tchar *syst, *end;\n\n\tif (ftp == NULL) {\n\t\treturn NULL;\n\t}\n\n\t/* default to cached value */\n\tif (ftp->syst) {\n\t\treturn ftp->syst;\n\t}\n\tif (!ftp_putcmd(ftp, \"SYST\", sizeof(\"SYST\")-1, NULL, (size_t) 0)) {\n\t\treturn NULL;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp != 215) {\n\t\treturn NULL;\n\t}\n\tsyst = ftp->inbuf;\n\twhile (*syst == ' ') {\n\t\tsyst++;\n\t}\n\tif ((end = strchr(syst, ' '))) {\n\t\t*end = 0;\n\t}\n\tftp->syst = estrdup(syst);\n\tif (end) {\n\t\t*end = ' ';\n\t}\n\treturn ftp->syst;\n}\n/* }}} */\n\n/* {{{ ftp_pwd */\nconst char*\nftp_pwd(ftpbuf_t *ftp)\n{\n\tchar *pwd, *end;\n\n\tif (ftp == NULL) {\n\t\treturn NULL;\n\t}\n\n\t/* default to cached value */\n\tif (ftp->pwd) {\n\t\treturn ftp->pwd;\n\t}\n\tif (!ftp_putcmd(ftp, \"PWD\", sizeof(\"PWD\")-1, NULL, (size_t) 0)) {\n\t\treturn NULL;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp != 257) {\n\t\treturn NULL;\n\t}\n\t/* copy out the pwd from response */\n\tif ((pwd = strchr(ftp->inbuf, '\"')) == NULL) {\n\t\treturn NULL;\n\t}\n\tif ((end = strrchr(++pwd, '\"')) == NULL) {\n\t\treturn NULL;\n\t}\n\tftp->pwd = estrndup(pwd, end - pwd);\n\n\treturn ftp->pwd;\n}\n/* }}} */\n\n/* {{{ ftp_exec */\nint\nftp_exec(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len)\n{\n\tif (ftp == NULL) {\n\t\treturn 0;\n\t}\n\tif (!ftp_putcmd(ftp, \"SITE EXEC\", sizeof(\"SITE EXEC\")-1, cmd, cmd_len)) {\n\t\treturn 0;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp != 200) {\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n/* }}} */\n\n/* {{{ ftp_raw */\nvoid\nftp_raw(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, zval *return_value)\n{\n\tif (ftp == NULL || cmd == NULL) {\n\t\tRETURN_NULL();\n\t}\n\tif (!ftp_putcmd(ftp, cmd, cmd_len, NULL, (size_t) 0)) {\n\t\tRETURN_NULL();\n\t}\n\tarray_init(return_value);\n\twhile (ftp_readline(ftp)) {\n\t\tadd_next_index_string(return_value, ftp->inbuf);\n\t\tif (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {\n\t\t\treturn;\n\t\t}\n\t}\n}\n/* }}} */\n\n/* {{{ ftp_chdir */\nint\nftp_chdir(ftpbuf_t *ftp, const char *dir, const size_t dir_len)\n{\n\tif (ftp == NULL) {\n\t\treturn 0;\n\t}\n\n\tif (ftp->pwd) {\n\t\tefree(ftp->pwd);\n\t\tftp->pwd = NULL;\n\t}\n\n\tif (!ftp_putcmd(ftp, \"CWD\", sizeof(\"CWD\")-1, dir, dir_len)) {\n\t\treturn 0;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp != 250) {\n\t\treturn 0;\n\t}\n\treturn 1;\n}\n/* }}} */\n\n/* {{{ ftp_cdup */\nint\nftp_cdup(ftpbuf_t *ftp)\n{\n\tif (ftp == NULL) {\n\t\treturn 0;\n\t}\n\n\tif (ftp->pwd) {\n\t\tefree(ftp->pwd);\n\t\tftp->pwd = NULL;\n\t}\n\n\tif (!ftp_putcmd(ftp, \"CDUP\", sizeof(\"CDUP\")-1, NULL, (size_t) 0)) {\n\t\treturn 0;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp != 250) {\n\t\treturn 0;\n\t}\n\treturn 1;\n}\n/* }}} */\n\n/* {{{ ftp_mkdir */\nzend_string*\nftp_mkdir(ftpbuf_t *ftp, const char *dir, const size_t dir_len)\n{\n\tchar *mkd, *end;\n\tzend_string *ret;\n\n\tif (ftp == NULL) {\n\t\treturn NULL;\n\t}\n\tif (!ftp_putcmd(ftp, \"MKD\", sizeof(\"MKD\")-1, dir, dir_len)) {\n\t\treturn NULL;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp != 257) {\n\t\treturn NULL;\n\t}\n\t/* copy out the dir from response */\n\tif ((mkd = strchr(ftp->inbuf, '\"')) == NULL) {\n\t\treturn zend_string_init(dir, dir_len, 0);\n\t}\n\tif ((end = strrchr(++mkd, '\"')) == NULL) {\n\t\treturn NULL;\n\t}\n\t*end = 0;\n\tret = zend_string_init(mkd, end - mkd, 0);\n\t*end = '\"';\n\n\treturn ret;\n}\n/* }}} */\n\n/* {{{ ftp_rmdir */\nint\nftp_rmdir(ftpbuf_t *ftp, const char *dir, const size_t dir_len)\n{\n\tif (ftp == NULL) {\n\t\treturn 0;\n\t}\n\tif (!ftp_putcmd(ftp, \"RMD\", sizeof(\"RMD\")-1, dir, dir_len)) {\n\t\treturn 0;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp != 250) {\n\t\treturn 0;\n\t}\n\treturn 1;\n}\n/* }}} */\n\n/* {{{ ftp_chmod */\nint\nftp_chmod(ftpbuf_t *ftp, const int mode, const char *filename, const int filename_len)\n{\n\tchar *buffer;\n\tsize_t buffer_len;\n\n\tif (ftp == NULL || filename_len <= 0) {\n\t\treturn 0;\n\t}\n\n\tbuffer_len = spprintf(&buffer, 0, \"CHMOD %o %s\", mode, filename);\n\n\tif (!buffer) {\n\t\treturn 0;\n\t}\n\n\tif (!ftp_putcmd(ftp, \"SITE\", sizeof(\"SITE\")-1, buffer, buffer_len)) {\n\t\tefree(buffer);\n\t\treturn 0;\n\t}\n\n\tefree(buffer);\n\n\tif (!ftp_getresp(ftp) || ftp->resp != 200) {\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n/* }}} */\n\n/* {{{ ftp_alloc */\nint\nftp_alloc(ftpbuf_t *ftp, const zend_long size, zend_string **response)\n{\n\tchar buffer[64];\n\tint buffer_len;\n\n\tif (ftp == NULL || size <= 0) {\n\t\treturn 0;\n\t}\n\n\tbuffer_len = snprintf(buffer, sizeof(buffer) - 1, ZEND_LONG_FMT, size);\n\n\tif (buffer_len < 0) {\n\t\treturn 0;\n\t}\n\n\tif (!ftp_putcmd(ftp, \"ALLO\", sizeof(\"ALLO\")-1, buffer, buffer_len)) {\n\t\treturn 0;\n\t}\n\n\tif (!ftp_getresp(ftp)) {\n\t\treturn 0;\n\t}\n\n\tif (response) {\n\t\t*response = zend_string_init(ftp->inbuf, strlen(ftp->inbuf), 0);\n\t}\n\n\tif (ftp->resp < 200 || ftp->resp >= 300) {\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n/* }}} */\n\n/* {{{ ftp_nlist */\nchar**\nftp_nlist(ftpbuf_t *ftp, const char *path, const size_t path_len)\n{\n\treturn ftp_genlist(ftp, \"NLST\", sizeof(\"NLST\")-1, path, path_len);\n}\n/* }}} */\n\n/* {{{ ftp_list */\nchar**\nftp_list(ftpbuf_t *ftp, const char *path, const size_t path_len, int recursive)\n{\n\treturn ftp_genlist(ftp, ((recursive) ? \"LIST -R\" : \"LIST\"), ((recursive) ? sizeof(\"LIST -R\")-1 : sizeof(\"LIST\")-1), path, path_len);\n}\n/* }}} */\n\n/* {{{ ftp_mlsd */\nchar**\nftp_mlsd(ftpbuf_t *ftp, const char *path, const size_t path_len)\n{\n\treturn ftp_genlist(ftp, \"MLSD\", sizeof(\"MLSD\")-1, path, path_len);\n}\n/* }}} */\n\n/* {{{ ftp_mlsd_parse_line */\nint\nftp_mlsd_parse_line(HashTable *ht, const char *input) {\n\n\tzval zstr;\n\tconst char *end = input + strlen(input);\n\n\tconst char *sp = memchr(input, ' ', end - input);\n\tif (!sp) {\n\t\tphp_error_docref(NULL, E_WARNING, \"Missing pathname in MLSD response\");\n\t\treturn FAILURE;\n\t}\n\n\t/* Extract pathname */\n\tZVAL_STRINGL(&zstr, sp + 1, end - sp - 1);\n\tzend_hash_update(ht, ZSTR_KNOWN(ZEND_STR_NAME), &zstr);\n\tend = sp;\n\n\twhile (input < end) {\n\t\tconst char *semi, *eq;\n\n\t\t/* Find end of fact */\n\t\tsemi = memchr(input, ';', end - input);\n\t\tif (!semi) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"Malformed fact in MLSD response\");\n\t\t\treturn FAILURE;\n\t\t}\n\n\t\t/* Separate fact key and value */\n\t\teq = memchr(input, '=', semi - input);\n\t\tif (!eq) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"Malformed fact in MLSD response\");\n\t\t\treturn FAILURE;\n\t\t}\n\n\t\tZVAL_STRINGL(&zstr, eq + 1, semi - eq - 1);\n\t\tzend_hash_str_update(ht, input, eq - input, &zstr);\n\t\tinput = semi + 1;\n\t}\n\n\treturn SUCCESS;\n}\n/* }}} */\n\n/* {{{ ftp_type */\nint\nftp_type(ftpbuf_t *ftp, ftptype_t type)\n{\n\tconst char *typechar;\n\n\tif (ftp == NULL) {\n\t\treturn 0;\n\t}\n\tif (type == ftp->type) {\n\t\treturn 1;\n\t}\n\tif (type == FTPTYPE_ASCII) {\n\t\ttypechar = \"A\";\n\t} else if (type == FTPTYPE_IMAGE) {\n\t\ttypechar = \"I\";\n\t} else {\n\t\treturn 0;\n\t}\n\tif (!ftp_putcmd(ftp, \"TYPE\", sizeof(\"TYPE\")-1, typechar, 1)) {\n\t\treturn 0;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp != 200) {\n\t\treturn 0;\n\t}\n\tftp->type = type;\n\n\treturn 1;\n}\n/* }}} */\n\n/* {{{ ftp_pasv */\nint\nftp_pasv(ftpbuf_t *ftp, int pasv)\n{\n\tchar\t\t\t*ptr;\n\tunion ipbox\t\tipbox;\n\tunsigned long\t\tb[6];\n\tsocklen_t\t\t\tn;\n\tstruct sockaddr *sa;\n\tstruct sockaddr_in *sin;\n\n\tif (ftp == NULL) {\n\t\treturn 0;\n\t}\n\tif (pasv && ftp->pasv == 2) {\n\t\treturn 1;\n\t}\n\tftp->pasv = 0;\n\tif (!pasv) {\n\t\treturn 1;\n\t}\n\tn = sizeof(ftp->pasvaddr);\n\tmemset(&ftp->pasvaddr, 0, n);\n\tsa = (struct sockaddr *) &ftp->pasvaddr;\n\n\tif (getpeername(ftp->fd, sa, &n) < 0) {\n\t\treturn 0;\n\t}\n\n#ifdef HAVE_IPV6\n\tif (sa->sa_family == AF_INET6) {\n\t\tstruct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;\n\t\tchar *endptr, delimiter;\n\n\t\t/* try EPSV first */\n\t\tif (!ftp_putcmd(ftp, \"EPSV\", sizeof(\"EPSV\")-1, NULL, (size_t) 0)) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (!ftp_getresp(ftp)) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (ftp->resp == 229) {\n\t\t\t/* parse out the port */\n\t\t\tfor (ptr = ftp->inbuf; *ptr && *ptr != '('; ptr++);\n\t\t\tif (!*ptr) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tdelimiter = *++ptr;\n\t\t\tfor (n = 0; *ptr && n < 3; ptr++) {\n\t\t\t\tif (*ptr == delimiter) {\n\t\t\t\t\tn++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsin6->sin6_port = htons((unsigned short) strtoul(ptr, &endptr, 10));\n\t\t\tif (ptr == endptr || *endptr != delimiter) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tftp->pasv = 2;\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\t/* fall back to PASV */\n#endif\n\n\tif (!ftp_putcmd(ftp, \"PASV\",  sizeof(\"PASV\")-1, NULL, (size_t) 0)) {\n\t\treturn 0;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp != 227) {\n\t\treturn 0;\n\t}\n\t/* parse out the IP and port */\n\tfor (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);\n\tn = sscanf(ptr, \"%lu,%lu,%lu,%lu,%lu,%lu\", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]);\n\tif (n != 6) {\n\t\treturn 0;\n\t}\n\tfor (n = 0; n < 6; n++) {\n\t\tipbox.c[n] = (unsigned char) b[n];\n\t}\n\tsin = (struct sockaddr_in *) sa;\n\tif (ftp->usepasvaddress) {\n\t\tsin->sin_addr = ipbox.ia[0];\n\t}\n\tsin->sin_port = ipbox.s[2];\n\n\tftp->pasv = 2;\n\n\treturn 1;\n}\n/* }}} */\n\n/* {{{ ftp_get */\nint\nftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, const size_t path_len, ftptype_t type, zend_long resumepos)\n{\n\tdatabuf_t\t\t*data = NULL;\n\tsize_t\t\t\trcvd;\n\tchar\t\t\targ[MAX_LENGTH_OF_LONG];\n\n\tif (ftp == NULL) {\n\t\treturn 0;\n\t}\n\tif (!ftp_type(ftp, type)) {\n\t\tgoto bail;\n\t}\n\n\tif ((data = ftp_getdata(ftp)) == NULL) {\n\t\tgoto bail;\n\t}\n\n\tif (resumepos > 0) {\n\t\tint arg_len = snprintf(arg, sizeof(arg), ZEND_LONG_FMT, resumepos);\n\n\t\tif (arg_len < 0) {\n\t\t\tgoto bail;\n\t\t}\n\t\tif (!ftp_putcmd(ftp, \"REST\", sizeof(\"REST\")-1, arg, arg_len)) {\n\t\t\tgoto bail;\n\t\t}\n\t\tif (!ftp_getresp(ftp) || (ftp->resp != 350)) {\n\t\t\tgoto bail;\n\t\t}\n\t}\n\n\tif (!ftp_putcmd(ftp, \"RETR\", sizeof(\"RETR\")-1, path, path_len)) {\n\t\tgoto bail;\n\t}\n\tif (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {\n\t\tgoto bail;\n\t}\n\n\tif ((data = data_accept(data, ftp)) == NULL) {\n\t\tgoto bail;\n\t}\n\n\twhile ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {\n\t\tif (rcvd == (size_t)-1) {\n\t\t\tgoto bail;\n\t\t}\n\n\t\tif (type == FTPTYPE_ASCII) {\n#ifndef PHP_WIN32\n\t\t\tchar *s;\n#endif\n\t\t\tchar *ptr = data->buf;\n\t\t\tchar *e = ptr + rcvd;\n\t\t\t/* logic depends on the OS EOL\n\t\t\t * Win32 -> \\r\\n\n\t\t\t * Everything Else \\n\n\t\t\t */\n#ifdef PHP_WIN32\n\t\t\tphp_stream_write(outstream, ptr, (e - ptr));\n\t\t\tptr = e;\n#else\n\t\t\twhile (e > ptr && (s = memchr(ptr, '\\r', (e - ptr)))) {\n\t\t\t\tphp_stream_write(outstream, ptr, (s - ptr));\n\t\t\t\tif (*(s + 1) == '\\n') {\n\t\t\t\t\ts++;\n\t\t\t\t\tphp_stream_putc(outstream, '\\n');\n\t\t\t\t}\n\t\t\t\tptr = s + 1;\n\t\t\t}\n#endif\n\t\t\tif (ptr < e) {\n\t\t\t\tphp_stream_write(outstream, ptr, (e - ptr));\n\t\t\t}\n\t\t} else if (rcvd != php_stream_write(outstream, data->buf, rcvd)) {\n\t\t\tgoto bail;\n\t\t}\n\t}\n\n\tdata_close(ftp);\n\n\tif (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {\n\t\tgoto bail;\n\t}\n\n\treturn 1;\nbail:\n\tdata_close(ftp);\n\treturn 0;\n}\n/* }}} */\n\nstatic zend_result ftp_send_stream_to_data_socket(ftpbuf_t *ftp, databuf_t *data, php_stream *instream, ftptype_t type, bool send_once_and_return)\n{\n\tif (type == FTPTYPE_ASCII) {\n\t\t/* Change (and later restore) flags to make sure php_stream_get_line() searches '\\n'. */\n\t\tconst uint32_t flags_mask = PHP_STREAM_FLAG_EOL_UNIX | PHP_STREAM_FLAG_DETECT_EOL | PHP_STREAM_FLAG_EOL_MAC;\n\t\tuint32_t old_flags = instream->flags & flags_mask;\n\t\tinstream->flags = (instream->flags & ~flags_mask) | PHP_STREAM_FLAG_EOL_UNIX;\n\n\t\tchar *ptr = data->buf;\n\t\tconst char *end = data->buf + FTP_BUFSIZE;\n\t\twhile (!php_stream_eof(instream)) {\n\t\t\tsize_t line_length;\n\t\t\tif (!php_stream_get_line(instream, ptr, end - ptr, &line_length)) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tZEND_ASSERT(line_length != 0);\n\n\t\t\tptr += line_length - 1;\n\t\t\t/* Replace \\n with \\r\\n */\n\t\t\tif (*ptr == '\\n') {\n\t\t\t\t*ptr = '\\r';\n\t\t\t\t/* The streams layer always puts a \\0 byte at the end of a line,\n\t\t\t\t * so there is always place to add an extra byte. */\n\t\t\t\t*++ptr = '\\n';\n\t\t\t}\n\n\t\t\tptr++;\n\n\t\t\t/* If less than 2 bytes remain, either the buffer is completely full or there is a single byte left to put a '\\0'\n\t\t\t * which isn't really useful, in this case send and reset the buffer. */\n\t\t\tif (end - ptr < 2) {\n\t\t\t\tsize_t send_size = FTP_BUFSIZE - (end - ptr);\n\t\t\t\tif (UNEXPECTED(my_send(ftp, data->fd, data->buf, send_size) != send_size)) {\n\t\t\t\t\tinstream->flags = (instream->flags & ~flags_mask) | old_flags;\n\t\t\t\t\treturn FAILURE;\n\t\t\t\t}\n\t\t\t\tptr = data->buf;\n\t\t\t\tif (send_once_and_return) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tinstream->flags = (instream->flags & ~flags_mask) | old_flags;\n\n\t\tif (end - ptr < FTP_BUFSIZE) {\n\t\t\tsize_t send_size = FTP_BUFSIZE - (end - ptr);\n\t\t\tif (UNEXPECTED(my_send(ftp, data->fd, data->buf, send_size) != send_size)) {\n\t\t\t\treturn FAILURE;\n\t\t\t}\n\t\t}\n\t} else {\n\t\twhile (!php_stream_eof(instream)) {\n\t\t\tssize_t size = php_stream_read(instream, data->buf, FTP_BUFSIZE);\n\t\t\tif (size == 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (UNEXPECTED(size < 0)) {\n\t\t\t\treturn FAILURE;\n\t\t\t}\n\t\t\tif (UNEXPECTED(my_send(ftp, data->fd, data->buf, size) != size)) {\n\t\t\t\treturn FAILURE;\n\t\t\t}\n\t\t\tif (send_once_and_return) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn SUCCESS;\n}\n\n/* {{{ ftp_put */\nint\nftp_put(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *instream, ftptype_t type, zend_long startpos)\n{\n\tdatabuf_t\t\t*data = NULL;\n\tchar\t\t\targ[MAX_LENGTH_OF_LONG];\n\n\tif (ftp == NULL) {\n\t\treturn 0;\n\t}\n\tif (!ftp_type(ftp, type)) {\n\t\tgoto bail;\n\t}\n\tif ((data = ftp_getdata(ftp)) == NULL) {\n\t\tgoto bail;\n\t}\n\n\tif (startpos > 0) {\n\t\tint arg_len = snprintf(arg, sizeof(arg), ZEND_LONG_FMT, startpos);\n\n\t\tif (arg_len < 0) {\n\t\t\tgoto bail;\n\t\t}\n\t\tif (!ftp_putcmd(ftp, \"REST\", sizeof(\"REST\")-1, arg, arg_len)) {\n\t\t\tgoto bail;\n\t\t}\n\t\tif (!ftp_getresp(ftp) || (ftp->resp != 350)) {\n\t\t\tgoto bail;\n\t\t}\n\t}\n\n\tif (!ftp_putcmd(ftp, \"STOR\", sizeof(\"STOR\")-1, path, path_len)) {\n\t\tgoto bail;\n\t}\n\tif (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {\n\t\tgoto bail;\n\t}\n\tif ((data = data_accept(data, ftp)) == NULL) {\n\t\tgoto bail;\n\t}\n\n\tif (ftp_send_stream_to_data_socket(ftp, data, instream, type, false) != SUCCESS) {\n\t\tgoto bail;\n\t}\n\n\tdata_close(ftp);\n\n\tif (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250 && ftp->resp != 200)) {\n\t\tgoto bail;\n\t}\n\treturn 1;\nbail:\n\tdata_close(ftp);\n\treturn 0;\n}\n/* }}} */\n\n\n/* {{{ ftp_append */\nint\nftp_append(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *instream, ftptype_t type)\n{\n\tdatabuf_t\t\t*data = NULL;\n\n\tif (ftp == NULL) {\n\t\treturn 0;\n\t}\n\tif (!ftp_type(ftp, type)) {\n\t\tgoto bail;\n\t}\n\tif ((data = ftp_getdata(ftp)) == NULL) {\n\t\tgoto bail;\n\t}\n\tftp->data = data;\n\n\tif (!ftp_putcmd(ftp, \"APPE\", sizeof(\"APPE\")-1, path, path_len)) {\n\t\tgoto bail;\n\t}\n\tif (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {\n\t\tgoto bail;\n\t}\n\tif ((data = data_accept(data, ftp)) == NULL) {\n\t\tgoto bail;\n\t}\n\n\tif (ftp_send_stream_to_data_socket(ftp, data, instream, type, false) != SUCCESS) {\n\t\tgoto bail;\n\t}\n\n\tdata_close(ftp);\n\n\tif (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250 && ftp->resp != 200)) {\n\t\tgoto bail;\n\t}\n\treturn 1;\nbail:\n\tdata_close(ftp);\n\treturn 0;\n}\n/* }}} */\n\n/* {{{ ftp_size */\nzend_long\nftp_size(ftpbuf_t *ftp, const char *path, const size_t path_len)\n{\n\tif (ftp == NULL) {\n\t\treturn -1;\n\t}\n\tif (!ftp_type(ftp, FTPTYPE_IMAGE)) {\n\t\treturn -1;\n\t}\n\tif (!ftp_putcmd(ftp, \"SIZE\", sizeof(\"SIZE\")-1, path, path_len)) {\n\t\treturn -1;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp != 213) {\n\t\treturn -1;\n\t}\n\treturn ZEND_ATOL(ftp->inbuf);\n}\n/* }}} */\n\n/* {{{ ftp_mdtm */\ntime_t\nftp_mdtm(ftpbuf_t *ftp, const char *path, const size_t path_len)\n{\n\ttime_t\t\tstamp;\n\tstruct tm\t*gmt, tmbuf;\n\tstruct tm\ttm;\n\tchar\t\t*ptr;\n\tint\t\tn;\n\n\tif (ftp == NULL) {\n\t\treturn -1;\n\t}\n\tif (!ftp_putcmd(ftp, \"MDTM\", sizeof(\"MDTM\")-1, path, path_len)) {\n\t\treturn -1;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp != 213) {\n\t\treturn -1;\n\t}\n\t/* parse out the timestamp */\n\tfor (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);\n\tn = sscanf(ptr, \"%4d%2d%2d%2d%2d%2d\", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec);\n\tif (n != 6) {\n\t\treturn -1;\n\t}\n\ttm.tm_year -= 1900;\n\ttm.tm_mon--;\n\ttm.tm_isdst = -1;\n\n\t/* figure out the GMT offset */\n\tstamp = time(NULL);\n\tgmt = php_gmtime_r(&stamp, &tmbuf);\n\tif (!gmt) {\n\t\treturn -1;\n\t}\n\tgmt->tm_isdst = -1;\n\n\t/* apply the GMT offset */\n\ttm.tm_sec += stamp - mktime(gmt);\n\ttm.tm_isdst = gmt->tm_isdst;\n\n\tstamp = mktime(&tm);\n\n\treturn stamp;\n}\n/* }}} */\n\n/* {{{ ftp_delete */\nint\nftp_delete(ftpbuf_t *ftp, const char *path, const size_t path_len)\n{\n\tif (ftp == NULL) {\n\t\treturn 0;\n\t}\n\tif (!ftp_putcmd(ftp, \"DELE\", sizeof(\"DELE\")-1, path, path_len)) {\n\t\treturn 0;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp != 250) {\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n/* }}} */\n\n/* {{{ ftp_rename */\nint\nftp_rename(ftpbuf_t *ftp, const char *src, const size_t src_len, const char *dest, const size_t dest_len)\n{\n\tif (ftp == NULL) {\n\t\treturn 0;\n\t}\n\tif (!ftp_putcmd(ftp, \"RNFR\", sizeof(\"RNFR\")-1, src, src_len)) {\n\t\treturn 0;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp != 350) {\n\t\treturn 0;\n\t}\n\tif (!ftp_putcmd(ftp, \"RNTO\", sizeof(\"RNTO\")-1, dest, dest_len)) {\n\t\treturn 0;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp != 250) {\n\t\treturn 0;\n\t}\n\treturn 1;\n}\n/* }}} */\n\n/* {{{ ftp_site */\nint\nftp_site(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len)\n{\n\tif (ftp == NULL) {\n\t\treturn 0;\n\t}\n\tif (!ftp_putcmd(ftp, \"SITE\", sizeof(\"SITE\")-1, cmd, cmd_len)) {\n\t\treturn 0;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp < 200 || ftp->resp >= 300) {\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n/* }}} */\n\n/* static functions */\n\n/* {{{ ftp_putcmd */\nint\nftp_putcmd(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, const char *args, const size_t args_len)\n{\n\tint\t\tsize;\n\tchar\t\t*data;\n\n\tif (strpbrk(cmd, \"\\r\\n\")) {\n\t\treturn 0;\n\t}\n\t/* build the output buffer */\n\tif (args && args[0]) {\n\t\t/* \"cmd args\\r\\n\\0\" */\n\t\tif (cmd_len + args_len + 4 > FTP_BUFSIZE) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (strpbrk(args, \"\\r\\n\")) {\n\t\t\treturn 0;\n\t\t}\n\t\tsize = slprintf(ftp->outbuf, sizeof(ftp->outbuf), \"%s %s\\r\\n\", cmd, args);\n\t} else {\n\t\t/* \"cmd\\r\\n\\0\" */\n\t\tif (cmd_len + 3 > FTP_BUFSIZE) {\n\t\t\treturn 0;\n\t\t}\n\t\tsize = slprintf(ftp->outbuf, sizeof(ftp->outbuf), \"%s\\r\\n\", cmd);\n\t}\n\n\tdata = ftp->outbuf;\n\n\t/* Clear the inbuf and extra-lines buffer */\n\tftp->inbuf[0] = '\\0';\n\tftp->extra = NULL;\n\n\tif (my_send(ftp, ftp->fd, data, size) != size) {\n\t\treturn 0;\n\t}\n\treturn 1;\n}\n/* }}} */\n\n/* {{{ ftp_readline */\nint\nftp_readline(ftpbuf_t *ftp)\n{\n\tlong\t\tsize, rcvd;\n\tchar\t\t*data, *eol;\n\n\t/* shift the extra to the front */\n\tsize = FTP_BUFSIZE;\n\trcvd = 0;\n\tif (ftp->extra) {\n\t\tmemmove(ftp->inbuf, ftp->extra, ftp->extralen);\n\t\trcvd = ftp->extralen;\n\t}\n\n\tdata = ftp->inbuf;\n\n\tdo {\n\t\tsize -= rcvd;\n\t\tfor (eol = data; rcvd; rcvd--, eol++) {\n\t\t\tif (*eol == '\\r') {\n\t\t\t\t*eol = 0;\n\t\t\t\tftp->extra = eol + 1;\n\t\t\t\tif (rcvd > 1 && *(eol + 1) == '\\n') {\n\t\t\t\t\tftp->extra++;\n\t\t\t\t\trcvd--;\n\t\t\t\t}\n\t\t\t\tif ((ftp->extralen = --rcvd) == 0) {\n\t\t\t\t\tftp->extra = NULL;\n\t\t\t\t}\n\t\t\t\treturn 1;\n\t\t\t} else if (*eol == '\\n') {\n\t\t\t\t*eol = 0;\n\t\t\t\tftp->extra = eol + 1;\n\t\t\t\tif ((ftp->extralen = --rcvd) == 0) {\n\t\t\t\t\tftp->extra = NULL;\n\t\t\t\t}\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\n\t\tdata = eol;\n\t\tif ((rcvd = my_recv(ftp, ftp->fd, data, size)) < 1) {\n\t\t\t*data = 0;\n\t\t\treturn 0;\n\t\t}\n\t} while (size);\n\n\t*data = 0;\n\treturn 0;\n}\n/* }}} */\n\n/* {{{ ftp_getresp */\nint\nftp_getresp(ftpbuf_t *ftp)\n{\n\tif (ftp == NULL) {\n\t\treturn 0;\n\t}\n\tftp->resp = 0;\n\n\twhile (1) {\n\n\t\tif (!ftp_readline(ftp)) {\n\t\t\treturn 0;\n\t\t}\n\n\t\t/* Break out when the end-tag is found */\n\t\tif (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* translate the tag */\n\tif (!isdigit(ftp->inbuf[0]) || !isdigit(ftp->inbuf[1]) || !isdigit(ftp->inbuf[2])) {\n\t\treturn 0;\n\t}\n\n\tftp->resp = 100 * (ftp->inbuf[0] - '0') + 10 * (ftp->inbuf[1] - '0') + (ftp->inbuf[2] - '0');\n\n\tmemmove(ftp->inbuf, ftp->inbuf + 4, FTP_BUFSIZE - 4);\n\n\tif (ftp->extra) {\n\t\tftp->extra -= 4;\n\t}\n\treturn 1;\n}\n/* }}} */\n\nstatic ssize_t my_send_wrapper_with_restart(php_socket_t fd, const void *buf, size_t size, int flags) {\n\tssize_t n;\n\tdo {\n\t\tn = send(fd, buf, size, flags);\n\t} while (n == -1 && php_socket_errno() == EINTR);\n\treturn n;\n}\n\nstatic ssize_t my_recv_wrapper_with_restart(php_socket_t fd, void *buf, size_t size, int flags) {\n\tssize_t n;\n\tdo {\n\t\tn = recv(fd, buf, size, flags);\n\t} while (n == -1 && php_socket_errno() == EINTR);\n\treturn n;\n}\n\nint single_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t size) {\n#ifdef SW_HAVE_FTP_SSL\n\tint err;\n\tbool retry = 0;\n\tSSL *handle = NULL;\n\tphp_socket_t fd;\n\tsize_t sent;\n\n\tif (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {\n\t\thandle = ftp->ssl_handle;\n\t\tfd = ftp->fd;\n\t} else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {\n\t\thandle = ftp->data->ssl_handle;\n\t\tfd = ftp->data->fd;\n\t} else {\n\t\treturn my_send_wrapper_with_restart(s, buf, size, 0);\n\t}\n\n\tdo {\n\t\tsent = SSL_write(handle, buf, size);\n\t\terr = SSL_get_error(handle, sent);\n\n\t\tswitch (err) {\n\t\t\tcase SSL_ERROR_NONE:\n\t\t\t\tretry = 0;\n\t\t\t\tbreak;\n\n\t\t\tcase SSL_ERROR_ZERO_RETURN:\n\t\t\t\tretry = 0;\n\t\t\t\tSSL_shutdown(handle);\n\t\t\t\tbreak;\n\n\t\t\tcase SSL_ERROR_WANT_READ:\n\t\t\tcase SSL_ERROR_WANT_CONNECT: {\n\t\t\t\t\tphp_pollfd p;\n\t\t\t\t\tint i;\n\n\t\t\t\t\tp.fd = fd;\n\t\t\t\t\tp.events = POLLOUT;\n\t\t\t\t\tp.revents = 0;\n\n\t\t\t\t\ti = php_poll2(&p, 1, 300);\n\n\t\t\t\t\tretry = i > 0;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tphp_error_docref(NULL, E_WARNING, \"SSL write failed\");\n\t\t\t\treturn -1;\n\t\t}\n\t} while (retry);\n\treturn sent;\n#else\n\treturn my_send_wrapper_with_restart(s, buf, size, 0);\n#endif\n}\n\n#if PHP_VERSION_ID < 80300\ntypedef uint64_t zend_hrtime_t;\n#define ZEND_NANO_IN_SEC UINT64_C(1000000000)\n\nstatic zend_always_inline zend_hrtime_t zend_hrtime(void) {\n\tstruct timespec ts = { .tv_sec = 0, .tv_nsec = 0 };\n\tif (EXPECTED(0 == clock_gettime(CLOCK_MONOTONIC, &ts))) {\n\t\treturn ((zend_hrtime_t) ts.tv_sec * (zend_hrtime_t)ZEND_NANO_IN_SEC) + ts.tv_nsec;\n\t}\n\treturn 0;\n}\n#endif\n\nstatic int my_poll(php_socket_t fd, int events, int timeout) {\n\tint n;\n\tzend_hrtime_t timeout_hr = (zend_hrtime_t) timeout * 1000000;\n\n\twhile (true) {\n\t\tzend_hrtime_t start_ns = zend_hrtime();\n\t\tn = php_async_socket_poll(fd, events, (int) (timeout_hr / 1000000));\n\n\t\tif (n == -1 && php_socket_errno() == EINTR) {\n\t\t\tzend_hrtime_t delta_ns = zend_hrtime() - start_ns;\n\t\t\t/* delta_ns == 0 is only possible with a platform that does not support a high-res timer. */\n\t\t\tif (delta_ns > timeout_hr || UNEXPECTED(delta_ns == 0)) {\n#ifndef PHP_WIN32\n\t\t\t\terrno = ETIMEDOUT;\n#endif\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttimeout_hr -= delta_ns;\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn n;\n}\n\n/* {{{ my_send */\nint\nmy_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)\n{\n\tzend_long size, sent;\n\tint       n;\n\n\tsize = len;\n\twhile (size) {\n\t\tn = my_poll(s, POLLOUT, ftp->timeout_sec * 1000);\n\n\t\tif (n < 1) {\n\t\t\tchar buf[256];\n\t\t\tif (n == 0) {\n#ifdef PHP_WIN32\n\t\t\t\t_set_errno(ETIMEDOUT);\n#else\n\t\t\t\terrno = ETIMEDOUT;\n#endif\n\t\t\t}\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", php_socket_strerror(errno, buf, sizeof buf));\n\t\t\treturn -1;\n\t\t}\n\n\t\tsent = single_send(ftp, s, buf, size);\n\t\tif (sent == -1) {\n\t\t\treturn -1;\n\t\t}\n\n\t\tbuf = (char*) buf + sent;\n\t\tsize -= sent;\n\t}\n\n\treturn len;\n}\n/* }}} */\n\n/* {{{ my_recv */\nint\nmy_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)\n{\n\tint\t\tn, nr_bytes;\n#ifdef SW_HAVE_FTP_SSL\n\tint err;\n\tbool retry = 0;\n\tSSL *handle = NULL;\n\tphp_socket_t fd;\n#endif\n\tn = my_poll(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);\n\tif (n < 1) {\n\t\tchar buf[256];\n\t\tif (n == 0) {\n#ifdef PHP_WIN32\n\t\t\t_set_errno(ETIMEDOUT);\n#else\n\t\t\terrno = ETIMEDOUT;\n#endif\n\t\t}\n\t\tphp_error_docref(NULL, E_WARNING, \"%s\", php_socket_strerror(errno, buf, sizeof buf));\n\t\treturn -1;\n\t}\n\n#ifdef SW_HAVE_FTP_SSL\n\tif (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {\n\t\thandle = ftp->ssl_handle;\n\t\tfd = ftp->fd;\n\t} else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {\n\t\thandle = ftp->data->ssl_handle;\n\t\tfd = ftp->data->fd;\n\t}\n\n\tif (handle) {\n\t\tdo {\n\t\t\tnr_bytes = SSL_read(handle, buf, len);\n\t\t\terr = SSL_get_error(handle, nr_bytes);\n\n\t\t\tswitch (err) {\n\t\t\t\tcase SSL_ERROR_NONE:\n\t\t\t\t\tretry = 0;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase SSL_ERROR_ZERO_RETURN:\n\t\t\t\t\tretry = 0;\n\t\t\t\t\tSSL_shutdown(handle);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase SSL_ERROR_WANT_READ:\n\t\t\t\tcase SSL_ERROR_WANT_CONNECT: {\n\t\t\t\t\tint i = my_poll(fd, POLLIN, ftp->timeout_sec * 1000);\n\t\t\t\t\tretry = i > 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase SSL_ERROR_WANT_WRITE: {\n\t\t\t\t\tint i = my_poll(fd, POLLOUT, ftp->timeout_sec * 1000);\n\t\t\t\t\tretry = i > 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tphp_error_docref(NULL, E_WARNING, \"SSL read failed\");\n\t\t\t\t\treturn -1;\n\t\t\t}\n\t\t} while (retry);\n\t} else {\n#endif\n\t\tnr_bytes = my_recv_wrapper_with_restart(s, buf, len, 0);\n#ifdef SW_HAVE_FTP_SSL\n\t}\n#endif\n\treturn (nr_bytes);\n}\n/* }}} */\n\n/* {{{ data_available */\nint\ndata_available(ftpbuf_t *ftp, php_socket_t s)\n{\n\tint\t\tn;\n\n\tn = my_poll(s, PHP_POLLREADABLE, 1000);\n\tif (n < 1) {\n\t\tchar buf[256];\n\t\tif (n == 0) {\n#ifdef PHP_WIN32\n\t\t\t_set_errno(ETIMEDOUT);\n#else\n\t\t\terrno = ETIMEDOUT;\n#endif\n\t\t}\n\t\tphp_error_docref(NULL, E_WARNING, \"%s\", php_socket_strerror(errno, buf, sizeof buf));\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n/* }}} */\n/* {{{ data_writeable */\nint\ndata_writeable(ftpbuf_t *ftp, php_socket_t s)\n{\n\tint\t\tn;\n\n\tn = my_poll(s, POLLOUT, 1000);\n\tif (n < 1) {\n\t\tchar buf[256];\n\t\tif (n == 0) {\n#ifdef PHP_WIN32\n\t\t\t_set_errno(ETIMEDOUT);\n#else\n\t\t\terrno = ETIMEDOUT;\n#endif\n\t\t}\n\t\tphp_error_docref(NULL, E_WARNING, \"%s\", php_socket_strerror(errno, buf, sizeof buf));\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n/* }}} */\n\n/* {{{ my_accept */\nint\nmy_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen)\n{\n\tint\t\tn;\n\n\tn = my_poll(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);\n\tif (n < 1) {\n\t\tchar buf[256];\n\t\tif (n == 0) {\n#ifdef PHP_WIN32\n\t\t\t_set_errno(ETIMEDOUT);\n#else\n\t\t\terrno = ETIMEDOUT;\n#endif\n\t\t}\n\t\tphp_error_docref(NULL, E_WARNING, \"%s\", php_socket_strerror(errno, buf, sizeof buf));\n\t\treturn -1;\n\t}\n\n\treturn accept(s, addr, addrlen);\n}\n/* }}} */\n\n/* {{{ ftp_getdata */\ndatabuf_t*\nftp_getdata(ftpbuf_t *ftp)\n{\n\tint\t\t\t\tfd = -1;\n\tdatabuf_t\t\t*data;\n\tphp_sockaddr_storage addr;\n\tstruct sockaddr *sa;\n\tsocklen_t\t\tsize;\n\tunion ipbox\t\tipbox;\n\tchar\t\t\targ[sizeof(\"255, 255, 255, 255, 255, 255\")];\n\tstruct timeval\ttv;\n\tint\t\t\t\targ_len;\n\n\n\t/* ask for a passive connection if we need one */\n\tif (ftp->pasv && !ftp_pasv(ftp, 1)) {\n\t\treturn NULL;\n\t}\n\t/* alloc the data structure */\n\tdata = ecalloc(1, sizeof(*data));\n\tdata->listener = -1;\n\tdata->fd = -1;\n\tdata->type = ftp->type;\n\n\tsa = (struct sockaddr *) &ftp->localaddr;\n\t/* bind/listen */\n\tif ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == SOCK_ERR) {\n\t\tphp_error_docref(NULL, E_WARNING, \"socket() failed: %s (%d)\", strerror(errno), errno);\n\t\tgoto bail;\n\t}\n\n\t/* passive connection handler */\n\tif (ftp->pasv) {\n\t\t/* clear the ready status */\n\t\tftp->pasv = 1;\n\n\t\t/* connect */\n\t\t/* Win 95/98 seems not to like size > sizeof(sockaddr_in) */\n\t\tsize = php_sockaddr_size(&ftp->pasvaddr);\n\t\ttv.tv_sec = ftp->timeout_sec;\n\t\ttv.tv_usec = 0;\n\t\tif (php_connect_nonb(fd, (struct sockaddr*) &ftp->pasvaddr, size, &tv) == -1) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"php_connect_nonb() failed: %s (%d)\", strerror(errno), errno);\n\t\t\tgoto bail;\n\t\t}\n\n\t\tdata->fd = fd;\n\n\t\tftp->data = data;\n\t\treturn data;\n\t}\n\n\n\t/* active (normal) connection */\n\n\t/* bind to a local address */\n\tphp_any_addr(sa->sa_family, &addr, 0);\n\tsize = php_sockaddr_size(&addr);\n\n\tif (bind(fd, (struct sockaddr*) &addr, size) != 0) {\n\t\tphp_error_docref(NULL, E_WARNING, \"bind() failed: %s (%d)\", strerror(errno), errno);\n\t\tgoto bail;\n\t}\n\n\tif (getsockname(fd, (struct sockaddr*) &addr, &size) != 0) {\n\t\tphp_error_docref(NULL, E_WARNING, \"getsockname() failed: %s (%d)\", strerror(errno), errno);\n\t\tgoto bail;\n\t}\n\n\tif (listen(fd, 5) != 0) {\n\t\tphp_error_docref(NULL, E_WARNING, \"listen() failed: %s (%d)\", strerror(errno), errno);\n\t\tgoto bail;\n\t}\n\n\tdata->listener = fd;\n\n#ifdef HAVE_IPV6\n\tif (sa->sa_family == AF_INET6) {\n\t\t/* need to use EPRT */\n\t\tchar eprtarg[INET6_ADDRSTRLEN + sizeof(\"|x||xxxxx|\")];\n\t\tchar out[INET6_ADDRSTRLEN];\n\t\tint eprtarg_len;\n\t\tconst char *r;\n\t\tr = inet_ntop(AF_INET6, &((struct sockaddr_in6*) sa)->sin6_addr, out, sizeof(out));\n\t\tZEND_ASSERT(r != NULL);\n\n\t\teprtarg_len = snprintf(eprtarg, sizeof(eprtarg), \"|2|%s|%hu|\", out, ntohs(((struct sockaddr_in6 *) &addr)->sin6_port));\n\n\t\tif (eprtarg_len < 0) {\n\t\t\tgoto bail;\n\t\t}\n\n\t\tif (!ftp_putcmd(ftp, \"EPRT\", sizeof(\"EPRT\")-1, eprtarg, eprtarg_len)) {\n\t\t\tgoto bail;\n\t\t}\n\n\t\tif (!ftp_getresp(ftp) || ftp->resp != 200) {\n\t\t\tgoto bail;\n\t\t}\n\n\t\tftp->data = data;\n\t\treturn data;\n\t}\n#endif\n\n\t/* send the PORT */\n\tipbox.ia[0] = ((struct sockaddr_in*) sa)->sin_addr;\n\tipbox.s[2] = ((struct sockaddr_in*) &addr)->sin_port;\n\targ_len = snprintf(arg, sizeof(arg), \"%u,%u,%u,%u,%u,%u\", ipbox.c[0], ipbox.c[1], ipbox.c[2], ipbox.c[3], ipbox.c[4], ipbox.c[5]);\n\n\tif (arg_len < 0) {\n\t\tgoto bail;\n\t}\n\tif (!ftp_putcmd(ftp, \"PORT\", sizeof(\"PORT\")-1, arg, arg_len)) {\n\t\tgoto bail;\n\t}\n\tif (!ftp_getresp(ftp) || ftp->resp != 200) {\n\t\tgoto bail;\n\t}\n\n\tftp->data = data;\n\treturn data;\n\nbail:\n\tif (fd != -1) {\n\t\tclosesocket(fd);\n\t}\n\tefree(data);\n\treturn NULL;\n}\n/* }}} */\n\n/* {{{ data_accept */\ndatabuf_t*\ndata_accept(databuf_t *data, ftpbuf_t *ftp)\n{\n\tphp_sockaddr_storage addr;\n\tsocklen_t\t\t\tsize;\n\n#ifdef SW_HAVE_FTP_SSL\n\tSSL_CTX\t\t*ctx;\n\tSSL_SESSION *session;\n\tint err, res;\n\tbool retry;\n#endif\n\n\tif (data->fd != -1) {\n\t\tgoto data_accepted;\n\t}\n\tsize = sizeof(addr);\n\tdata->fd = my_accept(ftp, data->listener, (struct sockaddr*) &addr, &size);\n\tclosesocket(data->listener);\n\tdata->listener = -1;\n\n\tif (data->fd == -1) {\n\t\tefree(data);\n\t\treturn NULL;\n\t}\n\ndata_accepted:\n#ifdef SW_HAVE_FTP_SSL\n\n\t/* now enable ssl if we need to */\n\tif (ftp->use_ssl && ftp->use_ssl_for_data) {\n\t\tctx = SSL_get_SSL_CTX(ftp->ssl_handle);\n\t\tif (ctx == NULL) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"data_accept: failed to retrieve the existing SSL context\");\n\t\t\treturn 0;\n\t\t}\n\n\t\tdata->ssl_handle = SSL_new(ctx);\n\t\tif (data->ssl_handle == NULL) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"data_accept: failed to create the SSL handle\");\n\t\t\treturn 0;\n\t\t}\n\n\t\tSSL_set_fd(data->ssl_handle, data->fd);\n\n\t\tif (ftp->old_ssl) {\n\t\t\tSSL_copy_session_id(data->ssl_handle, ftp->ssl_handle);\n\t\t}\n\n\t\t/* get the session from the control connection so we can re-use it */\n\t\tsession = ftp->last_ssl_session;\n\t\tif (session == NULL) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"data_accept: failed to retrieve the existing SSL session\");\n\t\t\tSSL_free(data->ssl_handle);\n\t\t\treturn 0;\n\t\t}\n\n\t\t/* and set it on the data connection */\n\t\tSSL_set_app_data(data->ssl_handle, ftp); /* Needed for ftp_ssl_new_session_cb */\n\t\tres = SSL_set_session(data->ssl_handle, session);\n\t\tif (res == 0) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"data_accept: failed to set the existing SSL session\");\n\t\t\tSSL_free(data->ssl_handle);\n\t\t\treturn 0;\n\t\t}\n\n\t\tdo {\n\t\t\tres = SSL_connect(data->ssl_handle);\n\t\t\terr = SSL_get_error(data->ssl_handle, res);\n\n\t\t\tswitch (err) {\n\t\t\t\tcase SSL_ERROR_NONE:\n\t\t\t\t\tretry = 0;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase SSL_ERROR_ZERO_RETURN:\n\t\t\t\t\tretry = 0;\n\t\t\t\t\tSSL_shutdown(data->ssl_handle);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase SSL_ERROR_WANT_READ:\n\t\t\t\tcase SSL_ERROR_WANT_WRITE: {\n\t\t\t\t\t\tphp_pollfd p;\n\t\t\t\t\t\tint i;\n\n\t\t\t\t\t\tp.fd = data->fd;\n\t\t\t\t\t\tp.events = (err == SSL_ERROR_WANT_READ) ? (POLLIN|POLLPRI) : POLLOUT;\n\t\t\t\t\t\tp.revents = 0;\n\n\t\t\t\t\t\ti = php_poll2(&p, 1, 300);\n\n\t\t\t\t\t\tretry = i > 0;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tphp_error_docref(NULL, E_WARNING, \"data_accept: SSL/TLS handshake failed\");\n\t\t\t\t\tSSL_shutdown(data->ssl_handle);\n\t\t\t\t\tSSL_free(data->ssl_handle);\n\t\t\t\t\treturn 0;\n\t\t\t}\n\t\t} while (retry);\n\n\t\tdata->ssl_active = 1;\n\t}\n\n#endif\n\n\treturn data;\n}\n/* }}} */\n\n/* {{{ ftp_ssl_shutdown */\n#ifdef SW_HAVE_FTP_SSL\nstatic void ftp_ssl_shutdown(ftpbuf_t *ftp, php_socket_t fd, SSL *ssl_handle) {\n\t/* In TLS 1.3 it's common to receive session tickets after the handshake has completed. We need to train\n\t   the socket (read the tickets until EOF/close_notify alert) before closing the socket. Otherwise the\n\t   server might get an ECONNRESET which might lead to data truncation on server side.\n\t*/\n\tchar buf[256]; /* We will use this for the OpenSSL error buffer, so it has\n\t\t\t  to be at least 256 bytes long.*/\n\tint done = 1, err, nread;\n\tunsigned long sslerror;\n\n\terr = SSL_shutdown(ssl_handle);\n\tif (err < 0) {\n\t\tphp_error_docref(NULL, E_WARNING, \"SSL_shutdown failed\");\n\t}\n\telse if (err == 0) {\n\t\t/* The shutdown is not yet finished. Call SSL_read() to do a bidirectional shutdown. */\n\t\tdone = 0;\n\t}\n\n\twhile (!done && data_available(ftp, fd)) {\n\t\tERR_clear_error();\n\t\tnread = SSL_read(ssl_handle, buf, sizeof(buf));\n\t\tif (nread <= 0) {\n\t\t\terr = SSL_get_error(ssl_handle, nread);\n\t\t\tswitch (err) {\n\t\t\t\tcase SSL_ERROR_NONE: /* this is not an error */\n\t\t\t\tcase SSL_ERROR_ZERO_RETURN: /* no more data */\n\t\t\t\t\t/* This is the expected response. There was no data but only\n\t\t\t\t\t   the close notify alert */\n\t\t\t\t\tdone = 1;\n\t\t\t\t\tbreak;\n\t\t\t\tcase SSL_ERROR_WANT_READ:\n\t\t\t\t\t/* there's data pending, re-invoke SSL_read() */\n\t\t\t\t\tbreak;\n\t\t\t\tcase SSL_ERROR_WANT_WRITE:\n\t\t\t\t\t/* SSL wants a write. Really odd. Let's bail out. */\n\t\t\t\t\tdone = 1;\n\t\t\t\t\tbreak;\n\t\t\t\tcase SSL_ERROR_SYSCALL:\n\t\t\t\t\t/* most likely the peer closed the connection without\n\t\t\t\t\t   sending a close_notify shutdown alert;\n\t\t\t\t\t   bail out to avoid raising a spurious warning */\n\t\t\t\t\tdone = 1;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tif ((sslerror = ERR_get_error())) {\n\t\t\t\t\t\tERR_error_string_n(sslerror, buf, sizeof(buf));\n\t\t\t\t\t\tphp_error_docref(NULL, E_WARNING, \"SSL_read on shutdown: %s\", buf);\n\t\t\t\t\t} else if (errno) {\n\t\t\t\t\t\tphp_error_docref(NULL, E_WARNING, \"SSL_read on shutdown: %s (%d)\", strerror(errno), errno);\n\t\t\t\t\t}\n\t\t\t\t\tdone = 1;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\t(void)SSL_free(ssl_handle);\n}\n#endif\n/* }}} */\n\n/* {{{ data_close */\nvoid data_close(ftpbuf_t *ftp)\n{\n\tZEND_ASSERT(ftp != NULL);\n\tdatabuf_t *data = ftp->data;\n\tif (data == NULL) {\n\t\treturn;\n\t}\n\tif (data->listener != -1) {\n#ifdef SW_HAVE_FTP_SSL\n\t\tif (data->ssl_active) {\n\t\t\t/* don't free the data context, it's the same as the control */\n\t\t\tftp_ssl_shutdown(ftp, data->listener, data->ssl_handle);\n\t\t\tdata->ssl_active = 0;\n\t\t}\n#endif\n\t\tclosesocket(data->listener);\n\t}\n\tif (data->fd != -1) {\n#ifdef SW_HAVE_FTP_SSL\n\t\tif (data->ssl_active) {\n\t\t\t/* don't free the data context, it's the same as the control */\n\t\t\tftp_ssl_shutdown(ftp, data->fd, data->ssl_handle);\n\t\t\tdata->ssl_active = 0;\n\t\t}\n#endif\n\t\tclosesocket(data->fd);\n\t}\n\tftp->data = NULL;\n\tefree(data);\n}\n/* }}} */\n\n/* {{{ ftp_genlist */\nchar**\nftp_genlist(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, const char *path, const size_t path_len)\n{\n\tphp_stream\t*tmpstream = NULL;\n\tdatabuf_t\t*data = NULL;\n\tchar\t\t*ptr;\n\tint\t\tch, lastch;\n\tsize_t\t\tsize, rcvd;\n\tsize_t\t\tlines;\n\tchar\t\t**ret = NULL;\n\tchar\t\t**entry;\n\tchar\t\t*text;\n\n\n\tif ((tmpstream = php_stream_fopen_tmpfile()) == NULL) {\n\t\tphp_error_docref(NULL, E_WARNING, \"Unable to create temporary file.  Check permissions in temporary files directory.\");\n\t\treturn NULL;\n\t}\n\n\tif (!ftp_type(ftp, FTPTYPE_ASCII)) {\n\t\tgoto bail;\n\t}\n\n\tif ((data = ftp_getdata(ftp)) == NULL) {\n\t\tgoto bail;\n\t}\n\tftp->data = data;\n\n\tif (!ftp_putcmd(ftp, cmd, cmd_len, path, path_len)) {\n\t\tgoto bail;\n\t}\n\tif (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125 && ftp->resp != 226)) {\n\t\tgoto bail;\n\t}\n\n\t/* some servers don't open a ftp-data connection if the directory is empty */\n\tif (ftp->resp == 226) {\n\t\tdata_close(ftp);\n\t\tphp_stream_close(tmpstream);\n\t\treturn ecalloc(1, sizeof(char*));\n\t}\n\n\t/* pull data buffer into tmpfile */\n\tif ((data = data_accept(data, ftp)) == NULL) {\n\t\tgoto bail;\n\t}\n\tsize = 0;\n\tlines = 0;\n\tlastch = 0;\n\twhile ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {\n\t\tif (rcvd == (size_t)-1 || rcvd > ((size_t)(-1))-size) {\n\t\t\tgoto bail;\n\t\t}\n\n\t\tphp_stream_write(tmpstream, data->buf, rcvd);\n\n\t\tsize += rcvd;\n\t\tfor (ptr = data->buf; rcvd; rcvd--, ptr++) {\n\t\t\tif (*ptr == '\\n' && lastch == '\\r') {\n\t\t\t\tlines++;\n\t\t\t}\n\t\t\tlastch = *ptr;\n\t\t}\n\t}\n\n\tdata_close(ftp);\n\n\tphp_stream_rewind(tmpstream);\n\n\tret = safe_emalloc((lines + 1), sizeof(char*), size);\n\n\tentry = ret;\n\ttext = (char*) (ret + lines + 1);\n\t*entry = text;\n\tlastch = 0;\n\twhile ((ch = php_stream_getc(tmpstream)) != EOF) {\n\t\tif (ch == '\\n' && lastch == '\\r') {\n\t\t\t*(text - 1) = 0;\n\t\t\t*++entry = text;\n\t\t} else {\n\t\t\t*text++ = ch;\n\t\t}\n\t\tlastch = ch;\n\t}\n\t*entry = NULL;\n\n\tphp_stream_close(tmpstream);\n\n\tif (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {\n\t\tefree(ret);\n\t\treturn NULL;\n\t}\n\n\treturn ret;\nbail:\n\tdata_close(ftp);\n\tphp_stream_close(tmpstream);\n\tif (ret)\n\t\tefree(ret);\n\treturn NULL;\n}\n/* }}} */\n\n/* {{{ ftp_nb_get */\nint\nftp_nb_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, const size_t path_len, ftptype_t type, zend_long resumepos)\n{\n\tdatabuf_t\t\t*data = NULL;\n\tchar\t\t\targ[MAX_LENGTH_OF_LONG];\n\n\tif (ftp == NULL) {\n\t\treturn PHP_FTP_FAILED;\n\t}\n\n\tif (ftp->data != NULL) {\n\t\t/* If there is a transfer in action, abort it.\n\t\t * If we don't, we get an invalid state and memory leaks when the new connection gets opened. */\n\t\tdata_close(ftp);\n\t\tif (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {\n\t\t\tgoto bail;\n\t\t}\n\t}\n\n\tif (!ftp_type(ftp, type)) {\n\t\tgoto bail;\n\t}\n\n\tif ((data = ftp_getdata(ftp)) == NULL) {\n\t\tgoto bail;\n\t}\n\n\tif (resumepos>0) {\n\t\tint arg_len = snprintf(arg, sizeof(arg), ZEND_LONG_FMT, resumepos);\n\n\t\tif (arg_len < 0) {\n\t\t\tgoto bail;\n\t\t}\n\t\tif (!ftp_putcmd(ftp, \"REST\", sizeof(\"REST\")-1, arg, arg_len)) {\n\t\t\tgoto bail;\n\t\t}\n\t\tif (!ftp_getresp(ftp) || (ftp->resp != 350)) {\n\t\t\tgoto bail;\n\t\t}\n\t}\n\n\tif (!ftp_putcmd(ftp, \"RETR\", sizeof(\"RETR\")-1, path, path_len)) {\n\t\tgoto bail;\n\t}\n\tif (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {\n\t\tgoto bail;\n\t}\n\n\tif ((data = data_accept(data, ftp)) == NULL) {\n\t\tgoto bail;\n\t}\n\n\tftp->data = data;\n\tftp->stream = outstream;\n\tftp->lastch = 0;\n\tftp->nb = 1;\n\n\treturn (ftp_nb_continue_read(ftp));\n\nbail:\n\tdata_close(ftp);\n\treturn PHP_FTP_FAILED;\n}\n/* }}} */\n\n/* {{{ ftp_nb_continue_read */\nint\nftp_nb_continue_read(ftpbuf_t *ftp)\n{\n\tdatabuf_t\t*data = NULL;\n\tchar\t\t*ptr;\n\tchar\t\tlastch;\n\tsize_t\t\trcvd;\n\tftptype_t\ttype;\n\n\tdata = ftp->data;\n\n\t/* check if there is already more data */\n\tif (!data_available(ftp, data->fd)) {\n\t\treturn PHP_FTP_MOREDATA;\n\t}\n\n\ttype = ftp->type;\n\n\tlastch = ftp->lastch;\n\tif ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {\n\t\tif (rcvd == (size_t)-1) {\n\t\t\tgoto bail;\n\t\t}\n\n\t\tif (type == FTPTYPE_ASCII) {\n\t\t\tfor (ptr = data->buf; rcvd; rcvd--, ptr++) {\n\t\t\t\tif (lastch == '\\r' && *ptr != '\\n') {\n\t\t\t\t\tphp_stream_putc(ftp->stream, '\\r');\n\t\t\t\t}\n\t\t\t\tif (*ptr != '\\r') {\n\t\t\t\t\tphp_stream_putc(ftp->stream, *ptr);\n\t\t\t\t}\n\t\t\t\tlastch = *ptr;\n\t\t\t}\n\t\t} else if (rcvd != php_stream_write(ftp->stream, data->buf, rcvd)) {\n\t\t\tgoto bail;\n\t\t}\n\n\t\tftp->lastch = lastch;\n\t\treturn PHP_FTP_MOREDATA;\n\t}\n\n\tif (type == FTPTYPE_ASCII && lastch == '\\r') {\n\t\tphp_stream_putc(ftp->stream, '\\r');\n\t}\n\n\tdata_close(ftp);\n\n\tif (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {\n\t\tgoto bail;\n\t}\n\n\tftp->nb = 0;\n\treturn PHP_FTP_FINISHED;\nbail:\n\tftp->nb = 0;\n\tdata_close(ftp);\n\treturn PHP_FTP_FAILED;\n}\n/* }}} */\n\n/* {{{ ftp_nb_put */\nint\nftp_nb_put(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *instream, ftptype_t type, zend_long startpos)\n{\n\tdatabuf_t\t\t*data = NULL;\n\tchar\t\t\targ[MAX_LENGTH_OF_LONG];\n\n\tif (ftp == NULL) {\n\t\treturn 0;\n\t}\n\tif (!ftp_type(ftp, type)) {\n\t\tgoto bail;\n\t}\n\tif ((data = ftp_getdata(ftp)) == NULL) {\n\t\tgoto bail;\n\t}\n\tif (startpos > 0) {\n\t\tint arg_len = snprintf(arg, sizeof(arg), ZEND_LONG_FMT, startpos);\n\n\t\tif (arg_len < 0) {\n\t\t\tgoto bail;\n\t\t}\n\t\tif (!ftp_putcmd(ftp, \"REST\", sizeof(\"REST\")-1, arg, arg_len)) {\n\t\t\tgoto bail;\n\t\t}\n\t\tif (!ftp_getresp(ftp) || (ftp->resp != 350)) {\n\t\t\tgoto bail;\n\t\t}\n\t}\n\n\tif (!ftp_putcmd(ftp, \"STOR\", sizeof(\"STOR\")-1, path, path_len)) {\n\t\tgoto bail;\n\t}\n\tif (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {\n\t\tgoto bail;\n\t}\n\tif ((data = data_accept(data, ftp)) == NULL) {\n\t\tgoto bail;\n\t}\n\tftp->data = data;\n\tftp->stream = instream;\n\tftp->lastch = 0;\n\tftp->nb = 1;\n\n\treturn (ftp_nb_continue_write(ftp));\n\nbail:\n\tdata_close(ftp);\n\treturn PHP_FTP_FAILED;\n}\n/* }}} */\n\n\n/* {{{ ftp_nb_continue_write */\nint\nftp_nb_continue_write(ftpbuf_t *ftp)\n{\n\t/* check if we can write more data */\n\tif (!data_writeable(ftp, ftp->data->fd)) {\n\t\treturn PHP_FTP_MOREDATA;\n\t}\n\n\tif (ftp_send_stream_to_data_socket(ftp, ftp->data, ftp->stream, ftp->type, true) != SUCCESS) {\n\t\tgoto bail;\n\t}\n\n\tif (!php_stream_eof(ftp->stream)) {\n\t\treturn PHP_FTP_MOREDATA;\n\t}\n\n\tdata_close(ftp);\n\n\tif (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {\n\t\tgoto bail;\n\t}\n\tftp->nb = 0;\n\treturn PHP_FTP_FINISHED;\nbail:\n\tdata_close(ftp);\n\tftp->nb = 0;\n\treturn PHP_FTP_FAILED;\n}\n/* }}} */\n"
  },
  {
    "path": "thirdparty/php84/ftp/ftp.h",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | Copyright (c) The PHP Group                                          |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | https://www.php.net/license/3_01.txt                                 |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Authors: Andrew Skalski <askalski@chek.com>                          |\n   |          Stefan Esser <sesser@php.net> (resume functions)            |\n   +----------------------------------------------------------------------+\n */\n\n#ifndef\tFTP_H\n#define\tFTP_H\n\n#ifdef HAVE_CONFIG_H\n#include \"config.h\"\n#endif\n\n#include \"php_network.h\"\n\n#include <stdio.h>\n#ifdef HAVE_NETINET_IN_H\n#include <netinet/in.h>\n#endif\n\n#define\tFTP_DEFAULT_TIMEOUT\t90\n#define FTP_DEFAULT_AUTOSEEK 1\n#define FTP_DEFAULT_USEPASVADDRESS\t1\n#define PHP_FTP_FAILED\t\t\t0\n#define PHP_FTP_FINISHED\t\t1\n#define PHP_FTP_MOREDATA\t\t2\n\n/* XXX this should be configurable at runtime XXX */\n#define\tFTP_BUFSIZE\t4096\n\ntypedef enum ftptype {\n\tFTPTYPE_ASCII=1,\n\tFTPTYPE_IMAGE\n} ftptype_t;\n\ntypedef struct databuf\n{\n\tint\t\tlistener;\t\t/* listener socket */\n\tphp_socket_t\t\tfd;\t\t\t/* data connection */\n\tftptype_t\ttype;\t\t\t/* transfer type */\n\tchar\t\tbuf[FTP_BUFSIZE];\t/* data buffer */\n#ifdef SW_HAVE_FTP_SSL\n\tSSL\t\t*ssl_handle;\t/* ssl handle */\n\tint\t\tssl_active;\t\t/* flag if ssl is active or not */\n#endif\n} databuf_t;\n\ntypedef struct ftpbuf\n{\n\tphp_socket_t\t\tfd;\t\t\t/* control connection */\n\tphp_sockaddr_storage\tlocaladdr;\t/* local address */\n\tint\t\tresp;\t\t\t/* last response code */\n\tchar\t\tinbuf[FTP_BUFSIZE];\t/* last response text */\n\tchar\t\t*extra;\t\t\t/* extra characters */\n\tint\t\textralen;\t\t/* number of extra chars */\n\tchar\t\toutbuf[FTP_BUFSIZE];\t/* command output buffer */\n\tchar\t\t*pwd;\t\t\t/* cached pwd */\n\tchar\t\t*syst;\t\t\t/* cached system type */\n\tftptype_t\ttype;\t\t\t/* current transfer type */\n\tint\t\tpasv;\t\t\t/* 0=off; 1=pasv; 2=ready */\n\tphp_sockaddr_storage\tpasvaddr;\t/* passive mode address */\n\tzend_long\ttimeout_sec;\t/* User configurable timeout (seconds) */\n\tint\t\t\tautoseek;\t/* User configurable autoseek flag */\n\tint\t\t\tusepasvaddress;\t/* Use the address returned by the pasv command */\n\n\tdatabuf_t\t\t*data;\t/* Data connection for \"nonblocking\" transfers */\n\tphp_stream\t\t*stream; /* output stream for \"nonblocking\" transfers */\n\tbool\t\t\tnb;\t\t/* \"nonblocking\" transfer in progress */\n\tchar\t\t\tlastch;\t\t/* last char of previous call */\n\tbool\t\t\tdirection;\t/* recv = 0 / send = 1 */\n\tbool\t\t\tclosestream;/* close or not close stream */\n#ifdef SW_HAVE_FTP_SSL\n\tbool\t\t\tuse_ssl; /* enable(1) or disable(0) ssl */\n\tbool\t\t\tuse_ssl_for_data; /* en/disable ssl for the dataconnection */\n\tbool\t\t\told_ssl;\t/* old mode = forced data encryption */\n\tbool\t\t\tssl_active;\t\t  /* ssl active on control conn */\n\tSSL\t\t\t\t*ssl_handle;      /* handle for control connection */\n\tSSL_SESSION     *last_ssl_session; /* last negotiated session */\n#endif\n\n} ftpbuf_t;\n\n\n\n/* open a FTP connection, returns ftpbuf (NULL on error)\n * port is the ftp port in network byte order, or 0 for the default\n */\nftpbuf_t*\tftp_open(const char *host, short port, zend_long timeout_sec);\n\n/* quits from the ftp session (it still needs to be closed)\n * return true on success, false on error\n */\nint\t\tftp_quit(ftpbuf_t *ftp);\n\n/* frees up any cached data held in the ftp buffer */\nvoid\t\tftp_gc(ftpbuf_t *ftp);\n\n/* close the FTP connection and return NULL */\nftpbuf_t*\tftp_close(ftpbuf_t *ftp);\n\n/* logs into the FTP server, returns true on success, false on error */\nint\t\tftp_login(ftpbuf_t *ftp, const char *user, const size_t user_len, const char *pass, const size_t pass_len);\n\n/* reinitializes the connection, returns true on success, false on error */\nint\t\tftp_reinit(ftpbuf_t *ftp);\n\n/* returns the remote system type (NULL on error) */\nconst char*\tftp_syst(ftpbuf_t *ftp);\n\n/* returns the present working directory (NULL on error) */\nconst char*\tftp_pwd(ftpbuf_t *ftp);\n\n/* exec a command [special features], return true on success, false on error */\nint \tftp_exec(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len);\n\n/* send a raw ftp command, return response as a hashtable, NULL on error */\nvoid\tftp_raw(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, zval *return_value);\n\n/* changes directories, return true on success, false on error */\nint\t\tftp_chdir(ftpbuf_t *ftp, const char *dir, const size_t dir_len);\n\n/* changes to parent directory, return true on success, false on error */\nint\t\tftp_cdup(ftpbuf_t *ftp);\n\n/* creates a directory, return the directory name on success, NULL on error.\n * the return value must be freed\n */\nzend_string* ftp_mkdir(ftpbuf_t *ftp, const char *dir, const size_t dir_len);\n\n/* removes a directory, return true on success, false on error */\nint\t\tftp_rmdir(ftpbuf_t *ftp, const char *dir, const size_t dir_len);\n\n/* Set permissions on a file */\nint\t\tftp_chmod(ftpbuf_t *ftp, const int mode, const char *filename, const int filename_len);\n\n/* Allocate space on remote server with ALLO command\n * Many servers will respond with 202 Allocation not necessary,\n * however some servers will not accept STOR or APPE until ALLO is confirmed.\n * If response is passed, it is estrdup()ed from ftp->inbuf and must be freed\n * or assigned to a zval returned to the user */\nint\t\tftp_alloc(ftpbuf_t *ftp, const zend_long size, zend_string **response);\n\n/* returns a NULL-terminated array of filenames in the given path\n * or NULL on error.  the return array must be freed (but don't\n * free the array elements)\n */\nchar**\t\tftp_nlist(ftpbuf_t *ftp, const char *path, const size_t path_len);\n\n/* returns a NULL-terminated array of lines returned by the ftp\n * LIST command for the given path or NULL on error.  the return\n * array must be freed (but don't\n * free the array elements)\n */\nchar**\t\tftp_list(ftpbuf_t *ftp, const char *path, const size_t path_len, int recursive);\n\n/* populates a hashtable with the facts contained in one line of\n * an MLSD response.\n */\nint\t\t\tftp_mlsd_parse_line(HashTable *ht, const char *input);\n\n/* returns a NULL-terminated array of lines returned by the ftp\n * MLSD command for the given path or NULL on error.  the return\n * array must be freed (but don't\n * free the array elements)\n */\nchar**\t\tftp_mlsd(ftpbuf_t *ftp, const char *path, const size_t path_len);\n\n/* switches passive mode on or off\n * returns true on success, false on error\n */\nint\t\tftp_pasv(ftpbuf_t *ftp, int pasv);\n\n/* retrieves a file and saves its contents to outfp\n * returns true on success, false on error\n */\nint\t\tftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, const size_t path_len, ftptype_t type, zend_long resumepos);\n\n/* stores the data from a file, socket, or process as a file on the remote server\n * returns true on success, false on error\n */\nint\t\tftp_put(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *instream, ftptype_t type, zend_long startpos);\n\n/* append the data from a file, socket, or process as a file on the remote server\n * returns true on success, false on error\n */\nint\t\tftp_append(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *instream, ftptype_t type);\n\n/* returns the size of the given file, or -1 on error */\nzend_long\t\tftp_size(ftpbuf_t *ftp, const char *path, const size_t path_len);\n\n/* returns the last modified time of the given file, or -1 on error */\ntime_t\t\tftp_mdtm(ftpbuf_t *ftp, const char *path, const size_t path_len);\n\n/* renames a file on the server */\nint\t\tftp_rename(ftpbuf_t *ftp, const char *src, const size_t src_len, const char *dest, const size_t dest_len);\n\n/* deletes the file from the server */\nint\t\tftp_delete(ftpbuf_t *ftp, const char *path, const size_t path_len);\n\n/* sends a SITE command to the server */\nint\t\tftp_site(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len);\n\n/* retrieves part of a file and saves its contents to outfp\n * returns true on success, false on error\n */\nint\t\tftp_nb_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, const size_t path_len, ftptype_t type, zend_long resumepos);\n\n/* stores the data from a file, socket, or process as a file on the remote server\n * returns true on success, false on error\n */\nint\t\tftp_nb_put(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *instream, ftptype_t type, zend_long startpos);\n\n/* continues a previous nb_(f)get command\n */\nint\t\tftp_nb_continue_read(ftpbuf_t *ftp);\n\n/* continues a previous nb_(f)put command\n */\nint\t\tftp_nb_continue_write(ftpbuf_t *ftp);\n\n\n#endif\n"
  },
  {
    "path": "thirdparty/php84/ftp/php_ftp.c",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | Copyright (c) The PHP Group                                          |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | https://www.php.net/license/3_01.txt                                 |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Authors: Andrew Skalski <askalski@chek.com>                          |\n   |          Stefan Esser <sesser@php.net> (resume functions)            |\n   +----------------------------------------------------------------------+\n */\n\n#ifdef HAVE_CONFIG_H\n#include <config.h>\n#endif\n\n#include \"php.h\"\n#include \"php_swoole_api.h\"\n\n#ifdef SW_HAVE_FTP_SSL\n# include <openssl/ssl.h>\n#endif\n\n#ifdef SW_HAVE_FTP\n\n#include \"ext/standard/info.h\"\n#include \"ext/standard/file.h\"\n#include \"Zend/zend_attributes.h\"\n#include \"Zend/zend_exceptions.h\"\n\n#include \"php_ftp.h\"\n#include \"ftp.h\"\n\nstatic zend_class_entry *php_ftp_ce = NULL;\nstatic zend_object_handlers ftp_object_handlers;\n\nstatic void register_ftp_symbols(int module_number)\n{\n\tREGISTER_LONG_CONSTANT(\"FTP_ASCII\", FTPTYPE_ASCII, CONST_PERSISTENT);\n\tREGISTER_LONG_CONSTANT(\"FTP_TEXT\", FTPTYPE_ASCII, CONST_PERSISTENT);\n\tREGISTER_LONG_CONSTANT(\"FTP_BINARY\", FTPTYPE_IMAGE, CONST_PERSISTENT);\n\tREGISTER_LONG_CONSTANT(\"FTP_IMAGE\", FTPTYPE_IMAGE, CONST_PERSISTENT);\n\tREGISTER_LONG_CONSTANT(\"FTP_AUTORESUME\", PHP_FTP_AUTORESUME, CONST_PERSISTENT);\n\tREGISTER_LONG_CONSTANT(\"FTP_TIMEOUT_SEC\", PHP_FTP_OPT_TIMEOUT_SEC, CONST_PERSISTENT);\n\tREGISTER_LONG_CONSTANT(\"FTP_AUTOSEEK\", PHP_FTP_OPT_AUTOSEEK, CONST_PERSISTENT);\n\tREGISTER_LONG_CONSTANT(\"FTP_USEPASVADDRESS\", PHP_FTP_OPT_USEPASVADDRESS, CONST_PERSISTENT);\n\tREGISTER_LONG_CONSTANT(\"FTP_FAILED\", PHP_FTP_FAILED, CONST_PERSISTENT);\n\tREGISTER_LONG_CONSTANT(\"FTP_FINISHED\", PHP_FTP_FINISHED, CONST_PERSISTENT);\n\tREGISTER_LONG_CONSTANT(\"FTP_MOREDATA\", PHP_FTP_MOREDATA, CONST_PERSISTENT);\n\n#if PHP_VERSION_ID >= 80300\n\tzend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), \"ftp_login\", sizeof(\"ftp_login\") - 1), 2, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);\n#endif\n}\n\nstatic zend_class_entry *register_class_FTP_Connection(void)\n{\n\tzend_class_entry ce, *class_entry;\n\n#if PHP_VERSION_ID >= 80400\n\tINIT_NS_CLASS_ENTRY(ce, \"FTP\", \"Connection\", NULL);\n\tclass_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE);\n#else\n\tstatic const zend_function_entry class_FTP_Connection_methods[] = {\n\t\tZEND_FE_END\n\t};\n\tINIT_NS_CLASS_ENTRY(ce, \"FTP\", \"Connection\", class_FTP_Connection_methods);\n\tclass_entry = zend_register_internal_class_ex(&ce, NULL);\n\tclass_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE;\n#endif\n\n\treturn class_entry;\n}\n\ntypedef struct _php_ftp_object {\n\tftpbuf_t *ftp;\n\tzend_object std;\n} php_ftp_object;\n\nstatic inline zend_object *ftp_object_to_zend_object(php_ftp_object *obj) {\n\treturn ((zend_object*)(obj + 1)) - 1;\n}\n\nstatic inline php_ftp_object *ftp_object_from_zend_object(zend_object *zobj) {\n\treturn ((php_ftp_object*)(zobj + 1)) - 1;\n}\n\nstatic zend_object* ftp_object_create(zend_class_entry* ce) {\n\tphp_ftp_object *obj = zend_object_alloc(sizeof(php_ftp_object), ce);\n\tzend_object *zobj = ftp_object_to_zend_object(obj);\n\tobj->ftp = NULL;\n\tzend_object_std_init(zobj, ce);\n\tobject_properties_init(zobj, ce);\n\tzobj->handlers = &ftp_object_handlers;\n\n\treturn zobj;\n}\n\nstatic zend_function *ftp_object_get_constructor(zend_object *zobj) {\n\tzend_throw_error(NULL, \"Cannot directly construct FTP\\\\Connection, use ftp_connect() or ftp_ssl_connect() instead\");\n\treturn NULL;\n}\n\nstatic void ftp_object_destroy(zend_object *zobj) {\n\tphp_ftp_object *obj = ftp_object_from_zend_object(zobj);\n\n\tif (obj->ftp) {\n\t\tftp_close(obj->ftp);\n\t}\n\n\tzend_object_std_dtor(zobj);\n}\n\nPHP_MINIT_FUNCTION(ftp)\n{\n\tphp_ftp_ce = register_class_FTP_Connection();\n\tphp_ftp_ce->create_object = ftp_object_create;\n\n\tmemcpy(&ftp_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));\n\tftp_object_handlers.offset = XtOffsetOf(php_ftp_object, std);\n\tftp_object_handlers.get_constructor = ftp_object_get_constructor;\n\tftp_object_handlers.free_obj = ftp_object_destroy;\n\tftp_object_handlers.clone_obj = NULL;\n\n\tregister_ftp_symbols(module_number);\n\n\treturn SUCCESS;\n}\n\nPHP_MINFO_FUNCTION(ftp)\n{\n\tphp_info_print_table_row(2, \"FTP support\", \"enabled\");\n#ifdef SW_HAVE_FTP_SSL\n\tphp_info_print_table_row(2, \"FTPS support\", \"enabled\");\n#else\n\tphp_info_print_table_row(2, \"FTPS support\", \"disabled\");\n#endif\n}\n\n#define\tXTYPE(xtype, mode)\t{ \\\n\tif (mode != FTPTYPE_ASCII && mode != FTPTYPE_IMAGE) { \\\n\t\tzend_argument_value_error(4, \"must be either FTP_ASCII or FTP_BINARY\"); \\\n\t\tRETURN_THROWS(); \\\n\t} \\\n\txtype = mode; \\\n}\n\n\n/* {{{ Opens a FTP stream */\nPHP_FUNCTION(ftp_connect)\n{\n\tftpbuf_t\t*ftp;\n\tchar\t\t*host;\n\tsize_t\t\thost_len;\n\tzend_long \t\tport = 0;\n\tzend_long\t\ttimeout_sec = FTP_DEFAULT_TIMEOUT;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"s|ll\", &host, &host_len, &port, &timeout_sec) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\n\tif (timeout_sec <= 0) {\n\t\tzend_argument_value_error(3, \"must be greater than 0\");\n\t\tRETURN_THROWS();\n\t}\n\n\t/* connect */\n\tif (!(ftp = ftp_open(host, (short)port, timeout_sec))) {\n\t\tRETURN_FALSE;\n\t}\n\n\t/* autoseek for resuming */\n\tftp->autoseek = FTP_DEFAULT_AUTOSEEK;\n\tftp->usepasvaddress = FTP_DEFAULT_USEPASVADDRESS;\n#ifdef SW_HAVE_FTP_SSL\n\t/* disable ssl */\n\tftp->use_ssl = 0;\n#endif\n\n\tobject_init_ex(return_value, php_ftp_ce);\n\tftp_object_from_zend_object(Z_OBJ_P(return_value))->ftp = ftp;\n}\n/* }}} */\n\n#ifdef SW_HAVE_FTP_SSL\n/* {{{ Opens a FTP-SSL stream */\nPHP_FUNCTION(ftp_ssl_connect)\n{\n\tftpbuf_t\t*ftp;\n\tchar\t\t*host;\n\tsize_t\t\thost_len;\n\tzend_long\t\tport = 0;\n\tzend_long\t\ttimeout_sec = FTP_DEFAULT_TIMEOUT;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"s|ll\", &host, &host_len, &port, &timeout_sec) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\n\tif (timeout_sec <= 0) {\n\t\tzend_argument_value_error(3, \"must be greater than 0\");\n\t\tRETURN_THROWS();\n\t}\n\n\t/* connect */\n\tif (!(ftp = ftp_open(host, (short)port, timeout_sec))) {\n\t\tRETURN_FALSE;\n\t}\n\n\t/* autoseek for resuming */\n\tftp->autoseek = FTP_DEFAULT_AUTOSEEK;\n\tftp->usepasvaddress = FTP_DEFAULT_USEPASVADDRESS;\n\t/* enable ssl */\n\tftp->use_ssl = 1;\n\n\tobject_init_ex(return_value, php_ftp_ce);\n\tftp_object_from_zend_object(Z_OBJ_P(return_value))->ftp = ftp;\n}\n/* }}} */\n#endif\n\n#define GET_FTPBUF(ftpbuf, zftp) \\\n\tftpbuf = ftp_object_from_zend_object(Z_OBJ_P(zftp))->ftp; \\\n\tif (!ftpbuf) { \\\n\t\tzend_throw_exception(zend_ce_value_error, \"FTP\\\\Connection is already closed\", 0); \\\n\t\tRETURN_THROWS(); \\\n\t}\n\n/* {{{ Logs into the FTP server */\nPHP_FUNCTION(ftp_login)\n{\n\tzval \t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tchar *user, *pass;\n\tsize_t user_len, pass_len;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Oss\", &z_ftp, php_ftp_ce, &user, &user_len, &pass, &pass_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\t/* log in */\n\tif (!ftp_login(ftp, user, user_len, pass, pass_len)) {\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_FALSE;\n\t}\n\n\tRETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ Returns the present working directory */\nPHP_FUNCTION(ftp_pwd)\n{\n\tzval \t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tconst char\t*pwd;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"O\", &z_ftp, php_ftp_ce) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\tif (!(pwd = ftp_pwd(ftp))) {\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_FALSE;\n\t}\n\n\tRETURN_STRING((char*) pwd);\n}\n/* }}} */\n\n/* {{{ Changes to the parent directory */\nPHP_FUNCTION(ftp_cdup)\n{\n\tzval \t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"O\", &z_ftp, php_ftp_ce) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\tif (!ftp_cdup(ftp)) {\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_FALSE;\n\t}\n\n\tRETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ Changes directories */\nPHP_FUNCTION(ftp_chdir)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tchar\t\t*dir;\n\tsize_t\t\t\tdir_len;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Os\", &z_ftp, php_ftp_ce, &dir, &dir_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\t/* change directories */\n\tif (!ftp_chdir(ftp, dir, dir_len)) {\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_FALSE;\n\t}\n\n\tRETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ Requests execution of a program on the FTP server */\nPHP_FUNCTION(ftp_exec)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tchar\t\t*cmd;\n\tsize_t\t\t\tcmd_len;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Os\", &z_ftp, php_ftp_ce, &cmd, &cmd_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\t/* execute serverside command */\n\tif (!ftp_exec(ftp, cmd, cmd_len)) {\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_FALSE;\n\t}\n\n\tRETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ Sends a literal command to the FTP server */\nPHP_FUNCTION(ftp_raw)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tchar\t\t*cmd;\n\tsize_t\t\t\tcmd_len;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Os\", &z_ftp, php_ftp_ce, &cmd, &cmd_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\t/* execute arbitrary ftp command */\n\tftp_raw(ftp, cmd, cmd_len, return_value);\n}\n/* }}} */\n\n/* {{{ Creates a directory and returns the absolute path for the new directory or false on error */\nPHP_FUNCTION(ftp_mkdir)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tchar\t\t*dir;\n\tzend_string *tmp;\n\tsize_t\t\tdir_len;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Os\", &z_ftp, php_ftp_ce, &dir, &dir_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\t/* create directory */\n\tif (NULL == (tmp = ftp_mkdir(ftp, dir, dir_len))) {\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_FALSE;\n\t}\n\n\tRETURN_STR(tmp);\n}\n/* }}} */\n\n/* {{{ Removes a directory */\nPHP_FUNCTION(ftp_rmdir)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tchar\t\t*dir;\n\tsize_t\t\tdir_len;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Os\", &z_ftp, php_ftp_ce, &dir, &dir_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\t/* remove directorie */\n\tif (!ftp_rmdir(ftp, dir, dir_len)) {\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_FALSE;\n\t}\n\n\tRETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ Sets permissions on a file */\nPHP_FUNCTION(ftp_chmod)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tchar\t\t*filename;\n\tsize_t\t\tfilename_len;\n\tzend_long\t\tmode;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Olp\", &z_ftp, php_ftp_ce, &mode, &filename, &filename_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\tif (!ftp_chmod(ftp, mode, filename, filename_len)) {\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_FALSE;\n\t}\n\n\tRETURN_LONG(mode);\n}\n/* }}} */\n\n/* {{{ Attempt to allocate space on the remote FTP server */\nPHP_FUNCTION(ftp_alloc)\n{\n\tzval\t\t*z_ftp, *zresponse = NULL;\n\tftpbuf_t\t*ftp;\n\tzend_long\t\tsize, ret;\n\tzend_string\t*response = NULL;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Ol|z\", &z_ftp, php_ftp_ce, &size, &zresponse) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\tret = ftp_alloc(ftp, size, zresponse ? &response : NULL);\n\n\tif (response) {\n\t\tZEND_TRY_ASSIGN_REF_STR(zresponse, response);\n\t}\n\n\tif (!ret) {\n\t\tRETURN_FALSE;\n\t}\n\n\tRETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ Returns an array of filenames in the given directory */\nPHP_FUNCTION(ftp_nlist)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tchar\t\t**nlist, **ptr, *dir;\n\tsize_t\t\tdir_len;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Op\", &z_ftp, php_ftp_ce, &dir, &dir_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\t/* get list of files */\n\tif (NULL == (nlist = ftp_nlist(ftp, dir, dir_len))) {\n\t\tRETURN_FALSE;\n\t}\n\n\tarray_init(return_value);\n\tfor (ptr = nlist; *ptr; ptr++) {\n\t\tadd_next_index_string(return_value, *ptr);\n\t}\n\tefree(nlist);\n}\n/* }}} */\n\n/* {{{ Returns a detailed listing of a directory as an array of output lines */\nPHP_FUNCTION(ftp_rawlist)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tchar\t\t**llist, **ptr, *dir;\n\tsize_t\t\tdir_len;\n\tbool\trecursive = 0;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Os|b\", &z_ftp, php_ftp_ce, &dir, &dir_len, &recursive) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\t/* get raw directory listing */\n\tif (NULL == (llist = ftp_list(ftp, dir, dir_len, recursive))) {\n\t\tRETURN_FALSE;\n\t}\n\n\tarray_init(return_value);\n\tfor (ptr = llist; *ptr; ptr++) {\n\t\tadd_next_index_string(return_value, *ptr);\n\t}\n\tefree(llist);\n}\n/* }}} */\n\n/* {{{ Returns a detailed listing of a directory as an array of parsed output lines */\nPHP_FUNCTION(ftp_mlsd)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tchar\t\t**llist, **ptr, *dir;\n\tsize_t\t\tdir_len;\n\tzval\t\tentry;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Os\", &z_ftp, php_ftp_ce, &dir, &dir_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\t/* get raw directory listing */\n\tif (NULL == (llist = ftp_mlsd(ftp, dir, dir_len))) {\n\t\tRETURN_FALSE;\n\t}\n\n\tarray_init(return_value);\n\tfor (ptr = llist; *ptr; ptr++) {\n\t\tarray_init(&entry);\n\t\tif (ftp_mlsd_parse_line(Z_ARRVAL_P(&entry), *ptr) == SUCCESS) {\n\t\t\tzend_hash_next_index_insert(Z_ARRVAL_P(return_value), &entry);\n\t\t} else {\n\t\t\tzval_ptr_dtor(&entry);\n\t\t}\n\t}\n\n\tefree(llist);\n}\n/* }}} */\n\n/* {{{ Returns the system type identifier */\nPHP_FUNCTION(ftp_systype)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tconst char\t*syst;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"O\", &z_ftp, php_ftp_ce) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\tif (NULL == (syst = ftp_syst(ftp))) {\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_FALSE;\n\t}\n\n\tRETURN_STRING((char*) syst);\n}\n/* }}} */\n\n/* {{{ Retrieves a file from the FTP server and writes it to an open file */\nPHP_FUNCTION(ftp_fget)\n{\n\tzval\t\t*z_ftp, *z_file;\n\tftpbuf_t\t*ftp;\n\tftptype_t\txtype;\n\tphp_stream\t*stream;\n\tchar\t\t*file;\n\tsize_t\t\tfile_len;\n\tzend_long\t\tmode=FTPTYPE_IMAGE, resumepos=0;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Ors|ll\", &z_ftp, php_ftp_ce, &z_file, &file, &file_len, &mode, &resumepos) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\tphp_stream_from_res(stream, Z_RES_P(z_file));\n\tXTYPE(xtype, mode);\n\n\t/* ignore autoresume if autoseek is switched off */\n\tif (!ftp->autoseek && resumepos == PHP_FTP_AUTORESUME) {\n\t\tresumepos = 0;\n\t}\n\n\tif (ftp->autoseek && resumepos) {\n\t\t/* if autoresume is wanted seek to end */\n\t\tif (resumepos == PHP_FTP_AUTORESUME) {\n\t\t\tphp_stream_seek(stream, 0, SEEK_END);\n\t\t\tresumepos = php_stream_tell(stream);\n\t\t} else {\n\t\t\tphp_stream_seek(stream, resumepos, SEEK_SET);\n\t\t}\n\t}\n\n\tif (!ftp_get(ftp, stream, file, file_len, xtype, resumepos)) {\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_FALSE;\n\t}\n\n\tRETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ Retrieves a file from the FTP server asynchronly and writes it to an open file */\nPHP_FUNCTION(ftp_nb_fget)\n{\n\tzval\t\t*z_ftp, *z_file;\n\tftpbuf_t\t*ftp;\n\tftptype_t\txtype;\n\tphp_stream\t*stream;\n\tchar\t\t*file;\n\tsize_t\t\tfile_len;\n\tzend_long\t\tmode=FTPTYPE_IMAGE, resumepos=0, ret;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Ors|ll\", &z_ftp, php_ftp_ce, &z_file, &file, &file_len, &mode, &resumepos) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\tphp_stream_from_res(stream, Z_RES_P(z_file));\n\tXTYPE(xtype, mode);\n\n\t/* ignore autoresume if autoseek is switched off */\n\tif (!ftp->autoseek && resumepos == PHP_FTP_AUTORESUME) {\n\t\tresumepos = 0;\n\t}\n\n\tif (ftp->autoseek && resumepos) {\n\t\t/* if autoresume is wanted seek to end */\n\t\tif (resumepos == PHP_FTP_AUTORESUME) {\n\t\t\tphp_stream_seek(stream, 0, SEEK_END);\n\t\t\tresumepos = php_stream_tell(stream);\n\t\t} else {\n\t\t\tphp_stream_seek(stream, resumepos, SEEK_SET);\n\t\t}\n\t}\n\n\t/* configuration */\n\tftp->direction = 0;   /* recv */\n\tftp->closestream = 0; /* do not close */\n\n\tif ((ret = ftp_nb_get(ftp, stream, file, file_len, xtype, resumepos)) == PHP_FTP_FAILED) {\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_LONG(ret);\n\t}\n\n\tRETURN_LONG(ret);\n}\n/* }}} */\n\n/* {{{ Turns passive mode on or off */\nPHP_FUNCTION(ftp_pasv)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tbool\tpasv;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Ob\", &z_ftp, php_ftp_ce, &pasv) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\tif (!ftp_pasv(ftp, pasv ? 1 : 0)) {\n\t\tRETURN_FALSE;\n\t}\n\n\tRETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ Retrieves a file from the FTP server and writes it to a local file */\nPHP_FUNCTION(ftp_get)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tftptype_t\txtype;\n\tphp_stream\t*outstream;\n\tchar\t\t*local, *remote;\n\tsize_t\t\tlocal_len, remote_len;\n\tzend_long\t\tmode=FTPTYPE_IMAGE, resumepos=0;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Opp|ll\", &z_ftp, php_ftp_ce, &local, &local_len, &remote, &remote_len, &mode, &resumepos) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\tXTYPE(xtype, mode);\n\n\t/* ignore autoresume if autoseek is switched off */\n\tif (!ftp->autoseek && resumepos == PHP_FTP_AUTORESUME) {\n\t\tresumepos = 0;\n\t}\n\n#ifdef PHP_WIN32\n\tmode = FTPTYPE_IMAGE;\n#endif\n\n\tif (ftp->autoseek && resumepos) {\n\t\toutstream = php_stream_open_wrapper(local, mode == FTPTYPE_ASCII ? \"rt+\" : \"rb+\", REPORT_ERRORS, NULL);\n\t\tif (outstream == NULL) {\n\t\t\toutstream = php_stream_open_wrapper(local, mode == FTPTYPE_ASCII ? \"wt\" : \"wb\", REPORT_ERRORS, NULL);\n\t\t}\n\t\tif (outstream != NULL) {\n\t\t\t/* if autoresume is wanted seek to end */\n\t\t\tif (resumepos == PHP_FTP_AUTORESUME) {\n\t\t\t\tphp_stream_seek(outstream, 0, SEEK_END);\n\t\t\t\tresumepos = php_stream_tell(outstream);\n\t\t\t} else {\n\t\t\t\tphp_stream_seek(outstream, resumepos, SEEK_SET);\n\t\t\t}\n\t\t}\n\t} else {\n\t\toutstream = php_stream_open_wrapper(local, mode == FTPTYPE_ASCII ? \"wt\" : \"wb\", REPORT_ERRORS, NULL);\n\t}\n\n\tif (outstream == NULL)\t{\n\t\tphp_error_docref(NULL, E_WARNING, \"Error opening %s\", local);\n\t\tRETURN_FALSE;\n\t}\n\n\tif (!ftp_get(ftp, outstream, remote, remote_len, xtype, resumepos)) {\n\t\tphp_stream_close(outstream);\n\t\tVCWD_UNLINK(local);\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_FALSE;\n\t}\n\n\tphp_stream_close(outstream);\n\tRETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ Retrieves a file from the FTP server nbhronly and writes it to a local file */\nPHP_FUNCTION(ftp_nb_get)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tftptype_t\txtype;\n\tphp_stream\t*outstream;\n\tchar\t\t*local, *remote;\n\tsize_t\t\tlocal_len, remote_len;\n\tint ret;\n\tzend_long\t\tmode=FTPTYPE_IMAGE, resumepos=0;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Oss|ll\", &z_ftp, php_ftp_ce, &local, &local_len, &remote, &remote_len, &mode, &resumepos) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\tXTYPE(xtype, mode);\n\n\t/* ignore autoresume if autoseek is switched off */\n\tif (!ftp->autoseek && resumepos == PHP_FTP_AUTORESUME) {\n\t\tresumepos = 0;\n\t}\n#ifdef PHP_WIN32\n\tmode = FTPTYPE_IMAGE;\n#endif\n\tif (ftp->autoseek && resumepos) {\n\t\toutstream = php_stream_open_wrapper(local, mode == FTPTYPE_ASCII ? \"rt+\" : \"rb+\", REPORT_ERRORS, NULL);\n\t\tif (outstream == NULL) {\n\t\t\toutstream = php_stream_open_wrapper(local, mode == FTPTYPE_ASCII ? \"wt\" : \"wb\", REPORT_ERRORS, NULL);\n\t\t}\n\t\tif (outstream != NULL) {\n\t\t\t/* if autoresume is wanted seek to end */\n\t\t\tif (resumepos == PHP_FTP_AUTORESUME) {\n\t\t\t\tphp_stream_seek(outstream, 0, SEEK_END);\n\t\t\t\tresumepos = php_stream_tell(outstream);\n\t\t\t} else {\n\t\t\t\tphp_stream_seek(outstream, resumepos, SEEK_SET);\n\t\t\t}\n\t\t}\n\t} else {\n\t\toutstream = php_stream_open_wrapper(local, mode == FTPTYPE_ASCII ? \"wt\" : \"wb\", REPORT_ERRORS, NULL);\n\t}\n\n\tif (outstream == NULL)\t{\n\t\tphp_error_docref(NULL, E_WARNING, \"Error opening %s\", local);\n\t\tRETURN_FALSE;\n\t}\n\n\t/* configuration */\n\tftp->direction = 0;   /* recv */\n\tftp->closestream = 1; /* do close */\n\n\tif ((ret = ftp_nb_get(ftp, outstream, remote, remote_len, xtype, resumepos)) == PHP_FTP_FAILED) {\n\t\tphp_stream_close(outstream);\n\t\tftp->stream = NULL;\n\t\tVCWD_UNLINK(local);\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_LONG(PHP_FTP_FAILED);\n\t}\n\n\tif (ret == PHP_FTP_FINISHED){\n\t\tphp_stream_close(outstream);\n\t\tftp->stream = NULL;\n\t}\n\n\tRETURN_LONG(ret);\n}\n/* }}} */\n\n/* {{{ Continues retrieving/sending a file nbronously */\nPHP_FUNCTION(ftp_nb_continue)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tzend_long\t\tret;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"O\", &z_ftp, php_ftp_ce) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\tif (!ftp->nb) {\n\t\tphp_error_docref(NULL, E_WARNING, \"No nbronous transfer to continue\");\n\t\tRETURN_LONG(PHP_FTP_FAILED);\n\t}\n\n\tif (ftp->direction) {\n\t\tret=ftp_nb_continue_write(ftp);\n\t} else {\n\t\tret=ftp_nb_continue_read(ftp);\n\t}\n\n\tif (ret != PHP_FTP_MOREDATA && ftp->closestream) {\n\t\tphp_stream_close(ftp->stream);\n\t\tftp->stream = NULL;\n\t}\n\n\tif (ret == PHP_FTP_FAILED) {\n\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t}\n\n\tRETURN_LONG(ret);\n}\n/* }}} */\n\n/* {{{ Stores a file from an open file to the FTP server */\nPHP_FUNCTION(ftp_fput)\n{\n\tzval\t\t*z_ftp, *z_file;\n\tftpbuf_t\t*ftp;\n\tftptype_t\txtype;\n\tsize_t\t\tremote_len;\n\tzend_long\t\tmode=FTPTYPE_IMAGE, startpos=0;\n\tphp_stream\t*stream;\n\tchar\t\t*remote;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Osr|ll\", &z_ftp, php_ftp_ce, &remote, &remote_len, &z_file, &mode, &startpos) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\tphp_stream_from_zval(stream, z_file);\n\tXTYPE(xtype, mode);\n\n\t/* ignore autoresume if autoseek is switched off */\n\tif (!ftp->autoseek && startpos == PHP_FTP_AUTORESUME) {\n\t\tstartpos = 0;\n\t}\n\n\tif (ftp->autoseek && startpos) {\n\t\t/* if autoresume is wanted ask for remote size */\n\t\tif (startpos == PHP_FTP_AUTORESUME) {\n\t\t\tstartpos = ftp_size(ftp, remote, remote_len);\n\t\t\tif (startpos < 0) {\n\t\t\t\tstartpos = 0;\n\t\t\t}\n\t\t}\n\t\tif (startpos) {\n\t\t\tphp_stream_seek(stream, startpos, SEEK_SET);\n\t\t}\n\t}\n\n\tif (!ftp_put(ftp, remote, remote_len, stream, xtype, startpos)) {\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_FALSE;\n\t}\n\n\tRETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ Stores a file from an open file to the FTP server nbronly */\nPHP_FUNCTION(ftp_nb_fput)\n{\n\tzval\t\t*z_ftp, *z_file;\n\tftpbuf_t\t*ftp;\n\tftptype_t\txtype;\n\tsize_t\t\tremote_len;\n\tint             ret;\n\tzend_long\tmode=FTPTYPE_IMAGE, startpos=0;\n\tphp_stream\t*stream;\n\tchar\t\t*remote;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Osr|ll\", &z_ftp, php_ftp_ce, &remote, &remote_len, &z_file, &mode, &startpos) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\tphp_stream_from_res(stream, Z_RES_P(z_file));\n\tXTYPE(xtype, mode);\n\n\t/* ignore autoresume if autoseek is switched off */\n\tif (!ftp->autoseek && startpos == PHP_FTP_AUTORESUME) {\n\t\tstartpos = 0;\n\t}\n\n\tif (ftp->autoseek && startpos) {\n\t\t/* if autoresume is wanted ask for remote size */\n\t\tif (startpos == PHP_FTP_AUTORESUME) {\n\t\t\tstartpos = ftp_size(ftp, remote, remote_len);\n\t\t\tif (startpos < 0) {\n\t\t\t\tstartpos = 0;\n\t\t\t}\n\t\t}\n\t\tif (startpos) {\n\t\t\tphp_stream_seek(stream, startpos, SEEK_SET);\n\t\t}\n\t}\n\n\t/* configuration */\n\tftp->direction = 1;   /* send */\n\tftp->closestream = 0; /* do not close */\n\n\tif (((ret = ftp_nb_put(ftp, remote, remote_len, stream, xtype, startpos)) == PHP_FTP_FAILED)) {\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_LONG(ret);\n\t}\n\n\tRETURN_LONG(ret);\n}\n/* }}} */\n\n\n/* {{{ Stores a file on the FTP server */\nPHP_FUNCTION(ftp_put)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tftptype_t\txtype;\n\tchar\t\t*remote, *local;\n\tsize_t\t\tremote_len, local_len;\n\tzend_long\t\tmode=FTPTYPE_IMAGE, startpos=0;\n\tphp_stream \t*instream;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Opp|ll\", &z_ftp, php_ftp_ce, &remote, &remote_len, &local, &local_len, &mode, &startpos) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\tXTYPE(xtype, mode);\n\n\tif (!(instream = php_stream_open_wrapper(local, mode == FTPTYPE_ASCII ? \"rt\" : \"rb\", REPORT_ERRORS, NULL))) {\n\t\tRETURN_FALSE;\n\t}\n\n\t/* ignore autoresume if autoseek is switched off */\n\tif (!ftp->autoseek && startpos == PHP_FTP_AUTORESUME) {\n\t\tstartpos = 0;\n\t}\n\n\tif (ftp->autoseek && startpos) {\n\t\t/* if autoresume is wanted ask for remote size */\n\t\tif (startpos == PHP_FTP_AUTORESUME) {\n\t\t\tstartpos = ftp_size(ftp, remote, remote_len);\n\t\t\tif (startpos < 0) {\n\t\t\t\tstartpos = 0;\n\t\t\t}\n\t\t}\n\t\tif (startpos) {\n\t\t\tphp_stream_seek(instream, startpos, SEEK_SET);\n\t\t}\n\t}\n\n\tif (!ftp_put(ftp, remote, remote_len, instream, xtype, startpos)) {\n\t\tphp_stream_close(instream);\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_FALSE;\n\t}\n\tphp_stream_close(instream);\n\n\tRETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ Append content of a file a another file on the FTP server */\nPHP_FUNCTION(ftp_append)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tftptype_t\txtype;\n\tchar\t\t*remote, *local;\n\tsize_t\t\tremote_len, local_len;\n\tzend_long\t\tmode=FTPTYPE_IMAGE;\n\tphp_stream \t*instream;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Opp|l\", &z_ftp, php_ftp_ce, &remote, &remote_len, &local, &local_len, &mode) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\tXTYPE(xtype, mode);\n\n\tif (!(instream = php_stream_open_wrapper(local, mode == FTPTYPE_ASCII ? \"rt\" : \"rb\", REPORT_ERRORS, NULL))) {\n\t\tRETURN_FALSE;\n\t}\n\n\tif (!ftp_append(ftp, remote, remote_len, instream, xtype)) {\n\t\tphp_stream_close(instream);\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_FALSE;\n\t}\n\tphp_stream_close(instream);\n\n\tRETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ Stores a file on the FTP server */\nPHP_FUNCTION(ftp_nb_put)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tftptype_t\txtype;\n\tchar\t\t*remote, *local;\n\tsize_t\t\tremote_len, local_len;\n\tzend_long\t\tmode=FTPTYPE_IMAGE, startpos=0, ret;\n\tphp_stream \t*instream;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Opp|ll\", &z_ftp, php_ftp_ce, &remote, &remote_len, &local, &local_len, &mode, &startpos) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\tXTYPE(xtype, mode);\n\n\tif (!(instream = php_stream_open_wrapper(local, mode == FTPTYPE_ASCII ? \"rt\" : \"rb\", REPORT_ERRORS, NULL))) {\n\t\tRETURN_FALSE;\n\t}\n\n\t/* ignore autoresume if autoseek is switched off */\n\tif (!ftp->autoseek && startpos == PHP_FTP_AUTORESUME) {\n\t\tstartpos = 0;\n\t}\n\n\tif (ftp->autoseek && startpos) {\n\t\t/* if autoresume is wanted ask for remote size */\n\t\tif (startpos == PHP_FTP_AUTORESUME) {\n\t\t\tstartpos = ftp_size(ftp, remote, remote_len);\n\t\t\tif (startpos < 0) {\n\t\t\t\tstartpos = 0;\n\t\t\t}\n\t\t}\n\t\tif (startpos) {\n\t\t\tphp_stream_seek(instream, startpos, SEEK_SET);\n\t\t}\n\t}\n\n\t/* configuration */\n\tftp->direction = 1;   /* send */\n\tftp->closestream = 1; /* do close */\n\n\tret = ftp_nb_put(ftp, remote, remote_len, instream, xtype, startpos);\n\n\tif (ret != PHP_FTP_MOREDATA) {\n\t\tphp_stream_close(instream);\n\t\tftp->stream = NULL;\n\t}\n\n\tif (ret == PHP_FTP_FAILED) {\n\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t}\n\n\tRETURN_LONG(ret);\n}\n/* }}} */\n\n/* {{{ Returns the size of the file, or -1 on error */\nPHP_FUNCTION(ftp_size)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tchar\t\t*file;\n\tsize_t\t\tfile_len;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Op\", &z_ftp, php_ftp_ce, &file, &file_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\t/* get file size */\n\tRETURN_LONG(ftp_size(ftp, file, file_len));\n}\n/* }}} */\n\n/* {{{ Returns the last modification time of the file, or -1 on error */\nPHP_FUNCTION(ftp_mdtm)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tchar\t\t*file;\n\tsize_t\t\tfile_len;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Op\", &z_ftp, php_ftp_ce, &file, &file_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\t/* get file mod time */\n\tRETURN_LONG(ftp_mdtm(ftp, file, file_len));\n}\n/* }}} */\n\n/* {{{ Renames the given file to a new path */\nPHP_FUNCTION(ftp_rename)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tchar\t\t*src, *dest;\n\tsize_t\t\tsrc_len, dest_len;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Oss\", &z_ftp, php_ftp_ce, &src, &src_len, &dest, &dest_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\t/* rename the file */\n\tif (!ftp_rename(ftp, src, src_len, dest, dest_len)) {\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_FALSE;\n\t}\n\n\tRETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ Deletes a file */\nPHP_FUNCTION(ftp_delete)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tchar\t\t*file;\n\tsize_t\t\tfile_len;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Os\", &z_ftp, php_ftp_ce, &file, &file_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\t/* delete the file */\n\tif (!ftp_delete(ftp, file, file_len)) {\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_FALSE;\n\t}\n\n\tRETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ Sends a SITE command to the server */\nPHP_FUNCTION(ftp_site)\n{\n\tzval\t\t*z_ftp;\n\tftpbuf_t\t*ftp;\n\tchar\t\t*cmd;\n\tsize_t\t\tcmd_len;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Os\", &z_ftp, php_ftp_ce, &cmd, &cmd_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\t/* send the site command */\n\tif (!ftp_site(ftp, cmd, cmd_len)) {\n\t\tif (*ftp->inbuf) {\n\t\t\tphp_error_docref(NULL, E_WARNING, \"%s\", ftp->inbuf);\n\t\t}\n\t\tRETURN_FALSE;\n\t}\n\n\tRETURN_TRUE;\n}\n/* }}} */\n\n/* {{{ Closes the FTP stream */\nPHP_FUNCTION(ftp_close)\n{\n\tzval\t\t*z_ftp;\n\tphp_ftp_object *obj;\n\tbool success = true;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"O\", &z_ftp, php_ftp_ce) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\n\tobj = ftp_object_from_zend_object(Z_OBJ_P(z_ftp));\n\tif (obj->ftp) {\n\t\tsuccess = ftp_quit(obj->ftp);\n\t\tftp_close(obj->ftp);\n\t\tobj->ftp = NULL;\n\t}\n\n\tRETURN_BOOL(success);\n}\n/* }}} */\n\n/* {{{ Sets an FTP option */\nPHP_FUNCTION(ftp_set_option)\n{\n\tzval\t\t*z_ftp, *z_value;\n\tzend_long\t\toption;\n\tftpbuf_t\t*ftp;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Olz\", &z_ftp, php_ftp_ce, &option, &z_value) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\tswitch (option) {\n\t\tcase PHP_FTP_OPT_TIMEOUT_SEC:\n\t\t\tif (Z_TYPE_P(z_value) != IS_LONG) {\n\t\t\t\tzend_argument_type_error(3, \"must be of type int for the FTP_TIMEOUT_SEC option, %s given\", zend_zval_value_name(z_value));\n\t\t\t\tRETURN_THROWS();\n\t\t\t}\n\t\t\tif (Z_LVAL_P(z_value) <= 0) {\n\t\t\t\tzend_argument_value_error(3, \"must be greater than 0 for the FTP_TIMEOUT_SEC option\");\n\t\t\t\tRETURN_THROWS();\n\t\t\t}\n\t\t\tftp->timeout_sec = Z_LVAL_P(z_value);\n\t\t\tRETURN_TRUE;\n\t\t\tbreak;\n\t\tcase PHP_FTP_OPT_AUTOSEEK:\n\t\t\tif (Z_TYPE_P(z_value) != IS_TRUE && Z_TYPE_P(z_value) != IS_FALSE) {\n\t\t\t\tzend_argument_type_error(3, \"must be of type bool for the FTP_AUTOSEEK option, %s given\", zend_zval_value_name(z_value));\n\t\t\t\tRETURN_THROWS();\n\t\t\t}\n\t\t\tftp->autoseek = Z_TYPE_P(z_value) == IS_TRUE ? 1 : 0;\n\t\t\tRETURN_TRUE;\n\t\t\tbreak;\n\t\tcase PHP_FTP_OPT_USEPASVADDRESS:\n\t\t\tif (Z_TYPE_P(z_value) != IS_TRUE && Z_TYPE_P(z_value) != IS_FALSE) {\n\t\t\t\tzend_argument_type_error(3, \"must be of type bool for the FTP_USEPASVADDRESS option, %s given\", zend_zval_value_name(z_value));\n\t\t\t\tRETURN_THROWS();\n\t\t\t}\n\t\t\tftp->usepasvaddress = Z_TYPE_P(z_value) == IS_TRUE ? 1 : 0;\n\t\t\tRETURN_TRUE;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tzend_argument_value_error(2, \"must be one of FTP_TIMEOUT_SEC, FTP_AUTOSEEK, or FTP_USEPASVADDRESS\");\n\t\t\tRETURN_THROWS();\n\t\t\tbreak;\n\t}\n}\n/* }}} */\n\n/* {{{ Gets an FTP option */\nPHP_FUNCTION(ftp_get_option)\n{\n\tzval\t\t*z_ftp;\n\tzend_long\t\toption;\n\tftpbuf_t\t*ftp;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"Ol\", &z_ftp, php_ftp_ce, &option) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\tGET_FTPBUF(ftp, z_ftp);\n\n\tswitch (option) {\n\t\tcase PHP_FTP_OPT_TIMEOUT_SEC:\n\t\t\tRETURN_LONG(ftp->timeout_sec);\n\t\t\tbreak;\n\t\tcase PHP_FTP_OPT_AUTOSEEK:\n\t\t\tRETURN_BOOL(ftp->autoseek);\n\t\t\tbreak;\n\t\tcase PHP_FTP_OPT_USEPASVADDRESS:\n\t\t\tRETURN_BOOL(ftp->usepasvaddress);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tzend_argument_value_error(2, \"must be one of FTP_TIMEOUT_SEC, FTP_AUTOSEEK, or FTP_USEPASVADDRESS\");\n\t\t\tRETURN_THROWS();\n\t}\n}\n/* }}} */\n\n#endif /* SW_HAVE_FTP */\n"
  },
  {
    "path": "thirdparty/php84/ftp/php_ftp.h",
    "content": "/*\n   +----------------------------------------------------------------------+\n   | Copyright (c) The PHP Group                                          |\n   +----------------------------------------------------------------------+\n   | This source file is subject to version 3.01 of the PHP license,      |\n   | that is bundled with this package in the file LICENSE, and is        |\n   | available through the world-wide-web at the following url:           |\n   | https://www.php.net/license/3_01.txt                                 |\n   | If you did not receive a copy of the PHP license and are unable to   |\n   | obtain it through the world-wide-web, please send a note to          |\n   | license@php.net so we can mail you a copy immediately.               |\n   +----------------------------------------------------------------------+\n   | Authors: Andrew Skalski <askalski@chek.com>                          |\n   |          Stefan Esser <sesser@php.net> (resume functions)            |\n   +----------------------------------------------------------------------+\n */\n\n#ifndef\t_INCLUDED_FTP_H\n#define\t_INCLUDED_FTP_H\n\n#include \"php_version.h\"\n#define PHP_FTP_VERSION PHP_VERSION\n\n#define PHP_FTP_OPT_TIMEOUT_SEC\t0\n#define PHP_FTP_OPT_AUTOSEEK\t1\n#define PHP_FTP_OPT_USEPASVADDRESS\t2\n#define PHP_FTP_AUTORESUME\t\t-1\n\n\n#endif\n"
  },
  {
    "path": "thirdparty/php84/pdo_firebird/CREDITS",
    "content": "Firebird driver for PDO\nArd Biesheuvel\n"
  },
  {
    "path": "thirdparty/php84/pdo_firebird/firebird_driver.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Ard Biesheuvel <abies@php.net>                               |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_FIREBIRD_HOOK\n#include \"php_swoole_firebird.h\"\n\n#if PHP_VERSION_ID < 80500\n\n#ifndef _GNU_SOURCE\n# define _GNU_SOURCE\n#endif\n\n#include \"php.h\"\n#include \"zend_exceptions.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"ext/pdo/php_pdo.h\"\n#include \"ext/pdo/php_pdo_driver.h\"\n#include \"php_pdo_firebird_int.h\"\n#include \"pdo_firebird_utils.h\"\n\nstatic int php_firebird_alloc_prepare_stmt(pdo_dbh_t*, const zend_string*, XSQLDA*, isc_stmt_handle*,\n\tHashTable*);\nstatic bool php_firebird_rollback_transaction(pdo_dbh_t *dbh);\n\nconst char CHR_LETTER = 1;\nconst char CHR_DIGIT = 2;\nconst char CHR_IDENT = 4;\nconst char CHR_QUOTE = 8;\nconst char CHR_WHITE = 16;\nconst char CHR_HEX = 32;\nconst char CHR_INTRODUCER = 64;\n\nstatic const char classes_array[] = {\n\t/* 000     */ 0,\n\t/* 001     */ 0,\n\t/* 002     */ 0,\n\t/* 003     */ 0,\n\t/* 004     */ 0,\n\t/* 005     */ 0,\n\t/* 006     */ 0,\n\t/* 007     */ 0,\n\t/* 008     */ 0,\n\t/* 009     */ 16, /* CHR_WHITE */\n\t/* 010     */ 16, /* CHR_WHITE */\n\t/* 011     */ 0,\n\t/* 012     */ 0,\n\t/* 013     */ 16, /* CHR_WHITE */\n\t/* 014     */ 0,\n\t/* 015     */ 0,\n\t/* 016     */ 0,\n\t/* 017     */ 0,\n\t/* 018     */ 0,\n\t/* 019     */ 0,\n\t/* 020     */ 0,\n\t/* 021     */ 0,\n\t/* 022     */ 0,\n\t/* 023     */ 0,\n\t/* 024     */ 0,\n\t/* 025     */ 0,\n\t/* 026     */ 0,\n\t/* 027     */ 0,\n\t/* 028     */ 0,\n\t/* 029     */ 0,\n\t/* 030     */ 0,\n\t/* 031     */ 0,\n\t/* 032     */ 16, /* CHR_WHITE */\n\t/* 033  !  */ 0,\n\t/* 034  \"  */ 8, /* CHR_QUOTE */\n\t/* 035  #  */ 0,\n\t/* 036  $  */ 4, /* CHR_IDENT */\n\t/* 037  %  */ 0,\n\t/* 038  &  */ 0,\n\t/* 039  '  */ 8, /* CHR_QUOTE */\n\t/* 040  (  */ 0,\n\t/* 041  )  */ 0,\n\t/* 042  *  */ 0,\n\t/* 043  +  */ 0,\n\t/* 044  ,  */ 0,\n\t/* 045  -  */ 0,\n\t/* 046  .  */ 0,\n\t/* 047  /  */ 0,\n\t/* 048  0  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 049  1  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 050  2  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 051  3  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 052  4  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 053  5  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 054  6  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 055  7  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 056  8  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 057  9  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 058  :  */ 0,\n\t/* 059  ;  */ 0,\n\t/* 060  <  */ 0,\n\t/* 061  =  */ 0,\n\t/* 062  >  */ 0,\n\t/* 063  ?  */ 0,\n\t/* 064  @  */ 0,\n\t/* 065  A  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 066  B  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 067  C  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 068  D  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 069  E  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 070  F  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 071  G  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 072  H  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 073  I  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 074  J  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 075  K  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 076  L  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 077  M  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 078  N  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 079  O  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 080  P  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 081  Q  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 082  R  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 083  S  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 084  T  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 085  U  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 086  V  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 087  W  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 088  X  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 089  Y  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 090  Z  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 091  [  */ 0,\n\t/* 092  \\  */ 0,\n\t/* 093  ]  */ 0,\n\t/* 094  ^  */ 0,\n\t/* 095  _  */ 68, /* CHR_IDENT | CHR_INTRODUCER */\n\t/* 096  `  */ 0,\n\t/* 097  a  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 098  b  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 099  c  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 100  d  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 101  e  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 102  f  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 103  g  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 104  h  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 105  i  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 106  j  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 107  k  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 108  l  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 109  m  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 110  n  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 111  o  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 112  p  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 113  q  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 114  r  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 115  s  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 116  t  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 117  u  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 118  v  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 119  w  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 120  x  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 121  y  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 122  z  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 123  {  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 124  |  */ 0,\n\t/* 125  }  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 126  ~  */ 0,\n\t/* 127     */ 0\n};\n\nstatic inline char php_firebird_classes(char idx)\n{\n\tunsigned char uidx = (unsigned char) idx;\n\tif (uidx > 127) return 0;\n\treturn classes_array[uidx];\n}\n\ntypedef enum {\n\tttNone,\n\tttWhite,\n\tttComment,\n\tttBrokenComment,\n\tttString,\n\tttParamMark,\n\tttIdent,\n\tttOther\n} FbTokenType;\n\nstatic FbTokenType php_firebird_get_token(const char** begin, const char* end)\n{\n\tFbTokenType ret = ttNone;\n\tconst char* p = *begin;\n\n\tchar c = *p++;\n\tswitch (c)\n\t{\n\tcase ':':\n\tcase '?':\n\t\tret = ttParamMark;\n\t\tbreak;\n\n\tcase '\\'':\n\tcase '\"':\n\t\twhile (p < end)\n\t\t{\n\t\t\tif (*p++ == c)\n\t\t\t{\n\t\t\t\tret = ttString;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase '/':\n\t\tif (p < end && *p == '*')\n\t\t{\n\t\t\tret = ttBrokenComment;\n\t\t\tp++;\n\t\t\twhile (p < end)\n\t\t\t{\n\t\t\t\tif (*p++ == '*' && p < end && *p == '/')\n\t\t\t\t{\n\t\t\t\t\tp++;\n\t\t\t\t\tret = ttComment;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tret = ttOther;\n\t\t}\n\t\tbreak;\n\n\tcase '-':\n\t\tif (p < end && *p == '-')\n\t\t{\n\t\t\twhile (++p < end)\n\t\t\t{\n\t\t\t\tif (*p == '\\r')\n\t\t\t\t{\n\t\t\t\t\tp++;\n\t\t\t\t\tif (p < end && *p == '\\n')\n\t\t\t\t\t\tp++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse if (*p == '\\n')\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tret = ttComment;\n\t\t}\n\t\telse\n\t\t\tret = ttOther;\n\t\tbreak;\n\n\tdefault:\n\t\tif (php_firebird_classes(c) & CHR_DIGIT)\n\t\t{\n\t\t\twhile (p < end && (php_firebird_classes(*p) & CHR_DIGIT))\n\t\t\t\tp++;\n\t\t\tret = ttOther;\n\t\t}\n\t\telse if (php_firebird_classes(c) & CHR_IDENT)\n\t\t{\n\t\t\twhile (p < end && (php_firebird_classes(*p) & CHR_IDENT))\n\t\t\t\tp++;\n\t\t\tret = ttIdent;\n\t\t}\n\t\telse if (php_firebird_classes(c) & CHR_WHITE)\n\t\t{\n\t\t\twhile (p < end && (php_firebird_classes(*p) & CHR_WHITE))\n\t\t\t\tp++;\n\t\t\tret = ttWhite;\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile (p < end && !(php_firebird_classes(*p) & (CHR_DIGIT | CHR_IDENT | CHR_WHITE)) &&\n\t\t\t\t(*p != '/') && (*p != '-') && (*p != ':') && (*p != '?') &&\n\t\t\t\t(*p != '\\'') && (*p != '\"'))\n\t\t\t{\n\t\t\t\tp++;\n\t\t\t}\n\t\t\tret = ttOther;\n\t\t}\n\t}\n\n\t*begin = p;\n\treturn ret;\n}\n\nstatic int php_firebird_preprocess(const zend_string* sql, char* sql_out, HashTable* named_params)\n{\n\tbool passAsIs = 1, execBlock = 0;\n\tzend_long pindex = -1;\n\tchar pname[254], ident[253], ident2[253];\n\tunsigned int l;\n\tconst char* p = ZSTR_VAL(sql), * end = ZSTR_VAL(sql) + ZSTR_LEN(sql);\n\tconst char* start = p;\n\tFbTokenType tok = php_firebird_get_token(&p, end);\n\n\tconst char* i = start;\n\twhile (p < end && (tok == ttComment || tok == ttWhite))\n\t{\n\t\ti = p;\n\t\ttok = php_firebird_get_token(&p, end);\n\t}\n\n\tif (p >= end || tok != ttIdent)\n\t{\n\t\t/* Execute statement preprocess SQL error */\n\t\t/* Statement expected */\n\t\treturn 0;\n\t}\n\t/* skip leading comments ?? */\n\tstart = i;\n\tl = p - i;\n\t/* check the length of the identifier */\n\t/* in Firebird 4.0 it is 63 characters, in previous versions 31 bytes */\n\tif (l > 252) {\n\t\treturn 0;\n\t}\n\tstrncpy(ident, i, l);\n\tident[l] = '\\0';\n\tif (!strcasecmp(ident, \"EXECUTE\"))\n\t{\n\t\t/* For EXECUTE PROCEDURE and EXECUTE BLOCK statements, named parameters must be processed. */\n\t\t/* However, in EXECUTE BLOCK this is done in a special way. */\n\t\tconst char* i2 = p;\n\t\ttok = php_firebird_get_token(&p, end);\n\t\twhile (p < end && (tok == ttComment || tok == ttWhite))\n\t\t{\n\t\t\ti2 = p;\n\t\t\ttok = php_firebird_get_token(&p, end);\n\t\t}\n\t\tif (p >= end || tok != ttIdent)\n\t\t{\n\t\t\t/* Execute statement preprocess SQL error */\n\t\t\t/* Statement expected */\n\t\t\treturn 0;\n\t\t}\n\t\tl = p - i2;\n\t\t/* check the length of the identifier */\n\t\t/* in Firebird 4.0 it is 63 characters, in previous versions 31 bytes */\n\t\tif (l > 252) {\n\t\t\treturn 0;\n\t\t}\n\t\tstrncpy(ident2, i2, l);\n\t\tident2[l] = '\\0';\n\t\texecBlock = !strcasecmp(ident2, \"BLOCK\");\n\t\tpassAsIs = 0;\n\t}\n\telse\n\t{\n\t\t/* Named parameters must be processed in the INSERT, UPDATE, DELETE, MERGE statements. */\n\t\t/* If CTEs are present in the query, they begin with the WITH keyword. */\n\t\tpassAsIs = strcasecmp(ident, \"INSERT\") && strcasecmp(ident, \"UPDATE\") &&\n\t\t\tstrcasecmp(ident, \"DELETE\") && strcasecmp(ident, \"MERGE\") &&\n\t\t\tstrcasecmp(ident, \"SELECT\") && strcasecmp(ident, \"WITH\");\n\t}\n\n\tif (passAsIs)\n\t{\n\t\tstrcpy(sql_out, ZSTR_VAL(sql));\n\t\treturn 1;\n\t}\n\n\tstrncat(sql_out, start, p - start);\n\n\twhile (p < end)\n\t{\n\t\tstart = p;\n\t\ttok = php_firebird_get_token(&p, end);\n\t\tswitch (tok)\n\t\t{\n\t\tcase ttParamMark:\n\t\t\ttok = php_firebird_get_token(&p, end);\n\t\t\tif (tok == ttIdent /*|| tok == ttString*/)\n\t\t\t{\n\t\t\t\t++pindex;\n\t\t\t\tl = p - start;\n\t\t\t\t/* check the length of the identifier */\n\t\t\t\t/* in Firebird 4.0 it is 63 characters, in previous versions 31 bytes */\n\t\t\t\t/* + symbol \":\" */\n\t\t\t\tif (l > 253) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tstrncpy(pname, start, l);\n\t\t\t\tpname[l] = '\\0';\n\n\t\t\t\tif (named_params) {\n\t\t\t\t\tzval tmp;\n\t\t\t\t\tZVAL_LONG(&tmp, pindex);\n\t\t\t\t\tzend_hash_str_update(named_params, pname, l, &tmp);\n\t\t\t\t}\n\n\t\t\t\tstrcat(sql_out, \"?\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (strncmp(start, \"?\", 1)) {\n\t\t\t\t\t/* Execute statement preprocess SQL error */\n\t\t\t\t\t/* Parameter name expected */\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\t++pindex;\n\t\t\t\tstrncat(sql_out, start, p - start);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase ttIdent:\n\t\t\tif (execBlock)\n\t\t\t{\n\t\t\t\t/* In the EXECUTE BLOCK statement, processing must be */\n\t\t\t\t/* carried out up to the keyword AS. */\n\t\t\t\tl = p - start;\n\t\t\t\t/* check the length of the identifier */\n\t\t\t\t/* in Firebird 4.0 it is 63 characters, in previous versions 31 bytes */\n\t\t\t\tif (l > 252) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tstrncpy(ident, start, l);\n\t\t\t\tident[l] = '\\0';\n\t\t\t\tif (!strcasecmp(ident, \"AS\"))\n\t\t\t\t{\n\t\t\t\t\tstrncat(sql_out, start, end - start);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\t/* TODO Check this is correct? */\n\t\t\tZEND_FALLTHROUGH;\n\n\t\tcase ttWhite:\n\t\tcase ttComment:\n\t\tcase ttString:\n\t\tcase ttOther:\n\t\t\tstrncat(sql_out, start, p - start);\n\t\t\tbreak;\n\n\t\tcase ttBrokenComment:\n\t\t{\n\t\t\t/* Execute statement preprocess SQL error */\n\t\t\t/* Unclosed comment found near ''@1'' */\n\t\t\treturn 0;\n\t\t}\n\t\tbreak;\n\n\n\t\tcase ttNone:\n\t\t\t/* Execute statement preprocess SQL error */\n\t\t\treturn 0;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn 1;\n}\n\n#if FB_API_VER >= 40\n/* set coercing a data type */\nstatic void set_coercing_output_data_types(XSQLDA* sqlda)\n{\n\t/* Data types introduced in Firebird 4.0 are difficult to process using the Firebird Legacy API. */\n\t/* These data types include DECFLOAT(16), DECFLOAT(34), INT128 (NUMERIC/DECIMAL(38, x)). */\n\t/* In any case, at this data types can only be mapped to strings. */\n\t/* This function allows you to ensure minimal performance of queries if they contain columns of the above types. */\n\tunsigned int i;\n\tshort dtype;\n\tshort nullable;\n\tXSQLVAR* var;\n\tunsigned fb_client_version = fb_get_client_version();\n\tunsigned fb_client_major_version = (fb_client_version >> 8) & 0xFF;\n\tfor (i=0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++) {\n\t\tdtype = (var->sqltype & ~1); /* drop flag bit  */\n\t\tnullable = (var->sqltype & 1);\n\t\tswitch(dtype) {\n\t\t\tcase SQL_INT128:\n\t\t\t\tvar->sqltype = SQL_VARYING + nullable;\n\t\t\t\tvar->sqllen = 46;\n\t\t\t\tvar->sqlscale = 0;\n\t\t\t\tbreak;\n\n\t\t\tcase SQL_DEC16:\n\t\t\t\tvar->sqltype = SQL_VARYING + nullable;\n\t\t\t\tvar->sqllen = 24;\n\t\t\t\tbreak;\n\n\t\t\tcase SQL_DEC34:\n\t\t\t\tvar->sqltype = SQL_VARYING + nullable;\n\t\t\t\tvar->sqllen = 43;\n\t\t\t\tbreak;\n\n\t\t\tcase SQL_TIMESTAMP_TZ:\n\t\t\t    if (fb_client_major_version < 4) {\n\t\t\t\t\t/* If the client version is below 4.0, then it is impossible to handle time zones natively, */\n\t\t\t\t\t/* so we convert these types to a string. */\n\t\t\t\t\tvar->sqltype = SQL_VARYING + nullable;\n\t\t\t\t\tvar->sqllen = 58;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase SQL_TIME_TZ:\n\t\t\t\tif (fb_client_major_version < 4) {\n\t\t\t\t\t/* If the client version is below 4.0, then it is impossible to handle time zones natively, */\n\t\t\t\t\t/* so we convert these types to a string. */\n\t\t\t\t\tvar->sqltype = SQL_VARYING + nullable;\n\t\t\t\t\tvar->sqllen = 46;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n#endif\n\n/* map driver specific error message to PDO error */\nvoid php_firebird_set_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *state, const size_t state_len,\n\tconst char *msg, const size_t msg_len) /* {{{ */\n{\n\tpdo_error_type *const error_code = stmt ? &stmt->error_code : &dbh->error_code;\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\tpdo_firebird_error_info *einfo = &H->einfo;\n\tint sqlcode = -999;\n\n\tif (einfo->errmsg) {\n\t\tpefree(einfo->errmsg, dbh->is_persistent);\n\t\teinfo->errmsg = NULL;\n\t\teinfo->errmsg_length = 0;\n\t}\n\n\tif (H->isc_status && (H->isc_status[0] == 1 && H->isc_status[1] > 0)) {\n\t\tchar buf[512];\n\t\tsize_t buf_size = sizeof(buf), read_len = 0;\n\t\tssize_t tmp_len;\n\t\tconst ISC_STATUS *s = H->isc_status;\n\t\tsqlcode = isc_sqlcode(s);\n\n\t\twhile ((buf_size > (read_len + 1)) && (tmp_len = fb_interpret(&buf[read_len], (buf_size - read_len - 1), &s)) && tmp_len > 0) {\n\t\t\tread_len += tmp_len;\n\t\t\tbuf[read_len++] = ' ';\n\t\t}\n\n\t\t/* remove last space */\n\t\tif (read_len) {\n\t\t\tbuf[read_len--] = '\\0';\n\t\t}\n\n\t\teinfo->errmsg_length = read_len;\n\t\teinfo->errmsg = pestrndup(buf, read_len, dbh->is_persistent);\n\n\t\tchar sqlstate[sizeof(pdo_error_type)];\n\t\tfb_sqlstate(sqlstate, H->isc_status);\n\t\tif (sqlstate != NULL && strlen(sqlstate) < sizeof(pdo_error_type)) {\n\t\t\tstrcpy(*error_code, sqlstate);\n\t\t\tgoto end;\n\t\t}\n\t} else if (msg && msg_len) {\n\t\teinfo->errmsg_length = msg_len;\n\t\teinfo->errmsg = pestrndup(msg, einfo->errmsg_length, dbh->is_persistent);\n\t}\n\n\tif (state && state_len && state_len < sizeof(pdo_error_type)) {\n\t\tmemcpy(*error_code, state, state_len + 1);\n\t} else {\n\t\tmemcpy(*error_code, \"HY000\", sizeof(\"HY000\"));\n\t}\n\nend:\n\teinfo->sqlcode = sqlcode;\n\tif (!dbh->methods) {\n\t\tpdo_throw_exception(0, einfo->errmsg, error_code);\n\t}\n}\n/* }}} */\n\n/* called by PDO to close a db handle */\nstatic void firebird_handle_closer(pdo_dbh_t *dbh) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\n\tif (H->tr) {\n\t\tif (dbh->auto_commit) {\n\t\t\tphp_firebird_commit_transaction(dbh, /* retain */ false);\n\t\t} else {\n\t\t\tphp_firebird_rollback_transaction(dbh);\n\t\t}\n\t}\n\tH->in_manually_txn = 0;\n\n\t/* isc_detach_database returns 0 on success, 1 on failure. */\n\tif (H->db && isc_detach_database(H->isc_status, &H->db)) {\n\t\tphp_firebird_error(dbh);\n\t}\n\n\tif (H->date_format) {\n\t\tpefree(H->date_format, dbh->is_persistent);\n\t}\n\tif (H->time_format) {\n\t\tpefree(H->time_format, dbh->is_persistent);\n\t}\n\tif (H->timestamp_format) {\n\t\tpefree(H->timestamp_format, dbh->is_persistent);\n\t}\n\n\tif (H->einfo.errmsg) {\n\t\tpefree(H->einfo.errmsg, dbh->is_persistent);\n\t\tH->einfo.errmsg = NULL;\n\t}\n\n\tpefree(H, dbh->is_persistent);\n}\n/* }}} */\n\n/* called by PDO to prepare an SQL query */\nstatic bool firebird_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, /* {{{ */\n\tpdo_stmt_t *stmt, zval *driver_options)\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\tpdo_firebird_stmt *S = NULL;\n\tHashTable *np;\n\n\tdo {\n\t\tisc_stmt_handle s = PDO_FIREBIRD_HANDLE_INITIALIZER;\n\t\tXSQLDA num_sqlda;\n\t\tstatic char const info[] = { isc_info_sql_stmt_type };\n\t\tchar result[8];\n\n\t\tnum_sqlda.version = PDO_FB_SQLDA_VERSION;\n\t\tnum_sqlda.sqln = 1;\n\n\t\tALLOC_HASHTABLE(np);\n\t\tzend_hash_init(np, 8, NULL, NULL, 0);\n\n\t\t/* allocate and prepare statement */\n\t\tif (!php_firebird_alloc_prepare_stmt(dbh, sql, &num_sqlda, &s, np)) {\n\t\t\tbreak;\n\t\t}\n\n\t\t/* allocate a statement handle struct of the right size (struct out_sqlda is inlined) */\n\t\tS = ecalloc(1, sizeof(*S)-sizeof(XSQLDA) + XSQLDA_LENGTH(num_sqlda.sqld));\n\t\tS->H = H;\n\t\tS->stmt = s;\n\t\tS->out_sqlda.version = PDO_FB_SQLDA_VERSION;\n\t\tS->out_sqlda.sqln = stmt->column_count = num_sqlda.sqld;\n\t\tS->named_params = np;\n\n\t\t/* determine the statement type */\n\t\tif (isc_dsql_sql_info(H->isc_status, &s, sizeof(info), const_cast(info), sizeof(result),\n\t\t\t\tresult)) {\n\t\t\tbreak;\n\t\t}\n\t\tS->statement_type = result[3];\n\n\t\t/* fill the output sqlda with information about the prepared query */\n\t\tif (isc_dsql_describe(H->isc_status, &s, PDO_FB_SQLDA_VERSION, &S->out_sqlda)) {\n\t\t\tphp_firebird_error(dbh);\n\t\t\tbreak;\n\t\t}\n\n#if FB_API_VER >= 40\n\t\t/* set coercing a data type */\n\t\tset_coercing_output_data_types(&S->out_sqlda);\n#endif\n\n\t\t/* allocate the input descriptors */\n\t\tif (isc_dsql_describe_bind(H->isc_status, &s, PDO_FB_SQLDA_VERSION, &num_sqlda)) {\n\t\t\tbreak;\n\t\t}\n\n\t\tif (num_sqlda.sqld) {\n\t\t\tS->in_sqlda = ecalloc(1,XSQLDA_LENGTH(num_sqlda.sqld));\n\t\t\tS->in_sqlda->version = PDO_FB_SQLDA_VERSION;\n\t\t\tS->in_sqlda->sqln = num_sqlda.sqld;\n\n\t\t\tif (isc_dsql_describe_bind(H->isc_status, &s, PDO_FB_SQLDA_VERSION, S->in_sqlda)) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* make all parameters nullable */\n\t\t\tunsigned int i;\n\t\t\tXSQLVAR* var;\n\t\t\tfor (i = 0, var = S->in_sqlda->sqlvar; i < S->in_sqlda->sqld; i++, var++) {\n\t\t\t\t/* The low bit of sqltype indicates that the parameter can take a NULL value */\n\t\t\t\tvar->sqltype |= 1;\n\t\t\t}\n\t\t}\n\n\t\tstmt->driver_data = S;\n\t\tstmt->methods = &firebird_stmt_methods;\n\t\tstmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL;\n\n\t\treturn true;\n\n\t} while (0);\n\n\tphp_firebird_error(dbh);\n\n\tzend_hash_destroy(np);\n\tFREE_HASHTABLE(np);\n\n\tif (S) {\n\t\tif (S->in_sqlda) {\n\t\t\tefree(S->in_sqlda);\n\t\t}\n\t\tefree(S);\n\t}\n\n\treturn false;\n}\n/* }}} */\n\n/* called by PDO to execute a statement that doesn't produce a result set */\nstatic zend_long firebird_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\tisc_stmt_handle stmt = PDO_FIREBIRD_HANDLE_INITIALIZER;\n\tstatic char const info_count[] = { isc_info_sql_records };\n\tchar result[64];\n\tint ret = 0;\n\tXSQLDA in_sqlda, out_sqlda;\n\n\t/* TODO no placeholders in exec() for now */\n\tin_sqlda.version = out_sqlda.version = PDO_FB_SQLDA_VERSION;\n\tin_sqlda.sqld = out_sqlda.sqld = 0;\n\tout_sqlda.sqln = 1;\n\n\t/* allocate and prepare statement */\n\tif (!php_firebird_alloc_prepare_stmt(dbh, sql, &out_sqlda, &stmt, 0)) {\n\t\treturn -1;\n\t}\n\n\t/* execute the statement */\n\tif (isc_dsql_execute2(H->isc_status, &H->tr, &stmt, PDO_FB_SQLDA_VERSION, &in_sqlda, &out_sqlda)) {\n\t\tphp_firebird_error(dbh);\n\t\tret = -1;\n\t\tgoto free_statement;\n\t}\n\n\t/* find out how many rows were affected */\n\tif (isc_dsql_sql_info(H->isc_status, &stmt, sizeof(info_count), const_cast(info_count),\n\t\t\tsizeof(result),\tresult)) {\n\t\tphp_firebird_error(dbh);\n\t\tret = -1;\n\t\tgoto free_statement;\n\t}\n\n\tif (result[0] == isc_info_sql_records) {\n\t\tunsigned i = 3, result_size = isc_vax_integer(&result[1],2);\n\n\t\tif (result_size > sizeof(result)) {\n\t\t\tret = -1;\n\t\t\tgoto free_statement;\n\t\t}\n\t\twhile (result[i] != isc_info_end && i < result_size) {\n\t\t\tshort len = (short)isc_vax_integer(&result[i+1],2);\n\t\t\t/* bail out on bad len */\n\t\t\tif (len != 1 && len != 2 && len != 4) {\n\t\t\t\tret = -1;\n\t\t\t\tgoto free_statement;\n\t\t\t}\n\t\t\tif (result[i] != isc_info_req_select_count) {\n\t\t\t\tret += isc_vax_integer(&result[i+3],len);\n\t\t\t}\n\t\t\ti += len+3;\n\t\t}\n\t}\n\n\tif (dbh->auto_commit && !H->in_manually_txn) {\n\t\tif (!php_firebird_commit_transaction(dbh, /* retain */ true)) {\n\t\t\tret = -1;\n\t\t}\n\t}\n\nfree_statement:\n\n\tif (isc_dsql_free_statement(H->isc_status, &stmt, DSQL_drop)) {\n\t\tphp_firebird_error(dbh);\n\t}\n\n\treturn ret;\n}\n/* }}} */\n\n/* called by the PDO SQL parser to add quotes to values that are copied into SQL */\nstatic zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype)\n{\n\tsize_t qcount = 0;\n\tchar const *co, *l, *r;\n\tchar *c;\n\tsize_t quotedlen;\n\tzend_string *quoted_str;\n\n\tif (ZSTR_LEN(unquoted) == 0) {\n\t\treturn ZSTR_INIT_LITERAL(\"''\", 0);\n\t}\n\n\t/* Firebird only requires single quotes to be doubled if string lengths are used */\n\t/* count the number of ' characters */\n\tfor (co = ZSTR_VAL(unquoted); (co = strchr(co,'\\'')); qcount++, co++);\n\n\tif (UNEXPECTED(ZSTR_LEN(unquoted) + 2 > ZSTR_MAX_LEN - qcount)) {\n\t\treturn NULL;\n\t}\n\n\tquotedlen = ZSTR_LEN(unquoted) + qcount + 2;\n\tquoted_str = zend_string_alloc(quotedlen, 0);\n\tc = ZSTR_VAL(quoted_str);\n\t*c++ = '\\'';\n\n\t/* foreach (chunk that ends in a quote) */\n\tfor (l = ZSTR_VAL(unquoted); (r = strchr(l,'\\'')); l = r+1) {\n\t\tstrncpy(c, l, r-l+1);\n\t\tc += (r-l+1);\n\t\t/* add the second quote */\n\t\t*c++ = '\\'';\n\t}\n\n\t/* copy the remainder */\n\tstrncpy(c, l, quotedlen-(c-ZSTR_VAL(quoted_str))-1);\n\tZSTR_VAL(quoted_str)[quotedlen-1] = '\\'';\n\tZSTR_VAL(quoted_str)[quotedlen]   = '\\0';\n\n\treturn quoted_str;\n}\n/* }}} */\n\n/* php_firebird_begin_transaction */\nstatic bool php_firebird_begin_transaction(pdo_dbh_t *dbh, bool is_auto_commit_txn) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\n\t/* isc_xxx are all 1 byte. */\n\tchar tpb[4] = { isc_tpb_version3 };\n\tsize_t tpb_size;\n\n\t/* access mode. writable or readonly */\n\ttpb[1] = H->is_writable_txn ? isc_tpb_write : isc_tpb_read;\n\n\tif (is_auto_commit_txn) {\n\t\t/*\n\t\t * In autocommit mode, we need to always read the latest information, so we set `read committed`.\n\t\t */\n\t\ttpb[2] = isc_tpb_read_committed;\n\t\t/* Ignore indeterminate data from other transactions. This option only required with `read committed`. */\n\t\ttpb[3] = isc_tpb_rec_version;\n\t\ttpb_size = 4;\n\t} else {\n\t\tswitch (H->txn_isolation_level) {\n\t\t\t/*\n\t\t\t* firebird's `read committed` has the option to wait until other transactions\n\t\t\t* commit or rollback if there is indeterminate data.\n\t\t\t* Introducing too many configuration values at once can cause confusion, so\n\t\t\t* we don't support in PDO that feature yet.\n\t\t\t*/\n\t\t\tcase PDO_FB_READ_COMMITTED:\n\t\t\t\ttpb[2] = isc_tpb_read_committed;\n\t\t\t\t/* Ignore indeterminate data from other transactions. This option only required with `read committed`. */\n\t\t\t\ttpb[3] = isc_tpb_rec_version;\n\t\t\t\ttpb_size = 4;\n\t\t\t\tbreak;\n\n\t\t\tcase PDO_FB_SERIALIZABLE:\n\t\t\t\ttpb[2] = isc_tpb_consistency;\n\t\t\t\ttpb_size = 3;\n\t\t\t\tbreak;\n\n\t\t\tcase PDO_FB_REPEATABLE_READ:\n\t\t\tdefault:\n\t\t\t\ttpb[2] = isc_tpb_concurrency;\n\t\t\t\ttpb_size = 3;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (isc_start_transaction(H->isc_status, &H->tr, 1, &H->db, tpb_size, tpb)) {\n\t\tphp_firebird_error(dbh);\n\t\treturn false;\n\t}\n\treturn true;\n}\n/* }}} */\n\n/* called by PDO to start a transaction */\nstatic bool firebird_handle_manually_begin(pdo_dbh_t *dbh) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\n\t/**\n\t * If in autocommit mode and in transaction, we will need to close the transaction once.\n\t */\n\tif (dbh->auto_commit && H->tr) {\n\t\tif (!php_firebird_commit_transaction(dbh, /* retain */ false)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (!php_firebird_begin_transaction(dbh, /* auto commit mode */ false)) {\n\t\treturn false;\n\t}\n\tH->in_manually_txn = 1;\n\treturn true;\n}\n/* }}} */\n\n/* php_firebird_commit_transaction */\nbool php_firebird_commit_transaction(pdo_dbh_t *dbh, bool retain) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\n\t/**\n\t * `retaining` keeps the transaction open without closing it.\n\t *\n\t * firebird needs to always have a transaction open to emulate autocommit mode,\n\t * and in autocommit mode it keeps the transaction open.\n\t *\n\t * Same as close and then begin again, but use retain to save overhead.\n\t */\n\tif (retain) {\n\t\tif (isc_commit_retaining(H->isc_status, &H->tr)) {\n\t\t\tphp_firebird_error(dbh);\n\t\t\treturn false;\n\t\t}\n\t} else {\n\t\tif (isc_commit_transaction(H->isc_status, &H->tr)) {\n\t\t\tphp_firebird_error(dbh);\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n/* }}} */\n\n/* called by PDO to commit a transaction */\nstatic bool firebird_handle_manually_commit(pdo_dbh_t *dbh) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\tif (!php_firebird_commit_transaction(dbh, /*release*/ false)) {\n\t\treturn false;\n\t}\n\n\t/**\n\t * If in autocommit mode, begin the transaction again\n\t * Reopen instead of retain because isolation level may change\n\t */\n\tif (dbh->auto_commit) {\n\t\tif (!php_firebird_begin_transaction(dbh, /* auto commit mode */ true)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\tH->in_manually_txn = 0;\n\treturn true;\n}\n/* }}} */\n\n/* php_firebird_rollback_transaction */\nstatic bool php_firebird_rollback_transaction(pdo_dbh_t *dbh) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\n\tif (isc_rollback_transaction(H->isc_status, &H->tr)) {\n\t\tphp_firebird_error(dbh);\n\t\treturn false;\n\t}\n\treturn true;\n}\n/* }}} */\n\n/* called by PDO to rollback a transaction */\nstatic bool firebird_handle_manually_rollback(pdo_dbh_t *dbh) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\n\tif (!php_firebird_rollback_transaction(dbh)) {\n\t\treturn false;\n\t}\n\n\t/**\n\t * If in autocommit mode, begin the transaction again\n\t * Reopen instead of retain because isolation level may change\n\t */\n\tif (dbh->auto_commit) {\n\t\tif (!php_firebird_begin_transaction(dbh, /* auto commit mode */ true)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\tH->in_manually_txn = 0;\n\treturn true;\n}\n/* }}} */\n\n/* used by prepare and exec to allocate a statement handle and prepare the SQL */\nstatic int php_firebird_alloc_prepare_stmt(pdo_dbh_t *dbh, const zend_string *sql,\n\tXSQLDA *out_sqlda, isc_stmt_handle *s, HashTable *named_params)\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\tchar *new_sql;\n\n\t/* allocate the statement */\n\tif (isc_dsql_allocate_statement(H->isc_status, &H->db, s)) {\n\t\tphp_firebird_error(dbh);\n\t\treturn 0;\n\t}\n\n\t/* in order to support named params, which Firebird itself doesn't,\n\t   we need to replace :foo by ?, and store the name we just replaced */\n\tnew_sql = emalloc(ZSTR_LEN(sql)+1);\n\tnew_sql[0] = '\\0';\n\tif (!php_firebird_preprocess(sql, new_sql, named_params)) {\n\t\tphp_firebird_error_with_info(dbh, \"07000\", strlen(\"07000\"), NULL, 0);\n\t\tefree(new_sql);\n\t\treturn 0;\n\t}\n\n\t/* prepare the statement */\n\tif (isc_dsql_prepare(H->isc_status, &H->tr, s, 0, new_sql, H->sql_dialect, out_sqlda)) {\n\t\tphp_firebird_error(dbh);\n\t\tefree(new_sql);\n\t\treturn 0;\n\t}\n\n\tefree(new_sql);\n\treturn 1;\n}\n\n/* called by PDO to set a driver-specific dbh attribute */\nstatic bool pdo_firebird_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\tbool bval;\n\tzend_long lval;\n\n\tswitch (attr) {\n\t\tcase PDO_ATTR_AUTOCOMMIT:\n\t\t\t{\n\t\t\t\tif (!pdo_get_bool_param(&bval, val)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (H->in_manually_txn) {\n\t\t\t\t\t/* change auto commit mode with an open transaction is illegal, because\n\t\t\t\t\t\twe won't know what to do with it */\n\t\t\t\t\tpdo_raise_impl_error(dbh, NULL, \"HY000\", \"Cannot change autocommit mode while a transaction is already open\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t/* ignore if the new value equals the old one */\n\t\t\t\tif (dbh->auto_commit ^ bval) {\n\t\t\t\t\tif (bval) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * change to auto commit mode.\n\t\t\t\t\t\t * If the transaction is not started, start it.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tif (!H->tr) {\n\t\t\t\t\t\t\tif (!php_firebird_begin_transaction(dbh, /* auto commit mode */ true)) {\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * change to not auto commit mode.\n\t\t\t\t\t\t * close the transaction if exists.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tif (H->tr) {\n\t\t\t\t\t\t\tif (!php_firebird_commit_transaction(dbh, /* retain */ false)) {\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdbh->auto_commit = bval;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\n\t\tcase PDO_ATTR_FETCH_TABLE_NAMES:\n\t\t\tif (!pdo_get_bool_param(&bval, val)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tH->fetch_table_names = bval;\n\t\t\treturn true;\n\n\t\tcase PDO_FB_ATTR_DATE_FORMAT:\n\t\t\t{\n\t\t\t\tzend_string *str = zval_try_get_string(val);\n\t\t\t\tif (UNEXPECTED(!str)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (H->date_format) {\n\t\t\t\t\tpefree(H->date_format, dbh->is_persistent);\n\t\t\t\t\tH->date_format = NULL;\n\t\t\t\t}\n\t\t\t\tH->date_format = pestrndup(ZSTR_VAL(str), ZSTR_LEN(str),dbh->is_persistent);\n\t\t\t\tzend_string_release_ex(str, 0);\n\t\t\t}\n\t\t\treturn true;\n\n\t\tcase PDO_FB_ATTR_TIME_FORMAT:\n\t\t\t{\n\t\t\t\tzend_string *str = zval_try_get_string(val);\n\t\t\t\tif (UNEXPECTED(!str)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (H->time_format) {\n\t\t\t\t\tpefree(H->time_format, dbh->is_persistent);\n\t\t\t\t\tH->time_format = NULL;\n\t\t\t\t}\n\t\t\t\tH->time_format = pestrndup(ZSTR_VAL(str), ZSTR_LEN(str),dbh->is_persistent);\n\t\t\t\tzend_string_release_ex(str, 0);\n\t\t\t}\n\t\t\treturn true;\n\n\t\tcase PDO_FB_ATTR_TIMESTAMP_FORMAT:\n\t\t\t{\n\t\t\t\tzend_string *str = zval_try_get_string(val);\n\t\t\t\tif (UNEXPECTED(!str)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (H->timestamp_format) {\n\t\t\t\t\tpefree(H->timestamp_format, dbh->is_persistent);\n\t\t\t\t\tH->timestamp_format = NULL;\n\t\t\t\t}\n\t\t\t\tH->timestamp_format = pestrndup(ZSTR_VAL(str), ZSTR_LEN(str),dbh->is_persistent);\n\t\t\t\tzend_string_release_ex(str, 0);\n\t\t\t}\n\t\t\treturn true;\n\n\t\tcase PDO_FB_TRANSACTION_ISOLATION_LEVEL:\n\t\t\t{\n\t\t\t\tif (!pdo_get_long_param(&lval, val)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (H->in_manually_txn) {\n\t\t\t\t\tpdo_raise_impl_error(dbh, NULL, \"HY000\", \"Cannot change transaction isolation level while a transaction is already open\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t/* ignore if the new value equals the old one */\n\t\t\t\tif (H->txn_isolation_level != lval) {\n\t\t\t\t\tif (lval == PDO_FB_READ_COMMITTED ||\n\t\t\t\t\t\tlval == PDO_FB_REPEATABLE_READ ||\n\t\t\t\t\t\tlval == PDO_FB_SERIALIZABLE\n\t\t\t\t\t) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Autocommit mode is always read-committed, so this setting is used the next time\n\t\t\t\t\t\t * a manual transaction starts. Therefore, there is no need to immediately reopen the transaction.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tH->txn_isolation_level = lval;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tzend_value_error(\"Pdo\\\\Firebird::TRANSACTION_ISOLATION_LEVEL must be a valid transaction isolation level \"\n\t\t\t\t\t\t\t\"(Pdo\\\\Firebird::READ_COMMITTED, Pdo\\\\Firebird::REPEATABLE_READ, or Pdo\\\\Firebird::SERIALIZABLE)\");\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\n\t\tcase PDO_FB_WRITABLE_TRANSACTION:\n\t\t\t{\n\t\t\t\tif (!pdo_get_bool_param(&bval, val)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (H->in_manually_txn) {\n\t\t\t\t\tpdo_raise_impl_error(dbh, NULL, \"HY000\", \"Cannot change access mode while a transaction is already open\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t/* ignore if the new value equals the old one */\n\t\t\t\tif (H->is_writable_txn != bval) {\n\t\t\t\t\tH->is_writable_txn = bval;\n\t\t\t\t\tif (dbh->auto_commit) {\n\t\t\t\t\t\tif (H->tr) {\n\t\t\t\t\t\t\tif (!php_firebird_commit_transaction(dbh, /* retain */ false)) {\n\t\t\t\t\t\t\t\t/* In case of error, revert the setting */\n\t\t\t\t\t\t\t\tH->is_writable_txn = !bval;\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!php_firebird_begin_transaction(dbh, /* auto commit mode */ true)) {\n\t\t\t\t\t\t\t/* In case of error, revert the setting */\n\t\t\t\t\t\t\tH->is_writable_txn = !bval;\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n/* }}} */\n\n#define INFO_BUF_LEN 512\n\n/* callback to used to report database server info */\nstatic void php_firebird_info_cb(void *arg, char const *s) /* {{{ */\n{\n\tif (arg) {\n\t\tif (*(char*)arg) { /* second call */\n\t\t\tstrlcat(arg, \" \", INFO_BUF_LEN);\n\t\t}\n\t\tstrlcat(arg, s, INFO_BUF_LEN);\n\t}\n}\n/* }}} */\n\n/* called by PDO to get a driver-specific dbh attribute */\nstatic int pdo_firebird_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\n\tswitch (attr) {\n\t\tchar tmp[INFO_BUF_LEN];\n\n\t\tcase PDO_ATTR_AUTOCOMMIT:\n\t\t\tZVAL_BOOL(val,dbh->auto_commit);\n\t\t\treturn 1;\n\n\t\tcase PDO_ATTR_CONNECTION_STATUS:\n\t\t\tZVAL_BOOL(val, !isc_version(&H->db, php_firebird_info_cb, NULL));\n\t\t\treturn 1;\n\n\t\tcase PDO_ATTR_CLIENT_VERSION: {\n\t\t\t\tisc_get_client_version(tmp);\n\t\t\t\tZVAL_STRING(val, tmp);\n\t\t\t}\n\t\t\treturn 1;\n\n\t\tcase PDO_ATTR_SERVER_VERSION:\n\t\tcase PDO_ATTR_SERVER_INFO:\n\t\t\t*tmp = 0;\n\n\t\t\tif (!isc_version(&H->db, php_firebird_info_cb, (void*)tmp)) {\n\t\t\t\tZVAL_STRING(val, tmp);\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\treturn -1;\n\n\t\tcase PDO_ATTR_FETCH_TABLE_NAMES:\n\t\t\tZVAL_BOOL(val, H->fetch_table_names);\n\t\t\treturn 1;\n\n\t\tcase PDO_FB_ATTR_DATE_FORMAT:\n\t\t\tZVAL_STRING(val, H->date_format ? H->date_format : PDO_FB_DEF_DATE_FMT);\n\t\t\treturn 1;\n\n\t\tcase PDO_FB_ATTR_TIME_FORMAT:\n\t\t\tZVAL_STRING(val, H->time_format ? H->time_format : PDO_FB_DEF_TIME_FMT);\n\t\t\treturn 1;\n\n\t\tcase PDO_FB_ATTR_TIMESTAMP_FORMAT:\n\t\t\tZVAL_STRING(val, H->timestamp_format ? H->timestamp_format : PDO_FB_DEF_TIMESTAMP_FMT);\n\t\t\treturn 1;\n\n\t\tcase PDO_FB_TRANSACTION_ISOLATION_LEVEL:\n\t\t\tZVAL_LONG(val, H->txn_isolation_level);\n\t\t\treturn 1;\n\n\t\tcase PDO_FB_WRITABLE_TRANSACTION:\n\t\t\tZVAL_BOOL(val, H->is_writable_txn);\n\t\t\treturn 1;\n\t}\n\treturn 0;\n}\n/* }}} */\n\n/* called by PDO to check liveness */\nstatic zend_result pdo_firebird_check_liveness(pdo_dbh_t *dbh) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\n\t/* fb_ping return 0 if the connection is alive */\n\treturn fb_ping(H->isc_status, &H->db) ? FAILURE : SUCCESS;\n}\n/* }}} */\n\n/* called by PDO to retrieve driver-specific information about an error that has occurred */\nstatic void pdo_firebird_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\tif (H->einfo.sqlcode != IS_NULL) {\n\t\tadd_next_index_long(info, H->einfo.sqlcode);\n\t}\n\tif (H->einfo.errmsg && H->einfo.errmsg_length) {\n\t\tadd_next_index_stringl(info, H->einfo.errmsg, H->einfo.errmsg_length);\n\t}\n}\n/* }}} */\n\n/* {{{ firebird_in_manually_transaction */\nstatic bool pdo_firebird_in_manually_transaction(pdo_dbh_t *dbh)\n{\n\t/**\n\t * we can tell if a transaction exists now by checking H->tr,\n\t * but which will always be true in autocommit mode.\n\t * So this function checks if there is currently a \"manually begun transaction\".\n\t */\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\treturn H->in_manually_txn;\n}\n/* }}} */\n\nstatic const struct pdo_dbh_methods firebird_methods = { /* {{{ */\n\tfirebird_handle_closer,\n\tfirebird_handle_preparer,\n\tfirebird_handle_doer,\n\tfirebird_handle_quoter,\n\tfirebird_handle_manually_begin,\n\tfirebird_handle_manually_commit,\n\tfirebird_handle_manually_rollback,\n\tpdo_firebird_set_attribute,\n\tNULL, /* last_id not supported */\n\tpdo_firebird_fetch_error_func,\n\tpdo_firebird_get_attribute,\n\tpdo_firebird_check_liveness,\n\tNULL, /* get driver methods */\n\tNULL, /* request shutdown */\n\tpdo_firebird_in_manually_transaction,\n\tNULL, /* get gc */\n#if PHP_VERSION_ID >= 80400\n\tNULL /* scanner */\n#endif\n};\n/* }}} */\n\n/* the driver-specific PDO handle constructor */\nstatic int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */\n{\n\tstruct pdo_data_src_parser vars[] = {\n\t\t{ \"dbname\", NULL, 0 },\n\t\t{ \"charset\",  NULL,\t0 },\n\t\t{ \"role\", NULL,\t0 },\n\t\t{ \"dialect\", \"3\", 0 },\n\t\t{ \"user\", NULL, 0 },\n\t\t{ \"password\", NULL, 0 }\n\t};\n\tint i, ret = 0;\n\tshort buf_len = 256, dpb_len;\n\n\tpdo_firebird_db_handle *H = dbh->driver_data = pecalloc(1,sizeof(*H),dbh->is_persistent);\n\n\tphp_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 6);\n\n\tif (!dbh->username && vars[4].optval) {\n\t\tdbh->username = pestrdup(vars[4].optval, dbh->is_persistent);\n\t}\n\n\tif (!dbh->password && vars[5].optval) {\n\t\tdbh->password = pestrdup(vars[5].optval, dbh->is_persistent);\n\t}\n\n\tH->in_manually_txn = 0;\n\tH->is_writable_txn = pdo_attr_lval(driver_options, PDO_FB_WRITABLE_TRANSACTION, 1);\n\tzend_long txn_isolation_level = pdo_attr_lval(driver_options, PDO_FB_TRANSACTION_ISOLATION_LEVEL, PDO_FB_REPEATABLE_READ);\n\tif (txn_isolation_level == PDO_FB_READ_COMMITTED ||\n\t\ttxn_isolation_level == PDO_FB_REPEATABLE_READ ||\n\t\ttxn_isolation_level == PDO_FB_SERIALIZABLE\n\t) {\n\t\tH->txn_isolation_level = txn_isolation_level;\n\t} else {\n\t\tzend_value_error(\"Pdo\\\\Firebird::TRANSACTION_ISOLATION_LEVEL must be a valid transaction isolation level \"\n\t\t\t\"(Pdo\\\\Firebird::READ_COMMITTED, Pdo\\\\Firebird::REPEATABLE_READ, or Pdo\\\\Firebird::SERIALIZABLE)\");\n\t\tret = 0;\n\t}\n\n\tdo {\n\t\tstatic char const dpb_flags[] = {\n\t\t\tisc_dpb_user_name, isc_dpb_password, isc_dpb_lc_ctype, isc_dpb_sql_role_name };\n\t\tchar const *dpb_values[] = { dbh->username, dbh->password, vars[1].optval, vars[2].optval };\n\t\tchar dpb_buffer[256] = { isc_dpb_version1 }, *dpb;\n\n\t\tdpb = dpb_buffer + 1;\n\n\t\t/* loop through all the provided arguments and set dpb fields accordingly */\n\t\tfor (i = 0; i < sizeof(dpb_flags); ++i) {\n\t\t\tif (dpb_values[i] && buf_len > 0) {\n\t\t\t\tdpb_len = slprintf(dpb, buf_len, \"%c%c%s\", dpb_flags[i], (unsigned char)strlen(dpb_values[i]),\n\t\t\t\t\tdpb_values[i]);\n\t\t\t\tdpb += dpb_len;\n\t\t\t\tbuf_len -= dpb_len;\n\t\t\t}\n\t\t}\n\n\t\tH->sql_dialect = PDO_FB_DIALECT;\n\t\tif (vars[3].optval) {\n\t\t\tH->sql_dialect = atoi(vars[3].optval);\n\t\t}\n\n\t\t/* fire it up baby! */\n\t\tif (isc_attach_database(H->isc_status, 0, vars[0].optval, &H->db,(short)(dpb-dpb_buffer), dpb_buffer)) {\n\t\t\tbreak;\n\t\t}\n\n\t\tdbh->methods = &firebird_methods;\n\t\tdbh->native_case = PDO_CASE_UPPER;\n\t\tdbh->alloc_own_columns = 1;\n\n\t\tret = 1;\n\n\t} while (0);\n\n\tfor (i = 0; i < sizeof(vars)/sizeof(vars[0]); ++i) {\n\t\tif (vars[i].freeme) {\n\t\t\tefree(vars[i].optval);\n\t\t}\n\t}\n\n\tif (!dbh->methods) {\n\t\tchar errmsg[512];\n\t\tconst ISC_STATUS *s = H->isc_status;\n\t\tfb_interpret(errmsg, sizeof(errmsg),&s);\n\t\tzend_throw_exception_ex(php_pdo_get_exception(), H->isc_status[1], \"SQLSTATE[%s] [%ld] %s\",\n\t\t\t\t\"HY000\", H->isc_status[1], errmsg);\n\t}\n\n\tif (ret && dbh->auto_commit && !H->tr) {\n\t\tret = php_firebird_begin_transaction(dbh, /* auto commit mode */ true);\n\t}\n\n\tif (!ret) {\n\t\tfirebird_handle_closer(dbh);\n\t}\n\n\treturn ret;\n}\n/* }}} */\n\n\nconst pdo_driver_t swoole_pdo_firebird_driver = { /* {{{ */\n\tPDO_DRIVER_HEADER(firebird),\n\tpdo_firebird_handle_factory\n};\n/* }}} */\n\n#endif\n"
  },
  {
    "path": "thirdparty/php84/pdo_firebird/firebird_statement.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Ard Biesheuvel <abies@php.net>                               |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_FIREBIRD_HOOK\n#include \"php_swoole_firebird.h\"\n\n#if PHP_VERSION_ID < 80500\n\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"ext/pdo/php_pdo.h\"\n#include \"ext/pdo/php_pdo_driver.h\"\n#include \"php_pdo_firebird_int.h\"\n#include \"pdo_firebird_utils.h\"\n\n#include <time.h>\n\n#define READ_AND_RETURN_USING_MEMCPY(type, sqldata) do { \\\n\t\ttype ret; \\\n\t\tmemcpy(&ret, sqldata, sizeof(ret)); \\\n\t\treturn ret; \\\n\t} while (0);\n\nstatic zend_always_inline ISC_INT64 php_get_isc_int64_from_sqldata(const ISC_SCHAR *sqldata)\n{\n\tREAD_AND_RETURN_USING_MEMCPY(ISC_INT64, sqldata);\n}\n\nstatic zend_always_inline ISC_LONG php_get_isc_long_from_sqldata(const ISC_SCHAR *sqldata)\n{\n\tREAD_AND_RETURN_USING_MEMCPY(ISC_LONG, sqldata);\n}\n\nstatic zend_always_inline double php_get_double_from_sqldata(const ISC_SCHAR *sqldata)\n{\n\tREAD_AND_RETURN_USING_MEMCPY(double, sqldata);\n}\n\nstatic zend_always_inline float php_get_float_from_sqldata(const ISC_SCHAR *sqldata)\n{\n\tREAD_AND_RETURN_USING_MEMCPY(float, sqldata);\n}\n\nstatic zend_always_inline ISC_TIMESTAMP php_get_isc_timestamp_from_sqldata(const ISC_SCHAR *sqldata)\n{\n\tREAD_AND_RETURN_USING_MEMCPY(ISC_TIMESTAMP, sqldata);\n}\n\nstatic zend_always_inline ISC_QUAD php_get_isc_quad_from_sqldata(const ISC_SCHAR *sqldata)\n{\n\tREAD_AND_RETURN_USING_MEMCPY(ISC_QUAD, sqldata);\n}\n\n#if FB_API_VER >= 40\n\nstatic zend_always_inline ISC_TIME_TZ php_get_isc_time_tz_from_sqldata(const ISC_SCHAR *sqldata)\n{\n\tREAD_AND_RETURN_USING_MEMCPY(ISC_TIME_TZ, sqldata);\n}\n\nstatic zend_always_inline ISC_TIMESTAMP_TZ php_get_isc_timestamp_tz_from_sqldata(const ISC_SCHAR *sqldata)\n{\n\tREAD_AND_RETURN_USING_MEMCPY(ISC_TIMESTAMP_TZ, sqldata);\n}\n\n/* fetch formatted time with time zone */\nstatic int get_formatted_time_tz(pdo_stmt_t *stmt, const ISC_TIME_TZ* timeTz, zval *result)\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tunsigned hours = 0, minutes = 0, seconds = 0, fractions = 0;\n\tchar timeZoneBuffer[40] = {0};\n\tchar *fmt;\n\tstruct tm t;\n\tISC_TIME time;\n\tchar timeBuf[80] = {0};\n\tchar timeTzBuf[124] = {0};\n\tif (fb_decode_time_tz(S->H->isc_status, timeTz, &hours, &minutes, &seconds, &fractions, sizeof(timeZoneBuffer), timeZoneBuffer)) {\n\t\treturn 1;\n\t}\n\ttime = fb_encode_time(hours, minutes, seconds, fractions);\n\tisc_decode_sql_time(&time, &t);\n\tfmt = S->H->time_format ? S->H->time_format : PDO_FB_DEF_TIME_FMT;\n\n\tsize_t len = strftime(timeBuf, sizeof(timeBuf), fmt, &t);\n\tif (len == 0) {\n\t\treturn 1;\n\t}\n\n\tsize_t time_tz_len = sprintf(timeTzBuf, \"%s %s\", timeBuf, timeZoneBuffer);\n\tZVAL_STRINGL(result, timeTzBuf, time_tz_len);\n\treturn 0;\n}\n\n/* fetch formatted timestamp with time zone */\nstatic int get_formatted_timestamp_tz(pdo_stmt_t *stmt, const ISC_TIMESTAMP_TZ* timestampTz, zval *result)\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tunsigned year, month, day, hours, minutes, seconds, fractions;\n\tchar timeZoneBuffer[40] = {0};\n\tchar *fmt;\n\tstruct tm t;\n\tISC_TIMESTAMP ts;\n\tchar timestampBuf[80] = {0};\n\tchar timestampTzBuf[124] = {0};\n\tif (fb_decode_timestamp_tz(S->H->isc_status, timestampTz, &year, &month, &day, &hours, &minutes, &seconds, &fractions, sizeof(timeZoneBuffer), timeZoneBuffer)) {\n\t\treturn 1;\n\t}\n\tts.timestamp_date = fb_encode_date(year, month, day);\n\tts.timestamp_time = fb_encode_time(hours, minutes, seconds, fractions);\n\tisc_decode_timestamp(&ts, &t);\n\n\tfmt = S->H->timestamp_format ? S->H->timestamp_format : PDO_FB_DEF_TIMESTAMP_FMT;\n\n\tsize_t len = strftime(timestampBuf, sizeof(timestampBuf), fmt, &t);\n\tif (len == 0) {\n\t\treturn 1;\n\t}\n\n\tsize_t timestamp_tz_len = sprintf(timestampTzBuf, \"%s %s\", timestampBuf, timeZoneBuffer);\n\tZVAL_STRINGL(result, timestampTzBuf, timestamp_tz_len);\n\treturn 0;\n}\n\n#endif\n\n/* free the allocated space for passing field values to the db and back */\nstatic void php_firebird_free_sqlda(XSQLDA const *sqlda) /* {{{ */\n{\n\tint i;\n\n\tfor (i = 0; i < sqlda->sqld; ++i) {\n\t\tXSQLVAR const *var = &sqlda->sqlvar[i];\n\n\t\tif (var->sqlind) {\n\t\t\tefree(var->sqlind);\n\t\t}\n\t}\n}\n/* }}} */\n\n/* called by PDO to clean up a statement handle */\nstatic int pdo_firebird_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tint result = 1;\n\n\t/* TODO: for master, move this check to a separate function shared between pdo drivers.\n\t *       pdo_pgsql and pdo_mysql do this exact same thing */\n\tbool server_obj_usable = !Z_ISUNDEF(stmt->database_object_handle)\n\t\t&& IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)])\n\t\t&& !(OBJ_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED);\n\n\t/* release the statement.\n\t * Note: if the server object is already gone then the statement was closed already as well. */\n\tif (server_obj_usable && isc_dsql_free_statement(S->H->isc_status, &S->stmt, DSQL_drop)) {\n\t\tphp_firebird_error_stmt(stmt);\n\t\tresult = 0;\n\t}\n\n\tzend_hash_destroy(S->named_params);\n\tFREE_HASHTABLE(S->named_params);\n\n\t/* clean up the input descriptor */\n\tif (S->in_sqlda) {\n\t\tphp_firebird_free_sqlda(S->in_sqlda);\n\t\tefree(S->in_sqlda);\n\t}\n\n\tphp_firebird_free_sqlda(&S->out_sqlda);\n\tefree(S);\n\n\treturn result;\n}\n/* }}} */\n\n/* called by PDO to execute a prepared query */\nstatic int pdo_firebird_stmt_execute(pdo_stmt_t *stmt) /* {{{ */\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tpdo_firebird_db_handle *H = S->H;\n\tzend_ulong affected_rows = 0;\n\tstatic char info_count[] = {isc_info_sql_records};\n\tchar result[64];\n\n\tdo {\n\t\t/* named or open cursors should be closed first */\n\t\tif ((*S->name || S->cursor_open) && isc_dsql_free_statement(H->isc_status, &S->stmt, DSQL_close)) {\n\t\t\tbreak;\n\t\t}\n\t\tS->cursor_open = 0;\n\n\t\t/* allocate storage for the output data */\n\t\tif (S->out_sqlda.sqld) {\n\t\t\tunsigned int i;\n\t\t\tfor (i = 0; i < S->out_sqlda.sqld; i++) {\n\t\t\t\tXSQLVAR *var = &S->out_sqlda.sqlvar[i];\n\t\t\t\tif (var->sqlind) {\n\t\t\t\t\tefree(var->sqlind);\n\t\t\t\t}\n\t\t\t\tvar->sqlind = (void*)ecalloc(1, var->sqllen + 2 * sizeof(short));\n\t\t\t\tvar->sqldata = &((char*)var->sqlind)[sizeof(short)];\n\t\t\t}\n\t\t}\n\n\t\tif (S->statement_type == isc_info_sql_stmt_exec_procedure) {\n\t\t\tif (isc_dsql_execute2(H->isc_status, &H->tr, &S->stmt, PDO_FB_SQLDA_VERSION, S->in_sqlda, &S->out_sqlda)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (isc_dsql_execute(H->isc_status, &H->tr, &S->stmt, PDO_FB_SQLDA_VERSION, S->in_sqlda)) {\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Determine how many rows have changed. In this case we are\n\t\t * only interested in rows changed, not rows retrieved. That\n\t\t * should be handled by the client when fetching. */\n\t\tstmt->row_count = affected_rows;\n\n\t\tswitch (S->statement_type) {\n\t\t\tcase isc_info_sql_stmt_insert:\n\t\t\tcase isc_info_sql_stmt_update:\n\t\t\tcase isc_info_sql_stmt_delete:\n\t\t\tcase isc_info_sql_stmt_exec_procedure:\n\t\t\t\tif (isc_dsql_sql_info(H->isc_status, &S->stmt, sizeof ( info_count),\n\t\t\t\t\tinfo_count, sizeof(result), result)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (result[0] == isc_info_sql_records) {\n\t\t\t\t\tunsigned i = 3, result_size = isc_vax_integer(&result[1], 2);\n\t\t\t\t\tif (result_size > sizeof(result)) {\n\t\t\t\t\t\tgoto error;\n\t\t\t\t\t}\n\t\t\t\t\twhile (result[i] != isc_info_end && i < result_size) {\n\t\t\t\t\t\tshort len = (short) isc_vax_integer(&result[i + 1], 2);\n\t\t\t\t\t\tif (len != 1 && len != 2 && len != 4) {\n\t\t\t\t\t\t\tgoto error;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (result[i] != isc_info_req_select_count) {\n\t\t\t\t\t\t\taffected_rows += isc_vax_integer(&result[i + 3], len);\n\t\t\t\t\t\t}\n\t\t\t\t\t\ti += len + 3;\n\t\t\t\t\t}\n\t\t\t\t\tstmt->row_count = affected_rows;\n\t\t\t\t}\n\t\t\t/* TODO Dead code or assert one of the previous cases are hit? */\n\t\t\tdefault:\n\t\t\t\t;\n\t\t}\n\n\t\tif (stmt->dbh->auto_commit && !S->H->in_manually_txn && !php_firebird_commit_transaction(stmt->dbh, /* retain */ true)) {\n\t\t\tbreak;\n\t\t}\n\n\t\t*S->name = 0;\n\t\tS->cursor_open = S->out_sqlda.sqln && (S->statement_type != isc_info_sql_stmt_exec_procedure);\n\t\tS->exhausted = !S->out_sqlda.sqln; /* There are data to fetch */\n\n\t\treturn 1;\n\t} while (0);\n\nerror:\n\tphp_firebird_error_stmt(stmt);\n\n\treturn 0;\n}\n/* }}} */\n\n/* called by PDO to fetch the next row from a statement */\nstatic int pdo_firebird_stmt_fetch(pdo_stmt_t *stmt, /* {{{ */\n\tenum pdo_fetch_orientation ori, zend_long offset)\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tpdo_firebird_db_handle *H = S->H;\n\n\tif (!stmt->executed) {\n\t\tconst char *msg = \"Cannot fetch from a closed cursor\";\n\t\tphp_firebird_error_stmt_with_info(stmt, \"HY000\", strlen(\"HY000\"), msg, strlen(msg));\n\t} else if (!S->exhausted) {\n\t\tif (S->statement_type == isc_info_sql_stmt_exec_procedure) {\n\t\t\tstmt->row_count = 1;\n\t\t\tS->exhausted = 1;\n\t\t\treturn 1;\n\t\t}\n\t\tif (isc_dsql_fetch(H->isc_status, &S->stmt, PDO_FB_SQLDA_VERSION, &S->out_sqlda)) {\n\t\t\tif (H->isc_status[0] && H->isc_status[1]) {\n\t\t\t\tphp_firebird_error_stmt(stmt);\n\t\t\t}\n\t\t\tS->exhausted = 1;\n\t\t\treturn 0;\n\t\t}\n\t\tstmt->row_count++;\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n/* }}} */\n\n/* called by PDO to retrieve information about the fields being returned */\nstatic int pdo_firebird_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tstruct pdo_column_data *col = &stmt->columns[colno];\n\tXSQLVAR *var = &S->out_sqlda.sqlvar[colno];\n\tint colname_len;\n\tchar *cp;\n\n\tif ((var->sqltype & ~1) == SQL_TEXT) {\n\t\tvar->sqltype = SQL_VARYING | (var->sqltype & 1);\n\t}\n\tcolname_len = (S->H->fetch_table_names && var->relname_length)\n\t\t\t\t\t? (var->aliasname_length + var->relname_length + 1)\n\t\t\t\t\t: (var->aliasname_length);\n\tcol->precision = -var->sqlscale;\n\tcol->maxlen = var->sqllen;\n\tcol->name = zend_string_alloc(colname_len, 0);\n\tcp = ZSTR_VAL(col->name);\n\tif (colname_len > var->aliasname_length) {\n\t\tmemmove(cp, var->relname, var->relname_length);\n\t\tcp += var->relname_length;\n\t\t*cp++ = '.';\n\t}\n\tmemmove(cp, var->aliasname, var->aliasname_length);\n\t*(cp+var->aliasname_length) = '\\0';\n\n\treturn 1;\n}\n/* }}} */\n\nstatic int pdo_firebird_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt *) stmt->driver_data;\n\tXSQLVAR *var = &S->out_sqlda.sqlvar[colno];\n\n\tenum pdo_param_type param_type;\n\tif (var->sqlscale < 0) {\n\t\tparam_type = PDO_PARAM_STR;\n\t} else {\n\t\tswitch (var->sqltype & ~1) {\n\t\t\tcase SQL_SHORT:\n\t\t\tcase SQL_LONG:\n#if SIZEOF_ZEND_LONG >= 8\n\t\t\tcase SQL_INT64:\n#endif\n\t\t\t\tparam_type = PDO_PARAM_INT;\n\t\t\t\tbreak;\n\t\t\tcase SQL_BOOLEAN:\n\t\t\t\tparam_type = PDO_PARAM_BOOL;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tparam_type = PDO_PARAM_STR;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tarray_init(return_value);\n\tadd_assoc_long(return_value, \"pdo_type\", param_type);\n\treturn 1;\n}\n\n/* fetch a blob into a fetch buffer */\nstatic int php_firebird_fetch_blob(pdo_stmt_t *stmt, int colno, zval *result, ISC_QUAD *blob_id)\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tpdo_firebird_db_handle *H = S->H;\n\tisc_blob_handle blobh = PDO_FIREBIRD_HANDLE_INITIALIZER;\n\tchar const bl_item = isc_info_blob_total_length;\n\tchar bl_info[20];\n\tunsigned short i;\n\tint retval = 0;\n\tsize_t len = 0;\n\n\tif (isc_open_blob(H->isc_status, &H->db, &H->tr, &blobh, blob_id)) {\n\t\tphp_firebird_error_stmt(stmt);\n\t\treturn 0;\n\t}\n\n\tif (isc_blob_info(H->isc_status, &blobh, 1, const_cast(&bl_item),\n\t\t\tsizeof(bl_info), bl_info)) {\n\t\tphp_firebird_error_stmt(stmt);\n\t\tgoto fetch_blob_end;\n\t}\n\n\t/* find total length of blob's data */\n\tfor (i = 0; i < sizeof(bl_info); ) {\n\t\tunsigned short item_len;\n\t\tchar item = bl_info[i++];\n\n\t\tif (item == isc_info_end || item == isc_info_truncated || item == isc_info_error\n\t\t\t\t|| i >= sizeof(bl_info)) {\n\t\t\tconst char *msg = \"Couldn't determine BLOB size\";\n\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY000\", strlen(\"HY000\"), msg, strlen(msg));\n\t\t\tgoto fetch_blob_end;\n\t\t}\n\n\t\titem_len = (unsigned short) isc_vax_integer(&bl_info[i], 2);\n\n\t\tif (item == isc_info_blob_total_length) {\n\t\t\tlen = isc_vax_integer(&bl_info[i+2], item_len);\n\t\t\tbreak;\n\t\t}\n\t\ti += item_len+2;\n\t}\n\n\t/* we've found the blob's length, now fetch! */\n\n\tif (len) {\n\t\tzend_ulong cur_len;\n\t\tunsigned short seg_len;\n\t\tISC_STATUS stat;\n\t\tzend_string *str;\n\n\t\t/* prevent overflow */\n\t\tif (len > ZSTR_MAX_LEN) {\n\t\t\tresult = 0;\n\t\t\tgoto fetch_blob_end;\n\t\t}\n\n\t\tstr = zend_string_alloc(len, 0);\n\n\t\tfor (cur_len = stat = 0; (!stat || stat == isc_segment) && cur_len < len; cur_len += seg_len) {\n\n\t\t\tunsigned short chunk_size = (len - cur_len) > USHRT_MAX ? USHRT_MAX\n\t\t\t\t: (unsigned short)(len - cur_len);\n\n\t\t\tstat = isc_get_segment(H->isc_status, &blobh, &seg_len, chunk_size, ZSTR_VAL(str) + cur_len);\n\t\t}\n\n\t\tZSTR_VAL(str)[len] = '\\0';\n\t\tZVAL_STR(result, str);\n\n\t\tif (H->isc_status[0] == 1 && (stat != 0 && stat != isc_segstr_eof && stat != isc_segment)) {\n\t\t\tconst char *msg = \"Error reading from BLOB\";\n\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY000\", strlen(\"HY000\"), msg, strlen(msg));\n\t\t\tgoto fetch_blob_end;\n\t\t}\n\t}\n\tretval = 1;\n\nfetch_blob_end:\n\tif (isc_close_blob(H->isc_status, &blobh)) {\n\t\tphp_firebird_error_stmt(stmt);\n\t\treturn 0;\n\t}\n\treturn retval;\n}\n/* }}} */\n\nstatic int pdo_firebird_stmt_get_col(\n\t\tpdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type)\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tXSQLVAR const *var = &S->out_sqlda.sqlvar[colno];\n\n\tif (*var->sqlind == -1) {\n\t\tZVAL_NULL(result);\n\t} else {\n\t\tif (var->sqlscale < 0) {\n\t\t\tstatic ISC_INT64 const scales[] = { 1, 10, 100, 1000,\n\t\t\t\t10000,\n\t\t\t\t100000,\n\t\t\t\t1000000,\n\t\t\t\t10000000,\n\t\t\t\t100000000,\n\t\t\t\t1000000000,\n\t\t\t\tLL_LIT(10000000000),\n\t\t\t\tLL_LIT(100000000000),\n\t\t\t\tLL_LIT(1000000000000),\n\t\t\t\tLL_LIT(10000000000000),\n\t\t\t\tLL_LIT(100000000000000),\n\t\t\t\tLL_LIT(1000000000000000),\n\t\t\t\tLL_LIT(10000000000000000),\n\t\t\t\tLL_LIT(100000000000000000),\n\t\t\t\tLL_LIT(1000000000000000000)\n\t\t\t};\n\t\t\tISC_INT64 n, f = scales[-var->sqlscale];\n\t\t\tzend_string *str;\n\n\t\t\tswitch (var->sqltype & ~1) {\n\t\t\t\tcase SQL_SHORT:\n\t\t\t\t\tn = *(short*)var->sqldata;\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_LONG:\n\t\t\t\t\tn = php_get_isc_long_from_sqldata(var->sqldata);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_INT64:\n\t\t\t\t\tn = php_get_isc_int64_from_sqldata(var->sqldata);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_DOUBLE:\n\t\t\t\t\tbreak;\n\t\t\t\tEMPTY_SWITCH_DEFAULT_CASE()\n\t\t\t}\n\n\t\t\tif ((var->sqltype & ~1) == SQL_DOUBLE) {\n\t\t\t\tstr = zend_strpprintf(0, \"%.*F\", -var->sqlscale, php_get_double_from_sqldata(var->sqldata));\n\t\t\t} else if (n >= 0) {\n\t\t\t\tstr = zend_strpprintf(0, \"%\" LL_MASK \"d.%0*\" LL_MASK \"d\",\n\t\t\t\t\tn / f, -var->sqlscale, n % f);\n\t\t\t} else if (n <= -f) {\n\t\t\t\tstr = zend_strpprintf(0, \"%\" LL_MASK \"d.%0*\" LL_MASK \"d\",\n\t\t\t\t\tn / f, -var->sqlscale, -n % f);\n\t\t\t } else {\n\t\t\t\tstr = zend_strpprintf(0, \"-0.%0*\" LL_MASK \"d\", -var->sqlscale, -n % f);\n\t\t\t}\n\t\t\tZVAL_STR(result, str);\n\t\t} else {\n\t\t\tswitch (var->sqltype & ~1) {\n\t\t\t\tstruct tm t;\n\t\t\t\tchar *fmt;\n\n\t\t\t\tcase SQL_VARYING:\n\t\t\t\t\tZVAL_STRINGL_FAST(result, &var->sqldata[2], *(short*)var->sqldata);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_TEXT:\n\t\t\t\t\tZVAL_STRINGL_FAST(result, var->sqldata, var->sqllen);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_SHORT:\n\t\t\t\t\tZVAL_LONG(result, *(short*)var->sqldata);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_LONG:\n\t\t\t\t\tZVAL_LONG(result, php_get_isc_long_from_sqldata(var->sqldata));\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_INT64:\n#if SIZEOF_ZEND_LONG >= 8\n\t\t\t\t\tZVAL_LONG(result, php_get_isc_int64_from_sqldata(var->sqldata));\n#else\n\t\t\t\t\tZVAL_STR(result, zend_strpprintf(0, \"%\" LL_MASK \"d\", php_get_isc_int64_from_sqldata(var->sqldata)));\n#endif\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_FLOAT:\n\t\t\t\t\t/* TODO: Why is this not returned as the native type? */\n\t\t\t\t\tZVAL_STR(result, zend_strpprintf_unchecked(0, \"%.8H\", php_get_float_from_sqldata(var->sqldata)));\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_DOUBLE:\n\t\t\t\t\t/* TODO: Why is this not returned as the native type? */\n\t\t\t\t\tZVAL_STR(result, zend_strpprintf_unchecked(0, \"%.16H\", php_get_double_from_sqldata(var->sqldata)));\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_BOOLEAN:\n\t\t\t\t\tZVAL_BOOL(result, *(FB_BOOLEAN*)var->sqldata);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_TYPE_DATE:\n\t\t\t\t\tisc_decode_sql_date((ISC_DATE*)var->sqldata, &t);\n\t\t\t\t\tfmt = S->H->date_format ? S->H->date_format : PDO_FB_DEF_DATE_FMT;\n\t\t\t\t\tif (0) {\n\t\t\t\tcase SQL_TYPE_TIME:\n\t\t\t\t\t\tisc_decode_sql_time((ISC_TIME*)var->sqldata, &t);\n\t\t\t\t\t\tfmt = S->H->time_format ? S->H->time_format : PDO_FB_DEF_TIME_FMT;\n\t\t\t\t\t} else if (0) {\n\t\t\t\tcase SQL_TIMESTAMP:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tISC_TIMESTAMP timestamp = php_get_isc_timestamp_from_sqldata(var->sqldata);\n\t\t\t\t\t\t\tisc_decode_timestamp(&timestamp, &t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfmt = S->H->timestamp_format ? S->H->timestamp_format : PDO_FB_DEF_TIMESTAMP_FMT;\n\t\t\t\t\t}\n\t\t\t\t\t/* convert the timestamp into a string */\n\t\t\t\t\tchar buf[80];\n\t\t\t\t\tsize_t len = strftime(buf, sizeof(buf), fmt, &t);\n\t\t\t\t\tZVAL_STRINGL(result, buf, len);\n\t\t\t\t\tbreak;\n#if FB_API_VER >= 40\n\t\t\t\tcase SQL_TIME_TZ: {\n\t\t\t\t\tISC_TIME_TZ time = php_get_isc_time_tz_from_sqldata(var->sqldata);\n\t\t\t\t\treturn get_formatted_time_tz(stmt, &time, result);\n\t\t\t\t}\n\t\t\t\tcase SQL_TIMESTAMP_TZ: {\n\t\t\t\t\tISC_TIMESTAMP_TZ ts = php_get_isc_timestamp_tz_from_sqldata(var->sqldata);\n\t\t\t\t\treturn get_formatted_timestamp_tz(stmt, &ts, result);\n\t\t\t\t}\n#endif\n\t\t\t\tcase SQL_BLOB: {\n\t\t\t\t\tISC_QUAD quad = php_get_isc_quad_from_sqldata(var->sqldata);\n\t\t\t\t\treturn php_firebird_fetch_blob(stmt, colno, result, &quad);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn 1;\n}\n\nstatic int php_firebird_bind_blob(pdo_stmt_t *stmt, ISC_QUAD *blob_id, zval *param)\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tpdo_firebird_db_handle *H = S->H;\n\tisc_blob_handle h = PDO_FIREBIRD_HANDLE_INITIALIZER;\n\tzval data;\n\tzend_ulong put_cnt = 0, rem_cnt;\n\tunsigned short chunk_size;\n\tint result = 1;\n\n\tif (isc_create_blob(H->isc_status, &H->db, &H->tr, &h, blob_id)) {\n\t\tphp_firebird_error_stmt(stmt);\n\t\treturn 0;\n\t}\n\n\tif (Z_TYPE_P(param) != IS_STRING) {\n\t\tZVAL_STR(&data, zval_get_string_func(param));\n\t} else {\n\t\tZVAL_COPY_VALUE(&data, param);\n\t}\n\n\tfor (rem_cnt = Z_STRLEN(data); rem_cnt > 0; rem_cnt -= chunk_size) {\n\t\tchunk_size = rem_cnt > USHRT_MAX ? USHRT_MAX : (unsigned short)rem_cnt;\n\t\tif (isc_put_segment(H->isc_status, &h, chunk_size, &Z_STRVAL(data)[put_cnt])) {\n\t\t\tphp_firebird_error_stmt(stmt);\n\t\t\tresult = 0;\n\t\t\tbreak;\n\t\t}\n\t\tput_cnt += chunk_size;\n\t}\n\n\tif (Z_TYPE_P(param) != IS_STRING) {\n\t\tzval_ptr_dtor_str(&data);\n\t}\n\n\tif (isc_close_blob(H->isc_status, &h)) {\n\t\tphp_firebird_error_stmt(stmt);\n\t\treturn 0;\n\t}\n\treturn result;\n}\n\nstatic int pdo_firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, /* {{{ */\n\tenum pdo_param_event event_type)\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tXSQLDA *sqlda = param->is_param ? S->in_sqlda : &S->out_sqlda;\n\tXSQLVAR *var;\n\n\tif (event_type == PDO_PARAM_EVT_FREE) { /* not used */\n\t\treturn 1;\n\t}\n\n\tif (!sqlda || param->paramno >= sqlda->sqld) {\n\t\tconst char *msg = \"Invalid parameter index\";\n\t\tphp_firebird_error_stmt_with_info(stmt, \"HY093\", strlen(\"HY093\"), msg, strlen(msg));\n\t\treturn 0;\n\t}\n\tif (param->is_param && param->paramno == -1) {\n\t\tzval *index;\n\n\t\t/* try to determine the index by looking in the named_params hash */\n\t\tif ((index = zend_hash_find(S->named_params, param->name)) != NULL) {\n\t\t\tparam->paramno = Z_LVAL_P(index);\n\t\t} else {\n\t\t\t/* ... or by looking in the input descriptor */\n\t\t\tint i;\n\n\t\t\tfor (i = 0; i < sqlda->sqld; ++i) {\n\t\t\t\tXSQLVAR *var = &sqlda->sqlvar[i];\n\n\t\t\t\tif ((var->aliasname_length && !strncasecmp(ZSTR_VAL(param->name), var->aliasname,\n\t\t\t\t\t\tmin(ZSTR_LEN(param->name), var->aliasname_length)))\n\t\t\t\t\t\t|| (var->sqlname_length && !strncasecmp(ZSTR_VAL(param->name), var->sqlname,\n\t\t\t\t\t\tmin(ZSTR_LEN(param->name), var->sqlname_length)))) {\n\t\t\t\t\tparam->paramno = i;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (i >= sqlda->sqld) {\n\t\t\t\tconst char *msg = \"Invalid parameter name\";\n\t\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY093\", strlen(\"HY093\"), msg, strlen(msg));\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tvar = &sqlda->sqlvar[param->paramno];\n\n\tswitch (event_type) {\n\t\tzval *parameter;\n\n\t\tcase PDO_PARAM_EVT_ALLOC:\n\t\t\tif (param->is_param) {\n\t\t\t\t/* allocate the parameter */\n\t\t\t\tif (var->sqlind) {\n\t\t\t\t\tefree(var->sqlind);\n\t\t\t\t}\n\t\t\t\tvar->sqlind = (void*)emalloc(var->sqllen + 2*sizeof(short));\n\t\t\t\tvar->sqldata = &((char*)var->sqlind)[sizeof(short)];\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase PDO_PARAM_EVT_EXEC_PRE:\n\t\t\tif (!param->is_param) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t*var->sqlind = 0;\n\t\t\tif (Z_ISREF(param->parameter)) {\n\t\t\t\tparameter = Z_REFVAL(param->parameter);\n\t\t\t} else {\n\t\t\t\tparameter = &param->parameter;\n\t\t\t}\n\n\t\t\tif (Z_TYPE_P(parameter) == IS_RESOURCE) {\n\t\t\t\tphp_stream *stm = NULL;\n\n\t\t\t\tphp_stream_from_zval_no_verify(stm, parameter);\n\t\t\t\tif (stm) {\n\t\t\t\t\tzend_string *mem =  php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0);\n\t\t\t\t\tzval_ptr_dtor(parameter);\n\t\t\t\t\tZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC());\n\t\t\t\t} else {\n\t\t\t\t\tpdo_raise_impl_error(stmt->dbh, stmt, \"HY105\", \"Expected a stream resource\");\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tswitch (var->sqltype & ~1) {\n\t\t\t\tcase SQL_ARRAY:\n\t\t\t\t\t{\n\t\t\t\t\t\tconst char *msg = \"Cannot bind to array field\";\n\t\t\t\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY000\", strlen(\"HY000\"), msg, strlen(msg));\n\t\t\t\t\t}\n\t\t\t\t\treturn 0;\n\n\t\t\t\tcase SQL_BLOB: {\n\t\t\t\t\tif (Z_TYPE_P(parameter) == IS_NULL) {\n\t\t\t\t\t\t/* Check if field allow NULL values */\n\t\t\t\t\t\tif (~var->sqltype & 1) {\n\t\t\t\t\t\t\tconst char *msg = \"Parameter requires non-null value\";\n\t\t\t\t\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY105\", strlen(\"HY105\"), msg, strlen(msg));\n\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t*var->sqlind = -1;\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t\tISC_QUAD quad = php_get_isc_quad_from_sqldata(var->sqldata);\n\t\t\t\t\tif (php_firebird_bind_blob(stmt, &quad, parameter) != 0) {\n\t\t\t\t\t\tmemcpy(var->sqldata, &quad, sizeof(quad));\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* keep native BOOLEAN type */\n\t\t\tif ((var->sqltype & ~1) == SQL_BOOLEAN) {\n\t\t\t\tswitch (Z_TYPE_P(parameter)) {\n\t\t\t\t\tcase IS_LONG:\n\t\t\t\t\tcase IS_DOUBLE:\n\t\t\t\t\tcase IS_TRUE:\n\t\t\t\t\tcase IS_FALSE:\n\t\t\t\t\t\t*(FB_BOOLEAN*)var->sqldata = zend_is_true(parameter) ? FB_TRUE : FB_FALSE;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_STRING:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tzend_long lval;\n\t\t\t\t\t\t\tdouble dval;\n\n\t\t\t\t\t\t\tif (Z_STRLEN_P(parameter) == 0) {\n\t\t\t\t\t\t\t\t*(FB_BOOLEAN*)var->sqldata = FB_FALSE;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tswitch (is_numeric_string(Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), &lval, &dval, 0)) {\n\t\t\t\t\t\t\t\tcase IS_LONG:\n\t\t\t\t\t\t\t\t\t*(FB_BOOLEAN*)var->sqldata = (lval != 0) ? FB_TRUE : FB_FALSE;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase IS_DOUBLE:\n\t\t\t\t\t\t\t\t\t*(FB_BOOLEAN*)var->sqldata = (dval != 0) ? FB_TRUE : FB_FALSE;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\tif (!zend_binary_strncasecmp(Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), \"true\", 4, 4)) {\n\t\t\t\t\t\t\t\t\t\t*(FB_BOOLEAN*)var->sqldata = FB_TRUE;\n\t\t\t\t\t\t\t\t\t} else if (!zend_binary_strncasecmp(Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), \"false\", 5, 5)) {\n\t\t\t\t\t\t\t\t\t\t*(FB_BOOLEAN*)var->sqldata = FB_FALSE;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tconst char *msg = \"Cannot convert string to boolean\";\n\t\t\t\t\t\t\t\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY105\", strlen(\"HY105\"), msg, strlen(msg));\n\t\t\t\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_NULL:\n\t\t\t\t\t\t*var->sqlind = -1;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst char *msg = \"Binding arrays/objects is not supported\";\n\t\t\t\t\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY105\", strlen(\"HY105\"), msg, strlen(msg));\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* check if a NULL should be inserted */\n\t\t\tswitch (Z_TYPE_P(parameter)) {\n\t\t\t\tint force_null;\n\n\t\t\t\tcase IS_LONG:\n\t\t\t\t\t/* keep the allow-NULL flag */\n\t\t\t\t\tvar->sqltype = (sizeof(zend_long) == 8 ? SQL_INT64 : SQL_LONG) | (var->sqltype & 1);\n\t\t\t\t\tvar->sqldata = (void*)&Z_LVAL_P(parameter);\n\t\t\t\t\tvar->sqllen = sizeof(zend_long);\n\t\t\t\t\tbreak;\n\t\t\t\tcase IS_DOUBLE:\n\t\t\t\t\t/* keep the allow-NULL flag */\n\t\t\t\t\tvar->sqltype = SQL_DOUBLE | (var->sqltype & 1);\n\t\t\t\t\tvar->sqldata = (void*)&Z_DVAL_P(parameter);\n\t\t\t\t\tvar->sqllen = sizeof(double);\n\t\t\t\t\tbreak;\n\t\t\t\tcase IS_STRING:\n\t\t\t\t\tforce_null = 0;\n\n\t\t\t\t\t/* for these types, an empty string can be handled like a NULL value */\n\t\t\t\t\tswitch (var->sqltype & ~1) {\n\t\t\t\t\t\tcase SQL_SHORT:\n\t\t\t\t\t\tcase SQL_LONG:\n\t\t\t\t\t\tcase SQL_INT64:\n\t\t\t\t\t\tcase SQL_FLOAT:\n\t\t\t\t\t\tcase SQL_DOUBLE:\n\t\t\t\t\t\tcase SQL_TIMESTAMP:\n\t\t\t\t\t\tcase SQL_TYPE_DATE:\n\t\t\t\t\t\tcase SQL_TYPE_TIME:\n#if FB_API_VER >= 40\n\t\t\t\t\t\tcase SQL_INT128:\n\t\t\t\t\t\tcase SQL_DEC16:\n\t\t\t\t\t\tcase SQL_DEC34:\n\t\t\t\t\t\tcase SQL_TIMESTAMP_TZ:\n\t\t\t\t\t\tcase SQL_TIME_TZ:\n#endif\n\t\t\t\t\t\t\tforce_null = (Z_STRLEN_P(parameter) == 0);\n\t\t\t\t\t}\n\t\t\t\t\tif (!force_null) {\n\t\t\t\t\t\t/* keep the allow-NULL flag */\n\t\t\t\t\t\tvar->sqltype = SQL_TEXT | (var->sqltype & 1);\n\t\t\t\t\t\tvar->sqldata = Z_STRVAL_P(parameter);\n\t\t\t\t\t\tvar->sqllen = Z_STRLEN_P(parameter);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tZEND_FALLTHROUGH;\n\t\t\t\tcase IS_NULL:\n\t\t\t\t\t/* complain if this field doesn't allow NULL values */\n\t\t\t\t\tif (~var->sqltype & 1) {\n\t\t\t\t\t\tconst char *msg = \"Parameter requires non-null value\";\n\t\t\t\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY105\", strlen(\"HY105\"), msg, strlen(msg));\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\t}\n\t\t\t\t\t*var->sqlind = -1;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t{\n\t\t\t\t\t\tconst char *msg = \"Binding arrays/objects is not supported\";\n\t\t\t\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY105\", strlen(\"HY105\"), msg, strlen(msg));\n\t\t\t\t\t}\n\t\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase PDO_PARAM_EVT_FETCH_POST:\n\t\t\tif (param->paramno == -1) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tif (param->is_param) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (Z_ISREF(param->parameter)) {\n\t\t\t\tparameter = Z_REFVAL(param->parameter);\n\t\t\t} else {\n\t\t\t\tparameter = &param->parameter;\n\t\t\t}\n\t\t\tzval_ptr_dtor(parameter);\n\t\t\tZVAL_NULL(parameter);\n\t\t\treturn pdo_firebird_stmt_get_col(stmt, param->paramno, parameter, NULL);\n\t\tdefault:\n\t\t\t;\n\t}\n\treturn 1;\n}\n/* }}} */\n\nstatic int pdo_firebird_stmt_set_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) /* {{{ */\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\n\tswitch (attr) {\n\t\tdefault:\n\t\t\treturn 0;\n\t\tcase PDO_ATTR_CURSOR_NAME:\n\t\t\tif (!try_convert_to_string(val)) {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tif (isc_dsql_set_cursor_name(S->H->isc_status, &S->stmt, Z_STRVAL_P(val),0)) {\n\t\t\t\tphp_firebird_error_stmt(stmt);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tstrlcpy(S->name, Z_STRVAL_P(val), sizeof(S->name));\n\t\t\tbreak;\n\t}\n\treturn 1;\n}\n/* }}} */\n\nstatic int pdo_firebird_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) /* {{{ */\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\n\tswitch (attr) {\n\t\tdefault:\n\t\t\treturn 0;\n\t\tcase PDO_ATTR_CURSOR_NAME:\n\t\t\tif (*S->name) {\n\t\t\t\tZVAL_STRING(val, S->name);\n\t\t\t} else {\n\t\t\t\tZVAL_NULL(val);\n\t\t\t}\n\t\t\tbreak;\n\t}\n\treturn 1;\n}\n/* }}} */\n\nstatic int pdo_firebird_stmt_cursor_closer(pdo_stmt_t *stmt) /* {{{ */\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\n\t/* close the statement handle */\n\tif ((*S->name || S->cursor_open) && isc_dsql_free_statement(S->H->isc_status, &S->stmt, DSQL_close)) {\n\t\tphp_firebird_error_stmt(stmt);\n\t\treturn 0;\n\t}\n\t*S->name = 0;\n\tS->cursor_open = 0;\n\treturn 1;\n}\n/* }}} */\n\n\nconst struct pdo_stmt_methods firebird_stmt_methods = { /* {{{ */\n\tpdo_firebird_stmt_dtor,\n\tpdo_firebird_stmt_execute,\n\tpdo_firebird_stmt_fetch,\n\tpdo_firebird_stmt_describe,\n\tpdo_firebird_stmt_get_col,\n\tpdo_firebird_stmt_param_hook,\n\tpdo_firebird_stmt_set_attribute,\n\tpdo_firebird_stmt_get_attribute,\n\tpdo_firebird_stmt_get_column_meta,\n\tNULL, /* next_rowset_func */\n\tpdo_firebird_stmt_cursor_closer\n};\n/* }}} */\n\n#endif\n"
  },
  {
    "path": "thirdparty/php84/pdo_firebird/pdo_firebird_utils.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Simonov Denis <sim-mail@list.ru>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_FIREBIRD_HOOK\n#include \"php_swoole_firebird.h\"\n\n#if PHP_VERSION_ID < 80500\n\n#include \"pdo_firebird_utils.h\"\n#include <firebird/Interface.h>\n#include <cstring>\n\n/* Returns the client version. 0 bytes are minor version, 1 bytes are major version. */\nextern \"C\" unsigned fb_get_client_version(void)\n{\n\tFirebird::IMaster* master = Firebird::fb_get_master_interface();\n\tFirebird::IUtil* util = master->getUtilInterface();\n\treturn util->getClientVersion();\n}\n\nextern \"C\" ISC_TIME fb_encode_time(unsigned hours, unsigned minutes, unsigned seconds, unsigned fractions)\n{\n\tFirebird::IMaster* master = Firebird::fb_get_master_interface();\n\tFirebird::IUtil* util = master->getUtilInterface();\n\treturn util->encodeTime(hours, minutes, seconds, fractions);\n}\n\nextern \"C\" ISC_DATE fb_encode_date(unsigned year, unsigned month, unsigned day)\n{\n\tFirebird::IMaster* master = Firebird::fb_get_master_interface();\n\tFirebird::IUtil* util = master->getUtilInterface();\n\treturn util->encodeDate(year, month, day);\n}\n\n#if FB_API_VER >= 40\nstatic void fb_copy_status(const ISC_STATUS* from, ISC_STATUS* to, size_t maxLength)\n{\n\tfor(size_t i=0; i < maxLength; ++i) {\n\t\tmemcpy(to + i, from + i, sizeof(ISC_STATUS));\n\t\tif (from[i] == isc_arg_end) {\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n/* Decodes a time with time zone into its time components. */\nextern \"C\" ISC_STATUS fb_decode_time_tz(ISC_STATUS* isc_status, const ISC_TIME_TZ* timeTz, unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions,\n   unsigned timeZoneBufferLength, char* timeZoneBuffer)\n{\n\tFirebird::IMaster* master = Firebird::fb_get_master_interface();\n\tFirebird::IUtil* util = master->getUtilInterface();\n\tFirebird::IStatus* status = master->getStatus();\n\tFirebird::CheckStatusWrapper st(status);\n\tutil->decodeTimeTz(&st, timeTz, hours, minutes, seconds, fractions,\n\t\t\t\t\t\ttimeZoneBufferLength, timeZoneBuffer);\n\tif (st.hasData())  {\n\t\tfb_copy_status((const ISC_STATUS*)st.getErrors(), isc_status, 20);\n\t}\n\tstatus->dispose();\n\treturn isc_status[1];\n}\n\n/* Decodes a timestamp with time zone into its date and time components */\nextern \"C\" ISC_STATUS fb_decode_timestamp_tz(ISC_STATUS* isc_status, const ISC_TIMESTAMP_TZ* timestampTz,\n\tunsigned* year, unsigned* month, unsigned* day,\n\tunsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions,\n\tunsigned timeZoneBufferLength, char* timeZoneBuffer)\n{\n\tFirebird::IMaster* master = Firebird::fb_get_master_interface();\n\tFirebird::IUtil* util = master->getUtilInterface();\n\tFirebird::IStatus* status = master->getStatus();\n\tFirebird::CheckStatusWrapper st(status);\n\tutil->decodeTimeStampTz(&st, timestampTz, year, month, day,\n\t\t\t\t\t\t\thours, minutes, seconds, fractions,\n\t\t\t\t\t\t\ttimeZoneBufferLength, timeZoneBuffer);\n\tif (st.hasData())  {\n\t\tfb_copy_status((const ISC_STATUS*)st.getErrors(), isc_status, 20);\n\t}\n\tstatus->dispose();\n\treturn isc_status[1];\n}\n\n#endif\n#endif\n"
  },
  {
    "path": "thirdparty/php84/pdo_firebird/pdo_firebird_utils.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Simonov Denis <sim-mail@list.ru>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef PDO_FIREBIRD_UTILS_H\n#define PDO_FIREBIRD_UTILS_H\n\n#include <ibase.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nunsigned fb_get_client_version(void);\n\nISC_TIME fb_encode_time(unsigned hours, unsigned minutes, unsigned seconds, unsigned fractions);\n\nISC_DATE fb_encode_date(unsigned year, unsigned month, unsigned day);\n\n#if FB_API_VER >= 40\n\nISC_STATUS fb_decode_time_tz(ISC_STATUS* isc_status, const ISC_TIME_TZ* timeTz, unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions,\n\tunsigned timeZoneBufferLength, char* timeZoneBuffer);\n\nISC_STATUS fb_decode_timestamp_tz(ISC_STATUS* isc_status, const ISC_TIMESTAMP_TZ* timestampTz,\n\tunsigned* year, unsigned* month, unsigned* day,\n\tunsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions,\n\tunsigned timeZoneBufferLength, char* timeZoneBuffer);\n\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\t/* PDO_FIREBIRD_UTILS_H */\n"
  },
  {
    "path": "thirdparty/php84/pdo_firebird/php_pdo_firebird_int.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Ard Biesheuvel <abies@php.net>                               |\n  +----------------------------------------------------------------------+\n*/\n\n/* internal header; not supposed to be installed */\n\n#ifndef PHP_PDO_FIREBIRD_INT_H\n#define PHP_PDO_FIREBIRD_INT_H\n\n#include <ibase.h>\n\n#ifdef SQLDA_VERSION\n#define PDO_FB_SQLDA_VERSION SQLDA_VERSION\n#else\n#define PDO_FB_SQLDA_VERSION 1\n#endif\n\n#define PDO_FB_DIALECT 3\n\n#define PDO_FB_DEF_DATE_FMT \"%Y-%m-%d\"\n#define PDO_FB_DEF_TIME_FMT \"%H:%M:%S\"\n#define PDO_FB_DEF_TIMESTAMP_FMT PDO_FB_DEF_DATE_FMT \" \" PDO_FB_DEF_TIME_FMT\n\n#define SHORT_MAX (1 << (8*sizeof(short)-1))\n\n#if SIZEOF_ZEND_LONG == 8 && !defined(PHP_WIN32)\n# define LL_LIT(lit) lit ## L\n#else\n# define LL_LIT(lit) lit ## LL\n#endif\n#define LL_MASK \"ll\"\n\n/* Firebird API has a couple of missing const decls in its API */\n#define const_cast(s) ((char*)(s))\n\n#ifdef PHP_WIN32\ntypedef void (__stdcall *info_func_t)(char*);\n#else\ntypedef void (*info_func_t)(char*);\n#endif\n\n#ifndef min\n#define min(a,b) ((a)<(b)?(a):(b))\n#endif\n\n#if defined(_LP64) || defined(__LP64__) || defined(__arch64__) || defined(_WIN64)\n# define PDO_FIREBIRD_HANDLE_INITIALIZER 0U\n#else\n# define PDO_FIREBIRD_HANDLE_INITIALIZER NULL\n#endif\n\ntypedef struct {\n\tint sqlcode;\n\tchar *errmsg;\n\tsize_t errmsg_length;\n} pdo_firebird_error_info;\n\ntypedef struct {\n\t/* the result of the last API call */\n\tISC_STATUS isc_status[20];\n\n\t/* the connection handle */\n\tisc_db_handle db;\n\n\t/* the transaction handle */\n\tisc_tr_handle tr;\n\tbool in_manually_txn;\n\tbool is_writable_txn;\n\tzend_ulong txn_isolation_level;\n\n\t/* date and time format strings, can be set by the set_attribute method */\n\tchar *date_format;\n\tchar *time_format;\n\tchar *timestamp_format;\n\n\tunsigned sql_dialect:2;\n\n\t/* prepend table names on column names in fetch */\n\tunsigned fetch_table_names:1;\n\n\tunsigned _reserved:29;\n\n\tpdo_firebird_error_info einfo;\n} pdo_firebird_db_handle;\n\n\ntypedef struct {\n\t/* the link that owns this statement */\n\tpdo_firebird_db_handle *H;\n\n\t/* the statement handle */\n\tisc_stmt_handle stmt;\n\n\t/* the name of the cursor (if it has one) */\n\tchar name[32];\n\n\t/* the type of statement that was issued */\n\tchar statement_type:8;\n\n\t/* whether EOF was reached for this statement */\n\tunsigned exhausted:1;\n\n\t/* successful isc_dsql_execute opens a cursor */\n\tunsigned cursor_open:1;\n\n\tunsigned _reserved:22;\n\n\t/* the named params that were converted to ?'s by the driver */\n\tHashTable *named_params;\n\n\t/* the input SQLDA */\n\tXSQLDA *in_sqlda;\n\n\t/* the output SQLDA */\n\tXSQLDA out_sqlda; /* last member */\n} pdo_firebird_stmt;\n\nextern const pdo_driver_t pdo_firebird_driver;\n\nextern const struct pdo_stmt_methods firebird_stmt_methods;\n\nextern void php_firebird_set_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *state, const size_t state_len,\n\tconst char *msg, const size_t msg_len);\n#define php_firebird_error(d) php_firebird_set_error(d, NULL, NULL, 0, NULL, 0)\n#define php_firebird_error_stmt(s) php_firebird_set_error(s->dbh, s, NULL, 0, NULL, 0)\n#define php_firebird_error_with_info(d,e,el,m,ml) php_firebird_set_error(d, NULL, e, el, m, ml)\n#define php_firebird_error_stmt_with_info(s,e,el,m,ml) php_firebird_set_error(s->dbh, s, e, el, m, ml)\n\nextern bool php_firebird_commit_transaction(pdo_dbh_t *dbh, bool retain);\n\nenum {\n\tPDO_FB_ATTR_DATE_FORMAT = PDO_ATTR_DRIVER_SPECIFIC,\n\tPDO_FB_ATTR_TIME_FORMAT,\n\tPDO_FB_ATTR_TIMESTAMP_FORMAT,\n\n\t/*\n\t * transaction isolation level\n\t * firebird does not have a level equivalent to read uncommited.\n\t */\n\tPDO_FB_TRANSACTION_ISOLATION_LEVEL,\n\tPDO_FB_READ_COMMITTED,\n\tPDO_FB_REPEATABLE_READ,\n\tPDO_FB_SERIALIZABLE,\n\n\t/* transaction access mode */\n\tPDO_FB_WRITABLE_TRANSACTION,\n};\n\n#endif\t/* PHP_PDO_FIREBIRD_INT_H */\n"
  },
  {
    "path": "thirdparty/php84/pdo_odbc/odbc_driver.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n#define SW_USE_ODBC_HOOK\n#include \"php_swoole_odbc.h\"\n\n#if PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"pdo/php_pdo.h\"\n#include \"pdo/php_pdo_driver.h\"\n#include \"zend_exceptions.h\"\n#include <php_odbc_utils.h>\n\nstatic void pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    pdo_odbc_errinfo *einfo = &H->einfo;\n    pdo_odbc_stmt *S = NULL;\n    zend_string *message = NULL;\n\n    if (stmt) {\n        S = (pdo_odbc_stmt *) stmt->driver_data;\n        einfo = &S->einfo;\n    }\n\n    message = strpprintf(0,\n                         \"%s (%s[%ld] at %s:%d)\",\n                         einfo->last_err_msg,\n                         einfo->what,\n                         (long) einfo->last_error,\n                         einfo->file,\n                         einfo->line);\n\n    add_next_index_long(info, einfo->last_error);\n    add_next_index_str(info, message);\n    add_next_index_string(info, einfo->last_state);\n}\n\nvoid pdo_odbc_error(\n    pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line) /* {{{ */\n{\n    SQLRETURN rc;\n    SQLSMALLINT errmsgsize = 0;\n    SQLHANDLE eh;\n    SQLSMALLINT htype, recno = 1;\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    pdo_odbc_errinfo *einfo = &H->einfo;\n    pdo_odbc_stmt *S = NULL;\n    pdo_error_type *pdo_err = &dbh->error_code;\n\n    if (stmt) {\n        S = (pdo_odbc_stmt *) stmt->driver_data;\n\n        einfo = &S->einfo;\n        pdo_err = &stmt->error_code;\n    }\n\n    if (statement == SQL_NULL_HSTMT && S) {\n        statement = S->stmt;\n    }\n\n    if (statement) {\n        htype = SQL_HANDLE_STMT;\n        eh = statement;\n    } else if (H->dbc) {\n        htype = SQL_HANDLE_DBC;\n        eh = H->dbc;\n    } else {\n        htype = SQL_HANDLE_ENV;\n        eh = H->env;\n    }\n\n    rc = SQLGetDiagRec(htype,\n                       eh,\n                       recno++,\n                       (SQLCHAR *) einfo->last_state,\n                       &einfo->last_error,\n                       (SQLCHAR *) einfo->last_err_msg,\n                       sizeof(einfo->last_err_msg) - 1,\n                       &errmsgsize);\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        errmsgsize = 0;\n    }\n\n    einfo->last_err_msg[errmsgsize] = '\\0';\n    einfo->file = file;\n    einfo->line = line;\n    einfo->what = what;\n\n    strcpy(*pdo_err, einfo->last_state);\n    /* printf(\"@@ SQLSTATE[%s] %s\\n\", *pdo_err, einfo->last_err_msg); */\n    if (!dbh->methods) {\n        zend_throw_exception_ex(php_pdo_get_exception(),\n                                einfo->last_error,\n                                \"SQLSTATE[%s] %s: %d %s\",\n                                *pdo_err,\n                                what,\n                                einfo->last_error,\n                                einfo->last_err_msg);\n    }\n\n    /* just like a cursor, once you start pulling, you need to keep\n     * going until the end; SQL Server (at least) will mess with the\n     * actual cursor state if you don't finish retrieving all the\n     * diagnostic records (which can be generated by PRINT statements\n     * in the query, for instance). */\n    while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {\n        SQLCHAR discard_state[6];\n        SQLCHAR discard_buf[1024];\n        SQLINTEGER code;\n        rc = SQLGetDiagRec(htype, eh, recno++, discard_state, &code, discard_buf, sizeof(discard_buf) - 1, &errmsgsize);\n    }\n}\n/* }}} */\n\nstatic void odbc_handle_closer(pdo_dbh_t *dbh) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n\n    if (H->dbc != SQL_NULL_HANDLE) {\n        SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);\n        SQLDisconnect(H->dbc);\n        SQLFreeHandle(SQL_HANDLE_DBC, H->dbc);\n        H->dbc = NULL;\n    }\n    SQLFreeHandle(SQL_HANDLE_ENV, H->env);\n    H->env = NULL;\n    pefree(H, dbh->is_persistent);\n    dbh->driver_data = NULL;\n}\n\nstatic bool odbc_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) {\n    RETCODE rc;\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    pdo_odbc_stmt *S = ecalloc(1, sizeof(*S));\n    enum pdo_cursor_type cursor_type = PDO_CURSOR_FWDONLY;\n    int ret;\n    zend_string *nsql = NULL;\n\n    S->H = H;\n    S->assume_utf8 = H->assume_utf8;\n\n    /* before we prepare, we need to peek at the query; if it uses named parameters,\n     * we want PDO to rewrite them for us */\n    stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL;\n    ret = pdo_parse_params(stmt, sql, &nsql);\n\n    if (ret == 1) {\n        /* query was re-written */\n        sql = nsql;\n    } else if (ret == -1) {\n        /* couldn't grok it */\n        strcpy(dbh->error_code, stmt->error_code);\n        efree(S);\n        return false;\n    }\n\n    rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &S->stmt);\n\n    if (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) {\n        efree(S);\n        if (nsql) {\n            zend_string_release(nsql);\n        }\n        pdo_odbc_drv_error(\"SQLAllocStmt\");\n        return false;\n    }\n\n    stmt->driver_data = S;\n\n    cursor_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY);\n    if (cursor_type != PDO_CURSOR_FWDONLY) {\n        rc = SQLSetStmtAttr(S->stmt, SQL_ATTR_CURSOR_SCROLLABLE, (void *) SQL_SCROLLABLE, 0);\n        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n            pdo_odbc_stmt_error(\"SQLSetStmtAttr: SQL_ATTR_CURSOR_SCROLLABLE\");\n            SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);\n            if (nsql) {\n                zend_string_release(nsql);\n            }\n            return false;\n        }\n    }\n\n    rc = SQLPrepare(S->stmt, (SQLCHAR *) ZSTR_VAL(sql), SQL_NTS);\n    if (nsql) {\n        zend_string_release(nsql);\n    }\n\n    stmt->methods = &odbc_stmt_methods;\n\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_stmt_error(\"SQLPrepare\");\n        if (rc != SQL_SUCCESS_WITH_INFO) {\n            /* clone error information into the db handle */\n            strcpy(H->einfo.last_err_msg, S->einfo.last_err_msg);\n            H->einfo.file = S->einfo.file;\n            H->einfo.line = S->einfo.line;\n            H->einfo.what = S->einfo.what;\n            strcpy(dbh->error_code, stmt->error_code);\n        }\n    }\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        return false;\n    }\n    return true;\n}\n\nstatic zend_long odbc_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    RETCODE rc;\n    SQLLEN row_count = -1;\n    PDO_ODBC_HSTMT stmt;\n\n    rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt);\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_drv_error(\"SQLAllocHandle: STMT\");\n        return -1;\n    }\n\n    rc = SQLExecDirect(stmt, (SQLCHAR *) ZSTR_VAL(sql), ZSTR_LEN(sql));\n\n    if (rc == SQL_NO_DATA) {\n        /* If SQLExecDirect executes a searched update or delete statement that\n         * does not affect any rows at the data source, the call to\n         * SQLExecDirect returns SQL_NO_DATA. */\n        row_count = 0;\n        goto out;\n    }\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_doer_error(\"SQLExecDirect\");\n        goto out;\n    }\n\n    rc = SQLRowCount(stmt, &row_count);\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_doer_error(\"SQLRowCount\");\n        goto out;\n    }\n    if (row_count == -1) {\n        row_count = 0;\n    }\nout:\n    SQLFreeHandle(SQL_HANDLE_STMT, stmt);\n    return row_count;\n}\n\n/* TODO: Do ODBC quoter\nstatic int odbc_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t\n*quotedlen, enum pdo_param_type param_type )\n{\n        // pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;\n        // TODO: figure it out\n        return 0;\n}\n*/\n\nstatic bool odbc_handle_begin(pdo_dbh_t *dbh) {\n    if (dbh->auto_commit) {\n        /* we need to disable auto-commit now, to be able to initiate a transaction */\n        RETCODE rc;\n        pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n\n        rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER);\n        if (rc != SQL_SUCCESS) {\n            pdo_odbc_drv_error(\"SQLSetConnectAttr AUTOCOMMIT = OFF\");\n            return false;\n        }\n    }\n    return true;\n}\n\nstatic bool odbc_handle_commit(pdo_dbh_t *dbh) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    RETCODE rc;\n\n    rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT);\n\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_drv_error(\"SQLEndTran: Commit\");\n\n        if (rc != SQL_SUCCESS_WITH_INFO) {\n            return false;\n        }\n    }\n\n    if (dbh->auto_commit) {\n        /* turn auto-commit back on again */\n        rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);\n        if (rc != SQL_SUCCESS) {\n            pdo_odbc_drv_error(\"SQLSetConnectAttr AUTOCOMMIT = ON\");\n            return false;\n        }\n    }\n    return true;\n}\n\nstatic bool odbc_handle_rollback(pdo_dbh_t *dbh) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    RETCODE rc;\n\n    rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);\n\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_drv_error(\"SQLEndTran: Rollback\");\n\n        if (rc != SQL_SUCCESS_WITH_INFO) {\n            return false;\n        }\n    }\n    if (dbh->auto_commit && H->dbc) {\n        /* turn auto-commit back on again */\n        rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);\n        if (rc != SQL_SUCCESS) {\n            pdo_odbc_drv_error(\"SQLSetConnectAttr AUTOCOMMIT = ON\");\n            return false;\n        }\n    }\n\n    return true;\n}\n\nstatic bool odbc_handle_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    bool bval;\n\n    switch (attr) {\n    case PDO_ODBC_ATTR_ASSUME_UTF8:\n        if (!pdo_get_bool_param(&bval, val)) {\n            return false;\n        }\n        H->assume_utf8 = bval;\n        return true;\n    case PDO_ATTR_AUTOCOMMIT:\n        if (!pdo_get_bool_param(&bval, val)) {\n            return false;\n        }\n        if (dbh->in_txn) {\n            pdo_raise_impl_error(\n                dbh, NULL, \"HY000\", \"Cannot change autocommit mode while a transaction is already open\");\n            return false;\n        }\n        if (dbh->auto_commit ^ bval) {\n            dbh->auto_commit = bval;\n            RETCODE rc =\n                SQLSetConnectAttr(H->dbc,\n                                  SQL_ATTR_AUTOCOMMIT,\n                                  dbh->auto_commit ? (SQLPOINTER) SQL_AUTOCOMMIT_ON : (SQLPOINTER) SQL_AUTOCOMMIT_OFF,\n                                  SQL_IS_INTEGER);\n            if (rc != SQL_SUCCESS) {\n                pdo_odbc_drv_error(dbh->auto_commit ? \"SQLSetConnectAttr AUTOCOMMIT = ON\"\n                                                    : \"SQLSetConnectAttr AUTOCOMMIT = OFF\");\n                return false;\n            }\n        }\n        return true;\n    default:\n        strcpy(H->einfo.last_err_msg, \"Unknown Attribute\");\n        H->einfo.what = \"setAttribute\";\n        strcpy(H->einfo.last_state, \"IM001\");\n        return false;\n    }\n}\n\nstatic int pdo_odbc_get_info_string(pdo_dbh_t *dbh, SQLUSMALLINT type, zval *val) {\n    RETCODE rc;\n    SQLSMALLINT out_len;\n    char buf[256];\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    rc = SQLGetInfo(H->dbc, type, (SQLPOINTER) buf, sizeof(buf), &out_len);\n    /* returning -1 is treated as an error, not as unsupported */\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        return -1;\n    }\n    ZVAL_STRINGL(val, buf, out_len);\n    return 1;\n}\n\nstatic int odbc_handle_get_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) {\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n    switch (attr) {\n    case PDO_ATTR_CLIENT_VERSION:\n        ZVAL_STRING(val, \"ODBC-\" PDO_ODBC_TYPE);\n        return 1;\n\n    case PDO_ATTR_SERVER_VERSION:\n        return pdo_odbc_get_info_string(dbh, SQL_DBMS_VER, val);\n    case PDO_ATTR_SERVER_INFO:\n        return pdo_odbc_get_info_string(dbh, SQL_DBMS_NAME, val);\n    case PDO_ATTR_PREFETCH:\n    case PDO_ATTR_TIMEOUT:\n    case PDO_ATTR_CONNECTION_STATUS:\n        break;\n    case PDO_ODBC_ATTR_ASSUME_UTF8:\n        ZVAL_BOOL(val, H->assume_utf8);\n        return 1;\n    case PDO_ATTR_AUTOCOMMIT:\n        ZVAL_BOOL(val, dbh->auto_commit);\n        return 1;\n    }\n    return 0;\n}\n\nstatic zend_result odbc_handle_check_liveness(pdo_dbh_t *dbh) {\n    RETCODE ret;\n    UCHAR d_name[32];\n    SQLSMALLINT len;\n    SQLUINTEGER dead = SQL_CD_FALSE;\n    pdo_odbc_db_handle *H = (pdo_odbc_db_handle *) dbh->driver_data;\n\n    ret = SQLGetConnectAttr(H->dbc, SQL_ATTR_CONNECTION_DEAD, &dead, 0, NULL);\n    if (ret == SQL_SUCCESS && dead == SQL_CD_TRUE) {\n        /* Bail early here, since we know it's gone */\n        return FAILURE;\n    }\n    /*\n     * If the driver doesn't support SQL_ATTR_CONNECTION_DEAD, or if\n     * it returns false (which could be a false positive), fall back\n     * to using SQL_DATA_SOURCE_READ_ONLY, which isn't semantically\n     * correct, but works with many drivers.\n     */\n    ret = SQLGetInfo(H->dbc, SQL_DATA_SOURCE_READ_ONLY, d_name, sizeof(d_name), &len);\n\n    if (ret != SQL_SUCCESS || len == 0) {\n        return FAILURE;\n    }\n    return SUCCESS;\n}\n\nstatic const struct pdo_dbh_methods odbc_methods = {\n    odbc_handle_closer,\n    odbc_handle_preparer,\n    odbc_handle_doer,\n    NULL, /* quoter */\n    odbc_handle_begin,\n    odbc_handle_commit,\n    odbc_handle_rollback,\n    odbc_handle_set_attr,\n    NULL, /* last id */\n    pdo_odbc_fetch_error_func,\n    odbc_handle_get_attr,       /* get attr */\n    odbc_handle_check_liveness, /* check_liveness */\n    NULL,                       /* get_driver_methods */\n    NULL,                       /* request_shutdown */\n    NULL,                       /* in transaction, use PDO's internal tracking mechanism */\n    NULL,                       /* get_gc */\n    NULL                        /* scanner */\n};\n\nstatic int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */\n{\n    pdo_odbc_db_handle *H;\n    RETCODE rc;\n    int use_direct = 0;\n    zend_ulong cursor_lib;\n\n    H = pecalloc(1, sizeof(*H), dbh->is_persistent);\n\n    dbh->driver_data = H;\n\n    rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H->env);\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_drv_error(\"SQLAllocHandle: ENV\");\n        goto fail;\n    }\n\n    rc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_drv_error(\"SQLSetEnvAttr: ODBC3\");\n        goto fail;\n    }\n\n#ifdef SQL_ATTR_CONNECTION_POOLING\n    if (pdo_odbc_pool_on != SQL_CP_OFF) {\n        rc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void *) pdo_odbc_pool_mode, 0);\n        if (rc != SQL_SUCCESS) {\n            pdo_odbc_drv_error(\"SQLSetEnvAttr: SQL_ATTR_CP_MATCH\");\n            goto fail;\n        }\n    }\n#endif\n\n    rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc);\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_drv_error(\"SQLAllocHandle: DBC\");\n        goto fail;\n    }\n\n    rc = SQLSetConnectAttr(H->dbc,\n                           SQL_ATTR_AUTOCOMMIT,\n                           (SQLPOINTER) (intptr_t) (dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF),\n                           SQL_IS_INTEGER);\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_drv_error(\"SQLSetConnectAttr AUTOCOMMIT\");\n        goto fail;\n    }\n\n    /* set up the cursor library, if needed, or if configured explicitly */\n    cursor_lib = pdo_attr_lval(driver_options, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY, SQL_CUR_USE_IF_NEEDED);\n    rc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void *) cursor_lib, SQL_IS_INTEGER);\n    if (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) {\n        pdo_odbc_drv_error(\"SQLSetConnectAttr SQL_ODBC_CURSORS\");\n        goto fail;\n    }\n\n    /* a connection string may have = but not ; - i.e. \"DSN=PHP\" */\n    if (strchr(dbh->data_source, '=')) {\n        SQLCHAR dsnbuf[1024];\n        SQLSMALLINT dsnbuflen;\n\n        use_direct = 1;\n\n        size_t db_len = strlen(dbh->data_source);\n        bool use_uid_arg =\n            dbh->username != NULL && !php_memnistr(dbh->data_source, \"uid=\", strlen(\"uid=\"), dbh->data_source + db_len);\n        bool use_pwd_arg =\n            dbh->password != NULL && !php_memnistr(dbh->data_source, \"pwd=\", strlen(\"pwd=\"), dbh->data_source + db_len);\n\n        if (use_uid_arg || use_pwd_arg) {\n            char *db = (char *) emalloc(db_len + 1);\n            strcpy(db, dbh->data_source);\n            char *db_end = db + db_len;\n            db_end--;\n            if ((unsigned char) *(db_end) == ';') {\n                *db_end = '\\0';\n            }\n\n            char *uid = NULL, *pwd = NULL, *dsn = NULL;\n            bool should_quote_uid, should_quote_pwd;\n            size_t new_dsn_size;\n\n            if (use_uid_arg) {\n                should_quote_uid =\n                    !php_odbc_connstr_is_quoted(dbh->username) && php_odbc_connstr_should_quote(dbh->username);\n                if (should_quote_uid) {\n                    size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->username);\n                    uid = emalloc(estimated_length);\n                    php_odbc_connstr_quote(uid, dbh->username, estimated_length);\n                } else {\n                    uid = dbh->username;\n                }\n\n                if (!use_pwd_arg) {\n                    new_dsn_size = strlen(db) + strlen(uid) + strlen(\";UID=;\") + 1;\n                    dsn = pemalloc(new_dsn_size, dbh->is_persistent);\n                    snprintf(dsn, new_dsn_size, \"%s;UID=%s;\", db, uid);\n                }\n            }\n\n            if (use_pwd_arg) {\n                should_quote_pwd =\n                    !php_odbc_connstr_is_quoted(dbh->password) && php_odbc_connstr_should_quote(dbh->password);\n                if (should_quote_pwd) {\n                    size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->password);\n                    pwd = emalloc(estimated_length);\n                    php_odbc_connstr_quote(pwd, dbh->password, estimated_length);\n                } else {\n                    pwd = dbh->password;\n                }\n\n                if (!use_uid_arg) {\n                    new_dsn_size = strlen(db) + strlen(pwd) + strlen(\";PWD=;\") + 1;\n                    dsn = pemalloc(new_dsn_size, dbh->is_persistent);\n                    snprintf(dsn, new_dsn_size, \"%s;PWD=%s;\", db, pwd);\n                }\n            }\n\n            if (use_uid_arg && use_pwd_arg) {\n                new_dsn_size = strlen(db) + strlen(uid) + strlen(pwd) + strlen(\";UID=;PWD=;\") + 1;\n                dsn = pemalloc(new_dsn_size, dbh->is_persistent);\n                snprintf(dsn, new_dsn_size, \"%s;UID=%s;PWD=%s;\", db, uid, pwd);\n            }\n\n            pefree((char *) dbh->data_source, dbh->is_persistent);\n            dbh->data_source = dsn;\n            if (uid && should_quote_uid) {\n                efree(uid);\n            }\n            if (pwd && should_quote_pwd) {\n                efree(pwd);\n            }\n            efree(db);\n        }\n\n        rc = SQLDriverConnect(H->dbc,\n                              NULL,\n                              (SQLCHAR *) dbh->data_source,\n                              strlen(dbh->data_source),\n                              dsnbuf,\n                              sizeof(dsnbuf) - 1,\n                              &dsnbuflen,\n                              SQL_DRIVER_NOPROMPT);\n    }\n    if (!use_direct) {\n        rc = SQLConnect(H->dbc,\n                        (SQLCHAR *) dbh->data_source,\n                        SQL_NTS,\n                        (SQLCHAR *) dbh->username,\n                        SQL_NTS,\n                        (SQLCHAR *) dbh->password,\n                        SQL_NTS);\n    }\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_drv_error(use_direct ? \"SQLDriverConnect\" : \"SQLConnect\");\n        goto fail;\n    }\n\n    /* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */\n\n    dbh->methods = &odbc_methods;\n    dbh->alloc_own_columns = 1;\n\n    return 1;\n\nfail:\n    dbh->methods = &odbc_methods;\n    return 0;\n}\n/* }}} */\n\nconst pdo_driver_t swoole_pdo_odbc_driver = {PDO_DRIVER_HEADER(odbc), pdo_odbc_handle_factory};\n#endif\n"
  },
  {
    "path": "thirdparty/php84/pdo_odbc/odbc_stmt.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_ODBC_HOOK\n#include \"php_swoole_odbc.h\"\n\n#if PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"pdo/php_pdo.h\"\n#include \"pdo/php_pdo_driver.h\"\n\nenum pdo_odbc_conv_result { PDO_ODBC_CONV_NOT_REQUIRED, PDO_ODBC_CONV_OK, PDO_ODBC_CONV_FAIL };\n\nstatic int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SQLSMALLINT sqltype) {\n    if (!S->assume_utf8) return 0;\n    switch (sqltype) {\n#ifdef SQL_WCHAR\n    case SQL_WCHAR:\n        return 1;\n#endif\n#ifdef SQL_WLONGVARCHAR\n    case SQL_WLONGVARCHAR:\n        return 1;\n#endif\n#ifdef SQL_WVARCHAR\n    case SQL_WVARCHAR:\n        return 1;\n#endif\n    default:\n        return 0;\n    }\n}\n\nstatic int pdo_odbc_utf82ucs2(\n    pdo_stmt_t *stmt, int is_unicode, const char *buf, zend_ulong buflen, zend_ulong *outlen) {\n#ifdef PHP_WIN32\n    if (is_unicode && buflen) {\n        pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n        DWORD ret;\n\n        ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, NULL, 0);\n        if (ret == 0) {\n            /*printf(\"%s:%d %d [%d] %.*s\\n\", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/\n            return PDO_ODBC_CONV_FAIL;\n        }\n\n        ret *= sizeof(WCHAR);\n\n        if (S->convbufsize <= ret) {\n            S->convbufsize = ret + sizeof(WCHAR);\n            S->convbuf = erealloc(S->convbuf, S->convbufsize);\n        }\n\n        ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, (LPWSTR) S->convbuf, S->convbufsize / sizeof(WCHAR));\n        if (ret == 0) {\n            /*printf(\"%s:%d %d [%d] %.*s\\n\", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/\n            return PDO_ODBC_CONV_FAIL;\n        }\n\n        ret *= sizeof(WCHAR);\n        *outlen = ret;\n        return PDO_ODBC_CONV_OK;\n    }\n#endif\n    return PDO_ODBC_CONV_NOT_REQUIRED;\n}\n\nstatic int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, zval *result) {\n#ifdef PHP_WIN32\n    ZEND_ASSERT(Z_TYPE_P(result) == IS_STRING);\n    if (is_unicode && Z_STRLEN_P(result) != 0) {\n        pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n        DWORD ret;\n\n        ret = WideCharToMultiByte(\n            CP_UTF8, 0, (LPCWSTR) Z_STRVAL_P(result), Z_STRLEN_P(result) / sizeof(WCHAR), NULL, 0, NULL, NULL);\n        if (ret == 0) {\n            return PDO_ODBC_CONV_FAIL;\n        }\n\n        zend_string *str = zend_string_alloc(ret, 0);\n        ret = WideCharToMultiByte(CP_UTF8,\n                                  0,\n                                  (LPCWSTR) Z_STRVAL_P(result),\n                                  Z_STRLEN_P(result) / sizeof(WCHAR),\n                                  ZSTR_VAL(str),\n                                  ZSTR_LEN(str),\n                                  NULL,\n                                  NULL);\n        if (ret == 0) {\n            return PDO_ODBC_CONV_FAIL;\n        }\n\n        ZSTR_VAL(str)[ret] = '\\0';\n        zval_ptr_dtor_str(result);\n        ZVAL_STR(result, str);\n        return PDO_ODBC_CONV_OK;\n    }\n#endif\n    return PDO_ODBC_CONV_NOT_REQUIRED;\n}\n\nstatic void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S) {\n    if (S->cols) {\n        int i;\n\n        for (i = 0; i < S->col_count; i++) {\n            if (S->cols[i].data) {\n                efree(S->cols[i].data);\n            }\n        }\n        efree(S->cols);\n        S->cols = NULL;\n        S->col_count = 0;\n    }\n}\n\nstatic int odbc_stmt_dtor(pdo_stmt_t *stmt) {\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    if (S->stmt != SQL_NULL_HANDLE) {\n        if (stmt->executed) {\n            SQLCloseCursor(S->stmt);\n        }\n        SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);\n        S->stmt = SQL_NULL_HANDLE;\n    }\n\n    free_cols(stmt, S);\n    if (S->convbuf) {\n        efree(S->convbuf);\n    }\n    efree(S);\n\n    return 1;\n}\n\nstatic int odbc_stmt_execute(pdo_stmt_t *stmt) {\n    RETCODE rc, rc1;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n    char *buf = NULL;\n    SQLLEN row_count = -1;\n\n    if (stmt->executed) {\n        SQLCloseCursor(S->stmt);\n    }\n\n    rc = SQLExecute(S->stmt);\n\n    while (rc == SQL_NEED_DATA) {\n        struct pdo_bound_param_data *param;\n\n        rc = SQLParamData(S->stmt, (SQLPOINTER *) &param);\n        if (rc == SQL_NEED_DATA) {\n            php_stream *stm;\n            int len;\n            pdo_odbc_param *P;\n            zval *parameter;\n\n            P = (pdo_odbc_param *) param->driver_data;\n            if (Z_ISREF(param->parameter)) {\n                parameter = Z_REFVAL(param->parameter);\n            } else {\n                parameter = &param->parameter;\n            }\n            if (Z_TYPE_P(parameter) != IS_RESOURCE) {\n                /* they passed in a string */\n                zend_ulong ulen;\n                convert_to_string(parameter);\n\n                switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), &ulen)) {\n                case PDO_ODBC_CONV_NOT_REQUIRED:\n                    rc1 = SQLPutData(S->stmt, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter));\n                    if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {\n                        rc = rc1;\n                    }\n                    break;\n                case PDO_ODBC_CONV_OK:\n                    rc1 = SQLPutData(S->stmt, S->convbuf, ulen);\n                    if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {\n                        rc = rc1;\n                    }\n                    break;\n                case PDO_ODBC_CONV_FAIL:\n                    pdo_odbc_stmt_error(\"error converting input string\");\n                    SQLCloseCursor(S->stmt);\n                    if (buf) {\n                        efree(buf);\n                    }\n                    return 0;\n                }\n                continue;\n            }\n\n            /* we assume that LOBs are binary and don't need charset\n             * conversion */\n\n            php_stream_from_zval_no_verify(stm, parameter);\n            if (!stm) {\n                /* shouldn't happen either */\n                pdo_odbc_stmt_error(\"input LOB is no longer a stream\");\n                SQLCloseCursor(S->stmt);\n                if (buf) {\n                    efree(buf);\n                }\n                return 0;\n            }\n\n            /* now suck data from the stream and stick it into the database */\n            if (buf == NULL) {\n                buf = emalloc(8192);\n            }\n\n            do {\n                len = php_stream_read(stm, buf, 8192);\n                if (len == 0) {\n                    break;\n                }\n                rc1 = SQLPutData(S->stmt, buf, len);\n                if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {\n                    rc = rc1;\n                }\n            } while (1);\n        }\n    }\n\n    if (buf) {\n        efree(buf);\n    }\n\n    switch (rc) {\n    case SQL_SUCCESS:\n        break;\n    case SQL_NO_DATA_FOUND:\n    case SQL_SUCCESS_WITH_INFO:\n        pdo_odbc_stmt_error(\"SQLExecute\");\n        break;\n\n    default:\n        pdo_odbc_stmt_error(\"SQLExecute\");\n        return 0;\n    }\n\n    SQLRowCount(S->stmt, &row_count);\n    stmt->row_count = row_count;\n\n    if (S->cols == NULL) {\n        /* do first-time-only definition of bind/mapping stuff */\n        SQLSMALLINT colcount;\n\n        /* how many columns do we have ? */\n        SQLNumResultCols(S->stmt, &colcount);\n\n        stmt->column_count = S->col_count = (int) colcount;\n        S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));\n        S->going_long = 0;\n    }\n\n    return 1;\n}\n\nstatic int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, enum pdo_param_event event_type) {\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n    RETCODE rc;\n    SQLSMALLINT sqltype = 0, ctype = 0, scale = 0, nullable = 0;\n    SQLULEN precision = 0;\n    pdo_odbc_param *P;\n    zval *parameter;\n\n    /* we're only interested in parameters for prepared SQL right now */\n    if (param->is_param) {\n        switch (event_type) {\n        case PDO_PARAM_EVT_FETCH_PRE:\n        case PDO_PARAM_EVT_FETCH_POST:\n        case PDO_PARAM_EVT_NORMALIZE:\n            /* Do nothing */\n            break;\n\n        case PDO_PARAM_EVT_FREE:\n            P = param->driver_data;\n            if (P) {\n                efree(P);\n            }\n            break;\n\n        case PDO_PARAM_EVT_ALLOC: {\n            /* figure out what we're doing */\n            switch (PDO_PARAM_TYPE(param->param_type)) {\n            case PDO_PARAM_LOB:\n                break;\n\n            case PDO_PARAM_STMT:\n                return 0;\n\n            default:\n                break;\n            }\n\n            rc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno + 1, &sqltype, &precision, &scale, &nullable);\n            if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n                /* MS Access, for instance, doesn't support SQLDescribeParam,\n                 * so we need to guess */\n                switch (PDO_PARAM_TYPE(param->param_type)) {\n                case PDO_PARAM_INT:\n                    sqltype = SQL_INTEGER;\n                    break;\n                case PDO_PARAM_LOB:\n                    sqltype = SQL_LONGVARBINARY;\n                    break;\n                default:\n                    sqltype = SQL_LONGVARCHAR;\n                }\n                precision = 4000;\n                scale = 5;\n                nullable = 1;\n\n                if (param->max_value_len > 0) {\n                    precision = param->max_value_len;\n                }\n            }\n            if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) {\n                ctype = SQL_C_BINARY;\n            } else {\n                ctype = SQL_C_CHAR;\n            }\n\n            P = emalloc(sizeof(*P));\n            param->driver_data = P;\n\n            P->len = 0; /* is re-populated each EXEC_PRE */\n            P->outbuf = NULL;\n\n            P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype);\n            if (P->is_unicode) {\n                /* avoid driver auto-translation: we'll do it ourselves */\n                ctype = SQL_C_BINARY;\n            }\n\n            if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) {\n                P->paramtype = SQL_PARAM_INPUT_OUTPUT;\n            } else if (param->max_value_len <= 0) {\n                P->paramtype = SQL_PARAM_INPUT;\n            } else {\n                P->paramtype = SQL_PARAM_OUTPUT;\n            }\n\n            if (P->paramtype != SQL_PARAM_INPUT) {\n                if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) {\n                    /* need an explicit buffer to hold result */\n                    P->len = param->max_value_len > 0 ? param->max_value_len : precision;\n                    if (P->is_unicode) {\n                        P->len *= 2;\n                    }\n                    P->outbuf = emalloc(P->len + (P->is_unicode ? 2 : 1));\n                }\n            }\n\n            if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) {\n                pdo_odbc_stmt_error(\"Can't bind a lob for output\");\n                return 0;\n            }\n\n            rc = SQLBindParameter(S->stmt,\n                                  (SQLUSMALLINT) param->paramno + 1,\n                                  P->paramtype,\n                                  ctype,\n                                  sqltype,\n                                  precision,\n                                  scale,\n                                  P->paramtype == SQL_PARAM_INPUT ? (SQLPOINTER) param : P->outbuf,\n                                  P->len,\n                                  &P->len);\n\n            if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {\n                return 1;\n            }\n            pdo_odbc_stmt_error(\"SQLBindParameter\");\n            return 0;\n        }\n\n        case PDO_PARAM_EVT_EXEC_PRE:\n            P = param->driver_data;\n            if (!Z_ISREF(param->parameter)) {\n                parameter = &param->parameter;\n            } else {\n                parameter = Z_REFVAL(param->parameter);\n            }\n\n            if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {\n                if (Z_TYPE_P(parameter) == IS_RESOURCE) {\n                    php_stream *stm;\n                    php_stream_statbuf sb;\n\n                    php_stream_from_zval_no_verify(stm, parameter);\n\n                    if (!stm) {\n                        return 0;\n                    }\n\n                    if (0 == php_stream_stat(stm, &sb)) {\n                        if (P->outbuf) {\n                            int len, amount;\n                            char *ptr = P->outbuf;\n                            char *end = P->outbuf + P->len;\n\n                            P->len = 0;\n                            do {\n                                amount = end - ptr;\n                                if (amount == 0) {\n                                    break;\n                                }\n                                if (amount > 8192) amount = 8192;\n                                len = php_stream_read(stm, ptr, amount);\n                                if (len == 0) {\n                                    break;\n                                }\n                                ptr += len;\n                                P->len += len;\n                            } while (1);\n\n                        } else {\n                            P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size);\n                        }\n                    } else {\n                        if (P->outbuf) {\n                            P->len = 0;\n                        } else {\n                            P->len = SQL_LEN_DATA_AT_EXEC(0);\n                        }\n                    }\n                } else {\n                    convert_to_string(parameter);\n                    if (P->outbuf) {\n                        P->len = Z_STRLEN_P(parameter);\n                        memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len);\n                    } else {\n                        P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));\n                    }\n                }\n            } else if (Z_TYPE_P(parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) {\n                P->len = SQL_NULL_DATA;\n            } else {\n                convert_to_string(parameter);\n                if (P->outbuf) {\n                    zend_ulong ulen;\n                    switch (\n                        pdo_odbc_utf82ucs2(stmt, P->is_unicode, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), &ulen)) {\n                    case PDO_ODBC_CONV_FAIL:\n                    case PDO_ODBC_CONV_NOT_REQUIRED:\n                        P->len = Z_STRLEN_P(parameter);\n                        memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len);\n                        break;\n                    case PDO_ODBC_CONV_OK:\n                        P->len = ulen;\n                        memcpy(P->outbuf, S->convbuf, P->len);\n                        break;\n                    }\n                } else {\n                    P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));\n                }\n            }\n            return 1;\n\n        case PDO_PARAM_EVT_EXEC_POST:\n            P = param->driver_data;\n\n            if (P->outbuf) {\n                if (Z_ISREF(param->parameter)) {\n                    parameter = Z_REFVAL(param->parameter);\n                } else {\n                    parameter = &param->parameter;\n                }\n                zval_ptr_dtor(parameter);\n\n                if (P->len >= 0) {\n                    ZVAL_STRINGL(parameter, P->outbuf, P->len);\n                    switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, parameter)) {\n                    case PDO_ODBC_CONV_FAIL:\n                        /* something fishy, but allow it to come back as binary */\n                    case PDO_ODBC_CONV_NOT_REQUIRED:\n                        break;\n                    case PDO_ODBC_CONV_OK:\n                        break;\n                    }\n                } else {\n                    ZVAL_NULL(parameter);\n                }\n            }\n            return 1;\n        }\n    }\n    return 1;\n}\n\nstatic int odbc_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) {\n    RETCODE rc;\n    SQLSMALLINT odbcori;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    switch (ori) {\n    case PDO_FETCH_ORI_NEXT:\n        odbcori = SQL_FETCH_NEXT;\n        break;\n    case PDO_FETCH_ORI_PRIOR:\n        odbcori = SQL_FETCH_PRIOR;\n        break;\n    case PDO_FETCH_ORI_FIRST:\n        odbcori = SQL_FETCH_FIRST;\n        break;\n    case PDO_FETCH_ORI_LAST:\n        odbcori = SQL_FETCH_LAST;\n        break;\n    case PDO_FETCH_ORI_ABS:\n        odbcori = SQL_FETCH_ABSOLUTE;\n        break;\n    case PDO_FETCH_ORI_REL:\n        odbcori = SQL_FETCH_RELATIVE;\n        break;\n    default:\n        strcpy(stmt->error_code, \"HY106\");\n        return 0;\n    }\n    rc = SQLFetchScroll(S->stmt, odbcori, offset);\n\n    if (rc == SQL_SUCCESS) {\n        return 1;\n    }\n    if (rc == SQL_SUCCESS_WITH_INFO) {\n        pdo_odbc_stmt_error(\"SQLFetchScroll\");\n        return 1;\n    }\n\n    if (rc == SQL_NO_DATA) {\n        /* pdo_odbc_stmt_error(\"SQLFetchScroll\"); */\n        return 0;\n    }\n\n    pdo_odbc_stmt_error(\"SQLFetchScroll\");\n\n    return 0;\n}\n\nstatic int odbc_stmt_describe(pdo_stmt_t *stmt, int colno) {\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n    struct pdo_column_data *col = &stmt->columns[colno];\n    RETCODE rc;\n    SQLSMALLINT colnamelen;\n    SQLULEN colsize;\n    SQLLEN displaysize = 0;\n\n    rc = SQLDescribeCol(S->stmt,\n                        colno + 1,\n                        (SQLCHAR *) S->cols[colno].colname,\n                        sizeof(S->cols[colno].colname) - 1,\n                        &colnamelen,\n                        &S->cols[colno].coltype,\n                        &colsize,\n                        NULL,\n                        NULL);\n\n    /* This fixes a known issue with SQL Server and (max) lengths,\n    may affect others as well.  If we are SQL_VARCHAR,\n    SQL_VARBINARY, or SQL_WVARCHAR (or any of the long variations)\n    and zero is returned from colsize then consider it long */\n    if (0 == colsize && (S->cols[colno].coltype == SQL_VARCHAR || S->cols[colno].coltype == SQL_LONGVARCHAR ||\n#ifdef SQL_WVARCHAR\n                         S->cols[colno].coltype == SQL_WVARCHAR ||\n#endif\n#ifdef SQL_WLONGVARCHAR\n                         S->cols[colno].coltype == SQL_WLONGVARCHAR ||\n#endif\n                         S->cols[colno].coltype == SQL_VARBINARY || S->cols[colno].coltype == SQL_LONGVARBINARY)) {\n        S->going_long = 1;\n    }\n\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_stmt_error(\"SQLDescribeCol\");\n        if (rc != SQL_SUCCESS_WITH_INFO) {\n            return 0;\n        }\n    }\n\n    rc = SQLColAttribute(S->stmt, colno + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &displaysize);\n\n    if (rc != SQL_SUCCESS) {\n        pdo_odbc_stmt_error(\"SQLColAttribute\");\n        if (rc != SQL_SUCCESS_WITH_INFO) {\n            return 0;\n        }\n    }\n    colsize = displaysize;\n\n    col->maxlen = S->cols[colno].datalen = colsize;\n    col->name = zend_string_init(S->cols[colno].colname, colnamelen, 0);\n    S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype);\n\n    /* tell ODBC to put it straight into our buffer, but only if it\n     * isn't \"long\" data, and only if we haven't already bound a long\n     * column. */\n    if (colsize < 256 && !S->going_long) {\n        S->cols[colno].data = emalloc(colsize + 1);\n        S->cols[colno].is_long = 0;\n\n        rc = SQLBindCol(S->stmt,\n                        colno + 1,\n                        S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR,\n                        S->cols[colno].data,\n                        S->cols[colno].datalen + 1,\n                        &S->cols[colno].fetched_len);\n\n        if (rc != SQL_SUCCESS) {\n            pdo_odbc_stmt_error(\"SQLBindCol\");\n            return 0;\n        }\n    } else {\n        /* allocate a smaller buffer to keep around for smaller\n         * \"long\" columns */\n        S->cols[colno].data = emalloc(256);\n        S->going_long = 1;\n        S->cols[colno].is_long = 1;\n    }\n\n    return 1;\n}\n\nstatic int odbc_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) {\n    array_init(return_value);\n    add_assoc_long(return_value, \"pdo_type\", PDO_PARAM_STR);\n    return 1;\n}\n\nstatic int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) {\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n    pdo_odbc_column *C = &S->cols[colno];\n\n    /* if it is a column containing \"long\" data, perform late binding now */\n    if (C->is_long) {\n        SQLLEN orig_fetched_len = SQL_NULL_DATA;\n        RETCODE rc;\n\n        /* fetch it into C->data, which is allocated with a length\n         * of 256 bytes; if there is more to be had, we then allocate\n         * bigger buffer for the caller to free */\n\n        rc = SQLGetData(S->stmt, colno + 1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data, 256, &C->fetched_len);\n        orig_fetched_len = C->fetched_len;\n\n        if (rc == SQL_SUCCESS && C->fetched_len < 256) {\n            /* all the data fit into our little buffer;\n             * jump down to the generic bound data case */\n            goto in_data;\n        }\n\n        if (rc == SQL_SUCCESS_WITH_INFO || rc == SQL_SUCCESS) {\n            /* this is a 'long column'\n\n             read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks\n             in order into the output buffer; 255 bytes are an optimistic assumption, since the driver may assert\n             more or less NUL bytes at the end; we cater to that later, if actual length information is available\n\n             this loop has to work whether or not SQLGetData() provides the total column length.\n             calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read\n             for that size would be slower except maybe for extremely long columns.*/\n            char *buf2 = emalloc(256);\n            zend_string *str = zend_string_init(C->data, 256, 0);\n            size_t used = 255; /* not 256; the driver NUL terminated the buffer */\n\n            do {\n                C->fetched_len = 0;\n                /* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */\n                rc = SQLGetData(\n                    S->stmt, colno + 1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, buf2, 256, &C->fetched_len);\n\n                /* adjust `used` in case we have length info from the driver */\n                if (orig_fetched_len >= 0 && C->fetched_len >= 0) {\n                    SQLLEN fixed_used = orig_fetched_len - C->fetched_len;\n                    ZEND_ASSERT(fixed_used <= used + 1);\n                    used = fixed_used;\n                }\n\n                /* resize output buffer and reassemble block */\n                if (rc == SQL_SUCCESS_WITH_INFO || (rc == SQL_SUCCESS && C->fetched_len > 255)) {\n                    /* point 5, in section \"Retrieving Data with SQLGetData\" in\n                     http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx states that if\n                     SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size) (if a driver fails to\n                     follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */\n                    str = zend_string_realloc(str, used + 256, 0);\n                    memcpy(ZSTR_VAL(str) + used, buf2, 256);\n                    used = used + 255;\n                } else if (rc == SQL_SUCCESS) {\n                    str = zend_string_realloc(str, used + C->fetched_len, 0);\n                    memcpy(ZSTR_VAL(str) + used, buf2, C->fetched_len);\n                    used = used + C->fetched_len;\n                } else {\n                    /* includes SQL_NO_DATA */\n                    break;\n                }\n\n            } while (1);\n\n            efree(buf2);\n\n            /* NULL terminate the buffer once, when finished, for use with the rest of PHP */\n            ZSTR_VAL(str)[used] = '\\0';\n            ZVAL_STR(result, str);\n            if (C->is_unicode) {\n                goto unicode_conv;\n            }\n            return 1;\n        }\n\n        /* something went caca */\n        return 1;\n    }\n\nin_data:\n    /* check the indicator to ensure that the data is intact */\n    if (C->fetched_len == SQL_NULL_DATA) {\n        /* A NULL value */\n        ZVAL_NULL(result);\n        return 1;\n    } else if (C->fetched_len >= 0) {\n        /* it was stored perfectly */\n        ZVAL_STRINGL_FAST(result, C->data, C->fetched_len);\n        if (C->is_unicode) {\n            goto unicode_conv;\n        }\n        return 1;\n    } else {\n        /* no data? */\n        ZVAL_NULL(result);\n        return 1;\n    }\n\nunicode_conv:\n    switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, result)) {\n    case PDO_ODBC_CONV_FAIL:\n        /* oh well.  They can have the binary version of it */\n    case PDO_ODBC_CONV_NOT_REQUIRED:\n        /* shouldn't happen... */\n        return 1;\n    case PDO_ODBC_CONV_OK:\n        return 1;\n    }\n    return 1;\n}\n\nstatic int odbc_stmt_set_param(pdo_stmt_t *stmt, zend_long attr, zval *val) {\n    SQLRETURN rc;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    switch (attr) {\n    case PDO_ATTR_CURSOR_NAME:\n        convert_to_string(val);\n        rc = SQLSetCursorName(S->stmt, (SQLCHAR *) Z_STRVAL_P(val), Z_STRLEN_P(val));\n\n        if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {\n            return 1;\n        }\n        pdo_odbc_stmt_error(\"SQLSetCursorName\");\n        return 0;\n\n    case PDO_ODBC_ATTR_ASSUME_UTF8:\n        S->assume_utf8 = zval_is_true(val);\n        return 0;\n    default:\n        strcpy(S->einfo.last_err_msg, \"Unknown Attribute\");\n        S->einfo.what = \"setAttribute\";\n        strcpy(S->einfo.last_state, \"IM001\");\n        return -1;\n    }\n}\n\nstatic int odbc_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val) {\n    SQLRETURN rc;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    switch (attr) {\n    case PDO_ATTR_CURSOR_NAME: {\n        char buf[256];\n        SQLSMALLINT len = 0;\n        rc = SQLGetCursorName(S->stmt, (SQLCHAR *) buf, sizeof(buf), &len);\n\n        if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {\n            ZVAL_STRINGL(val, buf, len);\n            return 1;\n        }\n        pdo_odbc_stmt_error(\"SQLGetCursorName\");\n        return 0;\n    }\n\n    case PDO_ODBC_ATTR_ASSUME_UTF8:\n        ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0);\n        return 0;\n\n    default:\n        strcpy(S->einfo.last_err_msg, \"Unknown Attribute\");\n        S->einfo.what = \"getAttribute\";\n        strcpy(S->einfo.last_state, \"IM001\");\n        return -1;\n    }\n}\n\nstatic int odbc_stmt_next_rowset(pdo_stmt_t *stmt) {\n    SQLRETURN rc;\n    SQLSMALLINT colcount;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    /* NOTE: can't guarantee that output or input/output parameters\n     * are set until this fella returns SQL_NO_DATA, according to\n     * MSDN ODBC docs */\n    rc = SQLMoreResults(S->stmt);\n\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        return 0;\n    }\n\n    free_cols(stmt, S);\n    /* how many columns do we have ? */\n    SQLNumResultCols(S->stmt, &colcount);\n    stmt->column_count = S->col_count = (int) colcount;\n    S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));\n    S->going_long = 0;\n\n    return 1;\n}\n\nstatic int odbc_stmt_close_cursor(pdo_stmt_t *stmt) {\n    SQLRETURN rc;\n    pdo_odbc_stmt *S = (pdo_odbc_stmt *) stmt->driver_data;\n\n    rc = SQLCloseCursor(S->stmt);\n    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n        return 0;\n    }\n    return 1;\n}\n\nconst struct pdo_stmt_methods odbc_stmt_methods = {odbc_stmt_dtor,\n                                                   odbc_stmt_execute,\n                                                   odbc_stmt_fetch,\n                                                   odbc_stmt_describe,\n                                                   odbc_stmt_get_col,\n                                                   odbc_stmt_param_hook,\n                                                   odbc_stmt_set_param,\n                                                   odbc_stmt_get_attr,\n                                                   odbc_stmt_get_column_meta,\n                                                   odbc_stmt_next_rowset,\n                                                   odbc_stmt_close_cursor};\n#endif\n"
  },
  {
    "path": "thirdparty/php84/pdo_odbc/php_pdo_odbc_int.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#ifdef PHP_WIN32\n#define PDO_ODBC_TYPE \"Win32\"\n#endif\n\n#ifndef PDO_ODBC_TYPE\n#warning Please fix configure to give your ODBC libraries a name\n#define PDO_ODBC_TYPE \"Unknown\"\n#endif\n\n/* {{{ Roll a dice, pick a header at random... */\n#ifdef HAVE_SQLCLI1_H\n#include <sqlcli1.h>\n#if defined(DB268K) && HAVE_LIBRARYMANAGER_H\n#include <LibraryManager.h>\n#endif\n#endif\n\n#ifdef HAVE_ODBC_H\n#include <odbc.h>\n#endif\n\n#ifdef HAVE_IODBC_H\n#include <iodbc.h>\n#endif\n\n#if defined(HAVE_SQLUNIX_H) && !defined(PHP_WIN32)\n#include <sqlunix.h>\n#endif\n\n#ifdef HAVE_SQLTYPES_H\n#include <sqltypes.h>\n#endif\n\n#ifdef HAVE_SQLUCODE_H\n#include <sqlucode.h>\n#endif\n\n#ifdef HAVE_SQL_H\n#include <sql.h>\n#endif\n\n#ifdef HAVE_ISQL_H\n#include <isql.h>\n#endif\n\n#ifdef HAVE_SQLEXT_H\n#include <sqlext.h>\n#endif\n\n#ifdef HAVE_ISQLEXT_H\n#include <isqlext.h>\n#endif\n\n#ifdef HAVE_UDBCEXT_H\n#include <udbcext.h>\n#endif\n\n#ifdef HAVE_CLI0CORE_H\n#include <cli0core.h>\n#endif\n\n#ifdef HAVE_CLI0EXT1_H\n#include <cli0ext.h>\n#endif\n\n#ifdef HAVE_CLI0CLI_H\n#include <cli0cli.h>\n#endif\n\n#ifdef HAVE_CLI0DEFS_H\n#include <cli0defs.h>\n#endif\n\n#ifdef HAVE_CLI0ENV_H\n#include <cli0env.h>\n#endif\n\n/* }}} */\n\n/* {{{ Figure out the type for handles */\n#if !defined(HENV) && !defined(SQLHENV) && defined(SQLHANDLE)\n#define PDO_ODBC_HENV SQLHANDLE\n#define PDO_ODBC_HDBC SQLHANDLE\n#define PDO_ODBC_HSTMT SQLHANDLE\n#elif !defined(HENV) && (defined(SQLHENV) || defined(DB2CLI_VER))\n#define PDO_ODBC_HENV SQLHENV\n#define PDO_ODBC_HDBC SQLHDBC\n#define PDO_ODBC_HSTMT SQLHSTMT\n#else\n#define PDO_ODBC_HENV HENV\n#define PDO_ODBC_HDBC HDBC\n#define PDO_ODBC_HSTMT HSTMT\n#endif\n/* }}} */\n\ntypedef struct {\n    char last_state[6];\n    char last_err_msg[SQL_MAX_MESSAGE_LENGTH];\n    SQLINTEGER last_error;\n    const char *file, *what;\n    int line;\n} pdo_odbc_errinfo;\n\ntypedef struct {\n    PDO_ODBC_HENV env;\n    PDO_ODBC_HDBC dbc;\n    pdo_odbc_errinfo einfo;\n    unsigned assume_utf8 : 1;\n    unsigned _spare : 31;\n} pdo_odbc_db_handle;\n\ntypedef struct {\n    char *data;\n    zend_ulong datalen;\n    SQLLEN fetched_len;\n    SQLSMALLINT coltype;\n    char colname[128];\n    unsigned is_long;\n    unsigned is_unicode : 1;\n    unsigned _spare : 31;\n} pdo_odbc_column;\n\ntypedef struct {\n    PDO_ODBC_HSTMT stmt;\n    pdo_odbc_column *cols;\n    pdo_odbc_db_handle *H;\n    pdo_odbc_errinfo einfo;\n    char *convbuf;\n    zend_ulong convbufsize;\n    unsigned going_long : 1;\n    unsigned assume_utf8 : 1;\n    signed col_count : 16;\n    unsigned _spare : 14;\n} pdo_odbc_stmt;\n\ntypedef struct {\n    SQLLEN len;\n    SQLSMALLINT paramtype;\n    char *outbuf;\n    unsigned is_unicode : 1;\n    unsigned _spare : 31;\n} pdo_odbc_param;\n\nextern const pdo_driver_t pdo_odbc_driver;\nextern const struct pdo_stmt_methods odbc_stmt_methods;\n\nvoid pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line);\n#define pdo_odbc_drv_error(what) pdo_odbc_error(dbh, NULL, SQL_NULL_HSTMT, what, __FILE__, __LINE__)\n#define pdo_odbc_stmt_error(what) pdo_odbc_error(stmt->dbh, stmt, SQL_NULL_HSTMT, what, __FILE__, __LINE__)\n#define pdo_odbc_doer_error(what) pdo_odbc_error(dbh, NULL, stmt, what, __FILE__, __LINE__)\n\nvoid pdo_odbc_init_error_table(void);\nvoid pdo_odbc_fini_error_table(void);\n\n#ifdef SQL_ATTR_CONNECTION_POOLING\nextern zend_ulong pdo_odbc_pool_on;\nextern zend_ulong pdo_odbc_pool_mode;\n#endif\n\nenum {\n    PDO_ODBC_ATTR_USE_CURSOR_LIBRARY = PDO_ATTR_DRIVER_SPECIFIC,\n    PDO_ODBC_ATTR_ASSUME_UTF8 /* assume that input strings are UTF-8 when feeding data to unicode columns */\n};\n"
  },
  {
    "path": "thirdparty/php84/pdo_pgsql/pgsql_driver.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Authors: Edin Kadribasic <edink@emini.dk>                            |\n  |          Ilia Alshanestsky <ilia@prohost.org>                        |\n  |          Wez Furlong <wez@php.net>                                   |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_PGSQL_HOOK\n#include \"php_swoole_pgsql.h\"\n\n#if PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/php_string.h\" /* For php_addcslashes_str() in _pdo_pgsql_escape_credentials() */\n#include \"main/php_network.h\"\n#include \"ext/pdo/php_pdo.h\"\n#include \"ext/pdo/php_pdo_driver.h\"\n#include \"ext/pdo/php_pdo_error.h\"\n#include \"ext/standard/file.h\"\n#include \"php_pdo_pgsql_int.h\"\n#include \"zend_exceptions.h\"\n#include \"zend_smart_str.h\"\n#include \"pgsql_driver_arginfo.h\"\n\nstatic bool pgsql_handle_in_transaction(pdo_dbh_t *dbh);\n\nstatic char *_pdo_pgsql_trim_message(const char *message, int persistent) {\n    size_t i = strlen(message) - 1;\n    char *tmp;\n\n    if (i > 1 && (message[i - 1] == '\\r' || message[i - 1] == '\\n') && message[i] == '.') {\n        --i;\n    }\n    while (i > 0 && (message[i] == '\\r' || message[i] == '\\n')) {\n        --i;\n    }\n    ++i;\n    tmp = pemalloc(i + 1, persistent);\n    memcpy(tmp, message, i);\n    tmp[i] = '\\0';\n\n    return tmp;\n}\n\nstatic zend_string *_pdo_pgsql_escape_credentials(char *str) {\n    if (str) {\n        return php_addcslashes_str(str, strlen(str), \"\\\\'\", sizeof(\"\\\\'\"));\n    }\n\n    return NULL;\n}\n\nint _pdo_pgsql_error(pdo_dbh_t *dbh,\n                     pdo_stmt_t *stmt,\n                     int errcode,\n                     const char *sqlstate,\n                     const char *msg,\n                     const char *file,\n                     int line) /* {{{ */\n{\n    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data;\n    pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code;\n    pdo_pgsql_error_info *einfo = &H->einfo;\n    char *errmsg = PQerrorMessage(H->server);\n\n    einfo->errcode = errcode;\n    einfo->file = file;\n    einfo->line = line;\n\n    if (einfo->errmsg) {\n        pefree(einfo->errmsg, dbh->is_persistent);\n        einfo->errmsg = NULL;\n    }\n\n    if (sqlstate == NULL || strlen(sqlstate) >= sizeof(pdo_error_type)) {\n        strcpy(*pdo_err, \"HY000\");\n    } else {\n        strcpy(*pdo_err, sqlstate);\n    }\n\n    if (msg) {\n        einfo->errmsg = pestrdup(msg, dbh->is_persistent);\n    } else if (errmsg) {\n        einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent);\n    }\n\n    if (!dbh->methods) {\n        pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err);\n    }\n\n    return errcode;\n}\n/* }}} */\n\nstatic void _pdo_pgsql_notice(void *context, const char *message) /* {{{ */\n{\n    pdo_dbh_t *dbh = (pdo_dbh_t *) context;\n    zend_fcall_info_cache *fc = ((pdo_pgsql_db_handle *) dbh->driver_data)->notice_callback;\n    if (fc) {\n        zval zarg;\n        ZVAL_STRING(&zarg, message);\n        zend_call_known_fcc(fc, NULL, 1, &zarg, NULL);\n        zval_ptr_dtor_str(&zarg);\n    }\n}\n/* }}} */\n\nstatic void pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */\n{\n    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data;\n    pdo_pgsql_error_info *einfo = &H->einfo;\n\n    if (einfo->errcode) {\n        add_next_index_long(info, einfo->errcode);\n    } else {\n        /* Add null to respect expected info array structure */\n        add_next_index_null(info);\n    }\n    if (einfo->errmsg) {\n        add_next_index_string(info, einfo->errmsg);\n    }\n}\n/* }}} */\n\nvoid pdo_pgsql_cleanup_notice_callback(pdo_pgsql_db_handle *H) /* {{{ */\n{\n    if (H->notice_callback) {\n        zend_fcc_dtor(H->notice_callback);\n        efree(H->notice_callback);\n        H->notice_callback = NULL;\n    }\n}\n/* }}} */\n\n/* {{{ pdo_pgsql_create_lob_stream */\nstatic ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count) {\n    struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self *) stream->abstract;\n    return lo_write(self->conn, self->lfd, (char *) buf, count);\n}\n\nstatic ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count) {\n    struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self *) stream->abstract;\n    return lo_read(self->conn, self->lfd, buf, count);\n}\n\nstatic int pgsql_lob_close(php_stream *stream, int close_handle) {\n    struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self *) stream->abstract;\n    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) (Z_PDO_DBH_P(&self->dbh))->driver_data;\n\n    if (close_handle) {\n        lo_close(self->conn, self->lfd);\n    }\n    zend_hash_index_del(H->lob_streams, php_stream_get_resource_id(stream));\n    zval_ptr_dtor(&self->dbh);\n    efree(self);\n    return 0;\n}\n\nstatic int pgsql_lob_flush(php_stream *stream) {\n    return 0;\n}\n\nstatic int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) {\n    struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self *) stream->abstract;\n#ifdef ZEND_ENABLE_ZVAL_LONG64\n    zend_off_t pos = lo_lseek64(self->conn, self->lfd, offset, whence);\n#else\n    zend_off_t pos = lo_lseek(self->conn, self->lfd, offset, whence);\n#endif\n    *newoffset = pos;\n    return pos >= 0 ? 0 : -1;\n}\n\nconst php_stream_ops pdo_pgsql_lob_stream_ops = {pgsql_lob_write,\n                                                 pgsql_lob_read,\n                                                 pgsql_lob_close,\n                                                 pgsql_lob_flush,\n                                                 \"pdo_pgsql lob stream\",\n                                                 pgsql_lob_seek,\n                                                 NULL,\n                                                 NULL,\n                                                 NULL};\n\nphp_stream *pdo_pgsql_create_lob_stream(zval *dbh, int lfd, Oid oid) {\n    php_stream *stm;\n    struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self));\n    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) (Z_PDO_DBH_P(dbh))->driver_data;\n\n    ZVAL_COPY_VALUE(&self->dbh, dbh);\n    self->lfd = lfd;\n    self->oid = oid;\n    self->conn = H->server;\n\n    stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, \"r+b\");\n\n    if (stm) {\n        Z_ADDREF_P(dbh);\n        zend_hash_index_add_ptr(H->lob_streams, php_stream_get_resource_id(stm), stm->res);\n        return stm;\n    }\n\n    efree(self);\n    return NULL;\n}\n/* }}} */\n\nvoid pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh) {\n    zend_resource *res;\n    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data;\n    if (H->lob_streams) {\n        ZEND_HASH_REVERSE_FOREACH_PTR(H->lob_streams, res) {\n            if (res->type >= 0) {\n                zend_list_close(res);\n            }\n        }\n        ZEND_HASH_FOREACH_END();\n    }\n}\n\nstatic void pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */\n{\n    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data;\n    if (H) {\n        if (H->lob_streams) {\n            pdo_pgsql_close_lob_streams(dbh);\n            zend_hash_destroy(H->lob_streams);\n            pefree(H->lob_streams, dbh->is_persistent);\n            H->lob_streams = NULL;\n        }\n        pdo_pgsql_cleanup_notice_callback(H);\n        if (H->server) {\n            PQfinish(H->server);\n            H->server = NULL;\n        }\n        if (H->einfo.errmsg) {\n            pefree(H->einfo.errmsg, dbh->is_persistent);\n            H->einfo.errmsg = NULL;\n        }\n        pefree(H, dbh->is_persistent);\n        dbh->driver_data = NULL;\n    }\n}\n/* }}} */\n\nstatic bool pgsql_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) {\n    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data;\n    pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt));\n    int scrollable;\n    int ret;\n    zend_string *nsql = NULL;\n    int emulate = 0;\n    int execute_only = 0;\n\n    S->H = H;\n    stmt->driver_data = S;\n    stmt->methods = &swoole_pgsql_stmt_methods;\n\n    scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL;\n\n    if (scrollable) {\n        if (S->cursor_name) {\n            efree(S->cursor_name);\n        }\n        spprintf(&S->cursor_name, 0, \"pdo_crsr_%08x\", ++H->stmt_counter);\n        emulate = 1;\n    } else if (driver_options) {\n        if (pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares) == 1) {\n            emulate = 1;\n        }\n        if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_PREPARES, H->disable_prepares) == 1) {\n            execute_only = 1;\n        }\n    } else {\n        emulate = H->disable_native_prepares || H->emulate_prepares;\n        execute_only = H->disable_prepares;\n    }\n\n    if (emulate) {\n        stmt->supports_placeholders = PDO_PLACEHOLDER_NONE;\n    } else {\n        stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED;\n        stmt->named_rewrite_template = \"$%d\";\n    }\n\n    ret = pdo_parse_params(stmt, sql, &nsql);\n\n    if (ret == -1) {\n        /* couldn't grok it */\n        strcpy(dbh->error_code, stmt->error_code);\n        return false;\n    } else if (ret == 1) {\n        /* query was re-written */\n        S->query = nsql;\n    } else {\n        S->query = zend_string_copy(sql);\n    }\n\n    if (!emulate && !execute_only) {\n        /* prepared query: set the query name and defer the\n           actual prepare until the first execute call */\n        spprintf(&S->stmt_name, 0, \"pdo_stmt_%08x\", ++H->stmt_counter);\n    }\n\n    return true;\n}\n\nstatic zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) {\n    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data;\n    PGresult *res;\n    zend_long ret = 1;\n    ExecStatusType qs;\n\n    bool in_trans = pgsql_handle_in_transaction(dbh);\n\n    if (!(res = PQexec(H->server, ZSTR_VAL(sql)))) {\n        /* fatal error */\n        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n        return -1;\n    }\n    qs = PQresultStatus(res);\n    if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) {\n        pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res));\n        PQclear(res);\n        return -1;\n    }\n    H->pgoid = PQoidValue(res);\n    if (qs == PGRES_COMMAND_OK) {\n        ret = ZEND_ATOL(PQcmdTuples(res));\n    } else {\n        ret = Z_L(0);\n    }\n    PQclear(res);\n    if (in_trans && !pgsql_handle_in_transaction(dbh)) {\n        pdo_pgsql_close_lob_streams(dbh);\n    }\n\n    return ret;\n}\n\nstatic zend_string *pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype) {\n    unsigned char *escaped;\n    char *quoted;\n    size_t quotedlen;\n    zend_string *quoted_str;\n    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data;\n    size_t tmp_len;\n\n    switch (paramtype) {\n    case PDO_PARAM_LOB:\n        /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */\n        escaped = PQescapeByteaConn(H->server, (unsigned char *) ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &tmp_len);\n        quotedlen = tmp_len + 1;\n        quoted = emalloc(quotedlen + 1);\n        memcpy(quoted + 1, escaped, quotedlen - 2);\n        quoted[0] = '\\'';\n        quoted[quotedlen - 1] = '\\'';\n        quoted[quotedlen] = '\\0';\n        PQfreemem(escaped);\n        break;\n    default:\n        quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3);\n        quoted[0] = '\\'';\n        quotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), NULL);\n        quoted[quotedlen + 1] = '\\'';\n        quoted[quotedlen + 2] = '\\0';\n        quotedlen += 2;\n    }\n\n    quoted_str = zend_string_init(quoted, quotedlen, 0);\n    efree(quoted);\n    return quoted_str;\n}\n\nstatic zend_string *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const zend_string *name) {\n    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data;\n    zend_string *id = NULL;\n    PGresult *res;\n    ExecStatusType status;\n\n    if (name == NULL) {\n        res = PQexec(H->server, \"SELECT LASTVAL()\");\n    } else {\n        const char *q[1];\n        q[0] = ZSTR_VAL(name);\n\n        res = PQexecParams(H->server, \"SELECT CURRVAL($1)\", 1, NULL, q, NULL, NULL, 0);\n    }\n    status = PQresultStatus(res);\n\n    if (res && (status == PGRES_TUPLES_OK)) {\n        id = zend_string_init((char *) PQgetvalue(res, 0, 0), PQgetlength(res, 0, 0), 0);\n    } else {\n        pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res));\n    }\n\n    if (res) {\n        PQclear(res);\n    }\n\n    return id;\n}\n\nvoid pdo_libpq_version(char *buf, size_t len) {\n    int version = PQlibVersion();\n    int major = version / 10000;\n    if (major >= 10) {\n        int minor = version % 10000;\n        snprintf(buf, len, \"%d.%d\", major, minor);\n    } else {\n        int minor = version / 100 % 100;\n        int revision = version % 100;\n        snprintf(buf, len, \"%d.%d.%d\", major, minor, revision);\n    }\n}\n\nstatic int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) {\n    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data;\n\n    switch (attr) {\n    case PDO_ATTR_EMULATE_PREPARES:\n        ZVAL_BOOL(return_value, H->emulate_prepares);\n        break;\n\n    case PDO_PGSQL_ATTR_DISABLE_PREPARES:\n        ZVAL_BOOL(return_value, H->disable_prepares);\n        break;\n\n    case PDO_ATTR_CLIENT_VERSION: {\n        char buf[16];\n        pdo_libpq_version(buf, sizeof(buf));\n        ZVAL_STRING(return_value, buf);\n        break;\n    }\n\n    case PDO_ATTR_SERVER_VERSION:\n        ZVAL_STRING(return_value, (char *) PQparameterStatus(H->server, \"server_version\"));\n        break;\n\n    case PDO_ATTR_CONNECTION_STATUS:\n        switch (PQstatus(H->server)) {\n        case CONNECTION_STARTED:\n            ZVAL_STRINGL(\n                return_value, \"Waiting for connection to be made.\", strlen(\"Waiting for connection to be made.\"));\n            break;\n\n        case CONNECTION_MADE:\n        case CONNECTION_OK:\n            ZVAL_STRINGL(return_value, \"Connection OK; waiting to send.\", strlen(\"Connection OK; waiting to send.\"));\n            break;\n\n        case CONNECTION_AWAITING_RESPONSE:\n            ZVAL_STRINGL(return_value,\n                         \"Waiting for a response from the server.\",\n                         strlen(\"Waiting for a response from the server.\"));\n            break;\n\n        case CONNECTION_AUTH_OK:\n            ZVAL_STRINGL(return_value,\n                         \"Received authentication; waiting for backend start-up to finish.\",\n                         strlen(\"Received authentication; waiting for backend start-up to finish.\"));\n            break;\n#ifdef CONNECTION_SSL_STARTUP\n        case CONNECTION_SSL_STARTUP:\n            ZVAL_STRINGL(return_value, \"Negotiating SSL encryption.\", strlen(\"Negotiating SSL encryption.\"));\n            break;\n#endif\n        case CONNECTION_SETENV:\n            ZVAL_STRINGL(return_value,\n                         \"Negotiating environment-driven parameter settings.\",\n                         strlen(\"Negotiating environment-driven parameter settings.\"));\n            break;\n\n#ifdef CONNECTION_CONSUME\n        case CONNECTION_CONSUME:\n            ZVAL_STRINGL(return_value,\n                         \"Flushing send queue/consuming extra data.\",\n                         strlen(\"Flushing send queue/consuming extra data.\"));\n            break;\n#endif\n#ifdef CONNECTION_GSS_STARTUP\n        case CONNECTION_SSL_STARTUP:\n            ZVAL_STRINGL(return_value, \"Negotiating GSSAPI.\", strlen(\"Negotiating GSSAPI.\"));\n            break;\n#endif\n#ifdef CONNECTION_CHECK_TARGET\n        case CONNECTION_CHECK_TARGET:\n            ZVAL_STRINGL(return_value,\n                         \"Connection OK; checking target server properties.\",\n                         strlen(\"Connection OK; checking target server properties.\"));\n            break;\n#endif\n#ifdef CONNECTION_CHECK_STANDBY\n        case CONNECTION_CHECK_STANDBY:\n            ZVAL_STRINGL(return_value,\n                         \"Connection OK; checking if server in standby.\",\n                         strlen(\"Connection OK; checking if server in standby.\"));\n            break;\n#endif\n        case CONNECTION_BAD:\n        default:\n            ZVAL_STRINGL(return_value, \"Bad connection.\", strlen(\"Bad connection.\"));\n            break;\n        }\n        break;\n\n    case PDO_ATTR_SERVER_INFO: {\n        int spid = PQbackendPID(H->server);\n\n        zend_string *str_info =\n            strpprintf(0,\n                       \"PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s\",\n                       spid,\n                       (char *) PQparameterStatus(H->server, \"client_encoding\"),\n                       (char *) PQparameterStatus(H->server, \"is_superuser\"),\n                       (char *) PQparameterStatus(H->server, \"session_authorization\"),\n                       (char *) PQparameterStatus(H->server, \"DateStyle\"));\n\n        ZVAL_STR(return_value, str_info);\n        break;\n    }\n\n    default:\n        return 0;\n    }\n\n    return 1;\n}\n\n/* {{{ */\nstatic zend_result pdo_pgsql_check_liveness(pdo_dbh_t *dbh) {\n    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data;\n    if (!PQconsumeInput(H->server) || PQstatus(H->server) == CONNECTION_BAD) {\n        PQreset(H->server);\n    }\n    return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE;\n}\n/* }}} */\n\nstatic bool pgsql_handle_in_transaction(pdo_dbh_t *dbh) {\n    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data;\n\n    return PQtransactionStatus(H->server) > PQTRANS_IDLE;\n}\n\nstatic bool pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh) {\n    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data;\n    PGresult *res;\n    bool ret = true;\n\n    res = PQexec(H->server, cmd);\n\n    if (PQresultStatus(res) != PGRES_COMMAND_OK) {\n        pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res));\n        ret = false;\n    }\n\n    PQclear(res);\n    return ret;\n}\n\nstatic bool pgsql_handle_begin(pdo_dbh_t *dbh) {\n    return pdo_pgsql_transaction_cmd(\"BEGIN\", dbh);\n}\n\nstatic bool pgsql_handle_commit(pdo_dbh_t *dbh) {\n    bool ret = pdo_pgsql_transaction_cmd(\"COMMIT\", dbh);\n\n    /* When deferred constraints are used the commit could\n       fail, and a ROLLBACK implicitly ran. See bug #67462 */\n    if (ret) {\n        pdo_pgsql_close_lob_streams(dbh);\n    } else {\n        dbh->in_txn = pgsql_handle_in_transaction(dbh);\n    }\n\n    return ret;\n}\n\nstatic bool pgsql_handle_rollback(pdo_dbh_t *dbh) {\n    int ret = pdo_pgsql_transaction_cmd(\"ROLLBACK\", dbh);\n\n    if (ret) {\n        pdo_pgsql_close_lob_streams(dbh);\n    }\n\n    return ret;\n}\n\nvoid pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAMETERS) {\n    pdo_dbh_t *dbh;\n    pdo_pgsql_db_handle *H;\n\n    zval *pg_rows;\n\n    char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;\n    size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;\n    char *query;\n\n    PGresult *pgsql_result;\n    ExecStatusType status;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(),\n                              \"sa|sss!\",\n                              &table_name,\n                              &table_name_len,\n                              &pg_rows,\n                              &pg_delim,\n                              &pg_delim_len,\n                              &pg_null_as,\n                              &pg_null_as_len,\n                              &pg_fields,\n                              &pg_fields_len) == FAILURE) {\n        RETURN_THROWS();\n    }\n\n    if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) {\n        zend_argument_value_error(2, \"cannot be empty\");\n        RETURN_THROWS();\n    }\n\n    dbh = Z_PDO_DBH_P(ZEND_THIS);\n    PDO_CONSTRUCT_CHECK;\n    PDO_DBH_CLEAR_ERR();\n\n    /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */\n    if (pg_fields) {\n        spprintf(&query,\n                 0,\n                 \"COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'\",\n                 table_name,\n                 pg_fields,\n                 (pg_delim_len ? *pg_delim : '\\t'),\n                 (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n    } else {\n        spprintf(&query,\n                 0,\n                 \"COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'\",\n                 table_name,\n                 (pg_delim_len ? *pg_delim : '\\t'),\n                 (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n    }\n\n    /* Obtain db Handle */\n    H = (pdo_pgsql_db_handle *) dbh->driver_data;\n\n    while ((pgsql_result = PQgetResult(H->server))) {\n        PQclear(pgsql_result);\n    }\n    pgsql_result = PQexec(H->server, query);\n\n    efree(query);\n    query = NULL;\n\n    if (pgsql_result) {\n        status = PQresultStatus(pgsql_result);\n    } else {\n        status = (ExecStatusType) PQstatus(H->server);\n    }\n\n    if (status == PGRES_COPY_IN && pgsql_result) {\n        int command_failed = 0;\n        size_t buffer_len = 0;\n        zval *tmp;\n\n        PQclear(pgsql_result);\n        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) {\n            size_t query_len;\n            if (!try_convert_to_string(tmp)) {\n                efree(query);\n                RETURN_THROWS();\n            }\n\n            if (buffer_len < Z_STRLEN_P(tmp)) {\n                buffer_len = Z_STRLEN_P(tmp);\n                query = erealloc(query, buffer_len + 2); /* room for \\n\\0 */\n            }\n            query_len = Z_STRLEN_P(tmp);\n            memcpy(query, Z_STRVAL_P(tmp), query_len);\n            if (query[query_len - 1] != '\\n') {\n                query[query_len++] = '\\n';\n            }\n            query[query_len] = '\\0';\n            if (PQputCopyData(H->server, query, query_len) != 1) {\n                efree(query);\n                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n                PDO_HANDLE_DBH_ERR();\n                RETURN_FALSE;\n            }\n        }\n        ZEND_HASH_FOREACH_END();\n        if (query) {\n            efree(query);\n        }\n\n        if (PQputCopyEnd(H->server, NULL) != 1) {\n            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n            PDO_HANDLE_DBH_ERR();\n            RETURN_FALSE;\n        }\n\n        while ((pgsql_result = PQgetResult(H->server))) {\n            if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {\n                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n                command_failed = 1;\n            }\n            PQclear(pgsql_result);\n        }\n\n        PDO_HANDLE_DBH_ERR();\n        RETURN_BOOL(!command_failed);\n    } else {\n        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n        PQclear(pgsql_result);\n        PDO_HANDLE_DBH_ERR();\n        RETURN_FALSE;\n    }\n}\n\n/* {{{ Returns true if the copy worked fine or false if error */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray) {\n    pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n/* }}} */\n\nvoid pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAMETERS) {\n    pdo_dbh_t *dbh;\n    pdo_pgsql_db_handle *H;\n\n    char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;\n    size_t table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;\n    char *query;\n    PGresult *pgsql_result;\n    ExecStatusType status;\n    php_stream *stream;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(),\n                              \"sp|sss!\",\n                              &table_name,\n                              &table_name_len,\n                              &filename,\n                              &filename_len,\n                              &pg_delim,\n                              &pg_delim_len,\n                              &pg_null_as,\n                              &pg_null_as_len,\n                              &pg_fields,\n                              &pg_fields_len) == FAILURE) {\n        RETURN_THROWS();\n    }\n\n    /* Obtain db Handler */\n    dbh = Z_PDO_DBH_P(ZEND_THIS);\n    PDO_CONSTRUCT_CHECK;\n    PDO_DBH_CLEAR_ERR();\n\n    stream = php_stream_open_wrapper_ex(filename, \"rb\", 0, NULL, FG(default_context));\n    if (!stream) {\n        pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, \"Unable to open the file\");\n        PDO_HANDLE_DBH_ERR();\n        RETURN_FALSE;\n    }\n\n    /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */\n    if (pg_fields) {\n        spprintf(&query,\n                 0,\n                 \"COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'\",\n                 table_name,\n                 pg_fields,\n                 (pg_delim_len ? *pg_delim : '\\t'),\n                 (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n    } else {\n        spprintf(&query,\n                 0,\n                 \"COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'\",\n                 table_name,\n                 (pg_delim_len ? *pg_delim : '\\t'),\n                 (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n    }\n\n    H = (pdo_pgsql_db_handle *) dbh->driver_data;\n\n    while ((pgsql_result = PQgetResult(H->server))) {\n        PQclear(pgsql_result);\n    }\n    pgsql_result = PQexec(H->server, query);\n\n    efree(query);\n\n    if (pgsql_result) {\n        status = PQresultStatus(pgsql_result);\n    } else {\n        status = (ExecStatusType) PQstatus(H->server);\n    }\n\n    if (status == PGRES_COPY_IN && pgsql_result) {\n        char *buf;\n        int command_failed = 0;\n        size_t line_len = 0;\n\n        PQclear(pgsql_result);\n        while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) {\n            if (PQputCopyData(H->server, buf, line_len) != 1) {\n                efree(buf);\n                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n                php_stream_close(stream);\n                PDO_HANDLE_DBH_ERR();\n                RETURN_FALSE;\n            }\n            efree(buf);\n        }\n        php_stream_close(stream);\n\n        if (PQputCopyEnd(H->server, NULL) != 1) {\n            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n            PDO_HANDLE_DBH_ERR();\n            RETURN_FALSE;\n        }\n\n        while ((pgsql_result = PQgetResult(H->server))) {\n            if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {\n                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n                command_failed = 1;\n            }\n            PQclear(pgsql_result);\n        }\n\n        PDO_HANDLE_DBH_ERR();\n        RETURN_BOOL(!command_failed);\n    } else {\n        php_stream_close(stream);\n        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n        PQclear(pgsql_result);\n        PDO_HANDLE_DBH_ERR();\n        RETURN_FALSE;\n    }\n}\n\n/* {{{ Returns true if the copy worked fine or false if error */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile) {\n    pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n/* }}} */\n\nvoid pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAMETERS) {\n    pdo_dbh_t *dbh;\n    pdo_pgsql_db_handle *H;\n\n    char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL;\n    size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len;\n    char *query;\n\n    PGresult *pgsql_result;\n    ExecStatusType status;\n\n    php_stream *stream;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(),\n                              \"sp|sss!\",\n                              &table_name,\n                              &table_name_len,\n                              &filename,\n                              &filename_len,\n                              &pg_delim,\n                              &pg_delim_len,\n                              &pg_null_as,\n                              &pg_null_as_len,\n                              &pg_fields,\n                              &pg_fields_len) == FAILURE) {\n        RETURN_THROWS();\n    }\n\n    dbh = Z_PDO_DBH_P(ZEND_THIS);\n    PDO_CONSTRUCT_CHECK;\n    PDO_DBH_CLEAR_ERR();\n\n    H = (pdo_pgsql_db_handle *) dbh->driver_data;\n\n    stream = php_stream_open_wrapper_ex(filename, \"wb\", 0, NULL, FG(default_context));\n    if (!stream) {\n        pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, \"Unable to open the file for writing\");\n        PDO_HANDLE_DBH_ERR();\n        RETURN_FALSE;\n    }\n\n    while ((pgsql_result = PQgetResult(H->server))) {\n        PQclear(pgsql_result);\n    }\n\n    /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */\n    if (pg_fields) {\n        spprintf(&query,\n                 0,\n                 \"COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'\",\n                 table_name,\n                 pg_fields,\n                 (pg_delim_len ? *pg_delim : '\\t'),\n                 (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n    } else {\n        spprintf(&query,\n                 0,\n                 \"COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'\",\n                 table_name,\n                 (pg_delim_len ? *pg_delim : '\\t'),\n                 (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n    }\n    pgsql_result = PQexec(H->server, query);\n    efree(query);\n\n    if (pgsql_result) {\n        status = PQresultStatus(pgsql_result);\n    } else {\n        status = (ExecStatusType) PQstatus(H->server);\n    }\n\n    if (status == PGRES_COPY_OUT && pgsql_result) {\n        PQclear(pgsql_result);\n        while (1) {\n            char *csv = NULL;\n            int ret = PQgetCopyData(H->server, &csv, 0);\n\n            if (ret == -1) {\n                break; /* done */\n            } else if (ret > 0) {\n                if (php_stream_write(stream, csv, ret) != (size_t) ret) {\n                    pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, \"Unable to write to file\");\n                    PQfreemem(csv);\n                    php_stream_close(stream);\n                    PDO_HANDLE_DBH_ERR();\n                    RETURN_FALSE;\n                } else {\n                    PQfreemem(csv);\n                }\n            } else {\n                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n                php_stream_close(stream);\n                PDO_HANDLE_DBH_ERR();\n                RETURN_FALSE;\n            }\n        }\n        php_stream_close(stream);\n\n        while ((pgsql_result = PQgetResult(H->server))) {\n            PQclear(pgsql_result);\n        }\n        RETURN_TRUE;\n    } else {\n        php_stream_close(stream);\n        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n        PQclear(pgsql_result);\n        PDO_HANDLE_DBH_ERR();\n        RETURN_FALSE;\n    }\n}\n\n/* {{{ Returns true if the copy worked fine or false if error */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile) {\n    pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n/* }}} */\n\nvoid pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAMETERS) {\n    pdo_dbh_t *dbh;\n    pdo_pgsql_db_handle *H;\n\n    char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;\n    size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;\n    char *query;\n\n    PGresult *pgsql_result;\n    ExecStatusType status;\n\n    if (zend_parse_parameters(ZEND_NUM_ARGS(),\n                              \"s|sss!\",\n                              &table_name,\n                              &table_name_len,\n                              &pg_delim,\n                              &pg_delim_len,\n                              &pg_null_as,\n                              &pg_null_as_len,\n                              &pg_fields,\n                              &pg_fields_len) == FAILURE) {\n        RETURN_THROWS();\n    }\n\n    dbh = Z_PDO_DBH_P(ZEND_THIS);\n    PDO_CONSTRUCT_CHECK;\n    PDO_DBH_CLEAR_ERR();\n\n    H = (pdo_pgsql_db_handle *) dbh->driver_data;\n\n    while ((pgsql_result = PQgetResult(H->server))) {\n        PQclear(pgsql_result);\n    }\n\n    /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */\n    if (pg_fields) {\n        spprintf(&query,\n                 0,\n                 \"COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'\",\n                 table_name,\n                 pg_fields,\n                 (pg_delim_len ? *pg_delim : '\\t'),\n                 (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n    } else {\n        spprintf(&query,\n                 0,\n                 \"COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'\",\n                 table_name,\n                 (pg_delim_len ? *pg_delim : '\\t'),\n                 (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n    }\n    pgsql_result = PQexec(H->server, query);\n    efree(query);\n\n    if (pgsql_result) {\n        status = PQresultStatus(pgsql_result);\n    } else {\n        status = (ExecStatusType) PQstatus(H->server);\n    }\n\n    if (status == PGRES_COPY_OUT && pgsql_result) {\n        PQclear(pgsql_result);\n        array_init(return_value);\n\n        while (1) {\n            char *csv = NULL;\n            int ret = PQgetCopyData(H->server, &csv, 0);\n            if (ret == -1) {\n                break; /* copy done */\n            } else if (ret > 0) {\n                add_next_index_stringl(return_value, csv, ret);\n                PQfreemem(csv);\n            } else {\n                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n                PDO_HANDLE_DBH_ERR();\n                RETURN_FALSE;\n            }\n        }\n\n        while ((pgsql_result = PQgetResult(H->server))) {\n            PQclear(pgsql_result);\n        }\n    } else {\n        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n        PQclear(pgsql_result);\n        PDO_HANDLE_DBH_ERR();\n        RETURN_FALSE;\n    }\n}\n\n/* {{{ Returns true if the copy worked fine or false if error */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray) {\n    pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n/* }}} */\n\nvoid pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAMETERS) {\n    pdo_dbh_t *dbh;\n    pdo_pgsql_db_handle *H;\n    Oid lfd;\n\n    ZEND_PARSE_PARAMETERS_NONE();\n\n    dbh = Z_PDO_DBH_P(ZEND_THIS);\n    PDO_CONSTRUCT_CHECK;\n    PDO_DBH_CLEAR_ERR();\n\n    H = (pdo_pgsql_db_handle *) dbh->driver_data;\n    lfd = lo_creat(H->server, INV_READ | INV_WRITE);\n\n    if (lfd != InvalidOid) {\n        zend_string *buf = strpprintf(0, ZEND_ULONG_FMT, (zend_long) lfd);\n\n        RETURN_STR(buf);\n    }\n\n    pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n    PDO_HANDLE_DBH_ERR();\n    RETURN_FALSE;\n}\n\n/* {{{ Creates a new large object, returning its identifier.  Must be called inside a transaction. */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate) {\n    pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n/* }}} */\n\nvoid pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAMETERS) {\n    pdo_dbh_t *dbh;\n    pdo_pgsql_db_handle *H;\n    Oid oid;\n    int lfd;\n    char *oidstr;\n    size_t oidstrlen;\n    char *modestr = \"rb\";\n    size_t modestrlen;\n    int mode = INV_READ;\n    char *end_ptr;\n\n    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), \"s|s\", &oidstr, &oidstrlen, &modestr, &modestrlen)) {\n        RETURN_THROWS();\n    }\n\n    oid = (Oid) strtoul(oidstr, &end_ptr, 10);\n    if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {\n        RETURN_FALSE;\n    }\n\n    if (strpbrk(modestr, \"+w\")) {\n        mode = INV_READ | INV_WRITE;\n    }\n\n    dbh = Z_PDO_DBH_P(ZEND_THIS);\n    PDO_CONSTRUCT_CHECK;\n    PDO_DBH_CLEAR_ERR();\n\n    H = (pdo_pgsql_db_handle *) dbh->driver_data;\n\n    lfd = lo_open(H->server, oid, mode);\n\n    if (lfd >= 0) {\n        php_stream *stream = pdo_pgsql_create_lob_stream(ZEND_THIS, lfd, oid);\n        if (stream) {\n            php_stream_to_zval(stream, return_value);\n            return;\n        }\n    } else {\n        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n    }\n\n    PDO_HANDLE_DBH_ERR();\n    RETURN_FALSE;\n}\n\n/* {{{ Opens an existing large object stream.  Must be called inside a transaction. */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen) {\n    pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n/* }}} */\n\nvoid pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAMETERS) {\n    pdo_dbh_t *dbh;\n    pdo_pgsql_db_handle *H;\n    Oid oid;\n    char *oidstr, *end_ptr;\n    size_t oidlen;\n\n    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), \"s\", &oidstr, &oidlen)) {\n        RETURN_THROWS();\n    }\n\n    oid = (Oid) strtoul(oidstr, &end_ptr, 10);\n    if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {\n        RETURN_FALSE;\n    }\n\n    dbh = Z_PDO_DBH_P(ZEND_THIS);\n    PDO_CONSTRUCT_CHECK;\n    PDO_DBH_CLEAR_ERR();\n\n    H = (pdo_pgsql_db_handle *) dbh->driver_data;\n\n    if (1 == lo_unlink(H->server, oid)) {\n        RETURN_TRUE;\n    }\n\n    pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n    PDO_HANDLE_DBH_ERR();\n    RETURN_FALSE;\n}\n\n/* {{{ Deletes the large object identified by oid.  Must be called inside a transaction. */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink) {\n    pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n/* }}} */\n\nvoid pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAMETERS) {\n    pdo_dbh_t *dbh;\n    pdo_pgsql_db_handle *H;\n    zend_long result_type = PDO_FETCH_USE_DEFAULT;\n    zend_long ms_timeout = 0;\n    PGnotify *pgsql_notify;\n\n    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), \"|ll\", &result_type, &ms_timeout)) {\n        RETURN_THROWS();\n    }\n\n    dbh = Z_PDO_DBH_P(ZEND_THIS);\n    PDO_CONSTRUCT_CHECK;\n\n    if (result_type == PDO_FETCH_USE_DEFAULT) {\n        result_type = dbh->default_fetch_type;\n    }\n\n    if (result_type != PDO_FETCH_BOTH && result_type != PDO_FETCH_ASSOC && result_type != PDO_FETCH_NUM) {\n        zend_argument_value_error(1, \"must be one of PDO::FETCH_BOTH, PDO::FETCH_ASSOC, or PDO::FETCH_NUM\");\n        RETURN_THROWS();\n    }\n\n    if (ms_timeout < 0) {\n        zend_argument_value_error(2, \"must be greater than or equal to 0\");\n        RETURN_THROWS();\n#ifdef ZEND_ENABLE_ZVAL_LONG64\n    } else if (ms_timeout > INT_MAX) {\n        php_error_docref(NULL, E_WARNING, \"Timeout was shrunk to %d\", INT_MAX);\n        ms_timeout = INT_MAX;\n#endif\n    }\n\n    H = (pdo_pgsql_db_handle *) dbh->driver_data;\n\n    if (!PQconsumeInput(H->server)) {\n        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n        PDO_HANDLE_DBH_ERR();\n        RETURN_FALSE;\n    }\n    pgsql_notify = PQnotifies(H->server);\n\n    if (ms_timeout && !pgsql_notify) {\n        php_pollfd_for_ms(PQsocket(H->server), PHP_POLLREADABLE, (int) ms_timeout);\n\n        if (!PQconsumeInput(H->server)) {\n            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n            PDO_HANDLE_DBH_ERR();\n            RETURN_FALSE;\n        }\n        pgsql_notify = PQnotifies(H->server);\n    }\n\n    if (!pgsql_notify) {\n        RETURN_FALSE;\n    }\n\n    array_init(return_value);\n    if (result_type == PDO_FETCH_NUM || result_type == PDO_FETCH_BOTH) {\n        add_index_string(return_value, 0, pgsql_notify->relname);\n        add_index_long(return_value, 1, pgsql_notify->be_pid);\n        if (pgsql_notify->extra && pgsql_notify->extra[0]) {\n            add_index_string(return_value, 2, pgsql_notify->extra);\n        }\n    }\n    if (result_type == PDO_FETCH_ASSOC || result_type == PDO_FETCH_BOTH) {\n        add_assoc_string(return_value, \"message\", pgsql_notify->relname);\n        add_assoc_long(return_value, \"pid\", pgsql_notify->be_pid);\n        if (pgsql_notify->extra && pgsql_notify->extra[0]) {\n            add_assoc_string(return_value, \"payload\", pgsql_notify->extra);\n        }\n    }\n\n    PQfreemem(pgsql_notify);\n}\n\n/* {{{ Get asynchronous notification */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify) {\n    pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n/* }}} */\n\nvoid pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAMETERS) {\n    pdo_dbh_t *dbh;\n    pdo_pgsql_db_handle *H;\n\n    ZEND_PARSE_PARAMETERS_NONE();\n\n    dbh = Z_PDO_DBH_P(ZEND_THIS);\n    PDO_CONSTRUCT_CHECK;\n\n    H = (pdo_pgsql_db_handle *) dbh->driver_data;\n\n    RETURN_LONG(PQbackendPID(H->server));\n}\n\n/* {{{ Get backend(server) pid */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid) {\n    pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n/* }}} */\n\n/* {{{ Sets a callback to receive DB notices (after client_min_messages has been set) */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlSetNoticeCallback) {\n    zend_fcall_info fci = empty_fcall_info;\n    zend_fcall_info_cache fcc = empty_fcall_info_cache;\n    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), \"F!\", &fci, &fcc)) {\n        RETURN_THROWS();\n    }\n\n    pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);\n    PDO_CONSTRUCT_CHECK_WITH_CLEANUP(cleanup);\n\n    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data;\n\n    pdo_pgsql_cleanup_notice_callback(H);\n\n    if (ZEND_FCC_INITIALIZED(fcc)) {\n        H->notice_callback = emalloc(sizeof(zend_fcall_info_cache));\n        zend_fcc_dup(H->notice_callback, &fcc);\n    }\n\n    return;\n\ncleanup:\n    if (ZEND_FCC_INITIALIZED(fcc)) {\n        zend_fcc_dtor(&fcc);\n    }\n    RETURN_THROWS();\n}\n/* }}} */\n\nstatic const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind) {\n    switch (kind) {\n    case PDO_DBH_DRIVER_METHOD_KIND_DBH:\n        return class_PDO_PGSql_Ext_methods;\n    default:\n        return NULL;\n    }\n}\n\nstatic bool pdo_pgsql_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) {\n    bool bval;\n    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *) dbh->driver_data;\n\n    switch (attr) {\n    case PDO_ATTR_EMULATE_PREPARES:\n        if (!pdo_get_bool_param(&bval, val)) {\n            return false;\n        }\n        H->emulate_prepares = bval;\n        return true;\n    case PDO_PGSQL_ATTR_DISABLE_PREPARES:\n        if (!pdo_get_bool_param(&bval, val)) {\n            return false;\n        }\n        H->disable_prepares = bval;\n        return true;\n    default:\n        return false;\n    }\n}\n\nstatic const struct pdo_dbh_methods pgsql_methods = {pgsql_handle_closer,\n                                                     pgsql_handle_preparer,\n                                                     pgsql_handle_doer,\n                                                     pgsql_handle_quoter,\n                                                     pgsql_handle_begin,\n                                                     pgsql_handle_commit,\n                                                     pgsql_handle_rollback,\n                                                     pdo_pgsql_set_attr,\n                                                     pdo_pgsql_last_insert_id,\n                                                     pdo_pgsql_fetch_error_func,\n                                                     pdo_pgsql_get_attribute,\n                                                     pdo_pgsql_check_liveness,     /* check_liveness */\n                                                     pdo_pgsql_get_driver_methods, /* get_driver_methods */\n                                                     NULL,\n                                                     pgsql_handle_in_transaction,\n                                                     NULL, /* get_gc */\n                                                     pdo_pgsql_scanner};\n\nstatic int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */\n{\n    pdo_pgsql_db_handle *H;\n    int ret = 0;\n    char *p, *e;\n    zend_string *tmp_user, *tmp_pass;\n    smart_str conn_str = {0};\n    zend_long connect_timeout = 30;\n\n    H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent);\n    dbh->driver_data = H;\n\n    dbh->skip_param_evt = 1 << PDO_PARAM_EVT_EXEC_POST | 1 << PDO_PARAM_EVT_FETCH_PRE | 1 << PDO_PARAM_EVT_FETCH_POST;\n\n    H->einfo.errcode = 0;\n    H->einfo.errmsg = NULL;\n\n    /* PostgreSQL wants params in the connect string to be separated by spaces,\n     * if the PDO standard semicolons are used, we convert them to spaces\n     */\n    e = (char *) dbh->data_source + strlen(dbh->data_source);\n    p = (char *) dbh->data_source;\n    while ((p = memchr(p, ';', (e - p)))) {\n        *p = ' ';\n    }\n\n    if (driver_options) {\n        connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30);\n    }\n\n    /* escape username and password, if provided */\n    tmp_user = !strstr((char *) dbh->data_source, \"user=\") ? _pdo_pgsql_escape_credentials(dbh->username) : NULL;\n    tmp_pass = !strstr((char *) dbh->data_source, \"password=\") ? _pdo_pgsql_escape_credentials(dbh->password) : NULL;\n\n    smart_str_appends(&conn_str, dbh->data_source);\n    smart_str_append_printf(&conn_str, \" connect_timeout=\" ZEND_LONG_FMT, connect_timeout);\n\n    /* support both full connection string & connection string + login and/or password */\n    if (tmp_user) {\n        smart_str_append_printf(&conn_str, \" user='%s'\", ZSTR_VAL(tmp_user));\n    }\n\n    if (tmp_pass) {\n        smart_str_append_printf(&conn_str, \" password='%s'\", ZSTR_VAL(tmp_pass));\n    }\n    smart_str_0(&conn_str);\n\n    H->server = PQconnectdb(ZSTR_VAL(conn_str.s));\n    H->lob_streams = (HashTable *) pemalloc(sizeof(HashTable), dbh->is_persistent);\n    zend_hash_init(H->lob_streams, 0, NULL, NULL, 1);\n\n    if (tmp_user) {\n        zend_string_release_ex(tmp_user, 0);\n    }\n    if (tmp_pass) {\n        zend_string_release_ex(tmp_pass, 0);\n    }\n\n    smart_str_free(&conn_str);\n\n    if (PQstatus(H->server) != CONNECTION_OK) {\n        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE);\n        goto cleanup;\n    }\n\n    PQsetNoticeProcessor(H->server, _pdo_pgsql_notice, (void *) dbh);\n\n    H->attached = 1;\n    H->pgoid = -1;\n\n    dbh->methods = &pgsql_methods;\n    dbh->alloc_own_columns = 1;\n    dbh->max_escaped_char_length = 2;\n\n    ret = 1;\n\ncleanup:\n    dbh->methods = &pgsql_methods;\n    if (!ret) {\n        pgsql_handle_closer(dbh);\n    }\n\n    return ret;\n}\n/* }}} */\n\nconst pdo_driver_t swoole_pdo_pgsql_driver = {PDO_DRIVER_HEADER(pgsql), pdo_pgsql_handle_factory};\n#endif\n"
  },
  {
    "path": "thirdparty/php84/pdo_pgsql/pgsql_driver_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: dd20abc5d8580d72b25bfb3c598b1ca54a501fcc */\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, rows, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, \"\\\"\\\\t\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, \"\\\"\\\\\\\\\\\\\\\\N\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, \"\\\"\\\\t\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, \"\\\"\\\\\\\\\\\\\\\\N\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, \"\\\"\\\\t\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, \"\\\"\\\\\\\\\\\\\\\\N\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, 0, 0, 1)\n\tZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 0, \"\\\"rb\\\"\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fetchMode, IS_LONG, 0, \"PDO::FETCH_DEFAULT\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeoutMilliseconds, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, 0, 0, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlGetNotify);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlGetPid);\n\nstatic const zend_function_entry class_PDO_PGSql_Ext_methods[] = {\n\tZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlCopyToArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlCopyToFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlLOBCreate, arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlLOBOpen, arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlLOBUnlink, arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlGetNotify, arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlGetPid, arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, ZEND_ACC_PUBLIC)\n\tZEND_FE_END\n};\n"
  },
  {
    "path": "thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c",
    "content": "/* Generated by re2c 3.1 */\n/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Matteo Beccati <mbeccati@php.net>                            |\n  +----------------------------------------------------------------------+\n*/\n\n\n#include \"php.h\"\n#include \"ext/pdo/php_pdo_driver.h\"\n#include \"ext/pdo/pdo_sql_parser.h\"\n\nint pdo_pgsql_scanner(pdo_scanner_t *s)\n{\n\tconst char *cursor = s->cur;\n\n\ts->tok = cursor;\n\t\n\n\t\n{\n\tYYCTYPE yych;\n\tunsigned int yyaccept = 0;\n\tif ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase 0x00: goto yy1;\n\t\tcase '\"': goto yy4;\n\t\tcase '$': goto yy6;\n\t\tcase '\\'': goto yy7;\n\t\tcase '-': goto yy8;\n\t\tcase '/': goto yy9;\n\t\tcase ':': goto yy10;\n\t\tcase '?': goto yy11;\n\t\tcase 'E':\n\t\tcase 'e': goto yy13;\n\t\tdefault: goto yy2;\n\t}\nyy1:\n\tYYCURSOR = YYMARKER;\n\tswitch (yyaccept) {\n\t\tcase 0: goto yy5;\n\t\tcase 1: goto yy17;\n\t\tcase 2: goto yy23;\n\t\tdefault: goto yy35;\n\t}\nyy2:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase 0x00:\n\t\tcase '\"':\n\t\tcase '$':\n\t\tcase '\\'':\n\t\tcase '-':\n\t\tcase '/':\n\t\tcase ':':\n\t\tcase '?':\n\t\tcase 'E':\n\t\tcase 'e': goto yy3;\n\t\tdefault: goto yy2;\n\t}\nyy3:\n\t{ RET(PDO_PARSER_TEXT); }\nyy4:\n\tyyaccept = 0;\n\tyych = *(YYMARKER = ++YYCURSOR);\n\tif (yych >= 0x01) goto yy15;\nyy5:\n\t{ SKIP_ONE(PDO_PARSER_TEXT); }\nyy6:\n\tyyaccept = 0;\n\tyych = *(YYMARKER = ++YYCURSOR);\n\tswitch (yych) {\n\t\tcase 0x00:\n\t\tcase 0x01:\n\t\tcase 0x02:\n\t\tcase 0x03:\n\t\tcase 0x04:\n\t\tcase 0x05:\n\t\tcase 0x06:\n\t\tcase 0x07:\n\t\tcase 0x08:\n\t\tcase '\\t':\n\t\tcase '\\n':\n\t\tcase '\\v':\n\t\tcase '\\f':\n\t\tcase '\\r':\n\t\tcase 0x0E:\n\t\tcase 0x0F:\n\t\tcase 0x10:\n\t\tcase 0x11:\n\t\tcase 0x12:\n\t\tcase 0x13:\n\t\tcase 0x14:\n\t\tcase 0x15:\n\t\tcase 0x16:\n\t\tcase 0x17:\n\t\tcase 0x18:\n\t\tcase 0x19:\n\t\tcase 0x1A:\n\t\tcase 0x1B:\n\t\tcase 0x1C:\n\t\tcase 0x1D:\n\t\tcase 0x1E:\n\t\tcase 0x1F:\n\t\tcase ' ':\n\t\tcase '!':\n\t\tcase '\"':\n\t\tcase '#':\n\t\tcase '%':\n\t\tcase '&':\n\t\tcase '\\'':\n\t\tcase '(':\n\t\tcase ')':\n\t\tcase '*':\n\t\tcase '+':\n\t\tcase ',':\n\t\tcase '-':\n\t\tcase '.':\n\t\tcase '/':\n\t\tcase '0':\n\t\tcase '1':\n\t\tcase '2':\n\t\tcase '3':\n\t\tcase '4':\n\t\tcase '5':\n\t\tcase '6':\n\t\tcase '7':\n\t\tcase '8':\n\t\tcase '9':\n\t\tcase ':':\n\t\tcase ';':\n\t\tcase '<':\n\t\tcase '=':\n\t\tcase '>':\n\t\tcase '?':\n\t\tcase '@':\n\t\tcase '[':\n\t\tcase '\\\\':\n\t\tcase ']':\n\t\tcase '^':\n\t\tcase '`':\n\t\tcase '{':\n\t\tcase '|':\n\t\tcase '}':\n\t\tcase '~':\n\t\tcase 0x7F: goto yy5;\n\t\tcase '$': goto yy18;\n\t\tdefault: goto yy19;\n\t}\nyy7:\n\tyyaccept = 0;\n\tyych = *(YYMARKER = ++YYCURSOR);\n\tif (yych <= 0x00) goto yy5;\n\tgoto yy21;\nyy8:\n\tyych = *++YYCURSOR;\n\tswitch (yych) {\n\t\tcase '-': goto yy24;\n\t\tdefault: goto yy5;\n\t}\nyy9:\n\tyych = *++YYCURSOR;\n\tswitch (yych) {\n\t\tcase '*': goto yy26;\n\t\tdefault: goto yy5;\n\t}\nyy10:\n\tyych = *++YYCURSOR;\n\tswitch (yych) {\n\t\tcase '0':\n\t\tcase '1':\n\t\tcase '2':\n\t\tcase '3':\n\t\tcase '4':\n\t\tcase '5':\n\t\tcase '6':\n\t\tcase '7':\n\t\tcase '8':\n\t\tcase '9':\n\t\tcase 'A':\n\t\tcase 'B':\n\t\tcase 'C':\n\t\tcase 'D':\n\t\tcase 'E':\n\t\tcase 'F':\n\t\tcase 'G':\n\t\tcase 'H':\n\t\tcase 'I':\n\t\tcase 'J':\n\t\tcase 'K':\n\t\tcase 'L':\n\t\tcase 'M':\n\t\tcase 'N':\n\t\tcase 'O':\n\t\tcase 'P':\n\t\tcase 'Q':\n\t\tcase 'R':\n\t\tcase 'S':\n\t\tcase 'T':\n\t\tcase 'U':\n\t\tcase 'V':\n\t\tcase 'W':\n\t\tcase 'X':\n\t\tcase 'Y':\n\t\tcase 'Z':\n\t\tcase '_':\n\t\tcase 'a':\n\t\tcase 'b':\n\t\tcase 'c':\n\t\tcase 'd':\n\t\tcase 'e':\n\t\tcase 'f':\n\t\tcase 'g':\n\t\tcase 'h':\n\t\tcase 'i':\n\t\tcase 'j':\n\t\tcase 'k':\n\t\tcase 'l':\n\t\tcase 'm':\n\t\tcase 'n':\n\t\tcase 'o':\n\t\tcase 'p':\n\t\tcase 'q':\n\t\tcase 'r':\n\t\tcase 's':\n\t\tcase 't':\n\t\tcase 'u':\n\t\tcase 'v':\n\t\tcase 'w':\n\t\tcase 'x':\n\t\tcase 'y':\n\t\tcase 'z': goto yy27;\n\t\tcase ':': goto yy29;\n\t\tdefault: goto yy5;\n\t}\nyy11:\n\tyych = *++YYCURSOR;\n\tswitch (yych) {\n\t\tcase '?': goto yy31;\n\t\tdefault: goto yy12;\n\t}\nyy12:\n\t{ RET(PDO_PARSER_BIND_POS); }\nyy13:\n\tyyaccept = 0;\n\tyych = *(YYMARKER = ++YYCURSOR);\n\tswitch (yych) {\n\t\tcase '\\'': goto yy32;\n\t\tdefault: goto yy5;\n\t}\nyy14:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\nyy15:\n\tswitch (yych) {\n\t\tcase 0x00: goto yy1;\n\t\tcase '\"': goto yy16;\n\t\tdefault: goto yy14;\n\t}\nyy16:\n\tyyaccept = 1;\n\tYYMARKER = ++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase '\"': goto yy14;\n\t\tdefault: goto yy17;\n\t}\nyy17:\n\t{ RET(PDO_PARSER_TEXT); }\nyy18:\n\t++YYCURSOR;\n\t{ RET(PDO_PARSER_CUSTOM_QUOTE); }\nyy19:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase 0x00:\n\t\tcase 0x01:\n\t\tcase 0x02:\n\t\tcase 0x03:\n\t\tcase 0x04:\n\t\tcase 0x05:\n\t\tcase 0x06:\n\t\tcase 0x07:\n\t\tcase 0x08:\n\t\tcase '\\t':\n\t\tcase '\\n':\n\t\tcase '\\v':\n\t\tcase '\\f':\n\t\tcase '\\r':\n\t\tcase 0x0E:\n\t\tcase 0x0F:\n\t\tcase 0x10:\n\t\tcase 0x11:\n\t\tcase 0x12:\n\t\tcase 0x13:\n\t\tcase 0x14:\n\t\tcase 0x15:\n\t\tcase 0x16:\n\t\tcase 0x17:\n\t\tcase 0x18:\n\t\tcase 0x19:\n\t\tcase 0x1A:\n\t\tcase 0x1B:\n\t\tcase 0x1C:\n\t\tcase 0x1D:\n\t\tcase 0x1E:\n\t\tcase 0x1F:\n\t\tcase ' ':\n\t\tcase '!':\n\t\tcase '\"':\n\t\tcase '#':\n\t\tcase '%':\n\t\tcase '&':\n\t\tcase '\\'':\n\t\tcase '(':\n\t\tcase ')':\n\t\tcase '*':\n\t\tcase '+':\n\t\tcase ',':\n\t\tcase '-':\n\t\tcase '.':\n\t\tcase '/':\n\t\tcase ':':\n\t\tcase ';':\n\t\tcase '<':\n\t\tcase '=':\n\t\tcase '>':\n\t\tcase '?':\n\t\tcase '@':\n\t\tcase '[':\n\t\tcase '\\\\':\n\t\tcase ']':\n\t\tcase '^':\n\t\tcase '`':\n\t\tcase '{':\n\t\tcase '|':\n\t\tcase '}':\n\t\tcase '~':\n\t\tcase 0x7F: goto yy1;\n\t\tcase '$': goto yy18;\n\t\tdefault: goto yy19;\n\t}\nyy20:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\nyy21:\n\tswitch (yych) {\n\t\tcase 0x00: goto yy1;\n\t\tcase '\\'': goto yy22;\n\t\tdefault: goto yy20;\n\t}\nyy22:\n\tyyaccept = 2;\n\tYYMARKER = ++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase '\\'': goto yy20;\n\t\tdefault: goto yy23;\n\t}\nyy23:\n\t{ RET(PDO_PARSER_TEXT); }\nyy24:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase '\\n': goto yy25;\n\t\tdefault: goto yy24;\n\t}\nyy25:\n\t{ RET(PDO_PARSER_TEXT); }\nyy26:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase '*': goto yy33;\n\t\tdefault: goto yy26;\n\t}\nyy27:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase '0':\n\t\tcase '1':\n\t\tcase '2':\n\t\tcase '3':\n\t\tcase '4':\n\t\tcase '5':\n\t\tcase '6':\n\t\tcase '7':\n\t\tcase '8':\n\t\tcase '9':\n\t\tcase 'A':\n\t\tcase 'B':\n\t\tcase 'C':\n\t\tcase 'D':\n\t\tcase 'E':\n\t\tcase 'F':\n\t\tcase 'G':\n\t\tcase 'H':\n\t\tcase 'I':\n\t\tcase 'J':\n\t\tcase 'K':\n\t\tcase 'L':\n\t\tcase 'M':\n\t\tcase 'N':\n\t\tcase 'O':\n\t\tcase 'P':\n\t\tcase 'Q':\n\t\tcase 'R':\n\t\tcase 'S':\n\t\tcase 'T':\n\t\tcase 'U':\n\t\tcase 'V':\n\t\tcase 'W':\n\t\tcase 'X':\n\t\tcase 'Y':\n\t\tcase 'Z':\n\t\tcase '_':\n\t\tcase 'a':\n\t\tcase 'b':\n\t\tcase 'c':\n\t\tcase 'd':\n\t\tcase 'e':\n\t\tcase 'f':\n\t\tcase 'g':\n\t\tcase 'h':\n\t\tcase 'i':\n\t\tcase 'j':\n\t\tcase 'k':\n\t\tcase 'l':\n\t\tcase 'm':\n\t\tcase 'n':\n\t\tcase 'o':\n\t\tcase 'p':\n\t\tcase 'q':\n\t\tcase 'r':\n\t\tcase 's':\n\t\tcase 't':\n\t\tcase 'u':\n\t\tcase 'v':\n\t\tcase 'w':\n\t\tcase 'x':\n\t\tcase 'y':\n\t\tcase 'z': goto yy27;\n\t\tdefault: goto yy28;\n\t}\nyy28:\n\t{ RET(PDO_PARSER_BIND); }\nyy29:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase ':': goto yy29;\n\t\tdefault: goto yy30;\n\t}\nyy30:\n\t{ RET(PDO_PARSER_TEXT); }\nyy31:\n\t++YYCURSOR;\n\t{ RET(PDO_PARSER_ESCAPED_QUESTION); }\nyy32:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase 0x00: goto yy1;\n\t\tcase '\\'': goto yy34;\n\t\tcase '\\\\': goto yy36;\n\t\tdefault: goto yy32;\n\t}\nyy33:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase '*': goto yy33;\n\t\tcase '/': goto yy37;\n\t\tdefault: goto yy26;\n\t}\nyy34:\n\tyyaccept = 3;\n\tYYMARKER = ++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tswitch (yych) {\n\t\tcase '\\'': goto yy32;\n\t\tdefault: goto yy35;\n\t}\nyy35:\n\t{ RET(PDO_PARSER_TEXT); }\nyy36:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tif (yych <= 0x00) goto yy1;\n\tgoto yy32;\nyy37:\n\t++YYCURSOR;\n\tgoto yy25;\n}\n\n}\n"
  },
  {
    "path": "thirdparty/php84/pdo_pgsql/pgsql_statement.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Authors: Edin Kadribasic <edink@emini.dk>                            |\n  |          Ilia Alshanestsky <ilia@prohost.org>                        |\n  |          Wez Furlong <wez@php.net>                                   |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_PGSQL_HOOK\n#include \"php_swoole_pgsql.h\"\n\n#if PHP_VERSION_ID >= 80400 && PHP_VERSION_ID < 80500\n\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"ext/pdo/php_pdo.h\"\n#include \"ext/pdo/php_pdo_driver.h\"\n#include \"php_pdo_pgsql_int.h\"\n#ifdef HAVE_NETINET_IN_H\n#include <netinet/in.h>\n#endif\n\n/* from postgresql/src/include/catalog/pg_type.h */\n#define BOOLLABEL \"bool\"\n#define BOOLOID 16\n#define BYTEALABEL \"bytea\"\n#define BYTEAOID 17\n#define DATELABEL \"date\"\n#define DATEOID 1082\n#define INT2LABEL \"int2\"\n#define INT2OID 21\n#define INT4LABEL \"int4\"\n#define INT4OID 23\n#define INT8LABEL \"int8\"\n#define INT8OID 20\n#define OIDOID 26\n#define TEXTLABEL \"text\"\n#define TEXTOID 25\n#define TIMESTAMPLABEL \"timestamp\"\n#define TIMESTAMPOID 1114\n#define VARCHARLABEL \"varchar\"\n#define VARCHAROID 1043\n#define FLOAT4LABEL \"float4\"\n#define FLOAT4OID 700\n#define FLOAT8LABEL \"float8\"\n#define FLOAT8OID 701\n\nstatic int pgsql_stmt_dtor(pdo_stmt_t *stmt) {\n    pdo_pgsql_stmt *S = (pdo_pgsql_stmt *) stmt->driver_data;\n    bool server_obj_usable =\n        !Z_ISUNDEF(stmt->database_object_handle) &&\n        IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)]) &&\n        !(OBJ_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED);\n\n    if (S->result) {\n        /* free the resource */\n        PQclear(S->result);\n        S->result = NULL;\n    }\n\n    if (S->stmt_name) {\n        if (S->is_prepared && server_obj_usable) {\n            pdo_pgsql_db_handle *H = S->H;\n            char *q = NULL;\n            PGresult *res;\n\n            spprintf(&q, 0, \"DEALLOCATE %s\", S->stmt_name);\n            res = PQexec(H->server, q);\n            efree(q);\n            if (res) {\n                PQclear(res);\n            }\n        }\n        efree(S->stmt_name);\n        S->stmt_name = NULL;\n    }\n    if (S->param_lengths) {\n        efree(S->param_lengths);\n        S->param_lengths = NULL;\n    }\n    if (S->param_values) {\n        efree(S->param_values);\n        S->param_values = NULL;\n    }\n    if (S->param_formats) {\n        efree(S->param_formats);\n        S->param_formats = NULL;\n    }\n    if (S->param_types) {\n        efree(S->param_types);\n        S->param_types = NULL;\n    }\n    if (S->query) {\n        zend_string_release(S->query);\n        S->query = NULL;\n    }\n\n    if (S->cursor_name) {\n        if (server_obj_usable) {\n            pdo_pgsql_db_handle *H = S->H;\n            char *q = NULL;\n            PGresult *res;\n\n            spprintf(&q, 0, \"CLOSE %s\", S->cursor_name);\n            res = PQexec(H->server, q);\n            efree(q);\n            if (res) PQclear(res);\n        }\n        efree(S->cursor_name);\n        S->cursor_name = NULL;\n    }\n\n    if (S->cols) {\n        efree(S->cols);\n        S->cols = NULL;\n    }\n    efree(S);\n    stmt->driver_data = NULL;\n    return 1;\n}\n\nstatic int pgsql_stmt_execute(pdo_stmt_t *stmt) {\n    pdo_pgsql_stmt *S = (pdo_pgsql_stmt *) stmt->driver_data;\n    pdo_pgsql_db_handle *H = S->H;\n    ExecStatusType status;\n\n    bool in_trans = stmt->dbh->methods->in_transaction(stmt->dbh);\n\n    /* ensure that we free any previous unfetched results */\n    if (S->result) {\n        PQclear(S->result);\n        S->result = NULL;\n    }\n\n    S->current_row = 0;\n\n    if (S->cursor_name) {\n        char *q = NULL;\n\n        if (S->is_prepared) {\n            spprintf(&q, 0, \"CLOSE %s\", S->cursor_name);\n            PQclear(PQexec(H->server, q));\n            efree(q);\n        }\n\n        spprintf(\n            &q, 0, \"DECLARE %s SCROLL CURSOR WITH HOLD FOR %s\", S->cursor_name, ZSTR_VAL(stmt->active_query_string));\n        S->result = PQexec(H->server, q);\n        efree(q);\n\n        /* check if declare failed */\n        status = PQresultStatus(S->result);\n        if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {\n            pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));\n            return 0;\n        }\n        PQclear(S->result);\n\n        /* the cursor was declared correctly */\n        S->is_prepared = 1;\n\n        /* fetch to be able to get the number of tuples later, but don't advance the cursor pointer */\n        spprintf(&q, 0, \"FETCH FORWARD 0 FROM %s\", S->cursor_name);\n        S->result = PQexec(H->server, q);\n        efree(q);\n    } else if (S->stmt_name) {\n        /* using a prepared statement */\n\n        if (!S->is_prepared) {\n        stmt_retry:\n            /* we deferred the prepare until now, because we didn't\n             * know anything about the parameter types; now we do */\n            S->result = PQprepare(H->server,\n                                  S->stmt_name,\n                                  ZSTR_VAL(S->query),\n                                  stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0,\n                                  S->param_types);\n            status = PQresultStatus(S->result);\n            switch (status) {\n            case PGRES_COMMAND_OK:\n            case PGRES_TUPLES_OK:\n                /* it worked */\n                S->is_prepared = 1;\n                PQclear(S->result);\n                break;\n            default: {\n                char *sqlstate = pdo_pgsql_sqlstate(S->result);\n                /* 42P05 means that the prepared statement already existed. this can happen if you use\n                 * a connection pooling software line pgpool which doesn't close the db-connection once\n                 * php disconnects. if php dies (no chance to run RSHUTDOWN) during execution it has no\n                 * chance to DEALLOCATE the prepared statements it has created. so, if we hit a 42P05 we\n                 * deallocate it and retry ONCE (thies 2005.12.15)\n                 */\n                if (sqlstate && !strcmp(sqlstate, \"42P05\")) {\n                    char buf[100]; /* stmt_name == \"pdo_crsr_%08x\" */\n                    PGresult *res;\n                    snprintf(buf, sizeof(buf), \"DEALLOCATE %s\", S->stmt_name);\n                    res = PQexec(H->server, buf);\n                    if (res) {\n                        PQclear(res);\n                    }\n                    goto stmt_retry;\n                } else {\n                    pdo_pgsql_error_stmt(stmt, status, sqlstate);\n                    return 0;\n                }\n            }\n            }\n        }\n        S->result = PQexecPrepared(H->server,\n                                   S->stmt_name,\n                                   stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0,\n                                   (const char **) S->param_values,\n                                   S->param_lengths,\n                                   S->param_formats,\n                                   0);\n    } else if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED) {\n        /* execute query with parameters */\n        S->result = PQexecParams(H->server,\n                                 ZSTR_VAL(S->query),\n                                 stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0,\n                                 S->param_types,\n                                 (const char **) S->param_values,\n                                 S->param_lengths,\n                                 S->param_formats,\n                                 0);\n    } else {\n        /* execute plain query (with embedded parameters) */\n        S->result = PQexec(H->server, ZSTR_VAL(stmt->active_query_string));\n    }\n    status = PQresultStatus(S->result);\n\n    if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {\n        pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));\n        return 0;\n    }\n\n    stmt->column_count = (int) PQnfields(S->result);\n    if (S->cols == NULL) {\n        S->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column));\n    }\n\n    if (status == PGRES_COMMAND_OK) {\n        stmt->row_count = ZEND_ATOL(PQcmdTuples(S->result));\n        H->pgoid = PQoidValue(S->result);\n    } else {\n        stmt->row_count = (zend_long) PQntuples(S->result);\n    }\n\n    if (in_trans && !stmt->dbh->methods->in_transaction(stmt->dbh)) {\n        pdo_pgsql_close_lob_streams(stmt->dbh);\n    }\n\n    return 1;\n}\n\nstatic int pgsql_stmt_param_hook(pdo_stmt_t *stmt,\n                                 struct pdo_bound_param_data *param,\n                                 enum pdo_param_event event_type) {\n    pdo_pgsql_stmt *S = (pdo_pgsql_stmt *) stmt->driver_data;\n\n    if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED && param->is_param) {\n        switch (event_type) {\n        case PDO_PARAM_EVT_FREE:\n            if (param->driver_data) {\n                efree(param->driver_data);\n            }\n            break;\n\n        case PDO_PARAM_EVT_NORMALIZE:\n            /* decode name from $1, $2 into 0, 1 etc. */\n            if (param->name) {\n                if (ZSTR_VAL(param->name)[0] == '$') {\n                    param->paramno = ZEND_ATOL(ZSTR_VAL(param->name) + 1);\n                } else {\n                    /* resolve parameter name to rewritten name */\n                    zend_string *namevar;\n\n                    if (stmt->bound_param_map &&\n                        (namevar = zend_hash_find_ptr(stmt->bound_param_map, param->name)) != NULL) {\n                        param->paramno = ZEND_ATOL(ZSTR_VAL(namevar) + 1);\n                        param->paramno--;\n                    } else {\n                        pdo_pgsql_error_stmt_msg(stmt, 0, \"HY093\", ZSTR_VAL(param->name));\n                        return 0;\n                    }\n                }\n            }\n            break;\n\n        case PDO_PARAM_EVT_ALLOC:\n            if (!stmt->bound_param_map) {\n                return 1;\n            }\n            if (!zend_hash_index_exists(stmt->bound_param_map, param->paramno)) {\n                pdo_pgsql_error_stmt_msg(stmt, 0, \"HY093\", \"parameter was not defined\");\n                return 0;\n            }\n            ZEND_FALLTHROUGH;\n        case PDO_PARAM_EVT_EXEC_POST:\n        case PDO_PARAM_EVT_FETCH_PRE:\n        case PDO_PARAM_EVT_FETCH_POST:\n            /* work is handled by EVT_NORMALIZE */\n            return 1;\n\n        case PDO_PARAM_EVT_EXEC_PRE:\n            if (!stmt->bound_param_map) {\n                return 1;\n            }\n            if (!S->param_values) {\n                S->param_values = ecalloc(zend_hash_num_elements(stmt->bound_param_map), sizeof(char *));\n                S->param_lengths = ecalloc(zend_hash_num_elements(stmt->bound_param_map), sizeof(int));\n                S->param_formats = ecalloc(zend_hash_num_elements(stmt->bound_param_map), sizeof(int));\n                S->param_types = ecalloc(zend_hash_num_elements(stmt->bound_param_map), sizeof(Oid));\n            }\n            if (param->paramno >= 0) {\n                zval *parameter;\n\n                /*\n                if (param->paramno >= zend_hash_num_elements(stmt->bound_params)) {\n                        pdo_raise_impl_error(stmt->dbh, stmt, \"HY093\", \"parameter was not defined\");\n                        return 0;\n                }\n                */\n\n                if (Z_ISREF(param->parameter)) {\n                    parameter = Z_REFVAL(param->parameter);\n                } else {\n                    parameter = &param->parameter;\n                }\n\n                if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && Z_TYPE_P(parameter) == IS_RESOURCE) {\n                    php_stream *stm;\n                    php_stream_from_zval_no_verify(stm, parameter);\n                    if (stm) {\n                        if (php_stream_is(stm, &pdo_pgsql_lob_stream_ops)) {\n                            struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self *) stm->abstract;\n                            pdo_pgsql_bound_param *P = param->driver_data;\n\n                            if (P == NULL) {\n                                P = ecalloc(1, sizeof(*P));\n                                param->driver_data = P;\n                            }\n                            P->oid = htonl(self->oid);\n                            S->param_values[param->paramno] = (char *) &P->oid;\n                            S->param_lengths[param->paramno] = sizeof(P->oid);\n                            S->param_formats[param->paramno] = 1;\n                            S->param_types[param->paramno] = OIDOID;\n                            return 1;\n                        } else {\n                            zend_string *str = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0);\n                            if (str != NULL) {\n                                ZVAL_STR(parameter, str);\n                            } else {\n                                ZVAL_EMPTY_STRING(parameter);\n                            }\n                        }\n                    } else {\n                        /* expected a stream resource */\n                        pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, \"HY105\");\n                        return 0;\n                    }\n                }\n\n                if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || Z_TYPE_P(parameter) == IS_NULL) {\n                    S->param_values[param->paramno] = NULL;\n                    S->param_lengths[param->paramno] = 0;\n                } else if (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE) {\n                    S->param_values[param->paramno] = Z_TYPE_P(parameter) == IS_TRUE ? \"t\" : \"f\";\n                    S->param_lengths[param->paramno] = 1;\n                    S->param_formats[param->paramno] = 0;\n                } else {\n                    convert_to_string(parameter);\n                    S->param_values[param->paramno] = Z_STRVAL_P(parameter);\n                    S->param_lengths[param->paramno] = Z_STRLEN_P(parameter);\n                    S->param_formats[param->paramno] = 0;\n                }\n\n                if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {\n                    S->param_types[param->paramno] = 0;\n                    S->param_formats[param->paramno] = 1;\n                } else {\n                    S->param_types[param->paramno] = 0;\n                }\n            }\n            break;\n        }\n    } else if (param->is_param && event_type == PDO_PARAM_EVT_NORMALIZE) {\n        /* We need to manually convert to a pg native boolean value */\n        if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL &&\n            ((param->param_type & PDO_PARAM_INPUT_OUTPUT) != PDO_PARAM_INPUT_OUTPUT)) {\n            const char *s = zend_is_true(&param->parameter) ? \"t\" : \"f\";\n            param->param_type = PDO_PARAM_STR;\n            zval_ptr_dtor(&param->parameter);\n            ZVAL_STRINGL(&param->parameter, s, 1);\n        }\n    }\n    return 1;\n}\n\nstatic int pgsql_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) {\n    pdo_pgsql_stmt *S = (pdo_pgsql_stmt *) stmt->driver_data;\n\n    if (S->cursor_name) {\n        char *ori_str = NULL;\n        char *q = NULL;\n        ExecStatusType status;\n\n        switch (ori) {\n        case PDO_FETCH_ORI_NEXT:\n            spprintf(&ori_str, 0, \"NEXT\");\n            break;\n        case PDO_FETCH_ORI_PRIOR:\n            spprintf(&ori_str, 0, \"BACKWARD\");\n            break;\n        case PDO_FETCH_ORI_FIRST:\n            spprintf(&ori_str, 0, \"FIRST\");\n            break;\n        case PDO_FETCH_ORI_LAST:\n            spprintf(&ori_str, 0, \"LAST\");\n            break;\n        case PDO_FETCH_ORI_ABS:\n            spprintf(&ori_str, 0, \"ABSOLUTE \" ZEND_LONG_FMT, offset);\n            break;\n        case PDO_FETCH_ORI_REL:\n            spprintf(&ori_str, 0, \"RELATIVE \" ZEND_LONG_FMT, offset);\n            break;\n        default:\n            return 0;\n        }\n\n        if (S->result) {\n            PQclear(S->result);\n            S->result = NULL;\n        }\n\n        spprintf(&q, 0, \"FETCH %s FROM %s\", ori_str, S->cursor_name);\n        efree(ori_str);\n        S->result = PQexec(S->H->server, q);\n        efree(q);\n        status = PQresultStatus(S->result);\n\n        if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {\n            pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));\n            return 0;\n        }\n\n        if (PQntuples(S->result)) {\n            S->current_row = 1;\n            return 1;\n        } else {\n            return 0;\n        }\n    } else {\n        if (S->current_row < stmt->row_count) {\n            S->current_row++;\n            return 1;\n        } else {\n            return 0;\n        }\n    }\n}\n\nstatic int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno) {\n    pdo_pgsql_stmt *S = (pdo_pgsql_stmt *) stmt->driver_data;\n    struct pdo_column_data *cols = stmt->columns;\n    char *str;\n\n    if (!S->result) {\n        return 0;\n    }\n\n    str = PQfname(S->result, colno);\n    cols[colno].name = zend_string_init(str, strlen(str), 0);\n    cols[colno].maxlen = PQfsize(S->result, colno);\n    cols[colno].precision = PQfmod(S->result, colno);\n    S->cols[colno].pgsql_type = PQftype(S->result, colno);\n\n    return 1;\n}\n\nstatic int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) {\n    pdo_pgsql_stmt *S = (pdo_pgsql_stmt *) stmt->driver_data;\n    if (!S->result) {\n        return 0;\n    }\n\n    /* We have already increased count by 1 in pgsql_stmt_fetch() */\n    if (PQgetisnull(S->result, S->current_row - 1, colno)) { /* Check if we got NULL */\n        ZVAL_NULL(result);\n    } else {\n        char *ptr = PQgetvalue(S->result, S->current_row - 1, colno);\n        size_t len = PQgetlength(S->result, S->current_row - 1, colno);\n\n        switch (S->cols[colno].pgsql_type) {\n        case BOOLOID:\n            ZVAL_BOOL(result, *ptr == 't');\n            break;\n\n        case INT2OID:\n        case INT4OID:\n#if SIZEOF_ZEND_LONG >= 8\n        case INT8OID:\n#endif\n            ZVAL_LONG(result, ZEND_ATOL(ptr));\n            break;\n        case FLOAT4OID:\n        case FLOAT8OID:\n            if (strncmp(ptr, \"Infinity\", len) == 0) {\n                ZVAL_DOUBLE(result, ZEND_INFINITY);\n            } else if (strncmp(ptr, \"-Infinity\", len) == 0) {\n                ZVAL_DOUBLE(result, -ZEND_INFINITY);\n            } else if (strncmp(ptr, \"NaN\", len) == 0) {\n                ZVAL_DOUBLE(result, ZEND_NAN);\n            } else {\n                ZVAL_DOUBLE(result, zend_strtod(ptr, NULL));\n            }\n            break;\n\n        case OIDOID: {\n            char *end_ptr;\n            Oid oid = (Oid) strtoul(ptr, &end_ptr, 10);\n            if (type && *type == PDO_PARAM_LOB) {\n                /* If column was bound as LOB, return a stream. */\n                int loid = lo_open(S->H->server, oid, INV_READ);\n                if (loid >= 0) {\n                    php_stream *stream = pdo_pgsql_create_lob_stream(&stmt->database_object_handle, loid, oid);\n                    if (stream) {\n                        php_stream_to_zval(stream, result);\n                        return 1;\n                    }\n                }\n                return 0;\n            } else {\n                /* Otherwise return OID as integer. */\n                ZVAL_LONG(result, oid);\n            }\n            break;\n        }\n\n        case BYTEAOID: {\n            size_t tmp_len;\n            char *tmp_ptr = (char *) PQunescapeBytea((unsigned char *) ptr, &tmp_len);\n            if (!tmp_ptr) {\n                /* PQunescapeBytea returned an error */\n                return 0;\n            }\n\n            zend_string *str = zend_string_init(tmp_ptr, tmp_len, 0);\n            php_stream *stream = php_stream_memory_open(TEMP_STREAM_READONLY, str);\n            php_stream_to_zval(stream, result);\n            zend_string_release(str);\n            PQfreemem(tmp_ptr);\n            break;\n        }\n\n        default:\n            ZVAL_STRINGL_FAST(result, ptr, len);\n            break;\n        }\n    }\n\n    return 1;\n}\n\nstatic zend_always_inline char *pdo_pgsql_translate_oid_to_table(Oid oid, PGconn *conn) {\n    char *table_name = NULL;\n    PGresult *tmp_res;\n    char *querystr = NULL;\n\n    spprintf(&querystr, 0, \"SELECT RELNAME FROM PG_CLASS WHERE OID=%d\", oid);\n\n    if ((tmp_res = PQexec(conn, querystr)) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) {\n        if (tmp_res) {\n            PQclear(tmp_res);\n        }\n        efree(querystr);\n        return 0;\n    }\n    efree(querystr);\n\n    if (1 == PQgetisnull(tmp_res, 0, 0) || (table_name = PQgetvalue(tmp_res, 0, 0)) == NULL) {\n        PQclear(tmp_res);\n        return 0;\n    }\n\n    table_name = estrdup(table_name);\n\n    PQclear(tmp_res);\n    return table_name;\n}\n\nstatic int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) {\n    pdo_pgsql_stmt *S = (pdo_pgsql_stmt *) stmt->driver_data;\n    PGresult *res;\n    char *q = NULL;\n    ExecStatusType status;\n    Oid table_oid;\n    char *table_name = NULL;\n\n    if (!S->result) {\n        return FAILURE;\n    }\n\n    if (colno >= stmt->column_count) {\n        return FAILURE;\n    }\n\n    array_init(return_value);\n    add_assoc_long(return_value, \"pgsql:oid\", S->cols[colno].pgsql_type);\n\n    table_oid = PQftable(S->result, colno);\n    add_assoc_long(return_value, \"pgsql:table_oid\", table_oid);\n    table_name = pdo_pgsql_translate_oid_to_table(table_oid, S->H->server);\n    if (table_name) {\n        add_assoc_string(return_value, \"table\", table_name);\n        efree(table_name);\n    }\n\n    switch (S->cols[colno].pgsql_type) {\n    case BOOLOID:\n        add_assoc_string(return_value, \"native_type\", BOOLLABEL);\n        break;\n    case BYTEAOID:\n        add_assoc_string(return_value, \"native_type\", BYTEALABEL);\n        break;\n    case INT8OID:\n        add_assoc_string(return_value, \"native_type\", INT8LABEL);\n        break;\n    case INT2OID:\n        add_assoc_string(return_value, \"native_type\", INT2LABEL);\n        break;\n    case INT4OID:\n        add_assoc_string(return_value, \"native_type\", INT4LABEL);\n        break;\n    case FLOAT4OID:\n        add_assoc_string(return_value, \"native_type\", FLOAT4LABEL);\n        break;\n    case FLOAT8OID:\n        add_assoc_string(return_value, \"native_type\", FLOAT8LABEL);\n        break;\n    case TEXTOID:\n        add_assoc_string(return_value, \"native_type\", TEXTLABEL);\n        break;\n    case VARCHAROID:\n        add_assoc_string(return_value, \"native_type\", VARCHARLABEL);\n        break;\n    case DATEOID:\n        add_assoc_string(return_value, \"native_type\", DATELABEL);\n        break;\n    case TIMESTAMPOID:\n        add_assoc_string(return_value, \"native_type\", TIMESTAMPLABEL);\n        break;\n    default:\n        /* Fetch metadata from Postgres system catalogue */\n        spprintf(&q, 0, \"SELECT TYPNAME FROM PG_TYPE WHERE OID=%u\", S->cols[colno].pgsql_type);\n        res = PQexec(S->H->server, q);\n        efree(q);\n        status = PQresultStatus(res);\n        if (status == PGRES_TUPLES_OK && 1 == PQntuples(res)) {\n            add_assoc_string(return_value, \"native_type\", PQgetvalue(res, 0, 0));\n        }\n        PQclear(res);\n    }\n\n    enum pdo_param_type param_type;\n    switch (S->cols[colno].pgsql_type) {\n    case BOOLOID:\n        param_type = PDO_PARAM_BOOL;\n        break;\n    case INT2OID:\n    case INT4OID:\n    case INT8OID:\n        param_type = PDO_PARAM_INT;\n        break;\n    case OIDOID:\n    case BYTEAOID:\n        param_type = PDO_PARAM_LOB;\n        break;\n    default:\n        param_type = PDO_PARAM_STR;\n    }\n    add_assoc_long(return_value, \"pdo_type\", param_type);\n\n    return 1;\n}\n\nstatic int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt) {\n    return 1;\n}\n\nstatic int pgsql_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val) {\n    pdo_pgsql_stmt *S = (pdo_pgsql_stmt *) stmt->driver_data;\n\n    switch (attr) {\n#ifdef HAVE_PG_RESULT_MEMORY_SIZE\n    case PDO_PGSQL_ATTR_RESULT_MEMORY_SIZE:\n        if (stmt->executed) {\n            ZVAL_LONG(val, PQresultMemorySize(S->result));\n        } else {\n            char *tmp;\n            spprintf(&tmp, 0, \"statement '%s' has not been executed yet\", S->stmt_name);\n\n            pdo_pgsql_error_stmt_msg(stmt, 0, \"HY000\", tmp);\n            efree(tmp);\n\n            ZVAL_NULL(val);\n        }\n        return 1;\n#endif\n\n    default:\n        (void) S;\n        return 0;\n    }\n}\n\nconst struct pdo_stmt_methods swoole_pgsql_stmt_methods = {pgsql_stmt_dtor,\n                                                           pgsql_stmt_execute,\n                                                           pgsql_stmt_fetch,\n                                                           pgsql_stmt_describe,\n                                                           pgsql_stmt_get_col,\n                                                           pgsql_stmt_param_hook,\n                                                           NULL, /* set_attr */\n                                                           pgsql_stmt_get_attr,\n                                                           pgsql_stmt_get_column_meta,\n                                                           NULL, /* next_rowset */\n                                                           pdo_pgsql_stmt_cursor_closer};\n#endif\n"
  },
  {
    "path": "thirdparty/php84/pdo_pgsql/php_pdo_pgsql_int.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Authors: Edin Kadribasic <edink@emini.dk>                            |\n  |          Ilia Alshanestsky <ilia@prohost.org>                        |\n  |          Wez Furlong <wez@php.net>                                   |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef PHP_PDO_PGSQL_INT_H\n#define PHP_PDO_PGSQL_INT_H\n\n#include <libpq-fe.h>\n#include <libpq/libpq-fs.h>\n#include <php.h>\n\n#define PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE \"08006\"\n\ntypedef struct {\n    const char *file;\n    int line;\n    unsigned int errcode;\n    char *errmsg;\n} pdo_pgsql_error_info;\n\n/* stuff we use in a pgsql database handle */\ntypedef struct {\n    PGconn *server;\n    unsigned attached : 1;\n    unsigned _reserved : 31;\n    pdo_pgsql_error_info einfo;\n    Oid pgoid;\n    unsigned int stmt_counter;\n    /* The following two variables have the same purpose. Unfortunately we need\n       to keep track of two different attributes having the same effect. */\n    bool emulate_prepares;\n    bool disable_native_prepares; /* deprecated since 5.6 */\n    bool disable_prepares;\n    HashTable *lob_streams;\n    zend_fcall_info_cache *notice_callback;\n} pdo_pgsql_db_handle;\n\ntypedef struct {\n    Oid pgsql_type;\n} pdo_pgsql_column;\n\ntypedef struct {\n    pdo_pgsql_db_handle *H;\n    PGresult *result;\n    pdo_pgsql_column *cols;\n    char *cursor_name;\n    char *stmt_name;\n    zend_string *query;\n    char **param_values;\n    int *param_lengths;\n    int *param_formats;\n    Oid *param_types;\n    int current_row;\n    bool is_prepared;\n} pdo_pgsql_stmt;\n\ntypedef struct {\n    Oid oid;\n} pdo_pgsql_bound_param;\n\nextern const pdo_driver_t pdo_pgsql_driver;\n\nextern int pdo_pgsql_scanner(pdo_scanner_t *s);\n\nextern int _pdo_pgsql_error(\n    pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line);\n#define pdo_pgsql_error(d, e, z) _pdo_pgsql_error(d, NULL, e, z, NULL, __FILE__, __LINE__)\n#define pdo_pgsql_error_msg(d, e, m) _pdo_pgsql_error(d, NULL, e, NULL, m, __FILE__, __LINE__)\n#define pdo_pgsql_error_stmt(s, e, z) _pdo_pgsql_error(s->dbh, s, e, z, NULL, __FILE__, __LINE__)\n#define pdo_pgsql_error_stmt_msg(stmt, e, sqlstate, msg)                                                               \\\n    _pdo_pgsql_error(stmt->dbh, stmt, e, sqlstate, msg, __FILE__, __LINE__)\n\nextern const struct pdo_stmt_methods swoole_pgsql_stmt_methods;\n\n#define pdo_pgsql_sqlstate(r) PQresultErrorField(r, PG_DIAG_SQLSTATE)\n\nenum {\n    PDO_PGSQL_ATTR_DISABLE_PREPARES = PDO_ATTR_DRIVER_SPECIFIC,\n    PDO_PGSQL_ATTR_RESULT_MEMORY_SIZE,\n};\n\nstruct pdo_pgsql_lob_self {\n    zval dbh;\n    PGconn *conn;\n    int lfd;\n    Oid oid;\n};\n\nenum pdo_pgsql_specific_constants {\n    PGSQL_TRANSACTION_IDLE = PQTRANS_IDLE,\n    PGSQL_TRANSACTION_ACTIVE = PQTRANS_ACTIVE,\n    PGSQL_TRANSACTION_INTRANS = PQTRANS_INTRANS,\n    PGSQL_TRANSACTION_INERROR = PQTRANS_INERROR,\n    PGSQL_TRANSACTION_UNKNOWN = PQTRANS_UNKNOWN\n};\n\nphp_stream *pdo_pgsql_create_lob_stream(zval *pdh, int lfd, Oid oid);\nextern const php_stream_ops pdo_pgsql_lob_stream_ops;\n\nvoid pdo_pgsql_cleanup_notice_callback(pdo_pgsql_db_handle *H);\n\nvoid pdo_libpq_version(char *buf, size_t len);\nvoid pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh);\n\nvoid pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAMETERS);\n\n#endif /* PHP_PDO_PGSQL_INT_H */\n"
  },
  {
    "path": "thirdparty/php85/pdo_firebird/CREDITS",
    "content": "Firebird driver for PDO\nArd Biesheuvel\n"
  },
  {
    "path": "thirdparty/php85/pdo_firebird/firebird_driver.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Ard Biesheuvel <abies@php.net>                               |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_FIREBIRD_HOOK\n#include \"php_swoole_firebird.h\"\n\n#if PHP_VERSION_ID >= 80500\n\n#ifndef _GNU_SOURCE\n# define _GNU_SOURCE\n#endif\n\n#include \"php.h\"\n#include \"zend_exceptions.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"ext/pdo/php_pdo.h\"\n#include \"ext/pdo/php_pdo_driver.h\"\n#include \"php_pdo_firebird_int.h\"\n#include \"pdo_firebird_utils.h\"\n\nstatic int php_firebird_alloc_prepare_stmt(pdo_dbh_t*, const zend_string*, XSQLDA*, isc_stmt_handle*,\n\tHashTable*);\nstatic bool php_firebird_rollback_transaction(pdo_dbh_t *dbh);\n\nconst char CHR_LETTER = 1;\nconst char CHR_DIGIT = 2;\nconst char CHR_IDENT = 4;\nconst char CHR_QUOTE = 8;\nconst char CHR_WHITE = 16;\nconst char CHR_HEX = 32;\nconst char CHR_INTRODUCER = 64;\n\nstatic const char classes_array[] = {\n\t/* 000     */ 0,\n\t/* 001     */ 0,\n\t/* 002     */ 0,\n\t/* 003     */ 0,\n\t/* 004     */ 0,\n\t/* 005     */ 0,\n\t/* 006     */ 0,\n\t/* 007     */ 0,\n\t/* 008     */ 0,\n\t/* 009     */ 16, /* CHR_WHITE */\n\t/* 010     */ 16, /* CHR_WHITE */\n\t/* 011     */ 0,\n\t/* 012     */ 0,\n\t/* 013     */ 16, /* CHR_WHITE */\n\t/* 014     */ 0,\n\t/* 015     */ 0,\n\t/* 016     */ 0,\n\t/* 017     */ 0,\n\t/* 018     */ 0,\n\t/* 019     */ 0,\n\t/* 020     */ 0,\n\t/* 021     */ 0,\n\t/* 022     */ 0,\n\t/* 023     */ 0,\n\t/* 024     */ 0,\n\t/* 025     */ 0,\n\t/* 026     */ 0,\n\t/* 027     */ 0,\n\t/* 028     */ 0,\n\t/* 029     */ 0,\n\t/* 030     */ 0,\n\t/* 031     */ 0,\n\t/* 032     */ 16, /* CHR_WHITE */\n\t/* 033  !  */ 0,\n\t/* 034  \"  */ 8, /* CHR_QUOTE */\n\t/* 035  #  */ 0,\n\t/* 036  $  */ 4, /* CHR_IDENT */\n\t/* 037  %  */ 0,\n\t/* 038  &  */ 0,\n\t/* 039  '  */ 8, /* CHR_QUOTE */\n\t/* 040  (  */ 0,\n\t/* 041  )  */ 0,\n\t/* 042  *  */ 0,\n\t/* 043  +  */ 0,\n\t/* 044  ,  */ 0,\n\t/* 045  -  */ 0,\n\t/* 046  .  */ 0,\n\t/* 047  /  */ 0,\n\t/* 048  0  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 049  1  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 050  2  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 051  3  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 052  4  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 053  5  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 054  6  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 055  7  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 056  8  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 057  9  */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */\n\t/* 058  :  */ 0,\n\t/* 059  ;  */ 0,\n\t/* 060  <  */ 0,\n\t/* 061  =  */ 0,\n\t/* 062  >  */ 0,\n\t/* 063  ?  */ 0,\n\t/* 064  @  */ 0,\n\t/* 065  A  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 066  B  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 067  C  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 068  D  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 069  E  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 070  F  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 071  G  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 072  H  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 073  I  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 074  J  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 075  K  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 076  L  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 077  M  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 078  N  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 079  O  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 080  P  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 081  Q  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 082  R  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 083  S  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 084  T  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 085  U  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 086  V  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 087  W  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 088  X  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 089  Y  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 090  Z  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 091  [  */ 0,\n\t/* 092  \\  */ 0,\n\t/* 093  ]  */ 0,\n\t/* 094  ^  */ 0,\n\t/* 095  _  */ 68, /* CHR_IDENT | CHR_INTRODUCER */\n\t/* 096  `  */ 0,\n\t/* 097  a  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 098  b  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 099  c  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 100  d  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 101  e  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 102  f  */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */\n\t/* 103  g  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 104  h  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 105  i  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 106  j  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 107  k  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 108  l  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 109  m  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 110  n  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 111  o  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 112  p  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 113  q  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 114  r  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 115  s  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 116  t  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 117  u  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 118  v  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 119  w  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 120  x  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 121  y  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 122  z  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 123  {  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 124  |  */ 0,\n\t/* 125  }  */ 5, /* CHR_LETTER | CHR_IDENT */\n\t/* 126  ~  */ 0,\n\t/* 127     */ 0\n};\n\nstatic inline char php_firebird_classes(char idx)\n{\n\tunsigned char uidx = (unsigned char) idx;\n\tif (uidx > 127) return 0;\n\treturn classes_array[uidx];\n}\n\ntypedef enum {\n\tttNone,\n\tttWhite,\n\tttComment,\n\tttBrokenComment,\n\tttString,\n\tttParamMark,\n\tttIdent,\n\tttOther\n} FbTokenType;\n\nstatic FbTokenType php_firebird_get_token(const char** begin, const char* end)\n{\n\tFbTokenType ret = ttNone;\n\tconst char* p = *begin;\n\n\tchar c = *p++;\n\tswitch (c)\n\t{\n\tcase ':':\n\tcase '?':\n\t\tret = ttParamMark;\n\t\tbreak;\n\n\tcase '\\'':\n\tcase '\"':\n\t\twhile (p < end)\n\t\t{\n\t\t\tif (*p++ == c)\n\t\t\t{\n\t\t\t\tret = ttString;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase '/':\n\t\tif (p < end && *p == '*')\n\t\t{\n\t\t\tret = ttBrokenComment;\n\t\t\tp++;\n\t\t\twhile (p < end)\n\t\t\t{\n\t\t\t\tif (*p++ == '*' && p < end && *p == '/')\n\t\t\t\t{\n\t\t\t\t\tp++;\n\t\t\t\t\tret = ttComment;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tret = ttOther;\n\t\t}\n\t\tbreak;\n\n\tcase '-':\n\t\tif (p < end && *p == '-')\n\t\t{\n\t\t\twhile (++p < end)\n\t\t\t{\n\t\t\t\tif (*p == '\\r')\n\t\t\t\t{\n\t\t\t\t\tp++;\n\t\t\t\t\tif (p < end && *p == '\\n')\n\t\t\t\t\t\tp++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse if (*p == '\\n')\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tret = ttComment;\n\t\t}\n\t\telse\n\t\t\tret = ttOther;\n\t\tbreak;\n\n\tdefault:\n\t\tif (php_firebird_classes(c) & CHR_DIGIT)\n\t\t{\n\t\t\twhile (p < end && (php_firebird_classes(*p) & CHR_DIGIT))\n\t\t\t\tp++;\n\t\t\tret = ttOther;\n\t\t}\n\t\telse if (php_firebird_classes(c) & CHR_IDENT)\n\t\t{\n\t\t\twhile (p < end && (php_firebird_classes(*p) & CHR_IDENT))\n\t\t\t\tp++;\n\t\t\tret = ttIdent;\n\t\t}\n\t\telse if (php_firebird_classes(c) & CHR_WHITE)\n\t\t{\n\t\t\twhile (p < end && (php_firebird_classes(*p) & CHR_WHITE))\n\t\t\t\tp++;\n\t\t\tret = ttWhite;\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile (p < end && !(php_firebird_classes(*p) & (CHR_DIGIT | CHR_IDENT | CHR_WHITE)) &&\n\t\t\t\t(*p != '/') && (*p != '-') && (*p != ':') && (*p != '?') &&\n\t\t\t\t(*p != '\\'') && (*p != '\"'))\n\t\t\t{\n\t\t\t\tp++;\n\t\t\t}\n\t\t\tret = ttOther;\n\t\t}\n\t}\n\n\t*begin = p;\n\treturn ret;\n}\n\nstatic int php_firebird_preprocess(const zend_string* sql, char* sql_out, HashTable* named_params)\n{\n\tbool passAsIs = true, execBlock = false;\n\tzend_long pindex = -1;\n\tchar pname[254], ident[253], ident2[253];\n\tunsigned int l;\n\tconst char* p = ZSTR_VAL(sql), * end = ZSTR_VAL(sql) + ZSTR_LEN(sql);\n\tconst char* start = p;\n\tFbTokenType tok = php_firebird_get_token(&p, end);\n\n\tconst char* i = start;\n\twhile (p < end && (tok == ttComment || tok == ttWhite))\n\t{\n\t\ti = p;\n\t\ttok = php_firebird_get_token(&p, end);\n\t}\n\n\tif (p >= end || tok != ttIdent)\n\t{\n\t\t/* Execute statement preprocess SQL error */\n\t\t/* Statement expected */\n\t\treturn 0;\n\t}\n\t/* skip leading comments ?? */\n\tstart = i;\n\tl = p - i;\n\t/* check the length of the identifier */\n\t/* in Firebird 4.0 it is 63 characters, in previous versions 31 bytes */\n\tif (l > 252) {\n\t\treturn 0;\n\t}\n\tstrncpy(ident, i, l);\n\tident[l] = '\\0';\n\tif (!strcasecmp(ident, \"EXECUTE\"))\n\t{\n\t\t/* For EXECUTE PROCEDURE and EXECUTE BLOCK statements, named parameters must be processed. */\n\t\t/* However, in EXECUTE BLOCK this is done in a special way. */\n\t\tconst char* i2 = p;\n\t\ttok = php_firebird_get_token(&p, end);\n\t\twhile (p < end && (tok == ttComment || tok == ttWhite))\n\t\t{\n\t\t\ti2 = p;\n\t\t\ttok = php_firebird_get_token(&p, end);\n\t\t}\n\t\tif (p >= end || tok != ttIdent)\n\t\t{\n\t\t\t/* Execute statement preprocess SQL error */\n\t\t\t/* Statement expected */\n\t\t\treturn 0;\n\t\t}\n\t\tl = p - i2;\n\t\t/* check the length of the identifier */\n\t\t/* in Firebird 4.0 it is 63 characters, in previous versions 31 bytes */\n\t\tif (l > 252) {\n\t\t\treturn 0;\n\t\t}\n\t\tstrncpy(ident2, i2, l);\n\t\tident2[l] = '\\0';\n\t\texecBlock = !strcasecmp(ident2, \"BLOCK\");\n\t\tpassAsIs = false;\n\t}\n\telse\n\t{\n\t\t/* Named parameters must be processed in the INSERT, UPDATE, DELETE, MERGE statements. */\n\t\t/* If CTEs are present in the query, they begin with the WITH keyword. */\n\t\tpassAsIs = strcasecmp(ident, \"INSERT\") && strcasecmp(ident, \"UPDATE\") &&\n\t\t\tstrcasecmp(ident, \"DELETE\") && strcasecmp(ident, \"MERGE\") &&\n\t\t\tstrcasecmp(ident, \"SELECT\") && strcasecmp(ident, \"WITH\");\n\t}\n\n\tif (passAsIs)\n\t{\n\t\tstrcpy(sql_out, ZSTR_VAL(sql));\n\t\treturn 1;\n\t}\n\n\tstrncat(sql_out, start, p - start);\n\n\twhile (p < end)\n\t{\n\t\tstart = p;\n\t\ttok = php_firebird_get_token(&p, end);\n\t\tswitch (tok)\n\t\t{\n\t\tcase ttParamMark:\n\t\t\ttok = php_firebird_get_token(&p, end);\n\t\t\tif (tok == ttIdent /*|| tok == ttString*/)\n\t\t\t{\n\t\t\t\t++pindex;\n\t\t\t\tl = p - start;\n\t\t\t\t/* check the length of the identifier */\n\t\t\t\t/* in Firebird 4.0 it is 63 characters, in previous versions 31 bytes */\n\t\t\t\t/* + symbol \":\" */\n\t\t\t\tif (l > 253) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tstrncpy(pname, start, l);\n\t\t\t\tpname[l] = '\\0';\n\n\t\t\t\tif (named_params) {\n\t\t\t\t\tzval tmp;\n\t\t\t\t\tZVAL_LONG(&tmp, pindex);\n\t\t\t\t\tzend_hash_str_update(named_params, pname, l, &tmp);\n\t\t\t\t}\n\n\t\t\t\tstrcat(sql_out, \"?\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (strncmp(start, \"?\", 1)) {\n\t\t\t\t\t/* Execute statement preprocess SQL error */\n\t\t\t\t\t/* Parameter name expected */\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\t++pindex;\n\t\t\t\tstrncat(sql_out, start, p - start);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase ttIdent:\n\t\t\tif (execBlock)\n\t\t\t{\n\t\t\t\t/* In the EXECUTE BLOCK statement, processing must be */\n\t\t\t\t/* carried out up to the keyword AS. */\n\t\t\t\tl = p - start;\n\t\t\t\t/* check the length of the identifier */\n\t\t\t\t/* in Firebird 4.0 it is 63 characters, in previous versions 31 bytes */\n\t\t\t\tif (l > 252) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tstrncpy(ident, start, l);\n\t\t\t\tident[l] = '\\0';\n\t\t\t\tif (!strcasecmp(ident, \"AS\"))\n\t\t\t\t{\n\t\t\t\t\tstrncat(sql_out, start, end - start);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\t/* TODO Check this is correct? */\n\t\t\tZEND_FALLTHROUGH;\n\n\t\tcase ttWhite:\n\t\tcase ttComment:\n\t\tcase ttString:\n\t\tcase ttOther:\n\t\t\tstrncat(sql_out, start, p - start);\n\t\t\tbreak;\n\n\t\tcase ttBrokenComment:\n\t\t{\n\t\t\t/* Execute statement preprocess SQL error */\n\t\t\t/* Unclosed comment found near ''@1'' */\n\t\t\treturn 0;\n\t\t}\n\t\tbreak;\n\n\n\t\tcase ttNone:\n\t\t\t/* Execute statement preprocess SQL error */\n\t\t\treturn 0;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn 1;\n}\n\n#if FB_API_VER >= 40\n/* set coercing a data type */\nstatic void set_coercing_output_data_types(XSQLDA* sqlda)\n{\n\t/* Data types introduced in Firebird 4.0 are difficult to process using the Firebird Legacy API. */\n\t/* These data types include DECFLOAT(16), DECFLOAT(34), INT128 (NUMERIC/DECIMAL(38, x)). */\n\t/* In any case, at this data types can only be mapped to strings. */\n\t/* This function allows you to ensure minimal performance of queries if they contain columns of the above types. */\n\tunsigned int i;\n\tshort dtype;\n\tshort nullable;\n\tXSQLVAR* var;\n\tunsigned fb_client_version = fb_get_client_version();\n\tunsigned fb_client_major_version = (fb_client_version >> 8) & 0xFF;\n\tfor (i=0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++) {\n\t\tdtype = (var->sqltype & ~1); /* drop flag bit  */\n\t\tnullable = (var->sqltype & 1);\n\t\tswitch(dtype) {\n\t\t\tcase SQL_INT128:\n\t\t\t\tvar->sqltype = SQL_VARYING + nullable;\n\t\t\t\tvar->sqllen = 46;\n\t\t\t\tvar->sqlscale = 0;\n\t\t\t\tbreak;\n\n\t\t\tcase SQL_DEC16:\n\t\t\t\tvar->sqltype = SQL_VARYING + nullable;\n\t\t\t\tvar->sqllen = 24;\n\t\t\t\tbreak;\n\n\t\t\tcase SQL_DEC34:\n\t\t\t\tvar->sqltype = SQL_VARYING + nullable;\n\t\t\t\tvar->sqllen = 43;\n\t\t\t\tbreak;\n\n\t\t\tcase SQL_TIMESTAMP_TZ:\n\t\t\t    if (fb_client_major_version < 4) {\n\t\t\t\t\t/* If the client version is below 4.0, then it is impossible to handle time zones natively, */\n\t\t\t\t\t/* so we convert these types to a string. */\n\t\t\t\t\tvar->sqltype = SQL_VARYING + nullable;\n\t\t\t\t\tvar->sqllen = 58;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase SQL_TIME_TZ:\n\t\t\t\tif (fb_client_major_version < 4) {\n\t\t\t\t\t/* If the client version is below 4.0, then it is impossible to handle time zones natively, */\n\t\t\t\t\t/* so we convert these types to a string. */\n\t\t\t\t\tvar->sqltype = SQL_VARYING + nullable;\n\t\t\t\t\tvar->sqllen = 46;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n#endif\n\n/* map driver specific error message to PDO error */\nvoid php_firebird_set_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *state, const size_t state_len,\n\tconst char *msg, const size_t msg_len) /* {{{ */\n{\n\tpdo_error_type *const error_code = stmt ? &stmt->error_code : &dbh->error_code;\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\tpdo_firebird_error_info *einfo = &H->einfo;\n\tint sqlcode = -999;\n\n\tif (einfo->errmsg) {\n\t\tpefree(einfo->errmsg, dbh->is_persistent);\n\t\teinfo->errmsg = NULL;\n\t\teinfo->errmsg_length = 0;\n\t}\n\n\tif (H->isc_status[0] == 1 && H->isc_status[1] > 0) {\n\t\tchar buf[512];\n\t\tsize_t buf_size = sizeof(buf), read_len = 0;\n\t\tssize_t tmp_len;\n\t\tconst ISC_STATUS *s = H->isc_status;\n\t\tsqlcode = isc_sqlcode(s);\n\n\t\twhile ((buf_size > (read_len + 1)) && (tmp_len = fb_interpret(&buf[read_len], (buf_size - read_len - 1), &s)) && tmp_len > 0) {\n\t\t\tread_len += tmp_len;\n\t\t\tbuf[read_len++] = ' ';\n\t\t}\n\n\t\t/* remove last space */\n\t\tif (read_len) {\n\t\t\tbuf[read_len--] = '\\0';\n\t\t}\n\n\t\teinfo->errmsg_length = read_len;\n\t\teinfo->errmsg = pestrndup(buf, read_len, dbh->is_persistent);\n\n\t\tchar sqlstate[sizeof(pdo_error_type)];\n\t\tfb_sqlstate(sqlstate, H->isc_status);\n\t\tif (strlen(sqlstate) < sizeof(pdo_error_type)) {\n\t\t\tstrcpy(*error_code, sqlstate);\n\t\t\tgoto end;\n\t\t}\n\t} else if (msg && msg_len) {\n\t\teinfo->errmsg_length = msg_len;\n\t\teinfo->errmsg = pestrndup(msg, einfo->errmsg_length, dbh->is_persistent);\n\t}\n\n\tif (state && state_len && state_len < sizeof(pdo_error_type)) {\n\t\tmemcpy(*error_code, state, state_len + 1);\n\t} else {\n\t\tmemcpy(*error_code, \"HY000\", sizeof(\"HY000\"));\n\t}\n\nend:\n\teinfo->sqlcode = sqlcode;\n\tif (!dbh->methods) {\n\t\tpdo_throw_exception(0, einfo->errmsg, error_code);\n\t}\n}\n/* }}} */\n\n/* called by PDO to close a db handle */\nstatic void firebird_handle_closer(pdo_dbh_t *dbh) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\n\tif (H->tr) {\n\t\tif (dbh->auto_commit) {\n\t\t\tphp_firebird_commit_transaction(dbh, /* retain */ false);\n\t\t} else {\n\t\t\tphp_firebird_rollback_transaction(dbh);\n\t\t}\n\t}\n\tH->in_manually_txn = false;\n\n\t/* isc_detach_database returns 0 on success, 1 on failure. */\n\tif (H->db && isc_detach_database(H->isc_status, &H->db)) {\n\t\tphp_firebird_error(dbh);\n\t}\n\n\tif (H->date_format) {\n\t\tpefree(H->date_format, dbh->is_persistent);\n\t}\n\tif (H->time_format) {\n\t\tpefree(H->time_format, dbh->is_persistent);\n\t}\n\tif (H->timestamp_format) {\n\t\tpefree(H->timestamp_format, dbh->is_persistent);\n\t}\n\n\tif (H->einfo.errmsg) {\n\t\tpefree(H->einfo.errmsg, dbh->is_persistent);\n\t\tH->einfo.errmsg = NULL;\n\t}\n\n\tpefree(H, dbh->is_persistent);\n}\n/* }}} */\n\n/* called by PDO to prepare an SQL query */\nstatic bool firebird_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, /* {{{ */\n\tpdo_stmt_t *stmt, zval *driver_options)\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\tpdo_firebird_stmt *S = NULL;\n\tHashTable *np;\n\n\tdo {\n\t\tisc_stmt_handle s = PDO_FIREBIRD_HANDLE_INITIALIZER;\n\t\tXSQLDA num_sqlda;\n\t\tstatic char const info[] = { isc_info_sql_stmt_type };\n\t\tchar result[8];\n\n\t\tnum_sqlda.version = PDO_FB_SQLDA_VERSION;\n\t\tnum_sqlda.sqln = 1;\n\n\t\tALLOC_HASHTABLE(np);\n\t\tzend_hash_init(np, 8, NULL, NULL, 0);\n\n\t\t/* allocate and prepare statement */\n\t\tif (!php_firebird_alloc_prepare_stmt(dbh, sql, &num_sqlda, &s, np)) {\n\t\t\tbreak;\n\t\t}\n\n\t\t/* allocate a statement handle struct of the right size (struct out_sqlda is inlined) */\n\t\tS = ecalloc(1, sizeof(*S)-sizeof(XSQLDA) + XSQLDA_LENGTH(num_sqlda.sqld));\n\t\tS->H = H;\n\t\tS->stmt = s;\n\t\tS->out_sqlda.version = PDO_FB_SQLDA_VERSION;\n\t\tS->out_sqlda.sqln = stmt->column_count = num_sqlda.sqld;\n\t\tS->named_params = np;\n\n\t\t/* determine the statement type */\n\t\tif (isc_dsql_sql_info(H->isc_status, &s, sizeof(info), const_cast(info), sizeof(result),\n\t\t\t\tresult)) {\n\t\t\tbreak;\n\t\t}\n\t\tS->statement_type = result[3];\n\n\t\t/* fill the output sqlda with information about the prepared query */\n\t\tif (isc_dsql_describe(H->isc_status, &s, PDO_FB_SQLDA_VERSION, &S->out_sqlda)) {\n\t\t\tphp_firebird_error(dbh);\n\t\t\tbreak;\n\t\t}\n\n#if FB_API_VER >= 40\n\t\t/* set coercing a data type */\n\t\tset_coercing_output_data_types(&S->out_sqlda);\n#endif\n\n\t\t/* allocate the input descriptors */\n\t\tif (isc_dsql_describe_bind(H->isc_status, &s, PDO_FB_SQLDA_VERSION, &num_sqlda)) {\n\t\t\tbreak;\n\t\t}\n\n\t\tif (num_sqlda.sqld) {\n\t\t\tS->in_sqlda = ecalloc(1,XSQLDA_LENGTH(num_sqlda.sqld));\n\t\t\tS->in_sqlda->version = PDO_FB_SQLDA_VERSION;\n\t\t\tS->in_sqlda->sqln = num_sqlda.sqld;\n\n\t\t\tif (isc_dsql_describe_bind(H->isc_status, &s, PDO_FB_SQLDA_VERSION, S->in_sqlda)) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* make all parameters nullable */\n\t\t\tunsigned int i;\n\t\t\tXSQLVAR* var;\n\t\t\tfor (i = 0, var = S->in_sqlda->sqlvar; i < S->in_sqlda->sqld; i++, var++) {\n\t\t\t\t/* The low bit of sqltype indicates that the parameter can take a NULL value */\n\t\t\t\tvar->sqltype |= 1;\n\t\t\t}\n\t\t}\n\n\t\tstmt->driver_data = S;\n\t\tstmt->methods = &swoole_firebird_stmt_methods;\n\t\tstmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL;\n\n\t\treturn true;\n\n\t} while (0);\n\n\tphp_firebird_error(dbh);\n\n\tzend_hash_destroy(np);\n\tFREE_HASHTABLE(np);\n\n\tif (S) {\n\t\tif (S->in_sqlda) {\n\t\t\tefree(S->in_sqlda);\n\t\t}\n\t\tefree(S);\n\t}\n\n\treturn false;\n}\n/* }}} */\n\n/* called by PDO to execute a statement that doesn't produce a result set */\nstatic zend_long firebird_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\tisc_stmt_handle stmt = PDO_FIREBIRD_HANDLE_INITIALIZER;\n\tstatic char const info_count[] = { isc_info_sql_records };\n\tchar result[64];\n\tint ret = 0;\n\tXSQLDA in_sqlda, out_sqlda;\n\n\t/* TODO no placeholders in exec() for now */\n\tin_sqlda.version = out_sqlda.version = PDO_FB_SQLDA_VERSION;\n\tin_sqlda.sqld = out_sqlda.sqld = 0;\n\tout_sqlda.sqln = 1;\n\n\t/* allocate and prepare statement */\n\tif (!php_firebird_alloc_prepare_stmt(dbh, sql, &out_sqlda, &stmt, 0)) {\n\t\treturn -1;\n\t}\n\n\t/* execute the statement */\n\tif (isc_dsql_execute2(H->isc_status, &H->tr, &stmt, PDO_FB_SQLDA_VERSION, &in_sqlda, &out_sqlda)) {\n\t\tphp_firebird_error(dbh);\n\t\tret = -1;\n\t\tgoto free_statement;\n\t}\n\n\t/* find out how many rows were affected */\n\tif (isc_dsql_sql_info(H->isc_status, &stmt, sizeof(info_count), const_cast(info_count),\n\t\t\tsizeof(result),\tresult)) {\n\t\tphp_firebird_error(dbh);\n\t\tret = -1;\n\t\tgoto free_statement;\n\t}\n\n\tif (result[0] == isc_info_sql_records) {\n\t\tunsigned i = 3, result_size = isc_vax_integer(&result[1],2);\n\n\t\tif (result_size > sizeof(result)) {\n\t\t\tret = -1;\n\t\t\tgoto free_statement;\n\t\t}\n\t\twhile (result[i] != isc_info_end && i < result_size) {\n\t\t\tshort len = (short)isc_vax_integer(&result[i+1],2);\n\t\t\t/* bail out on bad len */\n\t\t\tif (len != 1 && len != 2 && len != 4) {\n\t\t\t\tret = -1;\n\t\t\t\tgoto free_statement;\n\t\t\t}\n\t\t\tif (result[i] != isc_info_req_select_count) {\n\t\t\t\tret += isc_vax_integer(&result[i+3],len);\n\t\t\t}\n\t\t\ti += len+3;\n\t\t}\n\t}\n\n\tif (dbh->auto_commit && !H->in_manually_txn) {\n\t\tif (!php_firebird_commit_transaction(dbh, /* retain */ true)) {\n\t\t\tret = -1;\n\t\t}\n\t}\n\nfree_statement:\n\n\tif (isc_dsql_free_statement(H->isc_status, &stmt, DSQL_drop)) {\n\t\tphp_firebird_error(dbh);\n\t}\n\n\treturn ret;\n}\n/* }}} */\n\n/* called by the PDO SQL parser to add quotes to values that are copied into SQL */\nstatic zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype)\n{\n\tsize_t qcount = 0;\n\tchar const *co, *l, *r;\n\tchar *c;\n\tsize_t quotedlen;\n\tzend_string *quoted_str;\n\n\tif (ZSTR_LEN(unquoted) == 0) {\n\t\treturn ZSTR_INIT_LITERAL(\"''\", 0);\n\t}\n\n\t/* Firebird only requires single quotes to be doubled if string lengths are used */\n\t/* count the number of ' characters */\n\tfor (co = ZSTR_VAL(unquoted); (co = strchr(co,'\\'')); qcount++, co++);\n\n\tif (UNEXPECTED(ZSTR_LEN(unquoted) + 2 > ZSTR_MAX_LEN - qcount)) {\n\t\treturn NULL;\n\t}\n\n\tquotedlen = ZSTR_LEN(unquoted) + qcount + 2;\n\tquoted_str = zend_string_alloc(quotedlen, 0);\n\tc = ZSTR_VAL(quoted_str);\n\t*c++ = '\\'';\n\n\t/* foreach (chunk that ends in a quote) */\n\tfor (l = ZSTR_VAL(unquoted); (r = strchr(l,'\\'')); l = r+1) {\n\t\tstrncpy(c, l, r-l+1);\n\t\tc += (r-l+1);\n\t\t/* add the second quote */\n\t\t*c++ = '\\'';\n\t}\n\n\t/* copy the remainder */\n\tstrncpy(c, l, quotedlen-(c-ZSTR_VAL(quoted_str))-1);\n\tZSTR_VAL(quoted_str)[quotedlen-1] = '\\'';\n\tZSTR_VAL(quoted_str)[quotedlen]   = '\\0';\n\n\treturn quoted_str;\n}\n/* }}} */\n\n/* php_firebird_begin_transaction */\nstatic bool php_firebird_begin_transaction(pdo_dbh_t *dbh, bool is_auto_commit_txn) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\n\t/* isc_xxx are all 1 byte. */\n\tchar tpb[4] = { isc_tpb_version3 };\n\tsize_t tpb_size;\n\n\t/* access mode. writable or readonly */\n\ttpb[1] = H->is_writable_txn ? isc_tpb_write : isc_tpb_read;\n\n\tif (is_auto_commit_txn) {\n\t\t/*\n\t\t * In autocommit mode, we need to always read the latest information, so we set `read committed`.\n\t\t */\n\t\ttpb[2] = isc_tpb_read_committed;\n\t\t/* Ignore indeterminate data from other transactions. This option only required with `read committed`. */\n\t\ttpb[3] = isc_tpb_rec_version;\n\t\ttpb_size = 4;\n\t} else {\n\t\tswitch (H->txn_isolation_level) {\n\t\t\t/*\n\t\t\t* firebird's `read committed` has the option to wait until other transactions\n\t\t\t* commit or rollback if there is indeterminate data.\n\t\t\t* Introducing too many configuration values at once can cause confusion, so\n\t\t\t* we don't support in PDO that feature yet.\n\t\t\t*/\n\t\t\tcase PDO_FB_READ_COMMITTED:\n\t\t\t\ttpb[2] = isc_tpb_read_committed;\n\t\t\t\t/* Ignore indeterminate data from other transactions. This option only required with `read committed`. */\n\t\t\t\ttpb[3] = isc_tpb_rec_version;\n\t\t\t\ttpb_size = 4;\n\t\t\t\tbreak;\n\n\t\t\tcase PDO_FB_SERIALIZABLE:\n\t\t\t\ttpb[2] = isc_tpb_consistency;\n\t\t\t\ttpb_size = 3;\n\t\t\t\tbreak;\n\n\t\t\tcase PDO_FB_REPEATABLE_READ:\n\t\t\tdefault:\n\t\t\t\ttpb[2] = isc_tpb_concurrency;\n\t\t\t\ttpb_size = 3;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (isc_start_transaction(H->isc_status, &H->tr, 1, &H->db, tpb_size, tpb)) {\n\t\tphp_firebird_error(dbh);\n\t\treturn false;\n\t}\n\treturn true;\n}\n/* }}} */\n\n/* called by PDO to start a transaction */\nstatic bool firebird_handle_manually_begin(pdo_dbh_t *dbh) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\n\t/**\n\t * If in autocommit mode and in transaction, we will need to close the transaction once.\n\t */\n\tif (dbh->auto_commit && H->tr) {\n\t\tif (!php_firebird_commit_transaction(dbh, /* retain */ false)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (!php_firebird_begin_transaction(dbh, /* auto commit mode */ false)) {\n\t\treturn false;\n\t}\n\tH->in_manually_txn = true;\n\treturn true;\n}\n/* }}} */\n\n/* php_firebird_commit_transaction */\nbool php_firebird_commit_transaction(pdo_dbh_t *dbh, bool retain) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\n\t/**\n\t * `retaining` keeps the transaction open without closing it.\n\t *\n\t * firebird needs to always have a transaction open to emulate autocommit mode,\n\t * and in autocommit mode it keeps the transaction open.\n\t *\n\t * Same as close and then begin again, but use retain to save overhead.\n\t */\n\tif (retain) {\n\t\tif (isc_commit_retaining(H->isc_status, &H->tr)) {\n\t\t\tphp_firebird_error(dbh);\n\t\t\treturn false;\n\t\t}\n\t} else {\n\t\tif (isc_commit_transaction(H->isc_status, &H->tr)) {\n\t\t\tphp_firebird_error(dbh);\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n/* }}} */\n\n/* called by PDO to commit a transaction */\nstatic bool firebird_handle_manually_commit(pdo_dbh_t *dbh) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\tif (!php_firebird_commit_transaction(dbh, /*release*/ false)) {\n\t\treturn false;\n\t}\n\n\t/**\n\t * If in autocommit mode, begin the transaction again\n\t * Reopen instead of retain because isolation level may change\n\t */\n\tif (dbh->auto_commit) {\n\t\tif (!php_firebird_begin_transaction(dbh, /* auto commit mode */ true)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\tH->in_manually_txn = false;\n\treturn true;\n}\n/* }}} */\n\n/* php_firebird_rollback_transaction */\nstatic bool php_firebird_rollback_transaction(pdo_dbh_t *dbh) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\n\tif (isc_rollback_transaction(H->isc_status, &H->tr)) {\n\t\tphp_firebird_error(dbh);\n\t\treturn false;\n\t}\n\treturn true;\n}\n/* }}} */\n\n/* called by PDO to rollback a transaction */\nstatic bool firebird_handle_manually_rollback(pdo_dbh_t *dbh) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\n\tif (!php_firebird_rollback_transaction(dbh)) {\n\t\treturn false;\n\t}\n\n\t/**\n\t * If in autocommit mode, begin the transaction again\n\t * Reopen instead of retain because isolation level may change\n\t */\n\tif (dbh->auto_commit) {\n\t\tif (!php_firebird_begin_transaction(dbh, /* auto commit mode */ true)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\tH->in_manually_txn = false;\n\treturn true;\n}\n/* }}} */\n\n/* used by prepare and exec to allocate a statement handle and prepare the SQL */\nstatic int php_firebird_alloc_prepare_stmt(pdo_dbh_t *dbh, const zend_string *sql,\n\tXSQLDA *out_sqlda, isc_stmt_handle *s, HashTable *named_params)\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\tchar *new_sql;\n\n\t/* allocate the statement */\n\tif (isc_dsql_allocate_statement(H->isc_status, &H->db, s)) {\n\t\tphp_firebird_error(dbh);\n\t\treturn 0;\n\t}\n\n\t/* in order to support named params, which Firebird itself doesn't,\n\t   we need to replace :foo by ?, and store the name we just replaced */\n\tnew_sql = emalloc(ZSTR_LEN(sql)+1);\n\tnew_sql[0] = '\\0';\n\tif (!php_firebird_preprocess(sql, new_sql, named_params)) {\n\t\tphp_firebird_error_with_info(dbh, \"07000\", strlen(\"07000\"), NULL, 0);\n\t\tefree(new_sql);\n\t\treturn 0;\n\t}\n\n\t/* prepare the statement */\n\tif (isc_dsql_prepare(H->isc_status, &H->tr, s, 0, new_sql, H->sql_dialect, out_sqlda)) {\n\t\tphp_firebird_error(dbh);\n\t\tefree(new_sql);\n\t\treturn 0;\n\t}\n\n\tefree(new_sql);\n\treturn 1;\n}\n\n/* called by PDO to set a driver-specific dbh attribute */\nstatic bool pdo_firebird_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\tbool bval;\n\tzend_long lval;\n\n\tswitch (attr) {\n\t\tcase PDO_ATTR_AUTOCOMMIT:\n\t\t\t{\n\t\t\t\tif (!pdo_get_bool_param(&bval, val)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (H->in_manually_txn) {\n\t\t\t\t\t/* change auto commit mode with an open transaction is illegal, because\n\t\t\t\t\t\twe won't know what to do with it */\n\t\t\t\t\tpdo_raise_impl_error(dbh, NULL, \"HY000\", \"Cannot change autocommit mode while a transaction is already open\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t/* ignore if the new value equals the old one */\n\t\t\t\tif (dbh->auto_commit ^ bval) {\n\t\t\t\t\tif (bval) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * change to auto commit mode.\n\t\t\t\t\t\t * If the transaction is not started, start it.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tif (!H->tr) {\n\t\t\t\t\t\t\tif (!php_firebird_begin_transaction(dbh, /* auto commit mode */ true)) {\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * change to not auto commit mode.\n\t\t\t\t\t\t * close the transaction if exists.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tif (H->tr) {\n\t\t\t\t\t\t\tif (!php_firebird_commit_transaction(dbh, /* retain */ false)) {\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdbh->auto_commit = bval;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\n\t\tcase PDO_ATTR_FETCH_TABLE_NAMES:\n\t\t\tif (!pdo_get_bool_param(&bval, val)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tH->fetch_table_names = bval;\n\t\t\treturn true;\n\n\t\tcase PDO_FB_ATTR_DATE_FORMAT:\n\t\t\t{\n\t\t\t\tzend_string *str = zval_try_get_string(val);\n\t\t\t\tif (UNEXPECTED(!str)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (H->date_format) {\n\t\t\t\t\tpefree(H->date_format, dbh->is_persistent);\n\t\t\t\t\tH->date_format = NULL;\n\t\t\t\t}\n\t\t\t\tH->date_format = pestrndup(ZSTR_VAL(str), ZSTR_LEN(str),dbh->is_persistent);\n\t\t\t\tzend_string_release_ex(str, 0);\n\t\t\t}\n\t\t\treturn true;\n\n\t\tcase PDO_FB_ATTR_TIME_FORMAT:\n\t\t\t{\n\t\t\t\tzend_string *str = zval_try_get_string(val);\n\t\t\t\tif (UNEXPECTED(!str)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (H->time_format) {\n\t\t\t\t\tpefree(H->time_format, dbh->is_persistent);\n\t\t\t\t\tH->time_format = NULL;\n\t\t\t\t}\n\t\t\t\tH->time_format = pestrndup(ZSTR_VAL(str), ZSTR_LEN(str),dbh->is_persistent);\n\t\t\t\tzend_string_release_ex(str, 0);\n\t\t\t}\n\t\t\treturn true;\n\n\t\tcase PDO_FB_ATTR_TIMESTAMP_FORMAT:\n\t\t\t{\n\t\t\t\tzend_string *str = zval_try_get_string(val);\n\t\t\t\tif (UNEXPECTED(!str)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (H->timestamp_format) {\n\t\t\t\t\tpefree(H->timestamp_format, dbh->is_persistent);\n\t\t\t\t\tH->timestamp_format = NULL;\n\t\t\t\t}\n\t\t\t\tH->timestamp_format = pestrndup(ZSTR_VAL(str), ZSTR_LEN(str),dbh->is_persistent);\n\t\t\t\tzend_string_release_ex(str, 0);\n\t\t\t}\n\t\t\treturn true;\n\n\t\tcase PDO_FB_TRANSACTION_ISOLATION_LEVEL:\n\t\t\t{\n\t\t\t\tif (!pdo_get_long_param(&lval, val)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (H->in_manually_txn) {\n\t\t\t\t\tpdo_raise_impl_error(dbh, NULL, \"HY000\", \"Cannot change transaction isolation level while a transaction is already open\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t/* ignore if the new value equals the old one */\n\t\t\t\tif (H->txn_isolation_level != lval) {\n\t\t\t\t\tif (lval == PDO_FB_READ_COMMITTED ||\n\t\t\t\t\t\tlval == PDO_FB_REPEATABLE_READ ||\n\t\t\t\t\t\tlval == PDO_FB_SERIALIZABLE\n\t\t\t\t\t) {\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Autocommit mode is always read-committed, so this setting is used the next time\n\t\t\t\t\t\t * a manual transaction starts. Therefore, there is no need to immediately reopen the transaction.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tH->txn_isolation_level = lval;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tzend_value_error(\"Pdo\\\\Firebird::TRANSACTION_ISOLATION_LEVEL must be a valid transaction isolation level \"\n\t\t\t\t\t\t\t\"(Pdo\\\\Firebird::READ_COMMITTED, Pdo\\\\Firebird::REPEATABLE_READ, or Pdo\\\\Firebird::SERIALIZABLE)\");\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\n\t\tcase PDO_FB_WRITABLE_TRANSACTION:\n\t\t\t{\n\t\t\t\tif (!pdo_get_bool_param(&bval, val)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (H->in_manually_txn) {\n\t\t\t\t\tpdo_raise_impl_error(dbh, NULL, \"HY000\", \"Cannot change access mode while a transaction is already open\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t/* ignore if the new value equals the old one */\n\t\t\t\tif (H->is_writable_txn != bval) {\n\t\t\t\t\tH->is_writable_txn = bval;\n\t\t\t\t\tif (dbh->auto_commit) {\n\t\t\t\t\t\tif (H->tr) {\n\t\t\t\t\t\t\tif (!php_firebird_commit_transaction(dbh, /* retain */ false)) {\n\t\t\t\t\t\t\t\t/* In case of error, revert the setting */\n\t\t\t\t\t\t\t\tH->is_writable_txn = !bval;\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!php_firebird_begin_transaction(dbh, /* auto commit mode */ true)) {\n\t\t\t\t\t\t\t/* In case of error, revert the setting */\n\t\t\t\t\t\t\tH->is_writable_txn = !bval;\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n/* }}} */\n\n#define INFO_BUF_LEN 512\n\n/* callback to used to report database server info */\nstatic void php_firebird_info_cb(void *arg, char const *s) /* {{{ */\n{\n\tif (arg) {\n\t\tif (*(char*)arg) { /* second call */\n\t\t\tstrlcat(arg, \" \", INFO_BUF_LEN);\n\t\t}\n\t\tstrlcat(arg, s, INFO_BUF_LEN);\n\t}\n}\n/* }}} */\n\n/* called by PDO to get a driver-specific dbh attribute */\nstatic int pdo_firebird_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\n\tswitch (attr) {\n\t\tchar tmp[INFO_BUF_LEN];\n\n\t\tcase PDO_ATTR_AUTOCOMMIT:\n\t\t\tZVAL_BOOL(val,dbh->auto_commit);\n\t\t\treturn 1;\n\n\t\tcase PDO_ATTR_CONNECTION_STATUS:\n\t\t\tZVAL_BOOL(val, !isc_version(&H->db, php_firebird_info_cb, NULL));\n\t\t\treturn 1;\n\n\t\tcase PDO_ATTR_CLIENT_VERSION:\n\t\t\tisc_get_client_version(tmp);\n\t\t\tZVAL_STRING(val, tmp);\n\t\t\treturn 1;\n\n\t\tcase PDO_ATTR_SERVER_VERSION:\n\t\tcase PDO_ATTR_SERVER_INFO:\n\t\t\t*tmp = 0;\n\n\t\t\tif (!isc_version(&H->db, php_firebird_info_cb, (void*)tmp)) {\n\t\t\t\tZVAL_STRING(val, tmp);\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\treturn -1;\n\n\t\tcase PDO_ATTR_FETCH_TABLE_NAMES:\n\t\t\tZVAL_BOOL(val, H->fetch_table_names);\n\t\t\treturn 1;\n\n\t\tcase PDO_FB_ATTR_DATE_FORMAT:\n\t\t\tZVAL_STRING(val, H->date_format ? H->date_format : PDO_FB_DEF_DATE_FMT);\n\t\t\treturn 1;\n\n\t\tcase PDO_FB_ATTR_TIME_FORMAT:\n\t\t\tZVAL_STRING(val, H->time_format ? H->time_format : PDO_FB_DEF_TIME_FMT);\n\t\t\treturn 1;\n\n\t\tcase PDO_FB_ATTR_TIMESTAMP_FORMAT:\n\t\t\tZVAL_STRING(val, H->timestamp_format ? H->timestamp_format : PDO_FB_DEF_TIMESTAMP_FMT);\n\t\t\treturn 1;\n\n\t\tcase PDO_FB_TRANSACTION_ISOLATION_LEVEL:\n\t\t\tZVAL_LONG(val, H->txn_isolation_level);\n\t\t\treturn 1;\n\n\t\tcase PDO_FB_WRITABLE_TRANSACTION:\n\t\t\tZVAL_BOOL(val, H->is_writable_txn);\n\t\t\treturn 1;\n\t}\n\treturn 0;\n}\n/* }}} */\n\n/* called by PDO to check liveness */\nstatic zend_result pdo_firebird_check_liveness(pdo_dbh_t *dbh) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\n\t/* fb_ping return 0 if the connection is alive */\n\treturn fb_ping(H->isc_status, &H->db) ? FAILURE : SUCCESS;\n}\n/* }}} */\n\n/* called by PDO to retrieve driver-specific information about an error that has occurred */\nstatic void pdo_firebird_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */\n{\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\tif (H->einfo.sqlcode != IS_NULL) {\n\t\tadd_next_index_long(info, H->einfo.sqlcode);\n\t}\n\tif (H->einfo.errmsg && H->einfo.errmsg_length) {\n\t\tadd_next_index_stringl(info, H->einfo.errmsg, H->einfo.errmsg_length);\n\t}\n}\n/* }}} */\n\n/* {{{ firebird_in_manually_transaction */\nstatic bool pdo_firebird_in_manually_transaction(pdo_dbh_t *dbh)\n{\n\t/**\n\t * we can tell if a transaction exists now by checking H->tr,\n\t * but which will always be true in autocommit mode.\n\t * So this function checks if there is currently a \"manually begun transaction\".\n\t */\n\tpdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;\n\treturn H->in_manually_txn;\n}\n/* }}} */\n\nstatic const struct pdo_dbh_methods firebird_methods = { /* {{{ */\n\tfirebird_handle_closer,\n\tfirebird_handle_preparer,\n\tfirebird_handle_doer,\n\tfirebird_handle_quoter,\n\tfirebird_handle_manually_begin,\n\tfirebird_handle_manually_commit,\n\tfirebird_handle_manually_rollback,\n\tpdo_firebird_set_attribute,\n\tNULL, /* last_id not supported */\n\tpdo_firebird_fetch_error_func,\n\tpdo_firebird_get_attribute,\n\tpdo_firebird_check_liveness,\n\tNULL, /* get driver methods */\n\tNULL, /* request shutdown */\n\tpdo_firebird_in_manually_transaction,\n\tNULL, /* get gc */\n\tNULL /* scanner */\n};\n/* }}} */\n\n/* the driver-specific PDO handle constructor */\nstatic int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */\n{\n\tstruct pdo_data_src_parser vars[] = {\n\t\t{ \"dbname\", NULL, 0 },\n\t\t{ \"charset\",  NULL,\t0 },\n\t\t{ \"role\", NULL,\t0 },\n\t\t{ \"dialect\", \"3\", 0 },\n\t\t{ \"user\", NULL, 0 },\n\t\t{ \"password\", NULL, 0 }\n\t};\n\tint i, ret = 0;\n\tshort buf_len = 256, dpb_len;\n\n\tpdo_firebird_db_handle *H = dbh->driver_data = pecalloc(1,sizeof(*H),dbh->is_persistent);\n\n\tphp_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 6);\n\n\tif (!dbh->username && vars[4].optval) {\n\t\tdbh->username = pestrdup(vars[4].optval, dbh->is_persistent);\n\t}\n\n\tif (!dbh->password && vars[5].optval) {\n\t\tdbh->password = pestrdup(vars[5].optval, dbh->is_persistent);\n\t}\n\n\tH->in_manually_txn = false;\n\tH->is_writable_txn = pdo_attr_lval(driver_options, PDO_FB_WRITABLE_TRANSACTION, 1);\n\tzend_long txn_isolation_level = pdo_attr_lval(driver_options, PDO_FB_TRANSACTION_ISOLATION_LEVEL, PDO_FB_REPEATABLE_READ);\n\tif (txn_isolation_level == PDO_FB_READ_COMMITTED ||\n\t\ttxn_isolation_level == PDO_FB_REPEATABLE_READ ||\n\t\ttxn_isolation_level == PDO_FB_SERIALIZABLE\n\t) {\n\t\tH->txn_isolation_level = txn_isolation_level;\n\t} else {\n\t\tzend_value_error(\"Pdo\\\\Firebird::TRANSACTION_ISOLATION_LEVEL must be a valid transaction isolation level \"\n\t\t\t\"(Pdo\\\\Firebird::READ_COMMITTED, Pdo\\\\Firebird::REPEATABLE_READ, or Pdo\\\\Firebird::SERIALIZABLE)\");\n\t\tret = 0;\n\t}\n\n\tdo {\n\t\tstatic char const dpb_flags[] = {\n\t\t\tisc_dpb_user_name, isc_dpb_password, isc_dpb_lc_ctype, isc_dpb_sql_role_name };\n\t\tchar const *dpb_values[] = { dbh->username, dbh->password, vars[1].optval, vars[2].optval };\n\t\tchar dpb_buffer[256] = { isc_dpb_version1 }, *dpb;\n\n\t\tdpb = dpb_buffer + 1;\n\n\t\t/* loop through all the provided arguments and set dpb fields accordingly */\n\t\tfor (i = 0; i < sizeof(dpb_flags); ++i) {\n\t\t\tif (dpb_values[i] && buf_len > 0) {\n\t\t\t\tdpb_len = slprintf(dpb, buf_len, \"%c%c%s\", dpb_flags[i], (unsigned char)strlen(dpb_values[i]),\n\t\t\t\t\tdpb_values[i]);\n\t\t\t\tdpb += dpb_len;\n\t\t\t\tbuf_len -= dpb_len;\n\t\t\t}\n\t\t}\n\n\t\tH->sql_dialect = PDO_FB_DIALECT;\n\t\tif (vars[3].optval) {\n\t\t\tH->sql_dialect = atoi(vars[3].optval);\n\t\t}\n\n\t\t/* fire it up baby! */\n\t\tif (isc_attach_database(H->isc_status, 0, vars[0].optval, &H->db,(short)(dpb-dpb_buffer), dpb_buffer)) {\n\t\t\tbreak;\n\t\t}\n\n\t\tdbh->methods = &firebird_methods;\n\t\tdbh->native_case = PDO_CASE_UPPER;\n\t\tdbh->alloc_own_columns = 1;\n\n\t\tret = 1;\n\n\t} while (0);\n\n\tfor (i = 0; i < sizeof(vars)/sizeof(vars[0]); ++i) {\n\t\tif (vars[i].freeme) {\n\t\t\tefree(vars[i].optval);\n\t\t}\n\t}\n\n\tif (!dbh->methods) {\n\t\tchar errmsg[512];\n\t\tconst ISC_STATUS *s = H->isc_status;\n\t\tfb_interpret(errmsg, sizeof(errmsg),&s);\n\t\tzend_throw_exception_ex(php_pdo_get_exception(), H->isc_status[1], \"SQLSTATE[%s] [%\" PRIiPTR \"] %s\",\n\t\t\t\t\"HY000\", H->isc_status[1], errmsg);\n\t}\n\n\tif (ret && dbh->auto_commit && !H->tr) {\n\t\tret = php_firebird_begin_transaction(dbh, /* auto commit mode */ true);\n\t}\n\n\tif (!ret) {\n\t\tfirebird_handle_closer(dbh);\n\t}\n\n\treturn ret;\n}\n/* }}} */\n\n\nconst pdo_driver_t swoole_pdo_firebird_driver = { /* {{{ */\n\tPDO_DRIVER_HEADER(firebird),\n\tpdo_firebird_handle_factory\n};\n/* }}} */\n\n#endif\n"
  },
  {
    "path": "thirdparty/php85/pdo_firebird/firebird_statement.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Ard Biesheuvel <abies@php.net>                               |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_FIREBIRD_HOOK\n#include \"php_swoole_firebird.h\"\n\n#if PHP_VERSION_ID >= 80500\n\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"ext/pdo/php_pdo.h\"\n#include \"ext/pdo/php_pdo_driver.h\"\n#include \"php_pdo_firebird_int.h\"\n#include \"pdo_firebird_utils.h\"\n\n#include <time.h>\n\n#define READ_AND_RETURN_USING_MEMCPY(type, sqldata) do { \\\n\t\ttype ret; \\\n\t\tmemcpy(&ret, sqldata, sizeof(ret)); \\\n\t\treturn ret; \\\n\t} while (0);\n\nstatic zend_always_inline ISC_INT64 php_get_isc_int64_from_sqldata(const ISC_SCHAR *sqldata)\n{\n\tREAD_AND_RETURN_USING_MEMCPY(ISC_INT64, sqldata);\n}\n\nstatic zend_always_inline ISC_LONG php_get_isc_long_from_sqldata(const ISC_SCHAR *sqldata)\n{\n\tREAD_AND_RETURN_USING_MEMCPY(ISC_LONG, sqldata);\n}\n\nstatic zend_always_inline double php_get_double_from_sqldata(const ISC_SCHAR *sqldata)\n{\n\tREAD_AND_RETURN_USING_MEMCPY(double, sqldata);\n}\n\nstatic zend_always_inline float php_get_float_from_sqldata(const ISC_SCHAR *sqldata)\n{\n\tREAD_AND_RETURN_USING_MEMCPY(float, sqldata);\n}\n\nstatic zend_always_inline ISC_TIMESTAMP php_get_isc_timestamp_from_sqldata(const ISC_SCHAR *sqldata)\n{\n\tREAD_AND_RETURN_USING_MEMCPY(ISC_TIMESTAMP, sqldata);\n}\n\nstatic zend_always_inline ISC_QUAD php_get_isc_quad_from_sqldata(const ISC_SCHAR *sqldata)\n{\n\tREAD_AND_RETURN_USING_MEMCPY(ISC_QUAD, sqldata);\n}\n\n#if FB_API_VER >= 40\n\nstatic zend_always_inline ISC_TIME_TZ php_get_isc_time_tz_from_sqldata(const ISC_SCHAR *sqldata)\n{\n\tREAD_AND_RETURN_USING_MEMCPY(ISC_TIME_TZ, sqldata);\n}\n\nstatic zend_always_inline ISC_TIMESTAMP_TZ php_get_isc_timestamp_tz_from_sqldata(const ISC_SCHAR *sqldata)\n{\n\tREAD_AND_RETURN_USING_MEMCPY(ISC_TIMESTAMP_TZ, sqldata);\n}\n\n/* fetch formatted time with time zone */\nstatic int get_formatted_time_tz(pdo_stmt_t *stmt, const ISC_TIME_TZ* timeTz, zval *result)\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tunsigned hours = 0, minutes = 0, seconds = 0, fractions = 0;\n\tchar timeZoneBuffer[40] = {0};\n\tchar *fmt;\n\tstruct tm t;\n\tISC_TIME time;\n\tchar timeBuf[80] = {0};\n\tif (fb_decode_time_tz(S->H->isc_status, timeTz, &hours, &minutes, &seconds, &fractions, sizeof(timeZoneBuffer), timeZoneBuffer)) {\n\t\treturn 1;\n\t}\n\ttime = fb_encode_time(hours, minutes, seconds, fractions);\n\tisc_decode_sql_time(&time, &t);\n\tfmt = S->H->time_format ? S->H->time_format : PDO_FB_DEF_TIME_FMT;\n\n\tsize_t len = strftime(timeBuf, sizeof(timeBuf), fmt, &t);\n\tif (len == 0) {\n\t\treturn 1;\n\t}\n\n\tzend_string *time_tz_str = zend_strpprintf(0, \"%s %s\", timeBuf, timeZoneBuffer);\n\tZVAL_NEW_STR(result, time_tz_str);\n\treturn 0;\n}\n\n/* fetch formatted timestamp with time zone */\nstatic int get_formatted_timestamp_tz(pdo_stmt_t *stmt, const ISC_TIMESTAMP_TZ* timestampTz, zval *result)\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tunsigned year, month, day, hours, minutes, seconds, fractions;\n\tchar timeZoneBuffer[40] = {0};\n\tchar *fmt;\n\tstruct tm t;\n\tISC_TIMESTAMP ts;\n\tchar timestampBuf[80] = {0};\n\tif (fb_decode_timestamp_tz(S->H->isc_status, timestampTz, &year, &month, &day, &hours, &minutes, &seconds, &fractions, sizeof(timeZoneBuffer), timeZoneBuffer)) {\n\t\treturn 1;\n\t}\n\tts.timestamp_date = fb_encode_date(year, month, day);\n\tts.timestamp_time = fb_encode_time(hours, minutes, seconds, fractions);\n\tisc_decode_timestamp(&ts, &t);\n\n\tfmt = S->H->timestamp_format ? S->H->timestamp_format : PDO_FB_DEF_TIMESTAMP_FMT;\n\n\tsize_t len = strftime(timestampBuf, sizeof(timestampBuf), fmt, &t);\n\tif (len == 0) {\n\t\treturn 1;\n\t}\n\n\tzend_string *timestamp_tz_str = zend_strpprintf(0, \"%s %s\", timestampBuf, timeZoneBuffer);\n\tZVAL_NEW_STR(result, timestamp_tz_str);\n\treturn 0;\n}\n\n#endif\n\n/* free the allocated space for passing field values to the db and back */\nstatic void php_firebird_free_sqlda(XSQLDA const *sqlda) /* {{{ */\n{\n\tint i;\n\n\tfor (i = 0; i < sqlda->sqld; ++i) {\n\t\tXSQLVAR const *var = &sqlda->sqlvar[i];\n\n\t\tif (var->sqlind) {\n\t\t\tefree(var->sqlind);\n\t\t}\n\t}\n}\n/* }}} */\n\n/* called by PDO to clean up a statement handle */\nstatic int pdo_firebird_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tint result = 1;\n\n\t/* release the statement.\n\t * Note: if the server object is already gone then the statement was closed already as well. */\n\tif (php_pdo_stmt_valid_db_obj_handle(stmt) && isc_dsql_free_statement(S->H->isc_status, &S->stmt, DSQL_drop)) {\n\t\tphp_firebird_error_stmt(stmt);\n\t\tresult = 0;\n\t}\n\n\tzend_hash_destroy(S->named_params);\n\tFREE_HASHTABLE(S->named_params);\n\n\t/* clean up the input descriptor */\n\tif (S->in_sqlda) {\n\t\tphp_firebird_free_sqlda(S->in_sqlda);\n\t\tefree(S->in_sqlda);\n\t}\n\n\tphp_firebird_free_sqlda(&S->out_sqlda);\n\tefree(S);\n\n\treturn result;\n}\n/* }}} */\n\n/* called by PDO to execute a prepared query */\nstatic int pdo_firebird_stmt_execute(pdo_stmt_t *stmt) /* {{{ */\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tpdo_firebird_db_handle *H = S->H;\n\tzend_ulong affected_rows = 0;\n\tstatic char info_count[] = {isc_info_sql_records};\n\tchar result[64];\n\n\tdo {\n\t\t/* named or open cursors should be closed first */\n\t\tif ((*S->name || S->cursor_open) && isc_dsql_free_statement(H->isc_status, &S->stmt, DSQL_close)) {\n\t\t\tbreak;\n\t\t}\n\t\tS->cursor_open = 0;\n\n\t\t/* allocate storage for the output data */\n\t\tif (S->out_sqlda.sqld) {\n\t\t\tunsigned int i;\n\t\t\tfor (i = 0; i < S->out_sqlda.sqld; i++) {\n\t\t\t\tXSQLVAR *var = &S->out_sqlda.sqlvar[i];\n\t\t\t\tif (var->sqlind) {\n\t\t\t\t\tefree(var->sqlind);\n\t\t\t\t}\n\t\t\t\tvar->sqlind = (void*)ecalloc(1, var->sqllen + 2 * sizeof(short));\n\t\t\t\tvar->sqldata = &((char*)var->sqlind)[sizeof(short)];\n\t\t\t}\n\t\t}\n\n\t\tif (S->statement_type == isc_info_sql_stmt_exec_procedure) {\n\t\t\tif (isc_dsql_execute2(H->isc_status, &H->tr, &S->stmt, PDO_FB_SQLDA_VERSION, S->in_sqlda, &S->out_sqlda)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (isc_dsql_execute(H->isc_status, &H->tr, &S->stmt, PDO_FB_SQLDA_VERSION, S->in_sqlda)) {\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Determine how many rows have changed. In this case we are\n\t\t * only interested in rows changed, not rows retrieved. That\n\t\t * should be handled by the client when fetching. */\n\t\tstmt->row_count = affected_rows;\n\n\t\tswitch (S->statement_type) {\n\t\t\tcase isc_info_sql_stmt_insert:\n\t\t\tcase isc_info_sql_stmt_update:\n\t\t\tcase isc_info_sql_stmt_delete:\n\t\t\tcase isc_info_sql_stmt_exec_procedure:\n\t\t\t\tif (isc_dsql_sql_info(H->isc_status, &S->stmt, sizeof ( info_count),\n\t\t\t\t\tinfo_count, sizeof(result), result)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (result[0] == isc_info_sql_records) {\n\t\t\t\t\tunsigned i = 3, result_size = isc_vax_integer(&result[1], 2);\n\t\t\t\t\tif (result_size > sizeof(result)) {\n\t\t\t\t\t\tgoto error;\n\t\t\t\t\t}\n\t\t\t\t\twhile (result[i] != isc_info_end && i < result_size) {\n\t\t\t\t\t\tshort len = (short) isc_vax_integer(&result[i + 1], 2);\n\t\t\t\t\t\tif (len != 1 && len != 2 && len != 4) {\n\t\t\t\t\t\t\tgoto error;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (result[i] != isc_info_req_select_count) {\n\t\t\t\t\t\t\taffected_rows += isc_vax_integer(&result[i + 3], len);\n\t\t\t\t\t\t}\n\t\t\t\t\t\ti += len + 3;\n\t\t\t\t\t}\n\t\t\t\t\tstmt->row_count = affected_rows;\n\t\t\t\t}\n\t\t\t/* TODO Dead code or assert one of the previous cases are hit? */\n\t\t\tdefault:\n\t\t\t\t;\n\t\t}\n\n\t\tif (stmt->dbh->auto_commit && !S->H->in_manually_txn && !php_firebird_commit_transaction(stmt->dbh, /* retain */ true)) {\n\t\t\tbreak;\n\t\t}\n\n\t\t*S->name = 0;\n\t\tS->cursor_open = S->out_sqlda.sqln && (S->statement_type != isc_info_sql_stmt_exec_procedure);\n\t\tS->exhausted = !S->out_sqlda.sqln; /* There are data to fetch */\n\n\t\treturn 1;\n\t} while (0);\n\nerror:\n\tphp_firebird_error_stmt(stmt);\n\n\treturn 0;\n}\n/* }}} */\n\n/* called by PDO to fetch the next row from a statement */\nstatic int pdo_firebird_stmt_fetch(pdo_stmt_t *stmt, /* {{{ */\n\tenum pdo_fetch_orientation ori, zend_long offset)\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tpdo_firebird_db_handle *H = S->H;\n\n\tif (!stmt->executed) {\n\t\tconst char *msg = \"Cannot fetch from a closed cursor\";\n\t\tphp_firebird_error_stmt_with_info(stmt, \"HY000\", strlen(\"HY000\"), msg, strlen(msg));\n\t} else if (!S->exhausted) {\n\t\tif (S->statement_type == isc_info_sql_stmt_exec_procedure) {\n\t\t\tstmt->row_count = 1;\n\t\t\tS->exhausted = 1;\n\t\t\treturn 1;\n\t\t}\n\t\tif (isc_dsql_fetch(H->isc_status, &S->stmt, PDO_FB_SQLDA_VERSION, &S->out_sqlda)) {\n\t\t\tif (H->isc_status[0] && H->isc_status[1]) {\n\t\t\t\tphp_firebird_error_stmt(stmt);\n\t\t\t}\n\t\t\tS->exhausted = 1;\n\t\t\treturn 0;\n\t\t}\n\t\tstmt->row_count++;\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n/* }}} */\n\n/* called by PDO to retrieve information about the fields being returned */\nstatic int pdo_firebird_stmt_describe(pdo_stmt_t *stmt, int colno) /* {{{ */\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tstruct pdo_column_data *col = &stmt->columns[colno];\n\tXSQLVAR *var = &S->out_sqlda.sqlvar[colno];\n\tint colname_len;\n\tchar *cp;\n\n\tif ((var->sqltype & ~1) == SQL_TEXT) {\n\t\tvar->sqltype = SQL_VARYING | (var->sqltype & 1);\n\t}\n\tcolname_len = (S->H->fetch_table_names && var->relname_length)\n\t\t\t\t\t? (var->aliasname_length + var->relname_length + 1)\n\t\t\t\t\t: (var->aliasname_length);\n\tcol->precision = -var->sqlscale;\n\tcol->maxlen = var->sqllen;\n\tcol->name = zend_string_alloc(colname_len, 0);\n\tcp = ZSTR_VAL(col->name);\n\tif (colname_len > var->aliasname_length) {\n\t\tmemmove(cp, var->relname, var->relname_length);\n\t\tcp += var->relname_length;\n\t\t*cp++ = '.';\n\t}\n\tmemmove(cp, var->aliasname, var->aliasname_length);\n\t*(cp+var->aliasname_length) = '\\0';\n\n\treturn 1;\n}\n/* }}} */\n\nstatic int pdo_firebird_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt *) stmt->driver_data;\n\tXSQLVAR *var = &S->out_sqlda.sqlvar[colno];\n\n\tenum pdo_param_type param_type;\n\tif (var->sqlscale < 0) {\n\t\tparam_type = PDO_PARAM_STR;\n\t} else {\n\t\tswitch (var->sqltype & ~1) {\n\t\t\tcase SQL_SHORT:\n\t\t\tcase SQL_LONG:\n#if SIZEOF_ZEND_LONG >= 8\n\t\t\tcase SQL_INT64:\n#endif\n\t\t\t\tparam_type = PDO_PARAM_INT;\n\t\t\t\tbreak;\n\t\t\tcase SQL_BOOLEAN:\n\t\t\t\tparam_type = PDO_PARAM_BOOL;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tparam_type = PDO_PARAM_STR;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tarray_init(return_value);\n\tadd_assoc_long(return_value, \"pdo_type\", param_type);\n\treturn 1;\n}\n\n/* fetch a blob into a fetch buffer */\nstatic int php_firebird_fetch_blob(pdo_stmt_t *stmt, int colno, zval *result, ISC_QUAD *blob_id)\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tpdo_firebird_db_handle *H = S->H;\n\tisc_blob_handle blobh = PDO_FIREBIRD_HANDLE_INITIALIZER;\n\tchar const bl_item = isc_info_blob_total_length;\n\tchar bl_info[20];\n\tunsigned short i;\n\tint retval = 0;\n\tsize_t len = 0;\n\n\tif (isc_open_blob(H->isc_status, &H->db, &H->tr, &blobh, blob_id)) {\n\t\tphp_firebird_error_stmt(stmt);\n\t\treturn 0;\n\t}\n\n\tif (isc_blob_info(H->isc_status, &blobh, 1, const_cast(&bl_item),\n\t\t\tsizeof(bl_info), bl_info)) {\n\t\tphp_firebird_error_stmt(stmt);\n\t\tgoto fetch_blob_end;\n\t}\n\n\t/* find total length of blob's data */\n\tfor (i = 0; i < sizeof(bl_info); ) {\n\t\tunsigned short item_len;\n\t\tchar item = bl_info[i++];\n\n\t\tif (item == isc_info_end || item == isc_info_truncated || item == isc_info_error\n\t\t\t\t|| i >= sizeof(bl_info)) {\n\t\t\tconst char *msg = \"Couldn't determine BLOB size\";\n\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY000\", strlen(\"HY000\"), msg, strlen(msg));\n\t\t\tgoto fetch_blob_end;\n\t\t}\n\n\t\titem_len = (unsigned short) isc_vax_integer(&bl_info[i], 2);\n\n\t\tif (item == isc_info_blob_total_length) {\n\t\t\tlen = isc_vax_integer(&bl_info[i+2], item_len);\n\t\t\tbreak;\n\t\t}\n\t\ti += item_len+2;\n\t}\n\n\t/* we've found the blob's length, now fetch! */\n\n\tif (len) {\n\t\tzend_ulong cur_len;\n\t\tunsigned short seg_len;\n\t\tISC_STATUS stat;\n\t\tzend_string *str;\n\n\t\t/* prevent overflow */\n\t\tif (len > ZSTR_MAX_LEN) {\n\t\t\tresult = 0;\n\t\t\tgoto fetch_blob_end;\n\t\t}\n\n\t\tstr = zend_string_alloc(len, 0);\n\n\t\tfor (cur_len = stat = 0; (!stat || stat == isc_segment) && cur_len < len; cur_len += seg_len) {\n\n\t\t\tunsigned short chunk_size = (len - cur_len) > USHRT_MAX ? USHRT_MAX\n\t\t\t\t: (unsigned short)(len - cur_len);\n\n\t\t\tstat = isc_get_segment(H->isc_status, &blobh, &seg_len, chunk_size, ZSTR_VAL(str) + cur_len);\n\t\t}\n\n\t\tZSTR_VAL(str)[len] = '\\0';\n\t\tZVAL_STR(result, str);\n\n\t\tif (H->isc_status[0] == 1 && (stat != 0 && stat != isc_segstr_eof && stat != isc_segment)) {\n\t\t\tconst char *msg = \"Error reading from BLOB\";\n\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY000\", strlen(\"HY000\"), msg, strlen(msg));\n\t\t\tgoto fetch_blob_end;\n\t\t}\n\t}\n\tretval = 1;\n\nfetch_blob_end:\n\tif (isc_close_blob(H->isc_status, &blobh)) {\n\t\tphp_firebird_error_stmt(stmt);\n\t\treturn 0;\n\t}\n\treturn retval;\n}\n/* }}} */\n\nstatic int pdo_firebird_stmt_get_col(\n\t\tpdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type)\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tXSQLVAR const *var = &S->out_sqlda.sqlvar[colno];\n\n\tif (*var->sqlind == -1) {\n\t\tZVAL_NULL(result);\n\t} else {\n\t\tif (var->sqlscale < 0) {\n\t\t\tstatic ISC_INT64 const scales[] = { 1, 10, 100, 1000,\n\t\t\t\t10000,\n\t\t\t\t100000,\n\t\t\t\t1000000,\n\t\t\t\t10000000,\n\t\t\t\t100000000,\n\t\t\t\t1000000000,\n\t\t\t\tLL_LIT(10000000000),\n\t\t\t\tLL_LIT(100000000000),\n\t\t\t\tLL_LIT(1000000000000),\n\t\t\t\tLL_LIT(10000000000000),\n\t\t\t\tLL_LIT(100000000000000),\n\t\t\t\tLL_LIT(1000000000000000),\n\t\t\t\tLL_LIT(10000000000000000),\n\t\t\t\tLL_LIT(100000000000000000),\n\t\t\t\tLL_LIT(1000000000000000000)\n\t\t\t};\n\t\t\tISC_INT64 n, f = scales[-var->sqlscale];\n\t\t\tzend_string *str;\n\n\t\t\tswitch (var->sqltype & ~1) {\n\t\t\t\tcase SQL_SHORT:\n\t\t\t\t\tn = *(short*)var->sqldata;\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_LONG:\n\t\t\t\t\tn = php_get_isc_long_from_sqldata(var->sqldata);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_INT64:\n\t\t\t\t\tn = php_get_isc_int64_from_sqldata(var->sqldata);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_DOUBLE:\n\t\t\t\t\tbreak;\n\t\t\t\tEMPTY_SWITCH_DEFAULT_CASE()\n\t\t\t}\n\n\t\t\tif ((var->sqltype & ~1) == SQL_DOUBLE) {\n\t\t\t\tstr = zend_strpprintf(0, \"%.*F\", -var->sqlscale, php_get_double_from_sqldata(var->sqldata));\n\t\t\t} else if (n >= 0) {\n\t\t\t\tstr = zend_strpprintf(0, \"%\" LL_MASK \"d.%0*\" LL_MASK \"d\",\n\t\t\t\t\tn / f, -var->sqlscale, n % f);\n\t\t\t} else if (n <= -f) {\n\t\t\t\tstr = zend_strpprintf(0, \"%\" LL_MASK \"d.%0*\" LL_MASK \"d\",\n\t\t\t\t\tn / f, -var->sqlscale, -n % f);\n\t\t\t } else {\n\t\t\t\tstr = zend_strpprintf(0, \"-0.%0*\" LL_MASK \"d\", -var->sqlscale, -n % f);\n\t\t\t}\n\t\t\tZVAL_STR(result, str);\n\t\t} else {\n\t\t\tswitch (var->sqltype & ~1) {\n\t\t\t\tstruct tm t;\n\t\t\t\tchar *fmt;\n\n\t\t\t\tcase SQL_VARYING:\n\t\t\t\t\tZVAL_STRINGL_FAST(result, &var->sqldata[2], *(short*)var->sqldata);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_TEXT:\n\t\t\t\t\tZVAL_STRINGL_FAST(result, var->sqldata, var->sqllen);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_SHORT:\n\t\t\t\t\tZVAL_LONG(result, *(short*)var->sqldata);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_LONG:\n\t\t\t\t\tZVAL_LONG(result, php_get_isc_long_from_sqldata(var->sqldata));\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_INT64:\n#if SIZEOF_ZEND_LONG >= 8\n\t\t\t\t\tZVAL_LONG(result, php_get_isc_int64_from_sqldata(var->sqldata));\n#else\n\t\t\t\t\tZVAL_STR(result, zend_strpprintf(0, \"%\" LL_MASK \"d\", php_get_isc_int64_from_sqldata(var->sqldata)));\n#endif\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_FLOAT:\n\t\t\t\t\t/* TODO: Why is this not returned as the native type? */\n\t\t\t\t\tZVAL_STR(result, zend_strpprintf_unchecked(0, \"%.8H\", php_get_float_from_sqldata(var->sqldata)));\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_DOUBLE:\n\t\t\t\t\t/* TODO: Why is this not returned as the native type? */\n\t\t\t\t\tZVAL_STR(result, zend_strpprintf_unchecked(0, \"%.16H\", php_get_double_from_sqldata(var->sqldata)));\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_BOOLEAN:\n\t\t\t\t\tZVAL_BOOL(result, *(FB_BOOLEAN*)var->sqldata);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SQL_TYPE_DATE:\n\t\t\t\t\tisc_decode_sql_date((ISC_DATE*)var->sqldata, &t);\n\t\t\t\t\tfmt = S->H->date_format ? S->H->date_format : PDO_FB_DEF_DATE_FMT;\n\t\t\t\t\tif (0) {\n\t\t\t\tcase SQL_TYPE_TIME:\n\t\t\t\t\t\tisc_decode_sql_time((ISC_TIME*)var->sqldata, &t);\n\t\t\t\t\t\tfmt = S->H->time_format ? S->H->time_format : PDO_FB_DEF_TIME_FMT;\n\t\t\t\t\t} else if (0) {\n\t\t\t\tcase SQL_TIMESTAMP:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tISC_TIMESTAMP timestamp = php_get_isc_timestamp_from_sqldata(var->sqldata);\n\t\t\t\t\t\t\tisc_decode_timestamp(&timestamp, &t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfmt = S->H->timestamp_format ? S->H->timestamp_format : PDO_FB_DEF_TIMESTAMP_FMT;\n\t\t\t\t\t}\n\t\t\t\t\t/* convert the timestamp into a string */\n\t\t\t\t\tchar buf[80];\n\t\t\t\t\tsize_t len = strftime(buf, sizeof(buf), fmt, &t);\n\t\t\t\t\tZVAL_STRINGL(result, buf, len);\n\t\t\t\t\tbreak;\n#if FB_API_VER >= 40\n\t\t\t\tcase SQL_TIME_TZ: {\n\t\t\t\t\tISC_TIME_TZ time = php_get_isc_time_tz_from_sqldata(var->sqldata);\n\t\t\t\t\treturn get_formatted_time_tz(stmt, &time, result);\n\t\t\t\t}\n\t\t\t\tcase SQL_TIMESTAMP_TZ: {\n\t\t\t\t\tISC_TIMESTAMP_TZ ts = php_get_isc_timestamp_tz_from_sqldata(var->sqldata);\n\t\t\t\t\treturn get_formatted_timestamp_tz(stmt, &ts, result);\n\t\t\t\t}\n#endif\n\t\t\t\tcase SQL_BLOB: {\n\t\t\t\t\tISC_QUAD quad = php_get_isc_quad_from_sqldata(var->sqldata);\n\t\t\t\t\treturn php_firebird_fetch_blob(stmt, colno, result, &quad);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn 1;\n}\n\nstatic int php_firebird_bind_blob(pdo_stmt_t *stmt, ISC_QUAD *blob_id, zval *param)\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tpdo_firebird_db_handle *H = S->H;\n\tisc_blob_handle h = PDO_FIREBIRD_HANDLE_INITIALIZER;\n\tzval data;\n\tzend_ulong put_cnt = 0, rem_cnt;\n\tunsigned short chunk_size;\n\tint result = 1;\n\n\tif (isc_create_blob(H->isc_status, &H->db, &H->tr, &h, blob_id)) {\n\t\tphp_firebird_error_stmt(stmt);\n\t\treturn 0;\n\t}\n\n\tif (Z_TYPE_P(param) != IS_STRING) {\n\t\tZVAL_STR(&data, zval_get_string_func(param));\n\t} else {\n\t\tZVAL_COPY_VALUE(&data, param);\n\t}\n\n\tfor (rem_cnt = Z_STRLEN(data); rem_cnt > 0; rem_cnt -= chunk_size) {\n\t\tchunk_size = rem_cnt > USHRT_MAX ? USHRT_MAX : (unsigned short)rem_cnt;\n\t\tif (isc_put_segment(H->isc_status, &h, chunk_size, &Z_STRVAL(data)[put_cnt])) {\n\t\t\tphp_firebird_error_stmt(stmt);\n\t\t\tresult = 0;\n\t\t\tbreak;\n\t\t}\n\t\tput_cnt += chunk_size;\n\t}\n\n\tif (Z_TYPE_P(param) != IS_STRING) {\n\t\tzval_ptr_dtor_str(&data);\n\t}\n\n\tif (isc_close_blob(H->isc_status, &h)) {\n\t\tphp_firebird_error_stmt(stmt);\n\t\treturn 0;\n\t}\n\treturn result;\n}\n\nstatic int pdo_firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, /* {{{ */\n\tenum pdo_param_event event_type)\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\tXSQLDA *sqlda = param->is_param ? S->in_sqlda : &S->out_sqlda;\n\tXSQLVAR *var;\n\n\tif (event_type == PDO_PARAM_EVT_FREE) { /* not used */\n\t\treturn 1;\n\t}\n\n\tif (!sqlda || param->paramno >= sqlda->sqld) {\n\t\tconst char *msg = \"Invalid parameter index\";\n\t\tphp_firebird_error_stmt_with_info(stmt, \"HY093\", strlen(\"HY093\"), msg, strlen(msg));\n\t\treturn 0;\n\t}\n\tif (param->is_param && param->paramno == -1) {\n\t\tzval *index;\n\n\t\t/* try to determine the index by looking in the named_params hash */\n\t\tif ((index = zend_hash_find(S->named_params, param->name)) != NULL) {\n\t\t\tparam->paramno = Z_LVAL_P(index);\n\t\t} else {\n\t\t\t/* ... or by looking in the input descriptor */\n\t\t\tint i;\n\n\t\t\tfor (i = 0; i < sqlda->sqld; ++i) {\n\t\t\t\tXSQLVAR *var = &sqlda->sqlvar[i];\n\n\t\t\t\tif ((var->aliasname_length && !strncasecmp(ZSTR_VAL(param->name), var->aliasname,\n\t\t\t\t\t\tmin(ZSTR_LEN(param->name), var->aliasname_length)))\n\t\t\t\t\t\t|| (var->sqlname_length && !strncasecmp(ZSTR_VAL(param->name), var->sqlname,\n\t\t\t\t\t\tmin(ZSTR_LEN(param->name), var->sqlname_length)))) {\n\t\t\t\t\tparam->paramno = i;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (i >= sqlda->sqld) {\n\t\t\t\tconst char *msg = \"Invalid parameter name\";\n\t\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY093\", strlen(\"HY093\"), msg, strlen(msg));\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tvar = &sqlda->sqlvar[param->paramno];\n\n\tswitch (event_type) {\n\t\tzval *parameter;\n\n\t\tcase PDO_PARAM_EVT_ALLOC:\n\t\t\tif (param->is_param) {\n\t\t\t\t/* allocate the parameter */\n\t\t\t\tif (var->sqlind) {\n\t\t\t\t\tefree(var->sqlind);\n\t\t\t\t}\n\t\t\t\tvar->sqlind = (void*)emalloc(var->sqllen + 2*sizeof(short));\n\t\t\t\tvar->sqldata = &((char*)var->sqlind)[sizeof(short)];\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase PDO_PARAM_EVT_EXEC_PRE:\n\t\t\tif (!param->is_param) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t*var->sqlind = 0;\n\t\t\tif (Z_ISREF(param->parameter)) {\n\t\t\t\tparameter = Z_REFVAL(param->parameter);\n\t\t\t} else {\n\t\t\t\tparameter = &param->parameter;\n\t\t\t}\n\n\t\t\tif (Z_TYPE_P(parameter) == IS_RESOURCE) {\n\t\t\t\tphp_stream *stm = NULL;\n\n\t\t\t\tphp_stream_from_zval_no_verify(stm, parameter);\n\t\t\t\tif (stm) {\n\t\t\t\t\tzend_string *mem =  php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0);\n\t\t\t\t\tzval_ptr_dtor(parameter);\n\t\t\t\t\tZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC());\n\t\t\t\t} else {\n\t\t\t\t\tpdo_raise_impl_error(stmt->dbh, stmt, \"HY105\", \"Expected a stream resource\");\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tswitch (var->sqltype & ~1) {\n\t\t\t\tcase SQL_ARRAY:\n\t\t\t\t\t{\n\t\t\t\t\t\tconst char *msg = \"Cannot bind to array field\";\n\t\t\t\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY000\", strlen(\"HY000\"), msg, strlen(msg));\n\t\t\t\t\t}\n\t\t\t\t\treturn 0;\n\n\t\t\t\tcase SQL_BLOB: {\n\t\t\t\t\tif (Z_TYPE_P(parameter) == IS_NULL) {\n\t\t\t\t\t\t/* Check if field allow NULL values */\n\t\t\t\t\t\tif (~var->sqltype & 1) {\n\t\t\t\t\t\t\tconst char *msg = \"Parameter requires non-null value\";\n\t\t\t\t\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY105\", strlen(\"HY105\"), msg, strlen(msg));\n\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t*var->sqlind = -1;\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t\tISC_QUAD quad = php_get_isc_quad_from_sqldata(var->sqldata);\n\t\t\t\t\tif (php_firebird_bind_blob(stmt, &quad, parameter) != 0) {\n\t\t\t\t\t\tmemcpy(var->sqldata, &quad, sizeof(quad));\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* keep native BOOLEAN type */\n\t\t\tif ((var->sqltype & ~1) == SQL_BOOLEAN) {\n\t\t\t\tswitch (Z_TYPE_P(parameter)) {\n\t\t\t\t\tcase IS_LONG:\n\t\t\t\t\tcase IS_DOUBLE:\n\t\t\t\t\tcase IS_TRUE:\n\t\t\t\t\tcase IS_FALSE:\n\t\t\t\t\t\t*(FB_BOOLEAN*)var->sqldata = zend_is_true(parameter) ? FB_TRUE : FB_FALSE;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_STRING:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tzend_long lval;\n\t\t\t\t\t\t\tdouble dval;\n\n\t\t\t\t\t\t\tif (Z_STRLEN_P(parameter) == 0) {\n\t\t\t\t\t\t\t\t*(FB_BOOLEAN*)var->sqldata = FB_FALSE;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tswitch (is_numeric_string(Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), &lval, &dval, 0)) {\n\t\t\t\t\t\t\t\tcase IS_LONG:\n\t\t\t\t\t\t\t\t\t*(FB_BOOLEAN*)var->sqldata = (lval != 0) ? FB_TRUE : FB_FALSE;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase IS_DOUBLE:\n\t\t\t\t\t\t\t\t\t*(FB_BOOLEAN*)var->sqldata = (dval != 0) ? FB_TRUE : FB_FALSE;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\tif (!zend_binary_strncasecmp(Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), \"true\", 4, 4)) {\n\t\t\t\t\t\t\t\t\t\t*(FB_BOOLEAN*)var->sqldata = FB_TRUE;\n\t\t\t\t\t\t\t\t\t} else if (!zend_binary_strncasecmp(Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), \"false\", 5, 5)) {\n\t\t\t\t\t\t\t\t\t\t*(FB_BOOLEAN*)var->sqldata = FB_FALSE;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tconst char *msg = \"Cannot convert string to boolean\";\n\t\t\t\t\t\t\t\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY105\", strlen(\"HY105\"), msg, strlen(msg));\n\t\t\t\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_NULL:\n\t\t\t\t\t\t*var->sqlind = -1;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst char *msg = \"Binding arrays/objects is not supported\";\n\t\t\t\t\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY105\", strlen(\"HY105\"), msg, strlen(msg));\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* check if a NULL should be inserted */\n\t\t\tswitch (Z_TYPE_P(parameter)) {\n\t\t\t\tint force_null;\n\n\t\t\t\tcase IS_LONG:\n\t\t\t\t\t/* keep the allow-NULL flag */\n\t\t\t\t\tvar->sqltype = (sizeof(zend_long) == 8 ? SQL_INT64 : SQL_LONG) | (var->sqltype & 1);\n\t\t\t\t\tvar->sqldata = (void*)&Z_LVAL_P(parameter);\n\t\t\t\t\tvar->sqllen = sizeof(zend_long);\n\t\t\t\t\tbreak;\n\t\t\t\tcase IS_DOUBLE:\n\t\t\t\t\t/* keep the allow-NULL flag */\n\t\t\t\t\tvar->sqltype = SQL_DOUBLE | (var->sqltype & 1);\n\t\t\t\t\tvar->sqldata = (void*)&Z_DVAL_P(parameter);\n\t\t\t\t\tvar->sqllen = sizeof(double);\n\t\t\t\t\tbreak;\n\t\t\t\tcase IS_STRING:\n\t\t\t\t\tforce_null = 0;\n\n\t\t\t\t\t/* for these types, an empty string can be handled like a NULL value */\n\t\t\t\t\tswitch (var->sqltype & ~1) {\n\t\t\t\t\t\tcase SQL_SHORT:\n\t\t\t\t\t\tcase SQL_LONG:\n\t\t\t\t\t\tcase SQL_INT64:\n\t\t\t\t\t\tcase SQL_FLOAT:\n\t\t\t\t\t\tcase SQL_DOUBLE:\n\t\t\t\t\t\tcase SQL_TIMESTAMP:\n\t\t\t\t\t\tcase SQL_TYPE_DATE:\n\t\t\t\t\t\tcase SQL_TYPE_TIME:\n#if FB_API_VER >= 40\n\t\t\t\t\t\tcase SQL_INT128:\n\t\t\t\t\t\tcase SQL_DEC16:\n\t\t\t\t\t\tcase SQL_DEC34:\n\t\t\t\t\t\tcase SQL_TIMESTAMP_TZ:\n\t\t\t\t\t\tcase SQL_TIME_TZ:\n#endif\n\t\t\t\t\t\t\tforce_null = (Z_STRLEN_P(parameter) == 0);\n\t\t\t\t\t}\n\t\t\t\t\tif (!force_null) {\n\t\t\t\t\t\t/* keep the allow-NULL flag */\n\t\t\t\t\t\tvar->sqltype = SQL_TEXT | (var->sqltype & 1);\n\t\t\t\t\t\tvar->sqldata = Z_STRVAL_P(parameter);\n\t\t\t\t\t\tvar->sqllen = Z_STRLEN_P(parameter);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tZEND_FALLTHROUGH;\n\t\t\t\tcase IS_NULL:\n\t\t\t\t\t/* complain if this field doesn't allow NULL values */\n\t\t\t\t\tif (~var->sqltype & 1) {\n\t\t\t\t\t\tconst char *msg = \"Parameter requires non-null value\";\n\t\t\t\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY105\", strlen(\"HY105\"), msg, strlen(msg));\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\t}\n\t\t\t\t\t*var->sqlind = -1;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t{\n\t\t\t\t\t\tconst char *msg = \"Binding arrays/objects is not supported\";\n\t\t\t\t\t\tphp_firebird_error_stmt_with_info(stmt, \"HY105\", strlen(\"HY105\"), msg, strlen(msg));\n\t\t\t\t\t}\n\t\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase PDO_PARAM_EVT_FETCH_POST:\n\t\t\tif (param->paramno == -1) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tif (param->is_param) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (Z_ISREF(param->parameter)) {\n\t\t\t\tparameter = Z_REFVAL(param->parameter);\n\t\t\t} else {\n\t\t\t\tparameter = &param->parameter;\n\t\t\t}\n\t\t\tzval_ptr_dtor(parameter);\n\t\t\tZVAL_NULL(parameter);\n\t\t\treturn pdo_firebird_stmt_get_col(stmt, param->paramno, parameter, NULL);\n\t\tdefault:\n\t\t\t;\n\t}\n\treturn 1;\n}\n/* }}} */\n\nstatic int pdo_firebird_stmt_set_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) /* {{{ */\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\n\tswitch (attr) {\n\t\tdefault:\n\t\t\treturn 0;\n\t\tcase PDO_ATTR_CURSOR_NAME: {\n\t\t\tzend_string *str_val = zval_try_get_string(val);\n\t\t\tif (str_val == NULL) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\t// TODO Check cursor name does not have null bytes?\n\t\t\tif (ZSTR_LEN(str_val) >= sizeof(S->name)) {\n\t\t\t\tzend_value_error(\"Cursor name must not be longer than %zu bytes\", sizeof(S->name) - 1);\n\t\t\t\tzend_string_release(str_val);\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tif (isc_dsql_set_cursor_name(S->H->isc_status, &S->stmt, ZSTR_VAL(str_val), 0)) {\n\t\t\t\tphp_firebird_error_stmt(stmt);\n\t\t\t\tzend_string_release(str_val);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\t/* Include trailing nul byte */\n\t\t\tmemcpy(S->name, ZSTR_VAL(str_val), ZSTR_LEN(str_val) + 1);\n\t\t\tzend_string_release(str_val);\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn 1;\n}\n/* }}} */\n\nstatic int pdo_firebird_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) /* {{{ */\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\n\tswitch (attr) {\n\t\tdefault:\n\t\t\treturn 0;\n\t\tcase PDO_ATTR_CURSOR_NAME:\n\t\t\tif (*S->name) {\n\t\t\t\tZVAL_STRING(val, S->name);\n\t\t\t} else {\n\t\t\t\tZVAL_NULL(val);\n\t\t\t}\n\t\t\tbreak;\n\t}\n\treturn 1;\n}\n/* }}} */\n\nstatic int pdo_firebird_stmt_cursor_closer(pdo_stmt_t *stmt) /* {{{ */\n{\n\tpdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data;\n\n\t/* close the statement handle */\n\tif ((*S->name || S->cursor_open) && isc_dsql_free_statement(S->H->isc_status, &S->stmt, DSQL_close)) {\n\t\tphp_firebird_error_stmt(stmt);\n\t\treturn 0;\n\t}\n\t*S->name = 0;\n\tS->cursor_open = 0;\n\treturn 1;\n}\n/* }}} */\n\n\nconst struct pdo_stmt_methods swoole_firebird_stmt_methods = { /* {{{ */\n\tpdo_firebird_stmt_dtor,\n\tpdo_firebird_stmt_execute,\n\tpdo_firebird_stmt_fetch,\n\tpdo_firebird_stmt_describe,\n\tpdo_firebird_stmt_get_col,\n\tpdo_firebird_stmt_param_hook,\n\tpdo_firebird_stmt_set_attribute,\n\tpdo_firebird_stmt_get_attribute,\n\tpdo_firebird_stmt_get_column_meta,\n\tNULL, /* next_rowset_func */\n\tpdo_firebird_stmt_cursor_closer\n};\n/* }}} */\n\n#endif\n"
  },
  {
    "path": "thirdparty/php85/pdo_firebird/pdo_firebird_utils.cpp",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Simonov Denis <sim-mail@list.ru>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_FIREBIRD_HOOK\n#include \"php_swoole_firebird.h\"\n\n#if PHP_VERSION_ID >= 80500\n\n#include \"pdo_firebird_utils.h\"\n#include <firebird/Interface.h>\n#include <cstring>\n\n/* Returns the client version. 0 bytes are minor version, 1 bytes are major version. */\nextern \"C\" unsigned fb_get_client_version(void)\n{\n\tFirebird::IMaster* master = Firebird::fb_get_master_interface();\n\tFirebird::IUtil* util = master->getUtilInterface();\n\treturn util->getClientVersion();\n}\n\nextern \"C\" ISC_TIME fb_encode_time(unsigned hours, unsigned minutes, unsigned seconds, unsigned fractions)\n{\n\tFirebird::IMaster* master = Firebird::fb_get_master_interface();\n\tFirebird::IUtil* util = master->getUtilInterface();\n\treturn util->encodeTime(hours, minutes, seconds, fractions);\n}\n\nextern \"C\" ISC_DATE fb_encode_date(unsigned year, unsigned month, unsigned day)\n{\n\tFirebird::IMaster* master = Firebird::fb_get_master_interface();\n\tFirebird::IUtil* util = master->getUtilInterface();\n\treturn util->encodeDate(year, month, day);\n}\n\n#if FB_API_VER >= 40\nstatic void fb_copy_status(const ISC_STATUS* from, ISC_STATUS* to, size_t maxLength)\n{\n\tfor(size_t i=0; i < maxLength; ++i) {\n\t\tmemcpy(to + i, from + i, sizeof(ISC_STATUS));\n\t\tif (from[i] == isc_arg_end) {\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n/* Decodes a time with time zone into its time components. */\nextern \"C\" ISC_STATUS fb_decode_time_tz(ISC_STATUS* isc_status, const ISC_TIME_TZ* timeTz, unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions,\n   unsigned timeZoneBufferLength, char* timeZoneBuffer)\n{\n\tFirebird::IMaster* master = Firebird::fb_get_master_interface();\n\tFirebird::IUtil* util = master->getUtilInterface();\n\tFirebird::IStatus* status = master->getStatus();\n\tFirebird::CheckStatusWrapper st(status);\n\tutil->decodeTimeTz(&st, timeTz, hours, minutes, seconds, fractions,\n\t\t\t\t\t\ttimeZoneBufferLength, timeZoneBuffer);\n\tif (st.hasData())  {\n\t\tfb_copy_status((const ISC_STATUS*)st.getErrors(), isc_status, 20);\n\t}\n\tstatus->dispose();\n\treturn isc_status[1];\n}\n\n/* Decodes a timestamp with time zone into its date and time components */\nextern \"C\" ISC_STATUS fb_decode_timestamp_tz(ISC_STATUS* isc_status, const ISC_TIMESTAMP_TZ* timestampTz,\n\tunsigned* year, unsigned* month, unsigned* day,\n\tunsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions,\n\tunsigned timeZoneBufferLength, char* timeZoneBuffer)\n{\n\tFirebird::IMaster* master = Firebird::fb_get_master_interface();\n\tFirebird::IUtil* util = master->getUtilInterface();\n\tFirebird::IStatus* status = master->getStatus();\n\tFirebird::CheckStatusWrapper st(status);\n\tutil->decodeTimeStampTz(&st, timestampTz, year, month, day,\n\t\t\t\t\t\t\thours, minutes, seconds, fractions,\n\t\t\t\t\t\t\ttimeZoneBufferLength, timeZoneBuffer);\n\tif (st.hasData())  {\n\t\tfb_copy_status((const ISC_STATUS*)st.getErrors(), isc_status, 20);\n\t}\n\tstatus->dispose();\n\treturn isc_status[1];\n}\n\n#endif\n#endif\n"
  },
  {
    "path": "thirdparty/php85/pdo_firebird/pdo_firebird_utils.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Simonov Denis <sim-mail@list.ru>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef PDO_FIREBIRD_UTILS_H\n#define PDO_FIREBIRD_UTILS_H\n\n#include <ibase.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nunsigned fb_get_client_version(void);\n\nISC_TIME fb_encode_time(unsigned hours, unsigned minutes, unsigned seconds, unsigned fractions);\n\nISC_DATE fb_encode_date(unsigned year, unsigned month, unsigned day);\n\n#if FB_API_VER >= 40\n\nISC_STATUS fb_decode_time_tz(ISC_STATUS* isc_status, const ISC_TIME_TZ* timeTz, unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions,\n\tunsigned timeZoneBufferLength, char* timeZoneBuffer);\n\nISC_STATUS fb_decode_timestamp_tz(ISC_STATUS* isc_status, const ISC_TIMESTAMP_TZ* timestampTz,\n\tunsigned* year, unsigned* month, unsigned* day,\n\tunsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions,\n\tunsigned timeZoneBufferLength, char* timeZoneBuffer);\n\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\t/* PDO_FIREBIRD_UTILS_H */\n"
  },
  {
    "path": "thirdparty/php85/pdo_firebird/php_pdo_firebird_int.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Ard Biesheuvel <abies@php.net>                               |\n  +----------------------------------------------------------------------+\n*/\n\n/* internal header; not supposed to be installed */\n\n#ifndef PHP_PDO_FIREBIRD_INT_H\n#define PHP_PDO_FIREBIRD_INT_H\n\n#include <ibase.h>\n\n#ifdef SQLDA_VERSION\n#define PDO_FB_SQLDA_VERSION SQLDA_VERSION\n#else\n#define PDO_FB_SQLDA_VERSION 1\n#endif\n\n#define PDO_FB_DIALECT 3\n\n#define PDO_FB_DEF_DATE_FMT \"%Y-%m-%d\"\n#define PDO_FB_DEF_TIME_FMT \"%H:%M:%S\"\n#define PDO_FB_DEF_TIMESTAMP_FMT PDO_FB_DEF_DATE_FMT \" \" PDO_FB_DEF_TIME_FMT\n\n#if SIZEOF_ZEND_LONG == 8 && !defined(PHP_WIN32)\n# define LL_LIT(lit) lit ## L\n#else\n# define LL_LIT(lit) lit ## LL\n#endif\n#define LL_MASK \"ll\"\n\n/* Firebird API has a couple of missing const decls in its API */\n#define const_cast(s) ((char*)(s))\n\n#ifndef min\n#define min(a,b) ((a)<(b)?(a):(b))\n#endif\n\n#if defined(_LP64) || defined(__LP64__) || defined(__arch64__) || defined(_WIN64)\n# define PDO_FIREBIRD_HANDLE_INITIALIZER 0U\n#else\n# define PDO_FIREBIRD_HANDLE_INITIALIZER NULL\n#endif\n\ntypedef struct {\n\tint sqlcode;\n\tchar *errmsg;\n\tsize_t errmsg_length;\n} pdo_firebird_error_info;\n\ntypedef struct {\n\t/* the result of the last API call */\n\tISC_STATUS isc_status[20];\n\n\t/* the connection handle */\n\tisc_db_handle db;\n\n\t/* the transaction handle */\n\tisc_tr_handle tr;\n\tbool in_manually_txn;\n\tbool is_writable_txn;\n\tzend_ulong txn_isolation_level;\n\n\t/* date and time format strings, can be set by the set_attribute method */\n\tchar *date_format;\n\tchar *time_format;\n\tchar *timestamp_format;\n\n\tunsigned sql_dialect:2;\n\n\t/* prepend table names on column names in fetch */\n\tunsigned fetch_table_names:1;\n\n\tunsigned _reserved:29;\n\n\tpdo_firebird_error_info einfo;\n} pdo_firebird_db_handle;\n\n\ntypedef struct {\n\t/* the link that owns this statement */\n\tpdo_firebird_db_handle *H;\n\n\t/* the statement handle */\n\tisc_stmt_handle stmt;\n\n\t/* the name of the cursor (if it has one) */\n\tchar name[32];\n\n\t/* the type of statement that was issued */\n\tchar statement_type:8;\n\n\t/* whether EOF was reached for this statement */\n\tunsigned exhausted:1;\n\n\t/* successful isc_dsql_execute opens a cursor */\n\tunsigned cursor_open:1;\n\n\tunsigned _reserved:22;\n\n\t/* the named params that were converted to ?'s by the driver */\n\tHashTable *named_params;\n\n\t/* the input SQLDA */\n\tXSQLDA *in_sqlda;\n\n\t/* the output SQLDA */\n\tXSQLDA out_sqlda; /* last member */\n} pdo_firebird_stmt;\n\nextern const pdo_driver_t pdo_firebird_driver;\n\nextern const struct pdo_stmt_methods swoole_firebird_stmt_methods;\n\nextern void php_firebird_set_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *state, const size_t state_len,\n\tconst char *msg, const size_t msg_len);\n#define php_firebird_error(d) php_firebird_set_error(d, NULL, NULL, 0, NULL, 0)\n#define php_firebird_error_stmt(s) php_firebird_set_error(s->dbh, s, NULL, 0, NULL, 0)\n#define php_firebird_error_with_info(d,e,el,m,ml) php_firebird_set_error(d, NULL, e, el, m, ml)\n#define php_firebird_error_stmt_with_info(s,e,el,m,ml) php_firebird_set_error(s->dbh, s, e, el, m, ml)\n\nextern bool php_firebird_commit_transaction(pdo_dbh_t *dbh, bool retain);\n\nenum {\n\tPDO_FB_ATTR_DATE_FORMAT = PDO_ATTR_DRIVER_SPECIFIC,\n\tPDO_FB_ATTR_TIME_FORMAT,\n\tPDO_FB_ATTR_TIMESTAMP_FORMAT,\n\n\t/*\n\t * transaction isolation level\n\t * firebird does not have a level equivalent to read uncommited.\n\t */\n\tPDO_FB_TRANSACTION_ISOLATION_LEVEL,\n\tPDO_FB_READ_COMMITTED,\n\tPDO_FB_REPEATABLE_READ,\n\tPDO_FB_SERIALIZABLE,\n\n\t/* transaction access mode */\n\tPDO_FB_WRITABLE_TRANSACTION,\n};\n\n#endif\t/* PHP_PDO_FIREBIRD_INT_H */\n"
  },
  {
    "path": "thirdparty/php85/pdo_odbc/odbc_driver.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n#define SW_USE_ODBC_HOOK\n#include \"php_swoole_odbc.h\"\n\n#if PHP_VERSION_ID >= 80500\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"ext/pdo/php_pdo.h\"\n#include \"ext/pdo/php_pdo_driver.h\"\n/* this file actually lives in main/ */\n#include \"php_odbc_utils.h\"\n#include \"php_pdo_odbc.h\"\n//#include \"php_pdo_odbc_int.h\"\n#include \"zend_exceptions.h\"\n\nstatic void pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info)\n{\n\tpdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;\n\tpdo_odbc_errinfo *einfo = &H->einfo;\n\tpdo_odbc_stmt *S = NULL;\n\tzend_string *message = NULL;\n\n\tif (stmt) {\n\t\tS = (pdo_odbc_stmt*)stmt->driver_data;\n\t\teinfo = &S->einfo;\n\t}\n\n\t/* If we don't have a driver error do not populate the info array */\n\tif (strlen(einfo->last_err_msg) == 0) {\n\t\treturn;\n\t}\n\n\tmessage = strpprintf(0, \"%s (%s[%ld] at %s:%d)\",\n\t\t\t\teinfo->last_err_msg,\n\t\t\t\teinfo->what, (long) einfo->last_error,\n\t\t\t\teinfo->file, einfo->line);\n\n\tadd_next_index_long(info, einfo->last_error);\n\tadd_next_index_str(info, message);\n\tadd_next_index_string(info, einfo->last_state);\n}\n\n\nvoid pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line) /* {{{ */\n{\n\tSQLRETURN rc;\n\tSQLSMALLINT\terrmsgsize = 0;\n\tSQLHANDLE eh;\n\tSQLSMALLINT htype, recno = 1;\n\tpdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data;\n\tpdo_odbc_errinfo *einfo = &H->einfo;\n\tpdo_odbc_stmt *S = NULL;\n\tpdo_error_type *pdo_err = &dbh->error_code;\n\n\tif (stmt) {\n\t\tS = (pdo_odbc_stmt*)stmt->driver_data;\n\n\t\teinfo = &S->einfo;\n\t\tpdo_err = &stmt->error_code;\n\t}\n\n\tif (statement == SQL_NULL_HSTMT && S) {\n\t\tstatement = S->stmt;\n\t}\n\n\tif (statement) {\n\t\thtype = SQL_HANDLE_STMT;\n\t\teh = statement;\n\t} else if (H->dbc) {\n\t\thtype = SQL_HANDLE_DBC;\n\t\teh = H->dbc;\n\t} else {\n\t\thtype = SQL_HANDLE_ENV;\n\t\teh = H->env;\n\t}\n\n\trc = SQLGetDiagRec(htype, eh, recno++, (SQLCHAR *) einfo->last_state, &einfo->last_error,\n\t\t\t(SQLCHAR *) einfo->last_err_msg, sizeof(einfo->last_err_msg)-1, &errmsgsize);\n\n\tif (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n\t\terrmsgsize = 0;\n\t}\n\n\teinfo->last_err_msg[errmsgsize] = '\\0';\n\teinfo->file = file;\n\teinfo->line = line;\n\teinfo->what = what;\n\n\tstrcpy(*pdo_err, einfo->last_state);\n/* printf(\"@@ SQLSTATE[%s] %s\\n\", *pdo_err, einfo->last_err_msg); */\n\tif (!dbh->methods) {\n\t\tzend_throw_exception_ex(php_pdo_get_exception(), einfo->last_error, \"SQLSTATE[%s] %s: %d %s\",\n\t\t\t\t*pdo_err, what, einfo->last_error, einfo->last_err_msg);\n\t}\n\n\t/* just like a cursor, once you start pulling, you need to keep\n\t * going until the end; SQL Server (at least) will mess with the\n\t * actual cursor state if you don't finish retrieving all the\n\t * diagnostic records (which can be generated by PRINT statements\n\t * in the query, for instance). */\n\twhile (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {\n\t\tSQLCHAR discard_state[6];\n\t\tSQLCHAR discard_buf[1024];\n\t\tSQLINTEGER code;\n\t\trc = SQLGetDiagRec(htype, eh, recno++, discard_state, &code,\n\t\t\t\tdiscard_buf, sizeof(discard_buf)-1, &errmsgsize);\n\t}\n\n}\n/* }}} */\n\nstatic void odbc_handle_closer(pdo_dbh_t *dbh)\n{\n\tpdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data;\n\n\tif (H->dbc != SQL_NULL_HANDLE) {\n\t\tSQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);\n\t\tSQLDisconnect(H->dbc);\n\t\tSQLFreeHandle(SQL_HANDLE_DBC, H->dbc);\n\t\tH->dbc = NULL;\n\t}\n\tSQLFreeHandle(SQL_HANDLE_ENV, H->env);\n\tH->env = NULL;\n\tpefree(H, dbh->is_persistent);\n\tdbh->driver_data = NULL;\n}\n\nstatic bool odbc_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options)\n{\n\tRETCODE rc;\n\tpdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;\n\tpdo_odbc_stmt *S = ecalloc(1, sizeof(*S));\n\tenum pdo_cursor_type cursor_type = PDO_CURSOR_FWDONLY;\n\tint ret;\n\tzend_string *nsql = NULL;\n\n\tS->H = H;\n\tS->assume_utf8 = H->assume_utf8;\n\n\t/* before we prepare, we need to peek at the query; if it uses named parameters,\n\t * we want PDO to rewrite them for us */\n\tstmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL;\n\tret = pdo_parse_params(stmt, sql, &nsql);\n\n\tif (ret == 1) {\n\t\t/* query was re-written */\n\t\tsql = nsql;\n\t} else if (ret == -1) {\n\t\t/* couldn't grok it */\n\t\tstrcpy(dbh->error_code, stmt->error_code);\n\t\tefree(S);\n\t\treturn false;\n\t}\n\n\trc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &S->stmt);\n\n\tif (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) {\n\t\tefree(S);\n\t\tif (nsql) {\n\t\t\tzend_string_release(nsql);\n\t\t}\n\t\tpdo_odbc_drv_error(\"SQLAllocStmt\");\n\t\treturn false;\n\t}\n\n\tstmt->driver_data = S;\n\n\tcursor_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY);\n\tif (cursor_type != PDO_CURSOR_FWDONLY) {\n\t\trc = SQLSetStmtAttr(S->stmt, SQL_ATTR_CURSOR_SCROLLABLE, (void*)SQL_SCROLLABLE, 0);\n\t\tif (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n\t\t\tpdo_odbc_stmt_error(\"SQLSetStmtAttr: SQL_ATTR_CURSOR_SCROLLABLE\");\n\t\t\tSQLFreeHandle(SQL_HANDLE_STMT, S->stmt);\n\t\t\tif (nsql) {\n\t\t\t\tzend_string_release(nsql);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\trc = SQLPrepare(S->stmt, (SQLCHAR *) ZSTR_VAL(sql), SQL_NTS);\n\tif (nsql) {\n\t\tzend_string_release(nsql);\n\t}\n\n\tstmt->methods = &swoole_odbc_stmt_methods;\n\n\tif (rc != SQL_SUCCESS) {\n\t\tpdo_odbc_stmt_error(\"SQLPrepare\");\n\t\tif (rc != SQL_SUCCESS_WITH_INFO) {\n\t\t\t/* clone error information into the db handle */\n\t\t\tstrcpy(H->einfo.last_err_msg, S->einfo.last_err_msg);\n\t\t\tH->einfo.file = S->einfo.file;\n\t\t\tH->einfo.line = S->einfo.line;\n\t\t\tH->einfo.what = S->einfo.what;\n\t\t\tstrcpy(dbh->error_code, stmt->error_code);\n\t\t}\n\t}\n\n\tif (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic zend_long odbc_handle_doer(pdo_dbh_t *dbh, const zend_string *sql)\n{\n\tpdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;\n\tRETCODE rc;\n\tSQLLEN row_count = -1;\n\tPDO_ODBC_HSTMT\tstmt;\n\n\trc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt);\n\tif (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n\t\tpdo_odbc_drv_error(\"SQLAllocHandle: STMT\");\n\t\treturn -1;\n\t}\n\n\trc = SQLExecDirect(stmt, (SQLCHAR *) ZSTR_VAL(sql), ZSTR_LEN(sql));\n\n\tif (rc == SQL_NO_DATA) {\n\t\t/* If SQLExecDirect executes a searched update or delete statement that\n\t\t * does not affect any rows at the data source, the call to\n\t\t * SQLExecDirect returns SQL_NO_DATA. */\n\t\trow_count = 0;\n\t\tgoto out;\n\t}\n\n\tif (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n\t\tpdo_odbc_doer_error(\"SQLExecDirect\");\n\t\tgoto out;\n\t}\n\n\trc = SQLRowCount(stmt, &row_count);\n\tif (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n\t\tpdo_odbc_doer_error(\"SQLRowCount\");\n\t\tgoto out;\n\t}\n\tif (row_count == -1) {\n\t\trow_count = 0;\n\t}\nout:\n\tSQLFreeHandle(SQL_HANDLE_STMT, stmt);\n\treturn row_count;\n}\n\n/* TODO: Do ODBC quoter\nstatic int odbc_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type param_type )\n{\n\t// pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;\n\t// TODO: figure it out\n\treturn 0;\n}\n*/\n\nstatic bool odbc_handle_begin(pdo_dbh_t *dbh)\n{\n\tif (dbh->auto_commit) {\n\t\t/* we need to disable auto-commit now, to be able to initiate a transaction */\n\t\tRETCODE rc;\n\t\tpdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;\n\n\t\trc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER);\n\t\tif (rc != SQL_SUCCESS) {\n\t\t\tpdo_odbc_drv_error(\"SQLSetConnectAttr AUTOCOMMIT = OFF\");\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\nstatic bool odbc_handle_commit(pdo_dbh_t *dbh)\n{\n\tpdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;\n\tRETCODE rc;\n\n\trc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT);\n\n\tif (rc != SQL_SUCCESS) {\n\t\tpdo_odbc_drv_error(\"SQLEndTran: Commit\");\n\n\t\tif (rc != SQL_SUCCESS_WITH_INFO) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (dbh->auto_commit) {\n\t\t/* turn auto-commit back on again */\n\t\trc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);\n\t\tif (rc != SQL_SUCCESS) {\n\t\t\tpdo_odbc_drv_error(\"SQLSetConnectAttr AUTOCOMMIT = ON\");\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\nstatic bool odbc_handle_rollback(pdo_dbh_t *dbh)\n{\n\tpdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;\n\tRETCODE rc;\n\n\trc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);\n\n\tif (rc != SQL_SUCCESS) {\n\t\tpdo_odbc_drv_error(\"SQLEndTran: Rollback\");\n\n\t\tif (rc != SQL_SUCCESS_WITH_INFO) {\n\t\t\treturn false;\n\t\t}\n\t}\n\tif (dbh->auto_commit && H->dbc) {\n\t\t/* turn auto-commit back on again */\n\t\trc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);\n\t\tif (rc != SQL_SUCCESS) {\n\t\t\tpdo_odbc_drv_error(\"SQLSetConnectAttr AUTOCOMMIT = ON\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic bool odbc_handle_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)\n{\n\tpdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;\n\tbool bval;\n\n\tswitch (attr) {\n\t\tcase PDO_ODBC_ATTR_ASSUME_UTF8:\n\t\t\tif (!pdo_get_bool_param(&bval, val)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tH->assume_utf8 = bval;\n\t\t\treturn true;\n\t\tcase PDO_ATTR_AUTOCOMMIT:\n\t\t\tif (!pdo_get_bool_param(&bval, val)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (dbh->in_txn) {\n\t\t\t\tpdo_raise_impl_error(dbh, NULL, \"HY000\", \"Cannot change autocommit mode while a transaction is already open\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (dbh->auto_commit ^ bval) {\n\t\t\t\tdbh->auto_commit = bval;\n\t\t\t\tRETCODE rc = SQLSetConnectAttr(\n\t\t\t\t\tH->dbc,\n\t\t\t\t\tSQL_ATTR_AUTOCOMMIT,\n\t\t\t\t\tdbh->auto_commit ? (SQLPOINTER) SQL_AUTOCOMMIT_ON : (SQLPOINTER) SQL_AUTOCOMMIT_OFF,\n\t\t\t\t\tSQL_IS_INTEGER\n\t\t\t\t);\n\t\t\t\tif (rc != SQL_SUCCESS) {\n\t\t\t\t\tpdo_odbc_drv_error(\n\t\t\t\t\t\tdbh->auto_commit ? \"SQLSetConnectAttr AUTOCOMMIT = ON\" : \"SQLSetConnectAttr AUTOCOMMIT = OFF\"\n\t\t\t\t\t);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\tdefault:\n\t\t\tstrcpy(H->einfo.last_err_msg, \"Unknown Attribute\");\n\t\t\tH->einfo.what = \"setAttribute\";\n\t\t\tstrcpy(H->einfo.last_state, \"IM001\");\n\t\t\treturn false;\n\t}\n}\n\nstatic int pdo_odbc_get_info_string(pdo_dbh_t *dbh, SQLUSMALLINT type, zval *val)\n{\n\tRETCODE rc;\n\tSQLSMALLINT out_len;\n\tchar buf[256];\n\tpdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;\n\trc = SQLGetInfo(H->dbc, type, (SQLPOINTER)buf, sizeof(buf), &out_len);\n\t/* returning -1 is treated as an error, not as unsupported */\n\tif (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n\t\treturn -1;\n\t}\n\tZVAL_STRINGL(val, buf, out_len);\n\treturn 1;\n}\n\nstatic int odbc_handle_get_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)\n{\n\tpdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;\n\tswitch (attr) {\n\t\tcase PDO_ATTR_CLIENT_VERSION:\n\t\t\tZVAL_STRING(val, \"ODBC-\" PDO_ODBC_TYPE);\n\t\t\treturn 1;\n\n\t\tcase PDO_ATTR_SERVER_VERSION:\n\t\t\treturn pdo_odbc_get_info_string(dbh, SQL_DBMS_VER, val);\n\t\tcase PDO_ATTR_SERVER_INFO:\n\t\t\treturn pdo_odbc_get_info_string(dbh, SQL_DBMS_NAME, val);\n\t\tcase PDO_ATTR_PREFETCH:\n\t\tcase PDO_ATTR_TIMEOUT:\n\t\tcase PDO_ATTR_CONNECTION_STATUS:\n\t\t\tbreak;\n\t\tcase PDO_ODBC_ATTR_ASSUME_UTF8:\n\t\t\tZVAL_BOOL(val, H->assume_utf8);\n\t\t\treturn 1;\n\t\tcase PDO_ATTR_AUTOCOMMIT:\n\t\t\tZVAL_BOOL(val, dbh->auto_commit);\n\t\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nstatic zend_result odbc_handle_check_liveness(pdo_dbh_t *dbh)\n{\n\tRETCODE ret;\n\tUCHAR d_name[32];\n\tSQLSMALLINT len;\n\tSQLUINTEGER dead = SQL_CD_FALSE;\n\tpdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;\n\n\tret = SQLGetConnectAttr(H->dbc, SQL_ATTR_CONNECTION_DEAD, &dead, 0, NULL);\n\tif (ret == SQL_SUCCESS && dead == SQL_CD_TRUE) {\n\t\t/* Bail early here, since we know it's gone */\n\t\treturn FAILURE;\n\t}\n\t/*\n\t * If the driver doesn't support SQL_ATTR_CONNECTION_DEAD, or if\n\t * it returns false (which could be a false positive), fall back\n\t * to using SQL_DATA_SOURCE_READ_ONLY, which isn't semantically\n\t * correct, but works with many drivers.\n\t */\n\tret = SQLGetInfo(H->dbc, SQL_DATA_SOURCE_READ_ONLY, d_name,\n\t\tsizeof(d_name), &len);\n\n\tif (ret != SQL_SUCCESS || len == 0) {\n\t\treturn FAILURE;\n\t}\n\treturn SUCCESS;\n}\n\nstatic const struct pdo_dbh_methods odbc_methods = {\n\todbc_handle_closer,\n\todbc_handle_preparer,\n\todbc_handle_doer,\n\tNULL, /* quoter */\n\todbc_handle_begin,\n\todbc_handle_commit,\n\todbc_handle_rollback,\n\todbc_handle_set_attr,\n\tNULL,\t/* last id */\n\tpdo_odbc_fetch_error_func,\n\todbc_handle_get_attr,\t/* get attr */\n\todbc_handle_check_liveness, /* check_liveness */\n\tNULL, /* get_driver_methods */\n\tNULL, /* request_shutdown */\n\tNULL, /* in transaction, use PDO's internal tracking mechanism */\n\tNULL, /* get_gc */\n\tNULL /* scanner */\n};\n\nstatic int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */\n{\n\tpdo_odbc_db_handle *H;\n\tRETCODE rc;\n\tint use_direct = 0;\n\tzend_ulong cursor_lib;\n\n\tH = pecalloc(1, sizeof(*H), dbh->is_persistent);\n\n\tdbh->driver_data = H;\n\n\trc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H->env);\n\tif (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n\t\tpdo_odbc_drv_error(\"SQLAllocHandle: ENV\");\n\t\tgoto fail;\n\t}\n\n\trc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);\n\n\tif (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n\t\tpdo_odbc_drv_error(\"SQLSetEnvAttr: ODBC3\");\n\t\tgoto fail;\n\t}\n\n#ifdef SQL_ATTR_CONNECTION_POOLING\n\tif (pdo_odbc_pool_on != SQL_CP_OFF) {\n\t\trc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void*)pdo_odbc_pool_mode, 0);\n\t\tif (rc != SQL_SUCCESS) {\n\t\t\tpdo_odbc_drv_error(\"SQLSetEnvAttr: SQL_ATTR_CP_MATCH\");\n\t\t\tgoto fail;\n\t\t}\n\t}\n#endif\n\n\trc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc);\n\tif (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n\t\tpdo_odbc_drv_error(\"SQLAllocHandle: DBC\");\n\t\tgoto fail;\n\t}\n\n\trc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT,\n\t\t(SQLPOINTER)(intptr_t)(dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF), SQL_IS_INTEGER);\n\tif (rc != SQL_SUCCESS) {\n\t\tpdo_odbc_drv_error(\"SQLSetConnectAttr AUTOCOMMIT\");\n\t\tgoto fail;\n\t}\n\n\t/* set up the cursor library, if needed, or if configured explicitly */\n\tcursor_lib = pdo_attr_lval(driver_options, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY, SQL_CUR_USE_IF_NEEDED);\n\trc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void*)cursor_lib, SQL_IS_INTEGER);\n\tif (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) {\n\t\tpdo_odbc_drv_error(\"SQLSetConnectAttr SQL_ODBC_CURSORS\");\n\t\tgoto fail;\n\t}\n\n\t/* a connection string may have = but not ; - i.e. \"DSN=PHP\" */\n\tif (strchr(dbh->data_source, '=')) {\n\t\tSQLCHAR dsnbuf[1024];\n\t\tSQLSMALLINT dsnbuflen;\n\n\t\tuse_direct = 1;\n\n\t\tbool use_uid_arg = dbh->username != NULL && !php_memnistr(dbh->data_source, \"uid=\", strlen(\"uid=\"), dbh->data_source + dbh->data_source_len);\n\t\tbool use_pwd_arg = dbh->password != NULL && !php_memnistr(dbh->data_source, \"pwd=\", strlen(\"pwd=\"), dbh->data_source + dbh->data_source_len);\n\n\t\tif (use_uid_arg || use_pwd_arg) {\n\t\t\tchar *db = (char*) estrndup(dbh->data_source, dbh->data_source_len);\n\t\t\tchar *db_end = db + dbh->data_source_len;\n\t\t\tdb_end--;\n\t\t\tif ((unsigned char)*(db_end) == ';') {\n\t\t\t\t*db_end = '\\0';\n\t\t\t}\n\n\t\t\tchar *uid = NULL, *pwd = NULL, *dsn = NULL;\n\t\t\tbool should_quote_uid, should_quote_pwd;\n\t\t\tsize_t new_dsn_size;\n\n\t\t\tif (use_uid_arg) {\n\t\t\t\tshould_quote_uid = !php_odbc_connstr_is_quoted(dbh->username) && php_odbc_connstr_should_quote(dbh->username);\n\t\t\t\tif (should_quote_uid) {\n\t\t\t\t\tsize_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->username);\n\t\t\t\t\tuid = emalloc(estimated_length);\n\t\t\t\t\tphp_odbc_connstr_quote(uid, dbh->username, estimated_length);\n\t\t\t\t} else {\n\t\t\t\t\tuid = dbh->username;\n\t\t\t\t}\n\n\t\t\t\tif (!use_pwd_arg) {\n\t\t\t\t\tnew_dsn_size = strlen(db) + strlen(uid) + strlen(\";UID=;\") + 1;\n\t\t\t\t\tdsn = pemalloc(new_dsn_size, dbh->is_persistent);\n\t\t\t\t\tsnprintf(dsn, new_dsn_size, \"%s;UID=%s;\", db, uid);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (use_pwd_arg) {\n\t\t\t\tshould_quote_pwd = !php_odbc_connstr_is_quoted(dbh->password) && php_odbc_connstr_should_quote(dbh->password);\n\t\t\t\tif (should_quote_pwd) {\n\t\t\t\t\tsize_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->password);\n\t\t\t\t\tpwd = emalloc(estimated_length);\n\t\t\t\t\tphp_odbc_connstr_quote(pwd, dbh->password, estimated_length);\n\t\t\t\t} else {\n\t\t\t\t\tpwd = dbh->password;\n\t\t\t\t}\n\n\t\t\t\tif (!use_uid_arg) {\n\t\t\t\t\tnew_dsn_size = strlen(db) + strlen(pwd) + strlen(\";PWD=;\") + 1;\n\t\t\t\t\tdsn = pemalloc(new_dsn_size, dbh->is_persistent);\n\t\t\t\t\tsnprintf(dsn, new_dsn_size, \"%s;PWD=%s;\", db, pwd);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (use_uid_arg && use_pwd_arg) {\n\t\t\t\tnew_dsn_size = strlen(db)\n\t\t\t\t\t+ strlen(uid) + strlen(pwd)\n\t\t\t\t\t+ strlen(\";UID=;PWD=;\") + 1;\n\t\t\t\tdsn = pemalloc(new_dsn_size, dbh->is_persistent);\n\t\t\t\tsnprintf(dsn, new_dsn_size, \"%s;UID=%s;PWD=%s;\", db, uid, pwd);\n\t\t\t}\n\n\t\t\tpefree((char*)dbh->data_source, dbh->is_persistent);\n\t\t\tdbh->data_source = dsn;\n\t\t\tdbh->data_source_len = strlen(dsn);\n\t\t\tif (uid && should_quote_uid) {\n\t\t\t\tefree(uid);\n\t\t\t}\n\t\t\tif (pwd && should_quote_pwd) {\n\t\t\t\tefree(pwd);\n\t\t\t}\n\t\t\tefree(db);\n\t\t}\n\n\t\trc = SQLDriverConnect(H->dbc, NULL, (SQLCHAR *) dbh->data_source, dbh->data_source_len,\n\t\t\t\tdsnbuf, sizeof(dsnbuf)-1, &dsnbuflen, SQL_DRIVER_NOPROMPT);\n\t}\n\tif (!use_direct) {\n\t\trc = SQLConnect(H->dbc, (SQLCHAR *) dbh->data_source, SQL_NTS, (SQLCHAR *) dbh->username, SQL_NTS, (SQLCHAR *) dbh->password, SQL_NTS);\n\t}\n\n\tif (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n\t\tpdo_odbc_drv_error(use_direct ? \"SQLDriverConnect\" : \"SQLConnect\");\n\t\tgoto fail;\n\t}\n\n\t/* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */\n\n\tdbh->methods = &odbc_methods;\n\tdbh->alloc_own_columns = 1;\n\n\treturn 1;\n\nfail:\n\tdbh->methods = &odbc_methods;\n\treturn 0;\n}\n/* }}} */\n\nconst pdo_driver_t swoole_pdo_odbc_driver = {\n\tPDO_DRIVER_HEADER(odbc),\n\tpdo_odbc_handle_factory\n};\n#endif"
  },
  {
    "path": "thirdparty/php85/pdo_odbc/odbc_stmt.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_ODBC_HOOK\n#include \"php_swoole_odbc.h\"\n\n#if PHP_VERSION_ID >= 80500\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"ext/pdo/php_pdo.h\"\n#include \"ext/pdo/php_pdo_driver.h\"\n#include \"php_pdo_odbc.h\"\n//#include \"php_pdo_odbc_int.h\"\n\n/* Buffer size; bigger columns than this become a \"long column\" */\n#define LONG_COLUMN_BUFFER_SIZE (ZEND_MM_PAGE_SIZE- ZSTR_MAX_OVERHEAD)\n\nenum pdo_odbc_conv_result {\n\tPDO_ODBC_CONV_NOT_REQUIRED,\n\tPDO_ODBC_CONV_OK,\n\tPDO_ODBC_CONV_FAIL\n};\n\nstatic int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SQLSMALLINT sqltype)\n{\n\tif (!S->assume_utf8) return 0;\n\tswitch (sqltype) {\n#ifdef SQL_WCHAR\n\t\tcase SQL_WCHAR:\n\t\t\treturn 1;\n#endif\n#ifdef SQL_WLONGVARCHAR\n\t\tcase SQL_WLONGVARCHAR:\n\t\t\treturn 1;\n#endif\n#ifdef SQL_WVARCHAR\n\t\tcase SQL_WVARCHAR:\n\t\t\treturn 1;\n#endif\n\t\tdefault:\n\t\t\treturn 0;\n\t}\n}\n\nstatic int pdo_odbc_utf82ucs2(pdo_stmt_t *stmt, int is_unicode, const char *buf,\n\tzend_ulong buflen, zend_ulong *outlen)\n{\n#ifdef PHP_WIN32\n\tif (is_unicode && buflen) {\n\t\tpdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;\n\t\tDWORD ret;\n\n\t\tret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, NULL, 0);\n\t\tif (ret == 0) {\n\t\t\t/*printf(\"%s:%d %d [%d] %.*s\\n\", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/\n\t\t\treturn PDO_ODBC_CONV_FAIL;\n\t\t}\n\n\t\tret *= sizeof(WCHAR);\n\n\t\tif (S->convbufsize <= ret) {\n\t\t\tS->convbufsize = ret + sizeof(WCHAR);\n\t\t\tS->convbuf = erealloc(S->convbuf, S->convbufsize);\n\t\t}\n\n\t\tret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, (LPWSTR)S->convbuf, S->convbufsize / sizeof(WCHAR));\n\t\tif (ret == 0) {\n\t\t\t/*printf(\"%s:%d %d [%d] %.*s\\n\", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/\n\t\t\treturn PDO_ODBC_CONV_FAIL;\n\t\t}\n\n\t\tret *= sizeof(WCHAR);\n\t\t*outlen = ret;\n\t\treturn PDO_ODBC_CONV_OK;\n\t}\n#endif\n\treturn PDO_ODBC_CONV_NOT_REQUIRED;\n}\n\nstatic int pdo_odbc_ucs22utf8(int is_unicode, zval *result)\n{\n#ifdef PHP_WIN32\n\tZEND_ASSERT(Z_TYPE_P(result) == IS_STRING);\n\tif (is_unicode && Z_STRLEN_P(result) != 0) {\n\t\tDWORD ret;\n\n\t\tret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) Z_STRVAL_P(result), Z_STRLEN_P(result)/sizeof(WCHAR), NULL, 0, NULL, NULL);\n\t\tif (ret == 0) {\n\t\t\treturn PDO_ODBC_CONV_FAIL;\n\t\t}\n\n\t\tzend_string *str = zend_string_alloc(ret, 0);\n\t\tret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) Z_STRVAL_P(result), Z_STRLEN_P(result)/sizeof(WCHAR), ZSTR_VAL(str), ZSTR_LEN(str), NULL, NULL);\n\t\tif (ret == 0) {\n\t\t\tzend_string_efree(str);\n\t\t\treturn PDO_ODBC_CONV_FAIL;\n\t\t}\n\n\t\tZSTR_VAL(str)[ret] = '\\0';\n\t\tzval_ptr_dtor_str(result);\n\t\tZVAL_STR(result, str);\n\t\treturn PDO_ODBC_CONV_OK;\n\t}\n#endif\n\treturn PDO_ODBC_CONV_NOT_REQUIRED;\n}\n\nstatic void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S)\n{\n\tif (S->cols) {\n\t\tint i;\n\n\t\tfor (i = 0; i < S->col_count; i++) {\n\t\t\tif (S->cols[i].data) {\n\t\t\t\tefree(S->cols[i].data);\n\t\t\t}\n\t\t}\n\t\tefree(S->cols);\n\t\tS->cols = NULL;\n\t\tS->col_count = 0;\n\t}\n}\n\nstatic int odbc_stmt_dtor(pdo_stmt_t *stmt)\n{\n\tpdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;\n\n\tif (S->stmt != SQL_NULL_HANDLE && php_pdo_stmt_valid_db_obj_handle(stmt)) {\n\t\tif (stmt->executed) {\n\t\t\tSQLCloseCursor(S->stmt);\n\t\t}\n\t\tSQLFreeHandle(SQL_HANDLE_STMT, S->stmt);\n\t\tS->stmt = SQL_NULL_HANDLE;\n\t}\n\n\tfree_cols(stmt, S);\n\tif (S->convbuf) {\n\t\tefree(S->convbuf);\n\t}\n\tefree(S);\n\n\treturn 1;\n}\n\nstatic int odbc_stmt_execute(pdo_stmt_t *stmt)\n{\n\tRETCODE rc, rc1;\n\tpdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;\n\tchar *buf = NULL;\n\tSQLLEN row_count = -1;\n\n\tif (stmt->executed) {\n\t\tSQLCloseCursor(S->stmt);\n\t}\n\n\trc = SQLExecute(S->stmt);\n\n\twhile (rc == SQL_NEED_DATA) {\n\t\tstruct pdo_bound_param_data *param;\n\n\t\trc = SQLParamData(S->stmt, (SQLPOINTER*)&param);\n\t\tif (rc == SQL_NEED_DATA) {\n\t\t\tphp_stream *stm;\n\t\t\tint len;\n\t\t\tpdo_odbc_param *P;\n\t\t\tzval *parameter;\n\n\t\t\tP = (pdo_odbc_param*)param->driver_data;\n\t\t\tif (Z_ISREF(param->parameter)) {\n\t\t\t\tparameter = Z_REFVAL(param->parameter);\n\t\t\t} else {\n\t\t\t\tparameter = &param->parameter;\n\t\t\t}\n\t\t\tif (Z_TYPE_P(parameter) != IS_RESOURCE) {\n\t\t\t\t/* they passed in a string */\n\t\t\t\tzend_ulong ulen;\n\t\t\t\tconvert_to_string(parameter);\n\n\t\t\t\tswitch (pdo_odbc_utf82ucs2(stmt, P->is_unicode,\n\t\t\t\t\t\t\tZ_STRVAL_P(parameter),\n\t\t\t\t\t\t\tZ_STRLEN_P(parameter),\n\t\t\t\t\t\t\t&ulen)) {\n\t\t\t\t\tcase PDO_ODBC_CONV_NOT_REQUIRED:\n\t\t\t\t\t\trc1 = SQLPutData(S->stmt, Z_STRVAL_P(parameter),\n\t\t\t\t\t\t\tZ_STRLEN_P(parameter));\n\t\t\t\t\t\tif (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {\n\t\t\t\t\t\t\trc = rc1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase PDO_ODBC_CONV_OK:\n\t\t\t\t\t\trc1 = SQLPutData(S->stmt, S->convbuf, ulen);\n\t\t\t\t\t\tif (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {\n\t\t\t\t\t\t\trc = rc1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase PDO_ODBC_CONV_FAIL:\n\t\t\t\t\t\tpdo_odbc_stmt_error(\"error converting input string\");\n\t\t\t\t\t\tSQLCloseCursor(S->stmt);\n\t\t\t\t\t\tif (buf) {\n\t\t\t\t\t\t\tefree(buf);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/* we assume that LOBs are binary and don't need charset\n\t\t\t * conversion */\n\n\t\t\tphp_stream_from_zval_no_verify(stm, parameter);\n\t\t\tif (!stm) {\n\t\t\t\t/* shouldn't happen either */\n\t\t\t\tpdo_odbc_stmt_error(\"input LOB is no longer a stream\");\n\t\t\t\tSQLCloseCursor(S->stmt);\n\t\t\t\tif (buf) {\n\t\t\t\t\tefree(buf);\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t/* now suck data from the stream and stick it into the database */\n\t\t\tif (buf == NULL) {\n\t\t\t\tbuf = emalloc(8192);\n\t\t\t}\n\n\t\t\tdo {\n\t\t\t\tlen = php_stream_read(stm, buf, 8192);\n\t\t\t\tif (len == 0) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\trc1 = SQLPutData(S->stmt, buf, len);\n\t\t\t\tif (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {\n\t\t\t\t\trc = rc1;\n\t\t\t\t}\n\t\t\t} while (1);\n\t\t}\n\t}\n\n\tif (buf) {\n\t\tefree(buf);\n\t}\n\n\tswitch (rc) {\n\t\tcase SQL_SUCCESS:\n\t\t\tbreak;\n\t\tcase SQL_NO_DATA_FOUND:\n\t\tcase SQL_SUCCESS_WITH_INFO:\n\t\t\tpdo_odbc_stmt_error(\"SQLExecute\");\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tpdo_odbc_stmt_error(\"SQLExecute\");\n\t\t\treturn 0;\n\t}\n\n\tSQLRowCount(S->stmt, &row_count);\n\tstmt->row_count = row_count;\n\n\tif (S->cols == NULL) {\n\t\t/* do first-time-only definition of bind/mapping stuff */\n\t\tSQLSMALLINT colcount;\n\n\t\t/* how many columns do we have ? */\n\t\tSQLNumResultCols(S->stmt, &colcount);\n\n\t\tstmt->column_count = S->col_count = (int)colcount;\n\t\tS->cols = ecalloc(colcount, sizeof(pdo_odbc_column));\n\t\tS->going_long = 0;\n\t}\n\n\treturn 1;\n}\n\nstatic int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,\n\t\tenum pdo_param_event event_type)\n{\n\tpdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;\n\tRETCODE rc;\n\tSQLSMALLINT sqltype = 0, ctype = 0, scale = 0, nullable = 0;\n\tSQLULEN precision = 0;\n\tpdo_odbc_param *P;\n\tzval *parameter;\n\n\t/* we're only interested in parameters for prepared SQL right now */\n\tif (param->is_param) {\n\n\t\tswitch (event_type) {\n\t\t\tcase PDO_PARAM_EVT_FETCH_PRE:\n\t\t\tcase PDO_PARAM_EVT_FETCH_POST:\n\t\t\tcase PDO_PARAM_EVT_NORMALIZE:\n\t\t\t\t/* Do nothing */\n\t\t\t\tbreak;\n\n\t\t\tcase PDO_PARAM_EVT_FREE:\n\t\t\t\tP = param->driver_data;\n\t\t\t\tif (P) {\n\t\t\t\t\tefree(P);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase PDO_PARAM_EVT_ALLOC:\n\t\t\t{\n\t\t\t\t/* figure out what we're doing */\n\t\t\t\tswitch (PDO_PARAM_TYPE(param->param_type)) {\n\t\t\t\t\tcase PDO_PARAM_LOB:\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase PDO_PARAM_STMT:\n\t\t\t\t\t\treturn 0;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\trc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno+1, &sqltype, &precision, &scale, &nullable);\n\t\t\t\tif (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n\t\t\t\t\t/* MS Access, for instance, doesn't support SQLDescribeParam,\n\t\t\t\t\t * so we need to guess */\n\t\t\t\t\tswitch (PDO_PARAM_TYPE(param->param_type)) {\n\t\t\t\t\t\tcase PDO_PARAM_INT:\n\t\t\t\t\t\t\tsqltype = SQL_INTEGER;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase PDO_PARAM_LOB:\n\t\t\t\t\t\t\tsqltype = SQL_LONGVARBINARY;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tsqltype = SQL_LONGVARCHAR;\n\t\t\t\t\t}\n\t\t\t\t\tprecision = 4000;\n\t\t\t\t\tscale = 5;\n\t\t\t\t\tnullable = 1;\n\n\t\t\t\t\tif (param->max_value_len > 0) {\n\t\t\t\t\t\tprecision = param->max_value_len;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) {\n\t\t\t\t\tctype = SQL_C_BINARY;\n\t\t\t\t} else {\n\t\t\t\t\tctype = SQL_C_CHAR;\n\t\t\t\t}\n\n\t\t\t\tP = emalloc(sizeof(*P));\n\t\t\t\tparam->driver_data = P;\n\n\t\t\t\tP->len = 0; /* is re-populated each EXEC_PRE */\n\t\t\t\tP->outbuf = NULL;\n\n\t\t\t\tP->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype);\n\t\t\t\tif (P->is_unicode) {\n\t\t\t\t\t/* avoid driver auto-translation: we'll do it ourselves */\n\t\t\t\t\tctype = SQL_C_BINARY;\n\t\t\t\t}\n\n\t\t\t\tif ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) {\n\t\t\t\t\tP->paramtype = SQL_PARAM_INPUT_OUTPUT;\n\t\t\t\t} else if (param->max_value_len <= 0) {\n\t\t\t\t\tP->paramtype = SQL_PARAM_INPUT;\n\t\t\t\t} else {\n\t\t\t\t\tP->paramtype = SQL_PARAM_OUTPUT;\n\t\t\t\t}\n\n\t\t\t\tif (P->paramtype != SQL_PARAM_INPUT) {\n\t\t\t\t\tif (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) {\n\t\t\t\t\t\t/* need an explicit buffer to hold result */\n\t\t\t\t\t\tP->len = param->max_value_len > 0 ? param->max_value_len : precision;\n\t\t\t\t\t\tif (P->is_unicode) {\n\t\t\t\t\t\t\tP->len *= 2;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tP->outbuf = emalloc(P->len + (P->is_unicode ? 2:1));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) {\n\t\t\t\t\tpdo_odbc_stmt_error(\"Can't bind a lob for output\");\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\n\t\t\t\trc = SQLBindParameter(S->stmt, (SQLUSMALLINT) param->paramno+1,\n\t\t\t\t\t\tP->paramtype, ctype, sqltype, precision, scale,\n\t\t\t\t\t\tP->paramtype == SQL_PARAM_INPUT ?\n\t\t\t\t\t\t\t(SQLPOINTER)param :\n\t\t\t\t\t\t\tP->outbuf,\n\t\t\t\t\t\tP->len,\n\t\t\t\t\t\t&P->len\n\t\t\t\t\t\t);\n\n\t\t\t\tif (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tpdo_odbc_stmt_error(\"SQLBindParameter\");\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tcase PDO_PARAM_EVT_EXEC_PRE:\n\t\t\t\tP = param->driver_data;\n\t\t\t\tif (!Z_ISREF(param->parameter)) {\n\t\t\t\t\tparameter = &param->parameter;\n\t\t\t\t} else {\n\t\t\t\t\tparameter = Z_REFVAL(param->parameter);\n\t\t\t\t}\n\n\t\t\t\tif (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {\n\t\t\t\t\tif (Z_TYPE_P(parameter) == IS_RESOURCE) {\n\t\t\t\t\t\tphp_stream *stm;\n\t\t\t\t\t\tphp_stream_statbuf sb;\n\n\t\t\t\t\t\tphp_stream_from_zval_no_verify(stm, parameter);\n\n\t\t\t\t\t\tif (!stm) {\n\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (0 == php_stream_stat(stm, &sb)) {\n\t\t\t\t\t\t\tif (P->outbuf) {\n\t\t\t\t\t\t\t\tint len, amount;\n\t\t\t\t\t\t\t\tchar *ptr = P->outbuf;\n\t\t\t\t\t\t\t\tchar *end = P->outbuf + P->len;\n\n\t\t\t\t\t\t\t\tP->len = 0;\n\t\t\t\t\t\t\t\tdo {\n\t\t\t\t\t\t\t\t\tamount = end - ptr;\n\t\t\t\t\t\t\t\t\tif (amount == 0) {\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tif (amount > 8192)\n\t\t\t\t\t\t\t\t\t\tamount = 8192;\n\t\t\t\t\t\t\t\t\tlen = php_stream_read(stm, ptr, amount);\n\t\t\t\t\t\t\t\t\tif (len == 0) {\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tptr += len;\n\t\t\t\t\t\t\t\t\tP->len += len;\n\t\t\t\t\t\t\t\t} while (1);\n\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tP->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (P->outbuf) {\n\t\t\t\t\t\t\t\tP->len = 0;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tP->len = SQL_LEN_DATA_AT_EXEC(0);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconvert_to_string(parameter);\n\t\t\t\t\t\tif (P->outbuf) {\n\t\t\t\t\t\t\tP->len = Z_STRLEN_P(parameter);\n\t\t\t\t\t\t\tmemcpy(P->outbuf, Z_STRVAL_P(parameter), P->len);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tP->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (Z_TYPE_P(parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) {\n\t\t\t\t\tP->len = SQL_NULL_DATA;\n\t\t\t\t} else {\n\t\t\t\t\tconvert_to_string(parameter);\n\t\t\t\t\tif (P->outbuf) {\n\t\t\t\t\t\tzend_ulong ulen;\n\t\t\t\t\t\tswitch (pdo_odbc_utf82ucs2(stmt, P->is_unicode,\n\t\t\t\t\t\t\t\tZ_STRVAL_P(parameter),\n\t\t\t\t\t\t\t\tZ_STRLEN_P(parameter),\n\t\t\t\t\t\t\t\t&ulen)) {\n\t\t\t\t\t\t\tcase PDO_ODBC_CONV_FAIL:\n\t\t\t\t\t\t\tcase PDO_ODBC_CONV_NOT_REQUIRED:\n\t\t\t\t\t\t\t\tP->len = Z_STRLEN_P(parameter);\n\t\t\t\t\t\t\t\tmemcpy(P->outbuf, Z_STRVAL_P(parameter), P->len);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase PDO_ODBC_CONV_OK:\n\t\t\t\t\t\t\t\tP->len = ulen;\n\t\t\t\t\t\t\t\tmemcpy(P->outbuf, S->convbuf, P->len);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tP->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn 1;\n\n\t\t\tcase PDO_PARAM_EVT_EXEC_POST:\n\t\t\t\tP = param->driver_data;\n\n\t\t\t\tif (P->outbuf) {\n\t\t\t\t\tif (Z_ISREF(param->parameter)) {\n\t\t\t\t\t\tparameter = Z_REFVAL(param->parameter);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tparameter = &param->parameter;\n\t\t\t\t\t}\n\t\t\t\t\tzval_ptr_dtor(parameter);\n\n\t\t\t\t\tif (P->len >= 0) {\n\t\t\t\t\t\t\tZVAL_STRINGL(parameter, P->outbuf, P->len);\n\t\t\t\t\t\t\tswitch (pdo_odbc_ucs22utf8(P->is_unicode, parameter)) {\n\t\t\t\t\t\t\t\tcase PDO_ODBC_CONV_FAIL:\n\t\t\t\t\t\t\t\t\t/* something fishy, but allow it to come back as binary */\n\t\t\t\t\t\t\t\tcase PDO_ODBC_CONV_NOT_REQUIRED:\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase PDO_ODBC_CONV_OK:\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tZVAL_NULL(parameter);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn 1;\n\t\t}\n\t}\n\treturn 1;\n}\n\nstatic int odbc_stmt_fetch(pdo_stmt_t *stmt,\n\tenum pdo_fetch_orientation ori, zend_long offset)\n{\n\tRETCODE rc;\n\tSQLSMALLINT odbcori;\n\tpdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;\n\n\tswitch (ori) {\n\t\tcase PDO_FETCH_ORI_NEXT:\todbcori = SQL_FETCH_NEXT; break;\n\t\tcase PDO_FETCH_ORI_PRIOR:\todbcori = SQL_FETCH_PRIOR; break;\n\t\tcase PDO_FETCH_ORI_FIRST:\todbcori = SQL_FETCH_FIRST; break;\n\t\tcase PDO_FETCH_ORI_LAST:\todbcori = SQL_FETCH_LAST; break;\n\t\tcase PDO_FETCH_ORI_ABS:\t\todbcori = SQL_FETCH_ABSOLUTE; break;\n\t\tcase PDO_FETCH_ORI_REL:\t\todbcori = SQL_FETCH_RELATIVE; break;\n\t\tdefault:\n\t\t\tstrcpy(stmt->error_code, \"HY106\");\n\t\t\treturn 0;\n\t}\n\trc = SQLFetchScroll(S->stmt, odbcori, offset);\n\n\tif (rc == SQL_SUCCESS) {\n\t\treturn 1;\n\t}\n\tif (rc == SQL_SUCCESS_WITH_INFO) {\n\t\tpdo_odbc_stmt_error(\"SQLFetchScroll\");\n\t\treturn 1;\n\t}\n\n\tif (rc == SQL_NO_DATA) {\n\t\t/* pdo_odbc_stmt_error(\"SQLFetchScroll\"); */\n\t\treturn 0;\n\t}\n\n\tpdo_odbc_stmt_error(\"SQLFetchScroll\");\n\n\treturn 0;\n}\n\nstatic int odbc_stmt_describe(pdo_stmt_t *stmt, int colno)\n{\n\tpdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;\n\tstruct pdo_column_data *col = &stmt->columns[colno];\n\tRETCODE rc;\n\tSQLSMALLINT colnamelen;\n\tSQLULEN\tcolsize;\n\tSQLLEN displaysize = 0;\n\n\trc = SQLDescribeCol(S->stmt, colno+1, (SQLCHAR *) S->cols[colno].colname,\n\t\t\tsizeof(S->cols[colno].colname)-1, &colnamelen,\n\t\t\t&S->cols[colno].coltype, &colsize, NULL, NULL);\n\n\t/* This fixes a known issue with SQL Server and (max) lengths,\n\tmay affect others as well.  If we are SQL_VARCHAR,\n\tSQL_VARBINARY, or SQL_WVARCHAR (or any of the long variations)\n\tand zero is returned from colsize then consider it long */\n\tif (0 == colsize &&\n\t\t(S->cols[colno].coltype == SQL_VARCHAR ||\n\t\t S->cols[colno].coltype == SQL_LONGVARCHAR ||\n#ifdef SQL_WVARCHAR\n\t\t S->cols[colno].coltype == SQL_WVARCHAR ||\n#endif\n#ifdef SQL_WLONGVARCHAR\n\t\t S->cols[colno].coltype == SQL_WLONGVARCHAR ||\n#endif\n\t\t S->cols[colno].coltype == SQL_VARBINARY ||\n\t\t S->cols[colno].coltype == SQL_LONGVARBINARY)) {\n\t\t\t S->going_long = 1;\n\t}\n\n\tif (rc != SQL_SUCCESS) {\n\t\tpdo_odbc_stmt_error(\"SQLDescribeCol\");\n\t\tif (rc != SQL_SUCCESS_WITH_INFO) {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\trc = SQLColAttribute(S->stmt, colno+1,\n\t\t\tSQL_DESC_DISPLAY_SIZE,\n\t\t\tNULL, 0, NULL, &displaysize);\n\n\tif (rc != SQL_SUCCESS) {\n\t\tpdo_odbc_stmt_error(\"SQLColAttribute\");\n\t\tif (rc != SQL_SUCCESS_WITH_INFO) {\n\t\t\treturn 0;\n\t\t}\n\t}\n\tcolsize = displaysize;\n\n\tcol->maxlen = S->cols[colno].datalen = colsize;\n\tcol->name = zend_string_init(S->cols[colno].colname, colnamelen, 0);\n\tS->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype);\n\n\t/* tell ODBC to put it straight into our buffer, but only if it\n\t * isn't \"long\" data, and only if we haven't already bound a long\n\t * column. */\n\tif (colsize < LONG_COLUMN_BUFFER_SIZE && !S->going_long) {\n\t\tS->cols[colno].data = emalloc(colsize+1);\n\t\tS->cols[colno].is_long = 0;\n\n\t\trc = SQLBindCol(S->stmt, colno+1,\n\t\t\tS->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR,\n\t\t\tS->cols[colno].data,\n\t\t\tS->cols[colno].datalen+1, &S->cols[colno].fetched_len);\n\n\t\tif (rc != SQL_SUCCESS) {\n\t\t\tpdo_odbc_stmt_error(\"SQLBindCol\");\n\t\t\treturn 0;\n\t\t}\n\t} else {\n\t\t/* allocate a smaller buffer to keep around for smaller\n\t\t * \"long\" columns */\n\t\tS->cols[colno].data = emalloc(LONG_COLUMN_BUFFER_SIZE);\n\t\tS->going_long = 1;\n\t\tS->cols[colno].is_long = 1;\n\t}\n\n\treturn 1;\n}\n\nstatic int odbc_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)\n{\n\tarray_init(return_value);\n\tadd_assoc_long(return_value, \"pdo_type\", PDO_PARAM_STR);\n\treturn 1;\n}\n\nstatic int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type)\n{\n\tpdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;\n\tpdo_odbc_column *C = &S->cols[colno];\n\n\t/* if it is a column containing \"long\" data, perform late binding now */\n\tif (C->is_long) {\n\t\tSQLLEN orig_fetched_len = SQL_NULL_DATA;\n\t\tRETCODE rc;\n\n\t\t/* fetch it into C->data, which is allocated with a length\n\t\t * of the page size minus zend_string overhead (LONG_COLUMN_BUFFER_SIZE);\n\t\t * if there is more to be had, we then allocate\n\t\t * bigger buffer for the caller to free */\n\n\t\trc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data,\n \t\t\tLONG_COLUMN_BUFFER_SIZE, &C->fetched_len);\n\t\torig_fetched_len = C->fetched_len;\n\n\t\tif (rc == SQL_SUCCESS && C->fetched_len < LONG_COLUMN_BUFFER_SIZE) {\n\t\t\t/* all the data fit into our little buffer;\n\t\t\t * jump down to the generic bound data case */\n\t\t\tgoto in_data;\n\t\t}\n\n\t\tif (rc == SQL_SUCCESS_WITH_INFO || rc == SQL_SUCCESS) {\n\t\t\t/*\n\t\t\t * This is a long column.\n\t\t\t *\n\t\t\t * Try to get as much as we can at once. If the\n\t\t\t * driver somehow has more for us, get more. We'll\n\t\t\t * assemble it into one big buffer at the end.\n\t\t\t *\n\t\t\t * N.B. with n and n+1 mentioned in the comments:\n\t\t\t * n is the size returned without null terminator.\n\t\t\t *\n\t\t\t * The extension previously tried getting it in 256\n\t\t\t * byte blocks, but this could have created trouble\n\t\t\t * with some drivers.\n\t\t\t *\n\t\t\t * However, depending on the driver, fetched_len may\n\t\t\t * not contain the number of bytes and SQL_NO_TOTAL\n\t\t\t * may be passed.\n\t\t\t * The behavior in this case is the same as before,\n\t\t\t * dividing the data into blocks. However, it has been\n\t\t\t * changed from 256 byte to LONG_COLUMN_BUFFER_SIZE.\n\t\t\t */\n\t\t\tssize_t to_fetch_len;\n\t\t\tif (orig_fetched_len == SQL_NO_TOTAL) {\n\t\t\t\tto_fetch_len = C->datalen > (LONG_COLUMN_BUFFER_SIZE - 1) ? (LONG_COLUMN_BUFFER_SIZE - 1) : C->datalen;\n\t\t\t} else {\n\t\t\t\tto_fetch_len = orig_fetched_len;\n\t\t\t}\n\t\t\tssize_t to_fetch_byte = to_fetch_len + 1;\n\t\t\tchar *buf2 = emalloc(to_fetch_byte);\n\t\t\tzend_string *str = zend_string_init(C->data, to_fetch_byte, 0);\n\t\t\tsize_t used = to_fetch_len;\n\n\t\t\tdo {\n\t\t\t\tC->fetched_len = 0;\n\t\t\t\t/* read block. n + 1 bytes => n bytes are actually read, the last 1 is NULL */\n\t\t\t\trc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, buf2, to_fetch_byte, &C->fetched_len);\n\n\t\t\t\t/* adjust `used` in case we have proper length info from the driver */\n\t\t\t\tif (orig_fetched_len >= 0 && C->fetched_len >= 0) {\n\t\t\t\t\tSQLLEN fixed_used = orig_fetched_len - C->fetched_len;\n\t\t\t\t\tif (fixed_used <= used + 1) {\n\t\t\t\t\t\tused = fixed_used;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/* resize output buffer and reassemble block */\n\t\t\t\tif (rc==SQL_SUCCESS_WITH_INFO || (rc==SQL_SUCCESS && C->fetched_len > to_fetch_len)) {\n\t\t\t\t\t/* point 5, in section \"Retrieving Data with SQLGetData\" in http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx\n\t\t\t\t\t states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > n (greater than buf2's size)\n\t\t\t\t\t (if a driver fails to follow that and wrote less than n bytes to buf2, this will AV or read garbage into buf) */\n\t\t\t\t\tstr = zend_string_realloc(str, used + to_fetch_byte, 0);\n\t\t\t\t\tmemcpy(ZSTR_VAL(str) + used, buf2, to_fetch_byte);\n\t\t\t\t\tused = used + to_fetch_len;\n\t\t\t\t} else if (rc==SQL_SUCCESS) {\n\t\t\t\t\tstr = zend_string_realloc(str, used + C->fetched_len, 0);\n\t\t\t\t\tmemcpy(ZSTR_VAL(str) + used, buf2, C->fetched_len);\n\t\t\t\t\tused = used + C->fetched_len;\n\t\t\t\t} else {\n\t\t\t\t\t/* includes SQL_NO_DATA */\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t} while (1);\n\n\t\t\tefree(buf2);\n\n\t\t\t/* NULL terminate the buffer once, when finished, for use with the rest of PHP */\n\t\t\tZSTR_VAL(str)[used] = '\\0';\n\t\t\tZVAL_STR(result, str);\n\t\t\tif (C->is_unicode) {\n\t\t\t\tgoto unicode_conv;\n\t\t\t}\n\t\t\treturn 1;\n\t\t}\n\n\t\t/* something went caca */\n\t\treturn 1;\n\t}\n\nin_data:\n\t/* check the indicator to ensure that the data is intact */\n\tif (C->fetched_len == SQL_NULL_DATA) {\n\t\t/* A NULL value */\n\t\tZVAL_NULL(result);\n\t\treturn 1;\n\t} else if (C->fetched_len >= 0) {\n\t\t/* it was stored perfectly */\n\t\tZVAL_STRINGL_FAST(result, C->data, C->fetched_len);\n\t\tif (C->is_unicode) {\n\t\t\tgoto unicode_conv;\n\t\t}\n\t\treturn 1;\n\t} else {\n\t\t/* no data? */\n\t\tZVAL_NULL(result);\n\t\treturn 1;\n\t}\n\nunicode_conv:\n\tswitch (pdo_odbc_ucs22utf8(C->is_unicode, result)) {\n\t\tcase PDO_ODBC_CONV_FAIL:\n\t\t\t/* oh well.  They can have the binary version of it */\n\t\tcase PDO_ODBC_CONV_NOT_REQUIRED:\n\t\t\t/* shouldn't happen... */\n\t\t\treturn 1;\n\t\tcase PDO_ODBC_CONV_OK:\n\t\t\treturn 1;\n\t}\n\treturn 1;\n}\n\nstatic int odbc_stmt_set_param(pdo_stmt_t *stmt, zend_long attr, zval *val)\n{\n\tSQLRETURN rc;\n\tpdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;\n\n\tswitch (attr) {\n\t\tcase PDO_ATTR_CURSOR_NAME:\n\t\t\tconvert_to_string(val);\n\t\t\trc = SQLSetCursorName(S->stmt, (SQLCHAR *) Z_STRVAL_P(val), Z_STRLEN_P(val));\n\n\t\t\tif (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tpdo_odbc_stmt_error(\"SQLSetCursorName\");\n\t\t\treturn 0;\n\n\t\tcase PDO_ODBC_ATTR_ASSUME_UTF8:\n\t\t\tS->assume_utf8 = zend_is_true(val);\n\t\t\treturn 0;\n\t\tdefault:\n\t\t\tstrcpy(S->einfo.last_err_msg, \"Unknown Attribute\");\n\t\t\tS->einfo.what = \"setAttribute\";\n\t\t\tstrcpy(S->einfo.last_state, \"IM001\");\n\t\t\treturn -1;\n\t}\n}\n\nstatic int odbc_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val)\n{\n\tSQLRETURN rc;\n\tpdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;\n\n\tswitch (attr) {\n\t\tcase PDO_ATTR_CURSOR_NAME:\n\t\t{\n\t\t\tchar buf[256];\n\t\t\tSQLSMALLINT len = 0;\n\t\t\trc = SQLGetCursorName(S->stmt, (SQLCHAR *) buf, sizeof(buf), &len);\n\n\t\t\tif (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {\n\t\t\t\tZVAL_STRINGL(val, buf, len);\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tpdo_odbc_stmt_error(\"SQLGetCursorName\");\n\t\t\treturn 0;\n\t\t}\n\n\t\tcase PDO_ODBC_ATTR_ASSUME_UTF8:\n\t\t\tZVAL_BOOL(val, S->assume_utf8 ? 1 : 0);\n\t\t\treturn 0;\n\n\t\tdefault:\n\t\t\tstrcpy(S->einfo.last_err_msg, \"Unknown Attribute\");\n\t\t\tS->einfo.what = \"getAttribute\";\n\t\t\tstrcpy(S->einfo.last_state, \"IM001\");\n\t\t\treturn -1;\n\t}\n}\n\nstatic int odbc_stmt_next_rowset(pdo_stmt_t *stmt)\n{\n\tSQLRETURN rc;\n\tSQLSMALLINT colcount;\n\tpdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;\n\n\t/* NOTE: can't guarantee that output or input/output parameters\n\t * are set until this fella returns SQL_NO_DATA, according to\n\t * MSDN ODBC docs */\n\trc = SQLMoreResults(S->stmt);\n\n\tif (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n\t\treturn 0;\n\t}\n\n\tfree_cols(stmt, S);\n\t/* how many columns do we have ? */\n\tSQLNumResultCols(S->stmt, &colcount);\n\tstmt->column_count = S->col_count = (int)colcount;\n\tS->cols = ecalloc(colcount, sizeof(pdo_odbc_column));\n\tS->going_long = 0;\n\n\treturn 1;\n}\n\nstatic int odbc_stmt_close_cursor(pdo_stmt_t *stmt)\n{\n\tSQLRETURN rc;\n\tpdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;\n\n\trc = SQLCloseCursor(S->stmt);\n\tif (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {\n\t\treturn 0;\n\t}\n\treturn 1;\n}\n\nconst struct pdo_stmt_methods swoole_odbc_stmt_methods = {\n\todbc_stmt_dtor,\n\todbc_stmt_execute,\n\todbc_stmt_fetch,\n\todbc_stmt_describe,\n\todbc_stmt_get_col,\n\todbc_stmt_param_hook,\n\todbc_stmt_set_param,\n\todbc_stmt_get_attr,\n\todbc_stmt_get_column_meta,\n\todbc_stmt_next_rowset,\n\todbc_stmt_close_cursor\n};\n#endif"
  },
  {
    "path": "thirdparty/php85/pdo_odbc/php_pdo_odbc.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef PHP_PDO_ODBC_H\n#define PHP_PDO_ODBC_H\n\nextern zend_module_entry pdo_odbc_module_entry;\n#define phpext_pdo_odbc_ptr &pdo_odbc_module_entry\n\n#include \"php_version.h\"\n#define PHP_PDO_ODBC_VERSION PHP_VERSION\n\n#ifdef ZTS\n#include \"TSRM.h\"\n#endif\n\nPHP_MINIT_FUNCTION(pdo_odbc);\nPHP_MSHUTDOWN_FUNCTION(pdo_odbc);\nPHP_RINIT_FUNCTION(pdo_odbc);\nPHP_RSHUTDOWN_FUNCTION(pdo_odbc);\nPHP_MINFO_FUNCTION(pdo_odbc);\n\n#endif\t/* PHP_PDO_ODBC_H */\n"
  },
  {
    "path": "thirdparty/php85/pdo_odbc/php_pdo_odbc_int.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Wez Furlong <wez@php.net>                                    |\n  +----------------------------------------------------------------------+\n*/\n\n/* internal header; not supposed to be installed */\n\n#ifdef PHP_WIN32\n# define PDO_ODBC_TYPE\t\"Win32\"\n#endif\n\n#ifndef PDO_ODBC_TYPE\n# warning Please fix configure to give your ODBC libraries a name\n# define PDO_ODBC_TYPE\t\"Unknown\"\n#endif\n\n/* {{{ Roll a dice, pick a header at random... */\n#ifdef HAVE_SQLCLI1_H\n# include <sqlcli1.h>\n# if defined(DB268K) && HAVE_LIBRARYMANAGER_H\n#  include <LibraryManager.h>\n# endif\n#endif\n\n#ifdef HAVE_ODBC_H\n# include <odbc.h>\n#endif\n\n#ifdef HAVE_IODBC_H\n# include <iodbc.h>\n#endif\n\n#if defined(HAVE_SQLUNIX_H) && !defined(PHP_WIN32)\n# include <sqlunix.h>\n#endif\n\n#ifdef HAVE_SQLTYPES_H\n# include <sqltypes.h>\n#endif\n\n#ifdef HAVE_SQLUCODE_H\n# include <sqlucode.h>\n#endif\n\n#ifdef HAVE_SQL_H\n# include <sql.h>\n#endif\n\n#ifdef HAVE_ISQL_H\n# include <isql.h>\n#endif\n\n#ifdef HAVE_SQLEXT_H\n# include <sqlext.h>\n#endif\n\n#ifdef HAVE_ISQLEXT_H\n# include <isqlext.h>\n#endif\n\n#ifdef HAVE_UDBCEXT_H\n# include <udbcext.h>\n#endif\n\n#ifdef HAVE_CLI0CORE_H\n# include <cli0core.h>\n#endif\n\n#ifdef HAVE_CLI0EXT1_H\n# include <cli0ext.h>\n#endif\n\n#ifdef HAVE_CLI0CLI_H\n# include <cli0cli.h>\n#endif\n\n#ifdef HAVE_CLI0DEFS_H\n# include <cli0defs.h>\n#endif\n\n#ifdef HAVE_CLI0ENV_H\n# include <cli0env.h>\n#endif\n\n/* }}} */\n\n/* {{{ Figure out the type for handles */\n#if !defined(HENV) && !defined(SQLHENV) && defined(SQLHANDLE)\n# define PDO_ODBC_HENV\t\tSQLHANDLE\n# define PDO_ODBC_HDBC\t\tSQLHANDLE\n# define PDO_ODBC_HSTMT\t\tSQLHANDLE\n#elif !defined(HENV) && (defined(SQLHENV) || defined(DB2CLI_VER))\n# define PDO_ODBC_HENV\t\tSQLHENV\n# define PDO_ODBC_HDBC\t\tSQLHDBC\n# define PDO_ODBC_HSTMT\t\tSQLHSTMT\n#else\n# define PDO_ODBC_HENV\t\tHENV\n# define PDO_ODBC_HDBC\t\tHDBC\n# define PDO_ODBC_HSTMT\t\tHSTMT\n#endif\n/* }}} */\n\ntypedef struct {\n\tchar last_state[6];\n\tchar last_err_msg[SQL_MAX_MESSAGE_LENGTH];\n\tSQLINTEGER last_error;\n\tconst char *file, *what;\n\tint line;\n} pdo_odbc_errinfo;\n\ntypedef struct {\n\tPDO_ODBC_HENV\tenv;\n\tPDO_ODBC_HDBC\tdbc;\n\tpdo_odbc_errinfo einfo;\n\tunsigned assume_utf8:1;\n\tunsigned _spare:31;\n} pdo_odbc_db_handle;\n\ntypedef struct {\n\tchar *data;\n\tzend_ulong datalen;\n\tSQLLEN fetched_len;\n\tSQLSMALLINT coltype;\n\tchar colname[128];\n\tunsigned is_long;\n\tunsigned is_unicode:1;\n\tunsigned _spare:31;\n} pdo_odbc_column;\n\ntypedef struct {\n\tPDO_ODBC_HSTMT\tstmt;\n\tpdo_odbc_column *cols;\n\tpdo_odbc_db_handle *H;\n\tpdo_odbc_errinfo einfo;\n\tchar *convbuf;\n\tzend_ulong convbufsize;\n\tunsigned going_long:1;\n\tunsigned assume_utf8:1;\n\tsigned col_count:16;\n\tunsigned _spare:14;\n} pdo_odbc_stmt;\n\ntypedef struct {\n\tSQLLEN len;\n\tSQLSMALLINT paramtype;\n\tchar *outbuf;\n\tunsigned is_unicode:1;\n\tunsigned _spare:31;\n} pdo_odbc_param;\n\nextern const pdo_driver_t pdo_odbc_driver;\nextern const struct pdo_stmt_methods swoole_odbc_stmt_methods;\n\nvoid pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line);\n#define pdo_odbc_drv_error(what)\tpdo_odbc_error(dbh, NULL, SQL_NULL_HSTMT, what, __FILE__, __LINE__)\n#define pdo_odbc_stmt_error(what)\tpdo_odbc_error(stmt->dbh, stmt, SQL_NULL_HSTMT, what, __FILE__, __LINE__)\n#define pdo_odbc_doer_error(what)\tpdo_odbc_error(dbh, NULL, stmt, what, __FILE__, __LINE__)\n\nvoid pdo_odbc_init_error_table(void);\nvoid pdo_odbc_fini_error_table(void);\n\n#ifdef SQL_ATTR_CONNECTION_POOLING\nextern zend_ulong pdo_odbc_pool_on;\nextern zend_ulong pdo_odbc_pool_mode;\n#endif\n\nenum {\n\tPDO_ODBC_ATTR_USE_CURSOR_LIBRARY = PDO_ATTR_DRIVER_SPECIFIC,\n\tPDO_ODBC_ATTR_ASSUME_UTF8 /* assume that input strings are UTF-8 when feeding data to unicode columns */\n};\n"
  },
  {
    "path": "thirdparty/php85/pdo_pgsql/pdo_pgsql_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 0ea21010467d661416f0858f2bda095583ea3a36 */\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Pdo_Pgsql_escapeIdentifier, 0, 1, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, input, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Pdo_Pgsql_copyFromArray, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, rows, IS_ARRAY, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, \"\\\"\\\\t\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, \"\\\"\\\\\\\\\\\\\\\\N\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Pdo_Pgsql_copyFromFile, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, \"\\\"\\\\t\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, \"\\\"\\\\\\\\\\\\\\\\N\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Pdo_Pgsql_copyToArray, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, \"\\\"\\\\t\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, \"\\\"\\\\\\\\\\\\\\\\N\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_Pdo_Pgsql_copyToFile arginfo_class_Pdo_Pgsql_copyFromFile\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Pdo_Pgsql_lobCreate, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_Pdo_Pgsql_lobOpen, 0, 0, 1)\n\tZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 0, \"\\\"rb\\\"\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Pdo_Pgsql_lobUnlink, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Pdo_Pgsql_getNotify, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fetchMode, IS_LONG, 0, \"PDO::FETCH_DEFAULT\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeoutMilliseconds, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Pdo_Pgsql_getPid, 0, 0, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Pdo_Pgsql_setNoticeCallback, 0, 1, IS_VOID, 0)\n\tZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 1)\nZEND_END_ARG_INFO()\n\nZEND_METHOD(Pdo_Pgsql, escapeIdentifier);\nZEND_METHOD(Pdo_Pgsql, copyFromArray);\nZEND_METHOD(Pdo_Pgsql, copyFromFile);\nZEND_METHOD(Pdo_Pgsql, copyToArray);\nZEND_METHOD(Pdo_Pgsql, copyToFile);\nZEND_METHOD(Pdo_Pgsql, lobCreate);\nZEND_METHOD(Pdo_Pgsql, lobOpen);\nZEND_METHOD(Pdo_Pgsql, lobUnlink);\nZEND_METHOD(Pdo_Pgsql, getNotify);\nZEND_METHOD(Pdo_Pgsql, getPid);\nZEND_METHOD(Pdo_Pgsql, setNoticeCallback);\n\nstatic const zend_function_entry class_Pdo_Pgsql_methods[] = {\n\tZEND_ME(Pdo_Pgsql, escapeIdentifier, arginfo_class_Pdo_Pgsql_escapeIdentifier, ZEND_ACC_PUBLIC)\n\tZEND_ME(Pdo_Pgsql, copyFromArray, arginfo_class_Pdo_Pgsql_copyFromArray, ZEND_ACC_PUBLIC)\n\tZEND_ME(Pdo_Pgsql, copyFromFile, arginfo_class_Pdo_Pgsql_copyFromFile, ZEND_ACC_PUBLIC)\n\tZEND_ME(Pdo_Pgsql, copyToArray, arginfo_class_Pdo_Pgsql_copyToArray, ZEND_ACC_PUBLIC)\n\tZEND_ME(Pdo_Pgsql, copyToFile, arginfo_class_Pdo_Pgsql_copyToFile, ZEND_ACC_PUBLIC)\n\tZEND_ME(Pdo_Pgsql, lobCreate, arginfo_class_Pdo_Pgsql_lobCreate, ZEND_ACC_PUBLIC)\n\tZEND_ME(Pdo_Pgsql, lobOpen, arginfo_class_Pdo_Pgsql_lobOpen, ZEND_ACC_PUBLIC)\n\tZEND_ME(Pdo_Pgsql, lobUnlink, arginfo_class_Pdo_Pgsql_lobUnlink, ZEND_ACC_PUBLIC)\n\tZEND_ME(Pdo_Pgsql, getNotify, arginfo_class_Pdo_Pgsql_getNotify, ZEND_ACC_PUBLIC)\n\tZEND_ME(Pdo_Pgsql, getPid, arginfo_class_Pdo_Pgsql_getPid, ZEND_ACC_PUBLIC)\n\tZEND_ME(Pdo_Pgsql, setNoticeCallback, arginfo_class_Pdo_Pgsql_setNoticeCallback, ZEND_ACC_PUBLIC)\n\tZEND_FE_END\n};\n\nstatic zend_class_entry *register_class_Pdo_Pgsql(zend_class_entry *class_entry_PDO)\n{\n\tzend_class_entry ce, *class_entry;\n\n\tINIT_NS_CLASS_ENTRY(ce, \"Pdo\", \"Pgsql\", class_Pdo_Pgsql_methods);\n\tclass_entry = zend_register_internal_class_with_flags(&ce, class_entry_PDO, ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE);\n\n\tzval const_ATTR_DISABLE_PREPARES_value;\n\tZVAL_LONG(&const_ATTR_DISABLE_PREPARES_value, PDO_PGSQL_ATTR_DISABLE_PREPARES);\n\tzend_string *const_ATTR_DISABLE_PREPARES_name = zend_string_init_interned(\"ATTR_DISABLE_PREPARES\", sizeof(\"ATTR_DISABLE_PREPARES\") - 1, true);\n\tzend_declare_typed_class_constant(class_entry, const_ATTR_DISABLE_PREPARES_name, &const_ATTR_DISABLE_PREPARES_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));\n\tzend_string_release_ex(const_ATTR_DISABLE_PREPARES_name, true);\n#if defined(HAVE_PG_RESULT_MEMORY_SIZE)\n\n\tzval const_ATTR_RESULT_MEMORY_SIZE_value;\n\tZVAL_LONG(&const_ATTR_RESULT_MEMORY_SIZE_value, PDO_PGSQL_ATTR_RESULT_MEMORY_SIZE);\n\tzend_string *const_ATTR_RESULT_MEMORY_SIZE_name = zend_string_init_interned(\"ATTR_RESULT_MEMORY_SIZE\", sizeof(\"ATTR_RESULT_MEMORY_SIZE\") - 1, true);\n\tzend_declare_typed_class_constant(class_entry, const_ATTR_RESULT_MEMORY_SIZE_name, &const_ATTR_RESULT_MEMORY_SIZE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));\n\tzend_string_release_ex(const_ATTR_RESULT_MEMORY_SIZE_name, true);\n#endif\n\n\tzval const_TRANSACTION_IDLE_value;\n\tZVAL_LONG(&const_TRANSACTION_IDLE_value, PGSQL_TRANSACTION_IDLE);\n\tzend_string *const_TRANSACTION_IDLE_name = zend_string_init_interned(\"TRANSACTION_IDLE\", sizeof(\"TRANSACTION_IDLE\") - 1, true);\n\tzend_class_constant *const_TRANSACTION_IDLE = zend_declare_typed_class_constant(class_entry, const_TRANSACTION_IDLE_name, &const_TRANSACTION_IDLE_value, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));\n\tzend_string_release_ex(const_TRANSACTION_IDLE_name, true);\n\n\tzval const_TRANSACTION_ACTIVE_value;\n\tZVAL_LONG(&const_TRANSACTION_ACTIVE_value, PGSQL_TRANSACTION_ACTIVE);\n\tzend_string *const_TRANSACTION_ACTIVE_name = zend_string_init_interned(\"TRANSACTION_ACTIVE\", sizeof(\"TRANSACTION_ACTIVE\") - 1, true);\n\tzend_class_constant *const_TRANSACTION_ACTIVE = zend_declare_typed_class_constant(class_entry, const_TRANSACTION_ACTIVE_name, &const_TRANSACTION_ACTIVE_value, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));\n\tzend_string_release_ex(const_TRANSACTION_ACTIVE_name, true);\n\n\tzval const_TRANSACTION_INTRANS_value;\n\tZVAL_LONG(&const_TRANSACTION_INTRANS_value, PGSQL_TRANSACTION_INTRANS);\n\tzend_string *const_TRANSACTION_INTRANS_name = zend_string_init_interned(\"TRANSACTION_INTRANS\", sizeof(\"TRANSACTION_INTRANS\") - 1, true);\n\tzend_class_constant *const_TRANSACTION_INTRANS = zend_declare_typed_class_constant(class_entry, const_TRANSACTION_INTRANS_name, &const_TRANSACTION_INTRANS_value, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));\n\tzend_string_release_ex(const_TRANSACTION_INTRANS_name, true);\n\n\tzval const_TRANSACTION_INERROR_value;\n\tZVAL_LONG(&const_TRANSACTION_INERROR_value, PGSQL_TRANSACTION_INERROR);\n\tzend_string *const_TRANSACTION_INERROR_name = zend_string_init_interned(\"TRANSACTION_INERROR\", sizeof(\"TRANSACTION_INERROR\") - 1, true);\n\tzend_class_constant *const_TRANSACTION_INERROR = zend_declare_typed_class_constant(class_entry, const_TRANSACTION_INERROR_name, &const_TRANSACTION_INERROR_value, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));\n\tzend_string_release_ex(const_TRANSACTION_INERROR_name, true);\n\n\tzval const_TRANSACTION_UNKNOWN_value;\n\tZVAL_LONG(&const_TRANSACTION_UNKNOWN_value, PGSQL_TRANSACTION_UNKNOWN);\n\tzend_string *const_TRANSACTION_UNKNOWN_name = zend_string_init_interned(\"TRANSACTION_UNKNOWN\", sizeof(\"TRANSACTION_UNKNOWN\") - 1, true);\n\tzend_class_constant *const_TRANSACTION_UNKNOWN = zend_declare_typed_class_constant(class_entry, const_TRANSACTION_UNKNOWN_name, &const_TRANSACTION_UNKNOWN_value, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));\n\tzend_string_release_ex(const_TRANSACTION_UNKNOWN_name, true);\n\n\n\tzend_attribute *attribute_Deprecated_const_TRANSACTION_IDLE_0 = zend_add_class_constant_attribute(class_entry, const_TRANSACTION_IDLE, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2);\n\tZVAL_STR(&attribute_Deprecated_const_TRANSACTION_IDLE_0->args[0].value, ZSTR_KNOWN(ZEND_STR_8_DOT_5));\n\tattribute_Deprecated_const_TRANSACTION_IDLE_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE);\n\tzend_string *attribute_Deprecated_const_TRANSACTION_IDLE_0_arg1_str = zend_string_init(\"as it has no effect\", strlen(\"as it has no effect\"), 1);\n\tZVAL_STR(&attribute_Deprecated_const_TRANSACTION_IDLE_0->args[1].value, attribute_Deprecated_const_TRANSACTION_IDLE_0_arg1_str);\n\tattribute_Deprecated_const_TRANSACTION_IDLE_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);\n\n\tzend_attribute *attribute_Deprecated_const_TRANSACTION_ACTIVE_0 = zend_add_class_constant_attribute(class_entry, const_TRANSACTION_ACTIVE, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2);\n\tZVAL_STR(&attribute_Deprecated_const_TRANSACTION_ACTIVE_0->args[0].value, ZSTR_KNOWN(ZEND_STR_8_DOT_5));\n\tattribute_Deprecated_const_TRANSACTION_ACTIVE_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE);\n\tZVAL_STR_COPY(&attribute_Deprecated_const_TRANSACTION_ACTIVE_0->args[1].value, attribute_Deprecated_const_TRANSACTION_IDLE_0_arg1_str);\n\tattribute_Deprecated_const_TRANSACTION_ACTIVE_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);\n\n\tzend_attribute *attribute_Deprecated_const_TRANSACTION_INTRANS_0 = zend_add_class_constant_attribute(class_entry, const_TRANSACTION_INTRANS, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2);\n\tZVAL_STR(&attribute_Deprecated_const_TRANSACTION_INTRANS_0->args[0].value, ZSTR_KNOWN(ZEND_STR_8_DOT_5));\n\tattribute_Deprecated_const_TRANSACTION_INTRANS_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE);\n\tZVAL_STR_COPY(&attribute_Deprecated_const_TRANSACTION_INTRANS_0->args[1].value, attribute_Deprecated_const_TRANSACTION_IDLE_0_arg1_str);\n\tattribute_Deprecated_const_TRANSACTION_INTRANS_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);\n\n\tzend_attribute *attribute_Deprecated_const_TRANSACTION_INERROR_0 = zend_add_class_constant_attribute(class_entry, const_TRANSACTION_INERROR, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2);\n\tZVAL_STR(&attribute_Deprecated_const_TRANSACTION_INERROR_0->args[0].value, ZSTR_KNOWN(ZEND_STR_8_DOT_5));\n\tattribute_Deprecated_const_TRANSACTION_INERROR_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE);\n\tZVAL_STR_COPY(&attribute_Deprecated_const_TRANSACTION_INERROR_0->args[1].value, attribute_Deprecated_const_TRANSACTION_IDLE_0_arg1_str);\n\tattribute_Deprecated_const_TRANSACTION_INERROR_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);\n\n\tzend_attribute *attribute_Deprecated_const_TRANSACTION_UNKNOWN_0 = zend_add_class_constant_attribute(class_entry, const_TRANSACTION_UNKNOWN, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2);\n\tZVAL_STR(&attribute_Deprecated_const_TRANSACTION_UNKNOWN_0->args[0].value, ZSTR_KNOWN(ZEND_STR_8_DOT_5));\n\tattribute_Deprecated_const_TRANSACTION_UNKNOWN_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE);\n\tZVAL_STR_COPY(&attribute_Deprecated_const_TRANSACTION_UNKNOWN_0->args[1].value, attribute_Deprecated_const_TRANSACTION_IDLE_0_arg1_str);\n\tattribute_Deprecated_const_TRANSACTION_UNKNOWN_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);\n\n\treturn class_entry;\n}\n"
  },
  {
    "path": "thirdparty/php85/pdo_pgsql/pgsql_driver.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Authors: Edin Kadribasic <edink@emini.dk>                            |\n  |          Ilia Alshanestsky <ilia@prohost.org>                        |\n  |          Wez Furlong <wez@php.net>                                   |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_PGSQL_HOOK\n#include \"php_swoole_pgsql.h\"\n\n#if PHP_VERSION_ID >= 80500\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/php_string.h\" /* For php_addcslashes_str() in _pdo_pgsql_escape_credentials() */\n#include \"main/php_network.h\"\n#include \"ext/pdo/php_pdo.h\"\n#include \"ext/pdo/php_pdo_driver.h\"\n#include \"ext/pdo/php_pdo_error.h\"\n#include \"ext/standard/file.h\"\n#include \"php_pdo_pgsql.h\"\n#include \"php_pdo_pgsql_int.h\"\n#include \"zend_exceptions.h\"\n#include \"zend_interfaces.h\"\n#include \"zend_smart_str.h\"\n#include \"pgsql_driver_arginfo.h\"\n\nstatic bool pgsql_handle_in_transaction(pdo_dbh_t *dbh);\n\nstatic char * _pdo_pgsql_trim_message(const char *message, int persistent)\n{\n\tsize_t i = strlen(message);\n\tchar *tmp;\n\tif (UNEXPECTED(i == 0)) {\n\t\ttmp = pemalloc(1, persistent);\n\t\ttmp[0] = '\\0';\n\t\treturn tmp;\n\t}\n\t--i;\n\n\tif (i>1 && (message[i-1] == '\\r' || message[i-1] == '\\n') && message[i] == '.') {\n\t\t--i;\n\t}\n\twhile (i>0 && (message[i] == '\\r' || message[i] == '\\n')) {\n\t\t--i;\n\t}\n\t++i;\n\ttmp = pemalloc(i + 1, persistent);\n\tmemcpy(tmp, message, i);\n\ttmp[i] = '\\0';\n\n\treturn tmp;\n}\n\nstatic zend_string* _pdo_pgsql_escape_credentials(char *str)\n{\n\tif (str) {\n\t\treturn php_addcslashes_str(str, strlen(str), \"\\\\'\", sizeof(\"\\\\'\"));\n\t}\n\n\treturn NULL;\n}\n\nint _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line) /* {{{ */\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tpdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code;\n\tpdo_pgsql_error_info *einfo = &H->einfo;\n\tchar *errmsg = PQerrorMessage(H->server);\n\n\teinfo->errcode = errcode;\n\teinfo->file = file;\n\teinfo->line = line;\n\n\tif (einfo->errmsg) {\n\t\tpefree(einfo->errmsg, dbh->is_persistent);\n\t\teinfo->errmsg = NULL;\n\t}\n\n\tif (sqlstate == NULL || strlen(sqlstate) >= sizeof(pdo_error_type)) {\n\t\tstrcpy(*pdo_err, \"HY000\");\n\t}\n\telse {\n\t\tstrcpy(*pdo_err, sqlstate);\n\t}\n\n\tif (msg) {\n\t\teinfo->errmsg = pestrdup(msg, dbh->is_persistent);\n\t}\n\telse if (errmsg) {\n\t\teinfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent);\n\t}\n\n\tif (!dbh->methods) {\n\t\tpdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err);\n\t}\n\n\treturn errcode;\n}\n/* }}} */\n\nstatic void _pdo_pgsql_notice(void *context, const char *message) /* {{{ */\n{\n\tpdo_dbh_t * dbh = (pdo_dbh_t *)context;\n\tzend_fcall_info_cache *fc = ((pdo_pgsql_db_handle *)dbh->driver_data)->notice_callback;\n\tif (fc) {\n\t\tzval zarg;\n\t\tZVAL_STRING(&zarg, message);\n\t\tzend_call_known_fcc(fc, NULL, 1, &zarg, NULL);\n\t\tzval_ptr_dtor_str(&zarg);\n\t}\n}\n/* }}} */\n\nstatic void pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tpdo_pgsql_error_info *einfo = &H->einfo;\n\n\tif (einfo->errcode) {\n\t\tadd_next_index_long(info, einfo->errcode);\n\t} else {\n\t\t/* Add null to respect expected info array structure */\n\t\tadd_next_index_null(info);\n\t}\n\tif (einfo->errmsg) {\n\t\tadd_next_index_string(info, einfo->errmsg);\n\t}\n}\n/* }}} */\n\nvoid pdo_pgsql_cleanup_notice_callback(pdo_pgsql_db_handle *H) /* {{{ */\n{\n\tif (H->notice_callback) {\n\t\tzend_fcc_dtor(H->notice_callback);\n\t\tefree(H->notice_callback);\n\t\tH->notice_callback = NULL;\n\t}\n}\n/* }}} */\n\n/* {{{ pdo_pgsql_create_lob_stream */\nstatic ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count)\n{\n\tstruct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;\n\treturn lo_write(self->conn, self->lfd, (char*)buf, count);\n}\n\nstatic ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count)\n{\n\tstruct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;\n\treturn lo_read(self->conn, self->lfd, buf, count);\n}\n\nstatic int pgsql_lob_close(php_stream *stream, int close_handle)\n{\n\tstruct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(&self->dbh))->driver_data;\n\n\tif (close_handle) {\n\t\tlo_close(self->conn, self->lfd);\n\t}\n\tzend_hash_index_del(H->lob_streams, php_stream_get_resource_id(stream));\n\tzval_ptr_dtor(&self->dbh);\n\tefree(self);\n\treturn 0;\n}\n\nstatic int pgsql_lob_flush(php_stream *stream)\n{\n\treturn 0;\n}\n\nstatic int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence,\n\t\tzend_off_t *newoffset)\n{\n\tstruct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;\n#ifdef ZEND_ENABLE_ZVAL_LONG64\n\tzend_off_t pos = lo_lseek64(self->conn, self->lfd, offset, whence);\n#else\n\tzend_off_t pos = lo_lseek(self->conn, self->lfd, offset, whence);\n#endif\n\t*newoffset = pos;\n\treturn pos >= 0 ? 0 : -1;\n}\n\nconst php_stream_ops pdo_pgsql_lob_stream_ops = {\n\tpgsql_lob_write,\n\tpgsql_lob_read,\n\tpgsql_lob_close,\n\tpgsql_lob_flush,\n\t\"pdo_pgsql lob stream\",\n\tpgsql_lob_seek,\n\tNULL,\n\tNULL,\n\tNULL\n};\n\nphp_stream *pdo_pgsql_create_lob_stream(zend_object *dbh, int lfd, Oid oid)\n{\n\tphp_stream *stm;\n\tstruct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self));\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(php_pdo_dbh_fetch_inner(dbh))->driver_data;\n\n\tZVAL_OBJ(&self->dbh, dbh);\n\tself->lfd = lfd;\n\tself->oid = oid;\n\tself->conn = H->server;\n\n\tstm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, \"r+b\");\n\n\tif (stm) {\n\t\tGC_ADDREF(dbh);\n\t\tzend_hash_index_add_ptr(H->lob_streams, php_stream_get_resource_id(stm), stm->res);\n\t\treturn stm;\n\t}\n\n\tefree(self);\n\treturn NULL;\n}\n/* }}} */\n\nvoid pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh)\n{\n\tzend_resource *res;\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tif (H->lob_streams) {\n\t\tZEND_HASH_REVERSE_FOREACH_PTR(H->lob_streams, res) {\n\t\t\tif (res->type >= 0) {\n\t\t\t\tzend_list_close(res);\n\t\t\t}\n\t\t} ZEND_HASH_FOREACH_END();\n\t}\n}\n\nstatic void pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tif (H) {\n\t\tif (H->lob_streams) {\n\t\t\tpdo_pgsql_close_lob_streams(dbh);\n\t\t\tzend_hash_destroy(H->lob_streams);\n\t\t\tpefree(H->lob_streams, dbh->is_persistent);\n\t\t\tH->lob_streams = NULL;\n\t\t}\n\t\tpdo_pgsql_cleanup_notice_callback(H);\n\t\tif (H->server) {\n\t\t\tPQfinish(H->server);\n\t\t\tH->server = NULL;\n\t\t}\n\t\tif (H->einfo.errmsg) {\n\t\t\tpefree(H->einfo.errmsg, dbh->is_persistent);\n\t\t\tH->einfo.errmsg = NULL;\n\t\t}\n\t\tpefree(H, dbh->is_persistent);\n\t\tdbh->driver_data = NULL;\n\t}\n}\n/* }}} */\n\nstatic bool pgsql_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tpdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt));\n\tint scrollable;\n\tint ret;\n\tzend_string *nsql = NULL;\n\tint emulate = 0;\n\tint execute_only = 0;\n\tzval *val;\n\tzend_long lval;\n\n\tS->H = H;\n\tstmt->driver_data = S;\n\tstmt->methods = &swoole_pgsql_stmt_methods;\n\n\tscrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR,\n\t\tPDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL;\n\n\tif (scrollable) {\n\t\tif (S->cursor_name) {\n\t\t\tefree(S->cursor_name);\n\t\t}\n\t\tspprintf(&S->cursor_name, 0, \"pdo_crsr_%08x\", ++H->stmt_counter);\n\t\temulate = 1;\n\t} else if (driver_options) {\n\t\tif (pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares) == 1) {\n\t\t\temulate = 1;\n\t\t}\n\t\tif (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_PREPARES, H->disable_prepares) == 1) {\n\t\t\texecute_only = 1;\n\t\t}\n\t} else {\n\t\temulate = H->emulate_prepares;\n\t\texecute_only = H->disable_prepares;\n\t}\n\n\tif (emulate) {\n\t\tstmt->supports_placeholders = PDO_PLACEHOLDER_NONE;\n\t} else {\n\t\tstmt->supports_placeholders = PDO_PLACEHOLDER_NAMED;\n\t\tstmt->named_rewrite_template = \"$%d\";\n\t}\n\n\tS->is_unbuffered =\n\t\tdriver_options\n\t\t&& (val = zend_hash_index_find(Z_ARRVAL_P(driver_options), PDO_ATTR_PREFETCH))\n\t\t&& pdo_get_long_param(&lval, val)\n\t\t? !lval\n\t\t: H->default_fetching_laziness\n\t;\n\n\tret = pdo_parse_params(stmt, sql, &nsql);\n\n\tif (ret == -1) {\n\t\t/* couldn't grok it */\n\t\tstrcpy(dbh->error_code, stmt->error_code);\n\t\treturn false;\n\t} else if (ret == 1) {\n\t\t/* query was re-written */\n\t\tS->query = nsql;\n\t} else {\n\t\tS->query = zend_string_copy(sql);\n\t}\n\n\tif (!emulate && !execute_only) {\n\t\t/* prepared query: set the query name and defer the\n\t\t   actual prepare until the first execute call */\n\t\tspprintf(&S->stmt_name, 0, \"pdo_stmt_%08x\", ++H->stmt_counter);\n\t}\n\n\treturn true;\n}\n\nstatic zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const zend_string *sql)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tPGresult *res;\n\tzend_long ret = 1;\n\tExecStatusType qs;\n\n\tbool in_trans = pgsql_handle_in_transaction(dbh);\n\n\tif (!(res = PQexec(H->server, ZSTR_VAL(sql)))) {\n\t\t/* fatal error */\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\treturn -1;\n\t}\n\tqs = PQresultStatus(res);\n\tif (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) {\n\t\tpdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res));\n\t\tPQclear(res);\n\t\treturn -1;\n\t}\n\tH->pgoid = PQoidValue(res);\n\tif (qs == PGRES_COMMAND_OK) {\n\t\tret = ZEND_ATOL(PQcmdTuples(res));\n\t} else {\n\t\tret = Z_L(0);\n\t}\n\tPQclear(res);\n\tif (in_trans && !pgsql_handle_in_transaction(dbh)) {\n\t\tpdo_pgsql_close_lob_streams(dbh);\n\t}\n\n\treturn ret;\n}\n\nstatic zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype)\n{\n\tunsigned char *escaped;\n\tchar *quoted;\n\tsize_t quotedlen;\n\tzend_string *quoted_str;\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tsize_t tmp_len;\n\tint err;\n\n\tswitch (paramtype) {\n\t\tcase PDO_PARAM_LOB:\n\t\t\t/* escapedlen returned by PQescapeBytea() accounts for trailing 0 */\n\t\t\tescaped = PQescapeByteaConn(H->server, (unsigned char *)ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &tmp_len);\n\t\t\tif (escaped == NULL) {\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tquotedlen = tmp_len + 1;\n\t\t\tquoted = emalloc(quotedlen + 1);\n\t\t\tmemcpy(quoted+1, escaped, quotedlen-2);\n\t\t\tquoted[0] = '\\'';\n\t\t\tquoted[quotedlen-1] = '\\'';\n\t\t\tquoted[quotedlen] = '\\0';\n\t\t\tPQfreemem(escaped);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tquoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3);\n\t\t\tquoted[0] = '\\'';\n\t\t\tquotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &err);\n\t\t\tif (err) {\n\t\t\t\tefree(quoted);\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tquoted[quotedlen + 1] = '\\'';\n\t\t\tquoted[quotedlen + 2] = '\\0';\n\t\t\tquotedlen += 2;\n\t}\n\n\tquoted_str = zend_string_init(quoted, quotedlen, 0);\n\tefree(quoted);\n\treturn quoted_str;\n}\n\nstatic zend_string *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const zend_string *name)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tzend_string *id = NULL;\n\tPGresult *res;\n\tExecStatusType status;\n\n\tif (name == NULL) {\n\t\tres = PQexec(H->server, \"SELECT LASTVAL()\");\n\t} else {\n\t\tconst char *q[1];\n\t\tq[0] = ZSTR_VAL(name);\n\n\t\tres = PQexecParams(H->server, \"SELECT CURRVAL($1)\", 1, NULL, q, NULL, NULL, 0);\n\t}\n\tstatus = PQresultStatus(res);\n\n\tif (res && (status == PGRES_TUPLES_OK)) {\n\t\tid = zend_string_init((char *)PQgetvalue(res, 0, 0), PQgetlength(res, 0, 0), 0);\n\t} else {\n\t\tpdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res));\n\t}\n\n\tif (res) {\n\t\tPQclear(res);\n\t}\n\n\treturn id;\n}\n\nvoid pdo_libpq_version(char *buf, size_t len)\n{\n\tint version = PQlibVersion();\n\tint major = version / 10000;\n\tif (major >= 10) {\n\t\tint minor = version % 10000;\n\t\tsnprintf(buf, len, \"%d.%d\", major, minor);\n\t} else {\n\t\tint minor = version / 100 % 100;\n\t\tint revision = version % 100;\n\t\tsnprintf(buf, len, \"%d.%d.%d\", major, minor, revision);\n\t}\n}\n\nstatic int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tswitch (attr) {\n\t\tcase PDO_ATTR_EMULATE_PREPARES:\n\t\t\tZVAL_BOOL(return_value, H->emulate_prepares);\n\t\t\tbreak;\n\n\t\tcase PDO_PGSQL_ATTR_DISABLE_PREPARES:\n\t\t\tZVAL_BOOL(return_value, H->disable_prepares);\n\t\t\tbreak;\n\n\t\tcase PDO_ATTR_CLIENT_VERSION: {\n\t\t\tchar buf[16];\n\t\t\tpdo_libpq_version(buf, sizeof(buf));\n\t\t\tZVAL_STRING(return_value, buf);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase PDO_ATTR_SERVER_VERSION:\n\t\t\tZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, \"server_version\"));\n\t\t\tbreak;\n\n\t\tcase PDO_ATTR_CONNECTION_STATUS:\n\t\t\tswitch (PQstatus(H->server)) {\n\t\t\t\tcase CONNECTION_STARTED:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Waiting for connection to be made.\", strlen(\"Waiting for connection to be made.\"));\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase CONNECTION_MADE:\n\t\t\t\tcase CONNECTION_OK:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Connection OK; waiting to send.\", strlen(\"Connection OK; waiting to send.\"));\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase CONNECTION_AWAITING_RESPONSE:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Waiting for a response from the server.\", strlen(\"Waiting for a response from the server.\"));\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase CONNECTION_AUTH_OK:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Received authentication; waiting for backend start-up to finish.\", strlen(\"Received authentication; waiting for backend start-up to finish.\"));\n\t\t\t\t\tbreak;\n#ifdef CONNECTION_SSL_STARTUP\n\t\t\t\tcase CONNECTION_SSL_STARTUP:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Negotiating SSL encryption.\", strlen(\"Negotiating SSL encryption.\"));\n\t\t\t\t\tbreak;\n#endif\n\t\t\t\tcase CONNECTION_SETENV:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Negotiating environment-driven parameter settings.\", strlen(\"Negotiating environment-driven parameter settings.\"));\n\t\t\t\t\tbreak;\n\n#ifdef CONNECTION_CONSUME\n\t\t\t\tcase CONNECTION_CONSUME:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Flushing send queue/consuming extra data.\", strlen(\"Flushing send queue/consuming extra data.\"));\n\t\t\t\t\tbreak;\n#endif\n#ifdef CONNECTION_GSS_STARTUP\n\t\t\t\tcase CONNECTION_SSL_STARTUP:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Negotiating GSSAPI.\", strlen(\"Negotiating GSSAPI.\"));\n\t\t\t\t\tbreak;\n#endif\n#ifdef CONNECTION_CHECK_TARGET\n\t\t\t\tcase CONNECTION_CHECK_TARGET:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Connection OK; checking target server properties.\", strlen(\"Connection OK; checking target server properties.\"));\n\t\t\t\t\tbreak;\n#endif\n#ifdef CONNECTION_CHECK_STANDBY\n\t\t\t\tcase CONNECTION_CHECK_STANDBY:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Connection OK; checking if server in standby.\", strlen(\"Connection OK; checking if server in standby.\"));\n\t\t\t\t\tbreak;\n#endif\n\t\t\t\tcase CONNECTION_BAD:\n\t\t\t\tdefault:\n\t\t\t\t\tZVAL_STRINGL(return_value, \"Bad connection.\", strlen(\"Bad connection.\"));\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase PDO_ATTR_SERVER_INFO: {\n\t\t\tint spid = PQbackendPID(H->server);\n\n\n\t\t\tzend_string *str_info =\n\t\t\t\tstrpprintf(0,\n\t\t\t\t\t\"PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s\",\n\t\t\t\t\tspid,\n\t\t\t\t\t(char*)PQparameterStatus(H->server, \"client_encoding\"),\n\t\t\t\t\t(char*)PQparameterStatus(H->server, \"is_superuser\"),\n\t\t\t\t\t(char*)PQparameterStatus(H->server, \"session_authorization\"),\n\t\t\t\t\t(char*)PQparameterStatus(H->server, \"DateStyle\"));\n\n\t\t\tZVAL_STR(return_value, str_info);\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\n/* {{{ */\nstatic zend_result pdo_pgsql_check_liveness(pdo_dbh_t *dbh)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tif (!PQconsumeInput(H->server) || PQstatus(H->server) == CONNECTION_BAD) {\n\t\tPQreset(H->server);\n\t}\n\treturn (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE;\n}\n/* }}} */\n\nstatic bool pgsql_handle_in_transaction(pdo_dbh_t *dbh)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\treturn PQtransactionStatus(H->server) > PQTRANS_IDLE;\n}\n\nstatic bool pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh)\n{\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tPGresult *res;\n\tbool ret = true;\n\n\tres = PQexec(H->server, cmd);\n\n\tif (PQresultStatus(res) != PGRES_COMMAND_OK) {\n\t\tpdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res));\n\t\tret = false;\n\t}\n\n\tPQclear(res);\n\treturn ret;\n}\n\nstatic bool pgsql_handle_begin(pdo_dbh_t *dbh)\n{\n\treturn pdo_pgsql_transaction_cmd(\"BEGIN\", dbh);\n}\n\nstatic bool pgsql_handle_commit(pdo_dbh_t *dbh)\n{\n\tbool ret = pdo_pgsql_transaction_cmd(\"COMMIT\", dbh);\n\n\t/* When deferred constraints are used the commit could\n\t   fail, and a ROLLBACK implicitly ran. See bug #67462 */\n\tif (ret) {\n\t\tpdo_pgsql_close_lob_streams(dbh);\n\t} else {\n\t\tdbh->in_txn = pgsql_handle_in_transaction(dbh);\n\t}\n\n\treturn ret;\n}\n\nstatic bool pgsql_handle_rollback(pdo_dbh_t *dbh)\n{\n\tint ret = pdo_pgsql_transaction_cmd(\"ROLLBACK\", dbh);\n\n\tif (ret) {\n\t\tpdo_pgsql_close_lob_streams(dbh);\n\t}\n\n\treturn ret;\n}\n\nstatic bool _pdo_pgsql_send_copy_data(pdo_pgsql_db_handle *H, zval *line) {\n\tsize_t query_len;\n\tzend_string *query;\n\n\tif (!try_convert_to_string(line)) {\n\t\treturn false;\n\t}\n\n\tquery_len = Z_STRLEN_P(line);\n\tquery = zend_string_alloc(query_len + 2, false); /* room for \\n\\0 */\n\tmemcpy(ZSTR_VAL(query), Z_STRVAL_P(line), query_len + 1);\n\tZSTR_LEN(query) = query_len;\n\n\tif (query_len > 0 && ZSTR_VAL(query)[query_len - 1] != '\\n') {\n\t\tZSTR_VAL(query)[query_len] = '\\n';\n\t\tZSTR_VAL(query)[query_len + 1] = '\\0';\n\t\tZSTR_LEN(query) ++;\n\t}\n\n\tif (PQputCopyData(H->server, ZSTR_VAL(query), ZSTR_LEN(query)) != 1) {\n\t\tzend_string_release_ex(query, false);\n\t\treturn false;\n\t}\n\n\tzend_string_release_ex(query, false);\n\treturn true;\n}\n\nvoid pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAMETERS)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\n\tzval *pg_rows;\n\n\tchar *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;\n\tsize_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;\n\tchar *query;\n\n\tPGresult *pgsql_result;\n\tExecStatusType status;\n\n\tZEND_PARSE_PARAMETERS_START(2, 5)\n\t\tZ_PARAM_STRING(table_name, table_name_len)\n\t\tZ_PARAM_ITERABLE(pg_rows)\n\t\tZ_PARAM_OPTIONAL\n\t\tZ_PARAM_STRING(pg_delim, pg_delim_len)\n\t\tZ_PARAM_STRING(pg_null_as, pg_null_as_len)\n\t\tZ_PARAM_STRING_OR_NULL(pg_fields, pg_fields_len)\n\tZEND_PARSE_PARAMETERS_END();\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\t/* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */\n\tif (pg_fields) {\n\t\tspprintf(&query, 0, \"COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t} else {\n\t\tspprintf(&query, 0, \"COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t}\n\n\t/* Obtain db Handle */\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\tPQclear(pgsql_result);\n\t}\n\tpgsql_result = PQexec(H->server, query);\n\n\tefree(query);\n\tquery = NULL;\n\n\tif (pgsql_result) {\n\t\tstatus = PQresultStatus(pgsql_result);\n\t} else {\n\t\tstatus = (ExecStatusType) PQstatus(H->server);\n\t}\n\n\tif (status == PGRES_COPY_IN && pgsql_result) {\n\t\tint command_failed = 0;\n\t\tzval *tmp;\n\t\tzend_object_iterator *iter;\n\n\t\tPQclear(pgsql_result);\n\n\t\tif (Z_TYPE_P(pg_rows) == IS_ARRAY) {\n\t\t\tZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) {\n\t\t\t\tif (!_pdo_pgsql_send_copy_data(H, tmp)) {\n\t\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\t\t\tRETURN_FALSE;\n\t\t\t\t}\n\t\t\t} ZEND_HASH_FOREACH_END();\n\t\t} else {\n\t\t\titer = Z_OBJCE_P(pg_rows)->get_iterator(Z_OBJCE_P(pg_rows), pg_rows, 0);\n\t\t\tif (iter == NULL || EG(exception)) {\n\t\t\t\tRETURN_THROWS();\n\t\t\t}\n\n\t\t\tif (iter->funcs->rewind) {\n\t\t\t\titer->funcs->rewind(iter);\n\t\t\t\tif (EG(exception)) {\n\t\t\t\t\tRETURN_THROWS();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (; iter->funcs->valid(iter) == SUCCESS && EG(exception) == NULL; iter->funcs->move_forward(iter)) {\n\t\t\t\ttmp = iter->funcs->get_current_data(iter);\n\t\t\t\tif (!_pdo_pgsql_send_copy_data(H, tmp)) {\n\t\t\t\t\tzend_iterator_dtor(iter);\n\t\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\t\t\tRETURN_FALSE;\n\t\t\t\t}\n\t\t\t}\n\t\t\tzend_iterator_dtor(iter);\n\t\t}\n\n\t\tif (PQputCopyEnd(H->server, NULL) != 1) {\n\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\tRETURN_FALSE;\n\t\t}\n\n\t\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\t\tif (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {\n\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\t\t\tcommand_failed = 1;\n\t\t\t}\n\t\t\tPQclear(pgsql_result);\n\t\t}\n\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_BOOL(!command_failed);\n\t} else {\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\tPQclear(pgsql_result);\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n}\n\n/* {{{ Returns true if the copy worked fine or false if error */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray)\n{\n\tpgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n/* }}} */\n\nvoid pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAMETERS)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\n\tchar *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;\n\tsize_t  table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;\n\tchar *query;\n\tPGresult *pgsql_result;\n\tExecStatusType status;\n\tphp_stream *stream;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"sp|sss!\",\n\t\t&table_name, &table_name_len, &filename, &filename_len,\n\t\t&pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\n\t/* Obtain db Handler */\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tstream = php_stream_open_wrapper_ex(filename, \"rb\", 0, NULL, FG(default_context));\n\tif (!stream) {\n\t\tpdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, \"Unable to open the file\");\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n\n\t/* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */\n\tif (pg_fields) {\n\t\tspprintf(&query, 0, \"COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t} else {\n\t\tspprintf(&query, 0, \"COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t}\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\tPQclear(pgsql_result);\n\t}\n\tpgsql_result = PQexec(H->server, query);\n\n\tefree(query);\n\n\tif (pgsql_result) {\n\t\tstatus = PQresultStatus(pgsql_result);\n\t} else {\n\t\tstatus = (ExecStatusType) PQstatus(H->server);\n\t}\n\n\tif (status == PGRES_COPY_IN && pgsql_result) {\n\t\tchar *buf;\n\t\tint command_failed = 0;\n\t\tsize_t line_len = 0;\n\n\t\tPQclear(pgsql_result);\n\t\twhile ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) {\n\t\t\tif (PQputCopyData(H->server, buf, line_len) != 1) {\n\t\t\t\tefree(buf);\n\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\t\tphp_stream_close(stream);\n\t\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\t\tRETURN_FALSE;\n\t\t\t}\n\t\t\tefree(buf);\n\t\t}\n\t\tphp_stream_close(stream);\n\n\t\tif (PQputCopyEnd(H->server, NULL) != 1) {\n\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\tRETURN_FALSE;\n\t\t}\n\n\t\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\t\tif (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {\n\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\t\t\tcommand_failed = 1;\n\t\t\t}\n\t\t\tPQclear(pgsql_result);\n\t\t}\n\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_BOOL(!command_failed);\n\t} else {\n\t\tphp_stream_close(stream);\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\tPQclear(pgsql_result);\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n}\n\n/* {{{ Returns true if the copy worked fine or false if error */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile)\n{\n\tpgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n/* }}} */\n\nvoid pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAMETERS)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\n\tchar *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL;\n\tsize_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len;\n\tchar *query;\n\n\tPGresult *pgsql_result;\n\tExecStatusType status;\n\n\tphp_stream *stream;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"sp|sss!\",\n\t\t\t\t\t&table_name, &table_name_len, &filename, &filename_len,\n\t\t\t\t\t&pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tstream = php_stream_open_wrapper_ex(filename, \"wb\", 0, NULL, FG(default_context));\n\tif (!stream) {\n\t\tpdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, \"Unable to open the file for writing\");\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n\n\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\tPQclear(pgsql_result);\n\t}\n\n\t/* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */\n\tif (pg_fields) {\n\t\tspprintf(&query, 0, \"COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t} else {\n\t\tspprintf(&query, 0, \"COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t}\n\tpgsql_result = PQexec(H->server, query);\n\tefree(query);\n\n\tif (pgsql_result) {\n\t\tstatus = PQresultStatus(pgsql_result);\n\t} else {\n\t\tstatus = (ExecStatusType) PQstatus(H->server);\n\t}\n\n\tif (status == PGRES_COPY_OUT && pgsql_result) {\n\t\tPQclear(pgsql_result);\n\t\twhile (1) {\n\t\t\tchar *csv = NULL;\n\t\t\tint ret = PQgetCopyData(H->server, &csv, 0);\n\n\t\t\tif (ret == -1) {\n\t\t\t\tbreak; /* done */\n\t\t\t} else if (ret > 0) {\n\t\t\t\tif (php_stream_write(stream, csv, ret) != (size_t)ret) {\n\t\t\t\t\tpdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, \"Unable to write to file\");\n\t\t\t\t\tPQfreemem(csv);\n\t\t\t\t\tphp_stream_close(stream);\n\t\t\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\t\t\tRETURN_FALSE;\n\t\t\t\t} else {\n\t\t\t\t\tPQfreemem(csv);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\t\tphp_stream_close(stream);\n\t\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\t\tRETURN_FALSE;\n\t\t\t}\n\t\t}\n\t\tphp_stream_close(stream);\n\n\t\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\t\tPQclear(pgsql_result);\n\t\t}\n\t\tRETURN_TRUE;\n\t} else {\n\t\tphp_stream_close(stream);\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\tPQclear(pgsql_result);\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n}\n\n/* {{{ Returns true if the copy worked fine or false if error */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile)\n{\n\tpgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n\n}\n/* }}} */\n\nvoid pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAMETERS)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\n\tchar *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;\n\tsize_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;\n\tchar *query;\n\n\tPGresult *pgsql_result;\n\tExecStatusType status;\n\n\tif (zend_parse_parameters(ZEND_NUM_ARGS(), \"s|sss!\",\n\t\t&table_name, &table_name_len,\n\t\t&pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {\n\t\tRETURN_THROWS();\n\t}\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\tPQclear(pgsql_result);\n\t}\n\n\t/* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */\n\tif (pg_fields) {\n\t\tspprintf(&query, 0, \"COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t} else {\n\t\tspprintf(&query, 0, \"COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'\", table_name, (pg_delim_len ? *pg_delim : '\\t'), (pg_null_as_len ? pg_null_as : \"\\\\\\\\N\"));\n\t}\n\tpgsql_result = PQexec(H->server, query);\n\tefree(query);\n\n\tif (pgsql_result) {\n\t\tstatus = PQresultStatus(pgsql_result);\n\t} else {\n\t\tstatus = (ExecStatusType) PQstatus(H->server);\n\t}\n\n\tif (status == PGRES_COPY_OUT && pgsql_result) {\n\t\tPQclear(pgsql_result);\n                array_init(return_value);\n\n\t\twhile (1) {\n\t\t\tchar *csv = NULL;\n\t\t\tint ret = PQgetCopyData(H->server, &csv, 0);\n\t\t\tif (ret == -1) {\n\t\t\t\tbreak; /* copy done */\n\t\t\t} else if (ret > 0) {\n\t\t\t\tadd_next_index_stringl(return_value, csv, ret);\n\t\t\t\tPQfreemem(csv);\n\t\t\t} else {\n\t\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\t\tRETURN_FALSE;\n\t\t\t}\n\t\t}\n\n\t\twhile ((pgsql_result = PQgetResult(H->server))) {\n\t\t\tPQclear(pgsql_result);\n\t\t}\n\t} else {\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));\n\t\tPQclear(pgsql_result);\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n}\n\n/* {{{ Returns true if the copy worked fine or false if error */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray)\n{\n\tpgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n/* }}} */\n\nvoid pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAMETERS)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\tOid lfd;\n\n\tZEND_PARSE_PARAMETERS_NONE();\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\tlfd = lo_creat(H->server, INV_READ|INV_WRITE);\n\n\tif (lfd != InvalidOid) {\n\t\tzend_string *buf = strpprintf(0, ZEND_ULONG_FMT, (zend_long) lfd);\n\n\t\tRETURN_STR(buf);\n\t}\n\n\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\tPDO_HANDLE_DBH_ERR();\n\tRETURN_FALSE;\n}\n\n/* {{{ Creates a new large object, returning its identifier.  Must be called inside a transaction. */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate)\n{\n\tpgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n/* }}} */\n\nvoid pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAMETERS)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\tOid oid;\n\tint lfd;\n\tchar *oidstr;\n\tsize_t oidstrlen;\n\tchar *modestr = \"rb\";\n\tsize_t modestrlen;\n\tint mode = INV_READ;\n\tchar *end_ptr;\n\n\tif (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), \"s|s\",\n\t\t\t\t&oidstr, &oidstrlen, &modestr, &modestrlen)) {\n\t\tRETURN_THROWS();\n\t}\n\n\toid = (Oid)strtoul(oidstr, &end_ptr, 10);\n\tif (oid == 0 && (errno == ERANGE || errno == EINVAL)) {\n\t\tRETURN_FALSE;\n\t}\n\n\tif (strpbrk(modestr, \"+w\")) {\n\t\tmode = INV_READ|INV_WRITE;\n\t}\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tlfd = lo_open(H->server, oid, mode);\n\n\tif (lfd >= 0) {\n\t\tphp_stream *stream = pdo_pgsql_create_lob_stream(Z_OBJ_P(ZEND_THIS), lfd, oid);\n\t\tif (stream) {\n\t\t\tphp_stream_to_zval(stream, return_value);\n\t\t\treturn;\n\t\t}\n\t} else {\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t}\n\n\tPDO_HANDLE_DBH_ERR();\n\tRETURN_FALSE;\n}\n\n/* {{{ Opens an existing large object stream.  Must be called inside a transaction. */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen)\n{\n\tpgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n/* }}} */\n\nvoid pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAMETERS)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\tOid oid;\n\tchar *oidstr, *end_ptr;\n\tsize_t oidlen;\n\n\tif (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), \"s\",\n\t\t\t\t&oidstr, &oidlen)) {\n\t\tRETURN_THROWS();\n\t}\n\n\toid = (Oid)strtoul(oidstr, &end_ptr, 10);\n\tif (oid == 0 && (errno == ERANGE || errno == EINVAL)) {\n\t\tRETURN_FALSE;\n\t}\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\tPDO_DBH_CLEAR_ERR();\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tif (1 == lo_unlink(H->server, oid)) {\n\t\tRETURN_TRUE;\n\t}\n\n\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\tPDO_HANDLE_DBH_ERR();\n\tRETURN_FALSE;\n}\n\n/* {{{ Deletes the large object identified by oid.  Must be called inside a transaction. */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink)\n{\n\tpgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n/* }}} */\n\nvoid pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAMETERS)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\tzend_long result_type = PDO_FETCH_USE_DEFAULT;\n\tzend_long ms_timeout = 0;\n\tPGnotify *pgsql_notify;\n\n\tif (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), \"|ll\",\n\t\t\t\t&result_type, &ms_timeout)) {\n\t\tRETURN_THROWS();\n\t}\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\n\tif (result_type == PDO_FETCH_USE_DEFAULT) {\n\t\tresult_type = dbh->default_fetch_type;\n\t}\n\n\tif (result_type != PDO_FETCH_BOTH && result_type != PDO_FETCH_ASSOC && result_type != PDO_FETCH_NUM) {\n\t\tzend_argument_value_error(1, \"must be one of PDO::FETCH_BOTH, PDO::FETCH_ASSOC, or PDO::FETCH_NUM\");\n\t\tRETURN_THROWS();\n\t}\n\n\tif (ms_timeout < 0) {\n\t\tzend_argument_value_error(2, \"must be greater than or equal to 0\");\n\t\tRETURN_THROWS();\n#ifdef ZEND_ENABLE_ZVAL_LONG64\n\t} else if (ms_timeout > INT_MAX) {\n\t\tphp_error_docref(NULL, E_WARNING, \"Timeout was shrunk to %d\", INT_MAX);\n\t\tms_timeout = INT_MAX;\n#endif\n\t}\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tif (!PQconsumeInput(H->server)) {\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\tPDO_HANDLE_DBH_ERR();\n\t\tRETURN_FALSE;\n\t}\n\tpgsql_notify = PQnotifies(H->server);\n\n\tif (ms_timeout && !pgsql_notify) {\n\t\tphp_pollfd_for_ms(PQsocket(H->server), PHP_POLLREADABLE, (int)ms_timeout);\n\n\t\tif (!PQconsumeInput(H->server)) {\n\t\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);\n\t\t\tPDO_HANDLE_DBH_ERR();\n\t\t\tRETURN_FALSE;\n\t\t}\n\t\tpgsql_notify = PQnotifies(H->server);\n\t}\n\n\tif (!pgsql_notify) {\n\t\tRETURN_FALSE;\n\t}\n\n\tarray_init(return_value);\n\tif (result_type == PDO_FETCH_NUM || result_type == PDO_FETCH_BOTH) {\n\t\tadd_index_string(return_value, 0, pgsql_notify->relname);\n\t\tadd_index_long(return_value, 1, pgsql_notify->be_pid);\n\t\tif (pgsql_notify->extra && pgsql_notify->extra[0]) {\n\t\t\tadd_index_string(return_value, 2, pgsql_notify->extra);\n\t\t}\n\t}\n\tif (result_type == PDO_FETCH_ASSOC || result_type == PDO_FETCH_BOTH) {\n\t\tadd_assoc_string(return_value, \"message\", pgsql_notify->relname);\n\t\tadd_assoc_long(return_value, \"pid\", pgsql_notify->be_pid);\n\t\tif (pgsql_notify->extra && pgsql_notify->extra[0]) {\n\t\t\tadd_assoc_string(return_value, \"payload\", pgsql_notify->extra);\n\t\t}\n\t}\n\n\tPQfreemem(pgsql_notify);\n}\n\n/* {{{ Get asynchronous notification */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify)\n{\n\tpgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n/* }}} */\n\nvoid pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAMETERS)\n{\n\tpdo_dbh_t *dbh;\n\tpdo_pgsql_db_handle *H;\n\n\tZEND_PARSE_PARAMETERS_NONE();\n\n\tdbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK;\n\n\tH = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tRETURN_LONG(PQbackendPID(H->server));\n}\n\n/* {{{ Get backend(server) pid */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid)\n{\n\tpgsqlGetPid_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n/* }}} */\n\n/* {{{ Sets a callback to receive DB notices (after client_min_messages has been set) */\nPHP_METHOD(PDO_PGSql_Ext, pgsqlSetNoticeCallback)\n{\n\tzend_fcall_info fci = empty_fcall_info;\n\tzend_fcall_info_cache fcc = empty_fcall_info_cache;\n\tif (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), \"F!\", &fci, &fcc)) {\n\t\tRETURN_THROWS();\n\t}\n\n\tpdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);\n\tPDO_CONSTRUCT_CHECK_WITH_CLEANUP(cleanup);\n\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tpdo_pgsql_cleanup_notice_callback(H);\n\n\tif (ZEND_FCC_INITIALIZED(fcc)) {\n\t\tH->notice_callback = emalloc(sizeof(zend_fcall_info_cache));\n\t\tzend_fcc_dup(H->notice_callback, &fcc);\n\t}\n\n\treturn;\n\ncleanup:\n\tif (ZEND_FCC_INITIALIZED(fcc)) {\n\t\tzend_fcc_dtor(&fcc);\n\t}\n\tRETURN_THROWS();\n}\n/* }}} */\n\nstatic const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind)\n{\n\tswitch (kind) {\n\t\tcase PDO_DBH_DRIVER_METHOD_KIND_DBH:\n\t\t\treturn class_PDO_PGSql_Ext_methods;\n\t\tdefault:\n\t\t\treturn NULL;\n\t}\n}\n\nstatic bool pdo_pgsql_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)\n{\n\tbool bval;\n\tpdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;\n\n\tswitch (attr) {\n\t\tcase PDO_ATTR_EMULATE_PREPARES:\n\t\t\tif (!pdo_get_bool_param(&bval, val)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tH->emulate_prepares = bval;\n\t\t\treturn true;\n\t\tcase PDO_PGSQL_ATTR_DISABLE_PREPARES:\n\t\t\tif (!pdo_get_bool_param(&bval, val)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tH->disable_prepares = bval;\n\t\t\treturn true;\n\t\tcase PDO_ATTR_PREFETCH:\n\t\t\tif (!pdo_get_bool_param(&bval, val)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tH->default_fetching_laziness = !bval;\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\nstatic const struct pdo_dbh_methods pgsql_methods = {\n\tpgsql_handle_closer,\n\tpgsql_handle_preparer,\n\tpgsql_handle_doer,\n\tpgsql_handle_quoter,\n\tpgsql_handle_begin,\n\tpgsql_handle_commit,\n\tpgsql_handle_rollback,\n\tpdo_pgsql_set_attr,\n\tpdo_pgsql_last_insert_id,\n\tpdo_pgsql_fetch_error_func,\n\tpdo_pgsql_get_attribute,\n\tpdo_pgsql_check_liveness,\t/* check_liveness */\n\tpdo_pgsql_get_driver_methods,  /* get_driver_methods */\n\tNULL,\n\tpgsql_handle_in_transaction,\n\tNULL, /* get_gc */\n\tpdo_pgsql_scanner\n};\n\nstatic int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */\n{\n\tpdo_pgsql_db_handle *H;\n\tint ret = 0;\n\tchar *p, *e;\n\tzend_string *tmp_user, *tmp_pass;\n\tsmart_str conn_str = {0};\n\tzend_long connect_timeout = 30;\n\n\tH = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent);\n\tdbh->driver_data = H;\n\n\tdbh->skip_param_evt =\n\t\t1 << PDO_PARAM_EVT_EXEC_POST |\n\t\t1 << PDO_PARAM_EVT_FETCH_PRE |\n\t\t1 << PDO_PARAM_EVT_FETCH_POST;\n\n\tH->einfo.errcode = 0;\n\tH->einfo.errmsg = NULL;\n\n\t/* PostgreSQL wants params in the connect string to be separated by spaces,\n\t * if the PDO standard semicolons are used, we convert them to spaces\n\t */\n\te = (char *) dbh->data_source + dbh->data_source_len;\n\tp = (char *) dbh->data_source;\n\twhile ((p = memchr(p, ';', (e - p)))) {\n\t\t*p = ' ';\n\t}\n\n\tif (driver_options) {\n\t\tconnect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30);\n\t}\n\n\t/* escape username and password, if provided */\n\ttmp_user = !strstr((char *) dbh->data_source, \"user=\") ? _pdo_pgsql_escape_credentials(dbh->username) : NULL;\n\ttmp_pass = !strstr((char *) dbh->data_source, \"password=\") ? _pdo_pgsql_escape_credentials(dbh->password) : NULL;\n\n\tsmart_str_appendl(&conn_str, dbh->data_source, dbh->data_source_len);\n\tsmart_str_append_printf(&conn_str, \" connect_timeout=\" ZEND_LONG_FMT, connect_timeout);\n\n\t/* support both full connection string & connection string + login and/or password */\n\tif (tmp_user) {\n\t\tsmart_str_append_printf(&conn_str, \" user='%s'\", ZSTR_VAL(tmp_user));\n\t}\n\n\tif (tmp_pass) {\n\t\tsmart_str_append_printf(&conn_str, \" password='%s'\", ZSTR_VAL(tmp_pass));\n\t}\n\tsmart_str_0(&conn_str);\n\n\tH->server = PQconnectdb(ZSTR_VAL(conn_str.s));\n\tH->lob_streams = (HashTable *) pemalloc(sizeof(HashTable), dbh->is_persistent);\n\tzend_hash_init(H->lob_streams, 0, NULL, NULL, 1);\n\n\tif (tmp_user) {\n\t\tzend_string_release_ex(tmp_user, 0);\n\t}\n\tif (tmp_pass) {\n\t\tzend_string_release_ex(tmp_pass, 0);\n\t}\n\n\tsmart_str_free(&conn_str);\n\n\tif (PQstatus(H->server) != CONNECTION_OK) {\n\t\tpdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE);\n\t\tgoto cleanup;\n\t}\n\n\tPQsetNoticeProcessor(H->server, _pdo_pgsql_notice, (void *)dbh);\n\n\tH->attached = 1;\n\tH->pgoid = -1;\n\n\tdbh->methods = &pgsql_methods;\n\tdbh->alloc_own_columns = 1;\n\tdbh->max_escaped_char_length = 2;\n\n\tret = 1;\n\ncleanup:\n\tdbh->methods = &pgsql_methods;\n\tif (!ret) {\n\t\tpgsql_handle_closer(dbh);\n\t}\n\n\treturn ret;\n}\n/* }}} */\n\nconst pdo_driver_t swoole_pdo_pgsql_driver = {\n\tPDO_DRIVER_HEADER(pgsql),\n\tpdo_pgsql_handle_factory\n};\n#endif"
  },
  {
    "path": "thirdparty/php85/pdo_pgsql/pgsql_driver_arginfo.h",
    "content": "/* This is a generated file, edit the .stub.php file instead.\n * Stub hash: 30c01b4d2e7f836b81a31dc0c1a115883eb41568 */\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)\n\tZEND_ARG_OBJ_TYPE_MASK(0, rows, Traversable, MAY_BE_ARRAY, NULL)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, \"\\\"\\\\t\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, \"\\\"\\\\\\\\\\\\\\\\N\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, 0, 2, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, \"\\\"\\\\t\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, \"\\\"\\\\\\\\\\\\\\\\N\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO(0, tableName, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, \"\\\"\\\\t\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nullAs, IS_STRING, 0, \"\\\"\\\\\\\\\\\\\\\\N\\\"\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fields, IS_STRING, 1, \"null\")\nZEND_END_ARG_INFO()\n\n#define arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, 0, 0, MAY_BE_STRING|MAY_BE_FALSE)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, 0, 0, 1)\n\tZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 0, \"\\\"rb\\\"\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, 0, 1, _IS_BOOL, 0)\n\tZEND_ARG_TYPE_INFO(0, oid, IS_STRING, 0)\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fetchMode, IS_LONG, 0, \"PDO::FETCH_DEFAULT\")\n\tZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeoutMilliseconds, IS_LONG, 0, \"0\")\nZEND_END_ARG_INFO()\n\nZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, 0, 0, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlGetNotify);\nZEND_METHOD(PDO_PGSql_Ext, pgsqlGetPid);\n\nstatic const zend_function_entry class_PDO_PGSql_Ext_methods[] = {\n\tZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromArray, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlCopyFromFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyFromFile, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlCopyToArray, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToArray, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlCopyToFile, arginfo_class_PDO_PGSql_Ext_pgsqlCopyToFile, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlLOBCreate, arginfo_class_PDO_PGSql_Ext_pgsqlLOBCreate, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlLOBOpen, arginfo_class_PDO_PGSql_Ext_pgsqlLOBOpen, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlLOBUnlink, arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlGetNotify, arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, ZEND_ACC_PUBLIC)\n\tZEND_ME(PDO_PGSql_Ext, pgsqlGetPid, arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, ZEND_ACC_PUBLIC)\n\tZEND_FE_END\n};\n"
  },
  {
    "path": "thirdparty/php85/pdo_pgsql/pgsql_sql_parser.c",
    "content": "/* Generated by re2c 4.3 on Fri Jul 18 21:15:02 2025 */\n/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Matteo Beccati <mbeccati@php.net>                            |\n  +----------------------------------------------------------------------+\n*/\n\n\n#include \"php.h\"\n#include \"ext/pdo/php_pdo_driver.h\"\n#include \"ext/pdo/pdo_sql_parser.h\"\n\nint pdo_pgsql_scanner(pdo_scanner_t *s)\n{\n\tconst char *cursor = s->cur;\n\n\ts->tok = cursor;\n\t\n\n\t\n{\n\tYYCTYPE yych;\n\tunsigned int yyaccept = 0;\n\tif ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);\n\tyych = *YYCURSOR;\n\tif (yych <= '.') {\n\t\tif (yych <= '$') {\n\t\t\tif (yych <= '!') {\n\t\t\t\tif (yych >= 0x01) goto yy2;\n\t\t\t} else {\n\t\t\t\tif (yych <= '\"') goto yy4;\n\t\t\t\tif (yych <= '#') goto yy2;\n\t\t\t\tgoto yy6;\n\t\t\t}\n\t\t} else {\n\t\t\tif (yych <= '\\'') {\n\t\t\t\tif (yych <= '&') goto yy2;\n\t\t\t\tgoto yy7;\n\t\t\t} else {\n\t\t\t\tif (yych == '-') goto yy8;\n\t\t\t\tgoto yy2;\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif (yych <= '?') {\n\t\t\tif (yych <= '9') {\n\t\t\t\tif (yych <= '/') goto yy9;\n\t\t\t\tgoto yy2;\n\t\t\t} else {\n\t\t\t\tif (yych <= ':') goto yy10;\n\t\t\t\tif (yych <= '>') goto yy2;\n\t\t\t\tgoto yy11;\n\t\t\t}\n\t\t} else {\n\t\t\tif (yych <= 'E') {\n\t\t\t\tif (yych <= 'D') goto yy2;\n\t\t\t\tgoto yy12;\n\t\t\t} else {\n\t\t\t\tif (yych == 'e') goto yy12;\n\t\t\t\tgoto yy2;\n\t\t\t}\n\t\t}\n\t}\nyy1:\n\tYYCURSOR = YYMARKER;\n\tif (yyaccept <= 1) {\n\t\tif (yyaccept == 0) goto yy5;\n\t\telse goto yy15;\n\t} else {\n\t\tif (yyaccept == 2) goto yy20;\n\t\telse goto yy31;\n\t}\nyy2:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tif (yych <= '.') {\n\t\tif (yych <= '$') {\n\t\t\tif (yych <= '!') {\n\t\t\t\tif (yych >= 0x01) goto yy2;\n\t\t\t} else {\n\t\t\t\tif (yych == '#') goto yy2;\n\t\t\t}\n\t\t} else {\n\t\t\tif (yych <= '\\'') {\n\t\t\t\tif (yych <= '&') goto yy2;\n\t\t\t} else {\n\t\t\t\tif (yych != '-') goto yy2;\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif (yych <= '?') {\n\t\t\tif (yych <= '9') {\n\t\t\t\tif (yych >= '0') goto yy2;\n\t\t\t} else {\n\t\t\t\tif (yych <= ':') goto yy3;\n\t\t\t\tif (yych <= '>') goto yy2;\n\t\t\t}\n\t\t} else {\n\t\t\tif (yych <= 'E') {\n\t\t\t\tif (yych <= 'D') goto yy2;\n\t\t\t} else {\n\t\t\t\tif (yych != 'e') goto yy2;\n\t\t\t}\n\t\t}\n\t}\nyy3:\n\t{ RET(PDO_PARSER_TEXT); }\nyy4:\n\tyyaccept = 0;\n\tyych = *(YYMARKER = ++YYCURSOR);\n\tif (yych >= 0x01) goto yy14;\nyy5:\n\t{ SKIP_ONE(PDO_PARSER_TEXT); }\nyy6:\n\tyyaccept = 0;\n\tyych = *(YYMARKER = ++YYCURSOR);\n\tif (yych <= '^') {\n\t\tif (yych <= '$') {\n\t\t\tif (yych <= '#') goto yy5;\n\t\t\tgoto yy16;\n\t\t} else {\n\t\t\tif (yych <= '@') goto yy5;\n\t\t\tif (yych <= 'Z') goto yy17;\n\t\t\tgoto yy5;\n\t\t}\n\t} else {\n\t\tif (yych <= '`') {\n\t\t\tif (yych <= '_') goto yy17;\n\t\t\tgoto yy5;\n\t\t} else {\n\t\t\tif (yych <= 'z') goto yy17;\n\t\t\tif (yych <= 0x7F) goto yy5;\n\t\t\tgoto yy17;\n\t\t}\n\t}\nyy7:\n\tyyaccept = 0;\n\tyych = *(YYMARKER = ++YYCURSOR);\n\tif (yych <= 0x00) goto yy5;\n\tgoto yy19;\nyy8:\n\tyych = *++YYCURSOR;\n\tif (yych == '-') goto yy21;\n\tgoto yy5;\nyy9:\n\tyych = *++YYCURSOR;\n\tif (yych == '*') goto yy23;\n\tgoto yy5;\nyy10:\n\tyych = *++YYCURSOR;\n\tif (yych <= 'Z') {\n\t\tif (yych <= '9') {\n\t\t\tif (yych <= '/') goto yy5;\n\t\t\tgoto yy24;\n\t\t} else {\n\t\t\tif (yych <= ':') goto yy26;\n\t\t\tif (yych <= '@') goto yy5;\n\t\t\tgoto yy24;\n\t\t}\n\t} else {\n\t\tif (yych <= '_') {\n\t\t\tif (yych <= '^') goto yy5;\n\t\t\tgoto yy24;\n\t\t} else {\n\t\t\tif (yych <= '`') goto yy5;\n\t\t\tif (yych <= 'z') goto yy24;\n\t\t\tgoto yy5;\n\t\t}\n\t}\nyy11:\n\tyych = *++YYCURSOR;\n\tif (yych == '?') goto yy27;\n\t{ RET(PDO_PARSER_BIND_POS); }\nyy12:\n\tyyaccept = 0;\n\tyych = *(YYMARKER = ++YYCURSOR);\n\tif (yych == '\\'') goto yy28;\n\tgoto yy5;\nyy13:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\nyy14:\n\tif (yych <= 0x00) goto yy1;\n\tif (yych != '\"') goto yy13;\n\tyyaccept = 1;\n\tYYMARKER = ++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tif (yych == '\"') goto yy13;\nyy15:\n\t{ RET(PDO_PARSER_TEXT); }\nyy16:\n\t++YYCURSOR;\n\t{ RET(PDO_PARSER_CUSTOM_QUOTE); }\nyy17:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tif (yych <= 'Z') {\n\t\tif (yych <= '/') {\n\t\t\tif (yych == '$') goto yy16;\n\t\t\tgoto yy1;\n\t\t} else {\n\t\t\tif (yych <= '9') goto yy17;\n\t\t\tif (yych <= '@') goto yy1;\n\t\t\tgoto yy17;\n\t\t}\n\t} else {\n\t\tif (yych <= '`') {\n\t\t\tif (yych == '_') goto yy17;\n\t\t\tgoto yy1;\n\t\t} else {\n\t\t\tif (yych <= 'z') goto yy17;\n\t\t\tif (yych <= 0x7F) goto yy1;\n\t\t\tgoto yy17;\n\t\t}\n\t}\nyy18:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\nyy19:\n\tif (yych <= 0x00) goto yy1;\n\tif (yych != '\\'') goto yy18;\n\tyyaccept = 2;\n\tYYMARKER = ++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tif (yych == '\\'') goto yy18;\nyy20:\n\t{ RET(PDO_PARSER_TEXT); }\nyy21:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tif (yych != '\\n') goto yy21;\nyy22:\n\t{ RET(PDO_PARSER_TEXT); }\nyy23:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tif (yych == '*') goto yy29;\n\tgoto yy23;\nyy24:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tif (yych <= 'Z') {\n\t\tif (yych <= '/') goto yy25;\n\t\tif (yych <= '9') goto yy24;\n\t\tif (yych >= 'A') goto yy24;\n\t} else {\n\t\tif (yych <= '_') {\n\t\t\tif (yych >= '_') goto yy24;\n\t\t} else {\n\t\t\tif (yych <= '`') goto yy25;\n\t\t\tif (yych <= 'z') goto yy24;\n\t\t}\n\t}\nyy25:\n\t{ RET(PDO_PARSER_BIND); }\nyy26:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tif (yych == ':') goto yy26;\n\t{ RET(PDO_PARSER_TEXT); }\nyy27:\n\t++YYCURSOR;\n\t{ RET(PDO_PARSER_ESCAPED_QUESTION); }\nyy28:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tif (yych <= '\\'') {\n\t\tif (yych <= 0x00) goto yy1;\n\t\tif (yych <= '&') goto yy28;\n\t\tgoto yy30;\n\t} else {\n\t\tif (yych == '\\\\') goto yy32;\n\t\tgoto yy28;\n\t}\nyy29:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tif (yych == '*') goto yy29;\n\tif (yych == '/') goto yy33;\n\tgoto yy23;\nyy30:\n\tyyaccept = 3;\n\tYYMARKER = ++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tif (yych == '\\'') goto yy28;\nyy31:\n\t{ RET(PDO_PARSER_TEXT); }\nyy32:\n\t++YYCURSOR;\n\tif (YYLIMIT <= YYCURSOR) YYFILL(1);\n\tyych = *YYCURSOR;\n\tif (yych <= 0x00) goto yy1;\n\tgoto yy28;\nyy33:\n\t++YYCURSOR;\n\tgoto yy22;\n}\n\n}\n"
  },
  {
    "path": "thirdparty/php85/pdo_pgsql/pgsql_statement.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Authors: Edin Kadribasic <edink@emini.dk>                            |\n  |          Ilia Alshanestsky <ilia@prohost.org>                        |\n  |          Wez Furlong <wez@php.net>                                   |\n  +----------------------------------------------------------------------+\n*/\n\n#define SW_USE_PGSQL_HOOK\n#include \"php_swoole_pgsql.h\"\n\n#if PHP_VERSION_ID >= 80500\n#include \"php.h\"\n#include \"php_ini.h\"\n#include \"ext/standard/info.h\"\n#include \"ext/pdo/php_pdo.h\"\n#include \"ext/pdo/php_pdo_driver.h\"\n#include \"php_pdo_pgsql.h\"\n#include \"php_pdo_pgsql_int.h\"\n#ifdef HAVE_NETINET_IN_H\n#include <netinet/in.h>\n#endif\n\n/* from postgresql/src/include/catalog/pg_type.h */\n#define BOOLLABEL   \"bool\"\n#define BOOLOID     16\n#define BYTEALABEL  \"bytea\"\n#define BYTEAOID    17\n#define DATELABEL   \"date\"\n#define DATEOID     1082\n#define INT2LABEL   \"int2\"\n#define INT2OID     21\n#define INT4LABEL   \"int4\"\n#define INT4OID     23\n#define INT8LABEL   \"int8\"\n#define INT8OID     20\n#define OIDOID      26\n#define TEXTLABEL   \"text\"\n#define TEXTOID     25\n#define TIMESTAMPLABEL \"timestamp\"\n#define TIMESTAMPOID   1114\n#define VARCHARLABEL \"varchar\"\n#define VARCHAROID   1043\n#define FLOAT4LABEL \"float4\"\n#define FLOAT4OID 700\n#define FLOAT8LABEL \"float8\"\n#define FLOAT8OID 701\n\n#define FIN_DISCARD 0x1\n#define FIN_CLOSE   0x2\n#define FIN_ABORT   0x4\n\n\n\nstatic void pgsql_stmt_finish(pdo_pgsql_stmt *S, int fin_mode)\n{\n\tpdo_pgsql_db_handle *H = S->H;\n\n\tif (S->is_running_unbuffered && S->result && (fin_mode & FIN_ABORT)) {\n\t\tPGcancel *cancel = PQgetCancel(H->server);\n\t\tchar errbuf[256];\n\t\tPQcancel(cancel, errbuf, 256);\n\t\tPQfreeCancel(cancel);\n\t\tS->is_running_unbuffered = false;\n\t}\n\n\tif (S->result) {\n\t\t/* free the resource */\n\t\tPQclear(S->result);\n\t\tS->result = NULL;\n\t}\n\n\tif (S->is_running_unbuffered) {\n\t\t/* https://postgresql.org/docs/current/libpq-async.html:\n\t\t * \"PQsendQuery cannot be called again until PQgetResult has returned NULL\"\n\t\t * And as all single-row functions are connection-wise instead of statement-wise,\n\t\t * any new single-row query has to make sure no preceding one is still running.\n\t\t */\n\t\t// @todo Implement !(fin_mode & FIN_DISCARD)\n\t\t//       instead of discarding results we could store them to their statement\n\t\t//       so that their fetch() will get them (albeit not in lazy mode anymore).\n\t\twhile ((S->result = PQgetResult(H->server))) {\n\t\t\tPQclear(S->result);\n\t\t\tS->result = NULL;\n\t\t}\n\t\tS->is_running_unbuffered = false;\n\t}\n\n\tif (S->stmt_name && S->is_prepared && (fin_mode & FIN_CLOSE)) {\n\t\tPGresult *res;\n#ifndef HAVE_PQCLOSEPREPARED\n\t\t// TODO (??) libpq does not support close statement protocol < postgres 17\n\t\t// check if we can circumvent this.\n\t\tchar *q = NULL;\n\t\tspprintf(&q, 0, \"DEALLOCATE %s\", S->stmt_name);\n\t\tres = PQexec(H->server, q);\n\t\tefree(q);\n#else\n\t\tres = PQclosePrepared(H->server, S->stmt_name);\n#endif\n\t\tif (res) {\n\t\t\tPQclear(res);\n\t\t}\n\n\t\tS->is_prepared = false;\n\t\tif (H->running_stmt == S) {\n\t\t\tH->running_stmt = NULL;\n\t\t}\n\t}\n}\n\nstatic int pgsql_stmt_dtor(pdo_stmt_t *stmt)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\tbool server_obj_usable = php_pdo_stmt_valid_db_obj_handle(stmt);\n\n\tpgsql_stmt_finish(S, FIN_DISCARD|(server_obj_usable ? FIN_CLOSE|FIN_ABORT : 0));\n\n\tif (S->stmt_name) {\n\t\tefree(S->stmt_name);\n\t\tS->stmt_name = NULL;\n\t}\n\tif (S->param_lengths) {\n\t\tefree(S->param_lengths);\n\t\tS->param_lengths = NULL;\n\t}\n\tif (S->param_values) {\n\t\tefree(S->param_values);\n\t\tS->param_values = NULL;\n\t}\n\tif (S->param_formats) {\n\t\tefree(S->param_formats);\n\t\tS->param_formats = NULL;\n\t}\n\tif (S->param_types) {\n\t\tefree(S->param_types);\n\t\tS->param_types = NULL;\n\t}\n\tif (S->query) {\n\t\tzend_string_release(S->query);\n\t\tS->query = NULL;\n\t}\n\n\tif (S->cursor_name) {\n\t\tif (server_obj_usable) {\n\t\t\tpdo_pgsql_db_handle *H = S->H;\n\t\t\tchar *q = NULL;\n\t\t\tPGresult *res;\n\n\t\t\tspprintf(&q, 0, \"CLOSE %s\", S->cursor_name);\n\t\t\tres = PQexec(H->server, q);\n\t\t\tefree(q);\n\t\t\tif (res) PQclear(res);\n\t\t}\n\t\tefree(S->cursor_name);\n\t\tS->cursor_name = NULL;\n\t}\n\n\tif(S->cols) {\n\t\tefree(S->cols);\n\t\tS->cols = NULL;\n\t}\n\tefree(S);\n\tstmt->driver_data = NULL;\n\treturn 1;\n}\n\nstatic int pgsql_stmt_execute(pdo_stmt_t *stmt)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\tpdo_pgsql_db_handle *H = S->H;\n\tExecStatusType status;\n\tint dispatch_result = 1;\n\n\tbool in_trans = stmt->dbh->methods->in_transaction(stmt->dbh);\n\n\t/* in unbuffered mode, finish any running statement: libpq explicitely prohibits this\n\t * and returns a PGRES_FATAL_ERROR when PQgetResult gets called for stmt 2 if DEALLOCATE\n\t * was called for stmt 1 inbetween\n\t * (maybe it will change with pipeline mode in libpq 14?) */\n\tif (S->is_unbuffered && H->running_stmt) {\n\t\tpgsql_stmt_finish(H->running_stmt, FIN_CLOSE);\n\t\tH->running_stmt = NULL;\n\t}\n\t/* ensure that we free any previous unfetched results */\n\tpgsql_stmt_finish(S, 0);\n\n\tS->current_row = 0;\n\n\tif (S->cursor_name) {\n\t\tchar *q = NULL;\n\n\t\tif (S->is_prepared) {\n\t\t\tspprintf(&q, 0, \"CLOSE %s\", S->cursor_name);\n\t\t\tPQclear(PQexec(H->server, q));\n\t\t\tefree(q);\n\t\t}\n\n\t\tspprintf(&q, 0, \"DECLARE %s SCROLL CURSOR WITH HOLD FOR %s\", S->cursor_name, ZSTR_VAL(stmt->active_query_string));\n\t\tS->result = PQexec(H->server, q);\n\t\tefree(q);\n\n\t\t/* check if declare failed */\n\t\tstatus = PQresultStatus(S->result);\n\t\tif (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {\n\t\t\tpdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));\n\t\t\treturn 0;\n\t\t}\n\t\tPQclear(S->result);\n\n\t\t/* the cursor was declared correctly */\n\t\tS->is_prepared = true;\n\n\t\t/* fetch to be able to get the number of tuples later, but don't advance the cursor pointer */\n\t\tspprintf(&q, 0, \"FETCH FORWARD 0 FROM %s\", S->cursor_name);\n\t\tS->result = PQexec(H->server, q);\n\t\tefree(q);\n\t} else if (S->stmt_name) {\n\t\t/* using a prepared statement */\n\n\t\tif (!S->is_prepared) {\nstmt_retry:\n\t\t\t/* we deferred the prepare until now, because we didn't\n\t\t\t * know anything about the parameter types; now we do */\n\t\t\tS->result = PQprepare(H->server, S->stmt_name, ZSTR_VAL(S->query),\n\t\t\t\t\t\tstmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0,\n\t\t\t\t\t\tS->param_types);\n\t\t\tstatus = PQresultStatus(S->result);\n\t\t\tswitch (status) {\n\t\t\t\tcase PGRES_COMMAND_OK:\n\t\t\t\tcase PGRES_TUPLES_OK:\n\t\t\t\t\t/* it worked */\n\t\t\t\t\tS->is_prepared = true;\n\t\t\t\t\tPQclear(S->result);\n\t\t\t\t\tS->result = NULL;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: {\n\t\t\t\t\tchar *sqlstate = pdo_pgsql_sqlstate(S->result);\n\t\t\t\t\t/* 42P05 means that the prepared statement already existed. this can happen if you use\n\t\t\t\t\t * a connection pooling software line pgpool which doesn't close the db-connection once\n\t\t\t\t\t * php disconnects. if php dies (no chance to run RSHUTDOWN) during execution it has no\n\t\t\t\t\t * chance to DEALLOCATE the prepared statements it has created. so, if we hit a 42P05 we\n\t\t\t\t\t * deallocate it and retry ONCE (thies 2005.12.15)\n\t\t\t\t\t */\n\t\t\t\t\tif (sqlstate && !strcmp(sqlstate, \"42P05\")) {\n\t\t\t\t\t\tPGresult *res;\n#ifndef HAVE_PQCLOSEPREPARED\n\t\t\t\t\t\tchar buf[100]; /* stmt_name == \"pdo_crsr_%08x\" */\n\t\t\t\t\t\tsnprintf(buf, sizeof(buf), \"DEALLOCATE %s\", S->stmt_name);\n\t\t\t\t\t\tres = PQexec(H->server, buf);\n#else\n\t\t\t\t\t\tres = PQclosePrepared(H->server, S->stmt_name);\n#endif\n\t\t\t\t\t\tif (res) {\n\t\t\t\t\t\t\tPQclear(res);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tgoto stmt_retry;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpdo_pgsql_error_stmt(stmt, status, sqlstate);\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (S->is_unbuffered) {\n\t\t\tdispatch_result = PQsendQueryPrepared(H->server, S->stmt_name,\n\t\t\t\t\tstmt->bound_params ?\n\t\t\t\t\t\tzend_hash_num_elements(stmt->bound_params) :\n\t\t\t\t\t\t0,\n\t\t\t\t\t(const char**)S->param_values,\n\t\t\t\t\tS->param_lengths,\n\t\t\t\t\tS->param_formats,\n\t\t\t\t\t0);\n\t\t} else {\n\t\t\tS->result = PQexecPrepared(H->server, S->stmt_name,\n\t\t\t\tstmt->bound_params ?\n\t\t\t\t\tzend_hash_num_elements(stmt->bound_params) :\n\t\t\t\t\t0,\n\t\t\t\t(const char**)S->param_values,\n\t\t\t\tS->param_lengths,\n\t\t\t\tS->param_formats,\n\t\t\t\t0);\n\t\t}\n\t} else if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED) {\n\t\t/* execute query with parameters */\n\t\tif (S->is_unbuffered) {\n\t\t\tdispatch_result = PQsendQueryParams(H->server, ZSTR_VAL(S->query),\n\t\t\t\t\tstmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0,\n\t\t\t\t\tS->param_types,\n\t\t\t\t\t(const char**)S->param_values,\n\t\t\t\t\tS->param_lengths,\n\t\t\t\t\tS->param_formats,\n\t\t\t\t\t0);\n\t\t} else {\n\t\t\tS->result = PQexecParams(H->server, ZSTR_VAL(S->query),\n\t\t\t\tstmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0,\n\t\t\t\tS->param_types,\n\t\t\t\t(const char**)S->param_values,\n\t\t\t\tS->param_lengths,\n\t\t\t\tS->param_formats,\n\t\t\t\t0);\n\t\t}\n\t} else {\n\t\t/* execute plain query (with embedded parameters) */\n\t\tif (S->is_unbuffered) {\n\t\t\tdispatch_result = PQsendQuery(H->server, ZSTR_VAL(stmt->active_query_string));\n\t\t} else {\n\t\t\tS->result = PQexec(H->server, ZSTR_VAL(stmt->active_query_string));\n\t\t}\n\t}\n\n\tH->running_stmt = S;\n\n\tif (S->is_unbuffered) {\n\t\tif (!dispatch_result) {\n\t\t\tpdo_pgsql_error_stmt(stmt, 0, NULL);\n\t\t\tH->running_stmt = NULL;\n\t\t\treturn 0;\n\t\t}\n\t\tS->is_running_unbuffered = true;\n\t\t(void)PQsetSingleRowMode(H->server);\n\t\t/* no matter if it returns 0: PQ then transparently fallbacks to full result fetching */\n\n\t\t/* try a first fetch to at least have column names and so on */\n\t\tS->result = PQgetResult(S->H->server);\n\t}\n\n\tstatus = PQresultStatus(S->result);\n\n\tif (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK && status != PGRES_SINGLE_TUPLE) {\n\t\tpdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));\n\t\treturn 0;\n\t}\n\n\tstmt->column_count = (int) PQnfields(S->result);\n\tif (S->cols == NULL) {\n\t\tS->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column));\n\t}\n\n\tif (status == PGRES_COMMAND_OK) {\n\t\tstmt->row_count = ZEND_ATOL(PQcmdTuples(S->result));\n\t\tH->pgoid = PQoidValue(S->result);\n\t} else {\n\t\tstmt->row_count = (zend_long)PQntuples(S->result);\n\t}\n\n\tif (in_trans && !stmt->dbh->methods->in_transaction(stmt->dbh)) {\n\t\tpdo_pgsql_close_lob_streams(stmt->dbh);\n\t}\n\n\treturn 1;\n}\n\nstatic int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,\n\t\tenum pdo_param_event event_type)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\n\tif (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED && param->is_param) {\n\t\tswitch (event_type) {\n\t\t\tcase PDO_PARAM_EVT_FREE:\n\t\t\t\tif (param->driver_data) {\n\t\t\t\t\tefree(param->driver_data);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase PDO_PARAM_EVT_NORMALIZE:\n\t\t\t\t/* decode name from $1, $2 into 0, 1 etc. */\n\t\t\t\tif (param->name) {\n\t\t\t\t\tif (ZSTR_VAL(param->name)[0] == '$') {\n\t\t\t\t\t\tparam->paramno = ZEND_ATOL(ZSTR_VAL(param->name) + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t/* resolve parameter name to rewritten name */\n\t\t\t\t\t\tzend_string *namevar;\n\n\t\t\t\t\t\tif (stmt->bound_param_map && (namevar = zend_hash_find_ptr(stmt->bound_param_map,\n\t\t\t\t\t\t\t\tparam->name)) != NULL) {\n\t\t\t\t\t\t\tparam->paramno = ZEND_ATOL(ZSTR_VAL(namevar) + 1);\n\t\t\t\t\t\t\tparam->paramno--;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tpdo_pgsql_error_stmt_msg(stmt, 0, \"HY093\", ZSTR_VAL(param->name));\n\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase PDO_PARAM_EVT_ALLOC:\n\t\t\t\tif (!stmt->bound_param_map) {\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tif (!zend_hash_index_exists(stmt->bound_param_map, param->paramno)) {\n\t\t\t\t\tpdo_pgsql_error_stmt_msg(stmt, 0, \"HY093\", \"parameter was not defined\");\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tZEND_FALLTHROUGH;\n\t\t\tcase PDO_PARAM_EVT_EXEC_POST:\n\t\t\tcase PDO_PARAM_EVT_FETCH_PRE:\n\t\t\tcase PDO_PARAM_EVT_FETCH_POST:\n\t\t\t\t/* work is handled by EVT_NORMALIZE */\n\t\t\t\treturn 1;\n\n\t\t\tcase PDO_PARAM_EVT_EXEC_PRE:\n\t\t\t\tif (!stmt->bound_param_map) {\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tif (!S->param_values) {\n\t\t\t\t\tS->param_values = ecalloc(\n\t\t\t\t\t\t\tzend_hash_num_elements(stmt->bound_param_map),\n\t\t\t\t\t\t\tsizeof(char*));\n\t\t\t\t\tS->param_lengths = ecalloc(\n\t\t\t\t\t\t\tzend_hash_num_elements(stmt->bound_param_map),\n\t\t\t\t\t\t\tsizeof(int));\n\t\t\t\t\tS->param_formats = ecalloc(\n\t\t\t\t\t\t\tzend_hash_num_elements(stmt->bound_param_map),\n\t\t\t\t\t\t\tsizeof(int));\n\t\t\t\t\tS->param_types = ecalloc(\n\t\t\t\t\t\t\tzend_hash_num_elements(stmt->bound_param_map),\n\t\t\t\t\t\t\tsizeof(Oid));\n\t\t\t\t}\n\t\t\t\tif (param->paramno >= 0) {\n\t\t\t\t\tzval *parameter;\n\n\t\t\t\t\t/*\n\t\t\t\t\tif (param->paramno >= zend_hash_num_elements(stmt->bound_params)) {\n\t\t\t\t\t\tpdo_raise_impl_error(stmt->dbh, stmt, \"HY093\", \"parameter was not defined\");\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\t}\n\t\t\t\t\t*/\n\n\t\t\t\t\tif (Z_ISREF(param->parameter)) {\n\t\t\t\t\t\tparameter = Z_REFVAL(param->parameter);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tparameter = &param->parameter;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB &&\n\t\t\t\t\t\t\tZ_TYPE_P(parameter) == IS_RESOURCE) {\n\t\t\t\t\t\tphp_stream *stm;\n\t\t\t\t\t\tphp_stream_from_zval_no_verify(stm, parameter);\n\t\t\t\t\t\tif (stm) {\n\t\t\t\t\t\t\tif (php_stream_is(stm, &pdo_pgsql_lob_stream_ops)) {\n\t\t\t\t\t\t\t\tstruct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stm->abstract;\n\t\t\t\t\t\t\t\tpdo_pgsql_bound_param *P = param->driver_data;\n\n\t\t\t\t\t\t\t\tif (P == NULL) {\n\t\t\t\t\t\t\t\t\tP = ecalloc(1, sizeof(*P));\n\t\t\t\t\t\t\t\t\tparam->driver_data = P;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tP->oid = htonl(self->oid);\n\t\t\t\t\t\t\t\tS->param_values[param->paramno] = (char*)&P->oid;\n\t\t\t\t\t\t\t\tS->param_lengths[param->paramno] = sizeof(P->oid);\n\t\t\t\t\t\t\t\tS->param_formats[param->paramno] = 1;\n\t\t\t\t\t\t\t\tS->param_types[param->paramno] = OIDOID;\n\t\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tzend_string *str = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0);\n\t\t\t\t\t\t\t\tif (str != NULL) {\n\t\t\t\t\t\t\t\t\tZVAL_STR(parameter, str);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tZVAL_EMPTY_STRING(parameter);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t/* expected a stream resource */\n\t\t\t\t\t\t\tpdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, \"HY105\");\n\t\t\t\t\t\t\treturn 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL ||\n\t\t\t\t\t\t\tZ_TYPE_P(parameter) == IS_NULL) {\n\t\t\t\t\t\tS->param_values[param->paramno] = NULL;\n\t\t\t\t\t\tS->param_lengths[param->paramno] = 0;\n\t\t\t\t\t} else if (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE) {\n\t\t\t\t\t\tS->param_values[param->paramno] = Z_TYPE_P(parameter) == IS_TRUE ? \"t\" : \"f\";\n\t\t\t\t\t\tS->param_lengths[param->paramno] = 1;\n\t\t\t\t\t\tS->param_formats[param->paramno] = 0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconvert_to_string(parameter);\n\t\t\t\t\t\tS->param_values[param->paramno] = Z_STRVAL_P(parameter);\n\t\t\t\t\t\tS->param_lengths[param->paramno] = Z_STRLEN_P(parameter);\n\t\t\t\t\t\tS->param_formats[param->paramno] = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {\n\t\t\t\t\t\tS->param_types[param->paramno] = 0;\n\t\t\t\t\t\tS->param_formats[param->paramno] = 1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tS->param_types[param->paramno] = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t} else if (param->is_param && event_type == PDO_PARAM_EVT_NORMALIZE) {\n\t\t/* We need to manually convert to a pg native boolean value */\n\t\tif (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL &&\n\t\t\t((param->param_type & PDO_PARAM_INPUT_OUTPUT) != PDO_PARAM_INPUT_OUTPUT)) {\n\t\t\tconst char *s = zend_is_true(&param->parameter) ? \"t\" : \"f\";\n\t\t\tparam->param_type = PDO_PARAM_STR;\n\t\t\tzval_ptr_dtor(&param->parameter);\n\t\t\tZVAL_STRINGL(&param->parameter, s, 1);\n\t\t}\n\t}\n\treturn 1;\n}\n\nstatic int pgsql_stmt_fetch(pdo_stmt_t *stmt,\n\tenum pdo_fetch_orientation ori, zend_long offset)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\n\tif (S->cursor_name) {\n\t\tchar *ori_str = NULL;\n\t\tchar *q = NULL;\n\t\tExecStatusType status;\n\n\t\tswitch (ori) {\n\t\t\tcase PDO_FETCH_ORI_NEXT: \tori_str = \"NEXT\"; break;\n\t\t\tcase PDO_FETCH_ORI_PRIOR:\tori_str = \"BACKWARD\"; break;\n\t\t\tcase PDO_FETCH_ORI_FIRST:\tori_str = \"FIRST\"; break;\n\t\t\tcase PDO_FETCH_ORI_LAST:\tori_str = \"LAST\"; break;\n\t\t\tcase PDO_FETCH_ORI_ABS:\t\tspprintf(&ori_str, 0, \"ABSOLUTE \" ZEND_LONG_FMT, offset); break;\n\t\t\tcase PDO_FETCH_ORI_REL:\t\tspprintf(&ori_str, 0, \"RELATIVE \" ZEND_LONG_FMT, offset); break;\n\t\t\tdefault:\n\t\t\t\treturn 0;\n\t\t}\n\n\t\tif(S->result) {\n\t\t\tPQclear(S->result);\n\t\t\tS->result = NULL;\n\t\t}\n\n\t\tspprintf(&q, 0, \"FETCH %s FROM %s\", ori_str, S->cursor_name);\n\t\tif (ori == PDO_FETCH_ORI_ABS || ori == PDO_FETCH_ORI_REL) {\n\t\t\tefree(ori_str);\n\t\t}\n\t\tS->result = PQexec(S->H->server, q);\n\t\tefree(q);\n\t\tstatus = PQresultStatus(S->result);\n\n\t\tif (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {\n\t\t\tpdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));\n\t\t\treturn 0;\n\t\t}\n\n\t\tif (PQntuples(S->result)) {\n\t\t\tS->current_row = 1;\n\t\t\treturn 1;\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t} else {\n\t\tif (S->is_running_unbuffered && S->current_row >= stmt->row_count) {\n\t\t\tExecStatusType status;\n\n\t\t\t/* @todo in unbuffered mode, PQ allows multiple queries to be passed:\n\t\t\t *       column_count should be recomputed on each iteration */\n\n\t\t\tif(S->result) {\n\t\t\t\tPQclear(S->result);\n\t\t\t\tS->result = NULL;\n\t\t\t}\n\n\t\t\tS->result = PQgetResult(S->H->server);\n\t\t\tstatus = PQresultStatus(S->result);\n\n\t\t\tif (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK && status != PGRES_SINGLE_TUPLE) {\n\t\t\t\tpdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tstmt->row_count = (zend_long)PQntuples(S->result);\n\t\t\tS->current_row = 0;\n\n\t\t\tif (!stmt->row_count) {\n\t\t\t\tS->is_running_unbuffered = false;\n\t\t\t\t/* libpq requires looping until getResult returns null */\n\t\t\t\tpgsql_stmt_finish(S, 0);\n\t\t\t}\n\t\t}\n\t\tif (S->current_row < stmt->row_count) {\n\t\t\tS->current_row++;\n\t\t\treturn 1;\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t}\n}\n\nstatic int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\tstruct pdo_column_data *cols = stmt->columns;\n\tchar *str;\n\n\tif (!S->result) {\n\t\treturn 0;\n\t}\n\n\tstr = PQfname(S->result, colno);\n\tcols[colno].name = zend_string_init(str, strlen(str), 0);\n\tcols[colno].maxlen = PQfsize(S->result, colno);\n\tcols[colno].precision = PQfmod(S->result, colno);\n\tS->cols[colno].pgsql_type = PQftype(S->result, colno);\n\n\treturn 1;\n}\n\nstatic int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\tif (!S->result) {\n\t\treturn 0;\n\t}\n\n\t/* We have already increased count by 1 in pgsql_stmt_fetch() */\n\tif (PQgetisnull(S->result, S->current_row - 1, colno)) { /* Check if we got NULL */\n\t\tZVAL_NULL(result);\n\t} else {\n\t\tchar *ptr = PQgetvalue(S->result, S->current_row - 1, colno);\n\t\tsize_t len = PQgetlength(S->result, S->current_row - 1, colno);\n\n\t\tswitch (S->cols[colno].pgsql_type) {\n\t\t\tcase BOOLOID:\n\t\t\t\tZVAL_BOOL(result, *ptr == 't');\n\t\t\t\tbreak;\n\n\t\t\tcase INT2OID:\n\t\t\tcase INT4OID:\n#if SIZEOF_ZEND_LONG >= 8\n\t\t\tcase INT8OID:\n#endif\n\t\t\t\tZVAL_LONG(result, ZEND_ATOL(ptr));\n\t\t\t\tbreak;\n\t\t\tcase FLOAT4OID:\n\t\t\tcase FLOAT8OID:\n                if (strncmp(ptr, \"Infinity\", len) == 0) {\n                    ZVAL_DOUBLE(result, ZEND_INFINITY);\n                } else if (strncmp(ptr, \"-Infinity\", len) == 0) {\n                    ZVAL_DOUBLE(result, -ZEND_INFINITY);\n                } else if (strncmp(ptr, \"NaN\", len) == 0) {\n                    ZVAL_DOUBLE(result, ZEND_NAN);\n                } else {\n                    ZVAL_DOUBLE(result, zend_strtod(ptr, NULL));\n                }\n\t\t\t\tbreak;\n\n\t\t\tcase OIDOID: {\n\t\t\t\tchar *end_ptr;\n\t\t\t\tOid oid = (Oid)strtoul(ptr, &end_ptr, 10);\n\t\t\t\tif (type && *type == PDO_PARAM_LOB) {\n\t\t\t\t\t/* If column was bound as LOB, return a stream. */\n\t\t\t\t\tint loid = lo_open(S->H->server, oid, INV_READ);\n\t\t\t\t\tif (loid >= 0) {\n\t\t\t\t\t\tphp_stream *stream = pdo_pgsql_create_lob_stream(stmt->database_object_handle, loid, oid);\n\t\t\t\t\t\tif (stream) {\n\t\t\t\t\t\t\tphp_stream_to_zval(stream, result);\n\t\t\t\t\t\t\treturn 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn 0;\n\t\t\t\t} else {\n\t\t\t\t\t/* Otherwise return OID as integer. */\n\t\t\t\t\tZVAL_LONG(result, oid);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase BYTEAOID: {\n\t\t\t\tsize_t tmp_len;\n\t\t\t\tchar *tmp_ptr = (char *)PQunescapeBytea((unsigned char *) ptr, &tmp_len);\n\t\t\t\tif (!tmp_ptr) {\n\t\t\t\t\t/* PQunescapeBytea returned an error */\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\n\t\t\t\tzend_string *str = zend_string_init(tmp_ptr, tmp_len, 0);\n\t\t\t\tphp_stream *stream = php_stream_memory_open(TEMP_STREAM_READONLY, str);\n\t\t\t\tphp_stream_to_zval(stream, result);\n\t\t\t\tzend_string_release(str);\n\t\t\t\tPQfreemem(tmp_ptr);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tZVAL_STRINGL_FAST(result, ptr, len);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn 1;\n}\n\nstatic zend_always_inline char * pdo_pgsql_translate_oid_to_table(Oid oid, PGconn *conn)\n{\n\tchar *table_name = NULL;\n\tPGresult *tmp_res;\n\tchar *querystr = NULL;\n\n\tspprintf(&querystr, 0, \"SELECT RELNAME FROM PG_CLASS WHERE OID=%d\", oid);\n\n\tif ((tmp_res = PQexec(conn, querystr)) == NULL || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) {\n\t\tif (tmp_res) {\n\t\t\tPQclear(tmp_res);\n\t\t}\n\t\tefree(querystr);\n\t\treturn 0;\n\t}\n\tefree(querystr);\n\n\tif (1 == PQgetisnull(tmp_res, 0, 0) || (table_name = PQgetvalue(tmp_res, 0, 0)) == NULL) {\n\t\tPQclear(tmp_res);\n\t\treturn 0;\n\t}\n\n\ttable_name = estrdup(table_name);\n\n\tPQclear(tmp_res);\n\treturn table_name;\n}\n\nstatic int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\tPGresult *res;\n\tchar *q=NULL;\n\tExecStatusType status;\n\tOid table_oid;\n\tchar *table_name=NULL;\n\n\tif (!S->result) {\n\t\treturn FAILURE;\n\t}\n\n\tif (colno >= stmt->column_count) {\n\t\treturn FAILURE;\n\t}\n\n\tarray_init(return_value);\n\tadd_assoc_long(return_value, \"pgsql:oid\", S->cols[colno].pgsql_type);\n\n\ttable_oid = PQftable(S->result, colno);\n\tadd_assoc_long(return_value, \"pgsql:table_oid\", table_oid);\n\ttable_name = pdo_pgsql_translate_oid_to_table(table_oid, S->H->server);\n\tif (table_name) {\n\t\tadd_assoc_string(return_value, \"table\", table_name);\n\t\tefree(table_name);\n\t}\n\n\tswitch (S->cols[colno].pgsql_type) {\n\t\tcase BOOLOID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", BOOLLABEL);\n\t\t\tbreak;\n\t\tcase BYTEAOID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", BYTEALABEL);\n\t\t\tbreak;\n\t\tcase INT8OID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", INT8LABEL);\n\t\t\tbreak;\n\t\tcase INT2OID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", INT2LABEL);\n\t\t\tbreak;\n\t\tcase INT4OID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", INT4LABEL);\n\t\t\tbreak;\n\t\tcase FLOAT4OID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", FLOAT4LABEL);\n\t\t\tbreak;\n\t\tcase FLOAT8OID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", FLOAT8LABEL);\n\t\t\tbreak;\n\t\tcase TEXTOID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", TEXTLABEL);\n\t\t\tbreak;\n\t\tcase VARCHAROID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", VARCHARLABEL);\n\t\t\tbreak;\n\t\tcase DATEOID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", DATELABEL);\n\t\t\tbreak;\n\t\tcase TIMESTAMPOID:\n\t\t\tadd_assoc_string(return_value, \"native_type\", TIMESTAMPLABEL);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t/* Fetch metadata from Postgres system catalogue */\n\t\t\tspprintf(&q, 0, \"SELECT TYPNAME FROM PG_TYPE WHERE OID=%u\", S->cols[colno].pgsql_type);\n\t\t\tres = PQexec(S->H->server, q);\n\t\t\tefree(q);\n\t\t\tstatus = PQresultStatus(res);\n\t\t\tif (status == PGRES_TUPLES_OK && 1 == PQntuples(res)) {\n\t\t\t\tadd_assoc_string(return_value, \"native_type\", PQgetvalue(res, 0, 0));\n\t\t\t}\n\t\t\tPQclear(res);\n\t}\n\n\tenum pdo_param_type param_type;\n\tswitch (S->cols[colno].pgsql_type) {\n\t\tcase BOOLOID:\n\t\t\tparam_type = PDO_PARAM_BOOL;\n\t\t\tbreak;\n\t\tcase INT2OID:\n\t\tcase INT4OID:\n\t\tcase INT8OID:\n\t\t\tparam_type = PDO_PARAM_INT;\n\t\t\tbreak;\n\t\tcase OIDOID:\n\t\tcase BYTEAOID:\n\t\t\tparam_type = PDO_PARAM_LOB;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tparam_type = PDO_PARAM_STR;\n\t}\n\tadd_assoc_long(return_value, \"pdo_type\", param_type);\n\n\treturn 1;\n}\n\nstatic int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt)\n{\n\treturn 1;\n}\n\nstatic int pgsql_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val)\n{\n\tpdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;\n\n\tswitch (attr) {\n#ifdef HAVE_PG_RESULT_MEMORY_SIZE\n\t\tcase PDO_PGSQL_ATTR_RESULT_MEMORY_SIZE:\n\t\t\tif(stmt->executed) {\n\t\t\t\tZVAL_LONG(val, PQresultMemorySize(S->result));\n\t\t\t} else {\n\t\t\t\tchar *tmp;\n\t\t\t\tspprintf(&tmp, 0, \"statement '%s' has not been executed yet\", S->stmt_name);\n\n\t\t\t\tpdo_pgsql_error_stmt_msg(stmt, 0, \"HY000\", tmp);\n\t\t\t\tefree(tmp);\n\n\t\t\t\tZVAL_NULL(val);\n\t\t\t}\n\t\t\treturn 1;\n#endif\n\n\t\tdefault:\n\t\t\t(void)S;\n\t\t\treturn 0;\n\t}\n}\n\nconst struct pdo_stmt_methods swoole_pgsql_stmt_methods = {\n\tpgsql_stmt_dtor,\n\tpgsql_stmt_execute,\n\tpgsql_stmt_fetch,\n\tpgsql_stmt_describe,\n\tpgsql_stmt_get_col,\n\tpgsql_stmt_param_hook,\n\tNULL, /* set_attr */\n\tpgsql_stmt_get_attr,\n\tpgsql_stmt_get_column_meta,\n\tNULL,  /* next_rowset */\n\tpdo_pgsql_stmt_cursor_closer\n};\n#endif"
  },
  {
    "path": "thirdparty/php85/pdo_pgsql/php_pdo_pgsql.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Author: Edin Kadribasic <edink@emini.dk>                             |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef PHP_PDO_PGSQL_H\n#define PHP_PDO_PGSQL_H\n\n#include <libpq-fe.h>\n\nextern zend_module_entry pdo_pgsql_module_entry;\n#define phpext_pdo_pgsql_ptr &pdo_pgsql_module_entry\n\n#include \"php_version.h\"\n#define PHP_PDO_PGSQL_VERSION PHP_VERSION\n\n#ifdef ZTS\n#include \"TSRM.h\"\n#endif\n\nPHP_MINIT_FUNCTION(pdo_pgsql);\nPHP_MSHUTDOWN_FUNCTION(pdo_pgsql);\nPHP_MINFO_FUNCTION(pdo_pgsql);\n\n#endif\t/* PHP_PDO_PGSQL_H */\n"
  },
  {
    "path": "thirdparty/php85/pdo_pgsql/php_pdo_pgsql_int.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Copyright (c) The PHP Group                                          |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 3.01 of the PHP license,      |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | https://www.php.net/license/3_01.txt                                 |\n  | If you did not receive a copy of the PHP license and are unable to   |\n  | obtain it through the world-wide-web, please send a note to          |\n  | license@php.net so we can mail you a copy immediately.               |\n  +----------------------------------------------------------------------+\n  | Authors: Edin Kadribasic <edink@emini.dk>                            |\n  |          Ilia Alshanestsky <ilia@prohost.org>                        |\n  |          Wez Furlong <wez@php.net>                                   |\n  +----------------------------------------------------------------------+\n*/\n\n/* internal header; not supposed to be installed */\n\n#ifndef PHP_PDO_PGSQL_INT_H\n#define PHP_PDO_PGSQL_INT_H\n\n#include <libpq-fe.h>\n#include <libpq/libpq-fs.h>\n#include <php.h>\n\n#define PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE \"08006\"\n\ntypedef struct {\n\tconst char *file;\n\tint line;\n\tunsigned int errcode;\n\tchar *errmsg;\n} pdo_pgsql_error_info;\n\ntypedef struct pdo_pgsql_stmt pdo_pgsql_stmt;\n\n/* stuff we use in a pgsql database handle */\ntypedef struct {\n\tPGconn\t\t*server;\n\tunsigned \tattached:1;\n\tunsigned \t_reserved:31;\n\tpdo_pgsql_error_info\teinfo;\n\tOid \t\tpgoid;\n\tunsigned int\tstmt_counter;\n\tbool\t\temulate_prepares;\n\tbool\t\tdisable_prepares;\n\tHashTable       *lob_streams;\n\tzend_fcall_info_cache *notice_callback;\n\tbool\t\tdefault_fetching_laziness;\n\tpdo_pgsql_stmt  *running_stmt;\n} pdo_pgsql_db_handle;\n\ntypedef struct {\n\tOid          pgsql_type;\n} pdo_pgsql_column;\n\nstruct pdo_pgsql_stmt {\n\tpdo_pgsql_db_handle     *H;\n\tPGresult                *result;\n\tpdo_pgsql_column        *cols;\n\tchar *cursor_name;\n\tchar *stmt_name;\n\tzend_string *query;\n\tchar **param_values;\n\tint *param_lengths;\n\tint *param_formats;\n\tOid *param_types;\n\tint                     current_row;\n\tbool is_prepared;\n\tbool is_unbuffered;\n\tbool is_running_unbuffered;\n};\n\ntypedef struct {\n\tOid     oid;\n} pdo_pgsql_bound_param;\n\nextern const pdo_driver_t pdo_pgsql_driver;\n\nextern int pdo_pgsql_scanner(pdo_scanner_t *s);\n\nextern int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line);\n#define pdo_pgsql_error(d,e,z)\t_pdo_pgsql_error(d, NULL, e, z, NULL, __FILE__, __LINE__)\n#define pdo_pgsql_error_msg(d,e,m)\t_pdo_pgsql_error(d, NULL, e, NULL, m, __FILE__, __LINE__)\n#define pdo_pgsql_error_stmt(s,e,z)\t_pdo_pgsql_error(s->dbh, s, e, z, NULL, __FILE__, __LINE__)\n#define pdo_pgsql_error_stmt_msg(stmt, e, sqlstate, msg) \\\n\t_pdo_pgsql_error(stmt->dbh, stmt, e, sqlstate, msg, __FILE__, __LINE__)\n\nextern const struct pdo_stmt_methods swoole_pgsql_stmt_methods;\n\n#define pdo_pgsql_sqlstate(r) PQresultErrorField(r, PG_DIAG_SQLSTATE)\n\nenum {\n\tPDO_PGSQL_ATTR_DISABLE_PREPARES = PDO_ATTR_DRIVER_SPECIFIC,\n\tPDO_PGSQL_ATTR_RESULT_MEMORY_SIZE,\n};\n\nstruct pdo_pgsql_lob_self {\n\tzval dbh;\n\tPGconn *conn;\n\tint lfd;\n\tOid oid;\n};\n\nenum pdo_pgsql_specific_constants {\n\tPGSQL_TRANSACTION_IDLE = PQTRANS_IDLE,\n\tPGSQL_TRANSACTION_ACTIVE = PQTRANS_ACTIVE,\n\tPGSQL_TRANSACTION_INTRANS = PQTRANS_INTRANS,\n\tPGSQL_TRANSACTION_INERROR = PQTRANS_INERROR,\n\tPGSQL_TRANSACTION_UNKNOWN = PQTRANS_UNKNOWN\n};\n\nphp_stream *pdo_pgsql_create_lob_stream(zend_object *pdh, int lfd, Oid oid);\nextern const php_stream_ops pdo_pgsql_lob_stream_ops;\n\nvoid pdo_pgsql_cleanup_notice_callback(pdo_pgsql_db_handle *H);\n\nvoid pdo_libpq_version(char *buf, size_t len);\nvoid pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh);\n\nvoid pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAMETERS);\nvoid pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAMETERS);\n\n#endif /* PHP_PDO_PGSQL_INT_H */\n"
  },
  {
    "path": "tools/analysis.php",
    "content": "<?php\n\ndefine('ROOT_PATH', dirname(__DIR__));\n\nrequire_once ROOT_PATH . '/tests/include/bootstrap.php';\n\n$core_header_files = _array(glob(ROOT_PATH . '/include/*.h'));\necho \"Number of core header files: \" . $core_header_files->count() . PHP_EOL;\n\n$ext_header_files = _array(glob(ROOT_PATH . '/ext-src/php_*.h'));\necho \"Number of php-ext header files: \" . $ext_header_files->count() . PHP_EOL;\n\n$core_source_files = _array(scan_dir_recursive(ROOT_PATH . '/src'))->filter(function ($value) {\n    return pathinfo($value, PATHINFO_EXTENSION) == 'cc';\n});\necho \"Number of core source files: \" . $core_source_files->count() . PHP_EOL;\n\n$core_source_files = _array(scan_dir(ROOT_PATH . '/ext-src'))->filter(function ($value) {\n    return pathinfo($value, PATHINFO_EXTENSION) == 'cc';\n});\necho \"Number of php-ext source files: \" . $core_source_files->count() . PHP_EOL;"
  },
  {
    "path": "tools/arginfo-check.php",
    "content": "#!/usr/bin/env php\n<?php\nrequire __DIR__ . '/bootstrap.php';\n\n// if no output, it means there is no mistake.\n$GLOBALS['error'] = 0;\n\n$root_dir = ROOT_DIR;\n$file_list_raw = explode(\"\\n\", `cd {$root_dir} && git ls-files`);\n$file_list_raw = array_filter($file_list_raw, function (string $filename) {\n    $ext = pathinfo($filename, PATHINFO_EXTENSION);\n    return $ext === 'h' || $ext === 'c' || $ext === 'cc';\n});\nswoole_ok(\"check \" . count($file_list_raw) . \" source files...\");\n$all_count = 0;\narray_walk($file_list_raw, function (string &$filename) use ($root_dir, &$all_count) {\n    $filename = $root_dir . '/' . $filename;\n    $content = file_get_contents($filename);\n    preg_match_all(\n        '/ZEND_BEGIN_ARG_INFO_EX\\(.+, (\\d+?)\\)\\n([\\s\\S]*?)ZEND_END_ARG_INFO\\(\\)/',\n        $content, $arg_info_matches, PREG_SET_ORDER\n    );\n    array_walk($arg_info_matches, function (array $arg_info) use ($filename) {\n        list($_, $arg_num, $arg_lines) = $arg_info;\n        $total_num = substr_count($arg_lines, \"ZEND_ARG_\");\n        if ((int)$arg_num > $total_num) {\n            $GLOBALS['error']++;\n            echo \"\\nin file {$filename}\\n\";\n            var_dump($_);\n        }\n    });\n    $count = substr_count($content, 'ZEND_PARSE_PARAMETERS_END');\n    $all_count += $count;\n    if ($count > 0) {\n        $match_count = preg_match_all(\n            '/\\s*ZEND_PARSE_PARAMETERS_START(?:[_A-Z]*)\\((?:[_A-Z]*, )?(?<min>[\\d-]+), ?(?<max>[\\d-]+)\\)(?<params>[\\s\\S]+?)\\s*ZEND_PARSE_PARAMETERS_END/',\n            $content, $params_info_matches, PREG_SET_ORDER\n        );\n        // check num\n        if (!assert(($count === $match_count) || preg_match('#/standard/exec\\.c|zend_API\\.h#', $filename) !== false)) {\n            echo \"\\nin file {$filename}\\n\";\n            var_dump($count, $match_count);\n        }\n        array_walk($params_info_matches, function (array $params_info) use ($filename) {\n            $declare_min = $params_info['min'];\n            $declare_max = $params_info['max'];\n            $params = array_filter(preg_split('/[\\s\\\\\\\\;]*\\n[\\s\\\\\\\\;]*/', $params_info['params']));\n            $real_min = $real_max = $find_opt = 0;\n            foreach ($params as $index => $param) {\n                if (!preg_match('/[A-Z]+/', $param) || preg_match('/Z_PARAM_VARIADIC/', $param)) {\n                    return;\n                }\n                if ($param === 'Z_PARAM_OPTIONAL') {\n                    $find_opt = 1;\n                } else {\n                    if (!$find_opt) {\n                        $real_min++;\n                    }\n                    $real_max++;\n                }\n            }\n            if ($declare_min != $real_min || ($declare_max != -1 && $declare_max != $real_max)) {\n                $GLOBALS['error']++;\n                echo \"\\nin file {$filename}\\n({$declare_min} != {$real_min}), ({$declare_max} != {$real_max})\\n\";\n                echo ltrim($params_info[0], \"\\n\") . \"\\n\";\n            }\n        });\n    }\n});\nswoole_ok(\"all ZEND_PARSE_PARAMETERS_END is {$all_count}\");\n\nif ($GLOBALS['error'] !== 0) {\n    swoole_error('Some mistakes on arg_info and parameters!');\n}\nswoole_success('All arg_info and parameters check ok!');\n"
  },
  {
    "path": "tools/bootstrap.php",
    "content": "<?php\n/**\n * This file is part of Swoole, for internal use only\n *\n * @link     https://www.swoole.com\n * @contact  team@swoole.com\n * @license  https://github.com/swoole/swoole-src/blob/master/LICENSE\n */\n\ndefine('ROOT_DIR', dirname(__DIR__));\nconst LIBRARY_DIR = ROOT_DIR . '/library';\nconst LIBRARY_SRC_DIR = LIBRARY_DIR . '/src';\n\nif (!defined('EMOJI_OK')) {\n    define('EMOJI_OK', '✅');\n}\nif (!defined('EMOJI_SUCCESS')) {\n    define('EMOJI_SUCCESS', '🚀');\n}\nif (!defined('EMOJI_ERROR')) {\n    define('EMOJI_ERROR', '❌');\n}\nif (!defined('EMOJI_WARN')) {\n    define('EMOJI_WARN', '⚠️');\n}\n\ndefine('SWOOLE_SOURCE_ROOT', dirname(__DIR__) . '/');\n\nif (!defined('SWOOLE_COLOR_RED')) {\n    define('SWOOLE_COLOR_RED', 1);\n    define('SWOOLE_COLOR_GREEN', 2);\n    define('SWOOLE_COLOR_YELLOW', 3);\n    define('SWOOLE_COLOR_BLUE', 4);\n    define('SWOOLE_COLOR_MAGENTA', 5);\n    define('SWOOLE_COLOR_CYAN', 6);\n    define('SWOOLE_COLOR_WHITE', 7);\n}\n\nfunction space(int $length): string\n{\n    return str_repeat(' ', $length);\n}\n\nfunction camelize(string $uncamelized_words, string $separator = '_'): string\n{\n    $uncamelized_words = $separator . str_replace($separator, ' ', strtolower($uncamelized_words));\n    return ltrim(str_replace(' ', '', ucwords($uncamelized_words)), $separator);\n}\n\nfunction unCamelize(string $camelCaps, string $separator = '_'): string\n{\n    $camelCaps = preg_replace('/([a-z])([A-Z])/', \"\\${1}{$separator}\\${2}\", $camelCaps);\n    /* for special case like: PDOPool => pdo_pool */\n    $camelCaps = preg_replace('/([A-Z]+)([A-Z][a-z]+)/', \"\\${1}{$separator}\\${2}\\${3}\", $camelCaps);\n    return strtolower($camelCaps);\n}\n\nfunction print_split_line(string $title = '', int $length = 32): void\n{\n    if ($length % 2 !== 0) {\n        $length += 1;\n    }\n    echo \"< {$title} > \" . str_repeat('=', $length) . PHP_EOL;\n}\n\nfunction swoole_log(string $content, int $color = 0): void\n{\n    echo ($color ? \"\\033[3{$color}m{$content}\\033[0m\" : $content) . PHP_EOL;\n}\n\nfunction swoole_check(bool $is_ok, string $output): void\n{\n    if ($is_ok) {\n        swoole_ok(\"{$output} OK!\");\n    } else {\n        swoole_error(\"{$output} Failed!\");\n    }\n}\n\nfunction swoole_warn(string ...$args): void\n{\n    foreach ($args as $arg) {\n        swoole_log(EMOJI_WARN . \" {$arg}\", SWOOLE_COLOR_YELLOW);\n    }\n}\n\nfunction swoole_error(string ...$args): void\n{\n    foreach ($args as $arg) {\n        swoole_log(EMOJI_ERROR . \" {$arg}\", SWOOLE_COLOR_RED);\n    }\n    exit(255);\n}\n\nfunction swoole_ok(string ...$args): void\n{\n    foreach ($args as $arg) {\n        swoole_log(EMOJI_OK . \" {$arg}\", SWOOLE_COLOR_GREEN);\n    }\n}\n\nfunction swoole_success(string $content): void\n{\n    swoole_log(\n        str_repeat(EMOJI_SUCCESS, 3) . $content . str_repeat(EMOJI_SUCCESS, 3),\n        SWOOLE_COLOR_CYAN\n    );\n    exit(0);\n}\n\nfunction swoole_execute_and_check(array $commands): void\n{\n    $basename = pathinfo($commands[1] ?? '', PATHINFO_FILENAME);\n    echo \"[{$basename}]\" . PHP_EOL;\n    echo \"===========  Execute  ==============\" . PHP_EOL;\n    $command = implode(' ', $commands);\n    exec($command, $output, $return_var);\n    if (str_starts_with($output[0] ?? '', '#!')) {\n        array_shift($output);\n    }\n    echo '> ' . implode(\"\\n> \", $output) . \"\" . PHP_EOL;\n    if ($return_var != 0) {\n        swoole_error(\"Exec {$command} failed with code {$return_var}!\");\n    }\n    echo \"=========== Finish Done ============\" . PHP_EOL . PHP_EOL;\n}\n\nfunction scan_dir(string $dir, ?callable $filter = null): array\n{\n    $files = array_filter(scandir($dir), function (string $file) {\n        return $file[0] !== '.';\n    });\n    array_walk($files, function (&$file) use ($dir) {\n        $file = \"{$dir}/{$file}\";\n    });\n    return array_values($filter ? array_filter($files, $filter) : $files);\n}\n\nfunction scan_dir_recursive(string $dir, ?callable $filter = null): array\n{\n    $result = [];\n    $files = scan_dir($dir, $filter);\n    foreach ($files as $f) {\n        if (is_dir($f)) {\n            $result = array_merge($result, scan_dir_recursive($f, $filter));\n        } else {\n            $result[] = $f;\n        }\n    }\n    return $result;\n}\n\nfunction file_size(string $filename, int $decimals = 2): string\n{\n    $bytes = filesize($filename);\n    $sz = 'BKMGTP';\n    $factor = (int)floor((strlen($bytes) - 1) / 3);\n    return sprintf(\"%.{$decimals}f\", $bytes / pow(1024, $factor)) . $sz[$factor];\n}\n\nfunction swoole_git_files(): array\n{\n    $root = SWOOLE_SOURCE_ROOT;\n    return explode(PHP_EOL, shell_exec(\"cd {$root} && git ls-files\"));\n}\n\nfunction swoole_source_list(array $ext_list = [], array $excepts = []): array\n{\n    $source_list = swoole_git_files();\n    $source_list = array_filter($source_list, function (string $filename) use ($ext_list, $excepts) {\n        $ext_list = $ext_list + [\n                'h' => true,\n                'c' => true,\n                'cc' => true\n            ];\n        $excepts = $excepts + [\n                'core-tests',\n                'examples',\n                'tools',\n                'thirdparty',\n            ];\n        foreach ($excepts as $except) {\n            if (preg_match(\"/{$except}/\", $filename)) {\n                return false;\n            }\n        }\n        $ext = pathinfo($filename, PATHINFO_EXTENSION);\n        return $ext_list[$ext] ?? false;\n    });\n    sort($source_list);\n\n    return $source_list;\n}\n"
  },
  {
    "path": "tools/build-library.php",
    "content": "#!/usr/bin/env php\n<?php\nif (isset($argv[1]) and $argv[1] == 'dev') {\n    putenv('SWOOLE_LIBRARY_DEV=1');\n}\n$argv[1] = realpath(__DIR__ . '/../library/src');\nputenv('SWOOLE_DIR=' . realpath(__DIR__ . '/..'));\nrequire __DIR__ . '/vendor/bin/make-library.php';\n"
  },
  {
    "path": "tools/code-generator.php",
    "content": "#!/usr/bin/env php\n<?php\nrequire __DIR__ . '/bootstrap.php';\n\n$swoole_c = ROOT_DIR . '/ext-src/php_swoole.cc';\n$swoole_c_content = file_get_contents($swoole_c);\n\n$error_h = ROOT_DIR . '/include/swoole_error.h';\n$error_h_content = file_get_contents($error_h);\n\n$log_h = ROOT_DIR . '/include/swoole_log.h';\n$log_h_content = file_get_contents($log_h);\n\n//---------------------------------------------------------------------------\n//                     generate ERROR constants\n//---------------------------------------------------------------------------\nif (!preg_match_all('/SW_ERROR_[0-9A-Z_]+/', $error_h_content, $matches_error,\n        PREG_PATTERN_ORDER) || empty($matches_error[0])) {\n    swoole_error('Match ERROR enums error!');\n}\n$matches_error[0] = array_unique($matches_error[0]);\n// trim start and end\narray_shift($matches_error[0]);\narray_pop($matches_error[0]);\n\n// generate ERROR constants\n$define_output = '';\nforeach ($matches_error[0] as $match) {\n    // convert error code to define\n    $constant_name = str_replace('SW_', 'SWOOLE_', $match);\n    $constant_value = $match;\n    $define_output .= space(4) . \"SW_REGISTER_LONG_CONSTANT(\\\"{$constant_name}\\\", {$constant_value});\\n\";\n}\n$swoole_c_content = preg_replace(\n    '/ *?(?:SW_REGISTER_LONG_CONSTANT\\(\"SWOOLE_ERROR_[0-9A-Z_]+?\", SW_ERROR_[0-9A-Z_]+?\\);\\n *)+/',\n    $define_output, $swoole_c_content, 1, $is_ok\n);\nswoole_check($is_ok, 'Generate ERROR constants');\n\n//---------------------------------------------------------------------------\n//                     generate TRACE constants\n//---------------------------------------------------------------------------\nif (!preg_match_all('/SW_TRACE_[0-9A-Z_]+/', $log_h_content, $matches_trace,\n        PREG_PATTERN_ORDER) || empty($matches_trace[0])) {\n    swoole_error('Match TRACE enums error!');\n}\n$matches_trace[0] = array_unique($matches_trace[0]);\n$define_output = '';\nforeach ($matches_trace[0] as $match) {\n    // convert error code to define\n    $constant_name = str_replace('SW_', 'SWOOLE_', $match);\n    $constant_value = $match;\n    $define_output .= space(4) . \"SW_REGISTER_LONG_CONSTANT(\\\"{$constant_name}\\\", {$constant_value});\\n\";\n}\n$swoole_c_content = preg_replace(\n    '/ *?(?:SW_REGISTER_LONG_CONSTANT\\(\"SWOOLE_TRACE_[0-9A-Z_]+?\", SW_TRACE_[0-9A-Z_]+?\\);\\n *)+/',\n    $define_output, $swoole_c_content, 1, $is_ok\n);\nswoole_check($is_ok, 'Generate TRACE constants');\n\nfile_put_contents($swoole_c, $swoole_c_content);\n\n// generate ERROR strings\n$swoole_error_cc = ROOT_DIR . '/src/core/error.cc';\n$swoole_error_cc_content = file_get_contents($swoole_error_cc);\n$swstrerror_output = space(4) . \"switch (code) {\\n\";\nforeach ($matches_error[0] as $match) {\n    // convert error code to swstrerror\n    $sw_error_str = implode(' ', explode('_', strtolower(str_replace('SW_ERROR_', '', $match))));\n    $replaces = [\n        'co ' => 'Coroutine ',\n        'php ' => 'PHP ',\n        'ssl ' => 'SSL ',\n        'dnslookup ' => 'DNS Lookup '\n    ];\n    $sw_error_str = str_replace(array_keys($replaces), array_values($replaces), $sw_error_str);\n    $sw_error_str[0]= strtoupper($sw_error_str[0]);\n    $swstrerror_output .= space(4) . \"case {$match}:\\n\" . space(8) . \"return \\\"{$sw_error_str}\\\";\\n\";\n}\n$swstrerror_output .= <<<CPP\n    default:\n        static char buffer[32];\n#ifndef __MACH__\n        snprintf(buffer, sizeof(buffer), \"Unknown error %d\", code);\n#else\n        snprintf(buffer, sizeof(buffer), \"Unknown error: %d\", code);\n#endif\n        return buffer;\n    }\nCPP;\n$swstrerror_output .= \"\\n\";\n$swoole_error_cc_content = preg_replace(\n    '/(\\* swstrerror \\{\\{\\{\\*\\/\\n)([\\s\\S]+?)(\\/\\*\\}\\}\\}\\*\\/)/',\n    '${1}' . $swstrerror_output . space(4).'${3}',\n    $swoole_error_cc_content, 1, $is_ok\n);\nswoole_check($is_ok, 'Generate ERROR stringify');\nfile_put_contents($swoole_error_cc, $swoole_error_cc_content);\n\nswoole_success('Generate all source codes OK!');\n"
  },
  {
    "path": "tools/composer.json",
    "content": "{\n    \"require\": {\n        \"swoole/make-library\": \"v1.0.0\"\n    }\n}\n"
  },
  {
    "path": "tools/constant-generator.php",
    "content": "#!/usr/bin/env php\n<?php\nrequire __DIR__ . '/bootstrap.php';\n\n$constant_php = LIBRARY_SRC_DIR . '/core/Constant.php';\nif (!file_exists($constant_php)) {\n    swoole_error(\"Unable to find source file [{$constant_php}]\");\n}\n\n$root_dir = ROOT_DIR;\n$file_list = explode(\"\\n\", `cd {$root_dir} && git ls-files`);\n$file_list = array_filter($file_list, function (string $filename) {\n    $ext = pathinfo($filename, PATHINFO_EXTENSION);\n    return $ext === 'h' || $ext === 'c' || $ext === 'cc';\n});\n\n$source_content = '';\nforeach ($file_list as $file) {\n    $source_content .= file_get_contents(\"{$root_dir}/{$file}\");\n}\n\npreg_match_all('/php_swoole_array_get_value\\(.+?, \"(.+?)\", .+?\\)/', $source_content, $matches);\n$matches = array_unique($matches[1]);\n\n$coroutineOptions = [\n    'exit_condition',\n    'deadlock_check_disable_trace',\n    'deadlock_check_limit',\n    'deadlock_check_depth'\n];\n$helperOptions = [\n    'stats_file',\n    'stats_timer_interval',\n    'admin_server',\n];\n$options = array_merge($matches, $coroutineOptions, $helperOptions);\n$result = '';\nforeach ($options as $option) {\n    $result .= space(4) . sprintf(\"public const OPTION_%s = '%s';\\n\\n\", strtoupper($option), $option);\n}\n\n$event_file = [\n    \"{$root_dir}/ext-src/swoole_server.cc\",\n    \"{$root_dir}/ext-src/swoole_server_port.cc\"\n];\n\n$server_event_content = '';\nforeach ($event_file as $file) {\n    $server_event_content .= file_get_contents($file);\n}\npreg_match_all('/vent\\(SW_SERVER_CB_on(.+?),/', $server_event_content, $server_event);\n$server_events = array_unique($server_event[1]);\n\n$event_result = '';\nforeach ($server_events as $event) {\n    if ($event === 'HandShake') {\n        $event = 'handshake';\n    }\n    $event_result .= space(4) . sprintf(\"public const EVENT_%s = '%s';\\n\\n\", strtoupper(unCamelize($event)), lcfirst($event));\n}\n\n$constant_php_content = file_get_contents($constant_php);\n\n$event_pattern = '/(\\/\\* \\{\\{\\{ EVENT \\*\\/\\n)([\\s\\S]*)(\\/\\* \\}\\}\\} EVENT \\*\\/)/';\n$option_pattern = '/(\\/\\* \\{\\{\\{ OPTION \\*\\/\\n)([\\s\\S]*)(\\/\\* \\}\\}\\} OPTION \\*\\/)/';\n\nfunction replaceConstantContent($pattern, $result, &$content) {\n    $content = preg_replace(\n        $pattern,\n        '${1}' . $result . space(4) . '${3}',\n        $content,\n        1,\n        $replaced\n    );\n\n    return $replaced;\n}\n\n$event_replaced = replaceConstantContent($event_pattern, $event_result, $constant_php_content);\n$option_replaced = replaceConstantContent($option_pattern, $result, $constant_php_content);\n\nif (!$event_replaced || !$option_replaced || !file_put_contents($constant_php, $constant_php_content)) {\n    swoole_error('Update constant failed');\n}\n\nswoole_success('Constant generator successfully done!');\n"
  },
  {
    "path": "tools/export.php",
    "content": "#!/usr/bin/env php\n<?php\nif ($argc == 1) {\n    exit(\"Usage: php export.php [class_name]\\n\");\n}\nReflectionClass::export($argv[1]);\n"
  },
  {
    "path": "tools/gen-data.php",
    "content": "#!/usr/bin/env php\n<?php\nif (!empty($argv[1])) {\n    $file = $argv[1];\n} else {\n    $file = __DIR__ . \"/test.txt\";\n}\n\n$op =\n\n$fp = fopen($file, \"w\");\nftruncate($fp, 0);\nfwrite($fp, str_repeat('A', 1024));\nfwrite($fp, str_repeat('B', 1024));\nfwrite($fp, str_repeat('C', 256) . \"\\n\");\nfclose($fp);"
  },
  {
    "path": "tools/gen-ext-class.php",
    "content": "<?php\n\nrequire __DIR__ . '/bootstrap.php';\nif (empty($argv[1])) {\n    exit(\"Usage: php {$argv[0]} module_name\\n\");\n}\n\n$module_name = trim($argv[1]);\n\n$gen_class_name = function ($module_name) {\n    $list = explode('_', $module_name);\n    $class = '';\n    foreach ($list as $li) {\n        $class .= ucfirst($li);\n    }\n    return $class;\n};\n\n$replacement = [\n    'file_name' => $module_name,\n    'module_name' => $module_name,\n    'var_name' => $module_name,\n    'class_name' => $gen_class_name($module_name),\n];\n\n$replacement['type_name'] = $replacement['class_name'].'Object';\n$replacement['php_var_name'] = \"{$replacement['var_name']}_object\";\n$replacement['class_name'] = addcslashes($replacement['class_name'], '\\\\');\nforeach ($replacement as $name => $value) {\n    $replacement[strtoupper($name)] = strtoupper($value);\n}\n\n$result = '';\n$srcTemplateFile = __DIR__ . '/templates/class.c';\n$content = file_get_contents($srcTemplateFile);\nforeach ($replacement as $name => $value) {\n    $content = str_replace(\"{{{$name}}}\", $value, $content);\n}\n\nfile_put_contents(ROOT_DIR.'/ext-src/swoole_'.$module_name.'.cc', $content);\n\n//, __DIR__ . '/templates/class.h'];\n\n"
  },
  {
    "path": "tools/get-ip-info.php",
    "content": "#!/usr/bin/env php\n<?php\nif (empty($argv[1])) {\n    exit(\"Usage: php get-ip-info.php sin_addr\\n\");\n}\n$v = $argv[1];\n$n = unpack('Nip', $v);\n$ip = long2ip($n['ip']);\n$results = `curl http://freeapi.ipip.net/$ip`;\necho \"IP: $ip, LOCATION: $results\\n\";\n"
  },
  {
    "path": "tools/next-version.php",
    "content": "#!/usr/bin/env php\n<?php\n\nclass Version\n{\n    public $major;\n    public $minor;\n    public $release;\n    public $extra;\n    public $api;\n\n    const REGX_MAJOR = '#define\\s+SWOOLE_MAJOR_VERSION\\s+(\\d+)#';\n    const REGX_MINOR = '#define\\s+SWOOLE_MINOR_VERSION\\s+(\\d+)#';\n    const REGX_RELEASE = '#define\\s+SWOOLE_RELEASE_VERSION\\s+(\\d+)#';\n    const REGX_EXTRA = '#define\\s+SWOOLE_EXTRA_VERSION\\s+\"(\\w*)\"#';\n    const REGX_API = '#define\\s+SWOOLE_API_VERSION_ID\\s+(0x[0-9a-f]*)#';\n\n    function getVersion()\n    {\n        $version = implode('.', [$this->major, $this->minor, $this->release]);\n        if ($this->extra) {\n            $version .= '-' . $this->extra;\n        }\n        return $version;\n    }\n\n    function getVersionId()\n    {\n        return intval(sprintf('%d%02d%02d', $this->major, $this->minor, $this->release));\n    }\n\n    function match($versionInfo) {\n        preg_match(Version::REGX_MAJOR, $versionInfo, $match_major) or die('no match MAJOR_VERSION');\n        preg_match(Version::REGX_MINOR, $versionInfo, $match_minor) or die('no match MAJOR_MINOR');;\n        preg_match(Version::REGX_RELEASE, $versionInfo,\n            $match_release) or die('no match RELEASE_VERSION');;\n        preg_match(Version::REGX_EXTRA, $versionInfo, $match_extra) or die('no match EXTRA_VERSION');\n        preg_match(Version::REGX_API, $versionInfo, $match_api) or die('no match API_VERSION_ID');\n\n        $this->major = intval($match_major[1]);\n        $this->minor = intval($match_minor[1]);\n        $this->release = intval($match_release[1]);\n        $this->extra = trim($match_extra[1]);\n        $this->api = trim($match_api[1]);\n    }\n}\n\n$type = empty($argv[1]) ? 'release' : trim($argv[1]);\n$kernel_version_file = dirname(__DIR__) . '/include/swoole_version.h';\n$cmake_file = dirname(__DIR__) . '/CMakeLists.txt';\n$package_file = dirname(__DIR__) . '/package.xml';\n\n$current = new Version;\n$current->match(file_get_contents($kernel_version_file));\n\n$next = clone $current;\n\nif ($type == 'release') {\n    if ($current->extra == '') {\n        $next->release++;\n        $next->extra = 'dev';\n    } else {\n        $next->extra = '';\n    }\n} elseif ($type == 'minor') {\n    $next->minor++;\n    $next->release = 0;\n    $next->extra = 'dev';\n} elseif ($type == 'major') {\n    $next->major++;\n    $next->minor = 0;\n    $next->release = 0;\n    $next->extra = 'dev';\n} elseif ($type == 'api') {\n    $date = substr($current->api, 2, strlen($current->api) - 3);\n    if ($date == date('Ym')) {\n        $c = substr($current->api, -1, 1);\n        if ($c == 'f') {\n            throw new RuntimeException(\"maximum exceeded[$current->api]\");\n        }\n        $next->api = '0x' . $date . chr(ord($c) + 1);\n    } else {\n        $next->api = '0x' . date('Ym') . 'a';\n    }\n} else {\n    exit(\"wrong version type\");\n}\n\nif (empty($next->extra)) {\n    $doc = file_get_contents($package_file);\n    file_put_contents($package_file, str_replace($current->getVersion(), $next->getVersion(), $doc));\n}\n\nob_start();\ninclude __DIR__ . '/templates/version.tpl.h';\nfile_put_contents($kernel_version_file, ob_get_clean());\n\nfile_put_contents($cmake_file,\n    preg_replace('#set\\(SWOOLE_VERSION\\s+[0-9\\.\\-a-z]+\\)#i', 'set(SWOOLE_VERSION ' . $next->getVersion() . ')',\n        file_get_contents($cmake_file)));\n"
  },
  {
    "path": "tools/option-generator.php",
    "content": "#!/usr/bin/env php\n<?php\nrequire __DIR__ . '/bootstrap.php';\n\nconst REGX = '\\[([^\\]]*)\\]';\n\n$server_helper_php = LIBRARY_SRC_DIR . '/core/Server/Helper.php';\nif (!file_exists($server_helper_php)) {\n    swoole_error(\"Unable to find source file [{$server_helper_php}]\");\n}\n$php_content = file_get_contents($server_helper_php);\n\nfunction _replace_options($file, &$php_content, $name) {\n    $source_content = file_get_contents($file);\n    preg_match_all('/php_swoole_array_get_value\\(.+?, \"(.+?)\", .+?\\)/', $source_content, $matches);\n    $matches = array_unique($matches[1]);\n    $result = '';\n    foreach ($matches as $option) {\n        $result .= space(8) . sprintf(\"'%s' => true,\\n\", strtolower($option), $option);\n    }\n\n    $php_content = preg_replace(\n        '/const '.$name.' = '.REGX.';/',\n        'const '.$name.' = ['.PHP_EOL.$result.space(4).'];',\n        $php_content,\n        1,\n        $replaced\n    );\n    if (!$replaced) {\n        swoole_error(\"error content [$name]\");\n    }\n}\n\n_replace_options(ROOT_DIR.'/ext-src/php_swoole.cc', $php_content, 'GLOBAL_OPTIONS');\n_replace_options(ROOT_DIR.'/ext-src/swoole_server.cc', $php_content, 'SERVER_OPTIONS');\n_replace_options(ROOT_DIR.'/ext-src/swoole_server_port.cc', $php_content, 'PORT_OPTIONS');\n_replace_options(ROOT_DIR.'/ext-src/swoole_async_coro.cc', $php_content, 'AIO_OPTIONS');\n_replace_options(ROOT_DIR.'/ext-src/swoole_coroutine_scheduler.cc', $php_content, 'COROUTINE_OPTIONS');\n\n// save\nif (!file_put_contents($server_helper_php, $php_content)) {\n    swoole_error('Update Server\\\\Helper failed');\n}\n\nswoole_success('Update Server\\\\Helper successfully done!');\n\n\n\n\n"
  },
  {
    "path": "tools/pecl-package.php",
    "content": "#!/usr/bin/env php\n<?php\n# @remicollet\n# https://github.com/swoole/swoole-src/commit/ffff7ce074accf7b47768fca6eb238627d7a6b93#r30410846\n# role=\"src\" => not installed, so files only used for the build\n# role=\"doc\" => in $(pecl config-get doc_dir), which is /usr/share/doc/pecl/swoole on RPM distro (LICENSE being an exception, manually moved to /usr/share/licenses)\n# role=\"test\" => in $(pecl config-get test_dir), which is /usr/share/tests/pecl/swoole on RPM distro\n\nrequire __DIR__ . '/bootstrap.php';\n\nfunction check_source_ver(string $expect_ver, $source_file)\n{\n    static $source_ver_regex = '/(SWOOLE_VERSION +)(\"?)(?<ver>[\\w\\-.]+)(\"?)/';\n    $replaced = false;\n    _check:\n    $source_content = file_get_contents($source_file);\n    if (!preg_match($source_ver_regex, $source_content, $matches)) {\n        swoole_log(\n            \"Warning: Match SWOOLE_VERSION Failed, skip check!\\n\",\n            SWOOLE_COLOR_MAGENTA\n        );\n        return;\n    }\n\n    $source_ver = $matches['ver'];\n\n    // auto fixed sub version values\n    if (strpos($source_content, 'SWOOLE_MAJOR_VERSION') !== false) {\n        $version_parts = array_values(array_filter(preg_split('/(?:\\b)|(?:(?<=[0-9])(?=[a-zA-Z]))/',\n            $source_ver), function (string $char) {\n            return preg_match('/[0-9a-zA-Z]/', $char);\n        }));\n        list($major, $minor, $release, $extra) = $version_parts;\n        $source_content = preg_replace(\n            '/^(\\#define[ ]+SWOOLE_VERSION_ID[ ]+)\\d+$/m',\n            '${1}' . sprintf('%d%02d%02d', $major, $minor, $release),\n            $source_content\n        );\n        (function (&$source_content, $replacements) {\n            foreach ($replacements as $replacement) {\n                $regex = '/^(\\#define[ ]+SWOOLE_' . $replacement[0] . '_VERSION[ ]+' . (is_numeric($replacement[1]) ? ')\\d+()$' : '\")[^\"]*(\"$)') . '/m';\n                $source_content = preg_replace(\n                    $regex, '${1}' . $replacement[1] . '${2}',\n                    $source_content,\n                    1\n                );\n            }\n        })($source_content, [['MAJOR', $major], ['MINOR', $minor], ['RELEASE', $release], ['EXTRA', $extra]]);\n        file_put_contents($source_file, $source_content);\n    }\n\n    if (!preg_match('/^\\d+?\\.\\d+?\\.\\d+?$/', $source_ver)) {\n        $is_release_ver = false;\n        swoole_warn(\"SWOOLE_VERSION v{$source_ver} is not a release version number in {$source_file}\");\n    } else {\n        $is_release_ver = true;\n    }\n    $compare = version_compare($source_ver, $expect_ver);\n    switch ($compare) {\n        case -1: // <\n            {\n                if ($replaced) {\n                    _replaced_error:\n                    swoole_error(\"Fix version number failed in {$source_file}\");\n                }\n                swoole_warn(\"SWOOLE_VERSION v{$source_ver} will be replaced to v{$expect_ver} in {$source_file}\");\n                $source_content = preg_replace(\n                    $source_ver_regex, '$1${2}' . $expect_ver . '$4',\n                    $source_content, 1, $replaced\n                );\n                if (!$replaced) {\n                    goto _replaced_error;\n                }\n                file_put_contents($source_file, $source_content);\n                $replaced = true;\n                goto _check;\n            }\n            break;\n        case 1: // >\n            {\n                if ($is_release_ver) {\n                    swoole_error(\"Wrong SWOOLE_VERSION {$source_ver} in {$source_file}, please check your package.xml\");\n                }\n            }\n            break;\n    }\n}\n\n// all check\nswoole_execute_and_check(['php', __DIR__ . '/arginfo-check.php']);\nswoole_execute_and_check(['php', __DIR__ . '/code-generator.php']);\nif (file_exists(LIBRARY_DIR)) {\n    swoole_execute_and_check(['php', __DIR__ . '/constant-generator.php']);\n    swoole_execute_and_check(['php', __DIR__ . '/build-library.php']);\n} else {\n    swoole_warn('Unable to find source of library, this step will be skipped');\n}\nswoole_execute_and_check(['php', __DIR__ . '/phpt-fixer.php']);\n\n// prepare\nswoole_ok('Start to package...');\n$this_dir = __DIR__;\n$tests_dir = ROOT_DIR . '/tests/';\n`cd {$tests_dir} && ./clean && cd {$this_dir}`;\n\n$root_dir = SWOOLE_SOURCE_ROOT;\n\n// check version definitions\n$package_ver_regex = '/<version>\\s+<release>(?<release_v>\\d+?\\.\\d+?\\.\\d+?(?:-?(?:alpine|beta|rc\\d*?))?)<\\/release>\\s+<api>(?<api_v>\\d+?\\.\\d+?)<\\/api>\\s+<\\/version>\\s+<stability>\\s+<release>(?<release_s>[a-z]+?)<\\/release>\\s+<api>(?<api_s>[a-z]+?)<\\/api>\\s+<\\/stability>/i';\npreg_match($package_ver_regex, file_get_contents(__DIR__ . '/../package.xml'), $matches);\n$package_release_ver = $matches['release_v'];\n$package_api_ver = $matches['api_v'];\n$package_release_stable = $matches['release_s'];\n$package_api_stable = $matches['api_s'];\n$major_version = explode(\".\", $package_release_ver)[0];\nif ((int) $major_version != $package_api_ver) {\n    swoole_error(\"Wrong api version [{$package_api_ver}] with release version [{$package_release_ver}]\");\n}\nif ($package_release_stable . $package_api_stable !== 'stable' . 'stable') {\n    if (preg_match('/RC\\d+$/', $package_release_ver)) {\n        swoole_warn(\"It's not a stable version (RC)\");\n    } else {\n        swoole_warn(\"It's not a stable version, can't be released by pecl\");\n    }\n}\necho \"[Version] => {$package_release_ver}\\n\";\necho \"[API-Ver] => {$package_api_ver}\\n\";\necho \"[RStable] => {$package_release_stable}\\n\";\necho \"[AStable] => {$package_api_stable}\\n\";\ncheck_source_ver($package_release_ver, dirname(__DIR__) . '/include/swoole_version.h');\ncheck_source_ver($package_release_ver, dirname(__DIR__) . '/CMakeLists.txt');\n\n// check file lists\n$file_list_raw = swoole_git_files();\n$file_list = [];\nforeach ($file_list_raw as $file) {\n    if (empty($file)) {\n        continue;\n    }\n    if (is_dir(\"{$root_dir}/{$file}\")) {\n        continue;\n    }\n    if ($file === 'package.xml' || substr($file, 0, 1) === '.') {\n        continue;\n    }\n    if (strpos($file, 'tests') === 0) {\n        $role = 'test';\n    } elseif (strpos($file, 'examples') === 0) {\n        $role = 'doc';\n    } else {\n        $ext = pathinfo($file, PATHINFO_EXTENSION);\n        $role = 'src';\n        switch ($ext) {\n            case 'phpt':\n                $role = 'test';\n                break;\n            case 'md':\n                $role = 'doc';\n                break;\n            case '':\n                static $spacial_source_list = [\n                    'Makefile' => true\n                ];\n                if ($spacial_source_list[pathinfo($file, PATHINFO_BASENAME)] ?? false) {\n                    break;\n                }\n                if (substr(file_get_contents(\"{$root_dir}/{$file}\"), 0, 2) !== '#!') {\n                    $role = 'doc';\n                }\n                break;\n        }\n    }\n    $file_list[] = \"<file role=\\\"{$role}\\\" name=\\\"{$file}\\\" />\\n\";\n}\n\n// get content from package.xml and find out the file list\n$content = file_get_contents(__DIR__ . '/../package.xml');\nif (!preg_match('/([ ]*)\\<dir[ ]name=\\\"\\/\\\">/', $content, $matches)) {\n    swoole_error('Match dir tag failed!');\n}\n\n// update file list\n$space = strlen($matches[1]);\n$space += 4;\n$space = str_repeat(' ', $space);\n$dir_tag = '<dir name=\"/\">' . \"\\n\";\n$content = preg_replace('/(\\<dir[ ]name=\\\"\\/\\\">)([\\s\\S]*?)(\\n[ ]*?\\<\\/dir>)/', '$1$3', $content, 1, $success);\nif (!$success) {\n    swoole_error('Replace old content failed!');\n}\n$content = str_replace($dir_tag, $dir_tag . $space . implode(\"{$space}\", $file_list), $content, $success);\nif (!$success) {\n    swoole_error('Replace new content failed!');\n}\n\n// update date\ndate_default_timezone_set('Asia/Shanghai');\n$date_tag = date('Y-m-d');\n$content = preg_replace('/(<date\\>)\\d+?-\\d+?-\\d+?(<\\/date>)/', '${1}' . $date_tag . '${2}', $content, $success);\nif (!$success) {\n    swoole_error('Replace date tag failed!');\n}\n$time_tag = date('H', time() + 3600) . ':00:00';\n$content = preg_replace('/(<time\\>)\\d+?:\\d+?:\\d+?(<\\/time>)/', '${1}' . $time_tag . '${2}', $content, $success);\nif (!$success) {\n    swoole_error('Replace time tag failed!');\n}\nif (!file_put_contents(__DIR__ . '/../package.xml', $content)) {\n    swoole_error('Output package.xml failed!');\n}\n\n// pack\n$package = trim(`cd {$root_dir} && pecl package`);\nif (preg_match('/Warning/i', $package)) {\n    $warn = explode(\"\\n\", $package);\n    $package = array_pop($warn);\n    $warn = implode(\"\\n\", $warn);\n    swoole_log(\"{$warn}\\n\", SWOOLE_COLOR_MAGENTA);\n}\n\n// check package status\nif (!preg_match('/Package (?<filename>swoole-.+?.tgz) done/', $package, $matches)) {\n    swoole_error($package);\n} else {\n    $file_name = $matches['filename'];\n    $file_path = \"{$root_dir}/{$file_name}\";\n    $file_size = file_size($file_path);\n    $file_hash = substr(md5_file($file_path), 0, 8);\n    swoole_success(\"Package {$file_name} ({$file_size}) ({$file_hash}) done\");\n}\n"
  },
  {
    "path": "tools/phpt-fixer.php",
    "content": "#!/usr/bin/env php\n<?php\nrequire __DIR__ . '/bootstrap.php';\n\nfunction fix_tests_in_this_dir(string $dir, string $root = '')\n{\n    $files = scan_dir($dir);\n    if (empty($root)) {\n        $title_dir_name = explode('/', $dir);\n        $title_dir_name = end($title_dir_name);\n    } else {\n        $title_dir_name = trim(str_replace(realpath($root), '', realpath($dir)), '/');\n    }\n    foreach ($files as $file) {\n        if (pathinfo($file, PATHINFO_EXTENSION) === 'phpt') {\n            $requirement_level = (function () use ($root, $file) {\n                for ($l = 0; $l < 8; $l++) {\n                    $file = dirname($file);\n                    if ($file === $root) {\n                        return $l;\n                    }\n                }\n                return -1;\n            })();\n            if ($requirement_level < 0) {\n                swoole_error(\"Failed to get requirement level of file {$file}\");\n            }\n            $content = file_get_contents($file);\n            $changed = false;\n            // empty lines\n            $_content = trim($content) . \"\\n\";\n            if ($content !== $_content) {\n                swoole_ok(\"Format empty lines in {$file}\");\n                $content = $_content;\n                $changed = true;\n            }\n            // white lines\n            $content = preg_replace('/\\n{2,}--/', \"\\n--\", $content, -1, $count);\n            if ($count) {\n                swoole_ok(\"Removed white lines in {$file}\");\n                $changed = true;\n            }\n            // no file\n            if (strpos($content, '--FILE--') === false) {\n                swoole_warn(\"Can not find --FILE-- in {$file}\");\n            }\n            // no bootstrap\n            if (strpos($content, 'include/bootstrap.php') === false) {\n                swoole_warn(\"Can not find bootstrap in {$file}\");\n            }\n            // unused skipif\n            if (strpos($content, 'include/skipif.inc') === false) {\n                $skip_requirement_outer = str_repeat('/..', $requirement_level);\n                $skip_requirement = \"<?php require __DIR__ . '{$skip_requirement_outer}/include/skipif.inc'; ?>\\n\";\n                if (strpos($content, '--SKIPIF--') !== false) {\n                    $content = preg_replace(\"/--SKIPIF--\\n/\", \"\\${0}{$skip_requirement}\", $content, 1, $count);\n                } else {\n                    $content = preg_replace(\"/--FILE--/\", \"--SKIPIF--\\n{$skip_requirement}\\${0}\", $content, 1, $count);\n                }\n                if (!$count) {\n                    swoole_error(\"Add skipif.inc failed in {$file}\");\n                } else {\n                    swoole_ok(\"Add skipif.inc to the script in file {$file}\");\n                }\n                $changed = true;\n            }\n            // title\n            preg_match('/--TEST--\\n([^\\n]+)/', $content, $matches);\n            $current_title = $matches[1] ?? '';\n            if (!$current_title) {\n                swoole_warn(\"Can not find title in {$file}\");\n                goto _finished;\n            }\n            $current_title_array = explode(':', $current_title);\n            $current_title_dir_name = $current_title_array[0];\n            if (count($current_title_array) < 2) {\n                $content = preg_replace(\n                    '/--TEST--\\n/',\n                    '$0' . $title_dir_name . ': ',\n                    $content,\n                    1, $count\n                );\n                _check_title:\n                if (!$count) {\n                    swoole_error(\"Replace title failed in {$file}\");\n                } else {\n                    swoole_ok(\"Fix title dir name from [{$current_title_dir_name}] to [{$title_dir_name}] in {$file}\");\n                }\n                $changed = true;\n            } elseif ($current_title_dir_name !== $title_dir_name) {\n                $content = preg_replace(\n                    '/(--TEST--\\n)(?:.+)(:)/',\n                    '$1' . $title_dir_name . '$2',\n                    $content,\n                    1, $count\n                );\n                goto _check_title;\n            }\n            _finished:\n            if ($changed) {\n                file_put_contents($file, $content);\n            }\n        } elseif (is_dir($file)) {\n            fix_tests_in_this_dir($file, $root);\n        }\n    }\n}\n\n$root = realpath(ROOT_DIR . '/tests');\n$dirs = scan_dir($root, function (string $file) {\n    return strpos(pathinfo($file, PATHINFO_FILENAME), 'swoole_') === 0;\n});\nforeach ($dirs as $dir) {\n    fix_tests_in_this_dir($dir, $root);\n}\nswoole_success('PHPT-Fixer done');\n"
  },
  {
    "path": "tools/rename.php",
    "content": "<?php\n\n$dirs = glob('./src/*');\n\nforeach ($dirs as $d) {\n    $files = glob($d.'/*.c');\n    foreach ($files as $f) {\n        `git mv $f {$f}c`;\n    }\n}"
  },
  {
    "path": "tools/send-http-data.php",
    "content": "#!/usr/bin/env php\n<?php\nif ($argc < 3)  {\n    exit(\"Usage: php send-http-data.php {port} {file} {:save}\\n\");\n}\n\n$port = intval($argv[1]);\n$file = $argv[2];\n\n\n$sock = stream_socket_client(\"tcp://127.0.0.1:{$port}\");\nfwrite($sock, file_get_contents($file));\nstream_set_chunk_size($sock, 2*1024*1024);\n$data = fread($sock, 8192 * 128);\n\nif ($argc == 4 and $argv[3] == 'save')\n{\n    file_put_contents(\"resp.html\", $data);\n}\nelse\n{\n    echo $data;\n}\n"
  },
  {
    "path": "tools/show-big-files.php",
    "content": "#!/usr/bin/env php\n<?php\nrequire __DIR__ . '/bootstrap.php';\n\n$git_files = swoole_git_files();\n$git_files_map = [];\nforeach ($git_files as $file) {\n    $git_files_map[$file] = filesize(__DIR__ . \"/../{$file}\");\n}\narray_multisort($git_files_map, SORT_DESC);\n$git_files_map = array_slice($git_files_map, 0, 36);\necho json_encode($git_files_map, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);\n"
  },
  {
    "path": "tools/templates/class.c",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  +----------------------------------------------------------------------+\n */\n\n#include \"php_swoole_private.h\"\n\nusing swoole::{{class_name}};\n\nzend_class_entry *swoole_{{module_name}}_ce;\nzend_object_handlers swoole_{{module_name}}_handlers;\n\nstruct {{type_name}} {\n    {{class_name}} *{{var_name}};\n    zend_object std;\n};\n\nstatic zend_always_inline {{class_name}} *swoole_{{module_name}}_get_handle(zend_object *object) {\n    return (({{type_name}} *) ((char *) object - swoole_{{module_name}}_handlers.offset))->{{var_name}};\n}\n\nstatic zend_always_inline {{type_name}} *swoole_{{module_name}}_get_object(zend_object *object) {\n    return ({{type_name}} *) ((char *) object - swoole_{{module_name}}_handlers.offset);\n}\n\nstatic zend_always_inline {{class_name}} *swoole_{{module_name}}_get_handle(zval *zobject) {\n    return (({{type_name}} *) ((char *) Z_OBJ_P(zobject) - swoole_{{module_name}}_handlers.offset))->{{var_name}};\n}\n\nstatic zend_always_inline {{type_name}} *swoole_{{module_name}}_get_object(zval *zobject) {\n    return ({{type_name}} *) ((char *) Z_OBJ_P(zobject) - swoole_{{module_name}}_handlers.offset);\n}\n\nstatic zend_always_inline {{type_name}} *swoole_{{module_name}}_get_object_safe(zval *zobject) {\n    {{class_name}} *{{var_name}} = swoole_{{module_name}}_get_handle(zobject);\n    if (!{{var_name}}) {\n        php_swoole_fatal_error(E_ERROR, \"you must call {{module_name}} constructor first\");\n    }\n    return swoole_{{module_name}}_get_object(zobject);\n}\n\nstatic zend_always_inline {{type_name}} *swoole_{{module_name}}_get_object_safe(zend_object *object) {\n    {{class_name}} *{{var_name}} = swoole_{{module_name}}_get_handle(object);\n    if (!{{var_name}}) {\n        php_swoole_fatal_error(E_ERROR, \"you must call {{module_name}} constructor first\");\n    }\n    return swoole_{{module_name}}_get_object(object);\n}\n\nstatic zend_object *swoole_{{module_name}}_create_object(zend_class_entry *ce) {\n    {{type_name}} *{{php_var_name}} = ({{type_name}} *) zend_object_alloc(sizeof(*{{php_var_name}}), ce);\n\n    zend_object_std_init(&{{php_var_name}}->std, ce);\n    object_properties_init(&{{php_var_name}}->std, ce);\n    {{php_var_name}}->std.handlers = &swoole_{{module_name}}_handlers;\n    {{php_var_name}}->{{var_name}} = new {{class_name}};\n\n    return &{{php_var_name}}->std;\n}\n\nstatic void swoole_{{module_name}}_free_object(zend_object *object) {\n    {{type_name}} *{{php_var_name}} = swoole_{{module_name}}_get_object(object);\n    zend_object_std_dtor(&{{php_var_name}}->std);\n}\n\nZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_{{module_name}}__construct, 0, ZEND_RETURN_VALUE, 0)\n    ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)\nZEND_END_ARG_INFO()\n\nstatic PHP_METHOD(swoole_{{module_name}}, __construct) {\n    zend_long value;\n\n    ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1)\n        Z_PARAM_LONG(value)\n    ZEND_PARSE_PARAMETERS_END();\n}\n\nstatic const zend_function_entry swoole_{{module_name}}_methods[] = {\n    PHP_ME(swoole_{{module_name}}, __construct, arginfo_swoole_{{module_name}}__construct, ZEND_ACC_PUBLIC)\n    PHP_FE_END\n};\n\nint swoole_{{module_name}}_module_init(INIT_FUNC_ARGS) {\n    SW_INIT_CLASS_ENTRY_STD(swoole_{{module_name}}, \"Swoole\\\\{{class_name}}\", swoole_{{module_name}}_methods);\n    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_{{module_name}});\n    SW_SET_CLASS_CLONEABLE(swoole_{{module_name}}, sw_zend_class_clone_deny);\n    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_{{module_name}}, sw_zend_class_unset_property_deny);\n    SW_SET_CLASS_CUSTOM_OBJECT(\n            swoole_{{module_name}}, swoole_{{module_name}}_create_object, swoole_{{module_name}}_free_object, {{type_name}}, std);\n\n    return SUCCESS;\n}\n"
  },
  {
    "path": "tools/templates/version.tpl.h",
    "content": "/*\n  +----------------------------------------------------------------------+\n  | Swoole                                                               |\n  +----------------------------------------------------------------------+\n  | This source file is subject to version 2.0 of the Apache license,    |\n  | that is bundled with this package in the file LICENSE, and is        |\n  | available through the world-wide-web at the following url:           |\n  | http://www.apache.org/licenses/LICENSE-2.0.html                      |\n  | If you did not receive a copy of the Apache2.0 license and are unable|\n  | to obtain it through the world-wide-web, please send a note to       |\n  | license@swoole.com so we can mail you a copy immediately.            |\n  +----------------------------------------------------------------------+\n  | Author: Tianfeng Han  <rango@swoole.com>                             |\n  |         Twosee  <twose@qq.com>                                       |\n  +----------------------------------------------------------------------+\n*/\n\n#ifndef SWOOLE_VERSION_H_\n#define SWOOLE_VERSION_H_\n\n#define SWOOLE_MAJOR_VERSION <?=$next->major.\"\\n\" ?>\n#define SWOOLE_MINOR_VERSION <?=$next->minor.\"\\n\" ?>\n#define SWOOLE_RELEASE_VERSION <?=$next->release.\"\\n\" ?>\n#define SWOOLE_EXTRA_VERSION \"<?=$next->extra ?>\"\n#define SWOOLE_VERSION \"<?=$next->getVersion() ?>\"\n#define SWOOLE_VERSION_ID <?=$next->getVersionId().\"\\n\" ?>\n#define SWOOLE_API_VERSION_ID <?=$next->api.\"\\n\" ?>\n\n#define SWOOLE_BUG_REPORT                                                                                              \\\n    \"A process crash occurred in Swoole-v\" SWOOLE_VERSION \". Please report this issue.\\n\"                              \\\n    \"You can refer to the documentation below, submit an issue to us on GitHub.\\n\"                                     \\\n    \">> https://github.com/swoole/swoole-src/blob/master/docs/ISSUE.md\\n\"\n#endif\n"
  }
]